summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /devtools/client/shared
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'devtools/client/shared')
-rw-r--r--devtools/client/shared/WeakMapMap.js107
-rw-r--r--devtools/client/shared/async-store-helper.js57
-rw-r--r--devtools/client/shared/autocomplete-popup.js709
-rw-r--r--devtools/client/shared/build/babel.js1
-rw-r--r--devtools/client/shared/build/build-debugger.js103
-rw-r--r--devtools/client/shared/build/build.js78
-rw-r--r--devtools/client/shared/build/node-templates.mozbuild36
-rw-r--r--devtools/client/shared/classnames.js40
-rw-r--r--devtools/client/shared/components/.eslintrc.js11
-rw-r--r--devtools/client/shared/components/Accordion.css89
-rw-r--r--devtools/client/shared/components/Accordion.js257
-rw-r--r--devtools/client/shared/components/AppErrorBoundary.css86
-rw-r--r--devtools/client/shared/components/AppErrorBoundary.js163
-rw-r--r--devtools/client/shared/components/Frame.js401
-rw-r--r--devtools/client/shared/components/HSplitBox.js165
-rw-r--r--devtools/client/shared/components/List.css41
-rw-r--r--devtools/client/shared/components/List.js352
-rw-r--r--devtools/client/shared/components/MdnLink.css33
-rw-r--r--devtools/client/shared/components/MdnLink.js38
-rw-r--r--devtools/client/shared/components/NotificationBox.css130
-rw-r--r--devtools/client/shared/components/NotificationBox.js403
-rw-r--r--devtools/client/shared/components/SearchBox.js275
-rw-r--r--devtools/client/shared/components/SearchBoxAutocompletePopup.js150
-rw-r--r--devtools/client/shared/components/SearchModifiers.css64
-rw-r--r--devtools/client/shared/components/SearchModifiers.js84
-rw-r--r--devtools/client/shared/components/Sidebar.js98
-rw-r--r--devtools/client/shared/components/SidebarToggle.css39
-rw-r--r--devtools/client/shared/components/SidebarToggle.js89
-rw-r--r--devtools/client/shared/components/SmartTrace.css170
-rw-r--r--devtools/client/shared/components/SmartTrace.js319
-rw-r--r--devtools/client/shared/components/StackTrace.js96
-rw-r--r--devtools/client/shared/components/Tree.css86
-rw-r--r--devtools/client/shared/components/Tree.js1072
-rw-r--r--devtools/client/shared/components/VirtualizedTree.js1071
-rw-r--r--devtools/client/shared/components/VisibilityHandler.js57
-rw-r--r--devtools/client/shared/components/menu/MenuButton.js450
-rw-r--r--devtools/client/shared/components/menu/MenuItem.js211
-rw-r--r--devtools/client/shared/components/menu/MenuList.js164
-rw-r--r--devtools/client/shared/components/menu/moz.build12
-rw-r--r--devtools/client/shared/components/menu/utils.js62
-rw-r--r--devtools/client/shared/components/moz.build41
-rw-r--r--devtools/client/shared/components/object-inspector/actions.js225
-rw-r--r--devtools/client/shared/components/object-inspector/components/ObjectInspector.css115
-rw-r--r--devtools/client/shared/components/object-inspector/components/ObjectInspector.js387
-rw-r--r--devtools/client/shared/components/object-inspector/components/ObjectInspectorItem.js285
-rw-r--r--devtools/client/shared/components/object-inspector/components/moz.build10
-rw-r--r--devtools/client/shared/components/object-inspector/index.js10
-rw-r--r--devtools/client/shared/components/object-inspector/moz.build16
-rw-r--r--devtools/client/shared/components/object-inspector/reducer.js147
-rw-r--r--devtools/client/shared/components/object-inspector/utils/client.js124
-rw-r--r--devtools/client/shared/components/object-inspector/utils/index.js52
-rw-r--r--devtools/client/shared/components/object-inspector/utils/load-properties.js260
-rw-r--r--devtools/client/shared/components/object-inspector/utils/moz.build13
-rw-r--r--devtools/client/shared/components/object-inspector/utils/node.js1059
-rw-r--r--devtools/client/shared/components/object-inspector/utils/selection.js16
-rw-r--r--devtools/client/shared/components/reps/images/input.svg7
-rw-r--r--devtools/client/shared/components/reps/images/jump-definition.svg8
-rw-r--r--devtools/client/shared/components/reps/images/open-a11y.svg10
-rw-r--r--devtools/client/shared/components/reps/images/open-inspector.svg6
-rw-r--r--devtools/client/shared/components/reps/index.js32
-rw-r--r--devtools/client/shared/components/reps/moz.build14
-rw-r--r--devtools/client/shared/components/reps/reps.css400
-rw-r--r--devtools/client/shared/components/reps/reps/accessible.js197
-rw-r--r--devtools/client/shared/components/reps/reps/accessor.js106
-rw-r--r--devtools/client/shared/components/reps/reps/array.js170
-rw-r--r--devtools/client/shared/components/reps/reps/attribute.js80
-rw-r--r--devtools/client/shared/components/reps/reps/big-int.js57
-rw-r--r--devtools/client/shared/components/reps/reps/comment-node.js76
-rw-r--r--devtools/client/shared/components/reps/reps/constants.js18
-rw-r--r--devtools/client/shared/components/reps/reps/custom-formatter.js256
-rw-r--r--devtools/client/shared/components/reps/reps/date-time.js95
-rw-r--r--devtools/client/shared/components/reps/reps/document-type.js60
-rw-r--r--devtools/client/shared/components/reps/reps/document.js79
-rw-r--r--devtools/client/shared/components/reps/reps/element-node.js322
-rw-r--r--devtools/client/shared/components/reps/reps/error.js338
-rw-r--r--devtools/client/shared/components/reps/reps/event.js115
-rw-r--r--devtools/client/shared/components/reps/reps/function.js264
-rw-r--r--devtools/client/shared/components/reps/reps/grip-array.js263
-rw-r--r--devtools/client/shared/components/reps/reps/grip-entry.js78
-rw-r--r--devtools/client/shared/components/reps/reps/grip-map.js234
-rw-r--r--devtools/client/shared/components/reps/reps/grip.js401
-rw-r--r--devtools/client/shared/components/reps/reps/infinity.js52
-rw-r--r--devtools/client/shared/components/reps/reps/moz.build45
-rw-r--r--devtools/client/shared/components/reps/reps/nan.js51
-rw-r--r--devtools/client/shared/components/reps/reps/null.js59
-rw-r--r--devtools/client/shared/components/reps/reps/number.js63
-rw-r--r--devtools/client/shared/components/reps/reps/object-with-text.js70
-rw-r--r--devtools/client/shared/components/reps/reps/object-with-url.js73
-rw-r--r--devtools/client/shared/components/reps/reps/object.js207
-rw-r--r--devtools/client/shared/components/reps/reps/promise.js105
-rw-r--r--devtools/client/shared/components/reps/reps/prop-rep.js106
-rw-r--r--devtools/client/shared/components/reps/reps/regexp.js66
-rw-r--r--devtools/client/shared/components/reps/reps/rep-utils.js596
-rw-r--r--devtools/client/shared/components/reps/reps/rep.js205
-rw-r--r--devtools/client/shared/components/reps/reps/string.js410
-rw-r--r--devtools/client/shared/components/reps/reps/stylesheet.js78
-rw-r--r--devtools/client/shared/components/reps/reps/symbol.js82
-rw-r--r--devtools/client/shared/components/reps/reps/text-node.js141
-rw-r--r--devtools/client/shared/components/reps/reps/undefined.js59
-rw-r--r--devtools/client/shared/components/reps/reps/window.js102
-rw-r--r--devtools/client/shared/components/reps/shared/dom-node-constants.js31
-rw-r--r--devtools/client/shared/components/reps/shared/grip-length-bubble.js64
-rw-r--r--devtools/client/shared/components/reps/shared/moz.build10
-rw-r--r--devtools/client/shared/components/splitter/Draggable.js106
-rw-r--r--devtools/client/shared/components/splitter/GridElementResizer.css32
-rw-r--r--devtools/client/shared/components/splitter/GridElementWidthResizer.js138
-rw-r--r--devtools/client/shared/components/splitter/SplitBox.css93
-rw-r--r--devtools/client/shared/components/splitter/SplitBox.js351
-rw-r--r--devtools/client/shared/components/splitter/moz.build11
-rw-r--r--devtools/client/shared/components/tabs/TabBar.js378
-rw-r--r--devtools/client/shared/components/tabs/Tabs.css128
-rw-r--r--devtools/client/shared/components/tabs/Tabs.js468
-rw-r--r--devtools/client/shared/components/tabs/moz.build10
-rw-r--r--devtools/client/shared/components/test/browser/browser.toml14
-rw-r--r--devtools/client/shared/components/test/browser/browser_notification_box_basic.js34
-rw-r--r--devtools/client/shared/components/test/browser/browser_reps_stubs.js406
-rw-r--r--devtools/client/shared/components/test/chrome/accordion.snapshots.js176
-rw-r--r--devtools/client/shared/components/test/chrome/chrome.toml87
-rw-r--r--devtools/client/shared/components/test/chrome/head.js379
-rw-r--r--devtools/client/shared/components/test/chrome/test_GridElementWidthResizer.html209
-rw-r--r--devtools/client/shared/components/test/chrome/test_GridElementWidthResizer_RTL.html210
-rw-r--r--devtools/client/shared/components/test/chrome/test_HSplitBox_01.html140
-rw-r--r--devtools/client/shared/components/test/chrome/test_accordion.html141
-rw-r--r--devtools/client/shared/components/test/chrome/test_frame_01.html361
-rw-r--r--devtools/client/shared/components/test/chrome/test_frame_02.html103
-rw-r--r--devtools/client/shared/components/test/chrome/test_list.html127
-rw-r--r--devtools/client/shared/components/test/chrome/test_list_keyboard.html283
-rw-r--r--devtools/client/shared/components/test/chrome/test_notification_box_01.html136
-rw-r--r--devtools/client/shared/components/test/chrome/test_notification_box_02.html73
-rw-r--r--devtools/client/shared/components/test/chrome/test_notification_box_03.html87
-rw-r--r--devtools/client/shared/components/test/chrome/test_notification_box_04.html67
-rw-r--r--devtools/client/shared/components/test/chrome/test_notification_box_05.html63
-rw-r--r--devtools/client/shared/components/test/chrome/test_searchbox-with-autocomplete.html301
-rw-r--r--devtools/client/shared/components/test/chrome/test_searchbox.html74
-rw-r--r--devtools/client/shared/components/test/chrome/test_sidebar_toggle.html59
-rw-r--r--devtools/client/shared/components/test/chrome/test_smart-trace-grouping.html141
-rw-r--r--devtools/client/shared/components/test/chrome/test_smart-trace-source-maps.html290
-rw-r--r--devtools/client/shared/components/test/chrome/test_smart-trace.html172
-rw-r--r--devtools/client/shared/components/test/chrome/test_stack-trace-source-maps.html98
-rw-r--r--devtools/client/shared/components/test/chrome/test_stack-trace.html100
-rw-r--r--devtools/client/shared/components/test/chrome/test_tabs_accessibility.html82
-rw-r--r--devtools/client/shared/components/test/chrome/test_tabs_menu.html84
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree-view_01.html290
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree-view_02.html136
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_01.html68
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_02.html49
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_03.html50
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_04.html133
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_05.html195
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_06.html340
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_07.html69
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_08.html61
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_09.html85
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_10.html57
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_11.html100
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_12.html146
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_13.html88
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_14.html245
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_15.html99
-rw-r--r--devtools/client/shared/components/test/chrome/test_tree_16.html145
-rw-r--r--devtools/client/shared/components/test/node/.eslintrc.js10
-rw-r--r--devtools/client/shared/components/test/node/__mocks__/Services.js14
-rw-r--r--devtools/client/shared/components/test/node/__mocks__/object-front.js55
-rw-r--r--devtools/client/shared/components/test/node/__mocks__/string-front.js15
-rw-r--r--devtools/client/shared/components/test/node/babel.config.js13
-rw-r--r--devtools/client/shared/components/test/node/components/__snapshots__/tree.test.js.snap1171
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/basic.test.js.snap63
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/classnames.test.js.snap351
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/entries.test.js.snap94
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/expand.test.js.snap175
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/getter-setter.test.js.snap51
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/keyboard-navigation.test.js.snap55
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/properties.test.js.snap19
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/proxy.test.js.snap9
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/window.test.js.snap2119
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/basic.test.js439
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/classnames.test.js53
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/create-long-string-front.test.js94
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/create-object-client.test.js114
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/entries.test.js137
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/events.test.js171
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/expand.test.js435
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/function.test.js90
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/getter-setter.test.js106
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/keyboard-navigation.test.js89
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/properties.test.js158
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/proxy.test.js133
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/should-item-update.test.js96
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/component/window.test.js96
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/test-utils.js231
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/__snapshots__/promises.test.js.snap49
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/create-node.test.js87
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/get-children.test.js278
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/get-closest-grip-node.test.js52
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/get-value.test.js91
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/make-node-for-properties.test.js295
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/make-numerical-buckets.test.js138
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/node-has-entries.test.js51
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/node-is-window.test.js20
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/node-supports-numerical-bucketing.test.js72
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/promises.test.js54
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-entries.test.js171
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-full-text.test.js56
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-indexed-properties.test.js259
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-non-indexed-properties.test.js222
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-prototype.test.js218
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-symbols.test.js218
-rw-r--r--devtools/client/shared/components/test/node/components/object-inspector/utils/should-render-roots-in-reps.test.js153
-rw-r--r--devtools/client/shared/components/test/node/components/reps/__snapshots__/accessor.test.js.snap3
-rw-r--r--devtools/client/shared/components/test/node/components/reps/__snapshots__/element-node.test.js.snap42
-rw-r--r--devtools/client/shared/components/test/node/components/reps/__snapshots__/error.test.js.snap1225
-rw-r--r--devtools/client/shared/components/test/node/components/reps/__snapshots__/nan.test.js.snap10
-rw-r--r--devtools/client/shared/components/test/node/components/reps/accessible.test.js321
-rw-r--r--devtools/client/shared/components/test/node/components/reps/accessor.test.js137
-rw-r--r--devtools/client/shared/components/test/node/components/reps/array.test.js117
-rw-r--r--devtools/client/shared/components/test/node/components/reps/attribute.test.js44
-rw-r--r--devtools/client/shared/components/test/node/components/reps/big-int.test.js106
-rw-r--r--devtools/client/shared/components/test/node/components/reps/comment-node.test.js80
-rw-r--r--devtools/client/shared/components/test/node/components/reps/date-time.test.js61
-rw-r--r--devtools/client/shared/components/test/node/components/reps/document-type.test.js51
-rw-r--r--devtools/client/shared/components/test/node/components/reps/document.test.js52
-rw-r--r--devtools/client/shared/components/test/node/components/reps/element-node.test.js668
-rw-r--r--devtools/client/shared/components/test/node/components/reps/error.test.js869
-rw-r--r--devtools/client/shared/components/test/node/components/reps/event.test.js160
-rw-r--r--devtools/client/shared/components/test/node/components/reps/failure.test.js66
-rw-r--r--devtools/client/shared/components/test/node/components/reps/function.test.js584
-rw-r--r--devtools/client/shared/components/test/node/components/reps/grip-array.test.js755
-rw-r--r--devtools/client/shared/components/test/node/components/reps/grip-entry.test.js191
-rw-r--r--devtools/client/shared/components/test/node/components/reps/grip-map.test.js390
-rw-r--r--devtools/client/shared/components/test/node/components/reps/grip.test.js750
-rw-r--r--devtools/client/shared/components/test/node/components/reps/helper-tests.test.js122
-rw-r--r--devtools/client/shared/components/test/node/components/reps/infinity.test.js70
-rw-r--r--devtools/client/shared/components/test/node/components/reps/long-string.test.js135
-rw-r--r--devtools/client/shared/components/test/node/components/reps/nan.test.js43
-rw-r--r--devtools/client/shared/components/test/node/components/reps/null.test.js47
-rw-r--r--devtools/client/shared/components/test/node/components/reps/number.test.js136
-rw-r--r--devtools/client/shared/components/test/node/components/reps/object-with-text.test.js66
-rw-r--r--devtools/client/shared/components/test/node/components/reps/object-with-url.test.js45
-rw-r--r--devtools/client/shared/components/test/node/components/reps/object.test.js356
-rw-r--r--devtools/client/shared/components/test/node/components/reps/promise.test.js229
-rw-r--r--devtools/client/shared/components/test/node/components/reps/regexp.test.js59
-rw-r--r--devtools/client/shared/components/test/node/components/reps/string-with-url.test.js630
-rw-r--r--devtools/client/shared/components/test/node/components/reps/string.test.js257
-rw-r--r--devtools/client/shared/components/test/node/components/reps/stylesheet.test.js41
-rw-r--r--devtools/client/shared/components/test/node/components/reps/symbol.test.js64
-rw-r--r--devtools/client/shared/components/test/node/components/reps/test-helpers.js116
-rw-r--r--devtools/client/shared/components/test/node/components/reps/text-node.test.js203
-rw-r--r--devtools/client/shared/components/test/node/components/reps/undefined.test.js58
-rw-r--r--devtools/client/shared/components/test/node/components/reps/window.test.js197
-rw-r--r--devtools/client/shared/components/test/node/components/tree.test.js911
-rw-r--r--devtools/client/shared/components/test/node/jest.config.js16
-rw-r--r--devtools/client/shared/components/test/node/package.json27
-rw-r--r--devtools/client/shared/components/test/node/setup.js15
-rw-r--r--devtools/client/shared/components/test/node/stubs/object-inspector/grip.js64
-rw-r--r--devtools/client/shared/components/test/node/stubs/object-inspector/map.js154
-rw-r--r--devtools/client/shared/components/test/node/stubs/object-inspector/performance.js784
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/accessible.js74
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/accessor.js85
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/attribute.js36
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/big-int.js196
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/browser_dummy.js11
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/comment-node.js36
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/date-time.js47
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/document-type.js40
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/document.js39
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/element-node.js292
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/error.js396
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/event.js269
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/failure.js21
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/function.js227
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/grip-array.js1087
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/grip-entry.js16
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/grip-map.js908
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/grip.js1057
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/infinity.js19
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/long-string.js39
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/nan.js15
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/null.js15
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/number.js21
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/object-with-text.js36
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/object-with-url.js22
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/promise.js244
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/regexp.js36
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/stubs.toml20
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/stylesheet.js29
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/symbol.js33
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/text-node.js141
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/undefined.js15
-rw-r--r--devtools/client/shared/components/test/node/stubs/reps/window.js65
-rw-r--r--devtools/client/shared/components/test/node/yarn.lock4209
-rw-r--r--devtools/client/shared/components/throttling/NetworkThrottlingMenu.js100
-rw-r--r--devtools/client/shared/components/throttling/actions.js22
-rw-r--r--devtools/client/shared/components/throttling/moz.build13
-rw-r--r--devtools/client/shared/components/throttling/profiles.js122
-rw-r--r--devtools/client/shared/components/throttling/reducer.js29
-rw-r--r--devtools/client/shared/components/throttling/types.js17
-rw-r--r--devtools/client/shared/components/tree/LabelCell.js76
-rw-r--r--devtools/client/shared/components/tree/ObjectProvider.js86
-rw-r--r--devtools/client/shared/components/tree/TreeCell.js139
-rw-r--r--devtools/client/shared/components/tree/TreeHeader.js120
-rw-r--r--devtools/client/shared/components/tree/TreeRow.js304
-rw-r--r--devtools/client/shared/components/tree/TreeView.css198
-rw-r--r--devtools/client/shared/components/tree/TreeView.js799
-rw-r--r--devtools/client/shared/components/tree/moz.build13
-rw-r--r--devtools/client/shared/css-angle.js349
-rw-r--r--devtools/client/shared/curl.js489
-rw-r--r--devtools/client/shared/devices.js182
-rw-r--r--devtools/client/shared/enum.js19
-rw-r--r--devtools/client/shared/events.js22
-rw-r--r--devtools/client/shared/fluent-l10n/fluent-l10n.js64
-rw-r--r--devtools/client/shared/fluent-l10n/moz.build9
-rw-r--r--devtools/client/shared/focus.js73
-rw-r--r--devtools/client/shared/inplace-editor.js1964
-rw-r--r--devtools/client/shared/key-shortcuts.js308
-rw-r--r--devtools/client/shared/keycodes.js139
-rw-r--r--devtools/client/shared/link.js86
-rw-r--r--devtools/client/shared/moz.build65
-rw-r--r--devtools/client/shared/node-attribute-parser.js435
-rw-r--r--devtools/client/shared/output-parser.js1984
-rw-r--r--devtools/client/shared/prefs.js239
-rw-r--r--devtools/client/shared/react-utils.js30
-rw-r--r--devtools/client/shared/redux/create-store.js90
-rw-r--r--devtools/client/shared/redux/middleware/debounce.js100
-rw-r--r--devtools/client/shared/redux/middleware/ignore.js38
-rw-r--r--devtools/client/shared/redux/middleware/log.js31
-rw-r--r--devtools/client/shared/redux/middleware/moz.build18
-rw-r--r--devtools/client/shared/redux/middleware/performance-marker.js68
-rw-r--r--devtools/client/shared/redux/middleware/promise.js69
-rw-r--r--devtools/client/shared/redux/middleware/task.js38
-rw-r--r--devtools/client/shared/redux/middleware/thunk.js23
-rw-r--r--devtools/client/shared/redux/middleware/wait-service.js64
-rw-r--r--devtools/client/shared/redux/middleware/xpcshell/.eslintrc.js10
-rw-r--r--devtools/client/shared/redux/middleware/xpcshell/head.js26
-rw-r--r--devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-01.js66
-rw-r--r--devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-02.js86
-rw-r--r--devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-03.js50
-rw-r--r--devtools/client/shared/redux/middleware/xpcshell/xpcshell.toml11
-rw-r--r--devtools/client/shared/redux/moz.build15
-rw-r--r--devtools/client/shared/redux/subscriber.js16
-rw-r--r--devtools/client/shared/redux/visibility-handler-connect.js35
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-addon.js186
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-binary.js240
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-client.js82
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-device.js53
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-process.js155
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-running-checker.js91
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-runtime.js129
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb-socket.js72
-rw-r--r--devtools/client/shared/remote-debugging/adb/adb.js176
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/index.js29
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/list-devices.js29
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/moz.build12
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js46
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/run-command.js66
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/shell.js107
-rw-r--r--devtools/client/shared/remote-debugging/adb/commands/track-devices.js163
-rw-r--r--devtools/client/shared/remote-debugging/adb/moz.build24
-rw-r--r--devtools/client/shared/remote-debugging/adb/xpcshell/.eslintrc.js9
-rw-r--r--devtools/client/shared/remote-debugging/adb/xpcshell/adb.py72
-rw-r--r--devtools/client/shared/remote-debugging/adb/xpcshell/test_adb.js247
-rw-r--r--devtools/client/shared/remote-debugging/adb/xpcshell/test_prepare-tcp-connection.js78
-rw-r--r--devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell-head.js10
-rw-r--r--devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell.toml14
-rw-r--r--devtools/client/shared/remote-debugging/constants.js24
-rw-r--r--devtools/client/shared/remote-debugging/moz.build20
-rw-r--r--devtools/client/shared/remote-debugging/remote-client-manager.js146
-rw-r--r--devtools/client/shared/remote-debugging/test/xpcshell/.eslintrc.js6
-rw-r--r--devtools/client/shared/remote-debugging/test/xpcshell/test_remote_client_manager.js153
-rw-r--r--devtools/client/shared/remote-debugging/test/xpcshell/test_version_checker.js159
-rw-r--r--devtools/client/shared/remote-debugging/test/xpcshell/xpcshell-head.js10
-rw-r--r--devtools/client/shared/remote-debugging/test/xpcshell/xpcshell.toml9
-rw-r--r--devtools/client/shared/remote-debugging/version-checker.js154
-rw-r--r--devtools/client/shared/screenshot.js424
-rw-r--r--devtools/client/shared/scroll.js144
-rw-r--r--devtools/client/shared/source-map-loader/index.js139
-rw-r--r--devtools/client/shared/source-map-loader/moz.build18
-rw-r--r--devtools/client/shared/source-map-loader/source-map.js646
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/browser.toml21
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/browser_getContentType.js32
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/browser_locations.js141
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/browser_source-map.js163
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/browser_wasm-source-map.js126
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js2
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js.map10
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js94
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js.map21
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js2
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js.map10
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/if.js12
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js16
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js.map7
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js62
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js.map8
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/missingmap.js2
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js2
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js.map9
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js2
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js.map10
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/fixtures/wasm.js.map7
-rw-r--r--devtools/client/shared/source-map-loader/test/browser/head.js27
-rw-r--r--devtools/client/shared/source-map-loader/utils/assert.js13
-rw-r--r--devtools/client/shared/source-map-loader/utils/fetchSourceMap.js139
-rw-r--r--devtools/client/shared/source-map-loader/utils/getOriginalStackFrames.js38
-rw-r--r--devtools/client/shared/source-map-loader/utils/index.js103
-rw-r--r--devtools/client/shared/source-map-loader/utils/moz.build15
-rw-r--r--devtools/client/shared/source-map-loader/utils/network-request.js43
-rw-r--r--devtools/client/shared/source-map-loader/utils/sourceMapRequests.js96
-rw-r--r--devtools/client/shared/source-map-loader/utils/wasmRemap.js107
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js66
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasmbin0 -> 246995 bytes
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/moz.build12
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js17
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js260
-rw-r--r--devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js215
-rw-r--r--devtools/client/shared/source-map-loader/worker.js50
-rw-r--r--devtools/client/shared/source-utils.js359
-rw-r--r--devtools/client/shared/sourceeditor/README259
-rw-r--r--devtools/client/shared/sourceeditor/autocomplete.js358
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/LICENSE23
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/accessibleTextarea.js146
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/comment/comment.js209
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/comment/continuecomment.js78
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.css32
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.js161
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/display/placeholder.js63
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/edit/closebrackets.js191
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/edit/closetag.js184
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/edit/continuelist.js99
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/edit/matchbrackets.js150
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/edit/matchtags.js66
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/edit/trailingspace.js27
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/brace-fold.js105
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/comment-fold.js59
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/foldcode.js152
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.css20
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.js151
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/indent-fold.js48
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/markdown-fold.js49
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/fold/xml-fold.js184
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/runmode/runmode.js72
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/scroll/annotatescrollbar.js128
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/search/match-highlighter.js165
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/search/matchesonscrollbar.js97
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/search/search.js323
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/search/searchcursor.js293
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/selection/active-line.js72
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/addon/selection/mark-selection.js119
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/cmiframe.html28
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/codemirror.bundle.js1
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/keymap/emacs.js418
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/keymap/sublime.js691
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/keymap/vim.js5494
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/lib/codemirror.css350
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js9788
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/clike/clike.js889
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/clojure/clojure.js292
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/coffeescript/coffeescript.js359
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/css/css.js831
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/elm/elm.js205
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/haxe/haxe.js515
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/htmlmixed/htmlmixed.js152
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/http/http.js113
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/javascript/javascript.js934
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/jsx/jsx.js148
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/rust/rust.js72
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/simple/simple.js216
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/wasm/wasm.js203
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mode/xml/xml.js413
-rw-r--r--devtools/client/shared/sourceeditor/codemirror/mozilla.css364
-rw-r--r--devtools/client/shared/sourceeditor/codemirror6/codemirror6.bundle.js1
-rw-r--r--devtools/client/shared/sourceeditor/codemirror6/index.mjs22
-rw-r--r--devtools/client/shared/sourceeditor/codemirror6/moz.build12
-rw-r--r--devtools/client/shared/sourceeditor/css-autocompleter.js1248
-rw-r--r--devtools/client/shared/sourceeditor/editor-commands-controller.js97
-rw-r--r--devtools/client/shared/sourceeditor/editor.js1818
-rw-r--r--devtools/client/shared/sourceeditor/moz.build22
-rw-r--r--devtools/client/shared/sourceeditor/package.json26
-rw-r--r--devtools/client/shared/sourceeditor/rollup.config.mjs24
-rw-r--r--devtools/client/shared/sourceeditor/test/CodeMirrorTestActors.sys.mjs49
-rw-r--r--devtools/client/shared/sourceeditor/test/browser.toml70
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_codemirror.js33
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_css_autocompletion.js172
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_css_getInfo.js250
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_css_statemachine.js144
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_detectindent.js99
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_addons.js33
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_alt_b_f.js46
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_basic.js51
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_events.js158
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_basic.js75
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_cursor.js52
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_cursor_blink.js73
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_disableSearchAddon.js38
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_find_again.js217
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_goto_line.js91
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_history.js30
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_markers.js43
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_movelines.js61
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_editor_prefs.js139
-rw-r--r--devtools/client/shared/sourceeditor/test/browser_vimemacs.js13
-rw-r--r--devtools/client/shared/sourceeditor/test/cm_mode_ruby.js285
-rw-r--r--devtools/client/shared/sourceeditor/test/cm_script_injection_test.js10
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/codemirror.html213
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/comment_test.js114
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/doc_test.js371
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/driver.js142
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/emacs_test.js149
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/mode/javascript/test.js513
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/mode_test.css23
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/mode_test.js193
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/multi_test.js295
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/search_test.js85
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/sublime_test.js284
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/test.js2686
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/vim_test.js4729
-rw-r--r--devtools/client/shared/sourceeditor/test/codemirror/vimemacs.html215
-rw-r--r--devtools/client/shared/sourceeditor/test/css_autocompletion_tests.json106
-rw-r--r--devtools/client/shared/sourceeditor/test/css_statemachine_testcases.css121
-rw-r--r--devtools/client/shared/sourceeditor/test/css_statemachine_tests.json319
-rw-r--r--devtools/client/shared/sourceeditor/test/head.js195
-rw-r--r--devtools/client/shared/sourceeditor/test/head.xhtml5
-rw-r--r--devtools/client/shared/sourceeditor/wasm.js93
-rw-r--r--devtools/client/shared/sourceeditor/webpack.config.js61
-rw-r--r--devtools/client/shared/string-utils.js40
-rw-r--r--devtools/client/shared/stylesheet-utils.js70
-rw-r--r--devtools/client/shared/suggestion-picker.js176
-rw-r--r--devtools/client/shared/telemetry.js819
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/ChromeUtils.js12
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/Services.js566
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/devtools-utils.js13
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/empty-module.js7
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/fluent-l10n.js23
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/generate-uuid.js11
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/indexed-db.js15
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/plural-form.js11
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/promise.js7
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/svgMock.js7
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/telemetry.js13
-rw-r--r--devtools/client/shared/test-helpers/jest-fixtures/unicode-url.js23
-rw-r--r--devtools/client/shared/test-helpers/shared-jest.config.js42
-rw-r--r--devtools/client/shared/test-helpers/shared-node-helpers.js142
-rw-r--r--devtools/client/shared/test/addons/test-addon-1/manifest.json10
-rw-r--r--devtools/client/shared/test/addons/test-addon-2/manifest.json10
-rw-r--r--devtools/client/shared/test/browser.toml323
-rw-r--r--devtools/client/shared/test/browser_autocomplete_popup.js121
-rw-r--r--devtools/client/shared/test/browser_autocomplete_popup_consecutive-show.js57
-rw-r--r--devtools/client/shared/test/browser_autocomplete_popup_input.js251
-rw-r--r--devtools/client/shared/test/browser_browserloader_mocks.js162
-rw-r--r--devtools/client/shared/test/browser_css_angle.js204
-rw-r--r--devtools/client/shared/test/browser_css_color.js106
-rw-r--r--devtools/client/shared/test/browser_cubic-bezier-01.js38
-rw-r--r--devtools/client/shared/test/browser_cubic-bezier-02.js206
-rw-r--r--devtools/client/shared/test/browser_cubic-bezier-03.js70
-rw-r--r--devtools/client/shared/test/browser_cubic-bezier-04.js59
-rw-r--r--devtools/client/shared/test/browser_cubic-bezier-05.js69
-rw-r--r--devtools/client/shared/test/browser_cubic-bezier-06.js95
-rw-r--r--devtools/client/shared/test/browser_cubic-bezier-07.js69
-rw-r--r--devtools/client/shared/test/browser_dbg_globalactor.js71
-rw-r--r--devtools/client/shared/test/browser_dbg_listaddons.js137
-rw-r--r--devtools/client/shared/test/browser_dbg_listtabs-01.js84
-rw-r--r--devtools/client/shared/test/browser_dbg_listtabs-02.js248
-rw-r--r--devtools/client/shared/test/browser_dbg_listworkers.js75
-rw-r--r--devtools/client/shared/test/browser_dbg_multiple-windows.js122
-rw-r--r--devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js43
-rw-r--r--devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js58
-rw-r--r--devtools/client/shared/test/browser_devices.js76
-rw-r--r--devtools/client/shared/test/browser_filter-editor-01.js150
-rw-r--r--devtools/client/shared/test/browser_filter-editor-02.js114
-rw-r--r--devtools/client/shared/test/browser_filter-editor-03.js84
-rw-r--r--devtools/client/shared/test/browser_filter-editor-04.js106
-rw-r--r--devtools/client/shared/test/browser_filter-editor-05.js166
-rw-r--r--devtools/client/shared/test/browser_filter-editor-06.js77
-rw-r--r--devtools/client/shared/test/browser_filter-editor-07.js32
-rw-r--r--devtools/client/shared/test/browser_filter-editor-08.js103
-rw-r--r--devtools/client/shared/test/browser_filter-editor-09.js155
-rw-r--r--devtools/client/shared/test/browser_filter-editor-10.js100
-rw-r--r--devtools/client/shared/test/browser_filter-presets-01.js117
-rw-r--r--devtools/client/shared/test/browser_filter-presets-02.js47
-rw-r--r--devtools/client/shared/test/browser_filter-presets-03.js42
-rw-r--r--devtools/client/shared/test/browser_html_tooltip-01.js78
-rw-r--r--devtools/client/shared/test/browser_html_tooltip-02.js227
-rw-r--r--devtools/client/shared/test/browser_html_tooltip-03.js96
-rw-r--r--devtools/client/shared/test/browser_html_tooltip-04.js100
-rw-r--r--devtools/client/shared/test/browser_html_tooltip-05.js101
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_arrow-01.js86
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_arrow-02.js83
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_consecutive-show.js70
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_doorhanger-01.js79
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_doorhanger-02.js76
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_height-auto.js108
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_hover.js65
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_offset.js97
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_resize.js97
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_rtl.js226
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_screen_edge.js74
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_variable-height.js77
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_width-auto.js53
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js79
-rw-r--r--devtools/client/shared/test/browser_html_tooltip_zoom.js74
-rw-r--r--devtools/client/shared/test/browser_inplace-editor-01.js202
-rw-r--r--devtools/client/shared/test/browser_inplace-editor-02.js80
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_autoclose_parentheses.js77
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js79
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js78
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js104
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js115
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_focus_closest_editor.js180
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_maxwidth.js138
-rw-r--r--devtools/client/shared/test/browser_inplace-editor_stop_on_key.js219
-rw-r--r--devtools/client/shared/test/browser_key_shortcuts.js468
-rw-r--r--devtools/client/shared/test/browser_keycodes.js12
-rw-r--r--devtools/client/shared/test/browser_layoutHelpers.js131
-rw-r--r--devtools/client/shared/test/browser_layoutHelpers_getBoxQuads1.js353
-rw-r--r--devtools/client/shared/test/browser_layoutHelpers_getBoxQuads2.js185
-rw-r--r--devtools/client/shared/test/browser_link.js40
-rw-r--r--devtools/client/shared/test/browser_num-l10n.js70
-rw-r--r--devtools/client/shared/test/browser_outputparser.js856
-rw-r--r--devtools/client/shared/test/browser_prefs-01.js53
-rw-r--r--devtools/client/shared/test/browser_prefs-02.js67
-rw-r--r--devtools/client/shared/test/browser_require_raw.js23
-rw-r--r--devtools/client/shared/test/browser_spectrum.js518
-rw-r--r--devtools/client/shared/test/browser_tableWidget_basic.js448
-rw-r--r--devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js202
-rw-r--r--devtools/client/shared/test/browser_tableWidget_mouse_interaction.js359
-rw-r--r--devtools/client/shared/test/browser_telemetry_button_eyedropper.js39
-rw-r--r--devtools/client/shared/test/browser_telemetry_button_responsive.js108
-rw-r--r--devtools/client/shared/test/browser_telemetry_misc.js51
-rw-r--r--devtools/client/shared/test/browser_telemetry_sidebar.js233
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolbox.js34
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js39
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js39
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js39
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js39
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js34
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js34
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js39
-rw-r--r--devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js39
-rw-r--r--devtools/client/shared/test/browser_theme.js145
-rw-r--r--devtools/client/shared/test/browser_theme_switching.js58
-rw-r--r--devtools/client/shared/test/browser_treeWidget_basic.js391
-rw-r--r--devtools/client/shared/test/browser_treeWidget_keyboard_interaction.js291
-rw-r--r--devtools/client/shared/test/browser_treeWidget_mouse_interaction.js185
-rw-r--r--devtools/client/shared/test/code_WorkerTargetActor.attachThread-worker.js18
-rw-r--r--devtools/client/shared/test/code_listworkers-worker1.js3
-rw-r--r--devtools/client/shared/test/code_listworkers-worker2.js3
-rw-r--r--devtools/client/shared/test/doc_WorkerTargetActor.attachThread-tab.html8
-rw-r--r--devtools/client/shared/test/doc_cubic-bezier-01.html1
-rw-r--r--devtools/client/shared/test/doc_cubic-bezier-02.html3
-rw-r--r--devtools/client/shared/test/doc_empty-tab-01.html14
-rw-r--r--devtools/client/shared/test/doc_empty-tab-02.html14
-rw-r--r--devtools/client/shared/test/doc_event-listeners-01.html45
-rw-r--r--devtools/client/shared/test/doc_event-listeners-03.html65
-rw-r--r--devtools/client/shared/test/doc_filter-editor-01.html1
-rw-r--r--devtools/client/shared/test/doc_html_tooltip-02.xhtml15
-rw-r--r--devtools/client/shared/test/doc_html_tooltip-03.xhtml19
-rw-r--r--devtools/client/shared/test/doc_html_tooltip-04.xhtml15
-rw-r--r--devtools/client/shared/test/doc_html_tooltip-05.xhtml12
-rw-r--r--devtools/client/shared/test/doc_html_tooltip.xhtml12
-rw-r--r--devtools/client/shared/test/doc_html_tooltip_arrow-01.xhtml90
-rw-r--r--devtools/client/shared/test/doc_html_tooltip_arrow-02.xhtml65
-rw-r--r--devtools/client/shared/test/doc_html_tooltip_doorhanger-01.xhtml73
-rw-r--r--devtools/client/shared/test/doc_html_tooltip_doorhanger-02.xhtml34
-rw-r--r--devtools/client/shared/test/doc_html_tooltip_hover.xhtml13
-rw-r--r--devtools/client/shared/test/doc_html_tooltip_rtl.xhtml14
-rw-r--r--devtools/client/shared/test/doc_inplace-editor_autocomplete_offset.xhtml7
-rw-r--r--devtools/client/shared/test/doc_layoutHelpers.html31
-rw-r--r--devtools/client/shared/test/doc_layoutHelpers_getBoxQuads1.html65
-rw-r--r--devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-a.html20
-rw-r--r--devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-b-and-d.html29
-rw-r--r--devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-c-and-e.html27
-rw-r--r--devtools/client/shared/test/doc_listworkers-tab.html8
-rw-r--r--devtools/client/shared/test/doc_native-event-handler.html25
-rw-r--r--devtools/client/shared/test/doc_script-switching-01.html18
-rw-r--r--devtools/client/shared/test/doc_script-switching-02.html18
-rw-r--r--devtools/client/shared/test/doc_spectrum.html2
-rw-r--r--devtools/client/shared/test/doc_tableWidget_basic.html7
-rw-r--r--devtools/client/shared/test/doc_tableWidget_keyboard_interaction.xhtml8
-rw-r--r--devtools/client/shared/test/doc_tableWidget_mouse_interaction.xhtml7
-rw-r--r--devtools/client/shared/test/doc_templater_basic.html12
-rw-r--r--devtools/client/shared/test/dummy.html1
-rw-r--r--devtools/client/shared/test/head.js211
-rw-r--r--devtools/client/shared/test/helper_color_data.js1499
-rw-r--r--devtools/client/shared/test/helper_html_tooltip.js116
-rw-r--r--devtools/client/shared/test/helper_inplace_editor.js164
-rw-r--r--devtools/client/shared/test/highlighter-test-actor.js939
-rw-r--r--devtools/client/shared/test/leakhunt.js173
-rw-r--r--devtools/client/shared/test/shared-head.js2324
-rw-r--r--devtools/client/shared/test/telemetry-test-helpers.js273
-rw-r--r--devtools/client/shared/test/test-mocked-module.js11
-rw-r--r--devtools/client/shared/test/testactors.js27
-rw-r--r--devtools/client/shared/test/xpcshell/.eslintrc.js6
-rw-r--r--devtools/client/shared/test/xpcshell/head.js10
-rw-r--r--devtools/client/shared/test/xpcshell/test_VariablesView_getString_promise.js81
-rw-r--r--devtools/client/shared/test/xpcshell/test_WeakMapMap.js69
-rw-r--r--devtools/client/shared/test/xpcshell/test_advanceValidate.js33
-rw-r--r--devtools/client/shared/test/xpcshell/test_attribute-parsing-01.js77
-rw-r--r--devtools/client/shared/test/xpcshell/test_attribute-parsing-02.js148
-rw-r--r--devtools/client/shared/test/xpcshell/test_bezierCanvas.js122
-rw-r--r--devtools/client/shared/test/xpcshell/test_classnames.js53
-rw-r--r--devtools/client/shared/test/xpcshell/test_cssAngle.js32
-rw-r--r--devtools/client/shared/test/xpcshell/test_cssColor-01.js75
-rw-r--r--devtools/client/shared/test/xpcshell/test_cssColor-02.js50
-rw-r--r--devtools/client/shared/test/xpcshell/test_cssColor-8-digit-hex.js20
-rw-r--r--devtools/client/shared/test/xpcshell/test_cssColorDatabase.js17
-rw-r--r--devtools/client/shared/test/xpcshell/test_cubicBezier.js152
-rw-r--r--devtools/client/shared/test/xpcshell/test_curl.js397
-rw-r--r--devtools/client/shared/test/xpcshell/test_escapeCSSComment.js41
-rw-r--r--devtools/client/shared/test/xpcshell/test_hasCSSVariable.js60
-rw-r--r--devtools/client/shared/test/xpcshell/test_linearEasing.js217
-rw-r--r--devtools/client/shared/test/xpcshell/test_parseDeclarations.js1641
-rw-r--r--devtools/client/shared/test/xpcshell/test_parsePseudoClassesAndAttributes.js202
-rw-r--r--devtools/client/shared/test/xpcshell/test_parseSingleValue.js106
-rw-r--r--devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js816
-rw-r--r--devtools/client/shared/test/xpcshell/test_source-utils.js249
-rw-r--r--devtools/client/shared/test/xpcshell/test_suggestion-picker.js147
-rw-r--r--devtools/client/shared/test/xpcshell/test_undoStack.js88
-rw-r--r--devtools/client/shared/test/xpcshell/test_unicode-url.js258
-rw-r--r--devtools/client/shared/test/xpcshell/xpcshell.toml57
-rw-r--r--devtools/client/shared/theme-switching.js143
-rw-r--r--devtools/client/shared/theme.js102
-rw-r--r--devtools/client/shared/thread-utils.js89
-rw-r--r--devtools/client/shared/toolbarbutton.css86
-rw-r--r--devtools/client/shared/undo.js190
-rw-r--r--devtools/client/shared/unicode-url.js106
-rw-r--r--devtools/client/shared/vendor/D3_LICENSE26
-rw-r--r--devtools/client/shared/vendor/DAGRE_D3_LICENSE19
-rw-r--r--devtools/client/shared/vendor/FLUENT_REACT_UPGRADING33
-rw-r--r--devtools/client/shared/vendor/FUZZALDRIN_PLUS_LICENSE20
-rw-r--r--devtools/client/shared/vendor/MD5_LICENSE27
-rw-r--r--devtools/client/shared/vendor/MD5_UPGRADING.md29
-rw-r--r--devtools/client/shared/vendor/REACT_PROP_TYPES_UPGRADING.md37
-rw-r--r--devtools/client/shared/vendor/REACT_REDUX_LICENSE21
-rw-r--r--devtools/client/shared/vendor/REACT_REDUX_UPGRADING.md36
-rw-r--r--devtools/client/shared/vendor/REACT_ROUTER_DOM_LICENSE21
-rw-r--r--devtools/client/shared/vendor/REACT_ROUTER_DOM_UPGRADING.md23
-rw-r--r--devtools/client/shared/vendor/REACT_UPGRADING.md160
-rw-r--r--devtools/client/shared/vendor/REDUX_LICENSE21
-rw-r--r--devtools/client/shared/vendor/REDUX_UPGRADING.md32
-rw-r--r--devtools/client/shared/vendor/RESELECT_LICENSE21
-rw-r--r--devtools/client/shared/vendor/RESELECT_UPGRADING12
-rw-r--r--devtools/client/shared/vendor/WASMPARSER_UPGRADING14
-rw-r--r--devtools/client/shared/vendor/WHATWG_URL_LICENSE21
-rw-r--r--devtools/client/shared/vendor/WasmDis.js2031
-rw-r--r--devtools/client/shared/vendor/WasmParser.js3873
-rw-r--r--devtools/client/shared/vendor/dagre-d3.js4560
-rw-r--r--devtools/client/shared/vendor/fluent-react.js686
-rw-r--r--devtools/client/shared/vendor/fuzzaldrin-plus.js1074
-rw-r--r--devtools/client/shared/vendor/immutable.js4997
-rw-r--r--devtools/client/shared/vendor/jszip.js11367
-rw-r--r--devtools/client/shared/vendor/md5.js7
-rwxr-xr-xdevtools/client/shared/vendor/micromatch/LICENSE21
-rw-r--r--devtools/client/shared/vendor/micromatch/UPGRADE.md17
-rw-r--r--devtools/client/shared/vendor/micromatch/micromatch.js5424
-rw-r--r--devtools/client/shared/vendor/micromatch/moz.build9
-rw-r--r--devtools/client/shared/vendor/micromatch/package-lock.json4497
-rw-r--r--devtools/client/shared/vendor/micromatch/package.json26
-rw-r--r--devtools/client/shared/vendor/micromatch/webpack.config.js20
-rw-r--r--devtools/client/shared/vendor/moz.build44
-rw-r--r--devtools/client/shared/vendor/react-dev.js3155
-rw-r--r--devtools/client/shared/vendor/react-dom-dev.js21413
-rw-r--r--devtools/client/shared/vendor/react-dom-factories.js195
-rw-r--r--devtools/client/shared/vendor/react-dom-server-dev.js3801
-rw-r--r--devtools/client/shared/vendor/react-dom-server.js2188
-rw-r--r--devtools/client/shared/vendor/react-dom-test-utils-dev.js1302
-rw-r--r--devtools/client/shared/vendor/react-dom-test-utils.js1150
-rw-r--r--devtools/client/shared/vendor/react-dom.js16370
-rw-r--r--devtools/client/shared/vendor/react-prop-types-dev.js1363
-rw-r--r--devtools/client/shared/vendor/react-prop-types.js1363
-rw-r--r--devtools/client/shared/vendor/react-redux.js2089
-rw-r--r--devtools/client/shared/vendor/react-router-dom.js3788
-rw-r--r--devtools/client/shared/vendor/react-test-renderer-shallow.js955
-rw-r--r--devtools/client/shared/vendor/react-test-renderer.js10580
-rw-r--r--devtools/client/shared/vendor/react.js2240
-rw-r--r--devtools/client/shared/vendor/redux.js715
-rw-r--r--devtools/client/shared/vendor/reselect.js291
-rw-r--r--devtools/client/shared/vendor/source-map/LICENSE28
-rw-r--r--devtools/client/shared/vendor/source-map/lib/array-set.js100
-rw-r--r--devtools/client/shared/vendor/source-map/lib/base64-vlq.js94
-rw-r--r--devtools/client/shared/vendor/source-map/lib/base64.js19
-rw-r--r--devtools/client/shared/vendor/source-map/lib/binary-search.js113
-rw-r--r--devtools/client/shared/vendor/source-map/lib/mapping-list.js83
-rw-r--r--devtools/client/shared/vendor/source-map/lib/mappings.wasmbin0 -> 48526 bytes
-rw-r--r--devtools/client/shared/vendor/source-map/lib/moz.build21
-rw-r--r--devtools/client/shared/vendor/source-map/lib/read-wasm.js46
-rw-r--r--devtools/client/shared/vendor/source-map/lib/source-map-consumer.js1078
-rw-r--r--devtools/client/shared/vendor/source-map/lib/source-map-generator.js439
-rw-r--r--devtools/client/shared/vendor/source-map/lib/source-node.js430
-rw-r--r--devtools/client/shared/vendor/source-map/lib/url.js21
-rw-r--r--devtools/client/shared/vendor/source-map/lib/util.js444
-rw-r--r--devtools/client/shared/vendor/source-map/lib/wasm.js138
-rw-r--r--devtools/client/shared/vendor/source-map/moz.build13
-rw-r--r--devtools/client/shared/vendor/source-map/moz.yaml47
-rw-r--r--devtools/client/shared/vendor/source-map/read_wasm.patch64
-rw-r--r--devtools/client/shared/vendor/source-map/relative_url.patch10
-rw-r--r--devtools/client/shared/vendor/source-map/source-map.js10
-rw-r--r--devtools/client/shared/vendor/whatwg-url.js8588
-rw-r--r--devtools/client/shared/view-source.js197
-rw-r--r--devtools/client/shared/webgl-utils.js53
-rw-r--r--devtools/client/shared/widgets/Chart.js532
-rw-r--r--devtools/client/shared/widgets/CubicBezierPresets.js64
-rw-r--r--devtools/client/shared/widgets/CubicBezierWidget.js986
-rw-r--r--devtools/client/shared/widgets/FilterWidget.js1131
-rw-r--r--devtools/client/shared/widgets/LinearEasingFunctionWidget.js731
-rw-r--r--devtools/client/shared/widgets/ShapesInContextEditor.js347
-rw-r--r--devtools/client/shared/widgets/Spectrum.js783
-rw-r--r--devtools/client/shared/widgets/TableWidget.js2031
-rw-r--r--devtools/client/shared/widgets/TreeWidget.js643
-rw-r--r--devtools/client/shared/widgets/cubic-bezier.css216
-rw-r--r--devtools/client/shared/widgets/filter-widget.css242
-rw-r--r--devtools/client/shared/widgets/linear-widget.css61
-rw-r--r--devtools/client/shared/widgets/moz.build22
-rw-r--r--devtools/client/shared/widgets/spectrum.css330
-rw-r--r--devtools/client/shared/widgets/tooltip/EventTooltipHelper.js419
-rw-r--r--devtools/client/shared/widgets/tooltip/HTMLTooltip.js1062
-rw-r--r--devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js145
-rw-r--r--devtools/client/shared/widgets/tooltip/RulePreviewTooltip.js69
-rw-r--r--devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js270
-rw-r--r--devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js363
-rw-r--r--devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip.js95
-rw-r--r--devtools/client/shared/widgets/tooltip/SwatchFilterTooltip.js117
-rw-r--r--devtools/client/shared/widgets/tooltip/SwatchLinearEasingFunctionTooltip.js97
-rw-r--r--devtools/client/shared/widgets/tooltip/TooltipToggle.js197
-rw-r--r--devtools/client/shared/widgets/tooltip/VariableTooltipHelper.js31
-rw-r--r--devtools/client/shared/widgets/tooltip/css-compatibility-tooltip-helper.js292
-rw-r--r--devtools/client/shared/widgets/tooltip/css-query-container-tooltip-helper.js145
-rw-r--r--devtools/client/shared/widgets/tooltip/css-selector-warnings-tooltip-helper.js64
-rw-r--r--devtools/client/shared/widgets/tooltip/inactive-css-tooltip-helper.js131
-rw-r--r--devtools/client/shared/widgets/tooltip/moz.build23
-rw-r--r--devtools/client/shared/widgets/view-helpers.js430
-rw-r--r--devtools/client/shared/widgets/widgets.css79
-rw-r--r--devtools/client/shared/worker-utils.js157
-rw-r--r--devtools/client/shared/workers-listener.js145
-rw-r--r--devtools/client/shared/zoom-keys.js75
833 files changed, 278942 insertions, 0 deletions
diff --git a/devtools/client/shared/WeakMapMap.js b/devtools/client/shared/WeakMapMap.js
new file mode 100644
index 0000000000..37eac656dc
--- /dev/null
+++ b/devtools/client/shared/WeakMapMap.js
@@ -0,0 +1,107 @@
+/* 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/. */
+
+/**
+ * WeakMapMap is a weakmap collection dual-keyed using an object and a string.
+ * This is useful for keeping data compartmentalized e.g. grouped by tab.
+ *
+ * It's name comes from the internal structure which maps a WeakMap to a map,
+ * which contains the target data.
+ *
+ * Usage:
+ * const myWeakMapMap = new WeakMapMap();
+ * const key = { randomObject: true };
+ * myWeakMapMap.set(key, "text1", "Some value1");
+ * myWeakMapMap.set(key, "text2", "Some value2");
+ * myWeakMapMap.get(key, "text1"); // Returns "Some value1"
+ * myWeakMapMap.get(key, "text2"); // Returns "Some value2"
+ * myWeakMapMap.has(key, "text1"); // Returns true
+ * myWeakMapMap.has(key, "notakey"); // Returns false
+ */
+
+"use strict";
+
+class WeakMapMap {
+ constructor() {
+ this.clear();
+ }
+
+ /**
+ * Returns the value associated to the key and nestedKey, or undefined if
+ * there is none.
+ *
+ * @param {Object} key
+ * The key associated with the desired value.
+ * @param {String} nestedKey
+ * The nested key associated with the desired value.
+ */
+ get(key, nestedKey) {
+ if (!this.has(key, nestedKey)) {
+ return undefined;
+ }
+
+ return this.store.get(key).get(nestedKey);
+ }
+
+ /**
+ * Returns the value associated to the key and nestedKey, or undefined if
+ * there is none.
+ *
+ * @param {Object} key
+ * The key associated with the desired value.
+ * @param {String} nestedKey
+ * The nested key associated with the desired value.
+ */
+ has(key, nestedKey) {
+ const hasKey = this.store.has(key);
+
+ return hasKey && this.store.get(key).has(nestedKey);
+ }
+
+ /**
+ *
+ * @param {Object} key
+ * The key associated with the value.
+ * @param {String} nestedKey
+ * The nested key associated with the value.
+ * @param {any} value
+ * The value to add.
+ */
+ set(key, nestedKey, value) {
+ if (!this.store.has(key)) {
+ this.store.set(key, new Map());
+ }
+
+ const innerMap = this.store.get(key);
+ innerMap.set(nestedKey, value);
+ }
+
+ /**
+ * Removes the value associated to the key and nestedKey.
+ *
+ * @param {Object} key
+ * The key associated with the desired value.
+ * @param {String} nestedKey
+ * The nested key associated with the desired value.
+ *
+ * @returns True if an element in the store has been removed successfully.
+ * False if the key is not found in the store.
+ */
+ delete(key, nestedKey) {
+ if (!this.store.has(key)) {
+ return false;
+ }
+
+ return this.store.get(key).delete(nestedKey);
+ }
+
+ /**
+ * Clear the store.
+ */
+ clear() {
+ this.store = new WeakMap();
+ }
+}
+
+module.exports = WeakMapMap;
diff --git a/devtools/client/shared/async-store-helper.js b/devtools/client/shared/async-store-helper.js
new file mode 100644
index 0000000000..0919a07b98
--- /dev/null
+++ b/devtools/client/shared/async-store-helper.js
@@ -0,0 +1,57 @@
+/* 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/>. */
+
+"use strict";
+
+const asyncStorage = require("resource://devtools/shared/async-storage.js");
+
+/*
+ * asyncStoreHelper wraps asyncStorage so that it is easy to define project
+ * specific properties. It is similar to PrefsHelper.
+ *
+ * e.g.
+ * const asyncStore = asyncStoreHelper("r", {a: "_a"})
+ * asyncStore.a // => asyncStorage.getItem("r._a")
+ * asyncStore.a = 2 // => asyncStorage.setItem("r._a", 2)
+ */
+function asyncStoreHelper(root, mappings) {
+ let store = {};
+
+ function getMappingKey(key) {
+ return Array.isArray(mappings[key]) ? mappings[key][0] : mappings[key];
+ }
+
+ function getMappingDefaultValue(key) {
+ return Array.isArray(mappings[key]) ? mappings[key][1] : null;
+ }
+
+ Object.keys(mappings).map(key =>
+ Object.defineProperty(store, key, {
+ async get() {
+ const value = await asyncStorage.getItem(
+ `${root}.${getMappingKey(key)}`
+ );
+ return value || getMappingDefaultValue(key);
+ },
+ set(value) {
+ asyncStorage.setItem(`${root}.${getMappingKey(key)}`, value);
+ },
+ })
+ );
+
+ store = new Proxy(store, {
+ set(target, property, value, receiver) {
+ if (!mappings.hasOwnProperty(property)) {
+ throw new Error(`AsyncStore: ${property} is not defined in mappings`);
+ }
+
+ Reflect.set(...arguments);
+ return true;
+ },
+ });
+
+ return store;
+}
+
+module.exports = asyncStoreHelper;
diff --git a/devtools/client/shared/autocomplete-popup.js b/devtools/client/shared/autocomplete-popup.js
new file mode 100644
index 0000000000..93ebc8d688
--- /dev/null
+++ b/devtools/client/shared/autocomplete-popup.js
@@ -0,0 +1,709 @@
+/* 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/. */
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+
+loader.lazyRequireGetter(
+ this,
+ "HTMLTooltip",
+ "resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "colorUtils",
+ "resource://devtools/shared/css/color.js",
+ true
+);
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+let itemIdCounter = 0;
+
+/**
+ * Autocomplete popup UI implementation.
+ *
+ * @constructor
+ * @param {Document} toolboxDoc
+ * The toolbox document to attach the autocomplete popup panel.
+ * @param {Object} options
+ * An object consiting any of the following options:
+ * - listId {String} The id for the list <UL> element.
+ * - position {String} The position for the tooltip ("top" or "bottom").
+ * - useXulWrapper {Boolean} If the tooltip is hosted in a XUL document, use a
+ * XUL panel in order to use all the screen viewport available (defaults to false).
+ * - autoSelect {Boolean} Boolean to allow the first entry of the popup
+ * panel to be automatically selected when the popup shows.
+ * - onSelect {String} Callback called when the selected index is updated.
+ * - onClick {String} Callback called when the autocomplete popup receives a click
+ * event. The selectedIndex will already be updated if need be.
+ * - input {Element} Optional input element the popup will be bound to. If provided
+ * the event listeners for navigating the autocomplete list are going to be
+ * automatically added.
+ */
+function AutocompletePopup(toolboxDoc, options = {}) {
+ EventEmitter.decorate(this);
+
+ this._document = toolboxDoc;
+ this.autoSelect = options.autoSelect || false;
+ this.listId = options.listId || null;
+ this.position = options.position || "bottom";
+ this.useXulWrapper = options.useXulWrapper || false;
+
+ this.onSelectCallback = options.onSelect;
+ this.onClickCallback = options.onClick;
+
+ // Array of raw autocomplete items
+ this.items = [];
+ // Map of autocompleteItem to HTMLElement
+ this.elements = new WeakMap();
+
+ this.selectedIndex = -1;
+
+ this.onClick = this.onClick.bind(this);
+ this.onInputKeyDown = this.onInputKeyDown.bind(this);
+ this.onInputBlur = this.onInputBlur.bind(this);
+
+ if (options.input) {
+ this.input = options.input;
+ options.input.addEventListener("keydown", this.onInputKeyDown);
+ options.input.addEventListener("blur", this.onInputBlur);
+ }
+}
+
+AutocompletePopup.prototype = {
+ _document: null,
+
+ get list() {
+ if (this._list) {
+ return this._list;
+ }
+
+ this._list = this._document.createElementNS(HTML_NS, "ul");
+ this._list.setAttribute("flex", "1");
+
+ // The list clone will be inserted in the same document as the anchor, and will be a
+ // copy of the main list to allow screen readers to access the list.
+ this._listClone = this._list.cloneNode();
+ this._listClone.className = "devtools-autocomplete-list-aria-clone";
+
+ if (this.listId) {
+ this._list.setAttribute("id", this.listId);
+ }
+
+ this._list.className = "devtools-autocomplete-listbox";
+
+ // We need to retrieve the item padding in order to correct the offset of the popup.
+ const paddingPropertyName = "--autocomplete-item-padding-inline";
+ const listPadding = this._document.defaultView
+ .getComputedStyle(this._list)
+ .getPropertyValue(paddingPropertyName)
+ .replace("px", "");
+
+ this._listPadding = 0;
+ if (!Number.isNaN(Number(listPadding))) {
+ this._listPadding = Number(listPadding);
+ }
+
+ this._list.addEventListener("click", this.onClick);
+
+ return this._list;
+ },
+
+ get tooltip() {
+ if (this._tooltip) {
+ return this._tooltip;
+ }
+
+ this._tooltip = new HTMLTooltip(this._document, {
+ useXulWrapper: this.useXulWrapper,
+ });
+
+ this._tooltip.panel.classList.add(
+ "devtools-autocomplete-popup",
+ "devtools-monospace"
+ );
+ this._tooltip.panel.appendChild(this.list);
+ this._tooltip.setContentSize({ height: "auto" });
+
+ return this._tooltip;
+ },
+
+ onInputKeyDown(event) {
+ // Only handle the even if the popup is opened.
+ if (!this.isOpen) {
+ return;
+ }
+
+ if (
+ this.selectedItem &&
+ this.onClickCallback &&
+ (event.key === "Enter" ||
+ (event.key === "ArrowRight" && !event.shiftKey) ||
+ (event.key === "Tab" && !event.shiftKey))
+ ) {
+ this.onClickCallback(event, this.selectedItem);
+
+ // Prevent the associated keypress to be triggered.
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+
+ // Close the popup when the user hit Left Arrow, but let the keypress be triggered
+ // so the cursor moves as the user wanted.
+ if (event.key === "ArrowLeft" && !event.shiftKey) {
+ this.clearItems();
+ this.hidePopup();
+ return;
+ }
+
+ // Close the popup when the user hit Escape.
+ if (event.key === "Escape") {
+ this.clearItems();
+ this.hidePopup();
+ // Prevent the associated keypress to be triggered.
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+
+ if (event.key === "ArrowDown") {
+ this.selectNextItem();
+ event.preventDefault();
+ event.stopPropagation();
+ return;
+ }
+
+ if (event.key === "ArrowUp") {
+ this.selectPreviousItem();
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ },
+
+ onInputBlur(event) {
+ if (this.isOpen) {
+ this.clearItems();
+ this.hidePopup();
+ }
+ },
+
+ onSelect(e) {
+ if (this.onSelectCallback) {
+ this.onSelectCallback(e);
+ }
+ },
+
+ onClick(e) {
+ const itemEl = e.target.closest(".autocomplete-item");
+ const index =
+ typeof itemEl?.dataset?.index !== "undefined"
+ ? parseInt(itemEl.dataset.index, 10)
+ : null;
+
+ if (index !== null) {
+ this.selectItemAtIndex(index);
+ }
+
+ this.emit("popup-click");
+
+ if (this.onClickCallback) {
+ const item = index !== null ? this.items[index] : null;
+ this.onClickCallback(e, item);
+ }
+ },
+
+ /**
+ * Open the autocomplete popup panel.
+ *
+ * @param {Node} anchor
+ * Optional node to anchor the panel to. Will default to this.input if it exists.
+ * @param {Number} xOffset
+ * Horizontal offset in pixels from the left of the node to the left
+ * of the popup.
+ * @param {Number} yOffset
+ * Vertical offset in pixels from the top of the node to the starting
+ * of the popup.
+ * @param {Number} index
+ * The position of item to select.
+ * @param {Object} options: Check `selectItemAtIndex` for more information.
+ */
+ async openPopup(anchor, xOffset = 0, yOffset = 0, index, options) {
+ if (!anchor && this.input) {
+ anchor = this.input;
+ }
+
+ // Retrieve the anchor's document active element to add accessibility metadata.
+ this._activeElement = anchor.ownerDocument.activeElement;
+
+ // We want the autocomplete items to be perflectly lined-up with the string the
+ // user entered, so we need to remove the left-padding and the left-border from
+ // the xOffset.
+ const leftBorderSize = 1;
+
+ // If we have another call to openPopup while the previous one isn't over yet, we
+ // need to wait until it's settled to not be in a compromised state.
+ if (this._pendingShowPromise) {
+ await this._pendingShowPromise;
+ }
+
+ this._pendingShowPromise = this.tooltip.show(anchor, {
+ x: xOffset - this._listPadding - leftBorderSize,
+ y: yOffset,
+ position: this.position,
+ });
+ await this._pendingShowPromise;
+ this._pendingShowPromise = null;
+
+ if (this.autoSelect) {
+ this.selectItemAtIndex(index, options);
+ }
+
+ this.emit("popup-opened");
+ },
+
+ /**
+ * Select item at the provided index.
+ *
+ * @param {Number} index
+ * The position of the item to select.
+ * @param {Object} options: An object that can contain:
+ * - {Boolean} preventSelectCallback: true to not call this.onSelectCallback as
+ * during the initial autoSelect.
+ */
+ selectItemAtIndex(index, options = {}) {
+ const { preventSelectCallback } = options;
+
+ if (!Number.isInteger(index)) {
+ // If no index was provided, select the first item.
+ index = 0;
+ }
+ const item = this.items[index];
+ const element = this.elements.get(item);
+
+ const previousSelected = this.list.querySelector(".autocomplete-selected");
+ if (previousSelected && previousSelected !== element) {
+ previousSelected.classList.remove("autocomplete-selected");
+ }
+
+ if (element && !element.classList.contains("autocomplete-selected")) {
+ element.classList.add("autocomplete-selected");
+ }
+
+ if (this.isOpen && item) {
+ this._scrollElementIntoViewIfNeeded(element);
+ this._setActiveDescendant(element.id);
+ } else {
+ this._clearActiveDescendant();
+ }
+ this.selectedIndex = index;
+
+ if (
+ this.isOpen &&
+ item &&
+ this.onSelectCallback &&
+ !preventSelectCallback
+ ) {
+ // Call the user-defined select callback if defined.
+ this.onSelectCallback(item);
+ }
+ },
+
+ /**
+ * Hide the autocomplete popup panel.
+ */
+ hidePopup() {
+ this._pendingShowPromise = null;
+ this.tooltip.once("hidden", () => {
+ this.emit("popup-closed");
+ });
+
+ this._clearActiveDescendant();
+ this._activeElement = null;
+ this.tooltip.hide();
+ },
+
+ /**
+ * Check if the autocomplete popup is open.
+ */
+ get isOpen() {
+ return !!this._tooltip && this.tooltip.isVisible();
+ },
+
+ /**
+ * Destroy the object instance. Please note that the panel DOM elements remain
+ * in the DOM, because they might still be in use by other instances of the
+ * same code. It is the responsability of the client code to perform DOM
+ * cleanup.
+ */
+ destroy() {
+ this._pendingShowPromise = null;
+ if (this.isOpen) {
+ this.hidePopup();
+ }
+
+ if (this._list) {
+ this._list.removeEventListener("click", this.onClick);
+
+ this._list.remove();
+ this._listClone.remove();
+
+ this._list = null;
+ }
+
+ if (this._tooltip) {
+ this._tooltip.destroy();
+ this._tooltip = null;
+ }
+
+ if (this.input) {
+ this.input.addEventListener("keydown", this.onInputKeyDown);
+ this.input.addEventListener("blur", this.onInputBlur);
+ this.input = null;
+ }
+
+ this._document = null;
+ },
+
+ /**
+ * Get the autocomplete items array.
+ *
+ * @param {Number} index
+ * The index of the item what is wanted.
+ *
+ * @return {Object} The autocomplete item at index index.
+ */
+ getItemAtIndex(index) {
+ return this.items[index];
+ },
+
+ /**
+ * Get the autocomplete items array.
+ *
+ * @return {Array} The array of autocomplete items.
+ */
+ getItems() {
+ // Return a copy of the array to avoid side effects from the caller code.
+ return this.items.slice(0);
+ },
+
+ /**
+ * Set the autocomplete items list, in one go.
+ *
+ * @param {Array} items
+ * The list of items you want displayed in the popup list.
+ * @param {Number} selectedIndex
+ * The position of the item to select.
+ * @param {Object} options: An object that can contain:
+ * - {Boolean} preventSelectCallback: true to not call this.onSelectCallback as
+ * during the initial autoSelect.
+ */
+ setItems(items, selectedIndex, options) {
+ this.clearItems();
+
+ // If there is no new items, no need to do unecessary work.
+ if (items.length === 0) {
+ return;
+ }
+
+ if (!Number.isInteger(selectedIndex) && this.autoSelect) {
+ selectedIndex = 0;
+ }
+
+ // Let's compute the max label length in the item list. This length will then be used
+ // to set the width of the popup.
+ let maxLabelLength = 0;
+
+ const fragment = this._document.createDocumentFragment();
+ items.forEach((item, i) => {
+ const selected = selectedIndex === i;
+ const listItem = this.createListItem(item, i, selected);
+ this.items.push(item);
+ this.elements.set(item, listItem);
+ fragment.appendChild(listItem);
+
+ let { label, postLabel, count } = item;
+ if (count) {
+ label += count + "";
+ }
+
+ if (postLabel) {
+ label += postLabel;
+ }
+ maxLabelLength = Math.max(label.length, maxLabelLength);
+ });
+
+ // The popup should be as wide as its longest item.
+ // We need to account for the inline padding
+ const fragmentClone = fragment.cloneNode(true);
+ let width = `calc(${
+ maxLabelLength + 3
+ }ch + 2 * var(--autocomplete-item-padding-inline, 0px))`;
+ // As well as add more space if we're displaying color swatches
+ if (fragment.querySelector(".autocomplete-colorswatch")) {
+ width = `calc(${width} + var(--autocomplete-item-color-swatch-size) + 2 * var(--autocomplete-item-color-swatch-margin-inline))`;
+ }
+ this.list.style.width = width;
+ this.list.appendChild(fragment);
+ // Update the clone content to match the current list content.
+ this._listClone.appendChild(fragmentClone);
+
+ this.selectItemAtIndex(selectedIndex, options);
+ },
+
+ _scrollElementIntoViewIfNeeded(element) {
+ const quads = element.getBoxQuads({
+ relativeTo: this.tooltip.panel,
+ createFramesForSuppressedWhitespace: false,
+ });
+ if (!quads || !quads[0]) {
+ return;
+ }
+
+ const { top, height } = quads[0].getBounds();
+ const containerHeight = this.tooltip.panel.getBoundingClientRect().height;
+ if (top < 0) {
+ // Element is above container.
+ element.scrollIntoView(true);
+ } else if (top + height > containerHeight) {
+ // Element is below container.
+ element.scrollIntoView(false);
+ }
+ },
+
+ /**
+ * Clear all the items from the autocomplete list.
+ */
+ clearItems() {
+ if (this._list) {
+ this._list.innerHTML = "";
+ }
+ if (this._listClone) {
+ this._listClone.innerHTML = "";
+ }
+
+ this.items = [];
+ this.elements = new WeakMap();
+ this.selectItemAtIndex(-1);
+ },
+
+ /**
+ * Getter for the selected item.
+ * @type Object
+ */
+ get selectedItem() {
+ return this.items[this.selectedIndex];
+ },
+
+ /**
+ * Setter for the selected item.
+ *
+ * @param {Object} item
+ * The object you want selected in the list.
+ */
+ set selectedItem(item) {
+ const index = this.items.indexOf(item);
+ if (index !== -1 && this.isOpen) {
+ this.selectItemAtIndex(index);
+ }
+ },
+
+ /**
+ * Update the aria-activedescendant attribute on the current active element for
+ * accessibility.
+ *
+ * @param {String} id
+ * The id (as in DOM id) of the currently selected autocomplete suggestion
+ */
+ _setActiveDescendant(id) {
+ if (!this._activeElement) {
+ return;
+ }
+
+ // Make sure the list clone is in the same document as the anchor.
+ const anchorDoc = this._activeElement.ownerDocument;
+ if (
+ !this._listClone.parentNode ||
+ this._listClone.ownerDocument !== anchorDoc
+ ) {
+ anchorDoc.documentElement.appendChild(this._listClone);
+ }
+
+ this._activeElement.setAttribute("aria-activedescendant", id);
+ },
+
+ /**
+ * Clear the aria-activedescendant attribute on the current active element.
+ */
+ _clearActiveDescendant() {
+ if (!this._activeElement) {
+ return;
+ }
+
+ this._activeElement.removeAttribute("aria-activedescendant");
+ },
+
+ createListItem(item, index, selected) {
+ const listItem = this._document.createElementNS(HTML_NS, "li");
+ // Items must have an id for accessibility.
+ listItem.setAttribute("id", "autocomplete-item-" + itemIdCounter++);
+ listItem.classList.add("autocomplete-item");
+ if (selected) {
+ listItem.classList.add("autocomplete-selected");
+ }
+ listItem.setAttribute("data-index", index);
+
+ if (this.direction) {
+ listItem.setAttribute("dir", this.direction);
+ }
+
+ const label = this._document.createElementNS(HTML_NS, "span");
+ label.textContent = item.label;
+ label.className = "autocomplete-value";
+
+ if (item.preLabel) {
+ const preDesc = this._document.createElementNS(HTML_NS, "span");
+ preDesc.textContent = item.preLabel;
+ preDesc.className = "initial-value";
+ listItem.appendChild(preDesc);
+ label.textContent = item.label.slice(item.preLabel.length);
+ }
+
+ listItem.appendChild(label);
+
+ if (item.postLabel) {
+ const postDesc = this._document.createElementNS(HTML_NS, "span");
+ postDesc.className = "autocomplete-postlabel";
+ postDesc.textContent = item.postLabel;
+ // Determines if the postlabel is a valid colour or other value
+ if (this._isValidColor(item.postLabel)) {
+ const colorSwatch = this._document.createElementNS(HTML_NS, "span");
+ colorSwatch.className = "autocomplete-swatch autocomplete-colorswatch";
+ colorSwatch.style.cssText = "background-color: " + item.postLabel;
+ postDesc.insertBefore(colorSwatch, postDesc.childNodes[0]);
+ }
+ listItem.appendChild(postDesc);
+ }
+
+ if (item.count && item.count > 1) {
+ const countDesc = this._document.createElementNS(HTML_NS, "span");
+ countDesc.textContent = item.count;
+ countDesc.setAttribute("flex", "1");
+ countDesc.className = "autocomplete-count";
+ listItem.appendChild(countDesc);
+ }
+
+ return listItem;
+ },
+
+ /**
+ * Getter for the number of items in the popup.
+ * @type {Number}
+ */
+ get itemCount() {
+ return this.items.length;
+ },
+
+ /**
+ * Getter for the height of each item in the list.
+ *
+ * @type {Number}
+ */
+ get _itemsPerPane() {
+ if (this.items.length) {
+ const listHeight = this.tooltip.panel.clientHeight;
+ const element = this.elements.get(this.items[0]);
+ const elementHeight = element.getBoundingClientRect().height;
+ return Math.floor(listHeight / elementHeight);
+ }
+ return 0;
+ },
+
+ /**
+ * Select the next item in the list.
+ *
+ * @return {Object}
+ * The newly selected item object.
+ */
+ selectNextItem() {
+ if (this.selectedIndex < this.items.length - 1) {
+ this.selectItemAtIndex(this.selectedIndex + 1);
+ } else {
+ this.selectItemAtIndex(0);
+ }
+ return this.selectedItem;
+ },
+
+ /**
+ * Select the previous item in the list.
+ *
+ * @return {Object}
+ * The newly-selected item object.
+ */
+ selectPreviousItem() {
+ if (this.selectedIndex > 0) {
+ this.selectItemAtIndex(this.selectedIndex - 1);
+ } else {
+ this.selectItemAtIndex(this.items.length - 1);
+ }
+
+ return this.selectedItem;
+ },
+
+ /**
+ * Select the top-most item in the next page of items or
+ * the last item in the list.
+ *
+ * @return {Object}
+ * The newly-selected item object.
+ */
+ selectNextPageItem() {
+ const nextPageIndex = this.selectedIndex + this._itemsPerPane + 1;
+ this.selectItemAtIndex(Math.min(nextPageIndex, this.itemCount - 1));
+ return this.selectedItem;
+ },
+
+ /**
+ * Select the bottom-most item in the previous page of items,
+ * or the first item in the list.
+ *
+ * @return {Object}
+ * The newly-selected item object.
+ */
+ selectPreviousPageItem() {
+ const prevPageIndex = this.selectedIndex - this._itemsPerPane - 1;
+ this.selectItemAtIndex(Math.max(prevPageIndex, 0));
+ return this.selectedItem;
+ },
+
+ /**
+ * Determines if the specified colour object is a valid colour, and if
+ * it is not a "special value"
+ *
+ * @return {Boolean}
+ * If the object represents a proper colour or not.
+ */
+ _isValidColor(color) {
+ const colorObj = new colorUtils.CssColor(color);
+ return colorObj.valid && !colorObj.specialValue;
+ },
+
+ /**
+ * Used by tests.
+ */
+ get _panel() {
+ return this.tooltip.panel;
+ },
+
+ /**
+ * Used by tests.
+ */
+ get _window() {
+ return this._document.defaultView;
+ },
+};
+
+module.exports = AutocompletePopup;
diff --git a/devtools/client/shared/build/babel.js b/devtools/client/shared/build/babel.js
new file mode 100644
index 0000000000..79c82bd41a
--- /dev/null
+++ b/devtools/client/shared/build/babel.js
@@ -0,0 +1 @@
+!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).Babel={})}(this,(function(e){"use strict";var t=Object.freeze({__proto__:null,get types(){return ff},get DEFAULT_EXTENSIONS(){return rP},get OptionManager(){return nP},get Plugin(){return aP},get File(){return Nw},get buildExternalHelpers(){return Ww},get resolvePlugin(){return rS},get resolvePreset(){return nS},get version(){return iS},get getEnv(){return oS},get tokTypes(){return lb},get traverse(){return sA},get template(){return dE},get createConfigItem(){return yD},get loadPartialConfig(){return lT},get loadPartialConfigSync(){return pT},get loadPartialConfigAsync(){return dT},get loadOptions(){return fT},get loadOptionsSync(){return hT},get loadOptionsAsync(){return mT},get transform(){return Vj},get transformSync(){return Wj},get transformAsync(){return Hj},get transformFile(){return qj},get transformFileSync(){return Kj},get transformFileAsync(){return zj},get transformFromAst(){return Yj},get transformFromAstSync(){return Jj},get transformFromAstAsync(){return $j},get parse(){return Zj},get parseSync(){return eP},get parseAsync(){return tP}});function r(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,n.key,n)}}function n(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}function a(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}function s(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function i(e,t){return t||(t=e.slice(0)),e.raw=t,e}function o(e,t){for(var r=0,n=Object.keys(t);r<n.length;r++){var a=n[r];if(e[a]!==t[a])return!1}return!0}function u(e,t){return!!e&&("ArrayExpression"===e.type&&(void 0===t||o(e,t)))}function c(e,t){return!!e&&("AssignmentExpression"===e.type&&(void 0===t||o(e,t)))}function l(e,t){return!!e&&("BinaryExpression"===e.type&&(void 0===t||o(e,t)))}function p(e,t){return!!e&&("BlockStatement"===e.type&&(void 0===t||o(e,t)))}function d(e,t){return!!e&&("BreakStatement"===e.type&&(void 0===t||o(e,t)))}function f(e,t){return!!e&&("CallExpression"===e.type&&(void 0===t||o(e,t)))}function h(e,t){return!!e&&("CatchClause"===e.type&&(void 0===t||o(e,t)))}function m(e,t){return!!e&&("ConditionalExpression"===e.type&&(void 0===t||o(e,t)))}function y(e,t){return!!e&&("ContinueStatement"===e.type&&(void 0===t||o(e,t)))}function g(e,t){return!!e&&("EmptyStatement"===e.type&&(void 0===t||o(e,t)))}function v(e,t){return!!e&&("ExpressionStatement"===e.type&&(void 0===t||o(e,t)))}function b(e,t){return!!e&&("File"===e.type&&(void 0===t||o(e,t)))}function x(e,t){return!!e&&("ForInStatement"===e.type&&(void 0===t||o(e,t)))}function E(e,t){return!!e&&("ForStatement"===e.type&&(void 0===t||o(e,t)))}function A(e,t){return!!e&&("FunctionDeclaration"===e.type&&(void 0===t||o(e,t)))}function w(e,t){return!!e&&("FunctionExpression"===e.type&&(void 0===t||o(e,t)))}function S(e,t){return!!e&&("Identifier"===e.type&&(void 0===t||o(e,t)))}function D(e,t){return!!e&&("IfStatement"===e.type&&(void 0===t||o(e,t)))}function C(e,t){return!!e&&("LabeledStatement"===e.type&&(void 0===t||o(e,t)))}function T(e,t){return!!e&&("StringLiteral"===e.type&&(void 0===t||o(e,t)))}function j(e,t){return!!e&&("NumericLiteral"===e.type&&(void 0===t||o(e,t)))}function P(e,t){return!!e&&("NullLiteral"===e.type&&(void 0===t||o(e,t)))}function k(e,t){return!!e&&("RegExpLiteral"===e.type&&(void 0===t||o(e,t)))}function F(e,t){return!!e&&("LogicalExpression"===e.type&&(void 0===t||o(e,t)))}function _(e,t){return!!e&&("MemberExpression"===e.type&&(void 0===t||o(e,t)))}function I(e,t){return!!e&&("NewExpression"===e.type&&(void 0===t||o(e,t)))}function B(e,t){return!!e&&("Program"===e.type&&(void 0===t||o(e,t)))}function O(e,t){return!!e&&("ObjectExpression"===e.type&&(void 0===t||o(e,t)))}function N(e,t){return!!e&&("ObjectMethod"===e.type&&(void 0===t||o(e,t)))}function R(e,t){return!!e&&("ObjectProperty"===e.type&&(void 0===t||o(e,t)))}function M(e,t){return!!e&&("RestElement"===e.type&&(void 0===t||o(e,t)))}function L(e,t){return!!e&&("ReturnStatement"===e.type&&(void 0===t||o(e,t)))}function U(e,t){return!!e&&("SequenceExpression"===e.type&&(void 0===t||o(e,t)))}function G(e,t){return!!e&&("SwitchStatement"===e.type&&(void 0===t||o(e,t)))}function V(e,t){return!!e&&("ThisExpression"===e.type&&(void 0===t||o(e,t)))}function W(e,t){return!!e&&("ThrowStatement"===e.type&&(void 0===t||o(e,t)))}function H(e,t){return!!e&&("UnaryExpression"===e.type&&(void 0===t||o(e,t)))}function q(e,t){return!!e&&("VariableDeclaration"===e.type&&(void 0===t||o(e,t)))}function K(e,t){return!!e&&("VariableDeclarator"===e.type&&(void 0===t||o(e,t)))}function z(e,t){return!!e&&("WhileStatement"===e.type&&(void 0===t||o(e,t)))}function X(e,t){return!!e&&("AssignmentPattern"===e.type&&(void 0===t||o(e,t)))}function Y(e,t){return!!e&&("ArrayPattern"===e.type&&(void 0===t||o(e,t)))}function J(e,t){return!!e&&("ArrowFunctionExpression"===e.type&&(void 0===t||o(e,t)))}function $(e,t){return!!e&&("ClassBody"===e.type&&(void 0===t||o(e,t)))}function Q(e,t){return!!e&&("ClassExpression"===e.type&&(void 0===t||o(e,t)))}function Z(e,t){return!!e&&("ClassDeclaration"===e.type&&(void 0===t||o(e,t)))}function ee(e,t){return!!e&&("ExportAllDeclaration"===e.type&&(void 0===t||o(e,t)))}function te(e,t){return!!e&&("ExportDefaultDeclaration"===e.type&&(void 0===t||o(e,t)))}function re(e,t){return!!e&&("ExportNamedDeclaration"===e.type&&(void 0===t||o(e,t)))}function ne(e,t){return!!e&&("ExportSpecifier"===e.type&&(void 0===t||o(e,t)))}function ae(e,t){return!!e&&("ImportDeclaration"===e.type&&(void 0===t||o(e,t)))}function se(e,t){return!!e&&("ImportDefaultSpecifier"===e.type&&(void 0===t||o(e,t)))}function ie(e,t){return!!e&&("ImportNamespaceSpecifier"===e.type&&(void 0===t||o(e,t)))}function oe(e,t){return!!e&&("ImportSpecifier"===e.type&&(void 0===t||o(e,t)))}function ue(e,t){return!!e&&("ClassMethod"===e.type&&(void 0===t||o(e,t)))}function ce(e,t){return!!e&&("ObjectPattern"===e.type&&(void 0===t||o(e,t)))}function le(e,t){return!!e&&("SpreadElement"===e.type&&(void 0===t||o(e,t)))}function pe(e,t){return!!e&&("Super"===e.type&&(void 0===t||o(e,t)))}function de(e,t){return!!e&&("TaggedTemplateExpression"===e.type&&(void 0===t||o(e,t)))}function fe(e,t){return!!e&&("TemplateLiteral"===e.type&&(void 0===t||o(e,t)))}function he(e,t){return!!e&&("YieldExpression"===e.type&&(void 0===t||o(e,t)))}function me(e,t){return!!e&&("AnyTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function ye(e,t){return!!e&&("ArrayTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function ge(e,t){return!!e&&("BooleanTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function ve(e,t){return!!e&&("DeclareExportDeclaration"===e.type&&(void 0===t||o(e,t)))}function be(e,t){return!!e&&("GenericTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function xe(e,t){return!!e&&("IntersectionTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function Ee(e,t){return!!e&&("MixedTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function Ae(e,t){return!!e&&("EmptyTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function we(e,t){return!!e&&("NullableTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function Se(e,t){return!!e&&("NumberTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function De(e,t){return!!e&&("StringTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function Ce(e,t){return!!e&&("TypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function Te(e,t){return!!e&&("TypeCastExpression"===e.type&&(void 0===t||o(e,t)))}function je(e,t){return!!e&&("UnionTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function Pe(e,t){return!!e&&("VoidTypeAnnotation"===e.type&&(void 0===t||o(e,t)))}function ke(e,t){return!!e&&("JSXAttribute"===e.type&&(void 0===t||o(e,t)))}function Fe(e,t){return!!e&&("JSXElement"===e.type&&(void 0===t||o(e,t)))}function _e(e,t){return!!e&&("JSXEmptyExpression"===e.type&&(void 0===t||o(e,t)))}function Ie(e,t){return!!e&&("JSXExpressionContainer"===e.type&&(void 0===t||o(e,t)))}function Be(e,t){return!!e&&("JSXIdentifier"===e.type&&(void 0===t||o(e,t)))}function Oe(e,t){return!!e&&("JSXMemberExpression"===e.type&&(void 0===t||o(e,t)))}function Ne(e,t){return!!e&&("JSXNamespacedName"===e.type&&(void 0===t||o(e,t)))}function Re(e,t){return!!e&&("JSXSpreadAttribute"===e.type&&(void 0===t||o(e,t)))}function Me(e,t){return!!e&&("JSXText"===e.type&&(void 0===t||o(e,t)))}function Le(e,t){return!!e&&("Placeholder"===e.type&&(void 0===t||o(e,t)))}function Ue(e,t){return!!e&&("AwaitExpression"===e.type&&(void 0===t||o(e,t)))}function Ge(e,t){return!!e&&("BindExpression"===e.type&&(void 0===t||o(e,t)))}function Ve(e,t){return!!e&&("ClassProperty"===e.type&&(void 0===t||o(e,t)))}function We(e,t){return!!e&&("OptionalMemberExpression"===e.type&&(void 0===t||o(e,t)))}function He(e,t){return!!e&&("PipelineTopicExpression"===e.type&&(void 0===t||o(e,t)))}function qe(e,t){return!!e&&("Import"===e.type&&(void 0===t||o(e,t)))}function Ke(e,t){return!!e&&("ExportDefaultSpecifier"===e.type&&(void 0===t||o(e,t)))}function ze(e,t){return!!e&&("ExportNamespaceSpecifier"===e.type&&(void 0===t||o(e,t)))}function Xe(e,t){return!!e&&("PrivateName"===e.type&&(void 0===t||o(e,t)))}function Ye(e,t){return!!e&&("TSArrayType"===e.type&&(void 0===t||o(e,t)))}function Je(e,t){return!!e&&("TSOptionalType"===e.type&&(void 0===t||o(e,t)))}function $e(e,t){return!!e&&("TSRestType"===e.type&&(void 0===t||o(e,t)))}function Qe(e,t){return!!e&&("TSUnionType"===e.type&&(void 0===t||o(e,t)))}function Ze(e,t){return!!e&&("TSIntersectionType"===e.type&&(void 0===t||o(e,t)))}function et(e,t){return!!e&&("TSAsExpression"===e.type&&(void 0===t||o(e,t)))}function tt(e,t){return!!e&&("TSTypeAssertion"===e.type&&(void 0===t||o(e,t)))}function rt(e,t){if(!e)return!1;var r=e.type;return("Expression"===r||"ArrayExpression"===r||"AssignmentExpression"===r||"BinaryExpression"===r||"CallExpression"===r||"ConditionalExpression"===r||"FunctionExpression"===r||"Identifier"===r||"StringLiteral"===r||"NumericLiteral"===r||"NullLiteral"===r||"BooleanLiteral"===r||"RegExpLiteral"===r||"LogicalExpression"===r||"MemberExpression"===r||"NewExpression"===r||"ObjectExpression"===r||"SequenceExpression"===r||"ParenthesizedExpression"===r||"ThisExpression"===r||"UnaryExpression"===r||"UpdateExpression"===r||"ArrowFunctionExpression"===r||"ClassExpression"===r||"MetaProperty"===r||"Super"===r||"TaggedTemplateExpression"===r||"TemplateLiteral"===r||"YieldExpression"===r||"TypeCastExpression"===r||"JSXElement"===r||"JSXFragment"===r||"AwaitExpression"===r||"BindExpression"===r||"OptionalMemberExpression"===r||"PipelinePrimaryTopicReference"===r||"OptionalCallExpression"===r||"Import"===r||"DoExpression"===r||"BigIntLiteral"===r||"TSAsExpression"===r||"TSTypeAssertion"===r||"TSNonNullExpression"===r||"Placeholder"===r&&("Expression"===e.expectedNode||"Identifier"===e.expectedNode||"StringLiteral"===e.expectedNode))&&(void 0===t||o(e,t))}function nt(e,t){if(!e)return!1;var r=e.type;return("Binary"===r||"BinaryExpression"===r||"LogicalExpression"===r)&&(void 0===t||o(e,t))}function at(e,t){if(!e)return!1;var r=e.type;return("Scopable"===r||"BlockStatement"===r||"CatchClause"===r||"DoWhileStatement"===r||"ForInStatement"===r||"ForStatement"===r||"FunctionDeclaration"===r||"FunctionExpression"===r||"Program"===r||"ObjectMethod"===r||"SwitchStatement"===r||"WhileStatement"===r||"ArrowFunctionExpression"===r||"ClassExpression"===r||"ClassDeclaration"===r||"ForOfStatement"===r||"ClassMethod"===r||"ClassPrivateMethod"===r||"TSModuleBlock"===r||"Placeholder"===r&&"BlockStatement"===e.expectedNode)&&(void 0===t||o(e,t))}function st(e,t){if(!e)return!1;var r=e.type;return("Statement"===r||"BlockStatement"===r||"BreakStatement"===r||"ContinueStatement"===r||"DebuggerStatement"===r||"DoWhileStatement"===r||"EmptyStatement"===r||"ExpressionStatement"===r||"ForInStatement"===r||"ForStatement"===r||"FunctionDeclaration"===r||"IfStatement"===r||"LabeledStatement"===r||"ReturnStatement"===r||"SwitchStatement"===r||"ThrowStatement"===r||"TryStatement"===r||"VariableDeclaration"===r||"WhileStatement"===r||"WithStatement"===r||"ClassDeclaration"===r||"ExportAllDeclaration"===r||"ExportDefaultDeclaration"===r||"ExportNamedDeclaration"===r||"ForOfStatement"===r||"ImportDeclaration"===r||"DeclareClass"===r||"DeclareFunction"===r||"DeclareInterface"===r||"DeclareModule"===r||"DeclareModuleExports"===r||"DeclareTypeAlias"===r||"DeclareOpaqueType"===r||"DeclareVariable"===r||"DeclareExportDeclaration"===r||"DeclareExportAllDeclaration"===r||"InterfaceDeclaration"===r||"OpaqueType"===r||"TypeAlias"===r||"EnumDeclaration"===r||"TSDeclareFunction"===r||"TSInterfaceDeclaration"===r||"TSTypeAliasDeclaration"===r||"TSEnumDeclaration"===r||"TSModuleDeclaration"===r||"TSImportEqualsDeclaration"===r||"TSExportAssignment"===r||"TSNamespaceExportDeclaration"===r||"Placeholder"===r&&("Statement"===e.expectedNode||"Declaration"===e.expectedNode||"BlockStatement"===e.expectedNode))&&(void 0===t||o(e,t))}function it(e,t){if(!e)return!1;var r=e.type;return("Conditional"===r||"ConditionalExpression"===r||"IfStatement"===r)&&(void 0===t||o(e,t))}function ot(e,t){if(!e)return!1;var r=e.type;return("Loop"===r||"DoWhileStatement"===r||"ForInStatement"===r||"ForStatement"===r||"WhileStatement"===r||"ForOfStatement"===r)&&(void 0===t||o(e,t))}function ut(e,t){if(!e)return!1;var r=e.type;return("For"===r||"ForInStatement"===r||"ForStatement"===r||"ForOfStatement"===r)&&(void 0===t||o(e,t))}function ct(e,t){if(!e)return!1;var r=e.type;return("ForXStatement"===r||"ForInStatement"===r||"ForOfStatement"===r)&&(void 0===t||o(e,t))}function lt(e,t){if(!e)return!1;var r=e.type;return("Function"===r||"FunctionDeclaration"===r||"FunctionExpression"===r||"ObjectMethod"===r||"ArrowFunctionExpression"===r||"ClassMethod"===r||"ClassPrivateMethod"===r)&&(void 0===t||o(e,t))}function pt(e,t){if(!e)return!1;var r=e.type;return("Pureish"===r||"FunctionDeclaration"===r||"FunctionExpression"===r||"StringLiteral"===r||"NumericLiteral"===r||"NullLiteral"===r||"BooleanLiteral"===r||"ArrowFunctionExpression"===r||"ClassExpression"===r||"ClassDeclaration"===r||"BigIntLiteral"===r||"Placeholder"===r&&"StringLiteral"===e.expectedNode)&&(void 0===t||o(e,t))}function dt(e,t){if(!e)return!1;var r=e.type;return("Declaration"===r||"FunctionDeclaration"===r||"VariableDeclaration"===r||"ClassDeclaration"===r||"ExportAllDeclaration"===r||"ExportDefaultDeclaration"===r||"ExportNamedDeclaration"===r||"ImportDeclaration"===r||"DeclareClass"===r||"DeclareFunction"===r||"DeclareInterface"===r||"DeclareModule"===r||"DeclareModuleExports"===r||"DeclareTypeAlias"===r||"DeclareOpaqueType"===r||"DeclareVariable"===r||"DeclareExportDeclaration"===r||"DeclareExportAllDeclaration"===r||"InterfaceDeclaration"===r||"OpaqueType"===r||"TypeAlias"===r||"EnumDeclaration"===r||"TSDeclareFunction"===r||"TSInterfaceDeclaration"===r||"TSTypeAliasDeclaration"===r||"TSEnumDeclaration"===r||"TSModuleDeclaration"===r||"Placeholder"===r&&"Declaration"===e.expectedNode)&&(void 0===t||o(e,t))}function ft(e,t){if(!e)return!1;var r=e.type;return("Literal"===r||"StringLiteral"===r||"NumericLiteral"===r||"NullLiteral"===r||"BooleanLiteral"===r||"RegExpLiteral"===r||"TemplateLiteral"===r||"BigIntLiteral"===r||"Placeholder"===r&&"StringLiteral"===e.expectedNode)&&(void 0===t||o(e,t))}function ht(e,t){if(!e)return!1;var r=e.type;return("Property"===r||"ObjectProperty"===r||"ClassProperty"===r||"ClassPrivateProperty"===r)&&(void 0===t||o(e,t))}function mt(e,t){if(!e)return!1;var r=e.type;return("UnaryLike"===r||"UnaryExpression"===r||"SpreadElement"===r)&&(void 0===t||o(e,t))}function yt(e,t){if(!e)return!1;var r=e.type;return("Pattern"===r||"AssignmentPattern"===r||"ArrayPattern"===r||"ObjectPattern"===r||"Placeholder"===r&&"Pattern"===e.expectedNode)&&(void 0===t||o(e,t))}function gt(e,t){if(!e)return!1;var r=e.type;return("Class"===r||"ClassExpression"===r||"ClassDeclaration"===r)&&(void 0===t||o(e,t))}function vt(e,t){if(!e)return!1;var r=e.type;return("ModuleDeclaration"===r||"ExportAllDeclaration"===r||"ExportDefaultDeclaration"===r||"ExportNamedDeclaration"===r||"ImportDeclaration"===r)&&(void 0===t||o(e,t))}function bt(e,t){if(!e)return!1;var r=e.type;return("ExportDeclaration"===r||"ExportAllDeclaration"===r||"ExportDefaultDeclaration"===r||"ExportNamedDeclaration"===r)&&(void 0===t||o(e,t))}function xt(e,t){if(!e)return!1;var r=e.type;return("ModuleSpecifier"===r||"ExportSpecifier"===r||"ImportDefaultSpecifier"===r||"ImportNamespaceSpecifier"===r||"ImportSpecifier"===r||"ExportDefaultSpecifier"===r||"ExportNamespaceSpecifier"===r)&&(void 0===t||o(e,t))}function Et(e,t){if(!e)return!1;var r=e.type;return("Flow"===r||"AnyTypeAnnotation"===r||"ArrayTypeAnnotation"===r||"BooleanTypeAnnotation"===r||"BooleanLiteralTypeAnnotation"===r||"NullLiteralTypeAnnotation"===r||"ClassImplements"===r||"DeclareClass"===r||"DeclareFunction"===r||"DeclareInterface"===r||"DeclareModule"===r||"DeclareModuleExports"===r||"DeclareTypeAlias"===r||"DeclareOpaqueType"===r||"DeclareVariable"===r||"DeclareExportDeclaration"===r||"DeclareExportAllDeclaration"===r||"DeclaredPredicate"===r||"ExistsTypeAnnotation"===r||"FunctionTypeAnnotation"===r||"FunctionTypeParam"===r||"GenericTypeAnnotation"===r||"InferredPredicate"===r||"InterfaceExtends"===r||"InterfaceDeclaration"===r||"InterfaceTypeAnnotation"===r||"IntersectionTypeAnnotation"===r||"MixedTypeAnnotation"===r||"EmptyTypeAnnotation"===r||"NullableTypeAnnotation"===r||"NumberLiteralTypeAnnotation"===r||"NumberTypeAnnotation"===r||"ObjectTypeAnnotation"===r||"ObjectTypeInternalSlot"===r||"ObjectTypeCallProperty"===r||"ObjectTypeIndexer"===r||"ObjectTypeProperty"===r||"ObjectTypeSpreadProperty"===r||"OpaqueType"===r||"QualifiedTypeIdentifier"===r||"StringLiteralTypeAnnotation"===r||"StringTypeAnnotation"===r||"ThisTypeAnnotation"===r||"TupleTypeAnnotation"===r||"TypeofTypeAnnotation"===r||"TypeAlias"===r||"TypeAnnotation"===r||"TypeCastExpression"===r||"TypeParameter"===r||"TypeParameterDeclaration"===r||"TypeParameterInstantiation"===r||"UnionTypeAnnotation"===r||"Variance"===r||"VoidTypeAnnotation"===r)&&(void 0===t||o(e,t))}function At(e,t){if(!e)return!1;var r=e.type;return("FlowBaseAnnotation"===r||"AnyTypeAnnotation"===r||"BooleanTypeAnnotation"===r||"NullLiteralTypeAnnotation"===r||"MixedTypeAnnotation"===r||"EmptyTypeAnnotation"===r||"NumberTypeAnnotation"===r||"StringTypeAnnotation"===r||"ThisTypeAnnotation"===r||"VoidTypeAnnotation"===r)&&(void 0===t||o(e,t))}function wt(e,t,r){if(!_(e))return!1;var n,a=Array.isArray(t)?t:t.split("."),s=[];for(n=e;_(n);n=n.object)s.push(n.property);if(s.push(n),s.length<a.length)return!1;if(!r&&s.length>a.length)return!1;for(var i=0,o=s.length-1;i<a.length;i++,o--){var u=s[o],c=void 0;if(S(u))c=u.name;else{if(!T(u))return!1;c=u.value}if(a[i]!==c)return!1}return!0}function St(e,t){var r=e.split(".");return function(e){return wt(e,r,t)}}var Dt=St("React.Component");var Ct=function(){this.__data__=[],this.size=0};var Tt=function(e,t){return e===t||e!=e&&t!=t};var jt=function(e,t){for(var r=e.length;r--;)if(Tt(e[r][0],t))return r;return-1},Pt=Array.prototype.splice;var kt=function(e){var t=this.__data__,r=jt(t,e);return!(r<0)&&(r==t.length-1?t.pop():Pt.call(t,r,1),--this.size,!0)};var Ft=function(e){var t=this.__data__,r=jt(t,e);return r<0?void 0:t[r][1]};var _t=function(e){return jt(this.__data__,e)>-1};var It=function(e,t){var r=this.__data__,n=jt(r,e);return n<0?(++this.size,r.push([e,t])):r[n][1]=t,this};function Bt(e){var t=-1,r=null==e?0:e.length;for(this.clear();++t<r;){var n=e[t];this.set(n[0],n[1])}}Bt.prototype.clear=Ct,Bt.prototype.delete=kt,Bt.prototype.get=Ft,Bt.prototype.has=_t,Bt.prototype.set=It;var Ot=Bt;var Nt=function(){this.__data__=new Ot,this.size=0};var Rt=function(e){var t=this.__data__,r=t.delete(e);return this.size=t.size,r};var Mt=function(e){return this.__data__.get(e)};var Lt=function(e){return this.__data__.has(e)},Ut="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function Gt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function Vt(e,t){return e(t={exports:{}},t.exports),t.exports}function Wt(e){return e&&e.default||e}var Ht="object"==typeof Ut&&Ut&&Ut.Object===Object&&Ut,qt="object"==typeof self&&self&&self.Object===Object&&self,Kt=Ht||qt||Function("return this")(),zt=Kt.Symbol,Xt=Object.prototype,Yt=Xt.hasOwnProperty,Jt=Xt.toString,$t=zt?zt.toStringTag:void 0;var Qt=function(e){var t=Yt.call(e,$t),r=e[$t];try{e[$t]=void 0;var n=!0}catch(e){}var a=Jt.call(e);return n&&(t?e[$t]=r:delete e[$t]),a},Zt=Object.prototype.toString;var er=function(e){return Zt.call(e)},tr=zt?zt.toStringTag:void 0;var rr=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":tr&&tr in Object(e)?Qt(e):er(e)};var nr=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)};var ar=function(e){if(!nr(e))return!1;var t=rr(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t},sr=Kt["__core-js_shared__"],ir=function(){var e=/[^.]+$/.exec(sr&&sr.keys&&sr.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}();var or=function(e){return!!ir&&ir in e},ur=Function.prototype.toString;var cr=function(e){if(null!=e){try{return ur.call(e)}catch(e){}try{return e+""}catch(e){}}return""},lr=/^\[object .+?Constructor\]$/,pr=Function.prototype,dr=Object.prototype,fr=pr.toString,hr=dr.hasOwnProperty,mr=RegExp("^"+fr.call(hr).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");var yr=function(e){return!(!nr(e)||or(e))&&(ar(e)?mr:lr).test(cr(e))};var gr=function(e,t){return null==e?void 0:e[t]};var vr=function(e,t){var r=gr(e,t);return yr(r)?r:void 0},br=vr(Kt,"Map"),xr=vr(Object,"create");var Er=function(){this.__data__=xr?xr(null):{},this.size=0};var Ar=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t},wr=Object.prototype.hasOwnProperty;var Sr=function(e){var t=this.__data__;if(xr){var r=t[e];return"__lodash_hash_undefined__"===r?void 0:r}return wr.call(t,e)?t[e]:void 0},Dr=Object.prototype.hasOwnProperty;var Cr=function(e){var t=this.__data__;return xr?void 0!==t[e]:Dr.call(t,e)};var Tr=function(e,t){var r=this.__data__;return this.size+=this.has(e)?0:1,r[e]=xr&&void 0===t?"__lodash_hash_undefined__":t,this};function jr(e){var t=-1,r=null==e?0:e.length;for(this.clear();++t<r;){var n=e[t];this.set(n[0],n[1])}}jr.prototype.clear=Er,jr.prototype.delete=Ar,jr.prototype.get=Sr,jr.prototype.has=Cr,jr.prototype.set=Tr;var Pr=jr;var kr=function(){this.size=0,this.__data__={hash:new Pr,map:new(br||Ot),string:new Pr}};var Fr=function(e){var t=typeof e;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==e:null===e};var _r=function(e,t){var r=e.__data__;return Fr(t)?r["string"==typeof t?"string":"hash"]:r.map};var Ir=function(e){var t=_r(this,e).delete(e);return this.size-=t?1:0,t};var Br=function(e){return _r(this,e).get(e)};var Or=function(e){return _r(this,e).has(e)};var Nr=function(e,t){var r=_r(this,e),n=r.size;return r.set(e,t),this.size+=r.size==n?0:1,this};function Rr(e){var t=-1,r=null==e?0:e.length;for(this.clear();++t<r;){var n=e[t];this.set(n[0],n[1])}}Rr.prototype.clear=kr,Rr.prototype.delete=Ir,Rr.prototype.get=Br,Rr.prototype.has=Or,Rr.prototype.set=Nr;var Mr=Rr;var Lr=function(e,t){var r=this.__data__;if(r instanceof Ot){var n=r.__data__;if(!br||n.length<199)return n.push([e,t]),this.size=++r.size,this;r=this.__data__=new Mr(n)}return r.set(e,t),this.size=r.size,this};function Ur(e){var t=this.__data__=new Ot(e);this.size=t.size}Ur.prototype.clear=Nt,Ur.prototype.delete=Rt,Ur.prototype.get=Mt,Ur.prototype.has=Lt,Ur.prototype.set=Lr;var Gr=Ur;var Vr=function(e,t){for(var r=-1,n=null==e?0:e.length;++r<n&&!1!==t(e[r],r,e););return e},Wr=function(){try{var e=vr(Object,"defineProperty");return e({},"",{}),e}catch(e){}}();var Hr=function(e,t,r){"__proto__"==t&&Wr?Wr(e,t,{configurable:!0,enumerable:!0,value:r,writable:!0}):e[t]=r},qr=Object.prototype.hasOwnProperty;var Kr=function(e,t,r){var n=e[t];qr.call(e,t)&&Tt(n,r)&&(void 0!==r||t in e)||Hr(e,t,r)};var zr=function(e,t,r,n){var a=!r;r||(r={});for(var s=-1,i=t.length;++s<i;){var o=t[s],u=n?n(r[o],e[o],o,r,e):void 0;void 0===u&&(u=e[o]),a?Hr(r,o,u):Kr(r,o,u)}return r};var Xr=function(e,t){for(var r=-1,n=Array(e);++r<e;)n[r]=t(r);return n};var Yr=function(e){return null!=e&&"object"==typeof e};var Jr=function(e){return Yr(e)&&"[object Arguments]"==rr(e)},$r=Object.prototype,Qr=$r.hasOwnProperty,Zr=$r.propertyIsEnumerable,en=Jr(function(){return arguments}())?Jr:function(e){return Yr(e)&&Qr.call(e,"callee")&&!Zr.call(e,"callee")},tn=Array.isArray;var rn=function(){return!1},nn=Vt((function(e,t){var r=t&&!t.nodeType&&t,n=r&&e&&!e.nodeType&&e,a=n&&n.exports===r?Kt.Buffer:void 0,s=(a?a.isBuffer:void 0)||rn;e.exports=s})),an=/^(?:0|[1-9]\d*)$/;var sn=function(e,t){var r=typeof e;return!!(t=null==t?9007199254740991:t)&&("number"==r||"symbol"!=r&&an.test(e))&&e>-1&&e%1==0&&e<t};var on=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991},un={};un["[object Float32Array]"]=un["[object Float64Array]"]=un["[object Int8Array]"]=un["[object Int16Array]"]=un["[object Int32Array]"]=un["[object Uint8Array]"]=un["[object Uint8ClampedArray]"]=un["[object Uint16Array]"]=un["[object Uint32Array]"]=!0,un["[object Arguments]"]=un["[object Array]"]=un["[object ArrayBuffer]"]=un["[object Boolean]"]=un["[object DataView]"]=un["[object Date]"]=un["[object Error]"]=un["[object Function]"]=un["[object Map]"]=un["[object Number]"]=un["[object Object]"]=un["[object RegExp]"]=un["[object Set]"]=un["[object String]"]=un["[object WeakMap]"]=!1;var cn=function(e){return Yr(e)&&on(e.length)&&!!un[rr(e)]};var ln=function(e){return function(t){return e(t)}},pn=Vt((function(e,t){var r=t&&!t.nodeType&&t,n=r&&e&&!e.nodeType&&e,a=n&&n.exports===r&&Ht.process,s=function(){try{var e=n&&n.require&&n.require("util").types;return e||a&&a.binding&&a.binding("util")}catch(e){}}();e.exports=s})),dn=pn&&pn.isTypedArray,fn=dn?ln(dn):cn,hn=Object.prototype.hasOwnProperty;var mn=function(e,t){var r=tn(e),n=!r&&en(e),a=!r&&!n&&nn(e),s=!r&&!n&&!a&&fn(e),i=r||n||a||s,o=i?Xr(e.length,String):[],u=o.length;for(var c in e)!t&&!hn.call(e,c)||i&&("length"==c||a&&("offset"==c||"parent"==c)||s&&("buffer"==c||"byteLength"==c||"byteOffset"==c)||sn(c,u))||o.push(c);return o},yn=Object.prototype;var gn=function(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||yn)};var vn=function(e,t){return function(r){return e(t(r))}},bn=vn(Object.keys,Object),xn=Object.prototype.hasOwnProperty;var En=function(e){if(!gn(e))return bn(e);var t=[];for(var r in Object(e))xn.call(e,r)&&"constructor"!=r&&t.push(r);return t};var An=function(e){return null!=e&&on(e.length)&&!ar(e)};var wn=function(e){return An(e)?mn(e):En(e)};var Sn=function(e,t){return e&&zr(t,wn(t),e)};var Dn=function(e){var t=[];if(null!=e)for(var r in Object(e))t.push(r);return t},Cn=Object.prototype.hasOwnProperty;var Tn=function(e){if(!nr(e))return Dn(e);var t=gn(e),r=[];for(var n in e)("constructor"!=n||!t&&Cn.call(e,n))&&r.push(n);return r};var jn=function(e){return An(e)?mn(e,!0):Tn(e)};var Pn=function(e,t){return e&&zr(t,jn(t),e)},kn=Vt((function(e,t){var r=t&&!t.nodeType&&t,n=r&&e&&!e.nodeType&&e,a=n&&n.exports===r?Kt.Buffer:void 0,s=a?a.allocUnsafe:void 0;e.exports=function(e,t){if(t)return e.slice();var r=e.length,n=s?s(r):new e.constructor(r);return e.copy(n),n}}));var Fn=function(e,t){var r=-1,n=e.length;for(t||(t=Array(n));++r<n;)t[r]=e[r];return t};var _n=function(e,t){for(var r=-1,n=null==e?0:e.length,a=0,s=[];++r<n;){var i=e[r];t(i,r,e)&&(s[a++]=i)}return s};var In=function(){return[]},Bn=Object.prototype.propertyIsEnumerable,On=Object.getOwnPropertySymbols,Nn=On?function(e){return null==e?[]:(e=Object(e),_n(On(e),(function(t){return Bn.call(e,t)})))}:In;var Rn=function(e,t){return zr(e,Nn(e),t)};var Mn=function(e,t){for(var r=-1,n=t.length,a=e.length;++r<n;)e[a+r]=t[r];return e},Ln=vn(Object.getPrototypeOf,Object),Un=Object.getOwnPropertySymbols?function(e){for(var t=[];e;)Mn(t,Nn(e)),e=Ln(e);return t}:In;var Gn=function(e,t){return zr(e,Un(e),t)};var Vn=function(e,t,r){var n=t(e);return tn(e)?n:Mn(n,r(e))};var Wn=function(e){return Vn(e,wn,Nn)};var Hn=function(e){return Vn(e,jn,Un)},qn=vr(Kt,"DataView"),Kn=vr(Kt,"Promise"),zn=vr(Kt,"Set"),Xn=vr(Kt,"WeakMap"),Yn=cr(qn),Jn=cr(br),$n=cr(Kn),Qn=cr(zn),Zn=cr(Xn),ea=rr;(qn&&"[object DataView]"!=ea(new qn(new ArrayBuffer(1)))||br&&"[object Map]"!=ea(new br)||Kn&&"[object Promise]"!=ea(Kn.resolve())||zn&&"[object Set]"!=ea(new zn)||Xn&&"[object WeakMap]"!=ea(new Xn))&&(ea=function(e){var t=rr(e),r="[object Object]"==t?e.constructor:void 0,n=r?cr(r):"";if(n)switch(n){case Yn:return"[object DataView]";case Jn:return"[object Map]";case $n:return"[object Promise]";case Qn:return"[object Set]";case Zn:return"[object WeakMap]"}return t});var ta=ea,ra=Object.prototype.hasOwnProperty;var na=function(e){var t=e.length,r=new e.constructor(t);return t&&"string"==typeof e[0]&&ra.call(e,"index")&&(r.index=e.index,r.input=e.input),r},aa=Kt.Uint8Array;var sa=function(e){var t=new e.constructor(e.byteLength);return new aa(t).set(new aa(e)),t};var ia=function(e,t){var r=t?sa(e.buffer):e.buffer;return new e.constructor(r,e.byteOffset,e.byteLength)},oa=/\w*$/;var ua=function(e){var t=new e.constructor(e.source,oa.exec(e));return t.lastIndex=e.lastIndex,t},ca=zt?zt.prototype:void 0,la=ca?ca.valueOf:void 0;var pa=function(e){return la?Object(la.call(e)):{}};var da=function(e,t){var r=t?sa(e.buffer):e.buffer;return new e.constructor(r,e.byteOffset,e.length)};var fa=function(e,t,r){var n=e.constructor;switch(t){case"[object ArrayBuffer]":return sa(e);case"[object Boolean]":case"[object Date]":return new n(+e);case"[object DataView]":return ia(e,r);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return da(e,r);case"[object Map]":return new n;case"[object Number]":case"[object String]":return new n(e);case"[object RegExp]":return ua(e);case"[object Set]":return new n;case"[object Symbol]":return pa(e)}},ha=Object.create,ma=function(){function e(){}return function(t){if(!nr(t))return{};if(ha)return ha(t);e.prototype=t;var r=new e;return e.prototype=void 0,r}}();var ya=function(e){return"function"!=typeof e.constructor||gn(e)?{}:ma(Ln(e))};var ga=function(e){return Yr(e)&&"[object Map]"==ta(e)},va=pn&&pn.isMap,ba=va?ln(va):ga;var xa=function(e){return Yr(e)&&"[object Set]"==ta(e)},Ea=pn&&pn.isSet,Aa=Ea?ln(Ea):xa,wa={};wa["[object Arguments]"]=wa["[object Array]"]=wa["[object ArrayBuffer]"]=wa["[object DataView]"]=wa["[object Boolean]"]=wa["[object Date]"]=wa["[object Float32Array]"]=wa["[object Float64Array]"]=wa["[object Int8Array]"]=wa["[object Int16Array]"]=wa["[object Int32Array]"]=wa["[object Map]"]=wa["[object Number]"]=wa["[object Object]"]=wa["[object RegExp]"]=wa["[object Set]"]=wa["[object String]"]=wa["[object Symbol]"]=wa["[object Uint8Array]"]=wa["[object Uint8ClampedArray]"]=wa["[object Uint16Array]"]=wa["[object Uint32Array]"]=!0,wa["[object Error]"]=wa["[object Function]"]=wa["[object WeakMap]"]=!1;var Sa=function e(t,r,n,a,s,i){var o,u=1&r,c=2&r,l=4&r;if(n&&(o=s?n(t,a,s,i):n(t)),void 0!==o)return o;if(!nr(t))return t;var p=tn(t);if(p){if(o=na(t),!u)return Fn(t,o)}else{var d=ta(t),f="[object Function]"==d||"[object GeneratorFunction]"==d;if(nn(t))return kn(t,u);if("[object Object]"==d||"[object Arguments]"==d||f&&!s){if(o=c||f?{}:ya(t),!u)return c?Gn(t,Pn(o,t)):Rn(t,Sn(o,t))}else{if(!wa[d])return s?t:{};o=fa(t,d,u)}}i||(i=new Gr);var h=i.get(t);if(h)return h;i.set(t,o),Aa(t)?t.forEach((function(a){o.add(e(a,r,n,a,t,i))})):ba(t)&&t.forEach((function(a,s){o.set(s,e(a,r,n,s,t,i))}));var m=l?c?Hn:Wn:c?keysIn:wn,y=p?void 0:m(t);return Vr(y||t,(function(a,s){y&&(a=t[s=a]),Kr(o,s,e(a,r,n,s,t,i))})),o};var Da=function(e){return Sa(e,4)},Ca=null;function Ta(e){if(null!==Ca&&(Ca.property,1)){var t=Ca;return Ca=Ta.prototype=null,t}return Ca=Ta.prototype=null==e?Object.create(null):e,new Ta}Ta();var ja=function(e){return Ta(e)},Pa="undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{};function ka(){throw new Error("setTimeout has not been defined")}function Fa(){throw new Error("clearTimeout has not been defined")}var _a=ka,Ia=Fa;function Ba(e){if(_a===setTimeout)return setTimeout(e,0);if((_a===ka||!_a)&&setTimeout)return _a=setTimeout,setTimeout(e,0);try{return _a(e,0)}catch(t){try{return _a.call(null,e,0)}catch(t){return _a.call(this,e,0)}}}"function"==typeof Pa.setTimeout&&(_a=setTimeout),"function"==typeof Pa.clearTimeout&&(Ia=clearTimeout);var Oa,Na=[],Ra=!1,Ma=-1;function La(){Ra&&Oa&&(Ra=!1,Oa.length?Na=Oa.concat(Na):Ma=-1,Na.length&&Ua())}function Ua(){if(!Ra){var e=Ba(La);Ra=!0;for(var t=Na.length;t;){for(Oa=Na,Na=[];++Ma<t;)Oa&&Oa[Ma].run();Ma=-1,t=Na.length}Oa=null,Ra=!1,function(e){if(Ia===clearTimeout)return clearTimeout(e);if((Ia===Fa||!Ia)&&clearTimeout)return Ia=clearTimeout,clearTimeout(e);try{Ia(e)}catch(t){try{return Ia.call(null,e)}catch(t){return Ia.call(this,e)}}}(e)}}function Ga(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var r=1;r<arguments.length;r++)t[r-1]=arguments[r];Na.push(new Va(e,t)),1!==Na.length||Ra||Ba(Ua)}function Va(e,t){this.fun=e,this.array=t}Va.prototype.run=function(){this.fun.apply(null,this.array)};function Wa(){}var Ha=Wa,qa=Wa,Ka=Wa,za=Wa,Xa=Wa,Ya=Wa,Ja=Wa;var $a=Pa.performance||{},Qa=$a.now||$a.mozNow||$a.msNow||$a.oNow||$a.webkitNow||function(){return(new Date).getTime()};var Za=new Date;var es={nextTick:Ga,title:"browser",browser:!0,env:{},argv:[],version:"",versions:{},on:Ha,addListener:qa,once:Ka,off:za,removeListener:Xa,removeAllListeners:Ya,emit:Ja,binding:function(e){throw new Error("process.binding is not supported")},cwd:function(){return"/"},chdir:function(e){throw new Error("process.chdir is not supported")},umask:function(){return 0},hrtime:function(e){var t=.001*Qa.call($a),r=Math.floor(t),n=Math.floor(t%1*1e9);return e&&(r-=e[0],(n-=e[1])<0&&(r--,n+=1e9)),[r,n]},platform:"browser",release:{},config:{},uptime:function(){return(new Date-Za)/1e3}},ts=Vt((function(e){!function(){function t(e){if(null==e)return!1;switch(e.type){case"BlockStatement":case"BreakStatement":case"ContinueStatement":case"DebuggerStatement":case"DoWhileStatement":case"EmptyStatement":case"ExpressionStatement":case"ForInStatement":case"ForStatement":case"IfStatement":case"LabeledStatement":case"ReturnStatement":case"SwitchStatement":case"ThrowStatement":case"TryStatement":case"VariableDeclaration":case"WhileStatement":case"WithStatement":return!0}return!1}function r(e){switch(e.type){case"IfStatement":return null!=e.alternate?e.alternate:e.consequent;case"LabeledStatement":case"ForStatement":case"ForInStatement":case"WhileStatement":case"WithStatement":return e.body}return null}e.exports={isExpression:function(e){if(null==e)return!1;switch(e.type){case"ArrayExpression":case"AssignmentExpression":case"BinaryExpression":case"CallExpression":case"ConditionalExpression":case"FunctionExpression":case"Identifier":case"Literal":case"LogicalExpression":case"MemberExpression":case"NewExpression":case"ObjectExpression":case"SequenceExpression":case"ThisExpression":case"UnaryExpression":case"UpdateExpression":return!0}return!1},isStatement:t,isIterationStatement:function(e){if(null==e)return!1;switch(e.type){case"DoWhileStatement":case"ForInStatement":case"ForStatement":case"WhileStatement":return!0}return!1},isSourceElement:function(e){return t(e)||null!=e&&"FunctionDeclaration"===e.type},isProblematicIfStatement:function(e){var t;if("IfStatement"!==e.type)return!1;if(null==e.alternate)return!1;t=e.consequent;do{if("IfStatement"===t.type&&null==t.alternate)return!0;t=r(t)}while(t);return!1},trailingStatement:r}}()})),rs=(ts.isExpression,ts.isStatement,ts.isIterationStatement,ts.isSourceElement,ts.isProblematicIfStatement,ts.trailingStatement,Vt((function(e){!function(){var t,r,n,a,s,i;function o(e){return e<=65535?String.fromCharCode(e):String.fromCharCode(Math.floor((e-65536)/1024)+55296)+String.fromCharCode((e-65536)%1024+56320)}for(r={NonAsciiIdentifierStart:/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/,NonAsciiIdentifierPart:/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]/},t={NonAsciiIdentifierStart:/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]/,NonAsciiIdentifierPart:/[\xAA\xB5\xB7\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D01-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1369-\u1371\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF6\u1CF8\u1CF9\u1D00-\u1DF5\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF30-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00\uDC01]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/},n=[5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8239,8287,12288,65279],a=new Array(128),i=0;i<128;++i)a[i]=i>=97&&i<=122||i>=65&&i<=90||36===i||95===i;for(s=new Array(128),i=0;i<128;++i)s[i]=i>=97&&i<=122||i>=65&&i<=90||i>=48&&i<=57||36===i||95===i;e.exports={isDecimalDigit:function(e){return 48<=e&&e<=57},isHexDigit:function(e){return 48<=e&&e<=57||97<=e&&e<=102||65<=e&&e<=70},isOctalDigit:function(e){return e>=48&&e<=55},isWhiteSpace:function(e){return 32===e||9===e||11===e||12===e||160===e||e>=5760&&n.indexOf(e)>=0},isLineTerminator:function(e){return 10===e||13===e||8232===e||8233===e},isIdentifierStartES5:function(e){return e<128?a[e]:r.NonAsciiIdentifierStart.test(o(e))},isIdentifierPartES5:function(e){return e<128?s[e]:r.NonAsciiIdentifierPart.test(o(e))},isIdentifierStartES6:function(e){return e<128?a[e]:t.NonAsciiIdentifierStart.test(o(e))},isIdentifierPartES6:function(e){return e<128?s[e]:t.NonAsciiIdentifierPart.test(o(e))}}}()}))),ns=(rs.isDecimalDigit,rs.isHexDigit,rs.isOctalDigit,rs.isWhiteSpace,rs.isLineTerminator,rs.isIdentifierStartES5,rs.isIdentifierPartES5,rs.isIdentifierStartES6,rs.isIdentifierPartES6,Vt((function(e){!function(){var t=rs;function r(e,t){return!(!t&&"yield"===e)&&n(e,t)}function n(e,t){if(t&&function(e){switch(e){case"implements":case"interface":case"package":case"private":case"protected":case"public":case"static":case"let":return!0;default:return!1}}(e))return!0;switch(e.length){case 2:return"if"===e||"in"===e||"do"===e;case 3:return"var"===e||"for"===e||"new"===e||"try"===e;case 4:return"this"===e||"else"===e||"case"===e||"void"===e||"with"===e||"enum"===e;case 5:return"while"===e||"break"===e||"catch"===e||"throw"===e||"const"===e||"yield"===e||"class"===e||"super"===e;case 6:return"return"===e||"typeof"===e||"delete"===e||"switch"===e||"export"===e||"import"===e;case 7:return"default"===e||"finally"===e||"extends"===e;case 8:return"function"===e||"continue"===e||"debugger"===e;case 10:return"instanceof"===e;default:return!1}}function a(e,t){return"null"===e||"true"===e||"false"===e||r(e,t)}function s(e,t){return"null"===e||"true"===e||"false"===e||n(e,t)}function i(e){var r,n,a;if(0===e.length)return!1;if(a=e.charCodeAt(0),!t.isIdentifierStartES5(a))return!1;for(r=1,n=e.length;r<n;++r)if(a=e.charCodeAt(r),!t.isIdentifierPartES5(a))return!1;return!0}function o(e){var r,n,a,s,i;if(0===e.length)return!1;for(i=t.isIdentifierStartES6,r=0,n=e.length;r<n;++r){if(55296<=(a=e.charCodeAt(r))&&a<=56319){if(++r>=n)return!1;if(!(56320<=(s=e.charCodeAt(r))&&s<=57343))return!1;a=1024*(a-55296)+(s-56320)+65536}if(!i(a))return!1;i=t.isIdentifierPartES6}return!0}e.exports={isKeywordES5:r,isKeywordES6:n,isReservedWordES5:a,isReservedWordES6:s,isRestrictedWord:function(e){return"eval"===e||"arguments"===e},isIdentifierNameES5:i,isIdentifierNameES6:o,isIdentifierES5:function(e,t){return i(e)&&!a(e,t)},isIdentifierES6:function(e,t){return o(e)&&!s(e,t)}}}()}))),as=(ns.isKeywordES5,ns.isKeywordES6,ns.isReservedWordES5,ns.isReservedWordES6,ns.isRestrictedWord,ns.isIdentifierNameES5,ns.isIdentifierNameES6,ns.isIdentifierES5,ns.isIdentifierES6,Vt((function(e,t){t.ast=ts,t.code=rs,t.keyword=ns})));as.ast,as.code,as.keyword;function ss(e,t){if(e===t)return!0;if(Fs[t])return!1;var r=_s[t];if(r){if(r[0]===e)return!0;var n=r,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}if(e===i)return!0}}return!1}function is(e,t){if(e===t)return!0;var r=hi[e];if(r){var n=r,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}if(t===i)return!0}}return!1}function os(e,t,r){return!!t&&(ss(t.type,e)?void 0===r||o(t,r):!r&&"Placeholder"===t.type&&e in _s&&is(t.expectedNode,e))}var us=["consequent","body","alternate"],cs=["left","init"],ls=["leadingComments","trailingComments","innerComments"],ps=["||","&&","??"],ds=["++","--"],fs=[">","<",">=","<="],hs=["==","===","!=","!=="],ms=[].concat(hs,["in","instanceof"]),ys=[].concat(ms,fs),gs=["-","/","%","*","**","&","|",">>",">>>","<<","^"],vs=["+"].concat(gs,ys),bs=["=","+="].concat(gs.map((function(e){return e+"="}))),xs=["delete","!"],Es=["+","-","~"],As=["typeof"],ws=["void","throw"].concat(xs,Es,As),Ss={optional:["typeAnnotation","typeParameters","returnType"],force:["start","loc","end"]},Ds=Symbol.for("var used to be block scoped"),Cs=Symbol.for("should not be considered a local binding");function Ts(e,t,r){if(e){var n=Is[e.type];if(n)js(e,t,r,n[t]),Ps(e,t,r)}}function js(e,t,r,n){n&&n.validate&&(n.optional&&null==r||n.validate(e,t,r))}function Ps(e,t,r){if(null!=r){var n=Ns[r.type];n&&n(e,t,r)}}var ks={},Fs={},_s={},Is={},Bs={},Os={},Ns={};function Rs(e){return Array.isArray(e)?"array":null===e?"null":typeof e}function Ms(e){return{validate:e}}function Ls(e){return"string"==typeof e?zs(e):zs.apply(void 0,e)}function Us(e){return Ms(Ls(e))}function Gs(e){return{validate:e,optional:!0}}function Vs(e){return{validate:Ls(e),optional:!0}}function Ws(e){return t=Ls(e),Js(Ys("array"),qs(t));var t}function Hs(e){return Ms(Ws(e))}function qs(e){function t(t,r,n){if(Array.isArray(n))for(var a=0;a<n.length;a++){var s=r+"["+a+"]",i=n[a];e(t,s,i),es.env.BABEL_TYPES_8_BREAKING&&Ps(t,s,i)}}return t.each=e,t}function Ks(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];function n(e,r,n){if(t.indexOf(n)<0)throw new TypeError("Property "+r+" expected value to be one of "+JSON.stringify(t)+" but got "+JSON.stringify(n))}return n.oneOf=t,n}function zs(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];function n(e,r,n){var a=t,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}if(os(o,n))return void Ps(e,r,n)}throw new TypeError("Property "+r+" of "+e.type+" expected node to be of a type "+JSON.stringify(t)+" but instead got "+JSON.stringify(n&&n.type))}return n.oneOfNodeTypes=t,n}function Xs(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];function n(e,r,n){var a=t,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o;if(Rs(n)===u||os(u,n))return void Ps(e,r,n)}throw new TypeError("Property "+r+" of "+e.type+" expected node to be of a type "+JSON.stringify(t)+" but instead got "+JSON.stringify(n&&n.type))}return n.oneOfNodeOrValueTypes=t,n}function Ys(e){function t(t,r,n){if(!(Rs(n)===e))throw new TypeError("Property "+r+" expected type of "+e+" but got "+Rs(n))}return t.type=e,t}function Js(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];function n(){var e=t,r=Array.isArray(e),n=0;for(e=r?e:e[Symbol.iterator]();;){var a;if(r){if(n>=e.length)break;a=e[n++]}else{if((n=e.next()).done)break;a=n.value}var s=a;s.apply(void 0,arguments)}}return n.chainOf=t,n}var $s=["aliases","builder","deprecatedAlias","fields","inherits","visitor","validate"],Qs=["default","optional","validate"];function Zs(e,t){void 0===t&&(t={});var r=t.inherits&&ei[t.inherits]||{},n=t.fields;if(!n&&(n={},r.fields))for(var a=0,s=Object.getOwnPropertyNames(r.fields);a<s.length;a++){var i=s[a],o=r.fields[i];n[i]={default:o.default,optional:o.optional,validate:o.validate}}for(var u=t.visitor||r.visitor||[],c=t.aliases||r.aliases||[],l=t.builder||r.builder||t.visitor||[],p=0,d=Object.keys(t);p<d.length;p++){var f=d[p];if(-1===$s.indexOf(f))throw new Error('Unknown type option "'+f+'" on '+e)}t.deprecatedAlias&&(Os[t.deprecatedAlias]=e);for(var h=0,m=u.concat(l);h<m.length;h++){var y=m[h];n[y]=n[y]||{}}for(var g=0,v=Object.keys(n);g<v.length;g++){var b=v[g],x=n[b];void 0!==x.default&&-1===l.indexOf(b)&&(x.optional=!0),void 0===x.default?x.default=null:x.validate||null==x.default||(x.validate=Ys(Rs(x.default)));for(var E=0,A=Object.keys(x);E<A.length;E++){var w=A[E];if(-1===Qs.indexOf(w))throw new Error('Unknown field key "'+w+'" on '+e+"."+b)}}ks[e]=t.visitor=u,Bs[e]=t.builder=l,Is[e]=t.fields=n,Fs[e]=t.aliases=c,c.forEach((function(t){_s[t]=_s[t]||[],_s[t].push(e)})),t.validate&&(Ns[e]=t.validate),ei[e]=t}var ei={};Zs("ArrayExpression",{fields:{elements:{validate:Js(Ys("array"),qs(Xs("null","Expression","SpreadElement"))),default:es.env.BABEL_TYPES_8_BREAKING?void 0:[]}},visitor:["elements"],aliases:["Expression"]}),Zs("AssignmentExpression",{fields:{operator:{validate:function(){if(!es.env.BABEL_TYPES_8_BREAKING)return Ys("string");var e=Ks.apply(void 0,bs),t=Ks("=");return function(r,n,a){(os("Pattern",r.left)?t:e)(r,n,a)}}()},left:{validate:es.env.BABEL_TYPES_8_BREAKING?zs("Identifier","MemberExpression","ArrayPattern","ObjectPattern"):zs("LVal")},right:{validate:zs("Expression")}},builder:["operator","left","right"],visitor:["left","right"],aliases:["Expression"]}),Zs("BinaryExpression",{builder:["operator","left","right"],fields:{operator:{validate:Ks.apply(void 0,vs)},left:{validate:zs("Expression")},right:{validate:zs("Expression")}},visitor:["left","right"],aliases:["Binary","Expression"]}),Zs("InterpreterDirective",{builder:["value"],fields:{value:{validate:Ys("string")}}}),Zs("Directive",{visitor:["value"],fields:{value:{validate:zs("DirectiveLiteral")}}}),Zs("DirectiveLiteral",{builder:["value"],fields:{value:{validate:Ys("string")}}}),Zs("BlockStatement",{builder:["body","directives"],visitor:["directives","body"],fields:{directives:{validate:Js(Ys("array"),qs(zs("Directive"))),default:[]},body:{validate:Js(Ys("array"),qs(zs("Statement")))}},aliases:["Scopable","BlockParent","Block","Statement"]}),Zs("BreakStatement",{visitor:["label"],fields:{label:{validate:zs("Identifier"),optional:!0}},aliases:["Statement","Terminatorless","CompletionStatement"]}),Zs("CallExpression",{visitor:["callee","arguments","typeParameters","typeArguments"],builder:["callee","arguments"],aliases:["Expression"],fields:Object.assign({callee:{validate:zs("Expression","V8IntrinsicIdentifier")},arguments:{validate:Js(Ys("array"),qs(zs("Expression","SpreadElement","JSXNamespacedName","ArgumentPlaceholder")))}},es.env.BABEL_TYPES_8_BREAKING?{}:{optional:{validate:Ks(!0,!1),optional:!0}},{typeArguments:{validate:zs("TypeParameterInstantiation"),optional:!0},typeParameters:{validate:zs("TSTypeParameterInstantiation"),optional:!0}})}),Zs("CatchClause",{visitor:["param","body"],fields:{param:{validate:zs("Identifier","ArrayPattern","ObjectPattern"),optional:!0},body:{validate:zs("BlockStatement")}},aliases:["Scopable","BlockParent"]}),Zs("ConditionalExpression",{visitor:["test","consequent","alternate"],fields:{test:{validate:zs("Expression")},consequent:{validate:zs("Expression")},alternate:{validate:zs("Expression")}},aliases:["Expression","Conditional"]}),Zs("ContinueStatement",{visitor:["label"],fields:{label:{validate:zs("Identifier"),optional:!0}},aliases:["Statement","Terminatorless","CompletionStatement"]}),Zs("DebuggerStatement",{aliases:["Statement"]}),Zs("DoWhileStatement",{visitor:["test","body"],fields:{test:{validate:zs("Expression")},body:{validate:zs("Statement")}},aliases:["Statement","BlockParent","Loop","While","Scopable"]}),Zs("EmptyStatement",{aliases:["Statement"]}),Zs("ExpressionStatement",{visitor:["expression"],fields:{expression:{validate:zs("Expression")}},aliases:["Statement","ExpressionWrapper"]}),Zs("File",{builder:["program","comments","tokens"],visitor:["program"],fields:{program:{validate:zs("Program")}}}),Zs("ForInStatement",{visitor:["left","right","body"],aliases:["Scopable","Statement","For","BlockParent","Loop","ForXStatement"],fields:{left:{validate:es.env.BABEL_TYPES_8_BREAKING?zs("VariableDeclaration","Identifier","MemberExpression","ArrayPattern","ObjectPattern"):zs("VariableDeclaration","LVal")},right:{validate:zs("Expression")},body:{validate:zs("Statement")}}}),Zs("ForStatement",{visitor:["init","test","update","body"],aliases:["Scopable","Statement","For","BlockParent","Loop"],fields:{init:{validate:zs("VariableDeclaration","Expression"),optional:!0},test:{validate:zs("Expression"),optional:!0},update:{validate:zs("Expression"),optional:!0},body:{validate:zs("Statement")}}});var ti={params:{validate:Js(Ys("array"),qs(zs("Identifier","Pattern","RestElement","TSParameterProperty")))},generator:{default:!1},async:{default:!1}},ri={returnType:{validate:zs("TypeAnnotation","TSTypeAnnotation","Noop"),optional:!0},typeParameters:{validate:zs("TypeParameterDeclaration","TSTypeParameterDeclaration","Noop"),optional:!0}},ni=Object.assign({},ti,{declare:{validate:Ys("boolean"),optional:!0},id:{validate:zs("Identifier"),optional:!0}});Zs("FunctionDeclaration",{builder:["id","params","body","generator","async"],visitor:["id","params","body","returnType","typeParameters"],fields:Object.assign({},ni,{},ri,{body:{validate:zs("BlockStatement")}}),aliases:["Scopable","Function","BlockParent","FunctionParent","Statement","Pureish","Declaration"],validate:function(){if(!es.env.BABEL_TYPES_8_BREAKING)return function(){};var e=zs("Identifier");return function(t,r,n){os("ExportDefaultDeclaration",t)||e(n,"id",n.id)}}()}),Zs("FunctionExpression",{inherits:"FunctionDeclaration",aliases:["Scopable","Function","BlockParent","FunctionParent","Expression","Pureish"],fields:Object.assign({},ti,{},ri,{id:{validate:zs("Identifier"),optional:!0},body:{validate:zs("BlockStatement")}})});var ai,si,ii,oi,ui,ci={typeAnnotation:{validate:zs("TypeAnnotation","TSTypeAnnotation","Noop"),optional:!0},decorators:{validate:Js(Ys("array"),qs(zs("Decorator")))}};Zs("Identifier",{builder:["name"],visitor:["typeAnnotation","decorators"],aliases:["Expression","PatternLike","LVal","TSEntityName"],fields:Object.assign({},ci,{name:{validate:Js(Ys("string"),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING&&!as.keyword.isIdentifierNameES6(r))throw new TypeError('"'+r+'" is not a valid identifier name')}))},optional:{validate:Ys("boolean"),optional:!0}}),validate:function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING){var n=/\.(\w+)$/.exec(t);if(n){var a=n[1],s={computed:!1};if("property"===a){if(os("MemberExpression",e,s))return;if(os("OptionalMemberExpression",e,s))return}else if("key"===a){if(os("Property",e,s))return;if(os("Method",e,s))return}else if("exported"===a){if(os("ExportSpecifier",e))return}else if("imported"===a){if(os("ImportSpecifier",e,{imported:r}))return}else if("meta"===a&&os("MetaProperty",e,{meta:r}))return;if(as.keyword.isReservedWordES6(r.name,!1)&&"this"!==r.name)throw new TypeError('"'+r.name+'" is not a valid identifer')}}}}),Zs("IfStatement",{visitor:["test","consequent","alternate"],aliases:["Statement","Conditional"],fields:{test:{validate:zs("Expression")},consequent:{validate:zs("Statement")},alternate:{optional:!0,validate:zs("Statement")}}}),Zs("LabeledStatement",{visitor:["label","body"],aliases:["Statement"],fields:{label:{validate:zs("Identifier")},body:{validate:zs("Statement")}}}),Zs("StringLiteral",{builder:["value"],fields:{value:{validate:Ys("string")}},aliases:["Expression","Pureish","Literal","Immutable"]}),Zs("NumericLiteral",{builder:["value"],deprecatedAlias:"NumberLiteral",fields:{value:{validate:Ys("number")}},aliases:["Expression","Pureish","Literal","Immutable"]}),Zs("NullLiteral",{aliases:["Expression","Pureish","Literal","Immutable"]}),Zs("BooleanLiteral",{builder:["value"],fields:{value:{validate:Ys("boolean")}},aliases:["Expression","Pureish","Literal","Immutable"]}),Zs("RegExpLiteral",{builder:["pattern","flags"],deprecatedAlias:"RegexLiteral",aliases:["Expression","Literal"],fields:{pattern:{validate:Ys("string")},flags:{validate:Js(Ys("string"),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING){var n=/[^gimsuy]/.exec(r);if(n)throw new TypeError('"'+n[0]+'" is not a valid RegExp flag')}})),default:""}}}),Zs("LogicalExpression",{builder:["operator","left","right"],visitor:["left","right"],aliases:["Binary","Expression"],fields:{operator:{validate:Ks.apply(void 0,ps)},left:{validate:zs("Expression")},right:{validate:zs("Expression")}}}),Zs("MemberExpression",{builder:["object","property","computed","optional"],visitor:["object","property"],aliases:["Expression","LVal"],fields:Object.assign({object:{validate:zs("Expression")},property:{validate:(ai=zs("Identifier","PrivateName"),si=zs("Expression"),function(e,t,r){(e.computed?si:ai)(e,t,r)})},computed:{default:!1}},es.env.BABEL_TYPES_8_BREAKING?{}:{optional:{validate:Ks(!0,!1),optional:!0}})}),Zs("NewExpression",{inherits:"CallExpression"}),Zs("Program",{visitor:["directives","body"],builder:["body","directives","sourceType","interpreter"],fields:{sourceFile:{validate:Ys("string")},sourceType:{validate:Ks("script","module"),default:"script"},interpreter:{validate:zs("InterpreterDirective"),default:null,optional:!0},directives:{validate:Js(Ys("array"),qs(zs("Directive"))),default:[]},body:{validate:Js(Ys("array"),qs(zs("Statement")))}},aliases:["Scopable","BlockParent","Block"]}),Zs("ObjectExpression",{visitor:["properties"],aliases:["Expression"],fields:{properties:{validate:Js(Ys("array"),qs(zs("ObjectMethod","ObjectProperty","SpreadElement")))}}}),Zs("ObjectMethod",{builder:["kind","key","params","body","computed","generator","async"],fields:Object.assign({},ti,{},ri,{kind:Object.assign({validate:Ks("method","get","set")},es.env.BABEL_TYPES_8_BREAKING?{}:{default:"method"}),computed:{default:!1},key:{validate:function(){var e=zs("Identifier","StringLiteral","NumericLiteral"),t=zs("Expression");return function(r,n,a){(r.computed?t:e)(r,n,a)}}()},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0},body:{validate:zs("BlockStatement")}}),visitor:["key","params","body","decorators","returnType","typeParameters"],aliases:["UserWhitespacable","Function","Scopable","BlockParent","FunctionParent","Method","ObjectMember"]}),Zs("ObjectProperty",{builder:["key","value","computed","shorthand"].concat(es.env.BABEL_TYPES_8_BREAKING?[]:["decorators"]),fields:{computed:{default:!1},key:{validate:function(){var e=zs("Identifier","StringLiteral","NumericLiteral"),t=zs("Expression");return function(r,n,a){(r.computed?t:e)(r,n,a)}}()},value:{validate:zs("Expression","PatternLike")},shorthand:{validate:Js(Ys("boolean"),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING&&r&&e.computed)throw new TypeError("Property shorthand of ObjectProperty cannot be true if computed is true")}),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING&&r&&!os("Identifier",e.key))throw new TypeError("Property shorthand of ObjectProperty cannot be true if key is not an Identifier")})),default:!1},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0}},visitor:["key","value","decorators"],aliases:["UserWhitespacable","Property","ObjectMember"],validate:function(){var e=zs("Identifier","Pattern"),t=zs("Expression");return function(r,n,a){es.env.BABEL_TYPES_8_BREAKING&&(os("ObjectPattern",r)?e:t)(a,"value",a.value)}}()}),Zs("RestElement",{visitor:["argument","typeAnnotation"],builder:["argument"],aliases:["LVal","PatternLike"],deprecatedAlias:"RestProperty",fields:Object.assign({},ci,{argument:{validate:es.env.BABEL_TYPES_8_BREAKING?zs("Identifier","Pattern","MemberExpression"):zs("LVal")}}),validate:function(e,t){if(es.env.BABEL_TYPES_8_BREAKING){var r=/(\w+)\[(\d+)\]/.exec(t);if(!r)throw new Error("Internal Babel error: malformed key.");var n=r[1],a=r[2];if(e[n].length>a+1)throw new TypeError("RestElement must be last element of "+n)}}}),Zs("ReturnStatement",{visitor:["argument"],aliases:["Statement","Terminatorless","CompletionStatement"],fields:{argument:{validate:zs("Expression"),optional:!0}}}),Zs("SequenceExpression",{visitor:["expressions"],fields:{expressions:{validate:Js(Ys("array"),qs(zs("Expression")))}},aliases:["Expression"]}),Zs("ParenthesizedExpression",{visitor:["expression"],aliases:["Expression","ExpressionWrapper"],fields:{expression:{validate:zs("Expression")}}}),Zs("SwitchCase",{visitor:["test","consequent"],fields:{test:{validate:zs("Expression"),optional:!0},consequent:{validate:Js(Ys("array"),qs(zs("Statement")))}}}),Zs("SwitchStatement",{visitor:["discriminant","cases"],aliases:["Statement","BlockParent","Scopable"],fields:{discriminant:{validate:zs("Expression")},cases:{validate:Js(Ys("array"),qs(zs("SwitchCase")))}}}),Zs("ThisExpression",{aliases:["Expression"]}),Zs("ThrowStatement",{visitor:["argument"],aliases:["Statement","Terminatorless","CompletionStatement"],fields:{argument:{validate:zs("Expression")}}}),Zs("TryStatement",{visitor:["block","handler","finalizer"],aliases:["Statement"],fields:{block:{validate:Js(zs("BlockStatement"),(function(e){if(es.env.BABEL_TYPES_8_BREAKING&&!e.handler&&!e.finalizer)throw new TypeError("TryStatement expects either a handler or finalizer, or both")}))},handler:{optional:!0,validate:zs("CatchClause")},finalizer:{optional:!0,validate:zs("BlockStatement")}}}),Zs("UnaryExpression",{builder:["operator","argument","prefix"],fields:{prefix:{default:!0},argument:{validate:zs("Expression")},operator:{validate:Ks.apply(void 0,ws)}},visitor:["argument"],aliases:["UnaryLike","Expression"]}),Zs("UpdateExpression",{builder:["operator","argument","prefix"],fields:{prefix:{default:!1},argument:{validate:es.env.BABEL_TYPES_8_BREAKING?zs("Identifier","MemberExpression"):zs("Expression")},operator:{validate:Ks.apply(void 0,ds)}},visitor:["argument"],aliases:["Expression"]}),Zs("VariableDeclaration",{builder:["kind","declarations"],visitor:["declarations"],aliases:["Statement","Declaration"],fields:{declare:{validate:Ys("boolean"),optional:!0},kind:{validate:Ks("var","let","const")},declarations:{validate:Js(Ys("array"),qs(zs("VariableDeclarator")))}},validate:function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING&&os("ForXStatement",e,{left:r})&&1!==r.declarations.length)throw new TypeError("Exactly one VariableDeclarator is required in the VariableDeclaration of a "+e.type)}}),Zs("VariableDeclarator",{visitor:["id","init"],fields:{id:{validate:function(){if(!es.env.BABEL_TYPES_8_BREAKING)return zs("LVal");var e=zs("Identifier","ArrayPattern","ObjectPattern"),t=zs("Identifier");return function(r,n,a){(r.init?e:t)(r,n,a)}}()},definite:{optional:!0,validate:Ys("boolean")},init:{optional:!0,validate:zs("Expression")}}}),Zs("WhileStatement",{visitor:["test","body"],aliases:["Statement","BlockParent","Loop","While","Scopable"],fields:{test:{validate:zs("Expression")},body:{validate:zs("Statement")}}}),Zs("WithStatement",{visitor:["object","body"],aliases:["Statement"],fields:{object:{validate:zs("Expression")},body:{validate:zs("Statement")}}}),Zs("AssignmentPattern",{visitor:["left","right","decorators"],builder:["left","right"],aliases:["Pattern","PatternLike","LVal"],fields:Object.assign({},ci,{left:{validate:zs("Identifier","ObjectPattern","ArrayPattern","MemberExpression")},right:{validate:zs("Expression")},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0}})}),Zs("ArrayPattern",{visitor:["elements","typeAnnotation"],builder:["elements"],aliases:["Pattern","PatternLike","LVal"],fields:Object.assign({},ci,{elements:{validate:Js(Ys("array"),qs(Xs("null","PatternLike")))},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0}})}),Zs("ArrowFunctionExpression",{builder:["params","body","async"],visitor:["params","body","returnType","typeParameters"],aliases:["Scopable","Function","BlockParent","FunctionParent","Expression","Pureish"],fields:Object.assign({},ti,{},ri,{expression:{validate:Ys("boolean")},body:{validate:zs("BlockStatement","Expression")}})}),Zs("ClassBody",{visitor:["body"],fields:{body:{validate:Js(Ys("array"),qs(zs("ClassMethod","ClassPrivateMethod","ClassProperty","ClassPrivateProperty","TSDeclareMethod","TSIndexSignature")))}}}),Zs("ClassExpression",{builder:["id","superClass","body","decorators"],visitor:["id","body","superClass","mixins","typeParameters","superTypeParameters","implements","decorators"],aliases:["Scopable","Class","Expression","Pureish"],fields:{id:{validate:zs("Identifier"),optional:!0},typeParameters:{validate:zs("TypeParameterDeclaration","TSTypeParameterDeclaration","Noop"),optional:!0},body:{validate:zs("ClassBody")},superClass:{optional:!0,validate:zs("Expression")},superTypeParameters:{validate:zs("TypeParameterInstantiation","TSTypeParameterInstantiation"),optional:!0},implements:{validate:Js(Ys("array"),qs(zs("TSExpressionWithTypeArguments","ClassImplements"))),optional:!0},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0}}}),Zs("ClassDeclaration",{inherits:"ClassExpression",aliases:["Scopable","Class","Statement","Declaration","Pureish"],fields:{declare:{validate:Ys("boolean"),optional:!0},abstract:{validate:Ys("boolean"),optional:!0}},validate:(ii=zs("Identifier"),function(e,t,r){es.env.BABEL_TYPES_8_BREAKING&&(os("ExportDefaultDeclaration",e)||ii(r,"id",r.id))})}),Zs("ExportAllDeclaration",{visitor:["source"],aliases:["Statement","Declaration","ModuleDeclaration","ExportDeclaration"],fields:{source:{validate:zs("StringLiteral")}}}),Zs("ExportDefaultDeclaration",{visitor:["declaration"],aliases:["Statement","Declaration","ModuleDeclaration","ExportDeclaration"],fields:{declaration:{validate:zs("FunctionDeclaration","TSDeclareFunction","ClassDeclaration","Expression")}}}),Zs("ExportNamedDeclaration",{visitor:["declaration","specifiers","source"],aliases:["Statement","Declaration","ModuleDeclaration","ExportDeclaration"],fields:{declaration:{optional:!0,validate:Js(zs("Declaration"),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING&&r&&e.specifiers.length)throw new TypeError("Only declaration or specifiers is allowed on ExportNamedDeclaration")}),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING&&r&&e.source)throw new TypeError("Cannot export a declaration from a source")}))},specifiers:{default:[],validate:Js(Ys("array"),qs((oi=zs("ExportSpecifier","ExportDefaultSpecifier","ExportNamespaceSpecifier"),ui=zs("ExportSpecifier"),es.env.BABEL_TYPES_8_BREAKING?function(e,t,r){(e.source?oi:ui)(e,t,r)}:oi)))},source:{validate:zs("StringLiteral"),optional:!0},exportKind:Gs(Ks("type","value"))}}),Zs("ExportSpecifier",{visitor:["local","exported"],aliases:["ModuleSpecifier"],fields:{local:{validate:zs("Identifier")},exported:{validate:zs("Identifier")}}}),Zs("ForOfStatement",{visitor:["left","right","body"],builder:["left","right","body","await"],aliases:["Scopable","Statement","For","BlockParent","Loop","ForXStatement"],fields:{left:{validate:function(){if(!es.env.BABEL_TYPES_8_BREAKING)return zs("VariableDeclaration","LVal");var e=zs("VariableDeclaration"),t=zs("Identifier","MemberExpression","ArrayPattern","ObjectPattern");return function(r,n,a){os("VariableDeclaration",a)?e(r,n,a):t(r,n,a)}}()},right:{validate:zs("Expression")},body:{validate:zs("Statement")},await:{default:!1}}}),Zs("ImportDeclaration",{visitor:["specifiers","source"],aliases:["Statement","Declaration","ModuleDeclaration"],fields:{specifiers:{validate:Js(Ys("array"),qs(zs("ImportSpecifier","ImportDefaultSpecifier","ImportNamespaceSpecifier")))},source:{validate:zs("StringLiteral")},importKind:{validate:Ks("type","typeof","value"),optional:!0}}}),Zs("ImportDefaultSpecifier",{visitor:["local"],aliases:["ModuleSpecifier"],fields:{local:{validate:zs("Identifier")}}}),Zs("ImportNamespaceSpecifier",{visitor:["local"],aliases:["ModuleSpecifier"],fields:{local:{validate:zs("Identifier")}}}),Zs("ImportSpecifier",{visitor:["local","imported"],aliases:["ModuleSpecifier"],fields:{local:{validate:zs("Identifier")},imported:{validate:zs("Identifier")},importKind:{validate:Ks("type","typeof"),optional:!0}}}),Zs("MetaProperty",{visitor:["meta","property"],aliases:["Expression"],fields:{meta:{validate:Js(zs("Identifier"),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING){var n;switch(r.name){case"function":n="sent";break;case"new":n="target";break;case"import":n="meta"}if(!os("Identifier",e.property,{name:n}))throw new TypeError("Unrecognised MetaProperty")}}))},property:{validate:zs("Identifier")}}});var li={abstract:{validate:Ys("boolean"),optional:!0},accessibility:{validate:Ks("public","private","protected"),optional:!0},static:{default:!1},computed:{default:!1},optional:{validate:Ys("boolean"),optional:!0},key:{validate:Js(function(){var e=zs("Identifier","StringLiteral","NumericLiteral"),t=zs("Expression");return function(r,n,a){(r.computed?t:e)(r,n,a)}}(),zs("Identifier","StringLiteral","NumericLiteral","Expression"))}},pi=Object.assign({},ti,{},li,{kind:{validate:Ks("get","set","method","constructor"),default:"method"},access:{validate:Js(Ys("string"),Ks("public","private","protected")),optional:!0},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0}});Zs("ClassMethod",{aliases:["Function","Scopable","BlockParent","FunctionParent","Method"],builder:["kind","key","params","body","computed","static","generator","async"],visitor:["key","params","body","decorators","returnType","typeParameters"],fields:Object.assign({},pi,{},ri,{body:{validate:zs("BlockStatement")}})}),Zs("ObjectPattern",{visitor:["properties","typeAnnotation","decorators"],builder:["properties"],aliases:["Pattern","PatternLike","LVal"],fields:Object.assign({},ci,{properties:{validate:Js(Ys("array"),qs(zs("RestElement","ObjectProperty")))}})}),Zs("SpreadElement",{visitor:["argument"],aliases:["UnaryLike"],deprecatedAlias:"SpreadProperty",fields:{argument:{validate:zs("Expression")}}}),Zs("Super",{aliases:["Expression"]}),Zs("TaggedTemplateExpression",{visitor:["tag","quasi"],aliases:["Expression"],fields:{tag:{validate:zs("Expression")},quasi:{validate:zs("TemplateLiteral")},typeParameters:{validate:zs("TypeParameterInstantiation","TSTypeParameterInstantiation"),optional:!0}}}),Zs("TemplateElement",{builder:["value","tail"],fields:{value:{validate:function(e){function t(t,r,n){for(var a=[],s=0,i=Object.keys(e);s<i.length;s++){var o=i[s];try{js(t,o,n[o],e[o])}catch(e){if(e instanceof TypeError){a.push(e.message);continue}throw e}}if(a.length)throw new TypeError("Property "+r+" of "+t.type+" expected to have the following:\n"+a.join("\n"))}return t.shapeOf=e,t}({raw:{validate:Ys("string")},cooked:{validate:Ys("string"),optional:!0}})},tail:{default:!1}}}),Zs("TemplateLiteral",{visitor:["quasis","expressions"],aliases:["Expression","Literal"],fields:{quasis:{validate:Js(Ys("array"),qs(zs("TemplateElement")))},expressions:{validate:Js(Ys("array"),qs(zs("Expression")),(function(e,t,r){if(e.quasis.length!==r.length+1)throw new TypeError("Number of "+e.type+" quasis should be exactly one more than the number of expressions.\nExpected "+(r.length+1)+" quasis but got "+e.quasis.length)}))}}}),Zs("YieldExpression",{builder:["argument","delegate"],visitor:["argument"],aliases:["Expression","Terminatorless"],fields:{delegate:{validate:Js(Ys("boolean"),(function(e,t,r){if(es.env.BABEL_TYPES_8_BREAKING&&r&&!e.argument)throw new TypeError("Property delegate of YieldExpression cannot be true if there is no argument")})),default:!1},argument:{optional:!0,validate:zs("Expression")}}});var di=function(e,t){void 0===t&&(t="TypeParameterDeclaration"),Zs(e,{builder:["id","typeParameters","extends","body"],visitor:["id","typeParameters","extends","mixins","implements","body"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us("Identifier"),typeParameters:Vs(t),extends:Gs(Ws("InterfaceExtends")),mixins:Gs(Ws("InterfaceExtends")),implements:Gs(Ws("ClassImplements")),body:Us("ObjectTypeAnnotation")}})};Zs("AnyTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("ArrayTypeAnnotation",{visitor:["elementType"],aliases:["Flow","FlowType"],fields:{elementType:Us("FlowType")}}),Zs("BooleanTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("BooleanLiteralTypeAnnotation",{builder:["value"],aliases:["Flow","FlowType"],fields:{value:Ms(Ys("boolean"))}}),Zs("NullLiteralTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("ClassImplements",{visitor:["id","typeParameters"],aliases:["Flow"],fields:{id:Us("Identifier"),typeParameters:Vs("TypeParameterInstantiation")}}),di("DeclareClass"),Zs("DeclareFunction",{visitor:["id"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us("Identifier"),predicate:Vs("DeclaredPredicate")}}),di("DeclareInterface"),Zs("DeclareModule",{builder:["id","body","kind"],visitor:["id","body"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us(["Identifier","StringLiteral"]),body:Us("BlockStatement"),kind:Gs(Ks("CommonJS","ES"))}}),Zs("DeclareModuleExports",{visitor:["typeAnnotation"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{typeAnnotation:Us("TypeAnnotation")}}),Zs("DeclareTypeAlias",{visitor:["id","typeParameters","right"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us("Identifier"),typeParameters:Vs("TypeParameterDeclaration"),right:Us("FlowType")}}),Zs("DeclareOpaqueType",{visitor:["id","typeParameters","supertype"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us("Identifier"),typeParameters:Vs("TypeParameterDeclaration"),supertype:Vs("FlowType")}}),Zs("DeclareVariable",{visitor:["id"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us("Identifier")}}),Zs("DeclareExportDeclaration",{visitor:["declaration","specifiers","source"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{declaration:Vs("Flow"),specifiers:Gs(Ws(["ExportSpecifier","ExportNamespaceSpecifier"])),source:Vs("StringLiteral"),default:Gs(Ys("boolean"))}}),Zs("DeclareExportAllDeclaration",{visitor:["source"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{source:Us("StringLiteral"),exportKind:Gs(Ks("type","value"))}}),Zs("DeclaredPredicate",{visitor:["value"],aliases:["Flow","FlowPredicate"],fields:{value:Us("Flow")}}),Zs("ExistsTypeAnnotation",{aliases:["Flow","FlowType"]}),Zs("FunctionTypeAnnotation",{visitor:["typeParameters","params","rest","returnType"],aliases:["Flow","FlowType"],fields:{typeParameters:Vs("TypeParameterDeclaration"),params:Ms(Ws("FunctionTypeParam")),rest:Vs("FunctionTypeParam"),returnType:Us("FlowType")}}),Zs("FunctionTypeParam",{visitor:["name","typeAnnotation"],aliases:["Flow"],fields:{name:Vs("Identifier"),typeAnnotation:Us("FlowType"),optional:Gs(Ys("boolean"))}}),Zs("GenericTypeAnnotation",{visitor:["id","typeParameters"],aliases:["Flow","FlowType"],fields:{id:Us(["Identifier","QualifiedTypeIdentifier"]),typeParameters:Vs("TypeParameterInstantiation")}}),Zs("InferredPredicate",{aliases:["Flow","FlowPredicate"]}),Zs("InterfaceExtends",{visitor:["id","typeParameters"],aliases:["Flow"],fields:{id:Us(["Identifier","QualifiedTypeIdentifier"]),typeParameters:Vs("TypeParameterInstantiation")}}),di("InterfaceDeclaration"),Zs("InterfaceTypeAnnotation",{visitor:["extends","body"],aliases:["Flow","FlowType"],fields:{extends:Gs(Ws("InterfaceExtends")),body:Us("ObjectTypeAnnotation")}}),Zs("IntersectionTypeAnnotation",{visitor:["types"],aliases:["Flow","FlowType"],fields:{types:Ms(Ws("FlowType"))}}),Zs("MixedTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("EmptyTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("NullableTypeAnnotation",{visitor:["typeAnnotation"],aliases:["Flow","FlowType"],fields:{typeAnnotation:Us("FlowType")}}),Zs("NumberLiteralTypeAnnotation",{builder:["value"],aliases:["Flow","FlowType"],fields:{value:Ms(Ys("number"))}}),Zs("NumberTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("ObjectTypeAnnotation",{visitor:["properties","indexers","callProperties","internalSlots"],aliases:["Flow","FlowType"],builder:["properties","indexers","callProperties","internalSlots","exact"],fields:{properties:Ms(Ws(["ObjectTypeProperty","ObjectTypeSpreadProperty"])),indexers:Gs(Ws("ObjectTypeIndexer")),callProperties:Gs(Ws("ObjectTypeCallProperty")),internalSlots:Gs(Ws("ObjectTypeInternalSlot")),exact:{validate:Ys("boolean"),default:!1},inexact:Gs(Ys("boolean"))}}),Zs("ObjectTypeInternalSlot",{visitor:["id","value","optional","static","method"],aliases:["Flow","UserWhitespacable"],fields:{id:Us("Identifier"),value:Us("FlowType"),optional:Ms(Ys("boolean")),static:Ms(Ys("boolean")),method:Ms(Ys("boolean"))}}),Zs("ObjectTypeCallProperty",{visitor:["value"],aliases:["Flow","UserWhitespacable"],fields:{value:Us("FlowType"),static:Ms(Ys("boolean"))}}),Zs("ObjectTypeIndexer",{visitor:["id","key","value","variance"],aliases:["Flow","UserWhitespacable"],fields:{id:Vs("Identifier"),key:Us("FlowType"),value:Us("FlowType"),static:Ms(Ys("boolean")),variance:Vs("Variance")}}),Zs("ObjectTypeProperty",{visitor:["key","value","variance"],aliases:["Flow","UserWhitespacable"],fields:{key:Us(["Identifier","StringLiteral"]),value:Us("FlowType"),kind:Ms(Ks("init","get","set")),static:Ms(Ys("boolean")),proto:Ms(Ys("boolean")),optional:Ms(Ys("boolean")),variance:Vs("Variance")}}),Zs("ObjectTypeSpreadProperty",{visitor:["argument"],aliases:["Flow","UserWhitespacable"],fields:{argument:Us("FlowType")}}),Zs("OpaqueType",{visitor:["id","typeParameters","supertype","impltype"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us("Identifier"),typeParameters:Vs("TypeParameterDeclaration"),supertype:Vs("FlowType"),impltype:Us("FlowType")}}),Zs("QualifiedTypeIdentifier",{visitor:["id","qualification"],aliases:["Flow"],fields:{id:Us("Identifier"),qualification:Us(["Identifier","QualifiedTypeIdentifier"])}}),Zs("StringLiteralTypeAnnotation",{builder:["value"],aliases:["Flow","FlowType"],fields:{value:Ms(Ys("string"))}}),Zs("StringTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("ThisTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("TupleTypeAnnotation",{visitor:["types"],aliases:["Flow","FlowType"],fields:{types:Ms(Ws("FlowType"))}}),Zs("TypeofTypeAnnotation",{visitor:["argument"],aliases:["Flow","FlowType"],fields:{argument:Us("FlowType")}}),Zs("TypeAlias",{visitor:["id","typeParameters","right"],aliases:["Flow","FlowDeclaration","Statement","Declaration"],fields:{id:Us("Identifier"),typeParameters:Vs("TypeParameterDeclaration"),right:Us("FlowType")}}),Zs("TypeAnnotation",{aliases:["Flow"],visitor:["typeAnnotation"],fields:{typeAnnotation:Us("FlowType")}}),Zs("TypeCastExpression",{visitor:["expression","typeAnnotation"],aliases:["Flow","ExpressionWrapper","Expression"],fields:{expression:Us("Expression"),typeAnnotation:Us("TypeAnnotation")}}),Zs("TypeParameter",{aliases:["Flow"],visitor:["bound","default","variance"],fields:{name:Ms(Ys("string")),bound:Vs("TypeAnnotation"),default:Vs("FlowType"),variance:Vs("Variance")}}),Zs("TypeParameterDeclaration",{aliases:["Flow"],visitor:["params"],fields:{params:Ms(Ws("TypeParameter"))}}),Zs("TypeParameterInstantiation",{aliases:["Flow"],visitor:["params"],fields:{params:Ms(Ws("FlowType"))}}),Zs("UnionTypeAnnotation",{visitor:["types"],aliases:["Flow","FlowType"],fields:{types:Ms(Ws("FlowType"))}}),Zs("Variance",{aliases:["Flow"],builder:["kind"],fields:{kind:Ms(Ks("minus","plus"))}}),Zs("VoidTypeAnnotation",{aliases:["Flow","FlowType","FlowBaseAnnotation"]}),Zs("EnumDeclaration",{aliases:["Statement","Declaration"],visitor:["id","body"],fields:{id:Us("Identifier"),body:Us(["EnumBooleanBody","EnumNumberBody","EnumStringBody","EnumSymbolBody"])}}),Zs("EnumBooleanBody",{aliases:["EnumBody"],visitor:["members"],fields:{explicit:Ms(Ys("boolean")),members:Hs("EnumBooleanMember")}}),Zs("EnumNumberBody",{aliases:["EnumBody"],visitor:["members"],fields:{explicit:Ms(Ys("boolean")),members:Hs("EnumNumberMember")}}),Zs("EnumStringBody",{aliases:["EnumBody"],visitor:["members"],fields:{explicit:Ms(Ys("boolean")),members:Hs(["EnumStringMember","EnumDefaultedMember"])}}),Zs("EnumSymbolBody",{aliases:["EnumBody"],visitor:["members"],fields:{members:Hs("EnumDefaultedMember")}}),Zs("EnumBooleanMember",{aliases:["EnumMember"],visitor:["id"],fields:{id:Us("Identifier"),init:Us("BooleanLiteral")}}),Zs("EnumNumberMember",{aliases:["EnumMember"],visitor:["id","init"],fields:{id:Us("Identifier"),init:Us("NumericLiteral")}}),Zs("EnumStringMember",{aliases:["EnumMember"],visitor:["id","init"],fields:{id:Us("Identifier"),init:Us("StringLiteral")}}),Zs("EnumDefaultedMember",{aliases:["EnumMember"],visitor:["id"],fields:{id:Us("Identifier")}}),Zs("JSXAttribute",{visitor:["name","value"],aliases:["JSX","Immutable"],fields:{name:{validate:zs("JSXIdentifier","JSXNamespacedName")},value:{optional:!0,validate:zs("JSXElement","JSXFragment","StringLiteral","JSXExpressionContainer")}}}),Zs("JSXClosingElement",{visitor:["name"],aliases:["JSX","Immutable"],fields:{name:{validate:zs("JSXIdentifier","JSXMemberExpression","JSXNamespacedName")}}}),Zs("JSXElement",{builder:["openingElement","closingElement","children","selfClosing"],visitor:["openingElement","children","closingElement"],aliases:["JSX","Immutable","Expression"],fields:{openingElement:{validate:zs("JSXOpeningElement")},closingElement:{optional:!0,validate:zs("JSXClosingElement")},children:{validate:Js(Ys("array"),qs(zs("JSXText","JSXExpressionContainer","JSXSpreadChild","JSXElement","JSXFragment")))}}}),Zs("JSXEmptyExpression",{aliases:["JSX"]}),Zs("JSXExpressionContainer",{visitor:["expression"],aliases:["JSX","Immutable"],fields:{expression:{validate:zs("Expression","JSXEmptyExpression")}}}),Zs("JSXSpreadChild",{visitor:["expression"],aliases:["JSX","Immutable"],fields:{expression:{validate:zs("Expression")}}}),Zs("JSXIdentifier",{builder:["name"],aliases:["JSX"],fields:{name:{validate:Ys("string")}}}),Zs("JSXMemberExpression",{visitor:["object","property"],aliases:["JSX"],fields:{object:{validate:zs("JSXMemberExpression","JSXIdentifier")},property:{validate:zs("JSXIdentifier")}}}),Zs("JSXNamespacedName",{visitor:["namespace","name"],aliases:["JSX"],fields:{namespace:{validate:zs("JSXIdentifier")},name:{validate:zs("JSXIdentifier")}}}),Zs("JSXOpeningElement",{builder:["name","attributes","selfClosing"],visitor:["name","attributes"],aliases:["JSX","Immutable"],fields:{name:{validate:zs("JSXIdentifier","JSXMemberExpression","JSXNamespacedName")},selfClosing:{default:!1},attributes:{validate:Js(Ys("array"),qs(zs("JSXAttribute","JSXSpreadAttribute")))},typeParameters:{validate:zs("TypeParameterInstantiation","TSTypeParameterInstantiation"),optional:!0}}}),Zs("JSXSpreadAttribute",{visitor:["argument"],aliases:["JSX"],fields:{argument:{validate:zs("Expression")}}}),Zs("JSXText",{aliases:["JSX","Immutable"],builder:["value"],fields:{value:{validate:Ys("string")}}}),Zs("JSXFragment",{builder:["openingFragment","closingFragment","children"],visitor:["openingFragment","children","closingFragment"],aliases:["JSX","Immutable","Expression"],fields:{openingFragment:{validate:zs("JSXOpeningFragment")},closingFragment:{validate:zs("JSXClosingFragment")},children:{validate:Js(Ys("array"),qs(zs("JSXText","JSXExpressionContainer","JSXSpreadChild","JSXElement","JSXFragment")))}}}),Zs("JSXOpeningFragment",{aliases:["JSX","Immutable"]}),Zs("JSXClosingFragment",{aliases:["JSX","Immutable"]});for(var fi=["Identifier","StringLiteral","Expression","Statement","Declaration","BlockStatement","ClassBody","Pattern"],hi={Declaration:["Statement"],Pattern:["PatternLike","LVal"]},mi=0,yi=fi;mi<yi.length;mi++){var gi=yi[mi],vi=Fs[gi];vi&&vi.length&&(hi[gi]=vi)}var bi={};Object.keys(hi).forEach((function(e){hi[e].forEach((function(t){Object.hasOwnProperty.call(bi,t)||(bi[t]=[]),bi[t].push(e)}))})),Zs("Noop",{visitor:[]}),Zs("Placeholder",{visitor:[],builder:["expectedNode","name"],fields:{name:{validate:zs("Identifier")},expectedNode:{validate:Ks.apply(void 0,fi)}}}),Zs("V8IntrinsicIdentifier",{builder:["name"],fields:{name:{validate:Ys("string")}}}),Zs("ArgumentPlaceholder",{}),Zs("AwaitExpression",{builder:["argument"],visitor:["argument"],aliases:["Expression","Terminatorless"],fields:{argument:{validate:zs("Expression")}}}),Zs("BindExpression",{visitor:["object","callee"],aliases:["Expression"],fields:es.env.BABEL_TYPES_8_BREAKING?{object:{validate:zs("Expression")},callee:{validate:zs("Expression")}}:{}}),Zs("ClassProperty",{visitor:["key","value","typeAnnotation","decorators"],builder:["key","value","typeAnnotation","decorators","computed","static"],aliases:["Property"],fields:Object.assign({},li,{value:{validate:zs("Expression"),optional:!0},definite:{validate:Ys("boolean"),optional:!0},typeAnnotation:{validate:zs("TypeAnnotation","TSTypeAnnotation","Noop"),optional:!0},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0},readonly:{validate:Ys("boolean"),optional:!0},declare:{validate:Ys("boolean"),optional:!0}})}),Zs("OptionalMemberExpression",{builder:["object","property","computed","optional"],visitor:["object","property"],aliases:["Expression"],fields:{object:{validate:zs("Expression")},property:{validate:function(){var e=zs("Identifier"),t=zs("Expression");return function(r,n,a){(r.computed?t:e)(r,n,a)}}()},computed:{default:!1},optional:{validate:Ys("boolean")}}}),Zs("PipelineTopicExpression",{builder:["expression"],visitor:["expression"],fields:{expression:{validate:zs("Expression")}}}),Zs("PipelineBareFunction",{builder:["callee"],visitor:["callee"],fields:{callee:{validate:zs("Expression")}}}),Zs("PipelinePrimaryTopicReference",{aliases:["Expression"]}),Zs("OptionalCallExpression",{visitor:["callee","arguments","typeParameters","typeArguments"],builder:["callee","arguments","optional"],aliases:["Expression"],fields:{callee:{validate:zs("Expression")},arguments:{validate:Js(Ys("array"),qs(zs("Expression","SpreadElement","JSXNamespacedName")))},optional:{validate:Ys("boolean")},typeArguments:{validate:zs("TypeParameterInstantiation"),optional:!0},typeParameters:{validate:zs("TSTypeParameterInstantiation"),optional:!0}}}),Zs("ClassPrivateProperty",{visitor:["key","value","decorators"],builder:["key","value","decorators"],aliases:["Property","Private"],fields:{key:{validate:zs("PrivateName")},value:{validate:zs("Expression"),optional:!0},decorators:{validate:Js(Ys("array"),qs(zs("Decorator"))),optional:!0}}}),Zs("ClassPrivateMethod",{builder:["kind","key","params","body","static"],visitor:["key","params","body","decorators","returnType","typeParameters"],aliases:["Function","Scopable","BlockParent","FunctionParent","Method","Private"],fields:Object.assign({},pi,{key:{validate:zs("PrivateName")},body:{validate:zs("BlockStatement")}})}),Zs("Import",{aliases:["Expression"]}),Zs("Decorator",{visitor:["expression"],fields:{expression:{validate:zs("Expression")}}}),Zs("DoExpression",{visitor:["body"],aliases:["Expression"],fields:{body:{validate:zs("BlockStatement")}}}),Zs("ExportDefaultSpecifier",{visitor:["exported"],aliases:["ModuleSpecifier"],fields:{exported:{validate:zs("Identifier")}}}),Zs("ExportNamespaceSpecifier",{visitor:["exported"],aliases:["ModuleSpecifier"],fields:{exported:{validate:zs("Identifier")}}}),Zs("PrivateName",{visitor:["id"],aliases:["Private"],fields:{id:{validate:zs("Identifier")}}}),Zs("BigIntLiteral",{builder:["value"],fields:{value:{validate:Ys("string")}},aliases:["Expression","Pureish","Literal","Immutable"]});var xi=Ys("boolean"),Ei={returnType:{validate:zs("TSTypeAnnotation","Noop"),optional:!0},typeParameters:{validate:zs("TSTypeParameterDeclaration","Noop"),optional:!0}};Zs("TSParameterProperty",{aliases:["LVal"],visitor:["parameter"],fields:{accessibility:{validate:Ks("public","private","protected"),optional:!0},readonly:{validate:Ys("boolean"),optional:!0},parameter:{validate:zs("Identifier","AssignmentPattern")}}}),Zs("TSDeclareFunction",{aliases:["Statement","Declaration"],visitor:["id","typeParameters","params","returnType"],fields:Object.assign({},ni,{},Ei)}),Zs("TSDeclareMethod",{visitor:["decorators","key","typeParameters","params","returnType"],fields:Object.assign({},pi,{},Ei)}),Zs("TSQualifiedName",{aliases:["TSEntityName"],visitor:["left","right"],fields:{left:Us("TSEntityName"),right:Us("Identifier")}});var Ai={typeParameters:Vs("TSTypeParameterDeclaration"),parameters:Hs(["Identifier","RestElement"]),typeAnnotation:Vs("TSTypeAnnotation")},wi={aliases:["TSTypeElement"],visitor:["typeParameters","parameters","typeAnnotation"],fields:Ai};Zs("TSCallSignatureDeclaration",wi),Zs("TSConstructSignatureDeclaration",wi);var Si={key:Us("Expression"),computed:Ms(xi),optional:Gs(xi)};Zs("TSPropertySignature",{aliases:["TSTypeElement"],visitor:["key","typeAnnotation","initializer"],fields:Object.assign({},Si,{readonly:Gs(xi),typeAnnotation:Vs("TSTypeAnnotation"),initializer:Vs("Expression")})}),Zs("TSMethodSignature",{aliases:["TSTypeElement"],visitor:["key","typeParameters","parameters","typeAnnotation"],fields:Object.assign({},Ai,{},Si)}),Zs("TSIndexSignature",{aliases:["TSTypeElement"],visitor:["parameters","typeAnnotation"],fields:{readonly:Gs(xi),parameters:Hs("Identifier"),typeAnnotation:Vs("TSTypeAnnotation")}});for(var Di=0,Ci=["TSAnyKeyword","TSBooleanKeyword","TSBigIntKeyword","TSNeverKeyword","TSNullKeyword","TSNumberKeyword","TSObjectKeyword","TSStringKeyword","TSSymbolKeyword","TSUndefinedKeyword","TSUnknownKeyword","TSVoidKeyword"];Di<Ci.length;Di++){Zs(Ci[Di],{aliases:["TSType"],visitor:[],fields:{}})}Zs("TSThisType",{aliases:["TSType"],visitor:[],fields:{}});var Ti={aliases:["TSType"],visitor:["typeParameters","parameters","typeAnnotation"],fields:Ai};Zs("TSFunctionType",Ti),Zs("TSConstructorType",Ti),Zs("TSTypeReference",{aliases:["TSType"],visitor:["typeName","typeParameters"],fields:{typeName:Us("TSEntityName"),typeParameters:Vs("TSTypeParameterInstantiation")}}),Zs("TSTypePredicate",{aliases:["TSType"],visitor:["parameterName","typeAnnotation"],builder:["parameterName","typeAnnotation","asserts"],fields:{parameterName:Us(["Identifier","TSThisType"]),typeAnnotation:Vs("TSTypeAnnotation"),asserts:Gs(xi)}}),Zs("TSTypeQuery",{aliases:["TSType"],visitor:["exprName"],fields:{exprName:Us(["TSEntityName","TSImportType"])}}),Zs("TSTypeLiteral",{aliases:["TSType"],visitor:["members"],fields:{members:Hs("TSTypeElement")}}),Zs("TSArrayType",{aliases:["TSType"],visitor:["elementType"],fields:{elementType:Us("TSType")}}),Zs("TSTupleType",{aliases:["TSType"],visitor:["elementTypes"],fields:{elementTypes:Hs("TSType")}}),Zs("TSOptionalType",{aliases:["TSType"],visitor:["typeAnnotation"],fields:{typeAnnotation:Us("TSType")}}),Zs("TSRestType",{aliases:["TSType"],visitor:["typeAnnotation"],fields:{typeAnnotation:Us("TSType")}});var ji={aliases:["TSType"],visitor:["types"],fields:{types:Hs("TSType")}};Zs("TSUnionType",ji),Zs("TSIntersectionType",ji),Zs("TSConditionalType",{aliases:["TSType"],visitor:["checkType","extendsType","trueType","falseType"],fields:{checkType:Us("TSType"),extendsType:Us("TSType"),trueType:Us("TSType"),falseType:Us("TSType")}}),Zs("TSInferType",{aliases:["TSType"],visitor:["typeParameter"],fields:{typeParameter:Us("TSTypeParameter")}}),Zs("TSParenthesizedType",{aliases:["TSType"],visitor:["typeAnnotation"],fields:{typeAnnotation:Us("TSType")}}),Zs("TSTypeOperator",{aliases:["TSType"],visitor:["typeAnnotation"],fields:{operator:Ms(Ys("string")),typeAnnotation:Us("TSType")}}),Zs("TSIndexedAccessType",{aliases:["TSType"],visitor:["objectType","indexType"],fields:{objectType:Us("TSType"),indexType:Us("TSType")}}),Zs("TSMappedType",{aliases:["TSType"],visitor:["typeParameter","typeAnnotation"],fields:{readonly:Gs(xi),typeParameter:Us("TSTypeParameter"),optional:Gs(xi),typeAnnotation:Vs("TSType")}}),Zs("TSLiteralType",{aliases:["TSType"],visitor:["literal"],fields:{literal:Us(["NumericLiteral","StringLiteral","BooleanLiteral"])}}),Zs("TSExpressionWithTypeArguments",{aliases:["TSType"],visitor:["expression","typeParameters"],fields:{expression:Us("TSEntityName"),typeParameters:Vs("TSTypeParameterInstantiation")}}),Zs("TSInterfaceDeclaration",{aliases:["Statement","Declaration"],visitor:["id","typeParameters","extends","body"],fields:{declare:Gs(xi),id:Us("Identifier"),typeParameters:Vs("TSTypeParameterDeclaration"),extends:Gs(Ws("TSExpressionWithTypeArguments")),body:Us("TSInterfaceBody")}}),Zs("TSInterfaceBody",{visitor:["body"],fields:{body:Hs("TSTypeElement")}}),Zs("TSTypeAliasDeclaration",{aliases:["Statement","Declaration"],visitor:["id","typeParameters","typeAnnotation"],fields:{declare:Gs(xi),id:Us("Identifier"),typeParameters:Vs("TSTypeParameterDeclaration"),typeAnnotation:Us("TSType")}}),Zs("TSAsExpression",{aliases:["Expression"],visitor:["expression","typeAnnotation"],fields:{expression:Us("Expression"),typeAnnotation:Us("TSType")}}),Zs("TSTypeAssertion",{aliases:["Expression"],visitor:["typeAnnotation","expression"],fields:{typeAnnotation:Us("TSType"),expression:Us("Expression")}}),Zs("TSEnumDeclaration",{aliases:["Statement","Declaration"],visitor:["id","members"],fields:{declare:Gs(xi),const:Gs(xi),id:Us("Identifier"),members:Hs("TSEnumMember"),initializer:Vs("Expression")}}),Zs("TSEnumMember",{visitor:["id","initializer"],fields:{id:Us(["Identifier","StringLiteral"]),initializer:Vs("Expression")}}),Zs("TSModuleDeclaration",{aliases:["Statement","Declaration"],visitor:["id","body"],fields:{declare:Gs(xi),global:Gs(xi),id:Us(["Identifier","StringLiteral"]),body:Us(["TSModuleBlock","TSModuleDeclaration"])}}),Zs("TSModuleBlock",{aliases:["Scopable","Block","BlockParent"],visitor:["body"],fields:{body:Hs("Statement")}}),Zs("TSImportType",{aliases:["TSType"],visitor:["argument","qualifier","typeParameters"],fields:{argument:Us("StringLiteral"),qualifier:Vs("TSEntityName"),typeParameters:Vs("TSTypeParameterInstantiation")}}),Zs("TSImportEqualsDeclaration",{aliases:["Statement"],visitor:["id","moduleReference"],fields:{isExport:Ms(xi),id:Us("Identifier"),moduleReference:Us(["TSEntityName","TSExternalModuleReference"])}}),Zs("TSExternalModuleReference",{visitor:["expression"],fields:{expression:Us("StringLiteral")}}),Zs("TSNonNullExpression",{aliases:["Expression"],visitor:["expression"],fields:{expression:Us("Expression")}}),Zs("TSExportAssignment",{aliases:["Statement"],visitor:["expression"],fields:{expression:Us("Expression")}}),Zs("TSNamespaceExportDeclaration",{aliases:["Statement"],visitor:["id"],fields:{id:Us("Identifier")}}),Zs("TSTypeAnnotation",{visitor:["typeAnnotation"],fields:{typeAnnotation:{validate:zs("TSType")}}}),Zs("TSTypeParameterInstantiation",{visitor:["params"],fields:{params:{validate:Js(Ys("array"),qs(zs("TSType")))}}}),Zs("TSTypeParameterDeclaration",{visitor:["params"],fields:{params:{validate:Js(Ys("array"),qs(zs("TSTypeParameter")))}}}),Zs("TSTypeParameter",{builder:["constraint","default","name"],visitor:["constraint","default"],fields:{name:{validate:Ys("string")},constraint:{validate:zs("TSType"),optional:!0},default:{validate:zs("TSType"),optional:!0}}}),ja(ks),ja(Fs),ja(_s),ja(Is),ja(Bs),ja(Os),ja(hi),ja(bi);var Pi=Object.keys(ks).concat(Object.keys(_s)).concat(Object.keys(Os));function ki(e){for(var t=arguments.length,r=new Array(t>1?t-1:0),n=1;n<t;n++)r[n-1]=arguments[n];var a=Bs[e],s=r.length;if(s>a.length)throw new Error(e+": Too many arguments passed. Received "+s+" but can receive no more than "+a.length);var i={type:e},o=0;a.forEach((function(t){var n,a=Is[e][t];o<s&&(n=r[o]),void 0===n&&(n=Da(a.default)),i[t]=n,o++}));for(var u=0,c=Object.keys(i);u<c.length;u++){var l=c[u];Ts(i,l,i[l])}return i}function Fi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ArrayExpression"].concat(t))}function _i(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["AssignmentExpression"].concat(t))}function Ii(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BinaryExpression"].concat(t))}function Bi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["InterpreterDirective"].concat(t))}function Oi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Directive"].concat(t))}function Ni(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DirectiveLiteral"].concat(t))}function Ri(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BlockStatement"].concat(t))}function Mi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BreakStatement"].concat(t))}function Li(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["CallExpression"].concat(t))}function Ui(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["CatchClause"].concat(t))}function Gi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ConditionalExpression"].concat(t))}function Vi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ContinueStatement"].concat(t))}function Wi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DebuggerStatement"].concat(t))}function Hi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DoWhileStatement"].concat(t))}function qi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EmptyStatement"].concat(t))}function Ki(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExpressionStatement"].concat(t))}function zi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["File"].concat(t))}function Xi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ForInStatement"].concat(t))}function Yi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ForStatement"].concat(t))}function Ji(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["FunctionDeclaration"].concat(t))}function $i(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["FunctionExpression"].concat(t))}function Qi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Identifier"].concat(t))}function Zi(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["IfStatement"].concat(t))}function eo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["LabeledStatement"].concat(t))}function to(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["StringLiteral"].concat(t))}function ro(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["NumericLiteral"].concat(t))}function no(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["NullLiteral"].concat(t))}function ao(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BooleanLiteral"].concat(t))}function so(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["RegExpLiteral"].concat(t))}function io(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["LogicalExpression"].concat(t))}function oo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["MemberExpression"].concat(t))}function uo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["NewExpression"].concat(t))}function co(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Program"].concat(t))}function lo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectExpression"].concat(t))}function po(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectMethod"].concat(t))}function fo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectProperty"].concat(t))}function ho(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["RestElement"].concat(t))}function mo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ReturnStatement"].concat(t))}function yo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["SequenceExpression"].concat(t))}function go(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ParenthesizedExpression"].concat(t))}function vo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["SwitchCase"].concat(t))}function bo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["SwitchStatement"].concat(t))}function xo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ThisExpression"].concat(t))}function Eo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ThrowStatement"].concat(t))}function Ao(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TryStatement"].concat(t))}function wo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["UnaryExpression"].concat(t))}function So(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["UpdateExpression"].concat(t))}function Do(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["VariableDeclaration"].concat(t))}function Co(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["VariableDeclarator"].concat(t))}function To(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["WhileStatement"].concat(t))}function jo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["WithStatement"].concat(t))}function Po(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["AssignmentPattern"].concat(t))}function ko(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ArrayPattern"].concat(t))}function Fo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ArrowFunctionExpression"].concat(t))}function _o(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassBody"].concat(t))}function Io(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassExpression"].concat(t))}function Bo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassDeclaration"].concat(t))}function Oo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExportAllDeclaration"].concat(t))}function No(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExportDefaultDeclaration"].concat(t))}function Ro(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExportNamedDeclaration"].concat(t))}function Mo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExportSpecifier"].concat(t))}function Lo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ForOfStatement"].concat(t))}function Uo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ImportDeclaration"].concat(t))}function Go(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ImportDefaultSpecifier"].concat(t))}function Vo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ImportNamespaceSpecifier"].concat(t))}function Wo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ImportSpecifier"].concat(t))}function Ho(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["MetaProperty"].concat(t))}function qo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassMethod"].concat(t))}function Ko(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectPattern"].concat(t))}function zo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["SpreadElement"].concat(t))}function Xo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Super"].concat(t))}function Yo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TaggedTemplateExpression"].concat(t))}function Jo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TemplateElement"].concat(t))}function $o(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TemplateLiteral"].concat(t))}function Qo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["YieldExpression"].concat(t))}function Zo(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["AnyTypeAnnotation"].concat(t))}function eu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ArrayTypeAnnotation"].concat(t))}function tu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BooleanTypeAnnotation"].concat(t))}function ru(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BooleanLiteralTypeAnnotation"].concat(t))}function nu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["NullLiteralTypeAnnotation"].concat(t))}function au(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassImplements"].concat(t))}function su(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareClass"].concat(t))}function iu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareFunction"].concat(t))}function ou(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareInterface"].concat(t))}function uu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareModule"].concat(t))}function cu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareModuleExports"].concat(t))}function lu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareTypeAlias"].concat(t))}function pu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareOpaqueType"].concat(t))}function du(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareVariable"].concat(t))}function fu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareExportDeclaration"].concat(t))}function hu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclareExportAllDeclaration"].concat(t))}function mu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DeclaredPredicate"].concat(t))}function yu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExistsTypeAnnotation"].concat(t))}function gu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["FunctionTypeAnnotation"].concat(t))}function vu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["FunctionTypeParam"].concat(t))}function bu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["GenericTypeAnnotation"].concat(t))}function xu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["InferredPredicate"].concat(t))}function Eu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["InterfaceExtends"].concat(t))}function Au(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["InterfaceDeclaration"].concat(t))}function wu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["InterfaceTypeAnnotation"].concat(t))}function Su(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["IntersectionTypeAnnotation"].concat(t))}function Du(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["MixedTypeAnnotation"].concat(t))}function Cu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EmptyTypeAnnotation"].concat(t))}function Tu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["NullableTypeAnnotation"].concat(t))}function ju(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["NumberLiteralTypeAnnotation"].concat(t))}function Pu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["NumberTypeAnnotation"].concat(t))}function ku(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectTypeAnnotation"].concat(t))}function Fu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectTypeInternalSlot"].concat(t))}function _u(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectTypeCallProperty"].concat(t))}function Iu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectTypeIndexer"].concat(t))}function Bu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectTypeProperty"].concat(t))}function Ou(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ObjectTypeSpreadProperty"].concat(t))}function Nu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["OpaqueType"].concat(t))}function Ru(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["QualifiedTypeIdentifier"].concat(t))}function Mu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["StringLiteralTypeAnnotation"].concat(t))}function Lu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["StringTypeAnnotation"].concat(t))}function Uu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ThisTypeAnnotation"].concat(t))}function Gu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TupleTypeAnnotation"].concat(t))}function Vu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TypeofTypeAnnotation"].concat(t))}function Wu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TypeAlias"].concat(t))}function Hu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TypeAnnotation"].concat(t))}function qu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TypeCastExpression"].concat(t))}function Ku(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TypeParameter"].concat(t))}function zu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TypeParameterDeclaration"].concat(t))}function Xu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TypeParameterInstantiation"].concat(t))}function Yu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["UnionTypeAnnotation"].concat(t))}function Ju(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Variance"].concat(t))}function $u(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["VoidTypeAnnotation"].concat(t))}function Qu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumDeclaration"].concat(t))}function Zu(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumBooleanBody"].concat(t))}function ec(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumNumberBody"].concat(t))}function tc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumStringBody"].concat(t))}function rc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumSymbolBody"].concat(t))}function nc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumBooleanMember"].concat(t))}function ac(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumNumberMember"].concat(t))}function sc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumStringMember"].concat(t))}function ic(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["EnumDefaultedMember"].concat(t))}function oc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXAttribute"].concat(t))}function uc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXClosingElement"].concat(t))}function cc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXElement"].concat(t))}function lc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXEmptyExpression"].concat(t))}function pc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXExpressionContainer"].concat(t))}function dc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXSpreadChild"].concat(t))}function fc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXIdentifier"].concat(t))}function hc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXMemberExpression"].concat(t))}function mc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXNamespacedName"].concat(t))}function yc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXOpeningElement"].concat(t))}function gc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXSpreadAttribute"].concat(t))}function vc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXText"].concat(t))}function bc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXFragment"].concat(t))}function xc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXOpeningFragment"].concat(t))}function Ec(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["JSXClosingFragment"].concat(t))}function Ac(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Noop"].concat(t))}function wc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Placeholder"].concat(t))}function Sc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["V8IntrinsicIdentifier"].concat(t))}function Dc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ArgumentPlaceholder"].concat(t))}function Cc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["AwaitExpression"].concat(t))}function Tc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BindExpression"].concat(t))}function jc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassProperty"].concat(t))}function Pc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["OptionalMemberExpression"].concat(t))}function kc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["PipelineTopicExpression"].concat(t))}function Fc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["PipelineBareFunction"].concat(t))}function _c(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["PipelinePrimaryTopicReference"].concat(t))}function Ic(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["OptionalCallExpression"].concat(t))}function Bc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassPrivateProperty"].concat(t))}function Oc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ClassPrivateMethod"].concat(t))}function Nc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Import"].concat(t))}function Rc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["Decorator"].concat(t))}function Mc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["DoExpression"].concat(t))}function Lc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExportDefaultSpecifier"].concat(t))}function Uc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["ExportNamespaceSpecifier"].concat(t))}function Gc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["PrivateName"].concat(t))}function Vc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["BigIntLiteral"].concat(t))}function Wc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSParameterProperty"].concat(t))}function Hc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSDeclareFunction"].concat(t))}function qc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSDeclareMethod"].concat(t))}function Kc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSQualifiedName"].concat(t))}function zc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSCallSignatureDeclaration"].concat(t))}function Xc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSConstructSignatureDeclaration"].concat(t))}function Yc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSPropertySignature"].concat(t))}function Jc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSMethodSignature"].concat(t))}function $c(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSIndexSignature"].concat(t))}function Qc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSAnyKeyword"].concat(t))}function Zc(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSBooleanKeyword"].concat(t))}function el(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSBigIntKeyword"].concat(t))}function tl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSNeverKeyword"].concat(t))}function rl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSNullKeyword"].concat(t))}function nl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSNumberKeyword"].concat(t))}function al(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSObjectKeyword"].concat(t))}function sl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSStringKeyword"].concat(t))}function il(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSSymbolKeyword"].concat(t))}function ol(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSUndefinedKeyword"].concat(t))}function ul(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSUnknownKeyword"].concat(t))}function cl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSVoidKeyword"].concat(t))}function ll(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSThisType"].concat(t))}function pl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSFunctionType"].concat(t))}function dl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSConstructorType"].concat(t))}function fl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeReference"].concat(t))}function hl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypePredicate"].concat(t))}function ml(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeQuery"].concat(t))}function yl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeLiteral"].concat(t))}function gl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSArrayType"].concat(t))}function vl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTupleType"].concat(t))}function bl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSOptionalType"].concat(t))}function xl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSRestType"].concat(t))}function El(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSUnionType"].concat(t))}function Al(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSIntersectionType"].concat(t))}function wl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSConditionalType"].concat(t))}function Sl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSInferType"].concat(t))}function Dl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSParenthesizedType"].concat(t))}function Cl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeOperator"].concat(t))}function Tl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSIndexedAccessType"].concat(t))}function jl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSMappedType"].concat(t))}function Pl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSLiteralType"].concat(t))}function kl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSExpressionWithTypeArguments"].concat(t))}function Fl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSInterfaceDeclaration"].concat(t))}function _l(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSInterfaceBody"].concat(t))}function Il(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeAliasDeclaration"].concat(t))}function Bl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSAsExpression"].concat(t))}function Ol(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeAssertion"].concat(t))}function Nl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSEnumDeclaration"].concat(t))}function Rl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSEnumMember"].concat(t))}function Ml(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSModuleDeclaration"].concat(t))}function Ll(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSModuleBlock"].concat(t))}function Ul(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSImportType"].concat(t))}function Gl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSImportEqualsDeclaration"].concat(t))}function Vl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSExternalModuleReference"].concat(t))}function Wl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSNonNullExpression"].concat(t))}function Hl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSExportAssignment"].concat(t))}function ql(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSNamespaceExportDeclaration"].concat(t))}function Kl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeAnnotation"].concat(t))}function zl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeParameterInstantiation"].concat(t))}function Xl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeParameterDeclaration"].concat(t))}function Yl(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return ki.apply(void 0,["TSTypeParameter"].concat(t))}function Jl(){console.trace("The node type NumberLiteral has been renamed to NumericLiteral");for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return Jl.apply(void 0,["NumberLiteral"].concat(t))}function $l(){console.trace("The node type RegexLiteral has been renamed to RegExpLiteral");for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return $l.apply(void 0,["RegexLiteral"].concat(t))}function Ql(){console.trace("The node type RestProperty has been renamed to RestElement");for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return Ql.apply(void 0,["RestProperty"].concat(t))}function Zl(){console.trace("The node type SpreadProperty has been renamed to SpreadElement");for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return Zl.apply(void 0,["SpreadProperty"].concat(t))}function ep(e,t){for(var r=e.value.split(/\r\n|\n|\r/),n=0,a=0;a<r.length;a++)r[a].match(/[^ \t]/)&&(n=a);for(var s="",i=0;i<r.length;i++){var o=r[i],u=0===i,c=i===r.length-1,l=i===n,p=o.replace(/\t/g," ");u||(p=p.replace(/^[ ]+/,"")),c||(p=p.replace(/[ ]+$/,"")),p&&(l||(p+=" "),s+=p)}s&&t.push(to(s))}function tp(e){return!(!e||!ks[e.type])}function rp(e,t,r){if(!os(e,t,r))throw new Error('Expected type "'+e+'" with option '+JSON.stringify(r)+', but instead got "'+t.type+'".')}function np(e,t){void 0===t&&(t={}),rp("Identifier",e,t)}function ap(e,t){void 0===t&&(t={}),rp("RestElement",e,t)}function sp(e){if("string"===e)return Lu();if("number"===e)return Pu();if("undefined"===e)return $u();if("boolean"===e)return tu();if("function"===e)return bu(Qi("Function"));if("object"===e)return bu(Qi("Object"));if("symbol"===e)return bu(Qi("Symbol"));throw new Error("Invalid typeof value")}function ip(e){for(var t={},r={},n=[],a=[],s=0;s<e.length;s++){var i=e[s];if(i&&!(a.indexOf(i)>=0)){if(me(i))return[i];if(At(i))r[i.type]=i;else if(je(i))n.indexOf(i.types)<0&&(e=e.concat(i.types),n.push(i.types));else if(be(i)){var o=i.id.name;if(t[o]){var u=t[o];u.typeParameters?i.typeParameters&&(u.typeParameters.params=ip(u.typeParameters.params.concat(i.typeParameters.params))):u=i.typeParameters}else t[o]=i}else a.push(i)}}for(var c=0,l=Object.keys(r);c<l.length;c++){var p=l[c];a.push(r[p])}for(var d=0,f=Object.keys(t);d<f.length;d++){var h=f[d];a.push(t[h])}return a}function op(e){var t=ip(e);return 1===t.length?t[0]:Yu(t)}var up=Function.call.bind(Object.prototype.hasOwnProperty);function cp(e,t){return e&&"string"==typeof e.type&&"CommentLine"!==e.type&&"CommentBlock"!==e.type?pp(e,t):e}function lp(e,t){return Array.isArray(e)?e.map((function(e){return cp(e,t)})):cp(e,t)}function pp(e,t){if(void 0===t&&(t=!0),!e)return e;var r=e.type,n={type:r};if("Identifier"===r)n.name=e.name,up(e,"optional")&&"boolean"==typeof e.optional&&(n.optional=e.optional),up(e,"typeAnnotation")&&(n.typeAnnotation=t?lp(e.typeAnnotation,!0):e.typeAnnotation);else{if(!up(Is,r))throw new Error('Unknown node type: "'+r+'"');for(var a=0,s=Object.keys(Is[r]);a<s.length;a++){var i=s[a];up(e,i)&&(n[i]=t?lp(e[i],!0):e[i])}}return up(e,"loc")&&(n.loc=e.loc),up(e,"leadingComments")&&(n.leadingComments=e.leadingComments),up(e,"innerComments")&&(n.innerComments=e.innerComments),up(e,"trailingComments")&&(n.trailingComments=e.trailingComments),up(e,"extra")&&(n.extra=Object.assign({},e.extra)),n}function dp(e){return pp(e,!1)}function fp(e,t,r){if(!r||!e)return e;var n=t+"Comments";return e[n]?e[n]="leading"===t?r.concat(e[n]):e[n].concat(r):e[n]=r,e}function hp(e,t,r,n){return fp(e,t,[{type:n?"CommentLine":"CommentBlock",value:r}])}var mp=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this};var yp=function(e){return this.__data__.has(e)};function gp(e){var t=-1,r=null==e?0:e.length;for(this.__data__=new Mr;++t<r;)this.add(e[t])}gp.prototype.add=gp.prototype.push=mp,gp.prototype.has=yp;var vp=gp;var bp=function(e,t,r,n){for(var a=e.length,s=r+(n?1:-1);n?s--:++s<a;)if(t(e[s],s,e))return s;return-1};var xp=function(e){return e!=e};var Ep=function(e,t,r){for(var n=r-1,a=e.length;++n<a;)if(e[n]===t)return n;return-1};var Ap=function(e,t,r){return t==t?Ep(e,t,r):bp(e,xp,r)};var wp=function(e,t){return!!(null==e?0:e.length)&&Ap(e,t,0)>-1};var Sp=function(e,t,r){for(var n=-1,a=null==e?0:e.length;++n<a;)if(r(t,e[n]))return!0;return!1};var Dp=function(e,t){return e.has(t)};var Cp=function(){};var Tp=function(e){var t=-1,r=Array(e.size);return e.forEach((function(e){r[++t]=e})),r},jp=zn&&1/Tp(new zn([,-0]))[1]==1/0?function(e){return new zn(e)}:Cp;var Pp=function(e,t,r){var n=-1,a=wp,s=e.length,i=!0,o=[],u=o;if(r)i=!1,a=Sp;else if(s>=200){var c=t?null:jp(e);if(c)return Tp(c);i=!1,a=Dp,u=new vp}else u=t?[]:o;e:for(;++n<s;){var l=e[n],p=t?t(l):l;if(l=r||0!==l?l:0,i&&p==p){for(var d=u.length;d--;)if(u[d]===p)continue e;t&&u.push(p),o.push(l)}else a(u,p,r)||(u!==o&&u.push(p),o.push(l))}return o};var kp=function(e){return e&&e.length?Pp(e):[]};function Fp(e,t,r){t&&r&&(t[e]=kp([].concat(t[e],r[e]).filter(Boolean)))}function _p(e,t){Fp("innerComments",e,t)}function Ip(e,t){Fp("leadingComments",e,t)}function Bp(e,t){Fp("trailingComments",e,t)}function Op(e,t){return Bp(e,t),Ip(e,t),_p(e,t),e}function Np(e){return ls.forEach((function(t){e[t]=null})),e}var Rp=_s.Expression,Mp=_s.Binary,Lp=_s.Scopable,Up=_s.BlockParent,Gp=_s.Block,Vp=_s.Statement,Wp=_s.Terminatorless,Hp=_s.CompletionStatement,qp=_s.Conditional,Kp=_s.Loop,zp=_s.While,Xp=_s.ExpressionWrapper,Yp=_s.For,Jp=_s.ForXStatement,$p=_s.Function,Qp=_s.FunctionParent,Zp=_s.Pureish,ed=_s.Declaration,td=_s.PatternLike,rd=_s.LVal,nd=_s.TSEntityName,ad=_s.Literal,sd=_s.Immutable,id=_s.UserWhitespacable,od=_s.Method,ud=_s.ObjectMember,cd=_s.Property,ld=_s.UnaryLike,pd=_s.Pattern,dd=_s.Class,fd=_s.ModuleDeclaration,hd=_s.ExportDeclaration,md=_s.ModuleSpecifier,yd=_s.Flow,gd=_s.FlowType,vd=_s.FlowBaseAnnotation,bd=_s.FlowDeclaration,xd=_s.FlowPredicate,Ed=_s.EnumBody,Ad=_s.EnumMember,wd=_s.JSX,Sd=_s.Private,Dd=_s.TSTypeElement,Cd=_s.TSType;function Td(e,t){if(p(e))return e;var r=[];return g(e)?r=[]:(st(e)||(e=lt(t)?mo(e):Ki(e)),r=[e]),Ri(r)}function jd(e,t){return void 0===t&&(t="body"),e[t]=Td(e[t],e)}function Pd(e,t){if(void 0===t&&(t=!0),"string"!=typeof e)return!1;if(t){if(as.keyword.isReservedWordES6(e,!0))return!1;if("await"===e)return!1}return as.keyword.isIdentifierNameES6(e)}function kd(e){return Pd(e=(e=(e=(e+="").replace(/[^a-zA-Z0-9$_]/g,"-")).replace(/^[-0-9]+/,"")).replace(/[-\s]+(.)?/g,(function(e,t){return t?t.toUpperCase():""})))||(e="_"+e),e||"_"}function Fd(e){return"eval"!==(e=kd(e))&&"arguments"!==e||(e="_"+e),e}function _d(e,t){return void 0===t&&(t=e.key||e.property),!e.computed&&S(t)&&(t=to(t.name)),t}function Id(e){if(v(e)&&(e=e.expression),rt(e))return e;if(gt(e)?e.type="ClassExpression":lt(e)&&(e.type="FunctionExpression"),!rt(e))throw new Error("cannot turn "+e.type+" to an expression");return e}function Bd(e,t,r){if(e){var n=ks[e.type];if(n){t(e,r=r||{});var a=n,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=e[o];if(Array.isArray(u)){var c=u,l=Array.isArray(c),p=0;for(c=l?c:c[Symbol.iterator]();;){var d;if(l){if(p>=c.length)break;d=c[p++]}else{if((p=c.next()).done)break;d=p.value}Bd(d,t,r)}}else Bd(u,t,r)}}}}var Od=["tokens","start","end","loc","raw","rawValue"],Nd=ls.concat(["comments"]).concat(Od);function Rd(e,t){void 0===t&&(t={});var r=t.preserveComments?Od:Nd,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s;null!=e[i]&&(e[i]=void 0)}for(var o=0,u=Object.keys(e);o<u.length;o++){var c=u[o];"_"===c[0]&&null!=e[c]&&(e[c]=void 0)}var l=Object.getOwnPropertySymbols(e),p=Array.isArray(l),d=0;for(l=p?l:l[Symbol.iterator]();;){var f;if(p){if(d>=l.length)break;f=l[d++]}else{if((d=l.next()).done)break;f=d.value}e[f]=null}}function Md(e,t){return Bd(e,Rd,t),e}function Ld(e,t){var r;return void 0===t&&(t=e.key),"method"===e.kind?Ld.increment()+"":(r=S(t)?t.name:T(t)?JSON.stringify(t.value):JSON.stringify(Md(pp(t))),e.computed&&(r="["+r+"]"),e.static&&(r="static:"+r),r)}function Ud(e,t,r){for(var n=[].concat(e),a=Object.create(null);n.length;){var s=n.shift();if(s){var i=Ud.keys[s.type];if(S(s))t?(a[s.name]=a[s.name]||[]).push(s):a[s.name]=s;else if(bt(s))dt(s.declaration)&&n.push(s.declaration);else{if(r){if(A(s)){n.push(s.id);continue}if(w(s))continue}if(i)for(var o=0;o<i.length;o++){var u=i[o];s[u]&&(n=n.concat(s[u]))}}}}return a}function Gd(e,t){if(e&&e.length){var r=[],n=function e(t,r,n){var a=[],s=!0,i=t,o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}var l=c;if(s=!1,rt(l))a.push(l);else if(v(l))a.push(l.expression);else if(q(l)){if("var"!==l.kind)return;for(var d=0,f=l.declarations;d<f.length;d++){for(var h=f[d],m=Ud(h),y=0,b=Object.keys(m);y<b.length;y++){var x=b[y];n.push({kind:l.kind,id:pp(m[x])})}h.init&&a.push(_i("=",h.id,h.init))}s=!0}else if(D(l)){var E=l.consequent?e([l.consequent],r,n):r.buildUndefinedNode(),A=l.alternate?e([l.alternate],r,n):r.buildUndefinedNode();if(!E||!A)return;a.push(Gi(l.test,E,A))}else if(p(l)){var w=e(l.body,r,n);if(!w)return;a.push(w)}else{if(!g(l))return;s=!0}}return s&&a.push(r.buildUndefinedNode()),1===a.length?a[0]:yo(a)}(e,t,r);if(n){for(var a=0,s=r;a<s.length;a++){var i=s[a];t.push(i)}return n}}}function Vd(e,t){if(st(e))return e;var r,n=!1;if(gt(e))n=!0,r="ClassDeclaration";else if(lt(e))n=!0,r="FunctionDeclaration";else if(c(e))return Ki(e);if(n&&!e.id&&(r=!1),!r){if(t)return!1;throw new Error("cannot turn "+e.type+" to a statement")}return e.type=r,e}Ld.uid=0,Ld.increment=function(){return Ld.uid>=Number.MAX_SAFE_INTEGER?Ld.uid=0:Ld.uid++},Ud.keys={DeclareClass:["id"],DeclareFunction:["id"],DeclareModule:["id"],DeclareVariable:["id"],DeclareInterface:["id"],DeclareTypeAlias:["id"],DeclareOpaqueType:["id"],InterfaceDeclaration:["id"],TypeAlias:["id"],OpaqueType:["id"],CatchClause:["param"],LabeledStatement:["label"],UnaryExpression:["argument"],AssignmentExpression:["left"],ImportSpecifier:["local"],ImportNamespaceSpecifier:["local"],ImportDefaultSpecifier:["local"],ImportDeclaration:["specifiers"],ExportSpecifier:["exported"],ExportNamespaceSpecifier:["exported"],ExportDefaultSpecifier:["exported"],FunctionDeclaration:["id","params"],FunctionExpression:["id","params"],ArrowFunctionExpression:["params"],ObjectMethod:["params"],ClassMethod:["params"],ForInStatement:["left"],ForOfStatement:["left"],ClassDeclaration:["id"],ClassExpression:["id"],RestElement:["argument"],UpdateExpression:["argument"],ObjectProperty:["value"],AssignmentPattern:["left"],ArrayPattern:["elements"],ObjectPattern:["properties"],VariableDeclaration:["declarations"],VariableDeclarator:["id"]};var Wd=Function.prototype,Hd=Object.prototype,qd=Wd.toString,Kd=Hd.hasOwnProperty,zd=qd.call(Object);var Xd=function(e){if(!Yr(e)||"[object Object]"!=rr(e))return!1;var t=Ln(e);if(null===t)return!0;var r=Kd.call(t,"constructor")&&t.constructor;return"function"==typeof r&&r instanceof r&&qd.call(r)==zd};var Yd=function(e){return Yr(e)&&"[object RegExp]"==rr(e)},Jd=pn&&pn.isRegExp,$d=Jd?ln(Jd):Yd;function Qd(e){if(void 0===e)return Qi("undefined");if(!0===e||!1===e)return ao(e);if(null===e)return no();if("string"==typeof e)return to(e);if("number"==typeof e){var t;if(Number.isFinite(e))t=ro(Math.abs(e));else t=Ii("/",Number.isNaN(e)?ro(0):ro(1),ro(0));return(e<0||Object.is(e,-0))&&(t=wo("-",t)),t}if($d(e))return so(e.source,e.toString().match(/\/([a-z]+|)$/)[1]);if(Array.isArray(e))return Fi(e.map(Qd));if(Xd(e)){for(var r=[],n=0,a=Object.keys(e);n<a.length;n++){var s=a[n],i=void 0;i=Pd(s)?Qi(s):to(s),r.push(fo(i,Qd(e[s])))}return lo(r)}throw new Error("don't know how to turn this value into a node")}function Zd(e,t,r){return void 0===r&&(r=!1),e.object=oo(e.object,e.property,e.computed),e.property=t,e.computed=!!r,e}function ef(e,t){if(!e||!t)return e;for(var r=0,n=Ss.optional;r<n.length;r++){var a=n[r];null==e[a]&&(e[a]=t[a])}for(var s=0,i=Object.keys(t);s<i.length;s++){var o=i[s];"_"===o[0]&&"__clone"!==o&&(e[o]=t[o])}for(var u=0,c=Ss.force;u<c.length;u++){var l=c[u];e[l]=t[l]}return Op(e,t),e}function tf(e,t){return Ud(e,t,!0)}function rf(e,t,r){"function"==typeof t&&(t={enter:t});var n=t;!function e(t,r,n,a,s){var i=ks[t.type];if(!i)return;r&&r(t,s,a);var o=i,u=Array.isArray(o),c=0;for(o=u?o:o[Symbol.iterator]();;){var l;if(u){if(c>=o.length)break;l=o[c++]}else{if((c=o.next()).done)break;l=c.value}var p=l,d=t[p];if(Array.isArray(d))for(var f=0;f<d.length;f++){var h=d[f];h&&(s.push({node:t,key:p,index:f}),e(h,r,n,a,s),s.pop())}else d&&(s.push({node:t,key:p}),e(d,r,n,a,s),s.pop())}n&&n(t,s,a)}(e,n.enter,n.exit,r,[])}function nf(e,t,r){if(r&&"Identifier"===e.type&&"ObjectProperty"===t.type&&"ObjectExpression"===r.type)return!1;var n=Ud.keys[t.type];if(n)for(var a=0;a<n.length;a++){var s=t[n[a]];if(Array.isArray(s)){if(s.indexOf(e)>=0)return!0}else if(s===e)return!0}return!1}function af(e){return q(e)&&("var"!==e.kind||e[Ds])}function sf(e){return A(e)||Z(e)||af(e)}function of(e,t,r){switch(t.type){case"MemberExpression":case"JSXMemberExpression":case"OptionalMemberExpression":return t.property===e?!!t.computed:t.object===e;case"VariableDeclarator":return t.init===e;case"ArrowFunctionExpression":return t.body===e;case"ExportSpecifier":return!t.source&&t.local===e;case"PrivateName":return!1;case"ClassMethod":case"ClassPrivateMethod":case"ObjectMethod":if(t.params.includes(e))return!1;case"ObjectProperty":case"ClassProperty":case"ClassPrivateProperty":return t.key===e?!!t.computed:t.value!==e||(!r||"ObjectPattern"!==r.type);case"ClassDeclaration":case"ClassExpression":return t.superClass===e;case"AssignmentExpression":case"AssignmentPattern":return t.right===e;case"LabeledStatement":case"CatchClause":case"RestElement":return!1;case"BreakStatement":case"ContinueStatement":return!1;case"FunctionDeclaration":case"FunctionExpression":return!1;case"ExportNamespaceSpecifier":case"ExportDefaultSpecifier":return!1;case"ImportDefaultSpecifier":case"ImportNamespaceSpecifier":case"ImportSpecifier":case"JSXAttribute":return!1;case"ObjectPattern":case"ArrayPattern":case"MetaProperty":return!1;case"ObjectTypeProperty":return t.key!==e;case"TSEnumMember":return t.id!==e;case"TSPropertySignature":return t.key!==e||!!t.computed}return!0}function uf(e,t){return(!p(e)||!lt(t,{body:e}))&&((!p(e)||!h(t,{body:e}))&&(!(!yt(e)||!lt(t))||at(e)))}var cf=new Set(["abstract","boolean","byte","char","double","enum","final","float","goto","implements","int","interface","long","native","package","private","protected","public","short","static","synchronized","throws","transient","volatile"]);function lf(e){return Pd(e)&&!cf.has(e)}function pf(e){return q(e,{kind:"var"})&&!e[Ds]}var df={isReactComponent:Dt,isCompatTag:function(e){return!!e&&/^[a-z]/.test(e)},buildChildren:function(e){for(var t=[],r=0;r<e.children.length;r++){var n=e.children[r];Me(n)?ep(n,t):(Ie(n)&&(n=n.expression),_e(n)||t.push(n))}return t}},ff=Object.freeze({__proto__:null,react:df,assertNode:function(e){if(!tp(e)){var t=e&&e.type||JSON.stringify(e);throw new TypeError('Not a valid node of type "'+t+'"')}},createTypeAnnotationBasedOnTypeof:sp,createUnionTypeAnnotation:op,cloneNode:pp,clone:dp,cloneDeep:function(e){return pp(e)},cloneWithoutLoc:function(e){var t=dp(e);return t.loc=null,t},addComment:hp,addComments:fp,inheritInnerComments:_p,inheritLeadingComments:Ip,inheritsComments:Op,inheritTrailingComments:Bp,removeComments:Np,ensureBlock:jd,toBindingIdentifierName:Fd,toBlock:Td,toComputedKey:_d,toExpression:Id,toIdentifier:kd,toKeyAlias:Ld,toSequenceExpression:Gd,toStatement:Vd,valueToNode:Qd,appendToMemberExpression:Zd,inherits:ef,prependToMemberExpression:function(e,t){return e.object=oo(t,e.object),e},removeProperties:Rd,removePropertiesDeep:Md,removeTypeDuplicates:ip,getBindingIdentifiers:Ud,getOuterBindingIdentifiers:tf,traverse:rf,traverseFast:Bd,shallowEqual:o,is:os,isBinding:nf,isBlockScoped:sf,isImmutable:function(e){return!!ss(e.type,"Immutable")||!!S(e)&&"undefined"===e.name},isLet:af,isNode:tp,isNodesEquivalent:function e(t,r){if("object"!=typeof t||"object"!=typeof r||null==t||null==r)return t===r;if(t.type!==r.type)return!1;for(var n=Object.keys(Is[t.type]||t.type),a=ks[t.type],s=0,i=n;s<i.length;s++){var o=i[s];if(typeof t[o]!=typeof r[o])return!1;if(null!=t[o]||null!=r[o]){if(null==t[o]||null==r[o])return!1;if(Array.isArray(t[o])){if(!Array.isArray(r[o]))return!1;if(t[o].length!==r[o].length)return!1;for(var u=0;u<t[o].length;u++)if(!e(t[o][u],r[o][u]))return!1}else if("object"!=typeof t[o]||a&&a.includes(o)){if(!e(t[o],r[o]))return!1}else for(var c=0,l=Object.keys(t[o]);c<l.length;c++){var p=l[c];if(t[o][p]!==r[o][p])return!1}}}return!0},isPlaceholderType:is,isReferenced:of,isScope:uf,isSpecifierDefault:function(e){return se(e)||S(e.imported||e.exported,{name:"default"})},isType:ss,isValidES3Identifier:lf,isValidIdentifier:Pd,isVar:pf,matchesPattern:wt,validate:Ts,buildMatchMemberExpression:St,assertArrayExpression:function(e,t){void 0===t&&(t={}),rp("ArrayExpression",e,t)},assertAssignmentExpression:function(e,t){void 0===t&&(t={}),rp("AssignmentExpression",e,t)},assertBinaryExpression:function(e,t){void 0===t&&(t={}),rp("BinaryExpression",e,t)},assertInterpreterDirective:function(e,t){void 0===t&&(t={}),rp("InterpreterDirective",e,t)},assertDirective:function(e,t){void 0===t&&(t={}),rp("Directive",e,t)},assertDirectiveLiteral:function(e,t){void 0===t&&(t={}),rp("DirectiveLiteral",e,t)},assertBlockStatement:function(e,t){void 0===t&&(t={}),rp("BlockStatement",e,t)},assertBreakStatement:function(e,t){void 0===t&&(t={}),rp("BreakStatement",e,t)},assertCallExpression:function(e,t){void 0===t&&(t={}),rp("CallExpression",e,t)},assertCatchClause:function(e,t){void 0===t&&(t={}),rp("CatchClause",e,t)},assertConditionalExpression:function(e,t){void 0===t&&(t={}),rp("ConditionalExpression",e,t)},assertContinueStatement:function(e,t){void 0===t&&(t={}),rp("ContinueStatement",e,t)},assertDebuggerStatement:function(e,t){void 0===t&&(t={}),rp("DebuggerStatement",e,t)},assertDoWhileStatement:function(e,t){void 0===t&&(t={}),rp("DoWhileStatement",e,t)},assertEmptyStatement:function(e,t){void 0===t&&(t={}),rp("EmptyStatement",e,t)},assertExpressionStatement:function(e,t){void 0===t&&(t={}),rp("ExpressionStatement",e,t)},assertFile:function(e,t){void 0===t&&(t={}),rp("File",e,t)},assertForInStatement:function(e,t){void 0===t&&(t={}),rp("ForInStatement",e,t)},assertForStatement:function(e,t){void 0===t&&(t={}),rp("ForStatement",e,t)},assertFunctionDeclaration:function(e,t){void 0===t&&(t={}),rp("FunctionDeclaration",e,t)},assertFunctionExpression:function(e,t){void 0===t&&(t={}),rp("FunctionExpression",e,t)},assertIdentifier:np,assertIfStatement:function(e,t){void 0===t&&(t={}),rp("IfStatement",e,t)},assertLabeledStatement:function(e,t){void 0===t&&(t={}),rp("LabeledStatement",e,t)},assertStringLiteral:function(e,t){void 0===t&&(t={}),rp("StringLiteral",e,t)},assertNumericLiteral:function(e,t){void 0===t&&(t={}),rp("NumericLiteral",e,t)},assertNullLiteral:function(e,t){void 0===t&&(t={}),rp("NullLiteral",e,t)},assertBooleanLiteral:function(e,t){void 0===t&&(t={}),rp("BooleanLiteral",e,t)},assertRegExpLiteral:function(e,t){void 0===t&&(t={}),rp("RegExpLiteral",e,t)},assertLogicalExpression:function(e,t){void 0===t&&(t={}),rp("LogicalExpression",e,t)},assertMemberExpression:function(e,t){void 0===t&&(t={}),rp("MemberExpression",e,t)},assertNewExpression:function(e,t){void 0===t&&(t={}),rp("NewExpression",e,t)},assertProgram:function(e,t){void 0===t&&(t={}),rp("Program",e,t)},assertObjectExpression:function(e,t){void 0===t&&(t={}),rp("ObjectExpression",e,t)},assertObjectMethod:function(e,t){void 0===t&&(t={}),rp("ObjectMethod",e,t)},assertObjectProperty:function(e,t){void 0===t&&(t={}),rp("ObjectProperty",e,t)},assertRestElement:ap,assertReturnStatement:function(e,t){void 0===t&&(t={}),rp("ReturnStatement",e,t)},assertSequenceExpression:function(e,t){void 0===t&&(t={}),rp("SequenceExpression",e,t)},assertParenthesizedExpression:function(e,t){void 0===t&&(t={}),rp("ParenthesizedExpression",e,t)},assertSwitchCase:function(e,t){void 0===t&&(t={}),rp("SwitchCase",e,t)},assertSwitchStatement:function(e,t){void 0===t&&(t={}),rp("SwitchStatement",e,t)},assertThisExpression:function(e,t){void 0===t&&(t={}),rp("ThisExpression",e,t)},assertThrowStatement:function(e,t){void 0===t&&(t={}),rp("ThrowStatement",e,t)},assertTryStatement:function(e,t){void 0===t&&(t={}),rp("TryStatement",e,t)},assertUnaryExpression:function(e,t){void 0===t&&(t={}),rp("UnaryExpression",e,t)},assertUpdateExpression:function(e,t){void 0===t&&(t={}),rp("UpdateExpression",e,t)},assertVariableDeclaration:function(e,t){void 0===t&&(t={}),rp("VariableDeclaration",e,t)},assertVariableDeclarator:function(e,t){void 0===t&&(t={}),rp("VariableDeclarator",e,t)},assertWhileStatement:function(e,t){void 0===t&&(t={}),rp("WhileStatement",e,t)},assertWithStatement:function(e,t){void 0===t&&(t={}),rp("WithStatement",e,t)},assertAssignmentPattern:function(e,t){void 0===t&&(t={}),rp("AssignmentPattern",e,t)},assertArrayPattern:function(e,t){void 0===t&&(t={}),rp("ArrayPattern",e,t)},assertArrowFunctionExpression:function(e,t){void 0===t&&(t={}),rp("ArrowFunctionExpression",e,t)},assertClassBody:function(e,t){void 0===t&&(t={}),rp("ClassBody",e,t)},assertClassExpression:function(e,t){void 0===t&&(t={}),rp("ClassExpression",e,t)},assertClassDeclaration:function(e,t){void 0===t&&(t={}),rp("ClassDeclaration",e,t)},assertExportAllDeclaration:function(e,t){void 0===t&&(t={}),rp("ExportAllDeclaration",e,t)},assertExportDefaultDeclaration:function(e,t){void 0===t&&(t={}),rp("ExportDefaultDeclaration",e,t)},assertExportNamedDeclaration:function(e,t){void 0===t&&(t={}),rp("ExportNamedDeclaration",e,t)},assertExportSpecifier:function(e,t){void 0===t&&(t={}),rp("ExportSpecifier",e,t)},assertForOfStatement:function(e,t){void 0===t&&(t={}),rp("ForOfStatement",e,t)},assertImportDeclaration:function(e,t){void 0===t&&(t={}),rp("ImportDeclaration",e,t)},assertImportDefaultSpecifier:function(e,t){void 0===t&&(t={}),rp("ImportDefaultSpecifier",e,t)},assertImportNamespaceSpecifier:function(e,t){void 0===t&&(t={}),rp("ImportNamespaceSpecifier",e,t)},assertImportSpecifier:function(e,t){void 0===t&&(t={}),rp("ImportSpecifier",e,t)},assertMetaProperty:function(e,t){void 0===t&&(t={}),rp("MetaProperty",e,t)},assertClassMethod:function(e,t){void 0===t&&(t={}),rp("ClassMethod",e,t)},assertObjectPattern:function(e,t){void 0===t&&(t={}),rp("ObjectPattern",e,t)},assertSpreadElement:function(e,t){void 0===t&&(t={}),rp("SpreadElement",e,t)},assertSuper:function(e,t){void 0===t&&(t={}),rp("Super",e,t)},assertTaggedTemplateExpression:function(e,t){void 0===t&&(t={}),rp("TaggedTemplateExpression",e,t)},assertTemplateElement:function(e,t){void 0===t&&(t={}),rp("TemplateElement",e,t)},assertTemplateLiteral:function(e,t){void 0===t&&(t={}),rp("TemplateLiteral",e,t)},assertYieldExpression:function(e,t){void 0===t&&(t={}),rp("YieldExpression",e,t)},assertAnyTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("AnyTypeAnnotation",e,t)},assertArrayTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("ArrayTypeAnnotation",e,t)},assertBooleanTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("BooleanTypeAnnotation",e,t)},assertBooleanLiteralTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("BooleanLiteralTypeAnnotation",e,t)},assertNullLiteralTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("NullLiteralTypeAnnotation",e,t)},assertClassImplements:function(e,t){void 0===t&&(t={}),rp("ClassImplements",e,t)},assertDeclareClass:function(e,t){void 0===t&&(t={}),rp("DeclareClass",e,t)},assertDeclareFunction:function(e,t){void 0===t&&(t={}),rp("DeclareFunction",e,t)},assertDeclareInterface:function(e,t){void 0===t&&(t={}),rp("DeclareInterface",e,t)},assertDeclareModule:function(e,t){void 0===t&&(t={}),rp("DeclareModule",e,t)},assertDeclareModuleExports:function(e,t){void 0===t&&(t={}),rp("DeclareModuleExports",e,t)},assertDeclareTypeAlias:function(e,t){void 0===t&&(t={}),rp("DeclareTypeAlias",e,t)},assertDeclareOpaqueType:function(e,t){void 0===t&&(t={}),rp("DeclareOpaqueType",e,t)},assertDeclareVariable:function(e,t){void 0===t&&(t={}),rp("DeclareVariable",e,t)},assertDeclareExportDeclaration:function(e,t){void 0===t&&(t={}),rp("DeclareExportDeclaration",e,t)},assertDeclareExportAllDeclaration:function(e,t){void 0===t&&(t={}),rp("DeclareExportAllDeclaration",e,t)},assertDeclaredPredicate:function(e,t){void 0===t&&(t={}),rp("DeclaredPredicate",e,t)},assertExistsTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("ExistsTypeAnnotation",e,t)},assertFunctionTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("FunctionTypeAnnotation",e,t)},assertFunctionTypeParam:function(e,t){void 0===t&&(t={}),rp("FunctionTypeParam",e,t)},assertGenericTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("GenericTypeAnnotation",e,t)},assertInferredPredicate:function(e,t){void 0===t&&(t={}),rp("InferredPredicate",e,t)},assertInterfaceExtends:function(e,t){void 0===t&&(t={}),rp("InterfaceExtends",e,t)},assertInterfaceDeclaration:function(e,t){void 0===t&&(t={}),rp("InterfaceDeclaration",e,t)},assertInterfaceTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("InterfaceTypeAnnotation",e,t)},assertIntersectionTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("IntersectionTypeAnnotation",e,t)},assertMixedTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("MixedTypeAnnotation",e,t)},assertEmptyTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("EmptyTypeAnnotation",e,t)},assertNullableTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("NullableTypeAnnotation",e,t)},assertNumberLiteralTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("NumberLiteralTypeAnnotation",e,t)},assertNumberTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("NumberTypeAnnotation",e,t)},assertObjectTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("ObjectTypeAnnotation",e,t)},assertObjectTypeInternalSlot:function(e,t){void 0===t&&(t={}),rp("ObjectTypeInternalSlot",e,t)},assertObjectTypeCallProperty:function(e,t){void 0===t&&(t={}),rp("ObjectTypeCallProperty",e,t)},assertObjectTypeIndexer:function(e,t){void 0===t&&(t={}),rp("ObjectTypeIndexer",e,t)},assertObjectTypeProperty:function(e,t){void 0===t&&(t={}),rp("ObjectTypeProperty",e,t)},assertObjectTypeSpreadProperty:function(e,t){void 0===t&&(t={}),rp("ObjectTypeSpreadProperty",e,t)},assertOpaqueType:function(e,t){void 0===t&&(t={}),rp("OpaqueType",e,t)},assertQualifiedTypeIdentifier:function(e,t){void 0===t&&(t={}),rp("QualifiedTypeIdentifier",e,t)},assertStringLiteralTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("StringLiteralTypeAnnotation",e,t)},assertStringTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("StringTypeAnnotation",e,t)},assertThisTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("ThisTypeAnnotation",e,t)},assertTupleTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("TupleTypeAnnotation",e,t)},assertTypeofTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("TypeofTypeAnnotation",e,t)},assertTypeAlias:function(e,t){void 0===t&&(t={}),rp("TypeAlias",e,t)},assertTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("TypeAnnotation",e,t)},assertTypeCastExpression:function(e,t){void 0===t&&(t={}),rp("TypeCastExpression",e,t)},assertTypeParameter:function(e,t){void 0===t&&(t={}),rp("TypeParameter",e,t)},assertTypeParameterDeclaration:function(e,t){void 0===t&&(t={}),rp("TypeParameterDeclaration",e,t)},assertTypeParameterInstantiation:function(e,t){void 0===t&&(t={}),rp("TypeParameterInstantiation",e,t)},assertUnionTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("UnionTypeAnnotation",e,t)},assertVariance:function(e,t){void 0===t&&(t={}),rp("Variance",e,t)},assertVoidTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("VoidTypeAnnotation",e,t)},assertEnumDeclaration:function(e,t){void 0===t&&(t={}),rp("EnumDeclaration",e,t)},assertEnumBooleanBody:function(e,t){void 0===t&&(t={}),rp("EnumBooleanBody",e,t)},assertEnumNumberBody:function(e,t){void 0===t&&(t={}),rp("EnumNumberBody",e,t)},assertEnumStringBody:function(e,t){void 0===t&&(t={}),rp("EnumStringBody",e,t)},assertEnumSymbolBody:function(e,t){void 0===t&&(t={}),rp("EnumSymbolBody",e,t)},assertEnumBooleanMember:function(e,t){void 0===t&&(t={}),rp("EnumBooleanMember",e,t)},assertEnumNumberMember:function(e,t){void 0===t&&(t={}),rp("EnumNumberMember",e,t)},assertEnumStringMember:function(e,t){void 0===t&&(t={}),rp("EnumStringMember",e,t)},assertEnumDefaultedMember:function(e,t){void 0===t&&(t={}),rp("EnumDefaultedMember",e,t)},assertJSXAttribute:function(e,t){void 0===t&&(t={}),rp("JSXAttribute",e,t)},assertJSXClosingElement:function(e,t){void 0===t&&(t={}),rp("JSXClosingElement",e,t)},assertJSXElement:function(e,t){void 0===t&&(t={}),rp("JSXElement",e,t)},assertJSXEmptyExpression:function(e,t){void 0===t&&(t={}),rp("JSXEmptyExpression",e,t)},assertJSXExpressionContainer:function(e,t){void 0===t&&(t={}),rp("JSXExpressionContainer",e,t)},assertJSXSpreadChild:function(e,t){void 0===t&&(t={}),rp("JSXSpreadChild",e,t)},assertJSXIdentifier:function(e,t){void 0===t&&(t={}),rp("JSXIdentifier",e,t)},assertJSXMemberExpression:function(e,t){void 0===t&&(t={}),rp("JSXMemberExpression",e,t)},assertJSXNamespacedName:function(e,t){void 0===t&&(t={}),rp("JSXNamespacedName",e,t)},assertJSXOpeningElement:function(e,t){void 0===t&&(t={}),rp("JSXOpeningElement",e,t)},assertJSXSpreadAttribute:function(e,t){void 0===t&&(t={}),rp("JSXSpreadAttribute",e,t)},assertJSXText:function(e,t){void 0===t&&(t={}),rp("JSXText",e,t)},assertJSXFragment:function(e,t){void 0===t&&(t={}),rp("JSXFragment",e,t)},assertJSXOpeningFragment:function(e,t){void 0===t&&(t={}),rp("JSXOpeningFragment",e,t)},assertJSXClosingFragment:function(e,t){void 0===t&&(t={}),rp("JSXClosingFragment",e,t)},assertNoop:function(e,t){void 0===t&&(t={}),rp("Noop",e,t)},assertPlaceholder:function(e,t){void 0===t&&(t={}),rp("Placeholder",e,t)},assertV8IntrinsicIdentifier:function(e,t){void 0===t&&(t={}),rp("V8IntrinsicIdentifier",e,t)},assertArgumentPlaceholder:function(e,t){void 0===t&&(t={}),rp("ArgumentPlaceholder",e,t)},assertAwaitExpression:function(e,t){void 0===t&&(t={}),rp("AwaitExpression",e,t)},assertBindExpression:function(e,t){void 0===t&&(t={}),rp("BindExpression",e,t)},assertClassProperty:function(e,t){void 0===t&&(t={}),rp("ClassProperty",e,t)},assertOptionalMemberExpression:function(e,t){void 0===t&&(t={}),rp("OptionalMemberExpression",e,t)},assertPipelineTopicExpression:function(e,t){void 0===t&&(t={}),rp("PipelineTopicExpression",e,t)},assertPipelineBareFunction:function(e,t){void 0===t&&(t={}),rp("PipelineBareFunction",e,t)},assertPipelinePrimaryTopicReference:function(e,t){void 0===t&&(t={}),rp("PipelinePrimaryTopicReference",e,t)},assertOptionalCallExpression:function(e,t){void 0===t&&(t={}),rp("OptionalCallExpression",e,t)},assertClassPrivateProperty:function(e,t){void 0===t&&(t={}),rp("ClassPrivateProperty",e,t)},assertClassPrivateMethod:function(e,t){void 0===t&&(t={}),rp("ClassPrivateMethod",e,t)},assertImport:function(e,t){void 0===t&&(t={}),rp("Import",e,t)},assertDecorator:function(e,t){void 0===t&&(t={}),rp("Decorator",e,t)},assertDoExpression:function(e,t){void 0===t&&(t={}),rp("DoExpression",e,t)},assertExportDefaultSpecifier:function(e,t){void 0===t&&(t={}),rp("ExportDefaultSpecifier",e,t)},assertExportNamespaceSpecifier:function(e,t){void 0===t&&(t={}),rp("ExportNamespaceSpecifier",e,t)},assertPrivateName:function(e,t){void 0===t&&(t={}),rp("PrivateName",e,t)},assertBigIntLiteral:function(e,t){void 0===t&&(t={}),rp("BigIntLiteral",e,t)},assertTSParameterProperty:function(e,t){void 0===t&&(t={}),rp("TSParameterProperty",e,t)},assertTSDeclareFunction:function(e,t){void 0===t&&(t={}),rp("TSDeclareFunction",e,t)},assertTSDeclareMethod:function(e,t){void 0===t&&(t={}),rp("TSDeclareMethod",e,t)},assertTSQualifiedName:function(e,t){void 0===t&&(t={}),rp("TSQualifiedName",e,t)},assertTSCallSignatureDeclaration:function(e,t){void 0===t&&(t={}),rp("TSCallSignatureDeclaration",e,t)},assertTSConstructSignatureDeclaration:function(e,t){void 0===t&&(t={}),rp("TSConstructSignatureDeclaration",e,t)},assertTSPropertySignature:function(e,t){void 0===t&&(t={}),rp("TSPropertySignature",e,t)},assertTSMethodSignature:function(e,t){void 0===t&&(t={}),rp("TSMethodSignature",e,t)},assertTSIndexSignature:function(e,t){void 0===t&&(t={}),rp("TSIndexSignature",e,t)},assertTSAnyKeyword:function(e,t){void 0===t&&(t={}),rp("TSAnyKeyword",e,t)},assertTSBooleanKeyword:function(e,t){void 0===t&&(t={}),rp("TSBooleanKeyword",e,t)},assertTSBigIntKeyword:function(e,t){void 0===t&&(t={}),rp("TSBigIntKeyword",e,t)},assertTSNeverKeyword:function(e,t){void 0===t&&(t={}),rp("TSNeverKeyword",e,t)},assertTSNullKeyword:function(e,t){void 0===t&&(t={}),rp("TSNullKeyword",e,t)},assertTSNumberKeyword:function(e,t){void 0===t&&(t={}),rp("TSNumberKeyword",e,t)},assertTSObjectKeyword:function(e,t){void 0===t&&(t={}),rp("TSObjectKeyword",e,t)},assertTSStringKeyword:function(e,t){void 0===t&&(t={}),rp("TSStringKeyword",e,t)},assertTSSymbolKeyword:function(e,t){void 0===t&&(t={}),rp("TSSymbolKeyword",e,t)},assertTSUndefinedKeyword:function(e,t){void 0===t&&(t={}),rp("TSUndefinedKeyword",e,t)},assertTSUnknownKeyword:function(e,t){void 0===t&&(t={}),rp("TSUnknownKeyword",e,t)},assertTSVoidKeyword:function(e,t){void 0===t&&(t={}),rp("TSVoidKeyword",e,t)},assertTSThisType:function(e,t){void 0===t&&(t={}),rp("TSThisType",e,t)},assertTSFunctionType:function(e,t){void 0===t&&(t={}),rp("TSFunctionType",e,t)},assertTSConstructorType:function(e,t){void 0===t&&(t={}),rp("TSConstructorType",e,t)},assertTSTypeReference:function(e,t){void 0===t&&(t={}),rp("TSTypeReference",e,t)},assertTSTypePredicate:function(e,t){void 0===t&&(t={}),rp("TSTypePredicate",e,t)},assertTSTypeQuery:function(e,t){void 0===t&&(t={}),rp("TSTypeQuery",e,t)},assertTSTypeLiteral:function(e,t){void 0===t&&(t={}),rp("TSTypeLiteral",e,t)},assertTSArrayType:function(e,t){void 0===t&&(t={}),rp("TSArrayType",e,t)},assertTSTupleType:function(e,t){void 0===t&&(t={}),rp("TSTupleType",e,t)},assertTSOptionalType:function(e,t){void 0===t&&(t={}),rp("TSOptionalType",e,t)},assertTSRestType:function(e,t){void 0===t&&(t={}),rp("TSRestType",e,t)},assertTSUnionType:function(e,t){void 0===t&&(t={}),rp("TSUnionType",e,t)},assertTSIntersectionType:function(e,t){void 0===t&&(t={}),rp("TSIntersectionType",e,t)},assertTSConditionalType:function(e,t){void 0===t&&(t={}),rp("TSConditionalType",e,t)},assertTSInferType:function(e,t){void 0===t&&(t={}),rp("TSInferType",e,t)},assertTSParenthesizedType:function(e,t){void 0===t&&(t={}),rp("TSParenthesizedType",e,t)},assertTSTypeOperator:function(e,t){void 0===t&&(t={}),rp("TSTypeOperator",e,t)},assertTSIndexedAccessType:function(e,t){void 0===t&&(t={}),rp("TSIndexedAccessType",e,t)},assertTSMappedType:function(e,t){void 0===t&&(t={}),rp("TSMappedType",e,t)},assertTSLiteralType:function(e,t){void 0===t&&(t={}),rp("TSLiteralType",e,t)},assertTSExpressionWithTypeArguments:function(e,t){void 0===t&&(t={}),rp("TSExpressionWithTypeArguments",e,t)},assertTSInterfaceDeclaration:function(e,t){void 0===t&&(t={}),rp("TSInterfaceDeclaration",e,t)},assertTSInterfaceBody:function(e,t){void 0===t&&(t={}),rp("TSInterfaceBody",e,t)},assertTSTypeAliasDeclaration:function(e,t){void 0===t&&(t={}),rp("TSTypeAliasDeclaration",e,t)},assertTSAsExpression:function(e,t){void 0===t&&(t={}),rp("TSAsExpression",e,t)},assertTSTypeAssertion:function(e,t){void 0===t&&(t={}),rp("TSTypeAssertion",e,t)},assertTSEnumDeclaration:function(e,t){void 0===t&&(t={}),rp("TSEnumDeclaration",e,t)},assertTSEnumMember:function(e,t){void 0===t&&(t={}),rp("TSEnumMember",e,t)},assertTSModuleDeclaration:function(e,t){void 0===t&&(t={}),rp("TSModuleDeclaration",e,t)},assertTSModuleBlock:function(e,t){void 0===t&&(t={}),rp("TSModuleBlock",e,t)},assertTSImportType:function(e,t){void 0===t&&(t={}),rp("TSImportType",e,t)},assertTSImportEqualsDeclaration:function(e,t){void 0===t&&(t={}),rp("TSImportEqualsDeclaration",e,t)},assertTSExternalModuleReference:function(e,t){void 0===t&&(t={}),rp("TSExternalModuleReference",e,t)},assertTSNonNullExpression:function(e,t){void 0===t&&(t={}),rp("TSNonNullExpression",e,t)},assertTSExportAssignment:function(e,t){void 0===t&&(t={}),rp("TSExportAssignment",e,t)},assertTSNamespaceExportDeclaration:function(e,t){void 0===t&&(t={}),rp("TSNamespaceExportDeclaration",e,t)},assertTSTypeAnnotation:function(e,t){void 0===t&&(t={}),rp("TSTypeAnnotation",e,t)},assertTSTypeParameterInstantiation:function(e,t){void 0===t&&(t={}),rp("TSTypeParameterInstantiation",e,t)},assertTSTypeParameterDeclaration:function(e,t){void 0===t&&(t={}),rp("TSTypeParameterDeclaration",e,t)},assertTSTypeParameter:function(e,t){void 0===t&&(t={}),rp("TSTypeParameter",e,t)},assertExpression:function(e,t){void 0===t&&(t={}),rp("Expression",e,t)},assertBinary:function(e,t){void 0===t&&(t={}),rp("Binary",e,t)},assertScopable:function(e,t){void 0===t&&(t={}),rp("Scopable",e,t)},assertBlockParent:function(e,t){void 0===t&&(t={}),rp("BlockParent",e,t)},assertBlock:function(e,t){void 0===t&&(t={}),rp("Block",e,t)},assertStatement:function(e,t){void 0===t&&(t={}),rp("Statement",e,t)},assertTerminatorless:function(e,t){void 0===t&&(t={}),rp("Terminatorless",e,t)},assertCompletionStatement:function(e,t){void 0===t&&(t={}),rp("CompletionStatement",e,t)},assertConditional:function(e,t){void 0===t&&(t={}),rp("Conditional",e,t)},assertLoop:function(e,t){void 0===t&&(t={}),rp("Loop",e,t)},assertWhile:function(e,t){void 0===t&&(t={}),rp("While",e,t)},assertExpressionWrapper:function(e,t){void 0===t&&(t={}),rp("ExpressionWrapper",e,t)},assertFor:function(e,t){void 0===t&&(t={}),rp("For",e,t)},assertForXStatement:function(e,t){void 0===t&&(t={}),rp("ForXStatement",e,t)},assertFunction:function(e,t){void 0===t&&(t={}),rp("Function",e,t)},assertFunctionParent:function(e,t){void 0===t&&(t={}),rp("FunctionParent",e,t)},assertPureish:function(e,t){void 0===t&&(t={}),rp("Pureish",e,t)},assertDeclaration:function(e,t){void 0===t&&(t={}),rp("Declaration",e,t)},assertPatternLike:function(e,t){void 0===t&&(t={}),rp("PatternLike",e,t)},assertLVal:function(e,t){void 0===t&&(t={}),rp("LVal",e,t)},assertTSEntityName:function(e,t){void 0===t&&(t={}),rp("TSEntityName",e,t)},assertLiteral:function(e,t){void 0===t&&(t={}),rp("Literal",e,t)},assertImmutable:function(e,t){void 0===t&&(t={}),rp("Immutable",e,t)},assertUserWhitespacable:function(e,t){void 0===t&&(t={}),rp("UserWhitespacable",e,t)},assertMethod:function(e,t){void 0===t&&(t={}),rp("Method",e,t)},assertObjectMember:function(e,t){void 0===t&&(t={}),rp("ObjectMember",e,t)},assertProperty:function(e,t){void 0===t&&(t={}),rp("Property",e,t)},assertUnaryLike:function(e,t){void 0===t&&(t={}),rp("UnaryLike",e,t)},assertPattern:function(e,t){void 0===t&&(t={}),rp("Pattern",e,t)},assertClass:function(e,t){void 0===t&&(t={}),rp("Class",e,t)},assertModuleDeclaration:function(e,t){void 0===t&&(t={}),rp("ModuleDeclaration",e,t)},assertExportDeclaration:function(e,t){void 0===t&&(t={}),rp("ExportDeclaration",e,t)},assertModuleSpecifier:function(e,t){void 0===t&&(t={}),rp("ModuleSpecifier",e,t)},assertFlow:function(e,t){void 0===t&&(t={}),rp("Flow",e,t)},assertFlowType:function(e,t){void 0===t&&(t={}),rp("FlowType",e,t)},assertFlowBaseAnnotation:function(e,t){void 0===t&&(t={}),rp("FlowBaseAnnotation",e,t)},assertFlowDeclaration:function(e,t){void 0===t&&(t={}),rp("FlowDeclaration",e,t)},assertFlowPredicate:function(e,t){void 0===t&&(t={}),rp("FlowPredicate",e,t)},assertEnumBody:function(e,t){void 0===t&&(t={}),rp("EnumBody",e,t)},assertEnumMember:function(e,t){void 0===t&&(t={}),rp("EnumMember",e,t)},assertJSX:function(e,t){void 0===t&&(t={}),rp("JSX",e,t)},assertPrivate:function(e,t){void 0===t&&(t={}),rp("Private",e,t)},assertTSTypeElement:function(e,t){void 0===t&&(t={}),rp("TSTypeElement",e,t)},assertTSType:function(e,t){void 0===t&&(t={}),rp("TSType",e,t)},assertNumberLiteral:function(e,t){console.trace("The node type NumberLiteral has been renamed to NumericLiteral"),rp("NumberLiteral",e,t)},assertRegexLiteral:function(e,t){console.trace("The node type RegexLiteral has been renamed to RegExpLiteral"),rp("RegexLiteral",e,t)},assertRestProperty:function(e,t){console.trace("The node type RestProperty has been renamed to RestElement"),rp("RestProperty",e,t)},assertSpreadProperty:function(e,t){console.trace("The node type SpreadProperty has been renamed to SpreadElement"),rp("SpreadProperty",e,t)},ArrayExpression:Fi,arrayExpression:Fi,AssignmentExpression:_i,assignmentExpression:_i,BinaryExpression:Ii,binaryExpression:Ii,InterpreterDirective:Bi,interpreterDirective:Bi,Directive:Oi,directive:Oi,DirectiveLiteral:Ni,directiveLiteral:Ni,BlockStatement:Ri,blockStatement:Ri,BreakStatement:Mi,breakStatement:Mi,CallExpression:Li,callExpression:Li,CatchClause:Ui,catchClause:Ui,ConditionalExpression:Gi,conditionalExpression:Gi,ContinueStatement:Vi,continueStatement:Vi,DebuggerStatement:Wi,debuggerStatement:Wi,DoWhileStatement:Hi,doWhileStatement:Hi,EmptyStatement:qi,emptyStatement:qi,ExpressionStatement:Ki,expressionStatement:Ki,File:zi,file:zi,ForInStatement:Xi,forInStatement:Xi,ForStatement:Yi,forStatement:Yi,FunctionDeclaration:Ji,functionDeclaration:Ji,FunctionExpression:$i,functionExpression:$i,Identifier:Qi,identifier:Qi,IfStatement:Zi,ifStatement:Zi,LabeledStatement:eo,labeledStatement:eo,StringLiteral:to,stringLiteral:to,NumericLiteral:ro,numericLiteral:ro,NullLiteral:no,nullLiteral:no,BooleanLiteral:ao,booleanLiteral:ao,RegExpLiteral:so,regExpLiteral:so,LogicalExpression:io,logicalExpression:io,MemberExpression:oo,memberExpression:oo,NewExpression:uo,newExpression:uo,Program:co,program:co,ObjectExpression:lo,objectExpression:lo,ObjectMethod:po,objectMethod:po,ObjectProperty:fo,objectProperty:fo,RestElement:ho,restElement:ho,ReturnStatement:mo,returnStatement:mo,SequenceExpression:yo,sequenceExpression:yo,ParenthesizedExpression:go,parenthesizedExpression:go,SwitchCase:vo,switchCase:vo,SwitchStatement:bo,switchStatement:bo,ThisExpression:xo,thisExpression:xo,ThrowStatement:Eo,throwStatement:Eo,TryStatement:Ao,tryStatement:Ao,UnaryExpression:wo,unaryExpression:wo,UpdateExpression:So,updateExpression:So,VariableDeclaration:Do,variableDeclaration:Do,VariableDeclarator:Co,variableDeclarator:Co,WhileStatement:To,whileStatement:To,WithStatement:jo,withStatement:jo,AssignmentPattern:Po,assignmentPattern:Po,ArrayPattern:ko,arrayPattern:ko,ArrowFunctionExpression:Fo,arrowFunctionExpression:Fo,ClassBody:_o,classBody:_o,ClassExpression:Io,classExpression:Io,ClassDeclaration:Bo,classDeclaration:Bo,ExportAllDeclaration:Oo,exportAllDeclaration:Oo,ExportDefaultDeclaration:No,exportDefaultDeclaration:No,ExportNamedDeclaration:Ro,exportNamedDeclaration:Ro,ExportSpecifier:Mo,exportSpecifier:Mo,ForOfStatement:Lo,forOfStatement:Lo,ImportDeclaration:Uo,importDeclaration:Uo,ImportDefaultSpecifier:Go,importDefaultSpecifier:Go,ImportNamespaceSpecifier:Vo,importNamespaceSpecifier:Vo,ImportSpecifier:Wo,importSpecifier:Wo,MetaProperty:Ho,metaProperty:Ho,ClassMethod:qo,classMethod:qo,ObjectPattern:Ko,objectPattern:Ko,SpreadElement:zo,spreadElement:zo,Super:Xo,super:Xo,TaggedTemplateExpression:Yo,taggedTemplateExpression:Yo,TemplateElement:Jo,templateElement:Jo,TemplateLiteral:$o,templateLiteral:$o,YieldExpression:Qo,yieldExpression:Qo,AnyTypeAnnotation:Zo,anyTypeAnnotation:Zo,ArrayTypeAnnotation:eu,arrayTypeAnnotation:eu,BooleanTypeAnnotation:tu,booleanTypeAnnotation:tu,BooleanLiteralTypeAnnotation:ru,booleanLiteralTypeAnnotation:ru,NullLiteralTypeAnnotation:nu,nullLiteralTypeAnnotation:nu,ClassImplements:au,classImplements:au,DeclareClass:su,declareClass:su,DeclareFunction:iu,declareFunction:iu,DeclareInterface:ou,declareInterface:ou,DeclareModule:uu,declareModule:uu,DeclareModuleExports:cu,declareModuleExports:cu,DeclareTypeAlias:lu,declareTypeAlias:lu,DeclareOpaqueType:pu,declareOpaqueType:pu,DeclareVariable:du,declareVariable:du,DeclareExportDeclaration:fu,declareExportDeclaration:fu,DeclareExportAllDeclaration:hu,declareExportAllDeclaration:hu,DeclaredPredicate:mu,declaredPredicate:mu,ExistsTypeAnnotation:yu,existsTypeAnnotation:yu,FunctionTypeAnnotation:gu,functionTypeAnnotation:gu,FunctionTypeParam:vu,functionTypeParam:vu,GenericTypeAnnotation:bu,genericTypeAnnotation:bu,InferredPredicate:xu,inferredPredicate:xu,InterfaceExtends:Eu,interfaceExtends:Eu,InterfaceDeclaration:Au,interfaceDeclaration:Au,InterfaceTypeAnnotation:wu,interfaceTypeAnnotation:wu,IntersectionTypeAnnotation:Su,intersectionTypeAnnotation:Su,MixedTypeAnnotation:Du,mixedTypeAnnotation:Du,EmptyTypeAnnotation:Cu,emptyTypeAnnotation:Cu,NullableTypeAnnotation:Tu,nullableTypeAnnotation:Tu,NumberLiteralTypeAnnotation:ju,numberLiteralTypeAnnotation:ju,NumberTypeAnnotation:Pu,numberTypeAnnotation:Pu,ObjectTypeAnnotation:ku,objectTypeAnnotation:ku,ObjectTypeInternalSlot:Fu,objectTypeInternalSlot:Fu,ObjectTypeCallProperty:_u,objectTypeCallProperty:_u,ObjectTypeIndexer:Iu,objectTypeIndexer:Iu,ObjectTypeProperty:Bu,objectTypeProperty:Bu,ObjectTypeSpreadProperty:Ou,objectTypeSpreadProperty:Ou,OpaqueType:Nu,opaqueType:Nu,QualifiedTypeIdentifier:Ru,qualifiedTypeIdentifier:Ru,StringLiteralTypeAnnotation:Mu,stringLiteralTypeAnnotation:Mu,StringTypeAnnotation:Lu,stringTypeAnnotation:Lu,ThisTypeAnnotation:Uu,thisTypeAnnotation:Uu,TupleTypeAnnotation:Gu,tupleTypeAnnotation:Gu,TypeofTypeAnnotation:Vu,typeofTypeAnnotation:Vu,TypeAlias:Wu,typeAlias:Wu,TypeAnnotation:Hu,typeAnnotation:Hu,TypeCastExpression:qu,typeCastExpression:qu,TypeParameter:Ku,typeParameter:Ku,TypeParameterDeclaration:zu,typeParameterDeclaration:zu,TypeParameterInstantiation:Xu,typeParameterInstantiation:Xu,UnionTypeAnnotation:Yu,unionTypeAnnotation:Yu,Variance:Ju,variance:Ju,VoidTypeAnnotation:$u,voidTypeAnnotation:$u,EnumDeclaration:Qu,enumDeclaration:Qu,EnumBooleanBody:Zu,enumBooleanBody:Zu,EnumNumberBody:ec,enumNumberBody:ec,EnumStringBody:tc,enumStringBody:tc,EnumSymbolBody:rc,enumSymbolBody:rc,EnumBooleanMember:nc,enumBooleanMember:nc,EnumNumberMember:ac,enumNumberMember:ac,EnumStringMember:sc,enumStringMember:sc,EnumDefaultedMember:ic,enumDefaultedMember:ic,JSXAttribute:oc,jsxAttribute:oc,jSXAttribute:oc,JSXClosingElement:uc,jsxClosingElement:uc,jSXClosingElement:uc,JSXElement:cc,jsxElement:cc,jSXElement:cc,JSXEmptyExpression:lc,jsxEmptyExpression:lc,jSXEmptyExpression:lc,JSXExpressionContainer:pc,jsxExpressionContainer:pc,jSXExpressionContainer:pc,JSXSpreadChild:dc,jsxSpreadChild:dc,jSXSpreadChild:dc,JSXIdentifier:fc,jsxIdentifier:fc,jSXIdentifier:fc,JSXMemberExpression:hc,jsxMemberExpression:hc,jSXMemberExpression:hc,JSXNamespacedName:mc,jsxNamespacedName:mc,jSXNamespacedName:mc,JSXOpeningElement:yc,jsxOpeningElement:yc,jSXOpeningElement:yc,JSXSpreadAttribute:gc,jsxSpreadAttribute:gc,jSXSpreadAttribute:gc,JSXText:vc,jsxText:vc,jSXText:vc,JSXFragment:bc,jsxFragment:bc,jSXFragment:bc,JSXOpeningFragment:xc,jsxOpeningFragment:xc,jSXOpeningFragment:xc,JSXClosingFragment:Ec,jsxClosingFragment:Ec,jSXClosingFragment:Ec,Noop:Ac,noop:Ac,Placeholder:wc,placeholder:wc,V8IntrinsicIdentifier:Sc,v8IntrinsicIdentifier:Sc,ArgumentPlaceholder:Dc,argumentPlaceholder:Dc,AwaitExpression:Cc,awaitExpression:Cc,BindExpression:Tc,bindExpression:Tc,ClassProperty:jc,classProperty:jc,OptionalMemberExpression:Pc,optionalMemberExpression:Pc,PipelineTopicExpression:kc,pipelineTopicExpression:kc,PipelineBareFunction:Fc,pipelineBareFunction:Fc,PipelinePrimaryTopicReference:_c,pipelinePrimaryTopicReference:_c,OptionalCallExpression:Ic,optionalCallExpression:Ic,ClassPrivateProperty:Bc,classPrivateProperty:Bc,ClassPrivateMethod:Oc,classPrivateMethod:Oc,Import:Nc,import:Nc,Decorator:Rc,decorator:Rc,DoExpression:Mc,doExpression:Mc,ExportDefaultSpecifier:Lc,exportDefaultSpecifier:Lc,ExportNamespaceSpecifier:Uc,exportNamespaceSpecifier:Uc,PrivateName:Gc,privateName:Gc,BigIntLiteral:Vc,bigIntLiteral:Vc,TSParameterProperty:Wc,tsParameterProperty:Wc,tSParameterProperty:Wc,TSDeclareFunction:Hc,tsDeclareFunction:Hc,tSDeclareFunction:Hc,TSDeclareMethod:qc,tsDeclareMethod:qc,tSDeclareMethod:qc,TSQualifiedName:Kc,tsQualifiedName:Kc,tSQualifiedName:Kc,TSCallSignatureDeclaration:zc,tsCallSignatureDeclaration:zc,tSCallSignatureDeclaration:zc,TSConstructSignatureDeclaration:Xc,tsConstructSignatureDeclaration:Xc,tSConstructSignatureDeclaration:Xc,TSPropertySignature:Yc,tsPropertySignature:Yc,tSPropertySignature:Yc,TSMethodSignature:Jc,tsMethodSignature:Jc,tSMethodSignature:Jc,TSIndexSignature:$c,tsIndexSignature:$c,tSIndexSignature:$c,TSAnyKeyword:Qc,tsAnyKeyword:Qc,tSAnyKeyword:Qc,TSBooleanKeyword:Zc,tsBooleanKeyword:Zc,tSBooleanKeyword:Zc,TSBigIntKeyword:el,tsBigIntKeyword:el,tSBigIntKeyword:el,TSNeverKeyword:tl,tsNeverKeyword:tl,tSNeverKeyword:tl,TSNullKeyword:rl,tsNullKeyword:rl,tSNullKeyword:rl,TSNumberKeyword:nl,tsNumberKeyword:nl,tSNumberKeyword:nl,TSObjectKeyword:al,tsObjectKeyword:al,tSObjectKeyword:al,TSStringKeyword:sl,tsStringKeyword:sl,tSStringKeyword:sl,TSSymbolKeyword:il,tsSymbolKeyword:il,tSSymbolKeyword:il,TSUndefinedKeyword:ol,tsUndefinedKeyword:ol,tSUndefinedKeyword:ol,TSUnknownKeyword:ul,tsUnknownKeyword:ul,tSUnknownKeyword:ul,TSVoidKeyword:cl,tsVoidKeyword:cl,tSVoidKeyword:cl,TSThisType:ll,tsThisType:ll,tSThisType:ll,TSFunctionType:pl,tsFunctionType:pl,tSFunctionType:pl,TSConstructorType:dl,tsConstructorType:dl,tSConstructorType:dl,TSTypeReference:fl,tsTypeReference:fl,tSTypeReference:fl,TSTypePredicate:hl,tsTypePredicate:hl,tSTypePredicate:hl,TSTypeQuery:ml,tsTypeQuery:ml,tSTypeQuery:ml,TSTypeLiteral:yl,tsTypeLiteral:yl,tSTypeLiteral:yl,TSArrayType:gl,tsArrayType:gl,tSArrayType:gl,TSTupleType:vl,tsTupleType:vl,tSTupleType:vl,TSOptionalType:bl,tsOptionalType:bl,tSOptionalType:bl,TSRestType:xl,tsRestType:xl,tSRestType:xl,TSUnionType:El,tsUnionType:El,tSUnionType:El,TSIntersectionType:Al,tsIntersectionType:Al,tSIntersectionType:Al,TSConditionalType:wl,tsConditionalType:wl,tSConditionalType:wl,TSInferType:Sl,tsInferType:Sl,tSInferType:Sl,TSParenthesizedType:Dl,tsParenthesizedType:Dl,tSParenthesizedType:Dl,TSTypeOperator:Cl,tsTypeOperator:Cl,tSTypeOperator:Cl,TSIndexedAccessType:Tl,tsIndexedAccessType:Tl,tSIndexedAccessType:Tl,TSMappedType:jl,tsMappedType:jl,tSMappedType:jl,TSLiteralType:Pl,tsLiteralType:Pl,tSLiteralType:Pl,TSExpressionWithTypeArguments:kl,tsExpressionWithTypeArguments:kl,tSExpressionWithTypeArguments:kl,TSInterfaceDeclaration:Fl,tsInterfaceDeclaration:Fl,tSInterfaceDeclaration:Fl,TSInterfaceBody:_l,tsInterfaceBody:_l,tSInterfaceBody:_l,TSTypeAliasDeclaration:Il,tsTypeAliasDeclaration:Il,tSTypeAliasDeclaration:Il,TSAsExpression:Bl,tsAsExpression:Bl,tSAsExpression:Bl,TSTypeAssertion:Ol,tsTypeAssertion:Ol,tSTypeAssertion:Ol,TSEnumDeclaration:Nl,tsEnumDeclaration:Nl,tSEnumDeclaration:Nl,TSEnumMember:Rl,tsEnumMember:Rl,tSEnumMember:Rl,TSModuleDeclaration:Ml,tsModuleDeclaration:Ml,tSModuleDeclaration:Ml,TSModuleBlock:Ll,tsModuleBlock:Ll,tSModuleBlock:Ll,TSImportType:Ul,tsImportType:Ul,tSImportType:Ul,TSImportEqualsDeclaration:Gl,tsImportEqualsDeclaration:Gl,tSImportEqualsDeclaration:Gl,TSExternalModuleReference:Vl,tsExternalModuleReference:Vl,tSExternalModuleReference:Vl,TSNonNullExpression:Wl,tsNonNullExpression:Wl,tSNonNullExpression:Wl,TSExportAssignment:Hl,tsExportAssignment:Hl,tSExportAssignment:Hl,TSNamespaceExportDeclaration:ql,tsNamespaceExportDeclaration:ql,tSNamespaceExportDeclaration:ql,TSTypeAnnotation:Kl,tsTypeAnnotation:Kl,tSTypeAnnotation:Kl,TSTypeParameterInstantiation:zl,tsTypeParameterInstantiation:zl,tSTypeParameterInstantiation:zl,TSTypeParameterDeclaration:Xl,tsTypeParameterDeclaration:Xl,tSTypeParameterDeclaration:Xl,TSTypeParameter:Yl,tsTypeParameter:Yl,tSTypeParameter:Yl,NumberLiteral:Jl,numberLiteral:Jl,RegexLiteral:$l,regexLiteral:$l,RestProperty:Ql,restProperty:Ql,SpreadProperty:Zl,spreadProperty:Zl,EXPRESSION_TYPES:Rp,BINARY_TYPES:Mp,SCOPABLE_TYPES:Lp,BLOCKPARENT_TYPES:Up,BLOCK_TYPES:Gp,STATEMENT_TYPES:Vp,TERMINATORLESS_TYPES:Wp,COMPLETIONSTATEMENT_TYPES:Hp,CONDITIONAL_TYPES:qp,LOOP_TYPES:Kp,WHILE_TYPES:zp,EXPRESSIONWRAPPER_TYPES:Xp,FOR_TYPES:Yp,FORXSTATEMENT_TYPES:Jp,FUNCTION_TYPES:$p,FUNCTIONPARENT_TYPES:Qp,PUREISH_TYPES:Zp,DECLARATION_TYPES:ed,PATTERNLIKE_TYPES:td,LVAL_TYPES:rd,TSENTITYNAME_TYPES:nd,LITERAL_TYPES:ad,IMMUTABLE_TYPES:sd,USERWHITESPACABLE_TYPES:id,METHOD_TYPES:od,OBJECTMEMBER_TYPES:ud,PROPERTY_TYPES:cd,UNARYLIKE_TYPES:ld,PATTERN_TYPES:pd,CLASS_TYPES:dd,MODULEDECLARATION_TYPES:fd,EXPORTDECLARATION_TYPES:hd,MODULESPECIFIER_TYPES:md,FLOW_TYPES:yd,FLOWTYPE_TYPES:gd,FLOWBASEANNOTATION_TYPES:vd,FLOWDECLARATION_TYPES:bd,FLOWPREDICATE_TYPES:xd,ENUMBODY_TYPES:Ed,ENUMMEMBER_TYPES:Ad,JSX_TYPES:wd,PRIVATE_TYPES:Sd,TSTYPEELEMENT_TYPES:Dd,TSTYPE_TYPES:Cd,STATEMENT_OR_BLOCK_KEYS:us,FLATTENABLE_KEYS:["body","expressions"],FOR_INIT_KEYS:cs,COMMENT_KEYS:ls,LOGICAL_OPERATORS:ps,UPDATE_OPERATORS:ds,BOOLEAN_NUMBER_BINARY_OPERATORS:fs,EQUALITY_BINARY_OPERATORS:hs,COMPARISON_BINARY_OPERATORS:ms,BOOLEAN_BINARY_OPERATORS:ys,NUMBER_BINARY_OPERATORS:gs,BINARY_OPERATORS:vs,ASSIGNMENT_OPERATORS:bs,BOOLEAN_UNARY_OPERATORS:xs,NUMBER_UNARY_OPERATORS:Es,STRING_UNARY_OPERATORS:As,UNARY_OPERATORS:ws,INHERIT_KEYS:Ss,BLOCK_SCOPED_SYMBOL:Ds,NOT_LOCAL_BINDING:Cs,VISITOR_KEYS:ks,ALIAS_KEYS:Fs,FLIPPED_ALIAS_KEYS:_s,NODE_FIELDS:Is,BUILDER_KEYS:Bs,DEPRECATED_KEYS:Os,NODE_PARENT_VALIDATIONS:Ns,PLACEHOLDERS:fi,PLACEHOLDERS_ALIAS:hi,PLACEHOLDERS_FLIPPED_ALIAS:bi,TYPES:Pi,isArrayExpression:u,isAssignmentExpression:c,isBinaryExpression:l,isInterpreterDirective:function(e,t){return!!e&&("InterpreterDirective"===e.type&&(void 0===t||o(e,t)))},isDirective:function(e,t){return!!e&&("Directive"===e.type&&(void 0===t||o(e,t)))},isDirectiveLiteral:function(e,t){return!!e&&("DirectiveLiteral"===e.type&&(void 0===t||o(e,t)))},isBlockStatement:p,isBreakStatement:d,isCallExpression:f,isCatchClause:h,isConditionalExpression:m,isContinueStatement:y,isDebuggerStatement:function(e,t){return!!e&&("DebuggerStatement"===e.type&&(void 0===t||o(e,t)))},isDoWhileStatement:function(e,t){return!!e&&("DoWhileStatement"===e.type&&(void 0===t||o(e,t)))},isEmptyStatement:g,isExpressionStatement:v,isFile:b,isForInStatement:x,isForStatement:E,isFunctionDeclaration:A,isFunctionExpression:w,isIdentifier:S,isIfStatement:D,isLabeledStatement:C,isStringLiteral:T,isNumericLiteral:j,isNullLiteral:P,isBooleanLiteral:function(e,t){return!!e&&("BooleanLiteral"===e.type&&(void 0===t||o(e,t)))},isRegExpLiteral:k,isLogicalExpression:F,isMemberExpression:_,isNewExpression:I,isProgram:B,isObjectExpression:O,isObjectMethod:N,isObjectProperty:R,isRestElement:M,isReturnStatement:L,isSequenceExpression:U,isParenthesizedExpression:function(e,t){return!!e&&("ParenthesizedExpression"===e.type&&(void 0===t||o(e,t)))},isSwitchCase:function(e,t){return!!e&&("SwitchCase"===e.type&&(void 0===t||o(e,t)))},isSwitchStatement:G,isThisExpression:V,isThrowStatement:W,isTryStatement:function(e,t){return!!e&&("TryStatement"===e.type&&(void 0===t||o(e,t)))},isUnaryExpression:H,isUpdateExpression:function(e,t){return!!e&&("UpdateExpression"===e.type&&(void 0===t||o(e,t)))},isVariableDeclaration:q,isVariableDeclarator:K,isWhileStatement:z,isWithStatement:function(e,t){return!!e&&("WithStatement"===e.type&&(void 0===t||o(e,t)))},isAssignmentPattern:X,isArrayPattern:Y,isArrowFunctionExpression:J,isClassBody:$,isClassExpression:Q,isClassDeclaration:Z,isExportAllDeclaration:ee,isExportDefaultDeclaration:te,isExportNamedDeclaration:re,isExportSpecifier:ne,isForOfStatement:function(e,t){return!!e&&("ForOfStatement"===e.type&&(void 0===t||o(e,t)))},isImportDeclaration:ae,isImportDefaultSpecifier:se,isImportNamespaceSpecifier:ie,isImportSpecifier:oe,isMetaProperty:function(e,t){return!!e&&("MetaProperty"===e.type&&(void 0===t||o(e,t)))},isClassMethod:ue,isObjectPattern:ce,isSpreadElement:le,isSuper:pe,isTaggedTemplateExpression:de,isTemplateElement:function(e,t){return!!e&&("TemplateElement"===e.type&&(void 0===t||o(e,t)))},isTemplateLiteral:fe,isYieldExpression:he,isAnyTypeAnnotation:me,isArrayTypeAnnotation:ye,isBooleanTypeAnnotation:ge,isBooleanLiteralTypeAnnotation:function(e,t){return!!e&&("BooleanLiteralTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isNullLiteralTypeAnnotation:function(e,t){return!!e&&("NullLiteralTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isClassImplements:function(e,t){return!!e&&("ClassImplements"===e.type&&(void 0===t||o(e,t)))},isDeclareClass:function(e,t){return!!e&&("DeclareClass"===e.type&&(void 0===t||o(e,t)))},isDeclareFunction:function(e,t){return!!e&&("DeclareFunction"===e.type&&(void 0===t||o(e,t)))},isDeclareInterface:function(e,t){return!!e&&("DeclareInterface"===e.type&&(void 0===t||o(e,t)))},isDeclareModule:function(e,t){return!!e&&("DeclareModule"===e.type&&(void 0===t||o(e,t)))},isDeclareModuleExports:function(e,t){return!!e&&("DeclareModuleExports"===e.type&&(void 0===t||o(e,t)))},isDeclareTypeAlias:function(e,t){return!!e&&("DeclareTypeAlias"===e.type&&(void 0===t||o(e,t)))},isDeclareOpaqueType:function(e,t){return!!e&&("DeclareOpaqueType"===e.type&&(void 0===t||o(e,t)))},isDeclareVariable:function(e,t){return!!e&&("DeclareVariable"===e.type&&(void 0===t||o(e,t)))},isDeclareExportDeclaration:ve,isDeclareExportAllDeclaration:function(e,t){return!!e&&("DeclareExportAllDeclaration"===e.type&&(void 0===t||o(e,t)))},isDeclaredPredicate:function(e,t){return!!e&&("DeclaredPredicate"===e.type&&(void 0===t||o(e,t)))},isExistsTypeAnnotation:function(e,t){return!!e&&("ExistsTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isFunctionTypeAnnotation:function(e,t){return!!e&&("FunctionTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isFunctionTypeParam:function(e,t){return!!e&&("FunctionTypeParam"===e.type&&(void 0===t||o(e,t)))},isGenericTypeAnnotation:be,isInferredPredicate:function(e,t){return!!e&&("InferredPredicate"===e.type&&(void 0===t||o(e,t)))},isInterfaceExtends:function(e,t){return!!e&&("InterfaceExtends"===e.type&&(void 0===t||o(e,t)))},isInterfaceDeclaration:function(e,t){return!!e&&("InterfaceDeclaration"===e.type&&(void 0===t||o(e,t)))},isInterfaceTypeAnnotation:function(e,t){return!!e&&("InterfaceTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isIntersectionTypeAnnotation:xe,isMixedTypeAnnotation:Ee,isEmptyTypeAnnotation:Ae,isNullableTypeAnnotation:we,isNumberLiteralTypeAnnotation:function(e,t){return!!e&&("NumberLiteralTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isNumberTypeAnnotation:Se,isObjectTypeAnnotation:function(e,t){return!!e&&("ObjectTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isObjectTypeInternalSlot:function(e,t){return!!e&&("ObjectTypeInternalSlot"===e.type&&(void 0===t||o(e,t)))},isObjectTypeCallProperty:function(e,t){return!!e&&("ObjectTypeCallProperty"===e.type&&(void 0===t||o(e,t)))},isObjectTypeIndexer:function(e,t){return!!e&&("ObjectTypeIndexer"===e.type&&(void 0===t||o(e,t)))},isObjectTypeProperty:function(e,t){return!!e&&("ObjectTypeProperty"===e.type&&(void 0===t||o(e,t)))},isObjectTypeSpreadProperty:function(e,t){return!!e&&("ObjectTypeSpreadProperty"===e.type&&(void 0===t||o(e,t)))},isOpaqueType:function(e,t){return!!e&&("OpaqueType"===e.type&&(void 0===t||o(e,t)))},isQualifiedTypeIdentifier:function(e,t){return!!e&&("QualifiedTypeIdentifier"===e.type&&(void 0===t||o(e,t)))},isStringLiteralTypeAnnotation:function(e,t){return!!e&&("StringLiteralTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isStringTypeAnnotation:De,isThisTypeAnnotation:function(e,t){return!!e&&("ThisTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isTupleTypeAnnotation:function(e,t){return!!e&&("TupleTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isTypeofTypeAnnotation:function(e,t){return!!e&&("TypeofTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isTypeAlias:function(e,t){return!!e&&("TypeAlias"===e.type&&(void 0===t||o(e,t)))},isTypeAnnotation:Ce,isTypeCastExpression:Te,isTypeParameter:function(e,t){return!!e&&("TypeParameter"===e.type&&(void 0===t||o(e,t)))},isTypeParameterDeclaration:function(e,t){return!!e&&("TypeParameterDeclaration"===e.type&&(void 0===t||o(e,t)))},isTypeParameterInstantiation:function(e,t){return!!e&&("TypeParameterInstantiation"===e.type&&(void 0===t||o(e,t)))},isUnionTypeAnnotation:je,isVariance:function(e,t){return!!e&&("Variance"===e.type&&(void 0===t||o(e,t)))},isVoidTypeAnnotation:Pe,isEnumDeclaration:function(e,t){return!!e&&("EnumDeclaration"===e.type&&(void 0===t||o(e,t)))},isEnumBooleanBody:function(e,t){return!!e&&("EnumBooleanBody"===e.type&&(void 0===t||o(e,t)))},isEnumNumberBody:function(e,t){return!!e&&("EnumNumberBody"===e.type&&(void 0===t||o(e,t)))},isEnumStringBody:function(e,t){return!!e&&("EnumStringBody"===e.type&&(void 0===t||o(e,t)))},isEnumSymbolBody:function(e,t){return!!e&&("EnumSymbolBody"===e.type&&(void 0===t||o(e,t)))},isEnumBooleanMember:function(e,t){return!!e&&("EnumBooleanMember"===e.type&&(void 0===t||o(e,t)))},isEnumNumberMember:function(e,t){return!!e&&("EnumNumberMember"===e.type&&(void 0===t||o(e,t)))},isEnumStringMember:function(e,t){return!!e&&("EnumStringMember"===e.type&&(void 0===t||o(e,t)))},isEnumDefaultedMember:function(e,t){return!!e&&("EnumDefaultedMember"===e.type&&(void 0===t||o(e,t)))},isJSXAttribute:ke,isJSXClosingElement:function(e,t){return!!e&&("JSXClosingElement"===e.type&&(void 0===t||o(e,t)))},isJSXElement:Fe,isJSXEmptyExpression:_e,isJSXExpressionContainer:Ie,isJSXSpreadChild:function(e,t){return!!e&&("JSXSpreadChild"===e.type&&(void 0===t||o(e,t)))},isJSXIdentifier:Be,isJSXMemberExpression:Oe,isJSXNamespacedName:Ne,isJSXOpeningElement:function(e,t){return!!e&&("JSXOpeningElement"===e.type&&(void 0===t||o(e,t)))},isJSXSpreadAttribute:Re,isJSXText:Me,isJSXFragment:function(e,t){return!!e&&("JSXFragment"===e.type&&(void 0===t||o(e,t)))},isJSXOpeningFragment:function(e,t){return!!e&&("JSXOpeningFragment"===e.type&&(void 0===t||o(e,t)))},isJSXClosingFragment:function(e,t){return!!e&&("JSXClosingFragment"===e.type&&(void 0===t||o(e,t)))},isNoop:function(e,t){return!!e&&("Noop"===e.type&&(void 0===t||o(e,t)))},isPlaceholder:Le,isV8IntrinsicIdentifier:function(e,t){return!!e&&("V8IntrinsicIdentifier"===e.type&&(void 0===t||o(e,t)))},isArgumentPlaceholder:function(e,t){return!!e&&("ArgumentPlaceholder"===e.type&&(void 0===t||o(e,t)))},isAwaitExpression:Ue,isBindExpression:Ge,isClassProperty:Ve,isOptionalMemberExpression:We,isPipelineTopicExpression:He,isPipelineBareFunction:function(e,t){return!!e&&("PipelineBareFunction"===e.type&&(void 0===t||o(e,t)))},isPipelinePrimaryTopicReference:function(e,t){return!!e&&("PipelinePrimaryTopicReference"===e.type&&(void 0===t||o(e,t)))},isOptionalCallExpression:function(e,t){return!!e&&("OptionalCallExpression"===e.type&&(void 0===t||o(e,t)))},isClassPrivateProperty:function(e,t){return!!e&&("ClassPrivateProperty"===e.type&&(void 0===t||o(e,t)))},isClassPrivateMethod:function(e,t){return!!e&&("ClassPrivateMethod"===e.type&&(void 0===t||o(e,t)))},isImport:qe,isDecorator:function(e,t){return!!e&&("Decorator"===e.type&&(void 0===t||o(e,t)))},isDoExpression:function(e,t){return!!e&&("DoExpression"===e.type&&(void 0===t||o(e,t)))},isExportDefaultSpecifier:Ke,isExportNamespaceSpecifier:ze,isPrivateName:Xe,isBigIntLiteral:function(e,t){return!!e&&("BigIntLiteral"===e.type&&(void 0===t||o(e,t)))},isTSParameterProperty:function(e,t){return!!e&&("TSParameterProperty"===e.type&&(void 0===t||o(e,t)))},isTSDeclareFunction:function(e,t){return!!e&&("TSDeclareFunction"===e.type&&(void 0===t||o(e,t)))},isTSDeclareMethod:function(e,t){return!!e&&("TSDeclareMethod"===e.type&&(void 0===t||o(e,t)))},isTSQualifiedName:function(e,t){return!!e&&("TSQualifiedName"===e.type&&(void 0===t||o(e,t)))},isTSCallSignatureDeclaration:function(e,t){return!!e&&("TSCallSignatureDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSConstructSignatureDeclaration:function(e,t){return!!e&&("TSConstructSignatureDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSPropertySignature:function(e,t){return!!e&&("TSPropertySignature"===e.type&&(void 0===t||o(e,t)))},isTSMethodSignature:function(e,t){return!!e&&("TSMethodSignature"===e.type&&(void 0===t||o(e,t)))},isTSIndexSignature:function(e,t){return!!e&&("TSIndexSignature"===e.type&&(void 0===t||o(e,t)))},isTSAnyKeyword:function(e,t){return!!e&&("TSAnyKeyword"===e.type&&(void 0===t||o(e,t)))},isTSBooleanKeyword:function(e,t){return!!e&&("TSBooleanKeyword"===e.type&&(void 0===t||o(e,t)))},isTSBigIntKeyword:function(e,t){return!!e&&("TSBigIntKeyword"===e.type&&(void 0===t||o(e,t)))},isTSNeverKeyword:function(e,t){return!!e&&("TSNeverKeyword"===e.type&&(void 0===t||o(e,t)))},isTSNullKeyword:function(e,t){return!!e&&("TSNullKeyword"===e.type&&(void 0===t||o(e,t)))},isTSNumberKeyword:function(e,t){return!!e&&("TSNumberKeyword"===e.type&&(void 0===t||o(e,t)))},isTSObjectKeyword:function(e,t){return!!e&&("TSObjectKeyword"===e.type&&(void 0===t||o(e,t)))},isTSStringKeyword:function(e,t){return!!e&&("TSStringKeyword"===e.type&&(void 0===t||o(e,t)))},isTSSymbolKeyword:function(e,t){return!!e&&("TSSymbolKeyword"===e.type&&(void 0===t||o(e,t)))},isTSUndefinedKeyword:function(e,t){return!!e&&("TSUndefinedKeyword"===e.type&&(void 0===t||o(e,t)))},isTSUnknownKeyword:function(e,t){return!!e&&("TSUnknownKeyword"===e.type&&(void 0===t||o(e,t)))},isTSVoidKeyword:function(e,t){return!!e&&("TSVoidKeyword"===e.type&&(void 0===t||o(e,t)))},isTSThisType:function(e,t){return!!e&&("TSThisType"===e.type&&(void 0===t||o(e,t)))},isTSFunctionType:function(e,t){return!!e&&("TSFunctionType"===e.type&&(void 0===t||o(e,t)))},isTSConstructorType:function(e,t){return!!e&&("TSConstructorType"===e.type&&(void 0===t||o(e,t)))},isTSTypeReference:function(e,t){return!!e&&("TSTypeReference"===e.type&&(void 0===t||o(e,t)))},isTSTypePredicate:function(e,t){return!!e&&("TSTypePredicate"===e.type&&(void 0===t||o(e,t)))},isTSTypeQuery:function(e,t){return!!e&&("TSTypeQuery"===e.type&&(void 0===t||o(e,t)))},isTSTypeLiteral:function(e,t){return!!e&&("TSTypeLiteral"===e.type&&(void 0===t||o(e,t)))},isTSArrayType:Ye,isTSTupleType:function(e,t){return!!e&&("TSTupleType"===e.type&&(void 0===t||o(e,t)))},isTSOptionalType:Je,isTSRestType:$e,isTSUnionType:Qe,isTSIntersectionType:Ze,isTSConditionalType:function(e,t){return!!e&&("TSConditionalType"===e.type&&(void 0===t||o(e,t)))},isTSInferType:function(e,t){return!!e&&("TSInferType"===e.type&&(void 0===t||o(e,t)))},isTSParenthesizedType:function(e,t){return!!e&&("TSParenthesizedType"===e.type&&(void 0===t||o(e,t)))},isTSTypeOperator:function(e,t){return!!e&&("TSTypeOperator"===e.type&&(void 0===t||o(e,t)))},isTSIndexedAccessType:function(e,t){return!!e&&("TSIndexedAccessType"===e.type&&(void 0===t||o(e,t)))},isTSMappedType:function(e,t){return!!e&&("TSMappedType"===e.type&&(void 0===t||o(e,t)))},isTSLiteralType:function(e,t){return!!e&&("TSLiteralType"===e.type&&(void 0===t||o(e,t)))},isTSExpressionWithTypeArguments:function(e,t){return!!e&&("TSExpressionWithTypeArguments"===e.type&&(void 0===t||o(e,t)))},isTSInterfaceDeclaration:function(e,t){return!!e&&("TSInterfaceDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSInterfaceBody:function(e,t){return!!e&&("TSInterfaceBody"===e.type&&(void 0===t||o(e,t)))},isTSTypeAliasDeclaration:function(e,t){return!!e&&("TSTypeAliasDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSAsExpression:et,isTSTypeAssertion:tt,isTSEnumDeclaration:function(e,t){return!!e&&("TSEnumDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSEnumMember:function(e,t){return!!e&&("TSEnumMember"===e.type&&(void 0===t||o(e,t)))},isTSModuleDeclaration:function(e,t){return!!e&&("TSModuleDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSModuleBlock:function(e,t){return!!e&&("TSModuleBlock"===e.type&&(void 0===t||o(e,t)))},isTSImportType:function(e,t){return!!e&&("TSImportType"===e.type&&(void 0===t||o(e,t)))},isTSImportEqualsDeclaration:function(e,t){return!!e&&("TSImportEqualsDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSExternalModuleReference:function(e,t){return!!e&&("TSExternalModuleReference"===e.type&&(void 0===t||o(e,t)))},isTSNonNullExpression:function(e,t){return!!e&&("TSNonNullExpression"===e.type&&(void 0===t||o(e,t)))},isTSExportAssignment:function(e,t){return!!e&&("TSExportAssignment"===e.type&&(void 0===t||o(e,t)))},isTSNamespaceExportDeclaration:function(e,t){return!!e&&("TSNamespaceExportDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSTypeAnnotation:function(e,t){return!!e&&("TSTypeAnnotation"===e.type&&(void 0===t||o(e,t)))},isTSTypeParameterInstantiation:function(e,t){return!!e&&("TSTypeParameterInstantiation"===e.type&&(void 0===t||o(e,t)))},isTSTypeParameterDeclaration:function(e,t){return!!e&&("TSTypeParameterDeclaration"===e.type&&(void 0===t||o(e,t)))},isTSTypeParameter:function(e,t){return!!e&&("TSTypeParameter"===e.type&&(void 0===t||o(e,t)))},isExpression:rt,isBinary:nt,isScopable:at,isBlockParent:function(e,t){if(!e)return!1;var r=e.type;return("BlockParent"===r||"BlockStatement"===r||"CatchClause"===r||"DoWhileStatement"===r||"ForInStatement"===r||"ForStatement"===r||"FunctionDeclaration"===r||"FunctionExpression"===r||"Program"===r||"ObjectMethod"===r||"SwitchStatement"===r||"WhileStatement"===r||"ArrowFunctionExpression"===r||"ForOfStatement"===r||"ClassMethod"===r||"ClassPrivateMethod"===r||"TSModuleBlock"===r||"Placeholder"===r&&"BlockStatement"===e.expectedNode)&&(void 0===t||o(e,t))},isBlock:function(e,t){if(!e)return!1;var r=e.type;return("Block"===r||"BlockStatement"===r||"Program"===r||"TSModuleBlock"===r||"Placeholder"===r&&"BlockStatement"===e.expectedNode)&&(void 0===t||o(e,t))},isStatement:st,isTerminatorless:function(e,t){if(!e)return!1;var r=e.type;return("Terminatorless"===r||"BreakStatement"===r||"ContinueStatement"===r||"ReturnStatement"===r||"ThrowStatement"===r||"YieldExpression"===r||"AwaitExpression"===r)&&(void 0===t||o(e,t))},isCompletionStatement:function(e,t){if(!e)return!1;var r=e.type;return("CompletionStatement"===r||"BreakStatement"===r||"ContinueStatement"===r||"ReturnStatement"===r||"ThrowStatement"===r)&&(void 0===t||o(e,t))},isConditional:it,isLoop:ot,isWhile:function(e,t){if(!e)return!1;var r=e.type;return("While"===r||"DoWhileStatement"===r||"WhileStatement"===r)&&(void 0===t||o(e,t))},isExpressionWrapper:function(e,t){if(!e)return!1;var r=e.type;return("ExpressionWrapper"===r||"ExpressionStatement"===r||"ParenthesizedExpression"===r||"TypeCastExpression"===r)&&(void 0===t||o(e,t))},isFor:ut,isForXStatement:ct,isFunction:lt,isFunctionParent:function(e,t){if(!e)return!1;var r=e.type;return("FunctionParent"===r||"FunctionDeclaration"===r||"FunctionExpression"===r||"ObjectMethod"===r||"ArrowFunctionExpression"===r||"ClassMethod"===r||"ClassPrivateMethod"===r)&&(void 0===t||o(e,t))},isPureish:pt,isDeclaration:dt,isPatternLike:function(e,t){if(!e)return!1;var r=e.type;return("PatternLike"===r||"Identifier"===r||"RestElement"===r||"AssignmentPattern"===r||"ArrayPattern"===r||"ObjectPattern"===r||"Placeholder"===r&&("Pattern"===e.expectedNode||"Identifier"===e.expectedNode))&&(void 0===t||o(e,t))},isLVal:function(e,t){if(!e)return!1;var r=e.type;return("LVal"===r||"Identifier"===r||"MemberExpression"===r||"RestElement"===r||"AssignmentPattern"===r||"ArrayPattern"===r||"ObjectPattern"===r||"TSParameterProperty"===r||"Placeholder"===r&&("Pattern"===e.expectedNode||"Identifier"===e.expectedNode))&&(void 0===t||o(e,t))},isTSEntityName:function(e,t){if(!e)return!1;var r=e.type;return("TSEntityName"===r||"Identifier"===r||"TSQualifiedName"===r||"Placeholder"===r&&"Identifier"===e.expectedNode)&&(void 0===t||o(e,t))},isLiteral:ft,isUserWhitespacable:function(e,t){if(!e)return!1;var r=e.type;return("UserWhitespacable"===r||"ObjectMethod"===r||"ObjectProperty"===r||"ObjectTypeInternalSlot"===r||"ObjectTypeCallProperty"===r||"ObjectTypeIndexer"===r||"ObjectTypeProperty"===r||"ObjectTypeSpreadProperty"===r)&&(void 0===t||o(e,t))},isMethod:function(e,t){if(!e)return!1;var r=e.type;return("Method"===r||"ObjectMethod"===r||"ClassMethod"===r||"ClassPrivateMethod"===r)&&(void 0===t||o(e,t))},isObjectMember:function(e,t){if(!e)return!1;var r=e.type;return("ObjectMember"===r||"ObjectMethod"===r||"ObjectProperty"===r)&&(void 0===t||o(e,t))},isProperty:ht,isUnaryLike:mt,isPattern:yt,isClass:gt,isModuleDeclaration:vt,isExportDeclaration:bt,isModuleSpecifier:xt,isFlow:Et,isFlowType:function(e,t){if(!e)return!1;var r=e.type;return("FlowType"===r||"AnyTypeAnnotation"===r||"ArrayTypeAnnotation"===r||"BooleanTypeAnnotation"===r||"BooleanLiteralTypeAnnotation"===r||"NullLiteralTypeAnnotation"===r||"ExistsTypeAnnotation"===r||"FunctionTypeAnnotation"===r||"GenericTypeAnnotation"===r||"InterfaceTypeAnnotation"===r||"IntersectionTypeAnnotation"===r||"MixedTypeAnnotation"===r||"EmptyTypeAnnotation"===r||"NullableTypeAnnotation"===r||"NumberLiteralTypeAnnotation"===r||"NumberTypeAnnotation"===r||"ObjectTypeAnnotation"===r||"StringLiteralTypeAnnotation"===r||"StringTypeAnnotation"===r||"ThisTypeAnnotation"===r||"TupleTypeAnnotation"===r||"TypeofTypeAnnotation"===r||"UnionTypeAnnotation"===r||"VoidTypeAnnotation"===r)&&(void 0===t||o(e,t))},isFlowBaseAnnotation:At,isFlowDeclaration:function(e,t){if(!e)return!1;var r=e.type;return("FlowDeclaration"===r||"DeclareClass"===r||"DeclareFunction"===r||"DeclareInterface"===r||"DeclareModule"===r||"DeclareModuleExports"===r||"DeclareTypeAlias"===r||"DeclareOpaqueType"===r||"DeclareVariable"===r||"DeclareExportDeclaration"===r||"DeclareExportAllDeclaration"===r||"InterfaceDeclaration"===r||"OpaqueType"===r||"TypeAlias"===r)&&(void 0===t||o(e,t))},isFlowPredicate:function(e,t){if(!e)return!1;var r=e.type;return("FlowPredicate"===r||"DeclaredPredicate"===r||"InferredPredicate"===r)&&(void 0===t||o(e,t))},isEnumBody:function(e,t){if(!e)return!1;var r=e.type;return("EnumBody"===r||"EnumBooleanBody"===r||"EnumNumberBody"===r||"EnumStringBody"===r||"EnumSymbolBody"===r)&&(void 0===t||o(e,t))},isEnumMember:function(e,t){if(!e)return!1;var r=e.type;return("EnumMember"===r||"EnumBooleanMember"===r||"EnumNumberMember"===r||"EnumStringMember"===r||"EnumDefaultedMember"===r)&&(void 0===t||o(e,t))},isJSX:function(e,t){if(!e)return!1;var r=e.type;return("JSX"===r||"JSXAttribute"===r||"JSXClosingElement"===r||"JSXElement"===r||"JSXEmptyExpression"===r||"JSXExpressionContainer"===r||"JSXSpreadChild"===r||"JSXIdentifier"===r||"JSXMemberExpression"===r||"JSXNamespacedName"===r||"JSXOpeningElement"===r||"JSXSpreadAttribute"===r||"JSXText"===r||"JSXFragment"===r||"JSXOpeningFragment"===r||"JSXClosingFragment"===r)&&(void 0===t||o(e,t))},isPrivate:function(e,t){if(!e)return!1;var r=e.type;return("Private"===r||"ClassPrivateProperty"===r||"ClassPrivateMethod"===r||"PrivateName"===r)&&(void 0===t||o(e,t))},isTSTypeElement:function(e,t){if(!e)return!1;var r=e.type;return("TSTypeElement"===r||"TSCallSignatureDeclaration"===r||"TSConstructSignatureDeclaration"===r||"TSPropertySignature"===r||"TSMethodSignature"===r||"TSIndexSignature"===r)&&(void 0===t||o(e,t))},isTSType:function(e,t){if(!e)return!1;var r=e.type;return("TSType"===r||"TSAnyKeyword"===r||"TSBooleanKeyword"===r||"TSBigIntKeyword"===r||"TSNeverKeyword"===r||"TSNullKeyword"===r||"TSNumberKeyword"===r||"TSObjectKeyword"===r||"TSStringKeyword"===r||"TSSymbolKeyword"===r||"TSUndefinedKeyword"===r||"TSUnknownKeyword"===r||"TSVoidKeyword"===r||"TSThisType"===r||"TSFunctionType"===r||"TSConstructorType"===r||"TSTypeReference"===r||"TSTypePredicate"===r||"TSTypeQuery"===r||"TSTypeLiteral"===r||"TSArrayType"===r||"TSTupleType"===r||"TSOptionalType"===r||"TSRestType"===r||"TSUnionType"===r||"TSIntersectionType"===r||"TSConditionalType"===r||"TSInferType"===r||"TSParenthesizedType"===r||"TSTypeOperator"===r||"TSIndexedAccessType"===r||"TSMappedType"===r||"TSLiteralType"===r||"TSExpressionWithTypeArguments"===r||"TSImportType"===r)&&(void 0===t||o(e,t))},isNumberLiteral:function(e,t){return console.trace("The node type NumberLiteral has been renamed to NumericLiteral"),!!e&&("NumberLiteral"===e.type&&(void 0===t||o(e,t)))},isRegexLiteral:function(e,t){return console.trace("The node type RegexLiteral has been renamed to RegExpLiteral"),!!e&&("RegexLiteral"===e.type&&(void 0===t||o(e,t)))},isRestProperty:function(e,t){return console.trace("The node type RestProperty has been renamed to RestElement"),!!e&&("RestProperty"===e.type&&(void 0===t||o(e,t)))},isSpreadProperty:function(e,t){return console.trace("The node type SpreadProperty has been renamed to SpreadElement"),!!e&&("SpreadProperty"===e.type&&(void 0===t||o(e,t)))}}),hf={types:["Identifier","JSXIdentifier"],checkPath:function(e,t){var r=e.node,n=e.parent;if(!S(r,t)&&!Oe(n,t)){if(!Be(r,t))return!1;if(df.isCompatTag(r.name))return!1}return of(r,n,e.parentPath.parent)}},mf={types:["MemberExpression"],checkPath:function(e){var t=e.node,r=e.parent;return _(t)&&of(t,r)}},yf={types:["Identifier"],checkPath:function(e){var t=e.node,r=e.parent,n=e.parentPath.parent;return S(t)&&nf(t,r,n)}},gf={types:["Statement"],checkPath:function(e){var t=e.node,r=e.parent;if(st(t)){if(q(t)){if(ct(r,{left:t}))return!1;if(E(r,{init:t}))return!1}return!0}return!1}},vf={types:["Expression"],checkPath:function(e){return e.isIdentifier()?e.isReferencedIdentifier():rt(e.node)}},bf={types:["Scopable","Pattern"],checkPath:function(e){return uf(e.node,e.parent)}},xf={checkPath:function(e){return of(e.node,e.parent)}},Ef={checkPath:function(e){return sf(e.node)}},Af={types:["VariableDeclaration"],checkPath:function(e){return pf(e.node)}},wf={types:["Flow","ImportDeclaration","ExportDeclaration","ImportSpecifier"],checkPath:function(e){var t=e.node;return!!Et(t)||(ae(t)?"type"===t.importKind||"typeof"===t.importKind:bt(t)?"type"===t.exportKind:!!oe(t)&&("type"===t.importKind||"typeof"===t.importKind))}},Sf=Object.freeze({__proto__:null,ReferencedIdentifier:hf,ReferencedMemberExpression:mf,BindingIdentifier:yf,Statement:gf,Expression:vf,Scope:bf,Referenced:xf,BlockScoped:Ef,Var:Af,User:{checkPath:function(e){return e.node&&!!e.node.loc}},Generated:{checkPath:function(e){return!e.isUser()}},Pure:{checkPath:function(e,t){return e.scope.isPure(e.node,t)}},Flow:wf,RestProperty:{types:["RestElement"],checkPath:function(e){return e.parentPath&&e.parentPath.isObjectPattern()}},SpreadProperty:{types:["RestElement"],checkPath:function(e){return e.parentPath&&e.parentPath.isObjectExpression()}},ExistentialTypeParam:{types:["ExistsTypeAnnotation"]},NumericLiteralTypeAnnotation:{types:["NumberLiteralTypeAnnotation"]},ForAwaitStatement:{types:["ForOfStatement"],checkPath:function(e){return!0===e.node.await}}}),Df=1e3,Cf=6e4,Tf=36e5,jf=24*Tf,Pf=function(e,t){t=t||{};var r=typeof e;if("string"===r&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var r=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*r;case"weeks":case"week":case"w":return 6048e5*r;case"days":case"day":case"d":return r*jf;case"hours":case"hour":case"hrs":case"hr":case"h":return r*Tf;case"minutes":case"minute":case"mins":case"min":case"m":return r*Cf;case"seconds":case"second":case"secs":case"sec":case"s":return r*Df;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}(e);if("number"===r&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=jf)return kf(e,t,jf,"day");if(t>=Tf)return kf(e,t,Tf,"hour");if(t>=Cf)return kf(e,t,Cf,"minute");if(t>=Df)return kf(e,t,Df,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=jf)return Math.round(e/jf)+"d";if(t>=Tf)return Math.round(e/Tf)+"h";if(t>=Cf)return Math.round(e/Cf)+"m";if(t>=Df)return Math.round(e/Df)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function kf(e,t,r,n){var a=t>=1.5*r;return Math.round(e/r)+" "+n+(a?"s":"")}var Ff=function(e){function t(e){for(var t=0,n=0;n<e.length;n++)t=(t<<5)-t+e.charCodeAt(n),t|=0;return r.colors[Math.abs(t)%r.colors.length]}function r(e){var s;function i(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];if(i.enabled){var a=i,o=Number(new Date),u=o-(s||o);a.diff=u,a.prev=s,a.curr=o,s=o,t[0]=r.coerce(t[0]),"string"!=typeof t[0]&&t.unshift("%O");var c=0;t[0]=t[0].replace(/%([a-zA-Z%])/g,(function(e,n){if("%%"===e)return e;c++;var s=r.formatters[n];if("function"==typeof s){var i=t[c];e=s.call(a,i),t.splice(c,1),c--}return e})),r.formatArgs.call(a,t);var l=a.log||r.log;l.apply(a,t)}}return i.namespace=e,i.enabled=r.enabled(e),i.useColors=r.useColors(),i.color=t(e),i.destroy=n,i.extend=a,"function"==typeof r.init&&r.init(i),r.instances.push(i),i}function n(){var e=r.instances.indexOf(this);return-1!==e&&(r.instances.splice(e,1),!0)}function a(e,t){var n=r(this.namespace+(void 0===t?":":t)+e);return n.log=this.log,n}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return r.debug=r,r.default=r,r.coerce=function(e){if(e instanceof Error)return e.stack||e.message;return e},r.disable=function(){var e=[].concat(r.names.map(s),r.skips.map(s).map((function(e){return"-"+e}))).join(",");return r.enable(""),e},r.enable=function(e){var t;r.save(e),r.names=[],r.skips=[];var n=("string"==typeof e?e:"").split(/[\s,]+/),a=n.length;for(t=0;t<a;t++)n[t]&&("-"===(e=n[t].replace(/\*/g,".*?"))[0]?r.skips.push(new RegExp("^"+e.substr(1)+"$")):r.names.push(new RegExp("^"+e+"$")));for(t=0;t<r.instances.length;t++){var s=r.instances[t];s.enabled=r.enabled(s.namespace)}},r.enabled=function(e){if("*"===e[e.length-1])return!0;var t,n;for(t=0,n=r.skips.length;t<n;t++)if(r.skips[t].test(e))return!1;for(t=0,n=r.names.length;t<n;t++)if(r.names[t].test(e))return!0;return!1},r.humanize=Pf,Object.keys(e).forEach((function(t){r[t]=e[t]})),r.instances=[],r.names=[],r.skips=[],r.formatters={},r.selectColor=t,r.enable(r.load()),r},_f=Vt((function(e,t){t.log=function(){var e;return"object"==typeof console&&console.log&&(e=console).log.apply(e,arguments)},t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;var r="color: "+this.color;t.splice(1,0,r,"color: inherit");var n=0,a=0;t[0].replace(/%[a-zA-Z%]/g,(function(e){"%%"!==e&&(n++,"%c"===e&&(a=n))})),t.splice(a,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){var e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==es&&"env"in es&&(e=es.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=Ff(t),e.exports.formatters.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}}));_f.log,_f.formatArgs,_f.save,_f.load,_f.useColors,_f.storage,_f.colors;var If=function(e){return"string"==typeof e||!tn(e)&&Yr(e)&&"[object String]"==rr(e)};var Bf=function(e){return"symbol"==typeof e||Yr(e)&&"[object Symbol]"==rr(e)},Of=/^\s+|\s+$/g,Nf=/^[-+]0x[0-9a-f]+$/i,Rf=/^0b[01]+$/i,Mf=/^0o[0-7]+$/i,Lf=parseInt;var Uf=function(e){if("number"==typeof e)return e;if(Bf(e))return NaN;if(nr(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=nr(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(Of,"");var r=Rf.test(e);return r||Mf.test(e)?Lf(e.slice(2),r?2:8):Nf.test(e)?NaN:+e};var Gf=function(e){return e?(e=Uf(e))===1/0||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0};var Vf=function(e){var t=Gf(e),r=t%1;return t==t?r?t-r:t:0};var Wf=function(e,t){for(var r=-1,n=null==e?0:e.length,a=Array(n);++r<n;)a[r]=t(e[r],r,e);return a};var Hf=function(e,t){return Wf(t,(function(t){return e[t]}))};var qf=function(e){return null==e?[]:Hf(e,wn(e))},Kf=Math.max;var zf=function(e,t,r,n){e=An(e)?e:qf(e),r=r&&!n?Vf(r):0;var a=e.length;return r<0&&(r=Kf(a+r,0)),If(e)?r<=a&&e.indexOf(t,r)>-1:!!a&&Ap(e,t,r)>-1},Xf=Math.floor;var Yf=function(e,t){var r="";if(!e||t<1||t>9007199254740991)return r;do{t%2&&(r+=e),(t=Xf(t/2))&&(e+=e)}while(t);return r};var Jf=function(e,t,r){if(!nr(r))return!1;var n=typeof t;return!!("number"==n?An(r)&&sn(t,r.length):"string"==n&&t in r)&&Tt(r[t],e)},$f=zt?zt.prototype:void 0,Qf=$f?$f.toString:void 0;var Zf=function e(t){if("string"==typeof t)return t;if(tn(t))return Wf(t,e)+"";if(Bf(t))return Qf?Qf.call(t):"";var r=t+"";return"0"==r&&1/t==-1/0?"-0":r};var eh=function(e){return null==e?"":Zf(e)};var th=function(e,t,r){return t=(r?Jf(e,t,r):void 0===t)?1:Vf(t),Yf(eh(e),t)},rh=function(){function e(e){var t=e.identifier,r=e.scope,n=e.path,a=e.kind;this.identifier=t,this.scope=r,this.path=n,this.kind=a,this.constantViolations=[],this.constant=!0,this.referencePaths=[],this.referenced=!1,this.references=0,this.clearValue()}var t=e.prototype;return t.deoptValue=function(){this.clearValue(),this.hasDeoptedValue=!0},t.setValue=function(e){this.hasDeoptedValue||(this.hasValue=!0,this.value=e)},t.clearValue=function(){this.hasDeoptedValue=!1,this.hasValue=!1,this.value=null},t.reassign=function(e){this.constant=!1,-1===this.constantViolations.indexOf(e)&&this.constantViolations.push(e)},t.reference=function(e){-1===this.referencePaths.indexOf(e)&&(this.referenced=!0,this.references++,this.referencePaths.push(e))},t.dereference=function(){this.references--,this.referenced=!!this.references},e}();function nh(e){if(!e.isExportDeclaration())throw new Error("Only export declarations can be splitted.");var t=e.isExportDefaultDeclaration(),r=e.get("declaration"),n=r.isClassDeclaration();if(t){var a=r.isFunctionDeclaration()||n,s=r.isScope()?r.scope.parent:r.scope,i=r.node.id,o=!1;i||(o=!0,i=s.generateUidIdentifier("default"),(a||r.isFunctionExpression()||r.isClassExpression())&&(r.node.id=pp(i)));var u=a?r:Do("var",[Co(pp(i),r.node)]),c=Ro(null,[Mo(pp(i),Qi("default"))]);return e.insertAfter(c),e.replaceWith(u),o&&s.registerDeclaration(e),e}if(e.get("specifiers").length>0)throw new Error("It doesn't make sense to split exported specifiers.");var l=r.getOuterBindingIdentifiers(),p=Ro(null,Object.keys(l).map((function(e){return Mo(Qi(e),Qi(e))})));return e.insertAfter(p),e.replaceWith(r.node),e}var ah={ReferencedIdentifier:function(e,t){var r=e.node;r.name===t.oldName&&(r.name=t.newName)},Scope:function(e,t){e.scope.bindingIdentifierEquals(t.oldName,t.binding.identifier)||e.skip()},"AssignmentExpression|Declaration":function(e,t){var r=e.getOuterBindingIdentifiers();for(var n in r)n===t.oldName&&(r[n].name=t.newName)}},sh=function(){function e(e,t,r){this.newName=r,this.oldName=t,this.binding=e}var t=e.prototype;return t.maybeConvertFromExportDeclaration=function(e){var t=e.parentPath;t.isExportDeclaration()&&(t.isExportDefaultDeclaration()&&!t.get("declaration").node.id||nh(t))},t.maybeConvertFromClassFunctionDeclaration=function(e){},t.maybeConvertFromClassFunctionExpression=function(e){},t.rename=function(e){var t=this.binding,r=this.oldName,n=this.newName,a=t.scope,s=t.path.find((function(e){return e.isDeclaration()||e.isFunctionExpression()||e.isClassExpression()}));s&&(s.getOuterBindingIdentifiers()[r]===t.identifier&&this.maybeConvertFromExportDeclaration(s));a.traverse(e||a.block,ah,this),e||(a.removeOwnBinding(r),a.bindings[n]=t,this.binding.identifier.name=n),t.type,s&&(this.maybeConvertFromClassFunctionDeclaration(s),this.maybeConvertFromClassFunctionExpression(s))},e}();var ih=function(e){return e};var oh=function(e,t,r){switch(r.length){case 0:return e.call(t);case 1:return e.call(t,r[0]);case 2:return e.call(t,r[0],r[1]);case 3:return e.call(t,r[0],r[1],r[2])}return e.apply(t,r)},uh=Math.max;var ch=function(e,t,r){return t=uh(void 0===t?e.length-1:t,0),function(){for(var n=arguments,a=-1,s=uh(n.length-t,0),i=Array(s);++a<s;)i[a]=n[t+a];a=-1;for(var o=Array(t+1);++a<t;)o[a]=n[a];return o[t]=r(i),oh(e,this,o)}};var lh=function(e){return function(){return e}},ph=Wr?function(e,t){return Wr(e,"toString",{configurable:!0,enumerable:!1,value:lh(t),writable:!0})}:ih,dh=Date.now;var fh=function(e){var t=0,r=0;return function(){var n=dh(),a=16-(n-r);if(r=n,a>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}(ph);var hh=function(e,t){return fh(ch(e,t,ih),e+"")},mh=Object.prototype,yh=mh.hasOwnProperty,gh=hh((function(e,t){e=Object(e);var r=-1,n=t.length,a=n>2?t[2]:void 0;for(a&&Jf(t[0],t[1],a)&&(n=1);++r<n;)for(var s=t[r],i=jn(s),o=-1,u=i.length;++o<u;){var c=i[o],l=e[c];(void 0===l||Tt(l,mh[c])&&!yh.call(e,c))&&(e[c]=s[c])}return e})),vh={Array:!1,ArrayBuffer:!1,Atomics:!1,BigInt:!1,BigInt64Array:!1,BigUint64Array:!1,Boolean:!1,constructor:!1,DataView:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Float32Array:!1,Float64Array:!1,Function:!1,globalThis:!1,hasOwnProperty:!1,Infinity:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Map:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,Promise:!1,propertyIsEnumerable:!1,Proxy:!1,RangeError:!1,ReferenceError:!1,Reflect:!1,RegExp:!1,Set:!1,SharedArrayBuffer:!1,String:!1,Symbol:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,Uint8ClampedArray:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1,WeakMap:!1,WeakSet:!1},bh={Array:!1,Boolean:!1,constructor:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Function:!1,hasOwnProperty:!1,Infinity:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,propertyIsEnumerable:!1,RangeError:!1,ReferenceError:!1,RegExp:!1,String:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1},xh={Array:!1,ArrayBuffer:!1,Boolean:!1,constructor:!1,DataView:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Float32Array:!1,Float64Array:!1,Function:!1,hasOwnProperty:!1,Infinity:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Map:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,Promise:!1,propertyIsEnumerable:!1,Proxy:!1,RangeError:!1,ReferenceError:!1,Reflect:!1,RegExp:!1,Set:!1,String:!1,Symbol:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,Uint8ClampedArray:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1,WeakMap:!1,WeakSet:!1},Eh={Array:!1,ArrayBuffer:!1,Atomics:!1,Boolean:!1,constructor:!1,DataView:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Float32Array:!1,Float64Array:!1,Function:!1,hasOwnProperty:!1,Infinity:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Map:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,Promise:!1,propertyIsEnumerable:!1,Proxy:!1,RangeError:!1,ReferenceError:!1,Reflect:!1,RegExp:!1,Set:!1,SharedArrayBuffer:!1,String:!1,Symbol:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,Uint8ClampedArray:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1,WeakMap:!1,WeakSet:!1},Ah={AbortController:!1,AbortSignal:!1,addEventListener:!1,alert:!1,AnalyserNode:!1,Animation:!1,AnimationEffectReadOnly:!1,AnimationEffectTiming:!1,AnimationEffectTimingReadOnly:!1,AnimationEvent:!1,AnimationPlaybackEvent:!1,AnimationTimeline:!1,applicationCache:!1,ApplicationCache:!1,ApplicationCacheErrorEvent:!1,atob:!1,Attr:!1,Audio:!1,AudioBuffer:!1,AudioBufferSourceNode:!1,AudioContext:!1,AudioDestinationNode:!1,AudioListener:!1,AudioNode:!1,AudioParam:!1,AudioProcessingEvent:!1,AudioScheduledSourceNode:!1,"AudioWorkletGlobalScope ":!1,AudioWorkletNode:!1,AudioWorkletProcessor:!1,BarProp:!1,BaseAudioContext:!1,BatteryManager:!1,BeforeUnloadEvent:!1,BiquadFilterNode:!1,Blob:!1,BlobEvent:!1,blur:!1,BroadcastChannel:!1,btoa:!1,BudgetService:!1,ByteLengthQueuingStrategy:!1,Cache:!1,caches:!1,CacheStorage:!1,cancelAnimationFrame:!1,cancelIdleCallback:!1,CanvasCaptureMediaStreamTrack:!1,CanvasGradient:!1,CanvasPattern:!1,CanvasRenderingContext2D:!1,ChannelMergerNode:!1,ChannelSplitterNode:!1,CharacterData:!1,clearInterval:!1,clearTimeout:!1,clientInformation:!1,ClipboardEvent:!1,close:!1,closed:!1,CloseEvent:!1,Comment:!1,CompositionEvent:!1,confirm:!1,console:!1,ConstantSourceNode:!1,ConvolverNode:!1,CountQueuingStrategy:!1,createImageBitmap:!1,Credential:!1,CredentialsContainer:!1,crypto:!1,Crypto:!1,CryptoKey:!1,CSS:!1,CSSConditionRule:!1,CSSFontFaceRule:!1,CSSGroupingRule:!1,CSSImportRule:!1,CSSKeyframeRule:!1,CSSKeyframesRule:!1,CSSMediaRule:!1,CSSNamespaceRule:!1,CSSPageRule:!1,CSSRule:!1,CSSRuleList:!1,CSSStyleDeclaration:!1,CSSStyleRule:!1,CSSStyleSheet:!1,CSSSupportsRule:!1,CustomElementRegistry:!1,customElements:!1,CustomEvent:!1,DataTransfer:!1,DataTransferItem:!1,DataTransferItemList:!1,defaultstatus:!1,defaultStatus:!1,DelayNode:!1,DeviceMotionEvent:!1,DeviceOrientationEvent:!1,devicePixelRatio:!1,dispatchEvent:!1,document:!1,Document:!1,DocumentFragment:!1,DocumentType:!1,DOMError:!1,DOMException:!1,DOMImplementation:!1,DOMMatrix:!1,DOMMatrixReadOnly:!1,DOMParser:!1,DOMPoint:!1,DOMPointReadOnly:!1,DOMQuad:!1,DOMRect:!1,DOMRectReadOnly:!1,DOMStringList:!1,DOMStringMap:!1,DOMTokenList:!1,DragEvent:!1,DynamicsCompressorNode:!1,Element:!1,ErrorEvent:!1,event:!1,Event:!1,EventSource:!1,EventTarget:!1,external:!1,fetch:!1,File:!1,FileList:!1,FileReader:!1,find:!1,focus:!1,FocusEvent:!1,FontFace:!1,FontFaceSetLoadEvent:!1,FormData:!1,frameElement:!1,frames:!1,GainNode:!1,Gamepad:!1,GamepadButton:!1,GamepadEvent:!1,getComputedStyle:!1,getSelection:!1,HashChangeEvent:!1,Headers:!1,history:!1,History:!1,HTMLAllCollection:!1,HTMLAnchorElement:!1,HTMLAreaElement:!1,HTMLAudioElement:!1,HTMLBaseElement:!1,HTMLBodyElement:!1,HTMLBRElement:!1,HTMLButtonElement:!1,HTMLCanvasElement:!1,HTMLCollection:!1,HTMLContentElement:!1,HTMLDataElement:!1,HTMLDataListElement:!1,HTMLDetailsElement:!1,HTMLDialogElement:!1,HTMLDirectoryElement:!1,HTMLDivElement:!1,HTMLDListElement:!1,HTMLDocument:!1,HTMLElement:!1,HTMLEmbedElement:!1,HTMLFieldSetElement:!1,HTMLFontElement:!1,HTMLFormControlsCollection:!1,HTMLFormElement:!1,HTMLFrameElement:!1,HTMLFrameSetElement:!1,HTMLHeadElement:!1,HTMLHeadingElement:!1,HTMLHRElement:!1,HTMLHtmlElement:!1,HTMLIFrameElement:!1,HTMLImageElement:!1,HTMLInputElement:!1,HTMLLabelElement:!1,HTMLLegendElement:!1,HTMLLIElement:!1,HTMLLinkElement:!1,HTMLMapElement:!1,HTMLMarqueeElement:!1,HTMLMediaElement:!1,HTMLMenuElement:!1,HTMLMetaElement:!1,HTMLMeterElement:!1,HTMLModElement:!1,HTMLObjectElement:!1,HTMLOListElement:!1,HTMLOptGroupElement:!1,HTMLOptionElement:!1,HTMLOptionsCollection:!1,HTMLOutputElement:!1,HTMLParagraphElement:!1,HTMLParamElement:!1,HTMLPictureElement:!1,HTMLPreElement:!1,HTMLProgressElement:!1,HTMLQuoteElement:!1,HTMLScriptElement:!1,HTMLSelectElement:!1,HTMLShadowElement:!1,HTMLSlotElement:!1,HTMLSourceElement:!1,HTMLSpanElement:!1,HTMLStyleElement:!1,HTMLTableCaptionElement:!1,HTMLTableCellElement:!1,HTMLTableColElement:!1,HTMLTableElement:!1,HTMLTableRowElement:!1,HTMLTableSectionElement:!1,HTMLTemplateElement:!1,HTMLTextAreaElement:!1,HTMLTimeElement:!1,HTMLTitleElement:!1,HTMLTrackElement:!1,HTMLUListElement:!1,HTMLUnknownElement:!1,HTMLVideoElement:!1,IDBCursor:!1,IDBCursorWithValue:!1,IDBDatabase:!1,IDBFactory:!1,IDBIndex:!1,IDBKeyRange:!1,IDBObjectStore:!1,IDBOpenDBRequest:!1,IDBRequest:!1,IDBTransaction:!1,IDBVersionChangeEvent:!1,IdleDeadline:!1,IIRFilterNode:!1,Image:!1,ImageBitmap:!1,ImageBitmapRenderingContext:!1,ImageCapture:!1,ImageData:!1,indexedDB:!1,innerHeight:!1,innerWidth:!1,InputEvent:!1,IntersectionObserver:!1,IntersectionObserverEntry:!1,Intl:!1,isSecureContext:!1,KeyboardEvent:!1,KeyframeEffect:!1,KeyframeEffectReadOnly:!1,length:!1,localStorage:!1,location:!0,Location:!1,locationbar:!1,matchMedia:!1,MediaDeviceInfo:!1,MediaDevices:!1,MediaElementAudioSourceNode:!1,MediaEncryptedEvent:!1,MediaError:!1,MediaKeyMessageEvent:!1,MediaKeySession:!1,MediaKeyStatusMap:!1,MediaKeySystemAccess:!1,MediaList:!1,MediaQueryList:!1,MediaQueryListEvent:!1,MediaRecorder:!1,MediaSettingsRange:!1,MediaSource:!1,MediaStream:!1,MediaStreamAudioDestinationNode:!1,MediaStreamAudioSourceNode:!1,MediaStreamEvent:!1,MediaStreamTrack:!1,MediaStreamTrackEvent:!1,menubar:!1,MessageChannel:!1,MessageEvent:!1,MessagePort:!1,MIDIAccess:!1,MIDIConnectionEvent:!1,MIDIInput:!1,MIDIInputMap:!1,MIDIMessageEvent:!1,MIDIOutput:!1,MIDIOutputMap:!1,MIDIPort:!1,MimeType:!1,MimeTypeArray:!1,MouseEvent:!1,moveBy:!1,moveTo:!1,MutationEvent:!1,MutationObserver:!1,MutationRecord:!1,name:!1,NamedNodeMap:!1,NavigationPreloadManager:!1,navigator:!1,Navigator:!1,NetworkInformation:!1,Node:!1,NodeFilter:!1,NodeIterator:!1,NodeList:!1,Notification:!1,OfflineAudioCompletionEvent:!1,OfflineAudioContext:!1,offscreenBuffering:!1,OffscreenCanvas:!0,onabort:!0,onafterprint:!0,onanimationend:!0,onanimationiteration:!0,onanimationstart:!0,onappinstalled:!0,onauxclick:!0,onbeforeinstallprompt:!0,onbeforeprint:!0,onbeforeunload:!0,onblur:!0,oncancel:!0,oncanplay:!0,oncanplaythrough:!0,onchange:!0,onclick:!0,onclose:!0,oncontextmenu:!0,oncuechange:!0,ondblclick:!0,ondevicemotion:!0,ondeviceorientation:!0,ondeviceorientationabsolute:!0,ondrag:!0,ondragend:!0,ondragenter:!0,ondragleave:!0,ondragover:!0,ondragstart:!0,ondrop:!0,ondurationchange:!0,onemptied:!0,onended:!0,onerror:!0,onfocus:!0,ongotpointercapture:!0,onhashchange:!0,oninput:!0,oninvalid:!0,onkeydown:!0,onkeypress:!0,onkeyup:!0,onlanguagechange:!0,onload:!0,onloadeddata:!0,onloadedmetadata:!0,onloadstart:!0,onlostpointercapture:!0,onmessage:!0,onmessageerror:!0,onmousedown:!0,onmouseenter:!0,onmouseleave:!0,onmousemove:!0,onmouseout:!0,onmouseover:!0,onmouseup:!0,onmousewheel:!0,onoffline:!0,ononline:!0,onpagehide:!0,onpageshow:!0,onpause:!0,onplay:!0,onplaying:!0,onpointercancel:!0,onpointerdown:!0,onpointerenter:!0,onpointerleave:!0,onpointermove:!0,onpointerout:!0,onpointerover:!0,onpointerup:!0,onpopstate:!0,onprogress:!0,onratechange:!0,onrejectionhandled:!0,onreset:!0,onresize:!0,onscroll:!0,onsearch:!0,onseeked:!0,onseeking:!0,onselect:!0,onstalled:!0,onstorage:!0,onsubmit:!0,onsuspend:!0,ontimeupdate:!0,ontoggle:!0,ontransitionend:!0,onunhandledrejection:!0,onunload:!0,onvolumechange:!0,onwaiting:!0,onwheel:!0,open:!1,openDatabase:!1,opener:!1,Option:!1,origin:!1,OscillatorNode:!1,outerHeight:!1,outerWidth:!1,PageTransitionEvent:!1,pageXOffset:!1,pageYOffset:!1,PannerNode:!1,parent:!1,Path2D:!1,PaymentAddress:!1,PaymentRequest:!1,PaymentRequestUpdateEvent:!1,PaymentResponse:!1,performance:!1,Performance:!1,PerformanceEntry:!1,PerformanceLongTaskTiming:!1,PerformanceMark:!1,PerformanceMeasure:!1,PerformanceNavigation:!1,PerformanceNavigationTiming:!1,PerformanceObserver:!1,PerformanceObserverEntryList:!1,PerformancePaintTiming:!1,PerformanceResourceTiming:!1,PerformanceTiming:!1,PeriodicWave:!1,Permissions:!1,PermissionStatus:!1,personalbar:!1,PhotoCapabilities:!1,Plugin:!1,PluginArray:!1,PointerEvent:!1,PopStateEvent:!1,postMessage:!1,Presentation:!1,PresentationAvailability:!1,PresentationConnection:!1,PresentationConnectionAvailableEvent:!1,PresentationConnectionCloseEvent:!1,PresentationConnectionList:!1,PresentationReceiver:!1,PresentationRequest:!1,print:!1,ProcessingInstruction:!1,ProgressEvent:!1,PromiseRejectionEvent:!1,prompt:!1,PushManager:!1,PushSubscription:!1,PushSubscriptionOptions:!1,queueMicrotask:!1,RadioNodeList:!1,Range:!1,ReadableStream:!1,registerProcessor:!1,RemotePlayback:!1,removeEventListener:!1,Request:!1,requestAnimationFrame:!1,requestIdleCallback:!1,resizeBy:!1,ResizeObserver:!1,ResizeObserverEntry:!1,resizeTo:!1,Response:!1,RTCCertificate:!1,RTCDataChannel:!1,RTCDataChannelEvent:!1,RTCDtlsTransport:!1,RTCIceCandidate:!1,RTCIceGatherer:!1,RTCIceTransport:!1,RTCPeerConnection:!1,RTCPeerConnectionIceEvent:!1,RTCRtpContributingSource:!1,RTCRtpReceiver:!1,RTCRtpSender:!1,RTCSctpTransport:!1,RTCSessionDescription:!1,RTCStatsReport:!1,RTCTrackEvent:!1,screen:!1,Screen:!1,screenLeft:!1,ScreenOrientation:!1,screenTop:!1,screenX:!1,screenY:!1,ScriptProcessorNode:!1,scroll:!1,scrollbars:!1,scrollBy:!1,scrollTo:!1,scrollX:!1,scrollY:!1,SecurityPolicyViolationEvent:!1,Selection:!1,self:!1,ServiceWorker:!1,ServiceWorkerContainer:!1,ServiceWorkerRegistration:!1,sessionStorage:!1,setInterval:!1,setTimeout:!1,ShadowRoot:!1,SharedWorker:!1,SourceBuffer:!1,SourceBufferList:!1,speechSynthesis:!1,SpeechSynthesisEvent:!1,SpeechSynthesisUtterance:!1,StaticRange:!1,status:!1,statusbar:!1,StereoPannerNode:!1,stop:!1,Storage:!1,StorageEvent:!1,StorageManager:!1,styleMedia:!1,StyleSheet:!1,StyleSheetList:!1,SubtleCrypto:!1,SVGAElement:!1,SVGAngle:!1,SVGAnimatedAngle:!1,SVGAnimatedBoolean:!1,SVGAnimatedEnumeration:!1,SVGAnimatedInteger:!1,SVGAnimatedLength:!1,SVGAnimatedLengthList:!1,SVGAnimatedNumber:!1,SVGAnimatedNumberList:!1,SVGAnimatedPreserveAspectRatio:!1,SVGAnimatedRect:!1,SVGAnimatedString:!1,SVGAnimatedTransformList:!1,SVGAnimateElement:!1,SVGAnimateMotionElement:!1,SVGAnimateTransformElement:!1,SVGAnimationElement:!1,SVGCircleElement:!1,SVGClipPathElement:!1,SVGComponentTransferFunctionElement:!1,SVGDefsElement:!1,SVGDescElement:!1,SVGDiscardElement:!1,SVGElement:!1,SVGEllipseElement:!1,SVGFEBlendElement:!1,SVGFEColorMatrixElement:!1,SVGFEComponentTransferElement:!1,SVGFECompositeElement:!1,SVGFEConvolveMatrixElement:!1,SVGFEDiffuseLightingElement:!1,SVGFEDisplacementMapElement:!1,SVGFEDistantLightElement:!1,SVGFEDropShadowElement:!1,SVGFEFloodElement:!1,SVGFEFuncAElement:!1,SVGFEFuncBElement:!1,SVGFEFuncGElement:!1,SVGFEFuncRElement:!1,SVGFEGaussianBlurElement:!1,SVGFEImageElement:!1,SVGFEMergeElement:!1,SVGFEMergeNodeElement:!1,SVGFEMorphologyElement:!1,SVGFEOffsetElement:!1,SVGFEPointLightElement:!1,SVGFESpecularLightingElement:!1,SVGFESpotLightElement:!1,SVGFETileElement:!1,SVGFETurbulenceElement:!1,SVGFilterElement:!1,SVGForeignObjectElement:!1,SVGGElement:!1,SVGGeometryElement:!1,SVGGradientElement:!1,SVGGraphicsElement:!1,SVGImageElement:!1,SVGLength:!1,SVGLengthList:!1,SVGLinearGradientElement:!1,SVGLineElement:!1,SVGMarkerElement:!1,SVGMaskElement:!1,SVGMatrix:!1,SVGMetadataElement:!1,SVGMPathElement:!1,SVGNumber:!1,SVGNumberList:!1,SVGPathElement:!1,SVGPatternElement:!1,SVGPoint:!1,SVGPointList:!1,SVGPolygonElement:!1,SVGPolylineElement:!1,SVGPreserveAspectRatio:!1,SVGRadialGradientElement:!1,SVGRect:!1,SVGRectElement:!1,SVGScriptElement:!1,SVGSetElement:!1,SVGStopElement:!1,SVGStringList:!1,SVGStyleElement:!1,SVGSVGElement:!1,SVGSwitchElement:!1,SVGSymbolElement:!1,SVGTextContentElement:!1,SVGTextElement:!1,SVGTextPathElement:!1,SVGTextPositioningElement:!1,SVGTitleElement:!1,SVGTransform:!1,SVGTransformList:!1,SVGTSpanElement:!1,SVGUnitTypes:!1,SVGUseElement:!1,SVGViewElement:!1,TaskAttributionTiming:!1,Text:!1,TextDecoder:!1,TextEncoder:!1,TextEvent:!1,TextMetrics:!1,TextTrack:!1,TextTrackCue:!1,TextTrackCueList:!1,TextTrackList:!1,TimeRanges:!1,toolbar:!1,top:!1,Touch:!1,TouchEvent:!1,TouchList:!1,TrackEvent:!1,TransitionEvent:!1,TreeWalker:!1,UIEvent:!1,URL:!1,URLSearchParams:!1,ValidityState:!1,visualViewport:!1,VisualViewport:!1,VTTCue:!1,WaveShaperNode:!1,WebAssembly:!1,WebGL2RenderingContext:!1,WebGLActiveInfo:!1,WebGLBuffer:!1,WebGLContextEvent:!1,WebGLFramebuffer:!1,WebGLProgram:!1,WebGLQuery:!1,WebGLRenderbuffer:!1,WebGLRenderingContext:!1,WebGLSampler:!1,WebGLShader:!1,WebGLShaderPrecisionFormat:!1,WebGLSync:!1,WebGLTexture:!1,WebGLTransformFeedback:!1,WebGLUniformLocation:!1,WebGLVertexArrayObject:!1,WebSocket:!1,WheelEvent:!1,window:!1,Window:!1,Worker:!1,WritableStream:!1,XMLDocument:!1,XMLHttpRequest:!1,XMLHttpRequestEventTarget:!1,XMLHttpRequestUpload:!1,XMLSerializer:!1,XPathEvaluator:!1,XPathExpression:!1,XPathResult:!1,XSLTProcessor:!1},wh={addEventListener:!1,applicationCache:!1,atob:!1,Blob:!1,BroadcastChannel:!1,btoa:!1,Cache:!1,caches:!1,clearInterval:!1,clearTimeout:!1,close:!0,console:!1,fetch:!1,FileReaderSync:!1,FormData:!1,Headers:!1,IDBCursor:!1,IDBCursorWithValue:!1,IDBDatabase:!1,IDBFactory:!1,IDBIndex:!1,IDBKeyRange:!1,IDBObjectStore:!1,IDBOpenDBRequest:!1,IDBRequest:!1,IDBTransaction:!1,IDBVersionChangeEvent:!1,ImageData:!1,importScripts:!0,indexedDB:!1,location:!1,MessageChannel:!1,MessagePort:!1,name:!1,navigator:!1,Notification:!1,onclose:!0,onconnect:!0,onerror:!0,onlanguagechange:!0,onmessage:!0,onoffline:!0,ononline:!0,onrejectionhandled:!0,onunhandledrejection:!0,performance:!1,Performance:!1,PerformanceEntry:!1,PerformanceMark:!1,PerformanceMeasure:!1,PerformanceNavigation:!1,PerformanceResourceTiming:!1,PerformanceTiming:!1,postMessage:!0,Promise:!1,queueMicrotask:!1,removeEventListener:!1,Request:!1,Response:!1,self:!0,ServiceWorkerRegistration:!1,setInterval:!1,setTimeout:!1,TextDecoder:!1,TextEncoder:!1,URL:!1,URLSearchParams:!1,WebSocket:!1,Worker:!1,WorkerGlobalScope:!1,XMLHttpRequest:!1},Sh={__dirname:!1,__filename:!1,Buffer:!1,clearImmediate:!1,clearInterval:!1,clearTimeout:!1,console:!1,exports:!0,global:!1,Intl:!1,module:!1,process:!1,queueMicrotask:!1,require:!1,setImmediate:!1,setInterval:!1,setTimeout:!1,TextDecoder:!1,TextEncoder:!1,URL:!1,URLSearchParams:!1},Dh={exports:!0,global:!1,module:!1,require:!1},Ch={define:!1,require:!1},Th={after:!1,afterEach:!1,before:!1,beforeEach:!1,context:!1,describe:!1,it:!1,mocha:!1,run:!1,setup:!1,specify:!1,suite:!1,suiteSetup:!1,suiteTeardown:!1,teardown:!1,test:!1,xcontext:!1,xdescribe:!1,xit:!1,xspecify:!1},jh={afterAll:!1,afterEach:!1,beforeAll:!1,beforeEach:!1,describe:!1,expect:!1,fail:!1,fdescribe:!1,fit:!1,it:!1,jasmine:!1,pending:!1,runs:!1,spyOn:!1,spyOnProperty:!1,waits:!1,waitsFor:!1,xdescribe:!1,xit:!1},Ph={afterAll:!1,afterEach:!1,beforeAll:!1,beforeEach:!1,describe:!1,expect:!1,fdescribe:!1,fit:!1,it:!1,jest:!1,pit:!1,require:!1,test:!1,xdescribe:!1,xit:!1,xtest:!1},kh={asyncTest:!1,deepEqual:!1,equal:!1,expect:!1,module:!1,notDeepEqual:!1,notEqual:!1,notOk:!1,notPropEqual:!1,notStrictEqual:!1,ok:!1,propEqual:!1,QUnit:!1,raises:!1,start:!1,stop:!1,strictEqual:!1,test:!1,throws:!1},Fh={console:!0,exports:!0,phantom:!0,require:!0,WebPage:!0},_h={emit:!1,exports:!1,getRow:!1,log:!1,module:!1,provides:!1,require:!1,respond:!1,send:!1,start:!1,sum:!1},Ih={defineClass:!1,deserialize:!1,gc:!1,help:!1,importClass:!1,importPackage:!1,java:!1,load:!1,loadClass:!1,Packages:!1,print:!1,quit:!1,readFile:!1,readUrl:!1,runCommand:!1,seal:!1,serialize:!1,spawn:!1,sync:!1,toint32:!1,version:!1},Bh={__DIR__:!1,__FILE__:!1,__LINE__:!1,com:!1,edu:!1,exit:!1,java:!1,Java:!1,javafx:!1,JavaImporter:!1,javax:!1,JSAdapter:!1,load:!1,loadWithNewGlobal:!1,org:!1,Packages:!1,print:!1,quit:!1},Oh={ActiveXObject:!0,Enumerator:!0,GetObject:!0,ScriptEngine:!0,ScriptEngineBuildVersion:!0,ScriptEngineMajorVersion:!0,ScriptEngineMinorVersion:!0,VBArray:!0,WScript:!0,WSH:!0,XDomainRequest:!0},Nh={$:!1,jQuery:!1},Rh={YAHOO:!1,YAHOO_config:!1,YUI:!1,YUI_config:!1},Mh={cat:!1,cd:!1,chmod:!1,config:!1,cp:!1,dirs:!1,echo:!1,env:!1,error:!1,exec:!1,exit:!1,find:!1,grep:!1,ln:!1,ls:!1,mkdir:!1,mv:!1,popd:!1,pushd:!1,pwd:!1,rm:!1,sed:!1,set:!1,target:!1,tempdir:!1,test:!1,touch:!1,which:!1},Lh={$:!1,$$:!1,$A:!1,$break:!1,$continue:!1,$F:!1,$H:!1,$R:!1,$w:!1,Abstract:!1,Ajax:!1,Autocompleter:!1,Builder:!1,Class:!1,Control:!1,Draggable:!1,Draggables:!1,Droppables:!1,Effect:!1,Element:!1,Enumerable:!1,Event:!1,Field:!1,Form:!1,Hash:!1,Insertion:!1,ObjectRange:!1,PeriodicalExecuter:!1,Position:!1,Prototype:!1,Scriptaculous:!1,Selector:!1,Sortable:!1,SortableObserver:!1,Sound:!1,Template:!1,Toggle:!1,Try:!1},Uh={_:!1,$:!1,Accounts:!1,AccountsClient:!1,AccountsCommon:!1,AccountsServer:!1,App:!1,Assets:!1,Blaze:!1,check:!1,Cordova:!1,DDP:!1,DDPRateLimiter:!1,DDPServer:!1,Deps:!1,EJSON:!1,Email:!1,HTTP:!1,Log:!1,Match:!1,Meteor:!1,Mongo:!1,MongoInternals:!1,Npm:!1,Package:!1,Plugin:!1,process:!1,Random:!1,ReactiveDict:!1,ReactiveVar:!1,Router:!1,ServiceConfiguration:!1,Session:!1,share:!1,Spacebars:!1,Template:!1,Tinytest:!1,Tracker:!1,UI:!1,Utils:!1,WebApp:!1,WebAppInternals:!1},Gh={_isWindows:!1,_rand:!1,BulkWriteResult:!1,cat:!1,cd:!1,connect:!1,db:!1,getHostName:!1,getMemInfo:!1,hostname:!1,ISODate:!1,listFiles:!1,load:!1,ls:!1,md5sumFile:!1,mkdir:!1,Mongo:!1,NumberInt:!1,NumberLong:!1,ObjectId:!1,PlanCache:!1,print:!1,printjson:!1,pwd:!1,quit:!1,removeFile:!1,rs:!1,sh:!1,UUID:!1,version:!1,WriteResult:!1},Vh={$:!1,Application:!1,Automation:!1,console:!1,delay:!1,Library:!1,ObjC:!1,ObjectSpecifier:!1,Path:!1,Progress:!1,Ref:!1},Wh={addEventListener:!1,applicationCache:!1,atob:!1,Blob:!1,BroadcastChannel:!1,btoa:!1,Cache:!1,caches:!1,CacheStorage:!1,clearInterval:!1,clearTimeout:!1,Client:!1,clients:!1,Clients:!1,close:!0,console:!1,ExtendableEvent:!1,ExtendableMessageEvent:!1,fetch:!1,FetchEvent:!1,FileReaderSync:!1,FormData:!1,Headers:!1,IDBCursor:!1,IDBCursorWithValue:!1,IDBDatabase:!1,IDBFactory:!1,IDBIndex:!1,IDBKeyRange:!1,IDBObjectStore:!1,IDBOpenDBRequest:!1,IDBRequest:!1,IDBTransaction:!1,IDBVersionChangeEvent:!1,ImageData:!1,importScripts:!1,indexedDB:!1,location:!1,MessageChannel:!1,MessagePort:!1,name:!1,navigator:!1,Notification:!1,onclose:!0,onconnect:!0,onerror:!0,onfetch:!0,oninstall:!0,onlanguagechange:!0,onmessage:!0,onmessageerror:!0,onnotificationclick:!0,onnotificationclose:!0,onoffline:!0,ononline:!0,onpush:!0,onpushsubscriptionchange:!0,onrejectionhandled:!0,onsync:!0,onunhandledrejection:!0,performance:!1,Performance:!1,PerformanceEntry:!1,PerformanceMark:!1,PerformanceMeasure:!1,PerformanceNavigation:!1,PerformanceResourceTiming:!1,PerformanceTiming:!1,postMessage:!0,Promise:!1,queueMicrotask:!1,registration:!1,removeEventListener:!1,Request:!1,Response:!1,self:!1,ServiceWorker:!1,ServiceWorkerContainer:!1,ServiceWorkerGlobalScope:!1,ServiceWorkerMessageEvent:!1,ServiceWorkerRegistration:!1,setInterval:!1,setTimeout:!1,skipWaiting:!1,TextDecoder:!1,TextEncoder:!1,URL:!1,URLSearchParams:!1,WebSocket:!1,WindowClient:!1,Worker:!1,WorkerGlobalScope:!1,XMLHttpRequest:!1},Hh={advanceClock:!1,fakeClearInterval:!1,fakeClearTimeout:!1,fakeSetInterval:!1,fakeSetTimeout:!1,resetTimeouts:!1,waitsForPromise:!1},qh={andThen:!1,click:!1,currentPath:!1,currentRouteName:!1,currentURL:!1,fillIn:!1,find:!1,findAll:!1,findWithAssert:!1,keyEvent:!1,pauseTest:!1,resumeTest:!1,triggerEvent:!1,visit:!1,wait:!1},Kh={$:!1,$$:!1,browser:!1,by:!1,By:!1,DartObject:!1,element:!1,protractor:!1},zh={browser:!1,chrome:!1,opr:!1},Xh={cloneInto:!1,createObjectIn:!1,exportFunction:!1,GM:!1,GM_addStyle:!1,GM_deleteValue:!1,GM_getResourceText:!1,GM_getResourceURL:!1,GM_getValue:!1,GM_info:!1,GM_listValues:!1,GM_log:!1,GM_openInTab:!1,GM_registerMenuCommand:!1,GM_setClipboard:!1,GM_setValue:!1,GM_xmlhttpRequest:!1,unsafeWindow:!1},Yh={$:!1,$_:!1,$$:!1,$0:!1,$1:!1,$2:!1,$3:!1,$4:!1,$x:!1,chrome:!1,clear:!1,copy:!1,debug:!1,dir:!1,dirxml:!1,getEventListeners:!1,inspect:!1,keys:!1,monitor:!1,monitorEvents:!1,profile:!1,profileEnd:!1,queryObjects:!1,table:!1,undebug:!1,unmonitor:!1,unmonitorEvents:!1,values:!1},Jh={builtin:vh,es5:bh,es2015:xh,es2017:Eh,browser:Ah,worker:wh,node:Sh,commonjs:Dh,amd:Ch,mocha:Th,jasmine:jh,jest:Ph,qunit:kh,phantomjs:Fh,couch:_h,rhino:Ih,nashorn:Bh,wsh:Oh,jquery:Nh,yui:Rh,shelljs:Mh,prototypejs:Lh,meteor:Uh,mongo:Gh,applescript:Vh,serviceworker:Wh,atomtest:Hh,embertest:qh,protractor:Kh,"shared-node-browser":{clearInterval:!1,clearTimeout:!1,console:!1,setInterval:!1,setTimeout:!1,URL:!1,URLSearchParams:!1},webextensions:zh,greasemonkey:Xh,devtools:Yh},$h=Wt(Object.freeze({__proto__:null,builtin:vh,es5:bh,es2015:xh,es2017:Eh,browser:Ah,worker:wh,node:Sh,commonjs:Dh,amd:Ch,mocha:Th,jasmine:jh,jest:Ph,qunit:kh,phantomjs:Fh,couch:_h,rhino:Ih,nashorn:Bh,wsh:Oh,jquery:Nh,yui:Rh,shelljs:Mh,prototypejs:Lh,meteor:Uh,mongo:Gh,applescript:Vh,serviceworker:Wh,atomtest:Hh,embertest:qh,protractor:Kh,webextensions:zh,greasemonkey:Xh,devtools:Yh,default:Jh})),Qh=new WeakMap,Zh=new WeakMap;function em(){Qh=new WeakMap}function tm(){Zh=new WeakMap}var rm=Object.freeze({__proto__:null,get path(){return Qh},get scope(){return Zh},clear:function(){em(),tm()},clearPath:em,clearScope:tm});var nm={For:function(e){for(var t=0,r=cs;t<r.length;t++){var n=r[t],a=e.get(n);if(a.isVar())(e.scope.getFunctionParent()||e.scope.getProgramParent()).registerBinding("var",a)}},Declaration:function(e){e.isBlockScoped()||(e.isExportDeclaration()&&e.get("declaration").isDeclaration()||(e.scope.getFunctionParent()||e.scope.getProgramParent()).registerDeclaration(e))},ReferencedIdentifier:function(e,t){t.references.push(e)},ForXStatement:function(e,t){var r=e.get("left");(r.isPattern()||r.isIdentifier())&&t.constantViolations.push(e)},ExportDeclaration:{exit:function(e){var t=e.node,r=e.scope,n=t.declaration;if(Z(n)||A(n)){var a=n.id;if(!a)return;var s=r.getBinding(a.name);s&&s.reference(e)}else if(q(n))for(var i=0,o=n.declarations;i<o.length;i++)for(var u=o[i],c=0,l=Object.keys(Ud(u));c<l.length;c++){var p=l[c],d=r.getBinding(p);d&&d.reference(e)}}},LabeledStatement:function(e){e.scope.getProgramParent().addGlobal(e.node),e.scope.getBlockParent().registerDeclaration(e)},AssignmentExpression:function(e,t){t.assignments.push(e)},UpdateExpression:function(e,t){t.constantViolations.push(e)},UnaryExpression:function(e,t){"delete"===e.node.operator&&t.constantViolations.push(e)},BlockScoped:function(e){var t=e.scope;if(t.path===e&&(t=t.parent),t.getBlockParent().registerDeclaration(e),e.isClassDeclaration()&&e.node.id){var r=e.node.id.name;e.scope.bindings[r]=e.scope.parent.getBinding(r)}},Block:function(e){for(var t=0,r=e.get("body");t<r.length;t++){var n=r[t];n.isFunctionDeclaration()&&e.scope.getBlockParent().registerDeclaration(n)}}},am=0,sm=function(){function e(e){var t=e.node,r=Zh.get(t);if(r&&r.path===e)return r;Zh.set(t,this),this.uid=am++,this.block=t,this.path=e,this.labels=new Map}var t=e.prototype;return t.traverse=function(e,t,r){sA(e,t,this,r,this.path)},t.generateDeclaredUidIdentifier=function(e){var t=this.generateUidIdentifier(e);return this.push({id:t}),pp(t)},t.generateUidIdentifier=function(e){return Qi(this.generateUid(e))},t.generateUid=function(e){var t;void 0===e&&(e="temp"),e=kd(e).replace(/^_+/,"").replace(/[0-9]+$/g,"");var r=0;do{t=this._generateUid(e,r),r++}while(this.hasLabel(t)||this.hasBinding(t)||this.hasGlobal(t)||this.hasReference(t));var n=this.getProgramParent();return n.references[t]=!0,n.uids[t]=!0,t},t._generateUid=function(e,t){var r=e;return t>1&&(r+=t),"_"+r},t.generateUidBasedOnNode=function(e,t){var r=e;c(e)?r=e.left:K(e)?r=e.id:(R(r)||N(r))&&(r=r.key);var n=[];!function e(t,r){if(vt(t))if(t.source)e(t.source,r);else if(t.specifiers&&t.specifiers.length)for(var n=0,a=t.specifiers;n<a.length;n++){e(a[n],r)}else t.declaration&&e(t.declaration,r);else if(xt(t))e(t.local,r);else if(_(t))e(t.object,r),e(t.property,r);else if(S(t))r.push(t.name);else if(ft(t))r.push(t.value);else if(f(t))e(t.callee,r);else if(O(t)||ce(t))for(var s=0,i=t.properties;s<i.length;s++){var o=i[s];e(o.key||o.argument,r)}else Xe(t)?e(t.id,r):V(t)?r.push("this"):pe(t)&&r.push("super")}(r,n);var a=n.join("$");return a=a.replace(/^_/,"")||t||"ref",this.generateUid(a.slice(0,20))},t.generateUidIdentifierBasedOnNode=function(e,t){return Qi(this.generateUidBasedOnNode(e,t))},t.isStatic=function(e){if(V(e)||pe(e))return!0;if(S(e)){var t=this.getBinding(e.name);return t?t.constant:this.hasBinding(e.name)}return!1},t.maybeGenerateMemoised=function(e,t){if(this.isStatic(e))return null;var r=this.generateUidIdentifierBasedOnNode(e);return t?r:(this.push({id:r}),pp(r))},t.checkBlockScopedCollisions=function(e,t,r,n){if("param"!==t&&("local"!==e.kind&&("let"===t||"let"===e.kind||"const"===e.kind||"module"===e.kind||"param"===e.kind&&("let"===t||"const"===t))))throw this.hub.buildError(n,'Duplicate declaration "'+r+'"',TypeError)},t.rename=function(e,t,r){var n=this.getBinding(e);if(n)return t=t||this.generateUidIdentifier(e).name,new sh(n,e,t).rename(r)},t._renameFromMap=function(e,t,r,n){e[t]&&(e[r]=n,e[t]=null)},t.dump=function(){var e=th("-",60);console.log(e);var t=this;do{console.log("#",t.block.type);for(var r=0,n=Object.keys(t.bindings);r<n.length;r++){var a=n[r],s=t.bindings[a];console.log(" -",a,{constant:s.constant,references:s.references,violations:s.constantViolations.length,kind:s.kind})}}while(t=t.parent);console.log(e)},t.toArray=function(e,t){if(S(e)){var r=this.getBinding(e.name);if(r&&r.constant&&r.path.isGenericType("Array"))return e}if(u(e))return e;if(S(e,{name:"arguments"}))return Li(oo(oo(oo(Qi("Array"),Qi("prototype")),Qi("slice")),Qi("call")),[e]);var n,a=[e];return!0===t?n="toConsumableArray":t?(a.push(ro(t)),n="slicedToArray"):n="toArray",Li(this.hub.addHelper(n),a)},t.hasLabel=function(e){return!!this.getLabel(e)},t.getLabel=function(e){return this.labels.get(e)},t.registerLabel=function(e){this.labels.set(e.node.label.name,e)},t.registerDeclaration=function(e){if(e.isLabeledStatement())this.registerLabel(e);else if(e.isFunctionDeclaration())this.registerBinding("hoisted",e.get("id"),e);else if(e.isVariableDeclaration())for(var t=0,r=e.get("declarations");t<r.length;t++){var n=r[t];this.registerBinding(e.node.kind,n)}else if(e.isClassDeclaration())this.registerBinding("let",e);else if(e.isImportDeclaration())for(var a=0,s=e.get("specifiers");a<s.length;a++){var i=s[a];this.registerBinding("module",i)}else if(e.isExportDeclaration()){var o=e.get("declaration");(o.isClassDeclaration()||o.isFunctionDeclaration()||o.isVariableDeclaration())&&this.registerDeclaration(o)}else this.registerBinding("unknown",e)},t.buildUndefinedNode=function(){return wo("void",ro(0),!0)},t.registerConstantViolation=function(e){for(var t=e.getBindingIdentifiers(),r=0,n=Object.keys(t);r<n.length;r++){var a=n[r],s=this.getBinding(a);s&&s.reassign(e)}},t.registerBinding=function(e,t,r){if(void 0===r&&(r=t),!e)throw new ReferenceError("no `kind`");if(t.isVariableDeclaration()){var n=t.get("declarations"),a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;this.registerBinding(e,o)}}else for(var u=this.getProgramParent(),c=t.getOuterBindingIdentifiers(!0),l=0,p=Object.keys(c);l<p.length;l++)for(var d=p[l],f=0,h=c[d];f<h.length;f++){var m=h[f],y=this.getOwnBinding(d);if(y){if(y.identifier===m)continue;this.checkBlockScopedCollisions(y,e,d,m)}u.references[d]=!0,y?this.registerConstantViolation(r):this.bindings[d]=new rh({identifier:m,scope:this,path:r,kind:e})}},t.addGlobal=function(e){this.globals[e.name]=e},t.hasUid=function(e){var t=this;do{if(t.uids[e])return!0}while(t=t.parent);return!1},t.hasGlobal=function(e){var t=this;do{if(t.globals[e])return!0}while(t=t.parent);return!1},t.hasReference=function(e){var t=this;do{if(t.references[e])return!0}while(t=t.parent);return!1},t.isPure=function(e,t){if(S(e)){var r=this.getBinding(e.name);return!!r&&(!t||r.constant)}if(gt(e))return!(e.superClass&&!this.isPure(e.superClass,t))&&this.isPure(e.body,t);if($(e)){var n=e.body,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;if(!this.isPure(o,t))return!1}return!0}if(nt(e))return this.isPure(e.left,t)&&this.isPure(e.right,t);if(u(e)){for(var c=0,l=e.elements;c<l.length;c++){var p=l[c];if(!this.isPure(p,t))return!1}return!0}if(O(e)){for(var d=0,f=e.properties;d<f.length;d++){var h=f[d];if(!this.isPure(h,t))return!1}return!0}if(ue(e))return!(e.computed&&!this.isPure(e.key,t))&&("get"!==e.kind&&"set"!==e.kind);if(ht(e))return!(e.computed&&!this.isPure(e.key,t))&&this.isPure(e.value,t);if(H(e))return this.isPure(e.argument,t);if(de(e))return wt(e.tag,"String.raw")&&!this.hasBinding("String",!0)&&this.isPure(e.quasi,t);if(fe(e)){for(var m=0,y=e.expressions;m<y.length;m++){var g=y[m];if(!this.isPure(g,t))return!1}return!0}return pt(e)},t.setData=function(e,t){return this.data[e]=t},t.getData=function(e){var t=this;do{var r=t.data[e];if(null!=r)return r}while(t=t.parent)},t.removeData=function(e){var t=this;do{null!=t.data[e]&&(t.data[e]=null)}while(t=t.parent)},t.init=function(){this.references||this.crawl()},t.crawl=function(){var e=this.path;if(this.references=Object.create(null),this.bindings=Object.create(null),this.globals=Object.create(null),this.uids=Object.create(null),this.data=Object.create(null),e.isLoop())for(var t=0,r=cs;t<r.length;t++){var n=r[t],a=e.get(n);a.isBlockScoped()&&this.registerBinding(a.node.kind,a)}if(e.isFunctionExpression()&&e.has("id")&&(e.get("id").node[Cs]||this.registerBinding("local",e.get("id"),e)),e.isClassExpression()&&e.has("id")&&(e.get("id").node[Cs]||this.registerBinding("local",e)),e.isFunction()){var s=e.get("params"),i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u;this.registerBinding("param",c)}}if(e.isCatchClause()&&this.registerBinding("let",e),!this.getProgramParent().crawling){var l={references:[],constantViolations:[],assignments:[]};this.crawling=!0,e.traverse(nm,l),this.crawling=!1;var p=l.assignments,d=Array.isArray(p),f=0;for(p=d?p:p[Symbol.iterator]();;){var h;if(d){if(f>=p.length)break;h=p[f++]}else{if((f=p.next()).done)break;h=f.value}for(var m=h,y=m.getBindingIdentifiers(),g=void 0,v=0,b=Object.keys(y);v<b.length;v++){var x=b[v];m.scope.getBinding(x)||(g=g||m.scope.getProgramParent()).addGlobal(y[x])}m.scope.registerConstantViolation(m)}var E=l.references,A=Array.isArray(E),w=0;for(E=A?E:E[Symbol.iterator]();;){var S;if(A){if(w>=E.length)break;S=E[w++]}else{if((w=E.next()).done)break;S=w.value}var D=S,C=D.scope.getBinding(D.node.name);C?C.reference(D):D.scope.getProgramParent().addGlobal(D.node)}var T=l.constantViolations,j=Array.isArray(T),P=0;for(T=j?T:T[Symbol.iterator]();;){var k;if(j){if(P>=T.length)break;k=T[P++]}else{if((P=T.next()).done)break;k=P.value}var F=k;F.scope.registerConstantViolation(F)}}},t.push=function(e){var t=this.path;t.isBlockStatement()||t.isProgram()||(t=this.getBlockParent().path),t.isSwitchStatement()&&(t=(this.getFunctionParent()||this.getProgramParent()).path),(t.isLoop()||t.isCatchClause()||t.isFunction())&&(t.ensureBlock(),t=t.get("body"));var r=e.unique,n=e.kind||"var",a=null==e._blockHoist?2:e._blockHoist,s="declaration:"+n+":"+a,i=!r&&t.getData(s);if(!i){var o=Do(n,[]);o._blockHoist=a,i=t.unshiftContainer("body",[o])[0],r||t.setData(s,i)}var u=Co(e.id,e.init);i.node.declarations.push(u),this.registerBinding(n,i.get("declarations").pop())},t.getProgramParent=function(){var e=this;do{if(e.path.isProgram())return e}while(e=e.parent);throw new Error("Couldn't find a Program")},t.getFunctionParent=function(){var e=this;do{if(e.path.isFunctionParent())return e}while(e=e.parent);return null},t.getBlockParent=function(){var e=this;do{if(e.path.isBlockParent())return e}while(e=e.parent);throw new Error("We couldn't find a BlockStatement, For, Switch, Function, Loop or Program...")},t.getAllBindings=function(){var e=Object.create(null),t=this;do{gh(e,t.bindings),t=t.parent}while(t);return e},t.getAllBindingsOfKind=function(){for(var e=Object.create(null),t=0,r=arguments;t<r.length;t++){var n=r[t],a=this;do{for(var s=0,i=Object.keys(a.bindings);s<i.length;s++){var o=i[s],u=a.bindings[o];u.kind===n&&(e[o]=u)}a=a.parent}while(a)}return e},t.bindingIdentifierEquals=function(e,t){return this.getBindingIdentifier(e)===t},t.getBinding=function(e){var t,r=this;do{var n=r.getOwnBinding(e);if(n&&!(t&&t.isPattern()&&t.parentPath.isFunction()&&"param"!==n.kind))return n;t=r.path}while(r=r.parent)},t.getOwnBinding=function(e){return this.bindings[e]},t.getBindingIdentifier=function(e){var t=this.getBinding(e);return t&&t.identifier},t.getOwnBindingIdentifier=function(e){var t=this.bindings[e];return t&&t.identifier},t.hasOwnBinding=function(e){return!!this.getOwnBinding(e)},t.hasBinding=function(t,r){return!!t&&(!!this.hasOwnBinding(t)||(!!this.parentHasBinding(t,r)||(!!this.hasUid(t)||(!(r||!zf(e.globals,t))||!(r||!zf(e.contextVariables,t))))))},t.parentHasBinding=function(e,t){return this.parent&&this.parent.hasBinding(e,t)},t.moveBindingTo=function(e,t){var r=this.getBinding(e);r&&(r.scope.removeOwnBinding(e),r.scope=t,t.bindings[e]=r)},t.removeOwnBinding=function(e){delete this.bindings[e]},t.removeBinding=function(e){var t=this.getBinding(e);t&&t.scope.removeOwnBinding(e);var r=this;do{r.uids[e]&&(r.uids[e]=!1)}while(r=r.parent)},n(e,[{key:"parent",get:function(){var e=this.path.findParent((function(e){return e.isScope()}));return e&&e.scope}},{key:"parentBlock",get:function(){return this.path.parent}},{key:"hub",get:function(){return this.path.hub}}]),e}();sm.globals=Object.keys($h.builtin),sm.contextVariables=["arguments","undefined","Infinity","NaN"];var im="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),om=function(e){if(0<=e&&e<im.length)return im[e];throw new TypeError("Must be between 0 and 63: "+e)},um=function(e){return 65<=e&&e<=90?e-65:97<=e&&e<=122?e-97+26:48<=e&&e<=57?e-48+52:43==e?62:47==e?63:-1};var cm=function(e){var t,r="",n=function(e){return e<0?1+(-e<<1):0+(e<<1)}(e);do{t=31&n,(n>>>=5)>0&&(t|=32),r+=om(t)}while(n>0);return r},lm=function(e,t,r){var n,a,s,i,o=e.length,u=0,c=0;do{if(t>=o)throw new Error("Expected more digits in base 64 VLQ value.");if(-1===(a=um(e.charCodeAt(t++))))throw new Error("Invalid base64 digit: "+e.charAt(t-1));n=!!(32&a),u+=(a&=31)<<c,c+=5}while(n);r.value=(i=(s=u)>>1,1==(1&s)?-i:i),r.rest=t},pm=Vt((function(e,t){t.getArg=function(e,t,r){if(t in e)return e[t];if(3===arguments.length)return r;throw new Error('"'+t+'" is a required argument.')};var r=/^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/,n=/^data:.+\,.+$/;function a(e){var t=e.match(r);return t?{scheme:t[1],auth:t[2],host:t[3],port:t[4],path:t[5]}:null}function s(e){var t="";return e.scheme&&(t+=e.scheme+":"),t+="//",e.auth&&(t+=e.auth+"@"),e.host&&(t+=e.host),e.port&&(t+=":"+e.port),e.path&&(t+=e.path),t}function i(e){var r=e,n=a(e);if(n){if(!n.path)return e;r=n.path}for(var i,o=t.isAbsolute(r),u=r.split(/\/+/),c=0,l=u.length-1;l>=0;l--)"."===(i=u[l])?u.splice(l,1):".."===i?c++:c>0&&(""===i?(u.splice(l+1,c),c=0):(u.splice(l,2),c--));return""===(r=u.join("/"))&&(r=o?"/":"."),n?(n.path=r,s(n)):r}function o(e,t){""===e&&(e="."),""===t&&(t=".");var r=a(t),o=a(e);if(o&&(e=o.path||"/"),r&&!r.scheme)return o&&(r.scheme=o.scheme),s(r);if(r||t.match(n))return t;if(o&&!o.host&&!o.path)return o.host=t,s(o);var u="/"===t.charAt(0)?t:i(e.replace(/\/+$/,"")+"/"+t);return o?(o.path=u,s(o)):u}t.urlParse=a,t.urlGenerate=s,t.normalize=i,t.join=o,t.isAbsolute=function(e){return"/"===e.charAt(0)||r.test(e)},t.relative=function(e,t){""===e&&(e="."),e=e.replace(/\/$/,"");for(var r=0;0!==t.indexOf(e+"/");){var n=e.lastIndexOf("/");if(n<0)return t;if((e=e.slice(0,n)).match(/^([^\/]+:\/)?\/*$/))return t;++r}return Array(r+1).join("../")+t.substr(e.length+1)};var u=!("__proto__"in Object.create(null));function c(e){return e}function l(e){if(!e)return!1;var t=e.length;if(t<9)return!1;if(95!==e.charCodeAt(t-1)||95!==e.charCodeAt(t-2)||111!==e.charCodeAt(t-3)||116!==e.charCodeAt(t-4)||111!==e.charCodeAt(t-5)||114!==e.charCodeAt(t-6)||112!==e.charCodeAt(t-7)||95!==e.charCodeAt(t-8)||95!==e.charCodeAt(t-9))return!1;for(var r=t-10;r>=0;r--)if(36!==e.charCodeAt(r))return!1;return!0}function p(e,t){return e===t?0:null===e?1:null===t?-1:e>t?1:-1}t.toSetString=u?c:function(e){return l(e)?"$"+e:e},t.fromSetString=u?c:function(e){return l(e)?e.slice(1):e},t.compareByOriginalPositions=function(e,t,r){var n=p(e.source,t.source);return 0!==n||0!==(n=e.originalLine-t.originalLine)||0!==(n=e.originalColumn-t.originalColumn)||r||0!==(n=e.generatedColumn-t.generatedColumn)||0!==(n=e.generatedLine-t.generatedLine)?n:p(e.name,t.name)},t.compareByGeneratedPositionsDeflated=function(e,t,r){var n=e.generatedLine-t.generatedLine;return 0!==n||0!==(n=e.generatedColumn-t.generatedColumn)||r||0!==(n=p(e.source,t.source))||0!==(n=e.originalLine-t.originalLine)||0!==(n=e.originalColumn-t.originalColumn)?n:p(e.name,t.name)},t.compareByGeneratedPositionsInflated=function(e,t){var r=e.generatedLine-t.generatedLine;return 0!==r||0!==(r=e.generatedColumn-t.generatedColumn)||0!==(r=p(e.source,t.source))||0!==(r=e.originalLine-t.originalLine)||0!==(r=e.originalColumn-t.originalColumn)?r:p(e.name,t.name)},t.parseSourceMapInput=function(e){return JSON.parse(e.replace(/^\)]}'[^\n]*\n/,""))},t.computeSourceURL=function(e,t,r){if(t=t||"",e&&("/"!==e[e.length-1]&&"/"!==t[0]&&(e+="/"),t=e+t),r){var n=a(r);if(!n)throw new Error("sourceMapURL could not be parsed");if(n.path){var u=n.path.lastIndexOf("/");u>=0&&(n.path=n.path.substring(0,u+1))}t=o(s(n),t)}return i(t)}})),dm=(pm.getArg,pm.urlParse,pm.urlGenerate,pm.normalize,pm.join,pm.isAbsolute,pm.relative,pm.toSetString,pm.fromSetString,pm.compareByOriginalPositions,pm.compareByGeneratedPositionsDeflated,pm.compareByGeneratedPositionsInflated,pm.parseSourceMapInput,pm.computeSourceURL,Object.prototype.hasOwnProperty),fm="undefined"!=typeof Map;function hm(){this._array=[],this._set=fm?new Map:Object.create(null)}hm.fromArray=function(e,t){for(var r=new hm,n=0,a=e.length;n<a;n++)r.add(e[n],t);return r},hm.prototype.size=function(){return fm?this._set.size:Object.getOwnPropertyNames(this._set).length},hm.prototype.add=function(e,t){var r=fm?e:pm.toSetString(e),n=fm?this.has(e):dm.call(this._set,r),a=this._array.length;n&&!t||this._array.push(e),n||(fm?this._set.set(e,a):this._set[r]=a)},hm.prototype.has=function(e){if(fm)return this._set.has(e);var t=pm.toSetString(e);return dm.call(this._set,t)},hm.prototype.indexOf=function(e){if(fm){var t=this._set.get(e);if(t>=0)return t}else{var r=pm.toSetString(e);if(dm.call(this._set,r))return this._set[r]}throw new Error('"'+e+'" is not in the set.')},hm.prototype.at=function(e){if(e>=0&&e<this._array.length)return this._array[e];throw new Error("No element indexed by "+e)},hm.prototype.toArray=function(){return this._array.slice()};var mm={ArraySet:hm};function ym(){this._array=[],this._sorted=!0,this._last={generatedLine:-1,generatedColumn:0}}ym.prototype.unsortedForEach=function(e,t){this._array.forEach(e,t)},ym.prototype.add=function(e){var t,r,n,a,s,i;t=this._last,r=e,n=t.generatedLine,a=r.generatedLine,s=t.generatedColumn,i=r.generatedColumn,a>n||a==n&&i>=s||pm.compareByGeneratedPositionsInflated(t,r)<=0?(this._last=e,this._array.push(e)):(this._sorted=!1,this._array.push(e))},ym.prototype.toArray=function(){return this._sorted||(this._array.sort(pm.compareByGeneratedPositionsInflated),this._sorted=!0),this._array};var gm=mm.ArraySet,vm={MappingList:ym}.MappingList;function bm(e){e||(e={}),this._file=pm.getArg(e,"file",null),this._sourceRoot=pm.getArg(e,"sourceRoot",null),this._skipValidation=pm.getArg(e,"skipValidation",!1),this._sources=new gm,this._names=new gm,this._mappings=new vm,this._sourcesContents=null}bm.prototype._version=3,bm.fromSourceMap=function(e){var t=e.sourceRoot,r=new bm({file:e.file,sourceRoot:t});return e.eachMapping((function(e){var n={generated:{line:e.generatedLine,column:e.generatedColumn}};null!=e.source&&(n.source=e.source,null!=t&&(n.source=pm.relative(t,n.source)),n.original={line:e.originalLine,column:e.originalColumn},null!=e.name&&(n.name=e.name)),r.addMapping(n)})),e.sources.forEach((function(n){var a=n;null!==t&&(a=pm.relative(t,n)),r._sources.has(a)||r._sources.add(a);var s=e.sourceContentFor(n);null!=s&&r.setSourceContent(n,s)})),r},bm.prototype.addMapping=function(e){var t=pm.getArg(e,"generated"),r=pm.getArg(e,"original",null),n=pm.getArg(e,"source",null),a=pm.getArg(e,"name",null);this._skipValidation||this._validateMapping(t,r,n,a),null!=n&&(n=String(n),this._sources.has(n)||this._sources.add(n)),null!=a&&(a=String(a),this._names.has(a)||this._names.add(a)),this._mappings.add({generatedLine:t.line,generatedColumn:t.column,originalLine:null!=r&&r.line,originalColumn:null!=r&&r.column,source:n,name:a})},bm.prototype.setSourceContent=function(e,t){var r=e;null!=this._sourceRoot&&(r=pm.relative(this._sourceRoot,r)),null!=t?(this._sourcesContents||(this._sourcesContents=Object.create(null)),this._sourcesContents[pm.toSetString(r)]=t):this._sourcesContents&&(delete this._sourcesContents[pm.toSetString(r)],0===Object.keys(this._sourcesContents).length&&(this._sourcesContents=null))},bm.prototype.applySourceMap=function(e,t,r){var n=t;if(null==t){if(null==e.file)throw new Error('SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, or the source map\'s "file" property. Both were omitted.');n=e.file}var a=this._sourceRoot;null!=a&&(n=pm.relative(a,n));var s=new gm,i=new gm;this._mappings.unsortedForEach((function(t){if(t.source===n&&null!=t.originalLine){var o=e.originalPositionFor({line:t.originalLine,column:t.originalColumn});null!=o.source&&(t.source=o.source,null!=r&&(t.source=pm.join(r,t.source)),null!=a&&(t.source=pm.relative(a,t.source)),t.originalLine=o.line,t.originalColumn=o.column,null!=o.name&&(t.name=o.name))}var u=t.source;null==u||s.has(u)||s.add(u);var c=t.name;null==c||i.has(c)||i.add(c)}),this),this._sources=s,this._names=i,e.sources.forEach((function(t){var n=e.sourceContentFor(t);null!=n&&(null!=r&&(t=pm.join(r,t)),null!=a&&(t=pm.relative(a,t)),this.setSourceContent(t,n))}),this)},bm.prototype._validateMapping=function(e,t,r,n){if(t&&"number"!=typeof t.line&&"number"!=typeof t.column)throw new Error("original.line and original.column are not numbers -- you probably meant to omit the original mapping entirely and only map the generated position. If so, pass null for the original mapping instead of an object with empty or null values.");if((!(e&&"line"in e&&"column"in e&&e.line>0&&e.column>=0)||t||r||n)&&!(e&&"line"in e&&"column"in e&&t&&"line"in t&&"column"in t&&e.line>0&&e.column>=0&&t.line>0&&t.column>=0&&r))throw new Error("Invalid mapping: "+JSON.stringify({generated:e,source:r,original:t,name:n}))},bm.prototype._serializeMappings=function(){for(var e,t,r,n,a=0,s=1,i=0,o=0,u=0,c=0,l="",p=this._mappings.toArray(),d=0,f=p.length;d<f;d++){if(e="",(t=p[d]).generatedLine!==s)for(a=0;t.generatedLine!==s;)e+=";",s++;else if(d>0){if(!pm.compareByGeneratedPositionsInflated(t,p[d-1]))continue;e+=","}e+=cm(t.generatedColumn-a),a=t.generatedColumn,null!=t.source&&(n=this._sources.indexOf(t.source),e+=cm(n-c),c=n,e+=cm(t.originalLine-1-o),o=t.originalLine-1,e+=cm(t.originalColumn-i),i=t.originalColumn,null!=t.name&&(r=this._names.indexOf(t.name),e+=cm(r-u),u=r)),l+=e}return l},bm.prototype._generateSourcesContent=function(e,t){return e.map((function(e){if(!this._sourcesContents)return null;null!=t&&(e=pm.relative(t,e));var r=pm.toSetString(e);return Object.prototype.hasOwnProperty.call(this._sourcesContents,r)?this._sourcesContents[r]:null}),this)},bm.prototype.toJSON=function(){var e={version:this._version,sources:this._sources.toArray(),names:this._names.toArray(),mappings:this._serializeMappings()};return null!=this._file&&(e.file=this._file),null!=this._sourceRoot&&(e.sourceRoot=this._sourceRoot),this._sourcesContents&&(e.sourcesContent=this._generateSourcesContent(e.sources,e.sourceRoot)),e},bm.prototype.toString=function(){return JSON.stringify(this.toJSON())};var xm={SourceMapGenerator:bm},Em=Vt((function(e,t){t.GREATEST_LOWER_BOUND=1,t.LEAST_UPPER_BOUND=2,t.search=function(e,r,n,a){if(0===r.length)return-1;var s=function e(r,n,a,s,i,o){var u=Math.floor((n-r)/2)+r,c=i(a,s[u],!0);return 0===c?u:c>0?n-u>1?e(u,n,a,s,i,o):o==t.LEAST_UPPER_BOUND?n<s.length?n:-1:u:u-r>1?e(r,u,a,s,i,o):o==t.LEAST_UPPER_BOUND?u:r<0?-1:r}(-1,r.length,e,r,n,a||t.GREATEST_LOWER_BOUND);if(s<0)return-1;for(;s-1>=0&&0===n(r[s],r[s-1],!0);)--s;return s}}));Em.GREATEST_LOWER_BOUND,Em.LEAST_UPPER_BOUND,Em.search;function Am(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function wm(e,t,r,n){if(r<n){var a=r-1;Am(e,(u=r,c=n,Math.round(u+Math.random()*(c-u))),n);for(var s=e[n],i=r;i<n;i++)t(e[i],s)<=0&&Am(e,a+=1,i);Am(e,a+1,i);var o=a+1;wm(e,t,r,o-1),wm(e,t,o+1,n)}var u,c}var Sm=mm.ArraySet,Dm=function(e,t){wm(e,t,0,e.length-1)};function Cm(e,t){var r=e;return"string"==typeof e&&(r=pm.parseSourceMapInput(e)),null!=r.sections?new Fm(r,t):new jm(r,t)}Cm.fromSourceMap=function(e,t){return jm.fromSourceMap(e,t)},Cm.prototype._version=3,Cm.prototype.__generatedMappings=null,Object.defineProperty(Cm.prototype,"_generatedMappings",{configurable:!0,enumerable:!0,get:function(){return this.__generatedMappings||this._parseMappings(this._mappings,this.sourceRoot),this.__generatedMappings}}),Cm.prototype.__originalMappings=null,Object.defineProperty(Cm.prototype,"_originalMappings",{configurable:!0,enumerable:!0,get:function(){return this.__originalMappings||this._parseMappings(this._mappings,this.sourceRoot),this.__originalMappings}}),Cm.prototype._charIsMappingSeparator=function(e,t){var r=e.charAt(t);return";"===r||","===r},Cm.prototype._parseMappings=function(e,t){throw new Error("Subclasses must implement _parseMappings")},Cm.GENERATED_ORDER=1,Cm.ORIGINAL_ORDER=2,Cm.GREATEST_LOWER_BOUND=1,Cm.LEAST_UPPER_BOUND=2,Cm.prototype.eachMapping=function(e,t,r){var n,a=t||null;switch(r||Cm.GENERATED_ORDER){case Cm.GENERATED_ORDER:n=this._generatedMappings;break;case Cm.ORIGINAL_ORDER:n=this._originalMappings;break;default:throw new Error("Unknown order of iteration.")}var s=this.sourceRoot;n.map((function(e){var t=null===e.source?null:this._sources.at(e.source);return{source:t=pm.computeSourceURL(s,t,this._sourceMapURL),generatedLine:e.generatedLine,generatedColumn:e.generatedColumn,originalLine:e.originalLine,originalColumn:e.originalColumn,name:null===e.name?null:this._names.at(e.name)}}),this).forEach(e,a)},Cm.prototype.allGeneratedPositionsFor=function(e){var t=pm.getArg(e,"line"),r={source:pm.getArg(e,"source"),originalLine:t,originalColumn:pm.getArg(e,"column",0)};if(r.source=this._findSourceIndex(r.source),r.source<0)return[];var n=[],a=this._findMapping(r,this._originalMappings,"originalLine","originalColumn",pm.compareByOriginalPositions,Em.LEAST_UPPER_BOUND);if(a>=0){var s=this._originalMappings[a];if(void 0===e.column)for(var i=s.originalLine;s&&s.originalLine===i;)n.push({line:pm.getArg(s,"generatedLine",null),column:pm.getArg(s,"generatedColumn",null),lastColumn:pm.getArg(s,"lastGeneratedColumn",null)}),s=this._originalMappings[++a];else for(var o=s.originalColumn;s&&s.originalLine===t&&s.originalColumn==o;)n.push({line:pm.getArg(s,"generatedLine",null),column:pm.getArg(s,"generatedColumn",null),lastColumn:pm.getArg(s,"lastGeneratedColumn",null)}),s=this._originalMappings[++a]}return n};var Tm=Cm;function jm(e,t){var r=e;"string"==typeof e&&(r=pm.parseSourceMapInput(e));var n=pm.getArg(r,"version"),a=pm.getArg(r,"sources"),s=pm.getArg(r,"names",[]),i=pm.getArg(r,"sourceRoot",null),o=pm.getArg(r,"sourcesContent",null),u=pm.getArg(r,"mappings"),c=pm.getArg(r,"file",null);if(n!=this._version)throw new Error("Unsupported version: "+n);i&&(i=pm.normalize(i)),a=a.map(String).map(pm.normalize).map((function(e){return i&&pm.isAbsolute(i)&&pm.isAbsolute(e)?pm.relative(i,e):e})),this._names=Sm.fromArray(s.map(String),!0),this._sources=Sm.fromArray(a,!0),this._absoluteSources=this._sources.toArray().map((function(e){return pm.computeSourceURL(i,e,t)})),this.sourceRoot=i,this.sourcesContent=o,this._mappings=u,this._sourceMapURL=t,this.file=c}function Pm(){this.generatedLine=0,this.generatedColumn=0,this.source=null,this.originalLine=null,this.originalColumn=null,this.name=null}jm.prototype=Object.create(Cm.prototype),jm.prototype.consumer=Cm,jm.prototype._findSourceIndex=function(e){var t,r=e;if(null!=this.sourceRoot&&(r=pm.relative(this.sourceRoot,r)),this._sources.has(r))return this._sources.indexOf(r);for(t=0;t<this._absoluteSources.length;++t)if(this._absoluteSources[t]==e)return t;return-1},jm.fromSourceMap=function(e,t){var r=Object.create(jm.prototype),n=r._names=Sm.fromArray(e._names.toArray(),!0),a=r._sources=Sm.fromArray(e._sources.toArray(),!0);r.sourceRoot=e._sourceRoot,r.sourcesContent=e._generateSourcesContent(r._sources.toArray(),r.sourceRoot),r.file=e._file,r._sourceMapURL=t,r._absoluteSources=r._sources.toArray().map((function(e){return pm.computeSourceURL(r.sourceRoot,e,t)}));for(var s=e._mappings.toArray().slice(),i=r.__generatedMappings=[],o=r.__originalMappings=[],u=0,c=s.length;u<c;u++){var l=s[u],p=new Pm;p.generatedLine=l.generatedLine,p.generatedColumn=l.generatedColumn,l.source&&(p.source=a.indexOf(l.source),p.originalLine=l.originalLine,p.originalColumn=l.originalColumn,l.name&&(p.name=n.indexOf(l.name)),o.push(p)),i.push(p)}return Dm(r.__originalMappings,pm.compareByOriginalPositions),r},jm.prototype._version=3,Object.defineProperty(jm.prototype,"sources",{get:function(){return this._absoluteSources.slice()}}),jm.prototype._parseMappings=function(e,t){for(var r,n,a,s,i,o=1,u=0,c=0,l=0,p=0,d=0,f=e.length,h=0,m={},y={},g=[],v=[];h<f;)if(";"===e.charAt(h))o++,h++,u=0;else if(","===e.charAt(h))h++;else{for((r=new Pm).generatedLine=o,s=h;s<f&&!this._charIsMappingSeparator(e,s);s++);if(a=m[n=e.slice(h,s)])h+=n.length;else{for(a=[];h<s;)lm(e,h,y),i=y.value,h=y.rest,a.push(i);if(2===a.length)throw new Error("Found a source, but no line and column");if(3===a.length)throw new Error("Found a source and line, but no column");m[n]=a}r.generatedColumn=u+a[0],u=r.generatedColumn,a.length>1&&(r.source=p+a[1],p+=a[1],r.originalLine=c+a[2],c=r.originalLine,r.originalLine+=1,r.originalColumn=l+a[3],l=r.originalColumn,a.length>4&&(r.name=d+a[4],d+=a[4])),v.push(r),"number"==typeof r.originalLine&&g.push(r)}Dm(v,pm.compareByGeneratedPositionsDeflated),this.__generatedMappings=v,Dm(g,pm.compareByOriginalPositions),this.__originalMappings=g},jm.prototype._findMapping=function(e,t,r,n,a,s){if(e[r]<=0)throw new TypeError("Line must be greater than or equal to 1, got "+e[r]);if(e[n]<0)throw new TypeError("Column must be greater than or equal to 0, got "+e[n]);return Em.search(e,t,a,s)},jm.prototype.computeColumnSpans=function(){for(var e=0;e<this._generatedMappings.length;++e){var t=this._generatedMappings[e];if(e+1<this._generatedMappings.length){var r=this._generatedMappings[e+1];if(t.generatedLine===r.generatedLine){t.lastGeneratedColumn=r.generatedColumn-1;continue}}t.lastGeneratedColumn=1/0}},jm.prototype.originalPositionFor=function(e){var t={generatedLine:pm.getArg(e,"line"),generatedColumn:pm.getArg(e,"column")},r=this._findMapping(t,this._generatedMappings,"generatedLine","generatedColumn",pm.compareByGeneratedPositionsDeflated,pm.getArg(e,"bias",Cm.GREATEST_LOWER_BOUND));if(r>=0){var n=this._generatedMappings[r];if(n.generatedLine===t.generatedLine){var a=pm.getArg(n,"source",null);null!==a&&(a=this._sources.at(a),a=pm.computeSourceURL(this.sourceRoot,a,this._sourceMapURL));var s=pm.getArg(n,"name",null);return null!==s&&(s=this._names.at(s)),{source:a,line:pm.getArg(n,"originalLine",null),column:pm.getArg(n,"originalColumn",null),name:s}}}return{source:null,line:null,column:null,name:null}},jm.prototype.hasContentsOfAllSources=function(){return!!this.sourcesContent&&(this.sourcesContent.length>=this._sources.size()&&!this.sourcesContent.some((function(e){return null==e})))},jm.prototype.sourceContentFor=function(e,t){if(!this.sourcesContent)return null;var r=this._findSourceIndex(e);if(r>=0)return this.sourcesContent[r];var n,a=e;if(null!=this.sourceRoot&&(a=pm.relative(this.sourceRoot,a)),null!=this.sourceRoot&&(n=pm.urlParse(this.sourceRoot))){var s=a.replace(/^file:\/\//,"");if("file"==n.scheme&&this._sources.has(s))return this.sourcesContent[this._sources.indexOf(s)];if((!n.path||"/"==n.path)&&this._sources.has("/"+a))return this.sourcesContent[this._sources.indexOf("/"+a)]}if(t)return null;throw new Error('"'+a+'" is not in the SourceMap.')},jm.prototype.generatedPositionFor=function(e){var t=pm.getArg(e,"source");if((t=this._findSourceIndex(t))<0)return{line:null,column:null,lastColumn:null};var r={source:t,originalLine:pm.getArg(e,"line"),originalColumn:pm.getArg(e,"column")},n=this._findMapping(r,this._originalMappings,"originalLine","originalColumn",pm.compareByOriginalPositions,pm.getArg(e,"bias",Cm.GREATEST_LOWER_BOUND));if(n>=0){var a=this._originalMappings[n];if(a.source===r.source)return{line:pm.getArg(a,"generatedLine",null),column:pm.getArg(a,"generatedColumn",null),lastColumn:pm.getArg(a,"lastGeneratedColumn",null)}}return{line:null,column:null,lastColumn:null}};var km=jm;function Fm(e,t){var r=e;"string"==typeof e&&(r=pm.parseSourceMapInput(e));var n=pm.getArg(r,"version"),a=pm.getArg(r,"sections");if(n!=this._version)throw new Error("Unsupported version: "+n);this._sources=new Sm,this._names=new Sm;var s={line:-1,column:0};this._sections=a.map((function(e){if(e.url)throw new Error("Support for url field in sections not implemented.");var r=pm.getArg(e,"offset"),n=pm.getArg(r,"line"),a=pm.getArg(r,"column");if(n<s.line||n===s.line&&a<s.column)throw new Error("Section offsets must be ordered and non-overlapping.");return s=r,{generatedOffset:{generatedLine:n+1,generatedColumn:a+1},consumer:new Cm(pm.getArg(e,"map"),t)}}))}Fm.prototype=Object.create(Cm.prototype),Fm.prototype.constructor=Cm,Fm.prototype._version=3,Object.defineProperty(Fm.prototype,"sources",{get:function(){for(var e=[],t=0;t<this._sections.length;t++)for(var r=0;r<this._sections[t].consumer.sources.length;r++)e.push(this._sections[t].consumer.sources[r]);return e}}),Fm.prototype.originalPositionFor=function(e){var t={generatedLine:pm.getArg(e,"line"),generatedColumn:pm.getArg(e,"column")},r=Em.search(t,this._sections,(function(e,t){var r=e.generatedLine-t.generatedOffset.generatedLine;return r||e.generatedColumn-t.generatedOffset.generatedColumn})),n=this._sections[r];return n?n.consumer.originalPositionFor({line:t.generatedLine-(n.generatedOffset.generatedLine-1),column:t.generatedColumn-(n.generatedOffset.generatedLine===t.generatedLine?n.generatedOffset.generatedColumn-1:0),bias:e.bias}):{source:null,line:null,column:null,name:null}},Fm.prototype.hasContentsOfAllSources=function(){return this._sections.every((function(e){return e.consumer.hasContentsOfAllSources()}))},Fm.prototype.sourceContentFor=function(e,t){for(var r=0;r<this._sections.length;r++){var n=this._sections[r].consumer.sourceContentFor(e,!0);if(n)return n}if(t)return null;throw new Error('"'+e+'" is not in the SourceMap.')},Fm.prototype.generatedPositionFor=function(e){for(var t=0;t<this._sections.length;t++){var r=this._sections[t];if(-1!==r.consumer._findSourceIndex(pm.getArg(e,"source"))){var n=r.consumer.generatedPositionFor(e);if(n)return{line:n.line+(r.generatedOffset.generatedLine-1),column:n.column+(r.generatedOffset.generatedLine===n.line?r.generatedOffset.generatedColumn-1:0)}}}return{line:null,column:null}},Fm.prototype._parseMappings=function(e,t){this.__generatedMappings=[],this.__originalMappings=[];for(var r=0;r<this._sections.length;r++)for(var n=this._sections[r],a=n.consumer._generatedMappings,s=0;s<a.length;s++){var i=a[s],o=n.consumer._sources.at(i.source);o=pm.computeSourceURL(n.consumer.sourceRoot,o,this._sourceMapURL),this._sources.add(o),o=this._sources.indexOf(o);var u=null;i.name&&(u=n.consumer._names.at(i.name),this._names.add(u),u=this._names.indexOf(u));var c={source:o,generatedLine:i.generatedLine+(n.generatedOffset.generatedLine-1),generatedColumn:i.generatedColumn+(n.generatedOffset.generatedLine===i.generatedLine?n.generatedOffset.generatedColumn-1:0),originalLine:i.originalLine,originalColumn:i.originalColumn,name:u};this.__generatedMappings.push(c),"number"==typeof c.originalLine&&this.__originalMappings.push(c)}Dm(this.__generatedMappings,pm.compareByGeneratedPositionsDeflated),Dm(this.__originalMappings,pm.compareByOriginalPositions)};var _m={SourceMapConsumer:Tm,BasicSourceMapConsumer:km,IndexedSourceMapConsumer:Fm},Im=xm.SourceMapGenerator,Bm=/(\r?\n)/,Om="$$$isSourceNode$$$";function Nm(e,t,r,n,a){this.children=[],this.sourceContents={},this.line=null==e?null:e,this.column=null==t?null:t,this.source=null==r?null:r,this.name=null==a?null:a,this[Om]=!0,null!=n&&this.add(n)}Nm.fromStringWithSourceMap=function(e,t,r){var n=new Nm,a=e.split(Bm),s=0,i=function(){return e()+(e()||"");function e(){return s<a.length?a[s++]:void 0}},o=1,u=0,c=null;return t.eachMapping((function(e){if(null!==c){if(!(o<e.generatedLine)){var t=(r=a[s]||"").substr(0,e.generatedColumn-u);return a[s]=r.substr(e.generatedColumn-u),u=e.generatedColumn,l(c,t),void(c=e)}l(c,i()),o++,u=0}for(;o<e.generatedLine;)n.add(i()),o++;if(u<e.generatedColumn){var r=a[s]||"";n.add(r.substr(0,e.generatedColumn)),a[s]=r.substr(e.generatedColumn),u=e.generatedColumn}c=e}),this),s<a.length&&(c&&l(c,i()),n.add(a.splice(s).join(""))),t.sources.forEach((function(e){var a=t.sourceContentFor(e);null!=a&&(null!=r&&(e=pm.join(r,e)),n.setSourceContent(e,a))})),n;function l(e,t){if(null===e||void 0===e.source)n.add(t);else{var a=r?pm.join(r,e.source):e.source;n.add(new Nm(e.originalLine,e.originalColumn,a,t,e.name))}}},Nm.prototype.add=function(e){if(Array.isArray(e))e.forEach((function(e){this.add(e)}),this);else{if(!e[Om]&&"string"!=typeof e)throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+e);e&&this.children.push(e)}return this},Nm.prototype.prepend=function(e){if(Array.isArray(e))for(var t=e.length-1;t>=0;t--)this.prepend(e[t]);else{if(!e[Om]&&"string"!=typeof e)throw new TypeError("Expected a SourceNode, string, or an array of SourceNodes and strings. Got "+e);this.children.unshift(e)}return this},Nm.prototype.walk=function(e){for(var t,r=0,n=this.children.length;r<n;r++)(t=this.children[r])[Om]?t.walk(e):""!==t&&e(t,{source:this.source,line:this.line,column:this.column,name:this.name})},Nm.prototype.join=function(e){var t,r,n=this.children.length;if(n>0){for(t=[],r=0;r<n-1;r++)t.push(this.children[r]),t.push(e);t.push(this.children[r]),this.children=t}return this},Nm.prototype.replaceRight=function(e,t){var r=this.children[this.children.length-1];return r[Om]?r.replaceRight(e,t):"string"==typeof r?this.children[this.children.length-1]=r.replace(e,t):this.children.push("".replace(e,t)),this},Nm.prototype.setSourceContent=function(e,t){this.sourceContents[pm.toSetString(e)]=t},Nm.prototype.walkSourceContents=function(e){for(var t=0,r=this.children.length;t<r;t++)this.children[t][Om]&&this.children[t].walkSourceContents(e);var n=Object.keys(this.sourceContents);for(t=0,r=n.length;t<r;t++)e(pm.fromSetString(n[t]),this.sourceContents[n[t]])},Nm.prototype.toString=function(){var e="";return this.walk((function(t){e+=t})),e},Nm.prototype.toStringWithSourceMap=function(e){var t={code:"",line:1,column:0},r=new Im(e),n=!1,a=null,s=null,i=null,o=null;return this.walk((function(e,u){t.code+=e,null!==u.source&&null!==u.line&&null!==u.column?(a===u.source&&s===u.line&&i===u.column&&o===u.name||r.addMapping({source:u.source,original:{line:u.line,column:u.column},generated:{line:t.line,column:t.column},name:u.name}),a=u.source,s=u.line,i=u.column,o=u.name,n=!0):n&&(r.addMapping({generated:{line:t.line,column:t.column}}),a=null,n=!1);for(var c=0,l=e.length;c<l;c++)10===e.charCodeAt(c)?(t.line++,t.column=0,c+1===l?(a=null,n=!1):n&&r.addMapping({source:u.source,original:{line:u.line,column:u.column},generated:{line:t.line,column:t.column},name:u.name})):t.column++})),this.walkSourceContents((function(e,t){r.setSourceContent(e,t)})),{code:t.code,map:r}};var Rm={SourceMapGenerator:xm.SourceMapGenerator,SourceMapConsumer:_m.SourceMapConsumer,SourceNode:{SourceNode:Nm}.SourceNode},Mm=function(){function e(e,t){this._cachedMap=null,this._code=t,this._opts=e,this._rawMappings=[]}var t=e.prototype;return t.get=function(){if(!this._cachedMap){var e=this._cachedMap=new Rm.SourceMapGenerator({sourceRoot:this._opts.sourceRoot}),t=this._code;"string"==typeof t?e.setSourceContent(this._opts.sourceFileName.replace(/\\/g,"/"),t):"object"==typeof t&&Object.keys(t).forEach((function(r){e.setSourceContent(r.replace(/\\/g,"/"),t[r])})),this._rawMappings.forEach((function(t){return e.addMapping(t)}),e)}return this._cachedMap.toJSON()},t.getRawMappings=function(){return this._rawMappings.slice()},t.mark=function(e,t,r,n,a,s,i){this._lastGenLine!==e&&null===r||(i||this._lastGenLine!==e||this._lastSourceLine!==r||this._lastSourceColumn!==n)&&(this._cachedMap=null,this._lastGenLine=e,this._lastSourceLine=r,this._lastSourceColumn=n,this._rawMappings.push({name:a||void 0,generated:{line:e,column:t},source:null==r?void 0:(s||this._opts.sourceFileName).replace(/\\/g,"/"),original:null==r?void 0:{line:r,column:n}}))},e}();var Lm=function(e){return"number"==typeof e&&e==Vf(e)},Um=/^[ \t]+$/,Gm=function(){function e(e){this._map=null,this._buf=[],this._last="",this._queue=[],this._position={line:1,column:0},this._sourcePosition={identifierName:null,line:null,column:null,filename:null},this._disallowedPop=null,this._map=e}var t=e.prototype;return t.get=function(){this._flush();var e=this._map,t={code:this._buf.join("").trimRight(),map:null,rawMappings:e&&e.getRawMappings()};return e&&Object.defineProperty(t,"map",{configurable:!0,enumerable:!0,get:function(){return this.map=e.get()},set:function(e){Object.defineProperty(this,"map",{value:e,writable:!0})}}),t},t.append=function(e){this._flush();var t=this._sourcePosition,r=t.line,n=t.column,a=t.filename,s=t.identifierName,i=t.force;this._append(e,r,n,s,a,i)},t.queue=function(e){if("\n"===e)for(;this._queue.length>0&&Um.test(this._queue[0][0]);)this._queue.shift();var t=this._sourcePosition,r=t.line,n=t.column,a=t.filename,s=t.identifierName,i=t.force;this._queue.unshift([e,r,n,s,a,i])},t._flush=function(){for(var e;e=this._queue.pop();)this._append.apply(this,e)},t._append=function(e,t,r,n,a,s){this._map&&"\n"!==e[0]&&this._map.mark(this._position.line,this._position.column,t,r,n,a,s),this._buf.push(e),this._last=e[e.length-1];for(var i=0;i<e.length;i++)"\n"===e[i]?(this._position.line++,this._position.column=0):this._position.column++},t.removeTrailingNewline=function(){this._queue.length>0&&"\n"===this._queue[0][0]&&this._queue.shift()},t.removeLastSemicolon=function(){this._queue.length>0&&";"===this._queue[0][0]&&this._queue.shift()},t.endsWith=function(e){if(1===e.length){var t;if(this._queue.length>0){var r=this._queue[0][0];t=r[r.length-1]}else t=this._last;return t===e}var n=this._last+this._queue.reduce((function(e,t){return t[0]+e}),"");return e.length<=n.length&&n.slice(-e.length)===e},t.hasContent=function(){return this._queue.length>0||!!this._last},t.exactSource=function(e,t){this.source("start",e,!0),t(),this.source("end",e),this._disallowPop("start",e)},t.source=function(e,t,r){e&&!t||this._normalizePosition(e,t,this._sourcePosition,r)},t.withSource=function(e,t,r){if(!this._map)return r();var n=this._sourcePosition.line,a=this._sourcePosition.column,s=this._sourcePosition.filename,i=this._sourcePosition.identifierName;this.source(e,t),r(),this._sourcePosition.force&&this._sourcePosition.line===n&&this._sourcePosition.column===a&&this._sourcePosition.filename===s||this._disallowedPop&&this._disallowedPop.line===n&&this._disallowedPop.column===a&&this._disallowedPop.filename===s||(this._sourcePosition.line=n,this._sourcePosition.column=a,this._sourcePosition.filename=s,this._sourcePosition.identifierName=i,this._sourcePosition.force=!1,this._disallowedPop=null)},t._disallowPop=function(e,t){e&&!t||(this._disallowedPop=this._normalizePosition(e,t))},t._normalizePosition=function(e,t,r,n){var a=t?t[e]:null;void 0===r&&(r={identifierName:null,line:null,column:null,filename:null,force:!1});var s=r.line,i=r.column,o=r.filename;return r.identifierName="start"===e&&t&&t.identifierName||null,r.line=a?a.line:null,r.column=a?a.column:null,r.filename=t&&t.filename||null,(n||r.line!==s||r.column!==i||r.filename!==o)&&(r.force=n),r},t.getCurrentColumn=function(){var e=this._queue.reduce((function(e,t){return t[0]+e}),""),t=e.lastIndexOf("\n");return-1===t?this._position.column+e.length:e.length-1-t},t.getCurrentLine=function(){for(var e=this._queue.reduce((function(e,t){return t[0]+e}),""),t=0,r=0;r<e.length;r++)"\n"===e[r]&&t++;return this._position.line+t},e}();function Vm(e,t){return void 0===t&&(t={}),_(e)?(Vm(e.object,t),e.computed&&Vm(e.property,t)):nt(e)||c(e)?(Vm(e.left,t),Vm(e.right,t)):f(e)?(t.hasCall=!0,Vm(e.callee,t)):lt(e)?t.hasFunction=!0:S(e)&&(t.hasHelper=t.hasHelper||Wm(e.callee)),t}function Wm(e){return _(e)?Wm(e.object)||Wm(e.property):S(e)?"require"===e.name||"_"===e.name[0]:f(e)?Wm(e.callee):!(!nt(e)&&!c(e))&&(S(e.left)&&Wm(e.left)||Wm(e.right))}function Hm(e){return ft(e)||O(e)||u(e)||S(e)||_(e)}var qm={AssignmentExpression:function(e){var t=Vm(e.right);if(t.hasCall&&t.hasHelper||t.hasFunction)return{before:t.hasFunction,after:!0}},SwitchCase:function(e,t){return{before:e.consequent.length||t.cases[0]===e,after:!e.consequent.length&&t.cases[t.cases.length-1]===e}},LogicalExpression:function(e){if(lt(e.left)||lt(e.right))return{after:!0}},Literal:function(e){if("use strict"===e.value)return{after:!0}},CallExpression:function(e){if(lt(e.callee)||Wm(e))return{before:!0,after:!0}},VariableDeclaration:function(e){for(var t=0;t<e.declarations.length;t++){var r=e.declarations[t],n=Wm(r.id)&&!Hm(r.init);if(!n){var a=Vm(r.init);n=Wm(r.init)&&a.hasCall||a.hasFunction}if(n)return{before:!0,after:!0}}},IfStatement:function(e){if(p(e.consequent))return{before:!0,after:!0}}};qm.ObjectProperty=qm.ObjectTypeProperty=qm.ObjectMethod=function(e,t){if(t.properties[0]===e)return{before:!0}},qm.ObjectTypeCallProperty=function(e,t){if(!(t.callProperties[0]!==e||t.properties&&t.properties.length))return{before:!0}},qm.ObjectTypeIndexer=function(e,t){if(!(t.indexers[0]!==e||t.properties&&t.properties.length||t.callProperties&&t.callProperties.length))return{before:!0}},qm.ObjectTypeInternalSlot=function(e,t){if(!(t.internalSlots[0]!==e||t.properties&&t.properties.length||t.callProperties&&t.callProperties.length||t.indexers&&t.indexers.length))return{before:!0}};[["Function",!0],["Class",!0],["Loop",!0],["LabeledStatement",!0],["SwitchStatement",!0],["TryStatement",!0]].forEach((function(e){var t=e[0],r=e[1];"boolean"==typeof r&&(r={after:r,before:r}),[t].concat(_s[t]||[]).forEach((function(e){qm[e]=function(){return r}}))}));var Km={"||":0,"??":0,"&&":1,"|":2,"^":3,"&":4,"==":5,"===":5,"!=":5,"!==":5,"<":6,">":6,"<=":6,">=":6,in:6,instanceof:6,">>":7,"<<":7,">>>":7,"+":8,"-":8,"*":9,"/":9,"%":9,"**":10},zm=function(e,t){return(Z(t)||Q(t))&&t.superClass===e};function Xm(e,t){return ye(t)||we(t)||xe(t)||je(t)}function Ym(e,t){return Ye(t)||Je(t)||Ze(t)||Qe(t)||$e(t)}function Jm(e,t){return nt(t)||mt(t)||f(t)||_(t)||I(t)||Ue(t)&&he(e)||m(t)&&e===t.test||zm(e,t)}function $m(e,t){return _(t,{object:e})||f(t,{callee:e})||I(t,{callee:e})||l(t,{operator:"**",left:e})||zm(e,t)}function Qm(e,t){return!!(mt(t)||nt(t)||m(t,{test:e})||Ue(t)||We(t)||de(t)||tt(t)||et(t))||$m(e,t)}function Zm(e,t){for(var r=void 0===t?{}:t,n=r.considerArrow,a=void 0!==n&&n,s=r.considerDefaultExports,i=void 0!==s&&s,o=e.length-1,u=e[o],l=e[--o];o>0;){if(v(l,{expression:u})||de(l)||i&&te(l,{declaration:u})||a&&J(l,{body:u}))return!0;if(!(f(l,{callee:u})||U(l)&&l.expressions[0]===u||_(l,{object:u})||it(l,{test:u})||nt(l,{left:u})||c(l,{left:u})))return!1;u=l,l=e[--o]}return!1}function ey(e){var t={};function r(e,r){var n=t[e];t[e]=n?function(e,t,a){var s=n(e,t,a);return null==s?r(e,t,a):s}:r}for(var n=0,a=Object.keys(e);n<a.length;n++){var s=a[n],i=_s[s];if(i){var o=i,u=Array.isArray(o),c=0;for(o=u?o:o[Symbol.iterator]();;){var l;if(u){if(c>=o.length)break;l=o[c++]}else{if((c=o.next()).done)break;l=c.value}r(l,e[s])}}else r(s,e[s])}return t}var ty=ey(Object.freeze({__proto__:null,NullableTypeAnnotation:function(e,t){return ye(t)},FunctionTypeAnnotation:function(e,t,r){return je(t)||xe(t)||ye(t)||Ce(t)&&J(r[r.length-3])},UpdateExpression:function(e,t){return _(t,{object:e})||f(t,{callee:e})||I(t,{callee:e})||zm(e,t)},ObjectExpression:function(e,t,r){return Zm(r,{considerArrow:!0})},DoExpression:function(e,t,r){return Zm(r)},Binary:function(e,t){if("**"===e.operator&&l(t,{operator:"**"}))return t.left===e;if(zm(e,t))return!0;if((f(t)||I(t))&&t.callee===e||mt(t)||_(t)&&t.object===e||Ue(t))return!0;if(nt(t)){var r=t.operator,n=Km[r],a=e.operator,s=Km[a];if(n===s&&t.right===e&&!F(t)||n>s)return!0}return!1},UnionTypeAnnotation:Xm,IntersectionTypeAnnotation:Xm,TSAsExpression:function(){return!0},TSTypeAssertion:function(){return!0},TSUnionType:Ym,TSIntersectionType:Ym,TSInferType:function(e,t){return Ye(t)||Je(t)},BinaryExpression:function(e,t){return"in"===e.operator&&(K(t)||ut(t))},SequenceExpression:function(e,t){return!(E(t)||W(t)||L(t)||D(t)&&t.test===e||z(t)&&t.test===e||x(t)&&t.right===e||G(t)&&t.discriminant===e||v(t)&&t.expression===e)},YieldExpression:Jm,AwaitExpression:Jm,ClassExpression:function(e,t,r){return Zm(r,{considerDefaultExports:!0})},UnaryLike:$m,FunctionExpression:function(e,t,r){return Zm(r,{considerDefaultExports:!0})},ArrowFunctionExpression:function(e,t){return bt(t)||Qm(e,t)},ConditionalExpression:Qm,OptionalMemberExpression:function(e,t){return f(t,{callee:e})||_(t,{object:e})},OptionalCallExpression:function(e,t){return f(t,{callee:e})||_(t,{object:e})},AssignmentExpression:function(e){return!!ce(e.left)||Qm.apply(void 0,arguments)},NewExpression:function(e,t){return zm(e,t)}})),ry=ey(qm),ny=ey({VariableDeclaration:function(e){return e.declarations.map((function(e){return e.init}))},ArrayExpression:function(e){return e.elements},ObjectExpression:function(e){return e.properties}});function ay(e,t,r,n){var a=e[t.type];return a?a(t,r,n):null}function sy(e,t,r){if(!e)return 0;v(e)&&(e=e.expression);var n=ay(ry,e,t);if(!n){var a=ay(ny,e,t);if(a)for(var s=0;s<a.length&&!(n=sy(a[s],e,r));s++);}return"object"==typeof n&&null!==n&&n[r]||0}function iy(e,t){return sy(e,t,"before")}function oy(e,t){return sy(e,t,"after")}function uy(e,t,r){return!!t&&(!(!I(t)||t.callee!==e||!function e(t){return!!f(t)||!!_(t)&&(e(t.object)||!t.computed&&e(t.property))}(e))||(!(!F(e)||"??"!==t.operator)||ay(ty,e,t,r)))}function cy(e){return function(t){if(this.word(e),t.delegate&&this.token("*"),t.argument){this.space();var r=this.startTerminatorless();this.print(t.argument,t),this.endTerminatorless(r)}}}var ly=cy("yield"),py=cy("await");function dy(e,t){var r=this.inForStatementInitCounter&&"in"===e.operator&&!uy(e,t);r&&this.token("("),this.print(e.left,e),this.space(),"in"===e.operator||"instanceof"===e.operator?this.word(e.operator):this.token(e.operator),this.space(),this.print(e.right,e),r&&this.token(")")}var fy=function(e){return function(t){this.word("for"),this.space(),"of"===e&&t.await&&(this.word("await"),this.space()),this.token("("),this.print(t.left,t),this.space(),this.word(e),this.space(),this.print(t.right,t),this.token(")"),this.printBlock(t)}},hy=fy("in"),my=fy("of");function yy(e,t){return void 0===t&&(t="label"),function(r){this.word(e);var n=r[t];if(n){this.space();var a="label"==t,s=this.startTerminatorless(a);this.print(n,r),this.endTerminatorless(s)}this.semicolon()}}var gy=yy("continue"),vy=yy("return","argument"),by=yy("break"),xy=yy("throw","argument");function Ey(){if(this.token(","),this.newline(),this.endsWith("\n"))for(var e=0;e<4;e++)this.space(!0)}function Ay(){if(this.token(","),this.newline(),this.endsWith("\n"))for(var e=0;e<6;e++)this.space(!0)}function wy(e,t){this.format.decoratorsBeforeExport&&(te(t)||re(t))||this.printJoin(e.decorators,e),e.declare&&(this.word("declare"),this.space()),e.abstract&&(this.word("abstract"),this.space()),this.word("class"),e.id&&(this.space(),this.print(e.id,e)),this.print(e.typeParameters,e),e.superClass&&(this.space(),this.word("extends"),this.space(),this.print(e.superClass,e),this.print(e.superTypeParameters,e)),e.implements&&(this.space(),this.word("implements"),this.space(),this.printList(e.implements,e)),this.space(),this.print(e.body,e)}function Sy(e){this._functionHead(e),this.space(),this.print(e.body,e)}function Dy(e){this.word("export"),this.space(),"type"===e.exportKind&&(this.word("type"),this.space()),this.token("*"),this.space(),this.word("from"),this.space(),this.print(e.source,e),this.semicolon()}function Cy(e){if(e.declaration){var t=e.declaration;this.print(t,e),st(t)||this.semicolon()}else{"type"===e.exportKind&&(this.word("type"),this.space());for(var r=e.specifiers.slice(0),n=!1;;){var a=r[0];if(!Ke(a)&&!ze(a))break;n=!0,this.print(r.shift(),e),r.length&&(this.token(","),this.space())}(r.length||!r.length&&!n)&&(this.token("{"),r.length&&(this.space(),this.printList(r,e),this.space()),this.token("}")),e.source&&(this.space(),this.word("from"),this.space(),this.print(e.source,e)),this.semicolon()}}var Ty=[],jy=[],Py="undefined"!=typeof Uint8Array?Uint8Array:Array,ky=!1;function Fy(){ky=!0;for(var e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",t=0,r=e.length;t<r;++t)Ty[t]=e[t],jy[e.charCodeAt(t)]=t;jy["-".charCodeAt(0)]=62,jy["_".charCodeAt(0)]=63}function _y(e,t,r){for(var n,a,s=[],i=t;i<r;i+=3)n=(e[i]<<16)+(e[i+1]<<8)+e[i+2],s.push(Ty[(a=n)>>18&63]+Ty[a>>12&63]+Ty[a>>6&63]+Ty[63&a]);return s.join("")}function Iy(e){var t;ky||Fy();for(var r=e.length,n=r%3,a="",s=[],i=0,o=r-n;i<o;i+=16383)s.push(_y(e,i,i+16383>o?o:i+16383));return 1===n?(t=e[r-1],a+=Ty[t>>2],a+=Ty[t<<4&63],a+="=="):2===n&&(t=(e[r-2]<<8)+e[r-1],a+=Ty[t>>10],a+=Ty[t>>4&63],a+=Ty[t<<2&63],a+="="),s.push(a),s.join("")}function By(e,t,r,n,a){var s,i,o=8*a-n-1,u=(1<<o)-1,c=u>>1,l=-7,p=r?a-1:0,d=r?-1:1,f=e[t+p];for(p+=d,s=f&(1<<-l)-1,f>>=-l,l+=o;l>0;s=256*s+e[t+p],p+=d,l-=8);for(i=s&(1<<-l)-1,s>>=-l,l+=n;l>0;i=256*i+e[t+p],p+=d,l-=8);if(0===s)s=1-c;else{if(s===u)return i?NaN:1/0*(f?-1:1);i+=Math.pow(2,n),s-=c}return(f?-1:1)*i*Math.pow(2,s-n)}function Oy(e,t,r,n,a,s){var i,o,u,c=8*s-a-1,l=(1<<c)-1,p=l>>1,d=23===a?Math.pow(2,-24)-Math.pow(2,-77):0,f=n?0:s-1,h=n?1:-1,m=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(o=isNaN(t)?1:0,i=l):(i=Math.floor(Math.log(t)/Math.LN2),t*(u=Math.pow(2,-i))<1&&(i--,u*=2),(t+=i+p>=1?d/u:d*Math.pow(2,1-p))*u>=2&&(i++,u/=2),i+p>=l?(o=0,i=l):i+p>=1?(o=(t*u-1)*Math.pow(2,a),i+=p):(o=t*Math.pow(2,p-1)*Math.pow(2,a),i=0));a>=8;e[r+f]=255&o,f+=h,o/=256,a-=8);for(i=i<<a|o,c+=a;c>0;e[r+f]=255&i,f+=h,i/=256,c-=8);e[r+f-h]|=128*m}var Ny={}.toString,Ry=Array.isArray||function(e){return"[object Array]"==Ny.call(e)};Gy.TYPED_ARRAY_SUPPORT=void 0===Pa.TYPED_ARRAY_SUPPORT||Pa.TYPED_ARRAY_SUPPORT;var My=Ly();function Ly(){return Gy.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function Uy(e,t){if(Ly()<t)throw new RangeError("Invalid typed array length");return Gy.TYPED_ARRAY_SUPPORT?(e=new Uint8Array(t)).__proto__=Gy.prototype:(null===e&&(e=new Gy(t)),e.length=t),e}function Gy(e,t,r){if(!(Gy.TYPED_ARRAY_SUPPORT||this instanceof Gy))return new Gy(e,t,r);if("number"==typeof e){if("string"==typeof t)throw new Error("If encoding is specified then the first argument must be a string");return Hy(this,e)}return Vy(this,e,t,r)}function Vy(e,t,r,n){if("number"==typeof t)throw new TypeError('"value" argument must not be a number');return"undefined"!=typeof ArrayBuffer&&t instanceof ArrayBuffer?function(e,t,r,n){if(t.byteLength,r<0||t.byteLength<r)throw new RangeError("'offset' is out of bounds");if(t.byteLength<r+(n||0))throw new RangeError("'length' is out of bounds");t=void 0===r&&void 0===n?new Uint8Array(t):void 0===n?new Uint8Array(t,r):new Uint8Array(t,r,n);Gy.TYPED_ARRAY_SUPPORT?(e=t).__proto__=Gy.prototype:e=qy(e,t);return e}(e,t,r,n):"string"==typeof t?function(e,t,r){"string"==typeof r&&""!==r||(r="utf8");if(!Gy.isEncoding(r))throw new TypeError('"encoding" must be a valid string encoding');var n=0|Xy(t,r),a=(e=Uy(e,n)).write(t,r);a!==n&&(e=e.slice(0,a));return e}(e,t,r):function(e,t){if(zy(t)){var r=0|Ky(t.length);return 0===(e=Uy(e,r)).length||t.copy(e,0,0,r),e}if(t){if("undefined"!=typeof ArrayBuffer&&t.buffer instanceof ArrayBuffer||"length"in t)return"number"!=typeof t.length||(n=t.length)!=n?Uy(e,0):qy(e,t);if("Buffer"===t.type&&Ry(t.data))return qy(e,t.data)}var n;throw new TypeError("First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.")}(e,t)}function Wy(e){if("number"!=typeof e)throw new TypeError('"size" argument must be a number');if(e<0)throw new RangeError('"size" argument must not be negative')}function Hy(e,t){if(Wy(t),e=Uy(e,t<0?0:0|Ky(t)),!Gy.TYPED_ARRAY_SUPPORT)for(var r=0;r<t;++r)e[r]=0;return e}function qy(e,t){var r=t.length<0?0:0|Ky(t.length);e=Uy(e,r);for(var n=0;n<r;n+=1)e[n]=255&t[n];return e}function Ky(e){if(e>=Ly())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+Ly().toString(16)+" bytes");return 0|e}function zy(e){return!(null==e||!e._isBuffer)}function Xy(e,t){if(zy(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var r=e.length;if(0===r)return 0;for(var n=!1;;)switch(t){case"ascii":case"latin1":case"binary":return r;case"utf8":case"utf-8":case void 0:return xg(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return Eg(e).length;default:if(n)return xg(e).length;t=(""+t).toLowerCase(),n=!0}}function Yy(e,t,r){var n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return cg(this,t,r);case"utf8":case"utf-8":return ig(this,t,r);case"ascii":return og(this,t,r);case"latin1":case"binary":return ug(this,t,r);case"base64":return sg(this,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return lg(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function Jy(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function $y(e,t,r,n,a){if(0===e.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),r=+r,isNaN(r)&&(r=a?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(a)return-1;r=e.length-1}else if(r<0){if(!a)return-1;r=0}if("string"==typeof t&&(t=Gy.from(t,n)),zy(t))return 0===t.length?-1:Qy(e,t,r,n,a);if("number"==typeof t)return t&=255,Gy.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?a?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):Qy(e,[t],r,n,a);throw new TypeError("val must be string, number or Buffer")}function Qy(e,t,r,n,a){var s,i=1,o=e.length,u=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;i=2,o/=2,u/=2,r/=2}function c(e,t){return 1===i?e[t]:e.readUInt16BE(t*i)}if(a){var l=-1;for(s=r;s<o;s++)if(c(e,s)===c(t,-1===l?0:s-l)){if(-1===l&&(l=s),s-l+1===u)return l*i}else-1!==l&&(s-=s-l),l=-1}else for(r+u>o&&(r=o-u),s=r;s>=0;s--){for(var p=!0,d=0;d<u;d++)if(c(e,s+d)!==c(t,d)){p=!1;break}if(p)return s}return-1}function Zy(e,t,r,n){r=Number(r)||0;var a=e.length-r;n?(n=Number(n))>a&&(n=a):n=a;var s=t.length;if(s%2!=0)throw new TypeError("Invalid hex string");n>s/2&&(n=s/2);for(var i=0;i<n;++i){var o=parseInt(t.substr(2*i,2),16);if(isNaN(o))return i;e[r+i]=o}return i}function eg(e,t,r,n){return Ag(xg(t,e.length-r),e,r,n)}function tg(e,t,r,n){return Ag(function(e){for(var t=[],r=0;r<e.length;++r)t.push(255&e.charCodeAt(r));return t}(t),e,r,n)}function rg(e,t,r,n){return tg(e,t,r,n)}function ng(e,t,r,n){return Ag(Eg(t),e,r,n)}function ag(e,t,r,n){return Ag(function(e,t){for(var r,n,a,s=[],i=0;i<e.length&&!((t-=2)<0);++i)r=e.charCodeAt(i),n=r>>8,a=r%256,s.push(a),s.push(n);return s}(t,e.length-r),e,r,n)}function sg(e,t,r){return 0===t&&r===e.length?Iy(e):Iy(e.slice(t,r))}function ig(e,t,r){r=Math.min(e.length,r);for(var n=[],a=t;a<r;){var s,i,o,u,c=e[a],l=null,p=c>239?4:c>223?3:c>191?2:1;if(a+p<=r)switch(p){case 1:c<128&&(l=c);break;case 2:128==(192&(s=e[a+1]))&&(u=(31&c)<<6|63&s)>127&&(l=u);break;case 3:s=e[a+1],i=e[a+2],128==(192&s)&&128==(192&i)&&(u=(15&c)<<12|(63&s)<<6|63&i)>2047&&(u<55296||u>57343)&&(l=u);break;case 4:s=e[a+1],i=e[a+2],o=e[a+3],128==(192&s)&&128==(192&i)&&128==(192&o)&&(u=(15&c)<<18|(63&s)<<12|(63&i)<<6|63&o)>65535&&u<1114112&&(l=u)}null===l?(l=65533,p=1):l>65535&&(l-=65536,n.push(l>>>10&1023|55296),l=56320|1023&l),n.push(l),a+=p}return function(e){var t=e.length;if(t<=4096)return String.fromCharCode.apply(String,e);var r="",n=0;for(;n<t;)r+=String.fromCharCode.apply(String,e.slice(n,n+=4096));return r}(n)}Gy.poolSize=8192,Gy._augment=function(e){return e.__proto__=Gy.prototype,e},Gy.from=function(e,t,r){return Vy(null,e,t,r)},Gy.TYPED_ARRAY_SUPPORT&&(Gy.prototype.__proto__=Uint8Array.prototype,Gy.__proto__=Uint8Array),Gy.alloc=function(e,t,r){return function(e,t,r,n){return Wy(t),t<=0?Uy(e,t):void 0!==r?"string"==typeof n?Uy(e,t).fill(r,n):Uy(e,t).fill(r):Uy(e,t)}(null,e,t,r)},Gy.allocUnsafe=function(e){return Hy(null,e)},Gy.allocUnsafeSlow=function(e){return Hy(null,e)},Gy.isBuffer=wg,Gy.compare=function(e,t){if(!zy(e)||!zy(t))throw new TypeError("Arguments must be Buffers");if(e===t)return 0;for(var r=e.length,n=t.length,a=0,s=Math.min(r,n);a<s;++a)if(e[a]!==t[a]){r=e[a],n=t[a];break}return r<n?-1:n<r?1:0},Gy.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"latin1":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return!0;default:return!1}},Gy.concat=function(e,t){if(!Ry(e))throw new TypeError('"list" argument must be an Array of Buffers');if(0===e.length)return Gy.alloc(0);var r;if(void 0===t)for(t=0,r=0;r<e.length;++r)t+=e[r].length;var n=Gy.allocUnsafe(t),a=0;for(r=0;r<e.length;++r){var s=e[r];if(!zy(s))throw new TypeError('"list" argument must be an Array of Buffers');s.copy(n,a),a+=s.length}return n},Gy.byteLength=Xy,Gy.prototype._isBuffer=!0,Gy.prototype.swap16=function(){var e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var t=0;t<e;t+=2)Jy(this,t,t+1);return this},Gy.prototype.swap32=function(){var e=this.length;if(e%4!=0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(var t=0;t<e;t+=4)Jy(this,t,t+3),Jy(this,t+1,t+2);return this},Gy.prototype.swap64=function(){var e=this.length;if(e%8!=0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(var t=0;t<e;t+=8)Jy(this,t,t+7),Jy(this,t+1,t+6),Jy(this,t+2,t+5),Jy(this,t+3,t+4);return this},Gy.prototype.toString=function(){var e=0|this.length;return 0===e?"":0===arguments.length?ig(this,0,e):Yy.apply(this,arguments)},Gy.prototype.equals=function(e){if(!zy(e))throw new TypeError("Argument must be a Buffer");return this===e||0===Gy.compare(this,e)},Gy.prototype.inspect=function(){var e="";return this.length>0&&(e=this.toString("hex",0,50).match(/.{2}/g).join(" "),this.length>50&&(e+=" ... ")),"<Buffer "+e+">"},Gy.prototype.compare=function(e,t,r,n,a){if(!zy(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===a&&(a=this.length),t<0||r>e.length||n<0||a>this.length)throw new RangeError("out of range index");if(n>=a&&t>=r)return 0;if(n>=a)return-1;if(t>=r)return 1;if(this===e)return 0;for(var s=(a>>>=0)-(n>>>=0),i=(r>>>=0)-(t>>>=0),o=Math.min(s,i),u=this.slice(n,a),c=e.slice(t,r),l=0;l<o;++l)if(u[l]!==c[l]){s=u[l],i=c[l];break}return s<i?-1:i<s?1:0},Gy.prototype.includes=function(e,t,r){return-1!==this.indexOf(e,t,r)},Gy.prototype.indexOf=function(e,t,r){return $y(this,e,t,r,!0)},Gy.prototype.lastIndexOf=function(e,t,r){return $y(this,e,t,r,!1)},Gy.prototype.write=function(e,t,r,n){if(void 0===t)n="utf8",r=this.length,t=0;else if(void 0===r&&"string"==typeof t)n=t,r=this.length,t=0;else{if(!isFinite(t))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");t|=0,isFinite(r)?(r|=0,void 0===n&&(n="utf8")):(n=r,r=void 0)}var a=this.length-t;if((void 0===r||r>a)&&(r=a),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var s=!1;;)switch(n){case"hex":return Zy(this,e,t,r);case"utf8":case"utf-8":return eg(this,e,t,r);case"ascii":return tg(this,e,t,r);case"latin1":case"binary":return rg(this,e,t,r);case"base64":return ng(this,e,t,r);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return ag(this,e,t,r);default:if(s)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),s=!0}},Gy.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function og(e,t,r){var n="";r=Math.min(e.length,r);for(var a=t;a<r;++a)n+=String.fromCharCode(127&e[a]);return n}function ug(e,t,r){var n="";r=Math.min(e.length,r);for(var a=t;a<r;++a)n+=String.fromCharCode(e[a]);return n}function cg(e,t,r){var n=e.length;(!t||t<0)&&(t=0),(!r||r<0||r>n)&&(r=n);for(var a="",s=t;s<r;++s)a+=bg(e[s]);return a}function lg(e,t,r){for(var n=e.slice(t,r),a="",s=0;s<n.length;s+=2)a+=String.fromCharCode(n[s]+256*n[s+1]);return a}function pg(e,t,r){if(e%1!=0||e<0)throw new RangeError("offset is not uint");if(e+t>r)throw new RangeError("Trying to access beyond buffer length")}function dg(e,t,r,n,a,s){if(!zy(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>a||t<s)throw new RangeError('"value" argument is out of bounds');if(r+n>e.length)throw new RangeError("Index out of range")}function fg(e,t,r,n){t<0&&(t=65535+t+1);for(var a=0,s=Math.min(e.length-r,2);a<s;++a)e[r+a]=(t&255<<8*(n?a:1-a))>>>8*(n?a:1-a)}function hg(e,t,r,n){t<0&&(t=4294967295+t+1);for(var a=0,s=Math.min(e.length-r,4);a<s;++a)e[r+a]=t>>>8*(n?a:3-a)&255}function mg(e,t,r,n,a,s){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function yg(e,t,r,n,a){return a||mg(e,0,r,4),Oy(e,t,r,n,23,4),r+4}function gg(e,t,r,n,a){return a||mg(e,0,r,8),Oy(e,t,r,n,52,8),r+8}Gy.prototype.slice=function(e,t){var r,n=this.length;if((e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t<e&&(t=e),Gy.TYPED_ARRAY_SUPPORT)(r=this.subarray(e,t)).__proto__=Gy.prototype;else{var a=t-e;r=new Gy(a,void 0);for(var s=0;s<a;++s)r[s]=this[s+e]}return r},Gy.prototype.readUIntLE=function(e,t,r){e|=0,t|=0,r||pg(e,t,this.length);for(var n=this[e],a=1,s=0;++s<t&&(a*=256);)n+=this[e+s]*a;return n},Gy.prototype.readUIntBE=function(e,t,r){e|=0,t|=0,r||pg(e,t,this.length);for(var n=this[e+--t],a=1;t>0&&(a*=256);)n+=this[e+--t]*a;return n},Gy.prototype.readUInt8=function(e,t){return t||pg(e,1,this.length),this[e]},Gy.prototype.readUInt16LE=function(e,t){return t||pg(e,2,this.length),this[e]|this[e+1]<<8},Gy.prototype.readUInt16BE=function(e,t){return t||pg(e,2,this.length),this[e]<<8|this[e+1]},Gy.prototype.readUInt32LE=function(e,t){return t||pg(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},Gy.prototype.readUInt32BE=function(e,t){return t||pg(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},Gy.prototype.readIntLE=function(e,t,r){e|=0,t|=0,r||pg(e,t,this.length);for(var n=this[e],a=1,s=0;++s<t&&(a*=256);)n+=this[e+s]*a;return n>=(a*=128)&&(n-=Math.pow(2,8*t)),n},Gy.prototype.readIntBE=function(e,t,r){e|=0,t|=0,r||pg(e,t,this.length);for(var n=t,a=1,s=this[e+--n];n>0&&(a*=256);)s+=this[e+--n]*a;return s>=(a*=128)&&(s-=Math.pow(2,8*t)),s},Gy.prototype.readInt8=function(e,t){return t||pg(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},Gy.prototype.readInt16LE=function(e,t){t||pg(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},Gy.prototype.readInt16BE=function(e,t){t||pg(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},Gy.prototype.readInt32LE=function(e,t){return t||pg(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},Gy.prototype.readInt32BE=function(e,t){return t||pg(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},Gy.prototype.readFloatLE=function(e,t){return t||pg(e,4,this.length),By(this,e,!0,23,4)},Gy.prototype.readFloatBE=function(e,t){return t||pg(e,4,this.length),By(this,e,!1,23,4)},Gy.prototype.readDoubleLE=function(e,t){return t||pg(e,8,this.length),By(this,e,!0,52,8)},Gy.prototype.readDoubleBE=function(e,t){return t||pg(e,8,this.length),By(this,e,!1,52,8)},Gy.prototype.writeUIntLE=function(e,t,r,n){(e=+e,t|=0,r|=0,n)||dg(this,e,t,r,Math.pow(2,8*r)-1,0);var a=1,s=0;for(this[t]=255&e;++s<r&&(a*=256);)this[t+s]=e/a&255;return t+r},Gy.prototype.writeUIntBE=function(e,t,r,n){(e=+e,t|=0,r|=0,n)||dg(this,e,t,r,Math.pow(2,8*r)-1,0);var a=r-1,s=1;for(this[t+a]=255&e;--a>=0&&(s*=256);)this[t+a]=e/s&255;return t+r},Gy.prototype.writeUInt8=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,1,255,0),Gy.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},Gy.prototype.writeUInt16LE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,2,65535,0),Gy.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):fg(this,e,t,!0),t+2},Gy.prototype.writeUInt16BE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,2,65535,0),Gy.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):fg(this,e,t,!1),t+2},Gy.prototype.writeUInt32LE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,4,4294967295,0),Gy.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):hg(this,e,t,!0),t+4},Gy.prototype.writeUInt32BE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,4,4294967295,0),Gy.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):hg(this,e,t,!1),t+4},Gy.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t|=0,!n){var a=Math.pow(2,8*r-1);dg(this,e,t,r,a-1,-a)}var s=0,i=1,o=0;for(this[t]=255&e;++s<r&&(i*=256);)e<0&&0===o&&0!==this[t+s-1]&&(o=1),this[t+s]=(e/i>>0)-o&255;return t+r},Gy.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t|=0,!n){var a=Math.pow(2,8*r-1);dg(this,e,t,r,a-1,-a)}var s=r-1,i=1,o=0;for(this[t+s]=255&e;--s>=0&&(i*=256);)e<0&&0===o&&0!==this[t+s+1]&&(o=1),this[t+s]=(e/i>>0)-o&255;return t+r},Gy.prototype.writeInt8=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,1,127,-128),Gy.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},Gy.prototype.writeInt16LE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,2,32767,-32768),Gy.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):fg(this,e,t,!0),t+2},Gy.prototype.writeInt16BE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,2,32767,-32768),Gy.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):fg(this,e,t,!1),t+2},Gy.prototype.writeInt32LE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,4,2147483647,-2147483648),Gy.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):hg(this,e,t,!0),t+4},Gy.prototype.writeInt32BE=function(e,t,r){return e=+e,t|=0,r||dg(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),Gy.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):hg(this,e,t,!1),t+4},Gy.prototype.writeFloatLE=function(e,t,r){return yg(this,e,t,!0,r)},Gy.prototype.writeFloatBE=function(e,t,r){return yg(this,e,t,!1,r)},Gy.prototype.writeDoubleLE=function(e,t,r){return gg(this,e,t,!0,r)},Gy.prototype.writeDoubleBE=function(e,t,r){return gg(this,e,t,!1,r)},Gy.prototype.copy=function(e,t,r,n){if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n<r&&(n=r),n===r)return 0;if(0===e.length||0===this.length)return 0;if(t<0)throw new RangeError("targetStart out of bounds");if(r<0||r>=this.length)throw new RangeError("sourceStart out of bounds");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t<n-r&&(n=e.length-t+r);var a,s=n-r;if(this===e&&r<t&&t<n)for(a=s-1;a>=0;--a)e[a+t]=this[a+r];else if(s<1e3||!Gy.TYPED_ARRAY_SUPPORT)for(a=0;a<s;++a)e[a+t]=this[a+r];else Uint8Array.prototype.set.call(e,this.subarray(r,r+s),t);return s},Gy.prototype.fill=function(e,t,r,n){if("string"==typeof e){if("string"==typeof t?(n=t,t=0,r=this.length):"string"==typeof r&&(n=r,r=this.length),1===e.length){var a=e.charCodeAt(0);a<256&&(e=a)}if(void 0!==n&&"string"!=typeof n)throw new TypeError("encoding must be a string");if("string"==typeof n&&!Gy.isEncoding(n))throw new TypeError("Unknown encoding: "+n)}else"number"==typeof e&&(e&=255);if(t<0||this.length<t||this.length<r)throw new RangeError("Out of range index");if(r<=t)return this;var s;if(t>>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(s=t;s<r;++s)this[s]=e;else{var i=zy(e)?e:xg(new Gy(e,n).toString()),o=i.length;for(s=0;s<r-t;++s)this[s+t]=i[s%o]}return this};var vg=/[^+\/0-9A-Za-z-_]/g;function bg(e){return e<16?"0"+e.toString(16):e.toString(16)}function xg(e,t){var r;t=t||1/0;for(var n=e.length,a=null,s=[],i=0;i<n;++i){if((r=e.charCodeAt(i))>55295&&r<57344){if(!a){if(r>56319){(t-=3)>-1&&s.push(239,191,189);continue}if(i+1===n){(t-=3)>-1&&s.push(239,191,189);continue}a=r;continue}if(r<56320){(t-=3)>-1&&s.push(239,191,189),a=r;continue}r=65536+(a-55296<<10|r-56320)}else a&&(t-=3)>-1&&s.push(239,191,189);if(a=null,r<128){if((t-=1)<0)break;s.push(r)}else if(r<2048){if((t-=2)<0)break;s.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;s.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;s.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return s}function Eg(e){return function(e){var t,r,n,a,s,i;ky||Fy();var o=e.length;if(o%4>0)throw new Error("Invalid string. Length must be a multiple of 4");s="="===e[o-2]?2:"="===e[o-1]?1:0,i=new Py(3*o/4-s),n=s>0?o-4:o;var u=0;for(t=0,r=0;t<n;t+=4,r+=3)a=jy[e.charCodeAt(t)]<<18|jy[e.charCodeAt(t+1)]<<12|jy[e.charCodeAt(t+2)]<<6|jy[e.charCodeAt(t+3)],i[u++]=a>>16&255,i[u++]=a>>8&255,i[u++]=255&a;return 2===s?(a=jy[e.charCodeAt(t)]<<2|jy[e.charCodeAt(t+1)]>>4,i[u++]=255&a):1===s&&(a=jy[e.charCodeAt(t)]<<10|jy[e.charCodeAt(t+1)]<<4|jy[e.charCodeAt(t+2)]>>2,i[u++]=a>>8&255,i[u++]=255&a),i}(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(vg,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function Ag(e,t,r,n){for(var a=0;a<n&&!(a+r>=t.length||a>=e.length);++a)t[a+r]=e[a];return a}function wg(e){return null!=e&&(!!e._isBuffer||Sg(e)||function(e){return"function"==typeof e.readFloatLE&&"function"==typeof e.slice&&Sg(e.slice(0,0))}(e))}function Sg(e){return!!e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}var Dg=Object.freeze({__proto__:null,INSPECT_MAX_BYTES:50,kMaxLength:My,Buffer:Gy,SlowBuffer:function(e){return+e!=e&&(e=0),Gy.alloc(+e)},isBuffer:wg}),Cg={},Tg=Cg.hasOwnProperty,jg=function(e,t){for(var r in e)Tg.call(e,r)&&t(r,e[r])},Pg=Cg.toString,kg=Array.isArray,Fg=wg,_g={'"':'\\"',"'":"\\'","\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t"},Ig=/["'\\\b\f\n\r\t]/,Bg=/[0-9]/,Og=/[ !#-&\(-\[\]-_a-~]/,Ng=function e(t,r){var n,a,s=function(){f=d,++r.indentLevel,d=r.indent.repeat(r.indentLevel)},i={escapeEverything:!1,minimal:!1,isScriptContext:!1,quotes:"single",wrap:!1,es6:!1,json:!1,compact:!0,lowercaseHex:!1,numbers:"decimal",indent:"\t",indentLevel:0,__inline1__:!1,__inline2__:!1},o=r&&r.json;o&&(i.quotes="double",i.wrap=!0),n=i,"single"!=(r=(a=r)?(jg(a,(function(e,t){n[e]=t})),n):n).quotes&&"double"!=r.quotes&&"backtick"!=r.quotes&&(r.quotes="single");var u,c="double"==r.quotes?'"':"backtick"==r.quotes?"`":"'",l=r.compact,p=r.lowercaseHex,d=r.indent.repeat(r.indentLevel),f="",h=r.__inline1__,m=r.__inline2__,y=l?"":"\n",g=!0,v="binary"==r.numbers,b="octal"==r.numbers,x="decimal"==r.numbers,E="hexadecimal"==r.numbers;if(o&&t&&"function"==typeof t.toJSON&&(t=t.toJSON()),!function(e){return"string"==typeof e||"[object String]"==Pg.call(e)}(t)){if(function(e){return"[object Map]"==Pg.call(e)}(t))return 0==t.size?"new Map()":(l||(r.__inline1__=!0,r.__inline2__=!1),"new Map("+e(Array.from(t),r)+")");if(function(e){return"[object Set]"==Pg.call(e)}(t))return 0==t.size?"new Set()":"new Set("+e(Array.from(t),r)+")";if(Fg(t))return 0==t.length?"Buffer.from([])":"Buffer.from("+e(Array.from(t),r)+")";if(kg(t))return u=[],r.wrap=!0,h&&(r.__inline1__=!1,r.__inline2__=!0),m||s(),function(e,t){for(var r=e.length,n=-1;++n<r;)t(e[n])}(t,(function(t){g=!1,m&&(r.__inline2__=!1),u.push((l||m?"":d)+e(t,r))})),g?"[]":m?"["+u.join(", ")+"]":"["+y+u.join(","+y)+y+(l?"":f)+"]";if(!function(e){return"number"==typeof e||"[object Number]"==Pg.call(e)}(t))return function(e){return"[object Object]"==Pg.call(e)}(t)?(u=[],r.wrap=!0,s(),jg(t,(function(t,n){g=!1,u.push((l?"":d)+e(t,r)+":"+(l?"":" ")+e(n,r))})),g?"{}":"{"+y+u.join(","+y)+y+(l?"":f)+"}"):o?JSON.stringify(t)||"null":String(t);if(o)return JSON.stringify(t);if(x)return String(t);if(E){var A=t.toString(16);return p||(A=A.toUpperCase()),"0x"+A}if(v)return"0b"+t.toString(2);if(b)return"0o"+t.toString(8)}var w=t,S=-1,D=w.length;for(u="";++S<D;){var C=w.charAt(S);if(r.es6){var T=w.charCodeAt(S);if(T>=55296&&T<=56319&&D>S+1){var j=w.charCodeAt(S+1);if(j>=56320&&j<=57343){var P=(1024*(T-55296)+j-56320+65536).toString(16);p||(P=P.toUpperCase()),u+="\\u{"+P+"}",++S;continue}}}if(!r.escapeEverything){if(Og.test(C)){u+=C;continue}if('"'==C){u+=c==C?'\\"':C;continue}if("`"==C){u+=c==C?"\\`":C;continue}if("'"==C){u+=c==C?"\\'":C;continue}}if("\0"!=C||o||Bg.test(w.charAt(S+1)))if(Ig.test(C))u+=_g[C];else{var k=C.charCodeAt(0);if(r.minimal&&8232!=k&&8233!=k)u+=C;else{var F=k.toString(16);p||(F=F.toUpperCase());var _=F.length>2||o,I="\\"+(_?"u":"x")+("0000"+F).slice(_?-4:-2);u+=I}}else u+="\\0"}return r.wrap&&(u=c+u+c),"`"==c&&(u=u.replace(/\$\{/g,"\\${")),r.isScriptContext?u.replace(/<\/(script|style)/gi,"<\\/$1").replace(/<!--/g,o?"\\u003C!--":"\\x3C!--"):u};Ng.version="2.5.2";var Rg=Ng;function Mg(e){this.token("..."),this.print(e.argument,e)}function Lg(e){var t=e.properties;this.token("{"),this.printInnerComments(e),t.length&&(this.space(),this.printList(t,e,{indent:!0,statement:!0}),this.space()),this.token("}")}function Ug(e){var t=e.elements,r=t.length;this.token("["),this.printInnerComments(e);for(var n=0;n<t.length;n++){var a=t[n];a?(n>0&&this.space(),this.print(a,e),n<r-1&&this.token(",")):this.token(",")}this.token("]")}function Gg(e){var t=this.getPossibleRaw(e),r=e.value+"";null==t?this.number(r):this.format.minified?this.number(t.length<r.length?t:r):this.number(t)}function Vg(e){var t=this.getPossibleRaw(e);if(this.format.minified||null==t){var r=this.format.jsescOption;this.format.jsonCompatibleStrings&&(r.json=!0);var n=Rg(e.value,r);return this.token(n)}this.token(t)}function Wg(e,t,r){r&&(e.space(),e.word("of"),e.space(),e.word(t)),e.space()}function Hg(e,t){var r=t.members;e.token("{"),e.indent(),e.newline();var n=r,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;e.print(o,t),e.newline()}e.dedent(),e.token("}")}function qg(e,t){var r=t.id,n=t.init;e.print(r,t),e.space(),e.token("="),e.space(),e.print(n,t),e.token(",")}function Kg(e){if(e.declaration){var t=e.declaration;this.print(t,e),st(t)||this.semicolon()}else this.token("{"),e.specifiers.length&&(this.space(),this.printList(e.specifiers,e),this.space()),this.token("}"),e.source&&(this.space(),this.word("from"),this.space(),this.print(e.source,e)),this.semicolon()}function zg(e){this.print(e.id,e),this.print(e.typeParameters,e)}function Xg(){this.space(),this.token("&"),this.space()}function Yg(e){this.token("<"),this.printList(e.params,e,{}),this.token(">")}function Jg(){this.space(),this.token("|"),this.space()}var $g=/(?:^|[^\\])(?:\\\\)*'/,Qg=/(?:^|[^\\])(?:\\\\)*"/;function Zg(){this.space()}function ev(e){this.token("<"),this.printList(e.params,e,{}),this.token(">")}function tv(e,t){!0!==t&&e.token(t)}var rv=Object.freeze({__proto__:null,TaggedTemplateExpression:function(e){this.print(e.tag,e),this.print(e.typeParameters,e),this.print(e.quasi,e)},TemplateElement:function(e,t){var r=t.quasis[0]===e,n=t.quasis[t.quasis.length-1]===e,a=(r?"`":"}")+e.value.raw+(n?"`":"${");this.token(a)},TemplateLiteral:function(e){for(var t=e.quasis,r=0;r<t.length;r++)this.print(t[r],e),r+1<t.length&&this.print(e.expressions[r],e)},UnaryExpression:function(e){"void"===e.operator||"delete"===e.operator||"typeof"===e.operator||"throw"===e.operator?(this.word(e.operator),this.space()):this.token(e.operator),this.print(e.argument,e)},DoExpression:function(e){this.word("do"),this.space(),this.print(e.body,e)},ParenthesizedExpression:function(e){this.token("("),this.print(e.expression,e),this.token(")")},UpdateExpression:function(e){e.prefix?(this.token(e.operator),this.print(e.argument,e)):(this.startTerminatorless(!0),this.print(e.argument,e),this.endTerminatorless(),this.token(e.operator))},ConditionalExpression:function(e){this.print(e.test,e),this.space(),this.token("?"),this.space(),this.print(e.consequent,e),this.space(),this.token(":"),this.space(),this.print(e.alternate,e)},NewExpression:function(e,t){this.word("new"),this.space(),this.print(e.callee,e),(!this.format.minified||0!==e.arguments.length||e.optional||f(t,{callee:e})||_(t)||I(t))&&(this.print(e.typeArguments,e),this.print(e.typeParameters,e),e.optional&&this.token("?."),this.token("("),this.printList(e.arguments,e),this.token(")"))},SequenceExpression:function(e){this.printList(e.expressions,e)},ThisExpression:function(){this.word("this")},Super:function(){this.word("super")},Decorator:function(e){this.token("@"),this.print(e.expression,e),this.newline()},OptionalMemberExpression:function(e){if(this.print(e.object,e),!e.computed&&_(e.property))throw new TypeError("Got a MemberExpression for MemberExpression property");var t=e.computed;ft(e.property)&&"number"==typeof e.property.value&&(t=!0),e.optional&&this.token("?."),t?(this.token("["),this.print(e.property,e),this.token("]")):(e.optional||this.token("."),this.print(e.property,e))},OptionalCallExpression:function(e){this.print(e.callee,e),this.print(e.typeArguments,e),this.print(e.typeParameters,e),e.optional&&this.token("?."),this.token("("),this.printList(e.arguments,e),this.token(")")},CallExpression:function(e){this.print(e.callee,e),this.print(e.typeArguments,e),this.print(e.typeParameters,e),this.token("("),this.printList(e.arguments,e),this.token(")")},Import:function(){this.word("import")},YieldExpression:ly,AwaitExpression:py,EmptyStatement:function(){this.semicolon(!0)},ExpressionStatement:function(e){this.print(e.expression,e),this.semicolon()},AssignmentPattern:function(e){this.print(e.left,e),e.left.optional&&this.token("?"),this.print(e.left.typeAnnotation,e),this.space(),this.token("="),this.space(),this.print(e.right,e)},AssignmentExpression:dy,BindExpression:function(e){this.print(e.object,e),this.token("::"),this.print(e.callee,e)},BinaryExpression:dy,LogicalExpression:dy,MemberExpression:function(e){if(this.print(e.object,e),!e.computed&&_(e.property))throw new TypeError("Got a MemberExpression for MemberExpression property");var t=e.computed;ft(e.property)&&"number"==typeof e.property.value&&(t=!0),t?(this.token("["),this.print(e.property,e),this.token("]")):(this.token("."),this.print(e.property,e))},MetaProperty:function(e){this.print(e.meta,e),this.token("."),this.print(e.property,e)},PrivateName:function(e){this.token("#"),this.print(e.id,e)},V8IntrinsicIdentifier:function(e){this.token("%"),this.word(e.name)},WithStatement:function(e){this.word("with"),this.space(),this.token("("),this.print(e.object,e),this.token(")"),this.printBlock(e)},IfStatement:function(e){this.word("if"),this.space(),this.token("("),this.print(e.test,e),this.token(")"),this.space();var t=e.alternate&&D(function e(t){return st(t.body)?e(t.body):t}(e.consequent));t&&(this.token("{"),this.newline(),this.indent()),this.printAndIndentOnComments(e.consequent,e),t&&(this.dedent(),this.newline(),this.token("}")),e.alternate&&(this.endsWith("}")&&this.space(),this.word("else"),this.space(),this.printAndIndentOnComments(e.alternate,e))},ForStatement:function(e){this.word("for"),this.space(),this.token("("),this.inForStatementInitCounter++,this.print(e.init,e),this.inForStatementInitCounter--,this.token(";"),e.test&&(this.space(),this.print(e.test,e)),this.token(";"),e.update&&(this.space(),this.print(e.update,e)),this.token(")"),this.printBlock(e)},WhileStatement:function(e){this.word("while"),this.space(),this.token("("),this.print(e.test,e),this.token(")"),this.printBlock(e)},ForInStatement:hy,ForOfStatement:my,DoWhileStatement:function(e){this.word("do"),this.space(),this.print(e.body,e),this.space(),this.word("while"),this.space(),this.token("("),this.print(e.test,e),this.token(")"),this.semicolon()},ContinueStatement:gy,ReturnStatement:vy,BreakStatement:by,ThrowStatement:xy,LabeledStatement:function(e){this.print(e.label,e),this.token(":"),this.space(),this.print(e.body,e)},TryStatement:function(e){this.word("try"),this.space(),this.print(e.block,e),this.space(),e.handlers?this.print(e.handlers[0],e):this.print(e.handler,e),e.finalizer&&(this.space(),this.word("finally"),this.space(),this.print(e.finalizer,e))},CatchClause:function(e){this.word("catch"),this.space(),e.param&&(this.token("("),this.print(e.param,e),this.token(")"),this.space()),this.print(e.body,e)},SwitchStatement:function(e){this.word("switch"),this.space(),this.token("("),this.print(e.discriminant,e),this.token(")"),this.space(),this.token("{"),this.printSequence(e.cases,e,{indent:!0,addNewlines:function(t,r){if(!t&&e.cases[e.cases.length-1]===r)return-1}}),this.token("}")},SwitchCase:function(e){e.test?(this.word("case"),this.space(),this.print(e.test,e),this.token(":")):(this.word("default"),this.token(":")),e.consequent.length&&(this.newline(),this.printSequence(e.consequent,e,{indent:!0}))},DebuggerStatement:function(){this.word("debugger"),this.semicolon()},VariableDeclaration:function(e,t){e.declare&&(this.word("declare"),this.space()),this.word(e.kind),this.space();var r,n=!1;if(!ut(t))for(var a=0,s=e.declarations;a<s.length;a++){s[a].init&&(n=!0)}n&&(r="const"===e.kind?Ay:Ey),this.printList(e.declarations,e,{separator:r}),(!ut(t)||t.left!==e&&t.init!==e)&&this.semicolon()},VariableDeclarator:function(e){this.print(e.id,e),e.definite&&this.token("!"),this.print(e.id.typeAnnotation,e),e.init&&(this.space(),this.token("="),this.space(),this.print(e.init,e))},ClassDeclaration:wy,ClassExpression:wy,ClassBody:function(e){this.token("{"),this.printInnerComments(e),0===e.body.length?this.token("}"):(this.newline(),this.indent(),this.printSequence(e.body,e),this.dedent(),this.endsWith("\n")||this.newline(),this.rightBrace())},ClassProperty:function(e){this.printJoin(e.decorators,e),this.tsPrintClassMemberModifiers(e,!0),e.computed?(this.token("["),this.print(e.key,e),this.token("]")):(this._variance(e),this.print(e.key,e)),e.optional&&this.token("?"),e.definite&&this.token("!"),this.print(e.typeAnnotation,e),e.value&&(this.space(),this.token("="),this.space(),this.print(e.value,e)),this.semicolon()},ClassPrivateProperty:function(e){e.static&&(this.word("static"),this.space()),this.print(e.key,e),this.print(e.typeAnnotation,e),e.value&&(this.space(),this.token("="),this.space(),this.print(e.value,e)),this.semicolon()},ClassMethod:function(e){this._classMethodHead(e),this.space(),this.print(e.body,e)},ClassPrivateMethod:function(e){this._classMethodHead(e),this.space(),this.print(e.body,e)},_classMethodHead:function(e){this.printJoin(e.decorators,e),this.tsPrintClassMemberModifiers(e,!1),this._methodHead(e)},_params:function(e){this.print(e.typeParameters,e),this.token("("),this._parameters(e.params,e),this.token(")"),this.print(e.returnType,e)},_parameters:function(e,t){for(var r=0;r<e.length;r++)this._param(e[r],t),r<e.length-1&&(this.token(","),this.space())},_param:function(e,t){this.printJoin(e.decorators,e),this.print(e,t),e.optional&&this.token("?"),this.print(e.typeAnnotation,e)},_methodHead:function(e){var t=e.kind,r=e.key;"get"!==t&&"set"!==t||(this.word(t),this.space()),e.async&&(this.word("async"),this.space()),"method"!==t&&"init"!==t||e.generator&&this.token("*"),e.computed?(this.token("["),this.print(r,e),this.token("]")):this.print(r,e),e.optional&&this.token("?"),this._params(e)},_predicate:function(e){e.predicate&&(e.returnType||this.token(":"),this.space(),this.print(e.predicate,e))},_functionHead:function(e){e.async&&(this.word("async"),this.space()),this.word("function"),e.generator&&this.token("*"),this.space(),e.id&&this.print(e.id,e),this._params(e),this._predicate(e)},FunctionExpression:Sy,FunctionDeclaration:Sy,ArrowFunctionExpression:function(e){e.async&&(this.word("async"),this.space());var t=e.params[0];1===e.params.length&&S(t)&&!function(e,t){return e.typeParameters||e.returnType||t.typeAnnotation||t.optional||t.trailingComments}(e,t)?this.format.retainLines&&e.loc&&e.body.loc&&e.loc.start.line<e.body.loc.start.line?(this.token("("),t.loc&&t.loc.start.line>e.loc.start.line?(this.indent(),this.print(t,e),this.dedent(),this._catchUp("start",e.body.loc)):this.print(t,e),this.token(")")):this.print(t,e):this._params(e),this._predicate(e),this.space(),this.token("=>"),this.space(),this.print(e.body,e)},ImportSpecifier:function(e){"type"!==e.importKind&&"typeof"!==e.importKind||(this.word(e.importKind),this.space()),this.print(e.imported,e),e.local&&e.local.name!==e.imported.name&&(this.space(),this.word("as"),this.space(),this.print(e.local,e))},ImportDefaultSpecifier:function(e){this.print(e.local,e)},ExportDefaultSpecifier:function(e){this.print(e.exported,e)},ExportSpecifier:function(e){this.print(e.local,e),e.exported&&e.local.name!==e.exported.name&&(this.space(),this.word("as"),this.space(),this.print(e.exported,e))},ExportNamespaceSpecifier:function(e){this.token("*"),this.space(),this.word("as"),this.space(),this.print(e.exported,e)},ExportAllDeclaration:Dy,ExportNamedDeclaration:function(e){this.format.decoratorsBeforeExport&&Z(e.declaration)&&this.printJoin(e.declaration.decorators,e),this.word("export"),this.space(),Cy.apply(this,arguments)},ExportDefaultDeclaration:function(e){this.format.decoratorsBeforeExport&&Z(e.declaration)&&this.printJoin(e.declaration.decorators,e),this.word("export"),this.space(),this.word("default"),this.space(),Cy.apply(this,arguments)},ImportDeclaration:function(e){this.word("import"),this.space(),"type"!==e.importKind&&"typeof"!==e.importKind||(this.word(e.importKind),this.space());var t=e.specifiers.slice(0);if(t&&t.length){for(;;){var r=t[0];if(!se(r)&&!ie(r))break;this.print(t.shift(),e),t.length&&(this.token(","),this.space())}t.length&&(this.token("{"),this.space(),this.printList(t,e),this.space(),this.token("}")),this.space(),this.word("from"),this.space()}this.print(e.source,e),this.semicolon()},ImportNamespaceSpecifier:function(e){this.token("*"),this.space(),this.word("as"),this.space(),this.print(e.local,e)},Identifier:function(e){var t=this;this.exactSource(e.loc,(function(){t.word(e.name)}))},ArgumentPlaceholder:function(){this.token("?")},RestElement:Mg,SpreadElement:Mg,ObjectExpression:Lg,ObjectPattern:Lg,ObjectMethod:function(e){this.printJoin(e.decorators,e),this._methodHead(e),this.space(),this.print(e.body,e)},ObjectProperty:function(e){if(this.printJoin(e.decorators,e),e.computed)this.token("["),this.print(e.key,e),this.token("]");else{if(X(e.value)&&S(e.key)&&e.key.name===e.value.left.name)return void this.print(e.value,e);if(this.print(e.key,e),e.shorthand&&S(e.key)&&S(e.value)&&e.key.name===e.value.name)return}this.token(":"),this.space(),this.print(e.value,e)},ArrayExpression:Ug,ArrayPattern:Ug,RegExpLiteral:function(e){this.word("/"+e.pattern+"/"+e.flags)},BooleanLiteral:function(e){this.word(e.value?"true":"false")},NullLiteral:function(){this.word("null")},NumericLiteral:Gg,StringLiteral:Vg,BigIntLiteral:function(e){var t=this.getPossibleRaw(e);this.format.minified||null==t?this.token(e.value):this.token(t)},PipelineTopicExpression:function(e){this.print(e.expression,e)},PipelineBareFunction:function(e){this.print(e.callee,e)},PipelinePrimaryTopicReference:function(){this.token("#")},AnyTypeAnnotation:function(){this.word("any")},ArrayTypeAnnotation:function(e){this.print(e.elementType,e),this.token("["),this.token("]")},BooleanTypeAnnotation:function(){this.word("boolean")},BooleanLiteralTypeAnnotation:function(e){this.word(e.value?"true":"false")},NullLiteralTypeAnnotation:function(){this.word("null")},DeclareClass:function(e,t){ve(t)||(this.word("declare"),this.space()),this.word("class"),this.space(),this._interfaceish(e)},DeclareFunction:function(e,t){ve(t)||(this.word("declare"),this.space()),this.word("function"),this.space(),this.print(e.id,e),this.print(e.id.typeAnnotation.typeAnnotation,e),e.predicate&&(this.space(),this.print(e.predicate,e)),this.semicolon()},InferredPredicate:function(){this.token("%"),this.word("checks")},DeclaredPredicate:function(e){this.token("%"),this.word("checks"),this.token("("),this.print(e.value,e),this.token(")")},DeclareInterface:function(e){this.word("declare"),this.space(),this.InterfaceDeclaration(e)},DeclareModule:function(e){this.word("declare"),this.space(),this.word("module"),this.space(),this.print(e.id,e),this.space(),this.print(e.body,e)},DeclareModuleExports:function(e){this.word("declare"),this.space(),this.word("module"),this.token("."),this.word("exports"),this.print(e.typeAnnotation,e)},DeclareTypeAlias:function(e){this.word("declare"),this.space(),this.TypeAlias(e)},DeclareOpaqueType:function(e,t){ve(t)||(this.word("declare"),this.space()),this.OpaqueType(e)},DeclareVariable:function(e,t){ve(t)||(this.word("declare"),this.space()),this.word("var"),this.space(),this.print(e.id,e),this.print(e.id.typeAnnotation,e),this.semicolon()},DeclareExportDeclaration:function(e){this.word("declare"),this.space(),this.word("export"),this.space(),e.default&&(this.word("default"),this.space()),Kg.apply(this,arguments)},DeclareExportAllDeclaration:function(){this.word("declare"),this.space(),Dy.apply(this,arguments)},EnumDeclaration:function(e){var t=e.id,r=e.body;this.word("enum"),this.space(),this.print(t,e),this.print(r,e)},EnumBooleanBody:function(e){Wg(this,"boolean",e.explicitType),Hg(this,e)},EnumNumberBody:function(e){Wg(this,"number",e.explicitType),Hg(this,e)},EnumStringBody:function(e){Wg(this,"string",e.explicitType),Hg(this,e)},EnumSymbolBody:function(e){Wg(this,"symbol",!0),Hg(this,e)},EnumDefaultedMember:function(e){var t=e.id;this.print(t,e),this.token(",")},EnumBooleanMember:function(e){qg(this,e)},EnumNumberMember:function(e){qg(this,e)},EnumStringMember:function(e){qg(this,e)},ExistsTypeAnnotation:function(){this.token("*")},FunctionTypeAnnotation:function(e,t){this.print(e.typeParameters,e),this.token("("),this.printList(e.params,e),e.rest&&(e.params.length&&(this.token(","),this.space()),this.token("..."),this.print(e.rest,e)),this.token(")"),"ObjectTypeCallProperty"===t.type||"DeclareFunction"===t.type||"ObjectTypeProperty"===t.type&&t.method?this.token(":"):(this.space(),this.token("=>")),this.space(),this.print(e.returnType,e)},FunctionTypeParam:function(e){this.print(e.name,e),e.optional&&this.token("?"),e.name&&(this.token(":"),this.space()),this.print(e.typeAnnotation,e)},InterfaceExtends:zg,ClassImplements:zg,GenericTypeAnnotation:zg,_interfaceish:function(e){this.print(e.id,e),this.print(e.typeParameters,e),e.extends.length&&(this.space(),this.word("extends"),this.space(),this.printList(e.extends,e)),e.mixins&&e.mixins.length&&(this.space(),this.word("mixins"),this.space(),this.printList(e.mixins,e)),e.implements&&e.implements.length&&(this.space(),this.word("implements"),this.space(),this.printList(e.implements,e)),this.space(),this.print(e.body,e)},_variance:function(e){e.variance&&("plus"===e.variance.kind?this.token("+"):"minus"===e.variance.kind&&this.token("-"))},InterfaceDeclaration:function(e){this.word("interface"),this.space(),this._interfaceish(e)},InterfaceTypeAnnotation:function(e){this.word("interface"),e.extends&&e.extends.length&&(this.space(),this.word("extends"),this.space(),this.printList(e.extends,e)),this.space(),this.print(e.body,e)},IntersectionTypeAnnotation:function(e){this.printJoin(e.types,e,{separator:Xg})},MixedTypeAnnotation:function(){this.word("mixed")},EmptyTypeAnnotation:function(){this.word("empty")},NullableTypeAnnotation:function(e){this.token("?"),this.print(e.typeAnnotation,e)},NumberTypeAnnotation:function(){this.word("number")},StringTypeAnnotation:function(){this.word("string")},ThisTypeAnnotation:function(){this.word("this")},TupleTypeAnnotation:function(e){this.token("["),this.printList(e.types,e),this.token("]")},TypeofTypeAnnotation:function(e){this.word("typeof"),this.space(),this.print(e.argument,e)},TypeAlias:function(e){this.word("type"),this.space(),this.print(e.id,e),this.print(e.typeParameters,e),this.space(),this.token("="),this.space(),this.print(e.right,e),this.semicolon()},TypeAnnotation:function(e){this.token(":"),this.space(),e.optional&&this.token("?"),this.print(e.typeAnnotation,e)},TypeParameterInstantiation:Yg,TypeParameterDeclaration:Yg,TypeParameter:function(e){this._variance(e),this.word(e.name),e.bound&&this.print(e.bound,e),e.default&&(this.space(),this.token("="),this.space(),this.print(e.default,e))},OpaqueType:function(e){this.word("opaque"),this.space(),this.word("type"),this.space(),this.print(e.id,e),this.print(e.typeParameters,e),e.supertype&&(this.token(":"),this.space(),this.print(e.supertype,e)),e.impltype&&(this.space(),this.token("="),this.space(),this.print(e.impltype,e)),this.semicolon()},ObjectTypeAnnotation:function(e){var t=this;e.exact?this.token("{|"):this.token("{");var r=e.properties.concat(e.callProperties||[],e.indexers||[],e.internalSlots||[]);r.length&&(this.space(),this.printJoin(r,e,{addNewlines:function(e){if(e&&!r[0])return 1},indent:!0,statement:!0,iterator:function(){(1!==r.length||e.inexact)&&(t.token(","),t.space())}}),this.space()),e.inexact&&(this.indent(),this.token("..."),r.length&&this.newline(),this.dedent()),e.exact?this.token("|}"):this.token("}")},ObjectTypeInternalSlot:function(e){e.static&&(this.word("static"),this.space()),this.token("["),this.token("["),this.print(e.id,e),this.token("]"),this.token("]"),e.optional&&this.token("?"),e.method||(this.token(":"),this.space()),this.print(e.value,e)},ObjectTypeCallProperty:function(e){e.static&&(this.word("static"),this.space()),this.print(e.value,e)},ObjectTypeIndexer:function(e){e.static&&(this.word("static"),this.space()),this._variance(e),this.token("["),e.id&&(this.print(e.id,e),this.token(":"),this.space()),this.print(e.key,e),this.token("]"),this.token(":"),this.space(),this.print(e.value,e)},ObjectTypeProperty:function(e){e.proto&&(this.word("proto"),this.space()),e.static&&(this.word("static"),this.space()),this._variance(e),this.print(e.key,e),e.optional&&this.token("?"),e.method||(this.token(":"),this.space()),this.print(e.value,e)},ObjectTypeSpreadProperty:function(e){this.token("..."),this.print(e.argument,e)},QualifiedTypeIdentifier:function(e){this.print(e.qualification,e),this.token("."),this.print(e.id,e)},UnionTypeAnnotation:function(e){this.printJoin(e.types,e,{separator:Jg})},TypeCastExpression:function(e){this.token("("),this.print(e.expression,e),this.print(e.typeAnnotation,e),this.token(")")},Variance:function(e){"plus"===e.kind?this.token("+"):this.token("-")},VoidTypeAnnotation:function(){this.word("void")},NumberLiteralTypeAnnotation:Gg,StringLiteralTypeAnnotation:Vg,File:function(e){e.program&&this.print(e.program.interpreter,e),this.print(e.program,e)},Program:function(e){this.printInnerComments(e,!1),this.printSequence(e.directives,e),e.directives&&e.directives.length&&this.newline(),this.printSequence(e.body,e)},BlockStatement:function(e){this.token("{"),this.printInnerComments(e);var t=e.directives&&e.directives.length;e.body.length||t?(this.newline(),this.printSequence(e.directives,e,{indent:!0}),t&&this.newline(),this.printSequence(e.body,e,{indent:!0}),this.removeTrailingNewline(),this.source("end",e.loc),this.endsWith("\n")||this.newline(),this.rightBrace()):(this.source("end",e.loc),this.token("}"))},Noop:function(){},Directive:function(e){this.print(e.value,e),this.semicolon()},DirectiveLiteral:function(e){var t=this.getPossibleRaw(e);if(null==t){var r=e.value;if(Qg.test(r)){if($g.test(r))throw new Error("Malformed AST: it is not possible to print a directive containing both unescaped single and double quotes.");this.token("'"+r+"'")}else this.token('"'+r+'"')}else this.token(t)},InterpreterDirective:function(e){this.token("#!"+e.value+"\n")},Placeholder:function(e){this.token("%%"),this.print(e.name),this.token("%%"),"Statement"===e.expectedNode&&this.semicolon()},JSXAttribute:function(e){this.print(e.name,e),e.value&&(this.token("="),this.print(e.value,e))},JSXIdentifier:function(e){this.word(e.name)},JSXNamespacedName:function(e){this.print(e.namespace,e),this.token(":"),this.print(e.name,e)},JSXMemberExpression:function(e){this.print(e.object,e),this.token("."),this.print(e.property,e)},JSXSpreadAttribute:function(e){this.token("{"),this.token("..."),this.print(e.argument,e),this.token("}")},JSXExpressionContainer:function(e){this.token("{"),this.print(e.expression,e),this.token("}")},JSXSpreadChild:function(e){this.token("{"),this.token("..."),this.print(e.expression,e),this.token("}")},JSXText:function(e){var t=this.getPossibleRaw(e);null!=t?this.token(t):this.token(e.value)},JSXElement:function(e){var t=e.openingElement;if(this.print(t,e),!t.selfClosing){this.indent();for(var r=0,n=e.children;r<n.length;r++){var a=n[r];this.print(a,e)}this.dedent(),this.print(e.closingElement,e)}},JSXOpeningElement:function(e){this.token("<"),this.print(e.name,e),this.print(e.typeParameters,e),e.attributes.length>0&&(this.space(),this.printJoin(e.attributes,e,{separator:Zg})),e.selfClosing?(this.space(),this.token("/>")):this.token(">")},JSXClosingElement:function(e){this.token("</"),this.print(e.name,e),this.token(">")},JSXEmptyExpression:function(e){this.printInnerComments(e)},JSXFragment:function(e){this.print(e.openingFragment,e),this.indent();for(var t=0,r=e.children;t<r.length;t++){var n=r[t];this.print(n,e)}this.dedent(),this.print(e.closingFragment,e)},JSXOpeningFragment:function(){this.token("<"),this.token(">")},JSXClosingFragment:function(){this.token("</"),this.token(">")},TSTypeAnnotation:function(e){this.token(":"),this.space(),e.optional&&this.token("?"),this.print(e.typeAnnotation,e)},TSTypeParameterInstantiation:ev,TSTypeParameterDeclaration:ev,TSTypeParameter:function(e){this.word(e.name),e.constraint&&(this.space(),this.word("extends"),this.space(),this.print(e.constraint,e)),e.default&&(this.space(),this.token("="),this.space(),this.print(e.default,e))},TSParameterProperty:function(e){e.accessibility&&(this.word(e.accessibility),this.space()),e.readonly&&(this.word("readonly"),this.space()),this._param(e.parameter)},TSDeclareFunction:function(e){e.declare&&(this.word("declare"),this.space()),this._functionHead(e),this.token(";")},TSDeclareMethod:function(e){this._classMethodHead(e),this.token(";")},TSQualifiedName:function(e){this.print(e.left,e),this.token("."),this.print(e.right,e)},TSCallSignatureDeclaration:function(e){this.tsPrintSignatureDeclarationBase(e),this.token(";")},TSConstructSignatureDeclaration:function(e){this.word("new"),this.space(),this.tsPrintSignatureDeclarationBase(e),this.token(";")},TSPropertySignature:function(e){var t=e.readonly,r=e.initializer;t&&(this.word("readonly"),this.space()),this.tsPrintPropertyOrMethodName(e),this.print(e.typeAnnotation,e),r&&(this.space(),this.token("="),this.space(),this.print(r,e)),this.token(";")},tsPrintPropertyOrMethodName:function(e){e.computed&&this.token("["),this.print(e.key,e),e.computed&&this.token("]"),e.optional&&this.token("?")},TSMethodSignature:function(e){this.tsPrintPropertyOrMethodName(e),this.tsPrintSignatureDeclarationBase(e),this.token(";")},TSIndexSignature:function(e){e.readonly&&(this.word("readonly"),this.space()),this.token("["),this._parameters(e.parameters,e),this.token("]"),this.print(e.typeAnnotation,e),this.token(";")},TSAnyKeyword:function(){this.word("any")},TSBigIntKeyword:function(){this.word("bigint")},TSUnknownKeyword:function(){this.word("unknown")},TSNumberKeyword:function(){this.word("number")},TSObjectKeyword:function(){this.word("object")},TSBooleanKeyword:function(){this.word("boolean")},TSStringKeyword:function(){this.word("string")},TSSymbolKeyword:function(){this.word("symbol")},TSVoidKeyword:function(){this.word("void")},TSUndefinedKeyword:function(){this.word("undefined")},TSNullKeyword:function(){this.word("null")},TSNeverKeyword:function(){this.word("never")},TSThisType:function(){this.word("this")},TSFunctionType:function(e){this.tsPrintFunctionOrConstructorType(e)},TSConstructorType:function(e){this.word("new"),this.space(),this.tsPrintFunctionOrConstructorType(e)},tsPrintFunctionOrConstructorType:function(e){var t=e.typeParameters,r=e.parameters;this.print(t,e),this.token("("),this._parameters(r,e),this.token(")"),this.space(),this.token("=>"),this.space(),this.print(e.typeAnnotation.typeAnnotation,e)},TSTypeReference:function(e){this.print(e.typeName,e),this.print(e.typeParameters,e)},TSTypePredicate:function(e){e.asserts&&(this.word("asserts"),this.space()),this.print(e.parameterName),e.typeAnnotation&&(this.space(),this.word("is"),this.space(),this.print(e.typeAnnotation.typeAnnotation))},TSTypeQuery:function(e){this.word("typeof"),this.space(),this.print(e.exprName)},TSTypeLiteral:function(e){this.tsPrintTypeLiteralOrInterfaceBody(e.members,e)},tsPrintTypeLiteralOrInterfaceBody:function(e,t){this.tsPrintBraced(e,t)},tsPrintBraced:function(e,t){if(this.token("{"),e.length){this.indent(),this.newline();var r=e,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s;this.print(i,t),this.newline()}this.dedent(),this.rightBrace()}else this.token("}")},TSArrayType:function(e){this.print(e.elementType,e),this.token("[]")},TSTupleType:function(e){this.token("["),this.printList(e.elementTypes,e),this.token("]")},TSOptionalType:function(e){this.print(e.typeAnnotation,e),this.token("?")},TSRestType:function(e){this.token("..."),this.print(e.typeAnnotation,e)},TSUnionType:function(e){this.tsPrintUnionOrIntersectionType(e,"|")},TSIntersectionType:function(e){this.tsPrintUnionOrIntersectionType(e,"&")},tsPrintUnionOrIntersectionType:function(e,t){this.printJoin(e.types,e,{separator:function(){this.space(),this.token(t),this.space()}})},TSConditionalType:function(e){this.print(e.checkType),this.space(),this.word("extends"),this.space(),this.print(e.extendsType),this.space(),this.token("?"),this.space(),this.print(e.trueType),this.space(),this.token(":"),this.space(),this.print(e.falseType)},TSInferType:function(e){this.token("infer"),this.space(),this.print(e.typeParameter)},TSParenthesizedType:function(e){this.token("("),this.print(e.typeAnnotation,e),this.token(")")},TSTypeOperator:function(e){this.token(e.operator),this.space(),this.print(e.typeAnnotation,e)},TSIndexedAccessType:function(e){this.print(e.objectType,e),this.token("["),this.print(e.indexType,e),this.token("]")},TSMappedType:function(e){var t=e.readonly,r=e.typeParameter,n=e.optional;this.token("{"),this.space(),t&&(tv(this,t),this.word("readonly"),this.space()),this.token("["),this.word(r.name),this.space(),this.word("in"),this.space(),this.print(r.constraint,r),this.token("]"),n&&(tv(this,n),this.token("?")),this.token(":"),this.space(),this.print(e.typeAnnotation,e),this.space(),this.token("}")},TSLiteralType:function(e){this.print(e.literal,e)},TSExpressionWithTypeArguments:function(e){this.print(e.expression,e),this.print(e.typeParameters,e)},TSInterfaceDeclaration:function(e){var t=e.declare,r=e.id,n=e.typeParameters,a=e.extends,s=e.body;t&&(this.word("declare"),this.space()),this.word("interface"),this.space(),this.print(r,e),this.print(n,e),a&&(this.space(),this.word("extends"),this.space(),this.printList(a,e)),this.space(),this.print(s,e)},TSInterfaceBody:function(e){this.tsPrintTypeLiteralOrInterfaceBody(e.body,e)},TSTypeAliasDeclaration:function(e){var t=e.declare,r=e.id,n=e.typeParameters,a=e.typeAnnotation;t&&(this.word("declare"),this.space()),this.word("type"),this.space(),this.print(r,e),this.print(n,e),this.space(),this.token("="),this.space(),this.print(a,e),this.token(";")},TSAsExpression:function(e){var t=e.expression,r=e.typeAnnotation;this.print(t,e),this.space(),this.word("as"),this.space(),this.print(r,e)},TSTypeAssertion:function(e){var t=e.typeAnnotation,r=e.expression;this.token("<"),this.print(t,e),this.token(">"),this.space(),this.print(r,e)},TSEnumDeclaration:function(e){var t=e.declare,r=e.const,n=e.id,a=e.members;t&&(this.word("declare"),this.space()),r&&(this.word("const"),this.space()),this.word("enum"),this.space(),this.print(n,e),this.space(),this.tsPrintBraced(a,e)},TSEnumMember:function(e){var t=e.id,r=e.initializer;this.print(t,e),r&&(this.space(),this.token("="),this.space(),this.print(r,e)),this.token(",")},TSModuleDeclaration:function(e){var t=e.declare,r=e.id;if(t&&(this.word("declare"),this.space()),e.global||(this.word("Identifier"===r.type?"namespace":"module"),this.space()),this.print(r,e),e.body){for(var n=e.body;"TSModuleDeclaration"===n.type;)this.token("."),this.print(n.id,n),n=n.body;this.space(),this.print(n,e)}else this.token(";")},TSModuleBlock:function(e){this.tsPrintBraced(e.body,e)},TSImportType:function(e){var t=e.argument,r=e.qualifier,n=e.typeParameters;this.word("import"),this.token("("),this.print(t,e),this.token(")"),r&&(this.token("."),this.print(r,e)),n&&this.print(n,e)},TSImportEqualsDeclaration:function(e){var t=e.isExport,r=e.id,n=e.moduleReference;t&&(this.word("export"),this.space()),this.word("import"),this.space(),this.print(r,e),this.space(),this.token("="),this.space(),this.print(n,e),this.token(";")},TSExternalModuleReference:function(e){this.token("require("),this.print(e.expression,e),this.token(")")},TSNonNullExpression:function(e){this.print(e.expression,e),this.token("!")},TSExportAssignment:function(e){this.word("export"),this.space(),this.token("="),this.space(),this.print(e.expression,e),this.token(";")},TSNamespaceExportDeclaration:function(e){this.word("export"),this.space(),this.word("as"),this.space(),this.word("namespace"),this.space(),this.print(e.id,e)},tsPrintSignatureDeclarationBase:function(e){var t=e.typeParameters,r=e.parameters;this.print(t,e),this.token("("),this._parameters(r,e),this.token(")"),this.print(e.typeAnnotation,e)},tsPrintClassMemberModifiers:function(e,t){t&&e.declare&&(this.word("declare"),this.space()),e.accessibility&&(this.word(e.accessibility),this.space()),e.static&&(this.word("static"),this.space()),e.abstract&&(this.word("abstract"),this.space()),t&&e.readonly&&(this.word("readonly"),this.space())}}),nv=/e/i,av=/\.0+$/,sv=/^0[box]/,iv=/^\s*[@#]__PURE__\s*$/,ov=function(){function e(e,t){this.inForStatementInitCounter=0,this._printStack=[],this._indent=0,this._insideAux=!1,this._printedCommentStarts={},this._parenPushNewlineState=null,this._noLineTerminator=!1,this._printAuxAfterOnNextUserNode=!1,this._printedComments=new WeakSet,this._endsWithInteger=!1,this._endsWithWord=!1,this.format=e||{},this._buf=new Gm(t)}var t=e.prototype;return t.generate=function(e){return this.print(e),this._maybeAddAuxComment(),this._buf.get()},t.indent=function(){this.format.compact||this.format.concise||this._indent++},t.dedent=function(){this.format.compact||this.format.concise||this._indent--},t.semicolon=function(e){void 0===e&&(e=!1),this._maybeAddAuxComment(),this._append(";",!e)},t.rightBrace=function(){this.format.minified&&this._buf.removeLastSemicolon(),this.token("}")},t.space=function(e){void 0===e&&(e=!1),this.format.compact||(this._buf.hasContent()&&!this.endsWith(" ")&&!this.endsWith("\n")||e)&&this._space()},t.word=function(e){(this._endsWithWord||this.endsWith("/")&&0===e.indexOf("/"))&&this._space(),this._maybeAddAuxComment(),this._append(e),this._endsWithWord=!0},t.number=function(e){this.word(e),this._endsWithInteger=Lm(+e)&&!sv.test(e)&&!nv.test(e)&&!av.test(e)&&"."!==e[e.length-1]},t.token=function(e){("--"===e&&this.endsWith("!")||"+"===e[0]&&this.endsWith("+")||"-"===e[0]&&this.endsWith("-")||"."===e[0]&&this._endsWithInteger)&&this._space(),this._maybeAddAuxComment(),this._append(e)},t.newline=function(e){if(!this.format.retainLines&&!this.format.compact)if(this.format.concise)this.space();else if(!(this.endsWith("\n\n")||("number"!=typeof e&&(e=1),e=Math.min(2,e),(this.endsWith("{\n")||this.endsWith(":\n"))&&e--,e<=0)))for(var t=0;t<e;t++)this._newline()},t.endsWith=function(e){return this._buf.endsWith(e)},t.removeTrailingNewline=function(){this._buf.removeTrailingNewline()},t.exactSource=function(e,t){this._catchUp("start",e),this._buf.exactSource(e,t)},t.source=function(e,t){this._catchUp(e,t),this._buf.source(e,t)},t.withSource=function(e,t,r){this._catchUp(e,t),this._buf.withSource(e,t,r)},t._space=function(){this._append(" ",!0)},t._newline=function(){this._append("\n",!0)},t._append=function(e,t){void 0===t&&(t=!1),this._maybeAddParen(e),this._maybeIndent(e),t?this._buf.queue(e):this._buf.append(e),this._endsWithWord=!1,this._endsWithInteger=!1},t._maybeIndent=function(e){this._indent&&this.endsWith("\n")&&"\n"!==e[0]&&this._buf.queue(this._getIndent())},t._maybeAddParen=function(e){var t=this._parenPushNewlineState;if(t){var r;for(this._parenPushNewlineState=null,r=0;r<e.length&&" "===e[r];r++);if(r!==e.length){var n=e[r];if("\n"!==n){if("/"!==n)return;if(r+1===e.length)return;var a=e[r+1];if("/"!==a&&"*"!==a)return}this.token("("),this.indent(),t.printed=!0}}},t._catchUp=function(e,t){if(this.format.retainLines){var r=t?t[e]:null;if(r&&null!==r.line)for(var n=r.line-this._buf.getCurrentLine(),a=0;a<n;a++)this._newline()}},t._getIndent=function(){return th(this.format.indent.style,this._indent)},t.startTerminatorless=function(e){return void 0===e&&(e=!1),e?(this._noLineTerminator=!0,null):this._parenPushNewlineState={printed:!1}},t.endTerminatorless=function(e){this._noLineTerminator=!1,e&&e.printed&&(this.dedent(),this.newline(),this.token(")"))},t.print=function(e,t){var r=this;if(e){var n=this.format.concise;e._compact&&(this.format.concise=!0);var a=this[e.type];if(!a)throw new ReferenceError("unknown node of type "+JSON.stringify(e.type)+" with constructor "+JSON.stringify(e&&e.constructor.name));this._printStack.push(e);var s=this._insideAux;this._insideAux=!e.loc,this._maybeAddAuxComment(this._insideAux&&!s);var i=uy(e,t,this._printStack);this.format.retainFunctionParens&&"FunctionExpression"===e.type&&e.extra&&e.extra.parenthesized&&(i=!0),i&&this.token("("),this._printLeadingComments(e);var o=B(e)||b(e)?null:e.loc;this.withSource("start",o,(function(){a.call(r,e,t)})),this._printTrailingComments(e),i&&this.token(")"),this._printStack.pop(),this.format.concise=n,this._insideAux=s}},t._maybeAddAuxComment=function(e){e&&this._printAuxBeforeComment(),this._insideAux||this._printAuxAfterComment()},t._printAuxBeforeComment=function(){if(!this._printAuxAfterOnNextUserNode){this._printAuxAfterOnNextUserNode=!0;var e=this.format.auxiliaryCommentBefore;e&&this._printComment({type:"CommentBlock",value:e})}},t._printAuxAfterComment=function(){if(this._printAuxAfterOnNextUserNode){this._printAuxAfterOnNextUserNode=!1;var e=this.format.auxiliaryCommentAfter;e&&this._printComment({type:"CommentBlock",value:e})}},t.getPossibleRaw=function(e){var t=e.extra;if(t&&null!=t.raw&&null!=t.rawValue&&e.value===t.rawValue)return t.raw},t.printJoin=function(e,t,r){if(void 0===r&&(r={}),e&&e.length){r.indent&&this.indent();for(var n={addNewlines:r.addNewlines},a=0;a<e.length;a++){var s=e[a];s&&(r.statement&&this._printNewline(!0,s,t,n),this.print(s,t),r.iterator&&r.iterator(s,a),r.separator&&a<e.length-1&&r.separator.call(this),r.statement&&this._printNewline(!1,s,t,n))}r.indent&&this.dedent()}},t.printAndIndentOnComments=function(e,t){var r=e.leadingComments&&e.leadingComments.length>0;r&&this.indent(),this.print(e,t),r&&this.dedent()},t.printBlock=function(e){var t=e.body;g(t)||this.space(),this.print(t,e)},t._printTrailingComments=function(e){this._printComments(this._getComments(!1,e))},t._printLeadingComments=function(e){this._printComments(this._getComments(!0,e),!0)},t.printInnerComments=function(e,t){void 0===t&&(t=!0),e.innerComments&&e.innerComments.length&&(t&&this.indent(),this._printComments(e.innerComments),t&&this.dedent())},t.printSequence=function(e,t,r){return void 0===r&&(r={}),r.statement=!0,this.printJoin(e,t,r)},t.printList=function(e,t,r){return void 0===r&&(r={}),null==r.separator&&(r.separator=uv),this.printJoin(e,t,r)},t._printNewline=function(e,t,r,n){if(!this.format.retainLines&&!this.format.compact)if(this.format.concise)this.space();else{var a=0;if(this._buf.hasContent())e||a++,n.addNewlines&&(a+=n.addNewlines(e,t)||0),(e?iy:oy)(t,r)&&a++;this.newline(a)}},t._getComments=function(e,t){return t&&(e?t.leadingComments:t.trailingComments)||[]},t._printComment=function(e,t){var r=this;if(this.format.shouldPrintComment(e.value)&&!e.ignore&&!this._printedComments.has(e)){if(this._printedComments.add(e),null!=e.start){if(this._printedCommentStarts[e.start])return;this._printedCommentStarts[e.start]=!0}var n="CommentBlock"===e.type,a=n&&!t&&!this._noLineTerminator;a&&this._buf.hasContent()&&this.newline(1),this.endsWith("[")||this.endsWith("{")||this.space();var s=n||this._noLineTerminator?"/*"+e.value+"*/":"//"+e.value+"\n";if(n&&this.format.indent.adjustMultilineComment){var i=e.loc&&e.loc.start.column;if(i){var o=new RegExp("\\n\\s{1,"+i+"}","g");s=s.replace(o,"\n")}var u=Math.max(this._getIndent().length,this._buf.getCurrentColumn());s=s.replace(/\n(?!$)/g,"\n"+th(" ",u))}this.endsWith("/")&&this._space(),this.withSource("start",e.loc,(function(){r._append(s)})),a&&this.newline(1)}},t._printComments=function(e,t){if(e&&e.length)if(t&&1===e.length&&iv.test(e[0].value))this._printComment(e[0],this._buf.hasContent()&&!this.endsWith("\n"));else{var r=e,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s;this._printComment(i)}}},e}();function uv(){this.token(","),this.space()}Object.assign(ov.prototype,rv);var cv=function(e){function t(t,r,n){var a;void 0===r&&(r={});var s=function(e,t){var r={auxiliaryCommentBefore:t.auxiliaryCommentBefore,auxiliaryCommentAfter:t.auxiliaryCommentAfter,shouldPrintComment:t.shouldPrintComment,retainLines:t.retainLines,retainFunctionParens:t.retainFunctionParens,comments:null==t.comments||t.comments,compact:t.compact,minified:t.minified,concise:t.concise,jsonCompatibleStrings:t.jsonCompatibleStrings,indent:{adjustMultilineComment:!0,style:" ",base:0},decoratorsBeforeExport:!!t.decoratorsBeforeExport,jsescOption:Object.assign({quotes:"double",wrap:!0},t.jsescOption)};r.minified?(r.compact=!0,r.shouldPrintComment=r.shouldPrintComment||function(){return r.comments}):r.shouldPrintComment=r.shouldPrintComment||function(e){return r.comments||e.indexOf("@license")>=0||e.indexOf("@preserve")>=0};"auto"===r.compact&&(r.compact=e.length>5e5,r.compact&&console.error("[BABEL] Note: The code generator has deoptimised the styling of "+t.filename+" as it exceeds the max of 500KB."));r.compact&&(r.indent.adjustMultilineComment=!1);return r}(n,r),i=r.sourceMaps?new Mm(r,n):null;return(a=e.call(this,s,i)||this).ast=t,a}return a(t,e),t.prototype.generate=function(){return e.prototype.generate.call(this,this.ast)},t}(ov);function lv(e,t,r){return new cv(e,t,r).generate()}var pv=Object.freeze({__proto__:null,findParent:function(e){for(var t=this;t=t.parentPath;)if(e(t))return t;return null},find:function(e){var t=this;do{if(e(t))return t}while(t=t.parentPath);return null},getFunctionParent:function(){return this.findParent((function(e){return e.isFunction()}))},getStatementParent:function(){var e=this;do{if(!e.parentPath||Array.isArray(e.container)&&e.isStatement())break;e=e.parentPath}while(e);if(e&&(e.isProgram()||e.isFile()))throw new Error("File/Program node, we can't possibly find a statement parent to this");return e},getEarliestCommonAncestorFrom:function(e){return this.getDeepestCommonAncestorFrom(e,(function(e,t,r){for(var n,a=ks[e.type],s=0,i=r;s<i.length;s++){var o=i[s][t+1];if(n)if(o.listKey&&n.listKey===o.listKey&&o.key<n.key)n=o;else a.indexOf(n.parentKey)>a.indexOf(o.parentKey)&&(n=o);else n=o}return n}))},getDeepestCommonAncestorFrom:function(e,t){var r=this;if(!e.length)return this;if(1===e.length)return e[0];var n,a,s=1/0,i=e.map((function(e){var t=[];do{t.unshift(e)}while((e=e.parentPath)&&e!==r);return t.length<s&&(s=t.length),t})),o=i[0];e:for(var u=0;u<s;u++){for(var c=o[u],l=0,p=i;l<p.length;l++){if(p[l][u]!==c)break e}n=u,a=c}if(a)return t?t(a,n,i):a;throw new Error("Couldn't find intersection")},getAncestry:function(){var e=this,t=[];do{t.push(e)}while(e=e.parentPath);return t},isAncestor:function(e){return e.isDescendant(this)},isDescendant:function(e){return!!this.findParent((function(t){return t===e}))},inType:function(){for(var e=this;e;){for(var t=0,r=arguments;t<r.length;t++){var n=r[t];if(e.node.type===n)return!0}e=e.parentPath}return!1}});function dv(e,t,r){var n=e.constantViolations.slice();return n.unshift(e.path),n.filter((function(e){var n=(e=e.resolve())._guessExecutionStatusRelativeTo(t);return r&&"unknown"===n&&r.push(e),"before"===n}))}function fv(e,t){var r,n,a,s=t.node.operator,i=t.get("right").resolve(),o=t.get("left").resolve();if(o.isIdentifier({name:e})?r=i:i.isIdentifier({name:e})&&(r=o),r)return"==="===s?r.getTypeAnnotation():fs.indexOf(s)>=0?Pu():void 0;if(("==="===s||"=="===s)&&(o.isUnaryExpression({operator:"typeof"})?(n=o,a=i):i.isUnaryExpression({operator:"typeof"})&&(n=i,a=o),n&&n.get("argument").isIdentifier({name:e})&&(a=a.resolve()).isLiteral())){var u=a.node.value;if("string"==typeof u)return sp(u)}}function hv(e){return e.typeAnnotation}function mv(){return bu(Qi("Array"))}function yv(){return mv()}function gv(){return bu(Qi("Function"))}hv.validParent=!0,yv.validParent=!0;var vv=St("Array.from"),bv=St("Object.keys"),xv=St("Object.values"),Ev=St("Object.entries");function Av(e){if((e=e.resolve()).isFunction()){if(e.is("async"))return e.is("generator")?bu(Qi("AsyncIterator")):bu(Qi("Promise"));if(e.node.returnType)return e.node.returnType}}var wv=Object.freeze({__proto__:null,VariableDeclarator:function(){if(this.get("id").isIdentifier()){var e=this.get("init"),t=e.getTypeAnnotation();return t&&"AnyTypeAnnotation"===t.type&&e.isCallExpression()&&e.get("callee").isIdentifier({name:"Array"})&&!e.scope.hasBinding("Array",!0)&&(t=mv()),t}},TypeCastExpression:hv,NewExpression:function(e){if(this.get("callee").isIdentifier())return bu(e.callee)},TemplateLiteral:function(){return Lu()},UnaryExpression:function(e){var t=e.operator;return"void"===t?$u():Es.indexOf(t)>=0?Pu():As.indexOf(t)>=0?Lu():xs.indexOf(t)>=0?tu():void 0},BinaryExpression:function(e){var t=e.operator;if(gs.indexOf(t)>=0)return Pu();if(ys.indexOf(t)>=0)return tu();if("+"===t){var r=this.get("right"),n=this.get("left");return n.isBaseType("number")&&r.isBaseType("number")?Pu():n.isBaseType("string")||r.isBaseType("string")?Lu():Yu([Lu(),Pu()])}},LogicalExpression:function(){return op([this.get("left").getTypeAnnotation(),this.get("right").getTypeAnnotation()])},ConditionalExpression:function(){return op([this.get("consequent").getTypeAnnotation(),this.get("alternate").getTypeAnnotation()])},SequenceExpression:function(){return this.get("expressions").pop().getTypeAnnotation()},ParenthesizedExpression:function(){return this.get("expression").getTypeAnnotation()},AssignmentExpression:function(){return this.get("right").getTypeAnnotation()},UpdateExpression:function(e){var t=e.operator;if("++"===t||"--"===t)return Pu()},StringLiteral:function(){return Lu()},NumericLiteral:function(){return Pu()},BooleanLiteral:function(){return tu()},NullLiteral:function(){return nu()},RegExpLiteral:function(){return bu(Qi("RegExp"))},ObjectExpression:function(){return bu(Qi("Object"))},ArrayExpression:mv,RestElement:yv,FunctionExpression:gv,ArrowFunctionExpression:gv,FunctionDeclaration:gv,ClassExpression:gv,ClassDeclaration:gv,CallExpression:function(){var e=this.node.callee;return bv(e)?eu(Lu()):vv(e)||xv(e)?eu(Zo()):Ev(e)?eu(Gu([Lu(),Zo()])):Av(this.get("callee"))},TaggedTemplateExpression:function(){return Av(this.get("tag"))},Identifier:function(e){if(this.isReferenced()){var t=this.scope.getBinding(e.name);return t?t.identifier.typeAnnotation?t.identifier.typeAnnotation:function(e,t,r){var n=[],a=[],s=dv(e,t,a),i=function e(t,r,n){var a=function(e,t,r){var n;for(;n=t.parentPath;){if(n.isIfStatement()||n.isConditionalExpression()){if("test"===t.key)return;return n}if(n.isFunction()&&n.parentPath.scope.getBinding(r)!==e)return;t=n}}(t,r,n);if(!a)return;for(var s=[a.get("test")],i=[],o=0;o<s.length;o++){var u=s[o];if(u.isLogicalExpression())"&&"===u.node.operator&&(s.push(u.get("left")),s.push(u.get("right")));else if(u.isBinaryExpression()){var c=fv(n,u);c&&i.push(c)}}if(i.length)return{typeAnnotation:op(i),ifStatement:a};return e(a,n)}(e,t,r);if(i){var o=dv(e,i.ifStatement);s=s.filter((function(e){return o.indexOf(e)<0})),n.push(i.typeAnnotation)}if(s.length){s=s.concat(a);for(var u=0,c=s;u<c.length;u++){var l=c[u];n.push(l.getTypeAnnotation())}}if(n.length)return op(n)}(t,this,e.name):"undefined"===e.name?$u():"NaN"===e.name||"Infinity"===e.name?Pu():void e.name}}});function Sv(e,t,r){if("string"===e)return De(t);if("number"===e)return Se(t);if("boolean"===e)return ge(t);if("any"===e)return me(t);if("mixed"===e)return Ee(t);if("empty"===e)return Ae(t);if("void"===e)return Pe(t);if(r)return!1;throw new Error("Unknown base type "+e)}var Dv=Object.freeze({__proto__:null,getTypeAnnotation:function(){if(this.typeAnnotation)return this.typeAnnotation;var e=this._getTypeAnnotation()||Zo();return Ce(e)&&(e=e.typeAnnotation),this.typeAnnotation=e},_getTypeAnnotation:function(){var e=this.node;if(e){if(e.typeAnnotation)return e.typeAnnotation;var t=wv[e.type];return t?t.call(this,e):(t=wv[this.parentPath.type])&&t.validParent?this.parentPath.getTypeAnnotation():void 0}if("init"===this.key&&this.parentPath.isVariableDeclarator()){var r=this.parentPath.parentPath,n=r.parentPath;return"left"===r.key&&n.isForInStatement()?Lu():"left"===r.key&&n.isForOfStatement()?Zo():$u()}},isBaseType:function(e,t){return Sv(e,this.getTypeAnnotation(),t)},couldBeBaseType:function(e){var t=this.getTypeAnnotation();if(me(t))return!0;if(je(t)){for(var r=0,n=t.types;r<n.length;r++){var a=n[r];if(me(a)||Sv(e,a,!0))return!0}return!1}return Sv(e,t,!0)},baseTypeStrictlyMatches:function(e){var t=this.getTypeAnnotation();if(e=e.getTypeAnnotation(),!me(t)&&At(t))return e.type===t.type},isGenericType:function(e){var t=this.getTypeAnnotation();return be(t)&&S(t.id,{name:e})}}),Cv=Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=/((['"])(?:(?!\2|\\).|\\(?:\r\n|[\s\S]))*(\2)?|`(?:[^`\\$]|\\[\s\S]|\$(?!\{)|\$\{(?:[^{}]|\{[^}]*\}?)*\}?)*(`)?)|(\/\/.*)|(\/\*(?:[^*]|\*(?!\/))*(\*\/)?)|(\/(?!\*)(?:\[(?:(?![\]\\]).|\\.)*\]|(?![\/\]\\]).|\\.)+\/(?:(?!\s*(?:\b|[\u0080-\uFFFF$\\'"~({]|[+\-!](?!=)|\.?\d))|[gmiyus]{1,6}\b(?![\u0080-\uFFFF$\\]|\s*(?:[+\-*%&|^<>!=?({]|\/(?![\/*])))))|(0[xX][\da-fA-F]+|0[oO][0-7]+|0[bB][01]+|(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?)|((?!\d)(?:(?!\s)[$\w\u0080-\uFFFF]|\\u[\da-fA-F]{4}|\\u\{[\da-fA-F]+\})+)|(--|\+\+|&&|\|\||=>|\.{3}|(?:[+\-\/%&|^]|\*{1,2}|<{1,2}|>{1,3}|!=?|={1,2})=?|[?~.,:;[\](){}])|(\s+)|(^$|[\s\S])/g,t.matchToToken=function(e){var t={type:"invalid",value:e[0],closed:void 0};return e[1]?(t.type="string",t.closed=!(!e[3]&&!e[4])):e[5]?t.type="comment":e[6]?(t.type="comment",t.closed=!!e[7]):e[8]?t.type="regex":e[9]?t.type="number":e[10]?t.type="name":e[11]?t.type="punctuator":e[12]&&(t.type="whitespace"),t}})),Tv=Gt(Cv),jv=Cv.matchToToken,Pv=/[|\\{}()[\]^$+*?.]/g,kv=function(e){if("string"!=typeof e)throw new TypeError("Expected a string");return e.replace(Pv,"\\$&")},Fv={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},_v=Vt((function(e){var t={};for(var r in Fv)Fv.hasOwnProperty(r)&&(t[Fv[r]]=r);var n=e.exports={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};for(var a in n)if(n.hasOwnProperty(a)){if(!("channels"in n[a]))throw new Error("missing channels property: "+a);if(!("labels"in n[a]))throw new Error("missing channel labels property: "+a);if(n[a].labels.length!==n[a].channels)throw new Error("channel and label counts mismatch: "+a);var s=n[a].channels,i=n[a].labels;delete n[a].channels,delete n[a].labels,Object.defineProperty(n[a],"channels",{value:s}),Object.defineProperty(n[a],"labels",{value:i})}function o(e,t){return Math.pow(e[0]-t[0],2)+Math.pow(e[1]-t[1],2)+Math.pow(e[2]-t[2],2)}n.rgb.hsl=function(e){var t,r,n=e[0]/255,a=e[1]/255,s=e[2]/255,i=Math.min(n,a,s),o=Math.max(n,a,s),u=o-i;return o===i?t=0:n===o?t=(a-s)/u:a===o?t=2+(s-n)/u:s===o&&(t=4+(n-a)/u),(t=Math.min(60*t,360))<0&&(t+=360),r=(i+o)/2,[t,100*(o===i?0:r<=.5?u/(o+i):u/(2-o-i)),100*r]},n.rgb.hsv=function(e){var t,r,n,a,s,i=e[0]/255,o=e[1]/255,u=e[2]/255,c=Math.max(i,o,u),l=c-Math.min(i,o,u),p=function(e){return(c-e)/6/l+.5};return 0===l?a=s=0:(s=l/c,t=p(i),r=p(o),n=p(u),i===c?a=n-r:o===c?a=1/3+t-n:u===c&&(a=2/3+r-t),a<0?a+=1:a>1&&(a-=1)),[360*a,100*s,100*c]},n.rgb.hwb=function(e){var t=e[0],r=e[1],a=e[2];return[n.rgb.hsl(e)[0],100*(1/255*Math.min(t,Math.min(r,a))),100*(a=1-1/255*Math.max(t,Math.max(r,a)))]},n.rgb.cmyk=function(e){var t,r=e[0]/255,n=e[1]/255,a=e[2]/255;return[100*((1-r-(t=Math.min(1-r,1-n,1-a)))/(1-t)||0),100*((1-n-t)/(1-t)||0),100*((1-a-t)/(1-t)||0),100*t]},n.rgb.keyword=function(e){var r=t[e];if(r)return r;var n,a=1/0;for(var s in Fv)if(Fv.hasOwnProperty(s)){var i=o(e,Fv[s]);i<a&&(a=i,n=s)}return n},n.keyword.rgb=function(e){return Fv[e]},n.rgb.xyz=function(e){var t=e[0]/255,r=e[1]/255,n=e[2]/255;return[100*(.4124*(t=t>.04045?Math.pow((t+.055)/1.055,2.4):t/12.92)+.3576*(r=r>.04045?Math.pow((r+.055)/1.055,2.4):r/12.92)+.1805*(n=n>.04045?Math.pow((n+.055)/1.055,2.4):n/12.92)),100*(.2126*t+.7152*r+.0722*n),100*(.0193*t+.1192*r+.9505*n)]},n.rgb.lab=function(e){var t=n.rgb.xyz(e),r=t[0],a=t[1],s=t[2];return a/=100,s/=108.883,r=(r/=95.047)>.008856?Math.pow(r,1/3):7.787*r+16/116,[116*(a=a>.008856?Math.pow(a,1/3):7.787*a+16/116)-16,500*(r-a),200*(a-(s=s>.008856?Math.pow(s,1/3):7.787*s+16/116))]},n.hsl.rgb=function(e){var t,r,n,a,s,i=e[0]/360,o=e[1]/100,u=e[2]/100;if(0===o)return[s=255*u,s,s];t=2*u-(r=u<.5?u*(1+o):u+o-u*o),a=[0,0,0];for(var c=0;c<3;c++)(n=i+1/3*-(c-1))<0&&n++,n>1&&n--,s=6*n<1?t+6*(r-t)*n:2*n<1?r:3*n<2?t+(r-t)*(2/3-n)*6:t,a[c]=255*s;return a},n.hsl.hsv=function(e){var t=e[0],r=e[1]/100,n=e[2]/100,a=r,s=Math.max(n,.01);return r*=(n*=2)<=1?n:2-n,a*=s<=1?s:2-s,[t,100*(0===n?2*a/(s+a):2*r/(n+r)),100*((n+r)/2)]},n.hsv.rgb=function(e){var t=e[0]/60,r=e[1]/100,n=e[2]/100,a=Math.floor(t)%6,s=t-Math.floor(t),i=255*n*(1-r),o=255*n*(1-r*s),u=255*n*(1-r*(1-s));switch(n*=255,a){case 0:return[n,u,i];case 1:return[o,n,i];case 2:return[i,n,u];case 3:return[i,o,n];case 4:return[u,i,n];case 5:return[n,i,o]}},n.hsv.hsl=function(e){var t,r,n,a=e[0],s=e[1]/100,i=e[2]/100,o=Math.max(i,.01);return n=(2-s)*i,r=s*o,[a,100*(r=(r/=(t=(2-s)*o)<=1?t:2-t)||0),100*(n/=2)]},n.hwb.rgb=function(e){var t,r,n,a,s,i,o,u=e[0]/360,c=e[1]/100,l=e[2]/100,p=c+l;switch(p>1&&(c/=p,l/=p),n=6*u-(t=Math.floor(6*u)),0!=(1&t)&&(n=1-n),a=c+n*((r=1-l)-c),t){default:case 6:case 0:s=r,i=a,o=c;break;case 1:s=a,i=r,o=c;break;case 2:s=c,i=r,o=a;break;case 3:s=c,i=a,o=r;break;case 4:s=a,i=c,o=r;break;case 5:s=r,i=c,o=a}return[255*s,255*i,255*o]},n.cmyk.rgb=function(e){var t=e[0]/100,r=e[1]/100,n=e[2]/100,a=e[3]/100;return[255*(1-Math.min(1,t*(1-a)+a)),255*(1-Math.min(1,r*(1-a)+a)),255*(1-Math.min(1,n*(1-a)+a))]},n.xyz.rgb=function(e){var t,r,n,a=e[0]/100,s=e[1]/100,i=e[2]/100;return r=-.9689*a+1.8758*s+.0415*i,n=.0557*a+-.204*s+1.057*i,t=(t=3.2406*a+-1.5372*s+-.4986*i)>.0031308?1.055*Math.pow(t,1/2.4)-.055:12.92*t,r=r>.0031308?1.055*Math.pow(r,1/2.4)-.055:12.92*r,n=n>.0031308?1.055*Math.pow(n,1/2.4)-.055:12.92*n,[255*(t=Math.min(Math.max(0,t),1)),255*(r=Math.min(Math.max(0,r),1)),255*(n=Math.min(Math.max(0,n),1))]},n.xyz.lab=function(e){var t=e[0],r=e[1],n=e[2];return r/=100,n/=108.883,t=(t/=95.047)>.008856?Math.pow(t,1/3):7.787*t+16/116,[116*(r=r>.008856?Math.pow(r,1/3):7.787*r+16/116)-16,500*(t-r),200*(r-(n=n>.008856?Math.pow(n,1/3):7.787*n+16/116))]},n.lab.xyz=function(e){var t,r,n,a=e[0];t=e[1]/500+(r=(a+16)/116),n=r-e[2]/200;var s=Math.pow(r,3),i=Math.pow(t,3),o=Math.pow(n,3);return r=s>.008856?s:(r-16/116)/7.787,t=i>.008856?i:(t-16/116)/7.787,n=o>.008856?o:(n-16/116)/7.787,[t*=95.047,r*=100,n*=108.883]},n.lab.lch=function(e){var t,r=e[0],n=e[1],a=e[2];return(t=360*Math.atan2(a,n)/2/Math.PI)<0&&(t+=360),[r,Math.sqrt(n*n+a*a),t]},n.lch.lab=function(e){var t,r=e[0],n=e[1];return t=e[2]/360*2*Math.PI,[r,n*Math.cos(t),n*Math.sin(t)]},n.rgb.ansi16=function(e){var t=e[0],r=e[1],a=e[2],s=1 in arguments?arguments[1]:n.rgb.hsv(e)[2];if(0===(s=Math.round(s/50)))return 30;var i=30+(Math.round(a/255)<<2|Math.round(r/255)<<1|Math.round(t/255));return 2===s&&(i+=60),i},n.hsv.ansi16=function(e){return n.rgb.ansi16(n.hsv.rgb(e),e[2])},n.rgb.ansi256=function(e){var t=e[0],r=e[1],n=e[2];return t===r&&r===n?t<8?16:t>248?231:Math.round((t-8)/247*24)+232:16+36*Math.round(t/255*5)+6*Math.round(r/255*5)+Math.round(n/255*5)},n.ansi16.rgb=function(e){var t=e%10;if(0===t||7===t)return e>50&&(t+=3.5),[t=t/10.5*255,t,t];var r=.5*(1+~~(e>50));return[(1&t)*r*255,(t>>1&1)*r*255,(t>>2&1)*r*255]},n.ansi256.rgb=function(e){if(e>=232){var t=10*(e-232)+8;return[t,t,t]}var r;return e-=16,[Math.floor(e/36)/5*255,Math.floor((r=e%36)/6)/5*255,r%6/5*255]},n.rgb.hex=function(e){var t=(((255&Math.round(e[0]))<<16)+((255&Math.round(e[1]))<<8)+(255&Math.round(e[2]))).toString(16).toUpperCase();return"000000".substring(t.length)+t},n.hex.rgb=function(e){var t=e.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!t)return[0,0,0];var r=t[0];3===t[0].length&&(r=r.split("").map((function(e){return e+e})).join(""));var n=parseInt(r,16);return[n>>16&255,n>>8&255,255&n]},n.rgb.hcg=function(e){var t,r=e[0]/255,n=e[1]/255,a=e[2]/255,s=Math.max(Math.max(r,n),a),i=Math.min(Math.min(r,n),a),o=s-i;return t=o<=0?0:s===r?(n-a)/o%6:s===n?2+(a-r)/o:4+(r-n)/o+4,t/=6,[360*(t%=1),100*o,100*(o<1?i/(1-o):0)]},n.hsl.hcg=function(e){var t=e[1]/100,r=e[2]/100,n=1,a=0;return(n=r<.5?2*t*r:2*t*(1-r))<1&&(a=(r-.5*n)/(1-n)),[e[0],100*n,100*a]},n.hsv.hcg=function(e){var t=e[1]/100,r=e[2]/100,n=t*r,a=0;return n<1&&(a=(r-n)/(1-n)),[e[0],100*n,100*a]},n.hcg.rgb=function(e){var t=e[0]/360,r=e[1]/100,n=e[2]/100;if(0===r)return[255*n,255*n,255*n];var a,s=[0,0,0],i=t%1*6,o=i%1,u=1-o;switch(Math.floor(i)){case 0:s[0]=1,s[1]=o,s[2]=0;break;case 1:s[0]=u,s[1]=1,s[2]=0;break;case 2:s[0]=0,s[1]=1,s[2]=o;break;case 3:s[0]=0,s[1]=u,s[2]=1;break;case 4:s[0]=o,s[1]=0,s[2]=1;break;default:s[0]=1,s[1]=0,s[2]=u}return a=(1-r)*n,[255*(r*s[0]+a),255*(r*s[1]+a),255*(r*s[2]+a)]},n.hcg.hsv=function(e){var t=e[1]/100,r=t+e[2]/100*(1-t),n=0;return r>0&&(n=t/r),[e[0],100*n,100*r]},n.hcg.hsl=function(e){var t=e[1]/100,r=e[2]/100*(1-t)+.5*t,n=0;return r>0&&r<.5?n=t/(2*r):r>=.5&&r<1&&(n=t/(2*(1-r))),[e[0],100*n,100*r]},n.hcg.hwb=function(e){var t=e[1]/100,r=t+e[2]/100*(1-t);return[e[0],100*(r-t),100*(1-r)]},n.hwb.hcg=function(e){var t=e[1]/100,r=1-e[2]/100,n=r-t,a=0;return n<1&&(a=(r-n)/(1-n)),[e[0],100*n,100*a]},n.apple.rgb=function(e){return[e[0]/65535*255,e[1]/65535*255,e[2]/65535*255]},n.rgb.apple=function(e){return[e[0]/255*65535,e[1]/255*65535,e[2]/255*65535]},n.gray.rgb=function(e){return[e[0]/100*255,e[0]/100*255,e[0]/100*255]},n.gray.hsl=n.gray.hsv=function(e){return[0,0,e[0]]},n.gray.hwb=function(e){return[0,100,e[0]]},n.gray.cmyk=function(e){return[0,0,0,e[0]]},n.gray.lab=function(e){return[e[0],0,0]},n.gray.hex=function(e){var t=255&Math.round(e[0]/100*255),r=((t<<16)+(t<<8)+t).toString(16).toUpperCase();return"000000".substring(r.length)+r},n.rgb.gray=function(e){return[(e[0]+e[1]+e[2])/3/255*100]}}));_v.rgb,_v.hsl,_v.hsv,_v.hwb,_v.cmyk,_v.xyz,_v.lab,_v.lch,_v.hex,_v.keyword,_v.ansi16,_v.ansi256,_v.hcg,_v.apple,_v.gray;function Iv(e){var t=function(){for(var e={},t=Object.keys(_v),r=t.length,n=0;n<r;n++)e[t[n]]={distance:-1,parent:null};return e}(),r=[e];for(t[e].distance=0;r.length;)for(var n=r.pop(),a=Object.keys(_v[n]),s=a.length,i=0;i<s;i++){var o=a[i],u=t[o];-1===u.distance&&(u.distance=t[n].distance+1,u.parent=n,r.unshift(o))}return t}function Bv(e,t){return function(r){return t(e(r))}}function Ov(e,t){for(var r=[t[e].parent,e],n=_v[t[e].parent][e],a=t[e].parent;t[a].parent;)r.unshift(t[a].parent),n=Bv(_v[t[a].parent][a],n),a=t[a].parent;return n.conversion=r,n}var Nv={};Object.keys(_v).forEach((function(e){Nv[e]={},Object.defineProperty(Nv[e],"channels",{value:_v[e].channels}),Object.defineProperty(Nv[e],"labels",{value:_v[e].labels});var t=function(e){for(var t=Iv(e),r={},n=Object.keys(t),a=n.length,s=0;s<a;s++){var i=n[s];null!==t[i].parent&&(r[i]=Ov(i,t))}return r}(e);Object.keys(t).forEach((function(r){var n=t[r];Nv[e][r]=function(e){var t=function(t){if(null==t)return t;arguments.length>1&&(t=Array.prototype.slice.call(arguments));var r=e(t);if("object"==typeof r)for(var n=r.length,a=0;a<n;a++)r[a]=Math.round(r[a]);return r};return"conversion"in e&&(t.conversion=e.conversion),t}(n),Nv[e][r].raw=function(e){var t=function(t){return null==t?t:(arguments.length>1&&(t=Array.prototype.slice.call(arguments)),e(t))};return"conversion"in e&&(t.conversion=e.conversion),t}(n)}))}));var Rv=Nv,Mv=Vt((function(e){var t=function(e,t){return function(){var r=e.apply(Rv,arguments);return"["+(r+t)+"m"}},r=function(e,t){return function(){var r=e.apply(Rv,arguments);return"["+(38+t)+";5;"+r+"m"}},n=function(e,t){return function(){var r=e.apply(Rv,arguments);return"["+(38+t)+";2;"+r[0]+";"+r[1]+";"+r[2]+"m"}};Object.defineProperty(e,"exports",{enumerable:!0,get:function(){var e=new Map,a={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],gray:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};a.color.grey=a.color.gray;for(var s=0,i=Object.keys(a);s<i.length;s++){for(var o=i[s],u=a[o],c=0,l=Object.keys(u);c<l.length;c++){var p=l[c],d=u[p];a[p]={open:"["+d[0]+"m",close:"["+d[1]+"m"},u[p]=a[p],e.set(d[0],d[1])}Object.defineProperty(a,o,{value:u,enumerable:!1}),Object.defineProperty(a,"codes",{value:e,enumerable:!1})}var f=function(e){return e},h=function(e,t,r){return[e,t,r]};a.color.close="",a.bgColor.close="",a.color.ansi={ansi:t(f,0)},a.color.ansi256={ansi256:r(f,0)},a.color.ansi16m={rgb:n(h,0)},a.bgColor.ansi={ansi:t(f,10)},a.bgColor.ansi256={ansi256:r(f,10)},a.bgColor.ansi16m={rgb:n(h,10)};for(var m=0,y=Object.keys(Rv);m<y.length;m++){var g=y[m];if("object"==typeof Rv[g]){var v=Rv[g];"ansi16"===g&&(g="ansi"),"ansi16"in v&&(a.color.ansi[g]=t(v.ansi16,0),a.bgColor.ansi[g]=t(v.ansi16,10)),"ansi256"in v&&(a.color.ansi256[g]=r(v.ansi256,0),a.bgColor.ansi256[g]=r(v.ansi256,10)),"rgb"in v&&(a.color.ansi16m[g]=n(v.rgb,0),a.bgColor.ansi16m[g]=n(v.rgb,10))}}return a}})})),Lv=!1,Uv=/(?:\\(u[a-f\d]{4}|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,Gv=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,Vv=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,Wv=/\\(u[a-f\d]{4}|x[a-f\d]{2}|.)|([^\\])/gi,Hv=new Map([["n","\n"],["r","\r"],["t","\t"],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e",""],["a",""]]);function qv(e){return"u"===e[0]&&5===e.length||"x"===e[0]&&3===e.length?String.fromCharCode(parseInt(e.slice(1),16)):Hv.get(e)||e}function Kv(e,t){var r,n=[],a=t.trim().split(/\s*,\s*/g),s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o;if(isNaN(u)){if(!(r=u.match(Vv)))throw new Error("Invalid Chalk template style argument: "+u+" (in style '"+e+"')");n.push(r[2].replace(Wv,(function(e,t,r){return t?qv(t):r})))}else n.push(Number(u))}return n}function zv(e){Gv.lastIndex=0;for(var t,r=[];null!==(t=Gv.exec(e));){var n=t[1];if(t[2]){var a=Kv(n,t[2]);r.push([n].concat(a))}else r.push([n])}return r}function Xv(e,t){var r={},n=t,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i,u=o.styles,c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}var d=p;r[d[0]]=o.inverse?null:d.slice(1)}}for(var f=e,h=0,m=Object.keys(r);h<m.length;h++){var y=m[h];if(Array.isArray(r[y])){if(!(y in f))throw new Error("Unknown Chalk style: "+y);f=r[y].length>0?f[y].apply(f,r[y]):f[y]}}return f}var Yv=function(e,t){var r=[],n=[],a=[];if(t.replace(Uv,(function(t,s,i,o,u,c){if(s)a.push(qv(s));else if(o){var l=a.join("");a=[],n.push(0===r.length?l:Xv(e,r)(l)),r.push({inverse:i,styles:zv(o)})}else if(u){if(0===r.length)throw new Error("Found extraneous } in Chalk template literal");n.push(Xv(e,r)(a.join(""))),a=[],r.pop()}else a.push(c)})),n.push(a.join("")),r.length>0){var s="Chalk template literal is missing "+r.length+" closing bracket"+(1===r.length?"":"s")+" (`}`)";throw new Error(s)}return n.join("")},Jv=Vt((function(e){var t=Lv,r="win32"===es.platform&&!(es.env.TERM||"").toLowerCase().startsWith("xterm"),n=["ansi","ansi","ansi256","ansi16m"],a=new Set(["gray"]),s=Object.create(null);function i(e,t){t=t||{};e.level=void 0===t.level?0:t.level,e.enabled="enabled"in t?t.enabled:e.level>0}function o(e){if(!this||!(this instanceof o)||this.template){var t={};return i(t,e),t.template=function(){var e=[].slice.call(arguments);return x.apply(null,[t.template].concat(e))},Object.setPrototypeOf(t,o.prototype),Object.setPrototypeOf(t.template,t),t.template.constructor=o,t.template}i(this,e)}r&&(Mv.blue.open="");for(var u=function(){var e=l[c];Mv[e].closeRe=new RegExp(kv(Mv[e].close),"g"),s[e]={get:function(){var t=Mv[e];return v.call(this,this._styles?this._styles.concat(t):[t],this._empty,e)}}},c=0,l=Object.keys(Mv);c<l.length;c++)u();s.visible={get:function(){return v.call(this,this._styles||[],!0,"visible")}},Mv.color.closeRe=new RegExp(kv(Mv.color.close),"g");for(var p=function(){var e=f[d];if(a.has(e))return"continue";s[e]={get:function(){var t=this.level;return function(){var r=Mv.color[n[t]][e].apply(null,arguments),a={open:r,close:Mv.color.close,closeRe:Mv.color.closeRe};return v.call(this,this._styles?this._styles.concat(a):[a],this._empty,e)}}}},d=0,f=Object.keys(Mv.color.ansi);d<f.length;d++)p();Mv.bgColor.closeRe=new RegExp(kv(Mv.bgColor.close),"g");for(var h=function(){var e=y[m];if(a.has(e))return"continue";var t="bg"+e[0].toUpperCase()+e.slice(1);s[t]={get:function(){var t=this.level;return function(){var r=Mv.bgColor[n[t]][e].apply(null,arguments),a={open:r,close:Mv.bgColor.close,closeRe:Mv.bgColor.closeRe};return v.call(this,this._styles?this._styles.concat(a):[a],this._empty,e)}}}},m=0,y=Object.keys(Mv.bgColor.ansi);m<y.length;m++)h();var g=Object.defineProperties((function(){}),s);function v(e,t,r){var n=function e(){return b.apply(e,arguments)};n._styles=e,n._empty=t;var a=this;return Object.defineProperty(n,"level",{enumerable:!0,get:function(){return a.level},set:function(e){a.level=e}}),Object.defineProperty(n,"enabled",{enumerable:!0,get:function(){return a.enabled},set:function(e){a.enabled=e}}),n.hasGrey=this.hasGrey||"gray"===r||"grey"===r,n.__proto__=g,n}function b(){var e=arguments,t=e.length,n=String(arguments[0]);if(0===t)return"";if(t>1)for(var a=1;a<t;a++)n+=" "+e[a];if(!this.enabled||this.level<=0||!n)return this._empty?"":n;var s=Mv.dim.open;r&&this.hasGrey&&(Mv.dim.open="");var i=this._styles.slice().reverse(),o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}var l=c;n=(n=l.open+n.replace(l.closeRe,l.open)+l.close).replace(/\r?\n/g,l.close+"$&"+l.open)}return Mv.dim.open=s,n}function x(e,t){if(!Array.isArray(t))return[].slice.call(arguments,1).join(" ");for(var r=[].slice.call(arguments,2),n=[t.raw[0]],a=1;a<t.length;a++)n.push(String(r[a-1]).replace(/[{}\\]/g,"\\$&")),n.push(String(t.raw[a]));return Yv(e,n.join(""))}Object.defineProperties(o.prototype,s),e.exports=o(),e.exports.supportsColor=t,e.exports.default=e.exports}));Jv.supportsColor;var $v=/\r\n|[\n\r\u2028\u2029]/,Qv=/^[a-z][\w-]*$/i,Zv=/^[()[\]{}]$/;function eb(e){var t=e.slice(-2),r=t[0],n=t[1],a=jv(e);if("name"===a.type){if(as.keyword.isReservedWordES6(a.value))return"keyword";if(Qv.test(a.value)&&("<"===n[r-1]||"</"==n.substr(r-2,2)))return"jsx_tag";if(a.value[0]!==a.value[0].toLowerCase())return"capitalized"}return"punctuator"===a.type&&Zv.test(a.value)?"bracket":"invalid"!==a.type||"@"!==a.value&&"#"!==a.value?a.type:"punctuator"}function tb(e){return Jv.supportsColor||e.forceColor}function rb(e){var t=Jv;return e.forceColor&&(t=new Jv.constructor({enabled:!0,level:1})),t}function nb(e,t){return void 0===t&&(t={}),tb(t)?function(e,t){return t.replace(Tv,(function(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];var a=eb(r),s=e[a];return s?r[0].split($v).map((function(e){return s(e)})).join("\n"):r[0]}))}(function(e){return{keyword:e.cyan,capitalized:e.yellow,jsx_tag:e.yellow,punctuator:e.yellow,number:e.magenta,string:e.green,regex:e.magenta,comment:e.grey,invalid:e.white.bgRed.bold}}(rb(t)),e):e}var ab=/\r\n|[\n\r\u2028\u2029]/;function sb(e,t,r){void 0===r&&(r={});var n=(r.highlightCode||r.forceColor)&&tb(r),a=rb(r),s=function(e){return{gutter:e.grey,marker:e.red.bold,message:e.red.bold}}(a),i=function(e,t){return n?e(t):t},o=function(e,t,r){var n=Object.assign({column:0,line:-1},e.start),a=Object.assign({},n,{},e.end),s=r||{},i=s.linesAbove,o=void 0===i?2:i,u=s.linesBelow,c=void 0===u?3:u,l=n.line,p=n.column,d=a.line,f=a.column,h=Math.max(l-(o+1),0),m=Math.min(t.length,d+c);-1===l&&(h=0),-1===d&&(m=t.length);var y=d-l,g={};if(y)for(var v=0;v<=y;v++){var b=v+l;if(p)if(0===v){var x=t[b-1].length;g[b]=[p,x-p+1]}else if(v===y)g[b]=[0,f];else{var E=t[b-v].length;g[b]=[0,E]}else g[b]=!0}else g[l]=p===f?!p||[p,0]:[p,f-p];return{start:h,end:m,markerLines:g}}(t,e.split(ab),r),u=o.start,c=o.end,l=o.markerLines,p=t.start&&"number"==typeof t.start.column,d=String(c).length,f=(n?nb(e,r):e).split(ab).slice(u,c).map((function(e,t){var n=u+1+t,a=" "+(" "+n).slice(-d)+" | ",o=l[n],c=!l[n+1];if(o){var p="";if(Array.isArray(o)){var f=e.slice(0,Math.max(o[0]-1,0)).replace(/[^\t]/g," "),h=o[1]||1;p=["\n ",i(s.gutter,a.replace(/\d/g," ")),f,i(s.marker,"^").repeat(h)].join(""),c&&r.message&&(p+=" "+i(s.message,r.message))}return[i(s.marker,">"),i(s.gutter,a),e,p].join("")}return" "+i(s.gutter,a)+e})).join("\n");return r.message&&!p&&(f=""+" ".repeat(d+1)+r.message+"\n"+f),n?a.reset(f):f}var ib=function(e,t){void 0===t&&(t={}),this.label=e,this.keyword=t.keyword,this.beforeExpr=!!t.beforeExpr,this.startsExpr=!!t.startsExpr,this.rightAssociative=!!t.rightAssociative,this.isLoop=!!t.isLoop,this.isAssign=!!t.isAssign,this.prefix=!!t.prefix,this.postfix=!!t.postfix,this.binop=null!=t.binop?t.binop:null,this.updateContext=null},ob=new Map;function ub(e,t){void 0===t&&(t={}),t.keyword=e;var r=new ib(e,t);return ob.set(e,r),r}function cb(e,t){return new ib(e,{beforeExpr:!0,binop:t})}var lb={num:new ib("num",{startsExpr:!0}),bigint:new ib("bigint",{startsExpr:!0}),regexp:new ib("regexp",{startsExpr:!0}),string:new ib("string",{startsExpr:!0}),name:new ib("name",{startsExpr:!0}),eof:new ib("eof"),bracketL:new ib("[",{beforeExpr:!0,startsExpr:!0}),bracketR:new ib("]"),braceL:new ib("{",{beforeExpr:!0,startsExpr:!0}),braceBarL:new ib("{|",{beforeExpr:!0,startsExpr:!0}),braceR:new ib("}"),braceBarR:new ib("|}"),parenL:new ib("(",{beforeExpr:!0,startsExpr:!0}),parenR:new ib(")"),comma:new ib(",",{beforeExpr:!0}),semi:new ib(";",{beforeExpr:!0}),colon:new ib(":",{beforeExpr:!0}),doubleColon:new ib("::",{beforeExpr:!0}),dot:new ib("."),question:new ib("?",{beforeExpr:!0}),questionDot:new ib("?."),arrow:new ib("=>",{beforeExpr:!0}),template:new ib("template"),ellipsis:new ib("...",{beforeExpr:!0}),backQuote:new ib("`",{startsExpr:!0}),dollarBraceL:new ib("${",{beforeExpr:!0,startsExpr:!0}),at:new ib("@"),hash:new ib("#",{startsExpr:!0}),interpreterDirective:new ib("#!..."),eq:new ib("=",{beforeExpr:!0,isAssign:!0}),assign:new ib("_=",{beforeExpr:!0,isAssign:!0}),incDec:new ib("++/--",{prefix:!0,postfix:!0,startsExpr:!0}),bang:new ib("!",{beforeExpr:!0,prefix:!0,startsExpr:!0}),tilde:new ib("~",{beforeExpr:!0,prefix:!0,startsExpr:!0}),pipeline:cb("|>",0),nullishCoalescing:cb("??",1),logicalOR:cb("||",1),logicalAND:cb("&&",2),bitwiseOR:cb("|",3),bitwiseXOR:cb("^",4),bitwiseAND:cb("&",5),equality:cb("==/!=/===/!==",6),relational:cb("</>/<=/>=",7),bitShift:cb("<</>>/>>>",8),plusMin:new ib("+/-",{beforeExpr:!0,binop:9,prefix:!0,startsExpr:!0}),modulo:new ib("%",{beforeExpr:!0,binop:10,startsExpr:!0}),star:cb("*",10),slash:cb("/",10),exponent:new ib("**",{beforeExpr:!0,binop:11,rightAssociative:!0}),_break:ub("break"),_case:ub("case",{beforeExpr:!0}),_catch:ub("catch"),_continue:ub("continue"),_debugger:ub("debugger"),_default:ub("default",{beforeExpr:!0}),_do:ub("do",{isLoop:!0,beforeExpr:!0}),_else:ub("else",{beforeExpr:!0}),_finally:ub("finally"),_for:ub("for",{isLoop:!0}),_function:ub("function",{startsExpr:!0}),_if:ub("if"),_return:ub("return",{beforeExpr:!0}),_switch:ub("switch"),_throw:ub("throw",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_try:ub("try"),_var:ub("var"),_const:ub("const"),_while:ub("while",{isLoop:!0}),_with:ub("with"),_new:ub("new",{beforeExpr:!0,startsExpr:!0}),_this:ub("this",{startsExpr:!0}),_super:ub("super",{startsExpr:!0}),_class:ub("class",{startsExpr:!0}),_extends:ub("extends",{beforeExpr:!0}),_export:ub("export"),_import:ub("import",{startsExpr:!0}),_null:ub("null",{startsExpr:!0}),_true:ub("true",{startsExpr:!0}),_false:ub("false",{startsExpr:!0}),_in:ub("in",{beforeExpr:!0,binop:7}),_instanceof:ub("instanceof",{beforeExpr:!0,binop:7}),_typeof:ub("typeof",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_void:ub("void",{beforeExpr:!0,prefix:!0,startsExpr:!0}),_delete:ub("delete",{beforeExpr:!0,prefix:!0,startsExpr:!0})},pb=/\r\n?|[\n\u2028\u2029]/,db=new RegExp(pb.source,"g");function fb(e){switch(e){case 10:case 13:case 8232:case 8233:return!0;default:return!1}}var hb=/(?:\s|\/\/.*|\/\*[^]*?\*\/)*/g;function mb(e){switch(e){case 9:case 11:case 12:case 32:case 160:case 5760:case 8192:case 8193:case 8194:case 8195:case 8196:case 8197:case 8198:case 8199:case 8200:case 8201:case 8202:case 8239:case 8287:case 12288:case 65279:return!0;default:return!1}}var yb=function(e,t){this.line=e,this.column=t},gb=function(e,t){this.start=e,this.end=t};function vb(e){return e[e.length-1]}var bb=function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.addComment=function(e){this.filename&&(e.loc.filename=this.filename),this.state.trailingComments.push(e),this.state.leadingComments.push(e)},r.adjustCommentsAfterTrailingComma=function(e,t,r){if(0!==this.state.leadingComments.length){for(var n=null,a=t.length;null===n&&a>0;)n=t[--a];if(null!==n){for(var s=0;s<this.state.leadingComments.length;s++)this.state.leadingComments[s].end<this.state.commentPreviousNode.end&&(this.state.leadingComments.splice(s,1),s--);for(var i=[],o=0;o<this.state.leadingComments.length;o++){var u=this.state.leadingComments[o];u.end<e.end?(i.push(u),r||(this.state.leadingComments.splice(o,1),o--)):(void 0===e.trailingComments&&(e.trailingComments=[]),e.trailingComments.push(u))}r&&(this.state.leadingComments=[]),i.length>0?n.trailingComments=i:void 0!==n.trailingComments&&(n.trailingComments=[])}}},r.processComment=function(e){if(!("Program"===e.type&&e.body.length>0)){var t,r,n,a,s,i=this.state.commentStack;if(this.state.trailingComments.length>0)this.state.trailingComments[0].start>=e.end?(n=this.state.trailingComments,this.state.trailingComments=[]):this.state.trailingComments.length=0;else if(i.length>0){var o=vb(i);o.trailingComments&&o.trailingComments[0].start>=e.end&&(n=o.trailingComments,delete o.trailingComments)}for(i.length>0&&vb(i).start>=e.start&&(t=i.pop());i.length>0&&vb(i).start>=e.start;)r=i.pop();if(!r&&t&&(r=t),t)switch(e.type){case"ObjectExpression":this.adjustCommentsAfterTrailingComma(e,e.properties);break;case"ObjectPattern":this.adjustCommentsAfterTrailingComma(e,e.properties,!0);break;case"CallExpression":this.adjustCommentsAfterTrailingComma(e,e.arguments);break;case"ArrayExpression":this.adjustCommentsAfterTrailingComma(e,e.elements);break;case"ArrayPattern":this.adjustCommentsAfterTrailingComma(e,e.elements,!0)}else this.state.commentPreviousNode&&("ImportSpecifier"===this.state.commentPreviousNode.type&&"ImportSpecifier"!==e.type||"ExportSpecifier"===this.state.commentPreviousNode.type&&"ExportSpecifier"!==e.type)&&this.adjustCommentsAfterTrailingComma(e,[this.state.commentPreviousNode]);if(r){if(r.leadingComments)if(r!==e&&r.leadingComments.length>0&&vb(r.leadingComments).end<=e.start)e.leadingComments=r.leadingComments,delete r.leadingComments;else for(a=r.leadingComments.length-2;a>=0;--a)if(r.leadingComments[a].end<=e.start){e.leadingComments=r.leadingComments.splice(0,a+1);break}}else if(this.state.leadingComments.length>0)if(vb(this.state.leadingComments).end<=e.start){if(this.state.commentPreviousNode)for(s=0;s<this.state.leadingComments.length;s++)this.state.leadingComments[s].end<this.state.commentPreviousNode.end&&(this.state.leadingComments.splice(s,1),s--);this.state.leadingComments.length>0&&(e.leadingComments=this.state.leadingComments,this.state.leadingComments=[])}else{for(a=0;a<this.state.leadingComments.length&&!(this.state.leadingComments[a].end>e.start);a++);var u=this.state.leadingComments.slice(0,a);u.length&&(e.leadingComments=u),0===(n=this.state.leadingComments.slice(a)).length&&(n=null)}this.state.commentPreviousNode=e,n&&(n.length&&n[0].start>=e.start&&vb(n).end<=e.end?e.innerComments=n:e.trailingComments=n),i.push(e)}},t}(function(){function e(){this.sawUnambiguousESM=!1,this.ambiguousScriptDifferentAst=!1}var t=e.prototype;return t.hasPlugin=function(e){return this.plugins.has(e)},t.getPluginOption=function(e,t){if(this.hasPlugin(e))return this.plugins.get(e)[t]},e}()),xb=Object.freeze({ArgumentsDisallowedInInitializer:"'arguments' is not allowed in class field initializer",AsyncFunctionInSingleStatementContext:"Async functions can only be declared at the top level or inside a block",AwaitBindingIdentifier:"Can not use 'await' as identifier inside an async function",AwaitExpressionFormalParameter:"await is not allowed in async function parameters",AwaitNotInAsyncFunction:"Can not use keyword 'await' outside an async function",BadGetterArity:"getter must not have any formal parameters",BadSetterArity:"setter must have exactly one formal parameter",BadSetterRestParameter:"setter function argument must not be a rest parameter",ConstructorClassField:"Classes may not have a field named 'constructor'",ConstructorClassPrivateField:"Classes may not have a private field named '#constructor'",ConstructorIsAccessor:"Constructor can't have get/set modifier",ConstructorIsAsync:"Constructor can't be an async function",ConstructorIsGenerator:"Constructor can't be a generator",DeclarationMissingInitializer:"%0 require an initialization value",DecoratorBeforeExport:"Decorators must be placed *before* the 'export' keyword. You can set the 'decoratorsBeforeExport' option to false to use the 'export @decorator class {}' syntax",DecoratorConstructor:"Decorators can't be used with a constructor. Did you mean '@dec class { ... }'?",DecoratorExportClass:"Using the export keyword between a decorator and a class is not allowed. Please use `export @dec class` instead.",DecoratorSemicolon:"Decorators must not be followed by a semicolon",DeletePrivateField:"Deleting a private field is not allowed",DestructureNamedImport:"ES2015 named imports do not destructure. Use another statement for destructuring after the import.",DuplicateConstructor:"Duplicate constructor in the same class",DuplicateDefaultExport:"Only one default export allowed per module.",DuplicateExport:"`%0` has already been exported. Exported identifiers must be unique.",DuplicateProto:"Redefinition of __proto__ property",DuplicateRegExpFlags:"Duplicate regular expression flag",ElementAfterRest:"Rest element must be last element",EscapedCharNotAnIdentifier:"Invalid Unicode escape",ForInOfLoopInitializer:"%0 loop variable declaration may not have an initializer",GeneratorInSingleStatementContext:"Generators can only be declared at the top level or inside a block",IllegalBreakContinue:"Unsyntactic %0",IllegalLanguageModeDirective:"Illegal 'use strict' directive in function with non-simple parameter list",IllegalReturn:"'return' outside of function",ImportCallArgumentTrailingComma:"Trailing comma is disallowed inside import(...) arguments",ImportCallArity:"import() requires exactly one argument",ImportCallArityLtOne:"Dynamic imports require a parameter: import('a.js')",ImportCallNotNewExpression:"Cannot use new with import(...)",ImportCallSpreadArgument:"... is not allowed in import()",ImportMetaOutsideModule:"import.meta may appear only with 'sourceType: \"module\"'",ImportOutsideModule:"'import' and 'export' may appear only with 'sourceType: \"module\"'",InvalidCodePoint:"Code point out of bounds",InvalidDigit:"Expected number in radix %0",InvalidEscapeSequence:"Bad character escape sequence",InvalidEscapeSequenceTemplate:"Invalid escape sequence in template",InvalidEscapedReservedWord:"Escape sequence in keyword %0",InvalidIdentifier:"Invalid identifier %0",InvalidLhs:"Invalid left-hand side in %0",InvalidLhsBinding:"Binding invalid left-hand side in %0",InvalidNumber:"Invalid number",InvalidOrUnexpectedToken:"Unexpected character '%0'",InvalidParenthesizedAssignment:"Invalid parenthesized assignment pattern",InvalidPrivateFieldResolution:"Private name #%0 is not defined",InvalidPropertyBindingPattern:"Binding member expression",InvalidRestAssignmentPattern:"Invalid rest operator's argument",LabelRedeclaration:"Label '%0' is already declared",LetInLexicalBinding:"'let' is not allowed to be used as a name in 'let' or 'const' declarations.",MalformedRegExpFlags:"Invalid regular expression flag",MissingClassName:"A class name is required",MissingEqInAssignment:"Only '=' operator can be used for specifying default value.",MissingUnicodeEscape:"Expecting Unicode escape sequence \\uXXXX",MixingCoalesceWithLogical:"Nullish coalescing operator(??) requires parens when mixing with logical operators",ModuleExportUndefined:"Export '%0' is not defined",MultipleDefaultsInSwitch:"Multiple default clauses",NewlineAfterThrow:"Illegal newline after throw",NoCatchOrFinally:"Missing catch or finally clause",NumberIdentifier:"Identifier directly after number",NumericSeparatorInEscapeSequence:"Numeric separators are not allowed inside unicode escape sequences or hex escape sequences",ObsoleteAwaitStar:"await* has been removed from the async functions proposal. Use Promise.all() instead.",OptionalChainingNoNew:"constructors in/after an Optional Chain are not allowed",OptionalChainingNoTemplate:"Tagged Template Literals are not allowed in optionalChain",ParamDupe:"Argument name clash",PatternHasAccessor:"Object pattern can't contain getter or setter",PatternHasMethod:"Object pattern can't contain methods",PipelineBodyNoArrow:'Unexpected arrow "=>" after pipeline body; arrow function in pipeline body must be parenthesized',PipelineBodySequenceExpression:"Pipeline body may not be a comma-separated sequence expression",PipelineHeadSequenceExpression:"Pipeline head should not be a comma-separated sequence expression",PipelineTopicUnused:"Pipeline is in topic style but does not use topic reference",PrimaryTopicNotAllowed:"Topic reference was used in a lexical context without topic binding",PrimaryTopicRequiresSmartPipeline:"Primary Topic Reference found but pipelineOperator not passed 'smart' for 'proposal' option.",PrivateNameRedeclaration:"Duplicate private name #%0",RestTrailingComma:"Unexpected trailing comma after rest element",SloppyFunction:"In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement",StaticPrototype:"Classes may not have static property named prototype",StrictDelete:"Deleting local variable in strict mode",StrictEvalArguments:"Assigning to '%0' in strict mode",StrictEvalArgumentsBinding:"Binding '%0' in strict mode",StrictFunction:"In strict mode code, functions can only be declared at top level or inside a block",StrictOctalLiteral:"Octal literal in strict mode",StrictWith:"'with' in strict mode",SuperNotAllowed:"super() is only valid inside a class constructor of a subclass. Maybe a typo in the method name ('constructor') or not extending another class?",SuperPrivateField:"Private fields can't be accessed on super",TrailingDecorator:"You have trailing decorators with no method",UnexpectedArgumentPlaceholder:"Unexpected argument placeholder",UnexpectedAwaitAfterPipelineBody:'Unexpected "await" after pipeline body; await must have parentheses in minimal proposal',UnexpectedDigitAfterHash:"Unexpected digit after hash token",UnexpectedImportExport:"'import' and 'export' may only appear at the top level",UnexpectedKeyword:"Unexpected keyword '%0'",UnexpectedLeadingDecorator:"Leading decorators must be attached to a class declaration",UnexpectedLexicalDeclaration:"Lexical declaration cannot appear in a single-statement context",UnexpectedNewTarget:"new.target can only be used in functions",UnexpectedNumericSeparator:"A numeric separator is only allowed between two digits",UnexpectedPrivateField:"Private names can only be used as the name of a class element (i.e. class C { #p = 42; #m() {} } )\n or a property of member expression (i.e. this.#p).",UnexpectedReservedWord:"Unexpected reserved word '%0'",UnexpectedSuper:"super is only allowed in object methods and classes",UnexpectedToken:"Unexpected token '%'",UnexpectedTokenUnaryExponentiation:"Illegal expression. Wrap left hand side or entire exponentiation in parentheses.",UnsupportedBind:"Binding should be performed on object property.",UnsupportedDecoratorExport:"You can only use decorators on an export when exporting a class",UnsupportedDefaultExport:"Only expressions, functions or classes are allowed as the `default` export.",UnsupportedImport:"import can only be used in import() or import.meta",UnsupportedMetaProperty:"The only valid meta property for %0 is %0.%1",UnsupportedParameterDecorator:"Stage 2 decorators cannot be used to decorate parameters",UnsupportedPropertyDecorator:"Stage 2 decorators disallow object literal property decorators",UnsupportedSuper:"super can only be used with function calls (i.e. super()) or in property accesses (i.e. super.prop or super[prop])",UnterminatedComment:"Unterminated comment",UnterminatedRegExp:"Unterminated regular expression",UnterminatedString:"Unterminated string constant",UnterminatedTemplate:"Unterminated template",VarRedeclaration:"Identifier '%0' has already been declared",YieldBindingIdentifier:"Can not use 'yield' as identifier inside a generator",YieldInParameter:"yield is not allowed in generator parameters",ZeroDigitNumericSeparator:"Numeric separator can not be used after leading 0"}),Eb=function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.getLocationForPosition=function(e){return e===this.state.start?this.state.startLoc:e===this.state.lastTokStart?this.state.lastTokStartLoc:e===this.state.end?this.state.endLoc:e===this.state.lastTokEnd?this.state.lastTokEndLoc:function(e,t){var r,n=1,a=0;for(db.lastIndex=0;(r=db.exec(e))&&r.index<t;)n++,a=db.lastIndex;return new yb(n,t-a)}(this.input,e)},r.raise=function(e,t){for(var r=arguments.length,n=new Array(r>2?r-2:0),a=2;a<r;a++)n[a-2]=arguments[a];return this.raiseWithData.apply(this,[e,void 0,t].concat(n))},r.raiseWithData=function(e,t,r){for(var n=arguments.length,a=new Array(n>3?n-3:0),s=3;s<n;s++)a[s-3]=arguments[s];var i=this.getLocationForPosition(e),o=r.replace(/%(\d+)/g,(function(e,t){return a[t]}))+" ("+i.line+":"+i.column+")";return this._raise(Object.assign({loc:i,pos:e},t),o)},r._raise=function(e,t){var r=new SyntaxError(t);if(Object.assign(r,e),this.options.errorRecovery)return this.isLookahead||this.state.errors.push(r),r;throw r},t}(bb);function Ab(e){return null!=e&&"Property"===e.type&&"init"===e.kind&&!1===e.method}var wb=function(e,t,r,n){this.token=e,this.isExpr=!!t,this.preserveSpace=!!r,this.override=n},Sb={braceStatement:new wb("{",!1),braceExpression:new wb("{",!0),templateQuasi:new wb("${",!1),parenStatement:new wb("(",!1),parenExpression:new wb("(",!0),template:new wb("`",!0,!0,(function(e){return e.readTmplToken()})),functionExpression:new wb("function",!0),functionStatement:new wb("function",!1)};lb.parenR.updateContext=lb.braceR.updateContext=function(){if(1!==this.state.context.length){var e=this.state.context.pop();e===Sb.braceStatement&&"function"===this.curContext().token&&(e=this.state.context.pop()),this.state.exprAllowed=!e.isExpr}else this.state.exprAllowed=!0},lb.name.updateContext=function(e){var t=!1;e!==lb.dot&&("of"===this.state.value&&!this.state.exprAllowed||"yield"===this.state.value&&this.prodParam.hasYield)&&(t=!0),this.state.exprAllowed=t,this.state.isIterator&&(this.state.isIterator=!1)},lb.braceL.updateContext=function(e){this.state.context.push(this.braceIsBlock(e)?Sb.braceStatement:Sb.braceExpression),this.state.exprAllowed=!0},lb.dollarBraceL.updateContext=function(){this.state.context.push(Sb.templateQuasi),this.state.exprAllowed=!0},lb.parenL.updateContext=function(e){var t=e===lb._if||e===lb._for||e===lb._with||e===lb._while;this.state.context.push(t?Sb.parenStatement:Sb.parenExpression),this.state.exprAllowed=!0},lb.incDec.updateContext=function(){},lb._function.updateContext=lb._class.updateContext=function(e){!e.beforeExpr||e===lb.semi||e===lb._else||e===lb._return&&pb.test(this.input.slice(this.state.lastTokEnd,this.state.start))||(e===lb.colon||e===lb.braceL)&&this.curContext()===Sb.b_stat?this.state.context.push(Sb.functionStatement):this.state.context.push(Sb.functionExpression),this.state.exprAllowed=!1},lb.backQuote.updateContext=function(){this.curContext()===Sb.template?this.state.context.pop():this.state.context.push(Sb.template),this.state.exprAllowed=!1};var Db=["eval","arguments"],Cb=new Set(["implements","interface","let","package","private","protected","public","static","yield"]),Tb=new Set(Db),jb=function(e,t){return t&&"await"===e||"enum"===e};function Pb(e,t){return jb(e,t)||Cb.has(e)}function kb(e){return Tb.has(e)}function Fb(e,t){return Pb(e,t)||kb(e)}var _b=/^in(stanceof)?$/,Ib="ªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙՠ-ֈא-תׯ-ײؠ-يٮٯٱ-ۓەۥۦۮۯۺ-ۼۿܐܒ-ܯݍ-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࡠ-ࡪࢠ-ࢴࢶ-ࢽऄ-हऽॐक़-ॡॱ-ঀঅ-ঌএঐও-নপ-রলশ-হঽৎড়ঢ়য়-ৡৰৱৼਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਖ਼-ੜਫ਼ੲ-ੴઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽૐૠૡૹଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽଡ଼ଢ଼ୟ-ୡୱஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹௐఅ-ఌఎ-ఐఒ-నప-హఽౘ-ౚౠౡಀಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽೞೠೡೱೲഅ-ഌഎ-ഐഒ-ഺഽൎൔ-ൖൟ-ൡൺ-ൿඅ-ඖක-නඳ-රලව-ෆก-ะาำเ-ๆກຂຄຆ-ຊຌ-ຣລວ-ະາຳຽເ-ໄໆໜ-ໟༀཀ-ཇཉ-ཬྈ-ྌက-ဪဿၐ-ၕၚ-ၝၡၥၦၮ-ၰၵ-ႁႎႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜑᜠ-ᜱᝀ-ᝑᝠ-ᝬᝮ-ᝰក-ឳៗៜᠠ-ᡸᢀ-ᢨᢪᢰ-ᣵᤀ-ᤞᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨖᨠ-ᩔᪧᬅ-ᬳᭅ-ᭋᮃ-ᮠᮮᮯᮺ-ᯥᰀ-ᰣᱍ-ᱏᱚ-ᱽᲀ-ᲈᲐ-ᲺᲽ-Ჿᳩ-ᳬᳮ-ᳳᳵᳶᳺᴀ-ᶿḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕ℘-ℝℤΩℨK-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ々-〇〡-〩〱-〵〸-〼ぁ-ゖ゛-ゟァ-ヺー-ヿㄅ-ㄯㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿯ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙿ-ꚝꚠ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞿꟂ-Ᶎꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠢꡀ-ꡳꢂ-ꢳꣲ-ꣷꣻꣽꣾꤊ-ꤥꤰ-ꥆꥠ-ꥼꦄ-ꦲꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨨꩀ-ꩂꩄ-ꩋꩠ-ꩶꩺꩾ-ꪯꪱꪵꪶꪹ-ꪽꫀꫂꫛ-ꫝꫠ-ꫪꫲ-ꫴꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭧꭰ-ꯢ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִײַ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ",Bb="‌‍·̀-ͯ·҃-֑҇-ׇֽֿׁׂׅׄؐ-ًؚ-٩ٰۖ-ۜ۟-۪ۤۧۨ-ۭ۰-۹ܑܰ-݊ަ-ް߀-߉߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣ०-९ঁ-ঃ়া-ৄেৈো-্ৗৢৣ০-৯৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑ੦-ੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣ૦-૯ૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣ୦-୯ஂா-ூெ-ைொ-்ௗ௦-௯ఀ-ఄా-ౄె-ైొ-్ౕౖౢౣ౦-౯ಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣ೦-೯ഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣ൦-൯ංඃ්ා-ුූෘ-ෟ෦-෯ෲෳัิ-ฺ็-๎๐-๙ັິ-ຼ່-ໍ໐-໙༘༙༠-༩༹༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှ၀-၉ၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏ-ႝ፝-፟፩-፱ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝០-៩᠋-᠍᠐-᠙ᢩᤠ-ᤫᤰ-᤻᥆-᥏᧐-᧚ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼-᪉᪐-᪙᪰-᪽ᬀ-ᬄ᬴-᭄᭐-᭙᭫-᭳ᮀ-ᮂᮡ-ᮭ᮰-᮹᯦-᯳ᰤ-᰷᱀-᱉᱐-᱙᳐-᳔᳒-᳨᳭᳴᳷-᳹᷀-᷹᷻-᷿‿⁀⁔⃐-⃥⃜⃡-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꘠-꘩꙯ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣐-꣙꣠-꣱ꣿ-꤉ꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀꧐-꧙ꧥ꧰-꧹ꨩ-ꨶꩃꩌꩍ꩐-꩙ꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭꯰-꯹ﬞ︀-️︠-︯︳︴﹍-﹏0-9_",Ob=new RegExp("["+Ib+"]"),Nb=new RegExp("["+Ib+Bb+"]");Ib=Bb=null;var Rb=[0,11,2,25,2,18,2,1,2,14,3,13,35,122,70,52,268,28,4,48,48,31,14,29,6,37,11,29,3,35,5,7,2,4,43,157,19,35,5,35,5,39,9,51,157,310,10,21,11,7,153,5,3,0,2,43,2,1,4,0,3,22,11,22,10,30,66,18,2,1,11,21,11,25,71,55,7,1,65,0,16,3,2,2,2,28,43,28,4,28,36,7,2,27,28,53,11,21,11,18,14,17,111,72,56,50,14,50,14,35,477,28,11,0,9,21,155,22,13,52,76,44,33,24,27,35,30,0,12,34,4,0,13,47,15,3,22,0,2,0,36,17,2,24,85,6,2,0,2,3,2,14,2,9,8,46,39,7,3,1,3,21,2,6,2,1,2,4,4,0,19,0,13,4,159,52,19,3,21,0,33,47,21,1,2,0,185,46,42,3,37,47,21,0,60,42,14,0,72,26,230,43,117,63,32,0,161,7,3,38,17,0,2,0,29,0,11,39,8,0,22,0,12,45,20,0,35,56,264,8,2,36,18,0,50,29,113,6,2,1,2,37,22,0,26,5,2,1,2,31,15,0,328,18,270,921,103,110,18,195,2749,1070,4050,582,8634,568,8,30,114,29,19,47,17,3,32,20,6,18,689,63,129,74,6,0,67,12,65,1,2,0,29,6135,9,754,9486,286,50,2,18,3,9,395,2309,106,6,12,4,8,8,9,5991,84,2,70,2,1,3,0,3,1,3,3,2,11,2,0,2,6,2,64,2,3,3,7,2,6,2,27,2,3,2,4,2,0,4,6,2,339,3,24,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,30,2,24,2,7,2357,44,11,6,17,0,370,43,1301,196,60,67,8,0,1205,3,2,26,2,1,2,0,3,0,2,9,2,3,2,0,2,0,7,0,5,0,2,0,2,0,2,2,2,1,2,0,3,0,2,0,2,0,2,0,2,0,2,1,2,0,3,3,2,6,2,3,2,3,2,0,2,9,2,16,6,2,2,4,2,16,4421,42710,42,4148,12,221,3,5761,15,7472,3104,541],Mb=[509,0,227,0,150,4,294,9,1368,2,2,1,6,3,41,2,5,0,166,1,574,3,9,9,525,10,176,2,54,14,32,9,16,3,46,10,54,9,7,2,37,13,2,9,6,1,45,0,13,2,49,13,9,3,4,9,83,11,7,0,161,11,6,9,7,3,56,1,2,6,3,1,3,2,10,0,11,1,3,6,4,4,193,17,10,9,5,0,82,19,13,9,214,6,3,8,28,1,83,16,16,9,82,12,9,9,84,14,5,9,243,14,166,9,232,6,3,6,4,0,29,9,41,6,2,3,9,0,10,10,47,15,406,7,2,7,17,9,57,21,2,13,123,5,4,0,2,1,2,6,2,0,9,9,49,4,2,1,2,4,9,9,330,3,19306,9,135,4,60,6,26,9,1014,0,2,54,8,3,19723,1,5319,4,4,5,9,7,3,6,31,3,149,2,1418,49,513,54,5,49,9,0,15,0,23,4,2,14,1361,6,2,16,3,6,2,1,2,4,262,6,10,9,419,13,1495,6,110,6,6,9,792487,239];function Lb(e,t){for(var r=65536,n=0,a=t.length;n<a;n+=2){if((r+=t[n])>e)return!1;if((r+=t[n+1])>=e)return!0}return!1}function Ub(e){return e<65?36===e:e<=90||(e<97?95===e:e<=122||(e<=65535?e>=170&&Ob.test(String.fromCharCode(e)):Lb(e,Rb)))}function Gb(e){return e<48?36===e:e<58||!(e<65)&&(e<=90||(e<97?95===e:e<=122||(e<=65535?e>=170&&Nb.test(String.fromCharCode(e)):Lb(e,Rb)||Lb(e,Mb))))}var Vb=new Set(["_","any","bool","boolean","empty","extends","false","interface","mixed","null","number","static","string","true","typeof","void"]),Wb=Object.freeze({AmbiguousConditionalArrow:"Ambiguous expression: wrap the arrow functions in parentheses to disambiguate.",AmbiguousDeclareModuleKind:"Found both `declare module.exports` and `declare export` in the same module. Modules can only have 1 since they are either an ES module or they are a CommonJS module",AssignReservedType:"Cannot overwrite reserved type %0",DuplicateDeclareModuleExports:"Duplicate `declare module.exports` statement",EnumBooleanMemberNotInitialized:"Boolean enum members need to be initialized. Use either `%0 = true,` or `%0 = false,` in enum `%1`.",EnumDuplicateMemberName:"Enum member names need to be unique, but the name `%0` has already been used before in enum `%1`.",EnumInconsistentMemberValues:"Enum `%0` has inconsistent member initializers. Either use no initializers, or consistently use literals (either booleans, numbers, or strings) for all member initializers.",EnumInvalidExplicitType:"Enum type `%1` is not valid. Use one of `boolean`, `number`, `string`, or `symbol` in enum `%0`.",EnumInvalidExplicitTypeUnknownSupplied:"Supplied enum type is not valid. Use one of `boolean`, `number`, `string`, or `symbol` in enum `%0`.",EnumInvalidMemberInitializerPrimaryType:"Enum `%0` has type `%2`, so the initializer of `%1` needs to be a %2 literal.",EnumInvalidMemberInitializerSymbolType:"Symbol enum members cannot be initialized. Use `%1,` in enum `%0`.",EnumInvalidMemberInitializerUnknownType:"The enum member initializer for `%1` needs to be a literal (either a boolean, number, or string) in enum `%0`.",EnumInvalidMemberName:"Enum member names cannot start with lowercase 'a' through 'z'. Instead of using `%0`, consider using `%1`, in enum `%2`.",EnumNumberMemberNotInitialized:"Number enum members need to be initialized, e.g. `%1 = 1` in enum `%0`.",EnumStringMemberInconsistentlyInitailized:"String enum members need to consistently either all use initializers, or use no initializers, in enum `%0`.",ImportTypeShorthandOnlyInPureImport:"The `type` and `typeof` keywords on named imports can only be used on regular `import` statements. It cannot be used with `import type` or `import typeof` statements",InexactInsideExact:"Explicit inexact syntax cannot appear inside an explicit exact object type",InexactInsideNonObject:"Explicit inexact syntax cannot appear in class or interface definitions",InexactVariance:"Explicit inexact syntax cannot have variance",InvalidNonTypeImportInDeclareModule:"Imports within a `declare module` body must always be `import type` or `import typeof`",MissingTypeParamDefault:"Type parameter declaration needs a default, since a preceding type parameter declaration has a default.",NestedDeclareModule:"`declare module` cannot be used inside another `declare module`",NestedFlowComment:"Cannot have a flow comment inside another flow comment",OptionalBindingPattern:"A binding pattern parameter cannot be optional in an implementation signature.",SpreadVariance:"Spread properties cannot have variance",TypeBeforeInitializer:"Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`",TypeCastInPattern:"The type cast expression is expected to be wrapped with parenthesis",UnexpectedExplicitInexactInObject:"Explicit inexact syntax must appear at the end of an inexact object",UnexpectedReservedType:"Unexpected reserved type %0",UnexpectedReservedUnderscore:"`_` is only allowed as a type argument to call or new",UnexpectedSpaceBetweenModuloChecks:"Spaces between ´%´ and ´checks´ are not allowed here.",UnexpectedSpreadType:"Spread operator cannot appear in class or interface definitions",UnexpectedSubtractionOperand:'Unexpected token, expected "number" or "bigint"',UnexpectedTokenAfterTypeParameter:"Expected an arrow function after this type parameter declaration",UnsupportedDeclareExportKind:"`declare export %0` is not supported. Use `%1` instead",UnsupportedStatementInDeclareModule:"Only declares and type imports are allowed inside declare module",UnterminatedFlowComment:"Unterminated flow-comment"});function Hb(e){return"type"===e.importKind||"typeof"===e.importKind}function qb(e){return(e.type===lb.name||!!e.type.keyword)&&"from"!==e.value}var Kb={const:"declare export var",let:"declare export var",type:"export type",interface:"export interface"};var zb=/\*?\s*@((?:no)?flow)\b/,Xb={quot:'"',amp:"&",apos:"'",lt:"<",gt:">",nbsp:" ",iexcl:"¡",cent:"¢",pound:"£",curren:"¤",yen:"¥",brvbar:"¦",sect:"§",uml:"¨",copy:"©",ordf:"ª",laquo:"«",not:"¬",shy:"­",reg:"®",macr:"¯",deg:"°",plusmn:"±",sup2:"²",sup3:"³",acute:"´",micro:"µ",para:"¶",middot:"·",cedil:"¸",sup1:"¹",ordm:"º",raquo:"»",frac14:"¼",frac12:"½",frac34:"¾",iquest:"¿",Agrave:"À",Aacute:"Á",Acirc:"Â",Atilde:"Ã",Auml:"Ä",Aring:"Å",AElig:"Æ",Ccedil:"Ç",Egrave:"È",Eacute:"É",Ecirc:"Ê",Euml:"Ë",Igrave:"Ì",Iacute:"Í",Icirc:"Î",Iuml:"Ï",ETH:"Ð",Ntilde:"Ñ",Ograve:"Ò",Oacute:"Ó",Ocirc:"Ô",Otilde:"Õ",Ouml:"Ö",times:"×",Oslash:"Ø",Ugrave:"Ù",Uacute:"Ú",Ucirc:"Û",Uuml:"Ü",Yacute:"Ý",THORN:"Þ",szlig:"ß",agrave:"à",aacute:"á",acirc:"â",atilde:"ã",auml:"ä",aring:"å",aelig:"æ",ccedil:"ç",egrave:"è",eacute:"é",ecirc:"ê",euml:"ë",igrave:"ì",iacute:"í",icirc:"î",iuml:"ï",eth:"ð",ntilde:"ñ",ograve:"ò",oacute:"ó",ocirc:"ô",otilde:"õ",ouml:"ö",divide:"÷",oslash:"ø",ugrave:"ù",uacute:"ú",ucirc:"û",uuml:"ü",yacute:"ý",thorn:"þ",yuml:"ÿ",OElig:"Œ",oelig:"œ",Scaron:"Š",scaron:"š",Yuml:"Ÿ",fnof:"ƒ",circ:"ˆ",tilde:"˜",Alpha:"Α",Beta:"Β",Gamma:"Γ",Delta:"Δ",Epsilon:"Ε",Zeta:"Ζ",Eta:"Η",Theta:"Θ",Iota:"Ι",Kappa:"Κ",Lambda:"Λ",Mu:"Μ",Nu:"Ν",Xi:"Ξ",Omicron:"Ο",Pi:"Π",Rho:"Ρ",Sigma:"Σ",Tau:"Τ",Upsilon:"Υ",Phi:"Φ",Chi:"Χ",Psi:"Ψ",Omega:"Ω",alpha:"α",beta:"β",gamma:"γ",delta:"δ",epsilon:"ε",zeta:"ζ",eta:"η",theta:"θ",iota:"ι",kappa:"κ",lambda:"λ",mu:"μ",nu:"ν",xi:"ξ",omicron:"ο",pi:"π",rho:"ρ",sigmaf:"ς",sigma:"σ",tau:"τ",upsilon:"υ",phi:"φ",chi:"χ",psi:"ψ",omega:"ω",thetasym:"ϑ",upsih:"ϒ",piv:"ϖ",ensp:" ",emsp:" ",thinsp:" ",zwnj:"‌",zwj:"‍",lrm:"‎",rlm:"‏",ndash:"–",mdash:"—",lsquo:"‘",rsquo:"’",sbquo:"‚",ldquo:"“",rdquo:"”",bdquo:"„",dagger:"†",Dagger:"‡",bull:"•",hellip:"…",permil:"‰",prime:"′",Prime:"″",lsaquo:"‹",rsaquo:"›",oline:"‾",frasl:"⁄",euro:"€",image:"ℑ",weierp:"℘",real:"ℜ",trade:"™",alefsym:"ℵ",larr:"←",uarr:"↑",rarr:"→",darr:"↓",harr:"↔",crarr:"↵",lArr:"⇐",uArr:"⇑",rArr:"⇒",dArr:"⇓",hArr:"⇔",forall:"∀",part:"∂",exist:"∃",empty:"∅",nabla:"∇",isin:"∈",notin:"∉",ni:"∋",prod:"∏",sum:"∑",minus:"−",lowast:"∗",radic:"√",prop:"∝",infin:"∞",ang:"∠",and:"∧",or:"∨",cap:"∩",cup:"∪",int:"∫",there4:"∴",sim:"∼",cong:"≅",asymp:"≈",ne:"≠",equiv:"≡",le:"≤",ge:"≥",sub:"⊂",sup:"⊃",nsub:"⊄",sube:"⊆",supe:"⊇",oplus:"⊕",otimes:"⊗",perp:"⊥",sdot:"⋅",lceil:"⌈",rceil:"⌉",lfloor:"⌊",rfloor:"⌋",lang:"〈",rang:"〉",loz:"◊",spades:"♠",clubs:"♣",hearts:"♥",diams:"♦"},Yb=/^[\da-fA-F]+$/,Jb=/^\d+$/,$b=Object.freeze({AttributeIsEmpty:"JSX attributes must only be assigned a non-empty expression",MissingClosingTagFragment:"Expected corresponding JSX closing tag for <>",MissingClosingTagElement:"Expected corresponding JSX closing tag for <%0>",UnsupportedJsxValue:"JSX value should be either an expression or a quoted JSX text",UnterminatedJsxContent:"Unterminated JSX contents",UnwrappedAdjacentJSXElements:"Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?"});function Qb(e){return!!e&&("JSXOpeningFragment"===e.type||"JSXClosingFragment"===e.type)}function Zb(e){if("JSXIdentifier"===e.type)return e.name;if("JSXNamespacedName"===e.type)return e.namespace.name+":"+e.name.name;if("JSXMemberExpression"===e.type)return Zb(e.object)+"."+Zb(e.property);throw new Error("Node had unexpected type: "+e.type)}Sb.j_oTag=new wb("<tag",!1),Sb.j_cTag=new wb("</tag",!1),Sb.j_expr=new wb("<tag>...</tag>",!0,!0),lb.jsxName=new ib("jsxName"),lb.jsxText=new ib("jsxText",{beforeExpr:!0}),lb.jsxTagStart=new ib("jsxTagStart",{startsExpr:!0}),lb.jsxTagEnd=new ib("jsxTagEnd"),lb.jsxTagStart.updateContext=function(){this.state.context.push(Sb.j_expr),this.state.context.push(Sb.j_oTag),this.state.exprAllowed=!1},lb.jsxTagEnd.updateContext=function(e){var t=this.state.context.pop();t===Sb.j_oTag&&e===lb.slash||t===Sb.j_cTag?(this.state.context.pop(),this.state.exprAllowed=this.curContext()===Sb.j_expr):this.state.exprAllowed=!0};var ex=function(e){this.var=[],this.lexical=[],this.functions=[],this.flags=e},tx=function(){function e(e,t){this.scopeStack=[],this.undefinedExports=new Map,this.undefinedPrivateNames=new Map,this.raise=e,this.inModule=t}var t=e.prototype;return t.createScope=function(e){return new ex(e)},t.enter=function(e){this.scopeStack.push(this.createScope(e))},t.exit=function(){this.scopeStack.pop()},t.treatFunctionsAsVarInScope=function(e){return!!(2&e.flags||!this.inModule&&1&e.flags)},t.declareName=function(e,t,r){var n=this.currentScope();if(8&t||16&t)this.checkRedeclarationInScope(n,e,t,r),16&t?n.functions.push(e):n.lexical.push(e),8&t&&this.maybeExportDefined(n,e);else if(4&t)for(var a=this.scopeStack.length-1;a>=0&&(n=this.scopeStack[a],this.checkRedeclarationInScope(n,e,t,r),n.var.push(e),this.maybeExportDefined(n,e),!(131&n.flags));--a);this.inModule&&1&n.flags&&this.undefinedExports.delete(e)},t.maybeExportDefined=function(e,t){this.inModule&&1&e.flags&&this.undefinedExports.delete(t)},t.checkRedeclarationInScope=function(e,t,r,n){this.isRedeclaredInScope(e,t,r)&&this.raise(n,xb.VarRedeclaration,t)},t.isRedeclaredInScope=function(e,t,r){return!!(1&r)&&(8&r?e.lexical.indexOf(t)>-1||e.functions.indexOf(t)>-1||e.var.indexOf(t)>-1:16&r?e.lexical.indexOf(t)>-1||!this.treatFunctionsAsVarInScope(e)&&e.var.indexOf(t)>-1:e.lexical.indexOf(t)>-1&&!(8&e.flags&&e.lexical[0]===t)||!this.treatFunctionsAsVarInScope(e)&&e.functions.indexOf(t)>-1)},t.checkLocalExport=function(e){-1===this.scopeStack[0].lexical.indexOf(e.name)&&-1===this.scopeStack[0].var.indexOf(e.name)&&-1===this.scopeStack[0].functions.indexOf(e.name)&&this.undefinedExports.set(e.name,e.start)},t.currentScope=function(){return this.scopeStack[this.scopeStack.length-1]},t.currentVarScope=function(){for(var e=this.scopeStack.length-1;;e--){var t=this.scopeStack[e];if(131&t.flags)return t}},t.currentThisScope=function(){for(var e=this.scopeStack.length-1;;e--){var t=this.scopeStack[e];if((131&t.flags||64&t.flags)&&!(4&t.flags))return t}},n(e,[{key:"inFunction",get:function(){return(2&this.currentVarScope().flags)>0}},{key:"allowSuper",get:function(){return(16&this.currentThisScope().flags)>0}},{key:"allowDirectSuper",get:function(){return(32&this.currentThisScope().flags)>0}},{key:"inClass",get:function(){return(64&this.currentThisScope().flags)>0}},{key:"inNonArrowFunction",get:function(){return(2&this.currentThisScope().flags)>0}},{key:"treatFunctionsAsVar",get:function(){return this.treatFunctionsAsVarInScope(this.currentScope())}}]),e}(),rx=function(e){function t(){for(var t,r=arguments.length,n=new Array(r),a=0;a<r;a++)n[a]=arguments[a];return(t=e.call.apply(e,[this].concat(n))||this).types=[],t.enums=[],t.constEnums=[],t.classes=[],t.exportOnlyBindings=[],t}return a(t,e),t}(ex),nx=function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.createScope=function(e){return new rx(e)},r.declareName=function(t,r,n){var a=this.currentScope();if(1024&r)return this.maybeExportDefined(a,t),void a.exportOnlyBindings.push(t);e.prototype.declareName.apply(this,arguments),2&r&&(1&r||(this.checkRedeclarationInScope(a,t,r,n),this.maybeExportDefined(a,t)),a.types.push(t)),256&r&&a.enums.push(t),512&r&&a.constEnums.push(t),128&r&&a.classes.push(t)},r.isRedeclaredInScope=function(t,r,n){if(t.enums.indexOf(r)>-1){if(256&n){var a=!!(512&n),s=t.constEnums.indexOf(r)>-1;return a!==s}return!0}return 128&n&&t.classes.indexOf(r)>-1?t.lexical.indexOf(r)>-1&&!!(1&n):!!(2&n&&t.types.indexOf(r)>-1)||e.prototype.isRedeclaredInScope.apply(this,arguments)},r.checkLocalExport=function(t){-1===this.scopeStack[0].types.indexOf(t.name)&&-1===this.scopeStack[0].exportOnlyBindings.indexOf(t.name)&&e.prototype.checkLocalExport.call(this,t)},t}(tx),ax=function(){function e(){this.stacks=[]}var t=e.prototype;return t.enter=function(e){this.stacks.push(e)},t.exit=function(){this.stacks.pop()},t.currentFlags=function(){return this.stacks[this.stacks.length-1]},n(e,[{key:"hasAwait",get:function(){return(2&this.currentFlags())>0}},{key:"hasYield",get:function(){return(1&this.currentFlags())>0}},{key:"hasReturn",get:function(){return(4&this.currentFlags())>0}}]),e}();function sx(e,t){return(e?2:0)|(t?1:0)}function ix(e){if(null==e)throw new Error("Unexpected "+e+" value.");return e}function ox(e){if(!e)throw new Error("Assert fail")}var ux=Object.freeze({ClassMethodHasDeclare:"Class methods cannot have the 'declare' modifier",ClassMethodHasReadonly:"Class methods cannot have the 'readonly' modifier",DeclareClassFieldHasInitializer:"'declare' class fields cannot have an initializer",DuplicateModifier:"Duplicate modifier: '%0'",EmptyHeritageClauseType:"'%0' list cannot be empty.",IndexSignatureHasAbstract:"Index signatures cannot have the 'abstract' modifier",IndexSignatureHasAccessibility:"Index signatures cannot have an accessibility modifier ('%0')",IndexSignatureHasStatic:"Index signatures cannot have the 'static' modifier",OptionalTypeBeforeRequired:"A required element cannot follow an optional element.",PatternIsOptional:"A binding pattern parameter cannot be optional in an implementation signature.",PrivateElementHasAbstract:"Private elements cannot have the 'abstract' modifier.",PrivateElementHasAccessibility:"Private elements cannot have an accessibility modifier ('%0')",TemplateTypeHasSubstitution:"Template literal types cannot have any substitution",TypeAnnotationAfterAssign:"Type annotations must come before default assignments, e.g. instead of `age = 25: number` use `age: number = 25`",UnexpectedReadonly:"'readonly' type modifier is only permitted on array and tuple literal types.",UnexpectedTypeAnnotation:"Did not expect a type annotation here.",UnexpectedTypeCastInParameter:"Unexpected type cast in parameter position.",UnsupportedImportTypeArgument:"Argument in a type import must be a string literal",UnsupportedParameterPropertyKind:"A parameter property may not be declared using a binding pattern.",UnsupportedSignatureParameterKind:"Name in a signature must be an Identifier, ObjectPattern or ArrayPattern, instead got %0"});lb.placeholder=new ib("%%",{startsExpr:!0});function cx(e,t){return e.some((function(e){return Array.isArray(e)?e[0]===t:e===t}))}function lx(e,t,r){var n=e.find((function(e){return Array.isArray(e)?e[0]===t:e===t}));return n&&Array.isArray(n)?n[1][r]:null}var px=["minimal","smart","fsharp"];var dx={estree:function(e){return function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.estreeParseRegExpLiteral=function(e){var t=e.pattern,r=e.flags,n=null;try{n=new RegExp(t,r)}catch(e){}var a=this.estreeParseLiteral(n);return a.regex={pattern:t,flags:r},a},r.estreeParseBigIntLiteral=function(e){var t="undefined"!=typeof BigInt?BigInt(e):null,r=this.estreeParseLiteral(t);return r.bigint=String(r.value||e),r},r.estreeParseLiteral=function(e){return this.parseLiteral(e,"Literal")},r.directiveToStmt=function(e){var t=e.value,r=this.startNodeAt(e.start,e.loc.start),n=this.startNodeAt(t.start,t.loc.start);return n.value=t.value,n.raw=t.extra.raw,r.expression=this.finishNodeAt(n,"Literal",t.end,t.loc.end),r.directive=t.extra.raw.slice(1,-1),this.finishNodeAt(r,"ExpressionStatement",e.end,e.loc.end)},r.initFunction=function(t,r){e.prototype.initFunction.call(this,t,r),t.expression=!1},r.checkDeclaration=function(t){Ab(t)?this.checkDeclaration(t.value):e.prototype.checkDeclaration.call(this,t)},r.checkGetterSetterParams=function(e){var t=e,r="get"===t.kind?0:1,n=t.start;t.value.params.length!==r?"get"===e.kind?this.raise(n,xb.BadGetterArity):this.raise(n,xb.BadSetterArity):"set"===t.kind&&"RestElement"===t.value.params[0].type&&this.raise(n,xb.BadSetterRestParameter)},r.checkLVal=function(t,r,n,a,s){var i=this;switch(void 0===r&&(r=64),t.type){case"ObjectPattern":t.properties.forEach((function(e){i.checkLVal("Property"===e.type?e.value:e,r,n,"object destructuring pattern",s)}));break;default:e.prototype.checkLVal.call(this,t,r,n,a,s)}},r.checkDuplicatedProto=function(e,t,r){if(!("SpreadElement"===e.type||e.computed||e.method||e.shorthand)){var n=e.key;"__proto__"===("Identifier"===n.type?n.name:String(n.value))&&"init"===e.kind&&(t.used&&(r&&-1===r.doubleProto?r.doubleProto=n.start:this.raise(n.start,xb.DuplicateProto)),t.used=!0)}},r.isValidDirective=function(e){return!("ExpressionStatement"!==e.type||"Literal"!==e.expression.type||"string"!=typeof e.expression.value||e.expression.extra&&e.expression.extra.parenthesized)},r.stmtToDirective=function(t){var r=e.prototype.stmtToDirective.call(this,t),n=t.expression.value;return r.value.value=n,r},r.parseBlockBody=function(t,r,n,a){var s=this;e.prototype.parseBlockBody.call(this,t,r,n,a);var i=t.directives.map((function(e){return s.directiveToStmt(e)}));t.body=i.concat(t.body),delete t.directives},r.pushClassMethod=function(e,t,r,n,a,s){this.parseMethod(t,r,n,a,s,"ClassMethod",!0),t.typeParameters&&(t.value.typeParameters=t.typeParameters,delete t.typeParameters),e.body.push(t)},r.parseExprAtom=function(t){switch(this.state.type){case lb.num:case lb.string:return this.estreeParseLiteral(this.state.value);case lb.regexp:return this.estreeParseRegExpLiteral(this.state.value);case lb.bigint:return this.estreeParseBigIntLiteral(this.state.value);case lb._null:return this.estreeParseLiteral(null);case lb._true:return this.estreeParseLiteral(!0);case lb._false:return this.estreeParseLiteral(!1);default:return e.prototype.parseExprAtom.call(this,t)}},r.parseLiteral=function(t,r,n,a){var s=e.prototype.parseLiteral.call(this,t,r,n,a);return s.raw=s.extra.raw,delete s.extra,s},r.parseFunctionBody=function(t,r,n){void 0===n&&(n=!1),e.prototype.parseFunctionBody.call(this,t,r,n),t.expression="BlockStatement"!==t.body.type},r.parseMethod=function(t,r,n,a,s,i,o){void 0===o&&(o=!1);var u=this.startNode();return u.kind=t.kind,(u=e.prototype.parseMethod.call(this,u,r,n,a,s,i,o)).type="FunctionExpression",delete u.kind,t.value=u,i="ClassMethod"===i?"MethodDefinition":i,this.finishNode(t,i)},r.parseObjectMethod=function(t,r,n,a,s){var i=e.prototype.parseObjectMethod.call(this,t,r,n,a,s);return i&&(i.type="Property","method"===i.kind&&(i.kind="init"),i.shorthand=!1),i},r.parseObjectProperty=function(t,r,n,a,s){var i=e.prototype.parseObjectProperty.call(this,t,r,n,a,s);return i&&(i.kind="init",i.type="Property"),i},r.toAssignable=function(t){return Ab(t)?(this.toAssignable(t.value),t):e.prototype.toAssignable.call(this,t)},r.toAssignableObjectExpressionProp=function(t,r){if("get"===t.kind||"set"===t.kind)throw this.raise(t.key.start,xb.PatternHasAccessor);if(t.method)throw this.raise(t.key.start,xb.PatternHasMethod);e.prototype.toAssignableObjectExpressionProp.call(this,t,r)},r.finishCallExpression=function(t,r){return e.prototype.finishCallExpression.call(this,t,r),"Import"===t.callee.type&&(t.type="ImportExpression",t.source=t.arguments[0],delete t.arguments,delete t.callee),t},r.toReferencedListDeep=function(t,r){t&&e.prototype.toReferencedListDeep.call(this,t,r)},t}(e)},jsx:function(e){return function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.jsxReadToken=function(){for(var t="",r=this.state.pos;;){if(this.state.pos>=this.length)throw this.raise(this.state.start,$b.UnterminatedJsxContent);var n=this.input.charCodeAt(this.state.pos);switch(n){case 60:case 123:return this.state.pos===this.state.start?60===n&&this.state.exprAllowed?(++this.state.pos,this.finishToken(lb.jsxTagStart)):e.prototype.getTokenFromCode.call(this,n):(t+=this.input.slice(r,this.state.pos),this.finishToken(lb.jsxText,t));case 38:t+=this.input.slice(r,this.state.pos),t+=this.jsxReadEntity(),r=this.state.pos;break;default:fb(n)?(t+=this.input.slice(r,this.state.pos),t+=this.jsxReadNewLine(!0),r=this.state.pos):++this.state.pos}}},r.jsxReadNewLine=function(e){var t,r=this.input.charCodeAt(this.state.pos);return++this.state.pos,13===r&&10===this.input.charCodeAt(this.state.pos)?(++this.state.pos,t=e?"\n":"\r\n"):t=String.fromCharCode(r),++this.state.curLine,this.state.lineStart=this.state.pos,t},r.jsxReadString=function(e){for(var t="",r=++this.state.pos;;){if(this.state.pos>=this.length)throw this.raise(this.state.start,xb.UnterminatedString);var n=this.input.charCodeAt(this.state.pos);if(n===e)break;38===n?(t+=this.input.slice(r,this.state.pos),t+=this.jsxReadEntity(),r=this.state.pos):fb(n)?(t+=this.input.slice(r,this.state.pos),t+=this.jsxReadNewLine(!1),r=this.state.pos):++this.state.pos}return t+=this.input.slice(r,this.state.pos++),this.finishToken(lb.string,t)},r.jsxReadEntity=function(){for(var e,t="",r=0,n=this.input[this.state.pos],a=++this.state.pos;this.state.pos<this.length&&r++<10;){if(";"===(n=this.input[this.state.pos++])){"#"===t[0]?"x"===t[1]?(t=t.substr(2),Yb.test(t)&&(e=String.fromCodePoint(parseInt(t,16)))):(t=t.substr(1),Jb.test(t)&&(e=String.fromCodePoint(parseInt(t,10)))):e=Xb[t];break}t+=n}return e||(this.state.pos=a,"&")},r.jsxReadWord=function(){var e,t=this.state.pos;do{e=this.input.charCodeAt(++this.state.pos)}while(Gb(e)||45===e);return this.finishToken(lb.jsxName,this.input.slice(t,this.state.pos))},r.jsxParseIdentifier=function(){var e=this.startNode();return this.match(lb.jsxName)?e.name=this.state.value:this.state.type.keyword?e.name=this.state.type.keyword:this.unexpected(),this.next(),this.finishNode(e,"JSXIdentifier")},r.jsxParseNamespacedName=function(){var e=this.state.start,t=this.state.startLoc,r=this.jsxParseIdentifier();if(!this.eat(lb.colon))return r;var n=this.startNodeAt(e,t);return n.namespace=r,n.name=this.jsxParseIdentifier(),this.finishNode(n,"JSXNamespacedName")},r.jsxParseElementName=function(){var e=this.state.start,t=this.state.startLoc,r=this.jsxParseNamespacedName();if("JSXNamespacedName"===r.type)return r;for(;this.eat(lb.dot);){var n=this.startNodeAt(e,t);n.object=r,n.property=this.jsxParseIdentifier(),r=this.finishNode(n,"JSXMemberExpression")}return r},r.jsxParseAttributeValue=function(){var e;switch(this.state.type){case lb.braceL:return e=this.startNode(),this.next(),"JSXEmptyExpression"===(e=this.jsxParseExpressionContainer(e)).expression.type&&this.raise(e.start,$b.AttributeIsEmpty),e;case lb.jsxTagStart:case lb.string:return this.parseExprAtom();default:throw this.raise(this.state.start,$b.UnsupportedJsxValue)}},r.jsxParseEmptyExpression=function(){var e=this.startNodeAt(this.state.lastTokEnd,this.state.lastTokEndLoc);return this.finishNodeAt(e,"JSXEmptyExpression",this.state.start,this.state.startLoc)},r.jsxParseSpreadChild=function(e){return this.next(),e.expression=this.parseExpression(),this.expect(lb.braceR),this.finishNode(e,"JSXSpreadChild")},r.jsxParseExpressionContainer=function(e){return this.match(lb.braceR)?e.expression=this.jsxParseEmptyExpression():e.expression=this.parseExpression(),this.expect(lb.braceR),this.finishNode(e,"JSXExpressionContainer")},r.jsxParseAttribute=function(){var e=this.startNode();return this.eat(lb.braceL)?(this.expect(lb.ellipsis),e.argument=this.parseMaybeAssign(),this.expect(lb.braceR),this.finishNode(e,"JSXSpreadAttribute")):(e.name=this.jsxParseNamespacedName(),e.value=this.eat(lb.eq)?this.jsxParseAttributeValue():null,this.finishNode(e,"JSXAttribute"))},r.jsxParseOpeningElementAt=function(e,t){var r=this.startNodeAt(e,t);return this.match(lb.jsxTagEnd)?(this.expect(lb.jsxTagEnd),this.finishNode(r,"JSXOpeningFragment")):(r.name=this.jsxParseElementName(),this.jsxParseOpeningElementAfterName(r))},r.jsxParseOpeningElementAfterName=function(e){for(var t=[];!this.match(lb.slash)&&!this.match(lb.jsxTagEnd);)t.push(this.jsxParseAttribute());return e.attributes=t,e.selfClosing=this.eat(lb.slash),this.expect(lb.jsxTagEnd),this.finishNode(e,"JSXOpeningElement")},r.jsxParseClosingElementAt=function(e,t){var r=this.startNodeAt(e,t);return this.match(lb.jsxTagEnd)?(this.expect(lb.jsxTagEnd),this.finishNode(r,"JSXClosingFragment")):(r.name=this.jsxParseElementName(),this.expect(lb.jsxTagEnd),this.finishNode(r,"JSXClosingElement"))},r.jsxParseElementAt=function(e,t){var r=this.startNodeAt(e,t),n=[],a=this.jsxParseOpeningElementAt(e,t),s=null;if(!a.selfClosing){e:for(;;)switch(this.state.type){case lb.jsxTagStart:if(e=this.state.start,t=this.state.startLoc,this.next(),this.eat(lb.slash)){s=this.jsxParseClosingElementAt(e,t);break e}n.push(this.jsxParseElementAt(e,t));break;case lb.jsxText:n.push(this.parseExprAtom());break;case lb.braceL:var i=this.startNode();this.next(),this.match(lb.ellipsis)?n.push(this.jsxParseSpreadChild(i)):n.push(this.jsxParseExpressionContainer(i));break;default:throw this.unexpected()}Qb(a)&&!Qb(s)?this.raise(s.start,$b.MissingClosingTagFragment):!Qb(a)&&Qb(s)?this.raise(s.start,$b.MissingClosingTagElement,Zb(a.name)):Qb(a)||Qb(s)||Zb(s.name)!==Zb(a.name)&&this.raise(s.start,$b.MissingClosingTagElement,Zb(a.name))}if(Qb(a)?(r.openingFragment=a,r.closingFragment=s):(r.openingElement=a,r.closingElement=s),r.children=n,this.isRelational("<"))throw this.raise(this.state.start,$b.UnwrappedAdjacentJSXElements);return Qb(a)?this.finishNode(r,"JSXFragment"):this.finishNode(r,"JSXElement")},r.jsxParseElement=function(){var e=this.state.start,t=this.state.startLoc;return this.next(),this.jsxParseElementAt(e,t)},r.parseExprAtom=function(t){return this.match(lb.jsxText)?this.parseLiteral(this.state.value,"JSXText"):this.match(lb.jsxTagStart)?this.jsxParseElement():this.isRelational("<")&&33!==this.input.charCodeAt(this.state.pos)?(this.finishToken(lb.jsxTagStart),this.jsxParseElement()):e.prototype.parseExprAtom.call(this,t)},r.getTokenFromCode=function(t){if(this.state.inPropertyName)return e.prototype.getTokenFromCode.call(this,t);var r=this.curContext();if(r===Sb.j_expr)return this.jsxReadToken();if(r===Sb.j_oTag||r===Sb.j_cTag){if(Ub(t))return this.jsxReadWord();if(62===t)return++this.state.pos,this.finishToken(lb.jsxTagEnd);if((34===t||39===t)&&r===Sb.j_oTag)return this.jsxReadString(t)}return 60===t&&this.state.exprAllowed&&33!==this.input.charCodeAt(this.state.pos+1)?(++this.state.pos,this.finishToken(lb.jsxTagStart)):e.prototype.getTokenFromCode.call(this,t)},r.updateContext=function(t){if(this.match(lb.braceL)){var r=this.curContext();r===Sb.j_oTag?this.state.context.push(Sb.braceExpression):r===Sb.j_expr?this.state.context.push(Sb.templateQuasi):e.prototype.updateContext.call(this,t),this.state.exprAllowed=!0}else{if(!this.match(lb.slash)||t!==lb.jsxTagStart)return e.prototype.updateContext.call(this,t);this.state.context.length-=2,this.state.context.push(Sb.j_cTag),this.state.exprAllowed=!1}},t}(e)},flow:function(e){return function(e){function t(t,r){var n;return(n=e.call(this,t,r)||this).flowPragma=void 0,n}a(t,e);var r=t.prototype;return r.shouldParseTypes=function(){return this.getPluginOption("flow","all")||"flow"===this.flowPragma},r.shouldParseEnums=function(){return!!this.getPluginOption("flow","enums")},r.finishToken=function(t,r){return t!==lb.string&&t!==lb.semi&&t!==lb.interpreterDirective&&void 0===this.flowPragma&&(this.flowPragma=null),e.prototype.finishToken.call(this,t,r)},r.addComment=function(t){if(void 0===this.flowPragma){var r=zb.exec(t.value);if(r)if("flow"===r[1])this.flowPragma="flow";else{if("noflow"!==r[1])throw new Error("Unexpected flow pragma");this.flowPragma="noflow"}else;}return e.prototype.addComment.call(this,t)},r.flowParseTypeInitialiser=function(e){var t=this.state.inType;this.state.inType=!0,this.expect(e||lb.colon);var r=this.flowParseType();return this.state.inType=t,r},r.flowParsePredicate=function(){var e=this.startNode(),t=this.state.startLoc,r=this.state.start;this.expect(lb.modulo);var n=this.state.startLoc;return this.expectContextual("checks"),t.line===n.line&&t.column===n.column-1||this.raise(r,Wb.UnexpectedSpaceBetweenModuloChecks),this.eat(lb.parenL)?(e.value=this.parseExpression(),this.expect(lb.parenR),this.finishNode(e,"DeclaredPredicate")):this.finishNode(e,"InferredPredicate")},r.flowParseTypeAndPredicateInitialiser=function(){var e=this.state.inType;this.state.inType=!0,this.expect(lb.colon);var t=null,r=null;return this.match(lb.modulo)?(this.state.inType=e,r=this.flowParsePredicate()):(t=this.flowParseType(),this.state.inType=e,this.match(lb.modulo)&&(r=this.flowParsePredicate())),[t,r]},r.flowParseDeclareClass=function(e){return this.next(),this.flowParseInterfaceish(e,!0),this.finishNode(e,"DeclareClass")},r.flowParseDeclareFunction=function(e){this.next();var t=e.id=this.parseIdentifier(),r=this.startNode(),n=this.startNode();this.isRelational("<")?r.typeParameters=this.flowParseTypeParameterDeclaration():r.typeParameters=null,this.expect(lb.parenL);var a=this.flowParseFunctionTypeParams();r.params=a.params,r.rest=a.rest,this.expect(lb.parenR);var s=this.flowParseTypeAndPredicateInitialiser();return r.returnType=s[0],e.predicate=s[1],n.typeAnnotation=this.finishNode(r,"FunctionTypeAnnotation"),t.typeAnnotation=this.finishNode(n,"TypeAnnotation"),this.resetEndLocation(t),this.semicolon(),this.finishNode(e,"DeclareFunction")},r.flowParseDeclare=function(e,t){if(this.match(lb._class))return this.flowParseDeclareClass(e);if(this.match(lb._function))return this.flowParseDeclareFunction(e);if(this.match(lb._var))return this.flowParseDeclareVariable(e);if(this.eatContextual("module"))return this.match(lb.dot)?this.flowParseDeclareModuleExports(e):(t&&this.raise(this.state.lastTokStart,Wb.NestedDeclareModule),this.flowParseDeclareModule(e));if(this.isContextual("type"))return this.flowParseDeclareTypeAlias(e);if(this.isContextual("opaque"))return this.flowParseDeclareOpaqueType(e);if(this.isContextual("interface"))return this.flowParseDeclareInterface(e);if(this.match(lb._export))return this.flowParseDeclareExportDeclaration(e,t);throw this.unexpected()},r.flowParseDeclareVariable=function(e){return this.next(),e.id=this.flowParseTypeAnnotatableIdentifier(!0),this.scope.declareName(e.id.name,5,e.id.start),this.semicolon(),this.finishNode(e,"DeclareVariable")},r.flowParseDeclareModule=function(e){var t=this;this.scope.enter(0),this.match(lb.string)?e.id=this.parseExprAtom():e.id=this.parseIdentifier();var r=e.body=this.startNode(),n=r.body=[];for(this.expect(lb.braceL);!this.match(lb.braceR);){var a=this.startNode();this.match(lb._import)?(this.next(),this.isContextual("type")||this.match(lb._typeof)||this.raise(this.state.lastTokStart,Wb.InvalidNonTypeImportInDeclareModule),this.parseImport(a)):(this.expectContextual("declare",Wb.UnsupportedStatementInDeclareModule),a=this.flowParseDeclare(a,!0)),n.push(a)}this.scope.exit(),this.expect(lb.braceR),this.finishNode(r,"BlockStatement");var s=null,i=!1;return n.forEach((function(e){!function(e){return"DeclareExportAllDeclaration"===e.type||"DeclareExportDeclaration"===e.type&&(!e.declaration||"TypeAlias"!==e.declaration.type&&"InterfaceDeclaration"!==e.declaration.type)}(e)?"DeclareModuleExports"===e.type&&(i&&t.raise(e.start,Wb.DuplicateDeclareModuleExports),"ES"===s&&t.raise(e.start,Wb.AmbiguousDeclareModuleKind),s="CommonJS",i=!0):("CommonJS"===s&&t.raise(e.start,Wb.AmbiguousDeclareModuleKind),s="ES")})),e.kind=s||"CommonJS",this.finishNode(e,"DeclareModule")},r.flowParseDeclareExportDeclaration=function(e,t){if(this.expect(lb._export),this.eat(lb._default))return this.match(lb._function)||this.match(lb._class)?e.declaration=this.flowParseDeclare(this.startNode()):(e.declaration=this.flowParseType(),this.semicolon()),e.default=!0,this.finishNode(e,"DeclareExportDeclaration");if(this.match(lb._const)||this.isLet()||(this.isContextual("type")||this.isContextual("interface"))&&!t){var r=this.state.value,n=Kb[r];throw this.raise(this.state.start,Wb.UnsupportedDeclareExportKind,r,n)}if(this.match(lb._var)||this.match(lb._function)||this.match(lb._class)||this.isContextual("opaque"))return e.declaration=this.flowParseDeclare(this.startNode()),e.default=!1,this.finishNode(e,"DeclareExportDeclaration");if(this.match(lb.star)||this.match(lb.braceL)||this.isContextual("interface")||this.isContextual("type")||this.isContextual("opaque"))return"ExportNamedDeclaration"===(e=this.parseExport(e)).type&&(e.type="ExportDeclaration",e.default=!1,delete e.exportKind),e.type="Declare"+e.type,e;throw this.unexpected()},r.flowParseDeclareModuleExports=function(e){return this.next(),this.expectContextual("exports"),e.typeAnnotation=this.flowParseTypeAnnotation(),this.semicolon(),this.finishNode(e,"DeclareModuleExports")},r.flowParseDeclareTypeAlias=function(e){return this.next(),this.flowParseTypeAlias(e),e.type="DeclareTypeAlias",e},r.flowParseDeclareOpaqueType=function(e){return this.next(),this.flowParseOpaqueType(e,!0),e.type="DeclareOpaqueType",e},r.flowParseDeclareInterface=function(e){return this.next(),this.flowParseInterfaceish(e),this.finishNode(e,"DeclareInterface")},r.flowParseInterfaceish=function(e,t){if(void 0===t&&(t=!1),e.id=this.flowParseRestrictedIdentifier(!t,!0),this.scope.declareName(e.id.name,t?17:9,e.id.start),this.isRelational("<")?e.typeParameters=this.flowParseTypeParameterDeclaration():e.typeParameters=null,e.extends=[],e.implements=[],e.mixins=[],this.eat(lb._extends))do{e.extends.push(this.flowParseInterfaceExtends())}while(!t&&this.eat(lb.comma));if(this.isContextual("mixins")){this.next();do{e.mixins.push(this.flowParseInterfaceExtends())}while(this.eat(lb.comma))}if(this.isContextual("implements")){this.next();do{e.implements.push(this.flowParseInterfaceExtends())}while(this.eat(lb.comma))}e.body=this.flowParseObjectType({allowStatic:t,allowExact:!1,allowSpread:!1,allowProto:t,allowInexact:!1})},r.flowParseInterfaceExtends=function(){var e=this.startNode();return e.id=this.flowParseQualifiedTypeIdentifier(),this.isRelational("<")?e.typeParameters=this.flowParseTypeParameterInstantiation():e.typeParameters=null,this.finishNode(e,"InterfaceExtends")},r.flowParseInterface=function(e){return this.flowParseInterfaceish(e),this.finishNode(e,"InterfaceDeclaration")},r.checkNotUnderscore=function(e){"_"===e&&this.raise(this.state.start,Wb.UnexpectedReservedUnderscore)},r.checkReservedType=function(e,t,r){Vb.has(e)&&this.raise(t,r?Wb.AssignReservedType:Wb.UnexpectedReservedType,e)},r.flowParseRestrictedIdentifier=function(e,t){return this.checkReservedType(this.state.value,this.state.start,t),this.parseIdentifier(e)},r.flowParseTypeAlias=function(e){return e.id=this.flowParseRestrictedIdentifier(!1,!0),this.scope.declareName(e.id.name,9,e.id.start),this.isRelational("<")?e.typeParameters=this.flowParseTypeParameterDeclaration():e.typeParameters=null,e.right=this.flowParseTypeInitialiser(lb.eq),this.semicolon(),this.finishNode(e,"TypeAlias")},r.flowParseOpaqueType=function(e,t){return this.expectContextual("type"),e.id=this.flowParseRestrictedIdentifier(!0,!0),this.scope.declareName(e.id.name,9,e.id.start),this.isRelational("<")?e.typeParameters=this.flowParseTypeParameterDeclaration():e.typeParameters=null,e.supertype=null,this.match(lb.colon)&&(e.supertype=this.flowParseTypeInitialiser(lb.colon)),e.impltype=null,t||(e.impltype=this.flowParseTypeInitialiser(lb.eq)),this.semicolon(),this.finishNode(e,"OpaqueType")},r.flowParseTypeParameter=function(e){void 0===e&&(e=!1);var t=this.state.start,r=this.startNode(),n=this.flowParseVariance(),a=this.flowParseTypeAnnotatableIdentifier();return r.name=a.name,r.variance=n,r.bound=a.typeAnnotation,this.match(lb.eq)?(this.eat(lb.eq),r.default=this.flowParseType()):e&&this.raise(t,Wb.MissingTypeParamDefault),this.finishNode(r,"TypeParameter")},r.flowParseTypeParameterDeclaration=function(){var e=this.state.inType,t=this.startNode();t.params=[],this.state.inType=!0,this.isRelational("<")||this.match(lb.jsxTagStart)?this.next():this.unexpected();var r=!1;do{var n=this.flowParseTypeParameter(r);t.params.push(n),n.default&&(r=!0),this.isRelational(">")||this.expect(lb.comma)}while(!this.isRelational(">"));return this.expectRelational(">"),this.state.inType=e,this.finishNode(t,"TypeParameterDeclaration")},r.flowParseTypeParameterInstantiation=function(){var e=this.startNode(),t=this.state.inType;e.params=[],this.state.inType=!0,this.expectRelational("<");var r=this.state.noAnonFunctionType;for(this.state.noAnonFunctionType=!1;!this.isRelational(">");)e.params.push(this.flowParseType()),this.isRelational(">")||this.expect(lb.comma);return this.state.noAnonFunctionType=r,this.expectRelational(">"),this.state.inType=t,this.finishNode(e,"TypeParameterInstantiation")},r.flowParseTypeParameterInstantiationCallOrNew=function(){var e=this.startNode(),t=this.state.inType;for(e.params=[],this.state.inType=!0,this.expectRelational("<");!this.isRelational(">");)e.params.push(this.flowParseTypeOrImplicitInstantiation()),this.isRelational(">")||this.expect(lb.comma);return this.expectRelational(">"),this.state.inType=t,this.finishNode(e,"TypeParameterInstantiation")},r.flowParseInterfaceType=function(){var e=this.startNode();if(this.expectContextual("interface"),e.extends=[],this.eat(lb._extends))do{e.extends.push(this.flowParseInterfaceExtends())}while(this.eat(lb.comma));return e.body=this.flowParseObjectType({allowStatic:!1,allowExact:!1,allowSpread:!1,allowProto:!1,allowInexact:!1}),this.finishNode(e,"InterfaceTypeAnnotation")},r.flowParseObjectPropertyKey=function(){return this.match(lb.num)||this.match(lb.string)?this.parseExprAtom():this.parseIdentifier(!0)},r.flowParseObjectTypeIndexer=function(e,t,r){return e.static=t,this.lookahead().type===lb.colon?(e.id=this.flowParseObjectPropertyKey(),e.key=this.flowParseTypeInitialiser()):(e.id=null,e.key=this.flowParseType()),this.expect(lb.bracketR),e.value=this.flowParseTypeInitialiser(),e.variance=r,this.finishNode(e,"ObjectTypeIndexer")},r.flowParseObjectTypeInternalSlot=function(e,t){return e.static=t,e.id=this.flowParseObjectPropertyKey(),this.expect(lb.bracketR),this.expect(lb.bracketR),this.isRelational("<")||this.match(lb.parenL)?(e.method=!0,e.optional=!1,e.value=this.flowParseObjectTypeMethodish(this.startNodeAt(e.start,e.loc.start))):(e.method=!1,this.eat(lb.question)&&(e.optional=!0),e.value=this.flowParseTypeInitialiser()),this.finishNode(e,"ObjectTypeInternalSlot")},r.flowParseObjectTypeMethodish=function(e){for(e.params=[],e.rest=null,e.typeParameters=null,this.isRelational("<")&&(e.typeParameters=this.flowParseTypeParameterDeclaration()),this.expect(lb.parenL);!this.match(lb.parenR)&&!this.match(lb.ellipsis);)e.params.push(this.flowParseFunctionTypeParam()),this.match(lb.parenR)||this.expect(lb.comma);return this.eat(lb.ellipsis)&&(e.rest=this.flowParseFunctionTypeParam()),this.expect(lb.parenR),e.returnType=this.flowParseTypeInitialiser(),this.finishNode(e,"FunctionTypeAnnotation")},r.flowParseObjectTypeCallProperty=function(e,t){var r=this.startNode();return e.static=t,e.value=this.flowParseObjectTypeMethodish(r),this.finishNode(e,"ObjectTypeCallProperty")},r.flowParseObjectType=function(e){var t=e.allowStatic,r=e.allowExact,n=e.allowSpread,a=e.allowProto,s=e.allowInexact,i=this.state.inType;this.state.inType=!0;var o,u,c=this.startNode();c.callProperties=[],c.properties=[],c.indexers=[],c.internalSlots=[];var l=!1;for(r&&this.match(lb.braceBarL)?(this.expect(lb.braceBarL),o=lb.braceBarR,u=!0):(this.expect(lb.braceL),o=lb.braceR,u=!1),c.exact=u;!this.match(o);){var p=!1,d=null,f=null,h=this.startNode();if(a&&this.isContextual("proto")){var m=this.lookahead();m.type!==lb.colon&&m.type!==lb.question&&(this.next(),d=this.state.start,t=!1)}if(t&&this.isContextual("static")){var y=this.lookahead();y.type!==lb.colon&&y.type!==lb.question&&(this.next(),p=!0)}var g=this.flowParseVariance();if(this.eat(lb.bracketL))null!=d&&this.unexpected(d),this.eat(lb.bracketL)?(g&&this.unexpected(g.start),c.internalSlots.push(this.flowParseObjectTypeInternalSlot(h,p))):c.indexers.push(this.flowParseObjectTypeIndexer(h,p,g));else if(this.match(lb.parenL)||this.isRelational("<"))null!=d&&this.unexpected(d),g&&this.unexpected(g.start),c.callProperties.push(this.flowParseObjectTypeCallProperty(h,p));else{var v="init";if(this.isContextual("get")||this.isContextual("set")){var b=this.lookahead();b.type!==lb.name&&b.type!==lb.string&&b.type!==lb.num||(v=this.state.value,this.next())}var x=this.flowParseObjectTypeProperty(h,p,d,g,v,n,null!=s?s:!u);null===x?(l=!0,f=this.state.lastTokStart):c.properties.push(x)}this.flowObjectTypeSemicolon(),!f||this.match(lb.braceR)||this.match(lb.braceBarR)||this.raise(f,Wb.UnexpectedExplicitInexactInObject)}this.expect(o),n&&(c.inexact=l);var E=this.finishNode(c,"ObjectTypeAnnotation");return this.state.inType=i,E},r.flowParseObjectTypeProperty=function(e,t,r,n,a,s,i){if(this.eat(lb.ellipsis))return this.match(lb.comma)||this.match(lb.semi)||this.match(lb.braceR)||this.match(lb.braceBarR)?(s?i||this.raise(this.state.lastTokStart,Wb.InexactInsideExact):this.raise(this.state.lastTokStart,Wb.InexactInsideNonObject),n&&this.raise(n.start,Wb.InexactVariance),null):(s||this.raise(this.state.lastTokStart,Wb.UnexpectedSpreadType),null!=r&&this.unexpected(r),n&&this.raise(n.start,Wb.SpreadVariance),e.argument=this.flowParseType(),this.finishNode(e,"ObjectTypeSpreadProperty"));e.key=this.flowParseObjectPropertyKey(),e.static=t,e.proto=null!=r,e.kind=a;var o=!1;return this.isRelational("<")||this.match(lb.parenL)?(e.method=!0,null!=r&&this.unexpected(r),n&&this.unexpected(n.start),e.value=this.flowParseObjectTypeMethodish(this.startNodeAt(e.start,e.loc.start)),"get"!==a&&"set"!==a||this.flowCheckGetterSetterParams(e)):("init"!==a&&this.unexpected(),e.method=!1,this.eat(lb.question)&&(o=!0),e.value=this.flowParseTypeInitialiser(),e.variance=n),e.optional=o,this.finishNode(e,"ObjectTypeProperty")},r.flowCheckGetterSetterParams=function(e){var t="get"===e.kind?0:1,r=e.start;e.value.params.length+(e.value.rest?1:0)!==t&&("get"===e.kind?this.raise(r,xb.BadGetterArity):this.raise(r,xb.BadSetterArity)),"set"===e.kind&&e.value.rest&&this.raise(r,xb.BadSetterRestParameter)},r.flowObjectTypeSemicolon=function(){this.eat(lb.semi)||this.eat(lb.comma)||this.match(lb.braceR)||this.match(lb.braceBarR)||this.unexpected()},r.flowParseQualifiedTypeIdentifier=function(e,t,r){e=e||this.state.start,t=t||this.state.startLoc;for(var n=r||this.flowParseRestrictedIdentifier(!0);this.eat(lb.dot);){var a=this.startNodeAt(e,t);a.qualification=n,a.id=this.flowParseRestrictedIdentifier(!0),n=this.finishNode(a,"QualifiedTypeIdentifier")}return n},r.flowParseGenericType=function(e,t,r){var n=this.startNodeAt(e,t);return n.typeParameters=null,n.id=this.flowParseQualifiedTypeIdentifier(e,t,r),this.isRelational("<")&&(n.typeParameters=this.flowParseTypeParameterInstantiation()),this.finishNode(n,"GenericTypeAnnotation")},r.flowParseTypeofType=function(){var e=this.startNode();return this.expect(lb._typeof),e.argument=this.flowParsePrimaryType(),this.finishNode(e,"TypeofTypeAnnotation")},r.flowParseTupleType=function(){var e=this.startNode();for(e.types=[],this.expect(lb.bracketL);this.state.pos<this.length&&!this.match(lb.bracketR)&&(e.types.push(this.flowParseType()),!this.match(lb.bracketR));)this.expect(lb.comma);return this.expect(lb.bracketR),this.finishNode(e,"TupleTypeAnnotation")},r.flowParseFunctionTypeParam=function(){var e=null,t=!1,r=null,n=this.startNode(),a=this.lookahead();return a.type===lb.colon||a.type===lb.question?(e=this.parseIdentifier(),this.eat(lb.question)&&(t=!0),r=this.flowParseTypeInitialiser()):r=this.flowParseType(),n.name=e,n.optional=t,n.typeAnnotation=r,this.finishNode(n,"FunctionTypeParam")},r.reinterpretTypeAsFunctionTypeParam=function(e){var t=this.startNodeAt(e.start,e.loc.start);return t.name=null,t.optional=!1,t.typeAnnotation=e,this.finishNode(t,"FunctionTypeParam")},r.flowParseFunctionTypeParams=function(e){void 0===e&&(e=[]);for(var t=null;!this.match(lb.parenR)&&!this.match(lb.ellipsis);)e.push(this.flowParseFunctionTypeParam()),this.match(lb.parenR)||this.expect(lb.comma);return this.eat(lb.ellipsis)&&(t=this.flowParseFunctionTypeParam()),{params:e,rest:t}},r.flowIdentToTypeAnnotation=function(e,t,r,n){switch(n.name){case"any":return this.finishNode(r,"AnyTypeAnnotation");case"bool":case"boolean":return this.finishNode(r,"BooleanTypeAnnotation");case"mixed":return this.finishNode(r,"MixedTypeAnnotation");case"empty":return this.finishNode(r,"EmptyTypeAnnotation");case"number":return this.finishNode(r,"NumberTypeAnnotation");case"string":return this.finishNode(r,"StringTypeAnnotation");default:return this.checkNotUnderscore(n.name),this.flowParseGenericType(e,t,n)}},r.flowParsePrimaryType=function(){var t,r,n=this.state.start,a=this.state.startLoc,s=this.startNode(),i=!1,o=this.state.noAnonFunctionType;switch(this.state.type){case lb.name:return this.isContextual("interface")?this.flowParseInterfaceType():this.flowIdentToTypeAnnotation(n,a,s,this.parseIdentifier());case lb.braceL:return this.flowParseObjectType({allowStatic:!1,allowExact:!1,allowSpread:!0,allowProto:!1,allowInexact:!0});case lb.braceBarL:return this.flowParseObjectType({allowStatic:!1,allowExact:!0,allowSpread:!0,allowProto:!1,allowInexact:!1});case lb.bracketL:return this.state.noAnonFunctionType=!1,r=this.flowParseTupleType(),this.state.noAnonFunctionType=o,r;case lb.relational:if("<"===this.state.value)return s.typeParameters=this.flowParseTypeParameterDeclaration(),this.expect(lb.parenL),t=this.flowParseFunctionTypeParams(),s.params=t.params,s.rest=t.rest,this.expect(lb.parenR),this.expect(lb.arrow),s.returnType=this.flowParseType(),this.finishNode(s,"FunctionTypeAnnotation");break;case lb.parenL:if(this.next(),!this.match(lb.parenR)&&!this.match(lb.ellipsis))if(this.match(lb.name)){var u=this.lookahead().type;i=u!==lb.question&&u!==lb.colon}else i=!0;if(i){if(this.state.noAnonFunctionType=!1,r=this.flowParseType(),this.state.noAnonFunctionType=o,this.state.noAnonFunctionType||!(this.match(lb.comma)||this.match(lb.parenR)&&this.lookahead().type===lb.arrow))return this.expect(lb.parenR),r;this.eat(lb.comma)}return t=r?this.flowParseFunctionTypeParams([this.reinterpretTypeAsFunctionTypeParam(r)]):this.flowParseFunctionTypeParams(),s.params=t.params,s.rest=t.rest,this.expect(lb.parenR),this.expect(lb.arrow),s.returnType=this.flowParseType(),s.typeParameters=null,this.finishNode(s,"FunctionTypeAnnotation");case lb.string:return this.parseLiteral(this.state.value,"StringLiteralTypeAnnotation");case lb._true:case lb._false:return s.value=this.match(lb._true),this.next(),this.finishNode(s,"BooleanLiteralTypeAnnotation");case lb.plusMin:if("-"===this.state.value){if(this.next(),this.match(lb.num))return this.parseLiteral(-this.state.value,"NumberLiteralTypeAnnotation",s.start,s.loc.start);if(this.match(lb.bigint))return this.parseLiteral(-this.state.value,"BigIntLiteralTypeAnnotation",s.start,s.loc.start);throw this.raise(this.state.start,Wb.UnexpectedSubtractionOperand)}throw this.unexpected();case lb.num:return this.parseLiteral(this.state.value,"NumberLiteralTypeAnnotation");case lb.bigint:return this.parseLiteral(this.state.value,"BigIntLiteralTypeAnnotation");case lb._void:return this.next(),this.finishNode(s,"VoidTypeAnnotation");case lb._null:return this.next(),this.finishNode(s,"NullLiteralTypeAnnotation");case lb._this:return this.next(),this.finishNode(s,"ThisTypeAnnotation");case lb.star:return this.next(),this.finishNode(s,"ExistsTypeAnnotation");default:if("typeof"===this.state.type.keyword)return this.flowParseTypeofType();if(this.state.type.keyword){var c=this.state.type.label;return this.next(),e.prototype.createIdentifier.call(this,s,c)}}throw this.unexpected()},r.flowParsePostfixType=function(){for(var e=this.state.start,t=this.state.startLoc,r=this.flowParsePrimaryType();this.match(lb.bracketL)&&!this.canInsertSemicolon();){var n=this.startNodeAt(e,t);n.elementType=r,this.expect(lb.bracketL),this.expect(lb.bracketR),r=this.finishNode(n,"ArrayTypeAnnotation")}return r},r.flowParsePrefixType=function(){var e=this.startNode();return this.eat(lb.question)?(e.typeAnnotation=this.flowParsePrefixType(),this.finishNode(e,"NullableTypeAnnotation")):this.flowParsePostfixType()},r.flowParseAnonFunctionWithoutParens=function(){var e=this.flowParsePrefixType();if(!this.state.noAnonFunctionType&&this.eat(lb.arrow)){var t=this.startNodeAt(e.start,e.loc.start);return t.params=[this.reinterpretTypeAsFunctionTypeParam(e)],t.rest=null,t.returnType=this.flowParseType(),t.typeParameters=null,this.finishNode(t,"FunctionTypeAnnotation")}return e},r.flowParseIntersectionType=function(){var e=this.startNode();this.eat(lb.bitwiseAND);var t=this.flowParseAnonFunctionWithoutParens();for(e.types=[t];this.eat(lb.bitwiseAND);)e.types.push(this.flowParseAnonFunctionWithoutParens());return 1===e.types.length?t:this.finishNode(e,"IntersectionTypeAnnotation")},r.flowParseUnionType=function(){var e=this.startNode();this.eat(lb.bitwiseOR);var t=this.flowParseIntersectionType();for(e.types=[t];this.eat(lb.bitwiseOR);)e.types.push(this.flowParseIntersectionType());return 1===e.types.length?t:this.finishNode(e,"UnionTypeAnnotation")},r.flowParseType=function(){var e=this.state.inType;this.state.inType=!0;var t=this.flowParseUnionType();return this.state.inType=e,this.state.exprAllowed=this.state.exprAllowed||this.state.noAnonFunctionType,t},r.flowParseTypeOrImplicitInstantiation=function(){if(this.state.type===lb.name&&"_"===this.state.value){var e=this.state.start,t=this.state.startLoc,r=this.parseIdentifier();return this.flowParseGenericType(e,t,r)}return this.flowParseType()},r.flowParseTypeAnnotation=function(){var e=this.startNode();return e.typeAnnotation=this.flowParseTypeInitialiser(),this.finishNode(e,"TypeAnnotation")},r.flowParseTypeAnnotatableIdentifier=function(e){var t=e?this.parseIdentifier():this.flowParseRestrictedIdentifier();return this.match(lb.colon)&&(t.typeAnnotation=this.flowParseTypeAnnotation(),this.resetEndLocation(t)),t},r.typeCastToParameter=function(e){return e.expression.typeAnnotation=e.typeAnnotation,this.resetEndLocation(e.expression,e.typeAnnotation.end,e.typeAnnotation.loc.end),e.expression},r.flowParseVariance=function(){var e=null;return this.match(lb.plusMin)&&(e=this.startNode(),"+"===this.state.value?e.kind="plus":e.kind="minus",this.next(),this.finishNode(e,"Variance")),e},r.parseFunctionBody=function(t,r,n){var a=this;return void 0===n&&(n=!1),r?this.forwardNoArrowParamsConversionAt(t,(function(){return e.prototype.parseFunctionBody.call(a,t,!0,n)})):e.prototype.parseFunctionBody.call(this,t,!1,n)},r.parseFunctionBodyAndFinish=function(t,r,n){if(void 0===n&&(n=!1),this.match(lb.colon)){var a=this.startNode(),s=this.flowParseTypeAndPredicateInitialiser();a.typeAnnotation=s[0],t.predicate=s[1],t.returnType=a.typeAnnotation?this.finishNode(a,"TypeAnnotation"):null}e.prototype.parseFunctionBodyAndFinish.call(this,t,r,n)},r.parseStatement=function(t,r){if(this.state.strict&&this.match(lb.name)&&"interface"===this.state.value){var n=this.startNode();return this.next(),this.flowParseInterface(n)}if(this.shouldParseEnums()&&this.isContextual("enum")){var a=this.startNode();return this.next(),this.flowParseEnumDeclaration(a)}var s=e.prototype.parseStatement.call(this,t,r);return void 0!==this.flowPragma||this.isValidDirective(s)||(this.flowPragma=null),s},r.parseExpressionStatement=function(t,r){if("Identifier"===r.type)if("declare"===r.name){if(this.match(lb._class)||this.match(lb.name)||this.match(lb._function)||this.match(lb._var)||this.match(lb._export))return this.flowParseDeclare(t)}else if(this.match(lb.name)){if("interface"===r.name)return this.flowParseInterface(t);if("type"===r.name)return this.flowParseTypeAlias(t);if("opaque"===r.name)return this.flowParseOpaqueType(t,!1)}return e.prototype.parseExpressionStatement.call(this,t,r)},r.shouldParseExportDeclaration=function(){return this.isContextual("type")||this.isContextual("interface")||this.isContextual("opaque")||this.shouldParseEnums()&&this.isContextual("enum")||e.prototype.shouldParseExportDeclaration.call(this)},r.isExportDefaultSpecifier=function(){return(!this.match(lb.name)||!("type"===this.state.value||"interface"===this.state.value||"opaque"===this.state.value||this.shouldParseEnums()&&"enum"===this.state.value))&&e.prototype.isExportDefaultSpecifier.call(this)},r.parseExportDefaultExpression=function(){if(this.shouldParseEnums()&&this.isContextual("enum")){var t=this.startNode();return this.next(),this.flowParseEnumDeclaration(t)}return e.prototype.parseExportDefaultExpression.call(this)},r.parseConditional=function(t,r,n,a,s){var i=this;if(!this.match(lb.question))return t;if(s){var o=this.tryParse((function(){return e.prototype.parseConditional.call(i,t,r,n,a)}));return o.node?(o.error&&(this.state=o.failState),o.node):(s.start=o.error.pos||this.state.start,t)}this.expect(lb.question);var u=this.state.clone(),c=this.state.noArrowAt,l=this.startNodeAt(n,a),p=this.tryParseConditionalConsequent(),d=p.consequent,f=p.failed,h=this.getArrowLikeExpressions(d),m=h[0],y=h[1];if(f||y.length>0){var g=[].concat(c);if(y.length>0){this.state=u,this.state.noArrowAt=g;for(var v=0;v<y.length;v++)g.push(y[v].start);var b=this.tryParseConditionalConsequent();d=b.consequent,f=b.failed;var x=this.getArrowLikeExpressions(d);m=x[0],y=x[1]}if(f&&m.length>1&&this.raise(u.start,Wb.AmbiguousConditionalArrow),f&&1===m.length){this.state=u,this.state.noArrowAt=g.concat(m[0].start);var E=this.tryParseConditionalConsequent();d=E.consequent,f=E.failed}}return this.getArrowLikeExpressions(d,!0),this.state.noArrowAt=c,this.expect(lb.colon),l.test=t,l.consequent=d,l.alternate=this.forwardNoArrowParamsConversionAt(l,(function(){return i.parseMaybeAssign(r,void 0,void 0,void 0)})),this.finishNode(l,"ConditionalExpression")},r.tryParseConditionalConsequent=function(){this.state.noArrowParamsConversionAt.push(this.state.start);var e=this.parseMaybeAssign(),t=!this.match(lb.colon);return this.state.noArrowParamsConversionAt.pop(),{consequent:e,failed:t}},r.getArrowLikeExpressions=function(e,t){for(var r=this,n=[e],a=[];0!==n.length;){var s=n.pop();"ArrowFunctionExpression"===s.type?(s.typeParameters||!s.returnType?this.finishArrowValidation(s):a.push(s),n.push(s.body)):"ConditionalExpression"===s.type&&(n.push(s.consequent),n.push(s.alternate))}return t?(a.forEach((function(e){return r.finishArrowValidation(e)})),[a,[]]):function(e,t){for(var r=[],n=[],a=0;a<e.length;a++)(t(e[a],a,e)?r:n).push(e[a]);return[r,n]}(a,(function(e){return e.params.every((function(e){return r.isAssignable(e,!0)}))}))},r.finishArrowValidation=function(t){var r;this.toAssignableList(t.params,null==(r=t.extra)?void 0:r.trailingComma),this.scope.enter(6),e.prototype.checkParams.call(this,t,!1,!0),this.scope.exit()},r.forwardNoArrowParamsConversionAt=function(e,t){var r;return-1!==this.state.noArrowParamsConversionAt.indexOf(e.start)?(this.state.noArrowParamsConversionAt.push(this.state.start),r=t(),this.state.noArrowParamsConversionAt.pop()):r=t(),r},r.parseParenItem=function(t,r,n){if(t=e.prototype.parseParenItem.call(this,t,r,n),this.eat(lb.question)&&(t.optional=!0,this.resetEndLocation(t)),this.match(lb.colon)){var a=this.startNodeAt(r,n);return a.expression=t,a.typeAnnotation=this.flowParseTypeAnnotation(),this.finishNode(a,"TypeCastExpression")}return t},r.assertModuleNodeAllowed=function(t){"ImportDeclaration"===t.type&&("type"===t.importKind||"typeof"===t.importKind)||"ExportNamedDeclaration"===t.type&&"type"===t.exportKind||"ExportAllDeclaration"===t.type&&"type"===t.exportKind||e.prototype.assertModuleNodeAllowed.call(this,t)},r.parseExport=function(t){var r=e.prototype.parseExport.call(this,t);return"ExportNamedDeclaration"!==r.type&&"ExportAllDeclaration"!==r.type||(r.exportKind=r.exportKind||"value"),r},r.parseExportDeclaration=function(t){if(this.isContextual("type")){t.exportKind="type";var r=this.startNode();return this.next(),this.match(lb.braceL)?(t.specifiers=this.parseExportSpecifiers(),this.parseExportFrom(t),null):this.flowParseTypeAlias(r)}if(this.isContextual("opaque")){t.exportKind="type";var n=this.startNode();return this.next(),this.flowParseOpaqueType(n,!1)}if(this.isContextual("interface")){t.exportKind="type";var a=this.startNode();return this.next(),this.flowParseInterface(a)}if(this.shouldParseEnums()&&this.isContextual("enum")){t.exportKind="value";var s=this.startNode();return this.next(),this.flowParseEnumDeclaration(s)}return e.prototype.parseExportDeclaration.call(this,t)},r.eatExportStar=function(t){return!!e.prototype.eatExportStar.apply(this,arguments)||!(!this.isContextual("type")||this.lookahead().type!==lb.star)&&(t.exportKind="type",this.next(),this.next(),!0)},r.maybeParseExportNamespaceSpecifier=function(t){var r=this.state.start,n=e.prototype.maybeParseExportNamespaceSpecifier.call(this,t);return n&&"type"===t.exportKind&&this.unexpected(r),n},r.parseClassId=function(t,r,n){e.prototype.parseClassId.call(this,t,r,n),this.isRelational("<")&&(t.typeParameters=this.flowParseTypeParameterDeclaration())},r.getTokenFromCode=function(t){var r=this.input.charCodeAt(this.state.pos+1);return 123===t&&124===r?this.finishOp(lb.braceBarL,2):!this.state.inType||62!==t&&60!==t?function(e,t){return 64===e&&64===t}(t,r)?(this.state.isIterator=!0,e.prototype.readWord.call(this)):e.prototype.getTokenFromCode.call(this,t):this.finishOp(lb.relational,1)},r.isAssignable=function(e,t){var r=this;switch(e.type){case"Identifier":case"ObjectPattern":case"ArrayPattern":case"AssignmentPattern":return!0;case"ObjectExpression":var n=e.properties.length-1;return e.properties.every((function(e,t){return"ObjectMethod"!==e.type&&(t===n||"SpreadElement"===e.type)&&r.isAssignable(e)}));case"ObjectProperty":return this.isAssignable(e.value);case"SpreadElement":return this.isAssignable(e.argument);case"ArrayExpression":return e.elements.every((function(e){return r.isAssignable(e)}));case"AssignmentExpression":return"="===e.operator;case"ParenthesizedExpression":case"TypeCastExpression":return this.isAssignable(e.expression);case"MemberExpression":case"OptionalMemberExpression":return!t;default:return!1}},r.toAssignable=function(t){return"TypeCastExpression"===t.type?e.prototype.toAssignable.call(this,this.typeCastToParameter(t)):e.prototype.toAssignable.call(this,t)},r.toAssignableList=function(t,r){for(var n=0;n<t.length;n++){var a=t[n];a&&"TypeCastExpression"===a.type&&(t[n]=this.typeCastToParameter(a))}return e.prototype.toAssignableList.call(this,t,r)},r.toReferencedList=function(e,t){for(var r=0;r<e.length;r++){var n=e[r];!n||"TypeCastExpression"!==n.type||n.extra&&n.extra.parenthesized||!(e.length>1)&&t||this.raise(n.typeAnnotation.start,Wb.TypeCastInPattern)}return e},r.checkLVal=function(t,r,n,a){if(void 0===r&&(r=64),"TypeCastExpression"!==t.type)return e.prototype.checkLVal.call(this,t,r,n,a)},r.parseClassProperty=function(t){return this.match(lb.colon)&&(t.typeAnnotation=this.flowParseTypeAnnotation()),e.prototype.parseClassProperty.call(this,t)},r.parseClassPrivateProperty=function(t){return this.match(lb.colon)&&(t.typeAnnotation=this.flowParseTypeAnnotation()),e.prototype.parseClassPrivateProperty.call(this,t)},r.isClassMethod=function(){return this.isRelational("<")||e.prototype.isClassMethod.call(this)},r.isClassProperty=function(){return this.match(lb.colon)||e.prototype.isClassProperty.call(this)},r.isNonstaticConstructor=function(t){return!this.match(lb.colon)&&e.prototype.isNonstaticConstructor.call(this,t)},r.pushClassMethod=function(t,r,n,a,s,i){r.variance&&this.unexpected(r.variance.start),delete r.variance,this.isRelational("<")&&(r.typeParameters=this.flowParseTypeParameterDeclaration()),e.prototype.pushClassMethod.call(this,t,r,n,a,s,i)},r.pushClassPrivateMethod=function(t,r,n,a){r.variance&&this.unexpected(r.variance.start),delete r.variance,this.isRelational("<")&&(r.typeParameters=this.flowParseTypeParameterDeclaration()),e.prototype.pushClassPrivateMethod.call(this,t,r,n,a)},r.parseClassSuper=function(t){if(e.prototype.parseClassSuper.call(this,t),t.superClass&&this.isRelational("<")&&(t.superTypeParameters=this.flowParseTypeParameterInstantiation()),this.isContextual("implements")){this.next();var r=t.implements=[];do{var n=this.startNode();n.id=this.flowParseRestrictedIdentifier(!0),this.isRelational("<")?n.typeParameters=this.flowParseTypeParameterInstantiation():n.typeParameters=null,r.push(this.finishNode(n,"ClassImplements"))}while(this.eat(lb.comma))}},r.parsePropertyName=function(t,r){var n=this.flowParseVariance(),a=e.prototype.parsePropertyName.call(this,t,r);return t.variance=n,a},r.parseObjPropValue=function(t,r,n,a,s,i,o,u){var c;t.variance&&this.unexpected(t.variance.start),delete t.variance,this.isRelational("<")&&(c=this.flowParseTypeParameterDeclaration(),this.match(lb.parenL)||this.unexpected()),e.prototype.parseObjPropValue.call(this,t,r,n,a,s,i,o,u),c&&((t.value||t).typeParameters=c)},r.parseAssignableListItemTypes=function(e){return this.eat(lb.question)&&("Identifier"!==e.type&&this.raise(e.start,Wb.OptionalBindingPattern),e.optional=!0),this.match(lb.colon)&&(e.typeAnnotation=this.flowParseTypeAnnotation()),this.resetEndLocation(e),e},r.parseMaybeDefault=function(t,r,n){var a=e.prototype.parseMaybeDefault.call(this,t,r,n);return"AssignmentPattern"===a.type&&a.typeAnnotation&&a.right.start<a.typeAnnotation.start&&this.raise(a.typeAnnotation.start,Wb.TypeBeforeInitializer),a},r.shouldParseDefaultImport=function(t){return Hb(t)?qb(this.state):e.prototype.shouldParseDefaultImport.call(this,t)},r.parseImportSpecifierLocal=function(e,t,r,n){t.local=Hb(e)?this.flowParseRestrictedIdentifier(!0,!0):this.parseIdentifier(),this.checkLVal(t.local,9,void 0,n),e.specifiers.push(this.finishNode(t,r))},r.maybeParseDefaultImportSpecifier=function(t){t.importKind="value";var r=null;if(this.match(lb._typeof)?r="typeof":this.isContextual("type")&&(r="type"),r){var n=this.lookahead();"type"===r&&n.type===lb.star&&this.unexpected(n.start),(qb(n)||n.type===lb.braceL||n.type===lb.star)&&(this.next(),t.importKind=r)}return e.prototype.maybeParseDefaultImportSpecifier.call(this,t)},r.parseImportSpecifier=function(e){var t=this.startNode(),r=this.state.start,n=this.parseIdentifier(!0),a=null;"type"===n.name?a="type":"typeof"===n.name&&(a="typeof");var s=!1;if(this.isContextual("as")&&!this.isLookaheadContextual("as")){var i=this.parseIdentifier(!0);null===a||this.match(lb.name)||this.state.type.keyword?(t.imported=n,t.importKind=null,t.local=this.parseIdentifier()):(t.imported=i,t.importKind=a,t.local=i.__clone())}else null!==a&&(this.match(lb.name)||this.state.type.keyword)?(t.imported=this.parseIdentifier(!0),t.importKind=a,this.eatContextual("as")?t.local=this.parseIdentifier():(s=!0,t.local=t.imported.__clone())):(s=!0,t.imported=n,t.importKind=null,t.local=t.imported.__clone());var o=Hb(e),u=Hb(t);o&&u&&this.raise(r,Wb.ImportTypeShorthandOnlyInPureImport),(o||u)&&this.checkReservedType(t.local.name,t.local.start,!0),!s||o||u||this.checkReservedWord(t.local.name,t.start,!0,!0),this.checkLVal(t.local,9,void 0,"import specifier"),e.specifiers.push(this.finishNode(t,"ImportSpecifier"))},r.parseFunctionParams=function(t,r){var n=t.kind;"get"!==n&&"set"!==n&&this.isRelational("<")&&(t.typeParameters=this.flowParseTypeParameterDeclaration()),e.prototype.parseFunctionParams.call(this,t,r)},r.parseVarId=function(t,r){e.prototype.parseVarId.call(this,t,r),this.match(lb.colon)&&(t.id.typeAnnotation=this.flowParseTypeAnnotation(),this.resetEndLocation(t.id))},r.parseAsyncArrowFromCallExpression=function(t,r){if(this.match(lb.colon)){var n=this.state.noAnonFunctionType;this.state.noAnonFunctionType=!0,t.returnType=this.flowParseTypeAnnotation(),this.state.noAnonFunctionType=n}return e.prototype.parseAsyncArrowFromCallExpression.call(this,t,r)},r.shouldParseAsyncArrow=function(){return this.match(lb.colon)||e.prototype.shouldParseAsyncArrow.call(this)},r.parseMaybeAssign=function(t,r,n,a){var s,i=this,o=null;if(this.hasPlugin("jsx")&&(this.match(lb.jsxTagStart)||this.isRelational("<"))){if(o=this.state.clone(),!(s=this.tryParse((function(){return e.prototype.parseMaybeAssign.call(i,t,r,n,a)}),o)).error)return s.node;var u=this.state.context;u[u.length-1]===Sb.j_oTag?u.length-=2:u[u.length-1]===Sb.j_expr&&(u.length-=1)}if(s&&s.error||this.isRelational("<")){var c;o=o||this.state.clone();var l=this.tryParse((function(){c=i.flowParseTypeParameterDeclaration();var s=i.forwardNoArrowParamsConversionAt(c,(function(){return e.prototype.parseMaybeAssign.call(i,t,r,n,a)}));return s.typeParameters=c,i.resetStartLocationFromNode(s,c),s}),o),p=l.node&&"ArrowFunctionExpression"===l.node.type?l.node:null;if(!l.error&&p)return p;if(s&&s.node)return this.state=s.failState,s.node;if(p)return this.state=l.failState,p;if(s&&s.thrown)throw s.error;if(l.thrown)throw l.error;throw this.raise(c.start,Wb.UnexpectedTokenAfterTypeParameter)}return e.prototype.parseMaybeAssign.call(this,t,r,n,a)},r.parseArrow=function(t){var r=this;if(this.match(lb.colon)){var n=this.tryParse((function(){var e=r.state.noAnonFunctionType;r.state.noAnonFunctionType=!0;var n=r.startNode(),a=r.flowParseTypeAndPredicateInitialiser();return n.typeAnnotation=a[0],t.predicate=a[1],r.state.noAnonFunctionType=e,r.canInsertSemicolon()&&r.unexpected(),r.match(lb.arrow)||r.unexpected(),n}));if(n.thrown)return null;n.error&&(this.state=n.failState),t.returnType=n.node.typeAnnotation?this.finishNode(n.node,"TypeAnnotation"):null}return e.prototype.parseArrow.call(this,t)},r.shouldParseArrow=function(){return this.match(lb.colon)||e.prototype.shouldParseArrow.call(this)},r.setArrowFunctionParameters=function(t,r){-1!==this.state.noArrowParamsConversionAt.indexOf(t.start)?t.params=r:e.prototype.setArrowFunctionParameters.call(this,t,r)},r.checkParams=function(t,r,n){if(!n||-1===this.state.noArrowParamsConversionAt.indexOf(t.start))return e.prototype.checkParams.apply(this,arguments)},r.parseParenAndDistinguishExpression=function(t){return e.prototype.parseParenAndDistinguishExpression.call(this,t&&-1===this.state.noArrowAt.indexOf(this.state.start))},r.parseSubscripts=function(t,r,n,a){var s=this;if("Identifier"===t.type&&"async"===t.name&&-1!==this.state.noArrowAt.indexOf(r)){this.next();var i=this.startNodeAt(r,n);i.callee=t,i.arguments=this.parseCallExpressionArguments(lb.parenR,!1),t=this.finishNode(i,"CallExpression")}else if("Identifier"===t.type&&"async"===t.name&&this.isRelational("<")){var o=this.state.clone(),u=this.tryParse((function(e){return s.parseAsyncArrowWithTypeParameters(r,n)||e()}),o);if(!u.error&&!u.aborted)return u.node;var c=this.tryParse((function(){return e.prototype.parseSubscripts.call(s,t,r,n,a)}),o);if(c.node&&!c.error)return c.node;if(u.node)return this.state=u.failState,u.node;if(c.node)return this.state=c.failState,c.node;throw u.error||c.error}return e.prototype.parseSubscripts.call(this,t,r,n,a)},r.parseSubscript=function(t,r,n,a,s){var i=this;if(this.match(lb.questionDot)&&this.isLookaheadRelational("<")){if(s.optionalChainMember=!0,a)return s.stop=!0,t;this.next();var o=this.startNodeAt(r,n);return o.callee=t,o.typeArguments=this.flowParseTypeParameterInstantiation(),this.expect(lb.parenL),o.arguments=this.parseCallExpressionArguments(lb.parenR,!1),o.optional=!0,this.finishCallExpression(o,!0)}if(!a&&this.shouldParseTypes()&&this.isRelational("<")){var u=this.startNodeAt(r,n);u.callee=t;var c=this.tryParse((function(){return u.typeArguments=i.flowParseTypeParameterInstantiationCallOrNew(),i.expect(lb.parenL),u.arguments=i.parseCallExpressionArguments(lb.parenR,!1),s.optionalChainMember&&(u.optional=!1),i.finishCallExpression(u,s.optionalChainMember)}));if(c.node)return c.error&&(this.state=c.failState),c.node}return e.prototype.parseSubscript.call(this,t,r,n,a,s)},r.parseNewArguments=function(t){var r=this,n=null;this.shouldParseTypes()&&this.isRelational("<")&&(n=this.tryParse((function(){return r.flowParseTypeParameterInstantiationCallOrNew()})).node),t.typeArguments=n,e.prototype.parseNewArguments.call(this,t)},r.parseAsyncArrowWithTypeParameters=function(e,t){var r=this.startNodeAt(e,t);if(this.parseFunctionParams(r),this.parseArrow(r))return this.parseArrowExpression(r,void 0,!0)},r.readToken_mult_modulo=function(t){var r=this.input.charCodeAt(this.state.pos+1);if(42===t&&47===r&&this.state.hasFlowComment)return this.state.hasFlowComment=!1,this.state.pos+=2,void this.nextToken();e.prototype.readToken_mult_modulo.call(this,t)},r.readToken_pipe_amp=function(t){var r=this.input.charCodeAt(this.state.pos+1);124!==t||125!==r?e.prototype.readToken_pipe_amp.call(this,t):this.finishOp(lb.braceBarR,2)},r.parseTopLevel=function(t,r){var n=e.prototype.parseTopLevel.call(this,t,r);return this.state.hasFlowComment&&this.raise(this.state.pos,Wb.UnterminatedFlowComment),n},r.skipBlockComment=function(){if(this.hasPlugin("flowComments")&&this.skipFlowComment())return this.state.hasFlowComment&&this.unexpected(null,Wb.NestedFlowComment),this.hasFlowCommentCompletion(),this.state.pos+=this.skipFlowComment(),void(this.state.hasFlowComment=!0);if(this.state.hasFlowComment){var t=this.input.indexOf("*-/",this.state.pos+=2);if(-1===t)throw this.raise(this.state.pos-2,xb.UnterminatedComment);this.state.pos=t+3}else e.prototype.skipBlockComment.call(this)},r.skipFlowComment=function(){for(var e=this.state.pos,t=2;[32,9].includes(this.input.charCodeAt(e+t));)t++;var r=this.input.charCodeAt(t+e),n=this.input.charCodeAt(t+e+1);return 58===r&&58===n?t+2:"flow-include"===this.input.slice(t+e,t+e+12)?t+12:58===r&&58!==n&&t},r.hasFlowCommentCompletion=function(){if(-1===this.input.indexOf("*/",this.state.pos))throw this.raise(this.state.pos,xb.UnterminatedComment)},r.flowEnumErrorBooleanMemberNotInitialized=function(e,t){var r=t.enumName,n=t.memberName;this.raise(e,Wb.EnumBooleanMemberNotInitialized,n,r)},r.flowEnumErrorInvalidMemberName=function(e,t){var r=t.enumName,n=t.memberName,a=n[0].toUpperCase()+n.slice(1);this.raise(e,Wb.EnumInvalidMemberName,n,a,r)},r.flowEnumErrorDuplicateMemberName=function(e,t){var r=t.enumName,n=t.memberName;this.raise(e,Wb.EnumDuplicateMemberName,n,r)},r.flowEnumErrorInconsistentMemberValues=function(e,t){var r=t.enumName;this.raise(e,Wb.EnumInconsistentMemberValues,r)},r.flowEnumErrorInvalidExplicitType=function(e,t){var r=t.enumName,n=t.suppliedType;return this.raise(e,null===n?Wb.EnumInvalidExplicitTypeUnknownSupplied:Wb.EnumInvalidExplicitType,r,n)},r.flowEnumErrorInvalidMemberInitializer=function(e,t){var r=t.enumName,n=t.explicitType,a=t.memberName,s=null;switch(n){case"boolean":case"number":case"string":s=Wb.EnumInvalidMemberInitializerPrimaryType;break;case"symbol":s=Wb.EnumInvalidMemberInitializerSymbolType;break;default:s=Wb.EnumInvalidMemberInitializerUnknownType}return this.raise(e,s,r,a,n)},r.flowEnumErrorNumberMemberNotInitialized=function(e,t){var r=t.enumName,n=t.memberName;this.raise(e,Wb.EnumNumberMemberNotInitialized,r,n)},r.flowEnumErrorStringMemberInconsistentlyInitailized=function(e,t){var r=t.enumName;this.raise(e,Wb.EnumStringMemberInconsistentlyInitailized,r)},r.flowEnumMemberInit=function(){var e=this,t=this.state.start,r=function(){return e.match(lb.comma)||e.match(lb.braceR)};switch(this.state.type){case lb.num:var n=this.parseLiteral(this.state.value,"NumericLiteral");return r()?{type:"number",pos:n.start,value:n}:{type:"invalid",pos:t};case lb.string:var a=this.parseLiteral(this.state.value,"StringLiteral");return r()?{type:"string",pos:a.start,value:a}:{type:"invalid",pos:t};case lb._true:case lb._false:var s=this.parseBooleanLiteral();return r()?{type:"boolean",pos:s.start,value:s}:{type:"invalid",pos:t};default:return{type:"invalid",pos:t}}},r.flowEnumMemberRaw=function(){var e=this.state.start;return{id:this.parseIdentifier(!0),init:this.eat(lb.eq)?this.flowEnumMemberInit():{type:"none",pos:e}}},r.flowEnumCheckExplicitTypeMismatch=function(e,t,r){var n=t.explicitType;null!==n&&n!==r&&this.flowEnumErrorInvalidMemberInitializer(e,t)},r.flowEnumMembers=function(e){for(var t=e.enumName,r=e.explicitType,n=new Set,a={booleanMembers:[],numberMembers:[],stringMembers:[],defaultedMembers:[]};!this.match(lb.braceR);){var s=this.startNode(),i=this.flowEnumMemberRaw(),o=i.id,u=i.init,c=o.name;if(""!==c){/^[a-z]/.test(c)&&this.flowEnumErrorInvalidMemberName(o.start,{enumName:t,memberName:c}),n.has(c)&&this.flowEnumErrorDuplicateMemberName(o.start,{enumName:t,memberName:c}),n.add(c);var l={enumName:t,explicitType:r,memberName:c};switch(s.id=o,u.type){case"boolean":this.flowEnumCheckExplicitTypeMismatch(u.pos,l,"boolean"),s.init=u.value,a.booleanMembers.push(this.finishNode(s,"EnumBooleanMember"));break;case"number":this.flowEnumCheckExplicitTypeMismatch(u.pos,l,"number"),s.init=u.value,a.numberMembers.push(this.finishNode(s,"EnumNumberMember"));break;case"string":this.flowEnumCheckExplicitTypeMismatch(u.pos,l,"string"),s.init=u.value,a.stringMembers.push(this.finishNode(s,"EnumStringMember"));break;case"invalid":throw this.flowEnumErrorInvalidMemberInitializer(u.pos,l);case"none":switch(r){case"boolean":this.flowEnumErrorBooleanMemberNotInitialized(u.pos,l);break;case"number":this.flowEnumErrorNumberMemberNotInitialized(u.pos,l);break;default:a.defaultedMembers.push(this.finishNode(s,"EnumDefaultedMember"))}}this.match(lb.braceR)||this.expect(lb.comma)}}return a},r.flowEnumStringMembers=function(e,t,r){var n=r.enumName;if(0===e.length)return t;if(0===t.length)return e;if(t.length>e.length){for(var a=0;a<e.length;a++){var s=e[a];this.flowEnumErrorStringMemberInconsistentlyInitailized(s.start,{enumName:n})}return t}for(var i=0;i<t.length;i++){var o=t[i];this.flowEnumErrorStringMemberInconsistentlyInitailized(o.start,{enumName:n})}return e},r.flowEnumParseExplicitType=function(e){var t=e.enumName;if(this.eatContextual("of")){if(!this.match(lb.name))throw this.flowEnumErrorInvalidExplicitType(this.state.start,{enumName:t,suppliedType:null});var r=this.state.value;return this.next(),"boolean"!==r&&"number"!==r&&"string"!==r&&"symbol"!==r&&this.flowEnumErrorInvalidExplicitType(this.state.start,{enumName:t,suppliedType:r}),r}return null},r.flowEnumBody=function(e,t){var r=this,n=t.enumName,a=t.nameLoc,s=this.flowEnumParseExplicitType({enumName:n});this.expect(lb.braceL);var i=this.flowEnumMembers({enumName:n,explicitType:s});switch(s){case"boolean":return e.explicitType=!0,e.members=i.booleanMembers,this.expect(lb.braceR),this.finishNode(e,"EnumBooleanBody");case"number":return e.explicitType=!0,e.members=i.numberMembers,this.expect(lb.braceR),this.finishNode(e,"EnumNumberBody");case"string":return e.explicitType=!0,e.members=this.flowEnumStringMembers(i.stringMembers,i.defaultedMembers,{enumName:n}),this.expect(lb.braceR),this.finishNode(e,"EnumStringBody");case"symbol":return e.members=i.defaultedMembers,this.expect(lb.braceR),this.finishNode(e,"EnumSymbolBody");default:var o=function(){return e.members=[],r.expect(lb.braceR),r.finishNode(e,"EnumStringBody")};e.explicitType=!1;var u=i.booleanMembers.length,c=i.numberMembers.length,l=i.stringMembers.length,p=i.defaultedMembers.length;if(u||c||l||p){if(u||c){if(!c&&!l&&u>=p){for(var d=0,f=i.defaultedMembers;d<f.length;d++){var h=f[d];this.flowEnumErrorBooleanMemberNotInitialized(h.start,{enumName:n,memberName:h.id.name})}return e.members=i.booleanMembers,this.expect(lb.braceR),this.finishNode(e,"EnumBooleanBody")}if(!u&&!l&&c>=p){for(var m=0,y=i.defaultedMembers;m<y.length;m++){var g=y[m];this.flowEnumErrorNumberMemberNotInitialized(g.start,{enumName:n,memberName:g.id.name})}return e.members=i.numberMembers,this.expect(lb.braceR),this.finishNode(e,"EnumNumberBody")}return this.flowEnumErrorInconsistentMemberValues(a,{enumName:n}),o()}return e.members=this.flowEnumStringMembers(i.stringMembers,i.defaultedMembers,{enumName:n}),this.expect(lb.braceR),this.finishNode(e,"EnumStringBody")}return o()}},r.flowParseEnumDeclaration=function(e){var t=this.parseIdentifier();return e.id=t,e.body=this.flowEnumBody(this.startNode(),{enumName:t.name,nameLoc:t.start}),this.finishNode(e,"EnumDeclaration")},t}(e)},typescript:function(e){return function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.getScopeHandler=function(){return nx},r.tsIsIdentifier=function(){return this.match(lb.name)},r.tsNextTokenCanFollowModifier=function(){return this.next(),!(this.hasPrecedingLineBreak()||this.match(lb.parenL)||this.match(lb.parenR)||this.match(lb.colon)||this.match(lb.eq)||this.match(lb.question)||this.match(lb.bang))},r.tsParseModifier=function(e){if(this.match(lb.name)){var t=this.state.value;return-1!==e.indexOf(t)&&this.tsTryParse(this.tsNextTokenCanFollowModifier.bind(this))?t:void 0}},r.tsParseModifiers=function(e,t){for(;;){var r=this.state.start,n=this.tsParseModifier(t);if(!n)break;Object.hasOwnProperty.call(e,n)&&this.raise(r,ux.DuplicateModifier,n),e[n]=!0}},r.tsIsListTerminator=function(e){switch(e){case"EnumMembers":case"TypeMembers":return this.match(lb.braceR);case"HeritageClauseElement":return this.match(lb.braceL);case"TupleElementTypes":return this.match(lb.bracketR);case"TypeParametersOrArguments":return this.isRelational(">")}throw new Error("Unreachable")},r.tsParseList=function(e,t){for(var r=[];!this.tsIsListTerminator(e);)r.push(t());return r},r.tsParseDelimitedList=function(e,t){return ix(this.tsParseDelimitedListWorker(e,t,!0))},r.tsParseDelimitedListWorker=function(e,t,r){for(var n=[];!this.tsIsListTerminator(e);){var a=t();if(null==a)return;if(n.push(a),!this.eat(lb.comma)){if(this.tsIsListTerminator(e))break;return void(r&&this.expect(lb.comma))}}return n},r.tsParseBracketedList=function(e,t,r,n){n||(r?this.expect(lb.bracketL):this.expectRelational("<"));var a=this.tsParseDelimitedList(e,t);return r?this.expect(lb.bracketR):this.expectRelational(">"),a},r.tsParseImportType=function(){var e=this.startNode();return this.expect(lb._import),this.expect(lb.parenL),this.match(lb.string)||this.raise(this.state.start,ux.UnsupportedImportTypeArgument),e.argument=this.parseExprAtom(),this.expect(lb.parenR),this.eat(lb.dot)&&(e.qualifier=this.tsParseEntityName(!0)),this.isRelational("<")&&(e.typeParameters=this.tsParseTypeArguments()),this.finishNode(e,"TSImportType")},r.tsParseEntityName=function(e){for(var t=this.parseIdentifier();this.eat(lb.dot);){var r=this.startNodeAtNode(t);r.left=t,r.right=this.parseIdentifier(e),t=this.finishNode(r,"TSQualifiedName")}return t},r.tsParseTypeReference=function(){var e=this.startNode();return e.typeName=this.tsParseEntityName(!1),!this.hasPrecedingLineBreak()&&this.isRelational("<")&&(e.typeParameters=this.tsParseTypeArguments()),this.finishNode(e,"TSTypeReference")},r.tsParseThisTypePredicate=function(e){this.next();var t=this.startNodeAtNode(e);return t.parameterName=e,t.typeAnnotation=this.tsParseTypeAnnotation(!1),this.finishNode(t,"TSTypePredicate")},r.tsParseThisTypeNode=function(){var e=this.startNode();return this.next(),this.finishNode(e,"TSThisType")},r.tsParseTypeQuery=function(){var e=this.startNode();return this.expect(lb._typeof),this.match(lb._import)?e.exprName=this.tsParseImportType():e.exprName=this.tsParseEntityName(!0),this.finishNode(e,"TSTypeQuery")},r.tsParseTypeParameter=function(){var e=this.startNode();return e.name=this.parseIdentifierName(e.start),e.constraint=this.tsEatThenParseType(lb._extends),e.default=this.tsEatThenParseType(lb.eq),this.finishNode(e,"TSTypeParameter")},r.tsTryParseTypeParameters=function(){if(this.isRelational("<"))return this.tsParseTypeParameters()},r.tsParseTypeParameters=function(){var e=this.startNode();return this.isRelational("<")||this.match(lb.jsxTagStart)?this.next():this.unexpected(),e.params=this.tsParseBracketedList("TypeParametersOrArguments",this.tsParseTypeParameter.bind(this),!1,!0),this.finishNode(e,"TSTypeParameterDeclaration")},r.tsTryNextParseConstantContext=function(){return this.lookahead().type===lb._const?(this.next(),this.tsParseTypeReference()):null},r.tsFillSignature=function(e,t){var r=e===lb.arrow;t.typeParameters=this.tsTryParseTypeParameters(),this.expect(lb.parenL),t.parameters=this.tsParseBindingListForSignature(),(r||this.match(e))&&(t.typeAnnotation=this.tsParseTypeOrTypePredicateAnnotation(e))},r.tsParseBindingListForSignature=function(){var e=this;return this.parseBindingList(lb.parenR,41).map((function(t){return"Identifier"!==t.type&&"RestElement"!==t.type&&"ObjectPattern"!==t.type&&"ArrayPattern"!==t.type&&e.raise(t.start,ux.UnsupportedSignatureParameterKind,t.type),t}))},r.tsParseTypeMemberSemicolon=function(){this.eat(lb.comma)||this.semicolon()},r.tsParseSignatureMember=function(e,t){return this.tsFillSignature(lb.colon,t),this.tsParseTypeMemberSemicolon(),this.finishNode(t,e)},r.tsIsUnambiguouslyIndexSignature=function(){return this.next(),this.eat(lb.name)&&this.match(lb.colon)},r.tsTryParseIndexSignature=function(e){if(this.match(lb.bracketL)&&this.tsLookAhead(this.tsIsUnambiguouslyIndexSignature.bind(this))){this.expect(lb.bracketL);var t=this.parseIdentifier();t.typeAnnotation=this.tsParseTypeAnnotation(),this.resetEndLocation(t),this.expect(lb.bracketR),e.parameters=[t];var r=this.tsTryParseTypeAnnotation();return r&&(e.typeAnnotation=r),this.tsParseTypeMemberSemicolon(),this.finishNode(e,"TSIndexSignature")}},r.tsParsePropertyOrMethodSignature=function(e,t){this.eat(lb.question)&&(e.optional=!0);var r=e;if(t||!this.match(lb.parenL)&&!this.isRelational("<")){var n=r;t&&(n.readonly=!0);var a=this.tsTryParseTypeAnnotation();return a&&(n.typeAnnotation=a),this.tsParseTypeMemberSemicolon(),this.finishNode(n,"TSPropertySignature")}var s=r;return this.tsFillSignature(lb.colon,s),this.tsParseTypeMemberSemicolon(),this.finishNode(s,"TSMethodSignature")},r.tsParseTypeMember=function(){var e=this.startNode();if(this.match(lb.parenL)||this.isRelational("<"))return this.tsParseSignatureMember("TSCallSignatureDeclaration",e);if(this.match(lb._new)){var t=this.startNode();return this.next(),this.match(lb.parenL)||this.isRelational("<")?this.tsParseSignatureMember("TSConstructSignatureDeclaration",e):(e.key=this.createIdentifier(t,"new"),this.tsParsePropertyOrMethodSignature(e,!1))}var r=!!this.tsParseModifier(["readonly"]),n=this.tsTryParseIndexSignature(e);return n?(r&&(e.readonly=!0),n):(this.parsePropertyName(e,!1),this.tsParsePropertyOrMethodSignature(e,r))},r.tsParseTypeLiteral=function(){var e=this.startNode();return e.members=this.tsParseObjectTypeMembers(),this.finishNode(e,"TSTypeLiteral")},r.tsParseObjectTypeMembers=function(){this.expect(lb.braceL);var e=this.tsParseList("TypeMembers",this.tsParseTypeMember.bind(this));return this.expect(lb.braceR),e},r.tsIsStartOfMappedType=function(){return this.next(),this.eat(lb.plusMin)?this.isContextual("readonly"):(this.isContextual("readonly")&&this.next(),!!this.match(lb.bracketL)&&(this.next(),!!this.tsIsIdentifier()&&(this.next(),this.match(lb._in))))},r.tsParseMappedTypeParameter=function(){var e=this.startNode();return e.name=this.parseIdentifierName(e.start),e.constraint=this.tsExpectThenParseType(lb._in),this.finishNode(e,"TSTypeParameter")},r.tsParseMappedType=function(){var e=this.startNode();return this.expect(lb.braceL),this.match(lb.plusMin)?(e.readonly=this.state.value,this.next(),this.expectContextual("readonly")):this.eatContextual("readonly")&&(e.readonly=!0),this.expect(lb.bracketL),e.typeParameter=this.tsParseMappedTypeParameter(),this.expect(lb.bracketR),this.match(lb.plusMin)?(e.optional=this.state.value,this.next(),this.expect(lb.question)):this.eat(lb.question)&&(e.optional=!0),e.typeAnnotation=this.tsTryParseType(),this.semicolon(),this.expect(lb.braceR),this.finishNode(e,"TSMappedType")},r.tsParseTupleType=function(){var e=this,t=this.startNode();t.elementTypes=this.tsParseBracketedList("TupleElementTypes",this.tsParseTupleElementType.bind(this),!0,!1);var r=!1;return t.elementTypes.forEach((function(t){"TSOptionalType"===t.type?r=!0:r&&"TSRestType"!==t.type&&e.raise(t.start,ux.OptionalTypeBeforeRequired)})),this.finishNode(t,"TSTupleType")},r.tsParseTupleElementType=function(){if(this.match(lb.ellipsis)){var e=this.startNode();return this.next(),e.typeAnnotation=this.tsParseType(),this.match(lb.comma)&&93!==this.lookaheadCharCode()&&this.raiseRestNotLast(this.state.start),this.finishNode(e,"TSRestType")}var t=this.tsParseType();if(this.eat(lb.question)){var r=this.startNodeAtNode(t);return r.typeAnnotation=t,this.finishNode(r,"TSOptionalType")}return t},r.tsParseParenthesizedType=function(){var e=this.startNode();return this.expect(lb.parenL),e.typeAnnotation=this.tsParseType(),this.expect(lb.parenR),this.finishNode(e,"TSParenthesizedType")},r.tsParseFunctionOrConstructorType=function(e){var t=this.startNode();return"TSConstructorType"===e&&this.expect(lb._new),this.tsFillSignature(lb.arrow,t),this.finishNode(t,e)},r.tsParseLiteralTypeNode=function(){var e=this,t=this.startNode();return t.literal=function(){switch(e.state.type){case lb.num:case lb.string:case lb._true:case lb._false:return e.parseExprAtom();default:throw e.unexpected()}}(),this.finishNode(t,"TSLiteralType")},r.tsParseTemplateLiteralType=function(){var e=this.startNode(),t=this.parseTemplate(!1);return t.expressions.length>0&&this.raise(t.expressions[0].start,ux.TemplateTypeHasSubstitution),e.literal=t,this.finishNode(e,"TSLiteralType")},r.tsParseThisTypeOrThisTypePredicate=function(){var e=this.tsParseThisTypeNode();return this.isContextual("is")&&!this.hasPrecedingLineBreak()?this.tsParseThisTypePredicate(e):e},r.tsParseNonArrayType=function(){switch(this.state.type){case lb.name:case lb._void:case lb._null:var e=this.match(lb._void)?"TSVoidKeyword":this.match(lb._null)?"TSNullKeyword":function(e){switch(e){case"any":return"TSAnyKeyword";case"boolean":return"TSBooleanKeyword";case"bigint":return"TSBigIntKeyword";case"never":return"TSNeverKeyword";case"number":return"TSNumberKeyword";case"object":return"TSObjectKeyword";case"string":return"TSStringKeyword";case"symbol":return"TSSymbolKeyword";case"undefined":return"TSUndefinedKeyword";case"unknown":return"TSUnknownKeyword";default:return}}(this.state.value);if(void 0!==e&&46!==this.lookaheadCharCode()){var t=this.startNode();return this.next(),this.finishNode(t,e)}return this.tsParseTypeReference();case lb.string:case lb.num:case lb._true:case lb._false:return this.tsParseLiteralTypeNode();case lb.plusMin:if("-"===this.state.value){var r=this.startNode();if(this.lookahead().type!==lb.num)throw this.unexpected();return r.literal=this.parseMaybeUnary(),this.finishNode(r,"TSLiteralType")}break;case lb._this:return this.tsParseThisTypeOrThisTypePredicate();case lb._typeof:return this.tsParseTypeQuery();case lb._import:return this.tsParseImportType();case lb.braceL:return this.tsLookAhead(this.tsIsStartOfMappedType.bind(this))?this.tsParseMappedType():this.tsParseTypeLiteral();case lb.bracketL:return this.tsParseTupleType();case lb.parenL:return this.tsParseParenthesizedType();case lb.backQuote:return this.tsParseTemplateLiteralType()}throw this.unexpected()},r.tsParseArrayTypeOrHigher=function(){for(var e=this.tsParseNonArrayType();!this.hasPrecedingLineBreak()&&this.eat(lb.bracketL);)if(this.match(lb.bracketR)){var t=this.startNodeAtNode(e);t.elementType=e,this.expect(lb.bracketR),e=this.finishNode(t,"TSArrayType")}else{var r=this.startNodeAtNode(e);r.objectType=e,r.indexType=this.tsParseType(),this.expect(lb.bracketR),e=this.finishNode(r,"TSIndexedAccessType")}return e},r.tsParseTypeOperator=function(e){var t=this.startNode();return this.expectContextual(e),t.operator=e,t.typeAnnotation=this.tsParseTypeOperatorOrHigher(),"readonly"===e&&this.tsCheckTypeAnnotationForReadOnly(t),this.finishNode(t,"TSTypeOperator")},r.tsCheckTypeAnnotationForReadOnly=function(e){switch(e.typeAnnotation.type){case"TSTupleType":case"TSArrayType":return;default:this.raise(e.start,ux.UnexpectedReadonly)}},r.tsParseInferType=function(){var e=this.startNode();this.expectContextual("infer");var t=this.startNode();return t.name=this.parseIdentifierName(t.start),e.typeParameter=this.finishNode(t,"TSTypeParameter"),this.finishNode(e,"TSInferType")},r.tsParseTypeOperatorOrHigher=function(){var e=this,t=["keyof","unique","readonly"].find((function(t){return e.isContextual(t)}));return t?this.tsParseTypeOperator(t):this.isContextual("infer")?this.tsParseInferType():this.tsParseArrayTypeOrHigher()},r.tsParseUnionOrIntersectionType=function(e,t,r){this.eat(r);var n=t();if(this.match(r)){for(var a=[n];this.eat(r);)a.push(t());var s=this.startNodeAtNode(n);s.types=a,n=this.finishNode(s,e)}return n},r.tsParseIntersectionTypeOrHigher=function(){return this.tsParseUnionOrIntersectionType("TSIntersectionType",this.tsParseTypeOperatorOrHigher.bind(this),lb.bitwiseAND)},r.tsParseUnionTypeOrHigher=function(){return this.tsParseUnionOrIntersectionType("TSUnionType",this.tsParseIntersectionTypeOrHigher.bind(this),lb.bitwiseOR)},r.tsIsStartOfFunctionType=function(){return!!this.isRelational("<")||this.match(lb.parenL)&&this.tsLookAhead(this.tsIsUnambiguouslyStartOfFunctionType.bind(this))},r.tsSkipParameterStart=function(){if(this.match(lb.name)||this.match(lb._this))return this.next(),!0;if(this.match(lb.braceL)){var e=1;for(this.next();e>0;)this.match(lb.braceL)?++e:this.match(lb.braceR)&&--e,this.next();return!0}if(this.match(lb.bracketL)){var t=1;for(this.next();t>0;)this.match(lb.bracketL)?++t:this.match(lb.bracketR)&&--t,this.next();return!0}return!1},r.tsIsUnambiguouslyStartOfFunctionType=function(){if(this.next(),this.match(lb.parenR)||this.match(lb.ellipsis))return!0;if(this.tsSkipParameterStart()){if(this.match(lb.colon)||this.match(lb.comma)||this.match(lb.question)||this.match(lb.eq))return!0;if(this.match(lb.parenR)&&(this.next(),this.match(lb.arrow)))return!0}return!1},r.tsParseTypeOrTypePredicateAnnotation=function(e){var t=this;return this.tsInType((function(){var r=t.startNode();t.expect(e);var n=t.tsTryParse(t.tsParseTypePredicateAsserts.bind(t));if(n&&t.match(lb._this)){var a=t.tsParseThisTypeOrThisTypePredicate();if("TSThisType"===a.type){var s=t.startNodeAtNode(r);s.parameterName=a,s.asserts=!0,a=t.finishNode(s,"TSTypePredicate")}else a.asserts=!0;return r.typeAnnotation=a,t.finishNode(r,"TSTypeAnnotation")}var i=t.tsIsIdentifier()&&t.tsTryParse(t.tsParseTypePredicatePrefix.bind(t));if(!i){if(!n)return t.tsParseTypeAnnotation(!1,r);var o=t.startNodeAtNode(r);return o.parameterName=t.parseIdentifier(),o.asserts=n,r.typeAnnotation=t.finishNode(o,"TSTypePredicate"),t.finishNode(r,"TSTypeAnnotation")}var u=t.tsParseTypeAnnotation(!1),c=t.startNodeAtNode(r);return c.parameterName=i,c.typeAnnotation=u,c.asserts=n,r.typeAnnotation=t.finishNode(c,"TSTypePredicate"),t.finishNode(r,"TSTypeAnnotation")}))},r.tsTryParseTypeOrTypePredicateAnnotation=function(){return this.match(lb.colon)?this.tsParseTypeOrTypePredicateAnnotation(lb.colon):void 0},r.tsTryParseTypeAnnotation=function(){return this.match(lb.colon)?this.tsParseTypeAnnotation():void 0},r.tsTryParseType=function(){return this.tsEatThenParseType(lb.colon)},r.tsParseTypePredicatePrefix=function(){var e=this.parseIdentifier();if(this.isContextual("is")&&!this.hasPrecedingLineBreak())return this.next(),e},r.tsParseTypePredicateAsserts=function(){if(!this.match(lb.name)||"asserts"!==this.state.value||this.hasPrecedingLineBreak())return!1;var e=this.state.containsEsc;return this.next(),!(!this.match(lb.name)&&!this.match(lb._this))&&(e&&this.raise(this.state.lastTokStart,xb.InvalidEscapedReservedWord,"asserts"),!0)},r.tsParseTypeAnnotation=function(e,t){var r=this;return void 0===e&&(e=!0),void 0===t&&(t=this.startNode()),this.tsInType((function(){e&&r.expect(lb.colon),t.typeAnnotation=r.tsParseType()})),this.finishNode(t,"TSTypeAnnotation")},r.tsParseType=function(){ox(this.state.inType);var e=this.tsParseNonConditionalType();if(this.hasPrecedingLineBreak()||!this.eat(lb._extends))return e;var t=this.startNodeAtNode(e);return t.checkType=e,t.extendsType=this.tsParseNonConditionalType(),this.expect(lb.question),t.trueType=this.tsParseType(),this.expect(lb.colon),t.falseType=this.tsParseType(),this.finishNode(t,"TSConditionalType")},r.tsParseNonConditionalType=function(){return this.tsIsStartOfFunctionType()?this.tsParseFunctionOrConstructorType("TSFunctionType"):this.match(lb._new)?this.tsParseFunctionOrConstructorType("TSConstructorType"):this.tsParseUnionTypeOrHigher()},r.tsParseTypeAssertion=function(){var e=this.startNode(),t=this.tsTryNextParseConstantContext();return e.typeAnnotation=t||this.tsNextThenParseType(),this.expectRelational(">"),e.expression=this.parseMaybeUnary(),this.finishNode(e,"TSTypeAssertion")},r.tsParseHeritageClause=function(e){var t=this.state.start,r=this.tsParseDelimitedList("HeritageClauseElement",this.tsParseExpressionWithTypeArguments.bind(this));return r.length||this.raise(t,ux.EmptyHeritageClauseType,e),r},r.tsParseExpressionWithTypeArguments=function(){var e=this.startNode();return e.expression=this.tsParseEntityName(!1),this.isRelational("<")&&(e.typeParameters=this.tsParseTypeArguments()),this.finishNode(e,"TSExpressionWithTypeArguments")},r.tsParseInterfaceDeclaration=function(e){e.id=this.parseIdentifier(),this.checkLVal(e.id,130,void 0,"typescript interface declaration"),e.typeParameters=this.tsTryParseTypeParameters(),this.eat(lb._extends)&&(e.extends=this.tsParseHeritageClause("extends"));var t=this.startNode();return t.body=this.tsInType(this.tsParseObjectTypeMembers.bind(this)),e.body=this.finishNode(t,"TSInterfaceBody"),this.finishNode(e,"TSInterfaceDeclaration")},r.tsParseTypeAliasDeclaration=function(e){return e.id=this.parseIdentifier(),this.checkLVal(e.id,2,void 0,"typescript type alias"),e.typeParameters=this.tsTryParseTypeParameters(),e.typeAnnotation=this.tsExpectThenParseType(lb.eq),this.semicolon(),this.finishNode(e,"TSTypeAliasDeclaration")},r.tsInNoContext=function(e){var t=this.state.context;this.state.context=[t[0]];try{return e()}finally{this.state.context=t}},r.tsInType=function(e){var t=this.state.inType;this.state.inType=!0;try{return e()}finally{this.state.inType=t}},r.tsEatThenParseType=function(e){return this.match(e)?this.tsNextThenParseType():void 0},r.tsExpectThenParseType=function(e){var t=this;return this.tsDoThenParseType((function(){return t.expect(e)}))},r.tsNextThenParseType=function(){var e=this;return this.tsDoThenParseType((function(){return e.next()}))},r.tsDoThenParseType=function(e){var t=this;return this.tsInType((function(){return e(),t.tsParseType()}))},r.tsParseEnumMember=function(){var e=this.startNode();return e.id=this.match(lb.string)?this.parseExprAtom():this.parseIdentifier(!0),this.eat(lb.eq)&&(e.initializer=this.parseMaybeAssign()),this.finishNode(e,"TSEnumMember")},r.tsParseEnumDeclaration=function(e,t){return t&&(e.const=!0),e.id=this.parseIdentifier(),this.checkLVal(e.id,t?779:267,void 0,"typescript enum declaration"),this.expect(lb.braceL),e.members=this.tsParseDelimitedList("EnumMembers",this.tsParseEnumMember.bind(this)),this.expect(lb.braceR),this.finishNode(e,"TSEnumDeclaration")},r.tsParseModuleBlock=function(){var e=this.startNode();return this.scope.enter(0),this.expect(lb.braceL),this.parseBlockOrModuleBlockBody(e.body=[],void 0,!0,lb.braceR),this.scope.exit(),this.finishNode(e,"TSModuleBlock")},r.tsParseModuleOrNamespaceDeclaration=function(e,t){if(void 0===t&&(t=!1),e.id=this.parseIdentifier(),t||this.checkLVal(e.id,1024,null,"module or namespace declaration"),this.eat(lb.dot)){var r=this.startNode();this.tsParseModuleOrNamespaceDeclaration(r,!0),e.body=r}else this.scope.enter(128),this.prodParam.enter(0),e.body=this.tsParseModuleBlock(),this.prodParam.exit(),this.scope.exit();return this.finishNode(e,"TSModuleDeclaration")},r.tsParseAmbientExternalModuleDeclaration=function(e){return this.isContextual("global")?(e.global=!0,e.id=this.parseIdentifier()):this.match(lb.string)?e.id=this.parseExprAtom():this.unexpected(),this.match(lb.braceL)?(this.scope.enter(128),this.prodParam.enter(0),e.body=this.tsParseModuleBlock(),this.prodParam.exit(),this.scope.exit()):this.semicolon(),this.finishNode(e,"TSModuleDeclaration")},r.tsParseImportEqualsDeclaration=function(e,t){return e.isExport=t||!1,e.id=this.parseIdentifier(),this.checkLVal(e.id,9,void 0,"import equals declaration"),this.expect(lb.eq),e.moduleReference=this.tsParseModuleReference(),this.semicolon(),this.finishNode(e,"TSImportEqualsDeclaration")},r.tsIsExternalModuleReference=function(){return this.isContextual("require")&&40===this.lookaheadCharCode()},r.tsParseModuleReference=function(){return this.tsIsExternalModuleReference()?this.tsParseExternalModuleReference():this.tsParseEntityName(!1)},r.tsParseExternalModuleReference=function(){var e=this.startNode();if(this.expectContextual("require"),this.expect(lb.parenL),!this.match(lb.string))throw this.unexpected();return e.expression=this.parseExprAtom(),this.expect(lb.parenR),this.finishNode(e,"TSExternalModuleReference")},r.tsLookAhead=function(e){var t=this.state.clone(),r=e();return this.state=t,r},r.tsTryParseAndCatch=function(e){var t=this.tryParse((function(t){return e()||t()}));if(!t.aborted&&t.node)return t.error&&(this.state=t.failState),t.node},r.tsTryParse=function(e){var t=this.state.clone(),r=e();return void 0!==r&&!1!==r?r:void(this.state=t)},r.tsTryParseDeclare=function(e){if(!this.isLineTerminator()){var t,r=this.state.type;switch(this.isContextual("let")&&(r=lb._var,t="let"),r){case lb._function:return this.parseFunctionStatement(e,!1,!0);case lb._class:return e.declare=!0,this.parseClass(e,!0,!1);case lb._const:if(this.match(lb._const)&&this.isLookaheadContextual("enum"))return this.expect(lb._const),this.expectContextual("enum"),this.tsParseEnumDeclaration(e,!0);case lb._var:return t=t||this.state.value,this.parseVarStatement(e,t);case lb.name:var n=this.state.value;return"global"===n?this.tsParseAmbientExternalModuleDeclaration(e):this.tsParseDeclaration(e,n,!0)}}},r.tsTryParseExportDeclaration=function(){return this.tsParseDeclaration(this.startNode(),this.state.value,!0)},r.tsParseExpressionStatement=function(e,t){switch(t.name){case"declare":var r=this.tsTryParseDeclare(e);if(r)return r.declare=!0,r;break;case"global":if(this.match(lb.braceL)){this.scope.enter(128),this.prodParam.enter(0);var n=e;return n.global=!0,n.id=t,n.body=this.tsParseModuleBlock(),this.scope.exit(),this.prodParam.exit(),this.finishNode(n,"TSModuleDeclaration")}break;default:return this.tsParseDeclaration(e,t.name,!1)}},r.tsParseDeclaration=function(e,t,r){switch(t){case"abstract":if(this.tsCheckLineTerminatorAndMatch(lb._class,r)){var n=e;return n.abstract=!0,r&&(this.next(),this.match(lb._class)||this.unexpected(null,lb._class)),this.parseClass(n,!0,!1)}break;case"enum":if(r||this.match(lb.name))return r&&this.next(),this.tsParseEnumDeclaration(e,!1);break;case"interface":if(this.tsCheckLineTerminatorAndMatch(lb.name,r))return r&&this.next(),this.tsParseInterfaceDeclaration(e);break;case"module":if(r&&this.next(),this.match(lb.string))return this.tsParseAmbientExternalModuleDeclaration(e);if(this.tsCheckLineTerminatorAndMatch(lb.name,r))return this.tsParseModuleOrNamespaceDeclaration(e);break;case"namespace":if(this.tsCheckLineTerminatorAndMatch(lb.name,r))return r&&this.next(),this.tsParseModuleOrNamespaceDeclaration(e);break;case"type":if(this.tsCheckLineTerminatorAndMatch(lb.name,r))return r&&this.next(),this.tsParseTypeAliasDeclaration(e)}},r.tsCheckLineTerminatorAndMatch=function(e,t){return(t||this.match(e))&&!this.isLineTerminator()},r.tsTryParseGenericAsyncArrowFunction=function(t,r){var n=this;if(this.isRelational("<")){var a=this.state.maybeInArrowParameters,s=this.state.yieldPos,i=this.state.awaitPos;this.state.maybeInArrowParameters=!0,this.state.yieldPos=-1,this.state.awaitPos=-1;var o=this.tsTryParseAndCatch((function(){var a=n.startNodeAt(t,r);return a.typeParameters=n.tsParseTypeParameters(),e.prototype.parseFunctionParams.call(n,a),a.returnType=n.tsTryParseTypeOrTypePredicateAnnotation(),n.expect(lb.arrow),a}));if(this.state.maybeInArrowParameters=a,this.state.yieldPos=s,this.state.awaitPos=i,o)return this.parseArrowExpression(o,null,!0)}},r.tsParseTypeArguments=function(){var e=this,t=this.startNode();return t.params=this.tsInType((function(){return e.tsInNoContext((function(){return e.expectRelational("<"),e.tsParseDelimitedList("TypeParametersOrArguments",e.tsParseType.bind(e))}))})),this.state.exprAllowed=!1,this.expectRelational(">"),this.finishNode(t,"TSTypeParameterInstantiation")},r.tsIsDeclarationStart=function(){if(this.match(lb.name))switch(this.state.value){case"abstract":case"declare":case"enum":case"interface":case"module":case"namespace":case"type":return!0}return!1},r.isExportDefaultSpecifier=function(){return!this.tsIsDeclarationStart()&&e.prototype.isExportDefaultSpecifier.call(this)},r.parseAssignableListItem=function(e,t){var r,n=this.state.start,a=this.state.startLoc,s=!1;e&&(r=this.parseAccessModifier(),s=!!this.tsParseModifier(["readonly"]));var i=this.parseMaybeDefault();this.parseAssignableListItemTypes(i);var o=this.parseMaybeDefault(i.start,i.loc.start,i);if(r||s){var u=this.startNodeAt(n,a);return t.length&&(u.decorators=t),r&&(u.accessibility=r),s&&(u.readonly=s),"Identifier"!==o.type&&"AssignmentPattern"!==o.type&&this.raise(u.start,ux.UnsupportedParameterPropertyKind),u.parameter=o,this.finishNode(u,"TSParameterProperty")}return t.length&&(i.decorators=t),o},r.parseFunctionBodyAndFinish=function(t,r,n){void 0===n&&(n=!1),this.match(lb.colon)&&(t.returnType=this.tsParseTypeOrTypePredicateAnnotation(lb.colon));var a="FunctionDeclaration"===r?"TSDeclareFunction":"ClassMethod"===r?"TSDeclareMethod":void 0;a&&!this.match(lb.braceL)&&this.isLineTerminator()?this.finishNode(t,a):e.prototype.parseFunctionBodyAndFinish.call(this,t,r,n)},r.registerFunctionStatementId=function(t){!t.body&&t.id?this.checkLVal(t.id,1024,null,"function name"):e.prototype.registerFunctionStatementId.apply(this,arguments)},r.parseSubscript=function(t,r,n,a,s){var i=this;if(!this.hasPrecedingLineBreak()&&this.match(lb.bang)){this.state.exprAllowed=!1,this.next();var o=this.startNodeAt(r,n);return o.expression=t,this.finishNode(o,"TSNonNullExpression")}if(this.isRelational("<")){var u=this.tsTryParseAndCatch((function(){if(!a&&i.atPossibleAsync(t)){var e=i.tsTryParseGenericAsyncArrowFunction(r,n);if(e)return e}var o=i.startNodeAt(r,n);o.callee=t;var u=i.tsParseTypeArguments();if(u){if(!a&&i.eat(lb.parenL))return o.arguments=i.parseCallExpressionArguments(lb.parenR,!1),o.typeParameters=u,i.finishCallExpression(o,s.optionalChainMember);if(i.match(lb.backQuote))return i.parseTaggedTemplateExpression(r,n,t,s,u)}i.unexpected()}));if(u)return u}return e.prototype.parseSubscript.call(this,t,r,n,a,s)},r.parseNewArguments=function(t){var r=this;if(this.isRelational("<")){var n=this.tsTryParseAndCatch((function(){var e=r.tsParseTypeArguments();return r.match(lb.parenL)||r.unexpected(),e}));n&&(t.typeParameters=n)}e.prototype.parseNewArguments.call(this,t)},r.parseExprOp=function(t,r,n,a,s){if(ix(lb._in.binop)>a&&!this.hasPrecedingLineBreak()&&this.isContextual("as")){var i=this.startNodeAt(r,n);i.expression=t;var o=this.tsTryNextParseConstantContext();return i.typeAnnotation=o||this.tsNextThenParseType(),this.finishNode(i,"TSAsExpression"),this.parseExprOp(i,r,n,a,s)}return e.prototype.parseExprOp.call(this,t,r,n,a,s)},r.checkReservedWord=function(e,t,r,n){},r.checkDuplicateExports=function(){},r.parseImport=function(t){return this.match(lb.name)&&this.lookahead().type===lb.eq?this.tsParseImportEqualsDeclaration(t):e.prototype.parseImport.call(this,t)},r.parseExport=function(t){if(this.match(lb._import))return this.expect(lb._import),this.tsParseImportEqualsDeclaration(t,!0);if(this.eat(lb.eq)){var r=t;return r.expression=this.parseExpression(),this.semicolon(),this.finishNode(r,"TSExportAssignment")}if(this.eatContextual("as")){var n=t;return this.expectContextual("namespace"),n.id=this.parseIdentifier(),this.semicolon(),this.finishNode(n,"TSNamespaceExportDeclaration")}return e.prototype.parseExport.call(this,t)},r.isAbstractClass=function(){return this.isContextual("abstract")&&this.lookahead().type===lb._class},r.parseExportDefaultExpression=function(){if(this.isAbstractClass()){var t=this.startNode();return this.next(),this.parseClass(t,!0,!0),t.abstract=!0,t}if("interface"===this.state.value){var r=this.tsParseDeclaration(this.startNode(),this.state.value,!0);if(r)return r}return e.prototype.parseExportDefaultExpression.call(this)},r.parseStatementContent=function(t,r){if(this.state.type===lb._const){var n=this.lookahead();if(n.type===lb.name&&"enum"===n.value){var a=this.startNode();return this.expect(lb._const),this.expectContextual("enum"),this.tsParseEnumDeclaration(a,!0)}}return e.prototype.parseStatementContent.call(this,t,r)},r.parseAccessModifier=function(){return this.tsParseModifier(["public","protected","private"])},r.parseClassMember=function(t,r,n,a){this.tsParseModifiers(r,["declare"]);var s=this.parseAccessModifier();s&&(r.accessibility=s),this.tsParseModifiers(r,["declare"]),e.prototype.parseClassMember.call(this,t,r,n,a)},r.parseClassMemberWithIsStatic=function(t,r,n,a,s){this.tsParseModifiers(r,["abstract","readonly","declare"]);var i=this.tsTryParseIndexSignature(r);if(i)return t.body.push(i),r.abstract&&this.raise(r.start,ux.IndexSignatureHasAbstract),a&&this.raise(r.start,ux.IndexSignatureHasStatic),void(r.accessibility&&this.raise(r.start,ux.IndexSignatureHasAccessibility,r.accessibility));e.prototype.parseClassMemberWithIsStatic.call(this,t,r,n,a,s)},r.parsePostMemberNameModifiers=function(e){this.eat(lb.question)&&(e.optional=!0),e.readonly&&this.match(lb.parenL)&&this.raise(e.start,ux.ClassMethodHasReadonly),e.declare&&this.match(lb.parenL)&&this.raise(e.start,ux.ClassMethodHasDeclare)},r.parseExpressionStatement=function(t,r){return("Identifier"===r.type?this.tsParseExpressionStatement(t,r):void 0)||e.prototype.parseExpressionStatement.call(this,t,r)},r.shouldParseExportDeclaration=function(){return!!this.tsIsDeclarationStart()||e.prototype.shouldParseExportDeclaration.call(this)},r.parseConditional=function(t,r,n,a,s){var i=this;if(!s||!this.match(lb.question))return e.prototype.parseConditional.call(this,t,r,n,a,s);var o=this.tryParse((function(){return e.prototype.parseConditional.call(i,t,r,n,a)}));return o.node?(o.error&&(this.state=o.failState),o.node):(s.start=o.error.pos||this.state.start,t)},r.parseParenItem=function(t,r,n){if(t=e.prototype.parseParenItem.call(this,t,r,n),this.eat(lb.question)&&(t.optional=!0,this.resetEndLocation(t)),this.match(lb.colon)){var a=this.startNodeAt(r,n);return a.expression=t,a.typeAnnotation=this.tsParseTypeAnnotation(),this.finishNode(a,"TSTypeCastExpression")}return t},r.parseExportDeclaration=function(t){var r,n=this.state.start,a=this.state.startLoc,s=this.eatContextual("declare");return this.match(lb.name)&&(r=this.tsTryParseExportDeclaration()),r||(r=e.prototype.parseExportDeclaration.call(this,t)),r&&s&&(this.resetStartLocation(r,n,a),r.declare=!0),r},r.parseClassId=function(t,r,n){if(r&&!n||!this.isContextual("implements")){e.prototype.parseClassId.call(this,t,r,n,t.declare?1024:139);var a=this.tsTryParseTypeParameters();a&&(t.typeParameters=a)}},r.parseClassPropertyAnnotation=function(e){!e.optional&&this.eat(lb.bang)&&(e.definite=!0);var t=this.tsTryParseTypeAnnotation();t&&(e.typeAnnotation=t)},r.parseClassProperty=function(t){return this.parseClassPropertyAnnotation(t),t.declare&&this.match(lb.equal)&&this.raise(this.state.start,ux.DeclareClassFieldHasInitializer),e.prototype.parseClassProperty.call(this,t)},r.parseClassPrivateProperty=function(t){return t.abstract&&this.raise(t.start,ux.PrivateElementHasAbstract),t.accessibility&&this.raise(t.start,ux.PrivateElementHasAccessibility,t.accessibility),this.parseClassPropertyAnnotation(t),e.prototype.parseClassPrivateProperty.call(this,t)},r.pushClassMethod=function(t,r,n,a,s,i){var o=this.tsTryParseTypeParameters();o&&(r.typeParameters=o),e.prototype.pushClassMethod.call(this,t,r,n,a,s,i)},r.pushClassPrivateMethod=function(t,r,n,a){var s=this.tsTryParseTypeParameters();s&&(r.typeParameters=s),e.prototype.pushClassPrivateMethod.call(this,t,r,n,a)},r.parseClassSuper=function(t){e.prototype.parseClassSuper.call(this,t),t.superClass&&this.isRelational("<")&&(t.superTypeParameters=this.tsParseTypeArguments()),this.eatContextual("implements")&&(t.implements=this.tsParseHeritageClause("implements"))},r.parseObjPropValue=function(t){var r,n=this.tsTryParseTypeParameters();n&&(t.typeParameters=n);for(var a=arguments.length,s=new Array(a>1?a-1:0),i=1;i<a;i++)s[i-1]=arguments[i];(r=e.prototype.parseObjPropValue).call.apply(r,[this,t].concat(s))},r.parseFunctionParams=function(t,r){var n=this.tsTryParseTypeParameters();n&&(t.typeParameters=n),e.prototype.parseFunctionParams.call(this,t,r)},r.parseVarId=function(t,r){e.prototype.parseVarId.call(this,t,r),"Identifier"===t.id.type&&this.eat(lb.bang)&&(t.definite=!0);var n=this.tsTryParseTypeAnnotation();n&&(t.id.typeAnnotation=n,this.resetEndLocation(t.id))},r.parseAsyncArrowFromCallExpression=function(t,r){return this.match(lb.colon)&&(t.returnType=this.tsParseTypeAnnotation()),e.prototype.parseAsyncArrowFromCallExpression.call(this,t,r)},r.parseMaybeAssign=function(){for(var t,r,n,a,s,i=this,o=arguments.length,u=new Array(o),c=0;c<o;c++)u[c]=arguments[c];if(this.match(lb.jsxTagStart)){if(t=this.state.clone(),!(r=this.tryParse((function(){var t;return(t=e.prototype.parseMaybeAssign).call.apply(t,[i].concat(u))}),t)).error)return r.node;var l=this.state.context;l[l.length-1]===Sb.j_oTag?l.length-=2:l[l.length-1]===Sb.j_expr&&(l.length-=1)}if(!(r&&r.error||this.isRelational("<")))return(a=e.prototype.parseMaybeAssign).call.apply(a,[this].concat(u));t=t||this.state.clone();var p=this.tryParse((function(t){var r;s=i.tsParseTypeParameters();var n=(r=e.prototype.parseMaybeAssign).call.apply(r,[i].concat(u));return("ArrowFunctionExpression"!==n.type||n.extra&&n.extra.parenthesized)&&t(),s&&0!==s.params.length&&i.resetStartLocationFromNode(n,s),n.typeParameters=s,n}),t);if(!p.error&&!p.aborted)return p.node;if(!r&&(ox(!this.hasPlugin("jsx")),!(n=this.tryParse((function(){var t;return(t=e.prototype.parseMaybeAssign).call.apply(t,[i].concat(u))}),t)).error))return n.node;if(r&&r.node)return this.state=r.failState,r.node;if(p.node)return this.state=p.failState,p.node;if(n&&n.node)return this.state=n.failState,n.node;if(r&&r.thrown)throw r.error;if(p.thrown)throw p.error;if(n&&n.thrown)throw n.error;throw r&&r.error||p.error||n&&n.error},r.parseMaybeUnary=function(t){return!this.hasPlugin("jsx")&&this.isRelational("<")?this.tsParseTypeAssertion():e.prototype.parseMaybeUnary.call(this,t)},r.parseArrow=function(t){var r=this;if(this.match(lb.colon)){var n=this.tryParse((function(e){var t=r.tsParseTypeOrTypePredicateAnnotation(lb.colon);return!r.canInsertSemicolon()&&r.match(lb.arrow)||e(),t}));if(n.aborted)return;n.thrown||(n.error&&(this.state=n.failState),t.returnType=n.node)}return e.prototype.parseArrow.call(this,t)},r.parseAssignableListItemTypes=function(e){this.eat(lb.question)&&("Identifier"!==e.type&&this.raise(e.start,ux.PatternIsOptional),e.optional=!0);var t=this.tsTryParseTypeAnnotation();return t&&(e.typeAnnotation=t),this.resetEndLocation(e),e},r.toAssignable=function(t){switch(t.type){case"TSTypeCastExpression":return e.prototype.toAssignable.call(this,this.typeCastToParameter(t));case"TSParameterProperty":return e.prototype.toAssignable.call(this,t);case"TSAsExpression":case"TSNonNullExpression":case"TSTypeAssertion":return t.expression=this.toAssignable(t.expression),t;default:return e.prototype.toAssignable.call(this,t)}},r.checkLVal=function(t,r,n,a){switch(void 0===r&&(r=64),t.type){case"TSTypeCastExpression":return;case"TSParameterProperty":return void this.checkLVal(t.parameter,r,n,"parameter property");case"TSAsExpression":case"TSNonNullExpression":case"TSTypeAssertion":return void this.checkLVal(t.expression,r,n,a);default:return void e.prototype.checkLVal.call(this,t,r,n,a)}},r.parseBindingAtom=function(){switch(this.state.type){case lb._this:return this.parseIdentifier(!0);default:return e.prototype.parseBindingAtom.call(this)}},r.parseMaybeDecoratorArguments=function(t){if(this.isRelational("<")){var r=this.tsParseTypeArguments();if(this.match(lb.parenL)){var n=e.prototype.parseMaybeDecoratorArguments.call(this,t);return n.typeParameters=r,n}this.unexpected(this.state.start,lb.parenL)}return e.prototype.parseMaybeDecoratorArguments.call(this,t)},r.isClassMethod=function(){return this.isRelational("<")||e.prototype.isClassMethod.call(this)},r.isClassProperty=function(){return this.match(lb.bang)||this.match(lb.colon)||e.prototype.isClassProperty.call(this)},r.parseMaybeDefault=function(){for(var t,r=arguments.length,n=new Array(r),a=0;a<r;a++)n[a]=arguments[a];var s=(t=e.prototype.parseMaybeDefault).call.apply(t,[this].concat(n));return"AssignmentPattern"===s.type&&s.typeAnnotation&&s.right.start<s.typeAnnotation.start&&this.raise(s.typeAnnotation.start,ux.TypeAnnotationAfterAssign),s},r.getTokenFromCode=function(t){return!this.state.inType||62!==t&&60!==t?e.prototype.getTokenFromCode.call(this,t):this.finishOp(lb.relational,1)},r.toAssignableList=function(t){for(var r=0;r<t.length;r++){var n=t[r];if(n)switch(n.type){case"TSTypeCastExpression":t[r]=this.typeCastToParameter(n);break;case"TSAsExpression":case"TSTypeAssertion":this.state.maybeInArrowParameters?this.raise(n.start,ux.UnexpectedTypeCastInParameter):t[r]=this.typeCastToParameter(n)}}return e.prototype.toAssignableList.apply(this,arguments)},r.typeCastToParameter=function(e){return e.expression.typeAnnotation=e.typeAnnotation,this.resetEndLocation(e.expression,e.typeAnnotation.end,e.typeAnnotation.loc.end),e.expression},r.toReferencedList=function(e,t){for(var r=0;r<e.length;r++){var n=e[r];n&&"TSTypeCastExpression"===n.type&&this.raise(n.start,ux.UnexpectedTypeAnnotation)}return e},r.shouldParseArrow=function(){return this.match(lb.colon)||e.prototype.shouldParseArrow.call(this)},r.shouldParseAsyncArrow=function(){return this.match(lb.colon)||e.prototype.shouldParseAsyncArrow.call(this)},r.canHaveLeadingDecorator=function(){return e.prototype.canHaveLeadingDecorator.call(this)||this.isAbstractClass()},r.jsxParseOpeningElementAfterName=function(t){var r=this;if(this.isRelational("<")){var n=this.tsTryParseAndCatch((function(){return r.tsParseTypeArguments()}));n&&(t.typeParameters=n)}return e.prototype.jsxParseOpeningElementAfterName.call(this,t)},r.getGetterSetterExpectedParamCount=function(t){var r=e.prototype.getGetterSetterExpectedParamCount.call(this,t),n=t.params[0];return n&&"Identifier"===n.type&&"this"===n.name?r+1:r},t}(e)},v8intrinsic:function(e){return function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.parseV8Intrinsic=function(){if(this.match(lb.modulo)){var e=this.state.start,t=this.startNode();if(this.eat(lb.modulo),this.match(lb.name)){var r=this.parseIdentifierName(this.state.start),n=this.createIdentifier(t,r);if(n.type="V8IntrinsicIdentifier",this.match(lb.parenL))return n}this.unexpected(e)}},r.parseExprAtom=function(){return this.parseV8Intrinsic()||e.prototype.parseExprAtom.apply(this,arguments)},t}(e)},placeholders:function(e){return function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.parsePlaceholder=function(t){if(this.match(lb.placeholder)){var r=this.startNode();return this.next(),this.assertNoSpace("Unexpected space in placeholder."),r.name=e.prototype.parseIdentifier.call(this,!0),this.assertNoSpace("Unexpected space in placeholder."),this.expect(lb.placeholder),this.finishPlaceholder(r,t)}},r.finishPlaceholder=function(e,t){var r=!(!e.expectedNode||"Placeholder"!==e.type);return e.expectedNode=t,r?e:this.finishNode(e,"Placeholder")},r.getTokenFromCode=function(t){return 37===t&&37===this.input.charCodeAt(this.state.pos+1)?this.finishOp(lb.placeholder,2):e.prototype.getTokenFromCode.apply(this,arguments)},r.parseExprAtom=function(){return this.parsePlaceholder("Expression")||e.prototype.parseExprAtom.apply(this,arguments)},r.parseIdentifier=function(){return this.parsePlaceholder("Identifier")||e.prototype.parseIdentifier.apply(this,arguments)},r.checkReservedWord=function(t){void 0!==t&&e.prototype.checkReservedWord.apply(this,arguments)},r.parseBindingAtom=function(){return this.parsePlaceholder("Pattern")||e.prototype.parseBindingAtom.apply(this,arguments)},r.checkLVal=function(t){"Placeholder"!==t.type&&e.prototype.checkLVal.apply(this,arguments)},r.toAssignable=function(t){return t&&"Placeholder"===t.type&&"Expression"===t.expectedNode?(t.expectedNode="Pattern",t):e.prototype.toAssignable.apply(this,arguments)},r.verifyBreakContinue=function(t){t.label&&"Placeholder"===t.label.type||e.prototype.verifyBreakContinue.apply(this,arguments)},r.parseExpressionStatement=function(t,r){if("Placeholder"!==r.type||r.extra&&r.extra.parenthesized)return e.prototype.parseExpressionStatement.apply(this,arguments);if(this.match(lb.colon)){var n=t;return n.label=this.finishPlaceholder(r,"Identifier"),this.next(),n.body=this.parseStatement("label"),this.finishNode(n,"LabeledStatement")}return this.semicolon(),t.name=r.name,this.finishPlaceholder(t,"Statement")},r.parseBlock=function(){return this.parsePlaceholder("BlockStatement")||e.prototype.parseBlock.apply(this,arguments)},r.parseFunctionId=function(){return this.parsePlaceholder("Identifier")||e.prototype.parseFunctionId.apply(this,arguments)},r.parseClass=function(e,t,r){var n=t?"ClassDeclaration":"ClassExpression";this.next(),this.takeDecorators(e);var a=this.parsePlaceholder("Identifier");if(a)if(this.match(lb._extends)||this.match(lb.placeholder)||this.match(lb.braceL))e.id=a;else{if(r||!t)return e.id=null,e.body=this.finishPlaceholder(a,"ClassBody"),this.finishNode(e,n);this.unexpected(null,"A class name is required")}else this.parseClassId(e,t,r);return this.parseClassSuper(e),e.body=this.parsePlaceholder("ClassBody")||this.parseClassBody(!!e.superClass),this.finishNode(e,n)},r.parseExport=function(t){var r=this.parsePlaceholder("Identifier");if(!r)return e.prototype.parseExport.apply(this,arguments);if(!this.isContextual("from")&&!this.match(lb.comma))return t.specifiers=[],t.source=null,t.declaration=this.finishPlaceholder(r,"Declaration"),this.finishNode(t,"ExportNamedDeclaration");this.expectPlugin("exportDefaultFrom");var n=this.startNode();return n.exported=r,t.specifiers=[this.finishNode(n,"ExportDefaultSpecifier")],e.prototype.parseExport.call(this,t)},r.maybeParseExportDefaultSpecifier=function(t){return!!(t.specifiers&&t.specifiers.length>0)||e.prototype.maybeParseExportDefaultSpecifier.apply(this,arguments)},r.checkExport=function(t){var r=t.specifiers;r&&r.length&&(t.specifiers=r.filter((function(e){return"Placeholder"===e.exported.type}))),e.prototype.checkExport.call(this,t),t.specifiers=r},r.parseImport=function(t){var r=this.parsePlaceholder("Identifier");if(!r)return e.prototype.parseImport.apply(this,arguments);if(t.specifiers=[],!this.isContextual("from")&&!this.match(lb.comma))return t.source=this.finishPlaceholder(r,"StringLiteral"),this.semicolon(),this.finishNode(t,"ImportDeclaration");var n=this.startNodeAtNode(r);if(n.local=r,this.finishNode(n,"ImportDefaultSpecifier"),t.specifiers.push(n),this.eat(lb.comma)){var a=this.maybeParseStarImportSpecifier(t);a||this.parseNamedImportSpecifiers(t)}return this.expectContextual("from"),t.source=this.parseImportSource(),this.semicolon(),this.finishNode(t,"ImportDeclaration")},r.parseImportSource=function(){return this.parsePlaceholder("StringLiteral")||e.prototype.parseImportSource.apply(this,arguments)},t}(e)}},fx=Object.keys(dx),hx={sourceType:"script",sourceFilename:void 0,startLine:1,allowAwaitOutsideFunction:!1,allowReturnOutsideFunction:!1,allowImportExportEverywhere:!1,allowSuperOutsideMethod:!1,allowUndeclaredExports:!1,plugins:[],strictMode:null,ranges:!1,tokens:!1,createParenthesizedExpressions:!1,errorRecovery:!1};var mx=function(){function e(){this.errors=[],this.potentialArrowAt=-1,this.noArrowAt=[],this.noArrowParamsConversionAt=[],this.inParameters=!1,this.maybeInArrowParameters=!1,this.inPipeline=!1,this.inType=!1,this.noAnonFunctionType=!1,this.inPropertyName=!1,this.hasFlowComment=!1,this.isIterator=!1,this.topicContext={maxNumOfResolvableTopics:0,maxTopicIndex:null},this.soloAwait=!1,this.inFSharpPipelineDirectBody=!1,this.labels=[],this.decoratorStack=[[]],this.yieldPos=-1,this.awaitPos=-1,this.comments=[],this.trailingComments=[],this.leadingComments=[],this.commentStack=[],this.commentPreviousNode=null,this.pos=0,this.lineStart=0,this.type=lb.eof,this.value=null,this.start=0,this.end=0,this.lastTokEndLoc=null,this.lastTokStartLoc=null,this.lastTokStart=0,this.lastTokEnd=0,this.context=[Sb.braceStatement],this.exprAllowed=!0,this.containsEsc=!1,this.octalPositions=[],this.exportedIdentifiers=[],this.tokensLength=0}var t=e.prototype;return t.init=function(e){this.strict=!1!==e.strictMode&&"module"===e.sourceType,this.curLine=e.startLine,this.startLoc=this.endLoc=this.curPosition()},t.curPosition=function(){return new yb(this.curLine,this.pos-this.lineStart)},t.clone=function(t){for(var r=new e,n=Object.keys(this),a=0,s=n.length;a<s;a++){var i=n[a],o=this[i];!t&&Array.isArray(o)&&(o=o.slice()),r[i]=o}return r},e}(),yx=function(e){return e>=48&&e<=57},gx=new Set(["g","m","s","i","y","u"]),vx={decBinOct:[46,66,69,79,95,98,101,111],hex:[46,88,95,120]},bx={bin:[48,49]};bx.oct=[].concat(bx.bin,[50,51,52,53,54,55]),bx.dec=[].concat(bx.oct,[56,57]),bx.hex=[].concat(bx.dec,[65,66,67,68,69,70,97,98,99,100,101,102]);var xx=function(e){this.type=e.type,this.value=e.value,this.start=e.start,this.end=e.end,this.loc=new gb(e.startLoc,e.endLoc)},Ex=function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.addExtra=function(e,t,r){e&&((e.extra=e.extra||{})[t]=r)},r.isRelational=function(e){return this.match(lb.relational)&&this.state.value===e},r.isLookaheadRelational=function(e){var t=this.nextTokenStart();if(this.input.charAt(t)===e){if(t+1===this.input.length)return!0;var r=this.input.charCodeAt(t+1);return r!==e.charCodeAt(0)&&61!==r}return!1},r.expectRelational=function(e){this.isRelational(e)?this.next():this.unexpected(null,lb.relational)},r.isContextual=function(e){return this.match(lb.name)&&this.state.value===e&&!this.state.containsEsc},r.isUnparsedContextual=function(e,t){var r=e+t.length;return this.input.slice(e,r)===t&&(r===this.input.length||!Gb(this.input.charCodeAt(r)))},r.isLookaheadContextual=function(e){var t=this.nextTokenStart();return this.isUnparsedContextual(t,e)},r.eatContextual=function(e){return this.isContextual(e)&&this.eat(lb.name)},r.expectContextual=function(e,t){this.eatContextual(e)||this.unexpected(null,t)},r.canInsertSemicolon=function(){return this.match(lb.eof)||this.match(lb.braceR)||this.hasPrecedingLineBreak()},r.hasPrecedingLineBreak=function(){return pb.test(this.input.slice(this.state.lastTokEnd,this.state.start))},r.isLineTerminator=function(){return this.eat(lb.semi)||this.canInsertSemicolon()},r.semicolon=function(){this.isLineTerminator()||this.unexpected(null,lb.semi)},r.expect=function(e,t){this.eat(e)||this.unexpected(t,e)},r.assertNoSpace=function(e){void 0===e&&(e="Unexpected space."),this.state.start>this.state.lastTokEnd&&this.raise(this.state.lastTokEnd,e)},r.unexpected=function(e,t){throw void 0===t&&(t="Unexpected token"),"string"!=typeof t&&(t='Unexpected token, expected "'+t.label+'"'),this.raise(null!=e?e:this.state.start,t)},r.expectPlugin=function(e,t){if(!this.hasPlugin(e))throw this.raiseWithData(null!=t?t:this.state.start,{missingPlugin:[e]},"This experimental syntax requires enabling the parser plugin: '"+e+"'");return!0},r.expectOnePlugin=function(e,t){var r=this;if(!e.some((function(e){return r.hasPlugin(e)})))throw this.raiseWithData(null!=t?t:this.state.start,{missingPlugin:e},"This experimental syntax requires enabling one of the following parser plugin(s): '"+e.join(", ")+"'")},r.checkYieldAwaitInDefaultParams=function(){-1!==this.state.yieldPos&&(-1===this.state.awaitPos||this.state.yieldPos<this.state.awaitPos)&&this.raise(this.state.yieldPos,"Yield cannot be used as name inside a generator function"),-1!==this.state.awaitPos&&this.raise(this.state.awaitPos,"Await cannot be used as name inside an async function")},r.tryParse=function(e,t){void 0===t&&(t=this.state.clone());var r={node:null};try{var n=e((function(e){throw void 0===e&&(e=null),r.node=e,r}));if(this.state.errors.length>t.errors.length){var a=this.state;return this.state=t,{node:n,error:a.errors[t.errors.length],thrown:!1,aborted:!1,failState:a}}return{node:n,error:null,thrown:!1,aborted:!1,failState:null}}catch(e){var s=this.state;if(this.state=t,e instanceof SyntaxError)return{node:null,error:e,thrown:!0,aborted:!1,failState:s};if(e===r)return{node:r.node,error:null,thrown:!1,aborted:!0,failState:s};throw e}},r.checkExpressionErrors=function(e,t){if(!e)return!1;var r=e.shorthandAssign,n=e.doubleProto;if(!t)return r>=0||n>=0;r>=0&&this.unexpected(r),n>=0&&this.raise(n,xb.DuplicateProto)},t}(function(e){function t(t,r){var n;return(n=e.call(this)||this).tokens=[],n.state=new mx,n.state.init(t),n.input=r,n.length=r.length,n.isLookahead=!1,n}a(t,e);var r=t.prototype;return r.pushToken=function(e){this.tokens.length=this.state.tokensLength,this.tokens.push(e),++this.state.tokensLength},r.next=function(){this.isLookahead||(this.checkKeywordEscapes(),this.options.tokens&&this.pushToken(new xx(this.state))),this.state.lastTokEnd=this.state.end,this.state.lastTokStart=this.state.start,this.state.lastTokEndLoc=this.state.endLoc,this.state.lastTokStartLoc=this.state.startLoc,this.nextToken()},r.eat=function(e){return!!this.match(e)&&(this.next(),!0)},r.match=function(e){return this.state.type===e},r.lookahead=function(){var e=this.state;this.state=e.clone(!0),this.isLookahead=!0,this.next(),this.isLookahead=!1;var t=this.state;return this.state=e,t},r.nextTokenStart=function(){var e=this.state.pos;return hb.lastIndex=e,e+hb.exec(this.input)[0].length},r.lookaheadCharCode=function(){return this.input.charCodeAt(this.nextTokenStart())},r.setStrict=function(e){if(this.state.strict=e,this.match(lb.num)||this.match(lb.string)){for(this.state.pos=this.state.start;this.state.pos<this.state.lineStart;)this.state.lineStart=this.input.lastIndexOf("\n",this.state.lineStart-2)+1,--this.state.curLine;this.nextToken()}},r.curContext=function(){return this.state.context[this.state.context.length-1]},r.nextToken=function(){var e=this.curContext();if(e&&e.preserveSpace||this.skipSpace(),this.state.octalPositions=[],this.state.start=this.state.pos,this.state.startLoc=this.state.curPosition(),this.state.pos>=this.length)this.finishToken(lb.eof);else{var t=null==e?void 0:e.override;t?t(this):this.getTokenFromCode(this.input.codePointAt(this.state.pos))}},r.pushComment=function(e,t,r,n,a,s){var i={type:e?"CommentBlock":"CommentLine",value:t,start:r,end:n,loc:new gb(a,s)};this.options.tokens&&this.pushToken(i),this.state.comments.push(i),this.addComment(i)},r.skipBlockComment=function(){var e,t=this.state.curPosition(),r=this.state.pos,n=this.input.indexOf("*/",this.state.pos+2);if(-1===n)throw this.raise(r,xb.UnterminatedComment);for(this.state.pos=n+2,db.lastIndex=r;(e=db.exec(this.input))&&e.index<this.state.pos;)++this.state.curLine,this.state.lineStart=e.index+e[0].length;this.isLookahead||this.pushComment(!0,this.input.slice(r+2,n),r,this.state.pos,t,this.state.curPosition())},r.skipLineComment=function(e){var t=this.state.pos,r=this.state.curPosition(),n=this.input.charCodeAt(this.state.pos+=e);if(this.state.pos<this.length)for(;!fb(n)&&++this.state.pos<this.length;)n=this.input.charCodeAt(this.state.pos);this.isLookahead||this.pushComment(!1,this.input.slice(t+e,this.state.pos),t,this.state.pos,r,this.state.curPosition())},r.skipSpace=function(){e:for(;this.state.pos<this.length;){var e=this.input.charCodeAt(this.state.pos);switch(e){case 32:case 160:case 9:++this.state.pos;break;case 13:10===this.input.charCodeAt(this.state.pos+1)&&++this.state.pos;case 10:case 8232:case 8233:++this.state.pos,++this.state.curLine,this.state.lineStart=this.state.pos;break;case 47:switch(this.input.charCodeAt(this.state.pos+1)){case 42:this.skipBlockComment();break;case 47:this.skipLineComment(2);break;default:break e}break;default:if(!mb(e))break e;++this.state.pos}}},r.finishToken=function(e,t){this.state.end=this.state.pos,this.state.endLoc=this.state.curPosition();var r=this.state.type;this.state.type=e,this.state.value=t,this.isLookahead||this.updateContext(r)},r.readToken_numberSign=function(){if(0!==this.state.pos||!this.readToken_interpreter()){var e=this.state.pos+1,t=this.input.charCodeAt(e);if(t>=48&&t<=57)throw this.raise(this.state.pos,xb.UnexpectedDigitAfterHash);if(!this.hasPlugin("classPrivateProperties")&&!this.hasPlugin("classPrivateMethods")&&"smart"!==this.getPluginOption("pipelineOperator","proposal"))throw this.raise(this.state.pos,xb.InvalidOrUnexpectedToken,"#");this.finishOp(lb.hash,1)}},r.readToken_dot=function(){var e=this.input.charCodeAt(this.state.pos+1);e>=48&&e<=57?this.readNumber(!0):46===e&&46===this.input.charCodeAt(this.state.pos+2)?(this.state.pos+=3,this.finishToken(lb.ellipsis)):(++this.state.pos,this.finishToken(lb.dot))},r.readToken_slash=function(){if(this.state.exprAllowed&&!this.state.inType)return++this.state.pos,void this.readRegexp();61===this.input.charCodeAt(this.state.pos+1)?this.finishOp(lb.assign,2):this.finishOp(lb.slash,1)},r.readToken_interpreter=function(){if(0!==this.state.pos||this.length<2)return!1;var e=this.state.pos;this.state.pos+=1;var t=this.input.charCodeAt(this.state.pos);if(33!==t)return!1;for(;!fb(t)&&++this.state.pos<this.length;)t=this.input.charCodeAt(this.state.pos);var r=this.input.slice(e+2,this.state.pos);return this.finishToken(lb.interpreterDirective,r),!0},r.readToken_mult_modulo=function(e){var t=42===e?lb.star:lb.modulo,r=1,n=this.input.charCodeAt(this.state.pos+1),a=this.state.exprAllowed;42===e&&42===n&&(r++,n=this.input.charCodeAt(this.state.pos+2),t=lb.exponent),61!==n||a||(r++,t=lb.assign),this.finishOp(t,r)},r.readToken_pipe_amp=function(e){var t=this.input.charCodeAt(this.state.pos+1);t!==e?124!==e||62!==t?61!==t?this.finishOp(124===e?lb.bitwiseOR:lb.bitwiseAND,1):this.finishOp(lb.assign,2):this.finishOp(lb.pipeline,2):61===this.input.charCodeAt(this.state.pos+2)?this.finishOp(lb.assign,3):this.finishOp(124===e?lb.logicalOR:lb.logicalAND,2)},r.readToken_caret=function(){61===this.input.charCodeAt(this.state.pos+1)?this.finishOp(lb.assign,2):this.finishOp(lb.bitwiseXOR,1)},r.readToken_plus_min=function(e){var t=this.input.charCodeAt(this.state.pos+1);if(t===e)return 45!==t||this.inModule||62!==this.input.charCodeAt(this.state.pos+2)||0!==this.state.lastTokEnd&&!pb.test(this.input.slice(this.state.lastTokEnd,this.state.pos))?void this.finishOp(lb.incDec,2):(this.skipLineComment(3),this.skipSpace(),void this.nextToken());61===t?this.finishOp(lb.assign,2):this.finishOp(lb.plusMin,1)},r.readToken_lt_gt=function(e){var t=this.input.charCodeAt(this.state.pos+1),r=1;return t===e?(r=62===e&&62===this.input.charCodeAt(this.state.pos+2)?3:2,61===this.input.charCodeAt(this.state.pos+r)?void this.finishOp(lb.assign,r+1):void this.finishOp(lb.bitShift,r)):33!==t||60!==e||this.inModule||45!==this.input.charCodeAt(this.state.pos+2)||45!==this.input.charCodeAt(this.state.pos+3)?(61===t&&(r=2),void this.finishOp(lb.relational,r)):(this.skipLineComment(4),this.skipSpace(),void this.nextToken())},r.readToken_eq_excl=function(e){var t=this.input.charCodeAt(this.state.pos+1);if(61!==t)return 61===e&&62===t?(this.state.pos+=2,void this.finishToken(lb.arrow)):void this.finishOp(61===e?lb.eq:lb.bang,1);this.finishOp(lb.equality,61===this.input.charCodeAt(this.state.pos+2)?3:2)},r.readToken_question=function(){var e=this.input.charCodeAt(this.state.pos+1),t=this.input.charCodeAt(this.state.pos+2);63!==e||this.state.inType?46!==e||t>=48&&t<=57?(++this.state.pos,this.finishToken(lb.question)):(this.state.pos+=2,this.finishToken(lb.questionDot)):61===t?this.finishOp(lb.assign,3):this.finishOp(lb.nullishCoalescing,2)},r.getTokenFromCode=function(e){switch(e){case 46:return void this.readToken_dot();case 40:return++this.state.pos,void this.finishToken(lb.parenL);case 41:return++this.state.pos,void this.finishToken(lb.parenR);case 59:return++this.state.pos,void this.finishToken(lb.semi);case 44:return++this.state.pos,void this.finishToken(lb.comma);case 91:return++this.state.pos,void this.finishToken(lb.bracketL);case 93:return++this.state.pos,void this.finishToken(lb.bracketR);case 123:return++this.state.pos,void this.finishToken(lb.braceL);case 125:return++this.state.pos,void this.finishToken(lb.braceR);case 58:return void(this.hasPlugin("functionBind")&&58===this.input.charCodeAt(this.state.pos+1)?this.finishOp(lb.doubleColon,2):(++this.state.pos,this.finishToken(lb.colon)));case 63:return void this.readToken_question();case 96:return++this.state.pos,void this.finishToken(lb.backQuote);case 48:var t=this.input.charCodeAt(this.state.pos+1);if(120===t||88===t)return void this.readRadixNumber(16);if(111===t||79===t)return void this.readRadixNumber(8);if(98===t||66===t)return void this.readRadixNumber(2);case 49:case 50:case 51:case 52:case 53:case 54:case 55:case 56:case 57:return void this.readNumber(!1);case 34:case 39:return void this.readString(e);case 47:return void this.readToken_slash();case 37:case 42:return void this.readToken_mult_modulo(e);case 124:case 38:return void this.readToken_pipe_amp(e);case 94:return void this.readToken_caret();case 43:case 45:return void this.readToken_plus_min(e);case 60:case 62:return void this.readToken_lt_gt(e);case 61:case 33:return void this.readToken_eq_excl(e);case 126:return void this.finishOp(lb.tilde,1);case 64:return++this.state.pos,void this.finishToken(lb.at);case 35:return void this.readToken_numberSign();case 92:return void this.readWord();default:if(Ub(e))return void this.readWord()}throw this.raise(this.state.pos,xb.InvalidOrUnexpectedToken,String.fromCodePoint(e))},r.finishOp=function(e,t){var r=this.input.slice(this.state.pos,this.state.pos+t);this.state.pos+=t,this.finishToken(e,r)},r.readRegexp=function(){for(var e,t,r=this.state.pos;;){if(this.state.pos>=this.length)throw this.raise(r,xb.UnterminatedRegExp);var n=this.input.charAt(this.state.pos);if(pb.test(n))throw this.raise(r,xb.UnterminatedRegExp);if(e)e=!1;else{if("["===n)t=!0;else if("]"===n&&t)t=!1;else if("/"===n&&!t)break;e="\\"===n}++this.state.pos}var a=this.input.slice(r,this.state.pos);++this.state.pos;for(var s="";this.state.pos<this.length;){var i=this.input[this.state.pos],o=this.input.codePointAt(this.state.pos);if(gx.has(i))s.indexOf(i)>-1&&this.raise(this.state.pos+1,xb.DuplicateRegExpFlags);else{if(!Gb(o)&&92!==o)break;this.raise(this.state.pos+1,xb.MalformedRegExpFlags)}++this.state.pos,s+=i}this.finishToken(lb.regexp,{pattern:a,flags:s})},r.readInt=function(e,t,r,n){void 0===n&&(n=!0);for(var a=this.state.pos,s=16===e?vx.hex:vx.decBinOct,i=16===e?bx.hex:10===e?bx.dec:8===e?bx.oct:bx.bin,o=!1,u=0,c=0,l=null==t?1/0:t;c<l;++c){var p=this.input.charCodeAt(this.state.pos),d=void 0;if(this.hasPlugin("numericSeparator")&&95===p){var f=this.input.charCodeAt(this.state.pos-1),h=this.input.charCodeAt(this.state.pos+1);(-1===i.indexOf(h)||s.indexOf(f)>-1||s.indexOf(h)>-1||Number.isNaN(h))&&this.raise(this.state.pos,xb.UnexpectedNumericSeparator),n||this.raise(this.state.pos,xb.NumericSeparatorInEscapeSequence),++this.state.pos}else{if((d=p>=97?p-97+10:p>=65?p-65+10:yx(p)?p-48:1/0)>=e)if(this.options.errorRecovery&&d<=9)d=0,this.raise(this.state.start+c+2,xb.InvalidDigit,e);else{if(!r)break;d=0,o=!0}++this.state.pos,u=u*e+d}}return this.state.pos===a||null!=t&&this.state.pos-a!==t||o?null:u},r.readRadixNumber=function(e){var t=this.state.pos,r=!1;this.state.pos+=2;var n=this.readInt(e);if(null==n&&this.raise(this.state.start+2,xb.InvalidDigit,e),this.hasPlugin("bigInt")&&110===this.input.charCodeAt(this.state.pos)&&(++this.state.pos,r=!0),Ub(this.input.codePointAt(this.state.pos)))throw this.raise(this.state.pos,xb.NumberIdentifier);if(r){var a=this.input.slice(t,this.state.pos).replace(/[_n]/g,"");this.finishToken(lb.bigint,a)}else this.finishToken(lb.num,n)},r.readNumber=function(e){var t=this.state.pos,r=!1,n=!1,a=!1;e||null!==this.readInt(10)||this.raise(t,xb.InvalidNumber);var s=this.state.pos-t>=2&&48===this.input.charCodeAt(t);s&&(this.state.strict&&this.raise(t,xb.StrictOctalLiteral),/[89]/.test(this.input.slice(t,this.state.pos))&&(s=!1,a=!0));var i=this.input.charCodeAt(this.state.pos);if(46!==i||s||(++this.state.pos,this.readInt(10),r=!0,i=this.input.charCodeAt(this.state.pos)),69!==i&&101!==i||s||(43!==(i=this.input.charCodeAt(++this.state.pos))&&45!==i||++this.state.pos,null===this.readInt(10)&&this.raise(t,"Invalid number"),r=!0,i=this.input.charCodeAt(this.state.pos)),this.hasPlugin("numericSeparator")&&(s||a)){var o=this.input.slice(t,this.state.pos).indexOf("_");o>0&&this.raise(o+t,xb.ZeroDigitNumericSeparator)}if(this.hasPlugin("bigInt")&&110===i&&((r||s||a)&&this.raise(t,"Invalid BigIntLiteral"),++this.state.pos,n=!0),Ub(this.input.codePointAt(this.state.pos)))throw this.raise(this.state.pos,xb.NumberIdentifier);var u=this.input.slice(t,this.state.pos).replace(/[_n]/g,"");if(n)this.finishToken(lb.bigint,u);else{var c=s?parseInt(u,8):parseFloat(u);this.finishToken(lb.num,c)}},r.readCodePoint=function(e){var t;if(123===this.input.charCodeAt(this.state.pos)){var r=++this.state.pos;if(t=this.readHexChar(this.input.indexOf("}",this.state.pos)-this.state.pos,!0,e),++this.state.pos,null!==t&&t>1114111){if(!e)return null;this.raise(r,xb.InvalidCodePoint)}}else t=this.readHexChar(4,!1,e);return t},r.readString=function(e){for(var t="",r=++this.state.pos;;){if(this.state.pos>=this.length)throw this.raise(this.state.start,xb.UnterminatedString);var n=this.input.charCodeAt(this.state.pos);if(n===e)break;if(92===n)t+=this.input.slice(r,this.state.pos),t+=this.readEscapedChar(!1),r=this.state.pos;else if(8232===n||8233===n)++this.state.pos,++this.state.curLine,this.state.lineStart=this.state.pos;else{if(fb(n))throw this.raise(this.state.start,xb.UnterminatedString);++this.state.pos}}t+=this.input.slice(r,this.state.pos++),this.finishToken(lb.string,t)},r.readTmplToken=function(){for(var e="",t=this.state.pos,r=!1;;){if(this.state.pos>=this.length)throw this.raise(this.state.start,xb.UnterminatedTemplate);var n=this.input.charCodeAt(this.state.pos);if(96===n||36===n&&123===this.input.charCodeAt(this.state.pos+1))return this.state.pos===this.state.start&&this.match(lb.template)?36===n?(this.state.pos+=2,void this.finishToken(lb.dollarBraceL)):(++this.state.pos,void this.finishToken(lb.backQuote)):(e+=this.input.slice(t,this.state.pos),void this.finishToken(lb.template,r?null:e));if(92===n){e+=this.input.slice(t,this.state.pos);var a=this.readEscapedChar(!0);null===a?r=!0:e+=a,t=this.state.pos}else if(fb(n)){switch(e+=this.input.slice(t,this.state.pos),++this.state.pos,n){case 13:10===this.input.charCodeAt(this.state.pos)&&++this.state.pos;case 10:e+="\n";break;default:e+=String.fromCharCode(n)}++this.state.curLine,this.state.lineStart=this.state.pos,t=this.state.pos}else++this.state.pos}},r.readEscapedChar=function(e){var t=!e,r=this.input.charCodeAt(++this.state.pos);switch(++this.state.pos,r){case 110:return"\n";case 114:return"\r";case 120:var n=this.readHexChar(2,!1,t);return null===n?null:String.fromCharCode(n);case 117:var a=this.readCodePoint(t);return null===a?null:String.fromCodePoint(a);case 116:return"\t";case 98:return"\b";case 118:return"\v";case 102:return"\f";case 13:10===this.input.charCodeAt(this.state.pos)&&++this.state.pos;case 10:this.state.lineStart=this.state.pos,++this.state.curLine;case 8232:case 8233:return"";case 56:case 57:if(e)return null;default:if(r>=48&&r<=55){var s=this.state.pos-1,i=this.input.substr(this.state.pos-1,3).match(/^[0-7]+/)[0],o=parseInt(i,8);o>255&&(i=i.slice(0,-1),o=parseInt(i,8)),this.state.pos+=i.length-1;var u=this.input.charCodeAt(this.state.pos);if("0"!==i||56===u||57===u){if(e)return null;this.state.strict?this.raise(s,xb.StrictOctalLiteral):this.state.octalPositions.push(s)}return String.fromCharCode(o)}return String.fromCharCode(r)}},r.readHexChar=function(e,t,r){var n=this.state.pos,a=this.readInt(16,e,t,!1);return null===a&&(r?this.raise(n,xb.InvalidEscapeSequence):this.state.pos=n-1),a},r.readWord1=function(){var e="";this.state.containsEsc=!1;for(var t=this.state.pos,r=this.state.pos;this.state.pos<this.length;){var n=this.input.codePointAt(this.state.pos);if(Gb(n))this.state.pos+=n<=65535?1:2;else if(this.state.isIterator&&64===n)++this.state.pos;else{if(92!==n)break;this.state.containsEsc=!0,e+=this.input.slice(r,this.state.pos);var a=this.state.pos,s=this.state.pos===t?Ub:Gb;if(117!==this.input.charCodeAt(++this.state.pos)){this.raise(this.state.pos,xb.MissingUnicodeEscape);continue}++this.state.pos;var i=this.readCodePoint(!0);null!==i&&(s(i)||this.raise(a,xb.EscapedCharNotAnIdentifier),e+=String.fromCodePoint(i)),r=this.state.pos}}return e+this.input.slice(r,this.state.pos)},r.isIterator=function(e){return"@@iterator"===e||"@@asyncIterator"===e},r.readWord=function(){var e=this.readWord1(),t=ob.get(e)||lb.name;!this.state.isIterator||this.isIterator(e)&&this.state.inType||this.raise(this.state.pos,xb.InvalidIdentifier,e),this.finishToken(t,e)},r.checkKeywordEscapes=function(){var e=this.state.type.keyword;e&&this.state.containsEsc&&this.raise(this.state.start,xb.InvalidEscapedReservedWord,e)},r.braceIsBlock=function(e){var t=this.curContext();return t===Sb.functionExpression||t===Sb.functionStatement||(e!==lb.colon||t!==Sb.braceStatement&&t!==Sb.braceExpression?e===lb._return||e===lb.name&&this.state.exprAllowed?pb.test(this.input.slice(this.state.lastTokEnd,this.state.start)):e===lb._else||e===lb.semi||e===lb.eof||e===lb.parenR||e===lb.arrow||(e===lb.braceL?t===Sb.braceStatement:e!==lb._var&&e!==lb._const&&e!==lb.name&&(e===lb.relational||!this.state.exprAllowed)):!t.isExpr)},r.updateContext=function(e){var t,r=this.state.type;!r.keyword||e!==lb.dot&&e!==lb.questionDot?(t=r.updateContext)?t.call(this,e):this.state.exprAllowed=r.beforeExpr:this.state.exprAllowed=!1},t}(Eb)),Ax=function(){this.shorthandAssign=-1,this.doubleProto=-1},wx=function(){function e(e,t,r){this.type="",this.start=t,this.end=0,this.loc=new gb(r),e&&e.options.ranges&&(this.range=[t,0]),e&&e.filename&&(this.loc.filename=e.filename)}return e.prototype.__clone=function(){for(var t=new e,r=Object.keys(this),n=0,a=r.length;n<a;n++){var s=r[n];"leadingComments"!==s&&"trailingComments"!==s&&"innerComments"!==s&&(t[s]=this[s])}return t},e}(),Sx=function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.startNode=function(){return new wx(this,this.state.start,this.state.startLoc)},r.startNodeAt=function(e,t){return new wx(this,e,t)},r.startNodeAtNode=function(e){return this.startNodeAt(e.start,e.loc.start)},r.finishNode=function(e,t){return this.finishNodeAt(e,t,this.state.lastTokEnd,this.state.lastTokEndLoc)},r.finishNodeAt=function(e,t,r,n){if(e.end>0)throw new Error("Do not call finishNode*() twice on the same node. Instead use resetEndLocation() or change type directly.");return e.type=t,e.end=r,e.loc.end=n,this.options.ranges&&(e.range[1]=r),this.processComment(e),e},r.resetStartLocation=function(e,t,r){e.start=t,e.loc.start=r,this.options.ranges&&(e.range[0]=t)},r.resetEndLocation=function(e,t,r){void 0===t&&(t=this.state.lastTokEnd),void 0===r&&(r=this.state.lastTokEndLoc),e.end=t,e.loc.end=r,this.options.ranges&&(e.range[1]=t)},r.resetStartLocationFromNode=function(e,t){this.resetStartLocation(e,t.start,t.loc.start)},t}(Ex),Dx=function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.checkDuplicatedProto=function(e,t,r){if(!("SpreadElement"===e.type||e.computed||e.kind||e.shorthand)){var n=e.key;"__proto__"===("Identifier"===n.type?n.name:String(n.value))&&(t.used&&(r?-1===r.doubleProto&&(r.doubleProto=n.start):this.raise(n.start,xb.DuplicateProto)),t.used=!0)}},r.getExpression=function(){var e=0;this.hasPlugin("topLevelAwait")&&this.inModule&&(e|=2),this.scope.enter(1),this.prodParam.enter(e),this.nextToken();var t=this.parseExpression();return this.match(lb.eof)||this.unexpected(),t.comments=this.state.comments,t.errors=this.state.errors,t},r.parseExpression=function(e,t){var r=this.state.start,n=this.state.startLoc,a=this.parseMaybeAssign(e,t);if(this.match(lb.comma)){var s=this.startNodeAt(r,n);for(s.expressions=[a];this.eat(lb.comma);)s.expressions.push(this.parseMaybeAssign(e,t));return this.toReferencedList(s.expressions),this.finishNode(s,"SequenceExpression")}return a},r.parseMaybeAssign=function(e,t,r,n){var a,s=this.state.start,i=this.state.startLoc;if(this.isContextual("yield")){if(this.prodParam.hasYield){var o=this.parseYield(e);return r&&(o=r.call(this,o,s,i)),o}this.state.exprAllowed=!1}t?a=!1:(t=new Ax,a=!0),(this.match(lb.parenL)||this.match(lb.name))&&(this.state.potentialArrowAt=this.state.start);var u=this.parseMaybeConditional(e,t,n);if(r&&(u=r.call(this,u,s,i)),this.state.type.isAssign){var c=this.startNodeAt(s,i),l=this.state.value;return c.operator=l,"??="===l&&this.expectPlugin("logicalAssignment"),"||="!==l&&"&&="!==l||this.expectPlugin("logicalAssignment"),this.match(lb.eq)?(c.left=this.toAssignable(u),t.doubleProto=-1):c.left=u,t.shorthandAssign>=c.left.start&&(t.shorthandAssign=-1),this.checkLVal(u,void 0,void 0,"assignment expression"),this.next(),c.right=this.parseMaybeAssign(e),this.finishNode(c,"AssignmentExpression")}return a&&this.checkExpressionErrors(t,!0),u},r.parseMaybeConditional=function(e,t,r){var n=this.state.start,a=this.state.startLoc,s=this.state.potentialArrowAt,i=this.parseExprOps(e,t);return"ArrowFunctionExpression"===i.type&&i.start===s||this.checkExpressionErrors(t,!1)?i:this.parseConditional(i,e,n,a,r)},r.parseConditional=function(e,t,r,n,a){if(this.eat(lb.question)){var s=this.startNodeAt(r,n);return s.test=e,s.consequent=this.parseMaybeAssign(),this.expect(lb.colon),s.alternate=this.parseMaybeAssign(t),this.finishNode(s,"ConditionalExpression")}return e},r.parseExprOps=function(e,t){var r=this.state.start,n=this.state.startLoc,a=this.state.potentialArrowAt,s=this.parseMaybeUnary(t);return"ArrowFunctionExpression"===s.type&&s.start===a||this.checkExpressionErrors(t,!1)?s:this.parseExprOp(s,r,n,-1,e)},r.parseExprOp=function(e,t,r,n,a){var s=this.state.type.binop;if(!(null==s||a&&this.match(lb._in))&&s>n){var i=this.state.value;if("|>"===i&&this.state.inFSharpPipelineDirectBody)return e;var o=this.startNodeAt(t,r);o.left=e,o.operator=i,"**"!==i||"UnaryExpression"!==e.type||!this.options.createParenthesizedExpressions&&e.extra&&e.extra.parenthesized||this.raise(e.argument.start,xb.UnexpectedTokenUnaryExponentiation);var u=this.state.type,c=u===lb.logicalOR||u===lb.logicalAND,l=u===lb.nullishCoalescing;if(u===lb.pipeline?(this.expectPlugin("pipelineOperator"),this.state.inPipeline=!0,this.checkPipelineAtInfixOperator(e,t)):l&&(s=lb.logicalAND.binop),this.next(),u===lb.pipeline&&"minimal"===this.getPluginOption("pipelineOperator","proposal")&&this.match(lb.name)&&"await"===this.state.value&&this.prodParam.hasAwait)throw this.raise(this.state.start,xb.UnexpectedAwaitAfterPipelineBody);o.right=this.parseExprOpRightExpr(u,s,a),this.finishNode(o,c||l?"LogicalExpression":"BinaryExpression");var p=this.state.type;if(l&&(p===lb.logicalOR||p===lb.logicalAND)||c&&p===lb.nullishCoalescing)throw this.raise(this.state.start,xb.MixingCoalesceWithLogical);return this.parseExprOp(o,t,r,n,a)}return e},r.parseExprOpRightExpr=function(e,t,r){var n=this,a=this.state.start,s=this.state.startLoc;switch(e){case lb.pipeline:switch(this.getPluginOption("pipelineOperator","proposal")){case"smart":return this.withTopicPermittingContext((function(){return n.parseSmartPipelineBody(n.parseExprOpBaseRightExpr(e,t,r),a,s)}));case"fsharp":return this.withSoloAwaitPermittingContext((function(){return n.parseFSharpPipelineBody(t,r)}))}default:return this.parseExprOpBaseRightExpr(e,t,r)}},r.parseExprOpBaseRightExpr=function(e,t,r){var n=this.state.start,a=this.state.startLoc;return this.parseExprOp(this.parseMaybeUnary(),n,a,e.rightAssociative?t-1:t,r)},r.parseMaybeUnary=function(e){if(this.isContextual("await")&&this.isAwaitAllowed())return this.parseAwait();if(this.state.type.prefix){var t=this.startNode(),r=this.match(lb.incDec);if(t.operator=this.state.value,t.prefix=!0,"throw"===t.operator&&this.expectPlugin("throwExpressions"),this.next(),t.argument=this.parseMaybeUnary(),this.checkExpressionErrors(e,!0),r)this.checkLVal(t.argument,void 0,void 0,"prefix operation");else if(this.state.strict&&"delete"===t.operator){var n=t.argument;"Identifier"===n.type?this.raise(t.start,xb.StrictDelete):"MemberExpression"===n.type&&"PrivateName"===n.property.type&&this.raise(t.start,xb.DeletePrivateField)}return this.finishNode(t,r?"UpdateExpression":"UnaryExpression")}var a=this.state.start,s=this.state.startLoc,i=this.parseExprSubscripts(e);if(this.checkExpressionErrors(e,!1))return i;for(;this.state.type.postfix&&!this.canInsertSemicolon();){var o=this.startNodeAt(a,s);o.operator=this.state.value,o.prefix=!1,o.argument=i,this.checkLVal(i,void 0,void 0,"postfix operation"),this.next(),i=this.finishNode(o,"UpdateExpression")}return i},r.parseExprSubscripts=function(e){var t=this.state.start,r=this.state.startLoc,n=this.state.potentialArrowAt,a=this.parseExprAtom(e);return"ArrowFunctionExpression"===a.type&&a.start===n?a:this.parseSubscripts(a,t,r)},r.parseSubscripts=function(e,t,r,n){var a={optionalChainMember:!1,maybeAsyncArrow:this.atPossibleAsync(e),stop:!1};do{e=this.parseSubscript(e,t,r,n,a),a.maybeAsyncArrow=!1}while(!a.stop);return e},r.parseSubscript=function(e,t,r,n,a){if(!n&&this.eat(lb.doubleColon)){var s=this.startNodeAt(t,r);return s.object=e,s.callee=this.parseNoCallExpr(),a.stop=!0,this.parseSubscripts(this.finishNode(s,"BindExpression"),t,r,n)}var i=!1;if(this.match(lb.questionDot)){if(a.optionalChainMember=i=!0,n&&40===this.lookaheadCharCode())return a.stop=!0,e;this.next()}var o=this.eat(lb.bracketL);if(i&&!this.match(lb.parenL)&&!this.match(lb.backQuote)||o||this.eat(lb.dot)){var u=this.startNodeAt(t,r);return u.object=e,u.property=o?this.parseExpression():i?this.parseIdentifier(!0):this.parseMaybePrivateName(!0),u.computed=o,"PrivateName"===u.property.type&&("Super"===u.object.type&&this.raise(t,xb.SuperPrivateField),this.classScope.usePrivateName(u.property.id.name,u.property.start)),o&&this.expect(lb.bracketR),a.optionalChainMember?(u.optional=i,this.finishNode(u,"OptionalMemberExpression")):this.finishNode(u,"MemberExpression")}if(!n&&this.match(lb.parenL)){var c=this.state.maybeInArrowParameters,l=this.state.yieldPos,p=this.state.awaitPos;this.state.maybeInArrowParameters=!0,this.state.yieldPos=-1,this.state.awaitPos=-1,this.next();var d=this.startNodeAt(t,r);return d.callee=e,i?(d.optional=!0,d.arguments=this.parseCallExpressionArguments(lb.parenR,!1)):d.arguments=this.parseCallExpressionArguments(lb.parenR,a.maybeAsyncArrow,"Import"===e.type,"Super"!==e.type,d),this.finishCallExpression(d,a.optionalChainMember),a.maybeAsyncArrow&&this.shouldParseAsyncArrow()&&!i?(a.stop=!0,d=this.parseAsyncArrowFromCallExpression(this.startNodeAt(t,r),d),this.checkYieldAwaitInDefaultParams(),this.state.yieldPos=l,this.state.awaitPos=p):(this.toReferencedListDeep(d.arguments),-1!==l&&(this.state.yieldPos=l),(this.isAwaitAllowed()||c)&&-1===p||(this.state.awaitPos=p)),this.state.maybeInArrowParameters=c,d}return this.match(lb.backQuote)?this.parseTaggedTemplateExpression(t,r,e,a):(a.stop=!0,e)},r.parseTaggedTemplateExpression=function(e,t,r,n,a){var s=this.startNodeAt(e,t);return s.tag=r,s.quasi=this.parseTemplate(!0),a&&(s.typeParameters=a),n.optionalChainMember&&this.raise(e,xb.OptionalChainingNoTemplate),this.finishNode(s,"TaggedTemplateExpression")},r.atPossibleAsync=function(e){return"Identifier"===e.type&&"async"===e.name&&this.state.lastTokEnd===e.end&&!this.canInsertSemicolon()&&"async"===this.input.slice(e.start,e.end)},r.finishCallExpression=function(e,t){if("Import"===e.callee.type)if(1!==e.arguments.length)this.raise(e.start,xb.ImportCallArity);else{var r=e.arguments[0];r&&"SpreadElement"===r.type&&this.raise(r.start,xb.ImportCallSpreadArgument)}return this.finishNode(e,t?"OptionalCallExpression":"CallExpression")},r.parseCallExpressionArguments=function(e,t,r,n,a){var s,i=[],o=!0,u=this.state.inFSharpPipelineDirectBody;for(this.state.inFSharpPipelineDirectBody=!1;!this.eat(e);){if(o)o=!1;else if(this.expect(lb.comma),this.match(e)){r&&this.raise(this.state.lastTokStart,xb.ImportCallArgumentTrailingComma),a&&this.addExtra(a,"trailingComma",this.state.lastTokStart),this.next();break}this.match(lb.parenL)&&!s&&(s=this.state.start),i.push(this.parseExprListItem(!1,t?new Ax:void 0,t?{start:0}:void 0,n))}return t&&s&&this.shouldParseAsyncArrow()&&this.unexpected(),this.state.inFSharpPipelineDirectBody=u,i},r.shouldParseAsyncArrow=function(){return this.match(lb.arrow)&&!this.canInsertSemicolon()},r.parseAsyncArrowFromCallExpression=function(e,t){var r;return this.expect(lb.arrow),this.parseArrowExpression(e,t.arguments,!0,null==(r=t.extra)?void 0:r.trailingComma),e},r.parseNoCallExpr=function(){var e=this.state.start,t=this.state.startLoc;return this.parseSubscripts(this.parseExprAtom(),e,t,!0)},r.parseExprAtom=function(e){this.state.type===lb.slash&&this.readRegexp();var t,r=this.state.potentialArrowAt===this.state.start;switch(this.state.type){case lb._super:return t=this.startNode(),this.next(),!this.match(lb.parenL)||this.scope.allowDirectSuper||this.options.allowSuperOutsideMethod?this.scope.allowSuper||this.options.allowSuperOutsideMethod||this.raise(t.start,xb.UnexpectedSuper):this.raise(t.start,xb.SuperNotAllowed),this.match(lb.parenL)||this.match(lb.bracketL)||this.match(lb.dot)||this.raise(t.start,xb.UnsupportedSuper),this.finishNode(t,"Super");case lb._import:return t=this.startNode(),this.next(),this.match(lb.dot)?this.parseImportMetaProperty(t):(this.match(lb.parenL)||this.raise(this.state.lastTokStart,xb.UnsupportedImport),this.finishNode(t,"Import"));case lb._this:return t=this.startNode(),this.next(),this.finishNode(t,"ThisExpression");case lb.name:t=this.startNode();var n=this.state.containsEsc,a=this.parseIdentifier();if(!n&&"async"===a.name&&this.match(lb._function)&&!this.canInsertSemicolon()){var s=this.state.context.length-1;if(this.state.context[s]!==Sb.functionStatement)throw new Error("Internal error");return this.state.context[s]=Sb.functionExpression,this.next(),this.parseFunction(t,void 0,!0)}if(r&&!n&&"async"===a.name&&this.match(lb.name)&&!this.canInsertSemicolon()){var i=this.state.maybeInArrowParameters,o=this.state.yieldPos,u=this.state.awaitPos;this.state.maybeInArrowParameters=!0,this.state.yieldPos=-1,this.state.awaitPos=-1;var c=[this.parseIdentifier()];return this.expect(lb.arrow),this.checkYieldAwaitInDefaultParams(),this.state.maybeInArrowParameters=i,this.state.yieldPos=o,this.state.awaitPos=u,this.parseArrowExpression(t,c,!0),t}return r&&this.match(lb.arrow)&&!this.canInsertSemicolon()?(this.next(),this.parseArrowExpression(t,[a],!1),t):a;case lb._do:this.expectPlugin("doExpressions");var l=this.startNode();this.next();var p=this.state.labels;return this.state.labels=[],l.body=this.parseBlock(),this.state.labels=p,this.finishNode(l,"DoExpression");case lb.regexp:var d=this.state.value;return(t=this.parseLiteral(d.value,"RegExpLiteral")).pattern=d.pattern,t.flags=d.flags,t;case lb.num:return this.parseLiteral(this.state.value,"NumericLiteral");case lb.bigint:return this.parseLiteral(this.state.value,"BigIntLiteral");case lb.string:return this.parseLiteral(this.state.value,"StringLiteral");case lb._null:return t=this.startNode(),this.next(),this.finishNode(t,"NullLiteral");case lb._true:case lb._false:return this.parseBooleanLiteral();case lb.parenL:return this.parseParenAndDistinguishExpression(r);case lb.bracketL:var f=this.state.inFSharpPipelineDirectBody;return this.state.inFSharpPipelineDirectBody=!1,t=this.startNode(),this.next(),t.elements=this.parseExprList(lb.bracketR,!0,e,t),this.state.maybeInArrowParameters||this.toReferencedList(t.elements),this.state.inFSharpPipelineDirectBody=f,this.finishNode(t,"ArrayExpression");case lb.braceL:var h=this.state.inFSharpPipelineDirectBody;this.state.inFSharpPipelineDirectBody=!1;var m=this.parseObj(!1,e);return this.state.inFSharpPipelineDirectBody=h,m;case lb._function:return this.parseFunctionExpression();case lb.at:this.parseDecorators();case lb._class:return t=this.startNode(),this.takeDecorators(t),this.parseClass(t,!1);case lb._new:return this.parseNew();case lb.backQuote:return this.parseTemplate(!1);case lb.doubleColon:t=this.startNode(),this.next(),t.object=null;var y=t.callee=this.parseNoCallExpr();if("MemberExpression"===y.type)return this.finishNode(t,"BindExpression");throw this.raise(y.start,xb.UnsupportedBind);case lb.hash:if(this.state.inPipeline)return t=this.startNode(),"smart"!==this.getPluginOption("pipelineOperator","proposal")&&this.raise(t.start,xb.PrimaryTopicRequiresSmartPipeline),this.next(),this.primaryTopicReferenceIsAllowedInCurrentTopicContext()||this.raise(t.start,xb.PrimaryTopicNotAllowed),this.registerTopicReference(),this.finishNode(t,"PipelinePrimaryTopicReference");default:throw this.unexpected()}},r.parseBooleanLiteral=function(){var e=this.startNode();return e.value=this.match(lb._true),this.next(),this.finishNode(e,"BooleanLiteral")},r.parseMaybePrivateName=function(e){if(this.match(lb.hash)){this.expectOnePlugin(["classPrivateProperties","classPrivateMethods"]),e||this.raise(this.state.pos,xb.UnexpectedPrivateField);var t=this.startNode();return this.next(),this.assertNoSpace("Unexpected space between # and identifier"),t.id=this.parseIdentifier(!0),this.finishNode(t,"PrivateName")}return this.parseIdentifier(!0)},r.parseFunctionExpression=function(){var e=this.startNode(),t=this.startNode();return this.next(),t=this.createIdentifier(t,"function"),this.prodParam.hasYield&&this.eat(lb.dot)?this.parseMetaProperty(e,t,"sent"):this.parseFunction(e)},r.parseMetaProperty=function(e,t,r){e.meta=t,"function"===t.name&&"sent"===r&&(this.isContextual(r)?this.expectPlugin("functionSent"):this.hasPlugin("functionSent")||this.unexpected());var n=this.state.containsEsc;return e.property=this.parseIdentifier(!0),(e.property.name!==r||n)&&this.raise(e.property.start,xb.UnsupportedMetaProperty,t.name,r),this.finishNode(e,"MetaProperty")},r.parseImportMetaProperty=function(e){var t=this.createIdentifier(this.startNodeAtNode(e),"import");return this.expect(lb.dot),this.isContextual("meta")?(this.expectPlugin("importMeta"),this.inModule||this.raiseWithData(t.start,{code:"BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED"},xb.ImportMetaOutsideModule),this.sawUnambiguousESM=!0):this.hasPlugin("importMeta")||this.raise(t.start,xb.ImportCallArityLtOne),this.parseMetaProperty(e,t,"meta")},r.parseLiteral=function(e,t,r,n){r=r||this.state.start,n=n||this.state.startLoc;var a=this.startNodeAt(r,n);return this.addExtra(a,"rawValue",e),this.addExtra(a,"raw",this.input.slice(r,this.state.end)),a.value=e,this.next(),this.finishNode(a,t)},r.parseParenAndDistinguishExpression=function(e){var t,r=this.state.start,n=this.state.startLoc;this.expect(lb.parenL);var a=this.state.maybeInArrowParameters,s=this.state.yieldPos,i=this.state.awaitPos,o=this.state.inFSharpPipelineDirectBody;this.state.maybeInArrowParameters=!0,this.state.yieldPos=-1,this.state.awaitPos=-1,this.state.inFSharpPipelineDirectBody=!1;for(var u,c,l=this.state.start,p=this.state.startLoc,d=[],f=new Ax,h={start:0},m=!0;!this.match(lb.parenR);){if(m)m=!1;else if(this.expect(lb.comma,h.start||null),this.match(lb.parenR)){c=this.state.start;break}if(this.match(lb.ellipsis)){var y=this.state.start,g=this.state.startLoc;u=this.state.start,d.push(this.parseParenItem(this.parseRestBinding(),y,g)),this.checkCommaAfterRest(41);break}d.push(this.parseMaybeAssign(!1,f,this.parseParenItem,h))}var v=this.state.start,b=this.state.startLoc;this.expect(lb.parenR),this.state.maybeInArrowParameters=a,this.state.inFSharpPipelineDirectBody=o;var x=this.startNodeAt(r,n);if(e&&this.shouldParseArrow()&&(x=this.parseArrow(x))){this.checkYieldAwaitInDefaultParams(),this.state.yieldPos=s,this.state.awaitPos=i;for(var E=0;E<d.length;E++){var A=d[E];A.extra&&A.extra.parenthesized&&this.unexpected(A.extra.parenStart)}return this.parseArrowExpression(x,d,!1),x}if(-1!==s&&(this.state.yieldPos=s),-1!==i&&(this.state.awaitPos=i),d.length||this.unexpected(this.state.lastTokStart),c&&this.unexpected(c),u&&this.unexpected(u),this.checkExpressionErrors(f,!0),h.start&&this.unexpected(h.start),this.toReferencedListDeep(d,!0),d.length>1?((t=this.startNodeAt(l,p)).expressions=d,this.finishNodeAt(t,"SequenceExpression",v,b)):t=d[0],!this.options.createParenthesizedExpressions)return this.addExtra(t,"parenthesized",!0),this.addExtra(t,"parenStart",r),t;var w=this.startNodeAt(r,n);return w.expression=t,this.finishNode(w,"ParenthesizedExpression"),w},r.shouldParseArrow=function(){return!this.canInsertSemicolon()},r.parseArrow=function(e){if(this.eat(lb.arrow))return e},r.parseParenItem=function(e,t,r){return e},r.parseNew=function(){var e=this.startNode(),t=this.startNode();if(this.next(),t=this.createIdentifier(t,"new"),this.eat(lb.dot)){var r=this.parseMetaProperty(e,t,"target");if(!this.scope.inNonArrowFunction&&!this.scope.inClass){var n=xb.UnexpectedNewTarget;this.hasPlugin("classProperties")&&(n+=" or class properties"),this.raise(r.start,n)}return r}return e.callee=this.parseNoCallExpr(),"Import"===e.callee.type?this.raise(e.callee.start,xb.ImportCallNotNewExpression):"OptionalMemberExpression"===e.callee.type||"OptionalCallExpression"===e.callee.type?this.raise(this.state.lastTokEnd,xb.OptionalChainingNoNew):this.eat(lb.questionDot)&&this.raise(this.state.start,xb.OptionalChainingNoNew),this.parseNewArguments(e),this.finishNode(e,"NewExpression")},r.parseNewArguments=function(e){if(this.eat(lb.parenL)){var t=this.parseExprList(lb.parenR);this.toReferencedList(t),e.arguments=t}else e.arguments=[]},r.parseTemplateElement=function(e){var t=this.startNode();return null===this.state.value&&(e||this.raise(this.state.start+1,xb.InvalidEscapeSequenceTemplate)),t.value={raw:this.input.slice(this.state.start,this.state.end).replace(/\r\n?/g,"\n"),cooked:this.state.value},this.next(),t.tail=this.match(lb.backQuote),this.finishNode(t,"TemplateElement")},r.parseTemplate=function(e){var t=this.startNode();this.next(),t.expressions=[];var r=this.parseTemplateElement(e);for(t.quasis=[r];!r.tail;)this.expect(lb.dollarBraceL),t.expressions.push(this.parseExpression()),this.expect(lb.braceR),t.quasis.push(r=this.parseTemplateElement(e));return this.next(),this.finishNode(t,"TemplateLiteral")},r.parseObj=function(e,t){var r=Object.create(null),n=!0,a=this.startNode();for(a.properties=[],this.next();!this.eat(lb.braceR);){if(n)n=!1;else if(this.expect(lb.comma),this.match(lb.braceR)){this.addExtra(a,"trailingComma",this.state.lastTokStart),this.next();break}var s=this.parseObjectMember(e,t);e||this.checkDuplicatedProto(s,r,t),s.shorthand&&this.addExtra(s,"shorthand",!0),a.properties.push(s)}return this.finishNode(a,e?"ObjectPattern":"ObjectExpression")},r.isAsyncProp=function(e){return!e.computed&&"Identifier"===e.key.type&&"async"===e.key.name&&(this.match(lb.name)||this.match(lb.num)||this.match(lb.string)||this.match(lb.bracketL)||this.state.type.keyword||this.match(lb.star))&&!this.hasPrecedingLineBreak()},r.parseObjectMember=function(e,t){var r=[];if(this.match(lb.at))for(this.hasPlugin("decorators")&&this.raise(this.state.start,xb.UnsupportedPropertyDecorator);this.match(lb.at);)r.push(this.parseDecorator());var n,a,s=this.startNode(),i=!1,o=!1;if(this.match(lb.ellipsis))return r.length&&this.unexpected(),e?(this.next(),s.argument=this.parseIdentifier(),this.checkCommaAfterRest(125),this.finishNode(s,"RestElement")):this.parseSpread();r.length&&(s.decorators=r,r=[]),s.method=!1,(e||t)&&(n=this.state.start,a=this.state.startLoc),e||(i=this.eat(lb.star));var u=this.state.containsEsc;return this.parsePropertyName(s,!1),e||u||i||!this.isAsyncProp(s)?o=!1:(o=!0,i=this.eat(lb.star),this.parsePropertyName(s,!1)),this.parseObjPropValue(s,n,a,i,o,e,t,u),s},r.isGetterOrSetterMethod=function(e,t){return!t&&!e.computed&&"Identifier"===e.key.type&&("get"===e.key.name||"set"===e.key.name)&&(this.match(lb.string)||this.match(lb.num)||this.match(lb.bracketL)||this.match(lb.name)||!!this.state.type.keyword)},r.getGetterSetterExpectedParamCount=function(e){return"get"===e.kind?0:1},r.checkGetterSetterParams=function(e){var t=this.getGetterSetterExpectedParamCount(e),r=e.start;e.params.length!==t&&("get"===e.kind?this.raise(r,xb.BadGetterArity):this.raise(r,xb.BadSetterArity)),"set"===e.kind&&"RestElement"===e.params[e.params.length-1].type&&this.raise(r,xb.BadSetterRestParameter)},r.parseObjectMethod=function(e,t,r,n,a){return r||t||this.match(lb.parenL)?(n&&this.unexpected(),e.kind="method",e.method=!0,this.parseMethod(e,t,r,!1,!1,"ObjectMethod")):!a&&this.isGetterOrSetterMethod(e,n)?((t||r)&&this.unexpected(),e.kind=e.key.name,this.parsePropertyName(e,!1),this.parseMethod(e,!1,!1,!1,!1,"ObjectMethod"),this.checkGetterSetterParams(e),e):void 0},r.parseObjectProperty=function(e,t,r,n,a){return e.shorthand=!1,this.eat(lb.colon)?(e.value=n?this.parseMaybeDefault(this.state.start,this.state.startLoc):this.parseMaybeAssign(!1,a),this.finishNode(e,"ObjectProperty")):e.computed||"Identifier"!==e.key.type?void 0:(this.checkReservedWord(e.key.name,e.key.start,!0,!0),n?e.value=this.parseMaybeDefault(t,r,e.key.__clone()):this.match(lb.eq)&&a?(-1===a.shorthandAssign&&(a.shorthandAssign=this.state.start),e.value=this.parseMaybeDefault(t,r,e.key.__clone())):e.value=e.key.__clone(),e.shorthand=!0,this.finishNode(e,"ObjectProperty"))},r.parseObjPropValue=function(e,t,r,n,a,s,i,o){var u=this.parseObjectMethod(e,n,a,s,o)||this.parseObjectProperty(e,t,r,s,i);return u||this.unexpected(),u},r.parsePropertyName=function(e,t){if(this.eat(lb.bracketL))e.computed=!0,e.key=this.parseMaybeAssign(),this.expect(lb.bracketR);else{var r=this.state.inPropertyName;this.state.inPropertyName=!0,e.key=this.match(lb.num)||this.match(lb.string)||this.match(lb.bigint)?this.parseExprAtom():this.parseMaybePrivateName(t),"PrivateName"!==e.key.type&&(e.computed=!1),this.state.inPropertyName=r}return e.key},r.initFunction=function(e,t){e.id=null,e.generator=!1,e.async=!!t},r.parseMethod=function(e,t,r,n,a,s,i){void 0===i&&(i=!1);var o=this.state.yieldPos,u=this.state.awaitPos;this.state.yieldPos=-1,this.state.awaitPos=-1,this.initFunction(e,r),e.generator=!!t;var c=n;return this.scope.enter(18|(i?64:0)|(a?32:0)),this.prodParam.enter(sx(r,e.generator)),this.parseFunctionParams(e,c),this.parseFunctionBodyAndFinish(e,s,!0),this.prodParam.exit(),this.scope.exit(),this.state.yieldPos=o,this.state.awaitPos=u,e},r.parseArrowExpression=function(e,t,r,n){this.scope.enter(6),this.prodParam.enter(sx(r,!1)),this.initFunction(e,r);var a=this.state.maybeInArrowParameters,s=this.state.yieldPos,i=this.state.awaitPos;return t&&(this.state.maybeInArrowParameters=!0,this.setArrowFunctionParameters(e,t,n)),this.state.maybeInArrowParameters=!1,this.state.yieldPos=-1,this.state.awaitPos=-1,this.parseFunctionBody(e,!0),this.prodParam.exit(),this.scope.exit(),this.state.maybeInArrowParameters=a,this.state.yieldPos=s,this.state.awaitPos=i,this.finishNode(e,"ArrowFunctionExpression")},r.setArrowFunctionParameters=function(e,t,r){e.params=this.toAssignableList(t,r)},r.parseFunctionBodyAndFinish=function(e,t,r){void 0===r&&(r=!1),this.parseFunctionBody(e,!1,r),this.finishNode(e,t)},r.parseFunctionBody=function(e,t,r){var n=this;void 0===r&&(r=!1);var a=t&&!this.match(lb.braceL),s=this.state.inParameters;if(this.state.inParameters=!1,a)e.body=this.parseMaybeAssign(),this.checkParams(e,!1,t,!1);else{var i=this.state.strict,o=this.state.labels;this.state.labels=[],this.prodParam.enter(4|this.prodParam.currentFlags()),e.body=this.parseBlock(!0,!1,(function(a){var s=!n.isSimpleParamList(e.params);if(a&&s){var o="method"!==e.kind&&"constructor"!==e.kind||!e.key?e.start:e.key.end;n.raise(o,xb.IllegalLanguageModeDirective)}var u=!i&&n.state.strict;n.checkParams(e,!(n.state.strict||t||r||s),t,u),n.state.strict&&e.id&&n.checkLVal(e.id,65,void 0,"function name",void 0,u)})),this.prodParam.exit(),this.state.labels=o}this.state.inParameters=s},r.isSimpleParamList=function(e){for(var t=0,r=e.length;t<r;t++)if("Identifier"!==e[t].type)return!1;return!0},r.checkParams=function(e,t,r,n){void 0===n&&(n=!0);for(var a=Object.create(null),s=0;s<e.params.length;s++)this.checkLVal(e.params[s],5,t?null:a,"function parameter list",void 0,n)},r.parseExprList=function(e,t,r,n){for(var a=[],s=!0;!this.eat(e);){if(s)s=!1;else if(this.expect(lb.comma),this.match(e)){n&&this.addExtra(n,"trailingComma",this.state.lastTokStart),this.next();break}a.push(this.parseExprListItem(t,r))}return a},r.parseExprListItem=function(e,t,r,n){var a;if(e&&this.match(lb.comma))a=null;else if(this.match(lb.ellipsis)){var s=this.state.start,i=this.state.startLoc;a=this.parseParenItem(this.parseSpread(t,r),s,i)}else if(this.match(lb.question)){this.expectPlugin("partialApplication"),n||this.raise(this.state.start,xb.UnexpectedArgumentPlaceholder);var o=this.startNode();this.next(),a=this.finishNode(o,"ArgumentPlaceholder")}else a=this.parseMaybeAssign(!1,t,this.parseParenItem,r);return a},r.parseIdentifier=function(e){var t=this.startNode(),r=this.parseIdentifierName(t.start,e);return this.createIdentifier(t,r)},r.createIdentifier=function(e,t){return e.name=t,e.loc.identifierName=t,this.finishNode(e,"Identifier")},r.parseIdentifierName=function(e,t){var r;if(this.match(lb.name))r=this.state.value;else{if(!this.state.type.keyword)throw this.unexpected();"class"!==(r=this.state.type.keyword)&&"function"!==r||this.state.lastTokEnd===this.state.lastTokStart+1&&46===this.input.charCodeAt(this.state.lastTokStart)||this.state.context.pop()}return t?this.state.type=lb.name:this.checkReservedWord(r,this.state.start,!!this.state.type.keyword,!1),this.next(),r},r.checkReservedWord=function(e,t,r,n){if(this.prodParam.hasYield&&"yield"===e)this.raise(t,xb.YieldBindingIdentifier);else{if("await"===e){if(this.prodParam.hasAwait)return void this.raise(t,xb.AwaitBindingIdentifier);-1===this.state.awaitPos&&(this.state.maybeInArrowParameters||this.isAwaitAllowed())&&(this.state.awaitPos=this.state.start)}if(!this.scope.inClass||this.scope.inNonArrowFunction||"arguments"!==e)if(r&&function(e){return ob.has(e)}(e))this.raise(t,xb.UnexpectedKeyword,e);else(this.state.strict?n?Fb:Pb:jb)(e,this.inModule)&&(this.prodParam.hasAwait||"await"!==e?this.raise(t,xb.UnexpectedReservedWord,e):this.raise(t,xb.AwaitNotInAsyncFunction));else this.raise(t,xb.ArgumentsDisallowedInInitializer)}},r.isAwaitAllowed=function(){return this.scope.inFunction?this.prodParam.hasAwait:!!this.options.allowAwaitOutsideFunction||!!this.hasPlugin("topLevelAwait")&&(this.inModule&&this.prodParam.hasAwait)},r.parseAwait=function(){var e=this.startNode();return this.next(),this.state.inParameters?this.raise(e.start,xb.AwaitExpressionFormalParameter):-1===this.state.awaitPos&&(this.state.awaitPos=e.start),this.eat(lb.star)&&this.raise(e.start,xb.ObsoleteAwaitStar),this.scope.inFunction||this.options.allowAwaitOutsideFunction||(this.hasPrecedingLineBreak()||this.match(lb.plusMin)||this.match(lb.parenL)||this.match(lb.bracketL)||this.match(lb.backQuote)||this.match(lb.regexp)||this.match(lb.slash)||this.hasPlugin("v8intrinsic")&&this.match(lb.modulo)?this.ambiguousScriptDifferentAst=!0:this.sawUnambiguousESM=!0),this.state.soloAwait||(e.argument=this.parseMaybeUnary()),this.finishNode(e,"AwaitExpression")},r.parseYield=function(e){var t=this.startNode();return this.state.inParameters?this.raise(t.start,xb.YieldInParameter):-1===this.state.yieldPos&&(this.state.yieldPos=t.start),this.next(),this.match(lb.semi)||!this.match(lb.star)&&!this.state.type.startsExpr||this.hasPrecedingLineBreak()?(t.delegate=!1,t.argument=null):(t.delegate=this.eat(lb.star),t.argument=this.parseMaybeAssign(e)),this.finishNode(t,"YieldExpression")},r.checkPipelineAtInfixOperator=function(e,t){"smart"===this.getPluginOption("pipelineOperator","proposal")&&"SequenceExpression"===e.type&&this.raise(t,xb.PipelineHeadSequenceExpression)},r.parseSmartPipelineBody=function(e,t,r){var n=this.checkSmartPipelineBodyStyle(e);return this.checkSmartPipelineBodyEarlyErrors(e,n,t),this.parseSmartPipelineBodyInStyle(e,n,t,r)},r.checkSmartPipelineBodyEarlyErrors=function(e,t,r){if(this.match(lb.arrow))throw this.raise(this.state.start,xb.PipelineBodyNoArrow);"PipelineTopicExpression"===t&&"SequenceExpression"===e.type&&this.raise(r,xb.PipelineBodySequenceExpression)},r.parseSmartPipelineBodyInStyle=function(e,t,r,n){var a=this.startNodeAt(r,n);switch(t){case"PipelineBareFunction":a.callee=e;break;case"PipelineBareConstructor":a.callee=e.callee;break;case"PipelineBareAwaitedFunction":a.callee=e.argument;break;case"PipelineTopicExpression":this.topicReferenceWasUsedInCurrentTopicContext()||this.raise(r,xb.PipelineTopicUnused),a.expression=e;break;default:throw new Error("Internal @babel/parser error: Unknown pipeline style ("+t+")")}return this.finishNode(a,t)},r.checkSmartPipelineBodyStyle=function(e){return e.type,this.isSimpleReference(e)?"PipelineBareFunction":"PipelineTopicExpression"},r.isSimpleReference=function(e){switch(e.type){case"MemberExpression":return!e.computed&&this.isSimpleReference(e.object);case"Identifier":return!0;default:return!1}},r.withTopicPermittingContext=function(e){var t=this.state.topicContext;this.state.topicContext={maxNumOfResolvableTopics:1,maxTopicIndex:null};try{return e()}finally{this.state.topicContext=t}},r.withTopicForbiddingContext=function(e){var t=this.state.topicContext;this.state.topicContext={maxNumOfResolvableTopics:0,maxTopicIndex:null};try{return e()}finally{this.state.topicContext=t}},r.withSoloAwaitPermittingContext=function(e){var t=this.state.soloAwait;this.state.soloAwait=!0;try{return e()}finally{this.state.soloAwait=t}},r.registerTopicReference=function(){this.state.topicContext.maxTopicIndex=0},r.primaryTopicReferenceIsAllowedInCurrentTopicContext=function(){return this.state.topicContext.maxNumOfResolvableTopics>=1},r.topicReferenceWasUsedInCurrentTopicContext=function(){return null!=this.state.topicContext.maxTopicIndex&&this.state.topicContext.maxTopicIndex>=0},r.parseFSharpPipelineBody=function(e,t){var r=this.state.start,n=this.state.startLoc;this.state.potentialArrowAt=this.state.start;var a=this.state.inFSharpPipelineDirectBody;this.state.inFSharpPipelineDirectBody=!0;var s=this.parseExprOp(this.parseMaybeUnary(),r,n,e,t);return this.state.inFSharpPipelineDirectBody=a,s},t}(function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.toAssignable=function(e){var t,r,n=void 0;switch(("ParenthesizedExpression"===e.type||(null==(t=e.extra)?void 0:t.parenthesized))&&"Identifier"!==(n=function e(t){return"ParenthesizedExpression"===t.type?e(t.expression):t}(e)).type&&"MemberExpression"!==n.type&&this.raise(e.start,xb.InvalidParenthesizedAssignment),e.type){case"Identifier":case"ObjectPattern":case"ArrayPattern":case"AssignmentPattern":break;case"ObjectExpression":e.type="ObjectPattern";for(var a=0,s=e.properties.length,i=s-1;a<s;a++){var o,u=e.properties[a],c=a===i;this.toAssignableObjectExpressionProp(u,c),c&&"RestElement"===u.type&&(null==(o=e.extra)?void 0:o.trailingComma)&&this.raiseRestNotLast(e.extra.trailingComma)}break;case"ObjectProperty":this.toAssignable(e.value);break;case"SpreadElement":this.checkToRestConversion(e),e.type="RestElement";var l=e.argument;this.toAssignable(l);break;case"ArrayExpression":e.type="ArrayPattern",this.toAssignableList(e.elements,null==(r=e.extra)?void 0:r.trailingComma);break;case"AssignmentExpression":"="!==e.operator&&this.raise(e.left.end,xb.MissingEqInAssignment),e.type="AssignmentPattern",delete e.operator,this.toAssignable(e.left);break;case"ParenthesizedExpression":this.toAssignable(n)}return e},r.toAssignableObjectExpressionProp=function(e,t){if("ObjectMethod"===e.type){var r="get"===e.kind||"set"===e.kind?xb.PatternHasAccessor:xb.PatternHasMethod;this.raise(e.key.start,r)}else"SpreadElement"!==e.type||t?this.toAssignable(e):this.raiseRestNotLast(e.start)},r.toAssignableList=function(e,t){var r=e.length;if(r){var n=e[r-1];if(n&&"RestElement"===n.type)--r;else if(n&&"SpreadElement"===n.type){n.type="RestElement";var a=n.argument;this.toAssignable(a),"Identifier"!==a.type&&"MemberExpression"!==a.type&&"ArrayPattern"!==a.type&&"ObjectPattern"!==a.type&&this.unexpected(a.start),t&&this.raiseTrailingCommaAfterRest(t),--r}}for(var s=0;s<r;s++){var i=e[s];i&&(this.toAssignable(i),"RestElement"===i.type&&this.raiseRestNotLast(i.start))}return e},r.toReferencedList=function(e,t){return e},r.toReferencedListDeep=function(e,t){this.toReferencedList(e,t);for(var r=0;r<e.length;r++){var n=e[r];n&&"ArrayExpression"===n.type&&this.toReferencedListDeep(n.elements)}},r.parseSpread=function(e,t){var r=this.startNode();return this.next(),r.argument=this.parseMaybeAssign(!1,e,void 0,t),this.finishNode(r,"SpreadElement")},r.parseRestBinding=function(){var e=this.startNode();return this.next(),e.argument=this.parseBindingAtom(),this.finishNode(e,"RestElement")},r.parseBindingAtom=function(){switch(this.state.type){case lb.bracketL:var e=this.startNode();return this.next(),e.elements=this.parseBindingList(lb.bracketR,93,!0),this.finishNode(e,"ArrayPattern");case lb.braceL:return this.parseObj(!0)}return this.parseIdentifier()},r.parseBindingList=function(e,t,r,n){for(var a=[],s=!0;!this.eat(e);)if(s?s=!1:this.expect(lb.comma),r&&this.match(lb.comma))a.push(null);else{if(this.eat(e))break;if(this.match(lb.ellipsis)){a.push(this.parseAssignableListItemTypes(this.parseRestBinding())),this.checkCommaAfterRest(t),this.expect(e);break}var i=[];for(this.match(lb.at)&&this.hasPlugin("decorators")&&this.raise(this.state.start,xb.UnsupportedParameterDecorator);this.match(lb.at);)i.push(this.parseDecorator());a.push(this.parseAssignableListItem(n,i))}return a},r.parseAssignableListItem=function(e,t){var r=this.parseMaybeDefault();this.parseAssignableListItemTypes(r);var n=this.parseMaybeDefault(r.start,r.loc.start,r);return t.length&&(r.decorators=t),n},r.parseAssignableListItemTypes=function(e){return e},r.parseMaybeDefault=function(e,t,r){if(t=t||this.state.startLoc,e=e||this.state.start,r=r||this.parseBindingAtom(),!this.eat(lb.eq))return r;var n=this.startNodeAt(e,t);return n.left=r,n.right=this.parseMaybeAssign(),this.finishNode(n,"AssignmentPattern")},r.checkLVal=function(e,t,r,n,a,s){switch(void 0===t&&(t=64),void 0===s&&(s=!1),e.type){case"Identifier":if(this.state.strict&&(s?Fb(e.name,this.inModule):kb(e.name))&&this.raise(e.start,64===t?xb.StrictEvalArguments:xb.StrictEvalArgumentsBinding,e.name),r){var i="_"+e.name;r[i]?this.raise(e.start,xb.ParamDupe):r[i]=!0}a&&"let"===e.name&&this.raise(e.start,xb.LetInLexicalBinding),64&t||this.scope.declareName(e.name,t,e.start);break;case"MemberExpression":64!==t&&this.raise(e.start,xb.InvalidPropertyBindingPattern);break;case"ObjectPattern":for(var o=0,u=e.properties;o<u.length;o++){var c=u[o];if("ObjectProperty"===c.type)c=c.value;else if("ObjectMethod"===c.type)continue;this.checkLVal(c,t,r,"object destructuring pattern",a)}break;case"ArrayPattern":for(var l=0,p=e.elements;l<p.length;l++){var d=p[l];d&&this.checkLVal(d,t,r,"array destructuring pattern",a)}break;case"AssignmentPattern":this.checkLVal(e.left,t,r,"assignment pattern");break;case"RestElement":this.checkLVal(e.argument,t,r,"rest element");break;case"ParenthesizedExpression":this.checkLVal(e.expression,t,r,"parenthesized expression");break;default:this.raise(e.start,64===t?xb.InvalidLhs:xb.InvalidLhsBinding,n)}},r.checkToRestConversion=function(e){"Identifier"!==e.argument.type&&"MemberExpression"!==e.argument.type&&this.raise(e.argument.start,xb.InvalidRestAssignmentPattern)},r.checkCommaAfterRest=function(e){this.match(lb.comma)&&(this.lookaheadCharCode()===e?this.raiseTrailingCommaAfterRest(this.state.start):this.raiseRestNotLast(this.state.start))},r.raiseRestNotLast=function(e){throw this.raise(e,xb.ElementAfterRest)},r.raiseTrailingCommaAfterRest=function(e){this.raise(e,xb.RestTrailingComma)},t}(Sx)),Cx={kind:"loop"},Tx={kind:"switch"},jx=function(e){function t(){return e.apply(this,arguments)||this}a(t,e);var r=t.prototype;return r.parseTopLevel=function(e,t){if(t.sourceType=this.options.sourceType,t.interpreter=this.parseInterpreterDirective(),this.parseBlockBody(t,!0,!0,lb.eof),this.inModule&&!this.options.allowUndeclaredExports&&this.scope.undefinedExports.size>0)for(var r=0,n=Array.from(this.scope.undefinedExports);r<n.length;r++){var a=n[r][0],s=this.scope.undefinedExports.get(a);this.raise(s,xb.ModuleExportUndefined,a)}return e.program=this.finishNode(t,"Program"),e.comments=this.state.comments,this.options.tokens&&(e.tokens=this.tokens),this.finishNode(e,"File")},r.stmtToDirective=function(e){var t=e.expression,r=this.startNodeAt(t.start,t.loc.start),n=this.startNodeAt(e.start,e.loc.start),a=this.input.slice(t.start,t.end),s=r.value=a.slice(1,-1);return this.addExtra(r,"raw",a),this.addExtra(r,"rawValue",s),n.value=this.finishNodeAt(r,"DirectiveLiteral",t.end,t.loc.end),this.finishNodeAt(n,"Directive",e.end,e.loc.end)},r.parseInterpreterDirective=function(){if(!this.match(lb.interpreterDirective))return null;var e=this.startNode();return e.value=this.state.value,this.next(),this.finishNode(e,"InterpreterDirective")},r.isLet=function(e){if(!this.isContextual("let"))return!1;var t=this.nextTokenStart(),r=this.input.charCodeAt(t);if(91===r)return!0;if(e)return!1;if(123===r)return!0;if(Ub(r)){for(var n=t+1;Gb(this.input.charCodeAt(n));)++n;var a=this.input.slice(t,n);if(!_b.test(a))return!0}return!1},r.parseStatement=function(e,t){return this.match(lb.at)&&this.parseDecorators(!0),this.parseStatementContent(e,t)},r.parseStatementContent=function(e,t){var r,n=this.state.type,a=this.startNode();switch(this.isLet(e)&&(n=lb._var,r="let"),n){case lb._break:case lb._continue:return this.parseBreakContinueStatement(a,n.keyword);case lb._debugger:return this.parseDebuggerStatement(a);case lb._do:return this.parseDoStatement(a);case lb._for:return this.parseForStatement(a);case lb._function:if(46===this.lookaheadCharCode())break;return e&&(this.state.strict?this.raise(this.state.start,xb.StrictFunction):"if"!==e&&"label"!==e&&this.raise(this.state.start,xb.SloppyFunction)),this.parseFunctionStatement(a,!1,!e);case lb._class:return e&&this.unexpected(),this.parseClass(a,!0);case lb._if:return this.parseIfStatement(a);case lb._return:return this.parseReturnStatement(a);case lb._switch:return this.parseSwitchStatement(a);case lb._throw:return this.parseThrowStatement(a);case lb._try:return this.parseTryStatement(a);case lb._const:case lb._var:return r=r||this.state.value,e&&"var"!==r&&this.raise(this.state.start,xb.UnexpectedLexicalDeclaration),this.parseVarStatement(a,r);case lb._while:return this.parseWhileStatement(a);case lb._with:return this.parseWithStatement(a);case lb.braceL:return this.parseBlock();case lb.semi:return this.parseEmptyStatement(a);case lb._export:case lb._import:var s,i=this.lookaheadCharCode();if(40===i||46===i)break;return this.options.allowImportExportEverywhere||t||this.raise(this.state.start,xb.UnexpectedImportExport),this.next(),n===lb._import?"ImportDeclaration"!==(s=this.parseImport(a)).type||s.importKind&&"value"!==s.importKind||(this.sawUnambiguousESM=!0):("ExportNamedDeclaration"!==(s=this.parseExport(a)).type||s.exportKind&&"value"!==s.exportKind)&&("ExportAllDeclaration"!==s.type||s.exportKind&&"value"!==s.exportKind)&&"ExportDefaultDeclaration"!==s.type||(this.sawUnambiguousESM=!0),this.assertModuleNodeAllowed(a),s;default:if(this.isAsyncFunction())return e&&this.raise(this.state.start,xb.AsyncFunctionInSingleStatementContext),this.next(),this.parseFunctionStatement(a,!0,!e)}var o=this.state.value,u=this.parseExpression();return n===lb.name&&"Identifier"===u.type&&this.eat(lb.colon)?this.parseLabeledStatement(a,o,u,e):this.parseExpressionStatement(a,u)},r.assertModuleNodeAllowed=function(e){this.options.allowImportExportEverywhere||this.inModule||this.raiseWithData(e.start,{code:"BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED"},xb.ImportOutsideModule)},r.takeDecorators=function(e){var t=this.state.decoratorStack[this.state.decoratorStack.length-1];t.length&&(e.decorators=t,this.resetStartLocationFromNode(e,t[0]),this.state.decoratorStack[this.state.decoratorStack.length-1]=[])},r.canHaveLeadingDecorator=function(){return this.match(lb._class)},r.parseDecorators=function(e){for(var t=this.state.decoratorStack[this.state.decoratorStack.length-1];this.match(lb.at);){var r=this.parseDecorator();t.push(r)}if(this.match(lb._export))e||this.unexpected(),this.hasPlugin("decorators")&&!this.getPluginOption("decorators","decoratorsBeforeExport")&&this.raise(this.state.start,xb.DecoratorExportClass);else if(!this.canHaveLeadingDecorator())throw this.raise(this.state.start,xb.UnexpectedLeadingDecorator)},r.parseDecorator=function(){this.expectOnePlugin(["decorators-legacy","decorators"]);var e=this.startNode();if(this.next(),this.hasPlugin("decorators")){this.state.decoratorStack.push([]);var t,r=this.state.start,n=this.state.startLoc;if(this.eat(lb.parenL))t=this.parseExpression(),this.expect(lb.parenR);else for(t=this.parseIdentifier(!1);this.eat(lb.dot);){var a=this.startNodeAt(r,n);a.object=t,a.property=this.parseIdentifier(!0),a.computed=!1,t=this.finishNode(a,"MemberExpression")}e.expression=this.parseMaybeDecoratorArguments(t),this.state.decoratorStack.pop()}else e.expression=this.parseExprSubscripts();return this.finishNode(e,"Decorator")},r.parseMaybeDecoratorArguments=function(e){if(this.eat(lb.parenL)){var t=this.startNodeAtNode(e);return t.callee=e,t.arguments=this.parseCallExpressionArguments(lb.parenR,!1),this.toReferencedList(t.arguments),this.finishNode(t,"CallExpression")}return e},r.parseBreakContinueStatement=function(e,t){var r="break"===t;return this.next(),this.isLineTerminator()?e.label=null:(e.label=this.parseIdentifier(),this.semicolon()),this.verifyBreakContinue(e,t),this.finishNode(e,r?"BreakStatement":"ContinueStatement")},r.verifyBreakContinue=function(e,t){var r,n="break"===t;for(r=0;r<this.state.labels.length;++r){var a=this.state.labels[r];if(null==e.label||a.name===e.label.name){if(null!=a.kind&&(n||"loop"===a.kind))break;if(e.label&&n)break}}r===this.state.labels.length&&this.raise(e.start,xb.IllegalBreakContinue,t)},r.parseDebuggerStatement=function(e){return this.next(),this.semicolon(),this.finishNode(e,"DebuggerStatement")},r.parseHeaderExpression=function(){this.expect(lb.parenL);var e=this.parseExpression();return this.expect(lb.parenR),e},r.parseDoStatement=function(e){var t=this;return this.next(),this.state.labels.push(Cx),e.body=this.withTopicForbiddingContext((function(){return t.parseStatement("do")})),this.state.labels.pop(),this.expect(lb._while),e.test=this.parseHeaderExpression(),this.eat(lb.semi),this.finishNode(e,"DoWhileStatement")},r.parseForStatement=function(e){this.next(),this.state.labels.push(Cx);var t=-1;if(this.isAwaitAllowed()&&this.eatContextual("await")&&(t=this.state.lastTokStart),this.scope.enter(0),this.expect(lb.parenL),this.match(lb.semi))return t>-1&&this.unexpected(t),this.parseFor(e,null);var r=this.isLet();if(this.match(lb._var)||this.match(lb._const)||r){var n=this.startNode(),a=r?"let":this.state.value;return this.next(),this.parseVar(n,!0,a),this.finishNode(n,"VariableDeclaration"),(this.match(lb._in)||this.isContextual("of"))&&1===n.declarations.length?this.parseForIn(e,n,t):(t>-1&&this.unexpected(t),this.parseFor(e,n))}var s=new Ax,i=this.parseExpression(!0,s);if(this.match(lb._in)||this.isContextual("of")){this.toAssignable(i);var o=this.isContextual("of")?"for-of statement":"for-in statement";return this.checkLVal(i,void 0,void 0,o),this.parseForIn(e,i,t)}return this.checkExpressionErrors(s,!0),t>-1&&this.unexpected(t),this.parseFor(e,i)},r.parseFunctionStatement=function(e,t,r){return this.next(),this.parseFunction(e,1|(r?0:2),t)},r.parseIfStatement=function(e){return this.next(),e.test=this.parseHeaderExpression(),e.consequent=this.parseStatement("if"),e.alternate=this.eat(lb._else)?this.parseStatement("if"):null,this.finishNode(e,"IfStatement")},r.parseReturnStatement=function(e){return this.prodParam.hasReturn||this.options.allowReturnOutsideFunction||this.raise(this.state.start,xb.IllegalReturn),this.next(),this.isLineTerminator()?e.argument=null:(e.argument=this.parseExpression(),this.semicolon()),this.finishNode(e,"ReturnStatement")},r.parseSwitchStatement=function(e){this.next(),e.discriminant=this.parseHeaderExpression();var t,r,n=e.cases=[];for(this.expect(lb.braceL),this.state.labels.push(Tx),this.scope.enter(0);!this.match(lb.braceR);)if(this.match(lb._case)||this.match(lb._default)){var a=this.match(lb._case);t&&this.finishNode(t,"SwitchCase"),n.push(t=this.startNode()),t.consequent=[],this.next(),a?t.test=this.parseExpression():(r&&this.raise(this.state.lastTokStart,xb.MultipleDefaultsInSwitch),r=!0,t.test=null),this.expect(lb.colon)}else t?t.consequent.push(this.parseStatement(null)):this.unexpected();return this.scope.exit(),t&&this.finishNode(t,"SwitchCase"),this.next(),this.state.labels.pop(),this.finishNode(e,"SwitchStatement")},r.parseThrowStatement=function(e){return this.next(),pb.test(this.input.slice(this.state.lastTokEnd,this.state.start))&&this.raise(this.state.lastTokEnd,xb.NewlineAfterThrow),e.argument=this.parseExpression(),this.semicolon(),this.finishNode(e,"ThrowStatement")},r.parseTryStatement=function(e){var t=this;if(this.next(),e.block=this.parseBlock(),e.handler=null,this.match(lb._catch)){var r=this.startNode();if(this.next(),this.match(lb.parenL)){this.expect(lb.parenL),r.param=this.parseBindingAtom();var n="Identifier"===r.param.type;this.scope.enter(n?8:0),this.checkLVal(r.param,9,null,"catch clause"),this.expect(lb.parenR)}else r.param=null,this.scope.enter(0);r.body=this.withTopicForbiddingContext((function(){return t.parseBlock(!1,!1)})),this.scope.exit(),e.handler=this.finishNode(r,"CatchClause")}return e.finalizer=this.eat(lb._finally)?this.parseBlock():null,e.handler||e.finalizer||this.raise(e.start,xb.NoCatchOrFinally),this.finishNode(e,"TryStatement")},r.parseVarStatement=function(e,t){return this.next(),this.parseVar(e,!1,t),this.semicolon(),this.finishNode(e,"VariableDeclaration")},r.parseWhileStatement=function(e){var t=this;return this.next(),e.test=this.parseHeaderExpression(),this.state.labels.push(Cx),e.body=this.withTopicForbiddingContext((function(){return t.parseStatement("while")})),this.state.labels.pop(),this.finishNode(e,"WhileStatement")},r.parseWithStatement=function(e){var t=this;return this.state.strict&&this.raise(this.state.start,xb.StrictWith),this.next(),e.object=this.parseHeaderExpression(),e.body=this.withTopicForbiddingContext((function(){return t.parseStatement("with")})),this.finishNode(e,"WithStatement")},r.parseEmptyStatement=function(e){return this.next(),this.finishNode(e,"EmptyStatement")},r.parseLabeledStatement=function(e,t,r,n){for(var a=0,s=this.state.labels;a<s.length;a++){s[a].name===t&&this.raise(r.start,xb.LabelRedeclaration,t)}for(var i=this.state.type.isLoop?"loop":this.match(lb._switch)?"switch":null,o=this.state.labels.length-1;o>=0;o--){var u=this.state.labels[o];if(u.statementStart!==e.start)break;u.statementStart=this.state.start,u.kind=i}return this.state.labels.push({name:t,kind:i,statementStart:this.state.start}),e.body=this.parseStatement(n?-1===n.indexOf("label")?n+"label":n:"label"),this.state.labels.pop(),e.label=r,this.finishNode(e,"LabeledStatement")},r.parseExpressionStatement=function(e,t){return e.expression=t,this.semicolon(),this.finishNode(e,"ExpressionStatement")},r.parseBlock=function(e,t,r){void 0===e&&(e=!1),void 0===t&&(t=!0);var n=this.startNode();return this.expect(lb.braceL),t&&this.scope.enter(0),this.parseBlockBody(n,e,!1,lb.braceR,r),t&&this.scope.exit(),this.finishNode(n,"BlockStatement")},r.isValidDirective=function(e){return"ExpressionStatement"===e.type&&"StringLiteral"===e.expression.type&&!e.expression.extra.parenthesized},r.parseBlockBody=function(e,t,r,n,a){var s=e.body=[],i=e.directives=[];this.parseBlockOrModuleBlockBody(s,t?i:void 0,r,n,a)},r.parseBlockOrModuleBlockBody=function(e,t,r,n,a){for(var s=[],i=!1,o=null;!this.eat(n);){!i&&this.state.octalPositions.length&&s.push.apply(s,this.state.octalPositions);var u=this.parseStatement(null,r);if(t&&!i&&this.isValidDirective(u)){var c=this.stmtToDirective(u);t.push(c),null===o&&"use strict"===c.value.value&&(o=this.state.strict,this.setStrict(!0))}else i=!0,e.push(u)}if(this.state.strict&&s.length)for(var l=0;l<s.length;l++){var p=s[l];this.raise(p,xb.StrictOctalLiteral)}a&&a.call(this,null!==o),!1===o&&this.setStrict(!1)},r.parseFor=function(e,t){var r=this;return e.init=t,this.expect(lb.semi),e.test=this.match(lb.semi)?null:this.parseExpression(),this.expect(lb.semi),e.update=this.match(lb.parenR)?null:this.parseExpression(),this.expect(lb.parenR),e.body=this.withTopicForbiddingContext((function(){return r.parseStatement("for")})),this.scope.exit(),this.state.labels.pop(),this.finishNode(e,"ForStatement")},r.parseForIn=function(e,t,r){var n=this,a=this.match(lb._in);return this.next(),a?r>-1&&this.unexpected(r):e.await=r>-1,"VariableDeclaration"!==t.type||null==t.declarations[0].init||a&&!this.state.strict&&"var"===t.kind&&"Identifier"===t.declarations[0].id.type?"AssignmentPattern"===t.type&&this.raise(t.start,xb.InvalidLhs,"for-loop"):this.raise(t.start,xb.ForInOfLoopInitializer,a?"for-in":"for-of"),e.left=t,e.right=a?this.parseExpression():this.parseMaybeAssign(),this.expect(lb.parenR),e.body=this.withTopicForbiddingContext((function(){return n.parseStatement("for")})),this.scope.exit(),this.state.labels.pop(),this.finishNode(e,a?"ForInStatement":"ForOfStatement")},r.parseVar=function(e,t,r){var n=e.declarations=[],a=this.hasPlugin("typescript");for(e.kind=r;;){var s=this.startNode();if(this.parseVarId(s,r),this.eat(lb.eq)?s.init=this.parseMaybeAssign(t):("const"!==r||this.match(lb._in)||this.isContextual("of")?"Identifier"===s.id.type||t&&(this.match(lb._in)||this.isContextual("of"))||this.raise(this.state.lastTokEnd,xb.DeclarationMissingInitializer,"Complex binding patterns"):a||this.unexpected(),s.init=null),n.push(this.finishNode(s,"VariableDeclarator")),!this.eat(lb.comma))break}return e},r.parseVarId=function(e,t){e.id=this.parseBindingAtom(),this.checkLVal(e.id,"var"===t?5:9,void 0,"variable declaration","var"!==t)},r.parseFunction=function(e,t,r){var n=this;void 0===t&&(t=0),void 0===r&&(r=!1);var a=1&t,s=2&t,i=!(!a||4&t);this.initFunction(e,r),this.match(lb.star)&&s&&this.raise(this.state.start,xb.GeneratorInSingleStatementContext),e.generator=this.eat(lb.star),a&&(e.id=this.parseFunctionId(i));var o=this.state.maybeInArrowParameters,u=this.state.yieldPos,c=this.state.awaitPos;return this.state.maybeInArrowParameters=!1,this.state.yieldPos=-1,this.state.awaitPos=-1,this.scope.enter(2),this.prodParam.enter(sx(r,e.generator)),a||(e.id=this.parseFunctionId()),this.parseFunctionParams(e),this.withTopicForbiddingContext((function(){n.parseFunctionBodyAndFinish(e,a?"FunctionDeclaration":"FunctionExpression")})),this.prodParam.exit(),this.scope.exit(),a&&!s&&this.registerFunctionStatementId(e),this.state.maybeInArrowParameters=o,this.state.yieldPos=u,this.state.awaitPos=c,e},r.parseFunctionId=function(e){return e||this.match(lb.name)?this.parseIdentifier():null},r.parseFunctionParams=function(e,t){var r=this.state.inParameters;this.state.inParameters=!0,this.expect(lb.parenL),e.params=this.parseBindingList(lb.parenR,41,!1,t),this.state.inParameters=r,this.checkYieldAwaitInDefaultParams()},r.registerFunctionStatementId=function(e){e.id&&this.scope.declareName(e.id.name,this.state.strict||e.generator||e.async?this.scope.treatFunctionsAsVar?5:9:17,e.id.start)},r.parseClass=function(e,t,r){this.next(),this.takeDecorators(e);var n=this.state.strict;return this.state.strict=!0,this.parseClassId(e,t,r),this.parseClassSuper(e),e.body=this.parseClassBody(!!e.superClass),this.state.strict=n,this.finishNode(e,t?"ClassDeclaration":"ClassExpression")},r.isClassProperty=function(){return this.match(lb.eq)||this.match(lb.semi)||this.match(lb.braceR)},r.isClassMethod=function(){return this.match(lb.parenL)},r.isNonstaticConstructor=function(e){return!(e.computed||e.static||"constructor"!==e.key.name&&"constructor"!==e.key.value)},r.parseClassBody=function(e){var t=this;this.classScope.enter();var r={hadConstructor:!1},n=[],a=this.startNode();if(a.body=[],this.expect(lb.braceL),this.withTopicForbiddingContext((function(){for(;!t.eat(lb.braceR);)if(t.eat(lb.semi)){if(n.length>0)throw t.raise(t.state.lastTokEnd,xb.DecoratorSemicolon)}else if(t.match(lb.at))n.push(t.parseDecorator());else{var s=t.startNode();n.length&&(s.decorators=n,t.resetStartLocationFromNode(s,n[0]),n=[]),t.parseClassMember(a,s,r,e),"constructor"===s.kind&&s.decorators&&s.decorators.length>0&&t.raise(s.start,xb.DecoratorConstructor)}})),n.length)throw this.raise(this.state.start,xb.TrailingDecorator);return this.classScope.exit(),this.finishNode(a,"ClassBody")},r.parseClassMember=function(e,t,r,n){var a=!1,s=this.state.containsEsc;if(this.match(lb.name)&&"static"===this.state.value){var i=this.parseIdentifier(!0);if(this.isClassMethod()){var o=t;return o.kind="method",o.computed=!1,o.key=i,o.static=!1,void this.pushClassMethod(e,o,!1,!1,!1,!1)}if(this.isClassProperty()){var u=t;return u.computed=!1,u.key=i,u.static=!1,void e.body.push(this.parseClassProperty(u))}if(s)throw this.unexpected();a=!0}this.parseClassMemberWithIsStatic(e,t,r,a,n)},r.parseClassMemberWithIsStatic=function(e,t,r,n,a){var s=t,i=t,o=t,u=t,c=s,l=s;if(t.static=n,this.eat(lb.star))return c.kind="method",this.parseClassPropertyName(c),"PrivateName"===c.key.type?void this.pushClassPrivateMethod(e,i,!0,!1):(this.isNonstaticConstructor(s)&&this.raise(s.key.start,xb.ConstructorIsGenerator),void this.pushClassMethod(e,s,!0,!1,!1,!1));var p=this.state.containsEsc,d=this.parseClassPropertyName(t),f="PrivateName"===d.type,h="Identifier"===d.type,m=this.state.start;if(this.parsePostMemberNameModifiers(l),this.isClassMethod()){if(c.kind="method",f)return void this.pushClassPrivateMethod(e,i,!1,!1);var y=this.isNonstaticConstructor(s),g=!1;y&&(s.kind="constructor",r.hadConstructor&&!this.hasPlugin("typescript")&&this.raise(d.start,xb.DuplicateConstructor),r.hadConstructor=!0,g=a),this.pushClassMethod(e,s,!1,!1,y,g)}else if(this.isClassProperty())f?this.pushClassPrivateProperty(e,u):this.pushClassProperty(e,o);else if(!h||"async"!==d.name||p||this.isLineTerminator())!h||"get"!==d.name&&"set"!==d.name||p||this.match(lb.star)&&this.isLineTerminator()?this.isLineTerminator()?f?this.pushClassPrivateProperty(e,u):this.pushClassProperty(e,o):this.unexpected():(c.kind=d.name,this.parseClassPropertyName(s),"PrivateName"===c.key.type?this.pushClassPrivateMethod(e,i,!1,!1):(this.isNonstaticConstructor(s)&&this.raise(s.key.start,xb.ConstructorIsAccessor),this.pushClassMethod(e,s,!1,!1,!1,!1)),this.checkGetterSetterParams(s));else{var v=this.eat(lb.star);l.optional&&this.unexpected(m),c.kind="method",this.parseClassPropertyName(c),this.parsePostMemberNameModifiers(l),"PrivateName"===c.key.type?this.pushClassPrivateMethod(e,i,v,!0):(this.isNonstaticConstructor(s)&&this.raise(s.key.start,xb.ConstructorIsAsync),this.pushClassMethod(e,s,v,!0,!1,!1))}},r.parseClassPropertyName=function(e){var t=this.parsePropertyName(e,!0);return e.computed||!e.static||"prototype"!==t.name&&"prototype"!==t.value||this.raise(t.start,xb.StaticPrototype),"PrivateName"===t.type&&"constructor"===t.id.name&&this.raise(t.start,xb.ConstructorClassPrivateField),t},r.pushClassProperty=function(e,t){t.computed||"constructor"!==t.key.name&&"constructor"!==t.key.value||this.raise(t.key.start,xb.ConstructorClassField),e.body.push(this.parseClassProperty(t))},r.pushClassPrivateProperty=function(e,t){this.expectPlugin("classPrivateProperties",t.key.start);var r=this.parseClassPrivateProperty(t);e.body.push(r),this.classScope.declarePrivateName(r.key.id.name,0,r.key.start)},r.pushClassMethod=function(e,t,r,n,a,s){e.body.push(this.parseMethod(t,r,n,a,s,"ClassMethod",!0))},r.pushClassPrivateMethod=function(e,t,r,n){this.expectPlugin("classPrivateMethods",t.key.start);var a=this.parseMethod(t,r,n,!1,!1,"ClassPrivateMethod",!0);e.body.push(a);var s="get"===a.kind?a.static?6:2:"set"===a.kind?a.static?5:1:0;this.classScope.declarePrivateName(a.key.id.name,s,a.key.start)},r.parsePostMemberNameModifiers=function(e){},r.parseAccessModifier=function(){},r.parseClassPrivateProperty=function(e){return this.scope.enter(80),this.prodParam.enter(0),e.value=this.eat(lb.eq)?this.parseMaybeAssign():null,this.semicolon(),this.prodParam.exit(),this.scope.exit(),this.finishNode(e,"ClassPrivateProperty")},r.parseClassProperty=function(e){return e.typeAnnotation||this.expectPlugin("classProperties"),this.scope.enter(80),this.prodParam.enter(0),this.match(lb.eq)?(this.expectPlugin("classProperties"),this.next(),e.value=this.parseMaybeAssign()):e.value=null,this.semicolon(),this.prodParam.exit(),this.scope.exit(),this.finishNode(e,"ClassProperty")},r.parseClassId=function(e,t,r,n){void 0===n&&(n=139),this.match(lb.name)?(e.id=this.parseIdentifier(),t&&this.checkLVal(e.id,n,void 0,"class name")):r||!t?e.id=null:this.unexpected(null,xb.MissingClassName)},r.parseClassSuper=function(e){e.superClass=this.eat(lb._extends)?this.parseExprSubscripts():null},r.parseExport=function(e){var t=this.maybeParseExportDefaultSpecifier(e),r=!t||this.eat(lb.comma),n=r&&this.eatExportStar(e),a=n&&this.maybeParseExportNamespaceSpecifier(e),s=r&&(!a||this.eat(lb.comma)),i=t||n;if(n&&!a)return t&&this.unexpected(),this.parseExportFrom(e,!0),this.finishNode(e,"ExportAllDeclaration");var o,u=this.maybeParseExportNamedSpecifiers(e);if(t&&r&&!n&&!u||a&&s&&!u)throw this.unexpected(null,lb.braceL);if(i||u?(o=!1,this.parseExportFrom(e,i)):o=this.maybeParseExportDeclaration(e),i||u||o)return this.checkExport(e,!0,!1,!!e.source),this.finishNode(e,"ExportNamedDeclaration");if(this.eat(lb._default))return e.declaration=this.parseExportDefaultExpression(),this.checkExport(e,!0,!0),this.finishNode(e,"ExportDefaultDeclaration");throw this.unexpected(null,lb.braceL)},r.eatExportStar=function(e){return this.eat(lb.star)},r.maybeParseExportDefaultSpecifier=function(e){if(this.isExportDefaultSpecifier()){this.expectPlugin("exportDefaultFrom");var t=this.startNode();return t.exported=this.parseIdentifier(!0),e.specifiers=[this.finishNode(t,"ExportDefaultSpecifier")],!0}return!1},r.maybeParseExportNamespaceSpecifier=function(e){if(this.isContextual("as")){e.specifiers||(e.specifiers=[]);var t=this.startNodeAt(this.state.lastTokStart,this.state.lastTokStartLoc);return this.next(),t.exported=this.parseIdentifier(!0),e.specifiers.push(this.finishNode(t,"ExportNamespaceSpecifier")),!0}return!1},r.maybeParseExportNamedSpecifiers=function(e){var t;return!!this.match(lb.braceL)&&(e.specifiers||(e.specifiers=[]),(t=e.specifiers).push.apply(t,this.parseExportSpecifiers()),e.source=null,e.declaration=null,!0)},r.maybeParseExportDeclaration=function(e){if(this.shouldParseExportDeclaration()){if(this.isContextual("async")){var t=this.nextTokenStart();this.isUnparsedContextual(t,"function")||this.unexpected(t,lb._function)}return e.specifiers=[],e.source=null,e.declaration=this.parseExportDeclaration(e),!0}return!1},r.isAsyncFunction=function(){if(!this.isContextual("async"))return!1;var e=this.nextTokenStart();return!pb.test(this.input.slice(this.state.pos,e))&&this.isUnparsedContextual(e,"function")},r.parseExportDefaultExpression=function(){var e=this.startNode(),t=this.isAsyncFunction();if(this.match(lb._function)||t)return this.next(),t&&this.next(),this.parseFunction(e,5,t);if(this.match(lb._class))return this.parseClass(e,!0,!0);if(this.match(lb.at))return this.hasPlugin("decorators")&&this.getPluginOption("decorators","decoratorsBeforeExport")&&this.raise(this.state.start,xb.DecoratorBeforeExport),this.parseDecorators(!1),this.parseClass(e,!0,!0);if(this.match(lb._const)||this.match(lb._var)||this.isLet())throw this.raise(this.state.start,xb.UnsupportedDefaultExport);var r=this.parseMaybeAssign();return this.semicolon(),r},r.parseExportDeclaration=function(e){return this.parseStatement(null)},r.isExportDefaultSpecifier=function(){if(this.match(lb.name))return"async"!==this.state.value&&"let"!==this.state.value;if(!this.match(lb._default))return!1;var e=this.nextTokenStart();return 44===this.input.charCodeAt(e)||this.isUnparsedContextual(e,"from")},r.parseExportFrom=function(e,t){this.eatContextual("from")?(e.source=this.parseImportSource(),this.checkExport(e)):t?this.unexpected():e.source=null,this.semicolon()},r.shouldParseExportDeclaration=function(){if(this.match(lb.at)&&(this.expectOnePlugin(["decorators","decorators-legacy"]),this.hasPlugin("decorators"))){if(!this.getPluginOption("decorators","decoratorsBeforeExport"))return!0;this.unexpected(this.state.start,xb.DecoratorBeforeExport)}return"var"===this.state.type.keyword||"const"===this.state.type.keyword||"function"===this.state.type.keyword||"class"===this.state.type.keyword||this.isLet()||this.isAsyncFunction()},r.checkExport=function(e,t,r,n){if(t)if(r)this.checkDuplicateExports(e,"default");else if(e.specifiers&&e.specifiers.length)for(var a=0,s=e.specifiers;a<s.length;a++){var i=s[a];this.checkDuplicateExports(i,i.exported.name),!n&&i.local&&(this.checkReservedWord(i.local.name,i.local.start,!0,!1),this.scope.checkLocalExport(i.local))}else if(e.declaration)if("FunctionDeclaration"===e.declaration.type||"ClassDeclaration"===e.declaration.type){var o=e.declaration.id;if(!o)throw new Error("Assertion failure");this.checkDuplicateExports(e,o.name)}else if("VariableDeclaration"===e.declaration.type)for(var u=0,c=e.declaration.declarations;u<c.length;u++){var l=c[u];this.checkDeclaration(l.id)}if(this.state.decoratorStack[this.state.decoratorStack.length-1].length){var p=e.declaration&&("ClassDeclaration"===e.declaration.type||"ClassExpression"===e.declaration.type);if(!e.declaration||!p)throw this.raise(e.start,xb.UnsupportedDecoratorExport);this.takeDecorators(e.declaration)}},r.checkDeclaration=function(e){if("Identifier"===e.type)this.checkDuplicateExports(e,e.name);else if("ObjectPattern"===e.type)for(var t=0,r=e.properties;t<r.length;t++){var n=r[t];this.checkDeclaration(n)}else if("ArrayPattern"===e.type)for(var a=0,s=e.elements;a<s.length;a++){var i=s[a];i&&this.checkDeclaration(i)}else"ObjectProperty"===e.type?this.checkDeclaration(e.value):"RestElement"===e.type?this.checkDeclaration(e.argument):"AssignmentPattern"===e.type&&this.checkDeclaration(e.left)},r.checkDuplicateExports=function(e,t){this.state.exportedIdentifiers.indexOf(t)>-1&&this.raise(e.start,"default"===t?xb.DuplicateDefaultExport:xb.DuplicateExport,t),this.state.exportedIdentifiers.push(t)},r.parseExportSpecifiers=function(){var e=[],t=!0;for(this.expect(lb.braceL);!this.eat(lb.braceR);){if(t)t=!1;else if(this.expect(lb.comma),this.eat(lb.braceR))break;var r=this.startNode();r.local=this.parseIdentifier(!0),r.exported=this.eatContextual("as")?this.parseIdentifier(!0):r.local.__clone(),e.push(this.finishNode(r,"ExportSpecifier"))}return e},r.parseImport=function(e){if(e.specifiers=[],!this.match(lb.string)){var t=!this.maybeParseDefaultImportSpecifier(e)||this.eat(lb.comma),r=t&&this.maybeParseStarImportSpecifier(e);t&&!r&&this.parseNamedImportSpecifiers(e),this.expectContextual("from")}return e.source=this.parseImportSource(),this.semicolon(),this.finishNode(e,"ImportDeclaration")},r.parseImportSource=function(){return this.match(lb.string)||this.unexpected(),this.parseExprAtom()},r.shouldParseDefaultImport=function(e){return this.match(lb.name)},r.parseImportSpecifierLocal=function(e,t,r,n){t.local=this.parseIdentifier(),this.checkLVal(t.local,9,void 0,n),e.specifiers.push(this.finishNode(t,r))},r.maybeParseDefaultImportSpecifier=function(e){return!!this.shouldParseDefaultImport(e)&&(this.parseImportSpecifierLocal(e,this.startNode(),"ImportDefaultSpecifier","default import specifier"),!0)},r.maybeParseStarImportSpecifier=function(e){if(this.match(lb.star)){var t=this.startNode();return this.next(),this.expectContextual("as"),this.parseImportSpecifierLocal(e,t,"ImportNamespaceSpecifier","import namespace specifier"),!0}return!1},r.parseNamedImportSpecifiers=function(e){var t=!0;for(this.expect(lb.braceL);!this.eat(lb.braceR);){if(t)t=!1;else{if(this.eat(lb.colon))throw this.raise(this.state.start,xb.DestructureNamedImport);if(this.expect(lb.comma),this.eat(lb.braceR))break}this.parseImportSpecifier(e)}},r.parseImportSpecifier=function(e){var t=this.startNode();t.imported=this.parseIdentifier(!0),this.eatContextual("as")?t.local=this.parseIdentifier():(this.checkReservedWord(t.imported.name,t.start,!0,!0),t.local=t.imported.__clone()),this.checkLVal(t.local,9,void 0,"import specifier"),e.specifiers.push(this.finishNode(t,"ImportSpecifier"))},t}(Dx),Px=function(){this.privateNames=new Set,this.loneAccessors=new Map,this.undefinedPrivateNames=new Map},kx=function(){function e(e){this.stack=[],this.undefinedPrivateNames=new Map,this.raise=e}var t=e.prototype;return t.current=function(){return this.stack[this.stack.length-1]},t.enter=function(){this.stack.push(new Px)},t.exit=function(){for(var e=this.stack.pop(),t=this.current(),r=0,n=Array.from(e.undefinedPrivateNames);r<n.length;r++){var a=n[r],s=a[0],i=a[1];t?t.undefinedPrivateNames.has(s)||t.undefinedPrivateNames.set(s,i):this.raise(i,xb.InvalidPrivateFieldResolution,s)}},t.declarePrivateName=function(e,t,r){var n=this.current(),a=n.privateNames.has(e);if(3&t){var s=a&&n.loneAccessors.get(e);if(s)(a=(3&s)===(3&t)||(4&s)!==(4&t))||n.loneAccessors.delete(e);else a||n.loneAccessors.set(e,t)}a&&this.raise(r,xb.PrivateNameRedeclaration,e),n.privateNames.add(e),n.undefinedPrivateNames.delete(e)},t.usePrivateName=function(e,t){for(var r,n=0,a=this.stack;n<a.length;n++)if((r=a[n]).privateNames.has(e))return;r?r.undefinedPrivateNames.set(e,t):this.raise(t,xb.InvalidPrivateFieldResolution,e)},e}(),Fx=function(e){function t(t,r){var n;t=function(e){for(var t={},r=0,n=Object.keys(hx);r<n.length;r++){var a=n[r];t[a]=e&&null!=e[a]?e[a]:hx[a]}return t}(t);var a=(n=e.call(this,t,r)||this).getScopeHandler();return n.options=t,n.inModule="module"===n.options.sourceType,n.scope=new a(n.raise.bind(s(n)),n.inModule),n.prodParam=new ax,n.classScope=new kx(n.raise.bind(s(n))),n.plugins=function(e){for(var t=new Map,r=0;r<e.length;r++){var n=e[r],a=Array.isArray(n)?n:[n,{}],s=a[0],i=a[1];t.has(s)||t.set(s,i||{})}return t}(n.options.plugins),n.filename=t.sourceFilename,n}a(t,e);var r=t.prototype;return r.getScopeHandler=function(){return tx},r.parse=function(){var e=0;this.hasPlugin("topLevelAwait")&&this.inModule&&(e|=2),this.scope.enter(1),this.prodParam.enter(e);var t=this.startNode(),r=this.startNode();return this.nextToken(),t.errors=null,this.parseTopLevel(t,r),t.errors=this.state.errors,t},t}(jx);function _x(e,t){if(!t||"unambiguous"!==t.sourceType)return Ix(t,e).parse();t=Object.assign({},t);try{t.sourceType="module";var r=Ix(t,e),n=r.parse();if(r.sawUnambiguousESM)return n;if(r.ambiguousScriptDifferentAst)try{return t.sourceType="script",Ix(t,e).parse()}catch(e){}else n.program.sourceType="script";return n}catch(r){try{return t.sourceType="script",Ix(t,e).parse()}catch(e){}throw r}}function Ix(e,t){var r=Fx;return e&&e.plugins&&(!function(e){if(cx(e,"decorators")){if(cx(e,"decorators-legacy"))throw new Error("Cannot use the decorators and decorators-legacy plugin together");var t=lx(e,"decorators","decoratorsBeforeExport");if(null==t)throw new Error("The 'decorators' plugin requires a 'decoratorsBeforeExport' option, whose value must be a boolean. If you are migrating from Babylon/Babel 6 or want to use the old decorators proposal, you should use the 'decorators-legacy' plugin instead of 'decorators'.");if("boolean"!=typeof t)throw new Error("'decoratorsBeforeExport' must be a boolean.")}if(cx(e,"flow")&&cx(e,"typescript"))throw new Error("Cannot combine flow and typescript plugins.");if(cx(e,"placeholders")&&cx(e,"v8intrinsic"))throw new Error("Cannot combine placeholders and v8intrinsic plugins.");if(cx(e,"pipelineOperator")&&!px.includes(lx(e,"pipelineOperator","proposal")))throw new Error("'pipelineOperator' requires 'proposal' option whose value should be one of: "+px.map((function(e){return"'"+e+"'"})).join(", "))}(e.plugins),r=function(e){var t=fx.filter((function(t){return cx(e,t)})),r=t.join("/"),n=Bx[r];if(!n){n=Fx;for(var a=0;a<t.length;a++){var s=t[a];n=dx[s](n)}Bx[r]=n}return n}(e.plugins)),new r(e,t)}var Bx={};var Ox={Function:function(e){e.skip()},VariableDeclaration:function(e){if("var"===e.node.kind){for(var t=e.getBindingIdentifiers(),r=0,n=Object.keys(t);r<n.length;r++){var a=n[r];e.scope.push({id:t[a]})}for(var s=[],i=0,o=e.node.declarations;i<o.length;i++){var u=o[i];u.init&&s.push(Ki(_i("=",u.id,u.init)))}e.replaceWithMultiple(s)}}};var Nx=Object.freeze({__proto__:null,replaceWithMultiple:function(e){this.resync(),Ip((e=this._verifyNodeList(e))[0],this.node),Bp(e[e.length-1],this.node),this.node=this.container[this.key]=null;var t=this.insertAfter(e);return this.node?this.requeue():this.remove(),t},replaceWithSourceString:function(e){this.resync();try{e=_x(e="("+e+")")}catch(r){var t=r.loc;throw t&&(r.message+=" - make sure this is an expression.\n"+sb(e,{start:{line:t.line,column:t.column+1}}),r.code="BABEL_REPLACE_SOURCE_ERROR"),r}return e=e.program.body[0].expression,sA.removeProperties(e),this.replaceWith(e)},replaceWith:function(e){if(this.resync(),this.removed)throw new Error("You can't replace this node, we've already removed it");if(e instanceof VE&&(e=e.node),!e)throw new Error("You passed `path.replaceWith()` a falsy node, use `path.remove()` instead");if(this.node===e)return[this];if(this.isProgram()&&!B(e))throw new Error("You can only replace a Program root node with another Program node");if(Array.isArray(e))throw new Error("Don't use `path.replaceWith()` with an array of nodes, use `path.replaceWithMultiple()`");if("string"==typeof e)throw new Error("Don't use `path.replaceWith()` with a source string, use `path.replaceWithSourceString()`");var t="";if(this.isNodeType("Statement")&&rt(e)&&(this.canHaveVariableDeclarationOrExpression()||this.canSwapBetweenExpressionAndStatement(e)||this.parentPath.isExportDefaultDeclaration()||(e=Ki(e),t="expression")),this.isNodeType("Expression")&&st(e)&&!this.canHaveVariableDeclarationOrExpression()&&!this.canSwapBetweenExpressionAndStatement(e))return this.replaceExpressionWithStatements([e]);var r=this.node;return r&&(Op(e,r),Np(r)),this._replaceWith(e),this.type=e.type,this.setScope(),this.requeue(),[t?this.get(t):this]},_replaceWith:function(e){if(!this.container)throw new ReferenceError("Container is falsy");this.inList?Ts(this.parent,this.key,[e]):Ts(this.parent,this.key,e),this.debug("Replace with "+(e&&e.type)),this.node=this.container[this.key]=e},replaceExpressionWithStatements:function(e){this.resync();var t=Gd(e,this.scope);if(t)return this.replaceWith(t)[0].get("expressions");var r=this.getFunctionParent(),n=r&&r.is("async"),a=Fo([],Ri(e));this.replaceWith(Li(a,[])),this.traverse(Ox);var s=this.get("callee").getCompletionRecords(),i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u;if(c.isExpressionStatement()){var l=c.findParent((function(e){return e.isLoop()}));if(l){var p=l.getData("expressionReplacementReturnUid");if(p)p=Qi(p.name);else{var d=this.get("callee");p=d.scope.generateDeclaredUidIdentifier("ret"),d.get("body").pushContainer("body",mo(pp(p))),l.setData("expressionReplacementReturnUid",p)}c.get("expression").replaceWith(_i("=",pp(p),c.node.expression))}else c.replaceWith(mo(c.node.expression))}}var f=this.get("callee");return f.arrowFunctionToExpression(),n&&sA.hasType(this.get("callee.body").node,"AwaitExpression",$p)&&(f.set("async",!0),this.replaceWith(Cc(this.node))),f.get("body.body")},replaceInline:function(e){if(this.resync(),Array.isArray(e)){if(Array.isArray(this.container)){e=this._verifyNodeList(e);var t=this._containerInsertAfter(e);return this.remove(),t}return this.replaceWithMultiple(e)}return this.replaceWith(e)}}),Rx=["String","Number","Math"],Mx=["random"];function Lx(e,t){t.confident&&(t.deoptPath=e,t.confident=!1)}function Ux(e,t){var r=e.node,n=t.seen;if(n.has(r)){var a=n.get(r);return a.resolved?a.value:void Lx(e,t)}var s={resolved:!1};n.set(r,s);var i=function(e,t){if(!t.confident)return;var r=e.node;if(e.isSequenceExpression()){var n=e.get("expressions");return Ux(n[n.length-1],t)}if(e.isStringLiteral()||e.isNumericLiteral()||e.isBooleanLiteral())return r.value;if(e.isNullLiteral())return null;if(e.isTemplateLiteral())return Gx(e,r.quasis,t);if(e.isTaggedTemplateExpression()&&e.get("tag").isMemberExpression()){var a=e.get("tag.object"),s=a.node.name,i=e.get("tag.property");if(a.isIdentifier()&&"String"===s&&!e.scope.getBinding(s,!0)&&i.isIdentifier&&"raw"===i.node.name)return Gx(e,r.quasi.quasis,t,!0)}if(e.isConditionalExpression()){var o=Ux(e.get("test"),t);if(!t.confident)return;return Ux(o?e.get("consequent"):e.get("alternate"),t)}if(e.isExpressionWrapper())return Ux(e.get("expression"),t);if(e.isMemberExpression()&&!e.parentPath.isCallExpression({callee:r})){var u=e.get("property"),c=e.get("object");if(c.isLiteral()&&u.isIdentifier()){var l=c.node.value,p=typeof l;if("number"===p||"string"===p)return l[u.node.name]}}if(e.isReferencedIdentifier()){var d=e.scope.getBinding(r.name);if(d&&d.constantViolations.length>0)return Lx(d.path,t);if(d&&e.node.start<d.path.node.end)return Lx(d.path,t);if(d&&d.hasValue)return d.value;if("undefined"===r.name)return d?Lx(d.path,t):void 0;if("Infinity"===r.name)return d?Lx(d.path,t):1/0;if("NaN"===r.name)return d?Lx(d.path,t):NaN;var f=e.resolve();return f===e?Lx(e,t):Ux(f,t)}if(e.isUnaryExpression({prefix:!0})){if("void"===r.operator)return;var h=e.get("argument");if("typeof"===r.operator&&(h.isFunction()||h.isClass()))return"function";var m=Ux(h,t);if(!t.confident)return;switch(r.operator){case"!":return!m;case"+":return+m;case"-":return-m;case"~":return~m;case"typeof":return typeof m}}if(e.isArrayExpression()){var y=[],g=e.get("elements"),v=Array.isArray(g),b=0;for(g=v?g:g[Symbol.iterator]();;){var x;if(v){if(b>=g.length)break;x=g[b++]}else{if((b=g.next()).done)break;x=b.value}var E=x,A=E.evaluate();if(!A.confident)return Lx(E,t);y.push(A.value)}return y}if(e.isObjectExpression()){var w={},S=e.get("properties"),D=Array.isArray(S),C=0;for(S=D?S:S[Symbol.iterator]();;){var T;if(D){if(C>=S.length)break;T=S[C++]}else{if((C=S.next()).done)break;T=C.value}var j=T;if(j.isObjectMethod()||j.isSpreadElement())return Lx(j,t);var P=j.get("key"),k=P;if(j.node.computed){if(!(k=k.evaluate()).confident)return Lx(P,t);k=k.value}else k=k.isIdentifier()?k.node.name:k.node.value;var F=j.get("value"),_=F.evaluate();if(!_.confident)return Lx(F,t);_=_.value,w[k]=_}return w}if(e.isLogicalExpression()){var I=t.confident,B=Ux(e.get("left"),t),O=t.confident;t.confident=I;var N=Ux(e.get("right"),t),R=t.confident;switch(r.operator){case"||":if(t.confident=O&&(!!B||R),!t.confident)return;return B||N;case"&&":if(t.confident=O&&(!B||R),!t.confident)return;return B&&N}}if(e.isBinaryExpression()){var M=Ux(e.get("left"),t);if(!t.confident)return;var L=Ux(e.get("right"),t);if(!t.confident)return;switch(r.operator){case"-":return M-L;case"+":return M+L;case"/":return M/L;case"*":return M*L;case"%":return M%L;case"**":return Math.pow(M,L);case"<":return M<L;case">":return M>L;case"<=":return M<=L;case">=":return M>=L;case"==":return M==L;case"!=":return M!=L;case"===":return M===L;case"!==":return M!==L;case"|":return M|L;case"&":return M&L;case"^":return M^L;case"<<":return M<<L;case">>":return M>>L;case">>>":return M>>>L}}if(e.isCallExpression()){var U,G,V=e.get("callee");if(V.isIdentifier()&&!e.scope.getBinding(V.node.name,!0)&&Rx.indexOf(V.node.name)>=0&&(G=Pa[r.callee.name]),V.isMemberExpression()){var W=V.get("object"),H=V.get("property");if(W.isIdentifier()&&H.isIdentifier()&&Rx.indexOf(W.node.name)>=0&&Mx.indexOf(H.node.name)<0&&(U=Pa[W.node.name],G=U[H.node.name]),W.isLiteral()&&H.isIdentifier()){var q=typeof W.node.value;"string"!==q&&"number"!==q||(U=W.node.value,G=U[H.node.name])}}if(G){var K=e.get("arguments").map((function(e){return Ux(e,t)}));if(!t.confident)return;return G.apply(U,K)}}Lx(e,t)}(e,t);return t.confident&&(s.resolved=!0,s.value=i),i}function Gx(e,t,r,n){void 0===n&&(n=!1);var a="",s=0,i=e.get("expressions"),o=t,u=Array.isArray(o),c=0;for(o=u?o:o[Symbol.iterator]();;){var l;if(u){if(c>=o.length)break;l=o[c++]}else{if((c=o.next()).done)break;l=c.value}var p=l;if(!r.confident)break;a+=n?p.value.raw:p.value.cooked;var d=i[s++];d&&(a+=String(Ux(d,r)))}if(r.confident)return a}var Vx=Object.freeze({__proto__:null,evaluateTruthy:function(){var e=this.evaluate();if(e.confident)return!!e.value},evaluate:function(){var e={confident:!0,deoptPath:null,seen:new Map},t=Ux(this,e);return e.confident||(t=void 0),{confident:e.confident,deopt:e.deoptPath,value:t}}});function Wx(e){return{code:function(e){return"/* @babel/template */;\n"+e},validate:function(){},unwrap:function(t){return e(t.program.body.slice(1))}}}var Hx=Wx((function(e){return e.length>1?e:e[0]})),qx=Wx((function(e){return e})),Kx=Wx((function(e){if(0===e.length)throw new Error("Found nothing to return.");if(e.length>1)throw new Error("Found multiple statements but wanted one");return e[0]})),zx={code:function(e){return"(\n"+e+"\n)"},validate:function(e){var t=e.program;if(t.body.length>1)throw new Error("Found multiple statements but wanted one");if(0===t.body[0].expression.start)throw new Error("Parse result included parens.")},unwrap:function(e){return e.program.body[0].expression}};function Xx(e,t){var r=t.placeholderWhitelist,n=void 0===r?e.placeholderWhitelist:r,a=t.placeholderPattern,s=void 0===a?e.placeholderPattern:a,i=t.preserveComments,o=void 0===i?e.preserveComments:i,u=t.syntacticPlaceholders,c=void 0===u?e.syntacticPlaceholders:u;return{parser:Object.assign({},e.parser,{},t.parser),placeholderWhitelist:n,placeholderPattern:s,preserveComments:o,syntacticPlaceholders:c}}function Yx(e){if(null!=e&&"object"!=typeof e)throw new Error("Unknown template options.");var t=e||{},r=t.placeholderWhitelist,n=t.placeholderPattern,a=t.preserveComments,s=t.syntacticPlaceholders,i=function(e,t){if(null==e)return{};var r,n,a={},s=Object.keys(e);for(n=0;n<s.length;n++)r=s[n],t.indexOf(r)>=0||(a[r]=e[r]);return a}(t,["placeholderWhitelist","placeholderPattern","preserveComments","syntacticPlaceholders"]);if(null!=r&&!(r instanceof Set))throw new Error("'.placeholderWhitelist' must be a Set, null, or undefined");if(null!=n&&!(n instanceof RegExp)&&!1!==n)throw new Error("'.placeholderPattern' must be a RegExp, false, null, or undefined");if(null!=a&&"boolean"!=typeof a)throw new Error("'.preserveComments' must be a boolean, null, or undefined");if(null!=s&&"boolean"!=typeof s)throw new Error("'.syntacticPlaceholders' must be a boolean, null, or undefined");if(!0===s&&(null!=r||null!=n))throw new Error("'.placeholderWhitelist' and '.placeholderPattern' aren't compatible with '.syntacticPlaceholders: true'");return{parser:i,placeholderWhitelist:r||void 0,placeholderPattern:null==n?void 0:n,preserveComments:null==a?void 0:a,syntacticPlaceholders:null==s?void 0:s}}function Jx(e){if(Array.isArray(e))return e.reduce((function(e,t,r){return e["$"+r]=t,e}),{});if("object"==typeof e||null==e)return e||void 0;throw new Error("Template replacements must be an array, object, null, or undefined")}var $x=/^[_$A-Z0-9]+$/;function Qx(e,t,r){var n=function(e,t){t=Object.assign({allowReturnOutsideFunction:!0,allowSuperOutsideMethod:!0,sourceType:"module"},t,{plugins:(t.plugins||[]).concat("placeholders")});try{return _x(e,t)}catch(t){var r=t.loc;throw r&&(t.message+="\n"+sb(e,{start:r}),t.code="BABEL_TEMPLATE_PARSE_ERROR"),t}}(t,r.parser),a=r.placeholderWhitelist,s=r.placeholderPattern,i=r.preserveComments,o=r.syntacticPlaceholders;Md(n,{preserveComments:i}),e.validate(n);var u={placeholders:[],placeholderNames:new Set},c={placeholders:[],placeholderNames:new Set},l={value:void 0};return rf(n,Zx,{syntactic:u,legacy:c,isLegacyRef:l,placeholderWhitelist:a,placeholderPattern:s,syntacticPlaceholders:o}),Object.assign({ast:n},l.value?c:u)}function Zx(e,t,r){var n;if(Le(e)){if(!1===r.syntacticPlaceholders)throw new Error("%%foo%%-style placeholders can't be used when '.syntacticPlaceholders' is false.");n=e.name.name,r.isLegacyRef.value=!1}else{if(!1===r.isLegacyRef.value||r.syntacticPlaceholders)return;if(S(e)||Be(e))n=e.name,r.isLegacyRef.value=!0;else{if(!T(e))return;n=e.value,r.isLegacyRef.value=!0}}if(!r.isLegacyRef.value&&(null!=r.placeholderPattern||null!=r.placeholderWhitelist))throw new Error("'.placeholderWhitelist' and '.placeholderPattern' aren't compatible with '.syntacticPlaceholders: true'");if(!r.isLegacyRef.value||!1!==r.placeholderPattern&&(r.placeholderPattern||$x).test(n)||r.placeholderWhitelist&&r.placeholderWhitelist.has(n)){var a,s=(t=t.slice())[t.length-1],i=s.node,o=s.key;T(e)||Le(e,{expectedNode:"StringLiteral"})?a="string":I(i)&&"arguments"===o||f(i)&&"arguments"===o||lt(i)&&"params"===o?a="param":v(i)&&!Le(e)?(a="statement",t=t.slice(0,-1)):a=st(e)&&Le(e)?"statement":"other";var u=r.isLegacyRef.value?r.legacy:r.syntactic,c=u.placeholders,l=u.placeholderNames;c.push({name:n,type:a,resolve:function(e){return function(e,t){for(var r=e,n=0;n<t.length-1;n++){var a=t[n],s=a.key,i=a.index;r=void 0===i?r[s]:r[s][i]}var o=t[t.length-1],u=o.key,c=o.index;return{parent:r,key:u,index:c}}(e,t)},isDuplicate:l.has(n)}),l.add(n)}}function eE(e,t){var r=pp(e.ast);return t&&(e.placeholders.forEach((function(e){if(!Object.prototype.hasOwnProperty.call(t,e.name)){var r=e.name;throw new Error('Error: No substitution given for "'+r+"\". If this is not meant to be a\n placeholder you may want to consider passing one of the following options to @babel/template:\n - { placeholderPattern: false, placeholderWhitelist: new Set(['"+r+"'])}\n - { placeholderPattern: /^"+r+"$/ }")}})),Object.keys(t).forEach((function(t){if(!e.placeholderNames.has(t))throw new Error('Unknown substitution "'+t+'" given')}))),e.placeholders.slice().reverse().forEach((function(e){try{!function(e,t,r){e.isDuplicate&&(Array.isArray(r)?r=r.map((function(e){return pp(e)})):"object"==typeof r&&(r=pp(r)));var n=e.resolve(t),a=n.parent,s=n.key,i=n.index;if("string"===e.type){if("string"==typeof r&&(r=to(r)),!r||!T(r))throw new Error("Expected string substitution")}else if("statement"===e.type)void 0===i?r?Array.isArray(r)?r=Ri(r):"string"==typeof r?r=Ki(Qi(r)):st(r)||(r=Ki(r)):r=qi():r&&!Array.isArray(r)&&("string"==typeof r&&(r=Qi(r)),st(r)||(r=Ki(r)));else if("param"===e.type){if("string"==typeof r&&(r=Qi(r)),void 0===i)throw new Error("Assertion failure.")}else if("string"==typeof r&&(r=Qi(r)),Array.isArray(r))throw new Error("Cannot replace single expression with an array.");if(void 0===i)Ts(a,s,r),a[s]=r;else{var o=a[s].slice();"statement"===e.type||"param"===e.type?null==r?o.splice(i,1):Array.isArray(r)?o.splice.apply(o,[i,1].concat(r)):o[i]=r:o[i]=r,Ts(a,s,o),a[s]=o}}(e,r,t&&t[e.name]||null)}catch(t){throw t.message='@babel/template placeholder "'+e.name+'": '+t.message,t}})),r}function tE(e,t,r){var n;return t=e.code(t),function(a){var s=Jx(a);return n||(n=Qx(e,t,r)),e.unwrap(eE(n,s))}}function rE(e,t,r){var n=function(e,t,r){var n,a,s,i="";do{var o=nE(t,i+="$");n=o.names,a=new Set(n),s=Qx(e,e.code(o.code),{parser:r.parser,placeholderWhitelist:new Set(o.names.concat(r.placeholderWhitelist?Array.from(r.placeholderWhitelist):[])),placeholderPattern:r.placeholderPattern,preserveComments:r.preserveComments,syntacticPlaceholders:r.syntacticPlaceholders})}while(s.placeholders.some((function(e){return e.isDuplicate&&a.has(e.name)})));return{metadata:s,names:n}}(e,t,r),a=n.metadata,s=n.names;return function(t){var r=t.reduce((function(e,t,r){return e[s[r]]=t,e}),{});return function(t){var n=Jx(t);return n&&Object.keys(n).forEach((function(e){if(Object.prototype.hasOwnProperty.call(r,e))throw new Error("Unexpected replacement overlap.")})),e.unwrap(eE(a,n?Object.assign(n,r):r))}}}function nE(e,t){for(var r=[],n=e[0],a=1;a<e.length;a++){var s=""+t+(a-1);r.push(s),n+=s+e[a]}return{names:r,code:n}}var aE=Yx({placeholderPattern:!1});function sE(e,t){var r=new WeakMap,n=new WeakMap,a=t||Yx(null);return Object.assign((function(t){for(var n=arguments.length,s=new Array(n>1?n-1:0),i=1;i<n;i++)s[i-1]=arguments[i];if("string"==typeof t){if(s.length>1)throw new Error("Unexpected extra params.");return iE(tE(e,t,Xx(a,Yx(s[0]))))}if(Array.isArray(t)){var o=r.get(t);return o||(o=rE(e,t,a),r.set(t,o)),iE(o(s))}if("object"==typeof t&&t){if(s.length>0)throw new Error("Unexpected extra params.");return sE(e,Xx(a,Yx(t)))}throw new Error("Unexpected template param "+typeof t)}),{ast:function(t){for(var r=arguments.length,s=new Array(r>1?r-1:0),i=1;i<r;i++)s[i-1]=arguments[i];if("string"==typeof t){if(s.length>1)throw new Error("Unexpected extra params.");return tE(e,t,Xx(Xx(a,Yx(s[0])),aE))()}if(Array.isArray(t)){var o=n.get(t);return o||(o=rE(e,t,Xx(a,aE)),n.set(t,o)),o(s)()}throw new Error("Unexpected template param "+typeof t)}})}function iE(e){var t="";try{throw new Error}catch(e){e.stack&&(t=e.stack.split("\n").slice(3).join("\n"))}return function(r){try{return e(r)}catch(e){throw e.stack+="\n =============\n"+t,e}}}var oE=sE(Hx),uE=sE(Kx),cE=sE(qx),lE=sE(zx),pE=sE({code:function(e){return e},validate:function(){},unwrap:function(e){return e.program}}),dE=Object.assign(oE.bind(void 0),{smart:oE,statement:uE,statements:cE,expression:lE,program:pE,ast:oE.ast}),fE=dE("\n (function (FUNCTION_KEY) {\n function FUNCTION_ID() {\n return FUNCTION_KEY.apply(this, arguments);\n }\n\n FUNCTION_ID.toString = function () {\n return FUNCTION_KEY.toString();\n }\n\n return FUNCTION_ID;\n })(FUNCTION)\n"),hE=dE("\n (function (FUNCTION_KEY) {\n function* FUNCTION_ID() {\n return yield* FUNCTION_KEY.apply(this, arguments);\n }\n\n FUNCTION_ID.toString = function () {\n return FUNCTION_KEY.toString();\n };\n\n return FUNCTION_ID;\n })(FUNCTION)\n"),mE={"ReferencedIdentifier|BindingIdentifier":function(e,t){e.node.name===t.name&&(e.scope.getBindingIdentifier(t.name)===t.outerDeclar&&(t.selfReference=!0,e.stop()))}};function yE(e,t,r,n){if(e.selfReference){if(!n.hasBinding(r.name)||n.hasGlobal(r.name)){if(!lt(t))return;var a=fE;t.generator&&(a=hE);for(var s=a({FUNCTION:t,FUNCTION_ID:r,FUNCTION_KEY:n.generateUidIdentifier(r.name)}).expression,i=s.callee.body.body[0].params,o=0,u=function(e){for(var t=e.params,r=0;r<t.length;r++){var n=t[r];if(X(n)||M(n))return r}return t.length}(t);o<u;o++)i.push(n.generateUidIdentifier("x"));return s}n.rename(r.name)}t.id=r,n.getProgramParent().references[r.name]=!0}function gE(e,t){var r=e.node,n=e.parent,a=e.scope,s=e.id;if(void 0===t&&(t=!1),!r.id){if(!R(n)&&!N(n,{kind:"method"})||n.computed&&!ft(n.key)){if(K(n)){if(S(s=n.id)&&!t){var i=a.parent.getBinding(s.name);if(i&&i.constant&&a.getBinding(s.name)===i)return r.id=pp(s),void(r.id[Cs]=!0)}}else if(c(n))s=n.left;else if(!s)return}else s=n.key;var o;if(s&&ft(s)?o=function(e){return P(e)?"null":k(e)?"_"+e.pattern+"_"+e.flags:fe(e)?e.quasis.map((function(e){return e.value.raw})).join(""):void 0!==e.value?e.value+"":""}(s):s&&S(s)&&(o=s.name),void 0!==o)return(s=Qi(o=Fd(o)))[Cs]=!0,yE(function(e,t,r){var n={selfAssignment:!1,selfReference:!1,outerDeclar:r.getBindingIdentifier(t),references:[],name:t},a=r.getOwnBinding(t);return a?"param"===a.kind&&(n.selfReference=!0):(n.outerDeclar||r.hasGlobal(t))&&r.traverse(e,mE,n),n}(r,o,a),r,s,a)||r}}function vE(e,t,r){void 0===t&&(t=!1),void 0===r&&(r=!0);var n=e.findParent((function(e){return e.isFunction()&&!e.isArrowFunctionExpression()||e.isProgram()||e.isClassProperty({static:!1})})),a=n&&"constructor"===n.node.kind;if(n.isClassProperty())throw e.buildCodeFrameError("Unable to transform arrow inside class property");var s,i=function(e){var t=[],r=[],n=[],a=[],s=[];return e.traverse({ClassProperty:function(e){e.skip()},Function:function(e){e.isArrowFunctionExpression()||e.skip()},ThisExpression:function(e){t.push(e)},JSXIdentifier:function(e){"this"===e.node.name&&(e.parentPath.isJSXMemberExpression({object:e.node})||e.parentPath.isJSXOpeningElement({name:e.node}))&&t.push(e)},CallExpression:function(e){e.get("callee").isSuper()&&s.push(e)},MemberExpression:function(e){e.get("object").isSuper()&&a.push(e)},ReferencedIdentifier:function(e){"arguments"===e.node.name&&r.push(e)},MetaProperty:function(e){e.get("meta").isIdentifier({name:"new"})&&e.get("property").isIdentifier({name:"target"})&&n.push(e)}}),{thisPaths:t,argumentsPaths:r,newTargetPaths:n,superProps:a,superCalls:s}}(e),o=i.thisPaths,u=i.argumentsPaths,c=i.newTargetPaths,l=i.superProps,p=i.superCalls;if(a&&p.length>0){if(!r)throw p[0].buildCodeFrameError("Unable to handle nested super() usage in arrow");var d=[];n.traverse({Function:function(e){e.isArrowFunctionExpression()||e.skip()},ClassProperty:function(e){e.skip()},CallExpression:function(e){e.get("callee").isSuper()&&d.push(e)}});var f=function(e){return xE(e,"supercall",(function(){var t=e.scope.generateUidIdentifier("args");return Fo([ho(t)],Li(Xo(),[zo(Qi(t.name))]))}))}(n);d.forEach((function(e){var t=Qi(f);t.loc=e.node.callee.loc,e.get("callee").replaceWith(t)}))}if(u.length>0){var h=xE(n,"arguments",(function(){return Qi("arguments")}));u.forEach((function(e){var t=Qi(h);t.loc=e.node.loc,e.replaceWith(t)}))}if(c.length>0){var m=xE(n,"newtarget",(function(){return Ho(Qi("new"),Qi("target"))}));c.forEach((function(e){var t=Qi(m);t.loc=e.node.loc,e.replaceWith(t)}))}if(l.length>0){if(!r)throw l[0].buildCodeFrameError("Unable to handle nested super.prop usage");l.reduce((function(e,t){return e.concat(function(e){if(e.parentPath.isAssignmentExpression()&&"="!==e.parentPath.node.operator){var t=e.parentPath,r=t.node.operator.slice(0,-1),n=t.node.right;if(t.node.operator="=",e.node.computed){var a=e.scope.generateDeclaredUidIdentifier("tmp");t.get("left").replaceWith(oo(e.node.object,_i("=",a,e.node.property),!0)),t.get("right").replaceWith(Ii(r,oo(e.node.object,Qi(a.name),!0),n))}else t.get("left").replaceWith(oo(e.node.object,e.node.property)),t.get("right").replaceWith(Ii(r,oo(e.node.object,Qi(e.node.property.name)),n));return[t.get("left"),t.get("right").get("left")]}if(e.parentPath.isUpdateExpression()){var s=e.parentPath,i=e.scope.generateDeclaredUidIdentifier("tmp"),o=e.node.computed?e.scope.generateDeclaredUidIdentifier("prop"):null,u=[_i("=",i,oo(e.node.object,o?_i("=",o,e.node.property):e.node.property,e.node.computed)),_i("=",oo(e.node.object,o?Qi(o.name):e.node.property,e.node.computed),Ii("+",Qi(i.name),ro(1)))];e.parentPath.node.prefix||u.push(Qi(i.name)),s.replaceWith(yo(u));var c=s.get("expressions.0.right"),l=s.get("expressions.1.left");return[c,l]}return[e]}(t))}),[]).forEach((function(e){var t=e.node.computed?"":e.get("property").node.name,r=e.parentPath.isAssignmentExpression({left:e.node}),a=e.parentPath.isCallExpression({callee:e.node}),s=function(e,t,r){return xE(e,"superprop_"+(t?"set":"get")+":"+(r||""),(function(){var n,a=[];if(r)n=oo(Xo(),Qi(r));else{var s=e.scope.generateUidIdentifier("prop");a.unshift(s),n=oo(Xo(),Qi(s.name),!0)}if(t){var i=e.scope.generateUidIdentifier("value");a.push(i),n=_i("=",n,Qi(i.name))}return Fo(a,n)}))}(n,r,t),i=[];if(e.node.computed&&i.push(e.get("property").node),r){var u=e.parentPath.node.right;i.push(u)}var c=Li(Qi(s),i);a?(e.parentPath.unshiftContainer("arguments",xo()),e.replaceWith(oo(c,Qi("call"))),o.push(e.parentPath.get("arguments.0"))):r?e.parentPath.replaceWith(c):e.replaceWith(c)}))}return(o.length>0||t)&&(s=function(e,t){return xE(e,"this",(function(r){if(!t||!bE(e))return xo();var n=new WeakSet;e.traverse({Function:function(e){e.isArrowFunctionExpression()||e.skip()},ClassProperty:function(e){e.skip()},CallExpression:function(e){e.get("callee").isSuper()&&(n.has(e.node)||(n.add(e.node),e.replaceWithMultiple([e.node,_i("=",Qi(r),Qi("this"))])))}})}))}(n,a),(!t||a&&bE(n))&&(o.forEach((function(e){var t=e.isJSX()?fc(s):Qi(s);t.loc=e.node.loc,e.replaceWith(t)})),t&&(s=null))),s}function bE(e){return e.isClassMethod()&&!!e.parentPath.parentPath.node.superClass}function xE(e,t,r){var n="binding:"+t,a=e.getData(n);if(!a){var s=e.scope.generateUidIdentifier(t);a=s.name,e.setData(n,a),e.scope.push({id:s,init:r(a)})}return a}var EE=Object.freeze({__proto__:null,toComputedKey:function(){var e,t=this.node;if(this.isMemberExpression())e=t.property;else{if(!this.isProperty()&&!this.isMethod())throw new ReferenceError("todo");e=t.key}return t.computed||S(e)&&(e=to(e.name)),e},ensureBlock:function(){var e=this.get("body"),t=e.node;if(Array.isArray(e))throw new Error("Can't convert array path to a block statement");if(!t)throw new Error("Can't convert node without a body");if(e.isBlockStatement())return t;var r,n,a=[],s="body";e.isStatement()?(n="body",r=0,a.push(e.node)):(s+=".body.0",this.isFunction()?(r="argument",a.push(mo(e.node))):(r="expression",a.push(Ki(e.node)))),this.node.body=Ri(a);var i=this.get(s);return e.setup(i,n?i.node[n]:i.node,n,r),this.node},arrowFunctionToShadowed:function(){this.isArrowFunctionExpression()&&this.arrowFunctionToExpression()},unwrapFunctionEnvironment:function(){if(!this.isArrowFunctionExpression()&&!this.isFunctionExpression()&&!this.isFunctionDeclaration())throw this.buildCodeFrameError("Can only unwrap the environment of a function.");vE(this)},arrowFunctionToExpression:function(e){var t=void 0===e?{}:e,r=t.allowInsertArrow,n=void 0===r||r,a=t.specCompliant,s=void 0!==a&&a;if(!this.isArrowFunctionExpression())throw this.buildCodeFrameError("Cannot convert non-arrow function to a function expression.");var i=vE(this,s,n);if(this.ensureBlock(),this.node.type="FunctionExpression",s){var o=i?null:this.parentPath.scope.generateUidIdentifier("arrowCheckId");o&&this.parentPath.scope.push({id:o,init:lo([])}),this.get("body").unshiftContainer("body",Ki(Li(this.hub.addHelper("newArrowCheck"),[xo(),Qi(o?o.name:i)]))),this.replaceWith(Li(oo(gE(this,!0)||this.node,Qi("bind")),[o?Qi(o.name):xo()]))}}});function AE(e){var t=this.node&&this.node[e];return t&&Array.isArray(t)?!!t.length:!!t}var wE=AE;function SE(e){return(e.scope.getFunctionParent()||e.scope.getProgramParent()).path}function DE(e,t){switch(e){case"LogicalExpression":return"right"===t;case"ConditionalExpression":case"IfStatement":return"consequent"===t||"alternate"===t;case"WhileStatement":case"DoWhileStatement":case"ForInStatement":case"ForOfStatement":return"body"===t;case"ForStatement":return"body"===t||"update"===t;case"SwitchStatement":return"cases"===t;case"TryStatement":return"handler"===t;case"AssignmentPattern":return"right"===t;case"OptionalMemberExpression":return"property"===t;case"OptionalCallExpression":return"arguments"===t;default:return!1}}function CE(e,t){for(var r=0;r<t;r++){var n=e[r];if(DE(n.parent.type,n.parentKey))return!0}return!1}var TE=new WeakSet;var jE=Object.freeze({__proto__:null,matchesPattern:function(e,t){return wt(this.node,e,t)},has:AE,isStatic:function(){return this.scope.isStatic(this.node)},is:wE,isnt:function(e){return!this.has(e)},equals:function(e,t){return this.node[e]===t},isNodeType:function(e){return ss(this.type,e)},canHaveVariableDeclarationOrExpression:function(){return("init"===this.key||"left"===this.key)&&this.parentPath.isFor()},canSwapBetweenExpressionAndStatement:function(e){return!("body"!==this.key||!this.parentPath.isArrowFunctionExpression())&&(this.isExpression()?p(e):!!this.isBlockStatement()&&rt(e))},isCompletionRecord:function(e){var t=this,r=!0;do{var n=t.container;if(t.isFunction()&&!r)return!!e;if(r=!1,Array.isArray(n)&&t.key!==n.length-1)return!1}while((t=t.parentPath)&&!t.isProgram());return!0},isStatementOrBlock:function(){return!this.parentPath.isLabeledStatement()&&!p(this.container)&&zf(us,this.key)},referencesImport:function(e,t){if(!this.isReferencedIdentifier())return!1;var r=this.scope.getBinding(this.node.name);if(!r||"module"!==r.kind)return!1;var n=r.path,a=n.parentPath;return!!a.isImportDeclaration()&&(a.node.source.value===e&&(!t||(!(!n.isImportDefaultSpecifier()||"default"!==t)||(!(!n.isImportNamespaceSpecifier()||"*"!==t)||!(!n.isImportSpecifier()||n.node.imported.name!==t)))))},getSource:function(){var e=this.node;if(e.end){var t=this.hub.getCode();if(t)return t.slice(e.start,e.end)}return""},willIMaybeExecuteBefore:function(e){return"after"!==this._guessExecutionStatusRelativeTo(e)},_guessExecutionStatusRelativeTo:function(e){var t={this:SE(this),target:SE(e)};if(t.target.node!==t.this.node)return this._guessExecutionStatusRelativeToDifferentFunctions(t.target);var r,n={target:e.getAncestry(),this:this.getAncestry()};if(n.target.indexOf(this)>=0)return"after";if(n.this.indexOf(e)>=0)return"before";for(var a={target:0,this:0};!r&&a.this<n.this.length;){var s=n.this[a.this];a.target=n.target.indexOf(s),a.target>=0?r=s:a.this++}if(!r)throw new Error("Internal Babel error - The two compared nodes don't appear to belong to the same program.");if(CE(n.this,a.this-1)||CE(n.target,a.target-1))return"unknown";var i={this:n.this[a.this-1],target:n.target[a.target-1]};if(i.target.listKey&&i.this.listKey&&i.target.container===i.this.container)return i.target.key>i.this.key?"before":"after";var o=ks[r.type],u=o.indexOf(i.this.parentKey);return o.indexOf(i.target.parentKey)>u?"before":"after"},_guessExecutionStatusRelativeToDifferentFunctions:function(e){if(!e.isFunctionDeclaration()||e.parentPath.isExportDeclaration())return"unknown";var t=e.scope.getBinding(e.node.id.name);if(!t.references)return"before";var r,n=t.referencePaths,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;if(!!!o.find((function(t){return t.node===e.node}))){if("callee"!==o.key||!o.parentPath.isCallExpression())return"unknown";if(!TE.has(o.node)){TE.add(o.node);var u=this._guessExecutionStatusRelativeTo(o);if(TE.delete(o.node),r&&r!==u)return"unknown";r=u}}}return r},resolve:function(e,t){return this._resolve(e,t)||this},_resolve:function(e,t){if(!(t&&t.indexOf(this)>=0))if((t=t||[]).push(this),this.isVariableDeclarator()){if(this.get("id").isIdentifier())return this.get("init").resolve(e,t)}else if(this.isReferencedIdentifier()){var r=this.scope.getBinding(this.node.name);if(!r)return;if(!r.constant)return;if("module"===r.kind)return;if(r.path!==this){var n=r.path.resolve(e,t);if(this.find((function(e){return e.node===n.node})))return;return n}}else{if(this.isTypeCastExpression())return this.get("expression").resolve(e,t);if(e&&this.isMemberExpression()){var a=this.toComputedKey();if(!ft(a))return;var s=a.value,i=this.get("object").resolve(e,t);if(i.isObjectExpression())for(var o=0,u=i.get("properties");o<u.length;o++){var c=u[o];if(c.isProperty()){var l=c.get("key"),p=c.isnt("computed")&&l.isIdentifier({name:s});if(p=p||l.isLiteral({value:s}))return c.get("value").resolve(e,t)}}else if(i.isArrayExpression()&&!isNaN(+s)){var d=i.get("elements")[s];if(d)return d.resolve(e,t)}}}},isConstantExpression:function(){if(this.isIdentifier()){var e=this.scope.getBinding(this.node.name);return!!e&&e.constant}return this.isLiteral()?!this.isRegExpLiteral()&&(!this.isTemplateLiteral()||this.get("expressions").every((function(e){return e.isConstantExpression()}))):this.isUnaryExpression()?"void"===this.get("operator").node&&this.get("argument").isConstantExpression():!!this.isBinaryExpression()&&(this.get("left").isConstantExpression()&&this.get("right").isConstantExpression())},isInStrictMode:function(){return!!(this.isProgram()?this:this.parentPath).find((function(e){if(e.isProgram({sourceType:"module"}))return!0;if(e.isClass())return!0;if(!e.isProgram()&&!e.isFunction())return!1;if(e.isArrowFunctionExpression()&&!e.get("body").isBlockStatement())return!1;var t=e.node;e.isFunction()&&(t=t.body);var r=t.directives,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}if("use strict"===s.value.value)return!0}}))}});var PE=Object.freeze({__proto__:null,call:function(e){var t=this.opts;return this.debug(e),!(!this.node||!this._call(t[e]))||!!this.node&&this._call(t[this.node.type]&&t[this.node.type][e])},_call:function(e){if(!e)return!1;var t=e,r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}var s=a;if(s){var i=this.node;if(!i)return!0;var o=s.call(this.state,this,this.state);if(o&&"object"==typeof o&&"function"==typeof o.then)throw new Error("You appear to be using a plugin with an async traversal visitor, which your current version of Babel does not support. If you're using a published plugin, you may need to upgrade your @babel/core version.");if(o)throw new Error("Unexpected return value from visitor method "+s);if(this.node!==i)return!0;if(this._traverseFlags>0)return!0}}return!1},isBlacklisted:function(){var e=this.opts.blacklist;return e&&e.indexOf(this.node.type)>-1},visit:function(){return!!this.node&&(!this.isBlacklisted()&&((!this.opts.shouldSkip||!this.opts.shouldSkip(this))&&(this.shouldSkip||this.call("enter")||this.shouldSkip?(this.debug("Skip..."),this.shouldStop):(this.debug("Recursing into..."),sA.node(this.node,this.opts,this.scope,this.state,this,this.skipKeys),this.call("exit"),this.shouldStop))))},skip:function(){this.shouldSkip=!0},skipKey:function(e){null==this.skipKeys&&(this.skipKeys={}),this.skipKeys[e]=!0},stop:function(){this._traverseFlags|=GE|UE},setScope:function(){if(!this.opts||!this.opts.noScope){for(var e,t=this.parentPath;t&&!e;){if(t.opts&&t.opts.noScope)return;e=t.scope,t=t.parentPath}this.scope=this.getScope(e),this.scope&&this.scope.init()}},setContext:function(e){return null!=this.skipKeys&&(this.skipKeys={}),this._traverseFlags=0,e&&(this.context=e,this.state=e.state,this.opts=e.opts),this.setScope(),this},resync:function(){this.removed||(this._resyncParent(),this._resyncList(),this._resyncKey())},_resyncParent:function(){this.parentPath&&(this.parent=this.parentPath.node)},_resyncKey:function(){if(this.container&&this.node!==this.container[this.key]){if(Array.isArray(this.container)){for(var e=0;e<this.container.length;e++)if(this.container[e]===this.node)return this.setKey(e)}else for(var t=0,r=Object.keys(this.container);t<r.length;t++){var n=r[t];if(this.container[n]===this.node)return this.setKey(n)}this.key=null}},_resyncList:function(){if(this.parent&&this.inList){var e=this.parent[this.listKey];this.container!==e&&(this.container=e||null)}},_resyncRemoved:function(){null!=this.key&&this.container&&this.container[this.key]===this.node||this._markRemoved()},popContext:function(){this.contexts.pop(),this.contexts.length>0?this.setContext(this.contexts[this.contexts.length-1]):this.setContext(void 0)},pushContext:function(e){this.contexts.push(e),this.setContext(e)},setup:function(e,t,r,n){this.listKey=r,this.container=t,this.parentPath=e||this.parentPath,this.setKey(n)},setKey:function(e){this.key=e,this.node=this.container[this.key],this.type=this.node&&this.node.type},requeue:function(e){if(void 0===e&&(e=this),!e.removed){var t=this.contexts,r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}a.maybeQueue(e)}}},_getQueueContexts:function(){for(var e=this,t=this.contexts;!t.length&&(e=e.parentPath);)t=e.contexts;return t}}),kE=[function(e,t){if("test"===e.key&&(t.isWhile()||t.isSwitchCase())||"declaration"===e.key&&t.isExportDeclaration()||"body"===e.key&&t.isLabeledStatement()||"declarations"===e.listKey&&t.isVariableDeclaration()&&1===t.node.declarations.length||"expression"===e.key&&t.isExpressionStatement())return t.remove(),!0},function(e,t){if(t.isSequenceExpression()&&1===t.node.expressions.length)return t.replaceWith(t.node.expressions[0]),!0},function(e,t){if(t.isBinary())return"left"===e.key?t.replaceWith(t.node.right):t.replaceWith(t.node.left),!0},function(e,t){if(t.isIfStatement()&&("consequent"===e.key||"alternate"===e.key)||"body"===e.key&&(t.isLoop()||t.isArrowFunctionExpression()))return e.replaceWith({type:"BlockStatement",body:[]}),!0}];var FE=Object.freeze({__proto__:null,remove:function(){this._assertUnremoved(),this.resync(),this.opts&&this.opts.noScope||this._removeFromScope(),this._callRemovalHooks()||(this.shareCommentsWithSiblings(),this._remove()),this._markRemoved()},_removeFromScope:function(){var e=this,t=this.getBindingIdentifiers();Object.keys(t).forEach((function(t){return e.scope.removeBinding(t)}))},_callRemovalHooks:function(){for(var e=0,t=kE;e<t.length;e++){if((0,t[e])(this,this.parentPath))return!0}},_remove:function(){Array.isArray(this.container)?(this.container.splice(this.key,1),this.updateSiblingKeys(this.key,-1)):this._replaceWith(null)},_markRemoved:function(){this._traverseFlags|=GE|LE,this.node=null},_assertUnremoved:function(){if(this.removed)throw this.buildCodeFrameError("NodePath has been removed so is read-only.")}}),_E={ReferencedIdentifier:function(e,t){if(!e.isJSXIdentifier()||!df.isCompatTag(e.node.name)||e.parentPath.isJSXMemberExpression()){if("this"===e.node.name){var r=e.scope;do{if(r.path.isFunction()&&!r.path.isArrowFunctionExpression())break}while(r=r.parent);r&&t.breakOnScopePaths.push(r.path)}var n=e.scope.getBinding(e.node.name);if(n){var a=n.constantViolations,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}if(o.scope!==n.path.scope)return t.mutableBinding=!0,void e.stop()}n===t.scope.getBinding(e.node.name)&&(t.bindings[e.node.name]=n)}}}},IE=function(){function e(e,t){this.breakOnScopePaths=[],this.bindings={},this.mutableBinding=!1,this.scopes=[],this.scope=t,this.path=e,this.attachAfter=!1}var t=e.prototype;return t.isCompatibleScope=function(e){for(var t=0,r=Object.keys(this.bindings);t<r.length;t++){var n=r[t],a=this.bindings[n];if(!e.bindingIdentifierEquals(n,a.identifier))return!1}return!0},t.getCompatibleScopes=function(){var e=this.path.scope;do{if(!this.isCompatibleScope(e))break;if(this.scopes.push(e),this.breakOnScopePaths.indexOf(e.path)>=0)break}while(e=e.parent)},t.getAttachmentPath=function(){var e=this._getAttachmentPath();if(e){var t=e.scope;if(t.path===e&&(t=e.scope.parent),t.path.isProgram()||t.path.isFunction())for(var r=0,n=Object.keys(this.bindings);r<n.length;r++){var a=n[r];if(t.hasOwnBinding(a)){var s=this.bindings[a];if("param"!==s.kind&&"params"!==s.path.parentKey)if(this.getAttachmentParentForPath(s.path).key>=e.key){this.attachAfter=!0,e=s.path;for(var i=0,o=s.constantViolations;i<o.length;i++){var u=o[i];this.getAttachmentParentForPath(u).key>e.key&&(e=u)}}}}return e}},t._getAttachmentPath=function(){var e=this.scopes.pop();if(e)if(e.path.isFunction()){if(!this.hasOwnParamBindings(e))return this.getNextScopeAttachmentParent();if(this.scope===e)return;for(var t=e.path.get("body").get("body"),r=0;r<t.length;r++)if(!t[r].node._blockHoist)return t[r]}else if(e.path.isProgram())return this.getNextScopeAttachmentParent()},t.getNextScopeAttachmentParent=function(){var e=this.scopes.pop();if(e)return this.getAttachmentParentForPath(e.path)},t.getAttachmentParentForPath=function(e){do{if(!e.parentPath||Array.isArray(e.container)&&e.isStatement())return e}while(e=e.parentPath)},t.hasOwnParamBindings=function(e){for(var t=0,r=Object.keys(this.bindings);t<r.length;t++){var n=r[t];if(e.hasOwnBinding(n)){var a=this.bindings[n];if("param"===a.kind&&a.constant)return!0}}return!1},t.run=function(){if(this.path.traverse(_E,this),!this.mutableBinding){this.getCompatibleScopes();var e=this.getAttachmentPath();if(e&&e.getFunctionParent()!==this.path.getFunctionParent()){var t=e.scope.generateUidIdentifier("ref"),r=Co(t,this.path.node),n=e[this.attachAfter?"insertAfter":"insertBefore"]([e.isVariableDeclarator()?r:Do("var",[r])])[0],a=this.path.parentPath;return a.isJSXElement()&&this.path.container===a.node.children&&(t=pc(t)),this.path.replaceWith(pp(t)),e.isVariableDeclarator()?n.get("init"):n.get("declarations.0.init")}}},e}();var BE=Object.freeze({__proto__:null,insertBefore:function(e){this._assertUnremoved(),e=this._verifyNodeList(e);var t=this.parentPath;if(t.isExpressionStatement()||t.isLabeledStatement()||t.isExportNamedDeclaration()||t.isExportDefaultDeclaration()&&this.isDeclaration())return t.insertBefore(e);if(this.isNodeType("Expression")&&!this.isJSXElement()||t.isForStatement()&&"init"===this.key)return this.node&&e.push(this.node),this.replaceExpressionWithStatements(e);if(Array.isArray(this.container))return this._containerInsertBefore(e);if(this.isStatementOrBlock()){var r=this.node&&(!this.isExpressionStatement()||null!=this.node.expression);return this.replaceWith(Ri(r?[this.node]:[])),this.unshiftContainer("body",e)}throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?")},_containerInsert:function(e,t){var r;this.updateSiblingKeys(e,t.length);var n=[];(r=this.container).splice.apply(r,[e,0].concat(t));for(var a=0;a<t.length;a++){var s=e+a,i=this.getSibling(s);n.push(i),this.context&&this.context.queue&&i.pushContext(this.context)}for(var o=this._getQueueContexts(),u=0,c=n;u<c.length;u++){var l=c[u];l.setScope(),l.debug("Inserted.");var p=o,d=Array.isArray(p),f=0;for(p=d?p:p[Symbol.iterator]();;){var h;if(d){if(f>=p.length)break;h=p[f++]}else{if((f=p.next()).done)break;h=f.value}h.maybeQueue(l,!0)}}return n},_containerInsertBefore:function(e){return this._containerInsert(this.key,e)},_containerInsertAfter:function(e){return this._containerInsert(this.key+1,e)},insertAfter:function(e){this._assertUnremoved(),e=this._verifyNodeList(e);var t=this.parentPath;if(t.isExpressionStatement()||t.isLabeledStatement()||t.isExportNamedDeclaration()||t.isExportDefaultDeclaration()&&this.isDeclaration())return t.insertAfter(e.map((function(e){return rt(e)?Ki(e):e})));if(this.isNodeType("Expression")&&!this.isJSXElement()&&!t.isJSXElement()||t.isForStatement()&&"init"===this.key){if(this.node){var r=this.scope;t.isMethod({computed:!0,key:this.node})&&(r=r.parent);var n=r.generateDeclaredUidIdentifier();e.unshift(Ki(_i("=",pp(n),this.node))),e.push(Ki(pp(n)))}return this.replaceExpressionWithStatements(e)}if(Array.isArray(this.container))return this._containerInsertAfter(e);if(this.isStatementOrBlock()){var a=this.node&&(!this.isExpressionStatement()||null!=this.node.expression);return this.replaceWith(Ri(a?[this.node]:[])),this.pushContainer("body",e)}throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?")},updateSiblingKeys:function(e,t){if(this.parent)for(var r=Qh.get(this.parent),n=0;n<r.length;n++){var a=r[n];a.key>=e&&(a.key+=t)}},_verifyNodeList:function(e){if(!e)return[];e.constructor!==Array&&(e=[e]);for(var t=0;t<e.length;t++){var r=e[t],n=void 0;if(r?"object"!=typeof r?n="contains a non-object node":r.type?r instanceof VE&&(n="has a NodePath when it expected a raw object"):n="without a type":n="has falsy node",n){var a=Array.isArray(r)?"array":typeof r;throw new Error("Node list "+n+" with the index of "+t+" and type of "+a)}}return e},unshiftContainer:function(e,t){return this._assertUnremoved(),t=this._verifyNodeList(t),VE.get({parentPath:this,parent:this.node,container:this.node[e],listKey:e,key:0})._containerInsertBefore(t)},pushContainer:function(e,t){this._assertUnremoved(),t=this._verifyNodeList(t);var r=this.node[e];return VE.get({parentPath:this,parent:this.node,container:r,listKey:e,key:r.length}).replaceWithMultiple(t)},hoist:function(e){return void 0===e&&(e=this.scope),new IE(this,e).run()}});function OE(e,t){return e?t.concat(e.getCompletionRecords()):t}var NE=Object.freeze({__proto__:null,getOpposite:function(){return"left"===this.key?this.getSibling("right"):"right"===this.key?this.getSibling("left"):void 0},getCompletionRecords:function(){var e=[];if(this.isIfStatement())e=OE(this.get("consequent"),e),e=OE(this.get("alternate"),e);else if(this.isDoExpression()||this.isFor()||this.isWhile())e=OE(this.get("body"),e);else if(this.isProgram()||this.isBlockStatement())e=OE(this.get("body").pop(),e);else{if(this.isFunction())return this.get("body").getCompletionRecords();this.isTryStatement()?(e=OE(this.get("block"),e),e=OE(this.get("handler"),e)):this.isCatchClause()?e=OE(this.get("body"),e):this.isSwitchStatement()?e=function(e,t){for(var r=!0,n=e.length-1;n>=0;n--){var a=e[n].get("consequent"),s=void 0;var i=a,o=Array.isArray(i),u=0;e:for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}var l=c;if(l.isBlockStatement()){var p=l.get("body"),d=Array.isArray(p),f=0;for(p=d?p:p[Symbol.iterator]();;){var h;if(d){if(f>=p.length)break;h=p[f++]}else{if((f=p.next()).done)break;h=f.value}var m=h;if(m.isBreakStatement()){s=m;break e}}}else if(l.isBreakStatement()){s=l;break}}if(s){for(;0===s.key&&s.parentPath.isBlockStatement();)s=s.parentPath;var y=s.getPrevSibling();s.key>0&&(y.isExpressionStatement()||y.isBlockStatement())?(t=OE(y,t),s.remove()):(s.replaceWith(s.scope.buildUndefinedNode()),t=OE(s,t))}else r&&a.some((function e(t){return!t.isBlockStatement()||t.get("body").some(e)}))&&(t=OE(a[a.length-1],t),r=!1)}return t}(this.get("cases"),e):e.push(this)}return e},getSibling:function(e){return VE.get({parentPath:this.parentPath,parent:this.parent,container:this.container,listKey:this.listKey,key:e})},getPrevSibling:function(){return this.getSibling(this.key-1)},getNextSibling:function(){return this.getSibling(this.key+1)},getAllNextSiblings:function(){for(var e=this.key,t=this.getSibling(++e),r=[];t.node;)r.push(t),t=this.getSibling(++e);return r},getAllPrevSiblings:function(){for(var e=this.key,t=this.getSibling(--e),r=[];t.node;)r.push(t),t=this.getSibling(--e);return r},get:function(e,t){!0===t&&(t=this.context);var r=e.split(".");return 1===r.length?this._getKey(e,t):this._getPattern(r,t)},_getKey:function(e,t){var r=this,n=this.node,a=n[e];return Array.isArray(a)?a.map((function(s,i){return VE.get({listKey:e,parentPath:r,parent:n,container:a,key:i}).setContext(t)})):VE.get({parentPath:this,parent:n,container:n,key:e}).setContext(t)},_getPattern:function(e,t){var r=this,n=e,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;r="."===o?r.parentPath:Array.isArray(r)?r[o]:r.get(o,t)}return r},getBindingIdentifiers:function(e){return Ud(this.node,e)},getOuterBindingIdentifiers:function(e){return tf(this.node,e)},getBindingIdentifierPaths:function(e,t){void 0===e&&(e=!1),void 0===t&&(t=!1);for(var r=[].concat(this),n=Object.create(null);r.length;){var a=r.shift();if(a&&a.node){var s=Ud.keys[a.node.type];if(a.isIdentifier())e?(n[a.node.name]=n[a.node.name]||[]).push(a):n[a.node.name]=a;else if(a.isExportDeclaration()){var i=a.get("declaration");i.isDeclaration()&&r.push(i)}else{if(t){if(a.isFunctionDeclaration()){r.push(a.get("id"));continue}if(a.isFunctionExpression())continue}if(s)for(var o=0;o<s.length;o++){var u=s[o],c=a.get(u);(Array.isArray(c)||c.node)&&(r=r.concat(c))}}}}return n},getOuterBindingIdentifierPaths:function(e){return this.getBindingIdentifierPaths(e,!0)}});var RE=Object.freeze({__proto__:null,shareCommentsWithSiblings:function(){if("string"!=typeof this.key){var e=this.node;if(e){var t=e.trailingComments,r=e.leadingComments;if(t||r){var n=this.getSibling(this.key-1),a=this.getSibling(this.key+1),s=Boolean(n.node),i=Boolean(a.node);s&&!i?n.addComments("trailing",t):i&&!s&&a.addComments("leading",r)}}}},addComment:function(e,t,r){hp(this.node,e,t,r)},addComments:function(e,t){fp(this.node,e,t)}}),ME=_f("babel"),LE=1,UE=2,GE=4,VE=function(){function e(e,t){this.parent=t,this.hub=e,this.contexts=[],this.data=null,this._traverseFlags=0,this.state=null,this.opts=null,this.skipKeys=null,this.parentPath=null,this.context=null,this.container=null,this.listKey=null,this.key=null,this.node=null,this.scope=null,this.type=null}e.get=function(t){var r=t.hub,n=t.parentPath,a=t.parent,s=t.container,i=t.listKey,o=t.key;if(!r&&n&&(r=n.hub),!a)throw new Error("To get a node path the parent needs to exist");var u,c=s[o],l=Qh.get(a)||[];Qh.has(a)||Qh.set(a,l);for(var p=0;p<l.length;p++){var d=l[p];if(d.node===c){u=d;break}}return u||(u=new e(r,a),l.push(u)),u.setup(n,s,i,o),u};var t=e.prototype;return t.getScope=function(e){return this.isScope()?new sm(this):e},t.setData=function(e,t){return null==this.data&&(this.data=Object.create(null)),this.data[e]=t},t.getData=function(e,t){null==this.data&&(this.data=Object.create(null));var r=this.data[e];return void 0===r&&void 0!==t&&(r=this.data[e]=t),r},t.buildCodeFrameError=function(e,t){return void 0===t&&(t=SyntaxError),this.hub.buildError(this.node,e,t)},t.traverse=function(e,t){sA(this.node,e,this.scope,t,this)},t.set=function(e,t){Ts(this.node,e,t),this.node[e]=t},t.getPathLocation=function(){var e=[],t=this;do{var r=t.key;t.inList&&(r=t.listKey+"["+r+"]"),e.unshift(r)}while(t=t.parentPath);return e.join(".")},t.debug=function(e){ME.enabled&&ME(this.getPathLocation()+" "+this.type+": "+e)},t.toString=function(){return lv(this.node).code},n(e,[{key:"inList",get:function(){return!!this.listKey},set:function(e){e||(this.listKey=null)}},{key:"parentKey",get:function(){return this.listKey||this.key}},{key:"shouldSkip",get:function(){return!!(this._traverseFlags&GE)},set:function(e){e?this._traverseFlags|=GE:this._traverseFlags&=~GE}},{key:"shouldStop",get:function(){return!!(this._traverseFlags&UE)},set:function(e){e?this._traverseFlags|=UE:this._traverseFlags&=~UE}},{key:"removed",get:function(){return!!(this._traverseFlags&LE)},set:function(e){e?this._traverseFlags|=LE:this._traverseFlags&=~LE}}]),e}();Object.assign(VE.prototype,pv,Dv,Nx,Vx,EE,jE,PE,FE,BE,NE,RE);for(var WE=function(){var e=qE[HE],t="is"+e,r=ff[t];VE.prototype[t]=function(e){return r(this.node,e)},VE.prototype["assert"+e]=function(t){if(!r(this.node,t))throw new TypeError("Expected node path of type "+e)}},HE=0,qE=Pi;HE<qE.length;HE++)WE();for(var KE=function(){var e=XE[zE];if("_"===e[0])return"continue";Pi.indexOf(e)<0&&Pi.push(e);var t=Sf[e];VE.prototype["is"+e]=function(e){return t.checkPath(this,e)}},zE=0,XE=Object.keys(Sf);zE<XE.length;zE++)KE();var YE=function(){function e(e,t,r,n){this.queue=null,this.parentPath=n,this.scope=e,this.state=r,this.opts=t}var t=e.prototype;return t.shouldVisit=function(e){var t=this.opts;if(t.enter||t.exit)return!0;if(t[e.type])return!0;var r=ks[e.type];if(!r||!r.length)return!1;var n=r,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}if(e[i])return!0}return!1},t.create=function(e,t,r,n){return VE.get({parentPath:this.parentPath,parent:e,container:t,key:r,listKey:n})},t.maybeQueue=function(e,t){if(this.trap)throw new Error("Infinite cycle detected");this.queue&&(t?this.queue.push(e):this.priorityQueue.push(e))},t.visitMultiple=function(e,t,r){if(0===e.length)return!1;for(var n=[],a=0;a<e.length;a++){var s=e[a];s&&this.shouldVisit(s)&&n.push(this.create(t,e,a,r))}return this.visitQueue(n)},t.visitSingle=function(e,t){return!!this.shouldVisit(e[t])&&this.visitQueue([this.create(e,e,t)])},t.visitQueue=function(e){this.queue=e,this.priorityQueue=[];var t=[],r=!1,n=e,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;if(o.resync(),0!==o.contexts.length&&o.contexts[o.contexts.length-1]===this||o.pushContext(this),null!==o.key&&!(t.indexOf(o.node)>=0)){if(t.push(o.node),o.visit()){r=!0;break}if(this.priorityQueue.length&&(r=this.visitQueue(this.priorityQueue),this.priorityQueue=[],this.queue=e,r))break}}var u=e,c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}p.popContext()}return this.queue=null,r},t.visit=function(e,t){var r=e[t];return!!r&&(Array.isArray(r)?this.visitMultiple(r,e,t):this.visitSingle(e,t))},e}();function JE(e){if(e._exploded)return e;e._exploded=!0;for(var t=0,r=Object.keys(e);t<r.length;t++){var n=r[t];if(!rA(n)){var a=n.split("|");if(1!==a.length){var s=e[n];delete e[n];var i=a,o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}e[c]=s}}}}$E(e),delete e.__esModule,function(e){for(var t=0,r=Object.keys(e);t<r.length;t++){var n=r[t];if(!rA(n)){var a=e[n];"function"==typeof a&&(e[n]={enter:a})}}}(e),eA(e);for(var l=0,p=Object.keys(e);l<p.length;l++){var d=p[l];if(!rA(d)){var f=Sf[d];if(f){for(var h=e[d],m=0,y=Object.keys(h);m<y.length;m++){var g=y[m];h[g]=tA(f,h[g])}if(delete e[d],f.types)for(var v=0,b=f.types;v<b.length;v++){var x=b[v];e[x]?nA(e[x],h):e[x]=h}else nA(e,h)}}}for(var E=0,A=Object.keys(e);E<A.length;E++){var w=A[E];if(!rA(w)){var S=e[w],D=_s[w],C=Os[w];if(C&&(console.trace("Visitor defined for "+w+" but it has been renamed to "+C),D=[C]),D){delete e[w];var T=D,j=Array.isArray(T),P=0;for(T=j?T:T[Symbol.iterator]();;){var k;if(j){if(P>=T.length)break;k=T[P++]}else{if((P=T.next()).done)break;k=P.value}var F=k,_=e[F];_?nA(_,S):e[F]=Da(S)}}}}for(var I=0,B=Object.keys(e);I<B.length;I++){var O=B[I];rA(O)||eA(e[O])}return e}function $E(e){if(!e._verified){if("function"==typeof e)throw new Error("You passed `traverse()` a function when it expected a visitor object, are you sure you didn't mean `{ enter: Function }`?");for(var t=0,r=Object.keys(e);t<r.length;t++){var n=r[t];if("enter"!==n&&"exit"!==n||QE(n,e[n]),!rA(n)){if(Pi.indexOf(n)<0)throw new Error("You gave us a visitor for the node type "+n+" but it's not a valid type");var a=e[n];if("object"==typeof a)for(var s=0,i=Object.keys(a);s<i.length;s++){var o=i[s];if("enter"!==o&&"exit"!==o)throw new Error("You passed `traverse()` a visitor object with the property "+n+" that has the invalid property "+o);QE(n+"."+o,a[o])}}}e._verified=!0}}function QE(e,t){var r=[].concat(t),n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}if("function"!=typeof s)throw new TypeError("Non-function found defined in "+e+" with type "+typeof s)}}function ZE(e,t,r){for(var n={},a=function(){var a=i[s],o=e[a];if(!Array.isArray(o))return"continue";o=o.map((function(e){var n=e;return t&&(n=function(r){return e.call(t,r,t)}),r&&(n=r(t.key,a,n)),n!==e&&(n.toString=function(){return e.toString()}),n})),n[a]=o},s=0,i=Object.keys(e);s<i.length;s++)a();return n}function eA(e){e.enter&&!Array.isArray(e.enter)&&(e.enter=[e.enter]),e.exit&&!Array.isArray(e.exit)&&(e.exit=[e.exit])}function tA(e,t){var r=function(r){if(e.checkPath(r))return t.apply(this,arguments)};return r.toString=function(){return t.toString()},r}function rA(e){return"_"===e[0]||("enter"===e||"exit"===e||"shouldSkip"===e||("blacklist"===e||"noScope"===e||"skipKeys"===e))}function nA(e,t){for(var r=0,n=Object.keys(t);r<n.length;r++){var a=n[r];e[a]=[].concat(e[a]||[],t[a])}}var aA=Object.freeze({__proto__:null,explode:JE,verify:$E,merge:function(e,t,r){void 0===t&&(t=[]);for(var n={},a=0;a<e.length;a++){var s=e[a],i=t[a];JE(s);for(var o=0,u=Object.keys(s);o<u.length;o++){var c=u[o],l=s[c];(i||r)&&(l=ZE(l,i,r)),nA(n[c]=n[c]||{},l)}}return n}});function sA(e,t,r,n,a){if(e){if(t||(t={}),!t.noScope&&!r&&"Program"!==e.type&&"File"!==e.type)throw new Error("You must pass a scope and parentPath unless traversing a Program/File. Instead of that you tried to traverse a "+e.type+" node without passing scope and parentPath.");ks[e.type]&&(JE(t),sA.node(e,t,r,n,a))}}function iA(e,t){e.node.type===t.type&&(t.has=!0,e.stop())}function oA(){var e=i(['\n import wrapNativeSuper from "wrapNativeSuper";\n import getPrototypeOf from "getPrototypeOf";\n import possibleConstructorReturn from "possibleConstructorReturn";\n import inherits from "inherits";\n\n export default function _wrapRegExp(re, groups) {\n _wrapRegExp = function(re, groups) {\n return new BabelRegExp(re, undefined, groups);\n };\n\n var _RegExp = wrapNativeSuper(RegExp);\n var _super = RegExp.prototype;\n var _groups = new WeakMap();\n\n function BabelRegExp(re, flags, groups) {\n var _this = _RegExp.call(this, re, flags);\n // if the regex is recreated with \'g\' flag\n _groups.set(_this, groups || _groups.get(re));\n return _this;\n }\n inherits(BabelRegExp, _RegExp);\n\n BabelRegExp.prototype.exec = function(str) {\n var result = _super.exec.call(this, str);\n if (result) result.groups = buildGroups(result, this);\n return result;\n };\n BabelRegExp.prototype[Symbol.replace] = function(str, substitution) {\n if (typeof substitution === "string") {\n var groups = _groups.get(this);\n return _super[Symbol.replace].call(\n this,\n str,\n substitution.replace(/\\$<([^>]+)>/g, function(_, name) {\n return "$" + groups[name];\n })\n );\n } else if (typeof substitution === "function") {\n var _this = this;\n return _super[Symbol.replace].call(\n this,\n str,\n function() {\n var args = [];\n args.push.apply(args, arguments);\n if (typeof args[args.length - 1] !== "object") {\n // Modern engines already pass result.groups as the last arg.\n args.push(buildGroups(args, _this));\n }\n return substitution.apply(this, args);\n }\n );\n } else {\n return _super[Symbol.replace].call(this, str, substitution);\n }\n }\n\n function buildGroups(result, re) {\n // NOTE: This function should return undefined if there are no groups,\n // but in that case Babel doesn\'t add the wrapper anyway.\n\n var g = _groups.get(re);\n return Object.keys(g).reduce(function(groups, name) {\n groups[name] = result[g[name]];\n return groups;\n }, Object.create(null));\n }\n\n return _wrapRegExp.apply(this, arguments);\n }\n'],['\n import wrapNativeSuper from "wrapNativeSuper";\n import getPrototypeOf from "getPrototypeOf";\n import possibleConstructorReturn from "possibleConstructorReturn";\n import inherits from "inherits";\n\n export default function _wrapRegExp(re, groups) {\n _wrapRegExp = function(re, groups) {\n return new BabelRegExp(re, undefined, groups);\n };\n\n var _RegExp = wrapNativeSuper(RegExp);\n var _super = RegExp.prototype;\n var _groups = new WeakMap();\n\n function BabelRegExp(re, flags, groups) {\n var _this = _RegExp.call(this, re, flags);\n // if the regex is recreated with \'g\' flag\n _groups.set(_this, groups || _groups.get(re));\n return _this;\n }\n inherits(BabelRegExp, _RegExp);\n\n BabelRegExp.prototype.exec = function(str) {\n var result = _super.exec.call(this, str);\n if (result) result.groups = buildGroups(result, this);\n return result;\n };\n BabelRegExp.prototype[Symbol.replace] = function(str, substitution) {\n if (typeof substitution === "string") {\n var groups = _groups.get(this);\n return _super[Symbol.replace].call(\n this,\n str,\n substitution.replace(/\\\\$<([^>]+)>/g, function(_, name) {\n return "$" + groups[name];\n })\n );\n } else if (typeof substitution === "function") {\n var _this = this;\n return _super[Symbol.replace].call(\n this,\n str,\n function() {\n var args = [];\n args.push.apply(args, arguments);\n if (typeof args[args.length - 1] !== "object") {\n // Modern engines already pass result.groups as the last arg.\n args.push(buildGroups(args, _this));\n }\n return substitution.apply(this, args);\n }\n );\n } else {\n return _super[Symbol.replace].call(this, str, substitution);\n }\n }\n\n function buildGroups(result, re) {\n // NOTE: This function should return undefined if there are no groups,\n // but in that case Babel doesn\'t add the wrapper anyway.\n\n var g = _groups.get(re);\n return Object.keys(g).reduce(function(groups, name) {\n groups[name] = result[g[name]];\n return groups;\n }, Object.create(null));\n }\n\n return _wrapRegExp.apply(this, arguments);\n }\n']);return oA=function(){return e},e}function uA(){var e=i(['\n export default function _classPrivateMethodSet() {\n throw new TypeError("attempted to reassign private method");\n }\n']);return uA=function(){return e},e}function cA(){var e=i(['\n export default function _classPrivateMethodGet(receiver, privateSet, fn) {\n if (!privateSet.has(receiver)) {\n throw new TypeError("attempted to get private field on non-instance");\n }\n return fn;\n }\n']);return cA=function(){return e},e}function lA(){var e=i(['\n import toArray from "toArray";\n import toPropertyKey from "toPropertyKey";\n\n // These comments are stripped by @babel/template\n /*::\n type PropertyDescriptor =\n | {\n value: any,\n writable: boolean,\n configurable: boolean,\n enumerable: boolean,\n }\n | {\n get?: () => any,\n set?: (v: any) => void,\n configurable: boolean,\n enumerable: boolean,\n };\n\n type FieldDescriptor ={\n writable: boolean,\n configurable: boolean,\n enumerable: boolean,\n };\n\n type Placement = "static" | "prototype" | "own";\n type Key = string | symbol; // PrivateName is not supported yet.\n\n type ElementDescriptor =\n | {\n kind: "method",\n key: Key,\n placement: Placement,\n descriptor: PropertyDescriptor\n }\n | {\n kind: "field",\n key: Key,\n placement: Placement,\n descriptor: FieldDescriptor,\n initializer?: () => any,\n };\n\n // This is exposed to the user code\n type ElementObjectInput = ElementDescriptor & {\n [@@toStringTag]?: "Descriptor"\n };\n\n // This is exposed to the user code\n type ElementObjectOutput = ElementDescriptor & {\n [@@toStringTag]?: "Descriptor"\n extras?: ElementDescriptor[],\n finisher?: ClassFinisher,\n };\n\n // This is exposed to the user code\n type ClassObject = {\n [@@toStringTag]?: "Descriptor",\n kind: "class",\n elements: ElementDescriptor[],\n };\n\n type ElementDecorator = (descriptor: ElementObjectInput) => ?ElementObjectOutput;\n type ClassDecorator = (descriptor: ClassObject) => ?ClassObject;\n type ClassFinisher = <A, B>(cl: Class<A>) => Class<B>;\n\n // Only used by Babel in the transform output, not part of the spec.\n type ElementDefinition =\n | {\n kind: "method",\n value: any,\n key: Key,\n static?: boolean,\n decorators?: ElementDecorator[],\n }\n | {\n kind: "field",\n value: () => any,\n key: Key,\n static?: boolean,\n decorators?: ElementDecorator[],\n };\n\n declare function ClassFactory<C>(initialize: (instance: C) => void): {\n F: Class<C>,\n d: ElementDefinition[]\n }\n\n */\n\n /*::\n // Various combinations with/without extras and with one or many finishers\n\n type ElementFinisherExtras = {\n element: ElementDescriptor,\n finisher?: ClassFinisher,\n extras?: ElementDescriptor[],\n };\n\n type ElementFinishersExtras = {\n element: ElementDescriptor,\n finishers: ClassFinisher[],\n extras: ElementDescriptor[],\n };\n\n type ElementsFinisher = {\n elements: ElementDescriptor[],\n finisher?: ClassFinisher,\n };\n\n type ElementsFinishers = {\n elements: ElementDescriptor[],\n finishers: ClassFinisher[],\n };\n\n */\n\n /*::\n\n type Placements = {\n static: Key[],\n prototype: Key[],\n own: Key[],\n };\n\n */\n\n // ClassDefinitionEvaluation (Steps 26-*)\n export default function _decorate(\n decorators /*: ClassDecorator[] */,\n factory /*: ClassFactory */,\n superClass /*: ?Class<*> */,\n mixins /*: ?Array<Function> */,\n ) /*: Class<*> */ {\n var api = _getDecoratorsApi();\n if (mixins) {\n for (var i = 0; i < mixins.length; i++) {\n api = mixins[i](api);\n }\n }\n\n var r = factory(function initialize(O) {\n api.initializeInstanceElements(O, decorated.elements);\n }, superClass);\n var decorated = api.decorateClass(\n _coalesceClassElements(r.d.map(_createElementDescriptor)),\n decorators,\n );\n\n api.initializeClassElements(r.F, decorated.elements);\n\n return api.runClassFinishers(r.F, decorated.finishers);\n }\n\n function _getDecoratorsApi() {\n _getDecoratorsApi = function() {\n return api;\n };\n\n var api = {\n elementsDefinitionOrder: [["method"], ["field"]],\n\n // InitializeInstanceElements\n initializeInstanceElements: function(\n /*::<C>*/ O /*: C */,\n elements /*: ElementDescriptor[] */,\n ) {\n ["method", "field"].forEach(function(kind) {\n elements.forEach(function(element /*: ElementDescriptor */) {\n if (element.kind === kind && element.placement === "own") {\n this.defineClassElement(O, element);\n }\n }, this);\n }, this);\n },\n\n // InitializeClassElements\n initializeClassElements: function(\n /*::<C>*/ F /*: Class<C> */,\n elements /*: ElementDescriptor[] */,\n ) {\n var proto = F.prototype;\n\n ["method", "field"].forEach(function(kind) {\n elements.forEach(function(element /*: ElementDescriptor */) {\n var placement = element.placement;\n if (\n element.kind === kind &&\n (placement === "static" || placement === "prototype")\n ) {\n var receiver = placement === "static" ? F : proto;\n this.defineClassElement(receiver, element);\n }\n }, this);\n }, this);\n },\n\n // DefineClassElement\n defineClassElement: function(\n /*::<C>*/ receiver /*: C | Class<C> */,\n element /*: ElementDescriptor */,\n ) {\n var descriptor /*: PropertyDescriptor */ = element.descriptor;\n if (element.kind === "field") {\n var initializer = element.initializer;\n descriptor = {\n enumerable: descriptor.enumerable,\n writable: descriptor.writable,\n configurable: descriptor.configurable,\n value: initializer === void 0 ? void 0 : initializer.call(receiver),\n };\n }\n Object.defineProperty(receiver, element.key, descriptor);\n },\n\n // DecorateClass\n decorateClass: function(\n elements /*: ElementDescriptor[] */,\n decorators /*: ClassDecorator[] */,\n ) /*: ElementsFinishers */ {\n var newElements /*: ElementDescriptor[] */ = [];\n var finishers /*: ClassFinisher[] */ = [];\n var placements /*: Placements */ = {\n static: [],\n prototype: [],\n own: [],\n };\n\n elements.forEach(function(element /*: ElementDescriptor */) {\n this.addElementPlacement(element, placements);\n }, this);\n\n elements.forEach(function(element /*: ElementDescriptor */) {\n if (!_hasDecorators(element)) return newElements.push(element);\n\n var elementFinishersExtras /*: ElementFinishersExtras */ = this.decorateElement(\n element,\n placements,\n );\n newElements.push(elementFinishersExtras.element);\n newElements.push.apply(newElements, elementFinishersExtras.extras);\n finishers.push.apply(finishers, elementFinishersExtras.finishers);\n }, this);\n\n if (!decorators) {\n return { elements: newElements, finishers: finishers };\n }\n\n var result /*: ElementsFinishers */ = this.decorateConstructor(\n newElements,\n decorators,\n );\n finishers.push.apply(finishers, result.finishers);\n result.finishers = finishers;\n\n return result;\n },\n\n // AddElementPlacement\n addElementPlacement: function(\n element /*: ElementDescriptor */,\n placements /*: Placements */,\n silent /*: boolean */,\n ) {\n var keys = placements[element.placement];\n if (!silent && keys.indexOf(element.key) !== -1) {\n throw new TypeError("Duplicated element (" + element.key + ")");\n }\n keys.push(element.key);\n },\n\n // DecorateElement\n decorateElement: function(\n element /*: ElementDescriptor */,\n placements /*: Placements */,\n ) /*: ElementFinishersExtras */ {\n var extras /*: ElementDescriptor[] */ = [];\n var finishers /*: ClassFinisher[] */ = [];\n\n for (\n var decorators = element.decorators, i = decorators.length - 1;\n i >= 0;\n i--\n ) {\n // (inlined) RemoveElementPlacement\n var keys = placements[element.placement];\n keys.splice(keys.indexOf(element.key), 1);\n\n var elementObject /*: ElementObjectInput */ = this.fromElementDescriptor(\n element,\n );\n var elementFinisherExtras /*: ElementFinisherExtras */ = this.toElementFinisherExtras(\n (0, decorators[i])(elementObject) /*: ElementObjectOutput */ ||\n elementObject,\n );\n\n element = elementFinisherExtras.element;\n this.addElementPlacement(element, placements);\n\n if (elementFinisherExtras.finisher) {\n finishers.push(elementFinisherExtras.finisher);\n }\n\n var newExtras /*: ElementDescriptor[] | void */ =\n elementFinisherExtras.extras;\n if (newExtras) {\n for (var j = 0; j < newExtras.length; j++) {\n this.addElementPlacement(newExtras[j], placements);\n }\n extras.push.apply(extras, newExtras);\n }\n }\n\n return { element: element, finishers: finishers, extras: extras };\n },\n\n // DecorateConstructor\n decorateConstructor: function(\n elements /*: ElementDescriptor[] */,\n decorators /*: ClassDecorator[] */,\n ) /*: ElementsFinishers */ {\n var finishers /*: ClassFinisher[] */ = [];\n\n for (var i = decorators.length - 1; i >= 0; i--) {\n var obj /*: ClassObject */ = this.fromClassDescriptor(elements);\n var elementsAndFinisher /*: ElementsFinisher */ = this.toClassDescriptor(\n (0, decorators[i])(obj) /*: ClassObject */ || obj,\n );\n\n if (elementsAndFinisher.finisher !== undefined) {\n finishers.push(elementsAndFinisher.finisher);\n }\n\n if (elementsAndFinisher.elements !== undefined) {\n elements = elementsAndFinisher.elements;\n\n for (var j = 0; j < elements.length - 1; j++) {\n for (var k = j + 1; k < elements.length; k++) {\n if (\n elements[j].key === elements[k].key &&\n elements[j].placement === elements[k].placement\n ) {\n throw new TypeError(\n "Duplicated element (" + elements[j].key + ")",\n );\n }\n }\n }\n }\n }\n\n return { elements: elements, finishers: finishers };\n },\n\n // FromElementDescriptor\n fromElementDescriptor: function(\n element /*: ElementDescriptor */,\n ) /*: ElementObject */ {\n var obj /*: ElementObject */ = {\n kind: element.kind,\n key: element.key,\n placement: element.placement,\n descriptor: element.descriptor,\n };\n\n var desc = {\n value: "Descriptor",\n configurable: true,\n };\n Object.defineProperty(obj, Symbol.toStringTag, desc);\n\n if (element.kind === "field") obj.initializer = element.initializer;\n\n return obj;\n },\n\n // ToElementDescriptors\n toElementDescriptors: function(\n elementObjects /*: ElementObject[] */,\n ) /*: ElementDescriptor[] */ {\n if (elementObjects === undefined) return;\n return toArray(elementObjects).map(function(elementObject) {\n var element = this.toElementDescriptor(elementObject);\n this.disallowProperty(elementObject, "finisher", "An element descriptor");\n this.disallowProperty(elementObject, "extras", "An element descriptor");\n return element;\n }, this);\n },\n\n // ToElementDescriptor\n toElementDescriptor: function(\n elementObject /*: ElementObject */,\n ) /*: ElementDescriptor */ {\n var kind = String(elementObject.kind);\n if (kind !== "method" && kind !== "field") {\n throw new TypeError(\n \'An element descriptor\\\'s .kind property must be either "method" or\' +\n \' "field", but a decorator created an element descriptor with\' +\n \' .kind "\' +\n kind +\n \'"\',\n );\n }\n\n var key = toPropertyKey(elementObject.key);\n\n var placement = String(elementObject.placement);\n if (\n placement !== "static" &&\n placement !== "prototype" &&\n placement !== "own"\n ) {\n throw new TypeError(\n \'An element descriptor\\\'s .placement property must be one of "static",\' +\n \' "prototype" or "own", but a decorator created an element descriptor\' +\n \' with .placement "\' +\n placement +\n \'"\',\n );\n }\n\n var descriptor /*: PropertyDescriptor */ = elementObject.descriptor;\n\n this.disallowProperty(elementObject, "elements", "An element descriptor");\n\n var element /*: ElementDescriptor */ = {\n kind: kind,\n key: key,\n placement: placement,\n descriptor: Object.assign({}, descriptor),\n };\n\n if (kind !== "field") {\n this.disallowProperty(elementObject, "initializer", "A method descriptor");\n } else {\n this.disallowProperty(\n descriptor,\n "get",\n "The property descriptor of a field descriptor",\n );\n this.disallowProperty(\n descriptor,\n "set",\n "The property descriptor of a field descriptor",\n );\n this.disallowProperty(\n descriptor,\n "value",\n "The property descriptor of a field descriptor",\n );\n\n element.initializer = elementObject.initializer;\n }\n\n return element;\n },\n\n toElementFinisherExtras: function(\n elementObject /*: ElementObject */,\n ) /*: ElementFinisherExtras */ {\n var element /*: ElementDescriptor */ = this.toElementDescriptor(\n elementObject,\n );\n var finisher /*: ClassFinisher */ = _optionalCallableProperty(\n elementObject,\n "finisher",\n );\n var extras /*: ElementDescriptors[] */ = this.toElementDescriptors(\n elementObject.extras,\n );\n\n return { element: element, finisher: finisher, extras: extras };\n },\n\n // FromClassDescriptor\n fromClassDescriptor: function(\n elements /*: ElementDescriptor[] */,\n ) /*: ClassObject */ {\n var obj = {\n kind: "class",\n elements: elements.map(this.fromElementDescriptor, this),\n };\n\n var desc = { value: "Descriptor", configurable: true };\n Object.defineProperty(obj, Symbol.toStringTag, desc);\n\n return obj;\n },\n\n // ToClassDescriptor\n toClassDescriptor: function(\n obj /*: ClassObject */,\n ) /*: ElementsFinisher */ {\n var kind = String(obj.kind);\n if (kind !== "class") {\n throw new TypeError(\n \'A class descriptor\\\'s .kind property must be "class", but a decorator\' +\n \' created a class descriptor with .kind "\' +\n kind +\n \'"\',\n );\n }\n\n this.disallowProperty(obj, "key", "A class descriptor");\n this.disallowProperty(obj, "placement", "A class descriptor");\n this.disallowProperty(obj, "descriptor", "A class descriptor");\n this.disallowProperty(obj, "initializer", "A class descriptor");\n this.disallowProperty(obj, "extras", "A class descriptor");\n\n var finisher = _optionalCallableProperty(obj, "finisher");\n var elements = this.toElementDescriptors(obj.elements);\n\n return { elements: elements, finisher: finisher };\n },\n\n // RunClassFinishers\n runClassFinishers: function(\n constructor /*: Class<*> */,\n finishers /*: ClassFinisher[] */,\n ) /*: Class<*> */ {\n for (var i = 0; i < finishers.length; i++) {\n var newConstructor /*: ?Class<*> */ = (0, finishers[i])(constructor);\n if (newConstructor !== undefined) {\n // NOTE: This should check if IsConstructor(newConstructor) is false.\n if (typeof newConstructor !== "function") {\n throw new TypeError("Finishers must return a constructor.");\n }\n constructor = newConstructor;\n }\n }\n return constructor;\n },\n\n disallowProperty: function(obj, name, objectType) {\n if (obj[name] !== undefined) {\n throw new TypeError(objectType + " can\'t have a ." + name + " property.");\n }\n }\n };\n\n return api;\n }\n\n // ClassElementEvaluation\n function _createElementDescriptor(\n def /*: ElementDefinition */,\n ) /*: ElementDescriptor */ {\n var key = toPropertyKey(def.key);\n\n var descriptor /*: PropertyDescriptor */;\n if (def.kind === "method") {\n descriptor = {\n value: def.value,\n writable: true,\n configurable: true,\n enumerable: false,\n };\n } else if (def.kind === "get") {\n descriptor = { get: def.value, configurable: true, enumerable: false };\n } else if (def.kind === "set") {\n descriptor = { set: def.value, configurable: true, enumerable: false };\n } else if (def.kind === "field") {\n descriptor = { configurable: true, writable: true, enumerable: true };\n }\n\n var element /*: ElementDescriptor */ = {\n kind: def.kind === "field" ? "field" : "method",\n key: key,\n placement: def.static\n ? "static"\n : def.kind === "field"\n ? "own"\n : "prototype",\n descriptor: descriptor,\n };\n if (def.decorators) element.decorators = def.decorators;\n if (def.kind === "field") element.initializer = def.value;\n\n return element;\n }\n\n // CoalesceGetterSetter\n function _coalesceGetterSetter(\n element /*: ElementDescriptor */,\n other /*: ElementDescriptor */,\n ) {\n if (element.descriptor.get !== undefined) {\n other.descriptor.get = element.descriptor.get;\n } else {\n other.descriptor.set = element.descriptor.set;\n }\n }\n\n // CoalesceClassElements\n function _coalesceClassElements(\n elements /*: ElementDescriptor[] */,\n ) /*: ElementDescriptor[] */ {\n var newElements /*: ElementDescriptor[] */ = [];\n\n var isSameElement = function(\n other /*: ElementDescriptor */,\n ) /*: boolean */ {\n return (\n other.kind === "method" &&\n other.key === element.key &&\n other.placement === element.placement\n );\n };\n\n for (var i = 0; i < elements.length; i++) {\n var element /*: ElementDescriptor */ = elements[i];\n var other /*: ElementDescriptor */;\n\n if (\n element.kind === "method" &&\n (other = newElements.find(isSameElement))\n ) {\n if (\n _isDataDescriptor(element.descriptor) ||\n _isDataDescriptor(other.descriptor)\n ) {\n if (_hasDecorators(element) || _hasDecorators(other)) {\n throw new ReferenceError(\n "Duplicated methods (" + element.key + ") can\'t be decorated.",\n );\n }\n other.descriptor = element.descriptor;\n } else {\n if (_hasDecorators(element)) {\n if (_hasDecorators(other)) {\n throw new ReferenceError(\n "Decorators can\'t be placed on different accessors with for " +\n "the same property (" +\n element.key +\n ").",\n );\n }\n other.decorators = element.decorators;\n }\n _coalesceGetterSetter(element, other);\n }\n } else {\n newElements.push(element);\n }\n }\n\n return newElements;\n }\n\n function _hasDecorators(element /*: ElementDescriptor */) /*: boolean */ {\n return element.decorators && element.decorators.length;\n }\n\n function _isDataDescriptor(desc /*: PropertyDescriptor */) /*: boolean */ {\n return (\n desc !== undefined &&\n !(desc.value === undefined && desc.writable === undefined)\n );\n }\n\n function _optionalCallableProperty /*::<T>*/(\n obj /*: T */,\n name /*: $Keys<T> */,\n ) /*: ?Function */ {\n var value = obj[name];\n if (value !== undefined && typeof value !== "function") {\n throw new TypeError("Expected \'" + name + "\' to be a function");\n }\n return value;\n }\n\n'],['\n import toArray from "toArray";\n import toPropertyKey from "toPropertyKey";\n\n // These comments are stripped by @babel/template\n /*::\n type PropertyDescriptor =\n | {\n value: any,\n writable: boolean,\n configurable: boolean,\n enumerable: boolean,\n }\n | {\n get?: () => any,\n set?: (v: any) => void,\n configurable: boolean,\n enumerable: boolean,\n };\n\n type FieldDescriptor ={\n writable: boolean,\n configurable: boolean,\n enumerable: boolean,\n };\n\n type Placement = "static" | "prototype" | "own";\n type Key = string | symbol; // PrivateName is not supported yet.\n\n type ElementDescriptor =\n | {\n kind: "method",\n key: Key,\n placement: Placement,\n descriptor: PropertyDescriptor\n }\n | {\n kind: "field",\n key: Key,\n placement: Placement,\n descriptor: FieldDescriptor,\n initializer?: () => any,\n };\n\n // This is exposed to the user code\n type ElementObjectInput = ElementDescriptor & {\n [@@toStringTag]?: "Descriptor"\n };\n\n // This is exposed to the user code\n type ElementObjectOutput = ElementDescriptor & {\n [@@toStringTag]?: "Descriptor"\n extras?: ElementDescriptor[],\n finisher?: ClassFinisher,\n };\n\n // This is exposed to the user code\n type ClassObject = {\n [@@toStringTag]?: "Descriptor",\n kind: "class",\n elements: ElementDescriptor[],\n };\n\n type ElementDecorator = (descriptor: ElementObjectInput) => ?ElementObjectOutput;\n type ClassDecorator = (descriptor: ClassObject) => ?ClassObject;\n type ClassFinisher = <A, B>(cl: Class<A>) => Class<B>;\n\n // Only used by Babel in the transform output, not part of the spec.\n type ElementDefinition =\n | {\n kind: "method",\n value: any,\n key: Key,\n static?: boolean,\n decorators?: ElementDecorator[],\n }\n | {\n kind: "field",\n value: () => any,\n key: Key,\n static?: boolean,\n decorators?: ElementDecorator[],\n };\n\n declare function ClassFactory<C>(initialize: (instance: C) => void): {\n F: Class<C>,\n d: ElementDefinition[]\n }\n\n */\n\n /*::\n // Various combinations with/without extras and with one or many finishers\n\n type ElementFinisherExtras = {\n element: ElementDescriptor,\n finisher?: ClassFinisher,\n extras?: ElementDescriptor[],\n };\n\n type ElementFinishersExtras = {\n element: ElementDescriptor,\n finishers: ClassFinisher[],\n extras: ElementDescriptor[],\n };\n\n type ElementsFinisher = {\n elements: ElementDescriptor[],\n finisher?: ClassFinisher,\n };\n\n type ElementsFinishers = {\n elements: ElementDescriptor[],\n finishers: ClassFinisher[],\n };\n\n */\n\n /*::\n\n type Placements = {\n static: Key[],\n prototype: Key[],\n own: Key[],\n };\n\n */\n\n // ClassDefinitionEvaluation (Steps 26-*)\n export default function _decorate(\n decorators /*: ClassDecorator[] */,\n factory /*: ClassFactory */,\n superClass /*: ?Class<*> */,\n mixins /*: ?Array<Function> */,\n ) /*: Class<*> */ {\n var api = _getDecoratorsApi();\n if (mixins) {\n for (var i = 0; i < mixins.length; i++) {\n api = mixins[i](api);\n }\n }\n\n var r = factory(function initialize(O) {\n api.initializeInstanceElements(O, decorated.elements);\n }, superClass);\n var decorated = api.decorateClass(\n _coalesceClassElements(r.d.map(_createElementDescriptor)),\n decorators,\n );\n\n api.initializeClassElements(r.F, decorated.elements);\n\n return api.runClassFinishers(r.F, decorated.finishers);\n }\n\n function _getDecoratorsApi() {\n _getDecoratorsApi = function() {\n return api;\n };\n\n var api = {\n elementsDefinitionOrder: [["method"], ["field"]],\n\n // InitializeInstanceElements\n initializeInstanceElements: function(\n /*::<C>*/ O /*: C */,\n elements /*: ElementDescriptor[] */,\n ) {\n ["method", "field"].forEach(function(kind) {\n elements.forEach(function(element /*: ElementDescriptor */) {\n if (element.kind === kind && element.placement === "own") {\n this.defineClassElement(O, element);\n }\n }, this);\n }, this);\n },\n\n // InitializeClassElements\n initializeClassElements: function(\n /*::<C>*/ F /*: Class<C> */,\n elements /*: ElementDescriptor[] */,\n ) {\n var proto = F.prototype;\n\n ["method", "field"].forEach(function(kind) {\n elements.forEach(function(element /*: ElementDescriptor */) {\n var placement = element.placement;\n if (\n element.kind === kind &&\n (placement === "static" || placement === "prototype")\n ) {\n var receiver = placement === "static" ? F : proto;\n this.defineClassElement(receiver, element);\n }\n }, this);\n }, this);\n },\n\n // DefineClassElement\n defineClassElement: function(\n /*::<C>*/ receiver /*: C | Class<C> */,\n element /*: ElementDescriptor */,\n ) {\n var descriptor /*: PropertyDescriptor */ = element.descriptor;\n if (element.kind === "field") {\n var initializer = element.initializer;\n descriptor = {\n enumerable: descriptor.enumerable,\n writable: descriptor.writable,\n configurable: descriptor.configurable,\n value: initializer === void 0 ? void 0 : initializer.call(receiver),\n };\n }\n Object.defineProperty(receiver, element.key, descriptor);\n },\n\n // DecorateClass\n decorateClass: function(\n elements /*: ElementDescriptor[] */,\n decorators /*: ClassDecorator[] */,\n ) /*: ElementsFinishers */ {\n var newElements /*: ElementDescriptor[] */ = [];\n var finishers /*: ClassFinisher[] */ = [];\n var placements /*: Placements */ = {\n static: [],\n prototype: [],\n own: [],\n };\n\n elements.forEach(function(element /*: ElementDescriptor */) {\n this.addElementPlacement(element, placements);\n }, this);\n\n elements.forEach(function(element /*: ElementDescriptor */) {\n if (!_hasDecorators(element)) return newElements.push(element);\n\n var elementFinishersExtras /*: ElementFinishersExtras */ = this.decorateElement(\n element,\n placements,\n );\n newElements.push(elementFinishersExtras.element);\n newElements.push.apply(newElements, elementFinishersExtras.extras);\n finishers.push.apply(finishers, elementFinishersExtras.finishers);\n }, this);\n\n if (!decorators) {\n return { elements: newElements, finishers: finishers };\n }\n\n var result /*: ElementsFinishers */ = this.decorateConstructor(\n newElements,\n decorators,\n );\n finishers.push.apply(finishers, result.finishers);\n result.finishers = finishers;\n\n return result;\n },\n\n // AddElementPlacement\n addElementPlacement: function(\n element /*: ElementDescriptor */,\n placements /*: Placements */,\n silent /*: boolean */,\n ) {\n var keys = placements[element.placement];\n if (!silent && keys.indexOf(element.key) !== -1) {\n throw new TypeError("Duplicated element (" + element.key + ")");\n }\n keys.push(element.key);\n },\n\n // DecorateElement\n decorateElement: function(\n element /*: ElementDescriptor */,\n placements /*: Placements */,\n ) /*: ElementFinishersExtras */ {\n var extras /*: ElementDescriptor[] */ = [];\n var finishers /*: ClassFinisher[] */ = [];\n\n for (\n var decorators = element.decorators, i = decorators.length - 1;\n i >= 0;\n i--\n ) {\n // (inlined) RemoveElementPlacement\n var keys = placements[element.placement];\n keys.splice(keys.indexOf(element.key), 1);\n\n var elementObject /*: ElementObjectInput */ = this.fromElementDescriptor(\n element,\n );\n var elementFinisherExtras /*: ElementFinisherExtras */ = this.toElementFinisherExtras(\n (0, decorators[i])(elementObject) /*: ElementObjectOutput */ ||\n elementObject,\n );\n\n element = elementFinisherExtras.element;\n this.addElementPlacement(element, placements);\n\n if (elementFinisherExtras.finisher) {\n finishers.push(elementFinisherExtras.finisher);\n }\n\n var newExtras /*: ElementDescriptor[] | void */ =\n elementFinisherExtras.extras;\n if (newExtras) {\n for (var j = 0; j < newExtras.length; j++) {\n this.addElementPlacement(newExtras[j], placements);\n }\n extras.push.apply(extras, newExtras);\n }\n }\n\n return { element: element, finishers: finishers, extras: extras };\n },\n\n // DecorateConstructor\n decorateConstructor: function(\n elements /*: ElementDescriptor[] */,\n decorators /*: ClassDecorator[] */,\n ) /*: ElementsFinishers */ {\n var finishers /*: ClassFinisher[] */ = [];\n\n for (var i = decorators.length - 1; i >= 0; i--) {\n var obj /*: ClassObject */ = this.fromClassDescriptor(elements);\n var elementsAndFinisher /*: ElementsFinisher */ = this.toClassDescriptor(\n (0, decorators[i])(obj) /*: ClassObject */ || obj,\n );\n\n if (elementsAndFinisher.finisher !== undefined) {\n finishers.push(elementsAndFinisher.finisher);\n }\n\n if (elementsAndFinisher.elements !== undefined) {\n elements = elementsAndFinisher.elements;\n\n for (var j = 0; j < elements.length - 1; j++) {\n for (var k = j + 1; k < elements.length; k++) {\n if (\n elements[j].key === elements[k].key &&\n elements[j].placement === elements[k].placement\n ) {\n throw new TypeError(\n "Duplicated element (" + elements[j].key + ")",\n );\n }\n }\n }\n }\n }\n\n return { elements: elements, finishers: finishers };\n },\n\n // FromElementDescriptor\n fromElementDescriptor: function(\n element /*: ElementDescriptor */,\n ) /*: ElementObject */ {\n var obj /*: ElementObject */ = {\n kind: element.kind,\n key: element.key,\n placement: element.placement,\n descriptor: element.descriptor,\n };\n\n var desc = {\n value: "Descriptor",\n configurable: true,\n };\n Object.defineProperty(obj, Symbol.toStringTag, desc);\n\n if (element.kind === "field") obj.initializer = element.initializer;\n\n return obj;\n },\n\n // ToElementDescriptors\n toElementDescriptors: function(\n elementObjects /*: ElementObject[] */,\n ) /*: ElementDescriptor[] */ {\n if (elementObjects === undefined) return;\n return toArray(elementObjects).map(function(elementObject) {\n var element = this.toElementDescriptor(elementObject);\n this.disallowProperty(elementObject, "finisher", "An element descriptor");\n this.disallowProperty(elementObject, "extras", "An element descriptor");\n return element;\n }, this);\n },\n\n // ToElementDescriptor\n toElementDescriptor: function(\n elementObject /*: ElementObject */,\n ) /*: ElementDescriptor */ {\n var kind = String(elementObject.kind);\n if (kind !== "method" && kind !== "field") {\n throw new TypeError(\n \'An element descriptor\\\\\'s .kind property must be either "method" or\' +\n \' "field", but a decorator created an element descriptor with\' +\n \' .kind "\' +\n kind +\n \'"\',\n );\n }\n\n var key = toPropertyKey(elementObject.key);\n\n var placement = String(elementObject.placement);\n if (\n placement !== "static" &&\n placement !== "prototype" &&\n placement !== "own"\n ) {\n throw new TypeError(\n \'An element descriptor\\\\\'s .placement property must be one of "static",\' +\n \' "prototype" or "own", but a decorator created an element descriptor\' +\n \' with .placement "\' +\n placement +\n \'"\',\n );\n }\n\n var descriptor /*: PropertyDescriptor */ = elementObject.descriptor;\n\n this.disallowProperty(elementObject, "elements", "An element descriptor");\n\n var element /*: ElementDescriptor */ = {\n kind: kind,\n key: key,\n placement: placement,\n descriptor: Object.assign({}, descriptor),\n };\n\n if (kind !== "field") {\n this.disallowProperty(elementObject, "initializer", "A method descriptor");\n } else {\n this.disallowProperty(\n descriptor,\n "get",\n "The property descriptor of a field descriptor",\n );\n this.disallowProperty(\n descriptor,\n "set",\n "The property descriptor of a field descriptor",\n );\n this.disallowProperty(\n descriptor,\n "value",\n "The property descriptor of a field descriptor",\n );\n\n element.initializer = elementObject.initializer;\n }\n\n return element;\n },\n\n toElementFinisherExtras: function(\n elementObject /*: ElementObject */,\n ) /*: ElementFinisherExtras */ {\n var element /*: ElementDescriptor */ = this.toElementDescriptor(\n elementObject,\n );\n var finisher /*: ClassFinisher */ = _optionalCallableProperty(\n elementObject,\n "finisher",\n );\n var extras /*: ElementDescriptors[] */ = this.toElementDescriptors(\n elementObject.extras,\n );\n\n return { element: element, finisher: finisher, extras: extras };\n },\n\n // FromClassDescriptor\n fromClassDescriptor: function(\n elements /*: ElementDescriptor[] */,\n ) /*: ClassObject */ {\n var obj = {\n kind: "class",\n elements: elements.map(this.fromElementDescriptor, this),\n };\n\n var desc = { value: "Descriptor", configurable: true };\n Object.defineProperty(obj, Symbol.toStringTag, desc);\n\n return obj;\n },\n\n // ToClassDescriptor\n toClassDescriptor: function(\n obj /*: ClassObject */,\n ) /*: ElementsFinisher */ {\n var kind = String(obj.kind);\n if (kind !== "class") {\n throw new TypeError(\n \'A class descriptor\\\\\'s .kind property must be "class", but a decorator\' +\n \' created a class descriptor with .kind "\' +\n kind +\n \'"\',\n );\n }\n\n this.disallowProperty(obj, "key", "A class descriptor");\n this.disallowProperty(obj, "placement", "A class descriptor");\n this.disallowProperty(obj, "descriptor", "A class descriptor");\n this.disallowProperty(obj, "initializer", "A class descriptor");\n this.disallowProperty(obj, "extras", "A class descriptor");\n\n var finisher = _optionalCallableProperty(obj, "finisher");\n var elements = this.toElementDescriptors(obj.elements);\n\n return { elements: elements, finisher: finisher };\n },\n\n // RunClassFinishers\n runClassFinishers: function(\n constructor /*: Class<*> */,\n finishers /*: ClassFinisher[] */,\n ) /*: Class<*> */ {\n for (var i = 0; i < finishers.length; i++) {\n var newConstructor /*: ?Class<*> */ = (0, finishers[i])(constructor);\n if (newConstructor !== undefined) {\n // NOTE: This should check if IsConstructor(newConstructor) is false.\n if (typeof newConstructor !== "function") {\n throw new TypeError("Finishers must return a constructor.");\n }\n constructor = newConstructor;\n }\n }\n return constructor;\n },\n\n disallowProperty: function(obj, name, objectType) {\n if (obj[name] !== undefined) {\n throw new TypeError(objectType + " can\'t have a ." + name + " property.");\n }\n }\n };\n\n return api;\n }\n\n // ClassElementEvaluation\n function _createElementDescriptor(\n def /*: ElementDefinition */,\n ) /*: ElementDescriptor */ {\n var key = toPropertyKey(def.key);\n\n var descriptor /*: PropertyDescriptor */;\n if (def.kind === "method") {\n descriptor = {\n value: def.value,\n writable: true,\n configurable: true,\n enumerable: false,\n };\n } else if (def.kind === "get") {\n descriptor = { get: def.value, configurable: true, enumerable: false };\n } else if (def.kind === "set") {\n descriptor = { set: def.value, configurable: true, enumerable: false };\n } else if (def.kind === "field") {\n descriptor = { configurable: true, writable: true, enumerable: true };\n }\n\n var element /*: ElementDescriptor */ = {\n kind: def.kind === "field" ? "field" : "method",\n key: key,\n placement: def.static\n ? "static"\n : def.kind === "field"\n ? "own"\n : "prototype",\n descriptor: descriptor,\n };\n if (def.decorators) element.decorators = def.decorators;\n if (def.kind === "field") element.initializer = def.value;\n\n return element;\n }\n\n // CoalesceGetterSetter\n function _coalesceGetterSetter(\n element /*: ElementDescriptor */,\n other /*: ElementDescriptor */,\n ) {\n if (element.descriptor.get !== undefined) {\n other.descriptor.get = element.descriptor.get;\n } else {\n other.descriptor.set = element.descriptor.set;\n }\n }\n\n // CoalesceClassElements\n function _coalesceClassElements(\n elements /*: ElementDescriptor[] */,\n ) /*: ElementDescriptor[] */ {\n var newElements /*: ElementDescriptor[] */ = [];\n\n var isSameElement = function(\n other /*: ElementDescriptor */,\n ) /*: boolean */ {\n return (\n other.kind === "method" &&\n other.key === element.key &&\n other.placement === element.placement\n );\n };\n\n for (var i = 0; i < elements.length; i++) {\n var element /*: ElementDescriptor */ = elements[i];\n var other /*: ElementDescriptor */;\n\n if (\n element.kind === "method" &&\n (other = newElements.find(isSameElement))\n ) {\n if (\n _isDataDescriptor(element.descriptor) ||\n _isDataDescriptor(other.descriptor)\n ) {\n if (_hasDecorators(element) || _hasDecorators(other)) {\n throw new ReferenceError(\n "Duplicated methods (" + element.key + ") can\'t be decorated.",\n );\n }\n other.descriptor = element.descriptor;\n } else {\n if (_hasDecorators(element)) {\n if (_hasDecorators(other)) {\n throw new ReferenceError(\n "Decorators can\'t be placed on different accessors with for " +\n "the same property (" +\n element.key +\n ").",\n );\n }\n other.decorators = element.decorators;\n }\n _coalesceGetterSetter(element, other);\n }\n } else {\n newElements.push(element);\n }\n }\n\n return newElements;\n }\n\n function _hasDecorators(element /*: ElementDescriptor */) /*: boolean */ {\n return element.decorators && element.decorators.length;\n }\n\n function _isDataDescriptor(desc /*: PropertyDescriptor */) /*: boolean */ {\n return (\n desc !== undefined &&\n !(desc.value === undefined && desc.writable === undefined)\n );\n }\n\n function _optionalCallableProperty /*::<T>*/(\n obj /*: T */,\n name /*: $Keys<T> */,\n ) /*: ?Function */ {\n var value = obj[name];\n if (value !== undefined && typeof value !== "function") {\n throw new TypeError("Expected \'" + name + "\' to be a function");\n }\n return value;\n }\n\n']);return lA=function(){return e},e}function pA(){var e=i(['\n export default function _classStaticPrivateMethodSet() {\n throw new TypeError("attempted to set read only static private field");\n }\n']);return pA=function(){return e},e}function dA(){var e=i(['\n export default function _classStaticPrivateMethodGet(receiver, classConstructor, method) {\n if (receiver !== classConstructor) {\n throw new TypeError("Private static access of wrong provenance");\n }\n return method;\n }\n']);return dA=function(){return e},e}function fA(){var e=i(['\n export default function _classStaticPrivateFieldSpecSet(receiver, classConstructor, descriptor, value) {\n if (receiver !== classConstructor) {\n throw new TypeError("Private static access of wrong provenance");\n }\n if (descriptor.set) {\n descriptor.set.call(receiver, value);\n } else {\n if (!descriptor.writable) {\n // This should only throw in strict mode, but class bodies are\n // always strict and private fields can only be used inside\n // class bodies.\n throw new TypeError("attempted to set read only private field");\n }\n descriptor.value = value;\n }\n\n return value;\n }\n']);return fA=function(){return e},e}function hA(){var e=i(['\n export default function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) {\n if (receiver !== classConstructor) {\n throw new TypeError("Private static access of wrong provenance");\n }\n if (descriptor.get) {\n return descriptor.get.call(receiver);\n }\n return descriptor.value;\n }\n']);return hA=function(){return e},e}function mA(){var e=i(['\n export default function _classPrivateFieldDestructureSet(receiver, privateMap) {\n if (!privateMap.has(receiver)) {\n throw new TypeError("attempted to set private field on non-instance");\n }\n var descriptor = privateMap.get(receiver);\n if (descriptor.set) {\n if (!("__destrObj" in descriptor)) {\n descriptor.__destrObj = {\n set value(v) {\n descriptor.set.call(receiver, v)\n },\n };\n }\n return descriptor.__destrObj;\n } else {\n if (!descriptor.writable) {\n // This should only throw in strict mode, but class bodies are\n // always strict and private fields can only be used inside\n // class bodies.\n throw new TypeError("attempted to set read only private field");\n }\n\n return descriptor;\n }\n }\n']);return mA=function(){return e},e}function yA(){var e=i(['\n export default function _classPrivateFieldSet(receiver, privateMap, value) {\n var descriptor = privateMap.get(receiver);\n if (!descriptor) {\n throw new TypeError("attempted to set private field on non-instance");\n }\n if (descriptor.set) {\n descriptor.set.call(receiver, value);\n } else {\n if (!descriptor.writable) {\n // This should only throw in strict mode, but class bodies are\n // always strict and private fields can only be used inside\n // class bodies.\n throw new TypeError("attempted to set read only private field");\n }\n\n descriptor.value = value;\n }\n\n return value;\n }\n']);return yA=function(){return e},e}function gA(){var e=i(['\n export default function _classPrivateFieldGet(receiver, privateMap) {\n var descriptor = privateMap.get(receiver);\n if (!descriptor) {\n throw new TypeError("attempted to get private field on non-instance");\n }\n if (descriptor.get) {\n return descriptor.get.call(receiver);\n }\n return descriptor.value;\n }\n']);return gA=function(){return e},e}function vA(){var e=i(['\n export default function _classPrivateFieldBase(receiver, privateKey) {\n if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {\n throw new TypeError("attempted to use private field on non-instance");\n }\n return receiver;\n }\n']);return vA=function(){return e},e}function bA(){var e=i(['\n var id = 0;\n export default function _classPrivateFieldKey(name) {\n return "__private_" + (id++) + "_" + name;\n }\n']);return bA=function(){return e},e}function xA(){var e=i(["\n export default function _applyDecoratedDescriptor(target, property, decorators, descriptor, context){\n var desc = {};\n Object.keys(descriptor).forEach(function(key){\n desc[key] = descriptor[key];\n });\n desc.enumerable = !!desc.enumerable;\n desc.configurable = !!desc.configurable;\n if ('value' in desc || desc.initializer){\n desc.writable = true;\n }\n\n desc = decorators.slice().reverse().reduce(function(desc, decorator){\n return decorator(target, property, desc) || desc;\n }, desc);\n\n if (context && desc.initializer !== void 0){\n desc.value = desc.initializer ? desc.initializer.call(context) : void 0;\n desc.initializer = undefined;\n }\n\n if (desc.initializer === void 0){\n // This is a hack to avoid this being processed by 'transform-runtime'.\n // See issue #9.\n Object.defineProperty(target, property, desc);\n desc = null;\n }\n\n return desc;\n }\n"]);return xA=function(){return e},e}function EA(){var e=i(["\n export default function _initializerDefineProperty(target, property, descriptor, context){\n if (!descriptor) return;\n\n Object.defineProperty(target, property, {\n enumerable: descriptor.enumerable,\n configurable: descriptor.configurable,\n writable: descriptor.writable,\n value: descriptor.initializer ? descriptor.initializer.call(context) : void 0,\n });\n }\n"]);return EA=function(){return e},e}function AA(){var e=i(["\n export default function _initializerWarningHelper(descriptor, context){\n throw new Error(\n 'Decorating class property failed. Please ensure that ' +\n 'proposal-class-properties is enabled and runs after the decorators transform.'\n );\n }\n"]);return AA=function(){return e},e}function wA(){var e=i(['\n import toPrimitive from "toPrimitive";\n\n export default function _toPropertyKey(arg) {\n var key = toPrimitive(arg, "string");\n return typeof key === "symbol" ? key : String(key);\n }\n']);return wA=function(){return e},e}function SA(){var e=i(['\n export default function _toPrimitive(\n input,\n hint /*: "default" | "string" | "number" | void */\n ) {\n if (typeof input !== "object" || input === null) return input;\n var prim = input[Symbol.toPrimitive];\n if (prim !== undefined) {\n var res = prim.call(input, hint || "default");\n if (typeof res !== "object") return res;\n throw new TypeError("@@toPrimitive must return a primitive value.");\n }\n return (hint === "string" ? String : Number)(input);\n }\n']);return SA=function(){return e},e}function DA(){var e=i(["\n export default function _skipFirstGeneratorNext(fn) {\n return function () {\n var it = fn.apply(this, arguments);\n it.next();\n return it;\n }\n }\n"]);return DA=function(){return e},e}function CA(){var e=i(['\n export default function _nonIterableRest() {\n throw new TypeError("Invalid attempt to destructure non-iterable instance");\n }\n']);return CA=function(){return e},e}function TA(){var e=i(['\n export default function _nonIterableSpread() {\n throw new TypeError("Invalid attempt to spread non-iterable instance");\n }\n']);return TA=function(){return e},e}function jA(){var e=i(['\n export default function _iterableToArrayLimitLoose(arr, i) {\n if (!(\n Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]"\n )) { return }\n var _arr = [];\n for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) {\n _arr.push(_step.value);\n if (i && _arr.length === i) break;\n }\n return _arr;\n }\n']);return jA=function(){return e},e}function PA(){var e=i(['\n export default function _iterableToArrayLimit(arr, i) {\n // this is an expanded form of `for...of` that properly supports abrupt completions of\n // iterators etc. variable names have been minimised to reduce the size of this massive\n // helper. sometimes spec compliance is annoying :(\n //\n // _n = _iteratorNormalCompletion\n // _d = _didIteratorError\n // _e = _iteratorError\n // _i = _iterator\n // _s = _step\n if (!(\n Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]"\n )) { return }\n var _arr = [];\n var _n = true;\n var _d = false;\n var _e = undefined;\n try {\n for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {\n _arr.push(_s.value);\n if (i && _arr.length === i) break;\n }\n } catch (err) {\n _d = true;\n _e = err;\n } finally {\n try {\n if (!_n && _i["return"] != null) _i["return"]();\n } finally {\n if (_d) throw _e;\n }\n }\n return _arr;\n }\n'],['\n export default function _iterableToArrayLimit(arr, i) {\n // this is an expanded form of \\`for...of\\` that properly supports abrupt completions of\n // iterators etc. variable names have been minimised to reduce the size of this massive\n // helper. sometimes spec compliance is annoying :(\n //\n // _n = _iteratorNormalCompletion\n // _d = _didIteratorError\n // _e = _iteratorError\n // _i = _iterator\n // _s = _step\n if (!(\n Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]"\n )) { return }\n var _arr = [];\n var _n = true;\n var _d = false;\n var _e = undefined;\n try {\n for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {\n _arr.push(_s.value);\n if (i && _arr.length === i) break;\n }\n } catch (err) {\n _d = true;\n _e = err;\n } finally {\n try {\n if (!_n && _i["return"] != null) _i["return"]();\n } finally {\n if (_d) throw _e;\n }\n }\n return _arr;\n }\n']);return PA=function(){return e},e}function kA(){var e=i(['\n export default function _iterableToArray(iter) {\n if (\n Symbol.iterator in Object(iter) ||\n Object.prototype.toString.call(iter) === "[object Arguments]"\n ) return Array.from(iter);\n }\n']);return kA=function(){return e},e}function FA(){var e=i(["\n export default function _arrayWithHoles(arr) {\n if (Array.isArray(arr)) return arr;\n }\n"]);return FA=function(){return e},e}function _A(){var e=i(["\n export default function _arrayWithoutHoles(arr) {\n if (Array.isArray(arr)) {\n for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) arr2[i] = arr[i];\n return arr2;\n }\n }\n"]);return _A=function(){return e},e}function IA(){var e=i(['\n import arrayWithoutHoles from "arrayWithoutHoles";\n import iterableToArray from "iterableToArray";\n import nonIterableSpread from "nonIterableSpread";\n\n export default function _toConsumableArray(arr) {\n return arrayWithoutHoles(arr) || iterableToArray(arr) || nonIterableSpread();\n }\n']);return IA=function(){return e},e}function BA(){var e=i(['\n import arrayWithHoles from "arrayWithHoles";\n import iterableToArray from "iterableToArray";\n import nonIterableRest from "nonIterableRest";\n\n export default function _toArray(arr) {\n return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest();\n }\n']);return BA=function(){return e},e}function OA(){var e=i(['\n import arrayWithHoles from "arrayWithHoles";\n import iterableToArrayLimitLoose from "iterableToArrayLimitLoose";\n import nonIterableRest from "nonIterableRest";\n\n export default function _slicedToArrayLoose(arr, i) {\n return arrayWithHoles(arr) || iterableToArrayLimitLoose(arr, i) || nonIterableRest();\n }\n']);return OA=function(){return e},e}function NA(){var e=i(['\n import arrayWithHoles from "arrayWithHoles";\n import iterableToArrayLimit from "iterableToArrayLimit";\n import nonIterableRest from "nonIterableRest";\n\n export default function _slicedToArray(arr, i) {\n return arrayWithHoles(arr) || iterableToArrayLimit(arr, i) || nonIterableRest();\n }\n']);return NA=function(){return e},e}function RA(){var e=i(['\n import undef from "temporalUndefined";\n import err from "tdz";\n\n export default function _temporalRef(val, name) {\n return val === undef ? err(name) : val;\n }\n']);return RA=function(){return e},e}function MA(){var e=i(['\n export default function _tdzError(name) {\n throw new ReferenceError(name + " is not defined - temporal dead zone");\n }\n']);return MA=function(){return e},e}function LA(){var e=i(["\n // This function isn't mean to be called, but to be used as a reference.\n // We can't use a normal object because it isn't hoisted.\n export default function _temporalUndefined() {}\n"]);return LA=function(){return e},e}function UA(){var e=i(['\n export default function _classNameTDZError(name) {\n throw new Error("Class \\"" + name + "\\" cannot be referenced in computed property keys.");\n }\n'],['\n export default function _classNameTDZError(name) {\n throw new Error("Class \\\\"" + name + "\\\\" cannot be referenced in computed property keys.");\n }\n']);return UA=function(){return e},e}function GA(){var e=i(['\n export default function _readOnlyError(name) {\n throw new Error("\\"" + name + "\\" is read-only");\n }\n'],['\n export default function _readOnlyError(name) {\n throw new Error("\\\\"" + name + "\\\\" is read-only");\n }\n']);return GA=function(){return e},e}function VA(){var e=i(["\n export default function _taggedTemplateLiteralLoose(strings, raw) {\n if (!raw) { raw = strings.slice(0); }\n strings.raw = raw;\n return strings;\n }\n"]);return VA=function(){return e},e}function WA(){var e=i(["\n export default function _taggedTemplateLiteral(strings, raw) {\n if (!raw) { raw = strings.slice(0); }\n return Object.freeze(Object.defineProperties(strings, {\n raw: { value: Object.freeze(raw) }\n }));\n }\n"]);return WA=function(){return e},e}function HA(){var e=i(['\n import superPropBase from "superPropBase";\n import defineProperty from "defineProperty";\n\n function set(target, property, value, receiver) {\n if (typeof Reflect !== "undefined" && Reflect.set) {\n set = Reflect.set;\n } else {\n set = function set(target, property, value, receiver) {\n var base = superPropBase(target, property);\n var desc;\n\n if (base) {\n desc = Object.getOwnPropertyDescriptor(base, property);\n if (desc.set) {\n desc.set.call(receiver, value);\n return true;\n } else if (!desc.writable) {\n // Both getter and non-writable fall into this.\n return false;\n }\n }\n\n // Without a super that defines the property, spec boils down to\n // "define on receiver" for some reason.\n desc = Object.getOwnPropertyDescriptor(receiver, property);\n if (desc) {\n if (!desc.writable) {\n // Setter, getter, and non-writable fall into this.\n return false;\n }\n\n desc.value = value;\n Object.defineProperty(receiver, property, desc);\n } else {\n // Avoid setters that may be defined on Sub\'s prototype, but not on\n // the instance.\n defineProperty(receiver, property, value);\n }\n\n return true;\n };\n }\n\n return set(target, property, value, receiver);\n }\n\n export default function _set(target, property, value, receiver, isStrict) {\n var s = set(target, property, value, receiver || target);\n if (!s && isStrict) {\n throw new Error(\'failed to set property\');\n }\n\n return value;\n }\n']);return HA=function(){return e},e}function qA(){var e=i(['\n import superPropBase from "superPropBase";\n\n export default function _get(target, property, receiver) {\n if (typeof Reflect !== "undefined" && Reflect.get) {\n _get = Reflect.get;\n } else {\n _get = function _get(target, property, receiver) {\n var base = superPropBase(target, property);\n\n if (!base) return;\n\n var desc = Object.getOwnPropertyDescriptor(base, property);\n if (desc.get) {\n return desc.get.call(receiver);\n }\n\n return desc.value;\n };\n }\n return _get(target, property, receiver || target);\n }\n']);return qA=function(){return e},e}function KA(){var e=i(['\n import getPrototypeOf from "getPrototypeOf";\n\n export default function _superPropBase(object, property) {\n // Yes, this throws if object is null to being with, that\'s on purpose.\n while (!Object.prototype.hasOwnProperty.call(object, property)) {\n object = getPrototypeOf(object);\n if (object === null) break;\n }\n return object;\n }\n']);return KA=function(){return e},e}function zA(){var e=i(['\n import assertThisInitialized from "assertThisInitialized";\n\n export default function _possibleConstructorReturn(self, call) {\n if (call && (typeof call === "object" || typeof call === "function")) {\n return call;\n }\n return assertThisInitialized(self);\n }\n']);return zA=function(){return e},e}function XA(){var e=i(["\n export default function _assertThisInitialized(self) {\n if (self === void 0) {\n throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");\n }\n return self;\n }\n"]);return XA=function(){return e},e}function YA(){var e=i(['\n import objectWithoutPropertiesLoose from "objectWithoutPropertiesLoose";\n\n export default function _objectWithoutProperties(source, excluded) {\n if (source == null) return {};\n\n var target = objectWithoutPropertiesLoose(source, excluded);\n var key, i;\n\n if (Object.getOwnPropertySymbols) {\n var sourceSymbolKeys = Object.getOwnPropertySymbols(source);\n for (i = 0; i < sourceSymbolKeys.length; i++) {\n key = sourceSymbolKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;\n target[key] = source[key];\n }\n }\n\n return target;\n }\n']);return YA=function(){return e},e}function JA(){var e=i(["\n export default function _objectWithoutPropertiesLoose(source, excluded) {\n if (source == null) return {};\n\n var target = {};\n var sourceKeys = Object.keys(source);\n var key, i;\n\n for (i = 0; i < sourceKeys.length; i++) {\n key = sourceKeys[i];\n if (excluded.indexOf(key) >= 0) continue;\n target[key] = source[key];\n }\n\n return target;\n }\n"]);return JA=function(){return e},e}function $A(){var e=i(['\n export default function _objectDestructuringEmpty(obj) {\n if (obj == null) throw new TypeError("Cannot destructure undefined");\n }\n']);return $A=function(){return e},e}function QA(){var e=i(['\n export default function _newArrowCheck(innerThis, boundThis) {\n if (innerThis !== boundThis) {\n throw new TypeError("Cannot instantiate an arrow function");\n }\n }\n']);return QA=function(){return e},e}function ZA(){var e=i(['\n function _getRequireWildcardCache() {\n if (typeof WeakMap !== "function") return null;\n\n var cache = new WeakMap();\n _getRequireWildcardCache = function () { return cache; };\n return cache;\n }\n\n export default function _interopRequireWildcard(obj) {\n if (obj && obj.__esModule) {\n return obj;\n }\n\n if (obj === null || (typeof obj !== "object" && typeof obj !== "function")) {\n return { default: obj }\n }\n\n var cache = _getRequireWildcardCache();\n if (cache && cache.has(obj)) {\n return cache.get(obj);\n }\n\n var newObj = {};\n var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;\n for (var key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n var desc = hasPropertyDescriptor\n ? Object.getOwnPropertyDescriptor(obj, key)\n : null;\n if (desc && (desc.get || desc.set)) {\n Object.defineProperty(newObj, key, desc);\n } else {\n newObj[key] = obj[key];\n }\n }\n }\n newObj.default = obj;\n if (cache) {\n cache.set(obj, newObj);\n }\n return newObj;\n }\n']);return ZA=function(){return e},e}function ew(){var e=i(["\n export default function _interopRequireDefault(obj) {\n return obj && obj.__esModule ? obj : { default: obj };\n }\n"]);return ew=function(){return e},e}function tw(){var e=i(['\n export default function _instanceof(left, right) {\n if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {\n return !!right[Symbol.hasInstance](left);\n } else {\n return left instanceof right;\n }\n }\n']);return tw=function(){return e},e}function rw(){var e=i(['\n import getPrototypeOf from "getPrototypeOf";\n import setPrototypeOf from "setPrototypeOf";\n import isNativeFunction from "isNativeFunction";\n import construct from "construct";\n\n export default function _wrapNativeSuper(Class) {\n var _cache = typeof Map === "function" ? new Map() : undefined;\n\n _wrapNativeSuper = function _wrapNativeSuper(Class) {\n if (Class === null || !isNativeFunction(Class)) return Class;\n if (typeof Class !== "function") {\n throw new TypeError("Super expression must either be null or a function");\n }\n if (typeof _cache !== "undefined") {\n if (_cache.has(Class)) return _cache.get(Class);\n _cache.set(Class, Wrapper);\n }\n function Wrapper() {\n return construct(Class, arguments, getPrototypeOf(this).constructor)\n }\n Wrapper.prototype = Object.create(Class.prototype, {\n constructor: {\n value: Wrapper,\n enumerable: false,\n writable: true,\n configurable: true,\n }\n });\n\n return setPrototypeOf(Wrapper, Class);\n }\n\n return _wrapNativeSuper(Class)\n }\n']);return rw=function(){return e},e}function nw(){var e=i(['\n export default function _isNativeFunction(fn) {\n // Note: This function returns "true" for core-js functions.\n return Function.toString.call(fn).indexOf("[native code]") !== -1;\n }\n']);return nw=function(){return e},e}function aw(){var e=i(["\n import setPrototypeOf from \"setPrototypeOf\";\n\n function isNativeReflectConstruct() {\n if (typeof Reflect === \"undefined\" || !Reflect.construct) return false;\n\n // core-js@3\n if (Reflect.construct.sham) return false;\n\n // Proxy can't be polyfilled. Every browser implemented\n // proxies before or at the same time as Reflect.construct,\n // so if they support Proxy they also support Reflect.construct.\n if (typeof Proxy === \"function\") return true;\n\n // Since Reflect.construct can't be properly polyfilled, some\n // implementations (e.g. core-js@2) don't set the correct internal slots.\n // Those polyfills don't allow us to subclass built-ins, so we need to\n // use our fallback implementation.\n try {\n // If the internal slots aren't set, this throws an error similar to\n // TypeError: this is not a Date object.\n Date.prototype.toString.call(Reflect.construct(Date, [], function() {}));\n return true;\n } catch (e) {\n return false;\n }\n }\n\n export default function _construct(Parent, args, Class) {\n if (isNativeReflectConstruct()) {\n _construct = Reflect.construct;\n } else {\n // NOTE: If Parent !== Class, the correct __proto__ is set *after*\n // calling the constructor.\n _construct = function _construct(Parent, args, Class) {\n var a = [null];\n a.push.apply(a, args);\n var Constructor = Function.bind.apply(Parent, a);\n var instance = new Constructor();\n if (Class) setPrototypeOf(instance, Class.prototype);\n return instance;\n };\n }\n // Avoid issues with Class being present but undefined when it wasn't\n // present in the original call.\n return _construct.apply(null, arguments);\n }\n"]);return aw=function(){return e},e}function sw(){var e=i(["\n export default function _setPrototypeOf(o, p) {\n _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {\n o.__proto__ = p;\n return o;\n };\n return _setPrototypeOf(o, p);\n }\n"]);return sw=function(){return e},e}function iw(){var e=i(["\n export default function _getPrototypeOf(o) {\n _getPrototypeOf = Object.setPrototypeOf\n ? Object.getPrototypeOf\n : function _getPrototypeOf(o) {\n return o.__proto__ || Object.getPrototypeOf(o);\n };\n return _getPrototypeOf(o);\n }\n"]);return iw=function(){return e},e}function ow(){var e=i(["\n export default function _inheritsLoose(subClass, superClass) {\n subClass.prototype = Object.create(superClass.prototype);\n subClass.prototype.constructor = subClass;\n subClass.__proto__ = superClass;\n }\n"]);return ow=function(){return e},e}function uw(){var e=i(['\n import setPrototypeOf from "setPrototypeOf";\n\n export default function _inherits(subClass, superClass) {\n if (typeof superClass !== "function" && superClass !== null) {\n throw new TypeError("Super expression must either be null or a function");\n }\n subClass.prototype = Object.create(superClass && superClass.prototype, {\n constructor: {\n value: subClass,\n writable: true,\n configurable: true\n }\n });\n if (superClass) setPrototypeOf(subClass, superClass);\n }\n']);return uw=function(){return e},e}function cw(){var e=i(['\n import defineProperty from "defineProperty";\n\n // This function is different to "Reflect.ownKeys". The enumerableOnly\n // filters on symbol properties only. Returned string properties are always\n // enumerable. It is good to use in objectSpread.\n\n function ownKeys(object, enumerableOnly) {\n var keys = Object.keys(object);\n if (Object.getOwnPropertySymbols) {\n var symbols = Object.getOwnPropertySymbols(object);\n if (enumerableOnly) symbols = symbols.filter(function (sym) {\n return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n });\n keys.push.apply(keys, symbols);\n }\n return keys;\n }\n\n export default function _objectSpread2(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = (arguments[i] != null) ? arguments[i] : {};\n if (i % 2) {\n ownKeys(Object(source), true).forEach(function (key) {\n defineProperty(target, key, source[key]);\n });\n } else if (Object.getOwnPropertyDescriptors) {\n Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n } else {\n ownKeys(Object(source)).forEach(function (key) {\n Object.defineProperty(\n target,\n key,\n Object.getOwnPropertyDescriptor(source, key)\n );\n });\n }\n }\n return target;\n }\n']);return cw=function(){return e},e}function lw(){var e=i(["\n import defineProperty from \"defineProperty\";\n\n export default function _objectSpread(target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = (arguments[i] != null) ? Object(arguments[i]) : {};\n var ownKeys = Object.keys(source);\n if (typeof Object.getOwnPropertySymbols === 'function') {\n ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function(sym) {\n return Object.getOwnPropertyDescriptor(source, sym).enumerable;\n }));\n }\n ownKeys.forEach(function(key) {\n defineProperty(target, key, source[key]);\n });\n }\n return target;\n }\n"]);return lw=function(){return e},e}function pw(){var e=i(["\n export default function _extends() {\n _extends = Object.assign || function (target) {\n for (var i = 1; i < arguments.length; i++) {\n var source = arguments[i];\n for (var key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n target[key] = source[key];\n }\n }\n }\n return target;\n };\n\n return _extends.apply(this, arguments);\n }\n"]);return pw=function(){return e},e}function dw(){var e=i(["\n export default function _defineProperty(obj, key, value) {\n // Shortcircuit the slow defineProperty path when possible.\n // We are trying to avoid issues where setters defined on the\n // prototype cause side effects under the fast path of simple\n // assignment. By checking for existence of the property with\n // the in operator, we can optimize most of this overhead away.\n if (key in obj) {\n Object.defineProperty(obj, key, {\n value: value,\n enumerable: true,\n configurable: true,\n writable: true\n });\n } else {\n obj[key] = value;\n }\n return obj;\n }\n"]);return dw=function(){return e},e}function fw(){var e=i(["\n export default function _defaults(obj, defaults) {\n var keys = Object.getOwnPropertyNames(defaults);\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n var value = Object.getOwnPropertyDescriptor(defaults, key);\n if (value && value.configurable && obj[key] === undefined) {\n Object.defineProperty(obj, key, value);\n }\n }\n return obj;\n }\n"]);return fw=function(){return e},e}function hw(){var e=i(['\n export default function _defineEnumerableProperties(obj, descs) {\n for (var key in descs) {\n var desc = descs[key];\n desc.configurable = desc.enumerable = true;\n if ("value" in desc) desc.writable = true;\n Object.defineProperty(obj, key, desc);\n }\n\n // Symbols are not enumerated over by for-in loops. If native\n // Symbols are available, fetch all of the descs object\'s own\n // symbol properties and define them on our target object too.\n if (Object.getOwnPropertySymbols) {\n var objectSymbols = Object.getOwnPropertySymbols(descs);\n for (var i = 0; i < objectSymbols.length; i++) {\n var sym = objectSymbols[i];\n var desc = descs[sym];\n desc.configurable = desc.enumerable = true;\n if ("value" in desc) desc.writable = true;\n Object.defineProperty(obj, sym, desc);\n }\n }\n return obj;\n }\n']);return hw=function(){return e},e}function mw(){var e=i(['\n function _defineProperties(target, props) {\n for (var i = 0; i < props.length; i ++) {\n var descriptor = props[i];\n descriptor.enumerable = descriptor.enumerable || false;\n descriptor.configurable = true;\n if ("value" in descriptor) descriptor.writable = true;\n Object.defineProperty(target, descriptor.key, descriptor);\n }\n }\n\n export default function _createClass(Constructor, protoProps, staticProps) {\n if (protoProps) _defineProperties(Constructor.prototype, protoProps);\n if (staticProps) _defineProperties(Constructor, staticProps);\n return Constructor;\n }\n']);return mw=function(){return e},e}function yw(){var e=i(['\n export default function _classCallCheck(instance, Constructor) {\n if (!(instance instanceof Constructor)) {\n throw new TypeError("Cannot call a class as a function");\n }\n }\n']);return yw=function(){return e},e}function gw(){var e=i(['\n function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {\n try {\n var info = gen[key](arg);\n var value = info.value;\n } catch (error) {\n reject(error);\n return;\n }\n\n if (info.done) {\n resolve(value);\n } else {\n Promise.resolve(value).then(_next, _throw);\n }\n }\n\n export default function _asyncToGenerator(fn) {\n return function () {\n var self = this, args = arguments;\n return new Promise(function (resolve, reject) {\n var gen = fn.apply(self, args);\n function _next(value) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);\n }\n function _throw(err) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);\n }\n\n _next(undefined);\n });\n };\n }\n']);return gw=function(){return e},e}function vw(){var e=i(['\n export default function _asyncGeneratorDelegate(inner, awaitWrap) {\n var iter = {}, waiting = false;\n\n function pump(key, value) {\n waiting = true;\n value = new Promise(function (resolve) { resolve(inner[key](value)); });\n return { done: false, value: awaitWrap(value) };\n };\n\n if (typeof Symbol === "function" && Symbol.iterator) {\n iter[Symbol.iterator] = function () { return this; };\n }\n\n iter.next = function (value) {\n if (waiting) {\n waiting = false;\n return value;\n }\n return pump("next", value);\n };\n\n if (typeof inner.throw === "function") {\n iter.throw = function (value) {\n if (waiting) {\n waiting = false;\n throw value;\n }\n return pump("throw", value);\n };\n }\n\n if (typeof inner.return === "function") {\n iter.return = function (value) {\n if (waiting) {\n waiting = false;\n return value;\n }\n return pump("return", value);\n };\n }\n\n return iter;\n }\n']);return vw=function(){return e},e}function bw(){var e=i(['\n import AwaitValue from "AwaitValue";\n\n export default function _awaitAsyncGenerator(value) {\n return new AwaitValue(value);\n }\n']);return bw=function(){return e},e}function xw(){var e=i(['\n import AsyncGenerator from "AsyncGenerator";\n\n export default function _wrapAsyncGenerator(fn) {\n return function () {\n return new AsyncGenerator(fn.apply(this, arguments));\n };\n }\n']);return xw=function(){return e},e}function Ew(){var e=i(['\n import AwaitValue from "AwaitValue";\n\n export default function AsyncGenerator(gen) {\n var front, back;\n\n function send(key, arg) {\n return new Promise(function (resolve, reject) {\n var request = {\n key: key,\n arg: arg,\n resolve: resolve,\n reject: reject,\n next: null,\n };\n\n if (back) {\n back = back.next = request;\n } else {\n front = back = request;\n resume(key, arg);\n }\n });\n }\n\n function resume(key, arg) {\n try {\n var result = gen[key](arg)\n var value = result.value;\n var wrappedAwait = value instanceof AwaitValue;\n\n Promise.resolve(wrappedAwait ? value.wrapped : value).then(\n function (arg) {\n if (wrappedAwait) {\n resume(key === "return" ? "return" : "next", arg);\n return\n }\n\n settle(result.done ? "return" : "normal", arg);\n },\n function (err) { resume("throw", err); });\n } catch (err) {\n settle("throw", err);\n }\n }\n\n function settle(type, value) {\n switch (type) {\n case "return":\n front.resolve({ value: value, done: true });\n break;\n case "throw":\n front.reject(value);\n break;\n default:\n front.resolve({ value: value, done: false });\n break;\n }\n\n front = front.next;\n if (front) {\n resume(front.key, front.arg);\n } else {\n back = null;\n }\n }\n\n this._invoke = send;\n\n // Hide "return" method if generator return is not supported\n if (typeof gen.return !== "function") {\n this.return = undefined;\n }\n }\n\n if (typeof Symbol === "function" && Symbol.asyncIterator) {\n AsyncGenerator.prototype[Symbol.asyncIterator] = function () { return this; };\n }\n\n AsyncGenerator.prototype.next = function (arg) { return this._invoke("next", arg); };\n AsyncGenerator.prototype.throw = function (arg) { return this._invoke("throw", arg); };\n AsyncGenerator.prototype.return = function (arg) { return this._invoke("return", arg); };\n']);return Ew=function(){return e},e}function Aw(){var e=i(["\n export default function _AwaitValue(value) {\n this.wrapped = value;\n }\n"]);return Aw=function(){return e},e}function ww(){var e=i(['\n export default function _asyncIterator(iterable) {\n var method\n if (typeof Symbol !== "undefined") {\n if (Symbol.asyncIterator) {\n method = iterable[Symbol.asyncIterator]\n if (method != null) return method.call(iterable);\n }\n if (Symbol.iterator) {\n method = iterable[Symbol.iterator]\n if (method != null) return method.call(iterable);\n }\n }\n throw new TypeError("Object is not async iterable");\n }\n']);return ww=function(){return e},e}function Sw(){var e=i(['\n var REACT_ELEMENT_TYPE;\n\n export default function _createRawReactElement(type, props, key, children) {\n if (!REACT_ELEMENT_TYPE) {\n REACT_ELEMENT_TYPE = (\n typeof Symbol === "function" && Symbol["for"] && Symbol["for"]("react.element")\n ) || 0xeac7;\n }\n\n var defaultProps = type && type.defaultProps;\n var childrenLength = arguments.length - 3;\n\n if (!props && childrenLength !== 0) {\n // If we\'re going to assign props.children, we create a new object now\n // to avoid mutating defaultProps.\n props = {\n children: void 0,\n };\n }\n\n if (childrenLength === 1) {\n props.children = children;\n } else if (childrenLength > 1) {\n var childArray = new Array(childrenLength);\n for (var i = 0; i < childrenLength; i++) {\n childArray[i] = arguments[i + 3];\n }\n props.children = childArray;\n }\n\n if (props && defaultProps) {\n for (var propName in defaultProps) {\n if (props[propName] === void 0) {\n props[propName] = defaultProps[propName];\n }\n }\n } else if (!props) {\n props = defaultProps || {};\n }\n\n return {\n $$typeof: REACT_ELEMENT_TYPE,\n type: type,\n key: key === undefined ? null : \'\' + key,\n ref: null,\n props: props,\n _owner: null,\n };\n }\n']);return Sw=function(){return e},e}function Dw(){var e=i(['\n export default function _typeof(obj) {\n "@babel/helpers - typeof";\n\n if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {\n _typeof = function (obj) { return typeof obj; };\n } else {\n _typeof = function (obj) {\n return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype\n ? "symbol"\n : typeof obj;\n };\n }\n\n return _typeof(obj);\n }\n']);return Dw=function(){return e},e}sA.visitors=aA,sA.verify=$E,sA.explode=JE,sA.cheap=function(e,t){return Bd(e,t)},sA.node=function(e,t,r,n,a,s){var i=ks[e.type];if(i){var o=new YE(r,t,n,a),u=i,c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}var d=p;if((!s||!s[d])&&o.visit(e,d))return}}},sA.clearNode=function(e,t){Rd(e,t),Qh.delete(e)},sA.removeProperties=function(e,t){return Bd(e,sA.clearNode,t),e},sA.hasType=function(e,t,r){if(zf(r,e.type))return!1;if(e.type===t)return!0;var n={has:!1,type:t};return sA(e,{noScope:!0,blacklist:r,enter:iA},null,n),n.has},sA.cache=rm;var Cw=Object.create(null),Tw=function(e){return function(t){return{minVersion:e,ast:function(){return dE.program.ast(t)}}}};function jw(e){for(var t=[];e.parentPath;e=e.parentPath)t.push(e.key),e.inList&&t.push(e.listKey);return t.reverse().join(".")}Cw.typeof=Tw("7.0.0-beta.0")(Dw()),Cw.jsx=Tw("7.0.0-beta.0")(Sw()),Cw.asyncIterator=Tw("7.0.0-beta.0")(ww()),Cw.AwaitValue=Tw("7.0.0-beta.0")(Aw()),Cw.AsyncGenerator=Tw("7.0.0-beta.0")(Ew()),Cw.wrapAsyncGenerator=Tw("7.0.0-beta.0")(xw()),Cw.awaitAsyncGenerator=Tw("7.0.0-beta.0")(bw()),Cw.asyncGeneratorDelegate=Tw("7.0.0-beta.0")(vw()),Cw.asyncToGenerator=Tw("7.0.0-beta.0")(gw()),Cw.classCallCheck=Tw("7.0.0-beta.0")(yw()),Cw.createClass=Tw("7.0.0-beta.0")(mw()),Cw.defineEnumerableProperties=Tw("7.0.0-beta.0")(hw()),Cw.defaults=Tw("7.0.0-beta.0")(fw()),Cw.defineProperty=Tw("7.0.0-beta.0")(dw()),Cw.extends=Tw("7.0.0-beta.0")(pw()),Cw.objectSpread=Tw("7.0.0-beta.0")(lw()),Cw.objectSpread2=Tw("7.5.0")(cw()),Cw.inherits=Tw("7.0.0-beta.0")(uw()),Cw.inheritsLoose=Tw("7.0.0-beta.0")(ow()),Cw.getPrototypeOf=Tw("7.0.0-beta.0")(iw()),Cw.setPrototypeOf=Tw("7.0.0-beta.0")(sw()),Cw.construct=Tw("7.0.0-beta.0")(aw()),Cw.isNativeFunction=Tw("7.0.0-beta.0")(nw()),Cw.wrapNativeSuper=Tw("7.0.0-beta.0")(rw()),Cw.instanceof=Tw("7.0.0-beta.0")(tw()),Cw.interopRequireDefault=Tw("7.0.0-beta.0")(ew()),Cw.interopRequireWildcard=Tw("7.0.0-beta.0")(ZA()),Cw.newArrowCheck=Tw("7.0.0-beta.0")(QA()),Cw.objectDestructuringEmpty=Tw("7.0.0-beta.0")($A()),Cw.objectWithoutPropertiesLoose=Tw("7.0.0-beta.0")(JA()),Cw.objectWithoutProperties=Tw("7.0.0-beta.0")(YA()),Cw.assertThisInitialized=Tw("7.0.0-beta.0")(XA()),Cw.possibleConstructorReturn=Tw("7.0.0-beta.0")(zA()),Cw.superPropBase=Tw("7.0.0-beta.0")(KA()),Cw.get=Tw("7.0.0-beta.0")(qA()),Cw.set=Tw("7.0.0-beta.0")(HA()),Cw.taggedTemplateLiteral=Tw("7.0.0-beta.0")(WA()),Cw.taggedTemplateLiteralLoose=Tw("7.0.0-beta.0")(VA()),Cw.readOnlyError=Tw("7.0.0-beta.0")(GA()),Cw.classNameTDZError=Tw("7.0.0-beta.0")(UA()),Cw.temporalUndefined=Tw("7.0.0-beta.0")(LA()),Cw.tdz=Tw("7.5.5")(MA()),Cw.temporalRef=Tw("7.0.0-beta.0")(RA()),Cw.slicedToArray=Tw("7.0.0-beta.0")(NA()),Cw.slicedToArrayLoose=Tw("7.0.0-beta.0")(OA()),Cw.toArray=Tw("7.0.0-beta.0")(BA()),Cw.toConsumableArray=Tw("7.0.0-beta.0")(IA()),Cw.arrayWithoutHoles=Tw("7.0.0-beta.0")(_A()),Cw.arrayWithHoles=Tw("7.0.0-beta.0")(FA()),Cw.iterableToArray=Tw("7.0.0-beta.0")(kA()),Cw.iterableToArrayLimit=Tw("7.0.0-beta.0")(PA()),Cw.iterableToArrayLimitLoose=Tw("7.0.0-beta.0")(jA()),Cw.nonIterableSpread=Tw("7.0.0-beta.0")(TA()),Cw.nonIterableRest=Tw("7.0.0-beta.0")(CA()),Cw.skipFirstGeneratorNext=Tw("7.0.0-beta.0")(DA()),Cw.toPrimitive=Tw("7.1.5")(SA()),Cw.toPropertyKey=Tw("7.1.5")(wA()),Cw.initializerWarningHelper=Tw("7.0.0-beta.0")(AA()),Cw.initializerDefineProperty=Tw("7.0.0-beta.0")(EA()),Cw.applyDecoratedDescriptor=Tw("7.0.0-beta.0")(xA()),Cw.classPrivateFieldLooseKey=Tw("7.0.0-beta.0")(bA()),Cw.classPrivateFieldLooseBase=Tw("7.0.0-beta.0")(vA()),Cw.classPrivateFieldGet=Tw("7.0.0-beta.0")(gA()),Cw.classPrivateFieldSet=Tw("7.0.0-beta.0")(yA()),Cw.classPrivateFieldDestructureSet=Tw("7.4.4")(mA()),Cw.classStaticPrivateFieldSpecGet=Tw("7.0.2")(hA()),Cw.classStaticPrivateFieldSpecSet=Tw("7.0.2")(fA()),Cw.classStaticPrivateMethodGet=Tw("7.3.2")(dA()),Cw.classStaticPrivateMethodSet=Tw("7.3.2")(pA()),Cw.decorate=Tw("7.1.5")(lA()),Cw.classPrivateMethodGet=Tw("7.1.6")(cA()),Cw.classPrivateMethodSet=Tw("7.1.6")(uA()),Cw.wrapRegExp=Tw("7.2.6")(oA());var Pw=Object.create(null);function kw(e){if(!Pw[e]){var t=Cw[e];if(!t)throw Object.assign(new ReferenceError("Unknown helper "+e),{code:"BABEL_HELPER_UNKNOWN",helper:e});var r=function(){return zi(t.ast())},n=function(e){var t,r,n=new Set,a=new Set,s=new Map,i=[],o=[],u=[];if(sA(e,{ImportDeclaration:function(e){var t=e.node.source.value;if(!Cw[t])throw e.buildCodeFrameError("Unknown helper "+t);if(1!==e.get("specifiers").length||!e.get("specifiers.0").isImportDefaultSpecifier())throw e.buildCodeFrameError("Helpers can only import a default value");var r=e.node.specifiers[0].local;s.set(r,t),o.push(jw(e))},ExportDefaultDeclaration:function(e){var n=e.get("declaration");if(n.isFunctionDeclaration()){if(!n.node.id)throw n.buildCodeFrameError("Helpers should give names to their exported func declaration");t=n.node.id.name}r=jw(e)},ExportAllDeclaration:function(e){throw e.buildCodeFrameError("Helpers can only export default")},ExportNamedDeclaration:function(e){throw e.buildCodeFrameError("Helpers can only export default")},Statement:function(e){e.isModuleDeclaration()||e.skip()}}),sA(e,{Program:function(e){var r=e.scope.getAllBindings();Object.keys(r).forEach((function(e){e!==t&&(s.has(r[e].identifier)||a.add(e))}))},ReferencedIdentifier:function(e){var t=e.node.name,r=e.scope.getBinding(t,!0);r?s.has(r.identifier)&&u.push(jw(e)):n.add(t)},AssignmentExpression:function(e){var r=e.get("left");if(t in r.getBindingIdentifiers()){if(!r.isIdentifier())throw r.buildCodeFrameError("Only simple assignments to exports are allowed in helpers");var n=e.scope.getBinding(t);n&&n.scope.path.isProgram()&&i.push(jw(e))}}}),!r)throw new Error("Helpers must default-export something.");return i.reverse(),{globals:Array.from(n),localBindingNames:Array.from(a),dependencies:s,exportBindingAssignments:i,exportPath:r,exportName:t,importBindingsReferences:u,importPaths:o}}(r());Pw[e]={build:function(e,t,a){var s=r();return function(e,t,r,n,a){if(n&&!r)throw new Error("Unexpected local bindings for module-based helpers.");if(r){var s=t.localBindingNames,i=t.dependencies,o=t.exportBindingAssignments,u=t.exportPath,c=t.exportName,l=t.importBindingsReferences,p=t.importPaths,d={};i.forEach((function(e,t){d[t.name]="function"==typeof a&&a(e)||t}));var f={},h=new Set(n||[]);s.forEach((function(e){for(var t=e;h.has(t);)t="_"+t;t!==e&&(f[e]=t)})),"Identifier"===r.type&&c!==r.name&&(f[c]=r.name),sA(e,{Program:function(e){var t=e.get(u),n=p.map((function(t){return e.get(t)})),a=l.map((function(t){return e.get(t)})),s=t.get("declaration");if("Identifier"===r.type)s.isFunctionDeclaration()?t.replaceWith(s):t.replaceWith(Do("var",[Co(r,s.node)]));else{if("MemberExpression"!==r.type)throw new Error("Unexpected helper format.");s.isFunctionDeclaration()?(o.forEach((function(t){var n=e.get(t);n.replaceWith(_i("=",r,n.node))})),t.replaceWith(s),e.pushContainer("body",Ki(_i("=",r,Qi(c))))):t.replaceWith(Ki(_i("=",r,s.node)))}Object.keys(f).forEach((function(t){e.scope.rename(t,f[t])}));var i=n,h=Array.isArray(i),m=0;for(i=h?i:i[Symbol.iterator]();;){var y;if(h){if(m>=i.length)break;y=i[m++]}else{if((m=i.next()).done)break;y=m.value}y.remove()}var g=a,v=Array.isArray(g),b=0;for(g=v?g:g[Symbol.iterator]();;){var x;if(v){if(b>=g.length)break;x=g[b++]}else{if((b=g.next()).done)break;x=b.value}var E=x,A=pp(d[E.node.name]);E.replaceWith(A)}e.stop()}})}}(s,n,t,a,e),{nodes:s.program.body,globals:n.globals}},minVersion:function(){return t.minVersion},dependencies:n.dependencies}}return Pw[e]}function Fw(e,t,r,n){return kw(e).build(t,r,n)}var _w=Object.keys(Cw).map((function(e){return e.replace(/^_/,"")})).filter((function(e){return"__esModule"!==e})),Iw=Vt((function(e,t){var r;t=e.exports=p,r="object"==typeof es&&es.env&&es.env.NODE_DEBUG&&/\bsemver\b/i.test(es.env.NODE_DEBUG)?function(){var e=Array.prototype.slice.call(arguments,0);e.unshift("SEMVER"),console.log.apply(console,e)}:function(){},t.SEMVER_SPEC_VERSION="2.0.0";var n=Number.MAX_SAFE_INTEGER||9007199254740991,a=t.re=[],s=t.src=[],i=t.tokens={},o=0;function u(e){i[e]=o++}u("NUMERICIDENTIFIER"),s[i.NUMERICIDENTIFIER]="0|[1-9]\\d*",u("NUMERICIDENTIFIERLOOSE"),s[i.NUMERICIDENTIFIERLOOSE]="[0-9]+",u("NONNUMERICIDENTIFIER"),s[i.NONNUMERICIDENTIFIER]="\\d*[a-zA-Z-][a-zA-Z0-9-]*",u("MAINVERSION"),s[i.MAINVERSION]="("+s[i.NUMERICIDENTIFIER]+")\\.("+s[i.NUMERICIDENTIFIER]+")\\.("+s[i.NUMERICIDENTIFIER]+")",u("MAINVERSIONLOOSE"),s[i.MAINVERSIONLOOSE]="("+s[i.NUMERICIDENTIFIERLOOSE]+")\\.("+s[i.NUMERICIDENTIFIERLOOSE]+")\\.("+s[i.NUMERICIDENTIFIERLOOSE]+")",u("PRERELEASEIDENTIFIER"),s[i.PRERELEASEIDENTIFIER]="(?:"+s[i.NUMERICIDENTIFIER]+"|"+s[i.NONNUMERICIDENTIFIER]+")",u("PRERELEASEIDENTIFIERLOOSE"),s[i.PRERELEASEIDENTIFIERLOOSE]="(?:"+s[i.NUMERICIDENTIFIERLOOSE]+"|"+s[i.NONNUMERICIDENTIFIER]+")",u("PRERELEASE"),s[i.PRERELEASE]="(?:-("+s[i.PRERELEASEIDENTIFIER]+"(?:\\."+s[i.PRERELEASEIDENTIFIER]+")*))",u("PRERELEASELOOSE"),s[i.PRERELEASELOOSE]="(?:-?("+s[i.PRERELEASEIDENTIFIERLOOSE]+"(?:\\."+s[i.PRERELEASEIDENTIFIERLOOSE]+")*))",u("BUILDIDENTIFIER"),s[i.BUILDIDENTIFIER]="[0-9A-Za-z-]+",u("BUILD"),s[i.BUILD]="(?:\\+("+s[i.BUILDIDENTIFIER]+"(?:\\."+s[i.BUILDIDENTIFIER]+")*))",u("FULL"),u("FULLPLAIN"),s[i.FULLPLAIN]="v?"+s[i.MAINVERSION]+s[i.PRERELEASE]+"?"+s[i.BUILD]+"?",s[i.FULL]="^"+s[i.FULLPLAIN]+"$",u("LOOSEPLAIN"),s[i.LOOSEPLAIN]="[v=\\s]*"+s[i.MAINVERSIONLOOSE]+s[i.PRERELEASELOOSE]+"?"+s[i.BUILD]+"?",u("LOOSE"),s[i.LOOSE]="^"+s[i.LOOSEPLAIN]+"$",u("GTLT"),s[i.GTLT]="((?:<|>)?=?)",u("XRANGEIDENTIFIERLOOSE"),s[i.XRANGEIDENTIFIERLOOSE]=s[i.NUMERICIDENTIFIERLOOSE]+"|x|X|\\*",u("XRANGEIDENTIFIER"),s[i.XRANGEIDENTIFIER]=s[i.NUMERICIDENTIFIER]+"|x|X|\\*",u("XRANGEPLAIN"),s[i.XRANGEPLAIN]="[v=\\s]*("+s[i.XRANGEIDENTIFIER]+")(?:\\.("+s[i.XRANGEIDENTIFIER]+")(?:\\.("+s[i.XRANGEIDENTIFIER]+")(?:"+s[i.PRERELEASE]+")?"+s[i.BUILD]+"?)?)?",u("XRANGEPLAINLOOSE"),s[i.XRANGEPLAINLOOSE]="[v=\\s]*("+s[i.XRANGEIDENTIFIERLOOSE]+")(?:\\.("+s[i.XRANGEIDENTIFIERLOOSE]+")(?:\\.("+s[i.XRANGEIDENTIFIERLOOSE]+")(?:"+s[i.PRERELEASELOOSE]+")?"+s[i.BUILD]+"?)?)?",u("XRANGE"),s[i.XRANGE]="^"+s[i.GTLT]+"\\s*"+s[i.XRANGEPLAIN]+"$",u("XRANGELOOSE"),s[i.XRANGELOOSE]="^"+s[i.GTLT]+"\\s*"+s[i.XRANGEPLAINLOOSE]+"$",u("COERCE"),s[i.COERCE]="(^|[^\\d])(\\d{1,16})(?:\\.(\\d{1,16}))?(?:\\.(\\d{1,16}))?(?:$|[^\\d])",u("COERCERTL"),a[i.COERCERTL]=new RegExp(s[i.COERCE],"g"),u("LONETILDE"),s[i.LONETILDE]="(?:~>?)",u("TILDETRIM"),s[i.TILDETRIM]="(\\s*)"+s[i.LONETILDE]+"\\s+",a[i.TILDETRIM]=new RegExp(s[i.TILDETRIM],"g");u("TILDE"),s[i.TILDE]="^"+s[i.LONETILDE]+s[i.XRANGEPLAIN]+"$",u("TILDELOOSE"),s[i.TILDELOOSE]="^"+s[i.LONETILDE]+s[i.XRANGEPLAINLOOSE]+"$",u("LONECARET"),s[i.LONECARET]="(?:\\^)",u("CARETTRIM"),s[i.CARETTRIM]="(\\s*)"+s[i.LONECARET]+"\\s+",a[i.CARETTRIM]=new RegExp(s[i.CARETTRIM],"g");u("CARET"),s[i.CARET]="^"+s[i.LONECARET]+s[i.XRANGEPLAIN]+"$",u("CARETLOOSE"),s[i.CARETLOOSE]="^"+s[i.LONECARET]+s[i.XRANGEPLAINLOOSE]+"$",u("COMPARATORLOOSE"),s[i.COMPARATORLOOSE]="^"+s[i.GTLT]+"\\s*("+s[i.LOOSEPLAIN]+")$|^$",u("COMPARATOR"),s[i.COMPARATOR]="^"+s[i.GTLT]+"\\s*("+s[i.FULLPLAIN]+")$|^$",u("COMPARATORTRIM"),s[i.COMPARATORTRIM]="(\\s*)"+s[i.GTLT]+"\\s*("+s[i.LOOSEPLAIN]+"|"+s[i.XRANGEPLAIN]+")",a[i.COMPARATORTRIM]=new RegExp(s[i.COMPARATORTRIM],"g");u("HYPHENRANGE"),s[i.HYPHENRANGE]="^\\s*("+s[i.XRANGEPLAIN]+")\\s+-\\s+("+s[i.XRANGEPLAIN]+")\\s*$",u("HYPHENRANGELOOSE"),s[i.HYPHENRANGELOOSE]="^\\s*("+s[i.XRANGEPLAINLOOSE]+")\\s+-\\s+("+s[i.XRANGEPLAINLOOSE]+")\\s*$",u("STAR"),s[i.STAR]="(<|>)?=?\\s*\\*";for(var c=0;c<o;c++)r(c,s[c]),a[c]||(a[c]=new RegExp(s[c]));function l(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof p)return e;if("string"!=typeof e)return null;if(e.length>256)return null;if(!(t.loose?a[i.LOOSE]:a[i.FULL]).test(e))return null;try{return new p(e,t)}catch(e){return null}}function p(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof p){if(e.loose===t.loose)return e;e=e.version}else if("string"!=typeof e)throw new TypeError("Invalid Version: "+e);if(e.length>256)throw new TypeError("version is longer than 256 characters");if(!(this instanceof p))return new p(e,t);r("SemVer",e,t),this.options=t,this.loose=!!t.loose;var s=e.trim().match(t.loose?a[i.LOOSE]:a[i.FULL]);if(!s)throw new TypeError("Invalid Version: "+e);if(this.raw=e,this.major=+s[1],this.minor=+s[2],this.patch=+s[3],this.major>n||this.major<0)throw new TypeError("Invalid major version");if(this.minor>n||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>n||this.patch<0)throw new TypeError("Invalid patch version");s[4]?this.prerelease=s[4].split(".").map((function(e){if(/^[0-9]+$/.test(e)){var t=+e;if(t>=0&&t<n)return t}return e})):this.prerelease=[],this.build=s[5]?s[5].split("."):[],this.format()}t.parse=l,t.valid=function(e,t){var r=l(e,t);return r?r.version:null},t.clean=function(e,t){var r=l(e.trim().replace(/^[=v]+/,""),t);return r?r.version:null},t.SemVer=p,p.prototype.format=function(){return this.version=this.major+"."+this.minor+"."+this.patch,this.prerelease.length&&(this.version+="-"+this.prerelease.join(".")),this.version},p.prototype.toString=function(){return this.version},p.prototype.compare=function(e){return r("SemVer.compare",this.version,this.options,e),e instanceof p||(e=new p(e,this.options)),this.compareMain(e)||this.comparePre(e)},p.prototype.compareMain=function(e){return e instanceof p||(e=new p(e,this.options)),f(this.major,e.major)||f(this.minor,e.minor)||f(this.patch,e.patch)},p.prototype.comparePre=function(e){if(e instanceof p||(e=new p(e,this.options)),this.prerelease.length&&!e.prerelease.length)return-1;if(!this.prerelease.length&&e.prerelease.length)return 1;if(!this.prerelease.length&&!e.prerelease.length)return 0;var t=0;do{var n=this.prerelease[t],a=e.prerelease[t];if(r("prerelease compare",t,n,a),void 0===n&&void 0===a)return 0;if(void 0===a)return 1;if(void 0===n)return-1;if(n!==a)return f(n,a)}while(++t)},p.prototype.compareBuild=function(e){e instanceof p||(e=new p(e,this.options));var t=0;do{var n=this.build[t],a=e.build[t];if(r("prerelease compare",t,n,a),void 0===n&&void 0===a)return 0;if(void 0===a)return 1;if(void 0===n)return-1;if(n!==a)return f(n,a)}while(++t)},p.prototype.inc=function(e,t){switch(e){case"premajor":this.prerelease.length=0,this.patch=0,this.minor=0,this.major++,this.inc("pre",t);break;case"preminor":this.prerelease.length=0,this.patch=0,this.minor++,this.inc("pre",t);break;case"prepatch":this.prerelease.length=0,this.inc("patch",t),this.inc("pre",t);break;case"prerelease":0===this.prerelease.length&&this.inc("patch",t),this.inc("pre",t);break;case"major":0===this.minor&&0===this.patch&&0!==this.prerelease.length||this.major++,this.minor=0,this.patch=0,this.prerelease=[];break;case"minor":0===this.patch&&0!==this.prerelease.length||this.minor++,this.patch=0,this.prerelease=[];break;case"patch":0===this.prerelease.length&&this.patch++,this.prerelease=[];break;case"pre":if(0===this.prerelease.length)this.prerelease=[0];else{for(var r=this.prerelease.length;--r>=0;)"number"==typeof this.prerelease[r]&&(this.prerelease[r]++,r=-2);-1===r&&this.prerelease.push(0)}t&&(this.prerelease[0]===t?isNaN(this.prerelease[1])&&(this.prerelease=[t,0]):this.prerelease=[t,0]);break;default:throw new Error("invalid increment argument: "+e)}return this.format(),this.raw=this.version,this},t.inc=function(e,t,r,n){"string"==typeof r&&(n=r,r=void 0);try{return new p(e,r).inc(t,n).version}catch(e){return null}},t.diff=function(e,t){if(g(e,t))return null;var r=l(e),n=l(t),a="";if(r.prerelease.length||n.prerelease.length){a="pre";var s="prerelease"}for(var i in r)if(("major"===i||"minor"===i||"patch"===i)&&r[i]!==n[i])return a+i;return s},t.compareIdentifiers=f;var d=/^[0-9]+$/;function f(e,t){var r=d.test(e),n=d.test(t);return r&&n&&(e=+e,t=+t),e===t?0:r&&!n?-1:n&&!r?1:e<t?-1:1}function h(e,t,r){return new p(e,r).compare(new p(t,r))}function m(e,t,r){return h(e,t,r)>0}function y(e,t,r){return h(e,t,r)<0}function g(e,t,r){return 0===h(e,t,r)}function v(e,t,r){return 0!==h(e,t,r)}function b(e,t,r){return h(e,t,r)>=0}function x(e,t,r){return h(e,t,r)<=0}function E(e,t,r,n){switch(t){case"===":return"object"==typeof e&&(e=e.version),"object"==typeof r&&(r=r.version),e===r;case"!==":return"object"==typeof e&&(e=e.version),"object"==typeof r&&(r=r.version),e!==r;case"":case"=":case"==":return g(e,r,n);case"!=":return v(e,r,n);case">":return m(e,r,n);case">=":return b(e,r,n);case"<":return y(e,r,n);case"<=":return x(e,r,n);default:throw new TypeError("Invalid operator: "+t)}}function A(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof A){if(e.loose===!!t.loose)return e;e=e.value}if(!(this instanceof A))return new A(e,t);r("comparator",e,t),this.options=t,this.loose=!!t.loose,this.parse(e),this.semver===w?this.value="":this.value=this.operator+this.semver.version,r("comp",this)}t.rcompareIdentifiers=function(e,t){return f(t,e)},t.major=function(e,t){return new p(e,t).major},t.minor=function(e,t){return new p(e,t).minor},t.patch=function(e,t){return new p(e,t).patch},t.compare=h,t.compareLoose=function(e,t){return h(e,t,!0)},t.compareBuild=function(e,t,r){var n=new p(e,r),a=new p(t,r);return n.compare(a)||n.compareBuild(a)},t.rcompare=function(e,t,r){return h(t,e,r)},t.sort=function(e,r){return e.sort((function(e,n){return t.compareBuild(e,n,r)}))},t.rsort=function(e,r){return e.sort((function(e,n){return t.compareBuild(n,e,r)}))},t.gt=m,t.lt=y,t.eq=g,t.neq=v,t.gte=b,t.lte=x,t.cmp=E,t.Comparator=A;var w={};function S(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof S)return e.loose===!!t.loose&&e.includePrerelease===!!t.includePrerelease?e:new S(e.raw,t);if(e instanceof A)return new S(e.value,t);if(!(this instanceof S))return new S(e,t);if(this.options=t,this.loose=!!t.loose,this.includePrerelease=!!t.includePrerelease,this.raw=e,this.set=e.split(/\s*\|\|\s*/).map((function(e){return this.parseRange(e.trim())}),this).filter((function(e){return e.length})),!this.set.length)throw new TypeError("Invalid SemVer Range: "+e);this.format()}function D(e,t){for(var r=!0,n=e.slice(),a=n.pop();r&&n.length;)r=n.every((function(e){return a.intersects(e,t)})),a=n.pop();return r}function C(e){return!e||"x"===e.toLowerCase()||"*"===e}function T(e,t,r,n,a,s,i,o,u,c,l,p,d){return((t=C(r)?"":C(n)?">="+r+".0.0":C(a)?">="+r+"."+n+".0":">="+t)+" "+(o=C(u)?"":C(c)?"<"+(+u+1)+".0.0":C(l)?"<"+u+"."+(+c+1)+".0":p?"<="+u+"."+c+"."+l+"-"+p:"<="+o)).trim()}function j(e,t,n){for(var a=0;a<e.length;a++)if(!e[a].test(t))return!1;if(t.prerelease.length&&!n.includePrerelease){for(a=0;a<e.length;a++)if(r(e[a].semver),e[a].semver!==w&&e[a].semver.prerelease.length>0){var s=e[a].semver;if(s.major===t.major&&s.minor===t.minor&&s.patch===t.patch)return!0}return!1}return!0}function P(e,t,r){try{t=new S(t,r)}catch(e){return!1}return t.test(e)}function k(e,t,r,n){var a,s,i,o,u;switch(e=new p(e,n),t=new S(t,n),r){case">":a=m,s=x,i=y,o=">",u=">=";break;case"<":a=y,s=b,i=m,o="<",u="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(P(e,t,n))return!1;for(var c=0;c<t.set.length;++c){var l=t.set[c],d=null,f=null;if(l.forEach((function(e){e.semver===w&&(e=new A(">=0.0.0")),d=d||e,f=f||e,a(e.semver,d.semver,n)?d=e:i(e.semver,f.semver,n)&&(f=e)})),d.operator===o||d.operator===u)return!1;if((!f.operator||f.operator===o)&&s(e,f.semver))return!1;if(f.operator===u&&i(e,f.semver))return!1}return!0}A.prototype.parse=function(e){var t=this.options.loose?a[i.COMPARATORLOOSE]:a[i.COMPARATOR],r=e.match(t);if(!r)throw new TypeError("Invalid comparator: "+e);this.operator=void 0!==r[1]?r[1]:"","="===this.operator&&(this.operator=""),r[2]?this.semver=new p(r[2],this.options.loose):this.semver=w},A.prototype.toString=function(){return this.value},A.prototype.test=function(e){if(r("Comparator.test",e,this.options.loose),this.semver===w||e===w)return!0;if("string"==typeof e)try{e=new p(e,this.options)}catch(e){return!1}return E(e,this.operator,this.semver,this.options)},A.prototype.intersects=function(e,t){if(!(e instanceof A))throw new TypeError("a Comparator is required");var r;if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),""===this.operator)return""===this.value||(r=new S(e.value,t),P(this.value,r,t));if(""===e.operator)return""===e.value||(r=new S(this.value,t),P(e.semver,r,t));var n=!(">="!==this.operator&&">"!==this.operator||">="!==e.operator&&">"!==e.operator),a=!("<="!==this.operator&&"<"!==this.operator||"<="!==e.operator&&"<"!==e.operator),s=this.semver.version===e.semver.version,i=!(">="!==this.operator&&"<="!==this.operator||">="!==e.operator&&"<="!==e.operator),o=E(this.semver,"<",e.semver,t)&&(">="===this.operator||">"===this.operator)&&("<="===e.operator||"<"===e.operator),u=E(this.semver,">",e.semver,t)&&("<="===this.operator||"<"===this.operator)&&(">="===e.operator||">"===e.operator);return n||a||s&&i||o||u},t.Range=S,S.prototype.format=function(){return this.range=this.set.map((function(e){return e.join(" ").trim()})).join("||").trim(),this.range},S.prototype.toString=function(){return this.range},S.prototype.parseRange=function(e){var t=this.options.loose;e=e.trim();var n=t?a[i.HYPHENRANGELOOSE]:a[i.HYPHENRANGE];e=e.replace(n,T),r("hyphen replace",e),e=e.replace(a[i.COMPARATORTRIM],"$1$2$3"),r("comparator trim",e,a[i.COMPARATORTRIM]),e=(e=(e=e.replace(a[i.TILDETRIM],"$1~")).replace(a[i.CARETTRIM],"$1^")).split(/\s+/).join(" ");var s=t?a[i.COMPARATORLOOSE]:a[i.COMPARATOR],o=e.split(" ").map((function(e){return function(e,t){return r("comp",e,t),e=function(e,t){return e.trim().split(/\s+/).map((function(e){return function(e,t){r("caret",e,t);var n=t.loose?a[i.CARETLOOSE]:a[i.CARET];return e.replace(n,(function(t,n,a,s,i){var o;return r("caret",e,t,n,a,s,i),C(n)?o="":C(a)?o=">="+n+".0.0 <"+(+n+1)+".0.0":C(s)?o="0"===n?">="+n+"."+a+".0 <"+n+"."+(+a+1)+".0":">="+n+"."+a+".0 <"+(+n+1)+".0.0":i?(r("replaceCaret pr",i),o="0"===n?"0"===a?">="+n+"."+a+"."+s+"-"+i+" <"+n+"."+a+"."+(+s+1):">="+n+"."+a+"."+s+"-"+i+" <"+n+"."+(+a+1)+".0":">="+n+"."+a+"."+s+"-"+i+" <"+(+n+1)+".0.0"):(r("no pr"),o="0"===n?"0"===a?">="+n+"."+a+"."+s+" <"+n+"."+a+"."+(+s+1):">="+n+"."+a+"."+s+" <"+n+"."+(+a+1)+".0":">="+n+"."+a+"."+s+" <"+(+n+1)+".0.0"),r("caret return",o),o}))}(e,t)})).join(" ")}(e,t),r("caret",e),e=function(e,t){return e.trim().split(/\s+/).map((function(e){return function(e,t){var n=t.loose?a[i.TILDELOOSE]:a[i.TILDE];return e.replace(n,(function(t,n,a,s,i){var o;return r("tilde",e,t,n,a,s,i),C(n)?o="":C(a)?o=">="+n+".0.0 <"+(+n+1)+".0.0":C(s)?o=">="+n+"."+a+".0 <"+n+"."+(+a+1)+".0":i?(r("replaceTilde pr",i),o=">="+n+"."+a+"."+s+"-"+i+" <"+n+"."+(+a+1)+".0"):o=">="+n+"."+a+"."+s+" <"+n+"."+(+a+1)+".0",r("tilde return",o),o}))}(e,t)})).join(" ")}(e,t),r("tildes",e),e=function(e,t){return r("replaceXRanges",e,t),e.split(/\s+/).map((function(e){return function(e,t){e=e.trim();var n=t.loose?a[i.XRANGELOOSE]:a[i.XRANGE];return e.replace(n,(function(n,a,s,i,o,u){r("xRange",e,n,a,s,i,o,u);var c=C(s),l=c||C(i),p=l||C(o),d=p;return"="===a&&d&&(a=""),u=t.includePrerelease?"-0":"",c?n=">"===a||"<"===a?"<0.0.0-0":"*":a&&d?(l&&(i=0),o=0,">"===a?(a=">=",l?(s=+s+1,i=0,o=0):(i=+i+1,o=0)):"<="===a&&(a="<",l?s=+s+1:i=+i+1),n=a+s+"."+i+"."+o+u):l?n=">="+s+".0.0"+u+" <"+(+s+1)+".0.0"+u:p&&(n=">="+s+"."+i+".0"+u+" <"+s+"."+(+i+1)+".0"+u),r("xRange return",n),n}))}(e,t)})).join(" ")}(e,t),r("xrange",e),e=function(e,t){return r("replaceStars",e,t),e.trim().replace(a[i.STAR],"")}(e,t),r("stars",e),e}(e,this.options)}),this).join(" ").split(/\s+/);return this.options.loose&&(o=o.filter((function(e){return!!e.match(s)}))),o=o.map((function(e){return new A(e,this.options)}),this)},S.prototype.intersects=function(e,t){if(!(e instanceof S))throw new TypeError("a Range is required");return this.set.some((function(r){return D(r,t)&&e.set.some((function(e){return D(e,t)&&r.every((function(r){return e.every((function(e){return r.intersects(e,t)}))}))}))}))},t.toComparators=function(e,t){return new S(e,t).set.map((function(e){return e.map((function(e){return e.value})).join(" ").trim().split(" ")}))},S.prototype.test=function(e){if(!e)return!1;if("string"==typeof e)try{e=new p(e,this.options)}catch(e){return!1}for(var t=0;t<this.set.length;t++)if(j(this.set[t],e,this.options))return!0;return!1},t.satisfies=P,t.maxSatisfying=function(e,t,r){var n=null,a=null;try{var s=new S(t,r)}catch(e){return null}return e.forEach((function(e){s.test(e)&&(n&&-1!==a.compare(e)||(a=new p(n=e,r)))})),n},t.minSatisfying=function(e,t,r){var n=null,a=null;try{var s=new S(t,r)}catch(e){return null}return e.forEach((function(e){s.test(e)&&(n&&1!==a.compare(e)||(a=new p(n=e,r)))})),n},t.minVersion=function(e,t){e=new S(e,t);var r=new p("0.0.0");if(e.test(r))return r;if(r=new p("0.0.0-0"),e.test(r))return r;r=null;for(var n=0;n<e.set.length;++n){e.set[n].forEach((function(e){var t=new p(e.semver.version);switch(e.operator){case">":0===t.prerelease.length?t.patch++:t.prerelease.push(0),t.raw=t.format();case"":case">=":r&&!m(r,t)||(r=t);break;case"<":case"<=":break;default:throw new Error("Unexpected operation: "+e.operator)}}))}if(r&&e.test(r))return r;return null},t.validRange=function(e,t){try{return new S(e,t).range||"*"}catch(e){return null}},t.ltr=function(e,t,r){return k(e,t,"<",r)},t.gtr=function(e,t,r){return k(e,t,">",r)},t.outside=k,t.prerelease=function(e,t){var r=l(e,t);return r&&r.prerelease.length?r.prerelease:null},t.intersects=function(e,t,r){return e=new S(e,r),t=new S(t,r),e.intersects(t)},t.coerce=function(e,t){if(e instanceof p)return e;"number"==typeof e&&(e=String(e));if("string"!=typeof e)return null;var r=null;if((t=t||{}).rtl){for(var n;(n=a[i.COERCERTL].exec(e))&&(!r||r.index+r[0].length!==e.length);)r&&n.index+n[0].length===r.index+r[0].length||(r=n),a[i.COERCERTL].lastIndex=n.index+n[1].length+n[2].length;a[i.COERCERTL].lastIndex=-1}else r=e.match(a[i.COERCE]);if(null===r)return null;return l(r[2]+"."+(r[3]||"0")+"."+(r[4]||"0"),t)}})),Bw=(Iw.SEMVER_SPEC_VERSION,Iw.re,Iw.src,Iw.tokens,Iw.parse,Iw.valid,Iw.clean,Iw.SemVer,Iw.inc,Iw.diff,Iw.compareIdentifiers,Iw.rcompareIdentifiers,Iw.major,Iw.minor,Iw.patch,Iw.compare,Iw.compareLoose,Iw.compareBuild,Iw.rcompare,Iw.sort,Iw.rsort,Iw.gt,Iw.lt,Iw.eq,Iw.neq,Iw.gte,Iw.lte,Iw.cmp,Iw.Comparator,Iw.Range,Iw.toComparators,Iw.satisfies,Iw.maxSatisfying,Iw.minSatisfying,Iw.minVersion,Iw.validRange,Iw.ltr,Iw.gtr,Iw.outside,Iw.prerelease,Iw.intersects,Iw.coerce),Ow={enter:function(e,t){var r=e.node.loc;r&&(t.loc=r,e.stop())}},Nw=function(){function e(e,t){var r=this,n=t.code,a=t.ast,s=t.inputMap;this._map=new Map,this.declarations={},this.path=null,this.ast={},this.metadata={},this.code="",this.inputMap=null,this.hub={file:this,getCode:function(){return r.code},getScope:function(){return r.scope},addHelper:this.addHelper.bind(this),buildError:this.buildCodeFrameError.bind(this)},this.opts=e,this.code=n,this.ast=a,this.inputMap=s,this.path=VE.get({hub:this.hub,parentPath:null,parent:this.ast,container:this.ast,key:"program"}).setContext(),this.scope=this.path.scope}var t=e.prototype;return t.set=function(e,t){if("helpersNamespace"===e)throw new Error("Babel 7.0.0-beta.56 has dropped support for the 'helpersNamespace' utility.If you are using @babel/plugin-external-helpers you will need to use a newer version than the one you currently have installed. If you have your own implementation, you'll want to explore using 'helperGenerator' alongside 'file.availableHelper()'.");this._map.set(e,t)},t.get=function(e){return this._map.get(e)},t.has=function(e){return this._map.has(e)},t.getModuleName=function(){var e=this.opts,t=e.filename,r=e.filenameRelative,n=void 0===r?t:r,a=e.moduleId,s=e.moduleIds,i=void 0===s?!!a:s,o=e.getModuleId,u=e.sourceRoot,c=e.moduleRoot,l=void 0===c?u:c,p=e.sourceRoot,d=void 0===p?l:p;if(!i)return null;if(null!=a&&!o)return a;var f=null!=l?l+"/":"";if(n){var h=null!=d?new RegExp("^"+d+"/?"):"";f+=n.replace(h,"").replace(/\.(\w*?)$/,"")}return f=f.replace(/\\/g,"/"),o&&o(f)||f},t.addImport=function(){throw new Error("This API has been removed. If you're looking for this functionality in Babel 7, you should import the '@babel/helper-module-imports' module and use the functions exposed from that module, such as 'addNamed' or 'addDefault'.")},t.availableHelper=function(e,t){var r;try{r=function(e){return kw(e).minVersion()}(e)}catch(e){if("BABEL_HELPER_UNKNOWN"!==e.code)throw e;return!1}return"string"!=typeof t||(Iw.valid(t)&&(t="^"+t),!Iw.intersects("<"+r,t)&&!Iw.intersects(">=8.0.0",t))},t.addHelper=function(e){var t=this,r=this.declarations[e];if(r)return pp(r);var n=this.get("helperGenerator");if(n){var a=n(e);if(a)return a}!function(e){kw(e)}(e);var s=this.declarations[e]=this.scope.generateUidIdentifier(e),i={},o=function(e){return Array.from(kw(e).dependencies.values())}(e),u=Array.isArray(o),c=0;for(o=u?o:o[Symbol.iterator]();;){var l;if(u){if(c>=o.length)break;l=o[c++]}else{if((c=o.next()).done)break;l=c.value}var p=l;i[p]=this.addHelper(p)}var d=Fw(e,(function(e){return i[e]}),s,Object.keys(this.scope.getAllBindings())),f=d.nodes;return d.globals.forEach((function(e){t.path.scope.hasBinding(e,!0)&&t.path.scope.rename(e)})),f.forEach((function(e){e._compact=!0})),this.path.unshiftContainer("body",f),this.path.get("body").forEach((function(e){-1!==f.indexOf(e.node)&&e.isVariableDeclaration()&&t.scope.registerDeclaration(e)})),s},t.addTemplateObject=function(){throw new Error("This function has been moved into the template literal transform itself.")},t.buildCodeFrameError=function(e,t,r){void 0===r&&(r=SyntaxError);var n=e&&(e.loc||e._loc);if(!n&&e){var a={loc:null};sA(e,Ow,this.scope,a);var s="This is an error on an internal node. Probably an internal error.";(n=a.loc)&&(s+=" Location has been estimated."),t+=" ("+s+")"}if(n){var i=this.opts.highlightCode,o=void 0===i||i;t+="\n"+sb(this.code,{start:{line:n.start.line,column:n.start.column+1},end:n.end&&n.start.line===n.end.line?{line:n.end.line,column:n.end.column+1}:void 0},{highlightCode:o})}return new r(t)},n(e,[{key:"shebang",get:function(){var e=this.path.node.interpreter;return e?e.value:""},set:function(e){e?this.path.get("interpreter").replaceWith(Bi(e)):this.path.get("interpreter").remove()}}]),e}();function Rw(){var e=i(['\n (function (root, factory) {\n if (typeof define === "function" && define.amd) {\n define(AMD_ARGUMENTS, factory);\n } else if (typeof exports === "object") {\n factory(COMMON_ARGUMENTS);\n } else {\n factory(BROWSER_ARGUMENTS);\n }\n })(UMD_ROOT, function (FACTORY_PARAMETERS) {\n FACTORY_BODY\n });\n ']);return Rw=function(){return e},e}function Mw(e){var t=Qi("babelHelpers"),r=[],n=co([Ki(Li($i(null,[Qi("global")],Ri(r)),[Gi(Ii("===",wo("typeof",Qi("global")),to("undefined")),Qi("self"),Qi("global"))]))]);return r.push(Do("var",[Co(t,_i("=",oo(Qi("global"),t),lo([])))])),Vw(r,t,e),n}function Lw(e){var t=[],r=Vw(t,null,e);return t.unshift(Ro(null,Object.keys(r).map((function(e){return Mo(pp(r[e]),Qi(e))})))),co(t,[],"module")}function Uw(e){var t,r=Qi("babelHelpers"),n=[];return n.push(Do("var",[Co(r,Qi("global"))])),Vw(n,r,e),co([(t={FACTORY_PARAMETERS:Qi("global"),BROWSER_ARGUMENTS:_i("=",oo(Qi("root"),r),lo([])),COMMON_ARGUMENTS:Qi("exports"),AMD_ARGUMENTS:Fi([to("exports")]),FACTORY_BODY:n,UMD_ROOT:Qi("this")},dE(Rw())(t))])}function Gw(e){var t=Qi("babelHelpers"),r=[];r.push(Do("var",[Co(t,lo([]))]));var n=co(r);return Vw(r,t,e),r.push(Ki(t)),n}function Vw(e,t,r){var n=function(e){return t?oo(t,Qi(e)):Qi("_"+e)},a={};return _w.forEach((function(t){if(!(r&&r.indexOf(t)<0)){var s=a[t]=n(t),i=Fw(t,n,s).nodes;e.push.apply(e,i)}})),a}function Ww(e,t){void 0===t&&(t="global");var r={global:Mw,module:Lw,umd:Uw,var:Gw}[t];if(!r)throw new Error("Unsupported output type "+t);return lv(r(e)).code}var Hw=Vt((function(e){var t=function(e){var t=Object.prototype,r=t.hasOwnProperty,n="function"==typeof Symbol?Symbol:{},a=n.iterator||"@@iterator",s=n.asyncIterator||"@@asyncIterator",i=n.toStringTag||"@@toStringTag";function o(e,t,r,n){var a=t&&t.prototype instanceof l?t:l,s=Object.create(a.prototype),i=new A(n||[]);return s._invoke=function(e,t,r){var n="suspendedStart";return function(a,s){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===a)throw s;return S()}for(r.method=a,r.arg=s;;){var i=r.delegate;if(i){var o=b(i,r);if(o){if(o===c)continue;return o}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var l=u(e,t,r);if("normal"===l.type){if(n=r.done?"completed":"suspendedYield",l.arg===c)continue;return{value:l.arg,done:r.done}}"throw"===l.type&&(n="completed",r.method="throw",r.arg=l.arg)}}}(e,r,i),s}function u(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}e.wrap=o;var c={};function l(){}function p(){}function d(){}var f={};f[a]=function(){return this};var h=Object.getPrototypeOf,m=h&&h(h(w([])));m&&m!==t&&r.call(m,a)&&(f=m);var y=d.prototype=l.prototype=Object.create(f);function g(e){["next","throw","return"].forEach((function(t){e[t]=function(e){return this._invoke(t,e)}}))}function v(e,t){var n;this._invoke=function(a,s){function i(){return new t((function(n,i){!function n(a,s,i,o){var c=u(e[a],e,s);if("throw"!==c.type){var l=c.arg,p=l.value;return p&&"object"==typeof p&&r.call(p,"__await")?t.resolve(p.__await).then((function(e){n("next",e,i,o)}),(function(e){n("throw",e,i,o)})):t.resolve(p).then((function(e){l.value=e,i(l)}),(function(e){return n("throw",e,i,o)}))}o(c.arg)}(a,s,n,i)}))}return n=n?n.then(i,i):i()}}function b(e,t){var r=e.iterator[t.method];if(void 0===r){if(t.delegate=null,"throw"===t.method){if(e.iterator.return&&(t.method="return",t.arg=void 0,b(e,t),"throw"===t.method))return c;t.method="throw",t.arg=new TypeError("The iterator does not provide a 'throw' method")}return c}var n=u(r,e.iterator,t.arg);if("throw"===n.type)return t.method="throw",t.arg=n.arg,t.delegate=null,c;var a=n.arg;return a?a.done?(t[e.resultName]=a.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,c):a:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,c)}function x(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function E(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function A(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(x,this),this.reset(!0)}function w(e){if(e){var t=e[a];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var n=-1,s=function t(){for(;++n<e.length;)if(r.call(e,n))return t.value=e[n],t.done=!1,t;return t.value=void 0,t.done=!0,t};return s.next=s}}return{next:S}}function S(){return{value:void 0,done:!0}}return p.prototype=y.constructor=d,d.constructor=p,d[i]=p.displayName="GeneratorFunction",e.isGeneratorFunction=function(e){var t="function"==typeof e&&e.constructor;return!!t&&(t===p||"GeneratorFunction"===(t.displayName||t.name))},e.mark=function(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,d):(e.__proto__=d,i in e||(e[i]="GeneratorFunction")),e.prototype=Object.create(y),e},e.awrap=function(e){return{__await:e}},g(v.prototype),v.prototype[s]=function(){return this},e.AsyncIterator=v,e.async=function(t,r,n,a,s){void 0===s&&(s=Promise);var i=new v(o(t,r,n,a),s);return e.isGeneratorFunction(r)?i:i.next().then((function(e){return e.done?e.value:i.next()}))},g(y),y[i]="Generator",y[a]=function(){return this},y.toString=function(){return"[object Generator]"},e.keys=function(e){var t=[];for(var r in e)t.push(r);return t.reverse(),function r(){for(;t.length;){var n=t.pop();if(n in e)return r.value=n,r.done=!1,r}return r.done=!0,r}},e.values=w,A.prototype={constructor:A,reset:function(e){if(this.prev=0,this.next=0,this.sent=this._sent=void 0,this.done=!1,this.delegate=null,this.method="next",this.arg=void 0,this.tryEntries.forEach(E),!e)for(var t in this)"t"===t.charAt(0)&&r.call(this,t)&&!isNaN(+t.slice(1))&&(this[t]=void 0)},stop:function(){this.done=!0;var e=this.tryEntries[0].completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(e){if(this.done)throw e;var t=this;function n(r,n){return i.type="throw",i.arg=e,t.next=r,n&&(t.method="next",t.arg=void 0),!!n}for(var a=this.tryEntries.length-1;a>=0;--a){var s=this.tryEntries[a],i=s.completion;if("root"===s.tryLoc)return n("end");if(s.tryLoc<=this.prev){var o=r.call(s,"catchLoc"),u=r.call(s,"finallyLoc");if(o&&u){if(this.prev<s.catchLoc)return n(s.catchLoc,!0);if(this.prev<s.finallyLoc)return n(s.finallyLoc)}else if(o){if(this.prev<s.catchLoc)return n(s.catchLoc,!0)}else{if(!u)throw new Error("try statement without catch or finally");if(this.prev<s.finallyLoc)return n(s.finallyLoc)}}}},abrupt:function(e,t){for(var n=this.tryEntries.length-1;n>=0;--n){var a=this.tryEntries[n];if(a.tryLoc<=this.prev&&r.call(a,"finallyLoc")&&this.prev<a.finallyLoc){var s=a;break}}s&&("break"===e||"continue"===e)&&s.tryLoc<=t&&t<=s.finallyLoc&&(s=null);var i=s?s.completion:{};return i.type=e,i.arg=t,s?(this.method="next",this.next=s.finallyLoc,c):this.complete(i)},complete:function(e,t){if("throw"===e.type)throw e.arg;return"break"===e.type||"continue"===e.type?this.next=e.arg:"return"===e.type?(this.rval=this.arg=e.arg,this.method="return",this.next="end"):"normal"===e.type&&t&&(this.next=t),c},finish:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),E(r),c}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var a=n.arg;E(r)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:w(e),resultName:t,nextLoc:r},"next"===this.method&&(this.arg=void 0),c}},e}(e.exports);try{regeneratorRuntime=t}catch(e){Function("r","regeneratorRuntime = r")(t)}})),qw=Hw.mark(Jw),Kw=Hw.mark($w),zw=Hw.mark(Qw),Xw=Hw.mark(Zw),Yw=Hw.mark(eS);function Jw(e){return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",null);case 1:case"end":return e.stop()}}),qw)}function $w(e){return Hw.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.abrupt("return",{filepath:e,directories:[],pkg:null,isPackage:!1});case 1:case"end":return t.stop()}}),Kw)}function Qw(e,t,r){return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",{pkg:null,config:null,ignore:null});case 1:case"end":return e.stop()}}),zw)}function Zw(e,t,r){return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",null);case 1:case"end":return e.stop()}}),Xw)}function eS(e,t,r,n){return Hw.wrap((function(r){for(;;)switch(r.prev=r.next){case 0:throw new Error("Cannot load "+e+" relative to "+t+" in a browser");case 1:case"end":return r.stop()}}),Yw)}var tS=[];function rS(e,t){return null}function nS(e,t){return null}function aS(e,t){throw new Error("Cannot load plugin "+e+" relative to "+t+" in a browser")}function sS(e,t){throw new Error("Cannot load preset "+e+" relative to "+t+" in a browser")}var iS="7.8.7";function oS(e){return void 0===e&&(e="development"),es.env.BABEL_ENV||e}function uS(e,t){for(var r=0,n=e.length-1;n>=0;n--){var a=e[n];"."===a?e.splice(n,1):".."===a?(e.splice(n,1),r++):r&&(e.splice(n,1),r--)}if(t)for(;r--;r)e.unshift("..");return e}var cS=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,lS=function(e){return cS.exec(e).slice(1)};function pS(){for(var e="",t=!1,r=arguments.length-1;r>=-1&&!t;r--){var n=r>=0?arguments[r]:"/";if("string"!=typeof n)throw new TypeError("Arguments to path.resolve must be strings");n&&(e=n+"/"+e,t="/"===n.charAt(0))}return(t?"/":"")+(e=uS(gS(e.split("/"),(function(e){return!!e})),!t).join("/"))||"."}function dS(e){var t=fS(e),r="/"===vS(e,-1);return(e=uS(gS(e.split("/"),(function(e){return!!e})),!t).join("/"))||t||(e="."),e&&r&&(e+="/"),(t?"/":"")+e}function fS(e){return"/"===e.charAt(0)}function hS(e,t){var r=lS(e)[2];return t&&r.substr(-1*t.length)===t&&(r=r.substr(0,r.length-t.length)),r}function mS(e){return lS(e)[3]}var yS={extname:mS,basename:hS,dirname:function(e){var t=lS(e),r=t[0],n=t[1];return r||n?(n&&(n=n.substr(0,n.length-1)),r+n):"."},sep:"/",delimiter:":",relative:function(e,t){function r(e){for(var t=0;t<e.length&&""===e[t];t++);for(var r=e.length-1;r>=0&&""===e[r];r--);return t>r?[]:e.slice(t,r-t+1)}e=pS(e).substr(1),t=pS(t).substr(1);for(var n=r(e.split("/")),a=r(t.split("/")),s=Math.min(n.length,a.length),i=s,o=0;o<s;o++)if(n[o]!==a[o]){i=o;break}var u=[];for(o=i;o<n.length;o++)u.push("..");return(u=u.concat(a.slice(i))).join("/")},join:function(){var e=Array.prototype.slice.call(arguments,0);return dS(gS(e,(function(e,t){if("string"!=typeof e)throw new TypeError("Arguments to path.join must be strings");return e})).join("/"))},isAbsolute:fS,normalize:dS,resolve:pS};function gS(e,t){if(e.filter)return e.filter(t);for(var r=[],n=0;n<e.length;n++)t(e[n],n,e)&&r.push(e[n]);return r}var vS="b"==="ab".substr(-1)?function(e,t,r){return e.substr(t,r)}:function(e,t,r){return t<0&&(t=e.length+t),e.substr(t,r)},bS=Symbol.for("gensync:v1:start"),xS=Symbol.for("gensync:v1:suspend"),ES=Object.assign((function(e){var t=e;return t="function"!=typeof e?function(e){var t=e.name,r=e.arity,n=e.sync,a=e.async,s=e.errback;if(AS("string","name",t,!0),AS("number","arity",r,!0),AS("function","sync",n),AS("function","async",a,!0),AS("function","errback",s,!0),a&&s)throw wS("Expected one of either opts.async or opts.errback, but got _both_.","GENSYNC_OPTIONS_ERROR");if("string"!=typeof t){var i;s&&s.name&&"errback"!==s.name&&(i=s.name),a&&a.name&&"async"!==a.name&&(i=a.name.replace(/Async$/,"")),n&&n.name&&"sync"!==n.name&&(i=n.name.replace(/Sync$/,"")),"string"==typeof i&&(t=i)}"number"!=typeof r&&(r=n.length);return SS({name:t,arity:r,sync:function(e){return n.apply(this,e)},async:function(e,t,r){a?a.apply(this,e).then(t,r):s?s.call.apply(s,[this].concat(e,[function(e,n){null==e?t(n):r(e)}])):t(n.apply(this,e))}})}(e):function(e){return PS(e.name,e.length,(function(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return e.apply(this,r)}))}(e),Object.assign(t,function(e){return{sync:function(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return DS(e.apply(this,r))},async:function(){for(var t=this,r=arguments.length,n=new Array(r),a=0;a<r;a++)n[a]=arguments[a];return new Promise((function(r,a){CS(e.apply(t,n),r,a)}))},errback:function(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];var a,s=r.pop();if("function"!=typeof s)throw wS("Asynchronous function called without callback","GENSYNC_ERRBACK_NO_CALLBACK");try{a=e.apply(this,r)}catch(e){return void s(e)}CS(a,(function(e){return s(void 0,e)}),(function(e){return s(e)}))}}}(t))}),{all:SS({name:"all",arity:1,sync:function(e){return Array.from(e[0]).map((function(e){return DS(e)}))},async:function(e,t,r){var n=Array.from(e[0]),a=0,s=n.map((function(){}));n.forEach((function(e,n){CS(e,(function(e){s[n]=e,(a+=1)===s.length&&t(s)}),r)}))}}),race:SS({name:"race",arity:1,sync:function(e){var t=Array.from(e[0]);if(0===t.length)throw wS("Must race at least 1 item","GENSYNC_RACE_NONEMPTY");return DS(t[0])},async:function(e,t,r){var n=Array.from(e[0]);if(0===n.length)throw wS("Must race at least 1 item","GENSYNC_RACE_NONEMPTY");for(var a=0,s=n;a<s.length;a++){CS(s[a],t,r)}}})});function AS(e,t,r,n){if(!(typeof r===e||n&&void 0===r))throw wS(n?"Expected opts."+t+" to be either a "+e+", or undefined.":"Expected opts."+t+" to be a "+e+".","GENSYNC_OPTIONS_ERROR")}function wS(e,t){return Object.assign(new Error(e),{code:t})}function SS(e){var t=e.name,r=e.arity,n=e.sync,a=e.async;return PS(t,r,Hw.mark((function e(){var t,r,s,i,o,u=arguments;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,bS;case 2:for(t=e.sent,r=u.length,s=new Array(r),i=0;i<r;i++)s[i]=u[i];if(t){e.next=6;break}return e.abrupt("return",n.call(this,s));case 6:try{a.call(this,s,(function(e){o||(o={value:e},t())}),(function(e){o||(o={err:e},t())}))}catch(e){o={err:e},t()}return e.next=9,xS;case 9:if(!o.hasOwnProperty("err")){e.next=11;break}throw o.err;case 11:return e.abrupt("return",o.value);case 12:case"end":return e.stop()}}),e,this)})))}function DS(e){for(var t;!(r=e.next(),t=r.value,r).done;){var r;TS(t,e)}return t}function CS(e,t,r){!function n(){try{for(var a,s=function(){TS(a,e);var t=!0,r=!1,s=e.next((function(){t?r=!0:n()}));if(t=!1,function(e,t){var r=e.value,n=e.done;if(!n&&r===xS)return;jS(t,wS(n?"Unexpected generator completion. If you get this, it is probably a gensync bug.":"Expected GENSYNC_SUSPEND, got "+JSON.stringify(r)+". If you get this, it is probably a gensync bug.","GENSYNC_EXPECTED_SUSPEND"))}(s,e),!r)return{v:void 0}};!(i=e.next(),a=i.value,i).done;){var i,o=s();if("object"==typeof o)return o.v}return t(a)}catch(e){return r(e)}}()}function TS(e,t){e!==bS&&jS(t,wS("Got unexpected yielded value in gensync generator: "+JSON.stringify(e)+". Did you perhaps mean to use 'yield*' instead of 'yield'?","GENSYNC_EXPECTED_START"))}function jS(e,t){throw e.throw&&e.throw(t),t}function PS(e,t,r){if("string"==typeof e){var n=Object.getOwnPropertyDescriptor(r,"name");n&&!n.configurable||Object.defineProperty(r,"name",Object.assign(n||{},{configurable:!0,value:e}))}if("number"==typeof t){var a=Object.getOwnPropertyDescriptor(r,"length");a&&!a.configurable||Object.defineProperty(r,"length",Object.assign(a||{},{configurable:!0,value:t}))}return r}var kS=function(e){return e},FS=ES(Hw.mark((function e(t){return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(t,"t0",1);case 1:return e.abrupt("return",e.t0);case 2:case"end":return e.stop()}}),e)}))),_S=ES({sync:function(){return!1},errback:function(e){return e(null,!0)}});var IS=ES({sync:function(e){return e("sync")},async:function(e){return e("async")}});function BS(e,t){var r=ES(e);return IS((function(e){var n=r[e];return t(n)}))}var OS=ES({name:"onFirstPause",arity:2,sync:function(e){return FS.sync(e)},errback:function(e,t,r){var n=!1;FS.errback(e,(function(e,t){n=!0,r(e,t)})),n||t()}}),NS=ES({sync:kS,async:kS});function RS(e){return!(!e||"object"!=typeof e&&"function"!=typeof e||!e.then||"function"!=typeof e.then)}function MS(e,t){for(var r=0,n=Object.keys(t);r<n.length;r++){var a=n[r];if("parserOpts"===a&&t.parserOpts){var s=t.parserOpts;LS(e.parserOpts=e.parserOpts||{},s)}else if("generatorOpts"===a&&t.generatorOpts){var i=t.generatorOpts;LS(e.generatorOpts=e.generatorOpts||{},i)}else{var o=t[a];void 0!==o&&(e[a]=o)}}}function LS(e,t){for(var r=0,n=Object.keys(t);r<n.length;r++){var a=n[r],s=t[a];void 0!==s&&(e[a]=s)}}function US(e){return!!e&&"function"==typeof e.next&&"function"==typeof e[Symbol.iterator]}var GS=Hw.mark(qS),VS=Hw.mark(JS),WS=Hw.mark($S),HS=function(e){return ES(e).sync};function qS(e){return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",!0);case 1:case"end":return e.stop()}}),GS)}function KS(e){return YS(WeakMap,e)}function zS(e){return HS(KS(e))}function XS(e){return HS(function(e){return YS(Map,e)}(e))}function YS(e,t){var r=new e,n=new e,a=new e;return Hw.mark((function e(s,i){var o,u,c,l,p,d,f,h;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(_S(),"t0",1);case 1:return o=e.t0,u=o?n:r,e.delegateYield($S(o,u,a,s,i),"t1",4);case 4:if(!(c=e.t1).valid){e.next=7;break}return e.abrupt("return",c.value);case 7:if(l=new eD(i),!US(p=t(s,l))){e.next=15;break}return h=p,e.delegateYield(OS(h,(function(){d=QS(l,a,s)})),"t2",12);case 12:f=e.t2,e.next=16;break;case 15:f=p;case 16:return ZS(u,l,s,f),d&&(a.delete(s),d.release(f)),e.abrupt("return",f);case 19:case"end":return e.stop()}}),e)}))}function JS(e,t,r){var n,a,s,i,o,u,c,l;return Hw.wrap((function(p){for(;;)switch(p.prev=p.next){case 0:if(!(n=e.get(t))){p.next=19;break}a=n,s=Array.isArray(a),i=0,a=s?a:a[Symbol.iterator]();case 3:if(!s){p.next=9;break}if(!(i>=a.length)){p.next=6;break}return p.abrupt("break",19);case 6:o=a[i++],p.next=13;break;case 9:if(!(i=a.next()).done){p.next=12;break}return p.abrupt("break",19);case 12:o=i.value;case 13:return c=(u=o).value,l=u.valid,p.delegateYield(l(r),"t0",15);case 15:if(!p.t0){p.next=17;break}return p.abrupt("return",{valid:!0,value:c});case 17:p.next=3;break;case 19:return p.abrupt("return",{valid:!1,value:null});case 20:case"end":return p.stop()}}),VS)}function $S(e,t,r,n,a){var s,i,o;return Hw.wrap((function(u){for(;;)switch(u.prev=u.next){case 0:return u.delegateYield(JS(t,n,a),"t0",1);case 1:if(!(s=u.t0).valid){u.next=4;break}return u.abrupt("return",s);case 4:if(!e){u.next=11;break}return u.delegateYield(JS(r,n,a),"t1",6);case 6:if(!(i=u.t1).valid){u.next=11;break}return u.delegateYield(NS(i.value.promise),"t2",9);case 9:return o=u.t2,u.abrupt("return",{valid:!0,value:o});case 11:return u.abrupt("return",{valid:!1,value:null});case 12:case"end":return u.stop()}}),WS)}function QS(e,t,r){var n=new rD;return ZS(t,e,r,n),n}function ZS(e,t,r,n){t.configured()||t.forever();var a=e.get(r);switch(t.deactivate(),t.mode()){case"forever":a=[{value:n,valid:qS}],e.set(r,a);break;case"invalidate":a=[{value:n,valid:t.validator()}],e.set(r,a);break;case"valid":a?a.push({value:n,valid:t.validator()}):(a=[{value:n,valid:t.validator()}],e.set(r,a))}}var eD=function(){function e(e){this._active=!0,this._never=!1,this._forever=!1,this._invalidate=!1,this._configured=!1,this._pairs=[],this._data=e}var t=e.prototype;return t.simple=function(){return function(e){function t(t){if("boolean"!=typeof t)return e.using((function(){return tD(t())}));t?e.forever():e.never()}return t.forever=function(){return e.forever()},t.never=function(){return e.never()},t.using=function(t){return e.using((function(){return tD(t())}))},t.invalidate=function(t){return e.invalidate((function(){return tD(t())}))},t}(this)},t.mode=function(){return this._never?"never":this._forever?"forever":this._invalidate?"invalidate":"valid"},t.forever=function(){if(!this._active)throw new Error("Cannot change caching after evaluation has completed.");if(this._never)throw new Error("Caching has already been configured with .never()");this._forever=!0,this._configured=!0},t.never=function(){if(!this._active)throw new Error("Cannot change caching after evaluation has completed.");if(this._forever)throw new Error("Caching has already been configured with .forever()");this._never=!0,this._configured=!0},t.using=function(e){var t=this;if(!this._active)throw new Error("Cannot change caching after evaluation has completed.");if(this._never||this._forever)throw new Error("Caching has already been configured with .never or .forever()");this._configured=!0;var r=e(this._data),n=function(e,t){return ES({sync:function(){for(var r=arguments.length,n=new Array(r),a=0;a<r;a++)n[a]=arguments[a];var s=e.apply(this,n);if(RS(s))throw new Error(t);return s},async:function(){for(var t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return Promise.resolve(e.apply(this,r))}})}(e,"You appear to be using an async cache handler, but Babel has been called synchronously");return RS(r)?r.then((function(e){return t._pairs.push([e,n]),e})):(this._pairs.push([r,n]),r)},t.invalidate=function(e){return this._invalidate=!0,this.using(e)},t.validator=function(){var e=this._pairs;return Hw.mark((function t(r){var n,a,s,i,o,u,c;return Hw.wrap((function(t){for(;;)switch(t.prev=t.next){case 0:n=e,a=Array.isArray(n),s=0,n=a?n:n[Symbol.iterator]();case 1:if(!a){t.next=7;break}if(!(s>=n.length)){t.next=4;break}return t.abrupt("break",19);case 4:i=n[s++],t.next=11;break;case 7:if(!(s=n.next()).done){t.next=10;break}return t.abrupt("break",19);case 10:i=s.value;case 11:return u=(o=i)[0],c=o[1],t.t0=u,t.delegateYield(c(r),"t1",14);case 14:if(t.t2=t.t1,t.t0===t.t2){t.next=17;break}return t.abrupt("return",!1);case 17:t.next=1;break;case 19:return t.abrupt("return",!0);case 20:case"end":return t.stop()}}),t)}))},t.deactivate=function(){this._active=!1},t.configured=function(){return this._configured},e}();function tD(e){if(RS(e))throw new Error("You appear to be using an async cache handler, which your current version of Babel does not support. We may add support for this in the future, but if you're on the most recent version of @babel/core and still seeing this error, then you'll need to synchronously handle your caching logic.");if(null!=e&&"string"!=typeof e&&"boolean"!=typeof e&&"number"!=typeof e)throw new Error("Cache keys must be either string, boolean, number, null, or undefined.");return e}var rD=function(){function e(){var e=this;this.released=!1,this.promise=new Promise((function(t){e._resolve=t}))}return e.prototype.release=function(e){this.released=!0,this._resolve(e)},e}();function nD(e,t,r){var n=t.plugins,a=t.presets,s=t.passPerPreset;return{options:t,plugins:n?function(){return uD(n,e)(r)}:function(){return[]},presets:a?function(){return iD(a,e)(r)(!!s)}:function(){return[]}}}function aD(e,t,r){var n,a;return{options:t,plugins:function(){return n||(n=dD(t.plugins||[],e,r)),n},presets:function(){return a||(a=pD(t.presets||[],e,r,!!t.passPerPreset)),a}}}var sD=new WeakMap,iD=zS((function(e,t){var r=t.using((function(e){return e}));return XS((function(t){return XS((function(n){return pD(e,r,t,n).map((function(e){return lD(sD,e)}))}))}))})),oD=new WeakMap,uD=zS((function(e,t){var r=t.using((function(e){return e}));return XS((function(t){return dD(e,r,t).map((function(e){return lD(oD,e)}))}))})),cD={};function lD(e,t){var r=t.value,n=t.options,a=void 0===n?cD:n;if(!1===a)return t;var s=e.get(r);s||(s=new WeakMap,e.set(r,s));var i=s.get(a);if(i||(i=[],s.set(a,i)),-1===i.indexOf(t)){var o=i.filter((function(e){return n=t,(r=e).name===n.name&&r.value===n.value&&r.options===n.options&&r.dirname===n.dirname&&r.alias===n.alias&&r.ownPass===n.ownPass&&(r.file&&r.file.request)===(n.file&&n.file.request)&&(r.file&&r.file.resolved)===(n.file&&n.file.resolved);var r,n}));if(o.length>0)return o[0];i.push(t)}return t}function pD(e,t,r,n){return fD("preset",e,t,r,n)}function dD(e,t,r){return fD("plugin",e,t,r)}function fD(e,t,r,n,a){var s=t.map((function(t,s){return hD(t,r,{type:e,alias:n+"$"+s,ownPass:!!a})}));return function(e){var t=new Map,r=function(){if(a){if(s>=n.length)return"break";i=n[s++]}else{if((s=n.next()).done)return"break";i=s.value}var r=i;if("function"!=typeof r.value)return"continue";var o=t.get(r.value);if(o||(o=new Set,t.set(r.value,o)),o.has(r.name)){var u=e.filter((function(e){return e.value===r.value}));throw new Error(["Duplicate plugin/preset detected.","If you'd like to use two separate instances of a plugin,","they need separate names, e.g.",""," plugins: ["," ['some-plugin', {}],"," ['some-plugin', {}, 'some unique name'],"," ]","","Duplicates detected are:",""+JSON.stringify(u,null,2)].join("\n"))}o.add(r.name)};var n=e,a=Array.isArray(n),s=0;e:for(n=a?n:n[Symbol.iterator]();;){var i;switch(r()){case"break":break e;case"continue":continue}}}(s),s}function hD(e,t,r){var n,a,s=r.type,i=r.alias,o=r.ownPass,u=gD(e);if(u)return u;var c=e;if(Array.isArray(c))if(3===c.length){var l=c;c=l[0],a=l[1],n=l[2]}else{var p=c;c=p[0],a=p[1]}var d=void 0,f=null;if("string"==typeof c){if("string"!=typeof s)throw new Error("To resolve a string-based item, the type of item must be given");var h=c,m=("plugin"===s?aS:sS)(c,t);f=m.filepath,c=m.value,d={request:h,resolved:f}}if(!c)throw new Error("Unexpected falsy value: "+String(c));if("object"==typeof c&&c.__esModule){if(!c.default)throw new Error("Must export a default export when using ES6 modules.");c=c.default}if("object"!=typeof c&&"function"!=typeof c)throw new Error("Unsupported format: "+typeof c+". Expected an object or a function.");if(null!==f&&"object"==typeof c&&c)throw new Error("Plugin/Preset files are not allowed to export objects, only functions. In "+f);return{name:n,alias:f||i,value:c,options:a,dirname:t,ownPass:o,file:d}}function mD(e){return new vD(e)}function yD(e,t){var r=void 0===t?{}:t,n=r.dirname,a=void 0===n?".":n,s=r.type;return mD(hD(e,yS.resolve(a),{type:s,alias:"programmatic item"}))}function gD(e){if(e instanceof vD)return e._descriptor}var vD=function(e){this._descriptor=e,Object.defineProperty(this,"_descriptor",{enumerable:!1}),this.value=this._descriptor.value,this.options=this._descriptor.options,this.dirname=this._descriptor.dirname,this.name=this._descriptor.name,this.file=this._descriptor.file?{request:this._descriptor.file.request,resolved:this._descriptor.file.resolved}:void 0,Object.freeze(this)};Object.freeze(vD.prototype);var bD=function(e,t,r){this.key=e.name||r,this.manipulateOptions=e.manipulateOptions,this.post=e.post,this.pre=e.pre,this.visitor=e.visitor||{},this.parserOverride=e.parserOverride,this.generatorOverride=e.generatorOverride,this.options=t},xD=864e5,ED=function(e,t){t=t||{};var r=typeof e;if("string"===r&&e.length>0)return function(e){if((e=String(e)).length>100)return;var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!t)return;var r=parseFloat(t[1]);switch((t[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return 315576e5*r;case"weeks":case"week":case"w":return 6048e5*r;case"days":case"day":case"d":return r*xD;case"hours":case"hour":case"hrs":case"hr":case"h":return 36e5*r;case"minutes":case"minute":case"mins":case"min":case"m":return 6e4*r;case"seconds":case"second":case"secs":case"sec":case"s":return 1e3*r;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}(e);if("number"===r&&isFinite(e))return t.long?function(e){var t=Math.abs(e);if(t>=xD)return AD(e,t,xD,"day");if(t>=36e5)return AD(e,t,36e5,"hour");if(t>=6e4)return AD(e,t,6e4,"minute");if(t>=1e3)return AD(e,t,1e3,"second");return e+" ms"}(e):function(e){var t=Math.abs(e);if(t>=xD)return Math.round(e/xD)+"d";if(t>=36e5)return Math.round(e/36e5)+"h";if(t>=6e4)return Math.round(e/6e4)+"m";if(t>=1e3)return Math.round(e/1e3)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function AD(e,t,r,n){var a=t>=1.5*r;return Math.round(e/r)+" "+n+(a?"s":"")}var wD=function(e){function t(e){for(var t=0,n=0;n<e.length;n++)t=(t<<5)-t+e.charCodeAt(n),t|=0;return r.colors[Math.abs(t)%r.colors.length]}function r(e){var s;function i(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];if(i.enabled){var a=i,o=Number(new Date),u=o-(s||o);a.diff=u,a.prev=s,a.curr=o,s=o,t[0]=r.coerce(t[0]),"string"!=typeof t[0]&&t.unshift("%O");var c=0;t[0]=t[0].replace(/%([a-zA-Z%])/g,(function(e,n){if("%%"===e)return e;c++;var s=r.formatters[n];if("function"==typeof s){var i=t[c];e=s.call(a,i),t.splice(c,1),c--}return e})),r.formatArgs.call(a,t);var l=a.log||r.log;l.apply(a,t)}}return i.namespace=e,i.enabled=r.enabled(e),i.useColors=r.useColors(),i.color=t(e),i.destroy=n,i.extend=a,"function"==typeof r.init&&r.init(i),r.instances.push(i),i}function n(){var e=r.instances.indexOf(this);return-1!==e&&(r.instances.splice(e,1),!0)}function a(e,t){var n=r(this.namespace+(void 0===t?":":t)+e);return n.log=this.log,n}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return r.debug=r,r.default=r,r.coerce=function(e){if(e instanceof Error)return e.stack||e.message;return e},r.disable=function(){var e=[].concat(r.names.map(s),r.skips.map(s).map((function(e){return"-"+e}))).join(",");return r.enable(""),e},r.enable=function(e){var t;r.save(e),r.names=[],r.skips=[];var n=("string"==typeof e?e:"").split(/[\s,]+/),a=n.length;for(t=0;t<a;t++)n[t]&&("-"===(e=n[t].replace(/\*/g,".*?"))[0]?r.skips.push(new RegExp("^"+e.substr(1)+"$")):r.names.push(new RegExp("^"+e+"$")));for(t=0;t<r.instances.length;t++){var s=r.instances[t];s.enabled=r.enabled(s.namespace)}},r.enabled=function(e){if("*"===e[e.length-1])return!0;var t,n;for(t=0,n=r.skips.length;t<n;t++)if(r.skips[t].test(e))return!1;for(t=0,n=r.names.length;t<n;t++)if(r.names[t].test(e))return!0;return!1},r.humanize=ED,Object.keys(e).forEach((function(t){r[t]=e[t]})),r.instances=[],r.names=[],r.skips=[],r.formatters={},r.selectColor=t,r.enable(r.load()),r},SD=Vt((function(e,t){t.log=function(){var e;return"object"==typeof console&&console.log&&(e=console).log.apply(e,arguments)},t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;var r="color: "+this.color;t.splice(1,0,r,"color: inherit");var n=0,a=0;t[0].replace(/%[a-zA-Z%]/g,(function(e){"%%"!==e&&(n++,"%c"===e&&(a=n))})),t.splice(a,0,r)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){var e;try{e=t.storage.getItem("debug")}catch(e){}!e&&void 0!==es&&"env"in es&&(e=es.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],e.exports=wD(t),e.exports.formatters.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}})),DD=(SD.log,SD.formatArgs,SD.save,SD.load,SD.useColors,SD.storage,SD.colors,{auxiliaryComment:{message:"Use `auxiliaryCommentBefore` or `auxiliaryCommentAfter`"},blacklist:{message:"Put the specific transforms you want in the `plugins` option"},breakConfig:{message:"This is not a necessary option in Babel 6"},experimental:{message:"Put the specific transforms you want in the `plugins` option"},externalHelpers:{message:"Use the `external-helpers` plugin instead. Check out http://babeljs.io/docs/plugins/external-helpers/"},extra:{message:""},jsxPragma:{message:"use the `pragma` option in the `react-jsx` plugin. Check out http://babeljs.io/docs/plugins/transform-react-jsx/"},loose:{message:"Specify the `loose` option for the relevant plugin you are using or use a preset that sets the option."},metadataUsedHelpers:{message:"Not required anymore as this is enabled by default"},modules:{message:"Use the corresponding module transform plugin in the `plugins` option. Check out http://babeljs.io/docs/plugins/#modules"},nonStandard:{message:"Use the `react-jsx` and `flow-strip-types` plugins to support JSX and Flow. Also check out the react preset http://babeljs.io/docs/plugins/preset-react/"},optional:{message:"Put the specific transforms you want in the `plugins` option"},sourceMapName:{message:"The `sourceMapName` option has been removed because it makes more sense for the tooling that calls Babel to assign `map.file` themselves."},stage:{message:"Check out the corresponding stage-x presets http://babeljs.io/docs/plugins/#presets"},whitelist:{message:"Put the specific transforms you want in the `plugins` option"},resolveModuleSource:{version:6,message:"Use `babel-plugin-module-resolver@3`'s 'resolvePath' options"},metadata:{version:6,message:"Generated plugin metadata is always included in the output result"},sourceMapTarget:{version:6,message:"The `sourceMapTarget` option has been removed because it makes more sense for the tooling that calls Babel to assign `map.file` themselves."}});function CD(e){switch(e.type){case"root":return"";case"env":return CD(e.parent)+'.env["'+e.name+'"]';case"overrides":return CD(e.parent)+".overrides["+e.index+"]";case"option":return CD(e.parent)+"."+e.name;case"access":return CD(e.parent)+"["+JSON.stringify(e.name)+"]";default:throw new Error("Assertion failure: Unknown type "+e.type)}}function TD(e,t){return{type:"access",name:t,parent:e}}function jD(e,t){if(void 0!==t&&"boolean"!=typeof t&&"inline"!==t&&"both"!==t)throw new Error(CD(e)+' must be a boolean, "inline", "both", or undefined');return t}function PD(e,t){if(void 0!==t&&"string"!=typeof t)throw new Error(CD(e)+" must be a string, or undefined");return t}function kD(e,t){if(void 0!==t&&"function"!=typeof t)throw new Error(CD(e)+" must be a function, or undefined");return t}function FD(e,t){if(void 0!==t&&"boolean"!=typeof t)throw new Error(CD(e)+" must be a boolean, or undefined");return t}function _D(e,t){if(void 0!==t&&("object"!=typeof t||Array.isArray(t)||!t))throw new Error(CD(e)+" must be an object, or undefined");return t}function ID(e,t){if(null!=t&&!Array.isArray(t))throw new Error(CD(e)+" must be an array, or undefined");return t}function BD(e,t){var r=ID(e,t);return r&&r.forEach((function(t,r){return function(e,t){if("string"!=typeof t&&"function"!=typeof t&&!(t instanceof RegExp))throw new Error(CD(e)+" must be an array of string/Function/RegExp values, or undefined");return t}(TD(e,r),t)})),r}function OD(e,t){if(void 0===t)return t;if(Array.isArray(t))t.forEach((function(t,r){if(!ND(t))throw new Error(CD(TD(e,r))+" must be a string/Function/RegExp.")}));else if(!ND(t))throw new Error(CD(e)+" must be a string/Function/RegExp, or an array of those");return t}function ND(e){return"string"==typeof e||"function"==typeof e||e instanceof RegExp}function RD(e,t){var r=ID(e,t);return r&&r.forEach((function(t,r){return function(e,t){if(Array.isArray(t)){if(0===t.length)throw new Error(CD(e)+" must include an object");if(t.length>3)throw new Error(CD(e)+" may only be a two-tuple or three-tuple");if(MD(TD(e,0),t[0]),t.length>1){var r=t[1];if(void 0!==r&&!1!==r&&("object"!=typeof r||Array.isArray(r)||null===r))throw new Error(CD(TD(e,1))+" must be an object, false, or undefined")}if(3===t.length){var n=t[2];if(void 0!==n&&"string"!=typeof n)throw new Error(CD(TD(e,2))+" must be a string, or undefined")}}else MD(e,t);return t}(TD(e,r),t)})),r}function MD(e,t){if(("object"!=typeof t||!t)&&"string"!=typeof t&&"function"!=typeof t)throw new Error(CD(e)+" must be a string, object, function");return t}var LD={cwd:PD,root:PD,rootMode:function(e,t){if(void 0!==t&&"root"!==t&&"upward"!==t&&"upward-optional"!==t)throw new Error(CD(e)+' must be a "root", "upward", "upward-optional" or undefined');return t},configFile:function(e,t){if(void 0!==t&&"boolean"!=typeof t&&"string"!=typeof t)throw new Error(CD(e)+" must be a undefined, a boolean, a string, got "+JSON.stringify(t));return t},caller:function(e,t){var r=_D(e,t);if(r){if("string"!=typeof r.name)throw new Error(CD(e)+' set but does not contain "name" property string');for(var n=0,a=Object.keys(r);n<a.length;n++){var s=a[n],i=TD(e,s),o=r[s];if(null!=o&&"boolean"!=typeof o&&"string"!=typeof o&&"number"!=typeof o)throw new Error(CD(i)+" must be null, undefined, a boolean, a string, or a number.")}}return t},filename:PD,filenameRelative:PD,code:FD,ast:FD,envName:PD},UD={babelrc:FD,babelrcRoots:function(e,t){if(void 0===t||"boolean"==typeof t)return t;if(Array.isArray(t))t.forEach((function(t,r){if(!ND(t))throw new Error(CD(TD(e,r))+" must be a string/Function/RegExp.")}));else if(!ND(t))throw new Error(CD(e)+" must be a undefined, a boolean, a string/Function/RegExp or an array of those, got "+JSON.stringify(t));return t}},GD={extends:PD,ignore:BD,only:BD},VD={inputSourceMap:function(e,t){if(void 0!==t&&"boolean"!=typeof t&&("object"!=typeof t||!t))throw new Error(CD(e)+" must be a boolean, object, or undefined");return t},presets:RD,plugins:RD,passPerPreset:FD,env:function(e,t){if("env"===e.parent.type)throw new Error(CD(e)+" is not allowed inside of another .env block");var r=e.parent,n=_D(e,t);if(n)for(var a=0,s=Object.keys(n);a<s.length;a++){var i=s[a],o=_D(TD(e,i),n[i]);if(o)HD({type:"env",name:i,parent:r},o)}return n},overrides:function(e,t){if("env"===e.parent.type)throw new Error(CD(e)+" is not allowed inside an .env block");if("overrides"===e.parent.type)throw new Error(CD(e)+" is not allowed inside an .overrides block");var r=e.parent,n=ID(e,t);if(n){var a=n.entries(),s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o,c=u[0],l=u[1],p=TD(e,c),d=_D(p,l);if(!d)throw new Error(CD(p)+" must be an object");HD({type:"overrides",index:c,parent:r},d)}}return n},test:OD,include:OD,exclude:OD,retainLines:FD,comments:FD,shouldPrintComment:kD,compact:function(e,t){if(void 0!==t&&"boolean"!=typeof t&&"auto"!==t)throw new Error(CD(e)+' must be a boolean, "auto", or undefined');return t},minified:FD,auxiliaryCommentBefore:PD,auxiliaryCommentAfter:PD,sourceType:function(e,t){if(void 0!==t&&"module"!==t&&"script"!==t&&"unambiguous"!==t)throw new Error(CD(e)+' must be "module", "script", "unambiguous", or undefined');return t},wrapPluginVisitorMethod:kD,highlightCode:FD,sourceMaps:jD,sourceMap:jD,sourceFileName:PD,sourceRoot:PD,getModuleId:kD,moduleRoot:PD,moduleIds:FD,moduleId:PD,parserOpts:_D,generatorOpts:_D};function WD(e,t){return HD({type:"root",source:e},t)}function HD(e,t){var r=function e(t){return"root"===t.type?t.source:e(t.parent)}(e);return function(e){if(KD(e,"sourceMap")&&KD(e,"sourceMaps"))throw new Error(".sourceMap is an alias for .sourceMaps, cannot use both")}(t),Object.keys(t).forEach((function(n){var a={type:"option",name:n,parent:e};if("preset"===r&&GD[n])throw new Error(CD(a)+" is not allowed in preset options");if("arguments"!==r&&LD[n])throw new Error(CD(a)+" is only allowed in root programmatic options");if("arguments"!==r&&"configfile"!==r&&UD[n]){if("babelrcfile"===r||"extendsfile"===r)throw new Error(CD(a)+' is not allowed in .babelrc or "extends"ed files, only in root programmatic options, or babel.config.js/config file options');throw new Error(CD(a)+" is only allowed in root programmatic options, or babel.config.js/config file options")}(VD[n]||GD[n]||UD[n]||LD[n]||qD)(a,t[n])})),t}function qD(e){var t=e.name;if(DD[t]){var r=DD[t],n=r.message,a=r.version;throw new Error("Using removed Babel "+(void 0===a?5:a)+" option: "+CD(e)+" - "+n)}var s=new Error("Unknown option: "+CD(e)+". Check out https://babeljs.io/docs/en/babel-core/#options for more information about options.");throw s.code="BABEL_UNKNOWN_OPTION",s}function KD(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function zD(e,t,r,n,a){e.file&&void 0===e.options&&"object"==typeof t.value&&(a.message+='\n- Maybe you meant to use\n"'+r+'": [\n ["'+e.file.request+'", '+JSON.stringify(t.value,void 0,2)+"]\n]\nTo be a valid "+r+", its name and options should be wrapped in a pair of brackets")}var XD=/[\\^$.*+?()[\]{}|]/g,YD=RegExp(XD.source);var JD=function(e){return(e=eh(e))&&YD.test(e)?e.replace(XD,"\\$&"):e},$D="\\"+yS.sep,QD="(?:"+$D+"|$)",ZD="[^"+$D+"]+",eC="(?:"+ZD+$D+")",tC="(?:"+ZD+QD+")",rC=eC+"*?",nC=eC+"*?"+tC+"?";function aC(e,t){var r=yS.resolve(t,e).split(yS.sep);return new RegExp(["^"].concat(r.map((function(e,t){var n=t===r.length-1;return"**"===e?n?nC:rC:"*"===e?n?tC:eC:0===e.indexOf("*.")?ZD+JD(e.slice(1))+(n?QD:$D):JD(e)+(n?QD:$D)}))).join(""))}var sC=Hw.mark(cC),iC=Hw.mark(mC),oC=Hw.mark(FC),uC=SD("babel:config:config-chain");function cC(e,t){var r;return Hw.wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return n.delegateYield(lC(e,t),"t0",1);case 1:if(r=n.t0){n.next=4;break}return n.abrupt("return",null);case 4:return n.abrupt("return",{plugins:OC(r.plugins),presets:OC(r.presets),options:r.options.map((function(e){return BC(e)}))});case 5:case"end":return n.stop()}}),sC)}var lC=kC({init:function(e){return e},root:function(e){return pC(e)},env:function(e,t){return dC(e)(t)},overrides:function(e,t){return fC(e)(t)},overridesEnv:function(e,t,r){return hC(e)(t)(r)}}),pC=zS((function(e){return CC(e,e.alias,aD)})),dC=zS((function(e){return XS((function(t){return TC(e,e.alias,aD,t)}))})),fC=zS((function(e){return XS((function(t){return jC(e,e.alias,aD,t)}))})),hC=zS((function(e){return XS((function(t){return XS((function(r){return PC(e,e.alias,aD,t,r)}))}))}));function mC(e,t){var r,n,a,s,i,o,u,c,l,p,d,f,h,m,y;return Hw.wrap((function(g){for(;;)switch(g.prev=g.next){case 0:return g.delegateYield(xC({options:e,dirname:t.cwd},t),"t0",1);case 1:if(r=g.t0){g.next=4;break}return g.abrupt("return",null);case 4:if("string"!=typeof e.configFile){g.next=9;break}return g.delegateYield(eS(e.configFile,t.cwd,t.envName,t.caller),"t1",6);case 6:n=g.t1,g.next=12;break;case 9:if(!1===e.configFile){g.next=12;break}return g.delegateYield(Zw(t.root,t.envName,t.caller),"t2",11);case 11:n=g.t2;case 12:if(a=e.babelrc,s=e.babelrcRoots,i=t.cwd,o={options:[],presets:[],plugins:[]},!n){g.next=24;break}return u=gC(n),g.delegateYield(EC(u,t),"t3",18);case 18:if(c=g.t3){g.next=21;break}return g.abrupt("return",null);case 21:void 0===a&&(a=u.options.babelrc),void 0===s&&(i=u.dirname,s=u.options.babelrcRoots),_C(o,c);case 24:if("string"!=typeof t.filename){g.next=29;break}return g.delegateYield($w(t.filename),"t5",26);case 26:g.t4=g.t5,g.next=30;break;case 29:g.t4=null;case 30:if(l=g.t4,f={options:[],presets:[],plugins:[]},!0!==a&&void 0!==a||!l||!yC(t,l,s,i)){g.next=45;break}return g.delegateYield(Qw(0,t.envName,t.caller),"t6",34);case 34:if(h=g.t6,p=h.ignore,d=h.config,!p||!MC(t,p.ignore,null,p.dirname)){g.next=39;break}return g.abrupt("return",null);case 39:if(!d){g.next=45;break}return g.delegateYield(EC(vC(d),t),"t7",41);case 41:if(m=g.t7){g.next=44;break}return g.abrupt("return",null);case 44:_C(f,m);case 45:return y=_C(_C(_C({options:[],presets:[],plugins:[]},o),f),r),g.abrupt("return",{plugins:OC(y.plugins),presets:OC(y.presets),options:y.options.map((function(e){return BC(e)})),ignore:p||void 0,babelrc:d||void 0,config:n||void 0});case 47:case"end":return g.stop()}}),iC)}function yC(e,t,r,n){if("boolean"==typeof r)return r;var a=e.root;if(void 0===r)return-1!==t.directories.indexOf(a);var s=r;return Array.isArray(s)||(s=[s]),1===(s=s.map((function(e){return"string"==typeof e?yS.resolve(n,e):e}))).length&&s[0]===a?-1!==t.directories.indexOf(a):s.some((function(r){return"string"==typeof r&&(r=aC(r,n)),t.directories.some((function(t){return UC(r,n,t,e)}))}))}var gC=zS((function(e){return{filepath:e.filepath,dirname:e.dirname,options:WD("configfile",e.options)}})),vC=zS((function(e){return{filepath:e.filepath,dirname:e.dirname,options:WD("babelrcfile",e.options)}})),bC=zS((function(e){return{filepath:e.filepath,dirname:e.dirname,options:WD("extendsfile",e.options)}})),xC=kC({root:function(e){return CC(e,"base",nD)},env:function(e,t){return TC(e,"base",nD,t)},overrides:function(e,t){return jC(e,"base",nD,t)},overridesEnv:function(e,t,r){return PC(e,"base",nD,t,r)}}),EC=kC({root:function(e){return AC(e)},env:function(e,t){return wC(e)(t)},overrides:function(e,t){return SC(e)(t)},overridesEnv:function(e,t,r){return DC(e)(t)(r)}}),AC=zS((function(e){return CC(e,e.filepath,aD)})),wC=zS((function(e){return XS((function(t){return TC(e,e.filepath,aD,t)}))})),SC=zS((function(e){return XS((function(t){return jC(e,e.filepath,aD,t)}))})),DC=zS((function(e){return XS((function(t){return XS((function(r){return PC(e,e.filepath,aD,t,r)}))}))}));function CC(e,t,r){return r(e.dirname,e.options,t)}function TC(e,t,r,n){var a=e.dirname,s=e.options,i=s.env&&s.env[n];return i?r(a,i,t+'.env["'+n+'"]'):null}function jC(e,t,r,n){var a=e.dirname,s=e.options,i=s.overrides&&s.overrides[n];if(!i)throw new Error("Assertion failure - missing override");return r(a,i,t+".overrides["+n+"]")}function PC(e,t,r,n,a){var s=e.dirname,i=e.options,o=i.overrides&&i.overrides[n];if(!o)throw new Error("Assertion failure - missing override");var u=o.env&&o.env[a];return u?r(s,u,t+".overrides["+n+'].env["'+a+'"]'):null}function kC(e){var t=e.root,r=e.env,n=e.overrides,a=e.overridesEnv;return Hw.mark((function e(s,i,o){var u,c,l,p,d,f,h,m;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(void 0===o&&(o=new Set),u=s.dirname,c=[],NC(l=t(s),u,i)&&(c.push(l),(p=r(s,i.envName))&&NC(p,u,i)&&c.push(p),(l.options.overrides||[]).forEach((function(e,t){var r=n(s,t);if(NC(r,u,i)){c.push(r);var o=a(s,t,i.envName);o&&NC(o,u,i)&&c.push(o)}}))),!c.some((function(e){var t=e.options,r=t.ignore,n=t.only;return MC(i,r,n,u)}))){e.next=7;break}return e.abrupt("return",null);case 7:d={options:[],presets:[],plugins:[]},f=0,h=c;case 9:if(!(f<h.length)){e.next=18;break}return m=h[f],e.delegateYield(FC(d,m.options,u,i,o),"t0",12);case 12:if(e.t0){e.next=14;break}return e.abrupt("return",null);case 14:IC(d,m);case 15:f++,e.next=9;break;case 18:return e.abrupt("return",d);case 19:case"end":return e.stop()}}),e)}))}function FC(e,t,r,n,a){var s,i;return Hw.wrap((function(o){for(;;)switch(o.prev=o.next){case 0:if(void 0!==t.extends){o.next=2;break}return o.abrupt("return",!0);case 2:return o.delegateYield(eS(t.extends,r,n.envName,n.caller),"t0",3);case 3:if(s=o.t0,!a.has(s)){o.next=6;break}throw new Error("Configuration cycle detected loading "+s.filepath+".\nFile already loaded following the config chain:\n"+Array.from(a,(function(e){return" - "+e.filepath})).join("\n"));case 6:return a.add(s),o.delegateYield(EC(bC(s),n,a),"t1",8);case 8:if(i=o.t1,a.delete(s),i){o.next=12;break}return o.abrupt("return",!1);case 12:return _C(e,i),o.abrupt("return",!0);case 14:case"end":return o.stop()}}),oC)}function _C(e,t){var r,n,a;return(r=e.options).push.apply(r,t.options),(n=e.plugins).push.apply(n,t.plugins),(a=e.presets).push.apply(a,t.presets),e}function IC(e,t){var r,n,a=t.options,s=t.plugins,i=t.presets;return e.options.push(a),(r=e.plugins).push.apply(r,s()),(n=e.presets).push.apply(n,i()),e}function BC(e){var t=Object.assign({},e);return delete t.extends,delete t.env,delete t.overrides,delete t.plugins,delete t.presets,delete t.passPerPreset,delete t.ignore,delete t.only,delete t.test,delete t.include,delete t.exclude,Object.prototype.hasOwnProperty.call(t,"sourceMap")&&(t.sourceMaps=t.sourceMap,delete t.sourceMap),t}function OC(e){var t=new Map,r=[],n=e,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;if("function"==typeof o.value){var u=o.value,c=t.get(u);c||(c=new Map,t.set(u,c));var l=c.get(o.name);l?l.value=o:(l={value:o},r.push(l),o.ownPass||c.set(o.name,l))}else r.push({value:o})}return r.reduce((function(e,t){return e.push(t.value),e}),[])}function NC(e,t,r){var n=e.options;return(void 0===n.test||RC(r,n.test,t))&&(void 0===n.include||RC(r,n.include,t))&&(void 0===n.exclude||!RC(r,n.exclude,t))}function RC(e,t,r){return LC(e,Array.isArray(t)?t:[t],r)}function MC(e,t,r,n){return t&&LC(e,t,n)?(uC("Ignored %o because it matched one of %O from %o",e.filename,t,n),!0):!(!r||LC(e,r,n))&&(uC("Ignored %o because it failed to match one of %O from %o",e.filename,r,n),!0)}function LC(e,t,r){return t.some((function(t){return UC(t,r,e.filename,e)}))}function UC(e,t,r,n){if("function"==typeof e)return!!e(r,{dirname:t,envName:n.envName,caller:n.caller});if("string"!=typeof r)throw new Error("Configuration contains string/RegExp pattern, but no filename was passed to Babel");return"string"==typeof e&&(e=aC(e,t)),e.test(r)}var GC={name:PD,manipulateOptions:kD,pre:kD,post:kD,inherits:kD,visitor:function(e,t){var r=_D(e,t);if(r&&(Object.keys(r).forEach((function(e){return function(e,t){if(t&&"object"==typeof t)Object.keys(t).forEach((function(t){if("enter"!==t&&"exit"!==t)throw new Error('.visitor["'+e+'"] may only have .enter and/or .exit handlers.')}));else if("function"!=typeof t)throw new Error('.visitor["'+e+'"] must be a function');return t}(e,r[e])})),r.enter||r.exit))throw new Error("."+e+' cannot contain catch-all "enter" or "exit" handlers. Please target individual nodes.');return r},parserOverride:kD,generatorOverride:kD};function VC(e){var t={type:"root",source:"plugin"};return Object.keys(e).forEach((function(r){var n=GC[r],a={type:"option",name:r,parent:t};if(!n){var s=new Error("."+r+" is not a valid Plugin property");throw s.code="BABEL_UNKNOWN_PLUGIN_PROPERTY",s}n(a,e[r])})),e}function WC(e){return{version:iS,cache:e.simple(),env:function(t){return e.using((function(e){return void 0===t?e.envName:"function"==typeof t?tD(t(e.envName)):(Array.isArray(t)||(t=[t]),t.some((function(t){if("string"!=typeof t)throw new Error("Unexpected non-string value");return t===e.envName})))}))},async:function(){return!1},caller:function(t){return e.using((function(e){return tD(t(e.caller))}))},assertVersion:HC,tokTypes:void 0}}function HC(e){if("number"==typeof e){if(!Number.isInteger(e))throw new Error("Expected string or integer value.");e="^"+e+".0.0-0"}if("string"!=typeof e)throw new Error("Expected string or integer value.");if(!Iw.satisfies(iS,e)){var t=Error.stackTraceLimit;"number"==typeof t&&t<25&&(Error.stackTraceLimit=25);var r=new Error('Requires Babel "'+e+'", but was loaded with "'+iS+'". If you are sure you have a compatible version of @babel/core, it is likely that something in your build process is loading the wrong version. Inspect the stack trace of this error to look for the first entry that doesn\'t mention "@babel/core" or "babel-core" to see what is calling Babel.');throw"number"==typeof t&&(Error.stackTraceLimit=t),Object.assign(r,{code:"BABEL_VERSION_UNSUPPORTED",version:iS,range:e})}}var qC=Hw.mark(zC),KC=Hw.mark(XC);function zC(e,t){var r,n;return Hw.wrap((function(a){for(;;)switch(a.prev=a.next){case 0:a.t0=t,a.next="root"===a.t0?3:"upward-optional"===a.t0?4:"upward"===a.t0?7:12;break;case 3:return a.abrupt("return",e);case 4:return a.delegateYield(Jw(),"t1",5);case 5:return r=a.t1,a.abrupt("return",null===r?e:r);case 7:return a.delegateYield(Jw(),"t2",8);case 8:if(null===(n=a.t2)){a.next=11;break}return a.abrupt("return",n);case 11:throw Object.assign(new Error('Babel was run with rootMode:"upward" but a root could not be found when searching upward from "'+e+'".\nOne of the following config files must be in the directory tree: "'+tS.join(", ")+'".'),{code:"BABEL_ROOT_NOT_FOUND",dirname:e});case 12:throw new Error("Assertion failure - unknown rootMode value.");case 13:case"end":return a.stop()}}),qC)}function XC(e){var t,r,n,a,s,i,o,u,c,l,p,d,f,h,m;return Hw.wrap((function(y){for(;;)switch(y.prev=y.next){case 0:if(null==e||"object"==typeof e&&!Array.isArray(e)){y.next=2;break}throw new Error("Babel options must be an object, null, or undefined");case 2:return t=e?WD("arguments",e):{},r=t.envName,n=void 0===r?oS():r,a=t.cwd,s=void 0===a?".":a,i=t.root,o=void 0===i?".":i,u=t.rootMode,c=void 0===u?"root":u,l=t.caller,p=yS.resolve(s),y.delegateYield(zC(yS.resolve(p,o),c),"t0",6);case 6:return d=y.t0,f={filename:"string"==typeof t.filename?yS.resolve(s,t.filename):void 0,cwd:p,root:d,envName:n,caller:l},y.delegateYield(mC(t,f),"t1",9);case 9:if(h=y.t1){y.next=12;break}return y.abrupt("return",null);case 12:return m={},h.options.forEach((function(e){MS(m,e)})),m.babelrc=!1,m.configFile=!1,m.passPerPreset=!1,m.envName=f.envName,m.cwd=f.cwd,m.root=f.root,m.filename="string"==typeof f.filename?f.filename:void 0,m.plugins=h.plugins.map((function(e){return mD(e)})),m.presets=h.presets.map((function(e){return mD(e)})),y.abrupt("return",{options:m,context:f,ignore:h.ignore,babelrc:h.babelrc,config:h.config});case 24:case"end":return y.stop()}}),KC)}var YC=ES(Hw.mark((function e(t){var r,n,a,s,i;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(XC(t),"t0",1);case 1:if(r=e.t0){e.next=4;break}return e.abrupt("return",null);case 4:return n=r.options,a=r.babelrc,s=r.ignore,i=r.config,(n.plugins||[]).forEach((function(e){if(e.value instanceof bD)throw new Error("Passing cached plugin instances is not supported in babel.loadPartialConfig()")})),e.abrupt("return",new JC(n,a?a.filepath:void 0,s?s.filepath:void 0,i?i.filepath:void 0));case 7:case"end":return e.stop()}}),e)}))),JC=function(){function e(e,t,r,n){this.options=e,this.babelignore=r,this.babelrc=t,this.config=n,Object.freeze(this)}return e.prototype.hasFilesystemConfig=function(){return void 0!==this.babelrc||void 0!==this.config},e}();Object.freeze(JC.prototype);var $C=Hw.mark(tT),QC=Hw.mark(sT),ZC=ES(Hw.mark((function e(t){var r,n,a,s,i,o,u,c;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(XC(t),"t0",1);case 1:if(r=e.t0){e.next=4;break}return e.abrupt("return",null);case 4:if(n=r.options,a=r.context,s={},i=[[]],e.prev=7,o=n.plugins,u=n.presets,o&&u){e.next=11;break}throw new Error("Assertion failure - plugins and presets exist");case 11:return e.delegateYield(Hw.mark((function e(t,r){var n,o,u,c,l,p,d,f,h,m,y,g,v;return Hw.wrap((function(b){for(;;)switch(b.prev=b.next){case 0:n=[],o=0;case 2:if(!(o<t.plugins.length)){b.next=19;break}if(!1===(u=t.plugins[o]).options){b.next=16;break}return b.prev=5,b.t0=n,b.delegateYield(tT(u,a),"t1",8);case 8:b.t2=b.t1,b.t0.push.call(b.t0,b.t2),b.next=16;break;case 12:throw b.prev=12,b.t3=b.catch(5),o>0&&"BABEL_UNKNOWN_PLUGIN_PROPERTY"===b.t3.code&&zD(t.plugins[o-1],u,"plugin",0,b.t3),b.t3;case 16:o++,b.next=2;break;case 19:c=[],l=0;case 21:if(!(l<t.presets.length)){b.next=40;break}if(!1===(p=t.presets[l]).options){b.next=37;break}return b.prev=24,b.t4=c,b.delegateYield(sT(p,a),"t5",27);case 27:b.t6=b.t5,b.t7=p.ownPass?[]:r,b.t8={preset:b.t6,pass:b.t7},b.t4.push.call(b.t4,b.t8),b.next=37;break;case 33:throw b.prev=33,b.t9=b.catch(24),l>0&&"BABEL_UNKNOWN_OPTION"===b.t9.code&&zD(t.presets[l-1],p,"preset",0,b.t9),b.t9;case 37:l++,b.next=21;break;case 40:if(!(c.length>0)){b.next=63;break}i.splice.apply(i,[1,0].concat(c.map((function(e){return e.pass})).filter((function(e){return e!==r})))),d=c,f=Array.isArray(d),h=0,d=f?d:d[Symbol.iterator]();case 43:if(!f){b.next=49;break}if(!(h>=d.length)){b.next=46;break}return b.abrupt("break",63);case 46:m=d[h++],b.next=53;break;case 49:if(!(h=d.next()).done){b.next=52;break}return b.abrupt("break",63);case 52:m=h.value;case 53:if(g=(y=m).preset,v=y.pass,g){b.next=56;break}return b.abrupt("return",!0);case 56:return b.delegateYield(e({plugins:g.plugins,presets:g.presets},v),"t10",57);case 57:if(!b.t10){b.next=60;break}return b.abrupt("return",!0);case 60:g.options.forEach((function(e){MS(s,e)}));case 61:b.next=43;break;case 63:n.length>0&&r.unshift.apply(r,n);case 64:case"end":return b.stop()}}),e,null,[[5,12],[24,33]])}))({plugins:o.map((function(e){var t=gD(e);if(!t)throw new Error("Assertion failure - must be config item");return t})),presets:u.map((function(e){var t=gD(e);if(!t)throw new Error("Assertion failure - must be config item");return t}))},i[0]),"t1",12);case 12:if(!e.t1){e.next=15;break}return e.abrupt("return",null);case 15:e.next=21;break;case 17:throw e.prev=17,e.t2=e.catch(7),/^\[BABEL\]/.test(e.t2.message)||(e.t2.message="[BABEL] "+(a.filename||"unknown")+": "+e.t2.message),e.t2;case 21:return MS(c=s,n),c.plugins=i[0],c.presets=i.slice(1).filter((function(e){return e.length>0})).map((function(e){return{plugins:e}})),c.passPerPreset=c.presets.length>0,e.abrupt("return",{options:c,passes:i});case 27:case"end":return e.stop()}}),e,null,[[7,17]])}))),eT=KS(Hw.mark((function e(r,n){var a,s,i,o,u,c;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(a=r.value,s=r.options,i=r.dirname,o=r.alias,!1!==s){e.next=3;break}throw new Error("Assertion failure");case 3:if(s=s||{},u=a,"function"!=typeof a){e.next=15;break}c=Object.assign({},t,{},WC(n)),e.prev=7,u=a(c,s,i),e.next=15;break;case 11:throw e.prev=11,e.t0=e.catch(7),o&&(e.t0.message+=" (While processing: "+JSON.stringify(o)+")"),e.t0;case 15:if(u&&"object"==typeof u){e.next=17;break}throw new Error("Plugin/Preset did not return an object.");case 17:if("function"!=typeof u.then){e.next=20;break}return e.delegateYield([],"t1",19);case 19:throw new Error("You appear to be using an async plugin, which your current version of Babel does not support. If you're using a published plugin, you may need to upgrade your @babel/core version.");case 20:return e.abrupt("return",{value:u,options:s,dirname:i,alias:o});case 21:case"end":return e.stop()}}),e,null,[[7,11]])})));function tT(e,t){return Hw.wrap((function(r){for(;;)switch(r.prev=r.next){case 0:if(!(e.value instanceof bD)){r.next=4;break}if(!e.options){r.next=3;break}throw new Error("Passed options to an existing Plugin instance will not work.");case 3:return r.abrupt("return",e.value);case 4:return r.t0=rT,r.delegateYield(eT(e,t),"t1",6);case 6:return r.t2=r.t1,r.t3=t,r.delegateYield((0,r.t0)(r.t2,r.t3),"t4",9);case 9:return r.abrupt("return",r.t4);case 10:case"end":return r.stop()}}),$C)}var rT=KS(Hw.mark((function e(t,r){var n,a,s,i,o,u,c,l;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(n=t.value,a=t.options,s=t.dirname,i=t.alias,o=VC(n),(u=Object.assign({},o)).visitor&&(u.visitor=sA.explode(Object.assign({},u.visitor))),!u.inherits){e.next=12;break}return c={name:void 0,alias:i+"$inherits",value:u.inherits,options:a,dirname:s},e.delegateYield(BS(tT,(function(e){return r.invalidate((function(t){return e(c,t)}))})),"t0",7);case 7:l=e.t0,u.pre=oT(l.pre,u.pre),u.post=oT(l.post,u.post),u.manipulateOptions=oT(l.manipulateOptions,u.manipulateOptions),u.visitor=sA.visitors.merge([l.visitor||{},u.visitor||{}]);case 12:return e.abrupt("return",new bD(u,a,i));case 13:case"end":return e.stop()}}),e)}))),nT=function(e,t){if(e.test||e.include||e.exclude){var r=t.name?'"'+t.name+'"':"/* your preset */";throw new Error(["Preset "+r+" requires a filename to be set when babel is called directly,","```","babel.transform(code, { filename: 'file.ts', presets: ["+r+"] });","```","See https://babeljs.io/docs/en/options#filename for more information."].join("\n"))}},aT=function(e,t,r){if(!t.filename){var n=e.options;nT(n,r),n.overrides&&n.overrides.forEach((function(e){return nT(e,r)}))}};function sT(e,t){var r;return Hw.wrap((function(n){for(;;)switch(n.prev=n.next){case 0:return n.t0=iT,n.delegateYield(eT(e,t),"t1",2);case 2:return n.t2=n.t1,r=(0,n.t0)(n.t2),aT(r,t,e),n.delegateYield(cC(r,t),"t3",6);case 6:return n.abrupt("return",n.t3);case 7:case"end":return n.stop()}}),QC)}var iT=zS((function(e){var t=e.value,r=e.dirname,n=e.alias;return{options:WD("preset",t),alias:n,dirname:r}}));function oT(e,t){var r=[e,t].filter(Boolean);return r.length<=1?r[0]:function(){for(var e=arguments.length,t=new Array(e),n=0;n<e;n++)t[n]=arguments[n];var a=r,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o;u.apply(this,t)}}}var uT=ES(Hw.mark((function e(t){var r;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(ZC(t),"t0",1);case 1:return r=e.t0,e.abrupt("return",r?r.options:null);case 3:case"end":return e.stop()}}),e)}))),cT=function(e){return function(t,r){return void 0===r&&"function"==typeof t&&(r=t,t=void 0),r?e.errback(t,r):e.sync(t)}},lT=cT(YC),pT=YC.sync,dT=YC.async,fT=cT(uT),hT=uT.sync,mT=uT.async,yT=function(){function e(e,t,r){this._map=new Map,this.key=t,this.file=e,this.opts=r||{},this.cwd=e.opts.cwd,this.filename=e.opts.filename}var t=e.prototype;return t.set=function(e,t){this._map.set(e,t)},t.get=function(e){return this._map.get(e)},t.availableHelper=function(e,t){return this.file.availableHelper(e,t)},t.addHelper=function(e){return this.file.addHelper(e)},t.addImport=function(){return this.file.addImport()},t.getModuleName=function(){return this.file.getModuleName()},t.buildCodeFrameError=function(e,t,r){return this.file.buildCodeFrameError(e,t,r)},e}(),gT=zt?zt.isConcatSpreadable:void 0;var vT=function(e){return tn(e)||en(e)||!!(gT&&e&&e[gT])};var bT=function e(t,r,n,a,s){var i=-1,o=t.length;for(n||(n=vT),s||(s=[]);++i<o;){var u=t[i];r>0&&n(u)?r>1?e(u,r-1,n,a,s):Mn(s,u):a||(s[s.length]=u)}return s};var xT=function(e,t){for(var r=-1,n=null==e?0:e.length;++r<n;)if(t(e[r],r,e))return!0;return!1};var ET=function(e,t,r,n,a,s){var i=1&r,o=e.length,u=t.length;if(o!=u&&!(i&&u>o))return!1;var c=s.get(e);if(c&&s.get(t))return c==t;var l=-1,p=!0,d=2&r?new vp:void 0;for(s.set(e,t),s.set(t,e);++l<o;){var f=e[l],h=t[l];if(n)var m=i?n(h,f,l,t,e,s):n(f,h,l,e,t,s);if(void 0!==m){if(m)continue;p=!1;break}if(d){if(!xT(t,(function(e,t){if(!Dp(d,t)&&(f===e||a(f,e,r,n,s)))return d.push(t)}))){p=!1;break}}else if(f!==h&&!a(f,h,r,n,s)){p=!1;break}}return s.delete(e),s.delete(t),p};var AT=function(e){var t=-1,r=Array(e.size);return e.forEach((function(e,n){r[++t]=[n,e]})),r},wT=zt?zt.prototype:void 0,ST=wT?wT.valueOf:void 0;var DT=function(e,t,r,n,a,s,i){switch(r){case"[object DataView]":if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case"[object ArrayBuffer]":return!(e.byteLength!=t.byteLength||!s(new aa(e),new aa(t)));case"[object Boolean]":case"[object Date]":case"[object Number]":return Tt(+e,+t);case"[object Error]":return e.name==t.name&&e.message==t.message;case"[object RegExp]":case"[object String]":return e==t+"";case"[object Map]":var o=AT;case"[object Set]":var u=1&n;if(o||(o=Tp),e.size!=t.size&&!u)return!1;var c=i.get(e);if(c)return c==t;n|=2,i.set(e,t);var l=ET(o(e),o(t),n,a,s,i);return i.delete(e),l;case"[object Symbol]":if(ST)return ST.call(e)==ST.call(t)}return!1},CT=Object.prototype.hasOwnProperty;var TT=function(e,t,r,n,a,s){var i=1&r,o=Wn(e),u=o.length;if(u!=Wn(t).length&&!i)return!1;for(var c=u;c--;){var l=o[c];if(!(i?l in t:CT.call(t,l)))return!1}var p=s.get(e);if(p&&s.get(t))return p==t;var d=!0;s.set(e,t),s.set(t,e);for(var f=i;++c<u;){var h=e[l=o[c]],m=t[l];if(n)var y=i?n(m,h,l,t,e,s):n(h,m,l,e,t,s);if(!(void 0===y?h===m||a(h,m,r,n,s):y)){d=!1;break}f||(f="constructor"==l)}if(d&&!f){var g=e.constructor,v=t.constructor;g!=v&&"constructor"in e&&"constructor"in t&&!("function"==typeof g&&g instanceof g&&"function"==typeof v&&v instanceof v)&&(d=!1)}return s.delete(e),s.delete(t),d},jT=Object.prototype.hasOwnProperty;var PT=function(e,t,r,n,a,s){var i=tn(e),o=tn(t),u=i?"[object Array]":ta(e),c=o?"[object Array]":ta(t),l="[object Object]"==(u="[object Arguments]"==u?"[object Object]":u),p="[object Object]"==(c="[object Arguments]"==c?"[object Object]":c),d=u==c;if(d&&nn(e)){if(!nn(t))return!1;i=!0,l=!1}if(d&&!l)return s||(s=new Gr),i||fn(e)?ET(e,t,r,n,a,s):DT(e,t,u,r,n,a,s);if(!(1&r)){var f=l&&jT.call(e,"__wrapped__"),h=p&&jT.call(t,"__wrapped__");if(f||h){var m=f?e.value():e,y=h?t.value():t;return s||(s=new Gr),a(m,y,r,n,s)}}return!!d&&(s||(s=new Gr),TT(e,t,r,n,a,s))};var kT=function e(t,r,n,a,s){return t===r||(null==t||null==r||!Yr(t)&&!Yr(r)?t!=t&&r!=r:PT(t,r,n,a,e,s))};var FT=function(e,t,r,n){var a=r.length,s=a,i=!n;if(null==e)return!s;for(e=Object(e);a--;){var o=r[a];if(i&&o[2]?o[1]!==e[o[0]]:!(o[0]in e))return!1}for(;++a<s;){var u=(o=r[a])[0],c=e[u],l=o[1];if(i&&o[2]){if(void 0===c&&!(u in e))return!1}else{var p=new Gr;if(n)var d=n(c,l,u,e,t,p);if(!(void 0===d?kT(l,c,3,n,p):d))return!1}}return!0};var _T=function(e){return e==e&&!nr(e)};var IT=function(e){for(var t=wn(e),r=t.length;r--;){var n=t[r],a=e[n];t[r]=[n,a,_T(a)]}return t};var BT=function(e,t){return function(r){return null!=r&&(r[e]===t&&(void 0!==t||e in Object(r)))}};var OT=function(e){var t=IT(e);return 1==t.length&&t[0][2]?BT(t[0][0],t[0][1]):function(r){return r===e||FT(r,e,t)}},NT=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,RT=/^\w*$/;var MT=function(e,t){if(tn(e))return!1;var r=typeof e;return!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=e&&!Bf(e))||(RT.test(e)||!NT.test(e)||null!=t&&e in Object(t))};function LT(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new TypeError("Expected a function");var r=function r(){var n=arguments,a=t?t.apply(this,n):n[0],s=r.cache;if(s.has(a))return s.get(a);var i=e.apply(this,n);return r.cache=s.set(a,i)||s,i};return r.cache=new(LT.Cache||Mr),r}LT.Cache=Mr;var UT=LT;var GT=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,VT=/\\(\\)?/g,WT=function(e){var t=UT(e,(function(e){return 500===r.size&&r.clear(),e})),r=t.cache;return t}((function(e){var t=[];return 46===e.charCodeAt(0)&&t.push(""),e.replace(GT,(function(e,r,n,a){t.push(n?a.replace(VT,"$1"):r||e)})),t}));var HT=function(e,t){return tn(e)?e:MT(e,t)?[e]:WT(eh(e))};var qT=function(e){if("string"==typeof e||Bf(e))return e;var t=e+"";return"0"==t&&1/e==-1/0?"-0":t};var KT=function(e,t){for(var r=0,n=(t=HT(t,e)).length;null!=e&&r<n;)e=e[qT(t[r++])];return r&&r==n?e:void 0};var zT=function(e,t,r){var n=null==e?void 0:KT(e,t);return void 0===n?r:n};var XT=function(e,t){return null!=e&&t in Object(e)};var YT=function(e,t,r){for(var n=-1,a=(t=HT(t,e)).length,s=!1;++n<a;){var i=qT(t[n]);if(!(s=null!=e&&r(e,i)))break;e=e[i]}return s||++n!=a?s:!!(a=null==e?0:e.length)&&on(a)&&sn(i,a)&&(tn(e)||en(e))};var JT=function(e,t){return null!=e&&YT(e,t,XT)};var $T=function(e,t){return MT(e)&&_T(t)?BT(qT(e),t):function(r){var n=zT(r,e);return void 0===n&&n===t?JT(r,e):kT(t,n,3)}};var QT=function(e){return function(t){return null==t?void 0:t[e]}};var ZT=function(e){return function(t){return KT(t,e)}};var ej=function(e){return MT(e)?QT(qT(e)):ZT(e)};var tj=function(e){return"function"==typeof e?e:null==e?ih:"object"==typeof e?tn(e)?$T(e[0],e[1]):OT(e):ej(e)};var rj=function(e){return function(t,r,n){for(var a=-1,s=Object(t),i=n(t),o=i.length;o--;){var u=i[e?o:++a];if(!1===r(s[u],u,s))break}return t}}();var nj=function(e,t){return function(r,n){if(null==r)return r;if(!An(r))return e(r,n);for(var a=r.length,s=t?a:-1,i=Object(r);(t?s--:++s<a)&&!1!==n(i[s],s,i););return r}}((function(e,t){return e&&rj(e,t,wn)}));var aj=function(e,t){var r=-1,n=An(e)?Array(e.length):[];return nj(e,(function(e,a,s){n[++r]=t(e,a,s)})),n};var sj=function(e,t){var r=e.length;for(e.sort(t);r--;)e[r]=e[r].value;return e};var ij=function(e,t){if(e!==t){var r=void 0!==e,n=null===e,a=e==e,s=Bf(e),i=void 0!==t,o=null===t,u=t==t,c=Bf(t);if(!o&&!c&&!s&&e>t||s&&i&&u&&!o&&!c||n&&i&&u||!r&&u||!a)return 1;if(!n&&!s&&!c&&e<t||c&&r&&a&&!n&&!s||o&&r&&a||!i&&a||!u)return-1}return 0};var oj=function(e,t,r){for(var n=-1,a=e.criteria,s=t.criteria,i=a.length,o=r.length;++n<i;){var u=ij(a[n],s[n]);if(u)return n>=o?u:u*("desc"==r[n]?-1:1)}return e.index-t.index};var uj,cj=function(e,t,r){var n=-1;t=Wf(t.length?t:[ih],ln(tj));var a=aj(e,(function(e,r,a){return{criteria:Wf(t,(function(t){return t(e)})),index:++n,value:e}}));return sj(a,(function(e,t){return oj(e,t,r)}))},lj=hh((function(e,t){if(null==e)return[];var r=t.length;return r>1&&Jf(e,t[0],t[1])?t=[]:r>2&&Jf(t[0],t[1],t[2])&&(t=[t[0]]),cj(e,bT(t,1),[])}));function pj(){if(!uj){var e=ZC.sync({babelrc:!1,configFile:!1,plugins:[dj]});if(!(uj=e?e.passes[0][0]:void 0))throw new Error("Assertion failure")}return uj}var dj={name:"internal.blockHoist",visitor:{Block:{exit:function(e){for(var t=e.node,r=!1,n=0;n<t.body.length;n++){var a=t.body[n];if(a&&null!=a._blockHoist){r=!0;break}}r&&(t.body=lj(t.body,(function(e){var t=e&&e._blockHoist;return null==t&&(t=1),!0===t&&(t=2),-1*t})))}}}};function fj(e){var t=e.options,r=t.filename,n=t.cwd,a=t.filenameRelative,s=void 0===a?"string"==typeof r?yS.relative(n,r):"unknown":a,i=t.sourceType,o=void 0===i?"module":i,u=t.inputSourceMap,c=t.sourceMaps,l=void 0===c?!!u:c,p=t.moduleRoot,d=t.sourceRoot,f=void 0===d?p:d,h=t.sourceFileName,m=void 0===h?yS.basename(s):h,y=t.comments,g=void 0===y||y,v=t.compact,b=void 0===v?"auto":v,x=e.options,E=Object.assign({},x,{parserOpts:Object.assign({sourceType:".mjs"===yS.extname(s)?"module":o,sourceFileName:r,plugins:[]},x.parserOpts),generatorOpts:Object.assign({filename:r,auxiliaryCommentBefore:x.auxiliaryCommentBefore,auxiliaryCommentAfter:x.auxiliaryCommentAfter,retainLines:x.retainLines,comments:g,shouldPrintComment:x.shouldPrintComment,compact:b,minified:x.minified,sourceMaps:l,sourceRoot:f,sourceFileName:m},x.generatorOpts)}),A=e.passes,w=Array.isArray(A),S=0;for(A=w?A:A[Symbol.iterator]();;){var D;if(w){if(S>=A.length)break;D=A[S++]}else{if((S=A.next()).done)break;D=S.value}var C=D,T=Array.isArray(C),j=0;for(C=T?C:C[Symbol.iterator]();;){var P;if(T){if(j>=C.length)break;P=C[j++]}else{if((j=C.next()).done)break;P=j.value}var k=P;k.manipulateOptions&&k.manipulateOptions(E,E.parserOpts)}}return E}var hj={};var mj=function(e){return Sa(e,5)},yj=Object.freeze({__proto__:null,default:{}}),gj=Vt((function(e,t){var r=Dg.Buffer;function n(e,t){for(var r in e)t[r]=e[r]}function a(e,t,n){return r(e,t,n)}r.from&&r.alloc&&r.allocUnsafe&&r.allocUnsafeSlow?e.exports=Dg:(n(Dg,t),t.Buffer=a),n(r,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return r(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var a=r(e);return void 0!==t?"string"==typeof n?a.fill(t,n):a.fill(t):a.fill(0),a},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return Dg.SlowBuffer(e)}})),vj=(gj.Buffer,Wt(yj)),bj=Vt((function(e,t){function r(e,r){(r=r||{}).isFileComment&&(e=function(e,r){var n=t.mapFileCommentRegex.exec(e),a=n[1]||n[2],s=yS.resolve(r,a);try{return vj.readFileSync(s,"utf8")}catch(e){throw new Error("An error occurred while trying to read the map file at "+s+"\n"+e)}}(e,r.commentFileDir)),r.hasComment&&(e=function(e){return e.split(",").pop()}(e)),r.isEncoded&&(e=function(e){return gj.Buffer.from(e,"base64").toString()}(e)),(r.isJSON||r.isEncoded)&&(e=JSON.parse(e)),this.sourcemap=e}Object.defineProperty(t,"commentRegex",{get:function(){return/^\s*\/(?:\/|\*)[@#]\s+sourceMappingURL=data:(?:application|text)\/json;(?:charset[:=]\S+?;)?base64,(?:.*)$/gm}}),Object.defineProperty(t,"mapFileCommentRegex",{get:function(){return/(?:\/\/[@#][ \t]+sourceMappingURL=([^\s'"`]+?)[ \t]*$)|(?:\/\*[@#][ \t]+sourceMappingURL=([^\*]+?)[ \t]*(?:\*\/){1}[ \t]*$)/gm}}),r.prototype.toJSON=function(e){return JSON.stringify(this.sourcemap,null,e)},r.prototype.toBase64=function(){var e=this.toJSON();return gj.Buffer.from(e,"utf8").toString("base64")},r.prototype.toComment=function(e){var t="sourceMappingURL=data:application/json;charset=utf-8;base64,"+this.toBase64();return e&&e.multiline?"/*# "+t+" */":"//# "+t},r.prototype.toObject=function(){return JSON.parse(this.toJSON())},r.prototype.addProperty=function(e,t){if(this.sourcemap.hasOwnProperty(e))throw new Error('property "'+e+'" already exists on the sourcemap, use set property instead');return this.setProperty(e,t)},r.prototype.setProperty=function(e,t){return this.sourcemap[e]=t,this},r.prototype.getProperty=function(e){return this.sourcemap[e]},t.fromObject=function(e){return new r(e)},t.fromJSON=function(e){return new r(e,{isJSON:!0})},t.fromBase64=function(e){return new r(e,{isEncoded:!0})},t.fromComment=function(e){return new r(e=e.replace(/^\/\*/g,"//").replace(/\*\/$/g,""),{isEncoded:!0,hasComment:!0})},t.fromMapFileComment=function(e,t){return new r(e,{commentFileDir:t,isFileComment:!0,isJSON:!0})},t.fromSource=function(e){var r=e.match(t.commentRegex);return r?t.fromComment(r.pop()):null},t.fromMapFileSource=function(e,r){var n=e.match(t.mapFileCommentRegex);return n?t.fromMapFileComment(n.pop(),r):null},t.removeComments=function(e){return e.replace(t.commentRegex,"")},t.removeMapFileComments=function(e){return e.replace(t.mapFileCommentRegex,"")},t.generateMapFileComment=function(e,t){var r="sourceMappingURL="+e;return t&&t.multiline?"/*# "+r+" */":"//# "+r}})),xj=(bj.fromObject,bj.fromJSON,bj.fromBase64,bj.fromComment,bj.fromMapFileComment,bj.fromSource,bj.fromMapFileSource,bj.removeComments,bj.removeMapFileComments,bj.generateMapFileComment,{classProperties:{syntax:{name:"@babel/plugin-syntax-class-properties",url:"https://git.io/vb4yQ"},transform:{name:"@babel/plugin-proposal-class-properties",url:"https://git.io/vb4SL"}},decorators:{syntax:{name:"@babel/plugin-syntax-decorators",url:"https://git.io/vb4y9"},transform:{name:"@babel/plugin-proposal-decorators",url:"https://git.io/vb4ST"}},doExpressions:{syntax:{name:"@babel/plugin-syntax-do-expressions",url:"https://git.io/vb4yh"},transform:{name:"@babel/plugin-proposal-do-expressions",url:"https://git.io/vb4S3"}},dynamicImport:{syntax:{name:"@babel/plugin-syntax-dynamic-import",url:"https://git.io/vb4Sv"}},exportDefaultFrom:{syntax:{name:"@babel/plugin-syntax-export-default-from",url:"https://git.io/vb4SO"},transform:{name:"@babel/plugin-proposal-export-default-from",url:"https://git.io/vb4yH"}},exportNamespaceFrom:{syntax:{name:"@babel/plugin-syntax-export-namespace-from",url:"https://git.io/vb4Sf"},transform:{name:"@babel/plugin-proposal-export-namespace-from",url:"https://git.io/vb4SG"}},flow:{syntax:{name:"@babel/plugin-syntax-flow",url:"https://git.io/vb4yb"},transform:{name:"@babel/plugin-transform-flow-strip-types",url:"https://git.io/vb49g"}},functionBind:{syntax:{name:"@babel/plugin-syntax-function-bind",url:"https://git.io/vb4y7"},transform:{name:"@babel/plugin-proposal-function-bind",url:"https://git.io/vb4St"}},functionSent:{syntax:{name:"@babel/plugin-syntax-function-sent",url:"https://git.io/vb4yN"},transform:{name:"@babel/plugin-proposal-function-sent",url:"https://git.io/vb4SZ"}},importMeta:{syntax:{name:"@babel/plugin-syntax-import-meta",url:"https://git.io/vbKK6"}},jsx:{syntax:{name:"@babel/plugin-syntax-jsx",url:"https://git.io/vb4yA"},transform:{name:"@babel/plugin-transform-react-jsx",url:"https://git.io/vb4yd"}},logicalAssignment:{syntax:{name:"@babel/plugin-syntax-logical-assignment-operators",url:"https://git.io/vAlBp"},transform:{name:"@babel/plugin-proposal-logical-assignment-operators",url:"https://git.io/vAlRe"}},numericSeparator:{syntax:{name:"@babel/plugin-syntax-numeric-separator",url:"https://git.io/vb4Sq"},transform:{name:"@babel/plugin-proposal-numeric-separator",url:"https://git.io/vb4yS"}},optionalChaining:{syntax:{name:"@babel/plugin-syntax-optional-chaining",url:"https://git.io/vb4Sc"},transform:{name:"@babel/plugin-proposal-optional-chaining",url:"https://git.io/vb4Sk"}},pipelineOperator:{syntax:{name:"@babel/plugin-syntax-pipeline-operator",url:"https://git.io/vb4yj"},transform:{name:"@babel/plugin-proposal-pipeline-operator",url:"https://git.io/vb4SU"}},throwExpressions:{syntax:{name:"@babel/plugin-syntax-throw-expressions",url:"https://git.io/vb4SJ"},transform:{name:"@babel/plugin-proposal-throw-expressions",url:"https://git.io/vb4yF"}},typescript:{syntax:{name:"@babel/plugin-syntax-typescript",url:"https://git.io/vb4SC"},transform:{name:"@babel/plugin-transform-typescript",url:"https://git.io/vb4Sm"}},asyncGenerators:{syntax:{name:"@babel/plugin-syntax-async-generators",url:"https://git.io/vb4SY"},transform:{name:"@babel/plugin-proposal-async-generator-functions",url:"https://git.io/vb4yp"}},nullishCoalescingOperator:{syntax:{name:"@babel/plugin-syntax-nullish-coalescing-operator",url:"https://git.io/vb4yx"},transform:{name:"@babel/plugin-proposal-nullish-coalescing-operator",url:"https://git.io/vb4Se"}},objectRestSpread:{syntax:{name:"@babel/plugin-syntax-object-rest-spread",url:"https://git.io/vb4y5"},transform:{name:"@babel/plugin-proposal-object-rest-spread",url:"https://git.io/vb4Ss"}},optionalCatchBinding:{syntax:{name:"@babel/plugin-syntax-optional-catch-binding",url:"https://git.io/vb4Sn"},transform:{name:"@babel/plugin-proposal-optional-catch-binding",url:"https://git.io/vb4SI"}}}),Ej=function(e){return e.name+" ("+e.url+")"};function Aj(e,t,r){var n="Support for the experimental syntax '"+e+"' isn't currently enabled ("+t.line+":"+(t.column+1)+"):\n\n"+r,a=xj[e];if(a){var s=a.syntax,i=a.transform;if(s)if(i)n+="\n\nAdd "+Ej(i)+" to the 'plugins' section of your Babel config to enable transformation.";else n+="\n\nAdd "+Ej(s)+" to the 'plugins' section of your Babel config to enable parsing."}return n}var wj=Hw.mark(Sj);function Sj(e,t,r){var n,a,s,i,o,u,c,l,p,d,f,h,m,y,g,v,b,x,E;return Hw.wrap((function(A){for(;;)switch(A.prev=A.next){case 0:n=t.parserOpts,a=t.highlightCode,s=void 0===a||a,i=t.filename,o=void 0===i?"unknown":i,A.prev=1,u=[],c=e,l=Array.isArray(c),p=0,c=l?c:c[Symbol.iterator]();case 4:if(!l){A.next=10;break}if(!(p>=c.length)){A.next=7;break}return A.abrupt("break",33);case 7:d=c[p++],A.next=14;break;case 10:if(!(p=c.next()).done){A.next=13;break}return A.abrupt("break",33);case 13:d=p.value;case 14:f=d,h=Array.isArray(f),m=0,f=h?f:f[Symbol.iterator]();case 16:if(!h){A.next=22;break}if(!(m>=f.length)){A.next=19;break}return A.abrupt("break",31);case 19:y=f[m++],A.next=26;break;case 22:if(!(m=f.next()).done){A.next=25;break}return A.abrupt("break",31);case 25:y=m.value;case 26:(g=y.parserOverride)&&void 0!==(v=g(r,n,_x))&&u.push(v);case 29:A.next=16;break;case 31:A.next=4;break;case 33:if(0!==u.length){A.next=37;break}return A.abrupt("return",_x(r,n));case 37:if(1!==u.length){A.next=42;break}return A.delegateYield([],"t0",39);case 39:if("function"!=typeof u[0].then){A.next=41;break}throw new Error("You appear to be using an async parser plugin, which your current version of Babel does not support. If you're using a published plugin, you may need to upgrade your @babel/core version.");case 41:return A.abrupt("return",u[0]);case 42:throw new Error("More than one plugin attempted to override parsing.");case 45:throw A.prev=45,A.t1=A.catch(1),"BABEL_PARSER_SOURCETYPE_MODULE_REQUIRED"===A.t1.code&&(A.t1.message+="\nConsider renaming the file to '.mjs', or setting sourceType:module or sourceType:unambiguous in your Babel config for this file."),b=A.t1.loc,x=A.t1.missingPlugin,b&&(E=sb(r,{start:{line:b.line,column:b.column+1}},{highlightCode:s}),A.t1.message=x?o+": "+Aj(x[0],b,E):o+": "+A.t1.message+"\n\n"+E,A.t1.code="BABEL_PARSE_ERROR"),A.t1;case 51:case"end":return A.stop()}}),wj,null,[[1,45]])}var Dj=Hw.mark(Tj),Cj=SD("babel:transform:file");function Tj(e,t,r,n){var a,s,i,o,u;return Hw.wrap((function(c){for(;;)switch(c.prev=c.next){case 0:if(r=""+(r||""),!n){c.next=11;break}if("Program"!==n.type){c.next=6;break}n=zi(n,[],[]),c.next=8;break;case 6:if("File"===n.type){c.next=8;break}throw new Error("AST root must be a Program or File node");case 8:n=mj(n),c.next=13;break;case 11:return c.delegateYield(Sj(e,t,r),"t0",12);case 12:n=c.t0;case 13:if(a=null,!1!==t.inputSourceMap){if("object"==typeof t.inputSourceMap&&(a=bj.fromObject(t.inputSourceMap)),!a&&(s=Fj(jj,n)))try{a=bj.fromComment(s)}catch(e){Cj("discarding unknown inline input sourcemap",e)}if(!a)if(i=Fj(Pj,n),"string"==typeof t.filename&&i)try{o=Pj.exec(i),(u=hj.readFileSync(yS.resolve(yS.dirname(t.filename),o[1]))).length>1e6?Cj("skip merging input map > 1 MB"):a=bj.fromJSON(u)}catch(e){Cj("discarding unknown file input sourcemap",e)}else i&&Cj("discarding un-loadable file input sourcemap")}return c.abrupt("return",new Nw(t,{code:r,ast:n,inputMap:a}));case 16:case"end":return c.stop()}}),Dj)}var jj=/^[@#]\s+sourceMappingURL=data:(?:application|text)\/json;(?:charset[:=]\S+?;)?base64,(?:.*)$/,Pj=/^[@#][ \t]+sourceMappingURL=([^\s'"`]+)[ \t]*$/;function kj(e,t,r){return t&&(t=t.filter((function(t){var n=t.value;return!e.test(n)||(r=n,!1)}))),[t,r]}function Fj(e,t){var r=null;return Bd(t,(function(t){var n=kj(e,t.leadingComments,r);t.leadingComments=n[0],r=n[1];var a=kj(e,t.innerComments,r);t.innerComments=a[0],r=a[1];var s=kj(e,t.trailingComments,r);t.trailingComments=s[0],r=s[1]})),r}function _j(e,t){var r=Bj(e),n=Bj(t),a=new Rm.SourceMapGenerator,s=r.sources,i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u.source;"string"==typeof c.content&&a.setSourceContent(c.path,c.content)}if(1===n.sources.length){var l=n.sources[0],p=new Map;!function(e,t){var r=e.sources,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s,o=i.source,u=i.mappings,c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}var d=p,f=d.original,h=d.generated,m=Array.isArray(h),y=0;for(h=m?h:h[Symbol.iterator]();;){var g;if(m){if(y>=h.length)break;g=h[y++]}else{if((y=h.next()).done)break;g=y.value}t(g,f,o)}}}}(r,(function(e,t,r){!function(e,t,r){var n=function(e,t){var r=e.mappings,n=t.line,a=t.columnStart,s=t.columnEnd;return function(e,t){for(var r=function(e,t){var r=0,n=e.length;for(;r<n;){var a=Math.floor((r+n)/2),s=e[a],i=t(s);if(0===i){r=a;break}i>=0?n=a:r=a+1}var o=r;if(o<e.length){for(;o>=0&&t(e[o])>=0;)o--;return o+1}return o}(e,t),n=[],a=r;a<e.length&&0===t(e[a]);a++)n.push(e[a]);return n}(r,(function(e){var t=e.original;return n>t.line?-1:n<t.line?1:a>=t.columnEnd?-1:s<=t.columnStart?1:0}))}(e,t),a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i.generated,u=Array.isArray(o),c=0;for(o=u?o:o[Symbol.iterator]();;){var l;if(u){if(c>=o.length)break;l=o[c++]}else{if((c=o.next()).done)break;l=c.value}r(l)}}}(l,e,(function(e){var n=Ij(e);p.has(n)||(p.set(n,e),a.addMapping({source:r.path,original:{line:t.line,column:t.columnStart},generated:{line:e.line,column:e.columnStart},name:t.name}))}))}));var d=p.values(),f=Array.isArray(d),h=0;for(d=f?d:d[Symbol.iterator]();;){var m;if(f){if(h>=d.length)break;m=d[h++]}else{if((h=d.next()).done)break;m=h.value}var y=m;if(y.columnEnd!==1/0){var g={line:y.line,columnStart:y.columnEnd},v=Ij(g);p.has(v)||a.addMapping({generated:{line:g.line,column:g.columnStart}})}}}var b=a.toJSON();return"string"==typeof r.sourceRoot&&(b.sourceRoot=r.sourceRoot),b}function Ij(e){return e.line+"/"+e.columnStart}function Bj(e){var t=new Rm.SourceMapConsumer(Object.assign({},e,{sourceRoot:null})),r=new Map,n=new Map,a=null;return t.computeColumnSpans(),t.eachMapping((function(e){if(null!==e.originalLine){var s=r.get(e.source);s||(s={path:e.source,content:t.sourceContentFor(e.source,!0)},r.set(e.source,s));var i=n.get(s);i||(i={source:s,mappings:[]},n.set(s,i));var o={line:e.originalLine,columnStart:e.originalColumn,columnEnd:1/0,name:e.name};a&&a.source===s&&a.mapping.line===e.originalLine&&(a.mapping.columnEnd=e.originalColumn),a={source:s,mapping:o},i.mappings.push({original:o,generated:t.allGeneratedPositionsFor({source:e.source,line:e.originalLine,column:e.originalColumn}).map((function(e){return{line:e.line,columnStart:e.column,columnEnd:e.lastColumn+1}}))})}}),null,Rm.SourceMapConsumer.ORIGINAL_ORDER),{file:e.file,sourceRoot:e.sourceRoot,sources:Array.from(n.values())}}function Oj(e,t){var r,n=t.opts,a=t.ast,s=t.code,i=t.inputMap,o=[],u=e,c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}var d=p,f=Array.isArray(d),h=0;for(d=f?d:d[Symbol.iterator]();;){var m;if(f){if(h>=d.length)break;m=d[h++]}else{if((h=d.next()).done)break;m=h.value}var y=m.generatorOverride;if(y){var g=y(a,n.generatorOpts,s,lv);void 0!==g&&o.push(g)}}}if(0===o.length)r=lv(a,n.generatorOpts,s);else{if(1!==o.length)throw new Error("More than one plugin attempted to override codegen.");if("function"==typeof(r=o[0]).then)throw new Error("You appear to be using an async codegen plugin, which your current version of Babel does not support. If you're using a published plugin, you may need to upgrade your @babel/core version.")}var v=r,b=v.code,x=v.map;return x&&i&&(x=_j(i.toObject(),x)),"inline"!==n.sourceMaps&&"both"!==n.sourceMaps||(b+="\n"+bj.fromObject(x).toComment()),"inline"===n.sourceMaps&&(x=null),{outputCode:b,outputMap:x}}var Nj=Hw.mark(Mj),Rj=Hw.mark(Lj);function Mj(e,t,r){var n,a,s,i,o,u,c;return Hw.wrap((function(l){for(;;)switch(l.prev=l.next){case 0:return l.delegateYield(Tj(e.passes,fj(e),t,r),"t0",1);case 1:return n=l.t0,a=n.opts,l.prev=3,l.delegateYield(Lj(n,e.passes),"t1",5);case 5:l.next=12;break;case 7:throw l.prev=7,l.t2=l.catch(3),l.t2.message=(null!=(s=a.filename)?s:"unknown")+": "+l.t2.message,l.t2.code||(l.t2.code="BABEL_TRANSFORM_ERROR"),l.t2;case 12:l.prev=12,!1!==a.code&&(u=Oj(e.passes,n),i=u.outputCode,o=u.outputMap),l.next=21;break;case 16:throw l.prev=16,l.t3=l.catch(12),l.t3.message=(null!=(c=a.filename)?c:"unknown")+": "+l.t3.message,l.t3.code||(l.t3.code="BABEL_GENERATE_ERROR"),l.t3;case 21:return l.abrupt("return",{metadata:n.metadata,options:a,ast:!0===a.ast?n.ast:null,code:void 0===i?null:i,map:void 0===o?null:o,sourceType:n.ast.program.sourceType});case 22:case"end":return l.stop()}}),Nj,null,[[3,7],[12,16]])}function Lj(e,t){var r,n,a,s,i,o,u,c,l,p,d,f,h,m,y,g,v,b,x,E,A,w,S,D,C,T,j,P;return Hw.wrap((function(k){for(;;)switch(k.prev=k.next){case 0:r=t,n=Array.isArray(r),a=0,r=n?r:r[Symbol.iterator]();case 1:if(!n){k.next=7;break}if(!(a>=r.length)){k.next=4;break}return k.abrupt("break",61);case 4:s=r[a++],k.next=11;break;case 7:if(!(a=r.next()).done){k.next=10;break}return k.abrupt("break",61);case 10:s=a.value;case 11:i=[],o=[],u=[],c=s.concat([pj()]),l=Array.isArray(c),p=0,c=l?c:c[Symbol.iterator]();case 16:if(!l){k.next=22;break}if(!(p>=c.length)){k.next=19;break}return k.abrupt("break",33);case 19:d=c[p++],k.next=26;break;case 22:if(!(p=c.next()).done){k.next=25;break}return k.abrupt("break",33);case 25:d=p.value;case 26:h=new yT(e,(f=d).key,f.options),i.push([f,h]),o.push(h),u.push(f.visitor);case 31:k.next=16;break;case 33:m=0,y=i;case 34:if(!(m<y.length)){k.next=45;break}if(g=y[m],v=g[0],b=g[1],!(x=v.pre)){k.next=42;break}return E=x.call(b,e),k.delegateYield([],"t0",40);case 40:if(!Uj(E)){k.next=42;break}throw new Error("You appear to be using an plugin with an async .pre, which your current version of Babel does not support. If you're using a published plugin, you may need to upgrade your @babel/core version.");case 42:m++,k.next=34;break;case 45:A=sA.visitors.merge(u,o,e.opts.wrapPluginVisitorMethod),sA(e.ast,A,e.scope),w=0,S=i;case 48:if(!(w<S.length)){k.next=59;break}if(D=S[w],C=D[0],T=D[1],!(j=C.post)){k.next=56;break}return P=j.call(T,e),k.delegateYield([],"t1",54);case 54:if(!Uj(P)){k.next=56;break}throw new Error("You appear to be using an plugin with an async .post, which your current version of Babel does not support. If you're using a published plugin, you may need to upgrade your @babel/core version.");case 56:w++,k.next=48;break;case 59:k.next=1;break;case 61:case"end":return k.stop()}}),Rj)}function Uj(e){return!(!e||"object"!=typeof e&&"function"!=typeof e||!e.then||"function"!=typeof e.then)}var Gj=ES(Hw.mark((function e(t,r){var n;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(ZC(r),"t0",1);case 1:if(null!==(n=e.t0)){e.next=4;break}return e.abrupt("return",null);case 4:return e.delegateYield(Mj(n,t),"t1",5);case 5:return e.abrupt("return",e.t1);case 6:case"end":return e.stop()}}),e)}))),Vj=function(e,t,r){if("function"==typeof t&&(r=t,t=void 0),void 0===r)return Gj.sync(e,t);Gj.errback(e,t,r)},Wj=Gj.sync,Hj=Gj.async,qj=function(e,t,r){"function"==typeof t&&(r=t),r(new Error("Transforming files is not supported in browsers"),null)};function Kj(){throw new Error("Transforming files is not supported in browsers")}function zj(){return Promise.reject(new Error("Transforming files is not supported in browsers"))}var Xj=ES(Hw.mark((function e(t,r,n){var a;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(ZC(n),"t0",1);case 1:if(null!==(a=e.t0)){e.next=4;break}return e.abrupt("return",null);case 4:if(t){e.next=6;break}throw new Error("No AST given");case 6:return e.delegateYield(Mj(a,r,t),"t1",7);case 7:return e.abrupt("return",e.t1);case 8:case"end":return e.stop()}}),e)}))),Yj=function(e,t,r,n){if("function"==typeof r&&(n=r,r=void 0),void 0===n)return Xj.sync(e,t,r);Xj.errback(e,t,r,n)},Jj=Xj.sync,$j=Xj.async,Qj=ES(Hw.mark((function e(t,r){var n;return Hw.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.delegateYield(ZC(r),"t0",1);case 1:if(null!==(n=e.t0)){e.next=4;break}return e.abrupt("return",null);case 4:return e.delegateYield(Sj(n.passes,fj(n),t),"t1",5);case 5:return e.abrupt("return",e.t1);case 6:case"end":return e.stop()}}),e)}))),Zj=function(e,t,r){if("function"==typeof t&&(r=t,t=void 0),void 0===r)return Qj.sync(e,t);Qj.errback(e,t,r)},eP=Qj.sync,tP=Qj.async,rP=Object.freeze([".js",".jsx",".es6",".es",".mjs"]),nP=function(){function e(){}return e.prototype.init=function(e){return fT(e)},e}();function aP(e){throw new Error("The ("+e+") Babel 5 plugin is being run with an unsupported Babel version.")}function sP(e){return function(t,r,n){return t.assertVersion||(t=Object.assign(function(e){var t=null;"string"==typeof e.version&&/^7\./.test(e.version)&&(!(t=Object.getPrototypeOf(e))||iP(t,"version")&&iP(t,"transform")&&iP(t,"template")&&iP(t,"types")||(t=null));return Object.assign({},t,{},e)}(t),{assertVersion:function(e){!function(e,t){if("number"==typeof e){if(!Number.isInteger(e))throw new Error("Expected string or integer value.");e="^"+e+".0.0-0"}if("string"!=typeof e)throw new Error("Expected string or integer value.");var r,n=Error.stackTraceLimit;"number"==typeof n&&n<25&&(Error.stackTraceLimit=25);r="7."===t.slice(0,2)?new Error('Requires Babel "^7.0.0-beta.41", but was loaded with "'+t+"\". You'll need to update your @babel/core version."):new Error('Requires Babel "'+e+'", but was loaded with "'+t+'". If you are sure you have a compatible version of @babel/core, it is likely that something in your build process is loading the wrong version. Inspect the stack trace of this error to look for the first entry that doesn\'t mention "@babel/core" or "babel-core" to see what is calling Babel.');"number"==typeof n&&(Error.stackTraceLimit=n);throw Object.assign(r,{code:"BABEL_VERSION_UNSUPPORTED",version:t,range:e})}(e,t.version)}})),e(t,r||{},n)}}function iP(e,t){return Object.prototype.hasOwnProperty.call(e,t)}var oP=Object.freeze({__proto__:null,declare:sP}),uP=sP((function(e,t){e.assertVersion(7);var r=t.helperVersion,n=void 0===r?"7.0.0-beta.0":r,a=t.whitelist,s=void 0!==a&&a;if(!1!==s&&(!Array.isArray(s)||s.some((function(e){return"string"!=typeof e}))))throw new Error(".whitelist must be undefined, false, or an array of strings");var i=s?new Set(s):null;return{name:"external-helpers",pre:function(e){e.set("helperGenerator",(function(t){if((!e.availableHelper||e.availableHelper(t,n))&&(!i||i.has(t)))return oo(Qi("babelHelpers"),Qi(t))}))}}})),cP=Wt(oP),lP=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-async-generators",manipulateOptions:function(e,t){t.plugins.push("asyncGenerators")}}}));t.default=r}))),pP=sP((function(e){return e.assertVersion(7),{name:"syntax-class-properties",manipulateOptions:function(e,t){t.plugins.push("classProperties","classPrivateProperties","classPrivateMethods")}}})),dP=sP((function(e,t){e.assertVersion(7);var r=t.legacy,n=void 0!==r&&r;if("boolean"!=typeof n)throw new Error("'legacy' must be a boolean.");var a=t.decoratorsBeforeExport;if(void 0===a){if(!n)throw new Error("The '@babel/plugin-syntax-decorators' plugin requires a 'decoratorsBeforeExport' option, whose value must be a boolean. If you want to use the legacy decorators semantics, you can set the 'legacy: true' option.")}else{if(n)throw new Error("'decoratorsBeforeExport' can't be used with legacy decorators.");if("boolean"!=typeof a)throw new Error("'decoratorsBeforeExport' must be a boolean.")}return{name:"syntax-decorators",manipulateOptions:function(e,t){t.plugins.push(n?"decorators-legacy":["decorators",{decoratorsBeforeExport:a}])}}})),fP=sP((function(e){return e.assertVersion(7),{name:"syntax-do-expressions",manipulateOptions:function(e,t){t.plugins.push("doExpressions")}}})),hP=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-dynamic-import",manipulateOptions:function(e,t){t.plugins.push("dynamicImport")}}}));t.default=r}))),mP=sP((function(e){return e.assertVersion(7),{name:"syntax-export-default-from",manipulateOptions:function(e,t){t.plugins.push("exportDefaultFrom")}}})),yP=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-export-namespace-from",manipulateOptions:function(e,t){t.plugins.push("exportNamespaceFrom")}}}));t.default=r}))),gP=sP((function(e,t){e.assertVersion(7);var r=t.all,n=t.enums;if("boolean"!=typeof r&&void 0!==r)throw new Error(".all must be a boolean, or undefined");if("boolean"!=typeof n&&void 0!==n)throw new Error(".enums must be a boolean, or undefined");return{name:"syntax-flow",manipulateOptions:function(e,t){t.plugins.some((function(e){return"typescript"===(Array.isArray(e)?e[0]:e)}))||t.plugins.push(["flow",{all:r,enums:n}])}}})),vP=sP((function(e){return e.assertVersion(7),{name:"syntax-function-bind",manipulateOptions:function(e,t){t.plugins.push("functionBind")}}})),bP=sP((function(e){return e.assertVersion(7),{name:"syntax-function-sent",manipulateOptions:function(e,t){t.plugins.push("functionSent")}}})),xP=sP((function(e){return e.assertVersion(7),{name:"syntax-import-meta",manipulateOptions:function(e,t){t.plugins.push("importMeta")}}})),EP=sP((function(e){return e.assertVersion(7),{name:"syntax-jsx",manipulateOptions:function(e,t){t.plugins.some((function(e){return"typescript"===(Array.isArray(e)?e[0]:e)}))||t.plugins.push("jsx")}}})),AP=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-object-rest-spread",manipulateOptions:function(e,t){t.plugins.push("objectRestSpread")}}}));t.default=r}))),wP=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-optional-catch-binding",manipulateOptions:function(e,t){t.plugins.push("optionalCatchBinding")}}}));t.default=r}))),SP=["minimal","smart","fsharp"],DP=sP((function(e,t){var r=t.proposal;if(e.assertVersion(7),"string"!=typeof r||!SP.includes(r))throw new Error("The pipeline operator plugin requires a 'proposal' option.'proposal' must be one of: "+SP.join(", ")+". More details: https://babeljs.io/docs/en/next/babel-plugin-proposal-pipeline-operator");return{name:"syntax-pipeline-operator",manipulateOptions:function(e,t){t.plugins.push(["pipelineOperator",{proposal:r}])}}})),CP=sP((function(e){return e.assertVersion(7),{name:"syntax-top-level-await",manipulateOptions:function(e,t){t.plugins.push("topLevelAwait")}}}));function TP(e,t){var r=[];e.forEach((function(e,n){(Array.isArray(e)?e[0]:e)===t&&r.unshift(n)}));for(var n=0,a=r;n<a.length;n++){var s=a[n];e.splice(s,1)}}var jP=sP((function(e,t){var r=t.isTSX;return e.assertVersion(7),{name:"syntax-typescript",manipulateOptions:function(e,t){var n=t.plugins;TP(n,"flow"),TP(n,"jsx"),t.plugins.push("typescript","classProperties","objectRestSpread"),r&&t.plugins.push("jsx")}}})),PP=dE.expression("\n (function () {\n var REF = FUNCTION;\n return function NAME(PARAMS) {\n return REF.apply(this, arguments);\n };\n })()\n"),kP=dE.expression("\n (function () {\n var REF = FUNCTION;\n function NAME(PARAMS) {\n return REF.apply(this, arguments);\n }\n return NAME;\n })()\n"),FP=dE("\n function NAME(PARAMS) { return REF.apply(this, arguments); }\n function REF() {\n REF = FUNCTION;\n return REF.apply(this, arguments);\n }\n");function _P(e,t){e.isClassMethod()||e.isObjectMethod()?function(e,t){var r=e.node,n=r.body,a=$i(null,[],Ri(n.body),!0);n.body=[mo(Li(Li(t,[a]),[]))],r.async=!1,r.generator=!1,e.get("body.body.0.argument.callee.arguments.0").unwrapFunctionEnvironment()}(e,t):function(e,t){var r=e.node,n=e.isFunctionDeclaration(),a=r.id,s=n?FP:a?kP:PP;e.isArrowFunctionExpression()&&e.arrowFunctionToExpression(),r.id=null,n&&(r.type="FunctionExpression");var i=Li(t,[r]),o=s({NAME:a||null,REF:e.scope.generateUidIdentifier(a?a.name:"ref"),FUNCTION:i,PARAMS:r.params.reduce((function(t,r){return t.done=t.done||X(r)||M(r),t.done||t.params.push(e.scope.generateUidIdentifier("x")),t}),{params:[],done:!1}).params});if(n)e.replaceWith(o[0]),e.insertAfter(o[1]);else{var u=o.callee.body.body[1].argument;a||gE({node:u,parent:e.parent,scope:e.scope}),!u||u.id||r.params.length?e.replaceWith(o):e.replaceWith(i)}}(e,t)}function IP(e){var t,r=e.node||e;(t=r.leadingComments)&&t.some((function(e){return/[@#]__PURE__/.test(e.value)}))||hp(r,"leading","#__PURE__")}var BP={Function:function(e){e.skip()},AwaitExpression:function(e,t){var r=t.wrapAwait,n=e.get("argument");e.parentPath.isYieldExpression()?e.replaceWith(n.node):e.replaceWith(Qo(r?Li(pp(r),[n.node]):n.node))}};function OP(e,t){e.traverse(BP,{wrapAwait:t.wrapAwait});var r=function(e){if(e.parentPath.isCallExpression({callee:e.node}))return!0;var t=e.parentPath;if(t.isMemberExpression()&&S(t.node.property,{name:"bind"})){var r=t.parentPath;return r.isCallExpression()&&1===r.node.arguments.length&&V(r.node.arguments[0])&&r.parentPath.isCallExpression({callee:r.node})}return!1}(e);e.node.async=!1,e.node.generator=!0,_P(e,pp(t.wrapAsync)),e.isObjectMethod()||e.isClassMethod()||e.parentPath.isObjectProperty()||e.parentPath.isClassProperty()||r||!e.isExpression()||IP(e)}var NP=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-async-generators",manipulateOptions:function(e,t){t.plugins.push("asyncGenerators")}}}));t.default=r}))),RP=dE("\n async function wrapper() {\n var ITERATOR_COMPLETION = true;\n var ITERATOR_HAD_ERROR_KEY = false;\n var ITERATOR_ERROR_KEY;\n try {\n for (\n var ITERATOR_KEY = GET_ITERATOR(OBJECT), STEP_KEY, STEP_VALUE;\n (\n STEP_KEY = await ITERATOR_KEY.next(),\n ITERATOR_COMPLETION = STEP_KEY.done,\n STEP_VALUE = await STEP_KEY.value,\n !ITERATOR_COMPLETION\n );\n ITERATOR_COMPLETION = true) {\n }\n } catch (err) {\n ITERATOR_HAD_ERROR_KEY = true;\n ITERATOR_ERROR_KEY = err;\n } finally {\n try {\n if (!ITERATOR_COMPLETION && ITERATOR_KEY.return != null) {\n await ITERATOR_KEY.return();\n }\n } finally {\n if (ITERATOR_HAD_ERROR_KEY) {\n throw ITERATOR_ERROR_KEY;\n }\n }\n }\n }\n");var MP=sP((function(e){e.assertVersion(7);var t={Function:function(e){e.skip()},YieldExpression:function(e,t){var r=e.node;if(r.delegate){var n=t.addHelper("asyncGeneratorDelegate");r.argument=Li(n,[Li(t.addHelper("asyncIterator"),[r.argument]),t.addHelper("awaitAsyncGenerator")])}}},r={Function:function(e){e.skip()},ForOfStatement:function(e,t){var r=t.file,n=e.node;if(n.await){var a=function(e,t){var r,n=t.getAsyncIterator,a=e.node,s=e.scope,i=e.parent,o=s.generateUidIdentifier("step"),u=s.generateUidIdentifier("value"),c=a.left;S(c)||yt(c)||_(c)?r=Ki(_i("=",c,u)):q(c)&&(r=Do(c.kind,[Co(c.declarations[0].id,u)]));var l=RP({ITERATOR_HAD_ERROR_KEY:s.generateUidIdentifier("didIteratorError"),ITERATOR_COMPLETION:s.generateUidIdentifier("iteratorNormalCompletion"),ITERATOR_ERROR_KEY:s.generateUidIdentifier("iteratorError"),ITERATOR_KEY:s.generateUidIdentifier("iterator"),GET_ITERATOR:n,OBJECT:a.right,STEP_VALUE:u,STEP_KEY:o});l=l.body.body;var p=C(i),d=l[3].block.body,f=d[0];return p&&(d[0]=eo(i.label,f)),{replaceParent:p,node:l,declar:r,loop:f}}(e,{getAsyncIterator:r.addHelper("asyncIterator")}),s=a.declar,i=a.loop,o=i.body;e.ensureBlock(),s&&o.body.push(s),o.body=o.body.concat(n.body.body),ef(i,n),ef(i.body,n.body),a.replaceParent?e.parentPath.replaceWithMultiple(a.node):e.replaceWithMultiple(a.node)}}},n={Function:function(e,n){e.node.async&&(e.traverse(r,n),e.node.generator&&(e.traverse(t,n),OP(e,{wrapAsync:n.addHelper("wrapAsyncGenerator"),wrapAwait:n.addHelper("awaitAsyncGenerator")})))}};return{name:"proposal-async-generator-functions",inherits:NP,visitor:{Program:function(e,t){e.traverse(n,t)}}}})),LP=function(){function e(){this._map=new WeakMap}var t=e.prototype;return t.has=function(e){return this._map.has(e)},t.get=function(e){if(this.has(e)){var t=this._map.get(e),r=t.value;return t.count--,0===t.count?_i("=",r,e):r}},t.set=function(e,t,r){return this._map.set(e,{count:r,value:t})},e}(),UP={memoise:function(){},handle:function(e){var t=e.node,r=e.parent,n=e.parentPath;if(n.isUpdateExpression({argument:t})){var a=r.operator,s=r.prefix;this.memoise(e,2);var i=Ii(a[0],wo("+",this.get(e)),ro(1));if(s)n.replaceWith(this.set(e,i));else{var o=e.scope,u=o.generateUidIdentifierBasedOnNode(t);o.push({id:u}),i.left=_i("=",pp(u),i.left),n.replaceWith(yo([this.set(e,i),pp(u)]))}}else{if(n.isAssignmentExpression({left:t})){var c=r.operator,l=r.right;return"="!==c&&(this.memoise(e,2),l=Ii(c.slice(0,-1),this.get(e),l)),void n.replaceWith(this.set(e,l))}if(n.isCallExpression({callee:t})){var p=r.arguments;n.replaceWith(this.call(e,p))}else n.isObjectProperty({value:t})&&n.parentPath.isObjectPattern()||n.isAssignmentPattern({left:t})&&n.parentPath.isObjectProperty({value:r})&&n.parentPath.parentPath.isObjectPattern()||n.isArrayPattern()||n.isAssignmentPattern({left:t})&&n.parentPath.isArrayPattern()||n.isRestElement()?e.replaceWith(this.destructureSet(e)):e.replaceWith(this.get(e))}}};function GP(e,t,r){e.traverse(t,Object.assign({},UP,{},r,{memoiser:new LP}))}function VP(e,t,r){return 1===r.length&&le(r[0])&&S(r[0].argument,{name:"arguments"})?Li(oo(e,Qi("apply")),[t,r[0].argument]):Li(oo(e,Qi("call")),[t].concat(r))}function WP(e,t,r,n){e=pp(e);var a=t||n?e:oo(e,Qi("prototype"));return Li(r.addHelper("getPrototypeOf"),[a])}function HP(e){if(e.node.computed){var t=ks[e.type],r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}var s=a;"key"!==s&&e.skipKey(s)}}else e.skip()}var qP={TypeAnnotation:function(e){e.skip()},Function:function(e){e.isMethod()||e.isArrowFunctionExpression()||e.skip()},"Method|ClassProperty|ClassPrivateProperty":function(e){HP(e)}},KP=sA.visitors.merge([qP,{Super:function(e,t){var r=e.node,n=e.parentPath;n.isMemberExpression({object:r})&&t.handle(n)}}]),zP={memoise:function(e,t){var r=e.scope,n=e.node,a=n.computed,s=n.property;if(a){var i=r.maybeGenerateMemoised(s);i&&this.memoiser.set(s,i,t)}},prop:function(e){var t=e.node,r=t.computed,n=t.property;return this.memoiser.has(n)?pp(this.memoiser.get(n)):r?pp(n):to(n.name)},get:function(e){return Li(this.file.addHelper("get"),[WP(this.getObjectRef(),this.isStatic,this.file,this.isPrivateMethod),this.prop(e),xo()])},set:function(e,t){return Li(this.file.addHelper("set"),[WP(this.getObjectRef(),this.isStatic,this.file,this.isPrivateMethod),this.prop(e),t,xo(),ao(e.isInStrictMode())])},destructureSet:function(e){throw e.buildCodeFrameError("Destructuring to a super field is not supported yet.")},call:function(e,t){return VP(this.get(e),xo(),t)}},XP=Object.assign({},zP,{prop:function(e){var t=e.node.property;return this.memoiser.has(t)?pp(this.memoiser.get(t)):pp(t)},get:function(e){var t,r=this.isStatic,n=this.superRef,a=e.node.computed,s=this.prop(e);return t=r?n?pp(n):oo(Qi("Function"),Qi("prototype")):oo(n?pp(n):Qi("Object"),Qi("prototype")),oo(t,s,a)},set:function(e,t){var r=e.node.computed,n=this.prop(e);return _i("=",oo(xo(),n,r),t)},destructureSet:function(e){var t=e.node.computed,r=this.prop(e);return oo(xo(),r,t)}}),YP=function(){function e(e){var t=e.methodPath;this.methodPath=t,this.isStatic=t.isObjectMethod()||t.node.static,this.isPrivateMethod=t.isPrivate()&&t.isMethod(),this.file=e.file,this.superRef=e.superRef,this.isLoose=e.isLoose,this.opts=e}var t=e.prototype;return t.getObjectRef=function(){return pp(this.opts.objectRef||this.opts.getObjectRef())},t.replace=function(){var e=this.isLoose?XP:zP;GP(this.methodPath,KP,Object.assign({file:this.file,isStatic:this.isStatic,isPrivateMethod:this.isPrivateMethod,getObjectRef:this.getObjectRef.bind(this),superRef:this.superRef},e))},e}();function JP(e){if(e.node.declare)throw e.buildCodeFrameError("TypeScript 'declare' fields must first be transformed by @babel/plugin-transform-typescript.\nIf you have already enabled that plugin (or '@babel/preset-typescript'), make sure that it runs before any plugin related to additional class features:\n - @babel/plugin-proposal-class-properties\n - @babel/plugin-proposal-private-methods\n - @babel/plugin-proposal-decorators")}function $P(){var e=i(["\n Object.defineProperty(",", ",", {\n // configurable is false by default\n // enumerable is false by default\n // writable is false by default\n value: ","\n });\n "]);return $P=function(){return e},e}function QP(){var e=i(["\n Object.defineProperty(",", ",", {\n // configurable is false by default\n // enumerable is false by default\n // writable is false by default\n get: ",",\n set: ","\n })\n "]);return QP=function(){return e},e}function ZP(){var e=i(["",".add(",")"]);return ZP=function(){return e},e}function ek(){var e=i(["\n ",".set(",", {\n get: ",",\n set: ","\n });\n "]);return ek=function(){return e},e}function tk(){var e=i(["\n Object.defineProperty(",", ",", {\n // configurable is false by default\n // enumerable is false by default\n // writable is false by default\n get: ",",\n set: ","\n });\n "]);return tk=function(){return e},e}function rk(){var e=i(["\n Object.defineProperty(",", ",", {\n // configurable is false by default\n // enumerable is false by default\n // writable is false by default\n value: ","\n });\n "]);return rk=function(){return e},e}function nk(){var e=i(["\n var "," = {\n // configurable is false by default\n // enumerable is false by default\n writable: true,\n value: ","\n };\n "]);return nk=function(){return e},e}function ak(){var e=i(["\n var "," = {\n // configurable is false by default\n // enumerable is false by default\n // writable is false by default\n get: ",",\n set: ","\n }\n "]);return ak=function(){return e},e}function sk(){var e=i(["",".set(",", {\n // configurable is always false for private elements\n // enumerable is always false for private elements\n writable: true,\n value: ",",\n })"]);return sk=function(){return e},e}function ik(){var e=i(["\n Object.defineProperty(",", ",", {\n // configurable is false by default\n // enumerable is false by default\n writable: true,\n value: ","\n });\n "]);return ik=function(){return e},e}function ok(){var e=i(["BASE(REF, PROP)[PROP]"]);return ok=function(){return e},e}function uk(){var e=i(["var "," = new WeakMap();"]);return uk=function(){return e},e}function ck(){var e=i(["var "," = new WeakSet();"]);return ck=function(){return e},e}function lk(){var e=i(["var "," = new WeakMap();"]);return lk=function(){return e},e}function pk(){var e=i(["\n var "," = ",'("','")\n ']);return pk=function(){return e},e}var dk={PrivateName:function(e){var t=this.privateNamesMap,r=e.node,n=e.parentPath;n.isMemberExpression({property:r})&&t.has(r.id.name)&&this.handle(n)},Class:function(e){var t=this.privateNamesMap,r=e.get("body.body"),n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s;if(i.isPrivate()&&t.has(i.node.key.id.name)){e.traverse(fk,this),e.skip();break}}}},fk=sA.visitors.merge([{PrivateName:dk.PrivateName},qP]),hk={memoise:function(e,t){var r=e.scope,n=e.node.object,a=r.maybeGenerateMemoised(n);a&&this.memoiser.set(n,a,t)},receiver:function(e){var t=e.node.object;return this.memoiser.has(t)?pp(this.memoiser.get(t)):pp(t)},get:function(e){var t=this.classRef,r=this.privateNamesMap,n=this.file,a=e.node.property.id.name,s=r.get(a),i=s.id,o=s.static,u=s.method,c=s.methodId,l=s.getId,p=s.setId,d=l||p;if(o){var f=u&&!d?"classStaticPrivateMethodGet":"classStaticPrivateFieldSpecGet";return Li(n.addHelper(f),[this.receiver(e),pp(t),pp(i)])}return u?d?Li(n.addHelper("classPrivateFieldGet"),[this.receiver(e),pp(i)]):Li(n.addHelper("classPrivateMethodGet"),[this.receiver(e),pp(i),pp(c)]):Li(n.addHelper("classPrivateFieldGet"),[this.receiver(e),pp(i)])},set:function(e,t){var r=this.classRef,n=this.privateNamesMap,a=this.file,s=e.node.property.id.name,i=n.get(s),o=i.id,u=i.static,c=i.method,l=i.setId,p=i.getId;if(u){var d=c&&!(p||l)?"classStaticPrivateMethodSet":"classStaticPrivateFieldSpecSet";return Li(a.addHelper(d),[this.receiver(e),pp(r),pp(o),t])}return c?l?Li(a.addHelper("classPrivateFieldSet"),[this.receiver(e),pp(o),t]):Li(a.addHelper("classPrivateMethodSet"),[]):Li(a.addHelper("classPrivateFieldSet"),[this.receiver(e),pp(o),t])},destructureSet:function(e){var t=this.privateNamesMap,r=this.file,n=e.node.property.id.name,a=t.get(n).id;return oo(Li(r.addHelper("classPrivateFieldDestructureSet"),[this.receiver(e),pp(a)]),Qi("value"))},call:function(e,t){return this.memoise(e,1),VP(this.get(e),this.receiver(e),t)}},mk={handle:function(e){var t=this.privateNamesMap,r=this.file,n=e.node.object,a=e.node.property.id.name;e.replaceWith(dE.expression(ok())({BASE:r.addHelper("classPrivateFieldLooseBase"),REF:n,PROP:t.get(a).id}))}};function yk(e,t,r){var n=r.get(t.node.key.id.name).id,a=t.node.value||t.scope.buildUndefinedNode();return dE.statement.ast(ik(),e,n,a)}function gk(e,t,r){var n=r.get(t.node.key.id.name).id,a=t.node.value||t.scope.buildUndefinedNode();return dE.statement.ast(sk(),n,e,a)}function vk(e,t){var r=t.get(e.node.key.id.name),n=r.id,a=r.getId,s=r.setId,i=r.initAdded,o=a||s;if(e.isProperty()||!i&&o){if(o)return t.set(e.node.key.id.name,Object.assign({},r,{initAdded:!0})),dE.statement.ast(ak(),n.name,a?a.name:e.scope.buildUndefinedNode(),s?s.name:e.scope.buildUndefinedNode());var u=e.node.value||e.scope.buildUndefinedNode();return dE.statement.ast(nk(),n,u)}}function bk(e,t,r){var n=r.get(t.node.key.id.name),a=n.methodId,s=n.id,i=n.getId,o=n.setId;if(!n.initAdded)return a?dE.statement.ast(rk(),e,s,a.name):i||o?(r.set(t.node.key.id.name,Object.assign({},n,{initAdded:!0})),dE.statement.ast(tk(),e,s,i?i.name:t.scope.buildUndefinedNode(),o?o.name:t.scope.buildUndefinedNode())):void 0}function xk(e,t,r){var n=r.get(t.node.key.id.name),a=n.id,s=n.getId,i=n.setId;if(!n.initAdded)return s||i?(r.set(t.node.key.id.name,Object.assign({},n,{initAdded:!0})),dE.statement.ast(ek(),a,e,s?s.name:t.scope.buildUndefinedNode(),i?i.name:t.scope.buildUndefinedNode())):dE.statement.ast(ZP(),a,e)}function Ek(e,t){var r=t.node,n=r.key,a=r.computed,s=t.node.value||t.scope.buildUndefinedNode();return Ki(_i("=",oo(e,n,a||ft(n)),s))}function Ak(e,t,r){var n=t.node,a=n.key,s=n.computed,i=t.node.value||t.scope.buildUndefinedNode();return Ki(Li(r.addHelper("defineProperty"),[e,s||ft(a)?a:to(a.name),i]))}function wk(e,t,r,n){var a=n.get(t.node.key.id.name),s=a.id,i=a.methodId,o=a.getId,u=a.setId;if(!a.initAdded)return o||u?(n.set(t.node.key.id.name,Object.assign({},a,{initAdded:!0})),dE.statement.ast(QP(),e,s,o?o.name:t.scope.buildUndefinedNode(),u?u.name:t.scope.buildUndefinedNode())):dE.statement.ast($P(),e,s,i.name)}function Sk(e,t,r){void 0===r&&(r=!1);var n=t.get(e.node.key.id.name),a=n.id,s=n.methodId,i=n.getId,o=n.setId,u=n.getterDeclared,c=n.setterDeclared,l=n.static,p=e.node,d=p.params,f=p.body,h=p.generator,m=p.async,y=$i(s,d,f,h,m),g=i&&!u&&0===d.length,v=o&&!c&&d.length>0;return g?(t.set(e.node.key.id.name,Object.assign({},n,{getterDeclared:!0})),Do("var",[Co(i,y)])):v?(t.set(e.node.key.id.name,Object.assign({},n,{setterDeclared:!0})),Do("var",[Co(o,y)])):Do("var",l&&!r?[Co(a,$i(a,d,f,h,m))]:[Co(s,y)])}var Dk=sA.visitors.merge([{ThisExpression:function(e,t){t.needsClassRef=!0,e.replaceWith(pp(t.classRef))}},qP]);function Ck(e,t,r,n,a){var s={classRef:t,needsClassRef:!1};return new YP({methodPath:e,isLoose:a,superRef:r,file:n,getObjectRef:function(){return s.needsClassRef=!0,e.node.static?t:oo(t,Qi("prototype"))}}).replace(),e.isProperty()&&e.traverse(Dk,s),s.needsClassRef}function Tk(){var e=i(["","(this)"]);return Tk=function(){return e},e}function jk(){var e=i(["let "," = ",""]);return jk=function(){return e},e}function Pk(){var e=i(["\n ","(\n ",",\n function (",", ",") {\n ","\n return { F: ",", d: "," };\n },\n ","\n )\n "]);return Pk=function(){return e},e}function kk(){var e=i(["return ",""]);return kk=function(){return e},e}function Fk(e){return!(!e.decorators||!e.decorators.length)}function _k(e,t){return t?fo(Qi(e),t):null}function Ik(e){var t;return e.decorators&&e.decorators.length>0&&(t=Fi(e.decorators.map((function(e){return e.expression})))),e.decorators=void 0,t}function Bk(e){return e.computed?e.key:S(e.key)?to(e.key.name):to(String(e.key.value))}function Ok(e,t,r){var n=r.node,a=r.scope,s=r.isClassMethod();if(r.isPrivate())throw r.buildCodeFrameError("Private "+(s?"methods":"fields")+" in decorated classes are not supported yet.");new YP({methodPath:r,methodNode:n,objectRef:e,isStatic:n.static,superRef:t,scope:a,file:this},!0).replace();var i,o,u=[_k("kind",to(s?n.kind:"field")),_k("decorators",Ik(n)),_k("static",n.static&&ao(!0)),_k("key",Bk(n))].filter(Boolean);if(s){var c=n.computed?null:n.key;Id(n),u.push(_k("value",gE({node:n,id:c,scope:a})||n))}else n.value?u.push((i="value",o=dE.statements.ast(kk(),n.value),po("method",Qi(i),[],Ri(o)))):u.push(_k("value",a.buildUndefinedNode()));return r.remove(),lo(u)}function Nk(){var e=i(["super(...args)"]);return Nk=function(){return e},e}var Rk=sA.visitors.merge([{Super:function(e){var t=e.node,r=e.parentPath;r.isCallExpression({callee:t})&&this.push(r)}},qP]),Mk={"TSTypeAnnotation|TypeAnnotation":function(e){e.skip()},ReferencedIdentifier:function(e){this.scope.hasOwnBinding(e.node.name)&&(this.scope.rename(e.node.name),e.skip())}};function Lk(e,t){if(t.classBinding&&t.classBinding===e.scope.getBinding(e.node.name)){var r=Li(t.file.addHelper("classNameTDZError"),[to(e.node.name)]);e.replaceWith(yo([r,e.node])),e.skip()}}var Uk={ReferencedIdentifier:Lk};function Gk(e,t,r,n){if(r.length){var a=!!e.node.superClass;if(!t){var s=qo("constructor",Qi("constructor"),[],Ri([]));a&&(s.params=[ho(Qi("args"))],s.body.body.push(dE.statement.ast(Nk()))),t=e.get("body").unshiftContainer("body",s)[0]}if(n&&n(Mk,{scope:t.scope}),a){var i=[];t.traverse(Rk,i);for(var o=0,u=i;o<u.length;o++){u[o].insertAfter(r)}}else t.get("body").unshiftContainer("body",r)}}var Vk=Object.freeze({fields:2,privateMethods:4,decorators:8}),Wk="@babel/plugin-class-features/featuresKey",Hk="@babel/plugin-class-features/looseKey";function qk(e,t){return!!(e.get(Wk)&t)}function Kk(e,t){return!!(e.get(Hk)&t)}function zk(e,t){if(Fk(e.node)){if(!qk(t,Vk.decorators))throw e.buildCodeFrameError('Decorators are not enabled.\nIf you are using ["@babel/plugin-proposal-decorators", { "legacy": true }], make sure it comes *before* "@babel/plugin-proposal-class-properties" and enable loose mode, like so:\n\t["@babel/plugin-proposal-decorators", { "legacy": true }]\n\t["@babel/plugin-proposal-class-properties", { "loose": true }]');if(e.isPrivate())throw e.buildCodeFrameError("Private "+(e.isClassMethod()?"methods":"fields")+" in decorated classes are not supported yet.")}if(e.isPrivate()&&e.isMethod()&&!qk(t,Vk.privateMethods))throw e.buildCodeFrameError("Class private methods are not enabled.");if(qk(t,Vk.privateMethods)&&qk(t,Vk.fields)&&Kk(t,Vk.privateMethods)!==Kk(t,Vk.fields))throw e.buildCodeFrameError("'loose' mode configuration must be the same for both @babel/plugin-proposal-class-properties and @babel/plugin-proposal-private-methods");if(e.isProperty()&&!qk(t,Vk.fields))throw e.buildCodeFrameError("Class fields are not enabled.")}var Xk={name:"@babel/helper-create-class-features-plugin",version:"7.8.6",author:"The Babel Team (https://babeljs.io/team)",license:"MIT",description:"Compile class public and private fields, private methods and decorators to ES6",repository:"https://github.com/babel/babel/tree/master/packages/babel-helper-create-class-features-plugin",main:"lib/index.js",publishConfig:{access:"public"},keywords:["babel","babel-plugin"],dependencies:{"@babel/helper-function-name":"^7.8.3","@babel/helper-member-expression-to-functions":"^7.8.3","@babel/helper-optimise-call-expression":"^7.8.3","@babel/helper-plugin-utils":"^7.8.3","@babel/helper-replace-supers":"^7.8.6","@babel/helper-split-export-declaration":"^7.8.3"},peerDependencies:{"@babel/core":"^7.0.0"},devDependencies:{"@babel/core":"^7.8.6","@babel/helper-plugin-test-runner":"^7.8.3"}}.version.split(".").reduce((function(e,t){return 1e5*e+ +t}),0),Yk="@babel/plugin-class-features/version";function Jk(e){var t=e.name,r=e.feature,n=e.loose;return{name:t,manipulateOptions:e.manipulateOptions,pre:function(){!function(e,t,r){qk(e,t)||(e.set(Wk,e.get(Wk)|t),r&&e.set(Hk,e.get(Hk)|t))}(this.file,r,n),(!this.file.get(Yk)||this.file.get(Yk)<Xk)&&this.file.set(Yk,Xk)},visitor:{Class:function(e,t){if(this.file.get(Yk)===Xk){zk(e,this.file);var n,a=Kk(this.file,r),s=Fk(e.node),i=[],o=[],u=[],c=new Set,l=e.get("body").get("body"),p=Array.isArray(l),d=0;for(l=p?l:l[Symbol.iterator]();;){var f;if(p){if(d>=l.length)break;f=l[d++]}else{if((d=l.next()).done)break;f=d.value}var h=f;if(zk(h,this.file),h.node.computed&&u.push(h),h.isPrivate()){var m=h.node.key.id.name,y="get "+m,g="set "+m;if("get"===h.node.kind){if(c.has(y)||c.has(m)&&!c.has(g))throw h.buildCodeFrameError("Duplicate private field");c.add(y).add(m)}else if("set"===h.node.kind){if(c.has(g)||c.has(m)&&!c.has(y))throw h.buildCodeFrameError("Duplicate private field");c.add(g).add(m)}else{if(c.has(m)&&!c.has(y)&&!c.has(g)||c.has(m)&&(c.has(y)||c.has(g)))throw h.buildCodeFrameError("Duplicate private field");c.add(m)}}h.isClassMethod({kind:"constructor"})?n=h:(o.push(h),(h.isProperty()||h.isPrivate())&&i.push(h)),s||(s=Fk(h.node))}if(i.length||s){var v;e.isClassExpression()||!e.node.id?(gE(e),v=e.scope.generateUidIdentifier("class")):v=e.node.id;var b,x,E,A,w=function(e){var t=new Map,r=e,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s,o=i.isPrivate(),u=!i.isProperty(),c=!i.node.static;if(o){var l=i.node.key.id.name,p=t.has(l)?t.get(l):{id:i.scope.generateUidIdentifier(l),static:!c,method:u};"get"===i.node.kind?p.getId=i.scope.generateUidIdentifier("get_"+l):"set"===i.node.kind?p.setId=i.scope.generateUidIdentifier("set_"+l):"method"===i.node.kind&&(p.methodId=i.scope.generateUidIdentifier(l)),t.set(l,p)}}return t}(i),S=function(e,t,r){var n=[],a=e,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o,c=u[0],l=u[1],p=l.id,d=l.static,f=l.method,h=l.getId,m=l.setId,y=h||m;t?n.push(dE.statement.ast(pk(),p,r.addHelper("classPrivateFieldLooseKey"),c)):f&&!d?y?n.push(dE.statement.ast(lk(),p)):n.push(dE.statement.ast(ck(),p)):d||n.push(dE.statement.ast(uk(),p))}return n}(w,a,t);if(function(e,t,r,n,a){var s=t.get("body");n?s.traverse(dk,Object.assign({privateNamesMap:r,file:a},mk)):GP(s,dk,Object.assign({privateNamesMap:r,classRef:e,file:a},hk))}(v,e,w,a,t),s){x=b=[];var D=function(e,t,r,n){var a,s=t.node,i=t.scope,o=i.generateUidIdentifier("initialize"),u=s.id&&t.isDeclaration(),c=t.isInStrictMode(),l=s.superClass;s.type="ClassDeclaration",s.id||(s.id=pp(e)),l&&(a=i.generateUidIdentifierBasedOnNode(s.superClass,"super"),s.superClass=a);var p=Ik(s),d=Fi(r.map(Ok.bind(n,s.id,a))),f=dE.expression.ast(Pk(),function(e){try{return e.addHelper("decorate")}catch(e){throw"BABEL_HELPER_UNKNOWN"===e.code&&(e.message+="\n '@babel/plugin-transform-decorators' in non-legacy mode requires '@babel/core' version ^7.0.2 and you appear to be using an older version."),e}}(n),p||no(),o,l?a:null,s,pp(s.id),d,l),h="arguments.1.body.body.0";return c||f.arguments[1].body.directives.push(Oi(Ni("use strict"))),u&&(f=dE.ast(jk(),e,f),h="declarations.0.init."+h),{instanceNodes:[dE.statement.ast(Tk(),o)],wrapClass:function(e){return e.replaceWith(f),e.get(h)}}}(v,e,o,this.file);E=D.instanceNodes,A=D.wrapClass}else{b=function(e,t,r,n){var a=[],s={classBinding:t.node.id&&t.scope.getBinding(t.node.id.name),file:n},i=r,o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}var l=c,p=l.get("key");p.isReferencedIdentifier()?Lk(p,s):p.traverse(Uk,s);var d=l.node;if(!p.isConstantExpression()){var f=t.scope.generateUidIdentifierBasedOnNode(d.key);t.scope.push({id:f,kind:"let"}),a.push(Ki(_i("=",pp(f),d.key))),d.key=pp(f)}}return a}(0,e,u,this.file);var C=function(e,t,r,n,a,s){var i=[],o=[],u=!1,c=r,l=Array.isArray(c),p=0;for(c=l?c:c[Symbol.iterator]();;){var d;if(l){if(p>=c.length)break;d=c[p++]}else{if((p=c.next()).done)break;d=p.value}var f=d;JP(f);var h=f.node.static,m=!h,y=f.isPrivate(),g=!y,v=f.isProperty(),b=!v;if(h||b&&y){var x=Ck(f,e,t,a,s);u=u||x}switch(!0){case h&&y&&v&&s:u=!0,i.push(yk(pp(e),f,n));break;case h&&y&&v&&!s:u=!0,i.push(vk(f,n));break;case h&&g&&v&&s:u=!0,i.push(Ek(pp(e),f));break;case h&&g&&v&&!s:u=!0,i.push(Ak(pp(e),f,a));break;case m&&y&&v&&s:o.push(yk(xo(),f,n));break;case m&&y&&v&&!s:o.push(gk(xo(),f,n));break;case m&&y&&b&&s:o.unshift(bk(xo(),f,n)),i.push(Sk(f,n,s));break;case m&&y&&b&&!s:o.unshift(xk(xo(),f,n)),i.push(Sk(f,n,s));break;case h&&y&&b&&!s:u=!0,i.push(vk(f,n)),i.unshift(Sk(f,n,s));break;case h&&y&&b&&s:u=!0,i.push(wk(pp(e),f,0,n)),i.unshift(Sk(f,n,s));break;case m&&g&&v&&s:o.push(Ek(xo(),f));break;case m&&g&&v&&!s:o.push(Ak(xo(),f,a));break;default:throw new Error("Unreachable.")}}return{staticNodes:i.filter(Boolean),instanceNodes:o.filter(Boolean),wrapClass:function(t){var n=r,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}i.remove()}return u?(t.isClassExpression()?(t.scope.push({id:e}),t.replaceWith(_i("=",pp(e),t.node))):t.node.id||(t.node.id=e),t):t}}}(v,e.node.superClass,i,w,t,a);x=C.staticNodes,E=C.instanceNodes,A=C.wrapClass}E.length>0&&Gk(e,n,E,(function(e,t){if(!s){var r=i,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var o;if(n){if(a>=r.length)break;o=r[a++]}else{if((a=r.next()).done)break;o=a.value}var u=o;u.node.static||u.traverse(e,t)}}})),(e=A(e)).insertBefore(b),e.insertAfter([].concat(S,x))}}},PrivateName:function(e){if(this.file.get(Yk)===Xk)throw e.buildCodeFrameError('Unknown PrivateName "'+e+'"')},ExportDefaultDeclaration:function(e){if(this.file.get(Yk)===Xk){var t=e.get("declaration");t.isClassDeclaration()&&function(e){return Fk(e)||e.body.body.some(Fk)}(t.node)&&(t.node.id?nh(e):t.node.type="ClassExpression")}}}}}var $k=sP((function(e,t){return e.assertVersion(7),Jk({name:"proposal-class-properties",feature:Vk.fields,loose:t.loose,manipulateOptions:function(e,t){t.plugins.push("classProperties","classPrivateProperties")}})})),Qk=dE("\n DECORATOR(CLASS_REF = INNER) || CLASS_REF;\n"),Zk=dE("\n CLASS_REF.prototype;\n"),eF=dE("\n Object.getOwnPropertyDescriptor(TARGET, PROPERTY);\n"),tF=dE("\n (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY), (TEMP = TEMP ? TEMP.value : undefined), {\n enumerable: true,\n configurable: true,\n writable: true,\n initializer: function(){\n return TEMP;\n }\n })\n"),rF=new WeakSet;function nF(e){var t=(e.isClass()?[e].concat(e.get("body.body")):e.get("properties")).reduce((function(e,t){return e.concat(t.node.decorators||[])}),[]).filter((function(e){return!S(e.expression)}));if(0!==t.length)return yo(t.map((function(t){var r=t.expression;return _i("=",t.expression=e.scope.generateDeclaredUidIdentifier("dec"),r)})).concat([e.node]))}function aF(e){return!(!e.decorators||!e.decorators.length)}function sF(e){return e.some((function(e){return e.decorators&&e.decorators.length}))}function iF(e,t,r){var n=e.scope.generateDeclaredUidIdentifier(e.isClass()?"class":"obj"),a=r.reduce((function(r,a){var s=a.decorators||[];if(a.decorators=null,0===s.length)return r;if(a.computed)throw e.buildCodeFrameError("Computed method/property decorators are not yet supported.");var i=ft(a.key)?a.key:to(a.key.name),o=e.isClass()&&!a.static?Zk({CLASS_REF:n}).expression:n;if(Ve(a,{static:!1})){var u=e.scope.generateDeclaredUidIdentifier("descriptor"),c=a.value?$i(null,[],Ri([mo(a.value)])):no();a.value=Li(t.addHelper("initializerWarningHelper"),[u,xo()]),rF.add(a.value),r=r.concat([_i("=",u,Li(t.addHelper("applyDecoratedDescriptor"),[pp(o),pp(i),Fi(s.map((function(e){return pp(e.expression)}))),lo([fo(Qi("configurable"),ao(!0)),fo(Qi("enumerable"),ao(!0)),fo(Qi("writable"),ao(!0)),fo(Qi("initializer"),c)])]))])}else r=r.concat(Li(t.addHelper("applyDecoratedDescriptor"),[pp(o),pp(i),Fi(s.map((function(e){return pp(e.expression)}))),R(a)||Ve(a,{static:!0})?tF({TEMP:e.scope.generateDeclaredUidIdentifier("init"),TARGET:pp(o),PROPERTY:pp(i)}).expression:eF({TARGET:pp(o),PROPERTY:pp(i)}).expression,pp(o)]));return r}),[]);return yo([_i("=",pp(n),e.node),yo(a),pp(n)])}function oF(e){var t=e.node,r=e.scope;if(aF(t)||sF(t.body.body))return Do("let",[Co(t.id?pp(t.id):r.generateUidIdentifier("class"),Id(t))])}var uF={ExportDefaultDeclaration:function(e){var t=e.get("declaration");if(t.isClassDeclaration()){var r=oF(t);if(r){var n=e.replaceWithMultiple([r,Ro(null,[Mo(pp(r.declarations[0].id),Qi("default"))])])[0];t.node.id||e.scope.registerDeclaration(n)}}},ClassDeclaration:function(e){var t=oF(e);t&&e.replaceWith(t)},ClassExpression:function(e,t){var r=nF(e)||function(e){if(aF(e.node)){var t=e.node.decorators||[];e.node.decorators=null;var r=e.scope.generateDeclaredUidIdentifier("class");return t.map((function(e){return e.expression})).reverse().reduce((function(e,t){return Qk({CLASS_REF:pp(r),DECORATOR:pp(t),INNER:e}).expression}),e.node)}}(e)||function(e,t){if(sF(e.node.body.body))return iF(e,t,e.node.body.body)}(e,t);r&&e.replaceWith(r)},ObjectExpression:function(e,t){var r=nF(e)||function(e,t){if(sF(e.node.properties))return iF(e,t,e.node.properties)}(e,t);r&&e.replaceWith(r)},AssignmentExpression:function(e,t){rF.has(e.node.right)&&e.replaceWith(Li(t.addHelper("initializerDefineProperty"),[pp(e.get("left.object").node),to(e.get("left.property").node.name||e.get("left.property").node.value),pp(e.get("right.arguments")[0].node),pp(e.get("right.arguments")[1].node)]))},CallExpression:function(e,t){3===e.node.arguments.length&&rF.has(e.node.arguments[2])&&e.node.callee.name===t.addHelper("defineProperty").name&&e.replaceWith(Li(t.addHelper("initializerDefineProperty"),[pp(e.get("arguments")[0].node),pp(e.get("arguments")[1].node),pp(e.get("arguments.2.arguments")[0].node),pp(e.get("arguments.2.arguments")[1].node)]))}},cF=sP((function(e,t){e.assertVersion(7);var r=t.legacy,n=void 0!==r&&r;if("boolean"!=typeof n)throw new Error("'legacy' must be a boolean.");var a=t.decoratorsBeforeExport;if(void 0===a){if(!n)throw new Error("The decorators plugin requires a 'decoratorsBeforeExport' option, whose value must be a boolean. If you want to use the legacy decorators semantics, you can set the 'legacy: true' option.")}else{if(n)throw new Error("'decoratorsBeforeExport' can't be used with legacy decorators.");if("boolean"!=typeof a)throw new Error("'decoratorsBeforeExport' must be a boolean.")}return n?{name:"proposal-decorators",inherits:dP,manipulateOptions:function(e){e.generatorOpts.decoratorsBeforeExport=a},visitor:uF}:Jk({name:"proposal-decorators",feature:Vk.decorators,manipulateOptions:function(e){var t=e.generatorOpts;e.parserOpts.plugins.push(["decorators",{decoratorsBeforeExport:a}]),t.decoratorsBeforeExport=a}})})),lF=sP((function(e){return e.assertVersion(7),{name:"proposal-do-expressions",inherits:fP,visitor:{DoExpression:{exit:function(e){var t=e.node.body.body;t.length?e.replaceExpressionWithStatements(t):e.replaceWith(e.scope.buildUndefinedNode())}}}}})),pF=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-dynamic-import",manipulateOptions:function(e,t){t.plugins.push("dynamicImport")}}}));t.default=r}))),dF=["commonjs","amd","systemjs"],fF=sP((function(e){return e.assertVersion(7),{name:"proposal-dynamic-import",inherits:pF,pre:function(){this.file.set("@babel/plugin-proposal-dynamic-import","7.8.3")},visitor:{Program:function(){var e=this.file.get("@babel/plugin-transform-modules-*");if(!dF.includes(e))throw new Error("@babel/plugin-proposal-dynamic-import depends on a modules\ntransform plugin. Supported plugins are:\n - @babel/plugin-transform-modules-commonjs ^7.4.0\n - @babel/plugin-transform-modules-amd ^7.4.0\n - @babel/plugin-transform-modules-systemjs ^7.4.0\n\nIf you are using Webpack or Rollup and thus don't want\nBabel to transpile your imports and exports, you can use\nthe @babel/plugin-syntax-dynamic-import plugin and let your\nbundler handle dynamic imports.\n")}}}})),hF=sP((function(e){return e.assertVersion(7),{name:"proposal-export-default-from",inherits:mP,visitor:{ExportNamedDeclaration:function(e){var t=e.node,r=e.scope,n=t.specifiers;if(Ke(n[0])){var a=n.shift().exported,s=r.generateUidIdentifier(a.name),i=[Uo([Go(s)],pp(t.source)),Ro(null,[Mo(pp(s),a)])];n.length>=1&&i.push(t);var o=e.replaceWithMultiple(i)[0];e.scope.registerDeclaration(o)}}}}})),mF=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-export-namespace-from",manipulateOptions:function(e,t){t.plugins.push("exportNamespaceFrom")}}}));t.default=r}))),yF=sP((function(e){return e.assertVersion(7),{name:"proposal-export-namespace-from",inherits:mF,visitor:{ExportNamedDeclaration:function(e){var t=e.node,r=e.scope,n=t.specifiers,a=Ke(n[0])?1:0;if(ze(n[a])){var s=[];1===a&&s.push(Ro(null,[n.shift()],t.source));var i=n.shift().exported,o=r.generateUidIdentifier(i.name);s.push(Uo([Vo(o)],pp(t.source)),Ro(null,[Mo(pp(o),i)])),t.specifiers.length>=1&&s.push(t);var u=e.replaceWithMultiple(s)[0];e.scope.registerDeclaration(u)}}}}})),gF=sP((function(e){function t(e,t){var r=function(e,t){var r=e.object||e.callee.object;return t.isStatic(r)&&r}(e,t);if(r)return pp(r);var n=function(e){var t=e.path.getData("functionBind");return t||(t=e.generateDeclaredUidIdentifier("context"),e.path.setData("functionBind",t))}(t);return e.object?e.callee=yo([_i("=",n,e.object),e.callee]):e.callee.object=_i("=",n,e.callee.object),n}return e.assertVersion(7),{name:"proposal-function-bind",inherits:vP,visitor:{CallExpression:function(e){var r=e.node,n=e.scope,a=r.callee;if(Ge(a)){var s=t(a,n);r.callee=oo(a.callee,Qi("call")),r.arguments.unshift(s)}},BindExpression:function(e){var r=e.node,n=t(r,e.scope);e.replaceWith(Li(oo(r.callee,Qi("bind")),[n]))}}}})),vF=sP((function(e){e.assertVersion(7);var t=function(e){return S(e.meta,{name:"function"})&&S(e.property,{name:"sent"})},r={Function:function(e){e.skip()},YieldExpression:function(e){(function(e,t){return c(e)&&S(e.left,{name:t})})(e.parent,this.sentId)||e.replaceWith(_i("=",Qi(this.sentId),e.node))},MetaProperty:function(e){t(e.node)&&e.replaceWith(Qi(this.sentId))}};return{name:"proposal-function-sent",inherits:bP,visitor:{MetaProperty:function(e,n){if(t(e.node)){var a=e.getFunctionParent();if(!a.node.generator)throw new Error("Parent generator function not found");var s=e.scope.generateUid("function.sent");a.traverse(r,{sentId:s}),a.node.body.body.unshift(Do("let",[Co(Qi(s),Qo())])),_P(a,n.addHelper("skipFirstGeneratorNext"))}}}}})),bF=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-json-strings",manipulateOptions:function(e,t){t.plugins.push("jsonStrings")}}}));t.default=r}))),xF=sP((function(e){e.assertVersion(7);var t=/(\\*)([\u2028\u2029])/g;function r(e,t,r){return t.length%2==1?e:t+"\\u"+r.charCodeAt(0).toString(16)}return{name:"proposal-json-strings",inherits:bF,visitor:{"DirectiveLiteral|StringLiteral":function(e){var n=e.node.extra;n&&n.raw&&(n.raw=n.raw.replace(t,r))}}}})),EF=sP((function(e){return e.assertVersion(7),{name:"syntax-logical-assignment-operators",manipulateOptions:function(e,t){t.plugins.push("logicalAssignment")}}})),AF=sP((function(e){return e.assertVersion(7),{name:"proposal-logical-assignment-operators",inherits:EF,visitor:{AssignmentExpression:function(e){var t=e.node,r=e.scope,n=t.operator,a=t.left,s=t.right;if("||="===n||"&&="===n||"??="===n){var i=pp(a);if(_(a)){var o=a.object,u=a.property,c=a.computed,l=r.maybeGenerateMemoised(o);if(l&&(a.object=l,i.object=_i("=",pp(l),o)),c){var p=r.maybeGenerateMemoised(u);p&&(a.property=p,i.property=_i("=",pp(p),u))}}e.replaceWith(io(n.slice(0,-1),i,_i("=",a,s)))}}}}})),wF=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-nullish-coalescing-operator",manipulateOptions:function(e,t){t.plugins.push("nullishCoalescingOperator")}}}));t.default=r}))),SF=sP((function(e,t){var r=t.loose,n=void 0!==r&&r;return e.assertVersion(7),{name:"proposal-nullish-coalescing-operator",inherits:wF,visitor:{LogicalExpression:function(e){var t=e.node,r=e.scope;if("??"===t.operator){var a,s=r.maybeGenerateMemoised(t.left);null===s?(s=t.left,a=pp(t.left)):a=_i("=",s,t.left),e.replaceWith(Gi(n?Ii("!=",a,no()):io("&&",Ii("!==",a,no()),Ii("!==",pp(s),r.buildUndefinedNode())),pp(s),t.right))}}}}})),DF=sP((function(e){return e.assertVersion(7),{name:"syntax-numeric-separator",manipulateOptions:function(e,t){t.plugins.push("numericSeparator")}}})),CF=sP((function(e){return e.assertVersion(7),{name:"proposal-numeric-separator",inherits:DF,visitor:{NumericLiteral:function(e){var t=e.node.extra;t&&/_/.test(t.raw)&&(t.raw=t.raw.replace(/_/g,""))}}}})),TF=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-object-rest-spread",manipulateOptions:function(e,t){t.plugins.push("objectRestSpread")}}}));t.default=r}))),jF=function(){var e=Qi("a"),t=fo(Qi("key"),e);return of(e,t,Ko([t]))?1:0}(),PF=sP((function(e,t){e.assertVersion(7);var r=t.useBuiltIns,n=void 0!==r&&r,a=t.loose,s=void 0!==a&&a;if("boolean"!=typeof s)throw new Error(".loose must be a boolean, or undefined");function i(e){return n?oo(Qi("Object"),Qi("assign")):e.addHelper("extends")}function o(e){var t=!1;return u(e,(function(e){t=!0,e.stop()})),t}function u(e,t){e.traverse({Expression:function(e){var t=e.parent.type;("AssignmentPattern"===t&&"right"===e.key||"ObjectProperty"===t&&e.parent.computed&&"key"===e.key)&&e.skip()},RestElement:t})}function c(e,t){var r=[],n=e,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i,u=o.get("key");if(o.node.computed&&!u.isPure()){var c=t.generateUidBasedOnNode(u.node),l=Co(Qi(c),u.node);r.push(l),u.replaceWith(Qi(c))}}return r}function l(e,t,r){var n=e.get("properties"),a=n[n.length-1];ap(a.node);var o=pp(a.node);a.remove();var u,l=c(e.get("properties"),e.scope),p=function(e){var t=[],r=!0,n=e.node.properties,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;S(o.key)&&!o.computed?t.push(to(o.key.name)):fe(o.key)?t.push(pp(o.key)):ft(o.key)?t.push(to(String(o.key.value))):(t.push(pp(o.key)),r=!1)}return{keys:t,allLiteral:r}}(e),d=p.keys,f=p.allLiteral;return 0===d.length?[l,o.argument,Li(i(t),[lo([]),pp(r)])]:(u=f?Fi(d):Li(oo(Fi(d),Qi("map")),[t.addHelper("toPropertyKey")]),[l,o.argument,Li(t.addHelper("objectWithoutProperties"+(s?"Loose":"")),[pp(r),u])])}function p(e,t){if(t.isAssignmentPattern())p(e,t.get("left"));else{if(t.isArrayPattern()&&o(t))for(var r=t.get("elements"),n=0;n<r.length;n++)p(e,r[n]);if(t.isObjectPattern()&&o(t)){var a=e.scope.generateUidIdentifier("ref"),s=Do("let",[Co(t.node,a)]);e.ensureBlock(),e.get("body").unshiftContainer("body",s),t.replaceWith(pp(a))}}}return{name:"proposal-object-rest-spread",inherits:TF,visitor:{Function:function(e){for(var t=e.get("params"),r=t.length-1;r>=0;r--)p(t[r].parentPath,t[r])},VariableDeclarator:function(e,t){if(e.get("id").isObjectPattern()){var r=e,n=e;u(e.get("id"),(function(e){if(e.parentPath.isObjectPattern()){if(n.node.id.properties.length>1&&!S(n.node.init)){var a=e.scope.generateUidIdentifierBasedOnNode(n.node.init,"ref");return n.insertBefore(Co(a,n.node.init)),void n.replaceWith(Co(n.node.id,pp(a)))}var i,o=n.node.init,u=[];e.findParent((function(e){if(e.isObjectProperty())u.unshift(e);else if(e.isVariableDeclarator())return i=e.parentPath.node.kind,!0}));var p=c(u,e.scope);u.forEach((function(e){var t=e.node;o=oo(o,pp(t.key),t.computed)}));var d=e.findParent((function(e){return e.isObjectPattern()})),f=l(d,t,o),h=f[0],m=f[1],y=f[2];s&&function(e){var t=e.getOuterBindingIdentifierPaths();Object.keys(t).forEach((function(r){var n=t[r].parentPath;e.scope.getBinding(r).references>jF||!n.isObjectProperty()||n.remove()}))}(d),np(m),r.insertBefore(h),r.insertBefore(p),r.insertAfter(Co(m,y)),r=r.getSibling(r.key+1),e.scope.registerBinding(i,r),0===d.node.properties.length&&d.findParent((function(e){return e.isObjectProperty()||e.isVariableDeclarator()})).remove()}}))}},ExportNamedDeclaration:function(e){var t=e.get("declaration");if(t.isVariableDeclaration()&&t.get("declarations").some((function(e){return o(e.get("id"))}))){for(var r=[],n=0,a=Object.keys(e.getOuterBindingIdentifiers(e));n<a.length;n++){var s=a[n];r.push(Mo(Qi(s),Qi(s)))}e.replaceWith(t.node),e.insertAfter(Ro(null,r))}},CatchClause:function(e){var t=e.get("param");p(t.parentPath,t)},AssignmentExpression:function(e,t){var r=e.get("left");if(r.isObjectPattern()&&o(r)){var n=[],a=e.scope.generateUidBasedOnNode(e.node.right,"ref");n.push(Do("var",[Co(Qi(a),e.node.right)]));var s=l(r,t,Qi(a)),i=s[0],u=s[1],c=s[2];i.length>0&&n.push(Do("var",i));var p=pp(e.node);p.right=Qi(a),n.push(Ki(p)),n.push(Vd(_i("=",u,c))),n.push(Ki(Qi(a))),e.replaceWithMultiple(n)}},ForXStatement:function(e){var t=e.node,r=e.scope,n=e.get("left"),a=t.left;if(function(e){var t=!1;return u(e,(function(e){e.parentPath.isObjectPattern()&&(t=!0,e.stop())})),t}(n))if(q(a)){var s=a.declarations[0].id,i=r.generateUidIdentifier("ref");t.left=Do(a.kind,[Co(i,null)]),e.ensureBlock(),t.body.body.unshift(Do(t.left.kind,[Co(s,pp(i))]))}else{var o=r.generateUidIdentifier("ref");t.left=Do("var",[Co(o)]),e.ensureBlock(),0===t.body.body.length&&e.isCompletionRecord()&&t.body.body.unshift(Ki(r.buildUndefinedNode())),t.body.body.unshift(Ki(_i("=",a,pp(o))))}},ArrayPattern:function(e){var t=[];if(u(e,(function(e){if(e.parentPath.isObjectPattern()){var r=e.parentPath,n=e.scope.generateUidIdentifier("ref");t.push(Co(r.node,n)),r.replaceWith(pp(n)),e.skip()}})),t.length>0){var r=e.getStatementParent();r.insertAfter(Do(r.node.kind||"var",t))}},ObjectExpression:function(e,t){if(function(e){var t=e.properties,r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}if(le(a))return!0}return!1}(e.node)){for(var r,n=[],a=[],o=0,u=e.node.properties;o<u.length;o++){var c=u[o];le(c)?(l(),n.push(c.argument)):a.push(c)}if(a.length&&l(),s)r=i(t);else try{r=t.addHelper("objectSpread2")}catch(e){this.file.declarations.objectSpread2=null,r=t.addHelper("objectSpread")}e.replaceWith(Li(r,n))}function l(){n.push(lo(a)),a=[]}}}}})),kF=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-optional-catch-binding",manipulateOptions:function(e,t){t.plugins.push("optionalCatchBinding")}}}));t.default=r}))),FF=sP((function(e){return e.assertVersion(7),{name:"proposal-optional-catch-binding",inherits:kF,visitor:{CatchClause:function(e){if(!e.node.param){var t=e.scope.generateUidIdentifier("unused");e.get("param").replaceWith(t)}}}}})),_F=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-optional-chaining",manipulateOptions:function(e,t){t.plugins.push("optionalChaining")}}}));t.default=r}))),IF=sP((function(e,t){e.assertVersion(7);var r=t.loose,n=void 0!==r&&r;return{name:"proposal-optional-chaining",inherits:_F,visitor:{"OptionalCallExpression|OptionalMemberExpression":function(e){for(var t=e.parentPath,r=e.scope,a=!1,s=[],i=e;i.isOptionalMemberExpression()||i.isOptionalCallExpression();){var o=i.node;o.optional&&s.push(o),i.isOptionalMemberExpression()?(i.node.type="MemberExpression",i=i.get("object")):i.isOptionalCallExpression()&&(i.node.type="CallExpression",i=i.get("callee"))}var u=e;t.isUnaryExpression({operator:"delete"})&&(u=t,a=!0);for(var c=s.length-1;c>=0;c--){var l=s[c],p=f(l),d=p?"callee":"object",h=l[d],m=void 0,y=void 0;if(n&&p?y=m=h:(m=r.maybeGenerateMemoised(h))?(y=_i("=",pp(m),h),l[d]=m):y=m=h,p&&_(h))if(n)l.callee=h;else{var g=h.object,v=r.maybeGenerateMemoised(g);v?h.object=_i("=",v,g):v=pe(g)?xo():g,l.arguments.unshift(pp(v)),l.callee=oo(l.callee,Qi("call"))}u.replaceWith(Gi(n?Ii("==",pp(y),no()):io("||",Ii("===",pp(y),no()),Ii("===",pp(m),r.buildUndefinedNode())),a?ao(!0):r.buildUndefinedNode(),u.node)),u=u.get("alternate")}}}}})),BF=function(e){var t,r=e.assign,n=e.call,a=e.path,s=r.left,i=r.right,o=n.callee,u=J(o)&&rt(o.body)&&!o.async&&!o.generator;if(u){var c=o.params;1===c.length&&S(c[0])?t=c[0]:c.length>0&&(u=!1)}else if(S(o,{name:"eval"})){var l=yo([ro(0),o]);return n.callee=l,a.scope.push({id:s}),yo([r,n])}return u&&!t?yo([i,o.body]):(a.scope.push({id:s}),t?(a.get("right").scope.rename(t.name,s.name),yo([r,o.body])):yo([r,n]))},OF={PipelinePrimaryTopicReference:function(e){e.replaceWith(this.topicId)},PipelineTopicExpression:function(e){e.skip()}},NF={minimal:{BinaryExpression:function(e){var t=e.scope,r=e.node,n=r.operator,a=r.left,s=r.right;if("|>"===n){var i=t.generateUidIdentifierBasedOnNode(a),o=Li(s,[pp(i)]);e.replaceWith(BF({assign:_i("=",pp(i),a),call:o,path:e}))}}},smart:{BinaryExpression:function(e){var t=e.scope,r=e.node,n=r.operator,a=r.left,s=r.right;if("|>"===n){var i,o=t.generateUidIdentifierBasedOnNode(a);if(t.push({id:o}),He(s))e.get("right").traverse(OF,{topicId:o}),i=s.expression;else{var u=s.callee;S(u,{name:"eval"})&&(u=yo([ro(0),u])),i=Li(u,[pp(o)])}e.replaceWith(yo([_i("=",pp(o),a),i]))}}},fsharp:{BinaryExpression:function(e){var t=e.scope,r=e.node,n=r.operator,a=r.left,s=r.right;if("|>"===n){var i=t.generateUidIdentifierBasedOnNode(a),o="AwaitExpression"===s.type?Cc(pp(i)):Li(s,[pp(i)]),u=BF({assign:_i("=",pp(i),a),call:o,path:e});e.replaceWith(u)}}}},RF=sP((function(e,t){return e.assertVersion(7),{name:"proposal-pipeline-operator",inherits:DP,visitor:NF[t.proposal]}})),MF=sP((function(e,t){return e.assertVersion(7),Jk({name:"proposal-private-methods",feature:Vk.privateMethods,loose:t.loose,manipulateOptions:function(e,t){t.plugins.push("classPrivateMethods")}})})),LF=sP((function(e){return e.assertVersion(7),{name:"syntax-throw-expressions",manipulateOptions:function(e,t){t.plugins.push("throwExpressions")}}})),UF=sP((function(e){return e.assertVersion(7),{name:"proposal-throw-expressions",inherits:LF,visitor:{UnaryExpression:function(e){var t=e.node,r=t.operator,n=t.argument;if("throw"===r){var a=$i(null,[Qi("e")],Ri([Eo(Qi("e"))]));e.replaceWith(Li(a,[n]))}}}}})),GF=Vt((function(e,t){(function(){var r={function:!0,object:!0},n=r[typeof window]&&window||this,a=r.object&&t&&!t.nodeType&&t,s=r.object&&e&&!e.nodeType,i=a&&s&&"object"==typeof Ut&&Ut;!i||i.global!==i&&i.window!==i&&i.self!==i||(n=i);var o=Object.prototype.hasOwnProperty;function u(){var e=Number(arguments[0]);if(!isFinite(e)||e<0||e>1114111||Math.floor(e)!=e)throw RangeError("Invalid code point: "+e);if(e<=65535)return String.fromCharCode(e);var t=55296+((e-=65536)>>10),r=e%1024+56320;return String.fromCharCode(t,r)}var c={};function l(e,t){if(-1==t.indexOf("|")){if(e==t)return;throw Error("Invalid node type: "+e+"; expected type: "+t)}if(!(t=o.call(c,t)?c[t]:c[t]=RegExp("^(?:"+t+")$")).test(e))throw Error("Invalid node type: "+e+"; expected types: "+t)}function p(e){var t=e.type;if(o.call(m,t))return m[t](e);throw Error("Invalid node type: "+t)}function d(e){return l(e.type,"anchor|characterClassEscape|characterClassRange|dot|value"),p(e)}function f(e){return l(e.type,"identifier"),e.value}function h(e){return l(e.type,"anchor|characterClass|characterClassEscape|empty|group|quantifier|reference|unicodePropertyEscape|value|dot"),p(e)}var m={alternative:function(e){l(e.type,"alternative");for(var t=e.body,r=-1,n=t.length,a="";++r<n;)a+=h(t[r]);return a},anchor:function(e){switch(l(e.type,"anchor"),e.kind){case"start":return"^";case"end":return"$";case"boundary":return"\\b";case"not-boundary":return"\\B";default:throw Error("Invalid assertion")}},characterClass:function(e){l(e.type,"characterClass");var t=e.body,r=-1,n=t.length,a="";for(e.negative&&(a+="^");++r<n;)a+=d(t[r]);return"["+a+"]"},characterClassEscape:function(e){return l(e.type,"characterClassEscape"),"\\"+e.value},characterClassRange:function(e){l(e.type,"characterClassRange");var t=e.min,r=e.max;if("characterClassRange"==t.type||"characterClassRange"==r.type)throw Error("Invalid character class range");return d(t)+"-"+d(r)},unicodePropertyEscape:function(e){return l(e.type,"unicodePropertyEscape"),"\\"+(e.negative?"P":"p")+"{"+e.value+"}"},disjunction:function(e){l(e.type,"disjunction");for(var t=e.body,r=-1,n=t.length,a="";++r<n;)0!=r&&(a+="|"),a+=p(t[r]);return a},dot:function(e){return l(e.type,"dot"),"."},group:function(e){l(e.type,"group");var t="";switch(e.behavior){case"normal":e.name&&(t+="?<"+f(e.name)+">");break;case"ignore":t+="?:";break;case"lookahead":t+="?=";break;case"negativeLookahead":t+="?!";break;case"lookbehind":t+="?<=";break;case"negativeLookbehind":t+="?<!";break;default:throw Error("Invalid behaviour: "+e.behaviour)}for(var r=e.body,n=-1,a=r.length;++n<a;)t+=p(r[n]);return"("+t+")"},quantifier:function(e){l(e.type,"quantifier");var t="",r=e.min,n=e.max;return t=null==n?0==r?"*":1==r?"+":"{"+r+",}":r==n?"{"+r+"}":0==r&&1==n?"?":"{"+r+","+n+"}",e.greedy||(t+="?"),function(e){return l(e.type,"anchor|characterClass|characterClassEscape|dot|group|reference|value"),p(e)}(e.body[0])+t},reference:function(e){if(l(e.type,"reference"),e.matchIndex)return"\\"+e.matchIndex;if(e.name)return"\\k<"+f(e.name)+">";throw new Error("Unknown reference type")},value:function(e){l(e.type,"value");var t=e.kind,r=e.codePoint;if("number"!=typeof r)throw new Error("Invalid code point: "+r);switch(t){case"controlLetter":return"\\c"+u(r+64);case"hexadecimalEscape":return"\\x"+("00"+r.toString(16).toUpperCase()).slice(-2);case"identifier":return"\\"+u(r);case"null":return"\\"+r;case"octal":return"\\"+r.toString(8);case"singleEscape":switch(r){case 8:return"\\b";case 9:return"\\t";case 10:return"\\n";case 11:return"\\v";case 12:return"\\f";case 13:return"\\r";default:throw Error("Invalid code point: "+r)}case"symbol":return u(r);case"unicodeEscape":return"\\u"+("0000"+r.toString(16).toUpperCase()).slice(-4);case"unicodeCodePointEscape":return"\\u{"+r.toString(16).toUpperCase()+"}";default:throw Error("Unsupported node kind: "+t)}}},y={generate:p};a&&s?a.generate=p:n.regjsgen=y}).call(Ut)})),VF=Vt((function(e){var t,r,n,a;n=String.fromCodePoint||(t=String.fromCharCode,r=Math.floor,function(){var e,n,a=16384,s=[],i=-1,o=arguments.length;if(!o)return"";for(var u="";++i<o;){var c=Number(arguments[i]);if(!isFinite(c)||c<0||c>1114111||r(c)!=c)throw RangeError("Invalid code point: "+c);c<=65535?s.push(c):(e=55296+((c-=65536)>>10),n=c%1024+56320,s.push(e,n)),(i+1==o||s.length>a)&&(u+=t.apply(null,s),s.length=0)}return u}),a={parse:function(e,t,r){function a(t){return t.raw=e.substring(t.range[0],t.range[1]),t}function s(e,t){return a({type:"anchor",kind:e,range:[L-t,L]})}function i(e,t,r,n){return a({type:"value",kind:e,codePoint:t,range:[r,n]})}function o(e,t,r,n){return n=n||0,i(e,t,L-(r.length+n),L)}function u(e){var t,r=e[0],n=r.charCodeAt(0);return M&&1===r.length&&n>=55296&&n<=56319&&(t=h().charCodeAt(0))>=56320&&t<=57343?i("symbol",1024*(n-55296)+t-56320+65536,++L-2,L):i("symbol",n,L-1,L)}function c(e,t,r,n){return a({type:"characterClass",body:e,negative:t,range:[r,n]})}function l(e,t,r,n){return e.codePoint>t.codePoint&&B("invalid range in character class",e.raw+"-"+t.raw,r,n),a({type:"characterClassRange",min:e,max:t,range:[r,n]})}function p(t){t=t||1;var r=e.substring(L,L+t);return L+=t||1,r}function d(e){f(e)||B("character",e)}function f(t){if(e.indexOf(t,L)===L)return p(t.length)}function h(){return e[L]}function m(t){return e.indexOf(t,L)===L}function y(t){return e[L+1]===t}function g(t){var r=e.substring(L).match(t);return r&&(r.range=[],r.range[0]=L,p(r[0].length),r.range[1]=L),r}function v(){var e=[],t=L;for(e.push(b());f("|");)e.push(b());return 1===e.length?e[0]:function(e,t,r){return a({type:"disjunction",body:e,range:[t,r]})}(e,t,L)}function b(){for(var e,t=[],r=L;e=x();)t.push(e);return 1===t.length?t[0]:function(e,t,r){return a({type:"alternative",body:e,range:[t,r]})}(t,r,L)}function x(){if(L>=e.length||m("|")||m(")"))return null;var t=f("^")?s("start",1):f("$")?s("end",1):f("\\b")?s("boundary",2):f("\\B")?s("not-boundary",2):E("(?=","lookahead","(?!","negativeLookahead");if(t)return t;var n=function(){var e;if(e=g(/^[^^$\\.*+?()[\]{}|]/))return u(e);if(!M&&(e=g(/^(?:]|})/)))return u(e);if(f("."))return a({type:"dot",range:[L-1,L]});if(f("\\")){if(!(e=D())){if(!M&&"c"==h())return i("symbol",92,L-1,L);B("atomEscape")}return e}if(e=function(){var e,t=L;return(e=g(/^\[\^/))?(e=F(),d("]"),c(e,!0,t,L)):f("[")?(e=F(),d("]"),c(e,!1,t,L)):null}())return e;if(r.lookbehind&&(e=E("(?<=","lookbehind","(?<!","negativeLookbehind")))return e;if(r.namedGroups&&f("(?<")){var t=j();d(">");var n=A("normal",t.range[0]-3);return n.name=t,n}return E("(?:","ignore","(","normal")}();return n||B("Expected atom"),n}function E(e,t,r,n){var a=null,s=L;if(f(e))a=t;else{if(!f(r))return!1;a=n}return A(a,s)}function A(e,t){var r=v();r||B("Expected disjunction"),d(")");var n=function(e,t,r,n){return a({type:"group",behavior:e,body:t,range:[r,n]})}(e,function(e){return"alternative"===e.type?e.body:[e]}(r),t,L);return"normal"==e&&R&&N++,n}function w(e){var t,r;if(M&&"unicodeEscape"==e.kind&&(t=e.codePoint)>=55296&&t<=56319&&m("\\")&&y("u")){var n=L;L++;var s=S();"unicodeEscape"==s.kind&&(r=s.codePoint)>=56320&&r<=57343?(e.range[1]=s.range[1],e.codePoint=1024*(t-55296)+r-56320+65536,e.type="value",e.kind="unicodeCodePointEscape",a(e)):L=n}return e}function S(){return D(!0)}function D(e){var t,n=L;if(t=function(){var e,t,r;if(e=g(/^(?!0)\d+/)){t=e[0];var n=parseInt(e[0],10);return n<=N?(r=e[0],a({type:"reference",matchIndex:parseInt(r,10),range:[L-1-r.length,L]})):(O.push(n),p(-e[0].length),(e=g(/^[0-7]{1,3}/))?o("octal",parseInt(e[0],8),e[0],1):function(e,t){return e.range[0]=t,a(e)}(e=u(g(/^[89]/)),e.range[0]-1))}return(e=g(/^[0-7]{1,3}/))?(t=e[0],/^0{1,3}$/.test(t)?o("null",0,"0",t.length+1):o("octal",parseInt(t,8),t,1)):!!(e=g(/^[dDsSwW]/))&&a({type:"characterClassEscape",value:e[0],range:[L-2,L]})}()||function(){if(r.namedGroups&&g(/^k<(?=.*?>)/)){var e=j();return d(">"),function(e){return a({type:"reference",name:e,range:[e.range[0]-3,L]})}(e)}}())return t;if(e){if(f("b"))return o("singleEscape",8,"\\b");if(f("B"))B("\\B not possible inside of CharacterClass","",n);else if(!M&&(t=g(/^c([0-9])/)))return o("controlLetter",t[1]+16,t[1],2);if(f("-")&&M)return o("singleEscape",45,"\\-")}return t=function(){var e,t,n,s=L;if(e=g(/^[fnrtv]/)){var i=0;switch(e[0]){case"t":i=9;break;case"n":i=10;break;case"v":i=11;break;case"f":i=12;break;case"r":i=13}return o("singleEscape",i,"\\"+e[0])}return(e=g(/^c([a-zA-Z])/))?o("controlLetter",e[1].charCodeAt(0)%32,e[1],2):(e=g(/^x([0-9a-fA-F]{2})/))?o("hexadecimalEscape",parseInt(e[1],16),e[1],2):(e=C())?((!e||e.codePoint>1114111)&&B("Invalid escape sequence",null,s,L),e):r.unicodePropertyEscape&&M&&(e=g(/^([pP])\{([^\}]+)\}/))?a({type:"unicodePropertyEscape",negative:"P"===e[1],value:e[2],range:[e.range[0]-1,e.range[1]],raw:e[0]}):(n=h(),M&&/[\^\$\.\*\+\?\(\)\\\[\]\{\}\|\/]/.test(n)||!M&&"c"!==n?"k"===n&&r.lookbehind?null:o("identifier",(t=p()).charCodeAt(0),t,1):null)}()}function C(){var e;return(e=g(/^u([0-9a-fA-F]{4})/))?w(o("unicodeEscape",parseInt(e[1],16),e[1],2)):M&&(e=g(/^u\{([0-9a-fA-F]+)\}/))?o("unicodeCodePointEscape",parseInt(e[1],16),e[1],4):void 0}function T(t){var r=h(),a=L;if("\\"===r){p();var s=C();return s&&t(s.codePoint)||B("Invalid escape sequence",null,a,L),n(s.codePoint)}var i=r.charCodeAt(0);if(i>=55296&&i<=56319){var o=(r+=e[L+1]).charCodeAt(1);o>=56320&&o<=57343&&(i=1024*(i-55296)+o-56320+65536)}if(t(i))return p(),i>65535&&p(),r}function j(){var e,t=L,r=T(P);for(r||B("Invalid identifier");e=T(k);)r+=e;return a({type:"identifier",value:r,range:[t,L]})}function P(e){return 36===e||95===e||e>=65&&e<=90||e>=97&&e<=122||e>=128&&/[\$A-Z_a-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2118-\u211D\u2124\u2126\u2128\u212A-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309B-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEF\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7B9\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDF00-\uDF1C\uDF27\uDF30-\uDF45]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF1A]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDE9D\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFF1]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/.test(n(e))}function k(e){return P(e)||e>=48&&e<=57||e>=128&&/[0-9_\xB7\u0300-\u036F\u0387\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u0669\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07C0-\u07C9\u07EB-\u07F3\u07FD\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D3-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0966-\u096F\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09E6-\u09EF\u09FE\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A66-\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AE6-\u0AEF\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B66-\u0B6F\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0BE6-\u0BEF\u0C00-\u0C04\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0CE6-\u0CEF\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D66-\u0D6F\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0E50-\u0E59\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0ED0-\u0ED9\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1040-\u1049\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F-\u109D\u135D-\u135F\u1369-\u1371\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u18A9\u1920-\u192B\u1930-\u193B\u1946-\u194F\u19D0-\u19DA\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AB0-\u1ABD\u1B00-\u1B04\u1B34-\u1B44\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BB0-\u1BB9\u1BE6-\u1BF3\u1C24-\u1C37\u1C40-\u1C49\u1C50-\u1C59\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u200C\u200D\u203F\u2040\u2054\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA620-\uA629\uA66F\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F1\uA8FF-\uA909\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9D0-\uA9D9\uA9E5\uA9F0-\uA9F9\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA50-\uAA59\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uABF0-\uABF9\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFF10-\uFF19\uFF3F]|\uD800[\uDDFD\uDEE0\uDF76-\uDF7A]|\uD801[\uDCA0-\uDCA9]|\uD802[\uDE01-\uDE03\uDE05\uDE06\uDE0C-\uDE0F\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD803[\uDD24-\uDD27\uDD30-\uDD39\uDF46-\uDF50]|\uD804[\uDC00-\uDC02\uDC38-\uDC46\uDC66-\uDC6F\uDC7F-\uDC82\uDCB0-\uDCBA\uDCF0-\uDCF9\uDD00-\uDD02\uDD27-\uDD34\uDD36-\uDD3F\uDD45\uDD46\uDD73\uDD80-\uDD82\uDDB3-\uDDC0\uDDC9-\uDDCC\uDDD0-\uDDD9\uDE2C-\uDE37\uDE3E\uDEDF-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF3B\uDF3C\uDF3E-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF57\uDF62\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC35-\uDC46\uDC50-\uDC59\uDC5E\uDCB0-\uDCC3\uDCD0-\uDCD9\uDDAF-\uDDB5\uDDB8-\uDDC0\uDDDC\uDDDD\uDE30-\uDE40\uDE50-\uDE59\uDEAB-\uDEB7\uDEC0-\uDEC9\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDC2C-\uDC3A\uDCE0-\uDCE9\uDE01-\uDE0A\uDE33-\uDE39\uDE3B-\uDE3E\uDE47\uDE51-\uDE5B\uDE8A-\uDE99]|\uD807[\uDC2F-\uDC36\uDC38-\uDC3F\uDC50-\uDC59\uDC92-\uDCA7\uDCA9-\uDCB6\uDD31-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD45\uDD47\uDD50-\uDD59\uDD8A-\uDD8E\uDD90\uDD91\uDD93-\uDD97\uDDA0-\uDDA9\uDEF3-\uDEF6]|\uD81A[\uDE60-\uDE69\uDEF0-\uDEF4\uDF30-\uDF36\uDF50-\uDF59]|\uD81B[\uDF51-\uDF7E\uDF8F-\uDF92]|\uD82F[\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD4A\uDD50-\uDD59]|\uDB40[\uDD00-\uDDEF]/.test(n(e))}function F(){var e,t;return m("]")?[]:((t=I())||B("classAtom"),(e=m("]")?[t]:_(t))||B("nonEmptyClassRanges"),e)}function _(e){var t,r,n;if(m("-")&&!y("]")){d("-"),(n=I())||B("classAtom"),r=L;var a=F();return a||B("classRanges"),t=e.range[0],"empty"===a.type?[l(e,n,t,r)]:[l(e,n,t,r)].concat(a)}return(n=function(){var e=I();return e||B("classAtom"),m("]")?e:_(e)}())||B("nonEmptyClassRangesNoDash"),[e].concat(n)}function I(){return f("-")?u("-"):(e=g(/^[^\\\]-]/))?u(e[0]):f("\\")?((e=S())||B("classEscape"),w(e)):void 0;var e}function B(t,r,n,a){n=null==n?L:n,a=null==a?n:a;var s=Math.max(0,n-10),i=Math.min(a+10,e.length),o=" "+e.substring(s,i),u=" "+new Array(n-s+1).join(" ")+"^";throw SyntaxError(t+" at position "+n+(r?": "+r:"")+"\n"+o+"\n"+u)}r||(r={});var O=[],N=0,R=!0,M=-1!==(t||"").indexOf("u"),L=0;""===(e=String(e))&&(e="(?:)");var U=v();U.range[1]!==e.length&&B("Could not parse entire input - got stuck","",U.range[1]);for(var G=0;G<O.length;G++)if(O[G]<=N)return L=0,R=!1,v();return U}},e.exports?e.exports=a:window.regjsparser=a})),WF=Vt((function(e,t){!function(r){var n=t,a=e&&e.exports==n&&e,s="object"==typeof Ut&&Ut;s.global!==s&&s.window!==s||(r=s);var i="A range’s `stop` value must be greater than or equal to the `start` value.",o="Invalid code point value. Code points range from U+000000 to U+10FFFF.",u=/\\x00([^0123456789]|$)/g,c={},l=c.hasOwnProperty,p=function(e,t){for(var r=-1,n=e.length;++r<n;)t(e[r],r)},d=c.toString,f=function(e){return"[object Array]"==d.call(e)},h=function(e){return"number"==typeof e||"[object Number]"==d.call(e)},m=function(e,t){var r=String(e);return r.length<t?("0000"+r).slice(-t):r},y=function(e){return Number(e).toString(16).toUpperCase()},g=[].slice,v=function(e,t){for(var r,n,a=0,s=e.length;a<s;){if(r=e[a],n=e[a+1],t>=r&&t<n)return t==r?n==r+1?(e.splice(a,2),e):(e[a]=t+1,e):t==n-1?(e[a+1]=t,e):(e.splice(a,2,r,t,t+1,n),e);a+=2}return e},b=function(e,t,r){if(r<t)throw Error(i);for(var n,a,s=0;s<e.length;){if(n=e[s],a=e[s+1]-1,n>r)return e;if(t<=n&&r>=a)e.splice(s,2);else{if(t>=n&&r<a)return t==n?(e[s]=r+1,e[s+1]=a+1,e):(e.splice(s,2,n,t,r+1,a+1),e);if(t>=n&&t<=a)e[s+1]=t;else if(r>=n&&r<=a)return e[s]=r+1,e;s+=2}}return e},x=function(e,t){var r,n,a=0,s=null,i=e.length;if(t<0||t>1114111)throw RangeError(o);for(;a<i;){if(r=e[a],n=e[a+1],t>=r&&t<n)return e;if(t==r-1)return e[a]=t,e;if(r>t)return e.splice(null!=s?s+2:0,0,t,t+1),e;if(t==n)return t+1==e[a+2]?(e.splice(a,4,r,e[a+3]),e):(e[a+1]=t+1,e);s=a,a+=2}return e.push(t,t+1),e},E=function(e,t){for(var r,n,a=0,s=e.slice(),i=t.length;a<i;)s=(r=t[a])==(n=t[a+1]-1)?x(s,r):w(s,r,n),a+=2;return s},A=function(e,t){for(var r,n,a=0,s=e.slice(),i=t.length;a<i;)s=(r=t[a])==(n=t[a+1]-1)?v(s,r):b(s,r,n),a+=2;return s},w=function(e,t,r){if(r<t)throw Error(i);if(t<0||t>1114111||r<0||r>1114111)throw RangeError(o);for(var n,a,s=0,u=!1,c=e.length;s<c;){if(n=e[s],a=e[s+1],u){if(n==r+1)return e.splice(s-1,2),e;if(n>r)return e;n>=t&&n<=r&&(a>t&&a-1<=r?(e.splice(s,2),s-=2):(e.splice(s-1,2),s-=2))}else{if(n==r+1)return e[s]=t,e;if(n>r)return e.splice(s,0,t,r+1),e;if(t>=n&&t<a&&r+1<=a)return e;t>=n&&t<a||a==t?(e[s+1]=r+1,u=!0):t<=n&&r+1>=a&&(e[s]=t,e[s+1]=r+1,u=!0)}s+=2}return u||e.push(t,r+1),e},S=function(e,t){var r=0,n=e.length,a=e[r],s=e[n-1];if(n>=2&&(t<a||t>s))return!1;for(;r<n;){if(a=e[r],s=e[r+1],t>=a&&t<s)return!0;r+=2}return!1},D=function(e){return!e.length},C=function(e){return 2==e.length&&e[0]+1==e[1]},T=function(e){for(var t,r,n=0,a=[],s=e.length;n<s;){for(t=e[n],r=e[n+1];t<r;)a.push(t),++t;n+=2}return a},j=Math.floor,P=function(e){return parseInt(j((e-65536)/1024)+55296,10)},k=function(e){return parseInt((e-65536)%1024+56320,10)},F=String.fromCharCode,_=function(e){return 9==e?"\\t":10==e?"\\n":12==e?"\\f":13==e?"\\r":45==e?"\\x2D":92==e?"\\\\":36==e||e>=40&&e<=43||46==e||47==e||63==e||e>=91&&e<=94||e>=123&&e<=125?"\\"+F(e):e>=32&&e<=126?F(e):e<=255?"\\x"+m(y(e),2):"\\u"+m(y(e),4)},I=function(e){return e<=65535?_(e):"\\u{"+e.toString(16).toUpperCase()+"}"},B=function(e){var t=e.length,r=e.charCodeAt(0);return r>=55296&&r<=56319&&t>1?1024*(r-55296)+e.charCodeAt(1)-56320+65536:r},O=function(e){var t,r,n="",a=0,s=e.length;if(C(e))return _(e[0]);for(;a<s;)n+=(t=e[a])==(r=e[a+1]-1)?_(t):t+1==r?_(t)+_(r):_(t)+"-"+_(r),a+=2;return"["+n+"]"},N=function(e){if(1==e.length)return e;for(var t=-1,r=-1;++t<e.length;){var n=e[t],a=n[1],s=a[0],i=a[1];for(r=t;++r<e.length;){var o=e[r],u=o[1],c=u[0],l=u[1];s==c&&i==l&&(C(o[0])?n[0]=x(n[0],o[0][0]):n[0]=w(n[0],o[0][0],o[0][1]-1),e.splice(r,1),--r)}}return e},R=function(e){if(!e.length)return[];for(var t,r,n,a,s,i,o=0,u=[],c=e.length;o<c;){t=e[o],r=e[o+1]-1,n=P(t),a=k(t),s=P(r);var l=57343==(i=k(r)),p=!1;n==s||56320==a&&l?(u.push([[n,s+1],[a,i+1]]),p=!0):u.push([[n,n+1],[a,57344]]),!p&&n+1<s&&(l?(u.push([[n+1,s+1],[56320,i+1]]),p=!0):u.push([[n+1,s],[56320,57344]])),p||u.push([[s,s+1],[56320,i+1]]),o+=2}return function(e){for(var t,r,n,a,s,i,o=[],u=[],c=!1,l=-1,p=e.length;++l<p;)if(t=e[l],r=e[l+1]){for(n=t[0],a=t[1],s=r[0],i=r[1],u=a;s&&n[0]==s[0]&&n[1]==s[1];)u=C(i)?x(u,i[0]):w(u,i[0],i[1]-1),n=(t=e[++l])[0],a=t[1],s=(r=e[l+1])&&r[0],i=r&&r[1],c=!0;o.push([n,c?u:a]),c=!1}else o.push(t);return N(o)}(u)},M=function(e,t,r){if(r)return function(e){var t,r,n="",a=0,s=e.length;if(C(e))return I(e[0]);for(;a<s;)n+=(t=e[a])==(r=e[a+1]-1)?I(t):t+1==r?I(t)+I(r):I(t)+"-"+I(r),a+=2;return"["+n+"]"}(e);var n=[],a=function(e){for(var t,r,n=[],a=[],s=[],i=[],o=0,u=e.length;o<u;)t=e[o],r=e[o+1]-1,t<55296?(r<55296&&s.push(t,r+1),r>=55296&&r<=56319&&(s.push(t,55296),n.push(55296,r+1)),r>=56320&&r<=57343&&(s.push(t,55296),n.push(55296,56320),a.push(56320,r+1)),r>57343&&(s.push(t,55296),n.push(55296,56320),a.push(56320,57344),r<=65535?s.push(57344,r+1):(s.push(57344,65536),i.push(65536,r+1)))):t>=55296&&t<=56319?(r>=55296&&r<=56319&&n.push(t,r+1),r>=56320&&r<=57343&&(n.push(t,56320),a.push(56320,r+1)),r>57343&&(n.push(t,56320),a.push(56320,57344),r<=65535?s.push(57344,r+1):(s.push(57344,65536),i.push(65536,r+1)))):t>=56320&&t<=57343?(r>=56320&&r<=57343&&a.push(t,r+1),r>57343&&(a.push(t,57344),r<=65535?s.push(57344,r+1):(s.push(57344,65536),i.push(65536,r+1)))):t>57343&&t<=65535?r<=65535?s.push(t,r+1):(s.push(t,65536),i.push(65536,r+1)):i.push(t,r+1),o+=2;return{loneHighSurrogates:n,loneLowSurrogates:a,bmp:s,astral:i}}(e),s=a.loneHighSurrogates,i=a.loneLowSurrogates,o=a.bmp,u=a.astral,c=!D(s),l=!D(i),d=R(u);return t&&(o=E(o,s),c=!1,o=E(o,i),l=!1),D(o)||n.push(O(o)),d.length&&n.push(function(e){var t=[];return p(e,(function(e){var r=e[0],n=e[1];t.push(O(r)+O(n))})),t.join("|")}(d)),c&&n.push(O(s)+"(?![\\uDC00-\\uDFFF])"),l&&n.push("(?:[^\\uD800-\\uDBFF]|^)"+O(i)),n.join("|")},L=function e(t){return arguments.length>1&&(t=g.call(arguments)),this instanceof e?(this.data=[],t?this.add(t):this):(new e).add(t)};L.version="1.3.3";var U=L.prototype;!function(e,t){var r;for(r in t)l.call(t,r)&&(e[r]=t[r])}(U,{add:function(e){var t=this;return null==e?t:e instanceof L?(t.data=E(t.data,e.data),t):(arguments.length>1&&(e=g.call(arguments)),f(e)?(p(e,(function(e){t.add(e)})),t):(t.data=x(t.data,h(e)?e:B(e)),t))},remove:function(e){var t=this;return null==e?t:e instanceof L?(t.data=A(t.data,e.data),t):(arguments.length>1&&(e=g.call(arguments)),f(e)?(p(e,(function(e){t.remove(e)})),t):(t.data=v(t.data,h(e)?e:B(e)),t))},addRange:function(e,t){return this.data=w(this.data,h(e)?e:B(e),h(t)?t:B(t)),this},removeRange:function(e,t){var r=h(e)?e:B(e),n=h(t)?t:B(t);return this.data=b(this.data,r,n),this},intersection:function(e){var t=e instanceof L?T(e.data):e;return this.data=function(e,t){for(var r,n=0,a=t.length,s=[];n<a;)r=t[n],S(e,r)&&s.push(r),++n;return function(e){for(var t,r=-1,n=e.length,a=n-1,s=[],i=!0,o=0;++r<n;)if(t=e[r],i)s.push(t),o=t,i=!1;else if(t==o+1){if(r!=a){o=t;continue}i=!0,s.push(t+1)}else s.push(o+1,t),o=t;return i||s.push(t+1),s}(s)}(this.data,t),this},contains:function(e){return S(this.data,h(e)?e:B(e))},clone:function(){var e=new L;return e.data=this.data.slice(0),e},toString:function(e){var t=M(this.data,!!e&&e.bmpOnly,!!e&&e.hasUnicodeFlag);return t?t.replace(u,"\\0$1"):"[]"},toRegExp:function(e){var t=this.toString(e&&-1!=e.indexOf("u")?{hasUnicodeFlag:!0}:null);return RegExp(t,e||"")},valueOf:function(){return T(this.data)}}),U.toArray=U.valueOf,n&&!n.nodeType?a?a.exports=L:n.regenerate=L:r.regenerate=L}(Ut)})),HF=new Set(["General_Category","Script","Script_Extensions","Alphabetic","Any","ASCII","ASCII_Hex_Digit","Assigned","Bidi_Control","Bidi_Mirrored","Case_Ignorable","Cased","Changes_When_Casefolded","Changes_When_Casemapped","Changes_When_Lowercased","Changes_When_NFKC_Casefolded","Changes_When_Titlecased","Changes_When_Uppercased","Dash","Default_Ignorable_Code_Point","Deprecated","Diacritic","Emoji","Emoji_Component","Emoji_Modifier","Emoji_Modifier_Base","Emoji_Presentation","Extended_Pictographic","Extender","Grapheme_Base","Grapheme_Extend","Hex_Digit","ID_Continue","ID_Start","Ideographic","IDS_Binary_Operator","IDS_Trinary_Operator","Join_Control","Logical_Order_Exception","Lowercase","Math","Noncharacter_Code_Point","Pattern_Syntax","Pattern_White_Space","Quotation_Mark","Radical","Regional_Indicator","Sentence_Terminal","Soft_Dotted","Terminal_Punctuation","Unified_Ideograph","Uppercase","Variation_Selector","White_Space","XID_Continue","XID_Start"]),qF=new Map([["scx","Script_Extensions"],["sc","Script"],["gc","General_Category"],["AHex","ASCII_Hex_Digit"],["Alpha","Alphabetic"],["Bidi_C","Bidi_Control"],["Bidi_M","Bidi_Mirrored"],["Cased","Cased"],["CI","Case_Ignorable"],["CWCF","Changes_When_Casefolded"],["CWCM","Changes_When_Casemapped"],["CWKCF","Changes_When_NFKC_Casefolded"],["CWL","Changes_When_Lowercased"],["CWT","Changes_When_Titlecased"],["CWU","Changes_When_Uppercased"],["Dash","Dash"],["Dep","Deprecated"],["DI","Default_Ignorable_Code_Point"],["Dia","Diacritic"],["EBase","Emoji_Modifier_Base"],["EComp","Emoji_Component"],["EMod","Emoji_Modifier"],["Emoji","Emoji"],["EPres","Emoji_Presentation"],["Ext","Extender"],["ExtPict","Extended_Pictographic"],["Gr_Base","Grapheme_Base"],["Gr_Ext","Grapheme_Extend"],["Hex","Hex_Digit"],["IDC","ID_Continue"],["Ideo","Ideographic"],["IDS","ID_Start"],["IDSB","IDS_Binary_Operator"],["IDST","IDS_Trinary_Operator"],["Join_C","Join_Control"],["LOE","Logical_Order_Exception"],["Lower","Lowercase"],["Math","Math"],["NChar","Noncharacter_Code_Point"],["Pat_Syn","Pattern_Syntax"],["Pat_WS","Pattern_White_Space"],["QMark","Quotation_Mark"],["Radical","Radical"],["RI","Regional_Indicator"],["SD","Soft_Dotted"],["STerm","Sentence_Terminal"],["Term","Terminal_Punctuation"],["UIdeo","Unified_Ideograph"],["Upper","Uppercase"],["VS","Variation_Selector"],["WSpace","White_Space"],["space","White_Space"],["XIDC","XID_Continue"],["XIDS","XID_Start"]]),KF=function(e){if(HF.has(e))return e;if(qF.has(e))return qF.get(e);throw new Error("Unknown property: "+e)},zF=new Map([["General_Category",new Map([["C","Other"],["Cc","Control"],["cntrl","Control"],["Cf","Format"],["Cn","Unassigned"],["Co","Private_Use"],["Cs","Surrogate"],["L","Letter"],["LC","Cased_Letter"],["Ll","Lowercase_Letter"],["Lm","Modifier_Letter"],["Lo","Other_Letter"],["Lt","Titlecase_Letter"],["Lu","Uppercase_Letter"],["M","Mark"],["Combining_Mark","Mark"],["Mc","Spacing_Mark"],["Me","Enclosing_Mark"],["Mn","Nonspacing_Mark"],["N","Number"],["Nd","Decimal_Number"],["digit","Decimal_Number"],["Nl","Letter_Number"],["No","Other_Number"],["P","Punctuation"],["punct","Punctuation"],["Pc","Connector_Punctuation"],["Pd","Dash_Punctuation"],["Pe","Close_Punctuation"],["Pf","Final_Punctuation"],["Pi","Initial_Punctuation"],["Po","Other_Punctuation"],["Ps","Open_Punctuation"],["S","Symbol"],["Sc","Currency_Symbol"],["Sk","Modifier_Symbol"],["Sm","Math_Symbol"],["So","Other_Symbol"],["Z","Separator"],["Zl","Line_Separator"],["Zp","Paragraph_Separator"],["Zs","Space_Separator"],["Other","Other"],["Control","Control"],["Format","Format"],["Unassigned","Unassigned"],["Private_Use","Private_Use"],["Surrogate","Surrogate"],["Letter","Letter"],["Cased_Letter","Cased_Letter"],["Lowercase_Letter","Lowercase_Letter"],["Modifier_Letter","Modifier_Letter"],["Other_Letter","Other_Letter"],["Titlecase_Letter","Titlecase_Letter"],["Uppercase_Letter","Uppercase_Letter"],["Mark","Mark"],["Spacing_Mark","Spacing_Mark"],["Enclosing_Mark","Enclosing_Mark"],["Nonspacing_Mark","Nonspacing_Mark"],["Number","Number"],["Decimal_Number","Decimal_Number"],["Letter_Number","Letter_Number"],["Other_Number","Other_Number"],["Punctuation","Punctuation"],["Connector_Punctuation","Connector_Punctuation"],["Dash_Punctuation","Dash_Punctuation"],["Close_Punctuation","Close_Punctuation"],["Final_Punctuation","Final_Punctuation"],["Initial_Punctuation","Initial_Punctuation"],["Other_Punctuation","Other_Punctuation"],["Open_Punctuation","Open_Punctuation"],["Symbol","Symbol"],["Currency_Symbol","Currency_Symbol"],["Modifier_Symbol","Modifier_Symbol"],["Math_Symbol","Math_Symbol"],["Other_Symbol","Other_Symbol"],["Separator","Separator"],["Line_Separator","Line_Separator"],["Paragraph_Separator","Paragraph_Separator"],["Space_Separator","Space_Separator"]])],["Script",new Map([["Adlm","Adlam"],["Aghb","Caucasian_Albanian"],["Ahom","Ahom"],["Arab","Arabic"],["Armi","Imperial_Aramaic"],["Armn","Armenian"],["Avst","Avestan"],["Bali","Balinese"],["Bamu","Bamum"],["Bass","Bassa_Vah"],["Batk","Batak"],["Beng","Bengali"],["Bhks","Bhaiksuki"],["Bopo","Bopomofo"],["Brah","Brahmi"],["Brai","Braille"],["Bugi","Buginese"],["Buhd","Buhid"],["Cakm","Chakma"],["Cans","Canadian_Aboriginal"],["Cari","Carian"],["Cham","Cham"],["Cher","Cherokee"],["Chrs","Chorasmian"],["Copt","Coptic"],["Qaac","Coptic"],["Cprt","Cypriot"],["Cyrl","Cyrillic"],["Deva","Devanagari"],["Diak","Dives_Akuru"],["Dogr","Dogra"],["Dsrt","Deseret"],["Dupl","Duployan"],["Egyp","Egyptian_Hieroglyphs"],["Elba","Elbasan"],["Elym","Elymaic"],["Ethi","Ethiopic"],["Geor","Georgian"],["Glag","Glagolitic"],["Gong","Gunjala_Gondi"],["Gonm","Masaram_Gondi"],["Goth","Gothic"],["Gran","Grantha"],["Grek","Greek"],["Gujr","Gujarati"],["Guru","Gurmukhi"],["Hang","Hangul"],["Hani","Han"],["Hano","Hanunoo"],["Hatr","Hatran"],["Hebr","Hebrew"],["Hira","Hiragana"],["Hluw","Anatolian_Hieroglyphs"],["Hmng","Pahawh_Hmong"],["Hmnp","Nyiakeng_Puachue_Hmong"],["Hrkt","Katakana_Or_Hiragana"],["Hung","Old_Hungarian"],["Ital","Old_Italic"],["Java","Javanese"],["Kali","Kayah_Li"],["Kana","Katakana"],["Khar","Kharoshthi"],["Khmr","Khmer"],["Khoj","Khojki"],["Kits","Khitan_Small_Script"],["Knda","Kannada"],["Kthi","Kaithi"],["Lana","Tai_Tham"],["Laoo","Lao"],["Latn","Latin"],["Lepc","Lepcha"],["Limb","Limbu"],["Lina","Linear_A"],["Linb","Linear_B"],["Lisu","Lisu"],["Lyci","Lycian"],["Lydi","Lydian"],["Mahj","Mahajani"],["Maka","Makasar"],["Mand","Mandaic"],["Mani","Manichaean"],["Marc","Marchen"],["Medf","Medefaidrin"],["Mend","Mende_Kikakui"],["Merc","Meroitic_Cursive"],["Mero","Meroitic_Hieroglyphs"],["Mlym","Malayalam"],["Modi","Modi"],["Mong","Mongolian"],["Mroo","Mro"],["Mtei","Meetei_Mayek"],["Mult","Multani"],["Mymr","Myanmar"],["Nand","Nandinagari"],["Narb","Old_North_Arabian"],["Nbat","Nabataean"],["Newa","Newa"],["Nkoo","Nko"],["Nshu","Nushu"],["Ogam","Ogham"],["Olck","Ol_Chiki"],["Orkh","Old_Turkic"],["Orya","Oriya"],["Osge","Osage"],["Osma","Osmanya"],["Palm","Palmyrene"],["Pauc","Pau_Cin_Hau"],["Perm","Old_Permic"],["Phag","Phags_Pa"],["Phli","Inscriptional_Pahlavi"],["Phlp","Psalter_Pahlavi"],["Phnx","Phoenician"],["Plrd","Miao"],["Prti","Inscriptional_Parthian"],["Rjng","Rejang"],["Rohg","Hanifi_Rohingya"],["Runr","Runic"],["Samr","Samaritan"],["Sarb","Old_South_Arabian"],["Saur","Saurashtra"],["Sgnw","SignWriting"],["Shaw","Shavian"],["Shrd","Sharada"],["Sidd","Siddham"],["Sind","Khudawadi"],["Sinh","Sinhala"],["Sogd","Sogdian"],["Sogo","Old_Sogdian"],["Sora","Sora_Sompeng"],["Soyo","Soyombo"],["Sund","Sundanese"],["Sylo","Syloti_Nagri"],["Syrc","Syriac"],["Tagb","Tagbanwa"],["Takr","Takri"],["Tale","Tai_Le"],["Talu","New_Tai_Lue"],["Taml","Tamil"],["Tang","Tangut"],["Tavt","Tai_Viet"],["Telu","Telugu"],["Tfng","Tifinagh"],["Tglg","Tagalog"],["Thaa","Thaana"],["Thai","Thai"],["Tibt","Tibetan"],["Tirh","Tirhuta"],["Ugar","Ugaritic"],["Vaii","Vai"],["Wara","Warang_Citi"],["Wcho","Wancho"],["Xpeo","Old_Persian"],["Xsux","Cuneiform"],["Yezi","Yezidi"],["Yiii","Yi"],["Zanb","Zanabazar_Square"],["Zinh","Inherited"],["Qaai","Inherited"],["Zyyy","Common"],["Zzzz","Unknown"],["Adlam","Adlam"],["Caucasian_Albanian","Caucasian_Albanian"],["Arabic","Arabic"],["Imperial_Aramaic","Imperial_Aramaic"],["Armenian","Armenian"],["Avestan","Avestan"],["Balinese","Balinese"],["Bamum","Bamum"],["Bassa_Vah","Bassa_Vah"],["Batak","Batak"],["Bengali","Bengali"],["Bhaiksuki","Bhaiksuki"],["Bopomofo","Bopomofo"],["Brahmi","Brahmi"],["Braille","Braille"],["Buginese","Buginese"],["Buhid","Buhid"],["Chakma","Chakma"],["Canadian_Aboriginal","Canadian_Aboriginal"],["Carian","Carian"],["Cherokee","Cherokee"],["Chorasmian","Chorasmian"],["Coptic","Coptic"],["Cypriot","Cypriot"],["Cyrillic","Cyrillic"],["Devanagari","Devanagari"],["Dives_Akuru","Dives_Akuru"],["Dogra","Dogra"],["Deseret","Deseret"],["Duployan","Duployan"],["Egyptian_Hieroglyphs","Egyptian_Hieroglyphs"],["Elbasan","Elbasan"],["Elymaic","Elymaic"],["Ethiopic","Ethiopic"],["Georgian","Georgian"],["Glagolitic","Glagolitic"],["Gunjala_Gondi","Gunjala_Gondi"],["Masaram_Gondi","Masaram_Gondi"],["Gothic","Gothic"],["Grantha","Grantha"],["Greek","Greek"],["Gujarati","Gujarati"],["Gurmukhi","Gurmukhi"],["Hangul","Hangul"],["Han","Han"],["Hanunoo","Hanunoo"],["Hatran","Hatran"],["Hebrew","Hebrew"],["Hiragana","Hiragana"],["Anatolian_Hieroglyphs","Anatolian_Hieroglyphs"],["Pahawh_Hmong","Pahawh_Hmong"],["Nyiakeng_Puachue_Hmong","Nyiakeng_Puachue_Hmong"],["Katakana_Or_Hiragana","Katakana_Or_Hiragana"],["Old_Hungarian","Old_Hungarian"],["Old_Italic","Old_Italic"],["Javanese","Javanese"],["Kayah_Li","Kayah_Li"],["Katakana","Katakana"],["Kharoshthi","Kharoshthi"],["Khmer","Khmer"],["Khojki","Khojki"],["Khitan_Small_Script","Khitan_Small_Script"],["Kannada","Kannada"],["Kaithi","Kaithi"],["Tai_Tham","Tai_Tham"],["Lao","Lao"],["Latin","Latin"],["Lepcha","Lepcha"],["Limbu","Limbu"],["Linear_A","Linear_A"],["Linear_B","Linear_B"],["Lycian","Lycian"],["Lydian","Lydian"],["Mahajani","Mahajani"],["Makasar","Makasar"],["Mandaic","Mandaic"],["Manichaean","Manichaean"],["Marchen","Marchen"],["Medefaidrin","Medefaidrin"],["Mende_Kikakui","Mende_Kikakui"],["Meroitic_Cursive","Meroitic_Cursive"],["Meroitic_Hieroglyphs","Meroitic_Hieroglyphs"],["Malayalam","Malayalam"],["Mongolian","Mongolian"],["Mro","Mro"],["Meetei_Mayek","Meetei_Mayek"],["Multani","Multani"],["Myanmar","Myanmar"],["Nandinagari","Nandinagari"],["Old_North_Arabian","Old_North_Arabian"],["Nabataean","Nabataean"],["Nko","Nko"],["Nushu","Nushu"],["Ogham","Ogham"],["Ol_Chiki","Ol_Chiki"],["Old_Turkic","Old_Turkic"],["Oriya","Oriya"],["Osage","Osage"],["Osmanya","Osmanya"],["Palmyrene","Palmyrene"],["Pau_Cin_Hau","Pau_Cin_Hau"],["Old_Permic","Old_Permic"],["Phags_Pa","Phags_Pa"],["Inscriptional_Pahlavi","Inscriptional_Pahlavi"],["Psalter_Pahlavi","Psalter_Pahlavi"],["Phoenician","Phoenician"],["Miao","Miao"],["Inscriptional_Parthian","Inscriptional_Parthian"],["Rejang","Rejang"],["Hanifi_Rohingya","Hanifi_Rohingya"],["Runic","Runic"],["Samaritan","Samaritan"],["Old_South_Arabian","Old_South_Arabian"],["Saurashtra","Saurashtra"],["SignWriting","SignWriting"],["Shavian","Shavian"],["Sharada","Sharada"],["Siddham","Siddham"],["Khudawadi","Khudawadi"],["Sinhala","Sinhala"],["Sogdian","Sogdian"],["Old_Sogdian","Old_Sogdian"],["Sora_Sompeng","Sora_Sompeng"],["Soyombo","Soyombo"],["Sundanese","Sundanese"],["Syloti_Nagri","Syloti_Nagri"],["Syriac","Syriac"],["Tagbanwa","Tagbanwa"],["Takri","Takri"],["Tai_Le","Tai_Le"],["New_Tai_Lue","New_Tai_Lue"],["Tamil","Tamil"],["Tangut","Tangut"],["Tai_Viet","Tai_Viet"],["Telugu","Telugu"],["Tifinagh","Tifinagh"],["Tagalog","Tagalog"],["Thaana","Thaana"],["Tibetan","Tibetan"],["Tirhuta","Tirhuta"],["Ugaritic","Ugaritic"],["Vai","Vai"],["Warang_Citi","Warang_Citi"],["Wancho","Wancho"],["Old_Persian","Old_Persian"],["Cuneiform","Cuneiform"],["Yezidi","Yezidi"],["Yi","Yi"],["Zanabazar_Square","Zanabazar_Square"],["Inherited","Inherited"],["Common","Common"],["Unknown","Unknown"]])],["Script_Extensions",new Map([["Adlm","Adlam"],["Aghb","Caucasian_Albanian"],["Ahom","Ahom"],["Arab","Arabic"],["Armi","Imperial_Aramaic"],["Armn","Armenian"],["Avst","Avestan"],["Bali","Balinese"],["Bamu","Bamum"],["Bass","Bassa_Vah"],["Batk","Batak"],["Beng","Bengali"],["Bhks","Bhaiksuki"],["Bopo","Bopomofo"],["Brah","Brahmi"],["Brai","Braille"],["Bugi","Buginese"],["Buhd","Buhid"],["Cakm","Chakma"],["Cans","Canadian_Aboriginal"],["Cari","Carian"],["Cham","Cham"],["Cher","Cherokee"],["Chrs","Chorasmian"],["Copt","Coptic"],["Qaac","Coptic"],["Cprt","Cypriot"],["Cyrl","Cyrillic"],["Deva","Devanagari"],["Diak","Dives_Akuru"],["Dogr","Dogra"],["Dsrt","Deseret"],["Dupl","Duployan"],["Egyp","Egyptian_Hieroglyphs"],["Elba","Elbasan"],["Elym","Elymaic"],["Ethi","Ethiopic"],["Geor","Georgian"],["Glag","Glagolitic"],["Gong","Gunjala_Gondi"],["Gonm","Masaram_Gondi"],["Goth","Gothic"],["Gran","Grantha"],["Grek","Greek"],["Gujr","Gujarati"],["Guru","Gurmukhi"],["Hang","Hangul"],["Hani","Han"],["Hano","Hanunoo"],["Hatr","Hatran"],["Hebr","Hebrew"],["Hira","Hiragana"],["Hluw","Anatolian_Hieroglyphs"],["Hmng","Pahawh_Hmong"],["Hmnp","Nyiakeng_Puachue_Hmong"],["Hrkt","Katakana_Or_Hiragana"],["Hung","Old_Hungarian"],["Ital","Old_Italic"],["Java","Javanese"],["Kali","Kayah_Li"],["Kana","Katakana"],["Khar","Kharoshthi"],["Khmr","Khmer"],["Khoj","Khojki"],["Kits","Khitan_Small_Script"],["Knda","Kannada"],["Kthi","Kaithi"],["Lana","Tai_Tham"],["Laoo","Lao"],["Latn","Latin"],["Lepc","Lepcha"],["Limb","Limbu"],["Lina","Linear_A"],["Linb","Linear_B"],["Lisu","Lisu"],["Lyci","Lycian"],["Lydi","Lydian"],["Mahj","Mahajani"],["Maka","Makasar"],["Mand","Mandaic"],["Mani","Manichaean"],["Marc","Marchen"],["Medf","Medefaidrin"],["Mend","Mende_Kikakui"],["Merc","Meroitic_Cursive"],["Mero","Meroitic_Hieroglyphs"],["Mlym","Malayalam"],["Modi","Modi"],["Mong","Mongolian"],["Mroo","Mro"],["Mtei","Meetei_Mayek"],["Mult","Multani"],["Mymr","Myanmar"],["Nand","Nandinagari"],["Narb","Old_North_Arabian"],["Nbat","Nabataean"],["Newa","Newa"],["Nkoo","Nko"],["Nshu","Nushu"],["Ogam","Ogham"],["Olck","Ol_Chiki"],["Orkh","Old_Turkic"],["Orya","Oriya"],["Osge","Osage"],["Osma","Osmanya"],["Palm","Palmyrene"],["Pauc","Pau_Cin_Hau"],["Perm","Old_Permic"],["Phag","Phags_Pa"],["Phli","Inscriptional_Pahlavi"],["Phlp","Psalter_Pahlavi"],["Phnx","Phoenician"],["Plrd","Miao"],["Prti","Inscriptional_Parthian"],["Rjng","Rejang"],["Rohg","Hanifi_Rohingya"],["Runr","Runic"],["Samr","Samaritan"],["Sarb","Old_South_Arabian"],["Saur","Saurashtra"],["Sgnw","SignWriting"],["Shaw","Shavian"],["Shrd","Sharada"],["Sidd","Siddham"],["Sind","Khudawadi"],["Sinh","Sinhala"],["Sogd","Sogdian"],["Sogo","Old_Sogdian"],["Sora","Sora_Sompeng"],["Soyo","Soyombo"],["Sund","Sundanese"],["Sylo","Syloti_Nagri"],["Syrc","Syriac"],["Tagb","Tagbanwa"],["Takr","Takri"],["Tale","Tai_Le"],["Talu","New_Tai_Lue"],["Taml","Tamil"],["Tang","Tangut"],["Tavt","Tai_Viet"],["Telu","Telugu"],["Tfng","Tifinagh"],["Tglg","Tagalog"],["Thaa","Thaana"],["Thai","Thai"],["Tibt","Tibetan"],["Tirh","Tirhuta"],["Ugar","Ugaritic"],["Vaii","Vai"],["Wara","Warang_Citi"],["Wcho","Wancho"],["Xpeo","Old_Persian"],["Xsux","Cuneiform"],["Yezi","Yezidi"],["Yiii","Yi"],["Zanb","Zanabazar_Square"],["Zinh","Inherited"],["Qaai","Inherited"],["Zyyy","Common"],["Zzzz","Unknown"],["Adlam","Adlam"],["Caucasian_Albanian","Caucasian_Albanian"],["Arabic","Arabic"],["Imperial_Aramaic","Imperial_Aramaic"],["Armenian","Armenian"],["Avestan","Avestan"],["Balinese","Balinese"],["Bamum","Bamum"],["Bassa_Vah","Bassa_Vah"],["Batak","Batak"],["Bengali","Bengali"],["Bhaiksuki","Bhaiksuki"],["Bopomofo","Bopomofo"],["Brahmi","Brahmi"],["Braille","Braille"],["Buginese","Buginese"],["Buhid","Buhid"],["Chakma","Chakma"],["Canadian_Aboriginal","Canadian_Aboriginal"],["Carian","Carian"],["Cherokee","Cherokee"],["Chorasmian","Chorasmian"],["Coptic","Coptic"],["Cypriot","Cypriot"],["Cyrillic","Cyrillic"],["Devanagari","Devanagari"],["Dives_Akuru","Dives_Akuru"],["Dogra","Dogra"],["Deseret","Deseret"],["Duployan","Duployan"],["Egyptian_Hieroglyphs","Egyptian_Hieroglyphs"],["Elbasan","Elbasan"],["Elymaic","Elymaic"],["Ethiopic","Ethiopic"],["Georgian","Georgian"],["Glagolitic","Glagolitic"],["Gunjala_Gondi","Gunjala_Gondi"],["Masaram_Gondi","Masaram_Gondi"],["Gothic","Gothic"],["Grantha","Grantha"],["Greek","Greek"],["Gujarati","Gujarati"],["Gurmukhi","Gurmukhi"],["Hangul","Hangul"],["Han","Han"],["Hanunoo","Hanunoo"],["Hatran","Hatran"],["Hebrew","Hebrew"],["Hiragana","Hiragana"],["Anatolian_Hieroglyphs","Anatolian_Hieroglyphs"],["Pahawh_Hmong","Pahawh_Hmong"],["Nyiakeng_Puachue_Hmong","Nyiakeng_Puachue_Hmong"],["Katakana_Or_Hiragana","Katakana_Or_Hiragana"],["Old_Hungarian","Old_Hungarian"],["Old_Italic","Old_Italic"],["Javanese","Javanese"],["Kayah_Li","Kayah_Li"],["Katakana","Katakana"],["Kharoshthi","Kharoshthi"],["Khmer","Khmer"],["Khojki","Khojki"],["Khitan_Small_Script","Khitan_Small_Script"],["Kannada","Kannada"],["Kaithi","Kaithi"],["Tai_Tham","Tai_Tham"],["Lao","Lao"],["Latin","Latin"],["Lepcha","Lepcha"],["Limbu","Limbu"],["Linear_A","Linear_A"],["Linear_B","Linear_B"],["Lycian","Lycian"],["Lydian","Lydian"],["Mahajani","Mahajani"],["Makasar","Makasar"],["Mandaic","Mandaic"],["Manichaean","Manichaean"],["Marchen","Marchen"],["Medefaidrin","Medefaidrin"],["Mende_Kikakui","Mende_Kikakui"],["Meroitic_Cursive","Meroitic_Cursive"],["Meroitic_Hieroglyphs","Meroitic_Hieroglyphs"],["Malayalam","Malayalam"],["Mongolian","Mongolian"],["Mro","Mro"],["Meetei_Mayek","Meetei_Mayek"],["Multani","Multani"],["Myanmar","Myanmar"],["Nandinagari","Nandinagari"],["Old_North_Arabian","Old_North_Arabian"],["Nabataean","Nabataean"],["Nko","Nko"],["Nushu","Nushu"],["Ogham","Ogham"],["Ol_Chiki","Ol_Chiki"],["Old_Turkic","Old_Turkic"],["Oriya","Oriya"],["Osage","Osage"],["Osmanya","Osmanya"],["Palmyrene","Palmyrene"],["Pau_Cin_Hau","Pau_Cin_Hau"],["Old_Permic","Old_Permic"],["Phags_Pa","Phags_Pa"],["Inscriptional_Pahlavi","Inscriptional_Pahlavi"],["Psalter_Pahlavi","Psalter_Pahlavi"],["Phoenician","Phoenician"],["Miao","Miao"],["Inscriptional_Parthian","Inscriptional_Parthian"],["Rejang","Rejang"],["Hanifi_Rohingya","Hanifi_Rohingya"],["Runic","Runic"],["Samaritan","Samaritan"],["Old_South_Arabian","Old_South_Arabian"],["Saurashtra","Saurashtra"],["SignWriting","SignWriting"],["Shavian","Shavian"],["Sharada","Sharada"],["Siddham","Siddham"],["Khudawadi","Khudawadi"],["Sinhala","Sinhala"],["Sogdian","Sogdian"],["Old_Sogdian","Old_Sogdian"],["Sora_Sompeng","Sora_Sompeng"],["Soyombo","Soyombo"],["Sundanese","Sundanese"],["Syloti_Nagri","Syloti_Nagri"],["Syriac","Syriac"],["Tagbanwa","Tagbanwa"],["Takri","Takri"],["Tai_Le","Tai_Le"],["New_Tai_Lue","New_Tai_Lue"],["Tamil","Tamil"],["Tangut","Tangut"],["Tai_Viet","Tai_Viet"],["Telugu","Telugu"],["Tifinagh","Tifinagh"],["Tagalog","Tagalog"],["Thaana","Thaana"],["Tibetan","Tibetan"],["Tirhuta","Tirhuta"],["Ugaritic","Ugaritic"],["Vai","Vai"],["Warang_Citi","Warang_Citi"],["Wancho","Wancho"],["Old_Persian","Old_Persian"],["Cuneiform","Cuneiform"],["Yezidi","Yezidi"],["Yi","Yi"],["Zanabazar_Square","Zanabazar_Square"],["Inherited","Inherited"],["Common","Common"],["Unknown","Unknown"]])]]),XF=function(e,t){var r=zF.get(e);if(!r)throw new Error("Unknown property `"+e+"`.");var n=r.get(t);if(n)return n;throw new Error("Unknown value `"+t+"` for property `"+e+"`.")},YF=new Map([[75,8490],[83,383],[107,8490],[115,383],[181,924],[197,8491],[223,7838],[229,8491],[383,83],[452,453],[453,452],[455,456],[456,455],[458,459],[459,458],[497,498],[498,497],[618,42926],[642,42949],[669,42930],[837,8126],[914,976],[917,1013],[920,1012],[921,8126],[922,1008],[924,181],[928,982],[929,1009],[931,962],[934,981],[937,8486],[952,1012],[962,931],[969,8486],[976,914],[977,1012],[981,934],[982,928],[1008,922],[1009,929],[1012,[920,977,952]],[1013,917],[1042,7296],[1044,7297],[1054,7298],[1057,7299],[1058,7301],[1066,7302],[1074,7296],[1076,7297],[1086,7298],[1089,7299],[1090,[7300,7301]],[1098,7302],[1122,7303],[1123,7303],[4304,7312],[4305,7313],[4306,7314],[4307,7315],[4308,7316],[4309,7317],[4310,7318],[4311,7319],[4312,7320],[4313,7321],[4314,7322],[4315,7323],[4316,7324],[4317,7325],[4318,7326],[4319,7327],[4320,7328],[4321,7329],[4322,7330],[4323,7331],[4324,7332],[4325,7333],[4326,7334],[4327,7335],[4328,7336],[4329,7337],[4330,7338],[4331,7339],[4332,7340],[4333,7341],[4334,7342],[4335,7343],[4336,7344],[4337,7345],[4338,7346],[4339,7347],[4340,7348],[4341,7349],[4342,7350],[4343,7351],[4344,7352],[4345,7353],[4346,7354],[4349,7357],[4350,7358],[4351,7359],[5024,43888],[5025,43889],[5026,43890],[5027,43891],[5028,43892],[5029,43893],[5030,43894],[5031,43895],[5032,43896],[5033,43897],[5034,43898],[5035,43899],[5036,43900],[5037,43901],[5038,43902],[5039,43903],[5040,43904],[5041,43905],[5042,43906],[5043,43907],[5044,43908],[5045,43909],[5046,43910],[5047,43911],[5048,43912],[5049,43913],[5050,43914],[5051,43915],[5052,43916],[5053,43917],[5054,43918],[5055,43919],[5056,43920],[5057,43921],[5058,43922],[5059,43923],[5060,43924],[5061,43925],[5062,43926],[5063,43927],[5064,43928],[5065,43929],[5066,43930],[5067,43931],[5068,43932],[5069,43933],[5070,43934],[5071,43935],[5072,43936],[5073,43937],[5074,43938],[5075,43939],[5076,43940],[5077,43941],[5078,43942],[5079,43943],[5080,43944],[5081,43945],[5082,43946],[5083,43947],[5084,43948],[5085,43949],[5086,43950],[5087,43951],[5088,43952],[5089,43953],[5090,43954],[5091,43955],[5092,43956],[5093,43957],[5094,43958],[5095,43959],[5096,43960],[5097,43961],[5098,43962],[5099,43963],[5100,43964],[5101,43965],[5102,43966],[5103,43967],[5104,5112],[5105,5113],[5106,5114],[5107,5115],[5108,5116],[5109,5117],[5112,5104],[5113,5105],[5114,5106],[5115,5107],[5116,5108],[5117,5109],[7296,[1042,1074]],[7297,[1044,1076]],[7298,[1054,1086]],[7299,[1057,1089]],[7300,[7301,1090]],[7301,[1058,7300,1090]],[7302,[1066,1098]],[7303,[1122,1123]],[7304,[42570,42571]],[7312,4304],[7313,4305],[7314,4306],[7315,4307],[7316,4308],[7317,4309],[7318,4310],[7319,4311],[7320,4312],[7321,4313],[7322,4314],[7323,4315],[7324,4316],[7325,4317],[7326,4318],[7327,4319],[7328,4320],[7329,4321],[7330,4322],[7331,4323],[7332,4324],[7333,4325],[7334,4326],[7335,4327],[7336,4328],[7337,4329],[7338,4330],[7339,4331],[7340,4332],[7341,4333],[7342,4334],[7343,4335],[7344,4336],[7345,4337],[7346,4338],[7347,4339],[7348,4340],[7349,4341],[7350,4342],[7351,4343],[7352,4344],[7353,4345],[7354,4346],[7357,4349],[7358,4350],[7359,4351],[7566,42950],[7776,7835],[7835,7776],[7838,223],[8064,8072],[8065,8073],[8066,8074],[8067,8075],[8068,8076],[8069,8077],[8070,8078],[8071,8079],[8072,8064],[8073,8065],[8074,8066],[8075,8067],[8076,8068],[8077,8069],[8078,8070],[8079,8071],[8080,8088],[8081,8089],[8082,8090],[8083,8091],[8084,8092],[8085,8093],[8086,8094],[8087,8095],[8088,8080],[8089,8081],[8090,8082],[8091,8083],[8092,8084],[8093,8085],[8094,8086],[8095,8087],[8096,8104],[8097,8105],[8098,8106],[8099,8107],[8100,8108],[8101,8109],[8102,8110],[8103,8111],[8104,8096],[8105,8097],[8106,8098],[8107,8099],[8108,8100],[8109,8101],[8110,8102],[8111,8103],[8115,8124],[8124,8115],[8126,[837,921]],[8131,8140],[8140,8131],[8179,8188],[8188,8179],[8486,[937,969]],[8490,75],[8491,[197,229]],[42570,7304],[42571,7304],[42900,42948],[42926,618],[42930,669],[42931,43859],[42932,42933],[42933,42932],[42934,42935],[42935,42934],[42936,42937],[42937,42936],[42938,42939],[42939,42938],[42940,42941],[42941,42940],[42942,42943],[42943,42942],[42946,42947],[42947,42946],[42948,42900],[42949,642],[42950,7566],[43859,42931],[43888,5024],[43889,5025],[43890,5026],[43891,5027],[43892,5028],[43893,5029],[43894,5030],[43895,5031],[43896,5032],[43897,5033],[43898,5034],[43899,5035],[43900,5036],[43901,5037],[43902,5038],[43903,5039],[43904,5040],[43905,5041],[43906,5042],[43907,5043],[43908,5044],[43909,5045],[43910,5046],[43911,5047],[43912,5048],[43913,5049],[43914,5050],[43915,5051],[43916,5052],[43917,5053],[43918,5054],[43919,5055],[43920,5056],[43921,5057],[43922,5058],[43923,5059],[43924,5060],[43925,5061],[43926,5062],[43927,5063],[43928,5064],[43929,5065],[43930,5066],[43931,5067],[43932,5068],[43933,5069],[43934,5070],[43935,5071],[43936,5072],[43937,5073],[43938,5074],[43939,5075],[43940,5076],[43941,5077],[43942,5078],[43943,5079],[43944,5080],[43945,5081],[43946,5082],[43947,5083],[43948,5084],[43949,5085],[43950,5086],[43951,5087],[43952,5088],[43953,5089],[43954,5090],[43955,5091],[43956,5092],[43957,5093],[43958,5094],[43959,5095],[43960,5096],[43961,5097],[43962,5098],[43963,5099],[43964,5100],[43965,5101],[43966,5102],[43967,5103],[66560,66600],[66561,66601],[66562,66602],[66563,66603],[66564,66604],[66565,66605],[66566,66606],[66567,66607],[66568,66608],[66569,66609],[66570,66610],[66571,66611],[66572,66612],[66573,66613],[66574,66614],[66575,66615],[66576,66616],[66577,66617],[66578,66618],[66579,66619],[66580,66620],[66581,66621],[66582,66622],[66583,66623],[66584,66624],[66585,66625],[66586,66626],[66587,66627],[66588,66628],[66589,66629],[66590,66630],[66591,66631],[66592,66632],[66593,66633],[66594,66634],[66595,66635],[66596,66636],[66597,66637],[66598,66638],[66599,66639],[66600,66560],[66601,66561],[66602,66562],[66603,66563],[66604,66564],[66605,66565],[66606,66566],[66607,66567],[66608,66568],[66609,66569],[66610,66570],[66611,66571],[66612,66572],[66613,66573],[66614,66574],[66615,66575],[66616,66576],[66617,66577],[66618,66578],[66619,66579],[66620,66580],[66621,66581],[66622,66582],[66623,66583],[66624,66584],[66625,66585],[66626,66586],[66627,66587],[66628,66588],[66629,66589],[66630,66590],[66631,66591],[66632,66592],[66633,66593],[66634,66594],[66635,66595],[66636,66596],[66637,66597],[66638,66598],[66639,66599],[66736,66776],[66737,66777],[66738,66778],[66739,66779],[66740,66780],[66741,66781],[66742,66782],[66743,66783],[66744,66784],[66745,66785],[66746,66786],[66747,66787],[66748,66788],[66749,66789],[66750,66790],[66751,66791],[66752,66792],[66753,66793],[66754,66794],[66755,66795],[66756,66796],[66757,66797],[66758,66798],[66759,66799],[66760,66800],[66761,66801],[66762,66802],[66763,66803],[66764,66804],[66765,66805],[66766,66806],[66767,66807],[66768,66808],[66769,66809],[66770,66810],[66771,66811],[66776,66736],[66777,66737],[66778,66738],[66779,66739],[66780,66740],[66781,66741],[66782,66742],[66783,66743],[66784,66744],[66785,66745],[66786,66746],[66787,66747],[66788,66748],[66789,66749],[66790,66750],[66791,66751],[66792,66752],[66793,66753],[66794,66754],[66795,66755],[66796,66756],[66797,66757],[66798,66758],[66799,66759],[66800,66760],[66801,66761],[66802,66762],[66803,66763],[66804,66764],[66805,66765],[66806,66766],[66807,66767],[66808,66768],[66809,66769],[66810,66770],[66811,66771],[68736,68800],[68737,68801],[68738,68802],[68739,68803],[68740,68804],[68741,68805],[68742,68806],[68743,68807],[68744,68808],[68745,68809],[68746,68810],[68747,68811],[68748,68812],[68749,68813],[68750,68814],[68751,68815],[68752,68816],[68753,68817],[68754,68818],[68755,68819],[68756,68820],[68757,68821],[68758,68822],[68759,68823],[68760,68824],[68761,68825],[68762,68826],[68763,68827],[68764,68828],[68765,68829],[68766,68830],[68767,68831],[68768,68832],[68769,68833],[68770,68834],[68771,68835],[68772,68836],[68773,68837],[68774,68838],[68775,68839],[68776,68840],[68777,68841],[68778,68842],[68779,68843],[68780,68844],[68781,68845],[68782,68846],[68783,68847],[68784,68848],[68785,68849],[68786,68850],[68800,68736],[68801,68737],[68802,68738],[68803,68739],[68804,68740],[68805,68741],[68806,68742],[68807,68743],[68808,68744],[68809,68745],[68810,68746],[68811,68747],[68812,68748],[68813,68749],[68814,68750],[68815,68751],[68816,68752],[68817,68753],[68818,68754],[68819,68755],[68820,68756],[68821,68757],[68822,68758],[68823,68759],[68824,68760],[68825,68761],[68826,68762],[68827,68763],[68828,68764],[68829,68765],[68830,68766],[68831,68767],[68832,68768],[68833,68769],[68834,68770],[68835,68771],[68836,68772],[68837,68773],[68838,68774],[68839,68775],[68840,68776],[68841,68777],[68842,68778],[68843,68779],[68844,68780],[68845,68781],[68846,68782],[68847,68783],[68848,68784],[68849,68785],[68850,68786],[71840,71872],[71841,71873],[71842,71874],[71843,71875],[71844,71876],[71845,71877],[71846,71878],[71847,71879],[71848,71880],[71849,71881],[71850,71882],[71851,71883],[71852,71884],[71853,71885],[71854,71886],[71855,71887],[71856,71888],[71857,71889],[71858,71890],[71859,71891],[71860,71892],[71861,71893],[71862,71894],[71863,71895],[71864,71896],[71865,71897],[71866,71898],[71867,71899],[71868,71900],[71869,71901],[71870,71902],[71871,71903],[71872,71840],[71873,71841],[71874,71842],[71875,71843],[71876,71844],[71877,71845],[71878,71846],[71879,71847],[71880,71848],[71881,71849],[71882,71850],[71883,71851],[71884,71852],[71885,71853],[71886,71854],[71887,71855],[71888,71856],[71889,71857],[71890,71858],[71891,71859],[71892,71860],[71893,71861],[71894,71862],[71895,71863],[71896,71864],[71897,71865],[71898,71866],[71899,71867],[71900,71868],[71901,71869],[71902,71870],[71903,71871],[93760,93792],[93761,93793],[93762,93794],[93763,93795],[93764,93796],[93765,93797],[93766,93798],[93767,93799],[93768,93800],[93769,93801],[93770,93802],[93771,93803],[93772,93804],[93773,93805],[93774,93806],[93775,93807],[93776,93808],[93777,93809],[93778,93810],[93779,93811],[93780,93812],[93781,93813],[93782,93814],[93783,93815],[93784,93816],[93785,93817],[93786,93818],[93787,93819],[93788,93820],[93789,93821],[93790,93822],[93791,93823],[93792,93760],[93793,93761],[93794,93762],[93795,93763],[93796,93764],[93797,93765],[93798,93766],[93799,93767],[93800,93768],[93801,93769],[93802,93770],[93803,93771],[93804,93772],[93805,93773],[93806,93774],[93807,93775],[93808,93776],[93809,93777],[93810,93778],[93811,93779],[93812,93780],[93813,93781],[93814,93782],[93815,93783],[93816,93784],[93817,93785],[93818,93786],[93819,93787],[93820,93788],[93821,93789],[93822,93790],[93823,93791],[125184,125218],[125185,125219],[125186,125220],[125187,125221],[125188,125222],[125189,125223],[125190,125224],[125191,125225],[125192,125226],[125193,125227],[125194,125228],[125195,125229],[125196,125230],[125197,125231],[125198,125232],[125199,125233],[125200,125234],[125201,125235],[125202,125236],[125203,125237],[125204,125238],[125205,125239],[125206,125240],[125207,125241],[125208,125242],[125209,125243],[125210,125244],[125211,125245],[125212,125246],[125213,125247],[125214,125248],[125215,125249],[125216,125250],[125217,125251],[125218,125184],[125219,125185],[125220,125186],[125221,125187],[125222,125188],[125223,125189],[125224,125190],[125225,125191],[125226,125192],[125227,125193],[125228,125194],[125229,125195],[125230,125196],[125231,125197],[125232,125198],[125233,125199],[125234,125200],[125235,125201],[125236,125202],[125237,125203],[125238,125204],[125239,125205],[125240,125206],[125241,125207],[125242,125208],[125243,125209],[125244,125210],[125245,125211],[125246,125212],[125247,125213],[125248,125214],[125249,125215],[125250,125216],[125251,125217]]),JF={REGULAR:new Map([["d",WF().addRange(48,57)],["D",WF().addRange(0,47).addRange(58,65535)],["s",WF(32,160,5760,8239,8287,12288,65279).addRange(9,13).addRange(8192,8202).addRange(8232,8233)],["S",WF().addRange(0,8).addRange(14,31).addRange(33,159).addRange(161,5759).addRange(5761,8191).addRange(8203,8231).addRange(8234,8238).addRange(8240,8286).addRange(8288,12287).addRange(12289,65278).addRange(65280,65535)],["w",WF(95).addRange(48,57).addRange(65,90).addRange(97,122)],["W",WF(96).addRange(0,47).addRange(58,64).addRange(91,94).addRange(123,65535)]]),UNICODE:new Map([["d",WF().addRange(48,57)],["D",WF().addRange(0,47).addRange(58,1114111)],["s",WF(32,160,5760,8239,8287,12288,65279).addRange(9,13).addRange(8192,8202).addRange(8232,8233)],["S",WF().addRange(0,8).addRange(14,31).addRange(33,159).addRange(161,5759).addRange(5761,8191).addRange(8203,8231).addRange(8234,8238).addRange(8240,8286).addRange(8288,12287).addRange(12289,65278).addRange(65280,1114111)],["w",WF(95).addRange(48,57).addRange(65,90).addRange(97,122)],["W",WF(96).addRange(0,47).addRange(58,64).addRange(91,94).addRange(123,1114111)]]),UNICODE_IGNORE_CASE:new Map([["d",WF().addRange(48,57)],["D",WF().addRange(0,47).addRange(58,1114111)],["s",WF(32,160,5760,8239,8287,12288,65279).addRange(9,13).addRange(8192,8202).addRange(8232,8233)],["S",WF().addRange(0,8).addRange(14,31).addRange(33,159).addRange(161,5759).addRange(5761,8191).addRange(8203,8231).addRange(8234,8238).addRange(8240,8286).addRange(8288,12287).addRange(12289,65278).addRange(65280,1114111)],["w",WF(95,383,8490).addRange(48,57).addRange(65,90).addRange(97,122)],["W",WF(96).addRange(0,47).addRange(58,64).addRange(91,94).addRange(123,382).addRange(384,8489).addRange(8491,1114111)]])},$F=GF.generate,QF=VF.parse,ZF=WF().addRange(0,1114111),e_=WF().addRange(0,65535),t_=ZF.clone().remove(10,13,8232,8233),r_=function(e,t,r){return t?r?JF.UNICODE_IGNORE_CASE.get(e):JF.UNICODE.get(e):JF.REGULAR.get(e)},n_=function(e,t){try{return function(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}()}catch(r){throw new Error("Failed to recognize value `"+t+"` for property `"+e+"`.")}},a_=function(e,t){var r,n=e.split("="),a=n[0];if(1==n.length)r=function(e){try{var t=XF("General_Category",e);return n_("General_Category",t)}catch(e){}var r=KF(e);return n_(r)}(a);else{var s=KF(a),i=XF(s,n[1]);r=n_(s,i)}return t?ZF.clone().remove(r):r.clone()};WF.prototype.iuAddRange=function(e,t){do{var r=o_(e);r&&this.add(r)}while(++e<=t);return this};var s_=function(e,t){var r=QF(t,l_.useUnicodeFlag?"u":"");switch(r.type){case"characterClass":case"group":case"value":break;default:r=i_(r,t)}Object.assign(e,r)},i_=function(e,t){return{type:"group",behavior:"ignore",body:[e],raw:"(?:"+t+")"}},o_=function(e){return YF.get(e)||!1},u_=function(e,t){delete e.name,e.matchIndex=t},c_=function e(t,r,n){switch(t.type){case"dot":l_.unicode?s_(t,(p=l_.dotAll,p?ZF:t_).toString(r)):l_.dotAll&&s_(t,"[\\s\\S]");break;case"characterClass":t=function(e,t){var r=WF(),n=e.body,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;switch(o.type){case"value":if(r.add(o.codePoint),l_.ignoreCase&&l_.unicode&&!l_.useUnicodeFlag){var u=o_(o.codePoint);u&&r.add(u)}break;case"characterClassRange":var c=o.min.codePoint,l=o.max.codePoint;r.addRange(c,l),l_.ignoreCase&&l_.unicode&&!l_.useUnicodeFlag&&r.iuAddRange(c,l);break;case"characterClassEscape":r.add(r_(o.value,l_.unicode,l_.ignoreCase));break;case"unicodePropertyEscape":r.add(a_(o.value,o.negative));break;default:throw new Error("Unknown term type: "+o.type)}}return e.negative&&(r=(l_.unicode?ZF:e_).clone().remove(r)),s_(e,r.toString(t)),e}(t,r);break;case"unicodePropertyEscape":l_.unicodePropertyEscape&&s_(t,a_(t.value,t.negative).toString(r));break;case"characterClassEscape":s_(t,r_(t.value,l_.unicode,l_.ignoreCase).toString(r));break;case"group":if("normal"==t.behavior&&n.lastIndex++,t.name&&l_.namedGroup){var a=t.name.value;if(n.names[a])throw new Error("Multiple groups with the same name ("+a+") are not allowed.");var s=n.lastIndex;delete t.name,n.names[a]=s,n.onNamedGroup&&n.onNamedGroup.call(null,a,s),n.unmatchedReferences[a]&&(n.unmatchedReferences[a].forEach((function(e){u_(e,s)})),delete n.unmatchedReferences[a])}case"alternative":case"disjunction":case"quantifier":t.body=t.body.map((function(t){return e(t,r,n)}));break;case"value":var i=t.codePoint,o=WF(i);if(l_.ignoreCase&&l_.unicode&&!l_.useUnicodeFlag){var u=o_(i);u&&o.add(u)}s_(t,o.toString(r));break;case"reference":if(t.name){var c=t.name.value,l=n.names[c];if(l){u_(t,l);break}n.unmatchedReferences[c]||(n.unmatchedReferences[c]=[]),n.unmatchedReferences[c].push(t)}break;case"anchor":case"empty":case"group":break;default:throw new Error("Unknown term type: "+t.type)}var p;return t},l_={ignoreCase:!1,unicode:!1,dotAll:!1,useUnicodeFlag:!1,unicodePropertyEscape:!1,namedGroup:!1},p_=function(e,t,r){l_.unicode=t&&t.includes("u");var n={unicodePropertyEscape:l_.unicode,namedGroups:!0,lookbehind:r&&r.lookbehind};l_.ignoreCase=t&&t.includes("i");var a=r&&r.dotAllFlag;l_.dotAll=a&&t&&t.includes("s"),l_.namedGroup=r&&r.namedGroup,l_.useUnicodeFlag=r&&r.useUnicodeFlag,l_.unicodePropertyEscape=r&&r.unicodePropertyEscape;var s={hasUnicodeFlag:l_.useUnicodeFlag,bmpOnly:!l_.unicode},i={onNamedGroup:r&&r.onNamedGroup,lastIndex:0,names:Object.create(null),unmatchedReferences:Object.create(null)},o=QF(e,t,n);return c_(o,s,i),function(e){var t=Object.keys(e.unmatchedReferences);if(t.length>0)throw new Error("Unknown group names: "+t)}(i),$F(o)},d_=Object.freeze({unicodeFlag:1,dotAllFlag:2,unicodePropertyEscape:4,namedCaptureGroups:8}),f_="@babel/plugin-regexp-features/featuresKey",h_="@babel/plugin-regexp-features/runtimeKey";function m_(e,t){return e|t}function y_(e,t){return!!(e&t)}var g_={name:"@babel/helper-create-regexp-features-plugin",version:"7.8.8",author:"The Babel Team (https://babeljs.io/team)",license:"MIT",description:"Compile ESNext Regular Expressions to ES5",repository:{type:"git",url:"https://github.com/babel/babel",directory:"packages/babel-helper-create-regexp-features-plugin"},main:"lib/index.js",publishConfig:{access:"public"},keywords:["babel","babel-plugin"],dependencies:{"@babel/helper-annotate-as-pure":"^7.8.3","@babel/helper-regex":"^7.8.3","regexpu-core":"^4.7.0"},peerDependencies:{"@babel/core":"^7.0.0"},devDependencies:{"@babel/core":"^7.8.6","@babel/helper-plugin-test-runner":"^7.8.3"},gitHead:"c831a2450dbf252c75750a455c63e1016c2f2244"};var v_=function(e,t,r,n){for(var a=r-1,s=e.length;++a<s;)if(n(e[a],t))return a;return-1},b_=Array.prototype.splice;var x_=function(e,t,r,n){var a=n?v_:Ap,s=-1,i=t.length,o=e;for(e===t&&(t=Fn(t)),r&&(o=Wf(e,ln(r)));++s<i;)for(var u=0,c=t[s],l=r?r(c):c;(u=a(o,l,u,n))>-1;)o!==e&&b_.call(o,u,1),b_.call(e,u,1);return e};var E_=hh((function(e,t){return e&&e.length&&t&&t.length?x_(e,t):e}));function A_(e,t){var r=e.flags.split("");e.flags.indexOf(t)<0||(E_(r,t),e.flags=r.join(""))}var w_=g_.version.split(".").reduce((function(e,t){return 1e5*e+ +t}),0),S_="@babel/plugin-regexp-features/version";function D_(e){var t=e.name,r=e.feature,n=e.options,a=void 0===n?{}:n;return{name:t,pre:function(){var e,t=this.file,n=null!=(e=t.get(f_))?e:0,s=m_(n,d_[r]),i=a.useUnicodeFlag,o=a.runtime,u=void 0===o||o;!1===i&&(s=m_(s,d_.unicodeFlag)),s!==n&&t.set(f_,s),u||t.set(h_,!1),(!t.has(S_)||t.get(S_)<w_)&&t.set(S_,w_)},visitor:{RegExpLiteral:function(e){var t,r=e.node,n=this.file,a=n.get(f_),s=null==(t=n.get(h_))||t,i=function(e,t){var r=!1,n=!1,a=!1,s=!1,i=e.flags,o=e.pattern,u=i.includes("u");return u&&(y_(t,d_.unicodeFlag)||(r=!0),y_(t,d_.unicodePropertyEscape)&&/\\[pP]{/.test(o)&&(a=!0)),y_(t,d_.dotAllFlag)&&i.indexOf("s")>=0&&(n=!0),y_(t,d_.namedCaptureGroups)&&/\(\?<(?![=!])/.test(o)&&(s=!0),s||a||n||u&&!r?(u&&i.indexOf("s")>=0&&(n=!0),{useUnicodeFlag:r,onNamedGroup:function(){},namedGroup:s,unicodePropertyEscape:a,dotAllFlag:n,lookbehind:!0}):null}(r,a);if(null!==i){var o={};if(i.namedGroup&&(i.onNamedGroup=function(e,t){o[e]=t}),r.pattern=p_(r.pattern,r.flags,i),i.namedGroup&&Object.keys(o).length>0&&s&&!function(e){return e.parentPath.isMemberExpression({object:e.node,computed:!1})&&e.parentPath.get("property").isIdentifier({name:"test"})}(e)){var u=Li(this.addHelper("wrapRegExp"),[r,Qd(o)]);IP(u),e.replaceWith(u)}y_(a,d_.unicodeFlag)&&A_(r,"u"),y_(a,d_.dotAllFlag)&&A_(r,"s")}}}}}var C_=sP((function(e,t){e.assertVersion(7);var r=t.useUnicodeFlag,n=void 0===r||r;if("boolean"!=typeof n)throw new Error(".useUnicodeFlag must be a boolean, or undefined");return D_({name:"proposal-unicode-property-regex",feature:"unicodePropertyEscape",options:{useUnicodeFlag:n}})})),T_="function"==typeof Object.create?function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:function(e,t){e.super_=t;var r=function(){};r.prototype=t.prototype,e.prototype=new r,e.prototype.constructor=e};function j_(e,t){var r={seen:[],stylize:k_};return arguments.length>=3&&(r.depth=arguments[2]),arguments.length>=4&&(r.colors=arguments[3]),O_(t)?r.showHidden=t:t&&z_(r,t),L_(r.showHidden)&&(r.showHidden=!1),L_(r.depth)&&(r.depth=2),L_(r.colors)&&(r.colors=!1),L_(r.customInspect)&&(r.customInspect=!0),r.colors&&(r.stylize=P_),F_(r,e,r.depth)}function P_(e,t){var r=j_.styles[t];return r?"["+j_.colors[r][0]+"m"+e+"["+j_.colors[r][1]+"m":e}function k_(e,t){return e}function F_(e,t,r){if(e.customInspect&&t&&H_(t.inspect)&&t.inspect!==j_&&(!t.constructor||t.constructor.prototype!==t)){var n=t.inspect(r,e);return M_(n)||(n=F_(e,n,r)),n}var a=function(e,t){if(L_(t))return e.stylize("undefined","undefined");if(M_(t)){var r="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(r,"string")}if(R_(t))return e.stylize(""+t,"number");if(O_(t))return e.stylize(""+t,"boolean");if(N_(t))return e.stylize("null","null")}(e,t);if(a)return a;var s=Object.keys(t),i=function(e){var t={};return e.forEach((function(e,r){t[e]=!0})),t}(s);if(e.showHidden&&(s=Object.getOwnPropertyNames(t)),W_(t)&&(s.indexOf("message")>=0||s.indexOf("description")>=0))return __(t);if(0===s.length){if(H_(t)){var o=t.name?": "+t.name:"";return e.stylize("[Function"+o+"]","special")}if(U_(t))return e.stylize(RegExp.prototype.toString.call(t),"regexp");if(V_(t))return e.stylize(Date.prototype.toString.call(t),"date");if(W_(t))return __(t)}var u,c="",l=!1,p=["{","}"];(B_(t)&&(l=!0,p=["[","]"]),H_(t))&&(c=" [Function"+(t.name?": "+t.name:"")+"]");return U_(t)&&(c=" "+RegExp.prototype.toString.call(t)),V_(t)&&(c=" "+Date.prototype.toUTCString.call(t)),W_(t)&&(c=" "+__(t)),0!==s.length||l&&0!=t.length?r<0?U_(t)?e.stylize(RegExp.prototype.toString.call(t),"regexp"):e.stylize("[Object]","special"):(e.seen.push(t),u=l?function(e,t,r,n,a){for(var s=[],i=0,o=t.length;i<o;++i)X_(t,String(i))?s.push(I_(e,t,r,n,String(i),!0)):s.push("");return a.forEach((function(a){a.match(/^\d+$/)||s.push(I_(e,t,r,n,a,!0))})),s}(e,t,r,i,s):s.map((function(n){return I_(e,t,r,i,n,l)})),e.seen.pop(),function(e,t,r){if(e.reduce((function(e,t){return t.indexOf("\n"),e+t.replace(/\u001b\[\d\d?m/g,"").length+1}),0)>60)return r[0]+(""===t?"":t+"\n ")+" "+e.join(",\n ")+" "+r[1];return r[0]+t+" "+e.join(", ")+" "+r[1]}(u,c,p)):p[0]+c+p[1]}function __(e){return"["+Error.prototype.toString.call(e)+"]"}function I_(e,t,r,n,a,s){var i,o,u;if((u=Object.getOwnPropertyDescriptor(t,a)||{value:t[a]}).get?o=u.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):u.set&&(o=e.stylize("[Setter]","special")),X_(n,a)||(i="["+a+"]"),o||(e.seen.indexOf(u.value)<0?(o=N_(r)?F_(e,u.value,null):F_(e,u.value,r-1)).indexOf("\n")>-1&&(o=s?o.split("\n").map((function(e){return" "+e})).join("\n").substr(2):"\n"+o.split("\n").map((function(e){return" "+e})).join("\n")):o=e.stylize("[Circular]","special")),L_(i)){if(s&&a.match(/^\d+$/))return o;(i=JSON.stringify(""+a)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(i=i.substr(1,i.length-2),i=e.stylize(i,"name")):(i=i.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),i=e.stylize(i,"string"))}return i+": "+o}function B_(e){return Array.isArray(e)}function O_(e){return"boolean"==typeof e}function N_(e){return null===e}function R_(e){return"number"==typeof e}function M_(e){return"string"==typeof e}function L_(e){return void 0===e}function U_(e){return G_(e)&&"[object RegExp]"===K_(e)}function G_(e){return"object"==typeof e&&null!==e}function V_(e){return G_(e)&&"[object Date]"===K_(e)}function W_(e){return G_(e)&&("[object Error]"===K_(e)||e instanceof Error)}function H_(e){return"function"==typeof e}function q_(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e}function K_(e){return Object.prototype.toString.call(e)}j_.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},j_.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"};function z_(e,t){if(!t||!G_(t))return e;for(var r=Object.keys(t),n=r.length;n--;)e[r[n]]=t[r[n]];return e}function X_(e,t){return Object.prototype.hasOwnProperty.call(e,t)}var Y_=T_;function J_(e,t){if(e===t)return 0;for(var r=e.length,n=t.length,a=0,s=Math.min(r,n);a<s;++a)if(e[a]!==t[a]){r=e[a],n=t[a];break}return r<n?-1:n<r?1:0}var $_,Q_=Object.prototype.hasOwnProperty,Z_=Object.keys||function(e){var t=[];for(var r in e)Q_.call(e,r)&&t.push(r);return t},eI=Array.prototype.slice;function tI(){return void 0!==$_?$_:$_="foo"===function(){}.name}function rI(e){return Object.prototype.toString.call(e)}function nI(e){return!wg(e)&&("function"==typeof Pa.ArrayBuffer&&("function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(e):!!e&&(e instanceof DataView||!!(e.buffer&&e.buffer instanceof ArrayBuffer))))}function aI(e,t){e||lI(e,!0,t,"==",pI)}var sI=/\s*function\s+([^\(\s]*)\s*/;function iI(e){if(H_(e)){if(tI())return e.name;var t=e.toString().match(sI);return t&&t[1]}}function oI(e){this.name="AssertionError",this.actual=e.actual,this.expected=e.expected,this.operator=e.operator,e.message?(this.message=e.message,this.generatedMessage=!1):(this.message=function(e){return uI(cI(e.actual),128)+" "+e.operator+" "+uI(cI(e.expected),128)}(this),this.generatedMessage=!0);var t=e.stackStartFunction||lI;if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var r=new Error;if(r.stack){var n=r.stack,a=iI(t),s=n.indexOf("\n"+a);if(s>=0){var i=n.indexOf("\n",s+1);n=n.substring(i+1)}this.stack=n}}}function uI(e,t){return"string"==typeof e?e.length<t?e:e.slice(0,t):e}function cI(e){if(tI()||!H_(e))return j_(e);var t=iI(e);return"[Function"+(t?": "+t:"")+"]"}function lI(e,t,r,n,a){throw new oI({message:r,actual:e,expected:t,operator:n,stackStartFunction:a})}function pI(e,t){e||lI(e,!0,t,"==",pI)}function dI(e,t,r,n){if(e===t)return!0;if(wg(e)&&wg(t))return 0===J_(e,t);if(V_(e)&&V_(t))return e.getTime()===t.getTime();if(U_(e)&&U_(t))return e.source===t.source&&e.global===t.global&&e.multiline===t.multiline&&e.lastIndex===t.lastIndex&&e.ignoreCase===t.ignoreCase;if(null!==e&&"object"==typeof e||null!==t&&"object"==typeof t){if(nI(e)&&nI(t)&&rI(e)===rI(t)&&!(e instanceof Float32Array||e instanceof Float64Array))return 0===J_(new Uint8Array(e.buffer),new Uint8Array(t.buffer));if(wg(e)!==wg(t))return!1;var a=(n=n||{actual:[],expected:[]}).actual.indexOf(e);return-1!==a&&a===n.expected.indexOf(t)||(n.actual.push(e),n.expected.push(t),function(e,t,r,n){if(null==e||null==t)return!1;if(q_(e)||q_(t))return e===t;if(r&&Object.getPrototypeOf(e)!==Object.getPrototypeOf(t))return!1;var a=fI(e),s=fI(t);if(a&&!s||!a&&s)return!1;if(a)return e=eI.call(e),t=eI.call(t),dI(e,t,r);var i,o,u=Z_(e),c=Z_(t);if(u.length!==c.length)return!1;for(u.sort(),c.sort(),o=u.length-1;o>=0;o--)if(u[o]!==c[o])return!1;for(o=u.length-1;o>=0;o--)if(i=u[o],!dI(e[i],t[i],r,n))return!1;return!0}(e,t,r,n))}return r?e===t:e==t}function fI(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function hI(e,t){if(!e||!t)return!1;if("[object RegExp]"==Object.prototype.toString.call(t))return t.test(e);try{if(e instanceof t)return!0}catch(e){}return!Error.isPrototypeOf(t)&&!0===t.call({},e)}function mI(e,t,r,n){var a;if("function"!=typeof t)throw new TypeError('"block" argument must be a function');"string"==typeof r&&(n=r,r=null),a=function(e){var t;try{e()}catch(e){t=e}return t}(t),n=(r&&r.name?" ("+r.name+").":".")+(n?" "+n:"."),e&&!a&&lI(a,r,"Missing expected exception"+n);var s="string"==typeof n,i=!e&&a&&!r;if((!e&&W_(a)&&s&&hI(a,r)||i)&&lI(a,r,"Got unwanted exception"+n),e&&a&&r&&!hI(a,r)||!e&&a)throw a}aI.AssertionError=oI,T_(oI,Error),aI.fail=lI,aI.ok=pI,aI.equal=function e(t,r,n){t!=r&&lI(t,r,n,"==",e)},aI.notEqual=function e(t,r,n){t==r&&lI(t,r,n,"!=",e)},aI.deepEqual=function e(t,r,n){dI(t,r,!1)||lI(t,r,n,"deepEqual",e)},aI.deepStrictEqual=function e(t,r,n){dI(t,r,!0)||lI(t,r,n,"deepStrictEqual",e)},aI.notDeepEqual=function e(t,r,n){dI(t,r,!1)&&lI(t,r,n,"notDeepEqual",e)},aI.notDeepStrictEqual=function e(t,r,n){dI(t,r,!0)&&lI(t,r,n,"notDeepStrictEqual",e)},aI.strictEqual=function e(t,r,n){t!==r&&lI(t,r,n,"===",e)},aI.notStrictEqual=function e(t,r,n){t===r&&lI(t,r,n,"!==",e)},aI.throws=function(e,t,r){mI(!0,e,t,r)},aI.doesNotThrow=function(e,t,r){mI(!1,e,t,r)},aI.ifError=function(e){if(e)throw e};var yI=function(){function e(e,t,r){this._statements=[],this._resultName=null,this._scope=null,this._hub=null,this._scope=t,this._hub=r,this._importedSource=e}var t=e.prototype;return t.done=function(){return{statements:this._statements,resultName:this._resultName}},t.import=function(){return this._statements.push(Uo([],to(this._importedSource))),this},t.require=function(){return this._statements.push(Ki(Li(Qi("require"),[to(this._importedSource)]))),this},t.namespace=function(e){void 0===e&&(e="namespace"),e=this._scope.generateUidIdentifier(e);var t=this._statements[this._statements.length-1];return aI("ImportDeclaration"===t.type),aI(0===t.specifiers.length),t.specifiers=[Vo(e)],this._resultName=pp(e),this},t.default=function(e){e=this._scope.generateUidIdentifier(e);var t=this._statements[this._statements.length-1];return aI("ImportDeclaration"===t.type),aI(0===t.specifiers.length),t.specifiers=[Go(e)],this._resultName=pp(e),this},t.named=function(e,t){if("default"===t)return this.default(e);e=this._scope.generateUidIdentifier(e);var r=this._statements[this._statements.length-1];return aI("ImportDeclaration"===r.type),aI(0===r.specifiers.length),r.specifiers=[Wo(e,Qi(t))],this._resultName=pp(e),this},t.var=function(e){e=this._scope.generateUidIdentifier(e);var t=this._statements[this._statements.length-1];return"ExpressionStatement"!==t.type&&(aI(this._resultName),t=Ki(this._resultName),this._statements.push(t)),this._statements[this._statements.length-1]=Do("var",[Co(e,t.expression)]),this._resultName=pp(e),this},t.defaultInterop=function(){return this._interop(this._hub.addHelper("interopRequireDefault"))},t.wildcardInterop=function(){return this._interop(this._hub.addHelper("interopRequireWildcard"))},t._interop=function(e){var t=this._statements[this._statements.length-1];return"ExpressionStatement"===t.type?t.expression=Li(e,[t.expression]):"VariableDeclaration"===t.type?(aI(1===t.declarations.length),t.declarations[0].init=Li(e,[t.declarations[0].init])):aI.fail("Unexpected type."),this},t.prop=function(e){var t=this._statements[this._statements.length-1];return"ExpressionStatement"===t.type?t.expression=oo(t.expression,Qi(e)):"VariableDeclaration"===t.type?(aI(1===t.declarations.length),t.declarations[0].init=oo(t.declarations[0].init,Qi(e))):aI.fail("Unexpected type:"+t.type),this},t.read=function(e){this._resultName=oo(this._resultName,Qi(e))},e}();function gI(e){var t=e.node.sourceType;if("module"!==t&&"script"!==t)throw e.buildCodeFrameError('Unknown sourceType "'+t+'", cannot transform.');return"module"===e.node.sourceType}var vI=function(){function e(e,t,r){this._defaultOpts={importedSource:null,importedType:"commonjs",importedInterop:"babel",importingInterop:"babel",ensureLiveReference:!1,ensureNoContext:!1};var n=e.find((function(e){return e.isProgram()}));this._programPath=n,this._programScope=n.scope,this._hub=n.hub,this._defaultOpts=this._applyDefaults(t,r,!0)}var t=e.prototype;return t.addDefault=function(e,t){return this.addNamed("default",e,t)},t.addNamed=function(e,t,r){return aI("string"==typeof e),this._generateImport(this._applyDefaults(t,r),e)},t.addNamespace=function(e,t){return this._generateImport(this._applyDefaults(e,t),null)},t.addSideEffect=function(e,t){return this._generateImport(this._applyDefaults(e,t),!1)},t._applyDefaults=function(e,t,r){void 0===r&&(r=!1);var n=[];"string"==typeof e?(n.push({importedSource:e}),n.push(t)):(aI(!t,"Unexpected secondary arguments."),n.push(e));for(var a=Object.assign({},this._defaultOpts),s=function(){var e=o[i];if(!e)return"continue";Object.keys(a).forEach((function(t){void 0!==e[t]&&(a[t]=e[t])})),r||(void 0!==e.nameHint&&(a.nameHint=e.nameHint),void 0!==e.blockHoist&&(a.blockHoist=e.blockHoist))},i=0,o=n;i<o.length;i++)s();return a},t._generateImport=function(e,t){var r="default"===t,n=!!t&&!r,a=null===t,s=e.importedSource,i=e.importedType,o=e.importedInterop,u=e.importingInterop,c=e.ensureLiveReference,l=e.ensureNoContext,p=e.nameHint,d=e.blockHoist,f=p||t,h=gI(this._programPath),m=h&&"node"===u,y=h&&"babel"===u,g=new yI(s,this._programScope,this._hub);if("es6"===i){if(!m&&!y)throw new Error("Cannot import an ES6 module from CommonJS");g.import(),a?g.namespace(p||s):(r||n)&&g.named(f,t)}else{if("commonjs"!==i)throw new Error('Unexpected interopType "'+i+'"');if("babel"===o)if(m){f="default"!==f?f:s;var v=s+"$es6Default";g.import(),a?g.default(v).var(f||s).wildcardInterop():r?c?g.default(v).var(f||s).defaultInterop().read("default"):g.default(v).var(f).defaultInterop().prop(t):n&&g.default(v).read(t)}else y?(g.import(),a?g.namespace(f||s):(r||n)&&g.named(f,t)):(g.require(),a?g.var(f||s).wildcardInterop():(r||n)&&c?r?(f="default"!==f?f:s,g.var(f).read(t),g.defaultInterop()):g.var(s).read(t):r?g.var(f).defaultInterop().prop(t):n&&g.var(f).prop(t));else if("compiled"===o)m?(g.import(),a?g.default(f||s):(r||n)&&g.default(s).read(f)):y?(g.import(),a?g.namespace(f||s):(r||n)&&g.named(f,t)):(g.require(),a?g.var(f||s):(r||n)&&(c?g.var(s).read(f):g.prop(t).var(f)));else{if("uncompiled"!==o)throw new Error('Unknown importedInterop "'+o+'".');if(r&&c)throw new Error("No live reference for commonjs default");m?(g.import(),a?g.default(f||s):r?g.default(f):n&&g.default(s).read(f)):y?(g.import(),a?g.default(f||s):r?g.default(f):n&&g.named(f,t)):(g.require(),a?g.var(f||s):r?g.var(f):n&&(c?g.var(s).read(f):g.var(f).prop(t)))}}var b=g.done(),x=b.statements,E=b.resultName;return this._insertStatements(x,d),(r||n)&&l&&"Identifier"!==E.type?yo([ro(0),E]):E},t._insertStatements=function(e,t){void 0===t&&(t=3),e.forEach((function(e){e._blockHoist=t}));var r=this._programPath.get("body").find((function(e){var t=e.node._blockHoist;return Number.isFinite(t)&&t<4}));r?r.insertBefore(e):this._programPath.unshiftContainer("body",e)},e}();var bI=sP((function(e,t){e.assertVersion(7);var r=t.method,n=t.module;return r&&n?{name:"transform-async-to-generator",visitor:{Function:function(e,t){if(e.node.async&&!e.node.generator){var a=t.methodWrapper;OP(e,{wrapAsync:a=a?pp(a):t.methodWrapper=function(e,t,r,n){return new vI(e).addNamed(t,r,n)}(e,r,n)})}}}}:{name:"transform-async-to-generator",visitor:{Function:function(e,t){e.node.async&&!e.node.generator&&OP(e,{wrapAsync:t.addHelper("asyncToGenerator")})}}}})),xI=sP((function(e,t){e.assertVersion(7);var r=t.spec;return{name:"transform-arrow-functions",visitor:{ArrowFunctionExpression:function(e){e.isArrowFunctionExpression()&&e.arrowFunctionToExpression({allowInsertArrow:!1,specCompliant:!!r})}}}})),EI=sP((function(e){function t(e,t){var r=t.get(e),n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s,o=i.node;if(i.isFunctionDeclaration()){var u=Do("let",[Co(o.id,Id(o))]);u._blockHoist=2,o.id=null,i.replaceWith(u)}}}return e.assertVersion(7),{name:"transform-block-scoped-functions",visitor:{BlockStatement:function(e){var r=e.node,n=e.parent;lt(n,{body:r})||bt(n)||t("body",e)},SwitchCase:function(e){t("consequent",e)}}}}));function AI(){var e=i(["",'("','")']);return AI=function(){return e},e}function wI(e,t,r){var n=r.letReferences[e.name];return!!n&&t.getBindingIdentifier(e.name)===n}var SI={ReferencedIdentifier:function(e,t){if(t.tdzEnabled){var r=e.node,n=e.parent,a=e.scope;if(!e.parentPath.isFor({left:r})&&wI(r,a,t)){var s=a.getBinding(r.name).path;if(!s.isFunctionDeclaration()){var i=function(e,t){var r=t._guessExecutionStatusRelativeTo(e);return"before"===r?"outside":"after"===r?"inside":"maybe"}(e,s);if("outside"!==i)if("maybe"===i){var o=function(e,t){return Li(t.addHelper("temporalRef"),[e,to(e.name)])}(r,t);if(s.parent._tdzThis=!0,e.skip(),e.parentPath.isUpdateExpression()){if(n._ignoreBlockScopingTDZ)return;e.parentPath.replaceWith(yo([o,n]))}else e.replaceWith(o)}else"inside"===i&&e.replaceWith(dE.ast(AI(),t.addHelper("tdz"),r.name))}}}},AssignmentExpression:{exit:function(e,t){if(t.tdzEnabled){var r=e.node;if(!r._ignoreBlockScopingTDZ){for(var n=[],a=e.getBindingIdentifiers(),s=0,i=Object.keys(a);s<i.length;s++){var o=a[i[s]];wI(o,e.scope,t)&&n.push(o)}n.length&&(r._ignoreBlockScopingTDZ=!0,n.push(r),e.replaceWithMultiple(n.map((function(e){return Ki(e)}))))}}}}};var DI=function(e){return hh((function(t,r){var n=-1,a=r.length,s=a>1?r[a-1]:void 0,i=a>2?r[2]:void 0;for(s=e.length>3&&"function"==typeof s?(a--,s):void 0,i&&Jf(r[0],r[1],i)&&(s=a<3?void 0:s,a=1),t=Object(t);++n<a;){var o=r[n];o&&e(t,o,n,s)}return t}))}((function(e,t){zr(t,jn(t),e)})),CI=new WeakSet,TI=sP((function(e,t){e.assertVersion(7);var r=t.throwIfClosureRequired,n=void 0!==r&&r,a=t.tdz,s=void 0!==a&&a;if("boolean"!=typeof n)throw new Error(".throwIfClosureRequired must be a boolean, or undefined");if("boolean"!=typeof s)throw new Error(".tdz must be a boolean, or undefined");return{name:"transform-block-scoping",visitor:{VariableDeclaration:function(e){var t=e.node,r=e.parent,n=e.scope;if(PI(t)&&(FI(e,null,r,n,!0),t._tdzThis)){for(var a=[t],s=0;s<t.declarations.length;s++){var i=t.declarations[s],o=_i("=",i.id,i.init||n.buildUndefinedNode());o._ignoreBlockScopingTDZ=!0,a.push(Ki(o)),i.init=this.addHelper("temporalUndefined")}t._blockHoist=2,e.isCompletionRecord()&&a.push(Ki(n.buildUndefinedNode())),e.replaceWithMultiple(a)}},Loop:function(e,t){var r=e.parent,a=e.scope;e.ensureBlock();var i=new LI(e,e.get("body"),r,a,n,s,t).run();i&&e.replaceWith(i)},CatchClause:function(e,t){var r=e.parent,a=e.scope;new LI(null,e.get("body"),r,a,n,s,t).run()},"BlockStatement|SwitchStatement|Program":function(e,t){(function(e){return ot(e.parent)||h(e.parent)})(e)||new LI(null,e,e.parent,e.scope,n,s,t).run()}}}}));var jI=dE('\n if (typeof RETURN === "object") return RETURN.v;\n');function PI(e){return!!q(e)&&(!!e[Ds]||("let"===e.kind||"const"===e.kind))}function kI(e){var t=e.find((function(e){return e.isLoop()||e.isFunction()}));return t&&t.isLoop()}function FI(e,t,r,n,a){if(void 0===a&&(a=!1),t||(t=e.node),kI(e)&&!ut(r))for(var s=0;s<t.declarations.length;s++){var i=t.declarations[s];i.init=i.init||n.buildUndefinedNode()}if(t[Ds]=!0,t.kind="var",a)for(var o=n.getFunctionParent()||n.getProgramParent(),u=0,c=Object.keys(e.getBindingIdentifiers());u<c.length;u++){var l=c[u],p=n.getOwnBinding(l);p&&(p.kind="var"),n.moveBindingTo(l,o)}}function _I(e){return q(e,{kind:"var"})&&!PI(e)}var II=sA.visitors.merge([{Loop:{enter:function(e,t){t.loopDepth++},exit:function(e,t){t.loopDepth--}},Function:function(e,t){return t.loopDepth>0?e.traverse(BI,t):e.traverse(SI,t),e.skip()}},SI]),BI=sA.visitors.merge([{ReferencedIdentifier:function(e,t){var r=t.letReferences[e.node.name];if(r){var n=e.scope.getBindingIdentifier(e.node.name);n&&n!==r||(t.closurify=!0)}}},SI]),OI={enter:function(e,t){var r=e.node;e.parent;if(e.isForStatement()){if(_I(r.init)){var n=t.pushDeclar(r.init);1===n.length?r.init=n[0]:r.init=yo(n)}}else if(e.isFor())_I(r.left)&&(t.pushDeclar(r.left),r.left=r.left.declarations[0].id);else if(_I(r))e.replaceWithMultiple(t.pushDeclar(r).map((function(e){return Ki(e)})));else if(e.isFunction())return e.skip()}},NI={LabeledStatement:function(e,t){var r=e.node;t.innerLabels.push(r.label.name)}},RI={enter:function(e,t){if(e.isAssignmentExpression()||e.isUpdateExpression())for(var r=0,n=Object.keys(e.getBindingIdentifiers());r<n.length;r++){var a=n[r];t.outsideReferences[a]===e.scope.getBindingIdentifier(a)&&(t.reassignments[a]=!0)}else e.isReturnStatement()&&t.returnStatements.push(e)}};var MI={Loop:function(e,t){var r=t.ignoreLabeless;t.ignoreLabeless=!0,e.traverse(MI,t),t.ignoreLabeless=r,e.skip()},Function:function(e){e.skip()},SwitchCase:function(e,t){var r=t.inSwitchCase;t.inSwitchCase=!0,e.traverse(MI,t),t.inSwitchCase=r,e.skip()},"BreakStatement|ContinueStatement|ReturnStatement":function(e,t){var r=e.node,n=e.scope;if(!r[this.LOOP_IGNORE]){var a,s=function(e){return d(e)?"break":y(e)?"continue":void 0}(r);if(s){if(r.label){if(t.innerLabels.indexOf(r.label.name)>=0)return;s=s+"|"+r.label.name}else{if(t.ignoreLabeless)return;if(d(r)&&t.inSwitchCase)return}t.hasBreakContinue=!0,t.map[s]=r,a=to(s)}e.isReturnStatement()&&(t.hasReturn=!0,a=lo([fo(Qi("v"),r.argument||n.buildUndefinedNode())])),a&&((a=mo(a))[this.LOOP_IGNORE]=!0,e.skip(),e.replaceWith(ef(a,r)))}}},LI=function(){function e(e,t,r,n,a,s,i){this.parent=r,this.scope=n,this.state=i,this.throwIfClosureRequired=a,this.tdzEnabled=s,this.blockPath=t,this.block=t.node,this.outsideLetReferences=Object.create(null),this.hasLetReferences=!1,this.letReferences=Object.create(null),this.body=[],e&&(this.loopParent=e.parent,this.loopLabel=C(this.loopParent)&&this.loopParent.label,this.loopPath=e,this.loop=e.node)}var t=e.prototype;return t.run=function(){var e=this.block;if(!CI.has(e)){CI.add(e);var t=this.getLetReferences();if(this.checkConstants(),lt(this.parent)||B(this.block))this.updateScopeInfo();else if(this.hasLetReferences)return t?this.wrapClosure():this.remap(),this.updateScopeInfo(t),this.loopLabel&&!C(this.loopParent)?eo(this.loopLabel,this.loop):void 0}},t.checkConstants=function(){for(var e=this.scope,t=this.state,r=0,n=Object.keys(e.bindings);r<n.length;r++){var a=n[r],s=e.bindings[a];if("const"===s.kind)for(var i=0,o=s.constantViolations;i<o.length;i++){var u=o[i],c=Li(t.addHelper("readOnlyError"),[to(a)]);u.isAssignmentExpression()?u.get("right").replaceWith(yo([c,u.get("right").node])):u.isUpdateExpression()?u.replaceWith(yo([c,u.node])):u.isForXStatement()&&(u.ensureBlock(),u.node.body.body.unshift(Ki(c)))}}},t.updateScopeInfo=function(e){for(var t=this.blockPath.scope,r=t.getFunctionParent()||t.getProgramParent(),n=this.letReferences,a=0,s=Object.keys(n);a<s.length;a++){var i=n[s[a]],o=t.getBinding(i.name);o&&("let"!==o.kind&&"const"!==o.kind||(o.kind="var",e?t.hasOwnBinding(i.name)&&t.removeBinding(i.name):t.moveBindingTo(i.name,r)))}},t.remap=function(){for(var e=this.letReferences,t=this.outsideLetReferences,r=this.scope,n=this.blockPath.scope,a=0,s=Object.keys(e);a<s.length;a++){var i=s[a],o=e[i];(r.parentHasBinding(i)||r.hasGlobal(i))&&(r.hasOwnBinding(i)&&r.rename(o.name),n.hasOwnBinding(i)&&n.rename(o.name))}for(var u=0,c=Object.keys(t);u<c.length;u++){var l=c[u],p=e[l];kI(this.blockPath)&&n.hasOwnBinding(l)&&n.rename(p.name)}},t.wrapClosure=function(){if(this.throwIfClosureRequired)throw this.blockPath.buildCodeFrameError("Compiling let/const in this block would add a closure (throwIfClosureRequired).");var e=this.block,t=this.outsideLetReferences;if(this.loop)for(var r=0,n=Object.keys(t);r<n.length;r++){var a=t[n[r]];(this.scope.hasGlobal(a.name)||this.scope.parentHasBinding(a.name))&&(delete t[a.name],delete this.letReferences[a.name],this.scope.rename(a.name),this.letReferences[a.name]=a,t[a.name]=a)}this.has=this.checkLoop(),this.hoistVarDeclarations();var s=qf(t).map((function(e){return pp(e)})),i=s.map((function(e){return pp(e)})),o=this.blockPath.isSwitchStatement(),u=$i(null,i,Ri(o?[e]:e.body));this.addContinuations(u);var c,l,p,d=Li(no(),s),f=".callee";if(sA.hasType(u.body,"YieldExpression",$p)&&(u.generator=!0,d=Qo(d,!0),f=".argument"+f),sA.hasType(u.body,"AwaitExpression",$p)&&(u.async=!0,d=Cc(d),f=".argument"+f),this.has.hasReturn||this.has.hasBreakContinue){var h=this.scope.generateUid("ret");this.body.push(Do("var",[Co(Qi(h),d)])),c="declarations.0.init"+f,l=this.body.length-1,this.buildHas(h)}else this.body.push(Ki(d)),c="expression"+f,l=this.body.length-1;if(o){var m=this.blockPath,y=m.parentPath,g=m.listKey,v=m.key;this.blockPath.replaceWithMultiple(this.body),p=y.get(g)[v+l]}else e.body=this.body,p=this.blockPath.get("body")[l];var b,x=p.get(c);if(this.loop){var E=this.scope.generateUid("loop"),A=this.loopPath.insertBefore(Do("var",[Co(Qi(E),u)]));x.replaceWith(Qi(E)),b=A[0].get("declarations.0.init")}else x.replaceWith(u),b=x;b.unwrapFunctionEnvironment()},t.addContinuations=function(e){var t=this,r={reassignments:{},returnStatements:[],outsideReferences:this.outsideLetReferences};this.scope.traverse(e,RI,r);for(var n=function(n){var a=e.params[n];if(!r.reassignments[a.name])return"continue";var s=a.name,i=t.scope.generateUid(a.name);e.params[n]=Qi(i),t.scope.rename(s,i,e),r.returnStatements.forEach((function(e){e.insertBefore(Ki(_i("=",Qi(s),Qi(i))))})),e.body.body.push(Ki(_i("=",Qi(s),Qi(i))))},a=0;a<e.params.length;a++)n(a)},t.getLetReferences=function(){var e=this,t=this.block,r=[];if(this.loop){var n=this.loop.left||this.loop.init;PI(n)&&(r.push(n),DI(this.outsideLetReferences,Ud(n)))}var a=function n(a,s){(Z(s=s||a.node)||A(s)||PI(s))&&(PI(s)&&FI(a,s,t,e.scope),r=r.concat(s.declarations||s)),C(s)&&n(a.get("body"),s.body)};if(t.body)for(var s=this.blockPath.get("body"),i=0;i<t.body.length;i++)a(s[i]);if(t.cases)for(var o=this.blockPath.get("cases"),u=0;u<t.cases.length;u++)for(var c=t.cases[u].consequent,l=0;l<c.length;l++){var p=c[l];a(o[u],p)}for(var d=0;d<r.length;d++){var f=Ud(r[d],!1,!0);DI(this.letReferences,f),this.hasLetReferences=!0}if(this.hasLetReferences){var h={letReferences:this.letReferences,closurify:!1,loopDepth:0,tdzEnabled:this.tdzEnabled,addHelper:function(t){return e.state.addHelper(t)}};return kI(this.blockPath)&&h.loopDepth++,this.blockPath.traverse(II,h),h.closurify}},t.checkLoop=function(){var e={hasBreakContinue:!1,ignoreLabeless:!1,inSwitchCase:!1,innerLabels:[],hasReturn:!1,isLoop:!!this.loop,map:{},LOOP_IGNORE:Symbol()};return this.blockPath.traverse(NI,e),this.blockPath.traverse(MI,e),e},t.hoistVarDeclarations=function(){this.blockPath.traverse(OI,this)},t.pushDeclar=function(e){for(var t=[],r=Ud(e),n=0,a=Object.keys(r);n<a.length;n++){var s=a[n];t.push(Co(r[s]))}this.body.push(Do(e.kind,t));for(var i=[],o=0;o<e.declarations.length;o++){var u=e.declarations[o];if(u.init){var c=_i("=",pp(u.id),pp(u.init));i.push(ef(c,u))}}return i},t.buildHas=function(e){var t,r=this.body,n=this.has,a=[];if(n.hasReturn&&(t=jI({RETURN:Qi(e)})),n.hasBreakContinue){for(var s=0,i=Object.keys(n.map);s<i.length;s++){var o=i[s];a.push(vo(to(o),[n.map[o]]))}if(n.hasReturn&&a.push(vo(null,[t])),1===a.length){var u=a[0];r.push(Zi(Ii("===",Qi(e),u.test),u.consequent[0]))}else{if(this.loop)for(var c=0;c<a.length;c++){var l=a[c].consequent[0];d(l)&&!l.label&&(this.loopLabel||(this.loopLabel=this.scope.generateUidIdentifier("loop")),l.label=pp(this.loopLabel))}r.push(bo(Qi(e),a))}}else n.hasReturn&&r.push(t)},e}(),UI={Array:!1,ArrayBuffer:!1,Atomics:!1,BigInt:!1,BigInt64Array:!1,BigUint64Array:!1,Boolean:!1,constructor:!1,DataView:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Float32Array:!1,Float64Array:!1,Function:!1,globalThis:!1,hasOwnProperty:!1,Infinity:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Map:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,Promise:!1,propertyIsEnumerable:!1,Proxy:!1,RangeError:!1,ReferenceError:!1,Reflect:!1,RegExp:!1,Set:!1,SharedArrayBuffer:!1,String:!1,Symbol:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,Uint8ClampedArray:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1,WeakMap:!1,WeakSet:!1},GI={Array:!1,Boolean:!1,constructor:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Function:!1,hasOwnProperty:!1,Infinity:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,propertyIsEnumerable:!1,RangeError:!1,ReferenceError:!1,RegExp:!1,String:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1},VI={Array:!1,ArrayBuffer:!1,Boolean:!1,constructor:!1,DataView:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Float32Array:!1,Float64Array:!1,Function:!1,hasOwnProperty:!1,Infinity:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Map:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,Promise:!1,propertyIsEnumerable:!1,Proxy:!1,RangeError:!1,ReferenceError:!1,Reflect:!1,RegExp:!1,Set:!1,String:!1,Symbol:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,Uint8ClampedArray:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1,WeakMap:!1,WeakSet:!1},WI={Array:!1,ArrayBuffer:!1,Atomics:!1,Boolean:!1,constructor:!1,DataView:!1,Date:!1,decodeURI:!1,decodeURIComponent:!1,encodeURI:!1,encodeURIComponent:!1,Error:!1,escape:!1,eval:!1,EvalError:!1,Float32Array:!1,Float64Array:!1,Function:!1,hasOwnProperty:!1,Infinity:!1,Int16Array:!1,Int32Array:!1,Int8Array:!1,isFinite:!1,isNaN:!1,isPrototypeOf:!1,JSON:!1,Map:!1,Math:!1,NaN:!1,Number:!1,Object:!1,parseFloat:!1,parseInt:!1,Promise:!1,propertyIsEnumerable:!1,Proxy:!1,RangeError:!1,ReferenceError:!1,Reflect:!1,RegExp:!1,Set:!1,SharedArrayBuffer:!1,String:!1,Symbol:!1,SyntaxError:!1,toLocaleString:!1,toString:!1,TypeError:!1,Uint16Array:!1,Uint32Array:!1,Uint8Array:!1,Uint8ClampedArray:!1,undefined:!1,unescape:!1,URIError:!1,valueOf:!1,WeakMap:!1,WeakSet:!1},HI={AbortController:!1,AbortSignal:!1,addEventListener:!1,alert:!1,AnalyserNode:!1,Animation:!1,AnimationEffectReadOnly:!1,AnimationEffectTiming:!1,AnimationEffectTimingReadOnly:!1,AnimationEvent:!1,AnimationPlaybackEvent:!1,AnimationTimeline:!1,applicationCache:!1,ApplicationCache:!1,ApplicationCacheErrorEvent:!1,atob:!1,Attr:!1,Audio:!1,AudioBuffer:!1,AudioBufferSourceNode:!1,AudioContext:!1,AudioDestinationNode:!1,AudioListener:!1,AudioNode:!1,AudioParam:!1,AudioProcessingEvent:!1,AudioScheduledSourceNode:!1,"AudioWorkletGlobalScope ":!1,AudioWorkletNode:!1,AudioWorkletProcessor:!1,BarProp:!1,BaseAudioContext:!1,BatteryManager:!1,BeforeUnloadEvent:!1,BiquadFilterNode:!1,Blob:!1,BlobEvent:!1,blur:!1,BroadcastChannel:!1,btoa:!1,BudgetService:!1,ByteLengthQueuingStrategy:!1,Cache:!1,caches:!1,CacheStorage:!1,cancelAnimationFrame:!1,cancelIdleCallback:!1,CanvasCaptureMediaStreamTrack:!1,CanvasGradient:!1,CanvasPattern:!1,CanvasRenderingContext2D:!1,ChannelMergerNode:!1,ChannelSplitterNode:!1,CharacterData:!1,clearInterval:!1,clearTimeout:!1,clientInformation:!1,ClipboardEvent:!1,close:!1,closed:!1,CloseEvent:!1,Comment:!1,CompositionEvent:!1,confirm:!1,console:!1,ConstantSourceNode:!1,ConvolverNode:!1,CountQueuingStrategy:!1,createImageBitmap:!1,Credential:!1,CredentialsContainer:!1,crypto:!1,Crypto:!1,CryptoKey:!1,CSS:!1,CSSConditionRule:!1,CSSFontFaceRule:!1,CSSGroupingRule:!1,CSSImportRule:!1,CSSKeyframeRule:!1,CSSKeyframesRule:!1,CSSMediaRule:!1,CSSNamespaceRule:!1,CSSPageRule:!1,CSSRule:!1,CSSRuleList:!1,CSSStyleDeclaration:!1,CSSStyleRule:!1,CSSStyleSheet:!1,CSSSupportsRule:!1,CustomElementRegistry:!1,customElements:!1,CustomEvent:!1,DataTransfer:!1,DataTransferItem:!1,DataTransferItemList:!1,defaultstatus:!1,defaultStatus:!1,DelayNode:!1,DeviceMotionEvent:!1,DeviceOrientationEvent:!1,devicePixelRatio:!1,dispatchEvent:!1,document:!1,Document:!1,DocumentFragment:!1,DocumentType:!1,DOMError:!1,DOMException:!1,DOMImplementation:!1,DOMMatrix:!1,DOMMatrixReadOnly:!1,DOMParser:!1,DOMPoint:!1,DOMPointReadOnly:!1,DOMQuad:!1,DOMRect:!1,DOMRectReadOnly:!1,DOMStringList:!1,DOMStringMap:!1,DOMTokenList:!1,DragEvent:!1,DynamicsCompressorNode:!1,Element:!1,ErrorEvent:!1,event:!1,Event:!1,EventSource:!1,EventTarget:!1,external:!1,fetch:!1,File:!1,FileList:!1,FileReader:!1,find:!1,focus:!1,FocusEvent:!1,FontFace:!1,FontFaceSetLoadEvent:!1,FormData:!1,frameElement:!1,frames:!1,GainNode:!1,Gamepad:!1,GamepadButton:!1,GamepadEvent:!1,getComputedStyle:!1,getSelection:!1,HashChangeEvent:!1,Headers:!1,history:!1,History:!1,HTMLAllCollection:!1,HTMLAnchorElement:!1,HTMLAreaElement:!1,HTMLAudioElement:!1,HTMLBaseElement:!1,HTMLBodyElement:!1,HTMLBRElement:!1,HTMLButtonElement:!1,HTMLCanvasElement:!1,HTMLCollection:!1,HTMLContentElement:!1,HTMLDataElement:!1,HTMLDataListElement:!1,HTMLDetailsElement:!1,HTMLDialogElement:!1,HTMLDirectoryElement:!1,HTMLDivElement:!1,HTMLDListElement:!1,HTMLDocument:!1,HTMLElement:!1,HTMLEmbedElement:!1,HTMLFieldSetElement:!1,HTMLFontElement:!1,HTMLFormControlsCollection:!1,HTMLFormElement:!1,HTMLFrameElement:!1,HTMLFrameSetElement:!1,HTMLHeadElement:!1,HTMLHeadingElement:!1,HTMLHRElement:!1,HTMLHtmlElement:!1,HTMLIFrameElement:!1,HTMLImageElement:!1,HTMLInputElement:!1,HTMLLabelElement:!1,HTMLLegendElement:!1,HTMLLIElement:!1,HTMLLinkElement:!1,HTMLMapElement:!1,HTMLMarqueeElement:!1,HTMLMediaElement:!1,HTMLMenuElement:!1,HTMLMetaElement:!1,HTMLMeterElement:!1,HTMLModElement:!1,HTMLObjectElement:!1,HTMLOListElement:!1,HTMLOptGroupElement:!1,HTMLOptionElement:!1,HTMLOptionsCollection:!1,HTMLOutputElement:!1,HTMLParagraphElement:!1,HTMLParamElement:!1,HTMLPictureElement:!1,HTMLPreElement:!1,HTMLProgressElement:!1,HTMLQuoteElement:!1,HTMLScriptElement:!1,HTMLSelectElement:!1,HTMLShadowElement:!1,HTMLSlotElement:!1,HTMLSourceElement:!1,HTMLSpanElement:!1,HTMLStyleElement:!1,HTMLTableCaptionElement:!1,HTMLTableCellElement:!1,HTMLTableColElement:!1,HTMLTableElement:!1,HTMLTableRowElement:!1,HTMLTableSectionElement:!1,HTMLTemplateElement:!1,HTMLTextAreaElement:!1,HTMLTimeElement:!1,HTMLTitleElement:!1,HTMLTrackElement:!1,HTMLUListElement:!1,HTMLUnknownElement:!1,HTMLVideoElement:!1,IDBCursor:!1,IDBCursorWithValue:!1,IDBDatabase:!1,IDBFactory:!1,IDBIndex:!1,IDBKeyRange:!1,IDBObjectStore:!1,IDBOpenDBRequest:!1,IDBRequest:!1,IDBTransaction:!1,IDBVersionChangeEvent:!1,IdleDeadline:!1,IIRFilterNode:!1,Image:!1,ImageBitmap:!1,ImageBitmapRenderingContext:!1,ImageCapture:!1,ImageData:!1,indexedDB:!1,innerHeight:!1,innerWidth:!1,InputEvent:!1,IntersectionObserver:!1,IntersectionObserverEntry:!1,Intl:!1,isSecureContext:!1,KeyboardEvent:!1,KeyframeEffect:!1,KeyframeEffectReadOnly:!1,length:!1,localStorage:!1,location:!0,Location:!1,locationbar:!1,matchMedia:!1,MediaDeviceInfo:!1,MediaDevices:!1,MediaElementAudioSourceNode:!1,MediaEncryptedEvent:!1,MediaError:!1,MediaKeyMessageEvent:!1,MediaKeySession:!1,MediaKeyStatusMap:!1,MediaKeySystemAccess:!1,MediaList:!1,MediaQueryList:!1,MediaQueryListEvent:!1,MediaRecorder:!1,MediaSettingsRange:!1,MediaSource:!1,MediaStream:!1,MediaStreamAudioDestinationNode:!1,MediaStreamAudioSourceNode:!1,MediaStreamEvent:!1,MediaStreamTrack:!1,MediaStreamTrackEvent:!1,menubar:!1,MessageChannel:!1,MessageEvent:!1,MessagePort:!1,MIDIAccess:!1,MIDIConnectionEvent:!1,MIDIInput:!1,MIDIInputMap:!1,MIDIMessageEvent:!1,MIDIOutput:!1,MIDIOutputMap:!1,MIDIPort:!1,MimeType:!1,MimeTypeArray:!1,MouseEvent:!1,moveBy:!1,moveTo:!1,MutationEvent:!1,MutationObserver:!1,MutationRecord:!1,name:!1,NamedNodeMap:!1,NavigationPreloadManager:!1,navigator:!1,Navigator:!1,NetworkInformation:!1,Node:!1,NodeFilter:!1,NodeIterator:!1,NodeList:!1,Notification:!1,OfflineAudioCompletionEvent:!1,OfflineAudioContext:!1,offscreenBuffering:!1,OffscreenCanvas:!0,onabort:!0,onafterprint:!0,onanimationend:!0,onanimationiteration:!0,onanimationstart:!0,onappinstalled:!0,onauxclick:!0,onbeforeinstallprompt:!0,onbeforeprint:!0,onbeforeunload:!0,onblur:!0,oncancel:!0,oncanplay:!0,oncanplaythrough:!0,onchange:!0,onclick:!0,onclose:!0,oncontextmenu:!0,oncuechange:!0,ondblclick:!0,ondevicemotion:!0,ondeviceorientation:!0,ondeviceorientationabsolute:!0,ondrag:!0,ondragend:!0,ondragenter:!0,ondragleave:!0,ondragover:!0,ondragstart:!0,ondrop:!0,ondurationchange:!0,onemptied:!0,onended:!0,onerror:!0,onfocus:!0,ongotpointercapture:!0,onhashchange:!0,oninput:!0,oninvalid:!0,onkeydown:!0,onkeypress:!0,onkeyup:!0,onlanguagechange:!0,onload:!0,onloadeddata:!0,onloadedmetadata:!0,onloadstart:!0,onlostpointercapture:!0,onmessage:!0,onmessageerror:!0,onmousedown:!0,onmouseenter:!0,onmouseleave:!0,onmousemove:!0,onmouseout:!0,onmouseover:!0,onmouseup:!0,onmousewheel:!0,onoffline:!0,ononline:!0,onpagehide:!0,onpageshow:!0,onpause:!0,onplay:!0,onplaying:!0,onpointercancel:!0,onpointerdown:!0,onpointerenter:!0,onpointerleave:!0,onpointermove:!0,onpointerout:!0,onpointerover:!0,onpointerup:!0,onpopstate:!0,onprogress:!0,onratechange:!0,onrejectionhandled:!0,onreset:!0,onresize:!0,onscroll:!0,onsearch:!0,onseeked:!0,onseeking:!0,onselect:!0,onstalled:!0,onstorage:!0,onsubmit:!0,onsuspend:!0,ontimeupdate:!0,ontoggle:!0,ontransitionend:!0,onunhandledrejection:!0,onunload:!0,onvolumechange:!0,onwaiting:!0,onwheel:!0,open:!1,openDatabase:!1,opener:!1,Option:!1,origin:!1,OscillatorNode:!1,outerHeight:!1,outerWidth:!1,PageTransitionEvent:!1,pageXOffset:!1,pageYOffset:!1,PannerNode:!1,parent:!1,Path2D:!1,PaymentAddress:!1,PaymentRequest:!1,PaymentRequestUpdateEvent:!1,PaymentResponse:!1,performance:!1,Performance:!1,PerformanceEntry:!1,PerformanceLongTaskTiming:!1,PerformanceMark:!1,PerformanceMeasure:!1,PerformanceNavigation:!1,PerformanceNavigationTiming:!1,PerformanceObserver:!1,PerformanceObserverEntryList:!1,PerformancePaintTiming:!1,PerformanceResourceTiming:!1,PerformanceTiming:!1,PeriodicWave:!1,Permissions:!1,PermissionStatus:!1,personalbar:!1,PhotoCapabilities:!1,Plugin:!1,PluginArray:!1,PointerEvent:!1,PopStateEvent:!1,postMessage:!1,Presentation:!1,PresentationAvailability:!1,PresentationConnection:!1,PresentationConnectionAvailableEvent:!1,PresentationConnectionCloseEvent:!1,PresentationConnectionList:!1,PresentationReceiver:!1,PresentationRequest:!1,print:!1,ProcessingInstruction:!1,ProgressEvent:!1,PromiseRejectionEvent:!1,prompt:!1,PushManager:!1,PushSubscription:!1,PushSubscriptionOptions:!1,queueMicrotask:!1,RadioNodeList:!1,Range:!1,ReadableStream:!1,registerProcessor:!1,RemotePlayback:!1,removeEventListener:!1,Request:!1,requestAnimationFrame:!1,requestIdleCallback:!1,resizeBy:!1,ResizeObserver:!1,ResizeObserverEntry:!1,resizeTo:!1,Response:!1,RTCCertificate:!1,RTCDataChannel:!1,RTCDataChannelEvent:!1,RTCDtlsTransport:!1,RTCIceCandidate:!1,RTCIceGatherer:!1,RTCIceTransport:!1,RTCPeerConnection:!1,RTCPeerConnectionIceEvent:!1,RTCRtpContributingSource:!1,RTCRtpReceiver:!1,RTCRtpSender:!1,RTCSctpTransport:!1,RTCSessionDescription:!1,RTCStatsReport:!1,RTCTrackEvent:!1,screen:!1,Screen:!1,screenLeft:!1,ScreenOrientation:!1,screenTop:!1,screenX:!1,screenY:!1,ScriptProcessorNode:!1,scroll:!1,scrollbars:!1,scrollBy:!1,scrollTo:!1,scrollX:!1,scrollY:!1,SecurityPolicyViolationEvent:!1,Selection:!1,self:!1,ServiceWorker:!1,ServiceWorkerContainer:!1,ServiceWorkerRegistration:!1,sessionStorage:!1,setInterval:!1,setTimeout:!1,ShadowRoot:!1,SharedWorker:!1,SourceBuffer:!1,SourceBufferList:!1,speechSynthesis:!1,SpeechSynthesisEvent:!1,SpeechSynthesisUtterance:!1,StaticRange:!1,status:!1,statusbar:!1,StereoPannerNode:!1,stop:!1,Storage:!1,StorageEvent:!1,StorageManager:!1,styleMedia:!1,StyleSheet:!1,StyleSheetList:!1,SubtleCrypto:!1,SVGAElement:!1,SVGAngle:!1,SVGAnimatedAngle:!1,SVGAnimatedBoolean:!1,SVGAnimatedEnumeration:!1,SVGAnimatedInteger:!1,SVGAnimatedLength:!1,SVGAnimatedLengthList:!1,SVGAnimatedNumber:!1,SVGAnimatedNumberList:!1,SVGAnimatedPreserveAspectRatio:!1,SVGAnimatedRect:!1,SVGAnimatedString:!1,SVGAnimatedTransformList:!1,SVGAnimateElement:!1,SVGAnimateMotionElement:!1,SVGAnimateTransformElement:!1,SVGAnimationElement:!1,SVGCircleElement:!1,SVGClipPathElement:!1,SVGComponentTransferFunctionElement:!1,SVGDefsElement:!1,SVGDescElement:!1,SVGDiscardElement:!1,SVGElement:!1,SVGEllipseElement:!1,SVGFEBlendElement:!1,SVGFEColorMatrixElement:!1,SVGFEComponentTransferElement:!1,SVGFECompositeElement:!1,SVGFEConvolveMatrixElement:!1,SVGFEDiffuseLightingElement:!1,SVGFEDisplacementMapElement:!1,SVGFEDistantLightElement:!1,SVGFEDropShadowElement:!1,SVGFEFloodElement:!1,SVGFEFuncAElement:!1,SVGFEFuncBElement:!1,SVGFEFuncGElement:!1,SVGFEFuncRElement:!1,SVGFEGaussianBlurElement:!1,SVGFEImageElement:!1,SVGFEMergeElement:!1,SVGFEMergeNodeElement:!1,SVGFEMorphologyElement:!1,SVGFEOffsetElement:!1,SVGFEPointLightElement:!1,SVGFESpecularLightingElement:!1,SVGFESpotLightElement:!1,SVGFETileElement:!1,SVGFETurbulenceElement:!1,SVGFilterElement:!1,SVGForeignObjectElement:!1,SVGGElement:!1,SVGGeometryElement:!1,SVGGradientElement:!1,SVGGraphicsElement:!1,SVGImageElement:!1,SVGLength:!1,SVGLengthList:!1,SVGLinearGradientElement:!1,SVGLineElement:!1,SVGMarkerElement:!1,SVGMaskElement:!1,SVGMatrix:!1,SVGMetadataElement:!1,SVGMPathElement:!1,SVGNumber:!1,SVGNumberList:!1,SVGPathElement:!1,SVGPatternElement:!1,SVGPoint:!1,SVGPointList:!1,SVGPolygonElement:!1,SVGPolylineElement:!1,SVGPreserveAspectRatio:!1,SVGRadialGradientElement:!1,SVGRect:!1,SVGRectElement:!1,SVGScriptElement:!1,SVGSetElement:!1,SVGStopElement:!1,SVGStringList:!1,SVGStyleElement:!1,SVGSVGElement:!1,SVGSwitchElement:!1,SVGSymbolElement:!1,SVGTextContentElement:!1,SVGTextElement:!1,SVGTextPathElement:!1,SVGTextPositioningElement:!1,SVGTitleElement:!1,SVGTransform:!1,SVGTransformList:!1,SVGTSpanElement:!1,SVGUnitTypes:!1,SVGUseElement:!1,SVGViewElement:!1,TaskAttributionTiming:!1,Text:!1,TextDecoder:!1,TextEncoder:!1,TextEvent:!1,TextMetrics:!1,TextTrack:!1,TextTrackCue:!1,TextTrackCueList:!1,TextTrackList:!1,TimeRanges:!1,toolbar:!1,top:!1,Touch:!1,TouchEvent:!1,TouchList:!1,TrackEvent:!1,TransitionEvent:!1,TreeWalker:!1,UIEvent:!1,URL:!1,URLSearchParams:!1,ValidityState:!1,visualViewport:!1,VisualViewport:!1,VTTCue:!1,WaveShaperNode:!1,WebAssembly:!1,WebGL2RenderingContext:!1,WebGLActiveInfo:!1,WebGLBuffer:!1,WebGLContextEvent:!1,WebGLFramebuffer:!1,WebGLProgram:!1,WebGLQuery:!1,WebGLRenderbuffer:!1,WebGLRenderingContext:!1,WebGLSampler:!1,WebGLShader:!1,WebGLShaderPrecisionFormat:!1,WebGLSync:!1,WebGLTexture:!1,WebGLTransformFeedback:!1,WebGLUniformLocation:!1,WebGLVertexArrayObject:!1,WebSocket:!1,WheelEvent:!1,window:!1,Window:!1,Worker:!1,WritableStream:!1,XMLDocument:!1,XMLHttpRequest:!1,XMLHttpRequestEventTarget:!1,XMLHttpRequestUpload:!1,XMLSerializer:!1,XPathEvaluator:!1,XPathExpression:!1,XPathResult:!1,XSLTProcessor:!1},qI={addEventListener:!1,applicationCache:!1,atob:!1,Blob:!1,BroadcastChannel:!1,btoa:!1,Cache:!1,caches:!1,clearInterval:!1,clearTimeout:!1,close:!0,console:!1,fetch:!1,FileReaderSync:!1,FormData:!1,Headers:!1,IDBCursor:!1,IDBCursorWithValue:!1,IDBDatabase:!1,IDBFactory:!1,IDBIndex:!1,IDBKeyRange:!1,IDBObjectStore:!1,IDBOpenDBRequest:!1,IDBRequest:!1,IDBTransaction:!1,IDBVersionChangeEvent:!1,ImageData:!1,importScripts:!0,indexedDB:!1,location:!1,MessageChannel:!1,MessagePort:!1,name:!1,navigator:!1,Notification:!1,onclose:!0,onconnect:!0,onerror:!0,onlanguagechange:!0,onmessage:!0,onoffline:!0,ononline:!0,onrejectionhandled:!0,onunhandledrejection:!0,performance:!1,Performance:!1,PerformanceEntry:!1,PerformanceMark:!1,PerformanceMeasure:!1,PerformanceNavigation:!1,PerformanceResourceTiming:!1,PerformanceTiming:!1,postMessage:!0,Promise:!1,queueMicrotask:!1,removeEventListener:!1,Request:!1,Response:!1,self:!0,ServiceWorkerRegistration:!1,setInterval:!1,setTimeout:!1,TextDecoder:!1,TextEncoder:!1,URL:!1,URLSearchParams:!1,WebSocket:!1,Worker:!1,WorkerGlobalScope:!1,XMLHttpRequest:!1},KI={__dirname:!1,__filename:!1,Buffer:!1,clearImmediate:!1,clearInterval:!1,clearTimeout:!1,console:!1,exports:!0,global:!1,Intl:!1,module:!1,process:!1,queueMicrotask:!1,require:!1,setImmediate:!1,setInterval:!1,setTimeout:!1,TextDecoder:!1,TextEncoder:!1,URL:!1,URLSearchParams:!1},zI={exports:!0,global:!1,module:!1,require:!1},XI={define:!1,require:!1},YI={after:!1,afterEach:!1,before:!1,beforeEach:!1,context:!1,describe:!1,it:!1,mocha:!1,run:!1,setup:!1,specify:!1,suite:!1,suiteSetup:!1,suiteTeardown:!1,teardown:!1,test:!1,xcontext:!1,xdescribe:!1,xit:!1,xspecify:!1},JI={afterAll:!1,afterEach:!1,beforeAll:!1,beforeEach:!1,describe:!1,expect:!1,fail:!1,fdescribe:!1,fit:!1,it:!1,jasmine:!1,pending:!1,runs:!1,spyOn:!1,spyOnProperty:!1,waits:!1,waitsFor:!1,xdescribe:!1,xit:!1},$I={afterAll:!1,afterEach:!1,beforeAll:!1,beforeEach:!1,describe:!1,expect:!1,fdescribe:!1,fit:!1,it:!1,jest:!1,pit:!1,require:!1,test:!1,xdescribe:!1,xit:!1,xtest:!1},QI={asyncTest:!1,deepEqual:!1,equal:!1,expect:!1,module:!1,notDeepEqual:!1,notEqual:!1,notOk:!1,notPropEqual:!1,notStrictEqual:!1,ok:!1,propEqual:!1,QUnit:!1,raises:!1,start:!1,stop:!1,strictEqual:!1,test:!1,throws:!1},ZI={console:!0,exports:!0,phantom:!0,require:!0,WebPage:!0},eB={emit:!1,exports:!1,getRow:!1,log:!1,module:!1,provides:!1,require:!1,respond:!1,send:!1,start:!1,sum:!1},tB={defineClass:!1,deserialize:!1,gc:!1,help:!1,importClass:!1,importPackage:!1,java:!1,load:!1,loadClass:!1,Packages:!1,print:!1,quit:!1,readFile:!1,readUrl:!1,runCommand:!1,seal:!1,serialize:!1,spawn:!1,sync:!1,toint32:!1,version:!1},rB={__DIR__:!1,__FILE__:!1,__LINE__:!1,com:!1,edu:!1,exit:!1,java:!1,Java:!1,javafx:!1,JavaImporter:!1,javax:!1,JSAdapter:!1,load:!1,loadWithNewGlobal:!1,org:!1,Packages:!1,print:!1,quit:!1},nB={ActiveXObject:!0,Enumerator:!0,GetObject:!0,ScriptEngine:!0,ScriptEngineBuildVersion:!0,ScriptEngineMajorVersion:!0,ScriptEngineMinorVersion:!0,VBArray:!0,WScript:!0,WSH:!0,XDomainRequest:!0},aB={$:!1,jQuery:!1},sB={YAHOO:!1,YAHOO_config:!1,YUI:!1,YUI_config:!1},iB={cat:!1,cd:!1,chmod:!1,config:!1,cp:!1,dirs:!1,echo:!1,env:!1,error:!1,exec:!1,exit:!1,find:!1,grep:!1,ln:!1,ls:!1,mkdir:!1,mv:!1,popd:!1,pushd:!1,pwd:!1,rm:!1,sed:!1,set:!1,target:!1,tempdir:!1,test:!1,touch:!1,which:!1},oB={$:!1,$$:!1,$A:!1,$break:!1,$continue:!1,$F:!1,$H:!1,$R:!1,$w:!1,Abstract:!1,Ajax:!1,Autocompleter:!1,Builder:!1,Class:!1,Control:!1,Draggable:!1,Draggables:!1,Droppables:!1,Effect:!1,Element:!1,Enumerable:!1,Event:!1,Field:!1,Form:!1,Hash:!1,Insertion:!1,ObjectRange:!1,PeriodicalExecuter:!1,Position:!1,Prototype:!1,Scriptaculous:!1,Selector:!1,Sortable:!1,SortableObserver:!1,Sound:!1,Template:!1,Toggle:!1,Try:!1},uB={_:!1,$:!1,Accounts:!1,AccountsClient:!1,AccountsCommon:!1,AccountsServer:!1,App:!1,Assets:!1,Blaze:!1,check:!1,Cordova:!1,DDP:!1,DDPRateLimiter:!1,DDPServer:!1,Deps:!1,EJSON:!1,Email:!1,HTTP:!1,Log:!1,Match:!1,Meteor:!1,Mongo:!1,MongoInternals:!1,Npm:!1,Package:!1,Plugin:!1,process:!1,Random:!1,ReactiveDict:!1,ReactiveVar:!1,Router:!1,ServiceConfiguration:!1,Session:!1,share:!1,Spacebars:!1,Template:!1,Tinytest:!1,Tracker:!1,UI:!1,Utils:!1,WebApp:!1,WebAppInternals:!1},cB={_isWindows:!1,_rand:!1,BulkWriteResult:!1,cat:!1,cd:!1,connect:!1,db:!1,getHostName:!1,getMemInfo:!1,hostname:!1,ISODate:!1,listFiles:!1,load:!1,ls:!1,md5sumFile:!1,mkdir:!1,Mongo:!1,NumberInt:!1,NumberLong:!1,ObjectId:!1,PlanCache:!1,print:!1,printjson:!1,pwd:!1,quit:!1,removeFile:!1,rs:!1,sh:!1,UUID:!1,version:!1,WriteResult:!1},lB={$:!1,Application:!1,Automation:!1,console:!1,delay:!1,Library:!1,ObjC:!1,ObjectSpecifier:!1,Path:!1,Progress:!1,Ref:!1},pB={addEventListener:!1,applicationCache:!1,atob:!1,Blob:!1,BroadcastChannel:!1,btoa:!1,Cache:!1,caches:!1,CacheStorage:!1,clearInterval:!1,clearTimeout:!1,Client:!1,clients:!1,Clients:!1,close:!0,console:!1,ExtendableEvent:!1,ExtendableMessageEvent:!1,fetch:!1,FetchEvent:!1,FileReaderSync:!1,FormData:!1,Headers:!1,IDBCursor:!1,IDBCursorWithValue:!1,IDBDatabase:!1,IDBFactory:!1,IDBIndex:!1,IDBKeyRange:!1,IDBObjectStore:!1,IDBOpenDBRequest:!1,IDBRequest:!1,IDBTransaction:!1,IDBVersionChangeEvent:!1,ImageData:!1,importScripts:!1,indexedDB:!1,location:!1,MessageChannel:!1,MessagePort:!1,name:!1,navigator:!1,Notification:!1,onclose:!0,onconnect:!0,onerror:!0,onfetch:!0,oninstall:!0,onlanguagechange:!0,onmessage:!0,onmessageerror:!0,onnotificationclick:!0,onnotificationclose:!0,onoffline:!0,ononline:!0,onpush:!0,onpushsubscriptionchange:!0,onrejectionhandled:!0,onsync:!0,onunhandledrejection:!0,performance:!1,Performance:!1,PerformanceEntry:!1,PerformanceMark:!1,PerformanceMeasure:!1,PerformanceNavigation:!1,PerformanceResourceTiming:!1,PerformanceTiming:!1,postMessage:!0,Promise:!1,queueMicrotask:!1,registration:!1,removeEventListener:!1,Request:!1,Response:!1,self:!1,ServiceWorker:!1,ServiceWorkerContainer:!1,ServiceWorkerGlobalScope:!1,ServiceWorkerMessageEvent:!1,ServiceWorkerRegistration:!1,setInterval:!1,setTimeout:!1,skipWaiting:!1,TextDecoder:!1,TextEncoder:!1,URL:!1,URLSearchParams:!1,WebSocket:!1,WindowClient:!1,Worker:!1,WorkerGlobalScope:!1,XMLHttpRequest:!1},dB={advanceClock:!1,fakeClearInterval:!1,fakeClearTimeout:!1,fakeSetInterval:!1,fakeSetTimeout:!1,resetTimeouts:!1,waitsForPromise:!1},fB={andThen:!1,click:!1,currentPath:!1,currentRouteName:!1,currentURL:!1,fillIn:!1,find:!1,findAll:!1,findWithAssert:!1,keyEvent:!1,pauseTest:!1,resumeTest:!1,triggerEvent:!1,visit:!1,wait:!1},hB={$:!1,$$:!1,browser:!1,by:!1,By:!1,DartObject:!1,element:!1,protractor:!1},mB={browser:!1,chrome:!1,opr:!1},yB={cloneInto:!1,createObjectIn:!1,exportFunction:!1,GM:!1,GM_addStyle:!1,GM_deleteValue:!1,GM_getResourceText:!1,GM_getResourceURL:!1,GM_getValue:!1,GM_info:!1,GM_listValues:!1,GM_log:!1,GM_openInTab:!1,GM_registerMenuCommand:!1,GM_setClipboard:!1,GM_setValue:!1,GM_xmlhttpRequest:!1,unsafeWindow:!1},gB={$:!1,$_:!1,$$:!1,$0:!1,$1:!1,$2:!1,$3:!1,$4:!1,$x:!1,chrome:!1,clear:!1,copy:!1,debug:!1,dir:!1,dirxml:!1,getEventListeners:!1,inspect:!1,keys:!1,monitor:!1,monitorEvents:!1,profile:!1,profileEnd:!1,queryObjects:!1,table:!1,undebug:!1,unmonitor:!1,unmonitorEvents:!1,values:!1},vB={builtin:UI,es5:GI,es2015:VI,es2017:WI,browser:HI,worker:qI,node:KI,commonjs:zI,amd:XI,mocha:YI,jasmine:JI,jest:$I,qunit:QI,phantomjs:ZI,couch:eB,rhino:tB,nashorn:rB,wsh:nB,jquery:aB,yui:sB,shelljs:iB,prototypejs:oB,meteor:uB,mongo:cB,applescript:lB,serviceworker:pB,atomtest:dB,embertest:fB,protractor:hB,"shared-node-browser":{clearInterval:!1,clearTimeout:!1,console:!1,setInterval:!1,setTimeout:!1,URL:!1,URLSearchParams:!1},webextensions:mB,greasemonkey:yB,devtools:gB},bB=Wt(Object.freeze({__proto__:null,builtin:UI,es5:GI,es2015:VI,es2017:WI,browser:HI,worker:qI,node:KI,commonjs:zI,amd:XI,mocha:YI,jasmine:JI,jest:$I,qunit:QI,phantomjs:ZI,couch:eB,rhino:tB,nashorn:rB,wsh:nB,jquery:aB,yui:sB,shelljs:iB,prototypejs:oB,meteor:uB,mongo:cB,applescript:lB,serviceworker:pB,atomtest:dB,embertest:fB,protractor:hB,webextensions:mB,greasemonkey:yB,devtools:gB,default:vB})),xB=Object.prototype.hasOwnProperty;var EB=function(e,t){return null!=e&&xB.call(e,t)};var AB=function(e,t){return null!=e&&YT(e,t,EB)};function wB(e,t,r,n,a){var s,i,o=Ld(t),u={};if(AB(e,o)&&(u=e[o]),e[o]=u,u._inherits=u._inherits||[],u._inherits.push(t),u._key=t.key,t.computed&&(u._computed=!0),t.decorators){var c=u.decorators=u.decorators||Fi([]);c.elements=c.elements.concat(t.decorators.map((function(e){return e.expression})).reverse())}if(u.value||u.initializer)throw n.buildCodeFrameError(t,"Key conflict with sibling node");(R(t)||N(t)||ue(t))&&(s=_d(t,t.key)),ht(t)?i=t.value:(N(t)||ue(t))&&((i=$i(null,t.params,t.body,t.generator,t.async)).returnType=t.returnType);var l=function(e){return!ue(e)&&!N(e)||"get"!==e.kind&&"set"!==e.kind?"value":e.kind}(t);return r&&"value"===l||(r=l),a&&T(s)&&("value"===r||"initializer"===r)&&w(i)&&(i=gE({id:s,node:i,scope:a})),i&&(Op(i,t),u[r]=i),u}function SB(e){for(var t=Fi([]),r=0;r<e.properties.length;r++){var n=e.properties[r],a=n.value;a.properties.unshift(fo(Qi("key"),_d(n))),t.elements.push(a)}return t}function DB(e){var t=lo([]);return Object.keys(e).forEach((function(r){var n=e[r],a=lo([]),s=fo(n._key,a,n._computed);Object.keys(n).forEach((function(e){var t=n[e];if("_"!==e[0]){var r=fo(Qi(e),t);Op(r,t),Np(t),a.properties.push(r)}})),t.properties.push(s)})),t}function CB(e){return Object.keys(e).forEach((function(t){var r=e[t];r.value&&(r.writable=ao(!0)),r.configurable=ao(!0),r.enumerable=ao(!0)})),DB(e)}function TB(){var e=i(["\n (function () {\n super(...arguments);\n })\n "]);return TB=function(){return e},e}function jB(e,t,r){var n=Ji(pp(e),[],t);return ef(n,r),n}function PB(e,t,r,n){var a={parent:void 0,scope:void 0,node:void 0,path:void 0,file:void 0,classId:void 0,classRef:void 0,superName:void 0,superReturns:[],isDerived:!1,extendsNative:!1,construct:void 0,constructorBody:void 0,userConstructor:void 0,userConstructorPath:void 0,hasConstructor:!1,instancePropBody:[],instancePropRefs:{},staticPropBody:[],body:[],superThises:[],pushedConstructor:!1,pushedInherits:!1,protoAlias:null,isLoose:!1,hasInstanceDescriptors:!1,hasStaticDescriptors:!1,instanceMutatorMap:{},staticMutatorMap:{}},s=function(e){Object.assign(a,e)},i=sA.visitors.merge([qP,{ThisExpression:function(e){a.superThises.push(e)}}]);function o(){if(function(){var e=!1,t=a.path.get("body.body"),r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var s;if(r){if(n>=t.length)break;s=t[n++]}else{if((n=t.next()).done)break;s=n.value}if(e=s.equals("kind","constructor"))break}if(!e){var i,o;if(a.isDerived){var u=dE.expression.ast(TB());i=u.params,o=u.body}else i=[],o=Ri([]);a.path.get("body").unshiftContainer("body",qo("constructor",Qi("constructor"),i,o))}}(),function(){var e=a.path.get("body.body"),t=Array.isArray(e),r=0;for(e=t?e:e[Symbol.iterator]();;){var n;if(t){if(r>=e.length)break;n=e[r++]}else{if((r=e.next()).done)break;n=r.value}var s=n,i=s.node;if(s.isClassProperty())throw s.buildCodeFrameError("Missing class properties transform.");if(i.decorators)throw s.buildCodeFrameError("Method has decorators, put the decorator plugin before the classes one.");ue(i)&&function(){var e="constructor"===i.kind;new YP({methodPath:s,objectRef:a.classRef,superRef:a.superName,isLoose:a.isLoose,file:a.file}).replace();var t=[];s.traverse(sA.visitors.merge([qP,{ReturnStatement:function(e){e.getFunctionParent().isArrowFunctionExpression()||t.push(e)}}])),e?p(t,i,s):l(i,s)}()}}(),function(){if(!a.isDerived)return;var e=a.userConstructorPath,t=e.get("body");e.traverse(i);var r=function(){var t=e.scope.generateDeclaredUidIdentifier("this");return r=function(){return pp(t)},t},n=a.superThises,s=Array.isArray(n),o=0;for(n=s?n:n[Symbol.iterator]();;){var u;if(s){if(o>=n.length)break;u=n[o++]}else{if((o=n.next()).done)break;u=o.value}var l=u,p=l.node;l.parentPath.isMemberExpression({object:p})?l.replaceWith(r()):l.replaceWith(Li(a.file.addHelper("assertThisInitialized"),[r()]))}var d=new Set;e.traverse(sA.visitors.merge([qP,{Super:function(e){var t=e.node,r=e.parentPath;r.isCallExpression({callee:t})&&d.add(r)}}]));var f,h=!!d.size,m=d,y=Array.isArray(m),g=0;for(m=y?m:m[Symbol.iterator]();;){var v;if(y){if(g>=m.length)break;v=m[g++]}else{if((g=m.next()).done)break;v=g.value}var b=v;c(b,a.superName,r,t),h&&b.find((function(t){return t===e||(t.isLoop()||t.isConditional()||t.isArrowFunctionExpression()?(h=!1,!0):void 0)}))}f=a.isLoose?function(e){var t=Li(a.file.addHelper("assertThisInitialized"),[r()]);return e?io("||",e,t):t}:function(e){return Li(a.file.addHelper("possibleConstructorReturn"),[r()].concat(e||[]))};var x=t.get("body");x.length&&x.pop().isReturnStatement()||t.pushContainer("body",mo(h?r():f()));var E=a.superReturns,A=Array.isArray(E),w=0;for(E=A?E:E[Symbol.iterator]();;){var S;if(A){if(w>=E.length)break;S=E[w++]}else{if((w=E.next()).done)break;S=w.value}var D=S;D.get("argument").replaceWith(f(D.node.argument))}}(),a.userConstructor){var e=a.constructorBody,t=a.userConstructor,r=a.construct;e.body=e.body.concat(t.body.body),ef(r,t),ef(e,t.body)}u()}function u(){d();var e,t,r=a.body;if(a.hasInstanceDescriptors&&(e=DB(a.instanceMutatorMap)),a.hasStaticDescriptors&&(t=DB(a.staticMutatorMap)),e||t){e&&(e=SB(e)),t&&(t=SB(t));var n=[pp(a.classRef),no(),no()];e&&(n[1]=e),t&&(n[2]=t);for(var i=0,o=0;o<n.length;o++)P(n[o])||(i=o);n=n.slice(0,i+1),r.push(Ki(Li(a.file.addHelper("createClass"),n)))}s({hasInstanceDescriptors:!1,hasStaticDescriptors:!1,instanceMutatorMap:{},staticMutatorMap:{}})}function c(e,t,r,n){var s,i=e.node;a.isLoose?(i.arguments.unshift(xo()),2===i.arguments.length&&le(i.arguments[1])&&S(i.arguments[1].argument,{name:"arguments"})?(i.arguments[1]=i.arguments[1].argument,i.callee=oo(pp(t),Qi("apply"))):i.callee=oo(pp(t),Qi("call")),s=io("||",i,xo())):(i=VP(Li(a.file.addHelper("getPrototypeOf"),[pp(a.classRef)]),xo(),i.arguments),s=Li(a.file.addHelper("possibleConstructorReturn"),[xo(),i])),e.parentPath.isExpressionStatement()&&e.parentPath.container===n.node.body&&n.node.body.length-1===e.parentPath.key?(a.superThises.length&&(s=_i("=",r(),s)),e.parentPath.replaceWith(mo(s))):e.replaceWith(_i("=",r(),s))}function l(e,t){var r=t?t.scope:a.scope;"method"===e.kind&&function(e,t){if(a.isLoose&&!e.decorators){var r=a.classRef;e.static||(!function(){if(null===a.protoAlias){s({protoAlias:a.scope.generateUidIdentifier("proto")});var e=oo(a.classRef,Qi("prototype")),t=Do("var",[Co(a.protoAlias,e)]);a.body.push(t)}}(),r=a.protoAlias);var n=oo(pp(r),e.key,e.computed||ft(e.key)),i=$i(null,e.params,e.body,e.generator,e.async);ef(i,e);var o=_d(e,e.key);T(o)&&(i=gE({node:i,id:o,scope:t}));var u=Ki(_i("=",n,i));return Op(u,e),a.body.push(u),!0}return!1}(e,r)||function(e,t,r,n){var i;void 0===r&&(r="value"),e.static?(s({hasStaticDescriptors:!0}),i=a.staticMutatorMap):(s({hasInstanceDescriptors:!0}),i=a.instanceMutatorMap);var o=wB(i,e,r,a.file,n);t&&(o.enumerable=ao(!0))}(e,!1,null,r)}function p(e,t,r){r.scope.hasOwnBinding(a.classRef.name)&&r.scope.rename(a.classRef.name),s({userConstructorPath:r,userConstructor:t,hasConstructor:!0,superReturns:e});var n=a.construct;Op(n,t),n.params=t.params,ef(n.body,t.body),n.body.directives=t.body.directives,function(){if(a.pushedConstructor)return;a.pushedConstructor=!0,(a.hasInstanceDescriptors||a.hasStaticDescriptors)&&u();a.body.push(a.construct),d()}()}function d(){a.isDerived&&!a.pushedInherits&&(s({pushedInherits:!0}),a.body.unshift(Ki(Li(a.file.addHelper(a.isLoose?"inheritsLoose":"inherits"),[pp(a.classRef),pp(a.superName)]))))}return function(e,t,r,n){s({parent:e.parent,scope:e.scope,node:e.node,path:e,file:t,isLoose:n}),s({classId:a.node.id,classRef:a.node.id?Qi(a.node.id.name):a.scope.generateUidIdentifier("class"),superName:a.node.superClass,isDerived:!!a.node.superClass,constructorBody:Ri([])}),s({extendsNative:a.isDerived&&r.has(a.superName.name)&&!a.scope.hasBinding(a.superName.name,!0)});var i=a.classRef,u=a.node,c=a.constructorBody;s({construct:jB(i,c,u)});var l=a.body,p=function(){var e=a.superName,t=[],r=[];if(a.isDerived){var n=pp(e);a.extendsNative&&IP(n=Li(a.file.addHelper("wrapNativeSuper"),[n]));var i=a.scope.generateUidIdentifierBasedOnNode(e);t.push(i),r.push(n),s({superName:pp(i)})}return{closureParams:t,closureArgs:r}}(),d=p.closureParams,f=p.closureArgs;o(),a.isLoose||c.body.unshift(Ki(Li(a.file.addHelper("classCallCheck"),[xo(),pp(a.classRef)]))),l=l.concat(a.staticPropBody.map((function(e){return e(pp(a.classRef))})));var h=e.isInStrictMode(),m=a.classId&&1===l.length;if(m&&!h){var y=a.construct.params,g=Array.isArray(y),v=0;for(y=g?y:y[Symbol.iterator]();;){var b;if(g){if(v>=y.length)break;b=y[v++]}else{if((v=y.next()).done)break;b=v.value}if(!S(b)){m=!1;break}}}var x=m?l[0].body.directives:[];return h||x.push(Oi(Ni("use strict"))),m?Id(l[0]):(l.push(mo(pp(a.classRef))),Li(Fo(d,Ri(l,x)),f))}(e,t,r,n)}var kB=function(e){return Object.keys(bB[e]).filter((function(e){return/^[A-Z]/.test(e)}))},FB=new Set([].concat(kB("builtin"),kB("browser"))),_B=sP((function(e,t){e.assertVersion(7);var r=t.loose,n=Symbol();return{name:"transform-classes",visitor:{ExportDefaultDeclaration:function(e){e.get("declaration").isClassDeclaration()&&nh(e)},ClassDeclaration:function(e){var t=e.node,r=t.id||e.scope.generateUidIdentifier("class");e.replaceWith(Do("let",[Co(r,Id(t))]))},ClassExpression:function(e,t){var a=e.node;if(!a[n]){var s=gE(e);s&&s!==a?e.replaceWith(s):(a[n]=!0,e.replaceWith(PB(e,t.file,FB,r)),e.isCallExpression()&&(IP(e),e.get("callee").isArrowFunctionExpression()&&e.get("callee").arrowFunctionToExpression()))}}}}})),IB=sP((function(e,t){e.assertVersion(7);var r=t.loose?function(e){var t=e.computedProps,r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}var o=a;"get"===o.kind||"set"===o.kind?i(e,o):s(pp(e.objId),o,e.body)}}:function(e){var t=e.objId,r=e.body,n=e.computedProps,o=e.state,u=n,c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}var d=p,f=_d(d);if("get"===d.kind||"set"===d.kind)i(e,d);else if(T(f,{value:"__proto__"}))s(t,d,r);else{if(1===n.length)return Li(o.addHelper("defineProperty"),[e.initPropExpression,f,a(d)]);r.push(Ki(Li(o.addHelper("defineProperty"),[pp(t),f,a(d)])))}}},n=dE("\n MUTATOR_MAP_REF[KEY] = MUTATOR_MAP_REF[KEY] || {};\n MUTATOR_MAP_REF[KEY].KIND = VALUE;\n ");function a(e){return R(e)?e.value:N(e)?$i(null,e.params,e.body,e.generator,e.async):void 0}function s(e,t,r){"get"===t.kind&&"set"===t.kind?i(e,t):r.push(Ki(_i("=",oo(pp(e),t.key,t.computed||ft(t.key)),a(t))))}function i(e,t){var r=e.body,s=e.getMutatorId,i=e.scope,o=!t.computed&&S(t.key)?to(t.key.name):t.key,u=i.maybeGenerateMemoised(o);u&&(r.push(Ki(_i("=",u,o))),o=u),r.push.apply(r,n({MUTATOR_MAP_REF:s(),KEY:pp(o),VALUE:a(t),KIND:Qi(t.kind)}))}return{name:"transform-computed-properties",visitor:{ObjectExpression:{exit:function(e,t){for(var n=e.node,a=e.parent,s=e.scope,i=!1,o=0,u=n.properties;o<u.length;o++){if(i=!0===u[o].computed)break}if(i){var c=[],l=[],p=!1,d=n.properties,f=Array.isArray(d),h=0;for(d=f?d:d[Symbol.iterator]();;){var m;if(f){if(h>=d.length)break;m=d[h++]}else{if((h=d.next()).done)break;m=h.value}var y=m;y.computed&&(p=!0),p?l.push(y):c.push(y)}var g,v=s.generateUidIdentifierBasedOnNode(a),b=lo(c),x=[];x.push(Do("var",[Co(v,b)]));var E=r({scope:s,objId:v,body:x,computedProps:l,initPropExpression:b,getMutatorId:function(){return g||(g=s.generateUidIdentifier("mutatorMap"),x.push(Do("var",[Co(g,lo([]))]))),pp(g)},state:t});g&&x.push(Ki(Li(t.addHelper("defineEnumerableProperties"),[pp(v),pp(g)]))),E?e.replaceWith(E):(x.push(Ki(pp(v))),e.replaceWithMultiple(x))}}}}}})),BB=sP((function(e,t){e.assertVersion(7);var r=t.loose,n=void 0!==r&&r,a=t.useBuiltIns,s=void 0!==a&&a;if("boolean"!=typeof n)throw new Error(".loose must be a boolean or undefined");var i=n;function o(e){for(var t=0,r=e.declarations;t<r.length;t++){if(yt(r[t].id))return!0}return!1}function c(e){for(var t=0,r=e.elements;t<r.length;t++){if(M(r[t]))return!0}return!1}var l={},p=function(e,t,r){if(t.length&&S(e)&&of(e,t[t.length-1])&&r.bindings[e.name])throw r.deopt=!0,l},d=function(){function e(e){this.blockHoist=e.blockHoist,this.operator=e.operator,this.arrays={},this.nodes=e.nodes||[],this.scope=e.scope,this.kind=e.kind,this.arrayOnlySpread=e.arrayOnlySpread,this.addHelper=e.addHelper}var t=e.prototype;return t.buildVariableAssignment=function(e,t){var r,n=this.operator;return _(e)&&(n="="),(r=n?Ki(_i(n,e,pp(t)||this.scope.buildUndefinedNode())):Do(this.kind,[Co(e,pp(t))]))._blockHoist=this.blockHoist,r},t.buildVariableDeclaration=function(e,t){var r=Do("var",[Co(pp(e),pp(t))]);return r._blockHoist=this.blockHoist,r},t.push=function(e,t){var r=pp(t);ce(e)?this.pushObjectPattern(e,r):Y(e)?this.pushArrayPattern(e,r):X(e)?this.pushAssignmentPattern(e,r):this.nodes.push(this.buildVariableAssignment(e,r))},t.toArray=function(e,t){return this.arrayOnlySpread||S(e)&&this.arrays[e.name]?e:this.scope.toArray(e,t)},t.pushAssignmentPattern=function(e,t){var r=e.left,n=e.right,a=this.scope.generateUidIdentifierBasedOnNode(t);this.nodes.push(this.buildVariableDeclaration(a,t));var s,i,o=Gi(Ii("===",pp(a),this.scope.buildUndefinedNode()),n,pp(a));yt(r)?("const"===this.kind?(s=this.scope.generateUidIdentifier(a.name),i=this.buildVariableDeclaration(s,o)):(s=a,i=Ki(_i("=",pp(a),o))),this.nodes.push(i),this.push(r,s)):this.nodes.push(this.buildVariableAssignment(r,o))},t.pushObjectRest=function(e,t,r,a){for(var i,o,u=[],c=!0,l=0;l<e.properties.length;l++){var p=e.properties[l];if(l>=a)break;if(!M(p)){var d=p.key;S(d)&&!p.computed?u.push(to(d.name)):fe(p.key)?u.push(pp(p.key)):ft(d)?u.push(to(String(d.value))):(u.push(pp(d)),c=!1)}}if(0===u.length)i=Li((o=this,s?oo(Qi("Object"),Qi("assign")):o.addHelper("extends")),[lo([]),pp(t)]);else{var f=Fi(u);c||(f=Li(oo(f,Qi("map")),[this.addHelper("toPropertyKey")])),i=Li(this.addHelper("objectWithoutProperties"+(n?"Loose":"")),[pp(t),f])}this.nodes.push(this.buildVariableAssignment(r.argument,i))},t.pushObjectProperty=function(e,t){ft(e.key)&&(e.computed=!0);var r=e.value,n=oo(pp(t),e.key,e.computed);yt(r)?this.push(r,n):this.nodes.push(this.buildVariableAssignment(r,n))},t.pushObjectPattern=function(e,t){if(e.properties.length||this.nodes.push(Ki(Li(this.addHelper("objectDestructuringEmpty"),[t]))),e.properties.length>1&&!this.scope.isStatic(t)){var r=this.scope.generateUidIdentifierBasedOnNode(t);this.nodes.push(this.buildVariableDeclaration(r,t)),t=r}if(function(e){for(var t=0,r=e.properties;t<r.length;t++){if(M(r[t]))return!0}return!1}(e))for(var n,a=0;a<e.properties.length;a++){var s=e.properties[a];if(M(s))break;var i=s.key;if(s.computed&&!this.scope.isPure(i)){var o=this.scope.generateUidIdentifierBasedOnNode(i);this.nodes.push(this.buildVariableDeclaration(o,i)),n||(n=e=Object.assign({},e,{properties:e.properties.slice()})),n.properties[a]=Object.assign({},n.properties[a],{key:o})}}for(var u=0;u<e.properties.length;u++){var c=e.properties[u];M(c)?this.pushObjectRest(e,t,c,u):this.pushObjectProperty(c,t)}},t.canUnpackArrayPattern=function(e,t){if(!u(t))return!1;if(!(e.elements.length>t.elements.length)){if(e.elements.length<t.elements.length&&!c(e))return!1;for(var r=0,n=e.elements;r<n.length;r++){var a=n[r];if(!a)return!1;if(_(a))return!1}for(var s=0,i=t.elements;s<i.length;s++){var o=i[s];if(le(o))return!1;if(f(o))return!1;if(_(o))return!1}var d={deopt:!1,bindings:Ud(e)};try{rf(t,p,d)}catch(e){if(e!==l)throw e}return!d.deopt}},t.pushUnpackedArrayPattern=function(e,t){for(var r=0;r<e.elements.length;r++){var n=e.elements[r];M(n)?this.push(n.argument,Fi(t.elements.slice(r))):this.push(n,t.elements[r])}},t.pushArrayPattern=function(e,t){if(e.elements){if(this.canUnpackArrayPattern(e,t))return this.pushUnpackedArrayPattern(e,t);var r=!c(e)&&e.elements.length,n=this.toArray(t,r);S(n)?t=n:(t=this.scope.generateUidIdentifierBasedOnNode(t),this.arrays[t.name]=!0,this.nodes.push(this.buildVariableDeclaration(t,n)));for(var a=0;a<e.elements.length;a++){var s=e.elements[a];if(s){var i=void 0;M(s)?(i=Li(oo(i=this.toArray(t),Qi("slice")),[ro(a)]),s=s.argument):i=oo(t,ro(a),!0),this.push(s,i)}}}},t.init=function(e,t){if(!u(t)&&!_(t)){var r=this.scope.maybeGenerateMemoised(t,!0);r&&(this.nodes.push(this.buildVariableDeclaration(r,pp(t))),t=r)}return this.push(e,t),this.nodes},e}();return{name:"transform-destructuring",visitor:{ExportNamedDeclaration:function(e){var t=e.get("declaration");if(t.isVariableDeclaration()&&o(t.node)){for(var r=[],n=0,a=Object.keys(e.getOuterBindingIdentifiers(e));n<a.length;n++){var s=a[n];r.push(Mo(Qi(s),Qi(s)))}e.replaceWith(t.node),e.insertAfter(Ro(null,r))}},ForXStatement:function(e){var t=this,r=e.node,n=e.scope,a=r.left;if(yt(a)){var s=n.generateUidIdentifier("ref");return r.left=Do("var",[Co(s)]),e.ensureBlock(),0===r.body.body.length&&e.isCompletionRecord()&&r.body.body.unshift(Ki(n.buildUndefinedNode())),void r.body.body.unshift(Ki(_i("=",a,s)))}if(q(a)){var o=a.declarations[0].id;if(yt(o)){var u=n.generateUidIdentifier("ref");r.left=Do(a.kind,[Co(u,null)]);var c=[];new d({kind:a.kind,scope:n,nodes:c,arrayOnlySpread:i,addHelper:function(e){return t.addHelper(e)}}).init(o,u),e.ensureBlock();var l=r.body;l.body=c.concat(l.body)}}},CatchClause:function(e){var t=this,r=e.node,n=e.scope,a=r.param;if(yt(a)){var s=n.generateUidIdentifier("ref");r.param=s;var o=[];new d({kind:"let",scope:n,nodes:o,arrayOnlySpread:i,addHelper:function(e){return t.addHelper(e)}}).init(a,s),r.body.body=o.concat(r.body.body)}},AssignmentExpression:function(e){var t=this,r=e.node,n=e.scope;if(yt(r.left)){var a,s=[],o=new d({operator:r.operator,scope:n,nodes:s,arrayOnlySpread:i,addHelper:function(e){return t.addHelper(e)}});!e.isCompletionRecord()&&e.parentPath.isExpressionStatement()||(a=n.generateUidIdentifierBasedOnNode(r.right,"ref"),s.push(Do("var",[Co(a,r.right)])),u(r.right)&&(o.arrays[a.name]=!0)),o.init(r.left,a||r.right),a&&(e.parentPath.isArrowFunctionExpression()?(e.replaceWith(Ri([])),s.push(mo(pp(a)))):s.push(Ki(pp(a)))),e.replaceWithMultiple(s),e.scope.crawl()}},VariableDeclaration:function(e){var t=this,r=e.node,n=e.scope,a=e.parent;if(!ct(a)&&a&&e.container&&o(r)){for(var s,u=r.kind,c=[],l=0;l<r.declarations.length;l++){var p=(s=r.declarations[l]).init,f=s.id,h=new d({blockHoist:r._blockHoist,nodes:c,scope:n,kind:r.kind,arrayOnlySpread:i,addHelper:function(e){return t.addHelper(e)}});yt(f)?(h.init(f,p),+l!=r.declarations.length-1&&ef(c[c.length-1],s)):c.push(ef(h.buildVariableAssignment(s.id,pp(s.init)),s))}for(var m=null,y=[],g=0,v=c;g<v.length;g++){var b,x=v[g];if(null!==m&&q(x))(b=m.declarations).push.apply(b,x.declarations);else x.kind=u,y.push(x),m=q(x)?x:null}for(var E=0,A=y;E<A.length;E++){var w=A[E];if(w.declarations){var S=w.declarations,D=Array.isArray(S),C=0;for(S=D?S:S[Symbol.iterator]();;){var T;if(D){if(C>=S.length)break;T=S[C++]}else{if((C=S.next()).done)break;T=C.value}var j=T.id.name;n.bindings[j]&&(n.bindings[j].kind=w.kind)}}}1===y.length?e.replaceWith(y[0]):e.replaceWithMultiple(y)}}}}})),OB=sP((function(e){return e.assertVersion(7),D_({name:"transform-dotall-regex",feature:"dotAllFlag"})}));var NB=sP((function(e){return e.assertVersion(7),{name:"transform-duplicate-keys",visitor:{ObjectExpression:function(e){var t,r=e.node.properties.filter((function(e){return!le(e)&&!e.computed})),n=Object.create(null),a=Object.create(null),s=Object.create(null),i=r,o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}var l=c,p=S(t=l.key)?t.name:t.value.toString(),d=!1;switch(l.kind){case"get":(n[p]||a[p])&&(d=!0),a[p]=!0;break;case"set":(n[p]||s[p])&&(d=!0),s[p]=!0;break;default:(n[p]||a[p]||s[p])&&(d=!0),n[p]=!0}d&&(l.computed=!0,l.key=to(p))}}}}}));function RB(e,t,r,n,a){var s,i,o;if(s=S(e)&&a?e:function(e,t,r,n){var a;if(pe(e))return e;if(S(e)){if(n.hasBinding(e.name))return e;a=e}else{if(!_(e))throw new Error("We can't explode this node type "+e.type);if(pe(a=e.object)||S(a)&&n.hasBinding(a.name))return a}var s=n.generateUidIdentifierBasedOnNode(a);return n.push({id:s}),t.push(_i("=",pp(s),pp(a))),s}(e,t,0,n),S(e))i=pp(e),o=s;else{var u=function(e,t,r,n){var a=e.property,s=_d(e,a);if(ft(s)&&pt(s))return s;var i=n.generateUidIdentifierBasedOnNode(a);return n.push({id:i}),t.push(_i("=",pp(i),pp(a))),i}(e,t,0,n),c=e.computed||ft(u);o=oo(pp(s),pp(u),c),i=oo(pp(s),pp(u),c)}return{uid:o,ref:i}}function MB(e){var t=e.build,r=e.operator;return{AssignmentExpression:function(e){var n=e.node,a=e.scope;if(n.operator===r+"="){var s=[],i=RB(n.left,s,0,a);s.push(_i("=",i.ref,t(i.uid,n.right))),e.replaceWith(yo(s))}},BinaryExpression:function(e){var n=e.node;n.operator===r&&e.replaceWith(t(n.left,n.right))}}}var LB=sP((function(e){return e.assertVersion(7),{name:"transform-exponentiation-operator",visitor:MB({operator:"**",build:function(e,t){return Li(oo(Qi("Math"),Qi("pow")),[e,t])}})}})),UB=sP((function(e){function t(e){return"string"==typeof e?{type:"CommentBlock",value:e}:e}function r(e){var r=e.ofPath,n=e.toPath,s=e.where,i=void 0===s?"trailing":s,o=e.optional,u=void 0!==o&&o,c=e.comments,l=void 0===c?a(r,u):c,p=e.keepType,d=void 0!==p&&p;if(n&&n.node||(n=r.getPrevSibling(),i="trailing"),n.node||(n=r.getNextSibling(),i="leading"),n.node||(n=r.parentPath,i="inner"),Array.isArray(l)||(l=[l]),l=l.map(t),!d&&r&&r.node){var f=r.node,h=r.parentPath,m=r.getPrevSibling(),y=r.getNextSibling(),g=!(m.node||y.node),v=f.leadingComments,b=f.trailingComments;g&&v&&h.addComments("inner",v),n.addComments(i,l),r.remove(),g&&b&&h.addComments("inner",b)}else n.addComments(i,l)}function n(e){r({ofPath:e,comments:a(e,e.parent.optional)})}function a(e,t){var r=e.getSource().replace(/\*-\//g,"*-ESCAPED/").replace(/\*\//g,"*-/");return t&&(r="?"+r),":"!==r[0]&&(r=":: "+r),r}function s(e){return"type"===e||"typeof"===e}return e.assertVersion(7),{name:"transform-flow-comments",inherits:gP,visitor:{TypeCastExpression:function(e){var t=e.node;r({ofPath:e.get("typeAnnotation"),toPath:e.get("expression"),keepType:!0}),e.replaceWith(go(t.expression))},Identifier:function(e){if(!e.parentPath.isFlow()){var t=e.node;t.typeAnnotation?(r({ofPath:e.get("typeAnnotation"),toPath:e,optional:t.optional||t.typeAnnotation.optional}),t.optional&&(t.optional=!1)):t.optional&&(r({toPath:e,comments:":: ?"}),t.optional=!1)}},AssignmentPattern:{exit:function(e){var t=e.node.left;t.optional&&(t.optional=!1)}},Function:function(e){if(!e.isDeclareFunction()){var t=e.node;t.typeParameters&&r({ofPath:e.get("typeParameters"),toPath:e.get("id"),optional:t.typeParameters.optional}),t.returnType&&r({ofPath:e.get("returnType"),toPath:e.get("body"),where:"leading",optional:t.returnType.typeAnnotation.optional})}},ClassProperty:function(e){var t=e.node;t.value?t.typeAnnotation&&r({ofPath:e.get("typeAnnotation"),toPath:e.get("key"),optional:t.typeAnnotation.optional}):n(e)},ExportNamedDeclaration:function(e){var t=e.node;("type"===t.exportKind||Et(t.declaration))&&n(e)},ImportDeclaration:function(e){var t=e.node;if(s(t.importKind))n(e);else{var a=t.specifiers.filter((function(e){return s(e.importKind)})),i=t.specifiers.filter((function(e){return!s(e.importKind)}));if(t.specifiers=i,a.length>0){var o=pp(t);o.specifiers=a;var u=":: "+lv(o).code;i.length>0?r({toPath:e,comments:u}):r({ofPath:e,comments:u})}}},ObjectPattern:function(e){var t=e.node;t.typeAnnotation&&r({ofPath:e.get("typeAnnotation"),toPath:e,optional:t.optional||t.typeAnnotation.optional})},Flow:function(e){n(e)},Class:function(e){var t=e.node,n=[];if(t.typeParameters){var s=e.get("typeParameters");n.push(a(s,t.typeParameters.optional));var i,o=t.typeParameters.trailingComments;if(o)(i=n).push.apply(i,o);s.remove()}if(t.superClass&&(n.length>0&&(r({toPath:e.get("id"),comments:n}),n=[]),t.superTypeParameters)){var u=e.get("superTypeParameters");n.push(a(u,u.node.optional)),u.remove()}if(t.implements){var c="implements "+e.get("implements").map((function(e){return a(e).replace(/^:: /,"")})).join(", ");delete t.implements,1===n.length?n[0]+=" "+c:n.push(":: "+c)}n.length>0&&r({toPath:e.get("body"),where:"leading",comments:n})}}}})),GB=sP((function(e){e.assertVersion(7);var t=/(@flow(\s+(strict(-local)?|weak))?|@noflow)/,r=!1;return{name:"transform-flow-strip-types",inherits:gP,visitor:{Program:function(e,n){var a=n.file.ast.comments,s=n.opts;r=!1;var i=!1;if(a)for(var o=0,u=a;o<u.length;o++){var c=u[o];t.test(c.value)&&(i=!0,c.value=c.value.replace(t,""),c.value.replace(/\*/g,"").trim()||(c.ignore=!0))}!i&&s.requireDirective&&(r=!0)},ImportDeclaration:function(e){if(!r&&e.node.specifiers.length){var t=0;e.node.specifiers.forEach((function(e){var r=e.importKind;"type"!==r&&"typeof"!==r||t++})),t===e.node.specifiers.length&&e.remove()}},Flow:function(e){if(r)throw e.buildCodeFrameError("A @flow directive is required when using Flow annotations with the `requireDirective` option.");e.remove()},ClassProperty:function(e){r||(e.node.variance=null,e.node.typeAnnotation=null,e.node.value||e.remove())},ClassPrivateProperty:function(e){r||(e.node.typeAnnotation=null)},Class:function(e){r||(e.node.implements=null,e.get("body.body").forEach((function(e){e.isClassProperty()&&(e.node.typeAnnotation=null,e.node.value||e.remove())})))},AssignmentPattern:function(e){var t=e.node;r||(t.left.optional=!1)},Function:function(e){var t=e.node;if(!r){for(var n=0;n<t.params.length;n++){var a=t.params[n];a.optional=!1,"AssignmentPattern"===a.type&&(a.left.optional=!1)}t.predicate=null}},TypeCastExpression:function(e){if(!r){var t=e.node;do{t=t.expression}while(Te(t));e.replaceWith(t)}},CallExpression:function(e){var t=e.node;r||(t.typeArguments=null)},OptionalCallExpression:function(e){var t=e.node;r||(t.typeArguments=null)},NewExpression:function(e){var t=e.node;r||(t.typeArguments=null)}}}})),VB=sP((function(e,t){e.assertVersion(7);var r=t.loose,n=t.assumeArray;if(!0===r&&!0===n)throw new Error("The loose and assumeArray options cannot be used together in @babel/plugin-transform-for-of");if(n)return{name:"transform-for-of",visitor:{ForOfStatement:function(e){var t=e.scope,r=e.node,n=r.left,a=r.right;if(!r.await){var s=t.generateUidIdentifier("i"),i=t.maybeGenerateMemoised(a,!0),o=[Co(s,ro(0))];i?o.push(Co(i,a)):i=a;var u,c,l=oo(pp(i),pp(s),!0);q(n)?(u=n).declarations[0].init=l:u=Ki(_i("=",n,l));var p=e.get("body");p.isBlockStatement()&&Object.keys(e.getBindingIdentifiers()).some((function(e){return p.scope.hasOwnBinding(e)}))?c=Ri([u,p.node]):(c=Td(p.node)).body.unshift(u),e.replaceWith(Yi(Do("let",o),Ii("<",pp(s),oo(pp(i),Qi("length"))),So("++",pp(s)),c))}}}};var a=r?function(e,t){var r,n,a,s=e.node,o=e.scope,u=e.parent,c=s.left;if(S(c)||yt(c)||_(c))n=c,a=null;else{if(!q(c))throw t.buildCodeFrameError(c,"Unknown node type "+c.type+" in ForStatement");n=o.generateUidIdentifier("ref"),r=Do(c.kind,[Co(c.declarations[0].id,Qi(n.name))]),a=Do("var",[Co(Qi(n.name))])}var l,p=o.generateUidIdentifier("iterator"),d=o.generateUidIdentifier("isArray"),f=i({LOOP_OBJECT:p,IS_ARRAY:d,OBJECT:s.right,INDEX:o.generateUidIdentifier("i"),ID:n,INTERMEDIATE:a}),h=C(u);h&&(l=eo(u.label,f));return{replaceParent:h,declar:r,node:l||f,loop:f}}:function(e,t){var r,n=e.node,a=e.scope,s=e.parent,i=n.left,u=a.generateUid("step"),c=oo(Qi(u),Qi("value"));if(S(i)||yt(i)||_(i))r=Ki(_i("=",i,c));else{if(!q(i))throw t.buildCodeFrameError(i,"Unknown node type "+i.type+" in ForStatement");r=Do(i.kind,[Co(i.declarations[0].id,c)])}var l=o({ITERATOR_HAD_ERROR_KEY:a.generateUidIdentifier("didIteratorError"),ITERATOR_COMPLETION:a.generateUidIdentifier("iteratorNormalCompletion"),ITERATOR_ERROR_KEY:a.generateUidIdentifier("iteratorError"),ITERATOR_KEY:a.generateUidIdentifier("iterator"),STEP_KEY:Qi(u),OBJECT:n.right}),p=C(s),d=l[3].block.body,f=d[0];p&&(d[0]=eo(s.label,f));return{replaceParent:p,declar:r,loop:f,node:l}},s=dE("\n for (var KEY = 0, NAME = ARR; KEY < NAME.length; KEY++) BODY;\n "),i=dE("\n for (var LOOP_OBJECT = OBJECT,\n IS_ARRAY = Array.isArray(LOOP_OBJECT),\n INDEX = 0,\n LOOP_OBJECT = IS_ARRAY ? LOOP_OBJECT : LOOP_OBJECT[Symbol.iterator]();;) {\n INTERMEDIATE;\n if (IS_ARRAY) {\n if (INDEX >= LOOP_OBJECT.length) break;\n ID = LOOP_OBJECT[INDEX++];\n } else {\n INDEX = LOOP_OBJECT.next();\n if (INDEX.done) break;\n ID = INDEX.value;\n }\n }\n "),o=dE("\n var ITERATOR_COMPLETION = true;\n var ITERATOR_HAD_ERROR_KEY = false;\n var ITERATOR_ERROR_KEY = undefined;\n try {\n for (\n var ITERATOR_KEY = OBJECT[Symbol.iterator](), STEP_KEY;\n !(ITERATOR_COMPLETION = (STEP_KEY = ITERATOR_KEY.next()).done);\n ITERATOR_COMPLETION = true\n ) {}\n } catch (err) {\n ITERATOR_HAD_ERROR_KEY = true;\n ITERATOR_ERROR_KEY = err;\n } finally {\n try {\n if (!ITERATOR_COMPLETION && ITERATOR_KEY.return != null) {\n ITERATOR_KEY.return();\n }\n } finally {\n if (ITERATOR_HAD_ERROR_KEY) {\n throw ITERATOR_ERROR_KEY;\n }\n }\n }\n ");function u(e){var t=e.node,r=e.scope,n=r.generateUidIdentifierBasedOnNode(t.right,"arr"),a=r.generateUidIdentifier("i"),i=s({BODY:t.body,KEY:a,NAME:n,ARR:t.right});ef(i,t),jd(i);var o=oo(pp(n),pp(a),!0),u=t.left;return q(u)?(u.declarations[0].init=o,i.body.body.unshift(u)):i.body.body.unshift(Ki(_i("=",u,o))),e.parentPath.isLabeledStatement()&&(i=eo(e.parentPath.node.label,i)),[i]}return{name:"transform-for-of",visitor:{ForOfStatement:function(e,t){var r=e.get("right");if(r.isArrayExpression()||r.isGenericType("Array")||ye(r.getTypeAnnotation()))!function(e){e.parentPath.isLabeledStatement()?e.parentPath.replaceWithMultiple(u(e)):e.replaceWithMultiple(u(e))}(e);else{var n=e.node,s=a(e,t),i=s.declar,o=s.loop,c=o.body;e.ensureBlock(),i&&c.body.push(i),c.body=c.body.concat(n.body.body),ef(o,n),ef(o.body,n.body),s.replaceParent?(e.parentPath.replaceWithMultiple(s.node),e.remove()):e.replaceWithMultiple(s.node)}}}}})),WB=sP((function(e){return e.assertVersion(7),{name:"transform-function-name",visitor:{FunctionExpression:{exit:function(e){if("value"!==e.key&&!e.parentPath.isObjectProperty()){var t=gE(e);t&&e.replaceWith(t)}}},ObjectProperty:function(e){var t=e.get("value");if(t.isFunction()){var r=gE(t);r&&t.replaceWith(r)}}}}})),HB=sP((function(e){return e.assertVersion(7),{name:"transform-instanceof",visitor:{BinaryExpression:function(e){var t=e.node;if("instanceof"===t.operator){var r=this.addHelper("instanceof");if(e.findParent((function(e){return e.isVariableDeclarator()&&e.node.id===r||e.isFunctionDeclaration()&&e.node.id&&e.node.id.name===r.name})))return;e.replaceWith(Li(r,[t.left,t.right]))}}}}})),qB=sP((function(e){return e.assertVersion(7),{name:"transform-jscript",visitor:{FunctionExpression:{exit:function(e){var t=e.node;t.id&&e.replaceWith(Li($i(null,[],Ri([Vd(t),mo(pp(t.id))])),[]))}}}}})),KB=sP((function(e){return e.assertVersion(7),{name:"transform-literals",visitor:{NumericLiteral:function(e){var t=e.node;t.extra&&/^0[ob]/i.test(t.extra.raw)&&(t.extra=void 0)},StringLiteral:function(e){var t=e.node;t.extra&&/\\[u]/gi.test(t.extra.raw)&&(t.extra=void 0)}}}})),zB=sP((function(e){return e.assertVersion(7),{name:"transform-member-expression-literals",visitor:{MemberExpression:{exit:function(e){var t=e.node,r=t.property;t.computed||!S(r)||lf(r.name)||(t.property=to(r.name),t.computed=!0)}}}}}));var XB=function(e,t,r){var n=-1,a=e.length;t<0&&(t=-t>a?0:a+t),(r=r>a?a:r)<0&&(r+=a),a=t>r?0:r-t>>>0,t>>>=0;for(var s=Array(a);++n<a;)s[n]=e[n+t];return s},YB=Math.ceil,JB=Math.max;var $B=function(e,t,r){t=(r?Jf(e,t,r):void 0===t)?1:JB(Vf(t),0);var n=null==e?0:e.length;if(!n||t<1)return[];for(var a=0,s=0,i=Array(YB(n/t));a<n;)i[s++]=XB(e,a,a+=t);return i};function QB(e){e.traverse(ZB)}var ZB={ThisExpression:function(e){e.replaceWith(e.scope.buildUndefinedNode())},Function:function(e){e.isMethod()?HP(e):e.isArrowFunctionExpression()||e.skip()},ClassProperty:function(e){HP(e)},ClassPrivateProperty:function(e){e.skip()}};function eO(e,t){e.traverse(tO,{scope:e.scope,bindingNames:t,seen:new WeakSet})}var tO={UpdateExpression:{exit:function(e){var t=this.scope,r=this.bindingNames,n=e.get("argument");if(n.isIdentifier()){var a=n.node.name;if(r.has(a)&&t.getBinding(a)===e.scope.getBinding(a))if(e.parentPath.isExpressionStatement()&&!e.isCompletionRecord()){var s="++"==e.node.operator?"+=":"-=";e.replaceWith(_i(s,n.node,ro(1)))}else if(e.node.prefix)e.replaceWith(_i("=",Qi(a),Ii(e.node.operator[0],wo("+",n.node),ro(1))));else{var i=e.scope.generateUidIdentifierBasedOnNode(n.node,"old"),o=i.name;e.scope.push({id:i});var u=Ii(e.node.operator[0],Qi(o),ro(1));e.replaceWith(yo([_i("=",Qi(o),wo("+",n.node)),_i("=",pp(n.node),u),Qi(o)]))}}}},AssignmentExpression:{exit:function(e){var t=this.scope,r=this.seen,n=this.bindingNames;if("="!==e.node.operator&&!r.has(e.node)){r.add(e.node);var a=e.get("left");if(a.isIdentifier()){var s=a.node.name;n.has(s)&&t.getBinding(s)===e.scope.getBinding(s)&&(e.node.right=Ii(e.node.operator.slice(0,-1),pp(e.node.left),e.node.right),e.node.operator="=")}}}}};function rO(){var e=i(["\n (function() {\n throw new Error('\"' + '","' + '\" is read-only.');\n })()\n "]);return rO=function(){return e},e}var nO={Scope:function(e){e.skip()},ClassDeclaration:function(e){var t=this.requeueInParent,r=this.exported,n=this.metadata,a=e.node.id;if(!a)throw new Error("Expected class to have a name");var s=a.name,i=r.get(s)||[];if(i.length>0){var o=Ki(aO(n,i,Qi(s)));o._blockHoist=e.node._blockHoist,t(e.insertAfter(o)[0])}},VariableDeclaration:function(e){var t=this.requeueInParent,r=this.exported,n=this.metadata;Object.keys(e.getOuterBindingIdentifiers()).forEach((function(a){var s=r.get(a)||[];if(s.length>0){var i=Ki(aO(n,s,Qi(a)));i._blockHoist=e.node._blockHoist,t(e.insertAfter(i)[0])}}))}},aO=function(e,t,r){return(t||[]).reduce((function(t,r){return _i("=",oo(Qi(e.exportName),Qi(r)),t)}),r)},sO=function(e){return dE.expression.ast(rO(),e)},iO={ReferencedIdentifier:function(e){var t=this.seen,r=this.buildImportReference,n=this.scope,a=this.imported,s=this.requeueInParent;if(!t.has(e.node)){t.add(e.node);var i=e.node.name,o=e.scope.getBinding(i);if(n.getBinding(i)===o){var u=a.get(i);if(u){var c=r(u,e.node);if(c.loc=e.node.loc,(e.parentPath.isCallExpression({callee:e.node})||e.parentPath.isOptionalCallExpression({callee:e.node})||e.parentPath.isTaggedTemplateExpression({tag:e.node}))&&_(c))e.replaceWith(yo([ro(0),c]));else if(e.isJSXIdentifier()&&_(c)){var l=c.object,p=c.property;e.replaceWith(hc(fc(l.name),fc(p.name)))}else e.replaceWith(c);s(e),e.skip()}}}},AssignmentExpression:{exit:function(e){var t=this,r=this.scope,n=this.seen,a=this.imported,s=this.exported,i=this.requeueInParent,o=this.buildImportReference;if(!n.has(e.node)){n.add(e.node);var u=e.get("left");if(!u.isMemberExpression())if(u.isIdentifier()){var c=u.node.name;if(r.getBinding(c)!==e.scope.getBinding(c))return;var l=s.get(c),p=a.get(c);if((null==l?void 0:l.length)>0||p){aI("="===e.node.operator,"Path was not simplified");var d=e.node;p&&(d.left=o(p,d.left),d.right=yo([d.right,sO(c)])),e.replaceWith(aO(this.metadata,l,d)),i(e)}}else{var f=u.getOuterBindingIdentifiers(),h=Object.keys(f).filter((function(t){return r.getBinding(t)===e.scope.getBinding(t)})),m=h.find((function(e){return a.has(e)}));m&&(e.node.right=yo([e.node.right,sO(m)]));var y=[];if(h.forEach((function(e){var r=s.get(e)||[];r.length>0&&y.push(aO(t.metadata,r,Qi(e)))})),y.length>0){var g=yo(y);e.parentPath.isExpressionStatement()&&((g=Ki(g))._blockHoist=e.parentPath.node._blockHoist),i(e.insertAfter(g)[0])}}}}},"ForOfStatement|ForInStatement":function(e){var t=e.scope,r=e.node.left,n=this.exported,a=this.scope;if(!q(r)){for(var s=!1,i=e.get("body"),o=i.scope,u=0,c=Object.keys(tf(r));u<c.length;u++){var l=c[u];n.get(l)&&a.getBinding(l)===t.getBinding(l)&&(s=!0,o.hasOwnBinding(l)&&o.rename(l))}if(!s)return;var p=t.generateUidIdentifierBasedOnNode(r);i.unshiftContainer("body",Ki(_i("=",r,p))),e.get("left").replaceWith(Do("let",[Co(p)])),t.registerDeclaration(e.get("left"))}}};function oO(e){return e.hasExports}function uO(e){return 0===e.imports.size&&0===e.importsNamespace.size&&0===e.reexports.size&&0===e.reexportNamespace.size&&!e.reexportAll}function cO(e,t,r){var n=void 0===r?{}:r,a=n.noInterop,s=void 0!==a&&a,i=n.loose,o=void 0!==i&&i,u=n.lazy,c=void 0!==u&&u,l=n.esNamespaceOnly,p=void 0!==l&&l;t||(t=e.scope.generateUidIdentifier("exports").name),function(e){e.get("body").forEach((function(e){e.isExportDefaultDeclaration()&&nh(e)}))}(e);var d=function(e,t){var r=t.loose,n=t.lazy,a=function(e,t){var r=new Map;e.get("body").forEach((function(e){var n;if(e.isImportDeclaration())n="import";else{if(e.isExportDefaultDeclaration()&&(e=e.get("declaration")),e.isExportNamedDeclaration())if(e.node.declaration)e=e.get("declaration");else if(t&&e.node.source&&e.get("source").isStringLiteral())return void e.node.specifiers.forEach((function(e){r.set(e.local.name,"block")}));if(e.isFunctionDeclaration())n="hoisted";else if(e.isClassDeclaration())n="block";else if(e.isVariableDeclaration({kind:"var"}))n="var";else{if(!e.isVariableDeclaration())return;n="block"}}Object.keys(e.getOuterBindingIdentifiers()).forEach((function(e){r.set(e,n)}))}));var n=new Map,a=function(e){var t=e.node.name,a=n.get(t);if(!a){var s=r.get(t);if(void 0===s)throw e.buildCodeFrameError('Exporting local "'+t+'", which is not declared.');a={names:[],kind:s},n.set(t,a)}return a};return e.get("body").forEach((function(e){if(!e.isExportNamedDeclaration()||!t&&e.node.source){if(e.isExportDefaultDeclaration()){var r=e.get("declaration");if(!r.isFunctionDeclaration()&&!r.isClassDeclaration())throw r.buildCodeFrameError("Unexpected default expression export.");a(r.get("id")).names.push("default")}}else if(e.node.declaration){var n=e.get("declaration"),s=n.getOuterBindingIdentifierPaths();Object.keys(s).forEach((function(e){if("__esModule"===e)throw n.buildCodeFrameError('Illegal export "__esModule".');a(s[e]).names.push(e)}))}else e.get("specifiers").forEach((function(e){var t=e.get("local"),r=e.get("exported");if("__esModule"===r.node.name)throw r.buildCodeFrameError('Illegal export "__esModule".');a(t).names.push(r.node.name)}))})),n}(e,r),s=new Map,i=function(t){var r=t.value,n=s.get(r);return n||(n={name:e.scope.generateUidIdentifier(hS(r,mS(r))).name,interop:"none",loc:null,imports:new Map,importsNamespace:new Set,reexports:new Map,reexportNamespace:new Set,reexportAll:null,lazy:!1},s.set(r,n)),n},o=!1;e.get("body").forEach((function(e){if(e.isImportDeclaration()){var t=i(e.node.source);t.loc||(t.loc=e.node.loc),e.get("specifiers").forEach((function(e){if(e.isImportDefaultSpecifier()){var r=e.get("local").node.name;t.imports.set(r,"default");var n=a.get(r);n&&(a.delete(r),n.names.forEach((function(e){t.reexports.set(e,"default")})))}else if(e.isImportNamespaceSpecifier()){var s=e.get("local").node.name;t.importsNamespace.add(s);var i=a.get(s);i&&(a.delete(s),i.names.forEach((function(e){t.reexportNamespace.add(e)})))}else if(e.isImportSpecifier()){var o=e.get("imported").node.name,u=e.get("local").node.name;t.imports.set(u,o);var c=a.get(u);c&&(a.delete(u),c.names.forEach((function(e){t.reexports.set(e,o)})))}}))}else if(e.isExportAllDeclaration()){o=!0;var r=i(e.node.source);r.loc||(r.loc=e.node.loc),r.reexportAll={loc:e.node.loc}}else if(e.isExportNamedDeclaration()&&e.node.source){o=!0;var n=i(e.node.source);n.loc||(n.loc=e.node.loc),e.get("specifiers").forEach((function(e){if(!e.isExportSpecifier())throw e.buildCodeFrameError("Unexpected export specifier type");var t=e.get("local").node.name,r=e.get("exported").node.name;if(n.reexports.set(r,t),"__esModule"===r)throw r.buildCodeFrameError('Illegal export "__esModule".')}))}else(e.isExportNamedDeclaration()||e.isExportDefaultDeclaration())&&(o=!0)}));var u=s.values(),c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}var d=p,f=!1,h=!1;d.importsNamespace.size>0&&(f=!0,h=!0),d.reexportAll&&(h=!0);var m=d.imports.values(),y=Array.isArray(m),g=0;for(m=y?m:m[Symbol.iterator]();;){var v;if(y){if(g>=m.length)break;v=m[g++]}else{if((g=m.next()).done)break;v=g.value}"default"===v?f=!0:h=!0}var b=d.reexports.values(),x=Array.isArray(b),E=0;for(b=x?b:b[Symbol.iterator]();;){var A;if(x){if(E>=b.length)break;A=b[E++]}else{if((E=b.next()).done)break;A=E.value}"default"===A?f=!0:h=!0}f&&h?d.interop="namespace":f&&(d.interop="default")}var w=s,S=Array.isArray(w),D=0;for(w=S?w:w[Symbol.iterator]();;){var C;if(S){if(D>=w.length)break;C=w[D++]}else{if((D=w.next()).done)break;C=D.value}var T=C,j=T[0],P=T[1];if(!1!==n&&!uO(P)&&!P.reexportAll)if(!0===n)P.lazy=!/\./.test(j);else if(Array.isArray(n))P.lazy=-1!==n.indexOf(j);else{if("function"!=typeof n)throw new Error(".lazy must be a boolean, string array, or function");P.lazy=n(j)}}return{hasExports:o,local:a,source:s}}(e,{loose:o,lazy:c}),f=d.local,h=d.source,m=d.hasExports;!function(e){e.get("body").forEach((function(e){if(e.isImportDeclaration())e.remove();else if(e.isExportNamedDeclaration())e.node.declaration?(e.node.declaration._blockHoist=e.node._blockHoist,e.replaceWith(e.node.declaration)):e.remove();else if(e.isExportDefaultDeclaration()){var t=e.get("declaration");if(!t.isFunctionDeclaration()&&!t.isClassDeclaration())throw t.buildCodeFrameError("Unexpected default expression export.");t._blockHoist=e.node._blockHoist,e.replaceWith(t)}else e.isExportAllDeclaration()&&e.remove()}))}(e);var y=h,g=Array.isArray(y),v=0;for(y=g?y:y[Symbol.iterator]();;){var b;if(g){if(v>=y.length)break;b=y[v++]}else{if((v=y.next()).done)break;b=v.value}var x=b[1];x.importsNamespace.size>0&&(x.name=x.importsNamespace.values().next().value),s?x.interop="none":p&&"namespace"===x.interop&&(x.interop="default")}return{exportName:t,exportNameListName:null,hasExports:m,local:f,source:h}}function lO(){var e=i(["EXPORTS.NAME = VALUE"]);return lO=function(){return e},e}function pO(){var e=i(["\n if (Object.prototype.hasOwnProperty.call(EXPORTS_LIST, key)) return;\n "]);return pO=function(){return e},e}function dO(){var e=i(['\n Object.keys(NAMESPACE).forEach(function(key) {\n if (key === "default" || key === "__esModule") return;\n VERIFY_NAME_LIST;\n\n Object.defineProperty(EXPORTS, key, {\n enumerable: true,\n get: function() {\n return NAMESPACE[key];\n },\n });\n });\n ']);return dO=function(){return e},e}function fO(){var e=i(['\n Object.keys(NAMESPACE).forEach(function(key) {\n if (key === "default" || key === "__esModule") return;\n VERIFY_NAME_LIST;\n\n EXPORTS[key] = NAMESPACE[key];\n });\n ']);return fO=function(){return e},e}function hO(){var e=i(['\n Object.defineProperty(EXPORTS, "__esModule", {\n value: true,\n });\n ']);return hO=function(){return e},e}function mO(){var e=i(["\n EXPORTS.__esModule = true;\n "]);return mO=function(){return e},e}function yO(){var e=i(['\n Object.defineProperty(EXPORTS, "EXPORT_NAME", {\n enumerable: true,\n get: function() {\n return NAMESPACE.IMPORT_NAME;\n },\n });\n ']);return yO=function(){return e},e}function gO(){var e=i(["EXPORTS.EXPORT_NAME = NAMESPACE.IMPORT_NAME;"]);return gO=function(){return e},e}function vO(){var e=i(["EXPORTS.NAME = NAMESPACE;"]);return vO=function(){return e},e}function bO(){var e=i(['\n Object.defineProperty(EXPORTS, "NAME", {\n enumerable: true,\n get: function() {\n return NAMESPACE;\n }\n });\n ']);return bO=function(){return e},e}function xO(){var e=i(["var NAME = SOURCE;"]);return xO=function(){return e},e}function EO(e,t){var r=t.exportName,n=t.strict,a=t.allowTopLevelThis,s=t.strictMode,i=t.loose,o=t.noInterop,u=t.lazy,c=t.esNamespaceOnly;aI(gI(e),"Cannot process module statements in a script"),e.node.sourceType="script";var l=cO(e,r,{noInterop:o,loose:i,lazy:u,esNamespaceOnly:c});(a||QB(e),function(e,t){var r=new Map,n=new Map,a=function(t){e.requeue(t)},s=t.source,i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u,l=c[0],p=c[1],d=p.imports,f=Array.isArray(d),h=0;for(d=f?d:d[Symbol.iterator]();;){var m;if(f){if(h>=d.length)break;m=d[h++]}else{if((h=d.next()).done)break;m=h.value}var y=m,g=y[0],v=y[1];r.set(g,[l,v,null])}var b=p.importsNamespace,x=Array.isArray(b),E=0;for(b=x?b:b[Symbol.iterator]();;){var A;if(x){if(E>=b.length)break;A=b[E++]}else{if((E=b.next()).done)break;A=E.value}var w=A;r.set(w,[l,null,w])}}var S=t.local,D=Array.isArray(S),C=0;for(S=D?S:S[Symbol.iterator]();;){var T,j;if(D){if(C>=S.length)break;j=S[C++]}else{if((C=S.next()).done)break;j=C.value}var P=j,k=P[0],F=P[1],_=n.get(k);_||(_=[],n.set(k,_)),(T=_).push.apply(T,F.names)}e.traverse(nO,{metadata:t,requeueInParent:a,scope:e.scope,exported:n}),eO(e,new Set([].concat(Array.from(r.keys()),Array.from(n.keys())))),e.traverse(iO,{seen:new WeakSet,metadata:t,requeueInParent:a,scope:e.scope,imported:r,exported:n,buildImportReference:function(e,r){var n=e[0],a=e[1],s=e[2],i=t.source.get(n);if(s)return i.lazy&&(r=Li(r,[])),r;var o=Qi(i.name);return i.lazy&&(o=Li(o,[])),oo(o,Qi(a))}})}(e,l),!1!==s)&&(e.node.directives.some((function(e){return"use strict"===e.value.value}))||e.unshiftContainer("directives",Oi(Ni("use strict"))));var p=[];oO(l)&&!n&&p.push(function(e,t){void 0===t&&(t=!1);return(t?dE.statement(mO()):dE.statement(hO()))({EXPORTS:e.exportName})}(l,i));var d=function(e,t){var r=Object.create(null),n=t.local.values(),a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i.names,u=Array.isArray(o),c=0;for(o=u?o:o[Symbol.iterator]();;){var l;if(u){if(c>=o.length)break;l=o[c++]}else{if((c=o.next()).done)break;l=c.value}r[l]=!0}}var p=!1,d=t.source.values(),f=Array.isArray(d),h=0;for(d=f?d:d[Symbol.iterator]();;){var m;if(f){if(h>=d.length)break;m=d[h++]}else{if((h=d.next()).done)break;m=h.value}var y=m,g=y.reexports.keys(),v=Array.isArray(g),b=0;for(g=v?g:g[Symbol.iterator]();;){var x;if(v){if(b>=g.length)break;x=g[b++]}else{if((b=g.next()).done)break;x=b.value}r[x]=!0}var E=y.reexportNamespace,A=Array.isArray(E),w=0;for(E=A?E:E[Symbol.iterator]();;){var S;if(A){if(w>=E.length)break;S=E[w++]}else{if((w=E.next()).done)break;S=w.value}r[S]=!0}p=p||y.reexportAll}if(!p||0===Object.keys(r).length)return null;var D=e.scope.generateUidIdentifier("exportNames");return delete r.default,{name:D.name,statement:Do("var",[Co(D,Qd(r))])}}(e,l);return d&&(l.exportNameListName=d.name,p.push(d.statement)),p.push.apply(p,function(e,t,r){void 0===r&&(r=!1);var n=[],a=[],s=t.local,i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u,l=c[0],p=c[1];"import"===p.kind||("hoisted"===p.kind?n.push(CO(t,p.names,Qi(l))):a.push.apply(a,p.names))}var d=t.source.values(),f=Array.isArray(d),h=0;for(d=f?d:d[Symbol.iterator]();;){var m;if(f){if(h>=d.length)break;m=d[h++]}else{if((h=d.next()).done)break;m=h.value}var y=m;r||n.push.apply(n,DO(t,y,r));var g=y.reexportNamespace,v=Array.isArray(g),b=0;for(g=v?g:g[Symbol.iterator]();;){var x;if(v){if(b>=g.length)break;x=g[b++]}else{if((b=g.next()).done)break;x=b.value}var E=x;a.push(E)}}return n.push.apply(n,$B(a,100).map((function(r){return CO(t,r,e.scope.buildUndefinedNode())}))),n}(e,l,i)),{meta:l,headers:p}}function AO(e){e.forEach((function(e){e._blockHoist=3}))}function wO(e,t,r){if("none"===r)return null;var n;if("default"===r)n="interopRequireDefault";else{if("namespace"!==r)throw new Error("Unknown interop: "+r);n="interopRequireWildcard"}return Li(e.hub.addHelper(n),[t])}function SO(e,t,r){void 0===r&&(r=!1);var n=[],a=Qi(t.name);t.lazy&&(a=Li(a,[]));var s=t.importsNamespace,i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u;c!==t.name&&n.push(dE.statement(xO())({NAME:c,SOURCE:pp(a)}))}r&&n.push.apply(n,DO(e,t,r));var l=t.reexportNamespace,p=Array.isArray(l),d=0;for(l=p?l:l[Symbol.iterator]();;){var f;if(p){if(d>=l.length)break;f=l[d++]}else{if((d=l.next()).done)break;f=d.value}var h=f;n.push((t.lazy?dE.statement(bO()):dE.statement(vO()))({EXPORTS:e.exportName,NAME:h,NAMESPACE:pp(a)}))}if(t.reexportAll){var m=function(e,t,r){return(r?dE.statement(fO()):dE.statement(dO()))({NAMESPACE:t,EXPORTS:e.exportName,VERIFY_NAME_LIST:e.exportNameListName?dE(pO())({EXPORTS_LIST:e.exportNameListName}):null})}(e,pp(a),r);m.loc=t.reexportAll.loc,n.push(m)}return n}var DO=function(e,t,r){var n=t.lazy?Li(Qi(t.name),[]):Qi(t.name),a=function(e){return e?dE.statement(gO()):dE(yO())}(r);return Array.from(t.reexports,(function(t){var r=t[0],s=t[1];return a({EXPORTS:e.exportName,EXPORT_NAME:r,NAMESPACE:pp(n),IMPORT_NAME:s})}))};function CO(e,t,r){return Ki(t.reduce((function(t,r){return dE.expression(lO())({EXPORTS:e.exportName,NAME:r,VALUE:t})}),r))}var TO=Vt((function(e,t){function r(e,t){return function(e){if(Array.isArray(e))return e}(e)||function(e,t){var r=[],n=!0,a=!1,s=void 0;try{for(var i,o=e[Symbol.iterator]();!(n=(i=o.next()).done)&&(r.push(i.value),!t||r.length!==t);n=!0);}catch(e){a=!0,s=e}finally{try{n||null==o.return||o.return()}finally{if(a)throw s}}return r}(e,t)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}()}function n(e,t){var n=t.arguments,a=r(n,1)[0];return e.isStringLiteral(a)||e.isTemplateLiteral(a)?(e.removeComments(a),a):e.templateLiteral([e.templateElement({raw:"",cooked:""}),e.templateElement({raw:"",cooked:""},!0)],n)}Object.defineProperty(t,"__esModule",{value:!0}),t.getImportSource=n,t.createDynamicImportTransform=function(e){var t=e.template,r=e.types,a=t("Promise.resolve().then(() => MODULE)");return function(e,t){var s=r.callExpression(r.identifier("require"),[n(r,t.parent)]),i=e.opts.noInterop,o=!0===(void 0!==i&&i)?s:r.callExpression(e.addHelper("interopRequireWildcard"),[s]),u=a({MODULE:o});t.parentPath.replaceWith(u)}}}));Gt(TO);TO.getImportSource,TO.createDynamicImportTransform;var jO=TO,PO=jO.createDynamicImportTransform,kO=jO.getImportSource;function FO(){var e=i(["\n new Promise((",", ",") =>\n ","(\n [","],\n imported => ","(","),\n ","\n )\n )"]);return FO=function(){return e},e}var _O=dE("\n define(MODULE_NAME, AMD_ARGUMENTS, function(IMPORT_NAMES) {\n })\n"),IO=dE('\n define(["require"], function(REQUIRE) {\n })\n');function BO(e,t){var r=e.node,n=r.body,a=r.directives;e.node.directives=[],e.node.body=[];var s=e.pushContainer("body",t)[0].get("expression.arguments").filter((function(e){return e.isFunctionExpression()}))[0].get("body");s.pushContainer("directives",a),s.pushContainer("body",n)}var OO=sP((function(e,t){e.assertVersion(7);var r=t.loose,n=t.allowTopLevelThis,a=t.strict,s=t.strictMode,i=t.noInterop;return{name:"transform-modules-amd",pre:function(){this.file.set("@babel/plugin-transform-modules-*","amd")},visitor:{CallExpression:function(e,t){if(this.file.has("@babel/plugin-proposal-dynamic-import")&&e.get("callee").isImport()){var r=t.requireId,n=t.resolveId,a=t.rejectId;r||(r=e.scope.generateUidIdentifier("require"),t.requireId=r),n&&a||(n=e.scope.generateUidIdentifier("resolve"),a=e.scope.generateUidIdentifier("reject"),t.resolveId=n,t.rejectId=a);var s=Qi("imported");i||(s=wO(e,s,"namespace")),e.replaceWith(dE.expression.ast(FO(),n,a,r,kO(ff,e.node),n,s,a))}},Program:{exit:function(e,t){var o=t.requireId;if(gI(e)){var u=[],c=[];o&&(u.push(to("require")),c.push(o));var l=this.getModuleName();l&&(l=to(l));var p=EO(e,{loose:r,strict:a,strictMode:s,allowTopLevelThis:n,noInterop:i}),d=p.meta,f=p.headers;oO(d)&&(u.push(to("exports")),c.push(Qi(d.exportName)));var h=d.source,m=Array.isArray(h),y=0;for(h=m?h:h[Symbol.iterator]();;){var g;if(m){if(y>=h.length)break;g=h[y++]}else{if((y=h.next()).done)break;g=y.value}var v=g,b=v[0],x=v[1];if(u.push(to(b)),c.push(Qi(x.name)),!uO(x)){var E=wO(e,Qi(x.name),x.interop);if(E){var A=Ki(_i("=",Qi(x.name),E));A.loc=x.loc,f.push(A)}}f.push.apply(f,SO(d,x,r))}AO(f),e.unshiftContainer("body",f),BO(e,_O({MODULE_NAME:l,AMD_ARGUMENTS:Fi(u),IMPORT_NAMES:c}))}else o&&BO(e,IO({REQUIRE:o}))}}}}}));function NO(){var e=i(["\n var "," = ",";\n "]);return NO=function(){return e},e}function RO(){var e=i(["\n function ","() {\n const data = ",";\n "," = function(){ return data; };\n return data;\n }\n "]);return RO=function(){return e},e}function MO(){var e=i(['\n (function(){\n throw new Error(\n "The CommonJS \'" + "','" + "\' variable is not available in ES6 modules." +\n "Consider setting setting sourceType:script or sourceType:unambiguous in your " +\n "Babel config for this file.");\n })()\n ']);return MO=function(){return e},e}var LO=sP((function(e,t){e.assertVersion(7);var r=PO(e),n=t.loose,a=t.strictNamespace,s=void 0!==a&&a,i=t.mjsStrictNamespace,o=void 0===i||i,u=t.allowTopLevelThis,c=t.strict,l=t.strictMode,p=t.noInterop,d=t.lazy,f=void 0!==d&&d,h=t.allowCommonJSExports,m=void 0===h||h;if(!("boolean"==typeof f||"function"==typeof f||Array.isArray(f)&&f.every((function(e){return"string"==typeof e}))))throw new Error(".lazy must be a boolean, array of strings, or a function");if("boolean"!=typeof s)throw new Error(".strictNamespace must be a boolean, or undefined");if("boolean"!=typeof o)throw new Error(".mjsStrictNamespace must be a boolean, or undefined");var y=function(e){return dE.expression.ast(MO(),e)},g={ReferencedIdentifier:function(e){var t=e.node.name;if("module"===t||"exports"===t){var r=e.scope.getBinding(t);this.scope.getBinding(t)!==r||e.parentPath.isObjectProperty({value:e.node})&&e.parentPath.parentPath.isObjectPattern()||e.parentPath.isAssignmentExpression({left:e.node})||e.isAssignmentExpression({left:e.node})||e.replaceWith(y(t))}},AssignmentExpression:function(e){var t=this,r=e.get("left");if(r.isIdentifier()){var n=e.node.name;if("module"!==n&&"exports"!==n)return;var a=e.scope.getBinding(n);if(this.scope.getBinding(n)!==a)return;var s=e.get("right");s.replaceWith(yo([s.node,y(n)]))}else if(r.isPattern()){var i=r.getOuterBindingIdentifiers(),o=Object.keys(i).filter((function(r){return("module"===r||"exports"===r)&&t.scope.getBinding(r)===e.scope.getBinding(r)}))[0];if(o){var u=e.get("right");u.replaceWith(yo([u.node,y(o)]))}}}};return{name:"transform-modules-commonjs",pre:function(){this.file.set("@babel/plugin-transform-modules-*","commonjs")},visitor:{CallExpression:function(e){if(this.file.has("@babel/plugin-proposal-dynamic-import")&&e.get("callee").isImport()){var t=e.scope;do{t.rename("require")}while(t=t.parent);r(this,e.get("callee"))}},Program:{exit:function(e,t){if(gI(e)){e.scope.rename("exports"),e.scope.rename("module"),e.scope.rename("require"),e.scope.rename("__filename"),e.scope.rename("__dirname"),m||(eO(e,new Set(["module","exports"])),e.traverse(g,{scope:e.scope}));var r=this.getModuleName();r&&(r=to(r));var a=EO(e,{exportName:"exports",loose:n,strict:c,strictMode:l,allowTopLevelThis:u,noInterop:p,lazy:f,esNamespaceOnly:"string"==typeof t.filename&&/\.mjs$/.test(t.filename)?o:s}),i=a.meta,d=a.headers,h=i.source,y=Array.isArray(h),v=0;for(h=y?h:h[Symbol.iterator]();;){var b;if(y){if(v>=h.length)break;b=h[v++]}else{if((v=h.next()).done)break;b=v.value}var x=b,E=x[0],A=x[1],w=Li(Qi("require"),[to(E)]),S=void 0;if(uO(A)){if(A.lazy)throw new Error("Assertion failure");S=Ki(w)}else{var D=wO(e,w,A.interop)||w;S=A.lazy?dE.ast(RO(),A.name,D,A.name):dE.ast(NO(),A.name,D)}S.loc=A.loc,d.push(S),d.push.apply(d,SO(i,A,n))}AO(d),e.unshiftContainer("body",d)}}}}}})),UO={Scope:function(e,t){"let"===t.kind&&e.skip()},Function:function(e){e.skip()},VariableDeclaration:function(e,t){if(!t.kind||e.node.kind===t.kind){var r,n=[],a=e.get("declarations"),s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o;r=u.node.id,u.node.init&&n.push(Ki(_i("=",u.node.id,u.node.init)));for(var c=0,l=Object.keys(u.getBindingIdentifiers());c<l.length;c++){var p=l[c];t.emit(Qi(p),p,null!==u.node.init)}}e.parentPath.isFor({left:e.node})?e.replaceWith(r):e.replaceWithMultiple(n)}}};function GO(e,t,r){void 0===r&&(r="var"),e.traverse(UO,{kind:r,emit:t})}var VO=dE('\n SYSTEM_REGISTER(MODULE_NAME, SOURCES, function (EXPORT_IDENTIFIER, CONTEXT_IDENTIFIER) {\n "use strict";\n BEFORE_BODY;\n return {\n setters: SETTERS,\n execute: function () {\n BODY;\n }\n };\n });\n'),WO=dE('\n for (var KEY in TARGET) {\n if (KEY !== "default" && KEY !== "__esModule") EXPORT_OBJ[KEY] = TARGET[KEY];\n }\n');function HO(e,t,r,n,a){var s=[];if(1===r.length)s.push(Ki(Li(t,[to(r[0]),n[0]])));else if(a){var i=e.scope.generateUid("exportObj");s.push(Do("var",[Co(Qi(i),lo([]))])),s.push(WO({KEY:e.scope.generateUidIdentifier("key"),EXPORT_OBJ:Qi(i),TARGET:a}));for(var o=0;o<r.length;o++){var u=r[o],c=n[o];s.push(Ki(_i("=",oo(Qi(i),Qi(u)),c)))}s.push(Ki(Li(t,[Qi(i)])))}else{for(var l=[],p=0;p<r.length;p++){var d=r[p],f=n[p];l.push(fo(Qi(d),f))}s.push(Ki(Li(t,[lo(l)])))}return s}var qO=sP((function(e,t){e.assertVersion(7);var r=t.systemGlobal,n=void 0===r?"System":r,a=t.allowTopLevelThis,s=void 0!==a&&a,i=Symbol(),o={"AssignmentExpression|UpdateExpression":function(e){if(!e.node[i]){e.node[i]=!0;var t=e.get(e.isAssignmentExpression()?"left":"argument");if(t.isObjectPattern()||t.isArrayPattern()){for(var r=[e.node],n=0,a=Object.keys(t.getBindingIdentifiers());n<a.length;n++){var s=a[n];if(this.scope.getBinding(s)!==e.scope.getBinding(s))return;var o=this.exports[s];if(!o)return;var u=o,c=Array.isArray(u),l=0;for(u=c?u:u[Symbol.iterator]();;){var p;if(c){if(l>=u.length)break;p=u[l++]}else{if((l=u.next()).done)break;p=l.value}var d=p;r.push(this.buildCall(d,Qi(s)).expression)}}e.replaceWith(yo(r))}else if(t.isIdentifier()){var f=t.node.name;if(this.scope.getBinding(f)===e.scope.getBinding(f)){var h=this.exports[f];if(h){var m=e.node,y=e.isUpdateExpression({prefix:!1});y&&(m=Ii(m.operator[0],wo("+",pp(m.argument)),ro(1)));var g=h,v=Array.isArray(g),b=0;for(g=v?g:g[Symbol.iterator]();;){var x;if(v){if(b>=g.length)break;x=g[b++]}else{if((b=g.next()).done)break;x=b.value}var E=x;m=this.buildCall(E,m).expression}y&&(m=yo([m,e.node])),e.replaceWith(m)}}}}}};return{name:"transform-modules-systemjs",pre:function(){this.file.set("@babel/plugin-transform-modules-*","systemjs")},visitor:{CallExpression:function(e,t){qe(e.node.callee)&&(this.file.has("@babel/plugin-proposal-dynamic-import")||console.warn("WARNING: Dynamic import() transformation must be enabled using the\n @babel/plugin-proposal-dynamic-import plugin. Babel 8 will\n no longer transform import() without using that plugin.\n"),e.replaceWith(Li(oo(Qi(t.contextIdent),Qi("import")),[kO(ff,e.node)])))},MetaProperty:function(e,t){"import"===e.node.meta.name&&"meta"===e.node.property.name&&e.replaceWith(oo(Qi(t.contextIdent),Qi("meta")))},ReferencedIdentifier:function(e,t){"__moduleName"!==e.node.name||e.scope.hasBinding("__moduleName")||e.replaceWith(oo(Qi(t.contextIdent),Qi("id")))},Program:{enter:function(e,t){t.contextIdent=e.scope.generateUid("context"),s||QB(e)},exit:function(e,t){var r=e.scope.buildUndefinedNode(),a=e.scope.generateUid("export"),s=t.contextIdent,i=Object.create(null),u=[],c=[],l=[],p=[],d=[],f=[];function h(e,t){i[e]=i[e]||[],i[e].push(t)}function m(e,t,r){var n;u.forEach((function(t){t.key===e&&(n=t)})),n||u.push(n={key:e,imports:[],exports:[]}),n[t]=n[t].concat(r)}function y(e,t){return Ki(Li(Qi(a),[to(e),t]))}var g=[],v=[],b=e.get("body"),x=Array.isArray(b),E=0;for(b=x?b:b[Symbol.iterator]();;){var w;if(x){if(E>=b.length)break;w=b[E++]}else{if((E=b.next()).done)break;w=E.value}var S=w;if(S.isFunctionDeclaration())c.push(S.node),f.push(S);else if(S.isClassDeclaration())d.push(S.node.id),S.replaceWith(Ki(_i("=",pp(S.node.id),Id(S.node))));else if(S.isImportDeclaration()){m(S.node.source.value,"imports",S.node.specifiers);for(var D=0,C=Object.keys(S.getBindingIdentifiers());D<C.length;D++){var T=C[D];S.scope.removeBinding(T),d.push(Qi(T))}S.remove()}else if(S.isExportAllDeclaration())m(S.node.source.value,"exports",S.node),S.remove();else if(S.isExportDefaultDeclaration()){var j=S.get("declaration"),P=j.node.id;j.isClassDeclaration()?P?(g.push("default"),v.push(r),d.push(P),h(P.name,"default"),S.replaceWith(Ki(_i("=",pp(P),Id(j.node))))):(g.push("default"),v.push(Id(j.node)),f.push(S)):j.isFunctionDeclaration()?(P?(c.push(j.node),g.push("default"),v.push(pp(P)),h(P.name,"default")):(g.push("default"),v.push(Id(j.node))),f.push(S)):S.replaceWith(y("default",j.node))}else if(S.isExportNamedDeclaration()){var k=S.get("declaration");if(k.node)if(S.replaceWith(k),S.isFunction()){var F=k.node,_=F.id.name;h(_,_),c.push(F),g.push(_),v.push(pp(F.id)),f.push(S)}else if(S.isClass()){var I=k.node.id.name;g.push(I),v.push(r),d.push(k.node.id),S.replaceWith(Ki(_i("=",pp(k.node.id),Id(k.node)))),h(I,I)}else for(var B=0,O=Object.keys(k.getBindingIdentifiers());B<O.length;B++){var N=O[B];h(N,N)}else{var R=S.node.specifiers;if(R&&R.length)if(S.node.source)m(S.node.source.value,"exports",R),S.remove();else{var M=[],L=R,U=Array.isArray(L),G=0;for(L=U?L:L[Symbol.iterator]();;){var V;if(U){if(G>=L.length)break;V=L[G++]}else{if((G=L.next()).done)break;V=G.value}var W=V,H=S.scope.getBinding(W.local.name);H&&A(H.path.node)?(g.push(W.exported.name),v.push(pp(W.local))):H||M.push(y(W.exported.name,W.local)),h(W.local.name,W.exported.name)}S.replaceWithMultiple(M)}else S.remove()}}}u.forEach((function(t){var r=[],n=e.scope.generateUid(t.key),s=t.imports,i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u;ie(c)?r.push(Ki(_i("=",c.local,Qi(n)))):se(c)&&(c=Wo(c.local,Qi("default"))),oe(c)&&r.push(Ki(_i("=",c.local,oo(Qi(n),c.imported))))}if(t.exports.length){var d=[],f=[],h=!1,m=t.exports,y=Array.isArray(m),g=0;for(m=y?m:m[Symbol.iterator]();;){var v;if(y){if(g>=m.length)break;v=m[g++]}else{if((g=m.next()).done)break;v=g.value}var b=v;ee(b)?h=!0:ne(b)&&(d.push(b.exported.name),f.push(oo(Qi(n),b.local)))}r=r.concat(HO(e,Qi(a),d,f,h?Qi(n):null))}p.push(to(t.key)),l.push($i(null,[Qi(n)],Ri(r)))}));var q=this.getModuleName();q&&(q=to(q)),GO(e,(function(e,t,n){d.push(e),n||(g.push(t),v.push(r))}),null),d.length&&c.unshift(Do("var",d.map((function(e){return Co(e)})))),g.length&&(c=c.concat(HO(e,Qi(a),g,v,null))),e.traverse(o,{exports:i,buildCall:y,scope:e.scope});for(var K=0,z=f;K<z.length;K++){z[K].remove()}e.node.body=[VO({SYSTEM_REGISTER:oo(Qi(n),Qi("register")),BEFORE_BODY:c,MODULE_NAME:q,SETTERS:Fi(l),SOURCES:Fi(p),BODY:e.node.body,EXPORT_IDENTIFIER:Qi(a),CONTEXT_IDENTIFIER:Qi(s)})]}}}}})),KO=dE("\n GLOBAL_REFERENCE = GLOBAL_REFERENCE || {}\n"),zO=dE('\n (function (global, factory) {\n if (typeof define === "function" && define.amd) {\n define(MODULE_NAME, AMD_ARGUMENTS, factory);\n } else if (typeof exports !== "undefined") {\n factory(COMMONJS_ARGUMENTS);\n } else {\n var mod = { exports: {} };\n factory(BROWSER_ARGUMENTS);\n\n GLOBAL_TO_ASSIGN;\n }\n })(\n typeof globalThis !== "undefined" ? globalThis\n : typeof self !== "undefined" ? self\n : this,\n function(IMPORT_NAMES) {\n })\n'),XO=sP((function(e,t){e.assertVersion(7);var r=t.globals,n=t.exactGlobals,a=t.loose,s=t.allowTopLevelThis,i=t.strict,o=t.strictMode,u=t.noInterop;function c(e,t,r,n){var a=n?n.value:hS(r,mS(r)),s=oo(Qi("global"),Qi(kd(a))),i=[];if(t){var o=e[a];if(o){i=[];var u=o.split(".");s=u.slice(1).reduce((function(e,t){return i.push(KO({GLOBAL_REFERENCE:pp(e)})),oo(e,Qi(t))}),oo(Qi("global"),Qi(u[0])))}}return i.push(Ki(_i("=",s,oo(Qi("mod"),Qi("exports"))))),i}function l(e,t,r){var n;if(t){var a=e[r];n=a?a.split(".").reduce((function(e,t){return oo(e,Qi(t))}),Qi("global")):oo(Qi("global"),Qi(kd(r)))}else{var s=hS(r,mS(r)),i=e[s]||s;n=oo(Qi("global"),Qi(kd(i)))}return n}return{name:"transform-modules-umd",visitor:{Program:{exit:function(e){if(gI(e)){var t=r||{},p=this.getModuleName();p&&(p=to(p));var d=EO(e,{loose:a,strict:i,strictMode:o,allowTopLevelThis:s,noInterop:u}),f=d.meta,h=d.headers,m=[],y=[],g=[],v=[];oO(f)&&(m.push(to("exports")),y.push(Qi("exports")),g.push(oo(Qi("mod"),Qi("exports"))),v.push(Qi(f.exportName)));var b=f.source,x=Array.isArray(b),E=0;for(b=x?b:b[Symbol.iterator]();;){var A;if(x){if(E>=b.length)break;A=b[E++]}else{if((E=b.next()).done)break;A=E.value}var w=A,S=w[0],D=w[1];if(m.push(to(S)),y.push(Li(Qi("require"),[to(S)])),g.push(l(t,n,S)),v.push(Qi(D.name)),!uO(D)){var C=wO(e,Qi(D.name),D.interop);if(C){var T=Ki(_i("=",Qi(D.name),C));T.loc=f.loc,h.push(T)}}h.push.apply(h,SO(f,D,a))}AO(h),e.unshiftContainer("body",h);var j=e.node,P=j.body,k=j.directives;e.node.directives=[],e.node.body=[];var F=e.pushContainer("body",[zO({MODULE_NAME:p,AMD_ARGUMENTS:Fi(m),COMMONJS_ARGUMENTS:y,BROWSER_ARGUMENTS:g,IMPORT_NAMES:v,GLOBAL_TO_ASSIGN:c(t,n,this.filename||"unknown",p)})])[0].get("expression.arguments")[1].get("body");F.pushContainer("directives",k),F.pushContainer("body",P)}}}}}}));function YO(e,t){var r=t.runtime,n=void 0===r||r;if("boolean"!=typeof n)throw new Error("The 'runtime' option must be boolean");return D_({name:"transform-named-capturing-groups-regex",feature:"namedCaptureGroups",options:{runtime:n}})}var JO=sP((function(e){return e.assertVersion(7),{name:"transform-new-target",visitor:{MetaProperty:function(e){var t=e.get("meta"),r=e.get("property"),n=e.scope;if(t.isIdentifier({name:"new"})&&r.isIdentifier({name:"target"})){var a=e.findParent((function(e){return!!e.isClass()||!(!e.isFunction()||e.isArrowFunctionExpression())&&!e.isClassMethod({kind:"constructor"})}));if(!a)throw e.buildCodeFrameError("new.target must be under a (non-arrow) function or a class.");var s=a.node;if(!s.id){if(a.isMethod())return void e.replaceWith(n.buildUndefinedNode());s.id=n.generateUidIdentifier("target")}var i=oo(xo(),Qi("constructor"));if(a.isClass())return void e.replaceWith(i);e.replaceWith(Gi(Ii("instanceof",xo(),pp(s.id)),i,n.buildUndefinedNode()))}}}}})),$O=sP((function(e){return e.assertVersion(7),{name:"transform-object-assign",visitor:{CallExpression:function(e,t){e.get("callee").matchesPattern("Object.assign")&&(e.node.callee=t.addHelper("extends"))}}}}));var QO=sP((function(e){return e.assertVersion(7),{name:"transform-object-super",visitor:{ObjectExpression:function(e,t){var r,n=function(){return r=r||e.scope.generateUidIdentifier("obj")};e.get("properties").forEach((function(e){e.isMethod()&&function(e,t,r){new YP({getObjectRef:t,methodPath:e,file:r}).replace()}(e,n,t)})),r&&(e.scope.push({id:pp(r)}),e.replaceWith(_i("=",pp(r),e.node)))}}}})),ZO=sP((function(e){return e.assertVersion(7),{name:"transform-object-set-prototype-of-to-assign",visitor:{CallExpression:function(e,t){e.get("callee").matchesPattern("Object.setPrototypeOf")&&(e.node.callee=t.addHelper("defaults"))}}}})),eN={enter:function(e,t){e.isThisExpression()&&(t.foundThis=!0),e.isReferencedIdentifier({name:"arguments"})&&(t.foundArguments=!0)},Function:function(e){e.skip()}};var tN=dE("\n let VARIABLE_NAME =\n arguments.length > ARGUMENT_KEY && arguments[ARGUMENT_KEY] !== undefined ?\n arguments[ARGUMENT_KEY]\n :\n DEFAULT_VALUE;\n"),rN=dE("\n if (ASSIGNMENT_IDENTIFIER === UNDEFINED) {\n ASSIGNMENT_IDENTIFIER = DEFAULT_VALUE;\n }\n"),nN=dE("\n let ASSIGNMENT_IDENTIFIER = PARAMETER_NAME === UNDEFINED ? DEFAULT_VALUE : PARAMETER_NAME ;\n"),aN=dE("\n let $0 = arguments.length > $1 ? arguments[$1] : undefined;\n");function sN(e,t){if(!e.hasOwnBinding(t.name))return!0;var r=e.getOwnBinding(t.name).kind;return"param"===r||"local"===r}var iN={ReferencedIdentifier:function(e,t){var r=e.scope,n=e.node;"eval"!==n.name&&sN(r,n)&&sN(t.scope,n)||(t.iife=!0,e.stop())},Scope:function(e){e.skip()}};function oN(e,t){for(var r=e.node,n=e.scope,a={iife:!1,scope:n},s=[],i=e.get("params"),o=null,u=0;u<i.length;u++){for(var c=i[u],l=0,p=Object.keys(c.getBindingIdentifiers());l<p.length;l++){var d,f=p[l],h=null==(d=n.bindings[f])?void 0:d.constantViolations;if(h){var m=h,y=Array.isArray(m),g=0;for(m=y?m:m[Symbol.iterator]();;){var v;if(y){if(g>=m.length)break;v=m[g++]}else{if((g=m.next()).done)break;v=g.value}var b=v,x=b.node;switch(x.type){case"VariableDeclarator":if(null===x.init){var E=b.parentPath;if(!E.parentPath.isFor()||E.parentPath.get("body")===E){b.remove();break}}case"FunctionDeclaration":a.iife=!0}}}}var A=c.isAssignmentPattern();if(A&&(t||"set"===r.kind)){var w=c.get("left"),S=c.get("right"),D=n.buildUndefinedNode();if(w.isIdentifier())s.push(rN({ASSIGNMENT_IDENTIFIER:pp(w.node),DEFAULT_VALUE:S.node,UNDEFINED:D})),c.replaceWith(w.node);else if(w.isObjectPattern()||w.isArrayPattern()){var C=n.generateUidIdentifier();s.push(nN({ASSIGNMENT_IDENTIFIER:w.node,DEFAULT_VALUE:S.node,PARAMETER_NAME:pp(C),UNDEFINED:D})),c.replaceWith(C)}}else if(A){null===o&&(o=u);var T=c.get("left"),j=c.get("right");a.iife||(j.isIdentifier()&&!sN(n,j.node)?a.iife=!0:j.traverse(iN,a));var P=tN({VARIABLE_NAME:T.node,DEFAULT_VALUE:j.node,ARGUMENT_KEY:ro(u)});s.push(P)}else if(null!==o){var k=aN([c.node,ro(u)]);s.push(k)}else if(c.isObjectPattern()||c.isArrayPattern()){var F=e.scope.generateUidIdentifier("ref"),_=Do("let",[Co(c.node,F)]);s.push(_),c.replaceWith(pp(F))}a.iife||c.isIdentifier()||c.traverse(iN,a)}return 0!==s.length&&(null!==o&&(r.params=r.params.slice(0,o)),e.ensureBlock(),a.iife?(s.push(function(e,t,r){void 0===t&&(t=e.scope),void 0===r&&(r=!0);var n=e.node,a=$i(null,[],n.body,n.generator,n.async),s=a,i=[];r&&GO(e,(function(e){return t.push({id:e})}));var o={foundThis:!1,foundArguments:!1};e.traverse(eN,o),(o.foundArguments||o.foundThis)&&(s=oo(a,Qi("apply")),i=[],o.foundThis&&i.push(xo()),o.foundArguments&&(o.foundThis||i.push(no()),i.push(Qi("arguments"))));var u=Li(s,i);return n.generator&&(u=Qo(u,!0)),mo(u)}(e,n,!1)),e.set("body",Ri(s))):e.get("body").unshiftContainer("body",s),!0)}var uN=dE("\n for (var LEN = ARGUMENTS.length,\n ARRAY = new Array(ARRAY_LEN),\n KEY = START;\n KEY < LEN;\n KEY++) {\n ARRAY[ARRAY_KEY] = ARGUMENTS[KEY];\n }\n"),cN=dE("\n (INDEX < OFFSET || ARGUMENTS.length <= INDEX) ? undefined : ARGUMENTS[INDEX]\n"),lN=dE("\n REF = INDEX, (REF < OFFSET || ARGUMENTS.length <= REF) ? undefined : ARGUMENTS[REF]\n"),pN=dE("\n ARGUMENTS.length <= OFFSET ? 0 : ARGUMENTS.length - OFFSET\n");function dN(e,t){return e.node.name===t.name&&e.scope.bindingIdentifierEquals(t.name,t.outerBinding)}var fN={Scope:function(e,t){e.scope.bindingIdentifierEquals(t.name,t.outerBinding)||e.skip()},Flow:function(e){e.isTypeCastExpression()||e.skip()},Function:function(e,t){var r=t.noOptimise;t.noOptimise=!0,e.traverse(fN,t),t.noOptimise=r,e.skip()},ReferencedIdentifier:function(e,t){var r=e.node;if("arguments"===r.name&&(t.deopted=!0),dN(e,t))if(t.noOptimise)t.deopted=!0;else{var n=e.parentPath;if("params"===n.listKey&&n.key<t.offset)return;if(n.isMemberExpression({object:r})){var a=n.parentPath;if(!t.deopted&&!(a.isAssignmentExpression()&&n.node===a.node.left||a.isLVal()||a.isForXStatement()||a.isUpdateExpression()||a.isUnaryExpression({operator:"delete"})||(a.isCallExpression()||a.isNewExpression())&&n.node===a.node.callee))if(n.node.computed){if(n.get("property").isBaseType("number"))return void t.candidates.push({cause:"indexGetter",path:e})}else if("length"===n.node.property.name)return void t.candidates.push({cause:"lengthGetter",path:e})}if(0===t.offset&&n.isSpreadElement()){var s=n.parentPath;if(s.isCallExpression()&&1===s.node.arguments.length)return void t.candidates.push({cause:"argSpread",path:e})}t.references.push(e)}},BindingIdentifier:function(e,t){dN(e,t)&&(t.deopted=!0)}};function hN(e,t,r){var n,a=ro(r);n=j(e.parent.property)?ro(e.parent.property.value+r):0===r?e.parent.property:Ii("+",e.parent.property,pp(a));var s=e.scope;if(s.isPure(n)){var i=e.parentPath;i.replaceWith(cN({ARGUMENTS:t,OFFSET:a,INDEX:n}));var o=i.get("test").get("left").evaluate();o.confident&&(!0===o.value?i.replaceWith(i.scope.buildUndefinedNode()):i.get("test").replaceWith(i.get("test").get("right")))}else{var u=s.generateUidIdentifierBasedOnNode(n);s.push({id:u,kind:"var"}),e.parentPath.replaceWith(lN({ARGUMENTS:t,OFFSET:a,INDEX:n,REF:pp(u)}))}}function mN(e,t,r){r?e.parentPath.replaceWith(pN({ARGUMENTS:t,OFFSET:ro(r)})):e.replaceWith(t)}function yN(e){var t=e.node,r=e.scope;if(!function(e){var t=e.params.length;return t>0&&M(e.params[t-1])}(t))return!1;var n=t.params.pop().argument,a=Qi("arguments");if(yt(n)){var s=Do("let",[Co(n,n=r.generateUidIdentifier("ref"))]);t.body.body.unshift(s)}var i=function(e){var t=e.params.length;return t>0&&S(e.params[0],{name:"this"})&&(t-=1),t}(t),o={references:[],offset:i,argumentsNode:a,outerBinding:r.getBindingIdentifier(n.name),candidates:[],name:n.name,deopted:!1};if(e.traverse(fN,o),!o.deopted&&!o.references.length){for(var u=0,c=o.candidates;u<c.length;u++){var l=c[u],p=l.path,d=l.cause,f=pp(a);switch(d){case"indexGetter":hN(p,f,o.offset);break;case"lengthGetter":mN(p,f,o.offset);break;default:p.replaceWith(f)}}return!0}o.references=o.references.concat(o.candidates.map((function(e){return e.path})));var h,m,y=ro(i),g=r.generateUidIdentifier("key"),v=r.generateUidIdentifier("len");i?(h=Ii("-",pp(g),pp(y)),m=Gi(Ii(">",pp(v),pp(y)),Ii("-",pp(v),pp(y)),ro(0))):(h=Qi(g.name),m=Qi(v.name));var b=uN({ARGUMENTS:a,ARRAY_KEY:h,ARRAY_LEN:m,START:y,ARRAY:n,KEY:g,LEN:v});if(o.deopted)t.body.body.unshift(b);else{var x=e.getEarliestCommonAncestorFrom(o.references).getStatementParent();x.findParent((function(e){if(!e.isLoop())return e.isFunction();x=e})),x.insertBefore(b)}return!0}var gN=sP((function(e,t){e.assertVersion(7);var r=t.loose;return{name:"transform-parameters",visitor:{Function:function(e){e.isArrowFunctionExpression()&&e.get("params").some((function(e){return e.isRestElement()||e.isAssignmentPattern()}))&&e.arrowFunctionToExpression();var t=yN(e),n=oN(e,r);(t||n)&&e.scope.crawl()}}}})),vN=sP((function(e){return e.assertVersion(7),{name:"transform-property-literals",visitor:{ObjectProperty:{exit:function(e){var t=e.node,r=t.key;t.computed||!S(r)||lf(r.name)||(t.key=to(r.name))}}}}})),bN=sP((function(e){return e.assertVersion(7),{name:"transform-property-mutators",visitor:{ObjectExpression:function(e,t){for(var r=e.node,n=!1,a=0,s=r.properties;a<s.length;a++){var i=s[a];if("get"===i.kind||"set"===i.kind){n=!0;break}}if(n){var o={};r.properties=r.properties.filter((function(e){return!!(e.computed||"get"!==e.kind&&"set"!==e.kind)||(wB(o,e,null,t),!1)})),e.replaceWith(Li(oo(Qi("Object"),Qi("defineProperties")),[r,CB(o)]))}}}}})),xN=sP((function(e){function t(e){return ft(_d(e,e.key),{value:"__proto__"})}function r(e){var t=e.left;return _(t)&&ft(_d(t,t.property),{value:"__proto__"})}function n(e,t,r){return Ki(Li(r.addHelper("defaults"),[t,e.right]))}return e.assertVersion(7),{name:"transform-proto-to-assign",visitor:{AssignmentExpression:function(e,t){if(r(e.node)){var a=[],s=e.node.left.object,i=e.scope.maybeGenerateMemoised(s);i&&a.push(Ki(_i("=",i,s))),a.push(n(e.node,pp(i||s),t)),i&&a.push(pp(i)),e.replaceWithMultiple(a)}},ExpressionStatement:function(e,t){var a=e.node.expression;c(a,{operator:"="})&&r(a)&&e.replaceWith(n(a,a.left.object,t))},ObjectExpression:function(e,r){for(var n,a=e.node,s=0,i=a.properties;s<i.length;s++){var o=i[s];t(o)&&(n=o.value,E_(a.properties,o))}if(n){var u=[lo([]),n];a.properties.length&&u.push(a),e.replaceWith(Li(r.addHelper("extends"),u))}}}}})),EN=sP((function(e,t){e.assertVersion(7);var r=t.allowMutablePropsOnTags;if(null!=r&&!Array.isArray(r))throw new Error(".allowMutablePropsOnTags must be an array, null, or undefined.");var n=new WeakSet,a={enter:function(e,t){var r=function(){t.isImmutable=!1,e.stop()};if(e.isJSXClosingElement())e.skip();else{if(e.isJSXIdentifier({name:"ref"})&&e.parentPath.isJSXAttribute({name:e.node}))return r();if(!(e.isJSXIdentifier()||e.isIdentifier()||e.isJSXMemberExpression()||e.isImmutable())){if(e.isPure()){var n=e.evaluate();if(n.confident){var a=n.value;if(!(!t.mutablePropsAllowed&&a&&"object"==typeof a||"function"==typeof a))return void e.skip()}else if(S(n.deopt))return}r()}}}};return{name:"transform-react-constant-elements",visitor:{JSXElement:function(e){if(!n.has(e.node)){n.add(e.node);var t={isImmutable:!0};if(null!=r){for(var s=e.get("openingElement.name");s.isJSXMemberExpression();)s=s.get("property");var i=s.node.name;t.mutablePropsAllowed=r.indexOf(i)>-1}if(e.traverse(a,t),t.isImmutable){var o=e.hoist();o&&IP(o)}}}}}})),AN=sP((function(e){function t(e,t){for(var r=t.arguments[0].properties,n=!0,a=0;a<r.length;a++){if(ft(_d(r[a]),{value:"displayName"})){n=!1;break}}n&&r.unshift(fo(Qi("displayName"),to(e)))}e.assertVersion(7);var r=St("React.createClass");function n(e){if(!e||!f(e))return!1;if(!r(e.callee)&&"createReactClass"!==e.callee.name)return!1;var t=e.arguments;return 1===t.length&&!!O(t[0])}return{name:"transform-react-display-name",visitor:{ExportDefaultDeclaration:function(e,r){var a=e.node;if(n(a.declaration)){var s=r.filename||"unknown",i=yS.basename(s,yS.extname(s));"index"===i&&(i=yS.basename(yS.dirname(s))),t(i,a.declaration)}},CallExpression:function(e){var r,a=e.node;n(a)&&(e.find((function(e){if(e.isAssignmentExpression())r=e.node.left;else if(e.isObjectProperty())r=e.node.key;else if(e.isVariableDeclarator())r=e.node.id;else if(e.isStatement())return!0;if(r)return!0})),r&&(_(r)&&(r=r.property),S(r)&&t(r.name,a)))}}}}));function wN(e){var t={JSXNamespacedName:function(t){if(e.throwIfNamespace)throw t.buildCodeFrameError("Namespace tags are not supported by default. React's JSX doesn't support namespace tags. You can turn on the 'throwIfNamespace' flag to bypass this warning.")},JSXSpreadChild:function(e){throw e.buildCodeFrameError("Spread children are not supported in React.")}};return t.JSXElement={exit:function(t,a){var s=function(t,a){if(e.filter&&!e.filter(t.node,a))return;var s=t.get("openingElement");s.parent.children=df.buildChildren(s.parent);var i,o=function e(t,r){if(Be(t)){if("this"===t.name&&of(t,r))return xo();if(!as.keyword.isIdentifierNameES6(t.name))return to(t.name);t.type="Identifier"}else{if(Oe(t))return oo(e(t.object,t),e(t.property,t));if(Ne(t))return to(t.namespace.name+":"+t.name.name)}return t}(s.node.name,s.node),u=[];S(o)?i=o.name:ft(o)&&(i=o.value);var c={tagExpr:o,tagName:i,args:u};e.pre&&e.pre(c,a);var l=s.node.attributes;l=l.length?function(e,t){var a=[],s=[],i=t.opts.useSpread,o=void 0!==i&&i;if("boolean"!=typeof o)throw new Error("transform-react-jsx currently only accepts a boolean option for useSpread (defaults to false)");var u=t.opts.useBuiltIns||!1;if("boolean"!=typeof u)throw new Error("transform-react-jsx currently only accepts a boolean option for useBuiltIns (defaults to false)");if(o&&u)throw new Error("transform-react-jsx currently only accepts useBuiltIns or useSpread but not both");if(o){return lo(e.map(r))}for(;e.length;){var c=e.shift();Re(c)?(a=n(a,s),s.push(c.argument)):a.push(r(c))}if(n(a,s),1===s.length)e=s[0];else{O(s[0])||s.unshift(lo([]));var l=u?oo(Qi("Object"),Qi("assign")):t.addHelper("extends");e=Li(l,s)}return e}(l,a):no();u.push.apply(u,[l].concat(t.node.children)),e.post&&e.post(c,a);return c.call||Li(c.callee,u)}(t,a);s&&t.replaceWith(ef(s,t.node))}},t.JSXFragment={exit:function(t,r){if(e.compat)throw t.buildCodeFrameError("Fragment tags are only supported in React 16 and up.");var n=function(t,r){if(e.filter&&!e.filter(t.node,r))return;var n=t.get("openingElement");n.parent.children=df.buildChildren(n.parent);var a=[],s={tagExpr:r.get("jsxFragIdentifier")(),tagName:null,args:a};e.pre&&e.pre(s,r);a.push.apply(a,[no()].concat(t.node.children)),e.post&&e.post(s,r);return r.set("usedFragment",!0),s.call||Li(s.callee,a)}(t,r);n&&t.replaceWith(ef(n,t.node))}},t;function r(e){var t,r=function(e){return Ie(e)?e.expression:e}(e.value||ao(!0));if(Re(e))return zo(e.argument);T(r)&&!Ie(e.value)&&(r.value=r.value.replace(/\n\s+/g," "),null==(t=r.extra)||delete t.raw);return Ne(e.name)?e.name=to(e.name.namespace.name+":"+e.name.name.name):as.keyword.isIdentifierNameES6(e.name.name)?e.name.type="Identifier":e.name=to(e.name.name),ef(fo(e.name,r),e)}function n(e,t){return e.length?(t.push(lo(e)),[]):e}}var SN=sP((function(e){function t(e,t){return ke(e)&&Be(e.name,{name:t})}return e.assertVersion(7),{name:"transform-react-inline-elements",visitor:wN({filter:function(e){return e.openingElement&&!function(e){for(var r=0;r<e.length;r++){var n=e[r];if(Re(n))return!0;if(t(n,"ref"))return!0}return!1}(e.openingElement.attributes)},pre:function(e){var t=e.tagName,r=e.args;df.isCompatTag(t)?r.push(to(t)):r.push(e.tagExpr)},post:function(e,t){e.callee=t.addHelper("jsx");var r=e.args[1],n=!1;if(O(r)){var a=r.properties.findIndex((function(e){return S(e.key,{name:"key"})}));a>-1&&(e.args.splice(2,0,r.properties[a].value),r.properties.splice(a,1),n=!0)}else P(r)&&e.args.splice(1,1,lo([]));!n&&e.args.length>2&&e.args.splice(2,0,wo("void",ro(0)))}})}})),DN=sP((function(e,t){e.assertVersion(7);var r=void 0===t.throwIfNamespace||!!t.throwIfNamespace,n=t.pragma||"React.createElement",a=t.pragmaFrag||"React.Fragment",s=/\*?\s*@jsx\s+([^\s]+)/,i=/\*?\s*@jsxFrag\s+([^\s]+)/,o=function(e){return function(){return e.split(".").map((function(e){return Qi(e)})).reduce((function(e,t){return oo(e,t)}))}},u=wN({pre:function(e){var t=e.tagName,r=e.args;df.isCompatTag(t)?r.push(to(t)):r.push(e.tagExpr)},post:function(e,t){e.callee=t.get("jsxIdentifier")()},throwIfNamespace:r});return u.Program={enter:function(e,r){var u=r.file,c=n,l=a,p=!!t.pragma,d=!!t.pragmaFrag;if(u.ast.comments)for(var f=0,h=u.ast.comments;f<h.length;f++){var m=h[f],y=s.exec(m.value);y&&(c=y[1],p=!0);var g=i.exec(m.value);g&&(l=g[1],d=!0)}r.set("jsxIdentifier",o(c)),r.set("jsxFragIdentifier",o(l)),r.set("usedFragment",!1),r.set("pragmaSet",p),r.set("pragmaFragSet",d)},exit:function(e,t){if(t.get("pragmaSet")&&t.get("usedFragment")&&!t.get("pragmaFragSet"))throw new Error("transform-react-jsx: pragma has been set but pragmaFrag has not been set")}},u.JSXAttribute=function(e){Fe(e.node.value)&&(e.node.value=pc(e.node.value))},{name:"transform-react-jsx",inherits:EP,visitor:u}})),CN=sP((function(e){return e.assertVersion(7),{name:"transform-react-jsx-compat",manipulateOptions:function(e,t){t.plugins.push("jsx")},visitor:wN({pre:function(e){e.callee=e.tagExpr},post:function(e){df.isCompatTag(e.tagName)&&(e.call=Li(oo(oo(Qi("React"),Qi("DOM")),e.tagExpr,ft(e.tagExpr)),e.args))},compat:!0})}})),TN=sP((function(e){return e.assertVersion(7),{name:"transform-react-jsx-self",visitor:{JSXOpeningElement:function(e){var t=e.node,r=fc("__self"),n=xo();t.attributes.push(oc(r,pc(n)))}}}})),jN=sP((function(e){return e.assertVersion(7),{name:"transform-react-jsx-source",visitor:{JSXOpeningElement:function(e,t){var r=fc("__source"),n=e.container.openingElement.loc;if(n){for(var a=e.container.openingElement.attributes,s=0;s<a.length;s++){var i=a[s].name;if(i&&"__source"===i.name)return}if(!t.fileNameIdentifier){var o=t.filename||"",u=e.scope.generateUidIdentifier("_jsxFileName"),c=e.hub.getScope();c&&c.push({id:u,init:to(o)}),t.fileNameIdentifier=u}var l=function(e,t){var r=null!=t?ro(t):no();return lo([fo(Qi("fileName"),e),fo(Qi("lineNumber"),r)])}(t.fileNameIdentifier,n.start.line);a.push(oc(r,pc(l)))}}}}})),PN=Vt((function(e){function t(r){return"function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?e.exports=t=function(e){return typeof e}:e.exports=t=function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},t(r)}e.exports=t})),kN=Vt((function(e){function t(){if("function"!=typeof WeakMap)return null;var e=new WeakMap;return t=function(){return e},e}e.exports=function(e){if(e&&e.__esModule)return e;if(null===e||"object"!==PN(e)&&"function"!=typeof e)return{default:e};var r=t();if(r&&r.has(e))return r.get(e);var n={},a=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if(Object.prototype.hasOwnProperty.call(e,s)){var i=a?Object.getOwnPropertyDescriptor(e,s):null;i&&(i.get||i.set)?Object.defineProperty(n,s,i):n[s]=e[s]}return n.default=e,r&&r.set(e,n),n}}));Gt(kN);var FN=Vt((function(e){e.exports=function(e){return e&&e.__esModule?e:{default:e}}}));Gt(FN);var _N=Vt((function(e,t){t.__esModule=!0,t.wrapWithTypes=function(e,t){return function(){var n=r;r=e;try{for(var a=arguments.length,s=new Array(a),i=0;i<a;i++)s[i]=arguments[i];return t.apply(this,s)}finally{r=n}}},t.getTypes=n,t.runtimeProperty=function(e){var t=n();return t.memberExpression(t.identifier("regeneratorRuntime"),t.identifier(e),!1)},t.isReference=function(e){return e.isReferenced()||e.parentPath.isAssignmentExpression({left:e.node})},t.replaceWithOrRemove=function(e,t){t?e.replaceWith(t):e.remove()};var r=null;function n(){return r}}));Gt(_N);_N.wrapWithTypes,_N.getTypes,_N.runtimeProperty,_N.isReference,_N.replaceWithOrRemove;var IN=kN(_N),BN=Object.prototype.hasOwnProperty,ON=function(e){var t=IN.getTypes();t.assertFunction(e.node);var r={};function n(e,n){var a=e.node,s=e.scope;t.assertVariableDeclaration(a);var i=[];return a.declarations.forEach((function(e){r[e.id.name]=t.identifier(e.id.name),s.removeBinding(e.id.name),e.init?i.push(t.assignmentExpression("=",e.id,e.init)):n&&i.push(e.id)})),0===i.length?null:1===i.length?i[0]:t.sequenceExpression(i)}e.get("body").traverse({VariableDeclaration:{exit:function(e){var r=n(e,!1);null===r?e.remove():IN.replaceWithOrRemove(e,t.expressionStatement(r)),e.skip()}},ForStatement:function(e){var t=e.get("init");t.isVariableDeclaration()&&IN.replaceWithOrRemove(t,n(t,!1))},ForXStatement:function(e){var t=e.get("left");t.isVariableDeclaration()&&IN.replaceWithOrRemove(t,n(t,!0))},FunctionDeclaration:function(e){var n=e.node;r[n.id.name]=n.id;var a=t.expressionStatement(t.assignmentExpression("=",t.clone(n.id),t.functionExpression(e.scope.generateUidIdentifierBasedOnNode(n),n.params,n.body,n.generator,n.expression)));e.parentPath.isBlockStatement()?(e.parentPath.unshiftContainer("body",a),e.remove()):IN.replaceWithOrRemove(e,a),e.scope.removeBinding(n.id.name),e.skip()},FunctionExpression:function(e){e.skip()},ArrowFunctionExpression:function(e){e.skip()}});var a={};e.get("params").forEach((function(e){var r=e.node;t.isIdentifier(r)&&(a[r.name]=r)}));var s=[];return Object.keys(r).forEach((function(e){BN.call(a,e)||s.push(t.variableDeclarator(r[e],null))})),0===s.length?null:t.variableDeclaration("var",s)},NN=FN(aI);function RN(){NN.default.ok(this instanceof RN)}function MN(e){RN.call(this),(0,_N.getTypes)().assertLiteral(e),this.returnLoc=e}Y_(MN,RN);var LN=MN;function UN(e,t,r){RN.call(this);var n=(0,_N.getTypes)();n.assertLiteral(e),n.assertLiteral(t),r?n.assertIdentifier(r):r=null,this.breakLoc=e,this.continueLoc=t,this.label=r}Y_(UN,RN);var GN=UN;function VN(e){RN.call(this),(0,_N.getTypes)().assertLiteral(e),this.breakLoc=e}Y_(VN,RN);var WN=VN;function HN(e,t,r){RN.call(this),(0,_N.getTypes)().assertLiteral(e),t?NN.default.ok(t instanceof KN):t=null,r?NN.default.ok(r instanceof XN):r=null,NN.default.ok(t||r),this.firstLoc=e,this.catchEntry=t,this.finallyEntry=r}Y_(HN,RN);var qN=HN;function KN(e,t){RN.call(this);var r=(0,_N.getTypes)();r.assertLiteral(e),r.assertIdentifier(t),this.firstLoc=e,this.paramId=t}Y_(KN,RN);var zN=KN;function XN(e,t){RN.call(this);var r=(0,_N.getTypes)();r.assertLiteral(e),r.assertLiteral(t),this.firstLoc=e,this.afterLoc=t}Y_(XN,RN);var YN=XN;function JN(e,t){RN.call(this);var r=(0,_N.getTypes)();r.assertLiteral(e),r.assertIdentifier(t),this.breakLoc=e,this.label=t}Y_(JN,RN);var $N=JN;function QN(e){NN.default.ok(this instanceof QN),NN.default.ok(e instanceof MR.Emitter),this.emitter=e,this.entryStack=[new MN(e.finalLoc)]}var ZN=QN.prototype,eR=QN;ZN.withEntry=function(e,t){NN.default.ok(e instanceof RN),this.entryStack.push(e);try{t.call(this.emitter)}finally{var r=this.entryStack.pop();NN.default.strictEqual(r,e)}},ZN._findLeapLocation=function(e,t){for(var r=this.entryStack.length-1;r>=0;--r){var n=this.entryStack[r],a=n[e];if(a)if(t){if(n.label&&n.label.name===t.name)return a}else if(!(n instanceof JN))return a}return null},ZN.getBreakLoc=function(e){return this._findLeapLocation("breakLoc",e)},ZN.getContinueLoc=function(e){return this._findLeapLocation("continueLoc",e)};var tR={FunctionEntry:LN,LoopEntry:GN,SwitchEntry:WN,TryEntry:qN,CatchEntry:zN,FinallyEntry:YN,LabeledEntry:$N,LeapManager:eR},rR=Object,nR=Object.defineProperty,aR=Object.create;function sR(e,t,r){if(nR)try{nR.call(rR,e,t,{value:r})}catch(n){e[t]=r}else e[t]=r}function iR(e){return e&&(sR(e,"call",e.call),sR(e,"apply",e.apply)),e}iR(nR),iR(aR);var oR=iR(Object.prototype.hasOwnProperty),uR=iR(Number.prototype.toString),cR=iR(String.prototype.slice),lR=function(){};function pR(e){return aR?aR.call(rR,e):(lR.prototype=e||null,new lR)}var dR=Math.random,fR=pR(null);function hR(){do{var e=mR(cR.call(uR.call(dR(),36),2))}while(oR.call(fR,e));return fR[e]=e}function mR(e){var t={};return t[e]=!0,Object.keys(t)[0]}var yR=Object.getOwnPropertyNames;function gR(e){return pR(null)}Object.getOwnPropertyNames=function(e){for(var t=yR(e),r=0,n=0,a=t.length;r<a;++r)oR.call(fR,t[r])||(r>n&&(t[n]=t[r]),++n);return t.length=n,t};var vR=function(e){var t=hR(),r=pR(null);function n(n){return oR.call(n,t)||function(n){var a;sR(n,t,(function(t,s){if(t===r)return s?a=null:a||(a=e(n))}))}(n),n[t](r)}return e=e||gR,n.forget=function(e){oR.call(e,t)&&e[t](r,!0)},n},bR=FN(aI),xR=vR(),ER=Object.prototype.hasOwnProperty;function AR(e,t){function r(e){var t=(0,_N.getTypes)();t.assertNode(e);var r=!1;function a(e){return r||(Array.isArray(e)?e.some(a):t.isNode(e)&&(bR.default.strictEqual(r,!1),r=n(e))),r}var s=t.VISITOR_KEYS[e.type];if(s)for(var i=0;i<s.length;i++){a(e[s[i]])}return r}function n(n){(0,_N.getTypes)().assertNode(n);var a=xR(n);return ER.call(a,e)?a[e]:ER.call(wR,n.type)?a[e]=!1:ER.call(t,n.type)?a[e]=!0:a[e]=r(n)}return n.onlyChildren=r,n}var wR={FunctionExpression:!0,ArrowFunctionExpression:!0},SR={CallExpression:!0,ForInStatement:!0,UnaryExpression:!0,BinaryExpression:!0,AssignmentExpression:!0,UpdateExpression:!0,NewExpression:!0},DR={YieldExpression:!0,BreakStatement:!0,ContinueStatement:!0,ReturnStatement:!0,ThrowStatement:!0};for(var CR in DR)ER.call(DR,CR)&&(SR[CR]=DR[CR]);var TR={hasSideEffects:AR("hasSideEffects",SR),containsLeap:AR("containsLeap",DR)},jR=FN(aI),PR=kN(tR),kR=kN(TR),FR=kN(_N),_R=Object.prototype.hasOwnProperty;function IR(e){jR.default.ok(this instanceof IR),FR.getTypes().assertIdentifier(e),this.nextTempId=0,this.contextId=e,this.listing=[],this.marked=[!0],this.insertedLocs=new Set,this.finalLoc=this.loc(),this.tryEntries=[],this.leapManager=new PR.LeapManager(this)}var BR=IR.prototype,OR=IR;function NR(e){return new Error("all declarations should have been transformed into assignments before the Exploder began its work: "+JSON.stringify(e))}BR.loc=function(){var e=FR.getTypes().numericLiteral(-1);return this.insertedLocs.add(e),e},BR.getInsertedLocs=function(){return this.insertedLocs},BR.getContextId=function(){return FR.getTypes().clone(this.contextId)},BR.mark=function(e){FR.getTypes().assertLiteral(e);var t=this.listing.length;return-1===e.value?e.value=t:jR.default.strictEqual(e.value,t),this.marked[t]=!0,e},BR.emit=function(e){var t=FR.getTypes();t.isExpression(e)&&(e=t.expressionStatement(e)),t.assertStatement(e),this.listing.push(e)},BR.emitAssign=function(e,t){return this.emit(this.assign(e,t)),e},BR.assign=function(e,t){var r=FR.getTypes();return r.expressionStatement(r.assignmentExpression("=",r.cloneDeep(e),t))},BR.contextProperty=function(e,t){var r=FR.getTypes();return r.memberExpression(this.getContextId(),t?r.stringLiteral(e):r.identifier(e),!!t)},BR.stop=function(e){e&&this.setReturnValue(e),this.jump(this.finalLoc)},BR.setReturnValue=function(e){FR.getTypes().assertExpression(e.value),this.emitAssign(this.contextProperty("rval"),this.explodeExpression(e))},BR.clearPendingException=function(e,t){var r=FR.getTypes();r.assertLiteral(e);var n=r.callExpression(this.contextProperty("catch",!0),[r.clone(e)]);t?this.emitAssign(t,n):this.emit(n)},BR.jump=function(e){this.emitAssign(this.contextProperty("next"),e),this.emit(FR.getTypes().breakStatement())},BR.jumpIf=function(e,t){var r=FR.getTypes();r.assertExpression(e),r.assertLiteral(t),this.emit(r.ifStatement(e,r.blockStatement([this.assign(this.contextProperty("next"),t),r.breakStatement()])))},BR.jumpIfNot=function(e,t){var r,n=FR.getTypes();n.assertExpression(e),n.assertLiteral(t),r=n.isUnaryExpression(e)&&"!"===e.operator?e.argument:n.unaryExpression("!",e),this.emit(n.ifStatement(r,n.blockStatement([this.assign(this.contextProperty("next"),t),n.breakStatement()])))},BR.makeTempVar=function(){return this.contextProperty("t"+this.nextTempId++)},BR.getContextFunction=function(e){var t=FR.getTypes();return t.functionExpression(e||null,[this.getContextId()],t.blockStatement([this.getDispatchLoop()]),!1,!1)},BR.getDispatchLoop=function(){var e,t=this,r=FR.getTypes(),n=[],a=!1;return t.listing.forEach((function(s,i){t.marked.hasOwnProperty(i)&&(n.push(r.switchCase(r.numericLiteral(i),e=[])),a=!1),a||(e.push(s),r.isCompletionStatement(s)&&(a=!0))})),this.finalLoc.value=this.listing.length,n.push(r.switchCase(this.finalLoc,[]),r.switchCase(r.stringLiteral("end"),[r.returnStatement(r.callExpression(this.contextProperty("stop"),[]))])),r.whileStatement(r.numericLiteral(1),r.switchStatement(r.assignmentExpression("=",this.contextProperty("prev"),this.contextProperty("next")),n))},BR.getTryLocsList=function(){if(0===this.tryEntries.length)return null;var e=FR.getTypes(),t=0;return e.arrayExpression(this.tryEntries.map((function(r){var n=r.firstLoc.value;jR.default.ok(n>=t,"try entries out of order"),t=n;var a=r.catchEntry,s=r.finallyEntry,i=[r.firstLoc,a?a.firstLoc:null];return s&&(i[2]=s.firstLoc,i[3]=s.afterLoc),e.arrayExpression(i.map((function(t){return t&&e.clone(t)})))})))},BR.explode=function(e,t){var r=FR.getTypes(),n=e.node;if(r.assertNode(n),r.isDeclaration(n))throw NR(n);if(r.isStatement(n))return this.explodeStatement(e);if(r.isExpression(n))return this.explodeExpression(e,t);switch(n.type){case"Program":return e.get("body").map(this.explodeStatement,this);case"VariableDeclarator":throw NR(n);case"Property":case"SwitchCase":case"CatchClause":throw new Error(n.type+" nodes should be handled by their parents");default:throw new Error("unknown Node of type "+JSON.stringify(n.type))}},BR.explodeStatement=function(e,t){var r,n,a,s=FR.getTypes(),i=e.node,o=this;if(s.assertStatement(i),t?s.assertIdentifier(t):t=null,s.isBlockStatement(i))e.get("body").forEach((function(e){o.explodeStatement(e)}));else if(kR.containsLeap(i))switch(i.type){case"ExpressionStatement":o.explodeExpression(e.get("expression"),!0);break;case"LabeledStatement":n=this.loc(),o.leapManager.withEntry(new PR.LabeledEntry(n,i.label),(function(){o.explodeStatement(e.get("body"),i.label)})),o.mark(n);break;case"WhileStatement":r=this.loc(),n=this.loc(),o.mark(r),o.jumpIfNot(o.explodeExpression(e.get("test")),n),o.leapManager.withEntry(new PR.LoopEntry(n,r,t),(function(){o.explodeStatement(e.get("body"))})),o.jump(r),o.mark(n);break;case"DoWhileStatement":var u=this.loc(),c=this.loc();n=this.loc(),o.mark(u),o.leapManager.withEntry(new PR.LoopEntry(n,c,t),(function(){o.explode(e.get("body"))})),o.mark(c),o.jumpIf(o.explodeExpression(e.get("test")),u),o.mark(n);break;case"ForStatement":a=this.loc();var l=this.loc();n=this.loc(),i.init&&o.explode(e.get("init"),!0),o.mark(a),i.test&&o.jumpIfNot(o.explodeExpression(e.get("test")),n),o.leapManager.withEntry(new PR.LoopEntry(n,l,t),(function(){o.explodeStatement(e.get("body"))})),o.mark(l),i.update&&o.explode(e.get("update"),!0),o.jump(a),o.mark(n);break;case"TypeCastExpression":return o.explodeExpression(e.get("expression"));case"ForInStatement":a=this.loc(),n=this.loc();var p=o.makeTempVar();o.emitAssign(p,s.callExpression(FR.runtimeProperty("keys"),[o.explodeExpression(e.get("right"))])),o.mark(a);var d=o.makeTempVar();o.jumpIf(s.memberExpression(s.assignmentExpression("=",d,s.callExpression(s.cloneDeep(p),[])),s.identifier("done"),!1),n),o.emitAssign(i.left,s.memberExpression(s.cloneDeep(d),s.identifier("value"),!1)),o.leapManager.withEntry(new PR.LoopEntry(n,a,t),(function(){o.explodeStatement(e.get("body"))})),o.jump(a),o.mark(n);break;case"BreakStatement":o.emitAbruptCompletion({type:"break",target:o.leapManager.getBreakLoc(i.label)});break;case"ContinueStatement":o.emitAbruptCompletion({type:"continue",target:o.leapManager.getContinueLoc(i.label)});break;case"SwitchStatement":var f=o.emitAssign(o.makeTempVar(),o.explodeExpression(e.get("discriminant")));n=this.loc();for(var h=this.loc(),m=h,y=[],g=i.cases||[],v=g.length-1;v>=0;--v){var b=g[v];s.assertSwitchCase(b),b.test?m=s.conditionalExpression(s.binaryExpression("===",s.cloneDeep(f),b.test),y[v]=this.loc(),m):y[v]=h}var x=e.get("discriminant");FR.replaceWithOrRemove(x,m),o.jump(o.explodeExpression(x)),o.leapManager.withEntry(new PR.SwitchEntry(n),(function(){e.get("cases").forEach((function(e){var t=e.key;o.mark(y[t]),e.get("consequent").forEach((function(e){o.explodeStatement(e)}))}))})),o.mark(n),-1===h.value&&(o.mark(h),jR.default.strictEqual(n.value,h.value));break;case"IfStatement":var E=i.alternate&&this.loc();n=this.loc(),o.jumpIfNot(o.explodeExpression(e.get("test")),E||n),o.explodeStatement(e.get("consequent")),E&&(o.jump(n),o.mark(E),o.explodeStatement(e.get("alternate"))),o.mark(n);break;case"ReturnStatement":o.emitAbruptCompletion({type:"return",value:o.explodeExpression(e.get("argument"))});break;case"WithStatement":throw new Error("WithStatement not supported in generator functions.");case"TryStatement":n=this.loc();var A=i.handler,w=A&&this.loc(),S=w&&new PR.CatchEntry(w,A.param),D=i.finalizer&&this.loc(),C=D&&new PR.FinallyEntry(D,n),T=new PR.TryEntry(o.getUnmarkedCurrentLoc(),S,C);o.tryEntries.push(T),o.updateContextPrevLoc(T.firstLoc),o.leapManager.withEntry(T,(function(){if(o.explodeStatement(e.get("block")),w){D?o.jump(D):o.jump(n),o.updateContextPrevLoc(o.mark(w));var t=e.get("handler.body"),r=o.makeTempVar();o.clearPendingException(T.firstLoc,r),t.traverse(RR,{getSafeParam:function(){return s.cloneDeep(r)},catchParamName:A.param.name}),o.leapManager.withEntry(S,(function(){o.explodeStatement(t)}))}D&&(o.updateContextPrevLoc(o.mark(D)),o.leapManager.withEntry(C,(function(){o.explodeStatement(e.get("finalizer"))})),o.emit(s.returnStatement(s.callExpression(o.contextProperty("finish"),[C.firstLoc]))))})),o.mark(n);break;case"ThrowStatement":o.emit(s.throwStatement(o.explodeExpression(e.get("argument"))));break;default:throw new Error("unknown Statement of type "+JSON.stringify(i.type))}else o.emit(i)};var RR={Identifier:function(e,t){e.node.name===t.catchParamName&&FR.isReference(e)&&FR.replaceWithOrRemove(e,t.getSafeParam())},Scope:function(e,t){e.scope.hasOwnBinding(t.catchParamName)&&e.skip()}};BR.emitAbruptCompletion=function(e){(function(e){var t=e.type;if("normal"===t)return!_R.call(e,"target");if("break"===t||"continue"===t)return!_R.call(e,"value")&&FR.getTypes().isLiteral(e.target);if("return"===t||"throw"===t)return _R.call(e,"value")&&!_R.call(e,"target");return!1})(e)||jR.default.ok(!1,"invalid completion record: "+JSON.stringify(e)),jR.default.notStrictEqual(e.type,"normal","normal completions are not abrupt");var t=FR.getTypes(),r=[t.stringLiteral(e.type)];"break"===e.type||"continue"===e.type?(t.assertLiteral(e.target),r[1]=this.insertedLocs.has(e.target)?e.target:t.cloneDeep(e.target)):"return"!==e.type&&"throw"!==e.type||e.value&&(t.assertExpression(e.value),r[1]=this.insertedLocs.has(e.value)?e.value:t.cloneDeep(e.value)),this.emit(t.returnStatement(t.callExpression(this.contextProperty("abrupt"),r)))},BR.getUnmarkedCurrentLoc=function(){return FR.getTypes().numericLiteral(this.listing.length)},BR.updateContextPrevLoc=function(e){var t=FR.getTypes();e?(t.assertLiteral(e),-1===e.value?e.value=this.listing.length:jR.default.strictEqual(e.value,this.listing.length)):e=this.getUnmarkedCurrentLoc(),this.emitAssign(this.contextProperty("prev"),e)},BR.explodeExpression=function(e,t){var r=FR.getTypes(),n=e.node;if(!n)return n;r.assertExpression(n);var a,s,i=this;function o(e){if(r.assertExpression(e),!t)return e;i.emit(e)}if(!kR.containsLeap(n))return o(n);var u=kR.containsLeap.onlyChildren(n);function c(e,t,n){jR.default.ok(!n||!e,"Ignoring the result of a child expression but forcing it to be assigned to a temporary variable?");var a=i.explodeExpression(t,n);return n||(e||u&&!r.isLiteral(a))&&(a=i.emitAssign(e||i.makeTempVar(),a)),a}switch(n.type){case"MemberExpression":return o(r.memberExpression(i.explodeExpression(e.get("object")),n.computed?c(null,e.get("property")):n.property,n.computed));case"CallExpression":var l,p=e.get("callee"),d=e.get("arguments"),f=[],h=!1;if(d.forEach((function(e){h=h||kR.containsLeap(e.node)})),r.isMemberExpression(p.node))if(h){var m=c(i.makeTempVar(),p.get("object")),y=p.node.computed?c(null,p.get("property")):p.node.property;f.unshift(m),l=r.memberExpression(r.memberExpression(r.cloneDeep(m),y,p.node.computed),r.identifier("call"),!1)}else l=i.explodeExpression(p);else l=c(null,p),r.isMemberExpression(l)&&(l=r.sequenceExpression([r.numericLiteral(0),r.cloneDeep(l)]));return d.forEach((function(e){f.push(c(null,e))})),o(r.callExpression(l,f.map((function(e){return r.cloneDeep(e)}))));case"NewExpression":return o(r.newExpression(c(null,e.get("callee")),e.get("arguments").map((function(e){return c(null,e)}))));case"ObjectExpression":return o(r.objectExpression(e.get("properties").map((function(e){return e.isObjectProperty()?r.objectProperty(e.node.key,c(null,e.get("value")),e.node.computed):e.node}))));case"ArrayExpression":return o(r.arrayExpression(e.get("elements").map((function(e){return c(null,e)}))));case"SequenceExpression":var g=n.expressions.length-1;return e.get("expressions").forEach((function(e){e.key===g?a=i.explodeExpression(e,t):i.explodeExpression(e,!0)})),a;case"LogicalExpression":s=this.loc(),t||(a=i.makeTempVar());var v=c(a,e.get("left"));return"&&"===n.operator?i.jumpIfNot(v,s):(jR.default.strictEqual(n.operator,"||"),i.jumpIf(v,s)),c(a,e.get("right"),t),i.mark(s),a;case"ConditionalExpression":var b=this.loc();s=this.loc();var x=i.explodeExpression(e.get("test"));return i.jumpIfNot(x,b),t||(a=i.makeTempVar()),c(a,e.get("consequent"),t),i.jump(s),i.mark(b),c(a,e.get("alternate"),t),i.mark(s),a;case"UnaryExpression":return o(r.unaryExpression(n.operator,i.explodeExpression(e.get("argument")),!!n.prefix));case"BinaryExpression":return o(r.binaryExpression(n.operator,c(null,e.get("left")),c(null,e.get("right"))));case"AssignmentExpression":if("="===n.operator)return o(r.assignmentExpression(n.operator,i.explodeExpression(e.get("left")),i.explodeExpression(e.get("right"))));var E=i.explodeExpression(e.get("left")),A=i.emitAssign(i.makeTempVar(),E);return o(r.assignmentExpression("=",r.cloneDeep(E),r.assignmentExpression(n.operator,r.cloneDeep(A),i.explodeExpression(e.get("right")))));case"UpdateExpression":return o(r.updateExpression(n.operator,i.explodeExpression(e.get("argument")),n.prefix));case"YieldExpression":s=this.loc();var w=n.argument&&i.explodeExpression(e.get("argument"));if(w&&n.delegate){var S=i.makeTempVar(),D=r.returnStatement(r.callExpression(i.contextProperty("delegateYield"),[w,r.stringLiteral(S.property.name),s]));return D.loc=n.loc,i.emit(D),i.mark(s),S}i.emitAssign(i.contextProperty("next"),s);var C=r.returnStatement(r.cloneDeep(w)||null);return C.loc=n.loc,i.emit(C),i.mark(s),i.contextProperty("sent");default:throw new Error("unknown Expression of type "+JSON.stringify(n.type))}};var MR={Emitter:OR},LR=Vt((function(e,t){t.__esModule=!0,t.default=function(e){var t=r.getTypes();if(!e.node||!t.isFunction(e.node))throw new Error("replaceShorthandObjectMethod can only be called on Function AST node paths.");if(!t.isObjectMethod(e.node))return e;if(!e.node.generator)return e;var n=e.node.params.map((function(e){return t.cloneDeep(e)})),a=t.functionExpression(null,n,t.cloneDeep(e.node.body),e.node.generator,e.node.async);return r.replaceWithOrRemove(e,t.objectProperty(t.cloneDeep(e.node.key),a,e.node.computed,!1)),e.get("value")};var r=kN(_N)}));Gt(LR);var UR=FN(aI),GR=FN(LR),VR=kN(_N);function WR(e,t){return e.generator?e.async?!1!==t.opts.asyncGenerators:!1!==t.opts.generators:!!e.async&&!1!==t.opts.async}var HR=vR();var qR={"FunctionExpression|FunctionDeclaration|Method":function(e){e.skip()},Identifier:function(e,t){"arguments"===e.node.name&&VR.isReference(e)&&(VR.replaceWithOrRemove(e,t.getArgsId()),t.usesArguments=!0)},ThisExpression:function(e,t){t.usesThis=!0}},KR={MetaProperty:function(e){var t=e.node;if("function"===t.meta.name&&"sent"===t.property.name){var r=VR.getTypes();VR.replaceWithOrRemove(e,r.memberExpression(r.clone(this.context),r.identifier("_sent")))}}},zR={Function:function(e){e.skip()},AwaitExpression:function(e){var t=VR.getTypes(),r=e.node.argument;VR.replaceWithOrRemove(e,t.yieldExpression(t.callExpression(VR.runtimeProperty("awrap"),[r]),!1))}},XR=function(e){var t=e.types;return{Method:function(e,r){var n=e.node;if(WR(n,r)){var a=t.functionExpression(null,[],t.cloneNode(n.body,!1),n.generator,n.async);e.get("body").set("body",[t.returnStatement(t.callExpression(a,[]))]),n.async=!1,n.generator=!1,e.get("body.body.0.argument.callee").unwrapFunctionEnvironment()}},Function:{exit:VR.wrapWithTypes(t,(function(e,r){var n=e.node;if(WR(n,r)){n=(e=(0,GR.default)(e)).node;var a=e.scope.generateUidIdentifier("context"),s=e.scope.generateUidIdentifier("args");e.ensureBlock();var i=e.get("body");n.async&&i.traverse(zR),i.traverse(KR,{context:a});var o=[],u=[];i.get("body").forEach((function(e){var r=e.node;t.isExpressionStatement(r)&&t.isStringLiteral(r.expression)||r&&null!=r._blockHoist?o.push(r):u.push(r)})),o.length>0&&(i.node.body=u);var c=function(e){var t=VR.getTypes(),r=e.node;t.assertFunction(r),r.id||(r.id=e.scope.parent.generateUidIdentifier("callee"));if(r.generator&&t.isFunctionDeclaration(r))return function(e){var t=VR.getTypes(),r=e.node;t.assertIdentifier(r.id);var n=e.findParent((function(e){return e.isProgram()||e.isBlockStatement()}));if(!n)return r.id;var a=n.node;UR.default.ok(Array.isArray(a.body));var s=HR(a);s.decl||(s.decl=t.variableDeclaration("var",[]),n.unshiftContainer("body",s.decl),s.declPath=n.get("body.0"));UR.default.strictEqual(s.declPath.node,s.decl);var i=n.scope.generateUidIdentifier("marked"),o=t.callExpression(VR.runtimeProperty("mark"),[t.clone(r.id)]),u=s.decl.declarations.push(t.variableDeclarator(i,o))-1,c=s.declPath.get("declarations."+u+".init");return UR.default.strictEqual(c.node,o),c.addComment("leading","#__PURE__"),t.clone(i)}(e);return t.clone(r.id)}(e);t.assertIdentifier(n.id);var l=t.identifier(n.id.name+"$"),p=ON(e),d={usesThis:!1,usesArguments:!1,getArgsId:function(){return t.clone(s)}};if(e.traverse(qR,d),d.usesArguments){p=p||t.variableDeclaration("var",[]);var f=t.identifier("arguments");f._shadowedFunctionLiteral=e,p.declarations.push(t.variableDeclarator(t.clone(s),f))}var h=new MR.Emitter(a);h.explode(e.get("body")),p&&p.declarations.length>0&&o.push(p);var m=[h.getContextFunction(l)],y=h.getTryLocsList();if(n.generator?m.push(c):(d.usesThis||y||n.async)&&m.push(t.nullLiteral()),d.usesThis?m.push(t.thisExpression()):(y||n.async)&&m.push(t.nullLiteral()),y?m.push(y):n.async&&m.push(t.nullLiteral()),n.async){var g=e.scope;do{g.hasOwnBinding("Promise")&&g.rename("Promise")}while(g=g.parent);m.push(t.identifier("Promise"))}var v=t.callExpression(VR.runtimeProperty(n.async?"async":"wrap"),m);o.push(t.returnStatement(v)),n.body=t.blockStatement(o),e.get("body.body").forEach((function(e){return e.scope.registerDeclaration(e)}));var b=i.node.directives;b&&(n.body.directives=b);var x=n.generator;x&&(n.generator=!1),n.async&&(n.async=!1),x&&t.isExpression(n)&&(VR.replaceWithOrRemove(e,t.callExpression(VR.runtimeProperty("mark"),[n])),e.addComment("leading","#__PURE__"));var E=h.getInsertedLocs();e.traverse({NumericLiteral:function(e){E.has(e.node)&&e.replaceWith(t.numericLiteral(e.node.value))}}),e.requeue()}}))}}},YR=Gt(Vt((function(e,t){t.__esModule=!0,t.default=function(e){var t={visitor:XR(e)},r=e&&e.version;r&&parseInt(r,10)>=7&&(t.name="regenerator-transform");return t}}))),JR=sP((function(e){return e.assertVersion(7),{name:"transform-reserved-words",visitor:{"BindingIdentifier|ReferencedIdentifier":function(e){lf(e.node.name)||e.scope.rename(e.node.name)}}}})),$R=function(){var e=Error.prepareStackTrace;Error.prepareStackTrace=function(e,t){return t};var t=(new Error).stack;return Error.prepareStackTrace=e,t[2].getFileName()},QR=Vt((function(e){var t="win32"===es.platform,r=/^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/,n=/^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/,a={};a.parse=function(e){if("string"!=typeof e)throw new TypeError("Parameter 'pathString' must be a string, not "+typeof e);var t,a,s,i,o,u=(t=e,a=r.exec(t),s=(a[1]||"")+(a[2]||""),i=a[3]||"",o=n.exec(i),[s,o[1],o[2],o[3]]);if(!u||4!==u.length)throw new TypeError("Invalid path '"+e+"'");return{root:u[0],dir:u[0]+u[1].slice(0,-1),base:u[2],ext:u[3],name:u[2].slice(0,u[2].length-u[3].length)}};var s=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/,i={};i.parse=function(e){if("string"!=typeof e)throw new TypeError("Parameter 'pathString' must be a string, not "+typeof e);var t,r=(t=e,s.exec(t).slice(1));if(!r||4!==r.length)throw new TypeError("Invalid path '"+e+"'");return r[1]=r[1]||"",r[2]=r[2]||"",r[3]=r[3]||"",{root:r[0],dir:r[0]+r[1].slice(0,-1),base:r[2],ext:r[3],name:r[2].slice(0,r[2].length-r[3].length)}},e.exports=t?a.parse:i.parse,e.exports.posix=i.parse,e.exports.win32=a.parse})),ZR=(QR.posix,QR.win32,yS.parse||QR),eM=function(e,t){var r="/";/^([A-Za-z]:)/.test(e)?r="":/^\\\\/.test(e)&&(r="\\\\");for(var n=[e],a=ZR(e);a.dir!==n[n.length-1];)n.push(a.dir),a=ZR(a.dir);return n.reduce((function(e,n){return e.concat(t.map((function(e){return yS.resolve(r,n,e)})))}),[])},tM=function(e,t,r){var n=t&&t.moduleDirectory?[].concat(t.moduleDirectory):["node_modules"];if(t&&"function"==typeof t.paths)return t.paths(r,e,(function(){return eM(e,n)}),t);var a=eM(e,n);return t&&t.paths?a.concat(t.paths):a},rM=function(e,t){return t||{}},nM=">= 13.4 && < 13.5",aM={assert:!0,async_hooks:">= 8",buffer_ieee754:"< 0.9.7",buffer:!0,child_process:!0,cluster:!0,console:!0,constants:!0,crypto:!0,_debug_agent:">= 1 && < 8",_debugger:"< 8",dgram:!0,dns:!0,domain:!0,events:!0,freelist:"< 6",fs:!0,"fs/promises":">= 10 && < 10.1",_http_agent:">= 0.11.1",_http_client:">= 0.11.1",_http_common:">= 0.11.1",_http_incoming:">= 0.11.1",_http_outgoing:">= 0.11.1",_http_server:">= 0.11.1",http:!0,http2:">= 8.8",https:!0,inspector:">= 8.0.0",_linklist:"< 8",module:!0,net:!0,"node-inspect/lib/_inspect":">= 7.6.0 && < 12","node-inspect/lib/internal/inspect_client":">= 7.6.0 && < 12","node-inspect/lib/internal/inspect_repl":">= 7.6.0 && < 12",os:!0,path:!0,perf_hooks:">= 8.5",process:">= 1",punycode:!0,querystring:!0,readline:!0,repl:!0,smalloc:">= 0.11.5 && < 3",_stream_duplex:">= 0.9.4",_stream_transform:">= 0.9.4",_stream_wrap:">= 1.4.1",_stream_passthrough:">= 0.9.4",_stream_readable:">= 0.9.4",_stream_writable:">= 0.9.4",stream:!0,string_decoder:!0,sys:!0,timers:!0,_tls_common:">= 0.11.13",_tls_legacy:">= 0.11.3 && < 10",_tls_wrap:">= 0.11.3",tls:!0,trace_events:">= 10",tty:!0,url:!0,util:!0,"v8/tools/arguments":">= 10 && < 12","v8/tools/codemap":[">= 4.4.0 && < 5",">= 5.2.0 && < 12"],"v8/tools/consarray":[">= 4.4.0 && < 5",">= 5.2.0 && < 12"],"v8/tools/csvparser":[">= 4.4.0 && < 5",">= 5.2.0 && < 12"],"v8/tools/logreader":[">= 4.4.0 && < 5",">= 5.2.0 && < 12"],"v8/tools/profile_view":[">= 4.4.0 && < 5",">= 5.2.0 && < 12"],"v8/tools/splaytree":[">= 4.4.0 && < 5",">= 5.2.0 && < 12"],v8:">= 1",vm:!0,wasi:nM,worker_threads:">= 11.7",zlib:!0},sM=Wt(Object.freeze({__proto__:null,assert:!0,async_hooks:">= 8",buffer_ieee754:"< 0.9.7",buffer:!0,child_process:!0,cluster:!0,console:!0,constants:!0,crypto:!0,_debug_agent:">= 1 && < 8",_debugger:"< 8",dgram:!0,dns:!0,domain:!0,events:!0,freelist:"< 6",fs:!0,_http_agent:">= 0.11.1",_http_client:">= 0.11.1",_http_common:">= 0.11.1",_http_incoming:">= 0.11.1",_http_outgoing:">= 0.11.1",_http_server:">= 0.11.1",http:!0,http2:">= 8.8",https:!0,inspector:">= 8.0.0",_linklist:"< 8",module:!0,net:!0,os:!0,path:!0,perf_hooks:">= 8.5",process:">= 1",punycode:!0,querystring:!0,readline:!0,repl:!0,smalloc:">= 0.11.5 && < 3",_stream_duplex:">= 0.9.4",_stream_transform:">= 0.9.4",_stream_wrap:">= 1.4.1",_stream_passthrough:">= 0.9.4",_stream_readable:">= 0.9.4",_stream_writable:">= 0.9.4",stream:!0,string_decoder:!0,sys:!0,timers:!0,_tls_common:">= 0.11.13",_tls_legacy:">= 0.11.3 && < 10",_tls_wrap:">= 0.11.3",tls:!0,trace_events:">= 10",tty:!0,url:!0,util:!0,v8:">= 1",vm:!0,wasi:nM,worker_threads:">= 11.7",zlib:!0,default:aM})),iM=es.versions&&es.versions.node&&es.versions.node.split(".")||[];function oM(e){for(var t=e.split(" "),r=t.length>1?t[0]:"=",n=(t.length>1?t[1]:t[0]).split("."),a=0;a<3;++a){var s=Number(iM[a]||0),i=Number(n[a]||0);if(s!==i)return"<"===r?s<i:">="===r&&s>=i}return">="===r}function uM(e){var t=e.split(/ ?&& ?/);if(0===t.length)return!1;for(var r=0;r<t.length;++r)if(!oM(t[r]))return!1;return!0}function cM(e){if("boolean"==typeof e)return e;if(e&&"object"==typeof e){for(var t=0;t<e.length;++t)if(uM(e[t]))return!0;return!1}return uM(e)}var lM={};for(var pM in sM)Object.prototype.hasOwnProperty.call(sM,pM)&&(lM[pM]=cM(sM[pM]));var dM=lM,fM=function(e){return Object.prototype.hasOwnProperty.call(dM,e)},hM=function(e,t){hj.stat(e,(function(e,r){return e?"ENOENT"===e.code||"ENOTDIR"===e.code?t(null,!1):t(e):t(null,r.isFile()||r.isFIFO())}))},mM=function(e,t){hj.stat(e,(function(e,r){return e?"ENOENT"===e.code||"ENOTDIR"===e.code?t(null,!1):t(e):t(null,r.isDirectory())}))},yM=function(e,t,r){t&&!1===t.preserveSymlinks?hj.realpath(e,(function(t,n){t&&"ENOENT"!==t.code?r(t):r(null,t?e:n)})):r(null,e)},gM=function(e,t,r){var n=r,a=t;if("function"==typeof t&&(n=a,a={}),"string"!=typeof e){var s=new TypeError("Path must be a string.");return Ga((function(){n(s)}))}var i=(a=rM(0,a)).isFile||hM,o=a.isDirectory||mM,u=a.readFile||hj.readFile,c=a.packageIterator,l=a.extensions||[".js"],p=a.basedir||yS.dirname($R()),d=a.filename||p;a.paths=a.paths||[];var f,h=yS.resolve(p);function m(t,r,s){t?n(t):r?n(null,r,s):g(f,(function(t,r,s){if(t)n(t);else if(r)yM(r,a,(function(e,t){e?n(e):n(null,t,s)}));else{var i=new Error("Cannot find module '"+e+"' from '"+d+"'");i.code="MODULE_NOT_FOUND",n(i)}}))}function y(e,t,r){var n=t,s=r;"function"==typeof n&&(s=n,n=void 0),function e(t,r,n){if(0===t.length)return s(null,void 0,n);var o=r+t[0],c=n;c?p(null,c):function e(t,r){if(""===t||"/"===t)return r(null);if("win32"===es.platform&&/^\w:[/\\]*$/.test(t))return r(null);if(/[/\\]node_modules[/\\]*$/.test(t))return r(null);yM(t,a,(function(n,s){if(n)return e(yS.dirname(t),r);var o=yS.join(s,"package.json");i(o,(function(n,s){if(!s)return e(yS.dirname(t),r);u(o,(function(e,n){e&&r(e);try{var s=JSON.parse(n)}catch(e){}s&&a.packageFilter&&(s=a.packageFilter(s,o)),r(null,s,t)}))}))}))}(yS.dirname(o),p);function p(n,u,p){if(c=u,n)return s(n);if(p&&c&&a.pathFilter){var f=yS.relative(p,o),h=f.slice(0,f.length-t[0].length),m=a.pathFilter(c,r,h);if(m)return e([""].concat(l.slice()),yS.resolve(p,m),c)}i(o,d)}function d(n,a){return n?s(n):a?s(null,o,c):void e(t.slice(1),r,c)}}([""].concat(l),e,n)}function g(e,t,r){var n=r,s=t;"function"==typeof s&&(n=s,s=a.package),yM(e,a,(function(t,r){if(t)return n(t);var o=yS.join(r,"package.json");i(o,(function(t,r){return t?n(t):r?void u(o,(function(t,r){if(t)return n(t);try{var s=JSON.parse(r)}catch(e){}if(s&&a.packageFilter&&(s=a.packageFilter(s,o)),s&&s.main){if("string"!=typeof s.main){var i=new TypeError("package “"+s.name+"” `main` must be a string");return i.code="INVALID_PACKAGE_MAIN",n(i)}return"."!==s.main&&"./"!==s.main||(s.main="index"),void y(yS.resolve(e,s.main),s,(function(t,r,a){return t?n(t):r?n(null,r,a):a?void g(yS.resolve(e,a.main),a,(function(t,r,a){return t?n(t):r?n(null,r,a):void y(yS.join(e,"index"),a,n)})):y(yS.join(e,"index"),a,n)}))}y(yS.join(e,"/index"),s,n)})):y(yS.join(e,"index"),s,n)}))}))}yM(h,a,(function(t,r){t?n(t):function(t){if(/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/.test(e))f=yS.resolve(t,e),"."!==e&&".."!==e&&"/"!==e.slice(-1)||(f+="/"),/\/$/.test(e)&&f===t?g(f,a.package,m):y(f,a.package,m);else{if(fM(e))return n(null,e);!function(e,t,r){var n=function(){return function(e,t,r){for(var n=tM(t,r,e),a=0;a<n.length;a++)n[a]=yS.join(n[a],e);return n}(e,t,a)};!function e(t,r){if(0===r.length)return t(null,void 0);var n=r[0];function s(s,o){return s?t(s):o?void y(n,a.package,i):e(t,r.slice(1))}function i(e,r,s){return e?t(e):r?t(null,r,s):void g(n,a.package,u)}function u(n,a,s){return n?t(n):a?t(null,a,s):void e(t,r.slice(1))}o(yS.dirname(n),s)}(r,c?c(e,t,n,a):n())}(e,t,(function(t,r,s){if(t)n(t);else{if(r)return yM(r,a,(function(e,t){e?n(e):n(null,t,s)}));var i=new Error("Cannot find module '"+e+"' from '"+d+"'");i.code="MODULE_NOT_FOUND",n(i)}}))}}(r)}))},vM=function(e){try{var t=hj.statSync(e)}catch(e){if(e&&("ENOENT"===e.code||"ENOTDIR"===e.code))return!1;throw e}return t.isFile()||t.isFIFO()},bM=function(e){try{var t=hj.statSync(e)}catch(e){if(e&&("ENOENT"===e.code||"ENOTDIR"===e.code))return!1;throw e}return t.isDirectory()},xM=function(e,t){if(t&&!1===t.preserveSymlinks)try{return hj.realpathSync(e)}catch(e){if("ENOENT"!==e.code)throw e}return e};gM.core=dM,gM.isCore=fM,gM.sync=function(e,t){if("string"!=typeof e)throw new TypeError("Path must be a string.");var r=rM(0,t),n=r.isFile||vM,a=r.readFileSync||hj.readFileSync,s=r.isDirectory||bM,i=r.packageIterator,o=r.extensions||[".js"],u=r.basedir||yS.dirname($R()),c=r.filename||u;r.paths=r.paths||[];var l=xM(yS.resolve(u),r);if(/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/.test(e)){var p=yS.resolve(l,e);"."!==e&&".."!==e&&"/"!==e.slice(-1)||(p+="/");var d=m(p)||y(p);if(d)return xM(d,r)}else{if(fM(e))return e;var f=function(e,t){for(var n=function(){return function(e,t,r){for(var n=tM(t,r,e),a=0;a<n.length;a++)n[a]=yS.join(n[a],e);return n}(e,t,r)},a=i?i(e,t,n,r):n(),o=0;o<a.length;o++){var u=a[o];if(s(yS.dirname(u))){var c=m(u);if(c)return c;var l=y(u);if(l)return l}}}(e,l);if(f)return xM(f,r)}var h=new Error("Cannot find module '"+e+"' from '"+c+"'");throw h.code="MODULE_NOT_FOUND",h;function m(e){var t=function e(t){if(""===t||"/"===t)return;if("win32"===es.platform&&/^\w:[/\\]*$/.test(t))return;if(/[/\\]node_modules[/\\]*$/.test(t))return;var s=yS.join(xM(t,r),"package.json");if(!n(s))return e(yS.dirname(t));var i=a(s);try{var o=JSON.parse(i)}catch(e){}o&&r.packageFilter&&(o=r.packageFilter(o,t));return{pkg:o,dir:t}}(yS.dirname(e));if(t&&t.dir&&t.pkg&&r.pathFilter){var s=yS.relative(t.dir,e),i=r.pathFilter(t.pkg,e,s);i&&(e=yS.resolve(t.dir,i))}if(n(e))return e;for(var u=0;u<o.length;u++){var c=e+o[u];if(n(c))return c}}function y(e){var t=yS.join(xM(e,r),"/package.json");if(n(t)){try{var s=a(t,"UTF8"),i=JSON.parse(s)}catch(e){}if(i&&r.packageFilter&&(i=r.packageFilter(i,e)),i&&i.main){if("string"!=typeof i.main){var o=new TypeError("package “"+i.name+"” `main` must be a string");throw o.code="INVALID_PACKAGE_MAIN",o}"."!==i.main&&"./"!==i.main||(i.main="index");try{var u=m(yS.resolve(e,i.main));if(u)return u;var c=y(yS.resolve(e,i.main));if(c)return c}catch(e){}}}return m(yS.join(e,"/index"))}};var EM=gM;var AM=function(e){var t=function(e,t){return!t||(Iw.valid(t)&&(t="^"+t),!Iw.intersects("<"+e,t)&&!Iw.intersects(">=8.0.0",t))}("7.0.1",e);return{BuiltIns:{Symbol:{stable:!0,path:"symbol"},Promise:{stable:!0,path:"promise"},Map:{stable:!0,path:"map"},WeakMap:{stable:!0,path:"weak-map"},Set:{stable:!0,path:"set"},WeakSet:{stable:!0,path:"weak-set"},setImmediate:{stable:!0,path:"set-immediate"},clearImmediate:{stable:!0,path:"clear-immediate"},parseFloat:{stable:!0,path:"parse-float"},parseInt:{stable:!0,path:"parse-int"}},StaticProperties:Object.assign({Array:{from:{stable:!0,path:"array/from"},isArray:{stable:!0,path:"array/is-array"},of:{stable:!0,path:"array/of"}},JSON:{stringify:{stable:!0,path:"json/stringify"}},Object:{assign:{stable:!0,path:"object/assign"},create:{stable:!0,path:"object/create"},defineProperties:{stable:!0,path:"object/define-properties"},defineProperty:{stable:!0,path:"object/define-property"},entries:{stable:!0,path:"object/entries"},freeze:{stable:!0,path:"object/freeze"},getOwnPropertyDescriptor:{stable:!0,path:"object/get-own-property-descriptor"},getOwnPropertyDescriptors:{stable:!0,path:"object/get-own-property-descriptors"},getOwnPropertyNames:{stable:!0,path:"object/get-own-property-names"},getOwnPropertySymbols:{stable:!0,path:"object/get-own-property-symbols"},getPrototypeOf:{stable:!0,path:"object/get-prototype-of"},isExtensible:{stable:!0,path:"object/is-extensible"},isFrozen:{stable:!0,path:"object/is-frozen"},isSealed:{stable:!0,path:"object/is-sealed"},is:{stable:!0,path:"object/is"},keys:{stable:!0,path:"object/keys"},preventExtensions:{stable:!0,path:"object/prevent-extensions"},seal:{stable:!0,path:"object/seal"},setPrototypeOf:{stable:!0,path:"object/set-prototype-of"},values:{stable:!0,path:"object/values"}}},t?{Math:{acosh:{stable:!0,path:"math/acosh"},asinh:{stable:!0,path:"math/asinh"},atanh:{stable:!0,path:"math/atanh"},cbrt:{stable:!0,path:"math/cbrt"},clz32:{stable:!0,path:"math/clz32"},cosh:{stable:!0,path:"math/cosh"},expm1:{stable:!0,path:"math/expm1"},fround:{stable:!0,path:"math/fround"},hypot:{stable:!0,path:"math/hypot"},imul:{stable:!0,path:"math/imul"},log10:{stable:!0,path:"math/log10"},log1p:{stable:!0,path:"math/log1p"},log2:{stable:!0,path:"math/log2"},sign:{stable:!0,path:"math/sign"},sinh:{stable:!0,path:"math/sinh"},tanh:{stable:!0,path:"math/tanh"},trunc:{stable:!0,path:"math/trunc"}}}:{},{Symbol:{for:{stable:!0,path:"symbol/for"},hasInstance:{stable:!0,path:"symbol/has-instance"},isConcatSpreadable:{stable:!0,path:"symbol/is-concat-spreadable"},iterator:{stable:!0,path:"symbol/iterator"},keyFor:{stable:!0,path:"symbol/key-for"},match:{stable:!0,path:"symbol/match"},replace:{stable:!0,path:"symbol/replace"},search:{stable:!0,path:"symbol/search"},species:{stable:!0,path:"symbol/species"},split:{stable:!0,path:"symbol/split"},toPrimitive:{stable:!0,path:"symbol/to-primitive"},toStringTag:{stable:!0,path:"symbol/to-string-tag"},unscopables:{stable:!0,path:"symbol/unscopables"}},String:{at:{stable:!0,path:"string/at"},fromCodePoint:{stable:!0,path:"string/from-code-point"},raw:{stable:!0,path:"string/raw"}},Number:{EPSILON:{stable:!0,path:"number/epsilon"},isFinite:{stable:!0,path:"number/is-finite"},isInteger:{stable:!0,path:"number/is-integer"},isNaN:{stable:!0,path:"number/is-nan"},isSafeInteger:{stable:!0,path:"number/is-safe-integer"},MAX_SAFE_INTEGER:{stable:!0,path:"number/max-safe-integer"},MIN_SAFE_INTEGER:{stable:!0,path:"number/min-safe-integer"},parseFloat:{stable:!0,path:"number/parse-float"},parseInt:{stable:!0,path:"number/parse-int"}},Reflect:{apply:{stable:!0,path:"reflect/apply"},construct:{stable:!0,path:"reflect/construct"},defineProperty:{stable:!0,path:"reflect/define-property"},deleteProperty:{stable:!0,path:"reflect/delete-property"},getOwnPropertyDescriptor:{stable:!0,path:"reflect/get-own-property-descriptor"},getPrototypeOf:{stable:!0,path:"reflect/get-prototype-of"},get:{stable:!0,path:"reflect/get"},has:{stable:!0,path:"reflect/has"},isExtensible:{stable:!0,path:"reflect/is-extensible"},ownKeys:{stable:!0,path:"reflect/own-keys"},preventExtensions:{stable:!0,path:"reflect/prevent-extensions"},setPrototypeOf:{stable:!0,path:"reflect/set-prototype-of"},set:{stable:!0,path:"reflect/set"}},Date:{now:{stable:!0,path:"date/now"}}})}},wM=function(){return{BuiltIns:{AggregateError:{stable:!1,path:"aggregate-error"},Map:{stable:!0,path:"map"},Observable:{stable:!1,path:"observable"},Promise:{stable:!0,path:"promise"},Set:{stable:!0,path:"set"},Symbol:{stable:!0,path:"symbol"},URL:{stable:!0,path:"url"},URLSearchParams:{stable:!0,path:"url-search-params"},WeakMap:{stable:!0,path:"weak-map"},WeakSet:{stable:!0,path:"weak-set"},clearImmediate:{stable:!0,path:"clear-immediate"},compositeKey:{stable:!1,path:"composite-key"},compositeSymbol:{stable:!1,path:"composite-symbol"},globalThis:{stable:!1,path:"global-this"},parseFloat:{stable:!0,path:"parse-float"},parseInt:{stable:!0,path:"parse-int"},queueMicrotask:{stable:!0,path:"queue-microtask"},setImmediate:{stable:!0,path:"set-immediate"},setInterval:{stable:!0,path:"set-interval"},setTimeout:{stable:!0,path:"set-timeout"}},StaticProperties:{Array:{from:{stable:!0,path:"array/from"},isArray:{stable:!0,path:"array/is-array"},of:{stable:!0,path:"array/of"}},Date:{now:{stable:!0,path:"date/now"}},JSON:{stringify:{stable:!0,path:"json/stringify"}},Math:{DEG_PER_RAD:{stable:!1,path:"math/deg-per-rad"},RAD_PER_DEG:{stable:!1,path:"math/rad-per-deg"},acosh:{stable:!0,path:"math/acosh"},asinh:{stable:!0,path:"math/asinh"},atanh:{stable:!0,path:"math/atanh"},cbrt:{stable:!0,path:"math/cbrt"},clamp:{stable:!1,path:"math/clamp"},clz32:{stable:!0,path:"math/clz32"},cosh:{stable:!0,path:"math/cosh"},degrees:{stable:!1,path:"math/degrees"},expm1:{stable:!0,path:"math/expm1"},fround:{stable:!0,path:"math/fround"},fscale:{stable:!1,path:"math/fscale"},hypot:{stable:!0,path:"math/hypot"},iaddh:{stable:!1,path:"math/iaddh"},imul:{stable:!0,path:"math/imul"},imulh:{stable:!1,path:"math/imulh"},isubh:{stable:!1,path:"math/isubh"},log10:{stable:!0,path:"math/log10"},log1p:{stable:!0,path:"math/log1p"},log2:{stable:!0,path:"math/log2"},radians:{stable:!1,path:"math/radians"},scale:{stable:!1,path:"math/scale"},seededPRNG:{stable:!1,path:"math/seeded-prng"},sign:{stable:!0,path:"math/sign"},signbit:{stable:!1,path:"math/signbit"},sinh:{stable:!0,path:"math/sinh"},tanh:{stable:!0,path:"math/tanh"},trunc:{stable:!0,path:"math/trunc"},umulh:{stable:!1,path:"math/umulh"}},Number:{EPSILON:{stable:!0,path:"number/epsilon"},MAX_SAFE_INTEGER:{stable:!0,path:"number/max-safe-integer"},MIN_SAFE_INTEGER:{stable:!0,path:"number/min-safe-integer"},fromString:{stable:!1,path:"number/from-string"},isFinite:{stable:!0,path:"number/is-finite"},isInteger:{stable:!0,path:"number/is-integer"},isNaN:{stable:!0,path:"number/is-nan"},isSafeInteger:{stable:!0,path:"number/is-safe-integer"},parseFloat:{stable:!0,path:"number/parse-float"},parseInt:{stable:!0,path:"number/parse-int"}},Object:{assign:{stable:!0,path:"object/assign"},create:{stable:!0,path:"object/create"},defineProperties:{stable:!0,path:"object/define-properties"},defineProperty:{stable:!0,path:"object/define-property"},entries:{stable:!0,path:"object/entries"},freeze:{stable:!0,path:"object/freeze"},fromEntries:{stable:!0,path:"object/from-entries"},getOwnPropertyDescriptor:{stable:!0,path:"object/get-own-property-descriptor"},getOwnPropertyDescriptors:{stable:!0,path:"object/get-own-property-descriptors"},getOwnPropertyNames:{stable:!0,path:"object/get-own-property-names"},getOwnPropertySymbols:{stable:!0,path:"object/get-own-property-symbols"},getPrototypeOf:{stable:!0,path:"object/get-prototype-of"},isExtensible:{stable:!0,path:"object/is-extensible"},isFrozen:{stable:!0,path:"object/is-frozen"},isSealed:{stable:!0,path:"object/is-sealed"},is:{stable:!0,path:"object/is"},keys:{stable:!0,path:"object/keys"},preventExtensions:{stable:!0,path:"object/prevent-extensions"},seal:{stable:!0,path:"object/seal"},setPrototypeOf:{stable:!0,path:"object/set-prototype-of"},values:{stable:!0,path:"object/values"}},Reflect:{apply:{stable:!0,path:"reflect/apply"},construct:{stable:!0,path:"reflect/construct"},defineMetadata:{stable:!1,path:"reflect/define-metadata"},defineProperty:{stable:!0,path:"reflect/define-property"},deleteMetadata:{stable:!1,path:"reflect/delete-metadata"},deleteProperty:{stable:!0,path:"reflect/delete-property"},getMetadata:{stable:!1,path:"reflect/get-metadata"},getMetadataKeys:{stable:!1,path:"reflect/get-metadata-keys"},getOwnMetadata:{stable:!1,path:"reflect/get-own-metadata"},getOwnMetadataKeys:{stable:!1,path:"reflect/get-own-metadata-keys"},getOwnPropertyDescriptor:{stable:!0,path:"reflect/get-own-property-descriptor"},getPrototypeOf:{stable:!0,path:"reflect/get-prototype-of"},get:{stable:!0,path:"reflect/get"},has:{stable:!0,path:"reflect/has"},hasMetadata:{stable:!1,path:"reflect/has-metadata"},hasOwnMetadata:{stable:!1,path:"reflect/has-own-metadata"},isExtensible:{stable:!0,path:"reflect/is-extensible"},metadata:{stable:!1,path:"reflect/metadata"},ownKeys:{stable:!0,path:"reflect/own-keys"},preventExtensions:{stable:!0,path:"reflect/prevent-extensions"},set:{stable:!0,path:"reflect/set"},setPrototypeOf:{stable:!0,path:"reflect/set-prototype-of"}},String:{fromCodePoint:{stable:!0,path:"string/from-code-point"},raw:{stable:!0,path:"string/raw"}},Symbol:{asyncIterator:{stable:!0,path:"symbol/async-iterator"},dispose:{stable:!1,path:"symbol/dispose"},for:{stable:!0,path:"symbol/for"},hasInstance:{stable:!0,path:"symbol/has-instance"},isConcatSpreadable:{stable:!0,path:"symbol/is-concat-spreadable"},iterator:{stable:!0,path:"symbol/iterator"},keyFor:{stable:!0,path:"symbol/key-for"},match:{stable:!0,path:"symbol/match"},observable:{stable:!1,path:"symbol/observable"},patternMatch:{stable:!1,path:"symbol/pattern-match"},replace:{stable:!0,path:"symbol/replace"},search:{stable:!0,path:"symbol/search"},species:{stable:!0,path:"symbol/species"},split:{stable:!0,path:"symbol/split"},toPrimitive:{stable:!0,path:"symbol/to-primitive"},toStringTag:{stable:!0,path:"symbol/to-string-tag"},unscopables:{stable:!0,path:"symbol/unscopables"}}},InstanceProperties:{at:{stable:!1,path:"at"},bind:{stable:!0,path:"bind"},codePointAt:{stable:!0,path:"code-point-at"},codePoints:{stable:!1,path:"code-points"},concat:{stable:!0,path:"concat",types:["array"]},copyWithin:{stable:!0,path:"copy-within"},endsWith:{stable:!0,path:"ends-with"},entries:{stable:!0,path:"entries"},every:{stable:!0,path:"every"},fill:{stable:!0,path:"fill"},filter:{stable:!0,path:"filter"},find:{stable:!0,path:"find"},findIndex:{stable:!0,path:"find-index"},flags:{stable:!0,path:"flags"},flatMap:{stable:!0,path:"flat-map"},flat:{stable:!0,path:"flat"},forEach:{stable:!0,path:"for-each"},includes:{stable:!0,path:"includes"},indexOf:{stable:!0,path:"index-of"},keys:{stable:!0,path:"keys"},lastIndexOf:{stable:!0,path:"last-index-of"},map:{stable:!0,path:"map"},matchAll:{stable:!1,path:"match-all"},padEnd:{stable:!0,path:"pad-end"},padStart:{stable:!0,path:"pad-start"},reduce:{stable:!0,path:"reduce"},reduceRight:{stable:!0,path:"reduce-right"},repeat:{stable:!0,path:"repeat"},replaceAll:{stable:!1,path:"replace-all"},reverse:{stable:!0,path:"reverse"},slice:{stable:!0,path:"slice"},some:{stable:!0,path:"some"},sort:{stable:!0,path:"sort"},splice:{stable:!0,path:"splice"},startsWith:{stable:!0,path:"starts-with"},trim:{stable:!0,path:"trim"},trimEnd:{stable:!0,path:"trim-end"},trimLeft:{stable:!0,path:"trim-left"},trimRight:{stable:!0,path:"trim-right"},trimStart:{stable:!0,path:"trim-start"},values:{stable:!0,path:"values"}}}};function SM(e){return!(!e||!e.supportsStaticESM)}var DM=sP((function(e,t,r){e.assertVersion(7);var n,a=t.corejs,s=t.helpers,i=void 0===s||s,o=t.regenerator,u=void 0===o||o,c=t.useESModules,l=void 0!==c&&c,p=t.version,d=void 0===p?"7.0.0-beta.0":p,f=t.absoluteRuntime,h=void 0!==f&&f,m=!1;"object"==typeof a&&null!==a?(n=a.version,m=Boolean(a.proposals)):n=a;var y=!!n&&Number(n);if(![!1,2,3].includes(y))throw new Error("The `core-js` version must be false, 2 or 3, but got "+JSON.stringify(n)+".");if(m&&(!y||y<3))throw new Error("The 'proposals' option is only supported when using 'corejs: 3'");if("boolean"!=typeof u)throw new Error("The 'regenerator' option must be undefined, or a boolean.");if("boolean"!=typeof i)throw new Error("The 'helpers' option must be undefined, or a boolean.");if("boolean"!=typeof l&&"auto"!==l)throw new Error("The 'useESModules' option must be undefined, or a boolean, or 'auto'.");if("boolean"!=typeof h&&"string"!=typeof h)throw new Error("The 'absoluteRuntime' option must be undefined, a boolean, or a string.");if("string"!=typeof d)throw new Error("The 'version' option must be a version string.");function g(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function v(e,t){return g(e,t)&&(m||e[t].stable)}function b(e,t){return g(F,e)&&v(F[e],t)}function x(e,t,r){if(function(e){var t=e.scope.getBinding(e.node.name);return!!t&&t.path.isImportNamespaceSpecifier()}(e.get("object")))return!1;if(!t[r].types)return!0;var n=function(e){switch(e.type){case"GenericTypeAnnotation":if(S(e.id,{name:"Array"}))return"array";break;case"StringTypeAnnotation":return"string"}}(e.get("object").getTypeAnnotation());return!n||t[r].types.some((function(e){return e===n}))}function E(e,t){var r=e.node;return t?e.isStringLiteral()?r.value:e.evaluate().value:r.name}if(g(t,"useBuiltIns"))throw t.useBuiltIns?new Error("The 'useBuiltIns' option has been removed. The @babel/runtime module now uses builtins by default."):new Error("The 'useBuiltIns' option has been removed. Use the 'corejs'option to polyfill with `core-js` via @babel/runtime.");if(g(t,"polyfill"))throw!1===t.polyfill?new Error("The 'polyfill' option has been removed. The @babel/runtime module now skips polyfilling by default."):new Error("The 'polyfill' option has been removed. Use the 'corejs'option to polyfill with `core-js` via @babel/runtime.");if(g(t,"moduleName"))throw new Error("The 'moduleName' option has been removed. @babel/transform-runtime no longer supports arbitrary runtimes. If you were using this to set an absolute path for Babel's standard runtimes, please use the 'absoluteRuntime' option.");var A="auto"===l?e.caller(SM):l,w=2===y,D=3===y,C=!1!==y,T=D?"@babel/runtime-corejs3":w?"@babel/runtime-corejs2":"@babel/runtime",j=D&&!m?"core-js-stable":"core-js",P=(w?AM:wM)(d),k=P.BuiltIns,F=P.StaticProperties,I=P.InstanceProperties,B=["interopRequireWildcard","interopRequireDefault"],O=T;return!1!==h&&(O=function(e,t){try{return yS.dirname(EM.sync(e+"/package.json",{basedir:t}))}catch(r){if("MODULE_NOT_FOUND"!==r.code)throw r;throw Object.assign(new Error('Failed to resolve "'+e+'" relative to "'+t+'"'),{code:"BABEL_RUNTIME_NOT_FOUND",runtime:e,dirname:t})}}(T,yS.resolve(r,!0===h?".":h))),{name:"transform-runtime",pre:function(e){var t=this;i&&e.set("helperGenerator",(function(r){if(!e.availableHelper||e.availableHelper(r,d)){var n=-1!==B.indexOf(r)&&!gI(e.path)?4:void 0,a=A&&"module"===e.path.node.sourceType?"helpers/esm":"helpers";return t.addDefaultImport(O+"/"+a+"/"+r,r,n)}}));var r=new Map;this.addDefaultImport=function(t,n,a){var s=t+":"+n+":"+(gI(e.path)||""),i=r.get(s);return i?i=pp(i):(i=function(e,t,r){return new vI(e).addDefault(t,r)}(e.path,t,{importedInterop:"uncompiled",nameHint:n,blockHoist:a}),r.set(s,i)),i}},visitor:{ReferencedIdentifier:function(e){var t=e.node,r=e.parent,n=e.scope,a=t.name;"regeneratorRuntime"===a&&u?e.replaceWith(this.addDefaultImport(O+"/regenerator","regeneratorRuntime")):C&&(_(r)||v(k,a)&&(n.getBindingIdentifier(a)||e.replaceWith(this.addDefaultImport(O+"/"+j+"/"+k[a].path,a))))},CallExpression:function(e){if(C){var t=e.node,r=t.callee;if(_(r)){var n,a,s=r.object,i=E(e.get("callee.property"),r.computed);if(D&&!b(s.name,i))if(v(I,i)&&x(e.get("callee"),I,i))return S(s)?(n=s,a=pp(s)):a=_i("=",n=e.scope.generateDeclaredUidIdentifier("context"),s),t.callee=oo(Li(this.addDefaultImport(T+"/"+j+"/instance/"+I[i].path,i+"InstanceProperty"),[a]),Qi("call")),void t.arguments.unshift(n);t.arguments.length||r.computed&&e.get("callee.property").matchesPattern("Symbol.iterator")&&e.replaceWith(Li(this.addDefaultImport(O+"/core-js/get-iterator","getIterator"),[s]))}}},BinaryExpression:function(e){C&&"in"===e.node.operator&&e.get("left").matchesPattern("Symbol.iterator")&&e.replaceWith(Li(this.addDefaultImport(O+"/core-js/is-iterable","isIterable"),[e.node.right]))},MemberExpression:{enter:function(e){if(C&&e.isReferenced()){var t=e.node,r=t.object;if(of(r,t))if(!w&&t.computed&&e.get("property").matchesPattern("Symbol.iterator"))e.replaceWith(Li(this.addDefaultImport(T+"/core-js/get-iterator-method","getIteratorMethod"),[r]));else{var n=r.name,a=E(e.get("property"),t.computed);!e.scope.getBindingIdentifier(n)&&b(n,a)?e.replaceWith(this.addDefaultImport(O+"/"+j+"/"+F[n][a].path,n+"$"+a)):D&&v(I,a)&&x(e,I,a)&&e.replaceWith(Li(this.addDefaultImport(T+"/"+j+"/instance/"+I[a].path,a+"InstanceProperty"),[r]))}}},exit:function(e){if(C&&e.isReferenced()&&!e.node.computed){var t=e.node,r=t.object.name;v(k,r)&&(e.scope.getBindingIdentifier(r)||e.replaceWith(oo(this.addDefaultImport(O+"/"+j+"/"+k[r].path,r),t.property)))}}}}}})),CM=sP((function(e){return e.assertVersion(7),{name:"transform-shorthand-properties",visitor:{ObjectMethod:function(e){var t=e.node;if("method"===t.kind){var r=$i(null,t.params,t.body,t.generator,t.async);r.returnType=t.returnType,e.replaceWith(fo(t.key,r,t.computed))}},ObjectProperty:function(e){var t=e.node;t.shorthand&&(t.shorthand=!1)}}}})),TM=sP((function(e,t){e.assertVersion(7);var r=t.loose;function n(e,t){return r&&!S(e.argument,{name:"arguments"})?e.argument:t.toArray(e.argument,!0)}function a(e){for(var t=0;t<e.length;t++)if(le(e[t]))return!0;return!1}function s(e,t){return e.length?(t.push(Fi(e)),[]):e}function i(e,t){var r=[],a=[],i=e,o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}var l=c;le(l)?(a=s(a,r),r.push(n(l,t))):a.push(l)}return s(a,r),r}return{name:"transform-spread",visitor:{ArrayExpression:function(e){var t=e.node,r=e.scope,n=t.elements;if(a(n)){var s=i(n,r),o=s[0];1!==s.length||o===n[0].argument?(u(o)?s.shift():o=Fi([]),e.replaceWith(Li(oo(o,Qi("concat")),s))):e.replaceWith(o)}},CallExpression:function(e){var t=e.node,r=e.scope,n=t.arguments;if(a(n)){var s=e.get("callee");if(!s.isSuper()){var o,u=r.buildUndefinedNode();t.arguments=[];var c=(o=1===n.length&&"arguments"===n[0].argument.name?[n[0].argument]:i(n,r)).shift();o.length?t.arguments.push(Li(oo(c,Qi("concat")),o)):t.arguments.push(c);var l=t.callee;if(s.isMemberExpression()){var p=r.maybeGenerateMemoised(l.object);p?(l.object=_i("=",p,l.object),u=p):u=pp(l.object),Zd(l,Qi("apply"))}else t.callee=oo(t.callee,Qi("apply"));pe(u)&&(u=xo()),t.arguments.unshift(pp(u))}}},NewExpression:function(e){var t=e.node,r=e.scope,n=t.arguments;if(a(n)){var s=i(n,r),o=s.shift();n=s.length?Li(oo(o,Qi("concat")),s):o,e.replaceWith(Li(e.hub.addHelper("construct"),[t.callee,n]))}}}}})),jM=sP((function(e){return e.assertVersion(7),{name:"transform-sticky-regex",visitor:{RegExpLiteral:function(e){var t=e.node;(function(e,t){return"RegExpLiteral"===e.type&&e.flags.indexOf(t)>=0})(t,"y")&&e.replaceWith(uo(Qi("RegExp"),[to(t.pattern),to(t.flags)]))}}}})),PM=sP((function(e){return e.assertVersion(7),{name:"transform-strict-mode",visitor:{Program:function(e){for(var t=0,r=e.node.directives;t<r.length;t++){if("use strict"===r[t].value.value)return}e.unshiftContainer("directives",Oi(Ni("use strict")))}}}}));function kM(){var e=i(["\n function ","() {\n const data = ",";\n "," = function() { return data };\n return data;\n } \n "]);return kM=function(){return e},e}var FM=sP((function(e,t){e.assertVersion(7);var r=t.loose,n="taggedTemplateLiteral";return r&&(n+="Loose"),{name:"transform-template-literals",visitor:{TaggedTemplateExpression:function(e){for(var t=e.node,r=t.quasi,a=[],s=[],i=!0,o=0,u=r.quasis;o<u.length;o++){var c=u[o].value,l=c.raw,p=c.cooked,d=null==p?e.scope.buildUndefinedNode():to(p);a.push(d),s.push(to(l)),l!==p&&(i=!1)}var f=e.scope.getProgramParent(),h=f.generateUidIdentifier("templateObject"),m=this.addHelper(n),y=[Fi(a)];i||y.push(Fi(s));var g=dE.ast(kM(),h,Li(m,y),h);f.path.unshiftContainer("body",g),e.replaceWith(Li(t.tag,[Li(pp(h),[])].concat(r.expressions)))},TemplateLiteral:function(e){for(var t=[],n=e.get("expressions"),a=0,s=0,i=e.node.quasis;s<i.length;s++){var o=i[s];if(o.value.cooked&&t.push(to(o.value.cooked)),a<n.length){var u=n[a++].node;T(u,{value:""})||t.push(u)}}var c=!r||!T(t[1]);!T(t[0])&&c&&t.unshift(to(""));var l,p=t[0];if(r)for(var d=1;d<t.length;d++)p=Ii("+",p,t[d]);else t.length>1&&(l=!0,p=t.reduce((function(e,t){var r=ft(t);return!r&&l&&(r=!0,l=!1),r&&f(e)?(e.arguments.push(t),e):Li(oo(e,Qi("concat")),[t])})));e.replaceWith(p)}}}})),_M=sP((function(e){return e.assertVersion(7),{name:"transform-typeof-symbol",visitor:{Scope:function(e){var t=e.scope;t.getBinding("Symbol")&&t.rename("Symbol")},UnaryExpression:function(e){var t=e.node,r=e.parent;if("typeof"===t.operator){if(e.parentPath.isBinaryExpression()&&hs.indexOf(r.operator)>=0){var n=e.getOpposite();if(n.isLiteral()&&"symbol"!==n.node.value&&"object"!==n.node.value)return}var a=e.findParent((function(e){var t;if(e.isFunction())return"@babel/helpers - typeof"===(null==(t=e.get("body.directives.0"))?void 0:t.node.value.value)}));if(!a){var s=this.addHelper("typeof");if(!(a=e.findParent((function(e){return e.isVariableDeclarator()&&e.node.id===s||e.isFunctionDeclaration()&&e.node.id&&e.node.id.name===s.name})))){var i=Li(s,[t.argument]),o=e.get("argument");if(o.isIdentifier()&&!e.scope.hasBinding(o.node.name,!0)){var u=wo("typeof",pp(t.argument));e.replaceWith(Gi(Ii("===",u,to("undefined")),to("undefined"),i))}else e.replaceWith(i)}}}}}}}));function IM(e,t){var r=e.node;if(r.declare)e.remove();else{if(r.const)throw e.buildCodeFrameError("'const' enums are not supported.");var n=r.id.name,a=function(e,t,r){var n=function(e,t){var r=Object.create(null),n=-1;return e.node.members.map((function(a){var s,i=t.isIdentifier(a.id)?a.id.name:a.id.value,o=a.initializer;if(o){var u=function(e,t){return r(e);function r(e){switch(e.type){case"StringLiteral":return e.value;case"UnaryExpression":return function(e){var t=e.argument,n=e.operator,a=r(t);if(void 0===a)return;switch(n){case"+":return a;case"-":return-a;case"~":return~a;default:return}}(e);case"BinaryExpression":return function(e){var t=r(e.left);if(void 0===t)return;var n=r(e.right);if(void 0===n)return;switch(e.operator){case"|":return t|n;case"&":return t&n;case">>":return t>>n;case">>>":return t>>>n;case"<<":return t<<n;case"^":return t^n;case"*":return t*n;case"/":return t/n;case"+":return t+n;case"-":return t-n;case"%":return t%n;default:return}}(e);case"NumericLiteral":return e.value;case"ParenthesizedExpression":return r(e.expression);case"Identifier":return t[e.name];case"TemplateLiteral":if(1===e.quasis.length)return e.quasis[0].value.cooked;default:return}}}(o,r);void 0!==u?(r[i]=u,"number"==typeof u?(s=t.numericLiteral(u),n=u):(aI("string"==typeof u),s=t.stringLiteral(u),n=void 0)):(s=o,n=void 0)}else{if(void 0===n)throw e.buildCodeFrameError("Enum member must have initializer.");n++,s=t.numericLiteral(n),r[i]=n}return[i,s]}))}(e,t).map((function(e){var n=e[0],a=e[1];return function(e,t){return(e?OM:NM)(t)}(t.isStringLiteral(a),{ENUM:t.cloneNode(r),NAME:n,VALUE:a})}));return BM({ID:t.cloneNode(r),ASSIGNMENTS:n})}(e,t,r.id);switch(e.parent.type){case"BlockStatement":case"ExportNamedDeclaration":case"Program":if(e.insertAfter(a),function e(t){if(t.isExportDeclaration())return e(t.parentPath);return!!t.getData(n)||(t.setData(n,!0),!1)}(e.parentPath))e.remove();else{var s=t.isProgram(e.parent);e.scope.registerDeclaration(e.replaceWith(function(e,t,r){return t.variableDeclaration(r,[t.variableDeclarator(e)])}(r.id,t,s?"var":"let"))[0])}break;default:throw new Error("Unexpected enum parent '"+e.parent.type)}}}var BM=dE("\n (function (ID) {\n ASSIGNMENTS;\n })(ID || (ID = {}));\n"),OM=dE('\n ENUM["NAME"] = VALUE;\n'),NM=dE('\n ENUM[ENUM["NAME"] = VALUE] = "NAME";\n');function RM(){var e=i(["\n (function (",") {\n ","\n })("," || ("," = ","));\n "]);return RM=function(){return e},e}function MM(){var e=i(["\n ","."," || (\n ","."," = ","\n )\n "]);return MM=function(){return e},e}function LM(e,t,r){if(e.node.declare||"StringLiteral"===e.node.id.type)e.remove();else{if(!r)throw e.hub.file.buildCodeFrameError(e.node.id,"Namespace not marked type-only declare. Non-declarative namespaces are only supported experimentally in Babel. To enable and review caveats see: https://babeljs.io/docs/en/babel-plugin-transform-typescript");var n=e.node.id.name,a=function e(t,r,n,a){for(var s=new Set,i=n.id,o=t.scope.generateUid(i.name),u=n.body.body,c=0;c<u.length;c++){var l=u[c];switch(l.type){case"TSModuleDeclaration":var p=e(t,r,l),d=l.id.name;s.has(d)?u[c]=p:(s.add(d),u.splice(c++,1,UM(r,d),p));continue;case"TSEnumDeclaration":case"FunctionDeclaration":case"ClassDeclaration":s.add(l.id.name);continue;case"VariableDeclaration":var f=l.declarations,h=Array.isArray(f),m=0;for(f=h?f:f[Symbol.iterator]();;){var y;if(h){if(m>=f.length)break;y=f[m++]}else{if((m=f.next()).done)break;y=m.value}var g=y;s.add(g.id.name)}continue;default:continue;case"ExportNamedDeclaration":}switch(l.declaration.type){case"TSEnumDeclaration":case"FunctionDeclaration":case"ClassDeclaration":var v=l.declaration.id.name;s.add(v),u.splice(c++,1,l.declaration,r.expressionStatement(r.assignmentExpression("=",GM(r,o,v),r.identifier(v))));break;case"VariableDeclaration":if("const"!==l.declaration.kind)throw t.hub.file.buildCodeFrameError(l.declaration,"Namespaces exporting non-const are not supported by Babel. Change to const or see: https://babeljs.io/docs/en/babel-plugin-transform-typescript");var b=l.declaration.declarations,x=Array.isArray(b),E=0;for(b=x?b:b[Symbol.iterator]();;){var A;if(x){if(E>=b.length)break;A=b[E++]}else{if((E=b.next()).done)break;A=E.value}var w=A;w.init=r.assignmentExpression("=",GM(r,o,w.id.name),w.init)}u[c]=l.declaration;break;case"TSModuleDeclaration":var S=e(t,r,l.declaration,r.identifier(o)),D=l.declaration.id.name;s.has(D)?u[c]=S:(s.add(D),u.splice(c++,1,UM(r,D),S))}}var C=r.objectExpression([]);a&&(C=dE.expression.ast(MM(),a,i,a,i,C));return dE.statement.ast(RM(),r.identifier(o),u,i,i,C)}(e,t,t.cloneDeep(e.node)),s=e.scope.hasOwnBinding(n);"ExportNamedDeclaration"===e.parent.type?s?e.parentPath.replaceWith(a):(e.parentPath.insertAfter(a),e.replaceWith(UM(t,n)),e.scope.registerDeclaration(e.parentPath)):s?e.replaceWith(a):e.scope.registerDeclaration(e.replaceWithMultiple([UM(t,n),a])[0])}}function UM(e,t){return e.variableDeclaration("let",[e.variableDeclarator(e.identifier(t))])}function GM(e,t,r){return e.memberExpression(e.identifier(t),e.identifier(r))}function VM(){var e=i(["this."," = ",""]);return VM=function(){return e},e}function WM(e){switch(e.parent.type){case"TSTypeReference":case"TSQualifiedName":case"TSExpressionWithTypeArguments":case"TSTypeQuery":return!0;default:return!1}}var HM=new WeakSet,qM=new WeakMap;function KM(e,t){var r=e.find((function(e){return e.isProgram()})).node;return!e.scope.hasOwnBinding(t)&&(!!qM.get(r).has(t)||(console.warn('The exported identifier "'+t+'" is not declared in Babel\'s scope tracker\nas a JavaScript value binding, and "@babel/plugin-transform-typescript"\nnever encountered it as a TypeScript type declaration.\nIt will be treated as a JavaScript value.\n\nThis problem is likely caused by another plugin injecting\n"'+t+'" without registering it in the scope tracker. If you are the author\n of that plugin, please use "scope.registerDeclaration(declarationPath)".'),!1))}function zM(e,t){qM.get(e.path.node).add(t)}var XM=sP((function(e,t){var r=t.jsxPragma,n=void 0===r?"React":r,a=t.allowNamespaces,s=void 0!==a&&a,i=t.allowDeclareFields,o=void 0!==i&&i;e.assertVersion(7);var u=/\*?\s*@jsx\s+([^\s]+)/,c=function(e){var t=e.node;if(!o&&t.declare)throw e.buildCodeFrameError("The 'declare' modifier is only allowed when the 'allowDeclareFields' option of @babel/plugin-transform-typescript or @babel/preset-typescript is enabled.");if(t.definite||t.declare){if(t.value)throw e.buildCodeFrameError("Definietly assigned fields and fields with the 'declare' modifier cannot be initialized here, but only in the constructor");t.decorators||e.remove()}else o||t.value||t.decorators||e.remove();t.accessibility&&(t.accessibility=null),t.abstract&&(t.abstract=null),t.readonly&&(t.readonly=null),t.optional&&(t.optional=null),t.typeAnnotation&&(t.typeAnnotation=null),t.definite&&(t.definite=null)},l=function(e){var t=e.node;t.accessibility&&(t.accessibility=null),t.abstract&&(t.abstract=null),t.optional&&(t.optional=null)},p=function(e,t){e.node.accessibility&&(e.node.accessibility=null);var r=[],n=e.node.params,a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;"TSParameterProperty"!==o.type||HM.has(o.parameter)||(HM.add(o.parameter),r.push(o.parameter))}if(r.length){var u=r.map((function(t){var r;if(S(t))r=t;else{if(!X(t)||!S(t.left))throw e.buildCodeFrameError("Parameter properties can not be destructuring patterns.");r=t.left}return dE.statement.ast(VM(),r,r)}));Gk(t,e,u)}};return{name:"transform-typescript",inherits:jP,visitor:{Pattern:d,Identifier:d,RestElement:d,Program:function(e,t){var r=t.file,a=null;if(qM.has(e.node)||qM.set(e.node,new Set),r.ast.comments)for(var s=0,i=r.ast.comments;s<i.length;s++){var o=i[s],c=u.exec(o.value);c&&(a=c[1])}var l=e.get("body"),p=Array.isArray(l),d=0;for(l=p?l:l[Symbol.iterator]();;){var h;if(p){if(d>=l.length)break;h=l[d++]}else{if((d=l.next()).done)break;h=d.value}var m=h;if(ae(m)){if(0===m.node.specifiers.length)continue;var y=!0,g=[],v=m.node.specifiers,b=Array.isArray(v),x=0;for(v=b?v:v[Symbol.iterator]();;){var E;if(b){if(x>=v.length)break;E=v[x++]}else{if((x=v.next()).done)break;E=x.value}var A=E,w=m.scope.getBinding(A.local.name);w&&f({binding:w,programPath:e,jsxPragma:a||n})?g.push(w.path):y=!1}if(y)m.remove();else{var S=g,D=Array.isArray(S),C=0;for(S=D?S:S[Symbol.iterator]();;){var T;if(D){if(C>=S.length)break;T=S[C++]}else{if((C=S.next()).done)break;T=C.value}T.remove()}}}else if(m.isExportDeclaration()&&(m=m.get("declaration")),m.isVariableDeclaration({declare:!0}))for(var j=0,P=Object.keys(m.getBindingIdentifiers());j<P.length;j++){var k=P[j];zM(e.scope,k)}else(m.isTSTypeAliasDeclaration()||m.isTSDeclareFunction()||m.isTSInterfaceDeclaration()||m.isClassDeclaration({declare:!0})||m.isTSEnumDeclaration({declare:!0})||m.isTSModuleDeclaration({declare:!0})&&m.get("id").isIdentifier())&&zM(e.scope,m.node.id.name)}},ExportNamedDeclaration:function(e){!e.node.source&&e.node.specifiers.length>0&&e.node.specifiers.every((function(t){var r=t.local;return KM(e,r.name)}))&&e.remove()},ExportSpecifier:function(e){!e.parent.source&&KM(e,e.node.local.name)&&e.remove()},ExportDefaultDeclaration:function(e){S(e.node.declaration)&&KM(e,e.node.declaration.name)&&e.remove()},TSDeclareFunction:function(e){e.remove()},TSDeclareMethod:function(e){e.remove()},VariableDeclaration:function(e){e.node.declare&&e.remove()},VariableDeclarator:function(e){var t=e.node;t.definite&&(t.definite=null)},TSIndexSignature:function(e){e.remove()},ClassDeclaration:function(e){e.node.declare&&e.remove()},Class:function(e){var t=e.node;t.typeParameters&&(t.typeParameters=null),t.superTypeParameters&&(t.superTypeParameters=null),t.implements&&(t.implements=null),t.abstract&&(t.abstract=null),e.get("body.body").forEach((function(t){t.isClassMethod()?"constructor"===t.node.kind?p(t,e):l(t,e):t.isClassProperty()&&c(t,e)}))},Function:function(e){var t=e.node;t.typeParameters&&(t.typeParameters=null),t.returnType&&(t.returnType=null);var r=t.params[0];r&&S(r)&&"this"===r.name&&t.params.shift(),t.params=t.params.map((function(e){return"TSParameterProperty"===e.type?e.parameter:e}))},TSModuleDeclaration:function(e){LM(e,ff,s)},TSInterfaceDeclaration:function(e){e.remove()},TSTypeAliasDeclaration:function(e){e.remove()},TSEnumDeclaration:function(e){IM(e,ff)},TSImportEqualsDeclaration:function(e){throw e.buildCodeFrameError("`import =` is not supported by @babel/plugin-transform-typescript\nPlease consider using `import <moduleName> from '<moduleName>';` alongside Typescript's --allowSyntheticDefaultImports option.")},TSExportAssignment:function(e){throw e.buildCodeFrameError("`export =` is not supported by @babel/plugin-transform-typescript\nPlease consider using `export <value>;`.")},TSTypeAssertion:function(e){e.replaceWith(e.node.expression)},TSAsExpression:function(e){var t=e.node;do{t=t.expression}while(et(t));e.replaceWith(t)},TSNonNullExpression:function(e){e.replaceWith(e.node.expression)},CallExpression:function(e){e.node.typeParameters=null},NewExpression:function(e){e.node.typeParameters=null},JSXOpeningElement:function(e){e.node.typeParameters=null},TaggedTemplateExpression:function(e){e.node.typeParameters=null}}};function d(e){var t=e.node;t.typeAnnotation&&(t.typeAnnotation=null),S(t)&&t.optional&&(t.optional=null)}function f(e){var t=e.binding,r=e.programPath,n=e.jsxPragma,a=t.referencePaths,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}if(!WM(o))return!1}if(t.identifier.name!==n)return!0;var u=!1;return r.traverse({JSXElement:function(){u=!0},JSXFragment:function(){u=!0}}),!u}})),YM=sP((function(e){return e.assertVersion(7),D_({name:"transform-unicode-regex",feature:"unicodeFlag"})})),JM={"external-helpers":uP,"syntax-async-generators":lP,"syntax-class-properties":pP,"syntax-decorators":dP,"syntax-do-expressions":fP,"syntax-dynamic-import":hP,"syntax-export-default-from":mP,"syntax-export-namespace-from":yP,"syntax-flow":gP,"syntax-function-bind":vP,"syntax-function-sent":bP,"syntax-import-meta":xP,"syntax-jsx":EP,"syntax-object-rest-spread":AP,"syntax-optional-catch-binding":wP,"syntax-pipeline-operator":DP,"syntax-top-level-await":CP,"syntax-typescript":jP,"proposal-async-generator-functions":MP,"proposal-class-properties":$k,"proposal-decorators":cF,"proposal-do-expressions":lF,"proposal-dynamic-import":fF,"proposal-export-default-from":hF,"proposal-export-namespace-from":yF,"proposal-function-bind":gF,"proposal-function-sent":vF,"proposal-json-strings":xF,"proposal-logical-assignment-operators":AF,"proposal-nullish-coalescing-operator":SF,"proposal-numeric-separator":CF,"proposal-object-rest-spread":PF,"proposal-optional-catch-binding":FF,"proposal-optional-chaining":IF,"proposal-pipeline-operator":RF,"proposal-private-methods":MF,"proposal-throw-expressions":UF,"proposal-unicode-property-regex":C_,"transform-async-to-generator":bI,"transform-arrow-functions":xI,"transform-block-scoped-functions":EI,"transform-block-scoping":TI,"transform-classes":_B,"transform-computed-properties":IB,"transform-destructuring":BB,"transform-dotall-regex":OB,"transform-duplicate-keys":NB,"transform-exponentiation-operator":LB,"transform-flow-comments":UB,"transform-flow-strip-types":GB,"transform-for-of":VB,"transform-function-name":WB,"transform-instanceof":HB,"transform-jscript":qB,"transform-literals":KB,"transform-member-expression-literals":zB,"transform-modules-amd":OO,"transform-modules-commonjs":LO,"transform-modules-systemjs":qO,"transform-modules-umd":XO,"transform-named-capturing-groups-regex":YO,"transform-new-target":JO,"transform-object-assign":$O,"transform-object-super":QO,"transform-object-set-prototype-of-to-assign":ZO,"transform-parameters":gN,"transform-property-literals":vN,"transform-property-mutators":bN,"transform-proto-to-assign":xN,"transform-react-constant-elements":EN,"transform-react-display-name":AN,"transform-react-inline-elements":SN,"transform-react-jsx":DN,"transform-react-jsx-compat":CN,"transform-react-jsx-self":TN,"transform-react-jsx-source":jN,"transform-regenerator":YR,"transform-reserved-words":JR,"transform-runtime":DM,"transform-shorthand-properties":CM,"transform-spread":TM,"transform-sticky-regex":jM,"transform-strict-mode":PM,"transform-template-literals":FM,"transform-typeof-symbol":_M,"transform-typescript":XM,"transform-unicode-regex":YM},$M=function(e,t){var r=!1,n="commonjs",a=!1;void 0!==t&&(void 0!==t.loose&&(r=t.loose),void 0!==t.modules&&(n=t.modules),void 0!==t.spec&&(a=t.spec));var s={loose:r};return{plugins:[[FM,{loose:r,spec:a}],KB,WB,[xI,{spec:a}],EI,[_B,s],QO,CM,NB,[IB,s],[VB,s],jM,YM,[TM,s],[gN,s],[BB,s],TI,_M,HB,("commonjs"===n||"cjs"===n)&&[LO,s],"systemjs"===n&&[qO,s],"amd"===n&&[OO,s],"umd"===n&&[XO,s],[YR,{async:!1,asyncGenerators:!1}]].filter(Boolean)}},QM=function(e,t){var r=!1;return void 0!==t&&void 0!==t.loose&&(r=t.loose),{plugins:[hP,xP,[IF,{loose:r}],[SF,{loose:r}],[$k,{loose:r}],xF,[MF,{loose:r}]]}},ZM=function(e,t){void 0===t&&(t={});var r=t,n=r.loose,a=void 0!==n&&n,s=r.useBuiltIns,i=void 0!==s&&s,o=r.decoratorsLegacy,u=void 0!==o&&o,c=r.decoratorsBeforeExport;return{presets:[[QM,{loose:a,useBuiltIns:i}]],plugins:[[cF,{legacy:u,decoratorsBeforeExport:c}],vF,yF,CF,UF]}},eL=function(e,t){void 0===t&&(t={});var r=t,n=r.loose,a=void 0!==n&&n,s=r.useBuiltIns,i=void 0!==s&&s,o=r.decoratorsLegacy,u=void 0!==o&&o,c=r.decoratorsBeforeExport,l=r.pipelineProposal;return{presets:[[ZM,{loose:a,useBuiltIns:i,decoratorsLegacy:u,decoratorsBeforeExport:c}]],plugins:[hF,AF,[RF,{proposal:void 0===l?"minimal":l}],lF]}},tL=Object.freeze({__proto__:null,default:[{name:"nodejs",version:"0.2.0",date:"2011-08-26",lts:!1,security:!1},{name:"nodejs",version:"0.3.0",date:"2011-08-26",lts:!1,security:!1},{name:"nodejs",version:"0.4.0",date:"2011-08-26",lts:!1,security:!1},{name:"nodejs",version:"0.5.0",date:"2011-08-26",lts:!1,security:!1},{name:"nodejs",version:"0.6.0",date:"2011-11-04",lts:!1,security:!1},{name:"nodejs",version:"0.7.0",date:"2012-01-17",lts:!1,security:!1},{name:"nodejs",version:"0.8.0",date:"2012-06-22",lts:!1,security:!1},{name:"nodejs",version:"0.9.0",date:"2012-07-20",lts:!1,security:!1},{name:"nodejs",version:"0.10.0",date:"2013-03-11",lts:!1,security:!1},{name:"nodejs",version:"0.11.0",date:"2013-03-28",lts:!1,security:!1},{name:"nodejs",version:"0.12.0",date:"2015-02-06",lts:!1,security:!1},{name:"iojs",version:"1.0.0",date:"2015-01-14"},{name:"iojs",version:"1.1.0",date:"2015-02-03"},{name:"iojs",version:"1.2.0",date:"2015-02-11"},{name:"iojs",version:"1.3.0",date:"2015-02-20"},{name:"iojs",version:"1.5.0",date:"2015-03-06"},{name:"iojs",version:"1.6.0",date:"2015-03-20"},{name:"iojs",version:"2.0.0",date:"2015-05-04"},{name:"iojs",version:"2.1.0",date:"2015-05-24"},{name:"iojs",version:"2.2.0",date:"2015-06-01"},{name:"iojs",version:"2.3.0",date:"2015-06-13"},{name:"iojs",version:"2.4.0",date:"2015-07-17"},{name:"iojs",version:"2.5.0",date:"2015-07-28"},{name:"iojs",version:"3.0.0",date:"2015-08-04"},{name:"iojs",version:"3.1.0",date:"2015-08-19"},{name:"iojs",version:"3.2.0",date:"2015-08-25"},{name:"iojs",version:"3.3.0",date:"2015-09-02"},{name:"nodejs",version:"4.0.0",date:"2015-09-08",lts:!1,security:!1},{name:"nodejs",version:"4.1.0",date:"2015-09-17",lts:!1,security:!1},{name:"nodejs",version:"4.2.0",date:"2015-10-12",lts:"Argon",security:!1},{name:"nodejs",version:"4.3.0",date:"2016-02-09",lts:"Argon",security:!1},{name:"nodejs",version:"4.4.0",date:"2016-03-08",lts:"Argon",security:!1},{name:"nodejs",version:"4.5.0",date:"2016-08-16",lts:"Argon",security:!1},{name:"nodejs",version:"4.6.0",date:"2016-09-27",lts:"Argon",security:!0},{name:"nodejs",version:"4.7.0",date:"2016-12-06",lts:"Argon",security:!1},{name:"nodejs",version:"4.8.0",date:"2017-02-21",lts:"Argon",security:!1},{name:"nodejs",version:"4.9.0",date:"2018-03-28",lts:"Argon",security:!0},{name:"nodejs",version:"5.0.0",date:"2015-10-29",lts:!1,security:!1},{name:"nodejs",version:"5.1.0",date:"2015-11-17",lts:!1,security:!1},{name:"nodejs",version:"5.2.0",date:"2015-12-09",lts:!1,security:!1},{name:"nodejs",version:"5.3.0",date:"2015-12-15",lts:!1,security:!1},{name:"nodejs",version:"5.4.0",date:"2016-01-06",lts:!1,security:!1},{name:"nodejs",version:"5.5.0",date:"2016-01-21",lts:!1,security:!1},{name:"nodejs",version:"5.6.0",date:"2016-02-09",lts:!1,security:!1},{name:"nodejs",version:"5.7.0",date:"2016-02-23",lts:!1,security:!1},{name:"nodejs",version:"5.8.0",date:"2016-03-09",lts:!1,security:!1},{name:"nodejs",version:"5.9.0",date:"2016-03-16",lts:!1,security:!1},{name:"nodejs",version:"5.10.0",date:"2016-04-01",lts:!1,security:!1},{name:"nodejs",version:"5.11.0",date:"2016-04-21",lts:!1,security:!1},{name:"nodejs",version:"5.12.0",date:"2016-06-23",lts:!1,security:!1},{name:"nodejs",version:"6.0.0",date:"2016-04-26",lts:!1,security:!1},{name:"nodejs",version:"6.1.0",date:"2016-05-05",lts:!1,security:!1},{name:"nodejs",version:"6.2.0",date:"2016-05-17",lts:!1,security:!1},{name:"nodejs",version:"6.3.0",date:"2016-07-06",lts:!1,security:!1},{name:"nodejs",version:"6.4.0",date:"2016-08-12",lts:!1,security:!1},{name:"nodejs",version:"6.5.0",date:"2016-08-26",lts:!1,security:!1},{name:"nodejs",version:"6.6.0",date:"2016-09-14",lts:!1,security:!1},{name:"nodejs",version:"6.7.0",date:"2016-09-27",lts:!1,security:!0},{name:"nodejs",version:"6.8.0",date:"2016-10-12",lts:!1,security:!1},{name:"nodejs",version:"6.9.0",date:"2016-10-18",lts:"Boron",security:!1},{name:"nodejs",version:"6.10.0",date:"2017-02-21",lts:"Boron",security:!1},{name:"nodejs",version:"6.11.0",date:"2017-06-06",lts:"Boron",security:!1},{name:"nodejs",version:"6.12.0",date:"2017-11-06",lts:"Boron",security:!1},{name:"nodejs",version:"6.13.0",date:"2018-02-10",lts:"Boron",security:!1},{name:"nodejs",version:"6.14.0",date:"2018-03-28",lts:"Boron",security:!0},{name:"nodejs",version:"6.15.0",date:"2018-11-27",lts:"Boron",security:!0},{name:"nodejs",version:"6.16.0",date:"2018-12-26",lts:"Boron",security:!1},{name:"nodejs",version:"6.17.0",date:"2019-02-28",lts:"Boron",security:!0},{name:"nodejs",version:"7.0.0",date:"2016-10-25",lts:!1,security:!1},{name:"nodejs",version:"7.1.0",date:"2016-11-08",lts:!1,security:!1},{name:"nodejs",version:"7.2.0",date:"2016-11-22",lts:!1,security:!1},{name:"nodejs",version:"7.3.0",date:"2016-12-20",lts:!1,security:!1},{name:"nodejs",version:"7.4.0",date:"2017-01-04",lts:!1,security:!1},{name:"nodejs",version:"7.5.0",date:"2017-01-31",lts:!1,security:!1},{name:"nodejs",version:"7.6.0",date:"2017-02-21",lts:!1,security:!1},{name:"nodejs",version:"7.7.0",date:"2017-02-28",lts:!1,security:!1},{name:"nodejs",version:"7.8.0",date:"2017-03-29",lts:!1,security:!1},{name:"nodejs",version:"7.9.0",date:"2017-04-11",lts:!1,security:!1},{name:"nodejs",version:"7.10.0",date:"2017-05-02",lts:!1,security:!1},{name:"nodejs",version:"8.0.0",date:"2017-05-30",lts:!1,security:!1},{name:"nodejs",version:"8.1.0",date:"2017-06-08",lts:!1,security:!1},{name:"nodejs",version:"8.2.0",date:"2017-07-19",lts:!1,security:!1},{name:"nodejs",version:"8.3.0",date:"2017-08-08",lts:!1,security:!1},{name:"nodejs",version:"8.4.0",date:"2017-08-15",lts:!1,security:!1},{name:"nodejs",version:"8.5.0",date:"2017-09-12",lts:!1,security:!1},{name:"nodejs",version:"8.6.0",date:"2017-09-26",lts:!1,security:!1},{name:"nodejs",version:"8.7.0",date:"2017-10-11",lts:!1,security:!1},{name:"nodejs",version:"8.8.0",date:"2017-10-24",lts:!1,security:!1},{name:"nodejs",version:"8.9.0",date:"2017-10-31",lts:"Carbon",security:!1},{name:"nodejs",version:"8.10.0",date:"2018-03-06",lts:"Carbon",security:!1},{name:"nodejs",version:"8.11.0",date:"2018-03-28",lts:"Carbon",security:!0},{name:"nodejs",version:"8.12.0",date:"2018-09-10",lts:"Carbon",security:!1},{name:"nodejs",version:"8.13.0",date:"2018-11-20",lts:"Carbon",security:!1},{name:"nodejs",version:"8.14.0",date:"2018-11-27",lts:"Carbon",security:!0},{name:"nodejs",version:"8.15.0",date:"2018-12-26",lts:"Carbon",security:!1},{name:"nodejs",version:"8.16.0",date:"2019-04-16",lts:"Carbon",security:!1},{name:"nodejs",version:"8.17.0",date:"2019-12-17",lts:"Carbon",security:!0},{name:"nodejs",version:"9.0.0",date:"2017-10-31",lts:!1,security:!1},{name:"nodejs",version:"9.1.0",date:"2017-11-07",lts:!1,security:!1},{name:"nodejs",version:"9.2.0",date:"2017-11-14",lts:!1,security:!1},{name:"nodejs",version:"9.3.0",date:"2017-12-12",lts:!1,security:!1},{name:"nodejs",version:"9.4.0",date:"2018-01-10",lts:!1,security:!1},{name:"nodejs",version:"9.5.0",date:"2018-01-31",lts:!1,security:!1},{name:"nodejs",version:"9.6.0",date:"2018-02-21",lts:!1,security:!1},{name:"nodejs",version:"9.7.0",date:"2018-03-01",lts:!1,security:!1},{name:"nodejs",version:"9.8.0",date:"2018-03-07",lts:!1,security:!1},{name:"nodejs",version:"9.9.0",date:"2018-03-21",lts:!1,security:!1},{name:"nodejs",version:"9.10.0",date:"2018-03-28",lts:!1,security:!0},{name:"nodejs",version:"9.11.0",date:"2018-04-04",lts:!1,security:!1},{name:"nodejs",version:"10.0.0",date:"2018-04-24",lts:!1,security:!1},{name:"nodejs",version:"10.1.0",date:"2018-05-08",lts:!1,security:!1},{name:"nodejs",version:"10.2.0",date:"2018-05-23",lts:!1,security:!1},{name:"nodejs",version:"10.3.0",date:"2018-05-29",lts:!1,security:!1},{name:"nodejs",version:"10.4.0",date:"2018-06-06",lts:!1,security:!1},{name:"nodejs",version:"10.5.0",date:"2018-06-20",lts:!1,security:!1},{name:"nodejs",version:"10.6.0",date:"2018-07-04",lts:!1,security:!1},{name:"nodejs",version:"10.7.0",date:"2018-07-18",lts:!1,security:!1},{name:"nodejs",version:"10.8.0",date:"2018-08-01",lts:!1,security:!1},{name:"nodejs",version:"10.9.0",date:"2018-08-15",lts:!1,security:!1},{name:"nodejs",version:"10.10.0",date:"2018-09-06",lts:!1,security:!1},{name:"nodejs",version:"10.11.0",date:"2018-09-19",lts:!1,security:!1},{name:"nodejs",version:"10.12.0",date:"2018-10-10",lts:!1,security:!1},{name:"nodejs",version:"10.13.0",date:"2018-10-30",lts:"Dubnium",security:!1},{name:"nodejs",version:"10.14.0",date:"2018-11-27",lts:"Dubnium",security:!0},{name:"nodejs",version:"10.15.0",date:"2018-12-26",lts:"Dubnium",security:!1},{name:"nodejs",version:"10.16.0",date:"2019-05-28",lts:"Dubnium",security:!1},{name:"nodejs",version:"10.17.0",date:"2019-10-21",lts:"Dubnium",security:!1},{name:"nodejs",version:"10.18.0",date:"2019-12-16",lts:"Dubnium",security:!0},{name:"nodejs",version:"10.19.0",date:"2020-02-05",lts:"Dubnium",security:!0},{name:"nodejs",version:"11.0.0",date:"2018-10-23",lts:!1,security:!1},{name:"nodejs",version:"11.1.0",date:"2018-10-30",lts:!1,security:!1},{name:"nodejs",version:"11.2.0",date:"2018-11-15",lts:!1,security:!1},{name:"nodejs",version:"11.3.0",date:"2018-11-27",lts:!1,security:!0},{name:"nodejs",version:"11.4.0",date:"2018-12-07",lts:!1,security:!1},{name:"nodejs",version:"11.5.0",date:"2018-12-18",lts:!1,security:!1},{name:"nodejs",version:"11.6.0",date:"2018-12-26",lts:!1,security:!1},{name:"nodejs",version:"11.7.0",date:"2019-01-17",lts:!1,security:!1},{name:"nodejs",version:"11.8.0",date:"2019-01-24",lts:!1,security:!1},{name:"nodejs",version:"11.9.0",date:"2019-01-30",lts:!1,security:!1},{name:"nodejs",version:"11.10.0",date:"2019-02-14",lts:!1,security:!1},{name:"nodejs",version:"11.11.0",date:"2019-03-05",lts:!1,security:!1},{name:"nodejs",version:"11.12.0",date:"2019-03-14",lts:!1,security:!1},{name:"nodejs",version:"11.13.0",date:"2019-03-28",lts:!1,security:!1},{name:"nodejs",version:"11.14.0",date:"2019-04-10",lts:!1,security:!1},{name:"nodejs",version:"11.15.0",date:"2019-04-30",lts:!1,security:!1},{name:"nodejs",version:"12.0.0",date:"2019-04-23",lts:!1,security:!1},{name:"nodejs",version:"12.1.0",date:"2019-04-29",lts:!1,security:!1},{name:"nodejs",version:"12.2.0",date:"2019-05-07",lts:!1,security:!1},{name:"nodejs",version:"12.3.0",date:"2019-05-21",lts:!1,security:!1},{name:"nodejs",version:"12.4.0",date:"2019-06-04",lts:!1,security:!1},{name:"nodejs",version:"12.5.0",date:"2019-06-26",lts:!1,security:!1},{name:"nodejs",version:"12.6.0",date:"2019-07-03",lts:!1,security:!1},{name:"nodejs",version:"12.7.0",date:"2019-07-23",lts:!1,security:!1},{name:"nodejs",version:"12.8.0",date:"2019-08-06",lts:!1,security:!1},{name:"nodejs",version:"12.9.0",date:"2019-08-20",lts:!1,security:!1},{name:"nodejs",version:"12.10.0",date:"2019-09-04",lts:!1,security:!1},{name:"nodejs",version:"12.11.0",date:"2019-09-25",lts:!1,security:!1},{name:"nodejs",version:"12.12.0",date:"2019-10-11",lts:!1,security:!1},{name:"nodejs",version:"12.13.0",date:"2019-10-21",lts:"Erbium",security:!1},{name:"nodejs",version:"12.14.0",date:"2019-12-16",lts:"Erbium",security:!0},{name:"nodejs",version:"12.15.0",date:"2020-02-05",lts:"Erbium",security:!0},{name:"nodejs",version:"12.16.0",date:"2020-02-11",lts:"Erbium",security:!1},{name:"nodejs",version:"13.0.0",date:"2019-10-10",lts:!1,security:!1},{name:"nodejs",version:"13.1.0",date:"2019-11-05",lts:!1,security:!1},{name:"nodejs",version:"13.2.0",date:"2019-11-21",lts:!1,security:!1},{name:"nodejs",version:"13.3.0",date:"2019-12-03",lts:!1,security:!1},{name:"nodejs",version:"13.4.0",date:"2019-12-17",lts:!1,security:!0},{name:"nodejs",version:"13.5.0",date:"2019-12-18",lts:!1,security:!1},{name:"nodejs",version:"13.6.0",date:"2020-01-07",lts:!1,security:!1},{name:"nodejs",version:"13.7.0",date:"2020-01-21",lts:!1,security:!1},{name:"nodejs",version:"13.8.0",date:"2020-02-05",lts:!1,security:!0},{name:"nodejs",version:"13.9.0",date:"2020-02-18",lts:!1,security:!1},{name:"nodejs",version:"13.10.0",date:"2020-03-03",lts:!1,security:!1}]}),rL={A:"ie",B:"edge",C:"firefox",D:"chrome",E:"safari",F:"opera",G:"ios_saf",H:"op_mini",I:"android",J:"bb",K:"op_mob",L:"and_chr",M:"and_ff",N:"ie_mob",O:"and_uc",P:"samsung",Q:"and_qq",R:"baidu",S:"kaios"},nL=Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0});t.browsers=rL}));Gt(nL);nL.browsers;var aL={0:"51",1:"52",2:"53",3:"54",4:"55",5:"56",6:"57",7:"58",8:"65",9:"60",A:"10",B:"11",C:"12",D:"9",E:"8",F:"7",G:"4",H:"16",I:"6",J:"17",K:"18",L:"11.1",M:"80",N:"13",O:"15",P:"46",Q:"68",R:"66",S:"12.1",T:"5",U:"19",V:"20",W:"21",X:"22",Y:"23",Z:"24",a:"25",b:"26",c:"27",d:"28",e:"29",f:"30",g:"31",h:"32",i:"33",j:"34",k:"35",l:"36",m:"37",n:"38",o:"39",p:"40",q:"41",r:"42",s:"43",t:"44",u:"45",v:"14",w:"47",x:"48",y:"49",z:"50",AB:"64",BB:"62",CB:"63",DB:"11.5",EB:"61",FB:"3",GB:"67",HB:"4.2-4.3",IB:"69",JB:"70",KB:"71",LB:"72",MB:"73",NB:"74",OB:"75",PB:"76",QB:"59",RB:"79",SB:"10.1",TB:"3.2",UB:"9.3",VB:"81",WB:"83",XB:"3.1",YB:"78",ZB:"5.1",aB:"6.1",bB:"7.1",cB:"9.1",dB:"77",eB:"3.6",fB:"5.5",gB:"TP",hB:"9.5-9.6",iB:"10.0-10.1",jB:"10.5",kB:"10.6",lB:"3.5",mB:"11.6",nB:"4.0-4.1",oB:"2",pB:"5.0-5.1",qB:"6.0-6.1",rB:"7.0-7.1",sB:"8.1-8.4",tB:"9.0-9.2",uB:"82",vB:"10.0-10.2",wB:"10.3",xB:"11.0-11.2",yB:"11.3-11.4",zB:"12.0-12.1","0B":"12.2-12.4","1B":"13.0-13.1","2B":"13.2","3B":"13.3","4B":"all","5B":"2.1","6B":"2.2","7B":"2.3","8B":"4.1","9B":"4.4",AC:"4.4.3-4.4.4",BC:"12.12",CC:"5.0-5.4",DC:"6.2-6.4",EC:"7.2-7.4",FC:"8.2",GC:"9.2",HC:"1.2",IC:"7.12",JC:"2.5"},sL=Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0});t.browserVersions=aL}));Gt(sL);sL.browserVersions;var iL={A:{A:{I:.00478465,F:.00478465,E:.100478,D:.157894,A:.0382772,B:1.39233,fB:.009298},B:"ms",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","fB","I","F","E","D","A","B","","",""],E:"IE",F:{fB:962323200,I:998870400,F:1161129600,E:1237420800,D:1300060800,A:1346716800,B:1381968e3}},B:{A:{C:.009132,N:.009132,v:.013698,O:.013698,H:.036528,J:.123282,K:1.93142,RB:0,M:0},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","C","N","v","O","H","J","K","RB","M","","",""],E:"Edge",F:{C:1438128e3,N:1447286400,v:1470096e3,O:1491868800,H:1508198400,J:1525046400,K:1542067200,RB:1579046400,M:1581033600},D:{C:"ms",N:"ms",v:"ms",O:"ms",H:"ms",J:"ms",K:"ms"}},C:{A:{0:.009132,1:.13698,2:.02283,3:.013698,4:.009132,5:.027396,6:.009132,7:.009132,8:.050226,9:.031962,oB:.004827,FB:.00487,G:.00974,T:.004879,I:.020136,F:.005725,E:.004525,D:.00533,A:.004283,B:.009042,C:.004471,N:.004486,v:.00453,O:.004465,H:.004417,J:.008922,K:.004393,U:.004443,V:.004283,W:.013596,X:.013698,Y:.004525,Z:.008786,a:.004403,b:.004317,c:.004393,d:.004418,e:.008834,f:.004403,g:.008928,h:.004471,i:.013698,j:.004707,k:.009132,l:.004465,m:.004783,n:.02283,o:.004783,p:.00487,q:.005029,r:.0047,s:.009132,t:.009132,u:.009132,P:.004525,w:.018264,x:.031962,y:.009132,z:.009132,QB:.009132,EB:.009132,BB:.009132,CB:.018264,AB:.018264,R:.04566,GB:.018264,Q:.150678,IB:.027396,JB:.036528,KB:.054792,LB:2.05013,MB:1.29218,NB:.041094,OB:0,PB:0,lB:.008786,eB:.00487},B:"moz",C:["","","oB","FB","lB","eB","G","T","I","F","E","D","A","B","C","N","v","O","H","J","K","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","P","w","x","y","z","0","1","2","3","4","5","6","7","QB","9","EB","BB","CB","AB","8","R","GB","Q","IB","JB","KB","LB","MB","NB","OB","PB",""],E:"Firefox",F:{0:1485216e3,1:1488844800,2:149256e4,3:1497312e3,4:1502150400,5:1506556800,6:1510617600,7:1516665600,8:154872e4,9:1525824e3,oB:1161648e3,FB:1213660800,lB:124632e4,eB:1264032e3,G:1300752e3,T:1308614400,I:1313452800,F:1317081600,E:1317081600,D:1320710400,A:1324339200,B:1327968e3,C:1331596800,N:1335225600,v:1338854400,O:1342483200,H:1346112e3,J:1349740800,K:1353628800,U:1357603200,V:1361232e3,W:1364860800,X:1368489600,Y:1372118400,Z:1375747200,a:1379376e3,b:1386633600,c:1391472e3,d:1395100800,e:1398729600,f:1402358400,g:1405987200,h:1409616e3,i:1413244800,j:1417392e3,k:1421107200,l:1424736e3,m:1428278400,n:1431475200,o:1435881600,p:1439251200,q:144288e4,r:1446508800,s:1450137600,t:1453852800,u:1457395200,P:1461628800,w:1465257600,x:1470096e3,y:1474329600,z:1479168e3,QB:1520985600,EB:1529971200,BB:1536105600,CB:1540252800,AB:1544486400,R:1552953600,GB:1558396800,Q:1562630400,IB:1567468800,JB:1571788800,KB:1575331200,LB:1578355200,MB:1581379200,NB:1583798400,OB:null,PB:null}},D:{A:{0:.013698,1:.004403,2:.027396,3:.013698,4:.031962,5:.031962,6:.054792,7:.027396,8:.050226,9:.027396,G:.004706,T:.004879,I:.004879,F:.005591,E:.005591,D:.005591,A:.004534,B:.004464,C:.010424,N:.004566,v:.004706,O:.015087,H:.004393,J:.004393,K:.008652,U:.004418,V:.004393,W:.004317,X:.004566,Y:.008786,Z:.004566,a:.004461,b:.004566,c:.004326,d:.0047,e:.004461,f:.004403,g:.013698,h:.004566,i:.013698,j:.009132,k:.004566,l:.004566,m:.004464,n:.02283,o:.004464,p:.013698,q:.004566,r:.004403,s:.018264,t:.004465,u:.009132,P:.004566,w:.009132,x:.036528,y:.474864,z:.009132,QB:.018264,EB:.031962,BB:.02283,CB:.27396,AB:.018264,R:.031962,GB:.059358,Q:.031962,IB:.15981,JB:.095886,KB:.123282,LB:.150678,MB:.13698,NB:.18264,OB:.200904,PB:.196338,dB:.219168,YB:.429204,RB:16.8622,M:10.657,VB:.036528,uB:.018264,WB:0},B:"webkit",C:["G","T","I","F","E","D","A","B","C","N","v","O","H","J","K","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","P","w","x","y","z","0","1","2","3","4","5","6","7","QB","9","EB","BB","CB","AB","8","R","GB","Q","IB","JB","KB","LB","MB","NB","OB","PB","dB","YB","RB","M","VB","uB","WB"],E:"Chrome",F:{0:1464134400,1:1469059200,2:1472601600,3:1476230400,4:1480550400,5:1485302400,6:1489017600,7:149256e4,8:1520294400,9:1500940800,G:1264377600,T:1274745600,I:1283385600,F:1287619200,E:1291248e3,D:1296777600,A:1299542400,B:1303862400,C:1307404800,N:1312243200,v:1316131200,O:1316131200,H:1319500800,J:1323734400,K:1328659200,U:1332892800,V:133704e4,W:1340668800,X:1343692800,Y:1348531200,Z:1352246400,a:1357862400,b:1361404800,c:1364428800,d:1369094400,e:1374105600,f:1376956800,g:1384214400,h:1389657600,i:1392940800,j:1397001600,k:1400544e3,l:1405468800,m:1409011200,n:141264e4,o:1416268800,p:1421798400,q:1425513600,r:1429401600,s:143208e4,t:1437523200,u:1441152e3,P:1444780800,w:1449014400,x:1453248e3,y:1456963200,z:1460592e3,QB:1496707200,EB:1504569600,BB:1508198400,CB:1512518400,AB:1516752e3,R:1523923200,GB:1527552e3,Q:1532390400,IB:1536019200,JB:1539648e3,KB:1543968e3,LB:154872e4,MB:1552348800,NB:1555977600,OB:1559606400,PB:1564444800,dB:1568073600,YB:1571702400,RB:1575936e3,M:1580860800,VB:null,uB:null,WB:null}},E:{A:{G:0,T:.004566,I:.009132,F:.004465,E:.02283,D:.013698,A:.013698,B:.031962,C:.11415,N:2.84918,XB:0,TB:.008692,ZB:.09132,aB:.00456,bB:.004283,cB:.04566,SB:.09132,L:.18264,S:.38811,gB:0},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","XB","TB","G","T","ZB","I","aB","F","bB","E","D","cB","A","SB","B","L","C","S","N","gB","",""],E:"Safari",F:{XB:1205798400,TB:1226534400,G:1244419200,T:1275868800,ZB:131112e4,I:1343174400,aB:13824e5,F:13824e5,bB:1410998400,E:1413417600,D:1443657600,cB:1458518400,A:1474329600,SB:1490572800,B:1505779200,L:1522281600,C:1537142400,S:1553472e3,N:1568851200,gB:null}},F:{A:{0:.004707,1:.004326,2:.008922,3:.014349,4:.004725,5:.004566,6:.004532,7:.004566,8:.02283,9:.004403,D:.0082,B:.016581,C:.004317,O:.00685,H:.00685,J:.00685,K:.005014,U:.006015,V:.004879,W:.006597,X:.006597,Y:.013434,Z:.006702,a:.006015,b:.005595,c:.004393,d:.008652,e:.004879,f:.004879,g:.009132,h:.005152,i:.005014,j:.009758,k:.004879,l:.009132,m:.004283,n:.004367,o:.004534,p:.004367,q:.004227,r:.004418,s:.009042,t:.004227,u:.004725,P:.004417,w:.008942,x:.004707,y:.004827,z:.004707,BB:.004532,CB:.004566,AB:.02283,R:.894936,hB:.00685,iB:0,jB:.008392,kB:.004706,L:.006229,DB:.004879,mB:.008786,S:.009132},B:"webkit",C:["","","","","","","","","","","","","","","","","D","hB","iB","jB","kB","B","L","DB","mB","C","S","O","H","J","K","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","P","w","x","y","z","0","1","2","3","4","5","6","7","9","BB","CB","AB","8","R","","",""],E:"Opera",F:{0:1517961600,1:1521676800,2:1525910400,3:1530144e3,4:1534982400,5:1537833600,6:1543363200,7:1548201600,8:1573689600,9:1554768e3,D:1150761600,hB:1223424e3,iB:1251763200,jB:1267488e3,kB:1277942400,B:1292457600,L:1302566400,DB:1309219200,mB:1323129600,C:1323129600,S:1352073600,O:1372723200,H:1377561600,J:1381104e3,K:1386288e3,U:1390867200,V:1393891200,W:1399334400,X:1401753600,Y:1405987200,Z:1409616e3,a:1413331200,b:1417132800,c:1422316800,d:1425945600,e:1430179200,f:1433808e3,g:1438646400,h:1442448e3,i:1445904e3,j:1449100800,k:1454371200,l:1457308800,m:146232e4,n:1465344e3,o:1470096e3,p:1474329600,q:1477267200,r:1481587200,s:1486425600,t:1490054400,u:1494374400,P:1498003200,w:1502236800,x:1506470400,y:1510099200,z:1515024e3,BB:1561593600,CB:1566259200,AB:1570406400,R:1578441600},D:{D:"o",B:"o",C:"o",hB:"o",iB:"o",jB:"o",kB:"o",L:"o",DB:"o",mB:"o",S:"o"}},G:{A:{E:0,TB:.0012907,nB:.0012907,HB:.0012907,pB:.00903488,qB:.00645349,rB:.0141977,sB:.0245233,tB:.0154884,UB:.165209,vB:.0503372,wB:.170372,xB:.13036,yB:.219419,zB:.317512,"0B":1.65596,"1B":.555,"2B":.247814,"3B":9.29302},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","TB","nB","HB","pB","qB","rB","E","sB","tB","UB","vB","wB","xB","yB","zB","0B","1B","2B","3B","","",""],E:"iOS Safari",F:{TB:1270252800,nB:1283904e3,HB:1299628800,pB:1331078400,qB:1359331200,rB:1394409600,E:1410912e3,sB:1413763200,tB:1442361600,UB:1458518400,vB:1473724800,wB:1490572800,xB:1505779200,yB:1522281600,zB:1537142400,"0B":1553472e3,"1B":1568851200,"2B":1572220800,"3B":1580169600}},H:{A:{"4B":.895153},B:"o",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","4B","","",""],E:"Opera Mini",F:{"4B":1426464e3}},I:{A:{FB:39504e-8,G:.0039504,M:0,"5B":0,"6B":790081e-9,"7B":39504e-8,"8B":.00592561,HB:.129968,"9B":0,AC:.103106},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","5B","6B","7B","FB","G","8B","HB","9B","AC","M","","",""],E:"Android Browser",F:{"5B":1256515200,"6B":1274313600,"7B":1291593600,FB:1298332800,G:1318896e3,"8B":1341792e3,HB:1374624e3,"9B":1386547200,AC:1401667200,M:1581984e3}},J:{A:{F:0,A:.010868},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","F","A","","",""],E:"Blackberry Browser",F:{F:1325376e3,A:1359504e3}},K:{A:{A:0,B:0,C:0,P:.0111391,L:0,DB:0,S:0},B:"o",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A","B","L","DB","C","S","P","","",""],E:"Opera Mobile",F:{A:1287100800,B:1300752e3,L:1314835200,DB:1318291200,C:1330300800,S:1349740800,P:1474588800},D:{P:"webkit"}},L:{A:{M:34.2581},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","M","","",""],E:"Chrome for Android",F:{M:null}},M:{A:{Q:.222794},B:"moz",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","Q","","",""],E:"Firefox for Android",F:{Q:1567468800}},N:{A:{A:.0115934,B:.043472},B:"ms",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","A","B","","",""],E:"IE Mobile",F:{A:1340150400,B:1353456e3}},O:{A:{BC:2.20077},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","BC","","",""],E:"UC Browser for Android",F:{BC:1471392e3},D:{BC:"webkit"}},P:{A:{G:.287499,CC:.0205357,DC:.0205357,EC:.112946,FC:.0308035,GC:.23616,SB:2.42321,L:.308035},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","G","CC","DC","EC","FC","GC","SB","L","","",""],E:"Samsung Internet",F:{G:1461024e3,CC:1481846400,DC:1509408e3,EC:1528329600,FC:1546128e3,GC:1554163200,SB:1567900800,L:1582588800}},Q:{A:{HC:.24453},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","HC","","",""],E:"QQ Browser",F:{HC:1483228800}},R:{A:{IC:0},B:"webkit",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","IC","","",""],E:"Baidu Browser",F:{IC:1491004800}},S:{A:{JC:.10868},B:"moz",C:["","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","","JC","","",""],E:"KaiOS Browser",F:{JC:1527811200}}},oL=Vt((function(e,t){function r(e){return Object.keys(e).reduce((function(t,r){return t[sL.browserVersions[r]]=e[r],t}),{})}Object.defineProperty(t,"__esModule",{value:!0}),t.agents=void 0;t.agents=Object.keys(iL).reduce((function(e,t){var n=iL[t];return e[nL.browsers[t]]=Object.keys(n).reduce((function(e,t){return"A"===t?e.usage_global=r(n[t]):"C"===t?e.versions=n[t].reduce((function(e,t){return""===t?e.push(null):e.push(sL.browserVersions[t]),e}),[]):"D"===t?e.prefix_exceptions=r(n[t]):"E"===t?e.browser=n[t]:"F"===t?e.release_date=Object.keys(n[t]).reduce((function(e,r){return e[sL.browserVersions[r]]=n[t][r],e}),{}):e.prefix=n[t],e}),{}),e}),{})}));Gt(oL);oL.agents;var uL={start:"2015-09-08",lts:"2015-10-12",maintenance:"2017-04-01",end:"2018-04-30",codename:"Argon"},cL={start:"2015-10-29",maintenance:"2016-04-30",end:"2016-06-30"},lL={start:"2016-04-26",lts:"2016-10-18",maintenance:"2018-04-30",end:"2019-04-30",codename:"Boron"},pL={start:"2016-10-25",maintenance:"2017-04-30",end:"2017-06-30"},dL={start:"2017-05-30",lts:"2017-10-31",maintenance:"2019-01-01",end:"2019-12-31",codename:"Carbon"},fL={start:"2017-10-01",maintenance:"2018-04-01",end:"2018-06-30"},hL={start:"2018-04-24",lts:"2018-10-30",maintenance:"2020-04-30",end:"2021-04-30",codename:"Dubnium"},mL={start:"2018-10-23",maintenance:"2019-04-22",end:"2019-06-01"},yL={start:"2019-04-23",lts:"2019-10-21",maintenance:"2020-10-20",end:"2022-04-30",codename:"Erbium"},gL={start:"2019-10-22",maintenance:"2020-04-01",end:"2020-06-01"},vL={start:"2020-04-21",lts:"2020-10-20",maintenance:"2021-10-19",end:"2023-04-30",codename:""},bL={"v0.10":{start:"2013-03-11",end:"2016-10-31"},"v0.12":{start:"2015-02-06",end:"2016-12-31"},v4:uL,v5:cL,v6:lL,v7:pL,v8:dL,v9:fL,v10:hL,v11:mL,v12:yL,v13:gL,v14:vL},xL=Object.freeze({__proto__:null,v4:uL,v5:cL,v6:lL,v7:pL,v8:dL,v9:fL,v10:hL,v11:mL,v12:yL,v13:gL,v14:vL,default:bL}),EL={"9.0":"82",8.1:"80","8.0":"79",7.1:"78","7.0":"78",6.1:"76","6.0":"76","5.0":"72",4.2:"69",4.1:"69","4.0":"69",3.1:"66","3.0":"66",2.1:"61","2.0":"61",1.8:"59",1.7:"58",1.6:"56",1.5:"54",1.4:"53",1.3:"52",1.2:"51",1.1:"50","1.0":"49",.37:"49",.36:"47",.35:"45",.34:"45",.33:"45",.32:"45",.31:"44","0.30":"44",.29:"43",.28:"43",.27:"42",.26:"42",.25:"42",.24:"41",.23:"41",.22:"41",.21:"40","0.20":"39"};function AL(e){this.name="BrowserslistError",this.message=e,this.browserslist=!0,Error.captureStackTrace&&Error.captureStackTrace(this,AL)}AL.prototype=Error.prototype;var wL=AL;function SL(){}var DL={loadQueries:function(){throw new wL("Sharable configs are not supported in client-side build of Browserslist")},getStat:function(e){return e.stats},loadConfig:function(e){if(e.config)throw new wL("Browserslist config are not supported in client-side build")},loadCountry:function(){throw new wL("Country statistics is not supported in client-side build of Browserslist")},currentNode:function(e,t){return e(["maintained node versions"],t)[0]},parseConfig:SL,readConfig:SL,findConfig:SL,clearCaches:SL,oldDataWarning:SL},CL=Wt(tL),TL=Wt(xL),jL=oL.agents;function PL(e,t){return 0===(e+".").indexOf(t+".")}function kL(e){return e.filter((function(e){return"string"==typeof e}))}function FL(e){var t=e;return 3===e.split(".").length&&(t=e.split(".").slice(0,-1).join(".")),t}function _L(e){return function(t){return e+" "+t}}function IL(e){return parseInt(e.split(".")[0])}function BL(e,t){if(0===e.length)return[];var r=OL(e.map(IL)),n=r[r.length-t];if(!n)return e;for(var a=[],s=e.length-1;s>=0&&!(n>IL(e[s]));s--)a.unshift(e[s]);return a}function OL(e){for(var t=[],r=0;r<e.length;r++)-1===t.indexOf(e[r])&&t.push(e[r]);return t}function NL(e,t,r){for(var n in r)e[t+" "+n]=r[n]}function RL(e,t){return t=parseFloat(t),">"===e?function(e){return parseFloat(e)>t}:">="===e?function(e){return parseFloat(e)>=t}:"<"===e?function(e){return parseFloat(e)<t}:function(e){return parseFloat(e)<=t}}function ML(e){return parseInt(e)}function LL(e,t){return e<t?-1:e>t?1:0}function UL(e,t){return LL(parseInt(e[0]),parseInt(t[0]))||LL(parseInt(e[1]||"0"),parseInt(t[1]||"0"))||LL(parseInt(e[2]||"0"),parseInt(t[2]||"0"))}function GL(e,t){switch(void 0===(t=t.split(".").map(ML))[1]&&(t[1]="x"),e){case"<=":return function(e){return VL(e=e.split(".").map(ML),t)<=0};default:case">=":return function(e){return VL(e=e.split(".").map(ML),t)>=0}}}function VL(e,t){return e[0]!==t[0]?e[0]<t[0]?-1:1:"x"===t[1]?0:e[1]!==t[1]?e[1]<t[1]?-1:1:0}function WL(e,t){var r=function(e,t){return-1!==e.versions.indexOf(t)?t:!!ZL.versionAliases[e.name][t]&&ZL.versionAliases[e.name][t]}(e,t);return r||1===e.versions.length&&e.versions[0]}function HL(e,t){return e/=1e3,Object.keys(jL).reduce((function(r,n){var a=KL(n,t);if(!a)return r;var s=Object.keys(a.releaseDate).filter((function(t){return a.releaseDate[t]>=e}));return r.concat(s.map(_L(a.name)))}),[])}function qL(e){return{name:e.name,versions:e.versions,released:e.released,releaseDate:e.releaseDate}}function KL(e,t){if(e=e.toLowerCase(),e=ZL.aliases[e]||e,t.mobileToDesktop&&ZL.desktopNames[e]){var r=ZL.data[ZL.desktopNames[e]];if("android"===e)return a=qL(ZL.data[e]),s=r,a.released=zL(a.released,s.released),a.versions=zL(a.versions,s.versions),a;var n=qL(r);return n.name=e,"op_mob"===e&&(n=function(e,t){e.versions=e.versions.map((function(e){return t[e]||e})),e.released=e.versions.map((function(e){return t[e]||e}));var r={};for(var n in e.releaseDate)r[t[n]||n]=e.releaseDate[n];return e.releaseDate=r,e}(n,{"10.0-10.1":"10"})),n}var a,s;return ZL.data[e]}function zL(e,t){var r=t[t.length-1];return e.filter((function(e){return/^(?:[2-4]\.|[34]$)/.test(e)})).concat(t.slice(37-r-1))}function XL(e,t){var r=KL(e,t);if(!r)throw new wL("Unknown browser "+e);return r}function YL(e){return new wL("Unknown browser query `"+e+"`. Maybe you are using old Browserslist or made typo in query.")}function JL(e,t,r){if(r.mobileToDesktop)return e;var n=ZL.data.android.released,a=n[n.length-1]-37-t;return a>0?e.slice(-1):e.slice(a-1)}function $L(e,t){return(e=Array.isArray(e)?function e(t){return Array.isArray(t)?t.reduce((function(t,r){return t.concat(e(r))}),[]):[t]}(e.map(eU)):eU(e)).reduce((function(e,r,n){var a=r.queryString,s=0===a.indexOf("not ");if(s){if(0===n)throw new wL("Write any browsers query (for instance, `defaults`) before `"+a+"`");a=a.slice(4)}for(var i=0;i<rU.length;i++){var o=rU[i],u=a.match(o.regexp);if(u){var c=[t].concat(u.slice(1)),l=o.select.apply(ZL,c).map((function(e){var r=e.split(" ");return"0"===r[1]?r[0]+" "+KL(r[0],t).versions[0]:e}));switch(r.type){case 2:return s?e.filter((function(e){return-1===l.indexOf(e)})):e.filter((function(e){return-1!==l.indexOf(e)}));case 1:default:if(s){var p={};return l.forEach((function(e){p[e]=!0})),e.filter((function(e){return!p[e]}))}return e.concat(l)}}}throw YL(a)}),[])}var QL={};function ZL(e,t){if(void 0===t&&(t={}),void 0===t.path&&(t.path=vj.resolve?vj.resolve("."):"."),null==e){var r=ZL.loadConfig(t);e=r||ZL.defaults}if("string"!=typeof e&&!Array.isArray(e))throw new wL("Browser queries must be an array or string. Got "+typeof e+".");var n={ignoreUnknownVersions:t.ignoreUnknownVersions,dangerousExtend:t.dangerousExtend,mobileToDesktop:t.mobileToDesktop};DL.oldDataWarning(ZL.data);var a=DL.getStat(t,ZL.data);if(a)for(var s in n.customUsage={},a)NL(n.customUsage,s,a[s]);var i=JSON.stringify([e,n]);if(QL[i])return QL[i];var o=OL($L(e,n)).sort((function(e,t){if(e=e.split(" "),t=t.split(" "),e[0]===t[0]){var r=e[1].split("-")[0];return UL(t[1].split("-")[0].split("."),r.split("."))}return LL(e[0],t[0])}));return es.env.BROWSERSLIST_DISABLE_CACHE||(QL[i]=o),o}function eU(e){var t=[];do{e=tU(e,t)}while(e);return t}function tU(e,t){var r=/^(?:,\s*|\s+or\s+)(.*)/i,n=/^\s+and\s+(.*)/i;return function(e,t){for(var r=1,n=e.length;r<=n;r++){var a=e.substr(-r,r);if(t(a,r,n))return e.slice(0,-r)}return""}(e,(function(e,a,s){return n.test(e)?(t.unshift({type:2,queryString:e.match(n)[1]}),!0):r.test(e)?(t.unshift({type:1,queryString:e.match(r)[1]}),!0):a===s&&(t.unshift({type:1,queryString:e.trim()}),!0)}))}ZL.data={},ZL.usage={global:{},custom:null},ZL.defaults=["> 0.5%","last 2 versions","Firefox ESR","not dead"],ZL.aliases={fx:"firefox",ff:"firefox",ios:"ios_saf",explorer:"ie",blackberry:"bb",explorermobile:"ie_mob",operamini:"op_mini",operamobile:"op_mob",chromeandroid:"and_chr",firefoxandroid:"and_ff",ucandroid:"and_uc",qqandroid:"and_qq"},ZL.desktopNames={and_chr:"chrome",and_ff:"firefox",ie_mob:"ie",op_mob:"opera",android:"chrome"},ZL.versionAliases={},ZL.clearCaches=DL.clearCaches,ZL.parseConfig=DL.parseConfig,ZL.readConfig=DL.readConfig,ZL.findConfig=DL.findConfig,ZL.loadConfig=DL.loadConfig,ZL.coverage=function(e,t){var r;if(void 0===t)r=ZL.usage.global;else if("my stats"===t){var n={};n.path=vj.resolve?vj.resolve("."):".";var a=DL.getStat(n);if(!a)throw new wL("Custom usage statistics was not provided");for(var s in r={},a)NL(r,s,a[s])}else if("string"==typeof t)t=t.length>2?t.toLowerCase():t.toUpperCase(),DL.loadCountry(ZL.usage,t,ZL.data),r=ZL.usage[t];else for(var i in"dataByBrowser"in t&&(t=t.dataByBrowser),r={},t)for(var o in t[i])r[i+" "+o]=t[i][o];return e.reduce((function(e,t){var n=r[t];return void 0===n&&(n=r[t.replace(/ \S+$/," 0")]),e+(n||0)}),0)};var rU=[{regexp:/^last\s+(\d+)\s+major\s+versions?$/i,select:function(e,t){return Object.keys(jL).reduce((function(r,n){var a=KL(n,e);if(!a)return r;var s=BL(a.released,t);return s=s.map(_L(a.name)),"android"===a.name&&(s=JL(s,t,e)),r.concat(s)}),[])}},{regexp:/^last\s+(\d+)\s+versions?$/i,select:function(e,t){return Object.keys(jL).reduce((function(r,n){var a=KL(n,e);if(!a)return r;var s=a.released.slice(-t);return s=s.map(_L(a.name)),"android"===a.name&&(s=JL(s,t,e)),r.concat(s)}),[])}},{regexp:/^last\s+(\d+)\s+electron\s+major\s+versions?$/i,select:function(e,t){return BL(Object.keys(EL).reverse(),t).map((function(e){return"chrome "+EL[e]}))}},{regexp:/^last\s+(\d+)\s+(\w+)\s+major\s+versions?$/i,select:function(e,t,r){var n=XL(r,e),a=BL(n.released,t).map(_L(n.name));return"android"===n.name&&(a=JL(a,t,e)),a}},{regexp:/^last\s+(\d+)\s+electron\s+versions?$/i,select:function(e,t){return Object.keys(EL).reverse().slice(-t).map((function(e){return"chrome "+EL[e]}))}},{regexp:/^last\s+(\d+)\s+(\w+)\s+versions?$/i,select:function(e,t,r){var n=XL(r,e),a=n.released.slice(-t).map(_L(n.name));return"android"===n.name&&(a=JL(a,t,e)),a}},{regexp:/^unreleased\s+versions$/i,select:function(e){return Object.keys(jL).reduce((function(t,r){var n=KL(r,e);if(!n)return t;var a=n.versions.filter((function(e){return-1===n.released.indexOf(e)}));return a=a.map(_L(n.name)),t.concat(a)}),[])}},{regexp:/^unreleased\s+electron\s+versions?$/i,select:function(){return[]}},{regexp:/^unreleased\s+(\w+)\s+versions?$/i,select:function(e,t){var r=XL(t,e);return r.versions.filter((function(e){return-1===r.released.indexOf(e)})).map(_L(r.name))}},{regexp:/^last\s+(\d*.?\d+)\s+years?$/i,select:function(e,t){return HL(Date.now()-31558432982.4*t,e)}},{regexp:/^since (\d+)(?:-(\d+))?(?:-(\d+))?$/i,select:function(e,t,r,n){return t=parseInt(t),r=parseInt(r||"01")-1,n=parseInt(n||"01"),HL(Date.UTC(t,r,n,0,0,0),e)}},{regexp:/^(>=?|<=?)\s*(\d*\.?\d+)%$/,select:function(e,t,r){r=parseFloat(r);var n=ZL.usage.global;return Object.keys(n).reduce((function(e,a){return">"===t?n[a]>r&&e.push(a):"<"===t?n[a]<r&&e.push(a):"<="===t?n[a]<=r&&e.push(a):n[a]>=r&&e.push(a),e}),[])}},{regexp:/^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+my\s+stats$/,select:function(e,t,r){if(r=parseFloat(r),!e.customUsage)throw new wL("Custom usage statistics was not provided");var n=e.customUsage;return Object.keys(n).reduce((function(e,a){return">"===t?n[a]>r&&e.push(a):"<"===t?n[a]<r&&e.push(a):"<="===t?n[a]<=r&&e.push(a):n[a]>=r&&e.push(a),e}),[])}},{regexp:/^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+(\S+)\s+stats$/,select:function(e,t,r,n){r=parseFloat(r);var a=DL.loadStat(e,n,ZL.data);if(a)for(var s in e.customUsage={},a)NL(e.customUsage,s,a[s]);if(!e.customUsage)throw new wL("Custom usage statistics was not provided");var i=e.customUsage;return Object.keys(i).reduce((function(e,n){return">"===t?i[n]>r&&e.push(n):"<"===t?i[n]<r&&e.push(n):"<="===t?i[n]<=r&&e.push(n):i[n]>=r&&e.push(n),e}),[])}},{regexp:/^(>=?|<=?)\s*(\d*\.?\d+)%\s+in\s+((alt-)?\w\w)$/,select:function(e,t,r,n){r=parseFloat(r),n=2===n.length?n.toUpperCase():n.toLowerCase(),DL.loadCountry(ZL.usage,n,ZL.data);var a=ZL.usage[n];return Object.keys(a).reduce((function(e,n){return">"===t?a[n]>r&&e.push(n):"<"===t?a[n]<r&&e.push(n):"<="===t?a[n]<=r&&e.push(n):a[n]>=r&&e.push(n),e}),[])}},{regexp:/^cover\s+(\d*\.?\d+)%(\s+in\s+(my\s+stats|(alt-)?\w\w))?$/,select:function(e,t,r){t=parseFloat(t);var n=ZL.usage.global;if(r)if(r.match(/^\s+in\s+my\s+stats$/)){if(!e.customUsage)throw new wL("Custom usage statistics was not provided");n=e.customUsage}else{var a=r.match(/\s+in\s+((alt-)?\w\w)/)[1];a=2===a.length?a.toUpperCase():a.toLowerCase(),DL.loadCountry(ZL.usage,a,ZL.data),n=ZL.usage[a]}for(var s,i=Object.keys(n).sort((function(e,t){return n[t]-n[e]})),o=0,u=[],c=0;c<=i.length&&(s=i[c],0!==n[s])&&(o+=n[s],u.push(s),!(o>=t));c++);return u}},{regexp:/^electron\s+([\d.]+)\s*-\s*([\d.]+)$/i,select:function(e,t,r){var n=FL(t),a=FL(r);if(!EL[n])throw new wL("Unknown version "+t+" of electron");if(!EL[a])throw new wL("Unknown version "+r+" of electron");return t=parseFloat(t),r=parseFloat(r),Object.keys(EL).filter((function(e){var n=parseFloat(e);return n>=t&&n<=r})).map((function(e){return"chrome "+EL[e]}))}},{regexp:/^node\s+([\d.]+)\s*-\s*([\d.]+)$/i,select:function(e,t,r){var n=CL.filter((function(e){return"nodejs"===e.name})).map((function(e){return e.version})),a=/^(0|[1-9]\d*)(\.(0|[1-9]\d*)){0,2}$/;if(!a.test(t))throw new wL("Unknown version "+t+" of Node.js");if(!a.test(r))throw new wL("Unknown version "+r+" of Node.js");return n.filter(GL(">=",t)).filter(GL("<=",r)).map((function(e){return"node "+e}))}},{regexp:/^(\w+)\s+([\d.]+)\s*-\s*([\d.]+)$/i,select:function(e,t,r,n){var a=XL(t,e);return r=parseFloat(WL(a,r)||r),n=parseFloat(WL(a,n)||n),a.released.filter((function(e){var t=parseFloat(e);return t>=r&&t<=n})).map(_L(a.name))}},{regexp:/^electron\s*(>=?|<=?)\s*([\d.]+)$/i,select:function(e,t,r){var n=FL(r);return Object.keys(EL).filter(RL(t,n)).map((function(e){return"chrome "+EL[e]}))}},{regexp:/^node\s*(>=?|<=?)\s*([\d.]+)$/i,select:function(e,t,r){return CL.filter((function(e){return"nodejs"===e.name})).map((function(e){return e.version})).filter(function(e,t){return(t=t.split(".").map(ML))[1]=t[1]||0,t[2]=t[2]||0,">"===e?function(e){return UL(e=e.split(".").map(ML),t)>0}:">="===e?function(e){return UL(e=e.split(".").map(ML),t)>=0}:"<"===e?function(e){return e=e.split(".").map(ML),UL(t,e)>0}:function(e){return e=e.split(".").map(ML),UL(t,e)>=0}}(t,r)).map((function(e){return"node "+e}))}},{regexp:/^(\w+)\s*(>=?|<=?)\s*([\d.]+)$/,select:function(e,t,r,n){var a=XL(t,e),s=ZL.versionAliases[a.name][n];return s&&(n=s),a.released.filter(RL(r,n)).map((function(e){return a.name+" "+e}))}},{regexp:/^(firefox|ff|fx)\s+esr$/i,select:function(){return["firefox 68"]}},{regexp:/(operamini|op_mini)\s+all/i,select:function(){return["op_mini all"]}},{regexp:/^electron\s+([\d.]+)$/i,select:function(e,t){var r=FL(t),n=EL[r];if(!n)throw new wL("Unknown version "+t+" of electron");return["chrome "+n]}},{regexp:/^node\s+(\d+(\.\d+)?(\.\d+)?)$/i,select:function(e,t){var r=CL.filter((function(e){return"nodejs"===e.name})).filter((function(e){return PL(e.version,t)}));if(0===r.length){if(e.ignoreUnknownVersions)return[];throw new wL("Unknown version "+t+" of Node.js")}return["node "+r[r.length-1].version]}},{regexp:/^current\s+node$/i,select:function(e){return[DL.currentNode($L,e)]}},{regexp:/^maintained\s+node\s+versions$/i,select:function(e){var t=Date.now();return $L(Object.keys(TL).filter((function(e){return t<Date.parse(TL[e].end)&&t>Date.parse(TL[e].start)&&function(e){var t=e.slice(1);return CL.some((function(e){return PL(e.version,t)}))}(e)})).map((function(e){return"node "+e.slice(1)})),e)}},{regexp:/^phantomjs\s+1.9$/i,select:function(){return["safari 5"]}},{regexp:/^phantomjs\s+2.1$/i,select:function(){return["safari 6"]}},{regexp:/^(\w+)\s+(tp|[\d.]+)$/i,select:function(e,t,r){/^tp$/i.test(r)&&(r="TP");var n=XL(t,e),a=WL(n,r);if(a)r=a;else{if(!(a=WL(n,a=-1===r.indexOf(".")?r+".0":r.replace(/\.0$/,"")))){if(e.ignoreUnknownVersions)return[];throw new wL("Unknown version "+r+" of "+t)}r=a}return[n.name+" "+r]}},{regexp:/^extends (.+)$/i,select:function(e,t){return $L(DL.loadQueries(e,t),e)}},{regexp:/^defaults$/i,select:function(e){return $L(ZL.defaults,e)}},{regexp:/^dead$/i,select:function(e){return $L(["ie <= 10","ie_mob <= 11","bb <= 10","op_mob <= 12.1","samsung 4"],e)}},{regexp:/^(\w+)$/i,select:function(e,t){throw KL(t,e)?new wL("Specify versions in Browserslist query for browser "+t):YL(t)}}];!function(){for(var e in jL){var t=jL[e];ZL.data[e]={name:e,versions:kL(jL[e].versions),released:kL(jL[e].versions.slice(0,-3)),releaseDate:jL[e].release_date},NL(ZL.usage.global,e,t.usage_global),ZL.versionAliases[e]={};for(var r=0;r<t.versions.length;r++){var n=t.versions[r];if(n&&-1!==n.indexOf("-"))for(var a=n.split("-"),s=0;s<a.length;s++)ZL.versionAliases[e][a[s]]=n}}}();var nU=ZL,aU=Vt((function(e){var t=[],r=[],n=function(e,n){if(e===n)return 0;var a=e;e.length>n.length&&(e=n,n=a);for(var s=e.length,i=n.length;s>0&&e.charCodeAt(~-s)===n.charCodeAt(~-i);)s--,i--;for(var o,u,c,l,p=0;p<s&&e.charCodeAt(p)===n.charCodeAt(p);)p++;if(i-=p,0===(s-=p))return i;for(var d=0,f=0;d<s;)r[d]=e.charCodeAt(p+d),t[d]=++d;for(;f<i;)for(o=n.charCodeAt(p+f),c=f++,u=f,d=0;d<s;d++)l=o===r[d]?c:c+1,c=t[d],u=t[d]=c>u?l>u?u+1:l:l>c?c+1:l;return u};e.exports=n,e.exports.default=n}));function sU(e,t){var r=Number.POSITIVE_INFINITY,n=void 0,a=t,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o,c=aU(e,u);c<r&&(r=c,n=u)}return n}var iU=function(e,t,r,n,a,s,i,o){if(void 0===t)throw new Error("invariant requires an error message argument");if(!e){var u;if(void 0===t)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[r,n,a,s,i,o],l=0;(u=new Error(t.replace(/%s/g,(function(){return c[l++]})))).name="Invariant Violation"}throw u.framesToPop=1,u}},oU=Wt(Object.freeze({__proto__:null,default:{"es6.module":{edge:"16",firefox:"60",chrome:"61",safari:"10.1",opera:"48",ios_saf:"10.3",android:"61",op_mob:"48",and_chr:"61",and_ff:"60",samsung:"8.2"}}})),uU={safari:"tp"},cU={and_chr:"chrome",and_ff:"firefox",android:"android",chrome:"chrome",edge:"edge",firefox:"firefox",ie:"ie",ie_mob:"ie",ios_saf:"ios",node:"node",op_mob:"opera",opera:"opera",safari:"safari",samsung:"samsung"},lU=/^(\d+|\d+.\d+)$/;function pU(e,t){return e&&Iw.lt(e,t)?e:t}function dU(e){if("string"==typeof e&&Iw.valid(e))return e;iU("number"==typeof e||"string"==typeof e&&lU.test(e),"'"+e+"' is not a valid version");for(var t=e.toString().split(".");t.length<3;)t.push("0");return t.join(".")}function fU(e,t){var r=uU[t];return!!r&&r===e.toString().toLowerCase()}function hU(e,t){var r=e[t];return r||"android"!==t?r:e.chrome}var mU={esmodules:"esmodules",node:"node",browsers:"browsers",chrome:"chrome",opera:"opera",edge:"edge",firefox:"firefox",safari:"safari",ie:"ie",ios:"ios",android:"android",electron:"electron",samsung:"samsung",uglify:"uglify"};function yU(e){if("string"!=typeof e)return e;var t=[Iw.major(e)],r=Iw.minor(e),n=Iw.patch(e);return(r||n)&&t.push(r),n&&t.push(n),t.join(".")}var gU=Wt(Object.freeze({__proto__:null,default:{"proposal-nullish-coalescing-operator":{chrome:"80",firefox:"72",safari:"tp",opera:"67"},"proposal-optional-chaining":{chrome:"80",firefox:"74",safari:"tp",opera:"67"},"proposal-json-strings":{chrome:"66",edge:"79",firefox:"62",safari:"12",node:"10",ios:"12",samsung:"9",opera:"53",electron:"3.1"},"proposal-optional-catch-binding":{chrome:"66",edge:"79",firefox:"58",safari:"11.1",node:"10",ios:"11.3",samsung:"9",opera:"53",electron:"3.1"},"proposal-async-generator-functions":{chrome:"63",edge:"79",firefox:"57",safari:"12",node:"10",ios:"12",samsung:"8",opera:"50",electron:"3.1"},"proposal-object-rest-spread":{chrome:"60",edge:"79",firefox:"55",safari:"11.1",node:"8.3",ios:"11.3",samsung:"8",opera:"47",electron:"2.1"},"transform-dotall-regex":{chrome:"62",edge:"79",safari:"11.1",node:"8.10",ios:"11.3",samsung:"8",opera:"49",electron:"3.1"},"proposal-unicode-property-regex":{chrome:"64",edge:"79",safari:"11.1",node:"10",ios:"11.3",samsung:"9",opera:"51",electron:"3.1"},"transform-named-capturing-groups-regex":{chrome:"64",edge:"79",safari:"11.1",node:"10",ios:"11.3",samsung:"9",opera:"51",electron:"3.1"},"transform-async-to-generator":{chrome:"55",edge:"15",firefox:"52",safari:"11",node:"7.6",ios:"11",samsung:"6",opera:"42",electron:"1.6"},"transform-exponentiation-operator":{chrome:"52",edge:"14",firefox:"52",safari:"10.1",node:"7",ios:"10.3",samsung:"6",opera:"39",electron:"1.3"},"transform-template-literals":{chrome:"41",edge:"13",firefox:"34",safari:"13",node:"4",ios:"13",samsung:"3.4",opera:"28",electron:"0.24"},"transform-literals":{chrome:"44",edge:"12",firefox:"53",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"transform-function-name":{chrome:"51",edge:"79",firefox:"53",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"transform-arrow-functions":{chrome:"47",edge:"13",firefox:"45",safari:"10",node:"6",ios:"10",samsung:"5",opera:"34",electron:"0.36"},"transform-block-scoped-functions":{chrome:"41",edge:"12",firefox:"46",safari:"10",node:"4",ie:"11",ios:"10",samsung:"3.4",opera:"28",electron:"0.24"},"transform-classes":{chrome:"46",edge:"13",firefox:"45",safari:"10",node:"5",ios:"10",samsung:"5",opera:"33",electron:"0.36"},"transform-object-super":{chrome:"46",edge:"13",firefox:"45",safari:"10",node:"5",ios:"10",samsung:"5",opera:"33",electron:"0.36"},"transform-shorthand-properties":{chrome:"43",edge:"12",firefox:"33",safari:"9",node:"4",ios:"9",samsung:"4",opera:"30",electron:"0.29"},"transform-duplicate-keys":{chrome:"42",edge:"12",firefox:"34",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"29",electron:"0.27"},"transform-computed-properties":{chrome:"44",edge:"12",firefox:"34",safari:"7.1",node:"4",ios:"8",samsung:"4",opera:"31",electron:"0.31"},"transform-for-of":{chrome:"51",edge:"15",firefox:"53",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"transform-sticky-regex":{chrome:"49",edge:"13",firefox:"3",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"transform-unicode-regex":{chrome:"50",edge:"13",firefox:"46",safari:"12",node:"6",ios:"12",samsung:"5",opera:"37",electron:"1.1"},"transform-spread":{chrome:"46",edge:"13",firefox:"36",safari:"10",node:"5",ios:"10",samsung:"5",opera:"33",electron:"0.36"},"transform-parameters":{chrome:"49",edge:"18",firefox:"53",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"transform-destructuring":{chrome:"51",edge:"15",firefox:"53",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"transform-block-scoping":{chrome:"49",edge:"14",firefox:"51",safari:"11",node:"6",ios:"11",samsung:"5",opera:"36",electron:"1"},"transform-typeof-symbol":{chrome:"38",edge:"12",firefox:"36",safari:"9",node:"0.12",ios:"9",samsung:"3",opera:"25",electron:"0.2"},"transform-new-target":{chrome:"46",edge:"14",firefox:"41",safari:"10",node:"5",ios:"10",samsung:"5",opera:"33",electron:"0.36"},"transform-regenerator":{chrome:"50",edge:"13",firefox:"53",safari:"10",node:"6",ios:"10",samsung:"5",opera:"37",electron:"1.1"},"transform-member-expression-literals":{chrome:"7",opera:"12",edge:"12",firefox:"2",safari:"5.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"5"},"transform-property-literals":{chrome:"7",opera:"12",edge:"12",firefox:"2",safari:"5.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"5"},"transform-reserved-words":{chrome:"13",opera:"10.50",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4.4",ios:"6",phantom:"2",samsung:"1",electron:"0.2"}}}));function vU(e,t,r){var n,a,s,i=void 0===r?{}:r,o=i.compatData,u=void 0===o?gU:o,c=i.includes,l=i.excludes;return(!l||!l.has(e))&&(!(!c||!c.has(e))||(n=t,a=u[e],!(0!==(s=Object.keys(n)).length&&0===s.filter((function(e){var t=hU(a,e);if(!t)return!0;var r=n[e];if(fU(r,e))return!1;if(fU(t,e))return!0;if(!Iw.valid(r.toString()))throw new Error('Invalid version passed for target "'+e+'": "'+r+'". Versions must be in semver format (major.minor.patch)');return Iw.gt(dU(t),r.toString())})).length)))}function bU(e,t,r,n,a,s,i){var o=new Set,u={compatData:e,includes:t,excludes:r};for(var c in e)if(vU(c,n,u))o.add(c);else if(i){var l=i.get(c);l&&o.add(l)}return a&&a.forEach((function(e){return!r.has(e)&&o.add(e)})),s&&s.forEach((function(e){return!t.has(e)&&o.delete(e)})),o}var xU=nU.defaults,EU=[].concat(Object.keys(nU.data),Object.keys(nU.aliases));function AU(e){return iU(void 0===e||function(e){return"string"==typeof e||Array.isArray(e)}(e),"Invalid Option: '"+e+"' is not a valid browserslist query"),e}function wU(e){return e.reduce((function(e,t){var r=t.split(" "),n=r[0],a=r[1],s=cU[n];if(!s)return e;try{var i=a.split("-")[0].toLowerCase(),o=fU(i,n);if(!e[s])return e[s]=o?i:dU(i),e;var u=e[s],c=fU(u,n);if(c&&o)e[s]=function(e,t,r){var n=uU[r],a=[e,t].some((function(e){return e===n}));return a?e===a?t:e||t:pU(e,t)}(u,i,n);else if(c)e[s]=dU(i);else if(!c&&!o){var l=dU(i);e[s]=pU(u,l)}}catch(e){}return e}),{})}function SU(e,t){try{return dU(t)}catch(r){throw new Error("Invalid Option: '"+t+"' is not a valid value for 'targets."+e+"'.")}}var DU={__default:function(e,t){return[e,fU(t,e)?t.toLowerCase():SU(e,t)]},node:function(e,t){return[e,!0===t||"current"===t?es.versions.node:SU(e,t)]}};function CU(e,t){void 0===e&&(e={}),void 0===t&&(t={});if(function(e){var t=Object.keys(mU);for(var r in e)if(!mU[r])throw new Error("Invalid Option: '"+r+"' is not a valid target\n Maybe you meant to use '"+sU(r,t)+"'?")}(e),e.esmodules){var r=oU["es6.module"];e.browsers=Object.keys(r).map((function(e){return e+" "+r[e]})).join(", ")}delete e.esmodules;var n,a,s=AU(e.browsers),i=Object.keys(e).length>0,o=!!e.browsers,u=!t.ignoreBrowserslistConfig&&!i;if(o||u){i||(nU.defaults=function(e){return Object.keys(e).reduce((function(t,r){if(EU.indexOf(r)>=0){var n=e[r];return t.concat(r+" "+n)}return t}),[])}(e));var c=wU(nU(s,{path:t.configPath,mobileToDesktop:!0}));n=c,a=e,e=Object.keys(a).reduce((function(e,t){return t!==mU.browsers&&(e[t]=a[t]),e}),n),nU.defaults=xU}var l,p=Object.keys(e).filter((function(e){return e!==mU.esmodules})).sort().reduce((function(t,r){if(r!==mU.browsers){var n=e[r];"number"==typeof n&&n%1!=0&&t.decimalWarnings.push({target:r,value:n});var a=(DU[r]||DU.__default)(r,n),s=a[0],i=a[1];i&&(t.targets[s]=i)}return t}),{targets:{},decimalWarnings:[]});return(l=p.decimalWarnings)&&l.length&&(console.log("Warning, the following targets are using a decimal version:"),console.log(""),l.forEach((function(e){var t=e.target,r=e.value;return console.log(" "+t+": "+r)})),console.log(""),console.log("We recommend using a string for minor/patch versions to avoid numbers like 6.10"),console.log("getting parsed as 6.1, which can lead to unexpected behavior."),console.log("")),p.targets}var TU=function(e){return e>1?"s":""},jU=function(e,t,r){var n=function(e,t,r){var n=r[e]||{};return Object.keys(t).reduce((function(e,r){var a=hU(n,r),s=t[r];if(a){var i=fU(a,r);fU(s,r)||!i&&!Iw.lt(s.toString(),dU(a))||(e[r]=yU(s))}else e[r]=yU(s);return e}),{})}(e,t,r),a=JSON.stringify(n).replace(/,/g,", ").replace(/^\{"/,'{ "').replace(/"\}$/,'" }');console.log(" "+e+" "+a)},PU=function(e,t,r,n,a,s){if("test"===es.env.BABEL_ENV&&(n=n.replace(/\\/g,"/")),t)if(r.size){console.log("\n["+n+"] Replaced "+e+" entries with the following polyfill"+TU(r.size)+":");var i=r,o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}jU(c,a,s)}}else console.log("\n["+n+"] Based on your targets, polyfills were not added.");else console.log("\n["+n+"] Import of "+e+" was not found.")},kU=function(e,t,r,n){if("test"===es.env.BABEL_ENV&&(t=t.replace(/\\/g,"/")),e.size){console.log("\n["+t+"] Added following core-js polyfill"+TU(e.size)+":");var a=e,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}jU(o,r,n)}}else console.log("\n["+t+"] Based on your code and targets, core-js polyfills were not added.")},FU=["transform-typeof-symbol"];var _U={auto:"transform-modules-commonjs",amd:"transform-modules-amd",commonjs:"transform-modules-commonjs",cjs:"transform-modules-commonjs",systemjs:"transform-modules-systemjs",umd:"transform-modules-umd"},IU={"es.symbol":{chrome:"49",edge:"15",electron:"0.37",firefox:"51",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.symbol.description":{chrome:"70",edge:"74",electron:"5.0",firefox:"63",ios:"12.2",node:"11.0",opera:"57",opera_mobile:"49",safari:"12.1",samsung:"10.0"},"es.symbol.async-iterator":{chrome:"63",edge:"74",electron:"3.0",firefox:"55",ios:"12.0",node:"10.0",opera:"50",opera_mobile:"46",safari:"12.0",samsung:"8.0"},"es.symbol.has-instance":{chrome:"50",edge:"15",electron:"1.1",firefox:"49",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.symbol.is-concat-spreadable":{chrome:"48",edge:"15",electron:"0.37",firefox:"48",ios:"10.0",node:"6.0",opera:"35",opera_mobile:"35",safari:"10.0",samsung:"5.0"},"es.symbol.iterator":{chrome:"39",edge:"13",electron:"0.20",firefox:"36",ios:"9.0",node:"1.0",opera:"26",opera_mobile:"26",safari:"9.0",samsung:"3.4"},"es.symbol.match":{chrome:"50",edge:"74",electron:"1.1",firefox:"40",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.symbol.match-all":{chrome:"73",edge:"74",electron:"5.0",firefox:"67",ios:"13.0",node:"12.0",opera:"60",opera_mobile:"52",safari:"13",samsung:"11.0"},"es.symbol.replace":{chrome:"50",edge:"74",electron:"1.1",firefox:"49",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.symbol.search":{chrome:"50",edge:"74",electron:"1.1",firefox:"49",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.symbol.species":{chrome:"51",edge:"13",electron:"1.2",firefox:"41",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.symbol.split":{chrome:"50",edge:"74",electron:"1.1",firefox:"49",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.symbol.to-primitive":{chrome:"47",edge:"15",electron:"0.36",firefox:"44",ios:"10.0",node:"6.0",opera:"34",opera_mobile:"34",safari:"10.0",samsung:"5.0"},"es.symbol.to-string-tag":{chrome:"49",edge:"15",electron:"0.37",firefox:"51",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.symbol.unscopables":{chrome:"39",edge:"13",electron:"0.20",firefox:"48",ios:"9.0",node:"1.0",opera:"26",opera_mobile:"26",safari:"9.0",samsung:"3.4"},"es.array.concat":{chrome:"51",edge:"15",electron:"1.2",firefox:"48",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.array.copy-within":{chrome:"45",edge:"12",electron:"0.31",firefox:"48",ios:"9.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"9.0",samsung:"5.0"},"es.array.every":{chrome:"48",edge:"15",electron:"0.37",firefox:"50",ios:"9.0",node:"6.0",opera:"35",opera_mobile:"35",safari:"9.0",samsung:"5.0"},"es.array.fill":{chrome:"45",edge:"12",electron:"0.31",firefox:"48",ios:"9.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"9.0",samsung:"5.0"},"es.array.filter":{chrome:"51",edge:"15",electron:"1.2",firefox:"48",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.array.find":{chrome:"45",edge:"13",electron:"0.31",firefox:"48",ios:"9.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"9.0",samsung:"5.0"},"es.array.find-index":{chrome:"45",edge:"13",electron:"0.31",firefox:"48",ios:"9.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"9.0",samsung:"5.0"},"es.array.flat":{chrome:"69",edge:"74",electron:"4.0",firefox:"62",ios:"12.0",node:"11.0",opera:"56",opera_mobile:"48",safari:"12.0",samsung:"10.0"},"es.array.flat-map":{chrome:"69",edge:"74",electron:"4.0",firefox:"62",ios:"12.0",node:"11.0",opera:"56",opera_mobile:"48",safari:"12.0",samsung:"10.0"},"es.array.for-each":{chrome:"48",edge:"15",electron:"0.37",firefox:"50",ios:"9.0",node:"6.0",opera:"35",opera_mobile:"35",safari:"9.0",samsung:"5.0"},"es.array.from":{chrome:"51",edge:"15",electron:"1.2",firefox:"53",ios:"9.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"9.0",samsung:"5.0"},"es.array.includes":{chrome:"53",edge:"15",electron:"1.4",firefox:"48",ios:"10.0",node:"7.0",opera:"40",opera_mobile:"40",safari:"10.0",samsung:"6.0"},"es.array.index-of":{chrome:"51",edge:"15",electron:"1.2",firefox:"50",ios:"11.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"11.0",samsung:"5.0"},"es.array.is-array":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"4",ie:"9",ios:"3.2",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"4.0",samsung:"1.0"},"es.array.iterator":{chrome:"66",edge:"15",electron:"3.0",firefox:"60",ios:"10.0",node:"10.0",opera:"53",opera_mobile:"47",safari:"10.0",samsung:"9.0"},"es.array.join":{android:"4.4",chrome:"26",edge:"13",electron:"0.20",firefox:"4",ios:"8.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"7.1",samsung:"1.5"},"es.array.last-index-of":{chrome:"51",edge:"13",electron:"1.2",firefox:"50",ios:"11.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"11.0",samsung:"5.0"},"es.array.map":{chrome:"51",edge:"13",electron:"1.2",firefox:"50",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.array.of":{chrome:"45",edge:"13",electron:"0.31",firefox:"25",ios:"9.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"9.0",samsung:"5.0"},"es.array.reduce":{chrome:"48",edge:"15",electron:"0.37",firefox:"50",ios:"9.0",node:"6.0",opera:"35",opera_mobile:"35",safari:"9.0",samsung:"5.0"},"es.array.reduce-right":{chrome:"48",edge:"15",electron:"0.37",firefox:"50",ios:"9.0",node:"6.0",opera:"35",opera_mobile:"35",safari:"9.0",samsung:"5.0"},"es.array.reverse":{android:"3.0",chrome:"1",edge:"12",electron:"0.20",firefox:"1",ie:"5.5",ios:"12.2",node:"0.0.3",opera:"10.50",opera_mobile:"10.50",safari:"12.0.2",samsung:"1.0"},"es.array.slice":{chrome:"51",edge:"15",electron:"1.2",firefox:"48",ios:"11.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"11.0",samsung:"5.0"},"es.array.some":{chrome:"48",edge:"15",electron:"0.37",firefox:"50",ios:"9.0",node:"6.0",opera:"35",opera_mobile:"35",safari:"9.0",samsung:"5.0"},"es.array.sort":{chrome:"63",edge:"12",electron:"3.0",firefox:"4",ie:"9",ios:"12.0",node:"10.0",opera:"50",opera_mobile:"46",safari:"12.0",samsung:"8.0"},"es.array.species":{chrome:"51",edge:"13",electron:"1.2",firefox:"48",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.array.splice":{chrome:"51",edge:"15",electron:"1.2",firefox:"49",ios:"11.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"11.0",samsung:"5.0"},"es.array.unscopables.flat":{chrome:"73",edge:"74",electron:"5.0",firefox:"67",ios:"13.0",node:"12.0",opera:"60",opera_mobile:"52",safari:"13",samsung:"11.0"},"es.array.unscopables.flat-map":{chrome:"73",edge:"74",electron:"5.0",firefox:"67",ios:"13.0",node:"12.0",opera:"60",opera_mobile:"52",safari:"13",samsung:"11.0"},"es.array-buffer.constructor":{android:"4.4",chrome:"26",edge:"14",electron:"0.20",firefox:"44",ios:"12.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"12.0",samsung:"1.5"},"es.array-buffer.is-view":{android:"4.4.3",chrome:"32",edge:"12",electron:"0.20",firefox:"29",ie:"11",ios:"8.0",node:"0.11.9",opera:"19",opera_mobile:"19",safari:"7.1",samsung:"2.0"},"es.array-buffer.slice":{android:"4.4.3",chrome:"31",edge:"12",electron:"0.20",firefox:"46",ie:"11",ios:"12.2",node:"0.11.8",opera:"18",opera_mobile:"18",safari:"12.1",samsung:"2.0"},"es.data-view":{android:"4.4",chrome:"26",edge:"12",electron:"0.20",firefox:"15",ie:"10",ios:"8.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"7.1",samsung:"1.5"},"es.date.now":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ie:"9",ios:"3.2",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"4.0",samsung:"1.0"},"es.date.to-iso-string":{android:"4.4",chrome:"26",edge:"12",electron:"0.20",firefox:"7",ie:"9",ios:"8.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"7.1",samsung:"1.5"},"es.date.to-json":{android:"4.4",chrome:"26",edge:"12",electron:"0.20",firefox:"4",ie:"9",ios:"10.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"10.0",samsung:"1.5"},"es.date.to-primitive":{chrome:"47",edge:"15",electron:"0.36",firefox:"44",ios:"10.0",node:"6.0",opera:"34",opera_mobile:"34",safari:"10.0",samsung:"5.0"},"es.date.to-string":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ie:"9",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.function.bind":{android:"3.0",chrome:"7",edge:"12",electron:"0.20",firefox:"4",ie:"9",ios:"5.1",node:"0.1.101",opera:"12",opera_mobile:"12",phantom:"2.0",safari:"5.1",samsung:"1.0"},"es.function.has-instance":{chrome:"51",edge:"15",electron:"1.2",firefox:"50",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.function.name":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"3.2",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"4.0",samsung:"1.0"},"es.global-this":{chrome:"71",edge:"74",electron:"5.0",firefox:"65",ios:"12.2",node:"12.0",opera:"58",opera_mobile:"50",safari:"12.1",samsung:"10.0"},"es.json.stringify":{chrome:"72",edge:"74",electron:"5.0",firefox:"64",ios:"12.2",node:"12.0",opera:"59",opera_mobile:"51",safari:"12.1",samsung:"11.0"},"es.json.to-string-tag":{chrome:"50",edge:"15",electron:"1.1",firefox:"51",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.map":{chrome:"51",edge:"15",electron:"1.2",firefox:"53",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.math.acosh":{chrome:"54",edge:"13",electron:"1.4",firefox:"25",ios:"8.0",node:"7.0",opera:"41",opera_mobile:"41",safari:"7.1",samsung:"6.0"},"es.math.asinh":{chrome:"38",edge:"13",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.atanh":{chrome:"38",edge:"13",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.cbrt":{chrome:"38",edge:"12",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.clz32":{chrome:"38",edge:"12",electron:"0.20",firefox:"31",ios:"9.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"9.0",samsung:"3.0"},"es.math.cosh":{chrome:"39",edge:"13",electron:"0.20",firefox:"25",ios:"8.0",node:"1.0",opera:"26",opera_mobile:"26",safari:"7.1",samsung:"3.4"},"es.math.expm1":{chrome:"39",edge:"13",electron:"0.20",firefox:"46",ios:"8.0",node:"1.0",opera:"26",opera_mobile:"26",safari:"7.1",samsung:"3.4"},"es.math.fround":{chrome:"38",edge:"12",electron:"0.20",firefox:"26",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.hypot":{chrome:"78",edge:"12",electron:"7.0",firefox:"27",ios:"8.0",node:"13.0",opera:"65",safari:"7.1"},"es.math.imul":{android:"4.4",chrome:"28",edge:"13",electron:"0.20",firefox:"20",ios:"9.0",node:"0.11.1",opera:"16",opera_mobile:"16",safari:"9.0",samsung:"1.5"},"es.math.log10":{chrome:"38",edge:"12",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.log1p":{chrome:"38",edge:"12",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.log2":{chrome:"38",edge:"12",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.sign":{chrome:"38",edge:"12",electron:"0.20",firefox:"25",ios:"9.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"9.0",samsung:"3.0"},"es.math.sinh":{chrome:"39",edge:"13",electron:"0.20",firefox:"25",ios:"8.0",node:"1.0",opera:"26",opera_mobile:"26",safari:"7.1",samsung:"3.4"},"es.math.tanh":{chrome:"38",edge:"12",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.math.to-string-tag":{chrome:"50",edge:"15",electron:"1.1",firefox:"51",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.math.trunc":{chrome:"38",edge:"12",electron:"0.20",firefox:"25",ios:"8.0",node:"0.11.15",opera:"25",opera_mobile:"25",safari:"7.1",samsung:"3.0"},"es.number.constructor":{chrome:"41",edge:"13",electron:"0.21",firefox:"46",ios:"9.0",node:"1.0",opera:"28",opera_mobile:"28",safari:"9.0",samsung:"3.4"},"es.number.epsilon":{chrome:"34",edge:"12",electron:"0.20",firefox:"25",ios:"9.0",node:"0.11.13",opera:"21",opera_mobile:"21",safari:"9.0",samsung:"2.0"},"es.number.is-finite":{android:"4.1",chrome:"19",edge:"12",electron:"0.20",firefox:"16",ios:"9.0",node:"0.7.3",opera:"15",opera_mobile:"15",safari:"9.0",samsung:"1.5"},"es.number.is-integer":{chrome:"34",edge:"12",electron:"0.20",firefox:"16",ios:"9.0",node:"0.11.13",opera:"21",opera_mobile:"21",safari:"9.0",samsung:"2.0"},"es.number.is-nan":{android:"4.1",chrome:"19",edge:"12",electron:"0.20",firefox:"15",ios:"9.0",node:"0.7.3",opera:"15",opera_mobile:"15",safari:"9.0",samsung:"1.5"},"es.number.is-safe-integer":{chrome:"34",edge:"12",electron:"0.20",firefox:"32",ios:"9.0",node:"0.11.13",opera:"21",opera_mobile:"21",safari:"9.0",samsung:"2.0"},"es.number.max-safe-integer":{chrome:"34",edge:"12",electron:"0.20",firefox:"31",ios:"9.0",node:"0.11.13",opera:"21",opera_mobile:"21",safari:"9.0",samsung:"2.0"},"es.number.min-safe-integer":{chrome:"34",edge:"12",electron:"0.20",firefox:"31",ios:"9.0",node:"0.11.13",opera:"21",opera_mobile:"21",safari:"9.0",samsung:"2.0"},"es.number.parse-float":{chrome:"35",edge:"13",electron:"0.20",firefox:"39",ios:"11.0",node:"0.11.13",opera:"22",opera_mobile:"22",safari:"11.0",samsung:"3.0"},"es.number.parse-int":{chrome:"35",edge:"13",electron:"0.20",firefox:"39",ios:"9.0",node:"0.11.13",opera:"22",opera_mobile:"22",safari:"9.0",samsung:"3.0"},"es.number.to-fixed":{android:"4.4",chrome:"26",edge:"74",electron:"0.20",firefox:"4",ios:"8.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"7.1",samsung:"1.5"},"es.number.to-precision":{android:"4.4",chrome:"26",edge:"12",electron:"0.20",firefox:"4",ie:"8",ios:"8.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"7.1",samsung:"1.5"},"es.object.assign":{chrome:"49",edge:"74",electron:"0.37",firefox:"36",ios:"9.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"9.0",samsung:"5.0"},"es.object.create":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"4",ie:"9",ios:"3.2",node:"0.1.27",opera:"12",opera_mobile:"12",phantom:"1.9",safari:"4.0",samsung:"1.0"},"es.object.define-getter":{chrome:"62",edge:"16",electron:"3.0",firefox:"48",ios:"8.0",node:"8.10",opera:"49",opera_mobile:"46",safari:"7.1",samsung:"8.0"},"es.object.define-properties":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"4",ie:"9",ios:"5.1",node:"0.1.27",opera:"12",opera_mobile:"12",phantom:"2.0",safari:"5.1",samsung:"1.0"},"es.object.define-property":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"4",ie:"9",ios:"5.1",node:"0.1.27",opera:"12",opera_mobile:"12",phantom:"2.0",safari:"5.1",samsung:"1.0"},"es.object.define-setter":{chrome:"62",edge:"16",electron:"3.0",firefox:"48",ios:"8.0",node:"8.10",opera:"49",opera_mobile:"46",safari:"7.1",samsung:"8.0"},"es.object.entries":{chrome:"54",edge:"14",electron:"1.4",firefox:"47",ios:"10.3",node:"7.0",opera:"41",opera_mobile:"41",safari:"10.1",samsung:"6.0"},"es.object.freeze":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.from-entries":{chrome:"73",edge:"74",electron:"5.0",firefox:"63",ios:"12.2",node:"12.0",opera:"60",opera_mobile:"52",safari:"12.1",samsung:"11.0"},"es.object.get-own-property-descriptor":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.get-own-property-descriptors":{chrome:"54",edge:"15",electron:"1.4",firefox:"50",ios:"10.0",node:"7.0",opera:"41",opera_mobile:"41",safari:"10.0",samsung:"6.0"},"es.object.get-own-property-names":{chrome:"40",edge:"13",electron:"0.21",firefox:"34",ios:"9.0",node:"1.0",opera:"27",opera_mobile:"27",safari:"9.0",samsung:"3.4"},"es.object.get-prototype-of":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.is":{android:"4.1",chrome:"19",edge:"12",electron:"0.20",firefox:"22",ios:"9.0",node:"0.7.3",opera:"15",opera_mobile:"15",safari:"9.0",samsung:"1.5"},"es.object.is-extensible":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.is-frozen":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.is-sealed":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.keys":{chrome:"40",edge:"13",electron:"0.21",firefox:"35",ios:"9.0",node:"1.0",opera:"27",opera_mobile:"27",safari:"9.0",samsung:"3.4"},"es.object.lookup-getter":{chrome:"62",edge:"16",electron:"3.0",firefox:"48",ios:"8.0",node:"8.10",opera:"49",opera_mobile:"46",safari:"7.1",samsung:"8.0"},"es.object.lookup-setter":{chrome:"62",edge:"16",electron:"3.0",firefox:"48",ios:"8.0",node:"8.10",opera:"49",opera_mobile:"46",safari:"7.1",samsung:"8.0"},"es.object.prevent-extensions":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.seal":{chrome:"44",edge:"13",electron:"0.30",firefox:"35",ios:"9.0",node:"3.0",opera:"31",opera_mobile:"31",safari:"9.0",samsung:"4.0"},"es.object.set-prototype-of":{chrome:"34",edge:"12",electron:"0.20",firefox:"31",ie:"11",ios:"9.0",node:"0.11.13",opera:"21",opera_mobile:"21",safari:"9.0",samsung:"2.0"},"es.object.to-string":{chrome:"49",edge:"15",electron:"0.37",firefox:"51",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.object.values":{chrome:"54",edge:"14",electron:"1.4",firefox:"47",ios:"10.3",node:"7.0",opera:"41",opera_mobile:"41",safari:"10.1",samsung:"6.0"},"es.parse-float":{chrome:"35",edge:"12",electron:"0.20",firefox:"8",ie:"8",ios:"8.0",node:"0.11.13",opera:"22",opera_mobile:"22",safari:"7.1",samsung:"3.0"},"es.parse-int":{chrome:"35",edge:"12",electron:"0.20",firefox:"21",ie:"9",ios:"8.0",node:"0.11.13",opera:"22",opera_mobile:"22",safari:"7.1",samsung:"3.0"},"es.promise":{chrome:"67",edge:"74",electron:"4.0",firefox:"69",ios:"11.0",node:"10.4",opera:"54",opera_mobile:"48",safari:"11.0",samsung:"9.0"},"es.promise.all-settled":{chrome:"76",edge:"76",electron:"6.0",firefox:"71",ios:"13.0",node:"12.9",opera:"63",opera_mobile:"54",safari:"13"},"es.promise.finally":{chrome:"67",edge:"74",electron:"4.0",firefox:"69",ios:"13.2.3",node:"10.4",opera:"54",opera_mobile:"48",safari:"13.0.3",samsung:"9.0"},"es.reflect.apply":{chrome:"49",edge:"15",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.construct":{chrome:"49",edge:"15",electron:"0.37",firefox:"44",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.define-property":{chrome:"49",edge:"13",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.delete-property":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.get":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.get-own-property-descriptor":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.get-prototype-of":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.has":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.is-extensible":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.own-keys":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.prevent-extensions":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.set":{chrome:"49",edge:"74",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.reflect.set-prototype-of":{chrome:"49",edge:"12",electron:"0.37",firefox:"42",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.regexp.constructor":{chrome:"51",edge:"74",electron:"1.2",firefox:"49",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.regexp.exec":{android:"4.4",chrome:"26",edge:"13",electron:"0.20",firefox:"44",ios:"10.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"10.0",samsung:"1.5"},"es.regexp.flags":{chrome:"49",edge:"74",electron:"0.37",firefox:"37",ios:"9.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"9.0",samsung:"5.0"},"es.regexp.sticky":{chrome:"49",edge:"13",electron:"0.37",firefox:"3",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.regexp.test":{chrome:"51",edge:"74",electron:"1.2",firefox:"46",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.regexp.to-string":{chrome:"50",edge:"74",electron:"1.1",firefox:"46",ios:"10.0",node:"6.0",opera:"37",opera_mobile:"37",safari:"10.0",samsung:"5.0"},"es.set":{chrome:"51",edge:"15",electron:"1.2",firefox:"53",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.string.code-point-at":{chrome:"41",edge:"13",electron:"0.21",firefox:"29",ios:"9.0",node:"1.0",opera:"28",opera_mobile:"28",safari:"9.0",samsung:"3.4"},"es.string.ends-with":{chrome:"51",edge:"74",electron:"1.2",firefox:"40",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.string.from-code-point":{chrome:"41",edge:"13",electron:"0.21",firefox:"29",ios:"9.0",node:"1.0",opera:"28",opera_mobile:"28",safari:"9.0",samsung:"3.4"},"es.string.includes":{chrome:"51",edge:"74",electron:"1.2",firefox:"40",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.string.iterator":{chrome:"39",edge:"13",electron:"0.20",firefox:"36",ios:"9.0",node:"1.0",opera:"26",opera_mobile:"26",safari:"9.0",samsung:"3.4"},"es.string.match":{chrome:"51",edge:"74",electron:"1.2",firefox:"49",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.string.match-all":{chrome:"80",edge:"80",electron:"8.0",firefox:"73",opera:"67",safari:"13.1"},"es.string.pad-end":{chrome:"57",edge:"15",electron:"1.7",firefox:"48",ios:"11.0",node:"8.0",opera:"44",opera_mobile:"43",safari:"11.0",samsung:"7.0"},"es.string.pad-start":{chrome:"57",edge:"15",electron:"1.7",firefox:"48",ios:"11.0",node:"8.0",opera:"44",opera_mobile:"43",safari:"11.0",samsung:"7.0"},"es.string.raw":{chrome:"41",edge:"13",electron:"0.21",firefox:"34",ios:"9.0",node:"1.0",opera:"28",opera_mobile:"28",safari:"9.0",samsung:"3.4"},"es.string.repeat":{chrome:"41",edge:"13",electron:"0.21",firefox:"24",ios:"9.0",node:"1.0",opera:"28",opera_mobile:"28",safari:"9.0",samsung:"3.4"},"es.string.replace":{chrome:"64",edge:"74",electron:"3.0",node:"10.0",opera:"51",opera_mobile:"47",samsung:"9.0"},"es.string.search":{chrome:"51",edge:"74",electron:"1.2",firefox:"49",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.string.split":{chrome:"54",edge:"74",electron:"1.4",firefox:"49",ios:"10.0",node:"7.0",opera:"41",opera_mobile:"41",safari:"10.0",samsung:"6.0"},"es.string.starts-with":{chrome:"51",edge:"74",electron:"1.2",firefox:"40",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.string.trim":{chrome:"59",edge:"15",electron:"1.8",firefox:"52",ios:"12.2",node:"8.3",opera:"46",opera_mobile:"43",safari:"12.1",samsung:"7.0"},"es.string.trim-end":{chrome:"66",edge:"74",electron:"3.0",firefox:"61",ios:"12.2",node:"10.0",opera:"53",opera_mobile:"47",safari:"12.1",samsung:"9.0"},"es.string.trim-start":{chrome:"66",edge:"74",electron:"3.0",firefox:"61",ios:"12.0",node:"10.0",opera:"53",opera_mobile:"47",safari:"12.0",samsung:"9.0"},"es.string.anchor":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"17",ios:"6.0",node:"0.1.27",opera:"15",opera_mobile:"15",phantom:"2.0",safari:"6.0",samsung:"1.0"},"es.string.big":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.blink":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.bold":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.fixed":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.fontcolor":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"17",ios:"6.0",node:"0.1.27",opera:"15",opera_mobile:"15",phantom:"2.0",safari:"6.0",samsung:"1.0"},"es.string.fontsize":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"17",ios:"6.0",node:"0.1.27",opera:"15",opera_mobile:"15",phantom:"2.0",safari:"6.0",samsung:"1.0"},"es.string.italics":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.link":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"17",ios:"6.0",node:"0.1.27",opera:"15",opera_mobile:"15",phantom:"2.0",safari:"6.0",samsung:"1.0"},"es.string.small":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.strike":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.sub":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.string.sup":{android:"3.0",chrome:"5",edge:"12",electron:"0.20",firefox:"2",ios:"2.0",node:"0.1.27",opera:"10.50",opera_mobile:"10.50",phantom:"1.9",safari:"3.1",samsung:"1.0"},"es.typed-array.float32-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.float64-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.int8-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.int16-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.int32-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.uint8-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.uint8-clamped-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.uint16-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.uint32-array":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.copy-within":{chrome:"45",edge:"13",electron:"0.31",firefox:"34",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.every":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.fill":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.filter":{chrome:"45",edge:"13",electron:"0.31",firefox:"38",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.find":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.find-index":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.for-each":{chrome:"45",edge:"13",electron:"0.31",firefox:"38",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.from":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.includes":{chrome:"49",edge:"14",electron:"0.37",firefox:"43",ios:"10.0",node:"6.0",opera:"36",opera_mobile:"36",safari:"10.0",samsung:"5.0"},"es.typed-array.index-of":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.iterator":{chrome:"47",edge:"13",electron:"0.36",firefox:"37",ios:"10.0",node:"6.0",opera:"34",opera_mobile:"34",safari:"10.0",samsung:"5.0"},"es.typed-array.join":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.last-index-of":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.map":{chrome:"45",edge:"13",electron:"0.31",firefox:"38",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.of":{chrome:"54",edge:"15",electron:"1.4",firefox:"55",node:"7.0",opera:"41",opera_mobile:"41",samsung:"6.0"},"es.typed-array.reduce":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.reduce-right":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.reverse":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.set":{android:"4.4",chrome:"26",edge:"13",electron:"0.20",firefox:"15",ios:"8.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"7.1",samsung:"1.5"},"es.typed-array.slice":{chrome:"45",edge:"13",electron:"0.31",firefox:"38",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.some":{chrome:"45",edge:"13",electron:"0.31",firefox:"37",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.sort":{chrome:"45",edge:"13",electron:"0.31",firefox:"46",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.subarray":{android:"4.4",chrome:"26",edge:"13",electron:"0.20",firefox:"15",ios:"8.0",node:"0.11.0",opera:"16",opera_mobile:"16",safari:"7.1",samsung:"1.5"},"es.typed-array.to-locale-string":{chrome:"45",edge:"74",electron:"0.31",firefox:"51",ios:"10.0",node:"4.0",opera:"32",opera_mobile:"32",safari:"10.0",samsung:"5.0"},"es.typed-array.to-string":{chrome:"51",edge:"13",electron:"1.2",firefox:"51",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.weak-map":{chrome:"51",edge:"15",electron:"1.2",firefox:"53",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"es.weak-set":{chrome:"51",edge:"15",electron:"1.2",firefox:"53",ios:"10.0",node:"6.5",opera:"38",opera_mobile:"38",safari:"10.0",samsung:"5.0"},"esnext.aggregate-error":{},"esnext.array.is-template-object":{},"esnext.array.last-index":{},"esnext.array.last-item":{},"esnext.async-iterator.constructor":{},"esnext.async-iterator.as-indexed-pairs":{},"esnext.async-iterator.drop":{},"esnext.async-iterator.every":{},"esnext.async-iterator.filter":{},"esnext.async-iterator.find":{},"esnext.async-iterator.flat-map":{},"esnext.async-iterator.for-each":{},"esnext.async-iterator.from":{},"esnext.async-iterator.map":{},"esnext.async-iterator.reduce":{},"esnext.async-iterator.some":{},"esnext.async-iterator.take":{},"esnext.async-iterator.to-array":{},"esnext.composite-key":{},"esnext.composite-symbol":{},"esnext.global-this":{chrome:"71",edge:"74",electron:"5.0",firefox:"65",ios:"12.2",node:"12.0",opera:"58",opera_mobile:"50",safari:"12.1",samsung:"10.0"},"esnext.iterator.constructor":{},"esnext.iterator.as-indexed-pairs":{},"esnext.iterator.drop":{},"esnext.iterator.every":{},"esnext.iterator.filter":{},"esnext.iterator.find":{},"esnext.iterator.flat-map":{},"esnext.iterator.for-each":{},"esnext.iterator.from":{},"esnext.iterator.map":{},"esnext.iterator.reduce":{},"esnext.iterator.some":{},"esnext.iterator.take":{},"esnext.iterator.to-array":{},"esnext.map.delete-all":{},"esnext.map.every":{},"esnext.map.filter":{},"esnext.map.find":{},"esnext.map.find-key":{},"esnext.map.from":{},"esnext.map.group-by":{},"esnext.map.includes":{},"esnext.map.key-by":{},"esnext.map.key-of":{},"esnext.map.map-keys":{},"esnext.map.map-values":{},"esnext.map.merge":{},"esnext.map.of":{},"esnext.map.reduce":{},"esnext.map.some":{},"esnext.map.update":{},"esnext.map.update-or-insert":{},"esnext.map.upsert":{},"esnext.math.clamp":{},"esnext.math.deg-per-rad":{},"esnext.math.degrees":{},"esnext.math.fscale":{},"esnext.math.iaddh":{},"esnext.math.imulh":{},"esnext.math.isubh":{},"esnext.math.rad-per-deg":{},"esnext.math.radians":{},"esnext.math.scale":{},"esnext.math.seeded-prng":{},"esnext.math.signbit":{},"esnext.math.umulh":{},"esnext.number.from-string":{},"esnext.object.iterate-entries":{},"esnext.object.iterate-keys":{},"esnext.object.iterate-values":{},"esnext.observable":{},"esnext.promise.all-settled":{chrome:"76",edge:"76",electron:"6.0",firefox:"71",ios:"13.0",node:"12.9",opera:"63",opera_mobile:"54",safari:"13"},"esnext.promise.any":{},"esnext.promise.try":{},"esnext.reflect.define-metadata":{},"esnext.reflect.delete-metadata":{},"esnext.reflect.get-metadata":{},"esnext.reflect.get-metadata-keys":{},"esnext.reflect.get-own-metadata":{},"esnext.reflect.get-own-metadata-keys":{},"esnext.reflect.has-metadata":{},"esnext.reflect.has-own-metadata":{},"esnext.reflect.metadata":{},"esnext.set.add-all":{},"esnext.set.delete-all":{},"esnext.set.difference":{},"esnext.set.every":{},"esnext.set.filter":{},"esnext.set.find":{},"esnext.set.from":{},"esnext.set.intersection":{},"esnext.set.is-disjoint-from":{},"esnext.set.is-subset-of":{},"esnext.set.is-superset-of":{},"esnext.set.join":{},"esnext.set.map":{},"esnext.set.of":{},"esnext.set.reduce":{},"esnext.set.some":{},"esnext.set.symmetric-difference":{},"esnext.set.union":{},"esnext.string.at":{},"esnext.string.code-points":{},"esnext.string.match-all":{chrome:"80",edge:"80",electron:"8.0",firefox:"73",opera:"67",safari:"13.1"},"esnext.string.replace-all":{},"esnext.symbol.async-dispose":{},"esnext.symbol.dispose":{},"esnext.symbol.observable":{},"esnext.symbol.pattern-match":{},"esnext.symbol.replace-all":{},"esnext.weak-map.delete-all":{},"esnext.weak-map.from":{},"esnext.weak-map.of":{},"esnext.weak-map.upsert":{},"esnext.weak-set.add-all":{},"esnext.weak-set.delete-all":{},"esnext.weak-set.from":{},"esnext.weak-set.of":{},"web.dom-collections.for-each":{chrome:"58",edge:"16",electron:"1.7",firefox:"50",ios:"10.0",node:"0.0.1",opera:"45",opera_mobile:"43",safari:"10.0",samsung:"7.0"},"web.dom-collections.iterator":{chrome:"66",edge:"74",electron:"3.0",firefox:"60",node:"0.0.1",opera:"53",opera_mobile:"47",safari:"13.1",samsung:"9.0"},"web.immediate":{ie:"10",node:"0.9.1"},"web.queue-microtask":{chrome:"71",edge:"74",electron:"5.0",firefox:"69",ios:"12.2",node:"12.0",opera:"58",opera_mobile:"50",safari:"12.1",samsung:"10.0"},"web.timers":{android:"1.5",chrome:"1",edge:"12",electron:"0.20",firefox:"1",ie:"10",ios:"1.0",node:"0.0.1",opera:"7",opera_mobile:"7",phantom:"1.9",safari:"1.0",samsung:"1.0"},"web.url":{chrome:"67",edge:"74",electron:"4.0",firefox:"57",node:"10.0",opera:"54",opera_mobile:"48",samsung:"9.0"},"web.url.to-json":{chrome:"71",edge:"74",electron:"5.0",firefox:"57",node:"10.0",opera:"58",opera_mobile:"50",samsung:"10.0"},"web.url-search-params":{chrome:"67",edge:"74",electron:"4.0",firefox:"57",node:"10.0",opera:"54",opera_mobile:"48",samsung:"9.0"}},BU=Vt((function(e){var t=[],r=[],n=function(e,n){if(e===n)return 0;var a=e;e.length>n.length&&(e=n,n=a);for(var s=e.length,i=n.length;s>0&&e.charCodeAt(~-s)===n.charCodeAt(~-i);)s--,i--;for(var o,u,c,l,p=0;p<s&&e.charCodeAt(p)===n.charCodeAt(p);)p++;if(i-=p,0===(s-=p))return i;for(var d=0,f=0;d<s;)r[d]=e.charCodeAt(p+d),t[d]=++d;for(;f<i;)for(o=n.charCodeAt(p+f),c=f++,u=f,d=0;d<s;d++)l=o===r[d]?c:c+1,c=t[d],u=t[d]=c>u?l>u?u+1:l:l>c?c+1:l;return u};e.exports=n,e.exports.default=n}));function OU(e,t){var r=Number.POSITIVE_INFINITY,n=void 0,a=t,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o,c=BU(e,u);c<r&&(r=c,n=u)}return n}for(var NU=function(e,t,r,n,a,s,i,o){if(void 0===t)throw new Error("invariant requires an error message argument");if(!e){var u;if(void 0===t)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[r,n,a,s,i,o],l=0;(u=new Error(t.replace(/%s/g,(function(){return c[l++]})))).name="Invariant Violation"}throw u.framesToPop=1,u}},RU=Wt(Object.freeze({__proto__:null,default:{"es6.array.copy-within":{chrome:"45",edge:"12",firefox:"32",safari:"9",node:"4",ios:"9",samsung:"5",opera:"32",electron:"0.35"},"es6.array.every":{chrome:"5",opera:"10.10",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.fill":{chrome:"45",edge:"12",firefox:"31",safari:"7.1",node:"4",ios:"8",samsung:"5",opera:"32",electron:"0.35"},"es6.array.filter":{chrome:"5",opera:"10.10",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.find":{chrome:"45",edge:"12",firefox:"25",safari:"7.1",node:"4",ios:"8",samsung:"5",opera:"32",electron:"0.35"},"es6.array.find-index":{chrome:"45",edge:"12",firefox:"25",safari:"7.1",node:"4",ios:"8",samsung:"5",opera:"32",electron:"0.35"},"es7.array.flat-map":{chrome:"69",edge:"79",firefox:"62",safari:"12",node:"11",ios:"12",samsung:"10",opera:"56",electron:"4"},"es6.array.for-each":{chrome:"5",opera:"10.10",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.from":{chrome:"51",edge:"15",firefox:"36",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es7.array.includes":{chrome:"47",edge:"14",firefox:"43",safari:"10",node:"6",ios:"10",samsung:"5",opera:"34",electron:"0.36"},"es6.array.index-of":{chrome:"5",opera:"10.10",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.is-array":{chrome:"5",opera:"10.50",edge:"12",firefox:"4",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.iterator":{chrome:"38",edge:"12",firefox:"28",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.array.last-index-of":{chrome:"5",opera:"10.10",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.map":{chrome:"5",opera:"10.10",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.of":{chrome:"45",edge:"12",firefox:"25",safari:"9",node:"4",ios:"9",samsung:"5",opera:"32",electron:"0.35"},"es6.array.reduce":{chrome:"5",opera:"10.50",edge:"12",firefox:"3",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.reduce-right":{chrome:"5",opera:"10.50",edge:"12",firefox:"3",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.some":{chrome:"5",opera:"10.10",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.array.sort":{chrome:"63",opera:"50",edge:"12",firefox:"5",safari:"12",node:"10",ie:"9",ios:"12",samsung:"8",electron:"3.1"},"es6.array.species":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.date.now":{chrome:"5",opera:"10.50",edge:"12",firefox:"2",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.date.to-iso-string":{chrome:"5",opera:"10.50",edge:"12",firefox:"3.5",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.date.to-json":{chrome:"5",opera:"12.10",edge:"12",firefox:"4",safari:"10",node:"0.10",ie:"9",android:"4",ios:"10",samsung:"1",electron:"1.1"},"es6.date.to-primitive":{chrome:"47",edge:"15",firefox:"44",safari:"10",node:"6",ios:"10",samsung:"5",opera:"34",electron:"0.36"},"es6.date.to-string":{chrome:"5",opera:"10.50",edge:"12",firefox:"2",safari:"3.1",node:"0.10",ie:"10",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.function.bind":{chrome:"7",opera:"12",edge:"12",firefox:"4",safari:"5.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"5"},"es6.function.has-instance":{chrome:"51",edge:"15",firefox:"50",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.function.name":{chrome:"5",opera:"10.50",edge:"14",firefox:"2",safari:"4",node:"0.10",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.map":{chrome:"51",edge:"15",firefox:"53",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.math.acosh":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.asinh":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.atanh":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.cbrt":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.clz32":{chrome:"38",edge:"12",firefox:"31",safari:"9",node:"0.12",ios:"9",samsung:"3",opera:"25",electron:"0.2"},"es6.math.cosh":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.expm1":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.fround":{chrome:"38",edge:"12",firefox:"26",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.hypot":{chrome:"38",edge:"12",firefox:"27",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.imul":{chrome:"30",edge:"12",firefox:"23",safari:"7",node:"0.12",android:"4.4",ios:"7",samsung:"2",opera:"17",electron:"0.2"},"es6.math.log1p":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.log10":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.log2":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.sign":{chrome:"38",edge:"12",firefox:"25",safari:"9",node:"0.12",ios:"9",samsung:"3",opera:"25",electron:"0.2"},"es6.math.sinh":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.tanh":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.math.trunc":{chrome:"38",edge:"12",firefox:"25",safari:"7.1",node:"0.12",ios:"8",samsung:"3",opera:"25",electron:"0.2"},"es6.number.constructor":{chrome:"41",edge:"12",firefox:"36",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.number.epsilon":{chrome:"34",edge:"12",firefox:"25",safari:"9",node:"0.12",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es6.number.is-finite":{chrome:"19",edge:"12",firefox:"16",safari:"9",node:"0.12",android:"4.1",ios:"9",samsung:"1.5",opera:"15",electron:"0.2"},"es6.number.is-integer":{chrome:"34",edge:"12",firefox:"16",safari:"9",node:"0.12",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es6.number.is-nan":{chrome:"19",edge:"12",firefox:"15",safari:"9",node:"0.12",android:"4.1",ios:"9",samsung:"1.5",opera:"15",electron:"0.2"},"es6.number.is-safe-integer":{chrome:"34",edge:"12",firefox:"32",safari:"9",node:"0.12",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es6.number.max-safe-integer":{chrome:"34",edge:"12",firefox:"31",safari:"9",node:"0.12",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es6.number.min-safe-integer":{chrome:"34",edge:"12",firefox:"31",safari:"9",node:"0.12",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es6.number.parse-float":{chrome:"34",edge:"12",firefox:"25",safari:"9",node:"0.12",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es6.number.parse-int":{chrome:"34",edge:"12",firefox:"25",safari:"9",node:"0.12",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es6.object.assign":{chrome:"49",edge:"13",firefox:"36",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.object.create":{chrome:"5",opera:"12",edge:"12",firefox:"4",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es7.object.define-getter":{chrome:"62",edge:"16",firefox:"48",safari:"9",node:"8.10",ios:"9",samsung:"8",opera:"49",electron:"3.1"},"es7.object.define-setter":{chrome:"62",edge:"16",firefox:"48",safari:"9",node:"8.10",ios:"9",samsung:"8",opera:"49",electron:"3.1"},"es6.object.define-property":{chrome:"5",opera:"12",edge:"12",firefox:"4",safari:"5.1",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.object.define-properties":{chrome:"5",opera:"12",edge:"12",firefox:"4",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es7.object.entries":{chrome:"54",edge:"14",firefox:"47",safari:"10.1",node:"7",ios:"10.3",samsung:"6",opera:"41",electron:"1.5"},"es6.object.freeze":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es6.object.get-own-property-descriptor":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es7.object.get-own-property-descriptors":{chrome:"54",edge:"15",firefox:"50",safari:"10.1",node:"7",ios:"10.3",samsung:"6",opera:"41",electron:"1.5"},"es6.object.get-own-property-names":{chrome:"40",edge:"12",firefox:"33",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"27",electron:"0.21"},"es6.object.get-prototype-of":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es7.object.lookup-getter":{chrome:"62",edge:"79",firefox:"36",safari:"9",node:"8.10",ios:"9",samsung:"8",opera:"49",electron:"3.1"},"es7.object.lookup-setter":{chrome:"62",edge:"79",firefox:"36",safari:"9",node:"8.10",ios:"9",samsung:"8",opera:"49",electron:"3.1"},"es6.object.prevent-extensions":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es6.object.to-string":{chrome:"57",edge:"15",firefox:"51",safari:"10",node:"8",ios:"10",samsung:"7",opera:"44",electron:"1.7"},"es6.object.is":{chrome:"19",edge:"12",firefox:"22",safari:"9",node:"0.12",android:"4.1",ios:"9",samsung:"1.5",opera:"15",electron:"0.2"},"es6.object.is-frozen":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es6.object.is-sealed":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es6.object.is-extensible":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es6.object.keys":{chrome:"40",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"27",electron:"0.21"},"es6.object.seal":{chrome:"44",edge:"12",firefox:"35",safari:"9",node:"4",ios:"9",samsung:"4",opera:"31",electron:"0.31"},"es6.object.set-prototype-of":{chrome:"34",edge:"12",firefox:"31",safari:"9",node:"0.12",ie:"11",ios:"9",samsung:"2",opera:"21",electron:"0.2"},"es7.object.values":{chrome:"54",edge:"14",firefox:"47",safari:"10.1",node:"7",ios:"10.3",samsung:"6",opera:"41",electron:"1.5"},"es6.promise":{chrome:"51",edge:"14",firefox:"45",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es7.promise.finally":{chrome:"63",edge:"18",firefox:"58",safari:"11.1",node:"10",ios:"11.3",samsung:"8",opera:"50",electron:"3.1"},"es6.reflect.apply":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.construct":{chrome:"49",edge:"13",firefox:"49",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.define-property":{chrome:"49",edge:"13",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.delete-property":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.get":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.get-own-property-descriptor":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.get-prototype-of":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.has":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.is-extensible":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.own-keys":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.prevent-extensions":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.set":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.reflect.set-prototype-of":{chrome:"49",edge:"12",firefox:"42",safari:"10",node:"6",ios:"10",samsung:"5",opera:"36",electron:"1"},"es6.regexp.constructor":{chrome:"50",edge:"79",firefox:"40",safari:"10",node:"6",ios:"10",samsung:"5",opera:"37",electron:"1.1"},"es6.regexp.flags":{chrome:"49",edge:"79",firefox:"37",safari:"9",node:"6",ios:"9",samsung:"5",opera:"36",electron:"1"},"es6.regexp.match":{chrome:"50",edge:"79",firefox:"49",safari:"10",node:"6",ios:"10",samsung:"5",opera:"37",electron:"1.1"},"es6.regexp.replace":{chrome:"50",edge:"79",firefox:"49",safari:"10",node:"6",ios:"10",samsung:"5",opera:"37",electron:"1.1"},"es6.regexp.split":{chrome:"50",edge:"79",firefox:"49",safari:"10",node:"6",ios:"10",samsung:"5",opera:"37",electron:"1.1"},"es6.regexp.search":{chrome:"50",edge:"79",firefox:"49",safari:"10",node:"6",ios:"10",samsung:"5",opera:"37",electron:"1.1"},"es6.regexp.to-string":{chrome:"50",edge:"79",firefox:"39",safari:"10",node:"6",ios:"10",samsung:"5",opera:"37",electron:"1.1"},"es6.set":{chrome:"51",edge:"15",firefox:"53",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.symbol":{chrome:"51",edge:"79",firefox:"51",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es7.symbol.async-iterator":{chrome:"63",edge:"79",firefox:"57",safari:"12",node:"10",ios:"12",samsung:"8",opera:"50",electron:"3.1"},"es6.string.anchor":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.big":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.blink":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.bold":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.code-point-at":{chrome:"41",edge:"12",firefox:"29",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.string.ends-with":{chrome:"41",edge:"12",firefox:"29",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.string.fixed":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.fontcolor":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.fontsize":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.from-code-point":{chrome:"41",edge:"12",firefox:"29",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.string.includes":{chrome:"41",edge:"12",firefox:"40",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.string.italics":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.iterator":{chrome:"38",edge:"12",firefox:"36",safari:"9",node:"0.12",ios:"9",samsung:"3",opera:"25",electron:"0.2"},"es6.string.link":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es7.string.pad-start":{chrome:"57",edge:"15",firefox:"48",safari:"10",node:"8",ios:"10",samsung:"7",opera:"44",electron:"1.7"},"es7.string.pad-end":{chrome:"57",edge:"15",firefox:"48",safari:"10",node:"8",ios:"10",samsung:"7",opera:"44",electron:"1.7"},"es6.string.raw":{chrome:"41",edge:"12",firefox:"34",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.string.repeat":{chrome:"41",edge:"12",firefox:"24",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.string.small":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.starts-with":{chrome:"41",edge:"12",firefox:"29",safari:"9",node:"4",ios:"9",samsung:"3.4",opera:"28",electron:"0.24"},"es6.string.strike":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.sub":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.sup":{chrome:"5",edge:"12",firefox:"17",safari:"6",node:"0.10",android:"4",ios:"7",phantom:"2",samsung:"1",opera:"15",electron:"1.1"},"es6.string.trim":{chrome:"5",opera:"10.50",edge:"12",firefox:"3.5",safari:"4",node:"0.10",ie:"9",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es7.string.trim-left":{chrome:"66",edge:"79",firefox:"61",safari:"12",node:"10",ios:"12",samsung:"9",opera:"53",electron:"3.1"},"es7.string.trim-right":{chrome:"66",edge:"79",firefox:"61",safari:"12",node:"10",ios:"12",samsung:"9",opera:"53",electron:"3.1"},"es6.typed.array-buffer":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.data-view":{chrome:"5",opera:"12",edge:"12",firefox:"15",safari:"5.1",node:"0.10",ie:"10",android:"4",ios:"6",phantom:"2",samsung:"1",electron:"1.1"},"es6.typed.int8-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.uint8-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.uint8-clamped-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.int16-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.uint16-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.int32-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.uint32-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.float32-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.typed.float64-array":{chrome:"51",edge:"13",firefox:"48",safari:"10",node:"6.5",ios:"10",samsung:"5",opera:"38",electron:"1.2"},"es6.weak-map":{chrome:"51",edge:"15",firefox:"53",safari:"9",node:"6.5",ios:"9",samsung:"5",opera:"38",electron:"1.2"},"es6.weak-set":{chrome:"51",edge:"15",firefox:"53",safari:"9",node:"6.5",ios:"9",samsung:"5",opera:"38",electron:"1.2"}}})),MU=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-async-generators",manipulateOptions:function(e,t){t.plugins.push("asyncGenerators")}}}));t.default=r}))),LU=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-dynamic-import",manipulateOptions:function(e,t){t.plugins.push("dynamicImport")}}}));t.default=r}))),UU=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-json-strings",manipulateOptions:function(e,t){t.plugins.push("jsonStrings")}}}));t.default=r}))),GU=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-nullish-coalescing-operator",manipulateOptions:function(e,t){t.plugins.push("nullishCoalescingOperator")}}}));t.default=r}))),VU=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-object-rest-spread",manipulateOptions:function(e,t){t.plugins.push("objectRestSpread")}}}));t.default=r}))),WU=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-optional-catch-binding",manipulateOptions:function(e,t){t.plugins.push("optionalCatchBinding")}}}));t.default=r}))),HU=Gt(Vt((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=(0,cP.declare)((function(e){return e.assertVersion(7),{name:"syntax-optional-chaining",manipulateOptions:function(e,t){t.plugins.push("optionalChaining")}}}));t.default=r}))),qU={"proposal-async-generator-functions":MP,"proposal-dynamic-import":fF,"proposal-json-strings":xF,"proposal-nullish-coalescing-operator":SF,"proposal-object-rest-spread":PF,"proposal-optional-catch-binding":FF,"proposal-optional-chaining":IF,"proposal-unicode-property-regex":C_,"syntax-async-generators":MU,"syntax-dynamic-import":LU,"syntax-json-strings":UU,"syntax-nullish-coalescing-operator":GU,"syntax-object-rest-spread":VU,"syntax-optional-catch-binding":WU,"syntax-optional-chaining":HU,"syntax-top-level-await":CP,"transform-arrow-functions":xI,"transform-async-to-generator":bI,"transform-block-scoped-functions":EI,"transform-block-scoping":TI,"transform-classes":_B,"transform-computed-properties":IB,"transform-destructuring":BB,"transform-dotall-regex":OB,"transform-duplicate-keys":NB,"transform-exponentiation-operator":LB,"transform-for-of":VB,"transform-function-name":WB,"transform-literals":KB,"transform-member-expression-literals":zB,"transform-modules-amd":OO,"transform-modules-commonjs":LO,"transform-modules-systemjs":qO,"transform-modules-umd":XO,"transform-named-capturing-groups-regex":YO,"transform-new-target":JO,"transform-object-super":QO,"transform-parameters":gN,"transform-property-literals":vN,"transform-regenerator":YR,"transform-reserved-words":JR,"transform-shorthand-properties":CM,"transform-spread":TM,"transform-sticky-regex":jM,"transform-template-literals":FM,"transform-typeof-symbol":_M,"transform-unicode-regex":YM},KU={},zU=0,XU=Object.keys(gU);zU<XU.length;zU++){var YU=XU[zU];Object.hasOwnProperty.call(qU,YU)&&(KU[YU]=gU[YU])}var JU={configPath:"configPath",corejs:"corejs",debug:"debug",exclude:"exclude",forceAllTransforms:"forceAllTransforms",ignoreBrowserslistConfig:"ignoreBrowserslistConfig",include:"include",loose:"loose",modules:"modules",shippedProposals:"shippedProposals",spec:"spec",targets:"targets",useBuiltIns:"useBuiltIns"},$U={false:!1,auto:"auto",amd:"amd",commonjs:"commonjs",cjs:"cjs",systemjs:"systemjs",umd:"umd"},QU={false:!1,entry:"entry",usage:"usage"},ZU=["web.timers","web.immediate","web.dom.iterable"];function eG(e){var t=Object.keys(e),r=!t.length,n=t.some((function(e){return"node"!==e}));return r||n?ZU:null}var tG=Object.keys(KU),rG=["proposal-dynamic-import"].concat(Object.keys(_U).map((function(e){return _U[e]}))),nG=function(e,t,r){return Array.from(function(e,t){return new Set([].concat(tG,"exclude"===e?rG:[],t?2==t?[].concat(Object.keys(RU),ZU):Object.keys(IU):[]))}(t,r)).filter((function(t){return e instanceof RegExp&&e.test(t)}))},aG=function(e,t,r){if(void 0===e&&(e=[]),0===e.length)return[];var n,a,s=e.map((function(e){return nG(function(e){if(e instanceof RegExp)return e;try{return new RegExp("^"+sG(e)+"$")}catch(e){return null}}(e),t,r)})),i=e.filter((function(e,t){return 0===s[t].length}));return NU(0===i.length,"Invalid Option: The plugins/built-ins '"+i.join(", ")+"' passed to the '"+t+"' option are not\n valid. Please check data/[plugin-features|built-in-features].js in babel-preset-env"),n=s,(a=[]).concat.apply(a,n)},sG=function(e){return e.replace(/^(@babel\/|babel-)(plugin-)?/,"")},iG=function(e,t,r){if(void 0===t&&(t=r),"boolean"!=typeof t)throw new Error("Preset env: '"+e+"' option must be a boolean.");return t};function oG(e){!function(e){var t=Object.keys(JU);for(var r in e)if(!JU[r])throw new Error("Invalid Option: "+r+" is not a valid top-level option.\n Maybe you meant to use '"+OU(r,t)+"'?")}(e);var t,r=(void 0===(t=e.useBuiltIns)&&(t=!1),NU(QU[t.toString()]||QU[t.toString()]===QU.false,"Invalid Option: The 'useBuiltIns' option must be either\n 'false' (default) to indicate no polyfill,\n '\"entry\"' to indicate replacing the entry polyfill, or\n '\"usage\"' to import only used polyfills per file"),t),n=function(e,t){var r,n=!1;t&&void 0===e?(r=2,console.warn("\nWARNING: We noticed you're using the `useBuiltIns` option without declaring a core-js version. Currently, we assume version 2.x when no version is passed. Since this default version will likely change in future versions of Babel, we recommend explicitly setting the core-js version you are using via the `corejs` option.\n\nYou should also be sure that the version you pass to the `corejs` option matches the version specified in your `package.json`'s `dependencies` section. If it doesn't, you need to run one of the following commands:\n\n npm install --save core-js@2 npm install --save core-js@3\n yarn add core-js@2 yarn add core-js@3\n")):"object"==typeof e&&null!==e?(r=e.version,n=Boolean(e.proposals)):r=e;var a=!!r&&Bw(String(r));if(!t&&a&&console.log("\nThe `corejs` option only has an effect when the `useBuiltIns` option is not `false`\n"),t&&(!a||a.major<2||a.major>3))throw new RangeError("Invalid Option: The version passed to `corejs` is invalid. Currently, only core-js@2 and core-js@3 are supported.");return{version:a,proposals:n}}(e.corejs,r),a=aG(e.include,JU.include,!!n.version&&n.version.major),s=aG(e.exclude,JU.exclude,!!n.version&&n.version.major);!function(e,t){void 0===e&&(e=[]),void 0===t&&(t=[]);var r=e.filter((function(e){return t.indexOf(e)>=0}));NU(0===r.length,"Invalid Option: The plugins/built-ins '"+r.join(", ")+'\' were found in both the "include" and\n "exclude" options.')}(a,s);var i,o,u,c,l=iG(JU.shippedProposals,e.shippedProposals,!1)||n.proposals;return{configPath:(c=e.configPath,void 0===c&&(c=es.cwd()),NU("string"==typeof c,"Invalid Option: The configPath option '"+c+"' is invalid, only strings are allowed."),c),corejs:n,debug:iG(JU.debug,e.debug,!1),include:a,exclude:s,forceAllTransforms:iG(JU.forceAllTransforms,e.forceAllTransforms,!1),ignoreBrowserslistConfig:(u=e.ignoreBrowserslistConfig,iG(JU.ignoreBrowserslistConfig,u,!1)),loose:iG(JU.loose,e.loose,!1),modules:(o=e.modules,void 0===o&&(o=$U.auto),NU($U[o.toString()]||$U[o.toString()]===$U.false,"Invalid Option: The 'modules' option must be one of \n - 'false' to indicate no module processing\n - a specific module type: 'commonjs', 'amd', 'umd', 'systemjs' - 'auto' (default) which will automatically select 'false' if the current\n process is known to support ES module syntax, or \"commonjs\" otherwise\n"),o),shippedProposals:l,spec:iG(JU.spec,e.spec,!1),targets:(i=e.targets,"string"==typeof i||Array.isArray(i)?{browsers:i}:Object.assign({},i)),useBuiltIns:r}}var uG={"proposal-async-generator-functions":"syntax-async-generators","proposal-json-strings":"syntax-json-strings","proposal-nullish-coalescing-operator":"syntax-nullish-coalescing-operator","proposal-object-rest-spread":"syntax-object-rest-spread","proposal-optional-catch-binding":"syntax-optional-catch-binding","proposal-optional-chaining":"syntax-optional-chaining","proposal-unicode-property-regex":null},cG=Object.keys(uG).map((function(e){return[e,uG[e]]})),lG={pluginSyntaxMap:new Map(cG),proposalPlugins:{}},pG=lG.pluginSyntaxMap,dG=lG.proposalPlugins,fG=Wt(Object.freeze({__proto__:null,default:{"transform-regenerator":[]}})),hG=["es6.object.to-string","es6.array.iterator","web.dom.iterable"],mG=["es6.string.iterator"].concat(hG),yG=["es6.object.to-string","es6.promise"],gG={DataView:"es6.typed.data-view",Float32Array:"es6.typed.float32-array",Float64Array:"es6.typed.float64-array",Int8Array:"es6.typed.int8-array",Int16Array:"es6.typed.int16-array",Int32Array:"es6.typed.int32-array",Map:["es6.map"].concat(mG),Number:"es6.number.constructor",Promise:yG,RegExp:["es6.regexp.constructor"],Set:["es6.set"].concat(mG),Symbol:["es6.symbol","es7.symbol.async-iterator"],Uint8Array:"es6.typed.uint8-array",Uint8ClampedArray:"es6.typed.uint8-clamped-array",Uint16Array:"es6.typed.uint16-array",Uint32Array:"es6.typed.uint32-array",WeakMap:["es6.weak-map"].concat(mG),WeakSet:["es6.weak-set"].concat(mG)},vG={__defineGetter__:["es7.object.define-getter"],__defineSetter__:["es7.object.define-setter"],__lookupGetter__:["es7.object.lookup-getter"],__lookupSetter__:["es7.object.lookup-setter"],anchor:["es6.string.anchor"],big:["es6.string.big"],bind:["es6.function.bind"],blink:["es6.string.blink"],bold:["es6.string.bold"],codePointAt:["es6.string.code-point-at"],copyWithin:["es6.array.copy-within"],endsWith:["es6.string.ends-with"],entries:hG,every:["es6.array.is-array"],fill:["es6.array.fill"],filter:["es6.array.filter"],finally:["es7.promise.finally"].concat(yG),find:["es6.array.find"],findIndex:["es6.array.find-index"],fixed:["es6.string.fixed"],flags:["es6.regexp.flags"],flatMap:["es7.array.flat-map"],fontcolor:["es6.string.fontcolor"],fontsize:["es6.string.fontsize"],forEach:["es6.array.for-each"],includes:["es6.string.includes","es7.array.includes"],indexOf:["es6.array.index-of"],italics:["es6.string.italics"],keys:hG,lastIndexOf:["es6.array.last-index-of"],link:["es6.string.link"],map:["es6.array.map"],match:["es6.regexp.match"],name:["es6.function.name"],padStart:["es7.string.pad-start"],padEnd:["es7.string.pad-end"],reduce:["es6.array.reduce"],reduceRight:["es6.array.reduce-right"],repeat:["es6.string.repeat"],replace:["es6.regexp.replace"],search:["es6.regexp.search"],slice:["es6.array.slice"],small:["es6.string.small"],some:["es6.array.some"],sort:["es6.array.sort"],split:["es6.regexp.split"],startsWith:["es6.string.starts-with"],strike:["es6.string.strike"],sub:["es6.string.sub"],sup:["es6.string.sup"],toISOString:["es6.date.to-iso-string"],toJSON:["es6.date.to-json"],toString:["es6.object.to-string","es6.date.to-string","es6.regexp.to-string"],trim:["es6.string.trim"],trimEnd:["es7.string.trim-right"],trimLeft:["es7.string.trim-left"],trimRight:["es7.string.trim-right"],trimStart:["es7.string.trim-left"],values:hG},bG={Array:{from:["es6.array.from","es6.string.iterator"],isArray:"es6.array.is-array",of:"es6.array.of"},Date:{now:"es6.date.now"},Object:{assign:"es6.object.assign",create:"es6.object.create",defineProperty:"es6.object.define-property",defineProperties:"es6.object.define-properties",entries:"es7.object.entries",freeze:"es6.object.freeze",getOwnPropertyDescriptors:"es7.object.get-own-property-descriptors",getOwnPropertySymbols:"es6.symbol",is:"es6.object.is",isExtensible:"es6.object.is-extensible",isFrozen:"es6.object.is-frozen",isSealed:"es6.object.is-sealed",keys:"es6.object.keys",preventExtensions:"es6.object.prevent-extensions",seal:"es6.object.seal",setPrototypeOf:"es6.object.set-prototype-of",values:"es7.object.values"},Math:{acosh:"es6.math.acosh",asinh:"es6.math.asinh",atanh:"es6.math.atanh",cbrt:"es6.math.cbrt",clz32:"es6.math.clz32",cosh:"es6.math.cosh",expm1:"es6.math.expm1",fround:"es6.math.fround",hypot:"es6.math.hypot",imul:"es6.math.imul",log1p:"es6.math.log1p",log10:"es6.math.log10",log2:"es6.math.log2",sign:"es6.math.sign",sinh:"es6.math.sinh",tanh:"es6.math.tanh",trunc:"es6.math.trunc"},String:{fromCodePoint:"es6.string.from-code-point",raw:"es6.string.raw"},Number:{EPSILON:"es6.number.epsilon",MIN_SAFE_INTEGER:"es6.number.min-safe-integer",MAX_SAFE_INTEGER:"es6.number.max-safe-integer",isFinite:"es6.number.is-finite",isInteger:"es6.number.is-integer",isSafeInteger:"es6.number.is-safe-integer",isNaN:"es6.number.is-nan",parseFloat:"es6.number.parse-float",parseInt:"es6.number.parse-int"},Promise:{all:mG,race:mG},Reflect:{apply:"es6.reflect.apply",construct:"es6.reflect.construct",defineProperty:"es6.reflect.define-property",deleteProperty:"es6.reflect.delete-property",get:"es6.reflect.get",getOwnPropertyDescriptor:"es6.reflect.get-own-property-descriptor",getPrototypeOf:"es6.reflect.get-prototype-of",has:"es6.reflect.has",isExtensible:"es6.reflect.is-extensible",ownKeys:"es6.reflect.own-keys",preventExtensions:"es6.reflect.prevent-extensions",set:"es6.reflect.set",setPrototypeOf:"es6.reflect.set-prototype-of"}},xG=Object.hasOwnProperty.call.bind(Object.hasOwnProperty);function EG(e){return Object.prototype.toString.call(e).slice(8,-1).toLowerCase()}function AG(e,t,r){var n=new Set,a=e,s=Array.isArray(a),i=0;for(a=s?a:a[Symbol.iterator]();;){var o;if(s){if(i>=a.length)break;o=a[i++]}else{if((i=a.next()).done)break;o=i.value}var u=o;t.has(u)&&r.has(u)&&n.add(u)}return n}function wG(e){var t=e.node;if(0===t.specifiers.length)return t.source.value}function SG(e){var t=e.node;if(v(t)){var r=t.expression;return f(r)&&S(r.callee)&&"require"===r.callee.name&&1===r.arguments.length&&T(r.arguments[0])?r.arguments[0].value:void 0}}function DG(e){return"@babel/polyfill"===e||"core-js"===e}var CG={"regenerator-runtime":"regenerator-runtime/runtime"};function TG(e){return CG[e]||"core-js/modules/"+e}function jG(e,t){return function(e,t,r){return new vI(e).addSideEffect(t,r)}(e,TG(t))}function PG(e){if(!e.node)return!1;var t=e.scope.getBinding(e.node.name);return!!t&&t.path.isImportNamespaceSpecifier()}var kG="\n When setting `useBuiltIns: 'usage'`, polyfills are automatically imported when needed.\n Please remove the `import '@babel/polyfill'` call or use `useBuiltIns: 'entry'` instead.";function FG(e,t){var r=e.types,n=t.include,a=t.exclude,s=t.polyfillTargets,i=t.debug,o=bU(RU,n,a,s,eG(s));return{name:"corejs2-usage",pre:function(e){var t=e.path;this.polyfillsSet=new Set,this.addImport=function(e){this.polyfillsSet.has(e)||(this.polyfillsSet.add(e),jG(t,e))},this.addUnsupported=function(e){var t=Array.isArray(e)?e:[e],r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}var s=a;o.has(s)&&this.addImport(s)}}},post:function(){i&&kU(this.polyfillsSet,this.file.opts.filename,s,RU)},visitor:{ImportDeclaration:function(e){DG(wG(e))&&(console.warn(kG),e.remove())},Program:function(e){e.get("body").forEach((function(e){DG(SG(e))&&(console.warn(kG),e.remove())}))},ReferencedIdentifier:function(e){var t=e.node.name,n=e.parent,a=e.scope;if(!r.isMemberExpression(n)&&xG(gG,t)&&!a.getBindingIdentifier(t)){var s=gG[t];this.addUnsupported(s)}},CallExpression:function(e){if(!e.node.arguments.length){var t=e.node.callee;r.isMemberExpression(t)&&t.computed&&e.get("callee.property").matchesPattern("Symbol.iterator")&&this.addImport("web.dom.iterable")}},BinaryExpression:function(e){"in"===e.node.operator&&e.get("left").matchesPattern("Symbol.iterator")&&this.addImport("web.dom.iterable")},YieldExpression:function(e){e.node.delegate&&this.addImport("web.dom.iterable")},MemberExpression:{enter:function(e){var t=e.node,n=t.object,a=t.property;if(!PG(e.get("object"))){var s=n.name,i="",o="";if(t.computed)if(r.isStringLiteral(a))i=a.value;else{var u=e.get("property").evaluate();u.confident&&u.value&&(i=u.value)}else i=a.name;if(e.scope.getBindingIdentifier(n.name)){var c=e.get("object").evaluate();c.value?o=EG(c.value):c.deopt&&c.deopt.isIdentifier()&&(s=c.deopt.node.name)}if(xG(bG,s)){var l=bG[s];if(xG(l,i)){var p=l[i];this.addUnsupported(p)}}if(xG(vG,i)){var d=vG[i];o&&(d=d.filter((function(e){return e.includes(o)}))),this.addUnsupported(d)}}},exit:function(e){var t=e.node.object.name;if(xG(gG,t)&&!e.scope.getBindingIdentifier(t)){var r=gG[t];this.addUnsupported(r)}}},VariableDeclarator:function(e){var t=e.node,n=t.id,a=t.init;if(r.isObjectPattern(n)&&(!a||!e.scope.getBindingIdentifier(a.name))){var s=n.properties,i=Array.isArray(s),o=0;for(s=i?s:s[Symbol.iterator]();;){var u;if(i){if(o>=s.length)break;u=s[o++]}else{if((o=s.next()).done)break;u=o.value}var c=u.key;if(!t.computed&&r.isIdentifier(c)&&xG(vG,c.name)){var l=vG[c.name];this.addUnsupported(l)}}}}}}}var _G=Wt(Object.freeze({__proto__:null,default:["esnext.global-this","esnext.promise.all-settled","esnext.string.match-all"]})),IG="object"==typeof es&&es.env&&es.env.NODE_DEBUG&&/\bsemver\b/i.test(es.env.NODE_DEBUG)?function(){for(var e,t=arguments.length,r=new Array(t),n=0;n<t;n++)r[n]=arguments[n];return(e=console).error.apply(e,["SEMVER"].concat(r))}:function(){},BG={SEMVER_SPEC_VERSION:"2.0.0",MAX_LENGTH:256,MAX_SAFE_INTEGER:Number.MAX_SAFE_INTEGER||9007199254740991,MAX_SAFE_COMPONENT_LENGTH:16},OG=Vt((function(e,t){var r=BG.MAX_SAFE_COMPONENT_LENGTH,n=(t=e.exports={}).re=[],a=t.src=[],s=t.t={},i=0,o=function(e,t,r){var o=i++;IG(o,t),s[e]=o,a[o]=t,n[o]=new RegExp(t,r?"g":void 0)};o("NUMERICIDENTIFIER","0|[1-9]\\d*"),o("NUMERICIDENTIFIERLOOSE","[0-9]+"),o("NONNUMERICIDENTIFIER","\\d*[a-zA-Z-][a-zA-Z0-9-]*"),o("MAINVERSION","("+a[s.NUMERICIDENTIFIER]+")\\.("+a[s.NUMERICIDENTIFIER]+")\\.("+a[s.NUMERICIDENTIFIER]+")"),o("MAINVERSIONLOOSE","("+a[s.NUMERICIDENTIFIERLOOSE]+")\\.("+a[s.NUMERICIDENTIFIERLOOSE]+")\\.("+a[s.NUMERICIDENTIFIERLOOSE]+")"),o("PRERELEASEIDENTIFIER","(?:"+a[s.NUMERICIDENTIFIER]+"|"+a[s.NONNUMERICIDENTIFIER]+")"),o("PRERELEASEIDENTIFIERLOOSE","(?:"+a[s.NUMERICIDENTIFIERLOOSE]+"|"+a[s.NONNUMERICIDENTIFIER]+")"),o("PRERELEASE","(?:-("+a[s.PRERELEASEIDENTIFIER]+"(?:\\."+a[s.PRERELEASEIDENTIFIER]+")*))"),o("PRERELEASELOOSE","(?:-?("+a[s.PRERELEASEIDENTIFIERLOOSE]+"(?:\\."+a[s.PRERELEASEIDENTIFIERLOOSE]+")*))"),o("BUILDIDENTIFIER","[0-9A-Za-z-]+"),o("BUILD","(?:\\+("+a[s.BUILDIDENTIFIER]+"(?:\\."+a[s.BUILDIDENTIFIER]+")*))"),o("FULLPLAIN","v?"+a[s.MAINVERSION]+a[s.PRERELEASE]+"?"+a[s.BUILD]+"?"),o("FULL","^"+a[s.FULLPLAIN]+"$"),o("LOOSEPLAIN","[v=\\s]*"+a[s.MAINVERSIONLOOSE]+a[s.PRERELEASELOOSE]+"?"+a[s.BUILD]+"?"),o("LOOSE","^"+a[s.LOOSEPLAIN]+"$"),o("GTLT","((?:<|>)?=?)"),o("XRANGEIDENTIFIERLOOSE",a[s.NUMERICIDENTIFIERLOOSE]+"|x|X|\\*"),o("XRANGEIDENTIFIER",a[s.NUMERICIDENTIFIER]+"|x|X|\\*"),o("XRANGEPLAIN","[v=\\s]*("+a[s.XRANGEIDENTIFIER]+")(?:\\.("+a[s.XRANGEIDENTIFIER]+")(?:\\.("+a[s.XRANGEIDENTIFIER]+")(?:"+a[s.PRERELEASE]+")?"+a[s.BUILD]+"?)?)?"),o("XRANGEPLAINLOOSE","[v=\\s]*("+a[s.XRANGEIDENTIFIERLOOSE]+")(?:\\.("+a[s.XRANGEIDENTIFIERLOOSE]+")(?:\\.("+a[s.XRANGEIDENTIFIERLOOSE]+")(?:"+a[s.PRERELEASELOOSE]+")?"+a[s.BUILD]+"?)?)?"),o("XRANGE","^"+a[s.GTLT]+"\\s*"+a[s.XRANGEPLAIN]+"$"),o("XRANGELOOSE","^"+a[s.GTLT]+"\\s*"+a[s.XRANGEPLAINLOOSE]+"$"),o("COERCE","(^|[^\\d])(\\d{1,"+r+"})(?:\\.(\\d{1,"+r+"}))?(?:\\.(\\d{1,"+r+"}))?(?:$|[^\\d])"),o("COERCERTL",a[s.COERCE],!0),o("LONETILDE","(?:~>?)"),o("TILDETRIM","(\\s*)"+a[s.LONETILDE]+"\\s+",!0),t.tildeTrimReplace="$1~",o("TILDE","^"+a[s.LONETILDE]+a[s.XRANGEPLAIN]+"$"),o("TILDELOOSE","^"+a[s.LONETILDE]+a[s.XRANGEPLAINLOOSE]+"$"),o("LONECARET","(?:\\^)"),o("CARETTRIM","(\\s*)"+a[s.LONECARET]+"\\s+",!0),t.caretTrimReplace="$1^",o("CARET","^"+a[s.LONECARET]+a[s.XRANGEPLAIN]+"$"),o("CARETLOOSE","^"+a[s.LONECARET]+a[s.XRANGEPLAINLOOSE]+"$"),o("COMPARATORLOOSE","^"+a[s.GTLT]+"\\s*("+a[s.LOOSEPLAIN]+")$|^$"),o("COMPARATOR","^"+a[s.GTLT]+"\\s*("+a[s.FULLPLAIN]+")$|^$"),o("COMPARATORTRIM","(\\s*)"+a[s.GTLT]+"\\s*("+a[s.LOOSEPLAIN]+"|"+a[s.XRANGEPLAIN]+")",!0),t.comparatorTrimReplace="$1$2$3",o("HYPHENRANGE","^\\s*("+a[s.XRANGEPLAIN]+")\\s+-\\s+("+a[s.XRANGEPLAIN]+")\\s*$"),o("HYPHENRANGELOOSE","^\\s*("+a[s.XRANGEPLAINLOOSE]+")\\s+-\\s+("+a[s.XRANGEPLAINLOOSE]+")\\s*$"),o("STAR","(<|>)?=?\\s*\\*")})),NG=(OG.re,OG.src,OG.t,OG.tildeTrimReplace,OG.caretTrimReplace,OG.comparatorTrimReplace,/^[0-9]+$/),RG=function(e,t){var r=NG.test(e),n=NG.test(t);return r&&n&&(e=+e,t=+t),e===t?0:r&&!n?-1:n&&!r?1:e<t?-1:1},MG=RG,LG=BG.MAX_LENGTH,UG=BG.MAX_SAFE_INTEGER,GG=OG.re,VG=OG.t,WG=MG,HG=function(){function e(t,r){if(r&&"object"==typeof r||(r={loose:!!r,includePrerelease:!1}),t instanceof e){if(t.loose===!!r.loose&&t.includePrerelease===!!r.includePrerelease)return t;t=t.version}else if("string"!=typeof t)throw new TypeError("Invalid Version: "+t);if(t.length>LG)throw new TypeError("version is longer than "+LG+" characters");IG("SemVer",t,r),this.options=r,this.loose=!!r.loose,this.includePrerelease=!!r.includePrerelease;var n=t.trim().match(r.loose?GG[VG.LOOSE]:GG[VG.FULL]);if(!n)throw new TypeError("Invalid Version: "+t);if(this.raw=t,this.major=+n[1],this.minor=+n[2],this.patch=+n[3],this.major>UG||this.major<0)throw new TypeError("Invalid major version");if(this.minor>UG||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>UG||this.patch<0)throw new TypeError("Invalid patch version");n[4]?this.prerelease=n[4].split(".").map((function(e){if(/^[0-9]+$/.test(e)){var t=+e;if(t>=0&&t<UG)return t}return e})):this.prerelease=[],this.build=n[5]?n[5].split("."):[],this.format()}var t=e.prototype;return t.format=function(){return this.version=this.major+"."+this.minor+"."+this.patch,this.prerelease.length&&(this.version+="-"+this.prerelease.join(".")),this.version},t.toString=function(){return this.version},t.compare=function(t){if(IG("SemVer.compare",this.version,this.options,t),!(t instanceof e)){if("string"==typeof t&&t===this.version)return 0;t=new e(t,this.options)}return t.version===this.version?0:this.compareMain(t)||this.comparePre(t)},t.compareMain=function(t){return t instanceof e||(t=new e(t,this.options)),WG(this.major,t.major)||WG(this.minor,t.minor)||WG(this.patch,t.patch)},t.comparePre=function(t){if(t instanceof e||(t=new e(t,this.options)),this.prerelease.length&&!t.prerelease.length)return-1;if(!this.prerelease.length&&t.prerelease.length)return 1;if(!this.prerelease.length&&!t.prerelease.length)return 0;var r=0;do{var n=this.prerelease[r],a=t.prerelease[r];if(IG("prerelease compare",r,n,a),void 0===n&&void 0===a)return 0;if(void 0===a)return 1;if(void 0===n)return-1;if(n!==a)return WG(n,a)}while(++r)},t.compareBuild=function(t){t instanceof e||(t=new e(t,this.options));var r=0;do{var n=this.build[r],a=t.build[r];if(IG("prerelease compare",r,n,a),void 0===n&&void 0===a)return 0;if(void 0===a)return 1;if(void 0===n)return-1;if(n!==a)return WG(n,a)}while(++r)},t.inc=function(e,t){switch(e){case"premajor":this.prerelease.length=0,this.patch=0,this.minor=0,this.major++,this.inc("pre",t);break;case"preminor":this.prerelease.length=0,this.patch=0,this.minor++,this.inc("pre",t);break;case"prepatch":this.prerelease.length=0,this.inc("patch",t),this.inc("pre",t);break;case"prerelease":0===this.prerelease.length&&this.inc("patch",t),this.inc("pre",t);break;case"major":0===this.minor&&0===this.patch&&0!==this.prerelease.length||this.major++,this.minor=0,this.patch=0,this.prerelease=[];break;case"minor":0===this.patch&&0!==this.prerelease.length||this.minor++,this.patch=0,this.prerelease=[];break;case"patch":0===this.prerelease.length&&this.patch++,this.prerelease=[];break;case"pre":if(0===this.prerelease.length)this.prerelease=[0];else{for(var r=this.prerelease.length;--r>=0;)"number"==typeof this.prerelease[r]&&(this.prerelease[r]++,r=-2);-1===r&&this.prerelease.push(0)}t&&(this.prerelease[0]===t?isNaN(this.prerelease[1])&&(this.prerelease=[t,0]):this.prerelease=[t,0]);break;default:throw new Error("invalid increment argument: "+e)}return this.format(),this.raw=this.version,this},e}(),qG=function(e,t,r){return new HG(e,r).compare(new HG(t,r))},KG=function(e,t,r){return 0===qG(e,t,r)},zG=function(e,t,r){return 0!==qG(e,t,r)},XG=function(e,t,r){return qG(e,t,r)>0},YG=function(e,t,r){return qG(e,t,r)>=0},JG=function(e,t,r){return qG(e,t,r)<0},$G=function(e,t,r){return qG(e,t,r)<=0},QG=function(e,t,r,n){switch(t){case"===":return"object"==typeof e&&(e=e.version),"object"==typeof r&&(r=r.version),e===r;case"!==":return"object"==typeof e&&(e=e.version),"object"==typeof r&&(r=r.version),e!==r;case"":case"=":case"==":return KG(e,r,n);case"!=":return zG(e,r,n);case">":return XG(e,r,n);case">=":return YG(e,r,n);case"<":return JG(e,r,n);case"<=":return $G(e,r,n);default:throw new TypeError("Invalid operator: "+t)}},ZG=BG.MAX_LENGTH,eV=OG.re,tV=OG.t,rV=function(e,t){if(t&&"object"==typeof t||(t={loose:!!t,includePrerelease:!1}),e instanceof HG)return e;if("string"!=typeof e)return null;if(e.length>ZG)return null;if(!(t.loose?eV[tV.LOOSE]:eV[tV.FULL]).test(e))return null;try{return new HG(e,t)}catch(e){return null}},nV=OG.re,aV=OG.t,sV=function(e,t){if(e instanceof HG)return e;if("number"==typeof e&&(e=String(e)),"string"!=typeof e)return null;var r=null;if((t=t||{}).rtl){for(var n;(n=nV[aV.COERCERTL].exec(e))&&(!r||r.index+r[0].length!==e.length);)r&&n.index+n[0].length===r.index+r[0].length||(r=n),nV[aV.COERCERTL].lastIndex=n.index+n[1].length+n[2].length;nV[aV.COERCERTL].lastIndex=-1}else r=e.match(nV[aV.COERCE]);return null===r?null:rV(r[2]+"."+(r[3]||"0")+"."+(r[4]||"0"),t)};var iV={compare:function(e,t,r){return QG(sV(e),t,sV(r))},has:Function.call.bind({}.hasOwnProperty),intersection:function(e,t){var r=e instanceof Set?e:new Set(e);return t.filter((function(e){return r.has(e)}))},semver:sV,sortObjectByKey:function(e,t){return Object.keys(e).sort(t).reduce((function(t,r){return t[r]=e[r],t}),{})}},oV=Object.freeze({__proto__:null,default:{"3.0":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.data-view","es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string","es.function.bind","es.function.has-instance","es.function.name","es.json.to-string-tag","es.map","es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc","es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values","es.parse-float","es.parse-int","es.promise","es.promise.finally","es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of","es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.to-string","es.set","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string","es.weak-map","es.weak-set","esnext.aggregate-error","esnext.array.last-index","esnext.array.last-item","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.dom-collections.for-each","web.dom-collections.iterator","web.immediate","web.queue-microtask","web.timers","web.url","web.url.to-json","web.url-search-params"],3.1:["es.string.match-all","es.symbol.match-all","esnext.symbol.replace-all"],3.2:["es.promise.all-settled","esnext.array.is-template-object","esnext.map.update-or-insert","esnext.symbol.async-dispose"],3.3:["es.global-this","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.upsert","esnext.weak-map.upsert"],3.4:["es.json.stringify"],3.5:["esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values"],3.6:["es.regexp.sticky","es.regexp.test"]}}),uV=Object.freeze({__proto__:null,default:["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.data-view","es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string","es.function.bind","es.function.has-instance","es.function.name","es.global-this","es.json.stringify","es.json.to-string-tag","es.map","es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc","es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values","es.parse-float","es.parse-int","es.promise","es.promise.all-settled","es.promise.finally","es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of","es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.set","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string","es.weak-map","es.weak-set","esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.dom-collections.for-each","web.dom-collections.iterator","web.immediate","web.queue-microtask","web.timers","web.url","web.url.to-json","web.url-search-params"]}),cV=Wt(oV),lV=Wt(uV),pV=iV.compare,dV=iV.intersection,fV=iV.semver,hV=function(e){var t=fV(e);if(3!==t.major)throw RangeError("This version of `core-js-compat` works only with `core-js@3`.");for(var r=[],n=0,a=Object.keys(cV);n<a.length;n++){var s=a[n];pV(s,"<=",t)&&r.push.apply(r,cV[s])}return dV(r,lV)},mV=["es.array.iterator","web.dom-collections.iterator"],yV=["es.string.iterator"].concat(mV),gV=["es.object.to-string"].concat(mV),vV=["es.object.to-string"].concat(yV),bV=["es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string","es.object.to-string","es.array.iterator","es.array-buffer.slice"],xV={from:"es.typed-array.from",of:"es.typed-array.of"},EV=["es.promise","es.object.to-string"],AV=[].concat(EV,yV),wV=["es.symbol","es.symbol.description","es.object.to-string"],SV=["es.map","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.includes","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.reduce","esnext.map.some","esnext.map.update"].concat(vV),DV=["es.set","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union"].concat(vV),CV=["es.weak-map","esnext.weak-map.delete-all"].concat(vV),TV=["es.weak-set","esnext.weak-set.add-all","esnext.weak-set.delete-all"].concat(vV),jV=["web.url"].concat(vV),PV={AggregateError:["esnext.aggregate-error"].concat(yV),ArrayBuffer:["es.array-buffer.constructor","es.array-buffer.slice","es.object.to-string"],DataView:["es.data-view","es.array-buffer.slice","es.object.to-string"],Date:["es.date.to-string"],Float32Array:["es.typed-array.float32-array"].concat(bV),Float64Array:["es.typed-array.float64-array"].concat(bV),Int8Array:["es.typed-array.int8-array"].concat(bV),Int16Array:["es.typed-array.int16-array"].concat(bV),Int32Array:["es.typed-array.int32-array"].concat(bV),Uint8Array:["es.typed-array.uint8-array"].concat(bV),Uint8ClampedArray:["es.typed-array.uint8-clamped-array"].concat(bV),Uint16Array:["es.typed-array.uint16-array"].concat(bV),Uint32Array:["es.typed-array.uint32-array"].concat(bV),Map:SV,Number:["es.number.constructor"],Observable:["esnext.observable","esnext.symbol.observable","es.object.to-string"].concat(vV),Promise:EV,RegExp:["es.regexp.constructor","es.regexp.exec","es.regexp.to-string"],Set:DV,Symbol:wV,URL:["web.url"].concat(jV),URLSearchParams:jV,WeakMap:CV,WeakSet:TV,clearImmediate:["web.immediate"],compositeKey:["esnext.composite-key"],compositeSymbol:["esnext.composite-symbol"].concat(wV),fetch:EV,globalThis:["es.global-this","esnext.global-this"],parseFloat:["es.parse-float"],parseInt:["es.parse-int"],queueMicrotask:["web.queue-microtask"],setTimeout:["web.timers"],setInterval:["web.timers"],setImmediate:["web.immediate"]},kV={at:["esnext.string.at"],anchor:["es.string.anchor"],big:["es.string.big"],bind:["es.function.bind"],blink:["es.string.blink"],bold:["es.string.bold"],codePointAt:["es.string.code-point-at"],codePoints:["esnext.string.code-points"],concat:["es.array.concat"],copyWithin:["es.array.copy-within"],description:["es.symbol","es.symbol.description"],endsWith:["es.string.ends-with"],entries:gV,every:["es.array.every"],exec:["es.regexp.exec"],fill:["es.array.fill"],filter:["es.array.filter"],finally:["es.promise.finally"].concat(EV),find:["es.array.find"],findIndex:["es.array.find-index"],fixed:["es.string.fixed"],flags:["es.regexp.flags"],flat:["es.array.flat","es.array.unscopables.flat"],flatMap:["es.array.flat-map","es.array.unscopables.flat-map"],fontcolor:["es.string.fontcolor"],fontsize:["es.string.fontsize"],forEach:["es.array.for-each","web.dom-collections.for-each"],includes:["es.array.includes","es.string.includes"],indexOf:["es.array.index-of"],italics:["es.string.italics"],join:["es.array.join"],keys:gV,lastIndex:["esnext.array.last-index"],lastIndexOf:["es.array.last-index-of"],lastItem:["esnext.array.last-item"],link:["es.string.link"],match:["es.string.match","es.regexp.exec"],matchAll:["es.string.match-all","esnext.string.match-all"],map:["es.array.map"],name:["es.function.name"],padEnd:["es.string.pad-end"],padStart:["es.string.pad-start"],reduce:["es.array.reduce"],reduceRight:["es.array.reduce-right"],repeat:["es.string.repeat"],replace:["es.string.replace","es.regexp.exec"],replaceAll:["esnext.string.replace-all"],reverse:["es.array.reverse"],search:["es.string.search","es.regexp.exec"],slice:["es.array.slice"],small:["es.string.small"],some:["es.array.some"],sort:["es.array.sort"],splice:["es.array.splice"],split:["es.string.split","es.regexp.exec"],startsWith:["es.string.starts-with"],strike:["es.string.strike"],sub:["es.string.sub"],sup:["es.string.sup"],toFixed:["es.number.to-fixed"],toISOString:["es.date.to-iso-string"],toJSON:["es.date.to-json","web.url.to-json"],toPrecision:["es.number.to-precision"],toString:["es.object.to-string","es.regexp.to-string","es.date.to-string"],trim:["es.string.trim"],trimEnd:["es.string.trim-end"],trimLeft:["es.string.trim-start"],trimRight:["es.string.trim-end"],trimStart:["es.string.trim-start"],values:gV,__defineGetter__:["es.object.define-getter"],__defineSetter__:["es.object.define-setter"],__lookupGetter__:["es.object.lookup-getter"],__lookupSetter__:["es.object.lookup-setter"]},FV={Array:{from:["es.array.from","es.string.iterator"],isArray:["es.array.is-array"],of:["es.array.of"]},Date:{now:"es.date.now"},Object:{assign:"es.object.assign",create:"es.object.create",defineProperty:"es.object.define-property",defineProperties:"es.object.define-properties",entries:"es.object.entries",freeze:"es.object.freeze",fromEntries:["es.object.from-entries","es.array.iterator"],getOwnPropertyDescriptor:"es.object.get-own-property-descriptor",getOwnPropertyDescriptors:"es.object.get-own-property-descriptors",getOwnPropertyNames:"es.object.get-own-property-names",getOwnPropertySymbols:"es.symbol",getPrototypeOf:"es.object.get-prototype-of",is:"es.object.is",isExtensible:"es.object.is-extensible",isFrozen:"es.object.is-frozen",isSealed:"es.object.is-sealed",keys:"es.object.keys",preventExtensions:"es.object.prevent-extensions",seal:"es.object.seal",setPrototypeOf:"es.object.set-prototype-of",values:"es.object.values"},Math:{DEG_PER_RAD:"esnext.math.deg-per-rad",RAD_PER_DEG:"esnext.math.rad-per-deg",acosh:"es.math.acosh",asinh:"es.math.asinh",atanh:"es.math.atanh",cbrt:"es.math.cbrt",clamp:"esnext.math.clamp",clz32:"es.math.clz32",cosh:"es.math.cosh",degrees:"esnext.math.degrees",expm1:"es.math.expm1",fround:"es.math.fround",fscale:"esnext.math.fscale",hypot:"es.math.hypot",iaddh:"esnext.math.iaddh",imul:"es.math.imul",imulh:"esnext.math.imulh",isubh:"esnext.math.isubh",log1p:"es.math.log1p",log10:"es.math.log10",log2:"es.math.log2",radians:"esnext.math.radians",scale:"esnext.math.scale",seededPRNG:"esnext.math.seeded-prng",sign:"es.math.sign",signbit:"esnext.math.signbit",sinh:"es.math.sinh",tanh:"es.math.tanh",trunc:"es.math.trunc",umulh:"esnext.math.umulh"},String:{fromCodePoint:"es.string.from-code-point",raw:"es.string.raw"},Number:{EPSILON:"es.number.epsilon",MIN_SAFE_INTEGER:"es.number.min-safe-integer",MAX_SAFE_INTEGER:"es.number.max-safe-integer",fromString:"esnext.number.from-string",isFinite:"es.number.is-finite",isInteger:"es.number.is-integer",isSafeInteger:"es.number.is-safe-integer",isNaN:"es.number.is-nan",parseFloat:"es.number.parse-float",parseInt:"es.number.parse-int"},Map:{from:["esnext.map.from"].concat(SV),groupBy:["esnext.map.group-by"].concat(SV),keyBy:["esnext.map.key-by"].concat(SV),of:["esnext.map.of"].concat(SV)},Set:{from:["esnext.set.from"].concat(DV),of:["esnext.set.of"].concat(DV)},WeakMap:{from:["esnext.weak-map.from"].concat(CV),of:["esnext.weak-map.of"].concat(CV)},WeakSet:{from:["esnext.weak-set.from"].concat(TV),of:["esnext.weak-set.of"].concat(TV)},Promise:{all:AV,allSettled:["es.promise.all-settled","esnext.promise.all-settled"].concat(AV),any:["esnext.promise.any","esnext.aggregate-error"].concat(AV),race:AV,try:["esnext.promise.try"].concat(AV)},Reflect:{apply:"es.reflect.apply",construct:"es.reflect.construct",defineMetadata:"esnext.reflect.define-metadata",defineProperty:"es.reflect.define-property",deleteMetadata:"esnext.reflect.delete-metadata",deleteProperty:"es.reflect.delete-property",get:"es.reflect.get",getMetadata:"esnext.reflect.get-metadata",getMetadataKeys:"esnext.reflect.get-metadata-keys",getOwnMetadata:"esnext.reflect.get-own-metadata",getOwnMetadataKeys:"esnext.reflect.get-own-metadata-keys",getOwnPropertyDescriptor:"es.reflect.get-own-property-descriptor",getPrototypeOf:"es.reflect.get-prototype-of",has:"es.reflect.has",hasMetadata:"esnext.reflect.has-metadata",hasOwnMetadata:"esnext.reflect.has-own-metadata",isExtensible:"es.reflect.is-extensible",metadata:"esnext.reflect.metadata",ownKeys:"es.reflect.own-keys",preventExtensions:"es.reflect.prevent-extensions",set:"es.reflect.set",setPrototypeOf:"es.reflect.set-prototype-of"},Symbol:{asyncIterator:["es.symbol.async-iterator"],dispose:["esnext.symbol.dispose"],hasInstance:["es.symbol.has-instance","es.function.has-instance"],isConcatSpreadable:["es.symbol.is-concat-spreadable","es.array.concat"],iterator:["es.symbol.iterator"].concat(vV),match:["es.symbol.match","es.string.match"],observable:["esnext.symbol.observable"],patternMatch:["esnext.symbol.pattern-match"],replace:["es.symbol.replace","es.string.replace"],search:["es.symbol.search","es.string.search"],species:["es.symbol.species","es.array.species"],split:["es.symbol.split","es.string.split"],toPrimitive:["es.symbol.to-primitive","es.date.to-primitive"],toStringTag:["es.symbol.to-string-tag","es.object.to-string","es.math.to-string-tag","es.json.to-string-tag"],unscopables:["es.symbol.unscopables"]},ArrayBuffer:{isView:["es.array-buffer.is-view"]},Int8Array:xV,Uint8Array:xV,Uint8ClampedArray:xV,Int16Array:xV,Uint16Array:xV,Int32Array:xV,Uint32Array:xV,Float32Array:xV,Float64Array:xV},_V=new Set(["es.object.to-string","es.object.define-getter","es.object.define-setter","es.object.lookup-getter","es.object.lookup-setter","es.regexp.exec"]),IV=new Set(["global","globalThis","self","window"]),BV="\n When setting `useBuiltIns: 'usage'`, polyfills are automatically imported when needed.\n Please remove the direct import of `core-js` or use `useBuiltIns: 'entry'` instead.",OV=Object.keys(IU).filter((function(e){return!e.startsWith("esnext.")})).reduce((function(e,t){return e[t]=IU[t],e}),{}),NV=_G.reduce((function(e,t){return e[t]=IU[t],e}),Object.assign({},OV));function RV(e,t){var r=t.corejs,n=t.include,a=t.exclude,s=t.polyfillTargets,i=t.proposals,o=t.shippedProposals,u=t.debug,c=bU(i?IU:o?NV:OV,n,a,s,null),l=new Set(hV(r.version));function p(e,t){var r=e.node,n=e.parent,a=e.scope;if(e.isStringLiteral())return r.value;var s=r.name,i=e.isIdentifier();if(i&&!t&&!n.computed)return s;if(!i||a.getBindingIdentifier(s)){var o=e.evaluate().value;if("string"==typeof o)return o}}function d(e){var t,r,n=e.node,a=e.scope;if(n&&(t=n.name,!e.isIdentifier()||a.getBindingIdentifier(t))){var s=e.evaluate(),i=s.deopt,o=s.value;void 0!==o?r=EG(o):i&&i.isIdentifier()&&(t=i.node.name)}return{builtIn:t,instanceType:r,isNamespaced:PG(e)}}return{name:"corejs3-usage",pre:function(){this.injectedPolyfills=new Set,this.polyfillsSet=new Set,this.addUnsupported=function(e){var t=Array.isArray(e)?e:[e],r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}var s=a;this.polyfillsSet.add(s)}},this.addBuiltInDependencies=function(e){if(xG(PV,e)){var t=PV[e];this.addUnsupported(t)}},this.addPropertyDependencies=function(e,t){void 0===e&&(e={});var r=e,n=r.builtIn,a=r.instanceType;if(!r.isNamespaced){if(IV.has(n))this.addBuiltInDependencies(t);else if(xG(FV,n)){var s=FV[n];if(xG(s,t)){var i=s[t];return this.addUnsupported(i)}}if(xG(kV,t)){var o=kV[t];a&&(o=o.filter((function(e){return e.includes(a)||_V.has(e)}))),this.addUnsupported(o)}}}},post:function(){u&&kU(this.injectedPolyfills,this.file.opts.filename,s,IU)},visitor:{ImportDeclaration:function(e){DG(wG(e))&&(console.warn(BV),e.remove())},Program:{enter:function(e){e.get("body").forEach((function(e){DG(SG(e))&&(console.warn(BV),e.remove())}))},exit:function(e){var t=this,r=AG(c,this.polyfillsSet,l),n=Array.from(r).reverse(),a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var o=i;this.injectedPolyfills.has(o)||jG(e,o)}r.forEach((function(e){return t.injectedPolyfills.add(e)}))}},Import:function(){this.addUnsupported(EV)},Function:function(e){e.node.async&&this.addUnsupported(EV)},"ForOfStatement|ArrayPattern":function(){this.addUnsupported(yV)},SpreadElement:function(e){e.parentPath.isObjectExpression()||this.addUnsupported(yV)},YieldExpression:function(e){e.node.delegate&&this.addUnsupported(yV)},ReferencedIdentifier:function(e){var t=e.node.name;e.scope.getBindingIdentifier(t)||this.addBuiltInDependencies(t)},MemberExpression:function(e){var t=d(e.get("object")),r=p(e.get("property"));this.addPropertyDependencies(t,r)},ObjectPattern:function(e){var t,r=e.parentPath,n=e.parent,a=e.key;if(r.isVariableDeclarator())t=d(r.get("init"));else if(r.isAssignmentExpression())t=d(r.get("right"));else if(r.isFunctionExpression()){var s=r.parentPath;(s.isCallExpression()||s.isNewExpression())&&s.node.callee===n&&(t=d(s.get("arguments")[a]))}var i=e.get("properties"),o=Array.isArray(i),u=0;for(i=o?i:i[Symbol.iterator]();;){var c;if(o){if(u>=i.length)break;c=i[u++]}else{if((u=i.next()).done)break;c=u.value}var l=c;if(l.isObjectProperty()){var f=p(l.get("key"));this.addPropertyDependencies(t,f)}}},BinaryExpression:function(e){if("in"===e.node.operator){var t=d(e.get("right")),r=p(e.get("left"),!0);this.addPropertyDependencies(t,r)}}}}}function MV(){return{name:"regenerator-usage",pre:function(){this.usesRegenerator=!1},visitor:{Function:function(e){var t=e.node;this.usesRegenerator||!t.generator&&!t.async||(this.usesRegenerator=!0,jG(e,"regenerator-runtime"))}},post:function(){if(this.opts.debug&&this.usesRegenerator){var e=this.file.opts.filename;"test"===es.env.BABEL_ENV&&(e=e.replace(/\\/g,"/")),console.log("\n["+e+"] Based on your code and targets, added regenerator-runtime.")}}}}function LV(e,t){var r=t.include,n=t.exclude,a=t.polyfillTargets,s=t.regenerator,i=t.debug,o=bU(RU,r,n,a,eG(a));return{name:"corejs2-entry",visitor:{ImportDeclaration:function(e){DG(wG(e))&&this.replaceBySeparateModulesImport(e)},Program:function(e){var t=this;e.get("body").forEach((function(e){DG(SG(e))&&t.replaceBySeparateModulesImport(e)}))}},pre:function(){this.importPolyfillIncluded=!1,this.replaceBySeparateModulesImport=function(e){this.importPolyfillIncluded=!0,s&&jG(e,"regenerator-runtime");var t=Array.from(o).reverse(),r=Array.isArray(t),n=0;for(t=r?t:t[Symbol.iterator]();;){var a;if(r){if(n>=t.length)break;a=t[n++]}else{if((n=t.next()).done)break;a=n.value}jG(e,a)}e.remove()}},post:function(){i&&PU("@babel/polyfill",this.importPolyfillIncluded,o,this.file.opts.filename,a,RU)}}}var UV={"core-js":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.data-view","es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string","es.function.bind","es.function.has-instance","es.function.name","es.global-this","es.json.stringify","es.json.to-string-tag","es.map","es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc","es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values","es.parse-float","es.parse-int","es.promise","es.promise.all-settled","es.promise.finally","es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of","es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.set","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string","es.weak-map","es.weak-set","esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.dom-collections.for-each","web.dom-collections.iterator","web.immediate","web.queue-microtask","web.timers","web.url","web.url.to-json","web.url-search-params"],"core-js/es":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.data-view","es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string","es.function.bind","es.function.has-instance","es.function.name","es.global-this","es.json.stringify","es.json.to-string-tag","es.map","es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc","es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values","es.parse-float","es.parse-int","es.promise","es.promise.all-settled","es.promise.finally","es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of","es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.set","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string","es.weak-map","es.weak-set"],"core-js/es/array":["es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.string.iterator"],"core-js/es/array-buffer":["es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.object.to-string"],"core-js/es/array-buffer/constructor":["es.array-buffer.constructor","es.object.to-string"],"core-js/es/array-buffer/is-view":["es.array-buffer.is-view"],"core-js/es/array-buffer/slice":["es.array-buffer.slice"],"core-js/es/array/concat":["es.array.concat"],"core-js/es/array/copy-within":["es.array.copy-within"],"core-js/es/array/entries":["es.array.iterator"],"core-js/es/array/every":["es.array.every"],"core-js/es/array/fill":["es.array.fill"],"core-js/es/array/filter":["es.array.filter"],"core-js/es/array/find":["es.array.find"],"core-js/es/array/find-index":["es.array.find-index"],"core-js/es/array/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/es/array/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/es/array/for-each":["es.array.for-each"],"core-js/es/array/from":["es.array.from","es.string.iterator"],"core-js/es/array/includes":["es.array.includes"],"core-js/es/array/index-of":["es.array.index-of"],"core-js/es/array/is-array":["es.array.is-array"],"core-js/es/array/iterator":["es.array.iterator"],"core-js/es/array/join":["es.array.join"],"core-js/es/array/keys":["es.array.iterator"],"core-js/es/array/last-index-of":["es.array.last-index-of"],"core-js/es/array/map":["es.array.map"],"core-js/es/array/of":["es.array.of"],"core-js/es/array/reduce":["es.array.reduce"],"core-js/es/array/reduce-right":["es.array.reduce-right"],"core-js/es/array/reverse":["es.array.reverse"],"core-js/es/array/slice":["es.array.slice"],"core-js/es/array/some":["es.array.some"],"core-js/es/array/sort":["es.array.sort"],"core-js/es/array/splice":["es.array.splice"],"core-js/es/array/values":["es.array.iterator"],"core-js/es/array/virtual":["es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.includes","es.array.index-of","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map"],"core-js/es/array/virtual/concat":["es.array.concat"],"core-js/es/array/virtual/copy-within":["es.array.copy-within"],"core-js/es/array/virtual/entries":["es.array.iterator"],"core-js/es/array/virtual/every":["es.array.every"],"core-js/es/array/virtual/fill":["es.array.fill"],"core-js/es/array/virtual/filter":["es.array.filter"],"core-js/es/array/virtual/find":["es.array.find"],"core-js/es/array/virtual/find-index":["es.array.find-index"],"core-js/es/array/virtual/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/es/array/virtual/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/es/array/virtual/for-each":["es.array.for-each"],"core-js/es/array/virtual/includes":["es.array.includes"],"core-js/es/array/virtual/index-of":["es.array.index-of"],"core-js/es/array/virtual/iterator":["es.array.iterator"],"core-js/es/array/virtual/join":["es.array.join"],"core-js/es/array/virtual/keys":["es.array.iterator"],"core-js/es/array/virtual/last-index-of":["es.array.last-index-of"],"core-js/es/array/virtual/map":["es.array.map"],"core-js/es/array/virtual/reduce":["es.array.reduce"],"core-js/es/array/virtual/reduce-right":["es.array.reduce-right"],"core-js/es/array/virtual/reverse":["es.array.reverse"],"core-js/es/array/virtual/slice":["es.array.slice"],"core-js/es/array/virtual/some":["es.array.some"],"core-js/es/array/virtual/sort":["es.array.sort"],"core-js/es/array/virtual/splice":["es.array.splice"],"core-js/es/array/virtual/values":["es.array.iterator"],"core-js/es/data-view":["es.data-view","es.object.to-string"],"core-js/es/date":["es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string"],"core-js/es/date/now":["es.date.now"],"core-js/es/date/to-iso-string":["es.date.to-iso-string","es.date.to-json"],"core-js/es/date/to-json":["es.date.to-json"],"core-js/es/date/to-primitive":["es.date.to-primitive"],"core-js/es/date/to-string":["es.date.to-string"],"core-js/es/function":["es.function.bind","es.function.has-instance","es.function.name"],"core-js/es/function/bind":["es.function.bind"],"core-js/es/function/has-instance":["es.function.has-instance"],"core-js/es/function/name":["es.function.name"],"core-js/es/function/virtual":["es.function.bind"],"core-js/es/function/virtual/bind":["es.function.bind"],"core-js/es/global-this":["es.global-this"],"core-js/es/instance/bind":["es.function.bind"],"core-js/es/instance/code-point-at":["es.string.code-point-at"],"core-js/es/instance/concat":["es.array.concat"],"core-js/es/instance/copy-within":["es.array.copy-within"],"core-js/es/instance/ends-with":["es.string.ends-with"],"core-js/es/instance/entries":["es.array.iterator"],"core-js/es/instance/every":["es.array.every"],"core-js/es/instance/fill":["es.array.fill"],"core-js/es/instance/filter":["es.array.filter"],"core-js/es/instance/find":["es.array.find"],"core-js/es/instance/find-index":["es.array.find-index"],"core-js/es/instance/flags":["es.regexp.flags"],"core-js/es/instance/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/es/instance/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/es/instance/for-each":["es.array.for-each"],"core-js/es/instance/includes":["es.array.includes","es.string.includes"],"core-js/es/instance/index-of":["es.array.index-of"],"core-js/es/instance/keys":["es.array.iterator"],"core-js/es/instance/last-index-of":["es.array.last-index-of"],"core-js/es/instance/map":["es.array.map"],"core-js/es/instance/match-all":["es.string.match-all"],"core-js/es/instance/pad-end":["es.string.pad-end"],"core-js/es/instance/pad-start":["es.string.pad-start"],"core-js/es/instance/reduce":["es.array.reduce"],"core-js/es/instance/reduce-right":["es.array.reduce-right"],"core-js/es/instance/repeat":["es.string.repeat"],"core-js/es/instance/reverse":["es.array.reverse"],"core-js/es/instance/slice":["es.array.slice"],"core-js/es/instance/some":["es.array.some"],"core-js/es/instance/sort":["es.array.sort"],"core-js/es/instance/splice":["es.array.splice"],"core-js/es/instance/starts-with":["es.string.starts-with"],"core-js/es/instance/trim":["es.string.trim"],"core-js/es/instance/trim-end":["es.string.trim-end"],"core-js/es/instance/trim-left":["es.string.trim-start"],"core-js/es/instance/trim-right":["es.string.trim-end"],"core-js/es/instance/trim-start":["es.string.trim-start"],"core-js/es/instance/values":["es.array.iterator"],"core-js/es/json":["es.json.stringify","es.json.to-string-tag"],"core-js/es/json/stringify":["es.json.stringify"],"core-js/es/json/to-string-tag":["es.json.to-string-tag"],"core-js/es/map":["es.map","es.object.to-string","es.string.iterator","web.dom-collections.iterator"],"core-js/es/math":["es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc"],"core-js/es/math/acosh":["es.math.acosh"],"core-js/es/math/asinh":["es.math.asinh"],"core-js/es/math/atanh":["es.math.atanh"],"core-js/es/math/cbrt":["es.math.cbrt"],"core-js/es/math/clz32":["es.math.clz32"],"core-js/es/math/cosh":["es.math.cosh"],"core-js/es/math/expm1":["es.math.expm1"],"core-js/es/math/fround":["es.math.fround"],"core-js/es/math/hypot":["es.math.hypot"],"core-js/es/math/imul":["es.math.imul"],"core-js/es/math/log10":["es.math.log10"],"core-js/es/math/log1p":["es.math.log1p"],"core-js/es/math/log2":["es.math.log2"],"core-js/es/math/sign":["es.math.sign"],"core-js/es/math/sinh":["es.math.sinh"],"core-js/es/math/tanh":["es.math.tanh"],"core-js/es/math/to-string-tag":["es.math.to-string-tag"],"core-js/es/math/trunc":["es.math.trunc"],"core-js/es/number":["es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision"],"core-js/es/number/constructor":["es.number.constructor"],"core-js/es/number/epsilon":["es.number.epsilon"],"core-js/es/number/is-finite":["es.number.is-finite"],"core-js/es/number/is-integer":["es.number.is-integer"],"core-js/es/number/is-nan":["es.number.is-nan"],"core-js/es/number/is-safe-integer":["es.number.is-safe-integer"],"core-js/es/number/max-safe-integer":["es.number.max-safe-integer"],"core-js/es/number/min-safe-integer":["es.number.min-safe-integer"],"core-js/es/number/parse-float":["es.number.parse-float"],"core-js/es/number/parse-int":["es.number.parse-int"],"core-js/es/number/to-fixed":["es.number.to-fixed"],"core-js/es/number/to-precision":["es.number.to-precision"],"core-js/es/number/virtual":["es.number.to-fixed","es.number.to-precision"],"core-js/es/number/virtual/to-fixed":["es.number.to-fixed"],"core-js/es/number/virtual/to-precision":["es.number.to-precision"],"core-js/es/object":["es.symbol","es.json.to-string-tag","es.math.to-string-tag","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values"],"core-js/es/object/assign":["es.object.assign"],"core-js/es/object/create":["es.object.create"],"core-js/es/object/define-getter":["es.object.define-getter"],"core-js/es/object/define-properties":["es.object.define-properties"],"core-js/es/object/define-property":["es.object.define-property"],"core-js/es/object/define-setter":["es.object.define-setter"],"core-js/es/object/entries":["es.object.entries"],"core-js/es/object/freeze":["es.object.freeze"],"core-js/es/object/from-entries":["es.array.iterator","es.object.from-entries"],"core-js/es/object/get-own-property-descriptor":["es.object.get-own-property-descriptor"],"core-js/es/object/get-own-property-descriptors":["es.object.get-own-property-descriptors"],"core-js/es/object/get-own-property-names":["es.object.get-own-property-names"],"core-js/es/object/get-own-property-symbols":["es.symbol"],"core-js/es/object/get-prototype-of":["es.object.get-prototype-of"],"core-js/es/object/is":["es.object.is"],"core-js/es/object/is-extensible":["es.object.is-extensible"],"core-js/es/object/is-frozen":["es.object.is-frozen"],"core-js/es/object/is-sealed":["es.object.is-sealed"],"core-js/es/object/keys":["es.object.keys"],"core-js/es/object/lookup-getter":["es.object.lookup-setter"],"core-js/es/object/lookup-setter":["es.object.lookup-setter"],"core-js/es/object/prevent-extensions":["es.object.prevent-extensions"],"core-js/es/object/seal":["es.object.seal"],"core-js/es/object/set-prototype-of":["es.object.set-prototype-of"],"core-js/es/object/to-string":["es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/es/object/values":["es.object.values"],"core-js/es/parse-float":["es.parse-float"],"core-js/es/parse-int":["es.parse-int"],"core-js/es/promise":["es.object.to-string","es.promise","es.promise.all-settled","es.promise.finally","es.string.iterator","web.dom-collections.iterator"],"core-js/es/promise/all-settled":["es.promise","es.promise.all-settled"],"core-js/es/promise/finally":["es.promise","es.promise.finally"],"core-js/es/reflect":["es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of"],"core-js/es/reflect/apply":["es.reflect.apply"],"core-js/es/reflect/construct":["es.reflect.construct"],"core-js/es/reflect/define-property":["es.reflect.define-property"],"core-js/es/reflect/delete-property":["es.reflect.delete-property"],"core-js/es/reflect/get":["es.reflect.get"],"core-js/es/reflect/get-own-property-descriptor":["es.reflect.get-own-property-descriptor"],"core-js/es/reflect/get-prototype-of":["es.reflect.get-prototype-of"],"core-js/es/reflect/has":["es.reflect.has"],"core-js/es/reflect/is-extensible":["es.reflect.is-extensible"],"core-js/es/reflect/own-keys":["es.reflect.own-keys"],"core-js/es/reflect/prevent-extensions":["es.reflect.prevent-extensions"],"core-js/es/reflect/set":["es.reflect.set"],"core-js/es/reflect/set-prototype-of":["es.reflect.set-prototype-of"],"core-js/es/regexp":["es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.string.match","es.string.replace","es.string.search","es.string.split"],"core-js/es/regexp/constructor":["es.regexp.constructor"],"core-js/es/regexp/flags":["es.regexp.flags"],"core-js/es/regexp/match":["es.string.match"],"core-js/es/regexp/replace":["es.string.replace"],"core-js/es/regexp/search":["es.string.search"],"core-js/es/regexp/split":["es.string.split"],"core-js/es/regexp/sticky":["es.regexp.sticky"],"core-js/es/regexp/test":["es.regexp.exec","es.regexp.test"],"core-js/es/regexp/to-string":["es.regexp.to-string"],"core-js/es/set":["es.object.to-string","es.set","es.string.iterator","web.dom-collections.iterator"],"core-js/es/string":["es.regexp.exec","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup"],"core-js/es/string/anchor":["es.string.anchor"],"core-js/es/string/big":["es.string.big"],"core-js/es/string/blink":["es.string.blink"],"core-js/es/string/bold":["es.string.bold"],"core-js/es/string/code-point-at":["es.string.code-point-at"],"core-js/es/string/ends-with":["es.string.ends-with"],"core-js/es/string/fixed":["es.string.fixed"],"core-js/es/string/fontcolor":["es.string.fontcolor"],"core-js/es/string/fontsize":["es.string.fontsize"],"core-js/es/string/from-code-point":["es.string.from-code-point"],"core-js/es/string/includes":["es.string.includes"],"core-js/es/string/italics":["es.string.italics"],"core-js/es/string/iterator":["es.string.iterator"],"core-js/es/string/link":["es.string.link"],"core-js/es/string/match":["es.regexp.exec","es.string.match"],"core-js/es/string/match-all":["es.string.match-all"],"core-js/es/string/pad-end":["es.string.pad-end"],"core-js/es/string/pad-start":["es.string.pad-start"],"core-js/es/string/raw":["es.string.raw"],"core-js/es/string/repeat":["es.string.repeat"],"core-js/es/string/replace":["es.regexp.exec","es.string.replace"],"core-js/es/string/search":["es.regexp.exec","es.string.search"],"core-js/es/string/small":["es.string.small"],"core-js/es/string/split":["es.regexp.exec","es.string.split"],"core-js/es/string/starts-with":["es.string.starts-with"],"core-js/es/string/strike":["es.string.strike"],"core-js/es/string/sub":["es.string.sub"],"core-js/es/string/sup":["es.string.sup"],"core-js/es/string/trim":["es.string.trim"],"core-js/es/string/trim-end":["es.string.trim-end"],"core-js/es/string/trim-left":["es.string.trim-start"],"core-js/es/string/trim-right":["es.string.trim-end"],"core-js/es/string/trim-start":["es.string.trim-start"],"core-js/es/string/virtual":["es.string.code-point-at","es.string.ends-with","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup"],"core-js/es/string/virtual/anchor":["es.string.anchor"],"core-js/es/string/virtual/big":["es.string.big"],"core-js/es/string/virtual/blink":["es.string.blink"],"core-js/es/string/virtual/bold":["es.string.bold"],"core-js/es/string/virtual/code-point-at":["es.string.code-point-at"],"core-js/es/string/virtual/ends-with":["es.string.ends-with"],"core-js/es/string/virtual/fixed":["es.string.fixed"],"core-js/es/string/virtual/fontcolor":["es.string.fontcolor"],"core-js/es/string/virtual/fontsize":["es.string.fontsize"],"core-js/es/string/virtual/includes":["es.string.includes"],"core-js/es/string/virtual/italics":["es.string.italics"],"core-js/es/string/virtual/iterator":["es.string.iterator"],"core-js/es/string/virtual/link":["es.string.link"],"core-js/es/string/virtual/match-all":["es.string.match-all"],"core-js/es/string/virtual/pad-end":["es.string.pad-end"],"core-js/es/string/virtual/pad-start":["es.string.pad-start"],"core-js/es/string/virtual/repeat":["es.string.repeat"],"core-js/es/string/virtual/small":["es.string.small"],"core-js/es/string/virtual/starts-with":["es.string.starts-with"],"core-js/es/string/virtual/strike":["es.string.strike"],"core-js/es/string/virtual/sub":["es.string.sub"],"core-js/es/string/virtual/sup":["es.string.sup"],"core-js/es/string/virtual/trim":["es.string.trim"],"core-js/es/string/virtual/trim-end":["es.string.trim-end"],"core-js/es/string/virtual/trim-left":["es.string.trim-start"],"core-js/es/string/virtual/trim-right":["es.string.trim-end"],"core-js/es/string/virtual/trim-start":["es.string.trim-start"],"core-js/es/symbol":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/es/symbol/async-iterator":["es.symbol.async-iterator"],"core-js/es/symbol/description":["es.symbol.description"],"core-js/es/symbol/for":["es.symbol"],"core-js/es/symbol/has-instance":["es.symbol.has-instance","es.function.has-instance"],"core-js/es/symbol/is-concat-spreadable":["es.symbol.is-concat-spreadable","es.array.concat"],"core-js/es/symbol/iterator":["es.symbol.iterator","es.string.iterator","web.dom-collections.iterator"],"core-js/es/symbol/key-for":["es.symbol"],"core-js/es/symbol/match":["es.symbol.match","es.string.match"],"core-js/es/symbol/match-all":["es.symbol.match-all","es.string.match-all"],"core-js/es/symbol/replace":["es.symbol.replace","es.string.replace"],"core-js/es/symbol/search":["es.symbol.search","es.string.search"],"core-js/es/symbol/species":["es.symbol.species"],"core-js/es/symbol/split":["es.symbol.split","es.string.split"],"core-js/es/symbol/to-primitive":["es.symbol.to-primitive"],"core-js/es/symbol/to-string-tag":["es.symbol.to-string-tag","es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/es/symbol/unscopables":["es.symbol.unscopables"],"core-js/es/typed-array":["es.object.to-string","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/copy-within":["es.typed-array.copy-within"],"core-js/es/typed-array/entries":["es.typed-array.iterator"],"core-js/es/typed-array/every":["es.typed-array.every"],"core-js/es/typed-array/fill":["es.typed-array.fill"],"core-js/es/typed-array/filter":["es.typed-array.filter"],"core-js/es/typed-array/find":["es.typed-array.find"],"core-js/es/typed-array/find-index":["es.typed-array.find-index"],"core-js/es/typed-array/float32-array":["es.object.to-string","es.typed-array.float32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/float64-array":["es.object.to-string","es.typed-array.float64-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/for-each":["es.typed-array.for-each"],"core-js/es/typed-array/from":["es.typed-array.from"],"core-js/es/typed-array/includes":["es.typed-array.includes"],"core-js/es/typed-array/index-of":["es.typed-array.index-of"],"core-js/es/typed-array/int16-array":["es.object.to-string","es.typed-array.int16-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/int32-array":["es.object.to-string","es.typed-array.int32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/int8-array":["es.object.to-string","es.typed-array.int8-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/iterator":["es.typed-array.iterator"],"core-js/es/typed-array/join":["es.typed-array.join"],"core-js/es/typed-array/keys":["es.typed-array.iterator"],"core-js/es/typed-array/last-index-of":["es.typed-array.last-index-of"],"core-js/es/typed-array/map":["es.typed-array.map"],"core-js/es/typed-array/methods":["es.object.to-string","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/of":["es.typed-array.of"],"core-js/es/typed-array/reduce":["es.typed-array.reduce"],"core-js/es/typed-array/reduce-right":["es.typed-array.reduce-right"],"core-js/es/typed-array/reverse":["es.typed-array.reverse"],"core-js/es/typed-array/set":["es.typed-array.set"],"core-js/es/typed-array/slice":["es.typed-array.slice"],"core-js/es/typed-array/some":["es.typed-array.some"],"core-js/es/typed-array/sort":["es.typed-array.sort"],"core-js/es/typed-array/subarray":["es.typed-array.subarray"],"core-js/es/typed-array/to-locale-string":["es.typed-array.to-locale-string"],"core-js/es/typed-array/to-string":["es.typed-array.to-string"],"core-js/es/typed-array/uint16-array":["es.object.to-string","es.typed-array.uint16-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/uint32-array":["es.object.to-string","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/uint8-array":["es.object.to-string","es.typed-array.uint8-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/uint8-clamped-array":["es.object.to-string","es.typed-array.uint8-clamped-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/es/typed-array/values":["es.typed-array.iterator"],"core-js/es/weak-map":["es.object.to-string","es.weak-map","web.dom-collections.iterator"],"core-js/es/weak-set":["es.object.to-string","es.weak-set","web.dom-collections.iterator"],"core-js/features":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.data-view","es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string","es.function.bind","es.function.has-instance","es.function.name","es.global-this","es.json.stringify","es.json.to-string-tag","es.map","es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc","es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values","es.parse-float","es.parse-int","es.promise","es.promise.all-settled","es.promise.finally","es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of","es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.set","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string","es.weak-map","es.weak-set","esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.dom-collections.for-each","web.dom-collections.iterator","web.immediate","web.queue-microtask","web.timers","web.url","web.url.to-json","web.url-search-params"],"core-js/features/aggregate-error":["es.string.iterator","esnext.aggregate-error","web.dom-collections.iterator"],"core-js/features/array":["es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.string.iterator","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item"],"core-js/features/array-buffer":["es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.object.to-string"],"core-js/features/array-buffer/constructor":["es.array-buffer.constructor","es.object.to-string"],"core-js/features/array-buffer/is-view":["es.array-buffer.is-view"],"core-js/features/array-buffer/slice":["es.array-buffer.slice"],"core-js/features/array/concat":["es.array.concat"],"core-js/features/array/copy-within":["es.array.copy-within"],"core-js/features/array/entries":["es.array.iterator"],"core-js/features/array/every":["es.array.every"],"core-js/features/array/fill":["es.array.fill"],"core-js/features/array/filter":["es.array.filter"],"core-js/features/array/find":["es.array.find"],"core-js/features/array/find-index":["es.array.find-index"],"core-js/features/array/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/features/array/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/features/array/for-each":["es.array.for-each"],"core-js/features/array/from":["es.array.from","es.string.iterator"],"core-js/features/array/includes":["es.array.includes"],"core-js/features/array/index-of":["es.array.index-of"],"core-js/features/array/is-array":["es.array.is-array"],"core-js/features/array/is-template-object":["esnext.array.is-template-object"],"core-js/features/array/iterator":["es.array.iterator"],"core-js/features/array/join":["es.array.join"],"core-js/features/array/keys":["es.array.iterator"],"core-js/features/array/last-index":["esnext.array.last-index"],"core-js/features/array/last-index-of":["es.array.last-index-of"],"core-js/features/array/last-item":["esnext.array.last-item"],"core-js/features/array/map":["es.array.map"],"core-js/features/array/of":["es.array.of"],"core-js/features/array/reduce":["es.array.reduce"],"core-js/features/array/reduce-right":["es.array.reduce-right"],"core-js/features/array/reverse":["es.array.reverse"],"core-js/features/array/slice":["es.array.slice"],"core-js/features/array/some":["es.array.some"],"core-js/features/array/sort":["es.array.sort"],"core-js/features/array/splice":["es.array.splice"],"core-js/features/array/values":["es.array.iterator"],"core-js/features/array/virtual":["es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.includes","es.array.index-of","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map"],"core-js/features/array/virtual/concat":["es.array.concat"],"core-js/features/array/virtual/copy-within":["es.array.copy-within"],"core-js/features/array/virtual/entries":["es.array.iterator"],"core-js/features/array/virtual/every":["es.array.every"],"core-js/features/array/virtual/fill":["es.array.fill"],"core-js/features/array/virtual/filter":["es.array.filter"],"core-js/features/array/virtual/find":["es.array.find"],"core-js/features/array/virtual/find-index":["es.array.find-index"],"core-js/features/array/virtual/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/features/array/virtual/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/features/array/virtual/for-each":["es.array.for-each"],"core-js/features/array/virtual/includes":["es.array.includes"],"core-js/features/array/virtual/index-of":["es.array.index-of"],"core-js/features/array/virtual/iterator":["es.array.iterator"],"core-js/features/array/virtual/join":["es.array.join"],"core-js/features/array/virtual/keys":["es.array.iterator"],"core-js/features/array/virtual/last-index-of":["es.array.last-index-of"],"core-js/features/array/virtual/map":["es.array.map"],"core-js/features/array/virtual/reduce":["es.array.reduce"],"core-js/features/array/virtual/reduce-right":["es.array.reduce-right"],"core-js/features/array/virtual/reverse":["es.array.reverse"],"core-js/features/array/virtual/slice":["es.array.slice"],"core-js/features/array/virtual/some":["es.array.some"],"core-js/features/array/virtual/sort":["es.array.sort"],"core-js/features/array/virtual/splice":["es.array.splice"],"core-js/features/array/virtual/values":["es.array.iterator"],"core-js/features/async-iterator":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","web.dom-collections.iterator"],"core-js/features/async-iterator/as-indexed-pairs":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","web.dom-collections.iterator"],"core-js/features/async-iterator/drop":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.drop","web.dom-collections.iterator"],"core-js/features/async-iterator/every":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.every","web.dom-collections.iterator"],"core-js/features/async-iterator/filter":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.filter","web.dom-collections.iterator"],"core-js/features/async-iterator/find":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.find","web.dom-collections.iterator"],"core-js/features/async-iterator/flat-map":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.flat-map","web.dom-collections.iterator"],"core-js/features/async-iterator/for-each":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.for-each","web.dom-collections.iterator"],"core-js/features/async-iterator/from":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.from","web.dom-collections.iterator"],"core-js/features/async-iterator/map":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.map","web.dom-collections.iterator"],"core-js/features/async-iterator/reduce":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.reduce","web.dom-collections.iterator"],"core-js/features/async-iterator/some":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.some","web.dom-collections.iterator"],"core-js/features/async-iterator/take":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.take","web.dom-collections.iterator"],"core-js/features/async-iterator/to-array":["es.object.to-string","es.promise","es.string.iterator","esnext.async-iterator.constructor","esnext.async-iterator.to-array","web.dom-collections.iterator"],"core-js/features/clear-immediate":["web.immediate"],"core-js/features/composite-key":["esnext.composite-key"],"core-js/features/composite-symbol":["es.symbol","esnext.composite-symbol"],"core-js/features/data-view":["es.data-view","es.object.to-string"],"core-js/features/date":["es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string"],"core-js/features/date/now":["es.date.now"],"core-js/features/date/to-iso-string":["es.date.to-iso-string","es.date.to-json"],"core-js/features/date/to-json":["es.date.to-json"],"core-js/features/date/to-primitive":["es.date.to-primitive"],"core-js/features/date/to-string":["es.date.to-string"],"core-js/features/dom-collections":["es.array.iterator","web.dom-collections.for-each","web.dom-collections.iterator"],"core-js/features/dom-collections/for-each":["web.dom-collections.for-each"],"core-js/features/dom-collections/iterator":["web.dom-collections.iterator"],"core-js/features/function":["es.function.bind","es.function.has-instance","es.function.name"],"core-js/features/function/bind":["es.function.bind"],"core-js/features/function/has-instance":["es.function.has-instance"],"core-js/features/function/name":["es.function.name"],"core-js/features/function/virtual":["es.function.bind"],"core-js/features/function/virtual/bind":["es.function.bind"],"core-js/features/get-iterator":["es.string.iterator","web.dom-collections.iterator"],"core-js/features/get-iterator-method":["es.string.iterator","web.dom-collections.iterator"],"core-js/features/global-this":["es.global-this","esnext.global-this"],"core-js/features/instance/at":["esnext.string.at"],"core-js/features/instance/bind":["es.function.bind"],"core-js/features/instance/code-point-at":["es.string.code-point-at"],"core-js/features/instance/code-points":["esnext.string.code-points"],"core-js/features/instance/concat":["es.array.concat"],"core-js/features/instance/copy-within":["es.array.copy-within"],"core-js/features/instance/ends-with":["es.string.ends-with"],"core-js/features/instance/entries":["es.array.iterator","web.dom-collections.iterator"],"core-js/features/instance/every":["es.array.every"],"core-js/features/instance/fill":["es.array.fill"],"core-js/features/instance/filter":["es.array.filter"],"core-js/features/instance/find":["es.array.find"],"core-js/features/instance/find-index":["es.array.find-index"],"core-js/features/instance/flags":["es.regexp.flags"],"core-js/features/instance/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/features/instance/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/features/instance/for-each":["es.array.for-each","web.dom-collections.iterator"],"core-js/features/instance/includes":["es.array.includes","es.string.includes"],"core-js/features/instance/index-of":["es.array.index-of"],"core-js/features/instance/keys":["es.array.iterator","web.dom-collections.iterator"],"core-js/features/instance/last-index-of":["es.array.last-index-of"],"core-js/features/instance/map":["es.array.map"],"core-js/features/instance/match-all":["es.string.match-all","esnext.string.match-all"],"core-js/features/instance/pad-end":["es.string.pad-end"],"core-js/features/instance/pad-start":["es.string.pad-start"],"core-js/features/instance/reduce":["es.array.reduce"],"core-js/features/instance/reduce-right":["es.array.reduce-right"],"core-js/features/instance/repeat":["es.string.repeat"],"core-js/features/instance/replace-all":["esnext.string.replace-all"],"core-js/features/instance/reverse":["es.array.reverse"],"core-js/features/instance/slice":["es.array.slice"],"core-js/features/instance/some":["es.array.some"],"core-js/features/instance/sort":["es.array.sort"],"core-js/features/instance/splice":["es.array.splice"],"core-js/features/instance/starts-with":["es.string.starts-with"],"core-js/features/instance/trim":["es.string.trim"],"core-js/features/instance/trim-end":["es.string.trim-end"],"core-js/features/instance/trim-left":["es.string.trim-start"],"core-js/features/instance/trim-right":["es.string.trim-end"],"core-js/features/instance/trim-start":["es.string.trim-start"],"core-js/features/instance/values":["es.array.iterator","web.dom-collections.iterator"],"core-js/features/is-iterable":["es.string.iterator","web.dom-collections.iterator"],"core-js/features/iterator":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","web.dom-collections.iterator"],"core-js/features/iterator/as-indexed-pairs":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","web.dom-collections.iterator"],"core-js/features/iterator/drop":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.drop","web.dom-collections.iterator"],"core-js/features/iterator/every":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.every","web.dom-collections.iterator"],"core-js/features/iterator/filter":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.filter","web.dom-collections.iterator"],"core-js/features/iterator/find":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.find","web.dom-collections.iterator"],"core-js/features/iterator/flat-map":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.flat-map","web.dom-collections.iterator"],"core-js/features/iterator/for-each":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.for-each","web.dom-collections.iterator"],"core-js/features/iterator/from":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.from","web.dom-collections.iterator"],"core-js/features/iterator/map":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.map","web.dom-collections.iterator"],"core-js/features/iterator/reduce":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.reduce","web.dom-collections.iterator"],"core-js/features/iterator/some":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.some","web.dom-collections.iterator"],"core-js/features/iterator/take":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.take","web.dom-collections.iterator"],"core-js/features/iterator/to-array":["es.object.to-string","es.string.iterator","esnext.iterator.constructor","esnext.iterator.to-array","web.dom-collections.iterator"],"core-js/features/json":["es.json.stringify","es.json.to-string-tag"],"core-js/features/json/stringify":["es.json.stringify"],"core-js/features/json/to-string-tag":["es.json.to-string-tag"],"core-js/features/map":["es.map","es.object.to-string","es.string.iterator","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","web.dom-collections.iterator"],"core-js/features/map/delete-all":["es.map","esnext.map.delete-all"],"core-js/features/map/every":["es.map","esnext.map.every"],"core-js/features/map/filter":["es.map","esnext.map.filter"],"core-js/features/map/find":["es.map","esnext.map.find"],"core-js/features/map/find-key":["es.map","esnext.map.find-key"],"core-js/features/map/from":["es.map","es.string.iterator","esnext.map.from","web.dom-collections.iterator"],"core-js/features/map/group-by":["es.map","esnext.map.group-by"],"core-js/features/map/includes":["es.map","esnext.map.includes"],"core-js/features/map/key-by":["es.map","esnext.map.key-by"],"core-js/features/map/key-of":["es.map","esnext.map.key-of"],"core-js/features/map/map-keys":["es.map","esnext.map.map-keys"],"core-js/features/map/map-values":["es.map","esnext.map.map-values"],"core-js/features/map/merge":["es.map","esnext.map.merge"],"core-js/features/map/of":["es.map","es.string.iterator","esnext.map.of","web.dom-collections.iterator"],"core-js/features/map/reduce":["es.map","esnext.map.reduce"],"core-js/features/map/some":["es.map","esnext.map.some"],"core-js/features/map/update":["es.map","esnext.map.update"],"core-js/features/map/update-or-insert":["es.map","esnext.map.update-or-insert"],"core-js/features/map/upsert":["es.map","esnext.map.upsert"],"core-js/features/math":["es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh"],"core-js/features/math/acosh":["es.math.acosh"],"core-js/features/math/asinh":["es.math.asinh"],"core-js/features/math/atanh":["es.math.atanh"],"core-js/features/math/cbrt":["es.math.cbrt"],"core-js/features/math/clamp":["esnext.math.clamp"],"core-js/features/math/clz32":["es.math.clz32"],"core-js/features/math/cosh":["es.math.cosh"],"core-js/features/math/deg-per-rad":["esnext.math.deg-per-rad"],"core-js/features/math/degrees":["esnext.math.degrees"],"core-js/features/math/expm1":["es.math.expm1"],"core-js/features/math/fround":["es.math.fround"],"core-js/features/math/fscale":["esnext.math.fscale"],"core-js/features/math/hypot":["es.math.hypot"],"core-js/features/math/iaddh":["esnext.math.iaddh"],"core-js/features/math/imul":["es.math.imul"],"core-js/features/math/imulh":["esnext.math.imulh"],"core-js/features/math/isubh":["esnext.math.isubh"],"core-js/features/math/log10":["es.math.log10"],"core-js/features/math/log1p":["es.math.log1p"],"core-js/features/math/log2":["es.math.log2"],"core-js/features/math/rad-per-deg":["esnext.math.rad-per-deg"],"core-js/features/math/radians":["esnext.math.radians"],"core-js/features/math/scale":["esnext.math.scale"],"core-js/features/math/seeded-prng":["esnext.math.seeded-prng"],"core-js/features/math/sign":["es.math.sign"],"core-js/features/math/signbit":["esnext.math.signbit"],"core-js/features/math/sinh":["es.math.sinh"],"core-js/features/math/tanh":["es.math.tanh"],"core-js/features/math/to-string-tag":["es.math.to-string-tag"],"core-js/features/math/trunc":["es.math.trunc"],"core-js/features/math/umulh":["esnext.math.umulh"],"core-js/features/number":["es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision","esnext.number.from-string"],"core-js/features/number/constructor":["es.number.constructor"],"core-js/features/number/epsilon":["es.number.epsilon"],"core-js/features/number/from-string":["esnext.number.from-string"],"core-js/features/number/is-finite":["es.number.is-finite"],"core-js/features/number/is-integer":["es.number.is-integer"],"core-js/features/number/is-nan":["es.number.is-nan"],"core-js/features/number/is-safe-integer":["es.number.is-safe-integer"],"core-js/features/number/max-safe-integer":["es.number.max-safe-integer"],"core-js/features/number/min-safe-integer":["es.number.min-safe-integer"],"core-js/features/number/parse-float":["es.number.parse-float"],"core-js/features/number/parse-int":["es.number.parse-int"],"core-js/features/number/to-fixed":["es.number.to-fixed"],"core-js/features/number/to-precision":["es.number.to-precision"],"core-js/features/number/virtual":["es.number.to-fixed","es.number.to-precision"],"core-js/features/number/virtual/to-fixed":["es.number.to-fixed"],"core-js/features/number/virtual/to-precision":["es.number.to-precision"],"core-js/features/object":["es.symbol","es.json.to-string-tag","es.math.to-string-tag","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values"],"core-js/features/object/assign":["es.object.assign"],"core-js/features/object/create":["es.object.create"],"core-js/features/object/define-getter":["es.object.define-getter"],"core-js/features/object/define-properties":["es.object.define-properties"],"core-js/features/object/define-property":["es.object.define-property"],"core-js/features/object/define-setter":["es.object.define-setter"],"core-js/features/object/entries":["es.object.entries"],"core-js/features/object/freeze":["es.object.freeze"],"core-js/features/object/from-entries":["es.array.iterator","es.object.from-entries"],"core-js/features/object/get-own-property-descriptor":["es.object.get-own-property-descriptor"],"core-js/features/object/get-own-property-descriptors":["es.object.get-own-property-descriptors"],"core-js/features/object/get-own-property-names":["es.object.get-own-property-names"],"core-js/features/object/get-own-property-symbols":["es.symbol"],"core-js/features/object/get-prototype-of":["es.object.get-prototype-of"],"core-js/features/object/is":["es.object.is"],"core-js/features/object/is-extensible":["es.object.is-extensible"],"core-js/features/object/is-frozen":["es.object.is-frozen"],"core-js/features/object/is-sealed":["es.object.is-sealed"],"core-js/features/object/iterate-entries":["esnext.object.iterate-entries"],"core-js/features/object/iterate-keys":["esnext.object.iterate-keys"],"core-js/features/object/iterate-values":["esnext.object.iterate-values"],"core-js/features/object/keys":["es.object.keys"],"core-js/features/object/lookup-getter":["es.object.lookup-setter"],"core-js/features/object/lookup-setter":["es.object.lookup-setter"],"core-js/features/object/prevent-extensions":["es.object.prevent-extensions"],"core-js/features/object/seal":["es.object.seal"],"core-js/features/object/set-prototype-of":["es.object.set-prototype-of"],"core-js/features/object/to-string":["es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/features/object/values":["es.object.values"],"core-js/features/observable":["es.object.to-string","es.string.iterator","esnext.observable","esnext.symbol.observable","web.dom-collections.iterator"],"core-js/features/parse-float":["es.parse-float"],"core-js/features/parse-int":["es.parse-int"],"core-js/features/promise":["es.object.to-string","es.promise","es.promise.all-settled","es.promise.finally","es.string.iterator","esnext.aggregate-error","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","web.dom-collections.iterator"],"core-js/features/promise/all-settled":["es.promise","es.promise.all-settled","esnext.promise.all-settled"],"core-js/features/promise/any":["es.promise","esnext.aggregate-error","esnext.promise.any"],"core-js/features/promise/finally":["es.promise","es.promise.finally"],"core-js/features/promise/try":["es.promise","esnext.promise.try"],"core-js/features/queue-microtask":["web.queue-microtask"],"core-js/features/reflect":["es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata"],"core-js/features/reflect/apply":["es.reflect.apply"],"core-js/features/reflect/construct":["es.reflect.construct"],"core-js/features/reflect/define-metadata":["esnext.reflect.define-metadata"],"core-js/features/reflect/define-property":["es.reflect.define-property"],"core-js/features/reflect/delete-metadata":["esnext.reflect.delete-metadata"],"core-js/features/reflect/delete-property":["es.reflect.delete-property"],"core-js/features/reflect/get":["es.reflect.get"],"core-js/features/reflect/get-metadata":["esnext.reflect.get-metadata"],"core-js/features/reflect/get-metadata-keys":["esnext.reflect.get-metadata-keys"],"core-js/features/reflect/get-own-metadata":["esnext.reflect.get-own-metadata"],"core-js/features/reflect/get-own-metadata-keys":["esnext.reflect.get-own-metadata-keys"],"core-js/features/reflect/get-own-property-descriptor":["es.reflect.get-own-property-descriptor"],"core-js/features/reflect/get-prototype-of":["es.reflect.get-prototype-of"],"core-js/features/reflect/has":["es.reflect.has"],"core-js/features/reflect/has-metadata":["esnext.reflect.has-metadata"],"core-js/features/reflect/has-own-metadata":["esnext.reflect.has-own-metadata"],"core-js/features/reflect/is-extensible":["es.reflect.is-extensible"],"core-js/features/reflect/metadata":["esnext.reflect.metadata"],"core-js/features/reflect/own-keys":["es.reflect.own-keys"],"core-js/features/reflect/prevent-extensions":["es.reflect.prevent-extensions"],"core-js/features/reflect/set":["es.reflect.set"],"core-js/features/reflect/set-prototype-of":["es.reflect.set-prototype-of"],"core-js/features/regexp":["es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.string.match","es.string.replace","es.string.search","es.string.split"],"core-js/features/regexp/constructor":["es.regexp.constructor"],"core-js/features/regexp/flags":["es.regexp.flags"],"core-js/features/regexp/match":["es.string.match"],"core-js/features/regexp/replace":["es.string.replace"],"core-js/features/regexp/search":["es.string.search"],"core-js/features/regexp/split":["es.string.split"],"core-js/features/regexp/sticky":["es.regexp.sticky"],"core-js/features/regexp/test":["es.regexp.exec","es.regexp.test"],"core-js/features/regexp/to-string":["es.regexp.to-string"],"core-js/features/set":["es.object.to-string","es.set","es.string.iterator","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","web.dom-collections.iterator"],"core-js/features/set-immediate":["web.immediate"],"core-js/features/set-interval":["web.timers"],"core-js/features/set-timeout":["web.timers"],"core-js/features/set/add-all":["es.set","esnext.set.add-all"],"core-js/features/set/delete-all":["es.set","esnext.set.delete-all"],"core-js/features/set/difference":["es.set","es.string.iterator","esnext.set.difference","web.dom-collections.iterator"],"core-js/features/set/every":["es.set","esnext.set.every"],"core-js/features/set/filter":["es.set","esnext.set.filter"],"core-js/features/set/find":["es.set","esnext.set.find"],"core-js/features/set/from":["es.set","es.string.iterator","esnext.set.from","web.dom-collections.iterator"],"core-js/features/set/intersection":["es.set","esnext.set.intersection"],"core-js/features/set/is-disjoint-from":["es.set","esnext.set.is-disjoint-from"],"core-js/features/set/is-subset-of":["es.set","es.string.iterator","esnext.set.is-subset-of","web.dom-collections.iterator"],"core-js/features/set/is-superset-of":["es.set","esnext.set.is-superset-of"],"core-js/features/set/join":["es.set","esnext.set.join"],"core-js/features/set/map":["es.set","esnext.set.map"],"core-js/features/set/of":["es.set","es.string.iterator","esnext.set.of","web.dom-collections.iterator"],"core-js/features/set/reduce":["es.set","esnext.set.reduce"],"core-js/features/set/some":["es.set","esnext.set.some"],"core-js/features/set/symmetric-difference":["es.set","es.string.iterator","esnext.set.symmetric-difference","web.dom-collections.iterator"],"core-js/features/set/union":["es.set","es.string.iterator","esnext.set.union","web.dom-collections.iterator"],"core-js/features/string":["es.regexp.exec","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all"],"core-js/features/string/anchor":["es.string.anchor"],"core-js/features/string/at":["esnext.string.at"],"core-js/features/string/big":["es.string.big"],"core-js/features/string/blink":["es.string.blink"],"core-js/features/string/bold":["es.string.bold"],"core-js/features/string/code-point-at":["es.string.code-point-at"],"core-js/features/string/code-points":["esnext.string.code-points"],"core-js/features/string/ends-with":["es.string.ends-with"],"core-js/features/string/fixed":["es.string.fixed"],"core-js/features/string/fontcolor":["es.string.fontcolor"],"core-js/features/string/fontsize":["es.string.fontsize"],"core-js/features/string/from-code-point":["es.string.from-code-point"],"core-js/features/string/includes":["es.string.includes"],"core-js/features/string/italics":["es.string.italics"],"core-js/features/string/iterator":["es.string.iterator"],"core-js/features/string/link":["es.string.link"],"core-js/features/string/match":["es.regexp.exec","es.string.match"],"core-js/features/string/match-all":["es.string.match-all","esnext.string.match-all"],"core-js/features/string/pad-end":["es.string.pad-end"],"core-js/features/string/pad-start":["es.string.pad-start"],"core-js/features/string/raw":["es.string.raw"],"core-js/features/string/repeat":["es.string.repeat"],"core-js/features/string/replace":["es.regexp.exec","es.string.replace"],"core-js/features/string/replace-all":["esnext.string.replace-all"],"core-js/features/string/search":["es.regexp.exec","es.string.search"],"core-js/features/string/small":["es.string.small"],"core-js/features/string/split":["es.regexp.exec","es.string.split"],"core-js/features/string/starts-with":["es.string.starts-with"],"core-js/features/string/strike":["es.string.strike"],"core-js/features/string/sub":["es.string.sub"],"core-js/features/string/sup":["es.string.sup"],"core-js/features/string/trim":["es.string.trim"],"core-js/features/string/trim-end":["es.string.trim-end"],"core-js/features/string/trim-left":["es.string.trim-start"],"core-js/features/string/trim-right":["es.string.trim-end"],"core-js/features/string/trim-start":["es.string.trim-start"],"core-js/features/string/virtual":["es.string.code-point-at","es.string.ends-with","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all"],"core-js/features/string/virtual/anchor":["es.string.anchor"],"core-js/features/string/virtual/at":["esnext.string.at"],"core-js/features/string/virtual/big":["es.string.big"],"core-js/features/string/virtual/blink":["es.string.blink"],"core-js/features/string/virtual/bold":["es.string.bold"],"core-js/features/string/virtual/code-point-at":["es.string.code-point-at"],"core-js/features/string/virtual/code-points":["esnext.string.code-points"],"core-js/features/string/virtual/ends-with":["es.string.ends-with"],"core-js/features/string/virtual/fixed":["es.string.fixed"],"core-js/features/string/virtual/fontcolor":["es.string.fontcolor"],"core-js/features/string/virtual/fontsize":["es.string.fontsize"],"core-js/features/string/virtual/includes":["es.string.includes"],"core-js/features/string/virtual/italics":["es.string.italics"],"core-js/features/string/virtual/iterator":["es.string.iterator"],"core-js/features/string/virtual/link":["es.string.link"],"core-js/features/string/virtual/match-all":["es.string.match-all","esnext.string.match-all"],"core-js/features/string/virtual/pad-end":["es.string.pad-end"],"core-js/features/string/virtual/pad-start":["es.string.pad-start"],"core-js/features/string/virtual/repeat":["es.string.repeat"],"core-js/features/string/virtual/replace-all":["esnext.string.replace-all"],"core-js/features/string/virtual/small":["es.string.small"],"core-js/features/string/virtual/starts-with":["es.string.starts-with"],"core-js/features/string/virtual/strike":["es.string.strike"],"core-js/features/string/virtual/sub":["es.string.sub"],"core-js/features/string/virtual/sup":["es.string.sup"],"core-js/features/string/virtual/trim":["es.string.trim"],"core-js/features/string/virtual/trim-end":["es.string.trim-end"],"core-js/features/string/virtual/trim-left":["es.string.trim-start"],"core-js/features/string/virtual/trim-right":["es.string.trim-end"],"core-js/features/string/virtual/trim-start":["es.string.trim-start"],"core-js/features/symbol":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.json.to-string-tag","es.math.to-string-tag","es.object.to-string","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all"],"core-js/features/symbol/async-dispose":["esnext.symbol.async-dispose"],"core-js/features/symbol/async-iterator":["es.symbol.async-iterator"],"core-js/features/symbol/description":["es.symbol.description"],"core-js/features/symbol/dispose":["esnext.symbol.dispose"],"core-js/features/symbol/for":["es.symbol"],"core-js/features/symbol/has-instance":["es.symbol.has-instance","es.function.has-instance"],"core-js/features/symbol/is-concat-spreadable":["es.symbol.is-concat-spreadable","es.array.concat"],"core-js/features/symbol/iterator":["es.symbol.iterator","es.string.iterator","web.dom-collections.iterator"],"core-js/features/symbol/key-for":["es.symbol"],"core-js/features/symbol/match":["es.symbol.match","es.string.match"],"core-js/features/symbol/match-all":["es.symbol.match-all","es.string.match-all"],"core-js/features/symbol/observable":["esnext.symbol.observable"],"core-js/features/symbol/pattern-match":["esnext.symbol.pattern-match"],"core-js/features/symbol/replace":["es.symbol.replace","es.string.replace"],"core-js/features/symbol/replace-all":["esnext.symbol.replace-all"],"core-js/features/symbol/search":["es.symbol.search","es.string.search"],"core-js/features/symbol/species":["es.symbol.species"],"core-js/features/symbol/split":["es.symbol.split","es.string.split"],"core-js/features/symbol/to-primitive":["es.symbol.to-primitive"],"core-js/features/symbol/to-string-tag":["es.symbol.to-string-tag","es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/features/symbol/unscopables":["es.symbol.unscopables"],"core-js/features/typed-array":["es.object.to-string","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/copy-within":["es.typed-array.copy-within"],"core-js/features/typed-array/entries":["es.typed-array.iterator"],"core-js/features/typed-array/every":["es.typed-array.every"],"core-js/features/typed-array/fill":["es.typed-array.fill"],"core-js/features/typed-array/filter":["es.typed-array.filter"],"core-js/features/typed-array/find":["es.typed-array.find"],"core-js/features/typed-array/find-index":["es.typed-array.find-index"],"core-js/features/typed-array/float32-array":["es.object.to-string","es.typed-array.float32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/float64-array":["es.object.to-string","es.typed-array.float64-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/for-each":["es.typed-array.for-each"],"core-js/features/typed-array/from":["es.typed-array.from"],"core-js/features/typed-array/includes":["es.typed-array.includes"],"core-js/features/typed-array/index-of":["es.typed-array.index-of"],"core-js/features/typed-array/int16-array":["es.object.to-string","es.typed-array.int16-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/int32-array":["es.object.to-string","es.typed-array.int32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/int8-array":["es.object.to-string","es.typed-array.int8-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/iterator":["es.typed-array.iterator"],"core-js/features/typed-array/join":["es.typed-array.join"],"core-js/features/typed-array/keys":["es.typed-array.iterator"],"core-js/features/typed-array/last-index-of":["es.typed-array.last-index-of"],"core-js/features/typed-array/map":["es.typed-array.map"],"core-js/features/typed-array/of":["es.typed-array.of"],"core-js/features/typed-array/reduce":["es.typed-array.reduce"],"core-js/features/typed-array/reduce-right":["es.typed-array.reduce-right"],"core-js/features/typed-array/reverse":["es.typed-array.reverse"],"core-js/features/typed-array/set":["es.typed-array.set"],"core-js/features/typed-array/slice":["es.typed-array.slice"],"core-js/features/typed-array/some":["es.typed-array.some"],"core-js/features/typed-array/sort":["es.typed-array.sort"],"core-js/features/typed-array/subarray":["es.typed-array.subarray"],"core-js/features/typed-array/to-locale-string":["es.typed-array.to-locale-string"],"core-js/features/typed-array/to-string":["es.typed-array.to-string"],"core-js/features/typed-array/uint16-array":["es.object.to-string","es.typed-array.uint16-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/uint32-array":["es.object.to-string","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/uint8-array":["es.object.to-string","es.typed-array.uint8-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/uint8-clamped-array":["es.object.to-string","es.typed-array.uint8-clamped-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/features/typed-array/values":["es.typed-array.iterator"],"core-js/features/url":["web.url","web.url.to-json","web.url-search-params"],"core-js/features/url-search-params":["web.url-search-params"],"core-js/features/url/to-json":["web.url.to-json"],"core-js/features/weak-map":["es.object.to-string","es.weak-map","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","web.dom-collections.iterator"],"core-js/features/weak-map/delete-all":["es.weak-map","esnext.weak-map.delete-all"],"core-js/features/weak-map/from":["es.string.iterator","es.weak-map","esnext.weak-map.from","web.dom-collections.iterator"],"core-js/features/weak-map/of":["es.string.iterator","es.weak-map","esnext.weak-map.of","web.dom-collections.iterator"],"core-js/features/weak-map/upsert":["es.weak-map","esnext.weak-map.upsert"],"core-js/features/weak-set":["es.object.to-string","es.weak-set","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.dom-collections.iterator"],"core-js/features/weak-set/add-all":["es.weak-set","esnext.weak-set.add-all"],"core-js/features/weak-set/delete-all":["es.weak-set","esnext.weak-set.delete-all"],"core-js/features/weak-set/from":["es.string.iterator","es.weak-set","esnext.weak-set.from","web.dom-collections.iterator"],"core-js/features/weak-set/of":["es.string.iterator","es.weak-set","esnext.weak-set.of","web.dom-collections.iterator"],"core-js/modules/es.array-buffer.constructor":["es.array-buffer.constructor"],"core-js/modules/es.array-buffer.is-view":["es.array-buffer.is-view"],"core-js/modules/es.array-buffer.slice":["es.array-buffer.slice"],"core-js/modules/es.array.concat":["es.array.concat"],"core-js/modules/es.array.copy-within":["es.array.copy-within"],"core-js/modules/es.array.every":["es.array.every"],"core-js/modules/es.array.fill":["es.array.fill"],"core-js/modules/es.array.filter":["es.array.filter"],"core-js/modules/es.array.find":["es.array.find"],"core-js/modules/es.array.find-index":["es.array.find-index"],"core-js/modules/es.array.flat":["es.array.flat"],"core-js/modules/es.array.flat-map":["es.array.flat-map"],"core-js/modules/es.array.for-each":["es.array.for-each"],"core-js/modules/es.array.from":["es.array.from"],"core-js/modules/es.array.includes":["es.array.includes"],"core-js/modules/es.array.index-of":["es.array.index-of"],"core-js/modules/es.array.is-array":["es.array.is-array"],"core-js/modules/es.array.iterator":["es.array.iterator"],"core-js/modules/es.array.join":["es.array.join"],"core-js/modules/es.array.last-index-of":["es.array.last-index-of"],"core-js/modules/es.array.map":["es.array.map"],"core-js/modules/es.array.of":["es.array.of"],"core-js/modules/es.array.reduce":["es.array.reduce"],"core-js/modules/es.array.reduce-right":["es.array.reduce-right"],"core-js/modules/es.array.reverse":["es.array.reverse"],"core-js/modules/es.array.slice":["es.array.slice"],"core-js/modules/es.array.some":["es.array.some"],"core-js/modules/es.array.sort":["es.array.sort"],"core-js/modules/es.array.species":["es.array.species"],"core-js/modules/es.array.splice":["es.array.splice"],"core-js/modules/es.array.unscopables.flat":["es.array.unscopables.flat"],"core-js/modules/es.array.unscopables.flat-map":["es.array.unscopables.flat-map"],"core-js/modules/es.data-view":["es.data-view"],"core-js/modules/es.date.now":["es.date.now"],"core-js/modules/es.date.to-iso-string":["es.date.to-iso-string"],"core-js/modules/es.date.to-json":["es.date.to-json"],"core-js/modules/es.date.to-primitive":["es.date.to-primitive"],"core-js/modules/es.date.to-string":["es.date.to-string"],"core-js/modules/es.function.bind":["es.function.bind"],"core-js/modules/es.function.has-instance":["es.function.has-instance"],"core-js/modules/es.function.name":["es.function.name"],"core-js/modules/es.global-this":["es.global-this"],"core-js/modules/es.json.stringify":["es.json.stringify"],"core-js/modules/es.json.to-string-tag":["es.json.to-string-tag"],"core-js/modules/es.map":["es.map"],"core-js/modules/es.math.acosh":["es.math.acosh"],"core-js/modules/es.math.asinh":["es.math.asinh"],"core-js/modules/es.math.atanh":["es.math.atanh"],"core-js/modules/es.math.cbrt":["es.math.cbrt"],"core-js/modules/es.math.clz32":["es.math.clz32"],"core-js/modules/es.math.cosh":["es.math.cosh"],"core-js/modules/es.math.expm1":["es.math.expm1"],"core-js/modules/es.math.fround":["es.math.fround"],"core-js/modules/es.math.hypot":["es.math.hypot"],"core-js/modules/es.math.imul":["es.math.imul"],"core-js/modules/es.math.log10":["es.math.log10"],"core-js/modules/es.math.log1p":["es.math.log1p"],"core-js/modules/es.math.log2":["es.math.log2"],"core-js/modules/es.math.sign":["es.math.sign"],"core-js/modules/es.math.sinh":["es.math.sinh"],"core-js/modules/es.math.tanh":["es.math.tanh"],"core-js/modules/es.math.to-string-tag":["es.math.to-string-tag"],"core-js/modules/es.math.trunc":["es.math.trunc"],"core-js/modules/es.number.constructor":["es.number.constructor"],"core-js/modules/es.number.epsilon":["es.number.epsilon"],"core-js/modules/es.number.is-finite":["es.number.is-finite"],"core-js/modules/es.number.is-integer":["es.number.is-integer"],"core-js/modules/es.number.is-nan":["es.number.is-nan"],"core-js/modules/es.number.is-safe-integer":["es.number.is-safe-integer"],"core-js/modules/es.number.max-safe-integer":["es.number.max-safe-integer"],"core-js/modules/es.number.min-safe-integer":["es.number.min-safe-integer"],"core-js/modules/es.number.parse-float":["es.number.parse-float"],"core-js/modules/es.number.parse-int":["es.number.parse-int"],"core-js/modules/es.number.to-fixed":["es.number.to-fixed"],"core-js/modules/es.number.to-precision":["es.number.to-precision"],"core-js/modules/es.object.assign":["es.object.assign"],"core-js/modules/es.object.create":["es.object.create"],"core-js/modules/es.object.define-getter":["es.object.define-getter"],"core-js/modules/es.object.define-properties":["es.object.define-properties"],"core-js/modules/es.object.define-property":["es.object.define-property"],"core-js/modules/es.object.define-setter":["es.object.define-setter"],"core-js/modules/es.object.entries":["es.object.entries"],"core-js/modules/es.object.freeze":["es.object.freeze"],"core-js/modules/es.object.from-entries":["es.object.from-entries"],"core-js/modules/es.object.get-own-property-descriptor":["es.object.get-own-property-descriptor"],"core-js/modules/es.object.get-own-property-descriptors":["es.object.get-own-property-descriptors"],"core-js/modules/es.object.get-own-property-names":["es.object.get-own-property-names"],"core-js/modules/es.object.get-prototype-of":["es.object.get-prototype-of"],"core-js/modules/es.object.is":["es.object.is"],"core-js/modules/es.object.is-extensible":["es.object.is-extensible"],"core-js/modules/es.object.is-frozen":["es.object.is-frozen"],"core-js/modules/es.object.is-sealed":["es.object.is-sealed"],"core-js/modules/es.object.keys":["es.object.keys"],"core-js/modules/es.object.lookup-getter":["es.object.lookup-getter"],"core-js/modules/es.object.lookup-setter":["es.object.lookup-setter"],"core-js/modules/es.object.prevent-extensions":["es.object.prevent-extensions"],"core-js/modules/es.object.seal":["es.object.seal"],"core-js/modules/es.object.set-prototype-of":["es.object.set-prototype-of"],"core-js/modules/es.object.to-string":["es.object.to-string"],"core-js/modules/es.object.values":["es.object.values"],"core-js/modules/es.parse-float":["es.parse-float"],"core-js/modules/es.parse-int":["es.parse-int"],"core-js/modules/es.promise":["es.promise"],"core-js/modules/es.promise.all-settled":["es.promise.all-settled"],"core-js/modules/es.promise.finally":["es.promise.finally"],"core-js/modules/es.reflect.apply":["es.reflect.apply"],"core-js/modules/es.reflect.construct":["es.reflect.construct"],"core-js/modules/es.reflect.define-property":["es.reflect.define-property"],"core-js/modules/es.reflect.delete-property":["es.reflect.delete-property"],"core-js/modules/es.reflect.get":["es.reflect.get"],"core-js/modules/es.reflect.get-own-property-descriptor":["es.reflect.get-own-property-descriptor"],"core-js/modules/es.reflect.get-prototype-of":["es.reflect.get-prototype-of"],"core-js/modules/es.reflect.has":["es.reflect.has"],"core-js/modules/es.reflect.is-extensible":["es.reflect.is-extensible"],"core-js/modules/es.reflect.own-keys":["es.reflect.own-keys"],"core-js/modules/es.reflect.prevent-extensions":["es.reflect.prevent-extensions"],"core-js/modules/es.reflect.set":["es.reflect.set"],"core-js/modules/es.reflect.set-prototype-of":["es.reflect.set-prototype-of"],"core-js/modules/es.regexp.constructor":["es.regexp.constructor"],"core-js/modules/es.regexp.exec":["es.regexp.exec"],"core-js/modules/es.regexp.flags":["es.regexp.flags"],"core-js/modules/es.regexp.sticky":["es.regexp.sticky"],"core-js/modules/es.regexp.test":["es.regexp.test"],"core-js/modules/es.regexp.to-string":["es.regexp.to-string"],"core-js/modules/es.set":["es.set"],"core-js/modules/es.string.anchor":["es.string.anchor"],"core-js/modules/es.string.big":["es.string.big"],"core-js/modules/es.string.blink":["es.string.blink"],"core-js/modules/es.string.bold":["es.string.bold"],"core-js/modules/es.string.code-point-at":["es.string.code-point-at"],"core-js/modules/es.string.ends-with":["es.string.ends-with"],"core-js/modules/es.string.fixed":["es.string.fixed"],"core-js/modules/es.string.fontcolor":["es.string.fontcolor"],"core-js/modules/es.string.fontsize":["es.string.fontsize"],"core-js/modules/es.string.from-code-point":["es.string.from-code-point"],"core-js/modules/es.string.includes":["es.string.includes"],"core-js/modules/es.string.italics":["es.string.italics"],"core-js/modules/es.string.iterator":["es.string.iterator"],"core-js/modules/es.string.link":["es.string.link"],"core-js/modules/es.string.match":["es.string.match"],"core-js/modules/es.string.match-all":["es.string.match-all"],"core-js/modules/es.string.pad-end":["es.string.pad-end"],"core-js/modules/es.string.pad-start":["es.string.pad-start"],"core-js/modules/es.string.raw":["es.string.raw"],"core-js/modules/es.string.repeat":["es.string.repeat"],"core-js/modules/es.string.replace":["es.string.replace"],"core-js/modules/es.string.search":["es.string.search"],"core-js/modules/es.string.small":["es.string.small"],"core-js/modules/es.string.split":["es.string.split"],"core-js/modules/es.string.starts-with":["es.string.starts-with"],"core-js/modules/es.string.strike":["es.string.strike"],"core-js/modules/es.string.sub":["es.string.sub"],"core-js/modules/es.string.sup":["es.string.sup"],"core-js/modules/es.string.trim":["es.string.trim"],"core-js/modules/es.string.trim-end":["es.string.trim-end"],"core-js/modules/es.string.trim-start":["es.string.trim-start"],"core-js/modules/es.symbol":["es.symbol"],"core-js/modules/es.symbol.async-iterator":["es.symbol.async-iterator"],"core-js/modules/es.symbol.description":["es.symbol.description"],"core-js/modules/es.symbol.has-instance":["es.symbol.has-instance"],"core-js/modules/es.symbol.is-concat-spreadable":["es.symbol.is-concat-spreadable"],"core-js/modules/es.symbol.iterator":["es.symbol.iterator"],"core-js/modules/es.symbol.match":["es.symbol.match"],"core-js/modules/es.symbol.match-all":["es.symbol.match-all"],"core-js/modules/es.symbol.replace":["es.symbol.replace"],"core-js/modules/es.symbol.search":["es.symbol.search"],"core-js/modules/es.symbol.species":["es.symbol.species"],"core-js/modules/es.symbol.split":["es.symbol.split"],"core-js/modules/es.symbol.to-primitive":["es.symbol.to-primitive"],"core-js/modules/es.symbol.to-string-tag":["es.symbol.to-string-tag"],"core-js/modules/es.symbol.unscopables":["es.symbol.unscopables"],"core-js/modules/es.typed-array.copy-within":["es.typed-array.copy-within"],"core-js/modules/es.typed-array.every":["es.typed-array.every"],"core-js/modules/es.typed-array.fill":["es.typed-array.fill"],"core-js/modules/es.typed-array.filter":["es.typed-array.filter"],"core-js/modules/es.typed-array.find":["es.typed-array.find"],"core-js/modules/es.typed-array.find-index":["es.typed-array.find-index"],"core-js/modules/es.typed-array.float32-array":["es.typed-array.float32-array"],"core-js/modules/es.typed-array.float64-array":["es.typed-array.float64-array"],"core-js/modules/es.typed-array.for-each":["es.typed-array.for-each"],"core-js/modules/es.typed-array.from":["es.typed-array.from"],"core-js/modules/es.typed-array.includes":["es.typed-array.includes"],"core-js/modules/es.typed-array.index-of":["es.typed-array.index-of"],"core-js/modules/es.typed-array.int16-array":["es.typed-array.int16-array"],"core-js/modules/es.typed-array.int32-array":["es.typed-array.int32-array"],"core-js/modules/es.typed-array.int8-array":["es.typed-array.int8-array"],"core-js/modules/es.typed-array.iterator":["es.typed-array.iterator"],"core-js/modules/es.typed-array.join":["es.typed-array.join"],"core-js/modules/es.typed-array.last-index-of":["es.typed-array.last-index-of"],"core-js/modules/es.typed-array.map":["es.typed-array.map"],"core-js/modules/es.typed-array.of":["es.typed-array.of"],"core-js/modules/es.typed-array.reduce":["es.typed-array.reduce"],"core-js/modules/es.typed-array.reduce-right":["es.typed-array.reduce-right"],"core-js/modules/es.typed-array.reverse":["es.typed-array.reverse"],"core-js/modules/es.typed-array.set":["es.typed-array.set"],"core-js/modules/es.typed-array.slice":["es.typed-array.slice"],"core-js/modules/es.typed-array.some":["es.typed-array.some"],"core-js/modules/es.typed-array.sort":["es.typed-array.sort"],"core-js/modules/es.typed-array.subarray":["es.typed-array.subarray"],"core-js/modules/es.typed-array.to-locale-string":["es.typed-array.to-locale-string"],"core-js/modules/es.typed-array.to-string":["es.typed-array.to-string"],"core-js/modules/es.typed-array.uint16-array":["es.typed-array.uint16-array"],"core-js/modules/es.typed-array.uint32-array":["es.typed-array.uint32-array"],"core-js/modules/es.typed-array.uint8-array":["es.typed-array.uint8-array"],"core-js/modules/es.typed-array.uint8-clamped-array":["es.typed-array.uint8-clamped-array"],"core-js/modules/es.weak-map":["es.weak-map"],"core-js/modules/es.weak-set":["es.weak-set"],"core-js/modules/esnext.aggregate-error":["esnext.aggregate-error"],"core-js/modules/esnext.array.is-template-object":["esnext.array.is-template-object"],"core-js/modules/esnext.array.last-index":["esnext.array.last-index"],"core-js/modules/esnext.array.last-item":["esnext.array.last-item"],"core-js/modules/esnext.async-iterator.as-indexed-pairs":["esnext.async-iterator.as-indexed-pairs"],"core-js/modules/esnext.async-iterator.constructor":["esnext.async-iterator.constructor"],"core-js/modules/esnext.async-iterator.drop":["esnext.async-iterator.drop"],"core-js/modules/esnext.async-iterator.every":["esnext.async-iterator.every"],"core-js/modules/esnext.async-iterator.filter":["esnext.async-iterator.filter"],"core-js/modules/esnext.async-iterator.find":["esnext.async-iterator.find"],"core-js/modules/esnext.async-iterator.flat-map":["esnext.async-iterator.flat-map"],"core-js/modules/esnext.async-iterator.for-each":["esnext.async-iterator.for-each"],"core-js/modules/esnext.async-iterator.from":["esnext.async-iterator.from"],"core-js/modules/esnext.async-iterator.map":["esnext.async-iterator.map"],"core-js/modules/esnext.async-iterator.reduce":["esnext.async-iterator.reduce"],"core-js/modules/esnext.async-iterator.some":["esnext.async-iterator.some"],"core-js/modules/esnext.async-iterator.take":["esnext.async-iterator.take"],"core-js/modules/esnext.async-iterator.to-array":["esnext.async-iterator.to-array"],"core-js/modules/esnext.composite-key":["esnext.composite-key"],"core-js/modules/esnext.composite-symbol":["esnext.composite-symbol"],"core-js/modules/esnext.global-this":["esnext.global-this"],"core-js/modules/esnext.iterator.as-indexed-pairs":["esnext.iterator.as-indexed-pairs"],"core-js/modules/esnext.iterator.constructor":["esnext.iterator.constructor"],"core-js/modules/esnext.iterator.drop":["esnext.iterator.drop"],"core-js/modules/esnext.iterator.every":["esnext.iterator.every"],"core-js/modules/esnext.iterator.filter":["esnext.iterator.filter"],"core-js/modules/esnext.iterator.find":["esnext.iterator.find"],"core-js/modules/esnext.iterator.flat-map":["esnext.iterator.flat-map"],"core-js/modules/esnext.iterator.for-each":["esnext.iterator.for-each"],"core-js/modules/esnext.iterator.from":["esnext.iterator.from"],"core-js/modules/esnext.iterator.map":["esnext.iterator.map"],"core-js/modules/esnext.iterator.reduce":["esnext.iterator.reduce"],"core-js/modules/esnext.iterator.some":["esnext.iterator.some"],"core-js/modules/esnext.iterator.take":["esnext.iterator.take"],"core-js/modules/esnext.iterator.to-array":["esnext.iterator.to-array"],"core-js/modules/esnext.map.delete-all":["esnext.map.delete-all"],"core-js/modules/esnext.map.every":["esnext.map.every"],"core-js/modules/esnext.map.filter":["esnext.map.filter"],"core-js/modules/esnext.map.find":["esnext.map.find"],"core-js/modules/esnext.map.find-key":["esnext.map.find-key"],"core-js/modules/esnext.map.from":["esnext.map.from"],"core-js/modules/esnext.map.group-by":["esnext.map.group-by"],"core-js/modules/esnext.map.includes":["esnext.map.includes"],"core-js/modules/esnext.map.key-by":["esnext.map.key-by"],"core-js/modules/esnext.map.key-of":["esnext.map.key-of"],"core-js/modules/esnext.map.map-keys":["esnext.map.map-keys"],"core-js/modules/esnext.map.map-values":["esnext.map.map-values"],"core-js/modules/esnext.map.merge":["esnext.map.merge"],"core-js/modules/esnext.map.of":["esnext.map.of"],"core-js/modules/esnext.map.reduce":["esnext.map.reduce"],"core-js/modules/esnext.map.some":["esnext.map.some"],"core-js/modules/esnext.map.update":["esnext.map.update"],"core-js/modules/esnext.map.update-or-insert":["esnext.map.update-or-insert"],"core-js/modules/esnext.map.upsert":["esnext.map.upsert"],"core-js/modules/esnext.math.clamp":["esnext.math.clamp"],"core-js/modules/esnext.math.deg-per-rad":["esnext.math.deg-per-rad"],"core-js/modules/esnext.math.degrees":["esnext.math.degrees"],"core-js/modules/esnext.math.fscale":["esnext.math.fscale"],"core-js/modules/esnext.math.iaddh":["esnext.math.iaddh"],"core-js/modules/esnext.math.imulh":["esnext.math.imulh"],"core-js/modules/esnext.math.isubh":["esnext.math.isubh"],"core-js/modules/esnext.math.rad-per-deg":["esnext.math.rad-per-deg"],"core-js/modules/esnext.math.radians":["esnext.math.radians"],"core-js/modules/esnext.math.scale":["esnext.math.scale"],"core-js/modules/esnext.math.seeded-prng":["esnext.math.seeded-prng"],"core-js/modules/esnext.math.signbit":["esnext.math.signbit"],"core-js/modules/esnext.math.umulh":["esnext.math.umulh"],"core-js/modules/esnext.number.from-string":["esnext.number.from-string"],"core-js/modules/esnext.object.iterate-entries":["esnext.object.iterate-entries"],"core-js/modules/esnext.object.iterate-keys":["esnext.object.iterate-keys"],"core-js/modules/esnext.object.iterate-values":["esnext.object.iterate-values"],"core-js/modules/esnext.observable":["esnext.observable"],"core-js/modules/esnext.promise.all-settled":["esnext.promise.all-settled"],"core-js/modules/esnext.promise.any":["esnext.promise.any"],"core-js/modules/esnext.promise.try":["esnext.promise.try"],"core-js/modules/esnext.reflect.define-metadata":["esnext.reflect.define-metadata"],"core-js/modules/esnext.reflect.delete-metadata":["esnext.reflect.delete-metadata"],"core-js/modules/esnext.reflect.get-metadata":["esnext.reflect.get-metadata"],"core-js/modules/esnext.reflect.get-metadata-keys":["esnext.reflect.get-metadata-keys"],"core-js/modules/esnext.reflect.get-own-metadata":["esnext.reflect.get-own-metadata"],"core-js/modules/esnext.reflect.get-own-metadata-keys":["esnext.reflect.get-own-metadata-keys"],"core-js/modules/esnext.reflect.has-metadata":["esnext.reflect.has-metadata"],"core-js/modules/esnext.reflect.has-own-metadata":["esnext.reflect.has-own-metadata"],"core-js/modules/esnext.reflect.metadata":["esnext.reflect.metadata"],"core-js/modules/esnext.set.add-all":["esnext.set.add-all"],"core-js/modules/esnext.set.delete-all":["esnext.set.delete-all"],"core-js/modules/esnext.set.difference":["esnext.set.difference"],"core-js/modules/esnext.set.every":["esnext.set.every"],"core-js/modules/esnext.set.filter":["esnext.set.filter"],"core-js/modules/esnext.set.find":["esnext.set.find"],"core-js/modules/esnext.set.from":["esnext.set.from"],"core-js/modules/esnext.set.intersection":["esnext.set.intersection"],"core-js/modules/esnext.set.is-disjoint-from":["esnext.set.is-disjoint-from"],"core-js/modules/esnext.set.is-subset-of":["esnext.set.is-subset-of"],"core-js/modules/esnext.set.is-superset-of":["esnext.set.is-superset-of"],"core-js/modules/esnext.set.join":["esnext.set.join"],"core-js/modules/esnext.set.map":["esnext.set.map"],"core-js/modules/esnext.set.of":["esnext.set.of"],"core-js/modules/esnext.set.reduce":["esnext.set.reduce"],"core-js/modules/esnext.set.some":["esnext.set.some"],"core-js/modules/esnext.set.symmetric-difference":["esnext.set.symmetric-difference"],"core-js/modules/esnext.set.union":["esnext.set.union"],"core-js/modules/esnext.string.at":["esnext.string.at"],"core-js/modules/esnext.string.code-points":["esnext.string.code-points"],"core-js/modules/esnext.string.match-all":["esnext.string.match-all"],"core-js/modules/esnext.string.replace-all":["esnext.string.replace-all"],"core-js/modules/esnext.symbol.async-dispose":["esnext.symbol.async-dispose"],"core-js/modules/esnext.symbol.dispose":["esnext.symbol.dispose"],"core-js/modules/esnext.symbol.observable":["esnext.symbol.observable"],"core-js/modules/esnext.symbol.pattern-match":["esnext.symbol.pattern-match"],"core-js/modules/esnext.symbol.replace-all":["esnext.symbol.replace-all"],"core-js/modules/esnext.weak-map.delete-all":["esnext.weak-map.delete-all"],"core-js/modules/esnext.weak-map.from":["esnext.weak-map.from"],"core-js/modules/esnext.weak-map.of":["esnext.weak-map.of"],"core-js/modules/esnext.weak-map.upsert":["esnext.weak-map.upsert"],"core-js/modules/esnext.weak-set.add-all":["esnext.weak-set.add-all"],"core-js/modules/esnext.weak-set.delete-all":["esnext.weak-set.delete-all"],"core-js/modules/esnext.weak-set.from":["esnext.weak-set.from"],"core-js/modules/esnext.weak-set.of":["esnext.weak-set.of"],"core-js/modules/web.dom-collections.for-each":["web.dom-collections.for-each"],"core-js/modules/web.dom-collections.iterator":["web.dom-collections.iterator"],"core-js/modules/web.immediate":["web.immediate"],"core-js/modules/web.queue-microtask":["web.queue-microtask"],"core-js/modules/web.timers":["web.timers"],"core-js/modules/web.url":["web.url"],"core-js/modules/web.url-search-params":["web.url-search-params"],"core-js/modules/web.url.to-json":["web.url.to-json"],"core-js/proposals":["esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.url","web.url.to-json","web.url-search-params"],"core-js/proposals/array-is-template-object":["esnext.array.is-template-object"],"core-js/proposals/array-last":["esnext.array.last-index","esnext.array.last-item"],"core-js/proposals/collection-methods":["esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.set.add-all","esnext.set.delete-all","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.join","esnext.set.map","esnext.set.reduce","esnext.set.some","esnext.weak-map.delete-all","esnext.weak-set.add-all","esnext.weak-set.delete-all"],"core-js/proposals/collection-of-from":["esnext.map.from","esnext.map.of","esnext.set.from","esnext.set.of","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-set.from","esnext.weak-set.of"],"core-js/proposals/efficient-64-bit-arithmetic":["esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.umulh"],"core-js/proposals/global-this":["esnext.global-this"],"core-js/proposals/iterator-helpers":["esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array"],"core-js/proposals/keys-composition":["esnext.composite-key","esnext.composite-symbol"],"core-js/proposals/map-update-or-insert":["esnext.map.update-or-insert","esnext.map.upsert","esnext.weak-map.upsert"],"core-js/proposals/map-upsert":["esnext.map.update-or-insert","esnext.map.upsert","esnext.weak-map.upsert"],"core-js/proposals/math-extensions":["esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale"],"core-js/proposals/math-signbit":["esnext.math.signbit"],"core-js/proposals/number-from-string":["esnext.number.from-string"],"core-js/proposals/object-iteration":["esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values"],"core-js/proposals/observable":["esnext.observable","esnext.symbol.observable"],"core-js/proposals/pattern-matching":["esnext.symbol.pattern-match"],"core-js/proposals/promise-all-settled":["esnext.promise.all-settled"],"core-js/proposals/promise-any":["esnext.aggregate-error","esnext.promise.any"],"core-js/proposals/promise-try":["esnext.promise.try"],"core-js/proposals/reflect-metadata":["esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata"],"core-js/proposals/seeded-random":["esnext.math.seeded-prng"],"core-js/proposals/set-methods":["esnext.set.difference","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.symmetric-difference","esnext.set.union"],"core-js/proposals/string-at":["esnext.string.at"],"core-js/proposals/string-code-points":["esnext.string.code-points"],"core-js/proposals/string-match-all":["esnext.string.match-all"],"core-js/proposals/string-replace-all":["esnext.string.replace-all","esnext.symbol.replace-all"],"core-js/proposals/url":["web.url","web.url.to-json","web.url-search-params"],"core-js/proposals/using-statement":["esnext.symbol.async-dispose","esnext.symbol.dispose"],"core-js/stable":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.data-view","es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string","es.function.bind","es.function.has-instance","es.function.name","es.global-this","es.json.stringify","es.json.to-string-tag","es.map","es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc","es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values","es.parse-float","es.parse-int","es.promise","es.promise.all-settled","es.promise.finally","es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of","es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.set","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string","es.weak-map","es.weak-set","web.dom-collections.for-each","web.dom-collections.iterator","web.immediate","web.queue-microtask","web.timers","web.url","web.url.to-json","web.url-search-params"],"core-js/stable/array":["es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.from","es.array.includes","es.array.index-of","es.array.is-array","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.of","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map","es.string.iterator"],"core-js/stable/array-buffer":["es.array-buffer.constructor","es.array-buffer.is-view","es.array-buffer.slice","es.object.to-string"],"core-js/stable/array-buffer/constructor":["es.array-buffer.constructor","es.object.to-string"],"core-js/stable/array-buffer/is-view":["es.array-buffer.is-view"],"core-js/stable/array-buffer/slice":["es.array-buffer.slice"],"core-js/stable/array/concat":["es.array.concat"],"core-js/stable/array/copy-within":["es.array.copy-within"],"core-js/stable/array/entries":["es.array.iterator"],"core-js/stable/array/every":["es.array.every"],"core-js/stable/array/fill":["es.array.fill"],"core-js/stable/array/filter":["es.array.filter"],"core-js/stable/array/find":["es.array.find"],"core-js/stable/array/find-index":["es.array.find-index"],"core-js/stable/array/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/stable/array/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/stable/array/for-each":["es.array.for-each"],"core-js/stable/array/from":["es.array.from","es.string.iterator"],"core-js/stable/array/includes":["es.array.includes"],"core-js/stable/array/index-of":["es.array.index-of"],"core-js/stable/array/is-array":["es.array.is-array"],"core-js/stable/array/iterator":["es.array.iterator"],"core-js/stable/array/join":["es.array.join"],"core-js/stable/array/keys":["es.array.iterator"],"core-js/stable/array/last-index-of":["es.array.last-index-of"],"core-js/stable/array/map":["es.array.map"],"core-js/stable/array/of":["es.array.of"],"core-js/stable/array/reduce":["es.array.reduce"],"core-js/stable/array/reduce-right":["es.array.reduce-right"],"core-js/stable/array/reverse":["es.array.reverse"],"core-js/stable/array/slice":["es.array.slice"],"core-js/stable/array/some":["es.array.some"],"core-js/stable/array/sort":["es.array.sort"],"core-js/stable/array/splice":["es.array.splice"],"core-js/stable/array/values":["es.array.iterator"],"core-js/stable/array/virtual":["es.array.concat","es.array.copy-within","es.array.every","es.array.fill","es.array.filter","es.array.find","es.array.find-index","es.array.flat","es.array.flat-map","es.array.for-each","es.array.includes","es.array.index-of","es.array.iterator","es.array.join","es.array.last-index-of","es.array.map","es.array.reduce","es.array.reduce-right","es.array.reverse","es.array.slice","es.array.some","es.array.sort","es.array.species","es.array.splice","es.array.unscopables.flat","es.array.unscopables.flat-map"],"core-js/stable/array/virtual/concat":["es.array.concat"],"core-js/stable/array/virtual/copy-within":["es.array.copy-within"],"core-js/stable/array/virtual/entries":["es.array.iterator"],"core-js/stable/array/virtual/every":["es.array.every"],"core-js/stable/array/virtual/fill":["es.array.fill"],"core-js/stable/array/virtual/filter":["es.array.filter"],"core-js/stable/array/virtual/find":["es.array.find"],"core-js/stable/array/virtual/find-index":["es.array.find-index"],"core-js/stable/array/virtual/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/stable/array/virtual/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/stable/array/virtual/for-each":["es.array.for-each"],"core-js/stable/array/virtual/includes":["es.array.includes"],"core-js/stable/array/virtual/index-of":["es.array.index-of"],"core-js/stable/array/virtual/iterator":["es.array.iterator"],"core-js/stable/array/virtual/join":["es.array.join"],"core-js/stable/array/virtual/keys":["es.array.iterator"],"core-js/stable/array/virtual/last-index-of":["es.array.last-index-of"],"core-js/stable/array/virtual/map":["es.array.map"],"core-js/stable/array/virtual/reduce":["es.array.reduce"],"core-js/stable/array/virtual/reduce-right":["es.array.reduce-right"],"core-js/stable/array/virtual/reverse":["es.array.reverse"],"core-js/stable/array/virtual/slice":["es.array.slice"],"core-js/stable/array/virtual/some":["es.array.some"],"core-js/stable/array/virtual/sort":["es.array.sort"],"core-js/stable/array/virtual/splice":["es.array.splice"],"core-js/stable/array/virtual/values":["es.array.iterator"],"core-js/stable/clear-immediate":["web.immediate"],"core-js/stable/data-view":["es.data-view","es.object.to-string"],"core-js/stable/date":["es.date.now","es.date.to-iso-string","es.date.to-json","es.date.to-primitive","es.date.to-string"],"core-js/stable/date/now":["es.date.now"],"core-js/stable/date/to-iso-string":["es.date.to-iso-string","es.date.to-json"],"core-js/stable/date/to-json":["es.date.to-json"],"core-js/stable/date/to-primitive":["es.date.to-primitive"],"core-js/stable/date/to-string":["es.date.to-string"],"core-js/stable/dom-collections":["es.array.iterator","web.dom-collections.for-each","web.dom-collections.iterator"],"core-js/stable/dom-collections/for-each":["web.dom-collections.for-each"],"core-js/stable/dom-collections/iterator":["web.dom-collections.iterator"],"core-js/stable/function":["es.function.bind","es.function.has-instance","es.function.name"],"core-js/stable/function/bind":["es.function.bind"],"core-js/stable/function/has-instance":["es.function.has-instance"],"core-js/stable/function/name":["es.function.name"],"core-js/stable/function/virtual":["es.function.bind"],"core-js/stable/function/virtual/bind":["es.function.bind"],"core-js/stable/global-this":["es.global-this"],"core-js/stable/instance/bind":["es.function.bind"],"core-js/stable/instance/code-point-at":["es.string.code-point-at"],"core-js/stable/instance/concat":["es.array.concat"],"core-js/stable/instance/copy-within":["es.array.copy-within"],"core-js/stable/instance/ends-with":["es.string.ends-with"],"core-js/stable/instance/entries":["es.array.iterator","web.dom-collections.iterator"],"core-js/stable/instance/every":["es.array.every"],"core-js/stable/instance/fill":["es.array.fill"],"core-js/stable/instance/filter":["es.array.filter"],"core-js/stable/instance/find":["es.array.find"],"core-js/stable/instance/find-index":["es.array.find-index"],"core-js/stable/instance/flags":["es.regexp.flags"],"core-js/stable/instance/flat":["es.array.flat","es.array.unscopables.flat"],"core-js/stable/instance/flat-map":["es.array.flat-map","es.array.unscopables.flat-map"],"core-js/stable/instance/for-each":["es.array.for-each","web.dom-collections.iterator"],"core-js/stable/instance/includes":["es.array.includes","es.string.includes"],"core-js/stable/instance/index-of":["es.array.index-of"],"core-js/stable/instance/keys":["es.array.iterator","web.dom-collections.iterator"],"core-js/stable/instance/last-index-of":["es.array.last-index-of"],"core-js/stable/instance/map":["es.array.map"],"core-js/stable/instance/match-all":["es.string.match-all"],"core-js/stable/instance/pad-end":["es.string.pad-end"],"core-js/stable/instance/pad-start":["es.string.pad-start"],"core-js/stable/instance/reduce":["es.array.reduce"],"core-js/stable/instance/reduce-right":["es.array.reduce-right"],"core-js/stable/instance/repeat":["es.string.repeat"],"core-js/stable/instance/reverse":["es.array.reverse"],"core-js/stable/instance/slice":["es.array.slice"],"core-js/stable/instance/some":["es.array.some"],"core-js/stable/instance/sort":["es.array.sort"],"core-js/stable/instance/splice":["es.array.splice"],"core-js/stable/instance/starts-with":["es.string.starts-with"],"core-js/stable/instance/trim":["es.string.trim"],"core-js/stable/instance/trim-end":["es.string.trim-end"],"core-js/stable/instance/trim-left":["es.string.trim-start"],"core-js/stable/instance/trim-right":["es.string.trim-end"],"core-js/stable/instance/trim-start":["es.string.trim-start"],"core-js/stable/instance/values":["es.array.iterator","web.dom-collections.iterator"],"core-js/stable/json":["es.json.stringify","es.json.to-string-tag"],"core-js/stable/json/stringify":["es.json.stringify"],"core-js/stable/json/to-string-tag":["es.json.to-string-tag"],"core-js/stable/map":["es.map","es.object.to-string","es.string.iterator","web.dom-collections.iterator"],"core-js/stable/math":["es.math.acosh","es.math.asinh","es.math.atanh","es.math.cbrt","es.math.clz32","es.math.cosh","es.math.expm1","es.math.fround","es.math.hypot","es.math.imul","es.math.log10","es.math.log1p","es.math.log2","es.math.sign","es.math.sinh","es.math.tanh","es.math.to-string-tag","es.math.trunc"],"core-js/stable/math/acosh":["es.math.acosh"],"core-js/stable/math/asinh":["es.math.asinh"],"core-js/stable/math/atanh":["es.math.atanh"],"core-js/stable/math/cbrt":["es.math.cbrt"],"core-js/stable/math/clz32":["es.math.clz32"],"core-js/stable/math/cosh":["es.math.cosh"],"core-js/stable/math/expm1":["es.math.expm1"],"core-js/stable/math/fround":["es.math.fround"],"core-js/stable/math/hypot":["es.math.hypot"],"core-js/stable/math/imul":["es.math.imul"],"core-js/stable/math/log10":["es.math.log10"],"core-js/stable/math/log1p":["es.math.log1p"],"core-js/stable/math/log2":["es.math.log2"],"core-js/stable/math/sign":["es.math.sign"],"core-js/stable/math/sinh":["es.math.sinh"],"core-js/stable/math/tanh":["es.math.tanh"],"core-js/stable/math/to-string-tag":["es.math.to-string-tag"],"core-js/stable/math/trunc":["es.math.trunc"],"core-js/stable/number":["es.number.constructor","es.number.epsilon","es.number.is-finite","es.number.is-integer","es.number.is-nan","es.number.is-safe-integer","es.number.max-safe-integer","es.number.min-safe-integer","es.number.parse-float","es.number.parse-int","es.number.to-fixed","es.number.to-precision"],"core-js/stable/number/constructor":["es.number.constructor"],"core-js/stable/number/epsilon":["es.number.epsilon"],"core-js/stable/number/is-finite":["es.number.is-finite"],"core-js/stable/number/is-integer":["es.number.is-integer"],"core-js/stable/number/is-nan":["es.number.is-nan"],"core-js/stable/number/is-safe-integer":["es.number.is-safe-integer"],"core-js/stable/number/max-safe-integer":["es.number.max-safe-integer"],"core-js/stable/number/min-safe-integer":["es.number.min-safe-integer"],"core-js/stable/number/parse-float":["es.number.parse-float"],"core-js/stable/number/parse-int":["es.number.parse-int"],"core-js/stable/number/to-fixed":["es.number.to-fixed"],"core-js/stable/number/to-precision":["es.number.to-precision"],"core-js/stable/number/virtual":["es.number.to-fixed","es.number.to-precision"],"core-js/stable/number/virtual/to-fixed":["es.number.to-fixed"],"core-js/stable/number/virtual/to-precision":["es.number.to-precision"],"core-js/stable/object":["es.symbol","es.json.to-string-tag","es.math.to-string-tag","es.object.assign","es.object.create","es.object.define-getter","es.object.define-properties","es.object.define-property","es.object.define-setter","es.object.entries","es.object.freeze","es.object.from-entries","es.object.get-own-property-descriptor","es.object.get-own-property-descriptors","es.object.get-own-property-names","es.object.get-prototype-of","es.object.is","es.object.is-extensible","es.object.is-frozen","es.object.is-sealed","es.object.keys","es.object.lookup-getter","es.object.lookup-setter","es.object.prevent-extensions","es.object.seal","es.object.set-prototype-of","es.object.to-string","es.object.values"],"core-js/stable/object/assign":["es.object.assign"],"core-js/stable/object/create":["es.object.create"],"core-js/stable/object/define-getter":["es.object.define-getter"],"core-js/stable/object/define-properties":["es.object.define-properties"],"core-js/stable/object/define-property":["es.object.define-property"],"core-js/stable/object/define-setter":["es.object.define-setter"],"core-js/stable/object/entries":["es.object.entries"],"core-js/stable/object/freeze":["es.object.freeze"],"core-js/stable/object/from-entries":["es.array.iterator","es.object.from-entries"],"core-js/stable/object/get-own-property-descriptor":["es.object.get-own-property-descriptor"],"core-js/stable/object/get-own-property-descriptors":["es.object.get-own-property-descriptors"],"core-js/stable/object/get-own-property-names":["es.object.get-own-property-names"],"core-js/stable/object/get-own-property-symbols":["es.symbol"],"core-js/stable/object/get-prototype-of":["es.object.get-prototype-of"],"core-js/stable/object/is":["es.object.is"],"core-js/stable/object/is-extensible":["es.object.is-extensible"],"core-js/stable/object/is-frozen":["es.object.is-frozen"],"core-js/stable/object/is-sealed":["es.object.is-sealed"],"core-js/stable/object/keys":["es.object.keys"],"core-js/stable/object/lookup-getter":["es.object.lookup-setter"],"core-js/stable/object/lookup-setter":["es.object.lookup-setter"],"core-js/stable/object/prevent-extensions":["es.object.prevent-extensions"],"core-js/stable/object/seal":["es.object.seal"],"core-js/stable/object/set-prototype-of":["es.object.set-prototype-of"],"core-js/stable/object/to-string":["es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/stable/object/values":["es.object.values"],"core-js/stable/parse-float":["es.parse-float"],"core-js/stable/parse-int":["es.parse-int"],"core-js/stable/promise":["es.object.to-string","es.promise","es.promise.all-settled","es.promise.finally","es.string.iterator","web.dom-collections.iterator"],"core-js/stable/promise/all-settled":["es.promise","es.promise.all-settled"],"core-js/stable/promise/finally":["es.promise","es.promise.finally"],"core-js/stable/queue-microtask":["web.queue-microtask"],"core-js/stable/reflect":["es.reflect.apply","es.reflect.construct","es.reflect.define-property","es.reflect.delete-property","es.reflect.get","es.reflect.get-own-property-descriptor","es.reflect.get-prototype-of","es.reflect.has","es.reflect.is-extensible","es.reflect.own-keys","es.reflect.prevent-extensions","es.reflect.set","es.reflect.set-prototype-of"],"core-js/stable/reflect/apply":["es.reflect.apply"],"core-js/stable/reflect/construct":["es.reflect.construct"],"core-js/stable/reflect/define-property":["es.reflect.define-property"],"core-js/stable/reflect/delete-property":["es.reflect.delete-property"],"core-js/stable/reflect/get":["es.reflect.get"],"core-js/stable/reflect/get-own-property-descriptor":["es.reflect.get-own-property-descriptor"],"core-js/stable/reflect/get-prototype-of":["es.reflect.get-prototype-of"],"core-js/stable/reflect/has":["es.reflect.has"],"core-js/stable/reflect/is-extensible":["es.reflect.is-extensible"],"core-js/stable/reflect/own-keys":["es.reflect.own-keys"],"core-js/stable/reflect/prevent-extensions":["es.reflect.prevent-extensions"],"core-js/stable/reflect/set":["es.reflect.set"],"core-js/stable/reflect/set-prototype-of":["es.reflect.set-prototype-of"],"core-js/stable/regexp":["es.regexp.constructor","es.regexp.exec","es.regexp.flags","es.regexp.sticky","es.regexp.test","es.regexp.to-string","es.string.match","es.string.replace","es.string.search","es.string.split"],"core-js/stable/regexp/constructor":["es.regexp.constructor"],"core-js/stable/regexp/flags":["es.regexp.flags"],"core-js/stable/regexp/match":["es.string.match"],"core-js/stable/regexp/replace":["es.string.replace"],"core-js/stable/regexp/search":["es.string.search"],"core-js/stable/regexp/split":["es.string.split"],"core-js/stable/regexp/sticky":["es.regexp.sticky"],"core-js/stable/regexp/test":["es.regexp.exec","es.regexp.test"],"core-js/stable/regexp/to-string":["es.regexp.to-string"],"core-js/stable/set":["es.object.to-string","es.set","es.string.iterator","web.dom-collections.iterator"],"core-js/stable/set-immediate":["web.immediate"],"core-js/stable/set-interval":["web.timers"],"core-js/stable/set-timeout":["web.timers"],"core-js/stable/string":["es.regexp.exec","es.string.code-point-at","es.string.ends-with","es.string.from-code-point","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.raw","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup"],"core-js/stable/string/anchor":["es.string.anchor"],"core-js/stable/string/big":["es.string.big"],"core-js/stable/string/blink":["es.string.blink"],"core-js/stable/string/bold":["es.string.bold"],"core-js/stable/string/code-point-at":["es.string.code-point-at"],"core-js/stable/string/ends-with":["es.string.ends-with"],"core-js/stable/string/fixed":["es.string.fixed"],"core-js/stable/string/fontcolor":["es.string.fontcolor"],"core-js/stable/string/fontsize":["es.string.fontsize"],"core-js/stable/string/from-code-point":["es.string.from-code-point"],"core-js/stable/string/includes":["es.string.includes"],"core-js/stable/string/italics":["es.string.italics"],"core-js/stable/string/iterator":["es.string.iterator"],"core-js/stable/string/link":["es.string.link"],"core-js/stable/string/match":["es.regexp.exec","es.string.match"],"core-js/stable/string/match-all":["es.string.match-all"],"core-js/stable/string/pad-end":["es.string.pad-end"],"core-js/stable/string/pad-start":["es.string.pad-start"],"core-js/stable/string/raw":["es.string.raw"],"core-js/stable/string/repeat":["es.string.repeat"],"core-js/stable/string/replace":["es.regexp.exec","es.string.replace"],"core-js/stable/string/search":["es.regexp.exec","es.string.search"],"core-js/stable/string/small":["es.string.small"],"core-js/stable/string/split":["es.regexp.exec","es.string.split"],"core-js/stable/string/starts-with":["es.string.starts-with"],"core-js/stable/string/strike":["es.string.strike"],"core-js/stable/string/sub":["es.string.sub"],"core-js/stable/string/sup":["es.string.sup"],"core-js/stable/string/trim":["es.string.trim"],"core-js/stable/string/trim-end":["es.string.trim-end"],"core-js/stable/string/trim-left":["es.string.trim-start"],"core-js/stable/string/trim-right":["es.string.trim-end"],"core-js/stable/string/trim-start":["es.string.trim-start"],"core-js/stable/string/virtual":["es.string.code-point-at","es.string.ends-with","es.string.includes","es.string.iterator","es.string.match","es.string.match-all","es.string.pad-end","es.string.pad-start","es.string.repeat","es.string.replace","es.string.search","es.string.split","es.string.starts-with","es.string.trim","es.string.trim-end","es.string.trim-start","es.string.anchor","es.string.big","es.string.blink","es.string.bold","es.string.fixed","es.string.fontcolor","es.string.fontsize","es.string.italics","es.string.link","es.string.small","es.string.strike","es.string.sub","es.string.sup"],"core-js/stable/string/virtual/anchor":["es.string.anchor"],"core-js/stable/string/virtual/big":["es.string.big"],"core-js/stable/string/virtual/blink":["es.string.blink"],"core-js/stable/string/virtual/bold":["es.string.bold"],"core-js/stable/string/virtual/code-point-at":["es.string.code-point-at"],"core-js/stable/string/virtual/ends-with":["es.string.ends-with"],"core-js/stable/string/virtual/fixed":["es.string.fixed"],"core-js/stable/string/virtual/fontcolor":["es.string.fontcolor"],"core-js/stable/string/virtual/fontsize":["es.string.fontsize"],"core-js/stable/string/virtual/includes":["es.string.includes"],"core-js/stable/string/virtual/italics":["es.string.italics"],"core-js/stable/string/virtual/iterator":["es.string.iterator"],"core-js/stable/string/virtual/link":["es.string.link"],"core-js/stable/string/virtual/match-all":["es.string.match-all"],"core-js/stable/string/virtual/pad-end":["es.string.pad-end"],"core-js/stable/string/virtual/pad-start":["es.string.pad-start"],"core-js/stable/string/virtual/repeat":["es.string.repeat"],"core-js/stable/string/virtual/small":["es.string.small"],"core-js/stable/string/virtual/starts-with":["es.string.starts-with"],"core-js/stable/string/virtual/strike":["es.string.strike"],"core-js/stable/string/virtual/sub":["es.string.sub"],"core-js/stable/string/virtual/sup":["es.string.sup"],"core-js/stable/string/virtual/trim":["es.string.trim"],"core-js/stable/string/virtual/trim-end":["es.string.trim-end"],"core-js/stable/string/virtual/trim-left":["es.string.trim-start"],"core-js/stable/string/virtual/trim-right":["es.string.trim-end"],"core-js/stable/string/virtual/trim-start":["es.string.trim-start"],"core-js/stable/symbol":["es.symbol","es.symbol.description","es.symbol.async-iterator","es.symbol.has-instance","es.symbol.is-concat-spreadable","es.symbol.iterator","es.symbol.match","es.symbol.match-all","es.symbol.replace","es.symbol.search","es.symbol.species","es.symbol.split","es.symbol.to-primitive","es.symbol.to-string-tag","es.symbol.unscopables","es.array.concat","es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/stable/symbol/async-iterator":["es.symbol.async-iterator"],"core-js/stable/symbol/description":["es.symbol.description"],"core-js/stable/symbol/for":["es.symbol"],"core-js/stable/symbol/has-instance":["es.symbol.has-instance","es.function.has-instance"],"core-js/stable/symbol/is-concat-spreadable":["es.symbol.is-concat-spreadable","es.array.concat"],"core-js/stable/symbol/iterator":["es.symbol.iterator","es.string.iterator","web.dom-collections.iterator"],"core-js/stable/symbol/key-for":["es.symbol"],"core-js/stable/symbol/match":["es.symbol.match","es.string.match"],"core-js/stable/symbol/match-all":["es.symbol.match-all","es.string.match-all"],"core-js/stable/symbol/replace":["es.symbol.replace","es.string.replace"],"core-js/stable/symbol/search":["es.symbol.search","es.string.search"],"core-js/stable/symbol/species":["es.symbol.species"],"core-js/stable/symbol/split":["es.symbol.split","es.string.split"],"core-js/stable/symbol/to-primitive":["es.symbol.to-primitive"],"core-js/stable/symbol/to-string-tag":["es.symbol.to-string-tag","es.json.to-string-tag","es.math.to-string-tag","es.object.to-string"],"core-js/stable/symbol/unscopables":["es.symbol.unscopables"],"core-js/stable/typed-array":["es.object.to-string","es.typed-array.float32-array","es.typed-array.float64-array","es.typed-array.int8-array","es.typed-array.int16-array","es.typed-array.int32-array","es.typed-array.uint8-array","es.typed-array.uint8-clamped-array","es.typed-array.uint16-array","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/copy-within":["es.typed-array.copy-within"],"core-js/stable/typed-array/entries":["es.typed-array.iterator"],"core-js/stable/typed-array/every":["es.typed-array.every"],"core-js/stable/typed-array/fill":["es.typed-array.fill"],"core-js/stable/typed-array/filter":["es.typed-array.filter"],"core-js/stable/typed-array/find":["es.typed-array.find"],"core-js/stable/typed-array/find-index":["es.typed-array.find-index"],"core-js/stable/typed-array/float32-array":["es.object.to-string","es.typed-array.float32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/float64-array":["es.object.to-string","es.typed-array.float64-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/for-each":["es.typed-array.for-each"],"core-js/stable/typed-array/from":["es.typed-array.from"],"core-js/stable/typed-array/includes":["es.typed-array.includes"],"core-js/stable/typed-array/index-of":["es.typed-array.index-of"],"core-js/stable/typed-array/int16-array":["es.object.to-string","es.typed-array.int16-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/int32-array":["es.object.to-string","es.typed-array.int32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/int8-array":["es.object.to-string","es.typed-array.int8-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/iterator":["es.typed-array.iterator"],"core-js/stable/typed-array/join":["es.typed-array.join"],"core-js/stable/typed-array/keys":["es.typed-array.iterator"],"core-js/stable/typed-array/last-index-of":["es.typed-array.last-index-of"],"core-js/stable/typed-array/map":["es.typed-array.map"],"core-js/stable/typed-array/of":["es.typed-array.of"],"core-js/stable/typed-array/reduce":["es.typed-array.reduce"],"core-js/stable/typed-array/reduce-right":["es.typed-array.reduce-right"],"core-js/stable/typed-array/reverse":["es.typed-array.reverse"],"core-js/stable/typed-array/set":["es.typed-array.set"],"core-js/stable/typed-array/slice":["es.typed-array.slice"],"core-js/stable/typed-array/some":["es.typed-array.some"],"core-js/stable/typed-array/sort":["es.typed-array.sort"],"core-js/stable/typed-array/subarray":["es.typed-array.subarray"],"core-js/stable/typed-array/to-locale-string":["es.typed-array.to-locale-string"],"core-js/stable/typed-array/to-string":["es.typed-array.to-string"],"core-js/stable/typed-array/uint16-array":["es.object.to-string","es.typed-array.uint16-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/uint32-array":["es.object.to-string","es.typed-array.uint32-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/uint8-array":["es.object.to-string","es.typed-array.uint8-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/uint8-clamped-array":["es.object.to-string","es.typed-array.uint8-clamped-array","es.typed-array.copy-within","es.typed-array.every","es.typed-array.fill","es.typed-array.filter","es.typed-array.find","es.typed-array.find-index","es.typed-array.for-each","es.typed-array.from","es.typed-array.includes","es.typed-array.index-of","es.typed-array.iterator","es.typed-array.join","es.typed-array.last-index-of","es.typed-array.map","es.typed-array.of","es.typed-array.reduce","es.typed-array.reduce-right","es.typed-array.reverse","es.typed-array.set","es.typed-array.slice","es.typed-array.some","es.typed-array.sort","es.typed-array.subarray","es.typed-array.to-locale-string","es.typed-array.to-string"],"core-js/stable/typed-array/values":["es.typed-array.iterator"],"core-js/stable/url":["web.url","web.url.to-json","web.url-search-params"],"core-js/stable/url-search-params":["web.url-search-params"],"core-js/stable/url/to-json":["web.url.to-json"],"core-js/stable/weak-map":["es.object.to-string","es.weak-map","web.dom-collections.iterator"],"core-js/stable/weak-set":["es.object.to-string","es.weak-set","web.dom-collections.iterator"],"core-js/stage":["esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.url","web.url.to-json","web.url-search-params"],"core-js/stage/0":["esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.url","web.url.to-json","web.url-search-params"],"core-js/stage/1":["esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of"],"core-js/stage/2":["esnext.aggregate-error","esnext.array.is-template-object","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.update-or-insert","esnext.map.upsert","esnext.promise.all-settled","esnext.promise.any","esnext.set.difference","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.symmetric-difference","esnext.set.union","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.replace-all","esnext.weak-map.upsert"],"core-js/stage/3":["esnext.aggregate-error","esnext.global-this","esnext.promise.all-settled","esnext.promise.any","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.replace-all"],"core-js/stage/4":["esnext.global-this","esnext.promise.all-settled","esnext.string.match-all"],"core-js/stage/pre":["esnext.aggregate-error","esnext.array.is-template-object","esnext.array.last-index","esnext.array.last-item","esnext.async-iterator.constructor","esnext.async-iterator.as-indexed-pairs","esnext.async-iterator.drop","esnext.async-iterator.every","esnext.async-iterator.filter","esnext.async-iterator.find","esnext.async-iterator.flat-map","esnext.async-iterator.for-each","esnext.async-iterator.from","esnext.async-iterator.map","esnext.async-iterator.reduce","esnext.async-iterator.some","esnext.async-iterator.take","esnext.async-iterator.to-array","esnext.composite-key","esnext.composite-symbol","esnext.global-this","esnext.iterator.constructor","esnext.iterator.as-indexed-pairs","esnext.iterator.drop","esnext.iterator.every","esnext.iterator.filter","esnext.iterator.find","esnext.iterator.flat-map","esnext.iterator.for-each","esnext.iterator.from","esnext.iterator.map","esnext.iterator.reduce","esnext.iterator.some","esnext.iterator.take","esnext.iterator.to-array","esnext.map.delete-all","esnext.map.every","esnext.map.filter","esnext.map.find","esnext.map.find-key","esnext.map.from","esnext.map.group-by","esnext.map.includes","esnext.map.key-by","esnext.map.key-of","esnext.map.map-keys","esnext.map.map-values","esnext.map.merge","esnext.map.of","esnext.map.reduce","esnext.map.some","esnext.map.update","esnext.map.update-or-insert","esnext.map.upsert","esnext.math.clamp","esnext.math.deg-per-rad","esnext.math.degrees","esnext.math.fscale","esnext.math.iaddh","esnext.math.imulh","esnext.math.isubh","esnext.math.rad-per-deg","esnext.math.radians","esnext.math.scale","esnext.math.seeded-prng","esnext.math.signbit","esnext.math.umulh","esnext.number.from-string","esnext.object.iterate-entries","esnext.object.iterate-keys","esnext.object.iterate-values","esnext.observable","esnext.promise.all-settled","esnext.promise.any","esnext.promise.try","esnext.reflect.define-metadata","esnext.reflect.delete-metadata","esnext.reflect.get-metadata","esnext.reflect.get-metadata-keys","esnext.reflect.get-own-metadata","esnext.reflect.get-own-metadata-keys","esnext.reflect.has-metadata","esnext.reflect.has-own-metadata","esnext.reflect.metadata","esnext.set.add-all","esnext.set.delete-all","esnext.set.difference","esnext.set.every","esnext.set.filter","esnext.set.find","esnext.set.from","esnext.set.intersection","esnext.set.is-disjoint-from","esnext.set.is-subset-of","esnext.set.is-superset-of","esnext.set.join","esnext.set.map","esnext.set.of","esnext.set.reduce","esnext.set.some","esnext.set.symmetric-difference","esnext.set.union","esnext.string.at","esnext.string.code-points","esnext.string.match-all","esnext.string.replace-all","esnext.symbol.async-dispose","esnext.symbol.dispose","esnext.symbol.observable","esnext.symbol.pattern-match","esnext.symbol.replace-all","esnext.weak-map.delete-all","esnext.weak-map.from","esnext.weak-map.of","esnext.weak-map.upsert","esnext.weak-set.add-all","esnext.weak-set.delete-all","esnext.weak-set.from","esnext.weak-set.of","web.url","web.url.to-json","web.url-search-params"],"core-js/web":["web.dom-collections.for-each","web.dom-collections.iterator","web.immediate","web.queue-microtask","web.timers","web.url","web.url.to-json","web.url-search-params"],"core-js/web/dom-collections":["web.dom-collections.for-each","web.dom-collections.iterator"],"core-js/web/immediate":["web.immediate"],"core-js/web/queue-microtask":["web.queue-microtask"],"core-js/web/timers":["web.timers"],"core-js/web/url":["web.url","web.url.to-json","web.url-search-params"],"core-js/web/url-search-params":["web.url-search-params"]};function GV(e){return"@babel/polyfill"===e||"babel-polyfill"===e}function VV(e){return"string"==typeof e&&(e=e.replace(/\\/g,"/").replace(/(\/(index)?)?(\.js)?$/i,"").toLowerCase()),xG(UV,e)&&UV[e]}var WV="\n `@babel/polyfill` is deprecated. Please, use required parts of `core-js`\n and `regenerator-runtime/runtime` separately";function HV(e,t){var r=t.corejs,n=t.include,a=t.exclude,s=t.polyfillTargets,i=t.debug,o=bU(IU,n,a,s,null),u=new Set(hV(r.version));function c(e,t){return!!t&&(1!==t.length||!o.has(t[0])||!u.has(t[0])||TG(t[0])!==e)}return{name:"corejs3-entry",visitor:{ImportDeclaration:function(e){var t=wG(e);if(t)if(GV(t))console.warn(WV);else{var r=VV(t);c(t,r)&&this.replaceBySeparateModulesImport(e,r)}},Program:{enter:function(e){var t=this;e.get("body").forEach((function(e){var r=SG(e);if(r)if(GV(r))console.warn(WV);else{var n=VV(r);c(r,n)&&t.replaceBySeparateModulesImport(e,n)}}))},exit:function(e){var t=this,r=AG(o,this.polyfillsSet,u),n=Array.from(r).reverse(),a=Array.isArray(n),s=0;for(n=a?n:n[Symbol.iterator]();;){var i;if(a){if(s>=n.length)break;i=n[s++]}else{if((s=n.next()).done)break;i=s.value}var c=i;this.injectedPolyfills.has(c)||jG(e,c)}r.forEach((function(e){return t.injectedPolyfills.add(e)}))}}},pre:function(){this.injectedPolyfills=new Set,this.polyfillsSet=new Set,this.replaceBySeparateModulesImport=function(e,t){var r=t,n=Array.isArray(r),a=0;for(r=n?r:r[Symbol.iterator]();;){var s;if(n){if(a>=r.length)break;s=r[a++]}else{if((a=r.next()).done)break;s=a.value}var i=s;this.polyfillsSet.add(i)}e.remove()}},post:function(){i&&PU("core-js",this.injectedPolyfills.size>0,this.injectedPolyfills,this.file.opts.filename,s,IU)}}}function qV(e){return"regenerator-runtime/runtime"===e}function KV(){return{name:"regenerator-entry",visitor:{ImportDeclaration:function(e){qV(wG(e))&&(this.regeneratorImportExcluded=!0,e.remove())},Program:function(e){var t=this;e.get("body").forEach((function(e){qV(SG(e))&&(t.regeneratorImportExcluded=!0,e.remove())}))}},pre:function(){this.regeneratorImportExcluded=!1},post:function(){if(this.opts.debug&&this.regeneratorImportExcluded){var e=this.file.opts.filename;"test"===es.env.BABEL_ENV&&(e=e.replace(/\\/g,"/")),console.log("\n["+e+"] Based on your targets, regenerator-runtime import excluded.")}}}}var zV=function(e,t){return Object.keys(e).reduce((function(r,n){return t[n]||(r[n]=e[n]),r}),{})}(KU,dG),XV=function(e){var t=qU[e];if(!t)throw new Error('Could not find plugin "'+e+'". Ensure there is an entry in ./available-plugins.js for it.');return t},YV=function(e){return e.reduce((function(e,t){return e[t.match(/^(es|es6|es7|esnext|web)\./)?"builtIns":"plugins"].add(t),e}),{all:e,plugins:new Set,builtIns:new Set})};function JV(e){return!(!e||!e.supportsStaticESM)}function $V(e){return!(!e||!e.supportsDynamicImport)}function QV(e){return!(!e||!e.supportsTopLevelAwait)}var ZV,eW=sP((function(e,t){e.assertVersion(7);var r=oG(t),n=r.configPath,a=r.debug,s=r.exclude,i=r.forceAllTransforms,o=r.ignoreBrowserslistConfig,u=r.include,c=r.loose,l=r.modules,p=r.shippedProposals,d=r.spec,f=r.targets,h=r.useBuiltIns,m=r.corejs,y=m.version,g=m.proposals,v=!1;f&&f.uglify&&(v=!0,delete f.uglify,console.log(""),console.log("The uglify target has been deprecated. Set the top level"),console.log("option `forceAllTransforms: true` instead."),console.log("")),f&&f.esmodules&&f.browsers&&(console.log(""),console.log("@babel/preset-env: esmodules and browsers targets have been specified together."),console.log("`browsers` target, `"+f.browsers+"` will be ignored."),console.log(""));var b,x,E=CU(f,{ignoreBrowserslistConfig:o,configPath:n}),A=YV(u),w=YV(s),S=i||v?{}:E,D=function(e){var t=e.modules,r=e.transformations,n=e.shouldTransformESM,a=e.shouldTransformDynamicImport,s=e.shouldParseTopLevelAwait,i=[];return!1!==t&&r[t]?(n&&i.push(r[t]),a&&n&&"umd"!==t?i.push("proposal-dynamic-import"):(a&&console.warn("Dynamic import can only be supported when transforming ES modules to AMD, CommonJS or SystemJS. Only the parser plugin will be enabled."),i.push("syntax-dynamic-import"))):i.push("syntax-dynamic-import"),s&&i.push("syntax-top-level-await"),i}({modules:l,transformations:_U,shouldTransformESM:"auto"!==l||!e.caller||!e.caller(JV),shouldTransformDynamicImport:"auto"!==l||!e.caller||!e.caller($V),shouldParseTopLevelAwait:!e.caller||e.caller(QV)}),C=bU(p?KU:zV,A.plugins,w.plugins,S,D,{loose:c}.loose?FU:null,pG);x=fG,(b=C).forEach((function(e){var t;null==(t=x[e])||t.forEach((function(e){return b.delete(e)}))}));var T=function(e){var t=e.useBuiltIns,r=e.corejs,n=e.polyfillTargets,a=e.include,s=e.exclude,i=e.proposals,o=e.shippedProposals,u=e.regenerator,c=e.debug,l=[];if("usage"===t||"entry"===t){var p={corejs:r,polyfillTargets:n,include:a,exclude:s,proposals:i,shippedProposals:o,regenerator:u,debug:c};r&&("usage"===t?(2===r.major?l.push([FG,p]):l.push([RV,p]),u&&l.push([MV,p])):2===r.major?l.push([LV,p]):(l.push([HV,p]),u||l.push([KV,p])))}return l}({useBuiltIns:h,corejs:y,polyfillTargets:E,include:A.builtIns,exclude:w.builtIns,proposals:g,shippedProposals:p,regenerator:C.has("transform-regenerator"),debug:a}),j=!1!==h,P=Array.from(C).map((function(e){return[XV(e),{spec:d,loose:c,useBuiltIns:j}]})).concat(T);return a&&(console.log("@babel/preset-env: `DEBUG` option"),console.log("\nUsing targets:"),console.log(JSON.stringify(function(e){return Object.keys(e).reduce((function(t,r){var n=e[r];return"string"==typeof n&&uU[r]!==n&&(n=yU(n)),t[r]=n,t}),{})}(E),null,2)),console.log("\nUsing modules transform: "+l.toString()),console.log("\nUsing plugins:"),C.forEach((function(e){jU(e,E,KU)})),h?console.log("\nUsing polyfills with `"+h+"` option:"):console.log("\nUsing polyfills: No polyfills were added, since the `useBuiltIns` option was not set.")),{plugins:P}})),tW=sP((function(e,t){var r=t.all;return e.assertVersion(7),{plugins:[[GB,{all:r}]]}})),rW=sP((function(e,t){e.assertVersion(7);var r=t.pragma||"React.createElement",n=t.pragmaFrag||"React.Fragment",a=void 0===t.throwIfNamespace||!!t.throwIfNamespace,s=!!t.development,i=!!t.useBuiltIns,o=t.useSpread;if("boolean"!=typeof s)throw new Error("@babel/preset-react 'development' option must be a boolean.");return{plugins:[[DN,{pragma:r,pragmaFrag:n,throwIfNamespace:a,useBuiltIns:i,useSpread:o}],AN,s&&jN,s&&TN].filter(Boolean)}})),nW=sP((function(e,t){var r=t.jsxPragma,n=t.allExtensions,a=void 0!==n&&n,s=t.isTSX,i=void 0!==s&&s,o=t.allowNamespaces,u=t.allowDeclareFields;if(e.assertVersion(7),"boolean"!=typeof a)throw new Error(".allExtensions must be a boolean, or undefined");if("boolean"!=typeof i)throw new Error(".isTSX must be a boolean, or undefined");if(i&&!a)throw new Error("isTSX:true requires allExtensions:true");var c=function(e){return{jsxPragma:r,isTSX:e,allowNamespaces:o,allowDeclareFields:u}};return{overrides:a?[{plugins:[[XM,c(i)]]}]:[{test:/\.ts$/,plugins:[[XM,c(!1)]]},{test:/\.tsx$/,plugins:[[XM,c(!0)]]}]}})),aW=["text/jsx","text/babel"],sW=0;function iW(e,t){var r=document.createElement("script");r.text=function(e,t){var r;return null!=t.url?r=t.url:(r="Inline Babel script",++sW>1&&(r+=" ("+sW+")")),e(t.content,function(e,t){return{filename:t,presets:e.presets||["react","es2015"],plugins:e.plugins||["proposal-class-properties","proposal-object-rest-spread","transform-flow-strip-types"],sourceMaps:"inline",sourceFileName:t}}(t,r)).code}(e,t),ZV.appendChild(r)}function oW(e,t){var r=e.getAttribute(t);return""===r?[]:r?r.split(",").map((function(e){return e.trim()})):null}function uW(e,t){var r=[],n=t.length;function a(){var t,a;for(a=0;a<n;a++)if((t=r[a]).loaded&&!t.executed)t.executed=!0,iW(e,t);else if(!t.loaded&&!t.error&&!t.async)break}t.forEach((function(e,t){var n={async:e.hasAttribute("async"),error:!1,executed:!1,plugins:oW(e,"data-plugins"),presets:oW(e,"data-presets")};e.src?(r[t]=Object.assign({},n,{content:null,loaded:!1,url:e.src}),function(e,t,r){var n=new XMLHttpRequest;n.open("GET",e,!0),"overrideMimeType"in n&&n.overrideMimeType("text/plain"),n.onreadystatechange=function(){if(4===n.readyState){if(0!==n.status&&200!==n.status)throw r(),new Error("Could not load "+e);t(n.responseText)}},n.send(null)}(e.src,(function(e){r[t].loaded=!0,r[t].content=e,a()}),(function(){r[t].error=!0,a()}))):r[t]=Object.assign({},n,{content:e.innerHTML,loaded:!0,url:e.getAttribute("data-module")||null})})),a()}var cW=Array.isArray||function(e){return"[object Array]"===Object.prototype.toString.call(e)};function lW(e,t){return cW(t)&&"string"==typeof t[0]?Object.prototype.hasOwnProperty.call(e,t[0])?[e[t[0]]].concat(t.slice(1)):void 0:"string"==typeof t?e[t]:t}function pW(e){var t=(e.presets||[]).map((function(e){var t=lW(hW,e);if(!t)throw new Error('Invalid preset specified in Babel options: "'+e+'"');return cW(t)&&"object"==typeof t[0]&&Object.prototype.hasOwnProperty.call(t[0],"buildPreset")&&(t[0]=Object.assign({},t[0],{buildPreset:t[0].buildPreset})),t})),r=(e.plugins||[]).map((function(e){var t=lW(fW,e);if(!t)throw new Error('Invalid plugin specified in Babel options: "'+e+'"');return t}));return Object.assign({babelrc:!1},e,{presets:t,plugins:r})}function dW(e,t){return Vj(e,pW(t))}var fW={},hW={},mW=Ww;function yW(e,t){Object.prototype.hasOwnProperty.call(fW,e)&&console.warn('A plugin named "'+e+'" is already registered, it will be overridden'),fW[e]=t}function gW(e){Object.keys(e).forEach((function(t){return yW(t,e[t])}))}function vW(e,t){Object.prototype.hasOwnProperty.call(hW,e)&&("env"===e?console.warn("@babel/preset-env is now included in @babel/standalone, please remove @babel/preset-env-standalone"):console.warn('A preset named "'+e+'" is already registered, it will be overridden')),hW[e]=t}function bW(e){Object.keys(e).forEach((function(t){return vW(t,e[t])}))}gW(JM),bW({env:eW,es2015:$M,es2016:function(){return{plugins:[fW["transform-exponentiation-operator"]]}},es2017:function(){return{plugins:[fW["transform-async-to-generator"]]}},react:rW,"stage-0":function(e,t){void 0===t&&(t={});var r=t,n=r.loose,a=void 0!==n&&n,s=r.useBuiltIns,i=void 0!==s&&s,o=r.decoratorsLegacy,u=void 0!==o&&o,c=r.decoratorsBeforeExport,l=r.pipelineProposal;return{presets:[[eL,{loose:a,useBuiltIns:i,decoratorsLegacy:u,decoratorsBeforeExport:c,pipelineProposal:void 0===l?"minimal":l}]],plugins:[gF]}},"stage-1":eL,"stage-2":ZM,"stage-3":QM,"es2015-loose":{presets:[[$M,{loose:!0}]]},"es2015-no-commonjs":{presets:[[$M,{modules:!1}]]},typescript:nW,flow:tW});function xW(){EW()}function EW(e){!function(e,t){ZV=document.getElementsByTagName("head")[0],t||(t=document.getElementsByTagName("script"));for(var r=[],n=0;n<t.length;n++){var a=t.item(n),s=a.type.split(";")[0];-1!==aW.indexOf(s)&&r.push(a)}0!==r.length&&(console.warn("You are using the in-browser Babel transformer. Be sure to precompile your scripts for production - https://babeljs.io/docs/setup/"),uW(e,r))}(dW,e)}"undefined"!=typeof window&&window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",xW,!1),e.availablePlugins=fW,e.availablePresets=hW,e.buildExternalHelpers=mW,e.disableScriptTags=function(){window.removeEventListener("DOMContentLoaded",xW)},e.registerPlugin=yW,e.registerPlugins=gW,e.registerPreset=vW,e.registerPresets=bW,e.transform=dW,e.transformFromAst=function(e,t,r){return Yj(e,t,pW(r))},e.transformScriptTags=EW,e.version="7.8.7",Object.defineProperty(e,"__esModule",{value:!0})}));
diff --git a/devtools/client/shared/build/build-debugger.js b/devtools/client/shared/build/build-debugger.js
new file mode 100644
index 0000000000..6cb964bc05
--- /dev/null
+++ b/devtools/client/shared/build/build-debugger.js
@@ -0,0 +1,103 @@
+/* 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/>. */
+
+"use strict";
+
+const Babel = require("./babel");
+const _path = require("path");
+
+function isRequire(t, node) {
+ return node && t.isCallExpression(node) && node.callee.name == "require";
+}
+
+function shouldLazyLoad(value) {
+ return (
+ !value.includes("codemirror/") &&
+ !value.endsWith(".properties") &&
+ !value.startsWith("devtools/") &&
+ !value.startsWith("resource://devtools/") &&
+ // XXX: the lazyRequire rewriter (in transformMC) fails for this module, it
+ // evaluates `t.thisExpression()` as `void 0` instead of `this`. But the
+ // rewriter still works for other call sites and seems mandatory for the
+ // debugger to start successfully (lazy requires help to break circular
+ // dependencies).
+ value !== "resource://gre/modules/AppConstants.jsm"
+ );
+}
+
+/**
+ * This Babel plugin is used to transpile a single Debugger module into a module that
+ * can be loaded in Firefox via the regular DevTools loader.
+ */
+function transformMC({ types: t }) {
+ return {
+ visitor: {
+ StringLiteral(path, state) {
+ const { filePath } = state.opts;
+ let value = path.node.value;
+
+ if (!isRequire(t, path.parent)) {
+ return;
+ }
+
+ if (shouldLazyLoad(value)) {
+ const requireCall = path.parentPath;
+ const declarator = requireCall.parentPath;
+ const declaration = declarator.parentPath;
+
+ // require()s that are not assigned to a variable cannot be safely lazily required
+ // since we lack anything to initiate the require (= the getter for the variable)
+ if (declarator.type !== "VariableDeclarator") {
+ return;
+ }
+
+ // update relative paths to be "absolute" (starting with devtools/)
+ // e.g. ./utils/source-queue
+ if (value.startsWith(".")) {
+ // Create full path
+ // e.g. z:\build\build\src\devtools\client\debugger\src\utils\source-queue
+ let newValue = _path.join(_path.dirname(filePath), value);
+
+ // Select the devtools portion of the path
+ // e.g. devtools\client\debugger\src\utils\source-queue
+ if (!newValue.startsWith("devtools")) {
+ newValue = newValue.match(/^(.*?)(devtools.*)/)[2];
+ }
+
+ // Replace forward slashes with back slashes
+ // e.g devtools/client/debugger/src/utils/source-queue
+ newValue = newValue.replace(/\\/g, "/");
+
+ value = newValue;
+ }
+
+ // rewrite to: loader.lazyRequireGetter(this, "variableName", "pathToFile")
+ const lazyRequire = t.callExpression(
+ t.memberExpression(
+ t.identifier("loader"),
+ t.identifier("lazyRequireGetter")
+ ),
+ [
+ t.thisExpression(),
+ t.stringLiteral(declarator.node.id.name || ""),
+ t.stringLiteral(value),
+ ]
+ );
+
+ declaration.replaceWith(lazyRequire);
+ }
+ },
+ },
+ };
+}
+
+Babel.registerPlugin("transform-mc", transformMC);
+
+module.exports = function (filePath) {
+ return [
+ "proposal-class-properties",
+ "transform-modules-commonjs",
+ ["transform-mc", { filePath }],
+ ];
+};
diff --git a/devtools/client/shared/build/build.js b/devtools/client/shared/build/build.js
new file mode 100644
index 0000000000..7bb00d5145
--- /dev/null
+++ b/devtools/client/shared/build/build.js
@@ -0,0 +1,78 @@
+/* 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/>. */
+/* globals process, __filename, __dirname */
+
+/* Usage: node build.js [LIST_OF_SOURCE_FILES...] OUTPUT_DIR
+ * Compiles all source files and places the results of the compilation in
+ * OUTPUT_DIR.
+ */
+
+"use strict";
+
+const Babel = require("./babel");
+const fs = require("fs");
+const _path = require("path");
+
+const defaultPlugins = ["proposal-class-properties"];
+
+function transform(filePath) {
+ // Use the extra plugins only for the debugger
+ const plugins = filePath.includes("devtools/client/debugger")
+ ? require("./build-debugger")(filePath)
+ : defaultPlugins;
+
+ const doc = fs.readFileSync(filePath, "utf8");
+
+ let out;
+ try {
+ out = Babel.transform(doc, { plugins });
+ } catch (err) {
+ throw new Error(`
+========================
+NODE COMPILATION ERROR!
+
+File: ${filePath}
+Stack:
+
+${err.stack}
+
+========================
+`);
+ }
+
+ return out.code;
+}
+
+// fs.mkdirSync's "recursive" option appears not to work, so I'm writing a
+// simple version of the function myself.
+function mkdirs(filePath) {
+ if (fs.existsSync(filePath)) {
+ return;
+ }
+ mkdirs(_path.dirname(filePath));
+ try {
+ fs.mkdirSync(filePath);
+ } catch (err) {
+ // Ignore any errors resulting from the directory already existing.
+ if (err.code != "EEXIST") {
+ throw err;
+ }
+ }
+}
+
+const deps = [__filename, _path.resolve(__dirname, "babel.js")];
+const outputDir = process.argv[process.argv.length - 1];
+mkdirs(outputDir);
+
+for (let i = 2; i < process.argv.length - 1; i++) {
+ const srcPath = process.argv[i];
+ const code = transform(srcPath);
+ const fullPath = _path.join(outputDir, _path.basename(srcPath));
+ fs.writeFileSync(fullPath, code);
+ deps.push(srcPath);
+}
+
+// Print all dependencies prefixed with 'dep:' in order to help node.py, the script that
+// calls this module, to report back the precise list of all dependencies.
+console.log(deps.map(file => "dep:" + file).join("\n"));
diff --git a/devtools/client/shared/build/node-templates.mozbuild b/devtools/client/shared/build/node-templates.mozbuild
new file mode 100644
index 0000000000..a45d12e3d0
--- /dev/null
+++ b/devtools/client/shared/build/node-templates.mozbuild
@@ -0,0 +1,36 @@
+# vim: set filetype=python:
+# 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/.
+
+
+@template
+def CompiledModules(*modules):
+ compiled_directory_whitelist = ("devtools/client/debugger/src",)
+
+ if not RELATIVEDIR.startswith(compiled_directory_whitelist):
+ error(
+ "File in directory provided to CompiledModules not allowed: " + RELATIVEDIR
+ )
+
+ # HACK. Template export() propagation is janky so we have to re-implement the
+ # logic for computing FINAL_TARGET from scratch. Here we emulate the
+ # DIST_SUBDIR export in devtools/moz.build.
+ if CONFIG["MOZ_BUILD_APP"] == "browser":
+ final_target = "/dist/bin/browser"
+ else:
+ final_target = "/dist/bin"
+
+ final = "/".join([final_target, "chrome/devtools/modules", RELATIVEDIR])
+ # For the same reason as https://searchfox.org/mozilla-central/source/mobile/android/base/moz.build#180-184
+ # we have to insert a first entry as recursivemake overrides the first entry and we end up with empty files
+ # for the first file only.
+ GeneratedFile(
+ "node.stub",
+ *[final + "/" + module for module in modules],
+ script="/python/mozbuild/mozbuild/action/node.py",
+ entry_point="generate",
+ inputs=["/devtools/client/shared/build/build.js"]
+ + [module for module in modules],
+ flags=["/".join([TOPOBJDIR, final])]
+ )
diff --git a/devtools/client/shared/classnames.js b/devtools/client/shared/classnames.js
new file mode 100644
index 0000000000..e10b256b03
--- /dev/null
+++ b/devtools/client/shared/classnames.js
@@ -0,0 +1,40 @@
+/* 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/. */
+"use strict";
+
+/**
+ * Take any number of parameters and returns a space-concatenated string.
+ * If a parameter is a non-empty string, it's automatically added to the result.
+ * If a parameter is an object, for each entry, if the value is truthy, then the key
+ * is added to the result.
+ *
+ * For example: `classnames("hi", null, undefined, false, { foo: true, bar: false })` will
+ * return `"hi foo"`
+ *
+ *
+ * @param {...string|object} argss
+ * @returns String
+ */
+module.exports = function (...args) {
+ let className = "";
+
+ for (const arg of args) {
+ if (!arg) {
+ continue;
+ }
+
+ if (typeof arg == "string") {
+ className += " " + arg;
+ } else if (Object(arg) === arg) {
+ // We don't test that we have an Object literal, so we can be as fast as we can
+ for (const key in arg) {
+ if (arg[key]) {
+ className += " " + key;
+ }
+ }
+ }
+ }
+
+ return className.trim();
+};
diff --git a/devtools/client/shared/components/.eslintrc.js b/devtools/client/shared/components/.eslintrc.js
new file mode 100644
index 0000000000..b67123ad2c
--- /dev/null
+++ b/devtools/client/shared/components/.eslintrc.js
@@ -0,0 +1,11 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ globals: {
+ define: true,
+ },
+};
diff --git a/devtools/client/shared/components/Accordion.css b/devtools/client/shared/components/Accordion.css
new file mode 100644
index 0000000000..4941d09537
--- /dev/null
+++ b/devtools/client/shared/components/Accordion.css
@@ -0,0 +1,89 @@
+/* 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/. */
+
+/* Accordion */
+
+.accordion {
+ width: 100%;
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+ /* Accordion root has tabindex="-1" to get focus programatically.
+ * This can give it a focus outline when clicked, which we don't want.
+ * The container itself is not in the focus order at all. */
+ outline: none;
+ background-color: var(--theme-sidebar-background);
+}
+
+.accordion-header {
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ /* Reserve 1px for the border */
+ min-height: calc(var(--theme-toolbar-height) + 1px);
+ margin: 0;
+ border-bottom: 1px solid var(--theme-splitter-color);
+ padding: 2px 4px;
+ font-size: inherit;
+ font-weight: normal;
+ user-select: none;
+ cursor: default;
+ background-color: var(--theme-accordion-header-background);
+ /* Adjust outline to make it visible */
+ outline-offset: -2px;
+}
+
+.accordion-header:hover {
+ background-color: var(--theme-accordion-header-hover);
+}
+
+/*
+ Arrow should be a bit closer to the text than to the start edge:
+ - total distance between text and start edge = 20px
+ - arrow width = 10px
+ - distance between arrow and start edge = 6px
+ - distance between arrow and text = 4px
+*/
+.accordion-header .theme-twisty {
+ display: inline-block;
+ flex: none;
+ width: 10px;
+ height: 10px;
+ margin-inline-start: 2px;
+ margin-inline-end: 4px;
+ pointer-events: none;
+}
+
+.accordion-header-label {
+ display: block;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-size: 12px;
+ line-height: 16px;
+ color: var(--theme-toolbar-color);
+}
+
+.accordion-header-buttons {
+ flex: none;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ max-width: 50%;
+ margin-inline-start: auto;
+ padding-inline-start: 4px;
+}
+
+.accordion-content {
+ overflow: auto;
+ border-bottom: 1px solid var(--theme-splitter-color);
+}
+
+.accordion-content[hidden] {
+ display: none;
+}
+
+.accordion-item:last-child > .accordion-content {
+ border-bottom: none;
+}
diff --git a/devtools/client/shared/components/Accordion.js b/devtools/client/shared/components/Accordion.js
new file mode 100644
index 0000000000..c3f1afa418
--- /dev/null
+++ b/devtools/client/shared/components/Accordion.js
@@ -0,0 +1,257 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ Component,
+ createElement,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const {
+ ul,
+ li,
+ h2,
+ div,
+ span,
+} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+class Accordion extends Component {
+ static get propTypes() {
+ return {
+ className: PropTypes.string,
+ // A list of all items to be rendered using an Accordion component.
+ items: PropTypes.arrayOf(
+ PropTypes.shape({
+ buttons: PropTypes.arrayOf(PropTypes.object),
+ className: PropTypes.string,
+ component: PropTypes.oneOfType([PropTypes.object, PropTypes.func]),
+ componentProps: PropTypes.object,
+ contentClassName: PropTypes.string,
+ header: PropTypes.string.isRequired,
+ id: PropTypes.string.isRequired,
+ onToggle: PropTypes.func,
+ // Determines the initial open state of the accordion item
+ opened: PropTypes.bool.isRequired,
+ // Enables dynamically changing the open state of the accordion
+ // on update.
+ shouldOpen: PropTypes.func,
+ })
+ ).isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ opened: {},
+ };
+
+ this.onHeaderClick = this.onHeaderClick.bind(this);
+ this.onHeaderKeyDown = this.onHeaderKeyDown.bind(this);
+ this.setInitialState = this.setInitialState.bind(this);
+ this.updateCurrentState = this.updateCurrentState.bind(this);
+ }
+
+ componentDidMount() {
+ this.setInitialState();
+ }
+
+ componentDidUpdate(prevProps) {
+ if (prevProps.items !== this.props.items) {
+ this.updateCurrentState();
+ }
+ }
+
+ setInitialState() {
+ /**
+ * Add initial data to the `state.opened` map.
+ * This happens only on initial mount of the accordion.
+ */
+ const newItems = this.props.items.filter(
+ ({ id }) => typeof this.state.opened[id] !== "boolean"
+ );
+
+ if (newItems.length) {
+ const everOpened = { ...this.state.everOpened };
+ const opened = { ...this.state.opened };
+ for (const item of newItems) {
+ everOpened[item.id] = item.opened;
+ opened[item.id] = item.opened;
+ }
+
+ this.setState({ everOpened, opened });
+ }
+ }
+
+ updateCurrentState() {
+ /**
+ * Updates the `state.opened` map based on the
+ * new items that have been added and those that
+ * `item.shouldOpen()` has changed. This happens
+ * on each update.
+ */
+ const updatedItems = this.props.items.filter(item => {
+ const notExist = typeof this.state.opened[item.id] !== "boolean";
+ if (typeof item.shouldOpen == "function") {
+ const currentState = this.state.opened[item.id];
+ return notExist || currentState !== item.shouldOpen(item, currentState);
+ }
+ return notExist;
+ });
+
+ if (updatedItems.length) {
+ const everOpened = { ...this.state.everOpened };
+ const opened = { ...this.state.opened };
+ for (const item of updatedItems) {
+ let itemOpen = item.opened;
+ if (typeof item.shouldOpen == "function") {
+ itemOpen = item.shouldOpen(item, itemOpen);
+ }
+ everOpened[item.id] = itemOpen;
+ opened[item.id] = itemOpen;
+ }
+ this.setState({ everOpened, opened });
+ }
+ }
+
+ /**
+ * @param {Event} event Click event.
+ * @param {Object} item The item to be collapsed/expanded.
+ */
+ onHeaderClick(event, item) {
+ event.preventDefault();
+ // In the Browser Toolbox's Inspector/Layout view, handleHeaderClick is
+ // called twice unless we call stopPropagation, making the accordion item
+ // open-and-close or close-and-open
+ event.stopPropagation();
+ this.toggleItem(item);
+ }
+
+ /**
+ * @param {Event} event Keyboard event.
+ * @param {Object} item The item to be collapsed/expanded.
+ */
+ onHeaderKeyDown(event, item) {
+ if (event.key === " " || event.key === "Enter") {
+ event.preventDefault();
+ this.toggleItem(item);
+ }
+ }
+
+ /**
+ * Expand or collapse an accordion list item.
+ * @param {Object} item The item to be collapsed or expanded.
+ */
+ toggleItem(item) {
+ const opened = !this.state.opened[item.id];
+
+ this.setState({
+ everOpened: {
+ ...this.state.everOpened,
+ [item.id]: true,
+ },
+ opened: {
+ ...this.state.opened,
+ [item.id]: opened,
+ },
+ });
+
+ if (typeof item.onToggle === "function") {
+ item.onToggle(opened, item);
+ }
+ }
+
+ renderItem(item) {
+ const {
+ buttons,
+ className = "",
+ component,
+ componentProps = {},
+ contentClassName = "",
+ header,
+ id,
+ } = item;
+
+ const headerId = `${id}-header`;
+ const opened = this.state.opened[id];
+ let itemContent;
+
+ // Only render content if the accordion item is open or has been opened once before.
+ // This saves us rendering complex components when users are keeping
+ // them closed (e.g. in Inspector/Layout) or may not open them at all.
+ if (this.state.everOpened && this.state.everOpened[id]) {
+ if (typeof component === "function") {
+ itemContent = createElement(component, componentProps);
+ } else if (typeof component === "object") {
+ itemContent = component;
+ }
+ }
+
+ return li(
+ {
+ key: id,
+ id,
+ className: `accordion-item ${
+ opened ? "accordion-open" : ""
+ } ${className} `.trim(),
+ "aria-labelledby": headerId,
+ },
+ h2(
+ {
+ id: headerId,
+ className: "accordion-header",
+ tabIndex: 0,
+ "aria-expanded": opened,
+ // If the header contains buttons, make sure the heading name only
+ // contains the "header" text and not the button text
+ "aria-label": header,
+ onKeyDown: event => this.onHeaderKeyDown(event, item),
+ onClick: event => this.onHeaderClick(event, item),
+ },
+ span({
+ className: `theme-twisty${opened ? " open" : ""}`,
+ role: "presentation",
+ }),
+ span(
+ {
+ className: "accordion-header-label",
+ },
+ header
+ ),
+ buttons &&
+ span(
+ {
+ className: "accordion-header-buttons",
+ role: "presentation",
+ },
+ buttons
+ )
+ ),
+ div(
+ {
+ className: `accordion-content ${contentClassName}`.trim(),
+ hidden: !opened,
+ role: "presentation",
+ },
+ itemContent
+ )
+ );
+ }
+
+ render() {
+ return ul(
+ {
+ className:
+ "accordion" +
+ (this.props.className ? ` ${this.props.className}` : ""),
+ tabIndex: -1,
+ },
+ this.props.items.map(item => this.renderItem(item))
+ );
+ }
+}
+
+module.exports = Accordion;
diff --git a/devtools/client/shared/components/AppErrorBoundary.css b/devtools/client/shared/components/AppErrorBoundary.css
new file mode 100644
index 0000000000..29b1732e20
--- /dev/null
+++ b/devtools/client/shared/components/AppErrorBoundary.css
@@ -0,0 +1,86 @@
+/* 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/. */
+
+/* Base styles (common to most error boundaries) */
+
+
+/* Container */
+.app-error-panel {
+ color: var(--theme-text-color-strong);
+ display: flex;
+ flex-direction: column;
+ font-family: inherit;
+ font-size: 16px;
+ margin: 0 0 2rem;
+ overflow-y: scroll;
+ padding: 1rem 3rem;
+ width: 100%;
+ height: 100%;
+}
+
+/* "Has crashed" header */
+.app-error-panel .error-panel-header {
+ align-self: center;
+ font-size: 1.5em;
+ font-weight: 300;
+}
+
+/* "File a Bug" button */
+.app-error-panel .error-panel-file-button {
+ align-self: center;
+ background-color: var(--blue-60);
+ outline: none;
+ color: white;
+ font-size: 1em;
+ font-weight: 400;
+ margin-bottom: 14.74px;
+ padding: 0.75rem;
+ text-align: center;
+ inline-size: 200px;
+ text-decoration: none;
+}
+
+.app-error-panel .error-panel-file-button:hover {
+ background-color: var(--blue-70);
+}
+
+.app-error-panel .error-panel-file-button:hover:active {
+ background-color: var(--blue-80);
+}
+
+/* Text of the error itself, not the stack trace */
+.app-error-panel .error-panel-error {
+ background-color: var(--theme-body-emphasized-background);
+ border: 1px solid var(--theme-toolbar-separator);
+ border-block-end: 0;
+ font-size: 1.2em;
+ font-weight: 500;
+ margin: 0;
+ padding: 2rem;
+}
+
+/* Stack trace; composed of <p> elements */
+.app-error-panel .stack-trace-section {
+ background-color: var(--theme-body-emphasized-background);
+ border: 1px solid var(--theme-toolbar-separator);
+ padding: 2rem;
+ margin-bottom: 1rem;
+}
+
+.app-error-panel .stack-trace-section p {
+ color: var(--theme-stack-trace-text);
+ margin: 0;
+ margin-inline-start: 1rem;
+}
+
+.app-error-panel .stack-trace-section p:first-child {
+ margin: 0;
+}
+
+/* Instructions to reopen the toolbox */
+.app-error-panel .error-panel-reload-info {
+ font-size: 1em;
+ font-weight: 400;
+ margin: 2rem 0 1rem;
+}
diff --git a/devtools/client/shared/components/AppErrorBoundary.js b/devtools/client/shared/components/AppErrorBoundary.js
new file mode 100644
index 0000000000..de29b3fc6f
--- /dev/null
+++ b/devtools/client/shared/components/AppErrorBoundary.js
@@ -0,0 +1,163 @@
+/* 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/. */
+
+"use strict";
+
+// React deps
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { div, h1, h2, h3, p, a } = dom;
+
+// Localized strings for (devtools/client/locales/en-US/components.properties)
+loader.lazyGetter(this, "L10N", function () {
+ const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+ return new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+ );
+});
+
+loader.lazyGetter(this, "FILE_BUG_BUTTON", function () {
+ return L10N.getStr("appErrorBoundary.fileBugButton");
+});
+
+loader.lazyGetter(this, "RELOAD_PAGE_INFO", function () {
+ return L10N.getStr("appErrorBoundary.reloadPanelInfo");
+});
+
+// File a bug for the selected component specifically
+// Add format=__default__ to make sure users without EDITBUGS permission still
+// use the regular UI to create bugs, including the prefilled description.
+const bugLink =
+ "https://bugzilla.mozilla.org/enter_bug.cgi?format=__default__&product=DevTools&component=";
+
+/**
+ * Error boundary that wraps around the a given component.
+ */
+class AppErrorBoundary extends Component {
+ static get propTypes() {
+ return {
+ children: PropTypes.any.isRequired,
+ panel: PropTypes.any.isRequired,
+ componentName: PropTypes.string.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ errorMsg: "No error",
+ errorStack: null,
+ errorInfo: null,
+ };
+ }
+
+ /**
+ * Map the `info` object to a render.
+ * Currently, `info` usually just contains something similar to the
+ * following object (which is provided to componentDidCatch):
+ * componentStack: {"\n in (component) \n in (other component)..."}
+ */
+ renderErrorInfo(info = {}) {
+ if (Object.keys(info).length) {
+ return Object.keys(info).map((obj, outerIdx) => {
+ const traceParts = info[obj]
+ .split("\n")
+ .map((part, idx) => p({ key: `strace${idx}` }, part));
+ return div(
+ { key: `st-div-${outerIdx}`, className: "stack-trace-section" },
+ h3({}, "React Component Stack"),
+ p({ key: `st-p-${outerIdx}` }, obj.toString()),
+ traceParts
+ );
+ });
+ }
+
+ return p({}, "undefined errorInfo");
+ }
+
+ renderStackTrace(stacktrace = "") {
+ const re = /:\d+:\d+/g;
+ const traces = stacktrace
+ .replace(re, "$&,")
+ .split(",")
+ .map((trace, index) => {
+ return p({ key: `rst-${index}` }, trace);
+ });
+
+ return div(
+ { className: "stack-trace-section" },
+ h3({}, "Stacktrace"),
+ traces
+ );
+ }
+
+ // Return a valid object, even if we don't receive one
+ getValidInfo(infoObj) {
+ if (!infoObj.componentStack) {
+ try {
+ return { componentStack: JSON.stringify(infoObj) };
+ } catch (err) {
+ return { componentStack: `Unknown Error: ${err}` };
+ }
+ }
+ return infoObj;
+ }
+
+ // Called when a child component throws an error.
+ componentDidCatch(error, info) {
+ const validInfo = this.getValidInfo(info);
+ this.setState({
+ errorMsg: error.toString(),
+ errorStack: error.stack,
+ errorInfo: validInfo,
+ });
+ }
+
+ getBugLink() {
+ const compStack = this.getValidInfo(this.state.errorInfo).componentStack;
+ const errorMsg = this.state.errorMsg;
+ const errorStack = this.state.errorStack;
+ const msg = `Error: \n${errorMsg}\n\nReact Component Stack: ${compStack}\n\nStacktrace: \n${errorStack}`;
+ return `${bugLink}${this.props.componentName}&comment=${encodeURIComponent(
+ msg
+ )}`;
+ }
+
+ render() {
+ if (this.state.errorInfo !== null) {
+ // "The (componentDesc) has crashed"
+ const errorDescription = L10N.getFormatStr(
+ "appErrorBoundary.description",
+ this.props.panel
+ );
+ return div(
+ {
+ className: `app-error-panel`,
+ },
+ h1({ className: "error-panel-header" }, errorDescription),
+ a(
+ {
+ className: "error-panel-file-button",
+ href: this.getBugLink(),
+ onClick: () => {
+ window.open(this.getBugLink(), "_blank");
+ },
+ },
+ FILE_BUG_BUTTON
+ ),
+ h2({ className: "error-panel-error" }, this.state.errorMsg),
+ div({}, this.renderErrorInfo(this.state.errorInfo)),
+ div({}, this.renderStackTrace(this.state.errorStack)),
+ p({ className: "error-panel-reload-info" }, RELOAD_PAGE_INFO)
+ );
+ }
+ return this.props.children;
+ }
+}
+
+module.exports = AppErrorBoundary;
diff --git a/devtools/client/shared/components/Frame.js b/devtools/client/shared/components/Frame.js
new file mode 100644
index 0000000000..4efc7d3bd6
--- /dev/null
+++ b/devtools/client/shared/components/Frame.js
@@ -0,0 +1,401 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const {
+ getUnicodeUrl,
+ getUnicodeUrlPath,
+ getUnicodeHostname,
+} = require("resource://devtools/client/shared/unicode-url.js");
+const {
+ getSourceNames,
+ parseURL,
+ getSourceMappedFile,
+} = require("resource://devtools/client/shared/source-utils.js");
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const {
+ MESSAGE_SOURCE,
+} = require("resource://devtools/client/webconsole/constants.js");
+
+const l10n = new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+);
+const webl10n = new LocalizationHelper(
+ "devtools/client/locales/webconsole.properties"
+);
+
+function savedFrameToLocation(frame) {
+ const { source: url, line, column, sourceId } = frame;
+ return {
+ url,
+ line,
+ column,
+ // The sourceId will be a string if it's a source actor ID, otherwise
+ // it is either a Spidermonkey-internal ID from a SavedFrame or missing,
+ // and in either case we can't use the ID for anything useful.
+ id: typeof sourceId === "string" ? sourceId : null,
+ };
+}
+
+/**
+ * Get the tooltip message.
+ * @param {string|undefined} messageSource
+ * @param {string} url
+ * @returns {string}
+ */
+function getTooltipMessage(messageSource, url) {
+ if (messageSource && messageSource === MESSAGE_SOURCE.CSS) {
+ return l10n.getFormatStr("frame.viewsourceinstyleeditor", url);
+ }
+ return l10n.getFormatStr("frame.viewsourceindebugger", url);
+}
+
+class Frame extends Component {
+ static get propTypes() {
+ return {
+ // Optional className that will be put into the element.
+ className: PropTypes.string,
+ // SavedFrame, or an object containing all the required properties.
+ frame: PropTypes.shape({
+ functionDisplayName: PropTypes.string,
+ // This could be a SavedFrame with a numeric sourceId, or it could
+ // be a SavedFrame-like client-side object, in which case the
+ // "sourceId" will be a source actor ID.
+ sourceId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ source: PropTypes.string.isRequired,
+ line: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ column: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ }).isRequired,
+ // Clicking on the frame link -- probably should link to the debugger.
+ onClick: PropTypes.func,
+ // Option to display a function name before the source link.
+ showFunctionName: PropTypes.bool,
+ // Option to display a function name even if it's anonymous.
+ showAnonymousFunctionName: PropTypes.bool,
+ // Option to display a host name after the source link.
+ showHost: PropTypes.bool,
+ // Option to display a host name if the filename is empty or just '/'
+ showEmptyPathAsHost: PropTypes.bool,
+ // Option to display a full source instead of just the filename.
+ showFullSourceUrl: PropTypes.bool,
+ // Service to enable the source map feature for console.
+ sourceMapURLService: PropTypes.object,
+ // The source of the message
+ messageSource: PropTypes.string,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ showFunctionName: false,
+ showAnonymousFunctionName: false,
+ showHost: false,
+ showEmptyPathAsHost: false,
+ showFullSourceUrl: false,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ originalLocation: null,
+ };
+ this._locationChanged = this._locationChanged.bind(this);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillMount() {
+ if (this.props.sourceMapURLService) {
+ const location = savedFrameToLocation(this.props.frame);
+ // Many things that make use of this component either:
+ // a) Pass in no sourceId because they have no way to know.
+ // b) Pass in no sourceId because the actor wasn't created when the
+ // server sent its response.
+ //
+ // and due to that, we need to use subscribeByLocation in order to
+ // handle both cases with an without an ID.
+ this.unsubscribeSourceMapURLService =
+ this.props.sourceMapURLService.subscribeByLocation(
+ location,
+ this._locationChanged
+ );
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.unsubscribeSourceMapURLService) {
+ this.unsubscribeSourceMapURLService();
+ }
+ }
+
+ _locationChanged(originalLocation) {
+ this.setState({ originalLocation });
+ }
+
+ /**
+ * Get current location's source, line, and column.
+ * @returns {{source: string, line: number|null, column: number|null}}
+ */
+ #getCurrentLocationInfo = () => {
+ const { frame } = this.props;
+ const { originalLocation } = this.state;
+
+ const generatedLocation = savedFrameToLocation(frame);
+ const currentLocation = originalLocation || generatedLocation;
+
+ const source = currentLocation.url || "";
+ const line =
+ currentLocation.line != void 0 ? Number(currentLocation.line) : null;
+ const column =
+ currentLocation.column != void 0 ? Number(currentLocation.column) : null;
+ return {
+ source,
+ line,
+ column,
+ };
+ };
+
+ /**
+ * Get unicode hostname of the source link.
+ * @returns {string}
+ */
+ #getCurrentLocationUnicodeHostName = () => {
+ const { source } = this.#getCurrentLocationInfo();
+
+ const { host } = getSourceNames(source);
+ return host ? getUnicodeHostname(host) : "";
+ };
+
+ /**
+ * Check if the current location is linkable.
+ * @returns {boolean}
+ */
+ #isCurrentLocationLinkable = () => {
+ const { frame } = this.props;
+ const { originalLocation } = this.state;
+
+ const generatedLocation = savedFrameToLocation(frame);
+
+ // Reparse the URL to determine if we should link this; `getSourceNames`
+ // has already cached this indirectly. We don't want to attempt to
+ // link to "self-hosted" and "(unknown)".
+ // Source mapped sources might not necessary linkable, but they
+ // are still valid in the debugger.
+ // If we have a source ID then we can show the source in the debugger.
+ return !!(
+ originalLocation ||
+ generatedLocation.id ||
+ !!parseURL(generatedLocation.url)
+ );
+ };
+
+ /**
+ * Get the props of the top element.
+ */
+ #getTopElementProps = () => {
+ const { className } = this.props;
+
+ const { source, line, column } = this.#getCurrentLocationInfo();
+ const { long } = getSourceNames(source);
+ const props = {
+ "data-url": long,
+ className: "frame-link" + (className ? ` ${className}` : ""),
+ };
+
+ // If we have a line number > 0.
+ if (line) {
+ // Add `data-line` attribute for testing
+ props["data-line"] = line;
+
+ // Intentionally exclude 0
+ if (column) {
+ // Add `data-column` attribute for testing
+ props["data-column"] = column;
+ }
+ }
+ return props;
+ };
+
+ /**
+ * Get the props of the source element.
+ */
+ #getSourceElementsProps = () => {
+ const { frame, onClick, messageSource } = this.props;
+
+ const generatedLocation = savedFrameToLocation(frame);
+ const { source, line, column } = this.#getCurrentLocationInfo();
+ const { long } = getSourceNames(source);
+ let url = getUnicodeUrl(long);
+
+ // Exclude all falsy values, including `0`, as line numbers start with 1.
+ if (line) {
+ url += `:${line}`;
+ // Intentionally exclude 0
+ if (column) {
+ url += `:${column}`;
+ }
+ }
+
+ const isLinkable = this.#isCurrentLocationLinkable();
+
+ // Inner el is useful for achieving ellipsis on the left and correct LTR/RTL
+ // ordering. See CSS styles for frame-link-source-[inner] and bug 1290056.
+ const tooltipMessage = getTooltipMessage(messageSource, url);
+
+ const sourceElConfig = {
+ key: "source",
+ className: "frame-link-source",
+ title: isLinkable ? tooltipMessage : url,
+ };
+
+ if (isLinkable) {
+ return {
+ ...sourceElConfig,
+ onClick: e => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ onClick(generatedLocation);
+ },
+ href: source,
+ draggable: false,
+ };
+ }
+
+ return sourceElConfig;
+ };
+
+ /**
+ * Render the source elements.
+ * @returns {React.ReactNode}
+ */
+ #renderSourceElements = () => {
+ const { line, column } = this.#getCurrentLocationInfo();
+
+ const sourceElements = [this.#renderDisplaySource()];
+
+ if (line) {
+ let lineInfo = `:${line}`;
+
+ // Intentionally exclude 0
+ if (column) {
+ lineInfo += `:${column}`;
+ }
+
+ sourceElements.push(
+ dom.span(
+ {
+ key: "line",
+ className: "frame-link-line",
+ },
+ lineInfo
+ )
+ );
+ }
+
+ if (this.#isCurrentLocationLinkable()) {
+ return dom.a(this.#getSourceElementsProps(), sourceElements);
+ }
+ // If source is not a URL (self-hosted, eval, etc.), don't make
+ // it an anchor link, as we can't link to it.
+ return dom.span(this.#getSourceElementsProps(), sourceElements);
+ };
+
+ /**
+ * Render the display source.
+ * @returns {React.ReactNode}
+ */
+ #renderDisplaySource = () => {
+ const { showEmptyPathAsHost, showFullSourceUrl } = this.props;
+ const { originalLocation } = this.state;
+
+ const { source } = this.#getCurrentLocationInfo();
+ const { short, long, host } = getSourceNames(source);
+ const unicodeShort = getUnicodeUrlPath(short);
+ const unicodeLong = getUnicodeUrl(long);
+ let displaySource = showFullSourceUrl ? unicodeLong : unicodeShort;
+ if (originalLocation) {
+ displaySource = getSourceMappedFile(displaySource);
+
+ // In case of pretty-printed HTML file, we would only get the formatted suffix; replace
+ // it with the full URL instead
+ if (showEmptyPathAsHost && displaySource == ":formatted") {
+ displaySource = host + displaySource;
+ }
+ } else if (
+ showEmptyPathAsHost &&
+ (displaySource === "" || displaySource === "/")
+ ) {
+ displaySource = host;
+ }
+
+ return dom.span(
+ {
+ key: "filename",
+ className: "frame-link-filename",
+ },
+ displaySource
+ );
+ };
+
+ /**
+ * Render the function display name.
+ * @returns {React.ReactNode}
+ */
+ #renderFunctionDisplayName = () => {
+ const { frame, showFunctionName, showAnonymousFunctionName } = this.props;
+ if (!showFunctionName) {
+ return null;
+ }
+ const functionDisplayName = frame.functionDisplayName;
+ if (functionDisplayName || showAnonymousFunctionName) {
+ return [
+ dom.span(
+ {
+ key: "function-display-name",
+ className: "frame-link-function-display-name",
+ },
+ functionDisplayName || webl10n.getStr("stacktrace.anonymousFunction")
+ ),
+ " ",
+ ];
+ }
+ return null;
+ };
+
+ render() {
+ const { showHost } = this.props;
+
+ const elements = [
+ this.#renderFunctionDisplayName(),
+ this.#renderSourceElements(),
+ ];
+
+ const unicodeHost = showHost
+ ? this.#getCurrentLocationUnicodeHostName()
+ : null;
+ if (unicodeHost) {
+ elements.push(" ");
+ elements.push(
+ dom.span(
+ {
+ key: "host",
+ className: "frame-link-host",
+ },
+ unicodeHost
+ )
+ );
+ }
+
+ return dom.span(this.#getTopElementProps(), ...elements);
+ }
+}
+
+module.exports = Frame;
diff --git a/devtools/client/shared/components/HSplitBox.js b/devtools/client/shared/components/HSplitBox.js
new file mode 100644
index 0000000000..65dfc0aaf6
--- /dev/null
+++ b/devtools/client/shared/components/HSplitBox.js
@@ -0,0 +1,165 @@
+/* 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/. */
+
+/* eslint-env browser */
+"use strict";
+
+// A box with a start and a end pane, separated by a dragable splitter that
+// allows the user to resize the relative widths of the panes.
+//
+// +-----------------------+---------------------+
+// | | |
+// | | |
+// | S |
+// | Start Pane p End Pane |
+// | l |
+// | i |
+// | t |
+// | t |
+// | e |
+// | r |
+// | | |
+// | | |
+// +-----------------------+---------------------+
+
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { assert } = require("resource://devtools/shared/DevToolsUtils.js");
+
+class HSplitBox extends Component {
+ static get propTypes() {
+ return {
+ // The contents of the start pane.
+ start: PropTypes.any.isRequired,
+
+ // The contents of the end pane.
+ end: PropTypes.any.isRequired,
+
+ // The relative width of the start pane, expressed as a number between 0 and
+ // 1. The relative width of the end pane is 1 - startWidth. For example,
+ // with startWidth = .5, both panes are of equal width; with startWidth =
+ // .25, the start panel will take up 1/4 width and the end panel will take
+ // up 3/4 width.
+ startWidth: PropTypes.number,
+
+ // A minimum css width value for the start and end panes.
+ minStartWidth: PropTypes.any,
+ minEndWidth: PropTypes.any,
+
+ // A callback fired when the user drags the splitter to resize the relative
+ // pane widths. The function is passed the startWidth value that would put
+ // the splitter underneath the users mouse.
+ onResize: PropTypes.func.isRequired,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ startWidth: 0.5,
+ minStartWidth: "20px",
+ minEndWidth: "20px",
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ mouseDown: false,
+ };
+
+ this._onMouseDown = this._onMouseDown.bind(this);
+ this._onMouseUp = this._onMouseUp.bind(this);
+ this._onMouseMove = this._onMouseMove.bind(this);
+ }
+
+ componentDidMount() {
+ document.defaultView.top.addEventListener("mouseup", this._onMouseUp);
+ document.defaultView.top.addEventListener("mousemove", this._onMouseMove);
+ }
+
+ componentWillUnmount() {
+ document.defaultView.top.removeEventListener("mouseup", this._onMouseUp);
+ document.defaultView.top.removeEventListener(
+ "mousemove",
+ this._onMouseMove
+ );
+ }
+
+ _onMouseDown(event) {
+ if (event.button !== 0) {
+ return;
+ }
+
+ this.setState({ mouseDown: true });
+ event.preventDefault();
+ }
+
+ _onMouseUp(event) {
+ if (event.button !== 0 || !this.state.mouseDown) {
+ return;
+ }
+
+ this.setState({ mouseDown: false });
+ event.preventDefault();
+ }
+
+ _onMouseMove(event) {
+ if (!this.state.mouseDown) {
+ return;
+ }
+
+ const rect = this.refs.box.getBoundingClientRect();
+ const { left, right } = rect;
+ const width = right - left;
+ const direction = this.refs.box.ownerDocument.dir;
+ const relative =
+ direction == "rtl" ? right - event.clientX : event.clientX - left;
+ this.props.onResize(relative / width);
+
+ event.preventDefault();
+ }
+
+ render() {
+ /* eslint-disable no-shadow */
+ const { start, end, startWidth, minStartWidth, minEndWidth } = this.props;
+ assert(
+ startWidth >= 0 && startWidth <= 1,
+ "0 <= this.props.startWidth <= 1"
+ );
+ /* eslint-enable */
+ return dom.div(
+ {
+ className: "h-split-box",
+ ref: "box",
+ },
+
+ dom.div(
+ {
+ className: "h-split-box-pane",
+ style: { flex: startWidth, minWidth: minStartWidth },
+ },
+ start
+ ),
+
+ dom.div({
+ className: "devtools-side-splitter",
+ onMouseDown: this._onMouseDown,
+ }),
+
+ dom.div(
+ {
+ className: "h-split-box-pane",
+ style: { flex: 1 - startWidth, minWidth: minEndWidth },
+ },
+ end
+ )
+ );
+ }
+}
+
+module.exports = HSplitBox;
diff --git a/devtools/client/shared/components/List.css b/devtools/client/shared/components/List.css
new file mode 100644
index 0000000000..1d0f668180
--- /dev/null
+++ b/devtools/client/shared/components/List.css
@@ -0,0 +1,41 @@
+/* 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/. */
+
+/* List */
+
+.list {
+ background-color: var(--theme-sidebar-background);
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ width: 100%;
+ white-space: nowrap;
+ overflow: auto;
+}
+
+.list:focus, .list .list-item-content:focus {
+ outline: 0;
+}
+
+.list::-moz-focus-inner, .list .list-item-content::-moz-focus-inner {
+ border: 0;
+}
+
+.list li.current {
+ background-color: var(--theme-toolbar-hover);
+}
+
+.list:focus li.current, .list li.active.current {
+ background-color: var(--theme-emphasized-splitter-color);
+}
+
+.list:focus li:not(.current):hover,
+.list:not(:focus) li:not(.active):hover {
+ background-color: var(--theme-selection-background-hover);
+}
+
+.list .list-item-content:not(:empty) {
+ font-size: 12px;
+ overflow: auto;
+}
diff --git a/devtools/client/shared/components/List.js b/devtools/client/shared/components/List.js
new file mode 100644
index 0000000000..95c3ffe4dd
--- /dev/null
+++ b/devtools/client/shared/components/List.js
@@ -0,0 +1,352 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ createFactory,
+ createRef,
+ Component,
+ cloneElement,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const {
+ ul,
+ li,
+ div,
+} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const {
+ scrollIntoView,
+} = require("resource://devtools/client/shared/scroll.js");
+const {
+ preventDefaultAndStopPropagation,
+} = require("resource://devtools/client/shared/events.js");
+
+loader.lazyRequireGetter(
+ this,
+ ["getFocusableElements", "wrapMoveFocus"],
+ "resource://devtools/client/shared/focus.js",
+ true
+);
+
+class ListItemClass extends Component {
+ static get propTypes() {
+ return {
+ active: PropTypes.bool,
+ current: PropTypes.bool,
+ onClick: PropTypes.func,
+ item: PropTypes.shape({
+ key: PropTypes.string,
+ component: PropTypes.object,
+ componentProps: PropTypes.object,
+ className: PropTypes.string,
+ }).isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.contentRef = createRef();
+
+ this._setTabbableState = this._setTabbableState.bind(this);
+ this._onKeyDown = this._onKeyDown.bind(this);
+ }
+
+ componentDidMount() {
+ this._setTabbableState();
+ }
+
+ componentDidUpdate() {
+ this._setTabbableState();
+ }
+
+ _onKeyDown(event) {
+ const { target, key, shiftKey } = event;
+
+ if (key !== "Tab") {
+ return;
+ }
+
+ const focusMoved = !!wrapMoveFocus(
+ getFocusableElements(this.contentRef.current),
+ target,
+ shiftKey
+ );
+ if (focusMoved) {
+ // Focus was moved to the begining/end of the list, so we need to prevent the
+ // default focus change that would happen here.
+ event.preventDefault();
+ }
+
+ event.stopPropagation();
+ }
+
+ /**
+ * Makes sure that none of the focusable elements inside the list item container are
+ * tabbable if the list item is not active. If the list item is active and focus is
+ * outside its container, focus on the first focusable element inside.
+ */
+ _setTabbableState() {
+ const elms = getFocusableElements(this.contentRef.current);
+ if (elms.length === 0) {
+ return;
+ }
+
+ if (!this.props.active) {
+ elms.forEach(elm => elm.setAttribute("tabindex", "-1"));
+ return;
+ }
+
+ if (!elms.includes(document.activeElement)) {
+ elms[0].focus();
+ }
+ }
+
+ render() {
+ const { active, item, current, onClick } = this.props;
+ const { className, component, componentProps } = item;
+
+ return li(
+ {
+ className: `${className}${current ? " current" : ""}${
+ active ? " active" : ""
+ }`,
+ id: item.key,
+ onClick,
+ onKeyDownCapture: active ? this._onKeyDown : null,
+ },
+ div(
+ {
+ className: "list-item-content",
+ role: "presentation",
+ ref: this.contentRef,
+ },
+ cloneElement(component, componentProps || {})
+ )
+ );
+ }
+}
+
+const ListItem = createFactory(ListItemClass);
+
+class List extends Component {
+ static get propTypes() {
+ return {
+ // A list of all items to be rendered using a List component.
+ items: PropTypes.arrayOf(
+ PropTypes.shape({
+ component: PropTypes.object,
+ componentProps: PropTypes.object,
+ className: PropTypes.string,
+ key: PropTypes.string.isRequired,
+ })
+ ).isRequired,
+
+ // Note: the two properties below are mutually exclusive. Only one of the
+ // label properties is necessary.
+ // ID of an element whose textual content serves as an accessible label for
+ // a list.
+ labelledBy: PropTypes.string,
+
+ // Accessibility label for a list widget.
+ label: PropTypes.string,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.listRef = createRef();
+
+ this.state = {
+ active: null,
+ current: null,
+ mouseDown: false,
+ };
+
+ this._setCurrentItem = this._setCurrentItem.bind(this);
+ this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
+ this._onKeyDown = this._onKeyDown.bind(this);
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ const { active, current, mouseDown } = this.state;
+
+ return (
+ current !== nextState.current ||
+ active !== nextState.active ||
+ mouseDown === nextState.mouseDown
+ );
+ }
+
+ _preventArrowKeyScrolling(e) {
+ switch (e.key) {
+ case "ArrowUp":
+ case "ArrowDown":
+ case "ArrowLeft":
+ case "ArrowRight":
+ preventDefaultAndStopPropagation(e);
+ break;
+ }
+ }
+
+ /**
+ * Sets the passed in item to be the current item.
+ *
+ * @param {null|Number} index
+ * The index of the item in to be set as current, or undefined to unset the
+ * current item.
+ */
+ _setCurrentItem(index = -1, options = {}) {
+ const item = this.props.items[index];
+ if (item !== undefined && !options.preventAutoScroll) {
+ const element = document.getElementById(item.key);
+ scrollIntoView(element, {
+ ...options,
+ container: this.listRef.current,
+ });
+ }
+
+ const state = {};
+ if (this.state.active != undefined) {
+ state.active = null;
+ if (this.listRef.current !== document.activeElement) {
+ this.listRef.current.focus();
+ }
+ }
+
+ if (this.state.current !== index) {
+ this.setState({
+ ...state,
+ current: index,
+ });
+ }
+ }
+
+ /**
+ * Handles key down events in the list's container.
+ *
+ * @param {Event} e
+ */
+ _onKeyDown(e) {
+ const { active, current } = this.state;
+ if (current == null) {
+ return;
+ }
+
+ if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+ return;
+ }
+
+ this._preventArrowKeyScrolling(e);
+
+ const { length } = this.props.items;
+ switch (e.key) {
+ case "ArrowUp":
+ current > 0 && this._setCurrentItem(current - 1, { alignTo: "top" });
+ break;
+
+ case "ArrowDown":
+ current < length - 1 &&
+ this._setCurrentItem(current + 1, { alignTo: "bottom" });
+ break;
+
+ case "Home":
+ this._setCurrentItem(0, { alignTo: "top" });
+ break;
+
+ case "End":
+ this._setCurrentItem(length - 1, { alignTo: "bottom" });
+ break;
+
+ case "Enter":
+ case " ":
+ // On space or enter make current list item active. This means keyboard focus
+ // handling is passed on to the component within the list item.
+ if (document.activeElement === this.listRef.current) {
+ preventDefaultAndStopPropagation(e);
+ if (active !== current) {
+ this.setState({ active: current });
+ }
+ }
+ break;
+
+ case "Escape":
+ // If current list item is active, make it inactive and let keyboard focusing be
+ // handled normally.
+ preventDefaultAndStopPropagation(e);
+ if (active != null) {
+ this.setState({ active: null });
+ }
+
+ this.listRef.current.focus();
+ break;
+ }
+ }
+
+ render() {
+ const { active, current } = this.state;
+ const { items } = this.props;
+
+ return ul(
+ {
+ ref: this.listRef,
+ className: "list",
+ tabIndex: 0,
+ onKeyDown: this._onKeyDown,
+ onKeyPress: this._preventArrowKeyScrolling,
+ onKeyUp: this._preventArrowKeyScrolling,
+ onMouseDown: () => this.setState({ mouseDown: true }),
+ onMouseUp: () => this.setState({ mouseDown: false }),
+ onFocus: () => {
+ if (current != null || this.state.mouseDown) {
+ return;
+ }
+
+ // Only set default current to the first list item if current item is
+ // not yet set and the focus event is not the result of a mouse
+ // interarction.
+ this._setCurrentItem(0);
+ },
+ onClick: () => {
+ // Focus should always remain on the list container itself.
+ this.listRef.current.focus();
+ },
+ onBlur: e => {
+ if (active != null) {
+ const { relatedTarget } = e;
+ if (!this.listRef.current.contains(relatedTarget)) {
+ this.setState({ active: null });
+ }
+ }
+ },
+ "aria-label": this.props.label,
+ "aria-labelledby": this.props.labelledBy,
+ "aria-activedescendant": current != null ? items[current].key : null,
+ },
+ items.map((item, index) => {
+ return ListItem({
+ item,
+ current: index === current,
+ active: index === active,
+ // We make a key unique depending on whether the list item is in active or
+ // inactive state to make sure that it is actually replaced and the tabbable
+ // state is reset.
+ key: `${item.key}-${index === active ? "active" : "inactive"}`,
+ // Since the user just clicked the item, there's no need to check if it should
+ // be scrolled into view.
+ onClick: () =>
+ this._setCurrentItem(index, { preventAutoScroll: true }),
+ });
+ })
+ );
+ }
+}
+
+module.exports = {
+ ListItem: ListItemClass,
+ List,
+};
diff --git a/devtools/client/shared/components/MdnLink.css b/devtools/client/shared/components/MdnLink.css
new file mode 100644
index 0000000000..0fef9c0bba
--- /dev/null
+++ b/devtools/client/shared/components/MdnLink.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+/* Learn more links */
+
+.network-monitor .learn-more-link {
+ display: inline-block;
+ line-height: 16px;
+}
+
+.network-monitor .learn-more-link::before {
+ background-image: url(chrome://devtools/skin/images/help.svg);
+ background-size: contain;
+}
+
+.network-monitor .tree-container .learn-more-link {
+ position: absolute;
+ top: 0;
+ inset-inline-start: 2px;
+ /* Override devtools-button styles to make this button 20x20,
+ * so that the icon is vertically centered in the table row */
+ padding: 1px 0;
+}
+
+.network-monitor .tree-container tr:not(:hover) .learn-more-link {
+ opacity: 0.4;
+}
+
+.network-monitor .tabpanel-summary-value.status {
+ display: flex;
+ align-items: center;
+}
diff --git a/devtools/client/shared/components/MdnLink.js b/devtools/client/shared/components/MdnLink.js
new file mode 100644
index 0000000000..344143f54c
--- /dev/null
+++ b/devtools/client/shared/components/MdnLink.js
@@ -0,0 +1,38 @@
+/* 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/. */
+
+"use strict";
+
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { a } = dom;
+
+loader.lazyRequireGetter(
+ this,
+ "openDocLink",
+ "resource://devtools/client/shared/link.js",
+ true
+);
+
+function MDNLink({ url, title }) {
+ return a({
+ className: "devtools-button learn-more-link",
+ title,
+ onClick: e => onLearnMoreClick(e, url),
+ });
+}
+
+MDNLink.displayName = "MDNLink";
+
+MDNLink.propTypes = {
+ url: PropTypes.string.isRequired,
+};
+
+function onLearnMoreClick(e, url) {
+ e.stopPropagation();
+ e.preventDefault();
+ openDocLink(url);
+}
+
+module.exports = MDNLink;
diff --git a/devtools/client/shared/components/NotificationBox.css b/devtools/client/shared/components/NotificationBox.css
new file mode 100644
index 0000000000..f2ff550f46
--- /dev/null
+++ b/devtools/client/shared/components/NotificationBox.css
@@ -0,0 +1,130 @@
+/* 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/. */
+
+/* Layout */
+
+.notificationbox .notificationInner {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+}
+
+.notificationInner .messageText {
+ flex: 1;
+ width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.notificationInner .messageImage,
+.notificationbox .notificationButton,
+.notificationbox .messageCloseButton {
+ flex: none;
+}
+
+.notificationbox .notificationInner:dir(rtl) {
+ flex-direction: row-reverse;
+}
+
+/* Style */
+
+.notificationbox .notification {
+ color: var(--theme-toolbar-color);
+ background-color: var(--theme-body-background);
+ text-shadow: none;
+ border-color: var(--theme-splitter-color);
+ border-style: solid;
+ border-width: 0;
+}
+
+.notificationbox.border-top .notification {
+ border-top-width: 1px;
+}
+
+.notificationbox.border-bottom .notification {
+ border-bottom-width: 1px;
+}
+
+.notificationbox .notification[data-type="info"] {
+ color: -moz-DialogText;
+ background-color: -moz-Dialog;
+}
+
+.notificationbox .notification[data-type="new"] {
+ color: var(--theme-contrast-color);
+ background-color: var(--theme-body-alternate-emphasized-background);
+}
+
+/**
+ * Remove button borders for notifications highlighting New features.
+ */
+.notification[data-type="new"] .notificationButton {
+ border-radius: 2px;
+ border-width: 0;
+ padding: 4px;
+}
+
+.notificationbox .notification[data-type="critical"] {
+ color: white;
+ background-image: linear-gradient(rgb(212,0,0), rgb(152,0,0));
+}
+
+.notificationbox .messageImage {
+ -moz-context-properties: fill;
+ fill: currentColor;
+ background-size: 16px;
+ width: 16px;
+ height: 16px;
+ margin: 6px;
+}
+
+/* Default icons for notifications */
+
+.notificationbox .messageImage[data-type="info"] {
+ background-image: url("chrome://devtools/skin/images/info.svg");
+}
+
+.notificationbox .messageImage[data-type="new"] {
+ background-image: url("chrome://global/skin/icons/whatsnew.svg");
+ fill: var(--theme-highlight-blue);
+}
+
+.notificationbox .messageImage[data-type="warning"] {
+ background-image: url("chrome://devtools/skin/images/alert.svg");
+ /* Keep the icon colored to make it more eye-catching */
+ fill: #ffbf00;
+}
+
+.notificationbox .messageImage[data-type="critical"] {
+ background-image: url("chrome://devtools/skin/images/error.svg");
+}
+
+/* Close button */
+
+.notificationbox .messageCloseButton {
+ width: 24px;
+ height: 24px;
+ margin: 2px 4px;
+ background-image: url("chrome://devtools/skin/images/close.svg");
+ background-position: center;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ border-radius: 2px;
+ border-width: 0;
+ -moz-context-properties: fill;
+ fill: var(--theme-icon-color);
+}
+
+.notificationbox .messageCloseButton:hover {
+ background-color: var(--theme-button-active-background);
+}
+
+.notificationbox .messageCloseButton:active {
+ background-color: rgba(170, 170, 170, .4); /* --toolbar-tab-hover-active */
+}
+
+.notificationbox.wrapping .notificationInner .messageText {
+ white-space: normal;
+}
diff --git a/devtools/client/shared/components/NotificationBox.js b/devtools/client/shared/components/NotificationBox.js
new file mode 100644
index 0000000000..53e1073d7a
--- /dev/null
+++ b/devtools/client/shared/components/NotificationBox.js
@@ -0,0 +1,403 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+
+const l10n = new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+);
+const { div, span, button } = dom;
+loader.lazyGetter(this, "MDNLink", function () {
+ return createFactory(
+ require("resource://devtools/client/shared/components/MdnLink.js")
+ );
+});
+
+// Priority Levels
+const PriorityLevels = {
+ PRIORITY_INFO_LOW: 1,
+ PRIORITY_INFO_MEDIUM: 2,
+ PRIORITY_INFO_HIGH: 3,
+ // Type NEW should be used to highlight new features, and should be more
+ // eye-catchy than INFO level notifications.
+ PRIORITY_NEW: 4,
+ PRIORITY_WARNING_LOW: 5,
+ PRIORITY_WARNING_MEDIUM: 6,
+ PRIORITY_WARNING_HIGH: 7,
+ PRIORITY_CRITICAL_LOW: 8,
+ PRIORITY_CRITICAL_MEDIUM: 9,
+ PRIORITY_CRITICAL_HIGH: 10,
+ PRIORITY_CRITICAL_BLOCK: 11,
+};
+
+/**
+ * This component represents Notification Box - HTML alternative for
+ * <xul:notificationbox> binding.
+ *
+ * See also MDN for more info about <xul:notificationbox>:
+ * https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/notificationbox
+ *
+ * This component can maintain its own state (list of notifications)
+ * as well as consume list of notifications provided as a prop
+ * (coming e.g. from Redux store).
+ */
+class NotificationBox extends Component {
+ static get propTypes() {
+ return {
+ // Optional box ID (used for mounted node ID attribute)
+ id: PropTypes.string,
+ /**
+ * List of notifications appended into the box. Each item of the map is an object
+ * of the following shape:
+ * - {String} label: Label to appear on the notification.
+ * - {String} value: Value used to identify the notification. Should be the same
+ * as the map key used for this notification.
+ * - {String} image: URL of image to appear on the notification. If "" then an
+ * appropriate icon for the priority level is used.
+ * - {Number} priority: Notification priority; see Priority Levels.
+ * - {Function} eventCallback: A function to call to notify you of interesting
+ things that happen with the notification box.
+ - {String} type: One of "info", "warning", or "critical" used to determine
+ what styling and icon are used for the notification.
+ * - {Array<Object>} buttons: Array of button descriptions to appear on the
+ * notification. Should be of the following shape:
+ * - {Function} callback: This function is passed 3 arguments:
+ 1) the NotificationBox component
+ the button is associated with.
+ 2) the button description as passed
+ to appendNotification.
+ 3) the element which was the target
+ of the button press event.
+ If the return value from this function
+ is not true, then the notification is
+ closed. The notification is also not
+ closed if an error is thrown.
+ - {String} label: The label to appear on the button.
+ - {String} accesskey: The accesskey attribute set on the
+ <button> element.
+ - {String} mdnUrl: URL to MDN docs. Optional but if set
+ turns button into a MDNLink and supersedes
+ all other properties. Uses Label as the title
+ for the link.
+ */
+ notifications: PropTypes.instanceOf(Map),
+ // Message that should be shown when hovering over the close button
+ closeButtonTooltip: PropTypes.string,
+ // Wraps text when passed from console window as wrapping: true
+ wrapping: PropTypes.bool,
+ // Display a top border (default to false)
+ displayBorderTop: PropTypes.bool,
+ // Display a bottom border (default to true)
+ displayBorderBottom: PropTypes.bool,
+ // Display a close button (default to true)
+ displayCloseButton: PropTypes.bool,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ closeButtonTooltip: l10n.getStr("notificationBox.closeTooltip"),
+ displayBorderTop: false,
+ displayBorderBottom: true,
+ displayCloseButton: true,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ notifications: new Map(),
+ };
+
+ this.appendNotification = this.appendNotification.bind(this);
+ this.removeNotification = this.removeNotification.bind(this);
+ this.getNotificationWithValue = this.getNotificationWithValue.bind(this);
+ this.getCurrentNotification = this.getCurrentNotification.bind(this);
+ this.close = this.close.bind(this);
+ this.renderButton = this.renderButton.bind(this);
+ this.renderNotification = this.renderNotification.bind(this);
+ }
+
+ /**
+ * Create a new notification and display it. If another notification is
+ * already present with a higher priority, the new notification will be
+ * added behind it. See `propTypes` for arguments description.
+ */
+ appendNotification(
+ label,
+ value,
+ image,
+ priority,
+ buttons = [],
+ eventCallback
+ ) {
+ const newState = appendNotification(this.state, {
+ label,
+ value,
+ image,
+ priority,
+ buttons,
+ eventCallback,
+ });
+
+ this.setState(newState);
+ }
+
+ /**
+ * Remove specific notification from the list.
+ */
+ removeNotification(notification) {
+ if (notification) {
+ this.close(this.state.notifications.get(notification.value));
+ }
+ }
+
+ /**
+ * Returns an object that represents a notification. It can be
+ * used to close it.
+ */
+ getNotificationWithValue(value) {
+ const notification = this.state.notifications.get(value);
+ if (!notification) {
+ return null;
+ }
+
+ // Return an object that can be used to remove the notification
+ // later (using `removeNotification` method) or directly close it.
+ return Object.assign({}, notification, {
+ close: () => {
+ this.close(notification);
+ },
+ });
+ }
+
+ getCurrentNotification() {
+ return getHighestPriorityNotification(this.state.notifications);
+ }
+
+ /**
+ * Close specified notification.
+ */
+ close(notification) {
+ if (!notification) {
+ return;
+ }
+
+ if (notification.eventCallback) {
+ notification.eventCallback("removed");
+ }
+
+ if (!this.state.notifications.get(notification.value)) {
+ return;
+ }
+
+ const newNotifications = new Map(this.state.notifications);
+ newNotifications.delete(notification.value);
+ this.setState({
+ notifications: newNotifications,
+ });
+ }
+
+ /**
+ * Render a button. A notification can have a set of custom buttons.
+ * These are used to execute custom callback. Will render a MDNLink
+ * if mdnUrl property is set.
+ */
+ renderButton(props, notification) {
+ if (props.mdnUrl != null) {
+ return MDNLink({
+ url: props.mdnUrl,
+ title: props.label,
+ });
+ }
+ const onClick = event => {
+ if (props.callback) {
+ const result = props.callback(this, props, event.target);
+ if (!result) {
+ this.close(notification);
+ }
+ event.stopPropagation();
+ }
+ };
+
+ return button(
+ {
+ key: props.label,
+ className: "notificationButton",
+ accesskey: props.accesskey,
+ onClick,
+ },
+ props.label
+ );
+ }
+
+ /**
+ * Render a notification.
+ */
+ renderNotification(notification) {
+ return div(
+ {
+ key: notification.value,
+ className: "notification",
+ "data-key": notification.value,
+ "data-type": notification.type,
+ },
+ div(
+ { className: "notificationInner" },
+ div({
+ className: "messageImage",
+ "data-type": notification.type,
+ }),
+ span(
+ {
+ className: "messageText",
+ title: notification.label,
+ },
+ notification.label
+ ),
+ notification.buttons.map(props =>
+ this.renderButton(props, notification)
+ ),
+ this.props.displayCloseButton
+ ? button({
+ className: "messageCloseButton",
+ title: this.props.closeButtonTooltip,
+ onClick: this.close.bind(this, notification),
+ })
+ : null
+ )
+ );
+ }
+
+ /**
+ * Render the top (highest priority) notification. Only one
+ * notification is rendered at a time.
+ */
+ render() {
+ const notifications = this.props.notifications || this.state.notifications;
+ const notification = getHighestPriorityNotification(notifications);
+ const content = notification ? this.renderNotification(notification) : null;
+
+ const classNames = ["notificationbox"];
+ if (this.props.wrapping) {
+ classNames.push("wrapping");
+ }
+
+ if (this.props.displayBorderBottom) {
+ classNames.push("border-bottom");
+ }
+
+ if (this.props.displayBorderTop) {
+ classNames.push("border-top");
+ }
+
+ return div(
+ {
+ className: classNames.join(" "),
+ id: this.props.id,
+ },
+ content
+ );
+ }
+}
+
+// Helpers
+
+/**
+ * Create a new notification. If another notification is already present with
+ * a higher priority, the new notification will be added behind it.
+ * See `propTypes` for arguments description.
+ */
+function appendNotification(state, props) {
+ const { label, value, image, priority, buttons, eventCallback } = props;
+
+ // Priority level must be within expected interval
+ // (see priority levels at the top of this file).
+ if (
+ priority < PriorityLevels.PRIORITY_INFO_LOW ||
+ priority > PriorityLevels.PRIORITY_CRITICAL_BLOCK
+ ) {
+ throw new Error("Invalid notification priority " + priority);
+ }
+
+ // Custom image URL is not supported yet.
+ if (image) {
+ throw new Error("Custom image URL is not supported yet");
+ }
+
+ let type = "warning";
+ if (priority == PriorityLevels.PRIORITY_NEW) {
+ type = "new";
+ } else if (priority >= PriorityLevels.PRIORITY_CRITICAL_LOW) {
+ type = "critical";
+ } else if (priority <= PriorityLevels.PRIORITY_INFO_HIGH) {
+ type = "info";
+ }
+
+ if (!state.notifications) {
+ state.notifications = new Map();
+ }
+
+ const notifications = new Map(state.notifications);
+ notifications.set(value, {
+ label,
+ value,
+ image,
+ priority,
+ type,
+ buttons: Array.isArray(buttons) ? buttons : [],
+ eventCallback,
+ });
+
+ return {
+ notifications,
+ };
+}
+
+function getNotificationWithValue(notifications, value) {
+ return notifications ? notifications.get(value) : null;
+}
+
+function removeNotificationWithValue(notifications, value) {
+ const newNotifications = new Map(notifications);
+ newNotifications.delete(value);
+
+ return {
+ notifications: newNotifications,
+ };
+}
+
+function getHighestPriorityNotification(notifications) {
+ if (!notifications) {
+ return null;
+ }
+
+ let currentNotification = null;
+ // High priorities must be on top.
+ for (const [, notification] of notifications) {
+ if (
+ !currentNotification ||
+ notification.priority > currentNotification.priority
+ ) {
+ currentNotification = notification;
+ }
+ }
+
+ return currentNotification;
+}
+
+module.exports.NotificationBox = NotificationBox;
+module.exports.PriorityLevels = PriorityLevels;
+module.exports.appendNotification = appendNotification;
+module.exports.getNotificationWithValue = getNotificationWithValue;
+module.exports.removeNotificationWithValue = removeNotificationWithValue;
diff --git a/devtools/client/shared/components/SearchBox.js b/devtools/client/shared/components/SearchBox.js
new file mode 100644
index 0000000000..a1c771bfb0
--- /dev/null
+++ b/devtools/client/shared/components/SearchBox.js
@@ -0,0 +1,275 @@
+/* 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/. */
+
+/* global window */
+
+"use strict";
+
+const {
+ createFactory,
+ createRef,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const l10n = new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+);
+
+loader.lazyGetter(this, "SearchBoxAutocompletePopup", function () {
+ return createFactory(
+ require("resource://devtools/client/shared/components/SearchBoxAutocompletePopup.js")
+ );
+});
+loader.lazyGetter(this, "MDNLink", function () {
+ return createFactory(
+ require("resource://devtools/client/shared/components/MdnLink.js")
+ );
+});
+
+loader.lazyRequireGetter(
+ this,
+ "KeyShortcuts",
+ "resource://devtools/client/shared/key-shortcuts.js"
+);
+
+class SearchBox extends PureComponent {
+ static get propTypes() {
+ return {
+ autocompleteProvider: PropTypes.func,
+ delay: PropTypes.number,
+ keyShortcut: PropTypes.string,
+ learnMoreTitle: PropTypes.string,
+ learnMoreUrl: PropTypes.string,
+ onBlur: PropTypes.func,
+ onChange: PropTypes.func.isRequired,
+ onClearButtonClick: PropTypes.func,
+ onFocus: PropTypes.func,
+ // Optional function that will be called on the focus keyboard shortcut, before
+ // setting the focus to the input. If the function returns false, the input won't
+ // get focused.
+ onFocusKeyboardShortcut: PropTypes.func,
+ onKeyDown: PropTypes.func,
+ placeholder: PropTypes.string.isRequired,
+ summary: PropTypes.string,
+ summaryTooltip: PropTypes.string,
+ type: PropTypes.string,
+ value: PropTypes.string,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ value: props.value || "",
+ focused: false,
+ };
+
+ this.autocompleteRef = createRef();
+ this.inputRef = createRef();
+
+ this.onBlur = this.onBlur.bind(this);
+ this.onChange = this.onChange.bind(this);
+ this.onClearButtonClick = this.onClearButtonClick.bind(this);
+ this.onFocus = this.onFocus.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
+ }
+
+ componentDidMount() {
+ if (!this.props.keyShortcut) {
+ return;
+ }
+
+ this.shortcuts = new KeyShortcuts({
+ window,
+ });
+ this.shortcuts.on(this.props.keyShortcut, event => {
+ if (this.props.onFocusKeyboardShortcut?.(event)) {
+ return;
+ }
+
+ event.preventDefault();
+ this.focus();
+ });
+ }
+
+ componentWillUnmount() {
+ if (this.shortcuts) {
+ this.shortcuts.destroy();
+ }
+
+ // Clean up an existing timeout.
+ if (this.searchTimeout) {
+ clearTimeout(this.searchTimeout);
+ }
+ }
+
+ focus() {
+ if (this.inputRef) {
+ this.inputRef.current.focus();
+ }
+ }
+
+ onChange(inputValue = "") {
+ if (this.state.value !== inputValue) {
+ this.setState({
+ focused: true,
+ value: inputValue,
+ });
+ }
+
+ if (!this.props.delay) {
+ this.props.onChange(inputValue);
+ return;
+ }
+
+ // Clean up an existing timeout before creating a new one.
+ if (this.searchTimeout) {
+ clearTimeout(this.searchTimeout);
+ }
+
+ // Execute the search after a timeout. It makes the UX
+ // smoother if the user is typing quickly.
+ this.searchTimeout = setTimeout(() => {
+ this.searchTimeout = null;
+ this.props.onChange(this.state.value);
+ }, this.props.delay);
+ }
+
+ onClearButtonClick() {
+ this.onChange("");
+
+ if (this.props.onClearButtonClick) {
+ this.props.onClearButtonClick();
+ }
+ }
+
+ onFocus() {
+ if (this.props.onFocus) {
+ this.props.onFocus();
+ }
+
+ this.setState({ focused: true });
+ }
+
+ onBlur() {
+ if (this.props.onBlur) {
+ this.props.onBlur();
+ }
+
+ this.setState({ focused: false });
+ }
+
+ onKeyDown(e) {
+ if (this.props.onKeyDown) {
+ this.props.onKeyDown(e);
+ }
+
+ const autocomplete = this.autocompleteRef.current;
+ if (!autocomplete || autocomplete.state.list.length <= 0) {
+ return;
+ }
+
+ switch (e.key) {
+ case "ArrowDown":
+ e.preventDefault();
+ autocomplete.jumpBy(1);
+ break;
+ case "ArrowUp":
+ e.preventDefault();
+ autocomplete.jumpBy(-1);
+ break;
+ case "PageDown":
+ e.preventDefault();
+ autocomplete.jumpBy(5);
+ break;
+ case "PageUp":
+ e.preventDefault();
+ autocomplete.jumpBy(-5);
+ break;
+ case "Enter":
+ case "Tab":
+ e.preventDefault();
+ autocomplete.select();
+ break;
+ case "Escape":
+ e.preventDefault();
+ this.onBlur();
+ break;
+ case "Home":
+ e.preventDefault();
+ autocomplete.jumpToTop();
+ break;
+ case "End":
+ e.preventDefault();
+ autocomplete.jumpToBottom();
+ break;
+ }
+ }
+
+ render() {
+ const {
+ autocompleteProvider,
+ summary,
+ summaryTooltip,
+ learnMoreTitle,
+ learnMoreUrl,
+ placeholder,
+ type = "search",
+ } = this.props;
+ const { value } = this.state;
+ const showAutocomplete =
+ autocompleteProvider && this.state.focused && value !== "";
+ const showLearnMoreLink = learnMoreUrl && value === "";
+
+ const inputClassList = [`devtools-${type}input`];
+
+ return dom.div(
+ { className: "devtools-searchbox" },
+ dom.input({
+ className: inputClassList.join(" "),
+ onBlur: this.onBlur,
+ onChange: e => this.onChange(e.target.value),
+ onFocus: this.onFocus,
+ onKeyDown: this.onKeyDown,
+ placeholder,
+ ref: this.inputRef,
+ value,
+ type: "search",
+ }),
+ showLearnMoreLink &&
+ MDNLink({
+ title: learnMoreTitle,
+ url: learnMoreUrl,
+ }),
+ summary
+ ? dom.span(
+ {
+ className: "devtools-searchinput-summary",
+ title: summaryTooltip || "",
+ },
+ summary
+ )
+ : null,
+ dom.button({
+ className: "devtools-searchinput-clear",
+ hidden: value === "",
+ onClick: this.onClearButtonClick,
+ title: l10n.getStr("searchBox.clearButtonTitle"),
+ }),
+ showAutocomplete &&
+ SearchBoxAutocompletePopup({
+ autocompleteProvider,
+ filter: value,
+ onItemSelected: itemValue => this.onChange(itemValue),
+ ref: this.autocompleteRef,
+ })
+ );
+ }
+}
+
+module.exports = SearchBox;
diff --git a/devtools/client/shared/components/SearchBoxAutocompletePopup.js b/devtools/client/shared/components/SearchBoxAutocompletePopup.js
new file mode 100644
index 0000000000..08aad18872
--- /dev/null
+++ b/devtools/client/shared/components/SearchBoxAutocompletePopup.js
@@ -0,0 +1,150 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+class SearchBoxAutocompletePopup extends Component {
+ static get propTypes() {
+ return {
+ /**
+ * autocompleteProvider takes search-box's entire input text as `filter` argument
+ * ie. "is:cached pr"
+ * returned value is array of objects like below
+ * [{value: "is:cached protocol", displayValue: "protocol"}[, ...]]
+ * `value` is used to update the search-box input box for given item
+ * `displayValue` is used to render the autocomplete list
+ */
+ autocompleteProvider: PropTypes.func.isRequired,
+ filter: PropTypes.string.isRequired,
+ onItemSelected: PropTypes.func.isRequired,
+ };
+ }
+
+ constructor(props, context) {
+ super(props, context);
+ this.state = this.computeState(props);
+ this.computeState = this.computeState.bind(this);
+ this.jumpToTop = this.jumpToTop.bind(this);
+ this.jumpToBottom = this.jumpToBottom.bind(this);
+ this.jumpBy = this.jumpBy.bind(this);
+ this.select = this.select.bind(this);
+ this.onMouseDown = this.onMouseDown.bind(this);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ if (this.props.filter === nextProps.filter) {
+ return;
+ }
+ this.setState(this.computeState(nextProps));
+ }
+
+ componentDidUpdate() {
+ if (this.refs.selected) {
+ this.refs.selected.scrollIntoView(false);
+ }
+ }
+
+ computeState({ autocompleteProvider, filter }) {
+ const list = autocompleteProvider(filter);
+ const selectedIndex = list.length ? 0 : -1;
+
+ return { list, selectedIndex };
+ }
+
+ /**
+ * Use this method to select the top-most item
+ * This method is public, called outside of the autocomplete-popup component.
+ */
+ jumpToTop() {
+ this.setState({ selectedIndex: 0 });
+ }
+
+ /**
+ * Use this method to select the bottom-most item
+ * This method is public.
+ */
+ jumpToBottom() {
+ this.setState({ selectedIndex: this.state.list.length - 1 });
+ }
+
+ /**
+ * Increment the selected index with the provided increment value. Will cycle to the
+ * beginning/end of the list if the index exceeds the list boundaries.
+ * This method is public.
+ *
+ * @param {number} increment - No. of hops in the direction
+ */
+ jumpBy(increment = 1) {
+ const { list, selectedIndex } = this.state;
+ let nextIndex = selectedIndex + increment;
+ if (increment > 0) {
+ // Positive cycling
+ nextIndex = nextIndex > list.length - 1 ? 0 : nextIndex;
+ } else if (increment < 0) {
+ // Inverse cycling
+ nextIndex = nextIndex < 0 ? list.length - 1 : nextIndex;
+ }
+ this.setState({ selectedIndex: nextIndex });
+ }
+
+ /**
+ * Submit the currently selected item to the onItemSelected callback
+ * This method is public.
+ */
+ select() {
+ if (this.refs.selected) {
+ this.props.onItemSelected(this.refs.selected.dataset.value);
+ }
+ }
+
+ onMouseDown(e) {
+ e.preventDefault();
+ this.setState(
+ { selectedIndex: Number(e.target.dataset.index) },
+ this.select
+ );
+ }
+
+ render() {
+ const { list } = this.state;
+
+ return (
+ !!list.length &&
+ dom.div(
+ { className: "devtools-autocomplete-popup devtools-monospace" },
+ dom.ul(
+ { className: "devtools-autocomplete-listbox" },
+ list.map((item, i) => {
+ const isSelected = this.state.selectedIndex == i;
+ const itemClassList = ["autocomplete-item"];
+
+ if (isSelected) {
+ itemClassList.push("autocomplete-selected");
+ }
+ return dom.li(
+ {
+ key: i,
+ "data-index": i,
+ "data-value": item.value,
+ className: itemClassList.join(" "),
+ ref: isSelected ? "selected" : null,
+ onMouseDown: this.onMouseDown,
+ },
+ item.displayValue
+ );
+ })
+ )
+ )
+ );
+ }
+}
+
+module.exports = SearchBoxAutocompletePopup;
diff --git a/devtools/client/shared/components/SearchModifiers.css b/devtools/client/shared/components/SearchModifiers.css
new file mode 100644
index 0000000000..b92f12b1f9
--- /dev/null
+++ b/devtools/client/shared/components/SearchModifiers.css
@@ -0,0 +1,64 @@
+/* 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/. */
+
+/* Search Modifiers */
+
+.search-modifiers {
+ display: flex;
+ align-items: center;
+}
+
+.search-modifiers button {
+ padding: 2px;
+ margin: 0 3px;
+ border: none;
+ background: none;
+ width: 20px;
+ height: 20px;
+ border-radius: 2px;
+}
+
+.search-modifiers .pipe-divider {
+ flex: none;
+ align-self: stretch;
+ width: 1px;
+ vertical-align: middle;
+ margin: 4px;
+ background-color: var(--theme-splitter-color);
+}
+
+.search-modifiers button > span {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ vertical-align: middle;
+ background-color: transparent;
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: contain;
+ /* do not let images shrink when used as flex children */
+ flex-shrink: 0;
+ fill: var(--theme-icon-color);
+ -moz-context-properties: fill;
+}
+
+.search-modifiers button > span.case-match {
+ background-image: url(chrome://devtools/content/debugger/images/case-match.svg);
+}
+
+.search-modifiers button > span.regex-match {
+ background-image: url(chrome://devtools/content/debugger/images/regex-match.svg);
+}
+
+.search-modifiers button > span.whole-word-match {
+ background-image: url(chrome://devtools/content/debugger/images/whole-word-match.svg);
+}
+
+.search-modifiers button:hover {
+ fill: var(--theme-toolbar-background-hover);
+}
+
+.search-modifiers button.active > span {
+ fill: var(--theme-icon-checked-color);
+}
diff --git a/devtools/client/shared/components/SearchModifiers.js b/devtools/client/shared/components/SearchModifiers.js
new file mode 100644
index 0000000000..fb66076b83
--- /dev/null
+++ b/devtools/client/shared/components/SearchModifiers.js
@@ -0,0 +1,84 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const {
+ div,
+ span,
+ button,
+} = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+loader.lazyGetter(this, "l10n", function () {
+ const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+ return new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+ );
+});
+
+const modifierOptions = [
+ {
+ value: "regexMatch",
+ className: "regex-match-btn",
+ svgName: "regex-match",
+ tooltip: l10n.getStr("searchModifier.regExpModifier"),
+ },
+ {
+ value: "caseSensitive",
+ className: "case-sensitive-btn",
+ svgName: "case-match",
+ tooltip: l10n.getStr("searchModifier.caseSensitiveModifier"),
+ },
+ {
+ value: "wholeWord",
+ className: "whole-word-btn",
+ svgName: "whole-word-match",
+ tooltip: l10n.getStr("searchModifier.wholeWordModifier"),
+ },
+];
+
+class SearchModifiers extends Component {
+ static get propTypes() {
+ return {
+ modifiers: PropTypes.object.isRequired,
+ onToggleSearchModifier: PropTypes.func.isRequired,
+ };
+ }
+
+ #renderSearchModifier({ value, className, svgName, tooltip }) {
+ const { modifiers, onToggleSearchModifier } = this.props;
+
+ return button(
+ {
+ className: `${className} ${modifiers?.[value] ? "active" : ""}`,
+ onMouseDown: () => {
+ modifiers[value] = !modifiers[value];
+ onToggleSearchModifier(modifiers);
+ },
+ onKeyDown: e => {
+ if (e.key === "Enter") {
+ modifiers[value] = !modifiers[value];
+ onToggleSearchModifier(modifiers);
+ }
+ },
+ title: tooltip,
+ },
+ span({ className: svgName })
+ );
+ }
+
+ render() {
+ return div(
+ { className: "search-modifiers" },
+ span({ className: "pipe-divider" }),
+ modifierOptions.map(options => this.#renderSearchModifier(options))
+ );
+ }
+}
+
+module.exports = SearchModifiers;
diff --git a/devtools/client/shared/components/Sidebar.js b/devtools/client/shared/components/Sidebar.js
new file mode 100644
index 0000000000..bf9ef9938d
--- /dev/null
+++ b/devtools/client/shared/components/Sidebar.js
@@ -0,0 +1,98 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createFactory,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+const SidebarToggle = createFactory(
+ require("resource://devtools/client/shared/components/SidebarToggle.js")
+);
+const Tabs = createFactory(
+ require("resource://devtools/client/shared/components/tabs/Tabs.js").Tabs
+);
+
+class Sidebar extends PureComponent {
+ static get propTypes() {
+ return {
+ children: PropTypes.oneOfType([PropTypes.array, PropTypes.element])
+ .isRequired,
+ onAfterChange: PropTypes.func,
+ onAllTabsMenuClick: PropTypes.func,
+ renderOnlySelected: PropTypes.bool,
+ showAllTabsMenu: PropTypes.bool,
+ allTabsMenuButtonTooltip: PropTypes.string,
+ sidebarToggleButton: PropTypes.shape({
+ collapsed: PropTypes.bool.isRequired,
+ collapsePaneTitle: PropTypes.string.isRequired,
+ expandPaneTitle: PropTypes.string.isRequired,
+ onClick: PropTypes.func.isRequired,
+ alignRight: PropTypes.bool,
+ canVerticalSplit: PropTypes.bool,
+ }),
+ activeTab: PropTypes.number,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.renderSidebarToggle = this.renderSidebarToggle.bind(this);
+ }
+
+ renderSidebarToggle() {
+ if (!this.props.sidebarToggleButton) {
+ return null;
+ }
+
+ const {
+ collapsed,
+ collapsePaneTitle,
+ expandPaneTitle,
+ onClick,
+ alignRight,
+ canVerticalSplit,
+ } = this.props.sidebarToggleButton;
+
+ return SidebarToggle({
+ collapsed,
+ collapsePaneTitle,
+ expandPaneTitle,
+ onClick,
+ alignRight,
+ canVerticalSplit,
+ });
+ }
+
+ render() {
+ const { renderSidebarToggle } = this;
+ const {
+ children,
+ onAfterChange,
+ onAllTabsMenuClick,
+ renderOnlySelected,
+ showAllTabsMenu,
+ allTabsMenuButtonTooltip,
+ activeTab,
+ } = this.props;
+
+ return Tabs(
+ {
+ onAfterChange,
+ onAllTabsMenuClick,
+ renderOnlySelected,
+ renderSidebarToggle,
+ showAllTabsMenu,
+ allTabsMenuButtonTooltip,
+ activeTab,
+ },
+ children
+ );
+ }
+}
+
+module.exports = Sidebar;
diff --git a/devtools/client/shared/components/SidebarToggle.css b/devtools/client/shared/components/SidebarToggle.css
new file mode 100644
index 0000000000..f715816d8c
--- /dev/null
+++ b/devtools/client/shared/components/SidebarToggle.css
@@ -0,0 +1,39 @@
+/* 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/. */
+
+.sidebar-toggle {
+ display: block;
+}
+
+.sidebar-toggle::before,
+.sidebar-toggle.pane-collapsed:dir(rtl)::before {
+ background-image: url(chrome://devtools/skin/images/pane-collapse.svg);
+}
+
+.sidebar-toggle.pane-collapsed::before,
+.sidebar-toggle:dir(rtl)::before {
+ background-image: url(chrome://devtools/skin/images/pane-expand.svg);
+}
+
+.sidebar-toggle.alignRight::before {
+ transform: scaleX(-1);
+}
+
+.sidebar-toggle.alignRight {
+ order: 10
+}
+
+/* Rotate button icon 90deg if the toolbox container is
+ in vertical mode (sidebar displayed under the main panel) */
+@media (max-width: 700px) {
+ .sidebar-toggle:not(.disableVerticalBehaviour)::before {
+ transform: rotate(90deg);
+ }
+
+ /* Since RTL swaps the used images, we need to flip them
+ the other way round */
+ .sidebar-toggle:not(.disableVerticalBehaviour):dir(rtl)::before {
+ transform: rotate(-90deg);
+ }
+}
diff --git a/devtools/client/shared/components/SidebarToggle.js b/devtools/client/shared/components/SidebarToggle.js
new file mode 100644
index 0000000000..3cb2a28438
--- /dev/null
+++ b/devtools/client/shared/components/SidebarToggle.js
@@ -0,0 +1,89 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+// Shortcuts
+const { button } = dom;
+
+/**
+ * Sidebar toggle button. This button is used to exapand
+ * and collapse Sidebar.
+ */
+class SidebarToggle extends Component {
+ static get propTypes() {
+ return {
+ // Set to true if collapsed.
+ collapsed: PropTypes.bool.isRequired,
+ // Tooltip text used when the button indicates expanded state.
+ collapsePaneTitle: PropTypes.string.isRequired,
+ // Tooltip text used when the button indicates collapsed state.
+ expandPaneTitle: PropTypes.string.isRequired,
+ // Click callback
+ onClick: PropTypes.func.isRequired,
+ // align toggle button to right
+ alignRight: PropTypes.bool,
+ // if set to true toggle-button rotate 90
+ canVerticalSplit: PropTypes.bool,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ alignRight: false,
+ canVerticalSplit: true,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ collapsed: props.collapsed,
+ };
+
+ this.onClick = this.onClick.bind(this);
+ }
+
+ // Events
+
+ onClick(event) {
+ event.stopPropagation();
+ this.setState({ collapsed: !this.state.collapsed });
+ this.props.onClick(event);
+ }
+
+ // Rendering
+
+ render() {
+ const title = this.state.collapsed
+ ? this.props.expandPaneTitle
+ : this.props.collapsePaneTitle;
+
+ const classNames = ["devtools-button", "sidebar-toggle"];
+ if (this.state.collapsed) {
+ classNames.push("pane-collapsed");
+ }
+ if (this.props.alignRight) {
+ classNames.push("alignRight");
+ }
+ if (!this.props.canVerticalSplit) {
+ classNames.push("disableVerticalBehaviour");
+ }
+
+ return button({
+ className: classNames.join(" "),
+ title,
+ onClick: this.onClick,
+ });
+ }
+}
+
+module.exports = SidebarToggle;
diff --git a/devtools/client/shared/components/SmartTrace.css b/devtools/client/shared/components/SmartTrace.css
new file mode 100644
index 0000000000..838711dd9e
--- /dev/null
+++ b/devtools/client/shared/components/SmartTrace.css
@@ -0,0 +1,170 @@
+/* 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/. */
+
+/**
+ * SmartTrace Component
+ * Styles for React component at `devtools/client/shared/components/SmartTrace.js`
+ */
+
+
+.frames-group .frame{
+ display: block;
+ padding-inline-start: 16px;
+}
+
+.img.annotation-logo{
+ background-color: var(--theme-body-color);
+}
+
+
+.frames [role="list"]{
+ display: inline-grid;
+ grid-template-columns: auto 1fr;
+ grid-column-gap: 8px;
+}
+
+.frames .frame {
+ /* Parent is a grid container whose grid template we want to use, so span the whole line */
+ grid-column: 1 / -1;
+ display: grid;
+ /* Grid is defined in `.frames [role="list"]` rule */
+ grid-template-columns: subgrid;
+ cursor: pointer;
+ white-space: normal;
+}
+
+.frames .title {
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ grid-column: 1 / 2;
+ color: var(--console-output-color);
+}
+
+.frames .location {
+ color: var(--frame-link-source);
+ grid-column: -1 / -2;
+ /* Force the location to be on one line and crop at start */
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ direction: rtl;
+ text-align: end;
+}
+
+.frames .location .line {
+ color: var(--frame-link-line-color);
+}
+
+.frames .frames-list .frame:hover .location {
+ text-decoration: underline;
+}
+
+.frames .location-async-cause {
+ grid-column: 1 / -1;
+ color: var(--theme-comment);
+}
+
+/******* Group styles *******/
+.frames-group {
+ grid-column:1 / -1;
+}
+
+.frames .frames-group .group {
+ display: flex;
+}
+
+.group-description {
+ display: flex;
+ align-items: center;
+ color: var(--console-output-color);
+}
+
+.frames .frames-group .frames-list {
+ margin-block-start: 2px;
+ /*
+ * We want to display each frame name on its own row, without having new lines in the
+ * clipboard when copying it. This does the trick.
+ */
+ display: grid;
+ grid-template-columns: 1fr;
+}
+
+.frames .frames-group .frames-list .frame {
+ padding-inline-start: 0;
+ text-overflow: ellipsis;
+}
+
+.frames-group .frames-list .title {
+ grid-column: -1 / 1;
+ padding-inline-start: 16px;
+}
+
+.frames .frames-group .frames-list .frame:first-of-type {
+ border-top: 1px solid var(--theme-splitter-color);
+ padding-block-start: 4px;
+}
+
+.frames .frames-group .frames-list .frame:last-of-type {
+ margin-block-end: 4px;
+ border-bottom: 1px solid var(--theme-splitter-color);
+}
+
+.badge {
+ background: var(--theme-toolbar-background-hover);
+ color: var(--theme-body-color);
+ border-radius: 8px;
+ padding: 1px 4px;
+ font-size: 0.9em;
+ display: inline-block;
+ text-align: center;
+ cursor: default;
+ margin-inline-start: 4px;
+}
+
+.frames .frames-group.expanded .group-description,
+.frames .frames-group.expanded .badge {
+ color: var(--theme-highlight-blue);
+}
+
+/** Images **/
+
+.frames .img.annotation-logo {
+ /* FIXME: In order to display the Framework icons, we need to find a way to share CSS
+ * from the debugger, where the background images are defined.
+ * See https://github.com/firefox-devtools/debugger.html/issues/7782.
+ */
+ display: none;
+ /*
+ background-color:var(--theme-body-color);
+ display: inline-block;
+ width: 12px;
+ height:12px;
+ vertical-align: middle;
+ margin-inline-end:4px;
+ */
+}
+
+.expanded .img.annotation-logo {
+ background-color: currentColor;
+}
+
+.group .img.arrow {
+ mask: url("chrome://devtools/content/debugger/images/arrow.svg");
+ margin-inline-end: 4px;
+ background-color: var(--theme-icon-dimmed-color);
+ width: 10px;
+ height: 10px;
+ mask-size: 100%;
+ display: inline-block;
+ transform: rotate(-90deg);
+ transition: transform 0.18s ease;
+}
+
+.group .img.arrow.expanded {
+ transform: rotate(0);
+}
+
+/* Frameworks */
+:root.theme-dark .annotation-logo:not(.angular) {
+ background-color: var(--theme-highlight-blue);
+}
diff --git a/devtools/client/shared/components/SmartTrace.js b/devtools/client/shared/components/SmartTrace.js
new file mode 100644
index 0000000000..d9613be7b8
--- /dev/null
+++ b/devtools/client/shared/components/SmartTrace.js
@@ -0,0 +1,319 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+
+const l10n = new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+);
+const dbgL10n = new LocalizationHelper(
+ "devtools/client/locales/debugger.properties"
+);
+const Frames = createFactory(
+ require("resource://devtools/client/debugger/src/components/SecondaryPanes/Frames/index.js")
+ .Frames
+);
+const {
+ annotateFramesWithLibrary,
+} = require("resource://devtools/client/debugger/src/utils/pause/frames/annotateFrames.js");
+const {
+ getDisplayURL,
+} = require("resource://devtools/client/debugger/src/utils/sources-tree/getURL.js");
+
+class SmartTrace extends Component {
+ static get propTypes() {
+ return {
+ stacktrace: PropTypes.array.isRequired,
+ onViewSource: PropTypes.func.isRequired,
+ onViewSourceInDebugger: PropTypes.func.isRequired,
+ // Service to enable the source map feature.
+ sourceMapURLService: PropTypes.object,
+ // A number in ms (defaults to 100) which we'll wait before doing the first actual
+ // render of this component, in order to avoid shifting layout rapidly in case the
+ // page is using sourcemap.
+ // Setting it to 0 or anything else than a number will force the first render to
+ // happen immediatly, without any delay.
+ initialRenderDelay: PropTypes.number,
+ onSourceMapResultDebounceDelay: PropTypes.number,
+ // Function that will be called when the SmartTrace is ready, i.e. once it was
+ // rendered.
+ onReady: PropTypes.func,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ initialRenderDelay: 100,
+ onSourceMapResultDebounceDelay: 200,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ hasError: false,
+ // If a sourcemap service is passed, we want to introduce a small delay in rendering
+ // so we can have the results from the sourcemap service, or render if they're not
+ // available yet.
+ ready: !props.sourceMapURLService || !this.hasInitialRenderDelay(),
+ updateCount: 0,
+ // Original positions for each indexed position
+ originalLocations: null,
+ };
+ }
+
+ getChildContext() {
+ return { l10n: dbgL10n };
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillMount() {
+ if (this.props.sourceMapURLService) {
+ this.sourceMapURLServiceUnsubscriptions = [];
+ const sourceMapInit = Promise.all(
+ this.props.stacktrace.map(
+ ({ filename, sourceId, lineNumber, columnNumber }, index) =>
+ new Promise(resolve => {
+ const callback = originalLocation => {
+ this.onSourceMapServiceChange(originalLocation, index);
+ resolve();
+ };
+
+ this.sourceMapURLServiceUnsubscriptions.push(
+ this.props.sourceMapURLService.subscribeByLocation(
+ {
+ id: sourceId,
+ url: filename.split(" -> ").pop(),
+ line: lineNumber,
+ column: columnNumber,
+ },
+ callback
+ )
+ );
+ })
+ )
+ );
+
+ // Without initial render delay, we don't have to do anything; if the frames are
+ // sourcemapped, we will get new renders from onSourceMapServiceChange.
+ if (!this.hasInitialRenderDelay()) {
+ return;
+ }
+
+ const delay = new Promise(res => {
+ this.initialRenderDelayTimeoutId = setTimeout(
+ res,
+ this.props.initialRenderDelay
+ );
+ });
+
+ // We wait either for the delay to be over (if it exists), or the sourcemapService
+ // results to be available, before setting the state as initialized.
+ Promise.race([delay, sourceMapInit]).then(() => {
+ if (this.initialRenderDelayTimeoutId) {
+ clearTimeout(this.initialRenderDelayTimeoutId);
+ }
+ this.setState(state => ({
+ // Force-update so that the ready state is detected.
+ updateCount: state.updateCount + 1,
+ ready: true,
+ }));
+ });
+ }
+ }
+
+ componentDidMount() {
+ if (this.props.onReady && this.state.ready) {
+ this.props.onReady();
+ }
+ }
+
+ shouldComponentUpdate(_, nextState) {
+ if (this.state.updateCount !== nextState.updateCount) {
+ return true;
+ }
+
+ return false;
+ }
+
+ componentDidUpdate(_, previousState) {
+ if (this.props.onReady && !previousState.ready && this.state.ready) {
+ this.props.onReady();
+ }
+ }
+
+ componentWillUnmount() {
+ if (this.initialRenderDelayTimeoutId) {
+ clearTimeout(this.initialRenderDelayTimeoutId);
+ }
+
+ if (this.onFrameLocationChangedTimeoutId) {
+ clearTimeout(this.initialRenderDelayTimeoutId);
+ }
+
+ if (this.sourceMapURLServiceUnsubscriptions) {
+ this.sourceMapURLServiceUnsubscriptions.forEach(unsubscribe => {
+ unsubscribe();
+ });
+ }
+ }
+
+ componentDidCatch(error, info) {
+ console.error(
+ "Error while rendering stacktrace:",
+ error,
+ info,
+ "props:",
+ this.props
+ );
+ this.setState(state => ({
+ // Force-update so the error is detected.
+ updateCount: state.updateCount + 1,
+ hasError: true,
+ }));
+ }
+
+ onSourceMapServiceChange(originalLocation, index) {
+ this.setState(({ originalLocations }) => {
+ if (!originalLocations) {
+ originalLocations = Array.from({
+ length: this.props.stacktrace.length,
+ });
+ }
+ return {
+ originalLocations: [
+ ...originalLocations.slice(0, index),
+ originalLocation,
+ ...originalLocations.slice(index + 1),
+ ],
+ };
+ });
+
+ if (this.onFrameLocationChangedTimeoutId) {
+ clearTimeout(this.onFrameLocationChangedTimeoutId);
+ }
+
+ // Since a trace may have many original positions, we don't want to
+ // constantly re-render every time one becomes available. To avoid this,
+ // we only update the component after an initial timeout, and on a
+ // debounce edge as more positions load after that.
+ if (this.state.ready === true) {
+ this.onFrameLocationChangedTimeoutId = setTimeout(() => {
+ this.setState(state => ({
+ updateCount: state.updateCount + 1,
+ }));
+ }, this.props.onSourceMapResultDebounceDelay);
+ }
+ }
+
+ hasInitialRenderDelay() {
+ return (
+ Number.isFinite(this.props.initialRenderDelay) &&
+ this.props.initialRenderDelay > 0
+ );
+ }
+
+ render() {
+ if (
+ this.state.hasError ||
+ (this.hasInitialRenderDelay() && !this.state.ready)
+ ) {
+ return null;
+ }
+
+ const { onViewSourceInDebugger, onViewSource, stacktrace } = this.props;
+ const { originalLocations } = this.state;
+
+ const frames = stacktrace.map(
+ (
+ {
+ filename,
+ sourceId,
+ lineNumber,
+ columnNumber,
+ functionName,
+ asyncCause,
+ },
+ i
+ ) => {
+ // Create partial debugger frontend "location" objects compliant with <Frames> react component requirements
+ const sourceUrl = filename.split(" -> ").pop();
+ const generatedLocation = {
+ line: lineNumber,
+ column: columnNumber,
+ source: {
+ // 'id' isn't used by Frames, but by selectFrame callback below
+ id: sourceId,
+ url: sourceUrl,
+ // 'displayURL' might be used by FrameComponent via getFilename
+ displayURL: getDisplayURL(sourceUrl),
+ },
+ };
+ let location = generatedLocation;
+ const originalLocation = originalLocations?.[i];
+ if (originalLocation) {
+ location = {
+ line: originalLocation.line,
+ column: originalLocation.column,
+ source: {
+ url: originalLocation.url,
+ // 'displayURL' might be used by FrameComponent via getFilename
+ displayURL: getDisplayURL(originalLocation.url),
+ },
+ };
+ }
+
+ // Create partial debugger frontend "frame" objects compliant with <Frames> react component requirements
+ return {
+ id: "fake-frame-id-" + i,
+ displayName: functionName,
+ asyncCause,
+ location,
+ // Note that for now, Frames component only uses 'location' attribute
+ // and never the 'generatedLocation'.
+ // But the code below does, the selectFrame callback.
+ generatedLocation,
+ };
+ }
+ );
+ annotateFramesWithLibrary(frames);
+
+ return Frames({
+ frames,
+ selectFrame: ({ generatedLocation }) => {
+ const viewSource = onViewSourceInDebugger || onViewSource;
+
+ viewSource({
+ id: generatedLocation.source.id,
+ url: generatedLocation.source.url,
+ line: generatedLocation.line,
+ column: generatedLocation.column,
+ });
+ },
+ getFrameTitle: url => {
+ return l10n.getFormatStr("frame.viewsourceindebugger", url);
+ },
+ disableFrameTruncate: true,
+ disableContextMenu: true,
+ frameworkGroupingOn: true,
+ // Force displaying the original location (we might try to use current Debugger state?)
+ shouldDisplayOriginalLocation: true,
+ displayFullUrl: !this.state || !this.state.originalLocations,
+ panel: "webconsole",
+ });
+ }
+}
+
+SmartTrace.childContextTypes = {
+ l10n: PropTypes.object,
+};
+
+module.exports = SmartTrace;
diff --git a/devtools/client/shared/components/StackTrace.js b/devtools/client/shared/components/StackTrace.js
new file mode 100644
index 0000000000..e89e79b344
--- /dev/null
+++ b/devtools/client/shared/components/StackTrace.js
@@ -0,0 +1,96 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const Frame = createFactory(
+ require("resource://devtools/client/shared/components/Frame.js")
+);
+
+const l10n = new LocalizationHelper(
+ "devtools/client/locales/webconsole.properties"
+);
+
+class AsyncFrameClass extends Component {
+ static get propTypes() {
+ return {
+ asyncCause: PropTypes.string.isRequired,
+ };
+ }
+
+ render() {
+ const { asyncCause } = this.props;
+
+ return dom.span(
+ { className: "frame-link-async-cause" },
+ l10n.getFormatStr("stacktrace.asyncStack", asyncCause)
+ );
+ }
+}
+
+class StackTrace extends Component {
+ static get propTypes() {
+ return {
+ stacktrace: PropTypes.array.isRequired,
+ onViewSourceInDebugger: PropTypes.func.isRequired,
+ // Service to enable the source map feature.
+ sourceMapURLService: PropTypes.object,
+ };
+ }
+
+ render() {
+ const { stacktrace, onViewSourceInDebugger, sourceMapURLService } =
+ this.props;
+
+ if (!stacktrace || !stacktrace.length) {
+ return null;
+ }
+
+ const frames = [];
+ stacktrace.forEach((s, i) => {
+ if (s.asyncCause) {
+ frames.push(
+ "\t",
+ AsyncFrame({
+ key: `${i}-asyncframe`,
+ asyncCause: s.asyncCause,
+ }),
+ "\n"
+ );
+ }
+
+ frames.push(
+ "\t",
+ Frame({
+ key: `${i}-frame`,
+ frame: {
+ functionDisplayName: s.functionName,
+ source: s.filename,
+ line: s.lineNumber,
+ column: s.columnNumber,
+ },
+ showFunctionName: true,
+ showAnonymousFunctionName: true,
+ showFullSourceUrl: true,
+ onClick: onViewSourceInDebugger,
+ sourceMapURLService,
+ }),
+ "\n"
+ );
+ });
+
+ return dom.div({ className: "stack-trace" }, frames);
+ }
+}
+
+const AsyncFrame = createFactory(AsyncFrameClass);
+
+module.exports = StackTrace;
diff --git a/devtools/client/shared/components/Tree.css b/devtools/client/shared/components/Tree.css
new file mode 100644
index 0000000000..3a0667cd5c
--- /dev/null
+++ b/devtools/client/shared/components/Tree.css
@@ -0,0 +1,86 @@
+/* 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/. */
+
+/* We can remove the outline since we do add our own focus style on nodes */
+.tree:focus {
+ outline: none;
+}
+
+.tree.inline {
+ display: inline-block;
+}
+
+.tree.nowrap {
+ white-space: nowrap;
+}
+
+.tree.noselect {
+ user-select: none;
+}
+
+.tree .tree-node {
+ display: flex;
+}
+
+.tree .tree-node:not(.focused):hover {
+ background-color: var(--theme-selection-background-hover);
+}
+
+.tree-indent {
+ display: inline-block;
+ width: 12px;
+ margin-inline-start: 3px;
+ border-inline-start: 1px solid #a2d1ff;
+ flex-shrink: 0;
+ height: 0;
+}
+
+.tree-node[data-expandable="false"] .tree-last-indent {
+ /* The 13px value is taken from the total width and margins of the arrow
+ element of expandables nodes (10px width + 3px margins). That way the
+ node's text are indented the same for both expandable and non-expandable
+ nodes */
+ margin-inline-end: 13px;
+}
+
+.tree .tree-node[data-expandable="true"] {
+ cursor: default;
+}
+
+.tree-node button.arrow {
+ mask: url("chrome://devtools/content/debugger/images/arrow.svg") no-repeat center;
+ mask-size: 10px;
+ vertical-align: -1px;
+ width: 10px;
+ height: 10px;
+ border: 0;
+ padding: 0;
+ margin-inline-end: 4px;
+ transform-origin: center center;
+ transition: transform 125ms var(--animation-curve);
+ background-color: var(--theme-icon-dimmed-color);
+}
+
+.tree-node button.arrow:not(.expanded) {
+ transform: rotate(-90deg);
+}
+
+html[dir="rtl"] .tree-node button.arrow:not(.expanded) {
+ transform: rotate(90deg);
+}
+
+.tree .tree-node.focused {
+ color: var(--theme-selection-color);
+ background-color: var(--theme-selection-background);
+}
+
+/* Invert text selection color in selected rows */
+.tree .tree-node.focused ::selection {
+ color: var(--theme-selection-background);
+ background-color: var(--theme-selection-color);
+}
+
+.tree-node.focused button.arrow {
+ background-color: currentColor;
+}
diff --git a/devtools/client/shared/components/Tree.js b/devtools/client/shared/components/Tree.js
new file mode 100644
index 0000000000..b1e9e18780
--- /dev/null
+++ b/devtools/client/shared/components/Tree.js
@@ -0,0 +1,1072 @@
+/* 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/>. */
+
+"use strict";
+
+const React = require("resource://devtools/client/shared/vendor/react.js");
+const { Component, createFactory } = React;
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+// Localized strings for (devtools/client/locales/en-US/components.properties)
+loader.lazyGetter(this, "L10N_COMPONENTS", function () {
+ const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+ return new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+ );
+});
+
+loader.lazyGetter(this, "EXPAND_LABEL", function () {
+ return L10N_COMPONENTS.getStr("treeNode.expandButtonTitle");
+});
+
+loader.lazyGetter(this, "COLLAPSE_LABEL", function () {
+ return L10N_COMPONENTS.getStr("treeNode.collapseButtonTitle");
+});
+
+// depth
+const AUTO_EXPAND_DEPTH = 0;
+
+// Simplied selector targetting elements that can receive the focus,
+// full version at https://stackoverflow.com/questions/1599660.
+const FOCUSABLE_SELECTOR = [
+ "a[href]:not([tabindex='-1'])",
+ "button:not([disabled], [tabindex='-1'])",
+ "iframe:not([tabindex='-1'])",
+ "input:not([disabled], [tabindex='-1'])",
+ "select:not([disabled], [tabindex='-1'])",
+ "textarea:not([disabled], [tabindex='-1'])",
+ "[tabindex]:not([tabindex='-1'])",
+].join(", ");
+
+/**
+ * An arrow that displays whether its node is expanded (▼) or collapsed
+ * (▶). When its node has no children, it is hidden.
+ */
+class ArrowExpander extends Component {
+ static get propTypes() {
+ return {
+ expanded: PropTypes.bool,
+ };
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return this.props.expanded !== nextProps.expanded;
+ }
+
+ render() {
+ const { expanded } = this.props;
+
+ const classNames = ["arrow"];
+ const title = expanded ? COLLAPSE_LABEL : EXPAND_LABEL;
+
+ if (expanded) {
+ classNames.push("expanded");
+ }
+ return dom.button({
+ className: classNames.join(" "),
+ title,
+ });
+ }
+}
+
+const treeIndent = dom.span({ className: "tree-indent" }, "\u200B");
+const treeLastIndent = dom.span(
+ { className: "tree-indent tree-last-indent" },
+ "\u200B"
+);
+
+class TreeNode extends Component {
+ static get propTypes() {
+ return {
+ id: PropTypes.any.isRequired,
+ index: PropTypes.number.isRequired,
+ depth: PropTypes.number.isRequired,
+ focused: PropTypes.bool.isRequired,
+ active: PropTypes.bool.isRequired,
+ expanded: PropTypes.bool.isRequired,
+ item: PropTypes.any.isRequired,
+ isExpandable: PropTypes.bool.isRequired,
+ onClick: PropTypes.func,
+ shouldItemUpdate: PropTypes.func,
+ renderItem: PropTypes.func.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.treeNodeRef = React.createRef();
+
+ this._onKeyDown = this._onKeyDown.bind(this);
+ }
+
+ componentDidMount() {
+ // Make sure that none of the focusable elements inside the tree node
+ // container are tabbable if the tree node is not active. If the tree node
+ // is active and focus is outside its container, focus on the first
+ // focusable element inside.
+ const elms = this.getFocusableElements();
+ if (this.props.active) {
+ const doc = this.treeNodeRef.current.ownerDocument;
+ if (elms.length && !elms.includes(doc.activeElement)) {
+ elms[0].focus();
+ }
+ } else {
+ elms.forEach(elm => elm.setAttribute("tabindex", "-1"));
+ }
+ }
+
+ shouldComponentUpdate(nextProps) {
+ return (
+ this.props.item !== nextProps.item ||
+ (this.props.shouldItemUpdate &&
+ this.props.shouldItemUpdate(this.props.item, nextProps.item)) ||
+ this.props.focused !== nextProps.focused ||
+ this.props.expanded !== nextProps.expanded
+ );
+ }
+
+ /**
+ * Get a list of all elements that are focusable with a keyboard inside the
+ * tree node.
+ */
+ getFocusableElements() {
+ return this.treeNodeRef.current
+ ? Array.from(
+ this.treeNodeRef.current.querySelectorAll(FOCUSABLE_SELECTOR)
+ )
+ : [];
+ }
+
+ /**
+ * Wrap and move keyboard focus to first/last focusable element inside the
+ * tree node to prevent the focus from escaping the tree node boundaries.
+ * element).
+ *
+ * @param {DOMNode} current currently focused element
+ * @param {Boolean} back direction
+ * @return {Boolean} true there is a newly focused element.
+ */
+ _wrapMoveFocus(current, back) {
+ const elms = this.getFocusableElements();
+ let next;
+
+ if (elms.length === 0) {
+ return false;
+ }
+
+ if (back) {
+ if (elms.indexOf(current) === 0) {
+ next = elms[elms.length - 1];
+ next.focus();
+ }
+ } else if (elms.indexOf(current) === elms.length - 1) {
+ next = elms[0];
+ next.focus();
+ }
+
+ return !!next;
+ }
+
+ _onKeyDown(e) {
+ const { target, key, shiftKey } = e;
+
+ if (key !== "Tab") {
+ return;
+ }
+
+ const focusMoved = this._wrapMoveFocus(target, shiftKey);
+ if (focusMoved) {
+ // Focus was moved to the begining/end of the list, so we need to prevent
+ // the default focus change that would happen here.
+ e.preventDefault();
+ }
+
+ e.stopPropagation();
+ }
+
+ render() {
+ const {
+ depth,
+ id,
+ item,
+ focused,
+ active,
+ expanded,
+ renderItem,
+ isExpandable,
+ } = this.props;
+
+ const arrow = isExpandable
+ ? ArrowExpanderFactory({
+ item,
+ expanded,
+ })
+ : null;
+
+ let ariaExpanded;
+ if (this.props.isExpandable) {
+ ariaExpanded = false;
+ }
+ if (this.props.expanded) {
+ ariaExpanded = true;
+ }
+
+ const indents = Array.from({ length: depth }, (_, i) => {
+ if (i == depth - 1) {
+ return treeLastIndent;
+ }
+ return treeIndent;
+ });
+
+ const items = indents.concat(
+ renderItem(item, depth, focused, arrow, expanded)
+ );
+
+ return dom.div(
+ {
+ id,
+ className: `tree-node${focused ? " focused" : ""}${
+ active ? " active" : ""
+ }`,
+ onClick: this.props.onClick,
+ onKeyDownCapture: active ? this._onKeyDown : null,
+ role: "treeitem",
+ ref: this.treeNodeRef,
+ "aria-level": depth + 1,
+ "aria-expanded": ariaExpanded,
+ "data-expandable": this.props.isExpandable,
+ },
+ ...items
+ );
+ }
+}
+
+const ArrowExpanderFactory = createFactory(ArrowExpander);
+const TreeNodeFactory = createFactory(TreeNode);
+
+/**
+ * Create a function that calls the given function `fn` only once per animation
+ * frame.
+ *
+ * @param {Function} fn
+ * @param {Object} options: object that contains the following properties:
+ * - {Function} getDocument: A function that return the document
+ * the component is rendered in.
+ * @returns {Function}
+ */
+function oncePerAnimationFrame(fn, { getDocument }) {
+ let animationId = null;
+ let argsToPass = null;
+ return function (...args) {
+ argsToPass = args;
+ if (animationId !== null) {
+ return;
+ }
+
+ const doc = getDocument();
+ if (!doc) {
+ return;
+ }
+
+ animationId = doc.defaultView.requestAnimationFrame(() => {
+ fn.call(this, ...argsToPass);
+ animationId = null;
+ argsToPass = null;
+ });
+ };
+}
+
+/**
+ * A generic tree component. See propTypes for the public API.
+ *
+ * This tree component doesn't make any assumptions about the structure of your
+ * tree data. Whether children are computed on demand, or stored in an array in
+ * the parent's `_children` property, it doesn't matter. We only require the
+ * implementation of `getChildren`, `getRoots`, `getParent`, and `isExpanded`
+ * functions.
+ *
+ * This tree component is well tested and reliable. See the tests in ./tests
+ * and its usage in the memory panel in mozilla-central.
+ *
+ * This tree component doesn't make any assumptions about how to render items in
+ * the tree. You provide a `renderItem` function, and this component will ensure
+ * that only those items whose parents are expanded and which are visible in the
+ * viewport are rendered. The `renderItem` function could render the items as a
+ * "traditional" tree or as rows in a table or anything else. It doesn't
+ * restrict you to only one certain kind of tree.
+ *
+ * The tree comes with basic styling for the indent, the arrow, as well as
+ * hovered and focused styles which can be override in CSS.
+ *
+ * ### Example Usage
+ *
+ * Suppose we have some tree data where each item has this form:
+ *
+ * {
+ * id: Number,
+ * label: String,
+ * parent: Item or null,
+ * children: Array of child items,
+ * expanded: bool,
+ * }
+ *
+ * Here is how we could render that data with this component:
+ *
+ * class MyTree extends Component {
+ * static get propTypes() {
+ * // The root item of the tree, with the form described above.
+ * return {
+ * root: PropTypes.object.isRequired
+ * };
+ * },
+ *
+ * render() {
+ * return Tree({
+ * getRoots: () => [this.props.root],
+ *
+ * getParent: item => item.parent,
+ * getChildren: item => item.children,
+ * getKey: item => item.id,
+ * isExpanded: item => item.expanded,
+ *
+ * renderItem: (item, depth, isFocused, arrow, isExpanded) => {
+ * let className = "my-tree-item";
+ * if (isFocused) {
+ * className += " focused";
+ * }
+ * return dom.div({
+ * className,
+ * },
+ * arrow,
+ * // And here is the label for this item.
+ * dom.span({ className: "my-tree-item-label" }, item.label)
+ * );
+ * },
+ *
+ * onExpand: item => dispatchExpandActionToRedux(item),
+ * onCollapse: item => dispatchCollapseActionToRedux(item),
+ * });
+ * }
+ * }
+ */
+class Tree extends Component {
+ static get propTypes() {
+ return {
+ // Required props
+
+ // A function to get an item's parent, or null if it is a root.
+ //
+ // Type: getParent(item: Item) -> Maybe<Item>
+ //
+ // Example:
+ //
+ // // The parent of this item is stored in its `parent` property.
+ // getParent: item => item.parent
+ getParent: PropTypes.func.isRequired,
+
+ // A function to get an item's children.
+ //
+ // Type: getChildren(item: Item) -> [Item]
+ //
+ // Example:
+ //
+ // // This item's children are stored in its `children` property.
+ // getChildren: item => item.children
+ getChildren: PropTypes.func.isRequired,
+
+ // A function to check if the tree node for the item should be updated.
+ //
+ // Type: shouldItemUpdate(prevItem: Item, nextItem: Item) -> Boolean
+ //
+ // Example:
+ //
+ // // This item should be updated if it's type is a long string
+ // shouldItemUpdate: (prevItem, nextItem) =>
+ // nextItem.type === "longstring"
+ shouldItemUpdate: PropTypes.func,
+
+ // A function which takes an item and ArrowExpander component instance and
+ // returns a component, or text, or anything else that React considers
+ // renderable.
+ //
+ // Type: renderItem(item: Item,
+ // depth: Number,
+ // isFocused: Boolean,
+ // arrow: ReactComponent,
+ // isExpanded: Boolean) -> ReactRenderable
+ //
+ // Example:
+ //
+ // renderItem: (item, depth, isFocused, arrow, isExpanded) => {
+ // let className = "my-tree-item";
+ // if (isFocused) {
+ // className += " focused";
+ // }
+ // return dom.div(
+ // {
+ // className,
+ // style: { marginLeft: depth * 10 + "px" }
+ // },
+ // arrow,
+ // dom.span({ className: "my-tree-item-label" }, item.label)
+ // );
+ // },
+ renderItem: PropTypes.func.isRequired,
+
+ // A function which returns the roots of the tree (forest).
+ //
+ // Type: getRoots() -> [Item]
+ //
+ // Example:
+ //
+ // // In this case, we only have one top level, root item. You could
+ // // return multiple items if you have many top level items in your
+ // // tree.
+ // getRoots: () => [this.props.rootOfMyTree]
+ getRoots: PropTypes.func.isRequired,
+
+ // A function to get a unique key for the given item. This helps speed up
+ // React's rendering a *TON*.
+ //
+ // Type: getKey(item: Item) -> String
+ //
+ // Example:
+ //
+ // getKey: item => `my-tree-item-${item.uniqueId}`
+ getKey: PropTypes.func.isRequired,
+
+ // A function to get whether an item is expanded or not. If an item is not
+ // expanded, then it must be collapsed.
+ //
+ // Type: isExpanded(item: Item) -> Boolean
+ //
+ // Example:
+ //
+ // isExpanded: item => item.expanded,
+ isExpanded: PropTypes.func.isRequired,
+
+ // Optional props
+
+ // The currently focused item, if any such item exists.
+ focused: PropTypes.any,
+
+ // Handle when a new item is focused.
+ onFocus: PropTypes.func,
+
+ // The depth to which we should automatically expand new items.
+ autoExpandDepth: PropTypes.number,
+ // Should auto expand all new items or just the new items under the first
+ // root item.
+ autoExpandAll: PropTypes.bool,
+
+ // Auto expand a node only if number of its children
+ // are less than autoExpandNodeChildrenLimit
+ autoExpandNodeChildrenLimit: PropTypes.number,
+
+ // Note: the two properties below are mutually exclusive. Only one of the
+ // label properties is necessary.
+ // ID of an element whose textual content serves as an accessible label
+ // for a tree.
+ labelledby: PropTypes.string,
+ // Accessibility label for a tree widget.
+ label: PropTypes.string,
+
+ // Optional event handlers for when items are expanded or collapsed.
+ // Useful for dispatching redux events and updating application state,
+ // maybe lazily loading subtrees from a worker, etc.
+ //
+ // Type:
+ // onExpand(item: Item)
+ // onCollapse(item: Item)
+ //
+ // Example:
+ //
+ // onExpand: item => dispatchExpandActionToRedux(item)
+ onExpand: PropTypes.func,
+ onCollapse: PropTypes.func,
+ // The currently active (keyboard) item, if any such item exists.
+ active: PropTypes.any,
+ // Optional event handler called with the current focused node when the
+ // Enter key is pressed. Can be useful to allow further keyboard actions
+ // within the tree node.
+ onActivate: PropTypes.func,
+ isExpandable: PropTypes.func,
+ // Additional classes to add to the root element.
+ className: PropTypes.string,
+ // style object to be applied to the root element.
+ style: PropTypes.object,
+ // Prevents blur when Tree loses focus
+ preventBlur: PropTypes.bool,
+ initiallyExpanded: PropTypes.func,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ autoExpandDepth: AUTO_EXPAND_DEPTH,
+ autoExpandAll: true,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ autoExpanded: new Set(),
+ };
+
+ this.treeRef = React.createRef();
+
+ const opaf = fn =>
+ oncePerAnimationFrame(fn, {
+ getDocument: () =>
+ this.treeRef.current && this.treeRef.current.ownerDocument,
+ });
+
+ this._onExpand = opaf(this._onExpand).bind(this);
+ this._onCollapse = opaf(this._onCollapse).bind(this);
+ this._focusPrevNode = opaf(this._focusPrevNode).bind(this);
+ this._focusNextNode = opaf(this._focusNextNode).bind(this);
+ this._focusParentNode = opaf(this._focusParentNode).bind(this);
+ this._focusFirstNode = opaf(this._focusFirstNode).bind(this);
+ this._focusLastNode = opaf(this._focusLastNode).bind(this);
+
+ this._autoExpand = this._autoExpand.bind(this);
+ this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
+ this._preventEvent = this._preventEvent.bind(this);
+ this._dfs = this._dfs.bind(this);
+ this._dfsFromRoots = this._dfsFromRoots.bind(this);
+ this._focus = this._focus.bind(this);
+ this._activate = this._activate.bind(this);
+ this._scrollNodeIntoView = this._scrollNodeIntoView.bind(this);
+ this._onBlur = this._onBlur.bind(this);
+ this._onKeyDown = this._onKeyDown.bind(this);
+ this._nodeIsExpandable = this._nodeIsExpandable.bind(this);
+ }
+
+ componentDidMount() {
+ this._autoExpand();
+ if (this.props.focused) {
+ this._scrollNodeIntoView(this.props.focused);
+ }
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ this._autoExpand();
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ if (this.props.focused && prevProps.focused !== this.props.focused) {
+ this._scrollNodeIntoView(this.props.focused);
+ }
+ }
+
+ _autoExpand() {
+ const { autoExpandDepth, autoExpandNodeChildrenLimit, initiallyExpanded } =
+ this.props;
+
+ if (!autoExpandDepth && !initiallyExpanded) {
+ return;
+ }
+
+ // Automatically expand the first autoExpandDepth levels for new items. Do
+ // not use the usual DFS infrastructure because we don't want to ignore
+ // collapsed nodes. Any initially expanded items will be expanded regardless
+ // of how deep they are.
+ const autoExpand = (item, currentDepth) => {
+ const initial = initiallyExpanded && initiallyExpanded(item);
+
+ if (!initial && currentDepth >= autoExpandDepth) {
+ return;
+ }
+
+ const children = this.props.getChildren(item);
+ if (
+ !initial &&
+ autoExpandNodeChildrenLimit &&
+ children.length > autoExpandNodeChildrenLimit
+ ) {
+ return;
+ }
+
+ if (!this.state.autoExpanded.has(item)) {
+ this.props.onExpand(item);
+ this.state.autoExpanded.add(item);
+ }
+
+ const length = children.length;
+ for (let i = 0; i < length; i++) {
+ autoExpand(children[i], currentDepth + 1);
+ }
+ };
+
+ const roots = this.props.getRoots();
+ const length = roots.length;
+ if (this.props.autoExpandAll) {
+ for (let i = 0; i < length; i++) {
+ autoExpand(roots[i], 0);
+ }
+ } else if (length != 0) {
+ autoExpand(roots[0], 0);
+
+ if (initiallyExpanded) {
+ for (let i = 1; i < length; i++) {
+ if (initiallyExpanded(roots[i])) {
+ autoExpand(roots[i], 0);
+ }
+ }
+ }
+ }
+ }
+
+ _preventArrowKeyScrolling(e) {
+ switch (e.key) {
+ case "ArrowUp":
+ case "ArrowDown":
+ case "ArrowLeft":
+ case "ArrowRight":
+ this._preventEvent(e);
+ break;
+ }
+ }
+
+ _preventEvent(e) {
+ e.preventDefault();
+ e.stopPropagation();
+ if (e.nativeEvent) {
+ if (e.nativeEvent.preventDefault) {
+ e.nativeEvent.preventDefault();
+ }
+ if (e.nativeEvent.stopPropagation) {
+ e.nativeEvent.stopPropagation();
+ }
+ }
+ }
+
+ /**
+ * Perform a pre-order depth-first search from item.
+ */
+ _dfs(item, maxDepth = Infinity, traversal = [], _depth = 0) {
+ traversal.push({ item, depth: _depth });
+
+ if (!this.props.isExpanded(item)) {
+ return traversal;
+ }
+
+ const nextDepth = _depth + 1;
+
+ if (nextDepth > maxDepth) {
+ return traversal;
+ }
+
+ const children = this.props.getChildren(item);
+ const length = children.length;
+ for (let i = 0; i < length; i++) {
+ this._dfs(children[i], maxDepth, traversal, nextDepth);
+ }
+
+ return traversal;
+ }
+
+ /**
+ * Perform a pre-order depth-first search over the whole forest.
+ */
+ _dfsFromRoots(maxDepth = Infinity) {
+ const traversal = [];
+
+ const roots = this.props.getRoots();
+ const length = roots.length;
+ for (let i = 0; i < length; i++) {
+ this._dfs(roots[i], maxDepth, traversal);
+ }
+
+ return traversal;
+ }
+
+ /**
+ * Expands current row.
+ *
+ * @param {Object} item
+ * @param {Boolean} expandAllChildren
+ */
+ _onExpand(item, expandAllChildren) {
+ if (this.props.onExpand) {
+ this.props.onExpand(item);
+
+ if (expandAllChildren) {
+ const children = this._dfs(item);
+ const length = children.length;
+ for (let i = 0; i < length; i++) {
+ this.props.onExpand(children[i].item);
+ }
+ }
+ }
+ }
+
+ /**
+ * Collapses current row.
+ *
+ * @param {Object} item
+ */
+ _onCollapse(item) {
+ if (this.props.onCollapse) {
+ this.props.onCollapse(item);
+ }
+ }
+
+ /**
+ * Sets the passed in item to be the focused item.
+ *
+ * @param {Object|undefined} item
+ * The item to be focused, or undefined to focus no item.
+ *
+ * @param {Object|undefined} options
+ * An options object which can contain:
+ * - dir: "up" or "down" to indicate if we should scroll the element
+ * to the top or the bottom of the scrollable container when
+ * the element is off canvas.
+ */
+ _focus(item, options = {}) {
+ const { preventAutoScroll } = options;
+ if (item && !preventAutoScroll) {
+ this._scrollNodeIntoView(item, options);
+ }
+
+ if (this.props.active != undefined) {
+ this._activate(undefined);
+ const doc = this.treeRef.current && this.treeRef.current.ownerDocument;
+ if (this.treeRef.current !== doc.activeElement) {
+ this.treeRef.current.focus();
+ }
+ }
+
+ if (this.props.onFocus) {
+ this.props.onFocus(item);
+ }
+ }
+
+ /**
+ * Sets the passed in item to be the active item.
+ *
+ * @param {Object|undefined} item
+ * The item to be activated, or undefined to activate no item.
+ */
+ _activate(item) {
+ if (this.props.onActivate) {
+ this.props.onActivate(item);
+ }
+ }
+
+ /**
+ * Sets the passed in item to be the focused item.
+ *
+ * @param {Object|undefined} item
+ * The item to be scrolled to.
+ *
+ * @param {Object|undefined} options
+ * An options object which can contain:
+ * - dir: "up" or "down" to indicate if we should scroll the element
+ * to the top or the bottom of the scrollable container when
+ * the element is off canvas.
+ */
+ _scrollNodeIntoView(item, options = {}) {
+ if (item !== undefined) {
+ const treeElement = this.treeRef.current;
+ const doc = treeElement && treeElement.ownerDocument;
+ const element = doc.getElementById(this.props.getKey(item));
+
+ if (element) {
+ const { top, bottom } = element.getBoundingClientRect();
+ const closestScrolledParent = node => {
+ if (node == null) {
+ return null;
+ }
+
+ if (node.scrollHeight > node.clientHeight) {
+ return node;
+ }
+ return closestScrolledParent(node.parentNode);
+ };
+ const scrolledParent = closestScrolledParent(treeElement);
+ const scrolledParentRect = scrolledParent
+ ? scrolledParent.getBoundingClientRect()
+ : null;
+ const isVisible =
+ !scrolledParent ||
+ (top >= scrolledParentRect.top &&
+ bottom <= scrolledParentRect.bottom);
+
+ if (!isVisible) {
+ const { alignTo } = options;
+ const scrollToTop = alignTo
+ ? alignTo === "top"
+ : !scrolledParentRect || top < scrolledParentRect.top;
+ element.scrollIntoView(scrollToTop);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the state to have no focused item.
+ */
+ _onBlur(e) {
+ if (this.props.active != undefined) {
+ const { relatedTarget } = e;
+ if (!this.treeRef.current.contains(relatedTarget)) {
+ this._activate(undefined);
+ }
+ } else if (!this.props.preventBlur) {
+ this._focus(undefined);
+ }
+ }
+
+ /**
+ * Handles key down events in the tree's container.
+ *
+ * @param {Event} e
+ */
+ // eslint-disable-next-line complexity
+ _onKeyDown(e) {
+ if (this.props.focused == null) {
+ return;
+ }
+
+ // Allow parent nodes to use navigation arrows with modifiers.
+ if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+ return;
+ }
+
+ this._preventArrowKeyScrolling(e);
+ const doc = this.treeRef.current && this.treeRef.current.ownerDocument;
+
+ switch (e.key) {
+ case "ArrowUp":
+ this._focusPrevNode();
+ return;
+
+ case "ArrowDown":
+ this._focusNextNode();
+ return;
+
+ case "ArrowLeft":
+ if (
+ this.props.isExpanded(this.props.focused) &&
+ this._nodeIsExpandable(this.props.focused)
+ ) {
+ this._onCollapse(this.props.focused);
+ } else {
+ this._focusParentNode();
+ }
+ return;
+
+ case "ArrowRight":
+ if (
+ this._nodeIsExpandable(this.props.focused) &&
+ !this.props.isExpanded(this.props.focused)
+ ) {
+ this._onExpand(this.props.focused);
+ } else {
+ this._focusNextNode();
+ }
+ return;
+
+ case "Home":
+ this._focusFirstNode();
+ return;
+
+ case "End":
+ this._focusLastNode();
+ return;
+
+ case "Enter":
+ case " ":
+ if (this.treeRef.current === doc.activeElement) {
+ this._preventEvent(e);
+ if (this.props.active !== this.props.focused) {
+ this._activate(this.props.focused);
+ }
+ }
+ return;
+
+ case "Escape":
+ this._preventEvent(e);
+ if (this.props.active != undefined) {
+ this._activate(undefined);
+ }
+
+ if (this.treeRef.current !== doc.activeElement) {
+ this.treeRef.current.focus();
+ }
+ }
+ }
+
+ /**
+ * Sets the previous node relative to the currently focused item, to focused.
+ */
+ _focusPrevNode() {
+ // Start a depth first search and keep going until we reach the currently
+ // focused node. Focus the previous node in the DFS, if it exists. If it
+ // doesn't exist, we're at the first node already.
+
+ let prev;
+
+ const traversal = this._dfsFromRoots();
+ const length = traversal.length;
+ for (let i = 0; i < length; i++) {
+ const item = traversal[i].item;
+ if (item === this.props.focused) {
+ break;
+ }
+ prev = item;
+ }
+ if (prev === undefined) {
+ return;
+ }
+
+ this._focus(prev, { alignTo: "top" });
+ }
+
+ /**
+ * Handles the down arrow key which will focus either the next child
+ * or sibling row.
+ */
+ _focusNextNode() {
+ // Start a depth first search and keep going until we reach the currently
+ // focused node. Focus the next node in the DFS, if it exists. If it
+ // doesn't exist, we're at the last node already.
+ const traversal = this._dfsFromRoots();
+ const length = traversal.length;
+ let i = 0;
+
+ while (i < length) {
+ if (traversal[i].item === this.props.focused) {
+ break;
+ }
+ i++;
+ }
+
+ if (i + 1 < traversal.length) {
+ this._focus(traversal[i + 1].item, { alignTo: "bottom" });
+ }
+ }
+
+ /**
+ * Handles the left arrow key, going back up to the current rows'
+ * parent row.
+ */
+ _focusParentNode() {
+ const parent = this.props.getParent(this.props.focused);
+ if (!parent) {
+ this._focusPrevNode(this.props.focused);
+ return;
+ }
+
+ this._focus(parent, { alignTo: "top" });
+ }
+
+ _focusFirstNode() {
+ const traversal = this._dfsFromRoots();
+ this._focus(traversal[0].item, { alignTo: "top" });
+ }
+
+ _focusLastNode() {
+ const traversal = this._dfsFromRoots();
+ const lastIndex = traversal.length - 1;
+ this._focus(traversal[lastIndex].item, { alignTo: "bottom" });
+ }
+
+ _nodeIsExpandable(item) {
+ return this.props.isExpandable
+ ? this.props.isExpandable(item)
+ : !!this.props.getChildren(item).length;
+ }
+
+ render() {
+ const traversal = this._dfsFromRoots();
+ const { active, focused } = this.props;
+
+ const nodes = traversal.map((v, i) => {
+ const { item, depth } = traversal[i];
+ const key = this.props.getKey(item, i);
+ const focusedKey = focused ? this.props.getKey(focused, i) : null;
+ return TreeNodeFactory({
+ // We make a key unique depending on whether the tree node is in active
+ // or inactive state to make sure that it is actually replaced and the
+ // tabbable state is reset.
+ key: `${key}-${active === item ? "active" : "inactive"}`,
+ id: key,
+ index: i,
+ item,
+ depth,
+ shouldItemUpdate: this.props.shouldItemUpdate,
+ renderItem: this.props.renderItem,
+ focused: focusedKey === key,
+ active: active === item,
+ expanded: this.props.isExpanded(item),
+ isExpandable: this._nodeIsExpandable(item),
+ onExpand: this._onExpand,
+ onCollapse: this._onCollapse,
+ onClick: e => {
+ // We can stop the propagation since click handler on the node can be
+ // created in `renderItem`.
+ e.stopPropagation();
+
+ // Since the user just clicked the node, there's no need to check if
+ // it should be scrolled into view.
+ this._focus(item, { preventAutoScroll: true });
+ if (this.props.isExpanded(item)) {
+ this.props.onCollapse(item, e.altKey);
+ } else {
+ this.props.onExpand(item, e.altKey);
+ }
+
+ // Focus should always remain on the tree container itself.
+ this.treeRef.current.focus();
+ },
+ });
+ });
+
+ const style = Object.assign({}, this.props.style || {});
+
+ return dom.div(
+ {
+ className: `tree ${this.props.className ? this.props.className : ""}`,
+ ref: this.treeRef,
+ role: "tree",
+ tabIndex: "0",
+ onKeyDown: this._onKeyDown,
+ onKeyPress: this._preventArrowKeyScrolling,
+ onKeyUp: this._preventArrowKeyScrolling,
+ onFocus: ({ nativeEvent }) => {
+ if (focused || !nativeEvent || !this.treeRef.current) {
+ return;
+ }
+
+ const { explicitOriginalTarget } = nativeEvent;
+ // Only set default focus to the first tree node if the focus came
+ // from outside the tree (e.g. by tabbing to the tree from other
+ // external elements).
+ if (
+ explicitOriginalTarget !== this.treeRef.current &&
+ !this.treeRef.current.contains(explicitOriginalTarget)
+ ) {
+ this._focus(traversal[0].item);
+ }
+ },
+ onBlur: this._onBlur,
+ "aria-label": this.props.label,
+ "aria-labelledby": this.props.labelledby,
+ "aria-activedescendant": focused && this.props.getKey(focused),
+ style,
+ },
+ nodes
+ );
+ }
+}
+
+module.exports = Tree;
diff --git a/devtools/client/shared/components/VirtualizedTree.js b/devtools/client/shared/components/VirtualizedTree.js
new file mode 100644
index 0000000000..4f8dab1bd5
--- /dev/null
+++ b/devtools/client/shared/components/VirtualizedTree.js
@@ -0,0 +1,1071 @@
+/* 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/. */
+/* eslint-env browser */
+"use strict";
+
+const {
+ Component,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const {
+ scrollIntoView,
+} = require("resource://devtools/client/shared/scroll.js");
+const {
+ preventDefaultAndStopPropagation,
+} = require("resource://devtools/client/shared/events.js");
+
+loader.lazyRequireGetter(
+ this,
+ ["wrapMoveFocus", "getFocusableElements"],
+ "resource://devtools/client/shared/focus.js",
+ true
+);
+
+const AUTO_EXPAND_DEPTH = 0;
+const NUMBER_OF_OFFSCREEN_ITEMS = 1;
+
+/**
+ * A fast, generic, expandable and collapsible tree component.
+ *
+ * This tree component is fast: it can handle trees with *many* items. It only
+ * renders the subset of those items which are visible in the viewport. It's
+ * been battle tested on huge trees in the memory panel. We've optimized tree
+ * traversal and rendering, even in the presence of cross-compartment wrappers.
+ *
+ * This tree component doesn't make any assumptions about the structure of your
+ * tree data. Whether children are computed on demand, or stored in an array in
+ * the parent's `_children` property, it doesn't matter. We only require the
+ * implementation of `getChildren`, `getRoots`, `getParent`, and `isExpanded`
+ * functions.
+ *
+ * This tree component is well tested and reliable. See
+ * devtools/client/shared/components/test/mochitest/test_tree_* and its usage in
+ * the performance and memory panels.
+ *
+ * This tree component doesn't make any assumptions about how to render items in
+ * the tree. You provide a `renderItem` function, and this component will ensure
+ * that only those items whose parents are expanded and which are visible in the
+ * viewport are rendered. The `renderItem` function could render the items as a
+ * "traditional" tree or as rows in a table or anything else. It doesn't
+ * restrict you to only one certain kind of tree.
+ *
+ * The only requirement is that every item in the tree render as the same
+ * height. This is required in order to compute which items are visible in the
+ * viewport in constant time.
+ *
+ * ### Example Usage
+ *
+ * Suppose we have some tree data where each item has this form:
+ *
+ * {
+ * id: Number,
+ * label: String,
+ * parent: Item or null,
+ * children: Array of child items,
+ * expanded: bool,
+ * }
+ *
+ * Here is how we could render that data with this component:
+ *
+ * class MyTree extends Component {
+ * static get propTypes() {
+ * // The root item of the tree, with the form described above.
+ * return {
+ * root: PropTypes.object.isRequired
+ * };
+ * }
+ *
+ * render() {
+ * return Tree({
+ * itemHeight: 20, // px
+ *
+ * getRoots: () => [this.props.root],
+ *
+ * getParent: item => item.parent,
+ * getChildren: item => item.children,
+ * getKey: item => item.id,
+ * isExpanded: item => item.expanded,
+ *
+ * renderItem: (item, depth, isFocused, arrow, isExpanded) => {
+ * let className = "my-tree-item";
+ * if (isFocused) {
+ * className += " focused";
+ * }
+ * return dom.div(
+ * {
+ * className,
+ * // Apply 10px nesting per expansion depth.
+ * style: { marginLeft: depth * 10 + "px" }
+ * },
+ * // Here is the expando arrow so users can toggle expansion and
+ * // collapse state.
+ * arrow,
+ * // And here is the label for this item.
+ * dom.span({ className: "my-tree-item-label" }, item.label)
+ * );
+ * },
+ *
+ * onExpand: item => dispatchExpandActionToRedux(item),
+ * onCollapse: item => dispatchCollapseActionToRedux(item),
+ * });
+ * }
+ * }
+ */
+class Tree extends Component {
+ static get propTypes() {
+ return {
+ // Required props
+
+ // A function to get an item's parent, or null if it is a root.
+ //
+ // Type: getParent(item: Item) -> Maybe<Item>
+ //
+ // Example:
+ //
+ // // The parent of this item is stored in its `parent` property.
+ // getParent: item => item.parent
+ getParent: PropTypes.func.isRequired,
+
+ // A function to get an item's children.
+ //
+ // Type: getChildren(item: Item) -> [Item]
+ //
+ // Example:
+ //
+ // // This item's children are stored in its `children` property.
+ // getChildren: item => item.children
+ getChildren: PropTypes.func.isRequired,
+
+ // A function which takes an item and ArrowExpander component instance and
+ // returns a component, or text, or anything else that React considers
+ // renderable.
+ //
+ // Type: renderItem(item: Item,
+ // depth: Number,
+ // isFocused: Boolean,
+ // arrow: ReactComponent,
+ // isExpanded: Boolean) -> ReactRenderable
+ //
+ // Example:
+ //
+ // renderItem: (item, depth, isFocused, arrow, isExpanded) => {
+ // let className = "my-tree-item";
+ // if (isFocused) {
+ // className += " focused";
+ // }
+ // return dom.div(
+ // {
+ // className,
+ // style: { marginLeft: depth * 10 + "px" }
+ // },
+ // arrow,
+ // dom.span({ className: "my-tree-item-label" }, item.label)
+ // );
+ // },
+ renderItem: PropTypes.func.isRequired,
+
+ // A function which returns the roots of the tree (forest).
+ //
+ // Type: getRoots() -> [Item]
+ //
+ // Example:
+ //
+ // // In this case, we only have one top level, root item. You could
+ // // return multiple items if you have many top level items in your
+ // // tree.
+ // getRoots: () => [this.props.rootOfMyTree]
+ getRoots: PropTypes.func.isRequired,
+
+ // A function to get a unique key for the given item. This helps speed up
+ // React's rendering a *TON*.
+ //
+ // Type: getKey(item: Item) -> String
+ //
+ // Example:
+ //
+ // getKey: item => `my-tree-item-${item.uniqueId}`
+ getKey: PropTypes.func.isRequired,
+
+ // A function to get whether an item is expanded or not. If an item is not
+ // expanded, then it must be collapsed.
+ //
+ // Type: isExpanded(item: Item) -> Boolean
+ //
+ // Example:
+ //
+ // isExpanded: item => item.expanded,
+ isExpanded: PropTypes.func.isRequired,
+
+ // The height of an item in the tree including margin and padding, in
+ // pixels.
+ itemHeight: PropTypes.number.isRequired,
+
+ // Optional props
+
+ // The currently focused item, if any such item exists.
+ focused: PropTypes.any,
+
+ // Handle when a new item is focused.
+ onFocus: PropTypes.func,
+
+ // The currently active (keyboard) item, if any such item exists.
+ active: PropTypes.any,
+
+ // Handle when item is activated with a keyboard (using Space or Enter)
+ onActivate: PropTypes.func,
+
+ // The currently shown item, if any such item exists.
+ shown: PropTypes.any,
+
+ // Indicates if pressing ArrowRight key should only expand expandable node
+ // or if the selection should also move to the next node.
+ preventNavigationOnArrowRight: PropTypes.bool,
+
+ // The depth to which we should automatically expand new items.
+ autoExpandDepth: PropTypes.number,
+
+ // Note: the two properties below are mutually exclusive. Only one of the
+ // label properties is necessary.
+ // ID of an element whose textual content serves as an accessible label for
+ // a tree.
+ labelledby: PropTypes.string,
+ // Accessibility label for a tree widget.
+ label: PropTypes.string,
+
+ // Optional event handlers for when items are expanded or collapsed. Useful
+ // for dispatching redux events and updating application state, maybe lazily
+ // loading subtrees from a worker, etc.
+ //
+ // Type:
+ // onExpand(item: Item)
+ // onCollapse(item: Item)
+ //
+ // Example:
+ //
+ // onExpand: item => dispatchExpandActionToRedux(item)
+ onExpand: PropTypes.func,
+ onCollapse: PropTypes.func,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ autoExpandDepth: AUTO_EXPAND_DEPTH,
+ preventNavigationOnArrowRight: true,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ scroll: 0,
+ height: window.innerHeight,
+ seen: new Set(),
+ mouseDown: false,
+ };
+
+ this._onExpand = oncePerAnimationFrame(this._onExpand).bind(this);
+ this._onCollapse = oncePerAnimationFrame(this._onCollapse).bind(this);
+ this._onScroll = oncePerAnimationFrame(this._onScroll).bind(this);
+ this._focusPrevNode = oncePerAnimationFrame(this._focusPrevNode).bind(this);
+ this._focusNextNode = oncePerAnimationFrame(this._focusNextNode).bind(this);
+ this._focusParentNode = oncePerAnimationFrame(this._focusParentNode).bind(
+ this
+ );
+ this._focusFirstNode = oncePerAnimationFrame(this._focusFirstNode).bind(
+ this
+ );
+ this._focusLastNode = oncePerAnimationFrame(this._focusLastNode).bind(this);
+
+ this._autoExpand = this._autoExpand.bind(this);
+ this._preventArrowKeyScrolling = this._preventArrowKeyScrolling.bind(this);
+ this._updateHeight = this._updateHeight.bind(this);
+ this._onResize = this._onResize.bind(this);
+ this._dfs = this._dfs.bind(this);
+ this._dfsFromRoots = this._dfsFromRoots.bind(this);
+ this._focus = this._focus.bind(this);
+ this._activate = this._activate.bind(this);
+ this._onKeyDown = this._onKeyDown.bind(this);
+ }
+
+ componentDidMount() {
+ window.addEventListener("resize", this._onResize);
+ this._autoExpand();
+ this._updateHeight();
+ this._scrollItemIntoView();
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ this._autoExpand();
+ this._updateHeight();
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ const { scroll, height, seen, mouseDown } = this.state;
+
+ return (
+ scroll !== nextState.scroll ||
+ height !== nextState.height ||
+ seen !== nextState.seen ||
+ mouseDown === nextState.mouseDown
+ );
+ }
+
+ componentDidUpdate() {
+ this._scrollItemIntoView();
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener("resize", this._onResize);
+ }
+
+ _scrollItemIntoView() {
+ const { shown } = this.props;
+ if (!shown) {
+ return;
+ }
+
+ this._scrollIntoView(shown);
+ }
+
+ _autoExpand() {
+ if (!this.props.autoExpandDepth) {
+ return;
+ }
+
+ // Automatically expand the first autoExpandDepth levels for new items. Do
+ // not use the usual DFS infrastructure because we don't want to ignore
+ // collapsed nodes.
+ const autoExpand = (item, currentDepth) => {
+ if (
+ currentDepth >= this.props.autoExpandDepth ||
+ this.state.seen.has(item)
+ ) {
+ return;
+ }
+
+ this.props.onExpand(item);
+ this.state.seen.add(item);
+
+ const children = this.props.getChildren(item);
+ const length = children.length;
+ for (let i = 0; i < length; i++) {
+ autoExpand(children[i], currentDepth + 1);
+ }
+ };
+
+ const roots = this.props.getRoots();
+ const length = roots.length;
+ for (let i = 0; i < length; i++) {
+ autoExpand(roots[i], 0);
+ }
+ }
+
+ _preventArrowKeyScrolling(e) {
+ switch (e.key) {
+ case "ArrowUp":
+ case "ArrowDown":
+ case "ArrowLeft":
+ case "ArrowRight":
+ preventDefaultAndStopPropagation(e);
+ break;
+ }
+ }
+
+ /**
+ * Updates the state's height based on clientHeight.
+ */
+ _updateHeight() {
+ this.setState({ height: this.refs.tree.clientHeight });
+ }
+
+ /**
+ * Perform a pre-order depth-first search from item.
+ */
+ _dfs(item, maxDepth = Infinity, traversal = [], _depth = 0) {
+ traversal.push({ item, depth: _depth });
+
+ if (!this.props.isExpanded(item)) {
+ return traversal;
+ }
+
+ const nextDepth = _depth + 1;
+
+ if (nextDepth > maxDepth) {
+ return traversal;
+ }
+
+ const children = this.props.getChildren(item);
+ const length = children.length;
+ for (let i = 0; i < length; i++) {
+ this._dfs(children[i], maxDepth, traversal, nextDepth);
+ }
+
+ return traversal;
+ }
+
+ /**
+ * Perform a pre-order depth-first search over the whole forest.
+ */
+ _dfsFromRoots(maxDepth = Infinity) {
+ const traversal = [];
+
+ const roots = this.props.getRoots();
+ const length = roots.length;
+ for (let i = 0; i < length; i++) {
+ this._dfs(roots[i], maxDepth, traversal);
+ }
+
+ return traversal;
+ }
+
+ /**
+ * Expands current row.
+ *
+ * @param {Object} item
+ * @param {Boolean} expandAllChildren
+ */
+ _onExpand(item, expandAllChildren) {
+ if (this.props.onExpand) {
+ this.props.onExpand(item);
+
+ if (expandAllChildren) {
+ const children = this._dfs(item);
+ const length = children.length;
+ for (let i = 0; i < length; i++) {
+ this.props.onExpand(children[i].item);
+ }
+ }
+ }
+ }
+
+ /**
+ * Collapses current row.
+ *
+ * @param {Object} item
+ */
+ _onCollapse(item) {
+ if (this.props.onCollapse) {
+ this.props.onCollapse(item);
+ }
+ }
+
+ /**
+ * Scroll item into view. Depending on whether the item is already rendered,
+ * we might have to calculate the position of the item based on its index and
+ * the item height.
+ *
+ * @param {Object} item
+ * The item to be scrolled into view.
+ * @param {Number|undefined} index
+ * The index of the item in a full DFS traversal (ignoring collapsed
+ * nodes) or undefined.
+ * @param {Object} options
+ * Optional information regarding item's requested alignement when
+ * scrolling.
+ */
+ _scrollIntoView(item, index, options = {}) {
+ const treeElement = this.refs.tree;
+ if (!treeElement) {
+ return;
+ }
+
+ const element = document.getElementById(this.props.getKey(item));
+ if (element) {
+ scrollIntoView(element, { ...options, container: treeElement });
+ return;
+ }
+
+ if (index == null) {
+ // If index is not provided, determine item index from traversal.
+ const traversal = this._dfsFromRoots();
+ index = traversal.findIndex(({ item: i }) => i === item);
+ }
+
+ if (index == null || index < 0) {
+ return;
+ }
+
+ const { itemHeight } = this.props;
+ const { clientHeight, scrollTop } = treeElement;
+ const elementTop = index * itemHeight;
+ let scrollTo;
+ if (scrollTop >= elementTop + itemHeight) {
+ scrollTo = elementTop;
+ } else if (scrollTop + clientHeight <= elementTop) {
+ scrollTo = elementTop + itemHeight - clientHeight;
+ }
+
+ if (scrollTo != undefined) {
+ treeElement.scrollTo({
+ left: 0,
+ top: scrollTo,
+ });
+ }
+ }
+
+ /**
+ * Sets the passed in item to be the focused item.
+ *
+ * @param {Number} index
+ * The index of the item in a full DFS traversal (ignoring collapsed
+ * nodes). Ignored if `item` is undefined.
+ *
+ * @param {Object|undefined} item
+ * The item to be focused, or undefined to focus no item.
+ */
+ _focus(index, item, options = {}) {
+ if (item !== undefined && !options.preventAutoScroll) {
+ this._scrollIntoView(item, index, options);
+ }
+
+ if (this.props.active != null) {
+ this._activate(null);
+ if (this.refs.tree !== this.activeElement) {
+ this.refs.tree.focus();
+ }
+ }
+
+ if (this.props.onFocus) {
+ this.props.onFocus(item);
+ }
+ }
+
+ _activate(item) {
+ if (this.props.onActivate) {
+ this.props.onActivate(item);
+ }
+ }
+
+ /**
+ * Update state height and tree's scrollTop if necessary.
+ */
+ _onResize() {
+ // When tree size changes without direct user action, scroll top cat get re-set to 0
+ // (for example, when tree height changes via CSS rule change). We need to ensure that
+ // the tree's scrollTop is in sync with the scroll state.
+ if (this.state.scroll !== this.refs.tree.scrollTop) {
+ this.refs.tree.scrollTo({ left: 0, top: this.state.scroll });
+ }
+
+ this._updateHeight();
+ }
+
+ /**
+ * Fired on a scroll within the tree's container, updates
+ * the stored position of the view port to handle virtual view rendering.
+ *
+ * @param {Event} e
+ */
+ _onScroll(e) {
+ this.setState({
+ scroll: Math.max(this.refs.tree.scrollTop, 0),
+ height: this.refs.tree.clientHeight,
+ });
+ }
+
+ /**
+ * Handles key down events in the tree's container.
+ *
+ * @param {Event} e
+ */
+ // eslint-disable-next-line complexity
+ _onKeyDown(e) {
+ if (this.props.focused == null) {
+ return;
+ }
+
+ // Allow parent nodes to use navigation arrows with modifiers.
+ if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) {
+ return;
+ }
+
+ this._preventArrowKeyScrolling(e);
+
+ switch (e.key) {
+ case "ArrowUp":
+ this._focusPrevNode();
+ break;
+
+ case "ArrowDown":
+ this._focusNextNode();
+ break;
+
+ case "ArrowLeft":
+ if (
+ this.props.isExpanded(this.props.focused) &&
+ this.props.getChildren(this.props.focused).length
+ ) {
+ this._onCollapse(this.props.focused);
+ } else {
+ this._focusParentNode();
+ }
+ break;
+
+ case "ArrowRight":
+ if (
+ this.props.getChildren(this.props.focused).length &&
+ !this.props.isExpanded(this.props.focused)
+ ) {
+ this._onExpand(this.props.focused);
+ } else if (!this.props.preventNavigationOnArrowRight) {
+ this._focusNextNode();
+ }
+ break;
+
+ case "Home":
+ this._focusFirstNode();
+ break;
+
+ case "End":
+ this._focusLastNode();
+ break;
+
+ case "Enter":
+ case " ":
+ // On space or enter make focused tree node active. This means keyboard focus
+ // handling is passed on to the tree node itself.
+ if (this.refs.tree === this.activeElement) {
+ preventDefaultAndStopPropagation(e);
+ if (this.props.active !== this.props.focused) {
+ this._activate(this.props.focused);
+ }
+ }
+ break;
+
+ case "Escape":
+ preventDefaultAndStopPropagation(e);
+ if (this.props.active != null) {
+ this._activate(null);
+ }
+
+ if (this.refs.tree !== this.activeElement) {
+ this.refs.tree.focus();
+ }
+ break;
+ }
+ }
+
+ get activeElement() {
+ return this.refs.tree.ownerDocument.activeElement;
+ }
+
+ _focusFirstNode() {
+ const traversal = this._dfsFromRoots();
+ this._focus(0, traversal[0].item, { alignTo: "top" });
+ }
+
+ _focusLastNode() {
+ const traversal = this._dfsFromRoots();
+ const lastIndex = traversal.length - 1;
+ this._focus(lastIndex, traversal[lastIndex].item, { alignTo: "bottom" });
+ }
+
+ /**
+ * Sets the previous node relative to the currently focused item, to focused.
+ */
+ _focusPrevNode() {
+ // Start a depth first search and keep going until we reach the currently
+ // focused node. Focus the previous node in the DFS, if it exists. If it
+ // doesn't exist, we're at the first node already.
+
+ let prev;
+ let prevIndex;
+
+ const traversal = this._dfsFromRoots();
+ const length = traversal.length;
+ for (let i = 0; i < length; i++) {
+ const item = traversal[i].item;
+ if (item === this.props.focused) {
+ break;
+ }
+ prev = item;
+ prevIndex = i;
+ }
+
+ if (prev === undefined) {
+ return;
+ }
+
+ this._focus(prevIndex, prev, { alignTo: "top" });
+ }
+
+ /**
+ * Handles the down arrow key which will focus either the next child
+ * or sibling row.
+ */
+ _focusNextNode() {
+ // Start a depth first search and keep going until we reach the currently
+ // focused node. Focus the next node in the DFS, if it exists. If it
+ // doesn't exist, we're at the last node already.
+
+ const traversal = this._dfsFromRoots();
+ const length = traversal.length;
+ let i = 0;
+
+ while (i < length) {
+ if (traversal[i].item === this.props.focused) {
+ break;
+ }
+ i++;
+ }
+
+ if (i + 1 < traversal.length) {
+ this._focus(i + 1, traversal[i + 1].item, { alignTo: "bottom" });
+ }
+ }
+
+ /**
+ * Handles the left arrow key, going back up to the current rows'
+ * parent row.
+ */
+ _focusParentNode() {
+ const parent = this.props.getParent(this.props.focused);
+ if (!parent) {
+ return;
+ }
+
+ const traversal = this._dfsFromRoots();
+ const length = traversal.length;
+ let parentIndex = 0;
+ for (; parentIndex < length; parentIndex++) {
+ if (traversal[parentIndex].item === parent) {
+ break;
+ }
+ }
+
+ this._focus(parentIndex, parent, { alignTo: "top" });
+ }
+
+ render() {
+ const traversal = this._dfsFromRoots();
+
+ // 'begin' and 'end' are the index of the first (at least partially) visible item
+ // and the index after the last (at least partially) visible item, respectively.
+ // `NUMBER_OF_OFFSCREEN_ITEMS` is removed from `begin` and added to `end` so that
+ // the top and bottom of the page are filled with the `NUMBER_OF_OFFSCREEN_ITEMS`
+ // previous and next items respectively, which helps the user to see fewer empty
+ // gaps when scrolling quickly.
+ const { itemHeight, active, focused } = this.props;
+ const { scroll, height } = this.state;
+ const begin = Math.max(
+ ((scroll / itemHeight) | 0) - NUMBER_OF_OFFSCREEN_ITEMS,
+ 0
+ );
+ const end =
+ Math.ceil((scroll + height) / itemHeight) + NUMBER_OF_OFFSCREEN_ITEMS;
+ const toRender = traversal.slice(begin, end);
+ const topSpacerHeight = begin * itemHeight;
+ const bottomSpacerHeight = Math.max(traversal.length - end, 0) * itemHeight;
+
+ const nodes = [
+ dom.div({
+ key: "top-spacer",
+ role: "presentation",
+ style: {
+ padding: 0,
+ margin: 0,
+ height: topSpacerHeight + "px",
+ },
+ }),
+ ];
+
+ for (let i = 0; i < toRender.length; i++) {
+ const index = begin + i;
+ const first = index == 0;
+ const last = index == traversal.length - 1;
+ const { item, depth } = toRender[i];
+ const key = this.props.getKey(item);
+ nodes.push(
+ TreeNode({
+ // We make a key unique depending on whether the tree node is in active or
+ // inactive state to make sure that it is actually replaced and the tabbable
+ // state is reset.
+ key: `${key}-${active === item ? "active" : "inactive"}`,
+ index,
+ first,
+ last,
+ item,
+ depth,
+ id: key,
+ renderItem: this.props.renderItem,
+ focused: focused === item,
+ active: active === item,
+ expanded: this.props.isExpanded(item),
+ hasChildren: !!this.props.getChildren(item).length,
+ onExpand: this._onExpand,
+ onCollapse: this._onCollapse,
+ // Since the user just clicked the node, there's no need to check if
+ // it should be scrolled into view.
+ onClick: () =>
+ this._focus(begin + i, item, { preventAutoScroll: true }),
+ })
+ );
+ }
+
+ nodes.push(
+ dom.div({
+ key: "bottom-spacer",
+ role: "presentation",
+ style: {
+ padding: 0,
+ margin: 0,
+ height: bottomSpacerHeight + "px",
+ },
+ })
+ );
+
+ return dom.div(
+ {
+ className: "tree",
+ ref: "tree",
+ role: "tree",
+ tabIndex: "0",
+ onKeyDown: this._onKeyDown,
+ onKeyPress: this._preventArrowKeyScrolling,
+ onKeyUp: this._preventArrowKeyScrolling,
+ onScroll: this._onScroll,
+ onMouseDown: () => this.setState({ mouseDown: true }),
+ onMouseUp: () => this.setState({ mouseDown: false }),
+ onFocus: () => {
+ if (focused || this.state.mouseDown) {
+ return;
+ }
+
+ // Only set default focus to the first tree node if focused node is
+ // not yet set and the focus event is not the result of a mouse
+ // interarction.
+ this._focus(begin, toRender[0].item);
+ },
+ onBlur: e => {
+ if (active != null) {
+ const { relatedTarget } = e;
+ if (!this.refs.tree.contains(relatedTarget)) {
+ this._activate(null);
+ }
+ }
+ },
+ onClick: () => {
+ // Focus should always remain on the tree container itself.
+ this.refs.tree.focus();
+ },
+ "aria-label": this.props.label,
+ "aria-labelledby": this.props.labelledby,
+ "aria-activedescendant": focused && this.props.getKey(focused),
+ style: {
+ padding: 0,
+ margin: 0,
+ },
+ },
+ nodes
+ );
+ }
+}
+
+/**
+ * An arrow that displays whether its node is expanded (▼) or collapsed
+ * (▶). When its node has no children, it is hidden.
+ */
+class ArrowExpanderClass extends Component {
+ static get propTypes() {
+ return {
+ item: PropTypes.any.isRequired,
+ visible: PropTypes.bool.isRequired,
+ expanded: PropTypes.bool.isRequired,
+ onCollapse: PropTypes.func.isRequired,
+ onExpand: PropTypes.func.isRequired,
+ };
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ this.props.item !== nextProps.item ||
+ this.props.visible !== nextProps.visible ||
+ this.props.expanded !== nextProps.expanded
+ );
+ }
+
+ render() {
+ const attrs = {
+ className: "arrow theme-twisty",
+ // To collapse/expand the tree rows use left/right arrow keys.
+ tabIndex: "-1",
+ "aria-hidden": true,
+ onClick: this.props.expanded
+ ? () => this.props.onCollapse(this.props.item)
+ : e => this.props.onExpand(this.props.item, e.altKey),
+ };
+
+ if (this.props.expanded) {
+ attrs.className += " open";
+ }
+
+ if (!this.props.visible) {
+ attrs.style = {
+ visibility: "hidden",
+ };
+ }
+
+ return dom.div(attrs);
+ }
+}
+
+class TreeNodeClass extends Component {
+ static get propTypes() {
+ return {
+ id: PropTypes.any.isRequired,
+ focused: PropTypes.bool.isRequired,
+ active: PropTypes.bool.isRequired,
+ item: PropTypes.any.isRequired,
+ expanded: PropTypes.bool.isRequired,
+ hasChildren: PropTypes.bool.isRequired,
+ onExpand: PropTypes.func.isRequired,
+ index: PropTypes.number.isRequired,
+ first: PropTypes.bool,
+ last: PropTypes.bool,
+ onClick: PropTypes.func,
+ onCollapse: PropTypes.func.isRequired,
+ depth: PropTypes.number.isRequired,
+ renderItem: PropTypes.func.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this._onKeyDown = this._onKeyDown.bind(this);
+ }
+
+ componentDidMount() {
+ // Make sure that none of the focusable elements inside the tree node container are
+ // tabbable if the tree node is not active. If the tree node is active and focus is
+ // outside its container, focus on the first focusable element inside.
+ const elms = getFocusableElements(this.refs.treenode);
+ if (elms.length === 0) {
+ return;
+ }
+
+ if (!this.props.active) {
+ elms.forEach(elm => elm.setAttribute("tabindex", "-1"));
+ return;
+ }
+
+ if (!elms.includes(this.refs.treenode.ownerDocument.activeElement)) {
+ elms[0].focus();
+ }
+ }
+
+ _onKeyDown(e) {
+ const { target, key, shiftKey } = e;
+
+ if (key !== "Tab") {
+ return;
+ }
+
+ const focusMoved = !!wrapMoveFocus(
+ getFocusableElements(this.refs.treenode),
+ target,
+ shiftKey
+ );
+ if (focusMoved) {
+ // Focus was moved to the begining/end of the list, so we need to prevent the
+ // default focus change that would happen here.
+ e.preventDefault();
+ }
+
+ e.stopPropagation();
+ }
+
+ render() {
+ const arrow = ArrowExpander({
+ item: this.props.item,
+ expanded: this.props.expanded,
+ visible: this.props.hasChildren,
+ onExpand: this.props.onExpand,
+ onCollapse: this.props.onCollapse,
+ });
+
+ const classList = ["tree-node", "div"];
+ if (this.props.index % 2) {
+ classList.push("tree-node-odd");
+ }
+ if (this.props.first) {
+ classList.push("tree-node-first");
+ }
+ if (this.props.last) {
+ classList.push("tree-node-last");
+ }
+ if (this.props.active) {
+ classList.push("tree-node-active");
+ }
+
+ let ariaExpanded;
+ if (this.props.hasChildren) {
+ ariaExpanded = false;
+ }
+ if (this.props.expanded) {
+ ariaExpanded = true;
+ }
+
+ return dom.div(
+ {
+ id: this.props.id,
+ className: classList.join(" "),
+ role: "treeitem",
+ ref: "treenode",
+ "aria-level": this.props.depth + 1,
+ onClick: this.props.onClick,
+ onKeyDownCapture: this.props.active ? this._onKeyDown : undefined,
+ "aria-expanded": ariaExpanded,
+ "data-expanded": this.props.expanded ? "" : undefined,
+ "data-depth": this.props.depth,
+ style: {
+ padding: 0,
+ margin: 0,
+ },
+ },
+
+ this.props.renderItem(
+ this.props.item,
+ this.props.depth,
+ this.props.focused,
+ arrow,
+ this.props.expanded
+ )
+ );
+ }
+}
+
+const ArrowExpander = createFactory(ArrowExpanderClass);
+const TreeNode = createFactory(TreeNodeClass);
+
+/**
+ * Create a function that calls the given function `fn` only once per animation
+ * frame.
+ *
+ * @param {Function} fn
+ * @returns {Function}
+ */
+function oncePerAnimationFrame(fn) {
+ let animationId = null;
+ let argsToPass = null;
+ return function (...args) {
+ argsToPass = args;
+ if (animationId !== null) {
+ return;
+ }
+
+ animationId = requestAnimationFrame(() => {
+ fn.call(this, ...argsToPass);
+ animationId = null;
+ argsToPass = null;
+ });
+ };
+}
+
+module.exports = Tree;
diff --git a/devtools/client/shared/components/VisibilityHandler.js b/devtools/client/shared/components/VisibilityHandler.js
new file mode 100644
index 0000000000..be1f3c5f93
--- /dev/null
+++ b/devtools/client/shared/components/VisibilityHandler.js
@@ -0,0 +1,57 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Helper class to disable panel rendering when it is in background.
+ *
+ * Toolbox code hides the iframes when switching to another panel
+ * and triggers `visibilitychange` events.
+ *
+ * See devtools/client/framework/toolbox.js:setIframeVisible().
+ */
+
+const {
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+class VisibilityHandler extends Component {
+ static get propTypes() {
+ return {
+ children: PropTypes.element.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.onVisibilityChange = this.onVisibilityChange.bind(this);
+ }
+
+ componentDidMount() {
+ document.addEventListener("visibilitychange", this.onVisibilityChange);
+ }
+
+ shouldComponentUpdate() {
+ return document.visibilityState == "visible";
+ }
+
+ componentWillUnmount() {
+ document.removeEventListener("visibilitychange", this.onVisibilityChange);
+ }
+
+ onVisibilityChange() {
+ if (document.visibilityState == "visible") {
+ this.forceUpdate();
+ }
+ }
+
+ render() {
+ return this.props.children;
+ }
+}
+
+module.exports = VisibilityHandler;
diff --git a/devtools/client/shared/components/menu/MenuButton.js b/devtools/client/shared/components/menu/MenuButton.js
new file mode 100644
index 0000000000..3367987c3c
--- /dev/null
+++ b/devtools/client/shared/components/menu/MenuButton.js
@@ -0,0 +1,450 @@
+/* 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/. */
+
+/* eslint-env browser */
+"use strict";
+
+// A button that toggles a doorhanger menu.
+
+const flags = require("resource://devtools/shared/flags.js");
+const {
+ createRef,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { button } = dom;
+
+const isMacOS = Services.appinfo.OS === "Darwin";
+
+loader.lazyRequireGetter(
+ this,
+ "HTMLTooltip",
+ "resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js",
+ true
+);
+
+loader.lazyRequireGetter(
+ this,
+ "focusableSelector",
+ "resource://devtools/client/shared/focus.js",
+ true
+);
+
+loader.lazyRequireGetter(
+ this,
+ "createPortal",
+ "resource://devtools/client/shared/vendor/react-dom.js",
+ true
+);
+
+// Return a copy of |obj| minus |fields|.
+const omit = (obj, fields) => {
+ const objCopy = { ...obj };
+ for (const field of fields) {
+ delete objCopy[field];
+ }
+ return objCopy;
+};
+
+class MenuButton extends PureComponent {
+ static get propTypes() {
+ return {
+ // The toolbox document that will be used for rendering the menu popup.
+ toolboxDoc: PropTypes.object.isRequired,
+
+ // A text content for the button.
+ label: PropTypes.string,
+
+ // URL of the icon to associate with the MenuButton. (Optional)
+ // e.g. chrome://devtools/skin/image/foo.svg
+ icon: PropTypes.string,
+
+ // An optional ID to assign to the menu's container tooltip object.
+ menuId: PropTypes.string,
+
+ // The preferred side of the anchor element to display the menu.
+ // Defaults to "bottom".
+ menuPosition: PropTypes.string.isRequired,
+
+ // The offset of the menu from the anchor element.
+ // Defaults to -5.
+ menuOffset: PropTypes.number.isRequired,
+
+ // The menu content.
+ children: PropTypes.any,
+
+ // Callback function to be invoked when the button is clicked.
+ onClick: PropTypes.func,
+
+ // Callback function to be invoked when the child panel is closed.
+ onCloseButton: PropTypes.func,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ menuPosition: "bottom",
+ menuOffset: -5,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.showMenu = this.showMenu.bind(this);
+ this.hideMenu = this.hideMenu.bind(this);
+ this.toggleMenu = this.toggleMenu.bind(this);
+ this.onHidden = this.onHidden.bind(this);
+ this.onClick = this.onClick.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
+ this.onTouchStart = this.onTouchStart.bind(this);
+
+ this.buttonRef = createRef();
+
+ this.state = {
+ expanded: false,
+ // In tests, initialize the menu immediately.
+ isMenuInitialized: flags.testing || false,
+ win: props.toolboxDoc.defaultView.top,
+ };
+ this.ignoreNextClick = false;
+
+ this.initializeTooltip();
+ }
+
+ componentDidMount() {
+ if (!this.state.isMenuInitialized) {
+ // Initialize the menu when the button is focused or moused over.
+ for (const event of ["focus", "mousemove"]) {
+ this.buttonRef.current.addEventListener(
+ event,
+ () => {
+ if (!this.state.isMenuInitialized) {
+ this.setState({ isMenuInitialized: true });
+ }
+ },
+ { once: true }
+ );
+ }
+ }
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ // If the window changes, we need to regenerate the HTMLTooltip or else the
+ // XUL wrapper element will appear above (in terms of z-index) the old
+ // window, and not the new.
+ const win = nextProps.toolboxDoc.defaultView.top;
+ if (
+ nextProps.toolboxDoc !== this.props.toolboxDoc ||
+ this.state.win !== win ||
+ nextProps.menuId !== this.props.menuId
+ ) {
+ this.setState({ win });
+ this.resetTooltip();
+ this.initializeTooltip();
+ }
+ }
+
+ componentDidUpdate() {
+ // The MenuButton creates the child panel when initializing the MenuButton.
+ // If the children function is called during the rendering process,
+ // this child list size might change. So we need to adjust content size here.
+ if (typeof this.props.children === "function") {
+ this.resizeContent();
+ }
+ }
+
+ componentWillUnmount() {
+ this.resetTooltip();
+ }
+
+ initializeTooltip() {
+ const tooltipProps = {
+ type: "doorhanger",
+ useXulWrapper: true,
+ isMenuTooltip: true,
+ };
+
+ if (this.props.menuId) {
+ tooltipProps.id = this.props.menuId;
+ }
+
+ this.tooltip = new HTMLTooltip(this.props.toolboxDoc, tooltipProps);
+ this.tooltip.on("hidden", this.onHidden);
+ }
+
+ async resetTooltip() {
+ if (!this.tooltip) {
+ return;
+ }
+
+ // Mark the menu as closed since the onHidden callback may not be called in
+ // this case.
+ this.setState({ expanded: false });
+ this.tooltip.off("hidden", this.onHidden);
+ this.tooltip.destroy();
+ this.tooltip = null;
+ }
+
+ async showMenu(anchor) {
+ this.setState({
+ expanded: true,
+ });
+
+ if (!this.tooltip) {
+ return;
+ }
+
+ await this.tooltip.show(anchor, {
+ position: this.props.menuPosition,
+ y: this.props.menuOffset,
+ });
+ }
+
+ async hideMenu() {
+ this.setState({
+ expanded: false,
+ });
+
+ if (!this.tooltip) {
+ return;
+ }
+
+ await this.tooltip.hide();
+ }
+
+ async toggleMenu(anchor) {
+ return this.state.expanded ? this.hideMenu() : this.showMenu(anchor);
+ }
+
+ // Used by the call site to indicate that the menu content has changed so
+ // its container should be updated.
+ resizeContent() {
+ if (!this.state.expanded || !this.tooltip || !this.buttonRef.current) {
+ return;
+ }
+
+ this.tooltip.show(this.buttonRef.current, {
+ position: this.props.menuPosition,
+ y: this.props.menuOffset,
+ });
+ }
+
+ // When we are closing the menu we will get a 'hidden' event before we get
+ // a 'click' event. We want to re-enable the pointer-events: auto setting we
+ // use on the button while the menu is visible, but we don't want to do it
+ // until after the subsequent click event since otherwise we will end up
+ // re-opening the menu.
+ //
+ // For mouse events, we achieve this by using setTimeout(..., 0) to schedule
+ // a separate task to run after the click event, but in the case of touch
+ // events the event order differs and the setTimeout callback will run before
+ // the click event.
+ //
+ // In order to prevent that we detect touch events and set a flag to ignore
+ // the next click event. However, we need to differentiate between touch drag
+ // events and long press events (which don't generate a 'click') and "taps"
+ // (which do). We do that by looking for a 'touchmove' event and clearing the
+ // flag if we get one.
+ onTouchStart(evt) {
+ const touchend = () => {
+ const anchorRect = this.buttonRef.current.getClientRects()[0];
+ const { clientX, clientY } = evt.changedTouches[0];
+ // We need to check that the click is inside the bounds since when the
+ // menu is being closed the button will currently have
+ // pointer-events: none (and if we don't check the bounds we will end up
+ // ignoring unrelated clicks).
+ if (
+ anchorRect.x <= clientX &&
+ clientX <= anchorRect.x + anchorRect.width &&
+ anchorRect.y <= clientY &&
+ clientY <= anchorRect.y + anchorRect.height
+ ) {
+ this.ignoreNextClick = true;
+ }
+ };
+
+ const touchmove = () => {
+ this.state.win.removeEventListener("touchend", touchend);
+ };
+
+ this.state.win.addEventListener("touchend", touchend, { once: true });
+ this.state.win.addEventListener("touchmove", touchmove, { once: true });
+ }
+
+ onHidden() {
+ this.setState({ expanded: false });
+ // While the menu is open, if we click _anywhere_ outside the menu, it will
+ // automatically close. This is performed by the XUL wrapper before we get
+ // any chance to see any event. To avoid immediately re-opening the menu
+ // when we process the subsequent click event on this button, we set
+ // 'pointer-events: none' on the button while the menu is open.
+ //
+ // After the menu is closed we need to remove the pointer-events style (so
+ // the button works again) but we don't want to do it immediately since the
+ // "popuphidden" event which triggers this callback might be dispatched
+ // before the "click" event that we want to ignore. As a result, we queue
+ // up a task using setTimeout() to run after the "click" event.
+ this.state.win.setTimeout(() => {
+ if (this.buttonRef.current) {
+ this.buttonRef.current.style.pointerEvents = "auto";
+ }
+ this.state.win.removeEventListener("touchstart", this.onTouchStart, true);
+ }, 0);
+
+ this.state.win.addEventListener("touchstart", this.onTouchStart, true);
+
+ if (this.props.onCloseButton) {
+ this.props.onCloseButton();
+ }
+ }
+
+ async onClick(e) {
+ if (this.ignoreNextClick) {
+ this.ignoreNextClick = false;
+ return;
+ }
+
+ if (e.target === this.buttonRef.current) {
+ // On Mac, even after clicking the button it doesn't get focus.
+ // Force focus to the button so that our keydown handlers get called.
+ this.buttonRef.current.focus();
+
+ if (this.props.onClick) {
+ this.props.onClick(e);
+ }
+
+ if (!e.defaultPrevented) {
+ const wasKeyboardEvent = e.screenX === 0 && e.screenY === 0;
+ // If the popup menu will be shown, disable this button in order to
+ // prevent reopening the popup menu. See extended comment in onHidden().
+ // above.
+ //
+ // Also, we should _not_ set 'pointer-events: none' if
+ // ui.popup.disable_autohide pref is in effect since, in that case,
+ // there's no redundant hiding behavior and we actually want clicking
+ // the button to close the menu.
+ if (
+ !this.state.expanded &&
+ !Services.prefs.getBoolPref("ui.popup.disable_autohide", false)
+ ) {
+ this.buttonRef.current.style.pointerEvents = "none";
+ }
+ await this.toggleMenu(e.target);
+ // If the menu was activated by keyboard, focus the first item.
+ if (wasKeyboardEvent && this.tooltip) {
+ this.tooltip.focus();
+ }
+
+ // MenuButton creates the children dynamically when clicking the button,
+ // so execute the goggle menu after updating the children panel.
+ if (typeof this.props.children === "function") {
+ this.forceUpdate();
+ }
+ }
+ // If we clicked one of the menu items, then, by default, we should
+ // auto-collapse the menu.
+ //
+ // We check for the defaultPrevented state, however, so that menu items can
+ // turn this behavior off (e.g. a menu item with an embedded button).
+ } else if (
+ this.state.expanded &&
+ !e.defaultPrevented &&
+ e.target.matches(focusableSelector)
+ ) {
+ this.hideMenu();
+ }
+ }
+
+ onKeyDown(e) {
+ if (!this.state.expanded) {
+ return;
+ }
+
+ const isButtonFocussed =
+ this.props.toolboxDoc &&
+ this.props.toolboxDoc.activeElement === this.buttonRef.current;
+
+ switch (e.key) {
+ case "Escape":
+ this.hideMenu();
+ e.preventDefault();
+ break;
+
+ case "Tab":
+ case "ArrowDown":
+ if (isButtonFocussed && this.tooltip) {
+ if (this.tooltip.focus()) {
+ e.preventDefault();
+ }
+ }
+ break;
+
+ case "ArrowUp":
+ if (isButtonFocussed && this.tooltip) {
+ if (this.tooltip.focusEnd()) {
+ e.preventDefault();
+ }
+ }
+ break;
+ case "t":
+ if ((isMacOS && e.metaKey) || (!isMacOS && e.ctrlKey)) {
+ // Close the menu if the user opens a new tab while it is still open.
+ //
+ // Bug 1499271: Once toolbox has been converted to XUL we should watch
+ // for the 'visibilitychange' event instead of explicitly looking for
+ // Ctrl+T.
+ this.hideMenu();
+ }
+ break;
+ }
+ }
+
+ render() {
+ const buttonProps = {
+ // Pass through any props set on the button, except the ones we handle
+ // here.
+ ...omit(this.props, Object.keys(MenuButton.propTypes)),
+ onClick: this.onClick,
+ "aria-expanded": this.state.expanded,
+ "aria-haspopup": "menu",
+ ref: this.buttonRef,
+ };
+
+ if (this.state.expanded) {
+ buttonProps.onKeyDown = this.onKeyDown;
+ }
+
+ if (this.props.menuId) {
+ buttonProps["aria-controls"] = this.props.menuId;
+ }
+
+ if (this.props.icon) {
+ const iconClass = "menu-button--iconic";
+ buttonProps.className = buttonProps.className
+ ? `${buttonProps.className} ${iconClass}`
+ : iconClass;
+ buttonProps.style = {
+ "--menuitem-icon-image": "url(" + this.props.icon + ")",
+ };
+ }
+
+ if (this.state.isMenuInitialized) {
+ const menu = createPortal(
+ typeof this.props.children === "function"
+ ? this.props.children()
+ : this.props.children,
+ this.tooltip.panel
+ );
+
+ return button(buttonProps, this.props.label, menu);
+ }
+
+ return button(buttonProps, this.props.label);
+ }
+}
+
+module.exports = MenuButton;
diff --git a/devtools/client/shared/components/menu/MenuItem.js b/devtools/client/shared/components/menu/MenuItem.js
new file mode 100644
index 0000000000..c3efa6db6c
--- /dev/null
+++ b/devtools/client/shared/components/menu/MenuItem.js
@@ -0,0 +1,211 @@
+/* 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/. */
+
+/* eslint-env browser */
+"use strict";
+
+// A command in a menu.
+
+const {
+ createFactory,
+ createRef,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { button, li, span } = dom;
+loader.lazyGetter(this, "Localized", () =>
+ createFactory(
+ require("resource://devtools/client/shared/vendor/fluent-react.js")
+ .Localized
+ )
+);
+
+class MenuItem extends PureComponent {
+ static get propTypes() {
+ return {
+ // An optional keyboard shortcut to display next to the item.
+ // (This does not actually register the event listener for the key.)
+ accelerator: PropTypes.string,
+
+ // A tri-state value that may be true/false if item should be checkable,
+ // and undefined otherwise.
+ checked: PropTypes.bool,
+
+ // Any additional classes to assign to the button specified as
+ // a space-separated string.
+ className: PropTypes.string,
+
+ // A disabled state of the menu item.
+ disabled: PropTypes.bool,
+
+ // URL of the icon to associate with the MenuItem. (Optional)
+ //
+ // e.g. chrome://devtools/skim/image/foo.svg
+ //
+ // This may also be set in CSS using the --menuitem-icon-image variable.
+ // Note that in this case, the variable should specify the CSS <image> to
+ // use, not simply the URL (e.g.
+ // "url(chrome://devtools/skim/image/foo.svg)").
+ icon: PropTypes.string,
+
+ // An optional ID to be assigned to the item.
+ id: PropTypes.string,
+
+ // The item label for use with legacy localization systems.
+ label: PropTypes.string,
+
+ // The Fluent ID for localizing the label.
+ l10nID: PropTypes.string,
+
+ // An optional callback to be invoked when the item is selected.
+ onClick: PropTypes.func,
+
+ // Optional menu item role override. Use this property with a value
+ // "menuitemradio" if the menu item is a radio.
+ role: PropTypes.string,
+
+ // An optional text for the item tooltip.
+ tooltip: PropTypes.string,
+ };
+ }
+
+ /**
+ * Use this as a fallback `icon` prop if your MenuList contains MenuItems
+ * with or without icon in order to keep all MenuItems aligned.
+ */
+ static get DUMMY_ICON() {
+ return `data:image/svg+xml,${encodeURIComponent(
+ '<svg height="16" width="16"></svg>'
+ )}`;
+ }
+
+ constructor(props) {
+ super(props);
+ this.labelRef = createRef();
+ }
+
+ componentDidMount() {
+ if (!this.labelRef.current) {
+ return;
+ }
+
+ // Pre-fetch any backgrounds specified for the item.
+ const win = this.labelRef.current.ownerDocument.defaultView;
+ this.preloadCallback = win.requestIdleCallback(() => {
+ this.preloadCallback = null;
+ if (!this.labelRef.current) {
+ return;
+ }
+
+ const backgrounds = win
+ .getComputedStyle(this.labelRef.current, ":before")
+ .getCSSImageURLs("background-image");
+ for (const background of backgrounds) {
+ const image = new Image();
+ image.src = background;
+ }
+ });
+ }
+
+ componentWillUnmount() {
+ if (!this.labelRef.current || !this.preloadCallback) {
+ return;
+ }
+
+ const win = this.labelRef.current.ownerDocument.defaultView;
+ if (win) {
+ win.cancelIdleCallback(this.preloadCallback);
+ }
+ this.preloadCallback = null;
+ }
+
+ render() {
+ const attr = {
+ className: "command",
+ };
+
+ if (this.props.id) {
+ attr.id = this.props.id;
+ }
+
+ if (this.props.className) {
+ attr.className += " " + this.props.className;
+ }
+
+ if (this.props.icon) {
+ attr.className += " iconic";
+ attr.style = { "--menuitem-icon-image": "url(" + this.props.icon + ")" };
+ }
+
+ if (this.props.onClick) {
+ attr.onClick = this.props.onClick;
+ }
+
+ if (this.props.tooltip) {
+ attr.title = this.props.tooltip;
+ }
+
+ if (this.props.disabled) {
+ attr.disabled = this.props.disabled;
+ }
+
+ if (this.props.role) {
+ attr.role = this.props.role;
+ } else if (typeof this.props.checked !== "undefined") {
+ attr.role = "menuitemcheckbox";
+ } else {
+ attr.role = "menuitem";
+ }
+
+ if (this.props.checked) {
+ attr["aria-checked"] = true;
+ }
+
+ const children = [];
+ const className = "label";
+
+ // Add the text label.
+ if (this.props.l10nID) {
+ // Fluent localized label.
+ children.push(
+ Localized(
+ { id: this.props.l10nID, key: "label" },
+ span({ className, ref: this.labelRef })
+ )
+ );
+ } else {
+ children.push(
+ span({ key: "label", className, ref: this.labelRef }, this.props.label)
+ );
+ }
+
+ if (this.props.l10nID && this.props.label) {
+ console.warn(
+ "<MenuItem> should only take either an l10nID or a label, not both"
+ );
+ }
+ if (!this.props.l10nID && !this.props.label) {
+ console.warn("<MenuItem> requires either an l10nID, or a label prop.");
+ }
+
+ if (typeof this.props.accelerator !== "undefined") {
+ const acceleratorLabel = span(
+ { key: "accelerator", className: "accelerator" },
+ this.props.accelerator
+ );
+ children.push(acceleratorLabel);
+ }
+
+ return li(
+ {
+ className: "menuitem",
+ role: "presentation",
+ },
+ button(attr, children)
+ );
+ }
+}
+
+module.exports = MenuItem;
diff --git a/devtools/client/shared/components/menu/MenuList.js b/devtools/client/shared/components/menu/MenuList.js
new file mode 100644
index 0000000000..4c355cca10
--- /dev/null
+++ b/devtools/client/shared/components/menu/MenuList.js
@@ -0,0 +1,164 @@
+/* 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/. */
+
+/* eslint-env browser */
+"use strict";
+
+// A list of menu items.
+//
+// This component provides keyboard navigation amongst any focusable
+// children.
+
+const {
+ Children,
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const { div } = dom;
+const {
+ focusableSelector,
+} = require("resource://devtools/client/shared/focus.js");
+
+class MenuList extends PureComponent {
+ static get propTypes() {
+ return {
+ // ID to assign to the list container.
+ id: PropTypes.string,
+
+ // Children of the list.
+ children: PropTypes.any,
+
+ // Called whenever there is a change to the hovered or selected child.
+ // The callback is passed the ID of the highlighted child or null if no
+ // child is highlighted.
+ onHighlightedChildChange: PropTypes.func,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.onKeyDown = this.onKeyDown.bind(this);
+ this.onMouseOverOrFocus = this.onMouseOverOrFocus.bind(this);
+ this.onMouseOutOrBlur = this.onMouseOutOrBlur.bind(this);
+ this.notifyHighlightedChildChange =
+ this.notifyHighlightedChildChange.bind(this);
+
+ this.setWrapperRef = element => {
+ this.wrapperRef = element;
+ };
+ }
+
+ onMouseOverOrFocus(e) {
+ this.notifyHighlightedChildChange(e.target.id);
+ }
+
+ onMouseOutOrBlur(e) {
+ const hoveredElem = this.wrapperRef.querySelector(":hover");
+ if (!hoveredElem) {
+ this.notifyHighlightedChildChange(null);
+ }
+ }
+
+ notifyHighlightedChildChange(id) {
+ if (this.props.onHighlightedChildChange) {
+ this.props.onHighlightedChildChange(id);
+ }
+ }
+
+ onKeyDown(e) {
+ // Check if the focus is in the list.
+ if (
+ !this.wrapperRef ||
+ !this.wrapperRef.contains(e.target.ownerDocument.activeElement)
+ ) {
+ return;
+ }
+
+ const getTabList = () =>
+ Array.from(this.wrapperRef.querySelectorAll(focusableSelector));
+
+ switch (e.key) {
+ case "Tab":
+ case "ArrowUp":
+ case "ArrowDown":
+ {
+ const tabList = getTabList();
+ const currentElement = e.target.ownerDocument.activeElement;
+ const currentIndex = tabList.indexOf(currentElement);
+ if (currentIndex !== -1) {
+ let nextIndex;
+ if (e.key === "ArrowDown" || (e.key === "Tab" && !e.shiftKey)) {
+ nextIndex =
+ currentIndex === tabList.length - 1 ? 0 : currentIndex + 1;
+ } else {
+ nextIndex =
+ currentIndex === 0 ? tabList.length - 1 : currentIndex - 1;
+ }
+ tabList[nextIndex].focus();
+ e.preventDefault();
+ }
+ }
+ break;
+
+ case "Home":
+ {
+ const firstItem = this.wrapperRef.querySelector(focusableSelector);
+ if (firstItem) {
+ firstItem.focus();
+ e.preventDefault();
+ }
+ }
+ break;
+
+ case "End":
+ {
+ const tabList = getTabList();
+ if (tabList.length) {
+ tabList[tabList.length - 1].focus();
+ e.preventDefault();
+ }
+ }
+ break;
+ }
+ }
+
+ render() {
+ const attr = {
+ role: "menu",
+ ref: this.setWrapperRef,
+ onKeyDown: this.onKeyDown,
+ onMouseOver: this.onMouseOverOrFocus,
+ onMouseOut: this.onMouseOutOrBlur,
+ onFocus: this.onMouseOverOrFocus,
+ onBlur: this.onMouseOutOrBlur,
+ className: "menu-standard-padding",
+ };
+
+ if (this.props.id) {
+ attr.id = this.props.id;
+ }
+
+ // Add padding for checkbox image if necessary.
+ let hasCheckbox = false;
+ Children.forEach(this.props.children, (child, i) => {
+ if (child == null || typeof child == "undefined") {
+ console.warn("MenuList children at index", i, "is", child);
+ return;
+ }
+
+ if (typeof child?.props?.checked !== "undefined") {
+ hasCheckbox = true;
+ }
+ });
+ if (hasCheckbox) {
+ attr.className = "checkbox-container menu-standard-padding";
+ }
+
+ return div(attr, this.props.children);
+ }
+}
+
+module.exports = MenuList;
diff --git a/devtools/client/shared/components/menu/moz.build b/devtools/client/shared/components/menu/moz.build
new file mode 100644
index 0000000000..08046199e5
--- /dev/null
+++ b/devtools/client/shared/components/menu/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "MenuButton.js",
+ "MenuItem.js",
+ "MenuList.js",
+ "utils.js",
+)
diff --git a/devtools/client/shared/components/menu/utils.js b/devtools/client/shared/components/menu/utils.js
new file mode 100644
index 0000000000..e6fca96822
--- /dev/null
+++ b/devtools/client/shared/components/menu/utils.js
@@ -0,0 +1,62 @@
+/* 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/. */
+
+"use strict";
+
+const Menu = require("resource://devtools/client/framework/menu.js");
+const MenuItem = require("resource://devtools/client/framework/menu-item.js");
+
+/**
+ * Helper function for opening context menu.
+ *
+ * @param {Array} items
+ * List of menu items.
+ * @param {Object} options:
+ * @property {Element} button
+ * Button element used to open the menu.
+ * @property {Number} screenX
+ * Screen x coordinate of the menu on the screen.
+ * @property {Number} screenY
+ * Screen y coordinate of the menu on the screen.
+ */
+function showMenu(items, options) {
+ if (items.length === 0) {
+ return;
+ }
+
+ // Build the menu object from provided menu items.
+ const menu = new Menu();
+ items.forEach(item => {
+ if (item == "-") {
+ item = { type: "separator" };
+ }
+
+ const menuItem = new MenuItem(item);
+ const subItems = item.submenu;
+
+ if (subItems) {
+ const subMenu = new Menu();
+ subItems.forEach(subItem => {
+ subMenu.append(new MenuItem(subItem));
+ });
+ menuItem.submenu = subMenu;
+ }
+
+ menu.append(menuItem);
+ });
+
+ // Calculate position on the screen according to
+ // the parent button if available.
+ if (options.button) {
+ menu.popupAtTarget(options.button);
+ } else {
+ const screenX = options.screenX;
+ const screenY = options.screenY;
+ menu.popup(screenX, screenY, window.document);
+ }
+}
+
+module.exports = {
+ showMenu,
+};
diff --git a/devtools/client/shared/components/moz.build b/devtools/client/shared/components/moz.build
new file mode 100644
index 0000000000..32f55ecaac
--- /dev/null
+++ b/devtools/client/shared/components/moz.build
@@ -0,0 +1,41 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "object-inspector",
+ "menu",
+ "reps",
+ "splitter",
+ "tabs",
+ "throttling",
+ "tree",
+]
+
+DevToolsModules(
+ "Accordion.js",
+ "AppErrorBoundary.js",
+ "Frame.js",
+ "HSplitBox.js",
+ "List.js",
+ "MdnLink.js",
+ "NotificationBox.js",
+ "SearchBox.js",
+ "SearchBoxAutocompletePopup.js",
+ "SearchModifiers.js",
+ "Sidebar.js",
+ "SidebarToggle.js",
+ "SmartTrace.js",
+ "StackTrace.js",
+ "Tree.js",
+ "VirtualizedTree.js",
+ "VisibilityHandler.js",
+)
+
+MOCHITEST_CHROME_MANIFESTS += ["test/chrome/chrome.toml"]
+BROWSER_CHROME_MANIFESTS += [
+ "test/browser/browser.toml",
+ "test/node/stubs/reps/stubs.toml",
+]
diff --git a/devtools/client/shared/components/object-inspector/actions.js b/devtools/client/shared/components/object-inspector/actions.js
new file mode 100644
index 0000000000..370f1b161a
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/actions.js
@@ -0,0 +1,225 @@
+/* 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/>. */
+
+const { loadItemProperties } = require("resource://devtools/client/shared/components/object-inspector/utils/load-properties.js");
+const {
+ getPathExpression,
+ getParentFront,
+ getParentGripValue,
+ getValue,
+ nodeIsBucket,
+ getFront,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+const { getLoadedProperties, getWatchpoints } = require("resource://devtools/client/shared/components/object-inspector/reducer.js");
+
+/**
+ * This action is responsible for expanding a given node, which also means that
+ * it will call the action responsible to fetch properties.
+ */
+function nodeExpand(node, actor) {
+ return async ({ dispatch }) => {
+ dispatch({ type: "NODE_EXPAND", data: { node } });
+ dispatch(nodeLoadProperties(node, actor));
+ };
+}
+
+function nodeCollapse(node) {
+ return {
+ type: "NODE_COLLAPSE",
+ data: { node },
+ };
+}
+
+/*
+ * This action checks if we need to fetch properties, entries, prototype and
+ * symbols for a given node. If we do, it will call the appropriate ObjectFront
+ * functions.
+ */
+function nodeLoadProperties(node, actor) {
+ return async ({ dispatch, client, getState }) => {
+ const state = getState();
+ const loadedProperties = getLoadedProperties(state);
+ if (loadedProperties.has(node.path)) {
+ return;
+ }
+
+ try {
+ const properties = await loadItemProperties(
+ node,
+ client,
+ loadedProperties
+ );
+
+ // If the client does not have a releaseActor function, it means the actors are
+ // handled directly by the consumer, so we don't need to track them.
+ if (!client || !client.releaseActor) {
+ actor = null;
+ }
+
+ dispatch(nodePropertiesLoaded(node, actor, properties));
+ } catch (e) {
+ console.error(e);
+ }
+ };
+}
+
+function nodePropertiesLoaded(node, actor, properties) {
+ return {
+ type: "NODE_PROPERTIES_LOADED",
+ data: { node, actor, properties },
+ };
+}
+
+/*
+ * This action adds a property watchpoint to an object
+ */
+function addWatchpoint(item, watchpoint) {
+ return async function({ dispatch, client }) {
+ const { parent, name } = item;
+ let object = getValue(parent);
+
+ if (nodeIsBucket(parent)) {
+ object = getValue(parent.parent);
+ }
+
+ if (!object) {
+ return;
+ }
+
+ const path = parent.path;
+ const property = name;
+ const label = getPathExpression(item);
+ const actor = object.actor;
+
+ await client.addWatchpoint(object, property, label, watchpoint);
+
+ dispatch({
+ type: "SET_WATCHPOINT",
+ data: { path, watchpoint, property, actor },
+ });
+ };
+}
+
+/*
+ * This action removes a property watchpoint from an object
+ */
+function removeWatchpoint(item) {
+ return async function({ dispatch, client }) {
+ const { parent, name } = item;
+ let object = getValue(parent);
+
+ if (nodeIsBucket(parent)) {
+ object = getValue(parent.parent);
+ }
+
+ const property = name;
+ const path = parent.path;
+ const actor = object.actor;
+
+ await client.removeWatchpoint(object, property);
+
+ dispatch({
+ type: "REMOVE_WATCHPOINT",
+ data: { path, property, actor },
+ });
+ };
+}
+
+function getActorIDs(roots) {
+ if (!roots) {
+ return []
+ }
+
+ const actorIds = [];
+ for (const root of roots) {
+ const front = getFront(root);
+ if (front?.actorID) {
+ actorIds.push(front.actorID);
+ }
+ }
+
+ return actorIds;
+}
+
+function closeObjectInspector(roots) {
+ return ({ client }) => {
+ releaseActors(client, roots);
+ };
+}
+
+/*
+ * This action is dispatched when the `roots` prop, provided by a consumer of
+ * the ObjectInspector (inspector, console, …), is modified. It will clean the
+ * internal state properties (expandedPaths, loadedProperties, …) and release
+ * the actors consumed with the previous roots.
+ * It takes a props argument which reflects what is passed by the upper-level
+ * consumer.
+ */
+function rootsChanged(roots, oldRoots) {
+ return ({ dispatch, client }) => {
+ releaseActors(client, oldRoots, roots);
+ dispatch({
+ type: "ROOTS_CHANGED",
+ data: roots,
+ });
+ };
+}
+
+/**
+ * Release any actors we don't need anymore
+ *
+ * @param {Object} client: Object with a `releaseActor` method
+ * @param {Array} oldRoots: The roots in which we want to cleanup now-unused actors
+ * @param {Array} newRoots: The current roots (might have item that are also in oldRoots)
+ */
+async function releaseActors(client, oldRoots, newRoots = []) {
+ if (!client?.releaseActor ) {
+ return;
+ }
+
+ let actorIdsToRelease = getActorIDs(oldRoots);
+ if (newRoots.length) {
+ const newActorIds = getActorIDs(newRoots);
+ actorIdsToRelease = actorIdsToRelease.filter(id => !newActorIds.includes(id));
+ }
+
+ if (!actorIdsToRelease.length) {
+ return;
+ }
+ await Promise.all(actorIdsToRelease.map(client.releaseActor));
+}
+
+function invokeGetter(node, receiverId) {
+ return async ({ dispatch, client, getState }) => {
+ try {
+ const objectFront =
+ getParentFront(node) ||
+ client.createObjectFront(getParentGripValue(node));
+ const getterName = node.propertyName || node.name;
+
+ const result = await objectFront.getPropertyValue(getterName, receiverId);
+ dispatch({
+ type: "GETTER_INVOKED",
+ data: {
+ node,
+ result,
+ },
+ });
+ } catch (e) {
+ console.error(e);
+ }
+ };
+}
+
+module.exports = {
+ closeObjectInspector,
+ invokeGetter,
+ nodeExpand,
+ nodeCollapse,
+ nodeLoadProperties,
+ nodePropertiesLoaded,
+ rootsChanged,
+ addWatchpoint,
+ removeWatchpoint,
+};
diff --git a/devtools/client/shared/components/object-inspector/components/ObjectInspector.css b/devtools/client/shared/components/object-inspector/components/ObjectInspector.css
new file mode 100644
index 0000000000..726e2ed8b8
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/components/ObjectInspector.css
@@ -0,0 +1,115 @@
+/* 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/. */
+
+.tree.object-inspector .node.object-node {
+ display: inline-block;
+}
+
+.tree.object-inspector .object-label,
+.tree.object-inspector .object-label * {
+ color: var(--theme-highlight-blue);
+}
+
+.tree.object-inspector .node .unavailable {
+ color: var(--theme-comment);
+}
+
+.tree.object-inspector .lessen,
+.tree.object-inspector .lessen *,
+.tree.object-inspector .lessen .object-label,
+.tree.object-inspector .lessen .object-label * {
+ color: var(--theme-comment);
+}
+
+.tree.object-inspector .block .object-label,
+.tree.object-inspector .block .object-label * {
+ color: var(--theme-body-color);
+}
+
+.tree.object-inspector .block .object-label::before {
+ content: "☲";
+ font-size: 1.1em;
+ display: inline;
+ padding-inline-end: 2px;
+ line-height: 14px;
+}
+
+.object-inspector .object-delimiter {
+ color: var(--theme-comment);
+ white-space: pre-wrap;
+}
+
+.object-inspector .tree-node .arrow {
+ display: inline-block;
+ vertical-align: middle;
+ margin-inline-start: -1px;
+}
+
+/* Focused styles */
+.tree.object-inspector .tree-node.focused * {
+ color: inherit;
+}
+
+.tree-node.focused button.open-inspector {
+ fill: currentColor;
+}
+
+.tree-node.focused button.invoke-getter {
+ background-color: currentColor;
+}
+
+button[class*="remove-watchpoint-"] {
+ background: url("chrome://devtools/content/debugger/images/webconsole-logpoint.svg")
+ no-repeat;
+ display: inline-block;
+ vertical-align: top;
+ height: 13px;
+ width: 15px;
+ margin: 1px 4px 0px 20px;
+ padding: 0;
+ border: none;
+ -moz-context-properties: fill, stroke;
+ cursor: pointer;
+}
+
+button.remove-watchpoint-set {
+ fill: var(--breakpoint-fill);
+ stroke: var(--breakpoint-fill);
+}
+
+button.remove-watchpoint-get {
+ fill: var(--purple-60);
+ stroke: var(--purple-60);
+}
+
+button.remove-watchpoint-getorset {
+ fill: var(--yellow-60);
+ stroke: var(--yellow-60);
+}
+
+.tree-node.focused button[class*="remove-watchpoint-"] {
+ stroke: white;
+}
+
+/* Don't display the light grey background we have on button hover */
+.theme-dark button[class*="remove-watchpoint-"]:hover,
+.theme-light button[class*="remove-watchpoint-"]:hover {
+ background-color: transparent;
+}
+
+
+/* Specific style for when root nodes are displayed as header (e.g. in debugger preview popup */
+.tree.object-inspector.header-root-node {
+ .tree-node[aria-level="1"] {
+ border-block-end: 1px solid var(--theme-splitter-color);
+ padding-block-end: 4px;
+ margin-block-end: 4px;
+ overflow-x: clip;
+ word-break: keep-all;
+ }
+
+ .tree-node:not([aria-level="1"]) .tree-indent:first-of-type {
+ width: 0;
+ }
+}
diff --git a/devtools/client/shared/components/object-inspector/components/ObjectInspector.js b/devtools/client/shared/components/object-inspector/components/ObjectInspector.js
new file mode 100644
index 0000000000..c4f77d0d2a
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/components/ObjectInspector.js
@@ -0,0 +1,387 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+ createElement,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const {
+ connect,
+ Provider,
+} = require("resource://devtools/client/shared/vendor/react-redux.js");
+loader.lazyRequireGetter(
+ this,
+ "createStore",
+ "resource://devtools/client/shared/redux/create-store.js"
+);
+
+const actions = require("resource://devtools/client/shared/components/object-inspector/actions.js");
+const {
+ getExpandedPaths,
+ getLoadedProperties,
+ getEvaluations,
+ default: reducer,
+} = require("resource://devtools/client/shared/components/object-inspector/reducer.js");
+
+const Tree = createFactory(require("resource://devtools/client/shared/components/Tree.js"));
+
+const ObjectInspectorItem = createFactory(
+ require("resource://devtools/client/shared/components/object-inspector/components/ObjectInspectorItem.js")
+);
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const { renderRep, shouldRenderRootsInReps } = Utils;
+const {
+ getChildrenWithEvaluations,
+ getActor,
+ getEvaluatedItem,
+ getParent,
+ getValue,
+ nodeIsPrimitive,
+ nodeHasGetter,
+ nodeHasSetter,
+} = Utils.node;
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+
+// This implements a component that renders an interactive inspector
+// for looking at JavaScript objects. It expects descriptions of
+// objects from the protocol, and will dynamically fetch children
+// properties as objects are expanded.
+//
+// If you want to inspect a single object, pass the name and the
+// protocol descriptor of it:
+//
+// ObjectInspector({
+// name: "foo",
+// desc: { writable: true, ..., { value: { actor: "1", ... }}},
+// ...
+// })
+//
+// If you want multiple top-level objects (like scopes), you can pass
+// an array of manually constructed nodes as `roots`:
+//
+// ObjectInspector({
+// roots: [{ name: ... }, ...],
+// ...
+// });
+
+// There are 3 types of nodes: a simple node with a children array, an
+// object that has properties that should be children when they are
+// fetched, and a primitive value that should be displayed with no
+// children.
+
+class ObjectInspector extends Component {
+ static defaultProps = {
+ autoReleaseObjectActors: true
+ };
+ constructor(props) {
+ super();
+ this.cachedNodes = new Map();
+
+ const self = this;
+
+ self.getItemChildren = this.getItemChildren.bind(this);
+ self.isNodeExpandable = this.isNodeExpandable.bind(this);
+ self.setExpanded = this.setExpanded.bind(this);
+ self.focusItem = this.focusItem.bind(this);
+ self.activateItem = this.activateItem.bind(this);
+ self.getRoots = this.getRoots.bind(this);
+ self.getNodeKey = this.getNodeKey.bind(this);
+ self.shouldItemUpdate = this.shouldItemUpdate.bind(this);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillMount() {
+ this.roots = this.props.roots;
+ this.focusedItem = this.props.focusedItem;
+ this.activeItem = this.props.activeItem;
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillUpdate(nextProps) {
+ this.removeOutdatedNodesFromCache(nextProps);
+
+ if (this.roots !== nextProps.roots) {
+ // Since the roots changed, we assume the properties did as well,
+ // so we need to cleanup the component internal state.
+ const oldRoots = this.roots;
+ this.roots = nextProps.roots;
+ this.focusedItem = nextProps.focusedItem;
+ this.activeItem = nextProps.activeItem;
+ if (this.props.rootsChanged) {
+ this.props.rootsChanged(this.roots, oldRoots);
+ }
+ }
+ }
+
+ removeOutdatedNodesFromCache(nextProps) {
+ // When the roots changes, we can wipe out everything.
+ if (this.roots !== nextProps.roots) {
+ this.cachedNodes.clear();
+ return;
+ }
+
+ for (const [path, properties] of nextProps.loadedProperties) {
+ if (properties !== this.props.loadedProperties.get(path)) {
+ this.cachedNodes.delete(path);
+ }
+ }
+
+ // If there are new evaluations, we want to remove the existing cached
+ // nodes from the cache.
+ if (nextProps.evaluations > this.props.evaluations) {
+ for (const key of nextProps.evaluations.keys()) {
+ if (!this.props.evaluations.has(key)) {
+ this.cachedNodes.delete(key);
+ }
+ }
+ }
+ }
+
+ shouldComponentUpdate(nextProps) {
+ const { expandedPaths, loadedProperties, evaluations } = this.props;
+
+ // We should update if:
+ // - there are new loaded properties
+ // - OR there are new evaluations
+ // - OR the expanded paths number changed, and all of them have properties
+ // loaded
+ // - OR the expanded paths number did not changed, but old and new sets
+ // differ
+ // - OR the focused node changed.
+ // - OR the active node changed.
+ return (
+ loadedProperties !== nextProps.loadedProperties ||
+ loadedProperties.size !== nextProps.loadedProperties.size ||
+ evaluations.size !== nextProps.evaluations.size ||
+ (expandedPaths.size !== nextProps.expandedPaths.size &&
+ [...nextProps.expandedPaths].every(path =>
+ nextProps.loadedProperties.has(path)
+ )) ||
+ (expandedPaths.size === nextProps.expandedPaths.size &&
+ [...nextProps.expandedPaths].some(key => !expandedPaths.has(key))) ||
+ this.focusedItem !== nextProps.focusedItem ||
+ this.activeItem !== nextProps.activeItem ||
+ this.roots !== nextProps.roots
+ );
+ }
+
+ componentWillUnmount() {
+ if (this.props.autoReleaseObjectActors){
+ this.props.closeObjectInspector(this.props.roots);
+ }
+ }
+
+ getItemChildren(item) {
+ const { loadedProperties, evaluations } = this.props;
+ const { cachedNodes } = this;
+
+ return getChildrenWithEvaluations({
+ evaluations,
+ loadedProperties,
+ cachedNodes,
+ item,
+ });
+ }
+
+ getRoots() {
+ const { evaluations, roots } = this.props;
+ const length = roots.length;
+
+ for (let i = 0; i < length; i++) {
+ let rootItem = roots[i];
+
+ if (evaluations.has(rootItem.path)) {
+ roots[i] = getEvaluatedItem(rootItem, evaluations);
+ }
+ }
+
+ return roots;
+ }
+
+ getNodeKey(item) {
+ return item.path && typeof item.path.toString === "function"
+ ? item.path.toString()
+ : JSON.stringify(item);
+ }
+
+ isNodeExpandable(item) {
+ if (
+ nodeIsPrimitive(item) ||
+ item.contents?.value?.useCustomFormatter
+ ) {
+ return false;
+ }
+
+ if (nodeHasSetter(item) || nodeHasGetter(item)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ setExpanded(item, expand) {
+ if (
+ !this.isNodeExpandable(item) ||
+ // Don't allow to collapse header root node
+ (
+ this.props.displayRootNodeAsHeader &&
+ !expand &&
+ this.props.roots[0] == item
+ )
+ ) {
+ return;
+ }
+
+ const {
+ nodeExpand,
+ nodeCollapse,
+ recordTelemetryEvent,
+ setExpanded,
+ roots,
+ } = this.props;
+
+ if (expand === true) {
+ const actor = getActor(item, roots);
+ nodeExpand(item, actor);
+ if (recordTelemetryEvent) {
+ recordTelemetryEvent("object_expanded");
+ }
+ } else {
+ nodeCollapse(item);
+ }
+
+ if (setExpanded) {
+ setExpanded(item, expand);
+ }
+ }
+
+ focusItem(item) {
+ const { focusable = true, onFocus } = this.props;
+
+ if (focusable && this.focusedItem !== item) {
+ this.focusedItem = item;
+ this.forceUpdate();
+
+ if (onFocus) {
+ onFocus(item);
+ }
+ }
+ }
+
+ activateItem(item) {
+ const { focusable = true, onActivate } = this.props;
+
+ if (focusable && this.activeItem !== item) {
+ this.activeItem = item;
+ this.forceUpdate();
+
+ if (onActivate) {
+ onActivate(item);
+ }
+ }
+ }
+
+ shouldItemUpdate(prevItem, nextItem) {
+ const value = getValue(nextItem);
+ // Long string should always update because fullText loading will not
+ // trigger item re-render.
+ return value && value.type === "longString";
+ }
+
+ render() {
+ const {
+ autoExpandAll = true,
+ autoExpandDepth = 1,
+ initiallyExpanded,
+ focusable = true,
+ disableWrap = false,
+ expandedPaths,
+ inline,
+ displayRootNodeAsHeader = false,
+ } = this.props;
+
+ const classNames = ["object-inspector"];
+ if (inline) {
+ classNames.push("inline");
+ }
+ if (disableWrap) {
+ classNames.push("nowrap");
+ }
+ if (displayRootNodeAsHeader) {
+ classNames.push("header-root-node");
+ }
+
+ return Tree({
+ className: classNames.join(" "),
+
+ autoExpandAll,
+ autoExpandDepth,
+ initiallyExpanded,
+ isExpanded: item => expandedPaths && expandedPaths.has(item.path),
+ isExpandable: this.isNodeExpandable,
+ focused: this.focusedItem,
+ active: this.activeItem,
+
+ getRoots: this.getRoots,
+ getParent,
+ getChildren: this.getItemChildren,
+ getKey: this.getNodeKey,
+
+ onExpand: item => this.setExpanded(item, true),
+ onCollapse: item => this.setExpanded(item, false),
+ onFocus: focusable ? this.focusItem : null,
+ onActivate: focusable ? this.activateItem : null,
+
+ shouldItemUpdate: this.shouldItemUpdate,
+ renderItem: (item, depth, focused, arrow, expanded) =>
+ ObjectInspectorItem({
+ ...this.props,
+ item,
+ depth,
+ focused,
+ arrow,
+ mode: displayRootNodeAsHeader && this.props.roots[0] == item ? MODE.HEADER : this.props.mode ,
+ expanded,
+ setExpanded: this.setExpanded,
+ }),
+ });
+ }
+}
+
+function mapStateToProps(state, props) {
+ return {
+ expandedPaths: getExpandedPaths(state),
+ loadedProperties: getLoadedProperties(state),
+ evaluations: getEvaluations(state),
+ };
+}
+
+const OI = connect(mapStateToProps, actions)(ObjectInspector);
+
+module.exports = props => {
+ const { roots, standalone = false } = props;
+
+ if (roots.length == 0) {
+ return null;
+ }
+
+ if (shouldRenderRootsInReps(roots, props)) {
+ return renderRep(roots[0], props);
+ }
+
+ const oiElement = createElement(OI, props);
+
+ if (!standalone) {
+ return oiElement;
+ }
+
+ const store = createStore(reducer);
+ return createElement(Provider, { store }, oiElement);
+};
diff --git a/devtools/client/shared/components/object-inspector/components/ObjectInspectorItem.js b/devtools/client/shared/components/object-inspector/components/ObjectInspectorItem.js
new file mode 100644
index 0000000000..4fce30b726
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/components/ObjectInspectorItem.js
@@ -0,0 +1,285 @@
+/* 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/>. */
+
+const { Component } = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const isMacOS = Services.appinfo.OS === "Darwin";
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+
+const {
+ getValue,
+ nodeHasAccessors,
+ nodeHasProperties,
+ nodeIsBlock,
+ nodeIsDefaultProperties,
+ nodeIsFunction,
+ nodeIsGetter,
+ nodeIsMapEntry,
+ nodeIsMissingArguments,
+ nodeIsOptimizedOut,
+ nodeIsPrimitive,
+ nodeIsPrototype,
+ nodeIsSetter,
+ nodeIsUninitializedBinding,
+ nodeIsUnmappedBinding,
+ nodeIsUnscopedBinding,
+ nodeIsWindow,
+ nodeIsLongString,
+ nodeHasFullText,
+ nodeHasGetter,
+ getNonPrototypeParentGripValue,
+} = Utils.node;
+
+class ObjectInspectorItem extends Component {
+ static get defaultProps() {
+ return {
+ onContextMenu: () => {},
+ renderItemActions: () => null,
+ };
+ }
+
+ // eslint-disable-next-line complexity
+ getLabelAndValue() {
+ const { item, depth, expanded, mode } = this.props;
+
+ const label = item.name;
+ const isPrimitive = nodeIsPrimitive(item);
+
+ if (nodeIsOptimizedOut(item)) {
+ return {
+ label,
+ value: dom.span({ className: "unavailable" }, "(optimized away)"),
+ };
+ }
+
+ if (nodeIsUninitializedBinding(item)) {
+ return {
+ label,
+ value: dom.span({ className: "unavailable" }, "(uninitialized)"),
+ };
+ }
+
+ if (nodeIsUnmappedBinding(item)) {
+ return {
+ label,
+ value: dom.span({ className: "unavailable" }, "(unmapped)"),
+ };
+ }
+
+ if (nodeIsUnscopedBinding(item)) {
+ return {
+ label,
+ value: dom.span({ className: "unavailable" }, "(unscoped)"),
+ };
+ }
+
+ const itemValue = getValue(item);
+ const unavailable =
+ isPrimitive &&
+ itemValue &&
+ itemValue.hasOwnProperty &&
+ itemValue.hasOwnProperty("unavailable");
+
+ if (nodeIsMissingArguments(item) || unavailable) {
+ return {
+ label,
+ value: dom.span({ className: "unavailable" }, "(unavailable)"),
+ };
+ }
+
+ if (
+ nodeIsFunction(item) &&
+ !nodeIsGetter(item) &&
+ !nodeIsSetter(item) &&
+ (mode === MODE.TINY || !mode)
+ ) {
+ return {
+ label: Utils.renderRep(item, {
+ ...this.props,
+ functionName: label,
+ }),
+ };
+ }
+
+ if (
+ nodeHasProperties(item) ||
+ nodeHasAccessors(item) ||
+ nodeIsMapEntry(item) ||
+ nodeIsLongString(item) ||
+ isPrimitive
+ ) {
+ const repProps = { ...this.props };
+ if (depth > 0) {
+ repProps.mode = mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
+ }
+
+
+ if (nodeIsLongString(item)) {
+ repProps.member = {
+ open: nodeHasFullText(item) && expanded,
+ };
+ }
+
+ if (nodeHasGetter(item)) {
+ const receiverGrip = getNonPrototypeParentGripValue(item);
+ if (receiverGrip) {
+ Object.assign(repProps, {
+ onInvokeGetterButtonClick: () =>
+ this.props.invokeGetter(item, receiverGrip.actor),
+ });
+ }
+ }
+
+ return {
+ label,
+ value: Utils.renderRep(item, repProps),
+ };
+ }
+
+ return {
+ label,
+ };
+ }
+
+ getTreeItemProps() {
+ const {
+ item,
+ depth,
+ focused,
+ expanded,
+ onCmdCtrlClick,
+ onDoubleClick,
+ dimTopLevelWindow,
+ onContextMenu,
+ } = this.props;
+
+ const classNames = ["node", "object-node"];
+ if (focused) {
+ classNames.push("focused");
+ }
+
+ if (nodeIsBlock(item)) {
+ classNames.push("block");
+ }
+
+ if (
+ !expanded &&
+ (nodeIsDefaultProperties(item) ||
+ nodeIsPrototype(item) ||
+ nodeIsGetter(item) ||
+ nodeIsSetter(item) ||
+ (dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0))
+ ) {
+ classNames.push("lessen");
+ }
+
+ const parentElementProps = {
+ className: classNames.join(" "),
+ onClick: e => {
+ if (
+ onCmdCtrlClick &&
+ ((isMacOS && e.metaKey) || (!isMacOS && e.ctrlKey))
+ ) {
+ onCmdCtrlClick(item, {
+ depth,
+ event: e,
+ focused,
+ expanded,
+ });
+ e.stopPropagation();
+ return;
+ }
+
+ // If this click happened because the user selected some text, bail out.
+ // Note that if the user selected some text before and then clicks here,
+ // the previously selected text will be first unselected, unless the
+ // user clicked on the arrow itself. Indeed because the arrow is an
+ // image, clicking on it does not remove any existing text selection.
+ // So we need to also check if the arrow was clicked.
+ if (
+ e.target &&
+ Utils.selection.documentHasSelection(e.target.ownerDocument) &&
+ !(e.target.matches && e.target.matches(".arrow"))
+ ) {
+ e.stopPropagation();
+ }
+ },
+ onContextMenu: e => onContextMenu(e, item),
+ };
+
+ if (onDoubleClick) {
+ parentElementProps.onDoubleClick = e => {
+ e.stopPropagation();
+ onDoubleClick(item, {
+ depth,
+ focused,
+ expanded,
+ });
+ };
+ }
+
+ return parentElementProps;
+ }
+
+ renderLabel(label) {
+ if (label === null || typeof label === "undefined") {
+ return null;
+ }
+
+ const { item, depth, focused, expanded, onLabelClick } = this.props;
+ return dom.span(
+ {
+ className: "object-label",
+ onClick: onLabelClick
+ ? event => {
+ event.stopPropagation();
+
+ // If the user selected text, bail out.
+ if (
+ Utils.selection.documentHasSelection(event.target.ownerDocument)
+ ) {
+ return;
+ }
+
+ onLabelClick(item, {
+ depth,
+ focused,
+ expanded,
+ setExpanded: this.props.setExpanded,
+ });
+ }
+ : undefined,
+ },
+ label
+ );
+ }
+
+ render() {
+ const { arrow, renderItemActions, item } = this.props;
+
+ const { label, value } = this.getLabelAndValue();
+ const labelElement = this.renderLabel(label);
+ const delimiter =
+ value && labelElement
+ ? dom.span({ className: "object-delimiter" }, ": ")
+ : null;
+
+ return dom.div(
+ this.getTreeItemProps(),
+ this.props.mode === MODE.HEADER ? null : arrow,
+ labelElement,
+ delimiter,
+ value,
+ renderItemActions(item)
+ );
+ }
+}
+
+module.exports = ObjectInspectorItem;
diff --git a/devtools/client/shared/components/object-inspector/components/moz.build b/devtools/client/shared/components/object-inspector/components/moz.build
new file mode 100644
index 0000000000..a1744891f2
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/components/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "ObjectInspector.js",
+ "ObjectInspectorItem.js",
+)
diff --git a/devtools/client/shared/components/object-inspector/index.js b/devtools/client/shared/components/object-inspector/index.js
new file mode 100644
index 0000000000..34e4d30086
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/index.js
@@ -0,0 +1,10 @@
+/* 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/>. */
+
+const ObjectInspector = require("resource://devtools/client/shared/components/object-inspector/components/ObjectInspector.js");
+const utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const reducer = require("resource://devtools/client/shared/components/object-inspector/reducer.js");
+const actions = require("resource://devtools/client/shared/components/object-inspector/actions.js");
+
+module.exports = { ObjectInspector, utils, actions, reducer };
diff --git a/devtools/client/shared/components/object-inspector/moz.build b/devtools/client/shared/components/object-inspector/moz.build
new file mode 100644
index 0000000000..14f9c285ba
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/moz.build
@@ -0,0 +1,16 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "components",
+ "utils",
+]
+
+DevToolsModules(
+ "actions.js",
+ "index.js",
+ "reducer.js",
+)
diff --git a/devtools/client/shared/components/object-inspector/reducer.js b/devtools/client/shared/components/object-inspector/reducer.js
new file mode 100644
index 0000000000..aa8af2b529
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/reducer.js
@@ -0,0 +1,147 @@
+/* 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/>. */
+
+function initialOIState(overrides) {
+ return {
+ expandedPaths: new Set(),
+ loadedProperties: new Map(),
+ evaluations: new Map(),
+ watchpoints: new Map(),
+ ...overrides,
+ };
+}
+
+function reducer(state = initialOIState(), action = {}) {
+ const { type, data } = action;
+
+ const cloneState = overrides => ({ ...state, ...overrides });
+
+ if (type === "NODE_EXPAND") {
+ return cloneState({
+ expandedPaths: new Set(state.expandedPaths).add(data.node.path),
+ });
+ }
+
+ if (type === "NODE_COLLAPSE") {
+ const expandedPaths = new Set(state.expandedPaths);
+ expandedPaths.delete(data.node.path);
+ return cloneState({ expandedPaths });
+ }
+
+ if (type == "SET_WATCHPOINT") {
+ const { watchpoint, property, path } = data;
+ const obj = state.loadedProperties.get(path);
+
+ return cloneState({
+ loadedProperties: new Map(state.loadedProperties).set(
+ path,
+ updateObject(obj, property, watchpoint)
+ ),
+ watchpoints: new Map(state.watchpoints).set(data.actor, data.watchpoint),
+ });
+ }
+
+ if (type === "REMOVE_WATCHPOINT") {
+ const { path, property, actor } = data;
+ const obj = state.loadedProperties.get(path);
+ const watchpoints = new Map(state.watchpoints);
+ watchpoints.delete(actor);
+
+ return cloneState({
+ loadedProperties: new Map(state.loadedProperties).set(
+ path,
+ updateObject(obj, property, null)
+ ),
+ watchpoints: watchpoints,
+ });
+ }
+
+ if (type === "NODE_PROPERTIES_LOADED") {
+ return cloneState({
+ loadedProperties: new Map(state.loadedProperties).set(
+ data.node.path,
+ action.data.properties
+ ),
+ });
+ }
+
+ if (type === "ROOTS_CHANGED") {
+ return cloneState();
+ }
+
+ if (type === "GETTER_INVOKED") {
+ return cloneState({
+ evaluations: new Map(state.evaluations).set(data.node.path, {
+ getterValue:
+ data.result &&
+ data.result.value &&
+ (data.result.value.throw || data.result.value.return),
+ }),
+ });
+ }
+
+ // NOTE: we clear the state on resume because otherwise the scopes pane
+ // would be out of date. Bug 1514760
+ if (type === "RESUME" || type == "NAVIGATE") {
+ return initialOIState({ watchpoints: state.watchpoints });
+ }
+
+ return state;
+}
+
+function updateObject(obj, property, watchpoint) {
+ return {
+ ...obj,
+ ownProperties: {
+ ...obj.ownProperties,
+ [property]: {
+ ...obj.ownProperties[property],
+ watchpoint,
+ },
+ },
+ };
+}
+
+function getObjectInspectorState(state) {
+ return state.objectInspector || state;
+}
+
+function getExpandedPaths(state) {
+ return getObjectInspectorState(state).expandedPaths;
+}
+
+function getExpandedPathKeys(state) {
+ return [...getExpandedPaths(state).keys()];
+}
+
+function getWatchpoints(state) {
+ return getObjectInspectorState(state).watchpoints;
+}
+
+function getLoadedProperties(state) {
+ return getObjectInspectorState(state).loadedProperties;
+}
+
+function getLoadedPropertyKeys(state) {
+ return [...getLoadedProperties(state).keys()];
+}
+
+function getEvaluations(state) {
+ return getObjectInspectorState(state).evaluations;
+}
+
+const selectors = {
+ getWatchpoints,
+ getEvaluations,
+ getExpandedPathKeys,
+ getExpandedPaths,
+ getLoadedProperties,
+ getLoadedPropertyKeys,
+};
+
+Object.defineProperty(module.exports, "__esModule", {
+ value: true,
+});
+module.exports = { ...selectors, initialOIState };
+module.exports.default = reducer;
diff --git a/devtools/client/shared/components/object-inspector/utils/client.js b/devtools/client/shared/components/object-inspector/utils/client.js
new file mode 100644
index 0000000000..eaa42be05a
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/utils/client.js
@@ -0,0 +1,124 @@
+/* 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/>. */
+
+const {
+ getValue,
+ nodeHasFullText,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+async function enumIndexedProperties(objectFront, start, end) {
+ try {
+ const iterator = await objectFront.enumProperties({
+ ignoreNonIndexedProperties: true,
+ });
+ const response = await iteratorSlice(iterator, start, end);
+ return response;
+ } catch (e) {
+ console.error("Error in enumIndexedProperties", e);
+ return {};
+ }
+}
+
+async function enumNonIndexedProperties(objectFront, start, end) {
+ try {
+ const iterator = await objectFront.enumProperties({
+ ignoreIndexedProperties: true,
+ });
+ const response = await iteratorSlice(iterator, start, end);
+ return response;
+ } catch (e) {
+ console.error("Error in enumNonIndexedProperties", e);
+ return {};
+ }
+}
+
+async function enumEntries(objectFront, start, end) {
+ try {
+ const iterator = await objectFront.enumEntries();
+ const response = await iteratorSlice(iterator, start, end);
+ return response;
+ } catch (e) {
+ console.error("Error in enumEntries", e);
+ return {};
+ }
+}
+
+async function enumSymbols(objectFront, start, end) {
+ try {
+ const iterator = await objectFront.enumSymbols();
+ const response = await iteratorSlice(iterator, start, end);
+ return response;
+ } catch (e) {
+ console.error("Error in enumSymbols", e);
+ return {};
+ }
+}
+
+async function enumPrivateProperties(objectFront, start, end) {
+ try {
+ const iterator = await objectFront.enumPrivateProperties();
+ const response = await iteratorSlice(iterator, start, end);
+ return response;
+ } catch (e) {
+ console.error("Error in enumPrivateProperties", e);
+ return {};
+ }
+}
+
+async function getPrototype(objectFront) {
+ if (typeof objectFront.getPrototype !== "function") {
+ console.error("objectFront.getPrototype is not a function");
+ return Promise.resolve({});
+ }
+ return objectFront.getPrototype();
+}
+
+async function getFullText(longStringFront, item) {
+ const { initial, fullText, length } = getValue(item);
+ // Return fullText property if it exists so that it can be added to the
+ // loadedProperties map.
+ if (nodeHasFullText(item)) {
+ return { fullText };
+ }
+
+ try {
+ const substring = await longStringFront.substring(initial.length, length);
+ return {
+ fullText: initial + substring,
+ };
+ } catch (e) {
+ console.error("LongStringFront.substring", e);
+ throw e;
+ }
+}
+
+async function getPromiseState(objectFront) {
+ return objectFront.getPromiseState();
+}
+
+async function getProxySlots(objectFront) {
+ return objectFront.getProxySlots();
+}
+
+function iteratorSlice(iterator, start, end) {
+ start = start || 0;
+ const count = end ? end - start + 1 : iterator.count;
+
+ if (count === 0) {
+ return Promise.resolve({});
+ }
+ return iterator.slice(start, count);
+}
+
+module.exports = {
+ enumEntries,
+ enumIndexedProperties,
+ enumNonIndexedProperties,
+ enumPrivateProperties,
+ enumSymbols,
+ getPrototype,
+ getFullText,
+ getPromiseState,
+ getProxySlots,
+};
diff --git a/devtools/client/shared/components/object-inspector/utils/index.js b/devtools/client/shared/components/object-inspector/utils/index.js
new file mode 100644
index 0000000000..13b3fd0049
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/utils/index.js
@@ -0,0 +1,52 @@
+/* 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/>. */
+
+const client = require("resource://devtools/client/shared/components/object-inspector/utils/client.js");
+const loadProperties = require("resource://devtools/client/shared/components/object-inspector/utils/load-properties.js");
+const node = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+const { nodeIsError, nodeIsPrimitive } = node;
+const selection = require("resource://devtools/client/shared/components/object-inspector/utils/selection.js");
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ REPS: { Rep, Grip },
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+function shouldRenderRootsInReps(roots, props = {}) {
+ if (roots.length !== 1) {
+ return false;
+ }
+
+ const root = roots[0];
+ const name = root && root.name;
+
+ return (
+ (name === null || typeof name === "undefined") &&
+ (nodeIsPrimitive(root) ||
+ (root?.contents?.value?.useCustomFormatter === true &&
+ Array.isArray(root?.contents?.value?.header)) ||
+ (nodeIsError(root) && props?.customFormat === true))
+ );
+}
+
+function renderRep(item, props) {
+ return Rep({
+ ...props,
+ front: item.contents.front,
+ object: node.getValue(item),
+ mode: props.mode || MODE.TINY,
+ defaultRep: Grip,
+ });
+}
+
+module.exports = {
+ client,
+ loadProperties,
+ node,
+ renderRep,
+ selection,
+ shouldRenderRootsInReps,
+};
diff --git a/devtools/client/shared/components/object-inspector/utils/load-properties.js b/devtools/client/shared/components/object-inspector/utils/load-properties.js
new file mode 100644
index 0000000000..42525e54f1
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/utils/load-properties.js
@@ -0,0 +1,260 @@
+/* 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/>. */
+
+const {
+ enumEntries,
+ enumIndexedProperties,
+ enumNonIndexedProperties,
+ enumPrivateProperties,
+ enumSymbols,
+ getPrototype,
+ getFullText,
+ getPromiseState,
+ getProxySlots,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/client.js");
+
+const {
+ getClosestGripNode,
+ getClosestNonBucketNode,
+ getFront,
+ getValue,
+ nodeHasAccessors,
+ nodeHasProperties,
+ nodeIsBucket,
+ nodeIsDefaultProperties,
+ nodeIsEntries,
+ nodeIsMapEntry,
+ nodeIsPrimitive,
+ nodeIsPromise,
+ nodeIsProxy,
+ nodeNeedsNumericalBuckets,
+ nodeIsLongString,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+function loadItemProperties(item, client, loadedProperties, threadActorID) {
+ const gripItem = getClosestGripNode(item);
+ const value = getValue(gripItem);
+ let front = getFront(gripItem);
+
+ if (!front && value && client && client.getFrontByID) {
+ front = client.getFrontByID(value.actor);
+ }
+
+ const getObjectFront = function() {
+ if (!front) {
+ front = client.createObjectFront(
+ value,
+ client.getFrontByID(threadActorID)
+ );
+ }
+
+ return front;
+ };
+
+ const [start, end] = item.meta
+ ? [item.meta.startIndex, item.meta.endIndex]
+ : [];
+
+ const promises = [];
+
+ if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
+ promises.push(enumIndexedProperties(getObjectFront(), start, end));
+ }
+
+ if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
+ promises.push(enumNonIndexedProperties(getObjectFront(), start, end));
+ }
+
+ if (shouldLoadItemEntries(item, loadedProperties)) {
+ promises.push(enumEntries(getObjectFront(), start, end));
+ }
+
+ if (shouldLoadItemPrototype(item, loadedProperties)) {
+ promises.push(getPrototype(getObjectFront()));
+ }
+
+ if (shouldLoadItemPrivateProperties(item, loadedProperties)) {
+ promises.push(enumPrivateProperties(getObjectFront(), start, end));
+ }
+
+ if (shouldLoadItemSymbols(item, loadedProperties)) {
+ promises.push(enumSymbols(getObjectFront(), start, end));
+ }
+
+ if (shouldLoadItemFullText(item, loadedProperties)) {
+ const longStringFront = front || client.createLongStringFront(value);
+ promises.push(getFullText(longStringFront, item));
+ }
+
+ if (shouldLoadItemPromiseState(item, loadedProperties)) {
+ promises.push(getPromiseState(getObjectFront()));
+ }
+
+ if (shouldLoadItemProxySlots(item, loadedProperties)) {
+ promises.push(getProxySlots(getObjectFront()));
+ }
+
+ return Promise.all(promises).then(mergeResponses);
+}
+
+function mergeResponses(responses) {
+ const data = {};
+
+ for (const response of responses) {
+ if (response.hasOwnProperty("ownProperties")) {
+ data.ownProperties = { ...data.ownProperties, ...response.ownProperties };
+ }
+
+ if (response.privateProperties && response.privateProperties.length > 0) {
+ data.privateProperties = response.privateProperties;
+ }
+
+ if (response.ownSymbols && response.ownSymbols.length > 0) {
+ data.ownSymbols = response.ownSymbols;
+ }
+
+ if (response.prototype) {
+ data.prototype = response.prototype;
+ }
+
+ if (response.fullText) {
+ data.fullText = response.fullText;
+ }
+
+ if (response.promiseState) {
+ data.promiseState = response.promiseState;
+ }
+
+ if (response.proxyTarget && response.proxyHandler) {
+ data.proxyTarget = response.proxyTarget;
+ data.proxyHandler = response.proxyHandler;
+ }
+ }
+
+ return data;
+}
+
+function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
+ const gripItem = getClosestGripNode(item);
+ const value = getValue(gripItem);
+
+ return (
+ value &&
+ nodeHasProperties(gripItem) &&
+ !loadedProperties.has(item.path) &&
+ !nodeIsProxy(item) &&
+ !nodeNeedsNumericalBuckets(item) &&
+ !nodeIsEntries(getClosestNonBucketNode(item)) &&
+ // The data is loaded when expanding the window node.
+ !nodeIsDefaultProperties(item)
+ );
+}
+
+function shouldLoadItemNonIndexedProperties(
+ item,
+ loadedProperties = new Map()
+) {
+ const gripItem = getClosestGripNode(item);
+ const value = getValue(gripItem);
+
+ return (
+ value &&
+ nodeHasProperties(gripItem) &&
+ !loadedProperties.has(item.path) &&
+ !nodeIsProxy(item) &&
+ !nodeIsEntries(getClosestNonBucketNode(item)) &&
+ !nodeIsBucket(item) &&
+ // The data is loaded when expanding the window node.
+ !nodeIsDefaultProperties(item)
+ );
+}
+
+function shouldLoadItemEntries(item, loadedProperties = new Map()) {
+ const gripItem = getClosestGripNode(item);
+ const value = getValue(gripItem);
+
+ return (
+ value &&
+ nodeIsEntries(getClosestNonBucketNode(item)) &&
+ !loadedProperties.has(item.path) &&
+ !nodeNeedsNumericalBuckets(item)
+ );
+}
+
+function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
+ const value = getValue(item);
+
+ return (
+ value &&
+ !loadedProperties.has(item.path) &&
+ !nodeIsBucket(item) &&
+ !nodeIsMapEntry(item) &&
+ !nodeIsEntries(item) &&
+ !nodeIsDefaultProperties(item) &&
+ !nodeHasAccessors(item) &&
+ !nodeIsPrimitive(item) &&
+ !nodeIsLongString(item) &&
+ !nodeIsProxy(item)
+ );
+}
+
+function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
+ const value = getValue(item);
+
+ return (
+ value &&
+ !loadedProperties.has(item.path) &&
+ !nodeIsBucket(item) &&
+ !nodeIsMapEntry(item) &&
+ !nodeIsEntries(item) &&
+ !nodeIsDefaultProperties(item) &&
+ !nodeHasAccessors(item) &&
+ !nodeIsPrimitive(item) &&
+ !nodeIsLongString(item) &&
+ !nodeIsProxy(item)
+ );
+}
+
+function shouldLoadItemPrivateProperties(item, loadedProperties = new Map()) {
+ const value = getValue(item);
+
+ return (
+ value &&
+ value?.preview?.privatePropertiesLength &&
+ !loadedProperties.has(item.path) &&
+ !nodeIsBucket(item) &&
+ !nodeIsMapEntry(item) &&
+ !nodeIsEntries(item) &&
+ !nodeIsDefaultProperties(item) &&
+ !nodeHasAccessors(item) &&
+ !nodeIsPrimitive(item) &&
+ !nodeIsLongString(item) &&
+ !nodeIsProxy(item)
+ );
+}
+
+function shouldLoadItemFullText(item, loadedProperties = new Map()) {
+ return !loadedProperties.has(item.path) && nodeIsLongString(item);
+}
+
+function shouldLoadItemPromiseState(item, loadedProperties = new Map()) {
+ return !loadedProperties.has(item.path) && nodeIsPromise(item);
+}
+
+function shouldLoadItemProxySlots(item, loadedProperties = new Map()) {
+ return !loadedProperties.has(item.path) && nodeIsProxy(item);
+}
+
+module.exports = {
+ loadItemProperties,
+ mergeResponses,
+ shouldLoadItemEntries,
+ shouldLoadItemIndexedProperties,
+ shouldLoadItemNonIndexedProperties,
+ shouldLoadItemPrototype,
+ shouldLoadItemSymbols,
+ shouldLoadItemFullText,
+ shouldLoadItemPromiseState,
+ shouldLoadItemProxySlots,
+};
diff --git a/devtools/client/shared/components/object-inspector/utils/moz.build b/devtools/client/shared/components/object-inspector/utils/moz.build
new file mode 100644
index 0000000000..1301b2aca6
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/utils/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "client.js",
+ "index.js",
+ "load-properties.js",
+ "node.js",
+ "selection.js",
+)
diff --git a/devtools/client/shared/components/object-inspector/utils/node.js b/devtools/client/shared/components/object-inspector/utils/node.js
new file mode 100644
index 0000000000..1ee0255d1d
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/utils/node.js
@@ -0,0 +1,1059 @@
+/* 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/>. */
+
+const {
+ maybeEscapePropertyName,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+const ArrayRep = require("resource://devtools/client/shared/components/reps/reps/array.js");
+const GripArrayRep = require("resource://devtools/client/shared/components/reps/reps/grip-array.js");
+const GripMap = require("resource://devtools/client/shared/components/reps/reps/grip-map.js");
+const GripEntryRep = require("resource://devtools/client/shared/components/reps/reps/grip-entry.js");
+const ErrorRep = require("resource://devtools/client/shared/components/reps/reps/error.js");
+const BigIntRep = require("resource://devtools/client/shared/components/reps/reps/big-int.js");
+const {
+ isLongString,
+} = require("resource://devtools/client/shared/components/reps/reps/string.js");
+
+const MAX_NUMERICAL_PROPERTIES = 100;
+
+const NODE_TYPES = {
+ BUCKET: Symbol("[n…m]"),
+ DEFAULT_PROPERTIES: Symbol("<default properties>"),
+ ENTRIES: Symbol("<entries>"),
+ GET: Symbol("<get>"),
+ GRIP: Symbol("GRIP"),
+ MAP_ENTRY_KEY: Symbol("<key>"),
+ MAP_ENTRY_VALUE: Symbol("<value>"),
+ PROMISE_REASON: Symbol("<reason>"),
+ PROMISE_STATE: Symbol("<state>"),
+ PROMISE_VALUE: Symbol("<value>"),
+ PROXY_HANDLER: Symbol("<handler>"),
+ PROXY_TARGET: Symbol("<target>"),
+ SET: Symbol("<set>"),
+ PROTOTYPE: Symbol("<prototype>"),
+ BLOCK: Symbol("☲"),
+ PRIMITIVE_VALUE: Symbol("<primitive value>")
+};
+
+let WINDOW_PROPERTIES = {};
+
+if (typeof window === "object") {
+ WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
+}
+
+function getType(item) {
+ return item.type;
+}
+
+function getValue(item) {
+ if (nodeHasValue(item)) {
+ return item.contents.value;
+ }
+
+ if (nodeHasGetterValue(item)) {
+ return item.contents.getterValue;
+ }
+
+ if (nodeHasAccessors(item)) {
+ return item.contents;
+ }
+
+ return undefined;
+}
+
+function getFront(item) {
+ return item && item.contents && item.contents.front;
+}
+
+function getActor(item, roots) {
+ const isRoot = isNodeRoot(item, roots);
+ const value = getValue(item);
+ return isRoot || !value ? null : value.actor;
+}
+
+function isNodeRoot(item, roots) {
+ const gripItem = getClosestGripNode(item);
+ const value = getValue(gripItem);
+
+ return (
+ value &&
+ roots.some(root => {
+ const rootValue = getValue(root);
+ return rootValue && rootValue.actor === value.actor;
+ })
+ );
+}
+
+function nodeIsBucket(item) {
+ return getType(item) === NODE_TYPES.BUCKET;
+}
+
+function nodeIsEntries(item) {
+ return getType(item) === NODE_TYPES.ENTRIES;
+}
+
+function nodeIsMapEntry(item) {
+ return GripEntryRep.supportsObject(getValue(item));
+}
+
+function nodeHasChildren(item) {
+ return Array.isArray(item.contents);
+}
+
+function nodeHasValue(item) {
+ return item && item.contents && item.contents.hasOwnProperty("value");
+}
+
+function nodeHasGetterValue(item) {
+ return item && item.contents && item.contents.hasOwnProperty("getterValue");
+}
+
+function nodeIsObject(item) {
+ const value = getValue(item);
+ return value && value.type === "object";
+}
+
+function nodeIsArrayLike(item) {
+ const value = getValue(item);
+ return GripArrayRep.supportsObject(value) || ArrayRep.supportsObject(value);
+}
+
+function nodeIsFunction(item) {
+ const value = getValue(item);
+ return value && value.class === "Function";
+}
+
+function nodeIsOptimizedOut(item) {
+ const value = getValue(item);
+ return !nodeHasChildren(item) && value && value.optimizedOut;
+}
+
+function nodeIsUninitializedBinding(item) {
+ const value = getValue(item);
+ return value && value.uninitialized;
+}
+
+// Used to check if an item represents a binding that exists in a sourcemap's
+// original file content, but does not match up with a binding found in the
+// generated code.
+function nodeIsUnmappedBinding(item) {
+ const value = getValue(item);
+ return value && value.unmapped;
+}
+
+// Used to check if an item represents a binding that exists in the debugger's
+// parser result, but does not match up with a binding returned by the
+// devtools server.
+function nodeIsUnscopedBinding(item) {
+ const value = getValue(item);
+ return value && value.unscoped;
+}
+
+function nodeIsMissingArguments(item) {
+ const value = getValue(item);
+ return !nodeHasChildren(item) && value && value.missingArguments;
+}
+
+function nodeHasProperties(item) {
+ return !nodeHasChildren(item) && nodeIsObject(item);
+}
+
+function nodeIsPrimitive(item) {
+ return (
+ nodeIsBigInt(item) ||
+ (!nodeHasChildren(item) &&
+ !nodeHasProperties(item) &&
+ !nodeIsEntries(item) &&
+ !nodeIsMapEntry(item) &&
+ !nodeHasAccessors(item) &&
+ !nodeIsBucket(item) &&
+ !nodeIsLongString(item))
+ );
+}
+
+function nodeIsDefaultProperties(item) {
+ return getType(item) === NODE_TYPES.DEFAULT_PROPERTIES;
+}
+
+function isDefaultWindowProperty(name) {
+ return WINDOW_PROPERTIES.includes(name);
+}
+
+function nodeIsPromise(item) {
+ const value = getValue(item);
+ if (!value) {
+ return false;
+ }
+
+ return value.class == "Promise";
+}
+
+function nodeIsProxy(item) {
+ const value = getValue(item);
+ if (!value) {
+ return false;
+ }
+
+ return value.class == "Proxy";
+}
+
+function nodeIsPrototype(item) {
+ return getType(item) === NODE_TYPES.PROTOTYPE;
+}
+
+function nodeIsWindow(item) {
+ const value = getValue(item);
+ if (!value) {
+ return false;
+ }
+
+ return value.class == "Window";
+}
+
+function nodeIsGetter(item) {
+ return getType(item) === NODE_TYPES.GET;
+}
+
+function nodeIsSetter(item) {
+ return getType(item) === NODE_TYPES.SET;
+}
+
+function nodeIsBlock(item) {
+ return getType(item) === NODE_TYPES.BLOCK;
+}
+
+function nodeIsError(item) {
+ return ErrorRep.supportsObject(getValue(item));
+}
+
+function nodeIsLongString(item) {
+ return isLongString(getValue(item));
+}
+
+function nodeIsBigInt(item) {
+ return BigIntRep.supportsObject(getValue(item));
+}
+
+function nodeHasFullText(item) {
+ const value = getValue(item);
+ return nodeIsLongString(item) && value.hasOwnProperty("fullText");
+}
+
+function nodeHasGetter(item) {
+ const getter = getNodeGetter(item);
+ return getter && getter.type !== "undefined";
+}
+
+function nodeHasSetter(item) {
+ const setter = getNodeSetter(item);
+ return setter && setter.type !== "undefined";
+}
+
+function nodeHasAccessors(item) {
+ return nodeHasGetter(item) || nodeHasSetter(item);
+}
+
+function nodeSupportsNumericalBucketing(item) {
+ // We exclude elements with entries since it's the <entries> node
+ // itself that can have buckets.
+ return (
+ (nodeIsArrayLike(item) && !nodeHasEntries(item)) ||
+ nodeIsEntries(item) ||
+ nodeIsBucket(item)
+ );
+}
+
+function nodeHasEntries(item) {
+ const value = getValue(item);
+ if (!value) {
+ return false;
+ }
+
+ const className = value.class;
+ return (
+ className === "Map" ||
+ className === "Set" ||
+ className === "WeakMap" ||
+ className === "WeakSet" ||
+ className === "Storage" ||
+ className === "URLSearchParams" ||
+ className === "Headers" ||
+ className === "FormData" ||
+ className === "MIDIInputMap" ||
+ className === "MIDIOutputMap" ||
+ className === "HighlightRegistry"
+ );
+}
+
+function nodeNeedsNumericalBuckets(item) {
+ return (
+ nodeSupportsNumericalBucketing(item) &&
+ getNumericalPropertiesCount(item) > MAX_NUMERICAL_PROPERTIES
+ );
+}
+
+function makeNodesForPromiseProperties(loadedProps, item) {
+ const { reason, value, state } = loadedProps.promiseState;
+ const properties = [];
+
+ if (state) {
+ properties.push(
+ createNode({
+ parent: item,
+ name: "<state>",
+ contents: { value: state },
+ type: NODE_TYPES.PROMISE_STATE,
+ })
+ );
+ }
+
+ if (reason) {
+ properties.push(
+ createNode({
+ parent: item,
+ name: "<reason>",
+ contents: {
+ value: reason.getGrip ? reason.getGrip() : reason,
+ front: reason.getGrip ? reason : null,
+ },
+ type: NODE_TYPES.PROMISE_REASON,
+ })
+ );
+ }
+
+ if (value) {
+ properties.push(
+ createNode({
+ parent: item,
+ name: "<value>",
+ contents: {
+ value: value.getGrip ? value.getGrip() : value,
+ front: value.getGrip ? value : null,
+ },
+ type: NODE_TYPES.PROMISE_VALUE,
+ })
+ );
+ }
+
+ return properties;
+}
+
+function makeNodesForProxyProperties(loadedProps, item) {
+ const { proxyHandler, proxyTarget } = loadedProps;
+
+ const isProxyHandlerFront = proxyHandler && proxyHandler.getGrip;
+ const proxyHandlerGrip = isProxyHandlerFront
+ ? proxyHandler.getGrip()
+ : proxyHandler;
+ const proxyHandlerFront = isProxyHandlerFront ? proxyHandler : null;
+
+ const isProxyTargetFront = proxyTarget && proxyTarget.getGrip;
+ const proxyTargetGrip = isProxyTargetFront
+ ? proxyTarget.getGrip()
+ : proxyTarget;
+ const proxyTargetFront = isProxyTargetFront ? proxyTarget : null;
+
+ return [
+ createNode({
+ parent: item,
+ name: "<target>",
+ contents: { value: proxyTargetGrip, front: proxyTargetFront },
+ type: NODE_TYPES.PROXY_TARGET,
+ }),
+ createNode({
+ parent: item,
+ name: "<handler>",
+ contents: { value: proxyHandlerGrip, front: proxyHandlerFront },
+ type: NODE_TYPES.PROXY_HANDLER,
+ }),
+ ];
+}
+
+function makeNodesForEntries(item) {
+ const nodeName = "<entries>";
+
+ return createNode({
+ parent: item,
+ name: nodeName,
+ contents: null,
+ type: NODE_TYPES.ENTRIES,
+ });
+}
+
+function makeNodeForPrimitiveValue(parent, value) {
+ const nodeName = "<primitive value>";
+
+ return createNode({
+ parent,
+ name: nodeName,
+ contents: {value},
+ type: NODE_TYPES.PRIMITIVE_VALUE,
+ });
+}
+
+function makeNodesForMapEntry(item) {
+ const nodeValue = getValue(item);
+ if (!nodeValue || !nodeValue.preview) {
+ return [];
+ }
+
+ const { key, value } = nodeValue.preview;
+ const isKeyFront = key && key.getGrip;
+ const keyGrip = isKeyFront ? key.getGrip() : key;
+ const keyFront = isKeyFront ? key : null;
+
+ const isValueFront = value && value.getGrip;
+ const valueGrip = isValueFront ? value.getGrip() : value;
+ const valueFront = isValueFront ? value : null;
+
+ return [
+ createNode({
+ parent: item,
+ name: "<key>",
+ contents: { value: keyGrip, front: keyFront },
+ type: NODE_TYPES.MAP_ENTRY_KEY,
+ }),
+ createNode({
+ parent: item,
+ name: "<value>",
+ contents: { value: valueGrip, front: valueFront },
+ type: NODE_TYPES.MAP_ENTRY_VALUE,
+ }),
+ ];
+}
+
+function getNodeGetter(item) {
+ return item && item.contents ? item.contents.get : undefined;
+}
+
+function getNodeSetter(item) {
+ return item && item.contents ? item.contents.set : undefined;
+}
+
+function sortProperties(properties) {
+ return properties.sort((a, b) => {
+ // Sort numbers in ascending order and sort strings lexicographically
+ const aInt = parseInt(a, 10);
+ const bInt = parseInt(b, 10);
+
+ if (isNaN(aInt) || isNaN(bInt)) {
+ return a > b ? 1 : -1;
+ }
+
+ return aInt - bInt;
+ });
+}
+
+function makeNumericalBuckets(parent) {
+ const numProperties = getNumericalPropertiesCount(parent);
+
+ // We want to have at most a hundred slices.
+ const bucketSize =
+ 10 ** Math.max(2, Math.ceil(Math.log10(numProperties)) - 2);
+ const numBuckets = Math.ceil(numProperties / bucketSize);
+
+ const buckets = [];
+ for (let i = 1; i <= numBuckets; i++) {
+ const minKey = (i - 1) * bucketSize;
+ const maxKey = Math.min(i * bucketSize - 1, numProperties - 1);
+ const startIndex = nodeIsBucket(parent) ? parent.meta.startIndex : 0;
+ const minIndex = startIndex + minKey;
+ const maxIndex = startIndex + maxKey;
+ const bucketName = `[${minIndex}…${maxIndex}]`;
+
+ buckets.push(
+ createNode({
+ parent,
+ name: bucketName,
+ contents: null,
+ type: NODE_TYPES.BUCKET,
+ meta: {
+ startIndex: minIndex,
+ endIndex: maxIndex,
+ },
+ })
+ );
+ }
+ return buckets;
+}
+
+function makeDefaultPropsBucket(propertiesNames, parent, ownProperties) {
+ const userPropertiesNames = [];
+ const defaultProperties = [];
+
+ propertiesNames.forEach(name => {
+ if (isDefaultWindowProperty(name)) {
+ defaultProperties.push(name);
+ } else {
+ userPropertiesNames.push(name);
+ }
+ });
+
+ const nodes = makeNodesForOwnProps(
+ userPropertiesNames,
+ parent,
+ ownProperties
+ );
+
+ if (defaultProperties.length > 0) {
+ const defaultPropertiesNode = createNode({
+ parent,
+ name: "<default properties>",
+ contents: null,
+ type: NODE_TYPES.DEFAULT_PROPERTIES,
+ });
+
+ const defaultNodes = makeNodesForOwnProps(
+ defaultProperties,
+ defaultPropertiesNode,
+ ownProperties
+ );
+ nodes.push(setNodeChildren(defaultPropertiesNode, defaultNodes));
+ }
+ return nodes;
+}
+
+function makeNodesForOwnProps(propertiesNames, parent, ownProperties) {
+ return propertiesNames.map(name => {
+ const property = ownProperties[name];
+
+ let propertyValue = property;
+ if (property && property.hasOwnProperty("getterValue")) {
+ propertyValue = property.getterValue;
+ } else if (property && property.hasOwnProperty("value")) {
+ propertyValue = property.value;
+ }
+
+ // propertyValue can be a front (LongString or Object) or a primitive grip.
+ const isFront = propertyValue && propertyValue.getGrip;
+ const front = isFront ? propertyValue : null;
+ const grip = isFront ? front.getGrip() : propertyValue;
+
+ return createNode({
+ parent,
+ name: maybeEscapePropertyName(name),
+ propertyName: name,
+ contents: {
+ ...(property || {}),
+ value: grip,
+ front,
+ },
+ });
+ });
+}
+
+function makeNodesForProperties(objProps, parent) {
+ const {
+ ownProperties = {},
+ ownSymbols,
+ privateProperties,
+ prototype,
+ safeGetterValues,
+ } = objProps;
+
+ const parentValue = getValue(parent);
+ const allProperties = { ...ownProperties, ...safeGetterValues };
+
+ // Ignore properties that are neither non-concrete nor getters/setters.
+ const propertiesNames = sortProperties(Object.keys(allProperties)).filter(
+ name => {
+ if (!allProperties[name]) {
+ return false;
+ }
+
+ const properties = Object.getOwnPropertyNames(allProperties[name]);
+ return properties.some(property =>
+ ["value", "getterValue", "get", "set"].includes(property)
+ );
+ }
+ );
+
+ const isParentNodeWindow = parentValue && parentValue.class == "Window";
+ const nodes = isParentNodeWindow
+ ? makeDefaultPropsBucket(propertiesNames, parent, allProperties)
+ : makeNodesForOwnProps(propertiesNames, parent, allProperties);
+
+ if (Array.isArray(ownSymbols)) {
+ ownSymbols.forEach((ownSymbol, index) => {
+ const descriptorValue = ownSymbol?.descriptor?.value;
+ const hasGrip = descriptorValue?.getGrip;
+ const symbolGrip = hasGrip ? descriptorValue.getGrip() : descriptorValue;
+ const symbolFront = hasGrip ? ownSymbol.descriptor.value : null;
+
+ nodes.push(
+ createNode({
+ parent,
+ name: ownSymbol.name,
+ path: `symbol-${index}`,
+ contents: {
+ value: symbolGrip,
+ front: symbolFront,
+ },
+ })
+ );
+ }, this);
+ }
+
+ if (Array.isArray(privateProperties)) {
+ privateProperties.forEach((privateProperty, index) => {
+ const descriptorValue = privateProperty?.descriptor?.value;
+ const hasGrip = descriptorValue?.getGrip;
+ const privatePropertyGrip = hasGrip
+ ? descriptorValue.getGrip()
+ : descriptorValue;
+ const privatePropertyFront = hasGrip
+ ? privateProperty.descriptor.value
+ : null;
+
+ nodes.push(
+ createNode({
+ parent,
+ name: privateProperty.name,
+ path: `private-${index}`,
+ contents: {
+ value: privatePropertyGrip,
+ front: privatePropertyFront,
+ },
+ })
+ );
+ }, this);
+ }
+
+ if (nodeIsPromise(parent)) {
+ nodes.push(...makeNodesForPromiseProperties(objProps, parent));
+ }
+
+ if (nodeHasEntries(parent)) {
+ nodes.push(makeNodesForEntries(parent));
+ }
+
+ // Add accessor nodes if needed
+ const defaultPropertiesNode = isParentNodeWindow
+ ? nodes.find(node => nodeIsDefaultProperties(node))
+ : null;
+
+ for (const name of propertiesNames) {
+ const property = allProperties[name];
+ const isDefaultProperty =
+ isParentNodeWindow &&
+ defaultPropertiesNode &&
+ isDefaultWindowProperty(name);
+ const parentNode = isDefaultProperty ? defaultPropertiesNode : parent;
+ const parentContentsArray =
+ isDefaultProperty && defaultPropertiesNode
+ ? defaultPropertiesNode.contents
+ : nodes;
+
+ if (property.get && property.get.type !== "undefined") {
+ parentContentsArray.push(
+ createGetterNode({
+ parent: parentNode,
+ property,
+ name,
+ })
+ );
+ }
+
+ if (property.set && property.set.type !== "undefined") {
+ parentContentsArray.push(
+ createSetterNode({
+ parent: parentNode,
+ property,
+ name,
+ })
+ );
+ }
+ }
+
+ const preview = parentValue?.preview;
+
+ if (preview && Object.hasOwn(preview, 'wrappedValue')) {
+ const primitiveValue = preview.wrappedValue
+ nodes.push(makeNodeForPrimitiveValue(parentValue, primitiveValue))
+ }
+
+ // Add the prototype if it exists and is not null
+ if (prototype && prototype.type !== "null") {
+ nodes.push(makeNodeForPrototype(objProps, parent));
+ }
+
+ return nodes;
+}
+
+function setNodeFullText(loadedProps, node) {
+ if (nodeHasFullText(node) || !nodeIsLongString(node)) {
+ return node;
+ }
+
+ const { fullText } = loadedProps;
+ if (nodeHasValue(node)) {
+ node.contents.value.fullText = fullText;
+ } else if (nodeHasGetterValue(node)) {
+ node.contents.getterValue.fullText = fullText;
+ }
+
+ return node;
+}
+
+function makeNodeForPrototype(objProps, parent) {
+ const { prototype } = objProps || {};
+
+ // Add the prototype if it exists and is not null
+ if (prototype && prototype.type !== "null") {
+ return createNode({
+ parent,
+ name: "<prototype>",
+ contents: {
+ value: prototype.getGrip ? prototype.getGrip() : prototype,
+ front: prototype.getGrip ? prototype : null,
+ },
+ type: NODE_TYPES.PROTOTYPE,
+ });
+ }
+
+ return null;
+}
+
+function createNode(options) {
+ const {
+ parent,
+ name,
+ propertyName,
+ path,
+ contents,
+ type = NODE_TYPES.GRIP,
+ meta,
+ } = options;
+
+ if (contents === undefined) {
+ return null;
+ }
+
+ // The path is important to uniquely identify the item in the entire
+ // tree. This helps debugging & optimizes React's rendering of large
+ // lists. The path will be separated by property name.
+
+ return {
+ parent,
+ name,
+ // `name` can be escaped; propertyName contains the original property name.
+ propertyName,
+ path: createPath(parent && parent.path, path || name),
+ contents,
+ type,
+ meta,
+ };
+}
+
+function createGetterNode({ parent, property, name }) {
+ const isFront = property.get && property.get.getGrip;
+ const grip = isFront ? property.get.getGrip() : property.get;
+ const front = isFront ? property.get : null;
+
+ return createNode({
+ parent,
+ name: `<get ${name}()>`,
+ contents: { value: grip, front },
+ type: NODE_TYPES.GET,
+ });
+}
+
+function createSetterNode({ parent, property, name }) {
+ const isFront = property.set && property.set.getGrip;
+ const grip = isFront ? property.set.getGrip() : property.set;
+ const front = isFront ? property.set : null;
+
+ return createNode({
+ parent,
+ name: `<set ${name}()>`,
+ contents: { value: grip, front },
+ type: NODE_TYPES.SET,
+ });
+}
+
+function setNodeChildren(node, children) {
+ node.contents = children;
+ return node;
+}
+
+function getEvaluatedItem(item, evaluations) {
+ if (!evaluations.has(item.path)) {
+ return item;
+ }
+
+ const evaluation = evaluations.get(item.path);
+ const isFront =
+ evaluation && evaluation.getterValue && evaluation.getterValue.getGrip;
+
+ const contents = isFront
+ ? {
+ getterValue: evaluation.getterValue.getGrip(),
+ front: evaluation.getterValue,
+ }
+ : evaluations.get(item.path);
+
+ return {
+ ...item,
+ contents,
+ };
+}
+
+function getChildrenWithEvaluations(options) {
+ const { item, loadedProperties, cachedNodes, evaluations } = options;
+
+ const children = getChildren({
+ loadedProperties,
+ cachedNodes,
+ item,
+ });
+
+ if (Array.isArray(children)) {
+ return children.map(i => getEvaluatedItem(i, evaluations));
+ }
+
+ if (children) {
+ return getEvaluatedItem(children, evaluations);
+ }
+
+ return [];
+}
+
+function getChildren(options) {
+ const { cachedNodes, item, loadedProperties = new Map() } = options;
+
+ const key = item.path;
+ if (cachedNodes && cachedNodes.has(key)) {
+ return cachedNodes.get(key);
+ }
+
+ const loadedProps = loadedProperties.get(key);
+ const hasLoadedProps = loadedProperties.has(key);
+
+ // Because we are dynamically creating the tree as the user
+ // expands it (not precalculated tree structure), we cache child
+ // arrays. This not only helps performance, but is necessary
+ // because the expanded state depends on instances of nodes
+ // being the same across renders. If we didn't do this, each
+ // node would be a new instance every render.
+ // If the node needs properties, we only add children to
+ // the cache if the properties are loaded.
+ const addToCache = children => {
+ if (cachedNodes) {
+ cachedNodes.set(item.path, children);
+ }
+ return children;
+ };
+
+ // Nodes can either have children already, or be an object with
+ // properties that we need to go and fetch.
+ if (nodeHasChildren(item)) {
+ return addToCache(item.contents);
+ }
+
+ if (nodeIsMapEntry(item)) {
+ return addToCache(makeNodesForMapEntry(item));
+ }
+
+ if (nodeIsProxy(item) && hasLoadedProps) {
+ return addToCache(makeNodesForProxyProperties(loadedProps, item));
+ }
+
+ if (nodeIsLongString(item) && hasLoadedProps) {
+ // Set longString object's fullText to fetched one.
+ return addToCache(setNodeFullText(loadedProps, item));
+ }
+
+ if (nodeNeedsNumericalBuckets(item) && hasLoadedProps) {
+ // Even if we have numerical buckets, we should have loaded non indexed
+ // properties.
+ const bucketNodes = makeNumericalBuckets(item);
+ return addToCache(
+ bucketNodes.concat(makeNodesForProperties(loadedProps, item))
+ );
+ }
+
+ if (!nodeIsEntries(item) && !nodeIsBucket(item) && !nodeHasProperties(item)) {
+ return [];
+ }
+
+ if (!hasLoadedProps) {
+ return [];
+ }
+
+ return addToCache(makeNodesForProperties(loadedProps, item));
+}
+
+// Builds an expression that resolves to the value of the item in question
+// e.g. `b` in { a: { b: 2 } } resolves to `a.b`
+function getPathExpression(item) {
+ if (item && item.parent) {
+ const parent = nodeIsBucket(item.parent) ? item.parent.parent : item.parent;
+ return `${getPathExpression(parent)}.${item.name}`;
+ }
+
+ return item.name;
+}
+
+function getParent(item) {
+ return item.parent;
+}
+
+function getNumericalPropertiesCount(item) {
+ if (nodeIsBucket(item)) {
+ return item.meta.endIndex - item.meta.startIndex + 1;
+ }
+
+ const value = getValue(getClosestGripNode(item));
+ if (!value) {
+ return 0;
+ }
+
+ if (GripArrayRep.supportsObject(value)) {
+ return GripArrayRep.getLength(value);
+ }
+
+ if (GripMap.supportsObject(value)) {
+ return GripMap.getLength(value);
+ }
+
+ // TODO: We can also have numerical properties on Objects, but at the
+ // moment we don't have a way to distinguish them from non-indexed properties,
+ // as they are all computed in a ownPropertiesLength property.
+
+ return 0;
+}
+
+function getClosestGripNode(item) {
+ const type = getType(item);
+ if (
+ type !== NODE_TYPES.BUCKET &&
+ type !== NODE_TYPES.DEFAULT_PROPERTIES &&
+ type !== NODE_TYPES.ENTRIES
+ ) {
+ return item;
+ }
+
+ const parent = getParent(item);
+ if (!parent) {
+ return null;
+ }
+
+ return getClosestGripNode(parent);
+}
+
+function getClosestNonBucketNode(item) {
+ const type = getType(item);
+
+ if (type !== NODE_TYPES.BUCKET) {
+ return item;
+ }
+
+ const parent = getParent(item);
+ if (!parent) {
+ return null;
+ }
+
+ return getClosestNonBucketNode(parent);
+}
+
+function getParentGripNode(item) {
+ const parentNode = getParent(item);
+ if (!parentNode) {
+ return null;
+ }
+
+ return getClosestGripNode(parentNode);
+}
+
+function getParentGripValue(item) {
+ const parentGripNode = getParentGripNode(item);
+ if (!parentGripNode) {
+ return null;
+ }
+
+ return getValue(parentGripNode);
+}
+
+function getParentFront(item) {
+ const parentGripNode = getParentGripNode(item);
+ if (!parentGripNode) {
+ return null;
+ }
+
+ return getFront(parentGripNode);
+}
+
+function getNonPrototypeParentGripValue(item) {
+ const parentGripNode = getParentGripNode(item);
+ if (!parentGripNode) {
+ return null;
+ }
+
+ if (getType(parentGripNode) === NODE_TYPES.PROTOTYPE) {
+ return getNonPrototypeParentGripValue(parentGripNode);
+ }
+
+ return getValue(parentGripNode);
+}
+
+function createPath(parentPath, path) {
+ return parentPath ? `${parentPath}◦${path}` : path;
+}
+
+module.exports = {
+ createNode,
+ createGetterNode,
+ createSetterNode,
+ getActor,
+ getChildren,
+ getChildrenWithEvaluations,
+ getClosestGripNode,
+ getClosestNonBucketNode,
+ getEvaluatedItem,
+ getFront,
+ getPathExpression,
+ getParent,
+ getParentFront,
+ getParentGripValue,
+ getNonPrototypeParentGripValue,
+ getNumericalPropertiesCount,
+ getValue,
+ makeNodesForEntries,
+ makeNodesForPromiseProperties,
+ makeNodesForProperties,
+ makeNumericalBuckets,
+ nodeHasAccessors,
+ nodeHasChildren,
+ nodeHasEntries,
+ nodeHasProperties,
+ nodeHasGetter,
+ nodeHasSetter,
+ nodeIsBlock,
+ nodeIsBucket,
+ nodeIsDefaultProperties,
+ nodeIsEntries,
+ nodeIsError,
+ nodeIsLongString,
+ nodeHasFullText,
+ nodeIsFunction,
+ nodeIsGetter,
+ nodeIsMapEntry,
+ nodeIsMissingArguments,
+ nodeIsObject,
+ nodeIsOptimizedOut,
+ nodeIsPrimitive,
+ nodeIsPromise,
+ nodeIsPrototype,
+ nodeIsProxy,
+ nodeIsSetter,
+ nodeIsUninitializedBinding,
+ nodeIsUnmappedBinding,
+ nodeIsUnscopedBinding,
+ nodeIsWindow,
+ nodeNeedsNumericalBuckets,
+ nodeSupportsNumericalBucketing,
+ setNodeChildren,
+ sortProperties,
+ NODE_TYPES,
+};
diff --git a/devtools/client/shared/components/object-inspector/utils/selection.js b/devtools/client/shared/components/object-inspector/utils/selection.js
new file mode 100644
index 0000000000..fdcca7ff6b
--- /dev/null
+++ b/devtools/client/shared/components/object-inspector/utils/selection.js
@@ -0,0 +1,16 @@
+/* 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/>. */
+
+function documentHasSelection(doc = document) {
+ const selection = doc.defaultView.getSelection();
+ if (!selection) {
+ return false;
+ }
+
+ return selection.type === "Range";
+}
+
+module.exports = {
+ documentHasSelection,
+};
diff --git a/devtools/client/shared/components/reps/images/input.svg b/devtools/client/shared/components/reps/images/input.svg
new file mode 100644
index 0000000000..830b651e9e
--- /dev/null
+++ b/devtools/client/shared/components/reps/images/input.svg
@@ -0,0 +1,7 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="context-fill #0b0b0b">
+ <path d="M11.04 5.46L7.29 1.71a.75.75 0 0 0-1.06 1.06L9.45 6 6.23 9.21a.75.75 0 1 0 1.06 1.06l3.75-3.75c.3-.3.3-.77 0-1.06z"/>
+ <path d="M6.04 5.46L2.29 1.71a.75.75 0 0 0-1.06 1.06L4.45 6 1.23 9.21a.75.75 0 1 0 1.06 1.06l3.75-3.75c.3-.3.3-.77 0-1.06z"/>
+</svg> \ No newline at end of file
diff --git a/devtools/client/shared/components/reps/images/jump-definition.svg b/devtools/client/shared/components/reps/images/jump-definition.svg
new file mode 100644
index 0000000000..9ac071523d
--- /dev/null
+++ b/devtools/client/shared/components/reps/images/jump-definition.svg
@@ -0,0 +1,8 @@
+<!-- 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/. -->
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" stroke="context-stroke" fill="none" stroke-linecap="round">
+ <path d="M5.5 3.5l2 2M5.5 7.5l2-2"/>
+ <path d="M7 5.5H4.006c-1.012 0-1.995 1.017-2.011 2.024-.005.023-.005 1.347 0 3.971" stroke-linejoin="round"/>
+ <path d="M10.5 5.5h4M9.5 3.5h5M9.5 7.5h5"/>
+</svg> \ No newline at end of file
diff --git a/devtools/client/shared/components/reps/images/open-a11y.svg b/devtools/client/shared/components/reps/images/open-a11y.svg
new file mode 100644
index 0000000000..cba2ab93c9
--- /dev/null
+++ b/devtools/client/shared/components/reps/images/open-a11y.svg
@@ -0,0 +1,10 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" fill="context-fill #0C0C0D">
+ <path d="M9.5 2.5C9.5 3.60457 8.60457 4.5 7.5 4.5C6.39543 4.5 5.5 3.60457 5.5 2.5C5.5 1.39543 6.39543 0.5 7.5 0.5C8.60457 0.5 9.5 1.39543 9.5 2.5Z"/>
+ <path d="M1.5 6C1.5 5.44772 1.94772 5 2.5 5H12.5C13.0523 5 13.5 5.44772 13.5 6C13.5 6.55228 13.0523 7 12.5 7H2.5C1.94772 7 1.5 6.55228 1.5 6Z"/>
+ <path d="M6 5C6.55228 5 7 5.44772 7 6L7 13C7 13.5523 6.55228 14 6 14C5.44771 14 5 13.5523 5 13L5 6C5 5.44772 5.44772 5 6 5Z"/>
+ <path d="M9 5C9.55228 5 10 5.44772 10 6V13C10 13.5523 9.55228 14 9 14C8.44771 14 8 13.5523 8 13L8 6C8 5.44772 8.44772 5 9 5Z"/>
+ <path d="M5 7H10V10.03H5V7Z"/>
+</svg>
diff --git a/devtools/client/shared/components/reps/images/open-inspector.svg b/devtools/client/shared/components/reps/images/open-inspector.svg
new file mode 100644
index 0000000000..9e8a277e7c
--- /dev/null
+++ b/devtools/client/shared/components/reps/images/open-inspector.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 15 15" fill="context-fill">
+ <path d="M7 3H5a2 2 0 0 0-2 2v2H1.5a.5.5 0 0 0 0 1H3v2c0 1.1.9 2 2 2h2v1.5a.5.5 0 0 0 1 0V12h2a2 2 0 0 0 2-2V8h1.5a.5.5 0 0 0 0-1H12V5a2 2 0 0 0-2-2H8V1.5a.5.5 0 0 0-1 0V3zM5 5h5v5H5V5z"/>
+</svg>
diff --git a/devtools/client/shared/components/reps/index.js b/devtools/client/shared/components/reps/index.js
new file mode 100644
index 0000000000..e99e642c38
--- /dev/null
+++ b/devtools/client/shared/components/reps/index.js
@@ -0,0 +1,32 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ MODE,
+} = require("devtools/client/shared/components/reps/reps/constants");
+const {
+ REPS,
+ getRep,
+} = require("devtools/client/shared/components/reps/reps/rep");
+const objectInspector = require("devtools/client/shared/components/object-inspector/index");
+
+const {
+ parseURLEncodedText,
+ parseURLParams,
+ maybeEscapePropertyName,
+ getGripPreviewItems,
+} = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+module.exports = {
+ REPS,
+ getRep,
+ MODE,
+ maybeEscapePropertyName,
+ parseURLEncodedText,
+ parseURLParams,
+ getGripPreviewItems,
+ objectInspector,
+};
diff --git a/devtools/client/shared/components/reps/moz.build b/devtools/client/shared/components/reps/moz.build
new file mode 100644
index 0000000000..058e8046a7
--- /dev/null
+++ b/devtools/client/shared/components/reps/moz.build
@@ -0,0 +1,14 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "reps",
+ "shared",
+]
+
+DevToolsModules(
+ "index.js",
+)
diff --git a/devtools/client/shared/components/reps/reps.css b/devtools/client/shared/components/reps/reps.css
new file mode 100644
index 0000000000..68eee2e78b
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps.css
@@ -0,0 +1,400 @@
+/* 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/. */
+
+.theme-dark,
+.theme-light {
+ --number-color: var(--theme-highlight-green);
+ --string-color: var(--theme-highlight-red);
+ --null-color: var(--theme-comment);
+ --object-color: var(--theme-highlight-blue);
+ --caption-color: var(--theme-highlight-blue);
+ --location-color: var(--theme-comment);
+ --source-link-color: var(--theme-link-color);
+ --node-color: var(--theme-highlight-purple);
+ --reference-color: var(--theme-highlight-blue);
+ --comment-node-color: var(--theme-comment);
+}
+
+/******************************************************************************/
+
+.inline {
+ display: inline;
+ white-space: normal;
+}
+
+.objectBox-object {
+ font-weight: bold;
+ color: var(--object-color);
+ white-space: pre-wrap;
+}
+
+.objectBox-string,
+.objectBox-symbol,
+.objectBox-text,
+.objectBox-textNode,
+.objectBox-table {
+ white-space: pre-wrap;
+}
+
+:is(
+ .objectBox-string,
+ .objectBox-textNode,
+ .objectBox > .nodeName,
+ .objectBox-node .tag-name,
+ .objectBox-node .attrName
+).has-rtl-char {
+ unicode-bidi: isolate;
+}
+
+.objectBox-number,
+.objectBox-styleRule,
+.objectBox-element,
+.objectBox-textNode,
+.objectBox-array > .length {
+ color: var(--number-color);
+}
+
+.objectBox-textNode,
+.objectBox-string,
+.objectBox-symbol {
+ color: var(--string-color);
+}
+
+.objectBox-empty-string {
+ font-style: italic;
+}
+
+.objectBox-string a {
+ word-break: break-all;
+}
+
+.objectBox-string a,
+.objectBox-string a:visited {
+ color: currentColor;
+ text-decoration: underline;
+ text-decoration-skip-ink: none;
+ font-style: italic;
+ cursor: pointer;
+}
+
+/* Visually hide the middle of "cropped" url */
+.objectBox-string a .cropped-url-middle {
+ max-width: 0;
+ max-height: 0;
+ display: inline-block;
+ overflow: hidden;
+ vertical-align: bottom;
+}
+
+.objectBox-string a .cropped-url-end::before {
+ content: "…";
+}
+
+
+.objectBox-function,
+.objectBox-profile {
+ color: var(--object-color);
+}
+
+.objectBox-stackTrace.reps-custom-format,
+.objectBox-stackTrace.reps-custom-format > .objectBox-string {
+ color: var(--error-color);
+}
+
+.objectBox-stackTrace-grid {
+ display: inline-grid;
+ grid-template-columns: auto auto;
+ margin-top: 3px;
+}
+
+.objectBox-stackTrace-fn {
+ color: var(--console-output-color);
+ padding-inline-start: 17px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ margin-inline-end: 5px;
+}
+
+.objectBox-stackTrace-location {
+ color: var(--frame-link-source, currentColor);
+ direction: rtl;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ text-align: end;
+}
+
+.objectBox-stackTrace-location:hover {
+ text-decoration: underline;
+}
+
+.objectBox-stackTrace-location {
+ cursor: pointer;
+}
+
+.objectBox-Location,
+.location {
+ color: var(--location-color);
+}
+
+.objectBox-null,
+.objectBox-undefined,
+.objectBox-hint,
+.objectBox-nan,
+.logRowHint {
+ color: var(--null-color);
+}
+
+.objectBox-sourceLink {
+ position: absolute;
+ right: 4px;
+ top: 2px;
+ padding-left: 8px;
+ font-weight: bold;
+ color: var(--source-link-color);
+}
+
+.objectBox-failure {
+ color: var(--string-color);
+ border-width: 1px;
+ border-style: solid;
+ border-radius: 2px;
+ font-size: 0.8em;
+ padding: 0 2px;
+}
+
+.objectBox-accessible.clickable,
+.objectBox-node.clickable {
+ cursor: pointer;
+}
+
+/* JsonML reps can be nested, though only the top-level rep needs layout
+ * adjustments to align it with the toggle arrow and fit its width to its
+ * contents. */
+.objectBox-jsonml-wrapper {
+ display: inline-flex;
+ flex-direction: column;
+ width: fit-content;
+ word-break: break-word;
+ line-height: normal;
+}
+
+.objectBox-jsonml-wrapper[data-expandable="true"] {
+ cursor: default;
+}
+
+.objectBox-jsonml-wrapper .jsonml-header-collapse-button {
+ margin: 0 4px 2px 0;
+ padding: 0;
+ vertical-align: middle;
+}
+
+.objectBox-jsonml-wrapper .jsonml-header-collapse-button::before {
+ content: "";
+ display: block;
+ width: 10px;
+ height: 10px;
+ background: url("chrome://devtools/skin/images/arrow.svg") no-repeat center;
+ background-size: 10px;
+ transform: rotate(-90deg);
+ transition: transform 125ms ease;
+ -moz-context-properties: fill;
+ fill: var(--theme-icon-dimmed-color);
+}
+
+.objectBox-jsonml-wrapper .jsonml-header-collapse-button[aria-expanded="true"]::before {
+ transform: rotate(0deg);
+}
+
+/******************************************************************************/
+
+.objectBox-event,
+.objectBox-eventLog,
+.objectBox-regexp,
+.objectBox-object {
+ color: var(--object-color);
+ white-space: pre-wrap;
+}
+
+.objectBox .Date {
+ color: var(--string-color);
+ white-space: pre-wrap;
+}
+
+/******************************************************************************/
+
+.objectBox.theme-comment {
+ color: var(--comment-node-color);
+}
+
+.accessible-role,
+.tag-name {
+ color: var(--object-color);
+}
+
+.attrName {
+ color: var(--string-color);
+}
+
+.attrEqual,
+.objectEqual {
+ color: var(--comment-node-color);
+}
+
+.attrValue,
+.attrValue.objectBox-string {
+ color: var(--node-color);
+}
+
+.angleBracket {
+ color: var(--theme-body-color);
+}
+
+/******************************************************************************/
+/* Length bubble for arraylikes and maplikes */
+
+.objectLengthBubble {
+ color: var(--null-color);
+}
+
+/******************************************************************************/
+
+.objectLeftBrace,
+.objectRightBrace,
+.arrayLeftBracket,
+.arrayRightBracket {
+ color: var(--object-color);
+}
+
+/******************************************************************************/
+/* Cycle reference */
+
+.objectBox-Reference {
+ font-weight: bold;
+ color: var(--reference-color);
+}
+
+[class*="objectBox"] > .objectTitle {
+ color: var(--object-color);
+}
+
+.caption {
+ color: var(--caption-color);
+}
+
+/******************************************************************************/
+/* Themes */
+
+.theme-dark .objectBox-null,
+.theme-dark .objectBox-undefined,
+.theme-light .objectBox-null,
+.theme-light .objectBox-undefined {
+ font-style: normal;
+}
+
+.theme-dark .objectBox-object,
+.theme-light .objectBox-object {
+ font-weight: normal;
+ white-space: pre-wrap;
+}
+
+.theme-dark .caption,
+.theme-light .caption {
+ font-weight: normal;
+}
+
+/******************************************************************************/
+/* Open DOMNode in inspector or Accessible in accessibility inspector button */
+
+:is(button, [role="button"]).open-accessibility-inspector {
+ background: url("chrome://devtools/content/shared/components/reps/images/open-a11y.svg")
+ no-repeat;
+}
+
+:is(button, [role="button"]).open-inspector {
+ background: url("chrome://devtools/content/shared/components/reps/images/open-inspector.svg")
+ no-repeat;
+}
+
+:is(button, [role="button"]).highlight-node {
+ background: url("chrome://devtools/skin/images/highlight-selector.svg")
+ no-repeat;
+}
+
+
+:is(button, [role="button"]):is(.open-accessibility-inspector, .open-inspector, .highlight-node) {
+ display: inline-block;
+ vertical-align: top;
+ height: 15px;
+ width: 15px;
+ margin: 0 4px;
+ padding: 0;
+ border: none;
+ fill: var(--theme-icon-color);
+ cursor: pointer;
+ -moz-context-properties: fill;
+}
+
+.objectBox-accessible:hover .open-accessibility-inspector,
+.objectBox-node:hover .open-inspector,
+.objectBox-textNode:hover .open-inspector,
+.open-accessibility-inspector:hover,
+.highlight-node:hover,
+.open-inspector:hover {
+ fill: var(--theme-icon-checked-color);
+}
+
+/******************************************************************************/
+/* Jump to definition button */
+
+button.jump-definition {
+ display: inline-block;
+ height: 16px;
+ margin-left: 0.25em;
+ vertical-align: middle;
+ background: 0% 50%
+ url("chrome://devtools/content/shared/components/reps/images/jump-definition.svg")
+ no-repeat;
+ border-color: transparent;
+ stroke: var(--theme-icon-color);
+ -moz-context-properties: stroke;
+ cursor: pointer;
+}
+
+.jump-definition:hover {
+ stroke: var(--theme-icon-checked-color);
+}
+
+.tree-node.focused .jump-definition {
+ stroke: currentColor;
+}
+
+/******************************************************************************/
+/* Invoke getter button */
+
+button.invoke-getter {
+ mask: url(chrome://devtools/content/shared/components/reps/images/input.svg)
+ no-repeat;
+ display: inline-block;
+ background-color: var(--theme-icon-color);
+ height: 10px;
+ vertical-align: middle;
+ border: none;
+}
+
+.invoke-getter:hover {
+ background-color: var(--theme-icon-checked-color);
+}
+
+/******************************************************************************/
+/* "more…" ellipsis */
+.more-ellipsis {
+ color: var(--comment-node-color);
+}
+
+/* function parameters */
+.objectBox-function .param {
+ color: var(--theme-highlight-red);
+}
diff --git a/devtools/client/shared/components/reps/reps/accessible.js b/devtools/client/shared/components/reps/reps/accessible.js
new file mode 100644
index 0000000000..796a850161
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/accessible.js
@@ -0,0 +1,197 @@
+/* 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/>. */
+
+"use strict";
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const {
+ button,
+ span,
+ } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Utils
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ rep: StringRep,
+ } = require("devtools/client/shared/components/reps/reps/string");
+
+ /**
+ * Renders Accessible object.
+ */
+
+ Accessible.propTypes = {
+ object: PropTypes.object.isRequired,
+ inspectIconTitle: PropTypes.string,
+ nameMaxLength: PropTypes.number,
+ onAccessibleClick: PropTypes.func,
+ onAccessibleMouseOver: PropTypes.func,
+ onAccessibleMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ roleFirst: PropTypes.bool,
+ separatorText: PropTypes.string,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function Accessible(props) {
+ const {
+ object,
+ inspectIconTitle,
+ nameMaxLength,
+ onAccessibleClick,
+ onInspectIconClick,
+ roleFirst,
+ separatorText,
+ } = props;
+
+ const isInTree = object.preview && object.preview.isConnected === true;
+
+ const config = getElementConfig({ ...props, isInTree });
+ const elements = getElements(
+ object,
+ nameMaxLength,
+ roleFirst,
+ separatorText
+ );
+ const inspectIcon = getInspectIcon({
+ object,
+ onInspectIconClick,
+ inspectIconTitle,
+ onAccessibleClick,
+ isInTree,
+ });
+
+ return span(config, ...elements, inspectIcon);
+ }
+
+ // Get React Config Obj
+ function getElementConfig(opts) {
+ const {
+ object,
+ isInTree,
+ onAccessibleClick,
+ onAccessibleMouseOver,
+ onAccessibleMouseOut,
+ shouldRenderTooltip,
+ roleFirst,
+ } = opts;
+ const { name, role } = object.preview;
+
+ // Initiate config
+ const config = {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-accessible",
+ };
+
+ if (isInTree) {
+ if (onAccessibleClick) {
+ Object.assign(config, {
+ onClick: _ => onAccessibleClick(object),
+ className: `${config.className} clickable`,
+ });
+ }
+
+ if (onAccessibleMouseOver) {
+ Object.assign(config, {
+ onMouseOver: _ => onAccessibleMouseOver(object),
+ });
+ }
+
+ if (onAccessibleMouseOut) {
+ Object.assign(config, {
+ onMouseOut: onAccessibleMouseOut,
+ });
+ }
+ }
+
+ // If tooltip, build tooltip
+ if (shouldRenderTooltip) {
+ let tooltip;
+ if (!name) {
+ tooltip = role;
+ } else {
+ const quotedName = `"${name}"`;
+ tooltip = `${roleFirst ? role : quotedName}: ${
+ roleFirst ? quotedName : role
+ }`;
+ }
+
+ config.title = tooltip;
+ }
+
+ // Return config obj
+ return config;
+ }
+
+ // Get Content Elements
+ function getElements(
+ grip,
+ nameMaxLength,
+ roleFirst = false,
+ separatorText = ": "
+ ) {
+ const { name, role } = grip.preview;
+ const elements = [];
+
+ // If there's a `name` value in `grip.preview`, render it with the
+ // StringRep and push element into Elements array
+
+ if (name) {
+ elements.push(
+ StringRep({
+ className: "accessible-name",
+ object: name,
+ cropLimit: nameMaxLength,
+ }),
+ span({ className: "separator" }, separatorText)
+ );
+ }
+
+ elements.push(span({ className: "accessible-role" }, role));
+ return roleFirst ? elements.reverse() : elements;
+ }
+
+ // Get Icon
+ function getInspectIcon(opts) {
+ const {
+ object,
+ onInspectIconClick,
+ inspectIconTitle,
+ onAccessibleClick,
+ isInTree,
+ } = opts;
+
+ if (!isInTree || !onInspectIconClick) {
+ return null;
+ }
+
+ return button({
+ className: "open-accessibility-inspector",
+ title: inspectIconTitle,
+ onClick: e => {
+ if (onAccessibleClick) {
+ e.stopPropagation();
+ }
+
+ onInspectIconClick(object, e);
+ },
+ });
+ }
+
+ // Registration
+ function supportsObject(object) {
+ return (
+ object?.preview && object.typeName && object.typeName === "accessible"
+ );
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(Accessible),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/accessor.js b/devtools/client/shared/components/reps/reps/accessor.js
new file mode 100644
index 0000000000..b234d814b3
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/accessor.js
@@ -0,0 +1,106 @@
+/* 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/>. */
+
+"use strict";
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const {
+ button,
+ span,
+ } = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ /**
+ * Renders an object. An object is represented by a list of its
+ * properties enclosed in curly brackets.
+ */
+
+ Accessor.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function Accessor(props) {
+ const {
+ object,
+ evaluation,
+ onInvokeGetterButtonClick,
+ shouldRenderTooltip,
+ } = props;
+
+ if (evaluation) {
+ const {
+ Rep,
+ Grip,
+ } = require("devtools/client/shared/components/reps/reps/rep");
+ return span(
+ {
+ className: "objectBox objectBox-accessor objectTitle",
+ },
+ Rep({
+ ...props,
+ object: evaluation.getterValue,
+ mode: props.mode || MODE.TINY,
+ defaultRep: Grip,
+ })
+ );
+ }
+
+ if (hasGetter(object) && onInvokeGetterButtonClick) {
+ return button({
+ className: "invoke-getter",
+ title: "Invoke getter",
+ onClick: event => {
+ onInvokeGetterButtonClick();
+ event.stopPropagation();
+ },
+ });
+ }
+
+ const accessors = [];
+ if (hasGetter(object)) {
+ accessors.push("Getter");
+ }
+
+ if (hasSetter(object)) {
+ accessors.push("Setter");
+ }
+
+ const accessorsString = accessors.join(" & ");
+
+ return span(
+ {
+ className: "objectBox objectBox-accessor objectTitle",
+ title: shouldRenderTooltip ? accessorsString : null,
+ },
+ accessorsString
+ );
+ }
+
+ function hasGetter(object) {
+ return object && object.get && object.get.type !== "undefined";
+ }
+
+ function hasSetter(object) {
+ return object && object.set && object.set.type !== "undefined";
+ }
+
+ function supportsObject(object) {
+ return hasGetter(object) || hasSetter(object);
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(Accessor),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/array.js b/devtools/client/shared/components/reps/reps/array.js
new file mode 100644
index 0000000000..f8797fa4d3
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/array.js
@@ -0,0 +1,170 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ const ModePropType = PropTypes.oneOf(Object.values(MODE));
+
+ /**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+
+ ArrayRep.propTypes = {
+ mode: ModePropType,
+ object: PropTypes.array.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function ArrayRep(props) {
+ const { object, mode = MODE.SHORT, shouldRenderTooltip = true } = props;
+
+ let brackets;
+ let items;
+ const needSpace = function (space) {
+ return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+ };
+
+ if (mode === MODE.TINY) {
+ const isEmpty = object.length === 0;
+ if (isEmpty) {
+ items = [];
+ } else {
+ items = [
+ span(
+ {
+ className: "more-ellipsis",
+ },
+ "…"
+ ),
+ ];
+ }
+ brackets = needSpace(false);
+ } else {
+ items = arrayIterator(props, object, maxLengthMap.get(mode));
+ brackets = needSpace(!!items.length);
+ }
+
+ return span(
+ {
+ className: "objectBox objectBox-array",
+ title: shouldRenderTooltip ? "Array" : null,
+ },
+ span(
+ {
+ className: "arrayLeftBracket",
+ },
+ brackets.left
+ ),
+ ...items,
+ span(
+ {
+ className: "arrayRightBracket",
+ },
+ brackets.right
+ )
+ );
+ }
+
+ function arrayIterator(props, array, max) {
+ const items = [];
+
+ for (let i = 0; i < array.length && i < max; i++) {
+ const config = {
+ mode: MODE.TINY,
+ delim: i == array.length - 1 ? "" : ", ",
+ };
+ let item;
+
+ try {
+ item = ItemRep({
+ ...props,
+ ...config,
+ object: array[i],
+ });
+ } catch (exc) {
+ item = ItemRep({
+ ...props,
+ ...config,
+ object: exc,
+ });
+ }
+ items.push(item);
+ }
+
+ if (array.length > max) {
+ items.push(
+ span(
+ {
+ className: "more-ellipsis",
+ },
+ "…"
+ )
+ );
+ }
+
+ return items;
+ }
+
+ /**
+ * Renders array item. Individual values are separated by a comma.
+ */
+
+ ItemRep.propTypes = {
+ object: PropTypes.any.isRequired,
+ delim: PropTypes.string.isRequired,
+ mode: ModePropType,
+ };
+
+ function ItemRep(props) {
+ const { Rep } = require("devtools/client/shared/components/reps/reps/rep");
+
+ const { object, delim, mode } = props;
+ return span(
+ {},
+ Rep({
+ ...props,
+ object,
+ mode,
+ }),
+ delim
+ );
+ }
+
+ function getLength(object) {
+ return object.length;
+ }
+
+ function supportsObject(object, noGrip = false) {
+ return (
+ noGrip &&
+ (Array.isArray(object) ||
+ Object.prototype.toString.call(object) === "[object Arguments]")
+ );
+ }
+
+ const maxLengthMap = new Map();
+ maxLengthMap.set(MODE.SHORT, 3);
+ maxLengthMap.set(MODE.LONG, 10);
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(ArrayRep),
+ supportsObject,
+ maxLengthMap,
+ getLength,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/attribute.js b/devtools/client/shared/components/reps/reps/attribute.js
new file mode 100644
index 0000000000..d9c4e7c237
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/attribute.js
@@ -0,0 +1,80 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ appendRTLClassNameIfNeeded,
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ rep: StringRep,
+ } = require("devtools/client/shared/components/reps/reps/string");
+
+ /**
+ * Renders DOM attribute
+ */
+
+ Attribute.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function Attribute(props) {
+ const { object, shouldRenderTooltip } = props;
+ const value = object.preview.value;
+ const attrName = getTitle(object);
+
+ const config = getElementConfig({
+ attrName,
+ shouldRenderTooltip,
+ value,
+ object,
+ });
+
+ return span(
+ config,
+ span(
+ {
+ className: appendRTLClassNameIfNeeded("attrName", attrName),
+ },
+ attrName
+ ),
+ span({ className: "attrEqual" }, "="),
+ StringRep({ className: "attrValue", object: value })
+ );
+ }
+
+ function getTitle(grip) {
+ return grip.preview.nodeName;
+ }
+
+ function getElementConfig(opts) {
+ const { attrName, shouldRenderTooltip, value, object } = opts;
+
+ return {
+ "data-link-actor-id": object.actor,
+ className: "objectBox-Attr",
+ title: shouldRenderTooltip ? `${attrName}="${value}"` : null,
+ };
+ }
+
+ // Registration
+ function supportsObject(grip, noGrip = false) {
+ return getGripType(grip, noGrip) == "Attr" && grip?.preview;
+ }
+
+ module.exports = {
+ rep: wrapRender(Attribute),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/big-int.js b/devtools/client/shared/components/reps/reps/big-int.js
new file mode 100644
index 0000000000..4bb7db507f
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/big-int.js
@@ -0,0 +1,57 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a BigInt Number
+ */
+
+ BigInt.propTypes = {
+ object: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.number,
+ PropTypes.bool,
+ ]).isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function BigInt(props) {
+ const { object, shouldRenderTooltip } = props;
+ const text = object.text;
+ const config = getElementConfig({ text, shouldRenderTooltip });
+
+ return span(config, `${text}n`);
+ }
+
+ function getElementConfig(opts) {
+ const { text, shouldRenderTooltip } = opts;
+
+ return {
+ className: "objectBox objectBox-number",
+ title: shouldRenderTooltip ? `${text}n` : null,
+ };
+ }
+ function supportsObject(object, noGrip = false) {
+ return getGripType(object, noGrip) === "BigInt";
+ }
+
+ // Exports from this module
+
+ module.exports = {
+ rep: wrapRender(BigInt),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/comment-node.js b/devtools/client/shared/components/reps/reps/comment-node.js
new file mode 100644
index 0000000000..5d4ce49e3d
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/comment-node.js
@@ -0,0 +1,76 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+ const {
+ cropString,
+ cropMultipleLines,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+ const nodeConstants = require("devtools/client/shared/components/reps/shared/dom-node-constants");
+
+ /**
+ * Renders DOM comment node.
+ */
+
+ CommentNode.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function CommentNode(props) {
+ const { object, mode = MODE.SHORT, shouldRenderTooltip } = props;
+
+ let { textContent } = object.preview;
+ if (mode === MODE.TINY || mode === MODE.HEADER) {
+ textContent = cropMultipleLines(textContent, 30);
+ } else if (mode === MODE.SHORT) {
+ textContent = cropString(textContent, 50);
+ }
+
+ const config = getElementConfig({
+ object,
+ textContent,
+ shouldRenderTooltip,
+ });
+
+ return span(config, `<!-- ${textContent} -->`);
+ }
+
+ function getElementConfig(opts) {
+ const { object, shouldRenderTooltip } = opts;
+
+ // Run textContent through cropString to sanitize
+ const uncroppedText = shouldRenderTooltip
+ ? cropString(object.preview.textContent)
+ : null;
+
+ return {
+ className: "objectBox theme-comment",
+ "data-link-actor-id": object.actor,
+ title: shouldRenderTooltip ? `<!-- ${uncroppedText} -->` : null,
+ };
+ }
+
+ // Registration
+ function supportsObject(object) {
+ return object?.preview?.nodeType === nodeConstants.COMMENT_NODE;
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(CommentNode),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/constants.js b/devtools/client/shared/components/reps/reps/constants.js
new file mode 100644
index 0000000000..2599ccab75
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/constants.js
@@ -0,0 +1,18 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ module.exports = {
+ MODE: {
+ TINY: Symbol("TINY"),
+ SHORT: Symbol("SHORT"),
+ LONG: Symbol("LONG"),
+ // Used by Debugger Preview popup
+ HEADER: Symbol("HEADER"),
+ },
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/custom-formatter.js b/devtools/client/shared/components/reps/reps/custom-formatter.js
new file mode 100644
index 0000000000..469866f790
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/custom-formatter.js
@@ -0,0 +1,256 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ // Dependencies
+ const {
+ Component,
+ createElement,
+ createFactory,
+ } = require("devtools/client/shared/vendor/react");
+ const {
+ cleanupStyle,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const flags = require("resource://devtools/shared/flags.js");
+
+ const ALLOWED_TAGS = new Set([
+ "span",
+ "div",
+ "ol",
+ "ul",
+ "li",
+ "table",
+ "tr",
+ "td",
+ ]);
+
+ class CustomFormatter extends Component {
+ static get propTypes() {
+ return {
+ autoExpandDepth: PropTypes.number,
+ client: PropTypes.object,
+ createElement: PropTypes.func,
+ frame: PropTypes.object,
+ front: PropTypes.object,
+ object: PropTypes.object.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = { open: false };
+ this.toggleBody = this.toggleBody.bind(this);
+ }
+
+ componentDidThrow(e) {
+ console.error("Error in CustomFormatter", e);
+ this.setState(state => ({ ...state, hasError: true }));
+ }
+
+ async toggleBody(evt) {
+ evt.stopPropagation();
+
+ const open = !this.state.open;
+ if (open && !this.state.bodyJsonMl) {
+ let front = this.props.front;
+ if (!front && this.props.client?.createObjectFront) {
+ if (flags.testing && !this.props.frame) {
+ throw new Error("props.frame is mandatory");
+ }
+ front = this.props.client.createObjectFront(
+ this.props.object,
+ this.props.frame
+ );
+ }
+ if (!front) {
+ return;
+ }
+
+ const response = await front.customFormatterBody();
+
+ const bodyJsonMl = renderJsonMl(response.customFormatterBody, {
+ ...this.props,
+ autoExpandDepth: this.props.autoExpandDepth
+ ? this.props.autoExpandDepth - 1
+ : 0,
+ object: null,
+ });
+
+ this.setState(state => ({
+ ...state,
+ bodyJsonMl,
+ open,
+ }));
+ } else {
+ this.setState(state => ({
+ ...state,
+ bodyJsonMl: null,
+ open,
+ }));
+ }
+ }
+
+ render() {
+ if (this.state && this.state.hasError) {
+ return createElement(
+ "span",
+ {
+ className: "objectBox objectBox-failure",
+ title:
+ "This object could not be rendered, " +
+ "please file a bug on bugzilla.mozilla.org",
+ },
+ "Invalid custom formatter object"
+ );
+ }
+
+ const headerJsonMl = renderJsonMl(this.props.object.header, {
+ ...this.props,
+ open: this.state?.open,
+ });
+
+ return createElement(
+ "span",
+ {
+ className: "objectBox-jsonml-wrapper",
+ "data-expandable": this.props.object.hasBody,
+ "aria-expanded": this.state.open,
+ onClick: this.props.object.hasBody ? this.toggleBody : null,
+ },
+ headerJsonMl,
+ this.state.bodyJsonMl
+ ? createElement(
+ "div",
+ { className: "objectBox-jsonml-body-wrapper" },
+ this.state.bodyJsonMl
+ )
+ : null
+ );
+ }
+ }
+
+ function renderJsonMl(jsonMl, props, index = 0) {
+ // The second item of the array can either be an object holding the attributes
+ // for the element or the first child element. Therefore, all array items after the
+ // first one are fetched together and split afterwards if needed.
+ let [tagName, ...attributesAndChildren] = jsonMl ?? [];
+
+ if (!ALLOWED_TAGS.has(tagName)) {
+ tagName = "div";
+ }
+
+ const attributes = attributesAndChildren[0];
+ const hasAttributes =
+ Object(attributes) === attributes && !Array.isArray(attributes);
+ const style =
+ hasAttributes && attributes?.style && props.createElement
+ ? cleanupStyle(attributes.style, props.createElement)
+ : null;
+ const children = attributesAndChildren;
+ if (hasAttributes) {
+ children.shift();
+ }
+
+ const childElements = [];
+
+ if (props.object?.hasBody) {
+ childElements.push(
+ createElement("button", {
+ "aria-expanded": props.open,
+ className: `collapse-button jsonml-header-collapse-button${
+ props.open ? " expanded" : ""
+ }`,
+ })
+ );
+ }
+
+ if (Array.isArray(children)) {
+ children.forEach((child, childIndex) => {
+ let childElement;
+ // If the child is an array, it should be a JsonML item, so use this function to
+ // render them.
+ if (Array.isArray(child)) {
+ childElement = renderJsonMl(
+ child,
+ { ...props, object: null },
+ childIndex
+ );
+ } else if (typeof child === "object" && child !== null) {
+ // If we don't have an array, this means that we're probably dealing with
+ // a front or a grip. If the object has a `getGrip` function, call it to get the
+ // actual grip.
+ const gripOrPrimitive =
+ (child.typeName == "obj" || child.typeName == "string") &&
+ typeof child?.getGrip == "function"
+ ? child.getGrip()
+ : child;
+
+ // If the grip represents an object that was custom formatted, we should render
+ // it using this component.
+ if (supportsObject(gripOrPrimitive)) {
+ childElement = createElement(CustomFormatter, {
+ ...props,
+ object: gripOrPrimitive,
+ front: child && !!child.typeName ? child : null,
+ });
+ } else {
+ // Here we have a non custom-formatted grip, so we let the ObjectInspector
+ // handles it.
+ const {
+ objectInspector,
+ MODE,
+ } = require("devtools/client/shared/components/reps/index");
+ childElement = createElement(objectInspector.ObjectInspector, {
+ ...props,
+ mode: props.mode == MODE.LONG ? MODE.SHORT : MODE.TINY,
+ roots: [
+ {
+ path: `${
+ gripOrPrimitive?.actorID ?? gripOrPrimitive?.actor ?? null
+ }`,
+ contents: {
+ value: gripOrPrimitive,
+ front: child && !!child.typeName ? child : null,
+ },
+ },
+ ],
+ });
+ }
+ } else {
+ // Here we have a primitive. We don't want to use Rep to render them as reps come
+ // with their own styling which might clash with the style defined in the JsonMl.
+ childElement = child;
+ }
+ childElements.push(childElement);
+ });
+ } else {
+ childElements.push(children);
+ }
+
+ return createElement(
+ tagName,
+ {
+ className: `objectBox objectBox-jsonml`,
+ key: `jsonml-${tagName}-${index}`,
+ style,
+ },
+ childElements
+ );
+ }
+
+ function supportsObject(grip) {
+ return grip?.useCustomFormatter === true && Array.isArray(grip?.header);
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: createFactory(CustomFormatter),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/date-time.js b/devtools/client/shared/components/reps/reps/date-time.js
new file mode 100644
index 0000000000..36d15fcbaa
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/date-time.js
@@ -0,0 +1,95 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Used to render JS built-in Date() object.
+ */
+
+ DateTime.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function DateTime(props) {
+ const { object: grip, shouldRenderTooltip } = props;
+ let date;
+ try {
+ const dateObject = new Date(grip.preview.timestamp);
+ // Calling `toISOString` will throw if the date is invalid,
+ // so we can render an `Invalid Date` element.
+ dateObject.toISOString();
+
+ const dateObjectString = dateObject.toString();
+
+ const config = getElementConfig({
+ grip,
+ dateObjectString,
+ shouldRenderTooltip,
+ });
+
+ date = span(
+ config,
+ getTitle(grip),
+ span({ className: "Date" }, dateObjectString)
+ );
+ } catch (e) {
+ date = span(
+ {
+ className: "objectBox",
+ title: shouldRenderTooltip ? "Invalid Date" : null,
+ },
+ "Invalid Date"
+ );
+ }
+
+ return date;
+ }
+
+ function getElementConfig(opts) {
+ const { grip, dateObjectString, shouldRenderTooltip } = opts;
+
+ return {
+ "data-link-actor-id": grip.actor,
+ className: "objectBox",
+ title: shouldRenderTooltip ? `${grip.class} ${dateObjectString}` : null,
+ };
+ }
+
+ // getTitle() is used to render the `Date ` before the stringified date object,
+ // not to render the actual span "title".
+
+ function getTitle(grip) {
+ return span(
+ {
+ className: "objectTitle",
+ },
+ `${grip.class} `
+ );
+ }
+
+ // Registration
+ function supportsObject(grip, noGrip = false) {
+ return getGripType(grip, noGrip) == "Date" && grip?.preview;
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(DateTime),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/document-type.js b/devtools/client/shared/components/reps/reps/document-type.js
new file mode 100644
index 0000000000..36442858fc
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/document-type.js
@@ -0,0 +1,60 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders DOM documentType object.
+ */
+
+ DocumentType.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function DocumentType(props) {
+ const { object, shouldRenderTooltip } = props;
+ const name =
+ object && object.preview && object.preview.nodeName
+ ? ` ${object.preview.nodeName}`
+ : "";
+
+ const config = getElementConfig({ object, shouldRenderTooltip, name });
+
+ return span(config, `<!DOCTYPE${name}>`);
+ }
+
+ function getElementConfig(opts) {
+ const { object, shouldRenderTooltip, name } = opts;
+
+ return {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-document",
+ title: shouldRenderTooltip ? `<!DOCTYPE${name}>` : null,
+ };
+ }
+
+ // Registration
+ function supportsObject(object, noGrip = false) {
+ return object?.preview && getGripType(object, noGrip) === "DocumentType";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(DocumentType),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/document.js b/devtools/client/shared/components/reps/reps/document.js
new file mode 100644
index 0000000000..1ee4eeb9a7
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/document.js
@@ -0,0 +1,79 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getGripType,
+ getURLDisplayString,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders DOM document object.
+ */
+
+ Document.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function Document(props) {
+ const grip = props.object;
+ const shouldRenderTooltip = props.shouldRenderTooltip;
+ const location = getLocation(grip);
+ const config = getElementConfig({ grip, location, shouldRenderTooltip });
+ return span(
+ config,
+ getTitle(grip),
+ location ? span({ className: "location" }, ` ${location}`) : null
+ );
+ }
+
+ function getElementConfig(opts) {
+ const { grip, location, shouldRenderTooltip } = opts;
+ const config = {
+ "data-link-actor-id": grip.actor,
+ className: "objectBox objectBox-document",
+ };
+
+ if (!shouldRenderTooltip || !location) {
+ return config;
+ }
+ config.title = `${grip.class} ${location}`;
+ return config;
+ }
+
+ function getLocation(grip) {
+ const location = grip.preview.location;
+ return location ? getURLDisplayString(location) : null;
+ }
+
+ function getTitle(grip) {
+ return span(
+ {
+ className: "objectTitle",
+ },
+ grip.class
+ );
+ }
+
+ // Registration
+ function supportsObject(object, noGrip = false) {
+ return object?.preview && getGripType(object, noGrip) === "HTMLDocument";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(Document),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/element-node.js b/devtools/client/shared/components/reps/reps/element-node.js
new file mode 100644
index 0000000000..a31fb4225b
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/element-node.js
@@ -0,0 +1,322 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const {
+ button,
+ span,
+ } = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ // Utils
+ const {
+ appendRTLClassNameIfNeeded,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ rep: StringRep,
+ isLongString,
+ } = require("devtools/client/shared/components/reps/reps/string");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+ const nodeConstants = require("devtools/client/shared/components/reps/shared/dom-node-constants");
+
+ const MAX_ATTRIBUTE_LENGTH = 50;
+
+ /**
+ * Renders DOM element node.
+ */
+
+ ElementNode.propTypes = {
+ object: PropTypes.object.isRequired,
+ // The class should be in reps.css
+ inspectIconTitle: PropTypes.oneOf(["open-inspector", "highlight-node"]),
+ inspectIconClassName: PropTypes.string,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ onDOMNodeClick: PropTypes.func,
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function ElementNode(props) {
+ const { object, mode, shouldRenderTooltip } = props;
+
+ const {
+ isAfterPseudoElement,
+ isBeforePseudoElement,
+ isMarkerPseudoElement,
+ } = object.preview;
+
+ let renderElements = [];
+ const isInTree = object.preview && object.preview.isConnected === true;
+ let config = getElementConfig({ ...props, isInTree });
+ const inspectIcon = getInspectIcon({ ...props, isInTree });
+
+ // Elements Case 1: Pseudo Element
+ if (
+ isAfterPseudoElement ||
+ isBeforePseudoElement ||
+ isMarkerPseudoElement
+ ) {
+ const pseudoNodeElement = getPseudoNodeElement(object);
+
+ // Regenerate config if shouldRenderTooltip
+ if (shouldRenderTooltip) {
+ const tooltipString = pseudoNodeElement.content;
+ config = getElementConfig({ ...props, tooltipString, isInTree });
+ }
+
+ // Return ONLY pseudo node element as array[0]
+ renderElements = [
+ span(pseudoNodeElement.config, pseudoNodeElement.content),
+ ];
+ } else if (mode === MODE.TINY) {
+ // Elements Case 2: MODE.TINY
+ const tinyElements = getTinyElements(object);
+
+ // Regenerate config to include tooltip title
+ if (shouldRenderTooltip) {
+ // Reduce for plaintext
+ const tooltipString = tinyElements.reduce(function (acc, cur) {
+ return acc.concat(cur.content);
+ }, "");
+
+ config = getElementConfig({ ...props, tooltipString, isInTree });
+ }
+
+ // Reduce for React elements
+ const tinyElementsRender = tinyElements.reduce(function (acc, cur) {
+ acc.push(span(cur.config, cur.content));
+ return acc;
+ }, []);
+
+ // Render array of React spans
+ renderElements = tinyElementsRender;
+ } else {
+ // Elements Default case
+ renderElements = getElements(props);
+ }
+
+ return span(config, ...renderElements, inspectIcon ? inspectIcon : null);
+ }
+
+ function getElementConfig(opts) {
+ const {
+ object,
+ isInTree,
+ onDOMNodeClick,
+ onDOMNodeMouseOver,
+ onDOMNodeMouseOut,
+ shouldRenderTooltip,
+ tooltipString,
+ } = opts;
+
+ // Initiate config
+ const config = {
+ "data-link-actor-id": object.actor,
+ "data-link-content-dom-reference": JSON.stringify(
+ object.contentDomReference
+ ),
+ className: "objectBox objectBox-node",
+ };
+
+ // Triage event handlers
+ if (isInTree) {
+ if (onDOMNodeClick) {
+ Object.assign(config, {
+ onClick: _ => onDOMNodeClick(object),
+ className: `${config.className} clickable`,
+ });
+ }
+
+ if (onDOMNodeMouseOver) {
+ Object.assign(config, {
+ onMouseOver: _ => onDOMNodeMouseOver(object),
+ });
+ }
+
+ if (onDOMNodeMouseOut) {
+ Object.assign(config, {
+ onMouseOut: _ => onDOMNodeMouseOut(object),
+ });
+ }
+ }
+
+ // If tooltip, build tooltip
+ if (tooltipString && shouldRenderTooltip) {
+ config.title = tooltipString;
+ }
+
+ // Return config obj
+ return config;
+ }
+
+ function getElements(opts) {
+ const { object: grip } = opts;
+
+ const { attributes, nodeName } = grip.preview;
+
+ const nodeNameElement = span(
+ {
+ className: appendRTLClassNameIfNeeded("tag-name", nodeName),
+ },
+ nodeName
+ );
+
+ const attributeKeys = Object.keys(attributes);
+ if (attributeKeys.includes("class")) {
+ attributeKeys.splice(attributeKeys.indexOf("class"), 1);
+ attributeKeys.unshift("class");
+ }
+ if (attributeKeys.includes("id")) {
+ attributeKeys.splice(attributeKeys.indexOf("id"), 1);
+ attributeKeys.unshift("id");
+ }
+ const attributeElements = attributeKeys.reduce((arr, name, i, keys) => {
+ const value = attributes[name];
+
+ let title = isLongString(value) ? value.initial : value;
+ if (title.length < MAX_ATTRIBUTE_LENGTH) {
+ title = null;
+ }
+
+ const attribute = span(
+ {},
+ span(
+ {
+ className: appendRTLClassNameIfNeeded("attrName", name),
+ },
+ name
+ ),
+ span({ className: "attrEqual" }, "="),
+ StringRep({
+ className: "attrValue",
+ object: value,
+ cropLimit: MAX_ATTRIBUTE_LENGTH,
+ title,
+ })
+ );
+
+ return arr.concat([" ", attribute]);
+ }, []);
+
+ return [
+ span({ className: "angleBracket" }, "<"),
+ nodeNameElement,
+ ...attributeElements,
+ span({ className: "angleBracket" }, ">"),
+ ];
+ }
+
+ function getTinyElements(grip) {
+ const { attributes, nodeName } = grip.preview;
+
+ // Initialize elements array
+ const elements = [
+ {
+ config: {
+ className: appendRTLClassNameIfNeeded("tag-name", nodeName),
+ },
+ content: nodeName,
+ },
+ ];
+
+ // Push ID element
+ if (attributes.id) {
+ elements.push({
+ config: {
+ className: appendRTLClassNameIfNeeded("attrName", attributes.id),
+ },
+ content: `#${attributes.id}`,
+ });
+ }
+
+ // Push Classes
+ if (attributes.class) {
+ const elementClasses = attributes.class
+ .trim()
+ .split(/\s+/)
+ .map(cls => `.${cls}`)
+ .join("");
+ elements.push({
+ config: {
+ className: appendRTLClassNameIfNeeded("attrName", elementClasses),
+ },
+ content: elementClasses,
+ });
+ }
+
+ return elements;
+ }
+
+ function getPseudoNodeElement(grip) {
+ const {
+ isAfterPseudoElement,
+ isBeforePseudoElement,
+ isMarkerPseudoElement,
+ } = grip.preview;
+
+ let pseudoNodeName;
+
+ if (isAfterPseudoElement) {
+ pseudoNodeName = "after";
+ } else if (isBeforePseudoElement) {
+ pseudoNodeName = "before";
+ } else if (isMarkerPseudoElement) {
+ pseudoNodeName = "marker";
+ }
+
+ return {
+ config: { className: "attrName" },
+ content: `::${pseudoNodeName}`,
+ };
+ }
+
+ function getInspectIcon(opts) {
+ const {
+ object,
+ isInTree,
+ onInspectIconClick,
+ inspectIconTitle,
+ inspectIconClassName,
+ onDOMNodeClick,
+ } = opts;
+
+ if (!isInTree || !onInspectIconClick) {
+ return null;
+ }
+
+ return button({
+ className: inspectIconClassName || "open-inspector",
+ // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
+ title: inspectIconTitle || "Click to select the node in the inspector",
+ onClick: e => {
+ if (onDOMNodeClick) {
+ e.stopPropagation();
+ }
+
+ onInspectIconClick(object, e);
+ },
+ });
+ }
+
+ // Registration
+ function supportsObject(object) {
+ return object?.preview?.nodeType === nodeConstants.ELEMENT_NODE;
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(ElementNode),
+ supportsObject,
+ MAX_ATTRIBUTE_LENGTH,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/error.js b/devtools/client/shared/components/reps/reps/error.js
new file mode 100644
index 0000000000..617bf8c8a2
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/error.js
@@ -0,0 +1,338 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const {
+ div,
+ span,
+ } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Utils
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ cleanFunctionName,
+ } = require("devtools/client/shared/components/reps/reps/function");
+ const {
+ isLongString,
+ } = require("devtools/client/shared/components/reps/reps/string");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ const IGNORED_SOURCE_URLS = ["debugger eval code"];
+
+ /**
+ * Renders Error objects.
+ */
+ ErrorRep.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ // An optional function that will be used to render the Error stacktrace.
+ renderStacktrace: PropTypes.func,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ /**
+ * Render an Error object.
+ * The customFormat prop allows to print a simplified view of the object, with only the
+ * message and the stacktrace, e.g.:
+ * Error: "blah"
+ * <anonymous> debugger eval code:1
+ *
+ * The customFormat prop will only be taken into account if the mode isn't tiny and the
+ * depth is 0. This is because we don't want error in previews or in object to be
+ * displayed unlike other objects:
+ * - Object { err: Error }
+ * - â–¼ {
+ * err: Error: "blah"
+ * }
+ */
+ function ErrorRep(props) {
+ const { object, mode, shouldRenderTooltip, depth } = props;
+ const preview = object.preview;
+ const customFormat =
+ props.customFormat &&
+ mode !== MODE.TINY &&
+ mode !== MODE.HEADER &&
+ !depth;
+
+ const name = getErrorName(props);
+ const errorTitle =
+ mode === MODE.TINY || mode === MODE.HEADER ? name : `${name}: `;
+ const content = [];
+
+ if (customFormat) {
+ content.push(errorTitle);
+ } else {
+ content.push(
+ span({ className: "objectTitle", key: "title" }, errorTitle)
+ );
+ }
+
+ if (mode !== MODE.TINY && mode !== MODE.HEADER) {
+ const {
+ Rep,
+ } = require("devtools/client/shared/components/reps/reps/rep");
+ content.push(
+ Rep({
+ ...props,
+ key: "message",
+ object: preview.message,
+ mode: props.mode || MODE.TINY,
+ useQuotes: false,
+ })
+ );
+ }
+ const renderStack = preview.stack && customFormat;
+ if (renderStack) {
+ const stacktrace = props.renderStacktrace
+ ? props.renderStacktrace(parseStackString(preview.stack))
+ : getStacktraceElements(props, preview);
+ content.push(stacktrace);
+ }
+
+ const renderCause = customFormat && preview.hasOwnProperty("cause");
+ if (renderCause) {
+ content.push(getCauseElement(props, preview));
+ }
+
+ return span(
+ {
+ "data-link-actor-id": object.actor,
+ className: `objectBox-stackTrace ${
+ customFormat ? "reps-custom-format" : ""
+ }`,
+ title: shouldRenderTooltip ? `${name}: "${preview.message}"` : null,
+ },
+ ...content
+ );
+ }
+
+ function getErrorName(props) {
+ const { object } = props;
+ const preview = object.preview;
+
+ let name;
+ if (typeof preview?.name === "string" && preview.kind) {
+ switch (preview.kind) {
+ case "Error":
+ name = preview.name;
+ break;
+ case "DOMException":
+ name = preview.kind;
+ break;
+ default:
+ throw new Error("Unknown preview kind for the Error rep.");
+ }
+ } else {
+ name = "Error";
+ }
+
+ return name;
+ }
+
+ /**
+ * Returns a React element reprensenting the Error stacktrace, i.e.
+ * transform error.stack from:
+ *
+ * semicolon@debugger eval code:1:109
+ * jkl@debugger eval code:1:63
+ * asdf@debugger eval code:1:28
+ * @debugger eval code:1:227
+ *
+ * Into a column layout:
+ *
+ * semicolon (<anonymous>:8:10)
+ * jkl (<anonymous>:5:10)
+ * asdf (<anonymous>:2:10)
+ * (<anonymous>:11:1)
+ */
+ function getStacktraceElements(props, preview) {
+ const stack = [];
+ if (!preview.stack) {
+ return stack;
+ }
+
+ parseStackString(preview.stack).forEach((frame, index, frames) => {
+ let onLocationClick;
+ const { filename, lineNumber, columnNumber, functionName, location } =
+ frame;
+
+ if (
+ props.onViewSourceInDebugger &&
+ !IGNORED_SOURCE_URLS.includes(filename)
+ ) {
+ onLocationClick = e => {
+ // Don't trigger ObjectInspector expand/collapse.
+ e.stopPropagation();
+ props.onViewSourceInDebugger({
+ url: filename,
+ line: lineNumber,
+ column: columnNumber,
+ });
+ };
+ }
+
+ stack.push(
+ "\t",
+ span(
+ {
+ key: `fn${index}`,
+ className: "objectBox-stackTrace-fn",
+ },
+ cleanFunctionName(functionName)
+ ),
+ " ",
+ span(
+ {
+ key: `location${index}`,
+ className: "objectBox-stackTrace-location",
+ onClick: onLocationClick,
+ title: onLocationClick
+ ? `View source in debugger → ${location}`
+ : undefined,
+ },
+ location
+ ),
+ "\n"
+ );
+ });
+
+ return span(
+ {
+ key: "stack",
+ className: "objectBox-stackTrace-grid",
+ },
+ stack
+ );
+ }
+
+ /**
+ * Returns a React element representing the cause of the Error i.e. the `cause`
+ * property in the second parameter of the Error constructor (`new Error("message", { cause })`)
+ *
+ * Example:
+ * Caused by: Error: original error
+ */
+ function getCauseElement(props, preview) {
+ const { Rep } = require("devtools/client/shared/components/reps/reps/rep");
+ return div(
+ {
+ key: "cause-container",
+ className: "error-rep-cause",
+ },
+ "Caused by: ",
+ Rep({
+ ...props,
+ key: "cause",
+ object: preview.cause,
+ mode: props.mode || MODE.TINY,
+ })
+ );
+ }
+
+ /**
+ * Parse a string that should represent a stack trace and returns an array of
+ * the frames. The shape of the frames are extremely important as they can then
+ * be processed here or in the toolbox by other components.
+ * @param {String} stack
+ * @returns {Array} Array of frames, which are object with the following shape:
+ * - {String} filename
+ * - {String} functionName
+ * - {String} location
+ * - {Number} columnNumber
+ * - {Number} lineNumber
+ */
+ function parseStackString(stack) {
+ if (!stack) {
+ return [];
+ }
+
+ const isStacktraceALongString = isLongString(stack);
+ const stackString = isStacktraceALongString ? stack.initial : stack;
+
+ if (typeof stackString !== "string") {
+ return [];
+ }
+
+ const res = [];
+ stackString.split("\n").forEach((frame, index, frames) => {
+ if (!frame) {
+ // Skip any blank lines
+ return;
+ }
+
+ // If the stacktrace is a longString, don't include the last frame in the
+ // array, since it is certainly incomplete.
+ // Can be removed when https://bugzilla.mozilla.org/show_bug.cgi?id=1448833
+ // is fixed.
+ if (isStacktraceALongString && index === frames.length - 1) {
+ return;
+ }
+
+ let functionName;
+ let location;
+
+ // Retrieve the index of the first @ to split the frame string.
+ const atCharIndex = frame.indexOf("@");
+ if (atCharIndex > -1) {
+ functionName = frame.slice(0, atCharIndex);
+ location = frame.slice(atCharIndex + 1);
+ }
+
+ if (location && location.includes(" -> ")) {
+ // If the resource was loaded by base-loader.sys.mjs, the location looks like:
+ // resource://devtools/shared/base-loader.sys.mjs -> resource://path/to/file.js .
+ // What's needed is only the last part after " -> ".
+ location = location.split(" -> ").pop();
+ }
+
+ if (!functionName) {
+ functionName = "<anonymous>";
+ }
+
+ // Given the input: "scriptLocation:2:100"
+ // Result:
+ // ["scriptLocation:2:100", "scriptLocation", "2", "100"]
+ const locationParts = location
+ ? location.match(/^(.*):(\d+):(\d+)$/)
+ : null;
+
+ if (location && locationParts) {
+ const [, filename, line, column] = locationParts;
+ res.push({
+ filename,
+ functionName,
+ location,
+ columnNumber: Number(column),
+ lineNumber: Number(line),
+ });
+ }
+ });
+
+ return res;
+ }
+
+ // Registration
+ function supportsObject(object) {
+ return (
+ object?.isError ||
+ object?.class === "DOMException" ||
+ object?.class === "Exception"
+ );
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(ErrorRep),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/event.js b/devtools/client/shared/components/reps/reps/event.js
new file mode 100644
index 0000000000..460f7e8c31
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/event.js
@@ -0,0 +1,115 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ // Reps
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+ const { rep } = require("devtools/client/shared/components/reps/reps/grip");
+
+ /**
+ * Renders DOM event objects.
+ */
+ Event.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ };
+
+ function Event(props) {
+ const gripProps = {
+ ...props,
+ title: getTitle(props),
+ object: {
+ ...props.object,
+ preview: {
+ ...props.object.preview,
+ ownProperties: {},
+ },
+ },
+ };
+
+ if (gripProps.object.preview.target) {
+ Object.assign(gripProps.object.preview.ownProperties, {
+ target: gripProps.object.preview.target,
+ });
+ }
+ Object.assign(
+ gripProps.object.preview.ownProperties,
+ gripProps.object.preview.properties
+ );
+
+ delete gripProps.object.preview.properties;
+ gripProps.object.ownPropertyLength = Object.keys(
+ gripProps.object.preview.ownProperties
+ ).length;
+
+ switch (gripProps.object.class) {
+ case "MouseEvent":
+ gripProps.isInterestingProp = (type, value, name) => {
+ return ["target", "clientX", "clientY", "layerX", "layerY"].includes(
+ name
+ );
+ };
+ break;
+ case "KeyboardEvent":
+ gripProps.isInterestingProp = (type, value, name) => {
+ return ["target", "key", "charCode", "keyCode"].includes(name);
+ };
+ break;
+ case "MessageEvent":
+ gripProps.isInterestingProp = (type, value, name) => {
+ return ["target", "isTrusted", "data"].includes(name);
+ };
+ break;
+ default:
+ gripProps.isInterestingProp = (type, value, name) => {
+ // We want to show the properties in the order they are declared.
+ return Object.keys(gripProps.object.preview.ownProperties).includes(
+ name
+ );
+ };
+ }
+
+ return rep(gripProps);
+ }
+
+ function getTitle(props) {
+ const preview = props.object.preview;
+ let title = preview.type;
+
+ if (
+ preview.eventKind == "key" &&
+ preview.modifiers &&
+ preview.modifiers.length
+ ) {
+ title = `${title} ${preview.modifiers.join("-")}`;
+ }
+ return title;
+ }
+
+ // Registration
+ function supportsObject(grip) {
+ return grip?.preview?.kind == "DOMEvent";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(Event),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/function.js b/devtools/client/shared/components/reps/reps/function.js
new file mode 100644
index 0000000000..54d8905c20
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/function.js
@@ -0,0 +1,264 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const {
+ button,
+ span,
+ } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getGripType,
+ cropString,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ const IGNORED_SOURCE_URLS = ["debugger eval code"];
+
+ /**
+ * This component represents a template for Function objects.
+ */
+
+ FunctionRep.propTypes = {
+ object: PropTypes.object.isRequired,
+ onViewSourceInDebugger: PropTypes.func,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function FunctionRep(props) {
+ const {
+ object: grip,
+ onViewSourceInDebugger,
+ recordTelemetryEvent,
+ shouldRenderTooltip,
+ } = props;
+
+ let jumpToDefinitionButton;
+
+ // Test to see if we should display the link back to the original function definition
+ if (
+ onViewSourceInDebugger &&
+ grip.location &&
+ grip.location.url &&
+ !IGNORED_SOURCE_URLS.includes(grip.location.url)
+ ) {
+ jumpToDefinitionButton = button({
+ className: "jump-definition",
+ draggable: false,
+ title: "Jump to definition",
+ onClick: async e => {
+ // Stop the event propagation so we don't trigger ObjectInspector
+ // expand/collapse.
+ e.stopPropagation();
+ if (recordTelemetryEvent) {
+ recordTelemetryEvent("jump_to_definition");
+ }
+
+ onViewSourceInDebugger(grip.location);
+ },
+ });
+ }
+
+ const elProps = {
+ "data-link-actor-id": grip.actor,
+ className: "objectBox objectBox-function",
+ // Set dir="ltr" to prevent parentheses from
+ // appearing in the wrong direction
+ dir: "ltr",
+ };
+
+ const parameterNames = (grip.parameterNames || []).filter(Boolean);
+ const fnTitle = getFunctionTitle(grip, props);
+ const fnName = getFunctionName(grip, props);
+
+ if (grip.isClassConstructor) {
+ const classTitle = getClassTitle(grip, props);
+ const classBodyTooltip = getClassBody(parameterNames, true, props);
+ const classTooltip = `${classTitle ? classTitle.props.children : ""}${
+ fnName ? fnName : ""
+ }${classBodyTooltip.join("")}`;
+
+ elProps.title = shouldRenderTooltip ? classTooltip : null;
+
+ return span(
+ elProps,
+ classTitle,
+ fnName,
+ ...getClassBody(parameterNames, false, props),
+ jumpToDefinitionButton
+ );
+ }
+
+ const fnTooltip = `${fnTitle ? fnTitle.props.children : ""}${
+ fnName ? fnName : ""
+ }(${parameterNames.join(", ")})`;
+
+ elProps.title = shouldRenderTooltip ? fnTooltip : null;
+
+ const returnSpan = span(
+ elProps,
+ fnTitle,
+ fnName,
+ "(",
+ ...getParams(parameterNames),
+ ")",
+ jumpToDefinitionButton
+ );
+
+ return returnSpan;
+ }
+
+ function getClassTitle(grip) {
+ return span(
+ {
+ className: "objectTitle",
+ },
+ "class "
+ );
+ }
+
+ function getFunctionTitle(grip, props) {
+ const { mode } = props;
+
+ if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
+ return null;
+ }
+
+ let title = mode === MODE.TINY ? "" : "function ";
+
+ if (grip.isGenerator) {
+ title = mode === MODE.TINY ? "* " : "function* ";
+ }
+
+ if (grip.isAsync) {
+ title = `${"async" + " "}${title}`;
+ }
+
+ return span(
+ {
+ className: "objectTitle",
+ },
+ title
+ );
+ }
+
+ /**
+ * Returns a ReactElement representing the function name.
+ *
+ * @param {Object} grip : Function grip
+ * @param {Object} props: Function rep props
+ */
+ function getFunctionName(grip, props = {}) {
+ let { functionName } = props;
+ let name;
+
+ if (functionName) {
+ const end = functionName.length - 1;
+ functionName =
+ functionName.startsWith('"') && functionName.endsWith('"')
+ ? functionName.substring(1, end)
+ : functionName;
+ }
+
+ if (
+ grip.displayName != undefined &&
+ functionName != undefined &&
+ grip.displayName != functionName
+ ) {
+ name = `${functionName}:${grip.displayName}`;
+ } else {
+ name = cleanFunctionName(
+ grip.userDisplayName ||
+ grip.displayName ||
+ grip.name ||
+ props.functionName ||
+ ""
+ );
+ }
+
+ return cropString(name, 100);
+ }
+
+ const objectProperty = /([\w\d\$]+)$/;
+ const arrayProperty = /\[(.*?)\]$/;
+ const functionProperty = /([\w\d]+)[\/\.<]*?$/;
+ const annonymousProperty = /([\w\d]+)\(\^\)$/;
+
+ /**
+ * Decodes an anonymous naming scheme that
+ * spider monkey implements based on "Naming Anonymous JavaScript Functions"
+ * http://johnjbarton.github.io/nonymous/index.html
+ *
+ * @param {String} name : Function name to clean up
+ * @returns String
+ */
+ function cleanFunctionName(name) {
+ for (const reg of [
+ objectProperty,
+ arrayProperty,
+ functionProperty,
+ annonymousProperty,
+ ]) {
+ const match = reg.exec(name);
+ if (match) {
+ return match[1];
+ }
+ }
+
+ return name;
+ }
+
+ function getClassBody(constructorParams, textOnly = false, props) {
+ const { mode } = props;
+
+ if (mode === MODE.TINY) {
+ return [];
+ }
+
+ return [" {", ...getClassConstructor(textOnly, constructorParams), "}"];
+ }
+
+ function getClassConstructor(textOnly = false, parameterNames) {
+ if (parameterNames.length === 0) {
+ return [];
+ }
+
+ if (textOnly) {
+ return [` constructor(${parameterNames.join(", ")}) `];
+ }
+ return [" constructor(", ...getParams(parameterNames), ") "];
+ }
+
+ function getParams(parameterNames) {
+ return parameterNames.flatMap((param, index, arr) => {
+ return [
+ span({ className: "param" }, param),
+ index === arr.length - 1 ? "" : span({ className: "delimiter" }, ", "),
+ ];
+ });
+ }
+
+ // Registration
+ function supportsObject(grip, noGrip = false) {
+ return getGripType(grip, noGrip) === "Function";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(FunctionRep),
+ supportsObject,
+ cleanFunctionName,
+ // exported for testing purpose.
+ getFunctionName,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/grip-array.js b/devtools/client/shared/components/reps/reps/grip-array.js
new file mode 100644
index 0000000000..e9c67039a4
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/grip-array.js
@@ -0,0 +1,263 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const {
+ lengthBubble,
+ } = require("devtools/client/shared/components/reps/shared/grip-length-bubble");
+ const {
+ interleave,
+ getGripType,
+ wrapRender,
+ ellipsisElement,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ const DEFAULT_TITLE = "Array";
+
+ /**
+ * Renders an array. The array is enclosed by left and right bracket
+ * and the max number of rendered items depends on the current mode.
+ */
+
+ GripArray.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ provider: PropTypes.object,
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function GripArray(props) {
+ const { object, mode = MODE.SHORT, shouldRenderTooltip } = props;
+
+ let brackets;
+ const needSpace = function (space) {
+ return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+ };
+
+ const config = {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-array",
+ title: shouldRenderTooltip ? "Array" : null,
+ };
+
+ const title = getTitle(props, object);
+
+ if (mode === MODE.TINY) {
+ const isEmpty = getLength(object) === 0;
+
+ // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
+ if (!isEmpty && object.class !== "Array") {
+ return span(config, title);
+ }
+
+ brackets = needSpace(false);
+ return span(
+ config,
+ title,
+ span(
+ {
+ className: "arrayLeftBracket",
+ },
+ brackets.left
+ ),
+ isEmpty ? null : ellipsisElement,
+ span(
+ {
+ className: "arrayRightBracket",
+ },
+ brackets.right
+ )
+ );
+ }
+
+ if (mode === MODE.HEADER) {
+ return span(config, title);
+ }
+
+ const max = maxLengthMap.get(mode);
+ const items = arrayIterator(props, object, max);
+ brackets = needSpace(!!items.length);
+
+ return span(
+ config,
+ title,
+ span(
+ {
+ className: "arrayLeftBracket",
+ },
+ brackets.left
+ ),
+ ...interleave(items, ", "),
+ span(
+ {
+ className: "arrayRightBracket",
+ },
+ brackets.right
+ ),
+ span({
+ className: "arrayProperties",
+ role: "group",
+ })
+ );
+ }
+
+ function getLength(grip) {
+ if (!grip.preview) {
+ return 0;
+ }
+
+ return grip.preview.length || grip.preview.childNodesLength || 0;
+ }
+
+ function getTitle(props, object) {
+ const objectLength = getLength(object);
+ const isEmpty = objectLength === 0;
+
+ let title = props.title || object.class || DEFAULT_TITLE;
+
+ const length = lengthBubble({
+ object,
+ mode: props.mode,
+ maxLengthMap,
+ getLength,
+ });
+
+ if (props.mode === MODE.TINY) {
+ if (isEmpty) {
+ if (object.class === DEFAULT_TITLE) {
+ return null;
+ }
+
+ return span({ className: "objectTitle" }, `${title} `);
+ }
+
+ let trailingSpace;
+ if (object.class === DEFAULT_TITLE) {
+ title = null;
+ trailingSpace = " ";
+ }
+
+ return span({ className: "objectTitle" }, title, length, trailingSpace);
+ }
+
+ if (props.mode === MODE.HEADER) {
+ return span({ className: "objectTitle" }, title, length);
+ }
+
+ return span({ className: "objectTitle" }, title, length, " ");
+ }
+
+ function getPreviewItems(grip) {
+ if (!grip.preview) {
+ return null;
+ }
+
+ return grip.preview.items || grip.preview.childNodes || [];
+ }
+
+ function arrayIterator(props, grip, max) {
+ const { Rep } = require("devtools/client/shared/components/reps/reps/rep");
+
+ let items = [];
+ const gripLength = getLength(grip);
+
+ if (!gripLength) {
+ return items;
+ }
+
+ const previewItems = getPreviewItems(grip);
+ const provider = props.provider;
+
+ let emptySlots = 0;
+ let foldedEmptySlots = 0;
+ items = previewItems.reduce((res, itemGrip) => {
+ if (res.length >= max) {
+ return res;
+ }
+
+ let object;
+ try {
+ if (!provider && itemGrip === null) {
+ emptySlots++;
+ return res;
+ }
+
+ object = provider ? provider.getValue(itemGrip) : itemGrip;
+ } catch (exc) {
+ object = exc;
+ }
+
+ if (emptySlots > 0) {
+ res.push(getEmptySlotsElement(emptySlots));
+ foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+ emptySlots = 0;
+ }
+
+ if (res.length < max) {
+ res.push(
+ Rep({
+ ...props,
+ object,
+ mode: MODE.TINY,
+ // Do not propagate title to array items reps
+ title: undefined,
+ })
+ );
+ }
+
+ return res;
+ }, []);
+
+ // Handle trailing empty slots if there are some.
+ if (items.length < max && emptySlots > 0) {
+ items.push(getEmptySlotsElement(emptySlots));
+ foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+ }
+
+ const itemsShown = items.length + foldedEmptySlots;
+ if (gripLength > itemsShown) {
+ items.push(ellipsisElement);
+ }
+
+ return items;
+ }
+
+ function getEmptySlotsElement(number) {
+ // TODO: Use l10N - See https://github.com/firefox-devtools/reps/issues/141
+ return `<${number} empty slot${number > 1 ? "s" : ""}>`;
+ }
+
+ function supportsObject(grip, noGrip = false) {
+ return (
+ grip?.preview &&
+ (grip.preview.kind == "ArrayLike" ||
+ getGripType(grip, noGrip) === "DocumentFragment")
+ );
+ }
+
+ const maxLengthMap = new Map();
+ maxLengthMap.set(MODE.SHORT, 3);
+ maxLengthMap.set(MODE.LONG, 10);
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(GripArray),
+ supportsObject,
+ maxLengthMap,
+ getLength,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/grip-entry.js b/devtools/client/shared/components/reps/reps/grip-entry.js
new file mode 100644
index 0000000000..6c08ca5d70
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/grip-entry.js
@@ -0,0 +1,78 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+ // Utils
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const PropRep = require("devtools/client/shared/components/reps/reps/prop-rep");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ /**
+ * Renders an entry of a Map, (Local|Session)Storage, Header or FormData entry.
+ */
+ GripEntry.propTypes = {
+ object: PropTypes.object,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ };
+
+ function GripEntry(props) {
+ const { object } = props;
+
+ let { key, value } = object.preview;
+ if (key && key.getGrip) {
+ key = key.getGrip();
+ }
+ if (value && value.getGrip) {
+ value = value.getGrip();
+ }
+
+ return span(
+ {
+ className: "objectBox objectBox-map-entry",
+ },
+ PropRep({
+ ...props,
+ name: key,
+ object: value,
+ equal: " \u2192 ",
+ title: null,
+ suppressQuotes: false,
+ })
+ );
+ }
+
+ function supportsObject(grip, noGrip = false) {
+ if (noGrip === true) {
+ return false;
+ }
+ return (
+ grip &&
+ (grip.type === "formDataEntry" ||
+ grip.type === "highlightRegistryEntry" ||
+ grip.type === "mapEntry" ||
+ grip.type === "storageEntry" ||
+ grip.type === "urlSearchParamsEntry") &&
+ grip.preview
+ );
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(GripEntry),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/grip-map.js b/devtools/client/shared/components/reps/reps/grip-map.js
new file mode 100644
index 0000000000..dcb7c50972
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/grip-map.js
@@ -0,0 +1,234 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const {
+ lengthBubble,
+ } = require("devtools/client/shared/components/reps/shared/grip-length-bubble");
+ const {
+ interleave,
+ wrapRender,
+ ellipsisElement,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const PropRep = require("devtools/client/shared/components/reps/reps/prop-rep");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ /**
+ * Renders an map. A map is represented by a list of its
+ * entries enclosed in curly brackets.
+ */
+
+ GripMap.propTypes = {
+ object: PropTypes.object,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ isInterestingEntry: PropTypes.func,
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ title: PropTypes.string,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function GripMap(props) {
+ const { mode, object, shouldRenderTooltip } = props;
+
+ const config = {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-object",
+ title: shouldRenderTooltip ? getTooltip(object, props) : null,
+ };
+
+ const title = getTitle(props, object);
+ const isEmpty = getLength(object) === 0;
+
+ if (isEmpty || mode === MODE.TINY || mode === MODE.HEADER) {
+ return span(config, title);
+ }
+
+ const propsArray = safeEntriesIterator(
+ props,
+ object,
+ maxLengthMap.get(mode)
+ );
+
+ return span(
+ config,
+ title,
+ span(
+ {
+ className: "objectLeftBrace",
+ },
+ " { "
+ ),
+ ...interleave(propsArray, ", "),
+ span(
+ {
+ className: "objectRightBrace",
+ },
+ " }"
+ )
+ );
+ }
+
+ function getTitle(props, object) {
+ const title =
+ props.title || (object && object.class ? object.class : "Map");
+ return span(
+ {
+ className: "objectTitle",
+ },
+ title,
+ lengthBubble({
+ object,
+ mode: props.mode,
+ maxLengthMap,
+ getLength,
+ showZeroLength: true,
+ })
+ );
+ }
+
+ function getTooltip(object, props) {
+ const tooltip =
+ props.title || (object && object.class ? object.class : "Map");
+ return `${tooltip}(${getLength(object)})`;
+ }
+
+ function safeEntriesIterator(props, object, max) {
+ max = typeof max === "undefined" ? 3 : max;
+ try {
+ return entriesIterator(props, object, max);
+ } catch (err) {
+ console.error(err);
+ }
+ return [];
+ }
+
+ function entriesIterator(props, object, max) {
+ // Entry filter. Show only interesting entries to the user.
+ const isInterestingEntry =
+ props.isInterestingEntry ||
+ ((type, value) => {
+ return (
+ type == "boolean" ||
+ type == "number" ||
+ (type == "string" && !!value.length)
+ );
+ });
+
+ const mapEntries =
+ object.preview && object.preview.entries ? object.preview.entries : [];
+
+ let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
+ if (indexes.length < max && indexes.length < mapEntries.length) {
+ // There are not enough entries yet, so we add uninteresting entries.
+ indexes = indexes.concat(
+ getEntriesIndexes(
+ mapEntries,
+ max - indexes.length,
+ (t, value, name) => {
+ return !isInterestingEntry(t, value, name);
+ }
+ )
+ );
+ }
+
+ const entries = getEntries(props, mapEntries, indexes);
+ if (entries.length < getLength(object)) {
+ // There are some undisplayed entries. Then display "…".
+ entries.push(ellipsisElement);
+ }
+
+ return entries;
+ }
+
+ /**
+ * Get entries ordered by index.
+ *
+ * @param {Object} props Component props.
+ * @param {Array} entries Entries array.
+ * @param {Array} indexes Indexes of entries.
+ * @return {Array} Array of PropRep.
+ */
+ function getEntries(props, entries, indexes) {
+ const { onDOMNodeMouseOver, onDOMNodeMouseOut, onInspectIconClick } = props;
+
+ // Make indexes ordered by ascending.
+ indexes.sort(function (a, b) {
+ return a - b;
+ });
+
+ return indexes.map((index, i) => {
+ const [key, entryValue] = entries[index];
+ const value =
+ entryValue.value !== undefined ? entryValue.value : entryValue;
+
+ return PropRep({
+ name: key && key.getGrip ? key.getGrip() : key,
+ equal: " \u2192 ",
+ object: value && value.getGrip ? value.getGrip() : value,
+ mode: MODE.TINY,
+ onDOMNodeMouseOver,
+ onDOMNodeMouseOut,
+ onInspectIconClick,
+ });
+ });
+ }
+
+ /**
+ * Get the indexes of entries in the map.
+ *
+ * @param {Array} entries Entries array.
+ * @param {Number} max The maximum length of indexes array.
+ * @param {Function} filter Filter the entry you want.
+ * @return {Array} Indexes of filtered entries in the map.
+ */
+ function getEntriesIndexes(entries, max, filter) {
+ return entries.reduce((indexes, [key, entry], i) => {
+ if (indexes.length < max) {
+ const value = entry && entry.value !== undefined ? entry.value : entry;
+ // Type is specified in grip's "class" field and for primitive
+ // values use typeof.
+ const type = (
+ value && value.class ? value.class : typeof value
+ ).toLowerCase();
+
+ if (filter(type, value, key)) {
+ indexes.push(i);
+ }
+ }
+
+ return indexes;
+ }, []);
+ }
+
+ function getLength(grip) {
+ return grip.preview.size || 0;
+ }
+
+ function supportsObject(grip) {
+ return grip?.preview?.kind == "MapLike";
+ }
+
+ const maxLengthMap = new Map();
+ maxLengthMap.set(MODE.SHORT, 3);
+ maxLengthMap.set(MODE.LONG, 10);
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(GripMap),
+ supportsObject,
+ maxLengthMap,
+ getLength,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/grip.js b/devtools/client/shared/components/reps/reps/grip.js
new file mode 100644
index 0000000000..68f356858a
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/grip.js
@@ -0,0 +1,401 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Dependencies
+ const {
+ interleave,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const PropRep = require("devtools/client/shared/components/reps/reps/prop-rep");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ /**
+ * Renders generic grip. Grip is client representation
+ * of remote JS object and is used as an input object
+ * for this rep component.
+ */
+
+ GripRep.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ isInterestingProp: PropTypes.func,
+ title: PropTypes.string,
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ noGrip: PropTypes.bool,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ const DEFAULT_TITLE = "Object";
+
+ function GripRep(props) {
+ const { mode = MODE.SHORT, object, shouldRenderTooltip } = props;
+
+ const config = {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-object",
+ };
+
+ if (mode === MODE.TINY) {
+ const propertiesLength = getPropertiesLength(object);
+
+ const tinyModeItems = [];
+ if (getTitle(props, object) !== DEFAULT_TITLE) {
+ tinyModeItems.push(getTitleElement(props, object));
+ } else {
+ tinyModeItems.push(
+ span(
+ {
+ className: "objectLeftBrace",
+ },
+ "{"
+ ),
+ propertiesLength > 0
+ ? span(
+ {
+ key: "more",
+ className: "more-ellipsis",
+ },
+ "…"
+ )
+ : null,
+ span(
+ {
+ className: "objectRightBrace",
+ },
+ "}"
+ )
+ );
+ }
+
+ config.title = shouldRenderTooltip ? getTitle(props, object) : null;
+
+ return span(config, ...tinyModeItems);
+ }
+
+ if (mode === MODE.HEADER) {
+ config.title = shouldRenderTooltip ? getTitle(props, object) : null;
+ return span(config, getTitleElement(props, object));
+ }
+
+ const propsArray = safePropIterator(props, object, maxLengthMap.get(mode));
+
+ config.title = shouldRenderTooltip ? getTitle(props, object) : null;
+
+ return span(
+ config,
+ getTitleElement(props, object),
+ span(
+ {
+ className: "objectLeftBrace",
+ },
+ " { "
+ ),
+ ...interleave(propsArray, ", "),
+ span(
+ {
+ className: "objectRightBrace",
+ },
+ " }"
+ )
+ );
+ }
+
+ function getTitleElement(props, object) {
+ return span(
+ {
+ className: "objectTitle",
+ },
+ getTitle(props, object)
+ );
+ }
+
+ function getTitle(props, object) {
+ return props.title || object.class || DEFAULT_TITLE;
+ }
+
+ function getPropertiesLength(object) {
+ let propertiesLength =
+ object.preview && object.preview.ownPropertiesLength
+ ? object.preview.ownPropertiesLength
+ : object.ownPropertyLength;
+
+ if (object.preview && object.preview.safeGetterValues) {
+ propertiesLength += Object.keys(object.preview.safeGetterValues).length;
+ }
+
+ if (object.preview && object.preview.ownSymbols) {
+ propertiesLength += object.preview.ownSymbolsLength;
+ }
+
+ if (object.preview && object.preview.privateProperties) {
+ propertiesLength += object.preview.privatePropertiesLength;
+ }
+
+ return propertiesLength;
+ }
+
+ function safePropIterator(props, object, max) {
+ max = typeof max === "undefined" ? maxLengthMap.get(MODE.SHORT) : max;
+ try {
+ return propIterator(props, object, max);
+ } catch (err) {
+ console.error(err);
+ }
+ return [];
+ }
+
+ function propIterator(props, object, max) {
+ if (
+ object.preview &&
+ Object.keys(object.preview).includes("wrappedValue")
+ ) {
+ const {
+ Rep,
+ } = require("devtools/client/shared/components/reps/reps/rep");
+
+ return [
+ Rep({
+ object: object.preview.wrappedValue,
+ mode: props.mode || MODE.TINY,
+ defaultRep: Grip,
+ }),
+ ];
+ }
+
+ // Property filter. Show only interesting properties to the user.
+ const isInterestingProp =
+ props.isInterestingProp ||
+ ((type, value) => {
+ return (
+ type == "boolean" ||
+ type == "number" ||
+ (type == "string" && !!value.length)
+ );
+ });
+
+ let properties = object.preview ? object.preview.ownProperties || {} : {};
+
+ const propertiesLength = getPropertiesLength(object);
+
+ if (object.preview && object.preview.safeGetterValues) {
+ properties = { ...properties, ...object.preview.safeGetterValues };
+ }
+
+ let indexes = getPropIndexes(properties, max, isInterestingProp);
+ if (indexes.length < max && indexes.length < propertiesLength) {
+ // There are not enough props yet.
+ // Then add uninteresting props to display them.
+ indexes = indexes.concat(
+ getPropIndexes(properties, max - indexes.length, (t, value, name) => {
+ return !isInterestingProp(t, value, name);
+ })
+ );
+ }
+
+ // The server synthesizes some property names for a Proxy, like
+ // <target> and <handler>; we don't want to quote these because,
+ // as synthetic properties, they appear more natural when
+ // unquoted. Analogous for a Promise.
+ const suppressQuotes = ["Proxy", "Promise"].includes(object.class);
+ const propsArray = getProps(props, properties, indexes, suppressQuotes);
+
+ // Show private properties
+ if (object.preview && object.preview.privateProperties) {
+ const { privateProperties } = object.preview;
+ const length = max - indexes.length;
+
+ const privateProps = privateProperties.slice(0, length).map(item => {
+ const value = item.descriptor.value;
+ const grip = value && value.getGrip ? value.getGrip() : value;
+
+ return PropRep({
+ ...props,
+ keyClassName: "private",
+ mode: MODE.TINY,
+ name: item.name,
+ object: grip,
+ equal: ": ",
+ defaultRep: Grip,
+ title: null,
+ suppressQuotes: true,
+ });
+ });
+
+ propsArray.push(...privateProps);
+ }
+
+ // Show symbols.
+ if (object.preview && object.preview.ownSymbols) {
+ const { ownSymbols } = object.preview;
+ const length = max - indexes.length;
+
+ const symbolsProps = ownSymbols.slice(0, length).map(symbolItem => {
+ const symbolValue = symbolItem.descriptor.value;
+ const symbolGrip =
+ symbolValue && symbolValue.getGrip
+ ? symbolValue.getGrip()
+ : symbolValue;
+
+ return PropRep({
+ ...props,
+ mode: MODE.TINY,
+ name: symbolItem,
+ object: symbolGrip,
+ equal: ": ",
+ defaultRep: Grip,
+ title: null,
+ suppressQuotes,
+ });
+ });
+
+ propsArray.push(...symbolsProps);
+ }
+
+ if (
+ Object.keys(properties).length > max ||
+ propertiesLength > max ||
+ // When the object has non-enumerable properties, we don't have them in the
+ // packet, but we might want to show there's something in the object.
+ propertiesLength > propsArray.length
+ ) {
+ // There are some undisplayed props. Then display "more...".
+ propsArray.push(
+ span(
+ {
+ key: "more",
+ className: "more-ellipsis",
+ },
+ "…"
+ )
+ );
+ }
+
+ return propsArray;
+ }
+
+ /**
+ * Get props ordered by index.
+ *
+ * @param {Object} componentProps Grip Component props.
+ * @param {Object} properties Properties of the object the Grip describes.
+ * @param {Array} indexes Indexes of properties.
+ * @param {Boolean} suppressQuotes true if we should suppress quotes
+ * on property names.
+ * @return {Array} Props.
+ */
+ function getProps(componentProps, properties, indexes, suppressQuotes) {
+ // Make indexes ordered by ascending.
+ indexes.sort(function (a, b) {
+ return a - b;
+ });
+
+ const propertiesKeys = Object.keys(properties);
+ return indexes.map(i => {
+ const name = propertiesKeys[i];
+ const value = getPropValue(properties[name]);
+
+ return PropRep({
+ ...componentProps,
+ mode: MODE.TINY,
+ name,
+ object: value,
+ equal: ": ",
+ defaultRep: Grip,
+ title: null,
+ suppressQuotes,
+ });
+ });
+ }
+
+ /**
+ * Get the indexes of props in the object.
+ *
+ * @param {Object} properties Props object.
+ * @param {Number} max The maximum length of indexes array.
+ * @param {Function} filter Filter the props you want.
+ * @return {Array} Indexes of interesting props in the object.
+ */
+ function getPropIndexes(properties, max, filter) {
+ const indexes = [];
+
+ try {
+ let i = 0;
+ for (const name in properties) {
+ if (indexes.length >= max) {
+ return indexes;
+ }
+
+ // Type is specified in grip's "class" field and for primitive
+ // values use typeof.
+ const value = getPropValue(properties[name]);
+ let type = value.class || typeof value;
+ type = type.toLowerCase();
+
+ if (filter(type, value, name)) {
+ indexes.push(i);
+ }
+ i++;
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ return indexes;
+ }
+
+ /**
+ * Get the actual value of a property.
+ *
+ * @param {Object} property
+ * @return {Object} Value of the property.
+ */
+ function getPropValue(property) {
+ let value = property;
+ if (typeof property === "object") {
+ const keys = Object.keys(property);
+ if (keys.includes("value")) {
+ value = property.value;
+ } else if (keys.includes("getterValue")) {
+ value = property.getterValue;
+ }
+ }
+ return value;
+ }
+
+ // Registration
+ function supportsObject(object, noGrip = false) {
+ if (object?.class === "DeadObject") {
+ return true;
+ }
+
+ return object?.preview
+ ? typeof object.preview.ownProperties !== "undefined"
+ : typeof object?.ownPropertyLength !== "undefined";
+ }
+
+ const maxLengthMap = new Map();
+ maxLengthMap.set(MODE.SHORT, 3);
+ maxLengthMap.set(MODE.LONG, 10);
+
+ // Grip is used in propIterator and has to be defined here.
+ const Grip = {
+ rep: wrapRender(GripRep),
+ supportsObject,
+ maxLengthMap,
+ };
+
+ // Exports from this module
+ module.exports = Grip;
+});
diff --git a/devtools/client/shared/components/reps/reps/infinity.js b/devtools/client/shared/components/reps/reps/infinity.js
new file mode 100644
index 0000000000..2a36698611
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/infinity.js
@@ -0,0 +1,52 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a Infinity object
+ */
+
+ InfinityRep.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function InfinityRep(props) {
+ const { object, shouldRenderTooltip } = props;
+
+ const config = getElementConfig(shouldRenderTooltip, object);
+
+ return span(config, object.type);
+ }
+
+ function getElementConfig(shouldRenderTooltip, object) {
+ return {
+ className: "objectBox objectBox-number",
+ title: shouldRenderTooltip ? object.type : null,
+ };
+ }
+
+ function supportsObject(object, noGrip = false) {
+ const type = getGripType(object, noGrip);
+ return type == "Infinity" || type == "-Infinity";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(InfinityRep),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/moz.build b/devtools/client/shared/components/reps/reps/moz.build
new file mode 100644
index 0000000000..30b7e72a73
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/moz.build
@@ -0,0 +1,45 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "accessible.js",
+ "accessor.js",
+ "array.js",
+ "attribute.js",
+ "big-int.js",
+ "comment-node.js",
+ "constants.js",
+ "custom-formatter.js",
+ "date-time.js",
+ "document-type.js",
+ "document.js",
+ "element-node.js",
+ "error.js",
+ "event.js",
+ "function.js",
+ "grip-array.js",
+ "grip-entry.js",
+ "grip-map.js",
+ "grip.js",
+ "infinity.js",
+ "nan.js",
+ "null.js",
+ "number.js",
+ "object-with-text.js",
+ "object-with-url.js",
+ "object.js",
+ "promise.js",
+ "prop-rep.js",
+ "regexp.js",
+ "rep-utils.js",
+ "rep.js",
+ "string.js",
+ "stylesheet.js",
+ "symbol.js",
+ "text-node.js",
+ "undefined.js",
+ "window.js",
+)
diff --git a/devtools/client/shared/components/reps/reps/nan.js b/devtools/client/shared/components/reps/reps/nan.js
new file mode 100644
index 0000000000..d8b022177c
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/nan.js
@@ -0,0 +1,51 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a NaN object
+ */
+
+ NaNRep.PropTypes = {
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function NaNRep(props) {
+ const shouldRenderTooltip = props.shouldRenderTooltip;
+
+ const config = getElementConfig(shouldRenderTooltip);
+
+ return span(config, "NaN");
+ }
+
+ function getElementConfig(shouldRenderTooltip) {
+ return {
+ className: "objectBox objectBox-nan",
+ title: shouldRenderTooltip ? "NaN" : null,
+ };
+ }
+
+ function supportsObject(object, noGrip = false) {
+ return getGripType(object, noGrip) == "NaN";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(NaNRep),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/null.js b/devtools/client/shared/components/reps/reps/null.js
new file mode 100644
index 0000000000..53dca6b8f3
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/null.js
@@ -0,0 +1,59 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders null value
+ */
+
+ Null.PropTypes = {
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function Null(props) {
+ const shouldRenderTooltip = props.shouldRenderTooltip;
+
+ const config = getElementConfig(shouldRenderTooltip);
+
+ return span(config, "null");
+ }
+
+ function getElementConfig(shouldRenderTooltip) {
+ return {
+ className: "objectBox objectBox-null",
+ title: shouldRenderTooltip ? "null" : null,
+ };
+ }
+
+ function supportsObject(object, noGrip = false) {
+ if (noGrip === true) {
+ return object === null;
+ }
+
+ if (object && object.type && object.type == "null") {
+ return true;
+ }
+
+ return object == null;
+ }
+
+ // Exports from this module
+
+ module.exports = {
+ rep: wrapRender(Null),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/number.js b/devtools/client/shared/components/reps/reps/number.js
new file mode 100644
index 0000000000..455cb971e7
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/number.js
@@ -0,0 +1,63 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a number
+ */
+
+ Number.propTypes = {
+ object: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.number,
+ PropTypes.bool,
+ ]).isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function Number(props) {
+ const value = stringify(props.object);
+ const config = getElementConfig(props.shouldRenderTooltip, value);
+
+ return span(config, value);
+ }
+
+ function stringify(object) {
+ const isNegativeZero =
+ Object.is(object, -0) || (object.type && object.type == "-0");
+
+ return isNegativeZero ? "-0" : String(object);
+ }
+
+ function getElementConfig(shouldRenderTooltip, value) {
+ return {
+ className: "objectBox objectBox-number",
+ title: shouldRenderTooltip ? value : null,
+ };
+ }
+
+ const SUPPORTED_TYPES = new Set(["boolean", "number", "-0"]);
+ function supportsObject(object, noGrip = false) {
+ return SUPPORTED_TYPES.has(getGripType(object, noGrip));
+ }
+
+ // Exports from this module
+
+ module.exports = {
+ rep: wrapRender(Number),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/object-with-text.js b/devtools/client/shared/components/reps/reps/object-with-text.js
new file mode 100644
index 0000000000..5dc3f27ae8
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/object-with-text.js
@@ -0,0 +1,70 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ const String =
+ require("devtools/client/shared/components/reps/reps/string").rep;
+
+ /**
+ * Renders a grip object with textual data.
+ */
+
+ ObjectWithText.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function ObjectWithText(props) {
+ const grip = props.object;
+ const config = getElementConfig(props);
+
+ return span(config, `${getType(grip)} `, getDescription(grip));
+ }
+
+ function getElementConfig(opts) {
+ const shouldRenderTooltip = opts.shouldRenderTooltip;
+ const grip = opts.object;
+
+ return {
+ "data-link-actor-id": grip.actor,
+ className: `objectTitle objectBox objectBox-${getType(grip)}`,
+ title: shouldRenderTooltip
+ ? `${getType(grip)} "${grip.preview.text}"`
+ : null,
+ };
+ }
+
+ function getType(grip) {
+ return grip.class;
+ }
+
+ function getDescription(grip) {
+ return String({
+ object: grip.preview.text,
+ });
+ }
+
+ // Registration
+ function supportsObject(grip) {
+ return grip?.preview?.kind == "ObjectWithText";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(ObjectWithText),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/object-with-url.js b/devtools/client/shared/components/reps/reps/object-with-url.js
new file mode 100644
index 0000000000..0d0660cacf
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/object-with-url.js
@@ -0,0 +1,73 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getURLDisplayString,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a grip object with URL data.
+ */
+
+ ObjectWithURL.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function ObjectWithURL(props) {
+ const grip = props.object;
+ const config = getElementConfig(props);
+
+ return span(
+ config,
+ getTitle(grip),
+ span({ className: "objectPropValue" }, getDescription(grip))
+ );
+ }
+
+ function getElementConfig(opts) {
+ const grip = opts.object;
+ const shouldRenderTooltip = opts.shouldRenderTooltip;
+ const tooltip = `${getType(grip)} ${getDescription(grip)}`;
+
+ return {
+ "data-link-actor-id": grip.actor,
+ className: `objectBox objectBox-${getType(grip)}`,
+ title: shouldRenderTooltip ? tooltip : null,
+ };
+ }
+
+ function getTitle(grip) {
+ return span({ className: "objectTitle" }, `${getType(grip)} `);
+ }
+
+ function getType(grip) {
+ return grip.class;
+ }
+
+ function getDescription(grip) {
+ return getURLDisplayString(grip.preview.url);
+ }
+
+ // Registration
+ function supportsObject(grip) {
+ return grip?.preview?.kind == "ObjectWithURL";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(ObjectWithURL),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/object.js b/devtools/client/shared/components/reps/reps/object.js
new file mode 100644
index 0000000000..bdd98cfd04
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/object.js
@@ -0,0 +1,207 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const {
+ wrapRender,
+ ellipsisElement,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const PropRep = require("devtools/client/shared/components/reps/reps/prop-rep");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ const DEFAULT_TITLE = "Object";
+
+ /**
+ * Renders an object. An object is represented by a list of its
+ * properties enclosed in curly brackets.
+ */
+
+ ObjectRep.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ title: PropTypes.string,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function ObjectRep(props) {
+ const object = props.object;
+ const { shouldRenderTooltip = true } = props;
+
+ if (props.mode === MODE.TINY) {
+ const tinyModeItems = [];
+ if (getTitle(props) !== DEFAULT_TITLE) {
+ tinyModeItems.push(getTitleElement(props));
+ } else {
+ tinyModeItems.push(
+ span(
+ {
+ className: "objectLeftBrace",
+ },
+ "{"
+ ),
+ Object.keys(object).length ? ellipsisElement : null,
+ span(
+ {
+ className: "objectRightBrace",
+ },
+ "}"
+ )
+ );
+ }
+
+ return span(
+ {
+ className: "objectBox objectBox-object",
+ title: shouldRenderTooltip ? getTitle(props) : null,
+ },
+ ...tinyModeItems
+ );
+ }
+
+ const propsArray = safePropIterator(props, object);
+
+ return span(
+ {
+ className: "objectBox objectBox-object",
+ title: shouldRenderTooltip ? getTitle(props) : null,
+ },
+ getTitleElement(props),
+ span(
+ {
+ className: "objectLeftBrace",
+ },
+ " { "
+ ),
+ ...propsArray,
+ span(
+ {
+ className: "objectRightBrace",
+ },
+ " }"
+ )
+ );
+ }
+
+ function getTitleElement(props) {
+ return span({ className: "objectTitle" }, getTitle(props));
+ }
+
+ function getTitle(props) {
+ return props.title || DEFAULT_TITLE;
+ }
+
+ function safePropIterator(props, object, max) {
+ max = typeof max === "undefined" ? 3 : max;
+ try {
+ return propIterator(props, object, max);
+ } catch (err) {
+ console.error(err);
+ }
+ return [];
+ }
+
+ function propIterator(props, object, max) {
+ // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=945377
+ if (Object.prototype.toString.call(object) === "[object Generator]") {
+ object = Object.getPrototypeOf(object);
+ }
+
+ const elements = [];
+ const unimportantProperties = [];
+ let propertiesNumber = 0;
+ const propertiesNames = Object.keys(object);
+
+ const pushPropRep = (name, value) => {
+ elements.push(
+ PropRep({
+ ...props,
+ key: name,
+ mode: MODE.TINY,
+ name,
+ object: value,
+ equal: ": ",
+ })
+ );
+ propertiesNumber++;
+
+ if (propertiesNumber < propertiesNames.length) {
+ elements.push(", ");
+ }
+ };
+
+ try {
+ for (const name of propertiesNames) {
+ if (propertiesNumber >= max) {
+ break;
+ }
+
+ let value;
+ try {
+ value = object[name];
+ } catch (exc) {
+ continue;
+ }
+
+ // Object members with non-empty values are preferred since it gives the
+ // user a better overview of the object.
+ if (isInterestingProp(value)) {
+ pushPropRep(name, value);
+ } else {
+ // If the property is not important, put its name on an array for later
+ // use.
+ unimportantProperties.push(name);
+ }
+ }
+ } catch (err) {
+ console.error(err);
+ }
+
+ if (propertiesNumber < max) {
+ for (const name of unimportantProperties) {
+ if (propertiesNumber >= max) {
+ break;
+ }
+
+ let value;
+ try {
+ value = object[name];
+ } catch (exc) {
+ continue;
+ }
+
+ pushPropRep(name, value);
+ }
+ }
+
+ if (propertiesNumber < propertiesNames.length) {
+ elements.push(ellipsisElement);
+ }
+
+ return elements;
+ }
+
+ function isInterestingProp(value) {
+ const type = typeof value;
+ return type == "boolean" || type == "number" || (type == "string" && value);
+ }
+
+ function supportsObject(object, noGrip = false) {
+ return noGrip;
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(ObjectRep),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/promise.js b/devtools/client/shared/components/reps/reps/promise.js
new file mode 100644
index 0000000000..186f4201eb
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/promise.js
@@ -0,0 +1,105 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Dependencies
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ const Grip = require("devtools/client/shared/components/reps/reps/grip");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ /**
+ * Renders a DOM Promise object.
+ */
+
+ PromiseRep.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function PromiseRep(props) {
+ const object = props.object;
+
+ // @backward-compat { version 85 } On older servers, the preview of a promise was
+ // useless and didn't include the internal promise state, which was directly exposed
+ // in the grip.
+ if (object.promiseState) {
+ const { state, value, reason } = object.promiseState;
+ const ownProperties = Object.create(null);
+ ownProperties["<state>"] = { value: state };
+ let ownPropertiesLength = 1;
+ if (state == "fulfilled") {
+ ownProperties["<value>"] = { value };
+ ++ownPropertiesLength;
+ } else if (state == "rejected") {
+ ownProperties["<reason>"] = { value: reason };
+ ++ownPropertiesLength;
+ }
+ object.preview = {
+ kind: "Object",
+ ownProperties,
+ ownPropertiesLength,
+ };
+ }
+
+ if (props.mode !== MODE.TINY && props.mode !== MODE.HEADER) {
+ return Grip.rep(props);
+ }
+
+ const shouldRenderTooltip = props.shouldRenderTooltip;
+ const config = {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-object",
+ title: shouldRenderTooltip ? "Promise" : null,
+ };
+
+ if (props.mode === MODE.HEADER) {
+ return span(config, getTitle(object));
+ }
+
+ const { Rep } = require("devtools/client/shared/components/reps/reps/rep");
+
+ return span(
+ config,
+ getTitle(object),
+ span({ className: "objectLeftBrace" }, " { "),
+ Rep({ object: object.preview.ownProperties["<state>"].value }),
+ span({ className: "objectRightBrace" }, " }")
+ );
+ }
+
+ function getTitle(object) {
+ return span({ className: "objectTitle" }, object.class);
+ }
+
+ // Registration
+ function supportsObject(object, noGrip = false) {
+ if (!Grip.supportsObject(object, noGrip)) {
+ return false;
+ }
+ return getGripType(object, noGrip) == "Promise";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(PromiseRep),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/prop-rep.js b/devtools/client/shared/components/reps/reps/prop-rep.js
new file mode 100644
index 0000000000..271aea1e21
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/prop-rep.js
@@ -0,0 +1,106 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const {
+ appendRTLClassNameIfNeeded,
+ maybeEscapePropertyName,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ /**
+ * Property for Obj (local JS objects), Grip (remote JS objects)
+ * and GripMap (remote JS maps and weakmaps) reps.
+ * It's used to render object properties.
+ */
+ PropRep.propTypes = {
+ // Additional class to set on the key element
+ keyClassName: PropTypes.string,
+ // Property name.
+ name: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired,
+ // Equal character rendered between property name and value.
+ equal: PropTypes.string,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ // Normally a PropRep will quote a property name that isn't valid
+ // when unquoted; but this flag can be used to suppress the
+ // quoting.
+ suppressQuotes: PropTypes.bool,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ /**
+ * Function that given a name, a delimiter and an object returns an array
+ * of React elements representing an object property (e.g. `name: value`)
+ *
+ * @param {Object} props
+ * @return {Array} Array of React elements.
+ */
+
+ function PropRep(props) {
+ const Grip = require("devtools/client/shared/components/reps/reps/grip");
+ const { Rep } = require("devtools/client/shared/components/reps/reps/rep");
+
+ let {
+ equal,
+ keyClassName,
+ mode,
+ name,
+ shouldRenderTooltip,
+ suppressQuotes,
+ } = props;
+
+ const className = `nodeName${keyClassName ? " " + keyClassName : ""}`;
+
+ let key;
+ // The key can be a simple string, for plain objects,
+ // or another object for maps and weakmaps.
+ if (typeof name === "string") {
+ if (!suppressQuotes) {
+ name = maybeEscapePropertyName(name);
+ }
+ key = span(
+ {
+ className: appendRTLClassNameIfNeeded(className, name),
+ title: shouldRenderTooltip ? name : null,
+ },
+ name
+ );
+ } else {
+ key = Rep({
+ ...props,
+ className,
+ object: name,
+ mode: mode || MODE.TINY,
+ defaultRep: Grip,
+ });
+ }
+
+ return [
+ key,
+ span(
+ {
+ className: "objectEqual",
+ },
+ equal
+ ),
+ Rep({ ...props }),
+ ];
+ }
+
+ // Exports from this module
+ module.exports = wrapRender(PropRep);
+});
diff --git a/devtools/client/shared/components/reps/reps/regexp.js b/devtools/client/shared/components/reps/reps/regexp.js
new file mode 100644
index 0000000000..fc753e0fca
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/regexp.js
@@ -0,0 +1,66 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getGripType,
+ wrapRender,
+ ELLIPSIS,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a grip object with regular expression.
+ */
+
+ RegExp.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function RegExp(props) {
+ const { object } = props;
+ const config = getElementConfig(props);
+
+ return span(config, getSource(object));
+ }
+
+ function getElementConfig(opts) {
+ const { object, shouldRenderTooltip } = opts;
+ const text = getSource(object);
+
+ return {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-regexp regexpSource",
+ title: shouldRenderTooltip ? text : null,
+ };
+ }
+
+ function getSource(grip) {
+ const { displayString } = grip;
+ if (displayString?.type === "longString") {
+ return `${displayString.initial}${ELLIPSIS}`;
+ }
+
+ return displayString;
+ }
+
+ // Registration
+ function supportsObject(object, noGrip = false) {
+ return getGripType(object, noGrip) == "RegExp";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(RegExp),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/rep-utils.js b/devtools/client/shared/components/reps/reps/rep-utils.js
new file mode 100644
index 0000000000..6d148bb06b
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/rep-utils.js
@@ -0,0 +1,596 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const validProtocols = /(http|https|ftp|data|resource|chrome):/i;
+
+ // URL Regex, common idioms:
+ //
+ // Lead-in (URL):
+ // ( Capture because we need to know if there was a lead-in
+ // character so we can include it as part of the text
+ // preceding the match. We lack look-behind matching.
+ // ^| The URL can start at the beginning of the string.
+ // [\s(,;'"`“] Or whitespace or some punctuation that does not imply
+ // a context which would preclude a URL.
+ // )
+ //
+ // We do not need a trailing look-ahead because our regex's will terminate
+ // because they run out of characters they can eat.
+
+ // What we do not attempt to have the regexp do:
+ // - Avoid trailing '.' and ')' characters. We let our greedy match absorb
+ // these, but have a separate regex for extra characters to leave off at the
+ // end.
+ //
+ // The Regex (apart from lead-in/lead-out):
+ // ( Begin capture of the URL
+ // (?: (potential detect beginnings)
+ // https?:\/\/| Start with "http" or "https"
+ // www\d{0,3}[.][a-z0-9.\-]{2,249}|
+ // Start with "www", up to 3 numbers, then "." then
+ // something that looks domain-namey. We differ from the
+ // next case in that we do not constrain the top-level
+ // domain as tightly and do not require a trailing path
+ // indicator of "/". This is IDN root compatible.
+ // [a-z0-9.\-]{2,250}[.][a-z]{2,4}\/
+ // Detect a non-www domain, but requiring a trailing "/"
+ // to indicate a path. This only detects IDN domains
+ // with a non-IDN root. This is reasonable in cases where
+ // there is no explicit http/https start us out, but
+ // unreasonable where there is. Our real fix is the bug
+ // to port the Thunderbird/gecko linkification logic.
+ //
+ // Domain names can be up to 253 characters long, and are
+ // limited to a-zA-Z0-9 and '-'. The roots don't have
+ // hyphens unless they are IDN roots. Root zones can be
+ // found here: http://www.iana.org/domains/root/db
+ // )
+ // [-\w.!~*'();,/?:@&=+$#%]*
+ // path onwards. We allow the set of characters that
+ // encodeURI does not escape plus the result of escaping
+ // (so also '%')
+ // )
+ // eslint-disable-next-line max-len
+ const urlRegex =
+ /(^|[\s(,;'"`“])((?:https?:\/(\/)?|www\d{0,3}[.][a-z0-9.\-]{2,249}|[a-z0-9.\-]{2,250}[.][a-z]{2,4}\/)[-\w.!~*'();,/?:@&=+$#%]*)/im;
+
+ // Set of terminators that are likely to have been part of the context rather
+ // than part of the URL and so should be uneaten. This is '(', ',', ';', plus
+ // quotes and question end-ing punctuation and the potential permutations with
+ // parentheses (english-specific).
+ const uneatLastUrlCharsRegex = /(?:[),;.!?`'"]|[.!?]\)|\)[.!?])$/;
+
+ const ELLIPSIS = "\u2026";
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { span } = dom;
+
+ function escapeNewLines(value) {
+ return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
+ }
+
+ // Map from character code to the corresponding escape sequence. \0
+ // isn't here because it would require special treatment in some
+ // situations. \b, \f, and \v aren't here because they aren't very
+ // common. \' isn't here because there's no need, we only
+ // double-quote strings.
+ const escapeMap = {
+ // Tab.
+ 9: "\\t",
+ // Newline.
+ 0xa: "\\n",
+ // Carriage return.
+ 0xd: "\\r",
+ // Quote.
+ 0x22: '\\"',
+ // Backslash.
+ 0x5c: "\\\\",
+ };
+
+ // All characters we might possibly want to escape, excluding quotes.
+ // Note that we over-match here, because it's difficult to, say, match
+ // an unpaired surrogate with a regexp. The details are worked out by
+ // the replacement function; see |escapeString|.
+ const commonEscapes =
+ // Backslash.
+ "\\\\" +
+ // Controls.
+ "\x00-\x1f" +
+ // More controls.
+ "\x7f-\x9f" +
+ // BOM
+ "\ufeff" +
+ // Specials, except for the replacement character.
+ "\ufff0-\ufffc\ufffe\uffff" +
+ // Surrogates.
+ "\ud800-\udfff" +
+ // Mathematical invisibles.
+ "\u2061-\u2064" +
+ // Line and paragraph separators.
+ "\u2028-\u2029" +
+ // Private use area.
+ "\ue000-\uf8ff";
+ const escapeRegexp = new RegExp(`[${commonEscapes}]`, "g");
+ const escapeRegexpIncludingDoubleQuote = new RegExp(
+ `[${commonEscapes}"]`,
+ "g"
+ );
+
+ /**
+ * Escape a string so that the result is viewable and valid JS.
+ * Control characters, other invisibles, invalid characters, and backslash
+ * are escaped. The resulting string is quoted with either double quotes,
+ * single quotes, or backticks. The preference is for a quote that doesn't
+ * require escaping, falling back to double quotes if that's not possible
+ * (and then escaping them in the string).
+ *
+ * @param {String} str
+ * the input
+ * @param {Boolean} escapeWhitespace
+ * if true, TAB, CR, and NL characters will be escaped
+ * @return {String} the escaped string
+ */
+ function escapeString(str, escapeWhitespace) {
+ let quote = '"';
+ let regexp = escapeRegexp;
+ if (str.includes('"')) {
+ if (!str.includes("'")) {
+ quote = "'";
+ } else if (!str.includes("`") && !str.includes("${")) {
+ quote = "`";
+ } else {
+ regexp = escapeRegexpIncludingDoubleQuote;
+ }
+ }
+ return `${quote}${str.replace(regexp, (match, offset) => {
+ const c = match.charCodeAt(0);
+ if (c in escapeMap) {
+ if (!escapeWhitespace && (c === 9 || c === 0xa || c === 0xd)) {
+ return match[0];
+ }
+ return escapeMap[c];
+ }
+ if (c >= 0xd800 && c <= 0xdfff) {
+ // Find the full code point containing the surrogate, with a
+ // special case for a trailing surrogate at the start of the
+ // string.
+ if (c >= 0xdc00 && offset > 0) {
+ --offset;
+ }
+ const codePoint = str.codePointAt(offset);
+ if (codePoint >= 0xd800 && codePoint <= 0xdfff) {
+ // Unpaired surrogate.
+ return `\\u${codePoint.toString(16)}`;
+ } else if (codePoint >= 0xf0000 && codePoint <= 0x10fffd) {
+ // Private use area. Because we visit each pair of a such a
+ // character, return the empty string for one half and the
+ // real result for the other, to avoid duplication.
+ if (c <= 0xdbff) {
+ return `\\u{${codePoint.toString(16)}}`;
+ }
+ return "";
+ }
+ // Other surrogate characters are passed through.
+ return match;
+ }
+ return `\\u${`0000${c.toString(16)}`.substr(-4)}`;
+ })}${quote}`;
+ }
+
+ /**
+ * Escape a property name, if needed. "Escaping" in this context
+ * means surrounding the property name with quotes.
+ *
+ * @param {String}
+ * name the property name
+ * @return {String} either the input, or the input surrounded by
+ * quotes, properly quoted in JS syntax.
+ */
+ function maybeEscapePropertyName(name) {
+ // Quote the property name if it needs quoting. This particular
+ // test is an approximation; see
+ // https://mathiasbynens.be/notes/javascript-properties. However,
+ // the full solution requires a fair amount of Unicode data, and so
+ // let's defer that until either it's important, or the \p regexp
+ // syntax lands, see
+ // https://github.com/tc39/proposal-regexp-unicode-property-escapes.
+ if (!/^\w+$/.test(name)) {
+ name = escapeString(name);
+ }
+ return name;
+ }
+
+ function cropMultipleLines(text, limit) {
+ return escapeNewLines(cropString(text, limit));
+ }
+
+ function rawCropString(text, limit, alternativeText = ELLIPSIS) {
+ // Crop the string only if a limit is actually specified.
+ if (!limit || limit <= 0) {
+ return text;
+ }
+
+ // Set the limit at least to the length of the alternative text
+ // plus one character of the original text.
+ if (limit <= alternativeText.length) {
+ limit = alternativeText.length + 1;
+ }
+
+ const halfLimit = (limit - alternativeText.length) / 2;
+
+ if (text.length > limit) {
+ return (
+ text.substr(0, Math.ceil(halfLimit)) +
+ alternativeText +
+ text.substr(text.length - Math.floor(halfLimit))
+ );
+ }
+
+ return text;
+ }
+
+ function cropString(text, limit, alternativeText) {
+ return rawCropString(sanitizeString(`${text}`), limit, alternativeText);
+ }
+
+ function sanitizeString(text) {
+ // Replace all non-printable characters, except of
+ // (horizontal) tab (HT: \x09) and newline (LF: \x0A, CR: \x0D),
+ // with unicode replacement character (u+fffd).
+ // eslint-disable-next-line no-control-regex
+ const re = new RegExp("[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]", "g");
+ return text.replace(re, "\ufffd");
+ }
+
+ function parseURLParams(url) {
+ url = new URL(url);
+ return parseURLEncodedText(url.searchParams);
+ }
+
+ function parseURLEncodedText(text) {
+ const params = [];
+
+ // In case the text is empty just return the empty parameters
+ if (text == "") {
+ return params;
+ }
+
+ const searchParams = new URLSearchParams(text);
+ const entries = [...searchParams.entries()];
+ return entries.map(entry => {
+ return {
+ name: entry[0],
+ value: entry[1],
+ };
+ });
+ }
+
+ function getFileName(url) {
+ const split = splitURLBase(url);
+ return split.name;
+ }
+
+ function splitURLBase(url) {
+ if (!isDataURL(url)) {
+ return splitURLTrue(url);
+ }
+ return {};
+ }
+
+ function getURLDisplayString(url) {
+ return cropString(url);
+ }
+
+ function isDataURL(url) {
+ return url && url.substr(0, 5) == "data:";
+ }
+
+ function splitURLTrue(url) {
+ const reSplitFile = /(.*?):\/{2,3}([^\/]*)(.*?)([^\/]*?)($|\?.*)/;
+ const m = reSplitFile.exec(url);
+
+ if (!m) {
+ return {
+ name: url,
+ path: url,
+ };
+ } else if (m[4] == "" && m[5] == "") {
+ return {
+ protocol: m[1],
+ domain: m[2],
+ path: m[3],
+ name: m[3] != "/" ? m[3] : m[2],
+ };
+ }
+
+ return {
+ protocol: m[1],
+ domain: m[2],
+ path: m[2] + m[3],
+ name: m[4] + m[5],
+ };
+ }
+
+ /**
+ * Wrap the provided render() method of a rep in a try/catch block that will
+ * render a fallback rep if the render fails.
+ */
+ function wrapRender(renderMethod) {
+ const wrappedFunction = function (props) {
+ try {
+ return renderMethod.call(this, props);
+ } catch (e) {
+ console.error(e);
+ return span(
+ {
+ className: "objectBox objectBox-failure",
+ title:
+ "This object could not be rendered, " +
+ "please file a bug on bugzilla.mozilla.org",
+ },
+ /* Labels have to be hardcoded for reps, see Bug 1317038. */
+ "Invalid object"
+ );
+ }
+ };
+ wrappedFunction.propTypes = renderMethod.propTypes;
+ return wrappedFunction;
+ }
+
+ /**
+ * Get preview items from a Grip.
+ *
+ * @param {Object} Grip from which we want the preview items
+ * @return {Array} Array of the preview items of the grip, or an empty array
+ * if the grip does not have preview items
+ */
+ function getGripPreviewItems(grip) {
+ if (!grip) {
+ return [];
+ }
+
+ // Array Grip
+ if (grip.preview && grip.preview.items) {
+ return grip.preview.items;
+ }
+
+ // Node Grip
+ if (grip.preview && grip.preview.childNodes) {
+ return grip.preview.childNodes;
+ }
+
+ // Set or Map Grip
+ if (grip.preview && grip.preview.entries) {
+ return grip.preview.entries.reduce((res, entry) => res.concat(entry), []);
+ }
+
+ // Event Grip
+ if (grip.preview && grip.preview.target) {
+ const keys = Object.keys(grip.preview.properties);
+ const values = Object.values(grip.preview.properties);
+ return [grip.preview.target, ...keys, ...values];
+ }
+
+ // RegEx Grip
+ if (grip.displayString) {
+ return [grip.displayString];
+ }
+
+ // Generic Grip
+ if (grip.preview && grip.preview.ownProperties) {
+ let propertiesValues = Object.values(grip.preview.ownProperties).map(
+ property => property.value || property
+ );
+
+ const propertyKeys = Object.keys(grip.preview.ownProperties);
+ propertiesValues = propertiesValues.concat(propertyKeys);
+
+ // ArrayBuffer Grip
+ if (grip.preview.safeGetterValues) {
+ propertiesValues = propertiesValues.concat(
+ Object.values(grip.preview.safeGetterValues).map(
+ property => property.getterValue || property
+ )
+ );
+ }
+
+ return propertiesValues;
+ }
+
+ return [];
+ }
+
+ /**
+ * Get the type of an object.
+ *
+ * @param {Object} Grip from which we want the type.
+ * @param {boolean} noGrip true if the object is not a grip.
+ * @return {boolean}
+ */
+ function getGripType(object, noGrip) {
+ if (noGrip || Object(object) !== object) {
+ return typeof object;
+ }
+ if (object.type === "object") {
+ return object.class;
+ }
+ return object.type;
+ }
+
+ /**
+ * Determines whether a grip is a string containing a URL.
+ *
+ * @param string grip
+ * The grip, which may contain a URL.
+ * @return boolean
+ * Whether the grip is a string containing a URL.
+ */
+ function containsURL(grip) {
+ // An URL can't be shorter than 5 char (e.g. "ftp:").
+ if (typeof grip !== "string" || grip.length < 5) {
+ return false;
+ }
+
+ return validProtocols.test(grip);
+ }
+
+ /**
+ * Determines whether a string token is a valid URL.
+ *
+ * @param string token
+ * The token.
+ * @return boolean
+ * Whether the token is a URL.
+ */
+ function isURL(token) {
+ try {
+ if (!validProtocols.test(token)) {
+ return false;
+ }
+ new URL(token);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns new array in which `char` are interleaved between the original items.
+ *
+ * @param {Array} items
+ * @param {String} char
+ * @returns Array
+ */
+ function interleave(items, char) {
+ return items.reduce((res, item, index) => {
+ if (index !== items.length - 1) {
+ return res.concat(item, char);
+ }
+ return res.concat(item);
+ }, []);
+ }
+
+ const ellipsisElement = span(
+ {
+ key: "more",
+ className: "more-ellipsis",
+ title: `more${ELLIPSIS}`,
+ },
+ ELLIPSIS
+ );
+
+ /**
+ * Removes any unallowed CSS properties from a string of CSS declarations
+ *
+ * @param {String} userProvidedStyle CSS declarations
+ * @param {Function} createElement Method to create a dummy element the styles get applied to
+ * @returns {Object} Filtered CSS properties as JavaScript object in camelCase notation
+ */
+ function cleanupStyle(userProvidedStyle, createElement) {
+ // Regular expression that matches the allowed CSS property names.
+ const allowedStylesRegex = new RegExp(
+ "^(?:-moz-)?(?:align|background|border|box|clear|color|cursor|display|" +
+ "float|font|justify|line|margin|padding|position|text|transition" +
+ "|outline|vertical-align|white-space|word|writing|" +
+ "(?:min-|max-)?width|(?:min-|max-)?height)"
+ );
+
+ const mozElementRegex = /\b((?:-moz-)?element)[\s('"]+/gi;
+
+ // Regex to retrieve usages of `url(*)` in property value
+ const cssUrlRegex = /url\([\'\"]?([^\)]*)/g;
+
+ // Use a dummy element to parse the style string.
+ const dummy = createElement("div");
+ dummy.style = userProvidedStyle;
+
+ // Return a style object as expected by React DOM components, e.g.
+ // {color: "red"}
+ // without forbidden properties and values.
+ return Array.from(dummy.style)
+ .filter(name => {
+ if (!allowedStylesRegex.test(name)) {
+ return false;
+ }
+
+ if (mozElementRegex.test(name)) {
+ return false;
+ }
+
+ if (name === "position") {
+ return ["static", "relative"].includes(
+ dummy.style.getPropertyValue(name)
+ );
+ }
+ // There can be multiple call to `url()` (e.g.` background: url("path/to/image"), url("data:image/png,…");`);
+ // filter out the property if the url function is called with anything that is not
+ // a data URL.
+ return Array.from(dummy.style[name].matchAll(cssUrlRegex))
+ .map(match => match[1])
+ .every(potentialUrl => potentialUrl.startsWith("data:"));
+ })
+ .reduce((object, name) => {
+ // React requires CSS properties to be provided in JavaScript form, i.e. camelCased.
+ const jsName = name.replace(/-([a-z])/g, (_, char) =>
+ char.toUpperCase()
+ );
+ return Object.assign(
+ {
+ [jsName]: dummy.style.getPropertyValue(name),
+ },
+ object
+ );
+ }, {});
+ }
+
+ /**
+ * Append has-rtl-char to className if passed string has RTL chars.
+ * has-rtl-char is used in reps.css to set `unicode-bidi: isolate` on the element.
+ * It's important to only apply it when needed as this CSS property can have an
+ * important impact on performance (See Bug 1879806)
+ *
+ * @param {String} className: The className want to set on an element
+ * @param {String} strToCheck: The string for which we want to check if it has RTL chars
+ * @returns {String}
+ */
+ function appendRTLClassNameIfNeeded(className = "", strToCheck) {
+ if (
+ // The JSONViewer, which uses some Reps component, doesn't have access to Services.
+ typeof Services == "undefined" ||
+ !Services?.intl?.stringHasRTLChars(strToCheck)
+ ) {
+ return className;
+ }
+ return `${className} has-rtl-char`;
+ }
+
+ module.exports = {
+ interleave,
+ isURL,
+ cropString,
+ containsURL,
+ rawCropString,
+ appendRTLClassNameIfNeeded,
+ sanitizeString,
+ escapeString,
+ wrapRender,
+ cropMultipleLines,
+ parseURLParams,
+ parseURLEncodedText,
+ getFileName,
+ getURLDisplayString,
+ maybeEscapePropertyName,
+ getGripPreviewItems,
+ getGripType,
+ ellipsisElement,
+ ELLIPSIS,
+ uneatLastUrlCharsRegex,
+ urlRegex,
+ cleanupStyle,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/rep.js b/devtools/client/shared/components/reps/reps/rep.js
new file mode 100644
index 0000000000..10d719c42c
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/rep.js
@@ -0,0 +1,205 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Load all existing rep templates
+ const Undefined = require("devtools/client/shared/components/reps/reps/undefined");
+ const Null = require("devtools/client/shared/components/reps/reps/null");
+ const StringRep = require("devtools/client/shared/components/reps/reps/string");
+ const Number = require("devtools/client/shared/components/reps/reps/number");
+ const ArrayRep = require("devtools/client/shared/components/reps/reps/array");
+ const Obj = require("devtools/client/shared/components/reps/reps/object");
+ const SymbolRep = require("devtools/client/shared/components/reps/reps/symbol");
+ const InfinityRep = require("devtools/client/shared/components/reps/reps/infinity");
+ const NaNRep = require("devtools/client/shared/components/reps/reps/nan");
+ const Accessor = require("devtools/client/shared/components/reps/reps/accessor");
+
+ // DOM types (grips)
+ const Accessible = require("devtools/client/shared/components/reps/reps/accessible");
+ const Attribute = require("devtools/client/shared/components/reps/reps/attribute");
+ const BigInt = require("devtools/client/shared/components/reps/reps/big-int");
+ const DateTime = require("devtools/client/shared/components/reps/reps/date-time");
+ const Document = require("devtools/client/shared/components/reps/reps/document");
+ const DocumentType = require("devtools/client/shared/components/reps/reps/document-type");
+ const Event = require("devtools/client/shared/components/reps/reps/event");
+ const Func = require("devtools/client/shared/components/reps/reps/function");
+ const PromiseRep = require("devtools/client/shared/components/reps/reps/promise");
+ const RegExp = require("devtools/client/shared/components/reps/reps/regexp");
+ const StyleSheet = require("devtools/client/shared/components/reps/reps/stylesheet");
+ const CommentNode = require("devtools/client/shared/components/reps/reps/comment-node");
+ const ElementNode = require("devtools/client/shared/components/reps/reps/element-node");
+ const TextNode = require("devtools/client/shared/components/reps/reps/text-node");
+ const ErrorRep = require("devtools/client/shared/components/reps/reps/error");
+ const Window = require("devtools/client/shared/components/reps/reps/window");
+ const ObjectWithText = require("devtools/client/shared/components/reps/reps/object-with-text");
+ const ObjectWithURL = require("devtools/client/shared/components/reps/reps/object-with-url");
+ const GripArray = require("devtools/client/shared/components/reps/reps/grip-array");
+ const GripEntry = require("devtools/client/shared/components/reps/reps/grip-entry");
+ const GripMap = require("devtools/client/shared/components/reps/reps/grip-map");
+ const Grip = require("devtools/client/shared/components/reps/reps/grip");
+
+ // List of all registered template.
+ // XXX there should be a way for extensions to register a new
+ // or modify an existing rep.
+ const reps = [
+ RegExp,
+ StyleSheet,
+ Event,
+ DateTime,
+ CommentNode,
+ Accessible,
+ ElementNode,
+ TextNode,
+ Attribute,
+ Func,
+ PromiseRep,
+ Document,
+ DocumentType,
+ Window,
+ ObjectWithText,
+ ObjectWithURL,
+ ErrorRep,
+ GripArray,
+ GripMap,
+ GripEntry,
+ Grip,
+ Undefined,
+ Null,
+ StringRep,
+ Number,
+ BigInt,
+ SymbolRep,
+ InfinityRep,
+ NaNRep,
+ Accessor,
+ ];
+
+ // Reps for rendering of native object reference (e.g. used from the JSONViewer, Netmonitor, …)
+ const noGripReps = [StringRep, Number, ArrayRep, Undefined, Null, Obj];
+
+ /**
+ * Generic rep that is used for rendering native JS types or an object.
+ * The right template used for rendering is picked automatically according
+ * to the current value type. The value must be passed in as the 'object'
+ * property.
+ */
+ const Rep = function (props) {
+ const { object, defaultRep } = props;
+ const rep = getRep(
+ object,
+ defaultRep,
+ props.noGrip,
+ props.mayUseCustomFormatter
+ );
+ return rep(props);
+ };
+
+ const exportedReps = {
+ Accessible,
+ Accessor,
+ ArrayRep,
+ Attribute,
+ BigInt,
+ CommentNode,
+ DateTime,
+ Document,
+ DocumentType,
+ ElementNode,
+ ErrorRep,
+ Event,
+ Func,
+ Grip,
+ GripArray,
+ GripMap,
+ GripEntry,
+ InfinityRep,
+ NaNRep,
+ Null,
+ Number,
+ Obj,
+ ObjectWithText,
+ ObjectWithURL,
+ PromiseRep,
+ RegExp,
+ Rep,
+ StringRep,
+ StyleSheet,
+ SymbolRep,
+ TextNode,
+ Undefined,
+ Window,
+ };
+
+ // Custom Formatters
+ // Services.prefs isn't available in jsonviewer. It doesn't matter as we don't want to use
+ // custom formatters there
+ if (typeof Services == "object" && Services?.prefs) {
+ const useCustomFormatters = Services.prefs.getBoolPref(
+ "devtools.custom-formatters.enabled",
+ false
+ );
+
+ if (useCustomFormatters) {
+ const CustomFormatter = require("devtools/client/shared/components/reps/reps/custom-formatter");
+ reps.unshift(CustomFormatter);
+ exportedReps.CustomFormatter = CustomFormatter;
+ }
+ }
+
+ // Helpers
+
+ /**
+ * Return a rep object that is responsible for rendering given
+ * object.
+ *
+ * @param object {Object} Object to be rendered in the UI. This
+ * can be generic JS object as well as a grip (handle to a remote
+ * debuggee object).
+ *
+ * @param defaultRep {React.Component} The default template
+ * that should be used to render given object if none is found.
+ *
+ * @param noGrip {Boolean} If true, will only check reps not made for remote
+ * objects.
+ *
+ * @param mayUseCustomFormatter {Boolean} If true, custom formatters are
+ * allowed to be used as rep.
+ */
+ function getRep(
+ object,
+ defaultRep = Grip,
+ noGrip = false,
+ mayUseCustomFormatter = false
+ ) {
+ const repsList = noGrip ? noGripReps : reps;
+ for (const rep of repsList) {
+ if (rep === exportedReps.CustomFormatter && !mayUseCustomFormatter) {
+ continue;
+ }
+
+ try {
+ // supportsObject could return weight (not only true/false
+ // but a number), which would allow to priorities templates and
+ // support better extensibility.
+ if (rep.supportsObject(object, noGrip)) {
+ return rep.rep;
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ return defaultRep.rep;
+ }
+
+ module.exports = {
+ Rep,
+ REPS: exportedReps,
+ // Exporting for tests
+ getRep,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/string.js b/devtools/client/shared/components/reps/reps/string.js
new file mode 100644
index 0000000000..3149f4a51b
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/string.js
@@ -0,0 +1,410 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const {
+ a,
+ span,
+ } = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ const {
+ appendRTLClassNameIfNeeded,
+ containsURL,
+ escapeString,
+ getGripType,
+ rawCropString,
+ sanitizeString,
+ wrapRender,
+ ELLIPSIS,
+ uneatLastUrlCharsRegex,
+ urlRegex,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a string. String value is enclosed within quotes.
+ */
+
+ StringRep.propTypes = {
+ useQuotes: PropTypes.bool,
+ escapeWhitespace: PropTypes.bool,
+ style: PropTypes.object,
+ cropLimit: PropTypes.number.isRequired,
+ urlCropLimit: PropTypes.number,
+ member: PropTypes.object,
+ object: PropTypes.object.isRequired,
+ openLink: PropTypes.func,
+ className: PropTypes.string,
+ title: PropTypes.string,
+ isInContentPage: PropTypes.bool,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function StringRep(props) {
+ const {
+ className,
+ style,
+ cropLimit,
+ urlCropLimit,
+ object,
+ useQuotes = true,
+ escapeWhitespace = true,
+ member,
+ openLink,
+ title,
+ isInContentPage,
+ transformEmptyString = false,
+ shouldRenderTooltip,
+ } = props;
+
+ let text = object;
+ const config = getElementConfig({
+ className,
+ style,
+ actor: object.actor,
+ title,
+ });
+
+ if (text == "" && transformEmptyString && !useQuotes) {
+ return span(
+ {
+ ...config,
+ title: "<empty string>",
+ className: `${config.className} objectBox-empty-string`,
+ },
+ "<empty string>"
+ );
+ }
+
+ const isLong = isLongString(object);
+ const isOpen = member && member.open;
+ const shouldCrop = !isOpen && cropLimit && text.length > cropLimit;
+
+ if (isLong) {
+ text = maybeCropLongString(
+ {
+ shouldCrop,
+ cropLimit,
+ },
+ text
+ );
+
+ const { fullText } = object;
+ if (isOpen && fullText) {
+ text = fullText;
+ }
+ }
+
+ text = formatText(
+ {
+ useQuotes,
+ escapeWhitespace,
+ },
+ text
+ );
+
+ if (shouldRenderTooltip) {
+ config.title = text;
+ }
+
+ if (!isLong) {
+ if (containsURL(text)) {
+ return span(
+ config,
+ getLinkifiedElements({
+ text,
+ cropLimit: shouldCrop ? cropLimit : null,
+ urlCropLimit,
+ openLink,
+ isInContentPage,
+ })
+ );
+ }
+
+ // Cropping of longString has been handled before formatting.
+ text = maybeCropString(
+ {
+ isLong,
+ shouldCrop,
+ cropLimit,
+ },
+ text
+ );
+ }
+
+ config.className = appendRTLClassNameIfNeeded(config.className, text);
+
+ return span(config, text);
+ }
+
+ function maybeCropLongString(opts, object) {
+ const { shouldCrop, cropLimit } = opts;
+
+ const grip = object && object.getGrip ? object.getGrip() : object;
+ const { initial, length } = grip;
+
+ let text = shouldCrop ? initial.substring(0, cropLimit) : initial;
+
+ if (text.length < length) {
+ text += ELLIPSIS;
+ }
+
+ return text;
+ }
+
+ function formatText(opts, text) {
+ const { useQuotes, escapeWhitespace } = opts;
+
+ return useQuotes
+ ? escapeString(text, escapeWhitespace)
+ : sanitizeString(text);
+ }
+
+ function getElementConfig(opts) {
+ const { className, style, actor, title } = opts;
+
+ const config = {};
+
+ if (actor) {
+ config["data-link-actor-id"] = actor;
+ }
+
+ if (title) {
+ config.title = title;
+ }
+
+ const classNames = ["objectBox", "objectBox-string"];
+ if (className) {
+ classNames.push(className);
+ }
+ config.className = classNames.join(" ");
+
+ if (style) {
+ config.style = style;
+ }
+
+ return config;
+ }
+
+ function maybeCropString(opts, text) {
+ const { shouldCrop, cropLimit } = opts;
+
+ return shouldCrop ? rawCropString(text, cropLimit) : text;
+ }
+
+ /**
+ * Get an array of the elements representing the string, cropped if needed,
+ * with actual links.
+ *
+ * @param {Object} An options object of the following shape:
+ * - text {String}: The actual string to linkify.
+ * - cropLimit {Integer}: The limit to apply on the whole text.
+ * - urlCropLimit {Integer}: The limit to apply on each URL.
+ * - openLink {Function} openLink: Function handling the link
+ * opening.
+ * - isInContentPage {Boolean}: pass true if the reps is
+ * rendered in the content page
+ * (e.g. in JSONViewer).
+ * @returns {Array<String|ReactElement>}
+ */
+ function getLinkifiedElements({
+ text,
+ cropLimit,
+ urlCropLimit,
+ openLink,
+ isInContentPage,
+ }) {
+ const halfLimit = Math.ceil((cropLimit - ELLIPSIS.length) / 2);
+ const startCropIndex = cropLimit ? halfLimit : null;
+ const endCropIndex = cropLimit ? text.length - halfLimit : null;
+
+ const items = [];
+ let currentIndex = 0;
+ let contentStart;
+ while (true) {
+ const url = urlRegex.exec(text);
+ // Pick the regexp with the earlier content; index will always be zero.
+ if (!url) {
+ break;
+ }
+ contentStart = url.index + url[1].length;
+ if (contentStart > 0) {
+ const nonUrlText = text.substring(0, contentStart);
+ items.push(
+ getCroppedString(
+ nonUrlText,
+ currentIndex,
+ startCropIndex,
+ endCropIndex
+ )
+ );
+ }
+
+ // There are some final characters for a URL that are much more likely
+ // to have been part of the enclosing text rather than the end of the
+ // URL.
+ let useUrl = url[2];
+ const uneat = uneatLastUrlCharsRegex.exec(useUrl);
+ if (uneat) {
+ useUrl = useUrl.substring(0, uneat.index);
+ }
+
+ currentIndex = currentIndex + contentStart;
+ const linkText = getCroppedString(
+ useUrl,
+ currentIndex,
+ startCropIndex,
+ endCropIndex
+ );
+
+ if (linkText) {
+ const linkItems = [];
+ const shouldCrop = urlCropLimit && useUrl.length > urlCropLimit;
+ if (shouldCrop) {
+ const urlCropHalf = Math.ceil((urlCropLimit - ELLIPSIS.length) / 2);
+ // We cut the string into 3 elements and we'll visually hide the second one
+ // in CSS. This way people can still copy the full link.
+ linkItems.push(
+ span(
+ { className: "cropped-url-start" },
+ useUrl.substring(0, urlCropHalf)
+ ),
+ span(
+ { className: "cropped-url-middle" },
+ useUrl.substring(urlCropHalf, useUrl.length - urlCropHalf)
+ ),
+ span(
+ { className: "cropped-url-end" },
+ useUrl.substring(useUrl.length - urlCropHalf)
+ )
+ );
+ } else {
+ linkItems.push(linkText);
+ }
+
+ items.push(
+ a(
+ {
+ key: `${useUrl}-${currentIndex}`,
+ className: "url" + (shouldCrop ? " cropped-url" : ""),
+ title: useUrl,
+ draggable: false,
+ // Because we don't want the link to be open in the current
+ // panel's frame, we only render the href attribute if `openLink`
+ // exists (so we can preventDefault) or if the reps will be
+ // displayed in content page (e.g. in the JSONViewer).
+ href: openLink || isInContentPage ? useUrl : null,
+ target: "_blank",
+ rel: "noopener noreferrer",
+ onClick: openLink
+ ? e => {
+ e.preventDefault();
+ openLink(useUrl, e);
+ }
+ : null,
+ },
+ linkItems
+ )
+ );
+ }
+
+ currentIndex = currentIndex + useUrl.length;
+ text = text.substring(url.index + url[1].length + useUrl.length);
+ }
+
+ // Clean up any non-URL text at the end of the source string,
+ // i.e. not handled in the loop.
+ if (text.length) {
+ if (currentIndex < endCropIndex) {
+ text = getCroppedString(
+ text,
+ currentIndex,
+ startCropIndex,
+ endCropIndex
+ );
+ }
+ items.push(text);
+ }
+
+ return items;
+ }
+
+ /**
+ * Returns a cropped substring given an offset, start and end crop indices in a
+ * parent string.
+ *
+ * @param {String} text: The substring to crop.
+ * @param {Integer} offset: The offset corresponding to the index at which
+ * the substring is in the parent string.
+ * @param {Integer|null} startCropIndex: the index where the start of the crop
+ * should happen in the parent string.
+ * @param {Integer|null} endCropIndex: the index where the end of the crop
+ * should happen in the parent string
+ * @returns {String|null} The cropped substring, or null if the text is
+ * completly cropped.
+ */
+ function getCroppedString(text, offset = 0, startCropIndex, endCropIndex) {
+ if (!startCropIndex) {
+ return text;
+ }
+
+ const start = offset;
+ const end = offset + text.length;
+
+ const shouldBeVisible = !(start >= startCropIndex && end <= endCropIndex);
+ if (!shouldBeVisible) {
+ return null;
+ }
+
+ const shouldCropEnd = start < startCropIndex && end > startCropIndex;
+ const shouldCropStart = start < endCropIndex && end > endCropIndex;
+ if (shouldCropEnd) {
+ const cutIndex = startCropIndex - start;
+ return (
+ text.substring(0, cutIndex) +
+ ELLIPSIS +
+ (shouldCropStart ? text.substring(endCropIndex - start) : "")
+ );
+ }
+
+ if (shouldCropStart) {
+ // The string should be cropped at the beginning.
+ const cutIndex = endCropIndex - start;
+ return text.substring(cutIndex);
+ }
+
+ return text;
+ }
+
+ function isLongString(object) {
+ const grip = object && object.getGrip ? object.getGrip() : object;
+ return grip && grip.type === "longString";
+ }
+
+ function supportsObject(object, noGrip = false) {
+ // Accept the object if the grip-type (or type for noGrip objects) is "string"
+ if (getGripType(object, noGrip) == "string") {
+ return true;
+ }
+
+ // Also accept longString objects if we're expecting grip
+ if (!noGrip) {
+ return isLongString(object);
+ }
+
+ return false;
+ }
+
+ // Exports from this module
+
+ module.exports = {
+ rep: wrapRender(StringRep),
+ supportsObject,
+ isLongString,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/stylesheet.js b/devtools/client/shared/components/reps/reps/stylesheet.js
new file mode 100644
index 0000000000..591442ebbf
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/stylesheet.js
@@ -0,0 +1,78 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getGripType,
+ getURLDisplayString,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders a grip representing CSSStyleSheet
+ */
+
+ StyleSheet.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function StyleSheet(props) {
+ const grip = props.object;
+ const shouldRenderTooltip = props.shouldRenderTooltip;
+ const location = getLocation(grip);
+ const config = getElementConfig({ grip, shouldRenderTooltip, location });
+
+ return span(
+ config,
+ getTitle(grip),
+ span({ className: "objectPropValue" }, location)
+ );
+ }
+
+ function getElementConfig(opts) {
+ const { grip, shouldRenderTooltip, location } = opts;
+
+ return {
+ "data-link-actor-id": grip.actor,
+ className: "objectBox objectBox-object",
+ title: shouldRenderTooltip
+ ? `${getGripType(grip, false)} ${location}`
+ : null,
+ };
+ }
+
+ function getTitle(grip) {
+ return span(
+ { className: "objectBoxTitle" },
+ `${getGripType(grip, false)} `
+ );
+ }
+
+ function getLocation(grip) {
+ // Embedded stylesheets don't have URL and so, no preview.
+ const url = grip.preview ? grip.preview.url : "";
+ return url ? getURLDisplayString(url) : "";
+ }
+
+ // Registration
+ function supportsObject(object, noGrip = false) {
+ return getGripType(object, noGrip) == "CSSStyleSheet";
+ }
+
+ // Exports from this module
+
+ module.exports = {
+ rep: wrapRender(StyleSheet),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/symbol.js b/devtools/client/shared/components/reps/reps/symbol.js
new file mode 100644
index 0000000000..89c76cdbb2
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/symbol.js
@@ -0,0 +1,82 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ const {
+ rep: StringRep,
+ } = require("devtools/client/shared/components/reps/reps/string");
+
+ const MAX_STRING_LENGTH = 50;
+
+ /**
+ * Renders a symbol.
+ */
+
+ SymbolRep.propTypes = {
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function SymbolRep(props) {
+ const {
+ className = "objectBox objectBox-symbol",
+ object,
+ shouldRenderTooltip,
+ } = props;
+ const { name } = object;
+
+ let symbolText = name || "";
+ if (name && name !== "Symbol.iterator" && name !== "Symbol.asyncIterator") {
+ symbolText = StringRep({
+ object: symbolText,
+ shouldCrop: true,
+ cropLimit: MAX_STRING_LENGTH,
+ useQuotes: true,
+ });
+ }
+
+ const config = getElementConfig(
+ {
+ shouldRenderTooltip,
+ className,
+ name,
+ },
+ object
+ );
+
+ return span(config, "Symbol(", symbolText, ")");
+ }
+
+ function getElementConfig(opts, object) {
+ const { shouldRenderTooltip, className, name } = opts;
+
+ return {
+ "data-link-actor-id": object.actor,
+ className,
+ title: shouldRenderTooltip ? `Symbol(${name})` : null,
+ };
+ }
+
+ function supportsObject(object, noGrip = false) {
+ return getGripType(object, noGrip) == "symbol";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(SymbolRep),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/text-node.js b/devtools/client/shared/components/reps/reps/text-node.js
new file mode 100644
index 0000000000..ae9a7bb109
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/text-node.js
@@ -0,0 +1,141 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const {
+ button,
+ span,
+ } = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ // Reps
+ const {
+ appendRTLClassNameIfNeeded,
+ cropString,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+ const {
+ rep: StringRep,
+ isLongString,
+ } = require("devtools/client/shared/components/reps/reps/string");
+
+ /**
+ * Renders DOM #text node.
+ */
+
+ TextNode.propTypes = {
+ object: PropTypes.object.isRequired,
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ onDOMNodeMouseOver: PropTypes.func,
+ onDOMNodeMouseOut: PropTypes.func,
+ onInspectIconClick: PropTypes.func,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function TextNode(props) {
+ const { object: grip, mode = MODE.SHORT } = props;
+
+ const isInTree = grip.preview && grip.preview.isConnected === true;
+ const config = getElementConfig({ ...props, isInTree });
+ const inspectIcon = getInspectIcon({ ...props, isInTree });
+
+ if (mode === MODE.TINY || mode === MODE.HEADER) {
+ return span(config, getTitle(grip), inspectIcon);
+ }
+
+ return span(
+ config,
+ getTitle(grip),
+ " ",
+ StringRep({
+ className: "nodeValue",
+ object: grip.preview.textContent,
+ }),
+ inspectIcon ? inspectIcon : null
+ );
+ }
+
+ function getElementConfig(opts) {
+ const {
+ object,
+ isInTree,
+ onDOMNodeMouseOver,
+ onDOMNodeMouseOut,
+ shouldRenderTooltip,
+ } = opts;
+
+ const text = getTextContent(object);
+ const config = {
+ "data-link-actor-id": object.actor,
+ "data-link-content-dom-reference": JSON.stringify(
+ object.contentDomReference
+ ),
+ className: appendRTLClassNameIfNeeded(
+ "objectBox objectBox-textNode",
+ text
+ ),
+ title: shouldRenderTooltip ? `#text "${text}"` : null,
+ };
+
+ if (isInTree) {
+ if (onDOMNodeMouseOver) {
+ Object.assign(config, {
+ onMouseOver: _ => onDOMNodeMouseOver(object),
+ });
+ }
+
+ if (onDOMNodeMouseOut) {
+ Object.assign(config, {
+ onMouseOut: _ => onDOMNodeMouseOut(object),
+ });
+ }
+ }
+
+ return config;
+ }
+
+ function getTextContent(grip) {
+ const text = grip.preview.textContent;
+ return cropString(isLongString(text) ? text.initial : text);
+ }
+
+ function getInspectIcon(opts) {
+ const { object, isInTree, onInspectIconClick } = opts;
+
+ if (!isInTree || !onInspectIconClick) {
+ return null;
+ }
+
+ return button({
+ className: "open-inspector",
+ draggable: false,
+ // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
+ title: "Click to select the node in the inspector",
+ onClick: e => onInspectIconClick(object, e),
+ });
+ }
+
+ function getTitle(grip) {
+ const title = "#text";
+ return span({}, title);
+ }
+
+ // Registration
+ function supportsObject(grip, noGrip = false) {
+ return grip?.preview && grip?.class == "Text";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(TextNode),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/undefined.js b/devtools/client/shared/components/reps/reps/undefined.js
new file mode 100644
index 0000000000..7e0ac4c786
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/undefined.js
@@ -0,0 +1,59 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ const {
+ getGripType,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ /**
+ * Renders undefined value
+ */
+
+ Undefined.propTypes = {
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function Undefined(props) {
+ const shouldRenderTooltip = props.shouldRenderTooltip;
+
+ const config = getElementConfig(shouldRenderTooltip);
+
+ return span(config, "undefined");
+ }
+
+ function getElementConfig(shouldRenderTooltip) {
+ return {
+ className: "objectBox objectBox-undefined",
+ title: shouldRenderTooltip ? "undefined" : null,
+ };
+ }
+
+ function supportsObject(object, noGrip = false) {
+ if (noGrip === true) {
+ return object === undefined;
+ }
+
+ return (
+ (object && object.type && object.type == "undefined") ||
+ getGripType(object, noGrip) == "undefined"
+ );
+ }
+
+ // Exports from this module
+
+ module.exports = {
+ rep: wrapRender(Undefined),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/reps/window.js b/devtools/client/shared/components/reps/reps/window.js
new file mode 100644
index 0000000000..2c420c2b30
--- /dev/null
+++ b/devtools/client/shared/components/reps/reps/window.js
@@ -0,0 +1,102 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // ReactJS
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { span } = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ getGripType,
+ getURLDisplayString,
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+
+ /**
+ * Renders a grip representing a window.
+ */
+
+ WindowRep.propTypes = {
+ mode: PropTypes.oneOf(Object.values(MODE)),
+ object: PropTypes.object.isRequired,
+ shouldRenderTooltip: PropTypes.bool,
+ };
+
+ function WindowRep(props) {
+ const { mode, object } = props;
+
+ if (mode === MODE.TINY) {
+ const tinyTitle = getTitle(object);
+ const title = getTitle(object, true);
+ const location = getLocation(object);
+ const config = getElementConfig({ ...props, title, location });
+
+ return span(
+ config,
+ span({ className: tinyTitle.className }, tinyTitle.content)
+ );
+ }
+
+ const title = getTitle(object, true);
+ const location = getLocation(object);
+ const config = getElementConfig({ ...props, title, location });
+
+ return span(
+ config,
+ span({ className: title.className }, title.content),
+ span({ className: "location" }, location)
+ );
+ }
+
+ function getElementConfig(opts) {
+ const { object, shouldRenderTooltip, title, location } = opts;
+ let tooltip;
+
+ if (location) {
+ tooltip = `${title.content}${location}`;
+ } else {
+ tooltip = `${title.content}`;
+ }
+
+ return {
+ "data-link-actor-id": object.actor,
+ className: "objectBox objectBox-Window",
+ title: shouldRenderTooltip ? tooltip : null,
+ };
+ }
+
+ function getTitle(object, trailingSpace) {
+ let title = object.displayClass || object.class || "Window";
+ if (trailingSpace === true) {
+ title = `${title} `;
+ }
+ return {
+ className: "objectTitle",
+ content: title,
+ };
+ }
+
+ function getLocation(object) {
+ return getURLDisplayString(object.preview.url);
+ }
+
+ // Registration
+ function supportsObject(object, noGrip = false) {
+ return object?.preview && getGripType(object, noGrip) == "Window";
+ }
+
+ // Exports from this module
+ module.exports = {
+ rep: wrapRender(WindowRep),
+ supportsObject,
+ };
+});
diff --git a/devtools/client/shared/components/reps/shared/dom-node-constants.js b/devtools/client/shared/components/reps/shared/dom-node-constants.js
new file mode 100644
index 0000000000..ecc1861d65
--- /dev/null
+++ b/devtools/client/shared/components/reps/shared/dom-node-constants.js
@@ -0,0 +1,31 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ module.exports = {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12,
+
+ // DocumentPosition
+ DOCUMENT_POSITION_DISCONNECTED: 0x01,
+ DOCUMENT_POSITION_PRECEDING: 0x02,
+ DOCUMENT_POSITION_FOLLOWING: 0x04,
+ DOCUMENT_POSITION_CONTAINS: 0x08,
+ DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+ DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20,
+ };
+});
diff --git a/devtools/client/shared/components/reps/shared/grip-length-bubble.js b/devtools/client/shared/components/reps/shared/grip-length-bubble.js
new file mode 100644
index 0000000000..8d0dcb8bb8
--- /dev/null
+++ b/devtools/client/shared/components/reps/shared/grip-length-bubble.js
@@ -0,0 +1,64 @@
+/* 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/>. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ const {
+ wrapRender,
+ } = require("devtools/client/shared/components/reps/reps/rep-utils");
+ const {
+ MODE,
+ } = require("devtools/client/shared/components/reps/reps/constants");
+ const {
+ ModePropType,
+ } = require("devtools/client/shared/components/reps/reps/array");
+
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { span } = dom;
+
+ GripLengthBubble.propTypes = {
+ object: PropTypes.object.isRequired,
+ maxLengthMap: PropTypes.instanceOf(Map).isRequired,
+ getLength: PropTypes.func.isRequired,
+ mode: ModePropType,
+ visibilityThreshold: PropTypes.number,
+ };
+
+ function GripLengthBubble(props) {
+ const {
+ object,
+ mode = MODE.SHORT,
+ visibilityThreshold = 2,
+ maxLengthMap,
+ getLength,
+ showZeroLength = false,
+ } = props;
+
+ const length = getLength(object);
+ const isEmpty = length === 0;
+ const isObvious =
+ [MODE.SHORT, MODE.LONG].includes(mode) &&
+ length > 0 &&
+ length <= maxLengthMap.get(mode) &&
+ length <= visibilityThreshold;
+ if ((isEmpty && !showZeroLength) || isObvious) {
+ return "";
+ }
+
+ return span(
+ {
+ className: "objectLengthBubble",
+ },
+ `(${length})`
+ );
+ }
+
+ module.exports = {
+ lengthBubble: wrapRender(GripLengthBubble),
+ };
+});
diff --git a/devtools/client/shared/components/reps/shared/moz.build b/devtools/client/shared/components/reps/shared/moz.build
new file mode 100644
index 0000000000..6704491b97
--- /dev/null
+++ b/devtools/client/shared/components/reps/shared/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "dom-node-constants.js",
+ "grip-length-bubble.js",
+)
diff --git a/devtools/client/shared/components/splitter/Draggable.js b/devtools/client/shared/components/splitter/Draggable.js
new file mode 100644
index 0000000000..3d18e49c34
--- /dev/null
+++ b/devtools/client/shared/components/splitter/Draggable.js
@@ -0,0 +1,106 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createRef,
+ Component,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+class Draggable extends Component {
+ static get propTypes() {
+ return {
+ onMove: PropTypes.func.isRequired,
+ onDoubleClick: PropTypes.func,
+ onStart: PropTypes.func,
+ onStop: PropTypes.func,
+ style: PropTypes.object,
+ title: PropTypes.string,
+ className: PropTypes.string,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.draggableEl = createRef();
+
+ this.startDragging = this.startDragging.bind(this);
+ this.stopDragging = this.stopDragging.bind(this);
+ this.onDoubleClick = this.onDoubleClick.bind(this);
+ this.onMove = this.onMove.bind(this);
+
+ this.mouseX = 0;
+ this.mouseY = 0;
+ }
+ startDragging(ev) {
+ const xDiff = Math.abs(this.mouseX - ev.clientX);
+ const yDiff = Math.abs(this.mouseY - ev.clientY);
+
+ // This allows for double-click.
+ if (this.props.onDoubleClick && xDiff + yDiff <= 1) {
+ return;
+ }
+ this.mouseX = ev.clientX;
+ this.mouseY = ev.clientY;
+
+ if (this.isDragging) {
+ return;
+ }
+ this.isDragging = true;
+ ev.preventDefault();
+
+ this.draggableEl.current.addEventListener("mousemove", this.onMove);
+ this.draggableEl.current.setPointerCapture(ev.pointerId);
+
+ this.props.onStart && this.props.onStart();
+ }
+
+ onDoubleClick() {
+ if (this.props.onDoubleClick) {
+ this.props.onDoubleClick();
+ }
+ }
+
+ onMove(ev) {
+ if (!this.isDragging) {
+ return;
+ }
+
+ ev.preventDefault();
+ // Use viewport coordinates so, moving mouse over iframes
+ // doesn't mangle (relative) coordinates.
+ this.props.onMove(ev.clientX, ev.clientY);
+ }
+
+ stopDragging(ev) {
+ if (!this.isDragging) {
+ return;
+ }
+ this.isDragging = false;
+ ev.preventDefault();
+
+ this.draggableEl.current.removeEventListener("mousemove", this.onMove);
+ this.draggableEl.current.releasePointerCapture(ev.pointerId);
+ this.props.onStop && this.props.onStop();
+ }
+
+ render() {
+ return dom.div({
+ ref: this.draggableEl,
+ role: "presentation",
+ style: this.props.style,
+ title: this.props.title,
+ className: this.props.className,
+ onMouseDown: this.startDragging,
+ onMouseUp: this.stopDragging,
+ onDoubleClick: this.onDoubleClick,
+ });
+ }
+}
+
+module.exports = Draggable;
diff --git a/devtools/client/shared/components/splitter/GridElementResizer.css b/devtools/client/shared/components/splitter/GridElementResizer.css
new file mode 100644
index 0000000000..dfa69592e9
--- /dev/null
+++ b/devtools/client/shared/components/splitter/GridElementResizer.css
@@ -0,0 +1,32 @@
+/* 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/. */
+
+.grid-element-width-resizer {
+ /* The space we'll have on each side of the "splitter border" */
+ --inline-inset: 3px;
+ /* We use the --inline-inset value that we multiply by 2 and add 1px to center the splitter */
+ width: calc(1px + (2 * var(--inline-inset)));
+ position: relative;
+ cursor: ew-resize;
+ z-index: 10;
+}
+
+.grid-element-width-resizer.start {
+ justify-self: start;
+ inset-inline-start: calc(-1 * var(--inline-inset));
+}
+
+.grid-element-width-resizer.end {
+ justify-self: end;
+ inset-inline-start: var(--inline-inset);
+}
+
+.dragging,
+.dragging * {
+ /* When resizing, we keep the "resize" cursor on every element we might hover */
+ cursor: ew-resize !important;
+ /* This prevents to trigger some :hover style and is better for performance
+ * when resizing */
+ pointer-events: none !important;
+}
diff --git a/devtools/client/shared/components/splitter/GridElementWidthResizer.js b/devtools/client/shared/components/splitter/GridElementWidthResizer.js
new file mode 100644
index 0000000000..c6ab6f3e14
--- /dev/null
+++ b/devtools/client/shared/components/splitter/GridElementWidthResizer.js
@@ -0,0 +1,138 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const Draggable = createFactory(
+ require("resource://devtools/client/shared/components/splitter/Draggable.js")
+);
+
+class GridElementWidthResizer extends Component {
+ static get propTypes() {
+ return {
+ getControlledElementNode: PropTypes.func.isRequired,
+ enabled: PropTypes.bool,
+ position: PropTypes.string.isRequired,
+ className: PropTypes.string,
+ onResizeEnd: PropTypes.func,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.onStartMove = this.onStartMove.bind(this);
+ this.onStopMove = this.onStopMove.bind(this);
+ this.onMove = this.onMove.bind(this);
+ this.state = {
+ dragging: false,
+ isRTLElement: false,
+ defaultCursor: null,
+ defaultWidth: null,
+ };
+ }
+
+ componentDidUpdate(prevProps) {
+ if (prevProps.enabled === true && this.props.enabled === false) {
+ this.onStopMove();
+ const controlledElementNode = this.props.getControlledElementNode();
+ controlledElementNode.style.width = this.state.defaultWidth;
+ }
+ }
+
+ // Dragging Events
+
+ /**
+ * Set 'resizing' cursor on entire document during splitter dragging.
+ * This avoids cursor-flickering that happens when the mouse leaves
+ * the splitter bar area (happens frequently).
+ */
+ onStartMove() {
+ const controlledElementNode = this.props.getControlledElementNode();
+ if (!controlledElementNode) {
+ return;
+ }
+
+ const doc = controlledElementNode.ownerDocument;
+ const defaultCursor = doc.documentElement.style.cursor;
+ const defaultWidth = doc.documentElement.style.width;
+ doc.documentElement.style.cursor = "ew-resize";
+ doc.firstElementChild.classList.add("dragging");
+
+ this.setState({
+ dragging: true,
+ isRTLElement:
+ controlledElementNode.ownerDocument.defaultView.getComputedStyle(
+ controlledElementNode
+ ).direction === "rtl",
+ defaultCursor,
+ defaultWidth,
+ });
+ }
+
+ onStopMove() {
+ const controlledElementNode = this.props.getControlledElementNode();
+ if (!this.state.dragging || !controlledElementNode) {
+ return;
+ }
+ const doc = controlledElementNode.ownerDocument;
+ doc.documentElement.style.cursor = this.state.defaultCursor;
+ doc.firstElementChild.classList.remove("dragging");
+
+ this.setState({
+ dragging: false,
+ });
+
+ if (this.props.onResizeEnd) {
+ const { width } = controlledElementNode.getBoundingClientRect();
+ this.props.onResizeEnd(width);
+ }
+ }
+
+ /**
+ * Adjust size of the controlled panel.
+ */
+ onMove(x) {
+ const controlledElementNode = this.props.getControlledElementNode();
+ if (!this.state.dragging || !controlledElementNode) {
+ return;
+ }
+ const nodeBounds = controlledElementNode.getBoundingClientRect();
+ const { isRTLElement } = this.state;
+ const { position } = this.props;
+
+ const size =
+ (isRTLElement && position === "end") ||
+ (!isRTLElement && position === "start")
+ ? nodeBounds.width + (nodeBounds.left - x)
+ : x - nodeBounds.left;
+
+ controlledElementNode.style.width = `${size}px`;
+ }
+
+ render() {
+ if (!this.props.enabled) {
+ return null;
+ }
+
+ const classNames = ["grid-element-width-resizer", this.props.position];
+ if (this.props.className) {
+ classNames.push(this.props.className);
+ }
+
+ return Draggable({
+ className: classNames.join(" "),
+ onStart: this.onStartMove,
+ onStop: this.onStopMove,
+ onMove: this.onMove,
+ });
+ }
+}
+
+module.exports = GridElementWidthResizer;
diff --git a/devtools/client/shared/components/splitter/SplitBox.css b/devtools/client/shared/components/splitter/SplitBox.css
new file mode 100644
index 0000000000..6028619e7b
--- /dev/null
+++ b/devtools/client/shared/components/splitter/SplitBox.css
@@ -0,0 +1,93 @@
+/* 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/. */
+
+.split-box {
+ display: flex;
+ flex: 1;
+ min-width: 0;
+ height: 100%;
+ width: 100%;
+}
+
+.split-box.vert {
+ flex-direction: row;
+}
+
+.split-box.horz {
+ flex-direction: column;
+}
+
+.split-box > .uncontrolled {
+ display: flex;
+ flex: 1;
+ min-width: 0;
+ overflow: auto;
+}
+
+.split-box > .controlled {
+ display: flex;
+ overflow: auto;
+}
+
+.split-box > .splitter {
+ background-image: none;
+ border: 0;
+ border-style: solid;
+ border-color: transparent;
+ background-color: var(--theme-splitter-color);
+ background-clip: content-box;
+ position: relative;
+
+ box-sizing: border-box;
+
+ /* Positive z-index positions the splitter on top of its siblings and makes
+ it clickable on both sides. */
+ z-index: 1;
+}
+
+.split-box.vert > .splitter {
+ min-width: var(--devtools-vertical-splitter-min-width);
+
+ border-inline-start-width: var(--devtools-splitter-inline-start-width);
+ border-inline-end-width: var(--devtools-splitter-inline-end-width);
+
+ margin-inline-start: calc(-1 * var(--devtools-splitter-inline-start-width) - 1px);
+ margin-inline-end: calc(-1 * var(--devtools-splitter-inline-end-width));
+
+ cursor: ew-resize;
+}
+
+.split-box.horz > .splitter {
+ /* Emphasize the horizontal splitter width and color */
+ min-height: var(--devtools-emphasized-horizontal-splitter-min-height);
+
+ background-color: var(--theme-emphasized-splitter-color);
+
+ border-top-width: var(--devtools-splitter-top-width);
+ border-bottom-width: var(--devtools-splitter-bottom-width);
+
+ margin-top: calc(-1 * var(--devtools-splitter-top-width) - 1px);
+ margin-bottom: calc(-1 * var(--devtools-splitter-bottom-width));
+
+ cursor: ns-resize;
+}
+
+/* Emphasized splitter has the hover style. */
+.split-box.horz > .splitter:hover {
+ background-color: var(--theme-emphasized-splitter-color-hover);
+}
+
+.split-box.disabled {
+ pointer-events: none;
+}
+
+/**
+ * Make sure splitter panels are not processing any mouse
+ * events. This is good for performance during splitter
+ * bar dragging.
+ */
+.split-box.dragging > .controlled,
+.split-box.dragging > .uncontrolled {
+ pointer-events: none;
+}
diff --git a/devtools/client/shared/components/splitter/SplitBox.js b/devtools/client/shared/components/splitter/SplitBox.js
new file mode 100644
index 0000000000..2bf0fdb74d
--- /dev/null
+++ b/devtools/client/shared/components/splitter/SplitBox.js
@@ -0,0 +1,351 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const Draggable = createFactory(
+ require("resource://devtools/client/shared/components/splitter/Draggable.js")
+);
+
+/**
+ * This component represents a Splitter. The splitter supports vertical
+ * as well as horizontal mode.
+ */
+class SplitBox extends Component {
+ static get propTypes() {
+ return {
+ // Custom class name. You can use more names separated by a space.
+ className: PropTypes.string,
+ // Initial size of controlled panel.
+ initialSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ // Initial width of controlled panel.
+ initialWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ // Initial height of controlled panel.
+ initialHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ // Left/top panel
+ startPanel: PropTypes.any,
+ // Left/top panel collapse state.
+ startPanelCollapsed: PropTypes.bool,
+ // Min panel size.
+ minSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ // Max panel size.
+ maxSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ // Right/bottom panel
+ endPanel: PropTypes.any,
+ // Right/bottom panel collapse state.
+ endPanelCollapsed: PropTypes.bool,
+ // True if the right/bottom panel should be controlled.
+ endPanelControl: PropTypes.bool,
+ // Size of the splitter handle bar.
+ splitterSize: PropTypes.number,
+ // True if the splitter bar is vertical (default is vertical).
+ vert: PropTypes.bool,
+ // Style object.
+ style: PropTypes.object,
+ // Call when controlled panel was resized.
+ onControlledPanelResized: PropTypes.func,
+ // Optional callback when splitbox resize stops
+ onResizeEnd: PropTypes.func,
+ // Retrieve DOM reference to the start panel element
+ onSelectContainerElement: PropTypes.any,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ splitterSize: 5,
+ vert: true,
+ endPanelControl: false,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ /**
+ * The state stores whether or not the end panel should be controlled, the current
+ * orientation (vertical or horizontal), the splitter size, and the current size
+ * (width/height). All these values can change during the component's life time.
+ */
+ this.state = {
+ // True if the right/bottom panel should be controlled.
+ endPanelControl: props.endPanelControl,
+ // True if the splitter bar is vertical (default is vertical).
+ vert: props.vert,
+ // Size of the splitter handle bar.
+ splitterSize: props.splitterSize,
+ // Width of controlled panel.
+ width: props.initialWidth || props.initialSize,
+ // Height of controlled panel.
+ height: props.initialHeight || props.initialSize,
+ };
+
+ this.onStartMove = this.onStartMove.bind(this);
+ this.onStopMove = this.onStopMove.bind(this);
+ this.onMove = this.onMove.bind(this);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ const { endPanelControl, splitterSize, vert } = nextProps;
+
+ if (endPanelControl != this.props.endPanelControl) {
+ this.setState({ endPanelControl });
+ }
+
+ if (splitterSize != this.props.splitterSize) {
+ this.setState({ splitterSize });
+ }
+
+ if (vert !== this.props.vert) {
+ this.setState({ vert });
+ }
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ nextState.width != this.state.width ||
+ nextState.endPanelControl != this.props.endPanelControl ||
+ nextState.height != this.state.height ||
+ nextState.vert != this.state.vert ||
+ nextState.splitterSize != this.state.splitterSize ||
+ nextProps.startPanel != this.props.startPanel ||
+ nextProps.endPanel != this.props.endPanel ||
+ nextProps.minSize != this.props.minSize ||
+ nextProps.maxSize != this.props.maxSize
+ );
+ }
+
+ componentDidUpdate(prevProps, prevState) {
+ if (
+ this.props.onControlledPanelResized &&
+ (prevState.width !== this.state.width ||
+ prevState.height !== this.state.height)
+ ) {
+ this.props.onControlledPanelResized(this.state.width, this.state.height);
+ }
+ }
+
+ // Dragging Events
+
+ /**
+ * Set 'resizing' cursor on entire document during splitter dragging.
+ * This avoids cursor-flickering that happens when the mouse leaves
+ * the splitter bar area (happens frequently).
+ */
+ onStartMove() {
+ const doc = this.splitBox.ownerDocument;
+ const defaultCursor = doc.documentElement.style.cursor;
+ doc.documentElement.style.cursor = this.state.vert
+ ? "ew-resize"
+ : "ns-resize";
+
+ this.splitBox.classList.add("dragging");
+
+ this.setState({
+ defaultCursor,
+ });
+ }
+
+ onStopMove() {
+ const doc = this.splitBox.ownerDocument;
+ doc.documentElement.style.cursor = this.state.defaultCursor;
+
+ this.splitBox.classList.remove("dragging");
+
+ if (this.props.onResizeEnd) {
+ this.props.onResizeEnd(
+ this.state.vert ? this.state.width : this.state.height
+ );
+ }
+ }
+
+ /**
+ * Adjust size of the controlled panel. Depending on the current
+ * orientation we either remember the width or height of
+ * the splitter box.
+ */
+ onMove(x, y) {
+ const nodeBounds = this.splitBox.getBoundingClientRect();
+
+ let size;
+ let { endPanelControl, vert } = this.state;
+
+ if (vert) {
+ // Use the document owning the SplitBox to detect rtl. The global document might be
+ // the one bound to the toolbox shared BrowserRequire, which is irrelevant here.
+ const doc = this.splitBox.ownerDocument;
+
+ // Switch the control flag in case of RTL. Note that RTL
+ // has impact on vertical splitter only.
+ if (doc.dir === "rtl") {
+ endPanelControl = !endPanelControl;
+ }
+
+ size = endPanelControl
+ ? nodeBounds.left + nodeBounds.width - x
+ : x - nodeBounds.left;
+
+ this.setState({
+ width: this.getConstrainedSizeInPx(size, nodeBounds.width),
+ });
+ } else {
+ size = endPanelControl
+ ? nodeBounds.top + nodeBounds.height - y
+ : y - nodeBounds.top;
+
+ this.setState({
+ height: this.getConstrainedSizeInPx(size, nodeBounds.height),
+ });
+ }
+ }
+
+ /**
+ * Calculates the constrained size taking into account the minimum width or
+ * height passed via this.props.minSize.
+ *
+ * @param {Number} requestedSize
+ * The requested size
+ * @param {Number} splitBoxWidthOrHeight
+ * The width or height of the splitBox
+ *
+ * @return {Number}
+ * The constrained size
+ */
+ getConstrainedSizeInPx(requestedSize, splitBoxWidthOrHeight) {
+ let minSize = this.props.minSize + "";
+
+ if (minSize.endsWith("%")) {
+ minSize = (parseFloat(minSize) / 100) * splitBoxWidthOrHeight;
+ } else if (minSize.endsWith("px")) {
+ minSize = parseFloat(minSize);
+ }
+ return Math.max(requestedSize, minSize);
+ }
+
+ // Rendering
+
+ // eslint-disable-next-line complexity
+ render() {
+ const { endPanelControl, splitterSize, vert } = this.state;
+ const {
+ startPanel,
+ startPanelCollapsed,
+ endPanel,
+ endPanelCollapsed,
+ minSize,
+ maxSize,
+ onSelectContainerElement,
+ } = this.props;
+
+ const style = Object.assign(
+ {
+ // Set the size of the controlled panel (height or width depending on the
+ // current state). This can be used to help with styling of dependent
+ // panels.
+ "--split-box-controlled-panel-size": `${
+ vert ? this.state.width : this.state.height
+ }`,
+ },
+ this.props.style
+ );
+
+ // Calculate class names list.
+ let classNames = ["split-box"];
+ classNames.push(vert ? "vert" : "horz");
+ if (this.props.className) {
+ classNames = classNames.concat(this.props.className.split(" "));
+ }
+
+ let leftPanelStyle;
+ let rightPanelStyle;
+
+ // Set proper size for panels depending on the current state.
+ if (vert) {
+ leftPanelStyle = {
+ maxWidth: endPanelControl ? null : maxSize,
+ minWidth: endPanelControl ? null : minSize,
+ width: endPanelControl ? null : this.state.width,
+ };
+ rightPanelStyle = {
+ maxWidth: endPanelControl ? maxSize : null,
+ minWidth: endPanelControl ? minSize : null,
+ width: endPanelControl ? this.state.width : null,
+ };
+ } else {
+ leftPanelStyle = {
+ maxHeight: endPanelControl ? null : maxSize,
+ minHeight: endPanelControl ? null : minSize,
+ height: endPanelControl ? null : this.state.height,
+ };
+ rightPanelStyle = {
+ maxHeight: endPanelControl ? maxSize : null,
+ minHeight: endPanelControl ? minSize : null,
+ height: endPanelControl ? this.state.height : null,
+ };
+ }
+
+ // Calculate splitter size
+ const splitterStyle = {
+ flex: "0 0 " + splitterSize + "px",
+ };
+
+ return dom.div(
+ {
+ className: classNames.join(" "),
+ ref: div => {
+ this.splitBox = div;
+ },
+ style,
+ },
+ startPanel && !startPanelCollapsed
+ ? dom.div(
+ {
+ className: endPanelControl ? "uncontrolled" : "controlled",
+ style: leftPanelStyle,
+ role: "presentation",
+ ref: div => {
+ this.startPanelContainer = div;
+ if (onSelectContainerElement) {
+ onSelectContainerElement(div);
+ }
+ },
+ },
+ startPanel
+ )
+ : null,
+ splitterSize > 0
+ ? Draggable({
+ className: "splitter",
+ style: splitterStyle,
+ onStart: this.onStartMove,
+ onStop: this.onStopMove,
+ onMove: this.onMove,
+ })
+ : null,
+ endPanel && !endPanelCollapsed
+ ? dom.div(
+ {
+ className: endPanelControl ? "controlled" : "uncontrolled",
+ style: rightPanelStyle,
+ role: "presentation",
+ ref: div => {
+ this.endPanelContainer = div;
+ },
+ },
+ endPanel
+ )
+ : null
+ );
+ }
+}
+
+module.exports = SplitBox;
diff --git a/devtools/client/shared/components/splitter/moz.build b/devtools/client/shared/components/splitter/moz.build
new file mode 100644
index 0000000000..4abe762b34
--- /dev/null
+++ b/devtools/client/shared/components/splitter/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "Draggable.js",
+ "GridElementWidthResizer.js",
+ "SplitBox.js",
+)
diff --git a/devtools/client/shared/components/tabs/TabBar.js b/devtools/client/shared/components/tabs/TabBar.js
new file mode 100644
index 0000000000..730e8c7802
--- /dev/null
+++ b/devtools/client/shared/components/tabs/TabBar.js
@@ -0,0 +1,378 @@
+/* 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/. */
+
+/* eslint-env browser */
+
+"use strict";
+
+const {
+ Component,
+ createFactory,
+ createRef,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+const Sidebar = createFactory(
+ require("resource://devtools/client/shared/components/Sidebar.js")
+);
+
+loader.lazyRequireGetter(
+ this,
+ "Menu",
+ "resource://devtools/client/framework/menu.js"
+);
+loader.lazyRequireGetter(
+ this,
+ "MenuItem",
+ "resource://devtools/client/framework/menu-item.js"
+);
+
+// Shortcuts
+const { div } = dom;
+
+/**
+ * Renders Tabbar component.
+ */
+class Tabbar extends Component {
+ static get propTypes() {
+ return {
+ children: PropTypes.array,
+ menuDocument: PropTypes.object,
+ onSelect: PropTypes.func,
+ showAllTabsMenu: PropTypes.bool,
+ allTabsMenuButtonTooltip: PropTypes.string,
+ activeTabId: PropTypes.string,
+ renderOnlySelected: PropTypes.bool,
+ sidebarToggleButton: PropTypes.shape({
+ // Set to true if collapsed.
+ collapsed: PropTypes.bool.isRequired,
+ // Tooltip text used when the button indicates expanded state.
+ collapsePaneTitle: PropTypes.string.isRequired,
+ // Tooltip text used when the button indicates collapsed state.
+ expandPaneTitle: PropTypes.string.isRequired,
+ // Click callback
+ onClick: PropTypes.func.isRequired,
+ // align toggle button to right
+ alignRight: PropTypes.bool,
+ // if set to true toggle-button rotate 90
+ canVerticalSplit: PropTypes.bool,
+ }),
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ menuDocument: window.parent.document,
+ showAllTabsMenu: false,
+ };
+ }
+
+ constructor(props, context) {
+ super(props, context);
+ const { activeTabId, children = [] } = props;
+ const tabs = this.createTabs(children);
+ const activeTab = tabs.findIndex((tab, index) => tab.id === activeTabId);
+
+ this.state = {
+ activeTab: activeTab === -1 ? 0 : activeTab,
+ tabs,
+ };
+
+ // Array of queued tabs to add to the Tabbar.
+ this.queuedTabs = [];
+
+ this.createTabs = this.createTabs.bind(this);
+ this.addTab = this.addTab.bind(this);
+ this.addAllQueuedTabs = this.addAllQueuedTabs.bind(this);
+ this.queueTab = this.queueTab.bind(this);
+ this.toggleTab = this.toggleTab.bind(this);
+ this.removeTab = this.removeTab.bind(this);
+ this.select = this.select.bind(this);
+ this.getTabIndex = this.getTabIndex.bind(this);
+ this.getTabId = this.getTabId.bind(this);
+ this.getCurrentTabId = this.getCurrentTabId.bind(this);
+ this.onTabChanged = this.onTabChanged.bind(this);
+ this.onAllTabsMenuClick = this.onAllTabsMenuClick.bind(this);
+ this.renderTab = this.renderTab.bind(this);
+ this.tabbarRef = createRef();
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ const { activeTabId, children = [] } = nextProps;
+ const tabs = this.createTabs(children);
+ const activeTab = tabs.findIndex((tab, index) => tab.id === activeTabId);
+
+ if (
+ activeTab !== this.state.activeTab ||
+ children !== this.props.children
+ ) {
+ this.setState({
+ activeTab: activeTab === -1 ? 0 : activeTab,
+ tabs,
+ });
+ }
+ }
+
+ createTabs(children) {
+ return children
+ .filter(panel => panel)
+ .map((panel, index) =>
+ Object.assign({}, children[index], {
+ id: panel.props.id || index,
+ panel,
+ title: panel.props.title,
+ })
+ );
+ }
+
+ // Public API
+
+ addTab(id, title, selected = false, panel, url, index = -1) {
+ const tabs = this.state.tabs.slice();
+
+ if (index >= 0) {
+ tabs.splice(index, 0, { id, title, panel, url });
+ } else {
+ tabs.push({ id, title, panel, url });
+ }
+
+ const newState = Object.assign({}, this.state, {
+ tabs,
+ });
+
+ if (selected) {
+ newState.activeTab = index >= 0 ? index : tabs.length - 1;
+ }
+
+ this.setState(newState, () => {
+ if (this.props.onSelect && selected) {
+ this.props.onSelect(id);
+ }
+ });
+ }
+
+ addAllQueuedTabs() {
+ if (!this.queuedTabs.length) {
+ return;
+ }
+
+ const tabs = this.state.tabs.slice();
+
+ // Preselect the first sidebar tab if none was explicitly selected.
+ let activeTab = 0;
+ let activeId = this.queuedTabs[0].id;
+
+ for (const { id, index, panel, selected, title, url } of this.queuedTabs) {
+ if (index >= 0) {
+ tabs.splice(index, 0, { id, title, panel, url });
+ } else {
+ tabs.push({ id, title, panel, url });
+ }
+
+ if (selected) {
+ activeId = id;
+ activeTab = index >= 0 ? index : tabs.length - 1;
+ }
+ }
+
+ const newState = Object.assign({}, this.state, {
+ activeTab,
+ tabs,
+ });
+
+ this.setState(newState, () => {
+ if (this.props.onSelect) {
+ this.props.onSelect(activeId);
+ }
+ });
+
+ this.queuedTabs = [];
+ }
+
+ /**
+ * Queues a tab to be added. This is more performant than calling addTab for every
+ * single tab to be added since we will limit the number of renders happening when
+ * a new state is set. Once all the tabs to be added have been queued, call
+ * addAllQueuedTabs() to populate the TabBar with all the queued tabs.
+ */
+ queueTab(id, title, selected = false, panel, url, index = -1) {
+ this.queuedTabs.push({
+ id,
+ index,
+ panel,
+ selected,
+ title,
+ url,
+ });
+ }
+
+ toggleTab(tabId, isVisible) {
+ const index = this.getTabIndex(tabId);
+ if (index < 0) {
+ return;
+ }
+
+ const tabs = this.state.tabs.slice();
+ tabs[index] = Object.assign({}, tabs[index], {
+ isVisible,
+ });
+
+ this.setState(
+ Object.assign({}, this.state, {
+ tabs,
+ })
+ );
+ }
+
+ removeTab(tabId) {
+ const index = this.getTabIndex(tabId);
+ if (index < 0) {
+ return;
+ }
+
+ const tabs = this.state.tabs.slice();
+ tabs.splice(index, 1);
+
+ let activeTab = this.state.activeTab - 1;
+ activeTab = activeTab === -1 ? 0 : activeTab;
+
+ this.setState(
+ Object.assign({}, this.state, {
+ activeTab,
+ tabs,
+ }),
+ () => {
+ // Select the next active tab and force the select event handler to initialize
+ // the panel if needed.
+ if (tabs.length && this.props.onSelect) {
+ this.props.onSelect(this.getTabId(activeTab));
+ }
+ }
+ );
+ }
+
+ select(tabId) {
+ const docRef = this.tabbarRef.current.ownerDocument;
+
+ const index = this.getTabIndex(tabId);
+ if (index < 0) {
+ return;
+ }
+
+ const newState = Object.assign({}, this.state, {
+ activeTab: index,
+ });
+
+ const tabDomElement = docRef.querySelector(`[data-tab-index="${index}"]`);
+
+ if (tabDomElement) {
+ tabDomElement.scrollIntoView();
+ }
+
+ this.setState(newState, () => {
+ if (this.props.onSelect) {
+ this.props.onSelect(tabId);
+ }
+ });
+ }
+
+ // Helpers
+
+ getTabIndex(tabId) {
+ let tabIndex = -1;
+ this.state.tabs.forEach((tab, index) => {
+ if (tab.id === tabId) {
+ tabIndex = index;
+ }
+ });
+ return tabIndex;
+ }
+
+ getTabId(index) {
+ return this.state.tabs[index].id;
+ }
+
+ getCurrentTabId() {
+ return this.state.tabs[this.state.activeTab].id;
+ }
+
+ // Event Handlers
+
+ onTabChanged(index) {
+ this.setState(
+ {
+ activeTab: index,
+ },
+ () => {
+ if (this.props.onSelect) {
+ this.props.onSelect(this.state.tabs[index].id);
+ }
+ }
+ );
+ }
+
+ onAllTabsMenuClick(event) {
+ const menu = new Menu();
+ const target = event.target;
+
+ // Generate list of menu items from the list of tabs.
+ this.state.tabs.forEach(tab => {
+ menu.append(
+ new MenuItem({
+ label: tab.title,
+ type: "checkbox",
+ checked: this.getCurrentTabId() === tab.id,
+ click: () => this.select(tab.id),
+ })
+ );
+ });
+
+ // Show a drop down menu with frames.
+ menu.popupAtTarget(target);
+
+ return menu;
+ }
+
+ // Rendering
+
+ renderTab(tab) {
+ if (typeof tab.panel === "function") {
+ return tab.panel({
+ key: tab.id,
+ title: tab.title,
+ id: tab.id,
+ url: tab.url,
+ });
+ }
+
+ return tab.panel;
+ }
+
+ render() {
+ const tabs = this.state.tabs.map(tab => this.renderTab(tab));
+
+ return div(
+ {
+ className: "devtools-sidebar-tabs",
+ ref: this.tabbarRef,
+ },
+ Sidebar(
+ {
+ onAllTabsMenuClick: this.onAllTabsMenuClick,
+ renderOnlySelected: this.props.renderOnlySelected,
+ showAllTabsMenu: this.props.showAllTabsMenu,
+ allTabsMenuButtonTooltip: this.props.allTabsMenuButtonTooltip,
+ sidebarToggleButton: this.props.sidebarToggleButton,
+ activeTab: this.state.activeTab,
+ onAfterChange: this.onTabChanged,
+ },
+ tabs
+ )
+ );
+ }
+}
+
+module.exports = Tabbar;
diff --git a/devtools/client/shared/components/tabs/Tabs.css b/devtools/client/shared/components/tabs/Tabs.css
new file mode 100644
index 0000000000..d483925a75
--- /dev/null
+++ b/devtools/client/shared/components/tabs/Tabs.css
@@ -0,0 +1,128 @@
+/* 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/. */
+
+/* Tabs General Styles */
+
+.tabs {
+ --tab-height: var(--theme-toolbar-height);
+ height: 100%;
+ background: var(--theme-sidebar-background);
+ display: flex;
+ flex-direction: column;
+}
+
+.tabs.tabs-tall {
+ --tab-height: var(--theme-toolbar-tall-height);
+}
+
+/* Hides the tab strip in the TabBar */
+div[hidetabs=true] .tabs .tabs-navigation {
+ display: none;
+}
+
+.tabs .tabs-navigation {
+ box-sizing: border-box;
+ display: flex;
+ /* Reserve 1px for the border */
+ height: calc(var(--tab-height) + 1px);
+ position: relative;
+ border-bottom: 1px solid var(--theme-splitter-color);
+ background: var(--theme-tab-toolbar-background);
+}
+
+.tabs .tabs-menu {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ margin-inline-end: 15px;
+ flex-grow: 1;
+ /* Adjust outline so it's not clipped */
+ outline-offset: -2px;
+}
+
+/* The tab takes entire horizontal space and individual tabs
+ should stretch accordingly. Use flexbox for the behavior.
+ Use also `overflow: hidden` so, 'overflow' and 'underflow'
+ events are fired (it's utilized by the all-tabs-menu). */
+.tabs .tabs-navigation .tabs-menu {
+ overflow: hidden;
+ display: flex;
+ overflow-x: scroll;
+ scrollbar-width: none;
+}
+
+.tabs .tabs-menu-item {
+ display: inline-block;
+ position: relative;
+ margin: 0;
+ padding: 0;
+ color: var(--theme-toolbar-color);
+}
+
+.tabs .tabs-menu-item.is-active {
+ color: var(--theme-toolbar-selected-color);
+}
+
+.tabs .tabs-menu-item:hover {
+ background-color: var(--theme-toolbar-hover);
+}
+
+.tabs .tabs-menu-item:hover:active:not(.is-active) {
+ background-color: var(--theme-toolbar-hover-active);
+}
+
+.tabs .tabs-menu-item a {
+ --text-height: 16px;
+ --devtools-tab-border-width: 1px;
+ display: flex;
+ justify-content: center;
+ /* Vertically center text, calculate space remaining by taking the full height and removing
+ the block borders and text. Divide by 2 to distribute above and below. */
+ padding: calc((var(--tab-height) - var(--text-height) - (var(--devtools-tab-border-width) * 2)) / 2) 10px;
+ border: var(--devtools-tab-border-width) solid transparent;
+ font-size: 12px;
+ line-height: var(--text-height);
+ text-decoration: none;
+ white-space: nowrap;
+ cursor: default;
+ user-select: none;
+ text-align: center;
+}
+
+.tabs .tabs-navigation .tabs-menu-item > a {
+ outline-offset: -2px;
+}
+
+.tabs .tabs-menu-item .tab-badge {
+ color: var(--theme-highlight-blue);
+ font-size: 80%;
+ font-style: italic;
+ /* Tabs have a 15px padding start/end, so we set the margins here in order to center the
+ badge after the tab title, with a 5px effective margin. */
+ margin-inline-start: 5px;
+ margin-inline-end: -10px;
+}
+
+.tabs .tabs-menu-item.is-active .tab-badge {
+ /* Use the same color as the tab-item when active */
+ color: inherit;
+}
+
+/* To avoid "select all" commands from selecting content in hidden tabs */
+.tabs .hidden,
+.tabs .hidden * {
+ user-select: none !important;
+}
+
+/* Make sure panel content takes entire vertical space. */
+.tabs .panels {
+ flex: 1;
+ overflow: hidden;
+}
+
+.tabs .tab-panel {
+ height: 100%;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
diff --git a/devtools/client/shared/components/tabs/Tabs.js b/devtools/client/shared/components/tabs/Tabs.js
new file mode 100644
index 0000000000..a265032f9e
--- /dev/null
+++ b/devtools/client/shared/components/tabs/Tabs.js
@@ -0,0 +1,468 @@
+/* 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/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+ const {
+ Component,
+ createRef,
+ } = require("devtools/client/shared/vendor/react");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ /**
+ * Renders simple 'tab' widget.
+ *
+ * Based on ReactSimpleTabs component
+ * https://github.com/pedronauck/react-simpletabs
+ *
+ * Component markup (+CSS) example:
+ *
+ * <div class='tabs'>
+ * <nav class='tabs-navigation'>
+ * <ul class='tabs-menu'>
+ * <li class='tabs-menu-item is-active'>Tab #1</li>
+ * <li class='tabs-menu-item'>Tab #2</li>
+ * </ul>
+ * </nav>
+ * <div class='panels'>
+ * The content of active panel here
+ * </div>
+ * <div>
+ */
+ class Tabs extends Component {
+ static get propTypes() {
+ return {
+ className: PropTypes.oneOfType([
+ PropTypes.array,
+ PropTypes.string,
+ PropTypes.object,
+ ]),
+ activeTab: PropTypes.number,
+ onMount: PropTypes.func,
+ onBeforeChange: PropTypes.func,
+ onAfterChange: PropTypes.func,
+ children: PropTypes.oneOfType([PropTypes.array, PropTypes.element])
+ .isRequired,
+ showAllTabsMenu: PropTypes.bool,
+ allTabsMenuButtonTooltip: PropTypes.string,
+ onAllTabsMenuClick: PropTypes.func,
+ tall: PropTypes.bool,
+
+ // To render a sidebar toggle button before the tab menu provide a function that
+ // returns a React component for the button.
+ renderSidebarToggle: PropTypes.func,
+ // Set true will only render selected panel on DOM. It's complete
+ // opposite of the created array, and it's useful if panels content
+ // is unpredictable and update frequently.
+ renderOnlySelected: PropTypes.bool,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ activeTab: 0,
+ showAllTabsMenu: false,
+ renderOnlySelected: false,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ activeTab: props.activeTab,
+
+ // This array is used to store an object containing information on whether a tab
+ // at a specified index has already been created (e.g. selected at least once) and
+ // the tab id. An example of the object structure is the following:
+ // [{ isCreated: true, tabId: "ruleview" }, { isCreated: false, tabId: "foo" }].
+ // If the tab at the specified index has already been created, it's rendered even
+ // if not currently selected. This is because in some cases we don't want
+ // to re-create tab content when it's being unselected/selected.
+ // E.g. in case of an iframe being used as a tab-content we want the iframe to
+ // stay in the DOM.
+ created: [],
+
+ // True if tabs can't fit into available horizontal space.
+ overflow: false,
+ };
+
+ this.tabsEl = createRef();
+
+ this.onOverflow = this.onOverflow.bind(this);
+ this.onUnderflow = this.onUnderflow.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
+ this.onClickTab = this.onClickTab.bind(this);
+ this.setActive = this.setActive.bind(this);
+ this.renderMenuItems = this.renderMenuItems.bind(this);
+ this.renderPanels = this.renderPanels.bind(this);
+ }
+
+ componentDidMount() {
+ const node = this.tabsEl.current;
+ node.addEventListener("keydown", this.onKeyDown);
+
+ // Register overflow listeners to manage visibility
+ // of all-tabs-menu. This menu is displayed when there
+ // is not enough h-space to render all tabs.
+ // It allows the user to select a tab even if it's hidden.
+ if (this.props.showAllTabsMenu) {
+ node.addEventListener("overflow", this.onOverflow);
+ node.addEventListener("underflow", this.onUnderflow);
+ }
+
+ const index = this.state.activeTab;
+ if (this.props.onMount) {
+ this.props.onMount(index);
+ }
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ let { children, activeTab } = nextProps;
+ const panels = children.filter(panel => panel);
+ let created = [...this.state.created];
+
+ // If the children props has changed due to an addition or removal of a tab,
+ // update the state's created array with the latest tab ids and whether or not
+ // the tab is already created.
+ if (this.state.created.length != panels.length) {
+ created = panels.map(panel => {
+ // Get whether or not the tab has already been created from the previous state.
+ const createdEntry = this.state.created.find(entry => {
+ return entry && entry.tabId === panel.props.id;
+ });
+ const isCreated = !!createdEntry && createdEntry.isCreated;
+ const tabId = panel.props.id;
+
+ return {
+ isCreated,
+ tabId,
+ };
+ });
+ }
+
+ // Check type of 'activeTab' props to see if it's valid (it's 0-based index).
+ if (typeof activeTab === "number") {
+ // Reset to index 0 if index overflows the range of panel array
+ activeTab = activeTab < panels.length && activeTab >= 0 ? activeTab : 0;
+
+ created[activeTab] = Object.assign({}, created[activeTab], {
+ isCreated: true,
+ });
+
+ this.setState({
+ activeTab,
+ });
+ }
+
+ this.setState({
+ created,
+ });
+ }
+
+ componentWillUnmount() {
+ const node = this.tabsEl.current;
+ node.removeEventListener("keydown", this.onKeyDown);
+
+ if (this.props.showAllTabsMenu) {
+ node.removeEventListener("overflow", this.onOverflow);
+ node.removeEventListener("underflow", this.onUnderflow);
+ }
+ }
+
+ // DOM Events
+
+ onOverflow(event) {
+ if (event.target.classList.contains("tabs-menu")) {
+ this.setState({
+ overflow: true,
+ });
+ }
+ }
+
+ onUnderflow(event) {
+ if (event.target.classList.contains("tabs-menu")) {
+ this.setState({
+ overflow: false,
+ });
+ }
+ }
+
+ onKeyDown(event) {
+ // Bail out if the focus isn't on a tab.
+ if (!event.target.closest(".tabs-menu-item")) {
+ return;
+ }
+
+ let activeTab = this.state.activeTab;
+ const tabCount = this.props.children.length;
+
+ const ltr = event.target.ownerDocument.dir == "ltr";
+ const nextOrLastTab = Math.min(tabCount - 1, activeTab + 1);
+ const previousOrFirstTab = Math.max(0, activeTab - 1);
+
+ switch (event.code) {
+ case "ArrowRight":
+ if (ltr) {
+ activeTab = nextOrLastTab;
+ } else {
+ activeTab = previousOrFirstTab;
+ }
+ break;
+ case "ArrowLeft":
+ if (ltr) {
+ activeTab = previousOrFirstTab;
+ } else {
+ activeTab = nextOrLastTab;
+ }
+ break;
+ }
+
+ if (this.state.activeTab != activeTab) {
+ this.setActive(activeTab);
+ }
+ }
+
+ onClickTab(index, event) {
+ this.setActive(index);
+
+ if (event) {
+ event.preventDefault();
+ }
+ }
+
+ onMouseDown(event) {
+ // Prevents click-dragging the tab headers
+ if (event) {
+ event.preventDefault();
+ }
+ }
+
+ // API
+
+ setActive(index) {
+ const onAfterChange = this.props.onAfterChange;
+ const onBeforeChange = this.props.onBeforeChange;
+
+ if (onBeforeChange) {
+ const cancel = onBeforeChange(index);
+ if (cancel) {
+ return;
+ }
+ }
+
+ const created = [...this.state.created];
+ created[index] = Object.assign({}, created[index], {
+ isCreated: true,
+ });
+
+ const newState = Object.assign({}, this.state, {
+ created,
+ activeTab: index,
+ });
+
+ this.setState(newState, () => {
+ // Properly set focus on selected tab.
+ const selectedTab = this.tabsEl.current.querySelector(".is-active > a");
+ if (selectedTab) {
+ selectedTab.focus();
+ }
+
+ if (onAfterChange) {
+ onAfterChange(index);
+ }
+ });
+ }
+
+ // Rendering
+
+ renderMenuItems() {
+ if (!this.props.children) {
+ throw new Error("There must be at least one Tab");
+ }
+
+ if (!Array.isArray(this.props.children)) {
+ this.props.children = [this.props.children];
+ }
+
+ const tabs = this.props.children
+ .map(tab => (typeof tab === "function" ? tab() : tab))
+ .filter(tab => tab)
+ .map((tab, index) => {
+ const {
+ id,
+ className: tabClassName,
+ title,
+ badge,
+ showBadge,
+ } = tab.props;
+
+ const ref = "tab-menu-" + index;
+ const isTabSelected = this.state.activeTab === index;
+
+ const className = [
+ "tabs-menu-item",
+ tabClassName,
+ isTabSelected ? "is-active" : "",
+ ].join(" ");
+
+ // Set tabindex to -1 (except the selected tab) so, it's focusable,
+ // but not reachable via sequential tab-key navigation.
+ // Changing selected tab (and so, moving focus) is done through
+ // left and right arrow keys.
+ // See also `onKeyDown()` event handler.
+ return dom.li(
+ {
+ className,
+ key: index,
+ ref,
+ role: "presentation",
+ },
+ dom.span({ className: "devtools-tab-line" }),
+ dom.a(
+ {
+ id: id ? id + "-tab" : "tab-" + index,
+ tabIndex: isTabSelected ? 0 : -1,
+ title,
+ "aria-controls": id ? id + "-panel" : "panel-" + index,
+ "aria-selected": isTabSelected,
+ role: "tab",
+ onClick: this.onClickTab.bind(this, index),
+ onMouseDown: this.onMouseDown.bind(this),
+ "data-tab-index": index,
+ },
+ title,
+ badge && !isTabSelected && showBadge()
+ ? dom.span({ className: "tab-badge" }, badge)
+ : null
+ )
+ );
+ });
+
+ // Display the menu only if there is not enough horizontal
+ // space for all tabs (and overflow happened).
+ const allTabsMenu = this.state.overflow
+ ? dom.button({
+ className: "all-tabs-menu",
+ title: this.props.allTabsMenuButtonTooltip,
+ onClick: this.props.onAllTabsMenuClick,
+ })
+ : null;
+
+ // Get the sidebar toggle button if a renderSidebarToggle function is provided.
+ const sidebarToggle = this.props.renderSidebarToggle
+ ? this.props.renderSidebarToggle()
+ : null;
+
+ return dom.nav(
+ { className: "tabs-navigation" },
+ sidebarToggle,
+ dom.ul({ className: "tabs-menu", role: "tablist" }, tabs),
+ allTabsMenu
+ );
+ }
+
+ renderPanels() {
+ let { children, renderOnlySelected } = this.props;
+
+ if (!children) {
+ throw new Error("There must be at least one Tab");
+ }
+
+ if (!Array.isArray(children)) {
+ children = [children];
+ }
+
+ const selectedIndex = this.state.activeTab;
+
+ const panels = children
+ .map(tab => (typeof tab === "function" ? tab() : tab))
+ .filter(tab => tab)
+ .map((tab, index) => {
+ const selected = selectedIndex === index;
+ if (renderOnlySelected && !selected) {
+ return null;
+ }
+
+ const id = tab.props.id;
+ const isCreated =
+ this.state.created[index] && this.state.created[index].isCreated;
+
+ // Use 'visibility:hidden' + 'height:0' for hiding content of non-selected
+ // tab. It's faster than 'display:none' because it avoids triggering frame
+ // destruction and reconstruction. 'width' is not changed to avoid relayout.
+ const style = {
+ visibility: selected ? "visible" : "hidden",
+ height: selected ? "100%" : "0",
+ };
+
+ // Allows lazy loading panels by creating them only if they are selected,
+ // then store a copy of the lazy created panel in `tab.panel`.
+ if (typeof tab.panel == "function" && selected) {
+ tab.panel = tab.panel(tab);
+ }
+ const panel = tab.panel || tab;
+
+ return dom.div(
+ {
+ id: id ? id + "-panel" : "panel-" + index,
+ key: id,
+ style,
+ className: selected ? "tab-panel-box" : "tab-panel-box hidden",
+ role: "tabpanel",
+ "aria-labelledby": id ? id + "-tab" : "tab-" + index,
+ },
+ selected || isCreated ? panel : null
+ );
+ });
+
+ return dom.div({ className: "panels" }, panels);
+ }
+
+ render() {
+ return dom.div(
+ {
+ className: [
+ "tabs",
+ ...(this.props.tall ? ["tabs-tall"] : []),
+ this.props.className,
+ ].join(" "),
+ ref: this.tabsEl,
+ },
+ this.renderMenuItems(),
+ this.renderPanels()
+ );
+ }
+ }
+
+ /**
+ * Renders simple tab 'panel'.
+ */
+ class Panel extends Component {
+ static get propTypes() {
+ return {
+ id: PropTypes.string.isRequired,
+ className: PropTypes.string,
+ title: PropTypes.string.isRequired,
+ children: PropTypes.oneOfType([PropTypes.array, PropTypes.element])
+ .isRequired,
+ };
+ }
+
+ render() {
+ const { className } = this.props;
+ return dom.div(
+ { className: `tab-panel ${className || ""}` },
+ this.props.children
+ );
+ }
+ }
+
+ // Exports from this module
+ exports.TabPanel = Panel;
+ exports.Tabs = Tabs;
+});
diff --git a/devtools/client/shared/components/tabs/moz.build b/devtools/client/shared/components/tabs/moz.build
new file mode 100644
index 0000000000..15ede75b9d
--- /dev/null
+++ b/devtools/client/shared/components/tabs/moz.build
@@ -0,0 +1,10 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "TabBar.js",
+ "Tabs.js",
+)
diff --git a/devtools/client/shared/components/test/browser/browser.toml b/devtools/client/shared/components/test/browser/browser.toml
new file mode 100644
index 0000000000..46412aae90
--- /dev/null
+++ b/devtools/client/shared/components/test/browser/browser.toml
@@ -0,0 +1,14 @@
+[DEFAULT]
+tags = "devtools"
+subsuite = "devtools"
+support-files = [
+ "!/devtools/client/shared/test/shared-head.js",
+ "!/devtools/client/shared/test/telemetry-test-helpers.js",
+]
+
+["browser_notification_box_basic.js"]
+
+["browser_reps_stubs.js"]
+skip-if = [
+ "!fission", # we need specific iframe targets for some evaluation
+]
diff --git a/devtools/client/shared/components/test/browser/browser_notification_box_basic.js b/devtools/client/shared/components/test/browser/browser_notification_box_basic.js
new file mode 100644
index 0000000000..0103c677d2
--- /dev/null
+++ b/devtools/client/shared/components/test/browser/browser_notification_box_basic.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+ this
+);
+
+const TEST_URI = "data:text/html;charset=utf-8,Test page";
+
+/**
+ * Basic test that checks existence of the Notification box.
+ */
+add_task(async function () {
+ info("Test Notification box basic started");
+
+ const toolbox = await openNewTabAndToolbox(TEST_URI, "webconsole");
+
+ // Append a notification
+ const notificationBox = toolbox.getNotificationBox();
+ notificationBox.appendNotification(
+ "Info message",
+ "id1",
+ null,
+ notificationBox.PRIORITY_INFO_HIGH
+ );
+
+ // Verify existence of one notification.
+ const parentNode = toolbox.doc.getElementById("toolbox-notificationbox");
+ const nodes = parentNode.querySelectorAll(".notification");
+ is(nodes.length, 1, "There must be one notification");
+});
diff --git a/devtools/client/shared/components/test/browser/browser_reps_stubs.js b/devtools/client/shared/components/test/browser/browser_reps_stubs.js
new file mode 100644
index 0000000000..29b56ab151
--- /dev/null
+++ b/devtools/client/shared/components/test/browser/browser_reps_stubs.js
@@ -0,0 +1,406 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+ this
+);
+
+const TEST_URI = "data:text/html;charset=utf-8,stub generation";
+/**
+ * A Map keyed by filename, and for which the value is also a Map, with the key being the
+ * label for the stub, and the value the expression to evaluate to get the stub.
+ */
+const EXPRESSIONS_BY_FILE = {
+ "attribute.js": new Map([
+ [
+ "Attribute",
+ `{
+ const a = document.createAttribute("class")
+ a.value = "autocomplete-suggestions";
+ a;
+ }`,
+ ],
+ ]),
+ "comment-node.js": new Map([
+ [
+ "Comment",
+ `{
+ document.createComment("test\\nand test\\nand test\\nand test\\nand test\\nand test\\nand test")
+ }`,
+ ],
+ ]),
+ "date-time.js": new Map([
+ ["DateTime", `new Date(1459372644859)`],
+ ["InvalidDateTime", `new Date("invalid")`],
+ ]),
+ "infinity.js": new Map([
+ ["Infinity", `Infinity`],
+ ["NegativeInfinity", `-Infinity`],
+ ]),
+ "nan.js": new Map([["NaN", `2 * document`]]),
+ "null.js": new Map([["Null", `null`]]),
+ "number.js": new Map([
+ ["Int", `2 + 3`],
+ ["True", `true`],
+ ["False", `false`],
+ ["NegZeroGrip", `1 / -Infinity`],
+ ]),
+ "stylesheet.js": new Map([
+ [
+ "StyleSheet",
+ {
+ expression: `
+ (async function() {
+ const link = document.createElement("link");
+ link.setAttribute("rel", "stylesheet");
+ link.type = "text/css";
+ link.href = "https://example.com/styles.css";
+ const onStylesheetHandled = new Promise(res => {
+ // The file does not exist so we'll get an error event, but it will
+ // still be put in document.styleSheets with its src, which is what we want.
+ link.addEventListener("error", () => res(), { once: true});
+ })
+ document.head.appendChild(link);
+ await onStylesheetHandled;
+ return document.styleSheets[0];
+ })()
+ `,
+ async: true,
+ },
+ ],
+ ]),
+ "symbol.js": new Map([
+ ["Symbol", `Symbol("foo")`],
+ ["SymbolWithoutIdentifier", `Symbol()`],
+ ["SymbolWithLongString", `Symbol("aa".repeat(10000))`],
+ ]),
+ "text-node.js": new Map([
+ [
+ "testRendering",
+ `let tn = document.createTextNode("hello world");
+ document.body.append(tn);
+ tn;`,
+ ],
+ ["testRenderingDisconnected", `document.createTextNode("hello world")`],
+ ["testRenderingWithEOL", `document.createTextNode("hello\\nworld")`],
+ ["testRenderingWithDoubleQuote", `document.createTextNode('hello"world')`],
+ [
+ "testRenderingWithLongString",
+ `document.createTextNode("a\\n" + ("a").repeat(20000))`,
+ ],
+ ]),
+ "undefined.js": new Map([["Undefined", `undefined`]]),
+ "window.js": new Map([
+ ["Window", `window`],
+ [
+ "CrossOriginIframeContentWindow",
+ {
+ expression: `
+ (async function() {
+ const iframe = document.createElement("iframe");
+ const onLoaded = new Promise(resolve =>
+ iframe.addEventListener("load", resolve, {once: true})
+ );
+ iframe.src = "http://example.org/document-builder.sjs?html=example.org";
+ document.body.append(iframe);
+ await onLoaded;
+ return iframe.contentWindow;
+ })()
+ `,
+ async: true,
+ },
+ ],
+ [
+ "CrossOriginIframeTopWindow",
+ {
+ expression: `window.top`,
+ iframeUrlForExecution:
+ "https://example.net/document-builder.sjs?html=example.net",
+ },
+ ],
+ ]),
+ // XXX: File a bug blocking Bug 1671400 for enabling automatic generation for one of
+ // the following file.
+ // "accessible.js",
+ // "accessor.js",
+ // "big-int.js",
+ // "document-type.js",
+ // "document.js",
+ // "element-node.js",
+ // "error.js",
+ // "event.js",
+ // "failure.js",
+ // "function.js",
+ // "grip-array.js",
+ // "grip-entry.js",
+ // "grip-map.js",
+ // "grip.js",
+ // "long-string.js",
+ // "object-with-text.js",
+ // "object-with-url.js",
+ // "promise.js",
+ // "regexp.js",
+};
+
+add_task(async function () {
+ const isStubsUpdate = Services.env.get(STUBS_UPDATE_ENV) == "true";
+
+ const tab = await addTab(TEST_URI);
+ const {
+ CommandsFactory,
+ } = require("devtools/shared/commands/commands-factory");
+ const commands = await CommandsFactory.forTab(tab);
+ await commands.targetCommand.startListening();
+
+ let failed = false;
+ for (const stubFile of Object.keys(EXPRESSIONS_BY_FILE)) {
+ info(`${isStubsUpdate ? "Update" : "Check"} ${stubFile}`);
+
+ const generatedStubs = await generateStubs(commands, stubFile);
+ if (isStubsUpdate) {
+ await writeStubsToFile(stubFile, generatedStubs);
+ ok(true, `${stubFile} was updated`);
+ continue;
+ }
+
+ const existingStubs = getStubFile(stubFile);
+ if (generatedStubs.size !== existingStubs.size) {
+ failed = true;
+ continue;
+ }
+
+ for (const [key, packet] of generatedStubs) {
+ const packetStr = getSerializedPacket(packet, {
+ sortKeys: true,
+ replaceActorIds: true,
+ });
+ const grip = getSerializedPacket(existingStubs.get(key), {
+ sortKeys: true,
+ replaceActorIds: true,
+ });
+ is(packetStr, grip, `"${key}" packet has expected value`);
+ failed = failed || packetStr !== grip;
+ }
+ }
+
+ if (failed) {
+ ok(
+ false,
+ "The reps stubs need to be updated by running `" +
+ `mach test ${getCurrentTestFilePath()} --headless --setenv STUBS_UPDATE=true` +
+ "`"
+ );
+ } else {
+ ok(true, "Stubs are up to date");
+ }
+
+ await removeTab(tab);
+});
+
+async function generateStubs(commands, stubFile) {
+ const stubs = new Map();
+
+ for (const [key, options] of EXPRESSIONS_BY_FILE[stubFile]) {
+ const expression =
+ typeof options == "string" ? options : options.expression;
+ const executeOptions = {};
+ if (options.async === true) {
+ executeOptions.mapped = { await: true };
+ }
+ if (options.iframeUrlForExecution) {
+ const { promise: onIframeTargetCreated, resolve } =
+ Promise.withResolvers();
+ const onTargetAvailable = ({ targetFront }) => {
+ if (targetFront.url === options.iframeUrlForExecution) {
+ resolve(targetFront);
+ }
+ };
+ await commands.targetCommand.watchTargets({
+ types: [commands.targetCommand.TYPES.FRAME],
+ onAvailable: onTargetAvailable,
+ });
+
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [options.iframeUrlForExecution],
+ url => {
+ const iframe = content.document.createElement("iframe");
+ iframe.src = url;
+ content.document.body.append(iframe);
+ }
+ );
+
+ const targetFront = await onIframeTargetCreated;
+ executeOptions.selectedTargetFront = targetFront;
+
+ await commands.targetCommand.unwatchTargets({
+ types: [commands.targetCommand.TYPES.FRAME],
+ onAvailable: onTargetAvailable,
+ });
+ }
+ const { result } = await commands.scriptCommand.execute(
+ expression,
+ executeOptions
+ );
+ stubs.set(key, getCleanedPacket(stubFile, key, result));
+ }
+
+ return stubs;
+}
+
+function getCleanedPacket(stubFile, key, packet) {
+ // Remove the targetFront property that has a cyclical reference and that we don't need
+ // in our node tests.
+ delete packet.targetFront;
+
+ const existingStubs = getStubFile(stubFile);
+ if (!existingStubs) {
+ return packet;
+ }
+
+ // Strip escaped characters.
+ const safeKey = key
+ .replace(/\\n/g, "\n")
+ .replace(/\\r/g, "\r")
+ .replace(/\\\"/g, `\"`)
+ .replace(/\\\'/g, `\'`);
+ if (!existingStubs.has(safeKey)) {
+ return packet;
+ }
+
+ // If the stub already exist, we want to ignore irrelevant properties (generated id, timer, …)
+ // that might changed and "pollute" the diff resulting from this stub generation.
+ const existingPacket = existingStubs.get(safeKey);
+
+ // copy existing contentDomReference
+ if (
+ packet._grip?.contentDomReference?.id &&
+ existingPacket._grip?.contentDomReference?.id
+ ) {
+ packet._grip.contentDomReference = existingPacket._grip.contentDomReference;
+ }
+
+ // `window`'s properties count can vary from OS to OS, so we clean `ownPropertyLength`.
+ if (
+ existingPacket &&
+ packet._grip?.class === "Window" &&
+ typeof packet._grip.ownPropertyLength ==
+ typeof existingPacket._grip.ownPropertyLength
+ ) {
+ packet._grip.ownPropertyLength = existingPacket._grip.ownPropertyLength;
+ }
+
+ return packet;
+}
+
+// HELPER
+
+const CHROME_PREFIX = "chrome://mochitests/content/browser/";
+const STUBS_FOLDER = "devtools/client/shared/components/test/node/stubs/reps/";
+const STUBS_UPDATE_ENV = "STUBS_UPDATE";
+
+/**
+ * Write stubs to a given file
+ *
+ * @param {String} fileName: The file to write the stubs in.
+ * @param {Map} packets: A Map of the packets.
+ */
+async function writeStubsToFile(fileName, packets) {
+ const mozRepo = Services.env.get("MOZ_DEVELOPER_REPO_DIR");
+ const filePath = `${mozRepo}/${STUBS_FOLDER + fileName}`;
+
+ const stubs = Array.from(packets.entries()).map(([key, packet]) => {
+ const stringifiedPacket = getSerializedPacket(packet);
+ return `stubs.set(\`${key}\`, ${stringifiedPacket});`;
+ });
+
+ const fileContent = `/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+${stubs.join("\n\n")}
+
+module.exports = stubs;
+`;
+
+ const textEncoder = new TextEncoder();
+ await IOUtils.write(filePath, textEncoder.encode(fileContent));
+}
+
+function getStubFile(fileName) {
+ return require(CHROME_PREFIX + STUBS_FOLDER + fileName);
+}
+
+function sortObjectKeys(obj) {
+ const isArray = Array.isArray(obj);
+ const isObject = Object.prototype.toString.call(obj) === "[object Object]";
+ const isFront = obj?._grip;
+
+ if (isObject && !isFront) {
+ // Reorder keys for objects, but skip fronts to avoid infinite recursion.
+ const sortedKeys = Object.keys(obj).sort((k1, k2) => k1.localeCompare(k2));
+ const withSortedKeys = {};
+ sortedKeys.forEach(k => {
+ withSortedKeys[k] = k !== "stacktrace" ? sortObjectKeys(obj[k]) : obj[k];
+ });
+ return withSortedKeys;
+ } else if (isArray) {
+ return obj.map(item => sortObjectKeys(item));
+ }
+ return obj;
+}
+
+/**
+ * @param {Object} packet
+ * The packet to serialize.
+ * @param {Object} options
+ * @param {Boolean} options.sortKeys
+ * Pass true to sort all keys alphabetically in the packet before serialization.
+ * For instance stub comparison should not fail if the order of properties changed.
+ * @param {Boolean} options.replaceActorIds
+ * Pass true to replace actorIDs with a fake one so it's easier to compare stubs
+ * that includes grips.
+ */
+function getSerializedPacket(
+ packet,
+ { sortKeys = false, replaceActorIds = false } = {}
+) {
+ if (sortKeys) {
+ packet = sortObjectKeys(packet);
+ }
+
+ const actorIdPlaceholder = "XXX";
+
+ return JSON.stringify(
+ packet,
+ function (key, value) {
+ // The message can have fronts that we need to serialize
+ if (value && value._grip) {
+ return {
+ _grip: value._grip,
+ actorID: replaceActorIds ? actorIdPlaceholder : value.actorID,
+ };
+ }
+
+ if (
+ replaceActorIds &&
+ (key === "actor" || key === "actorID" || key === "sourceId") &&
+ typeof value === "string"
+ ) {
+ return actorIdPlaceholder;
+ }
+
+ return value;
+ },
+ 2
+ );
+}
diff --git a/devtools/client/shared/components/test/chrome/accordion.snapshots.js b/devtools/client/shared/components/test/chrome/accordion.snapshots.js
new file mode 100644
index 0000000000..0f649b52d6
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/accordion.snapshots.js
@@ -0,0 +1,176 @@
+/* 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/. */
+"use strict";
+
+window._snapshots = {
+ "Accordion basic render.": {
+ type: "ul",
+ props: {
+ className: "accordion",
+ tabIndex: -1,
+ },
+ children: [
+ {
+ type: "li",
+ props: {
+ id: "accordion-item-1",
+ className: "accordion-item",
+ "aria-labelledby": "accordion-item-1-header",
+ },
+ children: [
+ {
+ type: "h2",
+ props: {
+ id: "accordion-item-1-header",
+ className: "accordion-header",
+ tabIndex: 0,
+ "aria-expanded": false,
+ "aria-label": "Test Accordion Item 1",
+ onKeyDown: "event => this.onHeaderKeyDown(event, item)",
+ onClick: "event => this.onHeaderClick(event, item)",
+ },
+ children: [
+ {
+ type: "span",
+ props: {
+ className: "theme-twisty",
+ role: "presentation",
+ },
+ children: null,
+ },
+ {
+ type: "span",
+ props: { className: "accordion-header-label" },
+ children: ["Test Accordion Item 1"],
+ },
+ ],
+ },
+ {
+ type: "div",
+ props: {
+ className: "accordion-content",
+ hidden: true,
+ role: "presentation",
+ },
+ children: null,
+ },
+ ],
+ },
+ {
+ type: "li",
+ props: {
+ id: "accordion-item-2",
+ className: "accordion-item",
+ "aria-labelledby": "accordion-item-2-header",
+ },
+ children: [
+ {
+ type: "h2",
+ props: {
+ id: "accordion-item-2-header",
+ className: "accordion-header",
+ tabIndex: 0,
+ "aria-expanded": false,
+ "aria-label": "Test Accordion Item 2",
+ onKeyDown: "event => this.onHeaderKeyDown(event, item)",
+ onClick: "event => this.onHeaderClick(event, item)",
+ },
+ children: [
+ {
+ type: "span",
+ props: {
+ className: "theme-twisty",
+ role: "presentation",
+ },
+ children: null,
+ },
+ {
+ type: "span",
+ props: { className: "accordion-header-label" },
+ children: ["Test Accordion Item 2"],
+ },
+ {
+ type: "span",
+ props: {
+ className: "accordion-header-buttons",
+ role: "presentation",
+ },
+ children: [
+ {
+ type: "button",
+ props: {},
+ children: null,
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: "div",
+ props: {
+ className: "accordion-content",
+ hidden: true,
+ role: "presentation",
+ },
+ children: null,
+ },
+ ],
+ },
+ {
+ type: "li",
+ props: {
+ id: "accordion-item-3",
+ className: "accordion-item accordion-open",
+ "aria-labelledby": "accordion-item-3-header",
+ },
+ children: [
+ {
+ type: "h2",
+ props: {
+ id: "accordion-item-3-header",
+ className: "accordion-header",
+ tabIndex: 0,
+ "aria-expanded": true,
+ "aria-label": "Test Accordion Item 3",
+ onKeyDown: "event => this.onHeaderKeyDown(event, item)",
+ onClick: "event => this.onHeaderClick(event, item)",
+ },
+ children: [
+ {
+ type: "span",
+ props: {
+ className: "theme-twisty open",
+ role: "presentation",
+ },
+ children: null,
+ },
+ {
+ type: "span",
+ props: {
+ className: "accordion-header-label",
+ },
+ children: ["Test Accordion Item 3"],
+ },
+ ],
+ },
+ {
+ type: "div",
+ props: {
+ className: "accordion-content",
+ hidden: false,
+ role: "presentation",
+ },
+ children: [
+ {
+ type: "div",
+ props: {},
+ children: null,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+};
diff --git a/devtools/client/shared/components/test/chrome/chrome.toml b/devtools/client/shared/components/test/chrome/chrome.toml
new file mode 100644
index 0000000000..ff9a5f1883
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/chrome.toml
@@ -0,0 +1,87 @@
+[DEFAULT]
+support-files = [
+ "head.js",
+ "accordion.snapshots.js",
+]
+
+["test_GridElementWidthResizer.html"]
+
+["test_GridElementWidthResizer_RTL.html"]
+
+["test_HSplitBox_01.html"]
+
+["test_accordion.html"]
+
+["test_frame_01.html"]
+
+["test_frame_02.html"]
+
+["test_list.html"]
+
+["test_list_keyboard.html"]
+
+["test_notification_box_01.html"]
+
+["test_notification_box_02.html"]
+
+["test_notification_box_03.html"]
+
+["test_notification_box_04.html"]
+
+["test_notification_box_05.html"]
+
+["test_searchbox-with-autocomplete.html"]
+
+["test_searchbox.html"]
+
+["test_sidebar_toggle.html"]
+
+["test_smart-trace-grouping.html"]
+
+["test_smart-trace-source-maps.html"]
+
+["test_smart-trace.html"]
+
+["test_stack-trace-source-maps.html"]
+
+["test_stack-trace.html"]
+
+["test_tabs_accessibility.html"]
+
+["test_tabs_menu.html"]
+
+["test_tree-view_01.html"]
+
+["test_tree-view_02.html"]
+
+["test_tree_01.html"]
+
+["test_tree_02.html"]
+
+["test_tree_03.html"]
+
+["test_tree_04.html"]
+
+["test_tree_05.html"]
+
+["test_tree_06.html"]
+
+["test_tree_07.html"]
+
+["test_tree_08.html"]
+
+["test_tree_09.html"]
+
+["test_tree_10.html"]
+
+["test_tree_11.html"]
+
+["test_tree_12.html"]
+
+["test_tree_13.html"]
+
+["test_tree_14.html"]
+
+["test_tree_15.html"]
+
+["test_tree_16.html"]
diff --git a/devtools/client/shared/components/test/chrome/head.js b/devtools/client/shared/components/test/chrome/head.js
new file mode 100644
index 0000000000..7abe54942f
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/head.js
@@ -0,0 +1,379 @@
+/* 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/. */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+/* global _snapshots */
+
+"use strict";
+
+var { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
+var { Assert } = ChromeUtils.importESModule(
+ "resource://testing-common/Assert.sys.mjs"
+);
+var { gDevTools } = require("resource://devtools/client/framework/devtools.js");
+var { BrowserLoader } = ChromeUtils.import(
+ "resource://devtools/shared/loader/browser-loader.js"
+);
+var {
+ DevToolsServer,
+} = require("resource://devtools/server/devtools-server.js");
+var {
+ DevToolsClient,
+} = require("resource://devtools/client/devtools-client.js");
+var DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
+var { Toolbox } = require("resource://devtools/client/framework/toolbox.js");
+
+var { require: browserRequire } = BrowserLoader({
+ baseURI: "resource://devtools/client/shared/",
+ window,
+});
+
+const React = browserRequire("devtools/client/shared/vendor/react");
+const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+const dom = browserRequire("devtools/client/shared/vendor/react-dom-factories");
+const TestUtils = browserRequire(
+ "devtools/client/shared/vendor/react-dom-test-utils"
+);
+
+const ShallowRenderer = browserRequire(
+ "devtools/client/shared/vendor/react-test-renderer-shallow"
+);
+const TestRenderer = browserRequire(
+ "devtools/client/shared/vendor/react-test-renderer"
+);
+
+var EXAMPLE_URL = "https://example.com/browser/browser/devtools/shared/test/";
+
+SimpleTest.registerCleanupFunction(() => {
+ window._snapshots = null;
+});
+
+function forceRender(comp) {
+ return setState(comp, {}).then(() => setState(comp, {}));
+}
+
+// All tests are asynchronous.
+SimpleTest.waitForExplicitFinish();
+
+function onNextAnimationFrame(fn) {
+ return () => requestAnimationFrame(() => requestAnimationFrame(fn));
+}
+
+function setState(component, newState) {
+ return new Promise(resolve => {
+ component.setState(newState, onNextAnimationFrame(resolve));
+ });
+}
+
+function dumpn(msg) {
+ dump(`SHARED-COMPONENTS-TEST: ${msg}\n`);
+}
+
+/**
+ * Tree View
+ */
+
+const TEST_TREE_VIEW = {
+ A: { label: "A", value: "A" },
+ B: { label: "B", value: "B" },
+ C: { label: "C", value: "C" },
+ D: { label: "D", value: "D" },
+ E: { label: "E", value: "E" },
+ F: { label: "F", value: "F" },
+ G: { label: "G", value: "G" },
+ H: { label: "H", value: "H" },
+ I: { label: "I", value: "I" },
+ J: { label: "J", value: "J" },
+ K: { label: "K", value: "K" },
+ L: { label: "L", value: "L" },
+};
+
+TEST_TREE_VIEW.children = {
+ A: [TEST_TREE_VIEW.B, TEST_TREE_VIEW.C, TEST_TREE_VIEW.D],
+ B: [TEST_TREE_VIEW.E, TEST_TREE_VIEW.F, TEST_TREE_VIEW.G],
+ C: [TEST_TREE_VIEW.H, TEST_TREE_VIEW.I],
+ D: [TEST_TREE_VIEW.J],
+ E: [TEST_TREE_VIEW.K, TEST_TREE_VIEW.L],
+ F: [],
+ G: [],
+ H: [],
+ I: [],
+ J: [],
+ K: [],
+ L: [],
+};
+
+const TEST_TREE_VIEW_INTERFACE = {
+ provider: {
+ getChildren: x => TEST_TREE_VIEW.children[x.label],
+ hasChildren: x => !!TEST_TREE_VIEW.children[x.label].length,
+ getLabel: x => x.label,
+ getValue: x => x.value,
+ getKey: x => x.label,
+ getType: () => "string",
+ },
+ object: TEST_TREE_VIEW.A,
+ columns: [{ id: "default" }, { id: "value" }],
+};
+
+/**
+ * Tree
+ */
+
+var TEST_TREE_INTERFACE = {
+ getParent: x => TEST_TREE.parent[x],
+ getChildren: x => TEST_TREE.children[x],
+ renderItem: (x, depth, focused) =>
+ "-".repeat(depth) + x + ":" + focused + "\n",
+ getRoots: () => ["A", "M"],
+ getKey: x => "key-" + x,
+ itemHeight: 1,
+ onExpand: x => TEST_TREE.expanded.add(x),
+ onCollapse: x => TEST_TREE.expanded.delete(x),
+ isExpanded: x => TEST_TREE.expanded.has(x),
+};
+
+function isRenderedTree(actual, expectedDescription, msg) {
+ const expected = expectedDescription.map(x => x + "\n").join("");
+ dumpn(`Expected tree:\n${expected}`);
+ dumpn(`Actual tree:\n${actual}`);
+ is(actual, expected, msg);
+}
+
+function isAccessibleTree(tree, options = {}) {
+ const treeNode = tree.refs.tree;
+ is(treeNode.getAttribute("tabindex"), "0", "Tab index is set");
+ is(treeNode.getAttribute("role"), "tree", "Tree semantics is present");
+ if (options.hasActiveDescendant) {
+ ok(
+ treeNode.hasAttribute("aria-activedescendant"),
+ "Tree has an active descendant set"
+ );
+ }
+
+ const treeNodes = [...treeNode.querySelectorAll(".tree-node")];
+ for (const node of treeNodes) {
+ ok(node.id, "TreeNode has an id");
+ is(node.getAttribute("role"), "treeitem", "Tree item semantics is present");
+ is(
+ parseInt(node.getAttribute("aria-level"), 10),
+ parseInt(node.getAttribute("data-depth"), 10) + 1,
+ "Aria level attribute is set correctly"
+ );
+ }
+}
+
+// Encoding of the following tree/forest:
+//
+// A
+// |-- B
+// | |-- E
+// | | |-- K
+// | | `-- L
+// | |-- F
+// | `-- G
+// |-- C
+// | |-- H
+// | `-- I
+// `-- D
+// `-- J
+// M
+// `-- N
+// `-- O
+var TEST_TREE = {
+ children: {
+ A: ["B", "C", "D"],
+ B: ["E", "F", "G"],
+ C: ["H", "I"],
+ D: ["J"],
+ E: ["K", "L"],
+ F: [],
+ G: [],
+ H: [],
+ I: [],
+ J: [],
+ K: [],
+ L: [],
+ M: ["N"],
+ N: ["O"],
+ O: [],
+ },
+ parent: {
+ A: null,
+ B: "A",
+ C: "A",
+ D: "A",
+ E: "B",
+ F: "B",
+ G: "B",
+ H: "C",
+ I: "C",
+ J: "D",
+ K: "E",
+ L: "E",
+ M: null,
+ N: "M",
+ O: "N",
+ },
+ expanded: new Set(),
+};
+
+/**
+ * Frame
+ */
+function checkFrameString({
+ el,
+ file,
+ line,
+ column,
+ source,
+ functionName,
+ shouldLink,
+ tooltip,
+ locationPrefix,
+}) {
+ const $ = selector => el.querySelector(selector);
+
+ const $func = $(".frame-link-function-display-name");
+ const $source = $(".frame-link-source");
+ const $locationPrefix = $(".frame-link-prefix");
+ const $filename = $(".frame-link-filename");
+ const $line = $(".frame-link-line");
+
+ is($filename.textContent, file, "Correct filename");
+ is(
+ el.getAttribute("data-line"),
+ line ? `${line}` : null,
+ "Expected `data-line` found"
+ );
+ is(
+ el.getAttribute("data-column"),
+ column ? `${column}` : null,
+ "Expected `data-column` found"
+ );
+ is($source.getAttribute("title"), tooltip, "Correct tooltip");
+ is($source.tagName, shouldLink ? "A" : "SPAN", "Correct linkable status");
+ if (shouldLink) {
+ is($source.getAttribute("href"), source, "Correct source");
+ }
+
+ if (line != null) {
+ let lineText = `:${line}`;
+ if (column != null) {
+ lineText += `:${column}`;
+ }
+
+ is($line.textContent, lineText, "Correct line number");
+ } else {
+ ok(!$line, "Should not have an element for `line`");
+ }
+
+ if (functionName != null) {
+ is($func.textContent, functionName, "Correct function name");
+ } else {
+ ok(!$func, "Should not have an element for `functionName`");
+ }
+
+ if (locationPrefix != null) {
+ is($locationPrefix.textContent, locationPrefix, "Correct prefix");
+ } else {
+ ok(!$locationPrefix, "Should not have an element for `locationPrefix`");
+ }
+}
+
+function checkSmartFrameString({ el, location, functionName, tooltip }) {
+ const $ = selector => el.querySelector(selector);
+
+ const $func = $(".title");
+ const $location = $(".location");
+
+ is($location.textContent, location, "Correct filename");
+ is(el.getAttribute("title"), tooltip, "Correct tooltip");
+ if (functionName != null) {
+ is($func.textContent, functionName, "Correct function name");
+ } else {
+ ok(!$func, "Should not have an element for `functionName`");
+ }
+}
+
+function renderComponent(component, props) {
+ const el = React.createElement(component, props, {});
+ // By default, renderIntoDocument() won't work for stateless components, but
+ // it will work if the stateless component is wrapped in a stateful one.
+ // See https://github.com/facebook/react/issues/4839
+ const wrappedEl = dom.span({}, [el]);
+ const renderedComponent = TestUtils.renderIntoDocument(wrappedEl);
+ return ReactDOM.findDOMNode(renderedComponent).children[0];
+}
+
+function shallowRenderComponent(component, props) {
+ const el = React.createElement(component, props);
+ const renderer = new ShallowRenderer();
+ renderer.render(el, {});
+ return renderer.getRenderOutput();
+}
+
+/**
+ * Creates a React Component for testing
+ *
+ * @param {string} factory - factory object of the component to be created
+ * @param {object} props - React props for the component
+ * @returns {object} - container Node, Object with React component
+ * and querySelector function with $ as name.
+ */
+async function createComponentTest(factory, props) {
+ const container = document.createElement("div");
+ document.body.appendChild(container);
+
+ const component = ReactDOM.render(factory(props), container);
+ await forceRender(component);
+
+ return {
+ container,
+ component,
+ $: s => container.querySelector(s),
+ };
+}
+
+async function waitFor(condition = () => true, delay = 50) {
+ do {
+ const res = condition();
+ if (res) {
+ return res;
+ }
+ await new Promise(resolve => setTimeout(resolve, delay));
+ } while (true);
+}
+
+/**
+ * Matches a component tree rendererd using TestRenderer to a given expected JSON
+ * snapshot.
+ * @param {String} name
+ * Name of the function derived from a test [step] name.
+ * @param {Object} el
+ * React element to be rendered using TestRenderer.
+ */
+function matchSnapshot(name, el) {
+ if (!_snapshots) {
+ is(false, "No snapshots were loaded into test.");
+ }
+
+ const snapshot = _snapshots[name];
+ if (snapshot === undefined) {
+ is(false, `Snapshot for "${name}" not found.`);
+ }
+
+ const renderer = TestRenderer.create(el, {});
+ const tree = renderer.toJSON();
+
+ is(
+ JSON.stringify(tree, (key, value) =>
+ typeof value === "function" ? value.toString() : value
+ ),
+ JSON.stringify(snapshot),
+ name
+ );
+}
diff --git a/devtools/client/shared/components/test/chrome/test_GridElementWidthResizer.html b/devtools/client/shared/components/test/chrome/test_GridElementWidthResizer.html
new file mode 100644
index 0000000000..cf30255141
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_GridElementWidthResizer.html
@@ -0,0 +1,209 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+ <!-- Basic tests for the GridElementWidthResizer component. -->
+ <head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link href="chrome://mochikit/content/tests/SimpleTest/test.css" rel="stylesheet" type="text/css"/>
+ <link href="chrome://devtools/skin/splitters.css" rel="stylesheet" type="text/css"/>
+ <link href="chrome://devtools/content/shared/components/splitter/GridElementResizer.css" rel="stylesheet" type="text/css"/>
+ <style>
+ * {
+ box-sizing: border-box;
+ }
+
+ html {
+ --theme-splitter-color: red;
+ }
+
+ main {
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+ grid-template-rows: 20px 20px 20px;
+ grid-gap: 10px;
+ }
+
+ .a,
+ .b,
+ .c,
+ .d {
+ border: 1px solid green;
+ }
+
+ header {
+ grid-column: 1 / -1;
+ }
+ .a {
+ grid-column: 1 / 2;
+ grid-row: 2 / -1;
+ min-width: 100px;
+ }
+ .b {
+ grid-column: 2 / 3;
+ grid-row: 2 / -1;
+ }
+
+ .c {
+ grid-column: 3 / 4;
+ grid-row: 2 / 3;
+ }
+
+ .d {
+ grid-column: 3 / 4;
+ grid-row: 3 / 4;
+ min-width: 150px;
+ }
+
+ .resizer-a {
+ grid-column: 1 / 2;
+ grid-row: 2 / -1;
+ }
+
+ .resizer-d {
+ grid-column: 3 / 4;
+ grid-row: 2 / -1;
+ }
+ </style>
+ </head>
+ <body>
+ <main></main>
+ <pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+const FUDGE_FACTOR = .5;
+function aboutEq(a, b) {
+ dumpn(`Checking ${a} ~= ${b}`);
+ return Math.abs(a - b) < FUDGE_FACTOR;
+}
+
+window.onload = async function () {
+ try {
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const GridElementWidthResizer = React.createFactory(browserRequire("devtools/client/shared/components/splitter/GridElementWidthResizer"));
+ ok(GridElementWidthResizer, "Should get GridElementWidthResizer");
+
+ const resizedA = [];
+ const resizedD = [];
+
+ ReactDOM.render([
+ dom.header({}, "header"),
+ dom.aside({className: "a"}, "A"),
+ GridElementWidthResizer({
+ getControlledElementNode: () => a,
+ enabled: true,
+ position: "end",
+ className: "resizer-a",
+ onResizeEnd: width => resizedA.push(width),
+ }),
+ dom.section({className: "b"}, "B"),
+ GridElementWidthResizer({
+ getControlledElementNode: () => window.document.querySelector(".b"),
+ enabled: false,
+ position: "start",
+ className: "resizer-disabled",
+ }),
+ dom.aside({className: "c"}, "C"),
+ dom.aside({className: "d"}, "D"),
+ GridElementWidthResizer({
+ getControlledElementNode: () => d,
+ enabled: true,
+ position: "start",
+ className: "resizer-d",
+ onResizeEnd: width => resizedD.push(width),
+ }),
+ ], window.document.querySelector("main"));
+
+ // wait for a bit as we may not have everything setup yet.
+ await new Promise(res => setTimeout(res, 10));
+
+ const a = window.document.querySelector(".a");
+ const d = window.document.querySelector(".d");
+
+ // Test that we properly rendered our two resizers, and not the disabled one.
+ const resizers = window.document.querySelectorAll(".grid-element-width-resizer");
+ is(resizers.length, 2, "The 2 enabled resizers are rendered");
+
+ const [resizerA, resizerD] = resizers;
+
+ ok(resizerA.classList.contains("resizer-a")
+ && resizerA.classList.contains("end"), "resizerA has expected classes");
+ ok(resizerD.classList.contains("resizer-d")
+ && resizerD.classList.contains("start"), "resizerD has expected classes");
+
+ const aBoundingRect = a.getBoundingClientRect();
+ const aOriginalWidth = aBoundingRect.width;
+
+ info("Resize element A");
+ await resize(resizerA, aBoundingRect.right + 20);
+
+ is(resizedA.length, 1, "onResizeEnd was called once");
+ is(resizedD.length, 0, "resizerD was not impacted");
+ let aWidth = a.getBoundingClientRect().width;
+ is(resizedA[0], aWidth, "onResizeEnd gives the width of the controlled element");
+ ok(aboutEq(aWidth, aOriginalWidth + 20),
+ "controlled element was resized to the expected size");
+
+ info("Resize element A below its min width");
+ await resize(resizerA, [aBoundingRect.left + 10]);
+ aWidth = a.getBoundingClientRect().width;
+ ok(aboutEq(aWidth, 100), "controlled element was resized to its min width");
+
+ info("Resize element D below its min width");
+ const dBoundingRect = d.getBoundingClientRect();
+ const dOriginalWidth = dBoundingRect.width;
+
+ await resize(resizerD, dBoundingRect.left + 100);
+ is(resizedD.length, 1, "onResizeEnd was called once for d");
+ is(resizedA.length, 2, "onResizeEnd wasn't called for a");
+ let dWidth = d.getBoundingClientRect().width;
+ is(resizedD[0], dWidth, "onResizeEnd gives the width of the controlled element");
+ ok(aboutEq(dWidth, 150), "controlled element wasn't resized below its min-width");
+
+ info("Resize element D");
+ await resize(resizerD, dBoundingRect.left - 15);
+ dWidth = d.getBoundingClientRect().width;
+ is(resizedD[1], dWidth, "onResizeEnd gives the width of the controlled element");
+ ok(aboutEq(dWidth, dOriginalWidth + 15), "element was resized");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+
+ async function resize(resizer, clientX) {
+ info("Mouse down to start dragging");
+ synthesizeMouseAtCenter(resizer, { button: 0, type: "mousedown" }, window);
+ await SimpleTest.promiseWaitForCondition(
+ () => document.firstElementChild.classList.contains("dragging"),
+ "dragging class is never set on the root element"
+ );
+ ok(true, "The dragging class is set on the root element");
+
+ const event = new MouseEvent("mousemove", { clientX });
+ resizer.dispatchEvent(event);
+
+ info("Mouse up to stop resizing");
+ synthesizeMouseAtCenter(document.body, { button: 0, type: "mouseup" }, window);
+
+ await SimpleTest.promiseWaitForCondition(
+ () => !document.firstElementChild.classList.contains("dragging"),
+ "dragging class is never removed from the root element"
+ );
+ ok(true, "The dragging class is removed from the root element");
+ }
+};
+</script>
+</pre>
+ </body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_GridElementWidthResizer_RTL.html b/devtools/client/shared/components/test/chrome/test_GridElementWidthResizer_RTL.html
new file mode 100644
index 0000000000..3768ecf0a0
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_GridElementWidthResizer_RTL.html
@@ -0,0 +1,210 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+ <!-- Basic tests for the GridElementWidthResizer component. -->
+ <head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link href="chrome://mochikit/content/tests/SimpleTest/test.css" rel="stylesheet" type="text/css"/>
+ <link href="chrome://devtools/skin/splitters.css" rel="stylesheet" type="text/css"/>
+ <link href="chrome://devtools/content/shared/components/splitter/GridElementResizer.css" rel="stylesheet" type="text/css"/>
+ <style>
+ * {
+ box-sizing: border-box;
+ }
+
+ html {
+ --theme-splitter-color: red;
+ }
+
+ main {
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+ grid-template-rows: 20px 20px 20px;
+ grid-gap: 10px;
+ direction: rtl;
+ }
+
+ .a,
+ .b,
+ .c,
+ .d {
+ border: 1px solid green;
+ }
+
+ header {
+ grid-column: 1 / -1;
+ }
+ .a {
+ grid-column: 1 / 2;
+ grid-row: 2 / -1;
+ min-width: 100px;
+ }
+ .b {
+ grid-column: 2 / 3;
+ grid-row: 2 / -1;
+ }
+
+ .c {
+ grid-column: 3 / 4;
+ grid-row: 2 / 3;
+ }
+
+ .d {
+ grid-column: 3 / 4;
+ grid-row: 3 / 4;
+ min-width: 150px;
+ }
+
+ .resizer-a {
+ grid-column: 1 / 2;
+ grid-row: 2 / -1;
+ }
+
+ .resizer-d {
+ grid-column: 3 / 4;
+ grid-row: 2 / -1;
+ }
+ </style>
+ </head>
+ <body>
+ <main></main>
+ <pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+const FUDGE_FACTOR = .5;
+function aboutEq(a, b) {
+ dumpn(`Checking ${a} ~= ${b}`);
+ return Math.abs(a - b) < FUDGE_FACTOR;
+}
+
+window.onload = async function () {
+ try {
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+ const GridElementWidthResizer = React.createFactory(browserRequire("devtools/client/shared/components/splitter/GridElementWidthResizer"));
+ ok(GridElementWidthResizer, "Should get GridElementWidthResizer");
+
+ const resizedA = [];
+ const resizedD = [];
+
+ ReactDOM.render([
+ dom.header({}, "header"),
+ dom.aside({className: "a"}, "A"),
+ GridElementWidthResizer({
+ getControlledElementNode: () => a,
+ enabled: true,
+ position: "end",
+ className: "resizer-a",
+ onResizeEnd: width => resizedA.push(width),
+ }),
+ dom.section({className: "b"}, "B"),
+ GridElementWidthResizer({
+ getControlledElementNode: () => window.document.querySelector(".b"),
+ enabled: false,
+ position: "start",
+ className: "resizer-disabled",
+ }),
+ dom.aside({className: "c"}, "C"),
+ dom.aside({className: "d"}, "D"),
+ GridElementWidthResizer({
+ getControlledElementNode: () => d,
+ enabled: true,
+ position: "start",
+ className: "resizer-d",
+ onResizeEnd: width => resizedD.push(width),
+ }),
+ ], window.document.querySelector("main"));
+
+ // wait for a bit as we may not have everything setup yet.
+ await new Promise(res => setTimeout(res, 10));
+
+ const a = window.document.querySelector(".a");
+ const d = window.document.querySelector(".d");
+
+ // Test that we properly rendered our two resizers, and not the disabled one.
+ const resizers = window.document.querySelectorAll(".grid-element-width-resizer");
+ is(resizers.length, 2, "The 2 enabled resizers are rendered");
+
+ const [resizerA, resizerD] = resizers;
+
+ ok(resizerA.classList.contains("resizer-a")
+ && resizerA.classList.contains("end"), "resizerA has expected classes");
+ ok(resizerD.classList.contains("resizer-d")
+ && resizerD.classList.contains("start"), "resizerD has expected classes");
+
+ const aBoundingRect = a.getBoundingClientRect();
+ const aOriginalWidth = aBoundingRect.width;
+
+ info("Resize element A");
+ await resize(resizerA, aBoundingRect.left - 20);
+
+ is(resizedA.length, 1, "onResizeEnd was called once");
+ is(resizedD.length, 0, "resizerD was not impacted");
+ let aWidth = a.getBoundingClientRect().width;
+ is(resizedA[0], aWidth, "onResizeEnd gives the width of the controlled element");
+ ok(aboutEq(aWidth, aOriginalWidth + 20),
+ "controlled element was resized to the expected size");
+
+ info("Resize element A below its min width");
+ await resize(resizerA, [aBoundingRect.right - 10]);
+ aWidth = a.getBoundingClientRect().width;
+ ok(aboutEq(aWidth, 100), "controlled element was resized to its min width");
+
+ info("Resize element D below its min width");
+ const dBoundingRect = d.getBoundingClientRect();
+ const dOriginalWidth = dBoundingRect.width;
+
+ await resize(resizerD, dBoundingRect.right - 100);
+ is(resizedD.length, 1, "onResizeEnd was called once for d");
+ is(resizedA.length, 2, "onResizeEnd wasn't called for a");
+ let dWidth = d.getBoundingClientRect().width;
+ is(resizedD[0], dWidth, "onResizeEnd gives the width of the controlled element");
+ ok(aboutEq(dWidth, 150), "controlled element wasn't resized below its min-width");
+
+ info("Resize element D");
+ await resize(resizerD, dBoundingRect.right + 15);
+ dWidth = d.getBoundingClientRect().width;
+ is(resizedD[1], dWidth, "onResizeEnd gives the width of the controlled element");
+ ok(aboutEq(dWidth, dOriginalWidth + 15), "element was resized");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+
+ async function resize(resizer, clientX) {
+ info("Mouse down to start dragging");
+ synthesizeMouseAtCenter(resizer, { button: 0, type: "mousedown" }, window);
+ await SimpleTest.promiseWaitForCondition(
+ () => document.firstElementChild.classList.contains("dragging"),
+ "dragging class is never set on the root element"
+ );
+ ok(true, "The dragging class is set on the root element");
+
+ const event = new MouseEvent("mousemove", { clientX });
+ resizer.dispatchEvent(event);
+
+ info("Mouse up to stop resizing");
+ synthesizeMouseAtCenter(document.body, { button: 0, type: "mouseup" }, window);
+
+ await SimpleTest.promiseWaitForCondition(
+ () => !document.firstElementChild.classList.contains("dragging"),
+ "dragging class is never removed from the root element"
+ );
+ ok(true, "The dragging class is removed from the root element");
+ }
+};
+</script>
+</pre>
+ </body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_HSplitBox_01.html b/devtools/client/shared/components/test/chrome/test_HSplitBox_01.html
new file mode 100644
index 0000000000..09da5dac6b
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_HSplitBox_01.html
@@ -0,0 +1,140 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Basic tests for the HSplitBox component.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" href="chrome://devtools/skin/splitters.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/skin/components-h-split-box.css" type="text/css"/>
+ <style>
+ html {
+ --theme-splitter-color: black;
+ }
+ </style>
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+const FUDGE_FACTOR = .1;
+function aboutEq(a, b) {
+ dumpn(`Checking ${a} ~= ${b}`);
+ return Math.abs(a - b) < FUDGE_FACTOR;
+}
+
+window.onload = async function () {
+ try {
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+
+ const HSplitBox = React.createFactory(browserRequire("devtools/client/shared/components/HSplitBox"));
+ ok(HSplitBox, "Should get HSplitBox");
+
+ const newSizes = [];
+
+ async function renderBox(props) {
+ const boxProps = Object.assign({
+ start: "hello!",
+ end: "world!",
+ startWidth: .5,
+ onResize(newSize) {
+ newSizes.push(newSize);
+ }
+ }, props);
+ const el = ReactDOM.render(HSplitBox(boxProps), window.document.body);
+ // wait until the element is rendered.
+ await SimpleTest.promiseWaitForCondition(
+ () => document.querySelector(".devtools-side-splitter")
+ );
+ return el;
+ }
+
+ await renderBox();
+
+ // Test that we properly rendered our two panes.
+
+ let panes = document.querySelectorAll(".h-split-box-pane");
+ is(panes.length, 2, "Should get two panes");
+ is(panes[0].style.flexGrow, "0.5", "Each pane should have .5 width");
+ is(panes[1].style.flexGrow, "0.5", "Each pane should have .5 width");
+ is(panes[0].textContent.trim(), "hello!", "First pane should be hello");
+ is(panes[1].textContent.trim(), "world!", "Second pane should be world");
+
+ // Now change the left width and assert that the changes are reflected.
+
+ await renderBox({ startWidth: .25 });
+ panes = document.querySelectorAll(".h-split-box-pane");
+ is(panes.length, 2, "Should still have two panes");
+ is(panes[0].style.flexGrow, "0.25", "First pane's width should be .25");
+ is(panes[1].style.flexGrow, "0.75", "Second pane's width should be .75");
+
+ // Mouse moves without having grabbed the splitter should have no effect.
+
+ const container = document.querySelector(".h-split-box");
+ ok(container, "Should get our container .h-split-box");
+
+ const { left, top, width } = container.getBoundingClientRect();
+ const middle = left + width / 2;
+ const oneQuarter = left + width / 4;
+ const threeQuarters = left + 3 * width / 4;
+
+ synthesizeMouse(container, middle, top, { type: "mousemove" }, window);
+ is(newSizes.length, 0, "Mouse moves without dragging the splitter should have no effect");
+
+ // Send a mouse down on the splitter, and then move the mouse a couple
+ // times. Now we should get resizes.
+
+ const splitter = document.querySelector(".devtools-side-splitter");
+ ok(splitter, "Should get our splitter");
+
+ synthesizeMouseAtCenter(splitter, { button: 0, type: "mousedown" }, window);
+
+ function mouseMove(clientX) {
+ const event = new MouseEvent("mousemove", { clientX });
+ document.defaultView.top.dispatchEvent(event);
+ }
+
+ mouseMove(middle);
+ is(newSizes.length, 1, "Should get 1 resize");
+ ok(aboutEq(newSizes[0], .5), "New size should be ~.5");
+
+ mouseMove(left);
+ is(newSizes.length, 2, "Should get 2 resizes");
+ ok(aboutEq(newSizes[1], 0), "New size should be ~0");
+
+ mouseMove(oneQuarter);
+ is(newSizes.length, 3, "Sould get 3 resizes");
+ ok(aboutEq(newSizes[2], .25), "New size should be ~.25");
+
+ mouseMove(threeQuarters);
+ is(newSizes.length, 4, "Should get 4 resizes");
+ ok(aboutEq(newSizes[3], .75), "New size should be ~.75");
+
+ synthesizeMouseAtCenter(splitter, { button: 0, type: "mouseup" }, window);
+
+ // Now that we have let go of the splitter, mouse moves should not result in resizes.
+
+ synthesizeMouse(container, middle, top, { type: "mousemove" }, window);
+ is(newSizes.length, 4, "Should still have 4 resizes");
+
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_accordion.html b/devtools/client/shared/components/test/chrome/test_accordion.html
new file mode 100644
index 0000000000..60d179be6f
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_accordion.html
@@ -0,0 +1,141 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that Accordion renders correctly.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Accordion component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="resource://testing-common/sinon-7.2.7.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script src="accordion.snapshots.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+/* global sinon */
+
+window.onload = async function() {
+ try {
+ const { button, div } = require("devtools/client/shared/vendor/react-dom-factories");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const {
+ Simulate,
+ renderIntoDocument,
+ findAllInRenderedTree,
+ } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Accordion =
+ browserRequire("devtools/client/shared/components/Accordion");
+
+ const testItems = [
+ {
+ header: "Test Accordion Item 1",
+ id: "accordion-item-1",
+ component: div({}),
+ opened: false,
+ onToggle: sinon.spy(),
+ },
+ {
+ header: "Test Accordion Item 2",
+ id: "accordion-item-2",
+ component: div({}),
+ buttons: button({}),
+ opened: false,
+ onToggle: sinon.spy(),
+ },
+ {
+ header: "Test Accordion Item 3",
+ id: "accordion-item-3",
+ component: div({}),
+ opened: true,
+ onToggle: sinon.spy(),
+ },
+ ];
+
+ // Accordion basic render
+ const accordion = React.createElement(Accordion, { items: testItems });
+
+ matchSnapshot("Accordion basic render.", accordion);
+
+ const tree = renderIntoDocument(accordion);
+ const headers = findAllInRenderedTree(tree, c => c.className === "accordion-header");
+
+ Simulate.click(headers[0]);
+ ok(testItems[0].onToggle.calledWith(true), "Handle toggling with click.");
+ ok(testItems[1].onToggle.notCalled,
+ "onToggle wasn't called on element we didn't click on.");
+
+ isDeeply(
+ tree.state,
+ {
+ everOpened: {
+ "accordion-item-1": true,
+ "accordion-item-2": false,
+ "accordion-item-3": true,
+ },
+ opened: {
+ "accordion-item-1": true,
+ "accordion-item-2": false,
+ "accordion-item-3": true,
+ },
+ },
+ "State updated correctly"
+ );
+
+ Simulate.keyDown(headers[0], { key: "Enter" });
+ ok(testItems[0].onToggle.calledWith(false), "Handle toggling with Enter key.");
+ isDeeply(
+ tree.state,
+ {
+ everOpened: {
+ "accordion-item-1": true,
+ "accordion-item-2": false,
+ "accordion-item-3": true,
+ },
+ opened: {
+ "accordion-item-1": false,
+ "accordion-item-2": false,
+ "accordion-item-3": true,
+ },
+ },
+ "State updated correctly"
+ );
+
+ Simulate.keyDown(headers[1], { key: " " });
+ ok(testItems[1].onToggle.calledWith(true), "Handle toggling with Space key.");
+ isDeeply(
+ tree.state,
+ {
+ everOpened: {
+ "accordion-item-1": true,
+ "accordion-item-2": true,
+ "accordion-item-3": true,
+ },
+ opened: {
+ "accordion-item-1": false,
+ "accordion-item-2": true,
+ "accordion-item-3": true,
+ },
+ },
+ "State updated correctly"
+ );
+
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_frame_01.html b/devtools/client/shared/components/test/chrome/test_frame_01.html
new file mode 100644
index 0000000000..f763b21395
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_frame_01.html
@@ -0,0 +1,361 @@
+<!-- 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/. -->
+<!DOCTYPE html>
+<html>
+ <!--
+Test the formatting of the file name, line and columns are correct in frame components,
+with optional columns, unknown and non-URL sources.
+-->
+ <head>
+ <meta charset="utf-8" />
+ <title>Frame component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link
+ rel="stylesheet"
+ type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ />
+ </head>
+ <body>
+ <pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const Frame = React.createFactory(browserRequire("devtools/client/shared/components/Frame"));
+ ok(Frame, "Should get Frame");
+
+ // Check when there's a column
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 55,
+ column: 10,
+ }
+ }, {
+ file: "mahscripts.js",
+ line: 55,
+ column: 10,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js:55:10",
+ });
+
+ // Check when there's no column
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 55,
+ }
+ }, {
+ file: "mahscripts.js",
+ line: 55,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js:55",
+ });
+
+ // Check when column === 0
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 55,
+ column: 0,
+ }
+ }, {
+ file: "mahscripts.js",
+ line: 55,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js:55",
+ });
+
+ // Check when there's an error in CSS (View source in Style Editor)
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/cafebabe.css",
+ line: 13,
+ },
+ messageSource: "css",
+ }, {
+ file: "cafebabe.css",
+ line: 13,
+ shouldLink: true,
+ tooltip: "View source in Style Editor → https://myfile.com/cafebabe.css:13",
+ });
+
+
+ // Check when there's no parseable URL source;
+ // should not link but should render line/columns
+ await checkFrameComponent({
+ frame: {
+ source: "self-hosted",
+ line: 1,
+ }
+ }, {
+ file: "self-hosted",
+ line: "1",
+ shouldLink: false,
+ tooltip: "self-hosted:1",
+ });
+ await checkFrameComponent({
+ frame: {
+ source: "self-hosted",
+ line: 1,
+ column: 10,
+ }
+ }, {
+ file: "self-hosted",
+ line: "1",
+ column: "10",
+ shouldLink: false,
+ tooltip: "self-hosted:1:10",
+ });
+
+ // Check when there's no source;
+ // should not link but should render line/columns
+ await checkFrameComponent({
+ frame: {
+ line: 1,
+ }
+ }, {
+ file: "(unknown)",
+ line: "1",
+ shouldLink: false,
+ tooltip: "(unknown):1",
+ });
+ await checkFrameComponent({
+ frame: {
+ line: 1,
+ column: 10,
+ }
+ }, {
+ file: "(unknown)",
+ line: "1",
+ column: "10",
+ shouldLink: false,
+ tooltip: "(unknown):1:10",
+ });
+
+ // Check when there's a column, but no line;
+ // no line/column info should render
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ column: 55,
+ }
+ }, {
+ file: "mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ // Check when line is 0; this should be an invalid
+ // line option, so don't render line/column
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 0,
+ column: 55,
+ }
+ }, {
+ file: "mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ // Check that line and column can be strings
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: "10",
+ column: "55",
+ }
+ }, {
+ file: "mahscripts.js",
+ line: 10,
+ column: 55,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js:10:55",
+ });
+
+ // Check that line and column can be strings,
+ // and that the `0` rendering rules apply when they are strings as well
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: "0",
+ column: "55",
+ }
+ }, {
+ file: "mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ // Check that the showFullSourceUrl option works correctly
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 0,
+ },
+ showFullSourceUrl: true
+ }, {
+ file: "https://myfile.com/mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ // Check that the showFunctionName option works correctly
+ await checkFrameComponent({
+ frame: {
+ functionDisplayName: "myfun",
+ source: "https://myfile.com/mahscripts.js",
+ line: 0,
+ }
+ }, {
+ functionName: null,
+ file: "mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ await checkFrameComponent({
+ frame: {
+ functionDisplayName: "myfun",
+ source: "https://myfile.com/mahscripts.js",
+ line: 0,
+ },
+ showFunctionName: true
+ }, {
+ functionName: "myfun",
+ file: "mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ // Check that anonymous function name is not displayed unless explicitly enabled
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 0,
+ },
+ showFunctionName: true
+ }, {
+ functionName: null,
+ file: "mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 0,
+ },
+ showFunctionName: true,
+ showAnonymousFunctionName: true
+ }, {
+ functionName: "<anonymous>",
+ file: "mahscripts.js",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js",
+ });
+
+ // Check if file is rendered with "/" for root documents when showEmptyPathAsHost is false
+ await checkFrameComponent({
+ frame: {
+ source: "https://www.cnn.com/",
+ line: "1",
+ },
+ showEmptyPathAsHost: false,
+ }, {
+ file: "/",
+ line: "1",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://www.cnn.com/:1",
+ });
+
+ // Check if file is rendered with hostname for root documents when showEmptyPathAsHost is true
+ await checkFrameComponent({
+ frame: {
+ source: "https://www.cnn.com/",
+ line: "1",
+ },
+ showEmptyPathAsHost: true,
+ }, {
+ file: "www.cnn.com",
+ line: "1",
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://www.cnn.com/:1",
+ });
+
+ const resolvedLocation = {
+ sourceId: "whatever",
+ line: 23,
+ sourceUrl: "https://bugzilla.mozilla.org/original.js",
+ };
+ const mockSourceMapURLService = {
+ subscribeByLocation (loc, callback) {
+ // Resolve immediately.
+ callback({
+ url: resolvedLocation.sourceUrl,
+ line: resolvedLocation.line,
+ column: undefined,
+ });
+ return () => {};
+ },
+ };
+ await checkFrameComponent({
+ frame: {
+ line: 97,
+ source: "https://bugzilla.mozilla.org/bundle.js",
+ },
+ sourceMapURLService: mockSourceMapURLService,
+ }, {
+ file: "original.js",
+ line: resolvedLocation.line,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:23",
+ source: "https://bugzilla.mozilla.org/original.js",
+ });
+
+ // Check when a message comes from a logPoint,
+ // a prefix should render before source
+ await checkFrameComponent({
+ frame: {
+ source: "https://myfile.com/mahscripts.js",
+ line: 55,
+ column: 10,
+ options: { logPoint: true },
+ }
+ }, {
+ file: "mahscripts.js",
+ line: 55,
+ column: 10,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js:55:10",
+ });
+
+ function checkFrameComponent(input, expected) {
+ const props = Object.assign({ onClick: () => {} }, input);
+ const frame = ReactDOM.render(Frame(props), window.document.body);
+ const el = ReactDOM.findDOMNode(frame);
+ const { source } = input.frame;
+ checkFrameString(Object.assign({ el, source }, expected));
+ ReactDOM.unmountComponentAtNode(window.document.body);
+ }
+
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+ </body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_frame_02.html b/devtools/client/shared/components/test/chrome/test_frame_02.html
new file mode 100644
index 0000000000..dd4bf9c2b7
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_frame_02.html
@@ -0,0 +1,103 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that the frame component reacts to source-map pref changse.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Frame component source-map test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const Frame = React.createFactory(browserRequire("devtools/client/shared/components/Frame"));
+
+ const resolvedLocation = {
+ sourceId: "whatever",
+ line: 23,
+ sourceUrl: "https://bugzilla.mozilla.org/original.js",
+ };
+ const mockSourceMapURLService = {
+ _update () {
+ this._callback(Services.prefs.getBoolPref(PREF)
+ ? {
+ url: resolvedLocation.sourceUrl,
+ line: resolvedLocation.line,
+ column: undefined,
+ }
+ : null);
+ },
+ subscribeByLocation (loc, callback) {
+ this._callback = callback;
+ // Resolve immediately.
+ this._update();
+
+ return () => {};
+ },
+ };
+
+ const props = {
+ onClick: () => {},
+ frame: {
+ line: 97,
+ source: "https://bugzilla.mozilla.org/bundle.js",
+ },
+ sourceMapURLService: mockSourceMapURLService,
+ };
+
+ const PREF = "devtools.source-map.client-service.enabled";
+ Services.prefs.setBoolPref(PREF, false);
+
+ const frame = ReactDOM.render(Frame(props), window.document.body);
+ const el = ReactDOM.findDOMNode(frame);
+ const { source } = props.frame;
+
+ const expectedOriginal = {
+ file: "original.js",
+ line: resolvedLocation.line,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:23",
+ source: "https://bugzilla.mozilla.org/original.js",
+ };
+ const expectedGenerated = {
+ file: "bundle.js",
+ line: 97,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://bugzilla.mozilla.org/bundle.js:97",
+ source: "https://bugzilla.mozilla.org/bundle.js",
+ };
+
+ checkFrameString(Object.assign({ el, source }, expectedGenerated));
+
+ Services.prefs.setBoolPref(PREF, true);
+ mockSourceMapURLService._update();
+ checkFrameString(Object.assign({ el, source }, expectedOriginal));
+
+ Services.prefs.setBoolPref(PREF, false);
+ mockSourceMapURLService._update();
+ checkFrameString(Object.assign({ el, source }, expectedGenerated));
+
+ Services.prefs.clearUserPref(PREF);
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_list.html b/devtools/client/shared/components/test/chrome/test_list.html
new file mode 100644
index 0000000000..8be8907b5d
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_list.html
@@ -0,0 +1,127 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that List renders correctly.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>List component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script src="list.snapshots.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function() {
+ try {
+ const { div } = require("devtools/client/shared/vendor/react-dom-factories");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const {
+ Simulate,
+ renderIntoDocument,
+ findRenderedDOMComponentWithClass,
+ scryRenderedDOMComponentsWithTag,
+ scryRenderedComponentsWithType,
+ } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const { List, ListItem } =
+ browserRequire("devtools/client/shared/components/List");
+
+ const testItems = [
+ {
+ component: div({ className: "item-1" }, "Test List Item 1"),
+ className: "list-item-1",
+ key: "list-item-1",
+ },
+ {
+ component: div({ className: "item-2" }, "Test List Item 2"),
+ className: "list-item-2",
+ key: "list-item-2",
+ },
+ {
+ component: div({ className: "item-3" }, "Test List Item 3"),
+ className: "list-item-3",
+ key: "list-item-3",
+ },
+ ];
+
+ const listReactEl = React.createElement(List, {
+ items: testItems,
+ labelledBy: "test-labelledby",
+ });
+
+ const list = renderIntoDocument(listReactEl);
+ const listEl = findRenderedDOMComponentWithClass(list, "list");
+ const items = scryRenderedComponentsWithType(list, ListItem);
+ const itemEls = scryRenderedDOMComponentsWithTag(list, "li");
+
+ function testCurrent(index) {
+ is(list.state.current, index, "Correct current item.");
+ is(listEl.getAttribute("aria-activedescendant"), testItems[index].key,
+ "Correct active descendant.");
+ }
+
+ is(items.length, 3, "Correct number of list item components in tree.");
+ is(itemEls.length, 3, "Correct number of list items is rendered.");
+ info("Testing initial tree properties.");
+ for (let index = 0; index < items.length; index++) {
+ const item = items[index];
+ const itemEl = itemEls[index];
+ const { active, current, item: itemProp } = item.props;
+ const content = itemEl.querySelector(".list-item-content");
+
+ is(active, false, "Correct active state.");
+ is(current, false, "Correct current state.");
+ is(itemProp, testItems[index], "Correct rendered item.");
+ is(item.contentRef.current, content, "Correct content ref.");
+
+ is(itemEl.className, testItems[index].className, "Correct list item class.");
+ is(itemEl.id, testItems[index].key, "Correct list item it.");
+ is(content.getAttribute("role"), "presentation", "Correct content role.");
+
+ is(content.innerHTML,
+ `<div class="item-${index + 1}">Test List Item ${index + 1}</div>`,
+ "Content rendered correctly.");
+ }
+
+ is(list.state.current, null, "Current item is not set by default.");
+ is(list.state.active, null, "Active item is not set by default.");
+ is(list.listRef.current, listEl, "Correct list ref.");
+
+ is(listEl.className, "list", "Correct list class.");
+ is(listEl.tabIndex, 0, "List is focusable.");
+ ok(!listEl.hasAttribute("aria-label"), "List has no label.");
+ is(listEl.getAttribute("aria-labelledby"), "test-labelledby",
+ "Correct list labelled by attribute.");
+ ok(!listEl.hasAttribute("aria-activedescendant"),
+ "No active descendant set by default.");
+
+ Simulate.focus(listEl);
+ testCurrent(0);
+
+ Simulate.click(itemEls[2]);
+ testCurrent(2);
+
+ Simulate.blur(listEl);
+ testCurrent(2);
+
+ Simulate.focus(listEl);
+ testCurrent(2);
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_list_keyboard.html b/devtools/client/shared/components/test/chrome/test_list_keyboard.html
new file mode 100644
index 0000000000..7558404ed2
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_list_keyboard.html
@@ -0,0 +1,283 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that List component has working keyboard interactions.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>List component keyboard test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = function() {
+ try {
+ const { a, button, div } =
+ require("devtools/client/shared/vendor/react-dom-factories");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const {
+ Simulate,
+ findRenderedDOMComponentWithClass,
+ findRenderedDOMComponentWithTag,
+ scryRenderedDOMComponentsWithTag,
+ } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const { List } =
+ browserRequire("devtools/client/shared/components/List");
+
+ const testItems = [
+ {
+ component: div({}, "Test List Item 1"),
+ className: "list-item-1",
+ key: "list-item-1",
+ },
+ {
+ component: div({},
+ "Test List Item 2",
+ a({ href: "#" }, "Focusable 1"),
+ button({ }, "Focusable 2")),
+ className: "list-item-2",
+ key: "list-item-2",
+ },
+ {
+ component: div({}, "Test List Item 3"),
+ className: "list-item-3",
+ key: "list-item-3",
+ },
+ ];
+
+ const list = React.createElement(List, {
+ items: testItems,
+ labelledby: "test-labelledby",
+ });
+
+ const tree = ReactDOM.render(list, document.body);
+ const listEl = findRenderedDOMComponentWithClass(tree, "list");
+ scryRenderedDOMComponentsWithTag(tree, "li");
+ const defaultFocus = listEl.ownerDocument.body;
+
+ function blurEl(el) {
+ // Simulate.blur does not seem to update the activeElement.
+ el.blur();
+ }
+
+ function focusEl(el) {
+ // Simulate.focus does not seem to update the activeElement.
+ el.focus();
+ }
+
+ const tests = [{
+ name: "Test default List state. Keyboard focus is set to document body by default.",
+ state: { current: null, active: null },
+ activeElement: defaultFocus,
+ }, {
+ name: "Current item must be set to the first list item on initial focus. " +
+ "Keyboard focus should be set on list's conatiner (<ul>).",
+ action: () => focusEl(listEl),
+ activeElement: listEl,
+ state: { current: 0 },
+ }, {
+ name: "Current item should remain set even when the list is blured. " +
+ "Keyboard focus should be set back to document body.",
+ action: () => blurEl(listEl),
+ state: { current: 0 },
+ activeElement: defaultFocus,
+ }, {
+ name: "Unset list's current state.",
+ action: () => tree.setState({ current: null }),
+ state: { current: null },
+ }, {
+ name: "Current item must be re-set again to the first list item on initial " +
+ "focus. Keyboard focus should be set on list's conatiner (<ul>).",
+ action: () => focusEl(listEl),
+ activeElement: listEl,
+ state: { current: 0 },
+ }, {
+ name: "Current item should be updated to next on ArrowDown.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
+ state: { current: 1 },
+ }, {
+ name: "Current item should be updated to last on ArrowDown.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
+ state: { current: 2 },
+ }, {
+ name: "Current item should remain on last on ArrowDown.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
+ state: { current: 2 },
+ }, {
+ name: "Current item should be updated to previous on ArrowUp.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
+ state: { current: 1 },
+ }, {
+ name: "Current item should be updated to first on ArrowUp.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
+ state: { current: 0 },
+ }, {
+ name: "Current item should remain on first on ArrowUp.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
+ state: { current: 0 },
+ }, {
+ name: "Current item should be updated to last on End.",
+ event: { type: "keyDown", el: listEl, options: { key: "End" }},
+ state: { current: 2 },
+ }, {
+ name: "Current item should be updated to first on Home.",
+ event: { type: "keyDown", el: listEl, options: { key: "Home" }},
+ state: { current: 0 },
+ }, {
+ name: "Current item should be set as active on Enter.",
+ event: { type: "keyDown", el: listEl, options: { key: "Enter" }},
+ state: { current: 0, active: 0 },
+ activeElement: listEl,
+ }, {
+ name: "Active item should be unset on Escape.",
+ event: { type: "keyDown", el: listEl, options: { key: "Escape" }},
+ state: { current: 0, active: null },
+ }, {
+ name: "Current item should be set as active on Space.",
+ event: { type: "keyDown", el: listEl, options: { key: " " }},
+ state: { current: 0, active: 0 },
+ activeElement: listEl,
+ }, {
+ name: "Current item should unset when focus leaves the list.",
+ action: () => blurEl(listEl),
+ state: { current: 0, active: null },
+ activeElement: defaultFocus,
+ }, {
+ name: "Keyboard focus should be set on list's conatiner (<ul>) on focus.",
+ action: () => focusEl(listEl),
+ activeElement: listEl,
+ }, {
+ name: "Current item should be updated to next on ArrowDown.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowDown" }},
+ state: { current: 1, active: null },
+ }, {
+ name: "Current item should be set as active on Enter. Keyboard focus should be " +
+ "set on the first focusable element inside the list item, if available.",
+ event: { type: "keyDown", el: listEl, options: { key: "Enter" }},
+ state: { current: 1, active: 1 },
+ get activeElement() {
+ // When list item becomes active/inactive, it is replaced with a newly rendered
+ // one.
+ return findRenderedDOMComponentWithTag(tree, "a");
+ },
+ }, {
+ name: "Keyboard focus should be set to next tabbable element inside the active " +
+ "list item on Tab.",
+ action() {
+ synthesizeKey("KEY_Tab");
+ },
+ state: { current: 1, active: 1 },
+ get activeElement() {
+ // When list item becomes active/inactive, it is replaced with a newly rendered
+ // one.
+ return findRenderedDOMComponentWithTag(tree, "button");
+ },
+ }, {
+ name: "Keyboard focus should wrap inside the list item when focused on last " +
+ "tabbable element.",
+ action() {
+ synthesizeKey("KEY_Tab");
+ },
+ state: { current: 1, active: 1 },
+ get activeElement() {
+ return findRenderedDOMComponentWithTag(tree, "a");
+ },
+ }, {
+ name: "Keyboard focus should wrap inside the list item when focused on first " +
+ "tabbable element.",
+ action() {
+ synthesizeKey("KEY_Tab", { shiftKey: true });
+ },
+ state: { current: 1, active: 1 },
+ get activeElement() {
+ return findRenderedDOMComponentWithTag(tree, "button");
+ },
+ }, {
+ name: "Active item should be unset on Escape. Focus should move back to the " +
+ "list container.",
+ event: { type: "keyDown", el: listEl, options: { key: "Escape" }},
+ state: { current: 1, active: null },
+ activeElement: listEl,
+ }, {
+ name: "Current item should be set as active on Space. Keyboard focus should be " +
+ "set on the first focusable element inside the list item, if available.",
+ event: { type: "keyDown", el: listEl, options: { key: " " }},
+ state: { current: 1, active: 1 },
+ get activeElement() {
+ // When list item becomes active/inactive, it is replaced with a newly rendered
+ // one.
+ return findRenderedDOMComponentWithTag(tree, "a");
+ },
+ }, {
+ name: "Current item should remain set even when the list is blured. " +
+ "Keyboard focus should be set back to document body.",
+ action: () => listEl.ownerDocument.activeElement.blur(),
+ state: { current: 1, active: null, },
+ activeElement: defaultFocus,
+ }, {
+ name: "Keyboard focus should be set on list's conatiner (<ul>) on focus.",
+ action: () => focusEl(listEl),
+ state: { current: 1, active: null },
+ activeElement: listEl,
+ }, {
+ name: "Current item should be updated to previous on ArrowUp.",
+ event: { type: "keyDown", el: listEl, options: { key: "ArrowUp" }},
+ state: { current: 0, active: null },
+ }, {
+ name: "Current item should be set as active on Enter.",
+ event: { type: "keyDown", el: listEl, options: { key: "Enter" }},
+ state: { current: 0, active: 0 },
+ activeElement: listEl,
+ }, {
+ name: "Keyboard focus should move to another focusable element outside of the " +
+ "list when there's nothing to focus on inside the list item.",
+ action() {
+ synthesizeKey("KEY_Tab", { shiftKey: true });
+ },
+ state: { current: 0, active: null },
+ activeElement: listEl.ownerDocument.documentElement,
+ }];
+
+ for (const test of tests) {
+ const { action, event, state, name } = test;
+
+ is(listEl, findRenderedDOMComponentWithClass(tree, "list"), "Sanity check");
+
+ info(name);
+ if (event) {
+ const { type, options, el } = event;
+ Simulate[type](el, options);
+ } else if (action) {
+ action();
+ }
+
+ if (test.activeElement) {
+ is(listEl.ownerDocument.activeElement, test.activeElement,
+ "Focus is set correctly.");
+ }
+
+ for (const key in state) {
+ is(tree.state[key], state[key], `${key} state is correct.`);
+ }
+ }
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_notification_box_01.html b/devtools/client/shared/components/test/chrome/test_notification_box_01.html
new file mode 100644
index 0000000000..2921d607c3
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_notification_box_01.html
@@ -0,0 +1,136 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for Notification Box. The test is checking:
+* Basic rendering
+* Appending correct classname on wrapping
+* Appending a notification
+* Notification priority
+* Closing notification
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Notification Box</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+
+ const renderedBox = shallowRenderComponent(NotificationBox, {});
+ is(renderedBox.type, "div", "NotificationBox is rendered as <div>");
+
+ info("Test rendering NotificationBox with default props");
+ const boxElement = React.createElement(NotificationBox);
+ const notificationBox = TestUtils.renderIntoDocument(boxElement);
+ const notificationNode = ReactDOM.findDOMNode(notificationBox);
+
+ ok(notificationNode.classList.contains("notificationbox"),
+ "NotificationBox has expected class");
+ ok(notificationNode.classList.contains("border-bottom"),
+ "NotificationBox has expected class");
+ is(notificationNode.textContent, "",
+ "Empty NotificationBox has no text content");
+
+ checkNumberOfNotifications(notificationBox, 0);
+
+ // Append a notification
+ notificationBox.appendNotification(
+ "Info message",
+ "id1",
+ null,
+ PriorityLevels.PRIORITY_INFO_HIGH
+ );
+
+ is (notificationNode.textContent, "Info message",
+ "The box must display notification message");
+ checkNumberOfNotifications(notificationBox, 1);
+
+ // Append more important notification
+ notificationBox.appendNotification(
+ "Critical message",
+ "id2",
+ null,
+ PriorityLevels.PRIORITY_CRITICAL_BLOCK
+ );
+
+ checkNumberOfNotifications(notificationBox, 1);
+
+ is (notificationNode.textContent, "Critical message",
+ "The box must display more important notification message");
+
+ // Append less important notification
+ notificationBox.appendNotification(
+ "Warning message",
+ "id3",
+ null,
+ PriorityLevels.PRIORITY_WARNING_HIGH
+ );
+
+ checkNumberOfNotifications(notificationBox, 1);
+
+ is (notificationNode.textContent, "Critical message",
+ "The box must still display the more important notification");
+
+ ok(notificationBox.getCurrentNotification(),
+ "There must be current notification");
+
+ notificationBox.getNotificationWithValue("id1").close();
+ checkNumberOfNotifications(notificationBox, 1);
+
+ notificationBox.getNotificationWithValue("id2").close();
+ checkNumberOfNotifications(notificationBox, 1);
+
+ notificationBox.getNotificationWithValue("id3").close();
+ checkNumberOfNotifications(notificationBox, 0);
+
+ info(`Check "wrapping" prop works as expected`);
+ // Append wrapping classname to the dom element when passing wrapping prop
+ const boxElementWrapped = React.createElement(NotificationBox, {wrapping: true});
+ const notificationBoxWrapped = TestUtils.renderIntoDocument(boxElementWrapped);
+ const wrappedNotificationNode = ReactDOM.findDOMNode(notificationBoxWrapped);
+
+ ok(wrappedNotificationNode.classList.contains("wrapping"),
+ "Wrapped notificationBox has expected class");
+
+ info(`Check "displayBorderTop/displayBorderBottom" props work as expected`);
+ const element = React.createElement(NotificationBox, {
+ displayBorderTop: true,
+ displayBorderBottom: false,
+ });
+ const renderedElement = TestUtils.renderIntoDocument(element);
+ const elementNode = ReactDOM.findDOMNode(renderedElement);
+
+ ok(elementNode.classList.contains("border-top"),
+ "truthy displayBorderTop render a border-top className");
+ ok(!elementNode.classList.contains("border-bottom"),
+ "falsy displayBorderBottom does not render a border-bottom className");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+
+function checkNumberOfNotifications(notificationBox, expected) {
+ is(TestUtils.scryRenderedDOMComponentsWithClass(
+ notificationBox, "notification").length, expected,
+ "The notification box must have expected number of notifications");
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_notification_box_02.html b/devtools/client/shared/components/test/chrome/test_notification_box_02.html
new file mode 100644
index 0000000000..f74194d128
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_notification_box_02.html
@@ -0,0 +1,73 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for Notification Box. The test is checking:
+* Using custom callback in a notification
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Notification Box</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+
+ // Test rendering
+ const boxElement = React.createElement(NotificationBox);
+ const notificationBox = TestUtils.renderIntoDocument(boxElement);
+ const notificationNode = ReactDOM.findDOMNode(notificationBox);
+
+ let callbackExecuted = false;
+
+ // Append a notification.
+ notificationBox.appendNotification(
+ "Info message",
+ "id1",
+ null,
+ PriorityLevels.PRIORITY_INFO_LOW,
+ undefined,
+ (reason) => {
+ callbackExecuted = true;
+ is(reason, "removed", "The reason must be expected string");
+ }
+ );
+
+ is(TestUtils.scryRenderedDOMComponentsWithClass(
+ notificationBox, "notification").length, 1,
+ "There must be one notification");
+
+ const closeButton = notificationNode.querySelector(
+ ".messageCloseButton");
+
+ // Click the close button to close the notification.
+ TestUtils.Simulate.click(closeButton);
+
+ is(TestUtils.scryRenderedDOMComponentsWithClass(
+ notificationBox, "notification").length, 0,
+ "The notification box must be empty now");
+
+ ok(callbackExecuted, "Event callback must be executed.");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_notification_box_03.html b/devtools/client/shared/components/test/chrome/test_notification_box_03.html
new file mode 100644
index 0000000000..816456edd3
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_notification_box_03.html
@@ -0,0 +1,87 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for Notification Box. The test is checking:
+* Using custom buttons in a notification
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Notification Box</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+
+ // Test rendering
+ const boxElement = React.createElement(NotificationBox);
+ const notificationBox = TestUtils.renderIntoDocument(boxElement);
+ const notificationNode = ReactDOM.findDOMNode(notificationBox);
+
+ let buttonCallbackExecuted = false;
+ const buttons = [{
+ label: "Button1",
+ callback: () => {
+ buttonCallbackExecuted = true;
+
+ // Do not close the notification
+ return true;
+ },
+ }, {
+ label: "Button2",
+ callback: () => {
+ // Close the notification (return value undefined)
+ },
+ }];
+
+ // Append a notification with buttons.
+ notificationBox.appendNotification(
+ "Info message",
+ "id1",
+ null,
+ PriorityLevels.PRIORITY_INFO_LOW,
+ buttons
+ );
+
+ const buttonNodes = notificationNode.querySelectorAll(
+ ".notificationButton");
+
+ is(buttonNodes.length, 2, "There must be two buttons");
+
+ // Click the first button
+ TestUtils.Simulate.click(buttonNodes[0]);
+ ok(buttonCallbackExecuted, "Button callback must be executed.");
+
+ is(TestUtils.scryRenderedDOMComponentsWithClass(
+ notificationBox, "notification").length, 1,
+ "There must be one notification");
+
+ // Click the second button (closing the notification)
+ TestUtils.Simulate.click(buttonNodes[1]);
+
+ is(TestUtils.scryRenderedDOMComponentsWithClass(
+ notificationBox, "notification").length, 0,
+ "The notification box must be empty now");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_notification_box_04.html b/devtools/client/shared/components/test/chrome/test_notification_box_04.html
new file mode 100644
index 0000000000..07ad9af25c
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_notification_box_04.html
@@ -0,0 +1,67 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for Notification Box. The test is checking:
+* Adding a mdnLink to a notification
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Notification Box</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+
+ // Render notification
+ const boxElement = React.createElement(NotificationBox);
+ const notificationBox = TestUtils.renderIntoDocument(boxElement);
+ const notificationNode = ReactDOM.findDOMNode(notificationBox);
+
+ const mdnLink = "https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors"
+
+ const mdnLinkButton = {mdnUrl: mdnLink, label: "learn more about error" }
+
+ // Append a notification with a learn-more link
+ notificationBox.appendNotification(
+ "Info message",
+ "id1",
+ null,
+ PriorityLevels.PRIORITY_INFO_LOW,
+ [mdnLinkButton],
+ (e) => false,
+ );
+
+ const linkNode = notificationNode.querySelector(
+ "a.learn-more-link");
+
+ ok(linkNode, "Link is present");
+
+ is(linkNode.title, "learn more about error", "link has correct title");
+ ok(linkNode.classList.contains("devtools-button"), "link has correct class")
+
+
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_notification_box_05.html b/devtools/client/shared/components/test/chrome/test_notification_box_05.html
new file mode 100644
index 0000000000..b3a4e96378
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_notification_box_05.html
@@ -0,0 +1,63 @@
+
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test for Notification Box. The test is checking:
+* the close button is not present when displayCloseButton is false
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Notification Box</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const { NotificationBox, PriorityLevels } = browserRequire("devtools/client/shared/components/NotificationBox");
+
+ // Test rendering with close button disabled
+ const boxElement = React.createElement(NotificationBox, {displayCloseButton: false});
+ const notificationBox = TestUtils.renderIntoDocument(boxElement);
+ const notificationNode = ReactDOM.findDOMNode(notificationBox);
+
+
+
+ // Append a notification.
+ notificationBox.appendNotification(
+ "Info message",
+ "id1",
+ null,
+ PriorityLevels.PRIORITY_INFO_LOW,
+ [],
+ (e) => false,
+ );
+
+ // Ensure close button is not present
+ const linkNode = notificationNode.querySelector(
+ ".messageCloseButton");
+
+ ok(!linkNode, "Close button is not present");
+
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_searchbox-with-autocomplete.html b/devtools/client/shared/components/test/chrome/test_searchbox-with-autocomplete.html
new file mode 100644
index 0000000000..110d5640c1
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_searchbox-with-autocomplete.html
@@ -0,0 +1,301 @@
+<!-- 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/. -->
+<!DOCTYPE html>
+<html>
+<!--
+Test the searchbox and autocomplete-popup components
+-->
+<head>
+ <meta charset="utf-8">
+ <title>SearchBox component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<script src="head.js"></script>
+<script>
+"use strict";
+window.onload = async function () {
+ /**
+ * Takes a DOMNode with its children as list items,
+ * Typically UL > LI and each item's text value is
+ * compared with the reference item's value as a test
+ *
+ * @params {Node} - Node to be compared
+ * @reference {array} - Reference array for comparison. The selected index is
+ * highlighted as a single element array ie. ["[abc]", "ab", "abcPQR"],
+ * Here the element "abc" is highlighted
+ */
+ function compareAutocompleteList(list, reference) {
+ const delimiter = " - ";
+ const observedList = [...list.children].map(el => {
+ return el.classList.contains("autocomplete-selected")
+ ? `[${el.textContent}]`
+ : el.textContent
+ });
+ is(observedList.join(delimiter), reference.join(delimiter),
+ "Autocomplete items are rendered as expected");
+ }
+
+ function compareCursorPosition(initialElement) {
+ const initialPosition = initialElement.selectionStart;
+ return (element) => {
+ is(element.selectionStart, initialPosition, "Input cursor position is not changed");
+ }
+ }
+
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const SearchBox = React.createFactory(
+ browserRequire("devtools/client/shared/components/SearchBox")
+ );
+ const { component, $ } = await createComponentTest(SearchBox, {
+ type: "search",
+ autocompleteProvider: (filter) => {
+ const baseList = [
+ "foo",
+ "BAR",
+ "baZ",
+ "abc",
+ "pqr",
+ "xyz",
+ "ABC",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ ];
+ if (!filter) {
+ return [];
+ }
+
+ const tokens = filter.split(/\s+/g);
+ const lastToken = tokens[tokens.length - 1];
+ const previousTokens = tokens.slice(0, tokens.length - 1);
+
+ if (!lastToken) {
+ return [];
+ }
+
+ return baseList
+ .filter((item) => {
+ return item.toLowerCase().startsWith(lastToken.toLowerCase())
+ && item.toLowerCase() !== lastToken.toLowerCase();
+ })
+ .sort()
+ .map(item => ({
+ value: [...previousTokens, item].join(" "),
+ displayValue: item,
+ }));
+ },
+ onChange: () => null,
+ });
+
+ async function testSearchBoxWithAutocomplete() {
+ ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible");
+
+ $(".devtools-searchinput").focus();
+ await forceRender(component); // Wait for state update
+ ok(!$(".devtools-autocomplete-popup"), "Autocomplete list not visible");
+
+ sendString("a");
+ await forceRender(component);
+
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "[ABC]",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "abc",
+ ]);
+
+ // Blur event
+ $(".devtools-searchinput").blur();
+ await forceRender(component);
+ ok(!component.state.focused, "focused state was properly set");
+ ok(!$(".devtools-autocomplete-popup"), "Autocomplete list removed from DOM");
+ }
+
+ async function testKeyEventsWithAutocomplete() {
+ // Clear the initial input
+ $(".devtools-searchinput").focus();
+ const cursorPositionIsNotChanged = compareCursorPosition($(".devtools-searchinput"));
+
+ // ArrowDown
+ synthesizeKey("KEY_ArrowDown");
+ await forceRender(component);
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "ABC",
+ "[a1]",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "abc",
+ ]);
+ ok($(".devtools-autocomplete-listbox .autocomplete-item:nth-child(2)")
+ .className.includes("autocomplete-selected"),
+ "Selection class applied");
+
+ // A double ArrowUp should roll back to the bottom of the list
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_ArrowUp");
+ await forceRender(component);
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "ABC",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "[abc]",
+ ]);
+ cursorPositionIsNotChanged($(".devtools-searchinput"));
+
+ // PageDown should take -5 places up
+ synthesizeKey("KEY_PageUp");
+ await forceRender(component);
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "ABC",
+ "[a1]",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "abc",
+ ]);
+ cursorPositionIsNotChanged($(".devtools-searchinput"));
+
+ // PageDown should take +5 places down
+ synthesizeKey("KEY_PageDown");
+ await forceRender(component);
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "ABC",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "[abc]",
+ ]);
+ cursorPositionIsNotChanged($(".devtools-searchinput"));
+
+ // Home should take to the top of the list
+ synthesizeKey("KEY_Home");
+ await forceRender(component);
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "[ABC]",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "abc",
+ ]);
+ cursorPositionIsNotChanged($(".devtools-searchinput"));
+
+ // End should take to the bottom of the list
+ synthesizeKey("KEY_End");
+ await forceRender(component);
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "ABC",
+ "a1",
+ "a2",
+ "a3",
+ "a4",
+ "a5",
+ "[abc]",
+ ]);
+ cursorPositionIsNotChanged($(".devtools-searchinput"));
+
+ // Key down in existing state should rollover to the top
+ synthesizeKey("KEY_ArrowDown");
+ await forceRender(component);
+ // Tab should select the component and hide popup
+ synthesizeKey("KEY_Tab");
+ await forceRender(component);
+ is(component.state.value, "ABC", "Tab hit selects the item");
+ ok(!$(".devtools-autocomplete-popup"), "Tab hit hides the popup");
+
+ // Activate popup by removing a key
+ synthesizeKey("KEY_Backspace");
+ await forceRender(component);
+ ok($(".devtools-autocomplete-popup"), "Popup is up");
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "[ABC]",
+ "abc"
+ ]);
+
+ // Enter key selection
+ synthesizeKey("KEY_ArrowUp");
+ await forceRender(component);
+ synthesizeKey("KEY_Enter");
+ is(component.state.value, "abc", "Enter selection");
+ ok(!$(".devtools-autocomplete-popup"), "Enter/Return hides the popup");
+
+ // Escape should remove the autocomplete component
+ synthesizeKey("KEY_Backspace");
+ await forceRender(component);
+ synthesizeKey("KEY_Escape");
+ await forceRender(component);
+ ok(!$(".devtools-autocomplete-popup"),
+ "Autocomplete list removed from DOM on Escape");
+ }
+
+ async function testMouseEventsWithAutocomplete() {
+ $(".devtools-searchinput").focus();
+ await setState(component, {
+ value: "",
+ focused: true,
+ });
+ await forceRender(component);
+
+ // ArrowDown
+ synthesizeKey("KEY_ArrowDown");
+ await forceRender(component);
+ synthesizeMouseAtCenter($(".devtools-searchinput"), {}, window);
+ await forceRender(component);
+ is(component.state.focused, true, "Component should now be focused");
+
+ sendString("pq");
+ await forceRender(component);
+ synthesizeMouseAtCenter(
+ $(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"),
+ {}, window
+ );
+ await forceRender(component);
+ is(component.state.value, "pqr", "Mouse click selects the item.");
+ ok(!$(".devtools-autocomplete-popup"), "Mouse click on item hides the popup");
+ }
+
+ async function testTokenizedAutocomplete() {
+ // Test for string "pqr ab" which should show list of ABC, abc
+ sendString(" ab");
+ await forceRender(component);
+ compareAutocompleteList($(".devtools-autocomplete-listbox"), [
+ "[ABC]",
+ "abc"
+ ]);
+
+ // Select the first element, value now should be "pqr ABC"
+ synthesizeMouseAtCenter(
+ $(".devtools-autocomplete-listbox .autocomplete-item:nth-child(1)"),
+ {}, window
+ );
+ is(component.state.value, "pqr ABC", "Post Tokenization value selection");
+ }
+
+ add_task(async function () {
+ await testSearchBoxWithAutocomplete();
+ await testKeyEventsWithAutocomplete();
+ await testMouseEventsWithAutocomplete();
+ await testTokenizedAutocomplete();
+ });
+};
+</script>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_searchbox.html b/devtools/client/shared/components/test/chrome/test_searchbox.html
new file mode 100644
index 0000000000..8e2f76c1b9
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_searchbox.html
@@ -0,0 +1,74 @@
+<!-- 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/. -->
+<!DOCTYPE html>
+<html>
+<!--
+Test the searchbox component
+-->
+<head>
+ <meta charset="utf-8">
+ <title>SearchBox component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<script src="head.js"></script>
+<script>
+"use strict";
+window.onload = function () {
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const SearchBox = React.createFactory(
+ browserRequire("devtools/client/shared/components/SearchBox")
+ );
+ ok(SearchBox, "Got the SearchBox factory");
+
+ async function testSimpleSearchBox() {
+ // Test initial state
+ const { component, $ } = await createComponentTest(SearchBox, {
+ type: "search",
+ keyShortcut: "CmdOrCtrl+F",
+ placeholder: "crazy placeholder",
+ });
+
+ is(component.state.value, "", "Initial value is blank");
+ ok(!component.state.focused, "Input isn't initially focused");
+ ok($(".devtools-searchinput-clear").hidden, "Clear button hidden");
+ is($(".devtools-searchinput").placeholder, "crazy placeholder",
+ "Placeholder is properly set");
+
+ synthesizeKey("f", { accelKey: true });
+ await forceRender(component); // Wait for state update
+ ok(component.state.focused, "Shortcut key focused the input box");
+
+ $(".devtools-searchinput").blur();
+ await forceRender(component);
+ ok(!component.state.focused, "`focused` state set to false after blur");
+
+ // Test changing value in state
+ await setState(component, {
+ value: "foo",
+ });
+
+ is(component.state.value, "foo", "value was properly set on state");
+ is($(".devtools-searchinput").value, "foo", "value was properly set on element");
+
+ // Filling input should show clear button
+ ok(!$(".devtools-searchinput-clear").hidden, "Clear button shown");
+
+ // Clearing value should hide clear button
+ await setState(component, {
+ value: "",
+ });
+ await forceRender(component);
+ ok($(".devtools-searchinput-clear").hidden, "Clear button was hidden");
+ }
+
+ add_task(async function () {
+ await testSimpleSearchBox();
+ });
+};
+</script>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_sidebar_toggle.html b/devtools/client/shared/components/test/chrome/test_sidebar_toggle.html
new file mode 100644
index 0000000000..0a31037a84
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_sidebar_toggle.html
@@ -0,0 +1,59 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test sidebar toggle button
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Sidebar toggle button test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ const SidebarToggle = browserRequire("devtools/client/shared/components/SidebarToggle.js");
+
+ try {
+ await test();
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+
+ function test() {
+ const output1 = shallowRenderComponent(SidebarToggle, {
+ collapsed: false,
+ collapsePaneTitle: "Expand",
+ expandPaneTitle: "Collapse"
+ });
+
+ is(output1.type, "button", "Output is a button element");
+ is(output1.props.title, "Expand", "Proper title is set");
+ is(output1.props.className.indexOf("pane-collapsed"), -1,
+ "Proper class name is set");
+
+ const output2 = shallowRenderComponent(SidebarToggle, {
+ collapsed: true,
+ collapsePaneTitle: "Expand",
+ expandPaneTitle: "Collapse"
+ });
+
+ is(output2.props.title, "Collapse", "Proper title is set");
+ ok(output2.props.className.includes("pane-collapsed"),
+ "Proper class name is set");
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_smart-trace-grouping.html b/devtools/client/shared/components/test/chrome/test_smart-trace-grouping.html
new file mode 100644
index 0000000000..174d0f87b4
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_smart-trace-grouping.html
@@ -0,0 +1,141 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the rendering of a stack trace
+-->
+<head>
+ <meta charset="utf-8">
+ <title>StackTrace component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<script src="head.js"></script>
+<script>
+"use strict";
+
+window.onload = function() {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const SmartTrace = React.createFactory(
+ browserRequire("devtools/client/shared/components/SmartTrace"));
+ ok(SmartTrace, "Got the SmartTrace factory");
+
+ add_task(async function() {
+ const REACT_FRAMES_COUNT = 10;
+
+ const stacktrace = [
+ {
+ filename: "https://myfile.com/mahscripts.js",
+ lineNumber: 55,
+ columnNumber: 10,
+ functionName: null,
+ },
+ // Simulated Redux frame
+ {
+ functionName: "rootReducer",
+ filename: "https://myfile.com/loader.js -> https://myfile.com/redux.js",
+ lineNumber: 2,
+ },
+ {
+ functionName: "loadFunc",
+ filename: "https://myfile.com/loader.js -> https://myfile.com/loadee.js",
+ lineNumber: 10,
+ },
+ // Simulated react frames
+ ...(Array.from({length: REACT_FRAMES_COUNT}, (_, i) => ({
+ functionName: "internalReact" + (REACT_FRAMES_COUNT - i),
+ filename: "https://myfile.com/loader.js -> https://myfile.com/react.js",
+ lineNumber: Number(i.toString().repeat(2)),
+ }))),
+ {
+ filename: "https://myfile.com/mahscripts.js",
+ lineNumber: 10,
+ columnNumber: 3,
+ functionName: "onClick",
+ },
+ ];
+
+ const props = {
+ stacktrace,
+ onViewSourceInDebugger: () => {},
+ };
+
+ const trace = ReactDOM.render(SmartTrace(props), window.document.body);
+ await forceRender(trace);
+
+ const traceEl = ReactDOM.findDOMNode(trace);
+ ok(traceEl, "Rendered SmartTrace has an element");
+
+ isDeeply(getStacktraceText(traceEl), [
+ `<anonymous> https://myfile.com/mahscripts.js:55`,
+ `rootReducer Redux`,
+ `loadFunc https://myfile.com/loadee.js:10`,
+ `▶︎ React 10`,
+ `onClick https://myfile.com/mahscripts.js:10`,
+ ], "React frames are grouped - Redux frame is not");
+
+ info("Expand React group");
+ let onReactGroupExpanded = waitFor(() =>
+ traceEl.querySelector(".frames-group.expanded"));
+ traceEl.querySelector(".group").click();
+ await onReactGroupExpanded;
+
+ isDeeply(getStacktraceText(traceEl), [
+ `<anonymous> https://myfile.com/mahscripts.js:55`,
+ `rootReducer Redux`,
+ `loadFunc https://myfile.com/loadee.js:10`,
+ `▼ React 10`,
+ `| internalReact10`,
+ `| internalReact9`,
+ `| internalReact8`,
+ `| internalReact7`,
+ `| internalReact6`,
+ `| internalReact5`,
+ `| internalReact4`,
+ `| internalReact3`,
+ `| internalReact2`,
+ `| internalReact1`,
+ `onClick https://myfile.com/mahscripts.js:10`,
+ ], "React frames can be expanded");
+
+ info("Collapse React group");
+ onReactGroupExpanded = waitFor(() =>
+ !traceEl.querySelector(".frames-group.expanded"));
+ traceEl.querySelector(".group").click();
+ await onReactGroupExpanded;
+
+ isDeeply(getStacktraceText(traceEl), [
+ `<anonymous> https://myfile.com/mahscripts.js:55`,
+ `rootReducer Redux`,
+ `loadFunc https://myfile.com/loadee.js:10`,
+ `▶︎ React 10`,
+ `onClick https://myfile.com/mahscripts.js:10`,
+ ], "React frames can be collapsed");
+ });
+
+ function getStacktraceText(traceElement) {
+ return Array.from(traceElement.querySelectorAll(".frame, .frames-group")).map(el => {
+ // If it's a group, we want to append an arrow representing the group state
+ if (el.classList.contains("frames-group")) {
+ const arrow = el.classList.contains("expanded") ? "▼" : "▶︎";
+ const content = el.querySelector(".group").textContent.trim();
+ return `${arrow} ${content}`;
+ }
+
+ const title = el.querySelector(".title");
+ if (el.closest(".frames-group")) {
+ return `| ${title.textContent}`;
+ }
+
+ const location = el.querySelector(".location");
+ return `${title.textContent} ${location.textContent}`;
+ });
+ }
+};
+</script>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_smart-trace-source-maps.html b/devtools/client/shared/components/test/chrome/test_smart-trace-source-maps.html
new file mode 100644
index 0000000000..1beade0c0c
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_smart-trace-source-maps.html
@@ -0,0 +1,290 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the rendering of a stack trace
+-->
+<head>
+ <meta charset="utf-8">
+ <title>StackTrace component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<script src="head.js"></script>
+<script>
+"use strict";
+
+window.onload = function() {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const SmartTrace = React.createFactory(
+ browserRequire("devtools/client/shared/components/SmartTrace"));
+ ok(SmartTrace, "Got the SmartTrace factory");
+
+ add_task(async function testHappyPath() {
+ const stacktrace = [
+ {
+ filename: "https://myfile.com/bundle.js",
+ lineNumber: 1,
+ columnNumber: 10,
+ },
+ {
+ functionName: "loadFunc",
+ filename: "https://myfile.com/bundle.js",
+ lineNumber: 2,
+ },
+ ];
+
+ let onReadyCount = 0;
+ const props = {
+ stacktrace,
+ initialRenderDelay: 2000,
+ onViewSourceInDebugger: () => {},
+ onReady: () => {
+ onReadyCount++;
+ },
+ // A mock source map service.
+ sourceMapURLService: {
+ subscribeByLocation ({ line, column }, callback) {
+ const newLine = Number(line.toString().repeat(2));
+ // Resolve immediately.
+ callback({
+ url: "https://bugzilla.mozilla.org/original.js",
+ line: newLine,
+ column,
+ });
+ return () => {};
+ },
+ },
+ };
+
+ const trace = ReactDOM.render(SmartTrace(props),
+ window.document.body.querySelector("#s1"));
+ await forceRender(trace);
+
+ const traceEl = ReactDOM.findDOMNode(trace);
+ ok(traceEl, "Rendered SmartTrace has an element");
+
+ const frameEls = Array.from(traceEl.querySelectorAll(".frame"));
+ ok(frameEls, "Rendered SmartTrace has frames");
+ is(frameEls.length, 2, "SmartTrace has 2 frames");
+
+ checkSmartFrameString({
+ el: frameEls[0],
+ functionName: "<anonymous>",
+ location: "original.js:11",
+ tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:11",
+ });
+
+ checkSmartFrameString({
+ el: frameEls[1],
+ functionName: "loadFunc",
+ location: "original.js:22",
+ tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:22",
+ });
+
+ is(onReadyCount, 1, "onReady was called once");
+ });
+
+ add_task(async function testSlowSourcemapService() {
+ const stacktrace = [
+ {
+ filename: "https://myfile.com/bundle.js",
+ functionName: "last",
+ lineNumber: 1,
+ columnNumber: 10,
+ },
+ {
+ filename: "https://myfile.com/bundle.js",
+ functionName: "first",
+ lineNumber: 2,
+ columnNumber: 10,
+ },
+ ];
+
+ const sourcemapTimeout = 2000;
+ const initialRenderDelay = 300;
+ let onReadyCount = 0;
+
+ const props = {
+ stacktrace,
+ initialRenderDelay,
+ onViewSourceInDebugger: () => {},
+ onReady: () => {
+ onReadyCount++;
+ },
+ // A mock source map service.
+ sourceMapURLService: {
+ subscribeByLocation ({ line, column }, callback) {
+ // Resolve after a while.
+ setTimeout(() => {
+ const newLine = Number(line.toString().repeat(2));
+ callback({
+ url: "https://myfile.com/react.js",
+ line: newLine,
+ column,
+ });
+ }, sourcemapTimeout)
+
+ return () => {};
+ },
+ },
+ };
+
+ const trace = ReactDOM.render(SmartTrace(props),
+ window.document.body.querySelector("#s2"));
+
+ let traceEl = ReactDOM.findDOMNode(trace);
+ ok(!traceEl, "Nothing was rendered at first");
+ is(onReadyCount, 0, "onReady isn't called if SmartTrace isn't rendered");
+
+ info("Wait for the initial delay to be over");
+ await new Promise(res => setTimeout(res, initialRenderDelay));
+
+ traceEl = ReactDOM.findDOMNode(trace);
+ ok(traceEl, "The trace was rendered");
+
+ let frameEls = Array.from(traceEl.querySelectorAll(".frame"));
+ ok(frameEls, "Rendered SmartTrace has frames");
+ is(frameEls.length, 2, "SmartTrace has 2 frames");
+
+ info("Check that the original frames are displayed after the initial delay");
+ checkSmartFrameString({
+ el: frameEls[0],
+ functionName: "last",
+ location: "https://myfile.com/bundle.js:1",
+ tooltip: "View source in Debugger → https://myfile.com/bundle.js:1",
+ });
+
+ checkSmartFrameString({
+ el: frameEls[1],
+ functionName: "first",
+ location: "https://myfile.com/bundle.js:2",
+ tooltip: "View source in Debugger → https://myfile.com/bundle.js:2",
+ });
+
+ is(onReadyCount, 1, "onReady was called once");
+
+ info("Check the the sourcemapped version is rendered after the sourcemapTimeout");
+ await waitFor(() => !!traceEl.querySelector(".group"));
+
+ frameEls = Array.from(traceEl.querySelectorAll(".frame"));
+ is(frameEls.length, 0, "SmartTrace has no frame");
+
+ const groups = Array.from(traceEl.querySelectorAll(".group"));
+ is(groups.length, 1, "SmartTrace has a group");
+ is(groups[0].textContent.trim(), "React 2", "A collapsed React group is displayed");
+
+ is(onReadyCount, 1, "onReady was only called once");
+ });
+
+ add_task(async function testFlakySourcemapService() {
+ const stacktrace = [
+ {
+ filename: "https://myfile.com/bundle.js",
+ functionName: "last",
+ lineNumber: 1,
+ columnNumber: 10,
+ },
+ {
+ filename: "https://myfile.com/bundle.js",
+ functionName: "pending",
+ lineNumber: 2,
+ columnNumber: 10,
+ },
+ {
+ filename: "https://myfile.com/bundle.js",
+ functionName: "first",
+ lineNumber: 3,
+ columnNumber: 10,
+ },
+ ];
+
+ const initialRenderDelay = 300;
+ const onSourceMapResultDebounceDelay = 50;
+ let onReadyCount = 0;
+
+ const props = {
+ stacktrace,
+ initialRenderDelay,
+ onSourceMapResultDebounceDelay,
+ onViewSourceInDebugger: () => {},
+ onReady: () => {
+ onReadyCount++;
+ },
+ // A mock source map service.
+ sourceMapURLService: {
+ subscribeByLocation ({ line, column }, callback) {
+ // Don't call the callback for the second frame to simulate a flaky sourcemap
+ // service request.
+ if (line === 2) {
+ return () => {};
+ }
+
+ const newLine = Number(line.toString().repeat(2));
+ callback({
+ url: `https://myfile.com/file-${line}.js`,
+ line: newLine,
+ column,
+ });
+ return () => {};
+ },
+ },
+ };
+
+ const trace = ReactDOM.render(SmartTrace(props),
+ window.document.body.querySelector("#s3"));
+
+ let traceEl = ReactDOM.findDOMNode(trace);
+ ok(!traceEl, "Nothing was rendered at first");
+ is(onReadyCount, 0, "onReady isn't called if SmartTrace isn't rendered");
+
+ info("Wait for the initial delay + debounce to be over");
+ await waitFor(() => {
+ const el = ReactDOM.findDOMNode(trace)
+ return el && el.textContent.includes("file-1.js");
+ });
+
+ traceEl = ReactDOM.findDOMNode(trace);
+ ok(traceEl, "The trace was rendered");
+
+ const frameEls = Array.from(traceEl.querySelectorAll(".frame"));
+ ok(frameEls, "Rendered SmartTrace has frames");
+ is(frameEls.length, 3, "SmartTrace has 3 frames");
+
+ info("Check that the original frames are displayed even if there's no sourcemap " +
+ "response for some frames");
+ checkSmartFrameString({
+ el: frameEls[0],
+ functionName: "last",
+ location: "file-1.js:11",
+ tooltip: "View source in Debugger → https://myfile.com/file-1.js:11",
+ });
+
+ checkSmartFrameString({
+ el: frameEls[1],
+ functionName: "pending",
+ location: "bundle.js:2",
+ tooltip: "View source in Debugger → https://myfile.com/bundle.js:2",
+ });
+
+ checkSmartFrameString({
+ el: frameEls[2],
+ functionName: "first",
+ location: "file-3.js:33",
+ tooltip: "View source in Debugger → https://myfile.com/file-3.js:33",
+ });
+
+ is(onReadyCount, 1, "onReady was only called once");
+ });
+
+};
+</script>
+<section id=s1></section>
+<section id=s2></section>
+<section id=s3></section>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_smart-trace.html b/devtools/client/shared/components/test/chrome/test_smart-trace.html
new file mode 100644
index 0000000000..eedb72cc13
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_smart-trace.html
@@ -0,0 +1,172 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the rendering of a stack trace
+-->
+<head>
+ <meta charset="utf-8">
+ <title>StackTrace component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+ <section id=s1></section>
+ <section id=s2></section>
+ <section id=s3></section>
+ <section id=s4></section>
+<script src="head.js"></script>
+<script>
+"use strict";
+
+window.onload = function() {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const SmartTrace = React.createFactory(
+ browserRequire("devtools/client/shared/components/SmartTrace"));
+ ok(SmartTrace, "Got the SmartTrace factory");
+
+ const stacktrace = [
+ {
+ filename: "https://myfile.com/mahscripts.js",
+ lineNumber: 55,
+ columnNumber: 10,
+ functionName: null,
+ },
+ {
+ functionName: "loadFunc",
+ filename: "https://myfile.com/loader.js -> https://myfile.com/loadee.js",
+ lineNumber: 10,
+ },
+ ];
+
+ add_task(async function testBasic() {
+ info("Check basic rendering");
+ let onReadyCount = 0;
+ const props = {
+ stacktrace,
+ onViewSourceInDebugger: () => {},
+ onReady: () => {
+ onReadyCount++;
+ },
+ };
+ await renderSmartTraceAndAssertContent(
+ window.document.body.querySelector("#s1"),
+ props
+ );
+ is(onReadyCount, 1, "onReady was called once");
+ });
+
+ add_task(async function testZeroDelay() {
+ info("Check rendering with source map service and 0 initial delay");
+ let onReadyCount = 0;
+ const props = {
+ stacktrace,
+ onViewSourceInDebugger: () => {},
+ onReady: () => {
+ onReadyCount++;
+ },
+ initialRenderDelay: 0,
+ sourceMapURLService: {
+ subscribeByLocation: () => {}
+ },
+ };
+ await renderSmartTraceAndAssertContent(
+ window.document.body.querySelector("#s2"),
+ props
+ );
+ is(onReadyCount, 1, "onReady was called once");
+ });
+
+ add_task(async function testNullDelay() {
+ info("Check rendering with source map service and null initial delay");
+ let onReadyCount = 0;
+ const props = {
+ stacktrace,
+ onViewSourceInDebugger: () => {},
+ onReady: () => {
+ onReadyCount++;
+ },
+ initialRenderDelay: 0,
+ sourceMapURLService: {
+ subscribeByLocation: () => {}
+ },
+ };
+ await renderSmartTraceAndAssertContent(
+ window.document.body.querySelector("#s3"),
+ props
+ );
+ is(onReadyCount, 1, "onReady was called once");
+ });
+
+ add_task(async function testDelay() {
+ info("Check rendering with source map service and initial delay");
+ let onReadyCount = 0;
+ const props = {
+ stacktrace,
+ onViewSourceInDebugger: () => {},
+ onReady: () => {
+ onReadyCount++;
+ },
+ initialRenderDelay: 500,
+ sourceMapURLService: {
+ subscribeByLocation: () => {}
+ },
+ };
+ const el = window.document.body.querySelector("#s4");
+ await renderSmartTraceAndAssertContent(
+ el,
+ props,
+ false
+ );
+ is(onReadyCount, 0, "onReady wasn't called at first");
+ info(`Wait for ${props.initialRenderDelay}ms so the stacktrace should be rendered`)
+ await new Promise(res => setTimeout(res, props.initialRenderDelay))
+ is(onReadyCount, 1, "onReady was called after waiting for the initial delay");
+ assertRenderedElementContent(el);
+ });
+
+ async function renderSmartTraceAndAssertContent(el, props, shouldBeRendered = true) {
+ let trace;
+ await new Promise(resolve => {
+ trace = ReactDOM.render(SmartTrace(props), el, resolve);
+ });
+
+ const traceEl = ReactDOM.findDOMNode(trace);
+
+ if (!shouldBeRendered) {
+ ok(!traceEl, "SmartTrace wasn't rendered initially");
+ return;
+ }
+
+ ok(traceEl, "Rendered SmartTrace has an element");
+ assertRenderedElementContent(traceEl);
+ }
+
+ function assertRenderedElementContent(el) {
+ const frameEls = Array.from(el.querySelectorAll(".frame"));
+ ok(frameEls, "Rendered SmartTrace has frames");
+ is(frameEls.length, 2, "SmartTrace has 2 frames");
+
+ checkSmartFrameString({
+ el: frameEls[0],
+ functionName: "<anonymous>",
+ location: "https://myfile.com/mahscripts.js:55",
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js:55",
+ });
+
+ // Check the third frame, the source should be parsed into a valid source URL
+ checkSmartFrameString({
+ el: frameEls[1],
+ functionName: "loadFunc",
+ location: "https://myfile.com/loadee.js:10",
+ tooltip: "View source in Debugger → https://myfile.com/loadee.js:10",
+ });
+ }
+
+};
+</script>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_stack-trace-source-maps.html b/devtools/client/shared/components/test/chrome/test_stack-trace-source-maps.html
new file mode 100644
index 0000000000..7535d4d2df
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_stack-trace-source-maps.html
@@ -0,0 +1,98 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the rendering of a stack trace with source maps
+-->
+<head>
+ <meta charset="utf-8">
+ <title>StackTrace component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<script src="head.js"></script>
+<script>
+"use strict";
+
+window.onload = function () {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const StackTrace = React.createFactory(
+ browserRequire("devtools/client/shared/components/StackTrace")
+ );
+ ok(StackTrace, "Got the StackTrace factory");
+
+ add_task(async function () {
+ const stacktrace = [
+ {
+ filename: "https://bugzilla.mozilla.org/bundle.js",
+ lineNumber: 99,
+ columnNumber: 10
+ },
+ {
+ functionName: "loadFunc",
+ filename: "https://bugzilla.mozilla.org/bundle.js",
+ lineNumber: 108,
+ }
+ ];
+
+ const props = {
+ stacktrace,
+ onViewSourceInDebugger: () => {},
+ // A mock source map service.
+ sourceMapURLService: {
+ subscribeByLocation ({ line, column }, callback) {
+ const newLine = line === 99 ? 1 : 7;
+ // Resolve immediately.
+ callback({
+ url: "https://bugzilla.mozilla.org/original.js",
+ line: newLine,
+ column,
+ });
+
+ return () => {}
+ },
+ },
+ };
+
+ const trace = ReactDOM.render(StackTrace(props), window.document.body);
+ await forceRender(trace);
+
+ const traceEl = ReactDOM.findDOMNode(trace);
+ ok(traceEl, "Rendered StackTrace has an element");
+
+ // Get the child nodes and filter out the text-only whitespace ones
+ const frameEls = Array.from(traceEl.childNodes)
+ .filter(n => n.className && n.className.includes("frame"));
+ ok(frameEls, "Rendered StackTrace has frames");
+ is(frameEls.length, 2, "StackTrace has 2 frames");
+
+ checkFrameString({
+ el: frameEls[0],
+ functionName: "<anonymous>",
+ source: "https://bugzilla.mozilla.org/original.js",
+ file: "original.js",
+ line: 1,
+ column: 10,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:1:10",
+ });
+
+ checkFrameString({
+ el: frameEls[1],
+ functionName: "loadFunc",
+ source: "https://bugzilla.mozilla.org/original.js",
+ file: "original.js",
+ line: 7,
+ column: null,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://bugzilla.mozilla.org/original.js:7",
+ });
+ });
+};
+</script>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_stack-trace.html b/devtools/client/shared/components/test/chrome/test_stack-trace.html
new file mode 100644
index 0000000000..56d9288f06
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_stack-trace.html
@@ -0,0 +1,100 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the rendering of a stack trace
+-->
+<head>
+ <meta charset="utf-8">
+ <title>StackTrace component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<script src="head.js"></script>
+<script>
+"use strict";
+
+window.onload = function() {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const StackTrace = React.createFactory(
+ browserRequire("devtools/client/shared/components/StackTrace")
+ );
+ ok(StackTrace, "Got the StackTrace factory");
+
+ add_task(async function() {
+ const stacktrace = [
+ {
+ filename: "https://myfile.com/mahscripts.js",
+ lineNumber: 55,
+ columnNumber: 10,
+ },
+ {
+ asyncCause: "because",
+ functionName: "loadFunc",
+ filename: "https://myfile.com/loadee.js",
+ lineNumber: 10,
+ },
+ ];
+
+ const props = {
+ stacktrace,
+ onViewSourceInDebugger: () => {},
+ };
+
+ const trace = ReactDOM.render(StackTrace(props), window.document.body);
+ await forceRender(trace);
+
+ const traceEl = ReactDOM.findDOMNode(trace);
+ ok(traceEl, "Rendered StackTrace has an element");
+
+ // Get the child nodes and filter out the text-only whitespace ones
+ const frameEls = Array.from(traceEl.childNodes)
+ .filter(n => n.className && n.className.includes("frame"));
+ ok(frameEls, "Rendered StackTrace has frames");
+ is(frameEls.length, 3, "StackTrace has 3 frames");
+
+ // Check the top frame, function name should be anonymous
+ checkFrameString({
+ el: frameEls[0],
+ functionName: "<anonymous>",
+ source: "https://myfile.com/mahscripts.js",
+ file: "https://myfile.com/mahscripts.js",
+ line: 55,
+ column: 10,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/mahscripts.js:55:10",
+ });
+
+ // Check the async cause node
+ is(frameEls[1].className, "frame-link-async-cause",
+ "Async cause has the right class");
+ is(frameEls[1].textContent, "(Async: because)", "Async cause has the right label");
+
+ // Check the third frame, the source should be parsed into a valid source URL
+ checkFrameString({
+ el: frameEls[2],
+ functionName: "loadFunc",
+ source: "https://myfile.com/loadee.js",
+ file: "https://myfile.com/loadee.js",
+ line: 10,
+ column: null,
+ shouldLink: true,
+ tooltip: "View source in Debugger → https://myfile.com/loadee.js:10",
+ });
+
+ // Check the tabs and newlines in the stack trace textContent
+ const traceText = traceEl.textContent;
+ const traceLines = traceText.split("\n");
+ ok(!!traceLines.length, "There are newlines in the stack trace text");
+ is(traceLines.pop(), "", "There is a newline at the end of the stack trace text");
+ is(traceLines.length, 3, "The stack trace text has 3 lines");
+ ok(traceLines.every(l => l[0] == "\t"), "Every stack trace line starts with tab");
+ });
+};
+</script>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tabs_accessibility.html b/devtools/client/shared/components/test/chrome/test_tabs_accessibility.html
new file mode 100644
index 0000000000..4d0ea6ef96
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tabs_accessibility.html
@@ -0,0 +1,82 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test tabs accessibility.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tabs component accessibility test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const InspectorTabPanel = createFactory(browserRequire("devtools/client/inspector/components/InspectorTabPanel"));
+ const Tabbar =
+ createFactory(browserRequire("devtools/client/shared/components/tabs/TabBar"));
+ const tabbar = Tabbar();
+ const tabbarReact = ReactDOM.render(tabbar, window.document.body);
+ const tabbarEl = ReactDOM.findDOMNode(tabbarReact);
+
+ // Setup for InspectorTabPanel
+ const tabpanels = document.createElement("div");
+ tabpanels.id = "tabpanels";
+ document.body.appendChild(tabpanels);
+
+ await addTabWithPanel(0);
+ await addTabWithPanel(1);
+
+ const tabAnchors = tabbarEl.querySelectorAll("li.tabs-menu-item a");
+
+ is(tabAnchors[0].parentElement.getAttribute("role"), "presentation", "li role is set correctly");
+ is(tabAnchors[0].getAttribute("role"), "tab", "Anchor role is set correctly");
+ is(tabAnchors[0].getAttribute("aria-selected"), "true", "Anchor aria-selected is set correctly by default");
+ is(tabAnchors[0].getAttribute("aria-controls"), "sidebar-0-panel", "Anchor aria-controls is set correctly");
+ is(tabAnchors[1].parentElement.getAttribute("role"), "presentation", "li role is set correctly");
+ is(tabAnchors[1].getAttribute("role"), "tab", "Anchor role is set correctly");
+ is(tabAnchors[1].getAttribute("aria-selected"), "false", "Anchor aria-selected is set correctly by default");
+ is(tabAnchors[1].getAttribute("aria-controls"), "sidebar-1-panel", "Anchor aria-controls is set correctly");
+
+ await setState(tabbarReact, Object.assign({}, tabbarReact.state, {
+ activeTab: 1
+ }));
+
+ is(tabAnchors[0].getAttribute("aria-selected"), "false", "Anchor aria-selected is reset correctly");
+ is(tabAnchors[1].getAttribute("aria-selected"), "true", "Anchor aria-selected is reset correctly");
+
+ function addTabWithPanel(tabId) {
+ // Setup for InspectorTabPanel
+ const panel = document.createElement("div");
+ panel.id = `sidebar-${tabId}`;
+ document.body.appendChild(panel);
+
+ return setState(tabbarReact, Object.assign({}, tabbarReact.state, {
+ tabs: tabbarReact.state.tabs.concat({
+ id: `sidebar-${tabId}`,
+ title: `tab-${tabId}`,
+ panel: InspectorTabPanel
+ }),
+ }));
+ }
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tabs_menu.html b/devtools/client/shared/components/test/chrome/test_tabs_menu.html
new file mode 100644
index 0000000000..cc4638e05a
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tabs_menu.html
@@ -0,0 +1,84 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html class="theme-light">
+<!--
+Test all-tabs menu.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tabs component All-tabs menu test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" type="text/css" href="chrome://devtools/skin/variables.css">
+ <link rel="stylesheet" type="text/css" href="chrome://devtools/skin/common.css">
+ <link rel="stylesheet" type="text/css" href="chrome://devtools/content/shared/components/tabs/Tabs.css">
+ <link rel="stylesheet" type="text/css" href="chrome://devtools/content/inspector/components/InspectorTabPanel.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { Component, createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Tabbar = createFactory(browserRequire("devtools/client/shared/components/tabs/TabBar"));
+
+ // Create container for the TabBar. Set smaller width
+ // to ensure that tabs won't fit and the all-tabs menu
+ // needs to appear.
+ const tabBarBox = document.createElement("div");
+ tabBarBox.style.width = "200px";
+ tabBarBox.style.height = "200px";
+ tabBarBox.style.border = "1px solid lightgray";
+ document.body.appendChild(tabBarBox);
+
+ // Render the tab-bar.
+ const tabbar = Tabbar({
+ showAllTabsMenu: true,
+ });
+
+ const tabbarReact = ReactDOM.render(tabbar, tabBarBox);
+
+ class TabPanelClass extends Component {
+ render() {
+ return dom.div({}, "content");
+ }
+ }
+
+ // Test panel.
+ const TabPanel = createFactory(TabPanelClass);
+
+ // Create a few panels.
+ await addTabWithPanel(1);
+ await addTabWithPanel(2);
+ await addTabWithPanel(3);
+ await addTabWithPanel(4);
+ await addTabWithPanel(5);
+
+ // Make sure the all-tabs menu is there.
+ const allTabsMenu = tabBarBox.querySelector(".all-tabs-menu");
+ ok(allTabsMenu, "All-tabs menu must be rendered");
+
+ function addTabWithPanel(tabId) {
+ return setState(tabbarReact, Object.assign({}, tabbarReact.state, {
+ tabs: tabbarReact.state.tabs.concat({id: `${tabId}`,
+ title: `tab-${tabId}`, panel: TabPanel}),
+ }));
+ }
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree-view_01.html b/devtools/client/shared/components/test/chrome/test_tree-view_01.html
new file mode 100644
index 0000000000..0acae4c1dc
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree-view_01.html
@@ -0,0 +1,290 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that TreeView component has working keyboard interactions.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>TreeView component keyboard test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = function() {
+ try {
+ const { a, button, div } =
+ require("devtools/client/shared/vendor/react-dom-factories");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const {
+ Simulate,
+ findRenderedDOMComponentWithClass,
+ findRenderedDOMComponentWithTag,
+ scryRenderedDOMComponentsWithClass,
+ } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const TreeView =
+ browserRequire("devtools/client/shared/components/tree/TreeView");
+
+ const _props = {
+ ...TEST_TREE_VIEW_INTERFACE,
+ renderValue: props => {
+ return (props.value === "C" ?
+ div({},
+ props.value + " ",
+ a({ href: "#" }, "Focusable 1"),
+ button({ }, "Focusable 2")) :
+ props.value + ""
+ );
+ },
+ };
+ const treeView = React.createElement(TreeView, _props);
+ const tree = ReactDOM.render(treeView, document.body);
+ const treeViewEl = findRenderedDOMComponentWithClass(tree, "treeTable");
+ const rows = scryRenderedDOMComponentsWithClass(tree, "treeRow");
+ const defaultFocus = treeViewEl.ownerDocument.body;
+
+ function blurEl(el) {
+ // Simulate.blur does not seem to update the activeElement.
+ el.blur();
+ }
+
+ function focusEl(el) {
+ // Simulate.focus does not seem to update the activeElement.
+ el.focus();
+ }
+
+ const tests = [{
+ name: "Test default TreeView state. Keyboard focus is set to document " +
+ "body by default.",
+ state: { selected: null, active: null },
+ activeElement: defaultFocus,
+ }, {
+ name: "Selected row must be set to the first row on initial focus. " +
+ "Keyboard focus should be set on TreeView's conatiner.",
+ action: () => {
+ focusEl(treeViewEl);
+ Simulate.click(rows[0]);
+ },
+ activeElement: treeViewEl,
+ state: { selected: "/B" },
+ }, {
+ name: "Selected row should remain set even when the treeView is " +
+ "blured. Keyboard focus should be set back to document body.",
+ action: () => blurEl(treeViewEl),
+ state: { selected: "/B" },
+ activeElement: defaultFocus,
+ }, {
+ name: "Selected row must be re-set again to the first row on initial " +
+ "focus. Keyboard focus should be set on treeView's conatiner.",
+ action: () => focusEl(treeViewEl),
+ activeElement: treeViewEl,
+ state: { selected: "/B" },
+ }, {
+ name: "Selected row should be updated to next on ArrowDown.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowDown" }},
+ state: { selected: "/C" },
+ }, {
+ name: "Selected row should be updated to last on ArrowDown.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowDown" }},
+ state: { selected: "/D" },
+ }, {
+ name: "Selected row should remain on last on ArrowDown.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowDown" }},
+ state: { selected: "/D" },
+ }, {
+ name: "Selected row should be updated to previous on ArrowUp.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowUp" }},
+ state: { selected: "/C" },
+ }, {
+ name: "Selected row should be updated to first on ArrowUp.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowUp" }},
+ state: { selected: "/B" },
+ }, {
+ name: "Selected row should remain on first on ArrowUp.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowUp" }},
+ state: { selected: "/B" },
+ }, {
+ name: "Selected row should move to the next matching row with first letter navigation.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "C" }},
+ state: { selected: "/C" },
+ }, {
+ name: "Selected row should not change when there are no more visible nodes matching first letter navigation.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "C" }},
+ state: { selected: "/C" },
+ }, {
+ name: "Selected row should be updated to last on End.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "End" }},
+ state: { selected: "/D" },
+ }, {
+ name: "Selected row should be updated to first on Home.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "Home" }},
+ state: { selected: "/B" },
+ }, {
+ name: "Selected row should be set as active on Enter.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "Enter" }},
+ state: { selected: "/B", active: "/B" },
+ activeElement: treeViewEl,
+ }, {
+ name: "Active row should be unset on Escape.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "Escape" }},
+ state: { selected: "/B", active: null },
+ }, {
+ name: "Selected row should be set as active on Space.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: " " }},
+ state: { selected: "/B", active: "/B" },
+ activeElement: treeViewEl,
+ }, {
+ name: "Selected row should unset when focus leaves the treeView.",
+ action: () => blurEl(treeViewEl),
+ state: { selected: "/B", active: null },
+ activeElement: defaultFocus,
+ }, {
+ name: "Keyboard focus should be set on treeView's conatiner on focus.",
+ action: () => focusEl(treeViewEl),
+ activeElement: treeViewEl,
+ }, {
+ name: "Selected row should be updated to next on ArrowDown.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowDown" }},
+ state: { selected: "/C", active: null },
+ }, {
+ name: "Selected row should be set as active on Enter. Keyboard focus " +
+ "should be set on the first focusable element inside the row, if " +
+ "available.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "Enter" }},
+ state: { selected: "/C", active: "/C" },
+ get activeElement() {
+ // When row becomes active/inactive, it is replaced with a newly
+ // rendered one.
+ return findRenderedDOMComponentWithTag(tree, "a");
+ },
+ }, {
+ name: "Keyboard focus should be set to next tabbable element inside " +
+ "the active row on Tab.",
+ action() {
+ synthesizeKey("KEY_Tab");
+ },
+ state: { selected: "/C", active: "/C" },
+ get activeElement() {
+ // When row becomes active/inactive, it is replaced with a newly
+ // rendered one.
+ return findRenderedDOMComponentWithTag(tree, "button");
+ },
+ }, {
+ name: "Keyboard focus should wrap inside the row when focused on last " +
+ "tabbable element.",
+ action() {
+ synthesizeKey("KEY_Tab");
+ },
+ state: { selected: "/C", active: "/C" },
+ get activeElement() {
+ return findRenderedDOMComponentWithTag(tree, "a");
+ },
+ }, {
+ name: "Keyboard focus should wrap inside the row when focused on first " +
+ "tabbable element.",
+ action() {
+ synthesizeKey("KEY_Tab", { shiftKey: true });
+ },
+ state: { selected: "/C", active: "/C" },
+ get activeElement() {
+ return findRenderedDOMComponentWithTag(tree, "button");
+ },
+ }, {
+ name: "Active row should be unset on Escape. Focus should move back to " +
+ "the treeView container.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "Escape" }},
+ state: { selected: "/C", active: null },
+ activeElement: treeViewEl,
+ }, {
+ name: "Selected row should be set as active on Space. Keyboard focus " +
+ "should be set on the first focusable element inside the row, if " +
+ "available.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: " " }},
+ state: { selected: "/C", active: "/C" },
+ get activeElement() {
+ // When row becomes active/inactive, it is replaced with a newly
+ // rendered one.
+ return findRenderedDOMComponentWithTag(tree, "a");
+ },
+ }, {
+ name: "Selected row should remain set even when the treeView is " +
+ "blured. Keyboard focus should be set back to document body.",
+ action: () => treeViewEl.ownerDocument.activeElement.blur(),
+ state: { selected: "/C", active: null },
+ activeElement: defaultFocus,
+ }, {
+ name: "Keyboard focus should be set on treeView's conatiner on focus.",
+ action: () => focusEl(treeViewEl),
+ state: { selected: "/C", active: null },
+ activeElement: treeViewEl,
+ }, {
+ name: "Selected row should be set as active on Space. Keyboard focus " +
+ "should be set on the first focusable element inside the row, if " +
+ "available.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: " " }},
+ state: { selected: "/C", active: "/C" },
+ get activeElement() {
+ // When row becomes active/inactive, it is replaced with a newly
+ // rendered one.
+ return findRenderedDOMComponentWithTag(tree, "a");
+ },
+ }, {
+ name: "Selected row should be updated to previous on ArrowUp.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "ArrowUp" }},
+ state: { selected: "/B", active: null },
+ activeElement: treeViewEl,
+ }, {
+ name: "Selected row should be set as active on Enter.",
+ event: { type: "keyDown", el: treeViewEl, options: { key: "Enter" }},
+ state: { selected: "/B", active: "/B" },
+ activeElement: treeViewEl,
+ }, {
+ name: "Keyboard focus should move to another focusable element outside " +
+ "of the treeView when there's nothing to focus on inside the row.",
+ action() {
+ synthesizeKey("KEY_Tab", { shiftKey: true });
+ },
+ state: { selected: "/B", active: null },
+ activeElement: treeViewEl.ownerDocument.documentElement,
+ }];
+
+ for (const test of tests) {
+ const { action, event, state, name } = test;
+
+ info(name);
+ if (event) {
+ const { type, options, el } = event;
+ Simulate[type](el, options);
+ } else if (action) {
+ action();
+ }
+
+ if (test.activeElement) {
+ is(treeViewEl.ownerDocument.activeElement, test.activeElement,
+ "Focus is set correctly.");
+ }
+
+ for (const key in state) {
+ is(tree.state[key], state[key], `${key} state is correct.`);
+ }
+ }
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree-view_02.html b/devtools/client/shared/components/test/chrome/test_tree-view_02.html
new file mode 100644
index 0000000000..77c5934a66
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree-view_02.html
@@ -0,0 +1,136 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that TreeView component filtering works with keyboard.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>TreeView component filtering keyboard test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ <link rel="stylesheet" href="chrome://devtools/content/shared/components/tree/TreeView.css" type="text/css">
+ <style>
+ .treeRow.hide {
+ display: none;
+ }
+ </style>
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = function() {
+ try {
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const {
+ Simulate,
+ findRenderedDOMComponentWithClass,
+ scryRenderedDOMComponentsWithClass,
+ } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const TreeView =
+ browserRequire("devtools/client/shared/components/tree/TreeView");
+
+ function testKeyboardInteraction(tree, treeViewEl, rows) {
+ // Expected tree when filtered (C is filtered)
+ //
+ // A
+ // |-- B
+ // `-- D
+ is(window.getComputedStyle(rows[1]).getPropertyValue("display"), "none",
+ "Row C must be hidden by default.");
+
+ const tests = [{
+ name: "Selected row must be set to the first row on initial focus. " +
+ "Keyboard focus must be set on TreeView's conatiner.",
+ action: () => {
+ Simulate.click(rows[0]);
+ },
+ activeElement: treeViewEl,
+ state: { selected: "/B" },
+ }, {
+ name: "Selecting next row must skip hidden row on ArrowDown.",
+ event: {
+ type: "keyDown",
+ el: treeViewEl,
+ options: { key: "ArrowDown" },
+ },
+ state: { selected: "/D" },
+ }, {
+ name: "Selecting previous row must be skip hidden row on ArrowUp.",
+ event: {
+ type: "keyDown",
+ el: treeViewEl,
+ options: { key: "ArrowUp" },
+ },
+ state: { selected: "/B" },
+ }];
+
+ for (const test of tests) {
+ const { action, event, state, name } = test;
+
+ info(name);
+ if (event) {
+ const { type, options, el } = event;
+ Simulate[type](el, options);
+ } else if (action) {
+ action();
+ }
+
+ for (const key in state) {
+ is(tree.state[key], state[key], `${key} state is correct.`);
+ }
+ }
+ }
+
+ info("Test hiding rows via decorator.");
+ const props = {
+ ...TEST_TREE_VIEW_INTERFACE,
+ decorator: {
+ getRowClass: ({ label }) => {
+ if (label === "C") {
+ return ["hide"];
+ }
+ return [];
+ }
+ }
+ };
+ let treeView = React.createElement(TreeView, props);
+ let tree = ReactDOM.render(treeView, document.body);
+ let treeViewEl = findRenderedDOMComponentWithClass(tree, "treeTable");
+ let rows = scryRenderedDOMComponentsWithClass(tree, "treeRow");
+
+ testKeyboardInteraction(tree, treeViewEl, rows);
+
+ // Remove TreeView component.
+ ReactDOM.unmountComponentAtNode(document.body);
+
+ info("Test hiding rows via onFilter.");
+ props.decorator = null;
+ props.onFilter = ({ label }) => {
+ console.log(`onFILTER ${label !== "C"}`)
+ return label !== "C";
+ };
+ treeView = React.createElement(TreeView, props);
+ tree = ReactDOM.render(treeView, document.body);
+ treeViewEl = findRenderedDOMComponentWithClass(tree, "treeTable");
+ rows = scryRenderedDOMComponentsWithClass(tree, "treeRow");
+
+ testKeyboardInteraction(tree, treeViewEl, rows);
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_01.html b/devtools/client/shared/components/test/chrome/test_tree_01.html
new file mode 100644
index 0000000000..0740c8957e
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_01.html
@@ -0,0 +1,68 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test trees get displayed with the items in correct order and at the correct
+depth.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ ok(React, "Should get React");
+ ok(Tree, "Should get Tree");
+
+ const t = Tree(TEST_TREE_INTERFACE);
+ ok(t, "Should be able to create Tree instances");
+
+ const tree = ReactDOM.render(t, window.document.body);
+ ok(tree, "Should be able to mount Tree instances");
+ isAccessibleTree(tree);
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "Should get the items rendered and indented as expected");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_02.html b/devtools/client/shared/components/test/chrome/test_tree_02.html
new file mode 100644
index 0000000000..f538965572
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_02.html
@@ -0,0 +1,49 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that collapsed subtrees aren't rendered.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ const tree = ReactDOM.render(Tree(TEST_TREE_INTERFACE), window.document.body);
+
+ isAccessibleTree(tree);
+ TEST_TREE.expanded = new Set("MNO".split(""));
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "Collapsed subtrees shouldn't be rendered");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_03.html b/devtools/client/shared/components/test/chrome/test_tree_03.html
new file mode 100644
index 0000000000..6ebefa1fb7
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_03.html
@@ -0,0 +1,50 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test Tree's autoExpandDepth.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ const tree = ReactDOM.render(Tree(Object.assign({}, TEST_TREE_INTERFACE, {
+ autoExpandDepth: 1
+ })), window.document.body);
+
+ isAccessibleTree(tree);
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "-C:false",
+ "-D:false",
+ "M:false",
+ "-N:false",
+ ], "Tree should be auto expanded one level");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_04.html b/devtools/client/shared/components/test/chrome/test_tree_04.html
new file mode 100644
index 0000000000..2213f72497
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_04.html
@@ -0,0 +1,133 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that we only render visible tree items.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ function getSpacerHeights() {
+ return {
+ top: document.querySelector(".tree > div:first-of-type").clientHeight,
+ bottom: document.querySelector(".tree > div:last-of-type").clientHeight,
+ };
+ }
+
+ const ITEM_HEIGHT = 3;
+
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const Tree = React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ const tree = ReactDOM.render(
+ Tree(Object.assign({}, TEST_TREE_INTERFACE, { itemHeight: ITEM_HEIGHT })),
+ window.document.body);
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ await setState(tree, {
+ height: 3 * ITEM_HEIGHT,
+ scroll: 1 * ITEM_HEIGHT
+ });
+
+ isAccessibleTree(tree);
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ ], "Tree should show the 2nd, 3rd, and 4th items + buffer of 1 item at each end");
+
+ let spacers = getSpacerHeights();
+ is(spacers.top, 0, "Top spacer has the correct height");
+ is(spacers.bottom, 10 * ITEM_HEIGHT, "Bottom spacer has the correct height");
+
+ await setState(tree, {
+ height: 2 * ITEM_HEIGHT,
+ scroll: 3 * ITEM_HEIGHT
+ });
+
+ isAccessibleTree(tree);
+ isRenderedTree(document.body.textContent, [
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ ], "Tree should show the 4th and 5th item + buffer of 1 item at each end");
+
+ spacers = getSpacerHeights();
+ is(spacers.top, 2 * ITEM_HEIGHT, "Top spacer has the correct height");
+ is(spacers.bottom, 9 * ITEM_HEIGHT, "Bottom spacer has the correct height");
+
+ // Set height to 2 items + 1 pixel at each end, scroll so that 4 items are visible
+ // (2 fully, 2 partially with 1 visible pixel)
+ await setState(tree, {
+ height: 2 * ITEM_HEIGHT + 2,
+ scroll: 3 * ITEM_HEIGHT - 1
+ });
+
+ isRenderedTree(document.body.textContent, [
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ ], "Tree should show the 4 visible items + buffer of 1 item at each end");
+
+ spacers = getSpacerHeights();
+ is(spacers.top, 1 * ITEM_HEIGHT, "Top spacer has the correct height");
+ is(spacers.bottom, 8 * ITEM_HEIGHT, "Bottom spacer has the correct height");
+
+ await setState(tree, {
+ height: 20 * ITEM_HEIGHT,
+ scroll: 0
+ });
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "Tree should show all rows");
+
+ spacers = getSpacerHeights();
+ is(spacers.top, 0, "Top spacer has zero height");
+ is(spacers.bottom, 0, "Bottom spacer has zero height");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_05.html b/devtools/client/shared/components/test/chrome/test_tree_05.html
new file mode 100644
index 0000000000..5427a1bd8d
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_05.html
@@ -0,0 +1,195 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test focusing with the Tree component.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const { Simulate } =
+ browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree =
+ createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ { onFocus: x => renderTree({ focused: x }) },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ const tree = renderTree();
+ const treeElem = document.querySelector(".tree");
+
+ isAccessibleTree(tree);
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ renderTree({ focused: "G" });
+ isAccessibleTree(tree, { hasActiveDescendant: true });
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:true",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "G should be focused");
+
+ // When tree gets focus by means other than mouse, do not set first node as
+ // focused node when there is already a focused node.
+ Simulate.focus(treeElem);
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:true",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "G should remain focused");
+
+ // Click the first tree node
+ document.querySelector(".tree-node").click();
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "A should be focused");
+
+ // Mouse down and mouse up events set tree "mouseDown" state correctly.
+ ok(!tree.state.mouseDown, "Mouse down state is not set.");
+ Simulate.mouseDown(document.querySelector(".tree-node"));
+ ok(tree.state.mouseDown, "Mouse down state is set.");
+ Simulate.mouseUp(document.querySelector(".tree-node"));
+ ok(!tree.state.mouseDown, "Mouse down state is reset.");
+
+ // Unset focused tree state.
+ renderTree({ focused: null });
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "No node should be focused");
+
+ // When tree gets focus while mouse is down, do not set first node as
+ // focused node.
+ Simulate.mouseDown(document.querySelector(".tree-node"));
+ Simulate.focus(treeElem);
+ Simulate.mouseUp(document.querySelector(".tree-node"));
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "No node should have been focused");
+
+ // When tree gets focus by means other than mouse, set first node as focused
+ // node if no nodes are focused.
+ Simulate.focus(treeElem);
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "A should be focused");
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_06.html b/devtools/client/shared/components/test/chrome/test_tree_06.html
new file mode 100644
index 0000000000..c8d1aa5e9f
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_06.html
@@ -0,0 +1,340 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test keyboard navigation with the Tree component.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const { Simulate } =
+ browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree =
+ createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ { onFocus: x => renderTree({ focused: x }) },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ const tree = renderTree();
+
+ isAccessibleTree(tree);
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ // UP ----------------------------------------------------------------------
+
+ info("Up to the previous sibling.");
+ renderTree({ focused: "L" });
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:true",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the UP, K should be focused.");
+
+ info("Up to the parent.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:true",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the UP, E should be focused.");
+
+ info("Try and navigate up, past the first item.");
+ renderTree({ focused: "A" });
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowUp" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the UP, A should be focused and we shouldn't have overflowed past it.");
+
+ // DOWN --------------------------------------------------------------------
+
+ info("Down to next sibling.");
+ renderTree({ focused: "K" });
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:true",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the DOWN, L should be focused.");
+
+ info("Down to parent's next sibling.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:true",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the DOWN, F should be focused.");
+
+ info("Try and go down past the last item.");
+ renderTree({ focused: "O" });
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowDown" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:true",
+ ], "After the DOWN, O should still be focused " +
+ "and we shouldn't have overflowed past it.");
+
+ // LEFT --------------------------------------------------------------------
+
+ info("Left to go to parent.");
+ renderTree({ focused: "L" });
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:true",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the LEFT, E should be focused.");
+
+ info("Left to collapse children.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:true",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the LEFT, E's children should be collapsed.");
+
+ // RIGHT -------------------------------------------------------------------
+
+ info("Right to expand children.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:true",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the RIGHT, E's children should be expanded again.");
+
+ info("Right on already expanded node.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:true",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the RIGHT on already expanded node, E should remain focused.");
+
+ info("Right when preventNavigationOnArrowRight is unset to go to next item.");
+ renderTree({ focused: "E", preventNavigationOnArrowRight: false });
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:true",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the RIGHT, K should be focused.");
+
+ // Check that keys are ignored if any modifier is present.
+ const keysWithModifier = [
+ { key: "ArrowDown", altKey: true },
+ { key: "ArrowDown", ctrlKey: true },
+ { key: "ArrowDown", metaKey: true },
+ { key: "ArrowDown", shiftKey: true },
+ ];
+ await forceRender(tree);
+
+ for (const key of keysWithModifier) {
+ Simulate.keyDown(document.querySelector(".tree"), key);
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:true",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After DOWN + (alt|ctrl|meta|shift), K should remain focused.");
+ }
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_07.html b/devtools/client/shared/components/test/chrome/test_tree_07.html
new file mode 100644
index 0000000000..2e763aaf20
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_07.html
@@ -0,0 +1,69 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that arrows get the open attribute when their item's children are expanded.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const React = browserRequire("devtools/client/shared/vendor/react");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const Tree =
+ React.createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ const treeProps = Object.assign({}, TEST_TREE_INTERFACE, {
+ renderItem: (item, depth, focused, arrow) => {
+ return dom.div(
+ {
+ id: item,
+ style: { marginLeft: depth * 16 + "px" }
+ },
+ arrow,
+ item
+ );
+ }
+ });
+ const tree = ReactDOM.render(Tree(treeProps), window.document.body);
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+ await forceRender(tree);
+
+ let arrows = document.querySelectorAll(".arrow");
+ for (const a of arrows) {
+ ok(a.classList.contains("open"), "Every arrow should be open.");
+ }
+
+ TEST_TREE.expanded = new Set();
+ await forceRender(tree);
+
+ arrows = document.querySelectorAll(".arrow");
+ for (const a of arrows) {
+ ok(!a.classList.contains("open"), "Every arrow should be closed.");
+ }
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_08.html b/devtools/client/shared/components/test/chrome/test_tree_08.html
new file mode 100644
index 0000000000..cfdff8090d
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_08.html
@@ -0,0 +1,61 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that when an item in the Tree component is clicked, it steals focus from
+other inputs.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ { onFocus: x => renderTree({ focused: x }) },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ const tree = renderTree();
+
+ const input = document.createElement("input");
+ document.body.appendChild(input);
+
+ input.focus();
+ is(document.activeElement, input, "The text input should be focused.");
+
+ document.querySelector(".tree-node").click();
+ await forceRender(tree);
+
+ isnot(document.activeElement, input,
+ "The input should have had it's focus stolen by clicking on a tree item.");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_09.html b/devtools/client/shared/components/test/chrome/test_tree_09.html
new file mode 100644
index 0000000000..4d6a1010b5
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_09.html
@@ -0,0 +1,85 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that when an item in the Tree component is expanded or collapsed the appropriate event handler fires.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const { Simulate } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ let numberOfExpands = 0;
+ let lastExpandedItem = null;
+
+ let numberOfCollapses = 0;
+ let lastCollapsedItem = null;
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ {
+ autoExpandDepth: 0,
+ onExpand: item => {
+ lastExpandedItem = item;
+ numberOfExpands++;
+ TEST_TREE.expanded.add(item);
+ },
+ onCollapse: item => {
+ lastCollapsedItem = item;
+ numberOfCollapses++;
+ TEST_TREE.expanded.delete(item);
+ },
+ onFocus: item => renderTree({ focused: item })
+ },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ const tree = renderTree({ focused: "A" });
+
+ is(lastExpandedItem, null);
+ is(lastCollapsedItem, null);
+
+ // Expand "A" via the keyboard and then let the component re-render.
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowRight" });
+ await forceRender(tree);
+
+ is(lastExpandedItem, "A", "Our onExpand callback should have been fired.");
+ is(numberOfExpands, 1);
+
+ // Now collapse "A" via the keyboard and then let the component re-render.
+ Simulate.keyDown(document.querySelector(".tree"), { key: "ArrowLeft" });
+ await forceRender(tree);
+
+ is(lastCollapsedItem, "A", "Our onCollapsed callback should have been fired.");
+ is(numberOfCollapses, 1);
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_10.html b/devtools/client/shared/components/test/chrome/test_tree_10.html
new file mode 100644
index 0000000000..7cda9e4348
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_10.html
@@ -0,0 +1,57 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that when an item in the Tree component is expanded or collapsed the appropriate event handler fires.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ { autoExpandDepth: 1 },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ renderTree({ focused: "A" });
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "-C:false",
+ "-D:false",
+ "M:false",
+ "-N:false",
+ ], "Should have auto-expanded one level.");
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_11.html b/devtools/client/shared/components/test/chrome/test_tree_11.html
new file mode 100644
index 0000000000..612a851018
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_11.html
@@ -0,0 +1,100 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that when an item in the Tree component is focused by arrow key, the view is scrolled.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ <style>
+ .tree {
+ height: 30px;
+ overflow: auto;
+ display: block;
+ }
+
+ .tree-node {
+ font-size: 10px;
+ height: 10px;
+ }
+ </style>
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+'use strict'
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const { Simulate } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree = createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ {
+ itemHeight: 10,
+ onFocus: item => renderTree({ focused: item })
+ },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ const tree = renderTree({ focused: "K" });
+
+ tree.setState({ scroll: 10 });
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:true",
+ "---L:false",
+ ], "Should render initial correctly");
+
+ await new Promise(resolve => {
+ const treeElem = document.querySelector(".tree");
+ treeElem.addEventListener("scroll", function onScroll() {
+ dumpn("Got scroll event");
+ treeElem.removeEventListener("scroll", onScroll);
+ resolve();
+ });
+
+ dumpn("Sending ArrowDown key");
+ Simulate.keyDown(treeElem, { key: "ArrowDown" });
+ });
+
+ dumpn("Forcing re-render");
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:true",
+ "--F:false",
+ ], "Should have scrolled down one");
+
+ } catch(e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_12.html b/devtools/client/shared/components/test/chrome/test_tree_12.html
new file mode 100644
index 0000000000..4bcf7ef705
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_12.html
@@ -0,0 +1,146 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test keyboard navigation/activation with the VirtualizedTree component.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const { Simulate } =
+ browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree =
+ createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ function renderTree(props) {
+ const treeProps = {
+ ...TEST_TREE_INTERFACE,
+ onFocus: x => renderTree({ focused: x }),
+ ...props
+ };
+
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ const tree = renderTree();
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ // Test Home key -----------------------------------------------------------
+
+ info("Press Home to move to the first node.");
+ renderTree({ focused: "L" });
+ Simulate.keyDown(document.querySelector(".tree"), { key: "Home" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the Home key, A should be focused.");
+
+ info("Press Home again when already on first node.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "Home" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "After the Home key again, A should still be focused.");
+
+ // Test End key ------------------------------------------------------------
+
+ info("Press End to move to the last node.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "End" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:true",
+ ], "After the End key, O should be focused.");
+
+ info("Press End again when already on last node.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "End" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ "--I:false",
+ "-D:false",
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:true",
+ ], "After the End key again, O should still be focused.");
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_13.html b/devtools/client/shared/components/test/chrome/test_tree_13.html
new file mode 100644
index 0000000000..183e144c82
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_13.html
@@ -0,0 +1,88 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test trees have the correct scroll position when they are resized.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <style>
+ .tree {
+ height: 50px;
+ overflow: auto;
+ display: block;
+ }
+
+ .tree-node {
+ font-size: 10px;
+ height: 10px;
+ }
+ </style>
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function() {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const { Simulate } =
+ browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree = createFactory(
+ browserRequire("devtools/client/shared/components/VirtualizedTree"));
+ const ITEM_HEIGHT = 10;
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ function renderTree(props) {
+ const treeProps = {
+ ...TEST_TREE_INTERFACE,
+ itemHeight: ITEM_HEIGHT,
+ onFocus: item => renderTree({ focused: item }),
+ ...props
+ };
+ return ReactDOM.render(Tree(treeProps), document.body);
+ }
+
+ const tree = renderTree({ focused: "L" });
+ const treeEl = tree.refs.tree;
+
+ is(tree.state.scroll, 0, "Scroll position should be 0 by default");
+ is(treeEl.scrollTop, 0, "Tree scrollTop should be 0 by default");
+
+ info(`Focus on the next node and scroll by ${ITEM_HEIGHT}`);
+ Simulate.keyDown(treeEl, { key: "ArrowDown" });
+ await forceRender(tree);
+
+ is(tree.state.scroll, ITEM_HEIGHT, `Scroll position should now be ${ITEM_HEIGHT}`);
+ is(treeEl.scrollTop, ITEM_HEIGHT,
+ `Tree scrollTop should now be ${ITEM_HEIGHT}`);
+
+ info("Simulate window resize along with scroll back to top");
+ treeEl.scrollTo({ left: 0, top: 0 });
+ window.dispatchEvent(new Event("resize"));
+ await forceRender(tree);
+
+ is(tree.state.scroll, ITEM_HEIGHT,
+ `Scroll position should remain at ${ITEM_HEIGHT}`);
+ is(treeEl.scrollTop, ITEM_HEIGHT,
+ `Tree scrollTop should remain at ${ITEM_HEIGHT}`);
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_14.html b/devtools/client/shared/components/test/chrome/test_tree_14.html
new file mode 100644
index 0000000000..d68d87d6c5
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_14.html
@@ -0,0 +1,245 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that Tree component has working keyboard interactions.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component keyboard test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function() {
+ try {
+ const { a, button, div } =
+ require("devtools/client/shared/vendor/react-dom-factories");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const {
+ Simulate,
+ findRenderedDOMComponentWithClass,
+ findRenderedDOMComponentWithTag,
+ } = browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree = createFactory(
+ browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ let gTree, gFocused, gActive;
+ function renderTree(props = {}) {
+ let toggle = true;
+ const treeProps = {
+ ...TEST_TREE_INTERFACE,
+ onFocus: x => {
+ gFocused = x;
+ renderTree({ focused: gFocused, active: gActive });
+ },
+ onActivate: x => {
+ gActive = x;
+ renderTree({ focused: gFocused, active: gActive });
+ },
+ renderItem: (x, depth, focused) => {
+ toggle = !toggle;
+ return toggle ?
+ (div(
+ {},
+ `${"-".repeat(depth)}${x}:${focused}`,
+ a({ href: "#" }, "Focusable 1"),
+ button({ }, "Focusable 2"),
+ "\n",
+ )
+ ) : `${"-".repeat(depth)}${x}:${focused}`;
+ },
+ ...props
+ };
+
+ gTree = ReactDOM.render(Tree(treeProps), document.body);
+ }
+
+ renderTree();
+ const els = {
+ get tree() {
+ // React will replace the tree via renderTree.
+ return findRenderedDOMComponentWithClass(gTree, "tree");
+ },
+ get anchor() {
+ // When tree node becomes active/inactive, it is replaced with a newly rendered
+ // one.
+ return findRenderedDOMComponentWithTag(gTree, "a");
+ },
+ get button() {
+ // When tree node becomes active/inactive, it is replaced with a newly rendered
+ // one.
+ return findRenderedDOMComponentWithTag(gTree, "button");
+ },
+ };
+
+ const tests = [{
+ name: "Test default Tree props. Keyboard focus is set to document body by default.",
+ props: { focused: undefined, active: undefined },
+ activeElement: document.body,
+ }, {
+ name: "Focused props must be set to the first node on initial focus. " +
+ "Keyboard focus should be set on the tree.",
+ action: () => els.tree.focus(),
+ activeElement: "tree",
+ props: { focused: "A" },
+ }, {
+ name: "Focused node should remain set even when the tree is blured. " +
+ "Keyboard focus should be set back to document body.",
+ action: () => els.tree.blur(),
+ props: { focused: "A" },
+ activeElement: document.body,
+ }, {
+ name: "Unset tree's focused prop.",
+ action: () => renderTree({ focused: null }),
+ props: { focused: null },
+ }, {
+ name: "Focused node must be re-set again to the first tree node on initial " +
+ "focus. Keyboard focus should be set on tree's conatiner.",
+ action: () => els.tree.focus(),
+ activeElement: "tree",
+ props: { focused: "A" },
+ }, {
+ name: "Focused node should be set as active on Enter.",
+ event: { type: "keyDown", el: "tree", options: { key: "Enter" }},
+ props: { focused: "A", active: "A" },
+ activeElement: "tree",
+ }, {
+ name: "Active node should be unset on Escape.",
+ event: { type: "keyDown", el: "tree", options: { key: "Escape" }},
+ props: { focused: "A", active: null },
+ }, {
+ name: "Focused node should be set as active on Space.",
+ event: { type: "keyDown", el: "tree", options: { key: " " }},
+ props: { focused: "A", active: "A" },
+ activeElement: "tree",
+ }, {
+ name: "Active node should unset when focus leaves the tree.",
+ action: () => els.tree.blur(),
+ props: { focused: "A", active: null },
+ activeElement: document.body,
+ }, {
+ name: "Keyboard focus should be set on tree's conatiner on focus.",
+ action: () => els.tree.focus(),
+ activeElement: "tree",
+ }, {
+ name: "Focused node should be updated to next on ArrowDown.",
+ event: { type: "keyDown", el: "tree", options: { key: "ArrowDown" }},
+ props: { focused: "M", active: null },
+ }, {
+ name: "Focused item should be set as active on Enter. Keyboard focus should be " +
+ "set on the first focusable element inside the tree node, if available.",
+ event: { type: "keyDown", el: "tree", options: { key: "Enter" }},
+ props: { focused: "M", active: "M" },
+ activeElement: "anchor",
+ }, {
+ name: "Keyboard focus should be set to next tabbable element inside the active " +
+ "node on Tab.",
+ action() {
+ synthesizeKey("KEY_Tab");
+ },
+ props: { focused: "M", active: "M" },
+ activeElement: "button",
+ }, {
+ name: "Keyboard focus should wrap inside the tree node when focused on last " +
+ "tabbable element.",
+ action() {
+ synthesizeKey("KEY_Tab");
+ },
+ props: { focused: "M", active: "M" },
+ activeElement: "anchor",
+ }, {
+ name: "Keyboard focus should wrap inside the tree node when focused on first " +
+ "tabbable element.",
+ action() {
+ synthesizeKey("KEY_Tab", { shiftKey: true });
+ },
+ props: { focused: "M", active: "M" },
+ activeElement: "button",
+ }, {
+ name: "Active tree node should be unset on Escape. Focus should move back to the " +
+ "tree container.",
+ event: { type: "keyDown", el: "tree", options: { key: "Escape" }},
+ props: { focused: "M", active: null },
+ activeElement: "tree",
+ }, {
+ name: "Focused node should be set as active on Space. Keyboard focus should be " +
+ "set on the first focusable element inside the tree node, if available.",
+ event: { type: "keyDown", el: "tree", options: { key: " " }},
+ props: { focused: "M", active: "M" },
+ activeElement: "anchor",
+ }, {
+ name: "Focused tree node should remain set even when the tree is blured. " +
+ "Keyboard focus should be set back to document body.",
+ action: () => document.activeElement.blur(),
+ props: { focused: "M", active: null, },
+ activeElement: document.body,
+ }, {
+ name: "Keyboard focus should be set on tree's conatiner on focus.",
+ action: () => els.tree.focus(),
+ props: { focused: "M", active: null },
+ activeElement: "tree",
+ }, {
+ name: "Focused tree node should be updated to previous on ArrowUp.",
+ event: { type: "keyDown", el: "tree", options: { key: "ArrowUp" }},
+ props: { focused: "A", active: null },
+ }, {
+ name: "Focused item should be set as active on Enter.",
+ event: { type: "keyDown", el: "tree", options: { key: "Enter" }},
+ props: { focused: "A", active: "A" },
+ activeElement: "tree",
+ }, {
+ name: "Keyboard focus should move to another focusable element outside of the " +
+ "tree when there's nothing to focus on inside the tree node.",
+ action() {
+ synthesizeKey("KEY_Tab", { shiftKey: true });
+ },
+ props: { focused: "A", active: null },
+ activeElement: document.documentElement,
+ }];
+
+ for (const test of tests) {
+ const { action, event, props, name } = test;
+
+ info(name);
+ if (event) {
+ const { type, options, el } = event;
+ const target = typeof el === "string" ? els[el] : el;
+ Simulate[type](target, options);
+ } else if (action) {
+ action();
+ }
+
+ await forceRender(gTree);
+
+ if (test.activeElement) {
+ const expected = typeof test.activeElement === "string" ?
+ els[test.activeElement] : test.activeElement;
+ // eslint-disable-next-line no-debugger
+ if (document.activeElement!==expected) {debugger;}
+ is(document.activeElement, expected, "Focus is set correctly.");
+ }
+
+ for (const key in props) {
+ is(gTree.props[key], props[key], `${key} prop is correct.`);
+ }
+ }
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_15.html b/devtools/client/shared/components/test/chrome/test_tree_15.html
new file mode 100644
index 0000000000..399e3d9ecd
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_15.html
@@ -0,0 +1,99 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test scroll position when focusing items in traversal but not rendered.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ <style>
+ .tree {
+ height: 30px;
+ overflow: auto;
+ display: block;
+ }
+
+ .tree-node {
+ font-size: 10px;
+ height: 10px;
+ }
+ </style>
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const { Simulate } =
+ browserRequire("devtools/client/shared/vendor/react-dom-test-utils");
+ const Tree =
+ createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ {
+ itemHeight: 10,
+ onFocus: item => renderTree({ focused: item })
+ },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ info("Test first focused.");
+ const tree = renderTree({ focused: "A" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ ], "Should render initial correctly");
+
+ info("Test last item focused when it was not yet rendered.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "End" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:true",
+ ], "Should render last focused item correctly");
+
+ info("Test first item focused when it was not yet rendered.");
+ Simulate.keyDown(document.querySelector(".tree"), { key: "Home" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:true",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ ], "Should render first focused item correctly");
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/chrome/test_tree_16.html b/devtools/client/shared/components/test/chrome/test_tree_16.html
new file mode 100644
index 0000000000..b70e63eade
--- /dev/null
+++ b/devtools/client/shared/components/test/chrome/test_tree_16.html
@@ -0,0 +1,145 @@
+<!-- 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/. -->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test scroll position when showing items both in traversal and/or rendered.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tree component test</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <link rel="stylesheet" href="chrome://devtools/skin/light-theme.css" type="text/css">
+ <style>
+ .tree {
+ height: 30px;
+ overflow: auto;
+ display: block;
+ }
+
+ .tree-node {
+ font-size: 10px;
+ height: 10px;
+ }
+ </style>
+</head>
+<body>
+<pre id="test">
+<script src="head.js" type="application/javascript"></script>
+<script type="application/javascript">
+
+"use strict";
+
+window.onload = async function () {
+ try {
+ const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
+ const { createFactory } = browserRequire("devtools/client/shared/vendor/react");
+ const Tree =
+ createFactory(browserRequire("devtools/client/shared/components/VirtualizedTree"));
+
+ TEST_TREE.expanded = new Set("ABCDEFGHIJKLMNO".split(""));
+
+ function renderTree(props) {
+ const treeProps = Object.assign({},
+ TEST_TREE_INTERFACE,
+ {
+ itemHeight: 10,
+ onFocus: item => renderTree({ shown: item })
+ },
+ props
+ );
+ return ReactDOM.render(Tree(treeProps), window.document.body);
+ }
+
+ info("Test first shown.");
+ const tree = renderTree({ shown: "A" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ ], "Should render initial correctly");
+
+ info("Test last as shown when it was not yet rendered.");
+ renderTree({ shown: "O" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "--J:false",
+ "M:false",
+ "-N:false",
+ "--O:false",
+ ], "Should render shown item correctly");
+
+ info("Test first item shown when it's not first rendered.");
+ renderTree({ shown: "A" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "A:false",
+ "-B:false",
+ "--E:false",
+ "---K:false",
+ ], "Should render shown item correctly");
+
+ info("Test mid item shown when it's not first rendered.");
+ renderTree({ shown: "G" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "---K:false",
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ ], "Should render shown item correctly");
+
+ info("Test mid item shown when it's already rendered.");
+ renderTree({ shown: "C" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ ], "Should render shown item correctly");
+
+ info("Test item that is not in traversal.");
+ renderTree({ shown: "Z" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ ], "Should render without changes");
+
+ info("Test item that is already shown.");
+ renderTree({ shown: "F" });
+ await forceRender(tree);
+
+ isRenderedTree(document.body.textContent, [
+ "---L:false",
+ "--F:false",
+ "--G:false",
+ "-C:false",
+ "--H:false",
+ ], "Should render without changes");
+ } catch (e) {
+ ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e));
+ } finally {
+ SimpleTest.finish();
+ }
+};
+</script>
+</pre>
+</body>
+</html>
diff --git a/devtools/client/shared/components/test/node/.eslintrc.js b/devtools/client/shared/components/test/node/.eslintrc.js
new file mode 100644
index 0000000000..ffb3e70473
--- /dev/null
+++ b/devtools/client/shared/components/test/node/.eslintrc.js
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+module.exports = {
+ env: {
+ jest: true,
+ },
+};
diff --git a/devtools/client/shared/components/test/node/__mocks__/Services.js b/devtools/client/shared/components/test/node/__mocks__/Services.js
new file mode 100644
index 0000000000..14581e8fda
--- /dev/null
+++ b/devtools/client/shared/components/test/node/__mocks__/Services.js
@@ -0,0 +1,14 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ appinfo: "",
+ prefs: {
+ getBoolPref(name, defaultVal) {
+ return defaultVal;
+ },
+ },
+};
diff --git a/devtools/client/shared/components/test/node/__mocks__/object-front.js b/devtools/client/shared/components/test/node/__mocks__/object-front.js
new file mode 100644
index 0000000000..def182111d
--- /dev/null
+++ b/devtools/client/shared/components/test/node/__mocks__/object-front.js
@@ -0,0 +1,55 @@
+/* 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/>. */
+
+"use strict";
+
+function ObjectFront(grip, overrides) {
+ return {
+ grip,
+ enumEntries() {
+ return Promise.resolve(
+ this.getIterator({
+ ownProperties: {},
+ })
+ );
+ },
+ enumProperties(options) {
+ return Promise.resolve(
+ this.getIterator({
+ ownProperties: {},
+ })
+ );
+ },
+ enumSymbols() {
+ return Promise.resolve(
+ this.getIterator({
+ ownSymbols: [],
+ })
+ );
+ },
+ enumPrivateProperties() {
+ return Promise.resolve(
+ this.getIterator({
+ privateProperties: [],
+ })
+ );
+ },
+ getPrototype() {
+ return Promise.resolve({
+ prototype: {},
+ });
+ },
+ // Declared here so we can override it.
+ getIterator(res) {
+ return {
+ slice(start, count) {
+ return Promise.resolve(res);
+ },
+ };
+ },
+ ...overrides,
+ };
+}
+
+module.exports = ObjectFront;
diff --git a/devtools/client/shared/components/test/node/__mocks__/string-front.js b/devtools/client/shared/components/test/node/__mocks__/string-front.js
new file mode 100644
index 0000000000..d743f79e8b
--- /dev/null
+++ b/devtools/client/shared/components/test/node/__mocks__/string-front.js
@@ -0,0 +1,15 @@
+/* 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/>. */
+
+"use strict";
+
+function LongStringFront(grip, overrides) {
+ return {
+ grip,
+ substring: async () => "",
+ ...overrides,
+ };
+}
+
+module.exports = { LongStringFront };
diff --git a/devtools/client/shared/components/test/node/babel.config.js b/devtools/client/shared/components/test/node/babel.config.js
new file mode 100644
index 0000000000..2a95c9f71c
--- /dev/null
+++ b/devtools/client/shared/components/test/node/babel.config.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+module.exports = {
+ plugins: [
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-optional-chaining",
+ "@babel/plugin-proposal-nullish-coalescing-operator",
+ "transform-amd-to-commonjs",
+ ],
+};
diff --git a/devtools/client/shared/components/test/node/components/__snapshots__/tree.test.js.snap b/devtools/client/shared/components/test/node/components/__snapshots__/tree.test.js.snap
new file mode 100644
index 0000000000..aad7d06189
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/__snapshots__/tree.test.js.snap
@@ -0,0 +1,1171 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Tree Don't auto expand root with very large number of children 1`] = `
+Array [
+ "key-A",
+ "key-B",
+ "key-E",
+ "key-F",
+ "key-G",
+ "key-C",
+ "key-H",
+ "key-I",
+ "key-D",
+ "key-J",
+ "key-M",
+ "key-N",
+]
+`;
+
+exports[`Tree active item - focus is inside the tree node and then blur 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L anchor]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - focus is inside the tree node when possible 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L anchor]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - focus is inside the tree node when possible 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L anchor]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - navigate inside the tree node 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L anchor]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - navigate inside the tree node 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L anchor]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - navigate inside the tree node 3`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L anchor]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - renders as expected when clicking away 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | [G]
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - renders as expected when clicking away 2`] = `
+"
+▶︎ [A]
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - renders as expected when moving away with keyboard 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - renders as expected when tree blurs 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | [G]
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - renders as expected when tree blurs 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | [G]
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - renders as expected when using keyboard and Enter 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree active item - renders as expected when using keyboard and Space 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree calls shouldItemUpdate when provided 1`] = `
+"
+▶︎ A
+▶︎ M
+"
+`;
+
+exports[`Tree calls shouldItemUpdate when provided 2`] = `
+"
+▶︎ A
+▶︎ M
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 3`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 4`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 5`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 6`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 7`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 8`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 9`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 10`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 11`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 12`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 13`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 14`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 15`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 16`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree ignores key strokes when pressing modifiers 17`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders arrows as expected when nodes are collapsed 1`] = `
+"
+▶︎ A
+▶︎ M
+"
+`;
+
+exports[`Tree renders arrows as expected when nodes are expanded 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected navigating down with keyboard on last node 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | [O]
+"
+`;
+
+exports[`Tree renders as expected navigating down with keyboard on last node 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | [O]
+"
+`;
+
+exports[`Tree renders as expected navigating up with the keyboard on a root 1`] = `
+"
+▼ [A]
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected navigating up with the keyboard on a root 2`] = `
+"
+▼ [A]
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected navigating with arrows on unexpandable roots 1`] = `
+"
+ [A]
+ M
+"
+`;
+
+exports[`Tree renders as expected navigating with arrows on unexpandable roots 2`] = `
+"
+ A
+ [M]
+"
+`;
+
+exports[`Tree renders as expected navigating with arrows on unexpandable roots 3`] = `
+"
+ [A]
+ M
+"
+`;
+
+exports[`Tree renders as expected when given a focused item 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | [G]
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when given a focused item 2`] = `
+"
+▶︎ [A]
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when given a focused item 3`] = `
+"
+▼ [A]
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when given a focused item 4`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating down with the keyboard 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | [K]
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating down with the keyboard 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating down with the keyboard 3`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | L
+| | [F]
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating up with the keyboard 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating up with the keyboard 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | [K]
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating up with the keyboard 3`] = `
+"
+▼ A
+| ▼ B
+| | ▼ [E]
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 1`] = `
+"
+▶︎ A
+▶︎ [M]
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 2`] = `
+"
+▶︎ [A]
+▶︎ M
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 3`] = `
+"
+▶︎ [A]
+▶︎ M
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 4`] = `
+"
+▶︎ A
+▶︎ [M]
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 5`] = `
+"
+▶︎ A
+▶︎ [M]
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 6`] = `
+"
+▶︎ A
+▼ [M]
+| ▶︎ N
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 7`] = `
+"
+▶︎ A
+▼ M
+| ▶︎ [N]
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 8`] = `
+"
+▶︎ A
+▼ M
+| ▶︎ [N]
+"
+`;
+
+exports[`Tree renders as expected when navigating with home/end 9`] = `
+"
+▶︎ [A]
+▼ M
+| ▶︎ N
+"
+`;
+
+exports[`Tree renders as expected when navigating with left arrows on roots 1`] = `
+"
+▶︎ A
+▶︎ [M]
+"
+`;
+
+exports[`Tree renders as expected when navigating with left arrows on roots 2`] = `
+"
+▶︎ [A]
+▶︎ M
+"
+`;
+
+exports[`Tree renders as expected when navigating with left arrows on roots 3`] = `
+"
+▶︎ [A]
+▶︎ M
+"
+`;
+
+exports[`Tree renders as expected when navigating with right/left arrows 1`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | K
+| | | [L]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating with right/left arrows 2`] = `
+"
+▼ A
+| ▼ B
+| | ▼ [E]
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating with right/left arrows 3`] = `
+"
+▼ A
+| ▼ B
+| | ▶︎ [E]
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating with right/left arrows 4`] = `
+"
+▼ A
+| ▼ B
+| | ▼ [E]
+| | | K
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when navigating with right/left arrows 5`] = `
+"
+▼ A
+| ▼ B
+| | ▼ E
+| | | [K]
+| | | L
+| | F
+| | G
+| ▼ C
+| | H
+| | I
+| ▼ D
+| | J
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree renders as expected when passed autoDepth:1 1`] = `
+"
+▼ A
+| ▶︎ B
+| ▶︎ C
+| ▶︎ D
+▼ M
+| ▶︎ N
+"
+`;
+
+exports[`Tree renders as expected with collapsed nodes 1`] = `
+"
+▶︎ A
+▼ M
+| ▼ N
+| | O
+"
+`;
+
+exports[`Tree uses isExpandable prop if it exists to render tree nodes 1`] = `
+"
+▶︎ A
+ M
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/basic.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/basic.test.js.snap
new file mode 100644
index 0000000000..9db3eadc93
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/basic.test.js.snap
@@ -0,0 +1,63 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - renders renders as expected 1`] = `
+"
+▶︎ {…}
+"
+`;
+
+exports[`ObjectInspector - renders renders as expected 2`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", … }
+"
+`;
+
+exports[`ObjectInspector - renders renders as expected 3`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+"
+`;
+
+exports[`ObjectInspector - renders renders as expected 4`] = `
+"
+▶︎ {…}
+"
+`;
+
+exports[`ObjectInspector - renders renders as expected when not provided a name 1`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", … }
+"
+`;
+
+exports[`ObjectInspector - renders renders block nodes as expected 1`] = `
+"
+▼ ☲ Block
+| a: 30
+| b: 32
+"
+`;
+
+exports[`ObjectInspector - renders renders objects as expected when provided a name 1`] = `
+"
+▶︎ myproperty: Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", … }
+"
+`;
+
+exports[`ObjectInspector - renders renders primitives as expected when provided a name 1`] = `
+"
+ myproperty: 42
+"
+`;
+
+exports[`ObjectInspector - renders updates when the root changes 1`] = `
+"
+[ ▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
+"
+`;
+
+exports[`ObjectInspector - renders updates when the root changes 2`] = `
+"
+[ ▶︎ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" } ]
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/classnames.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/classnames.test.js.snap
new file mode 100644
index 0000000000..81cc9f7028
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/classnames.test.js.snap
@@ -0,0 +1,351 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - classnames has the expected class 1`] = `
+<div
+ className="tree object-inspector"
+ onBlur={[Function]}
+ onFocus={[Function]}
+ onKeyDown={[Function]}
+ onKeyPress={[Function]}
+ onKeyUp={[Function]}
+ role="tree"
+ style={Object {}}
+ tabIndex="0"
+>
+ <TreeNode
+ active={false}
+ depth={0}
+ expanded={false}
+ focused={false}
+ id="root"
+ index={0}
+ isExpandable={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ }
+ }
+ key="root-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-level={1}
+ className="tree-node"
+ data-expandable={false}
+ id="root"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={null}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={0}
+ evaluations={Map {}}
+ expanded={false}
+ expandedPaths={Set {}}
+ focused={false}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <span
+ className="object-label"
+ >
+ root
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-number"
+ title={null}
+ >
+ 42
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+</div>
+`;
+
+exports[`ObjectInspector - classnames has the inline class when inline prop is true 1`] = `
+<div
+ className="tree object-inspector inline"
+ onBlur={[Function]}
+ onFocus={[Function]}
+ onKeyDown={[Function]}
+ onKeyPress={[Function]}
+ onKeyUp={[Function]}
+ role="tree"
+ style={Object {}}
+ tabIndex="0"
+>
+ <TreeNode
+ active={false}
+ depth={0}
+ expanded={false}
+ focused={false}
+ id="root"
+ index={0}
+ isExpandable={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ }
+ }
+ key="root-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-level={1}
+ className="tree-node"
+ data-expandable={false}
+ id="root"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={null}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={0}
+ evaluations={Map {}}
+ expanded={false}
+ expandedPaths={Set {}}
+ focused={false}
+ inline={true}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <span
+ className="object-label"
+ >
+ root
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-number"
+ title={null}
+ >
+ 42
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+</div>
+`;
+
+exports[`ObjectInspector - classnames has the nowrap class when disableWrap prop is true 1`] = `
+<div
+ className="tree object-inspector nowrap"
+ onBlur={[Function]}
+ onFocus={[Function]}
+ onKeyDown={[Function]}
+ onKeyPress={[Function]}
+ onKeyUp={[Function]}
+ role="tree"
+ style={Object {}}
+ tabIndex="0"
+>
+ <TreeNode
+ active={false}
+ depth={0}
+ expanded={false}
+ focused={false}
+ id="root"
+ index={0}
+ isExpandable={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ }
+ }
+ key="root-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-level={1}
+ className="tree-node"
+ data-expandable={false}
+ id="root"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={null}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={0}
+ disableWrap={true}
+ evaluations={Map {}}
+ expanded={false}
+ expandedPaths={Set {}}
+ focused={false}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": 42,
+ },
+ "name": "root",
+ "path": "root",
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <span
+ className="object-label"
+ >
+ root
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-number"
+ title={null}
+ >
+ 42
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+</div>
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/entries.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/entries.test.js.snap
new file mode 100644
index 0000000000..5208db3ebb
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/entries.test.js.snap
@@ -0,0 +1,94 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - entries calls ObjectFront.enumEntries when expected 1`] = `
+"
+▼ Map(11)
+| ▶︎ <entries>
+"
+`;
+
+exports[`ObjectInspector - entries calls ObjectFront.enumEntries when expected 2`] = `
+"
+ ▼ Map(11)
+[ | ▼ <entries> ]
+ | | ▶︎ 0: \\"key-0\\" → \\"value-0\\"
+ | | ▶︎ 1: \\"key-1\\" → \\"value-1\\"
+ | | ▶︎ 2: \\"key-2\\" → \\"value-2\\"
+ | | ▶︎ 3: \\"key-3\\" → \\"value-3\\"
+ | | ▶︎ 4: \\"key-4\\" → \\"value-4\\"
+ | | ▶︎ 5: \\"key-5\\" → \\"value-5\\"
+ | | ▶︎ 6: \\"key-6\\" → \\"value-6\\"
+ | | ▶︎ 7: \\"key-7\\" → \\"value-7\\"
+ | | ▶︎ 8: \\"key-8\\" → \\"value-8\\"
+ | | ▶︎ 9: \\"key-9\\" → \\"value-9\\"
+ | | ▶︎ 10: \\"key-10\\" → \\"value-10\\"
+"
+`;
+
+exports[`ObjectInspector - entries calls ObjectFront.enumEntries when expected 3`] = `
+"
+ ▼ Map(11)
+[ | ▶︎ <entries> ]
+"
+`;
+
+exports[`ObjectInspector - entries calls ObjectFront.enumEntries when expected 4`] = `
+"
+ ▼ Map(11)
+[ | ▼ <entries> ]
+ | | ▶︎ 0: \\"key-0\\" → \\"value-0\\"
+ | | ▶︎ 1: \\"key-1\\" → \\"value-1\\"
+ | | ▶︎ 2: \\"key-2\\" → \\"value-2\\"
+ | | ▶︎ 3: \\"key-3\\" → \\"value-3\\"
+ | | ▶︎ 4: \\"key-4\\" → \\"value-4\\"
+ | | ▶︎ 5: \\"key-5\\" → \\"value-5\\"
+ | | ▶︎ 6: \\"key-6\\" → \\"value-6\\"
+ | | ▶︎ 7: \\"key-7\\" → \\"value-7\\"
+ | | ▶︎ 8: \\"key-8\\" → \\"value-8\\"
+ | | ▶︎ 9: \\"key-9\\" → \\"value-9\\"
+ | | ▶︎ 10: \\"key-10\\" → \\"value-10\\"
+"
+`;
+
+exports[`ObjectInspector - entries renders Object with entries as expected 1`] = `
+"
+▼ Map { Symbol(\\"a\\") → \\"value-a\\", Symbol(\\"b\\") → \\"value-b\\" }
+| size: 2
+| ▼ <entries>
+| | ▼ 0: \\"key-0\\" → \\"value-0\\"
+| | | <key>: \\"key-0\\"
+| | | <value>: \\"value-0\\"
+| | ▼ 1: \\"key-1\\" → \\"value-1\\"
+| | | <key>: \\"key-1\\"
+| | | <value>: \\"value-1\\"
+| | ▼ 2: \\"key-2\\" → \\"value-2\\"
+| | | <key>: \\"key-2\\"
+| | | <value>: \\"value-2\\"
+| | ▼ 3: \\"key-3\\" → \\"value-3\\"
+| | | <key>: \\"key-3\\"
+| | | <value>: \\"value-3\\"
+| | ▼ 4: \\"key-4\\" → \\"value-4\\"
+| | | <key>: \\"key-4\\"
+| | | <value>: \\"value-4\\"
+| | ▼ 5: \\"key-5\\" → \\"value-5\\"
+| | | <key>: \\"key-5\\"
+| | | <value>: \\"value-5\\"
+| | ▼ 6: \\"key-6\\" → \\"value-6\\"
+| | | <key>: \\"key-6\\"
+| | | <value>: \\"value-6\\"
+| | ▼ 7: \\"key-7\\" → \\"value-7\\"
+| | | <key>: \\"key-7\\"
+| | | <value>: \\"value-7\\"
+| | ▼ 8: \\"key-8\\" → \\"value-8\\"
+| | | <key>: \\"key-8\\"
+| | | <value>: \\"value-8\\"
+| | ▼ 9: \\"key-9\\" → \\"value-9\\"
+| | | <key>: \\"key-9\\"
+| | | <value>: \\"value-9\\"
+| | ▼ 10: \\"key-10\\" → \\"value-10\\"
+| | | <key>: \\"key-10\\"
+| | | <value>: \\"value-10\\"
+| ▼ <prototype>: Object { … }
+| | <prototype>: Object { }
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/expand.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/expand.test.js.snap
new file mode 100644
index 0000000000..3cb8b39dee
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/expand.test.js.snap
@@ -0,0 +1,175 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - state does not expand if the user selected some text 1`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state does not expand if the user selected some text 2`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state does not handle actors when client does not have releaseActor function 1`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state does not handle actors when client does not have releaseActor function 2`] = `
+"
+[ ▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
+ | ▶︎ <prototype>: Object { }
+ ▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state does not handle actors when client does not have releaseActor function 3`] = `
+"
+ ▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+[ | ▼ <prototype>: Object { } ]
+ | | ▶︎ <prototype>: Object { }
+ ▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state does not throw when expanding a block node 1`] = `
+"
+▶︎ ☲ Block
+▶︎ Proxy: Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state does not throw when expanding a block node 2`] = `
+"
+[ ▼ ☲ Block ]
+ | a: 30
+ | b: 32
+ ▶︎ Proxy: Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state expanding a getter returning a longString does not throw 1`] = `
+"
+▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+| ▼ baseVal: \\"data:image/png;base64,initial<<<<\\"
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state expands if user selected some text and clicked the arrow 1`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state expands if user selected some text and clicked the arrow 2`] = `
+"
+[ ▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
+ | a: 1
+ | Symbol(): \\"hello\\"
+ | ▶︎ <prototype>: Object { … }
+ ▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 1`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 2`] = `
+"
+[ ▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
+ | a: 1
+ | Symbol(): \\"hello\\"
+ | ▶︎ <prototype>: Object { … }
+ ▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 3`] = `
+"
+[ ▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
+ ▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 4`] = `
+"
+ ▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+[ ▼ Proxy { <target>: {…}, <handler>: (3) […] } ]
+ | ▶︎ <target>: Object { … }
+ | ▶︎ <handler>: Array(3) [ … ]
+"
+`;
+
+exports[`ObjectInspector - state has the expected expandedPaths state when clicking nodes 5`] = `
+"
+[ ▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
+ | a: 1
+ | Symbol(): \\"hello\\"
+ | ▶︎ <prototype>: Object { … }
+ ▼ Proxy { <target>: {…}, <handler>: (3) […] }
+ | ▶︎ <target>: Object { … }
+ | ▶︎ <handler>: Array(3) [ … ]
+"
+`;
+
+exports[`ObjectInspector - state has the expected state when expanding a node 1`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected state when expanding a node 2`] = `
+"
+[ ▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … } ]
+ | ▶︎ <prototype>: Object { }
+ ▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected state when expanding a node 3`] = `
+"
+ ▼ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+[ | ▼ <prototype>: Object { } ]
+ | | ▶︎ <prototype>: Object { }
+ ▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected state when expanding a proxy node 1`] = `
+"
+▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+▶︎ Proxy { <target>: {…}, <handler>: (3) […] }
+"
+`;
+
+exports[`ObjectInspector - state has the expected state when expanding a proxy node 2`] = `
+"
+ ▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+[ ▼ Proxy ]
+ | ▶︎ <target>: Object { … }
+ | ▶︎ <handler>: Array(3) [ … ]
+"
+`;
+
+exports[`ObjectInspector - state has the expected state when expanding a proxy node 3`] = `
+"
+ ▶︎ Object { p0: \\"0\\", p1: \\"1\\", p2: \\"2\\", p3: \\"3\\", p4: \\"4\\", p5: \\"5\\", p6: \\"6\\", p7: \\"7\\", p8: \\"8\\", p9: \\"9\\", … }
+ ▼ Proxy
+ | ▶︎ <target>: Object { … }
+[ | ▼ <handler>: (3) […] ]
+ | | <prototype>: Object { }
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/getter-setter.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/getter-setter.test.js.snap
new file mode 100644
index 0000000000..86ededb690
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/getter-setter.test.js.snap
@@ -0,0 +1,51 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - getters & setters onInvokeGetterButtonClick + getter & setter 1`] = `
+"
+▼ root
+| x: (>>)
+| ▶︎ <get x()>: function x()
+| ▶︎ <set x()>: function x()
+"
+`;
+
+exports[`ObjectInspector - getters & setters onInvokeGetterButtonClick + getter 1`] = `
+"
+▼ root
+| x: (>>)
+| ▶︎ <get x()>: function x()
+"
+`;
+
+exports[`ObjectInspector - getters & setters onInvokeGetterButtonClick + setter 1`] = `
+"
+▼ root
+| x: Setter
+| ▶︎ <set x()>: function x()
+"
+`;
+
+exports[`ObjectInspector - getters & setters renders getters and setters as expected 1`] = `
+"
+▼ root
+| x: Getter & Setter
+| ▶︎ <get x()>: function x()
+| ▶︎ <set x()>: function x()
+"
+`;
+
+exports[`ObjectInspector - getters & setters renders getters as expected 1`] = `
+"
+▼ root
+| x: Getter
+| ▶︎ <get x()>: function x()
+"
+`;
+
+exports[`ObjectInspector - getters & setters renders setters as expected 1`] = `
+"
+▼ root
+| x: Setter
+| ▶︎ <set x()>: function x()
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/keyboard-navigation.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/keyboard-navigation.test.js.snap
new file mode 100644
index 0000000000..8998ce3aff
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/keyboard-navigation.test.js.snap
@@ -0,0 +1,55 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - keyboard navigation works as expected 1`] = `
+"
+▶︎ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" }
+"
+`;
+
+exports[`ObjectInspector - keyboard navigation works as expected 2`] = `
+"
+[ ▶︎ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" } ]
+"
+`;
+
+exports[`ObjectInspector - keyboard navigation works as expected 3`] = `
+"
+[ ▼ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" } ]
+ | <prototype>: Object { }
+"
+`;
+
+exports[`ObjectInspector - keyboard navigation works as expected 4`] = `
+"
+ ▼ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" }
+[ | <prototype>: Object { } ]
+"
+`;
+
+exports[`ObjectInspector - keyboard navigation works as expected 5`] = `
+"
+[ ▼ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" } ]
+ | <prototype>: Object { }
+"
+`;
+
+exports[`ObjectInspector - keyboard navigation works as expected 6`] = `
+"
+ ▼ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" }
+[ | <prototype>: Object { } ]
+"
+`;
+
+exports[`ObjectInspector - keyboard navigation works as expected 7`] = `
+"
+[ ▼ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" } ]
+ | <prototype>: Object { }
+"
+`;
+
+exports[`ObjectInspector - keyboard navigation works as expected 8`] = `
+"
+▼ Object { a: \\"a\\", b: \\"b\\", c: \\"c\\" }
+| <prototype>: Object { }
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/properties.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/properties.test.js.snap
new file mode 100644
index 0000000000..3bb9e517a8
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/properties.test.js.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - properties renders uninitialized bindings 1`] = `
+"
+ someFoo: (uninitialized)
+"
+`;
+
+exports[`ObjectInspector - properties renders unmapped bindings 1`] = `
+"
+ someFoo: (unmapped)
+"
+`;
+
+exports[`ObjectInspector - properties renders unscoped bindings 1`] = `
+"
+ someFoo: (unscoped)
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/proxy.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/proxy.test.js.snap
new file mode 100644
index 0000000000..d48a6e058d
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/proxy.test.js.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - Proxy renders Proxy as expected 1`] = `
+"
+▼ Proxy { <target>: {…}, <handler>: (3) […] }
+| ▶︎ <target>: Object { … }
+| ▶︎ <handler>: Array(3) [ … ]
+"
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/window.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/window.test.js.snap
new file mode 100644
index 0000000000..fcd2cc3693
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/__snapshots__/window.test.js.snap
@@ -0,0 +1,2119 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ObjectInspector - dimTopLevelWindow renders collapsed top-level window when dimTopLevelWindow =false 1`] = `
+<Provider
+ store={
+ Object {
+ "dispatch": [Function],
+ "getState": [Function],
+ "replaceReducer": [Function],
+ "subscribe": [Function],
+ Symbol(observable): [Function],
+ }
+ }
+>
+ <Component
+ autoExpandDepth={0}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <Connect(ObjectInspector)
+ autoExpandDepth={0}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <ObjectInspector
+ addWatchpoint={[Function]}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ evaluations={Map {}}
+ expandedPaths={Set {}}
+ invokeGetter={[Function]}
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ removeWatchpoint={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ >
+ <Tree
+ autoExpandAll={true}
+ autoExpandDepth={0}
+ className="object-inspector"
+ getChildren={[Function]}
+ getKey={[Function]}
+ getParent={[Function]}
+ getRoots={[Function]}
+ isExpandable={[Function]}
+ isExpanded={[Function]}
+ onActivate={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ onFocus={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ className="tree object-inspector"
+ onBlur={[Function]}
+ onFocus={[Function]}
+ onKeyDown={[Function]}
+ onKeyPress={[Function]}
+ onKeyUp={[Function]}
+ role="tree"
+ style={Object {}}
+ tabIndex="0"
+ >
+ <TreeNode
+ active={false}
+ depth={0}
+ expanded={false}
+ focused={false}
+ id="window"
+ index={0}
+ isExpandable={true}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ key="window-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-expanded={false}
+ aria-level={1}
+ className="tree-node"
+ data-expandable={true}
+ id="window"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={
+ <ArrowExpander
+ expanded={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ />
+ }
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={0}
+ evaluations={Map {}}
+ expanded={false}
+ expandedPaths={Set {}}
+ focused={false}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <ArrowExpander
+ expanded={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ >
+ <button
+ className="arrow"
+ title="Expand"
+ />
+ </ArrowExpander>
+ <span
+ className="object-label"
+ >
+ window
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-Window"
+ data-link-actor-id="server0.conn0.windowGlobal2147483651/obj35"
+ title={null}
+ >
+ <span
+ className="objectTitle"
+ >
+ Window
+ </span>
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+ </div>
+ </Tree>
+ </ObjectInspector>
+ </Connect(ObjectInspector)>
+ </Component>
+</Provider>
+`;
+
+exports[`ObjectInspector - dimTopLevelWindow renders sub-level window 1`] = `
+<Provider
+ store={
+ Object {
+ "dispatch": [Function],
+ "getState": [Function],
+ "replaceReducer": [Function],
+ "subscribe": [Function],
+ Symbol(observable): [Function],
+ }
+ }
+>
+ <Component
+ autoExpandDepth={0}
+ dimTopLevelWindow={true}
+ injectWaitService={true}
+ roots={
+ Array [
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <Connect(ObjectInspector)
+ autoExpandDepth={0}
+ dimTopLevelWindow={true}
+ injectWaitService={true}
+ roots={
+ Array [
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <ObjectInspector
+ addWatchpoint={[Function]}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expandedPaths={
+ Set {
+ "root",
+ }
+ }
+ injectWaitService={true}
+ invokeGetter={[Function]}
+ loadedProperties={
+ Map {
+ "root" => Object {},
+ }
+ }
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ removeWatchpoint={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ >
+ <Tree
+ autoExpandAll={true}
+ autoExpandDepth={0}
+ className="object-inspector"
+ focused={
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ getChildren={[Function]}
+ getKey={[Function]}
+ getParent={[Function]}
+ getRoots={[Function]}
+ isExpandable={[Function]}
+ isExpanded={[Function]}
+ onActivate={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ onFocus={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-activedescendant="root"
+ className="tree object-inspector"
+ onBlur={[Function]}
+ onFocus={[Function]}
+ onKeyDown={[Function]}
+ onKeyPress={[Function]}
+ onKeyUp={[Function]}
+ role="tree"
+ style={Object {}}
+ tabIndex="0"
+ >
+ <TreeNode
+ active={false}
+ depth={0}
+ expanded={true}
+ focused={true}
+ id="root"
+ index={0}
+ isExpandable={true}
+ item={
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ key="root-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-expanded={true}
+ aria-level={1}
+ className="tree-node focused"
+ data-expandable={true}
+ id="root"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={
+ <ArrowExpander
+ expanded={true}
+ item={
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ />
+ }
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={0}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expanded={true}
+ expandedPaths={
+ Set {
+ "root",
+ }
+ }
+ focused={true}
+ injectWaitService={true}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node focused"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <ArrowExpander
+ expanded={true}
+ item={
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ >
+ <button
+ className="arrow expanded"
+ title="Collapse"
+ />
+ </ArrowExpander>
+ <span
+ className="object-label"
+ >
+ root
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+ <TreeNode
+ active={false}
+ depth={1}
+ expanded={false}
+ focused={false}
+ id="window"
+ index={1}
+ isExpandable={true}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ key="window-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-expanded={false}
+ aria-level={2}
+ className="tree-node"
+ data-expandable={true}
+ id="window"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <span
+ className="tree-indent tree-last-indent"
+ >
+ ​
+ </span>
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={
+ <ArrowExpander
+ expanded={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ />
+ }
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={1}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expanded={false}
+ expandedPaths={
+ Set {
+ "root",
+ }
+ }
+ focused={false}
+ injectWaitService={true}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ],
+ "meta": undefined,
+ "name": "root",
+ "parent": undefined,
+ "path": "root",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <ArrowExpander
+ expanded={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ >
+ <button
+ className="arrow"
+ title="Expand"
+ />
+ </ArrowExpander>
+ <span
+ className="object-label"
+ >
+ window
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-Window"
+ data-link-actor-id="server0.conn0.windowGlobal2147483651/obj35"
+ title={null}
+ >
+ <span
+ className="objectTitle"
+ >
+ Window
+ </span>
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+ </div>
+ </Tree>
+ </ObjectInspector>
+ </Connect(ObjectInspector)>
+ </Component>
+</Provider>
+`;
+
+exports[`ObjectInspector - dimTopLevelWindow renders window as expected when dimTopLevelWindow is true 1`] = `
+<Provider
+ store={
+ Object {
+ "dispatch": [Function],
+ "getState": [Function],
+ "replaceReducer": [Function],
+ "subscribe": [Function],
+ Symbol(observable): [Function],
+ }
+ }
+>
+ <Component
+ autoExpandDepth={0}
+ dimTopLevelWindow={true}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <Connect(ObjectInspector)
+ autoExpandDepth={0}
+ dimTopLevelWindow={true}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <ObjectInspector
+ addWatchpoint={[Function]}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expandedPaths={Set {}}
+ invokeGetter={[Function]}
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ removeWatchpoint={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ >
+ <Tree
+ autoExpandAll={true}
+ autoExpandDepth={0}
+ className="object-inspector"
+ getChildren={[Function]}
+ getKey={[Function]}
+ getParent={[Function]}
+ getRoots={[Function]}
+ isExpandable={[Function]}
+ isExpanded={[Function]}
+ onActivate={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ onFocus={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ className="tree object-inspector"
+ onBlur={[Function]}
+ onFocus={[Function]}
+ onKeyDown={[Function]}
+ onKeyPress={[Function]}
+ onKeyUp={[Function]}
+ role="tree"
+ style={Object {}}
+ tabIndex="0"
+ >
+ <TreeNode
+ active={false}
+ depth={0}
+ expanded={false}
+ focused={false}
+ id="window"
+ index={0}
+ isExpandable={true}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ key="window-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-expanded={false}
+ aria-level={1}
+ className="tree-node"
+ data-expandable={true}
+ id="window"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={
+ <ArrowExpander
+ expanded={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ />
+ }
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={0}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expanded={false}
+ expandedPaths={Set {}}
+ focused={false}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node lessen"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <ArrowExpander
+ expanded={false}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ >
+ <button
+ className="arrow"
+ title="Expand"
+ />
+ </ArrowExpander>
+ <span
+ className="object-label"
+ >
+ window
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-Window"
+ data-link-actor-id="server0.conn0.windowGlobal2147483651/obj35"
+ title={null}
+ >
+ <span
+ className="objectTitle"
+ >
+ Window
+ </span>
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+ </div>
+ </Tree>
+ </ObjectInspector>
+ </Connect(ObjectInspector)>
+ </Component>
+</Provider>
+`;
+
+exports[`ObjectInspector - dimTopLevelWindow renders window as expected when dimTopLevelWindow is true 2`] = `
+<Provider
+ store={
+ Object {
+ "dispatch": [Function],
+ "getState": [Function],
+ "replaceReducer": [Function],
+ "subscribe": [Function],
+ Symbol(observable): [Function],
+ }
+ }
+>
+ <Component
+ autoExpandDepth={0}
+ dimTopLevelWindow={true}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <Connect(ObjectInspector)
+ autoExpandDepth={0}
+ dimTopLevelWindow={true}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ >
+ <ObjectInspector
+ addWatchpoint={[Function]}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expandedPaths={
+ Set {
+ "window",
+ }
+ }
+ invokeGetter={[Function]}
+ loadedProperties={
+ Map {
+ "window" => Object {
+ "ownProperties": Object {},
+ "prototype": Object {},
+ },
+ }
+ }
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ removeWatchpoint={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ >
+ <Tree
+ autoExpandAll={true}
+ autoExpandDepth={0}
+ className="object-inspector"
+ focused={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ getChildren={[Function]}
+ getKey={[Function]}
+ getParent={[Function]}
+ getRoots={[Function]}
+ isExpandable={[Function]}
+ isExpanded={[Function]}
+ onActivate={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ onFocus={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-activedescendant="window"
+ className="tree object-inspector"
+ onBlur={[Function]}
+ onFocus={[Function]}
+ onKeyDown={[Function]}
+ onKeyPress={[Function]}
+ onKeyUp={[Function]}
+ role="tree"
+ style={Object {}}
+ tabIndex="0"
+ >
+ <TreeNode
+ active={false}
+ depth={0}
+ expanded={true}
+ focused={true}
+ id="window"
+ index={0}
+ isExpandable={true}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ key="window-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-expanded={true}
+ aria-level={1}
+ className="tree-node focused"
+ data-expandable={true}
+ id="window"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={
+ <ArrowExpander
+ expanded={true}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ />
+ }
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={0}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expanded={true}
+ expandedPaths={
+ Set {
+ "window",
+ }
+ }
+ focused={true}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ loadedProperties={Map {}}
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node focused"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <ArrowExpander
+ expanded={true}
+ item={
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ }
+ }
+ >
+ <button
+ className="arrow expanded"
+ title="Collapse"
+ />
+ </ArrowExpander>
+ <span
+ className="object-label"
+ >
+ window
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-Window"
+ data-link-actor-id="server0.conn0.windowGlobal2147483651/obj35"
+ title={null}
+ >
+ <span
+ className="objectTitle"
+ >
+ Window
+ </span>
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+ <TreeNode
+ active={false}
+ depth={1}
+ expanded={false}
+ focused={false}
+ id="window◦<prototype>"
+ index={1}
+ isExpandable={false}
+ item={
+ Object {
+ "contents": Object {
+ "front": null,
+ "value": Object {},
+ },
+ "meta": undefined,
+ "name": "<prototype>",
+ "parent": Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ "path": "window◦<prototype>",
+ "propertyName": undefined,
+ "type": Symbol(<prototype>),
+ }
+ }
+ key="window◦<prototype>-inactive"
+ onClick={[Function]}
+ onCollapse={[Function]}
+ onExpand={[Function]}
+ renderItem={[Function]}
+ shouldItemUpdate={[Function]}
+ >
+ <div
+ aria-level={2}
+ className="tree-node"
+ data-expandable={false}
+ id="window◦<prototype>"
+ onClick={[Function]}
+ onKeyDownCapture={null}
+ role="treeitem"
+ >
+ <span
+ className="tree-indent tree-last-indent"
+ >
+ ​
+ </span>
+ <ObjectInspectorItem
+ addWatchpoint={[Function]}
+ arrow={null}
+ autoExpandDepth={0}
+ autoReleaseObjectActors={true}
+ closeObjectInspector={[Function]}
+ depth={1}
+ dimTopLevelWindow={true}
+ evaluations={Map {}}
+ expanded={false}
+ expandedPaths={
+ Set {
+ "window",
+ }
+ }
+ focused={false}
+ invokeGetter={[Function]}
+ item={
+ Object {
+ "contents": Object {
+ "front": null,
+ "value": Object {},
+ },
+ "meta": undefined,
+ "name": "<prototype>",
+ "parent": Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ "path": "window◦<prototype>",
+ "propertyName": undefined,
+ "type": Symbol(<prototype>),
+ }
+ }
+ loadedProperties={
+ Map {
+ "window" => Object {
+ "ownProperties": Object {},
+ "prototype": Object {},
+ },
+ }
+ }
+ nodeCollapse={[Function]}
+ nodeExpand={[Function]}
+ nodeLoadProperties={[Function]}
+ nodePropertiesLoaded={[Function]}
+ onContextMenu={[Function]}
+ removeWatchpoint={[Function]}
+ renderItemActions={[Function]}
+ roots={
+ Array [
+ Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "extensible": true,
+ "frozen": false,
+ "isError": false,
+ "ownPropertyLength": 806,
+ "preview": Object {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation",
+ },
+ "sealed": false,
+ "type": "object",
+ },
+ },
+ "meta": undefined,
+ "name": "window",
+ "parent": undefined,
+ "path": "window",
+ "propertyName": undefined,
+ "type": Symbol(GRIP),
+ },
+ ]
+ }
+ rootsChanged={[Function]}
+ setExpanded={[Function]}
+ >
+ <div
+ className="node object-node lessen"
+ onClick={[Function]}
+ onContextMenu={[Function]}
+ >
+ <span
+ className="object-label"
+ >
+ &lt;prototype&gt;
+ </span>
+ <span
+ className="object-delimiter"
+ >
+ :
+ </span>
+ <span
+ className="objectBox objectBox-object"
+ title={null}
+ >
+ <span
+ className="objectLeftBrace"
+ >
+ {
+ </span>
+ <span
+ className="objectRightBrace"
+ >
+ }
+ </span>
+ </span>
+ </div>
+ </ObjectInspectorItem>
+ </div>
+ </TreeNode>
+ </div>
+ </Tree>
+ </ObjectInspector>
+ </Connect(ObjectInspector)>
+ </Component>
+</Provider>
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/basic.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/basic.test.js
new file mode 100644
index 0000000000..47a919db02
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/basic.test.js
@@ -0,0 +1,439 @@
+/* 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/>. */
+
+const {
+ mountObjectInspector,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const { mount } = require("enzyme");
+const {
+ createNode,
+ NODE_TYPES,
+} = require("devtools/client/shared/components/object-inspector/utils/node");
+
+const { Rep } = require(`devtools/client/shared/components/reps/reps/rep`);
+const {
+ MODE,
+} = require(`devtools/client/shared/components/reps/reps/constants`);
+const {
+ formatObjectInspector,
+ waitForDispatch,
+ waitForLoadedProperties,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const ObjectFront = require("devtools/client/shared/components/test/node/__mocks__/object-front");
+const gripRepStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip`);
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ ...overrides,
+ };
+}
+
+function mountOI(props, { initialState } = {}) {
+ const client = {
+ createObjectFront: grip => ObjectFront(grip),
+ };
+
+ const obj = mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ initialState: {
+ objectInspector: {
+ ...initialState,
+ evaluations: new Map(),
+ },
+ },
+ });
+
+ return obj;
+}
+
+function renderOI(props, opts) {
+ return mountOI(props, opts).wrapper;
+}
+
+describe("ObjectInspector - renders", () => {
+ it("renders as expected", () => {
+ const stub = gripRepStubs.get("testMoreThanMaxProps");
+
+ const renderObjectInspector = mode =>
+ renderOI({
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ mode,
+ });
+
+ const renderRep = mode => Rep({ object: stub, mode });
+
+ const tinyOi = renderObjectInspector(MODE.TINY);
+ expect(tinyOi.find(".arrow").exists()).toBeTruthy();
+ expect(tinyOi.contains(renderRep(MODE.TINY))).toBeTruthy();
+ expect(formatObjectInspector(tinyOi)).toMatchSnapshot();
+
+ const shortOi = renderObjectInspector(MODE.SHORT);
+ expect(shortOi.find(".arrow").exists()).toBeTruthy();
+ expect(shortOi.contains(renderRep(MODE.SHORT))).toBeTruthy();
+ expect(formatObjectInspector(shortOi)).toMatchSnapshot();
+
+ const longOi = renderObjectInspector(MODE.LONG);
+ expect(longOi.find(".arrow").exists()).toBeTruthy();
+ expect(longOi.contains(renderRep(MODE.LONG))).toBeTruthy();
+ expect(formatObjectInspector(longOi)).toMatchSnapshot();
+
+ const oi = renderObjectInspector();
+ expect(oi.find(".arrow").exists()).toBeTruthy();
+ // When no mode is provided, it defaults to TINY mode to render the Rep.
+ expect(oi.contains(renderRep(MODE.TINY))).toBeTruthy();
+ expect(formatObjectInspector(oi)).toMatchSnapshot();
+ });
+
+ it("directly renders a Rep when the stub is not expandable", () => {
+ const object = 42;
+
+ const renderObjectInspector = mode =>
+ renderOI({
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: object,
+ },
+ },
+ ],
+ mode,
+ });
+
+ const renderRep = mode => mount(Rep({ object, mode }));
+
+ const tinyOi = renderObjectInspector(MODE.TINY);
+ expect(tinyOi.find(".arrow").exists()).toBeFalsy();
+ expect(tinyOi.html()).toEqual(renderRep(MODE.TINY).html());
+
+ const shortOi = renderObjectInspector(MODE.SHORT);
+ expect(shortOi.find(".arrow").exists()).toBeFalsy();
+ expect(shortOi.html()).toEqual(renderRep(MODE.SHORT).html());
+
+ const longOi = renderObjectInspector(MODE.LONG);
+ expect(longOi.find(".arrow").exists()).toBeFalsy();
+ expect(longOi.html()).toEqual(renderRep(MODE.LONG).html());
+
+ const oi = renderObjectInspector();
+ expect(oi.find(".arrow").exists()).toBeFalsy();
+ // When no mode is provided, it defaults to TINY mode to render the Rep.
+ expect(oi.html()).toEqual(renderRep(MODE.TINY).html());
+ });
+
+ it("renders objects as expected when provided a name", () => {
+ const object = gripRepStubs.get("testMoreThanMaxProps");
+ const name = "myproperty";
+
+ const oi = renderOI({
+ roots: [
+ {
+ path: "root",
+ name,
+ contents: {
+ value: object,
+ },
+ },
+ ],
+ mode: MODE.SHORT,
+ });
+
+ expect(oi.find(".object-label").text()).toEqual(name);
+ expect(formatObjectInspector(oi)).toMatchSnapshot();
+ });
+
+ it("renders primitives as expected when provided a name", () => {
+ const value = 42;
+ const name = "myproperty";
+
+ const oi = renderOI({
+ roots: [
+ {
+ path: "root",
+ name,
+ contents: { value },
+ },
+ ],
+ mode: MODE.SHORT,
+ });
+
+ expect(oi.find(".object-label").text()).toEqual(name);
+ expect(formatObjectInspector(oi)).toMatchSnapshot();
+ });
+
+ it("renders as expected when not provided a name", () => {
+ const object = gripRepStubs.get("testMoreThanMaxProps");
+
+ const oi = renderOI({
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: object,
+ },
+ },
+ ],
+ mode: MODE.SHORT,
+ });
+
+ expect(oi.find(".object-label").exists()).toBeFalsy();
+ expect(formatObjectInspector(oi)).toMatchSnapshot();
+ });
+
+ it("renders leaves with a shorter mode than the root", async () => {
+ const stub = gripRepStubs.get("testMaxProps");
+
+ const renderObjectInspector = mode =>
+ renderOI(
+ {
+ autoExpandDepth: 1,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ mode,
+ },
+ {
+ initialState: {
+ loadedProperties: new Map([
+ [
+ "root",
+ {
+ ownProperties: Object.keys(stub.preview.ownProperties).reduce(
+ (res, key) => ({
+ [key]: {
+ value: stub,
+ },
+ ...res,
+ }),
+ {}
+ ),
+ },
+ ],
+ ]),
+ },
+ }
+ );
+
+ const renderRep = mode => Rep({ object: stub, mode });
+
+ const tinyOi = renderObjectInspector(MODE.TINY);
+
+ expect(
+ tinyOi
+ .find(".node")
+ .at(1)
+ .contains(renderRep(MODE.TINY))
+ ).toBeTruthy();
+
+ const shortOi = renderObjectInspector(MODE.SHORT);
+ expect(
+ shortOi
+ .find(".node")
+ .at(1)
+ .contains(renderRep(MODE.TINY))
+ ).toBeTruthy();
+
+ const longOi = renderObjectInspector(MODE.LONG);
+ expect(
+ longOi
+ .find(".node")
+ .at(1)
+ .contains(renderRep(MODE.SHORT))
+ ).toBeTruthy();
+
+ const oi = renderObjectInspector();
+ // When no mode is provided, it defaults to TINY mode to render the Rep.
+ expect(
+ oi
+ .find(".node")
+ .at(1)
+ .contains(renderRep(MODE.TINY))
+ ).toBeTruthy();
+ });
+
+ it("renders less-important nodes as expected", async () => {
+ const defaultPropertiesNode = createNode({
+ name: "<default>",
+ contents: [],
+ type: NODE_TYPES.DEFAULT_PROPERTIES,
+ });
+
+ // The <default properties> node should have the "lessen" class only when
+ // collapsed.
+ let { store, wrapper } = mountOI({
+ roots: [defaultPropertiesNode],
+ });
+
+ let defaultPropertiesElementNode = wrapper.find(".node");
+ expect(defaultPropertiesElementNode.hasClass("lessen")).toBe(true);
+
+ let onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ defaultPropertiesElementNode.simulate("click");
+ await onPropertiesLoaded;
+ wrapper.update();
+ defaultPropertiesElementNode = wrapper.find(".node").first();
+ expect(
+ wrapper
+ .find(".node")
+ .first()
+ .hasClass("lessen")
+ ).toBe(false);
+
+ const prototypeNode = createNode({
+ name: "<prototype>",
+ contents: [],
+ type: NODE_TYPES.PROTOTYPE,
+ });
+
+ // The <prototype> node should have the "lessen" class only when collapsed.
+ ({ wrapper, store } = mountOI({
+ roots: [prototypeNode],
+ injectWaitService: true,
+ }));
+
+ let protoElementNode = wrapper.find(".node");
+ expect(protoElementNode.hasClass("lessen")).toBe(true);
+
+ onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ protoElementNode.simulate("click");
+ await onPropertiesLoaded;
+ wrapper.update();
+
+ protoElementNode = wrapper.find(".node").first();
+ expect(protoElementNode.hasClass("lessen")).toBe(false);
+ });
+
+ it("renders block nodes as expected", async () => {
+ const blockNode = createNode({
+ name: "Block",
+ contents: [
+ {
+ name: "a",
+ contents: {
+ value: 30,
+ },
+ },
+ {
+ name: "b",
+ contents: {
+ value: 32,
+ },
+ },
+ ],
+ type: NODE_TYPES.BLOCK,
+ });
+
+ const { wrapper, store } = mountOI({
+ roots: [blockNode],
+ autoExpandDepth: 1,
+ });
+
+ await waitForLoadedProperties(store, ["Block"]);
+ wrapper.update();
+
+ const blockElementNode = wrapper.find(".node").first();
+ expect(blockElementNode.hasClass("block")).toBe(true);
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it.skip("updates when the root changes", async () => {
+ let root = {
+ path: "root",
+ contents: {
+ value: gripRepStubs.get("testMoreThanMaxProps"),
+ },
+ };
+ const { wrapper } = mountOI({
+ roots: [root],
+ mode: MODE.LONG,
+ focusedItem: root,
+ });
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ root = {
+ path: "root-2",
+ contents: {
+ value: gripRepStubs.get("testMaxProps"),
+ },
+ };
+
+ wrapper.setProps({
+ roots: [root],
+ focusedItem: root,
+ });
+ wrapper.update();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it.skip("updates when the root changes but has same path", async () => {
+ const { wrapper, store } = mountOI({
+ injectWaitService: true,
+ roots: [
+ {
+ path: "root",
+ name: "root",
+ contents: [
+ {
+ name: "a",
+ contents: {
+ value: 30,
+ },
+ },
+ {
+ name: "b",
+ contents: {
+ value: 32,
+ },
+ },
+ ],
+ },
+ ],
+ mode: MODE.LONG,
+ });
+
+ wrapper
+ .find(".node")
+ .at(0)
+ .simulate("click");
+
+ const oldTree = formatObjectInspector(wrapper);
+
+ const onRootsChanged = waitForDispatch(store, "ROOTS_CHANGED");
+
+ wrapper.setProps({
+ roots: [
+ {
+ path: "root",
+ name: "root",
+ contents: [
+ {
+ name: "c",
+ contents: {
+ value: "i'm the new node",
+ },
+ },
+ ],
+ },
+ ],
+ });
+
+ await onRootsChanged;
+ wrapper.update();
+ expect(formatObjectInspector(wrapper)).not.toBe(oldTree);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/classnames.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/classnames.test.js
new file mode 100644
index 0000000000..9f93d8fb70
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/classnames.test.js
@@ -0,0 +1,53 @@
+/* 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/>. */
+
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+const {
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ roots: [
+ {
+ path: "root",
+ name: "root",
+ contents: { value: 42 },
+ },
+ ],
+ ...overrides,
+ };
+}
+
+function mount(props) {
+ const client = { createObjectFront: grip => ObjectFront(grip) };
+
+ return mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ });
+}
+
+describe("ObjectInspector - classnames", () => {
+ it("has the expected class", () => {
+ const { tree } = mount();
+ expect(tree.hasClass("tree")).toBeTruthy();
+ expect(tree.hasClass("inline")).toBeFalsy();
+ expect(tree.hasClass("nowrap")).toBeFalsy();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("has the nowrap class when disableWrap prop is true", () => {
+ const { tree } = mount({ disableWrap: true });
+ expect(tree.hasClass("nowrap")).toBeTruthy();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("has the inline class when inline prop is true", () => {
+ const { tree } = mount({ inline: true });
+ expect(tree.hasClass("inline")).toBeTruthy();
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/create-long-string-front.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/create-long-string-front.test.js
new file mode 100644
index 0000000000..40e2fd772a
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/create-long-string-front.test.js
@@ -0,0 +1,94 @@
+/* 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/>. */
+
+/* global jest */
+
+const {
+ mountObjectInspector,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const ObjectFront = require("devtools/client/shared/components/test/node/__mocks__/object-front");
+const {
+ LongStringFront,
+} = require("devtools/client/shared/components/test/node/__mocks__/string-front");
+
+const longStringStubs = require(`devtools/client/shared/components/test/node/stubs/reps/long-string`);
+
+function mount(props) {
+ const substring = jest.fn(() => Promise.resolve(""));
+
+ const client = {
+ createObjectFront: grip => ObjectFront(grip),
+ createLongStringFront: jest.fn(grip =>
+ LongStringFront(grip, { substring })
+ ),
+ };
+
+ const obj = mountObjectInspector({
+ client,
+ props,
+ });
+
+ return { ...obj, substring };
+}
+
+describe("createLongStringFront", () => {
+ it("is called with the expected object for longString node", () => {
+ const stub = longStringStubs.get("testMultiline");
+
+ const { client } = mount({
+ autoExpandDepth: 1,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ });
+
+ expect(client.createLongStringFront.mock.calls[0][0]).toBe(stub);
+ });
+
+ describe("substring", () => {
+ it("is called for longStrings with unloaded full text", () => {
+ const stub = longStringStubs.get("testUnloadedFullText");
+
+ const { substring } = mount({
+ autoExpandDepth: 1,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ });
+
+ expect(substring.mock.calls[0]).toHaveLength(2);
+ const [start, length] = substring.mock.calls[0];
+ expect(start).toBe(stub.initial.length);
+ expect(length).toBe(stub.length);
+ });
+
+ it("is not called for longString node w/ loaded full text", () => {
+ const stub = longStringStubs.get("testLoadedFullText");
+
+ const { substring } = mount({
+ autoExpandDepth: 1,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ });
+
+ expect(substring.mock.calls).toHaveLength(0);
+ });
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/create-object-client.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/create-object-client.test.js
new file mode 100644
index 0000000000..f969debfb7
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/create-object-client.test.js
@@ -0,0 +1,114 @@
+/* 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/>. */
+
+/* global jest */
+
+const {
+ mountObjectInspector,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const ObjectFront = require("devtools/client/shared/components/test/node/__mocks__/object-front");
+
+const {
+ createNode,
+ makeNodesForEntries,
+ makeNumericalBuckets,
+} = require("devtools/client/shared/components/object-inspector/utils/node");
+
+const gripRepStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip`);
+const gripArrayRepStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip-array`);
+
+function mount(props, overrides = {}) {
+ const client = {
+ createObjectFront:
+ overrides.createObjectFront || jest.fn(grip => ObjectFront(grip)),
+ getFrontByID: _id => null,
+ };
+
+ return mountObjectInspector({
+ client,
+ props,
+ });
+}
+
+describe("createObjectFront", () => {
+ it("is called with the expected object for regular node", () => {
+ const stub = gripRepStubs.get("testMoreThanMaxProps");
+ const { client } = mount({
+ autoExpandDepth: 1,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ });
+
+ expect(client.createObjectFront.mock.calls[0][0]).toBe(stub);
+ });
+
+ it("is called with the expected object for entries node", () => {
+ const grip = Symbol();
+ const mapStubNode = createNode({
+ name: "map",
+ contents: { value: grip },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+
+ const { client } = mount({
+ autoExpandDepth: 1,
+ roots: [entriesNode],
+ });
+
+ expect(client.createObjectFront.mock.calls[0][0]).toBe(grip);
+ });
+
+ it("is called with the expected object for bucket node", () => {
+ const grip = gripArrayRepStubs.get("testMaxProps");
+ const root = createNode({ name: "root", contents: { value: grip } });
+ const [bucket] = makeNumericalBuckets(root);
+
+ const { client } = mount({
+ autoExpandDepth: 1,
+ roots: [bucket],
+ });
+ expect(client.createObjectFront.mock.calls[0][0]).toBe(grip);
+ });
+
+ it("is called with the expected object for sub-bucket node", () => {
+ const grip = gripArrayRepStubs.get("testMaxProps");
+ const root = createNode({ name: "root", contents: { value: grip } });
+ const [bucket] = makeNumericalBuckets(root);
+ const [subBucket] = makeNumericalBuckets(bucket);
+
+ const { client } = mount({
+ autoExpandDepth: 1,
+ roots: [subBucket],
+ });
+
+ expect(client.createObjectFront.mock.calls[0][0]).toBe(grip);
+ });
+
+ it("doesn't fail when ObjectFront doesn't have expected methods", () => {
+ const stub = gripRepStubs.get("testMoreThanMaxProps");
+ const root = createNode({ name: "root", contents: { value: stub } });
+
+ // Override console.error so we don't spam test results.
+ const originalConsoleError = console.error;
+ console.error = () => {};
+
+ const createObjectFront = x => ({});
+ mount(
+ {
+ autoExpandDepth: 1,
+ roots: [root],
+ },
+ { createObjectFront }
+ );
+
+ // rollback console.error.
+ console.error = originalConsoleError;
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/entries.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/entries.test.js
new file mode 100644
index 0000000000..0cb896d3ae
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/entries.test.js
@@ -0,0 +1,137 @@
+/* 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/>. */
+
+/* global jest */
+
+const {
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ formatObjectInspector,
+ waitForDispatch,
+ waitForLoadedProperties,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+const gripMapRepStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const mapStubs = require("resource://devtools/client/shared/components/test/node/stubs/object-inspector/map.js");
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ createObjectFront: grip => ObjectFront(grip),
+ ...overrides,
+ };
+}
+
+function getEnumEntriesMock() {
+ return jest.fn(() => ({
+ slice: () => mapStubs.get("11-entries"),
+ }));
+}
+
+function mount(props, { initialState }) {
+ const enumEntries = getEnumEntriesMock();
+
+ const client = {
+ createObjectFront: grip => ObjectFront(grip, { enumEntries }),
+ getFrontByID: _id => null,
+ };
+ const obj = mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ initialState: {
+ objectInspector: {
+ ...initialState,
+ evaluations: new Map(),
+ },
+ },
+ });
+
+ return { ...obj, enumEntries };
+}
+
+describe("ObjectInspector - entries", () => {
+ it("renders Object with entries as expected", async () => {
+ const stub = gripMapRepStubs.get("testSymbolKeyedMap");
+
+ const { store, wrapper, enumEntries } = mount(
+ {
+ autoExpandDepth: 3,
+ roots: [
+ {
+ path: "root",
+ contents: { value: stub },
+ },
+ ],
+ mode: MODE.LONG,
+ },
+ {
+ initialState: {
+ loadedProperties: new Map([["root", mapStubs.get("properties")]]),
+ },
+ }
+ );
+
+ await waitForLoadedProperties(store, [
+ "root◦<entries>◦0",
+ "root◦<entries>◦1",
+ ]);
+
+ wrapper.update();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("calls ObjectFront.enumEntries when expected", async () => {
+ const stub = gripMapRepStubs.get("testMoreThanMaxEntries");
+
+ const { wrapper, store, enumEntries } = mount(
+ {
+ autoExpandDepth: 1,
+ injectWaitService: true,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ },
+ {
+ initialState: {
+ loadedProperties: new Map([
+ ["root", { ownProperties: stub.preview.entries }],
+ ]),
+ },
+ }
+ );
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ const nodes = wrapper.find(".node");
+ const entriesNode = nodes.at(1);
+ expect(entriesNode.text()).toBe("<entries>");
+
+ const onEntrieLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ entriesNode.simulate("click");
+ await onEntrieLoad;
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ expect(enumEntries.mock.calls).toHaveLength(1);
+
+ entriesNode.simulate("click");
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ entriesNode.simulate("click");
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ // it does not call enumEntries if entries were already loaded.
+ expect(enumEntries.mock.calls).toHaveLength(1);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/events.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/events.test.js
new file mode 100644
index 0000000000..e6483dbefb
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/events.test.js
@@ -0,0 +1,171 @@
+/* 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/>. */
+
+/* global jest */
+const {
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+const gripRepStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ ...overrides,
+ };
+}
+
+function mount(props) {
+ const client = { createObjectFront: grip => ObjectFront(grip) };
+
+ return mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ });
+}
+
+describe("ObjectInspector - properties", () => {
+ it("calls the onFocus prop when provided one and given focus", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+ const onFocus = jest.fn();
+
+ const { wrapper } = mount({
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ onFocus,
+ });
+
+ const node = wrapper.find(".node").first();
+ node.simulate("focus");
+
+ expect(onFocus.mock.calls).toHaveLength(1);
+ });
+
+ it("doesn't call the onFocus when given focus but focusable is false", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+ const onFocus = jest.fn();
+
+ const { wrapper } = mount({
+ focusable: false,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ onFocus,
+ });
+
+ const node = wrapper.find(".node").first();
+ node.simulate("focus");
+
+ expect(onFocus.mock.calls).toHaveLength(0);
+ });
+
+ it("calls onDoubleClick prop when provided one and double clicked", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+ const onDoubleClick = jest.fn();
+
+ const { wrapper } = mount({
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ onDoubleClick,
+ });
+
+ const node = wrapper.find(".node").first();
+ node.simulate("doubleclick");
+
+ expect(onDoubleClick.mock.calls).toHaveLength(1);
+ });
+
+ it("calls the onCmdCtrlClick prop when provided and cmd/ctrl-clicked", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+ const onCmdCtrlClick = jest.fn();
+
+ const { wrapper } = mount({
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ onCmdCtrlClick,
+ });
+
+ const node = wrapper.find(".node").first();
+ node.simulate("click", { ctrlKey: true });
+
+ expect(onCmdCtrlClick.mock.calls).toHaveLength(1);
+ });
+
+ it("calls the onLabel prop when provided one and label clicked", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+ const onLabelClick = jest.fn();
+
+ const { wrapper } = mount({
+ roots: [
+ {
+ path: "root",
+ name: "Label",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ onLabelClick,
+ });
+
+ const label = wrapper.find(".object-label").first();
+ label.simulate("click");
+
+ expect(onLabelClick.mock.calls).toHaveLength(1);
+ });
+
+ it("does not call the onLabel prop when the user selected text", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+ const onLabelClick = jest.fn();
+
+ const { wrapper } = mount({
+ roots: [
+ {
+ path: "root",
+ name: "Label",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ onLabelClick,
+ });
+
+ const label = wrapper.find(".object-label").first();
+
+ // Set a selection using the mock.
+ getSelection().setMockSelection("test");
+
+ label.simulate("click");
+
+ expect(onLabelClick.mock.calls).toHaveLength(0);
+
+ // Clear the selection for other tests.
+ getSelection().setMockSelection();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/expand.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/expand.test.js
new file mode 100644
index 0000000000..3243d8c259
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/expand.test.js
@@ -0,0 +1,435 @@
+/* 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/>. */
+const {
+ mountObjectInspector,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+
+const { MODE } = require(`devtools/client/shared/components/reps/index`);
+const ObjectFront = require("devtools/client/shared/components/test/node/__mocks__/object-front");
+const gripRepStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip`);
+const gripPropertiesStubs = require("devtools/client/shared/components/test/node/stubs/object-inspector/grip");
+const {
+ formatObjectInspector,
+ storeHasExactExpandedPaths,
+ storeHasExpandedPath,
+ storeHasLoadedProperty,
+ waitForDispatch,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const {
+ createNode,
+ NODE_TYPES,
+} = require("devtools/client/shared/components/object-inspector/utils/node");
+const {
+ getExpandedPaths,
+} = require("devtools/client/shared/components/object-inspector/reducer");
+
+const protoStub = {
+ prototype: {
+ type: "object",
+ actor: "server2.conn0.child1/obj628",
+ class: "Object",
+ },
+};
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ roots: [
+ {
+ path: "root-1",
+ contents: {
+ value: gripRepStubs.get("testMoreThanMaxProps"),
+ },
+ },
+ {
+ path: "root-2",
+ contents: {
+ value: gripRepStubs.get("testProxy"),
+ },
+ },
+ ],
+ createObjectFront: grip => ObjectFront(grip),
+ mode: MODE.LONG,
+ ...overrides,
+ };
+}
+const {
+ LongStringFront,
+} = require("devtools/client/shared/components/test/node/__mocks__/string-front");
+
+function getClient(overrides = {}) {
+ return {
+ releaseActor: () => {},
+
+ createObjectFront: grip =>
+ ObjectFront(grip, {
+ getPrototype: () => Promise.resolve(protoStub),
+ getProxySlots: () =>
+ Promise.resolve(gripRepStubs.get("testProxySlots")),
+ }),
+
+ createLongStringFront: grip =>
+ LongStringFront(grip, {
+ substring: async function(initiaLength, length) {
+ return "<<<<";
+ },
+ }),
+
+ getFrontByID: _id => null,
+
+ ...overrides,
+ };
+}
+
+function mount(props, { initialState, client = getClient() } = {}) {
+ return mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ initialState: {
+ objectInspector: {
+ ...initialState,
+ evaluations: new Map(),
+ },
+ },
+ });
+}
+
+describe("ObjectInspector - state", () => {
+ it("has the expected expandedPaths state when clicking nodes", async () => {
+ const { wrapper, store } = mount(
+ {},
+ {
+ initialState: {
+ loadedProperties: new Map([
+ ["root-1", gripPropertiesStubs.get("proto-properties-symbols")],
+ ]),
+ },
+ }
+ );
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ let nodes = wrapper.find(".node");
+
+ // Clicking on the root node adds it path to "expandedPaths".
+ const root1 = nodes.at(0);
+ const root2 = nodes.at(1);
+
+ root1.simulate("click");
+
+ expect(storeHasExactExpandedPaths(store, ["root-1"])).toBeTruthy();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ //
+ // Clicking on the root node removes it path from "expandedPaths".
+ root1.simulate("click");
+ expect(storeHasExactExpandedPaths(store, [])).toBeTruthy();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ root2.simulate("click");
+ await onPropertiesLoaded;
+ expect(storeHasExactExpandedPaths(store, ["root-2"])).toBeTruthy();
+
+ wrapper.update();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ root1.simulate("click");
+ expect(
+ storeHasExactExpandedPaths(store, ["root-1", "root-2"])
+ ).toBeTruthy();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ nodes = wrapper.find(".node");
+ const propNode = nodes.at(1);
+ const symbolNode = nodes.at(2);
+ const protoNode = nodes.at(3);
+
+ propNode.simulate("click");
+ symbolNode.simulate("click");
+ protoNode.simulate("click");
+
+ expect(
+ storeHasExactExpandedPaths(store, [
+ "root-1",
+ "root-2",
+ "root-1◦<prototype>",
+ ])
+ ).toBeTruthy();
+
+ // The property and symbols have primitive values, and can't be expanded.
+ expect(getExpandedPaths(store.getState()).size).toBe(3);
+ });
+
+ it("has the expected state when expanding a node", async () => {
+ const { wrapper, store } = mount({}, {});
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ let nodes = wrapper.find(".node");
+ const root1 = nodes.at(0);
+
+ let onPropertiesLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ root1.simulate("click");
+ await onPropertiesLoad;
+ wrapper.update();
+
+ expect(storeHasLoadedProperty(store, "root-1")).toBeTruthy();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ nodes = wrapper.find(".node");
+ const protoNode = nodes.at(1);
+
+ onPropertiesLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ protoNode.simulate("click");
+ await onPropertiesLoad;
+ wrapper.update();
+
+ // Once all the loading promises are resolved, actors and loadedProperties
+ // should have the expected values.
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ expect(storeHasLoadedProperty(store, "root-1◦<prototype>")).toBeTruthy();
+ });
+
+ it("does not handle actors when client does not have releaseActor function", async () => {
+ const { wrapper, store } = mount(
+ {},
+ { client: getClient({ releaseActor: null }) }
+ );
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ let nodes = wrapper.find(".node");
+ const root1 = nodes.at(0);
+
+ let onPropertiesLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ root1.simulate("click");
+ await onPropertiesLoad;
+ wrapper.update();
+
+ expect(storeHasLoadedProperty(store, "root-1")).toBeTruthy();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ nodes = wrapper.find(".node");
+ const protoNode = nodes.at(1);
+
+ onPropertiesLoad = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ protoNode.simulate("click");
+ await onPropertiesLoad;
+ wrapper.update();
+
+ // Once all the loading promises are resolved, actors and loadedProperties
+ // should have the expected values.
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ expect(storeHasLoadedProperty(store, "root-1◦<prototype>")).toBeTruthy();
+ });
+
+ it.skip("has the expected state when expanding a proxy node", async () => {
+ const { wrapper, store } = mount({});
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ let nodes = wrapper.find(".node");
+
+ const proxyNode = nodes.at(1);
+
+ let onLoadProperties = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ proxyNode.simulate("click");
+ await onLoadProperties;
+ wrapper.update();
+
+ // Once the properties are loaded, actors and loadedProperties should have
+ // the expected values.
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ nodes = wrapper.find(".node");
+ const handlerNode = nodes.at(3);
+ onLoadProperties = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ handlerNode.simulate("click");
+ await onLoadProperties;
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ expect(storeHasLoadedProperty(store, "root-2◦<handler>")).toBeTruthy();
+ });
+
+ it("does not expand if the user selected some text", async () => {
+ const { wrapper, store } = mount(
+ {},
+ {
+ initialSate: {
+ loadedProperties: new Map([
+ ["root-1", gripPropertiesStubs.get("proto-properties-symbols")],
+ ]),
+ },
+ }
+ );
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ const nodes = wrapper.find(".node");
+
+ // Set a selection using the mock.
+ getSelection().setMockSelection("test");
+
+ const root1 = nodes.at(0);
+ root1.simulate("click");
+ expect(storeHasExpandedPath(store, "root-1")).toBeFalsy();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // Clear the selection for other tests.
+ getSelection().setMockSelection();
+ });
+
+ it("expands if user selected some text and clicked the arrow", async () => {
+ const { wrapper, store } = mount(
+ {},
+ {
+ initialState: {
+ loadedProperties: new Map([
+ ["root-1", gripPropertiesStubs.get("proto-properties-symbols")],
+ ]),
+ },
+ }
+ );
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ const nodes = wrapper.find(".node");
+
+ // Set a selection using the mock.
+ getSelection().setMockSelection("test");
+
+ const root1 = nodes.at(0);
+ root1.find(".arrow").simulate("click");
+ expect(getExpandedPaths(store.getState()).has("root-1")).toBeTruthy();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // Clear the selection for other tests.
+ getSelection().setMockSelection();
+ });
+
+ it("does not throw when expanding a block node", async () => {
+ const blockNode = createNode({
+ name: "Block",
+ contents: [
+ {
+ name: "a",
+ contents: {
+ value: 30,
+ },
+ },
+ {
+ name: "b",
+ contents: {
+ value: 32,
+ },
+ },
+ ],
+ type: NODE_TYPES.BLOCK,
+ });
+
+ const proxyNode = createNode({
+ name: "Proxy",
+ contents: {
+ value: gripRepStubs.get("testProxy"),
+ },
+ });
+
+ const { wrapper, store } = mount({
+ roots: [blockNode, proxyNode],
+ });
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ const nodes = wrapper.find(".node");
+ const root = nodes.at(0);
+ const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ root.simulate("click");
+ await onPropertiesLoaded;
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("calls recordTelemetryEvent when expanding a node", async () => {
+ const recordTelemetryEvent = jest.fn();
+ const { wrapper, store } = mount(
+ {
+ recordTelemetryEvent,
+ },
+ {
+ initialState: {
+ loadedProperties: new Map([
+ ["root-1", gripPropertiesStubs.get("proto-properties-symbols")],
+ ]),
+ },
+ }
+ );
+
+ let nodes = wrapper.find(".node");
+ const root1 = nodes.at(0);
+ const root2 = nodes.at(1);
+
+ // Expanding a node calls recordTelemetryEvent.
+ root1.simulate("click");
+ expect(recordTelemetryEvent.mock.calls).toHaveLength(1);
+ expect(recordTelemetryEvent.mock.calls[0][0]).toEqual("object_expanded");
+
+ // Collapsing a node does not call recordTelemetryEvent.
+ root1.simulate("click");
+ expect(recordTelemetryEvent.mock.calls).toHaveLength(1);
+
+ // Expanding another node calls recordTelemetryEvent.
+ const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ root2.simulate("click");
+ await onPropertiesLoaded;
+ expect(recordTelemetryEvent.mock.calls).toHaveLength(2);
+ expect(recordTelemetryEvent.mock.calls[1][0]).toEqual("object_expanded");
+
+ wrapper.update();
+
+ // Re-expanding a node calls recordTelemetryEvent.
+ root1.simulate("click");
+ expect(recordTelemetryEvent.mock.calls).toHaveLength(3);
+ expect(recordTelemetryEvent.mock.calls[2][0]).toEqual("object_expanded");
+
+ nodes = wrapper.find(".node");
+ const propNode = nodes.at(1);
+ const symbolNode = nodes.at(2);
+ const protoNode = nodes.at(3);
+
+ propNode.simulate("click");
+ symbolNode.simulate("click");
+ protoNode.simulate("click");
+
+ // The property and symbols have primitive values, and can't be expanded.
+ expect(recordTelemetryEvent.mock.calls).toHaveLength(4);
+ expect(recordTelemetryEvent.mock.calls[3][0]).toEqual("object_expanded");
+ });
+
+ it("expanding a getter returning a longString does not throw", async () => {
+ const { wrapper, store } = mount(
+ {
+ focusable: false,
+ },
+ {
+ initialState: {
+ loadedProperties: new Map([
+ ["root-1", gripPropertiesStubs.get("longs-string-safe-getter")],
+ ]),
+ },
+ }
+ );
+
+ wrapper
+ .find(".node")
+ .at(0)
+ .simulate("click");
+ wrapper.update();
+
+ const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ wrapper
+ .find(".node")
+ .at(1)
+ .simulate("click");
+ await onPropertiesLoaded;
+
+ wrapper.update();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/function.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/function.test.js
new file mode 100644
index 0000000000..0e5bb9573a
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/function.test.js
@@ -0,0 +1,90 @@
+/* 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/>. */
+
+const {
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ createNode,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+const functionStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/function.js");
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 1,
+ ...overrides,
+ };
+}
+
+function mount(props) {
+ const client = {
+ createObjectFront: grip => ObjectFront(grip),
+ getFrontByID: _id => null,
+ };
+
+ return mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ });
+}
+
+describe("ObjectInspector - functions", () => {
+ it("renders named function properties as expected", () => {
+ const stub = functionStubs.get("Named");
+ const { wrapper } = mount({
+ roots: [
+ createNode({
+ name: "fn",
+ contents: { value: stub },
+ }),
+ ],
+ });
+
+ const nodes = wrapper.find(".node");
+ const functionNode = nodes.first();
+ expect(functionNode.text()).toBe("fn:testName()");
+ });
+
+ it("renders anon function properties as expected", () => {
+ const stub = functionStubs.get("Anon");
+ const { wrapper } = mount({
+ roots: [
+ createNode({
+ name: "fn",
+ contents: { value: stub },
+ }),
+ ],
+ });
+
+ const nodes = wrapper.find(".node");
+ const functionNode = nodes.first();
+ // It should have the name of the property.
+ expect(functionNode.text()).toBe("fn()");
+ });
+
+ it("renders non-TINY mode functions as expected", () => {
+ const stub = functionStubs.get("Named");
+ const { wrapper } = mount({
+ autoExpandDepth: 0,
+ roots: [
+ {
+ path: "root",
+ name: "x",
+ contents: { value: stub },
+ },
+ ],
+ mode: MODE.LONG,
+ });
+
+ const nodes = wrapper.find(".node");
+ const functionNode = nodes.first();
+ // It should have the name of the property.
+ expect(functionNode.text()).toBe("x: function testName()");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/getter-setter.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/getter-setter.test.js
new file mode 100644
index 0000000000..e5fbbff7de
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/getter-setter.test.js
@@ -0,0 +1,106 @@
+/* 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/>. */
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ formatObjectInspector,
+ waitForLoadedProperties,
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+const {
+ makeNodesForProperties,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+const accessorStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessor.js");
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 1,
+ createObjectFront: grip => ObjectFront(grip),
+ mode: MODE.LONG,
+ ...overrides,
+ };
+}
+
+function mount(stub, propsOverride = {}) {
+ const client = { createObjectFront: grip => ObjectFront(grip) };
+
+ const root = { path: "root", name: "root" };
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ x: stub,
+ },
+ },
+ root
+ );
+ root.contents = nodes;
+
+ return mountObjectInspector({
+ client,
+ props: generateDefaults({ roots: [root], ...propsOverride }),
+ });
+}
+
+describe("ObjectInspector - getters & setters", () => {
+ it("renders getters as expected", async () => {
+ const { store, wrapper } = mount(accessorStubs.get("getter"));
+ await waitForLoadedProperties(store, ["root"]);
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("renders setters as expected", async () => {
+ const { store, wrapper } = mount(accessorStubs.get("setter"));
+ await waitForLoadedProperties(store, ["root"]);
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("renders getters and setters as expected", async () => {
+ const { store, wrapper } = mount(accessorStubs.get("getter setter"));
+ await waitForLoadedProperties(store, ["root"]);
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("onInvokeGetterButtonClick + getter", async () => {
+ const onInvokeGetterButtonClick = jest.fn();
+ const { store, wrapper } = mount(accessorStubs.get("getter"), {
+ onInvokeGetterButtonClick,
+ });
+ await waitForLoadedProperties(store, ["root"]);
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("onInvokeGetterButtonClick + setter", async () => {
+ const onInvokeGetterButtonClick = jest.fn();
+ const { store, wrapper } = mount(accessorStubs.get("setter"), {
+ onInvokeGetterButtonClick,
+ });
+ await waitForLoadedProperties(store, ["root"]);
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("onInvokeGetterButtonClick + getter & setter", async () => {
+ const onInvokeGetterButtonClick = jest.fn();
+ const { store, wrapper } = mount(accessorStubs.get("getter setter"), {
+ onInvokeGetterButtonClick,
+ });
+ await waitForLoadedProperties(store, ["root"]);
+ wrapper.update();
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/keyboard-navigation.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/keyboard-navigation.test.js
new file mode 100644
index 0000000000..c36c611a53
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/keyboard-navigation.test.js
@@ -0,0 +1,89 @@
+/* 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/>. */
+
+const {
+ mountObjectInspector,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const { MODE } = require("devtools/client/shared/components/reps/index");
+
+const {
+ formatObjectInspector,
+ waitForDispatch,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const ObjectFront = require("devtools/client/shared/components/test/node/__mocks__/object-front");
+const gripRepStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip`);
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ mode: MODE.LONG,
+ ...overrides,
+ };
+}
+
+function mount(props) {
+ const client = {
+ createObjectFront: grip => ObjectFront(grip),
+ getFrontByID: _id => null,
+ };
+
+ return mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ });
+}
+
+describe("ObjectInspector - keyboard navigation", () => {
+ it("works as expected", async () => {
+ const stub = gripRepStubs.get("testMaxProps");
+
+ const { wrapper, store } = mount({
+ roots: [{ path: "root", contents: { value: stub } }],
+ });
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ wrapper.simulate("focus");
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // Pressing right arrow key should expand the node and lod its properties.
+ const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ simulateKeyDown(wrapper, "ArrowRight");
+ await onPropertiesLoaded;
+ wrapper.update();
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // The child node should be focused.
+ keyNavigate(wrapper, store, "ArrowDown");
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // The root node should be focused again.
+ keyNavigate(wrapper, store, "ArrowLeft");
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // The child node should be focused again.
+ keyNavigate(wrapper, store, "ArrowRight");
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // The root node should be focused again.
+ keyNavigate(wrapper, store, "ArrowUp");
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ wrapper.simulate("blur");
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+});
+
+function keyNavigate(wrapper, store, key) {
+ simulateKeyDown(wrapper, key);
+ wrapper.update();
+}
+
+function simulateKeyDown(wrapper, key) {
+ wrapper.simulate("keydown", {
+ key,
+ preventDefault: () => {},
+ stopPropagation: () => {},
+ });
+}
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/properties.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/properties.test.js
new file mode 100644
index 0000000000..8773cfd49f
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/properties.test.js
@@ -0,0 +1,158 @@
+/* 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/>. */
+
+/* global jest */
+
+const {
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+const gripRepStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+
+const {
+ formatObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ createObjectFront: grip => ObjectFront(grip),
+ ...overrides,
+ };
+}
+
+function getEnumPropertiesMock() {
+ return jest.fn(() => ({
+ slice: () => ({}),
+ }));
+}
+
+function mount(props, { initialState } = {}) {
+ const enumProperties = getEnumPropertiesMock();
+
+ const client = {
+ createObjectFront: grip => ObjectFront(grip, { enumProperties }),
+ getFrontByID: _id => null,
+ };
+
+ const obj = mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ initialState,
+ });
+
+ return { ...obj, enumProperties };
+}
+describe("ObjectInspector - properties", () => {
+ it("does not load properties if properties are already loaded", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+
+ const { enumProperties } = mount(
+ {
+ autoExpandDepth: 1,
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ },
+ {
+ initialState: {
+ objectInspector: {
+ loadedProperties: new Map([
+ ["root", { ownProperties: stub.preview.ownProperties }],
+ ]),
+ evaluations: new Map(),
+ },
+ },
+ }
+ );
+
+ expect(enumProperties.mock.calls).toHaveLength(0);
+ });
+
+ it("calls enumProperties when expandable leaf is clicked", () => {
+ const stub = gripRepStubs.get("testMaxProps");
+ const { enumProperties, wrapper } = mount({
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ createObjectFront: grip => ObjectFront(grip, { enumProperties }),
+ });
+
+ const node = wrapper.find(".node");
+ node.simulate("click");
+
+ // The function is called twice, to get both non-indexed and indexed props.
+ expect(enumProperties.mock.calls).toHaveLength(2);
+ expect(enumProperties.mock.calls[0][0]).toEqual({
+ ignoreNonIndexedProperties: true,
+ });
+ expect(enumProperties.mock.calls[1][0]).toEqual({
+ ignoreIndexedProperties: true,
+ });
+ });
+
+ it("renders uninitialized bindings", () => {
+ const { wrapper } = mount({
+ roots: [
+ {
+ name: "someFoo",
+ path: "root/someFoo",
+ contents: {
+ value: {
+ uninitialized: true,
+ },
+ },
+ },
+ ],
+ });
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("renders unmapped bindings", () => {
+ const { wrapper } = mount({
+ roots: [
+ {
+ name: "someFoo",
+ path: "root/someFoo",
+ contents: {
+ value: {
+ unmapped: true,
+ },
+ },
+ },
+ ],
+ });
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+
+ it("renders unscoped bindings", () => {
+ const { wrapper } = mount({
+ roots: [
+ {
+ name: "someFoo",
+ path: "root/someFoo",
+ contents: {
+ value: {
+ unscoped: true,
+ },
+ },
+ },
+ ],
+ });
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/proxy.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/proxy.test.js
new file mode 100644
index 0000000000..0bf716ccff
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/proxy.test.js
@@ -0,0 +1,133 @@
+/* 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/>. */
+
+/* global jest */
+const {
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const stub = gripStubs.get("testProxy");
+const proxySlots = gripStubs.get("testProxySlots");
+const {
+ formatObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+function generateDefaults(overrides) {
+ return {
+ roots: [
+ {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ },
+ ],
+ autoExpandDepth: 1,
+ mode: MODE.LONG,
+ ...overrides,
+ };
+}
+
+function getEnumPropertiesMock() {
+ return jest.fn(() => ({
+ slice: () => ({}),
+ }));
+}
+
+function getProxySlotsMock() {
+ return jest.fn(() => proxySlots);
+}
+
+function mount(props, { initialState } = {}) {
+ const enumProperties = getEnumPropertiesMock();
+ const getProxySlots = getProxySlotsMock();
+
+ const client = {
+ createObjectFront: grip =>
+ ObjectFront(grip, { enumProperties, getProxySlots }),
+ getFrontByID: _id => null,
+ };
+
+ const obj = mountObjectInspector({
+ client,
+ props: generateDefaults(props),
+ initialState,
+ });
+
+ return { ...obj, enumProperties, getProxySlots };
+}
+
+describe("ObjectInspector - Proxy", () => {
+ it("renders Proxy as expected", () => {
+ const { wrapper, enumProperties, getProxySlots } = mount(
+ {},
+ {
+ initialState: {
+ objectInspector: {
+ // Have the prototype already loaded so the component does not call
+ // enumProperties for the root's properties.
+ loadedProperties: new Map([["root", proxySlots]]),
+ evaluations: new Map(),
+ },
+ },
+ }
+ );
+
+ expect(formatObjectInspector(wrapper)).toMatchSnapshot();
+
+ // enumProperties should not have been called.
+ expect(enumProperties.mock.calls).toHaveLength(0);
+
+ // getProxySlots should not have been called.
+ expect(getProxySlots.mock.calls).toHaveLength(0);
+ });
+
+ it("calls enumProperties on <target> and <handler> clicks", () => {
+ const { wrapper, enumProperties } = mount(
+ {},
+ {
+ initialState: {
+ objectInspector: {
+ // Have the prototype already loaded so the component does not call
+ // enumProperties for the root's properties.
+ loadedProperties: new Map([["root", proxySlots]]),
+ evaluations: new Map(),
+ },
+ },
+ }
+ );
+
+ const nodes = wrapper.find(".node");
+
+ const targetNode = nodes.at(1);
+ const handlerNode = nodes.at(2);
+
+ targetNode.simulate("click");
+ // The function is called twice,
+ // to get both non-indexed and indexed properties.
+ expect(enumProperties.mock.calls).toHaveLength(2);
+ expect(enumProperties.mock.calls[0][0]).toEqual({
+ ignoreNonIndexedProperties: true,
+ });
+ expect(enumProperties.mock.calls[1][0]).toEqual({
+ ignoreIndexedProperties: true,
+ });
+
+ handlerNode.simulate("click");
+ // The function is called twice,
+ // to get both non-indexed and indexed properties.
+ expect(enumProperties.mock.calls).toHaveLength(4);
+ expect(enumProperties.mock.calls[2][0]).toEqual({
+ ignoreNonIndexedProperties: true,
+ });
+ expect(enumProperties.mock.calls[3][0]).toEqual({
+ ignoreIndexedProperties: true,
+ });
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/should-item-update.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/should-item-update.test.js
new file mode 100644
index 0000000000..645b4ede6c
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/should-item-update.test.js
@@ -0,0 +1,96 @@
+/* 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/>. */
+
+/* global jest */
+
+const {
+ mountObjectInspector,
+} = require("devtools/client/shared/components/test/node/components/object-inspector/test-utils");
+const ObjectFront = require("devtools/client/shared/components/test/node/__mocks__/object-front");
+const {
+ LongStringFront,
+} = require("devtools/client/shared/components/test/node/__mocks__/string-front");
+
+const longStringStubs = require(`devtools/client/shared/components/test/node/stubs/reps/long-string`);
+const gripStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip`);
+
+function mount(stub) {
+ const root = {
+ path: "root",
+ contents: {
+ value: stub,
+ },
+ };
+
+ const { wrapper } = mountObjectInspector({
+ client: {
+ createObjectFront: grip => ObjectFront(grip),
+ createLongStringFront: grip => LongStringFront(grip),
+ getFrontByID: _id => null,
+ },
+ props: {
+ roots: [root],
+ },
+ });
+
+ return { wrapper, root };
+}
+
+describe("shouldItemUpdate", () => {
+ it("for longStrings", () => {
+ shouldItemUpdateCheck(longStringStubs.get("testUnloadedFullText"), true, 2);
+ });
+
+ it("for basic object", () => {
+ shouldItemUpdateCheck(gripStubs.get("testBasic"), false, 1);
+ });
+});
+
+function shouldItemUpdateCheck(
+ stub,
+ shouldItemUpdateResult,
+ renderCallsLength
+) {
+ const { root, wrapper } = mount(stub);
+
+ const shouldItemUpdateSpy = getShouldItemUpdateSpy(wrapper);
+ const treeNodeRenderSpy = getTreeNodeRenderSpy(wrapper);
+
+ updateObjectInspectorTree(wrapper);
+
+ checkShouldItemUpdate(shouldItemUpdateSpy, root, shouldItemUpdateResult);
+ expect(treeNodeRenderSpy.mock.calls).toHaveLength(renderCallsLength);
+}
+
+function checkShouldItemUpdate(spy, item, result) {
+ expect(spy.mock.calls).toHaveLength(1);
+ expect(spy.mock.calls[0][0]).toBe(item);
+ expect(spy.mock.calls[0][1]).toBe(item);
+ expect(spy.mock.results[0].value).toBe(result);
+}
+
+function getInstance(wrapper, selector) {
+ return wrapper
+ .find(selector)
+ .first()
+ .instance();
+}
+
+function getShouldItemUpdateSpy(wrapper) {
+ return jest.spyOn(
+ getInstance(wrapper, "ObjectInspector"),
+ "shouldItemUpdate"
+ );
+}
+
+function getTreeNodeRenderSpy(wrapper) {
+ return jest.spyOn(getInstance(wrapper, "TreeNode"), "render");
+}
+
+function updateObjectInspectorTree(wrapper) {
+ // Update the ObjectInspector first to propagate its updated options to the
+ // Tree component.
+ getInstance(wrapper, "ObjectInspector").forceUpdate();
+ getInstance(wrapper, "Tree").forceUpdate();
+}
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/component/window.test.js b/devtools/client/shared/components/test/node/components/object-inspector/component/window.test.js
new file mode 100644
index 0000000000..c0b1036385
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/component/window.test.js
@@ -0,0 +1,96 @@
+/* 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/>. */
+
+const {
+ createNode,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+const {
+ waitForDispatch,
+ mountObjectInspector,
+} = require("resource://devtools/client/shared/components/test/node/components/object-inspector/test-utils.js");
+
+const gripWindowStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+const ObjectFront = require("resource://devtools/client/shared/components/test/node/__mocks__/object-front.js");
+const windowNode = createNode({
+ name: "window",
+ contents: { value: gripWindowStubs.get("Window")._grip },
+});
+
+const client = {
+ createObjectFront: grip => ObjectFront(grip),
+ getFrontByID: _id => null,
+};
+
+function generateDefaults(overrides) {
+ return {
+ autoExpandDepth: 0,
+ roots: [windowNode],
+ ...overrides,
+ };
+}
+
+describe("ObjectInspector - dimTopLevelWindow", () => {
+ it("renders window as expected when dimTopLevelWindow is true", async () => {
+ const props = generateDefaults({
+ dimTopLevelWindow: true,
+ });
+
+ const { wrapper, store } = mountObjectInspector({ client, props });
+ let nodes = wrapper.find(".node");
+ const node = nodes.at(0);
+
+ expect(nodes.at(0).hasClass("lessen")).toBeTruthy();
+ expect(wrapper).toMatchSnapshot();
+
+ const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ node.simulate("click");
+ await onPropertiesLoaded;
+ wrapper.update();
+
+ nodes = wrapper.find(".node");
+ expect(nodes.at(0).hasClass("lessen")).toBeFalsy();
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("renders collapsed top-level window when dimTopLevelWindow =false", () => {
+ // The window node should not have the "lessen" class when
+ // dimTopLevelWindow is falsy.
+ const props = generateDefaults();
+ const { wrapper } = mountObjectInspector({ client, props });
+
+ expect(wrapper.find(".node.lessen").exists()).toBeFalsy();
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it("renders sub-level window", async () => {
+ // The window node should not have the "lessen" class when it is not at
+ // top level.
+ const root = createNode({
+ name: "root",
+ contents: [windowNode],
+ });
+
+ const props = generateDefaults({
+ roots: [root],
+ dimTopLevelWindow: true,
+ injectWaitService: true,
+ });
+ const { wrapper, store } = mountObjectInspector({ client, props });
+
+ let nodes = wrapper.find(".node");
+ const node = nodes.at(0);
+ const onPropertiesLoaded = waitForDispatch(store, "NODE_PROPERTIES_LOADED");
+ node.simulate("click");
+ await onPropertiesLoaded;
+ wrapper.update();
+
+ nodes = wrapper.find(".node");
+ const win = nodes.at(1);
+
+ // Make sure we target the window object.
+ expect(win.find(".objectBox-Window").exists()).toBeTruthy();
+ expect(win.hasClass("lessen")).toBeFalsy();
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/test-utils.js b/devtools/client/shared/components/test/node/components/object-inspector/test-utils.js
new file mode 100644
index 0000000000..79d3e41161
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/test-utils.js
@@ -0,0 +1,231 @@
+/* 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/>. */
+
+const { mount } = require("enzyme");
+const { createFactory } = require("resource://devtools/client/shared/vendor/react.js");
+
+const { Provider } = require("resource://devtools/client/shared/vendor/react-redux.js");
+const {
+ combineReducers,
+ createStore,
+ applyMiddleware,
+} = require("resource://devtools/client/shared/vendor/redux.js");
+
+const { thunk } = require("resource://devtools/client/shared/redux/middleware/thunk.js");
+const {
+ waitUntilService,
+} = require("resource://devtools/client/shared/redux/middleware/wait-service.js");
+
+/**
+ * Redux store utils
+ * @module utils/create-store
+ */
+const objectInspector = require("resource://devtools/client/shared/components/object-inspector/index.js");
+const {
+ getLoadedProperties,
+ getLoadedPropertyKeys,
+ getExpandedPaths,
+ getExpandedPathKeys,
+} = require("resource://devtools/client/shared/components/object-inspector/reducer.js");
+
+const ObjectInspector = createFactory(objectInspector.ObjectInspector);
+
+const {
+ NAME: WAIT_UNTIL_TYPE,
+} = require("resource://devtools/client/shared/redux/middleware/wait-service.js");
+
+/*
+ * Takes an Enzyme wrapper (obtained with mount/shallow/…) and
+ * returns a stringified version of the ObjectInspector, e.g.
+ *
+ * ▼ Map { "a" → "value-a", "b" → "value-b" }
+ * | size : 2
+ * | ▼ <entries>
+ * | | ▼ 0 : "a" → "value-a"
+ * | | | <key> : "a"
+ * | | | <value> : "value-a"
+ * | | ▼ 1 : "b" → "value-b"
+ * | | | <key> : "b"
+ * | | | <value> : "value-b"
+ * | ▼ <prototype> : Object { … }
+ *
+ */
+function formatObjectInspector(wrapper) {
+ const hasFocusedNode = wrapper.find(".tree-node.focused").length > 0;
+ const textTree = wrapper
+ .find(".tree-node")
+ .map(node => {
+ const indentStr = "| ".repeat((node.prop("aria-level") || 1) - 1);
+ // Need to target .arrow or Enzyme will also match the ArrowExpander
+ // component.
+ const arrow = node.find(".arrow");
+ let arrowStr = " ";
+ if (arrow.exists()) {
+ arrowStr = arrow.hasClass("expanded") ? "▼ " : "▶︎ ";
+ } else {
+ arrowStr = " ";
+ }
+
+ const icon = node
+ .find(".node")
+ .first()
+ .hasClass("block")
+ ? "☲ "
+ : "";
+ let text = `${indentStr}${arrowStr}${icon}${getSanitizedNodeText(node)}`;
+
+ if (node.find("button.invoke-getter").exists()) {
+ text = `${text}(>>)`;
+ }
+
+ if (!hasFocusedNode) {
+ return text;
+ }
+ return node.hasClass("focused") ? `[ ${text} ]` : ` ${text}`;
+ })
+ .join("\n");
+ // Wrap the text representation in new lines so it keeps alignment between
+ // tree nodes.
+ return `\n${textTree}\n`;
+}
+
+function getSanitizedNodeText(node) {
+ // Stripping off the invisible space used in the indent.
+ return node.text().replace(/^\u200B+/, "");
+}
+
+/**
+ * Wait for a specific action type to be dispatched.
+ *
+ * @param {Object} store: Redux store
+ * @param {String} type: type of the actin to wait for
+ * @return {Promise}
+ */
+function waitForDispatch(store, type) {
+ return new Promise(resolve => {
+ store.dispatch({
+ type: WAIT_UNTIL_TYPE,
+ predicate: action => action.type === type,
+ run: (dispatch, getState, action) => {
+ resolve(action);
+ },
+ });
+ });
+}
+
+/**
+ * Wait until the condition evaluates to something truthy
+ * @param {function} condition: function that we need for returning something
+ * truthy.
+ * @param {int} interval: Time to wait before trying to evaluate condition again
+ * @param {int} maxTries: Number of evaluation to try.
+ */
+async function waitFor(condition, interval = 50, maxTries = 100) {
+ let res = condition();
+ while (!res) {
+ await new Promise(done => setTimeout(done, interval));
+ maxTries--;
+
+ if (maxTries <= 0) {
+ throw new Error("waitFor - maxTries limit hit");
+ }
+
+ res = condition();
+ }
+ return res;
+}
+
+/**
+ * Wait until the state has all the expected keys for the loadedProperties
+ * state prop.
+ * @param {Redux Store} store: function that we need for returning something
+ * truthy.
+ * @param {Array} expectedKeys: Array of stringified keys.
+ * @param {int} interval: Time to wait before trying to evaluate condition again
+ * @param {int} maxTries: Number of evaluation to try.
+ */
+function waitForLoadedProperties(store, expectedKeys, interval, maxTries) {
+ return waitFor(
+ () => storeHasLoadedPropertiesKeys(store, expectedKeys),
+ interval,
+ maxTries
+ );
+}
+
+function storeHasLoadedPropertiesKeys(store, expectedKeys) {
+ return expectedKeys.every(key => storeHasLoadedProperty(store, key));
+}
+
+function storeHasLoadedProperty(store, key) {
+ return getLoadedPropertyKeys(store.getState()).some(
+ k => k.toString() === key
+ );
+}
+
+function storeHasExactLoadedProperties(store, expectedKeys) {
+ return (
+ expectedKeys.length === getLoadedProperties(store.getState()).size &&
+ expectedKeys.every(key => storeHasLoadedProperty(store, key))
+ );
+}
+
+function storeHasExpandedPaths(store, expectedKeys) {
+ return expectedKeys.every(key => storeHasExpandedPath(store, key));
+}
+
+function storeHasExpandedPath(store, key) {
+ return getExpandedPathKeys(store.getState()).some(k => k.toString() === key);
+}
+
+function storeHasExactExpandedPaths(store, expectedKeys) {
+ return (
+ expectedKeys.length === getExpandedPaths(store.getState()).size &&
+ expectedKeys.every(key => storeHasExpandedPath(store, key))
+ );
+}
+
+function createOiStore(client, initialState = {}) {
+ const reducers = { objectInspector: objectInspector.reducer.default };
+ return configureStore({
+ thunkArgs: { client },
+ })(combineReducers(reducers), initialState);
+}
+
+const configureStore = (opts = {}) => {
+ const middleware = [thunk(opts.thunkArgs), waitUntilService];
+ return applyMiddleware(...middleware)(createStore);
+};
+
+function mountObjectInspector({ props, client, initialState = {} }) {
+ if (initialState.objectInspector) {
+ initialState.objectInspector = {
+ expandedPaths: new Set(),
+ loadedProperties: new Map(),
+ ...initialState.objectInspector,
+ };
+ }
+ const store = createOiStore(client, initialState);
+ const wrapper = mount(
+ createFactory(Provider)({ store }, ObjectInspector(props))
+ );
+
+ const tree = wrapper.find(".tree");
+
+ return { store, tree, wrapper, client };
+}
+
+module.exports = {
+ formatObjectInspector,
+ storeHasExpandedPaths,
+ storeHasExpandedPath,
+ storeHasExactExpandedPaths,
+ storeHasLoadedPropertiesKeys,
+ storeHasLoadedProperty,
+ storeHasExactLoadedProperties,
+ waitFor,
+ waitForDispatch,
+ waitForLoadedProperties,
+ mountObjectInspector,
+ createStore: createOiStore,
+};
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/__snapshots__/promises.test.js.snap b/devtools/client/shared/components/test/node/components/object-inspector/utils/__snapshots__/promises.test.js.snap
new file mode 100644
index 0000000000..75903c0ff1
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/__snapshots__/promises.test.js.snap
@@ -0,0 +1,49 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`promises utils function makeNodesForPromiseProperties 1`] = `
+Array [
+ Object {
+ "contents": Object {
+ "value": "rejected",
+ },
+ "meta": undefined,
+ "name": "<state>",
+ "parent": Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server2.conn2.child1/obj36",
+ "class": "Promise",
+ "type": "object",
+ },
+ },
+ "path": "root",
+ },
+ "path": "root◦<state>",
+ "propertyName": undefined,
+ "type": Symbol(<state>),
+ },
+ Object {
+ "contents": Object {
+ "front": null,
+ "value": Object {
+ "type": "3",
+ },
+ },
+ "meta": undefined,
+ "name": "<reason>",
+ "parent": Object {
+ "contents": Object {
+ "value": Object {
+ "actor": "server2.conn2.child1/obj36",
+ "class": "Promise",
+ "type": "object",
+ },
+ },
+ "path": "root",
+ },
+ "path": "root◦<reason>",
+ "propertyName": undefined,
+ "type": Symbol(<reason>),
+ },
+]
+`;
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/create-node.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/create-node.test.js
new file mode 100644
index 0000000000..792ad2dfb0
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/create-node.test.js
@@ -0,0 +1,87 @@
+/* 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/>. */
+
+const { createNode, NODE_TYPES } = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+describe("createNode", () => {
+ it("returns null when contents is undefined", () => {
+ expect(createNode({ name: "name" })).toBeNull();
+ });
+
+ it("does not return null when contents is null", () => {
+ expect(
+ createNode({
+ name: "name",
+ path: "path",
+ contents: null,
+ })
+ ).not.toBe(null);
+ });
+
+ it("returns the expected object when parent is undefined", () => {
+ const node = createNode({
+ name: "name",
+ path: "path",
+ contents: "contents",
+ });
+ expect(node).toEqual({
+ name: "name",
+ path: node.path,
+ contents: "contents",
+ type: NODE_TYPES.GRIP,
+ });
+ });
+
+ it("returns the expected object when parent is not null", () => {
+ const root = createNode({ name: "name", contents: null });
+ const child = createNode({
+ parent: root,
+ name: "name",
+ path: "path",
+ contents: "contents",
+ });
+ expect(child.parent).toEqual(root);
+ });
+
+ it("returns the expected object when type is not undefined", () => {
+ const root = createNode({ name: "name", contents: null });
+ const child = createNode({
+ parent: root,
+ name: "name",
+ path: "path",
+ contents: "contents",
+ type: NODE_TYPES.BUCKET,
+ });
+
+ expect(child.type).toEqual(NODE_TYPES.BUCKET);
+ });
+
+ it("uses the name property for the path when path is not provided", () => {
+ expect(
+ createNode({ name: "name", contents: "contents" }).path.toString()
+ ).toBe("name");
+ });
+
+ it("wraps the path in a Symbol when provided", () => {
+ expect(
+ createNode({
+ name: "name",
+ path: "path",
+ contents: "contents",
+ }).path.toString()
+ ).toBe("path");
+ });
+
+ it("uses parent path to compute its path", () => {
+ const root = createNode({ name: "root", contents: null });
+ expect(
+ createNode({
+ parent: root,
+ name: "name",
+ path: "path",
+ contents: "contents",
+ }).path.toString()
+ ).toBe("root◦path");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/get-children.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/get-children.test.js
new file mode 100644
index 0000000000..f57f82073d
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/get-children.test.js
@@ -0,0 +1,278 @@
+/* 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/>. */
+
+const accessorStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessor.js");
+const performanceStubs = require("resource://devtools/client/shared/components/test/node/stubs/object-inspector/performance.js");
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripEntryStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-entry.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+
+const {
+ createNode,
+ getChildren,
+ getValue,
+ makeNodesForProperties,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+function createRootNodeWithAccessorProperty(accessorStub) {
+ const node = { name: "root", path: "rootpath" };
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ x: accessorStub,
+ },
+ },
+ node
+ );
+ node.contents = nodes;
+
+ return createNode(node);
+}
+
+describe("getChildren", () => {
+ it("accessors - getter", () => {
+ const children = getChildren({
+ item: createRootNodeWithAccessorProperty(accessorStubs.get("getter")),
+ });
+
+ const names = children.map(n => n.name);
+ const paths = children.map(n => n.path.toString());
+
+ expect(names).toEqual(["x", "<get x()>"]);
+ expect(paths).toEqual(["rootpath◦x", "rootpath◦<get x()>"]);
+ });
+
+ it("accessors - setter", () => {
+ const children = getChildren({
+ item: createRootNodeWithAccessorProperty(accessorStubs.get("setter")),
+ });
+
+ const names = children.map(n => n.name);
+ const paths = children.map(n => n.path.toString());
+
+ expect(names).toEqual(["x", "<set x()>"]);
+ expect(paths).toEqual(["rootpath◦x", "rootpath◦<set x()>"]);
+ });
+
+ it("accessors - getter & setter", () => {
+ const children = getChildren({
+ item: createRootNodeWithAccessorProperty(
+ accessorStubs.get("getter setter")
+ ),
+ });
+
+ const names = children.map(n => n.name);
+ const paths = children.map(n => n.path.toString());
+
+ expect(names).toEqual(["x", "<get x()>", "<set x()>"]);
+ expect(paths).toEqual([
+ "rootpath◦x",
+ "rootpath◦<get x()>",
+ "rootpath◦<set x()>",
+ ]);
+ });
+
+ it("returns the expected nodes for Proxy", () => {
+ const proxyNode = createNode({
+ name: "root",
+ path: "rootpath",
+ contents: { value: gripStubs.get("testProxy") },
+ });
+ const loadedProperties = new Map([
+ [proxyNode.path, gripStubs.get("testProxySlots")],
+ ]);
+ const nodes = getChildren({ item: proxyNode, loadedProperties });
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual(["<target>", "<handler>"]);
+ expect(paths).toEqual(["rootpath◦<target>", "rootpath◦<handler>"]);
+ });
+
+ it("safeGetterValues", () => {
+ const stub = performanceStubs.get("timing");
+ const root = createNode({
+ name: "root",
+ path: "rootpath",
+ contents: {
+ value: {
+ actor: "rootactor",
+ type: "object",
+ },
+ },
+ });
+ const nodes = getChildren({
+ item: root,
+ loadedProperties: new Map([[root.path, stub]]),
+ });
+
+ const nodeEntries = nodes.map(n => [n.name, getValue(n)]);
+ const nodePaths = nodes.map(n => n.path.toString());
+
+ const childrenEntries = [
+ ["connectEnd", 1500967716401],
+ ["connectStart", 1500967716401],
+ ["domComplete", 1500967716719],
+ ["domContentLoadedEventEnd", 1500967716715],
+ ["domContentLoadedEventStart", 1500967716696],
+ ["domInteractive", 1500967716552],
+ ["domLoading", 1500967716426],
+ ["domainLookupEnd", 1500967716401],
+ ["domainLookupStart", 1500967716401],
+ ["fetchStart", 1500967716401],
+ ["loadEventEnd", 1500967716720],
+ ["loadEventStart", 1500967716719],
+ ["navigationStart", 1500967716401],
+ ["redirectEnd", 0],
+ ["redirectStart", 0],
+ ["requestStart", 1500967716401],
+ ["responseEnd", 1500967716401],
+ ["responseStart", 1500967716401],
+ ["secureConnectionStart", 1500967716401],
+ ["unloadEventEnd", 0],
+ ["unloadEventStart", 0],
+ ["<prototype>", stub.prototype],
+ ];
+ const childrenPaths = childrenEntries.map(([name]) => `rootpath◦${name}`);
+
+ expect(nodeEntries).toEqual(childrenEntries);
+ expect(nodePaths).toEqual(childrenPaths);
+ });
+
+ it("gets data from the cache when it exists", () => {
+ const mapNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("testSymbolKeyedMap"),
+ },
+ });
+ const cachedData = "";
+ const children = getChildren({
+ cachedNodes: new Map([[mapNode.path, cachedData]]),
+ item: mapNode,
+ });
+ expect(children).toBe(cachedData);
+ });
+
+ it("returns an empty array if the node does not represent an object", () => {
+ const node = createNode({ name: "root", contents: { value: 42 } });
+ expect(
+ getChildren({
+ item: node,
+ })
+ ).toEqual([]);
+ });
+
+ it("returns an empty array if a grip node has no loaded properties", () => {
+ const node = createNode({
+ name: "root",
+ contents: { value: gripMapStubs.get("testMaxProps") },
+ });
+ expect(
+ getChildren({
+ item: node,
+ })
+ ).toEqual([]);
+ });
+
+ it("adds children to cache when a grip node has loaded properties", () => {
+ const stub = performanceStubs.get("timing");
+ const cachedNodes = new Map();
+
+ const rootNode = createNode({
+ name: "root",
+ contents: {
+ value: {
+ actor: "rootactor",
+ type: "object",
+ },
+ },
+ });
+ const children = getChildren({
+ cachedNodes,
+ item: rootNode,
+ loadedProperties: new Map([[rootNode.path, stub]]),
+ });
+ expect(cachedNodes.get(rootNode.path)).toBe(children);
+ });
+
+ it("adds children to cache when it already has some", () => {
+ const cachedNodes = new Map();
+ const children = [""];
+ const rootNode = createNode({ name: "root", contents: children });
+ getChildren({
+ cachedNodes,
+ item: rootNode,
+ });
+ expect(cachedNodes.get(rootNode.path)).toBe(children);
+ });
+
+ it("adds children to cache on a node with accessors", () => {
+ const cachedNodes = new Map();
+ const node = createRootNodeWithAccessorProperty(
+ accessorStubs.get("getter setter")
+ );
+
+ const children = getChildren({
+ cachedNodes,
+ item: node,
+ });
+ expect(cachedNodes.get(node.path)).toBe(children);
+ });
+
+ it("adds children to cache on a map entry node", () => {
+ const cachedNodes = new Map();
+ const node = createNode({
+ name: "root",
+ contents: { value: gripEntryStubs.get("A → 0") },
+ });
+ const children = getChildren({
+ cachedNodes,
+ item: node,
+ });
+ expect(cachedNodes.get(node.path)).toBe(children);
+ });
+
+ it("adds children to cache on a proxy node having loaded props", () => {
+ const cachedNodes = new Map();
+ const node = createNode({
+ name: "root",
+ contents: { value: gripStubs.get("testProxy") },
+ });
+ const children = getChildren({
+ cachedNodes,
+ item: node,
+ loadedProperties: new Map([[node.path, gripStubs.get("testProxySlots")]]),
+ });
+ expect(cachedNodes.get(node.path)).toBe(children);
+ });
+
+ it("doesn't cache children on node with buckets and no loaded props", () => {
+ const cachedNodes = new Map();
+ const node = createNode({
+ name: "root",
+ contents: { value: gripArrayStubs.get("Array(234)") },
+ });
+ getChildren({
+ cachedNodes,
+ item: node,
+ });
+ expect(cachedNodes.has(node.path)).toBeFalsy();
+ });
+
+ it("caches children on a node with buckets having loaded props", () => {
+ const cachedNodes = new Map();
+ const node = createNode({
+ name: "root",
+ contents: { value: gripArrayStubs.get("Array(234)") },
+ });
+ const children = getChildren({
+ cachedNodes,
+ item: node,
+ loadedProperties: new Map([[node.path, { prototype: {} }]]),
+ });
+ expect(cachedNodes.get(node.path)).toBe(children);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/get-closest-grip-node.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/get-closest-grip-node.test.js
new file mode 100644
index 0000000000..9aa7e127a8
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/get-closest-grip-node.test.js
@@ -0,0 +1,52 @@
+/* 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/>. */
+
+const {
+ createNode,
+ getClosestGripNode,
+ makeNodesForEntries,
+ makeNumericalBuckets,
+} = require("devtools/client/shared/components/object-inspector/utils/node");
+
+const gripRepStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip`);
+const gripArrayRepStubs = require(`devtools/client/shared/components/test/node/stubs/reps/grip-array`);
+
+describe("getClosestGripNode", () => {
+ it("returns grip node itself", () => {
+ const stub = gripRepStubs.get("testMoreThanMaxProps");
+ const node = createNode({ name: "root", contents: { value: stub } });
+ expect(getClosestGripNode(node)).toBe(node);
+ });
+
+ it("returns the expected node for entries node", () => {
+ const mapStubNode = createNode({ name: "map", contents: { value: {} } });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(getClosestGripNode(entriesNode)).toBe(mapStubNode);
+ });
+
+ it("returns the expected node for bucket node", () => {
+ const grip = gripArrayRepStubs.get("testMaxProps");
+ const root = createNode({ name: "root", contents: { value: grip } });
+ const [bucket] = makeNumericalBuckets(root);
+ expect(getClosestGripNode(bucket)).toBe(root);
+ });
+
+ it("returns the expected node for sub-bucket node", () => {
+ const grip = gripArrayRepStubs.get("testMaxProps");
+ const root = createNode({ name: "root", contents: { value: grip } });
+ const [bucket] = makeNumericalBuckets(root);
+ const [subBucket] = makeNumericalBuckets(bucket);
+ expect(getClosestGripNode(subBucket)).toBe(root);
+ });
+
+ it("returns the expected node for deep sub-bucket node", () => {
+ const grip = gripArrayRepStubs.get("testMaxProps");
+ const root = createNode({ name: "root", contents: { value: grip } });
+ let [bucket] = makeNumericalBuckets(root);
+ for (let i = 0; i < 10; i++) {
+ bucket = makeNumericalBuckets({ ...bucket })[0];
+ }
+ expect(getClosestGripNode(bucket)).toBe(root);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/get-value.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/get-value.test.js
new file mode 100644
index 0000000000..29f0c0ffce
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/get-value.test.js
@@ -0,0 +1,91 @@
+/* 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/>. */
+
+const { getValue } = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+describe("getValue", () => {
+ it("get the value from contents.value", () => {
+ let item = {
+ contents: {
+ value: "my value",
+ },
+ };
+ expect(getValue(item)).toBe("my value");
+
+ item = {
+ contents: {
+ value: 0,
+ },
+ };
+ expect(getValue(item)).toBe(0);
+
+ item = {
+ contents: {
+ value: false,
+ },
+ };
+ expect(getValue(item)).toBe(false);
+
+ item = {
+ contents: {
+ value: null,
+ },
+ };
+ expect(getValue(item)).toBe(null);
+ });
+
+ it("get the value from contents.getterValue", () => {
+ let item = {
+ contents: {
+ getterValue: "my getter value",
+ },
+ };
+ expect(getValue(item)).toBe("my getter value");
+
+ item = {
+ contents: {
+ getterValue: 0,
+ },
+ };
+ expect(getValue(item)).toBe(0);
+
+ item = {
+ contents: {
+ getterValue: false,
+ },
+ };
+ expect(getValue(item)).toBe(false);
+
+ item = {
+ contents: {
+ getterValue: null,
+ },
+ };
+ expect(getValue(item)).toBe(null);
+ });
+
+ it("get the value from getter and setter", () => {
+ let item = {
+ contents: {
+ get: "get",
+ },
+ };
+ expect(getValue(item)).toEqual({ get: "get" });
+
+ item = {
+ contents: {
+ set: "set",
+ },
+ };
+ expect(getValue(item)).toEqual({ set: "set" });
+
+ item = {
+ contents: {
+ get: "get",
+ set: "set",
+ },
+ };
+ expect(getValue(item)).toEqual({ get: "get", set: "set" });
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/make-node-for-properties.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/make-node-for-properties.test.js
new file mode 100644
index 0000000000..da0a221531
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/make-node-for-properties.test.js
@@ -0,0 +1,295 @@
+/* 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/>. */
+
+const {
+ createNode,
+ makeNodesForProperties,
+ nodeIsDefaultProperties,
+ nodeIsEntries,
+ nodeIsMapEntry,
+ nodeIsPrototype,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+
+const root = {
+ path: "root",
+ contents: {
+ value: gripArrayStubs.get("testBasic"),
+ },
+};
+
+const objProperties = {
+ ownProperties: {
+ "0": {
+ value: {},
+ },
+ "2": {},
+ length: {
+ value: 3,
+ },
+ },
+ prototype: {
+ type: "object",
+ actor: "server2.conn1.child1/obj618",
+ class: "bla",
+ },
+};
+
+describe("makeNodesForProperties", () => {
+ it("kitchen sink", () => {
+ const nodes = makeNodesForProperties(objProperties, root);
+
+ const names = nodes.map(n => n.name);
+ expect(names).toEqual(["0", "length", "<prototype>"]);
+
+ const paths = nodes.map(n => n.path.toString());
+ expect(paths).toEqual(["root◦0", "root◦length", "root◦<prototype>"]);
+ });
+
+ it("includes getters and setters", () => {
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ foo: { value: "foo" },
+ bar: {
+ get: {
+ type: "object",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ baz: {
+ get: {
+ type: "undefined",
+ },
+ set: {
+ type: "object",
+ },
+ },
+ },
+ prototype: {
+ class: "bla",
+ },
+ },
+ root
+ );
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual([
+ "bar",
+ "baz",
+ "foo",
+ "<get bar()>",
+ "<set baz()>",
+ "<prototype>",
+ ]);
+
+ expect(paths).toEqual([
+ "root◦bar",
+ "root◦baz",
+ "root◦foo",
+ "root◦<get bar()>",
+ "root◦<set baz()>",
+ "root◦<prototype>",
+ ]);
+ });
+
+ it("does not include unrelevant properties", () => {
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ foo: undefined,
+ bar: null,
+ baz: {},
+ },
+ },
+ root
+ );
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path);
+
+ expect(names).toEqual([]);
+ expect(paths).toEqual([]);
+ });
+
+ it("sorts keys", () => {
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ bar: { value: {} },
+ 1: { value: {} },
+ 11: { value: {} },
+ 2: { value: {} },
+ _bar: { value: {} },
+ },
+ prototype: {
+ class: "bla",
+ },
+ },
+ root
+ );
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual(["1", "2", "11", "_bar", "bar", "<prototype>"]);
+ expect(paths).toEqual([
+ "root◦1",
+ "root◦2",
+ "root◦11",
+ "root◦_bar",
+ "root◦bar",
+ "root◦<prototype>",
+ ]);
+ });
+
+ it("prototype is included", () => {
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ bar: { value: {} },
+ },
+ prototype: { value: {}, class: "bla" },
+ },
+ root
+ );
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual(["bar", "<prototype>"]);
+ expect(paths).toEqual(["root◦bar", "root◦<prototype>"]);
+
+ expect(nodeIsPrototype(nodes[1])).toBe(true);
+ });
+
+ it("window object", () => {
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ bar: {
+ value: {},
+ get: { type: "function" },
+ set: { type: "function" },
+ },
+ location: { value: {} },
+ onload: {
+ get: { type: "function" },
+ set: { type: "function" },
+ },
+ },
+ class: "Window",
+ },
+ {
+ path: "root",
+ contents: { value: { class: "Window" } },
+ }
+ );
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path);
+
+ expect(names).toEqual([
+ "bar",
+ "<default properties>",
+ "<get bar()>",
+ "<set bar()>",
+ ]);
+ expect(paths).toEqual([
+ "root◦bar",
+ "root◦<default properties>",
+ "root◦<get bar()>",
+ "root◦<set bar()>",
+ ]);
+
+ const defaultPropertyNode = nodes[1];
+ expect(nodeIsDefaultProperties(defaultPropertyNode)).toBe(true);
+
+ const defaultPropNames = defaultPropertyNode.contents.map(n => n.name);
+ const defaultPropPath = defaultPropertyNode.contents.map(n => n.path);
+ expect(defaultPropNames).toEqual([
+ "location",
+ "onload",
+ "<get onload()>",
+ "<set onload()>",
+ ]);
+ expect(defaultPropPath).toEqual([
+ "root◦<default properties>◦location",
+ "root◦<default properties>◦onload",
+ "root◦<default properties>◦<get onload()>",
+ "root◦<default properties>◦<set onload()>",
+ ]);
+ });
+
+ it("object with entries", () => {
+ const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+
+ const mapNode = createNode({
+ name: "map",
+ path: "root",
+ contents: {
+ value: gripMapStubs.get("testSymbolKeyedMap"),
+ },
+ });
+
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ size: { value: 1 },
+ custom: { value: "customValue" },
+ },
+ },
+ mapNode
+ );
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual(["custom", "size", "<entries>"]);
+ expect(paths).toEqual(["root◦custom", "root◦size", "root◦<entries>"]);
+
+ const entriesNode = nodes[2];
+ expect(nodeIsEntries(entriesNode)).toBe(true);
+ });
+
+ it("quotes property names", () => {
+ const nodes = makeNodesForProperties(
+ {
+ ownProperties: {
+ // Numbers are ok.
+ 332217: { value: {} },
+ "needs-quotes": { value: {} },
+ unquoted: { value: {} },
+ "": { value: {} },
+ },
+ prototype: {
+ class: "WindowPrototype",
+ },
+ },
+ root
+ );
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual([
+ '""',
+ "332217",
+ '"needs-quotes"',
+ "unquoted",
+ "<prototype>",
+ ]);
+ expect(paths).toEqual([
+ 'root◦""',
+ "root◦332217",
+ 'root◦"needs-quotes"',
+ "root◦unquoted",
+ "root◦<prototype>",
+ ]);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/make-numerical-buckets.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/make-numerical-buckets.test.js
new file mode 100644
index 0000000000..02fb1a3bc5
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/make-numerical-buckets.test.js
@@ -0,0 +1,138 @@
+/* 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/>. */
+
+const {
+ createNode,
+ makeNumericalBuckets,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+
+describe("makeNumericalBuckets", () => {
+ it("handles simple numerical buckets", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ const nodes = makeNumericalBuckets(node);
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual(["[0…99]", "[100…199]", "[200…233]"]);
+
+ expect(paths).toEqual(["root◦[0…99]", "root◦[100…199]", "root◦[200…233]"]);
+ });
+
+ // TODO: Re-enable when we have support for lonely node.
+ it.skip("does not create a numerical bucket for a single node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(101)"),
+ },
+ });
+ const nodes = makeNumericalBuckets(node);
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual(["[0…99]", "100"]);
+
+ expect(paths).toEqual(["root◦bucket_0-99", "root◦100"]);
+ });
+
+ // TODO: Re-enable when we have support for lonely node.
+ it.skip("does create a numerical bucket for two node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ const nodes = makeNumericalBuckets(node);
+
+ const names = nodes.map(n => n.name);
+ const paths = nodes.map(n => n.path.toString());
+
+ expect(names).toEqual(["[0…99]", "[100…101]"]);
+
+ expect(paths).toEqual(["root◦bucket_0-99", "root◦bucket_100-101"]);
+ });
+
+ it("creates sub-buckets when needed", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(23456)"),
+ },
+ });
+ const nodes = makeNumericalBuckets(node);
+ const names = nodes.map(n => n.name);
+
+ expect(names).toEqual([
+ "[0…999]",
+ "[1000…1999]",
+ "[2000…2999]",
+ "[3000…3999]",
+ "[4000…4999]",
+ "[5000…5999]",
+ "[6000…6999]",
+ "[7000…7999]",
+ "[8000…8999]",
+ "[9000…9999]",
+ "[10000…10999]",
+ "[11000…11999]",
+ "[12000…12999]",
+ "[13000…13999]",
+ "[14000…14999]",
+ "[15000…15999]",
+ "[16000…16999]",
+ "[17000…17999]",
+ "[18000…18999]",
+ "[19000…19999]",
+ "[20000…20999]",
+ "[21000…21999]",
+ "[22000…22999]",
+ "[23000…23455]",
+ ]);
+
+ const firstBucketNodes = makeNumericalBuckets(nodes[0]);
+ const firstBucketNames = firstBucketNodes.map(n => n.name);
+ const firstBucketPaths = firstBucketNodes.map(n => n.path.toString());
+
+ expect(firstBucketNames).toEqual([
+ "[0…99]",
+ "[100…199]",
+ "[200…299]",
+ "[300…399]",
+ "[400…499]",
+ "[500…599]",
+ "[600…699]",
+ "[700…799]",
+ "[800…899]",
+ "[900…999]",
+ ]);
+ expect(firstBucketPaths[0]).toEqual("root◦[0…999]◦[0…99]");
+ expect(firstBucketPaths[firstBucketPaths.length - 1]).toEqual(
+ "root◦[0…999]◦[900…999]"
+ );
+
+ const lastBucketNodes = makeNumericalBuckets(nodes[nodes.length - 1]);
+ const lastBucketNames = lastBucketNodes.map(n => n.name);
+ const lastBucketPaths = lastBucketNodes.map(n => n.path.toString());
+ expect(lastBucketNames).toEqual([
+ "[23000…23099]",
+ "[23100…23199]",
+ "[23200…23299]",
+ "[23300…23399]",
+ "[23400…23455]",
+ ]);
+ expect(lastBucketPaths[0]).toEqual("root◦[23000…23455]◦[23000…23099]");
+ expect(lastBucketPaths[lastBucketPaths.length - 1]).toEqual(
+ "root◦[23000…23455]◦[23400…23455]"
+ );
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/node-has-entries.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/node-has-entries.test.js
new file mode 100644
index 0000000000..4a7aaa971d
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/node-has-entries.test.js
@@ -0,0 +1,51 @@
+/* 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/>. */
+
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+
+const {
+ createNode,
+ nodeHasEntries,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+const createRootNode = value =>
+ createNode({ name: "root", contents: { value } });
+describe("nodeHasEntries", () => {
+ it("returns true for Maps", () => {
+ expect(
+ nodeHasEntries(createRootNode(gripMapStubs.get("testSymbolKeyedMap")))
+ ).toBe(true);
+ });
+
+ it("returns true for WeakMaps", () => {
+ expect(
+ nodeHasEntries(createRootNode(gripMapStubs.get("testWeakMap")))
+ ).toBe(true);
+ });
+
+ it("returns true for Sets", () => {
+ expect(
+ nodeHasEntries(createRootNode(gripArrayStubs.get("new Set([1,2,3,4])")))
+ ).toBe(true);
+ });
+
+ it("returns true for WeakSets", () => {
+ expect(
+ nodeHasEntries(
+ createRootNode(
+ gripArrayStubs.get(
+ "new WeakSet(document.querySelectorAll('div, button'))"
+ )
+ )
+ )
+ ).toBe(true);
+ });
+
+ it("returns false for Arrays", () => {
+ expect(
+ nodeHasEntries(createRootNode(gripMapStubs.get("testMaxProps")))
+ ).toBe(false);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/node-is-window.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/node-is-window.test.js
new file mode 100644
index 0000000000..8fe920ed17
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/node-is-window.test.js
@@ -0,0 +1,20 @@
+/* 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/>. */
+
+const gripWindowStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+
+const {
+ createNode,
+ nodeIsWindow,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+const createRootNode = value =>
+ createNode({ name: "root", contents: { value } });
+describe("nodeIsWindow", () => {
+ it("returns true for Window", () => {
+ expect(
+ nodeIsWindow(createRootNode(gripWindowStubs.get("Window")._grip))
+ ).toBe(true);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/node-supports-numerical-bucketing.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/node-supports-numerical-bucketing.test.js
new file mode 100644
index 0000000000..51199146e0
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/node-supports-numerical-bucketing.test.js
@@ -0,0 +1,72 @@
+/* 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/>. */
+
+const {
+ createNode,
+ makeNodesForEntries,
+ nodeSupportsNumericalBucketing,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+const createRootNode = stub =>
+ createNode({
+ name: "root",
+ contents: { value: stub },
+ });
+
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+
+describe("nodeSupportsNumericalBucketing", () => {
+ it("returns true for Arrays", () => {
+ expect(
+ nodeSupportsNumericalBucketing(
+ createRootNode(gripArrayStubs.get("testBasic"))
+ )
+ ).toBe(true);
+ });
+
+ it("returns true for NodeMap", () => {
+ expect(
+ nodeSupportsNumericalBucketing(
+ createRootNode(gripArrayStubs.get("testNamedNodeMap"))
+ )
+ ).toBe(true);
+ });
+
+ it("returns true for NodeList", () => {
+ expect(
+ nodeSupportsNumericalBucketing(
+ createRootNode(gripArrayStubs.get("testNodeList"))
+ )
+ ).toBe(true);
+ });
+
+ it("returns true for DocumentFragment", () => {
+ expect(
+ nodeSupportsNumericalBucketing(
+ createRootNode(gripArrayStubs.get("testDocumentFragment"))
+ )
+ ).toBe(true);
+ });
+
+ it("returns true for <entries> node", () => {
+ expect(
+ nodeSupportsNumericalBucketing(
+ makeNodesForEntries(
+ createRootNode(gripMapStubs.get("testSymbolKeyedMap"))
+ )
+ )
+ ).toBe(true);
+ });
+
+ it("returns true for buckets node", () => {
+ expect(
+ nodeSupportsNumericalBucketing(
+ makeNodesForEntries(
+ createRootNode(gripMapStubs.get("testSymbolKeyedMap"))
+ )
+ )
+ ).toBe(true);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/promises.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/promises.test.js
new file mode 100644
index 0000000000..229f717b56
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/promises.test.js
@@ -0,0 +1,54 @@
+/* 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/>. */
+
+const {
+ makeNodesForPromiseProperties,
+ nodeIsPromise,
+} = require("resource://devtools/client/shared/components/object-inspector/utils/node.js");
+
+describe("promises utils function", () => {
+ it("is promise", () => {
+ const promise = {
+ contents: {
+ enumerable: true,
+ configurable: false,
+ value: {
+ actor: "server2.conn2.child1/obj36",
+ promiseState: {
+ state: "rejected",
+ reason: {
+ type: "undefined",
+ },
+ },
+ class: "Promise",
+ type: "object",
+ },
+ },
+ };
+
+ expect(nodeIsPromise(promise)).toEqual(true);
+ });
+
+ it("makeNodesForPromiseProperties", () => {
+ const item = {
+ path: "root",
+ contents: {
+ value: {
+ actor: "server2.conn2.child1/obj36",
+ class: "Promise",
+ type: "object",
+ },
+ },
+ };
+ const promiseState = {
+ state: "rejected",
+ reason: {
+ type: "3",
+ },
+ };
+
+ const properties = makeNodesForPromiseProperties({promiseState}, item);
+ expect(properties).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-entries.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-entries.test.js
new file mode 100644
index 0000000000..e4672f2a92
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-entries.test.js
@@ -0,0 +1,171 @@
+/* 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/>. */
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const { createNode, getChildren, makeNodesForEntries } = Utils.node;
+
+const { shouldLoadItemEntries } = Utils.loadProperties;
+
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+
+describe("shouldLoadItemEntries", () => {
+ it("returns true for an entries node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(shouldLoadItemEntries(entriesNode)).toBeTruthy();
+ });
+
+ it("returns false for an already loaded entries node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ const loadedProperties = new Map([[entriesNode.path, true]]);
+ expect(shouldLoadItemEntries(entriesNode, loadedProperties)).toBeFalsy();
+ });
+
+ it("returns true for entries on a Map with everything in preview", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("testSymbolKeyedMap"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(shouldLoadItemEntries(entriesNode)).toBeTruthy();
+ });
+
+ it("returns true for entries on a Set with everything in preview", () => {
+ const setStubNode = createNode({
+ name: "set",
+ contents: {
+ value: gripArrayStubs.get("new Set([1,2,3,4])"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(setStubNode);
+ expect(shouldLoadItemEntries(entriesNode)).toBeTruthy();
+ });
+
+ it("returns false for a Set node", () => {
+ const setStubNode = createNode({
+ name: "set",
+ contents: {
+ value: gripArrayStubs.get("new Set([1,2,3,4])"),
+ },
+ });
+ expect(shouldLoadItemEntries(setStubNode)).toBeFalsy();
+ });
+
+ it("returns false for a Map node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ expect(shouldLoadItemEntries(mapStubNode)).toBeFalsy();
+ });
+
+ it("returns false for an array", () => {
+ const node = createNode({
+ name: "array",
+ contents: {
+ value: gripMapStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemEntries(node)).toBeFalsy();
+ });
+
+ it("returns false for an object", () => {
+ const node = createNode({
+ name: "array",
+ contents: {
+ value: gripStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemEntries(node)).toBeFalsy();
+ });
+
+ it("returns false for an entries node with buckets", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("234-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(shouldLoadItemEntries(entriesNode)).toBeFalsy();
+ });
+
+ it("returns true for an entries bucket node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("234-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ const bucketNodes = getChildren({
+ item: entriesNode,
+ loadedProperties: new Map([[entriesNode.path, true]]),
+ });
+
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…99]");
+ expect(shouldLoadItemEntries(bucketNodes[0])).toBeTruthy();
+ });
+
+ it("returns false for an entries bucket node with sub-buckets", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("23456-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ const bucketNodes = getChildren({
+ item: entriesNode,
+ loadedProperties: new Map([[entriesNode.path, true]]),
+ });
+
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…999]");
+ expect(shouldLoadItemEntries(bucketNodes[0])).toBeFalsy();
+ });
+
+ it("returns true for an entries sub-bucket node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("23456-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ const bucketNodes = getChildren({
+ item: entriesNode,
+ loadedProperties: new Map([[entriesNode.path, true]]),
+ });
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…999]");
+
+ // Get the sub-buckets
+ const subBucketNodes = getChildren({
+ item: bucketNodes[0],
+ loadedProperties: new Map([[bucketNodes[0].path, true]]),
+ });
+ // Make sure we do have a bucket.
+ expect(subBucketNodes[0].name).toBe("[0…99]");
+ expect(shouldLoadItemEntries(subBucketNodes[0])).toBeTruthy();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-full-text.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-full-text.test.js
new file mode 100644
index 0000000000..9e696a028c
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-full-text.test.js
@@ -0,0 +1,56 @@
+/* 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/>. */
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const { createNode } = Utils.node;
+const { shouldLoadItemFullText } = Utils.loadProperties;
+
+const longStringStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/long-string.js");
+const symbolStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/symbol.js");
+
+describe("shouldLoadItemFullText", () => {
+ it("returns true for a longString node with unloaded full text", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: longStringStubs.get("testUnloadedFullText"),
+ },
+ });
+ expect(shouldLoadItemFullText(node)).toBeTruthy();
+ });
+
+ it("returns false for a longString node with loaded full text", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: longStringStubs.get("testLoadedFullText"),
+ },
+ });
+ const loadedProperties = new Map([[node.path, true]]);
+ expect(shouldLoadItemFullText(node, loadedProperties)).toBeFalsy();
+ });
+
+ it("returns false for non longString primitive nodes", () => {
+ const values = [
+ "primitive string",
+ 1,
+ -1,
+ 0,
+ true,
+ false,
+ null,
+ undefined,
+ symbolStubs.get("Symbol"),
+ ];
+
+ const nodes = values.map((value, i) =>
+ createNode({
+ name: `root${i}`,
+ contents: { value },
+ })
+ );
+
+ nodes.forEach(node => expect(shouldLoadItemFullText(node)).toBeFalsy());
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-indexed-properties.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-indexed-properties.test.js
new file mode 100644
index 0000000000..63e505b947
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-indexed-properties.test.js
@@ -0,0 +1,259 @@
+/* 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/>. */
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const {
+ createNode,
+ createGetterNode,
+ createSetterNode,
+ getChildren,
+ makeNodesForEntries,
+ nodeIsDefaultProperties,
+} = Utils.node;
+
+const { shouldLoadItemIndexedProperties } = Utils.loadProperties;
+
+const {
+ createGripMapEntry,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const accessorStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessor.js");
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const windowStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+
+describe("shouldLoadItemIndexedProperties", () => {
+ it("returns true for an array", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns false for an already loaded item", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ const loadedProperties = new Map([[node.path, true]]);
+ expect(shouldLoadItemIndexedProperties(node, loadedProperties)).toBeFalsy();
+ });
+
+ it("returns false for an array node with buckets", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeFalsy();
+ });
+
+ it("returns true for an array bucket node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ const bucketNodes = getChildren({
+ item: node,
+ loadedProperties: new Map([[node.path, true]]),
+ });
+
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…99]");
+ expect(shouldLoadItemIndexedProperties(bucketNodes[0])).toBeTruthy();
+ });
+
+ it("returns false for an array bucket node with sub-buckets", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(23456)"),
+ },
+ });
+ const bucketNodes = getChildren({
+ item: node,
+ loadedProperties: new Map([[node.path, true]]),
+ });
+
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…999]");
+ expect(shouldLoadItemIndexedProperties(bucketNodes[0])).toBeFalsy();
+ });
+
+ it("returns true for an array sub-bucket node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(23456)"),
+ },
+ });
+ const bucketNodes = getChildren({
+ item: node,
+ loadedProperties: new Map([[node.path, true]]),
+ });
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…999]");
+
+ // Get the sub-buckets
+ const subBucketNodes = getChildren({
+ item: bucketNodes[0],
+ loadedProperties: new Map([[bucketNodes[0].path, true]]),
+ });
+ // Make sure we do have a bucket.
+ expect(subBucketNodes[0].name).toBe("[0…99]");
+ expect(shouldLoadItemIndexedProperties(subBucketNodes[0])).toBeTruthy();
+ });
+
+ it("returns false for an entries node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(shouldLoadItemIndexedProperties(entriesNode)).toBeFalsy();
+ });
+
+ it("returns true for an Object", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns true for a Map", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns true for a Set", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("new Set([1,2,3,4])"),
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns true for a Window", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns false for a <default properties> node", () => {
+ const windowNode = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ const loadedProperties = new Map([
+ [
+ windowNode.path,
+ {
+ ownProperties: {
+ foo: { value: "bar" },
+ location: { value: "a" },
+ },
+ },
+ ],
+ ]);
+ const [, defaultPropertiesNode] = getChildren({
+ item: windowNode,
+ loadedProperties,
+ });
+ expect(nodeIsDefaultProperties(defaultPropertiesNode)).toBe(true);
+ expect(shouldLoadItemIndexedProperties(defaultPropertiesNode)).toBeFalsy();
+ });
+
+ it("returns false for a MapEntry node", () => {
+ const node = createGripMapEntry("key", "value");
+ expect(shouldLoadItemIndexedProperties(node)).toBeFalsy();
+ });
+
+ it("returns false for a Proxy node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeFalsy();
+ });
+
+ it("returns true for a Proxy target node", () => {
+ const proxyNode = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ const loadedProperties = new Map([
+ [proxyNode.path, gripStubs.get("testProxySlots")],
+ ]);
+ const [targetNode] = getChildren({ item: proxyNode, loadedProperties });
+ // Make sure we have the target node.
+ expect(targetNode.name).toBe("<target>");
+ expect(shouldLoadItemIndexedProperties(targetNode)).toBeTruthy();
+ });
+
+ it("returns false for an accessor node", () => {
+ const accessorNode = createNode({
+ name: "root",
+ contents: {
+ value: accessorStubs.get("getter"),
+ },
+ });
+ expect(shouldLoadItemIndexedProperties(accessorNode)).toBeFalsy();
+ });
+
+ it("returns true for an accessor <get> node", () => {
+ const getNode = createGetterNode({
+ name: "root",
+ property: accessorStubs.get("getter"),
+ });
+ expect(getNode.name).toBe("<get root()>");
+ expect(shouldLoadItemIndexedProperties(getNode)).toBeTruthy();
+ });
+
+ it("returns true for an accessor <set> node", () => {
+ const setNode = createSetterNode({
+ name: "root",
+ property: accessorStubs.get("setter"),
+ });
+ expect(setNode.name).toBe("<set root()>");
+ expect(shouldLoadItemIndexedProperties(setNode)).toBeTruthy();
+ });
+
+ it("returns false for a primitive node", () => {
+ const node = createNode({
+ name: "root",
+ contents: { value: 42 },
+ });
+ expect(shouldLoadItemIndexedProperties(node)).toBeFalsy();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-non-indexed-properties.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-non-indexed-properties.test.js
new file mode 100644
index 0000000000..425540eee2
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-non-indexed-properties.test.js
@@ -0,0 +1,222 @@
+/* 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/>. */
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const {
+ createNode,
+ createGetterNode,
+ createSetterNode,
+ getChildren,
+ makeNodesForEntries,
+ nodeIsDefaultProperties,
+} = Utils.node;
+
+const { shouldLoadItemNonIndexedProperties } = Utils.loadProperties;
+
+const {
+ createGripMapEntry,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const accessorStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessor.js");
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const windowStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+
+describe("shouldLoadItemNonIndexedProperties", () => {
+ it("returns true for an array", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns false for an already loaded item", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ const loadedProperties = new Map([[node.path, true]]);
+ expect(
+ shouldLoadItemNonIndexedProperties(node, loadedProperties)
+ ).toBeFalsy();
+ });
+
+ it("returns true for an array node with buckets", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns false for an array bucket node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ const bucketNodes = getChildren({
+ item: node,
+ loadedProperties: new Map([[node.path, true]]),
+ });
+
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…99]");
+ expect(shouldLoadItemNonIndexedProperties(bucketNodes[0])).toBeFalsy();
+ });
+
+ it("returns false for an entries node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(shouldLoadItemNonIndexedProperties(entriesNode)).toBeFalsy();
+ });
+
+ it("returns true for an Object", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns true for a Map", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns true for a Set", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("new Set([1,2,3,4])"),
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns true for a Window", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeTruthy();
+ });
+
+ it("returns false for a <default properties> node", () => {
+ const windowNode = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ const loadedProperties = new Map([
+ [
+ windowNode.path,
+ {
+ ownProperties: {
+ foo: { value: "bar" },
+ location: { value: "a" },
+ },
+ },
+ ],
+ ]);
+ const [, defaultPropertiesNode] = getChildren({
+ item: windowNode,
+ loadedProperties,
+ });
+ expect(nodeIsDefaultProperties(defaultPropertiesNode)).toBe(true);
+ expect(
+ shouldLoadItemNonIndexedProperties(defaultPropertiesNode)
+ ).toBeFalsy();
+ });
+
+ it("returns false for a MapEntry node", () => {
+ const node = createGripMapEntry("key", "value");
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeFalsy();
+ });
+
+ it("returns false for a Proxy node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeFalsy();
+ });
+
+ it("returns true for a Proxy target node", () => {
+ const proxyNode = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ const loadedProperties = new Map([
+ [proxyNode.path, gripStubs.get("testProxySlots")],
+ ]);
+ const [targetNode] = getChildren({ item: proxyNode, loadedProperties });
+ // Make sure we have the target node.
+ expect(targetNode.name).toBe("<target>");
+ expect(shouldLoadItemNonIndexedProperties(targetNode)).toBeTruthy();
+ });
+
+ it("returns false for an accessor node", () => {
+ const accessorNode = createNode({
+ name: "root",
+ contents: {
+ value: accessorStubs.get("getter"),
+ },
+ });
+ expect(shouldLoadItemNonIndexedProperties(accessorNode)).toBeFalsy();
+ });
+
+ it("returns true for an accessor <get> node", () => {
+ const getNode = createGetterNode({
+ name: "root",
+ property: accessorStubs.get("getter"),
+ });
+ expect(getNode.name).toBe("<get root()>");
+ expect(shouldLoadItemNonIndexedProperties(getNode)).toBeTruthy();
+ });
+
+ it("returns true for an accessor <set> node", () => {
+ const setNode = createSetterNode({
+ name: "root",
+ property: accessorStubs.get("setter"),
+ });
+ expect(setNode.name).toBe("<set root()>");
+ expect(shouldLoadItemNonIndexedProperties(setNode)).toBeTruthy();
+ });
+
+ it("returns false for a primitive node", () => {
+ const node = createNode({
+ name: "root",
+ contents: { value: 42 },
+ });
+ expect(shouldLoadItemNonIndexedProperties(node)).toBeFalsy();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-prototype.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-prototype.test.js
new file mode 100644
index 0000000000..83d45df70c
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-prototype.test.js
@@ -0,0 +1,218 @@
+/* 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/>. */
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const {
+ createNode,
+ createGetterNode,
+ createSetterNode,
+ getChildren,
+ makeNodesForEntries,
+ nodeIsDefaultProperties,
+} = Utils.node;
+
+const { shouldLoadItemPrototype } = Utils.loadProperties;
+
+const {
+ createGripMapEntry,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const accessorStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessor.js");
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const windowStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+
+describe("shouldLoadItemPrototype", () => {
+ it("returns true for an array", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeTruthy();
+ });
+
+ it("returns false for an already loaded item", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ const loadedProperties = new Map([[node.path, true]]);
+ expect(shouldLoadItemPrototype(node, loadedProperties)).toBeFalsy();
+ });
+
+ it("returns true for an array node with buckets", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeTruthy();
+ });
+
+ it("returns false for an array bucket node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ const bucketNodes = getChildren({
+ item: node,
+ loadedProperties: new Map([[node.path, true]]),
+ });
+
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…99]");
+ expect(shouldLoadItemPrototype(bucketNodes[0])).toBeFalsy();
+ });
+
+ it("returns false for an entries node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(shouldLoadItemPrototype(entriesNode)).toBeFalsy();
+ });
+
+ it("returns true for an Object", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeTruthy();
+ });
+
+ it("returns true for a Map", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeTruthy();
+ });
+
+ it("returns true for a Set", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("new Set([1,2,3,4])"),
+ },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeTruthy();
+ });
+
+ it("returns true for a Window", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeTruthy();
+ });
+
+ it("returns false for a <default properties> node", () => {
+ const windowNode = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ const loadedProperties = new Map([
+ [
+ windowNode.path,
+ {
+ ownProperties: {
+ foo: { value: "bar" },
+ location: { value: "a" },
+ },
+ },
+ ],
+ ]);
+ const [, defaultPropertiesNode] = getChildren({
+ item: windowNode,
+ loadedProperties,
+ });
+ expect(nodeIsDefaultProperties(defaultPropertiesNode)).toBe(true);
+ expect(shouldLoadItemPrototype(defaultPropertiesNode)).toBeFalsy();
+ });
+
+ it("returns false for a MapEntry node", () => {
+ const node = createGripMapEntry("key", "value");
+ expect(shouldLoadItemPrototype(node)).toBeFalsy();
+ });
+
+ it("returns false for a Proxy node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeFalsy();
+ });
+
+ it("returns true for a Proxy target node", () => {
+ const proxyNode = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ const loadedProperties = new Map([
+ [proxyNode.path, gripStubs.get("testProxySlots")],
+ ]);
+ const [targetNode] = getChildren({ item: proxyNode, loadedProperties });
+ // Make sure we have the target node.
+ expect(targetNode.name).toBe("<target>");
+ expect(shouldLoadItemPrototype(targetNode)).toBeTruthy();
+ });
+
+ it("returns false for an accessor node", () => {
+ const accessorNode = createNode({
+ name: "root",
+ contents: {
+ value: accessorStubs.get("getter"),
+ },
+ });
+ expect(shouldLoadItemPrototype(accessorNode)).toBeFalsy();
+ });
+
+ it("returns true for an accessor <get> node", () => {
+ const getNode = createGetterNode({
+ name: "root",
+ property: accessorStubs.get("getter"),
+ });
+ expect(getNode.name).toBe("<get root()>");
+ expect(shouldLoadItemPrototype(getNode)).toBeTruthy();
+ });
+
+ it("returns true for an accessor <set> node", () => {
+ const setNode = createSetterNode({
+ name: "root",
+ property: accessorStubs.get("setter"),
+ });
+ expect(setNode.name).toBe("<set root()>");
+ expect(shouldLoadItemPrototype(setNode)).toBeTruthy();
+ });
+
+ it("returns false for a primitive node", () => {
+ const node = createNode({
+ name: "root",
+ contents: { value: 42 },
+ });
+ expect(shouldLoadItemPrototype(node)).toBeFalsy();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-symbols.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-symbols.test.js
new file mode 100644
index 0000000000..a937b9fcab
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-load-item-symbols.test.js
@@ -0,0 +1,218 @@
+/* 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/>. */
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const {
+ createNode,
+ createGetterNode,
+ createSetterNode,
+ getChildren,
+ makeNodesForEntries,
+ nodeIsDefaultProperties,
+} = Utils.node;
+
+const { shouldLoadItemSymbols } = Utils.loadProperties;
+
+const {
+ createGripMapEntry,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const accessorStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessor.js");
+const gripMapStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const windowStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+
+describe("shouldLoadItemSymbols", () => {
+ it("returns true for an array", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeTruthy();
+ });
+
+ it("returns false for an already loaded item", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("testMaxProps"),
+ },
+ });
+ const loadedProperties = new Map([[node.path, true]]);
+ expect(shouldLoadItemSymbols(node, loadedProperties)).toBeFalsy();
+ });
+
+ it("returns true for an array node with buckets", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeTruthy();
+ });
+
+ it("returns false for an array bucket node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("Array(234)"),
+ },
+ });
+ const bucketNodes = getChildren({
+ item: node,
+ loadedProperties: new Map([[node.path, true]]),
+ });
+
+ // Make sure we do have a bucket.
+ expect(bucketNodes[0].name).toBe("[0…99]");
+ expect(shouldLoadItemSymbols(bucketNodes[0])).toBeFalsy();
+ });
+
+ it("returns false for an entries node", () => {
+ const mapStubNode = createNode({
+ name: "map",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ const entriesNode = makeNodesForEntries(mapStubNode);
+ expect(shouldLoadItemSymbols(entriesNode)).toBeFalsy();
+ });
+
+ it("returns true for an Object", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testMaxProps"),
+ },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeTruthy();
+ });
+
+ it("returns true for a Map", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripMapStubs.get("20-entries Map"),
+ },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeTruthy();
+ });
+
+ it("returns true for a Set", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripArrayStubs.get("new Set([1,2,3,4])"),
+ },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeTruthy();
+ });
+
+ it("returns true for a Window", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeTruthy();
+ });
+
+ it("returns false for a <default properties> node", () => {
+ const windowNode = createNode({
+ name: "root",
+ contents: {
+ value: windowStubs.get("Window")._grip,
+ },
+ });
+ const loadedProperties = new Map([
+ [
+ windowNode.path,
+ {
+ ownProperties: {
+ foo: { value: "bar" },
+ location: { value: "a" },
+ },
+ },
+ ],
+ ]);
+ const [, defaultPropertiesNode] = getChildren({
+ item: windowNode,
+ loadedProperties,
+ });
+ expect(nodeIsDefaultProperties(defaultPropertiesNode)).toBe(true);
+ expect(shouldLoadItemSymbols(defaultPropertiesNode)).toBeFalsy();
+ });
+
+ it("returns false for a MapEntry node", () => {
+ const node = createGripMapEntry("key", "value");
+ expect(shouldLoadItemSymbols(node)).toBeFalsy();
+ });
+
+ it("returns false for a Proxy node", () => {
+ const node = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeFalsy();
+ });
+
+ it("returns true for a Proxy target node", () => {
+ const proxyNode = createNode({
+ name: "root",
+ contents: {
+ value: gripStubs.get("testProxy"),
+ },
+ });
+ const loadedProperties = new Map([
+ [proxyNode.path, gripStubs.get("testProxySlots")],
+ ]);
+ const [targetNode] = getChildren({ item: proxyNode, loadedProperties });
+ // Make sure we have the target node.
+ expect(targetNode.name).toBe("<target>");
+ expect(shouldLoadItemSymbols(targetNode)).toBeTruthy();
+ });
+
+ it("returns false for an accessor node", () => {
+ const accessorNode = createNode({
+ name: "root",
+ contents: {
+ value: accessorStubs.get("getter"),
+ },
+ });
+ expect(shouldLoadItemSymbols(accessorNode)).toBeFalsy();
+ });
+
+ it("returns true for an accessor <get> node", () => {
+ const getNode = createGetterNode({
+ name: "root",
+ property: accessorStubs.get("getter"),
+ });
+ expect(getNode.name).toBe("<get root()>");
+ expect(shouldLoadItemSymbols(getNode)).toBeTruthy();
+ });
+
+ it("returns true for an accessor <set> node", () => {
+ const setNode = createSetterNode({
+ name: "root",
+ property: accessorStubs.get("setter"),
+ });
+ expect(setNode.name).toBe("<set root()>");
+ expect(shouldLoadItemSymbols(setNode)).toBeTruthy();
+ });
+
+ it("returns false for a primitive node", () => {
+ const node = createNode({
+ name: "root",
+ contents: { value: 42 },
+ });
+ expect(shouldLoadItemSymbols(node)).toBeFalsy();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/object-inspector/utils/should-render-roots-in-reps.test.js b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-render-roots-in-reps.test.js
new file mode 100644
index 0000000000..456326545a
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/object-inspector/utils/should-render-roots-in-reps.test.js
@@ -0,0 +1,153 @@
+/* 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/>. */
+
+const Utils = require("resource://devtools/client/shared/components/object-inspector/utils/index.js");
+const { shouldRenderRootsInReps } = Utils;
+
+const nullStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/null.js");
+const numberStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/number.js");
+const undefinedStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/undefined.js");
+const gripStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const symbolStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/symbol.js");
+const errorStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/error.js");
+const bigIntStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/big-int.js");
+
+describe("shouldRenderRootsInReps", () => {
+ it("returns true for a string", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ contents: { value: "Hello" },
+ },
+ ])
+ ).toBeTruthy();
+ });
+
+ it("returns true for an integer", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ contents: { value: numberStubs.get("Int") },
+ },
+ ])
+ ).toBeTruthy();
+ });
+
+ it("returns false for empty roots", () => {
+ expect(shouldRenderRootsInReps([])).toBeFalsy();
+ });
+
+ it("returns true for a big int", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ contents: { value: bigIntStubs.get("1n") },
+ },
+ ])
+ ).toBeTruthy();
+ });
+
+ it("returns true for undefined", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ contents: { value: undefinedStubs.get("Undefined") },
+ },
+ ])
+ ).toBeTruthy();
+ });
+
+ it("returns true for null", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ contents: { value: nullStubs.get("Null") },
+ },
+ ])
+ ).toBeTruthy();
+ });
+
+ it("returns true for Symbols", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ contents: { value: symbolStubs.get("Symbol") },
+ },
+ ])
+ ).toBeTruthy();
+ });
+
+ it("returns true for Errors when customFormat prop is true", () => {
+ expect(
+ shouldRenderRootsInReps(
+ [
+ {
+ contents: { value: errorStubs.get("MultilineStackError") },
+ },
+ ],
+ { customFormat: true }
+ )
+ ).toBeTruthy();
+ });
+
+ it("returns false for Errors when customFormat prop is false", () => {
+ expect(
+ shouldRenderRootsInReps(
+ [
+ {
+ contents: { value: errorStubs.get("MultilineStackError") },
+ },
+ ],
+ { customFormat: false }
+ )
+ ).toBeFalsy();
+ });
+
+ it("returns false when there are multiple primitive roots", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ contents: { value: "Hello" },
+ },
+ {
+ contents: { value: 42 },
+ },
+ ])
+ ).toBeFalsy();
+ });
+
+ it("returns false for primitive when the root specifies a name", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ name: "label",
+ contents: { value: 42 },
+ },
+ ])
+ ).toBeFalsy();
+ });
+
+ it("returns false for Grips", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ name: "label",
+ contents: { value: gripStubs.get("testMaxProps") },
+ },
+ ])
+ ).toBeFalsy();
+ });
+
+ it("returns false for Arrays", () => {
+ expect(
+ shouldRenderRootsInReps([
+ {
+ name: "label",
+ contents: { value: gripArrayStubs.get("testMaxProps") },
+ },
+ ])
+ ).toBeFalsy();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/__snapshots__/accessor.test.js.snap b/devtools/client/shared/components/test/node/components/reps/__snapshots__/accessor.test.js.snap
new file mode 100644
index 0000000000..d8e298956a
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/__snapshots__/accessor.test.js.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Accessor - Invoke getter does not render an icon when the object has an evaluation 1`] = `"\\"hello\\""`;
diff --git a/devtools/client/shared/components/test/node/components/reps/__snapshots__/element-node.test.js.snap b/devtools/client/shared/components/test/node/components/reps/__snapshots__/element-node.test.js.snap
new file mode 100644
index 0000000000..077bc6cc71
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/__snapshots__/element-node.test.js.snap
@@ -0,0 +1,42 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ElementNode - Node with spaces in the class name renders with expected text content 1`] = `
+<span
+ className="objectBox objectBox-node"
+ data-link-actor-id="server1.conn3.child1/obj59"
+>
+ <span
+ className="angleBracket"
+ >
+ &lt;
+ </span>
+ <span
+ className="tag-name"
+ >
+ body
+ </span>
+
+ <span>
+ <span
+ className="attrName"
+ >
+ class
+ </span>
+ <span
+ className="attrEqual"
+ >
+ =
+ </span>
+ <span
+ className="objectBox objectBox-string attrValue"
+ >
+ "a b c"
+ </span>
+ </span>
+ <span
+ className="angleBracket"
+ >
+ &gt;
+ </span>
+</span>
+`;
diff --git a/devtools/client/shared/components/test/node/components/reps/__snapshots__/error.test.js.snap b/devtools/client/shared/components/test/node/components/reps/__snapshots__/error.test.js.snap
new file mode 100644
index 0000000000..2dc830576f
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/__snapshots__/error.test.js.snap
@@ -0,0 +1,1225 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Error - Error with V8-like stack renders with expected text 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1020"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ BOOM
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ getAccount
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ http://moz.com/script.js:1:2
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Error with invalid stack renders with expected text 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1020"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ bad stack
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ />
+</span>
+`;
+
+exports[`Error - Error with stack having frames with multiple @ renders with expected text for Error object 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1021"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ bar
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ errorBar
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ https://example.com/turbo/from-npm.js@0.8.26/dist/from-npm.js:814:31
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn1"
+ >
+ errorFoo
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location1"
+ >
+ https://example.com/turbo/from-npm.js@0.8.26/dist/from-npm.js:815:31
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn2"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location2"
+ >
+ https://example.com/turbo/from-npm.js@0.8.26/dist/from-npm.js:816:31
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Error with undefined-grip message renders with expected text 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server0.conn0.child1/obj88"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-undefined"
+ title={null}
+ >
+ undefined
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:16:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Error with undefined-grip message renders with expected text 2`] = `
+<span
+ className="objectBox-stackTrace "
+ data-link-actor-id="server0.conn0.child1/obj88"
+ title={null}
+>
+ <span
+ className="objectTitle"
+ key="title"
+ >
+ Error
+ </span>
+</span>
+`;
+
+exports[`Error - Error with undefined-grip name renders with expected text 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server0.conn0.child1/obj88"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ too much recursion
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:16:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Error with undefined-grip name renders with expected text 2`] = `
+<span
+ className="objectBox-stackTrace "
+ data-link-actor-id="server0.conn0.child1/obj88"
+ title={null}
+>
+ <span
+ className="objectTitle"
+ key="title"
+ >
+ Error
+ </span>
+</span>
+`;
+
+exports[`Error - Error with undefined-grip stack renders with expected text 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server0.conn0.child1/obj88"
+ title={null}
+>
+ InternalError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ too much recursion
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ />
+</span>
+`;
+
+exports[`Error - Eval error renders with expected text for an EvalError 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1022"
+ title={null}
+>
+ EvalError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ EvalError message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:10:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Internal error renders with expected text for an InternalError 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1023"
+ title={null}
+>
+ InternalError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ InternalError message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:11:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Multi line stack error renders with expected text for Error object 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1021"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ bar
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ errorBar
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:6:15
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn1"
+ >
+ errorFoo
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location1"
+ >
+ debugger eval code:3:3
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn2"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location2"
+ >
+ debugger eval code:8:1
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Range error renders with expected text for RangeError 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1024"
+ title={null}
+>
+ RangeError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ RangeError message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:12:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Reference error renders with expected text for ReferenceError 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1025"
+ title={null}
+>
+ ReferenceError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ ReferenceError message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:13:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Simple error renders with error type and preview message when in short mode 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1021"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ bar
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ errorBar
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:6:15
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn1"
+ >
+ errorFoo
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location1"
+ >
+ debugger eval code:3:3
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn2"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location2"
+ >
+ debugger eval code:8:1
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Simple error renders with error type only when customFormat prop isn't set 1`] = `
+<span
+ className="objectBox-stackTrace "
+ data-link-actor-id="server1.conn1.child1/obj1021"
+ title={null}
+>
+ <span
+ className="objectTitle"
+ key="title"
+ >
+ Error:
+ </span>
+ <span
+ className="objectBox objectBox-string"
+ >
+ bar
+ </span>
+</span>
+`;
+
+exports[`Error - Simple error renders with error type only when depth is > 0 1`] = `
+<span
+ className="objectBox-stackTrace "
+ data-link-actor-id="server1.conn1.child1/obj1021"
+ title={null}
+>
+ <span
+ className="objectTitle"
+ key="title"
+ >
+ Error:
+ </span>
+ <span
+ className="objectBox objectBox-string"
+ >
+ bar
+ </span>
+</span>
+`;
+
+exports[`Error - Simple error renders with expected text for simple error 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1020"
+ title="Error: \\"Error message\\""
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ title="Error message"
+ >
+ Error message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:1:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Syntax error renders with expected text for SyntaxError 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1026"
+ title={null}
+>
+ SyntaxError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ SyntaxError message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:14:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - Type error renders with expected text for TypeError 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1027"
+ title={null}
+>
+ TypeError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ TypeError message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:15:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - URI error renders with expected text for URIError 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1028"
+ title={null}
+>
+ URIError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ URIError message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ &lt;anonymous&gt;
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ debugger eval code:16:13
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - base-loader.sys.mjs renders as expected in HEADER mode 1`] = `
+<span
+ className="objectBox-stackTrace "
+ data-link-actor-id="server1.conn1.child1/obj1020"
+ title={null}
+>
+ <span
+ className="objectTitle"
+ key="title"
+ >
+ Error
+ </span>
+</span>
+`;
+
+exports[`Error - base-loader.sys.mjs renders as expected in tiny mode 1`] = `
+<span
+ className="objectBox-stackTrace "
+ data-link-actor-id="server1.conn1.child1/obj1020"
+ title={null}
+>
+ <span
+ className="objectTitle"
+ key="title"
+ >
+ Error
+ </span>
+</span>
+`;
+
+exports[`Error - base-loader.sys.mjs renders as expected without mode 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1020"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ Error message
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ onPacket
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ resource://devtools/client/debugger-client.js:856:9
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn1"
+ >
+ send
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location1"
+ >
+ resource://devtools/shared/transport/transport.js:569:13
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn2"
+ >
+ makeInfallible
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location2"
+ >
+ resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn3"
+ >
+ makeInfallible
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location3"
+ >
+ resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - longString stacktrace - cut-off location renders as expected 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj33"
+ title={null}
+>
+ InternalError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ too much recursion
+ </span>
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ doStuff
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:32:1
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn1"
+ >
+ doStuff
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location1"
+ >
+ https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn2"
+ >
+ doStuff
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location2"
+ >
+ https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn3"
+ >
+ doStuff
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location3"
+ >
+ https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn4"
+ >
+ doStuff
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location4"
+ >
+ https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn5"
+ >
+ doStuff
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location5"
+ >
+ https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn6"
+ >
+ doStuff
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location6"
+ >
+ https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - longString stacktrace renders as expected 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn2.child1/obj33"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ />
+ <span
+ className="objectBox-stackTrace-grid"
+ key="stack"
+ >
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn0"
+ >
+ ngOnChanges
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location0"
+ >
+ webpack-internal:///./node_modules/@angular/common/esm5/common.js:2656:27
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn1"
+ >
+ checkAndUpdateDirectiveInline
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location1"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:12581:9
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn2"
+ >
+ checkAndUpdateNodeInline
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location2"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:14109:20
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn3"
+ >
+ checkAndUpdateNode
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location3"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:14052:16
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn4"
+ >
+ debugCheckAndUpdateNode
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location4"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:14945:55
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn5"
+ >
+ debugCheckDirectivesFn
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location5"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:14886:13
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn6"
+ >
+ View_MetaTableComponent_6
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location6"
+ >
+ ng:///AppModule/MetaTableComponent.ngfactory.js:98:5
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn7"
+ >
+ debugUpdateDirectives
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location7"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:14871:12
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn8"
+ >
+ checkAndUpdateView
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location8"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:14018:5
+ </span>
+
+
+
+ <span
+ className="objectBox-stackTrace-fn"
+ key="fn9"
+ >
+ callViewAction
+ </span>
+
+ <span
+ className="objectBox-stackTrace-location"
+ key="location9"
+ >
+ webpack-internal:///./node_modules/@angular/core/esm5/core.js:14369:21
+ </span>
+
+
+ </span>
+</span>
+`;
+
+exports[`Error - renderStacktrace prop uses renderStacktrace prop when provided 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj1021"
+ title={null}
+>
+ Error:
+ <span
+ className="objectBox objectBox-string"
+ >
+ bar
+ </span>
+ <li
+ className="frame"
+ >
+ Function errorBar called from debugger eval code:6:15
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function errorFoo called from debugger eval code:3:3
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function &lt;anonymous&gt; called from debugger eval code:8:1
+
+ </li>
+</span>
+`;
+
+exports[`Error - renderStacktrace prop uses renderStacktrace with longString errors too 1`] = `
+<span
+ className="objectBox-stackTrace reps-custom-format"
+ data-link-actor-id="server1.conn1.child1/obj33"
+ title={null}
+>
+ InternalError:
+ <span
+ className="objectBox objectBox-string"
+ >
+ too much recursion
+ </span>
+ <li
+ className="frame"
+ >
+ Function execute/AppComponent&lt;/AppComponent.prototype.doStuff called from https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:32:1
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function execute/AppComponent&lt;/AppComponent.prototype.doStuff called from https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function execute/AppComponent&lt;/AppComponent.prototype.doStuff called from https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function execute/AppComponent&lt;/AppComponent.prototype.doStuff called from https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function execute/AppComponent&lt;/AppComponent.prototype.doStuff called from https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function execute/AppComponent&lt;/AppComponent.prototype.doStuff called from https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+
+ </li>
+ <li
+ className="frame"
+ >
+ Function execute/AppComponent&lt;/AppComponent.prototype.doStuff called from https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21
+
+ </li>
+</span>
+`;
diff --git a/devtools/client/shared/components/test/node/components/reps/__snapshots__/nan.test.js.snap b/devtools/client/shared/components/test/node/components/reps/__snapshots__/nan.test.js.snap
new file mode 100644
index 0000000000..c80b14a2fb
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/__snapshots__/nan.test.js.snap
@@ -0,0 +1,10 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`NaN renders NaN Rep as expected 1`] = `
+<span
+ className="objectBox objectBox-nan"
+ title={null}
+>
+ NaN
+</span>
+`;
diff --git a/devtools/client/shared/components/test/node/components/reps/accessible.test.js b/devtools/client/shared/components/test/node/components/reps/accessible.test.js
new file mode 100644
index 0000000000..8df9650c36
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/accessible.test.js
@@ -0,0 +1,321 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest, __dirname */
+const { mount, shallow } = require("enzyme");
+const { JSDOM } = require("jsdom");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Accessible } = REPS;
+const {
+ ELLIPSIS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessible.js");
+
+describe("Accessible - Document", () => {
+ const stub = stubs.get("Document");
+
+ it("selects Accessible Rep", () => {
+ expect(getRep(stub)).toBe(Accessible.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual('"New Tab": document');
+ expect(renderedComponent.prop("title")).toEqual('"New Tab": document');
+ });
+});
+
+describe("Accessible - ButtonMenu", () => {
+ const stub = stubs.get("ButtonMenu");
+
+ it("selects Accessible Rep", () => {
+ expect(getRep(stub)).toBe(Accessible.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '"New to Nightly? Let’s get started.": buttonmenu'
+ );
+ });
+
+ it("renders an inspect icon", () => {
+ const onInspectIconClick = jest.fn();
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ onInspectIconClick,
+ })
+ );
+
+ const node = renderedComponent.find(".open-accessibility-inspector");
+ node.simulate("click", { type: "click" });
+
+ expect(node.exists()).toBeTruthy();
+ expect(onInspectIconClick.mock.calls).toHaveLength(1);
+ expect(onInspectIconClick.mock.calls[0][0]).toEqual(stub);
+ expect(onInspectIconClick.mock.calls[0][1].type).toEqual("click");
+ });
+
+ it("calls the expected function when click is fired on Rep", () => {
+ const onAccessibleClick = jest.fn();
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ onAccessibleClick,
+ })
+ );
+
+ renderedComponent.simulate("click");
+
+ expect(onAccessibleClick.mock.calls).toHaveLength(1);
+ });
+
+ it("calls the expected function when mouseout is fired on Rep", () => {
+ const onAccessibleMouseOut = jest.fn();
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ onAccessibleMouseOut,
+ })
+ );
+
+ renderedComponent.simulate("mouseout");
+
+ expect(onAccessibleMouseOut.mock.calls).toHaveLength(1);
+ });
+
+ it("calls the expected function when mouseover is fired on Rep", () => {
+ const onAccessibleMouseOver = jest.fn();
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ onAccessibleMouseOver,
+ })
+ );
+
+ renderedComponent.simulate("mouseover");
+
+ expect(onAccessibleMouseOver.mock.calls).toHaveLength(1);
+ expect(onAccessibleMouseOver.mock.calls[0][0]).toEqual(stub);
+ });
+});
+
+describe("Accessible - No Name Accessible", () => {
+ const stub = stubs.get("NoName");
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("text container");
+ expect(renderedComponent.prop("title")).toEqual("text container");
+ expect(renderedComponent.find(".separator").exists()).toBeFalsy();
+ expect(renderedComponent.find(".accessible-namer").exists()).toBeFalsy();
+ });
+});
+
+describe("Accessible - Disconnected accessible", () => {
+ const stub = stubs.get("DisconnectedAccessible");
+
+ it(
+ "renders no inspect icon when the accessible is not in the Accessible " +
+ "tree",
+ () => {
+ const onInspectIconClick = jest.fn();
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ onInspectIconClick,
+ })
+ );
+
+ expect(
+ renderedComponent.find(".open-accessibility-inspector").exists()
+ ).toBeFalsy();
+ }
+ );
+});
+
+describe("Accessible - No Preview (not a valid grip)", () => {
+ const stub = stubs.get("NoPreview");
+
+ it("does not select Accessible Rep", () => {
+ expect(getRep(stub)).not.toBe(Accessible.rep);
+ });
+});
+
+describe("Accessible - Accessible with long name", () => {
+ const stub = stubs.get("AccessibleWithLongName");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(Accessible.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ `"${"a".repeat(1000)}": text leaf`
+ );
+ });
+
+ it("renders with expected text content with name max length", () => {
+ const renderedComponent = shallow(
+ Accessible.rep({
+ object: stub,
+ nameMaxLength: 20,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ `"${"a".repeat(9)}${ELLIPSIS}${"a".repeat(8)}": text leaf`
+ );
+ });
+});
+
+describe("Accessible - Inspect icon title", () => {
+ const stub = stubs.get("PushButton");
+
+ it("renders with expected title", () => {
+ const inspectIconTitle = "inspect icon title";
+
+ const renderedComponent = shallow(
+ Accessible.rep({
+ inspectIconTitle,
+ object: stub,
+ onInspectIconClick: jest.fn(),
+ })
+ );
+
+ const iconNode = renderedComponent.find(".open-accessibility-inspector");
+ expect(iconNode.prop("title")).toEqual(inspectIconTitle);
+ });
+});
+
+describe("Accessible - Separator text", () => {
+ const stub = stubs.get("PushButton");
+
+ it("renders with expected title", () => {
+ const separatorText = " - ";
+
+ const renderedComponent = shallow(
+ Accessible.rep({
+ separatorText,
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual('"Search" - pushbutton');
+ });
+});
+
+describe("Accessible - Role first", () => {
+ const stub = stubs.get("PushButton");
+
+ it("renders with expected title", () => {
+ const renderedComponent = shallow(
+ Accessible.rep({
+ roleFirst: true,
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual('pushbutton: "Search"');
+ });
+});
+
+describe("Accessible - Cursor style", () => {
+ const stub = stubs.get("PushButton");
+
+ it("renders with styled cursor", async () => {
+ const window = await createWindowForCursorTest();
+ const attachTo = window.document.querySelector("#attach-to");
+ const renderedComponent = mount(
+ Accessible.rep({
+ object: stub,
+ onAccessibleClick: jest.fn(),
+ onInspectIconClick: jest.fn(),
+ }),
+ {
+ attachTo,
+ }
+ );
+
+ const objectNode = renderedComponent.getDOMNode();
+ const iconNode = objectNode.querySelector(".open-accessibility-inspector");
+ expect(renderedComponent.hasClass("clickable")).toBeTruthy();
+ expect(window.getComputedStyle(objectNode).cursor).toEqual("pointer");
+ expect(window.getComputedStyle(iconNode).cursor).toEqual("pointer");
+ });
+
+ it("renders with unstyled cursor", async () => {
+ const window = await createWindowForCursorTest();
+ const attachTo = window.document.querySelector("#attach-to");
+ const renderedComponent = mount(
+ Accessible.rep({
+ object: stub,
+ }),
+ {
+ attachTo,
+ }
+ );
+
+ const objectNode = renderedComponent.getDOMNode();
+ expect(renderedComponent.hasClass("clickable")).toBeFalsy();
+ expect(window.getComputedStyle(objectNode).cursor).toEqual("");
+ });
+});
+
+async function createWindowForCursorTest() {
+ const path = require("path");
+ const css = await readTextFile(
+ path.resolve(__dirname, "../../../../reps/", "reps.css")
+ );
+ const html = `
+ <body>
+ <style>${css}</style>
+ <div id="attach-to"></div>
+ </body>
+ `;
+
+ return new JSDOM(html).window;
+}
+
+async function readTextFile(fileName) {
+ return new Promise((resolve, reject) => {
+ const fs = require("fs");
+ fs.readFile(fileName, "utf8", (error, text) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(text);
+ }
+ });
+ });
+}
diff --git a/devtools/client/shared/components/test/node/components/reps/accessor.test.js b/devtools/client/shared/components/test/node/components/reps/accessor.test.js
new file mode 100644
index 0000000000..31499fea52
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/accessor.test.js
@@ -0,0 +1,137 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { Accessor, Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/accessor.js");
+
+describe("Accessor - getter", () => {
+ const object = stubs.get("getter");
+
+ it("Rep correctly selects Accessor Rep", () => {
+ expect(getRep(object)).toBe(Accessor.rep);
+ });
+
+ it("Accessor rep has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({ object, shouldRenderTooltip: true })
+ );
+ expect(renderedComponent.text()).toEqual("Getter");
+ expect(renderedComponent.prop("title")).toEqual("Getter");
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+});
+
+describe("Accessor - setter", () => {
+ const object = stubs.get("setter");
+
+ it("Rep correctly selects Accessor Rep", () => {
+ expect(getRep(object)).toBe(Accessor.rep);
+ });
+
+ it("Accessor rep has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({ object, shouldRenderTooltip: true })
+ );
+ expect(renderedComponent.text()).toEqual("Setter");
+ expect(renderedComponent.prop("title")).toEqual("Setter");
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+});
+
+describe("Accessor - getter & setter", () => {
+ const object = stubs.get("getter setter");
+
+ it("Rep correctly selects Accessor Rep", () => {
+ expect(getRep(object)).toBe(Accessor.rep);
+ });
+
+ it("Accessor rep has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({ object, shouldRenderTooltip: true })
+ );
+ expect(renderedComponent.text()).toEqual("Getter & Setter");
+ expect(renderedComponent.prop("title")).toEqual("Getter & Setter");
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+});
+
+describe("Accessor - Invoke getter", () => {
+ it("renders an icon for getter with onInvokeGetterButtonClick", () => {
+ const onInvokeGetterButtonClick = jest.fn();
+ const object = stubs.get("getter");
+ const renderedComponent = shallow(
+ Rep({ object, onInvokeGetterButtonClick })
+ );
+
+ const node = renderedComponent.find(".invoke-getter");
+ node.simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+ expect(node.prop("title")).toEqual("Invoke getter");
+ expect(node.exists()).toBeTruthy();
+ expect(onInvokeGetterButtonClick.mock.calls).toHaveLength(1);
+ });
+
+ it("does not render an icon for a setter only", () => {
+ const onInvokeGetterButtonClick = jest.fn();
+ const object = stubs.get("setter");
+ const renderedComponent = shallow(
+ Rep({ object, onInvokeGetterButtonClick })
+ );
+ expect(renderedComponent.text()).toEqual("Setter");
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+
+ it("renders an icon for getter/setter with onInvokeGetterButtonClick", () => {
+ const onInvokeGetterButtonClick = jest.fn();
+ const object = stubs.get("getter setter");
+ const renderedComponent = shallow(
+ Rep({ object, onInvokeGetterButtonClick })
+ );
+
+ const node = renderedComponent.find(".invoke-getter");
+ node.simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+
+ expect(node.exists()).toBeTruthy();
+ expect(onInvokeGetterButtonClick.mock.calls).toHaveLength(1);
+ });
+
+ it("does not render an icon when the object has an evaluation", () => {
+ const onInvokeGetterButtonClick = jest.fn();
+ const object = stubs.get("getter");
+ const renderedComponent = shallow(
+ Rep({
+ object,
+ onInvokeGetterButtonClick,
+ evaluation: { getterValue: "hello" },
+ })
+ );
+ expect(renderedComponent.text()).toMatchSnapshot();
+
+ const node = renderedComponent.find(".invoke-getter");
+ expect(node.exists()).toBeFalsy();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/array.test.js b/devtools/client/shared/components/test/node/components/reps/array.test.js
new file mode 100644
index 0000000000..cd8cfb9d97
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/array.test.js
@@ -0,0 +1,117 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const { ArrayRep, Rep } = REPS;
+const { maxLengthMap } = ArrayRep;
+
+describe("Array", () => {
+ it("selects Array Rep as expected", () => {
+ const stub = [];
+ expect(getRep(stub, undefined, true)).toBe(ArrayRep.rep);
+ });
+
+ it("renders empty array as expected", () => {
+ const object = [];
+ const renderRep = props => shallow(Rep({ object, noGrip: true, ...props }));
+
+ const defaultOutput = "[]";
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: undefined }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+
+ it("renders basic array as expected", () => {
+ const object = [1, "foo", {}];
+ const renderRep = props => shallow(Rep({ object, noGrip: true, ...props }));
+
+ const defaultOutput = '[ 1, "foo", {} ]';
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: undefined }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("[…]");
+ expect(renderRep({ mode: MODE.TINY }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+
+ it("renders array with more than SHORT mode max props as expected", () => {
+ const object = Array(maxLengthMap.get(MODE.SHORT) + 1).fill("foo");
+ const renderRep = props => shallow(Rep({ object, noGrip: true, ...props }));
+
+ const defaultShortOutput = `[ ${Array(maxLengthMap.get(MODE.SHORT))
+ .fill('"foo"')
+ .join(", ")}, … ]`;
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultShortOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("[…]");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultShortOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ `[ ${Array(maxLengthMap.get(MODE.SHORT) + 1)
+ .fill('"foo"')
+ .join(", ")} ]`
+ );
+ });
+
+ it("renders array with more than LONG mode maximum props as expected", () => {
+ const object = Array(maxLengthMap.get(MODE.LONG) + 1).fill("foo");
+ const renderRep = props => shallow(Rep({ object, noGrip: true, ...props }));
+
+ const defaultShortOutput = `[ ${Array(maxLengthMap.get(MODE.SHORT))
+ .fill('"foo"')
+ .join(", ")}, … ]`;
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultShortOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("[…]");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultShortOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ `[ ${Array(maxLengthMap.get(MODE.LONG)).fill('"foo"').join(", ")}, … ]`
+ );
+ });
+
+ it("renders recursive array as expected", () => {
+ const object = [1];
+ object.push(object);
+ const renderRep = props => shallow(Rep({ object, noGrip: true, ...props }));
+
+ const defaultOutput = "[ 1, […] ]";
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: undefined }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("[…]");
+ expect(renderRep({ mode: MODE.TINY }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+
+ it("renders array containing an object as expected", () => {
+ const object = [
+ {
+ p1: "s1",
+ p2: ["a1", "a2", "a3"],
+ p3: "s3",
+ p4: "s4",
+ },
+ ];
+ const renderRep = props => shallow(Rep({ object, noGrip: true, ...props }));
+
+ const defaultOutput = "[ {…} ]";
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: undefined }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("[…]");
+ expect(renderRep({ mode: MODE.TINY }).prop("title")).toBe("Array");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/attribute.test.js b/devtools/client/shared/components/test/node/components/reps/attribute.test.js
new file mode 100644
index 0000000000..c13ab59857
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/attribute.test.js
@@ -0,0 +1,44 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const { Attribute, Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/attribute.js");
+
+describe("Attribute", () => {
+ const stub = stubs.get("Attribute")._grip;
+
+ it("Rep correctly selects Attribute Rep", () => {
+ expect(getRep(stub)).toBe(Attribute.rep);
+ });
+
+ it("Attribute rep has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.text()).toEqual(
+ 'class="autocomplete-suggestions"'
+ );
+ expect(renderedComponent.prop("title")).toBe(
+ 'class="autocomplete-suggestions"'
+ );
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/big-int.test.js b/devtools/client/shared/components/test/node/components/reps/big-int.test.js
new file mode 100644
index 0000000000..1fa6cd0ee1
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/big-int.test.js
@@ -0,0 +1,106 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { BigInt, Rep } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/big-int.js");
+
+describe("BigInt", () => {
+ describe("1n", () => {
+ const stub = stubs.get("1n");
+
+ it("correctly selects BigInt Rep for BigInt value", () => {
+ expect(getRep(stub)).toBe(BigInt.rep);
+ });
+
+ it("renders with expected text content for BigInt", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("1n");
+ expect(renderedComponent.prop("title")).toBe("1n");
+ });
+ });
+
+ describe("-2n", () => {
+ const stub = stubs.get("-2n");
+
+ it("correctly selects BigInt Rep for negative BigInt value", () => {
+ expect(getRep(stub)).toBe(BigInt.rep);
+ });
+
+ it("renders with expected text content for negative BigInt", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("-2n");
+ expect(renderedComponent.prop("title")).toBe("-2n");
+ });
+ });
+
+ describe("0n", () => {
+ const stub = stubs.get("0n");
+
+ it("correctly selects BigInt Rep for zero BigInt value", () => {
+ expect(getRep(stub)).toBe(BigInt.rep);
+ });
+
+ it("renders with expected text content for zero BigInt", () => {
+ const renderedComponent = shallow(Rep({ object: stub }));
+ expect(renderedComponent.text()).toEqual("0n");
+ });
+ });
+
+ describe("in objects", () => {
+ it("renders with expected text content in Array", () => {
+ const stub = stubs.get("[1n,-2n,0n]");
+ const renderedComponent = shallow(Rep({ object: stub }));
+ expect(renderedComponent.text()).toEqual("Array(3) [ 1n, -2n, 0n ]");
+ });
+
+ it("renders with expected text content in Set", () => {
+ const stub = stubs.get("new Set([1n,-2n,0n])");
+ const renderedComponent = shallow(Rep({ object: stub }));
+ expect(renderedComponent.text()).toEqual("Set(3) [ 1n, -2n, 0n ]");
+ });
+
+ it("renders with expected text content in Map", () => {
+ const stub = stubs.get("new Map([ [1n, -1n], [-2n, 0n], [0n, -2n]])");
+ const renderedComponent = shallow(Rep({ object: stub }));
+ expect(renderedComponent.text()).toEqual(
+ "Map(3) { 1n → -1n, -2n → 0n, 0n → -2n }"
+ );
+ });
+
+ it("renders with expected text content in Object", () => {
+ const stub = stubs.get("({simple: 1n, negative: -2n, zero: 0n})");
+ const renderedComponent = shallow(Rep({ object: stub }));
+ expect(renderedComponent.text()).toEqual(
+ "Object { simple: 1n, negative: -2n, zero: 0n }"
+ );
+ });
+
+ it("renders with expected text content in Promise", () => {
+ const stub = stubs.get("Promise.resolve(1n)");
+ const renderedComponent = shallow(Rep({ object: stub }));
+ expect(renderedComponent.text()).toEqual(
+ 'Promise { <state>: "fulfilled", <value>: 1n }'
+ );
+ });
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/comment-node.test.js b/devtools/client/shared/components/test/node/components/reps/comment-node.test.js
new file mode 100644
index 0000000000..c46f7587aa
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/comment-node.test.js
@@ -0,0 +1,80 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const { Rep, CommentNode } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/comment-node.js");
+
+describe("CommentNode", () => {
+ const stub = stubs.get("Comment")._grip;
+
+ it("selects CommentNode Rep correctly", () => {
+ expect(getRep(stub)).toEqual(CommentNode.rep);
+ });
+
+ it("renders with correct class names", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.hasClass("objectBox theme-comment")).toBe(true);
+ });
+
+ it("renders with correct title tooltip", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.prop("title")).toBe(
+ "<!-- test\nand test\nand test\nand test\nand test\nand test\nand test -->"
+ );
+ });
+
+ it("renders as expected", () => {
+ const object = stubs.get("Comment")._grip;
+ const renderRep = props => shallow(CommentNode.rep({ object, ...props }));
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toEqual(
+ "<!-- test\nand test\nand test\nan…d test\nand test\nand test -->"
+ );
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toEqual(
+ "<!-- test\\nand test\\na… test\\nand test -->"
+ );
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toEqual(
+ "<!-- test\\nand test\\na… test\\nand test -->"
+ );
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toEqual(`<!-- ${stub.preview.textContent} -->`);
+ expectActorAttribute(component, object.actor);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/date-time.test.js b/devtools/client/shared/components/test/node/components/reps/date-time.test.js
new file mode 100644
index 0000000000..62a8557c3a
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/date-time.test.js
@@ -0,0 +1,61 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const { DateTime, Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/date-time.js");
+
+describe("test DateTime", () => {
+ const stub = stubs.get("DateTime")._grip;
+
+ it("selects DateTime as expected", () => {
+ expect(getRep(stub)).toBe(DateTime.rep);
+ });
+
+ it("renders DateTime as expected", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ const expectedDate = new Date(
+ "Date Thu Mar 31 2016 00:17:24 GMT+0300 (EAT)"
+ ).toString();
+
+ expect(renderedComponent.text()).toEqual(`Date ${expectedDate}`);
+ expect(renderedComponent.prop("title")).toEqual(`Date ${expectedDate}`);
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
+
+describe("test invalid DateTime", () => {
+ const stub = stubs.get("InvalidDateTime")._grip;
+
+ it("renders expected text for invalid date", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Invalid Date");
+ expect(renderedComponent.prop("title")).toEqual("Invalid Date");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/document-type.test.js b/devtools/client/shared/components/test/node/components/reps/document-type.test.js
new file mode 100644
index 0000000000..f4709c6d5d
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/document-type.test.js
@@ -0,0 +1,51 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const { DocumentType } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/document-type.js");
+
+describe("DocumentType", () => {
+ const stub = stubs.get("html");
+ it("correctly selects DocumentType Rep", () => {
+ expect(getRep(stub)).toBe(DocumentType.rep);
+ });
+
+ it("renders with expected text content on html doctype", () => {
+ const renderedComponent = shallow(
+ DocumentType.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("<!DOCTYPE html>");
+ expect(renderedComponent.prop("title")).toEqual("<!DOCTYPE html>");
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders with expected text content on empty doctype", () => {
+ const unnamedStub = stubs.get("unnamed");
+ const renderedComponent = shallow(
+ DocumentType.rep({
+ object: unnamedStub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.text()).toEqual("<!DOCTYPE>");
+ expect(renderedComponent.prop("title")).toEqual("<!DOCTYPE>");
+ expectActorAttribute(renderedComponent, unnamedStub.actor);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/document.test.js b/devtools/client/shared/components/test/node/components/reps/document.test.js
new file mode 100644
index 0000000000..36d2eced11
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/document.test.js
@@ -0,0 +1,52 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const { Document } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/document.js");
+
+describe("Document", () => {
+ const stub = stubs.get("Document");
+ it("correctly selects Document Rep", () => {
+ expect(getRep(stub)).toBe(Document.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ Document.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "HTMLDocument https://www.mozilla.org/en-US/firefox/new/"
+ );
+ expect(renderedComponent.prop("title")).toEqual(
+ "HTMLDocument https://www.mozilla.org/en-US/firefox/new/"
+ );
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders location-less document with expected text content", () => {
+ const renderedComponent = shallow(
+ Document.rep({
+ object: stubs.get("Location-less Document"),
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("HTMLDocument");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/element-node.test.js b/devtools/client/shared/components/test/node/components/reps/element-node.test.js
new file mode 100644
index 0000000000..966965e46a
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/element-node.test.js
@@ -0,0 +1,668 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest, __dirname */
+const { mount, shallow } = require("enzyme");
+const { JSDOM } = require("jsdom");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ MAX_ATTRIBUTE_LENGTH,
+} = require("resource://devtools/client/shared/components/reps/reps/element-node.js");
+const { ElementNode } = REPS;
+const {
+ expectActorAttribute,
+ getSelectableInInspectorGrips,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const {
+ ELLIPSIS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/element-node.js");
+
+describe("ElementNode - BodyNode", () => {
+ const stub = stubs.get("BodyNode");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<body id="body-id" class="body-class">'
+ );
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders with expected text content in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<body id="body-id" class="body-class">'
+ );
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders with expected text content on tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("body#body-id.body-class");
+ expect(renderedComponent.prop("title")).toEqual("body#body-id.body-class");
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
+
+describe("ElementNode - DocumentElement", () => {
+ const stub = stubs.get("DocumentElement");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual('<html dir="ltr" lang="en-US">');
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("html");
+ });
+});
+
+describe("ElementNode - Node", () => {
+ const stub = stubs.get("Node");
+ const grips = getSelectableInInspectorGrips(stub);
+
+ it("has one node grip", () => {
+ expect(grips).toHaveLength(1);
+ });
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<input id="newtab-customize-button" class="bar baz" dir="ltr" ' +
+ 'title="Customize your New Tab page" value="foo" type="button">'
+ );
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "input#newtab-customize-button.bar.baz"
+ );
+ });
+
+ it("renders an inspect icon", () => {
+ const onInspectIconClick = jest.fn();
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stubs.get("Node"),
+ onInspectIconClick,
+ })
+ );
+
+ const node = renderedComponent.find(".open-inspector");
+ node.simulate("click", { type: "click" });
+
+ expect(node.exists()).toBeTruthy();
+ expect(onInspectIconClick.mock.calls).toHaveLength(1);
+ expect(onInspectIconClick.mock.calls[0][0]).toEqual(stub);
+ expect(onInspectIconClick.mock.calls[0][1].type).toEqual("click");
+ });
+
+ it("calls the expected function when click is fired on Rep", () => {
+ const onDOMNodeClick = jest.fn();
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ onDOMNodeClick,
+ })
+ );
+
+ renderedComponent.simulate("click");
+
+ expect(onDOMNodeClick.mock.calls).toHaveLength(1);
+ });
+
+ it("calls the expected function when mouseout is fired on Rep", () => {
+ const onDOMNodeMouseOut = jest.fn();
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ onDOMNodeMouseOut,
+ })
+ );
+
+ renderedComponent.simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOut.mock.calls[0][0]).toEqual(stub);
+ });
+
+ it("calls the expected function when mouseover is fired on Rep", () => {
+ const onDOMNodeMouseOver = jest.fn();
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ onDOMNodeMouseOver,
+ })
+ );
+
+ renderedComponent.simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOver.mock.calls[0][0]).toEqual(stub);
+ });
+});
+
+describe("ElementNode - Leading and trailing spaces class name", () => {
+ const stub = stubs.get("NodeWithLeadingAndTrailingSpacesClassName");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<body id="nightly-whatsnew" class=" html-ltr ">'
+ );
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("body#nightly-whatsnew.html-ltr");
+ });
+});
+
+describe("ElementNode - Node with spaces in the class name", () => {
+ const stub = stubs.get("NodeWithSpacesInClassName");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("body.a.b.c");
+ });
+});
+
+describe("ElementNode - Node without attributes", () => {
+ const stub = stubs.get("NodeWithoutAttributes");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("<p>");
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("p");
+ });
+});
+
+describe("ElementNode - Node with many attributes", () => {
+ const stub = stubs.get("LotsOfAttributes");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<p id="lots-of-attributes" a="" b="" c="" d="" e="" f="" g="" ' +
+ 'h="" i="" j="" k="" l="" m="" n="">'
+ );
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("p#lots-of-attributes");
+ });
+});
+
+describe("ElementNode - SVG Node", () => {
+ const stub = stubs.get("SvgNode");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<clipPath id="clip" class="svg-element">'
+ );
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("clipPath#clip.svg-element");
+ });
+});
+
+describe("ElementNode - SVG Node in XHTML", () => {
+ const stub = stubs.get("SvgNodeInXHTML");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<svg:circle class="svg-element" cx="0" cy="0" r="5">'
+ );
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("svg:circle.svg-element");
+ });
+});
+
+describe("ElementNode - Disconnected node", () => {
+ const stub = stubs.get("DisconnectedNode");
+
+ it("renders no inspect icon when the node is not in the DOM tree", () => {
+ const onInspectIconClick = jest.fn();
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ onInspectIconClick,
+ })
+ );
+
+ expect(renderedComponent.find(".open-inspector").exists()).toBeFalsy();
+ });
+});
+
+describe("ElementNode - Element with longString attribute", () => {
+ const stub = stubs.get("NodeWithLongStringAttribute");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ `<div data-test="${"a".repeat(MAX_ATTRIBUTE_LENGTH)}${ELLIPSIS}">`
+ );
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("div");
+ });
+});
+
+describe("ElementNode - Element attribute cropping", () => {
+ it("renders no title attribute for short attribute", () => {
+ const stub = stubs.get("NodeWithSpacesInClassName");
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.first().find("span.attrValue").prop("title")).toBe(
+ undefined
+ );
+ });
+
+ it("renders partial value for long attribute", () => {
+ const stub = stubs.get("NodeWithLongAttribute");
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<p data-test="aaaaaaaaaaaaaaaaaaaaaaaa…aaaaaaaaaaaaaaaaaaaaaaa">'
+ );
+ expect(renderedComponent.first().find("span.attrValue").prop("title")).toBe(
+ "a".repeat(100)
+ );
+ });
+
+ it("renders partial attribute for LongString", () => {
+ const stub = stubs.get("NodeWithLongStringAttribute");
+
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ '<div data-test="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…">'
+ );
+ expect(renderedComponent.first().find("span.attrValue").prop("title")).toBe(
+ "a".repeat(1000)
+ );
+ });
+});
+
+describe("ElementNode - : Marker pseudo element", () => {
+ const stub = stubs.get("MarkerPseudoElement");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("::marker");
+ expect(renderedComponent.prop("title")).toEqual("::marker");
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("::marker");
+ expect(renderedComponent.prop("title")).toEqual("::marker");
+ });
+});
+
+describe("ElementNode - : Before pseudo element", () => {
+ const stub = stubs.get("BeforePseudoElement");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("::before");
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("::before");
+ });
+});
+
+describe("ElementNode - After pseudo element", () => {
+ const stub = stubs.get("AfterPseudoElement");
+
+ it("selects ElementNode Rep", () => {
+ expect(getRep(stub)).toBe(ElementNode.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("::after");
+ });
+
+ it("renders with expected text content in tiny mode", () => {
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("::after");
+ });
+});
+
+describe("ElementNode - Inspect icon title", () => {
+ const stub = stubs.get("Node");
+
+ it("renders with expected title", () => {
+ const inspectIconTitle = "inspect icon title";
+
+ const renderedComponent = shallow(
+ ElementNode.rep({
+ inspectIconTitle,
+ object: stub,
+ shouldRenderTooltip: true,
+ onInspectIconClick: jest.fn(),
+ })
+ );
+
+ const iconNode = renderedComponent.find(".open-inspector");
+ expect(iconNode.prop("title")).toEqual(inspectIconTitle);
+ });
+});
+
+describe("ElementNode - Cursor style", () => {
+ const stub = stubs.get("Node");
+
+ it("renders with styled cursor", async () => {
+ const window = await createWindowForCursorTest();
+ const attachTo = window.document.querySelector("#attach-to");
+ const renderedComponent = mount(
+ ElementNode.rep({
+ object: stub,
+ onDOMNodeClick: jest.fn(),
+ onInspectIconClick: jest.fn(),
+ }),
+ {
+ attachTo,
+ }
+ );
+
+ const objectNode = renderedComponent.getDOMNode();
+ const iconNode = objectNode.querySelector(".open-inspector");
+ expect(renderedComponent.hasClass("clickable")).toBeTruthy();
+ expect(window.getComputedStyle(objectNode).cursor).toEqual("pointer");
+ expect(window.getComputedStyle(iconNode).cursor).toEqual("pointer");
+ });
+
+ it("renders with unstyled cursor", async () => {
+ const window = await createWindowForCursorTest();
+ const attachTo = window.document.querySelector("#attach-to");
+ const renderedComponent = mount(
+ ElementNode.rep({
+ object: stub,
+ }),
+ {
+ attachTo,
+ }
+ );
+
+ const objectNode = renderedComponent.getDOMNode();
+ expect(renderedComponent.hasClass("clickable")).toBeFalsy();
+ expect(window.getComputedStyle(objectNode).cursor).toEqual("");
+ });
+});
+
+async function createWindowForCursorTest() {
+ const path = require("path");
+ const css = await readTextFile(
+ path.resolve(__dirname, "../../../../reps/", "reps.css")
+ );
+ const html = `
+ <body>
+ <style>${css}</style>
+ <div id="attach-to"></div>
+ </body>
+ `;
+
+ return new JSDOM(html).window;
+}
+
+async function readTextFile(fileName) {
+ return new Promise((resolve, reject) => {
+ const fs = require("fs");
+ fs.readFile(fileName, "utf8", (error, text) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(text);
+ }
+ });
+ });
+}
diff --git a/devtools/client/shared/components/test/node/components/reps/error.test.js b/devtools/client/shared/components/test/node/components/reps/error.test.js
new file mode 100644
index 0000000000..3486a6df44
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/error.test.js
@@ -0,0 +1,869 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const { ErrorRep } = REPS;
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/error.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+
+describe("Error - Simple error", () => {
+ // Test object = `new Error("Error message")`
+ const stub = stubs.get("SimpleError");
+
+ it("correctly selects Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for simple error", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ expect(renderedComponent.prop("title")).toBe('Error: "Error message"');
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders with expected text for simple error in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Error");
+ });
+
+ it("renders with expected text for simple error in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Error");
+ });
+
+ it("renders with error type and preview message when in short mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stubs.get("MultilineStackError"),
+ mode: MODE.SHORT,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with error type only when customFormat prop isn't set", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stubs.get("MultilineStackError"),
+ mode: MODE.SHORT,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with error type only when depth is > 0", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stubs.get("MultilineStackError"),
+ customFormat: true,
+ depth: 1,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+});
+
+describe("Error - Multi line stack error", () => {
+ /*
+ * Test object = `
+ * function errorFoo() {
+ * errorBar();
+ * }
+ * function errorBar() {
+ * console.log(new Error("bar"));
+ * }
+ * errorFoo();`
+ */
+ const stub = stubs.get("MultilineStackError");
+
+ it("correctly selects the Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for Error object", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders expected text for simple multiline error in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Error");
+ });
+
+ it("renders expected text for simple multiline error in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Error");
+ });
+});
+
+describe("Error - Error without stacktrace", () => {
+ const stub = stubs.get("ErrorWithoutStacktrace");
+
+ it("correctly selects the Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for Error object", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Error: Error message");
+ });
+
+ it("renders expected text for error without stacktrace in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Error");
+ });
+});
+
+describe("Error - Eval error", () => {
+ // Test object = `new EvalError("EvalError message")`
+ const stub = stubs.get("EvalError");
+
+ it("correctly selects the Error Rep for EvalError object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for an EvalError", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text for an EvalError in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("EvalError");
+ });
+
+ it("renders with expected text for an EvalError in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("EvalError");
+ });
+});
+
+describe("Error - Internal error", () => {
+ // Test object = `new InternalError("InternalError message")`
+ const stub = stubs.get("InternalError");
+
+ it("correctly selects the Error Rep for InternalError object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for an InternalError", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text for an InternalError in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("InternalError");
+ });
+
+ it("renders with expected text for an InternalError in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("InternalError");
+ });
+});
+
+describe("Error - Range error", () => {
+ // Test object = `new RangeError("RangeError message")`
+ const stub = stubs.get("RangeError");
+
+ it("correctly selects the Error Rep for RangeError object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for RangeError", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text for RangeError in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("RangeError");
+ });
+
+ it("renders with expected text for RangeError in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("RangeError");
+ });
+});
+
+describe("Error - Reference error", () => {
+ // Test object = `new ReferenceError("ReferenceError message"`
+ const stub = stubs.get("ReferenceError");
+
+ it("correctly selects the Error Rep for ReferenceError object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for ReferenceError", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text for ReferenceError in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("ReferenceError");
+ });
+
+ it("renders with expected text for ReferenceError in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("ReferenceError");
+ });
+});
+
+describe("Error - Syntax error", () => {
+ // Test object = `new SyntaxError("SyntaxError message"`
+ const stub = stubs.get("SyntaxError");
+
+ it("correctly selects the Error Rep for SyntaxError object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for SyntaxError", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text for SyntaxError in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("SyntaxError");
+ });
+
+ it("renders with expected text for SyntaxError in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("SyntaxError");
+ });
+});
+
+describe("Error - Type error", () => {
+ // Test object = `new TypeError("TypeError message"`
+ const stub = stubs.get("TypeError");
+
+ it("correctly selects the Error Rep for TypeError object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for TypeError", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text for TypeError in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("TypeError");
+ });
+
+ it("renders with expected text for TypeError in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("TypeError");
+ });
+});
+
+describe("Error - URI error", () => {
+ // Test object = `new URIError("URIError message")`
+ const stub = stubs.get("URIError");
+
+ it("correctly selects the Error Rep for URIError object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for URIError", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders with expected text for URIError in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("URIError");
+ });
+
+ it("renders with expected text for URIError in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("URIError");
+ });
+});
+
+describe("Error - DOMException", () => {
+ const stub = stubs.get("DOMException");
+
+ it("correctly selects Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text for DOMException", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "DOMException: 'foo;()bar!' is not a valid selector"
+ );
+ });
+
+ it("renders with expected text for DOMException in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("DOMException");
+ });
+
+ it("renders with expected text for DOMException in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("DOMException");
+ });
+});
+
+describe("Error - base-loader.sys.mjs", () => {
+ const stub = stubs.get("base-loader Error");
+
+ it("renders as expected without mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders as expected in tiny mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("renders as expected in HEADER mode", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.HEADER,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+});
+
+describe("Error - longString stacktrace", () => {
+ const stub = stubs.get("longString stack Error");
+
+ it("renders as expected", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+});
+
+describe("Error - longString stacktrace - cut-off location", () => {
+ const stub = stubs.get("longString stack Error - cut-off location");
+
+ it("renders as expected", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+});
+
+describe("Error - stacktrace location click", () => {
+ it("Calls onViewSourceInDebugger with the expected arguments", () => {
+ const onViewSourceInDebugger = jest.fn();
+ const object = stubs.get("base-loader Error");
+
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object,
+ onViewSourceInDebugger,
+ customFormat: true,
+ })
+ );
+
+ const locations = renderedComponent.find(".objectBox-stackTrace-location");
+ expect(locations.exists()).toBeTruthy();
+
+ expect(locations.first().prop("title")).toBe(
+ "View source in debugger → " +
+ "resource://devtools/client/debugger-client.js:856:9"
+ );
+ locations.first().simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+
+ expect(onViewSourceInDebugger.mock.calls).toHaveLength(1);
+ let mockCall = onViewSourceInDebugger.mock.calls[0][0];
+ expect(mockCall.url).toEqual(
+ "resource://devtools/client/debugger-client.js"
+ );
+ expect(mockCall.line).toEqual(856);
+ expect(mockCall.column).toEqual(9);
+
+ expect(locations.last().prop("title")).toBe(
+ "View source in debugger → " +
+ "resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14"
+ );
+ locations.last().simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+
+ expect(onViewSourceInDebugger.mock.calls).toHaveLength(2);
+ mockCall = onViewSourceInDebugger.mock.calls[1][0];
+ expect(mockCall.url).toEqual(
+ "resource://devtools/shared/ThreadSafeDevToolsUtils.js"
+ );
+ expect(mockCall.line).toEqual(109);
+ expect(mockCall.column).toEqual(14);
+ });
+
+ it("Does not call onViewSourceInDebugger on excluded urls", () => {
+ const onViewSourceInDebugger = jest.fn();
+ const object = stubs.get("URIError");
+
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object,
+ onViewSourceInDebugger,
+ customFormat: true,
+ })
+ );
+
+ const locations = renderedComponent.find(".objectBox-stackTrace-location");
+ expect(locations.exists()).toBeTruthy();
+ expect(locations.first().prop("title")).toBe(undefined);
+
+ locations.first().simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+
+ expect(onViewSourceInDebugger.mock.calls).toHaveLength(0);
+ });
+
+ it("Does not throw when onViewSourceInDebugger props is not provided", () => {
+ const object = stubs.get("base-loader Error");
+
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object,
+ customFormat: true,
+ })
+ );
+
+ const locations = renderedComponent.find(".objectBox-stackTrace-location");
+ expect(locations.exists()).toBeTruthy();
+ expect(locations.first().prop("title")).toBe(undefined);
+
+ locations.first().simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+ });
+});
+
+describe("Error - renderStacktrace prop", () => {
+ it("uses renderStacktrace prop when provided", () => {
+ const stub = stubs.get("MultilineStackError");
+
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ renderStacktrace: frames => {
+ return frames.map(frame =>
+ dom.li(
+ { className: "frame" },
+ `Function ${frame.functionName} called from ${frame.filename}:${frame.lineNumber}:${frame.columnNumber}\n`
+ )
+ );
+ },
+ customFormat: true,
+ })
+ );
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("uses renderStacktrace with longString errors too", () => {
+ const stub = stubs.get("longString stack Error - cut-off location");
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ renderStacktrace: frames => {
+ return frames.map(frame =>
+ dom.li(
+ { className: "frame" },
+ `Function ${frame.functionName} called from ${frame.filename}:${frame.lineNumber}:${frame.columnNumber}\n`
+ )
+ );
+ },
+ customFormat: true,
+ })
+ );
+ expect(renderedComponent).toMatchSnapshot();
+ });
+});
+
+describe("Error - Error with V8-like stack", () => {
+ // Test object:
+ // x = new Error("BOOM");
+ // x.stack = "Error: BOOM\ngetAccount@http://moz.com/script.js:1:2";
+ const stub = stubs.get("Error with V8-like stack");
+
+ it("correctly selects Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
+
+describe("Error - Error with invalid stack", () => {
+ // Test object:
+ // x = new Error("bad stack");
+ // x.stack = "bar\nbaz\nfoo\n\n\n\n\n\n\n";
+ const stub = stubs.get("Error with invalid stack");
+
+ it("correctly selects Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
+
+describe("Error - Error with undefined-grip stack", () => {
+ // Test object:
+ // x = new Error("sd");
+ // x.stack = undefined;
+ const stub = stubs.get("Error with undefined-grip stack");
+
+ it("correctly selects Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
+
+describe("Error - Error with undefined-grip name", () => {
+ // Test object:
+ // x = new Error("");
+ // x.name = undefined;
+ const stub = stubs.get("Error with undefined-grip name");
+
+ it("correctly selects Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+ expect(renderedComponent).toMatchSnapshot();
+
+ const tinyRenderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(tinyRenderedComponent).toMatchSnapshot();
+ });
+});
+
+describe("Error - Error with undefined-grip message", () => {
+ // Test object:
+ // x = new Error("");
+ // x.message = undefined;
+ const stub = stubs.get("Error with undefined-grip message");
+
+ it("correctly selects Error Rep for Error object", () => {
+ expect(getRep(stub)).toBe(ErrorRep.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+ expect(renderedComponent).toMatchSnapshot();
+
+ const tinyRenderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(tinyRenderedComponent).toMatchSnapshot();
+ });
+});
+
+describe("Error - Error with stack having frames with multiple @", () => {
+ const stub = stubs.get("Error with stack having frames with multiple @");
+
+ it("renders with expected text for Error object", () => {
+ const renderedComponent = shallow(
+ ErrorRep.rep({
+ object: stub,
+ customFormat: true,
+ })
+ );
+
+ expect(renderedComponent).toMatchSnapshot();
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/event.test.js b/devtools/client/shared/components/test/node/components/reps/event.test.js
new file mode 100644
index 0000000000..fe0f6b5601
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/event.test.js
@@ -0,0 +1,160 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Event } = REPS;
+const {
+ expectActorAttribute,
+ getSelectableInInspectorGrips,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/event.js");
+
+describe("Event - beforeprint", () => {
+ const object = stubs.get("testEvent");
+
+ it("correctly selects Event Rep", () => {
+ expect(getRep(object)).toBe(Event.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderedComponent = shallow(Event.rep({ object }));
+ expect(renderedComponent.text()).toEqual(
+ "beforeprint { target: Window, isTrusted: true, currentTarget: Window, " +
+ "… }"
+ );
+ expectActorAttribute(renderedComponent, object.actor);
+ });
+});
+
+describe("Event - keyboard event", () => {
+ const object = stubs.get("testKeyboardEvent");
+
+ it("correctly selects Event Rep", () => {
+ expect(getRep(object)).toBe(Event.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderRep = props => shallow(Event.rep({ object, ...props }));
+ expect(renderRep().text()).toEqual(
+ 'keyup { target: body, key: "Control", charCode: 0, … }'
+ );
+ expect(renderRep({ mode: MODE.LONG }).text()).toEqual(
+ 'keyup { target: body, key: "Control", charCode: 0, keyCode: 17 }'
+ );
+ });
+});
+
+describe("Event - keyboard event with modifiers", () => {
+ const object = stubs.get("testKeyboardEventWithModifiers");
+
+ it("correctly selects Event Rep", () => {
+ expect(getRep(object)).toBe(Event.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderRep = props => shallow(Event.rep({ object, ...props }));
+ expect(renderRep({ mode: MODE.LONG }).text()).toEqual(
+ 'keyup Meta-Shift { target: body, key: "M", charCode: 0, keyCode: 77 }'
+ );
+ });
+});
+
+describe("Event - message event", () => {
+ const object = stubs.get("testMessageEvent");
+
+ it("correctly selects Event Rep", () => {
+ expect(getRep(object)).toBe(Event.rep);
+ });
+
+ it("renders with expected text", () => {
+ const renderRep = props => shallow(Event.rep({ object, ...props }));
+ expect(renderRep().text()).toEqual(
+ 'message { target: Window, isTrusted: false, data: "test data", … }'
+ );
+ expect(renderRep({ mode: MODE.LONG }).text()).toEqual(
+ 'message { target: Window, isTrusted: false, data: "test data", ' +
+ 'origin: "null", lastEventId: "", source: Window, ports: Array, ' +
+ "currentTarget: Window, eventPhase: 2, bubbles: false, … }"
+ );
+ });
+});
+
+describe("Event - mouse event", () => {
+ const object = stubs.get("testMouseEvent");
+ const renderRep = props => shallow(Event.rep({ object, ...props }));
+
+ const grips = getSelectableInInspectorGrips(object);
+
+ it("has stub with one node grip", () => {
+ expect(grips).toHaveLength(1);
+ });
+
+ it("correctly selects Event Rep", () => {
+ expect(getRep(object)).toBe(Event.rep);
+ });
+
+ it("renders with expected text", () => {
+ expect(renderRep({ shouldRenderTooltip: true }).text()).toEqual(
+ "click { target: div#test, clientX: 62, clientY: 18, … }"
+ );
+ expect(renderRep({ shouldRenderTooltip: true }).prop("title")).toEqual(
+ "click"
+ );
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).text()
+ ).toEqual(
+ "click { target: div#test, buttons: 0, clientX: 62, clientY: 18, " +
+ "layerX: 0, layerY: 0 }"
+ );
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).prop("title")
+ ).toEqual("click");
+ });
+
+ it("renders an inspect icon", () => {
+ const onInspectIconClick = jest.fn();
+ const renderedComponent = renderRep({ onInspectIconClick });
+
+ const node = renderedComponent.find(".open-inspector");
+ node.simulate("click", { type: "click" });
+
+ expect(node.exists()).toBeTruthy();
+ expect(onInspectIconClick.mock.calls).toHaveLength(1);
+ expect(onInspectIconClick.mock.calls[0][0]).toEqual(grips[0]);
+ expect(onInspectIconClick.mock.calls[0][1].type).toEqual("click");
+ });
+
+ it("calls the expected function when mouseout is fired on Rep", () => {
+ const onDOMNodeMouseOut = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOut });
+
+ const node = wrapper.find(".objectBox-node");
+ node.simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOut.mock.calls[0][0]).toEqual(grips[0]);
+ });
+
+ it("calls the expected function when mouseover is fired on Rep", () => {
+ const onDOMNodeMouseOver = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOver });
+
+ const node = wrapper.find(".objectBox-node");
+ node.simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOver.mock.calls[0][0]).toEqual(grips[0]);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/failure.test.js b/devtools/client/shared/components/test/node/components/reps/failure.test.js
new file mode 100644
index 0000000000..6f31ae5687
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/failure.test.js
@@ -0,0 +1,66 @@
+/* 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/>. */
+
+"use strict";
+
+/* global beforeAll, afterAll */
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/failure.js");
+
+let originalConsoleError;
+beforeAll(() => {
+ // Let's override the console.error function so we don't get an error message
+ // in the jest output for the expected exception.
+ originalConsoleError = window.console.error;
+ window.console.error = () => {};
+});
+
+describe("test Failure", () => {
+ const stub = stubs.get("Failure");
+
+ it("Fallback rendering has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+ expect(renderedComponent.text()).toEqual("Invalid object");
+ });
+
+ it("Fallback array rendering has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn0.obj337",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ items: [1, stub, 2],
+ },
+ },
+ })
+ );
+ expect(renderedComponent.text()).toEqual(
+ "Array(3) [ 1, Invalid object, 2 ]"
+ );
+ });
+});
+
+afterAll(() => {
+ // Reverting the override.
+ window.console.error = originalConsoleError;
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/function.test.js b/devtools/client/shared/components/test/node/components/reps/function.test.js
new file mode 100644
index 0000000000..b9ad70dd26
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/function.test.js
@@ -0,0 +1,584 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { shallow } = require("enzyme");
+const {
+ REPS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const { Func } = REPS;
+const { getFunctionName } = Func;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/function.js");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const renderRep = (object, props) => {
+ return shallow(Func.rep({ object, ...props }));
+};
+
+describe("Function - Named", () => {
+ // Test declaration: `function testName() { let innerVar = "foo" }`
+ const object = stubs.get("Named");
+
+ it("renders named function as expected", () => {
+ expect(
+ renderRep(object, { mode: undefined, shouldRenderTooltip: true }).text()
+ ).toBe("function testName()");
+ expect(
+ renderRep(object, { mode: undefined, shouldRenderTooltip: true }).prop(
+ "title"
+ )
+ ).toBe("function testName()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: [] },
+ { shouldRenderTooltip: true }
+ ).text()
+ ).toBe("function testName()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: [] },
+ { shouldRenderTooltip: true }
+ ).prop("title")
+ ).toBe("function testName()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a"] },
+ { shouldRenderTooltip: true }
+ ).text()
+ ).toBe("function testName(a)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a"] },
+ { shouldRenderTooltip: true }
+ ).prop("title")
+ ).toBe("function testName(a)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { shouldRenderTooltip: true }
+ ).text()
+ ).toBe("function testName(a, b, c)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { shouldRenderTooltip: true }
+ ).prop("title")
+ ).toBe("function testName(a, b, c)");
+ expect(
+ renderRep(object, { mode: MODE.TINY, shouldRenderTooltip: true }).text()
+ ).toBe("testName()");
+ expect(
+ renderRep(object, { mode: MODE.TINY, shouldRenderTooltip: true }).prop(
+ "title"
+ )
+ ).toBe("testName()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { mode: MODE.TINY, shouldRenderTooltip: true }
+ ).text()
+ ).toBe("testName(a, b, c)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { mode: MODE.TINY, shouldRenderTooltip: true }
+ ).prop("title")
+ ).toBe("testName(a, b, c)");
+
+ expectActorAttribute(renderRep(object), object.actor);
+ });
+});
+
+describe("Function - User named", () => {
+ // Test declaration: `function testName() { let innerVar = "foo" }`
+ const object = stubs.get("UserNamed");
+
+ it("renders user named function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(
+ "function testUserName()"
+ );
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ "function testUserName()"
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ "function testUserName(a)"
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe("function testUserName(a, b, c)");
+ expect(
+ renderRep(object, {
+ mode: MODE.TINY,
+ }).text()
+ ).toBe("testUserName()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe("testUserName(a, b, c)");
+ });
+});
+
+describe("Function - Var named", () => {
+ // Test declaration: `let testVarName = function() { }`
+ const object = stubs.get("VarNamed");
+
+ it("renders var named function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(
+ "function testVarName()"
+ );
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ "function testVarName()"
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ "function testVarName(a)"
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe("function testVarName(a, b, c)");
+ expect(
+ renderRep(object, {
+ mode: MODE.TINY,
+ }).text()
+ ).toBe("testVarName()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe("testVarName(a, b, c)");
+ });
+});
+
+describe("Function - Anonymous", () => {
+ // Test declaration: `() => {}`
+ const object = stubs.get("Anon");
+
+ it("renders anonymous function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe("function ()");
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ "function ()"
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ "function (a)"
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe("function (a, b, c)");
+ expect(
+ renderRep(object, {
+ mode: MODE.TINY,
+ }).text()
+ ).toBe("()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe("(a, b, c)");
+ });
+});
+
+describe("Function - Long name", () => {
+ // eslint-disable-next-line max-len
+ // Test declaration: `let f = function loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong() { }`
+ const object = stubs.get("LongName");
+ const functionName =
+ "looooooooooooooooooooooooooooooooooooooooooooooo" +
+ "oo\u2026ooooooooooooooooooooooooooooooooooooooooooooooong";
+
+ it("renders long name function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(
+ `function ${functionName}()`
+ );
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ `function ${functionName}()`
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ `function ${functionName}(a)`
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe(`function ${functionName}(a, b, c)`);
+ expect(
+ renderRep(object, {
+ mode: MODE.TINY,
+ }).text()
+ ).toBe(`${functionName}()`);
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe(`${functionName}(a, b, c)`);
+ });
+});
+
+describe("Function - Async function", () => {
+ const object = stubs.get("AsyncFunction");
+
+ it("renders async function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(
+ "async function waitUntil2017()"
+ );
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe(
+ "async waitUntil2017()"
+ );
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(
+ "async function waitUntil2017()"
+ );
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(
+ "async function waitUntil2017()"
+ );
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ "async function waitUntil2017()"
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ "async function waitUntil2017(a)"
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe("async function waitUntil2017(a, b, c)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe("async waitUntil2017(a, b, c)");
+ });
+});
+
+describe("Function - Anonymous async function", () => {
+ const object = stubs.get("AnonAsyncFunction");
+
+ it("renders anonymous async function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(
+ "async function ()"
+ );
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe("async ()");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(
+ "async function ()"
+ );
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(
+ "async function ()"
+ );
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ "async function ()"
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ "async function (a)"
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe("async function (a, b, c)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe("async (a, b, c)");
+ });
+});
+
+describe("Function - Generator function", () => {
+ const object = stubs.get("GeneratorFunction");
+
+ it("renders generator function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(
+ "function* fib()"
+ );
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe("* fib()");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(
+ "function* fib()"
+ );
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(
+ "function* fib()"
+ );
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ "function* fib()"
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ "function* fib(a)"
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe("function* fib(a, b, c)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe("* fib(a, b, c)");
+ });
+});
+
+describe("Function - Anonymous generator function", () => {
+ const object = stubs.get("AnonGeneratorFunction");
+
+ it("renders anonymous generator function as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe("function* ()");
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe("* ()");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe("function* ()");
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe("function* ()");
+ expect(renderRep({ ...object, parameterNames: [] }).text()).toBe(
+ "function* ()"
+ );
+ expect(renderRep({ ...object, parameterNames: ["a"] }).text()).toBe(
+ "function* (a)"
+ );
+ expect(
+ renderRep({ ...object, parameterNames: ["a", "b", "c"] }).text()
+ ).toBe("function* (a, b, c)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ }
+ ).text()
+ ).toBe("* (a, b, c)");
+ });
+});
+
+describe("Function - Jump to definition", () => {
+ it("renders an icon when onViewSourceInDebugger props is provided", async () => {
+ let onViewSourceInDebugger;
+ const onViewSourceCalled = new Promise(resolve => {
+ onViewSourceInDebugger = jest.fn(resolve);
+ });
+ const object = stubs.get("getRandom");
+ const renderedComponent = renderRep(object, {
+ onViewSourceInDebugger,
+ });
+
+ const node = renderedComponent.find(".jump-definition");
+ node.simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+ await onViewSourceCalled;
+
+ expect(node.exists()).toBeTruthy();
+ expect(onViewSourceInDebugger.mock.calls).toHaveLength(1);
+ expect(onViewSourceInDebugger.mock.calls[0][0]).toEqual(object.location);
+ });
+
+ it("calls recordTelemetryEvent when jump to definition icon clicked", () => {
+ const onViewSourceInDebugger = jest.fn();
+ const recordTelemetryEvent = jest.fn();
+ const object = stubs.get("getRandom");
+ const renderedComponent = renderRep(object, {
+ onViewSourceInDebugger,
+ recordTelemetryEvent,
+ });
+
+ const node = renderedComponent.find(".jump-definition");
+ node.simulate("click", {
+ type: "click",
+ stopPropagation: () => {},
+ });
+
+ expect(node.exists()).toBeTruthy();
+ expect(recordTelemetryEvent.mock.calls).toHaveLength(1);
+ expect(recordTelemetryEvent.mock.calls[0][0]).toEqual("jump_to_definition");
+ });
+
+ it("no icon when onViewSourceInDebugger props not provided", () => {
+ const object = stubs.get("getRandom");
+ const renderedComponent = renderRep(object);
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+
+ it("does not render an icon when the object has no location", () => {
+ const object = {
+ ...stubs.get("getRandom"),
+ location: null,
+ };
+
+ const renderedComponent = renderRep(object, {
+ onViewSourceInDebugger: () => {},
+ });
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+
+ it("does not render an icon when the object has no url location", () => {
+ const object = {
+ ...stubs.get("getRandom"),
+ };
+ object.location = { ...object.location, url: null };
+ const renderedComponent = renderRep(object, {
+ onViewSourceInDebugger: () => {},
+ });
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+
+ it("no icon when function was declared in console input", () => {
+ const object = stubs.get("EvaledInDebuggerFunction");
+ const renderedComponent = renderRep(object, {
+ onViewSourceInDebugger: () => {},
+ });
+
+ const node = renderedComponent.find(".jump-definition");
+ expect(node.exists()).toBeFalsy();
+ });
+});
+
+describe("Function - Simplify name", () => {
+ const cases = {
+ defaultCase: [["define", "define"]],
+
+ objectProperty: [
+ ["z.foz", "foz"],
+ ["z.foz/baz", "baz"],
+ ["z.foz/baz/y.bay", "bay"],
+ ["outer/x.fox.bax.nx", "nx"],
+ ["outer/fow.baw", "baw"],
+ ["fromYUI._attach", "_attach"],
+ ["Y.ClassNameManager</getClassName", "getClassName"],
+ ["orion.textview.TextView</addHandler", "addHandler"],
+ ["this.eventPool_.createObject", "createObject"],
+ ],
+
+ arrayProperty: [
+ ["this.eventPool_[createObject]", "createObject"],
+ ["jQuery.each(^)/jQuery.fn[o]", "o"],
+ ["viewport[get+D]", "get+D"],
+ ["arr[0]", "0"],
+ ],
+
+ functionProperty: [
+ ["fromYUI._attach/<.", "_attach"],
+ ["Y.ClassNameManager<", "ClassNameManager"],
+ ["fromExtJS.setVisible/cb<", "cb"],
+ ["fromDojo.registerWin/<", "registerWin"],
+ ],
+
+ annonymousProperty: [["jQuery.each(^)", "each"]],
+ };
+
+ Object.keys(cases).forEach(type => {
+ for (const [kase, expected] of cases[type]) {
+ it(`${type} - ${kase}`, () =>
+ expect(getFunctionName({ displayName: kase })).toEqual(expected));
+ }
+ });
+});
+describe("Function - Two properties with same displayName", () => {
+ const object = stubs.get("ObjectProperty");
+
+ it("renders object properties as expected", () => {
+ expect(
+ renderRep(object, { mode: undefined, functionName: "$" }).text()
+ ).toBe("function $:jQuery()");
+ expect(
+ renderRep({ ...object, parameterNames: [] }, { functionName: "$" }).text()
+ ).toBe("function $:jQuery()");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a"] },
+ { functionName: "$" }
+ ).text()
+ ).toBe("function $:jQuery(a)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ functionName: "$",
+ }
+ ).text()
+ ).toBe("function $:jQuery(a, b, c)");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ {
+ mode: MODE.TINY,
+ functionName: "$",
+ }
+ ).text()
+ ).toBe("$:jQuery(a, b, c)");
+ });
+});
+
+describe("Function - Class constructor", () => {
+ const object = stubs.get("EmptyClass");
+
+ it("renders empty class as expected", () => {
+ expect(
+ renderRep(object, { mode: undefined, shouldRenderTooltip: true }).text()
+ ).toBe("class EmptyClass {}");
+ expect(
+ renderRep(object, { mode: undefined, shouldRenderTooltip: true }).prop(
+ "title"
+ )
+ ).toBe("class EmptyClass {}");
+ });
+
+ it("renders empty class in MODE.TINY as expected", () => {
+ expect(
+ renderRep(object, { mode: MODE.TINY, shouldRenderTooltip: true }).text()
+ ).toBe("class EmptyClass");
+ expect(
+ renderRep(object, { mode: MODE.TINY, shouldRenderTooltip: true }).prop(
+ "title"
+ )
+ ).toBe("class EmptyClass");
+ });
+
+ it("renders class with constructor as expected", () => {
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { shouldRenderTooltip: true }
+ ).text()
+ ).toBe("class EmptyClass { constructor(a, b, c) }");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { shouldRenderTooltip: true }
+ ).prop("title")
+ ).toBe("class EmptyClass { constructor(a, b, c) }");
+ });
+
+ it("renders class with constructor in MODE.TINY as expected", () => {
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { mode: MODE.TINY, shouldRenderTooltip: true }
+ ).text()
+ ).toBe("class EmptyClass");
+ expect(
+ renderRep(
+ { ...object, parameterNames: ["a", "b", "c"] },
+ { mode: MODE.TINY, shouldRenderTooltip: true }
+ ).prop("title")
+ ).toBe("class EmptyClass");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/grip-array.test.js b/devtools/client/shared/components/test/node/components/reps/grip-array.test.js
new file mode 100644
index 0000000000..b69bcb1dc5
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/grip-array.test.js
@@ -0,0 +1,755 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { shallow } = require("enzyme");
+const {
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const GripArray = require("resource://devtools/client/shared/components/reps/reps/grip-array.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const {
+ expectActorAttribute,
+ getSelectableInInspectorGrips,
+ getGripLengthBubbleText,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const { maxLengthMap } = GripArray;
+
+function shallowRenderRep(object, props = {}) {
+ return shallow(
+ GripArray.rep({
+ object,
+ ...props,
+ })
+ );
+}
+
+describe("GripArray - basic", () => {
+ const object = stubs.get("testBasic");
+
+ it("correctly selects GripArray Rep", () => {
+ expect(getRep(object)).toBe(GripArray.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+ const defaultOutput = `Array${length} []`;
+
+ let component = renderRep({ mode: undefined, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe("Array");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY, shouldRenderTooltip: true });
+ expect(component.text()).toBe("[]");
+ expect(component.prop("title")).toBe("Array");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true });
+ expect(component.text()).toBe("Array");
+ expect(component.prop("title")).toBe("Array");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe("Array");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe("Array");
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("GripArray - max props", () => {
+ const object = stubs.get("testMaxProps");
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+
+ let length = getGripLengthBubbleText(object);
+ const defaultOutput = `Array${length} [ 1, "foo", {} ]`;
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ // Check the custom title with nested objects to make sure nested objects
+ // are not displayed with their parent's title.
+ expect(
+ renderRep({
+ mode: MODE.LONG,
+ title: "CustomTitle",
+ }).text()
+ ).toBe(`CustomTitle${length} [ 1, "foo", {} ]`);
+ });
+});
+
+describe("GripArray - more than short mode max props", () => {
+ const object = stubs.get("testMoreThanShortMaxProps");
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+
+ const shortLength = maxLengthMap.get(MODE.SHORT);
+ const shortContent = Array(shortLength).fill('"test string"').join(", ");
+ const longContent = Array(shortLength + 1)
+ .fill('"test string"')
+ .join(", ");
+ const defaultOutput = `Array${length} [ ${shortContent}, … ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ `Array${length} [ ${longContent} ]`
+ );
+ });
+});
+
+describe("GripArray - more than long mode max props", () => {
+ const object = stubs.get("testMoreThanLongMaxProps");
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const shortLength = maxLengthMap.get(MODE.SHORT);
+ const longLength = maxLengthMap.get(MODE.LONG);
+ const shortContent = Array(shortLength).fill('"test string"').join(", ");
+ const defaultOutput = `Array${length} [ ${shortContent}, … ]`;
+ const longContent = Array(longLength).fill('"test string"').join(", ");
+
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Array");
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).text()
+ ).toBe(`${length} […]`);
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Array");
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).text()
+ ).toBe(`Array${length}`);
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Array");
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Array");
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).text()
+ ).toBe(`Array${length} [ ${longContent}, … ]`);
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Array");
+ });
+});
+
+describe("GripArray - recursive array", () => {
+ const object = stubs.get("testRecursiveArray");
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+ const childArrayLength = getGripLengthBubbleText(object.preview.items[0], {
+ mode: MODE.TINY,
+ });
+
+ const defaultOutput = `Array${length} [ ${childArrayLength} […] ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("GripArray - preview limit", () => {
+ const object = stubs.get("testPreviewLimit");
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const shortOutput = `Array${length} [ 0, 1, 2, … ]`;
+ const longOutput = `Array${length} [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(shortOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(shortOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+});
+
+describe("GripArray - empty slots", () => {
+ it("renders an array with empty slots only as expected", () => {
+ const object = stubs.get("Array(5)");
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+ const defaultOutput = `Array${length} [ <5 empty slots> ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ const longOutput = `Array${length} [ <5 empty slots> ]`;
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("one empty slot at the beginning as expected", () => {
+ const object = stubs.get("[,1,2,3]");
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ <1 empty slot>, 1, 2, … ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ const longOutput = `Array${length} [ <1 empty slot>, 1, 2, 3 ]`;
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("multiple consecutive empty slots at the beginning as expected", () => {
+ const object = stubs.get("[,,,3,4,5]");
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ <3 empty slots>, 3, 4, … ]`;
+ const longOutput = `Array${length} [ <3 empty slots>, 3, 4, 5 ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("one empty slot in the middle as expected", () => {
+ const object = stubs.get("[0,1,,3,4,5]");
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ 0, 1, <1 empty slot>, … ]`;
+ const longOutput = `Array${length} [ 0, 1, <1 empty slot>, 3, 4, 5 ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("successive empty slots in the middle as expected", () => {
+ const object = stubs.get("[0,1,,,,5]");
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ 0, 1, <3 empty slots>, … ]`;
+ const longOutput = `Array${length} [ 0, 1, <3 empty slots>, 5 ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("non successive single empty slots as expected", () => {
+ const object = stubs.get("[0,,2,,4,5]");
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ 0, <1 empty slot>, 2, … ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ `Array${length} [ 0, <1 empty slot>, 2, <1 empty slot>, 4, 5 ]`
+ );
+ });
+
+ it("multiple multi-slot holes as expected", () => {
+ const object = stubs.get("[0,,,3,,,,7,8]");
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ 0, <2 empty slots>, 3, … ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ `Array${length} [ 0, <2 empty slots>, 3, <3 empty slots>, 7, 8 ]`
+ );
+ });
+
+ it("a single slot hole at the end as expected", () => {
+ const object = stubs.get("[0,1,2,3,4,,]");
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ 0, 1, 2, … ]`;
+ const longOutput = `Array${length} [ 0, 1, 2, 3, 4, <1 empty slot> ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("multiple consecutive empty slots at the end as expected", () => {
+ const object = stubs.get("[0,1,2,,,,]");
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ 0, 1, 2, … ]`;
+ const longOutput = `Array${length} [ 0, 1, 2, <3 empty slots> ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+});
+
+describe("GripArray - NamedNodeMap", () => {
+ const object = stubs.get("testNamedNodeMap");
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+
+ const defaultOutput =
+ `NamedNodeMap${length} ` +
+ '[ class="myclass", cellpadding="7", border="3" ]';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`NamedNodeMap${length}`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(
+ `NamedNodeMap${length}`
+ );
+
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("GripArray - NodeList", () => {
+ const object = stubs.get("testNodeList");
+ const grips = getSelectableInInspectorGrips(object);
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+
+ it("renders as expected", () => {
+ const defaultOutput =
+ `NodeList${length} [ button#btn-1.btn.btn-log, ` +
+ "button#btn-2.btn.btn-err, button#btn-3.btn.btn-count ]";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`NodeList${length}`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`NodeList${length}`);
+
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+
+ it("has 3 node grip", () => {
+ expect(grips).toHaveLength(3);
+ });
+
+ it("calls the expected function on mouseover", () => {
+ const onDOMNodeMouseOver = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOver });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseover");
+ node.at(1).simulate("mouseover");
+ node.at(2).simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(3);
+ expect(onDOMNodeMouseOver.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOver.mock.calls[1][0]).toBe(grips[1]);
+ expect(onDOMNodeMouseOver.mock.calls[2][0]).toBe(grips[2]);
+ });
+
+ it("calls the expected function on mouseout", () => {
+ const onDOMNodeMouseOut = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOut });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseout");
+ node.at(1).simulate("mouseout");
+ node.at(2).simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(3);
+ expect(onDOMNodeMouseOut.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOut.mock.calls[1][0]).toBe(grips[1]);
+ expect(onDOMNodeMouseOut.mock.calls[2][0]).toBe(grips[2]);
+ });
+
+ it("calls the expected function on click", () => {
+ const onInspectIconClick = jest.fn();
+ const wrapper = renderRep({ onInspectIconClick });
+ const node = wrapper.find(".open-inspector");
+
+ node.at(0).simulate("click");
+ node.at(1).simulate("click");
+ node.at(2).simulate("click");
+
+ expect(onInspectIconClick.mock.calls).toHaveLength(3);
+ expect(onInspectIconClick.mock.calls[0][0]).toBe(grips[0]);
+ expect(onInspectIconClick.mock.calls[1][0]).toBe(grips[1]);
+ expect(onInspectIconClick.mock.calls[2][0]).toBe(grips[2]);
+ });
+
+ it("no inspect icon when nodes are not connected to the DOM tree", () => {
+ const renderedComponentWithoutInspectIcon = shallowRenderRep(
+ stubs.get("testDisconnectedNodeList")
+ );
+ const node = renderedComponentWithoutInspectIcon.find(".open-inspector");
+ expect(node.exists()).toBe(false);
+ });
+});
+
+describe("GripArray - DocumentFragment", () => {
+ const object = stubs.get("testDocumentFragment");
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+
+ let length = getGripLengthBubbleText(object);
+ const defaultOutput =
+ `DocumentFragment${length} [ li#li-0.list-element, ` +
+ "li#li-1.list-element, li#li-2.list-element, … ]";
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(
+ `DocumentFragment${length}`
+ );
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(
+ `DocumentFragment${length}`
+ );
+
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ const longOutput =
+ `DocumentFragment${length} [ ` +
+ "li#li-0.list-element, li#li-1.list-element, li#li-2.list-element, " +
+ "li#li-3.list-element, li#li-4.list-element ]";
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+});
+
+describe("GripArray - Items not in preview", () => {
+ const object = stubs.get("testItemsNotInPreview");
+
+ it("correctly selects GripArray Rep", () => {
+ expect(getRep(object)).toBe(GripArray.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+ const defaultOutput = `Array${length} [ … ]`;
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe(`${length} […]`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe(`Array${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("GripArray - Set", () => {
+ it("correctly selects GripArray Rep", () => {
+ const object = stubs.get("new Set([1,2,3,4])");
+ expect(getRep(object)).toBe(GripArray.rep);
+ });
+
+ it("renders short sets as expected", () => {
+ const object = stubs.get("new Set([1,2,3,4])");
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+ const defaultOutput = `Set${length} [ 1, 2, 3, … ]`;
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe(`Set${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe(`Set${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(`Set${length} [ 1, 2, 3, 4 ]`);
+ expectActorAttribute(component, object.actor);
+ });
+
+ it("renders larger sets as expected", () => {
+ const object = stubs.get("new Set([0,1,2,…,19])");
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+ const defaultOutput = `Set${length} [ 0, 1, 2, … ]`;
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe(`Set${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe(`Set${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(
+ `Set${length} [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, … ]`
+ );
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("GripArray - WeakSet", () => {
+ it("correctly selects GripArray Rep", () => {
+ const object = stubs.get(
+ "new WeakSet(document.querySelectorAll('button:nth-child(3n)'))"
+ );
+ expect(getRep(object)).toBe(GripArray.rep);
+ });
+
+ it("renders short WeakSets as expected", () => {
+ const object = stubs.get(
+ "new WeakSet(document.querySelectorAll('button:nth-child(3n)'))"
+ );
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+ const defaultOutput = `WeakSet${length} [ button, button, button, … ]`;
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe(`WeakSet${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe(`WeakSet${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(
+ `WeakSet${length} [ button, button, button, button ]`
+ );
+ expectActorAttribute(component, object.actor);
+ });
+
+ it("renders larger WeakSets as expected", () => {
+ const object = stubs.get(
+ "new WeakSet(document.querySelectorAll('div, button'))"
+ );
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+ const defaultOutput = `WeakSet${length} [ button, button, button, … ]`;
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe(`WeakSet${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe(`WeakSet${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(`WeakSet(12) [ ${"button, ".repeat(10)}… ]`);
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("GripArray - DOMTokenList", () => {
+ const object = stubs.get("DOMTokenList");
+
+ it("correctly selects GripArray Rep", () => {
+ expect(getRep(object)).toBe(GripArray.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getGripLengthBubbleText(object);
+ const defaultOutput = `DOMTokenList${length} []`;
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe(`DOMTokenList${length}`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("GripArray - accessor", () => {
+ it("renders an array with getter as expected", () => {
+ const object = stubs.get("TestArrayWithGetter");
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ Getter ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ const longOutput = `Array${length} [ Getter ]`;
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("renders an array with setter as expected", () => {
+ const object = stubs.get("TestArrayWithSetter");
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ Setter ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ const longOutput = `Array${length} [ Setter ]`;
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+
+ it("renders an array with getter and setter as expected", () => {
+ const object = stubs.get("TestArrayWithGetterAndSetter");
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getGripLengthBubbleText(object);
+
+ const defaultOutput = `Array${length} [ Getter & Setter ]`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`${length} […]`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Array${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ const longOutput = `Array${length} [ Getter & Setter ]`;
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/grip-entry.test.js b/devtools/client/shared/components/test/node/components/reps/grip-entry.test.js
new file mode 100644
index 0000000000..cdfcab755e
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/grip-entry.test.js
@@ -0,0 +1,191 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { GripEntry } = REPS;
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ createGripMapEntry,
+ getGripLengthBubbleText,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-entry.js");
+const nodeStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/element-node.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+
+const renderRep = (object, mode, props) => {
+ return shallow(
+ GripEntry.rep({
+ object,
+ mode,
+ ...props,
+ })
+ );
+};
+
+describe("GripEntry - simple", () => {
+ const stub = stubs.get("A → 0");
+
+ it("Rep correctly selects GripEntry Rep", () => {
+ expect(getRep(stub)).toBe(GripEntry.rep);
+ });
+
+ it("GripEntry rep has expected text content", () => {
+ const renderedComponent = renderRep(stub);
+ expect(renderedComponent.text()).toEqual("A → 0");
+ });
+});
+
+describe("GripEntry - createGripMapEntry", () => {
+ it("return the expected object", () => {
+ const entry = createGripMapEntry("A", 0);
+ expect(entry).toEqual(stubs.get("A → 0"));
+ });
+});
+
+describe("GripEntry - complex", () => {
+ it("Handles complex objects as key and value", () => {
+ let stub = gripArrayStubs.get("testBasic");
+ let length = getGripLengthBubbleText(stub);
+ let entry = createGripMapEntry("A", stub);
+ expect(renderRep(entry, MODE.TINY).text()).toEqual("A → []");
+ expect(renderRep(entry, MODE.SHORT).text()).toEqual(
+ `A → Array${length} []`
+ );
+ expect(renderRep(entry, MODE.LONG).text()).toEqual(`A → Array${length} []`);
+
+ entry = createGripMapEntry(stub, "A");
+ expect(renderRep(entry, MODE.TINY).text()).toEqual('[] → "A"');
+ expect(renderRep(entry, MODE.SHORT).text()).toEqual(
+ `Array${length} [] → "A"`
+ );
+ expect(renderRep(entry, MODE.LONG).text()).toEqual(
+ `Array${length} [] → "A"`
+ );
+
+ stub = gripArrayStubs.get("testMaxProps");
+ length = getGripLengthBubbleText(stub, { mode: MODE.TINY });
+ entry = createGripMapEntry("A", stub);
+ expect(renderRep(entry, MODE.TINY).text()).toEqual(`A → ${length} […]`);
+ length = getGripLengthBubbleText(stub);
+ expect(renderRep(entry, MODE.SHORT).text()).toEqual(
+ `A → Array${length} [ 1, "foo", {} ]`
+ );
+ length = getGripLengthBubbleText(stub, { mode: MODE.LONG });
+ expect(renderRep(entry, MODE.LONG).text()).toEqual(
+ `A → Array${length} [ 1, "foo", {} ]`
+ );
+
+ entry = createGripMapEntry(stub, "A");
+ length = getGripLengthBubbleText(stub, { mode: MODE.TINY });
+ expect(renderRep(entry, MODE.TINY).text()).toEqual(`${length} […] → "A"`);
+ length = getGripLengthBubbleText(stub, { mode: MODE.SHORT });
+ expect(renderRep(entry, MODE.SHORT).text()).toEqual(
+ `Array${length} [ 1, "foo", {} ] → "A"`
+ );
+ length = getGripLengthBubbleText(stub, { mode: MODE.LONG });
+ expect(renderRep(entry, MODE.LONG).text()).toEqual(
+ `Array${length} [ 1, "foo", {} ] → "A"`
+ );
+
+ stub = gripArrayStubs.get("testMoreThanShortMaxProps");
+ length = getGripLengthBubbleText(stub);
+ entry = createGripMapEntry("A", stub);
+ length = getGripLengthBubbleText(stub, { mode: MODE.TINY });
+ expect(renderRep(entry, MODE.TINY).text()).toEqual(`A → ${length} […]`);
+ length = getGripLengthBubbleText(stub, { mode: MODE.SHORT });
+ expect(renderRep(entry, MODE.SHORT).text()).toEqual(
+ `A → Array${length} [ "test string", "test string", "test string", … ]`
+ );
+ length = getGripLengthBubbleText(stub, { mode: MODE.LONG });
+ expect(renderRep(entry, MODE.LONG).text()).toEqual(
+ `A → Array${length} [ "test string", "test string", "test string",\
+ "test string" ]`
+ );
+
+ entry = createGripMapEntry(stub, "A");
+ length = getGripLengthBubbleText(stub, { mode: MODE.TINY });
+ expect(renderRep(entry, MODE.TINY).text()).toEqual(`${length} […] → "A"`);
+ length = getGripLengthBubbleText(stub, { mode: MODE.SHORT });
+ expect(renderRep(entry, MODE.SHORT).text()).toEqual(
+ `Array${length} [ "test string", "test string", "test string", … ] → "A"`
+ );
+ length = getGripLengthBubbleText(stub, { mode: MODE.LONG });
+ expect(renderRep(entry, MODE.LONG).text()).toEqual(
+ `Array${length} [ "test string", "test string", "test string", ` +
+ '"test string" ] → "A"'
+ );
+ });
+
+ it("Handles Element Nodes as key and value", () => {
+ const stub = nodeStubs.get("Node");
+
+ const onInspectIconClick = jest.fn();
+ const onDOMNodeMouseOut = jest.fn();
+ const onDOMNodeMouseOver = jest.fn();
+
+ let entry = createGripMapEntry("A", stub);
+ let renderedComponent = renderRep(entry, MODE.TINY, {
+ onInspectIconClick,
+ onDOMNodeMouseOut,
+ onDOMNodeMouseOver,
+ });
+ expect(renderRep(entry, MODE.TINY).text()).toEqual(
+ "A → input#newtab-customize-button.bar.baz"
+ );
+
+ let node = renderedComponent.find(".objectBox-node");
+ let icon = node.find(".open-inspector");
+ icon.simulate("click", { type: "click" });
+ expect(icon.exists()).toBeTruthy();
+ expect(onInspectIconClick.mock.calls).toHaveLength(1);
+ expect(onInspectIconClick.mock.calls[0][0]).toEqual(stub);
+ expect(onInspectIconClick.mock.calls[0][1].type).toEqual("click");
+
+ node.simulate("mouseout");
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOut.mock.calls[0][0]).toEqual(stub);
+
+ node.simulate("mouseover");
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOver.mock.calls[0][0]).toEqual(stub);
+
+ entry = createGripMapEntry(stub, "A");
+ renderedComponent = renderRep(entry, MODE.TINY, {
+ onInspectIconClick,
+ onDOMNodeMouseOut,
+ onDOMNodeMouseOver,
+ });
+ expect(renderRep(entry, MODE.TINY).text()).toEqual(
+ 'input#newtab-customize-button.bar.baz → "A"'
+ );
+
+ node = renderedComponent.find(".objectBox-node");
+ icon = node.find(".open-inspector");
+ icon.simulate("click", { type: "click" });
+ expect(node.exists()).toBeTruthy();
+ expect(onInspectIconClick.mock.calls).toHaveLength(2);
+ expect(onInspectIconClick.mock.calls[1][0]).toEqual(stub);
+ expect(onInspectIconClick.mock.calls[1][1].type).toEqual("click");
+
+ node.simulate("mouseout");
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(2);
+ expect(onDOMNodeMouseOut.mock.calls[1][0]).toEqual(stub);
+
+ node.simulate("mouseover");
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(2);
+ expect(onDOMNodeMouseOver.mock.calls[1][0]).toEqual(stub);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/grip-map.test.js b/devtools/client/shared/components/test/node/components/reps/grip-map.test.js
new file mode 100644
index 0000000000..729d912a21
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/grip-map.test.js
@@ -0,0 +1,390 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+
+const { shallow } = require("enzyme");
+const {
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const GripMap = require("resource://devtools/client/shared/components/reps/reps/grip-map.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+const {
+ expectActorAttribute,
+ getSelectableInInspectorGrips,
+ getMapLengthBubbleText,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const { maxLengthMap, getLength } = GripMap;
+
+function shallowRenderRep(object, props = {}) {
+ return shallow(
+ GripMap.rep({
+ object,
+ ...props,
+ })
+ );
+}
+
+describe("GripMap - empty map", () => {
+ const object = stubs.get("testEmptyMap");
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const length = getMapLengthBubbleText(object);
+ const defaultOutput = `Map${length}`;
+
+ let component = renderRep({ mode: undefined, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(`Map(${getLength(object)})`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(`Map(${getLength(object)})`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(`Map(${getLength(object)})`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(`Map(${getLength(object)})`);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG, shouldRenderTooltip: true });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(`Map(${getLength(object)})`);
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("GripMap - Symbol-keyed Map", () => {
+ // Test object:
+ // `new Map([[Symbol("a"), "value-a"], [Symbol("b"), "value-b"]])`
+ const object = stubs.get("testSymbolKeyedMap");
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getMapLengthBubbleText(object);
+ const out = `Map${length} { Symbol("a") → "value-a", Symbol("b") → "value-b" }`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(out);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(out);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(out);
+ });
+});
+
+describe("GripMap - WeakMap", () => {
+ // Test object: `new WeakMap([[{a: "key-a"}, "value-a"]])`
+ const object = stubs.get("testWeakMap");
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getMapLengthBubbleText(object);
+ const defaultOutput = `WeakMap${length} { {…} → "value-a" }`;
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).prop("title")
+ ).toBe(`WeakMap(${getLength(object)})`);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.TINY });
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).text()
+ ).toBe(`WeakMap${length}`);
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).prop("title")
+ ).toBe(`WeakMap(${getLength(object)})`);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.HEADER });
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).text()
+ ).toBe(`WeakMap${length}`);
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).prop("title")
+ ).toBe(`WeakMap(${getLength(object)})`);
+
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).prop("title")
+ ).toBe(`WeakMap(${getLength(object)})`);
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).prop("title")
+ ).toBe(`WeakMap(${getLength(object)})`);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.LONG });
+
+ expect(
+ renderRep({
+ mode: MODE.LONG,
+ title: "CustomTitle",
+ }).text()
+ ).toBe(`CustomTitle${length} { {…} → "value-a" }`);
+ expect(
+ renderRep({
+ mode: MODE.LONG,
+ title: "CustomTitle",
+ shouldRenderTooltip: true,
+ }).prop("title")
+ ).toBe(`CustomTitle(${getLength(object)})`);
+ });
+});
+
+describe("GripMap - max entries", () => {
+ // Test object:
+ // `new Map([["key-a","value-a"], ["key-b","value-b"], ["key-c","value-c"]])`
+ const object = stubs.get("testMaxEntries");
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("renders as expected", () => {
+ let length = getMapLengthBubbleText(object);
+ const renderRep = props => shallowRenderRep(object, props);
+ const out =
+ `Map${length} { ` +
+ '"key-a" → "value-a", "key-b" → "value-b", "key-c" → "value-c" }';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(out);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(out);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(out);
+ });
+});
+
+describe("GripMap - more than max entries", () => {
+ // Test object = `new Map(
+ // [["key-0", "value-0"], …, ["key-100", "value-100"]]}`
+ const object = stubs.get("testMoreThanMaxEntries");
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getMapLengthBubbleText(object);
+ const defaultOutput =
+ `Map${length} { "key-0" → "value-0", ` +
+ '"key-1" → "value-1", "key-2" → "value-2", … }';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ const longString = Array.from({ length: maxLengthMap.get(MODE.LONG) }).map(
+ (_, i) => `"key-${i}" → "value-${i}"`
+ );
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ `Map(${maxLengthMap.get(MODE.LONG) + 1}) { ${longString.join(", ")}, … }`
+ );
+ });
+});
+
+describe("GripMap - uninteresting entries", () => {
+ // Test object:
+ // `new Map([["key-a",null], ["key-b",undefined], ["key-c","value-c"],
+ // ["key-d",4]])`
+ const object = stubs.get("testUninterestingEntries");
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ let length = getMapLengthBubbleText(object);
+ const defaultOutput =
+ `Map${length} { "key-a" → null, ` +
+ '"key-c" → "value-c", "key-d" → 4, … }';
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.TINY });
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe(`Map${length}`);
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ length = getMapLengthBubbleText(object, { mode: MODE.LONG });
+ const longOutput =
+ `Map${length} { "key-a" → null, "key-b" → undefined, ` +
+ '"key-c" → "value-c", "key-d" → 4 }';
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+});
+
+describe("GripMap - Node-keyed entries", () => {
+ const object = stubs.get("testNodeKeyedMap");
+ const renderRep = props => shallowRenderRep(object, props);
+ const grips = getSelectableInInspectorGrips(object);
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("has the expected number of grips", () => {
+ expect(grips).toHaveLength(3);
+ });
+
+ it("calls the expected function on mouseover", () => {
+ const onDOMNodeMouseOver = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOver });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseover");
+ node.at(1).simulate("mouseover");
+ node.at(2).simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(3);
+ expect(onDOMNodeMouseOver.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOver.mock.calls[1][0]).toBe(grips[1]);
+ expect(onDOMNodeMouseOver.mock.calls[2][0]).toBe(grips[2]);
+ });
+
+ it("calls the expected function on mouseout", () => {
+ const onDOMNodeMouseOut = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOut });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseout");
+ node.at(1).simulate("mouseout");
+ node.at(2).simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(3);
+ expect(onDOMNodeMouseOut.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOut.mock.calls[1][0]).toBe(grips[1]);
+ expect(onDOMNodeMouseOut.mock.calls[2][0]).toBe(grips[2]);
+ });
+
+ it("calls the expected function on click", () => {
+ const onInspectIconClick = jest.fn();
+ const wrapper = renderRep({ onInspectIconClick });
+ const node = wrapper.find(".open-inspector");
+
+ node.at(0).simulate("click");
+ node.at(1).simulate("click");
+ node.at(2).simulate("click");
+
+ expect(onInspectIconClick.mock.calls).toHaveLength(3);
+ expect(onInspectIconClick.mock.calls[0][0]).toBe(grips[0]);
+ expect(onInspectIconClick.mock.calls[1][0]).toBe(grips[1]);
+ expect(onInspectIconClick.mock.calls[2][0]).toBe(grips[2]);
+ });
+});
+
+describe("GripMap - Node-valued entries", () => {
+ const object = stubs.get("testNodeValuedMap");
+ const renderRep = props => shallowRenderRep(object, props);
+ const grips = getSelectableInInspectorGrips(object);
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("has the expected number of grips", () => {
+ expect(grips).toHaveLength(3);
+ });
+
+ it("calls the expected function on mouseover", () => {
+ const onDOMNodeMouseOver = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOver });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseover");
+ node.at(1).simulate("mouseover");
+ node.at(2).simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(3);
+ expect(onDOMNodeMouseOver.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOver.mock.calls[1][0]).toBe(grips[1]);
+ expect(onDOMNodeMouseOver.mock.calls[2][0]).toBe(grips[2]);
+ });
+
+ it("calls the expected function on mouseout", () => {
+ const onDOMNodeMouseOut = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOut });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseout");
+ node.at(1).simulate("mouseout");
+ node.at(2).simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(3);
+ expect(onDOMNodeMouseOut.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOut.mock.calls[1][0]).toBe(grips[1]);
+ expect(onDOMNodeMouseOut.mock.calls[2][0]).toBe(grips[2]);
+ });
+
+ it("calls the expected function on click", () => {
+ const onInspectIconClick = jest.fn();
+ const wrapper = renderRep({ onInspectIconClick });
+ const node = wrapper.find(".open-inspector");
+
+ node.at(0).simulate("click");
+ node.at(1).simulate("click");
+ node.at(2).simulate("click");
+
+ expect(onInspectIconClick.mock.calls).toHaveLength(3);
+ expect(onInspectIconClick.mock.calls[0][0]).toBe(grips[0]);
+ expect(onInspectIconClick.mock.calls[1][0]).toBe(grips[1]);
+ expect(onInspectIconClick.mock.calls[2][0]).toBe(grips[2]);
+ });
+});
+
+describe("GripMap - Disconnected node-valued entries", () => {
+ const object = stubs.get("testDisconnectedNodeValuedMap");
+ const renderRep = props => shallowRenderRep(object, props);
+ const grips = getSelectableInInspectorGrips(object);
+
+ it("correctly selects GripMap Rep", () => {
+ expect(getRep(object)).toBe(GripMap.rep);
+ });
+
+ it("has the expected number of grips", () => {
+ expect(grips).toHaveLength(3);
+ });
+
+ it("no inspect icon when nodes are not connected to the DOM tree", () => {
+ const onInspectIconClick = jest.fn();
+ const wrapper = renderRep({ onInspectIconClick });
+
+ const node = wrapper.find(".open-inspector");
+ expect(node.exists()).toBe(false);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/grip.test.js b/devtools/client/shared/components/test/node/components/reps/grip.test.js
new file mode 100644
index 0000000000..3ef69ee052
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/grip.test.js
@@ -0,0 +1,750 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+
+const { shallow } = require("enzyme");
+const {
+ getRep,
+ Rep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const Grip = require("resource://devtools/client/shared/components/reps/reps/grip.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+const gripArrayStubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+
+const {
+ expectActorAttribute,
+ getSelectableInInspectorGrips,
+ getGripLengthBubbleText,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const { maxLengthMap } = Grip;
+
+function shallowRenderRep(object, props = {}) {
+ return shallow(
+ Grip.rep({
+ object,
+ ...props,
+ })
+ );
+}
+
+describe("Grip - empty object", () => {
+ // Test object: `{}`
+ const object = stubs.get("testBasic");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Object { }";
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe("{}");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe("Object");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("Grip - Boolean object", () => {
+ // Test object: `new Boolean(true)`
+ const object = stubs.get("testBooleanObject");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Boolean { true }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("Boolean");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Boolean");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - Number object", () => {
+ // Test object: `new Number(42)`
+ const object = stubs.get("testNumberObject");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Number { 42 }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("Number");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Number");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - String object", () => {
+ // Test object: `new String("foo")`
+ const object = stubs.get("testStringObject");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = 'String { "foo" }';
+
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).prop("title")
+ ).toBe("String");
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).text()
+ ).toBe("String");
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).prop("title")
+ ).toBe("String");
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).text()
+ ).toBe("String");
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).prop("title")
+ ).toBe("String");
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).prop("title")
+ ).toBe("String");
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).prop("title")
+ ).toBe("String");
+ });
+});
+
+describe("Grip - Proxy", () => {
+ // Test object: `new Proxy({a:1},[1,2,3])`
+ const object = stubs.get("testProxy");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const handler = object.preview.ownProperties["<handler>"].value;
+ const handlerLength = getGripLengthBubbleText(handler, {
+ mode: MODE.TINY,
+ });
+ const out = `Proxy { <target>: {…}, <handler>: ${handlerLength} […] }`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(out);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("Proxy");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Proxy");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(out);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(out);
+ });
+});
+
+describe("Grip - ArrayBuffer", () => {
+ // Test object: `new ArrayBuffer(10)`
+ const object = stubs.get("testArrayBuffer");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "ArrayBuffer { byteLength: 10 }";
+
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).prop("title")
+ ).toBe("ArrayBuffer");
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).text()
+ ).toBe("ArrayBuffer");
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).prop("title")
+ ).toBe("ArrayBuffer");
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).text()
+ ).toBe("ArrayBuffer");
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).prop("title")
+ ).toBe("ArrayBuffer");
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).prop("title")
+ ).toBe("ArrayBuffer");
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).prop("title")
+ ).toBe("ArrayBuffer");
+ });
+});
+
+describe("Grip - SharedArrayBuffer", () => {
+ // Test object: `new SharedArrayBuffer(5)`
+ const object = stubs.get("testSharedArrayBuffer");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "SharedArrayBuffer { byteLength: 5 }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("SharedArrayBuffer");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("SharedArrayBuffer");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - ApplicationCache", () => {
+ // Test object: `window.applicationCache`
+ const object = stubs.get("testApplicationCache");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput =
+ "OfflineResourceList { status: 0, onchecking: null, onerror: null, … }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("OfflineResourceList");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("OfflineResourceList");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ const longOutput =
+ "OfflineResourceList { status: 0, onchecking: null, " +
+ "onerror: null, onnoupdate: null, ondownloading: null, " +
+ "onprogress: null, onupdateready: null, oncached: null, " +
+ "onobsolete: null, mozItems: DOMStringList [] }";
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+});
+
+describe("Grip - Object with max props", () => {
+ // Test object: `{a: "a", b: "b", c: "c"}`
+ const object = stubs.get("testMaxProps");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = 'Object { a: "a", b: "b", c: "c" }';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - Object with more than short mode max props", () => {
+ // Test object: `{a: undefined, b: 1, more: 2, d: 3}`;
+ const object = stubs.get("testMoreProp");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Object { b: 1, more: 2, d: 3, … }";
+
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: undefined, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Object");
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).text()
+ ).toBe("{…}");
+ expect(
+ renderRep({ mode: MODE.TINY, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Object");
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).text()
+ ).toBe("Object");
+ expect(
+ renderRep({ mode: MODE.HEADER, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Object");
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).text()
+ ).toBe(defaultOutput);
+ expect(
+ renderRep({ mode: MODE.SHORT, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Object");
+
+ const longOutput = "Object { a: undefined, b: 1, more: 2, d: 3 }";
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).text()
+ ).toBe(longOutput);
+ expect(
+ renderRep({ mode: MODE.LONG, shouldRenderTooltip: true }).prop("title")
+ ).toBe("Object");
+ });
+});
+
+describe("Grip - Object with more than long mode max props", () => {
+ // Test object = `{p0: "0", p1: "1", p2: "2", …, p100: "100"}`
+ const object = stubs.get("testMoreThanMaxProps");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = 'Object { p0: "0", p1: "1", p2: "2", … }';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+
+ const props = Array.from({ length: maxLengthMap.get(MODE.LONG) }).map(
+ (item, i) => `p${i}: "${i}"`
+ );
+ const longOutput = `Object { ${props.join(", ")}, … }`;
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(longOutput);
+ });
+});
+
+describe("Grip - Object with non-enumerable properties", () => {
+ // Test object: `Object.defineProperty({}, "foo", {enumerable : false});`
+ const object = stubs.get("testNonEnumerableProps");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Object { … }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - Object with nested object", () => {
+ // Test object: `{objProp: {id: 1}, strProp: "test string"}`
+ const object = stubs.get("testNestedObject");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = 'Object { objProp: {…}, strProp: "test string" }';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+
+ // Check the custom title with nested objects to make sure nested objects
+ // are not displayed with their parent's title.
+ expect(
+ renderRep({
+ mode: MODE.LONG,
+ title: "CustomTitle",
+ }).text()
+ ).toBe('CustomTitle { objProp: {…}, strProp: "test string" }');
+ });
+});
+
+describe("Grip - Object with nested array", () => {
+ // Test object: `{arrProp: ["foo", "bar", "baz"]}`
+ const object = stubs.get("testNestedArray");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const propLength = getGripLengthBubbleText(
+ object.preview.ownProperties.arrProp.value,
+ { mode: MODE.TINY }
+ );
+ const defaultOutput = `Object { arrProp: ${propLength} […] }`;
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - Object with connected nodes", () => {
+ const object = stubs.get("testObjectWithNodes");
+ const grips = getSelectableInInspectorGrips(object);
+ const renderRep = props => shallowRenderRep(object, props);
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("has the expected number of node grip", () => {
+ expect(grips).toHaveLength(2);
+ });
+
+ it("renders as expected", () => {
+ const defaultOutput =
+ "Object { foo: button#btn-1.btn.btn-log, bar: button#btn-2.btn.btn-err }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+
+ it("calls the expected function on mouseover", () => {
+ const onDOMNodeMouseOver = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOver });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseover");
+ node.at(1).simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(2);
+ expect(onDOMNodeMouseOver.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOver.mock.calls[1][0]).toBe(grips[1]);
+ });
+
+ it("calls the expected function on mouseout", () => {
+ const onDOMNodeMouseOut = jest.fn();
+ const wrapper = renderRep({ onDOMNodeMouseOut });
+ const node = wrapper.find(".objectBox-node");
+
+ node.at(0).simulate("mouseout");
+ node.at(1).simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(2);
+ expect(onDOMNodeMouseOut.mock.calls[0][0]).toBe(grips[0]);
+ expect(onDOMNodeMouseOut.mock.calls[1][0]).toBe(grips[1]);
+ });
+
+ it("calls the expected function on click", () => {
+ const onInspectIconClick = jest.fn();
+ const wrapper = renderRep({ onInspectIconClick });
+ const node = wrapper.find(".open-inspector");
+
+ node.at(0).simulate("click");
+ node.at(1).simulate("click");
+
+ expect(onInspectIconClick.mock.calls).toHaveLength(2);
+ expect(onInspectIconClick.mock.calls[0][0]).toBe(grips[0]);
+ expect(onInspectIconClick.mock.calls[1][0]).toBe(grips[1]);
+ });
+});
+
+describe("Grip - Object with disconnected nodes", () => {
+ const object = stubs.get("testObjectWithDisconnectedNodes");
+ const renderRep = props => shallowRenderRep(object, props);
+ const grips = getSelectableInInspectorGrips(object);
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("has the expected number of grips", () => {
+ expect(grips).toHaveLength(2);
+ });
+
+ it("no inspect icon when nodes are not connected to the DOM tree", () => {
+ const onInspectIconClick = jest.fn();
+ const wrapper = renderRep({ onInspectIconClick });
+
+ const node = wrapper.find(".open-inspector");
+ expect(node.exists()).toBe(false);
+ });
+});
+
+describe("Grip - Object with getter", () => {
+ const object = stubs.get("TestObjectWithGetter");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Object { x: Getter }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - Object with setter", () => {
+ const object = stubs.get("TestObjectWithSetter");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Object { x: Setter }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - Object with getter and setter", () => {
+ const object = stubs.get("TestObjectWithGetterAndSetter");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Object { x: Getter & Setter }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - Object with symbol properties", () => {
+ const object = stubs.get("TestObjectWithSymbolProperties");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput =
+ 'Object { x: 10, Symbol(): "first unnamed symbol", ' +
+ 'Symbol(): "second unnamed symbol", … }';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ 'Object { x: 10, Symbol(): "first unnamed symbol", ' +
+ 'Symbol(): "second unnamed symbol", Symbol("named"): "named symbol", ' +
+ "Symbol(Symbol.iterator): () }"
+ );
+ });
+});
+
+describe("Grip - Object with more than max symbol properties", () => {
+ const object = stubs.get("TestObjectWithMoreThanMaxSymbolProperties");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput =
+ 'Object { Symbol("i-0"): "value-0", Symbol("i-1"): "value-1", ' +
+ 'Symbol("i-2"): "value-2", … }';
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(
+ 'Object { Symbol("i-0"): "value-0", Symbol("i-1"): "value-1", ' +
+ 'Symbol("i-2"): "value-2", Symbol("i-3"): "value-3", ' +
+ 'Symbol("i-4"): "value-4", Symbol("i-5"): "value-5", ' +
+ 'Symbol("i-6"): "value-6", Symbol("i-7"): "value-7", ' +
+ 'Symbol("i-8"): "value-8", Symbol("i-9"): "value-9", … }'
+ );
+ });
+});
+
+describe("Grip - Without preview", () => {
+ // Test object: `[1, "foo", {}]`
+ const object = gripArrayStubs.get("testMaxProps").preview.items[2];
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Object { }";
+
+ let component = renderRep({ mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.TINY });
+ expect(component.text()).toBe("{}");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.HEADER });
+ expect(component.text()).toBe("Object");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("Grip - Generator object", () => {
+ // Test object:
+ // function* genFunc() {
+ // var a = 5; while (a < 10) { yield a++; }
+ // };
+ // genFunc();
+ const object = stubs.get("Generator");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "Generator { }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("Generator");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Generator");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Grip - DeadObject object", () => {
+ // Test object (executed in a privileged content, like about:preferences):
+ // `var s = Cu.Sandbox(null);Cu.nukeSandbox(s);s;`
+
+ const object = stubs.get("DeadObject");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallowRenderRep(object, props);
+ const defaultOutput = "DeadObject { }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("DeadObject");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("DeadObject");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+// TODO: Re-enable and fix this test.
+describe.skip("Grip - Object with __proto__ property", () => {
+ const object = stubs.get("ObjectWith__proto__Property");
+
+ it("correctly selects Grip Rep", () => {
+ expect(getRep(object)).toBe(Grip.rep);
+ });
+
+ it("renders as expected", () => {
+ const renderRep = props => shallow(Rep({ object, ...props }));
+ const defaultOutput = "Object { __proto__: [] }";
+
+ expect(renderRep({ mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.TINY }).text()).toBe("{…}");
+ expect(renderRep({ mode: MODE.HEADER }).text()).toBe("Object");
+ expect(renderRep({ mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep({ mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+// Test that object that might look like raw objects or arrays are rendered
+// as grips when the `noGrip` parameter is not passed.
+describe("Object - noGrip prop", () => {
+ it("empty object", () => {
+ expect(getRep({})).toBe(Grip.rep);
+ });
+
+ it("object with custom property", () => {
+ expect(getRep({ foo: 123 })).toBe(Grip.rep);
+ });
+
+ it("empty array", () => {
+ expect(getRep([])).toBe(Grip.rep);
+ });
+
+ it("array with some item", () => {
+ expect(getRep([123])).toBe(Grip.rep);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/helper-tests.test.js b/devtools/client/shared/components/test/node/components/reps/helper-tests.test.js
new file mode 100644
index 0000000000..6fc1be64a3
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/helper-tests.test.js
@@ -0,0 +1,122 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+const {
+ getGripLengthBubbleText,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+describe("getGripLengthBubbleText - Zero length", () => {
+ const object = stubs.get("testBasic");
+
+ it("length bubble is invisible", () => {
+ const output = "";
+ let text = getGripLengthBubbleText(object, { mode: undefined });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.SHORT });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ expect(text).toBe(output);
+ });
+
+ it("length bubble is visible", () => {
+ const output = "(0)";
+ let text = getGripLengthBubbleText(object, {
+ mode: undefined,
+ showZeroLength: true,
+ });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, {
+ mode: MODE.TINY,
+ showZeroLength: true,
+ });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, {
+ mode: MODE.SHORT,
+ showZeroLength: true,
+ });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, {
+ mode: MODE.LONG,
+ showZeroLength: true,
+ });
+ expect(text).toBe(output);
+ });
+});
+
+describe("getGripLengthBubbleText - Obvious length for some modes", () => {
+ const object = stubs.get("testMoreThanShortMaxProps");
+ const visibleOutput = `(${object.preview.length})`;
+
+ it("text renders as expected", () => {
+ let text = getGripLengthBubbleText(object, { mode: undefined });
+ expect(text).toBe(visibleOutput);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(text).toBe(visibleOutput);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.SHORT });
+ expect(text).toBe(visibleOutput);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ expect(text).toBe(visibleOutput);
+
+ const visibilityThreshold = 5;
+ text = getGripLengthBubbleText(object, {
+ mode: undefined,
+ visibilityThreshold,
+ });
+ expect(text).toBe(visibleOutput);
+
+ text = getGripLengthBubbleText(object, {
+ mode: MODE.TINY,
+ visibilityThreshold,
+ });
+ expect(text).toBe(visibleOutput);
+
+ text = getGripLengthBubbleText(object, {
+ mode: MODE.SHORT,
+ visibilityThreshold,
+ });
+ expect(text).toBe(visibleOutput);
+
+ text = getGripLengthBubbleText(object, {
+ mode: MODE.LONG,
+ visibilityThreshold,
+ });
+ expect(text).toBe("");
+ });
+});
+
+describe("getGripLengthBubbleText - Visible length", () => {
+ const object = stubs.get("testMoreThanLongMaxProps");
+ const output = `(${object.preview.length})`;
+
+ it("length bubble is always visible", () => {
+ let text = getGripLengthBubbleText(object, { mode: undefined });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.TINY });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.SHORT });
+ expect(text).toBe(output);
+
+ text = getGripLengthBubbleText(object, { mode: MODE.LONG });
+ expect(text).toBe(output);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/infinity.test.js b/devtools/client/shared/components/test/node/components/reps/infinity.test.js
new file mode 100644
index 0000000000..93fd310917
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/infinity.test.js
@@ -0,0 +1,70 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { InfinityRep, Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/infinity.js");
+
+describe("testInfinity", () => {
+ const stub = stubs.get("Infinity");
+
+ it("Rep correctly selects Infinity Rep", () => {
+ expect(getRep(stub)).toBe(InfinityRep.rep);
+ });
+
+ it("Infinity rep has expected text content for Infinity", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+ expect(renderedComponent.text()).toEqual("Infinity");
+ });
+
+ it("Infinity rep has expected title content for Infinity", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.prop("title")).toEqual("Infinity");
+ });
+});
+
+describe("testNegativeInfinity", () => {
+ const stub = stubs.get("NegativeInfinity");
+
+ it("Rep correctly selects Infinity Rep", () => {
+ expect(getRep(stub)).toBe(InfinityRep.rep);
+ });
+
+ it("Infinity rep has expected text content for negative Infinity", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+ expect(renderedComponent.text()).toEqual("-Infinity");
+ });
+
+ it("Infinity rep has expected title content for negative Infinity", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.prop("title")).toEqual("-Infinity");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/long-string.test.js b/devtools/client/shared/components/test/node/components/reps/long-string.test.js
new file mode 100644
index 0000000000..ea6bb43e97
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/long-string.test.js
@@ -0,0 +1,135 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ ELLIPSIS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const { StringRep } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/long-string.js");
+
+function quoteNewlines(text) {
+ return text.split("\n").join("\\n");
+}
+
+describe("long StringRep", () => {
+ it("selects String Rep", () => {
+ const stub = stubs.get("testMultiline");
+
+ expect(getRep(stub)).toEqual(StringRep.rep);
+ });
+
+ it("renders with expected text content for multiline string", () => {
+ const stub = stubs.get("testMultiline");
+ const renderedComponent = shallow(
+ StringRep.rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ quoteNewlines(`"${stub.initial}${ELLIPSIS}"`)
+ );
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it(
+ "renders with expected text content for multiline string with " +
+ "specified number of characters",
+ () => {
+ const stub = stubs.get("testMultiline");
+ const renderedComponent = shallow(
+ StringRep.rep({
+ object: stub,
+ cropLimit: 20,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ `"a\\naaaaaaaaaaaaaaaaaa${ELLIPSIS}"`
+ );
+ }
+ );
+
+ it("renders with expected text for multiline string when open", () => {
+ const stub = stubs.get("testMultiline");
+ const renderedComponent = shallow(
+ StringRep.rep({
+ object: stub,
+ member: { open: true },
+ cropLimit: 20,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ quoteNewlines(`"${stub.initial}${ELLIPSIS}"`)
+ );
+ });
+
+ it(
+ "renders with expected text content when grip has a fullText" +
+ "property and is open",
+ () => {
+ const stub = stubs.get("testLoadedFullText");
+ const renderedComponent = shallow(
+ StringRep.rep({
+ object: stub,
+ member: { open: true },
+ cropLimit: 20,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ quoteNewlines(`"${stub.fullText}"`)
+ );
+ }
+ );
+
+ it(
+ "renders with expected text content when grip has a fullText " +
+ "property and is not open",
+ () => {
+ const stub = stubs.get("testLoadedFullText");
+ const renderedComponent = shallow(
+ StringRep.rep({
+ object: stub,
+ cropLimit: 20,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ `"a\\naaaaaaaaaaaaaaaaaa${ELLIPSIS}"`
+ );
+ }
+ );
+
+ it("expected to omit quotes", () => {
+ const stub = stubs.get("testMultiline");
+ const renderedComponent = shallow(
+ StringRep.rep({
+ object: stub,
+ cropLimit: 20,
+ useQuotes: false,
+ })
+ );
+
+ expect(renderedComponent.html()).toEqual(
+ '<span data-link-actor-id="server1.conn1.child1/longString58" ' +
+ `class="objectBox objectBox-string">a\naaaaaaaaaaaaaaaaaa${ELLIPSIS}` +
+ "</span>"
+ );
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/nan.test.js b/devtools/client/shared/components/test/node/components/reps/nan.test.js
new file mode 100644
index 0000000000..429e70acab
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/nan.test.js
@@ -0,0 +1,43 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { NaNRep, Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/nan.js");
+
+describe("NaN", () => {
+ const stub = stubs.get("NaN");
+
+ it("selects NaN Rep as expected", () => {
+ expect(getRep(stub)).toBe(NaNRep.rep);
+ });
+
+ it("renders NaN Rep as expected", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+ expect(renderedComponent).toMatchSnapshot();
+ });
+
+ it("NaN rep renders with the correct title element", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.prop("title")).toBe("NaN");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/null.test.js b/devtools/client/shared/components/test/node/components/reps/null.test.js
new file mode 100644
index 0000000000..af5f615cad
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/null.test.js
@@ -0,0 +1,47 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { Null, Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/null.js");
+
+describe("testNull", () => {
+ const stub = stubs.get("Null");
+
+ it("Rep correctly selects Null Rep", () => {
+ expect(getRep(stub)).toBe(Null.rep);
+ });
+
+ it("Rep correctly selects Null Rep for plain JS null object", () => {
+ expect(getRep(null, undefined, true)).toBe(Null.rep);
+ });
+
+ it("Null rep has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+ expect(renderedComponent.text()).toEqual("null");
+ });
+
+ it("Null rep displays null for title", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.prop("title")).toEqual("null");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/number.test.js b/devtools/client/shared/components/test/node/components/reps/number.test.js
new file mode 100644
index 0000000000..c0bbab3b0a
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/number.test.js
@@ -0,0 +1,136 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Number, Rep } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/number.js");
+
+describe("Int", () => {
+ const stub = stubs.get("Int");
+
+ it("correctly selects Number Rep for Integer value", () => {
+ expect(getRep(stub)).toBe(Number.rep);
+ });
+
+ it("renders with expected text content for integer", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("5");
+ expect(renderedComponent.prop("title")).toBe("5");
+ });
+});
+
+describe("Boolean", () => {
+ const stubTrue = stubs.get("True");
+ const stubFalse = stubs.get("False");
+
+ it("correctly selects Number Rep for boolean value", () => {
+ expect(getRep(stubTrue)).toBe(Number.rep);
+ });
+
+ it("renders with expected text content for boolean true", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stubTrue,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("true");
+ expect(renderedComponent.prop("title")).toBe("true");
+ });
+
+ it("renders with expected text content for boolean false", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stubFalse,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("false");
+ expect(renderedComponent.prop("title")).toBe("false");
+ });
+});
+
+describe("Negative Zero", () => {
+ const stubNegativeZeroGrip = stubs.get("NegZeroGrip");
+ const stubNegativeZeroValue = -0;
+
+ it("correctly selects Number Rep for negative zero grip", () => {
+ expect(getRep(stubNegativeZeroGrip)).toBe(Number.rep);
+ });
+
+ it("correctly selects Number Rep for negative zero value", () => {
+ expect(getRep(stubNegativeZeroValue)).toBe(Number.rep);
+ });
+
+ it("renders with expected text content for negative zero grip", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stubNegativeZeroGrip,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("-0");
+ expect(renderedComponent.prop("title")).toBe("-0");
+ });
+
+ it("renders with expected text content for negative zero value", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stubNegativeZeroValue,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("-0");
+ expect(renderedComponent.prop("title")).toBe("-0");
+ });
+});
+
+describe("Zero", () => {
+ it("correctly selects Number Rep for zero value", () => {
+ expect(getRep(0)).toBe(Number.rep);
+ });
+
+ it("renders with expected text content for zero value", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: 0,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("0");
+ expect(renderedComponent.prop("title")).toBe("0");
+ });
+});
+
+describe("Unsafe Int", () => {
+ it("renders with expected test content for a long number", () => {
+ const renderedComponent = shallow(
+ Rep({
+ // eslint-disable-next-line no-loss-of-precision
+ object: 900719925474099122,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("900719925474099100");
+ expect(renderedComponent.prop("title")).toBe("900719925474099100");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/object-with-text.test.js b/devtools/client/shared/components/test/node/components/reps/object-with-text.test.js
new file mode 100644
index 0000000000..164024f7e6
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/object-with-text.test.js
@@ -0,0 +1,66 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { shallow } = require("enzyme");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/object-with-text.js");
+const { ObjectWithText, Rep } = REPS;
+
+describe("Object with text - CSSStyleRule", () => {
+ const gripStub = stubs.get("ShadowRule");
+
+ // Test that correct rep is chosen
+ it("selects ObjectsWithText Rep", () => {
+ expect(getRep(gripStub)).toEqual(ObjectWithText.rep);
+ });
+
+ // Test rendering
+ it("renders with the correct text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: gripStub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual('CSSStyleRule ".Shadow"');
+ expect(renderedComponent.prop("title")).toEqual('CSSStyleRule ".Shadow"');
+ expectActorAttribute(renderedComponent, gripStub.actor);
+ });
+});
+
+describe("Object with text - CSSMediaRule", () => {
+ const gripStub = stubs.get("CSSMediaRule");
+
+ // Test that correct rep is chosen
+ it("selects ObjectsWithText Rep", () => {
+ expect(getRep(gripStub)).toEqual(ObjectWithText.rep);
+ });
+
+ // Test rendering
+ it("renders with the correct text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: gripStub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ const text =
+ 'CSSMediaRule "(min-height: 680px), screen and (orientation: portrait)"';
+ expect(renderedComponent.text()).toEqual(text);
+ expect(renderedComponent.prop("title")).toEqual(text);
+ expectActorAttribute(renderedComponent, gripStub.actor);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/object-with-url.test.js b/devtools/client/shared/components/test/node/components/reps/object-with-url.test.js
new file mode 100644
index 0000000000..055afbfde4
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/object-with-url.test.js
@@ -0,0 +1,45 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { ObjectWithURL } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/object-with-url.js");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+describe("ObjectWithURL", () => {
+ const stub = stubs.get("ObjectWithUrl");
+
+ it("selects the correct rep", () => {
+ expect(getRep(stub)).toEqual(ObjectWithURL.rep);
+ });
+
+ it("renders with correct class name and content", () => {
+ const renderedComponent = shallow(
+ ObjectWithURL.rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.text()).toBe(
+ "Location https://www.mozilla.org/en-US/"
+ );
+ expect(renderedComponent.prop("title")).toBe(
+ "Location https://www.mozilla.org/en-US/"
+ );
+ expect(renderedComponent.hasClass("objectBox-Location")).toBe(true);
+
+ const innerNode = renderedComponent.find(".objectPropValue");
+ expect(innerNode.text()).toBe("https://www.mozilla.org/en-US/");
+
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/object.test.js b/devtools/client/shared/components/test/node/components/reps/object.test.js
new file mode 100644
index 0000000000..9b32e51d20
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/object.test.js
@@ -0,0 +1,356 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Obj } = REPS;
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+
+const renderComponent = (object, props) => {
+ return shallow(Obj.rep({ object, ...props }));
+};
+
+describe("Object - Basic", () => {
+ const object = {};
+ const defaultOutput = "Object { }";
+
+ it("selects the correct rep", () => {
+ expect(getRep(object, undefined, true)).toBe(Obj.rep);
+ });
+
+ it("renders basic object as expected", () => {
+ expect(renderComponent(object, { mode: undefined }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: undefined }).prop("title")).toEqual(
+ "Object"
+ );
+
+ expect(renderComponent(object, { mode: MODE.TINY }).text()).toEqual("{}");
+ expect(renderComponent(object, { mode: MODE.TINY }).prop("title")).toEqual(
+ "Object"
+ );
+
+ expect(renderComponent(object, { mode: MODE.SHORT }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.LONG }).text()).toEqual(
+ defaultOutput
+ );
+ });
+});
+
+describe("Object - Max props", () => {
+ const object = { a: "a", b: "b", c: "c" };
+ const defaultOutput = 'Object { a: "a", b: "b", c: "c" }';
+
+ it("renders object with max props as expected", () => {
+ expect(renderComponent(object, { mode: undefined }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.TINY }).text()).toEqual("{…}");
+ expect(renderComponent(object, { mode: MODE.SHORT }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.LONG }).text()).toEqual(
+ defaultOutput
+ );
+ });
+});
+
+describe("Object - Many props", () => {
+ const object = {};
+ for (let i = 0; i < 100; i++) {
+ object[`p${i}`] = i;
+ }
+ const defaultOutput = "Object { p0: 0, p1: 1, p2: 2, … }";
+
+ it("renders object with many props as expected", () => {
+ expect(renderComponent(object, { mode: undefined }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.TINY }).text()).toEqual("{…}");
+ expect(renderComponent(object, { mode: MODE.SHORT }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.LONG }).text()).toEqual(
+ defaultOutput
+ );
+ });
+});
+
+describe("Object - Uninteresting props", () => {
+ const object = { a: undefined, b: undefined, c: "c", d: 0 };
+ const defaultOutput = 'Object { c: "c", d: 0, a: undefined, … }';
+
+ it("renders object with uninteresting props as expected", () => {
+ expect(renderComponent(object, { mode: undefined }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.TINY }).text()).toEqual("{…}");
+ expect(renderComponent(object, { mode: MODE.SHORT }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.LONG }).text()).toEqual(
+ defaultOutput
+ );
+ });
+});
+
+describe("Object - Escaped property names", () => {
+ const object = { "": 1, "quote-this": 2, noquotes: 3 };
+ const defaultOutput = 'Object { "": 1, "quote-this": 2, noquotes: 3 }';
+
+ it("renders object with escaped property names as expected", () => {
+ expect(renderComponent(object, { mode: undefined }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.TINY }).text()).toEqual("{…}");
+ expect(renderComponent(object, { mode: MODE.SHORT }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.LONG }).text()).toEqual(
+ defaultOutput
+ );
+ });
+});
+
+describe("Object - Nested", () => {
+ const object = {
+ objProp: {
+ id: 1,
+ arr: [2],
+ },
+ strProp: "test string",
+ arrProp: [1],
+ };
+ const defaultOutput =
+ 'Object { strProp: "test string", objProp: {…},' + " arrProp: […] }";
+
+ it("renders nested object as expected", () => {
+ expect(
+ renderComponent(object, { mode: undefined, noGrip: true }).text()
+ ).toEqual(defaultOutput);
+ expect(
+ renderComponent(object, { mode: MODE.TINY, noGrip: true }).text()
+ ).toEqual("{…}");
+ expect(
+ renderComponent(object, { mode: MODE.SHORT, noGrip: true }).text()
+ ).toEqual(defaultOutput);
+ expect(
+ renderComponent(object, { mode: MODE.LONG, noGrip: true }).text()
+ ).toEqual(defaultOutput);
+ });
+});
+
+describe("Object - More prop", () => {
+ const object = {
+ a: undefined,
+ b: 1,
+ more: 2,
+ d: 3,
+ };
+ const defaultOutput = "Object { b: 1, more: 2, d: 3, … }";
+
+ it("renders object with more properties as expected", () => {
+ expect(renderComponent(object, { mode: undefined }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.TINY }).text()).toEqual("{…}");
+ expect(renderComponent(object, { mode: MODE.SHORT }).text()).toEqual(
+ defaultOutput
+ );
+ expect(renderComponent(object, { mode: MODE.LONG }).text()).toEqual(
+ defaultOutput
+ );
+ });
+});
+
+describe("Object - Custom Title", () => {
+ const customTitle = "MyCustomObject";
+ const object = { a: "a", b: "b", c: "c" };
+ const defaultOutput = `${customTitle} { a: "a", b: "b", c: "c" }`;
+
+ it("renders object with more properties as expected", () => {
+ expect(
+ renderComponent(object, { mode: undefined, title: customTitle }).text()
+ ).toEqual(defaultOutput);
+ expect(
+ renderComponent(object, { mode: undefined, title: customTitle }).prop(
+ "title"
+ )
+ ).toEqual(customTitle);
+ expect(
+ renderComponent(object, { mode: MODE.TINY, title: customTitle }).text()
+ ).toEqual(customTitle);
+ expect(
+ renderComponent(object, { mode: MODE.TINY, title: customTitle }).prop(
+ "title"
+ )
+ ).toEqual(customTitle);
+ expect(
+ renderComponent(object, { mode: MODE.SHORT, title: customTitle }).text()
+ ).toEqual(defaultOutput);
+ expect(
+ renderComponent(object, { mode: MODE.LONG, title: customTitle }).text()
+ ).toEqual(defaultOutput);
+ });
+});
+
+// Test that object that might look like Grips are rendered as Object when
+// passed the `noGrip` property.
+describe("Object - noGrip prop", () => {
+ it("object with type property", () => {
+ expect(getRep({ type: "string" }, undefined, true)).toBe(Obj.rep);
+ });
+
+ it("object with actor property", () => {
+ expect(getRep({ actor: "fake/actorId" }, undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Attribute grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/attribute.js");
+ expect(getRep(stubs.get("Attribute"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("CommentNode grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/comment-node.js");
+ expect(getRep(stubs.get("Comment"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("DateTime grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/date-time.js");
+ expect(getRep(stubs.get("DateTime"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Document grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/document.js");
+ expect(getRep(stubs.get("Document"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("ElementNode grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/element-node.js");
+ expect(getRep(stubs.get("BodyNode"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Error grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/error.js");
+ expect(getRep(stubs.get("SimpleError"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Event grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/event.js");
+ expect(getRep(stubs.get("testEvent"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Function grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/function.js");
+ expect(getRep(stubs.get("Named"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Array grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js");
+ expect(getRep(stubs.get("testMaxProps"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Map grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-map.js");
+ expect(getRep(stubs.get("testSymbolKeyedMap"), undefined, true)).toBe(
+ Obj.rep
+ );
+ });
+
+ it("Object grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js");
+ expect(getRep(stubs.get("testMaxProps"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Infinity grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/infinity.js");
+ expect(getRep(stubs.get("Infinity"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("LongString grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/long-string.js");
+ expect(getRep(stubs.get("testMultiline"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("NaN grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/nan.js");
+ expect(getRep(stubs.get("NaN"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Null grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/null.js");
+ expect(getRep(stubs.get("Null"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Number grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/number.js");
+ expect(getRep(stubs.get("NegZeroGrip"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("ObjectWithText grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/object-with-text.js");
+ expect(getRep(stubs.get("ShadowRule"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("ObjectWithURL grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/object-with-url.js");
+ expect(getRep(stubs.get("ObjectWithUrl"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Promise grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/promise.js");
+ expect(getRep(stubs.get("Pending"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("RegExp grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/regexp.js");
+ expect(getRep(stubs.get("RegExp"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Stylesheet grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/stylesheet.js");
+ expect(getRep(stubs.get("StyleSheet"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Symbol grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/symbol.js");
+ expect(getRep(stubs.get("Symbol"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("TextNode grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/text-node.js");
+ expect(getRep(stubs.get("testRendering"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Undefined grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/undefined.js");
+ expect(getRep(stubs.get("Undefined"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Window grip", () => {
+ const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+ expect(getRep(stubs.get("Window"), undefined, true)).toBe(Obj.rep);
+ });
+
+ it("Object with class property", () => {
+ const object = {
+ class: "Array",
+ };
+ expect(getRep(object, undefined, true)).toBe(Obj.rep);
+
+ expect(
+ renderComponent(object, { mode: MODE.SHORT, noGrip: true }).text()
+ ).toEqual('Object { class: "Array" }');
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/promise.test.js b/devtools/client/shared/components/test/node/components/reps/promise.test.js
new file mode 100644
index 0000000000..5c340dccfd
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/promise.test.js
@@ -0,0 +1,229 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { PromiseRep } = REPS;
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/promise.js");
+const {
+ expectActorAttribute,
+ getSelectableInInspectorGrips,
+ getGripLengthBubbleText,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const renderRep = (object, props) => {
+ return shallow(PromiseRep.rep({ object, ...props }));
+};
+
+describe("Promise - Pending", () => {
+ const object = stubs.get("Pending");
+ const defaultOutput = 'Promise { <state>: "pending" }';
+
+ it("correctly selects PromiseRep Rep for pending Promise", () => {
+ expect(getRep(object)).toBe(PromiseRep.rep);
+ });
+
+ it("renders as expected", () => {
+ let component = renderRep(object, {
+ mode: undefined,
+ shouldRenderTooltip: true,
+ });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe("Promise");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep(object, {
+ mode: MODE.TINY,
+ shouldRenderTooltip: true,
+ });
+ expect(component.text()).toBe('Promise { "pending" }');
+ expect(component.prop("title")).toBe("Promise");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep(object, {
+ mode: MODE.HEADER,
+ shouldRenderTooltip: true,
+ });
+ expect(component.text()).toBe("Promise");
+ expect(component.prop("title")).toBe("Promise");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep(object, {
+ mode: MODE.SHORT,
+ shouldRenderTooltip: true,
+ });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe("Promise");
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep(object, {
+ mode: MODE.LONG,
+ shouldRenderTooltip: true,
+ });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe("Promise");
+ expectActorAttribute(component, object.actor);
+ });
+});
+
+describe("Promise - fulfilled with string", () => {
+ const object = stubs.get("FulfilledWithString");
+ const defaultOutput = 'Promise { <state>: "fulfilled", <value>: "foo" }';
+
+ it("selects PromiseRep Rep for Promise fulfilled with a string", () => {
+ expect(getRep(object)).toBe(PromiseRep.rep);
+ });
+
+ it("should render as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe(
+ 'Promise { "fulfilled" }'
+ );
+ expect(renderRep(object, { mode: MODE.HEADER }).text()).toBe("Promise");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Promise - fulfilled with object", () => {
+ const object = stubs.get("FulfilledWithObject");
+ const defaultOutput = 'Promise { <state>: "fulfilled", <value>: {…} }';
+
+ it("selects PromiseRep Rep for Promise fulfilled with an object", () => {
+ expect(getRep(object)).toBe(PromiseRep.rep);
+ });
+
+ it("should render as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe(
+ 'Promise { "fulfilled" }'
+ );
+ expect(renderRep(object, { mode: MODE.HEADER }).text()).toBe("Promise");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Promise - fulfilled with array", () => {
+ const object = stubs.get("FulfilledWithArray");
+ const length = getGripLengthBubbleText(
+ object.preview.ownProperties["<value>"].value,
+ {
+ mode: MODE.TINY,
+ }
+ );
+ const out = `Promise { <state>: "fulfilled", <value>: ${length} […] }`;
+
+ it("selects PromiseRep Rep for Promise fulfilled with an array", () => {
+ expect(getRep(object)).toBe(PromiseRep.rep);
+ });
+
+ it("should render as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(out);
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe(
+ 'Promise { "fulfilled" }'
+ );
+ expect(renderRep(object, { mode: MODE.HEADER }).text()).toBe("Promise");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(out);
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(out);
+ });
+});
+
+describe("Promise - fulfilled with node", () => {
+ const stub = stubs.get("FulfilledWithNode");
+ const grips = getSelectableInInspectorGrips(stub);
+
+ it("has one node grip", () => {
+ expect(grips).toHaveLength(1);
+ });
+
+ it("calls the expected function on mouseover", () => {
+ const onDOMNodeMouseOver = jest.fn();
+ const wrapper = renderRep(stub, { onDOMNodeMouseOver });
+ const node = wrapper.find(".objectBox-node");
+
+ node.simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOver).toHaveBeenCalledWith(grips[0]);
+ });
+
+ it("calls the expected function on mouseout", () => {
+ const onDOMNodeMouseOut = jest.fn();
+ const wrapper = renderRep(stub, { onDOMNodeMouseOut });
+ const node = wrapper.find(".objectBox-node");
+
+ node.simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOut).toHaveBeenCalledWith(grips[0]);
+ });
+
+ it("no inspect icon when the node is not connected to the DOM tree", () => {
+ const renderedComponentWithoutInspectIcon = renderRep(
+ stubs.get("FulfilledWithDisconnectedNode")
+ );
+ const node = renderedComponentWithoutInspectIcon.find(".open-inspector");
+
+ expect(node.exists()).toBe(false);
+ });
+
+ it("renders an inspect icon", () => {
+ const onInspectIconClick = jest.fn();
+ const renderedComponent = renderRep(stub, { onInspectIconClick });
+ const icon = renderedComponent.find(".open-inspector");
+
+ icon.simulate("click");
+
+ expect(icon.exists()).toBe(true);
+ expect(onInspectIconClick.mock.calls).toHaveLength(1);
+ });
+});
+
+describe("Promise - rejected with number", () => {
+ const object = stubs.get("RejectedWithNumber");
+ const defaultOutput = 'Promise { <state>: "rejected", <reason>: 123 }';
+
+ it("selects PromiseRep Rep for Promise rejected with an object", () => {
+ expect(getRep(object)).toBe(PromiseRep.rep);
+ });
+
+ it("should render as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe(
+ 'Promise { "rejected" }'
+ );
+ expect(renderRep(object, { mode: MODE.HEADER }).text()).toBe("Promise");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
+
+describe("Promise - rejected with object", () => {
+ const object = stubs.get("RejectedWithObject");
+ const defaultOutput = 'Promise { <state>: "rejected", <reason>: {…} }';
+
+ it("selects PromiseRep Rep for Promise rejected with an object", () => {
+ expect(getRep(object)).toBe(PromiseRep.rep);
+ });
+
+ it("should render as expected", () => {
+ expect(renderRep(object, { mode: undefined }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.TINY }).text()).toBe(
+ 'Promise { "rejected" }'
+ );
+ expect(renderRep(object, { mode: MODE.HEADER }).text()).toBe("Promise");
+ expect(renderRep(object, { mode: MODE.SHORT }).text()).toBe(defaultOutput);
+ expect(renderRep(object, { mode: MODE.LONG }).text()).toBe(defaultOutput);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/regexp.test.js b/devtools/client/shared/components/test/node/components/reps/regexp.test.js
new file mode 100644
index 0000000000..7e1ea841b8
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/regexp.test.js
@@ -0,0 +1,59 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Rep, RegExp } = REPS;
+const {
+ ELLIPSIS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/regexp.js");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+describe("test RegExp", () => {
+ const stub = stubs.get("RegExp");
+
+ it("selects RegExp Rep", () => {
+ expect(getRep(stub)).toEqual(RegExp.rep);
+ });
+
+ it("renders with expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("/ab+c/i");
+ expect(renderedComponent.prop("title")).toEqual("/ab+c/i");
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders regexp with longString displayString with expected text content", () => {
+ const longStringDisplayStringRegexpStub = stubs.get(
+ "longString displayString RegExp"
+ );
+ const renderedComponent = shallow(
+ Rep({
+ object: longStringDisplayStringRegexpStub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ `/${"ab ".repeat(333)}${ELLIPSIS}`
+ );
+ expectActorAttribute(
+ renderedComponent,
+ longStringDisplayStringRegexpStub.actor
+ );
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/string-with-url.test.js b/devtools/client/shared/components/test/node/components/reps/string-with-url.test.js
new file mode 100644
index 0000000000..a7f287a302
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/string-with-url.test.js
@@ -0,0 +1,630 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { mount } = require("enzyme");
+const {
+ REPS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Rep } = REPS;
+const {
+ getGripLengthBubbleText,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+const renderRep = (string, props) =>
+ mount(
+ Rep({
+ object: string,
+ ...props,
+ })
+ );
+
+const testLinkClick = (link, openLink, url) => {
+ let syntheticEvent;
+ const preventDefault = jest.fn().mockImplementation(function () {
+ // This refers to the event object for which preventDefault is called (in
+ // this case it is the syntheticEvent that is passed to onClick and
+ // consequently to openLink).
+ syntheticEvent = this;
+ });
+
+ link.simulate("click", { preventDefault });
+ // Prevent defaults behavior on click
+ expect(preventDefault).toHaveBeenCalled();
+ expect(openLink).toHaveBeenCalledWith(url, syntheticEvent);
+};
+
+describe("test String with URL", () => {
+ it("renders a URL", () => {
+ const url = "http://example.com";
+ const openLink = jest.fn();
+ const element = renderRep(url, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a href when openLink isn't defined", () => {
+ const url = "http://example.com";
+ const element = renderRep(url, { useQuotes: false });
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(null);
+ expect(link.prop("title")).toBe(url);
+ expect(link.prop("rel")).toBe("noopener noreferrer");
+ });
+
+ it("renders a href when no openLink but isInContentPage is true", () => {
+ const url = "http://example.com";
+ const element = renderRep(url, { useQuotes: false, isInContentPage: true });
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+ expect(link.prop("rel")).toBe("noopener noreferrer");
+ });
+
+ it("renders a simple quoted URL", () => {
+ const url = "http://example.com";
+ const string = `'${url}'`;
+ const openLink = jest.fn();
+ const element = renderRep(string, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(string);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a double quoted URL", () => {
+ const url = "http://example.com";
+ const string = `"${url}"`;
+ const openLink = jest.fn();
+ const element = renderRep(string, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(string);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a quoted URL when useQuotes is true", () => {
+ const url = "http://example.com";
+ const string = `"${url}"`;
+ const openLink = jest.fn();
+ const element = renderRep(string, { openLink, useQuotes: true });
+ expect(element.text()).toEqual(`'"${url}"'`);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a simple https URL", () => {
+ const url = "https://example.com";
+ const openLink = jest.fn();
+ const element = renderRep(url, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a simple http URL with one slash", () => {
+ const url = "https:/example.com";
+ const openLink = jest.fn();
+ const element = renderRep(url, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a URL with port", () => {
+ const url = "https://example.com:443";
+ const openLink = jest.fn();
+ const element = renderRep(url, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a URL with non-empty path", () => {
+ const url = "http://example.com/foo";
+ const openLink = jest.fn();
+ const element = renderRep(url, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a URL when surrounded by non-URL tokens", () => {
+ const url = "http://example.com";
+ const string = `foo ${url} bar`;
+ const openLink = jest.fn();
+ const element = renderRep(string, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(string);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a URL and whitespace is be preserved", () => {
+ const url = "http://example.com";
+ const string = `foo\n${url}\nbar\n`;
+ const openLink = jest.fn();
+ const element = renderRep(string, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(string);
+
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders multiple URLs", () => {
+ const url1 = "http://example.com";
+ const url2 = "https://example.com/foo";
+ const string = `${url1} ${url2}`;
+ const openLink = jest.fn();
+ const element = renderRep(string, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(string);
+ const links = element.find("a");
+ expect(links).toHaveLength(2);
+
+ const firstLink = links.at(0);
+ expect(firstLink.prop("href")).toBe(url1);
+ expect(firstLink.prop("title")).toBe(url1);
+ testLinkClick(firstLink, openLink, url1);
+
+ const secondLink = links.at(1);
+ expect(secondLink.prop("href")).toBe(url2);
+ expect(secondLink.prop("title")).toBe(url2);
+ testLinkClick(secondLink, openLink, url2);
+ });
+
+ it("renders multiple URLs with various spacing", () => {
+ const url1 = "http://example.com";
+ const url2 = "https://example.com/foo";
+ const string = ` ${url1} ${url2} ${url2} ${url1} `;
+ const element = renderRep(string, { useQuotes: false });
+ expect(element.text()).toEqual(string);
+ const links = element.find("a");
+ expect(links).toHaveLength(4);
+ });
+
+ it("renders a cropped URL", () => {
+ const url = "http://example.com";
+ const openLink = jest.fn();
+ const element = renderRep(url, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 15,
+ });
+
+ expect(element.text()).toEqual("http://…ple.com");
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders a non-cropped URL", () => {
+ const url = "http://example.com/foobarbaz";
+ const openLink = jest.fn();
+ const element = renderRep(url, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 50,
+ });
+
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders URL on an open string", () => {
+ const url = "http://example.com";
+ const openLink = jest.fn();
+ const element = renderRep(url, {
+ openLink,
+ useQuotes: false,
+ member: {
+ open: true,
+ },
+ });
+
+ expect(element.text()).toEqual(url);
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("renders URLs with a stripped string between", () => {
+ const text = "- http://example.fr --- http://example.us -";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 41,
+ });
+
+ expect(element.text()).toEqual("- http://example.fr … http://example.us -");
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example.fr");
+ expect(linkFr.prop("title")).toBe("http://example.fr");
+
+ const linkUs = element.find("a").at(1);
+ expect(linkUs.prop("href")).toBe("http://example.us");
+ expect(linkUs.prop("title")).toBe("http://example.us");
+ });
+
+ it("renders URLs with a cropped string between", () => {
+ const text = "- http://example.fr ---- http://example.us -";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 42,
+ });
+
+ expect(element.text()).toEqual(
+ "- http://example.fr -…- http://example.us -"
+ );
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example.fr");
+ expect(linkFr.prop("title")).toBe("http://example.fr");
+
+ const linkUs = element.find("a").at(1);
+ expect(linkUs.prop("href")).toBe("http://example.us");
+ expect(linkUs.prop("title")).toBe("http://example.us");
+ });
+
+ it("renders successive cropped URLs, 1 at the start, 1 at the end", () => {
+ const text = "- http://example-long.fr http://example.us -";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 20,
+ });
+
+ expect(element.text()).toEqual("- http://e…ample.us -");
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example-long.fr");
+ expect(linkFr.prop("title")).toBe("http://example-long.fr");
+
+ const linkUs = element.find("a").at(1);
+ expect(linkUs.prop("href")).toBe("http://example.us");
+ expect(linkUs.prop("title")).toBe("http://example.us");
+ });
+
+ it("renders successive URLs, one cropped in the middle", () => {
+ const text =
+ "- http://example-long.fr http://example.com http://example.us -";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 60,
+ });
+
+ expect(element.text()).toEqual(
+ "- http://example-long.fr http:…xample.com http://example.us -"
+ );
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example-long.fr");
+ expect(linkFr.prop("title")).toBe("http://example-long.fr");
+
+ const linkCom = element.find("a").at(1);
+ expect(linkCom.prop("href")).toBe("http://example.com");
+ expect(linkCom.prop("title")).toBe("http://example.com");
+
+ const linkUs = element.find("a").at(2);
+ expect(linkUs.prop("href")).toBe("http://example.us");
+ expect(linkUs.prop("title")).toBe("http://example.us");
+ });
+
+ it("renders successive cropped URLs with cropped elements between", () => {
+ const text =
+ "- http://example.fr test http://example.es test http://example.us -";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 20,
+ });
+
+ expect(element.text()).toEqual("- http://e…ample.us -");
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example.fr");
+ expect(linkFr.prop("title")).toBe("http://example.fr");
+
+ const linkUs = element.find("a").at(1);
+ expect(linkUs.prop("href")).toBe("http://example.us");
+ expect(linkUs.prop("title")).toBe("http://example.us");
+ });
+
+ it("renders a cropped URL followed by a cropped string", () => {
+ const text = "http://example.fr abcdefghijkl";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 20,
+ });
+
+ expect(element.text()).toEqual("http://exa…cdefghijkl");
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example.fr");
+ expect(linkFr.prop("title")).toBe("http://example.fr");
+ });
+
+ it("renders a cropped string followed by a cropped URL", () => {
+ const text = "abcdefghijkl stripped http://example.fr ";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 20,
+ });
+
+ expect(element.text()).toEqual("abcdefghij…xample.fr ");
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example.fr");
+ expect(linkFr.prop("title")).toBe("http://example.fr");
+ });
+
+ it("renders URLs without unrelated characters", () => {
+ const text =
+ "global(http://example.com) and local(http://example.us)" +
+ " and maybe https://example.fr, “https://example.cz“, https://example.es?";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ });
+
+ expect(element.text()).toEqual(text);
+ const linkCom = element.find("a").at(0);
+ expect(linkCom.prop("href")).toBe("http://example.com");
+
+ const linkUs = element.find("a").at(1);
+ expect(linkUs.prop("href")).toBe("http://example.us");
+
+ const linkFr = element.find("a").at(2);
+ expect(linkFr.prop("href")).toBe("https://example.fr");
+
+ const linkCz = element.find("a").at(3);
+ expect(linkCz.prop("href")).toBe("https://example.cz");
+
+ const linkEs = element.find("a").at(4);
+ expect(linkEs.prop("href")).toBe("https://example.es");
+ });
+
+ it("renders a cropped URL with urlCropLimit", () => {
+ const xyzUrl = "http://xyz.com/abcdefghijklmnopqrst";
+ const text = `${xyzUrl} is the best`;
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ urlCropLimit: 20,
+ });
+
+ expect(element.text()).toEqual(text);
+ const link = element.find("a.cropped-url").at(0);
+ expect(link.prop("href")).toBe(xyzUrl);
+ expect(link.prop("title")).toBe(xyzUrl);
+ const linkParts = link.find("span");
+ expect(linkParts.at(0).hasClass("cropped-url-start")).toBe(true);
+ expect(linkParts.at(0).text()).toEqual("http://xyz");
+ expect(linkParts.at(1).hasClass("cropped-url-middle")).toBe(true);
+ expect(linkParts.at(2).hasClass("cropped-url-end")).toBe(true);
+ expect(linkParts.at(2).text()).toEqual("klmnopqrst");
+ });
+
+ it("renders multiple cropped URL", () => {
+ const xyzUrl = "http://xyz.com/abcdefghijklmnopqrst";
+ const abcUrl = "http://abc.com/abcdefghijklmnopqrst";
+ const text = `${xyzUrl} is lit, not ${abcUrl}`;
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ urlCropLimit: 20,
+ });
+
+ expect(element.text()).toEqual(`${xyzUrl} is lit, not ${abcUrl}`);
+
+ const links = element.find("a.cropped-url");
+ const xyzLink = links.at(0);
+ expect(xyzLink.prop("href")).toBe(xyzUrl);
+ expect(xyzLink.prop("title")).toBe(xyzUrl);
+ const xyzLinkParts = xyzLink.find("span");
+ expect(xyzLinkParts.at(0).hasClass("cropped-url-start")).toBe(true);
+ expect(xyzLinkParts.at(0).text()).toEqual("http://xyz");
+ expect(xyzLinkParts.at(1).hasClass("cropped-url-middle")).toBe(true);
+ expect(xyzLinkParts.at(2).hasClass("cropped-url-end")).toBe(true);
+ expect(xyzLinkParts.at(2).text()).toEqual("klmnopqrst");
+
+ const abc = links.at(1);
+ expect(abc.prop("href")).toBe(abcUrl);
+ expect(abc.prop("title")).toBe(abcUrl);
+ const abcLinkParts = abc.find("span");
+ expect(abcLinkParts.at(0).hasClass("cropped-url-start")).toBe(true);
+ expect(abcLinkParts.at(0).text()).toEqual("http://abc");
+ expect(abcLinkParts.at(1).hasClass("cropped-url-middle")).toBe(true);
+ expect(abcLinkParts.at(2).hasClass("cropped-url-end")).toBe(true);
+ expect(abcLinkParts.at(2).text()).toEqual("klmnopqrst");
+ });
+
+ it("renders full URL if smaller than cropLimit", () => {
+ const xyzUrl = "http://example.com/";
+
+ const openLink = jest.fn();
+ const element = renderRep(xyzUrl, {
+ openLink,
+ useQuotes: false,
+ urlCropLimit: 20,
+ });
+
+ expect(element.text()).toEqual(xyzUrl);
+ const link = element.find("a").at(0);
+ expect(link.prop("href")).toBe(xyzUrl);
+ expect(link.prop("title")).toBe(xyzUrl);
+ expect(link.find(".cropped-url-start").length).toBe(0);
+ });
+
+ it("renders cropped URL followed by cropped string with urlCropLimit", () => {
+ const text = "http://example.fr abcdefghijkl";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 20,
+ });
+
+ expect(element.text()).toEqual("http://exa…cdefghijkl");
+ const linkFr = element.find("a").at(0);
+ expect(linkFr.prop("href")).toBe("http://example.fr");
+ expect(linkFr.prop("title")).toBe("http://example.fr");
+ });
+
+ it("does not render a link if the URL has no scheme", () => {
+ const url = "example.com";
+ const element = renderRep(url, { useQuotes: false });
+ expect(element.text()).toEqual(url);
+ expect(element.find("a").exists()).toBeFalsy();
+ });
+
+ it("does not render a link if the URL has an invalid scheme", () => {
+ const url = "foo://example.com";
+ const element = renderRep(url, { useQuotes: false });
+ expect(element.text()).toEqual(url);
+ expect(element.find("a").exists()).toBeFalsy();
+ });
+
+ it("does not render an invalid URL that requires cropping", () => {
+ const text =
+ "//www.youtubeinmp3.com/download/?video=https://www.youtube.com/watch?v=8vkfsCIfDFc";
+ const openLink = jest.fn();
+ const element = renderRep(text, {
+ openLink,
+ useQuotes: false,
+ cropLimit: 60,
+ });
+ expect(element.text()).toEqual(
+ "//www.youtubeinmp3.com/downloa…outube.com/watch?v=8vkfsCIfDFc"
+ );
+ expect(element.find("a").exists()).toBeFalsy();
+ });
+
+ it("does render a link in a plain array", () => {
+ const url = "http://example.com/abcdefghijabcdefghij";
+ const string = `${url} some other text`;
+ const object = [string];
+ const openLink = jest.fn();
+ const element = renderRep(object, { openLink, noGrip: true });
+ expect(element.text()).toEqual(`[ "${string}" ]`);
+
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("does render a link in a grip array", () => {
+ const object =
+ require("resource://devtools/client/shared/components/test/node/stubs/reps/grip-array.js").get(
+ '["http://example.com/abcdefghijabcdefghij some other text"]'
+ );
+ const length = getGripLengthBubbleText(object);
+ const openLink = jest.fn();
+ const element = renderRep(object, { openLink });
+
+ const url = "http://example.com/abcdefghijabcdefghij";
+ const string = `${url} some other text`;
+ expect(element.text()).toEqual(`Array${length} [ "${string}" ]`);
+
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("does render a link in a plain object", () => {
+ const url = "http://example.com/abcdefghijabcdefghij";
+ const string = `${url} some other text`;
+ const object = { test: string };
+ const openLink = jest.fn();
+ const element = renderRep(object, { openLink, noGrip: true });
+ expect(element.text()).toEqual(`Object { test: "${string}" }`);
+
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("does render a link in a grip object", () => {
+ const object =
+ require("resource://devtools/client/shared/components/test/node/stubs/reps/grip.js").get(
+ '{test: "http://example.com/ some other text"}'
+ );
+ const openLink = jest.fn();
+ const element = renderRep(object, { openLink });
+
+ const url = "http://example.com/";
+ const string = `${url} some other text`;
+ expect(element.text()).toEqual(`Object { test: "${string}" }`);
+
+ const link = element.find("a");
+ expect(link.prop("href")).toBe(url);
+ expect(link.prop("title")).toBe(url);
+
+ testLinkClick(link, openLink, url);
+ });
+
+ it("does not render links for js URL", () => {
+ const url = "javascript:x=42";
+ const string = `${url} some other text`;
+
+ const openLink = jest.fn();
+ const element = renderRep(string, { openLink, useQuotes: false });
+ expect(element.text()).toEqual(string);
+ const link = element.find("a");
+ expect(link.exists()).toBe(false);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/string.test.js b/devtools/client/shared/components/test/node/components/reps/string.test.js
new file mode 100644
index 0000000000..4e179ecab0
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/string.test.js
@@ -0,0 +1,257 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow, mount } = require("enzyme");
+const {
+ ELLIPSIS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+const {
+ REPS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Rep } = REPS;
+
+const renderRep = (string, props) =>
+ mount(
+ Rep({
+ object: string,
+ ...props,
+ })
+ );
+
+const testCases = [
+ {
+ name: "testMultiline",
+ props: {
+ object: "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n",
+ },
+ result:
+ '"aaaaaaaaaaaaaaaaaaaaa\\nbbbbbbbbbbbbbbbbbbb\\ncccccccccccccccc\\n"',
+ },
+ {
+ name: "testMultilineLimit",
+ props: {
+ object: "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n",
+ cropLimit: 20,
+ },
+ result: `\"aaaaaaaaa${ELLIPSIS}cccccc\\n\"`,
+ },
+ {
+ name: "testMultilineOpen",
+ props: {
+ object: "aaaaaaaaaaaaaaaaaaaaa\nbbbbbbbbbbbbbbbbbbb\ncccccccccccccccc\n",
+ member: { open: true },
+ },
+ result:
+ '"aaaaaaaaaaaaaaaaaaaaa\\nbbbbbbbbbbbbbbbbbbb\\ncccccccccccccccc\\n"',
+ },
+ {
+ name: "testUseQuotes",
+ props: {
+ object: "abc",
+ useQuotes: false,
+ },
+ result: "abc",
+ },
+ {
+ name: "testNonPrintableCharacters",
+ props: {
+ object: "a\x01b",
+ useQuotes: false,
+ },
+ result: "a\ufffdb",
+ },
+ {
+ name: "testQuoting",
+ props: {
+ object:
+ "\t\n\r\"'\\\x1f\x9f\ufeff\ufffe\ud8000\u2063\ufffc\u2028\ueeee\ufffd",
+ useQuotes: true,
+ },
+ result:
+ "`\\t\\n\\r\"'\\\\\\u001f\\u009f\\ufeff\\ufffe\\ud8000\\u2063" +
+ "\\ufffc\\u2028\\ueeee\ufffd`",
+ },
+ {
+ name: "testUnpairedSurrogate",
+ props: {
+ object: "\uDC23",
+ useQuotes: true,
+ },
+ result: '"\\udc23"',
+ },
+ {
+ name: "testValidSurrogate",
+ props: {
+ object: "\ud83d\udeec",
+ useQuotes: true,
+ },
+ result: '"\ud83d\udeec"',
+ },
+ {
+ name: "testNoEscapeWhitespace",
+ props: {
+ object: "line 1\r\nline 2\n\tline 3",
+ useQuotes: true,
+ escapeWhitespace: false,
+ },
+ result: '"line 1\r\nline 2\n\tline 3"',
+ },
+ {
+ name: "testIgnoreFullTextWhenOpen",
+ props: {
+ object: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ fullText:
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaa",
+ member: { open: true },
+ },
+ result: '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"',
+ },
+ {
+ name: "testIgnoreFullTextWithLimit",
+ props: {
+ object: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ fullText:
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaa",
+ cropLimit: 20,
+ },
+ result: `\"aaaaaaaaa${ELLIPSIS}aaaaaaaa\"`,
+ },
+ {
+ name: "testIgnoreFullTextWhenOpenWithLimit",
+ props: {
+ object: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ fullText:
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +
+ "aaaaaaaaaaaaa",
+ member: { open: true },
+ cropLimit: 20,
+ },
+ result: '"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"',
+ },
+ {
+ name: "testEmptyStringWithoutQuotes",
+ props: {
+ object: "",
+ transformEmptyString: true,
+ useQuotes: false,
+ },
+ result: "<empty string>",
+ },
+ {
+ name: "testEmptyStringWithoutQuotesAndNoTransform",
+ props: {
+ object: "",
+ useQuotes: false,
+ transformEmptyString: false,
+ },
+ result: "",
+ },
+ {
+ name: "testEmptyStringWithQuotes",
+ props: {
+ object: "",
+ useQuotes: true,
+ transformEmptyString: true,
+ },
+ result: `""`,
+ },
+ {
+ name: "testEmptyStringWithQuotesAndNoTransforms",
+ props: {
+ object: "",
+ useQuotes: true,
+ transformEmptyString: false,
+ },
+ result: `""`,
+ },
+ {
+ name: "testQuotingSingleQuote",
+ props: {
+ object: "'",
+ useQuotes: true,
+ },
+ result: `"'"`,
+ },
+ {
+ name: "testQuotingDoubleQuote",
+ props: {
+ object: '"',
+ useQuotes: true,
+ },
+ result: `'"'`,
+ },
+ {
+ name: "testQuotingBacktick",
+ props: {
+ object: "`",
+ useQuotes: true,
+ },
+ result: '"`"',
+ },
+ {
+ name: "testQuotingSingleAndDoubleQuotes",
+ props: {
+ object: "'\"",
+ useQuotes: true,
+ },
+ result: "`'\"`",
+ },
+ {
+ name: "testQuotingSingleAndDoubleQuotesAnd${",
+ props: {
+ object: "'\"${",
+ useQuotes: true,
+ },
+ result: '"\'\\"${"',
+ },
+ {
+ name: "testQuotingSingleQuoteAndBacktick",
+ props: {
+ object: "'`",
+ useQuotes: true,
+ },
+ result: '"\'`"',
+ },
+ {
+ name: "testQuotingDoubleQuoteAndBacktick",
+ props: {
+ object: '"`',
+ useQuotes: true,
+ },
+ result: "'\"`'",
+ },
+ {
+ name: "testQuotingSingleAndDoubleQuotesAndBacktick",
+ props: {
+ object: "'\"`",
+ useQuotes: true,
+ },
+ result: '"\'\\"`"',
+ },
+];
+
+describe("test String", () => {
+ for (const testCase of testCases) {
+ it(`String rep ${testCase.name}`, () => {
+ const renderedComponent = shallow(Rep(testCase.props));
+ expect(renderedComponent.text()).toEqual(testCase.result);
+ });
+ }
+
+ it("If shouldRenderTooltip, StringRep displays a tooltip title on the span element.", () => {
+ const tooltipText = "This is a tooltip";
+ const element = renderRep(tooltipText, { shouldRenderTooltip: true });
+ expect(element.prop("title")).toBe('"This is a tooltip"');
+ });
+
+ it("If !shouldRenderTooltip, StringRep doesn't display a tooltip title.", () => {
+ const noTooltip = "There is no tooltip";
+ const element = renderRep(noTooltip, { shouldRenderTooltip: false });
+ expect(element.prop("title")).toBe(undefined);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/stylesheet.test.js b/devtools/client/shared/components/test/node/components/reps/stylesheet.test.js
new file mode 100644
index 0000000000..31c40facfe
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/stylesheet.test.js
@@ -0,0 +1,41 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { StyleSheet, Rep } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/stylesheet.js");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+describe("Test StyleSheet", () => {
+ const stub = stubs.get("StyleSheet")._grip;
+
+ it("selects the StyleSheet Rep", () => {
+ expect(getRep(stub)).toEqual(StyleSheet.rep);
+ });
+
+ it("renders with the expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "CSSStyleSheet https://example.com/styles.css"
+ );
+ expect(renderedComponent.prop("title")).toEqual(
+ "CSSStyleSheet https://example.com/styles.css"
+ );
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/symbol.test.js b/devtools/client/shared/components/test/node/components/reps/symbol.test.js
new file mode 100644
index 0000000000..4959e9a813
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/symbol.test.js
@@ -0,0 +1,64 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+const {
+ REPS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const { Rep } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/symbol.js");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+describe("test Symbol", () => {
+ const stub = stubs.get("Symbol");
+
+ it("renders with the expected content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual('Symbol("foo")');
+ expect(renderedComponent.prop("title")).toBe("Symbol(foo)");
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
+
+describe("test Symbol without identifier", () => {
+ const stub = stubs.get("SymbolWithoutIdentifier");
+
+ it("renders the expected content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Symbol()");
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
+
+describe("test Symbol with long string", () => {
+ const stub = stubs.get("SymbolWithLongString");
+
+ it("renders the expected content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ 'Symbol("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…")'
+ );
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/test-helpers.js b/devtools/client/shared/components/test/node/components/reps/test-helpers.js
new file mode 100644
index 0000000000..d601f71e88
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/test-helpers.js
@@ -0,0 +1,116 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ lengthBubble,
+} = require("resource://devtools/client/shared/components/reps/shared/grip-length-bubble.js");
+const {
+ maxLengthMap: arrayLikeMaxLengthMap,
+ getLength: getArrayLikeLength,
+} = require("resource://devtools/client/shared/components/reps/reps/grip-array.js");
+const {
+ maxLengthMap: mapMaxLengths,
+ getLength: getMapLength,
+} = require("resource://devtools/client/shared/components/reps/reps/grip-map.js");
+const {
+ getGripPreviewItems,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+const nodeConstants = require("resource://devtools/client/shared/components/reps/shared/dom-node-constants.js");
+
+/**
+ * Get an array of all the items from the grip in parameter (including the grip
+ * itself) which can be selected in the inspector.
+ *
+ * @param {Object} Grip
+ * @return {Array} Flat array of the grips which can be selected in the
+ * inspector
+ */
+function getSelectableInInspectorGrips(grip) {
+ const grips = new Set(getFlattenedGrips([grip]));
+ return [...grips].filter(isGripSelectableInInspector);
+}
+
+/**
+ * Indicate if a Grip can be selected in the inspector,
+ * i.e. if it represents a node element.
+ *
+ * @param {Object} Grip
+ * @return {Boolean}
+ */
+function isGripSelectableInInspector(grip) {
+ return (
+ grip &&
+ typeof grip === "object" &&
+ grip.preview &&
+ [nodeConstants.TEXT_NODE, nodeConstants.ELEMENT_NODE].includes(
+ grip.preview.nodeType
+ )
+ );
+}
+
+/**
+ * Get a flat array of all the grips and their preview items.
+ *
+ * @param {Array} Grips
+ * @return {Array} Flat array of the grips and their preview items
+ */
+function getFlattenedGrips(grips) {
+ return grips.reduce((res, grip) => {
+ const previewItems = getGripPreviewItems(grip);
+ const flatPreviewItems = previewItems.length
+ ? getFlattenedGrips(previewItems)
+ : [];
+
+ return [...res, grip, ...flatPreviewItems];
+ }, []);
+}
+
+function expectActorAttribute(wrapper, expectedValue) {
+ const actorIdAttribute = "data-link-actor-id";
+ const attrElement = wrapper.find(`[${actorIdAttribute}]`);
+ expect(attrElement.exists()).toBeTruthy();
+ expect(attrElement.first().prop("data-link-actor-id")).toBe(expectedValue);
+}
+
+function getGripLengthBubbleText(object, props) {
+ const component = lengthBubble({
+ object,
+ maxLengthMap: arrayLikeMaxLengthMap,
+ getLength: getArrayLikeLength,
+ ...props,
+ });
+
+ return component ? shallow(component).text() : "";
+}
+
+function getMapLengthBubbleText(object, props) {
+ return getGripLengthBubbleText(object, {
+ maxLengthMap: mapMaxLengths,
+ getLength: getMapLength,
+ showZeroLength: true,
+ ...props,
+ });
+}
+
+function createGripMapEntry(key, value) {
+ return {
+ type: "mapEntry",
+ preview: {
+ key,
+ value,
+ },
+ };
+}
+
+module.exports = {
+ createGripMapEntry,
+ expectActorAttribute,
+ getSelectableInInspectorGrips,
+ getGripLengthBubbleText,
+ getMapLengthBubbleText,
+};
diff --git a/devtools/client/shared/components/test/node/components/reps/text-node.test.js b/devtools/client/shared/components/test/node/components/reps/text-node.test.js
new file mode 100644
index 0000000000..13a8a89154
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/text-node.test.js
@@ -0,0 +1,203 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const { TextNode } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/text-node.js");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+const {
+ ELLIPSIS,
+} = require("resource://devtools/client/shared/components/reps/reps/rep-utils.js");
+
+function quoteNewlines(text) {
+ return text.split("\n").join("\\n");
+}
+
+describe("TextNode", () => {
+ it("selects TextNode Rep as expected", () => {
+ expect(getRep(stubs.get("testRendering")._grip)).toBe(TextNode.rep);
+ });
+
+ it("renders as expected", () => {
+ const object = stubs.get("testRendering")._grip;
+ const renderRep = props => shallow(TextNode.rep({ object, ...props }));
+
+ const defaultOutput = '#text "hello world"';
+
+ let component = renderRep({ shouldRenderTooltip: true, mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.TINY });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.HEADER });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultOutput);
+ expectActorAttribute(component, object.actor);
+ });
+
+ it("renders as expected with EOL", () => {
+ const object = stubs.get("testRenderingWithEOL")._grip;
+ const renderRep = props => shallow(TextNode.rep({ object, ...props }));
+
+ const defaultOutput = quoteNewlines('#text "hello\nworld"');
+ const defaultTooltip = '#text "hello\nworld"';
+
+ let component = renderRep({ shouldRenderTooltip: true, mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.TINY });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.HEADER });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+ });
+
+ it("renders as expected with double quote", () => {
+ const object = stubs.get("testRenderingWithDoubleQuote")._grip;
+ const renderRep = props => shallow(TextNode.rep({ object, ...props }));
+
+ const defaultOutput = "#text 'hello\"world'";
+ const defaultTooltip = '#text "hello"world"';
+
+ let component = renderRep({ shouldRenderTooltip: true, mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.TINY });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.HEADER });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+ });
+
+ it("renders as expected with long string", () => {
+ const object = stubs.get("testRenderingWithLongString")._grip;
+ const renderRep = props => shallow(TextNode.rep({ object, ...props }));
+ const initialString = object.preview.textContent.initial;
+
+ const defaultOutput = `#text "${quoteNewlines(initialString)}${ELLIPSIS}"`;
+ const defaultTooltip = `#text "${initialString}"`;
+
+ let component = renderRep({ shouldRenderTooltip: true, mode: undefined });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.TINY });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.HEADER });
+ expect(component.text()).toBe("#text");
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.SHORT });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+
+ component = renderRep({ shouldRenderTooltip: true, mode: MODE.LONG });
+ expect(component.text()).toBe(defaultOutput);
+ expect(component.prop("title")).toBe(defaultTooltip);
+ });
+
+ it("calls the expected function on mouseover", () => {
+ const object = stubs.get("testRendering")._grip;
+ const onDOMNodeMouseOver = jest.fn();
+ const wrapper = shallow(TextNode.rep({ object, onDOMNodeMouseOver }));
+
+ wrapper.simulate("mouseover");
+
+ expect(onDOMNodeMouseOver.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOver).toHaveBeenCalledWith(object);
+ });
+
+ it("calls the expected function on mouseout", () => {
+ const object = stubs.get("testRendering")._grip;
+ const onDOMNodeMouseOut = jest.fn();
+ const wrapper = shallow(TextNode.rep({ object, onDOMNodeMouseOut }));
+
+ wrapper.simulate("mouseout");
+
+ expect(onDOMNodeMouseOut.mock.calls).toHaveLength(1);
+ expect(onDOMNodeMouseOut).toHaveBeenCalledWith(object);
+ });
+
+ it("displays a button when the node is connected", () => {
+ const object = stubs.get("testRendering")._grip;
+
+ const onInspectIconClick = jest.fn();
+ const wrapper = shallow(TextNode.rep({ object, onInspectIconClick }));
+
+ const inspectIconNode = wrapper.find(".open-inspector");
+ expect(inspectIconNode !== null).toBe(true);
+
+ const event = Symbol("click-event");
+ inspectIconNode.simulate("click", event);
+
+ // The function is called once
+ expect(onInspectIconClick.mock.calls).toHaveLength(1);
+ const [arg1, arg2] = onInspectIconClick.mock.calls[0];
+ // First argument is the grip
+ expect(arg1).toBe(object);
+ // Second one is the event
+ expect(arg2).toBe(event);
+ });
+
+ it("does not display a button when the node is connected", () => {
+ const object = stubs.get("testRenderingDisconnected")._grip;
+
+ const onInspectIconClick = jest.fn();
+ const wrapper = shallow(TextNode.rep({ object, onInspectIconClick }));
+ expect(wrapper.find(".open-inspector")).toHaveLength(0);
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/undefined.test.js b/devtools/client/shared/components/test/node/components/reps/undefined.test.js
new file mode 100644
index 0000000000..56fa512f55
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/undefined.test.js
@@ -0,0 +1,58 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const { Undefined, Rep } = REPS;
+
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/undefined.js");
+// Test that correct rep is chosen
+describe("Test Undefined", () => {
+ const stub = stubs.get("Undefined");
+
+ it("selects Undefined as expected", () => {
+ expect(getRep(stub)).toBe(Undefined.rep);
+ });
+
+ it("Rep correctly selects Undefined Rep for plain JS undefined", () => {
+ expect(getRep(undefined, undefined, true)).toBe(Undefined.rep);
+ });
+
+ it("Undefined rep has expected text content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+ expect(renderedComponent.text()).toEqual("undefined");
+ });
+
+ it("Undefined rep has expected class names", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+ expect(renderedComponent.hasClass("objectBox objectBox-undefined")).toEqual(
+ true
+ );
+ });
+
+ it("Undefined rep has expected title", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+ expect(renderedComponent.prop("title")).toEqual("undefined");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/reps/window.test.js b/devtools/client/shared/components/test/node/components/reps/window.test.js
new file mode 100644
index 0000000000..9f789d3be6
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/reps/window.test.js
@@ -0,0 +1,197 @@
+/* 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/>. */
+
+"use strict";
+
+const { shallow } = require("enzyme");
+
+const {
+ REPS,
+ getRep,
+} = require("resource://devtools/client/shared/components/reps/reps/rep.js");
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const { Rep, Window } = REPS;
+const stubs = require("resource://devtools/client/shared/components/test/node/stubs/reps/window.js");
+const {
+ expectActorAttribute,
+} = require("resource://devtools/client/shared/components/test/node/components/reps/test-helpers.js");
+
+describe("test Window", () => {
+ const stub = stubs.get("Window")._grip;
+
+ it("selects Window Rep correctly", () => {
+ expect(getRep(stub)).toBe(Window.rep);
+ });
+
+ it("renders with correct class name", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.hasClass("objectBox-Window")).toBe(true);
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders with correct content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "Window data:text/html;charset=utf-8,stub generation"
+ );
+ expect(renderedComponent.prop("title")).toEqual(
+ "Window data:text/html;charset=utf-8,stub generation"
+ );
+ });
+
+ it("renders with correct inner HTML structure and content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.find(".location").text()).toEqual(
+ "data:text/html;charset=utf-8,stub generation"
+ );
+ });
+
+ it("renders with expected text in TINY mode", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ mode: MODE.TINY,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Window");
+ expect(renderedComponent.prop("title")).toEqual(
+ "Window data:text/html;charset=utf-8,stub generation"
+ );
+ });
+
+ it("renders with expected text in LONG mode", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ mode: MODE.LONG,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "Window data:text/html;charset=utf-8,stub generation"
+ );
+ expect(renderedComponent.prop("title")).toEqual(
+ "Window data:text/html;charset=utf-8,stub generation"
+ );
+ });
+
+ it("renders expected text in TINY mode with Custom display class", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: {
+ ...stub,
+ displayClass: "Custom",
+ },
+ mode: MODE.TINY,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Custom");
+ });
+
+ it("renders expected text in LONG mode with Custom display class", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: {
+ ...stub,
+ displayClass: "Custom",
+ },
+ mode: MODE.LONG,
+ title: "Custom",
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "Custom data:text/html;charset=utf-8,stub generation"
+ );
+ });
+});
+
+describe("test cross-process iframe contentWindow", () => {
+ const stub = stubs.get("CrossOriginIframeContentWindow")._grip;
+
+ it("selects Window Rep correctly", () => {
+ expect(getRep(stub)).toBe(Window.rep);
+ });
+
+ it("renders with correct class name", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.hasClass("objectBox-Window")).toBe(true);
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders with correct content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual(
+ "Window http://example.org/document-builder.sjs?html=example.org"
+ );
+ expect(renderedComponent.prop("title")).toEqual(
+ "Window http://example.org/document-builder.sjs?html=example.org"
+ );
+ });
+});
+
+describe("test cross-process iframe top window", () => {
+ const stub = stubs.get("CrossOriginIframeTopWindow")._grip;
+
+ it("selects Window Rep correctly", () => {
+ expect(getRep(stub)).toBe(Window.rep);
+ });
+
+ it("renders with correct class name", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ })
+ );
+
+ expect(renderedComponent.hasClass("objectBox-Window")).toBe(true);
+ expectActorAttribute(renderedComponent, stub.actor);
+ });
+
+ it("renders with correct content", () => {
+ const renderedComponent = shallow(
+ Rep({
+ object: stub,
+ shouldRenderTooltip: true,
+ })
+ );
+
+ expect(renderedComponent.text()).toEqual("Window Restricted");
+ expect(renderedComponent.prop("title")).toEqual("Window Restricted");
+ });
+});
diff --git a/devtools/client/shared/components/test/node/components/tree.test.js b/devtools/client/shared/components/test/node/components/tree.test.js
new file mode 100644
index 0000000000..c70b66e8ff
--- /dev/null
+++ b/devtools/client/shared/components/test/node/components/tree.test.js
@@ -0,0 +1,911 @@
+/* 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/>. */
+
+"use strict";
+
+/* global jest */
+
+const React = require("react");
+const { mount } = require("enzyme");
+const dom = require("react-dom-factories");
+
+const { Component, createFactory } = React;
+const Tree = createFactory(
+ require("resource://devtools/client/shared/components/Tree.js")
+);
+
+function mountTree(overrides = {}) {
+ return mount(
+ createFactory(
+ class container extends Component {
+ constructor(props) {
+ super(props);
+ const state = {
+ expanded: overrides.expanded || new Set(),
+ focused: overrides.focused,
+ active: overrides.active,
+ };
+ delete overrides.focused;
+ delete overrides.active;
+ this.state = state;
+ }
+
+ render() {
+ return Tree(
+ Object.assign(
+ {
+ getParent: x => TEST_TREE.parent[x],
+ getChildren: x => TEST_TREE.children[x],
+ renderItem: (x, depth, focused, arrow) => {
+ return dom.div(
+ {},
+ arrow,
+ focused ? "[" : null,
+ x,
+ focused ? "]" : null
+ );
+ },
+ getRoots: () => ["A", "M"],
+ getKey: x => `key-${x}`,
+ itemHeight: 1,
+ onFocus: x => {
+ this.setState(previousState => {
+ return { focused: x };
+ });
+ },
+ onActivate: x => {
+ this.setState(previousState => {
+ return { active: x };
+ });
+ },
+ onExpand: x => {
+ this.setState(previousState => {
+ const expanded = new Set(previousState.expanded);
+ expanded.add(x);
+ return { expanded };
+ });
+ },
+ onCollapse: x => {
+ this.setState(previousState => {
+ const expanded = new Set(previousState.expanded);
+ expanded.delete(x);
+ return { expanded };
+ });
+ },
+ isExpanded: x => this.state && this.state.expanded.has(x),
+ focused: this.state.focused,
+ active: this.state.active,
+ },
+ overrides
+ )
+ );
+ }
+ }
+ )()
+ );
+}
+
+describe("Tree", () => {
+ it("does not throw", () => {
+ expect(mountTree()).toBeTruthy();
+ });
+
+ it("Don't auto expand root with very large number of children", () => {
+ const children = Array.from(
+ { length: 51 },
+ (_, i) => `should-not-be-visible-${i + 1}`
+ );
+ // N has a lot of children, so it won't be automatically expanded
+ const wrapper = mountTree({
+ autoExpandDepth: 2,
+ autoExpandNodeChildrenLimit: 50,
+ getChildren: item => {
+ if (item === "N") {
+ return children;
+ }
+
+ return TEST_TREE.children[item] || [];
+ },
+ });
+ const ids = getTreeNodes(wrapper).map(node => node.prop("id"));
+ expect(ids).toMatchSnapshot();
+ });
+
+ it("is accessible", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJMN".split("")),
+ });
+ expect(wrapper.getDOMNode().getAttribute("role")).toBe("tree");
+ expect(wrapper.getDOMNode().getAttribute("tabIndex")).toBe("0");
+
+ const expected = {
+ A: { id: "key-A", level: 1, expanded: true },
+ B: { id: "key-B", level: 2, expanded: true },
+ C: { id: "key-C", level: 2, expanded: true },
+ D: { id: "key-D", level: 2, expanded: true },
+ E: { id: "key-E", level: 3, expanded: true },
+ F: { id: "key-F", level: 3, expanded: true },
+ G: { id: "key-G", level: 3, expanded: true },
+ H: { id: "key-H", level: 3, expanded: true },
+ I: { id: "key-I", level: 3, expanded: true },
+ J: { id: "key-J", level: 3, expanded: true },
+ K: { id: "key-K", level: 4, expanded: undefined },
+ L: { id: "key-L", level: 4, expanded: undefined },
+ M: { id: "key-M", level: 1, expanded: true },
+ N: { id: "key-N", level: 2, expanded: true },
+ O: { id: "key-O", level: 3, expanded: undefined },
+ };
+
+ getTreeNodes(wrapper).forEach(node => {
+ const key = node.prop("id").replace("key-", "");
+ const item = expected[key];
+
+ expect(node.prop("id")).toBe(item.id);
+ expect(node.prop("role")).toBe("treeitem");
+ expect(node.prop("aria-level")).toBe(item.level);
+ expect(node.prop("aria-expanded")).toBe(item.expanded);
+ });
+ });
+
+ it("renders as expected", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ });
+
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ });
+
+ it("renders as expected when passed a className", () => {
+ const wrapper = mountTree({
+ className: "testClassName",
+ });
+
+ expect(wrapper.find(".tree").hasClass("testClassName")).toBe(true);
+ });
+
+ it("renders as expected when passed a style", () => {
+ const wrapper = mountTree({
+ style: {
+ color: "red",
+ },
+ });
+
+ expect(wrapper.getDOMNode().style.color).toBe("red");
+ });
+
+ it("renders as expected when passed a label", () => {
+ const wrapper = mountTree({
+ label: "testAriaLabel",
+ });
+ expect(wrapper.getDOMNode().getAttribute("aria-label")).toBe(
+ "testAriaLabel"
+ );
+ });
+
+ it("renders as expected when passed an aria-labelledby", () => {
+ const wrapper = mountTree({
+ labelledby: "testAriaLabelBy",
+ });
+ expect(wrapper.getDOMNode().getAttribute("aria-labelledby")).toBe(
+ "testAriaLabelBy"
+ );
+ });
+
+ it("renders as expected with collapsed nodes", () => {
+ const wrapper = mountTree({
+ expanded: new Set("MNO".split("")),
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ });
+
+ it("renders as expected when passed autoDepth:1", () => {
+ const wrapper = mountTree({
+ autoExpandDepth: 1,
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ });
+
+ it("calls shouldItemUpdate when provided", () => {
+ const shouldItemUpdate = jest.fn((prev, next) => true);
+ const wrapper = mountTree({
+ shouldItemUpdate,
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(shouldItemUpdate.mock.calls).toHaveLength(0);
+
+ wrapper.find("Tree").first().instance().forceUpdate();
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(shouldItemUpdate.mock.calls).toHaveLength(2);
+
+ expect(shouldItemUpdate.mock.calls[0][0]).toBe("A");
+ expect(shouldItemUpdate.mock.calls[0][1]).toBe("A");
+ expect(shouldItemUpdate.mock.results[0].value).toBe(true);
+ expect(shouldItemUpdate.mock.calls[1][0]).toBe("M");
+ expect(shouldItemUpdate.mock.calls[1][1]).toBe("M");
+ expect(shouldItemUpdate.mock.results[1].value).toBe(true);
+ });
+
+ it("active item - renders as expected when clicking away", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "G",
+ active: "G",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").prop("id")).toBe("key-G");
+
+ getTreeNodes(wrapper).first().simulate("click");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+ expect(wrapper.find(".active").exists()).toBe(false);
+ });
+
+ it("active item - renders as expected when tree blurs", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "G",
+ active: "G",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").prop("id")).toBe("key-G");
+
+ wrapper.simulate("blur");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").exists()).toBe(false);
+ });
+
+ it("active item - renders as expected when moving away with keyboard", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ active: "L",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+
+ simulateKeyDown(wrapper, "ArrowUp");
+ expect(wrapper.find(".active").exists()).toBe(false);
+ });
+
+ it("active item - renders as expected when using keyboard and Enter", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ });
+ wrapper.getDOMNode().focus();
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").exists()).toBe(false);
+
+ simulateKeyDown(wrapper, "Enter");
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.getDOMNode()
+ );
+
+ simulateKeyDown(wrapper, "Escape");
+ expect(wrapper.find(".active").exists()).toBe(false);
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.getDOMNode()
+ );
+ });
+
+ it("active item - renders as expected when using keyboard and Space", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ });
+ wrapper.getDOMNode().focus();
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").exists()).toBe(false);
+
+ simulateKeyDown(wrapper, " ");
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+
+ simulateKeyDown(wrapper, "Escape");
+ expect(wrapper.find(".active").exists()).toBe(false);
+ });
+
+ it("active item - focus is inside the tree node when possible", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ renderItem: renderItemWithFocusableContent,
+ });
+ wrapper.getDOMNode().focus();
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").exists()).toBe(false);
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.getDOMNode()
+ );
+
+ simulateKeyDown(wrapper, "Enter");
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.find("#active-anchor").getDOMNode()
+ );
+ });
+
+ it("active item - navigate inside the tree node", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ renderItem: renderItemWithFocusableContent,
+ });
+ wrapper.getDOMNode().focus();
+ simulateKeyDown(wrapper, "Enter");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.find("#active-anchor").getDOMNode()
+ );
+
+ simulateKeyDown(wrapper, "Tab");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.find("#active-anchor").getDOMNode()
+ );
+
+ simulateKeyDown(wrapper, "Tab", { shiftKey: true });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.find("#active-anchor").getDOMNode()
+ );
+ });
+
+ it("active item - focus is inside the tree node and then blur", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ renderItem: renderItemWithFocusableContent,
+ });
+ wrapper.getDOMNode().focus();
+ simulateKeyDown(wrapper, "Enter");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.find(".active").prop("id")).toBe("key-L");
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.find("#active-anchor").getDOMNode()
+ );
+
+ wrapper.find("#active-anchor").simulate("blur");
+ expect(wrapper.find(".active").exists()).toBe(false);
+ expect(wrapper.getDOMNode().ownerDocument.activeElement).toBe(
+ wrapper.getDOMNode().ownerDocument.body
+ );
+ });
+
+ it("renders as expected when given a focused item", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "G",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-G"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-G");
+
+ getTreeNodes(wrapper).first().simulate("click");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+
+ getTreeNodes(wrapper).first().simulate("click");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+
+ wrapper.simulate("blur");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().hasAttribute("aria-activedescendant")).toBe(
+ false
+ );
+ expect(wrapper.find(".focused").exists()).toBe(false);
+ });
+
+ it("renders as expected when navigating up with the keyboard", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-L"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-L");
+
+ simulateKeyDown(wrapper, "ArrowUp");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-K"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-K");
+
+ simulateKeyDown(wrapper, "ArrowUp");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-E"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-E");
+ });
+
+ it("renders as expected navigating up with the keyboard on a root", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "A",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+
+ simulateKeyDown(wrapper, "ArrowUp");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+ });
+
+ it("renders as expected when navigating down with the keyboard", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "K",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-K"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-K");
+
+ simulateKeyDown(wrapper, "ArrowDown");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-L"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-L");
+
+ simulateKeyDown(wrapper, "ArrowDown");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-F"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-F");
+ });
+
+ it("renders as expected navigating down with keyboard on last node", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "O",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-O"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-O");
+
+ simulateKeyDown(wrapper, "ArrowDown");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-O"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-O");
+ });
+
+ it("renders as expected when navigating with right/left arrows", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-L"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-L");
+
+ simulateKeyDown(wrapper, "ArrowLeft");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-E"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-E");
+
+ simulateKeyDown(wrapper, "ArrowLeft");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-E"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-E");
+
+ simulateKeyDown(wrapper, "ArrowRight");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-E"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-E");
+
+ simulateKeyDown(wrapper, "ArrowRight");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-K"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-K");
+ });
+
+ it("renders as expected when navigating with left arrows on roots", () => {
+ const wrapper = mountTree({
+ focused: "M",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-M"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-M");
+
+ simulateKeyDown(wrapper, "ArrowLeft");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+
+ simulateKeyDown(wrapper, "ArrowLeft");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+ });
+
+ it("renders as expected when navigating with home/end", () => {
+ const wrapper = mountTree({
+ focused: "M",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-M"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-M");
+
+ simulateKeyDown(wrapper, "Home");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+
+ simulateKeyDown(wrapper, "Home");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+
+ simulateKeyDown(wrapper, "End");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-M"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-M");
+
+ simulateKeyDown(wrapper, "End");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-M"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-M");
+
+ simulateKeyDown(wrapper, "ArrowRight");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-M"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-M");
+
+ simulateKeyDown(wrapper, "End");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-N"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-N");
+
+ simulateKeyDown(wrapper, "End");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-N"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-N");
+
+ simulateKeyDown(wrapper, "Home");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-A");
+ });
+
+ it("renders as expected navigating with arrows on unexpandable roots", () => {
+ const wrapper = mountTree({
+ focused: "A",
+ isExpandable: item => false,
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+
+ simulateKeyDown(wrapper, "ArrowRight");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-M"
+ );
+
+ simulateKeyDown(wrapper, "ArrowLeft");
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-A"
+ );
+ });
+
+ it("calls onFocus when expected", () => {
+ const onFocus = jest.fn(x => {
+ wrapper &&
+ wrapper.setState(() => {
+ return { focused: x };
+ });
+ });
+
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "I",
+ onFocus,
+ });
+
+ simulateKeyDown(wrapper, "ArrowUp");
+ expect(onFocus.mock.calls[0][0]).toBe("H");
+
+ simulateKeyDown(wrapper, "ArrowUp");
+ expect(onFocus.mock.calls[1][0]).toBe("C");
+
+ simulateKeyDown(wrapper, "ArrowLeft");
+ simulateKeyDown(wrapper, "ArrowLeft");
+ expect(onFocus.mock.calls[2][0]).toBe("A");
+
+ simulateKeyDown(wrapper, "ArrowRight");
+ expect(onFocus.mock.calls[3][0]).toBe("B");
+
+ simulateKeyDown(wrapper, "ArrowDown");
+ expect(onFocus.mock.calls[4][0]).toBe("E");
+ });
+
+ it("focus treeRef when a node is clicked", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ });
+ const treeRef = wrapper.find("Tree").first().instance().treeRef.current;
+ treeRef.focus = jest.fn();
+
+ getTreeNodes(wrapper).first().simulate("click");
+ expect(treeRef.focus.mock.calls).toHaveLength(1);
+ });
+
+ it("doesn't focus treeRef when focused is null", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "A",
+ });
+ const treeRef = wrapper.find("Tree").first().instance().treeRef.current;
+ treeRef.focus = jest.fn();
+ wrapper.simulate("blur");
+ expect(treeRef.focus.mock.calls).toHaveLength(0);
+ });
+
+ it("ignores key strokes when pressing modifiers", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ focused: "L",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-L"
+ );
+ expect(wrapper.find(".focused").prop("id")).toBe("key-L");
+
+ const testKeys = [
+ { key: "ArrowDown" },
+ { key: "ArrowUp" },
+ { key: "ArrowLeft" },
+ { key: "ArrowRight" },
+ ];
+ const modifiers = [
+ { altKey: true },
+ { ctrlKey: true },
+ { metaKey: true },
+ { shiftKey: true },
+ ];
+
+ for (const key of testKeys) {
+ for (const modifier of modifiers) {
+ wrapper.simulate("keydown", Object.assign({}, key, modifier));
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ expect(wrapper.getDOMNode().getAttribute("aria-activedescendant")).toBe(
+ "key-L"
+ );
+ }
+ }
+ });
+
+ it("renders arrows as expected when nodes are expanded", () => {
+ const wrapper = mountTree({
+ expanded: new Set("ABCDEFGHIJKLMNO".split("")),
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+
+ getTreeNodes(wrapper).forEach(n => {
+ if ("ABECDMN".split("").includes(getSanitizedNodeText(n))) {
+ expect(n.find(".arrow.expanded").exists()).toBe(true);
+ } else {
+ expect(n.find(".arrow").exists()).toBe(false);
+ }
+ });
+ });
+
+ it("renders arrows as expected when nodes are collapsed", () => {
+ const wrapper = mountTree();
+ expect(formatTree(wrapper)).toMatchSnapshot();
+
+ getTreeNodes(wrapper).forEach(n => {
+ const arrow = n.find(".arrow");
+ expect(arrow.exists()).toBe(true);
+ expect(arrow.hasClass("expanded")).toBe(false);
+ });
+ });
+
+ it("uses isExpandable prop if it exists to render tree nodes", () => {
+ const wrapper = mountTree({
+ isExpandable: item => item === "A",
+ });
+ expect(formatTree(wrapper)).toMatchSnapshot();
+ });
+
+ it("adds the expected data-expandable attribute", () => {
+ const wrapper = mountTree({
+ isExpandable: item => item === "A",
+ });
+ const nodes = getTreeNodes(wrapper);
+ expect(nodes.at(0).prop("data-expandable")).toBe(true);
+ expect(nodes.at(1).prop("data-expandable")).toBe(false);
+ });
+});
+
+function getTreeNodes(wrapper) {
+ return wrapper.find(".tree-node");
+}
+
+function simulateKeyDown(wrapper, key, options) {
+ wrapper.simulate("keydown", {
+ key,
+ preventDefault: () => {},
+ stopPropagation: () => {},
+ ...options,
+ });
+}
+
+function renderItemWithFocusableContent(x, depth, focused, arrow) {
+ const children = [arrow, focused ? "[" : null, x];
+ if (x === "L") {
+ children.push(dom.a({ id: "active-anchor", href: "#" }, " anchor"));
+ }
+
+ if (focused) {
+ children.push("]");
+ }
+
+ return dom.div({}, ...children);
+}
+
+/*
+ * Takes an Enzyme wrapper (obtained with mount/mount/…) and
+ * returns a stringified version of the Tree, e.g.
+ *
+ * ▼ A
+ * | ▼ B
+ * | | ▼ E
+ * | | | K
+ * | | | L
+ * | | F
+ * | | G
+ * | ▼ C
+ * | | H
+ * | | I
+ * | ▼ D
+ * | | J
+ * ▼ M
+ * | ▼ N
+ * | | O
+ *
+ */
+function formatTree(wrapper) {
+ const textTree = getTreeNodes(wrapper)
+ .map(node => {
+ const level = (node.prop("aria-level") || 1) - 1;
+ const indentStr = "| ".repeat(level);
+ const arrow = node.find(".arrow");
+ let arrowStr = " ";
+ if (arrow.exists()) {
+ arrowStr = arrow.hasClass("expanded") ? "▼ " : "▶︎ ";
+ }
+
+ return `${indentStr}${arrowStr}${getSanitizedNodeText(node)}`;
+ })
+ .join("\n");
+
+ // Wrap in new lines so tree nodes are aligned as expected.
+ return `\n${textTree}\n`;
+}
+
+function getSanitizedNodeText(node) {
+ // Stripping off the invisible space used in the indent.
+ return node.text().replace(/^\u200B+/, "");
+}
+
+// Encoding of the following tree/forest:
+//
+// A
+// |-- B
+// | |-- E
+// | | |-- K
+// | | `-- L
+// | |-- F
+// | `-- G
+// |-- C
+// | |-- H
+// | `-- I
+// `-- D
+// `-- J
+// M
+// `-- N
+// `-- O
+
+var TEST_TREE = {
+ children: {
+ A: ["B", "C", "D"],
+ B: ["E", "F", "G"],
+ C: ["H", "I"],
+ D: ["J"],
+ E: ["K", "L"],
+ F: [],
+ G: [],
+ H: [],
+ I: [],
+ J: [],
+ K: [],
+ L: [],
+ M: ["N"],
+ N: ["O"],
+ O: [],
+ },
+ parent: {
+ A: null,
+ B: "A",
+ C: "A",
+ D: "A",
+ E: "B",
+ F: "B",
+ G: "B",
+ H: "C",
+ I: "C",
+ J: "D",
+ K: "E",
+ L: "E",
+ M: null,
+ N: "M",
+ O: "N",
+ },
+};
diff --git a/devtools/client/shared/components/test/node/jest.config.js b/devtools/client/shared/components/test/node/jest.config.js
new file mode 100644
index 0000000000..9c5a211c25
--- /dev/null
+++ b/devtools/client/shared/components/test/node/jest.config.js
@@ -0,0 +1,16 @@
+/* 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/>. */
+
+/* global __dirname */
+
+"use strict";
+
+const sharedJestConfig = require(`${__dirname}/../../../test-helpers/shared-jest.config`);
+
+module.exports = {
+ ...sharedJestConfig,
+ setupFiles: ["<rootDir>/setup.js"],
+ snapshotSerializers: ["enzyme-to-json/serializer"],
+ testURL: "http://localhost/",
+};
diff --git a/devtools/client/shared/components/test/node/package.json b/devtools/client/shared/components/test/node/package.json
new file mode 100644
index 0000000000..9db588176e
--- /dev/null
+++ b/devtools/client/shared/components/test/node/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "devtools-client-shared-components-tests",
+ "license": "MPL-2.0",
+ "version": "0.0.1",
+ "engines": {
+ "node": ">=8.9.4"
+ },
+ "scripts": {
+ "test": "jest",
+ "test-ci": "jest --json"
+ },
+ "dependencies": {
+ "@babel/plugin-proposal-class-properties": "7.10.4",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-proposal-optional-chaining": "^7.8.3",
+ "babel-plugin-transform-amd-to-commonjs": "1.4.0",
+ "enzyme": "^3.9.0",
+ "enzyme-adapter-react-16": "^1.13.2",
+ "enzyme-to-json": "^3.3.5",
+ "jest": "^24.6.0",
+ "jsdom": "20.0.0",
+ "react": "16.4.1",
+ "react-dom": "16.4.1",
+ "react-dom-factories": "1.0.2",
+ "react-test-renderer": "16.4.1"
+ }
+}
diff --git a/devtools/client/shared/components/test/node/setup.js b/devtools/client/shared/components/test/node/setup.js
new file mode 100644
index 0000000000..570e4462ae
--- /dev/null
+++ b/devtools/client/shared/components/test/node/setup.js
@@ -0,0 +1,15 @@
+/* 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/>. */
+
+"use strict";
+
+// Configure enzyme with React 16 adapter.
+const Enzyme = require("enzyme");
+const Adapter = require("enzyme-adapter-react-16");
+Enzyme.configure({ adapter: new Adapter() });
+
+const {
+ setMocksInGlobal,
+} = require("resource://devtools/client/shared/test-helpers/shared-node-helpers.js");
+setMocksInGlobal();
diff --git a/devtools/client/shared/components/test/node/stubs/object-inspector/grip.js b/devtools/client/shared/components/test/node/stubs/object-inspector/grip.js
new file mode 100644
index 0000000000..5df79fd62d
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/object-inspector/grip.js
@@ -0,0 +1,64 @@
+/* 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/>. */
+
+const stubs = new Map();
+
+stubs.set("proto-properties-symbols", {
+ ownProperties: {
+ a: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: 1,
+ },
+ },
+ from: "server2.conn13.child19/propertyIterator160",
+ prototype: {
+ type: "object",
+ actor: "server2.conn13.child19/obj162",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 15,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownSymbols: [],
+ ownPropertiesLength: 15,
+ ownSymbolsLength: 0,
+ safeGetterValues: {},
+ },
+ },
+ ownSymbols: [
+ {
+ name: "Symbol()",
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "hello",
+ },
+ },
+ ],
+});
+
+stubs.set("longs-string-safe-getter", {
+ ownProperties: {
+ baseVal: {
+ getterValue: {
+ type: "longString",
+ initial: "data:image/png;base64,initial",
+ length: 95080,
+ actor: "server1.conn1.child1/longString28",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+ from: "server1.conn1.child1/propertyIterator30",
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/object-inspector/map.js b/devtools/client/shared/components/test/node/stubs/object-inspector/map.js
new file mode 100644
index 0000000000..ed97b51c88
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/object-inspector/map.js
@@ -0,0 +1,154 @@
+/* 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/>. */
+
+const stubs = new Map();
+
+stubs.set("properties", {
+ from: "server2.conn14.child18/obj30",
+ prototype: {
+ type: "object",
+ actor: "server2.conn14.child18/obj31",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 11,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownSymbols: [],
+ ownPropertiesLength: 11,
+ ownSymbolsLength: 2,
+ safeGetterValues: {},
+ },
+ },
+ ownProperties: {},
+ ownSymbols: [],
+ safeGetterValues: {
+ size: {
+ getterValue: 2,
+ getterPrototypeLevel: 2,
+ enumerable: false,
+ writable: true,
+ },
+ },
+});
+
+stubs.set("11-entries", {
+ ownProperties: {
+ "0": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-0",
+ value: "value-0",
+ },
+ },
+ },
+ "1": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-1",
+ value: "value-1",
+ },
+ },
+ },
+ "2": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-2",
+ value: "value-2",
+ },
+ },
+ },
+ "3": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-3",
+ value: "value-3",
+ },
+ },
+ },
+ "4": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-4",
+ value: "value-4",
+ },
+ },
+ },
+ "5": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-5",
+ value: "value-5",
+ },
+ },
+ },
+ "6": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-6",
+ value: "value-6",
+ },
+ },
+ },
+ "7": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-7",
+ value: "value-7",
+ },
+ },
+ },
+ "8": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-8",
+ value: "value-8",
+ },
+ },
+ },
+ "9": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-9",
+ value: "value-9",
+ },
+ },
+ },
+ "10": {
+ enumerable: true,
+ value: {
+ type: "mapEntry",
+ preview: {
+ key: "key-10",
+ value: "value-10",
+ },
+ },
+ },
+ },
+ from: "server4.conn4.child19/propertyIterator54",
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/object-inspector/performance.js b/devtools/client/shared/components/test/node/stubs/object-inspector/performance.js
new file mode 100644
index 0000000000..5488070f14
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/object-inspector/performance.js
@@ -0,0 +1,784 @@
+/* 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/>. */
+
+const stubs = new Map();
+
+stubs.set("performance", {
+ from: "server2.conn4.child1/obj30",
+ prototype: {
+ type: "object",
+ actor: "server2.conn4.child1/obj33",
+ class: "PerformancePrototype",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 16,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ now: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj34",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "now",
+ displayName: "now",
+ },
+ },
+ getEntries: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj35",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "getEntries",
+ displayName: "getEntries",
+ },
+ },
+ getEntriesByType: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj36",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "getEntriesByType",
+ displayName: "getEntriesByType",
+ },
+ },
+ getEntriesByName: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj37",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "getEntriesByName",
+ displayName: "getEntriesByName",
+ },
+ },
+ clearResourceTimings: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj38",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "clearResourceTimings",
+ displayName: "clearResourceTimings",
+ },
+ },
+ setResourceTimingBufferSize: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj39",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "setResourceTimingBufferSize",
+ displayName: "setResourceTimingBufferSize",
+ },
+ },
+ mark: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj40",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "mark",
+ displayName: "mark",
+ },
+ },
+ clearMarks: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj41",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "clearMarks",
+ displayName: "clearMarks",
+ },
+ },
+ measure: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj42",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "measure",
+ displayName: "measure",
+ },
+ },
+ clearMeasures: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj43",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "clearMeasures",
+ displayName: "clearMeasures",
+ },
+ },
+ },
+ ownPropertiesLength: 16,
+ },
+ },
+ ownProperties: {
+ userTimingJsNow: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: false,
+ },
+ userTimingJsNowPrefixed: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: false,
+ },
+ userTimingJsUserTiming: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: false,
+ },
+ userTimingJsUserTimingPrefixed: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: false,
+ },
+ userTimingJsPerformanceTimeline: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: false,
+ },
+ userTimingJsPerformanceTimelinePrefixed: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: false,
+ },
+ timeOrigin: {
+ enumerable: true,
+ writable: true,
+ value: 1500971976372.9033,
+ },
+ timing: {
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj44",
+ class: "PerformanceTiming",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {
+ navigationStart: {
+ getterValue: 1500971976373,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ unloadEventStart: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ unloadEventEnd: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectStart: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectEnd: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ fetchStart: {
+ getterValue: 1500971982226,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domainLookupStart: {
+ getterValue: 1500971982251,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domainLookupEnd: {
+ getterValue: 1500971982255,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ connectStart: {
+ getterValue: 1500971982255,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ connectEnd: {
+ getterValue: 1500971982638,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+ },
+ },
+ },
+ navigation: {
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn4.child1/obj45",
+ class: "PerformanceNavigation",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {
+ type: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectCount: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+ },
+ },
+ },
+ onresourcetimingbufferfull: {
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "null",
+ },
+ },
+ },
+ safeGetterValues: {
+ timeOrigin: {
+ getterValue: 1500971976372.9033,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ timing: {
+ getterValue: {
+ type: "object",
+ actor: "server2.conn4.child1/obj44",
+ class: "PerformanceTiming",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {
+ navigationStart: {
+ getterValue: 1500971976373,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ unloadEventStart: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ unloadEventEnd: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectStart: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectEnd: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ fetchStart: {
+ getterValue: 1500971982226,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domainLookupStart: {
+ getterValue: 1500971982251,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domainLookupEnd: {
+ getterValue: 1500971982255,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ connectStart: {
+ getterValue: 1500971982255,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ connectEnd: {
+ getterValue: 1500971982638,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+ },
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ navigation: {
+ getterValue: {
+ type: "object",
+ actor: "server2.conn4.child1/obj45",
+ class: "PerformanceNavigation",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {
+ type: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectCount: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+ },
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ onresourcetimingbufferfull: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+});
+
+stubs.set("timing", {
+ from: "server1.conn1.child1/obj31",
+ prototype: {
+ type: "object",
+ actor: "server1.conn1.child1/obj32",
+ class: "PerformanceTimingPrototype",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 23,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ toJSON: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj33",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "toJSON",
+ displayName: "toJSON",
+ },
+ },
+ navigationStart: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj34",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get navigationStart",
+ displayName: "get navigationStart",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ unloadEventStart: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj35",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get unloadEventStart",
+ displayName: "get unloadEventStart",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ unloadEventEnd: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj36",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get unloadEventEnd",
+ displayName: "get unloadEventEnd",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ redirectStart: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj37",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get redirectStart",
+ displayName: "get redirectStart",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ redirectEnd: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj38",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get redirectEnd",
+ displayName: "get redirectEnd",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ fetchStart: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj39",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get fetchStart",
+ displayName: "get fetchStart",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ domainLookupStart: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj40",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get domainLookupStart",
+ displayName: "get domainLookupStart",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ domainLookupEnd: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj41",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get domainLookupEnd",
+ displayName: "get domainLookupEnd",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ connectStart: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server1.conn1.child1/obj42",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get connectStart",
+ displayName: "get connectStart",
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ },
+ ownPropertiesLength: 23,
+ },
+ },
+ ownProperties: {},
+ safeGetterValues: {
+ navigationStart: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ unloadEventStart: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ unloadEventEnd: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectStart: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ redirectEnd: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ fetchStart: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domainLookupStart: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domainLookupEnd: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ connectStart: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ connectEnd: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ secureConnectionStart: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ requestStart: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ responseStart: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ responseEnd: {
+ getterValue: 1500967716401,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domLoading: {
+ getterValue: 1500967716426,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domInteractive: {
+ getterValue: 1500967716552,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domContentLoadedEventStart: {
+ getterValue: 1500967716696,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domContentLoadedEventEnd: {
+ getterValue: 1500967716715,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ domComplete: {
+ getterValue: 1500967716719,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ loadEventStart: {
+ getterValue: 1500967716719,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ loadEventEnd: {
+ getterValue: 1500967716720,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/accessible.js b/devtools/client/shared/components/test/node/stubs/reps/accessible.js
new file mode 100644
index 0000000000..3c9834e6d3
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/accessible.js
@@ -0,0 +1,74 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("Document", {
+ actor: "server1.conn1.child1/accessible31",
+ typeName: "accessible",
+ preview: {
+ name: "New Tab",
+ role: "document",
+ isConnected: true,
+ },
+});
+
+stubs.set("ButtonMenu", {
+ actor: "server1.conn1.child1/accessible38",
+ typeName: "accessible",
+ preview: {
+ name: "New to Nightly? Let’s get started.",
+ role: "buttonmenu",
+ isConnected: true,
+ },
+});
+
+stubs.set("NoName", {
+ actor: "server1.conn1.child1/accessible93",
+ typeName: "accessible",
+ preview: {
+ name: null,
+ role: "text container",
+ isConnected: true,
+ },
+});
+
+stubs.set("NoPreview", {
+ actor: "server1.conn1.child1/accessible93",
+ typeName: "accessible",
+});
+
+stubs.set("DisconnectedAccessible", {
+ actor: null,
+ typeName: "accessible",
+ preview: {
+ name: null,
+ role: "section",
+ isConnected: false,
+ },
+});
+
+const name = "a".repeat(1000);
+stubs.set("AccessibleWithLongName", {
+ actor: "server1.conn1.child1/accessible98",
+ typeName: "accessible",
+ preview: {
+ name,
+ role: "text leaf",
+ isConnected: true,
+ },
+});
+
+stubs.set("PushButton", {
+ actor: "server1.conn1.child1/accessible157",
+ typeName: "accessible",
+ preview: {
+ name: "Search",
+ role: "pushbutton",
+ isConnected: true,
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/accessor.js b/devtools/client/shared/components/test/node/stubs/reps/accessor.js
new file mode 100644
index 0000000000..cee4a836ce
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/accessor.js
@@ -0,0 +1,85 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+
+stubs.set("getter", {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server2.conn1.child1/obj106",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get x",
+ displayName: "get x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+ set: {
+ type: "undefined",
+ },
+});
+
+stubs.set("setter", {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "undefined",
+ },
+ set: {
+ type: "object",
+ actor: "server2.conn1.child1/obj116",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "set x",
+ displayName: "set x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+});
+
+stubs.set("getter setter", {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server2.conn1.child1/obj127",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get x",
+ displayName: "get x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+ set: {
+ type: "object",
+ actor: "server2.conn1.child1/obj128",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "set x",
+ displayName: "set x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+});
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/attribute.js b/devtools/client/shared/components/test/node/stubs/reps/attribute.js
new file mode 100644
index 0000000000..d820da07f7
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/attribute.js
@@ -0,0 +1,36 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Attribute`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal12884901889/obj24",
+ "class": "Attr",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "DOMNode",
+ "nodeType": 2,
+ "nodeName": "class",
+ "isConnected": false,
+ "value": "autocomplete-suggestions"
+ },
+ "contentDomReference": {
+ "browsingContextId": 51,
+ "id": 0.4985715593006155
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal12884901889/obj24"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/big-int.js b/devtools/client/shared/components/test/node/stubs/reps/big-int.js
new file mode 100644
index 0000000000..423145359b
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/big-int.js
@@ -0,0 +1,196 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("1n", {
+ type: "BigInt",
+ text: "1",
+});
+
+stubs.set("-2n", {
+ type: "BigInt",
+ text: "-2",
+});
+
+stubs.set("0n", {
+ type: "BigInt",
+ text: "0",
+});
+
+stubs.set("[1n,-2n,0n]", {
+ type: "object",
+ actor: "server1.conn15.child1/obj27",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ items: [
+ {
+ type: "BigInt",
+ text: "1",
+ },
+ {
+ type: "BigInt",
+ text: "-2",
+ },
+ {
+ type: "BigInt",
+ text: "0",
+ },
+ ],
+ },
+});
+
+stubs.set("new Set([1n,-2n,0n])", {
+ type: "object",
+ actor: "server1.conn15.child1/obj29",
+ class: "Set",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ items: [
+ {
+ type: "BigInt",
+ text: "1",
+ },
+ {
+ type: "BigInt",
+ text: "-2",
+ },
+ {
+ type: "BigInt",
+ text: "0",
+ },
+ ],
+ },
+});
+
+stubs.set("new Map([ [1n, -1n], [-2n, 0n], [0n, -2n]])", {
+ type: "object",
+ actor: "server1.conn15.child1/obj32",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 3,
+ entries: [
+ [
+ {
+ type: "BigInt",
+ text: "1",
+ },
+ {
+ type: "BigInt",
+ text: "-1",
+ },
+ ],
+ [
+ {
+ type: "BigInt",
+ text: "-2",
+ },
+ {
+ type: "BigInt",
+ text: "0",
+ },
+ ],
+ [
+ {
+ type: "BigInt",
+ text: "0",
+ },
+ {
+ type: "BigInt",
+ text: "-2",
+ },
+ ],
+ ],
+ },
+});
+
+stubs.set("({simple: 1n, negative: -2n, zero: 0n})", {
+ type: "object",
+ actor: "server1.conn15.child1/obj34",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 3,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ simple: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "BigInt",
+ text: "1",
+ },
+ },
+ negative: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "BigInt",
+ text: "-2",
+ },
+ },
+ zero: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "BigInt",
+ text: "0",
+ },
+ },
+ },
+ ownSymbols: [],
+ ownPropertiesLength: 3,
+ ownSymbolsLength: 0,
+ safeGetterValues: {},
+ },
+});
+
+stubs.set("Promise.resolve(1n)", {
+ type: "object",
+ actor: "server1.conn15.child1/obj36",
+ class: "Promise",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "fulfilled",
+ },
+ "<value>": {
+ value: {
+ type: "BigInt",
+ text: "1",
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/browser_dummy.js b/devtools/client/shared/components/test/node/stubs/reps/browser_dummy.js
new file mode 100644
index 0000000000..8a9353cd7e
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/browser_dummy.js
@@ -0,0 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This file is a fake test so we can have support files in the stubs.ini, which are then
+// referenced as support files in the webconsole mochitest ini file.
+
+"use strict";
+
+add_task(function() {
+ ok(true, "this is not a test");
+});
diff --git a/devtools/client/shared/components/test/node/stubs/reps/comment-node.js b/devtools/client/shared/components/test/node/stubs/reps/comment-node.js
new file mode 100644
index 0000000000..5ee0970283
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/comment-node.js
@@ -0,0 +1,36 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Comment`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal4294967299/obj26",
+ "class": "Comment",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "DOMNode",
+ "nodeType": 8,
+ "nodeName": "#comment",
+ "isConnected": false,
+ "textContent": "test\nand test\nand test\nand test\nand test\nand test\nand test"
+ },
+ "contentDomReference": {
+ "browsingContextId": 51,
+ "id": 0.7876406289746626
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal4294967299/obj26"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/date-time.js b/devtools/client/shared/components/test/node/stubs/reps/date-time.js
new file mode 100644
index 0000000000..9027397104
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/date-time.js
@@ -0,0 +1,47 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`DateTime`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal4294967299/obj28",
+ "class": "Date",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "timestamp": 1459372644859
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal4294967299/obj28"
+});
+
+stubs.set(`InvalidDateTime`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal4294967299/obj30",
+ "class": "Date",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "timestamp": {
+ "type": "NaN"
+ }
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal4294967299/obj30"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/document-type.js b/devtools/client/shared/components/test/node/stubs/reps/document-type.js
new file mode 100644
index 0000000000..a499670b58
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/document-type.js
@@ -0,0 +1,40 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("html", {
+ type: "object",
+ actor: "server1.conn7.child1/obj195",
+ class: "DocumentType",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 10,
+ nodeName: "html",
+ isConnected: true,
+ },
+});
+
+stubs.set("unnamed", {
+ type: "object",
+ actor: "server1.conn7.child1/obj195",
+ class: "DocumentType",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 10,
+ nodeName: "",
+ isConnected: true,
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/document.js b/devtools/client/shared/components/test/node/stubs/reps/document.js
new file mode 100644
index 0000000000..555bbff2d1
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/document.js
@@ -0,0 +1,39 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("Document", {
+ type: "object",
+ class: "HTMLDocument",
+ actor: "server1.conn17.obj115",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 9,
+ nodeName: "#document",
+ location: "https://www.mozilla.org/en-US/firefox/new/",
+ },
+});
+
+stubs.set("Location-less Document", {
+ type: "object",
+ actor: "server1.conn6.child1/obj31",
+ class: "HTMLDocument",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 9,
+ nodeName: "#document",
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/element-node.js b/devtools/client/shared/components/test/node/stubs/reps/element-node.js
new file mode 100644
index 0000000000..b0b4369b3c
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/element-node.js
@@ -0,0 +1,292 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("BodyNode", {
+ type: "object",
+ actor: "server1.conn1.child1/obj30",
+ class: "HTMLBodyElement",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "body",
+ attributes: {
+ class: "body-class",
+ id: "body-id",
+ },
+ attributesLength: 2,
+ },
+});
+
+stubs.set("DocumentElement", {
+ type: "object",
+ actor: "server1.conn1.child1/obj40",
+ class: "HTMLHtmlElement",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "html",
+ attributes: {
+ dir: "ltr",
+ lang: "en-US",
+ },
+ attributesLength: 2,
+ },
+});
+
+stubs.set("Node", {
+ type: "object",
+ actor: "server1.conn2.child1/obj116",
+ class: "HTMLInputElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "input",
+ isConnected: true,
+ attributes: {
+ id: "newtab-customize-button",
+ dir: "ltr",
+ title: "Customize your New Tab page",
+ class: "bar baz",
+ value: "foo",
+ type: "button",
+ },
+ attributesLength: 6,
+ },
+});
+
+stubs.set("DisconnectedNode", {
+ type: "object",
+ actor: "server1.conn2.child1/obj116",
+ class: "HTMLInputElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "input",
+ isConnected: false,
+ attributes: {
+ id: "newtab-customize-button",
+ dir: "ltr",
+ title: "Customize your New Tab page",
+ class: "bar baz",
+ value: "foo",
+ type: "button",
+ },
+ attributesLength: 6,
+ },
+});
+
+stubs.set("NodeWithLeadingAndTrailingSpacesClassName", {
+ type: "object",
+ actor: "server1.conn3.child1/obj59",
+ class: "HTMLBodyElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "body",
+ attributes: {
+ id: "nightly-whatsnew",
+ class: " html-ltr ",
+ },
+ attributesLength: 2,
+ },
+});
+
+stubs.set("NodeWithSpacesInClassName", {
+ type: "object",
+ actor: "server1.conn3.child1/obj59",
+ class: "HTMLBodyElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "body",
+ attributes: {
+ class: "a b c",
+ },
+ attributesLength: 1,
+ },
+});
+
+stubs.set("NodeWithoutAttributes", {
+ type: "object",
+ actor: "server1.conn1.child1/obj32",
+ class: "HTMLParagraphElement",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "p",
+ attributes: {},
+ attributesLength: 1,
+ },
+});
+
+stubs.set("LotsOfAttributes", {
+ type: "object",
+ actor: "server1.conn2.child1/obj30",
+ class: "HTMLParagraphElement",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "p",
+ attributes: {
+ id: "lots-of-attributes",
+ a: "",
+ b: "",
+ c: "",
+ d: "",
+ e: "",
+ f: "",
+ g: "",
+ h: "",
+ i: "",
+ j: "",
+ k: "",
+ l: "",
+ m: "",
+ n: "",
+ },
+ attributesLength: 15,
+ },
+});
+
+stubs.set("SvgNode", {
+ type: "object",
+ actor: "server1.conn1.child1/obj42",
+ class: "SVGClipPathElement",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "clipPath",
+ attributes: {
+ id: "clip",
+ class: "svg-element",
+ },
+ attributesLength: 0,
+ },
+});
+
+stubs.set("SvgNodeInXHTML", {
+ type: "object",
+ actor: "server1.conn3.child1/obj34",
+ class: "SVGCircleElement",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "svg:circle",
+ attributes: {
+ class: "svg-element",
+ cx: "0",
+ cy: "0",
+ r: "5",
+ },
+ attributesLength: 3,
+ },
+});
+
+stubs.set("NodeWithLongAttribute", {
+ type: "object",
+ actor: "server1.conn1.child1/obj32",
+ class: "HTMLParagraphElement",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "p",
+ attributes: {
+ "data-test": "a".repeat(100),
+ },
+ attributesLength: 1,
+ },
+});
+
+const initialText = "a".repeat(1000);
+stubs.set("NodeWithLongStringAttribute", {
+ type: "object",
+ actor: "server1.conn1.child1/obj28",
+ class: "HTMLDivElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "div",
+ isConnected: false,
+ attributes: {
+ "data-test": {
+ type: "longString",
+ initial: initialText,
+ length: 50000,
+ actor: "server1.conn1.child1/longString29",
+ },
+ },
+ attributesLength: 1,
+ },
+});
+
+stubs.set("MarkerPseudoElement", {
+ type: "object",
+ actor: "server1.conn1.child1/obj26",
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "_moz_generated_content_marker",
+ attributes: {},
+ attributesLength: 0,
+ isMarkerPseudoElement: true,
+ },
+});
+
+stubs.set("BeforePseudoElement", {
+ type: "object",
+ actor: "server1.conn1.child1/obj27",
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "_moz_generated_content_before",
+ attributes: {},
+ attributesLength: 0,
+ isBeforePseudoElement: true,
+ },
+});
+
+stubs.set("AfterPseudoElement", {
+ type: "object",
+ actor: "server1.conn1.child1/obj28",
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "_moz_generated_content_after",
+ attributes: {},
+ attributesLength: 0,
+ isAfterPseudoElement: true,
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/error.js b/devtools/client/shared/components/test/node/stubs/reps/error.js
new file mode 100644
index 0000000000..ea4b7ac4ee
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/error.js
@@ -0,0 +1,396 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("SimpleError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1020",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "Error message",
+ stack: "@debugger eval code:1:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 1,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("MultilineStackError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1021",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "bar",
+ stack:
+ "errorBar@debugger eval code:6:15\n" +
+ "errorFoo@debugger eval code:3:3\n" +
+ "@debugger eval code:8:1\n",
+ fileName: "debugger eval code",
+ lineNumber: 6,
+ columnNumber: 15,
+ },
+});
+
+stubs.set("ErrorWithoutStacktrace", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1020",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "Error message",
+ },
+});
+
+stubs.set("EvalError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1022",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "EvalError",
+ message: "EvalError message",
+ stack: "@debugger eval code:10:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 10,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("InternalError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1023",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "InternalError",
+ message: "InternalError message",
+ stack: "@debugger eval code:11:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 11,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("RangeError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1024",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "RangeError",
+ message: "RangeError message",
+ stack: "@debugger eval code:12:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 12,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("ReferenceError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1025",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "ReferenceError",
+ message: "ReferenceError message",
+ stack: "@debugger eval code:13:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 13,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("SyntaxError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1026",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "SyntaxError",
+ message: "SyntaxError message",
+ stack: "@debugger eval code:14:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 14,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("TypeError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1027",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "TypeError",
+ message: "TypeError message",
+ stack: "@debugger eval code:15:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 15,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("URIError", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1028",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "URIError",
+ message: "URIError message",
+ stack: "@debugger eval code:16:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 16,
+ columnNumber: 13,
+ },
+});
+
+/**
+ * Example code:
+ * try {
+ * var foo = document.querySelector("foo;()bar!");
+ * } catch (ex) {
+ * ex;
+ * }
+ */
+stubs.set("DOMException", {
+ type: "object",
+ actor: "server2.conn2.child3/obj32",
+ class: "DOMException",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMException",
+ name: "SyntaxError",
+ message: "'foo;()bar!' is not a valid selector",
+ code: 12,
+ result: 2152923148,
+ filename: "debugger eval code",
+ lineNumber: 1,
+ columnNumber: 0,
+ },
+});
+
+stubs.set("base-loader Error", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1020",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "Error message",
+ stack:
+ "onPacket@resource://devtools/shared/base-loader.sys.mjs -> resource://devtools/client/debugger-client.js:856:9\n" +
+ "send/<@resource://devtools/shared/base-loader.sys.mjs -> resource://devtools/shared/transport/transport.js:569:13\n" +
+ "exports.makeInfallible/<@resource://devtools/shared/base-loader.sys.mjs -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14\n" +
+ "exports.makeInfallible/<@resource://devtools/shared/base-loader.sys.mjs -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:109:14\n",
+ fileName: "debugger-client.js",
+ lineNumber: 859,
+ columnNumber: 9,
+ },
+});
+
+stubs.set("longString stack Error", {
+ type: "object",
+ actor: "server1.conn2.child1/obj33",
+ class: "Error",
+ isError: true,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "",
+ stack: {
+ type: "longString",
+ initial:
+ "NgForOf.prototype.ngOnChanges@webpack-internal:///./node_modules/@angular/common/esm5/common.js:2656:27\n checkAndUpdateDirectiveInline@webpack-internal:///./node_modules/@angular/core/esm5/core.js:12581:9\n checkAndUpdateNodeInline@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14109:20\n checkAndUpdateNode@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14052:16\n debugCheckAndUpdateNode@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14945:55\n debugCheckDirectivesFn@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14886:13\n View_MetaTableComponent_6/<@ng:///AppModule/MetaTableComponent.ngfactory.js:98:5\n debugUpdateDirectives@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14871:12\n checkAndUpdateView@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14018:5\n callViewAction@webpack-internal:///./node_modules/@angular/core/esm5/core.js:14369:21\n execEmbeddedViewsAction@webpack-internal:///./node_modules/@an",
+ length: 11907,
+ actor: "server1.conn2.child1/longString31",
+ },
+ fileName: "debugger eval code",
+ lineNumber: 1,
+ columnNumber: 5,
+ },
+});
+
+stubs.set("longString stack Error - cut-off location", {
+ type: "object",
+ actor: "server1.conn1.child1/obj33",
+ class: "Error",
+ isError: true,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 6,
+ preview: {
+ kind: "Error",
+ name: "InternalError",
+ message: "too much recursion",
+ stack: {
+ type: "longString",
+ initial:
+ "execute/AppComponent</AppComponent.prototype.doStuff@https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:32:1\nexecute/AppComponent</AppComponent.prototype.doStuff@https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21\nexecute/AppComponent</AppComponent.prototype.doStuff@https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21\nexecute/AppComponent</AppComponent.prototype.doStuff@https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21\nexecute/AppComponent</AppComponent.prototype.doStuff@https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21\nexecute/AppComponent</AppComponent.prototype.doStuff@https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21\nexecute/AppComponent</AppComponent.prototype.doStuff@https://angular-3eqab4.stackblitz.io/tmp/appfiles/src/app/app.component.ts:33:21\nexecute/AppComponent</AppComponent.prototype.doStuff@https://an",
+ length: 17151,
+ actor: "server1.conn1.child1/longString27",
+ },
+ fileName:
+ "https://c.staticblitz.com/assets/engineblock-bc7b07e99ec5c6739c766b4898e4cff5acfddc137ccb7218377069c32731f1d0.js line 1 > eval",
+ lineNumber: 32,
+ columnNumber: 1,
+ },
+});
+
+stubs.set("Error with V8-like stack", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1020",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "BOOM",
+ stack: "Error: BOOM\ngetAccount@http://moz.com/script.js:1:2",
+ fileName: "http://moz.com/script.js:1:2",
+ lineNumber: 1,
+ columnNumber: 2,
+ },
+});
+
+stubs.set("Error with invalid stack", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1020",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "bad stack",
+ stack: "bar\nbaz\nfoo\n\n\n\n\n\n\n",
+ fileName: "http://moz.com/script.js:1:2",
+ lineNumber: 1,
+ columnNumber: 2,
+ },
+});
+
+stubs.set("Error with undefined-grip stack", {
+ type: "object",
+ actor: "server0.conn0.child1/obj88",
+ class: "Error",
+ isError: true,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "InternalError",
+ message: "too much recursion",
+ stack: {
+ type: "undefined",
+ },
+ fileName: "debugger eval code",
+ lineNumber: 13,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("Error with undefined-grip name", {
+ type: "object",
+ actor: "server0.conn0.child1/obj88",
+ class: "Error",
+ isError: true,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: {
+ type: "undefined",
+ },
+ message: "too much recursion",
+ stack: "@debugger eval code:16:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 13,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("Error with undefined-grip message", {
+ type: "object",
+ actor: "server0.conn0.child1/obj88",
+ class: "Error",
+ isError: true,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ message: { type: "undefined" },
+ stack: "@debugger eval code:16:13\n",
+ fileName: "debugger eval code",
+ lineNumber: 13,
+ columnNumber: 13,
+ },
+});
+
+stubs.set("Error with stack having frames with multiple @", {
+ type: "object",
+ actor: "server1.conn1.child1/obj1021",
+ class: "Error",
+ isError: true,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Error",
+ name: "Error",
+ message: "bar",
+ stack:
+ "errorBar@https://example.com/turbo/from-npm.js@0.8.26/dist/from-npm.js:814:31\n" +
+ "errorFoo@https://example.com/turbo/from-npm.js@0.8.26/dist/from-npm.js:815:31\n" +
+ "@https://example.com/turbo/from-npm.js@0.8.26/dist/from-npm.js:816:31\n",
+ fileName: "from-npm.js",
+ lineNumber: 6,
+ columnNumber: 15,
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/event.js b/devtools/client/shared/components/test/node/stubs/reps/event.js
new file mode 100644
index 0000000000..c0c49c5e42
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/event.js
@@ -0,0 +1,269 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+
+stubs.set("testEvent", {
+ type: "object",
+ class: "Event",
+ actor: "server1.conn23.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "DOMEvent",
+ type: "beforeprint",
+ properties: {
+ isTrusted: true,
+ currentTarget: {
+ type: "object",
+ class: "Window",
+ actor: "server1.conn23.obj37",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 760,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "http://example.com",
+ },
+ },
+ eventPhase: 2,
+ bubbles: false,
+ cancelable: false,
+ defaultPrevented: false,
+ timeStamp: 1466780008434005,
+ originalTarget: {
+ type: "object",
+ class: "Window",
+ actor: "server1.conn23.obj38",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 760,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "http://example.com",
+ },
+ },
+ explicitOriginalTarget: {
+ type: "object",
+ class: "Window",
+ actor: "server1.conn23.obj39",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 760,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "http://example.com",
+ },
+ },
+ NONE: 0,
+ },
+ target: {
+ type: "object",
+ class: "Window",
+ actor: "server1.conn23.obj36",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 760,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "http://example.com",
+ },
+ },
+ },
+});
+
+stubs.set("testMouseEvent", {
+ type: "object",
+ class: "MouseEvent",
+ actor: "server1.conn20.obj39",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "DOMEvent",
+ type: "click",
+ properties: {
+ buttons: 0,
+ clientX: 62,
+ clientY: 18,
+ layerX: 0,
+ layerY: 0,
+ },
+ target: {
+ type: "object",
+ class: "HTMLDivElement",
+ actor: "server1.conn20.obj40",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "div",
+ isConnected: true,
+ attributes: {
+ id: "test",
+ },
+ attributesLength: 1,
+ },
+ },
+ },
+});
+
+stubs.set("testKeyboardEvent", {
+ type: "object",
+ class: "KeyboardEvent",
+ actor: "server1.conn21.obj49",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "DOMEvent",
+ type: "keyup",
+ properties: {
+ key: "Control",
+ charCode: 0,
+ keyCode: 17,
+ },
+ target: {
+ type: "object",
+ class: "HTMLBodyElement",
+ actor: "server1.conn21.obj50",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "body",
+ attributes: {},
+ attributesLength: 0,
+ },
+ },
+ eventKind: "key",
+ modifiers: [],
+ },
+});
+
+stubs.set("testKeyboardEventWithModifiers", {
+ type: "object",
+ class: "KeyboardEvent",
+ actor: "server1.conn21.obj49",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "DOMEvent",
+ type: "keyup",
+ properties: {
+ key: "M",
+ charCode: 0,
+ keyCode: 77,
+ },
+ target: {
+ type: "object",
+ class: "HTMLBodyElement",
+ actor: "server1.conn21.obj50",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "body",
+ attributes: {},
+ attributesLength: 0,
+ },
+ },
+ eventKind: "key",
+ modifiers: ["Meta", "Shift"],
+ },
+});
+
+stubs.set("testMessageEvent", {
+ type: "object",
+ class: "MessageEvent",
+ actor: "server1.conn3.obj34",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "DOMEvent",
+ type: "message",
+ properties: {
+ isTrusted: false,
+ data: "test data",
+ origin: "null",
+ lastEventId: "",
+ source: {
+ type: "object",
+ class: "Window",
+ actor: "server1.conn3.obj36",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 760,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "",
+ },
+ },
+ ports: {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn3.obj37",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ },
+ currentTarget: {
+ type: "object",
+ class: "Window",
+ actor: "server1.conn3.obj38",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 760,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "",
+ },
+ },
+ eventPhase: 2,
+ bubbles: false,
+ cancelable: false,
+ },
+ target: {
+ type: "object",
+ class: "Window",
+ actor: "server1.conn3.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 760,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "http://example.com",
+ },
+ },
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/failure.js b/devtools/client/shared/components/test/node/stubs/reps/failure.js
new file mode 100644
index 0000000000..f839d5eab5
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/failure.js
@@ -0,0 +1,21 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("Failure", {
+ type: "object",
+ class: "RegExp",
+ actor: "server1.conn22.obj39",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ get displayString() {
+ throw new Error("failure");
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/function.js b/devtools/client/shared/components/test/node/stubs/reps/function.js
new file mode 100644
index 0000000000..a0ba6ddf8d
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/function.js
@@ -0,0 +1,227 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("Named", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn6.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: false,
+ isGenerator: false,
+ name: "testName",
+ displayName: "testName",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("UserNamed", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn6.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: false,
+ isGenerator: false,
+ name: "testName",
+ userDisplayName: "testUserName",
+ displayName: "testName",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("VarNamed", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj41",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: false,
+ isGenerator: false,
+ displayName: "testVarName",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("Anon", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj45",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: false,
+ isGenerator: false,
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("LongName", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj67",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: false,
+ isGenerator: false,
+ name:
+ "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
+ "ooooooooooooooooooooooooooooooooooong",
+ displayName:
+ "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooo" +
+ "oooooooooooooooooooooooooooooooooooooooooong",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("AsyncFunction", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj45",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: true,
+ isGenerator: false,
+ name: "waitUntil2017",
+ displayName: "waitUntil2017",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("AnonAsyncFunction", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj45",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: true,
+ isGenerator: false,
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("GeneratorFunction", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj45",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: false,
+ isGenerator: true,
+ name: "fib",
+ displayName: "fib",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("AnonGeneratorFunction", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj45",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAsync: false,
+ isGenerator: true,
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("getRandom", {
+ type: "object",
+ actor: "server1.conn7.child1/obj984",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 3,
+ name: "getRandom",
+ displayName: "getRandom",
+ location: {
+ url: "https://nchevobbe.github.io/demo/console-test-app.html",
+ line: 314,
+ },
+});
+
+stubs.set("EvaledInDebuggerFunction", {
+ type: "object",
+ actor: "server1.conn2.child1/obj29",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 3,
+ name: "evaledInDebugger",
+ displayName: "evaledInDebugger",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("ObjectProperty", {
+ type: "object",
+ class: "Function",
+ actor: "server1.conn7.obj45",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isAync: false,
+ isGenerator: false,
+ name: "$",
+ displayName: "jQuery",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+});
+
+stubs.set("EmptyClass", {
+ actor: "server0.conn0.child1/obj27",
+ class: "Function",
+ displayName: "EmptyClass",
+ extensible: true,
+ frozen: false,
+ isAsync: false,
+ isClassConstructor: true,
+ isGenerator: false,
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ name: "EmptyClass",
+ parameterNames: [],
+ sealed: false,
+ type: "object",
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/grip-array.js b/devtools/client/shared/components/test/node/stubs/reps/grip-array.js
new file mode 100644
index 0000000000..93b07fa136
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/grip-array.js
@@ -0,0 +1,1087 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ maxLengthMap,
+} = require("resource://devtools/client/shared/components/reps/reps/grip-array.js");
+const stubs = new Map();
+
+stubs.set("testBasic", {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn0.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "ArrayLike",
+ length: 0,
+ items: [],
+ },
+});
+
+stubs.set("DOMTokenList", {
+ type: "object",
+ actor: "server2.conn4.child12/obj39",
+ class: "DOMTokenList",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ArrayLike",
+ length: 0,
+ items: [],
+ },
+});
+
+stubs.set("testMaxProps", {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn1.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ items: [
+ 1,
+ "foo",
+ {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn1.obj36",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ },
+ ],
+ },
+});
+
+stubs.set("testMoreThanShortMaxProps", {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn1.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: maxLengthMap.get(MODE.SHORT) + 1,
+ preview: {
+ kind: "ArrayLike",
+ length: maxLengthMap.get(MODE.SHORT) + 1,
+ items: new Array(maxLengthMap.get(MODE.SHORT) + 1).fill("test string"),
+ },
+});
+
+stubs.set("testMoreThanLongMaxProps", {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn1.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: maxLengthMap.get(MODE.LONG) + 1,
+ items: new Array(maxLengthMap.get(MODE.LONG) + 1).fill("test string"),
+ },
+});
+
+stubs.set("testPreviewLimit", {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn1.obj31",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 12,
+ preview: {
+ kind: "ArrayLike",
+ length: 11,
+ items: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ },
+});
+
+stubs.set("testRecursiveArray", {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn3.obj42",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ preview: {
+ kind: "ArrayLike",
+ length: 1,
+ items: [
+ {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn3.obj43",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ preview: {
+ kind: "ArrayLike",
+ length: 1,
+ },
+ },
+ ],
+ },
+});
+
+stubs.set("testNamedNodeMap", {
+ type: "object",
+ class: "NamedNodeMap",
+ actor: "server1.conn3.obj42",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 6,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ items: [
+ {
+ type: "object",
+ class: "Attr",
+ actor: "server1.conn3.obj43",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 2,
+ nodeName: "class",
+ value: "myclass",
+ },
+ },
+ {
+ type: "object",
+ class: "Attr",
+ actor: "server1.conn3.obj44",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 2,
+ nodeName: "cellpadding",
+ value: "7",
+ },
+ },
+ {
+ type: "object",
+ class: "Attr",
+ actor: "server1.conn3.obj44",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 2,
+ nodeName: "border",
+ value: "3",
+ },
+ },
+ ],
+ },
+});
+
+stubs.set("testNodeList", {
+ type: "object",
+ actor: "server1.conn1.child1/obj51",
+ class: "NodeList",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 3,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ items: [
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj52",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj53",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-2",
+ class: "btn btn-err",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj54",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-3",
+ class: "btn btn-count",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ },
+});
+
+stubs.set("testDisconnectedNodeList", {
+ type: "object",
+ actor: "server1.conn1.child1/obj51",
+ class: "NodeList",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 3,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ items: [
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj52",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: false,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj53",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: false,
+ attributes: {
+ id: "btn-2",
+ class: "btn btn-err",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj54",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: false,
+ attributes: {
+ id: "btn-3",
+ class: "btn btn-count",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ },
+});
+
+stubs.set("testDocumentFragment", {
+ type: "object",
+ actor: "server1.conn1.child1/obj45",
+ class: "DocumentFragment",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 11,
+ nodeName: "#document-fragment",
+ childNodesLength: 5,
+ childNodes: [
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj46",
+ class: "HTMLLIElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "li",
+ attributes: {
+ id: "li-0",
+ class: "list-element",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj47",
+ class: "HTMLLIElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "li",
+ attributes: {
+ id: "li-1",
+ class: "list-element",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj48",
+ class: "HTMLLIElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "li",
+ attributes: {
+ id: "li-2",
+ class: "list-element",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj49",
+ class: "HTMLLIElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "li",
+ attributes: {
+ id: "li-3",
+ class: "list-element",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj50",
+ class: "HTMLLIElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "li",
+ attributes: {
+ id: "li-4",
+ class: "list-element",
+ },
+ attributesLength: 2,
+ },
+ },
+ ],
+ },
+});
+
+stubs.set("Array(5)", {
+ type: "object",
+ actor: "server1.conn4.child1/obj33",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "ArrayLike",
+ length: 5,
+ items: [null, null, null, null, null],
+ },
+});
+
+stubs.set("[,1,2,3]", {
+ type: "object",
+ actor: "server1.conn4.child1/obj35",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 4,
+ items: [null, 1, 2, 3],
+ },
+});
+
+stubs.set("[,,,3,4,5]", {
+ type: "object",
+ actor: "server1.conn4.child1/obj37",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 6,
+ items: [null, null, null, 3, 4, 5],
+ },
+});
+
+stubs.set("[0,1,,3,4,5]", {
+ type: "object",
+ actor: "server1.conn4.child1/obj65",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 6,
+ preview: {
+ kind: "ArrayLike",
+ length: 6,
+ items: [0, 1, null, 3, 4, 5],
+ },
+});
+
+stubs.set("[0,1,,,,5]", {
+ type: "object",
+ actor: "server1.conn4.child1/obj83",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 6,
+ items: [0, 1, null, null, null, 5],
+ },
+});
+
+stubs.set("[0,,2,,4,5]", {
+ type: "object",
+ actor: "server1.conn4.child1/obj85",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 5,
+ preview: {
+ kind: "ArrayLike",
+ length: 6,
+ items: [0, null, 2, null, 4, 5],
+ },
+});
+
+stubs.set("[0,,,3,,,,7,8]", {
+ type: "object",
+ actor: "server1.conn4.child1/obj87",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 5,
+ preview: {
+ kind: "ArrayLike",
+ length: 9,
+ items: [0, null, null, 3, null, null, null, 7, 8],
+ },
+});
+
+stubs.set("[0,1,2,3,4,,]", {
+ type: "object",
+ actor: "server1.conn4.child1/obj89",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 6,
+ preview: {
+ kind: "ArrayLike",
+ length: 6,
+ items: [0, 1, 2, 3, 4, null],
+ },
+});
+
+stubs.set("[0,1,2,,,,]", {
+ type: "object",
+ actor: "server1.conn13.child1/obj88",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 6,
+ items: [0, 1, 2, null, null, null],
+ },
+});
+
+// We can have cases where we don't have the array items in the preview,
+// (e.g. in the packet for `Promise.resolve([1, 2, 3])`), but we have the
+// length of the array.
+stubs.set("testItemsNotInPreview", {
+ type: "object",
+ actor: "server2.conn0.child1/obj135",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ },
+});
+
+stubs.set("new Set([1,2,3,4])", {
+ type: "object",
+ actor: "server2.conn8.child18/obj30",
+ class: "Set",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ArrayLike",
+ length: 4,
+ items: [1, 2, 3, 4],
+ },
+});
+
+stubs.set("new Set([0,1,2,…,19])", {
+ type: "object",
+ actor: "server2.conn8.child18/obj42",
+ class: "Set",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ArrayLike",
+ length: 20,
+ items: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ },
+});
+
+stubs.set("new WeakSet(document.querySelectorAll('button:nth-child(3n)'))", {
+ type: "object",
+ actor: "server2.conn11.child18/obj107",
+ class: "WeakSet",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ArrayLike",
+ length: 4,
+ items: [
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj108",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "g",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj109",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "E",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj110",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "l",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj111",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "r",
+ },
+ attributesLength: 2,
+ },
+ },
+ ],
+ },
+});
+
+stubs.set("new WeakSet(document.querySelectorAll('div, button'))", {
+ type: "object",
+ actor: "server2.conn11.child18/obj172",
+ class: "WeakSet",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ArrayLike",
+ length: 12,
+ items: [
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj173",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "L",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj174",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "E",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj175",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "t",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj176",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "G",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj177",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "g",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj178",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "e",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj179",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "T",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj180",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "l",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj181",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "C",
+ },
+ attributesLength: 2,
+ },
+ },
+ {
+ type: "object",
+ actor: "server2.conn11.child18/obj182",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ type: "button",
+ "data-key": "c",
+ },
+ attributesLength: 2,
+ },
+ },
+ ],
+ },
+});
+
+stubs.set('["http://example.com/abcdefghijabcdefghij some other text"]', {
+ type: "object",
+ actor: "server2.conn3.child17/obj37",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ preview: {
+ kind: "ArrayLike",
+ length: 1,
+ items: ["http://example.com/abcdefghijabcdefghij some other text"],
+ },
+});
+
+stubs.set("Array(234)", {
+ type: "object",
+ actor: "server4.conn2.child19/obj668",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 235,
+ preview: {
+ kind: "ArrayLike",
+ length: 234,
+ items: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ },
+});
+
+stubs.set("Array(23456)", {
+ type: "object",
+ actor: "server4.conn2.child19/obj668",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 23457,
+ preview: {
+ kind: "ArrayLike",
+ length: 23456,
+ items: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+ },
+});
+
+stubs.set("TestArrayWithGetter", {
+ type: "object",
+ actor: "server0.conn0.windowGlobal13/obj21",
+ class: "Array",
+ ownPropertyLength: 2,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isError: false,
+ preview: {
+ kind: "ArrayLike",
+ length: 1,
+ items: [{
+ type: "accessor",
+ get: {
+ type: "object",
+ actor: "server0.conn0.windowGlobal13/obj22",
+ }
+ }]
+ }
+});
+
+stubs.set("TestArrayWithSetter", {
+ type: "object",
+ actor: "server0.conn0.windowGlobal13/obj24",
+ class: "Array",
+ ownPropertyLength: 2,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isError: false,
+ preview: {
+ kind: "ArrayLike",
+ length: 1,
+ items: [{
+ type: "accessor",
+ set: {
+ type: "object",
+ actor: "server0.conn0.windowGlobal13/obj25",
+ }
+ }]
+ }
+});
+
+stubs.set("TestArrayWithGetterAndSetter", {
+ type: "object",
+ actor: "server0.conn0.windowGlobal13/obj28",
+ class: "Array",
+ ownPropertyLength: 2,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ isError: false,
+ preview: {
+ kind: "ArrayLike",
+ length: 1,
+ items: [{
+ type: "accessor",
+ get: {
+ type: "object",
+ actor: "server0.conn0.windowGlobal13/obj29",
+ },
+ set: {
+ type: "object",
+ actor: "server0.conn0.windowGlobal13/obj30",
+ }
+ }]
+ }
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/grip-entry.js b/devtools/client/shared/components/test/node/stubs/reps/grip-entry.js
new file mode 100644
index 0000000000..6c21389845
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/grip-entry.js
@@ -0,0 +1,16 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("A → 0", {
+ type: "mapEntry",
+ preview: {
+ key: "A",
+ value: 0,
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/grip-map.js b/devtools/client/shared/components/test/node/stubs/reps/grip-map.js
new file mode 100644
index 0000000000..8c2af0956f
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/grip-map.js
@@ -0,0 +1,908 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ maxLengthMap,
+} = require("resource://devtools/client/shared/components/reps/reps/grip-map.js");
+
+const stubs = new Map();
+
+stubs.set("testEmptyMap", {
+ type: "object",
+ actor: "server1.conn1.child1/obj97",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 0,
+ entries: [],
+ },
+});
+
+stubs.set("testSymbolKeyedMap", {
+ type: "object",
+ actor: "server1.conn1.child1/obj118",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 2,
+ entries: [
+ [
+ {
+ type: "symbol",
+ name: "a",
+ },
+ "value-a",
+ ],
+ [
+ {
+ type: "symbol",
+ name: "b",
+ },
+ "value-b",
+ ],
+ ],
+ },
+});
+
+stubs.set("testWeakMap", {
+ type: "object",
+ actor: "server1.conn1.child1/obj115",
+ class: "WeakMap",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 1,
+ entries: [
+ [
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj116",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ },
+ "value-a",
+ ],
+ ],
+ },
+});
+
+stubs.set("testMaxEntries", {
+ type: "object",
+ actor: "server1.conn1.child1/obj109",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 3,
+ entries: [
+ ["key-a", "value-a"],
+ ["key-b", "value-b"],
+ ["key-c", "value-c"],
+ ],
+ },
+});
+
+stubs.set("testMoreThanMaxEntries", {
+ type: "object",
+ class: "Map",
+ actor: "server1.conn0.obj332",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: maxLengthMap.get(MODE.LONG) + 1,
+ entries: Array.from({ length: 10 }).map((_, i) => {
+ return [`key-${i}`, `value-${i}`];
+ }),
+ },
+});
+
+stubs.set("testUninterestingEntries", {
+ type: "object",
+ actor: "server1.conn1.child1/obj111",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 4,
+ entries: [
+ [
+ "key-a",
+ {
+ type: "null",
+ },
+ ],
+ [
+ "key-b",
+ {
+ type: "undefined",
+ },
+ ],
+ ["key-c", "value-c"],
+ ["key-d", 4],
+ ],
+ },
+});
+
+stubs.set("testDisconnectedNodeValuedMap", {
+ type: "object",
+ actor: "server1.conn1.child1/obj213",
+ class: "Map",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 3,
+ entries: [
+ [
+ "item-0",
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj214",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: false,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ [
+ "item-1",
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj215",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: false,
+ attributes: {
+ id: "btn-2",
+ class: "btn btn-err",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ [
+ "item-2",
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj216",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: false,
+ attributes: {
+ id: "btn-3",
+ class: "btn btn-count",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ ],
+ },
+});
+
+stubs.set("testNodeValuedMap", {
+ type: "object",
+ actor: "server1.conn1.child1/obj213",
+ class: "Map",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 3,
+ entries: [
+ [
+ "item-0",
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj214",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ [
+ "item-1",
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj215",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-2",
+ class: "btn btn-err",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ [
+ "item-2",
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj216",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-3",
+ class: "btn btn-count",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ ],
+ ],
+ },
+});
+
+stubs.set("testNodeKeyedMap", {
+ type: "object",
+ actor: "server1.conn1.child1/obj223",
+ class: "WeakMap",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 3,
+ entries: [
+ [
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj224",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ "item-0",
+ ],
+ [
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj225",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-3",
+ class: "btn btn-count",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ "item-2",
+ ],
+ [
+ {
+ type: "object",
+ actor: "server1.conn1.child1/obj226",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-2",
+ class: "btn btn-err",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ "item-1",
+ ],
+ ],
+ },
+});
+
+stubs.set("20-entries Map", {
+ type: "object",
+ actor: "server4.conn2.child19/obj777",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 20,
+ entries: [
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj778",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "1",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj779",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "2",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj780",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "3",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj781",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "4",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj782",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "5",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj783",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "6",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj784",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "7",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj785",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "8",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj786",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "9",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj787",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "10",
+ },
+ ],
+ ],
+ },
+});
+
+stubs.set("234-entries Map", {
+ type: "object",
+ actor: "server4.conn2.child19/obj789",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 234,
+ entries: [
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj790",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "1",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj791",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "2",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj792",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "3",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj793",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "4",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj794",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "5",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj795",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "6",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj796",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "7",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj797",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "8",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj798",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "9",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj799",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "10",
+ },
+ ],
+ ],
+ },
+});
+
+stubs.set("23456-entries Map", {
+ type: "object",
+ actor: "server4.conn2.child19/obj803",
+ class: "Map",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "MapLike",
+ size: 23456,
+ entries: [
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj804",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "1",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj805",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "2",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj806",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "3",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj807",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "4",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj808",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "5",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj809",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "6",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj810",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "7",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj811",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "8",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj812",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "9",
+ },
+ ],
+ [
+ {
+ type: "object",
+ actor: "server4.conn2.child19/obj813",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ {
+ type: "symbol",
+ name: "10",
+ },
+ ],
+ ],
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/grip.js b/devtools/client/shared/components/test/node/stubs/reps/grip.js
new file mode 100644
index 0000000000..69a24013ef
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/grip.js
@@ -0,0 +1,1057 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ MODE,
+} = require("resource://devtools/client/shared/components/reps/reps/constants.js");
+const {
+ maxLengthMap,
+} = require("resource://devtools/client/shared/components/reps/reps/grip.js");
+
+const stubs = new Map();
+
+stubs.set("testBasic", {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj304",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {},
+ },
+});
+
+stubs.set("testMaxProps", {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj337",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 3,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ a: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "a",
+ },
+ b: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "b",
+ },
+ c: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "c",
+ },
+ },
+ ownPropertiesLength: 3,
+ safeGetterValues: {},
+ },
+});
+
+const longModeMaxLength = maxLengthMap.get(MODE.LONG);
+
+stubs.set("testMoreThanMaxProps", {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj332",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: longModeMaxLength + 1,
+ preview: {
+ kind: "Object",
+ ownProperties: Array.from({ length: longModeMaxLength }).reduce(
+ (res, item, index) => ({
+ ...res,
+ [`p${index}`]: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: index.toString(),
+ },
+ }),
+ {}
+ ),
+ ownPropertiesLength: longModeMaxLength + 1,
+ safeGetterValues: {},
+ },
+});
+
+stubs.set("testUninterestingProps", {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj342",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ a: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "undefined",
+ },
+ },
+ b: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "undefined",
+ },
+ },
+ c: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "c",
+ },
+ d: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: 1,
+ },
+ },
+ ownPropertiesLength: 4,
+ safeGetterValues: {},
+ },
+});
+stubs.set("testNonEnumerableProps", {
+ type: "object",
+ actor: "server1.conn1.child1/obj30",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 1,
+ safeGetterValues: {},
+ },
+});
+stubs.set("testNestedObject", {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj145",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ objProp: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj146",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ },
+ },
+ strProp: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "test string",
+ },
+ },
+ ownPropertiesLength: 2,
+ safeGetterValues: {},
+ },
+});
+
+stubs.set("testNestedArray", {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj326",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ arrProp: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ class: "Array",
+ actor: "server1.conn0.obj327",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 1,
+ safeGetterValues: {},
+ },
+});
+
+stubs.set("testMoreProp", {
+ type: "object",
+ class: "Object",
+ actor: "server1.conn0.obj342",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ a: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "undefined",
+ },
+ },
+ b: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: 1,
+ },
+ more: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: 2,
+ },
+ d: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: 3,
+ },
+ },
+ ownPropertiesLength: 4,
+ safeGetterValues: {},
+ },
+});
+stubs.set("testBooleanObject", {
+ type: "object",
+ actor: "server1.conn1.child1/obj57",
+ class: "Boolean",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {},
+ wrappedValue: true,
+ },
+});
+stubs.set("testNumberObject", {
+ type: "object",
+ actor: "server1.conn1.child1/obj59",
+ class: "Number",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {},
+ wrappedValue: 42,
+ },
+});
+stubs.set("testStringObject", {
+ type: "object",
+ actor: "server1.conn1.child1/obj61",
+ class: "String",
+ ownPropertyLength: 4,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 4,
+ safeGetterValues: {},
+ wrappedValue: "foo",
+ },
+});
+stubs.set("testProxy", {
+ type: "object",
+ actor: "server1.conn1.child1/obj47",
+ class: "Proxy",
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<target>": {
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj48",
+ class: "Object",
+ ownPropertyLength: 1,
+ },
+ },
+ "<handler>": {
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj49",
+ class: "Array",
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+stubs.set("testProxySlots", {
+ proxyTarget: {
+ type: "object",
+ actor: "server1.conn1.child1/obj48",
+ class: "Object",
+ ownPropertyLength: 1,
+ },
+ proxyHandler: {
+ type: "object",
+ actor: "server1.conn1.child1/obj49",
+ class: "Array",
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ },
+ },
+});
+stubs.set("testArrayBuffer", {
+ type: "object",
+ actor: "server1.conn1.child1/obj170",
+ class: "ArrayBuffer",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {
+ byteLength: {
+ getterValue: 10,
+ getterPrototypeLevel: 1,
+ enumerable: false,
+ writable: true,
+ },
+ },
+ },
+});
+stubs.set("testSharedArrayBuffer", {
+ type: "object",
+ actor: "server1.conn1.child1/obj171",
+ class: "SharedArrayBuffer",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {
+ byteLength: {
+ getterValue: 5,
+ getterPrototypeLevel: 1,
+ enumerable: false,
+ writable: true,
+ },
+ },
+ },
+});
+stubs.set("testApplicationCache", {
+ type: "object",
+ actor: "server2.conn1.child2/obj45",
+ class: "OfflineResourceList",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {
+ status: {
+ getterValue: 0,
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ onchecking: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ onerror: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ onnoupdate: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ ondownloading: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ onprogress: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ onupdateready: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ oncached: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ onobsolete: {
+ getterValue: {
+ type: "null",
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ mozItems: {
+ getterValue: {
+ type: "object",
+ actor: "server2.conn1.child2/obj46",
+ class: "DOMStringList",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ArrayLike",
+ length: 0,
+ },
+ },
+ getterPrototypeLevel: 1,
+ enumerable: true,
+ writable: true,
+ },
+ },
+ },
+});
+stubs.set("testObjectWithNodes", {
+ type: "object",
+ actor: "server1.conn1.child1/obj214",
+ class: "Object",
+ ownPropertyLength: 2,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ foo: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj215",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ },
+ bar: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj216",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-2",
+ class: "btn btn-err",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ safeGetterValues: {},
+ },
+});
+stubs.set("testObjectWithDisconnectedNodes", {
+ type: "object",
+ actor: "server1.conn1.child1/obj214",
+ class: "Object",
+ ownPropertyLength: 2,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ foo: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj215",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ },
+ bar: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj216",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ attributes: {
+ id: "btn-2",
+ class: "btn btn-err",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ safeGetterValues: {},
+ },
+});
+
+// Packet for `({get x(){}})`
+stubs.set("TestObjectWithGetter", {
+ type: "object",
+ actor: "server2.conn1.child1/obj105",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ x: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server2.conn1.child1/obj106",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get x",
+ displayName: "get x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+ set: {
+ type: "undefined",
+ },
+ },
+ },
+ ownPropertiesLength: 1,
+ safeGetterValues: {},
+ },
+});
+
+// Packet for `({set x(s){}})`
+stubs.set("TestObjectWithSetter", {
+ type: "object",
+ actor: "server2.conn1.child1/obj115",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ x: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "undefined",
+ },
+ set: {
+ type: "object",
+ actor: "server2.conn1.child1/obj116",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "set x",
+ displayName: "set x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 1,
+ safeGetterValues: {},
+ },
+});
+
+// Packet for `({get x(){}, set x(s){}})`
+stubs.set("TestObjectWithGetterAndSetter", {
+ type: "object",
+ actor: "server2.conn1.child1/obj126",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ x: {
+ configurable: true,
+ enumerable: true,
+ get: {
+ type: "object",
+ actor: "server2.conn1.child1/obj127",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "get x",
+ displayName: "get x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+ set: {
+ type: "object",
+ actor: "server2.conn1.child1/obj128",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ name: "set x",
+ displayName: "set x",
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 1,
+ safeGetterValues: {},
+ },
+});
+
+// Packet for :
+// ({
+// [Symbol()]: "first unnamed symbol",
+// [Symbol()]: "second unnamed symbol",
+// [Symbol("named")] : "named symbol",
+// [Symbol.iterator] : function* () {yield 1;yield 2;},
+// x: 10,
+// })
+stubs.set("TestObjectWithSymbolProperties", {
+ type: "object",
+ actor: "server2.conn1.child1/obj30",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ x: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: 10,
+ },
+ },
+ ownSymbols: [
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "first unnamed symbol",
+ },
+ type: "symbol",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "second unnamed symbol",
+ },
+ type: "symbol",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "named symbol",
+ },
+ type: "symbol",
+ name: "named",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server2.conn1.child1/obj31",
+ class: "Function",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ location: {
+ url: "debugger eval code",
+ line: 1,
+ },
+ },
+ },
+ type: "symbol",
+ name: "Symbol.iterator",
+ },
+ ],
+ ownPropertiesLength: 1,
+ ownSymbolsLength: 4,
+ safeGetterValues: {},
+ },
+});
+
+// Packet for :
+// x = {};
+// for(let i = 0; i < 11; i++) {
+// x[Symbol(`i-${i}`)] = `value-${i}`
+// }
+// x;
+stubs.set("TestObjectWithMoreThanMaxSymbolProperties", {
+ type: "object",
+ actor: "server2.conn1.child1/obj39",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownSymbols: [
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-0",
+ },
+ type: "symbol",
+ name: "i-0",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-1",
+ },
+ type: "symbol",
+ name: "i-1",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-2",
+ },
+ type: "symbol",
+ name: "i-2",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-3",
+ },
+ type: "symbol",
+ name: "i-3",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-4",
+ },
+ type: "symbol",
+ name: "i-4",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-5",
+ },
+ type: "symbol",
+ name: "i-5",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-6",
+ },
+ type: "symbol",
+ name: "i-6",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-7",
+ },
+ type: "symbol",
+ name: "i-7",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-8",
+ },
+ type: "symbol",
+ name: "i-8",
+ },
+ {
+ descriptor: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "value-9",
+ },
+ type: "symbol",
+ name: "i-9",
+ },
+ ],
+ ownPropertiesLength: 0,
+ ownSymbolsLength: 11,
+ },
+});
+
+stubs.set('{test: "http://example.com/ some other text"}', {
+ type: "object",
+ actor: "server2.conn4.child17/obj30",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ test: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: "http://example.com/ some other text",
+ },
+ },
+ ownSymbols: [],
+ ownPropertiesLength: 1,
+ ownSymbolsLength: 0,
+ safeGetterValues: {},
+ },
+});
+
+stubs.set("Generator", {
+ type: "object",
+ actor: "server1.conn2.child1/obj33",
+ class: "Generator",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownSymbols: [],
+ ownPropertiesLength: 0,
+ ownSymbolsLength: 0,
+ safeGetterValues: {},
+ },
+});
+
+stubs.set("DeadObject", {
+ type: "object",
+ actor: "server1.conn7.child2/obj41",
+ class: "DeadObject",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+});
+
+// Packet for :
+// var obj = Object.create(null); obj.__proto__ = []; obj;
+stubs.set("ObjectWith__proto__Property", {
+ type: "object",
+ actor: "server1.conn1.child1/obj31",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ ["__proto__"]: {
+ configurable: true,
+ enumerable: true,
+ writable: true,
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj32",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ preview: {
+ kind: "ArrayLike",
+ length: 0,
+ },
+ },
+ },
+ },
+ ownSymbols: [],
+ ownPropertiesLength: 1,
+ ownSymbolsLength: 0,
+ safeGetterValues: {},
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/infinity.js b/devtools/client/shared/components/test/node/stubs/reps/infinity.js
new file mode 100644
index 0000000000..9948f218ef
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/infinity.js
@@ -0,0 +1,19 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Infinity`, {
+ "type": "Infinity"
+});
+
+stubs.set(`NegativeInfinity`, {
+ "type": "-Infinity"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/long-string.js b/devtools/client/shared/components/test/node/stubs/reps/long-string.js
new file mode 100644
index 0000000000..26715c4017
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/long-string.js
@@ -0,0 +1,39 @@
+/* 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/>. */
+
+"use strict";
+
+const multilineFullText = `a\n${Array(20000)
+ .fill("a")
+ .join("")}`;
+const fullTextLength = multilineFullText.length;
+const initialText = multilineFullText.substring(0, 10000);
+
+const stubs = new Map();
+
+stubs.set("testMultiline", {
+ type: "longString",
+ initial: initialText,
+ length: fullTextLength,
+ actor: "server1.conn1.child1/longString58",
+});
+
+stubs.set("testUnloadedFullText", {
+ type: "longString",
+ initial: Array(10000)
+ .fill("a")
+ .join(""),
+ length: 20000,
+ actor: "server1.conn1.child1/longString58",
+});
+
+stubs.set("testLoadedFullText", {
+ type: "longString",
+ fullText: multilineFullText,
+ initial: initialText,
+ length: fullTextLength,
+ actor: "server1.conn1.child1/longString58",
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/nan.js b/devtools/client/shared/components/test/node/stubs/reps/nan.js
new file mode 100644
index 0000000000..51b67dd128
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/nan.js
@@ -0,0 +1,15 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`NaN`, {
+ "type": "NaN"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/null.js b/devtools/client/shared/components/test/node/stubs/reps/null.js
new file mode 100644
index 0000000000..5f7ccf5f0c
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/null.js
@@ -0,0 +1,15 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Null`, {
+ "type": "null"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/number.js b/devtools/client/shared/components/test/node/stubs/reps/number.js
new file mode 100644
index 0000000000..217a3fa0da
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/number.js
@@ -0,0 +1,21 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Int`, 5);
+
+stubs.set(`True`, true);
+
+stubs.set(`False`, false);
+
+stubs.set(`NegZeroGrip`, {
+ "type": "-0"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/object-with-text.js b/devtools/client/shared/components/test/node/stubs/reps/object-with-text.js
new file mode 100644
index 0000000000..ea17d01d7e
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/object-with-text.js
@@ -0,0 +1,36 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("ShadowRule", {
+ type: "object",
+ class: "CSSStyleRule",
+ actor: "server1.conn3.obj273",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ObjectWithText",
+ text: ".Shadow",
+ },
+});
+
+stubs.set("CSSMediaRule", {
+ type: "object",
+ actor: "server2.conn8.child17/obj30",
+ class: "CSSMediaRule",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "ObjectWithText",
+ text: "(min-height: 680px), screen and (orientation: portrait)",
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/object-with-url.js b/devtools/client/shared/components/test/node/stubs/reps/object-with-url.js
new file mode 100644
index 0000000000..179faf0874
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/object-with-url.js
@@ -0,0 +1,22 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("ObjectWithUrl", {
+ type: "object",
+ class: "Location",
+ actor: "server1.conn2.obj272",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 15,
+ preview: {
+ kind: "ObjectWithURL",
+ url: "https://www.mozilla.org/en-US/",
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/promise.js b/devtools/client/shared/components/test/node/stubs/reps/promise.js
new file mode 100644
index 0000000000..6dc71cd230
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/promise.js
@@ -0,0 +1,244 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("Pending", {
+ type: "object",
+ actor: "server1.conn1.child1/obj54",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "pending",
+ },
+ },
+ ownPropertiesLength: 1,
+ },
+});
+
+stubs.set("FulfilledWithNumber", {
+ type: "object",
+ actor: "server1.conn1.child1/obj55",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "fulfilled",
+ },
+ "<value>": {
+ value: 42,
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+stubs.set("FulfilledWithString", {
+ type: "object",
+ actor: "server1.conn1.child1/obj56",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "fulfilled",
+ },
+ "<value>": {
+ value: "foo",
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+stubs.set("FulfilledWithObject", {
+ type: "object",
+ actor: "server1.conn1.child1/obj59",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "fulfilled",
+ },
+ "<value>": {
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj60",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 2,
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+stubs.set("FulfilledWithArray", {
+ type: "object",
+ actor: "server1.conn1.child1/obj57",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "fulfilled",
+ },
+ "<value>": {
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj58",
+ class: "Array",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 4,
+ preview: {
+ kind: "ArrayLike",
+ length: 3,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+stubs.set("FulfilledWithNode", {
+ type: "object",
+ actor: "server1.conn1.child1/obj217",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "fulfilled",
+ },
+ "<value>": {
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj218",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: true,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+stubs.set("FulfilledWithDisconnectedNode", {
+ type: "object",
+ actor: "server1.conn1.child1/obj217",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "fulfilled",
+ },
+ "<value>": {
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj218",
+ class: "HTMLButtonElement",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 0,
+ preview: {
+ kind: "DOMNode",
+ nodeType: 1,
+ nodeName: "button",
+ isConnected: false,
+ attributes: {
+ id: "btn-1",
+ class: "btn btn-log",
+ type: "button",
+ },
+ attributesLength: 3,
+ },
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+stubs.set("RejectedWithNumber", {
+ type: "object",
+ actor: "server0.conn0.child3/obj27",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "rejected",
+ },
+ "<reason>": {
+ value: 123,
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+stubs.set("RejectedWithObject", {
+ type: "object",
+ actor: "server0.conn0.child3/obj67",
+ class: "Promise",
+ ownPropertyLength: 0,
+ preview: {
+ kind: "Object",
+ ownProperties: {
+ "<state>": {
+ value: "rejected",
+ },
+ "<reason>": {
+ value: {
+ type: "object",
+ actor: "server1.conn1.child1/obj68",
+ class: "Object",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ },
+ },
+ },
+ ownPropertiesLength: 2,
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/regexp.js b/devtools/client/shared/components/test/node/stubs/reps/regexp.js
new file mode 100644
index 0000000000..0dd5b06e97
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/regexp.js
@@ -0,0 +1,36 @@
+/* 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/>. */
+
+"use strict";
+
+const stubs = new Map();
+stubs.set("RegExp", {
+ type: "object",
+ class: "RegExp",
+ actor: "server1.conn22.obj39",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ ownPropertyLength: 1,
+ displayString: "/ab+c/i",
+});
+
+stubs.set("longString displayString RegExp", {
+ type: "object",
+ actor: "server0.conn0.child2/obj79",
+ class: "RegExp",
+ ownPropertyLength: 1,
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ displayString: {
+ type: "longString",
+ actor: "server0.conn0.child2/longstractor78",
+ length: 30002,
+ initial:
+ "/ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ab ",
+ },
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/stubs.toml b/devtools/client/shared/components/test/node/stubs/reps/stubs.toml
new file mode 100644
index 0000000000..9cf1570cbe
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/stubs.toml
@@ -0,0 +1,20 @@
+[DEFAULT]
+tags = "devtools"
+subsuite = "devtools"
+support-files = [
+ "attribute.js",
+ "comment-node.js",
+ "date-time.js",
+ "infinity.js",
+ "nan.js",
+ "null.js",
+ "number.js",
+ "stylesheet.js",
+ "symbol.js",
+ "text-node.js",
+ "undefined.js",
+ "window.js",
+]
+
+["browser_dummy.js"]
+skip-if = ["true"] #This is only here so we can expose the support files in other toml files.
diff --git a/devtools/client/shared/components/test/node/stubs/reps/stylesheet.js b/devtools/client/shared/components/test/node/stubs/reps/stylesheet.js
new file mode 100644
index 0000000000..eeca646dd2
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/stylesheet.js
@@ -0,0 +1,29 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`StyleSheet`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal4294967299/obj40",
+ "class": "CSSStyleSheet",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ObjectWithURL",
+ "url": "https://example.com/styles.css"
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal4294967299/obj40"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/symbol.js b/devtools/client/shared/components/test/node/stubs/reps/symbol.js
new file mode 100644
index 0000000000..d7f0ace128
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/symbol.js
@@ -0,0 +1,33 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Symbol`, {
+ "type": "symbol",
+ "actor": "server0.conn0.windowGlobal4294967299/symbol40",
+ "name": "foo"
+});
+
+stubs.set(`SymbolWithoutIdentifier`, {
+ "type": "symbol",
+ "actor": "server0.conn0.windowGlobal4294967299/symbol42"
+});
+
+stubs.set(`SymbolWithLongString`, {
+ "type": "symbol",
+ "actor": "server0.conn0.windowGlobal4294967299/symbol44",
+ "name": {
+ "type": "longString",
+ "actor": "server0.conn0.windowGlobal4294967299/longstractor45",
+ "length": 20000,
+ "initial": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ }
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/text-node.js b/devtools/client/shared/components/test/node/stubs/reps/text-node.js
new file mode 100644
index 0000000000..eaf893cdbb
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/text-node.js
@@ -0,0 +1,141 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`testRendering`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal2147483651/obj40",
+ "class": "Text",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "DOMNode",
+ "nodeType": 3,
+ "nodeName": "#text",
+ "isConnected": true,
+ "textContent": "hello world"
+ },
+ "contentDomReference": {
+ "browsingContextId": 51,
+ "id": 0.5296372099388534
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal2147483651/obj40"
+});
+
+stubs.set(`testRenderingDisconnected`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal2147483651/obj42",
+ "class": "Text",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "DOMNode",
+ "nodeType": 3,
+ "nodeName": "#text",
+ "isConnected": false,
+ "textContent": "hello world"
+ },
+ "contentDomReference": {
+ "browsingContextId": 51,
+ "id": 0.6969799823627325
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal2147483651/obj42"
+});
+
+stubs.set(`testRenderingWithEOL`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal2147483651/obj44",
+ "class": "Text",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "DOMNode",
+ "nodeType": 3,
+ "nodeName": "#text",
+ "isConnected": false,
+ "textContent": "hello\nworld"
+ },
+ "contentDomReference": {
+ "browsingContextId": 51,
+ "id": 0.7640922670176581
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal2147483651/obj44"
+});
+
+stubs.set(`testRenderingWithDoubleQuote`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal2147483651/obj46",
+ "class": "Text",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "DOMNode",
+ "nodeType": 3,
+ "nodeName": "#text",
+ "isConnected": false,
+ "textContent": "hello\"world"
+ },
+ "contentDomReference": {
+ "browsingContextId": 51,
+ "id": 0.113013948491126
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal2147483651/obj46"
+});
+
+stubs.set(`testRenderingWithLongString`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal2147483651/obj48",
+ "class": "Text",
+ "ownPropertyLength": 0,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "DOMNode",
+ "nodeType": 3,
+ "nodeName": "#text",
+ "isConnected": false,
+ "textContent": {
+ "type": "longString",
+ "actor": "server0.conn0.windowGlobal2147483651/longstractor49",
+ "length": 20002,
+ "initial": "a\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ }
+ },
+ "contentDomReference": {
+ "browsingContextId": 51,
+ "id": 0.792316936882363
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal2147483651/obj48"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/undefined.js b/devtools/client/shared/components/test/node/stubs/reps/undefined.js
new file mode 100644
index 0000000000..7acd7be3cf
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/undefined.js
@@ -0,0 +1,15 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Undefined`, {
+ "type": "undefined"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/stubs/reps/window.js b/devtools/client/shared/components/test/node/stubs/reps/window.js
new file mode 100644
index 0000000000..6386e91054
--- /dev/null
+++ b/devtools/client/shared/components/test/node/stubs/reps/window.js
@@ -0,0 +1,65 @@
+/* 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/>. */
+
+"use strict";
+/*
+ * THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN browser_reps_stubs.js with STUBS_UPDATE=true env TO UPDATE.
+ */
+
+const stubs = new Map();
+stubs.set(`Window`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal2147483651/obj35",
+ "class": "Window",
+ "ownPropertyLength": 806,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ObjectWithURL",
+ "url": "data:text/html;charset=utf-8,stub generation"
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal2147483651/obj35"
+});
+
+stubs.set(`CrossOriginIframeContentWindow`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal10737418241/obj59",
+ "class": "Window",
+ "ownPropertyLength": 14,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ObjectWithURL",
+ "url": "http://example.org/document-builder.sjs?html=example.org"
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal10737418241/obj59"
+});
+
+stubs.set(`CrossOriginIframeTopWindow`, {
+ "_grip": {
+ "type": "object",
+ "actor": "server0.conn0.windowGlobal12884901889/obj20",
+ "class": "Window",
+ "ownPropertyLength": 16,
+ "extensible": true,
+ "frozen": false,
+ "sealed": false,
+ "isError": false,
+ "preview": {
+ "kind": "ObjectWithURL",
+ "url": "Restricted"
+ }
+ },
+ "actorID": "server0.conn0.windowGlobal12884901889/obj20"
+});
+
+module.exports = stubs;
diff --git a/devtools/client/shared/components/test/node/yarn.lock b/devtools/client/shared/components/test/node/yarn.lock
new file mode 100644
index 0000000000..de7f467a56
--- /dev/null
+++ b/devtools/client/shared/components/test/node/yarn.lock
@@ -0,0 +1,4209 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
+ integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
+ dependencies:
+ "@babel/highlight" "^7.10.4"
+
+"@babel/core@^7.1.0":
+ version "7.11.6"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.11.6.tgz#3a9455dc7387ff1bac45770650bc13ba04a15651"
+ integrity sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/generator" "^7.11.6"
+ "@babel/helper-module-transforms" "^7.11.0"
+ "@babel/helpers" "^7.10.4"
+ "@babel/parser" "^7.11.5"
+ "@babel/template" "^7.10.4"
+ "@babel/traverse" "^7.11.5"
+ "@babel/types" "^7.11.5"
+ convert-source-map "^1.7.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.1"
+ json5 "^2.1.2"
+ lodash "^4.17.19"
+ resolve "^1.3.2"
+ semver "^5.4.1"
+ source-map "^0.5.0"
+
+"@babel/generator@^7.11.5", "@babel/generator@^7.11.6", "@babel/generator@^7.4.0":
+ version "7.11.6"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.11.6.tgz#b868900f81b163b4d464ea24545c61cbac4dc620"
+ integrity sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==
+ dependencies:
+ "@babel/types" "^7.11.5"
+ jsesc "^2.5.1"
+ source-map "^0.5.0"
+
+"@babel/helper-create-class-features-plugin@^7.10.4":
+ version "7.10.5"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz#9f61446ba80e8240b0a5c85c6fdac8459d6f259d"
+ integrity sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==
+ dependencies:
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-member-expression-to-functions" "^7.10.5"
+ "@babel/helper-optimise-call-expression" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-replace-supers" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.10.4"
+
+"@babel/helper-function-name@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
+ integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
+ dependencies:
+ "@babel/helper-get-function-arity" "^7.10.4"
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-get-function-arity@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
+ integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-member-expression-to-functions@^7.10.4", "@babel/helper-member-expression-to-functions@^7.10.5":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz#ae69c83d84ee82f4b42f96e2a09410935a8f26df"
+ integrity sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
+"@babel/helper-module-imports@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz#4c5c54be04bd31670a7382797d75b9fa2e5b5620"
+ integrity sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-module-transforms@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz#b16f250229e47211abdd84b34b64737c2ab2d359"
+ integrity sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==
+ dependencies:
+ "@babel/helper-module-imports" "^7.10.4"
+ "@babel/helper-replace-supers" "^7.10.4"
+ "@babel/helper-simple-access" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.11.0"
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.11.0"
+ lodash "^4.17.19"
+
+"@babel/helper-optimise-call-expression@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz#50dc96413d594f995a77905905b05893cd779673"
+ integrity sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==
+ dependencies:
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.8.0":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz#2f75a831269d4f677de49986dff59927533cf375"
+ integrity sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==
+
+"@babel/helper-replace-supers@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz#d585cd9388ea06e6031e4cd44b6713cbead9e6cf"
+ integrity sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "^7.10.4"
+ "@babel/helper-optimise-call-expression" "^7.10.4"
+ "@babel/traverse" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-simple-access@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz#0f5ccda2945277a2a7a2d3a821e15395edcf3461"
+ integrity sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==
+ dependencies:
+ "@babel/template" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz#eec162f112c2f58d3af0af125e3bb57665146729"
+ integrity sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
+"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
+ integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
+ dependencies:
+ "@babel/types" "^7.11.0"
+
+"@babel/helper-validator-identifier@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
+ integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
+
+"@babel/helpers@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.10.4.tgz#2abeb0d721aff7c0a97376b9e1f6f65d7a475044"
+ integrity sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==
+ dependencies:
+ "@babel/template" "^7.10.4"
+ "@babel/traverse" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/highlight@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
+ integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.10.4"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.1.0", "@babel/parser@^7.10.4", "@babel/parser@^7.11.5", "@babel/parser@^7.4.3":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.11.5.tgz#c7ff6303df71080ec7a4f5b8c003c58f1cf51037"
+ integrity sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==
+
+"@babel/plugin-proposal-class-properties@7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz#a33bf632da390a59c7a8c570045d1115cd778807"
+ integrity sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.10.4"
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.8.3":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz#02a7e961fc32e6d5b2db0649e01bf80ddee7e04a"
+ integrity sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0"
+
+"@babel/plugin-proposal-optional-chaining@^7.8.3":
+ version "7.11.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz#de5866d0646f6afdaab8a566382fe3a221755076"
+ integrity sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.11.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.0"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+ integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-object-rest-spread@^7.0.0":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+ integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.0":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+ integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/template@^7.10.4", "@babel/template@^7.4.0":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
+ integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/parser" "^7.10.4"
+ "@babel/types" "^7.10.4"
+
+"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.11.5", "@babel/traverse@^7.4.3":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.11.5.tgz#be777b93b518eb6d76ee2e1ea1d143daa11e61c3"
+ integrity sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==
+ dependencies:
+ "@babel/code-frame" "^7.10.4"
+ "@babel/generator" "^7.11.5"
+ "@babel/helper-function-name" "^7.10.4"
+ "@babel/helper-split-export-declaration" "^7.11.0"
+ "@babel/parser" "^7.11.5"
+ "@babel/types" "^7.11.5"
+ debug "^4.1.0"
+ globals "^11.1.0"
+ lodash "^4.17.19"
+
+"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.11.5", "@babel/types@^7.3.0", "@babel/types@^7.4.0":
+ version "7.11.5"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.11.5.tgz#d9de577d01252d77c6800cee039ee64faf75662d"
+ integrity sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.10.4"
+ lodash "^4.17.19"
+ to-fast-properties "^2.0.0"
+
+"@cnakazawa/watch@^1.0.3":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
+ integrity sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==
+ dependencies:
+ exec-sh "^0.3.2"
+ minimist "^1.2.0"
+
+"@jest/console@^24.7.1", "@jest/console@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0"
+ integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==
+ dependencies:
+ "@jest/source-map" "^24.9.0"
+ chalk "^2.0.1"
+ slash "^2.0.0"
+
+"@jest/core@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4"
+ integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==
+ dependencies:
+ "@jest/console" "^24.7.1"
+ "@jest/reporters" "^24.9.0"
+ "@jest/test-result" "^24.9.0"
+ "@jest/transform" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ ansi-escapes "^3.0.0"
+ chalk "^2.0.1"
+ exit "^0.1.2"
+ graceful-fs "^4.1.15"
+ jest-changed-files "^24.9.0"
+ jest-config "^24.9.0"
+ jest-haste-map "^24.9.0"
+ jest-message-util "^24.9.0"
+ jest-regex-util "^24.3.0"
+ jest-resolve "^24.9.0"
+ jest-resolve-dependencies "^24.9.0"
+ jest-runner "^24.9.0"
+ jest-runtime "^24.9.0"
+ jest-snapshot "^24.9.0"
+ jest-util "^24.9.0"
+ jest-validate "^24.9.0"
+ jest-watcher "^24.9.0"
+ micromatch "^3.1.10"
+ p-each-series "^1.0.0"
+ realpath-native "^1.1.0"
+ rimraf "^2.5.4"
+ slash "^2.0.0"
+ strip-ansi "^5.0.0"
+
+"@jest/environment@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18"
+ integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==
+ dependencies:
+ "@jest/fake-timers" "^24.9.0"
+ "@jest/transform" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ jest-mock "^24.9.0"
+
+"@jest/fake-timers@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93"
+ integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ jest-message-util "^24.9.0"
+ jest-mock "^24.9.0"
+
+"@jest/reporters@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43"
+ integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==
+ dependencies:
+ "@jest/environment" "^24.9.0"
+ "@jest/test-result" "^24.9.0"
+ "@jest/transform" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ chalk "^2.0.1"
+ exit "^0.1.2"
+ glob "^7.1.2"
+ istanbul-lib-coverage "^2.0.2"
+ istanbul-lib-instrument "^3.0.1"
+ istanbul-lib-report "^2.0.4"
+ istanbul-lib-source-maps "^3.0.1"
+ istanbul-reports "^2.2.6"
+ jest-haste-map "^24.9.0"
+ jest-resolve "^24.9.0"
+ jest-runtime "^24.9.0"
+ jest-util "^24.9.0"
+ jest-worker "^24.6.0"
+ node-notifier "^5.4.2"
+ slash "^2.0.0"
+ source-map "^0.6.0"
+ string-length "^2.0.0"
+
+"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714"
+ integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==
+ dependencies:
+ callsites "^3.0.0"
+ graceful-fs "^4.1.15"
+ source-map "^0.6.0"
+
+"@jest/test-result@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca"
+ integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==
+ dependencies:
+ "@jest/console" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+
+"@jest/test-sequencer@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31"
+ integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==
+ dependencies:
+ "@jest/test-result" "^24.9.0"
+ jest-haste-map "^24.9.0"
+ jest-runner "^24.9.0"
+ jest-runtime "^24.9.0"
+
+"@jest/transform@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56"
+ integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==
+ dependencies:
+ "@babel/core" "^7.1.0"
+ "@jest/types" "^24.9.0"
+ babel-plugin-istanbul "^5.1.0"
+ chalk "^2.0.1"
+ convert-source-map "^1.4.0"
+ fast-json-stable-stringify "^2.0.0"
+ graceful-fs "^4.1.15"
+ jest-haste-map "^24.9.0"
+ jest-regex-util "^24.9.0"
+ jest-util "^24.9.0"
+ micromatch "^3.1.10"
+ pirates "^4.0.1"
+ realpath-native "^1.1.0"
+ slash "^2.0.0"
+ source-map "^0.6.1"
+ write-file-atomic "2.4.1"
+
+"@jest/types@^24.9.0":
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59"
+ integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==
+ dependencies:
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^1.1.1"
+ "@types/yargs" "^13.0.0"
+
+"@tootallnate/once@2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
+ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
+
+"@types/babel__core@^7.1.0":
+ version "7.1.10"
+ resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.10.tgz#ca58fc195dd9734e77e57c6f2df565623636ab40"
+ integrity sha512-x8OM8XzITIMyiwl5Vmo2B1cR1S1Ipkyv4mdlbJjMa1lmuKvKY9FrBbEANIaMlnWn5Rf7uO+rC/VgYabNkE17Hw==
+ dependencies:
+ "@babel/parser" "^7.1.0"
+ "@babel/types" "^7.0.0"
+ "@types/babel__generator" "*"
+ "@types/babel__template" "*"
+ "@types/babel__traverse" "*"
+
+"@types/babel__generator@*":
+ version "7.6.2"
+ resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.2.tgz#f3d71178e187858f7c45e30380f8f1b7415a12d8"
+ integrity sha512-MdSJnBjl+bdwkLskZ3NGFp9YcXGx5ggLpQQPqtgakVhsWK0hTtNYhjpZLlWQTviGTvF8at+Bvli3jV7faPdgeQ==
+ dependencies:
+ "@babel/types" "^7.0.0"
+
+"@types/babel__template@*":
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.0.3.tgz#b8aaeba0a45caca7b56a5de9459872dde3727214"
+ integrity sha512-uCoznIPDmnickEi6D0v11SBpW0OuVqHJCa7syXqQHy5uktSCreIlt0iglsCnmvz8yCb38hGcWeseA8cWJSwv5Q==
+ dependencies:
+ "@babel/parser" "^7.1.0"
+ "@babel/types" "^7.0.0"
+
+"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
+ version "7.0.15"
+ resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03"
+ integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A==
+ dependencies:
+ "@babel/types" "^7.3.0"
+
+"@types/cheerio@^0.22.22":
+ version "0.22.22"
+ resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.22.tgz#ae71cf4ca59b8bbaf34c99af7a5d6c8894988f5f"
+ integrity sha512-05DYX4zU96IBfZFY+t3Mh88nlwSMtmmzSYaQkKN48T495VV1dkHSah6qYyDTN5ngaS0i0VonH37m+RuzSM0YiA==
+ dependencies:
+ "@types/node" "*"
+
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762"
+ integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==
+
+"@types/istanbul-lib-report@*":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
+ integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
+ dependencies:
+ "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^1.1.1":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz#e875cc689e47bce549ec81f3df5e6f6f11cfaeb2"
+ integrity sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==
+ dependencies:
+ "@types/istanbul-lib-coverage" "*"
+ "@types/istanbul-lib-report" "*"
+
+"@types/node@*":
+ version "14.11.8"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.11.8.tgz#fe2012f2355e4ce08bca44aeb3abbb21cf88d33f"
+ integrity sha512-KPcKqKm5UKDkaYPTuXSx8wEP7vE9GnuaXIZKijwRYcePpZFDVuy2a57LarFKiORbHOuTOOwYzxVxcUzsh2P2Pw==
+
+"@types/stack-utils@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
+ integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
+
+"@types/yargs-parser@*":
+ version "15.0.0"
+ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
+ integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
+
+"@types/yargs@^13.0.0":
+ version "13.0.11"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.11.tgz#def2f0c93e4bdf2c61d7e34899b17e34be28d3b1"
+ integrity sha512-NRqD6T4gktUrDi1o1wLH3EKC1o2caCr7/wR87ODcbVITQF106OM3sFN92ysZ++wqelOd1CTzatnOBRDYYG6wGQ==
+ dependencies:
+ "@types/yargs-parser" "*"
+
+abab@^2.0.0:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
+ integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==
+
+abab@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
+ integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
+
+acorn-globals@^4.1.0:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7"
+ integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==
+ dependencies:
+ acorn "^6.0.1"
+ acorn-walk "^6.0.1"
+
+acorn-globals@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
+ integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==
+ dependencies:
+ acorn "^7.1.1"
+ acorn-walk "^7.1.1"
+
+acorn-walk@^6.0.1:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c"
+ integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==
+
+acorn-walk@^7.1.1:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
+ integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
+
+acorn@^5.5.3:
+ version "5.7.4"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e"
+ integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==
+
+acorn@^6.0.1:
+ version "6.4.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6"
+ integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==
+
+acorn@^7.1.1:
+ version "7.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+ integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
+acorn@^8.7.1:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
+ integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==
+
+agent-base@6:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
+ integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
+ dependencies:
+ debug "4"
+
+airbnb-prop-types@^2.16.0:
+ version "2.16.0"
+ resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz#b96274cefa1abb14f623f804173ee97c13971dc2"
+ integrity sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==
+ dependencies:
+ array.prototype.find "^2.1.1"
+ function.prototype.name "^1.1.2"
+ is-regex "^1.1.0"
+ object-is "^1.1.2"
+ object.assign "^4.1.0"
+ object.entries "^1.1.2"
+ prop-types "^15.7.2"
+ prop-types-exact "^1.2.0"
+ react-is "^16.13.1"
+
+ajv@^6.12.3:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-escapes@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
+ integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
+
+ansi-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
+ integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+
+ansi-regex@^4.0.0, ansi-regex@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
+ integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
+
+ansi-styles@^3.2.0, ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+anymatch@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+ integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==
+ dependencies:
+ micromatch "^3.1.4"
+ normalize-path "^2.1.1"
+
+arr-diff@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+ integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
+
+arr-flatten@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+ integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
+
+arr-union@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
+ integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=
+
+array-equal@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93"
+ integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=
+
+array-filter@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
+ integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
+
+array-unique@^0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+ integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+
+array.prototype.find@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.1.tgz#3baca26108ca7affb08db06bf0be6cb3115a969c"
+ integrity sha512-mi+MYNJYLTx2eNYy+Yh6raoQacCsNeeMUaspFPh9Y141lFSsWxxB8V9mM2ye+eqiRs917J6/pJ4M9ZPzenWckA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.4"
+
+array.prototype.flat@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b"
+ integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+
+asap@~2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+ integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
+
+asn1@~0.2.3:
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
+ integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
+ dependencies:
+ safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+ integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
+
+assign-symbols@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+ integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
+
+astral-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
+ integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
+
+async-limiter@~1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
+ integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
+
+asynckit@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+ integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+atob@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
+ integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+
+aws-sign2@~0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+ integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
+
+aws4@^1.8.0:
+ version "1.10.1"
+ resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.1.tgz#e1e82e4f3e999e2cfd61b161280d16a111f86428"
+ integrity sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==
+
+babel-jest@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54"
+ integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==
+ dependencies:
+ "@jest/transform" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ "@types/babel__core" "^7.1.0"
+ babel-plugin-istanbul "^5.1.0"
+ babel-preset-jest "^24.9.0"
+ chalk "^2.4.2"
+ slash "^2.0.0"
+
+babel-plugin-istanbul@^5.1.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz#df4ade83d897a92df069c4d9a25cf2671293c854"
+ integrity sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ find-up "^3.0.0"
+ istanbul-lib-instrument "^3.3.0"
+ test-exclude "^5.2.3"
+
+babel-plugin-jest-hoist@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756"
+ integrity sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==
+ dependencies:
+ "@types/babel__traverse" "^7.0.6"
+
+babel-plugin-transform-amd-to-commonjs@1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-amd-to-commonjs/-/babel-plugin-transform-amd-to-commonjs-1.4.0.tgz#d9bc5003eaa26dbdd4e854e453f84903852af2ca"
+ integrity sha512-Xx0kYPn0LPyms+8n2KLn9yd2R5XMb2P1sNe4qn64/UQY5F2KFYlhhhyYUNm/BThfODAzl7rbaOsEfpU2M8iDKQ==
+
+babel-preset-jest@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc"
+ integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==
+ dependencies:
+ "@babel/plugin-syntax-object-rest-spread" "^7.0.0"
+ babel-plugin-jest-hoist "^24.9.0"
+
+balanced-match@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
+ integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+
+base@^0.11.1:
+ version "0.11.2"
+ resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+ integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==
+ dependencies:
+ cache-base "^1.0.1"
+ class-utils "^0.3.5"
+ component-emitter "^1.2.1"
+ define-property "^1.0.0"
+ isobject "^3.0.1"
+ mixin-deep "^1.2.0"
+ pascalcase "^0.1.1"
+
+bcrypt-pbkdf@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+ integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
+ dependencies:
+ tweetnacl "^0.14.3"
+
+bindings@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
+ integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
+ dependencies:
+ file-uri-to-path "1.0.0"
+
+boolbase@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+ integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^2.3.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729"
+ integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==
+ dependencies:
+ arr-flatten "^1.1.0"
+ array-unique "^0.3.2"
+ extend-shallow "^2.0.1"
+ fill-range "^4.0.0"
+ isobject "^3.0.1"
+ repeat-element "^1.1.2"
+ snapdragon "^0.8.1"
+ snapdragon-node "^2.0.1"
+ split-string "^3.0.2"
+ to-regex "^3.0.1"
+
+browser-process-hrtime@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
+ integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
+
+browser-resolve@^1.11.3:
+ version "1.11.3"
+ resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6"
+ integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==
+ dependencies:
+ resolve "1.1.7"
+
+bser@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
+ integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
+ dependencies:
+ node-int64 "^0.4.0"
+
+buffer-from@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+ integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+
+cache-base@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+ integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==
+ dependencies:
+ collection-visit "^1.0.0"
+ component-emitter "^1.2.1"
+ get-value "^2.0.6"
+ has-value "^1.0.0"
+ isobject "^3.0.1"
+ set-value "^2.0.0"
+ to-object-path "^0.3.0"
+ union-value "^1.0.0"
+ unset-value "^1.0.0"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camelcase@^5.0.0, camelcase@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+capture-exit@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4"
+ integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==
+ dependencies:
+ rsvp "^4.8.4"
+
+caseless@~0.12.0:
+ version "0.12.0"
+ resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+ integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
+
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.2:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+cheerio@^1.0.0-rc.3:
+ version "1.0.0-rc.3"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
+ integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==
+ dependencies:
+ css-select "~1.2.0"
+ dom-serializer "~0.1.1"
+ entities "~1.1.1"
+ htmlparser2 "^3.9.1"
+ lodash "^4.15.0"
+ parse5 "^3.0.1"
+
+ci-info@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+ integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+
+class-utils@^0.3.5:
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+ integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==
+ dependencies:
+ arr-union "^3.1.0"
+ define-property "^0.2.5"
+ isobject "^3.0.0"
+ static-extend "^0.1.1"
+
+cliui@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
+ integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==
+ dependencies:
+ string-width "^3.1.0"
+ strip-ansi "^5.2.0"
+ wrap-ansi "^5.1.0"
+
+co@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+ integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
+
+collection-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+ integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=
+ dependencies:
+ map-visit "^1.0.0"
+ object-visit "^1.0.0"
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
+
+combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ dependencies:
+ delayed-stream "~1.0.0"
+
+commander@^2.19.0:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+component-emitter@^1.2.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0"
+ integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+convert-source-map@^1.4.0, convert-source-map@^1.7.0:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
+ integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
+ dependencies:
+ safe-buffer "~5.1.1"
+
+copy-descriptor@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+ integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
+
+core-js@^1.0.0:
+ version "1.2.7"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
+ integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
+
+core-util-is@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+ integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+
+cross-spawn@^6.0.0:
+ version "6.0.5"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
+ integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==
+ dependencies:
+ nice-try "^1.0.4"
+ path-key "^2.0.1"
+ semver "^5.5.0"
+ shebang-command "^1.2.0"
+ which "^1.2.9"
+
+css-select@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
+ integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
+ dependencies:
+ boolbase "~1.0.0"
+ css-what "2.1"
+ domutils "1.5.1"
+ nth-check "~1.0.1"
+
+css-what@2.1:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
+ integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
+
+cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0", cssom@~0.3.6:
+ version "0.3.8"
+ resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
+ integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
+
+cssom@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
+ integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
+
+cssstyle@^1.0.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1"
+ integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==
+ dependencies:
+ cssom "0.3.x"
+
+cssstyle@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
+ integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
+ dependencies:
+ cssom "~0.3.6"
+
+dashdash@^1.12.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+ integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
+ dependencies:
+ assert-plus "^1.0.0"
+
+data-urls@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe"
+ integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==
+ dependencies:
+ abab "^2.0.0"
+ whatwg-mimetype "^2.2.0"
+ whatwg-url "^7.0.0"
+
+data-urls@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
+ integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==
+ dependencies:
+ abab "^2.0.6"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^11.0.0"
+
+debug@4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+debug@^2.2.0, debug@^2.3.3:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^4.1.0, debug@^4.1.1:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
+ integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
+ dependencies:
+ ms "2.1.2"
+
+decamelize@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
+ integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
+
+decimal.js@^10.3.1:
+ version "10.4.0"
+ resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.0.tgz#97a7448873b01e92e5ff9117d89a7bca8e63e0fe"
+ integrity sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==
+
+decode-uri-component@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+ integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
+
+deep-is@~0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
+ integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+
+define-properties@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+ integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+ dependencies:
+ object-keys "^1.0.12"
+
+define-property@^0.2.5:
+ version "0.2.5"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+ integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=
+ dependencies:
+ is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+ integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY=
+ dependencies:
+ is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+ integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==
+ dependencies:
+ is-descriptor "^1.0.2"
+ isobject "^3.0.1"
+
+delayed-stream@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+ integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
+detect-newline@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2"
+ integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=
+
+diff-sequences@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
+ integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==
+
+discontinuous-range@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a"
+ integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=
+
+dom-serializer@0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
+ integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
+ dependencies:
+ domelementtype "^2.0.1"
+ entities "^2.0.0"
+
+dom-serializer@~0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
+ integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
+ dependencies:
+ domelementtype "^1.3.0"
+ entities "^1.1.1"
+
+domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
+ integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
+
+domelementtype@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.2.tgz#f3b6e549201e46f588b59463dd77187131fe6971"
+ integrity sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==
+
+domexception@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
+ integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
+ dependencies:
+ webidl-conversions "^4.0.2"
+
+domexception@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
+ integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
+ dependencies:
+ webidl-conversions "^7.0.0"
+
+domhandler@^2.3.0:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
+ integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==
+ dependencies:
+ domelementtype "1"
+
+domutils@1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+ integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
+domutils@^1.5.1:
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
+ integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
+ecc-jsbn@~0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+ integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
+ dependencies:
+ jsbn "~0.1.0"
+ safer-buffer "^2.1.0"
+
+emoji-regex@^7.0.1:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
+ integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
+
+encoding@^0.1.11:
+ version "0.1.13"
+ resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9"
+ integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==
+ dependencies:
+ iconv-lite "^0.6.2"
+
+end-of-stream@^1.1.0:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+ integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+ dependencies:
+ once "^1.4.0"
+
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
+ integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
+
+entities@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
+ integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
+
+entities@^4.3.0:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4"
+ integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==
+
+enzyme-adapter-react-16@^1.13.2:
+ version "1.15.5"
+ resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.5.tgz#7a6f0093d3edd2f7025b36e7fbf290695473ee04"
+ integrity sha512-33yUJGT1nHFQlbVI5qdo5Pfqvu/h4qPwi1o0a6ZZsjpiqq92a3HjynDhwd1IeED+Su60HDWV8mxJqkTnLYdGkw==
+ dependencies:
+ enzyme-adapter-utils "^1.13.1"
+ enzyme-shallow-equal "^1.0.4"
+ has "^1.0.3"
+ object.assign "^4.1.0"
+ object.values "^1.1.1"
+ prop-types "^15.7.2"
+ react-is "^16.13.1"
+ react-test-renderer "^16.0.0-0"
+ semver "^5.7.0"
+
+enzyme-adapter-utils@^1.13.1:
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.13.1.tgz#59c1b734b0927543e3d8dc477299ec957feb312d"
+ integrity sha512-5A9MXXgmh/Tkvee3bL/9RCAAgleHqFnsurTYCbymecO4ohvtNO5zqIhHxV370t7nJAwaCfkgtffarKpC0GPt0g==
+ dependencies:
+ airbnb-prop-types "^2.16.0"
+ function.prototype.name "^1.1.2"
+ object.assign "^4.1.0"
+ object.fromentries "^2.0.2"
+ prop-types "^15.7.2"
+ semver "^5.7.1"
+
+enzyme-shallow-equal@^1.0.1, enzyme-shallow-equal@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz#b9256cb25a5f430f9bfe073a84808c1d74fced2e"
+ integrity sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==
+ dependencies:
+ has "^1.0.3"
+ object-is "^1.1.2"
+
+enzyme-to-json@^3.3.5:
+ version "3.6.1"
+ resolved "https://registry.yarnpkg.com/enzyme-to-json/-/enzyme-to-json-3.6.1.tgz#d60740950bc7ca6384dfe6fe405494ec5df996bc"
+ integrity sha512-15tXuONeq5ORoZjV/bUo2gbtZrN2IH+Z6DvL35QmZyKHgbY1ahn6wcnLd9Xv9OjiwbAXiiP8MRZwbZrCv1wYNg==
+ dependencies:
+ "@types/cheerio" "^0.22.22"
+ lodash "^4.17.15"
+ react-is "^16.12.0"
+
+enzyme@^3.9.0:
+ version "3.11.0"
+ resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28"
+ integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==
+ dependencies:
+ array.prototype.flat "^1.2.3"
+ cheerio "^1.0.0-rc.3"
+ enzyme-shallow-equal "^1.0.1"
+ function.prototype.name "^1.1.2"
+ has "^1.0.3"
+ html-element-map "^1.2.0"
+ is-boolean-object "^1.0.1"
+ is-callable "^1.1.5"
+ is-number-object "^1.0.4"
+ is-regex "^1.0.5"
+ is-string "^1.0.5"
+ is-subset "^0.1.1"
+ lodash.escape "^4.0.1"
+ lodash.isequal "^4.5.0"
+ object-inspect "^1.7.0"
+ object-is "^1.0.2"
+ object.assign "^4.1.0"
+ object.entries "^1.1.1"
+ object.values "^1.1.1"
+ raf "^3.4.1"
+ rst-selector-parser "^2.2.3"
+ string.prototype.trim "^1.2.1"
+
+error-ex@^1.3.1:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+ integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+ dependencies:
+ is-arrayish "^0.2.1"
+
+es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5:
+ version "1.17.7"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c"
+ integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==
+ dependencies:
+ es-to-primitive "^1.2.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.1"
+ is-callable "^1.2.2"
+ is-regex "^1.1.1"
+ object-inspect "^1.8.0"
+ object-keys "^1.1.1"
+ object.assign "^4.1.1"
+ string.prototype.trimend "^1.0.1"
+ string.prototype.trimstart "^1.0.1"
+
+es-abstract@^1.18.0-next.0, es-abstract@^1.18.0-next.1:
+ version "1.18.0-next.1"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
+ integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
+ dependencies:
+ es-to-primitive "^1.2.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.1"
+ is-callable "^1.2.2"
+ is-negative-zero "^2.0.0"
+ is-regex "^1.1.1"
+ object-inspect "^1.8.0"
+ object-keys "^1.1.1"
+ object.assign "^4.1.1"
+ string.prototype.trimend "^1.0.1"
+ string.prototype.trimstart "^1.0.1"
+
+es-to-primitive@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+ integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+ dependencies:
+ is-callable "^1.1.4"
+ is-date-object "^1.0.1"
+ is-symbol "^1.0.2"
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escodegen@^1.9.1:
+ version "1.14.3"
+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
+ integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==
+ dependencies:
+ esprima "^4.0.1"
+ estraverse "^4.2.0"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.6.1"
+
+escodegen@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
+ integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==
+ dependencies:
+ esprima "^4.0.1"
+ estraverse "^5.2.0"
+ esutils "^2.0.2"
+ optionator "^0.8.1"
+ optionalDependencies:
+ source-map "~0.6.1"
+
+esprima@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+ integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+estraverse@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+exec-sh@^0.3.2:
+ version "0.3.4"
+ resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5"
+ integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==
+
+execa@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+ integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+ dependencies:
+ cross-spawn "^6.0.0"
+ get-stream "^4.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
+exit@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
+ integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=
+
+expand-brackets@^2.1.4:
+ version "2.1.4"
+ resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
+ integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI=
+ dependencies:
+ debug "^2.3.3"
+ define-property "^0.2.5"
+ extend-shallow "^2.0.1"
+ posix-character-classes "^0.1.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
+expect@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca"
+ integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ ansi-styles "^3.2.0"
+ jest-get-type "^24.9.0"
+ jest-matcher-utils "^24.9.0"
+ jest-message-util "^24.9.0"
+ jest-regex-util "^24.9.0"
+
+extend-shallow@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+ integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=
+ dependencies:
+ is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+ integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=
+ dependencies:
+ assign-symbols "^1.0.0"
+ is-extendable "^1.0.1"
+
+extend@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+ integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extglob@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+ integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==
+ dependencies:
+ array-unique "^0.3.2"
+ define-property "^1.0.0"
+ expand-brackets "^2.1.4"
+ extend-shallow "^2.0.1"
+ fragment-cache "^0.2.1"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
+extsprintf@1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+ integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
+
+extsprintf@^1.2.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+ integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
+
+fast-deep-equal@^3.1.1:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@~2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+fb-watchman@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
+ integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==
+ dependencies:
+ bser "2.1.1"
+
+fbjs@^0.8.16:
+ version "0.8.17"
+ resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
+ integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
+ dependencies:
+ core-js "^1.0.0"
+ isomorphic-fetch "^2.1.1"
+ loose-envify "^1.0.0"
+ object-assign "^4.1.0"
+ promise "^7.1.1"
+ setimmediate "^1.0.5"
+ ua-parser-js "^0.7.18"
+
+file-uri-to-path@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
+ integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
+
+fill-range@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
+ integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
+ to-regex-range "^2.1.0"
+
+find-up@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
+ integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
+ dependencies:
+ locate-path "^3.0.0"
+
+for-in@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
+ integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
+
+forever-agent@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+ integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
+
+form-data@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+ integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.8"
+ mime-types "^2.1.12"
+
+form-data@~2.3.2:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+ integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+ dependencies:
+ asynckit "^0.4.0"
+ combined-stream "^1.0.6"
+ mime-types "^2.1.12"
+
+fragment-cache@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+ integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=
+ dependencies:
+ map-cache "^0.2.2"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@^1.2.7:
+ version "1.2.13"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38"
+ integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==
+ dependencies:
+ bindings "^1.5.0"
+ nan "^2.12.1"
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+function.prototype.name@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.2.tgz#5cdf79d7c05db401591dfde83e3b70c5123e9a45"
+ integrity sha512-C8A+LlHBJjB2AdcRPorc5JvJ5VUoWlXdEHLOJdCI7kjHEtGTpHQUiqMvCIKUwIsGwZX2jZJy761AXsn356bJQg==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ functions-have-names "^1.2.0"
+
+functions-have-names@^1.2.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.1.tgz#a981ac397fa0c9964551402cdc5533d7a4d52f91"
+ integrity sha512-j48B/ZI7VKs3sgeI2cZp7WXWmZXu7Iq5pl5/vptV5N2mq+DGFuS/ulaDjtaoLpYzuD6u8UgrUKHfgo7fDTSiBA==
+
+gensync@^1.0.0-beta.1:
+ version "1.0.0-beta.1"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
+ integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==
+
+get-caller-file@^2.0.1:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-stream@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+ integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+ dependencies:
+ pump "^3.0.0"
+
+get-value@^2.0.3, get-value@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
+ integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=
+
+getpass@^0.1.1:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+ integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
+ dependencies:
+ assert-plus "^1.0.0"
+
+glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^11.1.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
+ integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+
+growly@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
+ integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=
+
+har-schema@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+ integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
+
+har-validator@~5.1.3:
+ version "5.1.5"
+ resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
+ integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
+ dependencies:
+ ajv "^6.12.3"
+ har-schema "^2.0.0"
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
+
+has-symbols@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
+ integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
+
+has-value@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+ integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=
+ dependencies:
+ get-value "^2.0.3"
+ has-values "^0.1.4"
+ isobject "^2.0.0"
+
+has-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+ integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=
+ dependencies:
+ get-value "^2.0.6"
+ has-values "^1.0.0"
+ isobject "^3.0.0"
+
+has-values@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+ integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E=
+
+has-values@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+ integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=
+ dependencies:
+ is-number "^3.0.0"
+ kind-of "^4.0.0"
+
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+hosted-git-info@^2.1.4:
+ version "2.8.8"
+ resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
+ integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
+
+html-element-map@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.2.0.tgz#dfbb09efe882806af63d990cf6db37993f099f22"
+ integrity sha512-0uXq8HsuG1v2TmQ8QkIhzbrqeskE4kn52Q18QJ9iAA/SnHoEKXWiUxHQtclRsCFWEUD2So34X+0+pZZu862nnw==
+ dependencies:
+ array-filter "^1.0.0"
+
+html-encoding-sniffer@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8"
+ integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==
+ dependencies:
+ whatwg-encoding "^1.0.1"
+
+html-encoding-sniffer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
+ integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
+ dependencies:
+ whatwg-encoding "^2.0.0"
+
+html-escaper@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
+ integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+
+htmlparser2@^3.9.1:
+ version "3.10.1"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
+ integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==
+ dependencies:
+ domelementtype "^1.3.1"
+ domhandler "^2.3.0"
+ domutils "^1.5.1"
+ entities "^1.1.1"
+ inherits "^2.0.1"
+ readable-stream "^3.1.1"
+
+http-proxy-agent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
+ integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
+ dependencies:
+ "@tootallnate/once" "2"
+ agent-base "6"
+ debug "4"
+
+http-signature@~1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+ integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
+ dependencies:
+ assert-plus "^1.0.0"
+ jsprim "^1.2.2"
+ sshpk "^1.7.0"
+
+https-proxy-agent@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+ integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+ dependencies:
+ agent-base "6"
+ debug "4"
+
+iconv-lite@0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+iconv-lite@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+ integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
+iconv-lite@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01"
+ integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
+import-local@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
+ integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==
+ dependencies:
+ pkg-dir "^3.0.0"
+ resolve-cwd "^2.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@^2.0.1, inherits@^2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+invariant@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
+ integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
+ dependencies:
+ loose-envify "^1.0.0"
+
+is-accessor-descriptor@^0.1.6:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+ integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+ integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==
+ dependencies:
+ kind-of "^6.0.0"
+
+is-arrayish@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+ integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
+
+is-boolean-object@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e"
+ integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==
+
+is-buffer@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+ integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
+
+is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
+ integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==
+
+is-ci@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+ integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
+ dependencies:
+ ci-info "^2.0.0"
+
+is-data-descriptor@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
+ integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+ integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==
+ dependencies:
+ kind-of "^6.0.0"
+
+is-date-object@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
+ integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
+
+is-descriptor@^0.1.0:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+ integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==
+ dependencies:
+ is-accessor-descriptor "^0.1.6"
+ is-data-descriptor "^0.1.4"
+ kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+ integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==
+ dependencies:
+ is-accessor-descriptor "^1.0.0"
+ is-data-descriptor "^1.0.0"
+ kind-of "^6.0.2"
+
+is-extendable@^0.1.0, is-extendable@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+ integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=
+
+is-extendable@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+ integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==
+ dependencies:
+ is-plain-object "^2.0.4"
+
+is-fullwidth-code-point@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
+ integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
+
+is-generator-fn@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
+ integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
+
+is-negative-zero@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
+ integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
+
+is-number-object@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197"
+ integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==
+
+is-number@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+ integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=
+ dependencies:
+ kind-of "^3.0.2"
+
+is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+is-potential-custom-element-name@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
+ integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
+
+is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
+ integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
+ dependencies:
+ has-symbols "^1.0.1"
+
+is-stream@^1.0.1, is-stream@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+ integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
+
+is-string@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
+ integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
+
+is-subset@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
+ integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=
+
+is-symbol@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
+ integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
+ dependencies:
+ has-symbols "^1.0.1"
+
+is-typedarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+ integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
+
+is-windows@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+ integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+
+is-wsl@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+ integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=
+
+isarray@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
+ integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=
+ dependencies:
+ isarray "1.0.0"
+
+isobject@^3.0.0, isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+isomorphic-fetch@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
+ integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
+ dependencies:
+ node-fetch "^1.0.1"
+ whatwg-fetch ">=0.10.0"
+
+isstream@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+ integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
+
+istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49"
+ integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==
+
+istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630"
+ integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==
+ dependencies:
+ "@babel/generator" "^7.4.0"
+ "@babel/parser" "^7.4.3"
+ "@babel/template" "^7.4.0"
+ "@babel/traverse" "^7.4.3"
+ "@babel/types" "^7.4.0"
+ istanbul-lib-coverage "^2.0.5"
+ semver "^6.0.0"
+
+istanbul-lib-report@^2.0.4:
+ version "2.0.8"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33"
+ integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==
+ dependencies:
+ istanbul-lib-coverage "^2.0.5"
+ make-dir "^2.1.0"
+ supports-color "^6.1.0"
+
+istanbul-lib-source-maps@^3.0.1:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8"
+ integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==
+ dependencies:
+ debug "^4.1.1"
+ istanbul-lib-coverage "^2.0.5"
+ make-dir "^2.1.0"
+ rimraf "^2.6.3"
+ source-map "^0.6.1"
+
+istanbul-reports@^2.2.6:
+ version "2.2.7"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931"
+ integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg==
+ dependencies:
+ html-escaper "^2.0.0"
+
+jest-changed-files@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039"
+ integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ execa "^1.0.0"
+ throat "^4.0.0"
+
+jest-cli@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af"
+ integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==
+ dependencies:
+ "@jest/core" "^24.9.0"
+ "@jest/test-result" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ chalk "^2.0.1"
+ exit "^0.1.2"
+ import-local "^2.0.0"
+ is-ci "^2.0.0"
+ jest-config "^24.9.0"
+ jest-util "^24.9.0"
+ jest-validate "^24.9.0"
+ prompts "^2.0.1"
+ realpath-native "^1.1.0"
+ yargs "^13.3.0"
+
+jest-config@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5"
+ integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==
+ dependencies:
+ "@babel/core" "^7.1.0"
+ "@jest/test-sequencer" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ babel-jest "^24.9.0"
+ chalk "^2.0.1"
+ glob "^7.1.1"
+ jest-environment-jsdom "^24.9.0"
+ jest-environment-node "^24.9.0"
+ jest-get-type "^24.9.0"
+ jest-jasmine2 "^24.9.0"
+ jest-regex-util "^24.3.0"
+ jest-resolve "^24.9.0"
+ jest-util "^24.9.0"
+ jest-validate "^24.9.0"
+ micromatch "^3.1.10"
+ pretty-format "^24.9.0"
+ realpath-native "^1.1.0"
+
+jest-diff@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da"
+ integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==
+ dependencies:
+ chalk "^2.0.1"
+ diff-sequences "^24.9.0"
+ jest-get-type "^24.9.0"
+ pretty-format "^24.9.0"
+
+jest-docblock@^24.3.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2"
+ integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==
+ dependencies:
+ detect-newline "^2.1.0"
+
+jest-each@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05"
+ integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ chalk "^2.0.1"
+ jest-get-type "^24.9.0"
+ jest-util "^24.9.0"
+ pretty-format "^24.9.0"
+
+jest-environment-jsdom@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b"
+ integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==
+ dependencies:
+ "@jest/environment" "^24.9.0"
+ "@jest/fake-timers" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ jest-mock "^24.9.0"
+ jest-util "^24.9.0"
+ jsdom "^11.5.1"
+
+jest-environment-node@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3"
+ integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==
+ dependencies:
+ "@jest/environment" "^24.9.0"
+ "@jest/fake-timers" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ jest-mock "^24.9.0"
+ jest-util "^24.9.0"
+
+jest-get-type@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e"
+ integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==
+
+jest-haste-map@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d"
+ integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ anymatch "^2.0.0"
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.1.15"
+ invariant "^2.2.4"
+ jest-serializer "^24.9.0"
+ jest-util "^24.9.0"
+ jest-worker "^24.9.0"
+ micromatch "^3.1.10"
+ sane "^4.0.3"
+ walker "^1.0.7"
+ optionalDependencies:
+ fsevents "^1.2.7"
+
+jest-jasmine2@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0"
+ integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==
+ dependencies:
+ "@babel/traverse" "^7.1.0"
+ "@jest/environment" "^24.9.0"
+ "@jest/test-result" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ chalk "^2.0.1"
+ co "^4.6.0"
+ expect "^24.9.0"
+ is-generator-fn "^2.0.0"
+ jest-each "^24.9.0"
+ jest-matcher-utils "^24.9.0"
+ jest-message-util "^24.9.0"
+ jest-runtime "^24.9.0"
+ jest-snapshot "^24.9.0"
+ jest-util "^24.9.0"
+ pretty-format "^24.9.0"
+ throat "^4.0.0"
+
+jest-leak-detector@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a"
+ integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==
+ dependencies:
+ jest-get-type "^24.9.0"
+ pretty-format "^24.9.0"
+
+jest-matcher-utils@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073"
+ integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==
+ dependencies:
+ chalk "^2.0.1"
+ jest-diff "^24.9.0"
+ jest-get-type "^24.9.0"
+ pretty-format "^24.9.0"
+
+jest-message-util@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3"
+ integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==
+ dependencies:
+ "@babel/code-frame" "^7.0.0"
+ "@jest/test-result" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ "@types/stack-utils" "^1.0.1"
+ chalk "^2.0.1"
+ micromatch "^3.1.10"
+ slash "^2.0.0"
+ stack-utils "^1.0.1"
+
+jest-mock@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6"
+ integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==
+ dependencies:
+ "@jest/types" "^24.9.0"
+
+jest-pnp-resolver@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
+ integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
+
+jest-regex-util@^24.3.0, jest-regex-util@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636"
+ integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==
+
+jest-resolve-dependencies@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab"
+ integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ jest-regex-util "^24.3.0"
+ jest-snapshot "^24.9.0"
+
+jest-resolve@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321"
+ integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ browser-resolve "^1.11.3"
+ chalk "^2.0.1"
+ jest-pnp-resolver "^1.2.1"
+ realpath-native "^1.1.0"
+
+jest-runner@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42"
+ integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==
+ dependencies:
+ "@jest/console" "^24.7.1"
+ "@jest/environment" "^24.9.0"
+ "@jest/test-result" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ chalk "^2.4.2"
+ exit "^0.1.2"
+ graceful-fs "^4.1.15"
+ jest-config "^24.9.0"
+ jest-docblock "^24.3.0"
+ jest-haste-map "^24.9.0"
+ jest-jasmine2 "^24.9.0"
+ jest-leak-detector "^24.9.0"
+ jest-message-util "^24.9.0"
+ jest-resolve "^24.9.0"
+ jest-runtime "^24.9.0"
+ jest-util "^24.9.0"
+ jest-worker "^24.6.0"
+ source-map-support "^0.5.6"
+ throat "^4.0.0"
+
+jest-runtime@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac"
+ integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==
+ dependencies:
+ "@jest/console" "^24.7.1"
+ "@jest/environment" "^24.9.0"
+ "@jest/source-map" "^24.3.0"
+ "@jest/transform" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ "@types/yargs" "^13.0.0"
+ chalk "^2.0.1"
+ exit "^0.1.2"
+ glob "^7.1.3"
+ graceful-fs "^4.1.15"
+ jest-config "^24.9.0"
+ jest-haste-map "^24.9.0"
+ jest-message-util "^24.9.0"
+ jest-mock "^24.9.0"
+ jest-regex-util "^24.3.0"
+ jest-resolve "^24.9.0"
+ jest-snapshot "^24.9.0"
+ jest-util "^24.9.0"
+ jest-validate "^24.9.0"
+ realpath-native "^1.1.0"
+ slash "^2.0.0"
+ strip-bom "^3.0.0"
+ yargs "^13.3.0"
+
+jest-serializer@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73"
+ integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==
+
+jest-snapshot@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba"
+ integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==
+ dependencies:
+ "@babel/types" "^7.0.0"
+ "@jest/types" "^24.9.0"
+ chalk "^2.0.1"
+ expect "^24.9.0"
+ jest-diff "^24.9.0"
+ jest-get-type "^24.9.0"
+ jest-matcher-utils "^24.9.0"
+ jest-message-util "^24.9.0"
+ jest-resolve "^24.9.0"
+ mkdirp "^0.5.1"
+ natural-compare "^1.4.0"
+ pretty-format "^24.9.0"
+ semver "^6.2.0"
+
+jest-util@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162"
+ integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==
+ dependencies:
+ "@jest/console" "^24.9.0"
+ "@jest/fake-timers" "^24.9.0"
+ "@jest/source-map" "^24.9.0"
+ "@jest/test-result" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ callsites "^3.0.0"
+ chalk "^2.0.1"
+ graceful-fs "^4.1.15"
+ is-ci "^2.0.0"
+ mkdirp "^0.5.1"
+ slash "^2.0.0"
+ source-map "^0.6.0"
+
+jest-validate@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab"
+ integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ camelcase "^5.3.1"
+ chalk "^2.0.1"
+ jest-get-type "^24.9.0"
+ leven "^3.1.0"
+ pretty-format "^24.9.0"
+
+jest-watcher@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b"
+ integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==
+ dependencies:
+ "@jest/test-result" "^24.9.0"
+ "@jest/types" "^24.9.0"
+ "@types/yargs" "^13.0.0"
+ ansi-escapes "^3.0.0"
+ chalk "^2.0.1"
+ jest-util "^24.9.0"
+ string-length "^2.0.0"
+
+jest-worker@^24.6.0, jest-worker@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5"
+ integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==
+ dependencies:
+ merge-stream "^2.0.0"
+ supports-color "^6.1.0"
+
+jest@^24.6.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-24.9.0.tgz#987d290c05a08b52c56188c1002e368edb007171"
+ integrity sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==
+ dependencies:
+ import-local "^2.0.0"
+ jest-cli "^24.9.0"
+
+"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+jsbn@~0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+ integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
+
+jsdom@20.0.0:
+ version "20.0.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.0.tgz#882825ac9cc5e5bbee704ba16143e1fa78361ebf"
+ integrity sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA==
+ dependencies:
+ abab "^2.0.6"
+ acorn "^8.7.1"
+ acorn-globals "^6.0.0"
+ cssom "^0.5.0"
+ cssstyle "^2.3.0"
+ data-urls "^3.0.2"
+ decimal.js "^10.3.1"
+ domexception "^4.0.0"
+ escodegen "^2.0.0"
+ form-data "^4.0.0"
+ html-encoding-sniffer "^3.0.0"
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.1"
+ is-potential-custom-element-name "^1.0.1"
+ nwsapi "^2.2.0"
+ parse5 "^7.0.0"
+ saxes "^6.0.0"
+ symbol-tree "^3.2.4"
+ tough-cookie "^4.0.0"
+ w3c-hr-time "^1.0.2"
+ w3c-xmlserializer "^3.0.0"
+ webidl-conversions "^7.0.0"
+ whatwg-encoding "^2.0.0"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^11.0.0"
+ ws "^8.8.0"
+ xml-name-validator "^4.0.0"
+
+jsdom@^11.5.1:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8"
+ integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==
+ dependencies:
+ abab "^2.0.0"
+ acorn "^5.5.3"
+ acorn-globals "^4.1.0"
+ array-equal "^1.0.0"
+ cssom ">= 0.3.2 < 0.4.0"
+ cssstyle "^1.0.0"
+ data-urls "^1.0.0"
+ domexception "^1.0.1"
+ escodegen "^1.9.1"
+ html-encoding-sniffer "^1.0.2"
+ left-pad "^1.3.0"
+ nwsapi "^2.0.7"
+ parse5 "4.0.0"
+ pn "^1.1.0"
+ request "^2.87.0"
+ request-promise-native "^1.0.5"
+ sax "^1.2.4"
+ symbol-tree "^3.2.2"
+ tough-cookie "^2.3.4"
+ w3c-hr-time "^1.0.1"
+ webidl-conversions "^4.0.2"
+ whatwg-encoding "^1.0.3"
+ whatwg-mimetype "^2.1.0"
+ whatwg-url "^6.4.1"
+ ws "^5.2.0"
+ xml-name-validator "^3.0.0"
+
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+json-parse-better-errors@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
+ integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
+ integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
+
+json-stringify-safe@~5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+ integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
+
+json5@^2.1.2:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
+ integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
+ dependencies:
+ minimist "^1.2.5"
+
+jsprim@^1.2.2:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
+ integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
+ dependencies:
+ assert-plus "1.0.0"
+ extsprintf "1.3.0"
+ json-schema "0.2.3"
+ verror "1.10.0"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+ integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc=
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+ integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+kleur@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
+ integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
+
+left-pad@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
+ integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==
+
+leven@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+ integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
+levn@~0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
+ integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
+ dependencies:
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+
+load-json-file@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
+ integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs=
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^4.0.0"
+ pify "^3.0.0"
+ strip-bom "^3.0.0"
+
+locate-path@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
+ integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
+ dependencies:
+ p-locate "^3.0.0"
+ path-exists "^3.0.0"
+
+lodash.escape@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98"
+ integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=
+
+lodash.flattendeep@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
+ integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
+
+lodash.isequal@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+ integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
+
+lodash.sortby@^4.7.0:
+ version "4.7.0"
+ resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
+ integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
+
+lodash@^4.15.0, lodash@^4.17.15, lodash@^4.17.19:
+ version "4.17.20"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
+ integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
+
+loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+make-dir@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
+ integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
+ dependencies:
+ pify "^4.0.1"
+ semver "^5.6.0"
+
+makeerror@1.0.x:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
+ integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=
+ dependencies:
+ tmpl "1.0.x"
+
+map-cache@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+ integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
+
+map-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+ integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=
+ dependencies:
+ object-visit "^1.0.0"
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+micromatch@^3.1.10, micromatch@^3.1.4:
+ version "3.1.10"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
+ integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==
+ dependencies:
+ arr-diff "^4.0.0"
+ array-unique "^0.3.2"
+ braces "^2.3.1"
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ extglob "^2.0.4"
+ fragment-cache "^0.2.1"
+ kind-of "^6.0.2"
+ nanomatch "^1.2.9"
+ object.pick "^1.3.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.2"
+
+mime-db@1.44.0:
+ version "1.44.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
+ integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+ version "2.1.27"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
+ integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==
+ dependencies:
+ mime-db "1.44.0"
+
+minimatch@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
+ integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
+mixin-deep@^1.2.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566"
+ integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==
+ dependencies:
+ for-in "^1.0.2"
+ is-extendable "^1.0.1"
+
+mkdirp@^0.5.1:
+ version "0.5.5"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
+ integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
+ dependencies:
+ minimist "^1.2.5"
+
+moo@^0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
+ integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+nan@^2.12.1:
+ version "2.14.1"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
+ integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
+
+nanomatch@^1.2.9:
+ version "1.2.13"
+ resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
+ integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==
+ dependencies:
+ arr-diff "^4.0.0"
+ array-unique "^0.3.2"
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ fragment-cache "^0.2.1"
+ is-windows "^1.0.2"
+ kind-of "^6.0.2"
+ object.pick "^1.3.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
+nearley@^2.7.10:
+ version "2.19.7"
+ resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.7.tgz#eafbe3e2d8ccfe70adaa5c026ab1f9709c116218"
+ integrity sha512-Y+KNwhBPcSJKeyQCFjn8B/MIe+DDlhaaDgjVldhy5xtFewIbiQgcbZV8k2gCVwkI1ZsKCnjIYZbR+0Fim5QYgg==
+ dependencies:
+ commander "^2.19.0"
+ moo "^0.5.0"
+ railroad-diagrams "^1.0.0"
+ randexp "0.4.6"
+ semver "^5.4.1"
+
+nice-try@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
+ integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+
+node-fetch@^1.0.1:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
+ integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
+ dependencies:
+ encoding "^0.1.11"
+ is-stream "^1.0.1"
+
+node-int64@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+ integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=
+
+node-modules-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
+ integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
+
+node-notifier@^5.4.2:
+ version "5.4.3"
+ resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50"
+ integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==
+ dependencies:
+ growly "^1.3.0"
+ is-wsl "^1.1.0"
+ semver "^5.5.0"
+ shellwords "^0.1.1"
+ which "^1.3.0"
+
+normalize-package-data@^2.3.2:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8"
+ integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==
+ dependencies:
+ hosted-git-info "^2.1.4"
+ resolve "^1.10.0"
+ semver "2 || 3 || 4 || 5"
+ validate-npm-package-license "^3.0.1"
+
+normalize-path@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
+ integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
+ dependencies:
+ remove-trailing-separator "^1.0.1"
+
+npm-run-path@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
+ integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
+ dependencies:
+ path-key "^2.0.0"
+
+nth-check@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
+ integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+ dependencies:
+ boolbase "~1.0.0"
+
+nwsapi@^2.0.7:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
+ integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
+
+nwsapi@^2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c"
+ integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg==
+
+oauth-sign@~0.9.0:
+ version "0.9.0"
+ resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+ integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+object-assign@^4.1.0, object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-copy@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+ integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw=
+ dependencies:
+ copy-descriptor "^0.1.0"
+ define-property "^0.2.5"
+ kind-of "^3.0.3"
+
+object-inspect@^1.7.0, object-inspect@^1.8.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
+ integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
+
+object-is@^1.0.2, object-is@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81"
+ integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.18.0-next.1"
+
+object-keys@^1.0.12, object-keys@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object-visit@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+ integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=
+ dependencies:
+ isobject "^3.0.0"
+
+object.assign@^4.1.0, object.assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd"
+ integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.18.0-next.0"
+ has-symbols "^1.0.1"
+ object-keys "^1.1.1"
+
+object.entries@^1.1.1, object.entries@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add"
+ integrity sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.5"
+ has "^1.0.3"
+
+object.fromentries@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.2.tgz#4a09c9b9bb3843dd0f89acdb517a794d4f355ac9"
+ integrity sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+
+object.getownpropertydescriptors@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649"
+ integrity sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+
+object.pick@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747"
+ integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=
+ dependencies:
+ isobject "^3.0.1"
+
+object.values@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e"
+ integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.0-next.1"
+ function-bind "^1.1.1"
+ has "^1.0.3"
+
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+optionator@^0.8.1:
+ version "0.8.3"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
+ integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
+ dependencies:
+ deep-is "~0.1.3"
+ fast-levenshtein "~2.0.6"
+ levn "~0.3.0"
+ prelude-ls "~1.1.2"
+ type-check "~0.3.2"
+ word-wrap "~1.2.3"
+
+p-each-series@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71"
+ integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=
+ dependencies:
+ p-reduce "^1.0.0"
+
+p-finally@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+ integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
+
+p-limit@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
+p-locate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
+ integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
+ dependencies:
+ p-limit "^2.0.0"
+
+p-reduce@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
+ integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+parse-json@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
+ integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
+ dependencies:
+ error-ex "^1.3.1"
+ json-parse-better-errors "^1.0.1"
+
+parse5@4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
+ integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==
+
+parse5@^3.0.1:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
+ integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==
+ dependencies:
+ "@types/node" "*"
+
+parse5@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.0.0.tgz#51f74a5257f5fcc536389e8c2d0b3802e1bfa91a"
+ integrity sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==
+ dependencies:
+ entities "^4.3.0"
+
+pascalcase@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
+ integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-key@^2.0.0, path-key@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
+ integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
+
+path-parse@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+ integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
+
+path-type@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
+ integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==
+ dependencies:
+ pify "^3.0.0"
+
+performance-now@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+ integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
+
+pify@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176"
+ integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=
+
+pify@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
+ integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
+
+pirates@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
+ integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
+ dependencies:
+ node-modules-regexp "^1.0.0"
+
+pkg-dir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
+ integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==
+ dependencies:
+ find-up "^3.0.0"
+
+pn@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
+ integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
+
+posix-character-classes@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+ integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
+
+prelude-ls@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+ integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
+
+pretty-format@^24.9.0:
+ version "24.9.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9"
+ integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==
+ dependencies:
+ "@jest/types" "^24.9.0"
+ ansi-regex "^4.0.0"
+ ansi-styles "^3.2.0"
+ react-is "^16.8.4"
+
+promise@^7.1.1:
+ version "7.3.1"
+ resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+ integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
+ dependencies:
+ asap "~2.0.3"
+
+prompts@^2.0.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068"
+ integrity sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==
+ dependencies:
+ kleur "^3.0.3"
+ sisteransi "^1.0.4"
+
+prop-types-exact@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869"
+ integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==
+ dependencies:
+ has "^1.0.3"
+ object.assign "^4.1.0"
+ reflect.ownkeys "^0.2.0"
+
+prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2:
+ version "15.7.2"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+ integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.8.1"
+
+psl@^1.1.28:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
+ integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
+
+psl@^1.1.33:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
+ integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
+
+pump@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+ integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+punycode@^2.1.0, punycode@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@~6.5.2:
+ version "6.5.2"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+ integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+
+querystringify@^2.1.1:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
+ integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
+
+raf@^3.4.1:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
+ integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
+ dependencies:
+ performance-now "^2.1.0"
+
+railroad-diagrams@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
+ integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=
+
+randexp@0.4.6:
+ version "0.4.6"
+ resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"
+ integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==
+ dependencies:
+ discontinuous-range "1.0.0"
+ ret "~0.1.10"
+
+react-dom-factories@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/react-dom-factories/-/react-dom-factories-1.0.2.tgz#eb7705c4db36fb501b3aa38ff759616aa0ff96e0"
+ integrity sha1-63cFxNs2+1AbOqOP91lhaqD/luA=
+
+react-dom@16.4.1:
+ version "16.4.1"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.1.tgz#7f8b0223b3a5fbe205116c56deb85de32685dad6"
+ integrity sha512-1Gin+wghF/7gl4Cqcvr1DxFX2Osz7ugxSwl6gBqCMpdrxHjIFUS7GYxrFftZ9Ln44FHw0JxCFD9YtZsrbR5/4A==
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.0"
+
+react-is@^16.12.0, react-is@^16.13.1, react-is@^16.4.1, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+react-test-renderer@16.4.1:
+ version "16.4.1"
+ resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.4.1.tgz#f2fb30c2c7b517db6e5b10ed20bb6b0a7ccd8d70"
+ integrity sha512-wyyiPxRZOTpKnNIgUBOB6xPLTpIzwcQMIURhZvzUqZzezvHjaGNsDPBhMac5fIY3Jf5NuKxoGvV64zDSOECPPQ==
+ dependencies:
+ fbjs "^0.8.16"
+ object-assign "^4.1.1"
+ prop-types "^15.6.0"
+ react-is "^16.4.1"
+
+react-test-renderer@^16.0.0-0:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.13.1.tgz#de25ea358d9012606de51e012d9742e7f0deabc1"
+ integrity sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==
+ dependencies:
+ object-assign "^4.1.1"
+ prop-types "^15.6.2"
+ react-is "^16.8.6"
+ scheduler "^0.19.1"
+
+react@16.4.1:
+ version "16.4.1"
+ resolved "https://registry.yarnpkg.com/react/-/react-16.4.1.tgz#de51ba5764b5dbcd1f9079037b862bd26b82fe32"
+ integrity sha512-3GEs0giKp6E0Oh/Y9ZC60CmYgUPnp7voH9fbjWsvXtYFb4EWtgQub0ADSq0sJR0BbHc4FThLLtzlcFaFXIorwg==
+ dependencies:
+ fbjs "^0.8.16"
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+ prop-types "^15.6.0"
+
+read-pkg-up@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-4.0.0.tgz#1b221c6088ba7799601c808f91161c66e58f8978"
+ integrity sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==
+ dependencies:
+ find-up "^3.0.0"
+ read-pkg "^3.0.0"
+
+read-pkg@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
+ integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=
+ dependencies:
+ load-json-file "^4.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^3.0.0"
+
+readable-stream@^3.1.1:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+ integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+realpath-native@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
+ integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==
+ dependencies:
+ util.promisify "^1.0.0"
+
+reflect.ownkeys@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
+ integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA=
+
+regex-not@^1.0.0, regex-not@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+ integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
+ dependencies:
+ extend-shallow "^3.0.2"
+ safe-regex "^1.1.0"
+
+remove-trailing-separator@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
+ integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
+
+repeat-element@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce"
+ integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==
+
+repeat-string@^1.6.1:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+ integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
+
+request-promise-core@1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f"
+ integrity sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==
+ dependencies:
+ lodash "^4.17.19"
+
+request-promise-native@^1.0.5:
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.9.tgz#e407120526a5efdc9a39b28a5679bf47b9d9dc28"
+ integrity sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==
+ dependencies:
+ request-promise-core "1.1.4"
+ stealthy-require "^1.1.1"
+ tough-cookie "^2.3.3"
+
+request@^2.87.0:
+ version "2.88.2"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
+ integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
+ dependencies:
+ aws-sign2 "~0.7.0"
+ aws4 "^1.8.0"
+ caseless "~0.12.0"
+ combined-stream "~1.0.6"
+ extend "~3.0.2"
+ forever-agent "~0.6.1"
+ form-data "~2.3.2"
+ har-validator "~5.1.3"
+ http-signature "~1.2.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.19"
+ oauth-sign "~0.9.0"
+ performance-now "^2.1.0"
+ qs "~6.5.2"
+ safe-buffer "^5.1.2"
+ tough-cookie "~2.5.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.3.2"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-main-filename@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
+ integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
+
+requires-port@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
+
+resolve-cwd@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
+ integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=
+ dependencies:
+ resolve-from "^3.0.0"
+
+resolve-from@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+ integrity sha1-six699nWiBvItuZTM17rywoYh0g=
+
+resolve-url@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+ integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
+
+resolve@1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
+ integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
+
+resolve@^1.10.0, resolve@^1.3.2:
+ version "1.17.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
+ integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
+ dependencies:
+ path-parse "^1.0.6"
+
+ret@~0.1.10:
+ version "0.1.15"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
+ integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
+
+rimraf@^2.5.4, rimraf@^2.6.3:
+ version "2.7.1"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+ integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+ dependencies:
+ glob "^7.1.3"
+
+rst-selector-parser@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
+ integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=
+ dependencies:
+ lodash.flattendeep "^4.4.0"
+ nearley "^2.7.10"
+
+rsvp@^4.8.4:
+ version "4.8.5"
+ resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734"
+ integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-regex@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+ integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4=
+ dependencies:
+ ret "~0.1.10"
+
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sane@^4.0.3:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded"
+ integrity sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==
+ dependencies:
+ "@cnakazawa/watch" "^1.0.3"
+ anymatch "^2.0.0"
+ capture-exit "^2.0.0"
+ exec-sh "^0.3.2"
+ execa "^1.0.0"
+ fb-watchman "^2.0.0"
+ micromatch "^3.1.4"
+ minimist "^1.1.1"
+ walker "~1.0.5"
+
+sax@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+saxes@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
+ integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==
+ dependencies:
+ xmlchars "^2.2.0"
+
+scheduler@^0.19.1:
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
+ integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==
+ dependencies:
+ loose-envify "^1.1.0"
+ object-assign "^4.1.1"
+
+"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
+ integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
+
+semver@^6.0.0, semver@^6.2.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+set-blocking@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+ integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+
+set-value@^2.0.0, set-value@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
+ integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-extendable "^0.1.1"
+ is-plain-object "^2.0.3"
+ split-string "^3.0.1"
+
+setimmediate@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
+ integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
+
+shebang-command@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
+ integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=
+ dependencies:
+ shebang-regex "^1.0.0"
+
+shebang-regex@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
+ integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
+
+shellwords@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b"
+ integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==
+
+signal-exit@^3.0.0, signal-exit@^3.0.2:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
+ integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
+
+sisteransi@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
+ integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
+
+slash@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
+ integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
+
+snapdragon-node@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+ integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==
+ dependencies:
+ define-property "^1.0.0"
+ isobject "^3.0.0"
+ snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+ integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==
+ dependencies:
+ kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+ version "0.8.2"
+ resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d"
+ integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==
+ dependencies:
+ base "^0.11.1"
+ debug "^2.2.0"
+ define-property "^0.2.5"
+ extend-shallow "^2.0.1"
+ map-cache "^0.2.2"
+ source-map "^0.5.6"
+ source-map-resolve "^0.5.0"
+ use "^3.1.0"
+
+source-map-resolve@^0.5.0:
+ version "0.5.3"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
+ integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==
+ dependencies:
+ atob "^2.1.2"
+ decode-uri-component "^0.2.0"
+ resolve-url "^0.2.1"
+ source-map-url "^0.4.0"
+ urix "^0.1.0"
+
+source-map-support@^0.5.6:
+ version "0.5.19"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
+ integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map-url@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+ integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=
+
+source-map@^0.5.0, source-map@^0.5.6:
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
+ integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
+
+source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+spdx-correct@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
+ integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
+ dependencies:
+ spdx-expression-parse "^3.0.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-exceptions@^2.1.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d"
+ integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==
+
+spdx-expression-parse@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679"
+ integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==
+ dependencies:
+ spdx-exceptions "^2.1.0"
+ spdx-license-ids "^3.0.0"
+
+spdx-license-ids@^3.0.0:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz#c80757383c28abf7296744998cbc106ae8b854ce"
+ integrity sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==
+
+split-string@^3.0.1, split-string@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+ integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==
+ dependencies:
+ extend-shallow "^3.0.0"
+
+sshpk@^1.7.0:
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
+ integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
+ dependencies:
+ asn1 "~0.2.3"
+ assert-plus "^1.0.0"
+ bcrypt-pbkdf "^1.0.0"
+ dashdash "^1.12.0"
+ ecc-jsbn "~0.1.1"
+ getpass "^0.1.1"
+ jsbn "~0.1.0"
+ safer-buffer "^2.0.2"
+ tweetnacl "~0.14.0"
+
+stack-utils@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8"
+ integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==
+
+static-extend@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+ integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=
+ dependencies:
+ define-property "^0.2.5"
+ object-copy "^0.1.0"
+
+stealthy-require@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
+ integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=
+
+string-length@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
+ integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=
+ dependencies:
+ astral-regex "^1.0.0"
+ strip-ansi "^4.0.0"
+
+string-width@^3.0.0, string-width@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
+ integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
+ dependencies:
+ emoji-regex "^7.0.1"
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^5.1.0"
+
+string.prototype.trim@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.2.tgz#f538d0bacd98fc4297f0bef645226d5aaebf59f3"
+ integrity sha512-b5yrbl3BXIjHau9Prk7U0RRYcUYdN4wGSVaqoBQS50CCE3KBuYU0TYRNPFCP7aVoNMX87HKThdMRVIP3giclKg==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.18.0-next.0"
+
+string.prototype.trimend@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
+ integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.5"
+
+string.prototype.trimstart@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
+ integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.5"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+strip-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
+ integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
+ dependencies:
+ ansi-regex "^3.0.0"
+
+strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
+ integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
+ dependencies:
+ ansi-regex "^4.1.0"
+
+strip-bom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+ integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
+strip-eof@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
+ integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
+ integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
+ dependencies:
+ has-flag "^3.0.0"
+
+symbol-tree@^3.2.2, symbol-tree@^3.2.4:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
+ integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
+
+test-exclude@^5.2.3:
+ version "5.2.3"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.2.3.tgz#c3d3e1e311eb7ee405e092dac10aefd09091eac0"
+ integrity sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==
+ dependencies:
+ glob "^7.1.3"
+ minimatch "^3.0.4"
+ read-pkg-up "^4.0.0"
+ require-main-filename "^2.0.0"
+
+throat@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a"
+ integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=
+
+tmpl@1.0.x:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
+ integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-object-path@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
+ integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=
+ dependencies:
+ kind-of "^3.0.2"
+
+to-regex-range@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
+ integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=
+ dependencies:
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
+
+to-regex@^3.0.1, to-regex@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce"
+ integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==
+ dependencies:
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ regex-not "^1.0.2"
+ safe-regex "^1.1.0"
+
+tough-cookie@^2.3.3, tough-cookie@^2.3.4, tough-cookie@~2.5.0:
+ version "2.5.0"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+ integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+ dependencies:
+ psl "^1.1.28"
+ punycode "^2.1.1"
+
+tough-cookie@^4.0.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874"
+ integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==
+ dependencies:
+ psl "^1.1.33"
+ punycode "^2.1.1"
+ universalify "^0.2.0"
+ url-parse "^1.5.3"
+
+tr46@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
+ integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
+ dependencies:
+ punycode "^2.1.0"
+
+tr46@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
+ integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
+ dependencies:
+ punycode "^2.1.1"
+
+tunnel-agent@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+ integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=
+ dependencies:
+ safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+ integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
+
+type-check@~0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
+ integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
+ dependencies:
+ prelude-ls "~1.1.2"
+
+ua-parser-js@^0.7.18:
+ version "0.7.22"
+ resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.22.tgz#960df60a5f911ea8f1c818f3747b99c6e177eae3"
+ integrity sha512-YUxzMjJ5T71w6a8WWVcMGM6YWOTX27rCoIQgLXiWaxqXSx9D7DNjiGWn1aJIRSQ5qr0xuhra77bSIh6voR/46Q==
+
+union-value@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847"
+ integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==
+ dependencies:
+ arr-union "^3.1.0"
+ get-value "^2.0.6"
+ is-extendable "^0.1.1"
+ set-value "^2.0.1"
+
+universalify@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0"
+ integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==
+
+unset-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
+ integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=
+ dependencies:
+ has-value "^0.3.1"
+ isobject "^3.0.0"
+
+uri-js@^4.2.2:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
+ integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==
+ dependencies:
+ punycode "^2.1.0"
+
+urix@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
+ integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=
+
+url-parse@^1.5.3:
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
+ integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
+ dependencies:
+ querystringify "^2.1.1"
+ requires-port "^1.0.0"
+
+use@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
+ integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
+
+util-deprecate@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+util.promisify@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
+ integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
+ dependencies:
+ define-properties "^1.1.3"
+ es-abstract "^1.17.2"
+ has-symbols "^1.0.1"
+ object.getownpropertydescriptors "^2.1.0"
+
+uuid@^3.3.2:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+ integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
+validate-npm-package-license@^3.0.1:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
+ integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==
+ dependencies:
+ spdx-correct "^3.0.0"
+ spdx-expression-parse "^3.0.0"
+
+verror@1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+ integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
+ dependencies:
+ assert-plus "^1.0.0"
+ core-util-is "1.0.2"
+ extsprintf "^1.2.0"
+
+w3c-hr-time@^1.0.1, w3c-hr-time@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
+ integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
+ dependencies:
+ browser-process-hrtime "^1.0.0"
+
+w3c-xmlserializer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
+ integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
+ dependencies:
+ xml-name-validator "^4.0.0"
+
+walker@^1.0.7, walker@~1.0.5:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
+ integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=
+ dependencies:
+ makeerror "1.0.x"
+
+webidl-conversions@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
+ integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
+
+webidl-conversions@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+ integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
+
+whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
+ integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
+ dependencies:
+ iconv-lite "0.4.24"
+
+whatwg-encoding@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
+ integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
+ dependencies:
+ iconv-lite "0.6.3"
+
+whatwg-fetch@>=0.10.0:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz#e5f871572d6879663fa5674c8f833f15a8425ab3"
+ integrity sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==
+
+whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
+ integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+
+whatwg-mimetype@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
+ integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
+
+whatwg-url@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
+ integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
+ dependencies:
+ tr46 "^3.0.0"
+ webidl-conversions "^7.0.0"
+
+whatwg-url@^6.4.1:
+ version "6.5.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
+ integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==
+ dependencies:
+ lodash.sortby "^4.7.0"
+ tr46 "^1.0.1"
+ webidl-conversions "^4.0.2"
+
+whatwg-url@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
+ integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
+ dependencies:
+ lodash.sortby "^4.7.0"
+ tr46 "^1.0.1"
+ webidl-conversions "^4.0.2"
+
+which-module@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
+ integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
+
+which@^1.2.9, which@^1.3.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+ integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ dependencies:
+ isexe "^2.0.0"
+
+word-wrap@~1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrap-ansi@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"
+ integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==
+ dependencies:
+ ansi-styles "^3.2.0"
+ string-width "^3.0.0"
+ strip-ansi "^5.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+write-file-atomic@2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529"
+ integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==
+ dependencies:
+ graceful-fs "^4.1.11"
+ imurmurhash "^0.1.4"
+ signal-exit "^3.0.2"
+
+ws@^5.2.0:
+ version "5.2.2"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
+ integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
+ dependencies:
+ async-limiter "~1.0.0"
+
+ws@^8.8.0:
+ version "8.8.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
+ integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
+
+xml-name-validator@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
+ integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+
+xml-name-validator@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
+ integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+
+xmlchars@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
+ integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
+
+y18n@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+ integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+
+yargs-parser@^13.1.2:
+ version "13.1.2"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
+ integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
+ dependencies:
+ camelcase "^5.0.0"
+ decamelize "^1.2.0"
+
+yargs@^13.3.0:
+ version "13.3.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd"
+ integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==
+ dependencies:
+ cliui "^5.0.0"
+ find-up "^3.0.0"
+ get-caller-file "^2.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^2.0.0"
+ set-blocking "^2.0.0"
+ string-width "^3.0.0"
+ which-module "^2.0.0"
+ y18n "^4.0.0"
+ yargs-parser "^13.1.2"
diff --git a/devtools/client/shared/components/throttling/NetworkThrottlingMenu.js b/devtools/client/shared/components/throttling/NetworkThrottlingMenu.js
new file mode 100644
index 0000000000..d74016686b
--- /dev/null
+++ b/devtools/client/shared/components/throttling/NetworkThrottlingMenu.js
@@ -0,0 +1,100 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ PureComponent,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const dom = require("resource://devtools/client/shared/vendor/react-dom-factories.js");
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+const throttlingProfiles = require("resource://devtools/client/shared/components/throttling/profiles.js");
+const Types = require("resource://devtools/client/shared/components/throttling/types.js");
+
+// Localization
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/network-throttling.properties"
+);
+const NO_THROTTLING_LABEL = L10N.getStr("responsive.noThrottling");
+
+loader.lazyRequireGetter(
+ this,
+ "showMenu",
+ "resource://devtools/client/shared/components/menu/utils.js",
+ true
+);
+
+/**
+ * This component represents selector button that can be used
+ * to throttle network bandwidth.
+ */
+class NetworkThrottlingMenu extends PureComponent {
+ static get propTypes() {
+ return {
+ networkThrottling: PropTypes.shape(Types.networkThrottling).isRequired,
+ onChangeNetworkThrottling: PropTypes.func.isRequired,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.onShowThrottlingMenu = this.onShowThrottlingMenu.bind(this);
+ }
+
+ onShowThrottlingMenu(event) {
+ const { networkThrottling, onChangeNetworkThrottling } = this.props;
+
+ const menuItems = throttlingProfiles.profiles.map(profile => {
+ return {
+ label: profile.id,
+ type: "checkbox",
+ checked:
+ networkThrottling.enabled && profile.id == networkThrottling.profile,
+ click: () => onChangeNetworkThrottling(true, profile.id),
+ };
+ });
+
+ menuItems.unshift("-");
+
+ menuItems.unshift({
+ label: NO_THROTTLING_LABEL,
+ type: "checkbox",
+ checked: !networkThrottling.enabled,
+ click: () => onChangeNetworkThrottling(false, ""),
+ });
+
+ showMenu(menuItems, { button: event.target });
+ }
+
+ render() {
+ const { networkThrottling } = this.props;
+ const label = networkThrottling.enabled
+ ? networkThrottling.profile
+ : NO_THROTTLING_LABEL;
+
+ let title = NO_THROTTLING_LABEL;
+
+ if (networkThrottling.enabled) {
+ const id = networkThrottling.profile;
+ const selectedProfile = throttlingProfiles.profiles.find(
+ profile => profile.id === id
+ );
+ title = selectedProfile.description;
+ }
+
+ return dom.button(
+ {
+ id: "network-throttling-menu",
+ className: "devtools-button devtools-dropdown-button",
+ title,
+ onClick: this.onShowThrottlingMenu,
+ },
+ dom.span({ className: "title" }, label)
+ );
+ }
+}
+
+module.exports = NetworkThrottlingMenu;
diff --git a/devtools/client/shared/components/throttling/actions.js b/devtools/client/shared/components/throttling/actions.js
new file mode 100644
index 0000000000..b6a686ab22
--- /dev/null
+++ b/devtools/client/shared/components/throttling/actions.js
@@ -0,0 +1,22 @@
+/* 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/. */
+
+"use strict";
+
+const actionTypes = {
+ CHANGE_NETWORK_THROTTLING: "CHANGE_NETWORK_THROTTLING",
+};
+
+function changeNetworkThrottling(enabled, profile) {
+ return {
+ type: actionTypes.CHANGE_NETWORK_THROTTLING,
+ enabled,
+ profile,
+ };
+}
+
+module.exports = {
+ ...actionTypes,
+ changeNetworkThrottling,
+};
diff --git a/devtools/client/shared/components/throttling/moz.build b/devtools/client/shared/components/throttling/moz.build
new file mode 100644
index 0000000000..2c178219fc
--- /dev/null
+++ b/devtools/client/shared/components/throttling/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "actions.js",
+ "NetworkThrottlingMenu.js",
+ "profiles.js",
+ "reducer.js",
+ "types.js",
+)
diff --git a/devtools/client/shared/components/throttling/profiles.js b/devtools/client/shared/components/throttling/profiles.js
new file mode 100644
index 0000000000..cd61f70772
--- /dev/null
+++ b/devtools/client/shared/components/throttling/profiles.js
@@ -0,0 +1,122 @@
+/* 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/. */
+
+"use strict";
+
+const K = 1024;
+const M = 1024 * 1024;
+const Bps = 1 / 8;
+const KBps = K * Bps;
+const MBps = M * Bps;
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/network-throttling.properties"
+);
+
+/**
+ * Predefined network throttling profiles.
+ * Speeds are in bytes per second. Latency is in ms.
+ */
+
+class ThrottlingProfile {
+ constructor({ id, download, upload, latency }) {
+ this.id = id;
+ this.download = download;
+ this.upload = upload;
+ this.latency = latency;
+ }
+
+ get description() {
+ const download = this.#toDescriptionData(this.download);
+ const upload = this.#toDescriptionData(this.upload);
+ return L10N.getFormatStr(
+ "throttling.profile.description",
+ download.value,
+ download.unit,
+ upload.value,
+ upload.unit,
+ this.latency
+ );
+ }
+
+ #toDescriptionData(val) {
+ if (val % MBps === 0) {
+ return { value: val / MBps, unit: "Mbps" };
+ }
+ return { value: val / KBps, unit: "Kbps" };
+ }
+}
+
+const PROFILE_CONSTANTS = {
+ GPRS: "GPRS",
+ REGULAR_2G: "Regular 2G",
+ GOOD_2G: "Good 2G",
+ REGULAR_3G: "Regular 3G",
+ GOOD_3G: "Good 3G",
+ REGULAR_4G_LTE: "Regular 4G / LTE",
+ DSL: "DSL",
+ WIFI: "Wi-Fi",
+ OFFLINE: "Offline",
+};
+
+// Should be synced with devtools/docs/user/network_monitor/throttling/index.rst
+const profiles = [
+ {
+ id: PROFILE_CONSTANTS.GPRS,
+ download: 50 * KBps,
+ upload: 20 * KBps,
+ latency: 500,
+ },
+ {
+ id: PROFILE_CONSTANTS.REGULAR_2G,
+ download: 250 * KBps,
+ upload: 50 * KBps,
+ latency: 300,
+ },
+ {
+ id: PROFILE_CONSTANTS.GOOD_2G,
+ download: 450 * KBps,
+ upload: 150 * KBps,
+ latency: 150,
+ },
+ {
+ id: PROFILE_CONSTANTS.REGULAR_3G,
+ download: 750 * KBps,
+ upload: 250 * KBps,
+ latency: 100,
+ },
+ {
+ id: PROFILE_CONSTANTS.GOOD_3G,
+ download: 1.5 * MBps,
+ upload: 750 * KBps,
+ latency: 40,
+ },
+ {
+ id: PROFILE_CONSTANTS.REGULAR_4G_LTE,
+ download: 4 * MBps,
+ upload: 3 * MBps,
+ latency: 20,
+ },
+ {
+ id: PROFILE_CONSTANTS.DSL,
+ download: 2 * MBps,
+ upload: 1 * MBps,
+ latency: 5,
+ },
+ {
+ id: PROFILE_CONSTANTS.WIFI,
+ download: 30 * MBps,
+ upload: 15 * MBps,
+ latency: 2,
+ },
+ {
+ id: PROFILE_CONSTANTS.OFFLINE,
+ download: 0,
+ upload: 0,
+ latency: 5,
+ },
+].map(profile => new ThrottlingProfile(profile));
+
+module.exports = { profiles, PROFILE_CONSTANTS };
diff --git a/devtools/client/shared/components/throttling/reducer.js b/devtools/client/shared/components/throttling/reducer.js
new file mode 100644
index 0000000000..ea6408fe9f
--- /dev/null
+++ b/devtools/client/shared/components/throttling/reducer.js
@@ -0,0 +1,29 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ CHANGE_NETWORK_THROTTLING,
+} = require("resource://devtools/client/shared/components/throttling/actions.js");
+
+const INITIAL_STATE = {
+ enabled: false,
+ profile: "",
+};
+
+function throttlingReducer(state = INITIAL_STATE, action) {
+ switch (action.type) {
+ case CHANGE_NETWORK_THROTTLING: {
+ return {
+ enabled: action.enabled,
+ profile: action.profile,
+ };
+ }
+ default:
+ return state;
+ }
+}
+
+module.exports = throttlingReducer;
diff --git a/devtools/client/shared/components/throttling/types.js b/devtools/client/shared/components/throttling/types.js
new file mode 100644
index 0000000000..a54797057d
--- /dev/null
+++ b/devtools/client/shared/components/throttling/types.js
@@ -0,0 +1,17 @@
+/* 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/. */
+
+"use strict";
+
+const PropTypes = require("resource://devtools/client/shared/vendor/react-prop-types.js");
+
+/**
+ * Network throttling state.
+ */
+exports.networkThrottling = {
+ // Whether or not network throttling is enabled
+ enabled: PropTypes.bool,
+ // Name of the selected throttling profile
+ profile: PropTypes.string,
+};
diff --git a/devtools/client/shared/components/tree/LabelCell.js b/devtools/client/shared/components/tree/LabelCell.js
new file mode 100644
index 0000000000..e42a9dfd1c
--- /dev/null
+++ b/devtools/client/shared/components/tree/LabelCell.js
@@ -0,0 +1,76 @@
+/* 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/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ const { Component } = require("devtools/client/shared/vendor/react");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+
+ /**
+ * Render the default cell used for toggle buttons
+ */
+ class LabelCell extends Component {
+ // See the TreeView component for details related
+ // to the 'member' object.
+ static get propTypes() {
+ return {
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string,
+ member: PropTypes.object.isRequired,
+ renderSuffix: PropTypes.func,
+ };
+ }
+
+ render() {
+ const id = this.props.id;
+ const title = this.props.title;
+ const member = this.props.member;
+ const level = member.level || 0;
+ const renderSuffix = this.props.renderSuffix;
+
+ const iconClassList = ["treeIcon"];
+ if (member.hasChildren && member.loading) {
+ iconClassList.push("devtools-throbber");
+ } else if (member.hasChildren) {
+ iconClassList.push("theme-twisty");
+ }
+ if (member.open) {
+ iconClassList.push("open");
+ }
+
+ return dom.td(
+ {
+ className: "treeLabelCell",
+ title,
+ style: {
+ // Compute indentation dynamically. The deeper the item is
+ // inside the hierarchy, the bigger is the left padding.
+ "--tree-label-cell-indent": `${level * 16}px`,
+ },
+ key: "default",
+ role: "presentation",
+ },
+ dom.span({
+ className: iconClassList.join(" "),
+ role: "presentation",
+ }),
+ dom.span(
+ {
+ className: "treeLabel " + member.type + "Label",
+ title,
+ "aria-labelledby": id,
+ "data-level": level,
+ },
+ member.name
+ ),
+ renderSuffix && renderSuffix(member)
+ );
+ }
+ }
+
+ // Exports from this module
+ module.exports = LabelCell;
+});
diff --git a/devtools/client/shared/components/tree/ObjectProvider.js b/devtools/client/shared/components/tree/ObjectProvider.js
new file mode 100644
index 0000000000..48d577ff4d
--- /dev/null
+++ b/devtools/client/shared/components/tree/ObjectProvider.js
@@ -0,0 +1,86 @@
+/* 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/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ /**
+ * Implementation of the default data provider. A provider is state less
+ * object responsible for transformation data (usually a state) to
+ * a structure that can be directly consumed by the tree-view component.
+ */
+ const ObjectProvider = {
+ getChildren(object) {
+ const children = [];
+
+ if (object instanceof ObjectProperty) {
+ object = object.value;
+ }
+
+ if (!object) {
+ return [];
+ }
+
+ if (typeof object == "string") {
+ return [];
+ }
+
+ for (const prop in object) {
+ try {
+ children.push(new ObjectProperty(prop, object[prop]));
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ return children;
+ },
+
+ hasChildren(object) {
+ if (object instanceof ObjectProperty) {
+ object = object.value;
+ }
+
+ if (!object) {
+ return false;
+ }
+
+ if (typeof object == "string") {
+ return false;
+ }
+
+ if (typeof object !== "object") {
+ return false;
+ }
+
+ return !!Object.keys(object).length;
+ },
+
+ getLabel(object) {
+ return object instanceof ObjectProperty ? object.name : null;
+ },
+
+ getValue(object) {
+ return object instanceof ObjectProperty ? object.value : null;
+ },
+
+ getKey(object) {
+ return object instanceof ObjectProperty ? object.name : null;
+ },
+
+ getType(object) {
+ return object instanceof ObjectProperty
+ ? typeof object.value
+ : typeof object;
+ },
+ };
+
+ function ObjectProperty(name, value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ // Exports from this module
+ exports.ObjectProperty = ObjectProperty;
+ exports.ObjectProvider = ObjectProvider;
+});
diff --git a/devtools/client/shared/components/tree/TreeCell.js b/devtools/client/shared/components/tree/TreeCell.js
new file mode 100644
index 0000000000..1df830431f
--- /dev/null
+++ b/devtools/client/shared/components/tree/TreeCell.js
@@ -0,0 +1,139 @@
+/* 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/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ const { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { input, span, td } = dom;
+
+ /**
+ * This template represents a cell in TreeView row. It's rendered
+ * using <td> element (the row is <tr> and the entire tree is <table>).
+ */
+ class TreeCell extends Component {
+ // See TreeView component for detailed property explanation.
+ static get propTypes() {
+ return {
+ value: PropTypes.any,
+ decorator: PropTypes.object,
+ id: PropTypes.string.isRequired,
+ member: PropTypes.object.isRequired,
+ renderValue: PropTypes.func.isRequired,
+ enableInput: PropTypes.bool,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ inputEnabled: false,
+ };
+
+ this.getCellClass = this.getCellClass.bind(this);
+ this.updateInputEnabled = this.updateInputEnabled.bind(this);
+ }
+
+ /**
+ * Optimize cell rendering. Rerender cell content only if
+ * the value or expanded state changes.
+ */
+ shouldComponentUpdate(nextProps, nextState) {
+ return (
+ this.props.value !== nextProps.value ||
+ this.state !== nextState ||
+ this.props.member.open !== nextProps.member.open
+ );
+ }
+
+ getCellClass(object, id) {
+ const decorator = this.props.decorator;
+ if (!decorator || !decorator.getCellClass) {
+ return [];
+ }
+
+ // Decorator can return a simple string or array of strings.
+ let classNames = decorator.getCellClass(object, id);
+ if (!classNames) {
+ return [];
+ }
+
+ if (typeof classNames == "string") {
+ classNames = [classNames];
+ }
+
+ return classNames;
+ }
+
+ updateInputEnabled(evt) {
+ this.setState(
+ Object.assign({}, this.state, {
+ inputEnabled: evt.target.nodeName.toLowerCase() !== "input",
+ })
+ );
+ }
+
+ render() {
+ let { member, id, value, decorator, renderValue, enableInput } =
+ this.props;
+ const type = member.type || "";
+
+ // Compute class name list for the <td> element.
+ const classNames = this.getCellClass(member.object, id) || [];
+ classNames.push("treeValueCell");
+ classNames.push(type + "Cell");
+
+ // Render value using a default render function or custom
+ // provided function from props or a decorator.
+ renderValue = renderValue || defaultRenderValue;
+ if (decorator?.renderValue) {
+ renderValue = decorator.renderValue(member.object, id) || renderValue;
+ }
+
+ const props = Object.assign({}, this.props, {
+ object: value,
+ });
+
+ let cellElement;
+ if (enableInput && this.state.inputEnabled && type !== "object") {
+ classNames.push("inputEnabled");
+ cellElement = input({
+ autoFocus: true,
+ onBlur: this.updateInputEnabled,
+ readOnly: true,
+ value,
+ "aria-labelledby": id,
+ });
+ } else {
+ cellElement = span(
+ {
+ onClick: type !== "object" ? this.updateInputEnabled : null,
+ "aria-labelledby": id,
+ },
+ renderValue(props)
+ );
+ }
+
+ // Render me!
+ return td(
+ {
+ className: classNames.join(" "),
+ role: "presentation",
+ },
+ cellElement
+ );
+ }
+ }
+
+ // Default value rendering.
+ const defaultRenderValue = props => {
+ return props.object + "";
+ };
+
+ // Exports from this module
+ module.exports = TreeCell;
+});
diff --git a/devtools/client/shared/components/tree/TreeHeader.js b/devtools/client/shared/components/tree/TreeHeader.js
new file mode 100644
index 0000000000..41986210fc
--- /dev/null
+++ b/devtools/client/shared/components/tree/TreeHeader.js
@@ -0,0 +1,120 @@
+/* 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/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ const { Component } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { thead, tr, td, div } = dom;
+
+ /**
+ * This component is responsible for rendering tree header.
+ * It's based on <thead> element.
+ */
+ class TreeHeader extends Component {
+ // See also TreeView component for detailed info about properties.
+ static get propTypes() {
+ return {
+ // Custom tree decorator
+ decorator: PropTypes.object,
+ // True if the header should be visible
+ header: PropTypes.bool,
+ // Array with column definition
+ columns: PropTypes.array,
+ };
+ }
+
+ static get defaultProps() {
+ return {
+ columns: [
+ {
+ id: "default",
+ },
+ ],
+ };
+ }
+
+ constructor(props) {
+ super(props);
+ this.getHeaderClass = this.getHeaderClass.bind(this);
+ }
+
+ getHeaderClass(colId) {
+ const decorator = this.props.decorator;
+ if (!decorator || !decorator.getHeaderClass) {
+ return [];
+ }
+
+ // Decorator can return a simple string or array of strings.
+ let classNames = decorator.getHeaderClass(colId);
+ if (!classNames) {
+ return [];
+ }
+
+ if (typeof classNames == "string") {
+ classNames = [classNames];
+ }
+
+ return classNames;
+ }
+
+ render() {
+ const cells = [];
+ const visible = this.props.header;
+
+ // Render the rest of the columns (if any)
+ this.props.columns.forEach(col => {
+ const cellStyle = {
+ width: col.width ? col.width : "",
+ };
+
+ let classNames = [];
+
+ if (visible) {
+ classNames = this.getHeaderClass(col.id);
+ classNames.push("treeHeaderCell");
+ }
+
+ cells.push(
+ td(
+ {
+ className: classNames.join(" "),
+ style: cellStyle,
+ role: "presentation",
+ id: col.id,
+ key: col.id,
+ },
+ visible
+ ? div(
+ {
+ className: "treeHeaderCellBox",
+ role: "presentation",
+ },
+ col.title
+ )
+ : null
+ )
+ );
+ });
+
+ return thead(
+ {
+ role: "presentation",
+ },
+ tr(
+ {
+ className: visible ? "treeHeaderRow" : "",
+ role: "presentation",
+ },
+ cells
+ )
+ );
+ }
+ }
+
+ // Exports from this module
+ module.exports = TreeHeader;
+});
diff --git a/devtools/client/shared/components/tree/TreeRow.js b/devtools/client/shared/components/tree/TreeRow.js
new file mode 100644
index 0000000000..23fe0227f7
--- /dev/null
+++ b/devtools/client/shared/components/tree/TreeRow.js
@@ -0,0 +1,304 @@
+/* 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/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ const {
+ Component,
+ createFactory,
+ createRef,
+ } = require("devtools/client/shared/vendor/react");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
+ const { tr } = dom;
+
+ // Tree
+ const TreeCell = createFactory(
+ require("devtools/client/shared/components/tree/TreeCell")
+ );
+ const LabelCell = createFactory(
+ require("devtools/client/shared/components/tree/LabelCell")
+ );
+
+ const {
+ wrapMoveFocus,
+ getFocusableElements,
+ } = require("devtools/client/shared/focus");
+
+ const UPDATE_ON_PROPS = [
+ "name",
+ "open",
+ "value",
+ "loading",
+ "level",
+ "selected",
+ "active",
+ "hasChildren",
+ ];
+
+ /**
+ * This template represents a node in TreeView component. It's rendered
+ * using <tr> element (the entire tree is one big <table>).
+ */
+ class TreeRow extends Component {
+ // See TreeView component for more details about the props and
+ // the 'member' object.
+ static get propTypes() {
+ return {
+ member: PropTypes.shape({
+ object: PropTypes.object,
+ name: PropTypes.string,
+ type: PropTypes.string.isRequired,
+ rowClass: PropTypes.string.isRequired,
+ level: PropTypes.number.isRequired,
+ hasChildren: PropTypes.bool,
+ value: PropTypes.any,
+ open: PropTypes.bool.isRequired,
+ path: PropTypes.string.isRequired,
+ hidden: PropTypes.bool,
+ selected: PropTypes.bool,
+ active: PropTypes.bool,
+ loading: PropTypes.bool,
+ }),
+ decorator: PropTypes.object,
+ renderCell: PropTypes.func,
+ renderLabelCell: PropTypes.func,
+ columns: PropTypes.array.isRequired,
+ id: PropTypes.string.isRequired,
+ provider: PropTypes.object.isRequired,
+ onClick: PropTypes.func.isRequired,
+ onContextMenu: PropTypes.func,
+ onMouseOver: PropTypes.func,
+ onMouseOut: PropTypes.func,
+ };
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.treeRowRef = createRef();
+
+ this.getRowClass = this.getRowClass.bind(this);
+ this._onKeyDown = this._onKeyDown.bind(this);
+ }
+
+ componentDidMount() {
+ this._setTabbableState();
+
+ // Child components might add/remove new focusable elements, watch for the
+ // additions/removals of descendant nodes and update focusable state.
+ const win = this.treeRowRef.current.ownerDocument.defaultView;
+ const { MutationObserver } = win;
+ this.observer = new MutationObserver(() => {
+ this._setTabbableState();
+ });
+ this.observer.observe(this.treeRowRef.current, {
+ childList: true,
+ subtree: true,
+ });
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ // I don't like accessing the underlying DOM elements directly,
+ // but this optimization makes the filtering so damn fast!
+ // The row doesn't have to be re-rendered, all we really need
+ // to do is toggling a class name.
+ // The important part is that DOM elements don't need to be
+ // re-created when they should appear again.
+ if (nextProps.member.hidden != this.props.member.hidden) {
+ const row = findDOMNode(this);
+ row.classList.toggle("hidden");
+ }
+ }
+
+ /**
+ * Optimize row rendering. If props are the same do not render.
+ * This makes the rendering a lot faster!
+ */
+ shouldComponentUpdate(nextProps) {
+ for (const prop of UPDATE_ON_PROPS) {
+ if (nextProps.member[prop] !== this.props.member[prop]) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ componentWillUnmount() {
+ this.observer.disconnect();
+ this.observer = null;
+ }
+
+ /**
+ * Makes sure that none of the focusable elements inside the row container
+ * are tabbable if the row is not active. If the row is active and focus
+ * is outside its container, focus on the first focusable element inside.
+ */
+ _setTabbableState() {
+ const elms = getFocusableElements(this.treeRowRef.current);
+ if (elms.length === 0) {
+ return;
+ }
+
+ const { active } = this.props.member;
+ if (!active) {
+ elms.forEach(elm => elm.setAttribute("tabindex", "-1"));
+ return;
+ }
+
+ if (!elms.includes(document.activeElement)) {
+ elms[0].focus();
+ }
+ }
+
+ _onKeyDown(e) {
+ const { target, key, shiftKey } = e;
+
+ if (key !== "Tab") {
+ return;
+ }
+
+ const focusMoved = !!wrapMoveFocus(
+ getFocusableElements(this.treeRowRef.current),
+ target,
+ shiftKey
+ );
+ if (focusMoved) {
+ // Focus was moved to the begining/end of the list, so we need to
+ // prevent the default focus change that would happen here.
+ e.preventDefault();
+ }
+
+ e.stopPropagation();
+ }
+
+ getRowClass(object) {
+ const decorator = this.props.decorator;
+ if (!decorator || !decorator.getRowClass) {
+ return [];
+ }
+
+ // Decorator can return a simple string or array of strings.
+ let classNames = decorator.getRowClass(object);
+ if (!classNames) {
+ return [];
+ }
+
+ if (typeof classNames == "string") {
+ classNames = [classNames];
+ }
+
+ return classNames;
+ }
+
+ render() {
+ const member = this.props.member;
+ const decorator = this.props.decorator;
+
+ const props = {
+ id: this.props.id,
+ ref: this.treeRowRef,
+ role: "treeitem",
+ "aria-level": member.level + 1,
+ "aria-selected": !!member.selected,
+ onClick: this.props.onClick,
+ onContextMenu: this.props.onContextMenu,
+ onKeyDownCapture: member.active ? this._onKeyDown : undefined,
+ onMouseOver: this.props.onMouseOver,
+ onMouseOut: this.props.onMouseOut,
+ };
+
+ // Compute class name list for the <tr> element.
+ const classNames = this.getRowClass(member.object) || [];
+ classNames.push("treeRow");
+ classNames.push(member.type + "Row");
+
+ if (member.hasChildren) {
+ classNames.push("hasChildren");
+
+ // There are 2 situations where hasChildren is true:
+ // 1. it is an object with children. Only set aria-expanded in this situation
+ // 2. It is a long string (> 50 chars) that can be expanded to fully display it
+ if (member.type !== "string") {
+ props["aria-expanded"] = member.open;
+ }
+ }
+
+ if (member.open) {
+ classNames.push("opened");
+ }
+
+ if (member.loading) {
+ classNames.push("loading");
+ }
+
+ if (member.selected) {
+ classNames.push("selected");
+ }
+
+ if (member.hidden) {
+ classNames.push("hidden");
+ }
+
+ props.className = classNames.join(" ");
+
+ // The label column (with toggle buttons) is usually
+ // the first one, but there might be cases (like in
+ // the Memory panel) where the toggling is done
+ // in the last column.
+ const cells = [];
+
+ // Get components for rendering cells.
+ let renderCell = this.props.renderCell || RenderCell;
+ let renderLabelCell = this.props.renderLabelCell || RenderLabelCell;
+ if (decorator?.renderLabelCell) {
+ renderLabelCell =
+ decorator.renderLabelCell(member.object) || renderLabelCell;
+ }
+
+ // Render a cell for every column.
+ this.props.columns.forEach(col => {
+ const cellProps = Object.assign({}, this.props, {
+ key: col.id,
+ id: col.id,
+ value: this.props.provider.getValue(member.object, col.id),
+ });
+
+ if (decorator?.renderCell) {
+ renderCell = decorator.renderCell(member.object, col.id);
+ }
+
+ const render = col.id == "default" ? renderLabelCell : renderCell;
+
+ // Some cells don't have to be rendered. This happens when some
+ // other cells span more columns. Note that the label cells contains
+ // toggle buttons and should be usually there unless we are rendering
+ // a simple non-expandable table.
+ if (render) {
+ cells.push(render(cellProps));
+ }
+ });
+
+ // Render tree row
+ return tr(props, cells);
+ }
+ }
+
+ // Helpers
+
+ const RenderCell = props => {
+ return TreeCell(props);
+ };
+
+ const RenderLabelCell = props => {
+ return LabelCell(props);
+ };
+
+ // Exports from this module
+ module.exports = TreeRow;
+});
diff --git a/devtools/client/shared/components/tree/TreeView.css b/devtools/client/shared/components/tree/TreeView.css
new file mode 100644
index 0000000000..c09df2f557
--- /dev/null
+++ b/devtools/client/shared/components/tree/TreeView.css
@@ -0,0 +1,198 @@
+/* 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/. */
+
+@import url('chrome://devtools/content/shared/components/reps/reps.css');
+
+/******************************************************************************/
+/* TreeView Colors */
+
+:root {
+ --tree-header-background: #C8D2DC;
+ --tree-header-sorted-background: #AAC3DC;
+}
+
+/******************************************************************************/
+/* TreeView Table*/
+
+.treeTable {
+ color: var(--theme-highlight-blue);
+}
+
+.treeTable .treeLabelCell,
+.treeTable .treeValueCell {
+ padding: 2px 0;
+ padding-inline-start: 4px;
+ line-height: 16px; /* make rows 20px tall */
+ vertical-align: top;
+ overflow: hidden;
+}
+
+.treeTable .treeLabelCell {
+ white-space: nowrap;
+ cursor: default;
+ padding-inline-start: var(--tree-label-cell-indent);
+}
+
+.treeTable .treeLabelCell::after {
+ content: ":";
+ color: var(--object-color);
+}
+
+.treeTable .treeValueCell.inputEnabled {
+ padding-block: 0;
+}
+
+.treeTable .treeValueCell.inputEnabled input {
+ width: 100%;
+ height: 20px;
+ margin: 0;
+ margin-inline-start: -2px;
+ border: solid 1px transparent;
+ outline: none;
+ box-shadow: none;
+ padding: 0 1px;
+ color: var(--theme-text-color-strong);
+ background: var(--theme-sidebar-background);
+}
+
+.treeTable .treeValueCell.inputEnabled input:focus {
+ transition: all 150ms ease-in-out;
+}
+
+.treeTable .treeValueCell > [aria-labelledby],
+.treeTable .treeLabelCell > .treeLabel {
+ unicode-bidi: plaintext;
+ text-align: match-parent;
+}
+
+/* No padding if there is actually no label */
+.treeTable .treeLabel:empty {
+ padding-inline-start: 0;
+}
+
+.treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
+ cursor: pointer;
+ text-decoration: underline;
+}
+
+/* :not(.selected) is used because row selection styles should have
+ more precedence than row hovering. */
+.treeTable .treeRow:not(.selected):hover {
+ background-color: var(--theme-selection-background-hover) !important;
+}
+
+.treeTable .treeRow.selected {
+ background-color: var(--theme-selection-background);
+}
+
+.treeTable .treeRow.selected :where(:not(.objectBox-jsonml)),
+.treeTable .treeRow.selected .treeLabelCell::after {
+ color: var(--theme-selection-color);
+ fill: currentColor;
+}
+
+/* Invert text selection color in selected rows */
+.treeTable .treeRow.selected :not(input, textarea)::selection {
+ color: var(--theme-selection-background);
+ background-color: var(--theme-selection-color);
+}
+
+/* Filtering */
+.treeTable .treeRow.hidden {
+ display: none !important;
+}
+
+.treeTable .treeValueCellDivider {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+}
+
+/* Learn More link */
+.treeTable .treeValueCell .learn-more-link {
+ user-select: none;
+ color: var(--theme-link-color);
+ cursor: pointer;
+ margin: 0 5px;
+}
+
+.treeTable .treeValueCell .learn-more-link:hover {
+ text-decoration: underline;
+}
+
+/******************************************************************************/
+/* Toggle Icon */
+
+.treeTable .treeRow .treeIcon {
+ box-sizing: content-box;
+ height: 14px;
+ width: 14px;
+ padding: 1px;
+ /* Set the size of loading spinner (see .devtools-throbber) */
+ font-size: 10px;
+ line-height: 14px;
+ display: inline-block;
+ vertical-align: bottom;
+ /* Use a total width of 20px (margins + padding + width) */
+ margin-inline: 3px 1px;
+}
+
+/* All expanded/collapsed styles need to apply on immediate children
+ since there might be nested trees within a tree. */
+.treeTable .treeRow.hasChildren > .treeLabelCell > .treeIcon {
+ cursor: pointer;
+ background-repeat: no-repeat;
+}
+
+/******************************************************************************/
+/* Header */
+
+.treeTable .treeHeaderRow {
+ height: 18px;
+}
+
+.treeTable .treeHeaderCell {
+ cursor: pointer;
+ user-select: none;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.2);
+ padding: 0 !important;
+ background: linear-gradient(
+ rgba(255, 255, 255, 0.05),
+ rgba(0, 0, 0, 0.05)),
+ radial-gradient(1px 60% at right,
+ rgba(0, 0, 0, 0.8) 0%,
+ transparent 80%) repeat-x var(--tree-header-background);
+ color: var(--theme-body-color);
+ white-space: nowrap;
+}
+
+.treeTable .treeHeaderCellBox {
+ padding-block: 2px;
+ padding-inline: 10px 14px;
+}
+
+.treeTable .treeHeaderRow > .treeHeaderCell:first-child > .treeHeaderCellBox {
+ padding: 0;
+}
+
+.treeTable .treeHeaderSorted {
+ background-color: var(--tree-header-sorted-background);
+}
+
+.treeTable .treeHeaderSorted > .treeHeaderCellBox {
+ background: url(chrome://devtools/skin/images/sort-descending-arrow.svg) no-repeat calc(100% - 4px);
+}
+
+.treeTable .treeHeaderSorted.sortedAscending > .treeHeaderCellBox {
+ background-image: url(chrome://devtools/skin/images/sort-ascending-arrow.svg);
+}
+
+.treeTable .treeHeaderCell:hover:active {
+ background-image: linear-gradient(
+ rgba(0, 0, 0, 0.1),
+ transparent),
+ radial-gradient(1px 60% at right,
+ rgba(0, 0, 0, 0.8) 0%,
+ transparent 80%);
+}
diff --git a/devtools/client/shared/components/tree/TreeView.js b/devtools/client/shared/components/tree/TreeView.js
new file mode 100644
index 0000000000..d9ef7c0088
--- /dev/null
+++ b/devtools/client/shared/components/tree/TreeView.js
@@ -0,0 +1,799 @@
+/* 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/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ const {
+ cloneElement,
+ Component,
+ createFactory,
+ createRef,
+ } = require("devtools/client/shared/vendor/react");
+ const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+
+ // Reps
+ const {
+ ObjectProvider,
+ } = require("devtools/client/shared/components/tree/ObjectProvider");
+ const TreeRow = createFactory(
+ require("devtools/client/shared/components/tree/TreeRow")
+ );
+ const TreeHeader = createFactory(
+ require("devtools/client/shared/components/tree/TreeHeader")
+ );
+
+ const { scrollIntoView } = require("devtools/client/shared/scroll");
+
+ const SUPPORTED_KEYS = [
+ "ArrowUp",
+ "ArrowDown",
+ "ArrowLeft",
+ "ArrowRight",
+ "End",
+ "Home",
+ "Enter",
+ " ",
+ "Escape",
+ ];
+
+ const defaultProps = {
+ object: null,
+ renderRow: null,
+ provider: ObjectProvider,
+ expandedNodes: new Set(),
+ selected: null,
+ defaultSelectFirstNode: true,
+ active: null,
+ expandableStrings: true,
+ columns: [],
+ };
+
+ /**
+ * This component represents a tree view with expandable/collapsible nodes.
+ * The tree is rendered using <table> element where every node is represented
+ * by <tr> element. The tree is one big table where nodes (rows) are properly
+ * indented from the left to mimic hierarchical structure of the data.
+ *
+ * The tree can have arbitrary number of columns and so, might be use
+ * as an expandable tree-table UI widget as well. By default, there is
+ * one column for node label and one for node value.
+ *
+ * The tree is maintaining its (presentation) state, which consists
+ * from list of expanded nodes and list of columns.
+ *
+ * Complete data provider interface:
+ * var TreeProvider = {
+ * getChildren: function(object);
+ * hasChildren: function(object);
+ * getLabel: function(object, colId);
+ * getLevel: function(object); // optional
+ * getValue: function(object, colId);
+ * getKey: function(object);
+ * getType: function(object);
+ * }
+ *
+ * Complete tree decorator interface:
+ * var TreeDecorator = {
+ * getRowClass: function(object);
+ * getCellClass: function(object, colId);
+ * getHeaderClass: function(colId);
+ * renderValue: function(object, colId);
+ * renderRow: function(object);
+ * renderCell: function(object, colId);
+ * renderLabelCell: function(object);
+ * }
+ */
+ class TreeView extends Component {
+ // The only required property (not set by default) is the input data
+ // object that is used to populate the tree.
+ static get propTypes() {
+ return {
+ // The input data object.
+ object: PropTypes.any,
+ className: PropTypes.string,
+ label: PropTypes.string,
+ // Data provider (see also the interface above)
+ provider: PropTypes.shape({
+ getChildren: PropTypes.func,
+ hasChildren: PropTypes.func,
+ getLabel: PropTypes.func,
+ getValue: PropTypes.func,
+ getKey: PropTypes.func,
+ getLevel: PropTypes.func,
+ getType: PropTypes.func,
+ }).isRequired,
+ // Tree decorator (see also the interface above)
+ decorator: PropTypes.shape({
+ getRowClass: PropTypes.func,
+ getCellClass: PropTypes.func,
+ getHeaderClass: PropTypes.func,
+ renderValue: PropTypes.func,
+ renderRow: PropTypes.func,
+ renderCell: PropTypes.func,
+ renderLabelCell: PropTypes.func,
+ }),
+ // Custom tree row (node) renderer
+ renderRow: PropTypes.func,
+ // Custom cell renderer
+ renderCell: PropTypes.func,
+ // Custom value renderer
+ renderValue: PropTypes.func,
+ // Custom tree label (including a toggle button) renderer
+ renderLabelCell: PropTypes.func,
+ // Set of expanded nodes
+ expandedNodes: PropTypes.object,
+ // Selected node
+ selected: PropTypes.string,
+ // Select first node by default
+ defaultSelectFirstNode: PropTypes.bool,
+ // The currently active (keyboard) item, if any such item exists.
+ active: PropTypes.string,
+ // Custom filtering callback
+ onFilter: PropTypes.func,
+ // Custom sorting callback
+ onSort: PropTypes.func,
+ // Custom row click callback
+ onClickRow: PropTypes.func,
+ // Row context menu event handler
+ onContextMenuRow: PropTypes.func,
+ // Tree context menu event handler
+ onContextMenuTree: PropTypes.func,
+ // A header is displayed if set to true
+ header: PropTypes.bool,
+ // Long string is expandable by a toggle button
+ expandableStrings: PropTypes.bool,
+ // Array of columns
+ columns: PropTypes.arrayOf(
+ PropTypes.shape({
+ id: PropTypes.string.isRequired,
+ title: PropTypes.string,
+ width: PropTypes.string,
+ })
+ ),
+ };
+ }
+
+ static get defaultProps() {
+ return defaultProps;
+ }
+
+ static subPath(path, subKey) {
+ return path + "/" + String(subKey).replace(/[\\/]/g, "\\$&");
+ }
+
+ /**
+ * Creates a set with the paths of the nodes that should be expanded by default
+ * according to the passed options.
+ * @param {Object} The root node of the tree.
+ * @param {Object} [optional] An object with the following optional parameters:
+ * - maxLevel: nodes nested deeper than this level won't be expanded.
+ * - maxNodes: maximum number of nodes that can be expanded. The traversal is
+ breadth-first, so expanding nodes nearer to the root will be preferred.
+ Sibling nodes will either be all expanded or none expanded.
+ * }
+ */
+ static getExpandedNodes(
+ rootObj,
+ { maxLevel = Infinity, maxNodes = Infinity } = {}
+ ) {
+ const expandedNodes = new Set();
+ const queue = [
+ {
+ object: rootObj,
+ level: 1,
+ path: "",
+ },
+ ];
+ while (queue.length) {
+ const { object, level, path } = queue.shift();
+ if (Object(object) !== object) {
+ continue;
+ }
+ const keys = Object.keys(object);
+ if (expandedNodes.size + keys.length > maxNodes) {
+ // Avoid having children half expanded.
+ break;
+ }
+ for (const key of keys) {
+ const nodePath = TreeView.subPath(path, key);
+ expandedNodes.add(nodePath);
+ if (level < maxLevel) {
+ queue.push({
+ object: object[key],
+ level: level + 1,
+ path: nodePath,
+ });
+ }
+ }
+ }
+ return expandedNodes;
+ }
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ expandedNodes: props.expandedNodes,
+ columns: ensureDefaultColumn(props.columns),
+ selected: props.selected,
+ active: props.active,
+ lastSelectedIndex: props.defaultSelectFirstNode ? 0 : null,
+ mouseDown: false,
+ };
+
+ this.treeRef = createRef();
+
+ this.toggle = this.toggle.bind(this);
+ this.isExpanded = this.isExpanded.bind(this);
+ this.onFocus = this.onFocus.bind(this);
+ this.onKeyDown = this.onKeyDown.bind(this);
+ this.onClickRow = this.onClickRow.bind(this);
+ this.getSelectedRow = this.getSelectedRow.bind(this);
+ this.selectRow = this.selectRow.bind(this);
+ this.activateRow = this.activateRow.bind(this);
+ this.isSelected = this.isSelected.bind(this);
+ this.onFilter = this.onFilter.bind(this);
+ this.onSort = this.onSort.bind(this);
+ this.getMembers = this.getMembers.bind(this);
+ this.renderRows = this.renderRows.bind(this);
+ }
+
+ // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
+ UNSAFE_componentWillReceiveProps(nextProps) {
+ const { expandedNodes, selected } = nextProps;
+ const state = {
+ expandedNodes,
+ lastSelectedIndex: this.getSelectedRowIndex(),
+ };
+
+ if (selected) {
+ state.selected = selected;
+ }
+
+ this.setState(Object.assign({}, this.state, state));
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ const {
+ expandedNodes,
+ columns,
+ selected,
+ active,
+ lastSelectedIndex,
+ mouseDown,
+ } = this.state;
+
+ return (
+ expandedNodes !== nextState.expandedNodes ||
+ columns !== nextState.columns ||
+ selected !== nextState.selected ||
+ active !== nextState.active ||
+ lastSelectedIndex !== nextState.lastSelectedIndex ||
+ mouseDown === nextState.mouseDown
+ );
+ }
+
+ componentDidUpdate() {
+ const selected = this.getSelectedRow();
+ if (selected || this.state.active) {
+ return;
+ }
+
+ const rows = this.visibleRows;
+ if (rows.length === 0) {
+ return;
+ }
+
+ // Only select a row if there is a previous lastSelected Index
+ // This mostly happens when the treeview is loaded the first time
+ if (this.state.lastSelectedIndex !== null) {
+ this.selectRow(
+ rows[Math.min(this.state.lastSelectedIndex, rows.length - 1)],
+ { alignTo: "top" }
+ );
+ }
+ }
+
+ /**
+ * Get rows that are currently visible. Some rows can be filtered and made
+ * invisible, in which case, when navigating around the tree we need to
+ * ignore the ones that are not reachable by the user.
+ */
+ get visibleRows() {
+ return this.rows.filter(row => {
+ const rowEl = findDOMNode(row);
+ return rowEl?.offsetParent;
+ });
+ }
+
+ // Node expand/collapse
+
+ toggle(nodePath) {
+ const nodes = this.state.expandedNodes;
+ if (this.isExpanded(nodePath)) {
+ nodes.delete(nodePath);
+ } else {
+ nodes.add(nodePath);
+ }
+
+ // Compute new state and update the tree.
+ this.setState(
+ Object.assign({}, this.state, {
+ expandedNodes: nodes,
+ })
+ );
+ }
+
+ isExpanded(nodePath) {
+ return this.state.expandedNodes.has(nodePath);
+ }
+
+ // Event Handlers
+
+ onFocus(_event) {
+ if (this.state.mouseDown) {
+ return;
+ }
+ // Set focus to the first element, if none is selected or activated
+ // This is needed because keyboard navigation won't work without an element being selected
+ this.componentDidUpdate();
+ }
+
+ // eslint-disable-next-line complexity
+ onKeyDown(event) {
+ const keyEligibleForFirstLetterNavigation = event.key.length === 1;
+ if (
+ (!SUPPORTED_KEYS.includes(event.key) &&
+ !keyEligibleForFirstLetterNavigation) ||
+ event.shiftKey ||
+ event.ctrlKey ||
+ event.metaKey ||
+ event.altKey
+ ) {
+ return;
+ }
+
+ const row = this.getSelectedRow();
+ if (!row) {
+ return;
+ }
+
+ const rows = this.visibleRows;
+ const index = rows.indexOf(row);
+ const { hasChildren, open } = row.props.member;
+
+ switch (event.key) {
+ case "ArrowRight":
+ if (hasChildren) {
+ if (open) {
+ const firstChildRow = this.rows
+ .slice(index + 1)
+ .find(r => r.props.member.level > row.props.member.level);
+ if (firstChildRow) {
+ this.selectRow(firstChildRow, { alignTo: "bottom" });
+ }
+ } else {
+ this.toggle(this.state.selected);
+ }
+ }
+ break;
+ case "ArrowLeft":
+ if (hasChildren && open) {
+ this.toggle(this.state.selected);
+ } else {
+ const parentRow = rows
+ .slice(0, index)
+ .reverse()
+ .find(r => r.props.member.level < row.props.member.level);
+ if (parentRow) {
+ this.selectRow(parentRow, { alignTo: "top" });
+ }
+ }
+ break;
+ case "ArrowDown":
+ const nextRow = rows[index + 1];
+ if (nextRow) {
+ this.selectRow(nextRow, { alignTo: "bottom" });
+ }
+ break;
+ case "ArrowUp":
+ const previousRow = rows[index - 1];
+ if (previousRow) {
+ this.selectRow(previousRow, { alignTo: "top" });
+ }
+ break;
+ case "Home":
+ const firstRow = rows[0];
+
+ if (firstRow) {
+ this.selectRow(firstRow, { alignTo: "top" });
+ }
+ break;
+ case "End":
+ const lastRow = rows[rows.length - 1];
+ if (lastRow) {
+ this.selectRow(lastRow, { alignTo: "bottom" });
+ }
+ break;
+ case "Enter":
+ case " ":
+ // On space or enter make selected row active. This means keyboard
+ // focus handling is passed on to the tree row itself.
+ if (this.treeRef.current === document.activeElement) {
+ event.stopPropagation();
+ event.preventDefault();
+ if (this.state.active !== this.state.selected) {
+ this.activateRow(this.state.selected);
+ }
+
+ return;
+ }
+ break;
+ case "Escape":
+ event.stopPropagation();
+ if (this.state.active != null) {
+ this.activateRow(null);
+ }
+ break;
+ }
+
+ if (keyEligibleForFirstLetterNavigation) {
+ const next = rows
+ .slice(index + 1)
+ .find(r => r.props.member.name.startsWith(event.key));
+ if (next) {
+ this.selectRow(next, { alignTo: "bottom" });
+ }
+ }
+
+ // Focus should always remain on the tree container itself.
+ this.treeRef.current.focus();
+ event.preventDefault();
+ }
+
+ onClickRow(nodePath, event) {
+ const onClickRow = this.props.onClickRow;
+ const row = this.visibleRows.find(r => r.props.member.path === nodePath);
+
+ // Call custom click handler and bail out if it returns true.
+ if (
+ onClickRow &&
+ onClickRow.call(this, nodePath, event, row.props.member)
+ ) {
+ return;
+ }
+
+ event.stopPropagation();
+
+ const cell = event.target.closest("td");
+ if (cell && cell.classList.contains("treeLabelCell")) {
+ this.toggle(nodePath);
+ }
+
+ this.selectRow(row, { preventAutoScroll: true });
+ }
+
+ onContextMenu(member, event) {
+ const onContextMenuRow = this.props.onContextMenuRow;
+ if (onContextMenuRow) {
+ onContextMenuRow.call(this, member, event);
+ }
+ }
+
+ getSelectedRow() {
+ const rows = this.visibleRows;
+ if (!this.state.selected || rows.length === 0) {
+ return null;
+ }
+ return rows.find(row => this.isSelected(row.props.member.path));
+ }
+
+ getSelectedRowIndex() {
+ const row = this.getSelectedRow();
+ if (!row) {
+ return this.props.defaultSelectFirstNode ? 0 : null;
+ }
+
+ return this.visibleRows.indexOf(row);
+ }
+
+ _scrollIntoView(row, options = {}) {
+ const treeEl = this.treeRef.current;
+ if (!treeEl || !row) {
+ return;
+ }
+
+ const { props: { member: { path } = {} } = {} } = row;
+ if (!path) {
+ return;
+ }
+
+ const element = treeEl.ownerDocument.getElementById(path);
+ if (!element) {
+ return;
+ }
+
+ scrollIntoView(element, { ...options });
+ }
+
+ selectRow(row, options = {}) {
+ const { props: { member: { path } = {} } = {} } = row;
+ if (this.isSelected(path)) {
+ return;
+ }
+
+ if (this.state.active != null) {
+ const treeEl = this.treeRef.current;
+ if (treeEl && treeEl !== treeEl.ownerDocument.activeElement) {
+ treeEl.focus();
+ }
+ }
+
+ if (!options.preventAutoScroll) {
+ this._scrollIntoView(row, options);
+ }
+
+ this.setState({
+ ...this.state,
+ selected: path,
+ active: null,
+ });
+ }
+
+ activateRow(active) {
+ this.setState({
+ ...this.state,
+ active,
+ });
+ }
+
+ isSelected(nodePath) {
+ return nodePath === this.state.selected;
+ }
+
+ isActive(nodePath) {
+ return nodePath === this.state.active;
+ }
+
+ // Filtering & Sorting
+
+ /**
+ * Filter out nodes that don't correspond to the current filter.
+ * @return {Boolean} true if the node should be visible otherwise false.
+ */
+ onFilter(object) {
+ const onFilter = this.props.onFilter;
+ return onFilter ? onFilter(object) : true;
+ }
+
+ onSort(parent, children) {
+ const onSort = this.props.onSort;
+ return onSort ? onSort(parent, children) : children;
+ }
+
+ // Members
+
+ /**
+ * Return children node objects (so called 'members') for given
+ * parent object.
+ */
+ getMembers(parent, level, path) {
+ // Strings don't have children. Note that 'long' strings are using
+ // the expander icon (+/-) to display the entire original value,
+ // but there are no child items.
+ if (typeof parent == "string") {
+ return [];
+ }
+
+ const { expandableStrings, provider } = this.props;
+ let children = provider.getChildren(parent) || [];
+
+ // If the return value is non-array, the children
+ // are being loaded asynchronously.
+ if (!Array.isArray(children)) {
+ return children;
+ }
+
+ children = this.onSort(parent, children) || children;
+
+ return children.map(child => {
+ const key = provider.getKey(child);
+ const nodePath = TreeView.subPath(path, key);
+ const type = provider.getType(child);
+ let hasChildren = provider.hasChildren(child);
+
+ // Value with no column specified is used for optimization.
+ // The row is re-rendered only if this value changes.
+ // Value for actual column is get when a cell is rendered.
+ const value = provider.getValue(child);
+
+ if (expandableStrings && isLongString(value)) {
+ hasChildren = true;
+ }
+
+ // Return value is a 'member' object containing meta-data about
+ // tree node. It describes node label, value, type, etc.
+ return {
+ // An object associated with this node.
+ object: child,
+ // A label for the child node
+ name: provider.getLabel(child),
+ // Data type of the child node (used for CSS customization)
+ type,
+ // Class attribute computed from the type.
+ rowClass: "treeRow-" + type,
+ // Level of the child within the hierarchy (top == 0)
+ level: provider.getLevel ? provider.getLevel(child, level) : level,
+ // True if this node has children.
+ hasChildren,
+ // Value associated with this node (as provided by the data provider)
+ value,
+ // True if the node is expanded.
+ open: this.isExpanded(nodePath),
+ // Node path
+ path: nodePath,
+ // True if the node is hidden (used for filtering)
+ hidden: !this.onFilter(child),
+ // True if the node is selected with keyboard
+ selected: this.isSelected(nodePath),
+ // True if the node is activated with keyboard
+ active: this.isActive(nodePath),
+ };
+ });
+ }
+
+ /**
+ * Render tree rows/nodes.
+ */
+ renderRows(parent, level = 0, path = "") {
+ let rows = [];
+ const decorator = this.props.decorator;
+ let renderRow = this.props.renderRow || TreeRow;
+
+ // Get children for given parent node, iterate over them and render
+ // a row for every one. Use row template (a component) from properties.
+ // If the return value is non-array, the children are being loaded
+ // asynchronously.
+ const members = this.getMembers(parent, level, path);
+ if (!Array.isArray(members)) {
+ return members;
+ }
+
+ members.forEach(member => {
+ if (decorator?.renderRow) {
+ renderRow = decorator.renderRow(member.object) || renderRow;
+ }
+
+ const props = Object.assign({}, this.props, {
+ key: `${member.path}-${member.active ? "active" : "inactive"}`,
+ member,
+ columns: this.state.columns,
+ id: member.path,
+ ref: row => row && this.rows.push(row),
+ onClick: this.onClickRow.bind(this, member.path),
+ onContextMenu: this.onContextMenu.bind(this, member),
+ });
+
+ // Render single row.
+ rows.push(renderRow(props));
+
+ // If a child node is expanded render its rows too.
+ if (member.hasChildren && member.open) {
+ const childRows = this.renderRows(
+ member.object,
+ level + 1,
+ member.path
+ );
+
+ // If children needs to be asynchronously fetched first,
+ // set 'loading' property to the parent row. Otherwise
+ // just append children rows to the array of all rows.
+ if (!Array.isArray(childRows)) {
+ const lastIndex = rows.length - 1;
+ props.member.loading = true;
+ rows[lastIndex] = cloneElement(rows[lastIndex], props);
+ } else {
+ rows = rows.concat(childRows);
+ }
+ }
+ });
+
+ return rows;
+ }
+
+ render() {
+ const root = this.props.object;
+ const classNames = ["treeTable"];
+ this.rows = [];
+
+ const { className, onContextMenuTree } = this.props;
+ // Use custom class name from props.
+ if (className) {
+ classNames.push(...className.split(" "));
+ }
+
+ // Alright, let's render all tree rows. The tree is one big <table>.
+ let rows = this.renderRows(root, 0, "");
+
+ // This happens when the view needs to do initial asynchronous
+ // fetch for the root object. The tree might provide a hook API
+ // for rendering animated spinner (just like for tree nodes).
+ if (!Array.isArray(rows)) {
+ rows = [];
+ }
+
+ const props = Object.assign({}, this.props, {
+ columns: this.state.columns,
+ });
+
+ return dom.table(
+ {
+ className: classNames.join(" "),
+ role: "tree",
+ ref: this.treeRef,
+ tabIndex: 0,
+ onFocus: this.onFocus,
+ onKeyDown: this.onKeyDown,
+ onContextMenu: onContextMenuTree && onContextMenuTree.bind(this),
+ onMouseDown: () => this.setState({ mouseDown: true }),
+ onMouseUp: () => this.setState({ mouseDown: false }),
+ onClick: () => {
+ // Focus should always remain on the tree container itself.
+ this.treeRef.current.focus();
+ },
+ onBlur: event => {
+ if (this.state.active != null) {
+ const { relatedTarget } = event;
+ if (!this.treeRef.current.contains(relatedTarget)) {
+ this.activateRow(null);
+ }
+ }
+ },
+ "aria-label": this.props.label || "",
+ "aria-activedescendant": this.state.selected,
+ cellPadding: 0,
+ cellSpacing: 0,
+ },
+ TreeHeader(props),
+ dom.tbody(
+ {
+ role: "presentation",
+ tabIndex: -1,
+ },
+ rows
+ )
+ );
+ }
+ }
+
+ // Helpers
+
+ /**
+ * There should always be at least one column (the one with toggle buttons)
+ * and this function ensures that it's true.
+ */
+ function ensureDefaultColumn(columns) {
+ if (!columns) {
+ columns = [];
+ }
+
+ const defaultColumn = columns.filter(col => col.id == "default");
+ if (defaultColumn.length) {
+ return columns;
+ }
+
+ // The default column is usually the first one.
+ return [{ id: "default" }, ...columns];
+ }
+
+ function isLongString(value) {
+ return typeof value == "string" && value.length > 50;
+ }
+
+ // Exports from this module
+ module.exports = TreeView;
+});
diff --git a/devtools/client/shared/components/tree/moz.build b/devtools/client/shared/components/tree/moz.build
new file mode 100644
index 0000000000..0700575f17
--- /dev/null
+++ b/devtools/client/shared/components/tree/moz.build
@@ -0,0 +1,13 @@
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "LabelCell.js",
+ "ObjectProvider.js",
+ "TreeCell.js",
+ "TreeHeader.js",
+ "TreeRow.js",
+ "TreeView.js",
+)
diff --git a/devtools/client/shared/css-angle.js b/devtools/client/shared/css-angle.js
new file mode 100644
index 0000000000..903b7813ad
--- /dev/null
+++ b/devtools/client/shared/css-angle.js
@@ -0,0 +1,349 @@
+/* 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/. */
+
+"use strict";
+
+const SPECIALVALUES = new Set(["initial", "inherit", "unset"]);
+
+const { getCSSLexer } = require("resource://devtools/shared/css/lexer.js");
+
+loader.lazyRequireGetter(
+ this,
+ "CSS_ANGLEUNIT",
+ "resource://devtools/shared/css/constants.js",
+ true
+);
+
+/**
+ * This module is used to convert between various angle units.
+ *
+ * Usage:
+ * let {angleUtils} = require("devtools/client/shared/css-angle");
+ * let angle = new angleUtils.CssAngle("180deg");
+ *
+ * angle.authored === "180deg"
+ * angle.valid === true
+ * angle.rad === "3,14rad"
+ * angle.grad === "200grad"
+ * angle.turn === "0.5turn"
+ *
+ * angle.toString() === "180deg"; // Outputs the angle value and its unit
+ * // Angle objects can be reused
+ * angle.newAngle("-1TURN") === "-1TURN"; // true
+ */
+
+function CssAngle(angleValue) {
+ this.newAngle(angleValue);
+}
+
+module.exports.angleUtils = {
+ CssAngle,
+ classifyAngle,
+};
+
+CssAngle.prototype = {
+ // Still keep trying to lazy load properties-db by lazily getting ANGLEUNIT
+ get ANGLEUNIT() {
+ return CSS_ANGLEUNIT;
+ },
+
+ _angleUnit: null,
+ _angleUnitUppercase: false,
+
+ // The value as-authored.
+ authored: null,
+ // A lower-cased copy of |authored|.
+ lowerCased: null,
+
+ get angleUnit() {
+ if (this._angleUnit === null) {
+ this._angleUnit = classifyAngle(this.authored);
+ }
+ return this._angleUnit;
+ },
+
+ set angleUnit(unit) {
+ this._angleUnit = unit;
+ },
+
+ get valid() {
+ const token = getCSSLexer(this.authored).nextToken();
+ if (!token) {
+ return false;
+ }
+ return (
+ token.tokenType === "dimension" &&
+ token.text.toLowerCase() in this.ANGLEUNIT
+ );
+ },
+
+ get specialValue() {
+ return SPECIALVALUES.has(this.lowerCased) ? this.authored : null;
+ },
+
+ get deg() {
+ const invalidOrSpecialValue = this._getInvalidOrSpecialValue();
+ if (invalidOrSpecialValue !== false) {
+ return invalidOrSpecialValue;
+ }
+
+ const angleUnit = classifyAngle(this.authored);
+ if (angleUnit === this.ANGLEUNIT.deg) {
+ // The angle is valid and is in degree.
+ return this.authored;
+ }
+
+ let degValue;
+ if (angleUnit === this.ANGLEUNIT.rad) {
+ // The angle is valid and is in radian.
+ degValue = this.authoredAngleValue / (Math.PI / 180);
+ }
+
+ if (angleUnit === this.ANGLEUNIT.grad) {
+ // The angle is valid and is in gradian.
+ degValue = this.authoredAngleValue * 0.9;
+ }
+
+ if (angleUnit === this.ANGLEUNIT.turn) {
+ // The angle is valid and is in turn.
+ degValue = this.authoredAngleValue * 360;
+ }
+
+ let unitStr = this.ANGLEUNIT.deg;
+ if (this._angleUnitUppercase === true) {
+ unitStr = unitStr.toUpperCase();
+ }
+ return `${Math.round(degValue * 100) / 100}${unitStr}`;
+ },
+
+ get rad() {
+ const invalidOrSpecialValue = this._getInvalidOrSpecialValue();
+ if (invalidOrSpecialValue !== false) {
+ return invalidOrSpecialValue;
+ }
+
+ const unit = classifyAngle(this.authored);
+ if (unit === this.ANGLEUNIT.rad) {
+ // The angle is valid and is in radian.
+ return this.authored;
+ }
+
+ let radValue;
+ if (unit === this.ANGLEUNIT.deg) {
+ // The angle is valid and is in degree.
+ radValue = this.authoredAngleValue * (Math.PI / 180);
+ }
+
+ if (unit === this.ANGLEUNIT.grad) {
+ // The angle is valid and is in gradian.
+ radValue = this.authoredAngleValue * 0.9 * (Math.PI / 180);
+ }
+
+ if (unit === this.ANGLEUNIT.turn) {
+ // The angle is valid and is in turn.
+ radValue = this.authoredAngleValue * 360 * (Math.PI / 180);
+ }
+
+ let unitStr = this.ANGLEUNIT.rad;
+ if (this._angleUnitUppercase === true) {
+ unitStr = unitStr.toUpperCase();
+ }
+ return `${Math.round(radValue * 10000) / 10000}${unitStr}`;
+ },
+
+ get grad() {
+ const invalidOrSpecialValue = this._getInvalidOrSpecialValue();
+ if (invalidOrSpecialValue !== false) {
+ return invalidOrSpecialValue;
+ }
+
+ const unit = classifyAngle(this.authored);
+ if (unit === this.ANGLEUNIT.grad) {
+ // The angle is valid and is in gradian
+ return this.authored;
+ }
+
+ let gradValue;
+ if (unit === this.ANGLEUNIT.deg) {
+ // The angle is valid and is in degree
+ gradValue = this.authoredAngleValue / 0.9;
+ }
+
+ if (unit === this.ANGLEUNIT.rad) {
+ // The angle is valid and is in radian
+ gradValue = this.authoredAngleValue / 0.9 / (Math.PI / 180);
+ }
+
+ if (unit === this.ANGLEUNIT.turn) {
+ // The angle is valid and is in turn
+ gradValue = this.authoredAngleValue * 400;
+ }
+
+ let unitStr = this.ANGLEUNIT.grad;
+ if (this._angleUnitUppercase === true) {
+ unitStr = unitStr.toUpperCase();
+ }
+ return `${Math.round(gradValue * 100) / 100}${unitStr}`;
+ },
+
+ get turn() {
+ const invalidOrSpecialValue = this._getInvalidOrSpecialValue();
+ if (invalidOrSpecialValue !== false) {
+ return invalidOrSpecialValue;
+ }
+
+ const unit = classifyAngle(this.authored);
+ if (unit === this.ANGLEUNIT.turn) {
+ // The angle is valid and is in turn
+ return this.authored;
+ }
+
+ let turnValue;
+ if (unit === this.ANGLEUNIT.deg) {
+ // The angle is valid and is in degree
+ turnValue = this.authoredAngleValue / 360;
+ }
+
+ if (unit === this.ANGLEUNIT.rad) {
+ // The angle is valid and is in radian
+ turnValue = this.authoredAngleValue / (Math.PI / 180) / 360;
+ }
+
+ if (unit === this.ANGLEUNIT.grad) {
+ // The angle is valid and is in gradian
+ turnValue = this.authoredAngleValue / 400;
+ }
+
+ let unitStr = this.ANGLEUNIT.turn;
+ if (this._angleUnitUppercase === true) {
+ unitStr = unitStr.toUpperCase();
+ }
+ return `${Math.round(turnValue * 100) / 100}${unitStr}`;
+ },
+
+ /**
+ * Check whether the angle value is in the special list e.g.
+ * inherit or invalid.
+ *
+ * @return {String|Boolean}
+ * - If the current angle is a special value e.g. "inherit" then
+ * return the angle.
+ * - If the angle is invalid return an empty string.
+ * - If the angle is a regular angle e.g. 90deg so we return false
+ * to indicate that the angle is neither invalid nor special.
+ */
+ _getInvalidOrSpecialValue() {
+ if (this.specialValue) {
+ return this.specialValue;
+ }
+ if (!this.valid) {
+ return "";
+ }
+ return false;
+ },
+
+ /**
+ * Change angle
+ *
+ * @param {String} angle
+ * Any valid angle value + unit string
+ */
+ newAngle(angle) {
+ // Store a lower-cased version of the angle to help with format
+ // testing. The original text is kept as well so it can be
+ // returned when needed.
+ this.lowerCased = angle.toLowerCase();
+ this._angleUnitUppercase = angle === angle.toUpperCase();
+ this.authored = angle;
+
+ const reg = new RegExp(`(${Object.keys(this.ANGLEUNIT).join("|")})$`, "i");
+ const unitStartIdx = angle.search(reg);
+ this.authoredAngleValue = angle.substring(0, unitStartIdx);
+ this.authoredAngleUnit = angle.substring(unitStartIdx, angle.length);
+
+ return this;
+ },
+
+ nextAngleUnit() {
+ // Get a reordered array from the formats object
+ // to have the current format at the front so we can cycle through.
+ let formats = Object.keys(this.ANGLEUNIT);
+ const putOnEnd = formats.splice(0, formats.indexOf(this.angleUnit));
+ formats = formats.concat(putOnEnd);
+ const currentDisplayedValue = this[formats[0]];
+
+ for (const format of formats) {
+ if (this[format].toLowerCase() !== currentDisplayedValue.toLowerCase()) {
+ this.angleUnit = this.ANGLEUNIT[format];
+ break;
+ }
+ }
+ return this.toString();
+ },
+
+ /**
+ * Return a string representing a angle
+ */
+ toString() {
+ let angle;
+
+ switch (this.angleUnit) {
+ case this.ANGLEUNIT.deg:
+ angle = this.deg;
+ break;
+ case this.ANGLEUNIT.rad:
+ angle = this.rad;
+ break;
+ case this.ANGLEUNIT.grad:
+ angle = this.grad;
+ break;
+ case this.ANGLEUNIT.turn:
+ angle = this.turn;
+ break;
+ default:
+ angle = this.deg;
+ }
+
+ if (this._angleUnitUppercase && this.angleUnit != this.ANGLEUNIT.authored) {
+ angle = angle.toUpperCase();
+ }
+ return angle;
+ },
+
+ /**
+ * This method allows comparison of CssAngle objects using ===.
+ */
+ valueOf() {
+ return this.deg;
+ },
+};
+
+/**
+ * Given a color, classify its type as one of the possible angle
+ * units, as known by |CssAngle.angleUnit|.
+ *
+ * @param {String} value
+ * The angle, in any form accepted by CSS.
+ * @return {String}
+ * The angle classification, one of "deg", "rad", "grad", or "turn".
+ */
+function classifyAngle(value) {
+ value = value.toLowerCase();
+ if (value.endsWith("deg")) {
+ return CSS_ANGLEUNIT.deg;
+ }
+
+ if (value.endsWith("grad")) {
+ return CSS_ANGLEUNIT.grad;
+ }
+
+ if (value.endsWith("rad")) {
+ return CSS_ANGLEUNIT.rad;
+ }
+ if (value.endsWith("turn")) {
+ return CSS_ANGLEUNIT.turn;
+ }
+
+ return CSS_ANGLEUNIT.deg;
+}
diff --git a/devtools/client/shared/curl.js b/devtools/client/shared/curl.js
new file mode 100644
index 0000000000..47d2aacfe8
--- /dev/null
+++ b/devtools/client/shared/curl.js
@@ -0,0 +1,489 @@
+/* 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/. */
+
+/*
+ * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2009 Anthony Ricaud <rik@webkit.org>
+ * Copyright (C) 2011 Google Inc. All rights reserved.
+ * Copyright (C) 2009 Mozilla Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+"use strict";
+
+const Curl = {
+ /**
+ * Generates a cURL command string which can be used from the command line etc.
+ *
+ * @param object data
+ * Datasource to create the command from.
+ * The object must contain the following properties:
+ * - url:string, the URL of the request.
+ * - method:string, the request method upper cased. HEAD / GET / POST etc.
+ * - headers:array, an array of request headers {name:x, value:x} tuples.
+ * - httpVersion:string, http protocol version rfc2616 formatted. Eg. "HTTP/1.1"
+ * - postDataText:string, optional - the request payload.
+ *
+ * @param string platform
+ * Optional parameter to override platform,
+ * Fallbacks to current platform if not defined.
+ *
+ * @return string
+ * A cURL command.
+ */
+ generateCommand(data, platform) {
+ const utils = CurlUtils;
+
+ let command = ["curl"];
+
+ // Make sure to use the following helpers to sanitize arguments before execution.
+ const addParam = value => {
+ const safe = /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value);
+ command.push(safe);
+ };
+
+ const addPostData = value => {
+ const safe = /^[a-zA-Z-]+$/.test(value) ? value : escapeString(value);
+ postData.push(safe);
+ };
+
+ const ignoredHeaders = new Set();
+ const currentPlatform = platform || Services.appinfo.OS;
+
+ // The cURL command is expected to run on the same platform that Firefox runs
+ // (it may be different from the inspected page platform).
+ const escapeString =
+ currentPlatform == "WINNT"
+ ? utils.escapeStringWin
+ : utils.escapeStringPosix;
+
+ // Add URL.
+ addParam(data.url);
+
+ // Disable globbing if the URL contains brackets.
+ // cURL also globs braces but they are already percent-encoded.
+ if (data.url.includes("[") || data.url.includes("]")) {
+ addParam("--globoff");
+ }
+
+ let postDataText = null;
+ const multipartRequest = utils.isMultipartRequest(data);
+
+ // Create post data.
+ const postData = [];
+ if (multipartRequest) {
+ // WINDOWS KNOWN LIMITATIONS: Due to the specificity of running curl on
+ // cmd.exe even correctly escaped windows newline \r\n will be
+ // treated by curl as plain local newline. It corresponds in unix
+ // to single \n and that's what curl will send in payload.
+ // It may be particularly hurtful for multipart/form-data payloads
+ // which composed using \n only, not \r\n, may be not parsable for
+ // peers which split parts of multipart payload using \r\n.
+ postDataText = data.postDataText;
+ addPostData("--data-binary");
+ const boundary = utils.getMultipartBoundary(data);
+ const text = utils.removeBinaryDataFromMultipartText(
+ postDataText,
+ boundary
+ );
+ addPostData(text);
+ ignoredHeaders.add("content-length");
+ } else if (
+ data.postDataText &&
+ (utils.isUrlEncodedRequest(data) ||
+ ["PUT", "POST", "PATCH"].includes(data.method))
+ ) {
+ // When no postData exists, --data-raw should not be set
+ postDataText = data.postDataText;
+ addPostData("--data-raw");
+ addPostData(utils.writePostDataTextParams(postDataText));
+ ignoredHeaders.add("content-length");
+ }
+ // curl generates the host header itself based on the given URL
+ ignoredHeaders.add("host");
+
+ // Add --compressed if the response is compressed
+ if (utils.isContentEncodedResponse(data)) {
+ addParam("--compressed");
+ }
+
+ // Add -I (HEAD)
+ // For servers that supports HEAD.
+ // This will fetch the header of a document only.
+ if (data.method === "HEAD") {
+ addParam("-I");
+ } else if (data.method !== "GET") {
+ // Add method.
+ // For HEAD and GET requests this is not necessary. GET is the
+ // default, -I implies HEAD.
+ addParam("-X");
+ addParam(data.method);
+ }
+
+ // Add request headers.
+ let headers = data.headers;
+ if (multipartRequest) {
+ const multipartHeaders = utils.getHeadersFromMultipartText(postDataText);
+ headers = headers.concat(multipartHeaders);
+ }
+ for (let i = 0; i < headers.length; i++) {
+ const header = headers[i];
+ if (ignoredHeaders.has(header.name.toLowerCase())) {
+ continue;
+ }
+ addParam("-H");
+ addParam(header.name + ": " + header.value);
+ }
+
+ // Add post data.
+ command = command.concat(postData);
+
+ return command.join(" ");
+ },
+};
+
+exports.Curl = Curl;
+
+/**
+ * Utility functions for the Curl command generator.
+ */
+const CurlUtils = {
+ /**
+ * Check if the request is an URL encoded request.
+ *
+ * @param object data
+ * The data source. See the description in the Curl object.
+ * @return boolean
+ * True if the request is URL encoded, false otherwise.
+ */
+ isUrlEncodedRequest(data) {
+ let postDataText = data.postDataText;
+ if (!postDataText) {
+ return false;
+ }
+
+ postDataText = postDataText.toLowerCase();
+ if (
+ postDataText.includes("content-type: application/x-www-form-urlencoded")
+ ) {
+ return true;
+ }
+
+ const contentType = this.findHeader(data.headers, "content-type");
+
+ return (
+ contentType &&
+ contentType.toLowerCase().includes("application/x-www-form-urlencoded")
+ );
+ },
+
+ /**
+ * Check if the request is a multipart request.
+ *
+ * @param object data
+ * The data source.
+ * @return boolean
+ * True if the request is multipart reqeust, false otherwise.
+ */
+ isMultipartRequest(data) {
+ let postDataText = data.postDataText;
+ if (!postDataText) {
+ return false;
+ }
+
+ postDataText = postDataText.toLowerCase();
+ if (postDataText.includes("content-type: multipart/form-data")) {
+ return true;
+ }
+
+ const contentType = this.findHeader(data.headers, "content-type");
+
+ return (
+ contentType && contentType.toLowerCase().includes("multipart/form-data;")
+ );
+ },
+
+ /**
+ * Check if the response of an URL has content encoding header.
+ *
+ * @param object data
+ * The data source. See the description in the Curl object.
+ * @return boolean
+ * True if the response is compressed, false otherwise.
+ */
+ isContentEncodedResponse(data) {
+ return !!this.findHeader(data.responseHeaders, "content-encoding");
+ },
+
+ /**
+ * Write out paramters from post data text.
+ *
+ * @param object postDataText
+ * Post data text.
+ * @return string
+ * Post data parameters.
+ */
+ writePostDataTextParams(postDataText) {
+ if (!postDataText) {
+ return "";
+ }
+ const lines = postDataText.split("\r\n");
+ return lines[lines.length - 1];
+ },
+
+ /**
+ * Finds the header with the given name in the headers array.
+ *
+ * @param array headers
+ * Array of headers info {name:x, value:x}.
+ * @param string name
+ * The header name to find.
+ * @return string
+ * The found header value or null if not found.
+ */
+ findHeader(headers, name) {
+ if (!headers) {
+ return null;
+ }
+
+ name = name.toLowerCase();
+ for (const header of headers) {
+ if (name == header.name.toLowerCase()) {
+ return header.value;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * Returns the boundary string for a multipart request.
+ *
+ * @param string data
+ * The data source. See the description in the Curl object.
+ * @return string
+ * The boundary string for the request.
+ */
+ getMultipartBoundary(data) {
+ const boundaryRe = /\bboundary=(-{3,}\w+)/i;
+
+ // Get the boundary string from the Content-Type request header.
+ const contentType = this.findHeader(data.headers, "Content-Type");
+ if (boundaryRe.test(contentType)) {
+ return contentType.match(boundaryRe)[1];
+ }
+ // Temporary workaround. As of 2014-03-11 the requestHeaders array does not
+ // always contain the Content-Type header for mulitpart requests. See bug 978144.
+ // Find the header from the request payload.
+ const boundaryString = data.postDataText.match(boundaryRe)[1];
+ if (boundaryString) {
+ return boundaryString;
+ }
+
+ return null;
+ },
+
+ /**
+ * Removes the binary data from multipart text.
+ *
+ * @param string multipartText
+ * Multipart form data text.
+ * @param string boundary
+ * The boundary string.
+ * @return string
+ * The multipart text without the binary data.
+ */
+ removeBinaryDataFromMultipartText(multipartText, boundary) {
+ let result = "";
+ boundary = "--" + boundary;
+ const parts = multipartText.split(boundary);
+ for (const part of parts) {
+ // Each part is expected to have a content disposition line.
+ let contentDispositionLine = part.trimLeft().split("\r\n")[0];
+ if (!contentDispositionLine) {
+ continue;
+ }
+ contentDispositionLine = contentDispositionLine.toLowerCase();
+ if (contentDispositionLine.includes("content-disposition: form-data")) {
+ if (contentDispositionLine.includes("filename=")) {
+ // The header lines and the binary blob is separated by 2 CRLF's.
+ // Add only the headers to the result.
+ const headers = part.split("\r\n\r\n")[0];
+ result += boundary + headers + "\r\n\r\n";
+ } else {
+ result += boundary + part;
+ }
+ }
+ }
+ result += boundary + "--\r\n";
+
+ return result;
+ },
+
+ /**
+ * Get the headers from a multipart post data text.
+ *
+ * @param string multipartText
+ * Multipart post text.
+ * @return array
+ * An array of header objects {name:x, value:x}
+ */
+ getHeadersFromMultipartText(multipartText) {
+ const headers = [];
+ if (!multipartText || multipartText.startsWith("---")) {
+ return headers;
+ }
+
+ // Get the header section.
+ const index = multipartText.indexOf("\r\n\r\n");
+ if (index == -1) {
+ return headers;
+ }
+
+ // Parse the header lines.
+ const headersText = multipartText.substring(0, index);
+ const headerLines = headersText.split("\r\n");
+ let lastHeaderName = null;
+
+ for (const line of headerLines) {
+ // Create a header for each line in fields that spans across multiple lines.
+ // Subsquent lines always begins with at least one space or tab character.
+ // (rfc2616)
+ if (lastHeaderName && /^\s+/.test(line)) {
+ headers.push({ name: lastHeaderName, value: line.trim() });
+ continue;
+ }
+
+ const indexOfColon = line.indexOf(":");
+ if (indexOfColon == -1) {
+ continue;
+ }
+
+ const header = [
+ line.slice(0, indexOfColon),
+ line.slice(indexOfColon + 1),
+ ];
+ if (header.length != 2) {
+ continue;
+ }
+ lastHeaderName = header[0].trim();
+ headers.push({ name: lastHeaderName, value: header[1].trim() });
+ }
+
+ return headers;
+ },
+
+ /**
+ * Escape util function for POSIX oriented operating systems.
+ * Credit: Google DevTools
+ */
+ escapeStringPosix(str) {
+ function escapeCharacter(x) {
+ let code = x.charCodeAt(0);
+ if (code < 256) {
+ // Add leading zero when needed to not care about the next character.
+ return code < 16
+ ? "\\x0" + code.toString(16)
+ : "\\x" + code.toString(16);
+ }
+ code = code.toString(16);
+ return "\\u" + ("0000" + code).substr(code.length, 4);
+ }
+
+ if (/[^\x20-\x7E]|\'/.test(str)) {
+ // Use ANSI-C quoting syntax.
+ return (
+ "$'" +
+ str
+ .replace(/\\/g, "\\\\")
+ .replace(/\'/g, "\\'")
+ .replace(/\n/g, "\\n")
+ .replace(/\r/g, "\\r")
+ .replace(/!/g, "\\041")
+ .replace(/[^\x20-\x7E]/g, escapeCharacter) +
+ "'"
+ );
+ }
+
+ // Use single quote syntax.
+ return "'" + str + "'";
+ },
+
+ /**
+ * Escape util function for Windows systems.
+ * Credit: Google DevTools
+ */
+ escapeStringWin(str) {
+ /*
+ Because cmd.exe parser and MS Crt arguments parsers use some of the
+ same escape characters, they can interact with each other in
+ horrible ways, the order of operations is critical.
+ */
+ const encapsChars = '"';
+ return (
+ encapsChars +
+ str
+
+ // Replace \ with \\ first because it is an escape character for certain
+ // conditions in both parsers.
+ .replace(/\\/g, "\\\\")
+
+ // Replace double quote chars with two double quotes (not by escaping with \") because it is
+ // recognized by both cmd.exe and MS Crt arguments parser.
+ .replace(/"/g, '""')
+
+ // Escape ` and $ so commands do not get executed e.g $(calc.exe) or `\$(calc.exe)
+ .replace(/[`$]/g, "\\$&")
+
+ // Then escape all characters we are not sure about with ^ to ensure it
+ // gets to MS Crt parser safely.
+ .replace(/[^a-zA-Z0-9\s_\-:=+~\/.',?;()*\$&\\{}\"`]/g, "^$&")
+
+ // The % character is special because MS Crt parser will try and look for
+ // ENV variables and fill them in its place. We cannot escape them with %
+ // and cannot escape them with ^ (because it's cmd.exe's escape not MS Crt
+ // parser); So we can get cmd.exe parser to escape the character after it,
+ // if it is followed by a valid beginning character of an ENV variable.
+ // This ensures we do not try and double escape another ^ if it was placed
+ // by the previous replace.
+ .replace(/%(?=[a-zA-Z0-9_])/g, "%^")
+
+ // We replace \r and \r\n with \n, this allows to consistently escape all new
+ // lines in the next replace
+ .replace(/\r\n?/g, "\n")
+
+ // Lastly we replace new lines with ^ and TWO new lines because the first
+ // new line is there to enact the escape command the second is the character
+ // to escape (in this case new line).
+ // The extra " enables escaping new lines with ^ within quotes in cmd.exe.
+ .replace(/\n/g, '"^\r\n\r\n"') +
+ encapsChars
+ );
+ },
+};
+
+exports.CurlUtils = CurlUtils;
diff --git a/devtools/client/shared/devices.js b/devtools/client/shared/devices.js
new file mode 100644
index 0000000000..6bb3135e61
--- /dev/null
+++ b/devtools/client/shared/devices.js
@@ -0,0 +1,182 @@
+/* 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/. */
+
+"use strict";
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/device.properties"
+);
+
+const { RemoteSettings } = ChromeUtils.importESModule(
+ "resource://services-settings/remote-settings.sys.mjs"
+);
+
+loader.lazyRequireGetter(
+ this,
+ "asyncStorage",
+ "resource://devtools/shared/async-storage.js"
+);
+
+const LOCAL_DEVICES = "devtools.devices.local";
+
+/* This is a catalog of common web-enabled devices and their properties,
+ * intended for (mobile) device emulation.
+ *
+ * The properties of a device are:
+ * - name: brand and model(s).
+ * - width: viewport width.
+ * - height: viewport height.
+ * - pixelRatio: ratio from viewport to physical screen pixels.
+ * - userAgent: UA string of the device's browser.
+ * - touch: whether it has a touch screen.
+ * - os: default OS, such as "ios", "fxos", "android".
+ *
+ * The device types are:
+ * ["phones", "tablets", "laptops", "televisions", "consoles", "watches"].
+ *
+ * To propose new devices for the shared catalog, see
+ * https://firefox-source-docs.mozilla.org/devtools/responsive/devices.html#adding-and-removing-devices.
+ *
+ * You can easily add more devices to this catalog from your own code (e.g. an
+ * addon) like so:
+ *
+ * var myPhone = { name: "My Phone", ... };
+ * require("devtools/client/shared/devices").addDevice(myPhone, "phones");
+ */
+
+// Local devices catalog that addons can add to.
+let localDevices;
+let localDevicesLoaded = false;
+
+/**
+ * Load local devices from storage.
+ */
+async function loadLocalDevices() {
+ if (localDevicesLoaded) {
+ return;
+ }
+ let devicesJSON = await asyncStorage.getItem(LOCAL_DEVICES);
+ if (!devicesJSON) {
+ devicesJSON = "{}";
+ }
+ localDevices = JSON.parse(devicesJSON);
+ localDevicesLoaded = true;
+}
+
+/**
+ * Add a device to the local catalog.
+ * Returns `true` if the device is added, `false` otherwise.
+ */
+async function addDevice(device, type = "phones") {
+ await loadLocalDevices();
+ let list = localDevices[type];
+ if (!list) {
+ list = localDevices[type] = [];
+ }
+
+ // Ensure the new device is has a unique name
+ const exists = list.some(entry => entry.name == device.name);
+ if (exists) {
+ return false;
+ }
+
+ list.push(Object.assign({}, device));
+ await asyncStorage.setItem(LOCAL_DEVICES, JSON.stringify(localDevices));
+
+ return true;
+}
+
+/**
+ * Edit a device from the local catalog.
+ * Returns `true` if the device is edited, `false` otherwise.
+ */
+async function editDevice(oldDevice, newDevice, type = "phones") {
+ await loadLocalDevices();
+ const list = localDevices[type];
+ if (!list) {
+ return false;
+ }
+
+ const index = list.findIndex(entry => entry.name == oldDevice.name);
+ if (index == -1) {
+ return false;
+ }
+
+ // Replace old device info with new one
+ list.splice(index, 1, newDevice);
+ await asyncStorage.setItem(LOCAL_DEVICES, JSON.stringify(localDevices));
+
+ return true;
+}
+
+/**
+ * Remove a device from the local catalog.
+ * Returns `true` if the device is removed, `false` otherwise.
+ */
+async function removeDevice(device, type = "phones") {
+ await loadLocalDevices();
+ const list = localDevices[type];
+ if (!list) {
+ return false;
+ }
+
+ const index = list.findIndex(entry => entry.name == device.name);
+ if (index == -1) {
+ return false;
+ }
+
+ list.splice(index, 1);
+ await asyncStorage.setItem(LOCAL_DEVICES, JSON.stringify(localDevices));
+
+ return true;
+}
+
+/**
+ * Remove all local devices. Useful to clear everything when testing.
+ */
+async function removeLocalDevices() {
+ await asyncStorage.removeItem(LOCAL_DEVICES);
+ localDevices = {};
+}
+
+/**
+ * Get the complete devices catalog.
+ */
+async function getDevices() {
+ const records = await RemoteSettings("devtools-devices").get();
+ const devicesByType = new Map();
+ for (const record of records) {
+ const { type } = record;
+ if (!devicesByType.has(type)) {
+ devicesByType.set(type, []);
+ }
+ devicesByType.get(type).push(record);
+ }
+
+ await loadLocalDevices();
+ for (const type in localDevices) {
+ if (!devicesByType.has(type)) {
+ devicesByType.set(type, []);
+ }
+ devicesByType.get(type).push(...localDevices[type]);
+ }
+ return devicesByType;
+}
+
+/**
+ * Get the localized string for a device type.
+ */
+function getDeviceString(deviceType) {
+ return L10N.getStr("device." + deviceType);
+}
+
+module.exports = {
+ addDevice,
+ editDevice,
+ removeDevice,
+ removeLocalDevices,
+ getDevices,
+ getDeviceString,
+};
diff --git a/devtools/client/shared/enum.js b/devtools/client/shared/enum.js
new file mode 100644
index 0000000000..4c3bab958a
--- /dev/null
+++ b/devtools/client/shared/enum.js
@@ -0,0 +1,19 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ /**
+ * Create a simple enum-like object with keys mirrored to values from an array.
+ * This makes comparison to a specfic value simpler without having to repeat and
+ * mis-type the value.
+ */
+ createEnum(array, target = {}) {
+ for (const key of array) {
+ target[key] = key;
+ }
+ return target;
+ },
+};
diff --git a/devtools/client/shared/events.js b/devtools/client/shared/events.js
new file mode 100644
index 0000000000..fcfd32d3bf
--- /dev/null
+++ b/devtools/client/shared/events.js
@@ -0,0 +1,22 @@
+/* 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/. */
+"use strict";
+
+/**
+ * Prevent event default behaviour and stop its propagation.
+ * @param {Object} event
+ * Event or react synthetic event.
+ */
+exports.preventDefaultAndStopPropagation = function (event) {
+ event.preventDefault();
+ event.stopPropagation();
+ if (event.nativeEvent) {
+ if (event.nativeEvent.preventDefault) {
+ event.nativeEvent.preventDefault();
+ }
+ if (event.nativeEvent.stopPropagation) {
+ event.nativeEvent.stopPropagation();
+ }
+ }
+};
diff --git a/devtools/client/shared/fluent-l10n/fluent-l10n.js b/devtools/client/shared/fluent-l10n/fluent-l10n.js
new file mode 100644
index 0000000000..d3a5c33408
--- /dev/null
+++ b/devtools/client/shared/fluent-l10n/fluent-l10n.js
@@ -0,0 +1,64 @@
+/* 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/. */
+
+"use strict";
+
+const FluentReact = require("resource://devtools/client/shared/vendor/fluent-react.js");
+
+/**
+ * Wrapper over FluentReact. It encapsulates instantiation of the localization
+ * bundles, and offers a simpler way of accessing `getString`.
+ */
+class FluentL10n {
+ /**
+ * Initializes the wrapper, generating the bundles for the given resource ids.
+ * It can optionally add the right attributes to the document element.
+ * @param {Array} resourceIds
+ * @param {Object} [options]
+ * @param {boolean} [options.setAttributesOnDocument]
+ */
+ async init(resourceIds, { setAttributesOnDocument } = {}) {
+ if (setAttributesOnDocument) {
+ const primaryLocale = Services.locale.appLocalesAsBCP47[0];
+ document.documentElement.setAttribute("lang", primaryLocale);
+ const direction = Services.locale.isAppLocaleRTL ? "rtl" : "ltr";
+ document.documentElement.setAttribute("dir", direction);
+ }
+
+ const locales = Services.locale.appLocalesAsBCP47;
+ const generator = L10nRegistry.getInstance().generateBundles(
+ locales,
+ resourceIds
+ );
+
+ this._bundles = [];
+ for await (const bundle of generator) {
+ this._bundles.push(bundle);
+ }
+ this._reactLocalization = new FluentReact.ReactLocalization(this._bundles);
+ }
+
+ /**
+ * Returns the fluent bundles generated.
+ */
+ getBundles() {
+ return this._bundles;
+ }
+
+ /**
+ * Returns the localized string for the provided id, formatted using args.
+ */
+ getString(id, args, fallback) {
+ // Forward arguments via .apply() so that the original method can:
+ // - perform asserts based on the number of arguments
+ // - add new arguments
+ return this._reactLocalization.getString.apply(
+ this._reactLocalization,
+ arguments
+ );
+ }
+}
+
+// Export the class
+exports.FluentL10n = FluentL10n;
diff --git a/devtools/client/shared/fluent-l10n/moz.build b/devtools/client/shared/fluent-l10n/moz.build
new file mode 100644
index 0000000000..654348ea26
--- /dev/null
+++ b/devtools/client/shared/fluent-l10n/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "fluent-l10n.js",
+)
diff --git a/devtools/client/shared/focus.js b/devtools/client/shared/focus.js
new file mode 100644
index 0000000000..6248c69573
--- /dev/null
+++ b/devtools/client/shared/focus.js
@@ -0,0 +1,73 @@
+/* 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/. */
+
+"use strict";
+
+/*
+ * Simplied selector targetting elements that can receive the focus, full
+ * version at http://stackoverflow.com/questions/1599660/which-html-elements-can-receive-focus
+ */
+const focusableSelector = [
+ "a[href]:not([tabindex='-1'])",
+ "button:not([disabled], [tabindex='-1'])",
+ "iframe:not([tabindex='-1'])",
+ "input:not([disabled], [tabindex='-1'])",
+ "select:not([disabled], [tabindex='-1'])",
+ "textarea:not([disabled], [tabindex='-1'])",
+ "[tabindex]:not([tabindex='-1'])",
+].join(", ");
+
+/**
+ * Wrap and move keyboard focus to first/last focusable element inside a container
+ * element to prevent the focus from escaping the container.
+ *
+ * @param {Array} elms
+ * focusable elements inside a container
+ * @param {DOMNode} current
+ * currently focused element inside containter
+ * @param {Boolean} back
+ * direction
+ * @return {DOMNode}
+ * newly focused element
+ */
+function wrapMoveFocus(elms, current, back) {
+ let next;
+
+ if (elms.length === 0) {
+ return false;
+ }
+
+ if (back) {
+ if (elms.indexOf(current) === 0) {
+ next = elms[elms.length - 1];
+ next.focus();
+ }
+ } else if (elms.indexOf(current) === elms.length - 1) {
+ next = elms[0];
+ next.focus();
+ }
+
+ return next;
+}
+
+/**
+ * Get a list of all elements that are focusable with a keyboard inside the parent element
+ *
+ * @param {DOMNode} parentEl
+ * parent DOM element to be queried
+ * @return {Array}
+ * array of focusable children elements inside the parent
+ */
+function getFocusableElements(parentEl) {
+ return parentEl
+ ? Array.from(parentEl.querySelectorAll(focusableSelector))
+ : [];
+}
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ module.exports.focusableSelector = focusableSelector;
+ exports.wrapMoveFocus = wrapMoveFocus;
+ exports.getFocusableElements = getFocusableElements;
+});
diff --git a/devtools/client/shared/inplace-editor.js b/devtools/client/shared/inplace-editor.js
new file mode 100644
index 0000000000..fa97b2a982
--- /dev/null
+++ b/devtools/client/shared/inplace-editor.js
@@ -0,0 +1,1964 @@
+/* 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/. */
+
+/**
+ * Basic use:
+ * let spanToEdit = document.getElementById("somespan");
+ *
+ * editableField({
+ * element: spanToEdit,
+ * done: function(value, commit, direction, key) {
+ * if (commit) {
+ * spanToEdit.textContent = value;
+ * }
+ * },
+ * trigger: "dblclick"
+ * });
+ *
+ * See editableField() for more options.
+ */
+
+"use strict";
+
+const focusManager = Services.focus;
+const isOSX = Services.appinfo.OS === "Darwin";
+const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const {
+ findMostRelevantCssPropertyIndex,
+} = require("resource://devtools/client/shared/suggestion-picker.js");
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const CONTENT_TYPES = {
+ PLAIN_TEXT: 0,
+ CSS_VALUE: 1,
+ CSS_MIXED: 2,
+ CSS_PROPERTY: 3,
+};
+
+// The limit of 500 autocomplete suggestions should not be reached but is kept
+// for safety.
+const MAX_POPUP_ENTRIES = 500;
+
+const FOCUS_FORWARD = focusManager.MOVEFOCUS_FORWARD;
+const FOCUS_BACKWARD = focusManager.MOVEFOCUS_BACKWARD;
+
+const WORD_REGEXP = /\w/;
+const isWordChar = function (str) {
+ return str && WORD_REGEXP.test(str);
+};
+
+const GRID_PROPERTY_NAMES = [
+ "grid-area",
+ "grid-row",
+ "grid-row-start",
+ "grid-row-end",
+ "grid-column",
+ "grid-column-start",
+ "grid-column-end",
+];
+const GRID_ROW_PROPERTY_NAMES = [
+ "grid-area",
+ "grid-row",
+ "grid-row-start",
+ "grid-row-end",
+];
+const GRID_COL_PROPERTY_NAMES = [
+ "grid-area",
+ "grid-column",
+ "grid-column-start",
+ "grid-column-end",
+];
+
+/**
+ * Helper to check if the provided key matches one of the expected keys.
+ * Keys will be prefixed with DOM_VK_ and should match a key in KeyCodes.
+ *
+ * @param {String} key
+ * the key to check (can be a keyCode).
+ * @param {...String} keys
+ * list of possible keys allowed.
+ * @return {Boolean} true if the key matches one of the keys.
+ */
+function isKeyIn(key, ...keys) {
+ return keys.some(expectedKey => {
+ return key === KeyCodes["DOM_VK_" + expectedKey];
+ });
+}
+
+/**
+ * Mark a span editable. |editableField| will listen for the span to
+ * be focused and create an InlineEditor to handle text input.
+ * Changes will be committed when the InlineEditor's input is blurred
+ * or dropped when the user presses escape.
+ *
+ * @param {Object} options: Options for the editable field
+ * @param {Element} options.element:
+ * (required) The span to be edited on focus.
+ * @param {Function} options.canEdit:
+ * Will be called before creating the inplace editor. Editor
+ * won't be created if canEdit returns false.
+ * @param {Function} options.start:
+ * Will be called when the inplace editor is initialized.
+ * @param {Function} options.change:
+ * Will be called when the text input changes. Will be called
+ * with the current value of the text input.
+ * @param {Function} options.done:
+ * Called when input is committed or blurred. Called with
+ * current value, a boolean telling the caller whether to
+ * commit the change, the direction of the next element to be
+ * selected and the event keybode. Direction may be one of Services.focus.MOVEFOCUS_FORWARD,
+ * Services.focus.MOVEFOCUS_BACKWARD, or null (no movement).
+ * This function is called before the editor has been torn down.
+ * @param {Function} options.destroy:
+ * Called when the editor is destroyed and has been torn down.
+ * @param {Function} options.contextMenu:
+ * Called when the user triggers a contextmenu event on the input.
+ * @param {Object} options.advanceChars:
+ * This can be either a string or a function.
+ * If it is a string, then if any characters in it are typed,
+ * focus will advance to the next element.
+ * Otherwise, if it is a function, then the function will
+ * be called with three arguments: a key code, the current text,
+ * and the insertion point. If the function returns true,
+ * then the focus advance takes place. If it returns false,
+ * then the character is inserted instead.
+ * @param {Boolean} options.stopOnReturn:
+ * If true, the return key will not advance the editor to the next
+ * focusable element. Note that Ctrl/Cmd+Enter will still advance the editor
+ * @param {Boolean} options.stopOnTab:
+ * If true, the tab key will not advance the editor to the next
+ * focusable element.
+ * @param {Boolean} options.stopOnShiftTab:
+ * If true, shift tab will not advance the editor to the previous
+ * focusable element.
+ * @param {String} options.trigger: The DOM event that should trigger editing,
+ * defaults to "click"
+ * @param {Boolean} options.multiline: Should the editor be a multiline textarea?
+ * defaults to false
+ * @param {Function or options.Number} maxWidth:
+ * Should the editor wrap to remain below the provided max width. Only
+ * available if multiline is true. If a function is provided, it will be
+ * called when replacing the element by the inplace input.
+ * @param {Boolean} options.trimOutput: Should the returned string be trimmed?
+ * defaults to true
+ * @param {Boolean} options.preserveTextStyles: If true, do not copy text-related styles
+ * from `element` to the new input.
+ * defaults to false
+ * @param {Object} options.cssProperties: An instance of CSSProperties.
+ * @param {Object} options.cssVariables: A Map object containing all CSS variables.
+ * @param {Number} options.defaultIncrement: The value by which the input is incremented
+ * or decremented by default (0.1 for properties like opacity and 1 by default)
+ * @param {Function} options.getGridLineNames:
+ * Will be called before offering autocomplete sugestions, if the property is
+ * a member of GRID_PROPERTY_NAMES.
+ * @param {Boolean} options.showSuggestCompletionOnEmpty:
+ * If true, show the suggestions in case that the current text becomes empty.
+ * Defaults to false.
+ * @param {Boolean} options.focusEditableFieldAfterApply
+ * If true, try to focus the next editable field after the input value is commited.
+ * When set to true, focusEditableFieldContainerSelector is mandatory.
+ * If no editable field can be found within the element retrieved with
+ * focusEditableFieldContainerSelector, the focus will be moved to the next focusable
+ * element (which won't be an editable field)
+ * @param {String} options.focusEditableFieldContainerSelector
+ * A CSS selector that will be used to retrieve the container element into which
+ * the next focused element should be in, when focusEditableFieldAfterApply
+ * is set to true. This allows to bail out if we can't find a suitable
+ * focusable field.
+ * @param {String} options.inputAriaLabel
+ * Optional aria-label attribute value that will be added to the input.
+ * @param {String} options.inputAriaLabelledBy
+ * Optional aria-labelled-by attribute value that will be added to the input.
+ */
+function editableField(options) {
+ return editableItem(options, function (element, event) {
+ if (!options.element.inplaceEditor) {
+ new InplaceEditor(options, event);
+ }
+ });
+}
+
+exports.editableField = editableField;
+
+/**
+ * Handle events for an element that should respond to
+ * clicks and sit in the editing tab order, and call
+ * a callback when it is activated.
+ *
+ * @param {Object} options
+ * The options for this editor, including:
+ * {Element} element: The DOM element.
+ * {String} trigger: The DOM event that should trigger editing,
+ * defaults to "click"
+ * @param {Function} callback
+ * Called when the editor is activated.
+ * @return {Function} function which calls callback
+ */
+function editableItem(options, callback) {
+ const trigger = options.trigger || "click";
+ const element = options.element;
+ element.addEventListener(trigger, function (evt) {
+ if (evt.target.nodeName !== "a") {
+ const win = this.ownerDocument.defaultView;
+ const selection = win.getSelection();
+ if (trigger != "click" || selection.isCollapsed) {
+ callback(element, evt);
+ }
+ evt.stopPropagation();
+ }
+ });
+
+ // If focused by means other than a click, start editing by
+ // pressing enter or space.
+ element.addEventListener(
+ "keypress",
+ function (evt) {
+ if (evt.target.nodeName === "button") {
+ return;
+ }
+
+ if (isKeyIn(evt.keyCode, "RETURN") || isKeyIn(evt.charCode, "SPACE")) {
+ callback(element);
+ }
+ },
+ true
+ );
+
+ // Ugly workaround - the element is focused on mousedown but
+ // the editor is activated on click/mouseup. This leads
+ // to an ugly flash of the focus ring before showing the editor.
+ // So hide the focus ring while the mouse is down.
+ element.addEventListener("mousedown", function (evt) {
+ if (evt.target.nodeName !== "a") {
+ const cleanup = function () {
+ element.style.removeProperty("outline-style");
+ element.removeEventListener("mouseup", cleanup);
+ element.removeEventListener("mouseout", cleanup);
+ };
+ element.style.setProperty("outline-style", "none");
+ element.addEventListener("mouseup", cleanup);
+ element.addEventListener("mouseout", cleanup);
+ }
+ });
+
+ // Mark the element editable field for tab
+ // navigation while editing.
+ element._editable = true;
+
+ // Save the trigger type so we can dispatch this later
+ element._trigger = trigger;
+
+ // Add button semantics to the element, to indicate that it can be activated.
+ element.setAttribute("role", "button");
+
+ return function turnOnEditMode() {
+ callback(element);
+ };
+}
+
+exports.editableItem = editableItem;
+
+/*
+ * Various API consumers (especially tests) sometimes want to grab the
+ * inplaceEditor expando off span elements. However, when each global has its
+ * own compartment, those expandos live on Xray wrappers that are only visible
+ * within this JSM. So we provide a little workaround here.
+ */
+
+function getInplaceEditorForSpan(span) {
+ return span.inplaceEditor;
+}
+
+exports.getInplaceEditorForSpan = getInplaceEditorForSpan;
+
+class InplaceEditor extends EventEmitter {
+ constructor(options, event) {
+ super();
+
+ this.elt = options.element;
+ const doc = this.elt.ownerDocument;
+ this.doc = doc;
+ this.elt.inplaceEditor = this;
+ this.cssProperties = options.cssProperties;
+ this.cssVariables = options.cssVariables || new Map();
+ this.change = options.change;
+ this.done = options.done;
+ this.contextMenu = options.contextMenu;
+ this.defaultIncrement = options.defaultIncrement || 1;
+ this.destroy = options.destroy;
+ this.initial = options.initial ? options.initial : this.elt.textContent;
+ this.multiline = options.multiline || false;
+ this.maxWidth = options.maxWidth;
+ if (typeof this.maxWidth == "function") {
+ this.maxWidth = this.maxWidth();
+ }
+
+ this.trimOutput =
+ options.trimOutput === undefined ? true : !!options.trimOutput;
+ this.stopOnShiftTab = !!options.stopOnShiftTab;
+ this.stopOnTab = !!options.stopOnTab;
+ this.stopOnReturn = !!options.stopOnReturn;
+ this.contentType = options.contentType || CONTENT_TYPES.PLAIN_TEXT;
+ this.property = options.property;
+ this.popup = options.popup;
+ this.preserveTextStyles =
+ options.preserveTextStyles === undefined
+ ? false
+ : !!options.preserveTextStyles;
+ this.showSuggestCompletionOnEmpty = !!options.showSuggestCompletionOnEmpty;
+ this.focusEditableFieldAfterApply =
+ options.focusEditableFieldAfterApply === true;
+ this.focusEditableFieldContainerSelector =
+ options.focusEditableFieldContainerSelector;
+
+ if (
+ this.focusEditableFieldAfterApply &&
+ !this.focusEditableFieldContainerSelector
+ ) {
+ throw new Error(
+ "focusEditableFieldContainerSelector is mandatory when focusEditableFieldAfterApply is true"
+ );
+ }
+
+ this.#createInput(options);
+
+ // Hide the provided element and add our editor.
+ this.originalDisplay = this.elt.style.display;
+ this.elt.style.display = "none";
+ this.elt.parentNode.insertBefore(this.input, this.elt);
+
+ // After inserting the input to have all CSS styles applied, start autosizing.
+ this.#autosize();
+
+ this.inputCharDimensions = this.#getInputCharDimensions();
+ // Pull out character codes for advanceChars, listing the
+ // characters that should trigger a blur.
+ if (typeof options.advanceChars === "function") {
+ this.#advanceChars = options.advanceChars;
+ } else {
+ const advanceCharcodes = {};
+ const advanceChars = options.advanceChars || "";
+ for (let i = 0; i < advanceChars.length; i++) {
+ advanceCharcodes[advanceChars.charCodeAt(i)] = true;
+ }
+ this.#advanceChars = charCode => charCode in advanceCharcodes;
+ }
+
+ this.input.focus();
+
+ if (typeof options.selectAll == "undefined" || options.selectAll) {
+ this.input.select();
+ }
+
+ const win = doc.defaultView;
+ this.#abortController = new win.AbortController();
+ const eventListenerConfig = { signal: this.#abortController.signal };
+
+ this.input.addEventListener("blur", this.#onBlur, eventListenerConfig);
+ this.input.addEventListener(
+ "keypress",
+ this.#onKeyPress,
+ eventListenerConfig
+ );
+ this.input.addEventListener("input", this.#onInput, eventListenerConfig);
+ this.input.addEventListener(
+ "dblclick",
+ this.#stopEventPropagation,
+ eventListenerConfig
+ );
+ this.input.addEventListener(
+ "click",
+ this.#stopEventPropagation,
+ eventListenerConfig
+ );
+ this.input.addEventListener(
+ "mousedown",
+ this.#stopEventPropagation,
+ eventListenerConfig
+ );
+ this.input.addEventListener(
+ "contextmenu",
+ this.#onContextMenu,
+ eventListenerConfig
+ );
+ win.addEventListener("blur", this.#onWindowBlur, eventListenerConfig);
+
+ this.validate = options.validate;
+
+ if (this.validate) {
+ this.input.addEventListener("keyup", this.#onKeyup, eventListenerConfig);
+ }
+
+ this.#updateSize();
+
+ if (options.start) {
+ options.start(this, event);
+ }
+
+ this.#getGridNamesBeforeCompletion(options.getGridLineNames);
+ }
+ static CONTENT_TYPES = CONTENT_TYPES;
+
+ #abortController;
+ #advanceChars;
+ #applied;
+ #measurement;
+ #openPopupTimeout;
+ #pressedKey;
+ #preventSuggestions;
+ #selectedIndex;
+
+ get currentInputValue() {
+ const val = this.trimOutput ? this.input.value.trim() : this.input.value;
+ return val;
+ }
+
+ /**
+ * Create the input element.
+ *
+ * @param {Object} options
+ * @param {String} options.inputAriaLabel
+ * Optional aria-label attribute value that will be added to the input.
+ * @param {String} options.inputAriaLabelledBy
+ * Optional aria-labelledby attribute value that will be added to the input.
+ */
+ #createInput(options = {}) {
+ this.input = this.doc.createElementNS(
+ HTML_NS,
+ this.multiline ? "textarea" : "input"
+ );
+ this.input.inplaceEditor = this;
+
+ if (this.multiline) {
+ // Hide the textarea resize handle.
+ this.input.style.resize = "none";
+ this.input.style.overflow = "hidden";
+ // Also reset padding.
+ this.input.style.padding = "0";
+ }
+
+ this.input.classList.add("styleinspector-propertyeditor");
+ this.input.value = this.initial;
+ if (options.inputAriaLabel) {
+ this.input.setAttribute("aria-label", options.inputAriaLabel);
+ } else if (options.inputAriaLabelledBy) {
+ this.input.setAttribute("aria-labelledby", options.inputAriaLabelledBy);
+ }
+
+ if (!this.preserveTextStyles) {
+ copyTextStyles(this.elt, this.input);
+ }
+ }
+
+ /**
+ * Get rid of the editor.
+ */
+ #clear() {
+ if (!this.input) {
+ // Already cleared.
+ return;
+ }
+
+ this.#abortController.abort();
+ this.#stopAutosize();
+
+ this.elt.style.display = this.originalDisplay;
+
+ if (this.doc.activeElement == this.input) {
+ this.elt.focus();
+ }
+
+ this.input.remove();
+ this.input = null;
+
+ delete this.elt.inplaceEditor;
+ delete this.elt;
+
+ if (this.destroy) {
+ this.destroy();
+ }
+ }
+
+ /**
+ * Keeps the editor close to the size of its input string. This is pretty
+ * crappy, suggestions for improvement welcome.
+ */
+ #autosize() {
+ // Create a hidden, absolutely-positioned span to measure the text
+ // in the input. Boo.
+
+ // We can't just measure the original element because a) we don't
+ // change the underlying element's text ourselves (we leave that
+ // up to the client), and b) without tweaking the style of the
+ // original element, it might wrap differently or something.
+ this.#measurement = this.doc.createElementNS(
+ HTML_NS,
+ this.multiline ? "pre" : "span"
+ );
+ this.#measurement.className = "autosizer";
+ this.elt.parentNode.appendChild(this.#measurement);
+ const style = this.#measurement.style;
+ style.visibility = "hidden";
+ style.position = "absolute";
+ style.top = "0";
+ style.left = "0";
+
+ if (this.multiline) {
+ style.whiteSpace = "pre-wrap";
+ style.wordWrap = "break-word";
+ if (this.maxWidth) {
+ style.maxWidth = this.maxWidth + "px";
+ // Use position fixed to measure dimensions without any influence from
+ // the container of the editor.
+ style.position = "fixed";
+ }
+ }
+
+ copyAllStyles(this.input, this.#measurement);
+ this.#updateSize();
+ }
+
+ /**
+ * Clean up the mess created by _autosize().
+ */
+ #stopAutosize() {
+ if (!this.#measurement) {
+ return;
+ }
+ this.#measurement.remove();
+ this.#measurement = null;
+ }
+
+ /**
+ * Size the editor to fit its current contents.
+ */
+ #updateSize() {
+ // Replace spaces with non-breaking spaces. Otherwise setting
+ // the span's textContent will collapse spaces and the measurement
+ // will be wrong.
+ let content = this.input.value;
+ const unbreakableSpace = "\u00a0";
+
+ // Make sure the content is not empty.
+ if (content === "") {
+ content = unbreakableSpace;
+ }
+
+ // If content ends with a new line, add a blank space to force the autosize
+ // element to adapt its height.
+ if (content.lastIndexOf("\n") === content.length - 1) {
+ content = content + unbreakableSpace;
+ }
+
+ if (!this.multiline) {
+ content = content.replace(/ /g, unbreakableSpace);
+ }
+
+ this.#measurement.textContent = content;
+
+ // Do not use offsetWidth: it will round floating width values.
+ let width = this.#measurement.getBoundingClientRect().width;
+ if (this.multiline) {
+ if (this.maxWidth) {
+ width = Math.min(this.maxWidth, width);
+ }
+ const height = this.#measurement.getBoundingClientRect().height;
+ this.input.style.height = height + "px";
+ }
+ this.input.style.width = width + "px";
+ }
+
+ /**
+ * Get the width and height of a single character in the input to properly
+ * position the autocompletion popup.
+ */
+ #getInputCharDimensions() {
+ // Just make the text content to be 'x' to get the width and height of any
+ // character in a monospace font.
+ this.#measurement.textContent = "x";
+ const width = this.#measurement.clientWidth;
+ const height = this.#measurement.clientHeight;
+ return { width, height };
+ }
+
+ /**
+ * Increment property values in rule view.
+ *
+ * @param {Number} increment
+ * The amount to increase/decrease the property value.
+ * @return {Boolean} true if value has been incremented.
+ */
+ #incrementValue(increment) {
+ const value = this.input.value;
+ const selectionStart = this.input.selectionStart;
+ const selectionEnd = this.input.selectionEnd;
+
+ const newValue = this.#incrementCSSValue(
+ value,
+ increment,
+ selectionStart,
+ selectionEnd
+ );
+
+ if (!newValue) {
+ return false;
+ }
+
+ this.input.value = newValue.value;
+ this.input.setSelectionRange(newValue.start, newValue.end);
+ this.#doValidation();
+
+ // Call the user's change handler if available.
+ if (this.change) {
+ this.change(this.currentInputValue);
+ }
+
+ return true;
+ }
+
+ /**
+ * Increment the property value based on the property type.
+ *
+ * @param {String} value
+ * Property value.
+ * @param {Number} increment
+ * Amount to increase/decrease the property value.
+ * @param {Number} selStart
+ * Starting index of the value.
+ * @param {Number} selEnd
+ * Ending index of the value.
+ * @return {Object} object with properties 'value', 'start', and 'end'.
+ */
+ #incrementCSSValue(value, increment, selStart, selEnd) {
+ const range = this.#parseCSSValue(value, selStart);
+ const type = range?.type || "";
+ const rawValue = range ? value.substring(range.start, range.end) : "";
+ const preRawValue = range ? value.substr(0, range.start) : "";
+ const postRawValue = range ? value.substr(range.end) : "";
+ let info;
+
+ let incrementedValue = null,
+ selection;
+ if (type === "num") {
+ if (rawValue == "0") {
+ info = {};
+ info.units = this.#findCompatibleUnit(preRawValue, postRawValue);
+ }
+
+ const newValue = this.#incrementRawValue(rawValue, increment, info);
+ if (newValue !== null) {
+ incrementedValue = newValue;
+ selection = [0, incrementedValue.length];
+ }
+ } else if (type === "hex") {
+ const exprOffset = selStart - range.start;
+ const exprOffsetEnd = selEnd - range.start;
+ const newValue = this.#incHexColor(
+ rawValue,
+ increment,
+ exprOffset,
+ exprOffsetEnd
+ );
+ if (newValue) {
+ incrementedValue = newValue.value;
+ selection = newValue.selection;
+ }
+ } else {
+ if (type === "rgb" || type === "hsl" || type === "hwb") {
+ info = {};
+ const isCSS4Color = !value.includes(",");
+ // In case the value uses the new syntax of the CSS Color 4 specification,
+ // it is split by the spaces and the slash separating the alpha value
+ // between the different color components.
+ // Example: rgb(255 0 0 / 0.5)
+ // Otherwise, the value is represented using the old color syntax and is
+ // split by the commas between the color components.
+ // Example: rgba(255, 0, 0, 0.5)
+ const part =
+ value
+ .substring(range.start, selStart)
+ .split(isCSS4Color ? / ?\/ ?| / : ",").length - 1;
+ if (part === 3) {
+ // alpha
+ info.minValue = 0;
+ info.maxValue = 1;
+ } else if (type === "rgb") {
+ info.minValue = 0;
+ info.maxValue = 255;
+ } else if (part !== 0) {
+ // hsl or hwb percentage
+ info.minValue = 0;
+ info.maxValue = 100;
+
+ // select the previous number if the selection is at the end of a
+ // percentage sign.
+ if (value.charAt(selStart - 1) === "%") {
+ --selStart;
+ }
+ }
+ }
+ return this.#incrementGenericValue(
+ value,
+ increment,
+ selStart,
+ selEnd,
+ info
+ );
+ }
+
+ if (incrementedValue === null) {
+ return null;
+ }
+
+ return {
+ value: preRawValue + incrementedValue + postRawValue,
+ start: range.start + selection[0],
+ end: range.start + selection[1],
+ };
+ }
+
+ /**
+ * Find a compatible unit to use for a CSS number value inserted between the
+ * provided beforeValue and afterValue. The compatible unit will be picked
+ * from a selection of default units corresponding to supported CSS value
+ * dimensions (distance, angle, duration).
+ *
+ * @param {String} beforeValue
+ * The string preceeding the number value in the current property
+ * value.
+ * @param {String} afterValue
+ * The string following the number value in the current property value.
+ * @return {String} a valid unit that can be used for this number value or
+ * empty string if no match could be found.
+ */
+ #findCompatibleUnit(beforeValue, afterValue) {
+ if (!this.property || !this.property.name) {
+ return "";
+ }
+
+ // A DOM element is used to test the validity of various units. This is to
+ // avoid having to do an async call to the server to get this information.
+ const el = this.doc.createElement("div");
+
+ // Cycle through unitless (""), pixels, degrees and seconds.
+ const units = ["", "px", "deg", "s"];
+ for (const unit of units) {
+ const value = beforeValue + "1" + unit + afterValue;
+ el.style.setProperty(this.property.name, "");
+ el.style.setProperty(this.property.name, value);
+ // The property was set to `""` first, so if the value is no longer `""`,
+ // it means that the second `setProperty` call set a valid property and we
+ // can use this unit.
+ if (el.style.getPropertyValue(this.property.name) !== "") {
+ return unit;
+ }
+ }
+ return "";
+ }
+
+ /**
+ * Parses the property value and type.
+ *
+ * @param {String} value
+ * Property value.
+ * @param {Number} offset
+ * Starting index of value.
+ * @return {Object} object with properties 'value', 'start', 'end', and
+ * 'type'.
+ */
+ #parseCSSValue(value, offset) {
+ /* eslint-disable max-len */
+ const reSplitCSS =
+ /(?<url>url\("?[^"\)]+"?\)?)|(?<rgb>rgba?\([^)]*\)?)|(?<hsl>hsla?\([^)]*\)?)|(?<hwb>hwb\([^)]*\)?)|(?<hex>#[\dA-Fa-f]+)|(?<number>-?\d*\.?\d+(%|[a-z]{1,4})?)|"([^"]*)"?|'([^']*)'?|([^,\s\/!\(\)]+)|(!(.*)?)/;
+ /* eslint-enable */
+ let start = 0;
+ let m;
+
+ // retreive values from left to right until we find the one at our offset
+ while ((m = reSplitCSS.exec(value)) && m.index + m[0].length < offset) {
+ value = value.substring(m.index + m[0].length);
+ start += m.index + m[0].length;
+ offset -= m.index + m[0].length;
+ }
+
+ if (!m) {
+ return null;
+ }
+
+ let type;
+ if (m.groups.url) {
+ type = "url";
+ } else if (m.groups.rgb) {
+ type = "rgb";
+ } else if (m.groups.hsl) {
+ type = "hsl";
+ } else if (m.groups.hwb) {
+ type = "hwb";
+ } else if (m.groups.hex) {
+ type = "hex";
+ } else if (m.groups.number) {
+ type = "num";
+ }
+
+ return {
+ value: m[0],
+ start: start + m.index,
+ end: start + m.index + m[0].length,
+ type,
+ };
+ }
+
+ /**
+ * Increment the property value for types other than
+ * number or hex, such as rgb, hsl, hwb, and file names.
+ *
+ * @param {String} value
+ * Property value.
+ * @param {Number} increment
+ * Amount to increment/decrement.
+ * @param {Number} offset
+ * Starting index of the property value.
+ * @param {Number} offsetEnd
+ * Ending index of the property value.
+ * @param {Object} info
+ * Object with details about the property value.
+ * @return {Object} object with properties 'value', 'start', and 'end'.
+ */
+ #incrementGenericValue(value, increment, offset, offsetEnd, info) {
+ // Try to find a number around the cursor to increment.
+ let start, end;
+ // Check if we are incrementing in a non-number context (such as a URL)
+ if (
+ /^-?[0-9.]/.test(value.substring(offset, offsetEnd)) &&
+ !/\d/.test(value.charAt(offset - 1) + value.charAt(offsetEnd))
+ ) {
+ // We have a number selected, possibly with a suffix, and we are not in
+ // the disallowed case of just part of a known number being selected.
+ // Use that number.
+ start = offset;
+ end = offsetEnd;
+ } else {
+ // Parse periods as belonging to the number only if we are in a known
+ // number context. (This makes incrementing the 1 in 'image1.gif' work.)
+ const pattern = "[" + (info ? "0-9." : "0-9") + "]*";
+ const before = new RegExp(pattern + "$").exec(value.substr(0, offset))[0]
+ .length;
+ const after = new RegExp("^" + pattern).exec(value.substr(offset))[0]
+ .length;
+
+ start = offset - before;
+ end = offset + after;
+
+ // Expand the number to contain an initial minus sign if it seems
+ // free-standing.
+ if (
+ value.charAt(start - 1) === "-" &&
+ (start - 1 === 0 || /[ (:,='"]/.test(value.charAt(start - 2)))
+ ) {
+ --start;
+ }
+ }
+
+ if (start !== end) {
+ // Include percentages as part of the incremented number (they are
+ // common enough).
+ if (value.charAt(end) === "%") {
+ ++end;
+ }
+
+ const first = value.substr(0, start);
+ let mid = value.substring(start, end);
+ const last = value.substr(end);
+
+ mid = this.#incrementRawValue(mid, increment, info);
+
+ if (mid !== null) {
+ return {
+ value: first + mid + last,
+ start,
+ end: start + mid.length,
+ };
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Increment the property value for numbers.
+ *
+ * @param {String} rawValue
+ * Raw value to increment.
+ * @param {Number} increment
+ * Amount to increase/decrease the raw value.
+ * @param {Object} info
+ * Object with info about the property value.
+ * @return {String} the incremented value.
+ */
+ #incrementRawValue(rawValue, increment, info) {
+ const num = parseFloat(rawValue);
+
+ if (isNaN(num)) {
+ return null;
+ }
+
+ const number = /\d+(\.\d+)?/.exec(rawValue);
+
+ let units = rawValue.substr(number.index + number[0].length);
+ if (info && "units" in info) {
+ units = info.units;
+ }
+
+ // avoid rounding errors
+ let newValue = Math.round((num + increment) * 1000) / 1000;
+
+ if (info && "minValue" in info) {
+ newValue = Math.max(newValue, info.minValue);
+ }
+ if (info && "maxValue" in info) {
+ newValue = Math.min(newValue, info.maxValue);
+ }
+
+ newValue = newValue.toString();
+
+ return newValue + units;
+ }
+
+ /**
+ * Increment the property value for hex.
+ *
+ * @param {String} value
+ * Property value.
+ * @param {Number} increment
+ * Amount to increase/decrease the property value.
+ * @param {Number} offset
+ * Starting index of the property value.
+ * @param {Number} offsetEnd
+ * Ending index of the property value.
+ * @return {Object} object with properties 'value' and 'selection'.
+ */
+ #incHexColor(rawValue, increment, offset, offsetEnd) {
+ // Return early if no part of the rawValue is selected.
+ if (offsetEnd > rawValue.length && offset >= rawValue.length) {
+ return null;
+ }
+ if (offset < 1 && offsetEnd <= 1) {
+ return null;
+ }
+ // Ignore the leading #.
+ rawValue = rawValue.substr(1);
+ --offset;
+ --offsetEnd;
+
+ // Clamp the selection to within the actual value.
+ offset = Math.max(offset, 0);
+ offsetEnd = Math.min(offsetEnd, rawValue.length);
+ offsetEnd = Math.max(offsetEnd, offset);
+
+ // Normalize #ABC -> #AABBCC.
+ if (rawValue.length === 3) {
+ rawValue =
+ rawValue.charAt(0) +
+ rawValue.charAt(0) +
+ rawValue.charAt(1) +
+ rawValue.charAt(1) +
+ rawValue.charAt(2) +
+ rawValue.charAt(2);
+ offset *= 2;
+ offsetEnd *= 2;
+ }
+
+ // Normalize #ABCD -> #AABBCCDD.
+ if (rawValue.length === 4) {
+ rawValue =
+ rawValue.charAt(0) +
+ rawValue.charAt(0) +
+ rawValue.charAt(1) +
+ rawValue.charAt(1) +
+ rawValue.charAt(2) +
+ rawValue.charAt(2) +
+ rawValue.charAt(3) +
+ rawValue.charAt(3);
+ offset *= 2;
+ offsetEnd *= 2;
+ }
+
+ if (rawValue.length !== 6 && rawValue.length !== 8) {
+ return null;
+ }
+
+ // If no selection, increment an adjacent color, preferably one to the left.
+ if (offset === offsetEnd) {
+ if (offset === 0) {
+ offsetEnd = 1;
+ } else {
+ offset = offsetEnd - 1;
+ }
+ }
+
+ // Make the selection cover entire parts.
+ offset -= offset % 2;
+ offsetEnd += offsetEnd % 2;
+
+ // Remap the increments from [0.1, 1, 10] to [1, 1, 16].
+ if (increment > -1 && increment < 1) {
+ increment = increment < 0 ? -1 : 1;
+ }
+ if (Math.abs(increment) === 10) {
+ increment = increment < 0 ? -16 : 16;
+ }
+
+ const isUpper = rawValue.toUpperCase() === rawValue;
+
+ for (let pos = offset; pos < offsetEnd; pos += 2) {
+ // Increment the part in [pos, pos+2).
+ let mid = rawValue.substr(pos, 2);
+ const value = parseInt(mid, 16);
+
+ if (isNaN(value)) {
+ return null;
+ }
+
+ mid = Math.min(Math.max(value + increment, 0), 255).toString(16);
+
+ while (mid.length < 2) {
+ mid = "0" + mid;
+ }
+ if (isUpper) {
+ mid = mid.toUpperCase();
+ }
+
+ rawValue = rawValue.substr(0, pos) + mid + rawValue.substr(pos + 2);
+ }
+
+ return {
+ value: "#" + rawValue,
+ selection: [offset + 1, offsetEnd + 1],
+ };
+ }
+
+ /**
+ * Cycle through the autocompletion suggestions in the popup.
+ *
+ * @param {Boolean} reverse
+ * true to select previous item from the popup.
+ * @param {Boolean} noSelect
+ * true to not select the text after selecting the newly selectedItem
+ * from the popup.
+ */
+ #cycleCSSSuggestion(reverse, noSelect) {
+ // selectedItem can be null when nothing is selected in an empty editor.
+ const { label, preLabel } = this.popup.selectedItem || {
+ label: "",
+ preLabel: "",
+ };
+ if (reverse) {
+ this.popup.selectPreviousItem();
+ } else {
+ this.popup.selectNextItem();
+ }
+
+ this.#selectedIndex = this.popup.selectedIndex;
+ const input = this.input;
+ let pre = "";
+
+ if (input.selectionStart < input.selectionEnd) {
+ pre = input.value.slice(0, input.selectionStart);
+ } else {
+ pre = input.value.slice(
+ 0,
+ input.selectionStart - label.length + preLabel.length
+ );
+ }
+
+ const post = input.value.slice(input.selectionEnd, input.value.length);
+ const item = this.popup.selectedItem;
+ const toComplete = item.label.slice(item.preLabel.length);
+ input.value = pre + toComplete + post;
+
+ if (!noSelect) {
+ input.setSelectionRange(pre.length, pre.length + toComplete.length);
+ } else {
+ input.setSelectionRange(
+ pre.length + toComplete.length,
+ pre.length + toComplete.length
+ );
+ }
+
+ this.#updateSize();
+ // This emit is mainly for the purpose of making the test flow simpler.
+ this.emit("after-suggest");
+ }
+
+ /**
+ * Call the client's done handler and clear out.
+ */
+ #apply(direction, key) {
+ if (this.#applied) {
+ return null;
+ }
+
+ this.#applied = true;
+
+ if (this.done) {
+ const val = this.cancelled ? this.initial : this.currentInputValue;
+ return this.done(val, !this.cancelled, direction, key);
+ }
+
+ return null;
+ }
+
+ /**
+ * Hide the popup and cancel any pending popup opening.
+ */
+ #onWindowBlur = () => {
+ if (this.popup && this.popup.isOpen) {
+ this.popup.hidePopup();
+ }
+
+ if (this.#openPopupTimeout) {
+ this.doc.defaultView.clearTimeout(this.#openPopupTimeout);
+ }
+ };
+
+ /**
+ * Event handler called when the inplace-editor's input loses focus.
+ */
+ #onBlur = event => {
+ if (
+ event &&
+ this.popup &&
+ this.popup.isOpen &&
+ this.popup.selectedIndex >= 0
+ ) {
+ this.#acceptPopupSuggestion();
+ } else {
+ this.#apply();
+ this.#clear();
+ }
+ };
+
+ /**
+ * Before offering autocomplete, set this.gridLineNames as the line names
+ * of the current grid, if they exist.
+ *
+ * @param {Function} getGridLineNames
+ * A function which gets the line names of the current grid.
+ */
+ async #getGridNamesBeforeCompletion(getGridLineNames) {
+ if (
+ getGridLineNames &&
+ this.property &&
+ GRID_PROPERTY_NAMES.includes(this.property.name)
+ ) {
+ this.gridLineNames = await getGridLineNames();
+ }
+
+ if (
+ this.contentType == CONTENT_TYPES.CSS_VALUE &&
+ this.input &&
+ this.input.value == ""
+ ) {
+ this.#maybeSuggestCompletion(false);
+ }
+ }
+
+ /**
+ * Event handler called by the autocomplete popup when receiving a click
+ * event.
+ */
+ #onAutocompletePopupClick = () => {
+ this.#acceptPopupSuggestion();
+ };
+
+ #acceptPopupSuggestion() {
+ let label, preLabel;
+
+ if (this.#selectedIndex === undefined) {
+ ({ label, preLabel } = this.popup.getItemAtIndex(
+ this.popup.selectedIndex
+ ));
+ } else {
+ ({ label, preLabel } = this.popup.getItemAtIndex(this.#selectedIndex));
+ }
+
+ const input = this.input;
+
+ let pre = "";
+
+ // CSS_MIXED needs special treatment here to make it so that
+ // multiple presses of tab will cycle through completions, but
+ // without selecting the completed text. However, this same
+ // special treatment will do the wrong thing for other editing
+ // styles.
+ if (
+ input.selectionStart < input.selectionEnd ||
+ this.contentType !== CONTENT_TYPES.CSS_MIXED
+ ) {
+ pre = input.value.slice(0, input.selectionStart);
+ } else {
+ pre = input.value.slice(
+ 0,
+ input.selectionStart - label.length + preLabel.length
+ );
+ }
+ const post = input.value.slice(input.selectionEnd, input.value.length);
+ const item = this.popup.selectedItem;
+ this.#selectedIndex = this.popup.selectedIndex;
+ const toComplete = item.label.slice(item.preLabel.length);
+ input.value = pre + toComplete + post;
+ input.setSelectionRange(
+ pre.length + toComplete.length,
+ pre.length + toComplete.length
+ );
+ this.#updateSize();
+ // Wait for the popup to hide and then focus input async otherwise it does
+ // not work.
+ const onPopupHidden = () => {
+ this.popup.off("popup-closed", onPopupHidden);
+ this.doc.defaultView.setTimeout(() => {
+ input.focus();
+ this.emit("after-suggest");
+ }, 0);
+ };
+ this.popup.on("popup-closed", onPopupHidden);
+ this.#hideAutocompletePopup();
+ }
+
+ /**
+ * Handle the input field's keypress event.
+ */
+ // eslint-disable-next-line complexity
+ #onKeyPress = event => {
+ let prevent = false;
+
+ const key = event.keyCode;
+ const input = this.input;
+
+ // We want to autoclose some characters, remember the pressed key in order to process
+ // it later on in maybeSuggestionCompletion().
+ this.#pressedKey = event.key;
+
+ const multilineNavigation =
+ !this.#isSingleLine() && isKeyIn(key, "UP", "DOWN", "LEFT", "RIGHT");
+ const isPlainText = this.contentType == CONTENT_TYPES.PLAIN_TEXT;
+ const isPopupOpen = this.popup && this.popup.isOpen;
+
+ let increment = 0;
+ if (!isPlainText && !multilineNavigation) {
+ increment = this.#getIncrement(event);
+ }
+
+ if (isKeyIn(key, "PAGE_UP", "PAGE_DOWN")) {
+ this.#preventSuggestions = true;
+ }
+
+ let cycling = false;
+ if (increment && this.#incrementValue(increment)) {
+ this.#updateSize();
+ prevent = true;
+ cycling = true;
+ }
+
+ if (isPopupOpen && isKeyIn(key, "UP", "DOWN", "PAGE_UP", "PAGE_DOWN")) {
+ prevent = true;
+ cycling = true;
+ this.#cycleCSSSuggestion(isKeyIn(key, "UP", "PAGE_UP"));
+ this.#doValidation();
+ }
+
+ if (isKeyIn(key, "BACK_SPACE", "DELETE", "LEFT", "RIGHT", "HOME", "END")) {
+ if (isPopupOpen && this.currentInputValue !== "") {
+ this.#hideAutocompletePopup();
+ }
+ } else if (
+ // We may show the suggestion completion if Ctrl+space is pressed, or if an
+ // otherwise unhandled key is pressed and the user is not cycling through the
+ // options in the pop-up menu, it is not an expanded shorthand property, and no
+ // modifier key is pressed.
+ (event.key === " " && event.ctrlKey) ||
+ (!cycling &&
+ !multilineNavigation &&
+ !event.metaKey &&
+ !event.altKey &&
+ !event.ctrlKey)
+ ) {
+ this.#maybeSuggestCompletion(true);
+ }
+
+ if (this.multiline && event.shiftKey && isKeyIn(key, "RETURN")) {
+ prevent = false;
+ } else if (
+ this.#advanceChars(event.charCode, input.value, input.selectionStart) ||
+ isKeyIn(key, "RETURN", "TAB")
+ ) {
+ prevent = true;
+
+ const ctrlOrCmd = isOSX ? event.metaKey : event.ctrlKey;
+
+ let direction;
+ if (
+ (this.stopOnReturn && isKeyIn(key, "RETURN") && !ctrlOrCmd) ||
+ (this.stopOnTab && !event.shiftKey && isKeyIn(key, "TAB")) ||
+ (this.stopOnShiftTab && event.shiftKey && isKeyIn(key, "TAB"))
+ ) {
+ direction = null;
+ } else if (event.shiftKey && isKeyIn(key, "TAB")) {
+ direction = FOCUS_BACKWARD;
+ } else {
+ direction = FOCUS_FORWARD;
+ }
+
+ // Now we don't want to suggest anything as we are moving out.
+ this.#preventSuggestions = true;
+ // But we still want to show suggestions for css values. i.e. moving out
+ // of css property input box in forward direction
+ if (
+ this.contentType == CONTENT_TYPES.CSS_PROPERTY &&
+ direction == FOCUS_FORWARD
+ ) {
+ this.#preventSuggestions = false;
+ }
+
+ if (isKeyIn(key, "TAB") && this.contentType == CONTENT_TYPES.CSS_MIXED) {
+ if (this.popup && input.selectionStart < input.selectionEnd) {
+ event.preventDefault();
+ input.setSelectionRange(input.selectionEnd, input.selectionEnd);
+ this.emit("after-suggest");
+ return;
+ } else if (this.popup && this.popup.isOpen) {
+ event.preventDefault();
+ this.#cycleCSSSuggestion(event.shiftKey, true);
+ return;
+ }
+ }
+
+ this.#apply(direction, key);
+
+ // Close the popup if open
+ if (this.popup && this.popup.isOpen) {
+ this.#hideAutocompletePopup();
+ }
+
+ if (direction !== null && focusManager.focusedElement === input) {
+ // If the focused element wasn't changed by the done callback,
+ // move the focus as requested.
+ const next = moveFocus(
+ this.doc.defaultView,
+ direction,
+ this.focusEditableFieldAfterApply,
+ this.focusEditableFieldContainerSelector
+ );
+
+ // If the next node to be focused has been tagged as an editable
+ // node, trigger editing using the configured event
+ if (next && next.ownerDocument === this.doc && next._editable) {
+ const e = this.doc.createEvent("Event");
+ e.initEvent(next._trigger, true, true);
+ next.dispatchEvent(e);
+ }
+ }
+
+ this.#clear();
+ } else if (isKeyIn(key, "ESCAPE")) {
+ // Cancel and blur ourselves.
+ // Now we don't want to suggest anything as we are moving out.
+ this.#preventSuggestions = true;
+ // Close the popup if open
+ if (this.popup && this.popup.isOpen) {
+ this.#hideAutocompletePopup();
+ }
+ prevent = true;
+ this.cancelled = true;
+ this.#apply();
+ this.#clear();
+ event.stopPropagation();
+ } else if (isKeyIn(key, "SPACE")) {
+ // No need for leading spaces here. This is particularly
+ // noticable when adding a property: it's very natural to type
+ // <name>: (which advances to the next property) then spacebar.
+ prevent = !input.value;
+ }
+
+ if (prevent) {
+ event.preventDefault();
+ }
+ };
+
+ #onContextMenu = event => {
+ if (this.contextMenu) {
+ // Call stopPropagation() and preventDefault() here so that avoid to show default
+ // context menu in about:devtools-toolbox. See Bug 1515265.
+ event.stopPropagation();
+ event.preventDefault();
+ this.contextMenu(event);
+ }
+ };
+
+ /**
+ * Open the autocomplete popup, adding a custom click handler and classname.
+ *
+ * @param {Number} offset
+ * X-offset relative to the input starting edge.
+ * @param {Number} selectedIndex
+ * The index of the item that should be selected. Use -1 to have no
+ * item selected.
+ */
+ #openAutocompletePopup(offset, selectedIndex) {
+ this.popup.on("popup-click", this.#onAutocompletePopupClick);
+ this.popup.openPopup(this.input, offset, 0, selectedIndex);
+ }
+
+ /**
+ * Remove the custom classname and click handler and close the autocomplete
+ * popup.
+ */
+ #hideAutocompletePopup() {
+ this.popup.off("popup-click", this.#onAutocompletePopupClick);
+ this.popup.hidePopup();
+ }
+
+ /**
+ * Get the increment/decrement step to use for the provided key event.
+ */
+ #getIncrement(event) {
+ const getSmallIncrementKey = evt => {
+ if (isOSX) {
+ return evt.altKey;
+ }
+ return evt.ctrlKey;
+ };
+
+ const largeIncrement = 100;
+ const mediumIncrement = 10;
+ const smallIncrement = 0.1;
+
+ let increment = 0;
+ const key = event.keyCode;
+
+ if (isKeyIn(key, "UP", "PAGE_UP")) {
+ increment = 1 * this.defaultIncrement;
+ } else if (isKeyIn(key, "DOWN", "PAGE_DOWN")) {
+ increment = -1 * this.defaultIncrement;
+ }
+
+ if (event.shiftKey && !getSmallIncrementKey(event)) {
+ if (isKeyIn(key, "PAGE_UP", "PAGE_DOWN")) {
+ increment *= largeIncrement;
+ } else {
+ increment *= mediumIncrement;
+ }
+ } else if (getSmallIncrementKey(event) && !event.shiftKey) {
+ increment *= smallIncrement;
+ }
+
+ return increment;
+ }
+
+ /**
+ * Handle the input field's keyup event.
+ */
+ #onKeyup = () => {
+ this.#applied = false;
+ };
+
+ /**
+ * Handle changes to the input text.
+ */
+ #onInput = () => {
+ // Validate the entered value.
+ this.#doValidation();
+
+ // Update size if we're autosizing.
+ if (this.#measurement) {
+ this.#updateSize();
+ }
+
+ // Call the user's change handler if available.
+ if (this.change) {
+ this.change(this.currentInputValue);
+ }
+
+ // In case that the current value becomes empty, show the suggestions if needed.
+ if (this.currentInputValue === "" && this.showSuggestCompletionOnEmpty) {
+ this.#maybeSuggestCompletion(false);
+ }
+ };
+
+ /**
+ * Stop propagation on the provided event
+ */
+ #stopEventPropagation(e) {
+ e.stopPropagation();
+ }
+
+ /**
+ * Fire validation callback with current input
+ */
+ #doValidation() {
+ if (this.validate && this.input) {
+ this.validate(this.input.value);
+ }
+ }
+
+ /**
+ * Handles displaying suggestions based on the current input.
+ *
+ * @param {Boolean} autoInsert
+ * Pass true to automatically insert the most relevant suggestion.
+ */
+ #maybeSuggestCompletion(autoInsert) {
+ // Input can be null in cases when you intantaneously switch out of it.
+ if (!this.input) {
+ return;
+ }
+
+ const preTimeoutQuery = this.input.value;
+
+ // Since we are calling this method from a keypress event handler, the
+ // |input.value| does not include currently typed character. Thus we perform
+ // this method async.
+ // eslint-disable-next-line complexity
+ this.#openPopupTimeout = this.doc.defaultView.setTimeout(() => {
+ if (this.#preventSuggestions) {
+ this.#preventSuggestions = false;
+ return;
+ }
+ if (this.contentType == CONTENT_TYPES.PLAIN_TEXT) {
+ return;
+ }
+ if (!this.input) {
+ return;
+ }
+ const input = this.input;
+ // The length of input.value should be increased by 1
+ if (input.value.length - preTimeoutQuery.length > 1) {
+ return;
+ }
+ const query = input.value.slice(0, input.selectionStart);
+ let startCheckQuery = query;
+ if (query == null) {
+ return;
+ }
+ // If nothing is selected and there is a word (\w) character after the cursor, do
+ // not autocomplete.
+ if (
+ input.selectionStart == input.selectionEnd &&
+ input.selectionStart < input.value.length
+ ) {
+ const nextChar = input.value.slice(input.selectionStart)[0];
+ // Check if the next character is a valid word character, no suggestion should be
+ // provided when preceeding a word.
+ if (/[\w-]/.test(nextChar)) {
+ // This emit is mainly to make the test flow simpler.
+ this.emit("after-suggest", "nothing to autocomplete");
+ return;
+ }
+ }
+
+ let list = [];
+ let postLabelValues = [];
+
+ if (this.contentType == CONTENT_TYPES.CSS_PROPERTY) {
+ list = this.#getCSSVariableNames().concat(this.#getCSSPropertyList());
+ } else if (this.contentType == CONTENT_TYPES.CSS_VALUE) {
+ // Get the last query to be completed before the caret.
+ const match = /([^\s,.\/]+$)/.exec(query);
+ if (match) {
+ startCheckQuery = match[0];
+ } else {
+ startCheckQuery = "";
+ }
+
+ // Check if the query to be completed is a CSS variable.
+ const varMatch = /^var\(([^\s]+$)/.exec(startCheckQuery);
+
+ if (varMatch && varMatch.length == 2) {
+ startCheckQuery = varMatch[1];
+ list = this.#getCSSVariableNames();
+ postLabelValues = list.map(varName =>
+ this.#getCSSVariableValue(varName)
+ );
+ } else {
+ list = [
+ "!important",
+ ...this.#getCSSValuesForPropertyName(this.property.name),
+ ];
+ }
+
+ if (query == "") {
+ // Do not suggest '!important' without any manually typed character.
+ list.splice(0, 1);
+ }
+ } else if (
+ this.contentType == CONTENT_TYPES.CSS_MIXED &&
+ /^\s*style\s*=/.test(query)
+ ) {
+ // Check if the style attribute is closed before the selection.
+ const styleValue = query.replace(/^\s*style\s*=\s*/, "");
+ // Look for a quote matching the opening quote (single or double).
+ if (/^("[^"]*"|'[^']*')/.test(styleValue)) {
+ // This emit is mainly to make the test flow simpler.
+ this.emit("after-suggest", "nothing to autocomplete");
+ return;
+ }
+
+ // Detecting if cursor is at property or value;
+ const match = query.match(/([:;"'=]?)\s*([^"';:=]+)?$/);
+ if (match && match.length >= 2) {
+ if (match[1] == ":") {
+ // We are in CSS value completion
+ const propertyName = query.match(
+ /[;"'=]\s*([^"';:= ]+)\s*:\s*[^"';:=]*$/
+ )[1];
+ list = [
+ "!important;",
+ ...this.#getCSSValuesForPropertyName(propertyName),
+ ];
+ const matchLastQuery = /([^\s,.\/]+$)/.exec(match[2] || "");
+ if (matchLastQuery) {
+ startCheckQuery = matchLastQuery[0];
+ } else {
+ startCheckQuery = "";
+ }
+ if (!match[2]) {
+ // Don't suggest '!important' without any manually typed character
+ list.splice(0, 1);
+ }
+ } else if (match[1]) {
+ // We are in CSS property name completion
+ list = this.#getCSSVariableNames().concat(
+ this.#getCSSPropertyList()
+ );
+ startCheckQuery = match[2];
+ }
+ if (startCheckQuery == null) {
+ // This emit is mainly to make the test flow simpler.
+ this.emit("after-suggest", "nothing to autocomplete");
+ return;
+ }
+ }
+ }
+
+ if (!this.popup) {
+ // This emit is mainly to make the test flow simpler.
+ this.emit("after-suggest", "no popup");
+ return;
+ }
+
+ const finalList = [];
+ const length = list.length;
+ for (let i = 0, count = 0; i < length && count < MAX_POPUP_ENTRIES; i++) {
+ if (startCheckQuery != null && list[i].startsWith(startCheckQuery)) {
+ count++;
+ finalList.push({
+ preLabel: startCheckQuery,
+ label: list[i],
+ postLabel: postLabelValues[i] ? postLabelValues[i] : "",
+ });
+ } else if (count > 0) {
+ // Since count was incremented, we had already crossed the entries
+ // which would have started with query, assuming that list is sorted.
+ break;
+ } else if (startCheckQuery != null && list[i][0] > startCheckQuery[0]) {
+ // We have crossed all possible matches alphabetically.
+ break;
+ }
+ }
+
+ // Sort items starting with [a-z0-9] first, to make sure vendor-prefixed
+ // values and "!important" are suggested only after standard values.
+ finalList.sort((item1, item2) => {
+ // Get the expected alphabetical comparison between the items.
+ let comparison = item1.label.localeCompare(item2.label);
+ if (/^\w/.test(item1.label) != /^\w/.test(item2.label)) {
+ // One starts with [a-z0-9], one does not: flip the comparison.
+ comparison = -1 * comparison;
+ }
+ return comparison;
+ });
+
+ let index = 0;
+ if (startCheckQuery) {
+ // Only select a "best" suggestion when the user started a query.
+ const cssValues = finalList.map(item => item.label);
+ index = findMostRelevantCssPropertyIndex(cssValues);
+ }
+
+ // Insert the most relevant item from the final list as the input value.
+ if (autoInsert && finalList[index]) {
+ const item = finalList[index].label;
+ input.value =
+ query +
+ item.slice(startCheckQuery.length) +
+ input.value.slice(query.length);
+ input.setSelectionRange(
+ query.length,
+ query.length + item.length - startCheckQuery.length
+ );
+ this.#updateSize();
+ }
+
+ // Display the list of suggestions if there are more than one.
+ if (finalList.length > 1) {
+ // Calculate the popup horizontal offset.
+ const indent = this.input.selectionStart - startCheckQuery.length;
+ let offset = indent * this.inputCharDimensions.width;
+ offset = this.#isSingleLine() ? offset : 0;
+
+ // Select the most relevantItem if autoInsert is allowed
+ const selectedIndex = autoInsert ? index : -1;
+
+ // Open the suggestions popup.
+ this.popup.setItems(finalList, selectedIndex);
+ this.#openAutocompletePopup(offset, selectedIndex);
+ } else {
+ this.#hideAutocompletePopup();
+ }
+
+ this.#autocloseParenthesis();
+
+ // This emit is mainly for the purpose of making the test flow simpler.
+ this.emit("after-suggest");
+ this.#doValidation();
+ }, 0);
+ }
+
+ /**
+ * Automatically add closing parenthesis and skip closing parenthesis when needed.
+ */
+ #autocloseParenthesis() {
+ // Split the current value at the cursor index to rebuild the string.
+ const parts = this.#splitStringAt(
+ this.input.value,
+ this.input.selectionStart
+ );
+
+ // Lookup the character following the caret to know if the string should be modified.
+ const nextChar = parts[1][0];
+
+ // Autocomplete closing parenthesis if the last key pressed was "(" and the next
+ // character is not a "word" character.
+ if (this.#pressedKey == "(" && !isWordChar(nextChar)) {
+ this.#updateValue(parts[0] + ")" + parts[1]);
+ }
+
+ // Skip inserting ")" if the next character is already a ")" (note that we actually
+ // insert and remove the extra ")" here, as the input has already been modified).
+ if (this.#pressedKey == ")" && nextChar == ")") {
+ this.#updateValue(parts[0] + parts[1].substring(1));
+ }
+
+ this.#pressedKey = null;
+ }
+
+ /**
+ * Update the current value of the input while preserving the caret position.
+ */
+ #updateValue(str) {
+ const start = this.input.selectionStart;
+ this.input.value = str;
+ this.input.setSelectionRange(start, start);
+ this.#updateSize();
+ }
+
+ /**
+ * Split the provided string at the provided index. Returns an array of two strings.
+ * _splitStringAt("1234567", 3) will return ["123", "4567"]
+ */
+ #splitStringAt(str, index) {
+ return [str.substring(0, index), str.substring(index, str.length)];
+ }
+
+ /**
+ * Check if the current input is displaying more than one line of text.
+ *
+ * @return {Boolean} true if the input has a single line of text
+ */
+ #isSingleLine() {
+ if (!this.multiline) {
+ // Checking the inputCharDimensions.height only makes sense with multiline
+ // editors, because the textarea is directly sized using
+ // inputCharDimensions (see _updateSize()).
+ // By definition if !this.multiline, then we are in single line mode.
+ return true;
+ }
+ const inputRect = this.input.getBoundingClientRect();
+ return inputRect.height < 2 * this.inputCharDimensions.height;
+ }
+
+ /**
+ * Returns the list of CSS properties to use for the autocompletion. This
+ * method is overridden by tests in order to use mocked suggestion lists.
+ *
+ * @return {Array} array of CSS property names (Strings)
+ */
+ #getCSSPropertyList() {
+ return this.cssProperties.getNames().sort();
+ }
+
+ /**
+ * Returns a list of CSS values valid for a provided property name to use for
+ * the autocompletion. This method is overridden by tests in order to use
+ * mocked suggestion lists.
+ *
+ * @param {String} propertyName
+ * @return {Array} array of CSS property values (Strings)
+ */
+ #getCSSValuesForPropertyName(propertyName) {
+ const gridLineList = [];
+ if (this.gridLineNames) {
+ if (GRID_ROW_PROPERTY_NAMES.includes(this.property.name)) {
+ gridLineList.push(...this.gridLineNames.rows);
+ }
+ if (GRID_COL_PROPERTY_NAMES.includes(this.property.name)) {
+ gridLineList.push(...this.gridLineNames.cols);
+ }
+ }
+ // Must be alphabetically sorted before comparing the results with
+ // the user input, otherwise we will lose some results.
+ return gridLineList
+ .concat(this.cssProperties.getValues(propertyName))
+ .sort();
+ }
+
+ /**
+ * Returns the list of all CSS variables to use for the autocompletion.
+ *
+ * @return {Array} array of CSS variable names (Strings)
+ */
+ #getCSSVariableNames() {
+ return Array.from(this.cssVariables.keys()).sort();
+ }
+
+ /**
+ * Returns the variable's value for the given CSS variable name.
+ *
+ * @param {String} varName
+ * The variable name to retrieve the value of
+ * @return {String} the variable value to the given CSS variable name
+ */
+ #getCSSVariableValue(varName) {
+ return this.cssVariables.get(varName);
+ }
+}
+
+exports.InplaceEditor = InplaceEditor;
+
+/**
+ * Copy text-related styles from one element to another.
+ */
+function copyTextStyles(from, to) {
+ const win = from.ownerDocument.defaultView;
+ const style = win.getComputedStyle(from);
+
+ to.style.fontFamily = style.fontFamily;
+ to.style.fontSize = style.fontSize;
+ to.style.fontWeight = style.fontWeight;
+ to.style.fontStyle = style.fontStyle;
+}
+
+/**
+ * Copy all styles which could have an impact on the element size.
+ */
+function copyAllStyles(from, to) {
+ const win = from.ownerDocument.defaultView;
+ const style = win.getComputedStyle(from);
+
+ copyTextStyles(from, to);
+ to.style.lineHeight = style.lineHeight;
+
+ // If box-sizing is set to border-box, box model styles also need to be
+ // copied.
+ const boxSizing = style.boxSizing;
+ if (boxSizing === "border-box") {
+ to.style.boxSizing = boxSizing;
+ copyBoxModelStyles(from, to);
+ }
+}
+
+/**
+ * Copy box model styles that can impact width and height measurements when box-
+ * sizing is set to "border-box" instead of "content-box".
+ *
+ * @param {DOMNode} from
+ * the element from which styles are copied
+ * @param {DOMNode} to
+ * the element on which copied styles are applied
+ */
+function copyBoxModelStyles(from, to) {
+ const properties = [
+ // Copy all paddings.
+ "paddingTop",
+ "paddingRight",
+ "paddingBottom",
+ "paddingLeft",
+ // Copy border styles.
+ "borderTopStyle",
+ "borderRightStyle",
+ "borderBottomStyle",
+ "borderLeftStyle",
+ // Copy border widths.
+ "borderTopWidth",
+ "borderRightWidth",
+ "borderBottomWidth",
+ "borderLeftWidth",
+ ];
+
+ const win = from.ownerDocument.defaultView;
+ const style = win.getComputedStyle(from);
+ for (const property of properties) {
+ to.style[property] = style[property];
+ }
+}
+
+/**
+ * Trigger a focus change similar to pressing tab/shift-tab.
+ *
+ * @param {Window} win: The window into which the focus should be moved
+ * @param {Number} direction: See Services.focus.MOVEFOCUS_*
+ * @param {Boolean} focusEditableField: Set to true to move the focus to the previous/next
+ * editable field. If not set, the focus will be set on the next focusable element.
+ * The function might still put the focus on a non-editable field, if none is found
+ * within the element matching focusEditableFieldContainerSelector
+ * @param {String} focusEditableFieldContainerSelector: A CSS selector the editabled element
+ * we want to focus should be in. This is only used when focusEditableField is set
+ * to true.
+ * It's important to pass a boundary otherwise we might hit an infinite loop
+ * @returns {Element} The element that received the focus
+ */
+function moveFocus(
+ win,
+ direction,
+ focusEditableField,
+ focusEditableFieldContainerSelector
+) {
+ if (!focusEditableField) {
+ return focusManager.moveFocus(win, null, direction, 0);
+ }
+
+ if (!win.document.querySelector(focusEditableFieldContainerSelector)) {
+ console.error(
+ focusEditableFieldContainerSelector,
+ "can't be found in document.",
+ `focusEditableFieldContainerSelector should match an existing element`
+ );
+ return focusManager.moveFocus(win, null, direction, 0);
+ }
+
+ // Let's look for the next/previous editable element to focus
+ while (true) {
+ const focusedElement = focusManager.moveFocus(win, null, direction, 0);
+ // The _editable property is set by the InplaceEditor on the target element
+ if (focusedElement._editable) {
+ return focusedElement;
+ }
+
+ // If the focus was moved outside of the container, simply return the focused element
+ if (!focusedElement.closest(focusEditableFieldContainerSelector)) {
+ return focusedElement;
+ }
+ }
+}
diff --git a/devtools/client/shared/key-shortcuts.js b/devtools/client/shared/key-shortcuts.js
new file mode 100644
index 0000000000..35905fdc35
--- /dev/null
+++ b/devtools/client/shared/key-shortcuts.js
@@ -0,0 +1,308 @@
+/* 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/. */
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const isOSX = Services.appinfo.OS === "Darwin";
+const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
+
+// List of electron keys mapped to DOM API (DOM_VK_*) key code
+const ElectronKeysMapping = {
+ F1: "DOM_VK_F1",
+ F2: "DOM_VK_F2",
+ F3: "DOM_VK_F3",
+ F4: "DOM_VK_F4",
+ F5: "DOM_VK_F5",
+ F6: "DOM_VK_F6",
+ F7: "DOM_VK_F7",
+ F8: "DOM_VK_F8",
+ F9: "DOM_VK_F9",
+ F10: "DOM_VK_F10",
+ F11: "DOM_VK_F11",
+ F12: "DOM_VK_F12",
+ F13: "DOM_VK_F13",
+ F14: "DOM_VK_F14",
+ F15: "DOM_VK_F15",
+ F16: "DOM_VK_F16",
+ F17: "DOM_VK_F17",
+ F18: "DOM_VK_F18",
+ F19: "DOM_VK_F19",
+ F20: "DOM_VK_F20",
+ F21: "DOM_VK_F21",
+ F22: "DOM_VK_F22",
+ F23: "DOM_VK_F23",
+ F24: "DOM_VK_F24",
+ Space: "DOM_VK_SPACE",
+ Backspace: "DOM_VK_BACK_SPACE",
+ Delete: "DOM_VK_DELETE",
+ Insert: "DOM_VK_INSERT",
+ Return: "DOM_VK_RETURN",
+ Enter: "DOM_VK_RETURN",
+ Up: "DOM_VK_UP",
+ Down: "DOM_VK_DOWN",
+ Left: "DOM_VK_LEFT",
+ Right: "DOM_VK_RIGHT",
+ Home: "DOM_VK_HOME",
+ End: "DOM_VK_END",
+ PageUp: "DOM_VK_PAGE_UP",
+ PageDown: "DOM_VK_PAGE_DOWN",
+ Escape: "DOM_VK_ESCAPE",
+ Esc: "DOM_VK_ESCAPE",
+ Tab: "DOM_VK_TAB",
+ VolumeUp: "DOM_VK_VOLUME_UP",
+ VolumeDown: "DOM_VK_VOLUME_DOWN",
+ VolumeMute: "DOM_VK_VOLUME_MUTE",
+ PrintScreen: "DOM_VK_PRINTSCREEN",
+};
+
+/**
+ * Helper to listen for keyboard events described in .properties file.
+ *
+ * let shortcuts = new KeyShortcuts({
+ * window
+ * });
+ * shortcuts.on("Ctrl+F", event => {
+ * // `event` is the KeyboardEvent which relates to the key shortcuts
+ * });
+ *
+ * @param DOMWindow window
+ * The window object of the document to listen events from.
+ * @param DOMElement target
+ * Optional DOM Element on which we should listen events from.
+ * If omitted, we listen for all events fired on `window`.
+ */
+function KeyShortcuts({ window, target }) {
+ this.window = window;
+ this.target = target || window;
+ this.keys = new Map();
+ this.eventEmitter = new EventEmitter();
+ this.target.addEventListener("keydown", this);
+}
+
+/*
+ * Parse an electron-like key string and return a normalized object which
+ * allow efficient match on DOM key event. The normalized object matches DOM
+ * API.
+ *
+ * @param DOMWindow window
+ * Any DOM Window object, just to fetch its `KeyboardEvent` object
+ * @param String str
+ * The shortcut string to parse, following this document:
+ * https://github.com/electron/electron/blob/master/docs/api/accelerator.md
+ */
+KeyShortcuts.parseElectronKey = function (window, str) {
+ // If a localized string is found but has no value in the properties file,
+ // getStr will return `null`. See Bug 1569572.
+ if (typeof str !== "string") {
+ console.error("Invalid key passed to parseElectronKey, stacktrace below");
+ console.trace();
+
+ return null;
+ }
+
+ const modifiers = str.split("+");
+ let key = modifiers.pop();
+
+ const shortcut = {
+ ctrl: false,
+ meta: false,
+ alt: false,
+ shift: false,
+ // Set for character keys
+ key: undefined,
+ // Set for non-character keys
+ keyCode: undefined,
+ };
+ for (const mod of modifiers) {
+ if (mod === "Alt") {
+ shortcut.alt = true;
+ } else if (["Command", "Cmd"].includes(mod)) {
+ shortcut.meta = true;
+ } else if (["CommandOrControl", "CmdOrCtrl"].includes(mod)) {
+ if (isOSX) {
+ shortcut.meta = true;
+ } else {
+ shortcut.ctrl = true;
+ }
+ } else if (["Control", "Ctrl"].includes(mod)) {
+ shortcut.ctrl = true;
+ } else if (mod === "Shift") {
+ shortcut.shift = true;
+ } else {
+ console.error("Unsupported modifier:", mod, "from key:", str);
+ return null;
+ }
+ }
+
+ // Plus is a special case. It's a character key and shouldn't be matched
+ // against a keycode as it is only accessible via Shift/Capslock
+ if (key === "Plus") {
+ key = "+";
+ }
+
+ if (typeof key === "string" && key.length === 1) {
+ if (shortcut.alt) {
+ // When Alt is involved, some platforms (macOS) give different printable characters
+ // for the `key` value, like `®` for the key `R`. In this case, prefer matching by
+ // `keyCode` instead.
+ shortcut.keyCode = KeyCodes[`DOM_VK_${key.toUpperCase()}`];
+ shortcut.keyCodeString = key;
+ } else {
+ // Match any single character
+ shortcut.key = key.toLowerCase();
+ }
+ } else if (key in ElectronKeysMapping) {
+ // Maps the others manually to DOM API DOM_VK_*
+ key = ElectronKeysMapping[key];
+ shortcut.keyCode = KeyCodes[key];
+ // Used only to stringify the shortcut
+ shortcut.keyCodeString = key;
+ shortcut.key = key;
+ } else {
+ console.error("Unsupported key:", key);
+ return null;
+ }
+
+ return shortcut;
+};
+
+KeyShortcuts.stringify = function (shortcut) {
+ if (shortcut === null) {
+ // parseElectronKey might return null in several situations.
+ return "";
+ }
+
+ const list = [];
+ if (shortcut.alt) {
+ list.push("Alt");
+ }
+ if (shortcut.ctrl) {
+ list.push("Ctrl");
+ }
+ if (shortcut.meta) {
+ list.push("Cmd");
+ }
+ if (shortcut.shift) {
+ list.push("Shift");
+ }
+ let key;
+ if (shortcut.key) {
+ key = shortcut.key.toUpperCase();
+ } else {
+ key = shortcut.keyCodeString;
+ }
+ list.push(key);
+ return list.join("+");
+};
+
+/*
+ * Parse an xul-like key string and return an electron-like string.
+ */
+KeyShortcuts.parseXulKey = function (modifiers, shortcut) {
+ modifiers = modifiers
+ .split(",")
+ .map(mod => {
+ if (mod == "alt") {
+ return "Alt";
+ } else if (mod == "shift") {
+ return "Shift";
+ } else if (mod == "accel") {
+ return "CmdOrCtrl";
+ }
+ return mod;
+ })
+ .join("+");
+
+ if (shortcut.startsWith("VK_")) {
+ shortcut = shortcut.substr(3);
+ }
+
+ return modifiers + "+" + shortcut;
+};
+
+KeyShortcuts.prototype = {
+ destroy() {
+ this.target.removeEventListener("keydown", this);
+ this.keys.clear();
+ },
+
+ doesEventMatchShortcut(event, shortcut) {
+ if (shortcut.meta != event.metaKey) {
+ return false;
+ }
+ if (shortcut.ctrl != event.ctrlKey) {
+ return false;
+ }
+ if (shortcut.alt != event.altKey) {
+ return false;
+ }
+ if (shortcut.shift != event.shiftKey) {
+ // Check the `keyCode` to see whether it's a character (see also Bug 1493646)
+ const char = String.fromCharCode(event.keyCode);
+ let isAlphabetical = char.length == 1 && char.match(/[a-zA-Z]/);
+
+ // Shift is a special modifier, it may implicitly be required if the expected key
+ // is a special character accessible via shift.
+ if (!isAlphabetical) {
+ isAlphabetical = event.key && event.key.match(/[a-zA-Z]/);
+ }
+
+ // OSX: distinguish cmd+[key] from cmd+shift+[key] shortcuts (Bug 1300458)
+ const cmdShortcut = shortcut.meta && !shortcut.alt && !shortcut.ctrl;
+ if (isAlphabetical || cmdShortcut) {
+ return false;
+ }
+ }
+
+ if (shortcut.keyCode) {
+ return event.keyCode == shortcut.keyCode;
+ } else if (event.key in ElectronKeysMapping) {
+ return ElectronKeysMapping[event.key] === shortcut.key;
+ }
+
+ // get the key from the keyCode if key is not provided.
+ const key = event.key || String.fromCharCode(event.keyCode);
+
+ // For character keys, we match if the final character is the expected one.
+ // But for digits we also accept indirect match to please azerty keyboard,
+ // which requires Shift to be pressed to get digits.
+ return (
+ key.toLowerCase() == shortcut.key ||
+ (shortcut.key.match(/[0-9]/) &&
+ event.keyCode == shortcut.key.charCodeAt(0))
+ );
+ },
+
+ handleEvent(event) {
+ for (const [key, shortcut] of this.keys) {
+ if (this.doesEventMatchShortcut(event, shortcut)) {
+ this.eventEmitter.emit(key, event);
+ }
+ }
+ },
+
+ on(key, listener) {
+ if (typeof listener !== "function") {
+ throw new Error(
+ "KeyShortcuts.on() expects a function as " + "second argument"
+ );
+ }
+ if (!this.keys.has(key)) {
+ const shortcut = KeyShortcuts.parseElectronKey(this.window, key);
+ // The key string is wrong and we were unable to compute the key shortcut
+ if (!shortcut) {
+ return;
+ }
+ this.keys.set(key, shortcut);
+ }
+ this.eventEmitter.on(key, listener);
+ },
+
+ off(key, listener) {
+ this.eventEmitter.off(key, listener);
+ },
+};
+
+module.exports = KeyShortcuts;
diff --git a/devtools/client/shared/keycodes.js b/devtools/client/shared/keycodes.js
new file mode 100644
index 0000000000..0a70a8fbcf
--- /dev/null
+++ b/devtools/client/shared/keycodes.js
@@ -0,0 +1,139 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Keyboard handling is a mess. http://unixpapa.com/js/key.html
+ * It would be good to use DOM L3 Keyboard events,
+ * http://www.w3.org/TR/2010/WD-DOM-Level-3-Events-20100907/#events-keyboardevents
+ * however only Webkit supports them, and there isn't a shim on Modernizr:
+ * https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-browser-Polyfills
+ * and when the code that uses this KeyEvent was written, nothing was clear,
+ * so instead, we're using this unmodern shim:
+ * http://stackoverflow.com/questions/5681146/chrome-10-keyevent-or-something-similar-to-firefoxs-keyevent
+ * See BUG 664991: keyboard handling should be updated to use DOM-L3
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=664991
+ */
+
+exports.KeyCodes = {
+ DOM_VK_CANCEL: 3,
+ DOM_VK_HELP: 6,
+ DOM_VK_BACK_SPACE: 8,
+ DOM_VK_TAB: 9,
+ DOM_VK_CLEAR: 12,
+ DOM_VK_RETURN: 13,
+ DOM_VK_SHIFT: 16,
+ DOM_VK_CONTROL: 17,
+ DOM_VK_ALT: 18,
+ DOM_VK_PAUSE: 19,
+ DOM_VK_CAPS_LOCK: 20,
+ DOM_VK_ESCAPE: 27,
+ DOM_VK_SPACE: 32,
+ DOM_VK_PAGE_UP: 33,
+ DOM_VK_PAGE_DOWN: 34,
+ DOM_VK_END: 35,
+ DOM_VK_HOME: 36,
+ DOM_VK_LEFT: 37,
+ DOM_VK_UP: 38,
+ DOM_VK_RIGHT: 39,
+ DOM_VK_DOWN: 40,
+ DOM_VK_PRINTSCREEN: 44,
+ DOM_VK_INSERT: 45,
+ DOM_VK_DELETE: 46,
+ DOM_VK_0: 48,
+ DOM_VK_1: 49,
+ DOM_VK_2: 50,
+ DOM_VK_3: 51,
+ DOM_VK_4: 52,
+ DOM_VK_5: 53,
+ DOM_VK_6: 54,
+ DOM_VK_7: 55,
+ DOM_VK_8: 56,
+ DOM_VK_9: 57,
+ DOM_VK_SEMICOLON: 59,
+ DOM_VK_EQUALS: 61,
+ DOM_VK_A: 65,
+ DOM_VK_B: 66,
+ DOM_VK_C: 67,
+ DOM_VK_D: 68,
+ DOM_VK_E: 69,
+ DOM_VK_F: 70,
+ DOM_VK_G: 71,
+ DOM_VK_H: 72,
+ DOM_VK_I: 73,
+ DOM_VK_J: 74,
+ DOM_VK_K: 75,
+ DOM_VK_L: 76,
+ DOM_VK_M: 77,
+ DOM_VK_N: 78,
+ DOM_VK_O: 79,
+ DOM_VK_P: 80,
+ DOM_VK_Q: 81,
+ DOM_VK_R: 82,
+ DOM_VK_S: 83,
+ DOM_VK_T: 84,
+ DOM_VK_U: 85,
+ DOM_VK_V: 86,
+ DOM_VK_W: 87,
+ DOM_VK_X: 88,
+ DOM_VK_Y: 89,
+ DOM_VK_Z: 90,
+ DOM_VK_CONTEXT_MENU: 93,
+ DOM_VK_NUMPAD0: 96,
+ DOM_VK_NUMPAD1: 97,
+ DOM_VK_NUMPAD2: 98,
+ DOM_VK_NUMPAD3: 99,
+ DOM_VK_NUMPAD4: 100,
+ DOM_VK_NUMPAD5: 101,
+ DOM_VK_NUMPAD6: 102,
+ DOM_VK_NUMPAD7: 103,
+ DOM_VK_NUMPAD8: 104,
+ DOM_VK_NUMPAD9: 105,
+ DOM_VK_MULTIPLY: 106,
+ DOM_VK_ADD: 107,
+ DOM_VK_SEPARATOR: 108,
+ DOM_VK_SUBTRACT: 109,
+ DOM_VK_DECIMAL: 110,
+ DOM_VK_DIVIDE: 111,
+ DOM_VK_F1: 112,
+ DOM_VK_F2: 113,
+ DOM_VK_F3: 114,
+ DOM_VK_F4: 115,
+ DOM_VK_F5: 116,
+ DOM_VK_F6: 117,
+ DOM_VK_F7: 118,
+ DOM_VK_F8: 119,
+ DOM_VK_F9: 120,
+ DOM_VK_F10: 121,
+ DOM_VK_F11: 122,
+ DOM_VK_F12: 123,
+ DOM_VK_F13: 124,
+ DOM_VK_F14: 125,
+ DOM_VK_F15: 126,
+ DOM_VK_F16: 127,
+ DOM_VK_F17: 128,
+ DOM_VK_F18: 129,
+ DOM_VK_F19: 130,
+ DOM_VK_F20: 131,
+ DOM_VK_F21: 132,
+ DOM_VK_F22: 133,
+ DOM_VK_F23: 134,
+ DOM_VK_F24: 135,
+ DOM_VK_NUM_LOCK: 144,
+ DOM_VK_SCROLL_LOCK: 145,
+ DOM_VK_COMMA: 188,
+ DOM_VK_PERIOD: 190,
+ DOM_VK_SLASH: 191,
+ DOM_VK_BACK_QUOTE: 192,
+ DOM_VK_OPEN_BRACKET: 219,
+ DOM_VK_BACK_SLASH: 220,
+ DOM_VK_CLOSE_BRACKET: 221,
+ DOM_VK_QUOTE: 222,
+ DOM_VK_META: 224,
+ DOM_VK_COLON: 58,
+ DOM_VK_VOLUME_MUTE: 181,
+ DOM_VK_VOLUME_DOWN: 182,
+ DOM_VK_VOLUME_UP: 183,
+};
diff --git a/devtools/client/shared/link.js b/devtools/client/shared/link.js
new file mode 100644
index 0000000000..2e85caad0c
--- /dev/null
+++ b/devtools/client/shared/link.js
@@ -0,0 +1,86 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ gDevTools,
+} = require("resource://devtools/client/framework/devtools.js");
+
+/**
+ * Retrieve the most recent chrome window.
+ */
+function _getTopWindow() {
+ // Try the main application window, such as a browser window.
+ let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
+ if (win?.openWebLinkIn && win?.openTrustedLinkIn) {
+ return win;
+ }
+ // For non-browser cases like Browser Toolbox, try any chrome window.
+ win = Services.wm.getMostRecentWindow(null);
+ if (win?.openWebLinkIn && win?.openTrustedLinkIn) {
+ return win;
+ }
+ return null;
+}
+
+/**
+ * Opens a |url| that does not require trusted access, such as a documentation page, in a
+ * new tab.
+ *
+ * @param {String} url
+ * The url to open.
+ * @param {Object} options
+ * Optional parameters, see documentation for openUILinkIn in utilityOverlay.js
+ */
+exports.openDocLink = async function (url, options) {
+ const top = _getTopWindow();
+ if (!top) {
+ return;
+ }
+ top.openWebLinkIn(url, "tab", options);
+};
+
+/**
+ * Opens a |url| controlled by web content in a new tab.
+ *
+ * If the current tab has an open toolbox, this will attempt to refine the
+ * `triggeringPrincipal` of the link using the tab's `contentPrincipal`. This is only an
+ * approximation, so bug 1467945 hopes to improve this.
+ *
+ * @param {String} url
+ * The url to open.
+ * @param {Object} options
+ * Optional parameters, see documentation for openUILinkIn in utilityOverlay.js
+ */
+exports.openContentLink = async function (url, options = {}) {
+ const top = _getTopWindow();
+ if (!top) {
+ return;
+ }
+ if (!options.triggeringPrincipal && top.gBrowser) {
+ const tab = top.gBrowser.selectedTab;
+ if (gDevTools.hasToolboxForTab(tab)) {
+ options.triggeringPrincipal = tab.linkedBrowser.contentPrincipal;
+ options.csp = tab.linkedBrowser.csp;
+ }
+ }
+ top.openWebLinkIn(url, "tab", options);
+};
+
+/**
+ * Open a trusted |url| in a new tab using the SystemPrincipal.
+ *
+ * @param {String} url
+ * The url to open.
+ * @param {Object} options
+ * Optional parameters, see documentation for openUILinkIn in utilityOverlay.js
+ */
+exports.openTrustedLink = async function (url, options) {
+ const top = _getTopWindow();
+ if (!top) {
+ return;
+ }
+ top.openTrustedLinkIn(url, "tab", options);
+};
diff --git a/devtools/client/shared/moz.build b/devtools/client/shared/moz.build
new file mode 100644
index 0000000000..ca0e3ef124
--- /dev/null
+++ b/devtools/client/shared/moz.build
@@ -0,0 +1,65 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+BROWSER_CHROME_MANIFESTS += ["test/browser.toml"]
+XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell/xpcshell.toml"]
+TEST_HARNESS_FILES.xpcshell.devtools.client.shared.test += [
+ "test/shared-head.js",
+]
+
+DIRS += [
+ "components",
+ "fluent-l10n",
+ "redux",
+ "remote-debugging",
+ "source-map-loader",
+ "sourceeditor",
+ "vendor",
+ "widgets",
+]
+
+DevToolsModules(
+ "async-store-helper.js",
+ "autocomplete-popup.js",
+ "classnames.js",
+ "css-angle.js",
+ "curl.js",
+ "devices.js",
+ "enum.js",
+ "events.js",
+ "focus.js",
+ "inplace-editor.js",
+ "key-shortcuts.js",
+ "keycodes.js",
+ "link.js",
+ "node-attribute-parser.js",
+ "output-parser.js",
+ "prefs.js",
+ "react-utils.js",
+ "screenshot.js",
+ "scroll.js",
+ "source-utils.js",
+ "string-utils.js",
+ "stylesheet-utils.js",
+ "suggestion-picker.js",
+ "telemetry.js",
+ "theme.js",
+ "thread-utils.js",
+ "undo.js",
+ "unicode-url.js",
+ "view-source.js",
+ "WeakMapMap.js",
+ "webgl-utils.js",
+ "worker-utils.js",
+ "workers-listener.js",
+ "zoom-keys.js",
+)
+
+with Files("**"):
+ BUG_COMPONENT = ("DevTools", "General")
+
+with Files("components/**"):
+ BUG_COMPONENT = ("DevTools", "Shared Components")
diff --git a/devtools/client/shared/node-attribute-parser.js b/devtools/client/shared/node-attribute-parser.js
new file mode 100644
index 0000000000..815a23ee8a
--- /dev/null
+++ b/devtools/client/shared/node-attribute-parser.js
@@ -0,0 +1,435 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * This module contains a small element attribute value parser. It's primary
+ * goal is to extract link information from attribute values (like the href in
+ * <a href="/some/link.html"> for example).
+ *
+ * There are several types of linkable attribute values:
+ * - TYPE_URI: a URI (e.g. <a href="uri">).
+ * - TYPE_URI_LIST: a space separated list of URIs (e.g. <a ping="uri1 uri2">).
+ * - TYPE_IDREF: a reference to an other element in the same document via its id
+ * (e.g. <label for="input-id"> or <key command="command-id">).
+ * - TYPE_IDREF_LIST: a space separated list of IDREFs (e.g.
+ * <output for="id1 id2">).
+ * - TYPE_JS_RESOURCE_URI: a URI to a javascript resource that can be opened in
+ * the devtools (e.g. <script src="uri">).
+ * - TYPE_CSS_RESOURCE_URI: a URI to a css resource that can be opened in the
+ * devtools (e.g. <link href="uri">).
+ *
+ * parseAttribute is the parser entry function, exported on this module.
+ */
+
+const TYPE_STRING = "string";
+const TYPE_URI = "uri";
+const TYPE_URI_LIST = "uriList";
+const TYPE_IDREF = "idref";
+const TYPE_IDREF_LIST = "idrefList";
+const TYPE_JS_RESOURCE_URI = "jsresource";
+const TYPE_CSS_RESOURCE_URI = "cssresource";
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+const WILDCARD = Symbol();
+
+const ATTRIBUTE_TYPES = new Map([
+ ["action", { form: { namespaceURI: HTML_NS, type: TYPE_URI } }],
+ [
+ "aria-activedescendant",
+ { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF } },
+ ],
+ [
+ "aria-controls",
+ { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST } },
+ ],
+ [
+ "aria-describedby",
+ { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST } },
+ ],
+ [
+ "aria-details",
+ { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST } },
+ ],
+ [
+ "aria-errormessage",
+ { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF } },
+ ],
+ [
+ "aria-flowto",
+ { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST } },
+ ],
+ [
+ "aria-labelledby",
+ { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST } },
+ ],
+ ["aria-owns", { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST } }],
+ ["background", { body: { namespaceURI: HTML_NS, type: TYPE_URI } }],
+ [
+ "cite",
+ {
+ blockquote: { namespaceURI: HTML_NS, type: TYPE_URI },
+ q: { namespaceURI: HTML_NS, type: TYPE_URI },
+ del: { namespaceURI: HTML_NS, type: TYPE_URI },
+ ins: { namespaceURI: HTML_NS, type: TYPE_URI },
+ },
+ ],
+ ["classid", { object: { namespaceURI: HTML_NS, type: TYPE_URI } }],
+ [
+ "codebase",
+ {
+ object: { namespaceURI: HTML_NS, type: TYPE_URI },
+ applet: { namespaceURI: HTML_NS, type: TYPE_URI },
+ },
+ ],
+ [
+ "command",
+ {
+ menuitem: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ key: { namespaceURI: XUL_NS, type: TYPE_IDREF },
+ },
+ ],
+ [
+ "contextmenu",
+ {
+ WILDCARD: { namespaceURI: WILDCARD, type: TYPE_IDREF },
+ },
+ ],
+ ["data", { object: { namespaceURI: HTML_NS, type: TYPE_URI } }],
+ [
+ "for",
+ {
+ label: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ output: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST },
+ },
+ ],
+ [
+ "form",
+ {
+ button: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ fieldset: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ input: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ keygen: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ label: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ object: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ output: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ select: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ textarea: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ },
+ ],
+ [
+ "formaction",
+ {
+ button: { namespaceURI: HTML_NS, type: TYPE_URI },
+ input: { namespaceURI: HTML_NS, type: TYPE_URI },
+ },
+ ],
+ [
+ "headers",
+ {
+ td: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST },
+ th: { namespaceURI: HTML_NS, type: TYPE_IDREF_LIST },
+ },
+ ],
+ [
+ "href",
+ {
+ a: { namespaceURI: HTML_NS, type: TYPE_URI },
+ area: { namespaceURI: HTML_NS, type: TYPE_URI },
+ link: [
+ {
+ namespaceURI: WILDCARD,
+ type: TYPE_CSS_RESOURCE_URI,
+ isValid: attributes => {
+ return getAttribute(attributes, "rel") === "stylesheet";
+ },
+ },
+ { namespaceURI: WILDCARD, type: TYPE_URI },
+ ],
+ base: { namespaceURI: HTML_NS, type: TYPE_URI },
+ },
+ ],
+ [
+ "icon",
+ {
+ menuitem: { namespaceURI: HTML_NS, type: TYPE_URI },
+ },
+ ],
+ ["invoketarget", { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF } }],
+ ["list", { input: { namespaceURI: HTML_NS, type: TYPE_IDREF } }],
+ [
+ "longdesc",
+ {
+ img: { namespaceURI: HTML_NS, type: TYPE_URI },
+ frame: { namespaceURI: HTML_NS, type: TYPE_URI },
+ iframe: { namespaceURI: HTML_NS, type: TYPE_URI },
+ },
+ ],
+ ["manifest", { html: { namespaceURI: HTML_NS, type: TYPE_URI } }],
+ [
+ "menu",
+ {
+ button: { namespaceURI: HTML_NS, type: TYPE_IDREF },
+ WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF },
+ },
+ ],
+ [
+ "ping",
+ {
+ a: { namespaceURI: HTML_NS, type: TYPE_URI_LIST },
+ area: { namespaceURI: HTML_NS, type: TYPE_URI_LIST },
+ },
+ ],
+ ["poster", { video: { namespaceURI: HTML_NS, type: TYPE_URI } }],
+ ["profile", { head: { namespaceURI: HTML_NS, type: TYPE_URI } }],
+ [
+ "src",
+ {
+ script: { namespaceURI: WILDCARD, type: TYPE_JS_RESOURCE_URI },
+ input: { namespaceURI: HTML_NS, type: TYPE_URI },
+ frame: { namespaceURI: HTML_NS, type: TYPE_URI },
+ iframe: { namespaceURI: HTML_NS, type: TYPE_URI },
+ img: { namespaceURI: HTML_NS, type: TYPE_URI },
+ audio: { namespaceURI: HTML_NS, type: TYPE_URI },
+ embed: { namespaceURI: HTML_NS, type: TYPE_URI },
+ source: { namespaceURI: HTML_NS, type: TYPE_URI },
+ track: { namespaceURI: HTML_NS, type: TYPE_URI },
+ video: { namespaceURI: HTML_NS, type: TYPE_URI },
+ stringbundle: { namespaceURI: XUL_NS, type: TYPE_URI },
+ },
+ ],
+ [
+ "usemap",
+ {
+ img: { namespaceURI: HTML_NS, type: TYPE_URI },
+ input: { namespaceURI: HTML_NS, type: TYPE_URI },
+ object: { namespaceURI: HTML_NS, type: TYPE_URI },
+ },
+ ],
+ ["xmlns", { WILDCARD: { namespaceURI: WILDCARD, type: TYPE_URI } }],
+ ["containment", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_URI } }],
+ ["context", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ ["datasources", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_URI_LIST } }],
+ ["insertafter", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ ["insertbefore", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ ["observes", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ ["popovertarget", { WILDCARD: { namespaceURI: HTML_NS, type: TYPE_IDREF } }],
+ ["popup", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ ["ref", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_URI } }],
+ ["removeelement", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ ["template", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ ["tooltip", { WILDCARD: { namespaceURI: XUL_NS, type: TYPE_IDREF } }],
+ // SVG links aren't handled yet, see bug 1158831.
+ // ["fill", {
+ // WILDCARD: {namespaceURI: SVG_NS, type: },
+ // }],
+ // ["stroke", {
+ // WILDCARD: {namespaceURI: SVG_NS, type: },
+ // }],
+ // ["markerstart", {
+ // WILDCARD: {namespaceURI: SVG_NS, type: },
+ // }],
+ // ["markermid", {
+ // WILDCARD: {namespaceURI: SVG_NS, type: },
+ // }],
+ // ["markerend", {
+ // WILDCARD: {namespaceURI: SVG_NS, type: },
+ // }],
+ // ["xlink:href", {
+ // WILDCARD: {namespaceURI: SVG_NS, type: },
+ // }],
+]);
+
+var parsers = {
+ [TYPE_URI](attributeValue) {
+ return [
+ {
+ type: TYPE_URI,
+ value: attributeValue,
+ },
+ ];
+ },
+ [TYPE_URI_LIST](attributeValue) {
+ const data = splitBy(attributeValue, " ");
+ for (const token of data) {
+ if (!token.type) {
+ token.type = TYPE_URI;
+ }
+ }
+ return data;
+ },
+ [TYPE_JS_RESOURCE_URI](attributeValue) {
+ return [
+ {
+ type: TYPE_JS_RESOURCE_URI,
+ value: attributeValue,
+ },
+ ];
+ },
+ [TYPE_CSS_RESOURCE_URI](attributeValue) {
+ return [
+ {
+ type: TYPE_CSS_RESOURCE_URI,
+ value: attributeValue,
+ },
+ ];
+ },
+ [TYPE_IDREF](attributeValue) {
+ return [
+ {
+ type: TYPE_IDREF,
+ value: attributeValue,
+ },
+ ];
+ },
+ [TYPE_IDREF_LIST](attributeValue) {
+ const data = splitBy(attributeValue, " ");
+ for (const token of data) {
+ if (!token.type) {
+ token.type = TYPE_IDREF;
+ }
+ }
+ return data;
+ },
+};
+
+/**
+ * Parse an attribute value.
+ * @param {String} namespaceURI The namespaceURI of the node that has the
+ * attribute.
+ * @param {String} tagName The tagName of the node that has the attribute.
+ * @param {Array} attributes The list of all attributes of the node. This should
+ * be an array of {name, value} objects.
+ * @param {String} attributeName The name of the attribute to parse.
+ * @param {String} attributeValue The value of the attribute to parse.
+ * @return {Array} An array of tokens that represents the value. Each token is
+ * an object {type: [string|uri|jsresource|cssresource|idref], value}.
+ * For instance parsing the ping attribute in <a ping="uri1 uri2"> returns:
+ * [
+ * {type: "uri", value: "uri2"},
+ * {type: "string", value: " "},
+ * {type: "uri", value: "uri1"}
+ * ]
+ */
+function parseAttribute(
+ namespaceURI,
+ tagName,
+ attributes,
+ attributeName,
+ attributeValue
+) {
+ const type = getType(namespaceURI, tagName, attributes, attributeName);
+ if (!type) {
+ return [
+ {
+ type: TYPE_STRING,
+ value: attributeValue,
+ },
+ ];
+ }
+
+ return parsers[type](attributeValue);
+}
+
+/**
+ * Get the type for links in this attribute if any.
+ * @param {String} namespaceURI The node's namespaceURI.
+ * @param {String} tagName The node's tagName.
+ * @param {Array} attributes The node's attributes, as a list of {name, value}
+ * objects.
+ * @param {String} attributeName The name of the attribute to get the type for.
+ * @return {Object} null if no type exist for this attribute on this node, the
+ * type object otherwise.
+ */
+function getType(namespaceURI, tagName, attributes, attributeName) {
+ const attributeType = ATTRIBUTE_TYPES.get(attributeName);
+ if (!attributeType) {
+ return null;
+ }
+
+ const lcTagName = tagName.toLowerCase();
+ const typeData = attributeType[lcTagName] || attributeType.WILDCARD;
+
+ if (!typeData) {
+ return null;
+ }
+
+ if (Array.isArray(typeData)) {
+ for (const data of typeData) {
+ const hasNamespace =
+ data.namespaceURI === WILDCARD || data.namespaceURI === namespaceURI;
+ const isValid = data.isValid ? data.isValid(attributes) : true;
+
+ if (hasNamespace && isValid) {
+ return data.type;
+ }
+ }
+
+ return null;
+ } else if (
+ typeData.namespaceURI === WILDCARD ||
+ typeData.namespaceURI === namespaceURI
+ ) {
+ return typeData.type;
+ }
+
+ return null;
+}
+
+function getAttribute(attributes, attributeName) {
+ const attribute = attributes.find(x => x.name === attributeName);
+ return attribute ? attribute.value : null;
+}
+
+/**
+ * Split a string by a given character and return an array of objects parts.
+ * The array will contain objects for the split character too, marked with
+ * TYPE_STRING type.
+ * @param {String} value The string to parse.
+ * @param {String} splitChar A 1 length split character.
+ * @return {Array}
+ */
+function splitBy(value, splitChar) {
+ const data = [];
+
+ let i = 0,
+ buffer = "";
+ while (i <= value.length) {
+ if (i === value.length && buffer) {
+ data.push({ value: buffer });
+ }
+ if (value[i] === splitChar) {
+ if (buffer) {
+ data.push({ value: buffer });
+ }
+ data.push({
+ type: TYPE_STRING,
+ value: splitChar,
+ });
+ buffer = "";
+ } else {
+ buffer += value[i];
+ }
+
+ i++;
+ }
+ return data;
+}
+
+exports.parseAttribute = parseAttribute;
+exports.ATTRIBUTE_TYPES = {
+ TYPE_STRING,
+ TYPE_URI,
+ TYPE_URI_LIST,
+ TYPE_IDREF,
+ TYPE_IDREF_LIST,
+ TYPE_JS_RESOURCE_URI,
+ TYPE_CSS_RESOURCE_URI,
+};
+
+// Exported for testing only.
+exports.splitBy = splitBy;
diff --git a/devtools/client/shared/output-parser.js b/devtools/client/shared/output-parser.js
new file mode 100644
index 0000000000..4ae1a60b11
--- /dev/null
+++ b/devtools/client/shared/output-parser.js
@@ -0,0 +1,1984 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ angleUtils,
+} = require("resource://devtools/client/shared/css-angle.js");
+const { colorUtils } = require("resource://devtools/shared/css/color.js");
+const { getCSSLexer } = require("resource://devtools/shared/css/lexer.js");
+const {
+ appendText,
+} = require("resource://devtools/client/inspector/shared/utils.js");
+
+const STYLE_INSPECTOR_PROPERTIES =
+ "devtools/shared/locales/styleinspector.properties";
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
+
+// Functions that accept an angle argument.
+const ANGLE_TAKING_FUNCTIONS = [
+ "linear-gradient",
+ "-moz-linear-gradient",
+ "repeating-linear-gradient",
+ "-moz-repeating-linear-gradient",
+ "conic-gradient",
+ "repeating-conic-gradient",
+ "rotate",
+ "rotateX",
+ "rotateY",
+ "rotateZ",
+ "rotate3d",
+ "skew",
+ "skewX",
+ "skewY",
+ "hue-rotate",
+];
+// All cubic-bezier CSS timing-function names.
+const BEZIER_KEYWORDS = [
+ "linear",
+ "ease-in-out",
+ "ease-in",
+ "ease-out",
+ "ease",
+];
+// Functions that accept a color argument.
+const COLOR_TAKING_FUNCTIONS = [
+ "linear-gradient",
+ "-moz-linear-gradient",
+ "repeating-linear-gradient",
+ "-moz-repeating-linear-gradient",
+ "radial-gradient",
+ "-moz-radial-gradient",
+ "repeating-radial-gradient",
+ "-moz-repeating-radial-gradient",
+ "conic-gradient",
+ "repeating-conic-gradient",
+ "drop-shadow",
+ "color-mix",
+];
+// Functions that accept a shape argument.
+const BASIC_SHAPE_FUNCTIONS = ["polygon", "circle", "ellipse", "inset"];
+
+const BACKDROP_FILTER_ENABLED = Services.prefs.getBoolPref(
+ "layout.css.backdrop-filter.enabled"
+);
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+// Very long text properties should be truncated using CSS to avoid creating
+// extremely tall propertyvalue containers. 5000 characters is an arbitrary
+// limit. Assuming an average ruleview can hold 50 characters per line, this
+// should start truncating properties which would otherwise be 100 lines long.
+const TRUNCATE_LENGTH_THRESHOLD = 5000;
+const TRUNCATE_NODE_CLASSNAME = "propertyvalue-long-text";
+
+/**
+ * This module is used to process CSS text declarations and output DOM fragments (to be
+ * appended to panels in DevTools) for CSS values decorated with additional UI and
+ * functionality.
+ *
+ * For example:
+ * - attaching swatches for values instrumented with specialized tools: colors, timing
+ * functions (cubic-bezier), filters, shapes, display values (flex/grid), etc.
+ * - adding previews where possible (images, fonts, CSS transforms).
+ * - converting between color types on Shift+click on their swatches.
+ *
+ * Usage:
+ * const OutputParser = require("devtools/client/shared/output-parser");
+ * const parser = new OutputParser(document, cssProperties);
+ * parser.parseCssProperty("color", "red"); // Returns document fragment.
+ *
+ */
+class OutputParser {
+ /**
+ * @param {Document} document
+ * Used to create DOM nodes.
+ * @param {CssProperties} cssProperties
+ * Instance of CssProperties, an object which provides an interface for
+ * working with the database of supported CSS properties and values.
+ */
+ constructor(document, cssProperties) {
+ this.#doc = document;
+ this.#cssProperties = cssProperties;
+ }
+
+ #angleSwatches = new WeakMap();
+ #colorSwatches = new WeakMap();
+ #cssProperties;
+ #doc;
+ #parsed = [];
+
+ /**
+ * Parse a CSS property value given a property name.
+ *
+ * @param {String} name
+ * CSS Property Name
+ * @param {String} value
+ * CSS Property value
+ * @param {Object} [options]
+ * Options object. For valid options and default values see
+ * #mergeOptions().
+ * @return {DocumentFragment}
+ * A document fragment containing color swatches etc.
+ */
+ parseCssProperty(name, value, options = {}) {
+ options = this.#mergeOptions(options);
+
+ options.expectCubicBezier = this.#cssProperties.supportsType(
+ name,
+ "timing-function"
+ );
+ options.expectLinearEasing = this.#cssProperties.supportsType(
+ name,
+ "timing-function"
+ );
+ options.expectDisplay = name === "display";
+ options.expectFilter =
+ name === "filter" ||
+ (BACKDROP_FILTER_ENABLED && name === "backdrop-filter");
+ options.expectShape =
+ name === "clip-path" ||
+ name === "shape-outside" ||
+ name === "offset-path";
+ options.expectFont = name === "font-family";
+ options.supportsColor =
+ this.#cssProperties.supportsType(name, "color") ||
+ this.#cssProperties.supportsType(name, "gradient") ||
+ (name.startsWith("--") && InspectorUtils.isValidCSSColor(value));
+
+ // The filter property is special in that we want to show the
+ // swatch even if the value is invalid, because this way the user
+ // can easily use the editor to fix it.
+ if (options.expectFilter || this.#cssPropertySupportsValue(name, value)) {
+ return this.#parse(value, options);
+ }
+ this.#appendTextNode(value);
+
+ return this.#toDOM();
+ }
+
+ /**
+ * Read tokens from |tokenStream| and collect all the (non-comment)
+ * text. Return the collected texts and variable data (if any).
+ * Stop when an unmatched closing paren is seen.
+ * If |stopAtComma| is true, then also stop when a top-level
+ * (unparenthesized) comma is seen.
+ *
+ * @param {String} text
+ * The original source text.
+ * @param {CSSLexer} tokenStream
+ * The token stream from which to read.
+ * @param {Object} options
+ * The options object in use; @see #mergeOptions.
+ * @param {Boolean} stopAtComma
+ * If true, stop at a comma.
+ * @return {Object}
+ * An object of the form {tokens, functionData, sawComma, sawVariable}.
+ * |tokens| is a list of the non-comment, non-whitespace tokens
+ * that were seen. The stopping token (paren or comma) will not
+ * be included.
+ * |functionData| is a list of parsed strings and nodes that contain the
+ * data between the matching parenthesis. The stopping token's text will
+ * not be included.
+ * |sawComma| is true if the stop was due to a comma, or false otherwise.
+ * |sawVariable| is true if a variable was seen while parsing the text.
+ */
+ #parseMatchingParens(text, tokenStream, options, stopAtComma) {
+ let depth = 1;
+ const functionData = [];
+ const tokens = [];
+ let sawVariable = false;
+
+ while (depth > 0) {
+ const token = tokenStream.nextToken();
+ if (!token) {
+ break;
+ }
+ if (token.tokenType === "comment") {
+ continue;
+ }
+
+ if (token.tokenType === "symbol") {
+ if (stopAtComma && depth === 1 && token.text === ",") {
+ return { tokens, functionData, sawComma: true, sawVariable };
+ } else if (token.text === "(") {
+ ++depth;
+ } else if (token.text === ")") {
+ --depth;
+ if (depth === 0) {
+ break;
+ }
+ }
+ } else if (
+ token.tokenType === "function" &&
+ token.text === "var" &&
+ options.getVariableValue
+ ) {
+ sawVariable = true;
+ const { node } = this.#parseVariable(token, text, tokenStream, options);
+ functionData.push(node);
+ } else if (token.tokenType === "function") {
+ ++depth;
+ }
+
+ if (
+ token.tokenType !== "function" ||
+ token.text !== "var" ||
+ !options.getVariableValue
+ ) {
+ functionData.push(text.substring(token.startOffset, token.endOffset));
+ }
+
+ if (token.tokenType !== "whitespace") {
+ tokens.push(token);
+ }
+ }
+
+ return { tokens, functionData, sawComma: false, sawVariable };
+ }
+
+ /**
+ * Parse var() use and return a variable node to be added to the output state.
+ * This will read tokens up to and including the ")" that closes the "var("
+ * invocation.
+ *
+ * @param {CSSToken} initialToken
+ * The "var(" token that was already seen.
+ * @param {String} text
+ * The original input text.
+ * @param {CSSLexer} tokenStream
+ * The token stream from which to read.
+ * @param {Object} options
+ * The options object in use; @see #mergeOptions.
+ * @return {Object}
+ * - node: A node for the variable, with the appropriate text and
+ * title. Eg. a span with "var(--var1)" as the textContent
+ * and a title for --var1 like "--var1 = 10" or
+ * "--var1 is not set".
+ * - value: The value for the variable.
+ */
+ #parseVariable(initialToken, text, tokenStream, options) {
+ // Handle the "var(".
+ const varText = text.substring(
+ initialToken.startOffset,
+ initialToken.endOffset
+ );
+ const variableNode = this.#createNode("span", {}, varText);
+
+ // Parse the first variable name within the parens of var().
+ const { tokens, functionData, sawComma, sawVariable } =
+ this.#parseMatchingParens(text, tokenStream, options, true);
+
+ const result = sawVariable ? "" : functionData.join("");
+
+ // Display options for the first and second argument in the var().
+ const firstOpts = {};
+ const secondOpts = {};
+
+ let varValue;
+
+ // Get the variable value if it is in use.
+ if (tokens && tokens.length === 1) {
+ varValue = options.getVariableValue(tokens[0].text);
+ }
+
+ // Get the variable name.
+ const varName = text.substring(tokens[0].startOffset, tokens[0].endOffset);
+
+ if (typeof varValue === "string") {
+ // The variable value is valid, set the variable name's title of the first argument
+ // in var() to display the variable name and value.
+ firstOpts["data-variable"] = STYLE_INSPECTOR_L10N.getFormatStr(
+ "rule.variableValue",
+ varName,
+ varValue
+ );
+ firstOpts.class = options.matchedVariableClass;
+ secondOpts.class = options.unmatchedVariableClass;
+ } else {
+ // The variable name is not valid, mark it unmatched.
+ firstOpts.class = options.unmatchedVariableClass;
+ firstOpts["data-variable"] = STYLE_INSPECTOR_L10N.getFormatStr(
+ "rule.variableUnset",
+ varName
+ );
+ }
+
+ variableNode.appendChild(this.#createNode("span", firstOpts, result));
+
+ // If we saw a ",", then append it and show the remainder using
+ // the correct highlighting.
+ if (sawComma) {
+ variableNode.appendChild(this.#doc.createTextNode(","));
+
+ // Parse the text up until the close paren, being sure to
+ // disable the special case for filter.
+ const subOptions = Object.assign({}, options);
+ subOptions.expectFilter = false;
+ const saveParsed = this.#parsed;
+ this.#parsed = [];
+ const rest = this.#doParse(text, subOptions, tokenStream, true);
+ this.#parsed = saveParsed;
+
+ const span = this.#createNode("span", secondOpts);
+ span.appendChild(rest);
+ variableNode.appendChild(span);
+ }
+ variableNode.appendChild(this.#doc.createTextNode(")"));
+
+ return { node: variableNode, value: varValue };
+ }
+
+ /**
+ * The workhorse for @see #parse. This parses some CSS text,
+ * stopping at EOF; or optionally when an umatched close paren is
+ * seen.
+ *
+ * @param {String} text
+ * The original input text.
+ * @param {Object} options
+ * The options object in use; @see #mergeOptions.
+ * @param {CSSLexer} tokenStream
+ * The token stream from which to read
+ * @param {Boolean} stopAtCloseParen
+ * If true, stop at an umatched close paren.
+ * @return {DocumentFragment}
+ * A document fragment.
+ */
+ // eslint-disable-next-line complexity
+ #doParse(text, options, tokenStream, stopAtCloseParen) {
+ let parenDepth = stopAtCloseParen ? 1 : 0;
+ let outerMostFunctionTakesColor = false;
+ const colorFunctions = [];
+ let fontFamilyNameParts = [];
+ let previousWasBang = false;
+
+ const colorOK = function () {
+ return (
+ options.supportsColor ||
+ (options.expectFilter &&
+ parenDepth === 1 &&
+ outerMostFunctionTakesColor)
+ );
+ };
+
+ const angleOK = function (angle) {
+ return new angleUtils.CssAngle(angle).valid;
+ };
+
+ let spaceNeeded = false;
+ let done = false;
+
+ while (!done) {
+ const token = tokenStream.nextToken();
+ if (!token) {
+ break;
+ }
+ const lowerCaseTokenText = token.text?.toLowerCase();
+
+ if (token.tokenType === "comment") {
+ // This doesn't change spaceNeeded, because we didn't emit
+ // anything to the output.
+ continue;
+ }
+
+ switch (token.tokenType) {
+ case "function": {
+ const isColorTakingFunction =
+ COLOR_TAKING_FUNCTIONS.includes(lowerCaseTokenText);
+ if (
+ isColorTakingFunction ||
+ ANGLE_TAKING_FUNCTIONS.includes(lowerCaseTokenText)
+ ) {
+ // The function can accept a color or an angle argument, and we know
+ // it isn't special in some other way. So, we let it
+ // through to the ordinary parsing loop so that the value
+ // can be handled in a single place.
+ this.#appendTextNode(
+ text.substring(token.startOffset, token.endOffset)
+ );
+ if (parenDepth === 0) {
+ outerMostFunctionTakesColor = isColorTakingFunction;
+ }
+ if (isColorTakingFunction) {
+ colorFunctions.push({ parenDepth, functionName: token.text });
+ }
+ ++parenDepth;
+ } else if (lowerCaseTokenText === "var" && options.getVariableValue) {
+ const { node: variableNode, value } = this.#parseVariable(
+ token,
+ text,
+ tokenStream,
+ options
+ );
+ if (value && colorOK() && InspectorUtils.isValidCSSColor(value)) {
+ this.#appendColor(value, {
+ ...options,
+ variableContainer: variableNode,
+ colorFunction: colorFunctions.at(-1)?.functionName,
+ });
+ } else {
+ this.#parsed.push(variableNode);
+ }
+ } else {
+ const { functionData, sawVariable } = this.#parseMatchingParens(
+ text,
+ tokenStream,
+ options
+ );
+
+ const functionName = text.substring(
+ token.startOffset,
+ token.endOffset
+ );
+
+ if (sawVariable) {
+ // If function contains variable, we need to add both strings
+ // and nodes.
+ this.#appendTextNode(functionName);
+ for (const data of functionData) {
+ if (typeof data === "string") {
+ this.#appendTextNode(data);
+ } else if (data) {
+ this.#parsed.push(data);
+ }
+ }
+ this.#appendTextNode(")");
+ } else {
+ // If no variable in function, join the text together and add
+ // to DOM accordingly.
+ const functionText = functionName + functionData.join("") + ")";
+
+ if (
+ options.expectCubicBezier &&
+ lowerCaseTokenText === "cubic-bezier"
+ ) {
+ this.#appendCubicBezier(functionText, options);
+ } else if (
+ options.expectLinearEasing &&
+ lowerCaseTokenText === "linear"
+ ) {
+ this.#appendLinear(functionText, options);
+ } else if (
+ colorOK() &&
+ InspectorUtils.isValidCSSColor(functionText)
+ ) {
+ this.#appendColor(functionText, {
+ ...options,
+ colorFunction: colorFunctions.at(-1)?.functionName,
+ });
+ } else if (
+ options.expectShape &&
+ BASIC_SHAPE_FUNCTIONS.includes(lowerCaseTokenText)
+ ) {
+ this.#appendShape(functionText, options);
+ } else {
+ this.#appendTextNode(functionText);
+ }
+ }
+ }
+ break;
+ }
+
+ case "ident":
+ if (
+ options.expectCubicBezier &&
+ BEZIER_KEYWORDS.includes(lowerCaseTokenText)
+ ) {
+ this.#appendCubicBezier(token.text, options);
+ } else if (
+ options.expectLinearEasing &&
+ lowerCaseTokenText == "linear"
+ ) {
+ this.#appendLinear(token.text, options);
+ } else if (this.#isDisplayFlex(text, token, options)) {
+ this.#appendHighlighterToggle(token.text, options.flexClass);
+ } else if (this.#isDisplayGrid(text, token, options)) {
+ this.#appendHighlighterToggle(token.text, options.gridClass);
+ } else if (colorOK() && InspectorUtils.isValidCSSColor(token.text)) {
+ this.#appendColor(token.text, {
+ ...options,
+ colorFunction: colorFunctions.at(-1)?.functionName,
+ });
+ } else if (angleOK(token.text)) {
+ this.#appendAngle(token.text, options);
+ } else if (options.expectFont && !previousWasBang) {
+ // We don't append the identifier if the previous token
+ // was equal to '!', since in that case we expect the
+ // identifier to be equal to 'important'.
+ fontFamilyNameParts.push(token.text);
+ } else {
+ this.#appendTextNode(
+ text.substring(token.startOffset, token.endOffset)
+ );
+ }
+ break;
+
+ case "id":
+ case "hash": {
+ const original = text.substring(token.startOffset, token.endOffset);
+ if (colorOK() && InspectorUtils.isValidCSSColor(original)) {
+ if (spaceNeeded) {
+ // Insert a space to prevent token pasting when a #xxx
+ // color is changed to something like rgb(...).
+ this.#appendTextNode(" ");
+ }
+ this.#appendColor(original, {
+ ...options,
+ colorFunction: colorFunctions.at(-1)?.functionName,
+ });
+ } else {
+ this.#appendTextNode(original);
+ }
+ break;
+ }
+ case "dimension":
+ const value = text.substring(token.startOffset, token.endOffset);
+ if (angleOK(value)) {
+ this.#appendAngle(value, options);
+ } else {
+ this.#appendTextNode(value);
+ }
+ break;
+ case "url":
+ case "bad_url":
+ this.#appendURL(
+ text.substring(token.startOffset, token.endOffset),
+ token.text,
+ options
+ );
+ break;
+
+ case "string":
+ if (options.expectFont) {
+ fontFamilyNameParts.push(
+ text.substring(token.startOffset, token.endOffset)
+ );
+ } else {
+ this.#appendTextNode(
+ text.substring(token.startOffset, token.endOffset)
+ );
+ }
+ break;
+
+ case "whitespace":
+ if (options.expectFont) {
+ fontFamilyNameParts.push(" ");
+ } else {
+ this.#appendTextNode(
+ text.substring(token.startOffset, token.endOffset)
+ );
+ }
+ break;
+
+ case "symbol":
+ if (token.text === "(") {
+ ++parenDepth;
+ } else if (token.text === ")") {
+ --parenDepth;
+
+ if (colorFunctions.at(-1)?.parenDepth == parenDepth) {
+ colorFunctions.pop();
+ }
+
+ if (stopAtCloseParen && parenDepth === 0) {
+ done = true;
+ break;
+ }
+
+ if (parenDepth === 0) {
+ outerMostFunctionTakesColor = false;
+ }
+ } else if (
+ (token.text === "," || token.text === "!") &&
+ options.expectFont &&
+ fontFamilyNameParts.length !== 0
+ ) {
+ this.#appendFontFamily(fontFamilyNameParts.join(""), options);
+ fontFamilyNameParts = [];
+ }
+ // falls through
+ default:
+ this.#appendTextNode(
+ text.substring(token.startOffset, token.endOffset)
+ );
+ break;
+ }
+
+ // If this token might possibly introduce token pasting when
+ // color-cycling, require a space.
+ spaceNeeded =
+ token.tokenType === "ident" ||
+ token.tokenType === "at" ||
+ token.tokenType === "id" ||
+ token.tokenType === "hash" ||
+ token.tokenType === "number" ||
+ token.tokenType === "dimension" ||
+ token.tokenType === "percentage" ||
+ token.tokenType === "dimension";
+ previousWasBang = token.tokenType === "symbol" && token.text === "!";
+ }
+
+ if (options.expectFont && fontFamilyNameParts.length !== 0) {
+ this.#appendFontFamily(fontFamilyNameParts.join(""), options);
+ }
+
+ let result = this.#toDOM();
+
+ if (options.expectFilter && !options.filterSwatch) {
+ result = this.#wrapFilter(text, options, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Parse a string.
+ *
+ * @param {String} text
+ * Text to parse.
+ * @param {Object} [options]
+ * Options object. For valid options and default values see
+ * #mergeOptions().
+ * @return {DocumentFragment}
+ * A document fragment.
+ */
+ #parse(text, options = {}) {
+ text = text.trim();
+ this.#parsed.length = 0;
+
+ const tokenStream = getCSSLexer(text);
+ return this.#doParse(text, options, tokenStream, false);
+ }
+
+ /**
+ * Returns true if it's a "display: [inline-]flex" token.
+ *
+ * @param {String} text
+ * The parsed text.
+ * @param {Object} token
+ * The parsed token.
+ * @param {Object} options
+ * The options given to #parse.
+ */
+ #isDisplayFlex(text, token, options) {
+ return (
+ options.expectDisplay &&
+ (token.text === "flex" || token.text === "inline-flex")
+ );
+ }
+
+ /**
+ * Returns true if it's a "display: [inline-]grid" token.
+ *
+ * @param {String} text
+ * The parsed text.
+ * @param {Object} token
+ * The parsed token.
+ * @param {Object} options
+ * The options given to #parse.
+ */
+ #isDisplayGrid(text, token, options) {
+ return (
+ options.expectDisplay &&
+ (token.text === "grid" || token.text === "inline-grid")
+ );
+ }
+
+ /**
+ * Append a cubic-bezier timing function value to the output
+ *
+ * @param {String} bezier
+ * The cubic-bezier timing function
+ * @param {Object} options
+ * Options object. For valid options and default values see
+ * #mergeOptions()
+ */
+ #appendCubicBezier(bezier, options) {
+ const container = this.#createNode("span", {
+ "data-bezier": bezier,
+ });
+
+ if (options.bezierSwatchClass) {
+ const swatch = this.#createNode("span", {
+ class: options.bezierSwatchClass,
+ tabindex: "0",
+ role: "button",
+ });
+ container.appendChild(swatch);
+ }
+
+ const value = this.#createNode(
+ "span",
+ {
+ class: options.bezierClass,
+ },
+ bezier
+ );
+
+ container.appendChild(value);
+ this.#parsed.push(container);
+ }
+
+ #appendLinear(text, options) {
+ const container = this.#createNode("span", {
+ "data-linear": text,
+ });
+
+ if (options.linearEasingSwatchClass) {
+ const swatch = this.#createNode("span", {
+ class: options.linearEasingSwatchClass,
+ tabindex: "0",
+ role: "button",
+ "data-linear": text,
+ });
+ container.appendChild(swatch);
+ }
+
+ const value = this.#createNode(
+ "span",
+ {
+ class: options.linearEasingClass,
+ },
+ text
+ );
+
+ container.appendChild(value);
+ this.#parsed.push(container);
+ }
+
+ /**
+ * Append a Flexbox|Grid highlighter toggle icon next to the value in a
+ * "display: [inline-]flex" or "display: [inline-]grid" declaration.
+ *
+ * @param {String} text
+ * The text value to append
+ * @param {String} className
+ * The class name for the toggle span
+ */
+ #appendHighlighterToggle(text, className) {
+ const container = this.#createNode("span", {});
+
+ const toggle = this.#createNode("span", {
+ class: className,
+ });
+
+ const value = this.#createNode("span", {});
+ value.textContent = text;
+
+ container.appendChild(toggle);
+ container.appendChild(value);
+ this.#parsed.push(container);
+ }
+
+ /**
+ * Append a CSS shapes highlighter toggle next to the value, and parse the value
+ * into spans, each containing a point that can be hovered over.
+ *
+ * @param {String} shape
+ * The shape text value to append
+ * @param {Object} options
+ * Options object. For valid options and default values see
+ * #mergeOptions()
+ */
+ #appendShape(shape, options) {
+ const shapeTypes = [
+ {
+ prefix: "polygon(",
+ coordParser: this.#addPolygonPointNodes.bind(this),
+ },
+ {
+ prefix: "circle(",
+ coordParser: this.#addCirclePointNodes.bind(this),
+ },
+ {
+ prefix: "ellipse(",
+ coordParser: this.#addEllipsePointNodes.bind(this),
+ },
+ {
+ prefix: "inset(",
+ coordParser: this.#addInsetPointNodes.bind(this),
+ },
+ ];
+
+ const container = this.#createNode("span", {});
+
+ const toggle = this.#createNode("span", {
+ class: options.shapeSwatchClass,
+ tabindex: "0",
+ role: "button",
+ });
+
+ const lowerCaseShape = shape.toLowerCase();
+ for (const { prefix, coordParser } of shapeTypes) {
+ if (lowerCaseShape.includes(prefix)) {
+ const coordsBegin = prefix.length;
+ const coordsEnd = shape.lastIndexOf(")");
+ let valContainer = this.#createNode("span", {
+ class: options.shapeClass,
+ });
+
+ container.appendChild(toggle);
+
+ appendText(valContainer, shape.substring(0, coordsBegin));
+
+ const coordsString = shape.substring(coordsBegin, coordsEnd);
+ valContainer = coordParser(coordsString, valContainer);
+
+ appendText(valContainer, shape.substring(coordsEnd));
+ container.appendChild(valContainer);
+ }
+ }
+
+ this.#parsed.push(container);
+ }
+
+ /**
+ * Parse the given polygon coordinates and create a span for each coordinate pair,
+ * adding it to the given container node.
+ *
+ * @param {String} coords
+ * The string of coordinate pairs.
+ * @param {Node} container
+ * The node to which spans containing points are added.
+ * @returns {Node} The container to which spans have been added.
+ */
+ // eslint-disable-next-line complexity
+ #addPolygonPointNodes(coords, container) {
+ const tokenStream = getCSSLexer(coords);
+ let token = tokenStream.nextToken();
+ let coord = "";
+ let i = 0;
+ let depth = 0;
+ let isXCoord = true;
+ let fillRule = false;
+ let coordNode = this.#createNode("span", {
+ class: "ruleview-shape-point",
+ "data-point": `${i}`,
+ });
+
+ while (token) {
+ if (token.tokenType === "symbol" && token.text === ",") {
+ // Comma separating coordinate pairs; add coordNode to container and reset vars
+ if (!isXCoord) {
+ // Y coord not added to coordNode yet
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": `${i}`,
+ "data-pair": isXCoord ? "x" : "y",
+ },
+ coord
+ );
+ coordNode.appendChild(node);
+ coord = "";
+ isXCoord = !isXCoord;
+ }
+
+ if (fillRule) {
+ // If the last text added was a fill-rule, do not increment i.
+ fillRule = false;
+ } else {
+ container.appendChild(coordNode);
+ i++;
+ }
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ coord = "";
+ depth = 0;
+ isXCoord = true;
+ coordNode = this.#createNode("span", {
+ class: "ruleview-shape-point",
+ "data-point": `${i}`,
+ });
+ } else if (token.tokenType === "symbol" && token.text === "(") {
+ depth++;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "symbol" && token.text === ")") {
+ depth--;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "whitespace" && coord === "") {
+ // Whitespace at beginning of coord; add to container
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ } else if (token.tokenType === "whitespace" && depth === 0) {
+ // Whitespace signifying end of coord
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": `${i}`,
+ "data-pair": isXCoord ? "x" : "y",
+ },
+ coord
+ );
+ coordNode.appendChild(node);
+ appendText(
+ coordNode,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ coord = "";
+ isXCoord = !isXCoord;
+ } else if (
+ token.tokenType === "number" ||
+ token.tokenType === "dimension" ||
+ token.tokenType === "percentage" ||
+ token.tokenType === "function"
+ ) {
+ if (isXCoord && coord && depth === 0) {
+ // Whitespace is not necessary between x/y coords.
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": `${i}`,
+ "data-pair": "x",
+ },
+ coord
+ );
+ coordNode.appendChild(node);
+ isXCoord = false;
+ coord = "";
+ }
+
+ coord += coords.substring(token.startOffset, token.endOffset);
+ if (token.tokenType === "function") {
+ depth++;
+ }
+ } else if (
+ token.tokenType === "ident" &&
+ (token.text === "nonzero" || token.text === "evenodd")
+ ) {
+ // A fill-rule (nonzero or evenodd).
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ fillRule = true;
+ } else {
+ coord += coords.substring(token.startOffset, token.endOffset);
+ }
+ token = tokenStream.nextToken();
+ }
+
+ // Add coords if any are left over
+ if (coord) {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": `${i}`,
+ "data-pair": isXCoord ? "x" : "y",
+ },
+ coord
+ );
+ coordNode.appendChild(node);
+ container.appendChild(coordNode);
+ }
+ return container;
+ }
+
+ /**
+ * Parse the given circle coordinates and populate the given container appropriately
+ * with a separate span for the center point.
+ *
+ * @param {String} coords
+ * The circle definition.
+ * @param {Node} container
+ * The node to which the definition is added.
+ * @returns {Node} The container to which the definition has been added.
+ */
+ // eslint-disable-next-line complexity
+ #addCirclePointNodes(coords, container) {
+ const tokenStream = getCSSLexer(coords);
+ let token = tokenStream.nextToken();
+ let depth = 0;
+ let coord = "";
+ let point = "radius";
+ const centerNode = this.#createNode("span", {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ });
+ while (token) {
+ if (token.tokenType === "symbol" && token.text === "(") {
+ depth++;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "symbol" && token.text === ")") {
+ depth--;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "whitespace" && coord === "") {
+ // Whitespace at beginning of coord; add to container
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ } else if (
+ token.tokenType === "whitespace" &&
+ point === "radius" &&
+ depth === 0
+ ) {
+ // Whitespace signifying end of radius
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "radius",
+ },
+ coord
+ );
+ container.appendChild(node);
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ point = "cx";
+ coord = "";
+ depth = 0;
+ } else if (token.tokenType === "whitespace" && depth === 0) {
+ // Whitespace signifying end of cx/cy
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ "data-pair": point === "cx" ? "x" : "y",
+ },
+ coord
+ );
+ centerNode.appendChild(node);
+ appendText(
+ centerNode,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ point = point === "cx" ? "cy" : "cx";
+ coord = "";
+ depth = 0;
+ } else if (token.tokenType === "ident" && token.text === "at") {
+ // "at"; Add radius to container if not already done so
+ if (point === "radius" && coord) {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "radius",
+ },
+ coord
+ );
+ container.appendChild(node);
+ }
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ point = "cx";
+ coord = "";
+ depth = 0;
+ } else if (
+ token.tokenType === "number" ||
+ token.tokenType === "dimension" ||
+ token.tokenType === "percentage" ||
+ token.tokenType === "function"
+ ) {
+ if (point === "cx" && coord && depth === 0) {
+ // Center coords don't require whitespace between x/y. So if current point is
+ // cx, we have the cx coord, and depth is 0, then this token is actually cy.
+ // Add cx to centerNode and set point to cy.
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ "data-pair": "x",
+ },
+ coord
+ );
+ centerNode.appendChild(node);
+ point = "cy";
+ coord = "";
+ }
+
+ coord += coords.substring(token.startOffset, token.endOffset);
+ if (token.tokenType === "function") {
+ depth++;
+ }
+ } else {
+ coord += coords.substring(token.startOffset, token.endOffset);
+ }
+ token = tokenStream.nextToken();
+ }
+
+ // Add coords if any are left over.
+ if (coord) {
+ if (point === "radius") {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "radius",
+ },
+ coord
+ );
+ container.appendChild(node);
+ } else {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ "data-pair": point === "cx" ? "x" : "y",
+ },
+ coord
+ );
+ centerNode.appendChild(node);
+ }
+ }
+
+ if (centerNode.textContent) {
+ container.appendChild(centerNode);
+ }
+ return container;
+ }
+
+ /**
+ * Parse the given ellipse coordinates and populate the given container appropriately
+ * with a separate span for each point
+ *
+ * @param {String} coords
+ * The ellipse definition.
+ * @param {Node} container
+ * The node to which the definition is added.
+ * @returns {Node} The container to which the definition has been added.
+ */
+ // eslint-disable-next-line complexity
+ #addEllipsePointNodes(coords, container) {
+ const tokenStream = getCSSLexer(coords);
+ let token = tokenStream.nextToken();
+ let depth = 0;
+ let coord = "";
+ let point = "rx";
+ const centerNode = this.#createNode("span", {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ });
+ while (token) {
+ if (token.tokenType === "symbol" && token.text === "(") {
+ depth++;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "symbol" && token.text === ")") {
+ depth--;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "whitespace" && coord === "") {
+ // Whitespace at beginning of coord; add to container
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ } else if (token.tokenType === "whitespace" && depth === 0) {
+ if (point === "rx" || point === "ry") {
+ // Whitespace signifying end of rx/ry
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": point,
+ },
+ coord
+ );
+ container.appendChild(node);
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ point = point === "rx" ? "ry" : "cx";
+ coord = "";
+ depth = 0;
+ } else {
+ // Whitespace signifying end of cx/cy
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ "data-pair": point === "cx" ? "x" : "y",
+ },
+ coord
+ );
+ centerNode.appendChild(node);
+ appendText(
+ centerNode,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ point = point === "cx" ? "cy" : "cx";
+ coord = "";
+ depth = 0;
+ }
+ } else if (token.tokenType === "ident" && token.text === "at") {
+ // "at"; Add radius to container if not already done so
+ if (point === "ry" && coord) {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "ry",
+ },
+ coord
+ );
+ container.appendChild(node);
+ }
+ appendText(
+ container,
+ coords.substring(token.startOffset, token.endOffset)
+ );
+ point = "cx";
+ coord = "";
+ depth = 0;
+ } else if (
+ token.tokenType === "number" ||
+ token.tokenType === "dimension" ||
+ token.tokenType === "percentage" ||
+ token.tokenType === "function"
+ ) {
+ if (point === "rx" && coord && depth === 0) {
+ // Radius coords don't require whitespace between x/y.
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "rx",
+ },
+ coord
+ );
+ container.appendChild(node);
+ point = "ry";
+ coord = "";
+ }
+ if (point === "cx" && coord && depth === 0) {
+ // Center coords don't require whitespace between x/y.
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ "data-pair": "x",
+ },
+ coord
+ );
+ centerNode.appendChild(node);
+ point = "cy";
+ coord = "";
+ }
+
+ coord += coords.substring(token.startOffset, token.endOffset);
+ if (token.tokenType === "function") {
+ depth++;
+ }
+ } else {
+ coord += coords.substring(token.startOffset, token.endOffset);
+ }
+ token = tokenStream.nextToken();
+ }
+
+ // Add coords if any are left over.
+ if (coord) {
+ if (point === "rx" || point === "ry") {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": point,
+ },
+ coord
+ );
+ container.appendChild(node);
+ } else {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ "data-point": "center",
+ "data-pair": point === "cx" ? "x" : "y",
+ },
+ coord
+ );
+ centerNode.appendChild(node);
+ }
+ }
+
+ if (centerNode.textContent) {
+ container.appendChild(centerNode);
+ }
+ return container;
+ }
+
+ /**
+ * Parse the given inset coordinates and populate the given container appropriately.
+ *
+ * @param {String} coords
+ * The inset definition.
+ * @param {Node} container
+ * The node to which the definition is added.
+ * @returns {Node} The container to which the definition has been added.
+ */
+ // eslint-disable-next-line complexity
+ #addInsetPointNodes(coords, container) {
+ const insetPoints = ["top", "right", "bottom", "left"];
+ const tokenStream = getCSSLexer(coords);
+ let token = tokenStream.nextToken();
+ let depth = 0;
+ let coord = "";
+ let i = 0;
+ let round = false;
+ // nodes is an array containing all the coordinate spans. otherText is an array of
+ // arrays, each containing the text that should be inserted into container before
+ // the node with the same index. i.e. all elements of otherText[i] is inserted
+ // into container before nodes[i].
+ const nodes = [];
+ const otherText = [[]];
+
+ while (token) {
+ if (round) {
+ // Everything that comes after "round" should just be plain text
+ otherText[i].push(coords.substring(token.startOffset, token.endOffset));
+ } else if (token.tokenType === "symbol" && token.text === "(") {
+ depth++;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "symbol" && token.text === ")") {
+ depth--;
+ coord += coords.substring(token.startOffset, token.endOffset);
+ } else if (token.tokenType === "whitespace" && coord === "") {
+ // Whitespace at beginning of coord; add to container
+ otherText[i].push(coords.substring(token.startOffset, token.endOffset));
+ } else if (token.tokenType === "whitespace" && depth === 0) {
+ // Whitespace signifying end of coord; create node and push to nodes
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ },
+ coord
+ );
+ nodes.push(node);
+ i++;
+ coord = "";
+ otherText[i] = [coords.substring(token.startOffset, token.endOffset)];
+ depth = 0;
+ } else if (
+ token.tokenType === "number" ||
+ token.tokenType === "dimension" ||
+ token.tokenType === "percentage" ||
+ token.tokenType === "function"
+ ) {
+ if (coord && depth === 0) {
+ // Inset coords don't require whitespace between each coord.
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ },
+ coord
+ );
+ nodes.push(node);
+ i++;
+ coord = "";
+ otherText[i] = [];
+ }
+
+ coord += coords.substring(token.startOffset, token.endOffset);
+ if (token.tokenType === "function") {
+ depth++;
+ }
+ } else if (token.tokenType === "ident" && token.text === "round") {
+ if (coord && depth === 0) {
+ // Whitespace is not necessary before "round"; create a new node for the coord
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ },
+ coord
+ );
+ nodes.push(node);
+ i++;
+ coord = "";
+ otherText[i] = [];
+ }
+ round = true;
+ otherText[i].push(coords.substring(token.startOffset, token.endOffset));
+ } else {
+ coord += coords.substring(token.startOffset, token.endOffset);
+ }
+ token = tokenStream.nextToken();
+ }
+
+ // Take care of any leftover text
+ if (coord) {
+ if (round) {
+ otherText[i].push(coord);
+ } else {
+ const node = this.#createNode(
+ "span",
+ {
+ class: "ruleview-shape-point",
+ },
+ coord
+ );
+ nodes.push(node);
+ }
+ }
+
+ // insetPoints contains the 4 different possible inset points in the order they are
+ // defined. By taking the modulo of the index in insetPoints with the number of nodes,
+ // we can get which node represents each point (e.g. if there is only 1 node, it
+ // represents all 4 points). The exception is "left" when there are 3 nodes. In that
+ // case, it is nodes[1] that represents the left point rather than nodes[0].
+ for (let j = 0; j < 4; j++) {
+ const point = insetPoints[j];
+ const nodeIndex =
+ point === "left" && nodes.length === 3 ? 1 : j % nodes.length;
+ nodes[nodeIndex].classList.add(point);
+ }
+
+ nodes.forEach((node, j, array) => {
+ for (const text of otherText[j]) {
+ appendText(container, text);
+ }
+ container.appendChild(node);
+ });
+
+ // Add text that comes after the last node, if any exists
+ if (otherText[nodes.length]) {
+ for (const text of otherText[nodes.length]) {
+ appendText(container, text);
+ }
+ }
+
+ return container;
+ }
+
+ /**
+ * Append a angle value to the output
+ *
+ * @param {String} angle
+ * angle to append
+ * @param {Object} options
+ * Options object. For valid options and default values see
+ * #mergeOptions()
+ */
+ #appendAngle(angle, options) {
+ const angleObj = new angleUtils.CssAngle(angle);
+ const container = this.#createNode("span", {
+ "data-angle": angle,
+ });
+
+ if (options.angleSwatchClass) {
+ const swatch = this.#createNode("span", {
+ class: options.angleSwatchClass,
+ tabindex: "0",
+ role: "button",
+ });
+ this.#angleSwatches.set(swatch, angleObj);
+ swatch.addEventListener("mousedown", this.#onAngleSwatchMouseDown);
+
+ // Add click listener to stop event propagation when shift key is pressed
+ // in order to prevent the value input to be focused.
+ // Bug 711942 will add a tooltip to edit angle values and we should
+ // be able to move this listener to Tooltip.js when it'll be implemented.
+ swatch.addEventListener("click", function (event) {
+ if (event.shiftKey) {
+ event.stopPropagation();
+ }
+ });
+ container.appendChild(swatch);
+ }
+
+ const value = this.#createNode(
+ "span",
+ {
+ class: options.angleClass,
+ },
+ angle
+ );
+
+ container.appendChild(value);
+ this.#parsed.push(container);
+ }
+
+ /**
+ * Check if a CSS property supports a specific value.
+ *
+ * @param {String} name
+ * CSS Property name to check
+ * @param {String} value
+ * CSS Property value to check
+ */
+ #cssPropertySupportsValue(name, value) {
+ // Checking pair as a CSS declaration string to account for "!important" in value.
+ const declaration = `${name}:${value}`;
+ return this.#doc.defaultView.CSS.supports(declaration);
+ }
+
+ /**
+ * Tests if a given colorObject output by CssColor is valid for parsing.
+ * Valid means it's really a color, not any of the CssColor SPECIAL_VALUES
+ * except transparent
+ */
+ #isValidColor(colorObj) {
+ return (
+ colorObj.valid &&
+ (!colorObj.specialValue || colorObj.specialValue === "transparent")
+ );
+ }
+
+ /**
+ * Append a color to the output.
+ *
+ * @param {String} color
+ * Color to append
+ * @param {Object} [options]
+ * Options object. For valid options and default values see
+ * #mergeOptions().
+ */
+ #appendColor(color, options = {}) {
+ const colorObj = new colorUtils.CssColor(color);
+
+ if (this.#isValidColor(colorObj)) {
+ const container = this.#createNode("span", {
+ "data-color": color,
+ });
+
+ if (options.colorSwatchClass) {
+ let attributes = {
+ class: options.colorSwatchClass,
+ style: "background-color:" + color,
+ };
+
+ // Color swatches next to values trigger the color editor everywhere aside from
+ // the Computed panel where values are read-only.
+ if (!options.colorSwatchClass.startsWith("computed-")) {
+ attributes = { ...attributes, tabindex: "0", role: "button" };
+ }
+
+ // The swatch is a <span> instead of a <button> intentionally. See Bug 1597125.
+ // It is made keyboard accessible via `tabindex` and has keydown handlers
+ // attached for pressing SPACE and RETURN in SwatchBasedEditorTooltip.js
+ const swatch = this.#createNode("span", attributes);
+ this.#colorSwatches.set(swatch, colorObj);
+ if (options.colorFunction) {
+ swatch.dataset.colorFunction = options.colorFunction;
+ }
+ swatch.addEventListener("mousedown", this.#onColorSwatchMouseDown);
+ container.appendChild(swatch);
+ }
+
+ let colorUnit = options.defaultColorUnit;
+ if (!options.useDefaultColorUnit) {
+ // If we're not being asked to convert the color to the default color type
+ // specified by the user, then force the CssColor instance to be set to the type
+ // of the current color.
+ // Not having a type means that the default color type will be automatically used.
+ colorUnit = colorUtils.classifyColor(color);
+ }
+ color = colorObj.toString(colorUnit);
+ container.dataset.color = color;
+
+ // Next we create the markup to show the value of the property.
+ if (options.variableContainer) {
+ // If we are creating a color swatch for a CSS variable we simply reuse
+ // the markup created for the variableContainer.
+ if (options.colorClass) {
+ options.variableContainer.classList.add(options.colorClass);
+ }
+ container.appendChild(options.variableContainer);
+ } else {
+ // Otherwise we create a new element with the `color` as textContent.
+ const value = this.#createNode(
+ "span",
+ {
+ class: options.colorClass,
+ },
+ color
+ );
+
+ container.appendChild(value);
+ }
+
+ this.#parsed.push(container);
+ } else {
+ this.#appendTextNode(color);
+ }
+ }
+
+ /**
+ * Wrap some existing nodes in a filter editor.
+ *
+ * @param {String} filters
+ * The full text of the "filter" property.
+ * @param {object} options
+ * The options object passed to parseCssProperty().
+ * @param {object} nodes
+ * Nodes created by #toDOM().
+ *
+ * @returns {object}
+ * A new node that supplies a filter swatch and that wraps |nodes|.
+ */
+ #wrapFilter(filters, options, nodes) {
+ const container = this.#createNode("span", {
+ "data-filters": filters,
+ });
+
+ if (options.filterSwatchClass) {
+ const swatch = this.#createNode("span", {
+ class: options.filterSwatchClass,
+ tabindex: "0",
+ role: "button",
+ });
+ container.appendChild(swatch);
+ }
+
+ const value = this.#createNode("span", {
+ class: options.filterClass,
+ });
+ value.appendChild(nodes);
+ container.appendChild(value);
+
+ return container;
+ }
+
+ #onColorSwatchMouseDown = event => {
+ if (!event.shiftKey) {
+ return;
+ }
+
+ // Prevent click event to be fired to not show the tooltip
+ event.stopPropagation();
+
+ const swatch = event.target;
+ const color = this.#colorSwatches.get(swatch);
+ const val = color.nextColorUnit();
+
+ swatch.nextElementSibling.textContent = val;
+ swatch.parentNode.dataset.color = val;
+
+ const unitChangeEvent = new swatch.ownerGlobal.CustomEvent("unit-change");
+ swatch.dispatchEvent(unitChangeEvent);
+ };
+
+ #onAngleSwatchMouseDown = event => {
+ if (!event.shiftKey) {
+ return;
+ }
+
+ event.stopPropagation();
+
+ const swatch = event.target;
+ const angle = this.#angleSwatches.get(swatch);
+ const val = angle.nextAngleUnit();
+
+ swatch.nextElementSibling.textContent = val;
+
+ const unitChangeEvent = new swatch.ownerGlobal.CustomEvent("unit-change");
+ swatch.dispatchEvent(unitChangeEvent);
+ };
+
+ /**
+ * A helper function that sanitizes a possibly-unterminated URL.
+ */
+ #sanitizeURL(url) {
+ // Re-lex the URL and add any needed termination characters.
+ const urlTokenizer = getCSSLexer(url);
+ // Just read until EOF; there will only be a single token.
+ while (urlTokenizer.nextToken()) {
+ // Nothing.
+ }
+
+ return urlTokenizer.performEOFFixup(url, true);
+ }
+
+ /**
+ * Append a URL to the output.
+ *
+ * @param {String} match
+ * Complete match that may include "url(xxx)"
+ * @param {String} url
+ * Actual URL
+ * @param {Object} [options]
+ * Options object. For valid options and default values see
+ * #mergeOptions().
+ */
+ #appendURL(match, url, options) {
+ if (options.urlClass) {
+ // Sanitize the URL. Note that if we modify the URL, we just
+ // leave the termination characters. This isn't strictly
+ // "as-authored", but it makes a bit more sense.
+ match = this.#sanitizeURL(match);
+ // This regexp matches a URL token. It puts the "url(", any
+ // leading whitespace, and any opening quote into |leader|; the
+ // URL text itself into |body|, and any trailing quote, trailing
+ // whitespace, and the ")" into |trailer|. We considered adding
+ // functionality for this to CSSLexer, in some way, but this
+ // seemed simpler on the whole.
+ const urlParts =
+ /^(url\([ \t\r\n\f]*(["']?))(.*?)(\2[ \t\r\n\f]*\))$/i.exec(match);
+
+ // Bail out if that didn't match anything.
+ if (!urlParts) {
+ this.#appendTextNode(match);
+ return;
+ }
+
+ const [, leader, , body, trailer] = urlParts;
+
+ this.#appendTextNode(leader);
+
+ let href = url;
+ if (options.baseURI) {
+ try {
+ href = new URL(url, options.baseURI).href;
+ } catch (e) {
+ // Ignore.
+ }
+ }
+
+ this.#appendNode(
+ "a",
+ {
+ target: "_blank",
+ class: options.urlClass,
+ href,
+ },
+ body
+ );
+
+ this.#appendTextNode(trailer);
+ } else {
+ this.#appendTextNode(match);
+ }
+ }
+
+ /**
+ * Append a font family to the output.
+ *
+ * @param {String} fontFamily
+ * Font family to append
+ * @param {Object} options
+ * Options object. For valid options and default values see
+ * #mergeOptions().
+ */
+ #appendFontFamily(fontFamily, options) {
+ let spanContents = fontFamily;
+ let quoteChar = null;
+ let trailingWhitespace = false;
+
+ // Before appending the actual font-family span, we need to trim
+ // down the actual contents by removing any whitespace before and
+ // after, and any quotation characters in the passed string. Any
+ // such characters are preserved in the actual output, but just
+ // not inside the span element.
+
+ if (spanContents[0] === " ") {
+ this.#appendTextNode(" ");
+ spanContents = spanContents.slice(1);
+ }
+
+ if (spanContents[spanContents.length - 1] === " ") {
+ spanContents = spanContents.slice(0, -1);
+ trailingWhitespace = true;
+ }
+
+ if (spanContents[0] === "'" || spanContents[0] === '"') {
+ quoteChar = spanContents[0];
+ }
+
+ if (quoteChar) {
+ this.#appendTextNode(quoteChar);
+ spanContents = spanContents.slice(1, -1);
+ }
+
+ this.#appendNode(
+ "span",
+ {
+ class: options.fontFamilyClass,
+ },
+ spanContents
+ );
+
+ if (quoteChar) {
+ this.#appendTextNode(quoteChar);
+ }
+
+ if (trailingWhitespace) {
+ this.#appendTextNode(" ");
+ }
+ }
+
+ /**
+ * Create a node.
+ *
+ * @param {String} tagName
+ * Tag type e.g. "div"
+ * @param {Object} attributes
+ * e.g. {class: "someClass", style: "cursor:pointer"};
+ * @param {String} [value]
+ * If a value is included it will be appended as a text node inside
+ * the tag. This is useful e.g. for span tags.
+ * @return {Node} Newly created Node.
+ */
+ #createNode(tagName, attributes, value = "") {
+ const node = this.#doc.createElementNS(HTML_NS, tagName);
+ const attrs = Object.getOwnPropertyNames(attributes);
+
+ for (const attr of attrs) {
+ if (attributes[attr]) {
+ node.setAttribute(attr, attributes[attr]);
+ }
+ }
+
+ if (value) {
+ const textNode = this.#doc.createTextNode(value);
+ node.appendChild(textNode);
+ }
+
+ return node;
+ }
+
+ /**
+ * Append a node to the output.
+ *
+ * @param {String} tagName
+ * Tag type e.g. "div"
+ * @param {Object} attributes
+ * e.g. {class: "someClass", style: "cursor:pointer"};
+ * @param {String} [value]
+ * If a value is included it will be appended as a text node inside
+ * the tag. This is useful e.g. for span tags.
+ */
+ #appendNode(tagName, attributes, value = "") {
+ const node = this.#createNode(tagName, attributes, value);
+ if (value.length > TRUNCATE_LENGTH_THRESHOLD) {
+ node.classList.add(TRUNCATE_NODE_CLASSNAME);
+ }
+
+ this.#parsed.push(node);
+ }
+
+ /**
+ * Append a text node to the output. If the previously output item was a text
+ * node then we append the text to that node.
+ *
+ * @param {String} text
+ * Text to append
+ */
+ #appendTextNode(text) {
+ const lastItem = this.#parsed[this.#parsed.length - 1];
+ if (text.length > TRUNCATE_LENGTH_THRESHOLD) {
+ // If the text is too long, force creating a node, which will add the
+ // necessary classname to truncate the property correctly.
+ this.#appendNode("span", {}, text);
+ } else if (typeof lastItem === "string") {
+ this.#parsed[this.#parsed.length - 1] = lastItem + text;
+ } else {
+ this.#parsed.push(text);
+ }
+ }
+
+ /**
+ * Take all output and append it into a single DocumentFragment.
+ *
+ * @return {DocumentFragment}
+ * Document Fragment
+ */
+ #toDOM() {
+ const frag = this.#doc.createDocumentFragment();
+
+ for (const item of this.#parsed) {
+ if (typeof item === "string") {
+ frag.appendChild(this.#doc.createTextNode(item));
+ } else {
+ frag.appendChild(item);
+ }
+ }
+
+ this.#parsed.length = 0;
+ return frag;
+ }
+
+ /**
+ * Merges options objects. Default values are set here.
+ *
+ * @param {Object} overrides
+ * The option values to override e.g. #mergeOptions({colors: false})
+ * @param {Boolean} overrides.useDefaultColorUnit: Convert colors to the default type
+ * selected in the options panel.
+ * @param {String} overrides.angleClass: The class to use for the angle value that follows
+ * the swatch.
+ * @param {String} overrides.angleSwatchClass: The class to use for angle swatches.
+ * @param {} overrides.bezierClass: "" // The class to use for the bezier value
+ * // that follows the swatch.
+ * @param {} overrides.bezierSwatchClass: "" // The class to use for bezier swatches.
+ * @param {} overrides.colorClass: "" // The class to use for the color value
+ * // that follows the swatch.
+ * @param {} overrides.colorSwatchClass: "" // The class to use for color swatches.
+ * @param {} overrides.filterSwatch: false // A special case for parsing a
+ * // "filter" property, causing the
+ * // parser to skip the call to
+ * // #wrapFilter. Used only for
+ * // previewing with the filter swatch.
+ * @param {} overrides.flexClass: "" // The class to use for the flex icon.
+ * @param {} overrides.gridClass: "" // The class to use for the grid icon.
+ * @param {} overrides.shapeClass: "" // The class to use for the shape value
+ * // that follows the swatch.
+ * @param {} overrides.shapeSwatchClass: "" // The class to use for the shape swatch.
+ * @param {} overrides.supportsColor: false // Does the CSS property support colors?
+ * @param {} overrides.urlClass: "" // The class to be used for url() links.
+ * @param {} overrides.fontFamilyClass: "" // The class to be used for font families.
+ * @param {} overrides.baseURI: undefined // A string used to resolve
+ * // relative links.
+ * @param {} overrides.getVariableValue // A function taking a single
+ * // argument, the name of a variable.
+ * // This should return the variable's
+ * // value, if it is in use; or null.
+ * - unmatchedVariableClass: ""
+ * // The class to use for a component
+ * // of a "var(...)" that is not in
+ * // use.
+ * @return {Object}
+ * Overridden options object
+ */
+ #mergeOptions(overrides) {
+ const defaults = {
+ useDefaultColorUnit: true,
+ defaultColorUnit: "authored",
+ angleClass: "",
+ angleSwatchClass: "",
+ bezierClass: "",
+ bezierSwatchClass: "",
+ colorClass: "",
+ colorSwatchClass: "",
+ filterSwatch: false,
+ flexClass: "",
+ gridClass: "",
+ shapeClass: "",
+ shapeSwatchClass: "",
+ supportsColor: false,
+ urlClass: "",
+ fontFamilyClass: "",
+ baseURI: undefined,
+ getVariableValue: null,
+ unmatchedVariableClass: null,
+ };
+
+ for (const item in overrides) {
+ defaults[item] = overrides[item];
+ }
+ return defaults;
+ }
+}
+
+module.exports = OutputParser;
diff --git a/devtools/client/shared/prefs.js b/devtools/client/shared/prefs.js
new file mode 100644
index 0000000000..226038d5ec
--- /dev/null
+++ b/devtools/client/shared/prefs.js
@@ -0,0 +1,239 @@
+/* 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/. */
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+
+/**
+ * Shortcuts for lazily accessing and setting various preferences.
+ * Usage:
+ * let prefs = new Prefs("root.path.to.branch", {
+ * myIntPref: ["Int", "leaf.path.to.my-int-pref"],
+ * myCharPref: ["Char", "leaf.path.to.my-char-pref"],
+ * myJsonPref: ["Json", "leaf.path.to.my-json-pref"],
+ * myFloatPref: ["Float", "leaf.path.to.my-float-pref"]
+ * ...
+ * });
+ *
+ * Get/set:
+ * prefs.myCharPref = "foo";
+ * let aux = prefs.myCharPref;
+ *
+ * Observe:
+ * prefs.registerObserver();
+ * prefs.on("pref-changed", (prefValue) => {
+ * ...
+ * });
+ *
+ * @param string prefsRoot
+ * The root path to the required preferences branch.
+ * @param object prefsBlueprint
+ * An object containing { accessorName: [prefType, prefName] } keys.
+ */
+function PrefsHelper(prefsRoot = "", prefsBlueprint = {}) {
+ EventEmitter.decorate(this);
+
+ const cache = new Map();
+
+ for (const accessorName in prefsBlueprint) {
+ const [prefType, prefName, fallbackValue] = prefsBlueprint[accessorName];
+ map(
+ this,
+ cache,
+ accessorName,
+ prefType,
+ prefsRoot,
+ prefName,
+ fallbackValue
+ );
+ }
+
+ const observer = makeObserver(this, cache, prefsRoot, prefsBlueprint);
+ this.registerObserver = () => observer.register();
+ this.unregisterObserver = () => observer.unregister();
+}
+
+/**
+ * Helper method for getting a pref value.
+ *
+ * @param Map cache
+ * @param string prefType
+ * @param string prefsRoot
+ * @param string prefName
+ * @param string|int|boolean fallbackValue
+ * @return any
+ */
+function get(cache, prefType, prefsRoot, prefName, fallbackValue) {
+ const cachedPref = cache.get(prefName);
+ if (cachedPref !== undefined) {
+ return cachedPref;
+ }
+ const value = Services.prefs["get" + prefType + "Pref"](
+ [prefsRoot, prefName].join("."),
+ fallbackValue
+ );
+ cache.set(prefName, value);
+ return value;
+}
+
+/**
+ * Helper method for setting a pref value.
+ *
+ * @param Map cache
+ * @param string prefType
+ * @param string prefsRoot
+ * @param string prefName
+ * @param any value
+ */
+function set(cache, prefType, prefsRoot, prefName, value) {
+ Services.prefs["set" + prefType + "Pref"](
+ [prefsRoot, prefName].join("."),
+ value
+ );
+ cache.set(prefName, value);
+}
+
+/**
+ * Maps a property name to a pref, defining lazy getters and setters.
+ * Supported types are "Bool", "Char", "Int", "Float" (sugar around "Char"
+ * type and casting), and "Json" (which is basically just sugar for "Char"
+ * using the standard JSON serializer).
+ *
+ * @param PrefsHelper self
+ * @param Map cache
+ * @param string accessorName
+ * @param string prefType
+ * @param string prefsRoot
+ * @param string prefName
+ * @param string|int|boolean fallbackValue
+ * @param array serializer [optional]
+ */
+function map(
+ self,
+ cache,
+ accessorName,
+ prefType,
+ prefsRoot,
+ prefName,
+ fallbackValue,
+ serializer = { in: e => e, out: e => e }
+) {
+ if (prefName in self) {
+ throw new Error(
+ `Can't use ${prefName} because it overrides a property` +
+ "on the instance."
+ );
+ }
+ if (prefType == "Json") {
+ map(
+ self,
+ cache,
+ accessorName,
+ "String",
+ prefsRoot,
+ prefName,
+ fallbackValue,
+ {
+ in: JSON.parse,
+ out: JSON.stringify,
+ }
+ );
+ return;
+ }
+ if (prefType == "Float") {
+ map(self, cache, accessorName, "Char", prefsRoot, prefName, fallbackValue, {
+ in: Number.parseFloat,
+ out: n => n + "",
+ });
+ return;
+ }
+
+ Object.defineProperty(self, accessorName, {
+ get: () =>
+ serializer.in(get(cache, prefType, prefsRoot, prefName, fallbackValue)),
+ set: e => {
+ set(cache, prefType, prefsRoot, prefName, serializer.out(e));
+ },
+ });
+}
+
+/**
+ * Finds the accessor for the provided pref, based on the blueprint object
+ * used in the constructor.
+ *
+ * @param PrefsHelper self
+ * @param object prefsBlueprint
+ * @return string
+ */
+function accessorNameForPref(somePrefName, prefsBlueprint) {
+ for (const accessorName in prefsBlueprint) {
+ const [, prefName] = prefsBlueprint[accessorName];
+ if (somePrefName == prefName) {
+ return accessorName;
+ }
+ }
+ return "";
+}
+
+/**
+ * Creates a pref observer for `self`.
+ *
+ * @param PrefsHelper self
+ * @param Map cache
+ * @param string prefsRoot
+ * @param object prefsBlueprint
+ * @return object
+ */
+function makeObserver(self, cache, prefsRoot, prefsBlueprint) {
+ return {
+ register() {
+ this._branch = Services.prefs.getBranch(prefsRoot + ".");
+ this._branch.addObserver("", this);
+ },
+ unregister() {
+ this._branch.removeObserver("", this);
+ },
+ observe(subject, topic, prefName) {
+ // If this particular pref isn't handled by the blueprint object,
+ // even though it's in the specified branch, ignore it.
+ const accessorName = accessorNameForPref(prefName, prefsBlueprint);
+ if (!(accessorName in self)) {
+ return;
+ }
+ cache.delete(prefName);
+ self.emit("pref-changed", accessorName, self[accessorName]);
+ },
+ };
+}
+
+exports.PrefsHelper = PrefsHelper;
+
+/**
+ * A PreferenceObserver observes a pref branch for pref changes.
+ * It emits an event for each preference change.
+ */
+class PrefObserver extends EventEmitter {
+ constructor(branchName) {
+ super();
+
+ this.#branchName = branchName;
+ this.#branch = Services.prefs.getBranch(branchName);
+ this.#branch.addObserver("", this);
+ }
+
+ #branchName;
+ #branch;
+
+ observe(subject, topic, data) {
+ if (topic == "nsPref:changed") {
+ this.emit(this.#branchName + data);
+ }
+ }
+
+ destroy() {
+ this.#branch.removeObserver("", this);
+ }
+}
+
+exports.PrefObserver = PrefObserver;
diff --git a/devtools/client/shared/react-utils.js b/devtools/client/shared/react-utils.js
new file mode 100644
index 0000000000..f5bdfbc6bd
--- /dev/null
+++ b/devtools/client/shared/react-utils.js
@@ -0,0 +1,30 @@
+/* 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/. */
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ // Dependencies
+ const React = require("devtools/client/shared/vendor/react");
+
+ /**
+ * Create React factories for given arguments.
+ * Example:
+ * const {
+ * Tabs,
+ * TabPanel
+ * } = createFactories(require("devtools/client/shared/components/tabs/Tabs"));
+ */
+ function createFactories(args) {
+ const result = {};
+ for (const p in args) {
+ result[p] = React.createFactory(args[p]);
+ }
+ return result;
+ }
+
+ module.exports = {
+ createFactories,
+ };
+});
diff --git a/devtools/client/shared/redux/create-store.js b/devtools/client/shared/redux/create-store.js
new file mode 100644
index 0000000000..fb348d14dc
--- /dev/null
+++ b/devtools/client/shared/redux/create-store.js
@@ -0,0 +1,90 @@
+/* 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/. */
+"use strict";
+
+const {
+ combineReducers,
+ createStore,
+ applyMiddleware,
+} = require("resource://devtools/client/shared/vendor/redux.js");
+const {
+ thunk,
+} = require("resource://devtools/client/shared/redux/middleware/thunk.js");
+const {
+ waitUntilService,
+} = require("resource://devtools/client/shared/redux/middleware/wait-service.js");
+const {
+ task,
+} = require("resource://devtools/client/shared/redux/middleware/task.js");
+const {
+ promise,
+} = require("resource://devtools/client/shared/redux/middleware/promise.js");
+const flags = require("resource://devtools/shared/flags.js");
+
+loader.lazyRequireGetter(
+ this,
+ "log",
+ "resource://devtools/client/shared/redux/middleware/log.js",
+ true
+);
+
+/**
+ * This creates a dispatcher with all the standard middleware in place
+ * that all code requires. It can also be optionally configured in
+ * various ways, such as logging and recording.
+ *
+ * @param {object} opts:
+ * - enableTaskMiddleware: if true, include the task middleware
+ * - log: log all dispatched actions to console
+ * - middleware: array of middleware to be included in the redux store
+ * - thunkOptions: object that will be spread within a {dispatch, getState} object,
+ * that will be passed in each thunk action.
+ */
+const createStoreWithMiddleware = (opts = {}) => {
+ const middleware = [];
+ if (opts.enableTaskMiddleware) {
+ middleware.push(task);
+ }
+ middleware.push(
+ thunk(opts.thunkOptions),
+ promise,
+
+ // Order is important: services must go last as they always
+ // operate on "already transformed" actions. Actions going through
+ // them shouldn't have any special fields like promises, they
+ // should just be normal JSON objects.
+ waitUntilService
+ );
+
+ if (opts.middleware) {
+ opts.middleware.forEach(fn => middleware.push(fn));
+ }
+
+ if (opts.log) {
+ middleware.push(log);
+ }
+
+ return applyMiddleware(...middleware)(createStore);
+};
+
+module.exports = (
+ reducers,
+ {
+ shouldLog = false,
+ initialState = undefined,
+ thunkOptions,
+ enableTaskMiddleware = false,
+ } = {}
+) => {
+ const reducer =
+ typeof reducers === "function" ? reducers : combineReducers(reducers);
+
+ const store = createStoreWithMiddleware({
+ enableTaskMiddleware,
+ log: flags.testing && shouldLog,
+ thunkOptions,
+ })(reducer, initialState);
+
+ return store;
+};
diff --git a/devtools/client/shared/redux/middleware/debounce.js b/devtools/client/shared/redux/middleware/debounce.js
new file mode 100644
index 0000000000..fc5625a0fe
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/debounce.js
@@ -0,0 +1,100 @@
+/* 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/. */
+"use strict";
+
+/**
+ * Redux middleware for debouncing actions.
+ *
+ * Schedules actions with { meta: { debounce: true } } to be delayed
+ * by wait milliseconds. If another action is fired during this
+ * time-frame both actions are inserted into a queue and delayed.
+ * Maximum delay is defined by maxWait argument.
+ *
+ * Handling more actions at once results in better performance since
+ * components need to be re-rendered less often.
+ *
+ * @param string wait Wait for specified amount of milliseconds
+ * before executing an action. The time is used
+ * to collect more actions and handle them all
+ * at once.
+ * @param string maxWait Max waiting time. It's used in case of
+ * a long stream of actions.
+ */
+function debounceActions(wait, maxWait) {
+ let queuedActions = [];
+
+ return store => next => {
+ const debounced = debounce(
+ () => {
+ next(batchActions(queuedActions));
+ queuedActions = [];
+ },
+ wait,
+ maxWait
+ );
+
+ return action => {
+ if (!action.meta || !action.meta.debounce) {
+ return next(action);
+ }
+
+ if (!wait || !maxWait) {
+ return next(action);
+ }
+
+ if (action.type == BATCH_ACTIONS) {
+ queuedActions.push(...action.actions);
+ } else {
+ queuedActions.push(action);
+ }
+
+ return debounced();
+ };
+ };
+}
+
+function debounce(cb, wait, maxWait) {
+ let timeout, maxTimeout;
+ const doFunction = () => {
+ clearTimeout(timeout);
+ clearTimeout(maxTimeout);
+ timeout = maxTimeout = null;
+ cb();
+ };
+
+ return () => {
+ return new Promise(resolve => {
+ const onTimeout = () => {
+ doFunction();
+ resolve();
+ };
+
+ clearTimeout(timeout);
+
+ timeout = setTimeout(onTimeout, wait);
+ if (!maxTimeout) {
+ maxTimeout = setTimeout(onTimeout, maxWait);
+ }
+ });
+ };
+}
+
+const BATCH_ACTIONS = Symbol("BATCH_ACTIONS");
+
+/**
+ * Action creator for action-batching.
+ */
+function batchActions(batchedActions, debounceFlag = true) {
+ return {
+ type: BATCH_ACTIONS,
+ meta: { debounce: debounceFlag },
+ actions: batchedActions,
+ };
+}
+
+module.exports = {
+ BATCH_ACTIONS,
+ batchActions,
+ debounceActions,
+};
diff --git a/devtools/client/shared/redux/middleware/ignore.js b/devtools/client/shared/redux/middleware/ignore.js
new file mode 100644
index 0000000000..d2e8cd9e24
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/ignore.js
@@ -0,0 +1,38 @@
+/* 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/. */
+"use strict";
+
+const IGNORING = Symbol("IGNORING");
+const START_IGNORE_ACTION = "START_IGNORE_ACTION";
+
+/**
+ * A middleware that prevents any action of being called once it is activated.
+ * This is useful to apply while destroying a given panel, as it will ignore all calls
+ * to actions, where we usually make our client -> server communications.
+ * This middleware should be declared before any other middleware to to effectively
+ * ignore every actions.
+ */
+function ignore({ getState }) {
+ return next => action => {
+ if (action.type === START_IGNORE_ACTION) {
+ getState()[IGNORING] = true;
+ return null;
+ }
+
+ if (getState()[IGNORING]) {
+ // We only print the action type, and not the whole action object, as it can holds
+ // very complex data that would clutter stdout logs and make them impossible
+ // to parse for treeherder.
+ console.warn("IGNORED REDUX ACTION:", action.type);
+ return null;
+ }
+
+ return next(action);
+ };
+}
+
+module.exports = {
+ ignore,
+ START_IGNORE_ACTION: { type: START_IGNORE_ACTION },
+};
diff --git a/devtools/client/shared/redux/middleware/log.js b/devtools/client/shared/redux/middleware/log.js
new file mode 100644
index 0000000000..4ea09491ca
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/log.js
@@ -0,0 +1,31 @@
+/* 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/. */
+"use strict";
+
+/**
+ * A middleware that logs all actions coming through the system
+ * to the console.
+ */
+function log({ dispatch, getState }) {
+ return next => action => {
+ try {
+ // Only print the action type, rather than printing the whole object
+ console.log("[DISPATCH] action type:", action.type);
+ /*
+ * USE WITH CAUTION!! This will output everything from an action object,
+ * and these can be quite large. Printing out large objects will slow
+ * down tests and cause test failures
+ *
+ * console.log("[DISPATCH]", JSON.stringify(action, null, 2));
+ */
+ } catch (e) {
+ // this occurs if JSON.stringify throws.
+ console.warn(e);
+ console.log("[DISPATCH]", action);
+ }
+ next(action);
+ };
+}
+
+exports.log = log;
diff --git a/devtools/client/shared/redux/middleware/moz.build b/devtools/client/shared/redux/middleware/moz.build
new file mode 100644
index 0000000000..82de1417e6
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "debounce.js",
+ "ignore.js",
+ "log.js",
+ "performance-marker.js",
+ "promise.js",
+ "task.js",
+ "thunk.js",
+ "wait-service.js",
+)
+
+XPCSHELL_TESTS_MANIFESTS += ["xpcshell/xpcshell.toml"]
diff --git a/devtools/client/shared/redux/middleware/performance-marker.js b/devtools/client/shared/redux/middleware/performance-marker.js
new file mode 100644
index 0000000000..c829dffe3e
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/performance-marker.js
@@ -0,0 +1,68 @@
+/* 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/. */
+"use strict";
+
+/**
+ * This function returns a middleware, which is responsible for adding markers that will
+ * be visible in performance profiles, and may help investigate performance issues.
+ *
+ * Example usage, adding a marker when console messages are added, and when they are cleared:
+ *
+ * return createPerformanceMarkerMiddleware({
+ * "MESSAGES_ADD": {
+ * label: "WebconsoleAddMessages",
+ * sessionId: 12345,
+ * getMarkerDescription: function({ action, state }) {
+ * const { messages } = action;
+ * const totalMessageCount = state.messages.mutableMessagesById.size;
+ * return `${messages.length} messages handled, store now has ${totalMessageCount} messages`;
+ * },
+ * },
+ * "MESSAGES_CLEARED": {
+ * label: "WebconsoleClearMessages",
+ * sessionId: 12345
+ * },
+ * });
+ *
+ * @param {Object} cases: An object, keyed by action type, that will determine if a
+ * given action will add a marker.
+ * @param {String} cases.{actionType} - The type of the action that will trigger the
+ * marker creation.
+ * @param {String} cases.{actionType}.label - The marker label
+ * @param {Integer} cases.{actionType}.sessionId - The telemetry sessionId. This is used
+ * to be able to distinguish markers coming from different toolboxes.
+ * @param {Function} [cases.{actionType}.getMarkerDescription] - An optional function that
+ * will be called when adding the marker to populate its description. The function
+ * is called with an object holding the action and the state
+ */
+function createPerformanceMarkerMiddleware(cases) {
+ return function (store) {
+ return next => action => {
+ const condition = cases[action.type];
+ const shouldAddProfileMarker = !!condition;
+
+ // Start the marker timer before calling next(action).
+ const startTime = shouldAddProfileMarker ? Cu.now() : null;
+ const newState = next(action);
+
+ if (shouldAddProfileMarker) {
+ ChromeUtils.addProfilerMarker(
+ `${condition.label} ${condition.sessionId}`,
+ startTime,
+ condition.getMarkerDescription
+ ? condition.getMarkerDescription({
+ action,
+ state: store.getState(),
+ })
+ : ""
+ );
+ }
+ return newState;
+ };
+ };
+}
+
+module.exports = {
+ createPerformanceMarkerMiddleware,
+};
diff --git a/devtools/client/shared/redux/middleware/promise.js b/devtools/client/shared/redux/middleware/promise.js
new file mode 100644
index 0000000000..7f88651a61
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/promise.js
@@ -0,0 +1,69 @@
+/* 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/. */
+"use strict";
+
+loader.lazyRequireGetter(
+ this,
+ "generateUUID",
+ "resource://devtools/shared/generate-uuid.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ ["entries", "executeSoon", "toObject"],
+ "resource://devtools/shared/DevToolsUtils.js",
+ true
+);
+
+const PROMISE = (exports.PROMISE = "@@dispatch/promise");
+
+function promiseMiddleware({ dispatch, getState }) {
+ return next => action => {
+ if (!(PROMISE in action)) {
+ return next(action);
+ }
+ // Return the promise so action creators can still compose if they
+ // want to.
+ return new Promise((resolve, reject) => {
+ const promiseInst = action[PROMISE];
+ const seqId = generateUUID().toString();
+
+ // Create a new action that doesn't have the promise field and has
+ // the `seqId` field that represents the sequence id
+ action = Object.assign(
+ toObject(entries(action).filter(pair => pair[0] !== PROMISE)),
+ { seqId }
+ );
+
+ dispatch(Object.assign({}, action, { status: "start" }));
+
+ promiseInst.then(
+ value => {
+ executeSoon(() => {
+ dispatch(
+ Object.assign({}, action, {
+ status: "done",
+ value,
+ })
+ );
+ resolve(value);
+ });
+ },
+ error => {
+ executeSoon(() => {
+ dispatch(
+ Object.assign({}, action, {
+ status: "error",
+ error: error.message || error,
+ })
+ );
+ reject(error);
+ });
+ }
+ );
+ });
+ };
+}
+
+exports.promise = promiseMiddleware;
diff --git a/devtools/client/shared/redux/middleware/task.js b/devtools/client/shared/redux/middleware/task.js
new file mode 100644
index 0000000000..1ffb033589
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/task.js
@@ -0,0 +1,38 @@
+/* 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/. */
+"use strict";
+
+loader.lazyRequireGetter(
+ this,
+ ["executeSoon", "isAsyncFunction", "reportException"],
+ "resource://devtools/shared/DevToolsUtils.js",
+ true
+);
+
+const ERROR_TYPE = (exports.ERROR_TYPE = "@@redux/middleware/task#error");
+
+/**
+ * A middleware that allows async thunks (async functions) to be dispatched.
+ * The middleware is called "task" for historical reasons. TODO: rename?
+ */
+
+function task({ dispatch, getState }) {
+ return next => action => {
+ if (isAsyncFunction(action)) {
+ return action({ dispatch, getState }).catch(
+ handleError.bind(null, dispatch)
+ );
+ }
+ return next(action);
+ };
+}
+
+function handleError(dispatch, error) {
+ executeSoon(() => {
+ reportException(ERROR_TYPE, error);
+ dispatch({ type: ERROR_TYPE, error });
+ });
+}
+
+exports.task = task;
diff --git a/devtools/client/shared/redux/middleware/thunk.js b/devtools/client/shared/redux/middleware/thunk.js
new file mode 100644
index 0000000000..adcca77ede
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/thunk.js
@@ -0,0 +1,23 @@
+/* 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/. */
+"use strict";
+
+/**
+ * A middleware that allows thunks (functions) to be dispatched
+ * If it's a thunk, it is called with an argument that will be an object
+ * containing `dispatch` and `getState` properties, plus any additional properties
+ * defined in the `options` parameters.
+ * This allows the action to create multiple actions (most likely asynchronously).
+ */
+function thunk(options = {}) {
+ return function ({ dispatch, getState }) {
+ return next => action => {
+ return typeof action === "function"
+ ? action({ dispatch, getState, ...options })
+ : next(action);
+ };
+ };
+}
+
+exports.thunk = thunk;
diff --git a/devtools/client/shared/redux/middleware/wait-service.js b/devtools/client/shared/redux/middleware/wait-service.js
new file mode 100644
index 0000000000..9416fd22dd
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/wait-service.js
@@ -0,0 +1,64 @@
+/* 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/. */
+"use strict";
+
+/**
+ * A middleware which acts like a service, because it is stateful
+ * and "long-running" in the background. It provides the ability
+ * for actions to install a function to be run once when a specific
+ * condition is met by an action coming through the system. Think of
+ * it as a thunk that blocks until the condition is met. Example:
+ *
+ * ```js
+ * const services = { WAIT_UNTIL: require('wait-service').NAME };
+ *
+ * { type: services.WAIT_UNTIL,
+ * predicate: action => action.type === constants.ADD_ITEM,
+ * run: (dispatch, getState, action) => {
+ * // Do anything here. You only need to accept the arguments
+ * // if you need them. `action` is the action that satisfied
+ * // the predicate.
+ * }
+ * }
+ * ```
+ */
+const NAME = (exports.NAME = "@@service/waitUntil");
+
+function waitUntilService({ dispatch, getState }) {
+ let pending = [];
+
+ function checkPending(action) {
+ const readyRequests = [];
+ const stillPending = [];
+
+ // Find the pending requests whose predicates are satisfied with
+ // this action. Wait to run the requests until after we update the
+ // pending queue because the request handler may synchronously
+ // dispatch again and run this service (that use case is
+ // completely valid).
+ for (const request of pending) {
+ if (request.predicate(action)) {
+ readyRequests.push(request);
+ } else {
+ stillPending.push(request);
+ }
+ }
+
+ pending = stillPending;
+ for (const request of readyRequests) {
+ request.run(dispatch, getState, action);
+ }
+ }
+
+ return next => action => {
+ if (action.type === NAME) {
+ pending.push(action);
+ return null;
+ }
+ const result = next(action);
+ checkPending(action);
+ return result;
+ };
+}
+exports.waitUntilService = waitUntilService;
diff --git a/devtools/client/shared/redux/middleware/xpcshell/.eslintrc.js b/devtools/client/shared/redux/middleware/xpcshell/.eslintrc.js
new file mode 100644
index 0000000000..f69336917a
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/xpcshell/.eslintrc.js
@@ -0,0 +1,10 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ // Extend from the shared list of defined globals for mochitests.
+ extends: "../../../../../.eslintrc.xpcshell.js",
+};
diff --git a/devtools/client/shared/redux/middleware/xpcshell/head.js b/devtools/client/shared/redux/middleware/xpcshell/head.js
new file mode 100644
index 0000000000..9f4ba4392a
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/xpcshell/head.js
@@ -0,0 +1,26 @@
+/* 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/. */
+
+/* exported waitUntilState */
+
+"use strict";
+
+const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
+
+function waitUntilState(store, predicate) {
+ return new Promise(resolve => {
+ const unsubscribe = store.subscribe(check);
+ function check() {
+ if (predicate(store.getState())) {
+ unsubscribe();
+ resolve();
+ }
+ }
+
+ // Fire the check immediately incase the action has already occurred
+ check();
+ });
+}
diff --git a/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-01.js b/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-01.js
new file mode 100644
index 0000000000..d8516feecc
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-01.js
@@ -0,0 +1,66 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createStore,
+ applyMiddleware,
+} = require("resource://devtools/client/shared/vendor/redux.js");
+const {
+ task,
+} = require("resource://devtools/client/shared/redux/middleware/task.js");
+
+/**
+ * Tests that task middleware allows dispatching generators, promises and objects
+ * that return actions;
+ */
+add_task(async function () {
+ const store = applyMiddleware(task)(createStore)(reducer);
+
+ store.dispatch(fetch1("generator"));
+ await waitUntilState(store, () => store.getState().length === 1);
+ equal(
+ store.getState()[0].data,
+ "generator",
+ "task middleware async dispatches an action via generator"
+ );
+
+ store.dispatch(fetch2("sync"));
+ await waitUntilState(store, () => store.getState().length === 2);
+ equal(
+ store.getState()[1].data,
+ "sync",
+ "task middleware sync dispatches an action via sync"
+ );
+});
+
+function fetch1(data) {
+ return async function ({ dispatch, getState }) {
+ equal(
+ getState().length,
+ 0,
+ "`getState` is accessible in a generator action"
+ );
+ let moreData = await new Promise(resolve => resolve(data));
+ // Ensure it handles more than one yield
+ moreData = await new Promise(resolve => resolve(data));
+ dispatch({ type: "fetch1", data: moreData });
+ };
+}
+
+function fetch2(data) {
+ return {
+ type: "fetch2",
+ data,
+ };
+}
+
+function reducer(state = [], action) {
+ info("Action called: " + action.type);
+ if (["fetch1", "fetch2"].includes(action.type)) {
+ state.push(action);
+ }
+ return [...state];
+}
diff --git a/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-02.js b/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-02.js
new file mode 100644
index 0000000000..eaa573a8ae
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-02.js
@@ -0,0 +1,86 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Tests that task middleware allows dispatching generators that dispatch
+ * additional sync and async actions.
+ */
+
+const {
+ createStore,
+ applyMiddleware,
+} = require("resource://devtools/client/shared/vendor/redux.js");
+const {
+ task,
+} = require("resource://devtools/client/shared/redux/middleware/task.js");
+
+add_task(async function () {
+ const store = applyMiddleware(task)(createStore)(reducer);
+
+ store.dispatch(comboAction());
+ await waitUntilState(store, () => store.getState().length === 4);
+
+ equal(
+ store.getState()[0].type,
+ "fetchAsync-start",
+ "Async dispatched actions in a generator task are fired"
+ );
+ equal(
+ store.getState()[1].type,
+ "fetchAsync-end",
+ "Async dispatched actions in a generator task are fired"
+ );
+ equal(
+ store.getState()[2].type,
+ "fetchSync",
+ "Return values of yielded sync dispatched actions are correct"
+ );
+ equal(
+ store.getState()[3].type,
+ "fetch-done",
+ "Return values of yielded async dispatched actions are correct"
+ );
+ equal(
+ store.getState()[3].data.sync.data,
+ "sync",
+ "Return values of dispatched sync values are correct"
+ );
+ equal(
+ store.getState()[3].data.async,
+ "async",
+ "Return values of dispatched async values are correct"
+ );
+});
+
+function comboAction() {
+ return async function ({ dispatch, getState }) {
+ const data = {};
+ data.async = await dispatch(fetchAsync("async"));
+ data.sync = await dispatch(fetchSync("sync"));
+ dispatch({ type: "fetch-done", data });
+ };
+}
+
+function fetchSync(data) {
+ return { type: "fetchSync", data };
+}
+
+function fetchAsync(data) {
+ return async function ({ dispatch }) {
+ dispatch({ type: "fetchAsync-start" });
+ const val = await new Promise(resolve => resolve(data));
+ dispatch({ type: "fetchAsync-end" });
+ return val;
+ };
+}
+
+function reducer(state = [], action) {
+ info("Action called: " + action.type);
+ if (/fetch/.test(action.type)) {
+ state.push(action);
+ }
+ return [...state];
+}
diff --git a/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-03.js b/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-03.js
new file mode 100644
index 0000000000..94087e31de
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/xpcshell/test_middleware-task-03.js
@@ -0,0 +1,50 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createStore,
+ applyMiddleware,
+} = require("resource://devtools/client/shared/vendor/redux.js");
+const {
+ task,
+ ERROR_TYPE,
+} = require("resource://devtools/client/shared/redux/middleware/task.js");
+
+/**
+ * Tests that the middleware handles errors thrown in tasks, and rejected promises.
+ */
+
+add_task(async function () {
+ const store = applyMiddleware(task)(createStore)(reducer);
+
+ store.dispatch(asyncError());
+ await waitUntilState(store, () => store.getState().length === 1);
+ equal(
+ store.getState()[0].type,
+ ERROR_TYPE,
+ "generator errors dispatch ERROR_TYPE actions"
+ );
+ equal(
+ store.getState()[0].error,
+ "task-middleware-error-generator",
+ "generator errors dispatch ERROR_TYPE actions with error"
+ );
+});
+
+function asyncError() {
+ return async ({ dispatch, getState }) => {
+ const error = "task-middleware-error-generator";
+ throw error;
+ };
+}
+
+function reducer(state = [], action) {
+ info("Action called: " + action.type);
+ if (action.type === ERROR_TYPE) {
+ state.push(action);
+ }
+ return [...state];
+}
diff --git a/devtools/client/shared/redux/middleware/xpcshell/xpcshell.toml b/devtools/client/shared/redux/middleware/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..d1bb2822d3
--- /dev/null
+++ b/devtools/client/shared/redux/middleware/xpcshell/xpcshell.toml
@@ -0,0 +1,11 @@
+[DEFAULT]
+tags = "devtools"
+head = "head.js"
+firefox-appdir = "browser"
+skip-if = ["os == 'android'"]
+
+["test_middleware-task-01.js"]
+
+["test_middleware-task-02.js"]
+
+["test_middleware-task-03.js"]
diff --git a/devtools/client/shared/redux/moz.build b/devtools/client/shared/redux/moz.build
new file mode 100644
index 0000000000..d4c7bc503b
--- /dev/null
+++ b/devtools/client/shared/redux/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "middleware",
+]
+
+DevToolsModules(
+ "create-store.js",
+ "subscriber.js",
+ "visibility-handler-connect.js",
+)
diff --git a/devtools/client/shared/redux/subscriber.js b/devtools/client/shared/redux/subscriber.js
new file mode 100644
index 0000000000..39b0823770
--- /dev/null
+++ b/devtools/client/shared/redux/subscriber.js
@@ -0,0 +1,16 @@
+/* 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/>. */
+
+"use strict";
+
+function registerStoreObserver(store, subscriber) {
+ let oldState = store.getState();
+ store.subscribe(() => {
+ const state = store.getState();
+ subscriber(state, oldState);
+ oldState = state;
+ });
+}
+
+exports.registerStoreObserver = registerStoreObserver;
diff --git a/devtools/client/shared/redux/visibility-handler-connect.js b/devtools/client/shared/redux/visibility-handler-connect.js
new file mode 100644
index 0000000000..85835a1862
--- /dev/null
+++ b/devtools/client/shared/redux/visibility-handler-connect.js
@@ -0,0 +1,35 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ createFactory,
+ createElement,
+} = require("resource://devtools/client/shared/vendor/react.js");
+const VisibilityHandler = createFactory(
+ require("resource://devtools/client/shared/components/VisibilityHandler.js")
+);
+const {
+ connect,
+} = require("resource://devtools/client/shared/vendor/react-redux.js");
+
+/**
+ * This helper is wrapping Redux's connect() method and applying
+ * HOC (VisibilityHandler component) on whatever component is
+ * originally passed in. The HOC is responsible for not causing
+ * rendering if the owner panel runs in the background.
+ */
+function visibilityHandlerConnect() {
+ const args = [].slice.call(arguments);
+ return component => {
+ return connect(...args)(props => {
+ return VisibilityHandler(null, createElement(component, props));
+ });
+ };
+}
+
+module.exports = {
+ connect: visibilityHandlerConnect,
+};
diff --git a/devtools/client/shared/remote-debugging/adb/adb-addon.js b/devtools/client/shared/remote-debugging/adb/adb-addon.js
new file mode 100644
index 0000000000..ddce411cb3
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-addon.js
@@ -0,0 +1,186 @@
+/* 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/. */
+
+"use strict";
+
+const { AddonManager } = ChromeUtils.importESModule(
+ "resource://gre/modules/AddonManager.sys.mjs",
+ // AddonManager is a singleton, never create two instances of it.
+ { loadInDevToolsLoader: false }
+);
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+
+const PREF_ADB_EXTENSION_URL = "devtools.remote.adb.extensionURL";
+const PREF_ADB_EXTENSION_ID = "devtools.remote.adb.extensionID";
+
+const ADB_ADDON_STATES = {
+ DOWNLOADING: "downloading",
+ INSTALLED: "installed",
+ INSTALLING: "installing",
+ PREPARING: "preparing",
+ UNINSTALLED: "uninstalled",
+ UNKNOWN: "unknown",
+};
+exports.ADB_ADDON_STATES = ADB_ADDON_STATES;
+
+/**
+ * Wrapper around the ADB Extension providing ADB binaries for devtools remote debugging.
+ * Fires the following events:
+ * - "update": the status of the addon was updated
+ * - "failure": addon installation failed
+ * - "progress": addon download in progress
+ *
+ * AdbAddon::state can take any of the values from ADB_ADDON_STATES.
+ */
+class ADBAddon extends EventEmitter {
+ constructor() {
+ super();
+
+ this._status = ADB_ADDON_STATES.UNKNOWN;
+
+ const addonsListener = {};
+ addonsListener.onEnabled =
+ addonsListener.onDisabled =
+ addonsListener.onInstalled =
+ addonsListener.onUninstalled =
+ () => this.updateInstallStatus();
+ AddonManager.addAddonListener(addonsListener);
+
+ this.updateInstallStatus();
+ }
+
+ set status(value) {
+ if (this._status != value) {
+ this._status = value;
+ this.emit("update");
+ }
+ }
+
+ get status() {
+ return this._status;
+ }
+
+ async _getAddon() {
+ const addonId = Services.prefs.getCharPref(PREF_ADB_EXTENSION_ID);
+ return AddonManager.getAddonByID(addonId);
+ }
+
+ async updateInstallStatus() {
+ const addon = await this._getAddon();
+ if (addon && !addon.userDisabled) {
+ this.status = ADB_ADDON_STATES.INSTALLED;
+ } else {
+ this.status = ADB_ADDON_STATES.UNINSTALLED;
+ }
+ }
+
+ /**
+ * Returns the platform specific download link for the ADB extension.
+ */
+ _getXpiLink() {
+ const platform = Services.appShell.hiddenDOMWindow.navigator.platform;
+ let OS = "";
+ if (platform.includes("Win")) {
+ OS = "win32";
+ } else if (platform.includes("Mac")) {
+ OS = "mac64";
+ } else if (platform.includes("Linux")) {
+ if (platform.includes("x86_64")) {
+ OS = "linux64";
+ } else {
+ OS = "linux";
+ }
+ }
+
+ const xpiLink = Services.prefs.getCharPref(PREF_ADB_EXTENSION_URL);
+ return xpiLink.replace(/#OS#/g, OS);
+ }
+
+ /**
+ * Install and enable the adb extension. Returns a promise that resolves when ADB is
+ * enabled.
+ *
+ * @param {String} source
+ * String passed to the AddonManager for telemetry.
+ */
+ async install(source) {
+ if (!source) {
+ throw new Error(
+ "Missing mandatory `source` parameter for adb-addon.install"
+ );
+ }
+
+ const addon = await this._getAddon();
+ if (addon && !addon.userDisabled) {
+ this.status = ADB_ADDON_STATES.INSTALLED;
+ return;
+ }
+ this.status = ADB_ADDON_STATES.PREPARING;
+ if (addon?.userDisabled) {
+ await addon.enable();
+ } else {
+ const install = await AddonManager.getInstallForURL(this._getXpiLink(), {
+ telemetryInfo: { source },
+ });
+ install.addListener(this);
+ install.install();
+ }
+ }
+
+ async uninstall() {
+ const addon = await this._getAddon();
+ addon.uninstall();
+ }
+
+ installFailureHandler(install, message) {
+ this.status = ADB_ADDON_STATES.UNINSTALLED;
+ this.emit("failure", message);
+ }
+
+ // Expected AddonManager install listener.
+ onDownloadStarted() {
+ this.status = ADB_ADDON_STATES.DOWNLOADING;
+ }
+
+ // Expected AddonManager install listener.
+ onDownloadProgress(install) {
+ if (install.maxProgress == -1) {
+ this.emit("progress", -1);
+ } else {
+ this.emit("progress", install.progress / install.maxProgress);
+ }
+ }
+
+ // Expected AddonManager install listener.
+ onDownloadCancelled(install) {
+ this.installFailureHandler(install, "Download cancelled");
+ }
+
+ // Expected AddonManager install listener.
+ onDownloadFailed(install) {
+ this.installFailureHandler(install, "Download failed");
+ }
+
+ // Expected AddonManager install listener.
+ onInstallStarted() {
+ this.status = ADB_ADDON_STATES.INSTALLING;
+ }
+
+ // Expected AddonManager install listener.
+ onInstallCancelled(install) {
+ this.installFailureHandler(install, "Install cancelled");
+ }
+
+ // Expected AddonManager install listener.
+ onInstallFailed(install) {
+ this.installFailureHandler(install, "Install failed");
+ }
+
+ // Expected AddonManager install listener.
+ onInstallEnded({ addon }) {
+ addon.enable();
+ }
+}
+
+exports.adbAddon = new ADBAddon();
diff --git a/devtools/client/shared/remote-debugging/adb/adb-binary.js b/devtools/client/shared/remote-debugging/adb/adb-binary.js
new file mode 100644
index 0000000000..2621b62f45
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-binary.js
@@ -0,0 +1,240 @@
+/* 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/. */
+
+"use strict";
+
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
+ FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
+ NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
+});
+loader.lazyGetter(this, "UNPACKED_ROOT_PATH", () => {
+ return PathUtils.join(PathUtils.localProfileDir, "adb");
+});
+loader.lazyGetter(this, "EXTENSION_ID", () => {
+ return Services.prefs.getCharPref("devtools.remote.adb.extensionID");
+});
+loader.lazyGetter(this, "ADB_BINARY_PATH", () => {
+ let adbBinaryPath = PathUtils.join(UNPACKED_ROOT_PATH, "adb");
+ if (Services.appinfo.OS === "WINNT") {
+ adbBinaryPath += ".exe";
+ }
+ return adbBinaryPath;
+});
+
+const MANIFEST = "manifest.json";
+
+/**
+ * Read contents from a given uri in the devtools-adb-extension and parse the
+ * contents as JSON.
+ */
+async function readFromExtension(fileUri) {
+ return new Promise(resolve => {
+ lazy.NetUtil.asyncFetch(
+ {
+ uri: fileUri,
+ loadUsingSystemPrincipal: true,
+ },
+ input => {
+ try {
+ const string = lazy.NetUtil.readInputStreamToString(
+ input,
+ input.available()
+ );
+ resolve(JSON.parse(string));
+ } catch (e) {
+ dumpn(`Could not read ${fileUri} in the extension: ${e}`);
+ resolve(null);
+ }
+ }
+ );
+ });
+}
+
+/**
+ * Unpack file from the extension.
+ * Uses NetUtil to read and write, since it's required for reading.
+ *
+ * @param {string} file
+ * The path name of the file in the extension.
+ */
+async function unpackFile(file) {
+ const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
+ if (!policy) {
+ return;
+ }
+
+ // Assumes that destination dir already exists.
+ const basePath = file.substring(file.lastIndexOf("/") + 1);
+ const filePath = PathUtils.join(UNPACKED_ROOT_PATH, basePath);
+ await new Promise((resolve, reject) => {
+ lazy.NetUtil.asyncFetch(
+ {
+ uri: policy.getURL(file),
+ loadUsingSystemPrincipal: true,
+ },
+ input => {
+ try {
+ // Since we have to use NetUtil to read, probably it's okay to use for
+ // writing, rather than bouncing to IOUtils...?
+ const outputFile = new lazy.FileUtils.File(filePath);
+ const output = lazy.FileUtils.openAtomicFileOutputStream(outputFile);
+ lazy.NetUtil.asyncCopy(input, output, resolve);
+ } catch (e) {
+ dumpn(`Could not unpack file ${file} in the extension: ${e}`);
+ reject(e);
+ }
+ }
+ );
+ });
+ // Mark binaries as executable.
+ await IOUtils.setPermissions(filePath, 0o744);
+}
+
+/**
+ * Extract files in the extension into local profile directory and returns
+ * if it fails.
+ */
+async function extractFiles() {
+ const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
+ if (!policy) {
+ return false;
+ }
+ const uri = policy.getURL("adb.json");
+ const adbInfo = await readFromExtension(uri);
+ if (!adbInfo) {
+ return false;
+ }
+
+ let filesForAdb;
+ try {
+ // The adbInfo is an object looks like this;
+ //
+ // {
+ // "Linux": {
+ // "x86": [
+ // "linux/adb"
+ // ],
+ // "x86_64": [
+ // "linux64/adb"
+ // ]
+ // },
+ // ...
+
+ // XPCOMABI looks this; x86_64-gcc3, so drop the compiler name.
+ let architecture = Services.appinfo.XPCOMABI.split("-")[0];
+ if (architecture === "aarch64") {
+ // Fallback on x86 or x86_64 binaries for aarch64 - See Bug 1522149
+ const hasX86Binary = !!adbInfo[Services.appinfo.OS].x86;
+ architecture = hasX86Binary ? "x86" : "x86_64";
+ }
+ filesForAdb = adbInfo[Services.appinfo.OS][architecture];
+ } catch (e) {
+ return false;
+ }
+
+ // manifest.json isn't in adb.json but has to be unpacked for version
+ // comparison
+ filesForAdb.push(MANIFEST);
+
+ await IOUtils.makeDirectory(UNPACKED_ROOT_PATH);
+
+ for (const file of filesForAdb) {
+ try {
+ await unpackFile(file);
+ } catch (e) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Read the manifest from inside the devtools-adb-extension.
+ * Uses NetUtil since data is packed inside the extension, not a local file.
+ */
+async function getManifestFromExtension() {
+ const policy = lazy.ExtensionParent.WebExtensionPolicy.getByID(EXTENSION_ID);
+ if (!policy) {
+ return null;
+ }
+
+ const manifestUri = policy.getURL(MANIFEST);
+ return readFromExtension(manifestUri);
+}
+
+/**
+ * Returns whether manifest.json has already been unpacked.
+ */
+async function isManifestUnpacked() {
+ const manifestPath = PathUtils.join(UNPACKED_ROOT_PATH, MANIFEST);
+ return IOUtils.exists(manifestPath);
+}
+
+/**
+ * Read the manifest from the unpacked binary directory.
+ * Uses IOUtils since this is a local file.
+ */
+async function getManifestFromUnpacked() {
+ if (!(await isManifestUnpacked())) {
+ throw new Error("Manifest doesn't exist at unpacked path");
+ }
+
+ const manifestPath = PathUtils.join(UNPACKED_ROOT_PATH, MANIFEST);
+ const binary = await IOUtils.read(manifestPath);
+ const json = new TextDecoder().decode(binary);
+ let data;
+ try {
+ data = JSON.parse(json);
+ } catch (e) {}
+ return data;
+}
+
+/**
+ * Check state of binary unpacking, including the location and manifest.
+ */
+async function isUnpacked() {
+ if (!(await isManifestUnpacked())) {
+ dumpn("Needs unpacking, no manifest found");
+ return false;
+ }
+
+ const manifestInExtension = await getManifestFromExtension();
+ const unpackedManifest = await getManifestFromUnpacked();
+ if (manifestInExtension.version != unpackedManifest.version) {
+ dumpn(
+ `Needs unpacking, extension version ${manifestInExtension.version} != ` +
+ `unpacked version ${unpackedManifest.version}`
+ );
+ return false;
+ }
+ dumpn("Already unpacked");
+ return true;
+}
+
+/**
+ * Get a file object for the adb binary from the 'adb@mozilla.org' extension
+ * which has been already installed.
+ *
+ * @return {nsIFile}
+ * File object for the binary.
+ */
+async function getFileForBinary() {
+ if (!(await isUnpacked()) && !(await extractFiles())) {
+ return null;
+ }
+
+ const file = new lazy.FileUtils.File(ADB_BINARY_PATH);
+ if (!file.exists()) {
+ return null;
+ }
+ return file;
+}
+
+exports.getFileForBinary = getFileForBinary;
diff --git a/devtools/client/shared/remote-debugging/adb/adb-client.js b/devtools/client/shared/remote-debugging/adb/adb-client.js
new file mode 100644
index 0000000000..ff238d6392
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-client.js
@@ -0,0 +1,82 @@
+/* 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/. */
+
+/*
+ * A module to track device changes
+ * Adapted from adb.js at
+ * https://github.com/mozilla/adbhelper/tree/f44386c2d8cb7635a7d2c5a51191c89b886f8327
+ */
+
+"use strict";
+
+const {
+ AdbSocket,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-socket.js");
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+
+const OKAY = 0x59414b4f;
+const FAIL = 0x4c494146;
+
+// Return buffer, which differs between Gecko versions
+function getBuffer(packet) {
+ return packet.buffer ? packet.buffer : packet;
+}
+
+// @param aPacket The packet to get the length from.
+// @param aIgnoreResponse True if this packet has no OKAY/FAIL.
+// @return A js object { length:...; data:... }
+function unpackPacket(packet, ignoreResponse) {
+ const buffer = getBuffer(packet);
+ dumpn("Len buffer: " + buffer.byteLength);
+ if (buffer.byteLength === 4 && !ignoreResponse) {
+ dumpn("Packet empty");
+ return { length: 0, data: "" };
+ }
+ const lengthView = new Uint8Array(buffer, ignoreResponse ? 0 : 4, 4);
+ const decoder = new TextDecoder();
+ const length = parseInt(decoder.decode(lengthView), 16);
+ const text = new Uint8Array(buffer, ignoreResponse ? 4 : 8, length);
+ return { length, data: decoder.decode(text) };
+}
+
+// Checks if the response is expected (defaults to OKAY).
+// @return true if response equals expected.
+function checkResponse(packet, expected = OKAY) {
+ const buffer = getBuffer(packet);
+ const view = new Uint32Array(buffer, 0, 1);
+ if (view[0] == FAIL) {
+ dumpn("Response: FAIL");
+ }
+ dumpn("view[0] = " + view[0]);
+ return view[0] == expected;
+}
+
+// @param aCommand A protocol-level command as described in
+// http://androidxref.com/4.0.4/xref/system/core/adb/OVERVIEW.TXT and
+// http://androidxref.com/4.0.4/xref/system/core/adb/SERVICES.TXT
+// @return A 8 bit typed array.
+function createRequest(command) {
+ let length = command.length.toString(16).toUpperCase();
+ while (length.length < 4) {
+ length = "0" + length;
+ }
+
+ const encoder = new TextEncoder();
+ dumpn("Created request: " + length + command);
+ return encoder.encode(length + command);
+}
+
+function connect() {
+ return new AdbSocket();
+}
+
+const client = {
+ getBuffer,
+ unpackPacket,
+ checkResponse,
+ createRequest,
+ connect,
+};
+
+module.exports = client;
diff --git a/devtools/client/shared/remote-debugging/adb/adb-device.js b/devtools/client/shared/remote-debugging/adb/adb-device.js
new file mode 100644
index 0000000000..def22f27c3
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-device.js
@@ -0,0 +1,53 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ shell,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/index.js");
+
+/**
+ * A Device instance is created and registered with the Devices module whenever
+ * ADB notices a new device is connected.
+ */
+class AdbDevice {
+ constructor(id) {
+ this.id = id;
+ }
+
+ async initialize() {
+ const model = await shell(this.id, "getprop ro.product.model");
+ this.model = model.trim();
+ }
+
+ get name() {
+ return this.model || this.id;
+ }
+
+ async getRuntimeSocketPaths() {
+ // A matching entry looks like:
+ // 00000000: 00000002 00000000 00010000 0001 01 6551588
+ // /data/data/org.mozilla.fennec/firefox-debugger-socket
+ const query = "cat /proc/net/unix";
+ const rawSocketInfo = await shell(this.id, query);
+
+ // Filter to lines with "firefox-debugger-socket"
+ let socketInfos = rawSocketInfo.split(/\r?\n/);
+ socketInfos = socketInfos.filter(l =>
+ l.includes("firefox-debugger-socket")
+ );
+
+ // It's possible to have multiple lines with the same path, so de-dupe them
+ const socketPaths = new Set();
+ for (const socketInfo of socketInfos) {
+ const socketPath = socketInfo.split(" ").pop();
+ socketPaths.add(socketPath);
+ }
+
+ return socketPaths;
+ }
+}
+
+module.exports = AdbDevice;
diff --git a/devtools/client/shared/remote-debugging/adb/adb-process.js b/devtools/client/shared/remote-debugging/adb/adb-process.js
new file mode 100644
index 0000000000..ade509125e
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-process.js
@@ -0,0 +1,155 @@
+/* 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/. */
+
+"use strict";
+
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const {
+ getFileForBinary,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-binary.js");
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+loader.lazyRequireGetter(
+ this,
+ "runCommand",
+ "resource://devtools/client/shared/remote-debugging/adb/commands/index.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "check",
+ "resource://devtools/client/shared/remote-debugging/adb/adb-running-checker.js",
+ true
+);
+
+// Waits until a predicate returns true or re-tries the predicate calls
+// |retry| times, we wait for 100ms between each calls.
+async function waitUntil(predicate, retry = 20) {
+ let count = 0;
+ while (count++ < retry) {
+ if (await predicate()) {
+ return true;
+ }
+ // Wait for 100 milliseconds.
+ await new Promise(resolve => setTimeout(resolve, 100));
+ }
+ // Timed out after trying too many times.
+ return false;
+}
+
+// Class representing the ADB process.
+class AdbProcess extends EventEmitter {
+ constructor() {
+ super();
+
+ this._ready = false;
+ this._didRunInitially = false;
+ }
+
+ get ready() {
+ return this._ready;
+ }
+
+ _getAdbFile() {
+ if (this._adbFilePromise) {
+ return this._adbFilePromise;
+ }
+ this._adbFilePromise = getFileForBinary();
+ return this._adbFilePromise;
+ }
+
+ async _runProcess(process, params) {
+ return new Promise((resolve, reject) => {
+ process.runAsync(
+ params,
+ params.length,
+ {
+ observe(subject, topic, data) {
+ switch (topic) {
+ case "process-finished":
+ resolve();
+ break;
+ case "process-failed":
+ reject();
+ break;
+ }
+ },
+ },
+ false
+ );
+ });
+ }
+
+ // We startup by launching adb in server mode, and setting
+ // the tcp socket preference to |true|
+ async start() {
+ const onSuccessfulStart = () => {
+ this._ready = true;
+ this.emit("adb-ready");
+ };
+
+ const isAdbRunning = await check();
+ if (isAdbRunning) {
+ dumpn("Found ADB process running, not restarting");
+ onSuccessfulStart();
+ return;
+ }
+ dumpn("Didn't find ADB process running, restarting");
+
+ this._didRunInitially = true;
+ const process = Cc["@mozilla.org/process/util;1"].createInstance(
+ Ci.nsIProcess
+ );
+
+ // FIXME: Bug 1481691 - We should avoid extracting files every time.
+ const adbFile = await this._getAdbFile();
+ process.init(adbFile);
+ // Hide command prompt window on Windows
+ process.startHidden = true;
+ process.noShell = true;
+ const params = ["start-server"];
+ let isStarted = false;
+ try {
+ await this._runProcess(process, params);
+ isStarted = await waitUntil(check);
+ } catch (e) {}
+
+ if (isStarted) {
+ onSuccessfulStart();
+ } else {
+ this._ready = false;
+ throw new Error("ADB Process didn't start");
+ }
+ }
+
+ /**
+ * Stop the ADB server, but only if we started it. If it was started before
+ * us, we return immediately.
+ */
+ async stop() {
+ if (!this._didRunInitially) {
+ return; // We didn't start the server, nothing to do
+ }
+ await this.kill();
+ }
+
+ /**
+ * Kill the ADB server.
+ */
+ async kill() {
+ try {
+ await runCommand("host:kill");
+ } catch (e) {
+ dumpn("Failed to send host:kill command");
+ }
+ dumpn("adb server was terminated by host:kill");
+ this._ready = false;
+ this._didRunInitially = false;
+ }
+}
+
+exports.adbProcess = new AdbProcess();
diff --git a/devtools/client/shared/remote-debugging/adb/adb-running-checker.js b/devtools/client/shared/remote-debugging/adb/adb-running-checker.js
new file mode 100644
index 0000000000..7f952ca39b
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-running-checker.js
@@ -0,0 +1,91 @@
+/* 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/.
+ */
+
+/*
+ * Uses host:version service to detect if ADB is running
+ * Modified from adb-file-transfer from original ADB
+ */
+
+"use strict";
+
+const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+
+exports.check = async function check() {
+ let socket;
+ let state;
+ let timerID;
+ const TIMEOUT_TIME = 1000;
+
+ dumpn("Asking for host:version");
+
+ return new Promise(resolve => {
+ // On MacOSX connecting to a port which is not started listening gets
+ // stuck (bug 1481963), to avoid the stuck, we do forcibly fail the
+ // connection after |TIMEOUT_TIME| elapsed.
+ timerID = setTimeout(() => {
+ socket.close();
+ resolve(false);
+ }, TIMEOUT_TIME);
+
+ function finish(returnValue) {
+ clearTimeout(timerID);
+ resolve(returnValue);
+ }
+
+ const runFSM = function runFSM(packetData) {
+ dumpn("runFSM " + state);
+ switch (state) {
+ case "start":
+ const req = client.createRequest("host:version");
+ socket.send(req);
+ state = "wait-version";
+ break;
+ case "wait-version":
+ // TODO: Actually check the version number to make sure the daemon
+ // supports the commands we want to use
+ const { length, data } = client.unpackPacket(packetData);
+ dumpn("length: ", length, "data: ", data);
+ socket.close();
+ const version = parseInt(data, 16);
+ if (version >= 31) {
+ finish(true);
+ } else {
+ dumpn("killing existing adb as we need version >= 31");
+ finish(false);
+ }
+ break;
+ default:
+ dumpn("Unexpected State: " + state);
+ finish(false);
+ }
+ };
+
+ const setupSocket = function () {
+ socket.s.onerror = function (event) {
+ dumpn("running checker onerror");
+ finish(false);
+ };
+
+ socket.s.onopen = function (event) {
+ dumpn("running checker onopen");
+ state = "start";
+ runFSM();
+ };
+
+ socket.s.onclose = function (event) {
+ dumpn("running checker onclose");
+ };
+
+ socket.s.ondata = function (event) {
+ dumpn("running checker ondata");
+ runFSM(event.data);
+ };
+ };
+
+ socket = client.connect();
+ setupSocket();
+ });
+};
diff --git a/devtools/client/shared/remote-debugging/adb/adb-runtime.js b/devtools/client/shared/remote-debugging/adb/adb-runtime.js
new file mode 100644
index 0000000000..77d39d643e
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-runtime.js
@@ -0,0 +1,129 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ prepareTCPConnection,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/index.js");
+const {
+ shell,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/index.js");
+
+class AdbRuntime {
+ constructor(adbDevice, socketPath) {
+ this._adbDevice = adbDevice;
+ this._socketPath = socketPath;
+ // Set a default version name in case versionName cannot be parsed.
+ this._versionName = "";
+ }
+
+ async init() {
+ const packageName = this._packageName();
+ const query = `dumpsys package ${packageName} | grep versionName`;
+ const versionNameString = await shell(this._adbDevice.id, query);
+
+ // The versionName can have different formats depending on the channel
+ // - `versionName=Nightly 191016 06:01\n` on Nightly
+ // - `versionName=2.1.0\n` on Release
+ // We use a very flexible regular expression to accommodate for those
+ // different formats.
+ const matches = versionNameString.match(/versionName=(.*)\n/);
+ if (matches?.[1]) {
+ this._versionName = matches[1];
+ }
+ }
+
+ get id() {
+ return this._adbDevice.id + "|" + this._socketPath;
+ }
+
+ get isFenix() {
+ // Firefox Release uses "org.mozilla.firefox"
+ // Firefox Beta uses "org.mozilla.firefox_beta"
+ // Firefox Nightly uses "org.mozilla.fenix"
+ const isFirefox =
+ this._packageName().includes("org.mozilla.firefox") ||
+ this._packageName().includes("org.mozilla.fenix");
+
+ if (!isFirefox) {
+ return false;
+ }
+
+ // Firefox Release (based on Fenix) is not released in all regions yet, so
+ // we should still check for Fennec using the version number.
+ // Note that Fennec's versionName followed Firefox versions (eg "68.11.0").
+ // We can find the main version number in it. Fenix on the other hand has
+ // version names such as "Nightly 200730 06:21".
+ const mainVersion = Number(this.versionName.split(".")[0]);
+ const isFennec = mainVersion === 68;
+
+ // Application is Fenix if this is a Firefox application with a version
+ // different from the Fennec version.
+ return !isFennec;
+ }
+
+ get deviceId() {
+ return this._adbDevice.id;
+ }
+
+ get deviceName() {
+ return this._adbDevice.name;
+ }
+
+ get versionName() {
+ return this._versionName;
+ }
+
+ get shortName() {
+ const packageName = this._packageName();
+
+ switch (packageName) {
+ case "org.mozilla.firefox":
+ if (!this.isFenix) {
+ // Old Fennec release
+ return "Firefox (Fennec)";
+ }
+ // Official Firefox app, based on Fenix
+ return "Firefox";
+ case "org.mozilla.firefox_beta":
+ // Official Firefox Beta app, based on Fenix
+ return "Firefox Beta";
+ case "org.mozilla.fenix":
+ case "org.mozilla.fenix.nightly":
+ // Official Firefox Nightly app, based on Fenix
+ return "Firefox Nightly";
+ default:
+ // Unknown package name
+ return `Firefox (${packageName})`;
+ }
+ }
+
+ get socketPath() {
+ return this._socketPath;
+ }
+
+ get name() {
+ return `${this.shortName} on Android (${this.deviceName})`;
+ }
+
+ connect(connection) {
+ return prepareTCPConnection(this.deviceId, this._socketPath).then(port => {
+ connection.host = "localhost";
+ connection.port = port;
+ connection.connect();
+ });
+ }
+
+ _packageName() {
+ // If using abstract socket address, it is "@org.mozilla.firefox/..."
+ // If using path base socket, it is "/data/data/<package>...""
+ // Until Fennec 62 only supports path based UNIX domain socket, but
+ // Fennec 63+ supports both path based and abstract socket.
+ return this._socketPath.startsWith("@")
+ ? this._socketPath.substr(1).split("/")[0]
+ : this._socketPath.split("/")[3];
+ }
+}
+exports.AdbRuntime = AdbRuntime;
diff --git a/devtools/client/shared/remote-debugging/adb/adb-socket.js b/devtools/client/shared/remote-debugging/adb/adb-socket.js
new file mode 100644
index 0000000000..6b2a6d7a1c
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb-socket.js
@@ -0,0 +1,72 @@
+/* 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/. */
+
+"use strict";
+
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+
+function createTCPSocket(location, port, options) {
+ const { TCPSocket } = Cu.getGlobalForObject(
+ ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs")
+ );
+
+ return new TCPSocket(location, port, options);
+}
+
+// Creates a socket connected to the adb instance.
+// This instantiation is sync, and returns before we know if opening the
+// connection succeeds. Callers must attach handlers to the s field.
+class AdbSocket {
+ constructor() {
+ this.s = createTCPSocket("127.0.0.1", 5037, { binaryType: "arraybuffer" });
+ }
+
+ /**
+ * Dump the first few bytes of the given array to the console.
+ *
+ * @param {TypedArray} inputArray
+ * the array to dump
+ */
+ _hexdump(inputArray) {
+ const decoder = new TextDecoder("windows-1252");
+ const array = new Uint8Array(inputArray.buffer);
+ const s = decoder.decode(array);
+ const len = array.length;
+ let dbg = "len=" + len + " ";
+ const l = len > 20 ? 20 : len;
+
+ for (let i = 0; i < l; i++) {
+ let c = array[i].toString(16);
+ if (c.length == 1) {
+ c = "0" + c;
+ }
+ dbg += c;
+ }
+ dbg += " ";
+ for (let i = 0; i < l; i++) {
+ const c = array[i];
+ if (c < 32 || c > 127) {
+ dbg += ".";
+ } else {
+ dbg += s[i];
+ }
+ }
+ dumpn(dbg);
+ }
+
+ // debugging version of tcpsocket.send()
+ send(array) {
+ this._hexdump(array);
+
+ this.s.send(array.buffer, array.byteOffset, array.byteLength);
+ }
+
+ close() {
+ if (this.s.readyState === "open" || this.s.readyState === "connecting") {
+ this.s.close();
+ }
+ }
+}
+
+exports.AdbSocket = AdbSocket;
diff --git a/devtools/client/shared/remote-debugging/adb/adb.js b/devtools/client/shared/remote-debugging/adb/adb.js
new file mode 100644
index 0000000000..b4c2a9377f
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/adb.js
@@ -0,0 +1,176 @@
+/* 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/. */
+
+"use strict";
+
+const { clearInterval, setInterval } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const {
+ adbProcess,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
+const {
+ adbAddon,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-addon.js");
+const AdbDevice = require("resource://devtools/client/shared/remote-debugging/adb/adb-device.js");
+const {
+ AdbRuntime,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-runtime.js");
+const {
+ TrackDevicesCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/track-devices.js");
+loader.lazyRequireGetter(
+ this,
+ "check",
+ "resource://devtools/client/shared/remote-debugging/adb/adb-running-checker.js",
+ true
+);
+
+// Duration in milliseconds of the runtime polling. We resort to polling here because we
+// have no event to know when a runtime started on an already discovered ADB device.
+const UPDATE_RUNTIMES_INTERVAL = 3000;
+
+class Adb extends EventEmitter {
+ constructor() {
+ super();
+
+ this._trackDevicesCommand = new TrackDevicesCommand();
+
+ this._isTrackingDevices = false;
+ this._isUpdatingRuntimes = false;
+
+ this._listeners = new Set();
+ this._devices = new Map();
+ this._runtimes = [];
+
+ this._updateAdbProcess = this._updateAdbProcess.bind(this);
+ this._onDeviceConnected = this._onDeviceConnected.bind(this);
+ this._onDeviceDisconnected = this._onDeviceDisconnected.bind(this);
+ this._onNoDevicesDetected = this._onNoDevicesDetected.bind(this);
+
+ this._trackDevicesCommand.on("device-connected", this._onDeviceConnected);
+ this._trackDevicesCommand.on(
+ "device-disconnected",
+ this._onDeviceDisconnected
+ );
+ this._trackDevicesCommand.on(
+ "no-devices-detected",
+ this._onNoDevicesDetected
+ );
+ adbAddon.on("update", this._updateAdbProcess);
+ }
+
+ registerListener(listener) {
+ this._listeners.add(listener);
+ this.on("runtime-list-updated", listener);
+ this._updateAdbProcess();
+ }
+
+ unregisterListener(listener) {
+ this._listeners.delete(listener);
+ this.off("runtime-list-updated", listener);
+ this._updateAdbProcess();
+ }
+
+ async updateRuntimes() {
+ try {
+ const devices = [...this._devices.values()];
+ const promises = devices.map(d => this._getDeviceRuntimes(d));
+ const allRuntimes = await Promise.all(promises);
+
+ this._runtimes = allRuntimes.flat();
+ this.emit("runtime-list-updated");
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ getRuntimes() {
+ return this._runtimes;
+ }
+
+ getDevices() {
+ return [...this._devices.values()];
+ }
+
+ async isProcessStarted() {
+ return check();
+ }
+
+ async _startTracking() {
+ this._isTrackingDevices = true;
+ await adbProcess.start();
+
+ this._trackDevicesCommand.run();
+
+ // Device runtimes are detected by running a shell command and checking for
+ // "firefox-debugger-socket" in the list of currently running processes.
+ this._timer = setInterval(
+ this.updateRuntimes.bind(this),
+ UPDATE_RUNTIMES_INTERVAL
+ );
+ }
+
+ async _stopTracking() {
+ clearInterval(this._timer);
+ this._isTrackingDevices = false;
+ this._trackDevicesCommand.stop();
+
+ this._devices = new Map();
+ this._runtimes = [];
+ this.updateRuntimes();
+ }
+
+ _shouldTrack() {
+ return adbAddon.status === "installed" && this._listeners.size > 0;
+ }
+
+ /**
+ * This method will emit "runtime-list-ready" to notify the consumer that the list of
+ * runtimes is ready to be retrieved.
+ */
+ async _updateAdbProcess() {
+ if (!this._isTrackingDevices && this._shouldTrack()) {
+ const onRuntimesUpdated = this.once("runtime-list-updated");
+ this._startTracking();
+ // If we are starting to track runtimes, the list of runtimes will only be ready
+ // once the first "runtime-list-updated" event has been processed.
+ await onRuntimesUpdated;
+ } else if (this._isTrackingDevices && !this._shouldTrack()) {
+ this._stopTracking();
+ }
+ this.emit("runtime-list-ready");
+ }
+
+ async _onDeviceConnected(deviceId) {
+ const adbDevice = new AdbDevice(deviceId);
+ await adbDevice.initialize();
+ this._devices.set(deviceId, adbDevice);
+ this.updateRuntimes();
+ }
+
+ _onDeviceDisconnected(deviceId) {
+ this._devices.delete(deviceId);
+ this.updateRuntimes();
+ }
+
+ _onNoDevicesDetected() {
+ this.updateRuntimes();
+ }
+
+ async _getDeviceRuntimes(device) {
+ const socketPaths = [...(await device.getRuntimeSocketPaths())];
+ const runtimes = [];
+ for (const socketPath of socketPaths) {
+ const runtime = new AdbRuntime(device, socketPath);
+ await runtime.init();
+ runtimes.push(runtime);
+ }
+ return runtimes;
+ }
+}
+
+exports.adb = new Adb();
diff --git a/devtools/client/shared/remote-debugging/adb/commands/index.js b/devtools/client/shared/remote-debugging/adb/commands/index.js
new file mode 100644
index 0000000000..1032f1a4b8
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/index.js
@@ -0,0 +1,29 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ listDevices,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/list-devices.js");
+const {
+ prepareTCPConnection,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js");
+const {
+ runCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/run-command.js");
+const {
+ shell,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/shell.js");
+const {
+ TrackDevicesCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/track-devices.js");
+
+module.exports = {
+ listDevices,
+ prepareTCPConnection,
+ runCommand,
+ shell,
+ TrackDevicesCommand,
+};
diff --git a/devtools/client/shared/remote-debugging/adb/commands/list-devices.js b/devtools/client/shared/remote-debugging/adb/commands/list-devices.js
new file mode 100644
index 0000000000..c04ba9eb1e
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/list-devices.js
@@ -0,0 +1,29 @@
+/* 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/. */
+
+"use strict";
+
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+/**
+ * The listDevices command is currently unused in DevTools. We are keeping it while
+ * working on RemoteDebugging NG, in case it becomes needed later. We will remove it from
+ * the codebase if unused at the end of the project. See Bug 1511779.
+ */
+const listDevices = function () {
+ dumpn("listDevices");
+
+ return this.runCommand("host:devices").then(function onSuccess(data) {
+ const lines = data.split("\n");
+ const res = [];
+ lines.forEach(function (line) {
+ if (!line.length) {
+ return;
+ }
+ const [device] = line.split("\t");
+ res.push(device);
+ });
+ return res;
+ });
+};
+exports.listDevices = listDevices;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/moz.build b/devtools/client/shared/remote-debugging/adb/commands/moz.build
new file mode 100644
index 0000000000..ecd3428a1b
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/moz.build
@@ -0,0 +1,12 @@
+# 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/.
+
+DevToolsModules(
+ "index.js",
+ "list-devices.js",
+ "prepare-tcp-connection.js",
+ "run-command.js",
+ "shell.js",
+ "track-devices.js",
+)
diff --git a/devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js b/devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js
new file mode 100644
index 0000000000..dc9103f56b
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection.js
@@ -0,0 +1,46 @@
+/* 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/. */
+
+"use strict";
+
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+const {
+ runCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/run-command.js");
+
+// sends adb forward deviceId, localPort and devicePort
+const forwardPort = function (deviceId, localPort, devicePort) {
+ dumpn("forwardPort " + localPort + " -- " + devicePort);
+ // Send "host-serial:<serial-number>:<request>",
+ // with <request> set to "forward:<local>;<remote>"
+ // See https://android.googlesource.com/platform/system/core/+/jb-dev/adb/SERVICES.TXT
+ return runCommand(
+ `host-serial:${deviceId}:forward:${localPort};${devicePort}`
+ ).then(function onSuccess(data) {
+ return data;
+ });
+};
+
+const getFreeTCPPort = function () {
+ const serv = Cc["@mozilla.org/network/server-socket;1"].createInstance(
+ Ci.nsIServerSocket
+ );
+ serv.init(-1, true, -1);
+ const port = serv.port;
+ serv.close();
+ return port;
+};
+
+// Prepare TCP connection for provided device id and socket path.
+// The returned value is a port number of localhost for the connection.
+const prepareTCPConnection = async function (deviceId, socketPath) {
+ const port = getFreeTCPPort();
+ const local = `tcp:${port}`;
+ const remote = socketPath.startsWith("@")
+ ? `localabstract:${socketPath.substring(1)}`
+ : `localfilesystem:${socketPath}`;
+ await forwardPort(deviceId, local, remote);
+ return port;
+};
+exports.prepareTCPConnection = prepareTCPConnection;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/run-command.js b/devtools/client/shared/remote-debugging/adb/commands/run-command.js
new file mode 100644
index 0000000000..5fc521b5e7
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/run-command.js
@@ -0,0 +1,66 @@
+/* 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/. */
+
+// Wrapper around the ADB utility.
+
+"use strict";
+
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+const {
+ adbProcess,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
+const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
+
+const OKAY = 0x59414b4f;
+
+// Asynchronously runs an adb command.
+// @param command The command as documented in
+// http://androidxref.com/4.0.4/xref/system/core/adb/SERVICES.TXT
+const runCommand = function (command) {
+ dumpn("runCommand " + command);
+ return new Promise((resolve, reject) => {
+ if (!adbProcess.ready) {
+ setTimeout(function () {
+ reject("ADB_NOT_READY");
+ });
+ return;
+ }
+
+ const socket = client.connect();
+
+ socket.s.onopen = function () {
+ dumpn("runCommand onopen");
+ const req = client.createRequest(command);
+ socket.send(req);
+ };
+
+ socket.s.onerror = function () {
+ dumpn("runCommand onerror");
+ reject("NETWORK_ERROR");
+ };
+
+ socket.s.onclose = function () {
+ dumpn("runCommand onclose");
+ };
+
+ socket.s.ondata = function (event) {
+ dumpn("runCommand ondata");
+ const data = event.data;
+
+ const packet = client.unpackPacket(data, false);
+ if (!client.checkResponse(data, OKAY)) {
+ socket.close();
+ dumpn("Error: " + packet.data);
+ reject("PROTOCOL_ERROR");
+ return;
+ }
+
+ resolve(packet.data);
+ };
+ });
+};
+exports.runCommand = runCommand;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/shell.js b/devtools/client/shared/remote-debugging/adb/commands/shell.js
new file mode 100644
index 0000000000..03f4bfcf78
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/shell.js
@@ -0,0 +1,107 @@
+/* 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/. */
+
+// Wrapper around the ADB utility.
+
+"use strict";
+
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
+
+const OKAY = 0x59414b4f;
+
+const shell = async function (deviceId, command) {
+ if (!deviceId) {
+ throw new Error("ADB shell command needs the device id");
+ }
+
+ let state;
+ let stdout = "";
+
+ dumpn("shell " + command + " on " + deviceId);
+
+ return new Promise((resolve, reject) => {
+ const shutdown = function () {
+ dumpn("shell shutdown");
+ socket.close();
+ reject("BAD_RESPONSE");
+ };
+
+ const runFSM = function runFSM(data) {
+ dumpn("runFSM " + state);
+ let req;
+ let ignoreResponseCode = false;
+ switch (state) {
+ case "start":
+ state = "send-transport";
+ runFSM();
+ break;
+ case "send-transport":
+ req = client.createRequest("host:transport:" + deviceId);
+ socket.send(req);
+ state = "wait-transport";
+ break;
+ case "wait-transport":
+ if (!client.checkResponse(data, OKAY)) {
+ shutdown();
+ return;
+ }
+ state = "send-shell";
+ runFSM();
+ break;
+ case "send-shell":
+ req = client.createRequest("shell:" + command);
+ socket.send(req);
+ state = "rec-shell";
+ break;
+ case "rec-shell":
+ if (!client.checkResponse(data, OKAY)) {
+ shutdown();
+ return;
+ }
+ state = "decode-shell";
+ if (client.getBuffer(data).byteLength == 4) {
+ break;
+ }
+ ignoreResponseCode = true;
+ // eslint-disable-next-lined no-fallthrough
+ case "decode-shell":
+ const decoder = new TextDecoder();
+ const text = new Uint8Array(
+ client.getBuffer(data),
+ ignoreResponseCode ? 4 : 0
+ );
+ stdout += decoder.decode(text);
+ break;
+ default:
+ dumpn("shell Unexpected State: " + state);
+ reject("UNEXPECTED_STATE");
+ }
+ };
+
+ const socket = client.connect();
+ socket.s.onerror = function (event) {
+ dumpn("shell onerror");
+ reject("SOCKET_ERROR");
+ };
+
+ socket.s.onopen = function (event) {
+ dumpn("shell onopen");
+ state = "start";
+ runFSM();
+ };
+
+ socket.s.onclose = function (event) {
+ resolve(stdout);
+ dumpn("shell onclose");
+ };
+
+ socket.s.ondata = function (event) {
+ dumpn("shell ondata");
+ runFSM(event.data);
+ };
+ });
+};
+
+exports.shell = shell;
diff --git a/devtools/client/shared/remote-debugging/adb/commands/track-devices.js b/devtools/client/shared/remote-debugging/adb/commands/track-devices.js
new file mode 100644
index 0000000000..2d796668ea
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/commands/track-devices.js
@@ -0,0 +1,163 @@
+/* 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/. */
+
+// Wrapper around the ADB utility.
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const { dumpn } = require("resource://devtools/shared/DevToolsUtils.js");
+const { setTimeout } = ChromeUtils.importESModule(
+ "resource://gre/modules/Timer.sys.mjs"
+);
+
+const {
+ adbProcess,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
+const client = require("resource://devtools/client/shared/remote-debugging/adb/adb-client.js");
+
+const ADB_STATUS_OFFLINE = "offline";
+const OKAY = 0x59414b4f;
+
+// Start tracking devices connecting and disconnecting from the host.
+// We can't reuse runCommand here because we keep the socket alive.
+class TrackDevicesCommand extends EventEmitter {
+ run() {
+ this._waitForFirst = true;
+ // Hold device statuses. key: device id, value: status.
+ this._devices = new Map();
+ this._socket = client.connect();
+
+ this._socket.s.onopen = this._onOpen.bind(this);
+ this._socket.s.onerror = this._onError.bind(this);
+ this._socket.s.onclose = this._onClose.bind(this);
+ this._socket.s.ondata = this._onData.bind(this);
+ }
+
+ stop() {
+ if (this._socket) {
+ this._socket.close();
+
+ this._socket.s.onopen = null;
+ this._socket.s.onerror = null;
+ this._socket.s.onclose = null;
+ this._socket.s.ondata = null;
+ }
+ }
+
+ _onOpen() {
+ dumpn("trackDevices onopen");
+ const req = client.createRequest("host:track-devices");
+ this._socket.send(req);
+ }
+
+ _onError(event) {
+ dumpn("trackDevices onerror: " + event);
+ }
+
+ _onClose() {
+ dumpn("trackDevices onclose");
+
+ // Report all devices as disconnected
+ this._disconnectAllDevices();
+
+ // When we lose connection to the server,
+ // and the adb is still on, we most likely got our server killed
+ // by local adb. So we do try to reconnect to it.
+
+ // Give some time to the new adb to start
+ setTimeout(() => {
+ // Only try to reconnect/restart if the add-on is still enabled
+ if (adbProcess.ready) {
+ // try to connect to the new local adb server or spawn a new one
+ adbProcess.start().then(() => {
+ // Re-track devices
+ this.run();
+ });
+ }
+ }, 2000);
+ }
+
+ _onData(event) {
+ dumpn("trackDevices ondata");
+ const data = event.data;
+ dumpn("length=" + data.byteLength);
+ const dec = new TextDecoder();
+ dumpn(dec.decode(new Uint8Array(data)).trim());
+
+ // check the OKAY or FAIL on first packet.
+ if (this._waitForFirst) {
+ if (!client.checkResponse(data, OKAY)) {
+ this._socket.close();
+ return;
+ }
+ }
+
+ const packet = client.unpackPacket(data, !this._waitForFirst);
+ this._waitForFirst = false;
+
+ if (packet.data == "") {
+ // All devices got disconnected.
+ this._disconnectAllDevices();
+ } else {
+ // One line per device, each line being $DEVICE\t(offline|device)
+ const lines = packet.data.split("\n");
+ const newDevices = new Map();
+ lines.forEach(function (line) {
+ if (!line.length) {
+ return;
+ }
+
+ const [deviceId, status] = line.split("\t");
+ newDevices.set(deviceId, status);
+ });
+
+ // Fire events if needed.
+ const deviceIds = new Set([
+ ...this._devices.keys(),
+ ...newDevices.keys(),
+ ]);
+ for (const deviceId of deviceIds) {
+ const currentStatus = this._devices.get(deviceId);
+ const newStatus = newDevices.get(deviceId);
+ this._fireConnectionEventIfNeeded(deviceId, currentStatus, newStatus);
+ }
+
+ // Update devices.
+ this._devices = newDevices;
+ }
+ }
+
+ _disconnectAllDevices() {
+ if (this._devices.size === 0) {
+ // If no devices were detected, fire an event to let consumer resume.
+ this.emit("no-devices-detected");
+ } else {
+ for (const [deviceId, status] of this._devices.entries()) {
+ if (status !== ADB_STATUS_OFFLINE) {
+ this.emit("device-disconnected", deviceId);
+ }
+ }
+ }
+ this._devices = new Map();
+ }
+
+ _fireConnectionEventIfNeeded(deviceId, currentStatus, newStatus) {
+ const isCurrentOnline = !!(
+ currentStatus && currentStatus !== ADB_STATUS_OFFLINE
+ );
+ const isNewOnline = !!(newStatus && newStatus !== ADB_STATUS_OFFLINE);
+
+ if (isCurrentOnline === isNewOnline) {
+ return;
+ }
+
+ if (isNewOnline) {
+ this.emit("device-connected", deviceId);
+ } else {
+ this.emit("device-disconnected", deviceId);
+ }
+ }
+}
+exports.TrackDevicesCommand = TrackDevicesCommand;
diff --git a/devtools/client/shared/remote-debugging/adb/moz.build b/devtools/client/shared/remote-debugging/adb/moz.build
new file mode 100644
index 0000000000..ddc11068d3
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/moz.build
@@ -0,0 +1,24 @@
+# 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/.
+
+DIRS += [
+ "commands",
+]
+
+DevToolsModules(
+ "adb-addon.js",
+ "adb-binary.js",
+ "adb-client.js",
+ "adb-device.js",
+ "adb-process.js",
+ "adb-running-checker.js",
+ "adb-runtime.js",
+ "adb-socket.js",
+ "adb.js",
+)
+
+with Files("**"):
+ BUG_COMPONENT = ("DevTools", "about:debugging")
+
+XPCSHELL_TESTS_MANIFESTS += ["xpcshell/xpcshell.toml"]
diff --git a/devtools/client/shared/remote-debugging/adb/xpcshell/.eslintrc.js b/devtools/client/shared/remote-debugging/adb/xpcshell/.eslintrc.js
new file mode 100644
index 0000000000..e4da98dd14
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/xpcshell/.eslintrc.js
@@ -0,0 +1,9 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ extends: "../../../../../.eslintrc.xpcshell.js",
+};
diff --git a/devtools/client/shared/remote-debugging/adb/xpcshell/adb.py b/devtools/client/shared/remote-debugging/adb/xpcshell/adb.py
new file mode 100644
index 0000000000..4b720a1f86
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/xpcshell/adb.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+# 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/.
+
+"""
+A fake ADB binary
+"""
+
+import os
+import socketserver
+import sys
+
+HOST = "127.0.0.1"
+PORT = 5037
+
+
+class ADBRequestHandler(socketserver.BaseRequestHandler):
+ def sendData(self, data):
+ header = "OKAY%04x" % len(data)
+ all_data = header + data
+ total_length = len(all_data)
+ sent_length = 0
+ # Make sure send all data to the client.
+ # Though the data length here is pretty small but sometimes when the
+ # client is on heavy load (e.g. MOZ_CHAOSMODE) we can't send the whole
+ # data at once.
+ while sent_length < total_length:
+ sent = self.request.send(all_data[sent_length:].encode("utf-8", "replace"))
+ sent_length = sent_length + sent
+
+ def handle(self):
+ while True:
+ data = self.request.recv(4096).decode("utf-8", "replace")
+ if "host:kill" in data:
+ self.sendData("")
+ # Implicitly close all open sockets by exiting the program.
+ # This should be done ASAP, because upon receiving the OKAY,
+ # the client expects adb to have released the server's port.
+ os._exit(0)
+ break
+ elif "host:version" in data:
+ self.sendData("001F")
+ self.request.close()
+ break
+ elif "host:track-devices" in data:
+ self.sendData("1234567890\tdevice")
+ break
+
+
+class ADBServer(socketserver.TCPServer):
+ def __init__(self, server_address):
+ # Create a socketserver with bind_and_activate 'False' to set
+ # allow_reuse_address before binding.
+ socketserver.TCPServer.__init__(
+ self, server_address, ADBRequestHandler, bind_and_activate=False
+ )
+
+
+if len(sys.argv) == 2 and sys.argv[1] == "start-server":
+ # daemonize
+ if os.fork() > 0:
+ sys.exit(0)
+ os.setsid()
+ if os.fork() > 0:
+ sys.exit(0)
+
+ server = ADBServer((HOST, PORT))
+ server.allow_reuse_address = True
+ server.server_bind()
+ server.server_activate()
+ server.serve_forever()
diff --git a/devtools/client/shared/remote-debugging/adb/xpcshell/test_adb.js b/devtools/client/shared/remote-debugging/adb/xpcshell/test_adb.js
new file mode 100644
index 0000000000..dfbaab5a6b
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/xpcshell/test_adb.js
@@ -0,0 +1,247 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { ExtensionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/ExtensionXPCShellUtils.sys.mjs"
+);
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+const {
+ getFileForBinary,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-binary.js");
+const {
+ check,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-running-checker.js");
+const {
+ adbProcess,
+} = require("resource://devtools/client/shared/remote-debugging/adb/adb-process.js");
+const {
+ TrackDevicesCommand,
+} = require("resource://devtools/client/shared/remote-debugging/adb/commands/index.js");
+
+const ADB_JSON = {
+ Linux: {
+ x86: ["linux/adb"],
+ x86_64: ["linux64/adb"],
+ },
+ Darwin: {
+ x86_64: ["mac64/adb"],
+ },
+ WINNT: {
+ x86: ["win32/adb.exe", "win32/AdbWinApi.dll", "win32/AdbWinUsbApi.dll"],
+ x86_64: ["win32/adb.exe", "win32/AdbWinApi.dll", "win32/AdbWinUsbApi.dll"],
+ },
+};
+let extension_version = 1.0;
+
+ExtensionTestUtils.init(this);
+
+function readAdbMockContent() {
+ const adbMockFile = do_get_file("adb.py", false);
+ const s = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
+ Ci.nsIFileInputStream
+ );
+ s.init(adbMockFile, -1, -1, false);
+ try {
+ return NetUtil.readInputStreamToString(s, s.available());
+ } finally {
+ s.close();
+ }
+}
+
+const adbMock = readAdbMockContent();
+
+add_task(async function setup() {
+ // Prepare the profile directory where the adb extension will be installed.
+ do_get_profile();
+});
+
+add_task(async function testAdbIsNotRunningInitially() {
+ const isAdbRunning = await check();
+ // Assume that no adb server running.
+ ok(!isAdbRunning, "adb is not running initially");
+});
+
+add_task(async function testNoAdbExtension() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ version: (extension_version++).toString(),
+ browser_specific_settings: {
+ gecko: { id: "not-adb@mozilla.org" },
+ },
+ },
+ });
+
+ await extension.startup();
+
+ const adbBinary = await getFileForBinary();
+ equal(adbBinary, null);
+
+ await extension.unload();
+});
+
+add_task(async function testNoAdbJSON() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ version: (extension_version++).toString(),
+ browser_specific_settings: {
+ // The extension id here and in later test cases should match the
+ // corresponding prefrece value.
+ gecko: { id: "adb@mozilla.org" },
+ },
+ },
+ });
+
+ await extension.startup();
+
+ const adbBinary = await getFileForBinary();
+ equal(adbBinary, null);
+
+ await extension.unload();
+});
+
+add_task(async function testNoTargetBinaries() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ version: (extension_version++).toString(),
+ browser_specific_settings: {
+ gecko: { id: "adb@mozilla.org" },
+ },
+ },
+ files: {
+ "adb.json": JSON.stringify(ADB_JSON),
+ },
+ });
+
+ await extension.startup();
+
+ const adbBinary = await getFileForBinary();
+ equal(adbBinary, null);
+
+ await extension.unload();
+});
+
+add_task(async function testExtract() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ version: (extension_version++).toString(),
+ browser_specific_settings: {
+ gecko: { id: "adb@mozilla.org" },
+ },
+ },
+ files: {
+ "adb.json": JSON.stringify(ADB_JSON),
+ "linux/adb": "adb",
+ "linux64/adb": "adb",
+ "mac64/adb": "adb",
+ "win32/adb.exe": "adb.exe",
+ "win32/AdbWinApi.dll": "AdbWinApi.dll",
+ "win32/AdbWinUsbApi.dll": "AdbWinUsbApi.dll",
+ },
+ });
+
+ await extension.startup();
+
+ const adbBinary = await getFileForBinary();
+ ok(await adbBinary.exists());
+
+ await extension.unload();
+});
+
+add_task(
+ {
+ skip_if: () => mozinfo.os == "win", // bug 1482008
+ },
+ async function testStartAndStop() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ version: (extension_version++).toString(),
+ browser_specific_settings: {
+ gecko: { id: "adb@mozilla.org" },
+ },
+ },
+ files: {
+ "adb.json": JSON.stringify(ADB_JSON),
+ "linux/adb": adbMock,
+ "linux64/adb": adbMock,
+ "mac64/adb": adbMock,
+ "win32/adb.exe": adbMock,
+ "win32/AdbWinApi.dll": "dummy",
+ "win32/AdbWinUsbApi.dll": "dummy",
+ },
+ });
+
+ await extension.startup();
+
+ // Call start() once and call stop() afterwards.
+ await adbProcess.start();
+ ok(adbProcess.ready);
+ ok(await check(), "adb is now running");
+
+ await adbProcess.stop();
+ ok(!adbProcess.ready);
+ ok(!(await check()), "adb is no longer running");
+
+ // Call start() twice and call stop() afterwards.
+ await adbProcess.start();
+ await adbProcess.start();
+ ok(adbProcess.ready);
+ ok(await check(), "adb is now running");
+
+ await adbProcess.stop();
+ ok(!adbProcess.ready);
+ ok(!(await check()), "adb is no longer running");
+
+ await extension.unload();
+ }
+);
+
+add_task(
+ {
+ skip_if: () => mozinfo.os == "win", // bug 1482008
+ },
+ async function testTrackDevices() {
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ version: (extension_version++).toString(),
+ browser_specific_settings: {
+ gecko: { id: "adb@mozilla.org" },
+ },
+ },
+ files: {
+ "adb.json": JSON.stringify(ADB_JSON),
+ "linux/adb": adbMock,
+ "linux64/adb": adbMock,
+ "mac64/adb": adbMock,
+ "win32/adb.exe": adbMock,
+ "win32/AdbWinApi.dll": "dummy",
+ "win32/AdbWinUsbApi.dll": "dummy",
+ },
+ });
+
+ await extension.startup();
+
+ await adbProcess.start();
+ ok(adbProcess.ready);
+
+ ok(await check(), "adb is now running");
+
+ const receivedDeviceId = await new Promise(resolve => {
+ const trackDevicesCommand = new TrackDevicesCommand();
+ trackDevicesCommand.on("device-connected", deviceId => {
+ resolve(deviceId);
+ });
+ trackDevicesCommand.run();
+ });
+
+ equal(receivedDeviceId, "1234567890");
+
+ await adbProcess.stop();
+ ok(!adbProcess.ready);
+
+ await extension.unload();
+ }
+);
diff --git a/devtools/client/shared/remote-debugging/adb/xpcshell/test_prepare-tcp-connection.js b/devtools/client/shared/remote-debugging/adb/xpcshell/test_prepare-tcp-connection.js
new file mode 100644
index 0000000000..09dad165f2
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/xpcshell/test_prepare-tcp-connection.js
@@ -0,0 +1,78 @@
+"use strict";
+
+const DEVICE_ID = "FAKE_DEVICE_ID";
+const SOCKET_PATH = "fake/socket/path";
+
+/**
+ * Test the prepare-tcp-connection command in isolation.
+ */
+add_task(async function testParseFileUri() {
+ info("Enable devtools.testing for this test to allow mocked modules");
+ Services.prefs.setBoolPref("devtools.testing", true);
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.testing");
+ });
+
+ // Mocks are not supported for the regular DevTools loader.
+ info("Create a BrowserLoader to enable mocks in the test");
+ const { BrowserLoader } = ChromeUtils.import(
+ "resource://devtools/shared/loader/browser-loader.js"
+ );
+ const mockedRequire = BrowserLoader({
+ baseURI: "resource://devtools/client/shared/remote-debugging/adb",
+ window: {},
+ }).require;
+
+ // Prepare a mocked version of the run-command.js module to test
+ // prepare-tcp-connection in isolation.
+ info("Create a run-command module mock");
+ let createdPort;
+ const mock = {
+ runCommand: command => {
+ // Check that we only receive the expected command and extract the port
+ // number.
+
+ // The command should follow the pattern
+ // `host-serial:${deviceId}:forward:tcp:${localPort};${devicePort}`
+ // with devicePort = `localfilesystem:${socketPath}`
+ const socketPathRe = SOCKET_PATH.replace(/\//g, "\\/");
+ const regexp = new RegExp(
+ `host-serial:${DEVICE_ID}:forward:tcp:(\\d+);localfilesystem:${socketPathRe}`
+ );
+ const matches = regexp.exec(command);
+ equal(matches.length, 2, "The command is the expected");
+ createdPort = matches[1];
+
+ // Finally return a Promise-like object.
+ return {
+ then: () => {},
+ };
+ },
+ };
+
+ info("Enable the mocked run-command module");
+ const { setMockedModule, removeMockedModule } = mockedRequire(
+ "devtools/shared/loader/browser-loader-mocks"
+ );
+ setMockedModule(
+ mock,
+ "devtools/client/shared/remote-debugging/adb/commands/run-command"
+ );
+ registerCleanupFunction(() => {
+ removeMockedModule(
+ "devtools/client/shared/remote-debugging/adb/commands/run-command"
+ );
+ });
+
+ const { prepareTCPConnection } = mockedRequire(
+ "devtools/client/shared/remote-debugging/adb/commands/prepare-tcp-connection"
+ );
+
+ const port = await prepareTCPConnection(DEVICE_ID, SOCKET_PATH);
+ ok(!Number.isNaN(port), "prepareTCPConnection returned a valid port");
+ equal(
+ port,
+ Number(createdPort),
+ "prepareTCPConnection returned the port sent to the host-serial command"
+ );
+});
diff --git a/devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell-head.js b/devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell-head.js
new file mode 100644
index 0000000000..733c0400da
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell-head.js
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
diff --git a/devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell.toml b/devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..c31ffcecd2
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/adb/xpcshell/xpcshell.toml
@@ -0,0 +1,14 @@
+[DEFAULT]
+tags = "devtools"
+head = "xpcshell-head.js"
+firefox-appdir = "browser"
+skip-if = [
+ "os == 'android'",
+ "socketprocess_networking",
+]
+support-files = ["adb.py"]
+
+["test_adb.js"]
+run-sequentially = "An extension having the same id is installed/uninstalled in different tests"
+
+["test_prepare-tcp-connection.js"]
diff --git a/devtools/client/shared/remote-debugging/constants.js b/devtools/client/shared/remote-debugging/constants.js
new file mode 100644
index 0000000000..ad4d180548
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/constants.js
@@ -0,0 +1,24 @@
+/* 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/. */
+
+"use strict";
+
+const CONNECTION_TYPES = {
+ NETWORK: "network",
+ THIS_FIREFOX: "this-firefox",
+ UNKNOWN: "unknown",
+ USB: "usb",
+};
+
+const DEBUG_TARGET_TYPES = {
+ EXTENSION: "extension",
+ PROCESS: "process",
+ TAB: "tab",
+ WORKER: "worker",
+};
+
+module.exports = {
+ CONNECTION_TYPES,
+ DEBUG_TARGET_TYPES,
+};
diff --git a/devtools/client/shared/remote-debugging/moz.build b/devtools/client/shared/remote-debugging/moz.build
new file mode 100644
index 0000000000..b38de3744d
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/moz.build
@@ -0,0 +1,20 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "adb",
+]
+
+DevToolsModules(
+ "constants.js",
+ "remote-client-manager.js",
+ "version-checker.js",
+)
+
+XPCSHELL_TESTS_MANIFESTS += ["test/xpcshell/xpcshell.toml"]
+
+with Files("**"):
+ BUG_COMPONENT = ("DevTools", "about:debugging")
diff --git a/devtools/client/shared/remote-debugging/remote-client-manager.js b/devtools/client/shared/remote-debugging/remote-client-manager.js
new file mode 100644
index 0000000000..45e7a1be4a
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/remote-client-manager.js
@@ -0,0 +1,146 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ CONNECTION_TYPES,
+} = require("resource://devtools/client/shared/remote-debugging/constants.js");
+
+/**
+ * This class is designed to be a singleton shared by all DevTools to get access to
+ * existing clients created for remote debugging.
+ */
+class RemoteClientManager {
+ constructor() {
+ this._clients = new Map();
+ this._runtimeInfoMap = new Map();
+ this._onClientClosed = this._onClientClosed.bind(this);
+ }
+
+ /**
+ * Store a remote client that is already connected.
+ *
+ * @param {String} id
+ * Remote runtime id (see devtools/client/aboutdebugging/src/types).
+ * @param {String} type
+ * Remote runtime type (see devtools/client/aboutdebugging/src/types).
+ * @param {DevToolsClient} client
+ * @param {Object} runtimeInfo
+ * See runtimeInfo type from client/aboutdebugging/src/types/runtime.js
+ */
+ setClient(id, type, client, runtimeInfo) {
+ const key = this._getKey(id, type);
+ this._clients.set(key, client);
+ if (runtimeInfo) {
+ this._runtimeInfoMap.set(key, runtimeInfo);
+ }
+ client.once("closed", this._onClientClosed);
+ }
+
+ // See JSDoc for id, type from setClient.
+ hasClient(id, type) {
+ return this._clients.has(this._getKey(id, type));
+ }
+
+ // See JSDoc for id, type from setClient.
+ getClient(id, type) {
+ return this._clients.get(this._getKey(id, type));
+ }
+
+ // See JSDoc for id, type from setClient.
+ removeClient(id, type) {
+ const key = this._getKey(id, type);
+ this._removeClientByKey(key);
+ }
+
+ removeAllClients() {
+ const keys = [...this._clients.keys()];
+ for (const key of keys) {
+ this._removeClientByKey(key);
+ }
+ }
+
+ /**
+ * Retrieve a unique, url-safe key based on a runtime id and type.
+ */
+ getRemoteId(id, type) {
+ return encodeURIComponent(this._getKey(id, type));
+ }
+
+ /**
+ * Retrieve a managed client for a remote id. The remote id should have been generated
+ * using getRemoteId.
+ */
+ getClientByRemoteId(remoteId) {
+ const key = this._getKeyByRemoteId(remoteId);
+ return this._clients.get(key);
+ }
+
+ /**
+ * Retrieve the runtime info for a remote id. To display metadata about a runtime, such
+ * as name, device name, version... this runtimeInfo should be used rather than calling
+ * APIs on the client.
+ */
+ getRuntimeInfoByRemoteId(remoteId) {
+ const key = this._getKeyByRemoteId(remoteId);
+ return this._runtimeInfoMap.get(key);
+ }
+
+ /**
+ * Retrieve a managed client for a remote id. The remote id should have been generated
+ * using getRemoteId.
+ */
+ getConnectionTypeByRemoteId(remoteId) {
+ const key = this._getKeyByRemoteId(remoteId);
+ for (const type of Object.values(CONNECTION_TYPES)) {
+ if (key.endsWith(type)) {
+ return type;
+ }
+ }
+ return CONNECTION_TYPES.UNKNOWN;
+ }
+
+ _getKey(id, type) {
+ return id + "-" + type;
+ }
+
+ _getKeyByRemoteId(remoteId) {
+ if (!remoteId) {
+ // If no remote id was provided, return the key corresponding to the local
+ // this-firefox runtime.
+ const { THIS_FIREFOX } = CONNECTION_TYPES;
+ return this._getKey(THIS_FIREFOX, THIS_FIREFOX);
+ }
+
+ return decodeURIComponent(remoteId);
+ }
+
+ _removeClientByKey(key) {
+ const client = this._clients.get(key);
+ if (client) {
+ client.off("closed", this._onClientClosed);
+ this._clients.delete(key);
+ this._runtimeInfoMap.delete(key);
+ }
+ }
+
+ /**
+ * Cleanup all closed clients when a "closed" notification is received from a client.
+ */
+ _onClientClosed() {
+ const closedClientKeys = [...this._clients.keys()].filter(key => {
+ return this._clients.get(key)._transportClosed;
+ });
+
+ for (const key of closedClientKeys) {
+ this._removeClientByKey(key);
+ }
+ }
+}
+
+// Expose a singleton of RemoteClientManager.
+module.exports = {
+ remoteClientManager: new RemoteClientManager(),
+};
diff --git a/devtools/client/shared/remote-debugging/test/xpcshell/.eslintrc.js b/devtools/client/shared/remote-debugging/test/xpcshell/.eslintrc.js
new file mode 100644
index 0000000000..86bd54c245
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/test/xpcshell/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ // Extend from the common devtools xpcshell eslintrc config.
+ extends: "../../../../../.eslintrc.xpcshell.js",
+};
diff --git a/devtools/client/shared/remote-debugging/test/xpcshell/test_remote_client_manager.js b/devtools/client/shared/remote-debugging/test/xpcshell/test_remote_client_manager.js
new file mode 100644
index 0000000000..e66c38b67d
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/test/xpcshell/test_remote_client_manager.js
@@ -0,0 +1,153 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+ remoteClientManager,
+} = require("resource://devtools/client/shared/remote-debugging/remote-client-manager.js");
+const {
+ CONNECTION_TYPES,
+} = require("resource://devtools/client/shared/remote-debugging/constants.js");
+
+add_task(async function testRemoteClientManager() {
+ for (const type of Object.values(CONNECTION_TYPES)) {
+ const fakeClient = createFakeClient();
+ const runtimeInfo = {};
+ const clientId = "clientId";
+ const remoteId = remoteClientManager.getRemoteId(clientId, type);
+
+ const connectionType =
+ remoteClientManager.getConnectionTypeByRemoteId(remoteId);
+ equal(
+ connectionType,
+ type,
+ `[${type}]: Correct connection type was returned by getConnectionTypeByRemoteId`
+ );
+
+ equal(
+ remoteClientManager.hasClient(clientId, type),
+ false,
+ `[${type}]: hasClient returns false if no client was set`
+ );
+ equal(
+ remoteClientManager.getClient(clientId, type),
+ null,
+ `[${type}]: getClient returns null if no client was set`
+ );
+ equal(
+ remoteClientManager.getClientByRemoteId(remoteId),
+ null,
+ `[${type}]: getClientByRemoteId returns null if no client was set`
+ );
+ equal(
+ remoteClientManager.getRuntimeInfoByRemoteId(remoteId),
+ null,
+ `[${type}]: getRuntimeInfoByRemoteId returns null if no client was set`
+ );
+
+ remoteClientManager.setClient(clientId, type, fakeClient, runtimeInfo);
+ equal(
+ remoteClientManager.hasClient(clientId, type),
+ true,
+ `[${type}]: hasClient returns true`
+ );
+ equal(
+ remoteClientManager.getClient(clientId, type),
+ fakeClient,
+ `[${type}]: getClient returns the correct client`
+ );
+ equal(
+ remoteClientManager.getClientByRemoteId(remoteId),
+ fakeClient,
+ `[${type}]: getClientByRemoteId returns the correct client`
+ );
+ equal(
+ remoteClientManager.getRuntimeInfoByRemoteId(remoteId),
+ runtimeInfo,
+ `[${type}]: getRuntimeInfoByRemoteId returns the correct object`
+ );
+
+ remoteClientManager.removeClient(clientId, type);
+ equal(
+ remoteClientManager.hasClient(clientId, type),
+ false,
+ `[${type}]: hasClient returns false after removing the client`
+ );
+ equal(
+ remoteClientManager.getClient(clientId, type),
+ null,
+ `[${type}]: getClient returns null after removing the client`
+ );
+ equal(
+ remoteClientManager.getClientByRemoteId(remoteId),
+ null,
+ `[${type}]: getClientByRemoteId returns null after removing the client`
+ );
+ equal(
+ remoteClientManager.getRuntimeInfoByRemoteId(),
+ null,
+ `[${type}]: getRuntimeInfoByRemoteId returns null after removing the client`
+ );
+ }
+
+ // Test various fallback scenarios for APIs relying on remoteId, when called without a
+ // remoteId, we expect to get the information for the local this-firefox runtime.
+ const { THIS_FIREFOX } = CONNECTION_TYPES;
+ const thisFirefoxClient = createFakeClient();
+ const thisFirefoxInfo = {};
+ remoteClientManager.setClient(
+ THIS_FIREFOX,
+ THIS_FIREFOX,
+ thisFirefoxClient,
+ thisFirefoxInfo
+ );
+
+ equal(
+ remoteClientManager.getClientByRemoteId(),
+ thisFirefoxClient,
+ `[fallback]: getClientByRemoteId returns this-firefox if remoteId is null`
+ );
+ equal(
+ remoteClientManager.getRuntimeInfoByRemoteId(),
+ thisFirefoxInfo,
+ `[fallback]: getRuntimeInfoByRemoteId returns this-firefox if remoteId is null`
+ );
+
+ const otherRemoteId = remoteClientManager.getRemoteId(
+ "clientId",
+ CONNECTION_TYPES.USB
+ );
+ equal(
+ remoteClientManager.getClientByRemoteId(otherRemoteId),
+ null,
+ `[fallback]: getClientByRemoteId does not fallback if remoteId is non-null`
+ );
+ equal(
+ remoteClientManager.getRuntimeInfoByRemoteId(otherRemoteId),
+ null,
+ `[fallback]: getRuntimeInfoByRemoteId does not fallback if remoteId is non-null`
+ );
+});
+
+add_task(async function testRemoteClientManagerWithUnknownType() {
+ const remoteId = remoteClientManager.getRemoteId(
+ "someClientId",
+ "NotARealType"
+ );
+ const connectionType =
+ remoteClientManager.getConnectionTypeByRemoteId(remoteId);
+ equal(
+ connectionType,
+ CONNECTION_TYPES.UNKNOWN,
+ `Connection type UNKNOWN was returned by getConnectionTypeByRemoteId`
+ );
+});
+
+function createFakeClient() {
+ const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+
+ const client = {};
+ EventEmitter.decorate(client);
+ return client;
+}
diff --git a/devtools/client/shared/remote-debugging/test/xpcshell/test_version_checker.js b/devtools/client/shared/remote-debugging/test/xpcshell/test_version_checker.js
new file mode 100644
index 0000000000..92f7d54a1e
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/test/xpcshell/test_version_checker.js
@@ -0,0 +1,159 @@
+/* global equal */
+
+"use strict";
+
+const {
+ _compareVersionCompatibility,
+ checkVersionCompatibility,
+ COMPATIBILITY_STATUS,
+} = require("resource://devtools/client/shared/remote-debugging/version-checker.js");
+
+const TEST_DATA = [
+ {
+ description: "same build date and same version number",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190131000000",
+ runtimeVersion: "60.0",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description: "same build date and older version in range (-1)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190131000000",
+ runtimeVersion: "59.0",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description: "same build date and older version in range (-2)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190131000000",
+ runtimeVersion: "58.0",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description: "same build date and older version in range (-2 Nightly)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190131000000",
+ runtimeVersion: "58.0a1",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description: "same build date and older version out of range (-3)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190131000000",
+ runtimeVersion: "57.0",
+ expected: COMPATIBILITY_STATUS.TOO_OLD,
+ },
+ {
+ description: "same build date and newer version out of range (+1)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190131000000",
+ runtimeVersion: "61.0",
+ expected: COMPATIBILITY_STATUS.TOO_RECENT,
+ },
+ {
+ description: "same major version and build date in range (-10 days)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190121000000",
+ runtimeVersion: "60.0",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description: "same major version and build date in range (+2 days)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190202000000",
+ runtimeVersion: "60.0",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description: "same major version and build date out of range (+8 days)",
+ localBuildId: "20190131000000",
+ localVersion: "60.0",
+ runtimeBuildId: "20190208000000",
+ runtimeVersion: "60.0",
+ expected: COMPATIBILITY_STATUS.TOO_RECENT,
+ },
+ {
+ description:
+ "fennec 68 compatibility error not raised for 68 -> 68 Android",
+ localBuildId: "20190131000000",
+ localVersion: "68.0",
+ runtimeBuildId: "20190202000000",
+ runtimeVersion: "68.0",
+ runtimeOs: "Android",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description:
+ "fennec 68 compatibility error not raised for 70 -> 68 Android",
+ localBuildId: "20190131000000",
+ localVersion: "70.0",
+ runtimeBuildId: "20190202000000",
+ runtimeVersion: "68.0",
+ runtimeOs: "Android",
+ expected: COMPATIBILITY_STATUS.COMPATIBLE,
+ },
+ {
+ description: "fennec 68 compatibility error raised for 71 -> 68 Android",
+ localBuildId: "20190131000000",
+ localVersion: "71.0",
+ runtimeBuildId: "20190202000000",
+ runtimeVersion: "68.0",
+ runtimeOs: "Android",
+ expected: COMPATIBILITY_STATUS.TOO_OLD_FENNEC,
+ },
+ {
+ description:
+ "fennec 68 compatibility error not raised for 71 -> 68 non-Android",
+ localBuildId: "20190131000000",
+ localVersion: "71.0",
+ runtimeBuildId: "20190202000000",
+ runtimeVersion: "68.0",
+ runtimeOs: "NotAndroid",
+ expected: COMPATIBILITY_STATUS.TOO_OLD,
+ },
+];
+
+add_task(async function testVersionChecker() {
+ for (const testData of TEST_DATA) {
+ const localDescription = {
+ appbuildid: testData.localBuildId,
+ platformversion: testData.localVersion,
+ };
+
+ const runtimeDescription = {
+ appbuildid: testData.runtimeBuildId,
+ platformversion: testData.runtimeVersion,
+ os: testData.runtimeOs,
+ };
+
+ const report = _compareVersionCompatibility(
+ localDescription,
+ runtimeDescription
+ );
+ equal(
+ report.status,
+ testData.expected,
+ "Expected status for test: " + testData.description
+ );
+ }
+});
+
+add_task(async function testVersionCheckWithVeryOldClient() {
+ // Use an empty object as devtools client, calling any method on it will fail.
+ const emptyClient = {};
+ const report = await checkVersionCompatibility(emptyClient);
+ equal(
+ report.status,
+ COMPATIBILITY_STATUS.TOO_OLD,
+ "Report status too old if devtools client is not implementing expected interface"
+ );
+});
diff --git a/devtools/client/shared/remote-debugging/test/xpcshell/xpcshell-head.js b/devtools/client/shared/remote-debugging/test/xpcshell/xpcshell-head.js
new file mode 100644
index 0000000000..733c0400da
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/test/xpcshell/xpcshell-head.js
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
diff --git a/devtools/client/shared/remote-debugging/test/xpcshell/xpcshell.toml b/devtools/client/shared/remote-debugging/test/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..8485b021ab
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/test/xpcshell/xpcshell.toml
@@ -0,0 +1,9 @@
+[DEFAULT]
+tags = "devtools"
+head = "xpcshell-head.js"
+firefox-appdir = "browser"
+skip-if = ["os == 'android'"]
+
+["test_remote_client_manager.js"]
+
+["test_version_checker.js"]
diff --git a/devtools/client/shared/remote-debugging/version-checker.js b/devtools/client/shared/remote-debugging/version-checker.js
new file mode 100644
index 0000000000..7023a5bcd3
--- /dev/null
+++ b/devtools/client/shared/remote-debugging/version-checker.js
@@ -0,0 +1,154 @@
+/* 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/. */
+
+"use strict";
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+const MS_PER_DAY = 1000 * 60 * 60 * 24;
+
+const COMPATIBILITY_STATUS = {
+ COMPATIBLE: "compatible",
+ TOO_OLD: "too-old",
+ TOO_OLD_FENNEC: "too-old-fennec",
+ TOO_RECENT: "too-recent",
+};
+exports.COMPATIBILITY_STATUS = COMPATIBILITY_STATUS;
+
+function getDateFromBuildID(buildID) {
+ // Build IDs are a timestamp in the yyyyMMddHHmmss format.
+ // Extract the year, month and day information.
+ const fields = buildID.match(/(\d{4})(\d{2})(\d{2})/);
+ // Date expects 0 - 11 for months
+ const month = Number.parseInt(fields[2], 10) - 1;
+ return new Date(fields[1], month, fields[3]);
+}
+
+function getMajorVersion(platformVersion) {
+ // Retrieve the major platform version, i.e. if we are on Firefox 64.0a1, it will be 64.
+ return Number.parseInt(platformVersion.match(/\d+/)[0], 10);
+}
+
+/**
+ * Compute the minimum and maximum supported version for remote debugging for the provided
+ * version of Firefox. Backward compatibility policy for devtools supports at most 2
+ * versions older than the current version.
+ *
+ * @param {String} localVersion
+ * The version of the local Firefox instance, eg "67.0"
+ * @return {Object}
+ * - minVersion {String} the minimum supported version, eg "65.0a1"
+ * - maxVersion {String} the first unsupported version, eg "68.0a1"
+ */
+function computeMinMaxVersion(localVersion) {
+ // Retrieve the major platform version, i.e. if we are on Firefox 64.0a1, it will be 64.
+ const localMajorVersion = getMajorVersion(localVersion);
+
+ return {
+ // Define the minimum officially supported version of Firefox when connecting to a
+ // remote runtime. (Use ".0a1" to support the very first nightly version)
+ // This matches the release channel's version when we are on nightly,
+ // or 2 versions before when we are on other channels.
+ minVersion: localMajorVersion - 2 + ".0a1",
+ // The maximum version is the first excluded from the support range. That's why we
+ // increase the current version by 1 and use ".0a1" to point to the first Nightly.
+ // We do not support forward compatibility at all.
+ maxVersion: localMajorVersion + 1 + ".0a1",
+ };
+}
+
+/**
+ * Tells if the remote device is using a supported version of Firefox.
+ *
+ * @param {DevToolsClient} devToolsClient
+ * DevToolsClient instance connected to the target remote Firefox.
+ * @return Object with the following attributes:
+ * * String status, one of COMPATIBILITY_STATUS
+ * COMPATIBLE if the runtime is compatible,
+ * TOO_RECENT if the runtime uses a too recent version,
+ * TOO_OLD if the runtime uses a too old version.
+ * * String minVersion
+ * The minimum supported version.
+ * * String runtimeVersion
+ * The remote runtime version.
+ * * String localID
+ * Build ID of local runtime. A date with like this: YYYYMMDD.
+ * * String deviceID
+ * Build ID of remote runtime. A date with like this: YYYYMMDD.
+ */
+async function checkVersionCompatibility(devToolsClient) {
+ const localDescription = {
+ appbuildid: Services.appinfo.appBuildID,
+ platformversion: AppConstants.MOZ_APP_VERSION,
+ };
+
+ try {
+ const deviceFront = await devToolsClient.mainRoot.getFront("device");
+ const description = await deviceFront.getDescription();
+ return _compareVersionCompatibility(localDescription, description);
+ } catch (e) {
+ // If we failed to retrieve the device description, assume we are trying to connect to
+ // a really old version of Firefox.
+ const localVersion = localDescription.platformversion;
+ const { minVersion } = computeMinMaxVersion(localVersion);
+ return {
+ minVersion,
+ runtimeVersion: "<55",
+ status: COMPATIBILITY_STATUS.TOO_OLD,
+ };
+ }
+}
+exports.checkVersionCompatibility = checkVersionCompatibility;
+
+function _compareVersionCompatibility(localDescription, deviceDescription) {
+ const runtimeID = deviceDescription.appbuildid.substr(0, 8);
+ const localID = localDescription.appbuildid.substr(0, 8);
+
+ const runtimeDate = getDateFromBuildID(runtimeID);
+ const localDate = getDateFromBuildID(localID);
+
+ const runtimeVersion = deviceDescription.platformversion;
+ const localVersion = localDescription.platformversion;
+
+ const { minVersion, maxVersion } = computeMinMaxVersion(localVersion);
+ const isTooOld = Services.vc.compare(runtimeVersion, minVersion) < 0;
+ const isTooRecent = Services.vc.compare(runtimeVersion, maxVersion) >= 0;
+
+ const runtimeMajorVersion = getMajorVersion(runtimeVersion);
+ const localMajorVersion = getMajorVersion(localVersion);
+ const isSameMajorVersion = runtimeMajorVersion === localMajorVersion;
+
+ let status;
+ if (isTooOld) {
+ if (runtimeMajorVersion === 68 && deviceDescription.os === "Android") {
+ status = COMPATIBILITY_STATUS.TOO_OLD_FENNEC;
+ } else {
+ status = COMPATIBILITY_STATUS.TOO_OLD;
+ }
+ } else if (isTooRecent) {
+ status = COMPATIBILITY_STATUS.TOO_RECENT;
+ } else if (isSameMajorVersion && runtimeDate - localDate > 7 * MS_PER_DAY) {
+ // If both local and remote runtimes have the same major version, compare build dates.
+ // This check is useful for Gecko developers as we might introduce breaking changes
+ // within a Nightly cycle.
+ // Still allow devices to be newer by up to a week. This accommodates those with local
+ // device builds, since their devices will almost always be newer than the client.
+ status = COMPATIBILITY_STATUS.TOO_RECENT;
+ } else {
+ status = COMPATIBILITY_STATUS.COMPATIBLE;
+ }
+
+ return {
+ localID,
+ localVersion,
+ minVersion,
+ runtimeID,
+ runtimeVersion,
+ status,
+ };
+}
+// Exported for tests.
+exports._compareVersionCompatibility = _compareVersionCompatibility;
diff --git a/devtools/client/shared/screenshot.js b/devtools/client/shared/screenshot.js
new file mode 100644
index 0000000000..ca746f66e7
--- /dev/null
+++ b/devtools/client/shared/screenshot.js
@@ -0,0 +1,424 @@
+/* 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/. */
+
+"use strict";
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ Downloads: "resource://gre/modules/Downloads.sys.mjs",
+ FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
+ PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
+});
+
+const STRINGS_URI = "devtools/shared/locales/screenshot.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
+
+/**
+ * Take a screenshot of a browser element matching the passed target and save it to a file
+ * or the clipboard.
+ *
+ * @param {TargetFront} targetFront: The targetFront of the frame we want to take a screenshot of.
+ * @param {Window} window: The DevTools Client window.
+ * @param {Object} args
+ * @param {Boolean} args.fullpage: Should the screenshot be the height of the whole page
+ * @param {String} args.filename: Expected filename for the screenshot
+ * @param {Boolean} args.clipboard: Whether or not the screenshot should be saved to the clipboard.
+ * @param {Number} args.dpr: Scale of the screenshot. Defaults to the window `devicePixelRatio`.
+ * ⚠️ Note that the scale might be decreased if the resulting
+ * image would be too big to draw safely. Warning will be emitted
+ * to the console if that's the case.
+ * @param {Number} args.delay: Number of seconds to wait before taking the screenshot
+ * @param {Boolean} args.help: Set to true to receive a message with the screenshot command
+ * documentation.
+ * @param {Boolean} args.disableFlash: Set to true to disable the flash animation when the
+ * screenshot is taken.
+ * @param {Boolean} args.ignoreDprForFileScale: Set to true to if the resulting screenshot
+ * file size shouldn't be impacted by the dpr. Note that the dpr will still
+ * be taken into account when taking the screenshot, only the size of the
+ * file will be different.
+ * @returns {Array<Object{text, level}>} An array of object representing the different
+ * messages emitted throught the process, that should be displayed to the user.
+ */
+async function captureAndSaveScreenshot(targetFront, window, args = {}) {
+ if (args.help) {
+ // Wrap message in an array so that the return value is consistant.
+ return [{ text: getFormattedHelpData() }];
+ }
+
+ const captureResponse = await captureScreenshot(targetFront, args);
+
+ if (captureResponse.error) {
+ return captureResponse.messages || [];
+ }
+
+ const saveMessages = await saveScreenshot(window, args, captureResponse);
+ return (captureResponse.messages || []).concat(saveMessages);
+}
+
+/**
+ * Take a screenshot of a browser element matching the passed target
+ * @param {TargetFront} targetFront: The targetFront of the frame we want to take a screenshot of.
+ * @param {Object} args: See args param in captureAndSaveScreenshot
+ */
+async function captureScreenshot(targetFront, args) {
+ // @backward-compat { version 87 } The screenshot-content actor was introduced in 87,
+ // so we can always use it once 87 reaches release.
+ const supportsContentScreenshot = targetFront.hasActor("screenshotContent");
+ if (!supportsContentScreenshot) {
+ const screenshotFront = await targetFront.getFront("screenshot");
+ return screenshotFront.capture(args);
+ }
+
+ if (args.delay > 0) {
+ await new Promise(res => setTimeout(res, args.delay * 1000));
+ }
+
+ const screenshotContentFront = await targetFront.getFront(
+ "screenshot-content"
+ );
+
+ // Call the content-process on the server to retrieve informations that will be needed
+ // by the parent process.
+ const { rect, windowDpr, windowZoom, messages, error } =
+ await screenshotContentFront.prepareCapture(args);
+
+ if (error) {
+ return { error, messages };
+ }
+
+ if (rect) {
+ args.rect = rect;
+ }
+
+ args.dpr ||= windowDpr;
+
+ args.snapshotScale = args.dpr * windowZoom;
+ if (args.ignoreDprForFileScale) {
+ args.fileScale = windowZoom;
+ }
+
+ args.browsingContextID = targetFront.browsingContextID;
+
+ // We can now call the parent process which will take the screenshot via
+ // the drawSnapshot API
+ const rootFront = targetFront.client.mainRoot;
+ const parentProcessScreenshotFront = await rootFront.getFront("screenshot");
+ const captureResponse = await parentProcessScreenshotFront.capture(args);
+
+ return {
+ ...captureResponse,
+ messages: (messages || []).concat(captureResponse.messages || []),
+ };
+}
+
+const screenshotDescription = L10N.getStr("screenshotDesc");
+const screenshotGroupOptions = L10N.getStr("screenshotGroupOptions");
+const screenshotCommandParams = [
+ {
+ name: "clipboard",
+ type: "boolean",
+ description: L10N.getStr("screenshotClipboardDesc"),
+ manual: L10N.getStr("screenshotClipboardManual"),
+ },
+ {
+ name: "delay",
+ type: "number",
+ description: L10N.getStr("screenshotDelayDesc"),
+ manual: L10N.getStr("screenshotDelayManual"),
+ },
+ {
+ name: "dpr",
+ type: "number",
+ description: L10N.getStr("screenshotDPRDesc"),
+ manual: L10N.getStr("screenshotDPRManual"),
+ },
+ {
+ name: "fullpage",
+ type: "boolean",
+ description: L10N.getStr("screenshotFullPageDesc"),
+ manual: L10N.getStr("screenshotFullPageManual"),
+ },
+ {
+ name: "selector",
+ type: "string",
+ description: L10N.getStr("inspectNodeDesc"),
+ manual: L10N.getStr("inspectNodeManual"),
+ },
+ {
+ name: "file",
+ type: "boolean",
+ description: L10N.getStr("screenshotFileDesc"),
+ manual: L10N.getStr("screenshotFileManual"),
+ },
+ {
+ name: "filename",
+ type: "string",
+ description: L10N.getStr("screenshotFilenameDesc"),
+ manual: L10N.getStr("screenshotFilenameManual"),
+ },
+];
+
+/**
+ * Creates a string from an object for use when screenshot is passed the `--help` argument
+ *
+ * @param object param
+ * The param object to be formatted.
+ * @return string
+ * The formatted information from the param object as a string
+ */
+function formatHelpField(param) {
+ const padding = " ".repeat(5);
+ return Object.entries(param)
+ .map(([key, value]) => {
+ if (key === "name") {
+ const name = `${padding}--${value}`;
+ return name;
+ }
+ return `${padding.repeat(2)}${key}: ${value}`;
+ })
+ .join("\n");
+}
+
+/**
+ * Creates a string response from the screenshot options for use when
+ * screenshot is passed the `--help` argument
+ *
+ * @return string
+ * The formatted information from the param object as a string
+ */
+function getFormattedHelpData() {
+ const formattedParams = screenshotCommandParams
+ .map(formatHelpField)
+ .join("\n\n");
+
+ return `${screenshotDescription}\n${screenshotGroupOptions}\n\n${formattedParams}`;
+}
+
+/**
+ * Main entry point in this file; Takes the original arguments that `:screenshot` was
+ * called with and the image value from the server, and uses the client window to add
+ * and audio effect.
+ *
+ * @param object window
+ * The DevTools Client window.
+ *
+ * @param object args
+ * The original args with which the screenshot
+ * was called.
+ * @param object value
+ * an object with a image value and file name
+ *
+ * @return string[]
+ * Response messages from processing the screenshot
+ */
+function saveScreenshot(window, args = {}, value) {
+ // @backward-compat { version 87 } This is still needed by the console when connecting
+ // to an older server. Once 87 is in release, we can remove this whole block since we
+ // already handle args.help in captureScreenshotAndSave.
+ if (args.help) {
+ // Wrap message in an array so that the return value is consistant.
+ return [{ text: getFormattedHelpData() }];
+ }
+
+ // Guard against missing image data.
+ if (!value.data) {
+ return [];
+ }
+
+ simulateCameraShutter(window);
+ return save(window, args, value);
+}
+
+/**
+ * This function is called to simulate camera effects
+ *
+ * @param object document
+ * The DevTools Client document.
+ */
+function simulateCameraShutter(window) {
+ if (Services.prefs.getBoolPref("devtools.screenshot.audio.enabled")) {
+ const audioCamera = new window.Audio(
+ "resource://devtools/client/themes/audio/shutter.wav"
+ );
+ audioCamera.play();
+ }
+}
+
+/**
+ * Save the captured screenshot to one of several destinations.
+ *
+ * @param object window
+ * The DevTools Client window.
+ *
+ * @param object args
+ * The original args with which the screenshot was called.
+ *
+ * @param object image
+ * The image object that was sent from the server.
+ *
+ *
+ * @return string[]
+ * Response messages from processing the screenshot.
+ */
+async function save(window, args, image) {
+ const fileNeeded = args.filename || !args.clipboard || args.file;
+ const results = [];
+
+ if (args.clipboard) {
+ const result = saveToClipboard(image.data);
+ results.push(result);
+ }
+
+ if (fileNeeded) {
+ const result = await saveToFile(window, image);
+ results.push(result);
+ }
+ return results;
+}
+
+/**
+ * Save the image data to the clipboard. This returns a promise, so it can
+ * be treated exactly like file processing.
+ *
+ * @param string base64URI
+ * The image data encoded in a base64 URI that was sent from the server.
+ *
+ * @return string
+ * Response message from processing the screenshot.
+ */
+function saveToClipboard(base64URI) {
+ try {
+ const imageTools = Cc["@mozilla.org/image/tools;1"].getService(
+ Ci.imgITools
+ );
+
+ const base64Data = base64URI.replace("data:image/png;base64,", "");
+
+ const image = atob(base64Data);
+ const img = imageTools.decodeImageFromBuffer(
+ image,
+ image.length,
+ "image/png"
+ );
+
+ const transferable = Cc[
+ "@mozilla.org/widget/transferable;1"
+ ].createInstance(Ci.nsITransferable);
+ transferable.init(null);
+ transferable.addDataFlavor("image/png");
+ transferable.setTransferData("image/png", img);
+
+ Services.clipboard.setData(
+ transferable,
+ null,
+ Services.clipboard.kGlobalClipboard
+ );
+ return { text: L10N.getStr("screenshotCopied") };
+ } catch (ex) {
+ console.error(ex);
+ return { level: "error", text: L10N.getStr("screenshotErrorCopying") };
+ }
+}
+
+let _outputDirectory = null;
+
+/**
+ * Returns the default directory for screenshots.
+ * If a specific directory for screenshots is not defined,
+ * it falls back to the system downloads directory.
+ *
+ * @return {Promise<String>} Resolves the path as a string
+ */
+async function getOutputDirectory() {
+ if (_outputDirectory) {
+ return _outputDirectory;
+ }
+
+ try {
+ // This will throw if there is not a screenshot directory set for the platform
+ _outputDirectory = Services.dirsvc.get("Scrnshts", Ci.nsIFile).path;
+ } catch (e) {
+ _outputDirectory = await lazy.Downloads.getPreferredDownloadsDirectory();
+ }
+
+ return _outputDirectory;
+}
+
+/**
+ * Save the screenshot data to disk, returning a promise which is resolved on
+ * completion.
+ *
+ * @param object window
+ * The DevTools Client window.
+ *
+ * @param object image
+ * The image object that was sent from the server.
+ *
+ * @return string
+ * Response message from processing the screenshot.
+ */
+async function saveToFile(window, image) {
+ let filename = image.filename;
+
+ // Guard against missing image data.
+ if (!image.data) {
+ return "";
+ }
+
+ // Check there is a .png extension to filename
+ if (!filename.match(/.png$/i)) {
+ filename += ".png";
+ }
+
+ const dir = await getOutputDirectory();
+ const dirExists = await IOUtils.exists(dir);
+ if (dirExists) {
+ // If filename is absolute, it will override the downloads directory and
+ // still be applied as expected.
+ filename = PathUtils.isAbsolute(filename)
+ ? filename
+ : PathUtils.joinRelative(dir, filename);
+ }
+
+ const targetFile = new lazy.FileUtils.File(filename);
+
+ // Create download and track its progress.
+ try {
+ const download = await lazy.Downloads.createDownload({
+ source: {
+ url: image.data,
+ // Here we want to know if the window in which the screenshot is taken is private.
+ // We have a ChromeWindow when this is called from Browser Console (:screenshot) and
+ // RDM (screenshot button).
+ isPrivate: window.isChromeWindow
+ ? lazy.PrivateBrowsingUtils.isWindowPrivate(window)
+ : lazy.PrivateBrowsingUtils.isBrowserPrivate(
+ window.browsingContext.embedderElement
+ ),
+ },
+ target: targetFile,
+ });
+ const list = await lazy.Downloads.getList(lazy.Downloads.ALL);
+ // add the download to the download list in the Downloads list in the Browser UI
+ list.add(download);
+ // Await successful completion of the save via the download manager
+ await download.start();
+ return { text: L10N.getFormatStr("screenshotSavedToFile", filename) };
+ } catch (ex) {
+ console.error(ex);
+ return {
+ level: "error",
+ text: L10N.getFormatStr("screenshotErrorSavingToFile", filename),
+ };
+ }
+}
+
+module.exports = {
+ captureAndSaveScreenshot,
+ captureScreenshot,
+ saveScreenshot,
+};
diff --git a/devtools/client/shared/scroll.js b/devtools/client/shared/scroll.js
new file mode 100644
index 0000000000..e740e5c8f6
--- /dev/null
+++ b/devtools/client/shared/scroll.js
@@ -0,0 +1,144 @@
+/* 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/. */
+
+"use strict";
+
+// Make this available to both AMD and CJS environments
+define(function (require, exports, module) {
+ /**
+ * Scroll the document so that the element "elem" appears in the viewport.
+ *
+ * @param {DOMNode} elem
+ * The element that needs to appear in the viewport.
+ * @param {Boolean} centered
+ * true if you want it centered, false if you want it to appear on the
+ * top of the viewport. It is true by default, and that is usually what
+ * you want.
+ * @param {Boolean} smooth
+ * true if you want the scroll to happen smoothly, instead of instantly.
+ * It is false by default.
+ */
+ function scrollIntoViewIfNeeded(elem, centered = true, smooth = false) {
+ const win = elem.ownerDocument.defaultView;
+ const clientRect = elem.getBoundingClientRect();
+
+ // The following are always from the {top, bottom}
+ // of the viewport, to the {top, …} of the box.
+ // Think of them as geometrical vectors, it helps.
+ // The origin is at the top left.
+
+ const topToBottom = clientRect.bottom;
+ const bottomToTop = clientRect.top - win.innerHeight;
+ // We allow one translation on the y axis.
+ let yAllowed = true;
+
+ // disable smooth scrolling when the user prefers reduced motion
+ const reducedMotion = win.matchMedia("(prefers-reduced-motion)").matches;
+ smooth = smooth && !reducedMotion;
+
+ const options = { behavior: smooth ? "smooth" : "auto" };
+
+ // Whatever `centered` is, the behavior is the same if the box is
+ // (even partially) visible.
+ if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
+ win.scrollBy(
+ Object.assign(
+ { left: 0, top: topToBottom - elem.offsetHeight },
+ options
+ )
+ );
+ yAllowed = false;
+ } else if (
+ (bottomToTop < 0 || !centered) &&
+ bottomToTop >= -elem.offsetHeight
+ ) {
+ win.scrollBy(
+ Object.assign(
+ { left: 0, top: bottomToTop + elem.offsetHeight },
+ options
+ )
+ );
+
+ yAllowed = false;
+ }
+
+ // If we want it centered, and the box is completely hidden,
+ // then we center it explicitly.
+ if (centered) {
+ if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
+ const x = win.scrollX;
+ const y =
+ win.scrollY +
+ clientRect.top -
+ (win.innerHeight - elem.offsetHeight) / 2;
+ win.scroll(Object.assign({ left: x, top: y }, options));
+ }
+ }
+ }
+
+ function closestScrolledParent(node) {
+ if (node == null) {
+ return null;
+ }
+
+ if (node.scrollHeight > node.clientHeight) {
+ return node;
+ }
+
+ return closestScrolledParent(node.parentNode);
+ }
+
+ /**
+ * Scrolls the element into view if it is not visible.
+ *
+ * @param {DOMNode|undefined} element
+ * The item to be scrolled to.
+ *
+ * @param {Object|undefined} options
+ * An options object which can contain:
+ * - container: possible scrollable container. If it is not scrollable, we will
+ * look it up.
+ * - alignTo: "top" or "bottom" to indicate if we should scroll the element
+ * to the top or the bottom of the scrollable container when the
+ * element is off canvas.
+ * - center: Indicate if we should scroll the element to the middle of the
+ * scrollable container when the element is off canvas.
+ */
+ function scrollIntoView(element, options = {}) {
+ if (!element) {
+ return;
+ }
+
+ const { alignTo, center, container } = options;
+
+ const { top, bottom } = element.getBoundingClientRect();
+ const scrolledParent = closestScrolledParent(
+ container || element.parentNode
+ );
+ const scrolledParentRect = scrolledParent
+ ? scrolledParent.getBoundingClientRect()
+ : null;
+ const isVisible =
+ !scrolledParent ||
+ (top >= scrolledParentRect.top && bottom <= scrolledParentRect.bottom);
+
+ if (isVisible) {
+ return;
+ }
+
+ if (center) {
+ element.scrollIntoView({ block: "center" });
+ return;
+ }
+
+ const scrollToTop = alignTo
+ ? alignTo === "top"
+ : !scrolledParentRect || top < scrolledParentRect.top;
+ element.scrollIntoView(scrollToTop);
+ }
+
+ // Exports from this module
+ module.exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
+ module.exports.scrollIntoView = scrollIntoView;
+});
diff --git a/devtools/client/shared/source-map-loader/index.js b/devtools/client/shared/source-map-loader/index.js
new file mode 100644
index 0000000000..ccdb86520f
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/index.js
@@ -0,0 +1,139 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ WorkerDispatcher,
+} = require("resource://devtools/client/shared/worker-utils.js");
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/toolbox.properties"
+);
+
+const SOURCE_MAP_WORKER_URL =
+ "resource://devtools/client/shared/source-map-loader/worker.js";
+
+const {
+ originalToGeneratedId,
+ generatedToOriginalId,
+ isGeneratedId,
+ isOriginalId,
+} = require("resource://devtools/client/shared/source-map-loader/utils/index.js");
+
+class SourceMapLoader extends WorkerDispatcher {
+ constructor(targetCommand) {
+ super(SOURCE_MAP_WORKER_URL);
+ this.#targetCommand = targetCommand;
+ }
+
+ #setSourceMapForGeneratedSources = this.task(
+ "setSourceMapForGeneratedSources"
+ );
+ #getOriginalURLs = this.task("getOriginalURLs");
+ #getOriginalSourceText = this.task("getOriginalSourceText");
+ #targetCommand = null;
+
+ async getOriginalURLs(urlInfo) {
+ try {
+ return await this.#getOriginalURLs(urlInfo);
+ } catch (error) {
+ // Note that tests may not pass the targetCommand
+ if (this.#targetCommand) {
+ // Catch all errors and log them to the Web Console for users to see.
+ const message = L10N.getFormatStr(
+ "toolbox.sourceMapFailure",
+ error,
+ urlInfo.url,
+ urlInfo.sourceMapURL
+ );
+ this.#targetCommand.targetFront.logWarningInPage(message, "source map");
+ }
+
+ // And re-throw the error so that the debugger can also interpret the error
+ throw error;
+ }
+ }
+
+ hasOriginalURL = this.task("hasOriginalURL");
+ getOriginalRanges = this.task("getOriginalRanges");
+
+ getGeneratedRanges = this.task("getGeneratedRanges", {
+ queue: true,
+ });
+ getGeneratedLocation = this.task("getGeneratedLocation", {
+ queue: true,
+ });
+ getOriginalLocation = this.task("getOriginalLocation", {
+ queue: true,
+ });
+
+ getOriginalLocations = this.task("getOriginalLocations");
+ getGeneratedRangesForOriginal = this.task("getGeneratedRangesForOriginal");
+ getFileGeneratedRange = this.task("getFileGeneratedRange");
+ loadSourceMap = this.task("loadSourceMap");
+
+ async getOriginalSourceText(originalSourceId) {
+ try {
+ return await this.#getOriginalSourceText(originalSourceId);
+ } catch (error) {
+ const message = L10N.getFormatStr(
+ "toolbox.sourceMapSourceFailure",
+ error.message,
+ error.metadata ? error.metadata.url : "<unknown>"
+ );
+
+ // Note that tests may not pass the targetCommand
+ if (this.#targetCommand) {
+ // Catch all errors and log them to the Web Console for users to see.
+ this.#targetCommand.targetFront.logWarningInPage(message, "source map");
+ }
+
+ // Also replace the result with the error text.
+ // Note that this result has to have the same form
+ // as whatever the upstream getOriginalSourceText
+ // returns.
+ return {
+ text: message,
+ contentType: "text/plain",
+ };
+ }
+ }
+
+ clearSourceMaps = this.task("clearSourceMaps");
+ getOriginalStackFrames = this.task("getOriginalStackFrames");
+
+ async setSourceMapForGeneratedSources(generatedIds, sourceMap) {
+ const rv = await this.#setSourceMapForGeneratedSources(
+ generatedIds,
+ sourceMap
+ );
+
+ // Notify and ensure waiting for the SourceMapURLService to process the source map before resolving.
+ // Otherwise tests start failing because of pending request made by this component.
+ await this.emitAsync("source-map-created", generatedIds);
+
+ return rv;
+ }
+
+ destroy() {
+ // Request to stop the underlying DOM Worker
+ this.stop();
+ // Unregister all listeners
+ this.clearEvents();
+ // SourceMapLoader may be leaked and so it is important to clear most outer references
+ this.#targetCommand = null;
+ }
+}
+EventEmitter.decorate(SourceMapLoader.prototype);
+
+module.exports = {
+ SourceMapLoader,
+ originalToGeneratedId,
+ generatedToOriginalId,
+ isGeneratedId,
+ isOriginalId,
+};
diff --git a/devtools/client/shared/source-map-loader/moz.build b/devtools/client/shared/source-map-loader/moz.build
new file mode 100644
index 0000000000..c9b399c4cb
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "utils",
+ "wasm-dwarf",
+]
+
+DevToolsModules(
+ "index.js",
+ "source-map.js",
+ "worker.js",
+)
+
+BROWSER_CHROME_MANIFESTS += ["test/browser/browser.toml"]
diff --git a/devtools/client/shared/source-map-loader/source-map.js b/devtools/client/shared/source-map-loader/source-map.js
new file mode 100644
index 0000000000..f493271236
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/source-map.js
@@ -0,0 +1,646 @@
+/* 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/>. */
+
+"use strict";
+
+/**
+ * Source Map Worker
+ * @module utils/source-map-worker
+ */
+
+const {
+ SourceMapConsumer,
+} = require("resource://devtools/client/shared/vendor/source-map/source-map.js");
+
+// Initialize the source-map library right away so that all other code can use it.
+SourceMapConsumer.initialize({
+ "lib/mappings.wasm":
+ "resource://devtools/client/shared/vendor/source-map/lib/mappings.wasm",
+});
+
+const {
+ networkRequest,
+} = require("resource://devtools/client/shared/source-map-loader/utils/network-request.js");
+const assert = require("resource://devtools/client/shared/source-map-loader/utils/assert.js");
+const {
+ fetchSourceMap,
+ resolveSourceMapURL,
+ hasOriginalURL,
+ clearOriginalURLs,
+} = require("resource://devtools/client/shared/source-map-loader/utils/fetchSourceMap.js");
+const {
+ getSourceMap,
+ getSourceMapWithMetadata,
+ setSourceMap,
+ clearSourceMaps: clearSourceMapsRequests,
+} = require("resource://devtools/client/shared/source-map-loader/utils/sourceMapRequests.js");
+const {
+ originalToGeneratedId,
+ generatedToOriginalId,
+ isGeneratedId,
+ isOriginalId,
+ getContentType,
+} = require("resource://devtools/client/shared/source-map-loader/utils/index.js");
+const {
+ clearWasmXScopes,
+} = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js");
+
+/**
+ * Create "original source info" objects being handed over to the main thread
+ * to describe original sources referenced in a source map
+ */
+function mapToOriginalSourceInfos(generatedId, urls) {
+ return urls.map(url => {
+ return {
+ id: generatedToOriginalId(generatedId, url),
+ url,
+ };
+ });
+}
+
+/**
+ * Load the source map and retrieved infos about all the original sources
+ * referenced in that source map.
+ *
+ * @param {Object} generatedSource
+ * Source object for a bundle referencing a source map
+ * @return {Array<Object>|null}
+ * List of object with id and url attributes describing the original sources.
+ */
+async function getOriginalURLs(generatedSource) {
+ const { resolvedSourceMapURL, baseURL } =
+ resolveSourceMapURL(generatedSource);
+ const map = await fetchSourceMap(
+ generatedSource,
+ resolvedSourceMapURL,
+ baseURL
+ );
+ return map ? mapToOriginalSourceInfos(generatedSource.id, map.sources) : null;
+}
+
+/**
+ * Load the source map for a given bundle and return information
+ * about the related original sources and the source map itself.
+ *
+ * @param {Object} generatedSource
+ * Source object for the bundle.
+ * @return {Object}
+ * - {Array<Object>} sources
+ * Object with id and url attributes, refering to the related original sources
+ * referenced in the source map.
+ * - [String} resolvedSourceMapURL
+ * Absolute URL for the source map file.
+ * - {Array<String>} ignoreListUrls
+ * List of URLs of sources, designated by the source map, to be ignored in the debugger.
+ * - {String} exception
+ * In case of error, a string describing the situation.
+ */
+async function loadSourceMap(generatedSource) {
+ const { resolvedSourceMapURL, baseURL } =
+ resolveSourceMapURL(generatedSource);
+ try {
+ const map = await fetchSourceMap(
+ generatedSource,
+ resolvedSourceMapURL,
+ baseURL
+ );
+ if (!map.sources.length) {
+ throw new Error("No sources are declared in this source map.");
+ }
+ let ignoreListUrls = [];
+ if (map.x_google_ignoreList?.length) {
+ ignoreListUrls = map.x_google_ignoreList.map(
+ sourceIndex => map.sources[sourceIndex]
+ );
+ }
+ return {
+ sources: mapToOriginalSourceInfos(generatedSource.id, map.sources),
+ resolvedSourceMapURL,
+ ignoreListUrls,
+ };
+ } catch (e) {
+ return {
+ sources: [],
+ resolvedSourceMapURL,
+ ignoreListUrls: [],
+ exception: e.message,
+ };
+ }
+}
+
+const COMPUTED_SPANS = new WeakSet();
+
+const SOURCE_MAPPINGS = new WeakMap();
+async function getOriginalRanges(sourceId) {
+ if (!isOriginalId(sourceId)) {
+ return [];
+ }
+
+ const generatedSourceId = originalToGeneratedId(sourceId);
+ const data = await getSourceMapWithMetadata(generatedSourceId);
+ if (!data) {
+ return [];
+ }
+ const { map } = data;
+ const url = data.urlsById.get(sourceId);
+
+ let mappings = SOURCE_MAPPINGS.get(map);
+ if (!mappings) {
+ mappings = new Map();
+ SOURCE_MAPPINGS.set(map, mappings);
+ }
+
+ let fileMappings = mappings.get(url);
+ if (!fileMappings) {
+ fileMappings = [];
+ mappings.set(url, fileMappings);
+
+ const originalMappings = fileMappings;
+ map.eachMapping(
+ mapping => {
+ if (mapping.source !== url) {
+ return;
+ }
+
+ const last = originalMappings[originalMappings.length - 1];
+
+ if (last && last.line === mapping.originalLine) {
+ if (last.columnStart < mapping.originalColumn) {
+ last.columnEnd = mapping.originalColumn;
+ } else {
+ // Skip this duplicate original location,
+ return;
+ }
+ }
+
+ originalMappings.push({
+ line: mapping.originalLine,
+ columnStart: mapping.originalColumn,
+ columnEnd: Infinity,
+ });
+ },
+ null,
+ SourceMapConsumer.ORIGINAL_ORDER
+ );
+ }
+
+ return fileMappings;
+}
+
+/**
+ * Given an original location, find the ranges on the generated file that
+ * are mapped from the original range containing the location.
+ */
+async function getGeneratedRanges(location) {
+ if (!isOriginalId(location.sourceId)) {
+ return [];
+ }
+
+ const generatedSourceId = originalToGeneratedId(location.sourceId);
+ const data = await getSourceMapWithMetadata(generatedSourceId);
+ if (!data) {
+ return [];
+ }
+ const { urlsById, map } = data;
+
+ if (!COMPUTED_SPANS.has(map)) {
+ COMPUTED_SPANS.add(map);
+ map.computeColumnSpans();
+ }
+
+ // We want to use 'allGeneratedPositionsFor' to get the _first_ generated
+ // location, but it hard-codes SourceMapConsumer.LEAST_UPPER_BOUND as the
+ // bias, making it search in the wrong direction for this usecase.
+ // To work around this, we use 'generatedPositionFor' and then look up the
+ // exact original location, making any bias value unnecessary, and then
+ // use that location for the call to 'allGeneratedPositionsFor'.
+ const genPos = map.generatedPositionFor({
+ source: urlsById.get(location.sourceId),
+ line: location.line,
+ column: location.column == null ? 0 : location.column,
+ bias: SourceMapConsumer.GREATEST_LOWER_BOUND,
+ });
+ if (genPos.line === null) {
+ return [];
+ }
+
+ const positions = map.allGeneratedPositionsFor(
+ map.originalPositionFor({
+ line: genPos.line,
+ column: genPos.column,
+ })
+ );
+
+ return positions
+ .map(mapping => ({
+ line: mapping.line,
+ columnStart: mapping.column,
+ columnEnd: mapping.lastColumn,
+ }))
+ .sort((a, b) => {
+ const line = a.line - b.line;
+ return line === 0 ? a.column - b.column : line;
+ });
+}
+
+async function getGeneratedLocation(location) {
+ if (!isOriginalId(location.sourceId)) {
+ return null;
+ }
+
+ const generatedSourceId = originalToGeneratedId(location.sourceId);
+ const data = await getSourceMapWithMetadata(generatedSourceId);
+ if (!data) {
+ return null;
+ }
+ const { urlsById, map } = data;
+
+ const positions = map.allGeneratedPositionsFor({
+ source: urlsById.get(location.sourceId),
+ line: location.line,
+ column: location.column == null ? 0 : location.column,
+ });
+
+ // Prior to source-map 0.7, the source-map module returned the earliest
+ // generated location in the file when there were multiple generated
+ // locations. The current comparison fn in 0.7 does not appear to take
+ // generated location into account properly.
+ let match;
+ for (const pos of positions) {
+ if (!match || pos.line < match.line || pos.column < match.column) {
+ match = pos;
+ }
+ }
+
+ if (!match) {
+ match = map.generatedPositionFor({
+ source: urlsById.get(location.sourceId),
+ line: location.line,
+ column: location.column == null ? 0 : location.column,
+ bias: SourceMapConsumer.LEAST_UPPER_BOUND,
+ });
+ }
+
+ return {
+ sourceId: generatedSourceId,
+ line: match.line,
+ column: match.column,
+ };
+}
+
+/**
+ * Map the breakable positions (line and columns) from generated to original locations.
+ *
+ * @param {Object} breakpointPositions
+ * List of columns per line refering to the breakable columns per line
+ * for a given source:
+ * {
+ * 1: [2, 6], // On line 1, column 2 and 6 are breakable.
+ * ...
+ * }
+ * @param {string} sourceId
+ * The ID for the generated source.
+ */
+async function getOriginalLocations(breakpointPositions, sourceId) {
+ const map = await getSourceMap(sourceId);
+ if (!map) {
+ return null;
+ }
+ for (const line in breakpointPositions) {
+ const breakableColumnsPerLine = breakpointPositions[line];
+ for (let i = 0; i < breakableColumnsPerLine.length; i++) {
+ const column = breakableColumnsPerLine[i];
+ const mappedLocation = getOriginalLocationSync(map, {
+ sourceId,
+ line: parseInt(line, 10),
+ column,
+ });
+ if (mappedLocation) {
+ // As we replace the `column` with the mappedLocation,
+ // also transfer the generated column so that we can compute both original and generated locations
+ // in the main thread.
+ mappedLocation.generatedColumn = column;
+ breakableColumnsPerLine[i] = mappedLocation;
+ }
+ }
+ }
+ return breakpointPositions;
+}
+
+/**
+ * Query the source map for a mapping from bundle location to original location.
+ *
+ * @param {SourceMapConsumer} map
+ * The source map for the bundle source.
+ * @param {Object} location
+ * A location within a bundle to map to an original location.
+ * @param {Object} options
+ * @param {Boolean} options.looseSearch
+ * Optional, if true, will do a loose search on first column and next lines
+ * until a mapping is found.
+ * @return {location}
+ * The mapped location in the original source.
+ */
+function getOriginalLocationSync(map, location, { looseSearch = false } = {}) {
+ // First check for an exact match
+ let match = map.originalPositionFor({
+ line: location.line,
+ column: location.column == null ? 0 : location.column,
+ });
+
+ // Then check for a loose match by sliding to first column and next lines
+ if (match.sourceUrl == null && looseSearch) {
+ let line = location.line;
+ // if a non-0 column was passed, we want to do the search from the beginning of the line,
+ // otherwise, we can start looking into next lines
+ let firstLineChecked = (location.column || 0) !== 0;
+
+ // Avoid looping through the whole file and limit the sliding search to the next 10 lines.
+ while (match.sourceUrl === null && line < location.line + 10) {
+ if (firstLineChecked) {
+ line++;
+ } else {
+ firstLineChecked = true;
+ }
+ match = map.originalPositionFor({
+ line,
+ column: 0,
+ bias: SourceMapConsumer.LEAST_UPPER_BOUND,
+ });
+ }
+ }
+
+ const { source: sourceUrl, line, column } = match;
+
+ if (sourceUrl == null) {
+ // No url means the location didn't map.
+ return null;
+ }
+
+ return {
+ sourceId: generatedToOriginalId(location.sourceId, sourceUrl),
+ sourceUrl,
+ line,
+ column,
+ };
+}
+
+/**
+ * Map a bundle location to an original one.
+ *
+ * @param {Object} location
+ * Bundle location
+ * @param {Object} options
+ * See getORiginalLocationSync.
+ * @return {Object}
+ * Original location
+ */
+async function getOriginalLocation(location, options) {
+ if (!isGeneratedId(location.sourceId)) {
+ return null;
+ }
+
+ const map = await getSourceMap(location.sourceId);
+ if (!map) {
+ return null;
+ }
+
+ return getOriginalLocationSync(map, location, options);
+}
+
+async function getOriginalSourceText(originalSourceId) {
+ assert(isOriginalId(originalSourceId), "Source is not an original source");
+
+ const generatedSourceId = originalToGeneratedId(originalSourceId);
+ const data = await getSourceMapWithMetadata(generatedSourceId);
+ if (!data) {
+ return null;
+ }
+ const { urlsById, map } = data;
+
+ const url = urlsById.get(originalSourceId);
+ let text = map.sourceContentFor(url, true);
+ if (!text) {
+ try {
+ const response = await networkRequest(url, {
+ sourceMapBaseURL: map.sourceMapBaseURL,
+ loadFromCache: false,
+ allowsRedirects: false,
+ });
+ text = response.content;
+ } catch (err) {
+ // Workers exceptions are processed by worker-utils module and
+ // only metadata attribute is transferred between threads.
+ // Notify the main thread about which url failed loading.
+ err.metadata = {
+ url,
+ };
+ throw err;
+ }
+ }
+
+ return {
+ text,
+ contentType: getContentType(url || ""),
+ };
+}
+
+/**
+ * Find the set of ranges on the generated file that map from the original
+ * file's locations.
+ *
+ * @param sourceId - The original ID of the file we are processing.
+ * @param url - The original URL of the file we are processing.
+ * @param mergeUnmappedRegions - If unmapped regions are encountered between
+ * two mappings for the given original file, allow the two mappings to be
+ * merged anyway. This is useful if you are more interested in the general
+ * contiguous ranges associated with a file, rather than the specifics of
+ * the ranges provided by the sourcemap.
+ */
+const GENERATED_MAPPINGS = new WeakMap();
+async function getGeneratedRangesForOriginal(
+ sourceId,
+ mergeUnmappedRegions = false
+) {
+ assert(isOriginalId(sourceId), "Source is not an original source");
+
+ const data = await getSourceMapWithMetadata(originalToGeneratedId(sourceId));
+ // NOTE: this is only needed for Flow
+ if (!data) {
+ return [];
+ }
+ const { urlsById, map } = data;
+ const url = urlsById.get(sourceId);
+
+ if (!COMPUTED_SPANS.has(map)) {
+ COMPUTED_SPANS.add(map);
+ map.computeColumnSpans();
+ }
+
+ if (!GENERATED_MAPPINGS.has(map)) {
+ GENERATED_MAPPINGS.set(map, new Map());
+ }
+
+ const generatedRangesMap = GENERATED_MAPPINGS.get(map);
+ if (!generatedRangesMap) {
+ return [];
+ }
+
+ if (generatedRangesMap.has(sourceId)) {
+ // NOTE we need to coerce the result to an array for Flow
+ return generatedRangesMap.get(sourceId) || [];
+ }
+
+ // Gather groups of mappings on the generated file, with new groups created
+ // if we cross a mapping for a different file.
+ let currentGroup = [];
+ const originalGroups = [currentGroup];
+ map.eachMapping(
+ mapping => {
+ if (mapping.source === url) {
+ currentGroup.push({
+ start: {
+ line: mapping.generatedLine,
+ column: mapping.generatedColumn,
+ },
+ end: {
+ line: mapping.generatedLine,
+ // The lastGeneratedColumn value is an inclusive value so we add
+ // one to it to get the exclusive end position.
+ column: mapping.lastGeneratedColumn + 1,
+ },
+ });
+ } else if (typeof mapping.source === "string" && currentGroup.length) {
+ // If there is a URL, but it is for a _different_ file, we create a
+ // new group of mappings so that we can tell
+ currentGroup = [];
+ originalGroups.push(currentGroup);
+ }
+ },
+ null,
+ SourceMapConsumer.GENERATED_ORDER
+ );
+
+ const generatedMappingsForOriginal = [];
+ if (mergeUnmappedRegions) {
+ // If we don't care about excluding unmapped regions, then we just need to
+ // create a range that is the fully encompasses each group, ignoring the
+ // empty space between each individual range.
+ for (const group of originalGroups) {
+ if (group.length) {
+ generatedMappingsForOriginal.push({
+ start: group[0].start,
+ end: group[group.length - 1].end,
+ });
+ }
+ }
+ } else {
+ let lastEntry;
+ for (const group of originalGroups) {
+ lastEntry = null;
+ for (const { start, end } of group) {
+ const lastEnd = lastEntry
+ ? wrappedMappingPosition(lastEntry.end)
+ : null;
+
+ // If this entry comes immediately after the previous one, extend the
+ // range of the previous entry instead of adding a new one.
+ if (
+ lastEntry &&
+ lastEnd &&
+ lastEnd.line === start.line &&
+ lastEnd.column === start.column
+ ) {
+ lastEntry.end = end;
+ } else {
+ const newEntry = { start, end };
+ generatedMappingsForOriginal.push(newEntry);
+ lastEntry = newEntry;
+ }
+ }
+ }
+ }
+
+ generatedRangesMap.set(sourceId, generatedMappingsForOriginal);
+ return generatedMappingsForOriginal;
+}
+
+function wrappedMappingPosition(pos) {
+ if (pos.column !== Infinity) {
+ return pos;
+ }
+
+ // If the end of the entry consumes the whole line, treat it as wrapping to
+ // the next line.
+ return {
+ line: pos.line + 1,
+ column: 0,
+ };
+}
+
+async function getFileGeneratedRange(originalSourceId) {
+ assert(isOriginalId(originalSourceId), "Source is not an original source");
+
+ const data = await getSourceMapWithMetadata(
+ originalToGeneratedId(originalSourceId)
+ );
+ if (!data) {
+ return null;
+ }
+ const { urlsById, map } = data;
+
+ const start = map.generatedPositionFor({
+ source: urlsById.get(originalSourceId),
+ line: 1,
+ column: 0,
+ bias: SourceMapConsumer.LEAST_UPPER_BOUND,
+ });
+
+ const end = map.generatedPositionFor({
+ source: urlsById.get(originalSourceId),
+ line: Number.MAX_SAFE_INTEGER,
+ column: Number.MAX_SAFE_INTEGER,
+ bias: SourceMapConsumer.GREATEST_LOWER_BOUND,
+ });
+
+ return {
+ start,
+ end,
+ };
+}
+
+/**
+ * Set the sourceMap for multiple passed source ids.
+ *
+ * @param {Array<string>} generatedSourceIds
+ * @param {Object} map: An actual sourcemap (as generated with SourceMapGenerator#toJSON)
+ */
+function setSourceMapForGeneratedSources(generatedSourceIds, map) {
+ const sourceMapConsumer = new SourceMapConsumer(map);
+ for (const generatedId of generatedSourceIds) {
+ setSourceMap(generatedId, Promise.resolve(sourceMapConsumer));
+ }
+}
+
+function clearSourceMaps() {
+ clearSourceMapsRequests();
+ clearWasmXScopes();
+ clearOriginalURLs();
+}
+
+module.exports = {
+ getOriginalURLs,
+ loadSourceMap,
+ hasOriginalURL,
+ getOriginalRanges,
+ getGeneratedRanges,
+ getGeneratedLocation,
+ getOriginalLocation,
+ getOriginalLocations,
+ getOriginalSourceText,
+ getGeneratedRangesForOriginal,
+ getFileGeneratedRange,
+ setSourceMapForGeneratedSources,
+ clearSourceMaps,
+};
diff --git a/devtools/client/shared/source-map-loader/test/browser/browser.toml b/devtools/client/shared/source-map-loader/test/browser/browser.toml
new file mode 100644
index 0000000000..5a602e9eeb
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/browser.toml
@@ -0,0 +1,21 @@
+[DEFAULT]
+tags = "devtools"
+subsuite = "devtools"
+support-files = [
+ "head.js",
+ "fixtures/*",
+ "!/devtools/client/shared/test/shared-head.js",
+ "!/devtools/client/shared/test/telemetry-test-helpers.js",
+]
+
+["browser_getContentType.js"]
+
+["browser_locations.js"]
+
+["browser_source-map.js"]
+
+["browser_wasm-source-map.js"]
+skip-if = [
+ "http3", # Bug 1829298
+ "http2",
+]
diff --git a/devtools/client/shared/source-map-loader/test/browser/browser_getContentType.js b/devtools/client/shared/source-map-loader/test/browser/browser_getContentType.js
new file mode 100644
index 0000000000..5ac402951c
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/browser_getContentType.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Cover the automatic mapping of content type based on file extension
+
+const {
+ getContentType,
+ contentMapForTesting,
+} = require("resource://devtools/client/shared/source-map-loader/utils/index.js");
+
+add_task(async () => {
+ for (const ext in contentMapForTesting) {
+ is(
+ getContentType(`whatever.${ext}`),
+ contentMapForTesting[ext],
+ `${ext} file extension is correctly mapping the expected content type`
+ );
+ }
+ is(
+ getContentType(`whateverjs`),
+ "text/plain",
+ `A valid extension in file name doesn't cause a special content type mapping`
+ );
+
+ is(
+ getContentType("whatever.platypus"),
+ "text/plain",
+ "Test unknown extension defaults to text plain"
+ );
+});
diff --git a/devtools/client/shared/source-map-loader/test/browser/browser_locations.js b/devtools/client/shared/source-map-loader/test/browser/browser_locations.js
new file mode 100644
index 0000000000..4d43df5902
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/browser_locations.js
@@ -0,0 +1,141 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Covert getOriginalLocation and getGeneratedLocation functions.
+
+add_task(async function testGetOriginalLocation() {
+ await fetchFixtureSourceMap("bundle");
+
+ const generatedLocation = {
+ sourceId: "bundle.js",
+ line: 49,
+ };
+
+ const originalLocation = await gSourceMapLoader.getOriginalLocation(
+ generatedLocation
+ );
+ Assert.deepEqual(
+ originalLocation,
+ {
+ column: 0,
+ line: 3,
+ sourceId: "bundle.js/originalSource-fe2c41d3535b76c158e39ba4f3ff826a",
+ sourceUrl: "webpack:///entry.js",
+ },
+ "Mapped a generated location"
+ );
+
+ const originalLocation2 = await gSourceMapLoader.getOriginalLocation(
+ originalLocation
+ );
+ Assert.deepEqual(originalLocation2, null, "No mapped location");
+
+ gSourceMapLoader.clearSourceMaps();
+ const originalLocation3 = await gSourceMapLoader.getOriginalLocation(
+ generatedLocation
+ );
+ Assert.deepEqual(
+ originalLocation3,
+ null,
+ "after clearing the source maps, the same generated location no longer maps"
+ );
+});
+
+add_task(async function testGetGeneratedLocation() {
+ await fetchFixtureSourceMap("bundle");
+
+ const originalLocation = {
+ column: 0,
+ line: 3,
+ sourceId: "bundle.js/originalSource-fe2c41d3535b76c158e39ba4f3ff826a",
+ };
+
+ const source = {
+ url: "webpack:///entry.js",
+ id: "bundle.js/originalSource-fe2c41d3535b76c158e39ba4f3ff826a",
+ };
+
+ const generatedLocation = await gSourceMapLoader.getGeneratedLocation(
+ originalLocation,
+ source
+ );
+ Assert.deepEqual(
+ generatedLocation,
+ {
+ sourceId: "bundle.js",
+ line: 49,
+ column: 0,
+ },
+ "Map an original location"
+ );
+
+ {
+ gSourceMapLoader.clearSourceMaps();
+
+ const secondGeneratedLocation = await gSourceMapLoader.getGeneratedLocation(
+ originalLocation,
+ source
+ );
+ Assert.deepEqual(
+ secondGeneratedLocation,
+ null,
+ "after clearing source maps, the same location no longer maps to an original location"
+ );
+ }
+
+ {
+ // we expect symmetric mappings, which means that if
+ // we map a generated location to an original location,
+ // and then map it back, we should get the original generated value.
+ // e.g. G[8, 0] -> O[5, 4] -> G[8, 0]
+ await fetchFixtureSourceMap("if.out");
+
+ const genLoc1 = {
+ sourceId: "if.out.js",
+ column: 0,
+ line: 8,
+ };
+
+ const ifSource = {
+ url: "if.js",
+ id: "if.out.js/originalSource-5ad3141023dae912c5f8833c7e03beeb",
+ };
+
+ const oLoc = await gSourceMapLoader.getOriginalLocation(genLoc1);
+ const genLoc2 = await gSourceMapLoader.getGeneratedLocation(oLoc, ifSource);
+
+ Assert.deepEqual(genLoc2, genLoc1, "location mapping is symmetric");
+ }
+
+ {
+ // we expect that an undefined column will be handled like a
+ // location w/ column 0. e.g. G[8, u] -> O[5, 4] -> G[8, 0]
+ await fetchFixtureSourceMap("if.out");
+
+ const genLoc1 = {
+ sourceId: "if.out.js",
+ column: undefined,
+ line: 8,
+ };
+
+ const ifSource = {
+ url: "if.js",
+ id: "if.out.js/originalSource-5ad3141023dae912c5f8833c7e03beeb",
+ };
+
+ const oLoc = await gSourceMapLoader.getOriginalLocation(genLoc1);
+ const genLoc2 = await gSourceMapLoader.getGeneratedLocation(oLoc, ifSource);
+
+ Assert.deepEqual(
+ genLoc2,
+ {
+ sourceId: "if.out.js",
+ column: 0,
+ line: 8,
+ },
+ "undefined column is handled like 0 column"
+ );
+ }
+});
diff --git a/devtools/client/shared/source-map-loader/test/browser/browser_source-map.js b/devtools/client/shared/source-map-loader/test/browser/browser_source-map.js
new file mode 100644
index 0000000000..34d4fedf27
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/browser_source-map.js
@@ -0,0 +1,163 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Cover the high level API of these modules:
+// getOriginalURLs getGeneratedRangesForOriginal functions
+
+async function assertFixtureOriginalURLs(
+ fixtureName,
+ expectedUrls,
+ testMessage
+) {
+ const originalSources = await fetchFixtureSourceMap(fixtureName);
+ const urls = originalSources.map(s => s.url);
+ Assert.deepEqual(urls, expectedUrls, testMessage);
+}
+
+add_task(async function testGetOriginalURLs() {
+ await assertFixtureOriginalURLs(
+ "absolute",
+ ["https://example.com/cheese/heart.js"],
+ "Test absolute URL"
+ );
+
+ await assertFixtureOriginalURLs(
+ "bundle",
+ [
+ "webpack:///webpack/bootstrap%204ef8c7ec7c1df790781e",
+ "webpack:///entry.js",
+ "webpack:///times2.js",
+ "webpack:///output.js",
+ "webpack:///opts.js",
+ ],
+ "Test source with a url"
+ );
+
+ await assertFixtureOriginalURLs(
+ "empty",
+ [`${URL_ROOT_SSL}fixtures/heart.js`],
+ "Test empty sourceRoot resolution"
+ );
+
+ await assertFixtureOriginalURLs(
+ "noroot",
+ [`${URL_ROOT_SSL}fixtures/heart.js`],
+ "Test Non-existing sourceRoot resolution"
+ );
+
+ await assertFixtureOriginalURLs(
+ "noroot2",
+ [`${URL_ROOT_SSL}fixtures/heart.js`],
+ "Test Non-existing sourceRoot resolution with relative URLs"
+ );
+});
+
+add_task(async function testGetGeneratedRangesForOriginal() {
+ const originals = await fetchFixtureSourceMap("intermingled-sources");
+
+ const ranges = await gSourceMapLoader.getGeneratedRangesForOriginal(
+ originals[0].id
+ );
+
+ Assert.deepEqual(
+ ranges,
+ [
+ {
+ start: {
+ line: 4,
+ column: 69,
+ },
+ end: {
+ line: 9,
+ column: Infinity,
+ },
+ },
+ {
+ start: {
+ line: 11,
+ column: 0,
+ },
+ end: {
+ line: 17,
+ column: 3,
+ },
+ },
+ {
+ start: {
+ line: 19,
+ column: 18,
+ },
+ end: {
+ line: 19,
+ column: 22,
+ },
+ },
+ {
+ start: {
+ line: 26,
+ column: 0,
+ },
+ end: {
+ line: 26,
+ column: Infinity,
+ },
+ },
+ {
+ start: {
+ line: 28,
+ column: 0,
+ },
+ end: {
+ line: 28,
+ column: Infinity,
+ },
+ },
+ ],
+ "Test the overall generated ranges on the source"
+ );
+
+ {
+ // Note that we have to clear the source map in order to get the merged ranges,
+ // otherwise we are still fetching the previous unmerged ones!
+ const secondOriginals = await fetchFixtureSourceMap("intermingled-sources");
+ const mergedRanges = await gSourceMapLoader.getGeneratedRangesForOriginal(
+ secondOriginals[0].id,
+ true
+ );
+
+ Assert.deepEqual(
+ mergedRanges,
+ [
+ {
+ start: {
+ line: 4,
+ column: 69,
+ },
+ end: {
+ line: 28,
+ column: Infinity,
+ },
+ },
+ ],
+ "Test the merged generated ranges on the source"
+ );
+ }
+});
+
+add_task(async function testBaseURLErrorHandling() {
+ const source = {
+ id: "missingmap.js",
+ sourceMapURL: "missingmap.js.map",
+ // Notice the duplicated ":" which cause the error here
+ sourceMapBaseURL: "http:://example.com/",
+ };
+
+ try {
+ await gSourceMapLoader.getOriginalURLs(source);
+ ok(false, "Should throw");
+ } catch (e) {
+ is(e.message, "URL constructor: http:://example.com/ is not a valid URL.");
+ }
+});
diff --git a/devtools/client/shared/source-map-loader/test/browser/browser_wasm-source-map.js b/devtools/client/shared/source-map-loader/test/browser/browser_wasm-source-map.js
new file mode 100644
index 0000000000..0f69d7b038
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/browser_wasm-source-map.js
@@ -0,0 +1,126 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test source mappings WASM sources.
+// This test is quite general and test various functions.
+
+const {
+ WasmRemap,
+} = require("resource://devtools/client/shared/source-map-loader/utils/wasmRemap.js");
+const {
+ SourceMapConsumer,
+} = require("resource://devtools/client/shared/vendor/source-map/source-map.js");
+
+SourceMapConsumer.initialize({
+ "lib/mappings.wasm":
+ "resource://devtools/client/shared/vendor/source-map/lib/mappings.wasm",
+});
+
+add_task(async function smokeTest() {
+ const testMap1 = {
+ version: 3,
+ file: "min.js",
+ names: [],
+ sources: ["one.js", "two.js"],
+ sourceRoot: "/the/root",
+ mappings: "CAAC,IAAM,SACU,GAAC",
+ };
+ const testMap1Entries = [
+ { offset: 1, line: 1, column: 1 },
+ { offset: 5, line: 1, column: 7 },
+ { offset: 14, line: 2, column: 17 },
+ { offset: 17, line: 2, column: 18 },
+ ];
+
+ const map1 = await new SourceMapConsumer(testMap1);
+
+ const remap1 = new WasmRemap(map1);
+
+ is(remap1.file, "min.js");
+ is(remap1.hasContentsOfAllSources(), false);
+ is(remap1.sources.length, 2);
+ is(remap1.sources[0], "/the/root/one.js");
+ is(remap1.sources[1], "/the/root/two.js");
+
+ const expectedEntries = testMap1Entries.slice(0);
+ remap1.eachMapping(function (entry) {
+ const expected = expectedEntries.shift();
+ is(entry.generatedLine, expected.offset);
+ is(entry.generatedColumn, 0);
+ is(entry.originalLine, expected.line);
+ is(entry.originalColumn, expected.column);
+ is(entry.name, null);
+ });
+
+ const pos1 = remap1.originalPositionFor({ line: 5, column: 0 });
+ is(pos1.line, 1);
+ is(pos1.column, 7);
+ is(pos1.source, "/the/root/one.js");
+
+ const pos2 = remap1.generatedPositionFor({
+ source: "/the/root/one.js",
+ line: 2,
+ column: 18,
+ });
+ is(pos2.line, 17);
+ is(pos2.column, 0);
+ is(pos2.lastColumn, undefined);
+
+ remap1.computeColumnSpans();
+ const pos3 = remap1.allGeneratedPositionsFor({
+ source: "/the/root/one.js",
+ line: 2,
+ column: 17,
+ });
+ is(pos3.length, 1);
+ is(pos3[0].line, 14);
+ is(pos3[0].column, 0);
+ is(pos3[0].lastColumn, 0);
+
+ map1.destroy();
+});
+
+add_task(async function contentPresents() {
+ const testMap2 = {
+ version: 3,
+ file: "none.js",
+ names: [],
+ sources: ["zero.js"],
+ mappings: "",
+ sourcesContent: ["//test"],
+ };
+
+ const map2 = await new SourceMapConsumer(testMap2);
+ const remap2 = new WasmRemap(map2);
+ is(remap2.file, "none.js");
+ ok(remap2.hasContentsOfAllSources());
+ is(remap2.sourceContentFor("zero.js"), "//test");
+
+ map2.destroy();
+});
+
+add_task(async function readAndTransposeWasmMap() {
+ const source = {
+ id: "wasm.js",
+ sourceMapBaseURL: "wasm:http://example.com/whatever/:min.js",
+ sourceMapURL: `${URL_ROOT}fixtures/wasm.js.map`,
+ isWasm: true,
+ };
+
+ const urls = await gSourceMapLoader.getOriginalURLs(source);
+ Assert.deepEqual(urls, [
+ {
+ id: "wasm.js/originalSource-63954a1c231200652c0d99c6a69cd178",
+ url: `${URL_ROOT}fixtures/one.js`,
+ },
+ ]);
+
+ const { line, column } = await gSourceMapLoader.getOriginalLocation({
+ sourceId: source.id,
+ line: 5,
+ });
+ is(line, 1);
+ is(column, 7);
+});
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js
new file mode 100644
index 0000000000..adcc337eca
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js
@@ -0,0 +1,2 @@
+/* Doesn't really matter what is in here. */
+// # sourceMappingURL=absolute.js.map
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js.map
new file mode 100644
index 0000000000..0597515870
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/absolute.js.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "sources": [
+ "heart.js"
+ ],
+ "names": [],
+ "mappings": ";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA,QAAO,SAAS;AAChB;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;ACfA;AACA;AACA;;;;;;;ACFA;AACA;AACA;;AAEA,mBAAkB;;;;;;;ACJlB;AACA;AACA",
+ "file": "absolute.js",
+ "sourceRoot": "https://example.com/cheese/"
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js
new file mode 100644
index 0000000000..9ee477b856
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js
@@ -0,0 +1,94 @@
+/** ****/ (function(modules) { // webpackBootstrap
+/** ****/ // The module cache
+/** ****/ var installedModules = {};
+/** ****/
+/** ****/ // The require function
+/** ****/ function __webpack_require__(moduleId) {
+/** ****/
+/** ****/ // Check if module is in cache
+/** ****/ if (installedModules[moduleId])
+/** ****/ {return installedModules[moduleId].exports;}
+/** ****/
+/** ****/ // Create a new module (and put it into the cache)
+/** ****/ var module = installedModules[moduleId] = {
+/** ****/ exports: {},
+/** ****/ id: moduleId,
+/** ****/ loaded: false
+/** ****/ };
+/** ****/
+/** ****/ // Execute the module function
+/** ****/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/** ****/
+/** ****/ // Flag the module as loaded
+/** ****/ module.loaded = true;
+/** ****/
+/** ****/ // Return the exports of the module
+/** ****/ return module.exports;
+/** ****/ }
+/** ****/
+/** ****/
+/** ****/ // expose the modules object (__webpack_modules__)
+/** ****/ __webpack_require__.m = modules;
+/** ****/
+/** ****/ // expose the module cache
+/** ****/ __webpack_require__.c = installedModules;
+/** ****/
+/** ****/ // __webpack_public_path__
+/** ****/ __webpack_require__.p = "";
+/** ****/
+/** ****/ // Load entry module and return exports
+/** ****/ return __webpack_require__(0);
+/** ****/ })([
+/* 0 */
+/** */ function(module, exports, __webpack_require__) {
+
+ const times2 = __webpack_require__(1);
+ const { output } = __webpack_require__(2);
+ const opts = __webpack_require__(3);
+
+ output(times2(1));
+ output(times2(2));
+
+ if (opts.extra) {
+ output(times2(3));
+}
+
+ window.keepMeAlive = function() {
+ // This function exists to make sure this script is never garbage
+ // collected. It is also callable from tests.
+ return times2(4);
+};
+
+/** *
+/ },
+/* 1 */
+/** */ function(module, exports) {
+
+ module.exports = function(x) {
+ return x * 2;
+};
+
+/** *
+/ },
+/* 2 */
+/** */ function(module, exports) {
+
+ function output(str) {
+ console.log(str);
+}
+
+ module.exports = { output };
+
+/** *
+/ },
+/* 3 */
+/** */ function(module, exports) {
+
+ module.exports = {
+ extra: true
+};
+
+/** *
+/ }
+/** ****/ ]);
+// # sourceMappingURL=bundle.js.map
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js.map
new file mode 100644
index 0000000000..f53036cb8e
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/bundle.js.map
@@ -0,0 +1,21 @@
+{
+ "version": 3,
+ "sources": [
+ "webpack:///webpack/bootstrap 4ef8c7ec7c1df790781e",
+ "webpack:///./entry.js",
+ "webpack:///./times2.js",
+ "webpack:///./output.js",
+ "webpack:///./opts.js"
+ ],
+ "names": [],
+ "mappings": ";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA,QAAO,SAAS;AAChB;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;ACfA;AACA;AACA;;;;;;;ACFA;AACA;AACA;;AAEA,mBAAkB;;;;;;;ACJlB;AACA;AACA",
+ "file": "bundle.js",
+ "sourcesContent": [
+ " \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 4ef8c7ec7c1df790781e",
+ "const times2 = require(\"./times2\");\nconst { output } = require(\"./output\");\nconst opts = require(\"./opts\");\n\noutput(times2(1));\noutput(times2(2));\n\nif(opts.extra) {\n output(times2(3));\n}\n\nwindow.keepMeAlive = function() {\n // This function exists to make sure this script is never garbage\n // collected. It is also callable from tests.\n return times2(4);\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./entry.js\n// module id = 0\n// module chunks = 0",
+ "module.exports = function(x) {\n return x * 2;\n}\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./times2.js\n// module id = 1\n// module chunks = 0",
+ "function output(str) {\n console.log(str);\n}\n\nmodule.exports = { output };\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./output.js\n// module id = 2\n// module chunks = 0",
+ "module.exports = {\n extra: true\n};\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./opts.js\n// module id = 3\n// module chunks = 0"
+ ],
+ "sourceRoot": ""
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js
new file mode 100644
index 0000000000..72c472a745
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js
@@ -0,0 +1,2 @@
+/* Doesn't really matter what is in here. */
+// # sourceMappingURL=empty.js.map
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js.map
new file mode 100644
index 0000000000..9ebfec598d
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/empty.js.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "sources": [
+ "heart.js"
+ ],
+ "names": [],
+ "mappings": ";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA,QAAO,SAAS;AAChB;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;ACfA;AACA;AACA;;;;;;;ACFA;AACA;AACA;;AAEA,mBAAkB;;;;;;;ACJlB;AACA;AACA",
+ "file": "empty.js",
+ "sourceRoot": ""
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/if.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/if.js
new file mode 100644
index 0000000000..4ee69b19ef
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/if.js
@@ -0,0 +1,12 @@
+function componentWillReceiveProps(nextProps) {
+ console.log("start");
+ const { selectedSource } = nextProps;
+
+ if (
+ nextProps.startPanelSize !== this.props.startPanelSize ||
+ nextProps.endPanelSize !== this.props.endPanelSize
+ ) {
+ this.state.editor.codeMirror.setSize();
+ }
+ console.log("done");
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js
new file mode 100644
index 0000000000..9bb0752a92
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js
@@ -0,0 +1,16 @@
+"use strict";
+
+function componentWillReceiveProps(nextProps) {
+ console.log("start");
+ var selectedSource = nextProps.selectedSource;
+
+ if (
+ nextProps.startPanelSize !== this.props.startPanelSize ||
+ nextProps.endPanelSize !== this.props.endPanelSize
+ ) {
+ this.state.editor.codeMirror.setSize();
+ }
+ console.log("done");
+}
+
+//# sourceMappingURL=if.out.js.map
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js.map
new file mode 100644
index 0000000000..27feb5278f
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/if.out.js.map
@@ -0,0 +1,7 @@
+{
+ "version":3,
+ "sources":["if.js"],
+ "names":[],
+ "mappings":";;AAAA,SAAS,yBAAT,CAAmC,SAAnC,EAA8C;AAC7C,UAAQ,GAAR,CAAY,OAAZ;AAD6C,MAElC,cAFkC,GAEf,SAFe,CAElC,cAFkC;;;AAI1C,MACE,UAAU,cAAV,KAA6B,KAAK,KAAL,CAAW,cAAxC,IACA,UAAU,YAAV,KAA2B,KAAK,KAAL,CAAW,YAFxC,EAGE;AACA,SAAK,KAAL,CAAW,MAAX,CAAkB,UAAlB,CAA6B,OAA7B;AACD;AACJ,UAAQ,GAAR,CAAY,MAAZ;AACA","file":"if.out.js",
+ "sourcesContent":["function componentWillReceiveProps(nextProps) {\n\tconsole.log('start');\n const { selectedSource } = nextProps;\n\n if (\n nextProps.startPanelSize !== this.props.startPanelSize ||\n nextProps.endPanelSize !== this.props.endPanelSize\n ) {\n this.state.editor.codeMirror.setSize();\n }\n\tconsole.log('done');\n}\n"]
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js
new file mode 100644
index 0000000000..c3d9a150bd
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js
@@ -0,0 +1,62 @@
+"use strict";
+
+var decl = (function() {
+ var _ref = _asyncToGenerator(
+ /*#__PURE__*/ regeneratorRuntime.mark(function _callee() {
+ return regeneratorRuntime.wrap(
+ function _callee$(_context) {
+ while (1) {
+ switch ((_context.prev = _context.next)) {
+ case 0:
+ console.log("2");
+
+ case 1:
+ case "end":
+ return _context.stop();
+ }
+ }
+ },
+ _callee,
+ this
+ );
+ })
+ );
+
+ return function decl() {
+ return _ref.apply(this, arguments);
+ };
+})();
+
+function _asyncToGenerator(fn) {
+ return function() {
+ var gen = fn.apply(this, arguments);
+ return new Promise(function(resolve, reject) {
+ function step(key, arg) {
+ try {
+ var info = gen[key](arg);
+ var value = info.value;
+ } catch (error) {
+ reject(error);
+ return;
+ }
+ if (info.done) {
+ resolve(value);
+ } else {
+ return Promise.resolve(value).then(
+ function(value) {
+ step("next", value);
+ },
+ function(err) {
+ step("throw", err);
+ }
+ );
+ }
+ }
+ return step("next");
+ });
+ };
+}
+
+console.log("1");
+
+console.log("3");
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js.map
new file mode 100644
index 0000000000..2876b9900b
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/intermingled-sources.js.map
@@ -0,0 +1,8 @@
+{
+ "version":3,
+ "file":"output.js",
+ "mappings":";;;qEAEA;AAAA;AAAA;AAAA;AAAA;AACE,oBAAQ,GAAR,CAAY,GAAZ;;AADF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,G;;kBAAe,I;;;;;;;AAFf,QAAQ,GAAR,CAAY,GAAZ;;AAMA,QAAQ,GAAR,CAAY,GAAZ",
+ "sources":["input.js"],
+ "sourcesContent":["console.log(\"1\")\n\nasync function decl(){\n console.log(\"2\");\n}\n\nconsole.log(\"3\");"],
+ "names":[]
+} \ No newline at end of file
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/missingmap.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/missingmap.js
new file mode 100644
index 0000000000..5a162652a7
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/missingmap.js
@@ -0,0 +1,2 @@
+// Source where the map is missing.
+// # sourceMappingURL=missingmap.js.map
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js
new file mode 100644
index 0000000000..33d15cf494
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js
@@ -0,0 +1,2 @@
+/* Doesn't really matter what is in here. */
+// # sourceMappingURL=noroot.js.map
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js.map
new file mode 100644
index 0000000000..dfeee2d765
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot.js.map
@@ -0,0 +1,9 @@
+{
+ "version": 3,
+ "sources": [
+ "heart.js"
+ ],
+ "names": [],
+ "mappings": ";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA,QAAO,SAAS;AAChB;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;ACfA;AACA;AACA;;;;;;;ACFA;AACA;AACA;;AAEA,mBAAkB;;;;;;;ACJlB;AACA;AACA",
+ "file": "noroot.js"
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js
new file mode 100644
index 0000000000..35aa77b92c
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js
@@ -0,0 +1,2 @@
+/* Doesn't really matter what is in here. */
+// # sourceMappingURL=noroot2.js.map
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js.map
new file mode 100644
index 0000000000..1d22100178
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/noroot2.js.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "sources": [
+ "heart.js"
+ ],
+ "names": [],
+ "mappings": ";AAAA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA,uBAAe;AACf;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;;;;;;;ACtCA;AACA,QAAO,SAAS;AAChB;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;;;;;;;ACfA;AACA;AACA;;;;;;;ACFA;AACA;AACA;;AAEA,mBAAkB;;;;;;;ACJlB;AACA;AACA",
+ "file": "noroot.js",
+ "sourceContents": ["this is the full text"]
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/fixtures/wasm.js.map b/devtools/client/shared/source-map-loader/test/browser/fixtures/wasm.js.map
new file mode 100644
index 0000000000..828f6a9aa4
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/fixtures/wasm.js.map
@@ -0,0 +1,7 @@
+{
+ "version": 3,
+ "file": "wasm.js",
+ "names": [],
+ "sources": ["one.js"],
+ "mappings": "CAAC,IAAM"
+}
diff --git a/devtools/client/shared/source-map-loader/test/browser/head.js b/devtools/client/shared/source-map-loader/test/browser/head.js
new file mode 100644
index 0000000000..0f54e5a5f5
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/test/browser/head.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+ this
+);
+
+const {
+ SourceMapLoader,
+} = require("resource://devtools/client/shared/source-map-loader/index.js");
+
+const gSourceMapLoader = new SourceMapLoader();
+
+function fetchFixtureSourceMap(name) {
+ gSourceMapLoader.clearSourceMaps();
+
+ const source = {
+ id: `${name}.js`,
+ sourceMapURL: `${name}.js.map`,
+ sourceMapBaseURL: `${URL_ROOT_SSL}fixtures/${name}.js`,
+ };
+
+ return gSourceMapLoader.getOriginalURLs(source);
+}
diff --git a/devtools/client/shared/source-map-loader/utils/assert.js b/devtools/client/shared/source-map-loader/utils/assert.js
new file mode 100644
index 0000000000..e784100796
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/assert.js
@@ -0,0 +1,13 @@
+/* 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/>. */
+
+"use strict";
+
+function assert(condition, message) {
+ if (!condition) {
+ throw new Error(`Assertion failure: ${message}`);
+ }
+}
+
+module.exports = assert;
diff --git a/devtools/client/shared/source-map-loader/utils/fetchSourceMap.js b/devtools/client/shared/source-map-loader/utils/fetchSourceMap.js
new file mode 100644
index 0000000000..0fd8b15c25
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/fetchSourceMap.js
@@ -0,0 +1,139 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ networkRequest,
+} = require("resource://devtools/client/shared/source-map-loader/utils/network-request");
+
+const {
+ SourceMapConsumer,
+} = require("resource://devtools/client/shared/vendor/source-map/source-map.js");
+const {
+ getSourceMap,
+ setSourceMap,
+} = require("resource://devtools/client/shared/source-map-loader/utils/sourceMapRequests");
+const {
+ WasmRemap,
+} = require("resource://devtools/client/shared/source-map-loader/utils/wasmRemap");
+const {
+ convertToJSON,
+} = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON");
+
+// URLs which have been seen in a completed source map request.
+const originalURLs = new Set();
+
+function clearOriginalURLs() {
+ originalURLs.clear();
+}
+
+function hasOriginalURL(url) {
+ return originalURLs.has(url);
+}
+
+function resolveSourceMapURL(source) {
+ let { sourceMapBaseURL, sourceMapURL } = source;
+ sourceMapBaseURL = sourceMapBaseURL || "";
+ sourceMapURL = sourceMapURL || "";
+
+ if (!sourceMapBaseURL) {
+ // If the source doesn't have a URL, don't resolve anything.
+ return { resolvedSourceMapURL: sourceMapURL, baseURL: sourceMapURL };
+ }
+
+ let resolvedString;
+ let baseURL;
+
+ // When the sourceMap is a data: URL, fall back to using the source's URL,
+ // if possible. We don't use `new URL` here because it will be _very_ slow
+ // for large inlined source-maps, and we don't actually need to parse them.
+ if (sourceMapURL.startsWith("data:")) {
+ resolvedString = sourceMapURL;
+ baseURL = sourceMapBaseURL;
+ } else {
+ resolvedString = new URL(
+ sourceMapURL,
+ // If the URL is a data: URL, the sourceMapURL needs to be absolute, so
+ // we might as well pass `undefined` to avoid parsing a potentially
+ // very large data: URL for no reason.
+ sourceMapBaseURL.startsWith("data:") ? undefined : sourceMapBaseURL
+ ).toString();
+ baseURL = resolvedString;
+ }
+
+ return { resolvedSourceMapURL: resolvedString, baseURL };
+}
+
+async function _fetch(generatedSource, resolvedSourceMapURL, baseURL) {
+ let fetched = await networkRequest(resolvedSourceMapURL, {
+ loadFromCache: false,
+ // Blocking redirects on the sourceMappingUrl as its not easy to verify if the
+ // redirect protocol matches the supported ones.
+ allowRedirects: false,
+ sourceMapBaseURL: generatedSource.sourceMapBaseURL,
+ });
+
+ if (fetched.isDwarf) {
+ fetched = { content: await convertToJSON(fetched.content) };
+ }
+
+ // Create the source map and fix it up.
+ let map = await new SourceMapConsumer(fetched.content, baseURL);
+
+ if (generatedSource.isWasm) {
+ map = new WasmRemap(map);
+ // Check if experimental scope info exists.
+ if (fetched.content.includes("x-scopes")) {
+ const parsedJSON = JSON.parse(fetched.content);
+ map.xScopes = parsedJSON["x-scopes"];
+ }
+ }
+
+ // Extend the default map object with sourceMapBaseURL, used to check further
+ // network requests made for this sourcemap.
+ map.sourceMapBaseURL = generatedSource.sourceMapBaseURL;
+
+ if (map && map.sources) {
+ map.sources.forEach(url => originalURLs.add(url));
+ }
+
+ return map;
+}
+
+function fetchSourceMap(generatedSource, resolvedSourceMapURL, baseURL) {
+ const existingRequest = getSourceMap(generatedSource.id);
+
+ // If it has already been requested, return the request. Make sure
+ // to do this even if sourcemapping is turned off, because
+ // pretty-printing uses sourcemaps.
+ //
+ // An important behavior here is that if it's in the middle of
+ // requesting it, all subsequent calls will block on the initial
+ // request.
+ if (existingRequest) {
+ return existingRequest;
+ }
+
+ if (!generatedSource.sourceMapURL) {
+ return null;
+ }
+
+ // Fire off the request, set it in the cache, and return it.
+ const req = _fetch(generatedSource, resolvedSourceMapURL, baseURL);
+ // Make sure the cached promise does not reject, because we only
+ // want to report the error once.
+ setSourceMap(
+ generatedSource.id,
+ req.catch(() => null)
+ );
+ return req;
+}
+
+module.exports = {
+ fetchSourceMap,
+ hasOriginalURL,
+ clearOriginalURLs,
+ resolveSourceMapURL,
+};
diff --git a/devtools/client/shared/source-map-loader/utils/getOriginalStackFrames.js b/devtools/client/shared/source-map-loader/utils/getOriginalStackFrames.js
new file mode 100644
index 0000000000..c1ffee10e4
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/getOriginalStackFrames.js
@@ -0,0 +1,38 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ getWasmXScopes,
+} = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes");
+const {
+ getSourceMap,
+} = require("resource://devtools/client/shared/source-map-loader/utils/sourceMapRequests");
+const {
+ generatedToOriginalId,
+} = require("resource://devtools/client/shared/source-map-loader/utils/index");
+
+// Returns expanded stack frames details based on the generated location.
+// The function return null if not information was found.
+async function getOriginalStackFrames(generatedLocation) {
+ const wasmXScopes = await getWasmXScopes(generatedLocation.sourceId, {
+ getSourceMap,
+ generatedToOriginalId,
+ });
+ if (!wasmXScopes) {
+ return null;
+ }
+
+ const scopes = wasmXScopes.search(generatedLocation);
+ if (scopes.length === 0) {
+ console.warn("Something wrong with debug data: none original frames found");
+ return null;
+ }
+ return scopes;
+}
+
+module.exports = {
+ getOriginalStackFrames,
+};
diff --git a/devtools/client/shared/source-map-loader/utils/index.js b/devtools/client/shared/source-map-loader/utils/index.js
new file mode 100644
index 0000000000..1bcfcfac7d
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/index.js
@@ -0,0 +1,103 @@
+/* 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/>. */
+
+"use strict";
+
+const md5 = require("resource://devtools/client/shared/vendor/md5.js");
+
+function originalToGeneratedId(sourceId) {
+ if (isGeneratedId(sourceId)) {
+ return sourceId;
+ }
+
+ const lastIndex = sourceId.lastIndexOf("/originalSource");
+
+ return lastIndex !== -1 ? sourceId.slice(0, lastIndex) : "";
+}
+
+const getMd5 = memoize(url => md5(url));
+
+function generatedToOriginalId(generatedId, url) {
+ return `${generatedId}/originalSource-${getMd5(url)}`;
+}
+
+function isOriginalId(id) {
+ return id.includes("/originalSource");
+}
+
+function isGeneratedId(id) {
+ return !isOriginalId(id);
+}
+
+/**
+ * Trims the query part or reference identifier of a URL string, if necessary.
+ */
+function trimUrlQuery(url) {
+ const length = url.length;
+
+ for (let i = 0; i < length; ++i) {
+ if (url[i] === "?" || url[i] === "&" || url[i] === "#") {
+ return url.slice(0, i);
+ }
+ }
+
+ return url;
+}
+
+// Map suffix to content type.
+const contentMap = {
+ js: "text/javascript",
+ jsm: "text/javascript",
+ mjs: "text/javascript",
+ ts: "text/typescript",
+ tsx: "text/typescript-jsx",
+ jsx: "text/jsx",
+ vue: "text/vue",
+ coffee: "text/coffeescript",
+ elm: "text/elm",
+ cljc: "text/x-clojure",
+ cljs: "text/x-clojurescript",
+};
+
+/**
+ * Returns the content type for the specified URL. If no specific
+ * content type can be determined, "text/plain" is returned.
+ *
+ * @return String
+ * The content type.
+ */
+function getContentType(url) {
+ url = trimUrlQuery(url);
+ const dot = url.lastIndexOf(".");
+ if (dot >= 0) {
+ const name = url.substring(dot + 1);
+ if (name in contentMap) {
+ return contentMap[name];
+ }
+ }
+ return "text/plain";
+}
+
+function memoize(func) {
+ const map = new Map();
+
+ return arg => {
+ if (map.has(arg)) {
+ return map.get(arg);
+ }
+
+ const result = func(arg);
+ map.set(arg, result);
+ return result;
+ };
+}
+
+module.exports = {
+ originalToGeneratedId,
+ generatedToOriginalId,
+ isOriginalId,
+ isGeneratedId,
+ getContentType,
+ contentMapForTesting: contentMap,
+};
diff --git a/devtools/client/shared/source-map-loader/utils/moz.build b/devtools/client/shared/source-map-loader/utils/moz.build
new file mode 100644
index 0000000000..ffa77d72b7
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "assert.js",
+ "fetchSourceMap.js",
+ "getOriginalStackFrames.js",
+ "index.js",
+ "network-request.js",
+ "sourceMapRequests.js",
+ "wasmRemap.js",
+)
diff --git a/devtools/client/shared/source-map-loader/utils/network-request.js b/devtools/client/shared/source-map-loader/utils/network-request.js
new file mode 100644
index 0000000000..f331dc446e
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/network-request.js
@@ -0,0 +1,43 @@
+/* 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/>. */
+
+"use strict";
+
+async function networkRequest(url, opts) {
+ const supportedProtocols = ["http:", "https:", "data:"];
+
+ // Add file, chrome or moz-extension if the initial source was served by the
+ // same protocol.
+ const ADDITIONAL_PROTOCOLS = ["chrome:", "file:", "moz-extension:"];
+ for (const protocol of ADDITIONAL_PROTOCOLS) {
+ if (opts.sourceMapBaseURL?.startsWith(protocol)) {
+ supportedProtocols.push(protocol);
+ }
+ }
+
+ if (supportedProtocols.every(protocol => !url.startsWith(protocol))) {
+ throw new Error(`unsupported protocol for sourcemap request ${url}`);
+ }
+
+ const response = await fetch(url, {
+ cache: opts.loadFromCache ? "default" : "no-cache",
+ redirect: opts.allowRedirects ? "follow" : "error",
+ });
+
+ if (response.ok) {
+ if (response.headers.get("Content-Type") === "application/wasm") {
+ const buffer = await response.arrayBuffer();
+ return {
+ content: buffer,
+ isDwarf: true,
+ };
+ }
+ const text = await response.text();
+ return { content: text };
+ }
+
+ throw new Error(`request failed with status ${response.status}`);
+}
+
+module.exports = { networkRequest };
diff --git a/devtools/client/shared/source-map-loader/utils/sourceMapRequests.js b/devtools/client/shared/source-map-loader/utils/sourceMapRequests.js
new file mode 100644
index 0000000000..95f0420cba
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/sourceMapRequests.js
@@ -0,0 +1,96 @@
+/* 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/>. */
+
+"use strict";
+
+const {
+ generatedToOriginalId,
+} = require("resource://devtools/client/shared/source-map-loader/utils/index.js");
+
+const sourceMapRequests = new Map();
+
+function clearSourceMaps() {
+ for (const [, metadataPromise] of sourceMapRequests) {
+ // The source-map module leaks memory unless `.destroy` is called on
+ // the consumer instances when they are no longer being used.
+ metadataPromise.then(
+ metadata => {
+ if (metadata) {
+ metadata.map.destroy();
+ }
+ },
+ // We don't want this to cause any unhandled rejection errors.
+ () => {}
+ );
+ }
+
+ sourceMapRequests.clear();
+}
+
+/**
+ * For a given generated source, retrieve an object with many attributes:
+ * @param {String} generatedSourceId
+ * The id of the generated source
+ *
+ * @return {Object} Meta data object with many attributes
+ * - map: The SourceMapConsumer or WasmRemap instance
+ * - urlsById Map of Original Source ID (string) to Source URL (string)
+ * - sources: Array of object with the two following attributes:
+ * - id: Original Source ID (string)
+ * - url: Original Source URL (string)
+ */
+function getSourceMapWithMetadata(generatedSourceId) {
+ return sourceMapRequests.get(generatedSourceId);
+}
+
+/**
+ * Retrieve the SourceMapConsumer or WasmRemap instance for a given generated source.
+ *
+ * @param {String} generatedSourceId
+ * The id of the generated source
+ *
+ * @return null | Promise<SourceMapConsumer | WasmRemap>
+ */
+function getSourceMap(generatedSourceId) {
+ const request = getSourceMapWithMetadata(generatedSourceId);
+ if (!request) {
+ return null;
+ }
+
+ return request.then(result => result?.map);
+}
+
+/**
+ * Record the SourceMapConsumer or WasmRemap instance for a given generated source.
+ *
+ * @param {String} generatedId
+ * The generated source ID.
+ * @param {Promise<SourceMapConsumer or WasmRemap>} request
+ * A promise which should resolve to either a SourceMapConsume or WasmRemap instance.
+ */
+function setSourceMap(generatedId, request) {
+ sourceMapRequests.set(
+ generatedId,
+ request.then(map => {
+ if (!map || !map.sources) {
+ return null;
+ }
+
+ const urlsById = new Map();
+
+ for (const url of map.sources) {
+ const id = generatedToOriginalId(generatedId, url);
+ urlsById.set(id, url);
+ }
+ return { map, urlsById };
+ })
+ );
+}
+
+module.exports = {
+ clearSourceMaps,
+ getSourceMapWithMetadata,
+ getSourceMap,
+ setSourceMap,
+};
diff --git a/devtools/client/shared/source-map-loader/utils/wasmRemap.js b/devtools/client/shared/source-map-loader/utils/wasmRemap.js
new file mode 100644
index 0000000000..6b831c2919
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/utils/wasmRemap.js
@@ -0,0 +1,107 @@
+/* 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/>. */
+
+"use strict";
+
+/**
+ * SourceMapConsumer for WebAssembly source maps. It transposes columns with
+ * lines, which allows mapping data to be used with SpiderMonkey Debugger API.
+ */
+class WasmRemap {
+ /**
+ * @param map SourceMapConsumer
+ */
+ constructor(map) {
+ this._map = map;
+ this.version = map.version;
+ this.file = map.file;
+ this._computeColumnSpans = false;
+ }
+
+ get sources() {
+ return this._map.sources;
+ }
+
+ get sourceRoot() {
+ return this._map.sourceRoot;
+ }
+
+ get names() {
+ return this._map.names;
+ }
+
+ get sourcesContent() {
+ return this._map.sourcesContent;
+ }
+
+ get mappings() {
+ throw new Error("not supported");
+ }
+
+ computeColumnSpans() {
+ this._computeColumnSpans = true;
+ }
+
+ originalPositionFor(generatedPosition) {
+ const result = this._map.originalPositionFor({
+ line: 1,
+ column: generatedPosition.line,
+ bias: generatedPosition.bias,
+ });
+ return result;
+ }
+
+ _remapGeneratedPosition(position) {
+ const generatedPosition = {
+ line: position.column,
+ column: 0,
+ };
+ if (this._computeColumnSpans) {
+ generatedPosition.lastColumn = 0;
+ }
+ return generatedPosition;
+ }
+
+ generatedPositionFor(originalPosition) {
+ const position = this._map.generatedPositionFor(originalPosition);
+ return this._remapGeneratedPosition(position);
+ }
+
+ allGeneratedPositionsFor(originalPosition) {
+ const positions = this._map.allGeneratedPositionsFor(originalPosition);
+ return positions.map(position => {
+ return this._remapGeneratedPosition(position);
+ });
+ }
+
+ hasContentsOfAllSources() {
+ return this._map.hasContentsOfAllSources();
+ }
+
+ sourceContentFor(source, returnNullOnMissing) {
+ return this._map.sourceContentFor(source, returnNullOnMissing);
+ }
+
+ eachMapping(callback, context, order) {
+ this._map.eachMapping(
+ entry => {
+ const { source, generatedColumn, originalLine, originalColumn, name } =
+ entry;
+ callback({
+ source,
+ generatedLine: generatedColumn,
+ generatedColumn: 0,
+ lastGeneratedColumn: 0,
+ originalLine,
+ originalColumn,
+ name,
+ });
+ },
+ context,
+ order
+ );
+ }
+}
+
+exports.WasmRemap = WasmRemap;
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js b/devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js
new file mode 100644
index 0000000000..9faae5c901
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/convertToJSON.js
@@ -0,0 +1,66 @@
+/* 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/>. */
+
+/* eslint camelcase: 0*/
+
+"use strict";
+
+const {
+ getDwarfToWasmData,
+} = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js");
+
+let cachedWasmModule;
+let utf8Decoder;
+
+function convertDwarf(wasm, instance) {
+ const { memory, alloc_mem, free_mem, convert_dwarf } = instance.exports;
+ const wasmPtr = alloc_mem(wasm.byteLength);
+ new Uint8Array(memory.buffer, wasmPtr, wasm.byteLength).set(
+ new Uint8Array(wasm)
+ );
+ const resultPtr = alloc_mem(12);
+ const enableXScopes = true;
+ const success = convert_dwarf(
+ wasmPtr,
+ wasm.byteLength,
+ resultPtr,
+ resultPtr + 4,
+ enableXScopes
+ );
+ free_mem(wasmPtr);
+ const resultView = new DataView(memory.buffer, resultPtr, 12);
+ const outputPtr = resultView.getUint32(0, true),
+ outputLen = resultView.getUint32(4, true);
+ free_mem(resultPtr);
+ if (!success) {
+ throw new Error("Unable to convert from DWARF sections");
+ }
+ if (!utf8Decoder) {
+ utf8Decoder = new TextDecoder("utf-8");
+ }
+ const output = utf8Decoder.decode(
+ new Uint8Array(memory.buffer, outputPtr, outputLen)
+ );
+ free_mem(outputPtr);
+ return output;
+}
+
+async function convertToJSON(buffer) {
+ // Note: We don't 'await' here because it could mean that multiple
+ // calls to 'convertToJSON' could cause multiple fetches to be started.
+ cachedWasmModule = cachedWasmModule || loadConverterModule();
+
+ return convertDwarf(buffer, await cachedWasmModule);
+}
+
+async function loadConverterModule() {
+ const wasm = await getDwarfToWasmData();
+ const imports = {};
+ const { instance } = await WebAssembly.instantiate(wasm, imports);
+ return instance;
+}
+
+module.exports = {
+ convertToJSON,
+};
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm b/devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm
new file mode 100644
index 0000000000..c58b189035
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm
Binary files differ
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/moz.build b/devtools/client/shared/source-map-loader/wasm-dwarf/moz.build
new file mode 100644
index 0000000000..8bc40676fa
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "convertToJSON.js",
+ "wasmAsset.js",
+ "wasmDwarfExpressions.js",
+ "wasmXScopes.js",
+)
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js
new file mode 100644
index 0000000000..b4de822ffd
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmAsset.js
@@ -0,0 +1,17 @@
+/* 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/>. */
+
+"use strict";
+
+async function getDwarfToWasmData(name) {
+ const response = await fetch(
+ "resource://devtools/client/shared/source-map-loader/wasm-dwarf/dwarf_to_json.wasm"
+ );
+
+ return response.arrayBuffer();
+}
+
+module.exports = {
+ getDwarfToWasmData,
+};
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js
new file mode 100644
index 0000000000..c2f124e99f
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions.js
@@ -0,0 +1,260 @@
+/* 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/>. */
+
+/* eslint camelcase: 0*/
+/* eslint-disable no-inline-comments */
+
+"use strict";
+
+class Value {
+ val;
+
+ constructor(val) {
+ this.val = val;
+ }
+ toString() {
+ return `${this.val}`;
+ }
+}
+
+const Int32Formatter = {
+ fromAddr(addr) {
+ return `(new DataView(memory0.buffer).getInt32(${addr}, true))`;
+ },
+ fromValue(value) {
+ return `${value.val}`;
+ },
+};
+
+const Uint32Formatter = {
+ fromAddr(addr) {
+ return `(new DataView(memory0.buffer).getUint32(${addr}, true))`;
+ },
+ fromValue(value) {
+ return `(${value.val} >>> 0)`;
+ },
+};
+
+function createPieceFormatter(bytes) {
+ let getter;
+ switch (bytes) {
+ case 0:
+ case 1:
+ getter = "getUint8";
+ break;
+ case 2:
+ getter = "getUint16";
+ break;
+ case 3:
+ case 4:
+ default:
+ // FIXME 64-bit
+ getter = "getUint32";
+ break;
+ }
+ const mask = (1 << (8 * bytes)) - 1;
+ return {
+ fromAddr(addr) {
+ return `(new DataView(memory0.buffer).${getter}(${addr}, true))`;
+ },
+ fromValue(value) {
+ return `((${value.val} & ${mask}) >>> 0)`;
+ },
+ };
+}
+
+// eslint-disable-next-line complexity
+function toJS(buf, typeFormatter, frame_base = "fp()") {
+ const readU8 = function () {
+ return buf[i++];
+ };
+ const readS8 = function () {
+ return (readU8() << 24) >> 24;
+ };
+ const readU16 = function () {
+ const w = buf[i] | (buf[i + 1] << 8);
+ i += 2;
+ return w;
+ };
+ const readS16 = function () {
+ return (readU16() << 16) >> 16;
+ };
+ const readS32 = function () {
+ const w =
+ buf[i] | (buf[i + 1] << 8) | (buf[i + 2] << 16) | (buf[i + 3] << 24);
+ i += 4;
+ return w;
+ };
+ const readU32 = function () {
+ return readS32() >>> 0;
+ };
+ const readU = function () {
+ let n = 0,
+ shift = 0,
+ b;
+ while ((b = readU8()) & 0x80) {
+ n |= (b & 0x7f) << shift;
+ shift += 7;
+ }
+ return n | (b << shift);
+ };
+ const readS = function () {
+ let n = 0,
+ shift = 0,
+ b;
+ while ((b = readU8()) & 0x80) {
+ n |= (b & 0x7f) << shift;
+ shift += 7;
+ }
+ n |= b << shift;
+ shift += 7;
+ return shift > 32 ? (n << (32 - shift)) >> (32 - shift) : n;
+ };
+ const popValue = function (formatter) {
+ const loc = stack.pop();
+ if (loc instanceof Value) {
+ return formatter.fromValue(loc);
+ }
+ return formatter.fromAddr(loc);
+ };
+ let i = 0,
+ a,
+ b;
+ const stack = [frame_base];
+ while (i < buf.length) {
+ const code = buf[i++];
+ switch (code) {
+ case 0x03 /* DW_OP_addr */:
+ stack.push(Uint32Formatter.fromAddr(readU32()));
+ break;
+ case 0x08 /* DW_OP_const1u 0x08 1 1-byte constant */:
+ stack.push(readU8());
+ break;
+ case 0x09 /* DW_OP_const1s 0x09 1 1-byte constant */:
+ stack.push(readS8());
+ break;
+ case 0x0a /* DW_OP_const2u 0x0a 1 2-byte constant */:
+ stack.push(readU16());
+ break;
+ case 0x0b /* DW_OP_const2s 0x0b 1 2-byte constant */:
+ stack.push(readS16());
+ break;
+ case 0x0c /* DW_OP_const2u 0x0a 1 2-byte constant */:
+ stack.push(readU32());
+ break;
+ case 0x0d /* DW_OP_const2s 0x0b 1 2-byte constant */:
+ stack.push(readS32());
+ break;
+ case 0x10 /* DW_OP_constu 0x10 1 ULEB128 constant */:
+ stack.push(readU());
+ break;
+ case 0x11 /* DW_OP_const2s 0x0b 1 2-byte constant */:
+ stack.push(readS());
+ break;
+
+ case 0x1c /* DW_OP_minus */:
+ b = stack.pop();
+ a = stack.pop();
+ stack.push(`${a} - ${b}`);
+ break;
+
+ case 0x22 /* DW_OP_plus */:
+ b = stack.pop();
+ a = stack.pop();
+ stack.push(`${a} + ${b}`);
+ break;
+
+ case 0x23 /* DW_OP_plus_uconst */:
+ b = readU();
+ a = stack.pop();
+ stack.push(`${a} + ${b}`);
+ break;
+
+ case 0x30 /* DW_OP_lit0 */:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35:
+ case 0x36:
+ case 0x37:
+ case 0x38:
+ case 0x39:
+ case 0x3a:
+ case 0x3b:
+ case 0x3c:
+ case 0x3d:
+ case 0x3e:
+ case 0x3f:
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ stack.push(`${code - 0x30}`);
+ break;
+
+ case 0x93 /* DW_OP_piece */: {
+ a = readU();
+ const formatter = createPieceFormatter(a);
+ stack.push(popValue(formatter));
+ break;
+ }
+
+ case 0x9f /* DW_OP_stack_value */:
+ stack.push(new Value(stack.pop()));
+ break;
+
+ case 0xf6 /* WASM ext (old, FIXME phase out) */:
+ case 0xed /* WASM ext */:
+ b = readU();
+ a = readS();
+ switch (b) {
+ case 0:
+ stack.push(`var${a}`);
+ break;
+ case 1:
+ stack.push(`global${a}`);
+ break;
+ default:
+ stack.push(`ti${b}(${a})`);
+ break;
+ }
+ break;
+
+ default:
+ // Unknown encoding, baling out
+ return null;
+ }
+ }
+ // FIXME use real DWARF type information
+ return popValue(typeFormatter);
+}
+
+function decodeExpr(expr) {
+ if (expr.includes("//")) {
+ expr = expr.slice(0, expr.indexOf("//")).trim();
+ }
+ const code = new Uint8Array(expr.length >> 1);
+ for (let i = 0; i < code.length; i++) {
+ code[i] = parseInt(expr.substr(i << 1, 2), 16);
+ }
+ const typeFormatter = Int32Formatter;
+ return toJS(code, typeFormatter) || `dwarf("${expr}")`;
+}
+
+module.exports = {
+ decodeExpr,
+};
diff --git a/devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js
new file mode 100644
index 0000000000..82faa51dbe
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/wasm-dwarf/wasmXScopes.js
@@ -0,0 +1,215 @@
+/* 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/>. */
+
+/* eslint camelcase: 0*/
+
+"use strict";
+
+const {
+ decodeExpr,
+} = require("resource://devtools/client/shared/source-map-loader/wasm-dwarf/wasmDwarfExpressions");
+
+const xScopes = new Map();
+
+function indexLinkingNames(items) {
+ const result = new Map();
+ let queue = [...items];
+ while (queue.length) {
+ const item = queue.shift();
+ if ("uid" in item) {
+ result.set(item.uid, item);
+ } else if ("linkage_name" in item) {
+ // TODO the linkage_name string value is used for compatibility
+ // with old format. Remove in favour of the uid referencing.
+ result.set(item.linkage_name, item);
+ }
+ if ("children" in item) {
+ queue = [...queue, ...item.children];
+ }
+ }
+ return result;
+}
+
+function getIndexedItem(index, key) {
+ if (typeof key === "object" && key != null) {
+ return index.get(key.uid);
+ }
+ if (typeof key === "string") {
+ return index.get(key);
+ }
+ return null;
+}
+
+async function getXScopes(sourceId, getSourceMap) {
+ if (xScopes.has(sourceId)) {
+ return xScopes.get(sourceId);
+ }
+ const map = await getSourceMap(sourceId);
+ if (!map || !map.xScopes) {
+ xScopes.set(sourceId, null);
+ return null;
+ }
+ const { code_section_offset, debug_info } = map.xScopes;
+ const xScope = {
+ code_section_offset,
+ debug_info,
+ idIndex: indexLinkingNames(debug_info),
+ sources: map.sources,
+ };
+ xScopes.set(sourceId, xScope);
+ return xScope;
+}
+
+function isInRange(item, pc) {
+ if ("ranges" in item) {
+ return item.ranges.some(r => r[0] <= pc && pc < r[1]);
+ }
+ if ("high_pc" in item) {
+ return item.low_pc <= pc && pc < item.high_pc;
+ }
+ return false;
+}
+
+function decodeExprAt(expr, pc) {
+ if (typeof expr === "string") {
+ return decodeExpr(expr);
+ }
+ const foundAt = expr.find(i => i.range[0] <= pc && pc < i.range[1]);
+ return foundAt ? decodeExpr(foundAt.expr) : null;
+}
+
+function getVariables(scope, pc) {
+ const vars = scope.children
+ ? scope.children.reduce((result, item) => {
+ switch (item.tag) {
+ case "variable":
+ case "formal_parameter":
+ result.push({
+ name: item.name || "",
+ expr: item.location ? decodeExprAt(item.location, pc) : null,
+ });
+ break;
+ case "lexical_block":
+ // FIXME build scope blocks (instead of combining)
+ const tmp = getVariables(item, pc);
+ result = [...tmp.vars, ...result];
+ break;
+ }
+ return result;
+ }, [])
+ : [];
+ const frameBase = scope.frame_base ? decodeExpr(scope.frame_base) : null;
+ return {
+ vars,
+ frameBase,
+ };
+}
+
+function filterScopes(items, pc, lastItem, index) {
+ if (!items) {
+ return [];
+ }
+ return items.reduce((result, item) => {
+ switch (item.tag) {
+ case "compile_unit":
+ if (isInRange(item, pc)) {
+ result = [
+ ...result,
+ ...filterScopes(item.children, pc, lastItem, index),
+ ];
+ }
+ break;
+ case "namespace":
+ case "structure_type":
+ case "union_type":
+ result = [
+ ...result,
+ ...filterScopes(item.children, pc, lastItem, index),
+ ];
+ break;
+ case "subprogram":
+ if (isInRange(item, pc)) {
+ const s = {
+ id: item.linkage_name,
+ name: item.name,
+ variables: getVariables(item, pc),
+ };
+ result = [...result, s, ...filterScopes(item.children, pc, s, index)];
+ }
+ break;
+ case "inlined_subroutine":
+ if (isInRange(item, pc)) {
+ const linkedItem = getIndexedItem(index, item.abstract_origin);
+ const s = {
+ id: item.abstract_origin,
+ name: linkedItem ? linkedItem.name : void 0,
+ variables: getVariables(item, pc),
+ };
+ if (lastItem) {
+ lastItem.file = item.call_file;
+ lastItem.line = item.call_line;
+ }
+ result = [...result, s, ...filterScopes(item.children, pc, s, index)];
+ }
+ break;
+ }
+ return result;
+ }, []);
+}
+
+class XScope {
+ xScope;
+ sourceMapContext;
+
+ constructor(xScopeData, sourceMapContext) {
+ this.xScope = xScopeData;
+ this.sourceMapContext = sourceMapContext;
+ }
+
+ search(generatedLocation) {
+ const { code_section_offset, debug_info, sources, idIndex } = this.xScope;
+ const pc = generatedLocation.line - (code_section_offset || 0);
+ const scopes = filterScopes(debug_info, pc, null, idIndex);
+ scopes.reverse();
+
+ return scopes.map(i => {
+ if (!("file" in i)) {
+ return {
+ displayName: i.name || "",
+ variables: i.variables,
+ };
+ }
+ const sourceId = this.sourceMapContext.generatedToOriginalId(
+ generatedLocation.sourceId,
+ sources[i.file || 0]
+ );
+ return {
+ displayName: i.name || "",
+ variables: i.variables,
+ location: {
+ line: i.line || 0,
+ sourceId,
+ },
+ };
+ });
+ }
+}
+
+async function getWasmXScopes(sourceId, sourceMapContext) {
+ const { getSourceMap } = sourceMapContext;
+ const xScopeData = await getXScopes(sourceId, getSourceMap);
+ if (!xScopeData) {
+ return null;
+ }
+ return new XScope(xScopeData, sourceMapContext);
+}
+
+function clearWasmXScopes() {
+ xScopes.clear();
+}
+
+module.exports = {
+ getWasmXScopes,
+ clearWasmXScopes,
+};
diff --git a/devtools/client/shared/source-map-loader/worker.js b/devtools/client/shared/source-map-loader/worker.js
new file mode 100644
index 0000000000..d730359fba
--- /dev/null
+++ b/devtools/client/shared/source-map-loader/worker.js
@@ -0,0 +1,50 @@
+/* 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/>. */
+
+"use strict";
+
+importScripts("resource://gre/modules/workers/require.js");
+
+const {
+ getOriginalURLs,
+ hasOriginalURL,
+ getOriginalRanges,
+ getGeneratedRanges,
+ getGeneratedLocation,
+ getOriginalLocation,
+ getOriginalLocations,
+ getOriginalSourceText,
+ getGeneratedRangesForOriginal,
+ getFileGeneratedRange,
+ loadSourceMap,
+ clearSourceMaps,
+ setSourceMapForGeneratedSources,
+} = require("resource://devtools/client/shared/source-map-loader/source-map.js");
+
+const {
+ getOriginalStackFrames,
+} = require("resource://devtools/client/shared/source-map-loader/utils/getOriginalStackFrames.js");
+
+const {
+ workerHandler,
+} = require("resource://devtools/client/shared/worker-utils.js");
+
+// The interface is implemented in source-map to be
+// easier to unit test.
+self.onmessage = workerHandler({
+ getOriginalURLs,
+ hasOriginalURL,
+ getOriginalRanges,
+ getGeneratedRanges,
+ getGeneratedLocation,
+ getOriginalLocation,
+ getOriginalLocations,
+ getOriginalSourceText,
+ getOriginalStackFrames,
+ getGeneratedRangesForOriginal,
+ getFileGeneratedRange,
+ loadSourceMap,
+ setSourceMapForGeneratedSources,
+ clearSourceMaps,
+});
diff --git a/devtools/client/shared/source-utils.js b/devtools/client/shared/source-utils.js
new file mode 100644
index 0000000000..605c8b1fe0
--- /dev/null
+++ b/devtools/client/shared/source-utils.js
@@ -0,0 +1,359 @@
+/* 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/. */
+"use strict";
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+
+const l10n = new LocalizationHelper(
+ "devtools/client/locales/components.properties"
+);
+const UNKNOWN_SOURCE_STRING = l10n.getStr("frame.unknownSource");
+
+// Character codes used in various parsing helper functions.
+const CHAR_CODE_A = "a".charCodeAt(0);
+const CHAR_CODE_B = "b".charCodeAt(0);
+const CHAR_CODE_C = "c".charCodeAt(0);
+const CHAR_CODE_D = "d".charCodeAt(0);
+const CHAR_CODE_E = "e".charCodeAt(0);
+const CHAR_CODE_F = "f".charCodeAt(0);
+const CHAR_CODE_H = "h".charCodeAt(0);
+const CHAR_CODE_I = "i".charCodeAt(0);
+const CHAR_CODE_J = "j".charCodeAt(0);
+const CHAR_CODE_L = "l".charCodeAt(0);
+const CHAR_CODE_M = "m".charCodeAt(0);
+const CHAR_CODE_N = "n".charCodeAt(0);
+const CHAR_CODE_O = "o".charCodeAt(0);
+const CHAR_CODE_P = "p".charCodeAt(0);
+const CHAR_CODE_R = "r".charCodeAt(0);
+const CHAR_CODE_S = "s".charCodeAt(0);
+const CHAR_CODE_T = "t".charCodeAt(0);
+const CHAR_CODE_U = "u".charCodeAt(0);
+const CHAR_CODE_W = "w".charCodeAt(0);
+const CHAR_CODE_COLON = ":".charCodeAt(0);
+const CHAR_CODE_DASH = "-".charCodeAt(0);
+const CHAR_CODE_L_SQUARE_BRACKET = "[".charCodeAt(0);
+const CHAR_CODE_SLASH = "/".charCodeAt(0);
+
+// The cache used in the `parseURL` function.
+const gURLStore = new Map();
+// The cache used in the `getSourceNames` function.
+const gSourceNamesStore = new Map();
+
+/**
+ * Takes a string and returns an object containing all the properties
+ * available on an URL instance, with additional properties (fileName),
+ * Leverages caching.
+ *
+ * @param {String} location
+ * @return {Object?} An object containing most properties available
+ * in https://developer.mozilla.org/en-US/docs/Web/API/URL
+ */
+
+function parseURL(location) {
+ let url = gURLStore.get(location);
+
+ if (url !== void 0) {
+ return url;
+ }
+
+ try {
+ url = new URL(location);
+ // The callers were generally written to expect a URL from
+ // sdk/url, which is subtly different. So, work around some
+ // important differences here.
+ url = {
+ href: url.href,
+ protocol: url.protocol,
+ host: url.host,
+ hostname: url.hostname,
+ port: url.port || null,
+ pathname: url.pathname,
+ search: url.search,
+ hash: url.hash,
+ username: url.username,
+ password: url.password,
+ origin: url.origin,
+ };
+
+ // Definitions:
+ // Example: https://foo.com:8888/file.js
+ // `hostname`: "foo.com"
+ // `host`: "foo.com:8888"
+ const isChrome = isChromeScheme(location);
+
+ url.fileName = url.pathname
+ ? url.pathname.slice(url.pathname.lastIndexOf("/") + 1) || "/"
+ : "/";
+
+ if (isChrome) {
+ url.hostname = null;
+ url.host = null;
+ }
+
+ gURLStore.set(location, url);
+ return url;
+ } catch (e) {
+ gURLStore.set(location, null);
+ return null;
+ }
+}
+
+/**
+ * Parse a source into a short and long name as well as a host name.
+ *
+ * @param {String} source
+ * The source to parse. Can be a URI or names like "(eval)" or
+ * "self-hosted".
+ * @return {Object}
+ * An object with the following properties:
+ * - {String} short: A short name for the source.
+ * - "http://page.com/test.js#go?q=query" -> "test.js"
+ * - {String} long: The full, long name for the source, with
+ hash/query stripped.
+ * - "http://page.com/test.js#go?q=query" -> "http://page.com/test.js"
+ * - {String?} host: If available, the host name for the source.
+ * - "http://page.com/test.js#go?q=query" -> "page.com"
+ */
+function getSourceNames(source) {
+ const data = gSourceNamesStore.get(source);
+
+ if (data) {
+ return data;
+ }
+
+ let short, long, host;
+ const sourceStr = source ? String(source) : "";
+
+ // If `data:...` uri
+ if (isDataScheme(sourceStr)) {
+ const commaIndex = sourceStr.indexOf(",");
+ if (commaIndex > -1) {
+ // The `short` name for a data URI becomes `data:` followed by the actual
+ // encoded content, omitting the MIME type, and charset.
+ short = `data:${sourceStr.substring(commaIndex + 1)}`.slice(0, 100);
+ const result = { short, long: sourceStr };
+ gSourceNamesStore.set(source, result);
+ return result;
+ }
+ }
+
+ const parsedUrl = parseURL(sourceStr);
+
+ if (!parsedUrl) {
+ // Malformed URI.
+ long = sourceStr;
+ short = sourceStr.slice(0, 100);
+ } else {
+ host = parsedUrl.host;
+
+ long = parsedUrl.href;
+ if (parsedUrl.hash) {
+ long = long.replace(parsedUrl.hash, "");
+ }
+ if (parsedUrl.search) {
+ long = long.replace(parsedUrl.search, "");
+ }
+
+ short = parsedUrl.fileName;
+ // If `short` is just a slash, and we actually have a path,
+ // strip the slash and parse again to get a more useful short name.
+ // e.g. "http://foo.com/bar/" -> "bar", rather than "/"
+ if (short === "/" && parsedUrl.pathname !== "/") {
+ short = parseURL(long.replace(/\/$/, "")).fileName;
+ }
+ }
+
+ if (!short) {
+ if (!long) {
+ long = UNKNOWN_SOURCE_STRING;
+ }
+ short = long.slice(0, 100);
+ }
+
+ const result = { short, long, host };
+ gSourceNamesStore.set(source, result);
+ return result;
+}
+
+// For the functions below, we assume that we will never access the location
+// argument out of bounds, which is indeed the vast majority of cases.
+//
+// They are written this way because they are hot. Each frame is checked for
+// being content or chrome when processing the profile.
+
+function isColonSlashSlash(location, i = 0) {
+ return (
+ location.charCodeAt(++i) === CHAR_CODE_COLON &&
+ location.charCodeAt(++i) === CHAR_CODE_SLASH &&
+ location.charCodeAt(++i) === CHAR_CODE_SLASH
+ );
+}
+
+function isDataScheme(location, i = 0) {
+ return (
+ location.charCodeAt(i) === CHAR_CODE_D &&
+ location.charCodeAt(++i) === CHAR_CODE_A &&
+ location.charCodeAt(++i) === CHAR_CODE_T &&
+ location.charCodeAt(++i) === CHAR_CODE_A &&
+ location.charCodeAt(++i) === CHAR_CODE_COLON
+ );
+}
+
+function isContentScheme(location, i = 0) {
+ const firstChar = location.charCodeAt(i);
+
+ switch (firstChar) {
+ // "http://" or "https://"
+ case CHAR_CODE_H:
+ if (
+ location.charCodeAt(++i) === CHAR_CODE_T &&
+ location.charCodeAt(++i) === CHAR_CODE_T &&
+ location.charCodeAt(++i) === CHAR_CODE_P
+ ) {
+ if (location.charCodeAt(i + 1) === CHAR_CODE_S) {
+ ++i;
+ }
+ return isColonSlashSlash(location, i);
+ }
+ return false;
+
+ // "file://"
+ case CHAR_CODE_F:
+ if (
+ location.charCodeAt(++i) === CHAR_CODE_I &&
+ location.charCodeAt(++i) === CHAR_CODE_L &&
+ location.charCodeAt(++i) === CHAR_CODE_E
+ ) {
+ return isColonSlashSlash(location, i);
+ }
+ return false;
+
+ // "app://"
+ case CHAR_CODE_A:
+ if (
+ location.charCodeAt(++i) == CHAR_CODE_P &&
+ location.charCodeAt(++i) == CHAR_CODE_P
+ ) {
+ return isColonSlashSlash(location, i);
+ }
+ return false;
+
+ // "blob:"
+ case CHAR_CODE_B:
+ if (
+ location.charCodeAt(++i) == CHAR_CODE_L &&
+ location.charCodeAt(++i) == CHAR_CODE_O &&
+ location.charCodeAt(++i) == CHAR_CODE_B &&
+ location.charCodeAt(++i) == CHAR_CODE_COLON
+ ) {
+ return isContentScheme(location, i + 1);
+ }
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+function isChromeString(location, i = 0) {
+ if (
+ location.charCodeAt(i) === CHAR_CODE_C &&
+ location.charCodeAt(++i) === CHAR_CODE_H &&
+ location.charCodeAt(++i) === CHAR_CODE_R &&
+ location.charCodeAt(++i) === CHAR_CODE_O &&
+ location.charCodeAt(++i) === CHAR_CODE_M &&
+ location.charCodeAt(++i) === CHAR_CODE_E
+ ) {
+ return isColonSlashSlash(location, i);
+ }
+ return false;
+}
+
+function isResourceString(location, i = 0) {
+ if (
+ location.charCodeAt(i) === CHAR_CODE_R &&
+ location.charCodeAt(++i) === CHAR_CODE_E &&
+ location.charCodeAt(++i) === CHAR_CODE_S &&
+ location.charCodeAt(++i) === CHAR_CODE_O &&
+ location.charCodeAt(++i) === CHAR_CODE_U &&
+ location.charCodeAt(++i) === CHAR_CODE_R &&
+ location.charCodeAt(++i) === CHAR_CODE_C &&
+ location.charCodeAt(++i) === CHAR_CODE_E
+ ) {
+ return isColonSlashSlash(location, i);
+ }
+ return false;
+}
+
+function isJarFileString(location, i = 0) {
+ if (
+ location.charCodeAt(i) === CHAR_CODE_J &&
+ location.charCodeAt(++i) === CHAR_CODE_A &&
+ location.charCodeAt(++i) === CHAR_CODE_R &&
+ location.charCodeAt(++i) === CHAR_CODE_COLON &&
+ location.charCodeAt(++i) === CHAR_CODE_F &&
+ location.charCodeAt(++i) === CHAR_CODE_I &&
+ location.charCodeAt(++i) === CHAR_CODE_L &&
+ location.charCodeAt(++i) === CHAR_CODE_E
+ ) {
+ return isColonSlashSlash(location, i);
+ }
+ return false;
+}
+
+function isChromeScheme(location, i = 0) {
+ return (
+ isChromeString(location, i) ||
+ isResourceString(location, i) ||
+ isJarFileString(location, i)
+ );
+}
+
+function isWASM(location, i = 0) {
+ return (
+ // "wasm-function["
+ location.charCodeAt(i) === CHAR_CODE_W &&
+ location.charCodeAt(++i) === CHAR_CODE_A &&
+ location.charCodeAt(++i) === CHAR_CODE_S &&
+ location.charCodeAt(++i) === CHAR_CODE_M &&
+ location.charCodeAt(++i) === CHAR_CODE_DASH &&
+ location.charCodeAt(++i) === CHAR_CODE_F &&
+ location.charCodeAt(++i) === CHAR_CODE_U &&
+ location.charCodeAt(++i) === CHAR_CODE_N &&
+ location.charCodeAt(++i) === CHAR_CODE_C &&
+ location.charCodeAt(++i) === CHAR_CODE_T &&
+ location.charCodeAt(++i) === CHAR_CODE_I &&
+ location.charCodeAt(++i) === CHAR_CODE_O &&
+ location.charCodeAt(++i) === CHAR_CODE_N &&
+ location.charCodeAt(++i) === CHAR_CODE_L_SQUARE_BRACKET
+ );
+}
+
+/**
+ * A utility method to get the file name from a sourcemapped location
+ * The sourcemap location can be in any form. This method returns a
+ * formatted file name for different cases like Windows or OSX.
+ * @param source
+ * @returns String
+ */
+function getSourceMappedFile(source) {
+ // If sourcemapped source is a OSX path, return
+ // the characters after last "/".
+ // If sourcemapped source is a Windowss path, return
+ // the characters after last "\\".
+ if (source.lastIndexOf("/") >= 0) {
+ source = source.slice(source.lastIndexOf("/") + 1);
+ } else if (source.lastIndexOf("\\") >= 0) {
+ source = source.slice(source.lastIndexOf("\\") + 1);
+ }
+ return source;
+}
+
+exports.parseURL = parseURL;
+exports.getSourceNames = getSourceNames;
+exports.isChromeScheme = isChromeScheme;
+exports.isContentScheme = isContentScheme;
+exports.isWASM = isWASM;
+exports.isDataScheme = isDataScheme;
+exports.getSourceMappedFile = getSourceMappedFile;
diff --git a/devtools/client/shared/sourceeditor/README b/devtools/client/shared/sourceeditor/README
new file mode 100644
index 0000000000..e4a88f6d4e
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/README
@@ -0,0 +1,259 @@
+This is the CodeMirror editor packaged for the Mozilla Project. CodeMirror
+is a JavaScript component that provides a code editor in the browser. When
+a mode is available for the language you are coding in, it will color your
+code, and optionally help with indentation.
+
+# CodeMirror 6
+
+We're currently migrating to CodeMirror 6, which means we have bundle for version 6 _and_ 5,
+until we successfully migrated all consumers to CodeMirror 6.
+
+For version 6, we're generating a bundle (codemirror6/codemirror6.bundle.js) using rollup.
+The entry point for the bundle is codemirror6/index.mjs, where we export all the classes
+and functions that the editor needs. When adding new exported item, the bundle needs to
+be updated, which can be done by running:
+ > cd devtools/client/shared/sourceeditor
+ > npm install
+ > npm run build-cm6
+
+This will produced a minified bundle, which might not be ideal if you're debugging an issue or profiling.
+You can get an unminified bundle by running:
+> npm run build-cm6-unminified
+
+The generated bundle can be configurated in rollup.config.mjs.
+
+# CodeMirror 5
+
+## CodeMirror 5 Upgrade
+
+Currently used version is 5.58.1. To upgrade: download a new version of
+CodeMirror from the project's page [1] and replace all JavaScript and
+CSS files inside the codemirror directory [2].
+
+Then to recreate codemirror.bundle.js:
+ > cd devtools/client/shared/sourceeditor
+ > npm install
+ > npm run build
+
+When investigating an issue in CodeMirror, you might want to have a non-minified bundle.
+You can do this by running `npm run build-unminified` instead of `npm run build`.
+
+To confirm the functionality run mochitests for the following components:
+
+ * sourceeditor
+ * debugger
+ * styleditor
+ * netmonitor
+ * webconsole
+
+The sourceeditor component contains imported CodeMirror tests [3].
+
+ * Some tests were commented out because we don't use that functionality
+ within Firefox (for example Ruby editing mode). Be careful when updating
+ files test/codemirror.html and test/vimemacs.html; they were modified to
+ co-exist with Mozilla's testing infrastructure. Basically, vimemacs.html
+ is a copy of codemirror.html but only with VIM and Emacs mode tests
+ enabled.
+ * In cm_comment_test.js comment out fallbackToBlock and fallbackToLine
+ tests.
+ * The search addon (search.js) was slightly modified to make search
+ UI localizable (see patch below).
+
+Other than that, we don't have any Mozilla-specific patches applied to
+CodeMirror itself.
+
+## Addons
+
+To install a new CodeMirror 5 addon add it to the codemirror directory,
+jar.mn [4] file and editor.js [5]. Also, add it to the License section
+below.
+
+## License
+
+The following files in this directory and devtools/client/shared/sourceeditor/test/codemirror/
+are licensed according to the contents in the LICENSE file.
+
+## Localization patches
+
+diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js b/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js
+--- a/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js
++++ b/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js
+@@ -93,32 +93,47 @@
+ } else {
+ query = parseString(query)
+ }
+ if (typeof query == "string" ? query == "" : query.test(""))
+ query = /x^/;
+ return query;
+ }
+
+- var queryDialog =
+- '<span class="CodeMirror-search-label">Search:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
++ var queryDialog;
+
+ function startSearch(cm, state, query) {
+ state.queryText = query;
+ state.query = parseQuery(query);
+ cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
+ state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
+ cm.addOverlay(state.overlay);
+ if (cm.showMatchesOnScrollbar) {
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+ }
+ }
+
+ function doSearch(cm, rev, persistent, immediate) {
++ if (!queryDialog) {
++ let doc = cm.getWrapperElement().ownerDocument;
++ let inp = doc.createElement("input");
++
++ inp.type = "search";
++ inp.placeholder = cm.l10n("findCmd.promptMessage");
++ inp.style.marginInlineStart = "1em";
++ inp.style.marginInlineEnd = "1em";
++ inp.style.flexGrow = "1";
++ inp.addEventListener("focus", () => inp.select());
++
++ queryDialog = doc.createElement("div");
++ queryDialog.appendChild(inp);
++ queryDialog.style.display = "flex";
++ }
++
+ var state = getSearchState(cm);
+ if (state.query) return findNext(cm, rev);
+ var q = cm.getSelection() || state.lastQuery;
+ if (q instanceof RegExp && q.source == "x^") q = null
+ if (persistent && cm.openDialog) {
+ var hiding = null
+ var searchNext = function(query, event) {
+ CodeMirror.e_stop(event);
+@@ -181,56 +196,110 @@
+ var state = getSearchState(cm);
+ state.lastQuery = state.query;
+ if (!state.query) return;
+ state.query = state.queryText = null;
+ cm.removeOverlay(state.overlay);
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ });}
+
+- var replaceQueryDialog =
+- ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">(Use /re/ syntax for regexp search)</span>';
+- var replacementQueryDialog = '<span class="CodeMirror-search-label">With:</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
+- var doReplaceConfirm = '<span class="CodeMirror-search-label">Replace?</span> <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>';
+-
+ function replaceAll(cm, query, text) {
+ cm.operation(function() {
+ for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+ if (typeof query != "string") {
+ var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+ cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+ } else cursor.replace(text);
+ }
+ });
+ }
+
+ function replace(cm, all) {
+ if (cm.getOption("readOnly")) return;
+ var query = cm.getSelection() || getSearchState(cm).lastQuery;
+- var dialogText = '<span class="CodeMirror-search-label">' + (all ? 'Replace all:' : 'Replace:') + '</span>';
+- dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
++
++ let doc = cm.getWrapperElement().ownerDocument;
++
++ // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
++ // argument by itself, so it should be cloned.
++ let searchLabel = doc.createElement("span");
++ searchLabel.classList.add("CodeMirror-search-label");
++ searchLabel.textContent = all ? "Replace all:" : "Replace:";
++
++ let replaceQueryFragment = doc.createDocumentFragment();
++ replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
++
++ let searchField = doc.createElement("input");
++ searchField.setAttribute("type", "text");
++ searchField.setAttribute("style", "width: 10em");
++ searchField.classList.add("CodeMirror-search-field");
++ replaceQueryFragment.appendChild(searchField);
++
++ let searchHint = doc.createElement("span");
++ searchHint.setAttribute("style", "color: #888");
++ searchHint.classList.add("CodeMirror-search-hint");
++ searchHint.textContent = "(Use /re/ syntax for regexp search)";
++ replaceQueryFragment.appendChild(searchHint);
++
++ dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
+ if (!query) return;
+ query = parseQuery(query);
+- dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
++
++ let replacementQueryFragment = doc.createDocumentFragment();
++
++ let replaceWithLabel = searchLabel.cloneNode(false);
++ replaceWithLabel.textContent = "With:";
++ replacementQueryFragment.appendChild(replaceWithLabel);
++
++ let replaceField = doc.createElement("input");
++ replaceField.setAttribute("type", "text");
++ replaceField.setAttribute("style", "width: 10em");
++ replaceField.classList.add("CodeMirror-search-field");
++ replacementQueryFragment.appendChild(replaceField);
++
++ dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
+ text = parseString(text)
+ if (all) {
+ replaceAll(cm, query, text)
+ } else {
+ clearSearch(cm);
+ var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
+ var advance = function() {
+ var start = cursor.from(), match;
+ if (!(match = cursor.findNext())) {
+ cursor = getSearchCursor(cm, query);
+ if (!(match = cursor.findNext()) ||
+ (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+ }
+ cm.setSelection(cursor.from(), cursor.to());
+- cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
+- confirmDialog(cm, doReplaceConfirm, "Replace?",
++ cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
++
++ let replaceConfirmFragment = doc.createDocumentFragment();
++
++ let replaceConfirmLabel = searchLabel.cloneNode(false);
++ replaceConfirmLabel.textContent = "Replace?";
++ replaceConfirmFragment.appendChild(replaceConfirmLabel);
++
++ let yesButton = doc.createElement("button");
++ yesButton.textContent = "Yes";
++ replaceConfirmFragment.appendChild(yesButton);
++
++ let noButton = doc.createElement("button");
++ noButton.textContent = "No";
++ replaceConfirmFragment.appendChild(noButton);
++
++ let allButton = doc.createElement("button");
++ allButton.textContent = "All";
++ replaceConfirmFragment.appendChild(allButton);
++
++ let stopButton = doc.createElement("button");
++ stopButton.textContent = "Stop";
++ replaceConfirmFragment.appendChild(stopButton);
++
++ confirmDialog(cm, replaceConfirmFragment, "Replace?",
+ [function() {doReplace(match);}, advance,
+ function() {replaceAll(cm, query, text)}]);
+ };
+ var doReplace = function(match) {
+ cursor.replace(typeof query == "string" ? text :
+ text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+ advance();
+ };
+
+# Footnotes
+
+[1] http://codemirror.net
+[2] devtools/client/shared/sourceeditor/codemirror
+[3] devtools/client/shared/sourceeditor/test/browser_codemirror.js
+[4] devtools/client/jar.mn
+[5] devtools/client/shared/sourceeditor/editor.js
diff --git a/devtools/client/shared/sourceeditor/autocomplete.js b/devtools/client/shared/sourceeditor/autocomplete.js
new file mode 100644
index 0000000000..555bcf581c
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/autocomplete.js
@@ -0,0 +1,358 @@
+/* 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/. */
+
+"use strict";
+
+const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+
+loader.lazyRequireGetter(
+ this,
+ "KeyCodes",
+ "resource://devtools/client/shared/keycodes.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "CSSCompleter",
+ "resource://devtools/client/shared/sourceeditor/css-autocompleter.js"
+);
+
+const autocompleteMap = new WeakMap();
+
+/**
+ * Prepares an editor instance for autocompletion.
+ */
+function initializeAutoCompletion(ctx, options = {}) {
+ const { cm, ed, Editor } = ctx;
+ if (autocompleteMap.has(ed)) {
+ return;
+ }
+
+ const win = ed.container.contentWindow.wrappedJSObject;
+ const { CodeMirror } = win;
+
+ let completer = null;
+ const autocompleteKey =
+ "Ctrl-" + Editor.keyFor("autocompletion", { noaccel: true });
+ if (ed.config.mode == Editor.modes.css) {
+ completer = new CSSCompleter({
+ walker: options.walker,
+ cssProperties: options.cssProperties,
+ });
+ }
+
+ function insertSelectedPopupItem() {
+ const autocompleteState = autocompleteMap.get(ed);
+ if (!popup || !popup.isOpen || !autocompleteState) {
+ return false;
+ }
+
+ if (!autocompleteState.suggestionInsertedOnce && popup.selectedItem) {
+ autocompleteMap.get(ed).insertingSuggestion = true;
+ insertPopupItem(ed, popup.selectedItem);
+ }
+
+ popup.once("popup-closed", () => {
+ // This event is used in tests.
+ ed.emit("popup-hidden");
+ });
+ popup.hidePopup();
+ return true;
+ }
+
+ // Give each popup a new name to avoid sharing the elements.
+
+ let popup = new AutocompletePopup(win.parent.document, {
+ position: "bottom",
+ autoSelect: true,
+ onClick: insertSelectedPopupItem,
+ });
+
+ const cycle = reverse => {
+ if (popup?.isOpen) {
+ // eslint-disable-next-line mozilla/no-compare-against-boolean-literals
+ cycleSuggestions(ed, reverse == true);
+ return null;
+ }
+
+ return CodeMirror.Pass;
+ };
+
+ let keyMap = {
+ Tab: cycle,
+ Down: cycle,
+ "Shift-Tab": cycle.bind(null, true),
+ Up: cycle.bind(null, true),
+ Enter: () => {
+ const wasHandled = insertSelectedPopupItem();
+ return wasHandled ? true : CodeMirror.Pass;
+ },
+ };
+
+ const autoCompleteCallback = autoComplete.bind(null, ctx);
+ const keypressCallback = onEditorKeypress.bind(null, ctx);
+ keyMap[autocompleteKey] = autoCompleteCallback;
+ cm.addKeyMap(keyMap);
+
+ cm.on("keydown", keypressCallback);
+ ed.on("change", autoCompleteCallback);
+ ed.on("destroy", destroy);
+
+ function destroy() {
+ ed.off("destroy", destroy);
+ cm.off("keydown", keypressCallback);
+ ed.off("change", autoCompleteCallback);
+ cm.removeKeyMap(keyMap);
+ popup.destroy();
+ keyMap = popup = completer = null;
+ autocompleteMap.delete(ed);
+ }
+
+ autocompleteMap.set(ed, {
+ popup,
+ completer,
+ keyMap,
+ destroy,
+ insertingSuggestion: false,
+ suggestionInsertedOnce: false,
+ });
+}
+
+/**
+ * Destroy autocompletion on an editor instance.
+ */
+function destroyAutoCompletion(ctx) {
+ const { ed } = ctx;
+ if (!autocompleteMap.has(ed)) {
+ return;
+ }
+
+ const { destroy } = autocompleteMap.get(ed);
+ destroy();
+}
+
+/**
+ * Provides suggestions to autocomplete the current token/word being typed.
+ */
+function autoComplete({ ed, cm }) {
+ const autocompleteOpts = autocompleteMap.get(ed);
+ const { completer, popup } = autocompleteOpts;
+ if (
+ !completer ||
+ autocompleteOpts.insertingSuggestion ||
+ autocompleteOpts.doNotAutocomplete
+ ) {
+ autocompleteOpts.insertingSuggestion = false;
+ return;
+ }
+ const cur = ed.getCursor();
+ completer
+ .complete(cm.getRange({ line: 0, ch: 0 }, cur), cur)
+ .then(suggestions => {
+ if (
+ !suggestions ||
+ !suggestions.length ||
+ suggestions[0].preLabel == null
+ ) {
+ autocompleteOpts.suggestionInsertedOnce = false;
+ popup.once("popup-closed", () => {
+ // This event is used in tests.
+ ed.emit("after-suggest");
+ });
+ popup.hidePopup();
+ return;
+ }
+ // The cursor is at the end of the currently entered part of the token,
+ // like "backgr|" but we need to open the popup at the beginning of the
+ // character "b". Thus we need to calculate the width of the entered part
+ // of the token ("backgr" here).
+
+ const cursorElement =
+ cm.display.cursorDiv.querySelector(".CodeMirror-cursor");
+ const left = suggestions[0].preLabel.length * cm.defaultCharWidth();
+ popup.hidePopup();
+ popup.setItems(suggestions);
+
+ popup.once("popup-opened", () => {
+ // This event is used in tests.
+ ed.emit("after-suggest");
+ });
+ popup.openPopup(cursorElement, -1 * left, 0);
+ autocompleteOpts.suggestionInsertedOnce = false;
+ })
+ .catch(console.error);
+}
+
+/**
+ * Inserts a popup item into the current cursor location
+ * in the editor.
+ */
+function insertPopupItem(ed, popupItem) {
+ const { preLabel, text } = popupItem;
+ const cur = ed.getCursor();
+ const textBeforeCursor = ed.getText(cur.line).substring(0, cur.ch);
+ const backwardsTextBeforeCursor = textBeforeCursor
+ .split("")
+ .reverse()
+ .join("");
+ const backwardsPreLabel = preLabel.split("").reverse().join("");
+
+ // If there is additional text in the preLabel vs the line, then
+ // just insert the entire autocomplete text. An example:
+ // if you type 'a' and select '#about' from the autocomplete menu,
+ // then the final text needs to the end up as '#about'.
+ if (backwardsPreLabel.indexOf(backwardsTextBeforeCursor) === 0) {
+ ed.replaceText(text, { line: cur.line, ch: 0 }, cur);
+ } else {
+ ed.replaceText(text.slice(preLabel.length), cur, cur);
+ }
+}
+
+/**
+ * Cycles through provided suggestions by the popup in a top to bottom manner
+ * when `reverse` is not true. Opposite otherwise.
+ */
+function cycleSuggestions(ed, reverse) {
+ const autocompleteOpts = autocompleteMap.get(ed);
+ const { popup } = autocompleteOpts;
+ const cur = ed.getCursor();
+ autocompleteOpts.insertingSuggestion = true;
+ if (!autocompleteOpts.suggestionInsertedOnce) {
+ autocompleteOpts.suggestionInsertedOnce = true;
+ let firstItem;
+ if (reverse) {
+ firstItem = popup.getItemAtIndex(popup.itemCount - 1);
+ popup.selectPreviousItem();
+ } else {
+ firstItem = popup.getItemAtIndex(0);
+ if (firstItem.label == firstItem.preLabel && popup.itemCount > 1) {
+ firstItem = popup.getItemAtIndex(1);
+ popup.selectNextItem();
+ }
+ }
+ if (popup.itemCount == 1) {
+ popup.hidePopup();
+ }
+ insertPopupItem(ed, firstItem);
+ } else {
+ const fromCur = {
+ line: cur.line,
+ ch: cur.ch - popup.selectedItem.text.length,
+ };
+ if (reverse) {
+ popup.selectPreviousItem();
+ } else {
+ popup.selectNextItem();
+ }
+ ed.replaceText(popup.selectedItem.text, fromCur, cur);
+ }
+ // This event is used in tests.
+ ed.emit("suggestion-entered");
+}
+
+/**
+ * onkeydown handler for the editor instance to prevent autocompleting on some
+ * keypresses.
+ */
+function onEditorKeypress({ ed, Editor }, cm, event) {
+ const autocompleteOpts = autocompleteMap.get(ed);
+
+ // Do not try to autocomplete with multiple selections.
+ if (ed.hasMultipleSelections()) {
+ autocompleteOpts.doNotAutocomplete = true;
+ autocompleteOpts.popup.hidePopup();
+ return;
+ }
+
+ if (
+ (event.ctrlKey || event.metaKey) &&
+ event.keyCode == KeyCodes.DOM_VK_SPACE
+ ) {
+ // When Ctrl/Cmd + Space is pressed, two simultaneous keypresses are emitted
+ // first one for just the Ctrl/Cmd and second one for combo. The first one
+ // leave the autocompleteOpts.doNotAutocomplete as true, so we have to make
+ // it false
+ autocompleteOpts.doNotAutocomplete = false;
+ return;
+ }
+
+ if (event.ctrlKey || event.metaKey || event.altKey) {
+ autocompleteOpts.doNotAutocomplete = true;
+ autocompleteOpts.popup.hidePopup();
+ return;
+ }
+
+ switch (event.keyCode) {
+ case KeyCodes.DOM_VK_RETURN:
+ autocompleteOpts.doNotAutocomplete = true;
+ break;
+ case KeyCodes.DOM_VK_ESCAPE:
+ if (autocompleteOpts.popup.isOpen) {
+ // Prevent the Console input to open, but still remove the autocomplete popup.
+ autocompleteOpts.doNotAutocomplete = true;
+ autocompleteOpts.popup.hidePopup();
+ event.preventDefault();
+ }
+ break;
+ case KeyCodes.DOM_VK_LEFT:
+ case KeyCodes.DOM_VK_RIGHT:
+ case KeyCodes.DOM_VK_HOME:
+ case KeyCodes.DOM_VK_END:
+ autocompleteOpts.doNotAutocomplete = true;
+ autocompleteOpts.popup.hidePopup();
+ break;
+ case KeyCodes.DOM_VK_BACK_SPACE:
+ case KeyCodes.DOM_VK_DELETE:
+ if (ed.config.mode == Editor.modes.css) {
+ autocompleteOpts.completer.invalidateCache(ed.getCursor().line);
+ }
+ autocompleteOpts.doNotAutocomplete = true;
+ autocompleteOpts.popup.hidePopup();
+ break;
+ default:
+ autocompleteOpts.doNotAutocomplete = false;
+ }
+}
+
+/**
+ * Returns the private popup. This method is used by tests to test the feature.
+ */
+function getPopup({ ed }) {
+ if (autocompleteMap.has(ed)) {
+ return autocompleteMap.get(ed).popup;
+ }
+
+ return null;
+}
+
+/**
+ * Returns contextual information about the token covered by the caret if the
+ * implementation of completer supports it.
+ */
+function getInfoAt({ ed }, caret) {
+ if (autocompleteMap.has(ed)) {
+ const completer = autocompleteMap.get(ed).completer;
+ if (completer?.getInfoAt) {
+ return completer.getInfoAt(ed.getText(), caret);
+ }
+ }
+
+ return null;
+}
+
+/**
+ * Returns whether autocompletion is enabled for this editor.
+ * Used for testing
+ */
+function isAutocompletionEnabled({ ed }) {
+ return autocompleteMap.has(ed);
+}
+
+// Export functions
+
+module.exports.initializeAutoCompletion = initializeAutoCompletion;
+module.exports.destroyAutoCompletion = destroyAutoCompletion;
+module.exports.getAutocompletionPopup = getPopup;
+module.exports.getInfoAt = getInfoAt;
+module.exports.isAutocompletionEnabled = isAutocompletionEnabled;
diff --git a/devtools/client/shared/sourceeditor/codemirror/LICENSE b/devtools/client/shared/sourceeditor/codemirror/LICENSE
new file mode 100644
index 0000000000..4db615dd23
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/LICENSE
@@ -0,0 +1,23 @@
+Copyright (C) 2015 by Marijn Haverbeke <marijnh@gmail.com> and others
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+Please note that some subdirectories of the CodeMirror distribution
+include their own LICENSE files, and are released under different
+licences.
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/accessibleTextarea.js b/devtools/client/shared/sourceeditor/codemirror/addon/accessibleTextarea.js
new file mode 100644
index 0000000000..6119ca0818
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/accessibleTextarea.js
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+(function(mod) {
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+})(function(CodeMirror) {
+ // CodeMirror uses an offscreen <textarea> to detect input.
+ // Due to inconsistencies in the many browsers it supports, it simplifies things by
+ // regularly checking if something is in the textarea, adding those characters to the
+ // document, and then clearing the textarea.
+ // This breaks assistive technology that wants to read from CodeMirror, because the
+ // <textarea> that they interact with is constantly empty.
+ // Because we target up-to-date Firefox, we can guarantee consistent input events.
+ // This lets us leave the current line from the editor in our <textarea>.
+ // CodeMirror still expects a mostly empty <textarea>, so we pass CodeMirror a fake
+ // <textarea> that only contains the users input.
+ CodeMirror.inputStyles.accessibleTextArea = class extends CodeMirror.inputStyles.textarea {
+ /**
+ * @override
+ * @param {!Object} display
+ */
+ init(display) {
+ super.init(display);
+ this.textarea.addEventListener("compositionstart",
+ this._onCompositionStart.bind(this));
+ }
+
+ _onCompositionStart() {
+ if (this.textarea.selectionEnd === this.textarea.value.length) {
+ return;
+ }
+
+ // CodeMirror always expects the caret to be at the end of the textarea
+ // When in IME composition mode, clip the textarea to how CodeMirror expects it,
+ // and then let CodeMirror do its thing.
+ this.textarea.value = this.textarea.value.substring(0, this.textarea.selectionEnd);
+ const length = this.textarea.value.length;
+ this.textarea.setSelectionRange(length, length);
+ this.prevInput = this.textarea.value;
+ }
+
+ /**
+ * @override
+ * @param {Boolean} typing
+ */
+ reset(typing) {
+ if (
+ typing ||
+ this.contextMenuPending ||
+ this.composing ||
+ this.cm.somethingSelected()
+ ) {
+ super.reset(typing);
+ return;
+ }
+
+ // When navigating around the document, keep the current visual line in the textarea.
+ const cursor = this.cm.getCursor();
+ let start, end;
+ if (this.cm.options.lineWrapping) {
+ // To get the visual line, compute the leftmost and rightmost character positions.
+ const top = this.cm.charCoords(cursor, "page").top;
+ start = this.cm.coordsChar({left: -Infinity, top});
+ end = this.cm.coordsChar({left: Infinity, top});
+ } else {
+ // Limit the line to 1000 characters to prevent lag.
+ const offset = Math.floor(cursor.ch / 1000) * 1000;
+ start = {ch: offset, line: cursor.line};
+ end = {ch: offset + 1000, line: cursor.line};
+ }
+
+ this.textarea.value = this.cm.getRange(start, end);
+ const caretPosition = cursor.ch - start.ch;
+ this.textarea.setSelectionRange(caretPosition, caretPosition);
+ this.prevInput = this.textarea.value;
+ }
+
+ /**
+ * @override
+ * @return {boolean}
+ */
+ poll() {
+ if (this.contextMenuPending || this.composing) {
+ return super.poll();
+ }
+
+ const text = this.textarea.value;
+ let start = 0;
+ const length = Math.min(this.prevInput.length, text.length);
+
+ while (start < length && this.prevInput[start] === text[start]) {
+ ++start;
+ }
+
+ let end = 0;
+
+ while (
+ end < length - start &&
+ this.prevInput[this.prevInput.length - end - 1] === text[text.length - end - 1]
+ ) {
+ ++end;
+ }
+
+ // CodeMirror expects the user to be typing into a blank <textarea>.
+ // Pass a fake textarea into super.poll that only contains the users input.
+ const placeholder = this.textarea;
+ this.textarea = document.createElement("textarea");
+ this.textarea.value = text.substring(start, text.length - end);
+ this.textarea.setSelectionRange(
+ placeholder.selectionStart - start,
+ placeholder.selectionEnd - start
+ );
+ this.prevInput = "";
+ const result = super.poll();
+ this.prevInput = text;
+ this.textarea = placeholder;
+ return result;
+ }
+ };
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/comment/comment.js b/devtools/client/shared/sourceeditor/codemirror/addon/comment/comment.js
new file mode 100644
index 0000000000..3a9dfc2f4e
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/comment/comment.js
@@ -0,0 +1,209 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var noOptions = {};
+ var nonWS = /[^\s\u00a0]/;
+ var Pos = CodeMirror.Pos;
+
+ function firstNonWS(str) {
+ var found = str.search(nonWS);
+ return found == -1 ? 0 : found;
+ }
+
+ CodeMirror.commands.toggleComment = function(cm) {
+ cm.toggleComment();
+ };
+
+ CodeMirror.defineExtension("toggleComment", function(options) {
+ if (!options) options = noOptions;
+ var cm = this;
+ var minLine = Infinity, ranges = this.listSelections(), mode = null;
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var from = ranges[i].from(), to = ranges[i].to();
+ if (from.line >= minLine) continue;
+ if (to.line >= minLine) to = Pos(minLine, 0);
+ minLine = from.line;
+ if (mode == null) {
+ if (cm.uncomment(from, to, options)) mode = "un";
+ else { cm.lineComment(from, to, options); mode = "line"; }
+ } else if (mode == "un") {
+ cm.uncomment(from, to, options);
+ } else {
+ cm.lineComment(from, to, options);
+ }
+ }
+ });
+
+ // Rough heuristic to try and detect lines that are part of multi-line string
+ function probablyInsideString(cm, pos, line) {
+ return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
+ }
+
+ function getMode(cm, pos) {
+ var mode = cm.getMode()
+ return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
+ }
+
+ CodeMirror.defineExtension("lineComment", function(from, to, options) {
+ if (!options) options = noOptions;
+ var self = this, mode = getMode(self, from);
+ var firstLine = self.getLine(from.line);
+ if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
+
+ var commentString = options.lineComment || mode.lineComment;
+ if (!commentString) {
+ if (options.blockCommentStart || mode.blockCommentStart) {
+ options.fullLines = true;
+ self.blockComment(from, to, options);
+ }
+ return;
+ }
+
+ var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
+ var pad = options.padding == null ? " " : options.padding;
+ var blankLines = options.commentBlankLines || from.line == to.line;
+
+ self.operation(function() {
+ if (options.indent) {
+ var baseString = null;
+ for (var i = from.line; i < end; ++i) {
+ var line = self.getLine(i);
+ var whitespace = line.slice(0, firstNonWS(line));
+ if (baseString == null || baseString.length > whitespace.length) {
+ baseString = whitespace;
+ }
+ }
+ for (var i = from.line; i < end; ++i) {
+ var line = self.getLine(i), cut = baseString.length;
+ if (!blankLines && !nonWS.test(line)) continue;
+ if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
+ self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
+ }
+ } else {
+ for (var i = from.line; i < end; ++i) {
+ if (blankLines || nonWS.test(self.getLine(i)))
+ self.replaceRange(commentString + pad, Pos(i, 0));
+ }
+ }
+ });
+ });
+
+ CodeMirror.defineExtension("blockComment", function(from, to, options) {
+ if (!options) options = noOptions;
+ var self = this, mode = getMode(self, from);
+ var startString = options.blockCommentStart || mode.blockCommentStart;
+ var endString = options.blockCommentEnd || mode.blockCommentEnd;
+ if (!startString || !endString) {
+ if ((options.lineComment || mode.lineComment) && options.fullLines != false)
+ self.lineComment(from, to, options);
+ return;
+ }
+ if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
+
+ var end = Math.min(to.line, self.lastLine());
+ if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
+
+ var pad = options.padding == null ? " " : options.padding;
+ if (from.line > end) return;
+
+ self.operation(function() {
+ if (options.fullLines != false) {
+ var lastLineHasText = nonWS.test(self.getLine(end));
+ self.replaceRange(pad + endString, Pos(end));
+ self.replaceRange(startString + pad, Pos(from.line, 0));
+ var lead = options.blockCommentLead || mode.blockCommentLead;
+ if (lead != null) for (var i = from.line + 1; i <= end; ++i)
+ if (i != end || lastLineHasText)
+ self.replaceRange(lead + pad, Pos(i, 0));
+ } else {
+ self.replaceRange(endString, to);
+ self.replaceRange(startString, from);
+ }
+ });
+ });
+
+ CodeMirror.defineExtension("uncomment", function(from, to, options) {
+ if (!options) options = noOptions;
+ var self = this, mode = getMode(self, from);
+ var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
+
+ // Try finding line comments
+ var lineString = options.lineComment || mode.lineComment, lines = [];
+ var pad = options.padding == null ? " " : options.padding, didSomething;
+ lineComment: {
+ if (!lineString) break lineComment;
+ for (var i = start; i <= end; ++i) {
+ var line = self.getLine(i);
+ var found = line.indexOf(lineString);
+ if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
+ if (found == -1 && nonWS.test(line)) break lineComment;
+ if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
+ lines.push(line);
+ }
+ self.operation(function() {
+ for (var i = start; i <= end; ++i) {
+ var line = lines[i - start];
+ var pos = line.indexOf(lineString), endPos = pos + lineString.length;
+ if (pos < 0) continue;
+ if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
+ didSomething = true;
+ self.replaceRange("", Pos(i, pos), Pos(i, endPos));
+ }
+ });
+ if (didSomething) return true;
+ }
+
+ // Try block comments
+ var startString = options.blockCommentStart || mode.blockCommentStart;
+ var endString = options.blockCommentEnd || mode.blockCommentEnd;
+ if (!startString || !endString) return false;
+ var lead = options.blockCommentLead || mode.blockCommentLead;
+ var startLine = self.getLine(start), open = startLine.indexOf(startString)
+ if (open == -1) return false
+ var endLine = end == start ? startLine : self.getLine(end)
+ var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
+ var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
+ if (close == -1 ||
+ !/comment/.test(self.getTokenTypeAt(insideStart)) ||
+ !/comment/.test(self.getTokenTypeAt(insideEnd)) ||
+ self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
+ return false;
+
+ // Avoid killing block comments completely outside the selection.
+ // Positions of the last startString before the start of the selection, and the first endString after it.
+ var lastStart = startLine.lastIndexOf(startString, from.ch);
+ var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
+ if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
+ // Positions of the first endString after the end of the selection, and the last startString before it.
+ firstEnd = endLine.indexOf(endString, to.ch);
+ var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
+ lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
+ if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
+
+ self.operation(function() {
+ self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
+ Pos(end, close + endString.length));
+ var openEnd = open + startString.length;
+ if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
+ self.replaceRange("", Pos(start, open), Pos(start, openEnd));
+ if (lead) for (var i = start + 1; i <= end; ++i) {
+ var line = self.getLine(i), found = line.indexOf(lead);
+ if (found == -1 || nonWS.test(line.slice(0, found))) continue;
+ var foundEnd = found + lead.length;
+ if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
+ self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
+ }
+ });
+ return true;
+ });
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/comment/continuecomment.js b/devtools/client/shared/sourceeditor/codemirror/addon/comment/continuecomment.js
new file mode 100644
index 0000000000..2dda5e5d92
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/comment/continuecomment.js
@@ -0,0 +1,78 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ function continueComment(cm) {
+ if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ var ranges = cm.listSelections(), mode, inserts = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var pos = ranges[i].head
+ if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass;
+ var modeHere = cm.getModeAt(pos)
+ if (!mode) mode = modeHere;
+ else if (mode != modeHere) return CodeMirror.Pass;
+
+ var insert = null;
+ if (mode.blockCommentStart && mode.blockCommentContinue) {
+ var line = cm.getLine(pos.line).slice(0, pos.ch)
+ var end = line.lastIndexOf(mode.blockCommentEnd), found
+ if (end != -1 && end == pos.ch - mode.blockCommentEnd.length) {
+ // Comment ended, don't continue it
+ } else if ((found = line.lastIndexOf(mode.blockCommentStart)) > -1 && found > end) {
+ insert = line.slice(0, found)
+ if (/\S/.test(insert)) {
+ insert = ""
+ for (var j = 0; j < found; ++j) insert += " "
+ }
+ } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && !/\S/.test(line.slice(0, found))) {
+ insert = line.slice(0, found)
+ }
+ if (insert != null) insert += mode.blockCommentContinue
+ }
+ if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) {
+ var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment);
+ if (found > -1) {
+ insert = line.slice(0, found);
+ if (/\S/.test(insert)) insert = null;
+ else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0];
+ }
+ }
+ if (insert == null) return CodeMirror.Pass;
+ inserts[i] = "\n" + insert;
+ }
+
+ cm.operation(function() {
+ for (var i = ranges.length - 1; i >= 0; i--)
+ cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
+ });
+ }
+
+ function continueLineCommentEnabled(cm) {
+ var opt = cm.getOption("continueComments");
+ if (opt && typeof opt == "object")
+ return opt.continueLineComment !== false;
+ return true;
+ }
+
+ CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
+ if (prev && prev != CodeMirror.Init)
+ cm.removeKeyMap("continueComment");
+ if (val) {
+ var key = "Enter";
+ if (typeof val == "string")
+ key = val;
+ else if (typeof val == "object" && val.key)
+ key = val.key;
+ var map = {name: "continueComment"};
+ map[key] = continueComment;
+ cm.addKeyMap(map);
+ }
+ });
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.css b/devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.css
new file mode 100644
index 0000000000..677c078387
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.css
@@ -0,0 +1,32 @@
+.CodeMirror-dialog {
+ position: absolute;
+ left: 0; right: 0;
+ background: inherit;
+ z-index: 15;
+ padding: .1em .8em;
+ overflow: hidden;
+ color: inherit;
+}
+
+.CodeMirror-dialog-top {
+ border-bottom: 1px solid #eee;
+ top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+ border-top: 1px solid #eee;
+ bottom: 0;
+}
+
+.CodeMirror-dialog input {
+ border: none;
+ outline: none;
+ background: transparent;
+ width: 20em;
+ color: inherit;
+ font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+ font-size: 70%;
+}
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.js b/devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.js
new file mode 100644
index 0000000000..ac9bd333f0
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.js
@@ -0,0 +1,161 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Open simple dialogs on top of an editor. Relies on dialog.css.
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ function dialogDiv(cm, template, bottom) {
+ var wrap = cm.getWrapperElement();
+ var dialog;
+ dialog = wrap.appendChild(document.createElement("div"));
+ if (bottom)
+ dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
+ else
+ dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
+
+ if (typeof template == "string") {
+ dialog.innerHTML = template;
+ } else { // Assuming it's a detached DOM element.
+ dialog.appendChild(template);
+ }
+ CodeMirror.addClass(wrap, 'dialog-opened');
+ return dialog;
+ }
+
+ function closeNotification(cm, newVal) {
+ if (cm.state.currentNotificationClose)
+ cm.state.currentNotificationClose();
+ cm.state.currentNotificationClose = newVal;
+ }
+
+ CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+ if (!options) options = {};
+
+ closeNotification(this, null);
+
+ var dialog = dialogDiv(this, template, options.bottom);
+ var closed = false, me = this;
+ function close(newVal) {
+ if (typeof newVal == 'string') {
+ inp.value = newVal;
+ } else {
+ if (closed) return;
+ closed = true;
+ CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
+ dialog.parentNode.removeChild(dialog);
+ me.focus();
+
+ if (options.onClose) options.onClose(dialog);
+ }
+ }
+
+ var inp = dialog.getElementsByTagName("input")[0], button;
+ if (inp) {
+ inp.focus();
+
+ if (options.value) {
+ inp.value = options.value;
+ if (options.selectValueOnOpen !== false) {
+ inp.select();
+ }
+ }
+
+ if (options.onInput)
+ CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
+ if (options.onKeyUp)
+ CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
+
+ CodeMirror.on(inp, "keydown", function(e) {
+ if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
+ if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
+ inp.blur();
+ CodeMirror.e_stop(e);
+ close();
+ }
+ if (e.keyCode == 13) callback(inp.value, e);
+ });
+
+ if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
+ } else if (button = dialog.getElementsByTagName("button")[0]) {
+ CodeMirror.on(button, "click", function() {
+ close();
+ me.focus();
+ });
+
+ if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
+
+ button.focus();
+ }
+ return close;
+ });
+
+ CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
+ closeNotification(this, null);
+ var dialog = dialogDiv(this, template, options && options.bottom);
+ var buttons = dialog.getElementsByTagName("button");
+ var closed = false, me = this, blurring = 1;
+ function close() {
+ if (closed) return;
+ closed = true;
+ CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
+ dialog.parentNode.removeChild(dialog);
+ me.focus();
+ }
+ buttons[0].focus();
+ for (var i = 0; i < buttons.length; ++i) {
+ var b = buttons[i];
+ (function(callback) {
+ CodeMirror.on(b, "click", function(e) {
+ CodeMirror.e_preventDefault(e);
+ close();
+ if (callback) callback(me);
+ });
+ })(callbacks[i]);
+ CodeMirror.on(b, "blur", function() {
+ --blurring;
+ setTimeout(function() { if (blurring <= 0) close(); }, 200);
+ });
+ CodeMirror.on(b, "focus", function() { ++blurring; });
+ }
+ });
+
+ /*
+ * openNotification
+ * Opens a notification, that can be closed with an optional timer
+ * (default 5000ms timer) and always closes on click.
+ *
+ * If a notification is opened while another is opened, it will close the
+ * currently opened one and open the new one immediately.
+ */
+ CodeMirror.defineExtension("openNotification", function(template, options) {
+ closeNotification(this, close);
+ var dialog = dialogDiv(this, template, options && options.bottom);
+ var closed = false, doneTimer;
+ var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
+
+ function close() {
+ if (closed) return;
+ closed = true;
+ clearTimeout(doneTimer);
+ CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
+ dialog.parentNode.removeChild(dialog);
+ }
+
+ CodeMirror.on(dialog, 'click', function(e) {
+ CodeMirror.e_preventDefault(e);
+ close();
+ });
+
+ if (duration)
+ doneTimer = setTimeout(close, duration);
+
+ return close;
+ });
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/display/placeholder.js b/devtools/client/shared/sourceeditor/codemirror/addon/display/placeholder.js
new file mode 100644
index 0000000000..e3f95a49c4
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/display/placeholder.js
@@ -0,0 +1,63 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
+ var prev = old && old != CodeMirror.Init;
+ if (val && !prev) {
+ cm.on("blur", onBlur);
+ cm.on("change", onChange);
+ cm.on("swapDoc", onChange);
+ onChange(cm);
+ } else if (!val && prev) {
+ cm.off("blur", onBlur);
+ cm.off("change", onChange);
+ cm.off("swapDoc", onChange);
+ clearPlaceholder(cm);
+ var wrapper = cm.getWrapperElement();
+ wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
+ }
+
+ if (val && !cm.hasFocus()) onBlur(cm);
+ });
+
+ function clearPlaceholder(cm) {
+ if (cm.state.placeholder) {
+ cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
+ cm.state.placeholder = null;
+ }
+ }
+ function setPlaceholder(cm) {
+ clearPlaceholder(cm);
+ var elt = cm.state.placeholder = document.createElement("pre");
+ elt.style.cssText = "height: 0; overflow: visible";
+ elt.style.direction = cm.getOption("direction");
+ elt.className = "CodeMirror-placeholder CodeMirror-line-like";
+ var placeHolder = cm.getOption("placeholder")
+ if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
+ elt.appendChild(placeHolder)
+ cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
+ }
+
+ function onBlur(cm) {
+ if (isEmpty(cm)) setPlaceholder(cm);
+ }
+ function onChange(cm) {
+ var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
+ wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
+
+ if (empty) setPlaceholder(cm);
+ else clearPlaceholder(cm);
+ }
+
+ function isEmpty(cm) {
+ return (cm.lineCount() === 1) && (cm.getLine(0) === "");
+ }
+}); \ No newline at end of file
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/edit/closebrackets.js b/devtools/client/shared/sourceeditor/codemirror/addon/edit/closebrackets.js
new file mode 100644
index 0000000000..95c88bdf88
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/edit/closebrackets.js
@@ -0,0 +1,191 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var defaults = {
+ pairs: "()[]{}''\"\"",
+ closeBefore: ")]}'\":;>",
+ triples: "",
+ explode: "[]{}"
+ };
+
+ var Pos = CodeMirror.Pos;
+
+ CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.removeKeyMap(keyMap);
+ cm.state.closeBrackets = null;
+ }
+ if (val) {
+ ensureBound(getOption(val, "pairs"))
+ cm.state.closeBrackets = val;
+ cm.addKeyMap(keyMap);
+ }
+ });
+
+ function getOption(conf, name) {
+ if (name == "pairs" && typeof conf == "string") return conf;
+ if (typeof conf == "object" && conf[name] != null) return conf[name];
+ return defaults[name];
+ }
+
+ var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
+ function ensureBound(chars) {
+ for (var i = 0; i < chars.length; i++) {
+ var ch = chars.charAt(i), key = "'" + ch + "'"
+ if (!keyMap[key]) keyMap[key] = handler(ch)
+ }
+ }
+ ensureBound(defaults.pairs + "`")
+
+ function handler(ch) {
+ return function(cm) { return handleChar(cm, ch); };
+ }
+
+ function getConfig(cm) {
+ var deflt = cm.state.closeBrackets;
+ if (!deflt || deflt.override) return deflt;
+ var mode = cm.getModeAt(cm.getCursor());
+ return mode.closeBrackets || deflt;
+ }
+
+ function handleBackspace(cm) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var cur = ranges[i].head;
+ cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
+ }
+ }
+
+ function handleEnter(cm) {
+ var conf = getConfig(cm);
+ var explode = conf && getOption(conf, "explode");
+ if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ cm.operation(function() {
+ var linesep = cm.lineSeparator() || "\n";
+ cm.replaceSelection(linesep + linesep, null);
+ cm.execCommand("goCharLeft");
+ ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var line = ranges[i].head.line;
+ cm.indentLine(line, null, true);
+ cm.indentLine(line + 1, null, true);
+ }
+ });
+ }
+
+ function contractSelection(sel) {
+ var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
+ return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
+ head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
+ }
+
+ function handleChar(cm, ch) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var pos = pairs.indexOf(ch);
+ if (pos == -1) return CodeMirror.Pass;
+
+ var closeBefore = getOption(conf,"closeBefore");
+
+ var triples = getOption(conf, "triples");
+
+ var identical = pairs.charAt(pos + 1) == ch;
+ var ranges = cm.listSelections();
+ var opening = pos % 2 == 0;
+
+ var type;
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], cur = range.head, curType;
+ var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
+ if (opening && !range.empty()) {
+ curType = "surround";
+ } else if ((identical || !opening) && next == ch) {
+ if (identical && stringStartsAfter(cm, cur))
+ curType = "both";
+ else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
+ curType = "skipThree";
+ else
+ curType = "skip";
+ } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+ cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+ if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
+ curType = "addFour";
+ } else if (identical) {
+ var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
+ if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+ else return CodeMirror.Pass;
+ } else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
+ curType = "both";
+ } else {
+ return CodeMirror.Pass;
+ }
+ if (!type) type = curType;
+ else if (type != curType) return CodeMirror.Pass;
+ }
+
+ var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
+ var right = pos % 2 ? ch : pairs.charAt(pos + 1);
+ cm.operation(function() {
+ if (type == "skip") {
+ cm.execCommand("goCharRight");
+ } else if (type == "skipThree") {
+ for (var i = 0; i < 3; i++)
+ cm.execCommand("goCharRight");
+ } else if (type == "surround") {
+ var sels = cm.getSelections();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = left + sels[i] + right;
+ cm.replaceSelections(sels, "around");
+ sels = cm.listSelections().slice();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = contractSelection(sels[i]);
+ cm.setSelections(sels);
+ } else if (type == "both") {
+ cm.replaceSelection(left + right, null);
+ cm.triggerElectric(left + right);
+ cm.execCommand("goCharLeft");
+ } else if (type == "addFour") {
+ cm.replaceSelection(left + left + left + left, "before");
+ cm.execCommand("goCharRight");
+ }
+ });
+ }
+
+ function charsAround(cm, pos) {
+ var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+ Pos(pos.line, pos.ch + 1));
+ return str.length == 2 ? str : null;
+ }
+
+ function stringStartsAfter(cm, pos) {
+ var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
+ return /\bstring/.test(token.type) && token.start == pos.ch &&
+ (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/edit/closetag.js b/devtools/client/shared/sourceeditor/codemirror/addon/edit/closetag.js
new file mode 100644
index 0000000000..617cf71854
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/edit/closetag.js
@@ -0,0 +1,184 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/**
+ * Tag-closer extension for CodeMirror.
+ *
+ * This extension adds an "autoCloseTags" option that can be set to
+ * either true to get the default behavior, or an object to further
+ * configure its behavior.
+ *
+ * These are supported options:
+ *
+ * `whenClosing` (default true)
+ * Whether to autoclose when the '/' of a closing tag is typed.
+ * `whenOpening` (default true)
+ * Whether to autoclose the tag when the final '>' of an opening
+ * tag is typed.
+ * `dontCloseTags` (default is empty tags for HTML, none for XML)
+ * An array of tag names that should not be autoclosed.
+ * `indentTags` (default is block tags for HTML, none for XML)
+ * An array of tag names that should, when opened, cause a
+ * blank line to be added inside the tag, and the blank line and
+ * closing line to be indented.
+ * `emptyTags` (default is none)
+ * An array of XML tag names that should be autoclosed with '/>'.
+ *
+ * See demos/closetag.html for a usage example.
+ */
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/fold/xml-fold.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../fold/xml-fold"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
+ if (old != CodeMirror.Init && old)
+ cm.removeKeyMap("autoCloseTags");
+ if (!val) return;
+ var map = {name: "autoCloseTags"};
+ if (typeof val != "object" || val.whenClosing)
+ map["'/'"] = function(cm) { return autoCloseSlash(cm); };
+ if (typeof val != "object" || val.whenOpening)
+ map["'>'"] = function(cm) { return autoCloseGT(cm); };
+ cm.addKeyMap(map);
+ });
+
+ var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
+ "source", "track", "wbr"];
+ var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
+ "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
+
+ function autoCloseGT(cm) {
+ if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ var ranges = cm.listSelections(), replacements = [];
+ var opt = cm.getOption("autoCloseTags");
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var pos = ranges[i].head, tok = cm.getTokenAt(pos);
+ var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+ var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)
+ var tagName = tagInfo && tagInfo.name
+ if (!tagName) return CodeMirror.Pass
+
+ var html = inner.mode.configuration == "html";
+ var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
+ var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
+
+ if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
+ var lowerTagName = tagName.toLowerCase();
+ // Don't process the '>' at the end of an end-tag or self-closing tag
+ if (!tagName ||
+ tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
+ tok.type == "tag" && tagInfo.close ||
+ tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
+ dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
+ closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true))
+ return CodeMirror.Pass;
+
+ var emptyTags = typeof opt == "object" && opt.emptyTags;
+ if (emptyTags && indexOf(emptyTags, tagName) > -1) {
+ replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };
+ continue;
+ }
+
+ var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
+ replacements[i] = {indent: indent,
+ text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
+ newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
+ }
+
+ var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var info = replacements[i];
+ cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
+ var sel = cm.listSelections().slice(0);
+ sel[i] = {head: info.newPos, anchor: info.newPos};
+ cm.setSelections(sel);
+ if (!dontIndentOnAutoClose && info.indent) {
+ cm.indentLine(info.newPos.line, null, true);
+ cm.indentLine(info.newPos.line + 1, null, true);
+ }
+ }
+ }
+
+ function autoCloseCurrent(cm, typingSlash) {
+ var ranges = cm.listSelections(), replacements = [];
+ var head = typingSlash ? "/" : "</";
+ var opt = cm.getOption("autoCloseTags");
+ var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var pos = ranges[i].head, tok = cm.getTokenAt(pos);
+ var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+ if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
+ tok.start != pos.ch - 1))
+ return CodeMirror.Pass;
+ // Kludge to get around the fact that we are not in XML mode
+ // when completing in JS/CSS snippet in htmlmixed mode. Does not
+ // work for other XML embedded languages (there is no general
+ // way to go from a mixed mode to its current XML state).
+ var replacement, mixed = inner.mode.name != "xml" && cm.getMode().name == "htmlmixed"
+ if (mixed && inner.mode.name == "javascript") {
+ replacement = head + "script";
+ } else if (mixed && inner.mode.name == "css") {
+ replacement = head + "style";
+ } else {
+ var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)
+ if (!context || (context.length && closingTagExists(cm, context, context[context.length - 1], pos)))
+ return CodeMirror.Pass;
+ replacement = head + context[context.length - 1]
+ }
+ if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
+ replacements[i] = replacement;
+ }
+ cm.replaceSelections(replacements);
+ ranges = cm.listSelections();
+ if (!dontIndentOnAutoClose) {
+ for (var i = 0; i < ranges.length; i++)
+ if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
+ cm.indentLine(ranges[i].head.line);
+ }
+ }
+
+ function autoCloseSlash(cm) {
+ if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ return autoCloseCurrent(cm, true);
+ }
+
+ CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
+
+ function indexOf(collection, elt) {
+ if (collection.indexOf) return collection.indexOf(elt);
+ for (var i = 0, e = collection.length; i < e; ++i)
+ if (collection[i] == elt) return i;
+ return -1;
+ }
+
+ // If xml-fold is loaded, we use its functionality to try and verify
+ // whether a given tag is actually unclosed.
+ function closingTagExists(cm, context, tagName, pos, newTag) {
+ if (!CodeMirror.scanForClosingTag) return false;
+ var end = Math.min(cm.lastLine() + 1, pos.line + 500);
+ var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
+ if (!nextClose || nextClose.tag != tagName) return false;
+ // If the immediate wrapping context contains onCx instances of
+ // the same tag, a closing tag only exists if there are at least
+ // that many closing tags of that type following.
+ var onCx = newTag ? 1 : 0
+ for (var i = context.length - 1; i >= 0; i--) {
+ if (context[i] == tagName) ++onCx
+ else break
+ }
+ pos = nextClose.to;
+ for (var i = 1; i < onCx; i++) {
+ var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
+ if (!next || next.tag != tagName) return false;
+ pos = next.to;
+ }
+ return true;
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/edit/continuelist.js b/devtools/client/shared/sourceeditor/codemirror/addon/edit/continuelist.js
new file mode 100644
index 0000000000..222995f4d1
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/edit/continuelist.js
@@ -0,0 +1,99 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
+ emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
+ unorderedListRE = /[*+-]\s/;
+
+ CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
+ if (cm.getOption("disableInput")) return CodeMirror.Pass;
+ var ranges = cm.listSelections(), replacements = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var pos = ranges[i].head;
+
+ // If we're not in Markdown mode, fall back to normal newlineAndIndent
+ var eolState = cm.getStateAfter(pos.line);
+ var inner = CodeMirror.innerMode(cm.getMode(), eolState);
+ if (inner.mode.name !== "markdown") {
+ cm.execCommand("newlineAndIndent");
+ return;
+ } else {
+ eolState = inner.state;
+ }
+
+ var inList = eolState.list !== false;
+ var inQuote = eolState.quote !== 0;
+
+ var line = cm.getLine(pos.line), match = listRE.exec(line);
+ var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
+ if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
+ cm.execCommand("newlineAndIndent");
+ return;
+ }
+ if (emptyListRE.test(line)) {
+ if (!/>\s*$/.test(line)) cm.replaceRange("", {
+ line: pos.line, ch: 0
+ }, {
+ line: pos.line, ch: pos.ch + 1
+ });
+ replacements[i] = "\n";
+ } else {
+ var indent = match[1], after = match[5];
+ var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
+ var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
+ replacements[i] = "\n" + indent + bullet + after;
+
+ if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
+ }
+ }
+
+ cm.replaceSelections(replacements);
+ };
+
+ // Auto-updating Markdown list numbers when a new item is added to the
+ // middle of a list
+ function incrementRemainingMarkdownListNumbers(cm, pos) {
+ var startLine = pos.line, lookAhead = 0, skipCount = 0;
+ var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];
+
+ do {
+ lookAhead += 1;
+ var nextLineNumber = startLine + lookAhead;
+ var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);
+
+ if (nextItem) {
+ var nextIndent = nextItem[1];
+ var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
+ var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;
+
+ if (startIndent === nextIndent && !isNaN(nextNumber)) {
+ if (newNumber === nextNumber) itemNumber = nextNumber + 1;
+ if (newNumber > nextNumber) itemNumber = newNumber + 1;
+ cm.replaceRange(
+ nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
+ {
+ line: nextLineNumber, ch: 0
+ }, {
+ line: nextLineNumber, ch: nextLine.length
+ });
+ } else {
+ if (startIndent.length > nextIndent.length) return;
+ // This doesn't run if the next line immediatley indents, as it is
+ // not clear of the users intention (new indented item or same level)
+ if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
+ skipCount += 1;
+ }
+ }
+ } while (nextItem);
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/edit/matchbrackets.js b/devtools/client/shared/sourceeditor/codemirror/addon/edit/matchbrackets.js
new file mode 100644
index 0000000000..a688935672
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/edit/matchbrackets.js
@@ -0,0 +1,150 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
+ (document.documentMode == null || document.documentMode < 8);
+
+ var Pos = CodeMirror.Pos;
+
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
+
+ function bracketRegex(config) {
+ return config && config.bracketRegex || /[(){}[\]]/
+ }
+
+ function findMatchingBracket(cm, where, config) {
+ var line = cm.getLineHandle(where.line), pos = where.ch - 1;
+ var afterCursor = config && config.afterCursor
+ if (afterCursor == null)
+ afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
+ var re = bracketRegex(config)
+
+ // A cursor is defined as between two characters, but in in vim command mode
+ // (i.e. not insert mode), the cursor is visually represented as a
+ // highlighted box on top of the 2nd character. Otherwise, we allow matches
+ // from before or after the cursor.
+ var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
+ re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
+ if (!match) return null;
+ var dir = match.charAt(1) == ">" ? 1 : -1;
+ if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
+ var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
+
+ var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
+ if (found == null) return null;
+ return {from: Pos(where.line, pos), to: found && found.pos,
+ match: found && found.ch == match.charAt(0), forward: dir > 0};
+ }
+
+ // bracketRegex is used to specify which type of bracket to scan
+ // should be a regexp, e.g. /[[\]]/
+ //
+ // Note: If "where" is on an open bracket, then this bracket is ignored.
+ //
+ // Returns false when no bracket was found, null when it reached
+ // maxScanLines and gave up
+ function scanForBracket(cm, where, dir, style, config) {
+ var maxScanLen = (config && config.maxScanLineLength) || 10000;
+ var maxScanLines = (config && config.maxScanLines) || 1000;
+
+ var stack = [];
+ var re = bracketRegex(config)
+ var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
+ : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
+ for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
+ var line = cm.getLine(lineNo);
+ if (!line) continue;
+ var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
+ if (line.length > maxScanLen) continue;
+ if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
+ for (; pos != end; pos += dir) {
+ var ch = line.charAt(pos);
+ if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
+ var match = matching[ch];
+ if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
+ else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
+ else stack.pop();
+ }
+ }
+ }
+ return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
+ }
+
+ function matchBrackets(cm, autoclear, config) {
+ // Disable brace matching in long lines, since it'll cause hugely slow updates
+ var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
+ var marks = [], ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
+ if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
+ var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+ marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
+ if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
+ marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
+ }
+ }
+
+ if (marks.length) {
+ // Kludge to work around the IE bug from issue #1193, where text
+ // input stops going to the textare whever this fires.
+ if (ie_lt8 && cm.state.focused) cm.focus();
+
+ var clear = function() {
+ cm.operation(function() {
+ for (var i = 0; i < marks.length; i++) marks[i].clear();
+ });
+ };
+ if (autoclear) setTimeout(clear, 800);
+ else return clear;
+ }
+ }
+
+ function doMatchBrackets(cm) {
+ cm.operation(function() {
+ if (cm.state.matchBrackets.currentlyHighlighted) {
+ cm.state.matchBrackets.currentlyHighlighted();
+ cm.state.matchBrackets.currentlyHighlighted = null;
+ }
+ cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+ });
+ }
+
+ CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.off("cursorActivity", doMatchBrackets);
+ if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
+ cm.state.matchBrackets.currentlyHighlighted();
+ cm.state.matchBrackets.currentlyHighlighted = null;
+ }
+ }
+ if (val) {
+ cm.state.matchBrackets = typeof val == "object" ? val : {};
+ cm.on("cursorActivity", doMatchBrackets);
+ }
+ });
+
+ CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+ CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
+ // Backwards-compatibility kludge
+ if (oldConfig || typeof config == "boolean") {
+ if (!oldConfig) {
+ config = config ? {strict: true} : null
+ } else {
+ oldConfig.strict = config
+ config = oldConfig
+ }
+ }
+ return findMatchingBracket(this, pos, config)
+ });
+ CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
+ return scanForBracket(this, pos, dir, style, config);
+ });
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/edit/matchtags.js b/devtools/client/shared/sourceeditor/codemirror/addon/edit/matchtags.js
new file mode 100644
index 0000000000..e50d353a5f
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/edit/matchtags.js
@@ -0,0 +1,66 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/fold/xml-fold.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../fold/xml-fold"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.off("cursorActivity", doMatchTags);
+ cm.off("viewportChange", maybeUpdateMatch);
+ clear(cm);
+ }
+ if (val) {
+ cm.state.matchBothTags = typeof val == "object" && val.bothTags;
+ cm.on("cursorActivity", doMatchTags);
+ cm.on("viewportChange", maybeUpdateMatch);
+ doMatchTags(cm);
+ }
+ });
+
+ function clear(cm) {
+ if (cm.state.tagHit) cm.state.tagHit.clear();
+ if (cm.state.tagOther) cm.state.tagOther.clear();
+ cm.state.tagHit = cm.state.tagOther = null;
+ }
+
+ function doMatchTags(cm) {
+ cm.state.failedTagMatch = false;
+ cm.operation(function() {
+ clear(cm);
+ if (cm.somethingSelected()) return;
+ var cur = cm.getCursor(), range = cm.getViewport();
+ range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
+ var match = CodeMirror.findMatchingTag(cm, cur, range);
+ if (!match) return;
+ if (cm.state.matchBothTags) {
+ var hit = match.at == "open" ? match.open : match.close;
+ if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
+ }
+ var other = match.at == "close" ? match.open : match.close;
+ if (other)
+ cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
+ else
+ cm.state.failedTagMatch = true;
+ });
+ }
+
+ function maybeUpdateMatch(cm) {
+ if (cm.state.failedTagMatch) doMatchTags(cm);
+ }
+
+ CodeMirror.commands.toMatchingTag = function(cm) {
+ var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
+ if (found) {
+ var other = found.at == "close" ? found.open : found.close;
+ if (other) cm.extendSelection(other.to, other.from);
+ }
+ };
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/edit/trailingspace.js b/devtools/client/shared/sourceeditor/codemirror/addon/edit/trailingspace.js
new file mode 100644
index 0000000000..bda4084380
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/edit/trailingspace.js
@@ -0,0 +1,27 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
+ if (prev == CodeMirror.Init) prev = false;
+ if (prev && !val)
+ cm.removeOverlay("trailingspace");
+ else if (!prev && val)
+ cm.addOverlay({
+ token: function(stream) {
+ for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
+ if (i > stream.pos) { stream.pos = i; return null; }
+ stream.pos = l;
+ return "trailingspace";
+ },
+ name: "trailingspace"
+ });
+ });
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/brace-fold.js b/devtools/client/shared/sourceeditor/codemirror/addon/fold/brace-fold.js
new file mode 100644
index 0000000000..5479b5135b
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/brace-fold.js
@@ -0,0 +1,105 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("fold", "brace", function(cm, start) {
+ var line = start.line, lineText = cm.getLine(line);
+ var tokenType;
+
+ function findOpening(openCh) {
+ for (var at = start.ch, pass = 0;;) {
+ var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
+ if (found == -1) {
+ if (pass == 1) break;
+ pass = 1;
+ at = lineText.length;
+ continue;
+ }
+ if (pass == 1 && found < start.ch) break;
+ tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
+ if (!/^(comment|string)/.test(tokenType)) return found + 1;
+ at = found - 1;
+ }
+ }
+
+ var startToken = "{", endToken = "}", startCh = findOpening("{");
+ if (startCh == null) {
+ startToken = "[", endToken = "]";
+ startCh = findOpening("[");
+ }
+
+ if (startCh == null) return;
+ var count = 1, lastLine = cm.lastLine(), end, endCh;
+ outer: for (var i = line; i <= lastLine; ++i) {
+ var text = cm.getLine(i), pos = i == line ? startCh : 0;
+ for (;;) {
+ var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
+ if (nextOpen < 0) nextOpen = text.length;
+ if (nextClose < 0) nextClose = text.length;
+ pos = Math.min(nextOpen, nextClose);
+ if (pos == text.length) break;
+ if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
+ if (pos == nextOpen) ++count;
+ else if (!--count) { end = i; endCh = pos; break outer; }
+ }
+ ++pos;
+ }
+ }
+ if (end == null || line == end) return;
+ return {from: CodeMirror.Pos(line, startCh),
+ to: CodeMirror.Pos(end, endCh)};
+});
+
+CodeMirror.registerHelper("fold", "import", function(cm, start) {
+ function hasImport(line) {
+ if (line < cm.firstLine() || line > cm.lastLine()) return null;
+ var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
+ if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
+ if (start.type != "keyword" || start.string != "import") return null;
+ // Now find closing semicolon, return its position
+ for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
+ var text = cm.getLine(i), semi = text.indexOf(";");
+ if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
+ }
+ }
+
+ var startLine = start.line, has = hasImport(startLine), prev;
+ if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
+ return null;
+ for (var end = has.end;;) {
+ var next = hasImport(end.line + 1);
+ if (next == null) break;
+ end = next.end;
+ }
+ return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
+});
+
+CodeMirror.registerHelper("fold", "include", function(cm, start) {
+ function hasInclude(line) {
+ if (line < cm.firstLine() || line > cm.lastLine()) return null;
+ var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
+ if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
+ if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
+ }
+
+ var startLine = start.line, has = hasInclude(startLine);
+ if (has == null || hasInclude(startLine - 1) != null) return null;
+ for (var end = startLine;;) {
+ var next = hasInclude(end + 1);
+ if (next == null) break;
+ ++end;
+ }
+ return {from: CodeMirror.Pos(startLine, has + 1),
+ to: cm.clipPos(CodeMirror.Pos(end))};
+});
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/comment-fold.js b/devtools/client/shared/sourceeditor/codemirror/addon/fold/comment-fold.js
new file mode 100644
index 0000000000..58cf1bd86d
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/comment-fold.js
@@ -0,0 +1,59 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
+ return mode.blockCommentStart && mode.blockCommentEnd;
+}, function(cm, start) {
+ var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
+ if (!startToken || !endToken) return;
+ var line = start.line, lineText = cm.getLine(line);
+
+ var startCh;
+ for (var at = start.ch, pass = 0;;) {
+ var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
+ if (found == -1) {
+ if (pass == 1) return;
+ pass = 1;
+ at = lineText.length;
+ continue;
+ }
+ if (pass == 1 && found < start.ch) return;
+ if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
+ (found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
+ !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
+ startCh = found + startToken.length;
+ break;
+ }
+ at = found - 1;
+ }
+
+ var depth = 1, lastLine = cm.lastLine(), end, endCh;
+ outer: for (var i = line; i <= lastLine; ++i) {
+ var text = cm.getLine(i), pos = i == line ? startCh : 0;
+ for (;;) {
+ var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
+ if (nextOpen < 0) nextOpen = text.length;
+ if (nextClose < 0) nextClose = text.length;
+ pos = Math.min(nextOpen, nextClose);
+ if (pos == text.length) break;
+ if (pos == nextOpen) ++depth;
+ else if (!--depth) { end = i; endCh = pos; break outer; }
+ ++pos;
+ }
+ }
+ if (end == null || line == end && endCh == startCh) return;
+ return {from: CodeMirror.Pos(line, startCh),
+ to: CodeMirror.Pos(end, endCh)};
+});
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldcode.js b/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldcode.js
new file mode 100644
index 0000000000..902f1a6f95
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldcode.js
@@ -0,0 +1,152 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ function doFold(cm, pos, options, force) {
+ if (options && options.call) {
+ var finder = options;
+ options = null;
+ } else {
+ var finder = getOption(cm, options, "rangeFinder");
+ }
+ if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
+ var minSize = getOption(cm, options, "minFoldSize");
+
+ function getRange(allowFolded) {
+ var range = finder(cm, pos);
+ if (!range || range.to.line - range.from.line < minSize) return null;
+ var marks = cm.findMarksAt(range.from);
+ for (var i = 0; i < marks.length; ++i) {
+ if (marks[i].__isFold && force !== "fold") {
+ if (!allowFolded) return null;
+ range.cleared = true;
+ marks[i].clear();
+ }
+ }
+ return range;
+ }
+
+ var range = getRange(true);
+ if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
+ pos = CodeMirror.Pos(pos.line - 1, 0);
+ range = getRange(false);
+ }
+ if (!range || range.cleared || force === "unfold") return;
+
+ var myWidget = makeWidget(cm, options);
+ CodeMirror.on(myWidget, "mousedown", function(e) {
+ myRange.clear();
+ CodeMirror.e_preventDefault(e);
+ });
+ var myRange = cm.markText(range.from, range.to, {
+ replacedWith: myWidget,
+ clearOnEnter: getOption(cm, options, "clearOnEnter"),
+ __isFold: true
+ });
+ myRange.on("clear", function(from, to) {
+ CodeMirror.signal(cm, "unfold", cm, from, to);
+ });
+ CodeMirror.signal(cm, "fold", cm, range.from, range.to);
+ }
+
+ function makeWidget(cm, options) {
+ var widget = getOption(cm, options, "widget");
+ if (typeof widget == "string") {
+ var text = document.createTextNode(widget);
+ widget = document.createElement("span");
+ widget.appendChild(text);
+ widget.className = "CodeMirror-foldmarker";
+ } else if (widget) {
+ widget = widget.cloneNode(true)
+ }
+ return widget;
+ }
+
+ // Clumsy backwards-compatible interface
+ CodeMirror.newFoldFunction = function(rangeFinder, widget) {
+ return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
+ };
+
+ // New-style interface
+ CodeMirror.defineExtension("foldCode", function(pos, options, force) {
+ doFold(this, pos, options, force);
+ });
+
+ CodeMirror.defineExtension("isFolded", function(pos) {
+ var marks = this.findMarksAt(pos);
+ for (var i = 0; i < marks.length; ++i)
+ if (marks[i].__isFold) return true;
+ });
+
+ CodeMirror.commands.toggleFold = function(cm) {
+ cm.foldCode(cm.getCursor());
+ };
+ CodeMirror.commands.fold = function(cm) {
+ cm.foldCode(cm.getCursor(), null, "fold");
+ };
+ CodeMirror.commands.unfold = function(cm) {
+ cm.foldCode(cm.getCursor(), null, "unfold");
+ };
+ CodeMirror.commands.foldAll = function(cm) {
+ cm.operation(function() {
+ for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
+ cm.foldCode(CodeMirror.Pos(i, 0), null, "fold");
+ });
+ };
+ CodeMirror.commands.unfoldAll = function(cm) {
+ cm.operation(function() {
+ for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
+ cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold");
+ });
+ };
+
+ CodeMirror.registerHelper("fold", "combine", function() {
+ var funcs = Array.prototype.slice.call(arguments, 0);
+ return function(cm, start) {
+ for (var i = 0; i < funcs.length; ++i) {
+ var found = funcs[i](cm, start);
+ if (found) return found;
+ }
+ };
+ });
+
+ CodeMirror.registerHelper("fold", "auto", function(cm, start) {
+ var helpers = cm.getHelpers(start, "fold");
+ for (var i = 0; i < helpers.length; i++) {
+ var cur = helpers[i](cm, start);
+ if (cur) return cur;
+ }
+ });
+
+ var defaultOptions = {
+ rangeFinder: CodeMirror.fold.auto,
+ widget: "\u2194",
+ minFoldSize: 0,
+ scanUp: false,
+ clearOnEnter: true
+ };
+
+ CodeMirror.defineOption("foldOptions", null);
+
+ function getOption(cm, options, name) {
+ if (options && options[name] !== undefined)
+ return options[name];
+ var editorOptions = cm.options.foldOptions;
+ if (editorOptions && editorOptions[name] !== undefined)
+ return editorOptions[name];
+ return defaultOptions[name];
+ }
+
+ CodeMirror.defineExtension("foldOption", function(options, name) {
+ return getOption(this, options, name);
+ });
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.css b/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.css
new file mode 100644
index 0000000000..ad19ae2d3e
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.css
@@ -0,0 +1,20 @@
+.CodeMirror-foldmarker {
+ color: blue;
+ text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
+ font-family: arial;
+ line-height: .3;
+ cursor: pointer;
+}
+.CodeMirror-foldgutter {
+ width: .7em;
+}
+.CodeMirror-foldgutter-open,
+.CodeMirror-foldgutter-folded {
+ cursor: pointer;
+}
+.CodeMirror-foldgutter-open:after {
+ content: "\25BE";
+}
+.CodeMirror-foldgutter-folded:after {
+ content: "\25B8";
+}
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.js b/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.js
new file mode 100644
index 0000000000..a41fbfd21e
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/foldgutter.js
@@ -0,0 +1,151 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/fold/foldcode.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "./foldcode"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.clearGutter(cm.state.foldGutter.options.gutter);
+ cm.state.foldGutter = null;
+ cm.off("gutterClick", onGutterClick);
+ cm.off("changes", onChange);
+ cm.off("viewportChange", onViewportChange);
+ cm.off("fold", onFold);
+ cm.off("unfold", onFold);
+ cm.off("swapDoc", onChange);
+ }
+ if (val) {
+ cm.state.foldGutter = new State(parseOptions(val));
+ updateInViewport(cm);
+ cm.on("gutterClick", onGutterClick);
+ cm.on("changes", onChange);
+ cm.on("viewportChange", onViewportChange);
+ cm.on("fold", onFold);
+ cm.on("unfold", onFold);
+ cm.on("swapDoc", onChange);
+ }
+ });
+
+ var Pos = CodeMirror.Pos;
+
+ function State(options) {
+ this.options = options;
+ this.from = this.to = 0;
+ }
+
+ function parseOptions(opts) {
+ if (opts === true) opts = {};
+ if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
+ if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
+ if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
+ return opts;
+ }
+
+ function isFolded(cm, line) {
+ var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
+ for (var i = 0; i < marks.length; ++i) {
+ if (marks[i].__isFold) {
+ var fromPos = marks[i].find(-1);
+ if (fromPos && fromPos.line === line)
+ return marks[i];
+ }
+ }
+ }
+
+ function marker(spec) {
+ if (typeof spec == "string") {
+ var elt = document.createElement("div");
+ elt.className = spec + " CodeMirror-guttermarker-subtle";
+ return elt;
+ } else {
+ return spec.cloneNode(true);
+ }
+ }
+
+ function updateFoldInfo(cm, from, to) {
+ var opts = cm.state.foldGutter.options, cur = from;
+ var minSize = cm.foldOption(opts, "minFoldSize");
+ var func = cm.foldOption(opts, "rangeFinder");
+ cm.eachLine(from, to, function(line) {
+ var mark = null;
+ if (isFolded(cm, cur)) {
+ mark = marker(opts.indicatorFolded);
+ } else {
+ var pos = Pos(cur, 0);
+ var range = func && func(cm, pos);
+ if (range && range.to.line - range.from.line >= minSize)
+ mark = marker(opts.indicatorOpen);
+ }
+ cm.setGutterMarker(line, opts.gutter, mark);
+ ++cur;
+ });
+ }
+
+ function updateInViewport(cm) {
+ var vp = cm.getViewport(), state = cm.state.foldGutter;
+ if (!state) return;
+ cm.operation(function() {
+ updateFoldInfo(cm, vp.from, vp.to);
+ });
+ state.from = vp.from; state.to = vp.to;
+ }
+
+ function onGutterClick(cm, line, gutter) {
+ var state = cm.state.foldGutter;
+ if (!state) return;
+ var opts = state.options;
+ if (gutter != opts.gutter) return;
+ var folded = isFolded(cm, line);
+ if (folded) folded.clear();
+ else cm.foldCode(Pos(line, 0), opts);
+ }
+
+ function onChange(cm) {
+ var state = cm.state.foldGutter;
+ if (!state) return;
+ var opts = state.options;
+ state.from = state.to = 0;
+ clearTimeout(state.changeUpdate);
+ state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
+ }
+
+ function onViewportChange(cm) {
+ var state = cm.state.foldGutter;
+ if (!state) return;
+ var opts = state.options;
+ clearTimeout(state.changeUpdate);
+ state.changeUpdate = setTimeout(function() {
+ var vp = cm.getViewport();
+ if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
+ updateInViewport(cm);
+ } else {
+ cm.operation(function() {
+ if (vp.from < state.from) {
+ updateFoldInfo(cm, vp.from, state.from);
+ state.from = vp.from;
+ }
+ if (vp.to > state.to) {
+ updateFoldInfo(cm, state.to, vp.to);
+ state.to = vp.to;
+ }
+ });
+ }
+ }, opts.updateViewportTimeSpan || 400);
+ }
+
+ function onFold(cm, from) {
+ var state = cm.state.foldGutter;
+ if (!state) return;
+ var line = from.line;
+ if (line >= state.from && line < state.to)
+ updateFoldInfo(cm, line, line + 1);
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/indent-fold.js b/devtools/client/shared/sourceeditor/codemirror/addon/fold/indent-fold.js
new file mode 100644
index 0000000000..be72c2447d
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/indent-fold.js
@@ -0,0 +1,48 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+function lineIndent(cm, lineNo) {
+ var text = cm.getLine(lineNo)
+ var spaceTo = text.search(/\S/)
+ if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))
+ return -1
+ return CodeMirror.countColumn(text, null, cm.getOption("tabSize"))
+}
+
+CodeMirror.registerHelper("fold", "indent", function(cm, start) {
+ var myIndent = lineIndent(cm, start.line)
+ if (myIndent < 0) return
+ var lastLineInFold = null
+
+ // Go through lines until we find a line that definitely doesn't belong in
+ // the block we're folding, or to the end.
+ for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
+ var indent = lineIndent(cm, i)
+ if (indent == -1) {
+ } else if (indent > myIndent) {
+ // Lines with a greater indent are considered part of the block.
+ lastLineInFold = i;
+ } else {
+ // If this line has non-space, non-comment content, and is
+ // indented less or equal to the start line, it is the start of
+ // another block.
+ break;
+ }
+ }
+ if (lastLineInFold) return {
+ from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
+ to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
+ };
+});
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/markdown-fold.js b/devtools/client/shared/sourceeditor/codemirror/addon/fold/markdown-fold.js
new file mode 100644
index 0000000000..133a55f8c1
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/markdown-fold.js
@@ -0,0 +1,49 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
+ var maxDepth = 100;
+
+ function isHeader(lineNo) {
+ var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
+ return tokentype && /\bheader\b/.test(tokentype);
+ }
+
+ function headerLevel(lineNo, line, nextLine) {
+ var match = line && line.match(/^#+/);
+ if (match && isHeader(lineNo)) return match[0].length;
+ match = nextLine && nextLine.match(/^[=\-]+\s*$/);
+ if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
+ return maxDepth;
+ }
+
+ var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
+ var level = headerLevel(start.line, firstLine, nextLine);
+ if (level === maxDepth) return undefined;
+
+ var lastLineNo = cm.lastLine();
+ var end = start.line, nextNextLine = cm.getLine(end + 2);
+ while (end < lastLineNo) {
+ if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
+ ++end;
+ nextLine = nextNextLine;
+ nextNextLine = cm.getLine(end + 2);
+ }
+
+ return {
+ from: CodeMirror.Pos(start.line, firstLine.length),
+ to: CodeMirror.Pos(end, cm.getLine(end).length)
+ };
+});
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/fold/xml-fold.js b/devtools/client/shared/sourceeditor/codemirror/addon/fold/xml-fold.js
new file mode 100644
index 0000000000..34d69251b2
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/fold/xml-fold.js
@@ -0,0 +1,184 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var Pos = CodeMirror.Pos;
+ function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
+
+ var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
+ var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
+ var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
+
+ function Iter(cm, line, ch, range) {
+ this.line = line; this.ch = ch;
+ this.cm = cm; this.text = cm.getLine(line);
+ this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
+ this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
+ }
+
+ function tagAt(iter, ch) {
+ var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
+ return type && /\btag\b/.test(type);
+ }
+
+ function nextLine(iter) {
+ if (iter.line >= iter.max) return;
+ iter.ch = 0;
+ iter.text = iter.cm.getLine(++iter.line);
+ return true;
+ }
+ function prevLine(iter) {
+ if (iter.line <= iter.min) return;
+ iter.text = iter.cm.getLine(--iter.line);
+ iter.ch = iter.text.length;
+ return true;
+ }
+
+ function toTagEnd(iter) {
+ for (;;) {
+ var gt = iter.text.indexOf(">", iter.ch);
+ if (gt == -1) { if (nextLine(iter)) continue; else return; }
+ if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
+ var lastSlash = iter.text.lastIndexOf("/", gt);
+ var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
+ iter.ch = gt + 1;
+ return selfClose ? "selfClose" : "regular";
+ }
+ }
+ function toTagStart(iter) {
+ for (;;) {
+ var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
+ if (lt == -1) { if (prevLine(iter)) continue; else return; }
+ if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
+ xmlTagStart.lastIndex = lt;
+ iter.ch = lt;
+ var match = xmlTagStart.exec(iter.text);
+ if (match && match.index == lt) return match;
+ }
+ }
+
+ function toNextTag(iter) {
+ for (;;) {
+ xmlTagStart.lastIndex = iter.ch;
+ var found = xmlTagStart.exec(iter.text);
+ if (!found) { if (nextLine(iter)) continue; else return; }
+ if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
+ iter.ch = found.index + found[0].length;
+ return found;
+ }
+ }
+ function toPrevTag(iter) {
+ for (;;) {
+ var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
+ if (gt == -1) { if (prevLine(iter)) continue; else return; }
+ if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
+ var lastSlash = iter.text.lastIndexOf("/", gt);
+ var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
+ iter.ch = gt + 1;
+ return selfClose ? "selfClose" : "regular";
+ }
+ }
+
+ function findMatchingClose(iter, tag) {
+ var stack = [];
+ for (;;) {
+ var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
+ if (!next || !(end = toTagEnd(iter))) return;
+ if (end == "selfClose") continue;
+ if (next[1]) { // closing tag
+ for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
+ stack.length = i;
+ break;
+ }
+ if (i < 0 && (!tag || tag == next[2])) return {
+ tag: next[2],
+ from: Pos(startLine, startCh),
+ to: Pos(iter.line, iter.ch)
+ };
+ } else { // opening tag
+ stack.push(next[2]);
+ }
+ }
+ }
+ function findMatchingOpen(iter, tag) {
+ var stack = [];
+ for (;;) {
+ var prev = toPrevTag(iter);
+ if (!prev) return;
+ if (prev == "selfClose") { toTagStart(iter); continue; }
+ var endLine = iter.line, endCh = iter.ch;
+ var start = toTagStart(iter);
+ if (!start) return;
+ if (start[1]) { // closing tag
+ stack.push(start[2]);
+ } else { // opening tag
+ for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
+ stack.length = i;
+ break;
+ }
+ if (i < 0 && (!tag || tag == start[2])) return {
+ tag: start[2],
+ from: Pos(iter.line, iter.ch),
+ to: Pos(endLine, endCh)
+ };
+ }
+ }
+ }
+
+ CodeMirror.registerHelper("fold", "xml", function(cm, start) {
+ var iter = new Iter(cm, start.line, 0);
+ for (;;) {
+ var openTag = toNextTag(iter)
+ if (!openTag || iter.line != start.line) return
+ var end = toTagEnd(iter)
+ if (!end) return
+ if (!openTag[1] && end != "selfClose") {
+ var startPos = Pos(iter.line, iter.ch);
+ var endPos = findMatchingClose(iter, openTag[2]);
+ return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
+ }
+ }
+ });
+ CodeMirror.findMatchingTag = function(cm, pos, range) {
+ var iter = new Iter(cm, pos.line, pos.ch, range);
+ if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
+ var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
+ var start = end && toTagStart(iter);
+ if (!end || !start || cmp(iter, pos) > 0) return;
+ var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
+ if (end == "selfClose") return {open: here, close: null, at: "open"};
+
+ if (start[1]) { // closing tag
+ return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
+ } else { // opening tag
+ iter = new Iter(cm, to.line, to.ch, range);
+ return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
+ }
+ };
+
+ CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
+ var iter = new Iter(cm, pos.line, pos.ch, range);
+ for (;;) {
+ var open = findMatchingOpen(iter, tag);
+ if (!open) break;
+ var forward = new Iter(cm, pos.line, pos.ch, range);
+ var close = findMatchingClose(forward, open.tag);
+ if (close) return {open: open, close: close};
+ }
+ };
+
+ // Used by addon/edit/closetag.js
+ CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
+ var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
+ return findMatchingClose(iter, name);
+ };
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/runmode/runmode.js b/devtools/client/shared/sourceeditor/codemirror/addon/runmode/runmode.js
new file mode 100644
index 0000000000..7f23196ac1
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/runmode/runmode.js
@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.runMode = function(string, modespec, callback, options) {
+ var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
+ var ie = /MSIE \d/.test(navigator.userAgent);
+ var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
+
+ if (callback.appendChild) {
+ var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
+ var node = callback, col = 0;
+ node.innerHTML = "";
+ callback = function(text, style) {
+ if (text == "\n") {
+ // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
+ // Emitting a carriage return makes everything ok.
+ node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
+ col = 0;
+ return;
+ }
+ var content = "";
+ // replace tabs
+ for (var pos = 0;;) {
+ var idx = text.indexOf("\t", pos);
+ if (idx == -1) {
+ content += text.slice(pos);
+ col += text.length - pos;
+ break;
+ } else {
+ col += idx - pos;
+ content += text.slice(pos, idx);
+ var size = tabSize - col % tabSize;
+ col += size;
+ for (var i = 0; i < size; ++i) content += " ";
+ pos = idx + 1;
+ }
+ }
+
+ if (style) {
+ var sp = node.appendChild(document.createElement("span"));
+ sp.className = "cm-" + style.replace(/ +/g, " cm-");
+ sp.appendChild(document.createTextNode(content));
+ } else {
+ node.appendChild(document.createTextNode(content));
+ }
+ };
+ }
+
+ var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
+ for (var i = 0, e = lines.length; i < e; ++i) {
+ if (i) callback("\n");
+ var stream = new CodeMirror.StringStream(lines[i]);
+ if (!stream.string && mode.blankLine) mode.blankLine(state);
+ while (!stream.eol()) {
+ var style = mode.token(stream, state);
+ callback(stream.current(), style, i, stream.start, state);
+ stream.start = stream.pos;
+ }
+ }
+};
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/scroll/annotatescrollbar.js b/devtools/client/shared/sourceeditor/codemirror/addon/scroll/annotatescrollbar.js
new file mode 100644
index 0000000000..c12e44cda5
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/scroll/annotatescrollbar.js
@@ -0,0 +1,128 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineExtension("annotateScrollbar", function(options) {
+ if (typeof options == "string") options = {className: options};
+ return new Annotation(this, options);
+ });
+
+ CodeMirror.defineOption("scrollButtonHeight", 0);
+
+ function Annotation(cm, options) {
+ this.cm = cm;
+ this.options = options;
+ this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
+ this.annotations = [];
+ this.doRedraw = this.doUpdate = null;
+ this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
+ this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
+ this.computeScale();
+
+ function scheduleRedraw(delay) {
+ clearTimeout(self.doRedraw);
+ self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
+ }
+
+ var self = this;
+ cm.on("refresh", this.resizeHandler = function() {
+ clearTimeout(self.doUpdate);
+ self.doUpdate = setTimeout(function() {
+ if (self.computeScale()) scheduleRedraw(20);
+ }, 100);
+ });
+ cm.on("markerAdded", this.resizeHandler);
+ cm.on("markerCleared", this.resizeHandler);
+ if (options.listenForChanges !== false)
+ cm.on("changes", this.changeHandler = function() {
+ scheduleRedraw(250);
+ });
+ }
+
+ Annotation.prototype.computeScale = function() {
+ var cm = this.cm;
+ var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
+ cm.getScrollerElement().scrollHeight
+ if (hScale != this.hScale) {
+ this.hScale = hScale;
+ return true;
+ }
+ };
+
+ Annotation.prototype.update = function(annotations) {
+ this.annotations = annotations;
+ this.redraw();
+ };
+
+ Annotation.prototype.redraw = function(compute) {
+ if (compute !== false) this.computeScale();
+ var cm = this.cm, hScale = this.hScale;
+
+ var frag = document.createDocumentFragment(), anns = this.annotations;
+
+ var wrapping = cm.getOption("lineWrapping");
+ var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
+ var curLine = null, curLineObj = null;
+
+ function getY(pos, top) {
+ if (curLine != pos.line) {
+ curLine = pos.line
+ curLineObj = cm.getLineHandle(pos.line)
+ var visual = cm.getLineHandleVisualStart(curLineObj)
+ if (visual != curLineObj) {
+ curLine = cm.getLineNumber(visual)
+ curLineObj = visual
+ }
+ }
+ if ((curLineObj.widgets && curLineObj.widgets.length) ||
+ (wrapping && curLineObj.height > singleLineH))
+ return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
+ var topY = cm.heightAtLine(curLineObj, "local");
+ return topY + (top ? 0 : curLineObj.height);
+ }
+
+ var lastLine = cm.lastLine()
+ if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
+ var ann = anns[i];
+ if (ann.to.line > lastLine) continue;
+ var top = nextTop || getY(ann.from, true) * hScale;
+ var bottom = getY(ann.to, false) * hScale;
+ while (i < anns.length - 1) {
+ if (anns[i + 1].to.line > lastLine) break;
+ nextTop = getY(anns[i + 1].from, true) * hScale;
+ if (nextTop > bottom + .9) break;
+ ann = anns[++i];
+ bottom = getY(ann.to, false) * hScale;
+ }
+ if (bottom == top) continue;
+ var height = Math.max(bottom - top, 3);
+
+ var elt = frag.appendChild(document.createElement("div"));
+ elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
+ + (top + this.buttonHeight) + "px; height: " + height + "px";
+ elt.className = this.options.className;
+ if (ann.id) {
+ elt.setAttribute("annotation-id", ann.id);
+ }
+ }
+ this.div.textContent = "";
+ this.div.appendChild(frag);
+ };
+
+ Annotation.prototype.clear = function() {
+ this.cm.off("refresh", this.resizeHandler);
+ this.cm.off("markerAdded", this.resizeHandler);
+ this.cm.off("markerCleared", this.resizeHandler);
+ if (this.changeHandler) this.cm.off("changes", this.changeHandler);
+ this.div.parentNode.removeChild(this.div);
+ };
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/search/match-highlighter.js b/devtools/client/shared/sourceeditor/codemirror/addon/search/match-highlighter.js
new file mode 100644
index 0000000000..d829c3808b
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/search/match-highlighter.js
@@ -0,0 +1,165 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Highlighting text that matches the selection
+//
+// Defines an option highlightSelectionMatches, which, when enabled,
+// will style strings that match the selection throughout the
+// document.
+//
+// The option can be set to true to simply enable it, or to a
+// {minChars, style, wordsOnly, showToken, delay} object to explicitly
+// configure it. minChars is the minimum amount of characters that should be
+// selected for the behavior to occur, and style is the token style to
+// apply to the matches. This will be prefixed by "cm-" to create an
+// actual CSS class name. If wordsOnly is enabled, the matches will be
+// highlighted only if the selected text is a word. showToken, when enabled,
+// will cause the current token to be highlighted when nothing is selected.
+// delay is used to specify how much time to wait, in milliseconds, before
+// highlighting the matches. If annotateScrollbar is enabled, the occurences
+// will be highlighted on the scrollbar via the matchesonscrollbar addon.
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/search/matchesonscrollbar.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "./matchesonscrollbar"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var defaults = {
+ style: "matchhighlight",
+ minChars: 2,
+ delay: 100,
+ wordsOnly: false,
+ annotateScrollbar: false,
+ showToken: false,
+ trim: true
+ }
+
+ function State(options) {
+ this.options = {}
+ for (var name in defaults)
+ this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
+ this.overlay = this.timeout = null;
+ this.matchesonscroll = null;
+ this.active = false;
+ }
+
+ CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ removeOverlay(cm);
+ clearTimeout(cm.state.matchHighlighter.timeout);
+ cm.state.matchHighlighter = null;
+ cm.off("cursorActivity", cursorActivity);
+ cm.off("focus", onFocus)
+ }
+ if (val) {
+ var state = cm.state.matchHighlighter = new State(val);
+ if (cm.hasFocus()) {
+ state.active = true
+ highlightMatches(cm)
+ } else {
+ cm.on("focus", onFocus)
+ }
+ cm.on("cursorActivity", cursorActivity);
+ }
+ });
+
+ function cursorActivity(cm) {
+ var state = cm.state.matchHighlighter;
+ if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
+ }
+
+ function onFocus(cm) {
+ var state = cm.state.matchHighlighter
+ if (!state.active) {
+ state.active = true
+ scheduleHighlight(cm, state)
+ }
+ }
+
+ function scheduleHighlight(cm, state) {
+ clearTimeout(state.timeout);
+ state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
+ }
+
+ function addOverlay(cm, query, hasBoundary, style) {
+ var state = cm.state.matchHighlighter;
+ cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
+ if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
+ var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + "\\b") : query;
+ state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
+ {className: "CodeMirror-selection-highlight-scrollbar"});
+ }
+ }
+
+ function removeOverlay(cm) {
+ var state = cm.state.matchHighlighter;
+ if (state.overlay) {
+ cm.removeOverlay(state.overlay);
+ state.overlay = null;
+ if (state.matchesonscroll) {
+ state.matchesonscroll.clear();
+ state.matchesonscroll = null;
+ }
+ }
+ }
+
+ function highlightMatches(cm) {
+ cm.operation(function() {
+ var state = cm.state.matchHighlighter;
+ removeOverlay(cm);
+ if (!cm.somethingSelected() && state.options.showToken) {
+ var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
+ var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
+ while (start && re.test(line.charAt(start - 1))) --start;
+ while (end < line.length && re.test(line.charAt(end))) ++end;
+ if (start < end)
+ addOverlay(cm, line.slice(start, end), re, state.options.style);
+ return;
+ }
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
+ if (from.line != to.line) return;
+ if (state.options.wordsOnly && !isWord(cm, from, to)) return;
+ var selection = cm.getRange(from, to)
+ if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
+ if (selection.length >= state.options.minChars)
+ addOverlay(cm, selection, false, state.options.style);
+ });
+ }
+
+ function isWord(cm, from, to) {
+ var str = cm.getRange(from, to);
+ if (str.match(/^\w+$/) !== null) {
+ if (from.ch > 0) {
+ var pos = {line: from.line, ch: from.ch - 1};
+ var chr = cm.getRange(pos, from);
+ if (chr.match(/\W/) === null) return false;
+ }
+ if (to.ch < cm.getLine(from.line).length) {
+ var pos = {line: to.line, ch: to.ch + 1};
+ var chr = cm.getRange(to, pos);
+ if (chr.match(/\W/) === null) return false;
+ }
+ return true;
+ } else return false;
+ }
+
+ function boundariesAround(stream, re) {
+ return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
+ (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
+ }
+
+ function makeOverlay(query, hasBoundary, style) {
+ return {token: function(stream) {
+ if (stream.match(query) &&
+ (!hasBoundary || boundariesAround(stream, hasBoundary)))
+ return style;
+ stream.next();
+ stream.skipTo(query.charAt(0)) || stream.skipToEnd();
+ }};
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/search/matchesonscrollbar.js b/devtools/client/shared/sourceeditor/codemirror/addon/search/matchesonscrollbar.js
new file mode 100644
index 0000000000..0b505947d9
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/search/matchesonscrollbar.js
@@ -0,0 +1,97 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+ })(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
+ if (typeof options == "string") options = {className: options};
+ if (!options) options = {};
+ return new SearchAnnotation(this, query, caseFold, options);
+ });
+
+ function SearchAnnotation(cm, query, caseFold, options) {
+ this.cm = cm;
+ this.options = options;
+ var annotateOptions = {listenForChanges: false};
+ for (var prop in options) annotateOptions[prop] = options[prop];
+ if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
+ this.annotation = cm.annotateScrollbar(annotateOptions);
+ this.query = query;
+ this.caseFold = caseFold;
+ this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
+ this.matches = [];
+ this.update = null;
+
+ this.findMatches();
+ this.annotation.update(this.matches);
+
+ var self = this;
+ cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
+ }
+
+ var MAX_MATCHES = 1000;
+
+ SearchAnnotation.prototype.findMatches = function() {
+ if (!this.gap) return;
+ for (var i = 0; i < this.matches.length; i++) {
+ var match = this.matches[i];
+ if (match.from.line >= this.gap.to) break;
+ if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
+ }
+ var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});
+ var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
+ while (cursor.findNext()) {
+ var match = {from: cursor.from(), to: cursor.to()};
+ if (match.from.line >= this.gap.to) break;
+ this.matches.splice(i++, 0, match);
+ if (this.matches.length > maxMatches) break;
+ }
+ this.gap = null;
+ };
+
+ function offsetLine(line, changeStart, sizeChange) {
+ if (line <= changeStart) return line;
+ return Math.max(changeStart, line + sizeChange);
+ }
+
+ SearchAnnotation.prototype.onChange = function(change) {
+ var startLine = change.from.line;
+ var endLine = CodeMirror.changeEnd(change).line;
+ var sizeChange = endLine - change.to.line;
+ if (this.gap) {
+ this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
+ this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
+ } else {
+ this.gap = {from: change.from.line, to: endLine + 1};
+ }
+
+ if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
+ var match = this.matches[i];
+ var newFrom = offsetLine(match.from.line, startLine, sizeChange);
+ if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
+ var newTo = offsetLine(match.to.line, startLine, sizeChange);
+ if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
+ }
+ clearTimeout(this.update);
+ var self = this;
+ this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
+ };
+
+ SearchAnnotation.prototype.updateAfterChange = function() {
+ this.findMatches();
+ this.annotation.update(this.matches);
+ };
+
+ SearchAnnotation.prototype.clear = function() {
+ this.cm.off("change", this.changeHandler);
+ this.annotation.clear();
+ };
+ }); \ No newline at end of file
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js b/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js
new file mode 100644
index 0000000000..c7fcb75a38
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/search/search.js
@@ -0,0 +1,323 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Define search commands. Depends on dialog.js or another
+// implementation of the openDialog method.
+
+// Replace works a little oddly -- it will do the replace on the next
+// Ctrl-G (or whatever is bound to findNext) press. You prevent a
+// replace by making sure the match is no longer selected when hitting
+// Ctrl-G.
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/search/searchcursor.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ function searchOverlay(query, caseInsensitive) {
+ if (typeof query == "string")
+ query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
+ else if (!query.global)
+ query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
+
+ return {token: function(stream) {
+ query.lastIndex = stream.pos;
+ var match = query.exec(stream.string);
+ if (match && match.index == stream.pos) {
+ stream.pos += match[0].length || 1;
+ return "searching";
+ } else if (match) {
+ stream.pos = match.index;
+ } else {
+ stream.skipToEnd();
+ }
+ }};
+ }
+
+ function SearchState() {
+ this.posFrom = this.posTo = this.lastQuery = this.query = null;
+ this.overlay = null;
+ }
+
+ function getSearchState(cm) {
+ return cm.state.search || (cm.state.search = new SearchState());
+ }
+
+ function queryCaseInsensitive(query) {
+ return typeof query == "string" && query == query.toLowerCase();
+ }
+
+ function getSearchCursor(cm, query, pos) {
+ // Heuristic: if the query string is all lowercase, do a case insensitive search.
+ return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
+ }
+
+ function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
+ cm.openDialog(text, onEnter, {
+ value: deflt,
+ selectValueOnOpen: true,
+ closeOnEnter: false,
+ onClose: function() { clearSearch(cm); },
+ onKeyDown: onKeyDown
+ });
+ }
+
+ function dialog(cm, text, shortText, deflt, f) {
+ if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
+ else f(prompt(shortText, deflt));
+ }
+
+ function confirmDialog(cm, text, shortText, fs) {
+ if (cm.openConfirm) cm.openConfirm(text, fs);
+ else if (confirm(shortText)) fs[0]();
+ }
+
+ function parseString(string) {
+ return string.replace(/\\([nrt\\])/g, function(match, ch) {
+ if (ch == "n") return "\n"
+ if (ch == "r") return "\r"
+ if (ch == "t") return "\t"
+ if (ch == "\\") return "\\"
+ return match
+ })
+ }
+
+ function parseQuery(query) {
+ var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
+ if (isRE) {
+ try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
+ catch(e) {} // Not a regular expression after all, do a string search
+ } else {
+ query = parseString(query)
+ }
+ if (typeof query == "string" ? query == "" : query.test(""))
+ query = /x^/;
+ return query;
+ }
+
+ var queryDialog;
+
+ function startSearch(cm, state, query) {
+ state.queryText = query;
+ state.query = parseQuery(query);
+ cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
+ state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
+ cm.addOverlay(state.overlay);
+ if (cm.showMatchesOnScrollbar) {
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+ }
+ }
+
+ function doSearch(cm, rev, persistent, immediate) {
+ if (!queryDialog) {
+ let doc = cm.getWrapperElement().ownerDocument;
+ let inp = doc.createElement("input");
+
+ inp.type = "search";
+ inp.placeholder = cm.l10n("findCmd.promptMessage");
+ inp.style.marginInlineStart = "1em";
+ inp.style.marginInlineEnd = "1em";
+ inp.style.flexGrow = "1";
+ inp.addEventListener("focus", () => inp.select());
+
+ queryDialog = doc.createElement("div");
+ queryDialog.appendChild(inp);
+ queryDialog.style.display = "flex";
+ }
+
+ var state = getSearchState(cm);
+ if (state.query) return findNext(cm, rev);
+ var q = cm.getSelection() || state.lastQuery;
+ if (q instanceof RegExp && q.source == "x^") q = null
+ if (persistent && cm.openDialog) {
+ var hiding = null
+ var searchNext = function(query, event) {
+ CodeMirror.e_stop(event);
+ if (!query) return;
+ if (query != state.queryText) {
+ startSearch(cm, state, query);
+ state.posFrom = state.posTo = cm.getCursor();
+ }
+ if (hiding) hiding.style.opacity = 1
+ findNext(cm, event.shiftKey, function(_, to) {
+ var dialog
+ if (to.line < 3 && document.querySelector &&
+ (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
+ dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
+ (hiding = dialog).style.opacity = .4
+ })
+ };
+ persistentDialog(cm, queryDialog, q, searchNext, function(event, query) {
+ var keyName = CodeMirror.keyName(event)
+ var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
+ if (cmd == "findNext" || cmd == "findPrev" ||
+ cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
+ CodeMirror.e_stop(event);
+ startSearch(cm, getSearchState(cm), query);
+ cm.execCommand(cmd);
+ } else if (cmd == "find" || cmd == "findPersistent") {
+ CodeMirror.e_stop(event);
+ searchNext(query, event);
+ }
+ });
+ if (immediate && q) {
+ startSearch(cm, state, q);
+ findNext(cm, rev);
+ }
+ } else {
+ dialog(cm, queryDialog, "Search for:", q, function(query) {
+ if (query && !state.query) cm.operation(function() {
+ startSearch(cm, state, query);
+ state.posFrom = state.posTo = cm.getCursor();
+ findNext(cm, rev);
+ });
+ });
+ }
+ }
+
+ function findNext(cm, rev, callback) {cm.operation(function() {
+ var state = getSearchState(cm);
+ var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
+ if (!cursor.find(rev)) {
+ cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
+ if (!cursor.find(rev)) return;
+ }
+ cm.setSelection(cursor.from(), cursor.to());
+ cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
+ state.posFrom = cursor.from(); state.posTo = cursor.to();
+ if (callback) callback(cursor.from(), cursor.to())
+ });}
+
+ function clearSearch(cm) {cm.operation(function() {
+ var state = getSearchState(cm);
+ state.lastQuery = state.query;
+ if (!state.query) return;
+ state.query = state.queryText = null;
+ cm.removeOverlay(state.overlay);
+ if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+ });}
+
+ function replaceAll(cm, query, text) {
+ cm.operation(function() {
+ for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+ if (typeof query != "string") {
+ var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+ cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+ } else cursor.replace(text);
+ }
+ });
+ }
+
+ function replace(cm, all) {
+ if (cm.getOption("readOnly")) return;
+ var query = cm.getSelection() || getSearchState(cm).lastQuery;
+
+ let doc = cm.getWrapperElement().ownerDocument;
+
+ // `searchLabel` is used as part of `replaceQueryFragment` and as a separate
+ // argument by itself, so it should be cloned.
+ let searchLabel = doc.createElement("span");
+ searchLabel.classList.add("CodeMirror-search-label");
+ searchLabel.textContent = all ? "Replace all:" : "Replace:";
+
+ let replaceQueryFragment = doc.createDocumentFragment();
+ replaceQueryFragment.appendChild(searchLabel.cloneNode(true));
+
+ let searchField = doc.createElement("input");
+ searchField.setAttribute("type", "text");
+ searchField.setAttribute("style", "width: 10em");
+ searchField.classList.add("CodeMirror-search-field");
+ replaceQueryFragment.appendChild(searchField);
+
+ let searchHint = doc.createElement("span");
+ searchHint.setAttribute("style", "color: #888");
+ searchHint.classList.add("CodeMirror-search-hint");
+ searchHint.textContent = "(Use /re/ syntax for regexp search)";
+ replaceQueryFragment.appendChild(searchHint);
+
+ dialog(cm, replaceQueryFragment, searchLabel, query, function(query) {
+ if (!query) return;
+ query = parseQuery(query);
+
+ let replacementQueryFragment = doc.createDocumentFragment();
+
+ let replaceWithLabel = searchLabel.cloneNode(false);
+ replaceWithLabel.textContent = "With:";
+ replacementQueryFragment.appendChild(replaceWithLabel);
+
+ let replaceField = doc.createElement("input");
+ replaceField.setAttribute("type", "text");
+ replaceField.setAttribute("style", "width: 10em");
+ replaceField.classList.add("CodeMirror-search-field");
+ replacementQueryFragment.appendChild(replaceField);
+
+ dialog(cm, replacementQueryFragment, "Replace with:", "", function(text) {
+ text = parseString(text)
+ if (all) {
+ replaceAll(cm, query, text)
+ } else {
+ clearSearch(cm);
+ var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
+ var advance = function() {
+ var start = cursor.from(), match;
+ if (!(match = cursor.findNext())) {
+ cursor = getSearchCursor(cm, query);
+ if (!(match = cursor.findNext()) ||
+ (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+ }
+ cm.setSelection(cursor.from(), cursor.to());
+ cm.scrollIntoView({ from: cursor.from(), to: cursor.to() });
+
+ let replaceConfirmFragment = doc.createDocumentFragment();
+
+ let replaceConfirmLabel = searchLabel.cloneNode(false);
+ replaceConfirmLabel.textContent = "Replace?";
+ replaceConfirmFragment.appendChild(replaceConfirmLabel);
+
+ let yesButton = doc.createElement("button");
+ yesButton.textContent = "Yes";
+ replaceConfirmFragment.appendChild(yesButton);
+
+ let noButton = doc.createElement("button");
+ noButton.textContent = "No";
+ replaceConfirmFragment.appendChild(noButton);
+
+ let allButton = doc.createElement("button");
+ allButton.textContent = "All";
+ replaceConfirmFragment.appendChild(allButton);
+
+ let stopButton = doc.createElement("button");
+ stopButton.textContent = "Stop";
+ replaceConfirmFragment.appendChild(stopButton);
+
+ confirmDialog(cm, replaceConfirmFragment, "Replace?",
+ [function() {doReplace(match);}, advance,
+ function() {replaceAll(cm, query, text)}]);
+ };
+ var doReplace = function(match) {
+ cursor.replace(typeof query == "string" ? text :
+ text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+ advance();
+ };
+ advance();
+ }
+ });
+ });
+ }
+
+ CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
+ CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
+ CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
+ CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
+ CodeMirror.commands.findNext = doSearch;
+ CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
+ CodeMirror.commands.clearSearch = clearSearch;
+ CodeMirror.commands.replace = replace;
+ CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/search/searchcursor.js b/devtools/client/shared/sourceeditor/codemirror/addon/search/searchcursor.js
new file mode 100644
index 0000000000..50dea70c83
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/search/searchcursor.js
@@ -0,0 +1,293 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"))
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod)
+ else // Plain browser env
+ mod(CodeMirror)
+})(function(CodeMirror) {
+ "use strict"
+ var Pos = CodeMirror.Pos
+
+ function regexpFlags(regexp) {
+ var flags = regexp.flags
+ return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
+ + (regexp.global ? "g" : "")
+ + (regexp.multiline ? "m" : "")
+ }
+
+ function ensureFlags(regexp, flags) {
+ var current = regexpFlags(regexp), target = current
+ for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
+ target += flags.charAt(i)
+ return current == target ? regexp : new RegExp(regexp.source, target)
+ }
+
+ function maybeMultiline(regexp) {
+ return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
+ }
+
+ function searchRegexpForward(doc, regexp, start) {
+ regexp = ensureFlags(regexp, "g")
+ for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
+ regexp.lastIndex = ch
+ var string = doc.getLine(line), match = regexp.exec(string)
+ if (match)
+ return {from: Pos(line, match.index),
+ to: Pos(line, match.index + match[0].length),
+ match: match}
+ }
+ }
+
+ function searchRegexpForwardMultiline(doc, regexp, start) {
+ if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
+
+ regexp = ensureFlags(regexp, "gm")
+ var string, chunk = 1
+ for (var line = start.line, last = doc.lastLine(); line <= last;) {
+ // This grows the search buffer in exponentially-sized chunks
+ // between matches, so that nearby matches are fast and don't
+ // require concatenating the whole document (in case we're
+ // searching for something that has tons of matches), but at the
+ // same time, the amount of retries is limited.
+ for (var i = 0; i < chunk; i++) {
+ if (line > last) break
+ var curLine = doc.getLine(line++)
+ string = string == null ? curLine : string + "\n" + curLine
+ }
+ chunk = chunk * 2
+ regexp.lastIndex = start.ch
+ var match = regexp.exec(string)
+ if (match) {
+ var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
+ var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
+ return {from: Pos(startLine, startCh),
+ to: Pos(startLine + inside.length - 1,
+ inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
+ match: match}
+ }
+ }
+ }
+
+ function lastMatchIn(string, regexp) {
+ var cutOff = 0, match
+ for (;;) {
+ regexp.lastIndex = cutOff
+ var newMatch = regexp.exec(string)
+ if (!newMatch) return match
+ match = newMatch
+ cutOff = match.index + (match[0].length || 1)
+ if (cutOff == string.length) return match
+ }
+ }
+
+ function searchRegexpBackward(doc, regexp, start) {
+ regexp = ensureFlags(regexp, "g")
+ for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
+ var string = doc.getLine(line)
+ if (ch > -1) string = string.slice(0, ch)
+ var match = lastMatchIn(string, regexp)
+ if (match)
+ return {from: Pos(line, match.index),
+ to: Pos(line, match.index + match[0].length),
+ match: match}
+ }
+ }
+
+ function searchRegexpBackwardMultiline(doc, regexp, start) {
+ regexp = ensureFlags(regexp, "gm")
+ var string, chunk = 1
+ for (var line = start.line, first = doc.firstLine(); line >= first;) {
+ for (var i = 0; i < chunk; i++) {
+ var curLine = doc.getLine(line--)
+ string = string == null ? curLine.slice(0, start.ch) : curLine + "\n" + string
+ }
+ chunk *= 2
+
+ var match = lastMatchIn(string, regexp)
+ if (match) {
+ var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
+ var startLine = line + before.length, startCh = before[before.length - 1].length
+ return {from: Pos(startLine, startCh),
+ to: Pos(startLine + inside.length - 1,
+ inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
+ match: match}
+ }
+ }
+ }
+
+ var doFold, noFold
+ if (String.prototype.normalize) {
+ doFold = function(str) { return str.normalize("NFD").toLowerCase() }
+ noFold = function(str) { return str.normalize("NFD") }
+ } else {
+ doFold = function(str) { return str.toLowerCase() }
+ noFold = function(str) { return str }
+ }
+
+ // Maps a position in a case-folded line back to a position in the original line
+ // (compensating for codepoints increasing in number during folding)
+ function adjustPos(orig, folded, pos, foldFunc) {
+ if (orig.length == folded.length) return pos
+ for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
+ if (min == max) return min
+ var mid = (min + max) >> 1
+ var len = foldFunc(orig.slice(0, mid)).length
+ if (len == pos) return mid
+ else if (len > pos) max = mid
+ else min = mid + 1
+ }
+ }
+
+ function searchStringForward(doc, query, start, caseFold) {
+ // Empty string would match anything and never progress, so we
+ // define it to match nothing instead.
+ if (!query.length) return null
+ var fold = caseFold ? doFold : noFold
+ var lines = fold(query).split(/\r|\n\r?/)
+
+ search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
+ var orig = doc.getLine(line).slice(ch), string = fold(orig)
+ if (lines.length == 1) {
+ var found = string.indexOf(lines[0])
+ if (found == -1) continue search
+ var start = adjustPos(orig, string, found, fold) + ch
+ return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
+ to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
+ } else {
+ var cutFrom = string.length - lines[0].length
+ if (string.slice(cutFrom) != lines[0]) continue search
+ for (var i = 1; i < lines.length - 1; i++)
+ if (fold(doc.getLine(line + i)) != lines[i]) continue search
+ var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
+ if (endString.slice(0, lastLine.length) != lastLine) continue search
+ return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
+ to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
+ }
+ }
+ }
+
+ function searchStringBackward(doc, query, start, caseFold) {
+ if (!query.length) return null
+ var fold = caseFold ? doFold : noFold
+ var lines = fold(query).split(/\r|\n\r?/)
+
+ search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
+ var orig = doc.getLine(line)
+ if (ch > -1) orig = orig.slice(0, ch)
+ var string = fold(orig)
+ if (lines.length == 1) {
+ var found = string.lastIndexOf(lines[0])
+ if (found == -1) continue search
+ return {from: Pos(line, adjustPos(orig, string, found, fold)),
+ to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
+ } else {
+ var lastLine = lines[lines.length - 1]
+ if (string.slice(0, lastLine.length) != lastLine) continue search
+ for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
+ if (fold(doc.getLine(start + i)) != lines[i]) continue search
+ var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
+ if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
+ return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
+ to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
+ }
+ }
+ }
+
+ function SearchCursor(doc, query, pos, options) {
+ this.atOccurrence = false
+ this.doc = doc
+ pos = pos ? doc.clipPos(pos) : Pos(0, 0)
+ this.pos = {from: pos, to: pos}
+
+ var caseFold
+ if (typeof options == "object") {
+ caseFold = options.caseFold
+ } else { // Backwards compat for when caseFold was the 4th argument
+ caseFold = options
+ options = null
+ }
+
+ if (typeof query == "string") {
+ if (caseFold == null) caseFold = false
+ this.matches = function(reverse, pos) {
+ return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
+ }
+ } else {
+ query = ensureFlags(query, "gm")
+ if (!options || options.multiline !== false)
+ this.matches = function(reverse, pos) {
+ return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
+ }
+ else
+ this.matches = function(reverse, pos) {
+ return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
+ }
+ }
+ }
+
+ SearchCursor.prototype = {
+ findNext: function() {return this.find(false)},
+ findPrevious: function() {return this.find(true)},
+
+ find: function(reverse) {
+ var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
+
+ // Implements weird auto-growing behavior on null-matches for
+ // backwards-compatiblity with the vim code (unfortunately)
+ while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {
+ if (reverse) {
+ if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)
+ else if (result.from.line == this.doc.firstLine()) result = null
+ else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1)))
+ } else {
+ if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1)
+ else if (result.to.line == this.doc.lastLine()) result = null
+ else result = this.matches(reverse, Pos(result.to.line + 1, 0))
+ }
+ }
+
+ if (result) {
+ this.pos = result
+ this.atOccurrence = true
+ return this.pos.match || true
+ } else {
+ var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
+ this.pos = {from: end, to: end}
+ return this.atOccurrence = false
+ }
+ },
+
+ from: function() {if (this.atOccurrence) return this.pos.from},
+ to: function() {if (this.atOccurrence) return this.pos.to},
+
+ replace: function(newText, origin) {
+ if (!this.atOccurrence) return
+ var lines = CodeMirror.splitLines(newText)
+ this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
+ this.pos.to = Pos(this.pos.from.line + lines.length - 1,
+ lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
+ }
+ }
+
+ CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
+ return new SearchCursor(this.doc, query, pos, caseFold)
+ })
+ CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
+ return new SearchCursor(this, query, pos, caseFold)
+ })
+
+ CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
+ var ranges = []
+ var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
+ while (cur.findNext()) {
+ if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
+ ranges.push({anchor: cur.from(), head: cur.to()})
+ }
+ if (ranges.length)
+ this.setSelections(ranges, 0)
+ })
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/selection/active-line.js b/devtools/client/shared/sourceeditor/codemirror/addon/selection/active-line.js
new file mode 100644
index 0000000000..1bf6f97559
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/selection/active-line.js
@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+ var WRAP_CLASS = "CodeMirror-activeline";
+ var BACK_CLASS = "CodeMirror-activeline-background";
+ var GUTT_CLASS = "CodeMirror-activeline-gutter";
+
+ CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
+ var prev = old == CodeMirror.Init ? false : old;
+ if (val == prev) return
+ if (prev) {
+ cm.off("beforeSelectionChange", selectionChange);
+ clearActiveLines(cm);
+ delete cm.state.activeLines;
+ }
+ if (val) {
+ cm.state.activeLines = [];
+ updateActiveLines(cm, cm.listSelections());
+ cm.on("beforeSelectionChange", selectionChange);
+ }
+ });
+
+ function clearActiveLines(cm) {
+ for (var i = 0; i < cm.state.activeLines.length; i++) {
+ cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
+ cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
+ cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
+ }
+ }
+
+ function sameArray(a, b) {
+ if (a.length != b.length) return false;
+ for (var i = 0; i < a.length; i++)
+ if (a[i] != b[i]) return false;
+ return true;
+ }
+
+ function updateActiveLines(cm, ranges) {
+ var active = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ var option = cm.getOption("styleActiveLine");
+ if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
+ continue
+ var line = cm.getLineHandleVisualStart(range.head.line);
+ if (active[active.length - 1] != line) active.push(line);
+ }
+ if (sameArray(cm.state.activeLines, active)) return;
+ cm.operation(function() {
+ clearActiveLines(cm);
+ for (var i = 0; i < active.length; i++) {
+ cm.addLineClass(active[i], "wrap", WRAP_CLASS);
+ cm.addLineClass(active[i], "background", BACK_CLASS);
+ cm.addLineClass(active[i], "gutter", GUTT_CLASS);
+ }
+ cm.state.activeLines = active;
+ });
+ }
+
+ function selectionChange(cm, sel) {
+ updateActiveLines(cm, sel.ranges);
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/addon/selection/mark-selection.js b/devtools/client/shared/sourceeditor/codemirror/addon/selection/mark-selection.js
new file mode 100644
index 0000000000..d838723b48
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/addon/selection/mark-selection.js
@@ -0,0 +1,119 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Because sometimes you need to mark the selected *text*.
+//
+// Adds an option 'styleSelectedText' which, when enabled, gives
+// selected text the CSS class given as option value, or
+// "CodeMirror-selectedtext" when the value is not a string.
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
+ var prev = old && old != CodeMirror.Init;
+ if (val && !prev) {
+ cm.state.markedSelection = [];
+ cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
+ reset(cm);
+ cm.on("cursorActivity", onCursorActivity);
+ cm.on("change", onChange);
+ } else if (!val && prev) {
+ cm.off("cursorActivity", onCursorActivity);
+ cm.off("change", onChange);
+ clear(cm);
+ cm.state.markedSelection = cm.state.markedSelectionStyle = null;
+ }
+ });
+
+ function onCursorActivity(cm) {
+ if (cm.state.markedSelection)
+ cm.operation(function() { update(cm); });
+ }
+
+ function onChange(cm) {
+ if (cm.state.markedSelection && cm.state.markedSelection.length)
+ cm.operation(function() { clear(cm); });
+ }
+
+ var CHUNK_SIZE = 8;
+ var Pos = CodeMirror.Pos;
+ var cmp = CodeMirror.cmpPos;
+
+ function coverRange(cm, from, to, addAt) {
+ if (cmp(from, to) == 0) return;
+ var array = cm.state.markedSelection;
+ var cls = cm.state.markedSelectionStyle;
+ for (var line = from.line;;) {
+ var start = line == from.line ? from : Pos(line, 0);
+ var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
+ var end = atEnd ? to : Pos(endLine, 0);
+ var mark = cm.markText(start, end, {className: cls});
+ if (addAt == null) array.push(mark);
+ else array.splice(addAt++, 0, mark);
+ if (atEnd) break;
+ line = endLine;
+ }
+ }
+
+ function clear(cm) {
+ var array = cm.state.markedSelection;
+ for (var i = 0; i < array.length; ++i) array[i].clear();
+ array.length = 0;
+ }
+
+ function reset(cm) {
+ clear(cm);
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++)
+ coverRange(cm, ranges[i].from(), ranges[i].to());
+ }
+
+ function update(cm) {
+ if (!cm.somethingSelected()) return clear(cm);
+ if (cm.listSelections().length > 1) return reset(cm);
+
+ var from = cm.getCursor("start"), to = cm.getCursor("end");
+
+ var array = cm.state.markedSelection;
+ if (!array.length) return coverRange(cm, from, to);
+
+ var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
+ if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||
+ cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
+ return reset(cm);
+
+ while (cmp(from, coverStart.from) > 0) {
+ array.shift().clear();
+ coverStart = array[0].find();
+ }
+ if (cmp(from, coverStart.from) < 0) {
+ if (coverStart.to.line - from.line < CHUNK_SIZE) {
+ array.shift().clear();
+ coverRange(cm, from, coverStart.to, 0);
+ } else {
+ coverRange(cm, from, coverStart.from, 0);
+ }
+ }
+
+ while (cmp(to, coverEnd.to) < 0) {
+ array.pop().clear();
+ coverEnd = array[array.length - 1].find();
+ }
+ if (cmp(to, coverEnd.to) > 0) {
+ if (to.line - coverEnd.from.line < CHUNK_SIZE) {
+ array.pop().clear();
+ coverRange(cm, coverEnd.from, to);
+ } else {
+ coverRange(cm, coverEnd.to, to);
+ }
+ }
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/cmiframe.html b/devtools/client/shared/sourceeditor/codemirror/cmiframe.html
new file mode 100644
index 0000000000..85bcfe5a94
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/cmiframe.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html dir='ltr'>
+<head>
+ <meta charset="utf-8">
+ <meta name="color-scheme" content="light dark">
+ <style id="cmBaseStyle">
+ html, body { height: 100%; }
+ body { margin: 0; overflow: hidden; }
+ .CodeMirror {
+ width: 100% !important;
+ /*
+ 1. Use fallback values for CSS variables, so that the text is sized
+ correctly while all the stylesheets load. Fallback values should
+ match the values in devtools/client/themes/variables.css
+ 2. Avoid !important for the font-size, to let CodeMirror set its own
+ font-size based on prefs.
+ */
+ font-size: var(--theme-code-font-size, 11px);
+ line-height: var(--theme-code-line-height, 1.3636) !important;
+ }
+ </style>
+ <link rel='stylesheet' href="chrome://devtools/content/shared/sourceeditor/codemirror/lib/codemirror.css">
+ <link rel='stylesheet' href="chrome://devtools/content/shared/sourceeditor/codemirror/addon/dialog/dialog.css">
+ <link rel='stylesheet' href="chrome://devtools/content/shared/sourceeditor/codemirror/mozilla.css">
+ <script src="chrome://devtools/content/shared/theme-switching.js"></script>
+</head>
+<body class='theme-body devtools-monospace'></body>
+</html>
diff --git a/devtools/client/shared/sourceeditor/codemirror/codemirror.bundle.js b/devtools/client/shared/sourceeditor/codemirror/codemirror.bundle.js
new file mode 100644
index 0000000000..28a7c5fad4
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/codemirror.bundle.js
@@ -0,0 +1 @@
+var CodeMirror=function(e){var t={};function n(r){if(t[r])return t[r].exports;var i=t[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,n),i.l=!0,i.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var i in e)n.d(r,i,function(t){return e[t]}.bind(null,i));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=7)}([function(e,t,n){e.exports=function(){"use strict";var e=navigator.userAgent,t=navigator.platform,n=/gecko\/\d/i.test(e),r=/MSIE \d/.test(e),i=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(e),o=/Edge\/(\d+)/.exec(e),a=r||i||o,l=a&&(r?document.documentMode||6:+(o||i)[1]),s=!o&&/WebKit\//.test(e),c=s&&/Qt\/\d+\.\d+/.test(e),u=!o&&/Chrome\//.test(e),f=/Opera\//.test(e),d=/Apple Computer/.test(navigator.vendor),h=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(e),p=/PhantomJS/.test(e),m=!o&&/AppleWebKit/.test(e)&&/Mobile\/\w+/.test(e),g=/Android/.test(e),v=m||g||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(e),y=m||/Mac/.test(t),b=/\bCrOS\b/.test(e),x=/win/i.test(t),w=f&&e.match(/Version\/(\d*\.\d*)/);w&&(w=Number(w[1])),w&&w>=15&&(f=!1,s=!0);var k=y&&(c||f&&(null==w||w<12.11)),C=n||a&&l>=9;function S(e){return new RegExp("(^|\\s)"+e+"(?:$|\\s)\\s*")}var T,L=function(e,t){var n=e.className,r=S(t).exec(n);if(r){var i=n.slice(r.index+r[0].length);e.className=n.slice(0,r.index)+(i?r[1]+i:"")}};function M(e){for(var t=e.childNodes.length;t>0;--t)e.removeChild(e.firstChild);return e}function O(e,t){return M(e).appendChild(t)}function N(e,t,n,r){var i=document.createElement(e);if(n&&(i.className=n),r&&(i.style.cssText=r),"string"==typeof t)i.appendChild(document.createTextNode(t));else if(t)for(var o=0;o<t.length;++o)i.appendChild(t[o]);return i}function A(e,t,n,r){var i=N(e,t,n,r);return i.setAttribute("role","presentation"),i}function E(e,t){if(3==t.nodeType&&(t=t.parentNode),e.contains)return e.contains(t);do{if(11==t.nodeType&&(t=t.host),t==e)return!0}while(t=t.parentNode)}function z(){var e;try{e=document.activeElement}catch(t){e=document.body||null}for(;e&&e.shadowRoot&&e.shadowRoot.activeElement;)e=e.shadowRoot.activeElement;return e}function P(e,t){var n=e.className;S(t).test(n)||(e.className+=(n?" ":"")+t)}function D(e,t){for(var n=e.split(" "),r=0;r<n.length;r++)n[r]&&!S(n[r]).test(t)&&(t+=" "+n[r]);return t}T=document.createRange?function(e,t,n,r){var i=document.createRange();return i.setEnd(r||e,n),i.setStart(e,t),i}:function(e,t,n){var r=document.body.createTextRange();try{r.moveToElementText(e.parentNode)}catch(e){return r}return r.collapse(!0),r.moveEnd("character",n),r.moveStart("character",t),r};var I=function(e){e.select()};function F(e){var t=Array.prototype.slice.call(arguments,1);return function(){return e.apply(null,t)}}function _(e,t,n){for(var r in t||(t={}),e)!e.hasOwnProperty(r)||!1===n&&t.hasOwnProperty(r)||(t[r]=e[r]);return t}function W(e,t,n,r,i){null==t&&-1==(t=e.search(/[^\s\u00a0]/))&&(t=e.length);for(var o=r||0,a=i||0;;){var l=e.indexOf("\t",o);if(l<0||l>=t)return a+(t-o);a+=l-o,a+=n-a%n,o=l+1}}m?I=function(e){e.selectionStart=0,e.selectionEnd=e.value.length}:a&&(I=function(e){try{e.select()}catch(e){}});var H=function(){this.id=null,this.f=null,this.time=0,this.handler=F(this.onTimeout,this)};function R(e,t){for(var n=0;n<e.length;++n)if(e[n]==t)return n;return-1}H.prototype.onTimeout=function(e){e.id=0,e.time<=+new Date?e.f():setTimeout(e.handler,e.time-+new Date)},H.prototype.set=function(e,t){this.f=t;var n=+new Date+e;(!this.id||n<this.time)&&(clearTimeout(this.id),this.id=setTimeout(this.handler,e),this.time=n)};var B={toString:function(){return"CodeMirror.Pass"}},j={scroll:!1},q={origin:"*mouse"},U={origin:"+move"};function V(e,t,n){for(var r=0,i=0;;){var o=e.indexOf("\t",r);-1==o&&(o=e.length);var a=o-r;if(o==e.length||i+a>=t)return r+Math.min(a,t-i);if(i+=o-r,r=o+1,(i+=n-i%n)>=t)return r}}var K=[""];function $(e){for(;K.length<=e;)K.push(G(K)+" ");return K[e]}function G(e){return e[e.length-1]}function X(e,t){for(var n=[],r=0;r<e.length;r++)n[r]=t(e[r],r);return n}function Z(){}function Y(e,t){var n;return Object.create?n=Object.create(e):(Z.prototype=e,n=new Z),t&&_(t,n),n}var Q=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;function J(e){return/\w/.test(e)||e>"€"&&(e.toUpperCase()!=e.toLowerCase()||Q.test(e))}function ee(e,t){return t?!!(t.source.indexOf("\\w")>-1&&J(e))||t.test(e):J(e)}function te(e){for(var t in e)if(e.hasOwnProperty(t)&&e[t])return!1;return!0}var ne=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;function re(e){return e.charCodeAt(0)>=768&&ne.test(e)}function ie(e,t,n){for(;(n<0?t>0:t<e.length)&&re(e.charAt(t));)t+=n;return t}function oe(e,t,n){for(var r=t>n?-1:1;;){if(t==n)return t;var i=(t+n)/2,o=r<0?Math.ceil(i):Math.floor(i);if(o==t)return e(o)?t:n;e(o)?n=o:t=o+r}}var ae=null;function le(e,t,n){var r;ae=null;for(var i=0;i<e.length;++i){var o=e[i];if(o.from<t&&o.to>t)return i;o.to==t&&(o.from!=o.to&&"before"==n?r=i:ae=i),o.from==t&&(o.from!=o.to&&"before"!=n?r=i:ae=i)}return null!=r?r:ae}var se=function(){var e=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,t=/[stwN]/,n=/[LRr]/,r=/[Lb1n]/,i=/[1n]/;function o(e,t,n){this.level=e,this.from=t,this.to=n}return function(a,l){var s="ltr"==l?"L":"R";if(0==a.length||"ltr"==l&&!e.test(a))return!1;for(var c,u=a.length,f=[],d=0;d<u;++d)f.push((c=a.charCodeAt(d))<=247?"bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN".charAt(c):1424<=c&&c<=1524?"R":1536<=c&&c<=1785?"nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111".charAt(c-1536):1774<=c&&c<=2220?"r":8192<=c&&c<=8203?"w":8204==c?"b":"L");for(var h=0,p=s;h<u;++h){var m=f[h];"m"==m?f[h]=p:p=m}for(var g=0,v=s;g<u;++g){var y=f[g];"1"==y&&"r"==v?f[g]="n":n.test(y)&&(v=y,"r"==y&&(f[g]="R"))}for(var b=1,x=f[0];b<u-1;++b){var w=f[b];"+"==w&&"1"==x&&"1"==f[b+1]?f[b]="1":","!=w||x!=f[b+1]||"1"!=x&&"n"!=x||(f[b]=x),x=w}for(var k=0;k<u;++k){var C=f[k];if(","==C)f[k]="N";else if("%"==C){var S=void 0;for(S=k+1;S<u&&"%"==f[S];++S);for(var T=k&&"!"==f[k-1]||S<u&&"1"==f[S]?"1":"N",L=k;L<S;++L)f[L]=T;k=S-1}}for(var M=0,O=s;M<u;++M){var N=f[M];"L"==O&&"1"==N?f[M]="L":n.test(N)&&(O=N)}for(var A=0;A<u;++A)if(t.test(f[A])){var E=void 0;for(E=A+1;E<u&&t.test(f[E]);++E);for(var z="L"==(A?f[A-1]:s),P=z==("L"==(E<u?f[E]:s))?z?"L":"R":s,D=A;D<E;++D)f[D]=P;A=E-1}for(var I,F=[],_=0;_<u;)if(r.test(f[_])){var W=_;for(++_;_<u&&r.test(f[_]);++_);F.push(new o(0,W,_))}else{var H=_,R=F.length,B="rtl"==l?1:0;for(++_;_<u&&"L"!=f[_];++_);for(var j=H;j<_;)if(i.test(f[j])){H<j&&(F.splice(R,0,new o(1,H,j)),R+=B);var q=j;for(++j;j<_&&i.test(f[j]);++j);F.splice(R,0,new o(2,q,j)),R+=B,H=j}else++j;H<_&&F.splice(R,0,new o(1,H,_))}return"ltr"==l&&(1==F[0].level&&(I=a.match(/^\s+/))&&(F[0].from=I[0].length,F.unshift(new o(0,0,I[0].length))),1==G(F).level&&(I=a.match(/\s+$/))&&(G(F).to-=I[0].length,F.push(new o(0,u-I[0].length,u)))),"rtl"==l?F.reverse():F}}();function ce(e,t){var n=e.order;return null==n&&(n=e.order=se(e.text,t)),n}var ue=[],fe=function(e,t,n){if(e.addEventListener)e.addEventListener(t,n,!1);else if(e.attachEvent)e.attachEvent("on"+t,n);else{var r=e._handlers||(e._handlers={});r[t]=(r[t]||ue).concat(n)}};function de(e,t){return e._handlers&&e._handlers[t]||ue}function he(e,t,n){if(e.removeEventListener)e.removeEventListener(t,n,!1);else if(e.detachEvent)e.detachEvent("on"+t,n);else{var r=e._handlers,i=r&&r[t];if(i){var o=R(i,n);o>-1&&(r[t]=i.slice(0,o).concat(i.slice(o+1)))}}}function pe(e,t){var n=de(e,t);if(n.length)for(var r=Array.prototype.slice.call(arguments,2),i=0;i<n.length;++i)n[i].apply(null,r)}function me(e,t,n){return"string"==typeof t&&(t={type:t,preventDefault:function(){this.defaultPrevented=!0}}),pe(e,n||t.type,e,t),we(t)||t.codemirrorIgnore}function ge(e){var t=e._handlers&&e._handlers.cursorActivity;if(t)for(var n=e.curOp.cursorActivityHandlers||(e.curOp.cursorActivityHandlers=[]),r=0;r<t.length;++r)-1==R(n,t[r])&&n.push(t[r])}function ve(e,t){return de(e,t).length>0}function ye(e){e.prototype.on=function(e,t){fe(this,e,t)},e.prototype.off=function(e,t){he(this,e,t)}}function be(e){e.preventDefault?e.preventDefault():e.returnValue=!1}function xe(e){e.stopPropagation?e.stopPropagation():e.cancelBubble=!0}function we(e){return null!=e.defaultPrevented?e.defaultPrevented:0==e.returnValue}function ke(e){be(e),xe(e)}function Ce(e){return e.target||e.srcElement}function Se(e){var t=e.which;return null==t&&(1&e.button?t=1:2&e.button?t=3:4&e.button&&(t=2)),y&&e.ctrlKey&&1==t&&(t=3),t}var Te,Le,Me=function(){if(a&&l<9)return!1;var e=N("div");return"draggable"in e||"dragDrop"in e}();function Oe(e){if(null==Te){var t=N("span","​");O(e,N("span",[t,document.createTextNode("x")])),0!=e.firstChild.offsetHeight&&(Te=t.offsetWidth<=1&&t.offsetHeight>2&&!(a&&l<8))}var n=Te?N("span","​"):N("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");return n.setAttribute("cm-text",""),n}function Ne(e){if(null!=Le)return Le;var t=O(e,document.createTextNode("AخA")),n=T(t,0,1).getBoundingClientRect(),r=T(t,1,2).getBoundingClientRect();return M(e),!(!n||n.left==n.right)&&(Le=r.right-n.right<3)}var Ae,Ee=3!="\n\nb".split(/\n/).length?function(e){for(var t=0,n=[],r=e.length;t<=r;){var i=e.indexOf("\n",t);-1==i&&(i=e.length);var o=e.slice(t,"\r"==e.charAt(i-1)?i-1:i),a=o.indexOf("\r");-1!=a?(n.push(o.slice(0,a)),t+=a+1):(n.push(o),t=i+1)}return n}:function(e){return e.split(/\r\n?|\n/)},ze=window.getSelection?function(e){try{return e.selectionStart!=e.selectionEnd}catch(e){return!1}}:function(e){var t;try{t=e.ownerDocument.selection.createRange()}catch(e){}return!(!t||t.parentElement()!=e)&&0!=t.compareEndPoints("StartToEnd",t)},Pe="oncopy"in(Ae=N("div"))||(Ae.setAttribute("oncopy","return;"),"function"==typeof Ae.oncopy),De=null,Ie={},Fe={};function _e(e,t){arguments.length>2&&(t.dependencies=Array.prototype.slice.call(arguments,2)),Ie[e]=t}function We(e){if("string"==typeof e&&Fe.hasOwnProperty(e))e=Fe[e];else if(e&&"string"==typeof e.name&&Fe.hasOwnProperty(e.name)){var t=Fe[e.name];"string"==typeof t&&(t={name:t}),(e=Y(t,e)).name=t.name}else{if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+xml$/.test(e))return We("application/xml");if("string"==typeof e&&/^[\w\-]+\/[\w\-]+\+json$/.test(e))return We("application/json")}return"string"==typeof e?{name:e}:e||{name:"null"}}function He(e,t){t=We(t);var n=Ie[t.name];if(!n)return He(e,"text/plain");var r=n(e,t);if(Re.hasOwnProperty(t.name)){var i=Re[t.name];for(var o in i)i.hasOwnProperty(o)&&(r.hasOwnProperty(o)&&(r["_"+o]=r[o]),r[o]=i[o])}if(r.name=t.name,t.helperType&&(r.helperType=t.helperType),t.modeProps)for(var a in t.modeProps)r[a]=t.modeProps[a];return r}var Re={};function Be(e,t){_(t,Re.hasOwnProperty(e)?Re[e]:Re[e]={})}function je(e,t){if(!0===t)return t;if(e.copyState)return e.copyState(t);var n={};for(var r in t){var i=t[r];i instanceof Array&&(i=i.concat([])),n[r]=i}return n}function qe(e,t){for(var n;e.innerMode&&(n=e.innerMode(t))&&n.mode!=e;)t=n.state,e=n.mode;return n||{mode:e,state:t}}function Ue(e,t,n){return!e.startState||e.startState(t,n)}var Ve=function(e,t,n){this.pos=this.start=0,this.string=e,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0,this.lineOracle=n};function Ke(e,t){if((t-=e.first)<0||t>=e.size)throw new Error("There is no line "+(t+e.first)+" in the document.");for(var n=e;!n.lines;)for(var r=0;;++r){var i=n.children[r],o=i.chunkSize();if(t<o){n=i;break}t-=o}return n.lines[t]}function $e(e,t,n){var r=[],i=t.line;return e.iter(t.line,n.line+1,(function(e){var o=e.text;i==n.line&&(o=o.slice(0,n.ch)),i==t.line&&(o=o.slice(t.ch)),r.push(o),++i})),r}function Ge(e,t,n){var r=[];return e.iter(t,n,(function(e){r.push(e.text)})),r}function Xe(e,t){var n=t-e.height;if(n)for(var r=e;r;r=r.parent)r.height+=n}function Ze(e){if(null==e.parent)return null;for(var t=e.parent,n=R(t.lines,e),r=t.parent;r;t=r,r=r.parent)for(var i=0;r.children[i]!=t;++i)n+=r.children[i].chunkSize();return n+t.first}function Ye(e,t){var n=e.first;e:do{for(var r=0;r<e.children.length;++r){var i=e.children[r],o=i.height;if(t<o){e=i;continue e}t-=o,n+=i.chunkSize()}return n}while(!e.lines);for(var a=0;a<e.lines.length;++a){var l=e.lines[a].height;if(t<l)break;t-=l}return n+a}function Qe(e,t){return t>=e.first&&t<e.first+e.size}function Je(e,t){return String(e.lineNumberFormatter(t+e.firstLineNumber))}function et(e,t,n){if(void 0===n&&(n=null),!(this instanceof et))return new et(e,t,n);this.line=e,this.ch=t,this.sticky=n}function tt(e,t){return e.line-t.line||e.ch-t.ch}function nt(e,t){return e.sticky==t.sticky&&0==tt(e,t)}function rt(e){return et(e.line,e.ch)}function it(e,t){return tt(e,t)<0?t:e}function ot(e,t){return tt(e,t)<0?e:t}function at(e,t){return Math.max(e.first,Math.min(t,e.first+e.size-1))}function lt(e,t){if(t.line<e.first)return et(e.first,0);var n=e.first+e.size-1;return t.line>n?et(n,Ke(e,n).text.length):function(e,t){var n=e.ch;return null==n||n>t?et(e.line,t):n<0?et(e.line,0):e}(t,Ke(e,t.line).text.length)}function st(e,t){for(var n=[],r=0;r<t.length;r++)n[r]=lt(e,t[r]);return n}Ve.prototype.eol=function(){return this.pos>=this.string.length},Ve.prototype.sol=function(){return this.pos==this.lineStart},Ve.prototype.peek=function(){return this.string.charAt(this.pos)||void 0},Ve.prototype.next=function(){if(this.pos<this.string.length)return this.string.charAt(this.pos++)},Ve.prototype.eat=function(e){var t=this.string.charAt(this.pos);if("string"==typeof e?t==e:t&&(e.test?e.test(t):e(t)))return++this.pos,t},Ve.prototype.eatWhile=function(e){for(var t=this.pos;this.eat(e););return this.pos>t},Ve.prototype.eatSpace=function(){for(var e=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>e},Ve.prototype.skipToEnd=function(){this.pos=this.string.length},Ve.prototype.skipTo=function(e){var t=this.string.indexOf(e,this.pos);if(t>-1)return this.pos=t,!0},Ve.prototype.backUp=function(e){this.pos-=e},Ve.prototype.column=function(){return this.lastColumnPos<this.start&&(this.lastColumnValue=W(this.string,this.start,this.tabSize,this.lastColumnPos,this.lastColumnValue),this.lastColumnPos=this.start),this.lastColumnValue-(this.lineStart?W(this.string,this.lineStart,this.tabSize):0)},Ve.prototype.indentation=function(){return W(this.string,null,this.tabSize)-(this.lineStart?W(this.string,this.lineStart,this.tabSize):0)},Ve.prototype.match=function(e,t,n){if("string"!=typeof e){var r=this.string.slice(this.pos).match(e);return r&&r.index>0?null:(r&&!1!==t&&(this.pos+=r[0].length),r)}var i=function(e){return n?e.toLowerCase():e};if(i(this.string.substr(this.pos,e.length))==i(e))return!1!==t&&(this.pos+=e.length),!0},Ve.prototype.current=function(){return this.string.slice(this.start,this.pos)},Ve.prototype.hideFirstChars=function(e,t){this.lineStart+=e;try{return t()}finally{this.lineStart-=e}},Ve.prototype.lookAhead=function(e){var t=this.lineOracle;return t&&t.lookAhead(e)},Ve.prototype.baseToken=function(){var e=this.lineOracle;return e&&e.baseToken(this.pos)};var ct=function(e,t){this.state=e,this.lookAhead=t},ut=function(e,t,n,r){this.state=t,this.doc=e,this.line=n,this.maxLookAhead=r||0,this.baseTokens=null,this.baseTokenPos=1};function ft(e,t,n,r){var i=[e.state.modeGen],o={};xt(e,t.text,e.doc.mode,n,(function(e,t){return i.push(e,t)}),o,r);for(var a=n.state,l=function(r){n.baseTokens=i;var l=e.state.overlays[r],s=1,c=0;n.state=!0,xt(e,t.text,l.mode,n,(function(e,t){for(var n=s;c<e;){var r=i[s];r>e&&i.splice(s,1,e,i[s+1],r),s+=2,c=Math.min(e,r)}if(t)if(l.opaque)i.splice(n,s-n,e,"overlay "+t),s=n+2;else for(;n<s;n+=2){var o=i[n+1];i[n+1]=(o?o+" ":"")+"overlay "+t}}),o),n.state=a,n.baseTokens=null,n.baseTokenPos=1},s=0;s<e.state.overlays.length;++s)l(s);return{styles:i,classes:o.bgClass||o.textClass?o:null}}function dt(e,t,n){if(!t.styles||t.styles[0]!=e.state.modeGen){var r=ht(e,Ze(t)),i=t.text.length>e.options.maxHighlightLength&&je(e.doc.mode,r.state),o=ft(e,t,r);i&&(r.state=i),t.stateAfter=r.save(!i),t.styles=o.styles,o.classes?t.styleClasses=o.classes:t.styleClasses&&(t.styleClasses=null),n===e.doc.highlightFrontier&&(e.doc.modeFrontier=Math.max(e.doc.modeFrontier,++e.doc.highlightFrontier))}return t.styles}function ht(e,t,n){var r=e.doc,i=e.display;if(!r.mode.startState)return new ut(r,!0,t);var o=function(e,t,n){for(var r,i,o=e.doc,a=n?-1:t-(e.doc.mode.innerMode?1e3:100),l=t;l>a;--l){if(l<=o.first)return o.first;var s=Ke(o,l-1),c=s.stateAfter;if(c&&(!n||l+(c instanceof ct?c.lookAhead:0)<=o.modeFrontier))return l;var u=W(s.text,null,e.options.tabSize);(null==i||r>u)&&(i=l-1,r=u)}return i}(e,t,n),a=o>r.first&&Ke(r,o-1).stateAfter,l=a?ut.fromSaved(r,a,o):new ut(r,Ue(r.mode),o);return r.iter(o,t,(function(n){pt(e,n.text,l);var r=l.line;n.stateAfter=r==t-1||r%5==0||r>=i.viewFrom&&r<i.viewTo?l.save():null,l.nextLine()})),n&&(r.modeFrontier=l.line),l}function pt(e,t,n,r){var i=e.doc.mode,o=new Ve(t,e.options.tabSize,n);for(o.start=o.pos=r||0,""==t&&mt(i,n.state);!o.eol();)gt(i,o,n.state),o.start=o.pos}function mt(e,t){if(e.blankLine)return e.blankLine(t);if(e.innerMode){var n=qe(e,t);return n.mode.blankLine?n.mode.blankLine(n.state):void 0}}function gt(e,t,n,r){for(var i=0;i<10;i++){r&&(r[0]=qe(e,n).mode);var o=e.token(t,n);if(t.pos>t.start)return o}throw new Error("Mode "+e.name+" failed to advance stream.")}ut.prototype.lookAhead=function(e){var t=this.doc.getLine(this.line+e);return null!=t&&e>this.maxLookAhead&&(this.maxLookAhead=e),t},ut.prototype.baseToken=function(e){if(!this.baseTokens)return null;for(;this.baseTokens[this.baseTokenPos]<=e;)this.baseTokenPos+=2;var t=this.baseTokens[this.baseTokenPos+1];return{type:t&&t.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-e}},ut.prototype.nextLine=function(){this.line++,this.maxLookAhead>0&&this.maxLookAhead--},ut.fromSaved=function(e,t,n){return t instanceof ct?new ut(e,je(e.mode,t.state),n,t.lookAhead):new ut(e,je(e.mode,t),n)},ut.prototype.save=function(e){var t=!1!==e?je(this.doc.mode,this.state):this.state;return this.maxLookAhead>0?new ct(t,this.maxLookAhead):t};var vt=function(e,t,n){this.start=e.start,this.end=e.pos,this.string=e.current(),this.type=t||null,this.state=n};function yt(e,t,n,r){var i,o,a=e.doc,l=a.mode,s=Ke(a,(t=lt(a,t)).line),c=ht(e,t.line,n),u=new Ve(s.text,e.options.tabSize,c);for(r&&(o=[]);(r||u.pos<t.ch)&&!u.eol();)u.start=u.pos,i=gt(l,u,c.state),r&&o.push(new vt(u,i,je(a.mode,c.state)));return r?o:new vt(u,i,c.state)}function bt(e,t){if(e)for(;;){var n=e.match(/(?:^|\s+)line-(background-)?(\S+)/);if(!n)break;e=e.slice(0,n.index)+e.slice(n.index+n[0].length);var r=n[1]?"bgClass":"textClass";null==t[r]?t[r]=n[2]:new RegExp("(?:^|\\s)"+n[2]+"(?:$|\\s)").test(t[r])||(t[r]+=" "+n[2])}return e}function xt(e,t,n,r,i,o,a){var l=n.flattenSpans;null==l&&(l=e.options.flattenSpans);var s,c=0,u=null,f=new Ve(t,e.options.tabSize,r),d=e.options.addModeClass&&[null];for(""==t&&bt(mt(n,r.state),o);!f.eol();){if(f.pos>e.options.maxHighlightLength?(l=!1,a&&pt(e,t,r,f.pos),f.pos=t.length,s=null):s=bt(gt(n,f,r.state,d),o),d){var h=d[0].name;h&&(s="m-"+(s?h+" "+s:h))}if(!l||u!=s){for(;c<f.start;)i(c=Math.min(f.start,c+5e3),u);u=s}f.start=f.pos}for(;c<f.pos;){var p=Math.min(f.pos,c+5e3);i(p,u),c=p}}var wt=!1,kt=!1;function Ct(e,t,n){this.marker=e,this.from=t,this.to=n}function St(e,t){if(e)for(var n=0;n<e.length;++n){var r=e[n];if(r.marker==t)return r}}function Tt(e,t){for(var n,r=0;r<e.length;++r)e[r]!=t&&(n||(n=[])).push(e[r]);return n}function Lt(e,t){if(t.full)return null;var n=Qe(e,t.from.line)&&Ke(e,t.from.line).markedSpans,r=Qe(e,t.to.line)&&Ke(e,t.to.line).markedSpans;if(!n&&!r)return null;var i=t.from.ch,o=t.to.ch,a=0==tt(t.from,t.to),l=function(e,t,n){var r;if(e)for(var i=0;i<e.length;++i){var o=e[i],a=o.marker;if(null==o.from||(a.inclusiveLeft?o.from<=t:o.from<t)||o.from==t&&"bookmark"==a.type&&(!n||!o.marker.insertLeft)){var l=null==o.to||(a.inclusiveRight?o.to>=t:o.to>t);(r||(r=[])).push(new Ct(a,o.from,l?null:o.to))}}return r}(n,i,a),s=function(e,t,n){var r;if(e)for(var i=0;i<e.length;++i){var o=e[i],a=o.marker;if(null==o.to||(a.inclusiveRight?o.to>=t:o.to>t)||o.from==t&&"bookmark"==a.type&&(!n||o.marker.insertLeft)){var l=null==o.from||(a.inclusiveLeft?o.from<=t:o.from<t);(r||(r=[])).push(new Ct(a,l?null:o.from-t,null==o.to?null:o.to-t))}}return r}(r,o,a),c=1==t.text.length,u=G(t.text).length+(c?i:0);if(l)for(var f=0;f<l.length;++f){var d=l[f];if(null==d.to){var h=St(s,d.marker);h?c&&(d.to=null==h.to?null:h.to+u):d.to=i}}if(s)for(var p=0;p<s.length;++p){var m=s[p];null!=m.to&&(m.to+=u),null==m.from?St(l,m.marker)||(m.from=u,c&&(l||(l=[])).push(m)):(m.from+=u,c&&(l||(l=[])).push(m))}l&&(l=Mt(l)),s&&s!=l&&(s=Mt(s));var g=[l];if(!c){var v,y=t.text.length-2;if(y>0&&l)for(var b=0;b<l.length;++b)null==l[b].to&&(v||(v=[])).push(new Ct(l[b].marker,null,null));for(var x=0;x<y;++x)g.push(v);g.push(s)}return g}function Mt(e){for(var t=0;t<e.length;++t){var n=e[t];null!=n.from&&n.from==n.to&&!1!==n.marker.clearWhenEmpty&&e.splice(t--,1)}return e.length?e:null}function Ot(e){var t=e.markedSpans;if(t){for(var n=0;n<t.length;++n)t[n].marker.detachLine(e);e.markedSpans=null}}function Nt(e,t){if(t){for(var n=0;n<t.length;++n)t[n].marker.attachLine(e);e.markedSpans=t}}function At(e){return e.inclusiveLeft?-1:0}function Et(e){return e.inclusiveRight?1:0}function zt(e,t){var n=e.lines.length-t.lines.length;if(0!=n)return n;var r=e.find(),i=t.find(),o=tt(r.from,i.from)||At(e)-At(t);if(o)return-o;var a=tt(r.to,i.to)||Et(e)-Et(t);return a||t.id-e.id}function Pt(e,t){var n,r=kt&&e.markedSpans;if(r)for(var i=void 0,o=0;o<r.length;++o)(i=r[o]).marker.collapsed&&null==(t?i.from:i.to)&&(!n||zt(n,i.marker)<0)&&(n=i.marker);return n}function Dt(e){return Pt(e,!0)}function It(e){return Pt(e,!1)}function Ft(e,t){var n,r=kt&&e.markedSpans;if(r)for(var i=0;i<r.length;++i){var o=r[i];o.marker.collapsed&&(null==o.from||o.from<t)&&(null==o.to||o.to>t)&&(!n||zt(n,o.marker)<0)&&(n=o.marker)}return n}function _t(e,t,n,r,i){var o=Ke(e,t),a=kt&&o.markedSpans;if(a)for(var l=0;l<a.length;++l){var s=a[l];if(s.marker.collapsed){var c=s.marker.find(0),u=tt(c.from,n)||At(s.marker)-At(i),f=tt(c.to,r)||Et(s.marker)-Et(i);if(!(u>=0&&f<=0||u<=0&&f>=0)&&(u<=0&&(s.marker.inclusiveRight&&i.inclusiveLeft?tt(c.to,n)>=0:tt(c.to,n)>0)||u>=0&&(s.marker.inclusiveRight&&i.inclusiveLeft?tt(c.from,r)<=0:tt(c.from,r)<0)))return!0}}}function Wt(e){for(var t;t=Dt(e);)e=t.find(-1,!0).line;return e}function Ht(e,t){var n=Ke(e,t),r=Wt(n);return n==r?t:Ze(r)}function Rt(e,t){if(t>e.lastLine())return t;var n,r=Ke(e,t);if(!Bt(e,r))return t;for(;n=It(r);)r=n.find(1,!0).line;return Ze(r)+1}function Bt(e,t){var n=kt&&t.markedSpans;if(n)for(var r=void 0,i=0;i<n.length;++i)if((r=n[i]).marker.collapsed){if(null==r.from)return!0;if(!r.marker.widgetNode&&0==r.from&&r.marker.inclusiveLeft&&jt(e,t,r))return!0}}function jt(e,t,n){if(null==n.to){var r=n.marker.find(1,!0);return jt(e,r.line,St(r.line.markedSpans,n.marker))}if(n.marker.inclusiveRight&&n.to==t.text.length)return!0;for(var i=void 0,o=0;o<t.markedSpans.length;++o)if((i=t.markedSpans[o]).marker.collapsed&&!i.marker.widgetNode&&i.from==n.to&&(null==i.to||i.to!=n.from)&&(i.marker.inclusiveLeft||n.marker.inclusiveRight)&&jt(e,t,i))return!0}function qt(e){for(var t=0,n=(e=Wt(e)).parent,r=0;r<n.lines.length;++r){var i=n.lines[r];if(i==e)break;t+=i.height}for(var o=n.parent;o;o=(n=o).parent)for(var a=0;a<o.children.length;++a){var l=o.children[a];if(l==n)break;t+=l.height}return t}function Ut(e){if(0==e.height)return 0;for(var t,n=e.text.length,r=e;t=Dt(r);){var i=t.find(0,!0);r=i.from.line,n+=i.from.ch-i.to.ch}for(r=e;t=It(r);){var o=t.find(0,!0);n-=r.text.length-o.from.ch,n+=(r=o.to.line).text.length-o.to.ch}return n}function Vt(e){var t=e.display,n=e.doc;t.maxLine=Ke(n,n.first),t.maxLineLength=Ut(t.maxLine),t.maxLineChanged=!0,n.iter((function(e){var n=Ut(e);n>t.maxLineLength&&(t.maxLineLength=n,t.maxLine=e)}))}var Kt=function(e,t,n){this.text=e,Nt(this,t),this.height=n?n(this):1};function $t(e){e.parent=null,Ot(e)}Kt.prototype.lineNo=function(){return Ze(this)},ye(Kt);var Gt={},Xt={};function Zt(e,t){if(!e||/^\s*$/.test(e))return null;var n=t.addModeClass?Xt:Gt;return n[e]||(n[e]=e.replace(/\S+/g,"cm-$&"))}function Yt(e,t){var n=A("span",null,null,s?"padding-right: .1px":null),r={pre:A("pre",[n],"CodeMirror-line"),content:n,col:0,pos:0,cm:e,trailingSpace:!1,splitSpaces:e.getOption("lineWrapping")};t.measure={};for(var i=0;i<=(t.rest?t.rest.length:0);i++){var o=i?t.rest[i-1]:t.line,a=void 0;r.pos=0,r.addToken=Jt,Ne(e.display.measure)&&(a=ce(o,e.doc.direction))&&(r.addToken=en(r.addToken,a)),r.map=[],nn(o,r,dt(e,o,t!=e.display.externalMeasured&&Ze(o))),o.styleClasses&&(o.styleClasses.bgClass&&(r.bgClass=D(o.styleClasses.bgClass,r.bgClass||"")),o.styleClasses.textClass&&(r.textClass=D(o.styleClasses.textClass,r.textClass||""))),0==r.map.length&&r.map.push(0,0,r.content.appendChild(Oe(e.display.measure))),0==i?(t.measure.map=r.map,t.measure.cache={}):((t.measure.maps||(t.measure.maps=[])).push(r.map),(t.measure.caches||(t.measure.caches=[])).push({}))}if(s){var l=r.content.lastChild;(/\bcm-tab\b/.test(l.className)||l.querySelector&&l.querySelector(".cm-tab"))&&(r.content.className="cm-tab-wrap-hack")}return pe(e,"renderLine",e,t.line,r.pre),r.pre.className&&(r.textClass=D(r.pre.className,r.textClass||"")),r}function Qt(e){var t=N("span","•","cm-invalidchar");return t.title="\\u"+e.charCodeAt(0).toString(16),t.setAttribute("aria-label",t.title),t}function Jt(e,t,n,r,i,o,s){if(t){var c,u=e.splitSpaces?function(e,t){if(e.length>1&&!/ /.test(e))return e;for(var n=t,r="",i=0;i<e.length;i++){var o=e.charAt(i);" "!=o||!n||i!=e.length-1&&32!=e.charCodeAt(i+1)||(o=" "),r+=o,n=" "==o}return r}(t,e.trailingSpace):t,f=e.cm.state.specialChars,d=!1;if(f.test(t)){c=document.createDocumentFragment();for(var h=0;;){f.lastIndex=h;var p=f.exec(t),m=p?p.index-h:t.length-h;if(m){var g=document.createTextNode(u.slice(h,h+m));a&&l<9?c.appendChild(N("span",[g])):c.appendChild(g),e.map.push(e.pos,e.pos+m,g),e.col+=m,e.pos+=m}if(!p)break;h+=m+1;var v=void 0;if("\t"==p[0]){var y=e.cm.options.tabSize,b=y-e.col%y;(v=c.appendChild(N("span",$(b),"cm-tab"))).setAttribute("role","presentation"),v.setAttribute("cm-text","\t"),e.col+=b}else"\r"==p[0]||"\n"==p[0]?((v=c.appendChild(N("span","\r"==p[0]?"␍":"␤","cm-invalidchar"))).setAttribute("cm-text",p[0]),e.col+=1):((v=e.cm.options.specialCharPlaceholder(p[0])).setAttribute("cm-text",p[0]),a&&l<9?c.appendChild(N("span",[v])):c.appendChild(v),e.col+=1);e.map.push(e.pos,e.pos+1,v),e.pos++}}else e.col+=t.length,c=document.createTextNode(u),e.map.push(e.pos,e.pos+t.length,c),a&&l<9&&(d=!0),e.pos+=t.length;if(e.trailingSpace=32==u.charCodeAt(t.length-1),n||r||i||d||o||s){var x=n||"";r&&(x+=r),i&&(x+=i);var w=N("span",[c],x,o);if(s)for(var k in s)s.hasOwnProperty(k)&&"style"!=k&&"class"!=k&&w.setAttribute(k,s[k]);return e.content.appendChild(w)}e.content.appendChild(c)}}function en(e,t){return function(n,r,i,o,a,l,s){i=i?i+" cm-force-border":"cm-force-border";for(var c=n.pos,u=c+r.length;;){for(var f=void 0,d=0;d<t.length&&!((f=t[d]).to>c&&f.from<=c);d++);if(f.to>=u)return e(n,r,i,o,a,l,s);e(n,r.slice(0,f.to-c),i,o,null,l,s),o=null,r=r.slice(f.to-c),c=f.to}}}function tn(e,t,n,r){var i=!r&&n.widgetNode;i&&e.map.push(e.pos,e.pos+t,i),!r&&e.cm.display.input.needsContentAttribute&&(i||(i=e.content.appendChild(document.createElement("span"))),i.setAttribute("cm-marker",n.id)),i&&(e.cm.display.input.setUneditable(i),e.content.appendChild(i)),e.pos+=t,e.trailingSpace=!1}function nn(e,t,n){var r=e.markedSpans,i=e.text,o=0;if(r)for(var a,l,s,c,u,f,d,h=i.length,p=0,m=1,g="",v=0;;){if(v==p){s=c=u=l="",d=null,f=null,v=1/0;for(var y=[],b=void 0,x=0;x<r.length;++x){var w=r[x],k=w.marker;if("bookmark"==k.type&&w.from==p&&k.widgetNode)y.push(k);else if(w.from<=p&&(null==w.to||w.to>p||k.collapsed&&w.to==p&&w.from==p)){if(null!=w.to&&w.to!=p&&v>w.to&&(v=w.to,c=""),k.className&&(s+=" "+k.className),k.css&&(l=(l?l+";":"")+k.css),k.startStyle&&w.from==p&&(u+=" "+k.startStyle),k.endStyle&&w.to==v&&(b||(b=[])).push(k.endStyle,w.to),k.title&&((d||(d={})).title=k.title),k.attributes)for(var C in k.attributes)(d||(d={}))[C]=k.attributes[C];k.collapsed&&(!f||zt(f.marker,k)<0)&&(f=w)}else w.from>p&&v>w.from&&(v=w.from)}if(b)for(var S=0;S<b.length;S+=2)b[S+1]==v&&(c+=" "+b[S]);if(!f||f.from==p)for(var T=0;T<y.length;++T)tn(t,0,y[T]);if(f&&(f.from||0)==p){if(tn(t,(null==f.to?h+1:f.to)-p,f.marker,null==f.from),null==f.to)return;f.to==p&&(f=!1)}}if(p>=h)break;for(var L=Math.min(h,v);;){if(g){var M=p+g.length;if(!f){var O=M>L?g.slice(0,L-p):g;t.addToken(t,O,a?a+s:s,u,p+O.length==v?c:"",l,d)}if(M>=L){g=g.slice(L-p),p=L;break}p=M,u=""}g=i.slice(o,o=n[m++]),a=Zt(n[m++],t.cm.options)}}else for(var N=1;N<n.length;N+=2)t.addToken(t,i.slice(o,o=n[N]),Zt(n[N+1],t.cm.options))}function rn(e,t,n){this.line=t,this.rest=function(e){for(var t,n;t=It(e);)e=t.find(1,!0).line,(n||(n=[])).push(e);return n}(t),this.size=this.rest?Ze(G(this.rest))-n+1:1,this.node=this.text=null,this.hidden=Bt(e,t)}function on(e,t,n){for(var r,i=[],o=t;o<n;o=r){var a=new rn(e.doc,Ke(e.doc,o),o);r=o+a.size,i.push(a)}return i}var an=null,ln=null;function sn(e,t){var n=de(e,t);if(n.length){var r,i=Array.prototype.slice.call(arguments,2);an?r=an.delayedCallbacks:ln?r=ln:(r=ln=[],setTimeout(cn,0));for(var o=function(e){r.push((function(){return n[e].apply(null,i)}))},a=0;a<n.length;++a)o(a)}}function cn(){var e=ln;ln=null;for(var t=0;t<e.length;++t)e[t]()}function un(e,t,n,r){for(var i=0;i<t.changes.length;i++){var o=t.changes[i];"text"==o?hn(e,t):"gutter"==o?mn(e,t,n,r):"class"==o?pn(e,t):"widget"==o&&gn(e,t,r)}t.changes=null}function fn(e){return e.node==e.text&&(e.node=N("div",null,null,"position: relative"),e.text.parentNode&&e.text.parentNode.replaceChild(e.node,e.text),e.node.appendChild(e.text),a&&l<8&&(e.node.style.zIndex=2)),e.node}function dn(e,t){var n=e.display.externalMeasured;return n&&n.line==t.line?(e.display.externalMeasured=null,t.measure=n.measure,n.built):Yt(e,t)}function hn(e,t){var n=t.text.className,r=dn(e,t);t.text==t.node&&(t.node=r.pre),t.text.parentNode.replaceChild(r.pre,t.text),t.text=r.pre,r.bgClass!=t.bgClass||r.textClass!=t.textClass?(t.bgClass=r.bgClass,t.textClass=r.textClass,pn(e,t)):n&&(t.text.className=n)}function pn(e,t){!function(e,t){var n=t.bgClass?t.bgClass+" "+(t.line.bgClass||""):t.line.bgClass;if(n&&(n+=" CodeMirror-linebackground"),t.background)n?t.background.className=n:(t.background.parentNode.removeChild(t.background),t.background=null);else if(n){var r=fn(t);t.background=r.insertBefore(N("div",null,n),r.firstChild),e.display.input.setUneditable(t.background)}}(e,t),t.line.wrapClass?fn(t).className=t.line.wrapClass:t.node!=t.text&&(t.node.className="");var n=t.textClass?t.textClass+" "+(t.line.textClass||""):t.line.textClass;t.text.className=n||""}function mn(e,t,n,r){if(t.gutter&&(t.node.removeChild(t.gutter),t.gutter=null),t.gutterBackground&&(t.node.removeChild(t.gutterBackground),t.gutterBackground=null),t.line.gutterClass){var i=fn(t);t.gutterBackground=N("div",null,"CodeMirror-gutter-background "+t.line.gutterClass,"left: "+(e.options.fixedGutter?r.fixedPos:-r.gutterTotalWidth)+"px; width: "+r.gutterTotalWidth+"px"),e.display.input.setUneditable(t.gutterBackground),i.insertBefore(t.gutterBackground,t.text)}var o=t.line.gutterMarkers;if(e.options.lineNumbers||o){var a=fn(t),l=t.gutter=N("div",null,"CodeMirror-gutter-wrapper","left: "+(e.options.fixedGutter?r.fixedPos:-r.gutterTotalWidth)+"px");if(e.display.input.setUneditable(l),a.insertBefore(l,t.text),t.line.gutterClass&&(l.className+=" "+t.line.gutterClass),!e.options.lineNumbers||o&&o["CodeMirror-linenumbers"]||(t.lineNumber=l.appendChild(N("div",Je(e.options,n),"CodeMirror-linenumber CodeMirror-gutter-elt","left: "+r.gutterLeft["CodeMirror-linenumbers"]+"px; width: "+e.display.lineNumInnerWidth+"px"))),o)for(var s=0;s<e.display.gutterSpecs.length;++s){var c=e.display.gutterSpecs[s].className,u=o.hasOwnProperty(c)&&o[c];u&&l.appendChild(N("div",[u],"CodeMirror-gutter-elt","left: "+r.gutterLeft[c]+"px; width: "+r.gutterWidth[c]+"px"))}}}function gn(e,t,n){t.alignable&&(t.alignable=null);for(var r=S("CodeMirror-linewidget"),i=t.node.firstChild,o=void 0;i;i=o)o=i.nextSibling,r.test(i.className)&&t.node.removeChild(i);yn(e,t,n)}function vn(e,t,n,r){var i=dn(e,t);return t.text=t.node=i.pre,i.bgClass&&(t.bgClass=i.bgClass),i.textClass&&(t.textClass=i.textClass),pn(e,t),mn(e,t,n,r),yn(e,t,r),t.node}function yn(e,t,n){if(bn(e,t.line,t,n,!0),t.rest)for(var r=0;r<t.rest.length;r++)bn(e,t.rest[r],t,n,!1)}function bn(e,t,n,r,i){if(t.widgets)for(var o=fn(n),a=0,l=t.widgets;a<l.length;++a){var s=l[a],c=N("div",[s.node],"CodeMirror-linewidget"+(s.className?" "+s.className:""));s.handleMouseEvents||c.setAttribute("cm-ignore-events","true"),xn(s,c,n,r),e.display.input.setUneditable(c),i&&s.above?o.insertBefore(c,n.gutter||n.text):o.appendChild(c),sn(s,"redraw")}}function xn(e,t,n,r){if(e.noHScroll){(n.alignable||(n.alignable=[])).push(t);var i=r.wrapperWidth;t.style.left=r.fixedPos+"px",e.coverGutter||(i-=r.gutterTotalWidth,t.style.paddingLeft=r.gutterTotalWidth+"px"),t.style.width=i+"px"}e.coverGutter&&(t.style.zIndex=5,t.style.position="relative",e.noHScroll||(t.style.marginLeft=-r.gutterTotalWidth+"px"))}function wn(e){if(null!=e.height)return e.height;var t=e.doc.cm;if(!t)return 0;if(!E(document.body,e.node)){var n="position: relative;";e.coverGutter&&(n+="margin-left: -"+t.display.gutters.offsetWidth+"px;"),e.noHScroll&&(n+="width: "+t.display.wrapper.clientWidth+"px;"),O(t.display.measure,N("div",[e.node],null,n))}return e.height=e.node.parentNode.offsetHeight}function kn(e,t){for(var n=Ce(t);n!=e.wrapper;n=n.parentNode)if(!n||1==n.nodeType&&"true"==n.getAttribute("cm-ignore-events")||n.parentNode==e.sizer&&n!=e.mover)return!0}function Cn(e){return e.lineSpace.offsetTop}function Sn(e){return e.mover.offsetHeight-e.lineSpace.offsetHeight}function Tn(e){if(e.cachedPaddingH)return e.cachedPaddingH;var t=O(e.measure,N("pre","x","CodeMirror-line-like")),n=window.getComputedStyle?window.getComputedStyle(t):t.currentStyle,r={left:parseInt(n.paddingLeft),right:parseInt(n.paddingRight)};return isNaN(r.left)||isNaN(r.right)||(e.cachedPaddingH=r),r}function Ln(e){return 50-e.display.nativeBarWidth}function Mn(e){return e.display.scroller.clientWidth-Ln(e)-e.display.barWidth}function On(e){return e.display.scroller.clientHeight-Ln(e)-e.display.barHeight}function Nn(e,t,n){if(e.line==t)return{map:e.measure.map,cache:e.measure.cache};for(var r=0;r<e.rest.length;r++)if(e.rest[r]==t)return{map:e.measure.maps[r],cache:e.measure.caches[r]};for(var i=0;i<e.rest.length;i++)if(Ze(e.rest[i])>n)return{map:e.measure.maps[i],cache:e.measure.caches[i],before:!0}}function An(e,t,n,r){return Pn(e,zn(e,t),n,r)}function En(e,t){if(t>=e.display.viewFrom&&t<e.display.viewTo)return e.display.view[ur(e,t)];var n=e.display.externalMeasured;return n&&t>=n.lineN&&t<n.lineN+n.size?n:void 0}function zn(e,t){var n=Ze(t),r=En(e,n);r&&!r.text?r=null:r&&r.changes&&(un(e,r,n,or(e)),e.curOp.forceUpdate=!0),r||(r=function(e,t){var n=Ze(t=Wt(t)),r=e.display.externalMeasured=new rn(e.doc,t,n);r.lineN=n;var i=r.built=Yt(e,r);return r.text=i.pre,O(e.display.lineMeasure,i.pre),r}(e,t));var i=Nn(r,t,n);return{line:t,view:r,rect:null,map:i.map,cache:i.cache,before:i.before,hasHeights:!1}}function Pn(e,t,n,r,i){t.before&&(n=-1);var o,s=n+(r||"");return t.cache.hasOwnProperty(s)?o=t.cache[s]:(t.rect||(t.rect=t.view.text.getBoundingClientRect()),t.hasHeights||(function(e,t,n){var r=e.options.lineWrapping,i=r&&Mn(e);if(!t.measure.heights||r&&t.measure.width!=i){var o=t.measure.heights=[];if(r){t.measure.width=i;for(var a=t.text.firstChild.getClientRects(),l=0;l<a.length-1;l++){var s=a[l],c=a[l+1];Math.abs(s.bottom-c.bottom)>2&&o.push((s.bottom+c.top)/2-n.top)}}o.push(n.bottom-n.top)}}(e,t.view,t.rect),t.hasHeights=!0),(o=function(e,t,n,r){var i,o=Fn(t.map,n,r),s=o.node,c=o.start,u=o.end,f=o.collapse;if(3==s.nodeType){for(var d=0;d<4;d++){for(;c&&re(t.line.text.charAt(o.coverStart+c));)--c;for(;o.coverStart+u<o.coverEnd&&re(t.line.text.charAt(o.coverStart+u));)++u;if((i=a&&l<9&&0==c&&u==o.coverEnd-o.coverStart?s.parentNode.getBoundingClientRect():_n(T(s,c,u).getClientRects(),r)).left||i.right||0==c)break;u=c,c-=1,f="right"}a&&l<11&&(i=function(e,t){if(!window.screen||null==screen.logicalXDPI||screen.logicalXDPI==screen.deviceXDPI||!function(e){if(null!=De)return De;var t=O(e,N("span","x")),n=t.getBoundingClientRect(),r=T(t,0,1).getBoundingClientRect();return De=Math.abs(n.left-r.left)>1}(e))return t;var n=screen.logicalXDPI/screen.deviceXDPI,r=screen.logicalYDPI/screen.deviceYDPI;return{left:t.left*n,right:t.right*n,top:t.top*r,bottom:t.bottom*r}}(e.display.measure,i))}else{var h;c>0&&(f=r="right"),i=e.options.lineWrapping&&(h=s.getClientRects()).length>1?h["right"==r?h.length-1:0]:s.getBoundingClientRect()}if(a&&l<9&&!c&&(!i||!i.left&&!i.right)){var p=s.parentNode.getClientRects()[0];i=p?{left:p.left,right:p.left+ir(e.display),top:p.top,bottom:p.bottom}:In}for(var m=i.top-t.rect.top,g=i.bottom-t.rect.top,v=(m+g)/2,y=t.view.measure.heights,b=0;b<y.length-1&&!(v<y[b]);b++);var x=b?y[b-1]:0,w=y[b],k={left:("right"==f?i.right:i.left)-t.rect.left,right:("left"==f?i.left:i.right)-t.rect.left,top:x,bottom:w};return i.left||i.right||(k.bogus=!0),e.options.singleCursorHeightPerLine||(k.rtop=m,k.rbottom=g),k}(e,t,n,r)).bogus||(t.cache[s]=o)),{left:o.left,right:o.right,top:i?o.rtop:o.top,bottom:i?o.rbottom:o.bottom}}var Dn,In={left:0,right:0,top:0,bottom:0};function Fn(e,t,n){for(var r,i,o,a,l,s,c=0;c<e.length;c+=3)if(l=e[c],s=e[c+1],t<l?(i=0,o=1,a="left"):t<s?o=1+(i=t-l):(c==e.length-3||t==s&&e[c+3]>t)&&(i=(o=s-l)-1,t>=s&&(a="right")),null!=i){if(r=e[c+2],l==s&&n==(r.insertLeft?"left":"right")&&(a=n),"left"==n&&0==i)for(;c&&e[c-2]==e[c-3]&&e[c-1].insertLeft;)r=e[2+(c-=3)],a="left";if("right"==n&&i==s-l)for(;c<e.length-3&&e[c+3]==e[c+4]&&!e[c+5].insertLeft;)r=e[(c+=3)+2],a="right";break}return{node:r,start:i,end:o,collapse:a,coverStart:l,coverEnd:s}}function _n(e,t){var n=In;if("left"==t)for(var r=0;r<e.length&&(n=e[r]).left==n.right;r++);else for(var i=e.length-1;i>=0&&(n=e[i]).left==n.right;i--);return n}function Wn(e){if(e.measure&&(e.measure.cache={},e.measure.heights=null,e.rest))for(var t=0;t<e.rest.length;t++)e.measure.caches[t]={}}function Hn(e){e.display.externalMeasure=null,M(e.display.lineMeasure);for(var t=0;t<e.display.view.length;t++)Wn(e.display.view[t])}function Rn(e){Hn(e),e.display.cachedCharWidth=e.display.cachedTextHeight=e.display.cachedPaddingH=null,e.options.lineWrapping||(e.display.maxLineChanged=!0),e.display.lineNumChars=null}function Bn(){return u&&g?-(document.body.getBoundingClientRect().left-parseInt(getComputedStyle(document.body).marginLeft)):window.pageXOffset||(document.documentElement||document.body).scrollLeft}function jn(){return u&&g?-(document.body.getBoundingClientRect().top-parseInt(getComputedStyle(document.body).marginTop)):window.pageYOffset||(document.documentElement||document.body).scrollTop}function qn(e){var t=0;if(e.widgets)for(var n=0;n<e.widgets.length;++n)e.widgets[n].above&&(t+=wn(e.widgets[n]));return t}function Un(e,t,n,r,i){if(!i){var o=qn(t);n.top+=o,n.bottom+=o}if("line"==r)return n;r||(r="local");var a=qt(t);if("local"==r?a+=Cn(e.display):a-=e.display.viewOffset,"page"==r||"window"==r){var l=e.display.lineSpace.getBoundingClientRect();a+=l.top+("window"==r?0:jn());var s=l.left+("window"==r?0:Bn());n.left+=s,n.right+=s}return n.top+=a,n.bottom+=a,n}function Vn(e,t,n){if("div"==n)return t;var r=t.left,i=t.top;if("page"==n)r-=Bn(),i-=jn();else if("local"==n||!n){var o=e.display.sizer.getBoundingClientRect();r+=o.left,i+=o.top}var a=e.display.lineSpace.getBoundingClientRect();return{left:r-a.left,top:i-a.top}}function Kn(e,t,n,r,i){return r||(r=Ke(e.doc,t.line)),Un(e,r,An(e,r,t.ch,i),n)}function $n(e,t,n,r,i,o){function a(t,a){var l=Pn(e,i,t,a?"right":"left",o);return a?l.left=l.right:l.right=l.left,Un(e,r,l,n)}r=r||Ke(e.doc,t.line),i||(i=zn(e,r));var l=ce(r,e.doc.direction),s=t.ch,c=t.sticky;if(s>=r.text.length?(s=r.text.length,c="before"):s<=0&&(s=0,c="after"),!l)return a("before"==c?s-1:s,"before"==c);function u(e,t,n){return a(n?e-1:e,1==l[t].level!=n)}var f=le(l,s,c),d=ae,h=u(s,f,"before"==c);return null!=d&&(h.other=u(s,d,"before"!=c)),h}function Gn(e,t){var n=0;t=lt(e.doc,t),e.options.lineWrapping||(n=ir(e.display)*t.ch);var r=Ke(e.doc,t.line),i=qt(r)+Cn(e.display);return{left:n,right:n,top:i,bottom:i+r.height}}function Xn(e,t,n,r,i){var o=et(e,t,n);return o.xRel=i,r&&(o.outside=r),o}function Zn(e,t,n){var r=e.doc;if((n+=e.display.viewOffset)<0)return Xn(r.first,0,null,-1,-1);var i=Ye(r,n),o=r.first+r.size-1;if(i>o)return Xn(r.first+r.size-1,Ke(r,o).text.length,null,1,1);t<0&&(t=0);for(var a=Ke(r,i);;){var l=er(e,a,i,t,n),s=Ft(a,l.ch+(l.xRel>0||l.outside>0?1:0));if(!s)return l;var c=s.find(1);if(c.line==i)return c;a=Ke(r,i=c.line)}}function Yn(e,t,n,r){r-=qn(t);var i=t.text.length,o=oe((function(t){return Pn(e,n,t-1).bottom<=r}),i,0);return{begin:o,end:i=oe((function(t){return Pn(e,n,t).top>r}),o,i)}}function Qn(e,t,n,r){return n||(n=zn(e,t)),Yn(e,t,n,Un(e,t,Pn(e,n,r),"line").top)}function Jn(e,t,n,r){return!(e.bottom<=n)&&(e.top>n||(r?e.left:e.right)>t)}function er(e,t,n,r,i){i-=qt(t);var o=zn(e,t),a=qn(t),l=0,s=t.text.length,c=!0,u=ce(t,e.doc.direction);if(u){var f=(e.options.lineWrapping?nr:tr)(e,t,n,o,u,r,i);l=(c=1!=f.level)?f.from:f.to-1,s=c?f.to:f.from-1}var d,h,p=null,m=null,g=oe((function(t){var n=Pn(e,o,t);return n.top+=a,n.bottom+=a,!!Jn(n,r,i,!1)&&(n.top<=i&&n.left<=r&&(p=t,m=n),!0)}),l,s),v=!1;if(m){var y=r-m.left<m.right-r,b=y==c;g=p+(b?0:1),h=b?"after":"before",d=y?m.left:m.right}else{c||g!=s&&g!=l||g++,h=0==g?"after":g==t.text.length?"before":Pn(e,o,g-(c?1:0)).bottom+a<=i==c?"after":"before";var x=$n(e,et(n,g,h),"line",t,o);d=x.left,v=i<x.top?-1:i>=x.bottom?1:0}return Xn(n,g=ie(t.text,g,1),h,v,r-d)}function tr(e,t,n,r,i,o,a){var l=oe((function(l){var s=i[l],c=1!=s.level;return Jn($n(e,et(n,c?s.to:s.from,c?"before":"after"),"line",t,r),o,a,!0)}),0,i.length-1),s=i[l];if(l>0){var c=1!=s.level,u=$n(e,et(n,c?s.from:s.to,c?"after":"before"),"line",t,r);Jn(u,o,a,!0)&&u.top>a&&(s=i[l-1])}return s}function nr(e,t,n,r,i,o,a){var l=Yn(e,t,r,a),s=l.begin,c=l.end;/\s/.test(t.text.charAt(c-1))&&c--;for(var u=null,f=null,d=0;d<i.length;d++){var h=i[d];if(!(h.from>=c||h.to<=s)){var p=Pn(e,r,1!=h.level?Math.min(c,h.to)-1:Math.max(s,h.from)).right,m=p<o?o-p+1e9:p-o;(!u||f>m)&&(u=h,f=m)}}return u||(u=i[i.length-1]),u.from<s&&(u={from:s,to:u.to,level:u.level}),u.to>c&&(u={from:u.from,to:c,level:u.level}),u}function rr(e){if(null!=e.cachedTextHeight)return e.cachedTextHeight;if(null==Dn){Dn=N("pre",null,"CodeMirror-line-like");for(var t=0;t<49;++t)Dn.appendChild(document.createTextNode("x")),Dn.appendChild(N("br"));Dn.appendChild(document.createTextNode("x"))}O(e.measure,Dn);var n=Dn.offsetHeight/50;return n>3&&(e.cachedTextHeight=n),M(e.measure),n||1}function ir(e){if(null!=e.cachedCharWidth)return e.cachedCharWidth;var t=N("span","xxxxxxxxxx"),n=N("pre",[t],"CodeMirror-line-like");O(e.measure,n);var r=t.getBoundingClientRect(),i=(r.right-r.left)/10;return i>2&&(e.cachedCharWidth=i),i||10}function or(e){for(var t=e.display,n={},r={},i=t.gutters.clientLeft,o=t.gutters.firstChild,a=0;o;o=o.nextSibling,++a){var l=e.display.gutterSpecs[a].className;n[l]=o.offsetLeft+o.clientLeft+i,r[l]=o.clientWidth}return{fixedPos:ar(t),gutterTotalWidth:t.gutters.offsetWidth,gutterLeft:n,gutterWidth:r,wrapperWidth:t.wrapper.clientWidth}}function ar(e){return e.scroller.getBoundingClientRect().left-e.sizer.getBoundingClientRect().left}function lr(e){var t=rr(e.display),n=e.options.lineWrapping,r=n&&Math.max(5,e.display.scroller.clientWidth/ir(e.display)-3);return function(i){if(Bt(e.doc,i))return 0;var o=0;if(i.widgets)for(var a=0;a<i.widgets.length;a++)i.widgets[a].height&&(o+=i.widgets[a].height);return n?o+(Math.ceil(i.text.length/r)||1)*t:o+t}}function sr(e){var t=e.doc,n=lr(e);t.iter((function(e){var t=n(e);t!=e.height&&Xe(e,t)}))}function cr(e,t,n,r){var i=e.display;if(!n&&"true"==Ce(t).getAttribute("cm-not-content"))return null;var o,a,l=i.lineSpace.getBoundingClientRect();try{o=t.clientX-l.left,a=t.clientY-l.top}catch(e){return null}var s,c=Zn(e,o,a);if(r&&c.xRel>0&&(s=Ke(e.doc,c.line).text).length==c.ch){var u=W(s,s.length,e.options.tabSize)-s.length;c=et(c.line,Math.max(0,Math.round((o-Tn(e.display).left)/ir(e.display))-u))}return c}function ur(e,t){if(t>=e.display.viewTo)return null;if((t-=e.display.viewFrom)<0)return null;for(var n=e.display.view,r=0;r<n.length;r++)if((t-=n[r].size)<0)return r}function fr(e,t,n,r){null==t&&(t=e.doc.first),null==n&&(n=e.doc.first+e.doc.size),r||(r=0);var i=e.display;if(r&&n<i.viewTo&&(null==i.updateLineNumbers||i.updateLineNumbers>t)&&(i.updateLineNumbers=t),e.curOp.viewChanged=!0,t>=i.viewTo)kt&&Ht(e.doc,t)<i.viewTo&&hr(e);else if(n<=i.viewFrom)kt&&Rt(e.doc,n+r)>i.viewFrom?hr(e):(i.viewFrom+=r,i.viewTo+=r);else if(t<=i.viewFrom&&n>=i.viewTo)hr(e);else if(t<=i.viewFrom){var o=pr(e,n,n+r,1);o?(i.view=i.view.slice(o.index),i.viewFrom=o.lineN,i.viewTo+=r):hr(e)}else if(n>=i.viewTo){var a=pr(e,t,t,-1);a?(i.view=i.view.slice(0,a.index),i.viewTo=a.lineN):hr(e)}else{var l=pr(e,t,t,-1),s=pr(e,n,n+r,1);l&&s?(i.view=i.view.slice(0,l.index).concat(on(e,l.lineN,s.lineN)).concat(i.view.slice(s.index)),i.viewTo+=r):hr(e)}var c=i.externalMeasured;c&&(n<c.lineN?c.lineN+=r:t<c.lineN+c.size&&(i.externalMeasured=null))}function dr(e,t,n){e.curOp.viewChanged=!0;var r=e.display,i=e.display.externalMeasured;if(i&&t>=i.lineN&&t<i.lineN+i.size&&(r.externalMeasured=null),!(t<r.viewFrom||t>=r.viewTo)){var o=r.view[ur(e,t)];if(null!=o.node){var a=o.changes||(o.changes=[]);-1==R(a,n)&&a.push(n)}}}function hr(e){e.display.viewFrom=e.display.viewTo=e.doc.first,e.display.view=[],e.display.viewOffset=0}function pr(e,t,n,r){var i,o=ur(e,t),a=e.display.view;if(!kt||n==e.doc.first+e.doc.size)return{index:o,lineN:n};for(var l=e.display.viewFrom,s=0;s<o;s++)l+=a[s].size;if(l!=t){if(r>0){if(o==a.length-1)return null;i=l+a[o].size-t,o++}else i=l-t;t+=i,n+=i}for(;Ht(e.doc,n)!=n;){if(o==(r<0?0:a.length-1))return null;n+=r*a[o-(r<0?1:0)].size,o+=r}return{index:o,lineN:n}}function mr(e){for(var t=e.display.view,n=0,r=0;r<t.length;r++){var i=t[r];i.hidden||i.node&&!i.changes||++n}return n}function gr(e){e.display.input.showSelection(e.display.input.prepareSelection())}function vr(e,t){void 0===t&&(t=!0);for(var n=e.doc,r={},i=r.cursors=document.createDocumentFragment(),o=r.selection=document.createDocumentFragment(),a=0;a<n.sel.ranges.length;a++)if(t||a!=n.sel.primIndex){var l=n.sel.ranges[a];if(!(l.from().line>=e.display.viewTo||l.to().line<e.display.viewFrom)){var s=l.empty();(s||e.options.showCursorWhenSelecting)&&yr(e,l.head,i),s||xr(e,l,o)}}return r}function yr(e,t,n){var r=$n(e,t,"div",null,null,!e.options.singleCursorHeightPerLine),i=n.appendChild(N("div"," ","CodeMirror-cursor"));if(i.style.left=r.left+"px",i.style.top=r.top+"px",i.style.height=Math.max(0,r.bottom-r.top)*e.options.cursorHeight+"px",r.other){var o=n.appendChild(N("div"," ","CodeMirror-cursor CodeMirror-secondarycursor"));o.style.display="",o.style.left=r.other.left+"px",o.style.top=r.other.top+"px",o.style.height=.85*(r.other.bottom-r.other.top)+"px"}}function br(e,t){return e.top-t.top||e.left-t.left}function xr(e,t,n){var r=e.display,i=e.doc,o=document.createDocumentFragment(),a=Tn(e.display),l=a.left,s=Math.max(r.sizerWidth,Mn(e)-r.sizer.offsetLeft)-a.right,c="ltr"==i.direction;function u(e,t,n,r){t<0&&(t=0),t=Math.round(t),r=Math.round(r),o.appendChild(N("div",null,"CodeMirror-selected","position: absolute; left: "+e+"px;\n top: "+t+"px; width: "+(null==n?s-e:n)+"px;\n height: "+(r-t)+"px"))}function f(t,n,r){var o,a,f=Ke(i,t),d=f.text.length;function h(n,r){return Kn(e,et(t,n),"div",f,r)}function p(t,n,r){var i=Qn(e,f,null,t),o="ltr"==n==("after"==r)?"left":"right";return h("after"==r?i.begin:i.end-(/\s/.test(f.text.charAt(i.end-1))?2:1),o)[o]}var m=ce(f,i.direction);return function(e,t,n,r){if(!e)return r(t,n,"ltr",0);for(var i=!1,o=0;o<e.length;++o){var a=e[o];(a.from<n&&a.to>t||t==n&&a.to==t)&&(r(Math.max(a.from,t),Math.min(a.to,n),1==a.level?"rtl":"ltr",o),i=!0)}i||r(t,n,"ltr")}(m,n||0,null==r?d:r,(function(e,t,i,f){var g="ltr"==i,v=h(e,g?"left":"right"),y=h(t-1,g?"right":"left"),b=null==n&&0==e,x=null==r&&t==d,w=0==f,k=!m||f==m.length-1;if(y.top-v.top<=3){var C=(c?x:b)&&k,S=(c?b:x)&&w?l:(g?v:y).left,T=C?s:(g?y:v).right;u(S,v.top,T-S,v.bottom)}else{var L,M,O,N;g?(L=c&&b&&w?l:v.left,M=c?s:p(e,i,"before"),O=c?l:p(t,i,"after"),N=c&&x&&k?s:y.right):(L=c?p(e,i,"before"):l,M=!c&&b&&w?s:v.right,O=!c&&x&&k?l:y.left,N=c?p(t,i,"after"):s),u(L,v.top,M-L,v.bottom),v.bottom<y.top&&u(l,v.bottom,null,y.top),u(O,y.top,N-O,y.bottom)}(!o||br(v,o)<0)&&(o=v),br(y,o)<0&&(o=y),(!a||br(v,a)<0)&&(a=v),br(y,a)<0&&(a=y)})),{start:o,end:a}}var d=t.from(),h=t.to();if(d.line==h.line)f(d.line,d.ch,h.ch);else{var p=Ke(i,d.line),m=Ke(i,h.line),g=Wt(p)==Wt(m),v=f(d.line,d.ch,g?p.text.length+1:null).end,y=f(h.line,g?0:null,h.ch).start;g&&(v.top<y.top-2?(u(v.right,v.top,null,v.bottom),u(l,y.top,y.left,y.bottom)):u(v.right,v.top,y.left-v.right,v.bottom)),v.bottom<y.top&&u(l,v.bottom,null,y.top)}n.appendChild(o)}function wr(e){if(e.state.focused){var t=e.display;clearInterval(t.blinker);var n=!0;t.cursorDiv.style.visibility="",e.options.cursorBlinkRate>0?t.blinker=setInterval((function(){e.hasFocus()||Tr(e),t.cursorDiv.style.visibility=(n=!n)?"":"hidden"}),e.options.cursorBlinkRate):e.options.cursorBlinkRate<0&&(t.cursorDiv.style.visibility="hidden")}}function kr(e){e.state.focused||(e.display.input.focus(),Sr(e))}function Cr(e){e.state.delayingBlurEvent=!0,setTimeout((function(){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1,Tr(e))}),100)}function Sr(e,t){e.state.delayingBlurEvent&&(e.state.delayingBlurEvent=!1),"nocursor"!=e.options.readOnly&&(e.state.focused||(pe(e,"focus",e,t),e.state.focused=!0,P(e.display.wrapper,"CodeMirror-focused"),e.curOp||e.display.selForContextMenu==e.doc.sel||(e.display.input.reset(),s&&setTimeout((function(){return e.display.input.reset(!0)}),20)),e.display.input.receivedFocus()),wr(e))}function Tr(e,t){e.state.delayingBlurEvent||(e.state.focused&&(pe(e,"blur",e,t),e.state.focused=!1,L(e.display.wrapper,"CodeMirror-focused")),clearInterval(e.display.blinker),setTimeout((function(){e.state.focused||(e.display.shift=!1)}),150))}function Lr(e){for(var t=e.display,n=t.lineDiv.offsetTop,r=0;r<t.view.length;r++){var i=t.view[r],o=e.options.lineWrapping,s=void 0,c=0;if(!i.hidden){if(a&&l<8){var u=i.node.offsetTop+i.node.offsetHeight;s=u-n,n=u}else{var f=i.node.getBoundingClientRect();s=f.bottom-f.top,!o&&i.text.firstChild&&(c=i.text.firstChild.getBoundingClientRect().right-f.left-1)}var d=i.line.height-s;if((d>.005||d<-.005)&&(Xe(i.line,s),Mr(i.line),i.rest))for(var h=0;h<i.rest.length;h++)Mr(i.rest[h]);if(c>e.display.sizerWidth){var p=Math.ceil(c/ir(e.display));p>e.display.maxLineLength&&(e.display.maxLineLength=p,e.display.maxLine=i.line,e.display.maxLineChanged=!0)}}}}function Mr(e){if(e.widgets)for(var t=0;t<e.widgets.length;++t){var n=e.widgets[t],r=n.node.parentNode;r&&(n.height=r.offsetHeight)}}function Or(e,t,n){var r=n&&null!=n.top?Math.max(0,n.top):e.scroller.scrollTop;r=Math.floor(r-Cn(e));var i=n&&null!=n.bottom?n.bottom:r+e.wrapper.clientHeight,o=Ye(t,r),a=Ye(t,i);if(n&&n.ensure){var l=n.ensure.from.line,s=n.ensure.to.line;l<o?(o=l,a=Ye(t,qt(Ke(t,l))+e.wrapper.clientHeight)):Math.min(s,t.lastLine())>=a&&(o=Ye(t,qt(Ke(t,s))-e.wrapper.clientHeight),a=s)}return{from:o,to:Math.max(a,o+1)}}function Nr(e,t){var n=e.display,r=rr(e.display);t.top<0&&(t.top=0);var i=e.curOp&&null!=e.curOp.scrollTop?e.curOp.scrollTop:n.scroller.scrollTop,o=On(e),a={};t.bottom-t.top>o&&(t.bottom=t.top+o);var l=e.doc.height+Sn(n),s=t.top<r,c=t.bottom>l-r;if(t.top<i)a.scrollTop=s?0:t.top;else if(t.bottom>i+o){var u=Math.min(t.top,(c?l:t.bottom)-o);u!=i&&(a.scrollTop=u)}var f=e.curOp&&null!=e.curOp.scrollLeft?e.curOp.scrollLeft:n.scroller.scrollLeft,d=Mn(e)-(e.options.fixedGutter?n.gutters.offsetWidth:0),h=t.right-t.left>d;return h&&(t.right=t.left+d),t.left<10?a.scrollLeft=0:t.left<f?a.scrollLeft=Math.max(0,t.left-(h?0:10)):t.right>d+f-3&&(a.scrollLeft=t.right+(h?0:10)-d),a}function Ar(e,t){null!=t&&(Pr(e),e.curOp.scrollTop=(null==e.curOp.scrollTop?e.doc.scrollTop:e.curOp.scrollTop)+t)}function Er(e){Pr(e);var t=e.getCursor();e.curOp.scrollToPos={from:t,to:t,margin:e.options.cursorScrollMargin}}function zr(e,t,n){null==t&&null==n||Pr(e),null!=t&&(e.curOp.scrollLeft=t),null!=n&&(e.curOp.scrollTop=n)}function Pr(e){var t=e.curOp.scrollToPos;t&&(e.curOp.scrollToPos=null,Dr(e,Gn(e,t.from),Gn(e,t.to),t.margin))}function Dr(e,t,n,r){var i=Nr(e,{left:Math.min(t.left,n.left),top:Math.min(t.top,n.top)-r,right:Math.max(t.right,n.right),bottom:Math.max(t.bottom,n.bottom)+r});zr(e,i.scrollLeft,i.scrollTop)}function Ir(e,t){Math.abs(e.doc.scrollTop-t)<2||(n||si(e,{top:t}),Fr(e,t,!0),n&&si(e),ri(e,100))}function Fr(e,t,n){t=Math.max(0,Math.min(e.display.scroller.scrollHeight-e.display.scroller.clientHeight,t)),(e.display.scroller.scrollTop!=t||n)&&(e.doc.scrollTop=t,e.display.scrollbars.setScrollTop(t),e.display.scroller.scrollTop!=t&&(e.display.scroller.scrollTop=t))}function _r(e,t,n,r){t=Math.max(0,Math.min(t,e.display.scroller.scrollWidth-e.display.scroller.clientWidth)),(n?t==e.doc.scrollLeft:Math.abs(e.doc.scrollLeft-t)<2)&&!r||(e.doc.scrollLeft=t,fi(e),e.display.scroller.scrollLeft!=t&&(e.display.scroller.scrollLeft=t),e.display.scrollbars.setScrollLeft(t))}function Wr(e){var t=e.display,n=t.gutters.offsetWidth,r=Math.round(e.doc.height+Sn(e.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:e.options.fixedGutter?n:0,docHeight:r,scrollHeight:r+Ln(e)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:n}}var Hr=function(e,t,n){this.cm=n;var r=this.vert=N("div",[N("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),i=this.horiz=N("div",[N("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");r.tabIndex=i.tabIndex=-1,e(r),e(i),fe(r,"scroll",(function(){r.clientHeight&&t(r.scrollTop,"vertical")})),fe(i,"scroll",(function(){i.clientWidth&&t(i.scrollLeft,"horizontal")})),this.checkedZeroWidth=!1,a&&l<8&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")};Hr.prototype.update=function(e){var t=e.scrollWidth>e.clientWidth+1,n=e.scrollHeight>e.clientHeight+1,r=e.nativeBarWidth;if(n){this.vert.style.display="block",this.vert.style.bottom=t?r+"px":"0";var i=e.viewHeight-(t?r:0);this.vert.firstChild.style.height=Math.max(0,e.scrollHeight-e.clientHeight+i)+"px"}else this.vert.style.display="",this.vert.firstChild.style.height="0";if(t){this.horiz.style.display="block",this.horiz.style.right=n?r+"px":"0",this.horiz.style.left=e.barLeft+"px";var o=e.viewWidth-e.barLeft-(n?r:0);this.horiz.firstChild.style.width=Math.max(0,e.scrollWidth-e.clientWidth+o)+"px"}else this.horiz.style.display="",this.horiz.firstChild.style.width="0";return!this.checkedZeroWidth&&e.clientHeight>0&&(0==r&&this.zeroWidthHack(),this.checkedZeroWidth=!0),{right:n?r:0,bottom:t?r:0}},Hr.prototype.setScrollLeft=function(e){this.horiz.scrollLeft!=e&&(this.horiz.scrollLeft=e),this.disableHoriz&&this.enableZeroWidthBar(this.horiz,this.disableHoriz,"horiz")},Hr.prototype.setScrollTop=function(e){this.vert.scrollTop!=e&&(this.vert.scrollTop=e),this.disableVert&&this.enableZeroWidthBar(this.vert,this.disableVert,"vert")},Hr.prototype.zeroWidthHack=function(){var e=y&&!h?"12px":"18px";this.horiz.style.height=this.vert.style.width=e,this.horiz.style.pointerEvents=this.vert.style.pointerEvents="none",this.disableHoriz=new H,this.disableVert=new H},Hr.prototype.enableZeroWidthBar=function(e,t,n){e.style.pointerEvents="auto",t.set(1e3,(function r(){var i=e.getBoundingClientRect();("vert"==n?document.elementFromPoint(i.right-1,(i.top+i.bottom)/2):document.elementFromPoint((i.right+i.left)/2,i.bottom-1))!=e?e.style.pointerEvents="none":t.set(1e3,r)}))},Hr.prototype.clear=function(){var e=this.horiz.parentNode;e.removeChild(this.horiz),e.removeChild(this.vert)};var Rr=function(){};function Br(e,t){t||(t=Wr(e));var n=e.display.barWidth,r=e.display.barHeight;jr(e,t);for(var i=0;i<4&&n!=e.display.barWidth||r!=e.display.barHeight;i++)n!=e.display.barWidth&&e.options.lineWrapping&&Lr(e),jr(e,Wr(e)),n=e.display.barWidth,r=e.display.barHeight}function jr(e,t){var n=e.display,r=n.scrollbars.update(t);n.sizer.style.paddingRight=(n.barWidth=r.right)+"px",n.sizer.style.paddingBottom=(n.barHeight=r.bottom)+"px",n.heightForcer.style.borderBottom=r.bottom+"px solid transparent",r.right&&r.bottom?(n.scrollbarFiller.style.display="block",n.scrollbarFiller.style.height=r.bottom+"px",n.scrollbarFiller.style.width=r.right+"px"):n.scrollbarFiller.style.display="",r.bottom&&e.options.coverGutterNextToScrollbar&&e.options.fixedGutter?(n.gutterFiller.style.display="block",n.gutterFiller.style.height=r.bottom+"px",n.gutterFiller.style.width=t.gutterWidth+"px"):n.gutterFiller.style.display=""}Rr.prototype.update=function(){return{bottom:0,right:0}},Rr.prototype.setScrollLeft=function(){},Rr.prototype.setScrollTop=function(){},Rr.prototype.clear=function(){};var qr={native:Hr,null:Rr};function Ur(e){e.display.scrollbars&&(e.display.scrollbars.clear(),e.display.scrollbars.addClass&&L(e.display.wrapper,e.display.scrollbars.addClass)),e.display.scrollbars=new qr[e.options.scrollbarStyle]((function(t){e.display.wrapper.insertBefore(t,e.display.scrollbarFiller),fe(t,"mousedown",(function(){e.state.focused&&setTimeout((function(){return e.display.input.focus()}),0)})),t.setAttribute("cm-not-content","true")}),(function(t,n){"horizontal"==n?_r(e,t):Ir(e,t)}),e),e.display.scrollbars.addClass&&P(e.display.wrapper,e.display.scrollbars.addClass)}var Vr=0;function Kr(e){var t;e.curOp={cm:e,viewChanged:!1,startHeight:e.doc.height,forceUpdate:!1,updateInput:0,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++Vr},t=e.curOp,an?an.ops.push(t):t.ownsGroup=an={ops:[t],delayedCallbacks:[]}}function $r(e){var t=e.curOp;t&&function(e,t){var n=e.ownsGroup;if(n)try{!function(e){var t=e.delayedCallbacks,n=0;do{for(;n<t.length;n++)t[n].call(null);for(var r=0;r<e.ops.length;r++){var i=e.ops[r];if(i.cursorActivityHandlers)for(;i.cursorActivityCalled<i.cursorActivityHandlers.length;)i.cursorActivityHandlers[i.cursorActivityCalled++].call(null,i.cm)}}while(n<t.length)}(n)}finally{an=null,t(n)}}(t,(function(e){for(var t=0;t<e.ops.length;t++)e.ops[t].cm.curOp=null;!function(e){for(var t=e.ops,n=0;n<t.length;n++)Gr(t[n]);for(var r=0;r<t.length;r++)Xr(t[r]);for(var i=0;i<t.length;i++)Zr(t[i]);for(var o=0;o<t.length;o++)Yr(t[o]);for(var a=0;a<t.length;a++)Qr(t[a])}(e)}))}function Gr(e){var t=e.cm,n=t.display;!function(e){var t=e.display;!t.scrollbarsClipped&&t.scroller.offsetWidth&&(t.nativeBarWidth=t.scroller.offsetWidth-t.scroller.clientWidth,t.heightForcer.style.height=Ln(e)+"px",t.sizer.style.marginBottom=-t.nativeBarWidth+"px",t.sizer.style.borderRightWidth=Ln(e)+"px",t.scrollbarsClipped=!0)}(t),e.updateMaxLine&&Vt(t),e.mustUpdate=e.viewChanged||e.forceUpdate||null!=e.scrollTop||e.scrollToPos&&(e.scrollToPos.from.line<n.viewFrom||e.scrollToPos.to.line>=n.viewTo)||n.maxLineChanged&&t.options.lineWrapping,e.update=e.mustUpdate&&new oi(t,e.mustUpdate&&{top:e.scrollTop,ensure:e.scrollToPos},e.forceUpdate)}function Xr(e){e.updatedDisplay=e.mustUpdate&&ai(e.cm,e.update)}function Zr(e){var t=e.cm,n=t.display;e.updatedDisplay&&Lr(t),e.barMeasure=Wr(t),n.maxLineChanged&&!t.options.lineWrapping&&(e.adjustWidthTo=An(t,n.maxLine,n.maxLine.text.length).left+3,t.display.sizerWidth=e.adjustWidthTo,e.barMeasure.scrollWidth=Math.max(n.scroller.clientWidth,n.sizer.offsetLeft+e.adjustWidthTo+Ln(t)+t.display.barWidth),e.maxScrollLeft=Math.max(0,n.sizer.offsetLeft+e.adjustWidthTo-Mn(t))),(e.updatedDisplay||e.selectionChanged)&&(e.preparedSelection=n.input.prepareSelection())}function Yr(e){var t=e.cm;null!=e.adjustWidthTo&&(t.display.sizer.style.minWidth=e.adjustWidthTo+"px",e.maxScrollLeft<t.doc.scrollLeft&&_r(t,Math.min(t.display.scroller.scrollLeft,e.maxScrollLeft),!0),t.display.maxLineChanged=!1);var n=e.focus&&e.focus==z();e.preparedSelection&&t.display.input.showSelection(e.preparedSelection,n),(e.updatedDisplay||e.startHeight!=t.doc.height)&&Br(t,e.barMeasure),e.updatedDisplay&&ui(t,e.barMeasure),e.selectionChanged&&wr(t),t.state.focused&&e.updateInput&&t.display.input.reset(e.typing),n&&kr(e.cm)}function Qr(e){var t=e.cm,n=t.display,r=t.doc;e.updatedDisplay&&li(t,e.update),null==n.wheelStartX||null==e.scrollTop&&null==e.scrollLeft&&!e.scrollToPos||(n.wheelStartX=n.wheelStartY=null),null!=e.scrollTop&&Fr(t,e.scrollTop,e.forceScroll),null!=e.scrollLeft&&_r(t,e.scrollLeft,!0,!0),e.scrollToPos&&function(e,t){if(!me(e,"scrollCursorIntoView")){var n=e.display,r=n.sizer.getBoundingClientRect(),i=null;if(t.top+r.top<0?i=!0:t.bottom+r.top>(window.innerHeight||document.documentElement.clientHeight)&&(i=!1),null!=i&&!p){var o=N("div","​",null,"position: absolute;\n top: "+(t.top-n.viewOffset-Cn(e.display))+"px;\n height: "+(t.bottom-t.top+Ln(e)+n.barHeight)+"px;\n left: "+t.left+"px; width: "+Math.max(2,t.right-t.left)+"px;");e.display.lineSpace.appendChild(o),o.scrollIntoView(i),e.display.lineSpace.removeChild(o)}}}(t,function(e,t,n,r){var i;null==r&&(r=0),e.options.lineWrapping||t!=n||(n="before"==(t=t.ch?et(t.line,"before"==t.sticky?t.ch-1:t.ch,"after"):t).sticky?et(t.line,t.ch+1,"before"):t);for(var o=0;o<5;o++){var a=!1,l=$n(e,t),s=n&&n!=t?$n(e,n):l,c=Nr(e,i={left:Math.min(l.left,s.left),top:Math.min(l.top,s.top)-r,right:Math.max(l.left,s.left),bottom:Math.max(l.bottom,s.bottom)+r}),u=e.doc.scrollTop,f=e.doc.scrollLeft;if(null!=c.scrollTop&&(Ir(e,c.scrollTop),Math.abs(e.doc.scrollTop-u)>1&&(a=!0)),null!=c.scrollLeft&&(_r(e,c.scrollLeft),Math.abs(e.doc.scrollLeft-f)>1&&(a=!0)),!a)break}return i}(t,lt(r,e.scrollToPos.from),lt(r,e.scrollToPos.to),e.scrollToPos.margin));var i=e.maybeHiddenMarkers,o=e.maybeUnhiddenMarkers;if(i)for(var a=0;a<i.length;++a)i[a].lines.length||pe(i[a],"hide");if(o)for(var l=0;l<o.length;++l)o[l].lines.length&&pe(o[l],"unhide");n.wrapper.offsetHeight&&(r.scrollTop=t.display.scroller.scrollTop),e.changeObjs&&pe(t,"changes",t,e.changeObjs),e.update&&e.update.finish()}function Jr(e,t){if(e.curOp)return t();Kr(e);try{return t()}finally{$r(e)}}function ei(e,t){return function(){if(e.curOp)return t.apply(e,arguments);Kr(e);try{return t.apply(e,arguments)}finally{$r(e)}}}function ti(e){return function(){if(this.curOp)return e.apply(this,arguments);Kr(this);try{return e.apply(this,arguments)}finally{$r(this)}}}function ni(e){return function(){var t=this.cm;if(!t||t.curOp)return e.apply(this,arguments);Kr(t);try{return e.apply(this,arguments)}finally{$r(t)}}}function ri(e,t){e.doc.highlightFrontier<e.display.viewTo&&e.state.highlight.set(t,F(ii,e))}function ii(e){var t=e.doc;if(!(t.highlightFrontier>=e.display.viewTo)){var n=+new Date+e.options.workTime,r=ht(e,t.highlightFrontier),i=[];t.iter(r.line,Math.min(t.first+t.size,e.display.viewTo+500),(function(o){if(r.line>=e.display.viewFrom){var a=o.styles,l=o.text.length>e.options.maxHighlightLength?je(t.mode,r.state):null,s=ft(e,o,r,!0);l&&(r.state=l),o.styles=s.styles;var c=o.styleClasses,u=s.classes;u?o.styleClasses=u:c&&(o.styleClasses=null);for(var f=!a||a.length!=o.styles.length||c!=u&&(!c||!u||c.bgClass!=u.bgClass||c.textClass!=u.textClass),d=0;!f&&d<a.length;++d)f=a[d]!=o.styles[d];f&&i.push(r.line),o.stateAfter=r.save(),r.nextLine()}else o.text.length<=e.options.maxHighlightLength&&pt(e,o.text,r),o.stateAfter=r.line%5==0?r.save():null,r.nextLine();if(+new Date>n)return ri(e,e.options.workDelay),!0})),t.highlightFrontier=r.line,t.modeFrontier=Math.max(t.modeFrontier,r.line),i.length&&Jr(e,(function(){for(var t=0;t<i.length;t++)dr(e,i[t],"text")}))}}var oi=function(e,t,n){var r=e.display;this.viewport=t,this.visible=Or(r,e.doc,t),this.editorIsHidden=!r.wrapper.offsetWidth,this.wrapperHeight=r.wrapper.clientHeight,this.wrapperWidth=r.wrapper.clientWidth,this.oldDisplayWidth=Mn(e),this.force=n,this.dims=or(e),this.events=[]};function ai(e,t){var n=e.display,r=e.doc;if(t.editorIsHidden)return hr(e),!1;if(!t.force&&t.visible.from>=n.viewFrom&&t.visible.to<=n.viewTo&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo)&&n.renderedView==n.view&&0==mr(e))return!1;di(e)&&(hr(e),t.dims=or(e));var i=r.first+r.size,o=Math.max(t.visible.from-e.options.viewportMargin,r.first),a=Math.min(i,t.visible.to+e.options.viewportMargin);n.viewFrom<o&&o-n.viewFrom<20&&(o=Math.max(r.first,n.viewFrom)),n.viewTo>a&&n.viewTo-a<20&&(a=Math.min(i,n.viewTo)),kt&&(o=Ht(e.doc,o),a=Rt(e.doc,a));var l=o!=n.viewFrom||a!=n.viewTo||n.lastWrapHeight!=t.wrapperHeight||n.lastWrapWidth!=t.wrapperWidth;!function(e,t,n){var r=e.display;0==r.view.length||t>=r.viewTo||n<=r.viewFrom?(r.view=on(e,t,n),r.viewFrom=t):(r.viewFrom>t?r.view=on(e,t,r.viewFrom).concat(r.view):r.viewFrom<t&&(r.view=r.view.slice(ur(e,t))),r.viewFrom=t,r.viewTo<n?r.view=r.view.concat(on(e,r.viewTo,n)):r.viewTo>n&&(r.view=r.view.slice(0,ur(e,n)))),r.viewTo=n}(e,o,a),n.viewOffset=qt(Ke(e.doc,n.viewFrom)),e.display.mover.style.top=n.viewOffset+"px";var c=mr(e);if(!l&&0==c&&!t.force&&n.renderedView==n.view&&(null==n.updateLineNumbers||n.updateLineNumbers>=n.viewTo))return!1;var u=function(e){if(e.hasFocus())return null;var t=z();if(!t||!E(e.display.lineDiv,t))return null;var n={activeElt:t};if(window.getSelection){var r=window.getSelection();r.anchorNode&&r.extend&&E(e.display.lineDiv,r.anchorNode)&&(n.anchorNode=r.anchorNode,n.anchorOffset=r.anchorOffset,n.focusNode=r.focusNode,n.focusOffset=r.focusOffset)}return n}(e);return c>4&&(n.lineDiv.style.display="none"),function(e,t,n){var r=e.display,i=e.options.lineNumbers,o=r.lineDiv,a=o.firstChild;function l(t){var n=t.nextSibling;return s&&y&&e.display.currentWheelTarget==t?t.style.display="none":t.parentNode.removeChild(t),n}for(var c=r.view,u=r.viewFrom,f=0;f<c.length;f++){var d=c[f];if(d.hidden);else if(d.node&&d.node.parentNode==o){for(;a!=d.node;)a=l(a);var h=i&&null!=t&&t<=u&&d.lineNumber;d.changes&&(R(d.changes,"gutter")>-1&&(h=!1),un(e,d,u,n)),h&&(M(d.lineNumber),d.lineNumber.appendChild(document.createTextNode(Je(e.options,u)))),a=d.node.nextSibling}else{var p=vn(e,d,u,n);o.insertBefore(p,a)}u+=d.size}for(;a;)a=l(a)}(e,n.updateLineNumbers,t.dims),c>4&&(n.lineDiv.style.display=""),n.renderedView=n.view,function(e){if(e&&e.activeElt&&e.activeElt!=z()&&(e.activeElt.focus(),!/^(INPUT|TEXTAREA)$/.test(e.activeElt.nodeName)&&e.anchorNode&&E(document.body,e.anchorNode)&&E(document.body,e.focusNode))){var t=window.getSelection(),n=document.createRange();n.setEnd(e.anchorNode,e.anchorOffset),n.collapse(!1),t.removeAllRanges(),t.addRange(n),t.extend(e.focusNode,e.focusOffset)}}(u),M(n.cursorDiv),M(n.selectionDiv),n.gutters.style.height=n.sizer.style.minHeight=0,l&&(n.lastWrapHeight=t.wrapperHeight,n.lastWrapWidth=t.wrapperWidth,ri(e,400)),n.updateLineNumbers=null,!0}function li(e,t){for(var n=t.viewport,r=!0;;r=!1){if(r&&e.options.lineWrapping&&t.oldDisplayWidth!=Mn(e))r&&(t.visible=Or(e.display,e.doc,n));else if(n&&null!=n.top&&(n={top:Math.min(e.doc.height+Sn(e.display)-On(e),n.top)}),t.visible=Or(e.display,e.doc,n),t.visible.from>=e.display.viewFrom&&t.visible.to<=e.display.viewTo)break;if(!ai(e,t))break;Lr(e);var i=Wr(e);gr(e),Br(e,i),ui(e,i),t.force=!1}t.signal(e,"update",e),e.display.viewFrom==e.display.reportedViewFrom&&e.display.viewTo==e.display.reportedViewTo||(t.signal(e,"viewportChange",e,e.display.viewFrom,e.display.viewTo),e.display.reportedViewFrom=e.display.viewFrom,e.display.reportedViewTo=e.display.viewTo)}function si(e,t){var n=new oi(e,t);if(ai(e,n)){Lr(e),li(e,n);var r=Wr(e);gr(e),Br(e,r),ui(e,r),n.finish()}}function ci(e){var t=e.gutters.offsetWidth;e.sizer.style.marginLeft=t+"px"}function ui(e,t){e.display.sizer.style.minHeight=t.docHeight+"px",e.display.heightForcer.style.top=t.docHeight+"px",e.display.gutters.style.height=t.docHeight+e.display.barHeight+Ln(e)+"px"}function fi(e){var t=e.display,n=t.view;if(t.alignWidgets||t.gutters.firstChild&&e.options.fixedGutter){for(var r=ar(t)-t.scroller.scrollLeft+e.doc.scrollLeft,i=t.gutters.offsetWidth,o=r+"px",a=0;a<n.length;a++)if(!n[a].hidden){e.options.fixedGutter&&(n[a].gutter&&(n[a].gutter.style.left=o),n[a].gutterBackground&&(n[a].gutterBackground.style.left=o));var l=n[a].alignable;if(l)for(var s=0;s<l.length;s++)l[s].style.left=o}e.options.fixedGutter&&(t.gutters.style.left=r+i+"px")}}function di(e){if(!e.options.lineNumbers)return!1;var t=e.doc,n=Je(e.options,t.first+t.size-1),r=e.display;if(n.length!=r.lineNumChars){var i=r.measure.appendChild(N("div",[N("div",n)],"CodeMirror-linenumber CodeMirror-gutter-elt")),o=i.firstChild.offsetWidth,a=i.offsetWidth-o;return r.lineGutter.style.width="",r.lineNumInnerWidth=Math.max(o,r.lineGutter.offsetWidth-a)+1,r.lineNumWidth=r.lineNumInnerWidth+a,r.lineNumChars=r.lineNumInnerWidth?n.length:-1,r.lineGutter.style.width=r.lineNumWidth+"px",ci(e.display),!0}return!1}function hi(e,t){for(var n=[],r=!1,i=0;i<e.length;i++){var o=e[i],a=null;if("string"!=typeof o&&(a=o.style,o=o.className),"CodeMirror-linenumbers"==o){if(!t)continue;r=!0}n.push({className:o,style:a})}return t&&!r&&n.push({className:"CodeMirror-linenumbers",style:null}),n}function pi(e){var t=e.gutters,n=e.gutterSpecs;M(t),e.lineGutter=null;for(var r=0;r<n.length;++r){var i=n[r],o=i.className,a=i.style,l=t.appendChild(N("div",null,"CodeMirror-gutter "+o));a&&(l.style.cssText=a),"CodeMirror-linenumbers"==o&&(e.lineGutter=l,l.style.width=(e.lineNumWidth||1)+"px")}t.style.display=n.length?"":"none",ci(e)}function mi(e){pi(e.display),fr(e),fi(e)}function gi(e,t,r,i){var o=this;this.input=r,o.scrollbarFiller=N("div",null,"CodeMirror-scrollbar-filler"),o.scrollbarFiller.setAttribute("cm-not-content","true"),o.gutterFiller=N("div",null,"CodeMirror-gutter-filler"),o.gutterFiller.setAttribute("cm-not-content","true"),o.lineDiv=A("div",null,"CodeMirror-code"),o.selectionDiv=N("div",null,null,"position: relative; z-index: 1"),o.cursorDiv=N("div",null,"CodeMirror-cursors"),o.measure=N("div",null,"CodeMirror-measure"),o.lineMeasure=N("div",null,"CodeMirror-measure"),o.lineSpace=A("div",[o.measure,o.lineMeasure,o.selectionDiv,o.cursorDiv,o.lineDiv],null,"position: relative; outline: none");var c=A("div",[o.lineSpace],"CodeMirror-lines");o.mover=N("div",[c],null,"position: relative"),o.sizer=N("div",[o.mover],"CodeMirror-sizer"),o.sizerWidth=null,o.heightForcer=N("div",null,null,"position: absolute; height: 50px; width: 1px;"),o.gutters=N("div",null,"CodeMirror-gutters"),o.lineGutter=null,o.scroller=N("div",[o.sizer,o.heightForcer,o.gutters],"CodeMirror-scroll"),o.scroller.setAttribute("tabIndex","-1"),o.wrapper=N("div",[o.scrollbarFiller,o.gutterFiller,o.scroller],"CodeMirror"),a&&l<8&&(o.gutters.style.zIndex=-1,o.scroller.style.paddingRight=0),s||n&&v||(o.scroller.draggable=!0),e&&(e.appendChild?e.appendChild(o.wrapper):e(o.wrapper)),o.viewFrom=o.viewTo=t.first,o.reportedViewFrom=o.reportedViewTo=t.first,o.view=[],o.renderedView=null,o.externalMeasured=null,o.viewOffset=0,o.lastWrapHeight=o.lastWrapWidth=0,o.updateLineNumbers=null,o.nativeBarWidth=o.barHeight=o.barWidth=0,o.scrollbarsClipped=!1,o.lineNumWidth=o.lineNumInnerWidth=o.lineNumChars=null,o.alignWidgets=!1,o.cachedCharWidth=o.cachedTextHeight=o.cachedPaddingH=null,o.maxLine=null,o.maxLineLength=0,o.maxLineChanged=!1,o.wheelDX=o.wheelDY=o.wheelStartX=o.wheelStartY=null,o.shift=!1,o.selForContextMenu=null,o.activeTouch=null,o.gutterSpecs=hi(i.gutters,i.lineNumbers),pi(o),r.init(o)}oi.prototype.signal=function(e,t){ve(e,t)&&this.events.push(arguments)},oi.prototype.finish=function(){for(var e=0;e<this.events.length;e++)pe.apply(null,this.events[e])};var vi=0,yi=null;function bi(e){var t=e.wheelDeltaX,n=e.wheelDeltaY;return null==t&&e.detail&&e.axis==e.HORIZONTAL_AXIS&&(t=e.detail),null==n&&e.detail&&e.axis==e.VERTICAL_AXIS?n=e.detail:null==n&&(n=e.wheelDelta),{x:t,y:n}}function xi(e){var t=bi(e);return t.x*=yi,t.y*=yi,t}function wi(e,t){var r=bi(t),i=r.x,o=r.y,a=e.display,l=a.scroller,c=l.scrollWidth>l.clientWidth,u=l.scrollHeight>l.clientHeight;if(i&&c||o&&u){if(o&&y&&s)e:for(var d=t.target,h=a.view;d!=l;d=d.parentNode)for(var p=0;p<h.length;p++)if(h[p].node==d){e.display.currentWheelTarget=d;break e}if(i&&!n&&!f&&null!=yi)return o&&u&&Ir(e,Math.max(0,l.scrollTop+o*yi)),_r(e,Math.max(0,l.scrollLeft+i*yi)),(!o||o&&u)&&be(t),void(a.wheelStartX=null);if(o&&null!=yi){var m=o*yi,g=e.doc.scrollTop,v=g+a.wrapper.clientHeight;m<0?g=Math.max(0,g+m-50):v=Math.min(e.doc.height,v+m+50),si(e,{top:g,bottom:v})}vi<20&&(null==a.wheelStartX?(a.wheelStartX=l.scrollLeft,a.wheelStartY=l.scrollTop,a.wheelDX=i,a.wheelDY=o,setTimeout((function(){if(null!=a.wheelStartX){var e=l.scrollLeft-a.wheelStartX,t=l.scrollTop-a.wheelStartY,n=t&&a.wheelDY&&t/a.wheelDY||e&&a.wheelDX&&e/a.wheelDX;a.wheelStartX=a.wheelStartY=null,n&&(yi=(yi*vi+n)/(vi+1),++vi)}}),200)):(a.wheelDX+=i,a.wheelDY+=o))}}a?yi=-.53:n?yi=15:u?yi=-.7:d&&(yi=-1/3);var ki=function(e,t){this.ranges=e,this.primIndex=t};ki.prototype.primary=function(){return this.ranges[this.primIndex]},ki.prototype.equals=function(e){if(e==this)return!0;if(e.primIndex!=this.primIndex||e.ranges.length!=this.ranges.length)return!1;for(var t=0;t<this.ranges.length;t++){var n=this.ranges[t],r=e.ranges[t];if(!nt(n.anchor,r.anchor)||!nt(n.head,r.head))return!1}return!0},ki.prototype.deepCopy=function(){for(var e=[],t=0;t<this.ranges.length;t++)e[t]=new Ci(rt(this.ranges[t].anchor),rt(this.ranges[t].head));return new ki(e,this.primIndex)},ki.prototype.somethingSelected=function(){for(var e=0;e<this.ranges.length;e++)if(!this.ranges[e].empty())return!0;return!1},ki.prototype.contains=function(e,t){t||(t=e);for(var n=0;n<this.ranges.length;n++){var r=this.ranges[n];if(tt(t,r.from())>=0&&tt(e,r.to())<=0)return n}return-1};var Ci=function(e,t){this.anchor=e,this.head=t};function Si(e,t,n){var r=e&&e.options.selectionsMayTouch,i=t[n];t.sort((function(e,t){return tt(e.from(),t.from())})),n=R(t,i);for(var o=1;o<t.length;o++){var a=t[o],l=t[o-1],s=tt(l.to(),a.from());if(r&&!a.empty()?s>0:s>=0){var c=ot(l.from(),a.from()),u=it(l.to(),a.to()),f=l.empty()?a.from()==a.head:l.from()==l.head;o<=n&&--n,t.splice(--o,2,new Ci(f?u:c,f?c:u))}}return new ki(t,n)}function Ti(e,t){return new ki([new Ci(e,t||e)],0)}function Li(e){return e.text?et(e.from.line+e.text.length-1,G(e.text).length+(1==e.text.length?e.from.ch:0)):e.to}function Mi(e,t){if(tt(e,t.from)<0)return e;if(tt(e,t.to)<=0)return Li(t);var n=e.line+t.text.length-(t.to.line-t.from.line)-1,r=e.ch;return e.line==t.to.line&&(r+=Li(t).ch-t.to.ch),et(n,r)}function Oi(e,t){for(var n=[],r=0;r<e.sel.ranges.length;r++){var i=e.sel.ranges[r];n.push(new Ci(Mi(i.anchor,t),Mi(i.head,t)))}return Si(e.cm,n,e.sel.primIndex)}function Ni(e,t,n){return e.line==t.line?et(n.line,e.ch-t.ch+n.ch):et(n.line+(e.line-t.line),e.ch)}function Ai(e){e.doc.mode=He(e.options,e.doc.modeOption),Ei(e)}function Ei(e){e.doc.iter((function(e){e.stateAfter&&(e.stateAfter=null),e.styles&&(e.styles=null)})),e.doc.modeFrontier=e.doc.highlightFrontier=e.doc.first,ri(e,100),e.state.modeGen++,e.curOp&&fr(e)}function zi(e,t){return 0==t.from.ch&&0==t.to.ch&&""==G(t.text)&&(!e.cm||e.cm.options.wholeLineUpdateBefore)}function Pi(e,t,n,r){function i(e){return n?n[e]:null}function o(e,n,i){!function(e,t,n,r){e.text=t,e.stateAfter&&(e.stateAfter=null),e.styles&&(e.styles=null),null!=e.order&&(e.order=null),Ot(e),Nt(e,n);var i=r?r(e):1;i!=e.height&&Xe(e,i)}(e,n,i,r),sn(e,"change",e,t)}function a(e,t){for(var n=[],o=e;o<t;++o)n.push(new Kt(c[o],i(o),r));return n}var l=t.from,s=t.to,c=t.text,u=Ke(e,l.line),f=Ke(e,s.line),d=G(c),h=i(c.length-1),p=s.line-l.line;if(t.full)e.insert(0,a(0,c.length)),e.remove(c.length,e.size-c.length);else if(zi(e,t)){var m=a(0,c.length-1);o(f,f.text,h),p&&e.remove(l.line,p),m.length&&e.insert(l.line,m)}else if(u==f)if(1==c.length)o(u,u.text.slice(0,l.ch)+d+u.text.slice(s.ch),h);else{var g=a(1,c.length-1);g.push(new Kt(d+u.text.slice(s.ch),h,r)),o(u,u.text.slice(0,l.ch)+c[0],i(0)),e.insert(l.line+1,g)}else if(1==c.length)o(u,u.text.slice(0,l.ch)+c[0]+f.text.slice(s.ch),i(0)),e.remove(l.line+1,p);else{o(u,u.text.slice(0,l.ch)+c[0],i(0)),o(f,d+f.text.slice(s.ch),h);var v=a(1,c.length-1);p>1&&e.remove(l.line+1,p-1),e.insert(l.line+1,v)}sn(e,"change",e,t)}function Di(e,t,n){!function e(r,i,o){if(r.linked)for(var a=0;a<r.linked.length;++a){var l=r.linked[a];if(l.doc!=i){var s=o&&l.sharedHist;n&&!s||(t(l.doc,s),e(l.doc,r,s))}}}(e,null,!0)}function Ii(e,t){if(t.cm)throw new Error("This document is already in use.");e.doc=t,t.cm=e,sr(e),Ai(e),Fi(e),e.options.lineWrapping||Vt(e),e.options.mode=t.modeOption,fr(e)}function Fi(e){("rtl"==e.doc.direction?P:L)(e.display.lineDiv,"CodeMirror-rtl")}function _i(e){this.done=[],this.undone=[],this.undoDepth=1/0,this.lastModTime=this.lastSelTime=0,this.lastOp=this.lastSelOp=null,this.lastOrigin=this.lastSelOrigin=null,this.generation=this.maxGeneration=e||1}function Wi(e,t){var n={from:rt(t.from),to:Li(t),text:$e(e,t.from,t.to)};return qi(e,n,t.from.line,t.to.line+1),Di(e,(function(e){return qi(e,n,t.from.line,t.to.line+1)}),!0),n}function Hi(e){for(;e.length&&G(e).ranges;)e.pop()}function Ri(e,t,n,r){var i=e.history;i.undone.length=0;var o,a,l=+new Date;if((i.lastOp==r||i.lastOrigin==t.origin&&t.origin&&("+"==t.origin.charAt(0)&&i.lastModTime>l-(e.cm?e.cm.options.historyEventDelay:500)||"*"==t.origin.charAt(0)))&&(o=function(e,t){return t?(Hi(e.done),G(e.done)):e.done.length&&!G(e.done).ranges?G(e.done):e.done.length>1&&!e.done[e.done.length-2].ranges?(e.done.pop(),G(e.done)):void 0}(i,i.lastOp==r)))a=G(o.changes),0==tt(t.from,t.to)&&0==tt(t.from,a.to)?a.to=Li(t):o.changes.push(Wi(e,t));else{var s=G(i.done);for(s&&s.ranges||ji(e.sel,i.done),o={changes:[Wi(e,t)],generation:i.generation},i.done.push(o);i.done.length>i.undoDepth;)i.done.shift(),i.done[0].ranges||i.done.shift()}i.done.push(n),i.generation=++i.maxGeneration,i.lastModTime=i.lastSelTime=l,i.lastOp=i.lastSelOp=r,i.lastOrigin=i.lastSelOrigin=t.origin,a||pe(e,"historyAdded")}function Bi(e,t,n,r){var i=e.history,o=r&&r.origin;n==i.lastSelOp||o&&i.lastSelOrigin==o&&(i.lastModTime==i.lastSelTime&&i.lastOrigin==o||function(e,t,n,r){var i=t.charAt(0);return"*"==i||"+"==i&&n.ranges.length==r.ranges.length&&n.somethingSelected()==r.somethingSelected()&&new Date-e.history.lastSelTime<=(e.cm?e.cm.options.historyEventDelay:500)}(e,o,G(i.done),t))?i.done[i.done.length-1]=t:ji(t,i.done),i.lastSelTime=+new Date,i.lastSelOrigin=o,i.lastSelOp=n,r&&!1!==r.clearRedo&&Hi(i.undone)}function ji(e,t){var n=G(t);n&&n.ranges&&n.equals(e)||t.push(e)}function qi(e,t,n,r){var i=t["spans_"+e.id],o=0;e.iter(Math.max(e.first,n),Math.min(e.first+e.size,r),(function(n){n.markedSpans&&((i||(i=t["spans_"+e.id]={}))[o]=n.markedSpans),++o}))}function Ui(e){if(!e)return null;for(var t,n=0;n<e.length;++n)e[n].marker.explicitlyCleared?t||(t=e.slice(0,n)):t&&t.push(e[n]);return t?t.length?t:null:e}function Vi(e,t){var n=function(e,t){var n=t["spans_"+e.id];if(!n)return null;for(var r=[],i=0;i<t.text.length;++i)r.push(Ui(n[i]));return r}(e,t),r=Lt(e,t);if(!n)return r;if(!r)return n;for(var i=0;i<n.length;++i){var o=n[i],a=r[i];if(o&&a)e:for(var l=0;l<a.length;++l){for(var s=a[l],c=0;c<o.length;++c)if(o[c].marker==s.marker)continue e;o.push(s)}else a&&(n[i]=a)}return n}function Ki(e,t,n){for(var r=[],i=0;i<e.length;++i){var o=e[i];if(o.ranges)r.push(n?ki.prototype.deepCopy.call(o):o);else{var a=o.changes,l=[];r.push({changes:l});for(var s=0;s<a.length;++s){var c=a[s],u=void 0;if(l.push({from:c.from,to:c.to,text:c.text}),t)for(var f in c)(u=f.match(/^spans_(\d+)$/))&&R(t,Number(u[1]))>-1&&(G(l)[f]=c[f],delete c[f])}}}return r}function $i(e,t,n,r){if(r){var i=e.anchor;if(n){var o=tt(t,i)<0;o!=tt(n,i)<0?(i=t,t=n):o!=tt(t,n)<0&&(t=n)}return new Ci(i,t)}return new Ci(n||t,t)}function Gi(e,t,n,r,i){null==i&&(i=e.cm&&(e.cm.display.shift||e.extend)),Ji(e,new ki([$i(e.sel.primary(),t,n,i)],0),r)}function Xi(e,t,n){for(var r=[],i=e.cm&&(e.cm.display.shift||e.extend),o=0;o<e.sel.ranges.length;o++)r[o]=$i(e.sel.ranges[o],t[o],null,i);Ji(e,Si(e.cm,r,e.sel.primIndex),n)}function Zi(e,t,n,r){var i=e.sel.ranges.slice(0);i[t]=n,Ji(e,Si(e.cm,i,e.sel.primIndex),r)}function Yi(e,t,n,r){Ji(e,Ti(t,n),r)}function Qi(e,t,n){var r=e.history.done,i=G(r);i&&i.ranges?(r[r.length-1]=t,eo(e,t,n)):Ji(e,t,n)}function Ji(e,t,n){eo(e,t,n),Bi(e,e.sel,e.cm?e.cm.curOp.id:NaN,n)}function eo(e,t,n){(ve(e,"beforeSelectionChange")||e.cm&&ve(e.cm,"beforeSelectionChange"))&&(t=function(e,t,n){var r={ranges:t.ranges,update:function(t){this.ranges=[];for(var n=0;n<t.length;n++)this.ranges[n]=new Ci(lt(e,t[n].anchor),lt(e,t[n].head))},origin:n&&n.origin};return pe(e,"beforeSelectionChange",e,r),e.cm&&pe(e.cm,"beforeSelectionChange",e.cm,r),r.ranges!=t.ranges?Si(e.cm,r.ranges,r.ranges.length-1):t}(e,t,n));var r=n&&n.bias||(tt(t.primary().head,e.sel.primary().head)<0?-1:1);to(e,ro(e,t,r,!0)),n&&!1===n.scroll||!e.cm||Er(e.cm)}function to(e,t){t.equals(e.sel)||(e.sel=t,e.cm&&(e.cm.curOp.updateInput=1,e.cm.curOp.selectionChanged=!0,ge(e.cm)),sn(e,"cursorActivity",e))}function no(e){to(e,ro(e,e.sel,null,!1))}function ro(e,t,n,r){for(var i,o=0;o<t.ranges.length;o++){var a=t.ranges[o],l=t.ranges.length==e.sel.ranges.length&&e.sel.ranges[o],s=oo(e,a.anchor,l&&l.anchor,n,r),c=oo(e,a.head,l&&l.head,n,r);(i||s!=a.anchor||c!=a.head)&&(i||(i=t.ranges.slice(0,o)),i[o]=new Ci(s,c))}return i?Si(e.cm,i,t.primIndex):t}function io(e,t,n,r,i){var o=Ke(e,t.line);if(o.markedSpans)for(var a=0;a<o.markedSpans.length;++a){var l=o.markedSpans[a],s=l.marker,c="selectLeft"in s?!s.selectLeft:s.inclusiveLeft,u="selectRight"in s?!s.selectRight:s.inclusiveRight;if((null==l.from||(c?l.from<=t.ch:l.from<t.ch))&&(null==l.to||(u?l.to>=t.ch:l.to>t.ch))){if(i&&(pe(s,"beforeCursorEnter"),s.explicitlyCleared)){if(o.markedSpans){--a;continue}break}if(!s.atomic)continue;if(n){var f=s.find(r<0?1:-1),d=void 0;if((r<0?u:c)&&(f=ao(e,f,-r,f&&f.line==t.line?o:null)),f&&f.line==t.line&&(d=tt(f,n))&&(r<0?d<0:d>0))return io(e,f,t,r,i)}var h=s.find(r<0?-1:1);return(r<0?c:u)&&(h=ao(e,h,r,h.line==t.line?o:null)),h?io(e,h,t,r,i):null}}return t}function oo(e,t,n,r,i){var o=r||1,a=io(e,t,n,o,i)||!i&&io(e,t,n,o,!0)||io(e,t,n,-o,i)||!i&&io(e,t,n,-o,!0);return a||(e.cantEdit=!0,et(e.first,0))}function ao(e,t,n,r){return n<0&&0==t.ch?t.line>e.first?lt(e,et(t.line-1)):null:n>0&&t.ch==(r||Ke(e,t.line)).text.length?t.line<e.first+e.size-1?et(t.line+1,0):null:new et(t.line,t.ch+n)}function lo(e){e.setSelection(et(e.firstLine(),0),et(e.lastLine()),j)}function so(e,t,n){var r={canceled:!1,from:t.from,to:t.to,text:t.text,origin:t.origin,cancel:function(){return r.canceled=!0}};return n&&(r.update=function(t,n,i,o){t&&(r.from=lt(e,t)),n&&(r.to=lt(e,n)),i&&(r.text=i),void 0!==o&&(r.origin=o)}),pe(e,"beforeChange",e,r),e.cm&&pe(e.cm,"beforeChange",e.cm,r),r.canceled?(e.cm&&(e.cm.curOp.updateInput=2),null):{from:r.from,to:r.to,text:r.text,origin:r.origin}}function co(e,t,n){if(e.cm){if(!e.cm.curOp)return ei(e.cm,co)(e,t,n);if(e.cm.state.suppressEdits)return}if(!(ve(e,"beforeChange")||e.cm&&ve(e.cm,"beforeChange"))||(t=so(e,t,!0))){var r=wt&&!n&&function(e,t,n){var r=null;if(e.iter(t.line,n.line+1,(function(e){if(e.markedSpans)for(var t=0;t<e.markedSpans.length;++t){var n=e.markedSpans[t].marker;!n.readOnly||r&&-1!=R(r,n)||(r||(r=[])).push(n)}})),!r)return null;for(var i=[{from:t,to:n}],o=0;o<r.length;++o)for(var a=r[o],l=a.find(0),s=0;s<i.length;++s){var c=i[s];if(!(tt(c.to,l.from)<0||tt(c.from,l.to)>0)){var u=[s,1],f=tt(c.from,l.from),d=tt(c.to,l.to);(f<0||!a.inclusiveLeft&&!f)&&u.push({from:c.from,to:l.from}),(d>0||!a.inclusiveRight&&!d)&&u.push({from:l.to,to:c.to}),i.splice.apply(i,u),s+=u.length-3}}return i}(e,t.from,t.to);if(r)for(var i=r.length-1;i>=0;--i)uo(e,{from:r[i].from,to:r[i].to,text:i?[""]:t.text,origin:t.origin});else uo(e,t)}}function uo(e,t){if(1!=t.text.length||""!=t.text[0]||0!=tt(t.from,t.to)){var n=Oi(e,t);Ri(e,t,n,e.cm?e.cm.curOp.id:NaN),po(e,t,n,Lt(e,t));var r=[];Di(e,(function(e,n){n||-1!=R(r,e.history)||(yo(e.history,t),r.push(e.history)),po(e,t,null,Lt(e,t))}))}}function fo(e,t,n){var r=e.cm&&e.cm.state.suppressEdits;if(!r||n){for(var i,o=e.history,a=e.sel,l="undo"==t?o.done:o.undone,s="undo"==t?o.undone:o.done,c=0;c<l.length&&(i=l[c],n?!i.ranges||i.equals(e.sel):i.ranges);c++);if(c!=l.length){for(o.lastOrigin=o.lastSelOrigin=null;;){if(!(i=l.pop()).ranges){if(r)return void l.push(i);break}if(ji(i,s),n&&!i.equals(e.sel))return void Ji(e,i,{clearRedo:!1});a=i}var u=[];ji(a,s),s.push({changes:u,generation:o.generation}),o.generation=i.generation||++o.maxGeneration;for(var f=ve(e,"beforeChange")||e.cm&&ve(e.cm,"beforeChange"),d=function(n){var r=i.changes[n];if(r.origin=t,f&&!so(e,r,!1))return l.length=0,{};u.push(Wi(e,r));var o=n?Oi(e,r):G(l);po(e,r,o,Vi(e,r)),!n&&e.cm&&e.cm.scrollIntoView({from:r.from,to:Li(r)});var a=[];Di(e,(function(e,t){t||-1!=R(a,e.history)||(yo(e.history,r),a.push(e.history)),po(e,r,null,Vi(e,r))}))},h=i.changes.length-1;h>=0;--h){var p=d(h);if(p)return p.v}}}}function ho(e,t){if(0!=t&&(e.first+=t,e.sel=new ki(X(e.sel.ranges,(function(e){return new Ci(et(e.anchor.line+t,e.anchor.ch),et(e.head.line+t,e.head.ch))})),e.sel.primIndex),e.cm)){fr(e.cm,e.first,e.first-t,t);for(var n=e.cm.display,r=n.viewFrom;r<n.viewTo;r++)dr(e.cm,r,"gutter")}}function po(e,t,n,r){if(e.cm&&!e.cm.curOp)return ei(e.cm,po)(e,t,n,r);if(t.to.line<e.first)ho(e,t.text.length-1-(t.to.line-t.from.line));else if(!(t.from.line>e.lastLine())){if(t.from.line<e.first){var i=t.text.length-1-(e.first-t.from.line);ho(e,i),t={from:et(e.first,0),to:et(t.to.line+i,t.to.ch),text:[G(t.text)],origin:t.origin}}var o=e.lastLine();t.to.line>o&&(t={from:t.from,to:et(o,Ke(e,o).text.length),text:[t.text[0]],origin:t.origin}),t.removed=$e(e,t.from,t.to),n||(n=Oi(e,t)),e.cm?function(e,t,n){var r=e.doc,i=e.display,o=t.from,a=t.to,l=!1,s=o.line;e.options.lineWrapping||(s=Ze(Wt(Ke(r,o.line))),r.iter(s,a.line+1,(function(e){if(e==i.maxLine)return l=!0,!0}))),r.sel.contains(t.from,t.to)>-1&&ge(e),Pi(r,t,n,lr(e)),e.options.lineWrapping||(r.iter(s,o.line+t.text.length,(function(e){var t=Ut(e);t>i.maxLineLength&&(i.maxLine=e,i.maxLineLength=t,i.maxLineChanged=!0,l=!1)})),l&&(e.curOp.updateMaxLine=!0)),function(e,t){if(e.modeFrontier=Math.min(e.modeFrontier,t),!(e.highlightFrontier<t-10)){for(var n=e.first,r=t-1;r>n;r--){var i=Ke(e,r).stateAfter;if(i&&(!(i instanceof ct)||r+i.lookAhead<t)){n=r+1;break}}e.highlightFrontier=Math.min(e.highlightFrontier,n)}}(r,o.line),ri(e,400);var c=t.text.length-(a.line-o.line)-1;t.full?fr(e):o.line!=a.line||1!=t.text.length||zi(e.doc,t)?fr(e,o.line,a.line+1,c):dr(e,o.line,"text");var u=ve(e,"changes"),f=ve(e,"change");if(f||u){var d={from:o,to:a,text:t.text,removed:t.removed,origin:t.origin};f&&sn(e,"change",e,d),u&&(e.curOp.changeObjs||(e.curOp.changeObjs=[])).push(d)}e.display.selForContextMenu=null}(e.cm,t,r):Pi(e,t,r),eo(e,n,j),e.cantEdit&&oo(e,et(e.firstLine(),0))&&(e.cantEdit=!1)}}function mo(e,t,n,r,i){var o;r||(r=n),tt(r,n)<0&&(n=(o=[r,n])[0],r=o[1]),"string"==typeof t&&(t=e.splitLines(t)),co(e,{from:n,to:r,text:t,origin:i})}function go(e,t,n,r){n<e.line?e.line+=r:t<e.line&&(e.line=t,e.ch=0)}function vo(e,t,n,r){for(var i=0;i<e.length;++i){var o=e[i],a=!0;if(o.ranges){o.copied||((o=e[i]=o.deepCopy()).copied=!0);for(var l=0;l<o.ranges.length;l++)go(o.ranges[l].anchor,t,n,r),go(o.ranges[l].head,t,n,r)}else{for(var s=0;s<o.changes.length;++s){var c=o.changes[s];if(n<c.from.line)c.from=et(c.from.line+r,c.from.ch),c.to=et(c.to.line+r,c.to.ch);else if(t<=c.to.line){a=!1;break}}a||(e.splice(0,i+1),i=0)}}}function yo(e,t){var n=t.from.line,r=t.to.line,i=t.text.length-(r-n)-1;vo(e.done,n,r,i),vo(e.undone,n,r,i)}function bo(e,t,n,r){var i=t,o=t;return"number"==typeof t?o=Ke(e,at(e,t)):i=Ze(t),null==i?null:(r(o,i)&&e.cm&&dr(e.cm,i,n),o)}function xo(e){this.lines=e,this.parent=null;for(var t=0,n=0;n<e.length;++n)e[n].parent=this,t+=e[n].height;this.height=t}function wo(e){this.children=e;for(var t=0,n=0,r=0;r<e.length;++r){var i=e[r];t+=i.chunkSize(),n+=i.height,i.parent=this}this.size=t,this.height=n,this.parent=null}Ci.prototype.from=function(){return ot(this.anchor,this.head)},Ci.prototype.to=function(){return it(this.anchor,this.head)},Ci.prototype.empty=function(){return this.head.line==this.anchor.line&&this.head.ch==this.anchor.ch},xo.prototype={chunkSize:function(){return this.lines.length},removeInner:function(e,t){for(var n=e,r=e+t;n<r;++n){var i=this.lines[n];this.height-=i.height,$t(i),sn(i,"delete")}this.lines.splice(e,t)},collapse:function(e){e.push.apply(e,this.lines)},insertInner:function(e,t,n){this.height+=n,this.lines=this.lines.slice(0,e).concat(t).concat(this.lines.slice(e));for(var r=0;r<t.length;++r)t[r].parent=this},iterN:function(e,t,n){for(var r=e+t;e<r;++e)if(n(this.lines[e]))return!0}},wo.prototype={chunkSize:function(){return this.size},removeInner:function(e,t){this.size-=t;for(var n=0;n<this.children.length;++n){var r=this.children[n],i=r.chunkSize();if(e<i){var o=Math.min(t,i-e),a=r.height;if(r.removeInner(e,o),this.height-=a-r.height,i==o&&(this.children.splice(n--,1),r.parent=null),0==(t-=o))break;e=0}else e-=i}if(this.size-t<25&&(this.children.length>1||!(this.children[0]instanceof xo))){var l=[];this.collapse(l),this.children=[new xo(l)],this.children[0].parent=this}},collapse:function(e){for(var t=0;t<this.children.length;++t)this.children[t].collapse(e)},insertInner:function(e,t,n){this.size+=t.length,this.height+=n;for(var r=0;r<this.children.length;++r){var i=this.children[r],o=i.chunkSize();if(e<=o){if(i.insertInner(e,t,n),i.lines&&i.lines.length>50){for(var a=i.lines.length%25+25,l=a;l<i.lines.length;){var s=new xo(i.lines.slice(l,l+=25));i.height-=s.height,this.children.splice(++r,0,s),s.parent=this}i.lines=i.lines.slice(0,a),this.maybeSpill()}break}e-=o}},maybeSpill:function(){if(!(this.children.length<=10)){var e=this;do{var t=new wo(e.children.splice(e.children.length-5,5));if(e.parent){e.size-=t.size,e.height-=t.height;var n=R(e.parent.children,e);e.parent.children.splice(n+1,0,t)}else{var r=new wo(e.children);r.parent=e,e.children=[r,t],e=r}t.parent=e.parent}while(e.children.length>10);e.parent.maybeSpill()}},iterN:function(e,t,n){for(var r=0;r<this.children.length;++r){var i=this.children[r],o=i.chunkSize();if(e<o){var a=Math.min(t,o-e);if(i.iterN(e,a,n))return!0;if(0==(t-=a))break;e=0}else e-=o}}};var ko=function(e,t,n){if(n)for(var r in n)n.hasOwnProperty(r)&&(this[r]=n[r]);this.doc=e,this.node=t};function Co(e,t,n){qt(t)<(e.curOp&&e.curOp.scrollTop||e.doc.scrollTop)&&Ar(e,n)}ko.prototype.clear=function(){var e=this.doc.cm,t=this.line.widgets,n=this.line,r=Ze(n);if(null!=r&&t){for(var i=0;i<t.length;++i)t[i]==this&&t.splice(i--,1);t.length||(n.widgets=null);var o=wn(this);Xe(n,Math.max(0,n.height-o)),e&&(Jr(e,(function(){Co(e,n,-o),dr(e,r,"widget")})),sn(e,"lineWidgetCleared",e,this,r))}},ko.prototype.changed=function(){var e=this,t=this.height,n=this.doc.cm,r=this.line;this.height=null;var i=wn(this)-t;i&&(Bt(this.doc,r)||Xe(r,r.height+i),n&&Jr(n,(function(){n.curOp.forceUpdate=!0,Co(n,r,i),sn(n,"lineWidgetChanged",n,e,Ze(r))})))},ye(ko);var So=0,To=function(e,t){this.lines=[],this.type=t,this.doc=e,this.id=++So};function Lo(e,t,n,r,i){if(r&&r.shared)return function(e,t,n,r,i){(r=_(r)).shared=!1;var o=[Lo(e,t,n,r,i)],a=o[0],l=r.widgetNode;return Di(e,(function(e){l&&(r.widgetNode=l.cloneNode(!0)),o.push(Lo(e,lt(e,t),lt(e,n),r,i));for(var s=0;s<e.linked.length;++s)if(e.linked[s].isParent)return;a=G(o)})),new Mo(o,a)}(e,t,n,r,i);if(e.cm&&!e.cm.curOp)return ei(e.cm,Lo)(e,t,n,r,i);var o=new To(e,i),a=tt(t,n);if(r&&_(r,o,!1),a>0||0==a&&!1!==o.clearWhenEmpty)return o;if(o.replacedWith&&(o.collapsed=!0,o.widgetNode=A("span",[o.replacedWith],"CodeMirror-widget"),r.handleMouseEvents||o.widgetNode.setAttribute("cm-ignore-events","true"),r.insertLeft&&(o.widgetNode.insertLeft=!0)),o.collapsed){if(_t(e,t.line,t,n,o)||t.line!=n.line&&_t(e,n.line,t,n,o))throw new Error("Inserting collapsed marker partially overlapping an existing one");kt=!0}o.addToHistory&&Ri(e,{from:t,to:n,origin:"markText"},e.sel,NaN);var l,s=t.line,c=e.cm;if(e.iter(s,n.line+1,(function(e){c&&o.collapsed&&!c.options.lineWrapping&&Wt(e)==c.display.maxLine&&(l=!0),o.collapsed&&s!=t.line&&Xe(e,0),function(e,t){e.markedSpans=e.markedSpans?e.markedSpans.concat([t]):[t],t.marker.attachLine(e)}(e,new Ct(o,s==t.line?t.ch:null,s==n.line?n.ch:null)),++s})),o.collapsed&&e.iter(t.line,n.line+1,(function(t){Bt(e,t)&&Xe(t,0)})),o.clearOnEnter&&fe(o,"beforeCursorEnter",(function(){return o.clear()})),o.readOnly&&(wt=!0,(e.history.done.length||e.history.undone.length)&&e.clearHistory()),o.collapsed&&(o.id=++So,o.atomic=!0),c){if(l&&(c.curOp.updateMaxLine=!0),o.collapsed)fr(c,t.line,n.line+1);else if(o.className||o.startStyle||o.endStyle||o.css||o.attributes||o.title)for(var u=t.line;u<=n.line;u++)dr(c,u,"text");o.atomic&&no(c.doc),sn(c,"markerAdded",c,o)}return o}To.prototype.clear=function(){if(!this.explicitlyCleared){var e=this.doc.cm,t=e&&!e.curOp;if(t&&Kr(e),ve(this,"clear")){var n=this.find();n&&sn(this,"clear",n.from,n.to)}for(var r=null,i=null,o=0;o<this.lines.length;++o){var a=this.lines[o],l=St(a.markedSpans,this);e&&!this.collapsed?dr(e,Ze(a),"text"):e&&(null!=l.to&&(i=Ze(a)),null!=l.from&&(r=Ze(a))),a.markedSpans=Tt(a.markedSpans,l),null==l.from&&this.collapsed&&!Bt(this.doc,a)&&e&&Xe(a,rr(e.display))}if(e&&this.collapsed&&!e.options.lineWrapping)for(var s=0;s<this.lines.length;++s){var c=Wt(this.lines[s]),u=Ut(c);u>e.display.maxLineLength&&(e.display.maxLine=c,e.display.maxLineLength=u,e.display.maxLineChanged=!0)}null!=r&&e&&this.collapsed&&fr(e,r,i+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,e&&no(e.doc)),e&&sn(e,"markerCleared",e,this,r,i),t&&$r(e),this.parent&&this.parent.clear()}},To.prototype.find=function(e,t){var n,r;null==e&&"bookmark"==this.type&&(e=1);for(var i=0;i<this.lines.length;++i){var o=this.lines[i],a=St(o.markedSpans,this);if(null!=a.from&&(n=et(t?o:Ze(o),a.from),-1==e))return n;if(null!=a.to&&(r=et(t?o:Ze(o),a.to),1==e))return r}return n&&{from:n,to:r}},To.prototype.changed=function(){var e=this,t=this.find(-1,!0),n=this,r=this.doc.cm;t&&r&&Jr(r,(function(){var i=t.line,o=Ze(t.line),a=En(r,o);if(a&&(Wn(a),r.curOp.selectionChanged=r.curOp.forceUpdate=!0),r.curOp.updateMaxLine=!0,!Bt(n.doc,i)&&null!=n.height){var l=n.height;n.height=null;var s=wn(n)-l;s&&Xe(i,i.height+s)}sn(r,"markerChanged",r,e)}))},To.prototype.attachLine=function(e){if(!this.lines.length&&this.doc.cm){var t=this.doc.cm.curOp;t.maybeHiddenMarkers&&-1!=R(t.maybeHiddenMarkers,this)||(t.maybeUnhiddenMarkers||(t.maybeUnhiddenMarkers=[])).push(this)}this.lines.push(e)},To.prototype.detachLine=function(e){if(this.lines.splice(R(this.lines,e),1),!this.lines.length&&this.doc.cm){var t=this.doc.cm.curOp;(t.maybeHiddenMarkers||(t.maybeHiddenMarkers=[])).push(this)}},ye(To);var Mo=function(e,t){this.markers=e,this.primary=t;for(var n=0;n<e.length;++n)e[n].parent=this};function Oo(e){return e.findMarks(et(e.first,0),e.clipPos(et(e.lastLine())),(function(e){return e.parent}))}function No(e){for(var t=function(t){var n=e[t],r=[n.primary.doc];Di(n.primary.doc,(function(e){return r.push(e)}));for(var i=0;i<n.markers.length;i++){var o=n.markers[i];-1==R(r,o.doc)&&(o.parent=null,n.markers.splice(i--,1))}},n=0;n<e.length;n++)t(n)}Mo.prototype.clear=function(){if(!this.explicitlyCleared){this.explicitlyCleared=!0;for(var e=0;e<this.markers.length;++e)this.markers[e].clear();sn(this,"clear")}},Mo.prototype.find=function(e,t){return this.primary.find(e,t)},ye(Mo);var Ao=0,Eo=function(e,t,n,r,i){if(!(this instanceof Eo))return new Eo(e,t,n,r,i);null==n&&(n=0),wo.call(this,[new xo([new Kt("",null)])]),this.first=n,this.scrollTop=this.scrollLeft=0,this.cantEdit=!1,this.cleanGeneration=1,this.modeFrontier=this.highlightFrontier=n;var o=et(n,0);this.sel=Ti(o),this.history=new _i(null),this.id=++Ao,this.modeOption=t,this.lineSep=r,this.direction="rtl"==i?"rtl":"ltr",this.extend=!1,"string"==typeof e&&(e=this.splitLines(e)),Pi(this,{from:o,to:o,text:e}),Ji(this,Ti(o),j)};Eo.prototype=Y(wo.prototype,{constructor:Eo,iter:function(e,t,n){n?this.iterN(e-this.first,t-e,n):this.iterN(this.first,this.first+this.size,e)},insert:function(e,t){for(var n=0,r=0;r<t.length;++r)n+=t[r].height;this.insertInner(e-this.first,t,n)},remove:function(e,t){this.removeInner(e-this.first,t)},getValue:function(e){var t=Ge(this,this.first,this.first+this.size);return!1===e?t:t.join(e||this.lineSeparator())},setValue:ni((function(e){var t=et(this.first,0),n=this.first+this.size-1;co(this,{from:t,to:et(n,Ke(this,n).text.length),text:this.splitLines(e),origin:"setValue",full:!0},!0),this.cm&&zr(this.cm,0,0),Ji(this,Ti(t),j)})),replaceRange:function(e,t,n,r){mo(this,e,t=lt(this,t),n=n?lt(this,n):t,r)},getRange:function(e,t,n){var r=$e(this,lt(this,e),lt(this,t));return!1===n?r:r.join(n||this.lineSeparator())},getLine:function(e){var t=this.getLineHandle(e);return t&&t.text},getLineHandle:function(e){if(Qe(this,e))return Ke(this,e)},getLineNumber:function(e){return Ze(e)},getLineHandleVisualStart:function(e){return"number"==typeof e&&(e=Ke(this,e)),Wt(e)},lineCount:function(){return this.size},firstLine:function(){return this.first},lastLine:function(){return this.first+this.size-1},clipPos:function(e){return lt(this,e)},getCursor:function(e){var t=this.sel.primary();return null==e||"head"==e?t.head:"anchor"==e?t.anchor:"end"==e||"to"==e||!1===e?t.to():t.from()},listSelections:function(){return this.sel.ranges},somethingSelected:function(){return this.sel.somethingSelected()},setCursor:ni((function(e,t,n){Yi(this,lt(this,"number"==typeof e?et(e,t||0):e),null,n)})),setSelection:ni((function(e,t,n){Yi(this,lt(this,e),lt(this,t||e),n)})),extendSelection:ni((function(e,t,n){Gi(this,lt(this,e),t&&lt(this,t),n)})),extendSelections:ni((function(e,t){Xi(this,st(this,e),t)})),extendSelectionsBy:ni((function(e,t){Xi(this,st(this,X(this.sel.ranges,e)),t)})),setSelections:ni((function(e,t,n){if(e.length){for(var r=[],i=0;i<e.length;i++)r[i]=new Ci(lt(this,e[i].anchor),lt(this,e[i].head));null==t&&(t=Math.min(e.length-1,this.sel.primIndex)),Ji(this,Si(this.cm,r,t),n)}})),addSelection:ni((function(e,t,n){var r=this.sel.ranges.slice(0);r.push(new Ci(lt(this,e),lt(this,t||e))),Ji(this,Si(this.cm,r,r.length-1),n)})),getSelection:function(e){for(var t,n=this.sel.ranges,r=0;r<n.length;r++){var i=$e(this,n[r].from(),n[r].to());t=t?t.concat(i):i}return!1===e?t:t.join(e||this.lineSeparator())},getSelections:function(e){for(var t=[],n=this.sel.ranges,r=0;r<n.length;r++){var i=$e(this,n[r].from(),n[r].to());!1!==e&&(i=i.join(e||this.lineSeparator())),t[r]=i}return t},replaceSelection:function(e,t,n){for(var r=[],i=0;i<this.sel.ranges.length;i++)r[i]=e;this.replaceSelections(r,t,n||"+input")},replaceSelections:ni((function(e,t,n){for(var r=[],i=this.sel,o=0;o<i.ranges.length;o++){var a=i.ranges[o];r[o]={from:a.from(),to:a.to(),text:this.splitLines(e[o]),origin:n}}for(var l=t&&"end"!=t&&function(e,t,n){for(var r=[],i=et(e.first,0),o=i,a=0;a<t.length;a++){var l=t[a],s=Ni(l.from,i,o),c=Ni(Li(l),i,o);if(i=l.to,o=c,"around"==n){var u=e.sel.ranges[a],f=tt(u.head,u.anchor)<0;r[a]=new Ci(f?c:s,f?s:c)}else r[a]=new Ci(s,s)}return new ki(r,e.sel.primIndex)}(this,r,t),s=r.length-1;s>=0;s--)co(this,r[s]);l?Qi(this,l):this.cm&&Er(this.cm)})),undo:ni((function(){fo(this,"undo")})),redo:ni((function(){fo(this,"redo")})),undoSelection:ni((function(){fo(this,"undo",!0)})),redoSelection:ni((function(){fo(this,"redo",!0)})),setExtending:function(e){this.extend=e},getExtending:function(){return this.extend},historySize:function(){for(var e=this.history,t=0,n=0,r=0;r<e.done.length;r++)e.done[r].ranges||++t;for(var i=0;i<e.undone.length;i++)e.undone[i].ranges||++n;return{undo:t,redo:n}},clearHistory:function(){var e=this;this.history=new _i(this.history.maxGeneration),Di(this,(function(t){return t.history=e.history}),!0)},markClean:function(){this.cleanGeneration=this.changeGeneration(!0)},changeGeneration:function(e){return e&&(this.history.lastOp=this.history.lastSelOp=this.history.lastOrigin=null),this.history.generation},isClean:function(e){return this.history.generation==(e||this.cleanGeneration)},getHistory:function(){return{done:Ki(this.history.done),undone:Ki(this.history.undone)}},setHistory:function(e){var t=this.history=new _i(this.history.maxGeneration);t.done=Ki(e.done.slice(0),null,!0),t.undone=Ki(e.undone.slice(0),null,!0)},setGutterMarker:ni((function(e,t,n){return bo(this,e,"gutter",(function(e){var r=e.gutterMarkers||(e.gutterMarkers={});return r[t]=n,!n&&te(r)&&(e.gutterMarkers=null),!0}))})),clearGutter:ni((function(e){var t=this;this.iter((function(n){n.gutterMarkers&&n.gutterMarkers[e]&&bo(t,n,"gutter",(function(){return n.gutterMarkers[e]=null,te(n.gutterMarkers)&&(n.gutterMarkers=null),!0}))}))})),lineInfo:function(e){var t;if("number"==typeof e){if(!Qe(this,e))return null;if(t=e,!(e=Ke(this,e)))return null}else if(null==(t=Ze(e)))return null;return{line:t,handle:e,text:e.text,gutterMarkers:e.gutterMarkers,textClass:e.textClass,bgClass:e.bgClass,wrapClass:e.wrapClass,widgets:e.widgets}},addLineClass:ni((function(e,t,n){return bo(this,e,"gutter"==t?"gutter":"class",(function(e){var r="text"==t?"textClass":"background"==t?"bgClass":"gutter"==t?"gutterClass":"wrapClass";if(e[r]){if(S(n).test(e[r]))return!1;e[r]+=" "+n}else e[r]=n;return!0}))})),removeLineClass:ni((function(e,t,n){return bo(this,e,"gutter"==t?"gutter":"class",(function(e){var r="text"==t?"textClass":"background"==t?"bgClass":"gutter"==t?"gutterClass":"wrapClass",i=e[r];if(!i)return!1;if(null==n)e[r]=null;else{var o=i.match(S(n));if(!o)return!1;var a=o.index+o[0].length;e[r]=i.slice(0,o.index)+(o.index&&a!=i.length?" ":"")+i.slice(a)||null}return!0}))})),addLineWidget:ni((function(e,t,n){return function(e,t,n,r){var i=new ko(e,n,r),o=e.cm;return o&&i.noHScroll&&(o.display.alignWidgets=!0),bo(e,t,"widget",(function(t){var n=t.widgets||(t.widgets=[]);if(null==i.insertAt?n.push(i):n.splice(Math.min(n.length-1,Math.max(0,i.insertAt)),0,i),i.line=t,o&&!Bt(e,t)){var r=qt(t)<e.scrollTop;Xe(t,t.height+wn(i)),r&&Ar(o,i.height),o.curOp.forceUpdate=!0}return!0})),o&&sn(o,"lineWidgetAdded",o,i,"number"==typeof t?t:Ze(t)),i}(this,e,t,n)})),removeLineWidget:function(e){e.clear()},markText:function(e,t,n){return Lo(this,lt(this,e),lt(this,t),n,n&&n.type||"range")},setBookmark:function(e,t){var n={replacedWith:t&&(null==t.nodeType?t.widget:t),insertLeft:t&&t.insertLeft,clearWhenEmpty:!1,shared:t&&t.shared,handleMouseEvents:t&&t.handleMouseEvents};return Lo(this,e=lt(this,e),e,n,"bookmark")},findMarksAt:function(e){var t=[],n=Ke(this,(e=lt(this,e)).line).markedSpans;if(n)for(var r=0;r<n.length;++r){var i=n[r];(null==i.from||i.from<=e.ch)&&(null==i.to||i.to>=e.ch)&&t.push(i.marker.parent||i.marker)}return t},findMarks:function(e,t,n){e=lt(this,e),t=lt(this,t);var r=[],i=e.line;return this.iter(e.line,t.line+1,(function(o){var a=o.markedSpans;if(a)for(var l=0;l<a.length;l++){var s=a[l];null!=s.to&&i==e.line&&e.ch>=s.to||null==s.from&&i!=e.line||null!=s.from&&i==t.line&&s.from>=t.ch||n&&!n(s.marker)||r.push(s.marker.parent||s.marker)}++i})),r},getAllMarks:function(){var e=[];return this.iter((function(t){var n=t.markedSpans;if(n)for(var r=0;r<n.length;++r)null!=n[r].from&&e.push(n[r].marker)})),e},posFromIndex:function(e){var t,n=this.first,r=this.lineSeparator().length;return this.iter((function(i){var o=i.text.length+r;if(o>e)return t=e,!0;e-=o,++n})),lt(this,et(n,t))},indexFromPos:function(e){var t=(e=lt(this,e)).ch;if(e.line<this.first||e.ch<0)return 0;var n=this.lineSeparator().length;return this.iter(this.first,e.line,(function(e){t+=e.text.length+n})),t},copy:function(e){var t=new Eo(Ge(this,this.first,this.first+this.size),this.modeOption,this.first,this.lineSep,this.direction);return t.scrollTop=this.scrollTop,t.scrollLeft=this.scrollLeft,t.sel=this.sel,t.extend=!1,e&&(t.history.undoDepth=this.history.undoDepth,t.setHistory(this.getHistory())),t},linkedDoc:function(e){e||(e={});var t=this.first,n=this.first+this.size;null!=e.from&&e.from>t&&(t=e.from),null!=e.to&&e.to<n&&(n=e.to);var r=new Eo(Ge(this,t,n),e.mode||this.modeOption,t,this.lineSep,this.direction);return e.sharedHist&&(r.history=this.history),(this.linked||(this.linked=[])).push({doc:r,sharedHist:e.sharedHist}),r.linked=[{doc:this,isParent:!0,sharedHist:e.sharedHist}],function(e,t){for(var n=0;n<t.length;n++){var r=t[n],i=r.find(),o=e.clipPos(i.from),a=e.clipPos(i.to);if(tt(o,a)){var l=Lo(e,o,a,r.primary,r.primary.type);r.markers.push(l),l.parent=r}}}(r,Oo(this)),r},unlinkDoc:function(e){if(e instanceof Ma&&(e=e.doc),this.linked)for(var t=0;t<this.linked.length;++t)if(this.linked[t].doc==e){this.linked.splice(t,1),e.unlinkDoc(this),No(Oo(this));break}if(e.history==this.history){var n=[e.id];Di(e,(function(e){return n.push(e.id)}),!0),e.history=new _i(null),e.history.done=Ki(this.history.done,n),e.history.undone=Ki(this.history.undone,n)}},iterLinkedDocs:function(e){Di(this,e)},getMode:function(){return this.mode},getEditor:function(){return this.cm},splitLines:function(e){return this.lineSep?e.split(this.lineSep):Ee(e)},lineSeparator:function(){return this.lineSep||"\n"},setDirection:ni((function(e){var t;"rtl"!=e&&(e="ltr"),e!=this.direction&&(this.direction=e,this.iter((function(e){return e.order=null})),this.cm&&Jr(t=this.cm,(function(){Fi(t),fr(t)})))}))}),Eo.prototype.eachLine=Eo.prototype.iter;var zo=0;function Po(e){var t=this;if(Do(t),!me(t,e)&&!kn(t.display,e)){be(e),a&&(zo=+new Date);var n=cr(t,e,!0),r=e.dataTransfer.files;if(n&&!t.isReadOnly())if(r&&r.length&&window.FileReader&&window.File)for(var i=r.length,o=Array(i),l=0,s=function(){++l==i&&ei(t,(function(){var e={from:n=lt(t.doc,n),to:n,text:t.doc.splitLines(o.filter((function(e){return null!=e})).join(t.doc.lineSeparator())),origin:"paste"};co(t.doc,e),Qi(t.doc,Ti(lt(t.doc,n),lt(t.doc,Li(e))))}))()},c=function(e,n){if(t.options.allowDropFileTypes&&-1==R(t.options.allowDropFileTypes,e.type))s();else{var r=new FileReader;r.onerror=function(){return s()},r.onload=function(){var e=r.result;/[\x00-\x08\x0e-\x1f]{2}/.test(e)||(o[n]=e),s()},r.readAsText(e)}},u=0;u<r.length;u++)c(r[u],u);else{if(t.state.draggingText&&t.doc.sel.contains(n)>-1)return t.state.draggingText(e),void setTimeout((function(){return t.display.input.focus()}),20);try{var f=e.dataTransfer.getData("Text");if(f){var d;if(t.state.draggingText&&!t.state.draggingText.copy&&(d=t.listSelections()),eo(t.doc,Ti(n,n)),d)for(var h=0;h<d.length;++h)mo(t.doc,"",d[h].anchor,d[h].head,"drag");t.replaceSelection(f,"around","paste"),t.display.input.focus()}}catch(e){}}}}function Do(e){e.display.dragCursor&&(e.display.lineSpace.removeChild(e.display.dragCursor),e.display.dragCursor=null)}function Io(e){if(document.getElementsByClassName){for(var t=document.getElementsByClassName("CodeMirror"),n=[],r=0;r<t.length;r++){var i=t[r].CodeMirror;i&&n.push(i)}n.length&&n[0].operation((function(){for(var t=0;t<n.length;t++)e(n[t])}))}}var Fo=!1;function _o(){var e;Fo||(fe(window,"resize",(function(){null==e&&(e=setTimeout((function(){e=null,Io(Wo)}),100))})),fe(window,"blur",(function(){return Io(Tr)})),Fo=!0)}function Wo(e){var t=e.display;t.cachedCharWidth=t.cachedTextHeight=t.cachedPaddingH=null,t.scrollbarsClipped=!1,e.setSize()}for(var Ho={3:"Pause",8:"Backspace",9:"Tab",13:"Enter",16:"Shift",17:"Ctrl",18:"Alt",19:"Pause",20:"CapsLock",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"PrintScrn",45:"Insert",46:"Delete",59:";",61:"=",91:"Mod",92:"Mod",93:"Mod",106:"*",107:"=",109:"-",110:".",111:"/",145:"ScrollLock",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",224:"Mod",63232:"Up",63233:"Down",63234:"Left",63235:"Right",63272:"Delete",63273:"Home",63275:"End",63276:"PageUp",63277:"PageDown",63302:"Insert"},Ro=0;Ro<10;Ro++)Ho[Ro+48]=Ho[Ro+96]=String(Ro);for(var Bo=65;Bo<=90;Bo++)Ho[Bo]=String.fromCharCode(Bo);for(var jo=1;jo<=12;jo++)Ho[jo+111]=Ho[jo+63235]="F"+jo;var qo={};function Uo(e){var t,n,r,i,o=e.split(/-(?!$)/);e=o[o.length-1];for(var a=0;a<o.length-1;a++){var l=o[a];if(/^(cmd|meta|m)$/i.test(l))i=!0;else if(/^a(lt)?$/i.test(l))t=!0;else if(/^(c|ctrl|control)$/i.test(l))n=!0;else{if(!/^s(hift)?$/i.test(l))throw new Error("Unrecognized modifier name: "+l);r=!0}}return t&&(e="Alt-"+e),n&&(e="Ctrl-"+e),i&&(e="Cmd-"+e),r&&(e="Shift-"+e),e}function Vo(e){var t={};for(var n in e)if(e.hasOwnProperty(n)){var r=e[n];if(/^(name|fallthrough|(de|at)tach)$/.test(n))continue;if("..."==r){delete e[n];continue}for(var i=X(n.split(" "),Uo),o=0;o<i.length;o++){var a=void 0,l=void 0;o==i.length-1?(l=i.join(" "),a=r):(l=i.slice(0,o+1).join(" "),a="...");var s=t[l];if(s){if(s!=a)throw new Error("Inconsistent bindings for "+l)}else t[l]=a}delete e[n]}for(var c in t)e[c]=t[c];return e}function Ko(e,t,n,r){var i=(t=Zo(t)).call?t.call(e,r):t[e];if(!1===i)return"nothing";if("..."===i)return"multi";if(null!=i&&n(i))return"handled";if(t.fallthrough){if("[object Array]"!=Object.prototype.toString.call(t.fallthrough))return Ko(e,t.fallthrough,n,r);for(var o=0;o<t.fallthrough.length;o++){var a=Ko(e,t.fallthrough[o],n,r);if(a)return a}}}function $o(e){var t="string"==typeof e?e:Ho[e.keyCode];return"Ctrl"==t||"Alt"==t||"Shift"==t||"Mod"==t}function Go(e,t,n){var r=e;return t.altKey&&"Alt"!=r&&(e="Alt-"+e),(k?t.metaKey:t.ctrlKey)&&"Ctrl"!=r&&(e="Ctrl-"+e),(k?t.ctrlKey:t.metaKey)&&"Mod"!=r&&(e="Cmd-"+e),!n&&t.shiftKey&&"Shift"!=r&&(e="Shift-"+e),e}function Xo(e,t){if(f&&34==e.keyCode&&e.char)return!1;var n=Ho[e.keyCode];return null!=n&&!e.altGraphKey&&(3==e.keyCode&&e.code&&(n=e.code),Go(n,e,t))}function Zo(e){return"string"==typeof e?qo[e]:e}function Yo(e,t){for(var n=e.doc.sel.ranges,r=[],i=0;i<n.length;i++){for(var o=t(n[i]);r.length&&tt(o.from,G(r).to)<=0;){var a=r.pop();if(tt(a.from,o.from)<0){o.from=a.from;break}}r.push(o)}Jr(e,(function(){for(var t=r.length-1;t>=0;t--)mo(e.doc,"",r[t].from,r[t].to,"+delete");Er(e)}))}function Qo(e,t,n){var r=ie(e.text,t+n,n);return r<0||r>e.text.length?null:r}function Jo(e,t,n){var r=Qo(e,t.ch,n);return null==r?null:new et(t.line,r,n<0?"after":"before")}function ea(e,t,n,r,i){if(e){"rtl"==t.doc.direction&&(i=-i);var o=ce(n,t.doc.direction);if(o){var a,l=i<0?G(o):o[0],s=i<0==(1==l.level)?"after":"before";if(l.level>0||"rtl"==t.doc.direction){var c=zn(t,n);a=i<0?n.text.length-1:0;var u=Pn(t,c,a).top;a=oe((function(e){return Pn(t,c,e).top==u}),i<0==(1==l.level)?l.from:l.to-1,a),"before"==s&&(a=Qo(n,a,1))}else a=i<0?l.to:l.from;return new et(r,a,s)}}return new et(r,i<0?n.text.length:0,i<0?"before":"after")}qo.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"},qo.pcDefault={"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo","Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Up":"goLineUp","Ctrl-Down":"goLineDown","Ctrl-Left":"goGroupLeft","Ctrl-Right":"goGroupRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delGroupBefore","Ctrl-Delete":"delGroupAfter","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore","Ctrl-U":"undoSelection","Shift-Ctrl-U":"redoSelection","Alt-U":"redoSelection",fallthrough:"basic"},qo.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Alt-F":"goWordRight","Alt-B":"goWordLeft","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-D":"delWordAfter","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars","Ctrl-O":"openLine"},qo.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft","Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]},qo.default=y?qo.macDefault:qo.pcDefault;var ta={selectAll:lo,singleSelection:function(e){return e.setSelection(e.getCursor("anchor"),e.getCursor("head"),j)},killLine:function(e){return Yo(e,(function(t){if(t.empty()){var n=Ke(e.doc,t.head.line).text.length;return t.head.ch==n&&t.head.line<e.lastLine()?{from:t.head,to:et(t.head.line+1,0)}:{from:t.head,to:et(t.head.line,n)}}return{from:t.from(),to:t.to()}}))},deleteLine:function(e){return Yo(e,(function(t){return{from:et(t.from().line,0),to:lt(e.doc,et(t.to().line+1,0))}}))},delLineLeft:function(e){return Yo(e,(function(e){return{from:et(e.from().line,0),to:e.from()}}))},delWrappedLineLeft:function(e){return Yo(e,(function(t){var n=e.charCoords(t.head,"div").top+5;return{from:e.coordsChar({left:0,top:n},"div"),to:t.from()}}))},delWrappedLineRight:function(e){return Yo(e,(function(t){var n=e.charCoords(t.head,"div").top+5,r=e.coordsChar({left:e.display.lineDiv.offsetWidth+100,top:n},"div");return{from:t.from(),to:r}}))},undo:function(e){return e.undo()},redo:function(e){return e.redo()},undoSelection:function(e){return e.undoSelection()},redoSelection:function(e){return e.redoSelection()},goDocStart:function(e){return e.extendSelection(et(e.firstLine(),0))},goDocEnd:function(e){return e.extendSelection(et(e.lastLine()))},goLineStart:function(e){return e.extendSelectionsBy((function(t){return na(e,t.head.line)}),{origin:"+move",bias:1})},goLineStartSmart:function(e){return e.extendSelectionsBy((function(t){return ra(e,t.head)}),{origin:"+move",bias:1})},goLineEnd:function(e){return e.extendSelectionsBy((function(t){return function(e,t){var n=Ke(e.doc,t),r=function(e){for(var t;t=It(e);)e=t.find(1,!0).line;return e}(n);return r!=n&&(t=Ze(r)),ea(!0,e,n,t,-1)}(e,t.head.line)}),{origin:"+move",bias:-1})},goLineRight:function(e){return e.extendSelectionsBy((function(t){var n=e.cursorCoords(t.head,"div").top+5;return e.coordsChar({left:e.display.lineDiv.offsetWidth+100,top:n},"div")}),U)},goLineLeft:function(e){return e.extendSelectionsBy((function(t){var n=e.cursorCoords(t.head,"div").top+5;return e.coordsChar({left:0,top:n},"div")}),U)},goLineLeftSmart:function(e){return e.extendSelectionsBy((function(t){var n=e.cursorCoords(t.head,"div").top+5,r=e.coordsChar({left:0,top:n},"div");return r.ch<e.getLine(r.line).search(/\S/)?ra(e,t.head):r}),U)},goLineUp:function(e){return e.moveV(-1,"line")},goLineDown:function(e){return e.moveV(1,"line")},goPageUp:function(e){return e.moveV(-1,"page")},goPageDown:function(e){return e.moveV(1,"page")},goCharLeft:function(e){return e.moveH(-1,"char")},goCharRight:function(e){return e.moveH(1,"char")},goColumnLeft:function(e){return e.moveH(-1,"column")},goColumnRight:function(e){return e.moveH(1,"column")},goWordLeft:function(e){return e.moveH(-1,"word")},goGroupRight:function(e){return e.moveH(1,"group")},goGroupLeft:function(e){return e.moveH(-1,"group")},goWordRight:function(e){return e.moveH(1,"word")},delCharBefore:function(e){return e.deleteH(-1,"codepoint")},delCharAfter:function(e){return e.deleteH(1,"char")},delWordBefore:function(e){return e.deleteH(-1,"word")},delWordAfter:function(e){return e.deleteH(1,"word")},delGroupBefore:function(e){return e.deleteH(-1,"group")},delGroupAfter:function(e){return e.deleteH(1,"group")},indentAuto:function(e){return e.indentSelection("smart")},indentMore:function(e){return e.indentSelection("add")},indentLess:function(e){return e.indentSelection("subtract")},insertTab:function(e){return e.replaceSelection("\t")},insertSoftTab:function(e){for(var t=[],n=e.listSelections(),r=e.options.tabSize,i=0;i<n.length;i++){var o=n[i].from(),a=W(e.getLine(o.line),o.ch,r);t.push($(r-a%r))}e.replaceSelections(t)},defaultTab:function(e){e.somethingSelected()?e.indentSelection("add"):e.execCommand("insertTab")},transposeChars:function(e){return Jr(e,(function(){for(var t=e.listSelections(),n=[],r=0;r<t.length;r++)if(t[r].empty()){var i=t[r].head,o=Ke(e.doc,i.line).text;if(o)if(i.ch==o.length&&(i=new et(i.line,i.ch-1)),i.ch>0)i=new et(i.line,i.ch+1),e.replaceRange(o.charAt(i.ch-1)+o.charAt(i.ch-2),et(i.line,i.ch-2),i,"+transpose");else if(i.line>e.doc.first){var a=Ke(e.doc,i.line-1).text;a&&(i=new et(i.line,1),e.replaceRange(o.charAt(0)+e.doc.lineSeparator()+a.charAt(a.length-1),et(i.line-1,a.length-1),i,"+transpose"))}n.push(new Ci(i,i))}e.setSelections(n)}))},newlineAndIndent:function(e){return Jr(e,(function(){for(var t=e.listSelections(),n=t.length-1;n>=0;n--)e.replaceRange(e.doc.lineSeparator(),t[n].anchor,t[n].head,"+input");t=e.listSelections();for(var r=0;r<t.length;r++)e.indentLine(t[r].from().line,null,!0);Er(e)}))},openLine:function(e){return e.replaceSelection("\n","start")},toggleOverwrite:function(e){return e.toggleOverwrite()}};function na(e,t){var n=Ke(e.doc,t),r=Wt(n);return r!=n&&(t=Ze(r)),ea(!0,e,r,t,1)}function ra(e,t){var n=na(e,t.line),r=Ke(e.doc,n.line),i=ce(r,e.doc.direction);if(!i||0==i[0].level){var o=Math.max(n.ch,r.text.search(/\S/)),a=t.line==n.line&&t.ch<=o&&t.ch;return et(n.line,a?0:o,n.sticky)}return n}function ia(e,t,n){if("string"==typeof t&&!(t=ta[t]))return!1;e.display.input.ensurePolled();var r=e.display.shift,i=!1;try{e.isReadOnly()&&(e.state.suppressEdits=!0),n&&(e.display.shift=!1),i=t(e)!=B}finally{e.display.shift=r,e.state.suppressEdits=!1}return i}var oa=new H;function aa(e,t,n,r){var i=e.state.keySeq;if(i){if($o(t))return"handled";if(/\'$/.test(t)?e.state.keySeq=null:oa.set(50,(function(){e.state.keySeq==i&&(e.state.keySeq=null,e.display.input.reset())})),la(e,i+" "+t,n,r))return!0}return la(e,t,n,r)}function la(e,t,n,r){var i=function(e,t,n){for(var r=0;r<e.state.keyMaps.length;r++){var i=Ko(t,e.state.keyMaps[r],n,e);if(i)return i}return e.options.extraKeys&&Ko(t,e.options.extraKeys,n,e)||Ko(t,e.options.keyMap,n,e)}(e,t,r);return"multi"==i&&(e.state.keySeq=t),"handled"==i&&sn(e,"keyHandled",e,t,n),"handled"!=i&&"multi"!=i||(be(n),wr(e)),!!i}function sa(e,t){var n=Xo(t,!0);return!!n&&(t.shiftKey&&!e.state.keySeq?aa(e,"Shift-"+n,t,(function(t){return ia(e,t,!0)}))||aa(e,n,t,(function(t){if("string"==typeof t?/^go[A-Z]/.test(t):t.motion)return ia(e,t)})):aa(e,n,t,(function(t){return ia(e,t)})))}var ca=null;function ua(e){var t=this;if(!(e.target&&e.target!=t.display.input.getField()||(t.curOp.focus=z(),me(t,e)))){a&&l<11&&27==e.keyCode&&(e.returnValue=!1);var r=e.keyCode;t.display.shift=16==r||e.shiftKey;var i=sa(t,e);f&&(ca=i?r:null,i||88!=r||Pe||!(y?e.metaKey:e.ctrlKey)||t.replaceSelection("",null,"cut")),n&&!y&&!i&&46==r&&e.shiftKey&&!e.ctrlKey&&document.execCommand&&document.execCommand("cut"),18!=r||/\bCodeMirror-crosshair\b/.test(t.display.lineDiv.className)||function(e){var t=e.display.lineDiv;function n(e){18!=e.keyCode&&e.altKey||(L(t,"CodeMirror-crosshair"),he(document,"keyup",n),he(document,"mouseover",n))}P(t,"CodeMirror-crosshair"),fe(document,"keyup",n),fe(document,"mouseover",n)}(t)}}function fa(e){16==e.keyCode&&(this.doc.sel.shift=!1),me(this,e)}function da(e){var t=this;if(!(e.target&&e.target!=t.display.input.getField()||kn(t.display,e)||me(t,e)||e.ctrlKey&&!e.altKey||y&&e.metaKey)){var n=e.keyCode,r=e.charCode;if(f&&n==ca)return ca=null,void be(e);if(!f||e.which&&!(e.which<10)||!sa(t,e)){var i=String.fromCharCode(null==r?n:r);"\b"!=i&&(function(e,t,n){return aa(e,"'"+n+"'",t,(function(t){return ia(e,t,!0)}))}(t,e,i)||t.display.input.onKeyPress(e))}}}var ha,pa,ma=function(e,t,n){this.time=e,this.pos=t,this.button=n};function ga(e){var t=this,n=t.display;if(!(me(t,e)||n.activeTouch&&n.input.supportsTouch()))if(n.input.ensurePolled(),n.shift=e.shiftKey,kn(n,e))s||(n.scroller.draggable=!1,setTimeout((function(){return n.scroller.draggable=!0}),100));else if(!ba(t,e)){var r=cr(t,e),i=Se(e),o=r?function(e,t){var n=+new Date;return pa&&pa.compare(n,e,t)?(ha=pa=null,"triple"):ha&&ha.compare(n,e,t)?(pa=new ma(n,e,t),ha=null,"double"):(ha=new ma(n,e,t),pa=null,"single")}(r,i):"single";window.focus(),1==i&&t.state.selectingText&&t.state.selectingText(e),r&&function(e,t,n,r,i){var o="Click";return"double"==r?o="Double"+o:"triple"==r&&(o="Triple"+o),aa(e,Go(o=(1==t?"Left":2==t?"Middle":"Right")+o,i),i,(function(t){if("string"==typeof t&&(t=ta[t]),!t)return!1;var r=!1;try{e.isReadOnly()&&(e.state.suppressEdits=!0),r=t(e,n)!=B}finally{e.state.suppressEdits=!1}return r}))}(t,i,r,o,e)||(1==i?r?function(e,t,n,r){a?setTimeout(F(kr,e),0):e.curOp.focus=z();var i,o=function(e,t,n){var r=e.getOption("configureMouse"),i=r?r(e,t,n):{};if(null==i.unit){var o=b?n.shiftKey&&n.metaKey:n.altKey;i.unit=o?"rectangle":"single"==t?"char":"double"==t?"word":"line"}return(null==i.extend||e.doc.extend)&&(i.extend=e.doc.extend||n.shiftKey),null==i.addNew&&(i.addNew=y?n.metaKey:n.ctrlKey),null==i.moveOnDrag&&(i.moveOnDrag=!(y?n.altKey:n.ctrlKey)),i}(e,n,r),c=e.doc.sel;e.options.dragDrop&&Me&&!e.isReadOnly()&&"single"==n&&(i=c.contains(t))>-1&&(tt((i=c.ranges[i]).from(),t)<0||t.xRel>0)&&(tt(i.to(),t)>0||t.xRel<0)?function(e,t,n,r){var i=e.display,o=!1,c=ei(e,(function(t){s&&(i.scroller.draggable=!1),e.state.draggingText=!1,he(i.wrapper.ownerDocument,"mouseup",c),he(i.wrapper.ownerDocument,"mousemove",u),he(i.scroller,"dragstart",f),he(i.scroller,"drop",c),o||(be(t),r.addNew||Gi(e.doc,n,null,null,r.extend),s&&!d||a&&9==l?setTimeout((function(){i.wrapper.ownerDocument.body.focus({preventScroll:!0}),i.input.focus()}),20):i.input.focus())})),u=function(e){o=o||Math.abs(t.clientX-e.clientX)+Math.abs(t.clientY-e.clientY)>=10},f=function(){return o=!0};s&&(i.scroller.draggable=!0),e.state.draggingText=c,c.copy=!r.moveOnDrag,i.scroller.dragDrop&&i.scroller.dragDrop(),fe(i.wrapper.ownerDocument,"mouseup",c),fe(i.wrapper.ownerDocument,"mousemove",u),fe(i.scroller,"dragstart",f),fe(i.scroller,"drop",c),Cr(e),setTimeout((function(){return i.input.focus()}),20)}(e,r,t,o):function(e,t,n,r){var i=e.display,o=e.doc;be(t);var a,l,s=o.sel,c=s.ranges;if(r.addNew&&!r.extend?(l=o.sel.contains(n),a=l>-1?c[l]:new Ci(n,n)):(a=o.sel.primary(),l=o.sel.primIndex),"rectangle"==r.unit)r.addNew||(a=new Ci(n,n)),n=cr(e,t,!0,!0),l=-1;else{var u=va(e,n,r.unit);a=r.extend?$i(a,u.anchor,u.head,r.extend):u}r.addNew?-1==l?(l=c.length,Ji(o,Si(e,c.concat([a]),l),{scroll:!1,origin:"*mouse"})):c.length>1&&c[l].empty()&&"char"==r.unit&&!r.extend?(Ji(o,Si(e,c.slice(0,l).concat(c.slice(l+1)),0),{scroll:!1,origin:"*mouse"}),s=o.sel):Zi(o,l,a,q):(l=0,Ji(o,new ki([a],0),q),s=o.sel);var f=n;function d(t){if(0!=tt(f,t))if(f=t,"rectangle"==r.unit){for(var i=[],c=e.options.tabSize,u=W(Ke(o,n.line).text,n.ch,c),d=W(Ke(o,t.line).text,t.ch,c),h=Math.min(u,d),p=Math.max(u,d),m=Math.min(n.line,t.line),g=Math.min(e.lastLine(),Math.max(n.line,t.line));m<=g;m++){var v=Ke(o,m).text,y=V(v,h,c);h==p?i.push(new Ci(et(m,y),et(m,y))):v.length>y&&i.push(new Ci(et(m,y),et(m,V(v,p,c))))}i.length||i.push(new Ci(n,n)),Ji(o,Si(e,s.ranges.slice(0,l).concat(i),l),{origin:"*mouse",scroll:!1}),e.scrollIntoView(t)}else{var b,x=a,w=va(e,t,r.unit),k=x.anchor;tt(w.anchor,k)>0?(b=w.head,k=ot(x.from(),w.anchor)):(b=w.anchor,k=it(x.to(),w.head));var C=s.ranges.slice(0);C[l]=function(e,t){var n=t.anchor,r=t.head,i=Ke(e.doc,n.line);if(0==tt(n,r)&&n.sticky==r.sticky)return t;var o=ce(i);if(!o)return t;var a=le(o,n.ch,n.sticky),l=o[a];if(l.from!=n.ch&&l.to!=n.ch)return t;var s,c=a+(l.from==n.ch==(1!=l.level)?0:1);if(0==c||c==o.length)return t;if(r.line!=n.line)s=(r.line-n.line)*("ltr"==e.doc.direction?1:-1)>0;else{var u=le(o,r.ch,r.sticky),f=u-a||(r.ch-n.ch)*(1==l.level?-1:1);s=u==c-1||u==c?f<0:f>0}var d=o[c+(s?-1:0)],h=s==(1==d.level),p=h?d.from:d.to,m=h?"after":"before";return n.ch==p&&n.sticky==m?t:new Ci(new et(n.line,p,m),r)}(e,new Ci(lt(o,k),b)),Ji(o,Si(e,C,l),q)}}var h=i.wrapper.getBoundingClientRect(),p=0;function m(t){e.state.selectingText=!1,p=1/0,t&&(be(t),i.input.focus()),he(i.wrapper.ownerDocument,"mousemove",g),he(i.wrapper.ownerDocument,"mouseup",v),o.history.lastSelOrigin=null}var g=ei(e,(function(t){0!==t.buttons&&Se(t)?function t(n){var a=++p,l=cr(e,n,!0,"rectangle"==r.unit);if(l)if(0!=tt(l,f)){e.curOp.focus=z(),d(l);var s=Or(i,o);(l.line>=s.to||l.line<s.from)&&setTimeout(ei(e,(function(){p==a&&t(n)})),150)}else{var c=n.clientY<h.top?-20:n.clientY>h.bottom?20:0;c&&setTimeout(ei(e,(function(){p==a&&(i.scroller.scrollTop+=c,t(n))})),50)}}(t):m(t)})),v=ei(e,m);e.state.selectingText=v,fe(i.wrapper.ownerDocument,"mousemove",g),fe(i.wrapper.ownerDocument,"mouseup",v)}(e,r,t,o)}(t,r,o,e):Ce(e)==n.scroller&&be(e):2==i?(r&&Gi(t.doc,r),setTimeout((function(){return n.input.focus()}),20)):3==i&&(C?t.display.input.onContextMenu(e):Cr(t)))}}function va(e,t,n){if("char"==n)return new Ci(t,t);if("word"==n)return e.findWordAt(t);if("line"==n)return new Ci(et(t.line,0),lt(e.doc,et(t.line+1,0)));var r=n(e,t);return new Ci(r.from,r.to)}function ya(e,t,n,r){var i,o;if(t.touches)i=t.touches[0].clientX,o=t.touches[0].clientY;else try{i=t.clientX,o=t.clientY}catch(e){return!1}if(i>=Math.floor(e.display.gutters.getBoundingClientRect().right))return!1;r&&be(t);var a=e.display,l=a.lineDiv.getBoundingClientRect();if(o>l.bottom||!ve(e,n))return we(t);o-=l.top-a.viewOffset;for(var s=0;s<e.display.gutterSpecs.length;++s){var c=a.gutters.childNodes[s];if(c&&c.getBoundingClientRect().right>=i)return pe(e,n,e,Ye(e.doc,o),e.display.gutterSpecs[s].className,t),we(t)}}function ba(e,t){return ya(e,t,"gutterClick",!0)}function xa(e,t){kn(e.display,t)||function(e,t){return!!ve(e,"gutterContextMenu")&&ya(e,t,"gutterContextMenu",!1)}(e,t)||me(e,t,"contextmenu")||C||e.display.input.onContextMenu(t)}function wa(e){e.display.wrapper.className=e.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+e.options.theme.replace(/(^|\s)\s*/g," cm-s-"),Rn(e)}ma.prototype.compare=function(e,t,n){return this.time+400>e&&0==tt(t,this.pos)&&n==this.button};var ka={toString:function(){return"CodeMirror.Init"}},Ca={},Sa={};function Ta(e,t,n){if(!t!=!(n&&n!=ka)){var r=e.display.dragFunctions,i=t?fe:he;i(e.display.scroller,"dragstart",r.start),i(e.display.scroller,"dragenter",r.enter),i(e.display.scroller,"dragover",r.over),i(e.display.scroller,"dragleave",r.leave),i(e.display.scroller,"drop",r.drop)}}function La(e){e.options.lineWrapping?(P(e.display.wrapper,"CodeMirror-wrap"),e.display.sizer.style.minWidth="",e.display.sizerWidth=null):(L(e.display.wrapper,"CodeMirror-wrap"),Vt(e)),sr(e),fr(e),Rn(e),setTimeout((function(){return Br(e)}),100)}function Ma(e,t){var n=this;if(!(this instanceof Ma))return new Ma(e,t);this.options=t=t?_(t):{},_(Ca,t,!1);var r=t.value;"string"==typeof r?r=new Eo(r,t.mode,null,t.lineSeparator,t.direction):t.mode&&(r.modeOption=t.mode),this.doc=r;var i=new Ma.inputStyles[t.inputStyle](this),o=this.display=new gi(e,r,i,t);for(var c in o.wrapper.CodeMirror=this,wa(this),t.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap"),Ur(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,delayingBlurEvent:!1,focused:!1,suppressEdits:!1,pasteIncoming:-1,cutIncoming:-1,selectingText:!1,draggingText:!1,highlight:new H,keySeq:null,specialChars:null},t.autofocus&&!v&&o.input.focus(),a&&l<11&&setTimeout((function(){return n.display.input.reset(!0)}),20),function(e){var t=e.display;fe(t.scroller,"mousedown",ei(e,ga)),fe(t.scroller,"dblclick",a&&l<11?ei(e,(function(t){if(!me(e,t)){var n=cr(e,t);if(n&&!ba(e,t)&&!kn(e.display,t)){be(t);var r=e.findWordAt(n);Gi(e.doc,r.anchor,r.head)}}})):function(t){return me(e,t)||be(t)}),fe(t.scroller,"contextmenu",(function(t){return xa(e,t)})),fe(t.input.getField(),"contextmenu",(function(n){t.scroller.contains(n.target)||xa(e,n)}));var n,r={end:0};function i(){t.activeTouch&&(n=setTimeout((function(){return t.activeTouch=null}),1e3),(r=t.activeTouch).end=+new Date)}function o(e,t){if(null==t.left)return!0;var n=t.left-e.left,r=t.top-e.top;return n*n+r*r>400}fe(t.scroller,"touchstart",(function(i){if(!me(e,i)&&!function(e){if(1!=e.touches.length)return!1;var t=e.touches[0];return t.radiusX<=1&&t.radiusY<=1}(i)&&!ba(e,i)){t.input.ensurePolled(),clearTimeout(n);var o=+new Date;t.activeTouch={start:o,moved:!1,prev:o-r.end<=300?r:null},1==i.touches.length&&(t.activeTouch.left=i.touches[0].pageX,t.activeTouch.top=i.touches[0].pageY)}})),fe(t.scroller,"touchmove",(function(){t.activeTouch&&(t.activeTouch.moved=!0)})),fe(t.scroller,"touchend",(function(n){var r=t.activeTouch;if(r&&!kn(t,n)&&null!=r.left&&!r.moved&&new Date-r.start<300){var a,l=e.coordsChar(t.activeTouch,"page");a=!r.prev||o(r,r.prev)?new Ci(l,l):!r.prev.prev||o(r,r.prev.prev)?e.findWordAt(l):new Ci(et(l.line,0),lt(e.doc,et(l.line+1,0))),e.setSelection(a.anchor,a.head),e.focus(),be(n)}i()})),fe(t.scroller,"touchcancel",i),fe(t.scroller,"scroll",(function(){t.scroller.clientHeight&&(Ir(e,t.scroller.scrollTop),_r(e,t.scroller.scrollLeft,!0),pe(e,"scroll",e))})),fe(t.scroller,"mousewheel",(function(t){return wi(e,t)})),fe(t.scroller,"DOMMouseScroll",(function(t){return wi(e,t)})),fe(t.wrapper,"scroll",(function(){return t.wrapper.scrollTop=t.wrapper.scrollLeft=0})),t.dragFunctions={enter:function(t){me(e,t)||ke(t)},over:function(t){me(e,t)||(function(e,t){var n=cr(e,t);if(n){var r=document.createDocumentFragment();yr(e,n,r),e.display.dragCursor||(e.display.dragCursor=N("div",null,"CodeMirror-cursors CodeMirror-dragcursors"),e.display.lineSpace.insertBefore(e.display.dragCursor,e.display.cursorDiv)),O(e.display.dragCursor,r)}}(e,t),ke(t))},start:function(t){return function(e,t){if(a&&(!e.state.draggingText||+new Date-zo<100))ke(t);else if(!me(e,t)&&!kn(e.display,t)&&(t.dataTransfer.setData("Text",e.getSelection()),t.dataTransfer.effectAllowed="copyMove",t.dataTransfer.setDragImage&&!d)){var n=N("img",null,null,"position: fixed; left: 0; top: 0;");n.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",f&&(n.width=n.height=1,e.display.wrapper.appendChild(n),n._top=n.offsetTop),t.dataTransfer.setDragImage(n,0,0),f&&n.parentNode.removeChild(n)}}(e,t)},drop:ei(e,Po),leave:function(t){me(e,t)||Do(e)}};var s=t.input.getField();fe(s,"keyup",(function(t){return fa.call(e,t)})),fe(s,"keydown",ei(e,ua)),fe(s,"keypress",ei(e,da)),fe(s,"focus",(function(t){return Sr(e,t)})),fe(s,"blur",(function(t){return Tr(e,t)}))}(this),_o(),Kr(this),this.curOp.forceUpdate=!0,Ii(this,r),t.autofocus&&!v||this.hasFocus()?setTimeout((function(){n.hasFocus()&&!n.state.focused&&Sr(n)}),20):Tr(this),Sa)Sa.hasOwnProperty(c)&&Sa[c](this,t[c],ka);di(this),t.finishInit&&t.finishInit(this);for(var u=0;u<Oa.length;++u)Oa[u](this);$r(this),s&&t.lineWrapping&&"optimizelegibility"==getComputedStyle(o.lineDiv).textRendering&&(o.lineDiv.style.textRendering="auto")}Ma.defaults=Ca,Ma.optionHandlers=Sa;var Oa=[];function Na(e,t,n,r){var i,o=e.doc;null==n&&(n="add"),"smart"==n&&(o.mode.indent?i=ht(e,t).state:n="prev");var a=e.options.tabSize,l=Ke(o,t),s=W(l.text,null,a);l.stateAfter&&(l.stateAfter=null);var c,u=l.text.match(/^\s*/)[0];if(r||/\S/.test(l.text)){if("smart"==n&&((c=o.mode.indent(i,l.text.slice(u.length),l.text))==B||c>150)){if(!r)return;n="prev"}}else c=0,n="not";"prev"==n?c=t>o.first?W(Ke(o,t-1).text,null,a):0:"add"==n?c=s+e.options.indentUnit:"subtract"==n?c=s-e.options.indentUnit:"number"==typeof n&&(c=s+n),c=Math.max(0,c);var f="",d=0;if(e.options.indentWithTabs)for(var h=Math.floor(c/a);h;--h)d+=a,f+="\t";if(d<c&&(f+=$(c-d)),f!=u)return mo(o,f,et(t,0),et(t,u.length),"+input"),l.stateAfter=null,!0;for(var p=0;p<o.sel.ranges.length;p++){var m=o.sel.ranges[p];if(m.head.line==t&&m.head.ch<u.length){var g=et(t,u.length);Zi(o,p,new Ci(g,g));break}}}Ma.defineInitHook=function(e){return Oa.push(e)};var Aa=null;function Ea(e){Aa=e}function za(e,t,n,r,i){var o=e.doc;e.display.shift=!1,r||(r=o.sel);var a=+new Date-200,l="paste"==i||e.state.pasteIncoming>a,s=Ee(t),c=null;if(l&&r.ranges.length>1)if(Aa&&Aa.text.join("\n")==t){if(r.ranges.length%Aa.text.length==0){c=[];for(var u=0;u<Aa.text.length;u++)c.push(o.splitLines(Aa.text[u]))}}else s.length==r.ranges.length&&e.options.pasteLinesPerSelection&&(c=X(s,(function(e){return[e]})));for(var f=e.curOp.updateInput,d=r.ranges.length-1;d>=0;d--){var h=r.ranges[d],p=h.from(),m=h.to();h.empty()&&(n&&n>0?p=et(p.line,p.ch-n):e.state.overwrite&&!l?m=et(m.line,Math.min(Ke(o,m.line).text.length,m.ch+G(s).length)):l&&Aa&&Aa.lineWise&&Aa.text.join("\n")==s.join("\n")&&(p=m=et(p.line,0)));var g={from:p,to:m,text:c?c[d%c.length]:s,origin:i||(l?"paste":e.state.cutIncoming>a?"cut":"+input")};co(e.doc,g),sn(e,"inputRead",e,g)}t&&!l&&Da(e,t),Er(e),e.curOp.updateInput<2&&(e.curOp.updateInput=f),e.curOp.typing=!0,e.state.pasteIncoming=e.state.cutIncoming=-1}function Pa(e,t){var n=e.clipboardData&&e.clipboardData.getData("Text");if(n)return e.preventDefault(),t.isReadOnly()||t.options.disableInput||Jr(t,(function(){return za(t,n,0,null,"paste")})),!0}function Da(e,t){if(e.options.electricChars&&e.options.smartIndent)for(var n=e.doc.sel,r=n.ranges.length-1;r>=0;r--){var i=n.ranges[r];if(!(i.head.ch>100||r&&n.ranges[r-1].head.line==i.head.line)){var o=e.getModeAt(i.head),a=!1;if(o.electricChars){for(var l=0;l<o.electricChars.length;l++)if(t.indexOf(o.electricChars.charAt(l))>-1){a=Na(e,i.head.line,"smart");break}}else o.electricInput&&o.electricInput.test(Ke(e.doc,i.head.line).text.slice(0,i.head.ch))&&(a=Na(e,i.head.line,"smart"));a&&sn(e,"electricInput",e,i.head.line)}}}function Ia(e){for(var t=[],n=[],r=0;r<e.doc.sel.ranges.length;r++){var i=e.doc.sel.ranges[r].head.line,o={anchor:et(i,0),head:et(i+1,0)};n.push(o),t.push(e.getRange(o.anchor,o.head))}return{text:t,ranges:n}}function Fa(e,t,n,r){e.setAttribute("autocorrect",n?"":"off"),e.setAttribute("autocapitalize",r?"":"off"),e.setAttribute("spellcheck",!!t)}function _a(){var e=N("textarea",null,null,"position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"),t=N("div",[e],null,"overflow: hidden; position: relative; width: 3px; height: 0px;");return s?e.style.width="1000px":e.setAttribute("wrap","off"),m&&(e.style.border="1px solid black"),Fa(e),t}function Wa(e,t,n,r,i){var o=t,a=n,l=Ke(e,t.line),s=i&&"rtl"==e.direction?-n:n;function c(o){var a,c;if("codepoint"==r){var u=l.text.charCodeAt(t.ch+(r>0?0:-1));a=isNaN(u)?null:new et(t.line,Math.max(0,Math.min(l.text.length,t.ch+n*(u>=55296&&u<56320?2:1))),-n)}else a=i?function(e,t,n,r){var i=ce(t,e.doc.direction);if(!i)return Jo(t,n,r);n.ch>=t.text.length?(n.ch=t.text.length,n.sticky="before"):n.ch<=0&&(n.ch=0,n.sticky="after");var o=le(i,n.ch,n.sticky),a=i[o];if("ltr"==e.doc.direction&&a.level%2==0&&(r>0?a.to>n.ch:a.from<n.ch))return Jo(t,n,r);var l,s=function(e,n){return Qo(t,e instanceof et?e.ch:e,n)},c=function(n){return e.options.lineWrapping?(l=l||zn(e,t),Qn(e,t,l,n)):{begin:0,end:t.text.length}},u=c("before"==n.sticky?s(n,-1):n.ch);if("rtl"==e.doc.direction||1==a.level){var f=1==a.level==r<0,d=s(n,f?1:-1);if(null!=d&&(f?d<=a.to&&d<=u.end:d>=a.from&&d>=u.begin)){var h=f?"before":"after";return new et(n.line,d,h)}}var p=function(e,t,r){for(var o=function(e,t){return t?new et(n.line,s(e,1),"before"):new et(n.line,e,"after")};e>=0&&e<i.length;e+=t){var a=i[e],l=t>0==(1!=a.level),c=l?r.begin:s(r.end,-1);if(a.from<=c&&c<a.to)return o(c,l);if(c=l?a.from:s(a.to,-1),r.begin<=c&&c<r.end)return o(c,l)}},m=p(o+r,r,u);if(m)return m;var g=r>0?u.end:s(u.begin,-1);return null==g||r>0&&g==t.text.length||!(m=p(r>0?0:i.length-1,r,c(g)))?null:m}(e.cm,l,t,n):Jo(l,t,n);if(null==a){if(o||(c=t.line+s)<e.first||c>=e.first+e.size||(t=new et(c,t.ch,t.sticky),!(l=Ke(e,c))))return!1;t=ea(i,e.cm,l,t.line,s)}else t=a;return!0}if("char"==r||"codepoint"==r)c();else if("column"==r)c(!0);else if("word"==r||"group"==r)for(var u=null,f="group"==r,d=e.cm&&e.cm.getHelper(t,"wordChars"),h=!0;!(n<0)||c(!h);h=!1){var p=l.text.charAt(t.ch)||"\n",m=ee(p,d)?"w":f&&"\n"==p?"n":!f||/\s/.test(p)?null:"p";if(!f||h||m||(m="s"),u&&u!=m){n<0&&(n=1,c(),t.sticky="after");break}if(m&&(u=m),n>0&&!c(!h))break}var g=oo(e,t,o,a,!0);return nt(o,g)&&(g.hitSide=!0),g}function Ha(e,t,n,r){var i,o,a=e.doc,l=t.left;if("page"==r){var s=Math.min(e.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight),c=Math.max(s-.5*rr(e.display),3);i=(n>0?t.bottom:t.top)+n*c}else"line"==r&&(i=n>0?t.bottom+3:t.top-3);for(;(o=Zn(e,l,i)).outside;){if(n<0?i<=0:i>=a.height){o.hitSide=!0;break}i+=5*n}return o}var Ra=function(e){this.cm=e,this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null,this.polling=new H,this.composing=null,this.gracePeriod=!1,this.readDOMTimeout=null};function Ba(e,t){var n=En(e,t.line);if(!n||n.hidden)return null;var r=Ke(e.doc,t.line),i=Nn(n,r,t.line),o=ce(r,e.doc.direction),a="left";o&&(a=le(o,t.ch)%2?"right":"left");var l=Fn(i.map,t.ch,a);return l.offset="right"==l.collapse?l.end:l.start,l}function ja(e,t){return t&&(e.bad=!0),e}function qa(e,t,n){var r;if(t==e.display.lineDiv){if(!(r=e.display.lineDiv.childNodes[n]))return ja(e.clipPos(et(e.display.viewTo-1)),!0);t=null,n=0}else for(r=t;;r=r.parentNode){if(!r||r==e.display.lineDiv)return null;if(r.parentNode&&r.parentNode==e.display.lineDiv)break}for(var i=0;i<e.display.view.length;i++){var o=e.display.view[i];if(o.node==r)return Ua(o,t,n)}}function Ua(e,t,n){var r=e.text.firstChild,i=!1;if(!t||!E(r,t))return ja(et(Ze(e.line),0),!0);if(t==r&&(i=!0,t=r.childNodes[n],n=0,!t)){var o=e.rest?G(e.rest):e.line;return ja(et(Ze(o),o.text.length),i)}var a=3==t.nodeType?t:null,l=t;for(a||1!=t.childNodes.length||3!=t.firstChild.nodeType||(a=t.firstChild,n&&(n=a.nodeValue.length));l.parentNode!=r;)l=l.parentNode;var s=e.measure,c=s.maps;function u(t,n,r){for(var i=-1;i<(c?c.length:0);i++)for(var o=i<0?s.map:c[i],a=0;a<o.length;a+=3){var l=o[a+2];if(l==t||l==n){var u=Ze(i<0?e.line:e.rest[i]),f=o[a]+r;return(r<0||l!=t)&&(f=o[a+(r?1:0)]),et(u,f)}}}var f=u(a,l,n);if(f)return ja(f,i);for(var d=l.nextSibling,h=a?a.nodeValue.length-n:0;d;d=d.nextSibling){if(f=u(d,d.firstChild,0))return ja(et(f.line,f.ch-h),i);h+=d.textContent.length}for(var p=l.previousSibling,m=n;p;p=p.previousSibling){if(f=u(p,p.firstChild,-1))return ja(et(f.line,f.ch+m),i);m+=p.textContent.length}}Ra.prototype.init=function(e){var t=this,n=this,r=n.cm,i=n.div=e.lineDiv;function o(e){for(var t=e.target;t;t=t.parentNode){if(t==i)return!0;if(/\bCodeMirror-(?:line)?widget\b/.test(t.className))break}return!1}function a(e){if(o(e)&&!me(r,e)){if(r.somethingSelected())Ea({lineWise:!1,text:r.getSelections()}),"cut"==e.type&&r.replaceSelection("",null,"cut");else{if(!r.options.lineWiseCopyCut)return;var t=Ia(r);Ea({lineWise:!0,text:t.text}),"cut"==e.type&&r.operation((function(){r.setSelections(t.ranges,0,j),r.replaceSelection("",null,"cut")}))}if(e.clipboardData){e.clipboardData.clearData();var a=Aa.text.join("\n");if(e.clipboardData.setData("Text",a),e.clipboardData.getData("Text")==a)return void e.preventDefault()}var l=_a(),s=l.firstChild;r.display.lineSpace.insertBefore(l,r.display.lineSpace.firstChild),s.value=Aa.text.join("\n");var c=document.activeElement;I(s),setTimeout((function(){r.display.lineSpace.removeChild(l),c.focus(),c==i&&n.showPrimarySelection()}),50)}}Fa(i,r.options.spellcheck,r.options.autocorrect,r.options.autocapitalize),fe(i,"paste",(function(e){!o(e)||me(r,e)||Pa(e,r)||l<=11&&setTimeout(ei(r,(function(){return t.updateFromDOM()})),20)})),fe(i,"compositionstart",(function(e){t.composing={data:e.data,done:!1}})),fe(i,"compositionupdate",(function(e){t.composing||(t.composing={data:e.data,done:!1})})),fe(i,"compositionend",(function(e){t.composing&&(e.data!=t.composing.data&&t.readFromDOMSoon(),t.composing.done=!0)})),fe(i,"touchstart",(function(){return n.forceCompositionEnd()})),fe(i,"input",(function(){t.composing||t.readFromDOMSoon()})),fe(i,"copy",a),fe(i,"cut",a)},Ra.prototype.screenReaderLabelChanged=function(e){e?this.div.setAttribute("aria-label",e):this.div.removeAttribute("aria-label")},Ra.prototype.prepareSelection=function(){var e=vr(this.cm,!1);return e.focus=document.activeElement==this.div,e},Ra.prototype.showSelection=function(e,t){e&&this.cm.display.view.length&&((e.focus||t)&&this.showPrimarySelection(),this.showMultipleSelections(e))},Ra.prototype.getSelection=function(){return this.cm.display.wrapper.ownerDocument.getSelection()},Ra.prototype.showPrimarySelection=function(){var e=this.getSelection(),t=this.cm,r=t.doc.sel.primary(),i=r.from(),o=r.to();if(t.display.viewTo==t.display.viewFrom||i.line>=t.display.viewTo||o.line<t.display.viewFrom)e.removeAllRanges();else{var a=qa(t,e.anchorNode,e.anchorOffset),l=qa(t,e.focusNode,e.focusOffset);if(!a||a.bad||!l||l.bad||0!=tt(ot(a,l),i)||0!=tt(it(a,l),o)){var s=t.display.view,c=i.line>=t.display.viewFrom&&Ba(t,i)||{node:s[0].measure.map[2],offset:0},u=o.line<t.display.viewTo&&Ba(t,o);if(!u){var f=s[s.length-1].measure,d=f.maps?f.maps[f.maps.length-1]:f.map;u={node:d[d.length-1],offset:d[d.length-2]-d[d.length-3]}}if(c&&u){var h,p=e.rangeCount&&e.getRangeAt(0);try{h=T(c.node,c.offset,u.offset,u.node)}catch(e){}h&&(!n&&t.state.focused?(e.collapse(c.node,c.offset),h.collapsed||(e.removeAllRanges(),e.addRange(h))):(e.removeAllRanges(),e.addRange(h)),p&&null==e.anchorNode?e.addRange(p):n&&this.startGracePeriod()),this.rememberSelection()}else e.removeAllRanges()}}},Ra.prototype.startGracePeriod=function(){var e=this;clearTimeout(this.gracePeriod),this.gracePeriod=setTimeout((function(){e.gracePeriod=!1,e.selectionChanged()&&e.cm.operation((function(){return e.cm.curOp.selectionChanged=!0}))}),20)},Ra.prototype.showMultipleSelections=function(e){O(this.cm.display.cursorDiv,e.cursors),O(this.cm.display.selectionDiv,e.selection)},Ra.prototype.rememberSelection=function(){var e=this.getSelection();this.lastAnchorNode=e.anchorNode,this.lastAnchorOffset=e.anchorOffset,this.lastFocusNode=e.focusNode,this.lastFocusOffset=e.focusOffset},Ra.prototype.selectionInEditor=function(){var e=this.getSelection();if(!e.rangeCount)return!1;var t=e.getRangeAt(0).commonAncestorContainer;return E(this.div,t)},Ra.prototype.focus=function(){"nocursor"!=this.cm.options.readOnly&&(this.selectionInEditor()&&document.activeElement==this.div||this.showSelection(this.prepareSelection(),!0),this.div.focus())},Ra.prototype.blur=function(){this.div.blur()},Ra.prototype.getField=function(){return this.div},Ra.prototype.supportsTouch=function(){return!0},Ra.prototype.receivedFocus=function(){var e=this;this.selectionInEditor()?this.pollSelection():Jr(this.cm,(function(){return e.cm.curOp.selectionChanged=!0})),this.polling.set(this.cm.options.pollInterval,(function t(){e.cm.state.focused&&(e.pollSelection(),e.polling.set(e.cm.options.pollInterval,t))}))},Ra.prototype.selectionChanged=function(){var e=this.getSelection();return e.anchorNode!=this.lastAnchorNode||e.anchorOffset!=this.lastAnchorOffset||e.focusNode!=this.lastFocusNode||e.focusOffset!=this.lastFocusOffset},Ra.prototype.pollSelection=function(){if(null==this.readDOMTimeout&&!this.gracePeriod&&this.selectionChanged()){var e=this.getSelection(),t=this.cm;if(g&&u&&this.cm.display.gutterSpecs.length&&function(e){for(var t=e;t;t=t.parentNode)if(/CodeMirror-gutter-wrapper/.test(t.className))return!0;return!1}(e.anchorNode))return this.cm.triggerOnKeyDown({type:"keydown",keyCode:8,preventDefault:Math.abs}),this.blur(),void this.focus();if(!this.composing){this.rememberSelection();var n=qa(t,e.anchorNode,e.anchorOffset),r=qa(t,e.focusNode,e.focusOffset);n&&r&&Jr(t,(function(){Ji(t.doc,Ti(n,r),j),(n.bad||r.bad)&&(t.curOp.selectionChanged=!0)}))}}},Ra.prototype.pollContent=function(){null!=this.readDOMTimeout&&(clearTimeout(this.readDOMTimeout),this.readDOMTimeout=null);var e,t,n,r=this.cm,i=r.display,o=r.doc.sel.primary(),a=o.from(),l=o.to();if(0==a.ch&&a.line>r.firstLine()&&(a=et(a.line-1,Ke(r.doc,a.line-1).length)),l.ch==Ke(r.doc,l.line).text.length&&l.line<r.lastLine()&&(l=et(l.line+1,0)),a.line<i.viewFrom||l.line>i.viewTo-1)return!1;a.line==i.viewFrom||0==(e=ur(r,a.line))?(t=Ze(i.view[0].line),n=i.view[0].node):(t=Ze(i.view[e].line),n=i.view[e-1].node.nextSibling);var s,c,u=ur(r,l.line);if(u==i.view.length-1?(s=i.viewTo-1,c=i.lineDiv.lastChild):(s=Ze(i.view[u+1].line)-1,c=i.view[u+1].node.previousSibling),!n)return!1;for(var f=r.doc.splitLines(function(e,t,n,r,i){var o="",a=!1,l=e.doc.lineSeparator(),s=!1;function c(){a&&(o+=l,s&&(o+=l),a=s=!1)}function u(e){e&&(c(),o+=e)}function f(t){if(1==t.nodeType){var n=t.getAttribute("cm-text");if(n)return void u(n);var o,d=t.getAttribute("cm-marker");if(d){var h=e.findMarks(et(r,0),et(i+1,0),(g=+d,function(e){return e.id==g}));return void(h.length&&(o=h[0].find(0))&&u($e(e.doc,o.from,o.to).join(l)))}if("false"==t.getAttribute("contenteditable"))return;var p=/^(pre|div|p|li|table|br)$/i.test(t.nodeName);if(!/^br$/i.test(t.nodeName)&&0==t.textContent.length)return;p&&c();for(var m=0;m<t.childNodes.length;m++)f(t.childNodes[m]);/^(pre|p)$/i.test(t.nodeName)&&(s=!0),p&&(a=!0)}else 3==t.nodeType&&u(t.nodeValue.replace(/\u200b/g,"").replace(/\u00a0/g," "));var g}for(;f(t),t!=n;)t=t.nextSibling,s=!1;return o}(r,n,c,t,s)),d=$e(r.doc,et(t,0),et(s,Ke(r.doc,s).text.length));f.length>1&&d.length>1;)if(G(f)==G(d))f.pop(),d.pop(),s--;else{if(f[0]!=d[0])break;f.shift(),d.shift(),t++}for(var h=0,p=0,m=f[0],g=d[0],v=Math.min(m.length,g.length);h<v&&m.charCodeAt(h)==g.charCodeAt(h);)++h;for(var y=G(f),b=G(d),x=Math.min(y.length-(1==f.length?h:0),b.length-(1==d.length?h:0));p<x&&y.charCodeAt(y.length-p-1)==b.charCodeAt(b.length-p-1);)++p;if(1==f.length&&1==d.length&&t==a.line)for(;h&&h>a.ch&&y.charCodeAt(y.length-p-1)==b.charCodeAt(b.length-p-1);)h--,p++;f[f.length-1]=y.slice(0,y.length-p).replace(/^\u200b+/,""),f[0]=f[0].slice(h).replace(/\u200b+$/,"");var w=et(t,h),k=et(s,d.length?G(d).length-p:0);return f.length>1||f[0]||tt(w,k)?(mo(r.doc,f,w,k,"+input"),!0):void 0},Ra.prototype.ensurePolled=function(){this.forceCompositionEnd()},Ra.prototype.reset=function(){this.forceCompositionEnd()},Ra.prototype.forceCompositionEnd=function(){this.composing&&(clearTimeout(this.readDOMTimeout),this.composing=null,this.updateFromDOM(),this.div.blur(),this.div.focus())},Ra.prototype.readFromDOMSoon=function(){var e=this;null==this.readDOMTimeout&&(this.readDOMTimeout=setTimeout((function(){if(e.readDOMTimeout=null,e.composing){if(!e.composing.done)return;e.composing=null}e.updateFromDOM()}),80))},Ra.prototype.updateFromDOM=function(){var e=this;!this.cm.isReadOnly()&&this.pollContent()||Jr(this.cm,(function(){return fr(e.cm)}))},Ra.prototype.setUneditable=function(e){e.contentEditable="false"},Ra.prototype.onKeyPress=function(e){0==e.charCode||this.composing||(e.preventDefault(),this.cm.isReadOnly()||ei(this.cm,za)(this.cm,String.fromCharCode(null==e.charCode?e.keyCode:e.charCode),0))},Ra.prototype.readOnlyChanged=function(e){this.div.contentEditable=String("nocursor"!=e)},Ra.prototype.onContextMenu=function(){},Ra.prototype.resetPosition=function(){},Ra.prototype.needsContentAttribute=!0;var Va=function(e){this.cm=e,this.prevInput="",this.pollingFast=!1,this.polling=new H,this.hasSelection=!1,this.composing=null};Va.prototype.init=function(e){var t=this,n=this,r=this.cm;this.createField(e);var i=this.textarea;function o(e){if(!me(r,e)){if(r.somethingSelected())Ea({lineWise:!1,text:r.getSelections()});else{if(!r.options.lineWiseCopyCut)return;var t=Ia(r);Ea({lineWise:!0,text:t.text}),"cut"==e.type?r.setSelections(t.ranges,null,j):(n.prevInput="",i.value=t.text.join("\n"),I(i))}"cut"==e.type&&(r.state.cutIncoming=+new Date)}}e.wrapper.insertBefore(this.wrapper,e.wrapper.firstChild),m&&(i.style.width="0px"),fe(i,"input",(function(){a&&l>=9&&t.hasSelection&&(t.hasSelection=null),n.poll()})),fe(i,"paste",(function(e){me(r,e)||Pa(e,r)||(r.state.pasteIncoming=+new Date,n.fastPoll())})),fe(i,"cut",o),fe(i,"copy",o),fe(e.scroller,"paste",(function(t){if(!kn(e,t)&&!me(r,t)){if(!i.dispatchEvent)return r.state.pasteIncoming=+new Date,void n.focus();var o=new Event("paste");o.clipboardData=t.clipboardData,i.dispatchEvent(o)}})),fe(e.lineSpace,"selectstart",(function(t){kn(e,t)||be(t)})),fe(i,"compositionstart",(function(){var e=r.getCursor("from");n.composing&&n.composing.range.clear(),n.composing={start:e,range:r.markText(e,r.getCursor("to"),{className:"CodeMirror-composing"})}})),fe(i,"compositionend",(function(){n.composing&&(n.poll(),n.composing.range.clear(),n.composing=null)}))},Va.prototype.createField=function(e){this.wrapper=_a(),this.textarea=this.wrapper.firstChild},Va.prototype.screenReaderLabelChanged=function(e){e?this.textarea.setAttribute("aria-label",e):this.textarea.removeAttribute("aria-label")},Va.prototype.prepareSelection=function(){var e=this.cm,t=e.display,n=e.doc,r=vr(e);if(e.options.moveInputWithCursor){var i=$n(e,n.sel.primary().head,"div"),o=t.wrapper.getBoundingClientRect(),a=t.lineDiv.getBoundingClientRect();r.teTop=Math.max(0,Math.min(t.wrapper.clientHeight-10,i.top+a.top-o.top)),r.teLeft=Math.max(0,Math.min(t.wrapper.clientWidth-10,i.left+a.left-o.left))}return r},Va.prototype.showSelection=function(e){var t=this.cm.display;O(t.cursorDiv,e.cursors),O(t.selectionDiv,e.selection),null!=e.teTop&&(this.wrapper.style.top=e.teTop+"px",this.wrapper.style.left=e.teLeft+"px")},Va.prototype.reset=function(e){if(!this.contextMenuPending&&!this.composing){var t=this.cm;if(t.somethingSelected()){this.prevInput="";var n=t.getSelection();this.textarea.value=n,t.state.focused&&I(this.textarea),a&&l>=9&&(this.hasSelection=n)}else e||(this.prevInput=this.textarea.value="",a&&l>=9&&(this.hasSelection=null))}},Va.prototype.getField=function(){return this.textarea},Va.prototype.supportsTouch=function(){return!1},Va.prototype.focus=function(){if("nocursor"!=this.cm.options.readOnly&&(!v||z()!=this.textarea))try{this.textarea.focus()}catch(e){}},Va.prototype.blur=function(){this.textarea.blur()},Va.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0},Va.prototype.receivedFocus=function(){this.slowPoll()},Va.prototype.slowPoll=function(){var e=this;this.pollingFast||this.polling.set(this.cm.options.pollInterval,(function(){e.poll(),e.cm.state.focused&&e.slowPoll()}))},Va.prototype.fastPoll=function(){var e=!1,t=this;t.pollingFast=!0,t.polling.set(20,(function n(){t.poll()||e?(t.pollingFast=!1,t.slowPoll()):(e=!0,t.polling.set(60,n))}))},Va.prototype.poll=function(){var e=this,t=this.cm,n=this.textarea,r=this.prevInput;if(this.contextMenuPending||!t.state.focused||ze(n)&&!r&&!this.composing||t.isReadOnly()||t.options.disableInput||t.state.keySeq)return!1;var i=n.value;if(i==r&&!t.somethingSelected())return!1;if(a&&l>=9&&this.hasSelection===i||y&&/[\uf700-\uf7ff]/.test(i))return t.display.input.reset(),!1;if(t.doc.sel==t.display.selForContextMenu){var o=i.charCodeAt(0);if(8203!=o||r||(r="​"),8666==o)return this.reset(),this.cm.execCommand("undo")}for(var s=0,c=Math.min(r.length,i.length);s<c&&r.charCodeAt(s)==i.charCodeAt(s);)++s;return Jr(t,(function(){za(t,i.slice(s),r.length-s,null,e.composing?"*compose":null),i.length>1e3||i.indexOf("\n")>-1?n.value=e.prevInput="":e.prevInput=i,e.composing&&(e.composing.range.clear(),e.composing.range=t.markText(e.composing.start,t.getCursor("to"),{className:"CodeMirror-composing"}))})),!0},Va.prototype.ensurePolled=function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},Va.prototype.onKeyPress=function(){a&&l>=9&&(this.hasSelection=null),this.fastPoll()},Va.prototype.onContextMenu=function(e){var t=this,n=t.cm,r=n.display,i=t.textarea;t.contextMenuPending&&t.contextMenuPending();var o=cr(n,e),c=r.scroller.scrollTop;if(o&&!f){n.options.resetSelectionOnContextMenu&&-1==n.doc.sel.contains(o)&&ei(n,Ji)(n.doc,Ti(o),j);var u,d=i.style.cssText,h=t.wrapper.style.cssText,p=t.wrapper.offsetParent.getBoundingClientRect();if(t.wrapper.style.cssText="position: static",i.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(e.clientY-p.top-5)+"px; left: "+(e.clientX-p.left-5)+"px;\n z-index: 1000; background: "+(a?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",s&&(u=window.scrollY),r.input.focus(),s&&window.scrollTo(null,u),r.input.reset(),n.somethingSelected()||(i.value=t.prevInput=" "),t.contextMenuPending=v,r.selForContextMenu=n.doc.sel,clearTimeout(r.detectingSelectAll),a&&l>=9&&g(),C){ke(e);var m=function(){he(window,"mouseup",m),setTimeout(v,20)};fe(window,"mouseup",m)}else setTimeout(v,50)}function g(){if(null!=i.selectionStart){var e=n.somethingSelected(),o="​"+(e?i.value:"");i.value="⇚",i.value=o,t.prevInput=e?"":"​",i.selectionStart=1,i.selectionEnd=o.length,r.selForContextMenu=n.doc.sel}}function v(){if(t.contextMenuPending==v&&(t.contextMenuPending=!1,t.wrapper.style.cssText=h,i.style.cssText=d,a&&l<9&&r.scrollbars.setScrollTop(r.scroller.scrollTop=c),null!=i.selectionStart)){(!a||a&&l<9)&&g();var e=0,o=function(){r.selForContextMenu==n.doc.sel&&0==i.selectionStart&&i.selectionEnd>0&&"​"==t.prevInput?ei(n,lo)(n):e++<10?r.detectingSelectAll=setTimeout(o,500):(r.selForContextMenu=null,r.input.reset())};r.detectingSelectAll=setTimeout(o,200)}}},Va.prototype.readOnlyChanged=function(e){e||this.reset(),this.textarea.disabled="nocursor"==e,this.textarea.readOnly=!!e},Va.prototype.setUneditable=function(){},Va.prototype.needsContentAttribute=!1,function(e){var t=e.optionHandlers;function n(n,r,i,o){e.defaults[n]=r,i&&(t[n]=o?function(e,t,n){n!=ka&&i(e,t,n)}:i)}e.defineOption=n,e.Init=ka,n("value","",(function(e,t){return e.setValue(t)}),!0),n("mode",null,(function(e,t){e.doc.modeOption=t,Ai(e)}),!0),n("indentUnit",2,Ai,!0),n("indentWithTabs",!1),n("smartIndent",!0),n("tabSize",4,(function(e){Ei(e),Rn(e),fr(e)}),!0),n("lineSeparator",null,(function(e,t){if(e.doc.lineSep=t,t){var n=[],r=e.doc.first;e.doc.iter((function(e){for(var i=0;;){var o=e.text.indexOf(t,i);if(-1==o)break;i=o+t.length,n.push(et(r,o))}r++}));for(var i=n.length-1;i>=0;i--)mo(e.doc,t,n[i],et(n[i].line,n[i].ch+t.length))}})),n("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g,(function(e,t,n){e.state.specialChars=new RegExp(t.source+(t.test("\t")?"":"|\t"),"g"),n!=ka&&e.refresh()})),n("specialCharPlaceholder",Qt,(function(e){return e.refresh()}),!0),n("electricChars",!0),n("inputStyle",v?"contenteditable":"textarea",(function(){throw new Error("inputStyle can not (yet) be changed in a running editor")}),!0),n("spellcheck",!1,(function(e,t){return e.getInputField().spellcheck=t}),!0),n("autocorrect",!1,(function(e,t){return e.getInputField().autocorrect=t}),!0),n("autocapitalize",!1,(function(e,t){return e.getInputField().autocapitalize=t}),!0),n("rtlMoveVisually",!x),n("wholeLineUpdateBefore",!0),n("theme","default",(function(e){wa(e),mi(e)}),!0),n("keyMap","default",(function(e,t,n){var r=Zo(t),i=n!=ka&&Zo(n);i&&i.detach&&i.detach(e,r),r.attach&&r.attach(e,i||null)})),n("extraKeys",null),n("configureMouse",null),n("lineWrapping",!1,La,!0),n("gutters",[],(function(e,t){e.display.gutterSpecs=hi(t,e.options.lineNumbers),mi(e)}),!0),n("fixedGutter",!0,(function(e,t){e.display.gutters.style.left=t?ar(e.display)+"px":"0",e.refresh()}),!0),n("coverGutterNextToScrollbar",!1,(function(e){return Br(e)}),!0),n("scrollbarStyle","native",(function(e){Ur(e),Br(e),e.display.scrollbars.setScrollTop(e.doc.scrollTop),e.display.scrollbars.setScrollLeft(e.doc.scrollLeft)}),!0),n("lineNumbers",!1,(function(e,t){e.display.gutterSpecs=hi(e.options.gutters,t),mi(e)}),!0),n("firstLineNumber",1,mi,!0),n("lineNumberFormatter",(function(e){return e}),mi,!0),n("showCursorWhenSelecting",!1,gr,!0),n("resetSelectionOnContextMenu",!0),n("lineWiseCopyCut",!0),n("pasteLinesPerSelection",!0),n("selectionsMayTouch",!1),n("readOnly",!1,(function(e,t){"nocursor"==t&&(Tr(e),e.display.input.blur()),e.display.input.readOnlyChanged(t)})),n("screenReaderLabel",null,(function(e,t){t=""===t?null:t,e.display.input.screenReaderLabelChanged(t)})),n("disableInput",!1,(function(e,t){t||e.display.input.reset()}),!0),n("dragDrop",!0,Ta),n("allowDropFileTypes",null),n("cursorBlinkRate",530),n("cursorScrollMargin",0),n("cursorHeight",1,gr,!0),n("singleCursorHeightPerLine",!0,gr,!0),n("workTime",100),n("workDelay",100),n("flattenSpans",!0,Ei,!0),n("addModeClass",!1,Ei,!0),n("pollInterval",100),n("undoDepth",200,(function(e,t){return e.doc.history.undoDepth=t})),n("historyEventDelay",1250),n("viewportMargin",10,(function(e){return e.refresh()}),!0),n("maxHighlightLength",1e4,Ei,!0),n("moveInputWithCursor",!0,(function(e,t){t||e.display.input.resetPosition()})),n("tabindex",null,(function(e,t){return e.display.input.getField().tabIndex=t||""})),n("autofocus",null),n("direction","ltr",(function(e,t){return e.doc.setDirection(t)}),!0),n("phrases",null)}(Ma),function(e){var t=e.optionHandlers,n=e.helpers={};e.prototype={constructor:e,focus:function(){window.focus(),this.display.input.focus()},setOption:function(e,n){var r=this.options,i=r[e];r[e]==n&&"mode"!=e||(r[e]=n,t.hasOwnProperty(e)&&ei(this,t[e])(this,n,i),pe(this,"optionChange",this,e))},getOption:function(e){return this.options[e]},getDoc:function(){return this.doc},addKeyMap:function(e,t){this.state.keyMaps[t?"push":"unshift"](Zo(e))},removeKeyMap:function(e){for(var t=this.state.keyMaps,n=0;n<t.length;++n)if(t[n]==e||t[n].name==e)return t.splice(n,1),!0},addOverlay:ti((function(t,n){var r=t.token?t:e.getMode(this.options,t);if(r.startState)throw new Error("Overlays may not be stateful.");!function(e,t,n){for(var r=0,i=n(t);r<e.length&&n(e[r])<=i;)r++;e.splice(r,0,t)}(this.state.overlays,{mode:r,modeSpec:t,opaque:n&&n.opaque,priority:n&&n.priority||0},(function(e){return e.priority})),this.state.modeGen++,fr(this)})),removeOverlay:ti((function(e){for(var t=this.state.overlays,n=0;n<t.length;++n){var r=t[n].modeSpec;if(r==e||"string"==typeof e&&r.name==e)return t.splice(n,1),this.state.modeGen++,void fr(this)}})),indentLine:ti((function(e,t,n){"string"!=typeof t&&"number"!=typeof t&&(t=null==t?this.options.smartIndent?"smart":"prev":t?"add":"subtract"),Qe(this.doc,e)&&Na(this,e,t,n)})),indentSelection:ti((function(e){for(var t=this.doc.sel.ranges,n=-1,r=0;r<t.length;r++){var i=t[r];if(i.empty())i.head.line>n&&(Na(this,i.head.line,e,!0),n=i.head.line,r==this.doc.sel.primIndex&&Er(this));else{var o=i.from(),a=i.to(),l=Math.max(n,o.line);n=Math.min(this.lastLine(),a.line-(a.ch?0:1))+1;for(var s=l;s<n;++s)Na(this,s,e);var c=this.doc.sel.ranges;0==o.ch&&t.length==c.length&&c[r].from().ch>0&&Zi(this.doc,r,new Ci(o,c[r].to()),j)}}})),getTokenAt:function(e,t){return yt(this,e,t)},getLineTokens:function(e,t){return yt(this,et(e),t,!0)},getTokenTypeAt:function(e){e=lt(this.doc,e);var t,n=dt(this,Ke(this.doc,e.line)),r=0,i=(n.length-1)/2,o=e.ch;if(0==o)t=n[2];else for(;;){var a=r+i>>1;if((a?n[2*a-1]:0)>=o)i=a;else{if(!(n[2*a+1]<o)){t=n[2*a+2];break}r=a+1}}var l=t?t.indexOf("overlay "):-1;return l<0?t:0==l?null:t.slice(0,l-1)},getModeAt:function(t){var n=this.doc.mode;return n.innerMode?e.innerMode(n,this.getTokenAt(t).state).mode:n},getHelper:function(e,t){return this.getHelpers(e,t)[0]},getHelpers:function(e,t){var r=[];if(!n.hasOwnProperty(t))return r;var i=n[t],o=this.getModeAt(e);if("string"==typeof o[t])i[o[t]]&&r.push(i[o[t]]);else if(o[t])for(var a=0;a<o[t].length;a++){var l=i[o[t][a]];l&&r.push(l)}else o.helperType&&i[o.helperType]?r.push(i[o.helperType]):i[o.name]&&r.push(i[o.name]);for(var s=0;s<i._global.length;s++){var c=i._global[s];c.pred(o,this)&&-1==R(r,c.val)&&r.push(c.val)}return r},getStateAfter:function(e,t){var n=this.doc;return ht(this,(e=at(n,null==e?n.first+n.size-1:e))+1,t).state},cursorCoords:function(e,t){var n=this.doc.sel.primary();return $n(this,null==e?n.head:"object"==typeof e?lt(this.doc,e):e?n.from():n.to(),t||"page")},charCoords:function(e,t){return Kn(this,lt(this.doc,e),t||"page")},coordsChar:function(e,t){return Zn(this,(e=Vn(this,e,t||"page")).left,e.top)},lineAtHeight:function(e,t){return e=Vn(this,{top:e,left:0},t||"page").top,Ye(this.doc,e+this.display.viewOffset)},heightAtLine:function(e,t,n){var r,i=!1;if("number"==typeof e){var o=this.doc.first+this.doc.size-1;e<this.doc.first?e=this.doc.first:e>o&&(e=o,i=!0),r=Ke(this.doc,e)}else r=e;return Un(this,r,{top:0,left:0},t||"page",n||i).top+(i?this.doc.height-qt(r):0)},defaultTextHeight:function(){return rr(this.display)},defaultCharWidth:function(){return ir(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(e,t,n,r,i){var o,a,l,s=this.display,c=(e=$n(this,lt(this.doc,e))).bottom,u=e.left;if(t.style.position="absolute",t.setAttribute("cm-ignore-events","true"),this.display.input.setUneditable(t),s.sizer.appendChild(t),"over"==r)c=e.top;else if("above"==r||"near"==r){var f=Math.max(s.wrapper.clientHeight,this.doc.height),d=Math.max(s.sizer.clientWidth,s.lineSpace.clientWidth);("above"==r||e.bottom+t.offsetHeight>f)&&e.top>t.offsetHeight?c=e.top-t.offsetHeight:e.bottom+t.offsetHeight<=f&&(c=e.bottom),u+t.offsetWidth>d&&(u=d-t.offsetWidth)}t.style.top=c+"px",t.style.left=t.style.right="","right"==i?(u=s.sizer.clientWidth-t.offsetWidth,t.style.right="0px"):("left"==i?u=0:"middle"==i&&(u=(s.sizer.clientWidth-t.offsetWidth)/2),t.style.left=u+"px"),n&&(o=this,a={left:u,top:c,right:u+t.offsetWidth,bottom:c+t.offsetHeight},null!=(l=Nr(o,a)).scrollTop&&Ir(o,l.scrollTop),null!=l.scrollLeft&&_r(o,l.scrollLeft))},triggerOnKeyDown:ti(ua),triggerOnKeyPress:ti(da),triggerOnKeyUp:fa,triggerOnMouseDown:ti(ga),execCommand:function(e){if(ta.hasOwnProperty(e))return ta[e].call(null,this)},triggerElectric:ti((function(e){Da(this,e)})),findPosH:function(e,t,n,r){var i=1;t<0&&(i=-1,t=-t);for(var o=lt(this.doc,e),a=0;a<t&&!(o=Wa(this.doc,o,i,n,r)).hitSide;++a);return o},moveH:ti((function(e,t){var n=this;this.extendSelectionsBy((function(r){return n.display.shift||n.doc.extend||r.empty()?Wa(n.doc,r.head,e,t,n.options.rtlMoveVisually):e<0?r.from():r.to()}),U)})),deleteH:ti((function(e,t){var n=this.doc.sel,r=this.doc;n.somethingSelected()?r.replaceSelection("",null,"+delete"):Yo(this,(function(n){var i=Wa(r,n.head,e,t,!1);return e<0?{from:i,to:n.head}:{from:n.head,to:i}}))})),findPosV:function(e,t,n,r){var i=1,o=r;t<0&&(i=-1,t=-t);for(var a=lt(this.doc,e),l=0;l<t;++l){var s=$n(this,a,"div");if(null==o?o=s.left:s.left=o,(a=Ha(this,s,i,n)).hitSide)break}return a},moveV:ti((function(e,t){var n=this,r=this.doc,i=[],o=!this.display.shift&&!r.extend&&r.sel.somethingSelected();if(r.extendSelectionsBy((function(a){if(o)return e<0?a.from():a.to();var l=$n(n,a.head,"div");null!=a.goalColumn&&(l.left=a.goalColumn),i.push(l.left);var s=Ha(n,l,e,t);return"page"==t&&a==r.sel.primary()&&Ar(n,Kn(n,s,"div").top-l.top),s}),U),i.length)for(var a=0;a<r.sel.ranges.length;a++)r.sel.ranges[a].goalColumn=i[a]})),findWordAt:function(e){var t=Ke(this.doc,e.line).text,n=e.ch,r=e.ch;if(t){var i=this.getHelper(e,"wordChars");"before"!=e.sticky&&r!=t.length||!n?++r:--n;for(var o=t.charAt(n),a=ee(o,i)?function(e){return ee(e,i)}:/\s/.test(o)?function(e){return/\s/.test(e)}:function(e){return!/\s/.test(e)&&!ee(e)};n>0&&a(t.charAt(n-1));)--n;for(;r<t.length&&a(t.charAt(r));)++r}return new Ci(et(e.line,n),et(e.line,r))},toggleOverwrite:function(e){null!=e&&e==this.state.overwrite||((this.state.overwrite=!this.state.overwrite)?P(this.display.cursorDiv,"CodeMirror-overwrite"):L(this.display.cursorDiv,"CodeMirror-overwrite"),pe(this,"overwriteToggle",this,this.state.overwrite))},hasFocus:function(){return this.display.input.getField()==z()},isReadOnly:function(){return!(!this.options.readOnly&&!this.doc.cantEdit)},scrollTo:ti((function(e,t){zr(this,e,t)})),getScrollInfo:function(){var e=this.display.scroller;return{left:e.scrollLeft,top:e.scrollTop,height:e.scrollHeight-Ln(this)-this.display.barHeight,width:e.scrollWidth-Ln(this)-this.display.barWidth,clientHeight:On(this),clientWidth:Mn(this)}},scrollIntoView:ti((function(e,t){null==e?(e={from:this.doc.sel.primary().head,to:null},null==t&&(t=this.options.cursorScrollMargin)):"number"==typeof e?e={from:et(e,0),to:null}:null==e.from&&(e={from:e,to:null}),e.to||(e.to=e.from),e.margin=t||0,null!=e.from.line?function(e,t){Pr(e),e.curOp.scrollToPos=t}(this,e):Dr(this,e.from,e.to,e.margin)})),setSize:ti((function(e,t){var n=this,r=function(e){return"number"==typeof e||/^\d+$/.test(String(e))?e+"px":e};null!=e&&(this.display.wrapper.style.width=r(e)),null!=t&&(this.display.wrapper.style.height=r(t)),this.options.lineWrapping&&Hn(this);var i=this.display.viewFrom;this.doc.iter(i,this.display.viewTo,(function(e){if(e.widgets)for(var t=0;t<e.widgets.length;t++)if(e.widgets[t].noHScroll){dr(n,i,"widget");break}++i})),this.curOp.forceUpdate=!0,pe(this,"refresh",this)})),operation:function(e){return Jr(this,e)},startOperation:function(){return Kr(this)},endOperation:function(){return $r(this)},refresh:ti((function(){var e=this.display.cachedTextHeight;fr(this),this.curOp.forceUpdate=!0,Rn(this),zr(this,this.doc.scrollLeft,this.doc.scrollTop),ci(this.display),(null==e||Math.abs(e-rr(this.display))>.5||this.options.lineWrapping)&&sr(this),pe(this,"refresh",this)})),swapDoc:ti((function(e){var t=this.doc;return t.cm=null,this.state.selectingText&&this.state.selectingText(),Ii(this,e),Rn(this),this.display.input.reset(),zr(this,e.scrollLeft,e.scrollTop),this.curOp.forceScroll=!0,sn(this,"swapDoc",this,t),t})),phrase:function(e){var t=this.options.phrases;return t&&Object.prototype.hasOwnProperty.call(t,e)?t[e]:e},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}},ye(e),e.registerHelper=function(t,r,i){n.hasOwnProperty(t)||(n[t]=e[t]={_global:[]}),n[t][r]=i},e.registerGlobalHelper=function(t,r,i,o){e.registerHelper(t,r,o),n[t]._global.push({pred:i,val:o})}}(Ma);var Ka="iter insert remove copy getEditor constructor".split(" ");for(var $a in Eo.prototype)Eo.prototype.hasOwnProperty($a)&&R(Ka,$a)<0&&(Ma.prototype[$a]=function(e){return function(){return e.apply(this.doc,arguments)}}(Eo.prototype[$a]));return ye(Eo),Ma.inputStyles={textarea:Va,contenteditable:Ra},Ma.defineMode=function(e){Ma.defaults.mode||"null"==e||(Ma.defaults.mode=e),_e.apply(this,arguments)},Ma.defineMIME=function(e,t){Fe[e]=t},Ma.defineMode("null",(function(){return{token:function(e){return e.skipToEnd()}}})),Ma.defineMIME("text/plain","null"),Ma.defineExtension=function(e,t){Ma.prototype[e]=t},Ma.defineDocExtension=function(e,t){Eo.prototype[e]=t},Ma.fromTextArea=function(e,t){if((t=t?_(t):{}).value=e.value,!t.tabindex&&e.tabIndex&&(t.tabindex=e.tabIndex),!t.placeholder&&e.placeholder&&(t.placeholder=e.placeholder),null==t.autofocus){var n=z();t.autofocus=n==e||null!=e.getAttribute("autofocus")&&n==document.body}function r(){e.value=l.getValue()}var i;if(e.form&&(fe(e.form,"submit",r),!t.leaveSubmitMethodAlone)){var o=e.form;i=o.submit;try{var a=o.submit=function(){r(),o.submit=i,o.submit(),o.submit=a}}catch(e){}}t.finishInit=function(n){n.save=r,n.getTextArea=function(){return e},n.toTextArea=function(){n.toTextArea=isNaN,r(),e.parentNode.removeChild(n.getWrapperElement()),e.style.display="",e.form&&(he(e.form,"submit",r),t.leaveSubmitMethodAlone||"function"!=typeof e.form.submit||(e.form.submit=i))}},e.style.display="none";var l=Ma((function(t){return e.parentNode.insertBefore(t,e.nextSibling)}),t);return l},function(e){e.off=he,e.on=fe,e.wheelEventPixels=xi,e.Doc=Eo,e.splitLines=Ee,e.countColumn=W,e.findColumn=V,e.isWordChar=J,e.Pass=B,e.signal=pe,e.Line=Kt,e.changeEnd=Li,e.scrollbarModel=qr,e.Pos=et,e.cmpPos=tt,e.modes=Ie,e.mimeModes=Fe,e.resolveMode=We,e.getMode=He,e.modeExtensions=Re,e.extendMode=Be,e.copyState=je,e.startState=Ue,e.innerMode=qe,e.commands=ta,e.keyMap=qo,e.keyName=Xo,e.isModifierKey=$o,e.lookupKey=Ko,e.normalizeKeyMap=Vo,e.StringStream=Ve,e.SharedTextMarker=Mo,e.TextMarker=To,e.LineWidget=ko,e.e_preventDefault=be,e.e_stopPropagation=xe,e.e_stop=ke,e.addClass=P,e.contains=E,e.rmClass=L,e.keyNames=Ho}(Ma),Ma.version="5.58.1",Ma}()},function(e,t,n){!function(e){"use strict";var t,n,r=e.Pos;function i(e,t){for(var n=function(e){var t=e.flags;return null!=t?t:(e.ignoreCase?"i":"")+(e.global?"g":"")+(e.multiline?"m":"")}(e),r=n,i=0;i<t.length;i++)-1==r.indexOf(t.charAt(i))&&(r+=t.charAt(i));return n==r?e:new RegExp(e.source,r)}function o(e,t,n){t=i(t,"g");for(var o=n.line,a=n.ch,l=e.lastLine();o<=l;o++,a=0){t.lastIndex=a;var s=e.getLine(o),c=t.exec(s);if(c)return{from:r(o,c.index),to:r(o,c.index+c[0].length),match:c}}}function a(e,t,n){if(!function(e){return/\\s|\\n|\n|\\W|\\D|\[\^/.test(e.source)}(t))return o(e,t,n);t=i(t,"gm");for(var a,l=1,s=n.line,c=e.lastLine();s<=c;){for(var u=0;u<l&&!(s>c);u++){var f=e.getLine(s++);a=null==a?f:a+"\n"+f}l*=2,t.lastIndex=n.ch;var d=t.exec(a);if(d){var h=a.slice(0,d.index).split("\n"),p=d[0].split("\n"),m=n.line+h.length-1,g=h[h.length-1].length;return{from:r(m,g),to:r(m+p.length-1,1==p.length?g+p[0].length:p[p.length-1].length),match:d}}}}function l(e,t){for(var n,r=0;;){t.lastIndex=r;var i=t.exec(e);if(!i)return n;if((r=(n=i).index+(n[0].length||1))==e.length)return n}}function s(e,t,n){t=i(t,"g");for(var o=n.line,a=n.ch,s=e.firstLine();o>=s;o--,a=-1){var c=e.getLine(o);a>-1&&(c=c.slice(0,a));var u=l(c,t);if(u)return{from:r(o,u.index),to:r(o,u.index+u[0].length),match:u}}}function c(e,t,n){t=i(t,"gm");for(var o,a=1,s=n.line,c=e.firstLine();s>=c;){for(var u=0;u<a;u++){var f=e.getLine(s--);o=null==o?f.slice(0,n.ch):f+"\n"+o}a*=2;var d=l(o,t);if(d){var h=o.slice(0,d.index).split("\n"),p=d[0].split("\n"),m=s+h.length,g=h[h.length-1].length;return{from:r(m,g),to:r(m+p.length-1,1==p.length?g+p[0].length:p[p.length-1].length),match:d}}}}function u(e,t,n,r){if(e.length==t.length)return n;for(var i=0,o=n+Math.max(0,e.length-t.length);;){if(i==o)return i;var a=i+o>>1,l=r(e.slice(0,a)).length;if(l==n)return a;l>n?o=a:i=a+1}}function f(e,i,o,a){if(!i.length)return null;var l=a?t:n,s=l(i).split(/\r|\n\r?/);e:for(var c=o.line,f=o.ch,d=e.lastLine()+1-s.length;c<=d;c++,f=0){var h=e.getLine(c).slice(f),p=l(h);if(1==s.length){var m=p.indexOf(s[0]);if(-1==m)continue e;return o=u(h,p,m,l)+f,{from:r(c,u(h,p,m,l)+f),to:r(c,u(h,p,m+s[0].length,l)+f)}}var g=p.length-s[0].length;if(p.slice(g)==s[0]){for(var v=1;v<s.length-1;v++)if(l(e.getLine(c+v))!=s[v])continue e;var y=e.getLine(c+s.length-1),b=l(y),x=s[s.length-1];if(b.slice(0,x.length)==x)return{from:r(c,u(h,p,g,l)+f),to:r(c+s.length-1,u(y,b,x.length,l))}}}}function d(e,i,o,a){if(!i.length)return null;var l=a?t:n,s=l(i).split(/\r|\n\r?/);e:for(var c=o.line,f=o.ch,d=e.firstLine()-1+s.length;c>=d;c--,f=-1){var h=e.getLine(c);f>-1&&(h=h.slice(0,f));var p=l(h);if(1==s.length){var m=p.lastIndexOf(s[0]);if(-1==m)continue e;return{from:r(c,u(h,p,m,l)),to:r(c,u(h,p,m+s[0].length,l))}}var g=s[s.length-1];if(p.slice(0,g.length)==g){var v=1;for(o=c-s.length+1;v<s.length-1;v++)if(l(e.getLine(o+v))!=s[v])continue e;var y=e.getLine(c+1-s.length),b=l(y);if(b.slice(b.length-s[0].length)==s[0])return{from:r(c+1-s.length,u(y,b,y.length-s[0].length,l)),to:r(c,u(h,p,g.length,l))}}}}function h(e,t,n,l){var u;this.atOccurrence=!1,this.doc=e,n=n?e.clipPos(n):r(0,0),this.pos={from:n,to:n},"object"==typeof l?u=l.caseFold:(u=l,l=null),"string"==typeof t?(null==u&&(u=!1),this.matches=function(n,r){return(n?d:f)(e,t,r,u)}):(t=i(t,"gm"),l&&!1===l.multiline?this.matches=function(n,r){return(n?s:o)(e,t,r)}:this.matches=function(n,r){return(n?c:a)(e,t,r)})}String.prototype.normalize?(t=function(e){return e.normalize("NFD").toLowerCase()},n=function(e){return e.normalize("NFD")}):(t=function(e){return e.toLowerCase()},n=function(e){return e}),h.prototype={findNext:function(){return this.find(!1)},findPrevious:function(){return this.find(!0)},find:function(t){for(var n=this.matches(t,this.doc.clipPos(t?this.pos.from:this.pos.to));n&&0==e.cmpPos(n.from,n.to);)t?n.from.ch?n.from=r(n.from.line,n.from.ch-1):n=n.from.line==this.doc.firstLine()?null:this.matches(t,this.doc.clipPos(r(n.from.line-1))):n.to.ch<this.doc.getLine(n.to.line).length?n.to=r(n.to.line,n.to.ch+1):n=n.to.line==this.doc.lastLine()?null:this.matches(t,r(n.to.line+1,0));if(n)return this.pos=n,this.atOccurrence=!0,this.pos.match||!0;var i=r(t?this.doc.firstLine():this.doc.lastLine()+1,0);return this.pos={from:i,to:i},this.atOccurrence=!1},from:function(){if(this.atOccurrence)return this.pos.from},to:function(){if(this.atOccurrence)return this.pos.to},replace:function(t,n){if(this.atOccurrence){var i=e.splitLines(t);this.doc.replaceRange(i,this.pos.from,this.pos.to,n),this.pos.to=r(this.pos.from.line+i.length-1,i[i.length-1].length+(1==i.length?this.pos.from.ch:0))}}},e.defineExtension("getSearchCursor",(function(e,t,n){return new h(this.doc,e,t,n)})),e.defineDocExtension("getSearchCursor",(function(e,t,n){return new h(this,e,t,n)})),e.defineExtension("selectMatches",(function(t,n){for(var r=[],i=this.getSearchCursor(t,this.getCursor("from"),n);i.findNext()&&!(e.cmpPos(i.to(),this.getCursor("to"))>0);)r.push({anchor:i.from(),head:i.to()});r.length&&this.setSelections(r,0)}))}(n(0))},function(e,t,n){!function(e){"use strict";e.defineMode("javascript",(function(t,n){var r,i,o=t.indentUnit,a=n.statementIndent,l=n.jsonld,s=n.json||l,c=n.typescript,u=n.wordCharacters||/[\w$\xa1-\uffff]/,f=function(){function e(e){return{type:e,style:"keyword"}}var t=e("keyword a"),n=e("keyword b"),r=e("keyword c"),i=e("keyword d"),o=e("operator"),a={type:"atom",style:"atom"};return{if:e("if"),while:t,with:t,else:n,do:n,try:n,finally:n,return:i,break:i,continue:i,new:e("new"),delete:r,void:r,throw:r,debugger:e("debugger"),var:e("var"),const:e("var"),let:e("var"),function:e("function"),catch:e("catch"),for:e("for"),switch:e("switch"),case:e("case"),default:e("default"),in:o,typeof:o,instanceof:o,true:a,false:a,null:a,undefined:a,NaN:a,Infinity:a,this:e("this"),class:e("class"),super:e("atom"),yield:r,export:e("export"),import:e("import"),extends:r,await:r}}(),d=/[+\-*&%=<>!?|~^@]/,h=/^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;function p(e,t,n){return r=e,i=n,t}function m(e,t){var n,r=e.next();if('"'==r||"'"==r)return t.tokenize=(n=r,function(e,t){var r,i=!1;if(l&&"@"==e.peek()&&e.match(h))return t.tokenize=m,p("jsonld-keyword","meta");for(;null!=(r=e.next())&&(r!=n||i);)i=!i&&"\\"==r;return i||(t.tokenize=m),p("string","string")}),t.tokenize(e,t);if("."==r&&e.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/))return p("number","number");if("."==r&&e.match(".."))return p("spread","meta");if(/[\[\]{}\(\),;\:\.]/.test(r))return p(r);if("="==r&&e.eat(">"))return p("=>","operator");if("0"==r&&e.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/))return p("number","number");if(/\d/.test(r))return e.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/),p("number","number");if("/"==r)return e.eat("*")?(t.tokenize=g,g(e,t)):e.eat("/")?(e.skipToEnd(),p("comment","comment")):$e(e,t,1)?(function(e){for(var t,n=!1,r=!1;null!=(t=e.next());){if(!n){if("/"==t&&!r)return;"["==t?r=!0:r&&"]"==t&&(r=!1)}n=!n&&"\\"==t}}(e),e.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/),p("regexp","string-2")):(e.eat("="),p("operator","operator",e.current()));if("`"==r)return t.tokenize=v,v(e,t);if("#"==r&&"!"==e.peek())return e.skipToEnd(),p("meta","meta");if("#"==r&&e.eatWhile(u))return p("variable","property");if("<"==r&&e.match("!--")||"-"==r&&e.match("->")&&!/\S/.test(e.string.slice(0,e.start)))return e.skipToEnd(),p("comment","comment");if(d.test(r))return">"==r&&t.lexical&&">"==t.lexical.type||(e.eat("=")?"!"!=r&&"="!=r||e.eat("="):/[<>*+\-|&?]/.test(r)&&(e.eat(r),">"==r&&e.eat(r))),"?"==r&&e.eat(".")?p("."):p("operator","operator",e.current());if(u.test(r)){e.eatWhile(u);var i=e.current();if("."!=t.lastType){if(f.propertyIsEnumerable(i)){var o=f[i];return p(o.type,o.style,i)}if("async"==i&&e.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/,!1))return p("async","keyword",i)}return p("variable","variable",i)}}function g(e,t){for(var n,r=!1;n=e.next();){if("/"==n&&r){t.tokenize=m;break}r="*"==n}return p("comment","comment")}function v(e,t){for(var n,r=!1;null!=(n=e.next());){if(!r&&("`"==n||"$"==n&&e.eat("{"))){t.tokenize=m;break}r=!r&&"\\"==n}return p("quasi","string-2",e.current())}function y(e,t){t.fatArrowAt&&(t.fatArrowAt=null);var n=e.string.indexOf("=>",e.start);if(!(n<0)){if(c){var r=/:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(e.string.slice(e.start,n));r&&(n=r.index)}for(var i=0,o=!1,a=n-1;a>=0;--a){var l=e.string.charAt(a),s="([{}])".indexOf(l);if(s>=0&&s<3){if(!i){++a;break}if(0==--i){"("==l&&(o=!0);break}}else if(s>=3&&s<6)++i;else if(u.test(l))o=!0;else if(/["'\/`]/.test(l))for(;;--a){if(0==a)return;if(e.string.charAt(a-1)==l&&"\\"!=e.string.charAt(a-2)){a--;break}}else if(o&&!i){++a;break}}o&&!i&&(t.fatArrowAt=a)}}var b={atom:!0,number:!0,variable:!0,string:!0,regexp:!0,this:!0,"jsonld-keyword":!0};function x(e,t,n,r,i,o){this.indented=e,this.column=t,this.type=n,this.prev=i,this.info=o,null!=r&&(this.align=r)}function w(e,t){for(var n=e.localVars;n;n=n.next)if(n.name==t)return!0;for(var r=e.context;r;r=r.prev)for(n=r.vars;n;n=n.next)if(n.name==t)return!0}var k={state:null,column:null,marked:null,cc:null};function C(){for(var e=arguments.length-1;e>=0;e--)k.cc.push(arguments[e])}function S(){return C.apply(null,arguments),!0}function T(e,t){for(var n=t;n;n=n.next)if(n.name==e)return!0;return!1}function L(e){var t=k.state;if(k.marked="def",t.context)if("var"==t.lexical.info&&t.context&&t.context.block){var r=function e(t,n){if(n){if(n.block){var r=e(t,n.prev);return r?r==n.prev?n:new O(r,n.vars,!0):null}return T(t,n.vars)?n:new O(n.prev,new N(t,n.vars),!1)}return null}(e,t.context);if(null!=r)return void(t.context=r)}else if(!T(e,t.localVars))return void(t.localVars=new N(e,t.localVars));n.globalVars&&!T(e,t.globalVars)&&(t.globalVars=new N(e,t.globalVars))}function M(e){return"public"==e||"private"==e||"protected"==e||"abstract"==e||"readonly"==e}function O(e,t,n){this.prev=e,this.vars=t,this.block=n}function N(e,t){this.name=e,this.next=t}var A=new N("this",new N("arguments",null));function E(){k.state.context=new O(k.state.context,k.state.localVars,!1),k.state.localVars=A}function z(){k.state.context=new O(k.state.context,k.state.localVars,!0),k.state.localVars=null}function P(){k.state.localVars=k.state.context.vars,k.state.context=k.state.context.prev}function D(e,t){var n=function(){var n=k.state,r=n.indented;if("stat"==n.lexical.type)r=n.lexical.indented;else for(var i=n.lexical;i&&")"==i.type&&i.align;i=i.prev)r=i.indented;n.lexical=new x(r,k.stream.column(),e,null,n.lexical,t)};return n.lex=!0,n}function I(){var e=k.state;e.lexical.prev&&(")"==e.lexical.type&&(e.indented=e.lexical.indented),e.lexical=e.lexical.prev)}function F(e){return function t(n){return n==e?S():";"==e||"}"==n||")"==n||"]"==n?C():S(t)}}function _(e,t){return"var"==e?S(D("vardef",t),ye,F(";"),I):"keyword a"==e?S(D("form"),B,_,I):"keyword b"==e?S(D("form"),_,I):"keyword d"==e?k.stream.match(/^\s*$/,!1)?S():S(D("stat"),q,F(";"),I):"debugger"==e?S(F(";")):"{"==e?S(D("}"),z,oe,I,P):";"==e?S():"if"==e?("else"==k.state.lexical.info&&k.state.cc[k.state.cc.length-1]==I&&k.state.cc.pop()(),S(D("form"),B,_,I,Se)):"function"==e?S(Oe):"for"==e?S(D("form"),Te,_,I):"class"==e||c&&"interface"==t?(k.marked="keyword",S(D("form","class"==e?e:t),Pe,I)):"variable"==e?c&&"declare"==t?(k.marked="keyword",S(_)):c&&("module"==t||"enum"==t||"type"==t)&&k.stream.match(/^\s*\w/,!1)?(k.marked="keyword","enum"==t?S(Ve):"type"==t?S(Ae,F("operator"),ue,F(";")):S(D("form"),be,F("{"),D("}"),oe,I,I)):c&&"namespace"==t?(k.marked="keyword",S(D("form"),H,_,I)):c&&"abstract"==t?(k.marked="keyword",S(_)):S(D("stat"),Q):"switch"==e?S(D("form"),B,F("{"),D("}","switch"),z,oe,I,I,P):"case"==e?S(H,F(":")):"default"==e?S(F(":")):"catch"==e?S(D("form"),E,W,_,I,P):"export"==e?S(D("stat"),_e,I):"import"==e?S(D("stat"),He,I):"async"==e?S(_):"@"==t?S(H,_):C(D("stat"),H,F(";"),I)}function W(e){if("("==e)return S(Ee,F(")"))}function H(e,t){return j(e,t,!1)}function R(e,t){return j(e,t,!0)}function B(e){return"("!=e?C():S(D(")"),q,F(")"),I)}function j(e,t,n){if(k.state.fatArrowAt==k.stream.start){var r=n?X:G;if("("==e)return S(E,D(")"),re(Ee,")"),I,F("=>"),r,P);if("variable"==e)return C(E,be,F("=>"),r,P)}var i=n?V:U;return b.hasOwnProperty(e)?S(i):"function"==e?S(Oe,i):"class"==e||c&&"interface"==t?(k.marked="keyword",S(D("form"),ze,I)):"keyword c"==e||"async"==e?S(n?R:H):"("==e?S(D(")"),q,F(")"),I,i):"operator"==e||"spread"==e?S(n?R:H):"["==e?S(D("]"),Ue,I,i):"{"==e?ie(ee,"}",null,i):"quasi"==e?C(K,i):"new"==e?S(function(e){return function(t){return"."==t?S(e?Y:Z):"variable"==t&&c?S(me,e?V:U):C(e?R:H)}}(n)):"import"==e?S(H):S()}function q(e){return e.match(/[;\}\)\],]/)?C():C(H)}function U(e,t){return","==e?S(q):V(e,t,!1)}function V(e,t,n){var r=0==n?U:V,i=0==n?H:R;return"=>"==e?S(E,n?X:G,P):"operator"==e?/\+\+|--/.test(t)||c&&"!"==t?S(r):c&&"<"==t&&k.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/,!1)?S(D(">"),re(ue,">"),I,r):"?"==t?S(H,F(":"),i):S(i):"quasi"==e?C(K,r):";"!=e?"("==e?ie(R,")","call",r):"."==e?S(J,r):"["==e?S(D("]"),q,F("]"),I,r):c&&"as"==t?(k.marked="keyword",S(ue,r)):"regexp"==e?(k.state.lastType=k.marked="operator",k.stream.backUp(k.stream.pos-k.stream.start-1),S(i)):void 0:void 0}function K(e,t){return"quasi"!=e?C():"${"!=t.slice(t.length-2)?S(K):S(H,$)}function $(e){if("}"==e)return k.marked="string-2",k.state.tokenize=v,S(K)}function G(e){return y(k.stream,k.state),C("{"==e?_:H)}function X(e){return y(k.stream,k.state),C("{"==e?_:R)}function Z(e,t){if("target"==t)return k.marked="keyword",S(U)}function Y(e,t){if("target"==t)return k.marked="keyword",S(V)}function Q(e){return":"==e?S(I,_):C(U,F(";"),I)}function J(e){if("variable"==e)return k.marked="property",S()}function ee(e,t){return"async"==e?(k.marked="property",S(ee)):"variable"==e||"keyword"==k.style?(k.marked="property","get"==t||"set"==t?S(te):(c&&k.state.fatArrowAt==k.stream.start&&(n=k.stream.match(/^\s*:\s*/,!1))&&(k.state.fatArrowAt=k.stream.pos+n[0].length),S(ne))):"number"==e||"string"==e?(k.marked=l?"property":k.style+" property",S(ne)):"jsonld-keyword"==e?S(ne):c&&M(t)?(k.marked="keyword",S(ee)):"["==e?S(H,ae,F("]"),ne):"spread"==e?S(R,ne):"*"==t?(k.marked="keyword",S(ee)):":"==e?C(ne):void 0;var n}function te(e){return"variable"!=e?C(ne):(k.marked="property",S(Oe))}function ne(e){return":"==e?S(R):"("==e?C(Oe):void 0}function re(e,t,n){function r(i,o){if(n?n.indexOf(i)>-1:","==i){var a=k.state.lexical;return"call"==a.info&&(a.pos=(a.pos||0)+1),S((function(n,r){return n==t||r==t?C():C(e)}),r)}return i==t||o==t?S():n&&n.indexOf(";")>-1?C(e):S(F(t))}return function(n,i){return n==t||i==t?S():C(e,r)}}function ie(e,t,n){for(var r=3;r<arguments.length;r++)k.cc.push(arguments[r]);return S(D(t,n),re(e,t),I)}function oe(e){return"}"==e?S():C(_,oe)}function ae(e,t){if(c){if(":"==e)return S(ue);if("?"==t)return S(ae)}}function le(e,t){if(c&&(":"==e||"in"==t))return S(ue)}function se(e){if(c&&":"==e)return k.stream.match(/^\s*\w+\s+is\b/,!1)?S(H,ce,ue):S(ue)}function ce(e,t){if("is"==t)return k.marked="keyword",S()}function ue(e,t){return"keyof"==t||"typeof"==t||"infer"==t?(k.marked="keyword",S("typeof"==t?R:ue)):"variable"==e||"void"==t?(k.marked="type",S(pe)):"|"==t||"&"==t?S(ue):"string"==e||"number"==e||"atom"==e?S(pe):"["==e?S(D("]"),re(ue,"]",","),I,pe):"{"==e?S(D("}"),re(de,"}",",;"),I,pe):"("==e?S(re(he,")"),fe,pe):"<"==e?S(re(ue,">"),ue):void 0}function fe(e){if("=>"==e)return S(ue)}function de(e,t){return"variable"==e||"keyword"==k.style?(k.marked="property",S(de)):"?"==t||"number"==e||"string"==e?S(de):":"==e?S(ue):"["==e?S(F("variable"),le,F("]"),de):"("==e?C(Ne,de):void 0}function he(e,t){return"variable"==e&&k.stream.match(/^\s*[?:]/,!1)||"?"==t?S(he):":"==e?S(ue):"spread"==e?S(he):C(ue)}function pe(e,t){return"<"==t?S(D(">"),re(ue,">"),I,pe):"|"==t||"."==e||"&"==t?S(ue):"["==e?S(ue,F("]"),pe):"extends"==t||"implements"==t?(k.marked="keyword",S(ue)):"?"==t?S(ue,F(":"),ue):void 0}function me(e,t){if("<"==t)return S(D(">"),re(ue,">"),I,pe)}function ge(){return C(ue,ve)}function ve(e,t){if("="==t)return S(ue)}function ye(e,t){return"enum"==t?(k.marked="keyword",S(Ve)):C(be,ae,ke,Ce)}function be(e,t){return c&&M(t)?(k.marked="keyword",S(be)):"variable"==e?(L(t),S()):"spread"==e?S(be):"["==e?ie(we,"]"):"{"==e?ie(xe,"}"):void 0}function xe(e,t){return"variable"!=e||k.stream.match(/^\s*:/,!1)?("variable"==e&&(k.marked="property"),"spread"==e?S(be):"}"==e?C():"["==e?S(H,F("]"),F(":"),xe):S(F(":"),be,ke)):(L(t),S(ke))}function we(){return C(be,ke)}function ke(e,t){if("="==t)return S(R)}function Ce(e){if(","==e)return S(ye)}function Se(e,t){if("keyword b"==e&&"else"==t)return S(D("form","else"),_,I)}function Te(e,t){return"await"==t?S(Te):"("==e?S(D(")"),Le,I):void 0}function Le(e){return"var"==e?S(ye,Me):"variable"==e?S(Me):C(Me)}function Me(e,t){return")"==e?S():";"==e?S(Me):"in"==t||"of"==t?(k.marked="keyword",S(H,Me)):C(H,Me)}function Oe(e,t){return"*"==t?(k.marked="keyword",S(Oe)):"variable"==e?(L(t),S(Oe)):"("==e?S(E,D(")"),re(Ee,")"),I,se,_,P):c&&"<"==t?S(D(">"),re(ge,">"),I,Oe):void 0}function Ne(e,t){return"*"==t?(k.marked="keyword",S(Ne)):"variable"==e?(L(t),S(Ne)):"("==e?S(E,D(")"),re(Ee,")"),I,se,P):c&&"<"==t?S(D(">"),re(ge,">"),I,Ne):void 0}function Ae(e,t){return"keyword"==e||"variable"==e?(k.marked="type",S(Ae)):"<"==t?S(D(">"),re(ge,">"),I):void 0}function Ee(e,t){return"@"==t&&S(H,Ee),"spread"==e?S(Ee):c&&M(t)?(k.marked="keyword",S(Ee)):c&&"this"==e?S(ae,ke):C(be,ae,ke)}function ze(e,t){return"variable"==e?Pe(e,t):De(e,t)}function Pe(e,t){if("variable"==e)return L(t),S(De)}function De(e,t){return"<"==t?S(D(">"),re(ge,">"),I,De):"extends"==t||"implements"==t||c&&","==e?("implements"==t&&(k.marked="keyword"),S(c?ue:H,De)):"{"==e?S(D("}"),Ie,I):void 0}function Ie(e,t){return"async"==e||"variable"==e&&("static"==t||"get"==t||"set"==t||c&&M(t))&&k.stream.match(/^\s+[\w$\xa1-\uffff]/,!1)?(k.marked="keyword",S(Ie)):"variable"==e||"keyword"==k.style?(k.marked="property",S(Fe,Ie)):"number"==e||"string"==e?S(Fe,Ie):"["==e?S(H,ae,F("]"),Fe,Ie):"*"==t?(k.marked="keyword",S(Ie)):c&&"("==e?C(Ne,Ie):";"==e||","==e?S(Ie):"}"==e?S():"@"==t?S(H,Ie):void 0}function Fe(e,t){if("?"==t)return S(Fe);if(":"==e)return S(ue,ke);if("="==t)return S(R);var n=k.state.lexical.prev;return C(n&&"interface"==n.info?Ne:Oe)}function _e(e,t){return"*"==t?(k.marked="keyword",S(qe,F(";"))):"default"==t?(k.marked="keyword",S(H,F(";"))):"{"==e?S(re(We,"}"),qe,F(";")):C(_)}function We(e,t){return"as"==t?(k.marked="keyword",S(F("variable"))):"variable"==e?C(R,We):void 0}function He(e){return"string"==e?S():"("==e?C(H):C(Re,Be,qe)}function Re(e,t){return"{"==e?ie(Re,"}"):("variable"==e&&L(t),"*"==t&&(k.marked="keyword"),S(je))}function Be(e){if(","==e)return S(Re,Be)}function je(e,t){if("as"==t)return k.marked="keyword",S(Re)}function qe(e,t){if("from"==t)return k.marked="keyword",S(H)}function Ue(e){return"]"==e?S():C(re(R,"]"))}function Ve(){return C(D("form"),be,F("{"),D("}"),re(Ke,"}"),I,I)}function Ke(){return C(be,ke)}function $e(e,t,n){return t.tokenize==m&&/^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(t.lastType)||"quasi"==t.lastType&&/\{\s*$/.test(e.string.slice(0,e.pos-(n||0)))}return P.lex=!0,I.lex=!0,{startState:function(e){var t={tokenize:m,lastType:"sof",cc:[],lexical:new x((e||0)-o,0,"block",!1),localVars:n.localVars,context:n.localVars&&new O(null,null,!1),indented:e||0};return n.globalVars&&"object"==typeof n.globalVars&&(t.globalVars=n.globalVars),t},token:function(e,t){if(e.sol()&&(t.lexical.hasOwnProperty("align")||(t.lexical.align=!1),t.indented=e.indentation(),y(e,t)),t.tokenize!=g&&e.eatSpace())return null;var n=t.tokenize(e,t);return"comment"==r?n:(t.lastType="operator"!=r||"++"!=i&&"--"!=i?r:"incdec",function(e,t,n,r,i){var o=e.cc;for(k.state=e,k.stream=i,k.marked=null,k.cc=o,k.style=t,e.lexical.hasOwnProperty("align")||(e.lexical.align=!0);;)if((o.length?o.pop():s?H:_)(n,r)){for(;o.length&&o[o.length-1].lex;)o.pop()();return k.marked?k.marked:"variable"==n&&w(e,r)?"variable-2":t}}(t,n,r,i,e))},indent:function(t,r){if(t.tokenize==g||t.tokenize==v)return e.Pass;if(t.tokenize!=m)return 0;var i,l=r&&r.charAt(0),s=t.lexical;if(!/^\s*else\b/.test(r))for(var c=t.cc.length-1;c>=0;--c){var u=t.cc[c];if(u==I)s=s.prev;else if(u!=Se)break}for(;("stat"==s.type||"form"==s.type)&&("}"==l||(i=t.cc[t.cc.length-1])&&(i==U||i==V)&&!/^[,\.=+\-*:?[\(]/.test(r));)s=s.prev;a&&")"==s.type&&"stat"==s.prev.type&&(s=s.prev);var f=s.type,h=l==f;return"vardef"==f?s.indented+("operator"==t.lastType||","==t.lastType?s.info.length+1:0):"form"==f&&"{"==l?s.indented:"form"==f?s.indented+o:"stat"==f?s.indented+(function(e,t){return"operator"==e.lastType||","==e.lastType||d.test(t.charAt(0))||/[,.]/.test(t.charAt(0))}(t,r)?a||o:0):"switch"!=s.info||h||0==n.doubleIndentSwitch?s.align?s.column+(h?0:1):s.indented+(h?0:o):s.indented+(/^(?:case|default)\b/.test(r)?o:2*o)},electricInput:/^\s*(?:case .*?:|default:|\{|\})$/,blockCommentStart:s?null:"/*",blockCommentEnd:s?null:"*/",blockCommentContinue:s?null:" * ",lineComment:s?null:"//",fold:"brace",closeBrackets:"()[]{}''\"\"``",helperType:s?"json":"javascript",jsonldMode:l,jsonMode:s,expressionAllowed:$e,skipExpression:function(e){var t=e.cc[e.cc.length-1];t!=H&&t!=R||e.cc.pop()}}})),e.registerHelper("wordChars","javascript",/[\w$]/),e.defineMIME("text/javascript","javascript"),e.defineMIME("text/ecmascript","javascript"),e.defineMIME("application/javascript","javascript"),e.defineMIME("application/x-javascript","javascript"),e.defineMIME("application/ecmascript","javascript"),e.defineMIME("application/json",{name:"javascript",json:!0}),e.defineMIME("application/x-json",{name:"javascript",json:!0}),e.defineMIME("application/ld+json",{name:"javascript",jsonld:!0}),e.defineMIME("text/typescript",{name:"javascript",typescript:!0}),e.defineMIME("application/typescript",{name:"javascript",typescript:!0})}(n(0))},function(e,t,n){!function(e){"use strict";var t={autoSelfClosers:{area:!0,base:!0,br:!0,col:!0,command:!0,embed:!0,frame:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0,menuitem:!0},implicitlyClosed:{dd:!0,li:!0,optgroup:!0,option:!0,p:!0,rp:!0,rt:!0,tbody:!0,td:!0,tfoot:!0,th:!0,tr:!0},contextGrabbers:{dd:{dd:!0,dt:!0},dt:{dd:!0,dt:!0},li:{li:!0},option:{option:!0,optgroup:!0},optgroup:{optgroup:!0},p:{address:!0,article:!0,aside:!0,blockquote:!0,dir:!0,div:!0,dl:!0,fieldset:!0,footer:!0,form:!0,h1:!0,h2:!0,h3:!0,h4:!0,h5:!0,h6:!0,header:!0,hgroup:!0,hr:!0,menu:!0,nav:!0,ol:!0,p:!0,pre:!0,section:!0,table:!0,ul:!0},rp:{rp:!0,rt:!0},rt:{rp:!0,rt:!0},tbody:{tbody:!0,tfoot:!0},td:{td:!0,th:!0},tfoot:{tbody:!0},th:{td:!0,th:!0},thead:{tbody:!0,tfoot:!0},tr:{tr:!0}},doNotIndent:{pre:!0},allowUnquoted:!0,allowMissing:!0,caseFold:!0},n={autoSelfClosers:{},implicitlyClosed:{},contextGrabbers:{},doNotIndent:{},allowUnquoted:!1,allowMissing:!1,allowMissingTagName:!1,caseFold:!1};e.defineMode("xml",(function(r,i){var o,a,l=r.indentUnit,s={},c=i.htmlMode?t:n;for(var u in c)s[u]=c[u];for(var u in i)s[u]=i[u];function f(e,t){function n(n){return t.tokenize=n,n(e,t)}var r=e.next();return"<"==r?e.eat("!")?e.eat("[")?e.match("CDATA[")?n(h("atom","]]>")):null:e.match("--")?n(h("comment","--\x3e")):e.match("DOCTYPE",!0,!0)?(e.eatWhile(/[\w\._\-]/),n(function e(t){return function(n,r){for(var i;null!=(i=n.next());){if("<"==i)return r.tokenize=e(t+1),r.tokenize(n,r);if(">"==i){if(1==t){r.tokenize=f;break}return r.tokenize=e(t-1),r.tokenize(n,r)}}return"meta"}}(1))):null:e.eat("?")?(e.eatWhile(/[\w\._\-]/),t.tokenize=h("meta","?>"),"meta"):(o=e.eat("/")?"closeTag":"openTag",t.tokenize=d,"tag bracket"):"&"==r?(e.eat("#")?e.eat("x")?e.eatWhile(/[a-fA-F\d]/)&&e.eat(";"):e.eatWhile(/[\d]/)&&e.eat(";"):e.eatWhile(/[\w\.\-:]/)&&e.eat(";"))?"atom":"error":(e.eatWhile(/[^&<]/),null)}function d(e,t){var n,r,i=e.next();if(">"==i||"/"==i&&e.eat(">"))return t.tokenize=f,o=">"==i?"endTag":"selfcloseTag","tag bracket";if("="==i)return o="equals",null;if("<"==i){t.tokenize=f,t.state=v,t.tagName=t.tagStart=null;var a=t.tokenize(e,t);return a?a+" tag error":"tag error"}return/[\'\"]/.test(i)?(t.tokenize=(n=i,(r=function(e,t){for(;!e.eol();)if(e.next()==n){t.tokenize=d;break}return"string"}).isInAttribute=!0,r),t.stringStartCol=e.column(),t.tokenize(e,t)):(e.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/),"word")}function h(e,t){return function(n,r){for(;!n.eol();){if(n.match(t)){r.tokenize=f;break}n.next()}return e}}function p(e,t,n){this.prev=e.context,this.tagName=t,this.indent=e.indented,this.startOfLine=n,(s.doNotIndent.hasOwnProperty(t)||e.context&&e.context.noIndent)&&(this.noIndent=!0)}function m(e){e.context&&(e.context=e.context.prev)}function g(e,t){for(var n;;){if(!e.context)return;if(n=e.context.tagName,!s.contextGrabbers.hasOwnProperty(n)||!s.contextGrabbers[n].hasOwnProperty(t))return;m(e)}}function v(e,t,n){return"openTag"==e?(n.tagStart=t.column(),y):"closeTag"==e?b:v}function y(e,t,n){return"word"==e?(n.tagName=t.current(),a="tag",k):s.allowMissingTagName&&"endTag"==e?(a="tag bracket",k(e,0,n)):(a="error",y)}function b(e,t,n){if("word"==e){var r=t.current();return n.context&&n.context.tagName!=r&&s.implicitlyClosed.hasOwnProperty(n.context.tagName)&&m(n),n.context&&n.context.tagName==r||!1===s.matchClosing?(a="tag",x):(a="tag error",w)}return s.allowMissingTagName&&"endTag"==e?(a="tag bracket",x(e,0,n)):(a="error",w)}function x(e,t,n){return"endTag"!=e?(a="error",x):(m(n),v)}function w(e,t,n){return a="error",x(e,0,n)}function k(e,t,n){if("word"==e)return a="attribute",C;if("endTag"==e||"selfcloseTag"==e){var r=n.tagName,i=n.tagStart;return n.tagName=n.tagStart=null,"selfcloseTag"==e||s.autoSelfClosers.hasOwnProperty(r)?g(n,r):(g(n,r),n.context=new p(n,r,i==n.indented)),v}return a="error",k}function C(e,t,n){return"equals"==e?S:(s.allowMissing||(a="error"),k(e,0,n))}function S(e,t,n){return"string"==e?T:"word"==e&&s.allowUnquoted?(a="string",k):(a="error",k(e,0,n))}function T(e,t,n){return"string"==e?T:k(e,0,n)}return f.isInText=!0,{startState:function(e){var t={tokenize:f,state:v,indented:e||0,tagName:null,tagStart:null,context:null};return null!=e&&(t.baseIndent=e),t},token:function(e,t){if(!t.tagName&&e.sol()&&(t.indented=e.indentation()),e.eatSpace())return null;o=null;var n=t.tokenize(e,t);return(n||o)&&"comment"!=n&&(a=null,t.state=t.state(o||n,e,t),a&&(n="error"==a?n+" error":a)),n},indent:function(t,n,r){var i=t.context;if(t.tokenize.isInAttribute)return t.tagStart==t.indented?t.stringStartCol+1:t.indented+l;if(i&&i.noIndent)return e.Pass;if(t.tokenize!=d&&t.tokenize!=f)return r?r.match(/^(\s*)/)[0].length:0;if(t.tagName)return!1!==s.multilineTagIndentPastTag?t.tagStart+t.tagName.length+2:t.tagStart+l*(s.multilineTagIndentFactor||1);if(s.alignCDATA&&/<!\[CDATA\[/.test(n))return 0;var o=n&&/^<(\/)?([\w_:\.-]*)/.exec(n);if(o&&o[1])for(;i;){if(i.tagName==o[2]){i=i.prev;break}if(!s.implicitlyClosed.hasOwnProperty(i.tagName))break;i=i.prev}else if(o)for(;i;){var a=s.contextGrabbers[i.tagName];if(!a||!a.hasOwnProperty(o[2]))break;i=i.prev}for(;i&&i.prev&&!i.startOfLine;)i=i.prev;return i?i.indent+l:t.baseIndent||0},electricInput:/<\/[\s\w:]+>$/,blockCommentStart:"\x3c!--",blockCommentEnd:"--\x3e",configuration:s.htmlMode?"html":"xml",helperType:s.htmlMode?"html":"xml",skipAttribute:function(e){e.state==S&&(e.state=k)},xmlCurrentTag:function(e){return e.tagName?{name:e.tagName,close:"closeTag"==e.type}:null},xmlCurrentContext:function(e){for(var t=[],n=e.context;n;n=n.prev)n.tagName&&t.push(n.tagName);return t.reverse()}}})),e.defineMIME("text/xml","xml"),e.defineMIME("application/xml","xml"),e.mimeModes.hasOwnProperty("text/html")||e.defineMIME("text/html",{name:"xml",htmlMode:!0})}(n(0))},function(e,t,n){!function(e){function t(t,n,r){var i,o=t.getWrapperElement();return(i=o.appendChild(document.createElement("div"))).className=r?"CodeMirror-dialog CodeMirror-dialog-bottom":"CodeMirror-dialog CodeMirror-dialog-top","string"==typeof n?i.innerHTML=n:i.appendChild(n),e.addClass(o,"dialog-opened"),i}function n(e,t){e.state.currentNotificationClose&&e.state.currentNotificationClose(),e.state.currentNotificationClose=t}e.defineExtension("openDialog",(function(r,i,o){o||(o={}),n(this,null);var a=t(this,r,o.bottom),l=!1,s=this;function c(t){if("string"==typeof t)f.value=t;else{if(l)return;l=!0,e.rmClass(a.parentNode,"dialog-opened"),a.parentNode.removeChild(a),s.focus(),o.onClose&&o.onClose(a)}}var u,f=a.getElementsByTagName("input")[0];return f?(f.focus(),o.value&&(f.value=o.value,!1!==o.selectValueOnOpen&&f.select()),o.onInput&&e.on(f,"input",(function(e){o.onInput(e,f.value,c)})),o.onKeyUp&&e.on(f,"keyup",(function(e){o.onKeyUp(e,f.value,c)})),e.on(f,"keydown",(function(t){o&&o.onKeyDown&&o.onKeyDown(t,f.value,c)||((27==t.keyCode||!1!==o.closeOnEnter&&13==t.keyCode)&&(f.blur(),e.e_stop(t),c()),13==t.keyCode&&i(f.value,t))})),!1!==o.closeOnBlur&&e.on(f,"blur",c)):(u=a.getElementsByTagName("button")[0])&&(e.on(u,"click",(function(){c(),s.focus()})),!1!==o.closeOnBlur&&e.on(u,"blur",c),u.focus()),c})),e.defineExtension("openConfirm",(function(r,i,o){n(this,null);var a=t(this,r,o&&o.bottom),l=a.getElementsByTagName("button"),s=!1,c=this,u=1;function f(){s||(s=!0,e.rmClass(a.parentNode,"dialog-opened"),a.parentNode.removeChild(a),c.focus())}l[0].focus();for(var d=0;d<l.length;++d){var h=l[d];!function(t){e.on(h,"click",(function(n){e.e_preventDefault(n),f(),t&&t(c)}))}(i[d]),e.on(h,"blur",(function(){--u,setTimeout((function(){u<=0&&f()}),200)})),e.on(h,"focus",(function(){++u}))}})),e.defineExtension("openNotification",(function(r,i){n(this,c);var o,a=t(this,r,i&&i.bottom),l=!1,s=i&&void 0!==i.duration?i.duration:5e3;function c(){l||(l=!0,clearTimeout(o),e.rmClass(a.parentNode,"dialog-opened"),a.parentNode.removeChild(a))}return e.on(a,"click",(function(t){e.e_preventDefault(t),c()})),s&&(o=setTimeout(c,s)),c}))}(n(0))},function(e,t,n){!function(e){"use strict";function t(e){for(var t={},n=0;n<e.length;++n)t[e[n].toLowerCase()]=!0;return t}e.defineMode("css",(function(t,n){var r=n.inline;n.propertyKeywords||(n=e.resolveMode("text/css"));var i,o,a=t.indentUnit,l=n.tokenHooks,s=n.documentTypes||{},c=n.mediaTypes||{},u=n.mediaFeatures||{},f=n.mediaValueKeywords||{},d=n.propertyKeywords||{},h=n.nonStandardPropertyKeywords||{},p=n.fontProperties||{},m=n.counterDescriptors||{},g=n.colorKeywords||{},v=n.valueKeywords||{},y=n.allowNested,b=n.lineComment,x=!0===n.supportsAtComponent;function w(e,t){return i=t,e}function k(e,t){var n=e.next();if(l[n]){var r=l[n](e,t);if(!1!==r)return r}return"@"==n?(e.eatWhile(/[\w\\\-]/),w("def",e.current())):"="==n||("~"==n||"|"==n)&&e.eat("=")?w(null,"compare"):'"'==n||"'"==n?(t.tokenize=C(n),t.tokenize(e,t)):"#"==n?(e.eatWhile(/[\w\\\-]/),w("atom","hash")):"!"==n?(e.match(/^\s*\w*/),w("keyword","important")):/\d/.test(n)||"."==n&&e.eat(/\d/)?(e.eatWhile(/[\w.%]/),w("number","unit")):"-"!==n?/[,+>*\/]/.test(n)?w(null,"select-op"):"."==n&&e.match(/^-?[_a-z][_a-z0-9-]*/i)?w("qualifier","qualifier"):/[:;{}\[\]\(\)]/.test(n)?w(null,n):e.match(/[\w-.]+(?=\()/)?(/^(url(-prefix)?|domain|regexp)$/.test(e.current().toLowerCase())&&(t.tokenize=S),w("variable callee","variable")):/[\w\\\-]/.test(n)?(e.eatWhile(/[\w\\\-]/),w("property","word")):w(null,null):/[\d.]/.test(e.peek())?(e.eatWhile(/[\w.%]/),w("number","unit")):e.match(/^-[\w\\\-]*/)?(e.eatWhile(/[\w\\\-]/),e.match(/^\s*:/,!1)?w("variable-2","variable-definition"):w("variable-2","variable")):e.match(/^\w+-/)?w("meta","meta"):void 0}function C(e){return function(t,n){for(var r,i=!1;null!=(r=t.next());){if(r==e&&!i){")"==e&&t.backUp(1);break}i=!i&&"\\"==r}return(r==e||!i&&")"!=e)&&(n.tokenize=null),w("string","string")}}function S(e,t){return e.next(),e.match(/\s*[\"\')]/,!1)?t.tokenize=null:t.tokenize=C(")"),w(null,"(")}function T(e,t,n){this.type=e,this.indent=t,this.prev=n}function L(e,t,n,r){return e.context=new T(n,t.indentation()+(!1===r?0:a),e.context),n}function M(e){return e.context.prev&&(e.context=e.context.prev),e.context.type}function O(e,t,n){return E[n.context.type](e,t,n)}function N(e,t,n,r){for(var i=r||1;i>0;i--)n.context=n.context.prev;return O(e,t,n)}function A(e){var t=e.current().toLowerCase();o=v.hasOwnProperty(t)?"atom":g.hasOwnProperty(t)?"keyword":"variable"}var E={top:function(e,t,n){if("{"==e)return L(n,t,"block");if("}"==e&&n.context.prev)return M(n);if(x&&/@component/i.test(e))return L(n,t,"atComponentBlock");if(/^@(-moz-)?document$/i.test(e))return L(n,t,"documentTypes");if(/^@(media|supports|(-moz-)?document|import)$/i.test(e))return L(n,t,"atBlock");if(/^@(font-face|counter-style)/i.test(e))return n.stateArg=e,"restricted_atBlock_before";if(/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(e))return"keyframes";if(e&&"@"==e.charAt(0))return L(n,t,"at");if("hash"==e)o="builtin";else if("word"==e)o="tag";else{if("variable-definition"==e)return"maybeprop";if("interpolation"==e)return L(n,t,"interpolation");if(":"==e)return"pseudo";if(y&&"("==e)return L(n,t,"parens")}return n.context.type},block:function(e,t,n){if("word"==e){var r=t.current().toLowerCase();return d.hasOwnProperty(r)?(o="property","maybeprop"):h.hasOwnProperty(r)?(o="string-2","maybeprop"):y?(o=t.match(/^\s*:(?:\s|$)/,!1)?"property":"tag","block"):(o+=" error","maybeprop")}return"meta"==e?"block":y||"hash"!=e&&"qualifier"!=e?E.top(e,t,n):(o="error","block")},maybeprop:function(e,t,n){return":"==e?L(n,t,"prop"):O(e,t,n)},prop:function(e,t,n){if(";"==e)return M(n);if("{"==e&&y)return L(n,t,"propBlock");if("}"==e||"{"==e)return N(e,t,n);if("("==e)return L(n,t,"parens");if("hash"!=e||/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(t.current())){if("word"==e)A(t);else if("interpolation"==e)return L(n,t,"interpolation")}else o+=" error";return"prop"},propBlock:function(e,t,n){return"}"==e?M(n):"word"==e?(o="property","maybeprop"):n.context.type},parens:function(e,t,n){return"{"==e||"}"==e?N(e,t,n):")"==e?M(n):"("==e?L(n,t,"parens"):"interpolation"==e?L(n,t,"interpolation"):("word"==e&&A(t),"parens")},pseudo:function(e,t,n){return"meta"==e?"pseudo":"word"==e?(o="variable-3",n.context.type):O(e,t,n)},documentTypes:function(e,t,n){return"word"==e&&s.hasOwnProperty(t.current())?(o="tag",n.context.type):E.atBlock(e,t,n)},atBlock:function(e,t,n){if("("==e)return L(n,t,"atBlock_parens");if("}"==e||";"==e)return N(e,t,n);if("{"==e)return M(n)&&L(n,t,y?"block":"top");if("interpolation"==e)return L(n,t,"interpolation");if("word"==e){var r=t.current().toLowerCase();o="only"==r||"not"==r||"and"==r||"or"==r?"keyword":c.hasOwnProperty(r)?"attribute":u.hasOwnProperty(r)?"property":f.hasOwnProperty(r)?"keyword":d.hasOwnProperty(r)?"property":h.hasOwnProperty(r)?"string-2":v.hasOwnProperty(r)?"atom":g.hasOwnProperty(r)?"keyword":"error"}return n.context.type},atComponentBlock:function(e,t,n){return"}"==e?N(e,t,n):"{"==e?M(n)&&L(n,t,y?"block":"top",!1):("word"==e&&(o="error"),n.context.type)},atBlock_parens:function(e,t,n){return")"==e?M(n):"{"==e||"}"==e?N(e,t,n,2):E.atBlock(e,t,n)},restricted_atBlock_before:function(e,t,n){return"{"==e?L(n,t,"restricted_atBlock"):"word"==e&&"@counter-style"==n.stateArg?(o="variable","restricted_atBlock_before"):O(e,t,n)},restricted_atBlock:function(e,t,n){return"}"==e?(n.stateArg=null,M(n)):"word"==e?(o="@font-face"==n.stateArg&&!p.hasOwnProperty(t.current().toLowerCase())||"@counter-style"==n.stateArg&&!m.hasOwnProperty(t.current().toLowerCase())?"error":"property","maybeprop"):"restricted_atBlock"},keyframes:function(e,t,n){return"word"==e?(o="variable","keyframes"):"{"==e?L(n,t,"top"):O(e,t,n)},at:function(e,t,n){return";"==e?M(n):"{"==e||"}"==e?N(e,t,n):("word"==e?o="tag":"hash"==e&&(o="builtin"),"at")},interpolation:function(e,t,n){return"}"==e?M(n):"{"==e||";"==e?N(e,t,n):("word"==e?o="variable":"variable"!=e&&"("!=e&&")"!=e&&(o="error"),"interpolation")}};return{startState:function(e){return{tokenize:null,state:r?"block":"top",stateArg:null,context:new T(r?"block":"top",e||0,null)}},token:function(e,t){if(!t.tokenize&&e.eatSpace())return null;var n=(t.tokenize||k)(e,t);return n&&"object"==typeof n&&(i=n[1],n=n[0]),o=n,"comment"!=i&&(t.state=E[t.state](i,e,t)),o},indent:function(e,t){var n=e.context,r=t&&t.charAt(0),i=n.indent;return"prop"!=n.type||"}"!=r&&")"!=r||(n=n.prev),n.prev&&("}"!=r||"block"!=n.type&&"top"!=n.type&&"interpolation"!=n.type&&"restricted_atBlock"!=n.type?(")"!=r||"parens"!=n.type&&"atBlock_parens"!=n.type)&&("{"!=r||"at"!=n.type&&"atBlock"!=n.type)||(i=Math.max(0,n.indent-a)):i=(n=n.prev).indent),i},electricChars:"}",blockCommentStart:"/*",blockCommentEnd:"*/",blockCommentContinue:" * ",lineComment:b,fold:"brace"}}));var n=["domain","regexp","url","url-prefix"],r=t(n),i=["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"],o=t(i),a=["width","min-width","max-width","height","min-height","max-height","device-width","min-device-width","max-device-width","device-height","min-device-height","max-device-height","aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio","color","min-color","max-color","color-index","min-color-index","max-color-index","monochrome","min-monochrome","max-monochrome","resolution","min-resolution","max-resolution","scan","grid","orientation","device-pixel-ratio","min-device-pixel-ratio","max-device-pixel-ratio","pointer","any-pointer","hover","any-hover"],l=t(a),s=["landscape","portrait","none","coarse","fine","on-demand","hover","interlace","progressive"],c=t(s),u=["align-content","align-items","align-self","alignment-adjust","alignment-baseline","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","appearance","azimuth","backface-visibility","background","background-attachment","background-blend-mode","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","binding","bleed","bookmark-label","bookmark-level","bookmark-state","bookmark-target","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","color","color-profile","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","crop","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","drop-initial-after-adjust","drop-initial-after-align","drop-initial-before-adjust","drop-initial-before-align","drop-initial-size","drop-initial-value","elevation","empty-cells","fit","fit-position","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","float-offset","flow-from","flow-into","font","font-feature-settings","font-family","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-synthesis","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-gap","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-gap","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","inline-box-align","justify-content","justify-items","justify-self","left","letter-spacing","line-break","line-height","line-stacking","line-stacking-ruby","line-stacking-shift","line-stacking-strategy","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","marquee-direction","marquee-loop","marquee-play-count","marquee-speed","marquee-style","max-height","max-width","min-height","min-width","mix-blend-mode","move-to","nav-down","nav-index","nav-left","nav-right","nav-up","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-style","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","page-policy","pause","pause-after","pause-before","perspective","perspective-origin","pitch","pitch-range","place-content","place-items","place-self","play-during","position","presentation-level","punctuation-trim","quotes","region-break-after","region-break-before","region-break-inside","region-fragment","rendering-intent","resize","rest","rest-after","rest-before","richness","right","rotation","rotation-point","ruby-align","ruby-overhang","ruby-position","ruby-span","shape-image-threshold","shape-inside","shape-margin","shape-outside","size","speak","speak-as","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","string-set","tab-size","table-layout","target","target-name","target-new","target-position","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-height","text-indent","text-justify","text-outline","text-overflow","text-shadow","text-size-adjust","text-space-collapse","text-transform","text-underline-position","text-wrap","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","user-select","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","z-index","clip-path","clip-rule","mask","enable-background","filter","flood-color","flood-opacity","lighting-color","stop-color","stop-opacity","pointer-events","color-interpolation","color-interpolation-filters","color-rendering","fill","fill-opacity","fill-rule","image-rendering","marker","marker-end","marker-mid","marker-start","shape-rendering","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-rendering","baseline-shift","dominant-baseline","glyph-orientation-horizontal","glyph-orientation-vertical","text-anchor","writing-mode"],f=t(u),d=["scrollbar-arrow-color","scrollbar-base-color","scrollbar-dark-shadow-color","scrollbar-face-color","scrollbar-highlight-color","scrollbar-shadow-color","scrollbar-3d-light-color","scrollbar-track-color","shape-inside","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","zoom"],h=t(d),p=t(["font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"]),m=t(["additive-symbols","fallback","negative","pad","prefix","range","speak-as","suffix","symbols","system"]),g=["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","grey","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"],v=t(g),y=["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","auto-flow","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","color","color-burn","color-dodge","column","column-reverse","compact","condensed","contain","content","contents","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","darken","dashed","decimal","decimal-leading-zero","default","default-button","dense","destination-atop","destination-in","destination-out","destination-over","devanagari","difference","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","ellipsis","embed","end","ethiopic","ethiopic-abegede","ethiopic-abegede-am-et","ethiopic-abegede-gez","ethiopic-abegede-ti-er","ethiopic-abegede-ti-et","ethiopic-halehame-aa-er","ethiopic-halehame-aa-et","ethiopic-halehame-am-et","ethiopic-halehame-gez","ethiopic-halehame-om-et","ethiopic-halehame-sid-et","ethiopic-halehame-so-et","ethiopic-halehame-ti-er","ethiopic-halehame-ti-et","ethiopic-halehame-tig","ethiopic-numeric","ew-resize","exclusion","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","flex-end","flex-start","footnotes","forwards","from","geometricPrecision","georgian","graytext","grid","groove","gujarati","gurmukhi","hand","hangul","hangul-consonant","hard-light","hebrew","help","hidden","hide","higher","highlight","highlighttext","hiragana","hiragana-iroha","horizontal","hsl","hsla","hue","hwb","icon","ignore","inactiveborder","inactivecaption","inactivecaptiontext","infinite","infobackground","infotext","inherit","initial","inline","inline-axis","inline-block","inline-flex","inline-grid","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","lighten","line-through","linear","linear-gradient","lines","list-item","listbox","listitem","local","logical","loud","lower","lower-alpha","lower-armenian","lower-greek","lower-hexadecimal","lower-latin","lower-norwegian","lower-roman","lowercase","ltr","luminosity","malayalam","match","matrix","matrix3d","media-controls-background","media-current-time-display","media-fullscreen-button","media-mute-button","media-play-button","media-return-to-realtime-button","media-rewind-button","media-seek-back-button","media-seek-forward-button","media-slider","media-sliderthumb","media-time-remaining-display","media-volume-slider","media-volume-slider-container","media-volume-sliderthumb","medium","menu","menulist","menulist-button","menulist-text","menulist-textfield","menutext","message-box","middle","min-intrinsic","mix","mongolian","monospace","move","multiple","multiply","myanmar","n-resize","narrower","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","opacity","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row","row-resize","row-reverse","rtl","run-in","running","s-resize","sans-serif","saturation","scale","scale3d","scaleX","scaleY","scaleZ","screen","scroll","scrollbar","scroll-position","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","self-start","self-end","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","skip-white-space","slide","slider-horizontal","slider-vertical","sliderthumb-horizontal","sliderthumb-vertical","slow","small","small-caps","small-caption","smaller","soft-light","solid","somali","source-atop","source-in","source-out","source-over","space","space-around","space-between","space-evenly","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","system-ui","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","telugu","text","text-bottom","text-top","textarea","textfield","thai","thick","thin","threeddarkshadow","threedface","threedhighlight","threedlightshadow","threedshadow","tibetan","tigre","tigrinya-er","tigrinya-er-abegede","tigrinya-et","tigrinya-et-abegede","to","top","trad-chinese-formal","trad-chinese-informal","transform","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","unset","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","wrap","wrap-reverse","x-large","x-small","xor","xx-large","xx-small"],b=t(y),x=n.concat(i).concat(a).concat(s).concat(u).concat(d).concat(g).concat(y);function w(e,t){for(var n,r=!1;null!=(n=e.next());){if(r&&"/"==n){t.tokenize=null;break}r="*"==n}return["comment","comment"]}e.registerHelper("hintWords","css",x),e.defineMIME("text/css",{documentTypes:r,mediaTypes:o,mediaFeatures:l,mediaValueKeywords:c,propertyKeywords:f,nonStandardPropertyKeywords:h,fontProperties:p,counterDescriptors:m,colorKeywords:v,valueKeywords:b,tokenHooks:{"/":function(e,t){return!!e.eat("*")&&(t.tokenize=w,w(e,t))}},name:"css"}),e.defineMIME("text/x-scss",{mediaTypes:o,mediaFeatures:l,mediaValueKeywords:c,propertyKeywords:f,nonStandardPropertyKeywords:h,colorKeywords:v,valueKeywords:b,fontProperties:p,allowNested:!0,lineComment:"//",tokenHooks:{"/":function(e,t){return e.eat("/")?(e.skipToEnd(),["comment","comment"]):e.eat("*")?(t.tokenize=w,w(e,t)):["operator","operator"]},":":function(e){return!!e.match(/\s*\{/,!1)&&[null,null]},$:function(e){return e.match(/^[\w-]+/),e.match(/^\s*:/,!1)?["variable-2","variable-definition"]:["variable-2","variable"]},"#":function(e){return!!e.eat("{")&&[null,"interpolation"]}},name:"css",helperType:"scss"}),e.defineMIME("text/x-less",{mediaTypes:o,mediaFeatures:l,mediaValueKeywords:c,propertyKeywords:f,nonStandardPropertyKeywords:h,colorKeywords:v,valueKeywords:b,fontProperties:p,allowNested:!0,lineComment:"//",tokenHooks:{"/":function(e,t){return e.eat("/")?(e.skipToEnd(),["comment","comment"]):e.eat("*")?(t.tokenize=w,w(e,t)):["operator","operator"]},"@":function(e){return e.eat("{")?[null,"interpolation"]:!e.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i,!1)&&(e.eatWhile(/[\w\\\-]/),e.match(/^\s*:/,!1)?["variable-2","variable-definition"]:["variable-2","variable"])},"&":function(){return["atom","atom"]}},name:"css",helperType:"less"}),e.defineMIME("text/x-gss",{documentTypes:r,mediaTypes:o,mediaFeatures:l,propertyKeywords:f,nonStandardPropertyKeywords:h,fontProperties:p,counterDescriptors:m,colorKeywords:v,valueKeywords:b,supportsAtComponent:!0,tokenHooks:{"/":function(e,t){return!!e.eat("*")&&(t.tokenize=w,w(e,t))}},name:"css",helperType:"gss"})}(n(0))},function(e,t,n){!function(e){"use strict";function t(t,n,i,o){if(i&&i.call){var a=i;i=null}else a=r(t,i,"rangeFinder");"number"==typeof n&&(n=e.Pos(n,0));var l=r(t,i,"minFoldSize");function s(e){var r=a(t,n);if(!r||r.to.line-r.from.line<l)return null;for(var i=t.findMarksAt(r.from),s=0;s<i.length;++s)if(i[s].__isFold&&"fold"!==o){if(!e)return null;r.cleared=!0,i[s].clear()}return r}var c=s(!0);if(r(t,i,"scanUp"))for(;!c&&n.line>t.firstLine();)n=e.Pos(n.line-1,0),c=s(!1);if(c&&!c.cleared&&"unfold"!==o){var u=function(e,t){var n=r(e,t,"widget");if("string"==typeof n){var i=document.createTextNode(n);(n=document.createElement("span")).appendChild(i),n.className="CodeMirror-foldmarker"}else n&&(n=n.cloneNode(!0));return n}(t,i);e.on(u,"mousedown",(function(t){f.clear(),e.e_preventDefault(t)}));var f=t.markText(c.from,c.to,{replacedWith:u,clearOnEnter:r(t,i,"clearOnEnter"),__isFold:!0});f.on("clear",(function(n,r){e.signal(t,"unfold",t,n,r)})),e.signal(t,"fold",t,c.from,c.to)}}e.newFoldFunction=function(e,n){return function(r,i){t(r,i,{rangeFinder:e,widget:n})}},e.defineExtension("foldCode",(function(e,n,r){t(this,e,n,r)})),e.defineExtension("isFolded",(function(e){for(var t=this.findMarksAt(e),n=0;n<t.length;++n)if(t[n].__isFold)return!0})),e.commands.toggleFold=function(e){e.foldCode(e.getCursor())},e.commands.fold=function(e){e.foldCode(e.getCursor(),null,"fold")},e.commands.unfold=function(e){e.foldCode(e.getCursor(),null,"unfold")},e.commands.foldAll=function(t){t.operation((function(){for(var n=t.firstLine(),r=t.lastLine();n<=r;n++)t.foldCode(e.Pos(n,0),null,"fold")}))},e.commands.unfoldAll=function(t){t.operation((function(){for(var n=t.firstLine(),r=t.lastLine();n<=r;n++)t.foldCode(e.Pos(n,0),null,"unfold")}))},e.registerHelper("fold","combine",(function(){var e=Array.prototype.slice.call(arguments,0);return function(t,n){for(var r=0;r<e.length;++r){var i=e[r](t,n);if(i)return i}}})),e.registerHelper("fold","auto",(function(e,t){for(var n=e.getHelpers(t,"fold"),r=0;r<n.length;r++){var i=n[r](e,t);if(i)return i}}));var n={rangeFinder:e.fold.auto,widget:"↔",minFoldSize:0,scanUp:!1,clearOnEnter:!0};function r(e,t,r){if(t&&void 0!==t[r])return t[r];var i=e.options.foldOptions;return i&&void 0!==i[r]?i[r]:n[r]}e.defineOption("foldOptions",null),e.defineExtension("foldOption",(function(e,t){return r(this,e,t)}))}(n(0))},function(e,t,n){n(4),n(1),n(8),n(9),n(12),n(13),n(14),n(15),n(2),n(3),n(5),n(16),n(17),n(18),n(19),n(20),n(21),n(22),n(23),n(24),n(26),n(27),n(28),n(6),n(29),n(30),n(31),n(32),n(33),n(34),e.exports=n(0)},function(e,t,n){!function(e){"use strict";function t(){this.posFrom=this.posTo=this.lastQuery=this.query=null,this.overlay=null}function n(e){return e.state.search||(e.state.search=new t)}function r(e){return"string"==typeof e&&e==e.toLowerCase()}function i(e,t,n){return e.getSearchCursor(t,n,{caseFold:r(t),multiline:!0})}function o(e,t,n,r,i){e.openDialog?e.openDialog(t,i,{value:r,selectValueOnOpen:!0}):i(prompt(n,r))}function a(e){return e.replace(/\\([nrt\\])/g,(function(e,t){return"n"==t?"\n":"r"==t?"\r":"t"==t?"\t":"\\"==t?"\\":e}))}function l(e){var t=e.match(/^\/(.*)\/([a-z]*)$/);if(t)try{e=new RegExp(t[1],-1==t[2].indexOf("i")?"":"i")}catch(e){}else e=a(e);return("string"==typeof e?""==e:e.test(""))&&(e=/x^/),e}var s;function c(e,t,n){t.queryText=n,t.query=l(n),e.removeOverlay(t.overlay,r(t.query)),t.overlay=function(e,t){return"string"==typeof e?e=new RegExp(e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&"),t?"gi":"g"):e.global||(e=new RegExp(e.source,e.ignoreCase?"gi":"g")),{token:function(t){e.lastIndex=t.pos;var n=e.exec(t.string);if(n&&n.index==t.pos)return t.pos+=n[0].length||1,"searching";n?t.pos=n.index:t.skipToEnd()}}}(t.query,r(t.query)),e.addOverlay(t.overlay),e.showMatchesOnScrollbar&&(t.annotate&&(t.annotate.clear(),t.annotate=null),t.annotate=e.showMatchesOnScrollbar(t.query,r(t.query)))}function u(t,r,i,a){if(!s){let e=t.getWrapperElement().ownerDocument,n=e.createElement("input");n.type="search",n.placeholder=t.l10n("findCmd.promptMessage"),n.style.marginInlineStart="1em",n.style.marginInlineEnd="1em",n.style.flexGrow="1",n.addEventListener("focus",()=>n.select()),(s=e.createElement("div")).appendChild(n),s.style.display="flex"}var l=n(t);if(l.query)return f(t,r);var u=t.getSelection()||l.lastQuery;if(u instanceof RegExp&&"x^"==u.source&&(u=null),i&&t.openDialog){var h=null,p=function(n,r){e.e_stop(r),n&&(n!=l.queryText&&(c(t,l,n),l.posFrom=l.posTo=t.getCursor()),h&&(h.style.opacity=1),f(t,r.shiftKey,(function(e,n){var r;n.line<3&&document.querySelector&&(r=t.display.wrapper.querySelector(".CodeMirror-dialog"))&&r.getBoundingClientRect().bottom-4>t.cursorCoords(n,"window").top&&((h=r).style.opacity=.4)})))};!function(e,t,n,r,i){e.openDialog(t,r,{value:n,selectValueOnOpen:!0,closeOnEnter:!1,onClose:function(){d(e)},onKeyDown:i})}(t,s,u,p,(function(r,i){var o=e.keyName(r),a=t.getOption("extraKeys"),l=a&&a[o]||e.keyMap[t.getOption("keyMap")][o];"findNext"==l||"findPrev"==l||"findPersistentNext"==l||"findPersistentPrev"==l?(e.e_stop(r),c(t,n(t),i),t.execCommand(l)):"find"!=l&&"findPersistent"!=l||(e.e_stop(r),p(i,r))})),a&&u&&(c(t,l,u),f(t,r))}else o(t,s,"Search for:",u,(function(e){e&&!l.query&&t.operation((function(){c(t,l,e),l.posFrom=l.posTo=t.getCursor(),f(t,r)}))}))}function f(t,r,o){t.operation((function(){var a=n(t),l=i(t,a.query,r?a.posFrom:a.posTo);(l.find(r)||(l=i(t,a.query,r?e.Pos(t.lastLine()):e.Pos(t.firstLine(),0))).find(r))&&(t.setSelection(l.from(),l.to()),t.scrollIntoView({from:l.from(),to:l.to()},20),a.posFrom=l.from(),a.posTo=l.to(),o&&o(l.from(),l.to()))}))}function d(e){e.operation((function(){var t=n(e);t.lastQuery=t.query,t.query&&(t.query=t.queryText=null,e.removeOverlay(t.overlay),t.annotate&&(t.annotate.clear(),t.annotate=null))}))}function h(e,t,n){e.operation((function(){for(var r=i(e,t);r.findNext();)if("string"!=typeof t){var o=e.getRange(r.from(),r.to()).match(t);r.replace(n.replace(/\$(\d)/g,(function(e,t){return o[t]})))}else r.replace(n)}))}function p(e,t){if(e.getOption("readOnly"))return;var r=e.getSelection()||n(e).lastQuery;let s=e.getWrapperElement().ownerDocument,c=s.createElement("span");c.classList.add("CodeMirror-search-label"),c.textContent=t?"Replace all:":"Replace:";let u=s.createDocumentFragment();u.appendChild(c.cloneNode(!0));let f=s.createElement("input");f.setAttribute("type","text"),f.setAttribute("style","width: 10em"),f.classList.add("CodeMirror-search-field"),u.appendChild(f);let p=s.createElement("span");p.setAttribute("style","color: #888"),p.classList.add("CodeMirror-search-hint"),p.textContent="(Use /re/ syntax for regexp search)",u.appendChild(p),o(e,u,c,r,(function(n){if(!n)return;n=l(n);let r=s.createDocumentFragment(),u=c.cloneNode(!1);u.textContent="With:",r.appendChild(u);let f=s.createElement("input");f.setAttribute("type","text"),f.setAttribute("style","width: 10em"),f.classList.add("CodeMirror-search-field"),r.appendChild(f),o(e,r,"Replace with:","",(function(r){if(r=a(r),t)h(e,n,r);else{d(e);var o=i(e,n,e.getCursor("from")),l=function(){var t,a=o.from();if(!(t=o.findNext())&&(o=i(e,n),!(t=o.findNext())||a&&o.from().line==a.line&&o.from().ch==a.ch))return;e.setSelection(o.from(),o.to()),e.scrollIntoView({from:o.from(),to:o.to()});let f=s.createDocumentFragment(),d=c.cloneNode(!1);d.textContent="Replace?",f.appendChild(d);let p=s.createElement("button");p.textContent="Yes",f.appendChild(p);let m=s.createElement("button");m.textContent="No",f.appendChild(m);let g=s.createElement("button");g.textContent="All",f.appendChild(g);let v=s.createElement("button");v.textContent="Stop",f.appendChild(v),function(e,t,n,r){e.openConfirm?e.openConfirm(t,r):confirm(n)&&r[0]()}(e,f,"Replace?",[function(){u(t)},l,function(){h(e,n,r)}])},u=function(e){o.replace("string"==typeof n?r:r.replace(/\$(\d)/g,(function(t,n){return e[n]}))),l()};l()}}))}))}e.commands.find=function(e){d(e),u(e)},e.commands.findPersistent=function(e){d(e),u(e,!1,!0)},e.commands.findPersistentNext=function(e){u(e,!1,!0,!0)},e.commands.findPersistentPrev=function(e){u(e,!0,!0,!0)},e.commands.findNext=u,e.commands.findPrev=function(e){u(e,!0)},e.commands.clearSearch=d,e.commands.replace=p,e.commands.replaceAll=function(e){p(e,!0)}}(n(0),n(1),n(4))},function(e,t,n){!function(e){"use strict";var t={style:"matchhighlight",minChars:2,delay:100,wordsOnly:!1,annotateScrollbar:!1,showToken:!1,trim:!0};function n(e){for(var n in this.options={},t)this.options[n]=(e&&e.hasOwnProperty(n)?e:t)[n];this.overlay=this.timeout=null,this.matchesonscroll=null,this.active=!1}function r(e){var t=e.state.matchHighlighter;(t.active||e.hasFocus())&&o(e,t)}function i(e){var t=e.state.matchHighlighter;t.active||(t.active=!0,o(e,t))}function o(e,t){clearTimeout(t.timeout),t.timeout=setTimeout((function(){s(e)}),t.options.delay)}function a(e,t,n,r){var i=e.state.matchHighlighter;if(e.addOverlay(i.overlay=function(e,t,n){return{token:function(r){if(r.match(e)&&(!t||function(e,t){return!(e.start&&t.test(e.string.charAt(e.start-1))||e.pos!=e.string.length&&t.test(e.string.charAt(e.pos)))}(r,t)))return n;r.next(),r.skipTo(e.charAt(0))||r.skipToEnd()}}}(t,n,r)),i.options.annotateScrollbar&&e.showMatchesOnScrollbar){var o=n?new RegExp("\\b"+t.replace(/[\\\[.+*?(){|^$]/g,"\\$&")+"\\b"):t;i.matchesonscroll=e.showMatchesOnScrollbar(o,!1,{className:"CodeMirror-selection-highlight-scrollbar"})}}function l(e){var t=e.state.matchHighlighter;t.overlay&&(e.removeOverlay(t.overlay),t.overlay=null,t.matchesonscroll&&(t.matchesonscroll.clear(),t.matchesonscroll=null))}function s(e){e.operation((function(){var t=e.state.matchHighlighter;if(l(e),e.somethingSelected()||!t.options.showToken){var n=e.getCursor("from"),r=e.getCursor("to");if(n.line==r.line&&(!t.options.wordsOnly||function(e,t,n){if(null!==e.getRange(t,n).match(/^\w+$/)){if(t.ch>0){var r={line:t.line,ch:t.ch-1};if(null===e.getRange(r,t).match(/\W/))return!1}return!(n.ch<e.getLine(t.line).length&&(r={line:n.line,ch:n.ch+1},null===e.getRange(n,r).match(/\W/)))}return!1}(e,n,r))){var i=e.getRange(n,r);t.options.trim&&(i=i.replace(/^\s+|\s+$/g,"")),i.length>=t.options.minChars&&a(e,i,!1,t.options.style)}}else{for(var o=!0===t.options.showToken?/[\w$]/:t.options.showToken,s=e.getCursor(),c=e.getLine(s.line),u=s.ch,f=u;u&&o.test(c.charAt(u-1));)--u;for(;f<c.length&&o.test(c.charAt(f));)++f;u<f&&a(e,c.slice(u,f),o,t.options.style)}}))}e.defineOption("highlightSelectionMatches",!1,(function(t,o,a){if(a&&a!=e.Init&&(l(t),clearTimeout(t.state.matchHighlighter.timeout),t.state.matchHighlighter=null,t.off("cursorActivity",r),t.off("focus",i)),o){var c=t.state.matchHighlighter=new n(o);t.hasFocus()?(c.active=!0,s(t)):t.on("focus",i),t.on("cursorActivity",r)}}))}(n(0),n(10))},function(e,t,n){!function(e){"use strict";function t(e,t,n,r){this.cm=e,this.options=r;var i={listenForChanges:!1};for(var o in r)i[o]=r[o];i.className||(i.className="CodeMirror-search-match"),this.annotation=e.annotateScrollbar(i),this.query=t,this.caseFold=n,this.gap={from:e.firstLine(),to:e.lastLine()+1},this.matches=[],this.update=null,this.findMatches(),this.annotation.update(this.matches);var a=this;e.on("change",this.changeHandler=function(e,t){a.onChange(t)})}function n(e,t,n){return e<=t?e:Math.max(t,e+n)}e.defineExtension("showMatchesOnScrollbar",(function(e,n,r){return"string"==typeof r&&(r={className:r}),r||(r={}),new t(this,e,n,r)})),t.prototype.findMatches=function(){if(this.gap){for(var t=0;t<this.matches.length&&!((i=this.matches[t]).from.line>=this.gap.to);t++)i.to.line>=this.gap.from&&this.matches.splice(t--,1);for(var n=this.cm.getSearchCursor(this.query,e.Pos(this.gap.from,0),{caseFold:this.caseFold,multiline:this.options.multiline}),r=this.options&&this.options.maxMatches||1e3;n.findNext();){var i;if((i={from:n.from(),to:n.to()}).from.line>=this.gap.to)break;if(this.matches.splice(t++,0,i),this.matches.length>r)break}this.gap=null}},t.prototype.onChange=function(t){var r=t.from.line,i=e.changeEnd(t).line,o=i-t.to.line;if(this.gap?(this.gap.from=Math.min(n(this.gap.from,r,o),t.from.line),this.gap.to=Math.max(n(this.gap.to,r,o),t.from.line)):this.gap={from:t.from.line,to:i+1},o)for(var a=0;a<this.matches.length;a++){var l=this.matches[a],s=n(l.from.line,r,o);s!=l.from.line&&(l.from=e.Pos(s,l.from.ch));var c=n(l.to.line,r,o);c!=l.to.line&&(l.to=e.Pos(c,l.to.ch))}clearTimeout(this.update);var u=this;this.update=setTimeout((function(){u.updateAfterChange()}),250)},t.prototype.updateAfterChange=function(){this.findMatches(),this.annotation.update(this.matches)},t.prototype.clear=function(){this.cm.off("change",this.changeHandler),this.annotation.clear()}}(n(0),n(1),n(11))},function(e,t,n){!function(e){"use strict";function t(e,t){function n(e){clearTimeout(r.doRedraw),r.doRedraw=setTimeout((function(){r.redraw()}),e)}this.cm=e,this.options=t,this.buttonHeight=t.scrollButtonHeight||e.getOption("scrollButtonHeight"),this.annotations=[],this.doRedraw=this.doUpdate=null,this.div=e.getWrapperElement().appendChild(document.createElement("div")),this.div.style.cssText="position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none",this.computeScale();var r=this;e.on("refresh",this.resizeHandler=function(){clearTimeout(r.doUpdate),r.doUpdate=setTimeout((function(){r.computeScale()&&n(20)}),100)}),e.on("markerAdded",this.resizeHandler),e.on("markerCleared",this.resizeHandler),!1!==t.listenForChanges&&e.on("changes",this.changeHandler=function(){n(250)})}e.defineExtension("annotateScrollbar",(function(e){return"string"==typeof e&&(e={className:e}),new t(this,e)})),e.defineOption("scrollButtonHeight",0),t.prototype.computeScale=function(){var e=this.cm,t=(e.getWrapperElement().clientHeight-e.display.barHeight-2*this.buttonHeight)/e.getScrollerElement().scrollHeight;if(t!=this.hScale)return this.hScale=t,!0},t.prototype.update=function(e){this.annotations=e,this.redraw()},t.prototype.redraw=function(e){!1!==e&&this.computeScale();var t=this.cm,n=this.hScale,r=document.createDocumentFragment(),i=this.annotations,o=t.getOption("lineWrapping"),a=o&&1.5*t.defaultTextHeight(),l=null,s=null;function c(e,n){if(l!=e.line){l=e.line,s=t.getLineHandle(e.line);var r=t.getLineHandleVisualStart(s);r!=s&&(l=t.getLineNumber(r),s=r)}return s.widgets&&s.widgets.length||o&&s.height>a?t.charCoords(e,"local")[n?"top":"bottom"]:t.heightAtLine(s,"local")+(n?0:s.height)}var u=t.lastLine();if(t.display.barWidth)for(var f,d=0;d<i.length;d++){var h=i[d];if(!(h.to.line>u)){for(var p=f||c(h.from,!0)*n,m=c(h.to,!1)*n;d<i.length-1&&!(i[d+1].to.line>u)&&!((f=c(i[d+1].from,!0)*n)>m+.9);)m=c((h=i[++d]).to,!1)*n;if(m!=p){var g=Math.max(m-p,3),v=r.appendChild(document.createElement("div"));v.style.cssText="position: absolute; right: 0px; width: "+Math.max(t.display.barWidth-1,2)+"px; top: "+(p+this.buttonHeight)+"px; height: "+g+"px",v.className=this.options.className,h.id&&v.setAttribute("annotation-id",h.id)}}}this.div.textContent="",this.div.appendChild(r)},t.prototype.clear=function(){this.cm.off("refresh",this.resizeHandler),this.cm.off("markerAdded",this.resizeHandler),this.cm.off("markerCleared",this.resizeHandler),this.changeHandler&&this.cm.off("changes",this.changeHandler),this.div.parentNode.removeChild(this.div)}}(n(0))},function(e,t,n){!function(e){var t=/MSIE \d/.test(navigator.userAgent)&&(null==document.documentMode||document.documentMode<8),n=e.Pos,r={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<","<":">>",">":"<<"};function i(e){return e&&e.bracketRegex||/[(){}[\]]/}function o(e,t,o){var l=e.getLineHandle(t.line),s=t.ch-1,c=o&&o.afterCursor;null==c&&(c=/(^| )cm-fat-cursor($| )/.test(e.getWrapperElement().className));var u=i(o),f=!c&&s>=0&&u.test(l.text.charAt(s))&&r[l.text.charAt(s)]||u.test(l.text.charAt(s+1))&&r[l.text.charAt(++s)];if(!f)return null;var d=">"==f.charAt(1)?1:-1;if(o&&o.strict&&d>0!=(s==t.ch))return null;var h=e.getTokenTypeAt(n(t.line,s+1)),p=a(e,n(t.line,s+(d>0?1:0)),d,h||null,o);return null==p?null:{from:n(t.line,s),to:p&&p.pos,match:p&&p.ch==f.charAt(0),forward:d>0}}function a(e,t,o,a,l){for(var s=l&&l.maxScanLineLength||1e4,c=l&&l.maxScanLines||1e3,u=[],f=i(l),d=o>0?Math.min(t.line+c,e.lastLine()+1):Math.max(e.firstLine()-1,t.line-c),h=t.line;h!=d;h+=o){var p=e.getLine(h);if(p){var m=o>0?0:p.length-1,g=o>0?p.length:-1;if(!(p.length>s))for(h==t.line&&(m=t.ch-(o<0?1:0));m!=g;m+=o){var v=p.charAt(m);if(f.test(v)&&(void 0===a||e.getTokenTypeAt(n(h,m+1))==a)){var y=r[v];if(y&&">"==y.charAt(1)==o>0)u.push(v);else{if(!u.length)return{pos:n(h,m),ch:v};u.pop()}}}}}return h-o!=(o>0?e.lastLine():e.firstLine())&&null}function l(e,r,i){for(var a=e.state.matchBrackets.maxHighlightLineLength||1e3,l=[],s=e.listSelections(),c=0;c<s.length;c++){var u=s[c].empty()&&o(e,s[c].head,i);if(u&&e.getLine(u.from.line).length<=a){var f=u.match?"CodeMirror-matchingbracket":"CodeMirror-nonmatchingbracket";l.push(e.markText(u.from,n(u.from.line,u.from.ch+1),{className:f})),u.to&&e.getLine(u.to.line).length<=a&&l.push(e.markText(u.to,n(u.to.line,u.to.ch+1),{className:f}))}}if(l.length){t&&e.state.focused&&e.focus();var d=function(){e.operation((function(){for(var e=0;e<l.length;e++)l[e].clear()}))};if(!r)return d;setTimeout(d,800)}}function s(e){e.operation((function(){e.state.matchBrackets.currentlyHighlighted&&(e.state.matchBrackets.currentlyHighlighted(),e.state.matchBrackets.currentlyHighlighted=null),e.state.matchBrackets.currentlyHighlighted=l(e,!1,e.state.matchBrackets)}))}e.defineOption("matchBrackets",!1,(function(t,n,r){r&&r!=e.Init&&(t.off("cursorActivity",s),t.state.matchBrackets&&t.state.matchBrackets.currentlyHighlighted&&(t.state.matchBrackets.currentlyHighlighted(),t.state.matchBrackets.currentlyHighlighted=null)),n&&(t.state.matchBrackets="object"==typeof n?n:{},t.on("cursorActivity",s))})),e.defineExtension("matchBrackets",(function(){l(this,!0)})),e.defineExtension("findMatchingBracket",(function(e,t,n){return(n||"boolean"==typeof t)&&(n?(n.strict=t,t=n):t=t?{strict:!0}:null),o(this,e,t)})),e.defineExtension("scanForBracket",(function(e,t,n,r){return a(this,e,t,n,r)}))}(n(0))},function(e,t,n){!function(e){var t={pairs:"()[]{}''\"\"",closeBefore:")]}'\":;>",triples:"",explode:"[]{}"},n=e.Pos;function r(e,n){return"pairs"==n&&"string"==typeof e?e:"object"==typeof e&&null!=e[n]?e[n]:t[n]}e.defineOption("autoCloseBrackets",!1,(function(t,n,a){a&&a!=e.Init&&(t.removeKeyMap(i),t.state.closeBrackets=null),n&&(o(r(n,"pairs")),t.state.closeBrackets=n,t.addKeyMap(i))}));var i={Backspace:function(t){var i=l(t);if(!i||t.getOption("disableInput"))return e.Pass;for(var o=r(i,"pairs"),a=t.listSelections(),c=0;c<a.length;c++){if(!a[c].empty())return e.Pass;var u=s(t,a[c].head);if(!u||o.indexOf(u)%2!=0)return e.Pass}for(c=a.length-1;c>=0;c--){var f=a[c].head;t.replaceRange("",n(f.line,f.ch-1),n(f.line,f.ch+1),"+delete")}},Enter:function(t){var n=l(t),i=n&&r(n,"explode");if(!i||t.getOption("disableInput"))return e.Pass;for(var o=t.listSelections(),a=0;a<o.length;a++){if(!o[a].empty())return e.Pass;var c=s(t,o[a].head);if(!c||i.indexOf(c)%2!=0)return e.Pass}t.operation((function(){var e=t.lineSeparator()||"\n";t.replaceSelection(e+e,null),t.execCommand("goCharLeft"),o=t.listSelections();for(var n=0;n<o.length;n++){var r=o[n].head.line;t.indentLine(r,null,!0),t.indentLine(r+1,null,!0)}}))}};function o(e){for(var t=0;t<e.length;t++){var n=e.charAt(t),r="'"+n+"'";i[r]||(i[r]=a(n))}}function a(t){return function(i){return function(t,i){var o=l(t);if(!o||t.getOption("disableInput"))return e.Pass;var a=r(o,"pairs"),s=a.indexOf(i);if(-1==s)return e.Pass;for(var u,f=r(o,"closeBefore"),d=r(o,"triples"),h=a.charAt(s+1)==i,p=t.listSelections(),m=s%2==0,g=0;g<p.length;g++){var v,y=p[g],b=y.head,x=t.getRange(b,n(b.line,b.ch+1));if(m&&!y.empty())v="surround";else if(!h&&m||x!=i)if(h&&b.ch>1&&d.indexOf(i)>=0&&t.getRange(n(b.line,b.ch-2),b)==i+i){if(b.ch>2&&/\bstring/.test(t.getTokenTypeAt(n(b.line,b.ch-2))))return e.Pass;v="addFour"}else if(h){var w=0==b.ch?" ":t.getRange(n(b.line,b.ch-1),b);if(e.isWordChar(x)||w==i||e.isWordChar(w))return e.Pass;v="both"}else{if(!m||!(0===x.length||/\s/.test(x)||f.indexOf(x)>-1))return e.Pass;v="both"}else v=h&&c(t,b)?"both":d.indexOf(i)>=0&&t.getRange(b,n(b.line,b.ch+3))==i+i+i?"skipThree":"skip";if(u){if(u!=v)return e.Pass}else u=v}var k=s%2?a.charAt(s-1):i,C=s%2?i:a.charAt(s+1);t.operation((function(){if("skip"==u)t.execCommand("goCharRight");else if("skipThree"==u)for(var r=0;r<3;r++)t.execCommand("goCharRight");else if("surround"==u){var i=t.getSelections();for(r=0;r<i.length;r++)i[r]=k+i[r]+C;for(t.replaceSelections(i,"around"),i=t.listSelections().slice(),r=0;r<i.length;r++)i[r]=(o=i[r],a=void 0,a=e.cmpPos(o.anchor,o.head)>0,{anchor:new n(o.anchor.line,o.anchor.ch+(a?-1:1)),head:new n(o.head.line,o.head.ch+(a?1:-1))});t.setSelections(i)}else"both"==u?(t.replaceSelection(k+C,null),t.triggerElectric(k+C),t.execCommand("goCharLeft")):"addFour"==u&&(t.replaceSelection(k+k+k+k,"before"),t.execCommand("goCharRight"));var o,a}))}(i,t)}}function l(e){var t=e.state.closeBrackets;return!t||t.override?t:e.getModeAt(e.getCursor()).closeBrackets||t}function s(e,t){var r=e.getRange(n(t.line,t.ch-1),n(t.line,t.ch+1));return 2==r.length?r:null}function c(e,t){var r=e.getTokenAt(n(t.line,t.ch+1));return/\bstring/.test(r.type)&&r.start==t.ch&&(0==t.ch||!/\bstring/.test(e.getTokenTypeAt(t)))}o(t.pairs+"`")}(n(0))},function(e,t,n){!function(e){"use strict";var t={},n=/[^\s\u00a0]/,r=e.Pos;function i(e){var t=e.search(n);return-1==t?0:t}function o(e,t){var n=e.getMode();return!1!==n.useInnerComments&&n.innerMode?e.getModeAt(t):n}e.commands.toggleComment=function(e){e.toggleComment()},e.defineExtension("toggleComment",(function(e){e||(e=t);for(var n=1/0,i=this.listSelections(),o=null,a=i.length-1;a>=0;a--){var l=i[a].from(),s=i[a].to();l.line>=n||(s.line>=n&&(s=r(n,0)),n=l.line,null==o?this.uncomment(l,s,e)?o="un":(this.lineComment(l,s,e),o="line"):"un"==o?this.uncomment(l,s,e):this.lineComment(l,s,e))}})),e.defineExtension("lineComment",(function(e,a,l){l||(l=t);var s=this,c=o(s,e),u=s.getLine(e.line);if(null!=u&&(f=e,d=u,!/\bstring\b/.test(s.getTokenTypeAt(r(f.line,0)))||/^[\'\"\`]/.test(d))){var f,d,h=l.lineComment||c.lineComment;if(h){var p=Math.min(0!=a.ch||a.line==e.line?a.line+1:a.line,s.lastLine()+1),m=null==l.padding?" ":l.padding,g=l.commentBlankLines||e.line==a.line;s.operation((function(){if(l.indent){for(var t=null,o=e.line;o<p;++o){var a=(c=s.getLine(o)).slice(0,i(c));(null==t||t.length>a.length)&&(t=a)}for(o=e.line;o<p;++o){var c=s.getLine(o),u=t.length;(g||n.test(c))&&(c.slice(0,u)!=t&&(u=i(c)),s.replaceRange(t+h+m,r(o,0),r(o,u)))}}else for(o=e.line;o<p;++o)(g||n.test(s.getLine(o)))&&s.replaceRange(h+m,r(o,0))}))}else(l.blockCommentStart||c.blockCommentStart)&&(l.fullLines=!0,s.blockComment(e,a,l))}})),e.defineExtension("blockComment",(function(e,i,a){a||(a=t);var l=this,s=o(l,e),c=a.blockCommentStart||s.blockCommentStart,u=a.blockCommentEnd||s.blockCommentEnd;if(c&&u){if(!/\bcomment\b/.test(l.getTokenTypeAt(r(e.line,0)))){var f=Math.min(i.line,l.lastLine());f!=e.line&&0==i.ch&&n.test(l.getLine(f))&&--f;var d=null==a.padding?" ":a.padding;e.line>f||l.operation((function(){if(0!=a.fullLines){var t=n.test(l.getLine(f));l.replaceRange(d+u,r(f)),l.replaceRange(c+d,r(e.line,0));var o=a.blockCommentLead||s.blockCommentLead;if(null!=o)for(var h=e.line+1;h<=f;++h)(h!=f||t)&&l.replaceRange(o+d,r(h,0))}else l.replaceRange(u,i),l.replaceRange(c,e)}))}}else(a.lineComment||s.lineComment)&&0!=a.fullLines&&l.lineComment(e,i,a)})),e.defineExtension("uncomment",(function(e,i,a){a||(a=t);var l,s=this,c=o(s,e),u=Math.min(0!=i.ch||i.line==e.line?i.line:i.line-1,s.lastLine()),f=Math.min(e.line,u),d=a.lineComment||c.lineComment,h=[],p=null==a.padding?" ":a.padding;e:if(d){for(var m=f;m<=u;++m){var g=s.getLine(m),v=g.indexOf(d);if(v>-1&&!/comment/.test(s.getTokenTypeAt(r(m,v+1)))&&(v=-1),-1==v&&n.test(g))break e;if(v>-1&&n.test(g.slice(0,v)))break e;h.push(g)}if(s.operation((function(){for(var e=f;e<=u;++e){var t=h[e-f],n=t.indexOf(d),i=n+d.length;n<0||(t.slice(i,i+p.length)==p&&(i+=p.length),l=!0,s.replaceRange("",r(e,n),r(e,i)))}})),l)return!0}var y=a.blockCommentStart||c.blockCommentStart,b=a.blockCommentEnd||c.blockCommentEnd;if(!y||!b)return!1;var x=a.blockCommentLead||c.blockCommentLead,w=s.getLine(f),k=w.indexOf(y);if(-1==k)return!1;var C=u==f?w:s.getLine(u),S=C.indexOf(b,u==f?k+y.length:0),T=r(f,k+1),L=r(u,S+1);if(-1==S||!/comment/.test(s.getTokenTypeAt(T))||!/comment/.test(s.getTokenTypeAt(L))||s.getRange(T,L,"\n").indexOf(b)>-1)return!1;var M=w.lastIndexOf(y,e.ch),O=-1==M?-1:w.slice(0,e.ch).indexOf(b,M+y.length);if(-1!=M&&-1!=O&&O+b.length!=e.ch)return!1;O=C.indexOf(b,i.ch);var N=C.slice(i.ch).lastIndexOf(y,O-i.ch);return M=-1==O||-1==N?-1:i.ch+N,(-1==O||-1==M||M==i.ch)&&(s.operation((function(){s.replaceRange("",r(u,S-(p&&C.slice(S-p.length,S)==p?p.length:0)),r(u,S+b.length));var e=k+y.length;if(p&&w.slice(e,e+p.length)==p&&(e+=p.length),s.replaceRange("",r(f,k),r(f,e)),x)for(var t=f+1;t<=u;++t){var i=s.getLine(t),o=i.indexOf(x);if(-1!=o&&!n.test(i.slice(0,o))){var a=o+x.length;p&&i.slice(a,a+p.length)==p&&(a+=p.length),s.replaceRange("",r(t,o),r(t,a))}}})),!0)}))}(n(0))},function(e,t,n){!function(e){e.inputStyles.accessibleTextArea=class extends e.inputStyles.textarea{init(e){super.init(e),this.textarea.addEventListener("compositionstart",this._onCompositionStart.bind(this))}_onCompositionStart(){if(this.textarea.selectionEnd===this.textarea.value.length)return;this.textarea.value=this.textarea.value.substring(0,this.textarea.selectionEnd);const e=this.textarea.value.length;this.textarea.setSelectionRange(e,e),this.prevInput=this.textarea.value}reset(e){if(e||this.contextMenuPending||this.composing||this.cm.somethingSelected())return void super.reset(e);const t=this.cm.getCursor();let n,r;if(this.cm.options.lineWrapping){const e=this.cm.charCoords(t,"page").top;n=this.cm.coordsChar({left:-1/0,top:e}),r=this.cm.coordsChar({left:1/0,top:e})}else{const e=1e3*Math.floor(t.ch/1e3);n={ch:e,line:t.line},r={ch:e+1e3,line:t.line}}this.textarea.value=this.cm.getRange(n,r);const i=t.ch-n.ch;this.textarea.setSelectionRange(i,i),this.prevInput=this.textarea.value}poll(){if(this.contextMenuPending||this.composing)return super.poll();const e=this.textarea.value;let t=0;const n=Math.min(this.prevInput.length,e.length);for(;t<n&&this.prevInput[t]===e[t];)++t;let r=0;for(;r<n-t&&this.prevInput[this.prevInput.length-r-1]===e[e.length-r-1];)++r;const i=this.textarea;this.textarea=document.createElement("textarea"),this.textarea.value=e.substring(t,e.length-r),this.textarea.setSelectionRange(i.selectionStart-t,i.selectionEnd-t),this.prevInput="";const o=super.poll();return this.prevInput=e,this.textarea=i,o}}}(n(0))},function(e,t,n){!function(e){"use strict";e.defineMode("clojure",(function(t){var n=["false","nil","true"],r=[".","catch","def","do","if","monitor-enter","monitor-exit","new","quote","recur","set!","throw","try","var"],i=["*","*'","*1","*2","*3","*agent*","*allow-unresolved-vars*","*assert*","*clojure-version*","*command-line-args*","*compile-files*","*compile-path*","*compiler-options*","*data-readers*","*default-data-reader-fn*","*e","*err*","*file*","*flush-on-newline*","*fn-loader*","*in*","*math-context*","*ns*","*out*","*print-dup*","*print-length*","*print-level*","*print-meta*","*print-namespace-maps*","*print-readably*","*read-eval*","*reader-resolver*","*source-path*","*suppress-read*","*unchecked-math*","*use-context-classloader*","*verbose-defrecords*","*warn-on-reflection*","+","+'","-","-'","->","->>","->ArrayChunk","->Eduction","->Vec","->VecNode","->VecSeq","-cache-protocol-fn","-reset-methods","..","/","<","<=","=","==",">",">=","EMPTY-NODE","Inst","StackTraceElement->vec","Throwable->map","accessor","aclone","add-classpath","add-watch","agent","agent-error","agent-errors","aget","alength","alias","all-ns","alter","alter-meta!","alter-var-root","amap","ancestors","and","any?","apply","areduce","array-map","as->","aset","aset-boolean","aset-byte","aset-char","aset-double","aset-float","aset-int","aset-long","aset-short","assert","assoc","assoc!","assoc-in","associative?","atom","await","await-for","await1","bases","bean","bigdec","bigint","biginteger","binding","bit-and","bit-and-not","bit-clear","bit-flip","bit-not","bit-or","bit-set","bit-shift-left","bit-shift-right","bit-test","bit-xor","boolean","boolean-array","boolean?","booleans","bound-fn","bound-fn*","bound?","bounded-count","butlast","byte","byte-array","bytes","bytes?","case","cast","cat","char","char-array","char-escape-string","char-name-string","char?","chars","chunk","chunk-append","chunk-buffer","chunk-cons","chunk-first","chunk-next","chunk-rest","chunked-seq?","class","class?","clear-agent-errors","clojure-version","coll?","comment","commute","comp","comparator","compare","compare-and-set!","compile","complement","completing","concat","cond","cond->","cond->>","condp","conj","conj!","cons","constantly","construct-proxy","contains?","count","counted?","create-ns","create-struct","cycle","dec","dec'","decimal?","declare","dedupe","default-data-readers","definline","definterface","defmacro","defmethod","defmulti","defn","defn-","defonce","defprotocol","defrecord","defstruct","deftype","delay","delay?","deliver","denominator","deref","derive","descendants","destructure","disj","disj!","dissoc","dissoc!","distinct","distinct?","doall","dorun","doseq","dosync","dotimes","doto","double","double-array","double?","doubles","drop","drop-last","drop-while","eduction","empty","empty?","ensure","ensure-reduced","enumeration-seq","error-handler","error-mode","eval","even?","every-pred","every?","ex-data","ex-info","extend","extend-protocol","extend-type","extenders","extends?","false?","ffirst","file-seq","filter","filterv","find","find-keyword","find-ns","find-protocol-impl","find-protocol-method","find-var","first","flatten","float","float-array","float?","floats","flush","fn","fn?","fnext","fnil","for","force","format","frequencies","future","future-call","future-cancel","future-cancelled?","future-done?","future?","gen-class","gen-interface","gensym","get","get-in","get-method","get-proxy-class","get-thread-bindings","get-validator","group-by","halt-when","hash","hash-combine","hash-map","hash-ordered-coll","hash-set","hash-unordered-coll","ident?","identical?","identity","if-let","if-not","if-some","ifn?","import","in-ns","inc","inc'","indexed?","init-proxy","inst-ms","inst-ms*","inst?","instance?","int","int-array","int?","integer?","interleave","intern","interpose","into","into-array","ints","io!","isa?","iterate","iterator-seq","juxt","keep","keep-indexed","key","keys","keyword","keyword?","last","lazy-cat","lazy-seq","let","letfn","line-seq","list","list*","list?","load","load-file","load-reader","load-string","loaded-libs","locking","long","long-array","longs","loop","macroexpand","macroexpand-1","make-array","make-hierarchy","map","map-entry?","map-indexed","map?","mapcat","mapv","max","max-key","memfn","memoize","merge","merge-with","meta","method-sig","methods","min","min-key","mix-collection-hash","mod","munge","name","namespace","namespace-munge","nat-int?","neg-int?","neg?","newline","next","nfirst","nil?","nnext","not","not-any?","not-empty","not-every?","not=","ns","ns-aliases","ns-imports","ns-interns","ns-map","ns-name","ns-publics","ns-refers","ns-resolve","ns-unalias","ns-unmap","nth","nthnext","nthrest","num","number?","numerator","object-array","odd?","or","parents","partial","partition","partition-all","partition-by","pcalls","peek","persistent!","pmap","pop","pop!","pop-thread-bindings","pos-int?","pos?","pr","pr-str","prefer-method","prefers","primitives-classnames","print","print-ctor","print-dup","print-method","print-simple","print-str","printf","println","println-str","prn","prn-str","promise","proxy","proxy-call-with-super","proxy-mappings","proxy-name","proxy-super","push-thread-bindings","pvalues","qualified-ident?","qualified-keyword?","qualified-symbol?","quot","rand","rand-int","rand-nth","random-sample","range","ratio?","rational?","rationalize","re-find","re-groups","re-matcher","re-matches","re-pattern","re-seq","read","read-line","read-string","reader-conditional","reader-conditional?","realized?","record?","reduce","reduce-kv","reduced","reduced?","reductions","ref","ref-history-count","ref-max-history","ref-min-history","ref-set","refer","refer-clojure","reify","release-pending-sends","rem","remove","remove-all-methods","remove-method","remove-ns","remove-watch","repeat","repeatedly","replace","replicate","require","reset!","reset-meta!","reset-vals!","resolve","rest","restart-agent","resultset-seq","reverse","reversible?","rseq","rsubseq","run!","satisfies?","second","select-keys","send","send-off","send-via","seq","seq?","seqable?","seque","sequence","sequential?","set","set-agent-send-executor!","set-agent-send-off-executor!","set-error-handler!","set-error-mode!","set-validator!","set?","short","short-array","shorts","shuffle","shutdown-agents","simple-ident?","simple-keyword?","simple-symbol?","slurp","some","some->","some->>","some-fn","some?","sort","sort-by","sorted-map","sorted-map-by","sorted-set","sorted-set-by","sorted?","special-symbol?","spit","split-at","split-with","str","string?","struct","struct-map","subs","subseq","subvec","supers","swap!","swap-vals!","symbol","symbol?","sync","tagged-literal","tagged-literal?","take","take-last","take-nth","take-while","test","the-ns","thread-bound?","time","to-array","to-array-2d","trampoline","transduce","transient","tree-seq","true?","type","unchecked-add","unchecked-add-int","unchecked-byte","unchecked-char","unchecked-dec","unchecked-dec-int","unchecked-divide-int","unchecked-double","unchecked-float","unchecked-inc","unchecked-inc-int","unchecked-int","unchecked-long","unchecked-multiply","unchecked-multiply-int","unchecked-negate","unchecked-negate-int","unchecked-remainder-int","unchecked-short","unchecked-subtract","unchecked-subtract-int","underive","unquote","unquote-splicing","unreduced","unsigned-bit-shift-right","update","update-in","update-proxy","uri?","use","uuid?","val","vals","var-get","var-set","var?","vary-meta","vec","vector","vector-of","vector?","volatile!","volatile?","vreset!","vswap!","when","when-first","when-let","when-not","when-some","while","with-bindings","with-bindings*","with-in-str","with-loading-context","with-local-vars","with-meta","with-open","with-out-str","with-precision","with-redefs","with-redefs-fn","xml-seq","zero?","zipmap"];e.registerHelper("hintWords","clojure",[].concat(n,r,i));var o=g(n),a=g(r),l=g(i),s=g(["->","->>","as->","binding","bound-fn","case","catch","comment","cond","cond->","cond->>","condp","def","definterface","defmethod","defn","defmacro","defprotocol","defrecord","defstruct","deftype","do","doseq","dotimes","doto","extend","extend-protocol","extend-type","fn","for","future","if","if-let","if-not","if-some","let","letfn","locking","loop","ns","proxy","reify","struct-map","some->","some->>","try","when","when-first","when-let","when-not","when-some","while","with-bindings","with-bindings*","with-in-str","with-loading-context","with-local-vars","with-meta","with-open","with-out-str","with-precision","with-redefs","with-redefs-fn"]),c=/^(?:[\\\[\]\s"(),;@^`{}~]|$)/,u=/^(?:[+\-]?\d+(?:(?:N|(?:[eE][+\-]?\d+))|(?:\.?\d*(?:M|(?:[eE][+\-]?\d+))?)|\/\d+|[xX][0-9a-fA-F]+|r[0-9a-zA-Z]+)?(?=[\\\[\]\s"#'(),;@^`{}~]|$))/,f=/^(?:\\(?:backspace|formfeed|newline|return|space|tab|o[0-7]{3}|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{4}|.)?(?=[\\\[\]\s"(),;@^`{}~]|$))/,d=/^(?:(?:[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*(?:\.[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*\/)?(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*(?=[\\\[\]\s"(),;@^`{}~]|$))/;function h(e,t){if(e.eatSpace()||e.eat(","))return["space",null];if(e.match(u))return[null,"number"];if(e.match(f))return[null,"string-2"];if(e.eat(/^"/))return(t.tokenize=p)(e,t);if(e.eat(/^[(\[{]/))return["open","bracket"];if(e.eat(/^[)\]}]/))return["close","bracket"];if(e.eat(/^;/))return e.skipToEnd(),["space","comment"];if(e.eat(/^[#'@^`~]/))return[null,"meta"];var n=e.match(d),r=n&&n[0];return r?"comment"===r&&"("===t.lastToken?(t.tokenize=m)(e,t):v(r,o)||":"===r.charAt(0)?["symbol","atom"]:v(r,a)||v(r,l)?["symbol","keyword"]:"("===t.lastToken?["symbol","builtin"]:["symbol","variable"]:(e.next(),e.eatWhile((function(e){return!v(e,c)})),[null,"error"])}function p(e,t){for(var n,r=!1;n=e.next();){if('"'===n&&!r){t.tokenize=h;break}r=!r&&"\\"===n}return[null,"string"]}function m(e,t){for(var n,r=1;n=e.next();)if(")"===n&&r--,"("===n&&r++,0===r){e.backUp(1),t.tokenize=h;break}return["space","comment"]}function g(e){for(var t={},n=0;n<e.length;++n)t[e[n]]=!0;return t}function v(e,t){return t instanceof RegExp?t.test(e):t instanceof Object?t.propertyIsEnumerable(e):void 0}return{startState:function(){return{ctx:{prev:null,start:0,indentTo:0},lastToken:null,tokenize:h}},token:function(e,n){e.sol()&&"number"!=typeof n.ctx.indentTo&&(n.ctx.indentTo=n.ctx.start+1);var r=n.tokenize(e,n),i=r[0],o=r[1],a=e.current();return"space"!==i&&("("===n.lastToken&&null===n.ctx.indentTo?"symbol"===i&&v(a,s)?n.ctx.indentTo=n.ctx.start+t.indentUnit:n.ctx.indentTo="next":"next"===n.ctx.indentTo&&(n.ctx.indentTo=e.column()),n.lastToken=a),"open"===i?n.ctx={prev:n.ctx,start:e.column(),indentTo:null}:"close"===i&&(n.ctx=n.ctx.prev||n.ctx),o},indent:function(e){var t=e.ctx.indentTo;return"number"==typeof t?t:e.ctx.start+1},closeBrackets:{pairs:'()[]{}""'},lineComment:";;"}})),e.defineMIME("text/x-clojure","clojure"),e.defineMIME("text/x-clojurescript","clojure"),e.defineMIME("application/edn","clojure")}(n(0))},function(e,t,n){!function(e){"use strict";e.defineMode("haxe",(function(e,t){var n=e.indentUnit;function r(e){return{type:e,style:"keyword"}}var i,o=r("keyword a"),a=r("keyword b"),l=r("keyword c"),s=r("operator"),c={type:"atom",style:"atom"},u={type:"attribute",style:"attribute"},f=r("typedef"),d={if:o,while:o,else:a,do:a,try:a,return:l,break:l,continue:l,new:l,throw:l,var:r("var"),inline:u,static:u,using:r("import"),public:u,private:u,cast:r("cast"),import:r("import"),macro:r("macro"),function:r("function"),catch:r("catch"),untyped:r("untyped"),callback:r("cb"),for:r("for"),switch:r("switch"),case:r("case"),default:r("default"),in:s,never:r("property_access"),trace:r("trace"),class:f,abstract:f,enum:f,interface:f,typedef:f,extends:f,implements:f,dynamic:f,true:c,false:c,null:c},h=/[+\-*&%=<>!?|]/;function p(e,t,n){return t.tokenize=n,n(e,t)}function m(e,t){for(var n,r=!1;null!=(n=e.next());){if(n==t&&!r)return!0;r=!r&&"\\"==n}}function g(e,t,n){return f=e,i=n,t}function v(e,t){var n=e.next();if('"'==n||"'"==n)return p(e,t,(r=n,function(e,t){return m(e,r)&&(t.tokenize=v),g("string","string")}));if(/[\[\]{}\(\),;\:\.]/.test(n))return g(n);if("0"==n&&e.eat(/x/i))return e.eatWhile(/[\da-f]/i),g("number","number");if(/\d/.test(n)||"-"==n&&e.eat(/\d/))return e.match(/^\d*(?:\.\d*(?!\.))?(?:[eE][+\-]?\d+)?/),g("number","number");if(t.reAllowed&&"~"==n&&e.eat(/\//))return m(e,"/"),e.eatWhile(/[gimsu]/),g("regexp","string-2");if("/"==n)return e.eat("*")?p(e,t,y):e.eat("/")?(e.skipToEnd(),g("comment","comment")):(e.eatWhile(h),g("operator",null,e.current()));if("#"==n)return e.skipToEnd(),g("conditional","meta");if("@"==n)return e.eat(/:/),e.eatWhile(/[\w_]/),g("metadata","meta");if(h.test(n))return e.eatWhile(h),g("operator",null,e.current());if(/[A-Z]/.test(n))return e.eatWhile(/[\w_<>]/),g("type","variable-3",i=e.current());e.eatWhile(/[\w_]/);var r,i=e.current(),o=d.propertyIsEnumerable(i)&&d[i];return o&&t.kwAllowed?g(o.type,o.style,i):g("variable","variable",i)}function y(e,t){for(var n,r=!1;n=e.next();){if("/"==n&&r){t.tokenize=v;break}r="*"==n}return g("comment","comment")}var b={atom:!0,number:!0,variable:!0,string:!0,regexp:!0};function x(e,t,n,r,i,o){this.indented=e,this.column=t,this.type=n,this.prev=i,this.info=o,null!=r&&(this.align=r)}function w(e,t){for(var n=e.localVars;n;n=n.next)if(n.name==t)return!0}function k(e,t){if(/[a-z]/.test(t.charAt(0)))return!1;for(var n=e.importedtypes.length,r=0;r<n;r++)if(e.importedtypes[r]==t)return!0}function C(e){for(var t=S.state,n=t.importedtypes;n;n=n.next)if(n.name==e)return;t.importedtypes={name:e,next:t.importedtypes}}var S={state:null,column:null,marked:null,cc:null};function T(){for(var e=arguments.length-1;e>=0;e--)S.cc.push(arguments[e])}function L(){return T.apply(null,arguments),!0}function M(e,t){for(var n=t;n;n=n.next)if(n.name==e)return!0;return!1}function O(e){var t=S.state;if(t.context){if(S.marked="def",M(e,t.localVars))return;t.localVars={name:e,next:t.localVars}}else if(t.globalVars){if(M(e,t.globalVars))return;t.globalVars={name:e,next:t.globalVars}}}var N={name:"this",next:null};function A(){S.state.context||(S.state.localVars=N),S.state.context={prev:S.state.context,vars:S.state.localVars}}function E(){S.state.localVars=S.state.context.vars,S.state.context=S.state.context.prev}function z(e,t){var n=function(){var n=S.state;n.lexical=new x(n.indented,S.stream.column(),e,null,n.lexical,t)};return n.lex=!0,n}function P(){var e=S.state;e.lexical.prev&&(")"==e.lexical.type&&(e.indented=e.lexical.indented),e.lexical=e.lexical.prev)}function D(e){return function t(n){return n==e?L():";"==e?T():L(t)}}function I(e){return"@"==e?L(R):"var"==e?L(z("vardef"),X,D(";"),P):"keyword a"==e?L(z("form"),F,I,P):"keyword b"==e?L(z("form"),I,P):"{"==e?L(z("}"),A,G,P,E):";"==e?L():"attribute"==e?L(H):"function"==e?L(J):"for"==e?L(z("form"),D("("),z(")"),Y,D(")"),P,I,P):"variable"==e?L(z("stat"),U):"switch"==e?L(z("form"),F,z("}","switch"),D("{"),G,P,P):"case"==e?L(F,D(":")):"default"==e?L(D(":")):"catch"==e?L(z("form"),A,D("("),re,D(")"),I,P,E):"import"==e?L(j,D(";")):"typedef"==e?L(q):T(z("stat"),F,D(";"),P)}function F(e){return b.hasOwnProperty(e)||"type"==e?L(W):"function"==e?L(J):"keyword c"==e?L(_):"("==e?L(z(")"),_,D(")"),P,W):"operator"==e?L(F):"["==e?L(z("]"),$(_,"]"),P,W):"{"==e?L(z("}"),$(K,"}"),P,W):L()}function _(e){return e.match(/[;\}\)\],]/)?T():T(F)}function W(e,t){return"operator"==e&&/\+\+|--/.test(t)?L(W):"operator"==e||":"==e?L(F):";"!=e?"("==e?L(z(")"),$(F,")"),P,W):"."==e?L(V,W):"["==e?L(z("]"),F,D("]"),P,W):void 0:void 0}function H(e){return"attribute"==e?L(H):"function"==e?L(J):"var"==e?L(X):void 0}function R(e){return":"==e||"variable"==e?L(R):"("==e?L(z(")"),$(B,")"),P,I):void 0}function B(e){if("variable"==e)return L()}function j(e,t){return"variable"==e&&/[A-Z]/.test(t.charAt(0))?(C(t),L()):"variable"==e||"property"==e||"."==e||"*"==t?L(j):void 0}function q(e,t){return"variable"==e&&/[A-Z]/.test(t.charAt(0))?(C(t),L()):"type"==e&&/[A-Z]/.test(t.charAt(0))?L():void 0}function U(e){return":"==e?L(P,I):T(W,D(";"),P)}function V(e){if("variable"==e)return S.marked="property",L()}function K(e){if("variable"==e&&(S.marked="property"),b.hasOwnProperty(e))return L(D(":"),F)}function $(e,t){function n(r){return","==r?L(e,n):r==t?L():L(D(t))}return function(r){return r==t?L():T(e,n)}}function G(e){return"}"==e?L():T(I,G)}function X(e,t){return"variable"==e?(O(t),L(ee,Z)):L()}function Z(e,t){return"="==t?L(F,Z):","==e?L(X):void 0}function Y(e,t){return"variable"==e?(O(t),L(Q,F)):T()}function Q(e,t){if("in"==t)return L()}function J(e,t){return"variable"==e||"type"==e?(O(t),L(J)):"new"==t?L(J):"("==e?L(z(")"),A,$(re,")"),P,ee,I,E):void 0}function ee(e){if(":"==e)return L(te)}function te(e){return"type"==e||"variable"==e?L():"{"==e?L(z("}"),$(ne,"}"),P):void 0}function ne(e){if("variable"==e)return L(ee)}function re(e,t){if("variable"==e)return O(t),L(ee)}return E.lex=!0,P.lex=!0,{startState:function(e){var r={tokenize:v,reAllowed:!0,kwAllowed:!0,cc:[],lexical:new x((e||0)-n,0,"block",!1),localVars:t.localVars,importedtypes:["Int","Float","String","Void","Std","Bool","Dynamic","Array"],context:t.localVars&&{vars:t.localVars},indented:0};return t.globalVars&&"object"==typeof t.globalVars&&(r.globalVars=t.globalVars),r},token:function(e,t){if(e.sol()&&(t.lexical.hasOwnProperty("align")||(t.lexical.align=!1),t.indented=e.indentation()),e.eatSpace())return null;var n=t.tokenize(e,t);return"comment"==f?n:(t.reAllowed=!("operator"!=f&&"keyword c"!=f&&!f.match(/^[\[{}\(,;:]$/)),t.kwAllowed="."!=f,function(e,t,n,r,i){var o=e.cc;for(S.state=e,S.stream=i,S.marked=null,S.cc=o,e.lexical.hasOwnProperty("align")||(e.lexical.align=!0);;)if((o.length?o.pop():I)(n,r)){for(;o.length&&o[o.length-1].lex;)o.pop()();return S.marked?S.marked:"variable"==n&&w(e,r)?"variable-2":"variable"==n&&k(e,r)?"variable-3":t}}(t,n,f,i,e))},indent:function(e,t){if(e.tokenize!=v)return 0;var r=t&&t.charAt(0),i=e.lexical;"stat"==i.type&&"}"==r&&(i=i.prev);var o=i.type,a=r==o;return"vardef"==o?i.indented+4:"form"==o&&"{"==r?i.indented:"stat"==o||"form"==o?i.indented+n:"switch"!=i.info||a?i.align?i.column+(a?0:1):i.indented+(a?0:n):i.indented+(/^(?:case|default)\b/.test(t)?n:2*n)},electricChars:"{}",blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:"//"}})),e.defineMIME("text/x-haxe","haxe"),e.defineMode("hxml",(function(){return{startState:function(){return{define:!1,inString:!1}},token:function(e,t){var n=e.peek(),r=e.sol();if("#"==n)return e.skipToEnd(),"comment";if(r&&"-"==n){var i="variable-2";return e.eat(/-/),"-"==e.peek()&&(e.eat(/-/),i="keyword a"),"D"==e.peek()&&(e.eat(/[D]/),i="keyword c",t.define=!0),e.eatWhile(/[A-Z]/i),i}return n=e.peek(),0==t.inString&&"'"==n&&(t.inString=!0,e.next()),1==t.inString?(e.skipTo("'")||e.skipToEnd(),"'"==e.peek()&&(e.next(),t.inString=!1),"string"):(e.next(),null)},lineComment:"#"}})),e.defineMIME("text/x-hxml","hxml")}(n(0))},function(e,t,n){!function(e){"use strict";e.defineMode("http",(function(){function e(e,t){return e.skipToEnd(),t.cur=a,"error"}function t(t,r){return t.match(/^HTTP\/\d\.\d/)?(r.cur=n,"keyword"):t.match(/^[A-Z]+/)&&/[ \t]/.test(t.peek())?(r.cur=i,"keyword"):e(t,r)}function n(t,n){var i=t.match(/^\d+/);if(!i)return e(t,n);n.cur=r;var o=Number(i[0]);return o>=100&&o<200?"positive informational":o>=200&&o<300?"positive success":o>=300&&o<400?"positive redirect":o>=400&&o<500?"negative client-error":o>=500&&o<600?"negative server-error":"error"}function r(e,t){return e.skipToEnd(),t.cur=a,null}function i(e,t){return e.eatWhile(/\S/),t.cur=o,"string-2"}function o(t,n){return t.match(/^HTTP\/\d\.\d$/)?(n.cur=a,"keyword"):e(t,n)}function a(e){return e.sol()&&!e.eat(/[ \t]/)?e.match(/^.*?:/)?"atom":(e.skipToEnd(),"error"):(e.skipToEnd(),"string")}function l(e){return e.skipToEnd(),null}return{token:function(e,t){var n=t.cur;return n!=a&&n!=l&&e.eatSpace()?null:n(e,t)},blankLine:function(e){e.cur=l},startState:function(){return{cur:t}}}})),e.defineMIME("message/http","http")}(n(0))},function(e,t,n){!function(e){"use strict";var t={script:[["lang",/(javascript|babel)/i,"javascript"],["type",/^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i,"javascript"],["type",/./,"text/plain"],[null,null,"javascript"]],style:[["lang",/^css$/i,"css"],["type",/^(text\/)?(x-)?(stylesheet|css)$/i,"css"],["type",/./,"text/plain"],[null,null,"css"]]},n={};function r(e,t){var r=e.match(function(e){var t=n[e];return t||(n[e]=new RegExp("\\s+"+e+"\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*"))}(t));return r?/^\s*(.*?)\s*$/.exec(r[2])[1]:""}function i(e,t){return new RegExp((t?"^":"")+"</s*"+e+"s*>","i")}function o(e,t){for(var n in e)for(var r=t[n]||(t[n]=[]),i=e[n],o=i.length-1;o>=0;o--)r.unshift(i[o])}e.defineMode("htmlmixed",(function(n,a){var l=e.getMode(n,{name:"xml",htmlMode:!0,multilineTagIndentFactor:a.multilineTagIndentFactor,multilineTagIndentPastTag:a.multilineTagIndentPastTag}),s={},c=a&&a.tags,u=a&&a.scriptTypes;if(o(t,s),c&&o(c,s),u)for(var f=u.length-1;f>=0;f--)s.script.unshift(["type",u[f].matches,u[f].mode]);function d(t,o){var a,c=l.token(t,o.htmlState),u=/\btag\b/.test(c);if(u&&!/[<>\s\/]/.test(t.current())&&(a=o.htmlState.tagName&&o.htmlState.tagName.toLowerCase())&&s.hasOwnProperty(a))o.inTag=a+" ";else if(o.inTag&&u&&/>$/.test(t.current())){var f=/^([\S]+) (.*)/.exec(o.inTag);o.inTag=null;var h=">"==t.current()&&function(e,t){for(var n=0;n<e.length;n++){var i=e[n];if(!i[0]||i[1].test(r(t,i[0])))return i[2]}}(s[f[1]],f[2]),p=e.getMode(n,h),m=i(f[1],!0),g=i(f[1],!1);o.token=function(e,t){return e.match(m,!1)?(t.token=d,t.localState=t.localMode=null,null):function(e,t,n){var r=e.current(),i=r.search(t);return i>-1?e.backUp(r.length-i):r.match(/<\/?$/)&&(e.backUp(r.length),e.match(t,!1)||e.match(r)),n}(e,g,t.localMode.token(e,t.localState))},o.localMode=p,o.localState=e.startState(p,l.indent(o.htmlState,"",""))}else o.inTag&&(o.inTag+=t.current(),t.eol()&&(o.inTag+=" "));return c}return{startState:function(){return{token:d,inTag:null,localMode:null,localState:null,htmlState:e.startState(l)}},copyState:function(t){var n;return t.localState&&(n=e.copyState(t.localMode,t.localState)),{token:t.token,inTag:t.inTag,localMode:t.localMode,localState:n,htmlState:e.copyState(l,t.htmlState)}},token:function(e,t){return t.token(e,t)},indent:function(t,n,r){return!t.localMode||/^\s*<\//.test(n)?l.indent(t.htmlState,n,r):t.localMode.indent?t.localMode.indent(t.localState,n,r):e.Pass},innerMode:function(e){return{state:e.localState||e.htmlState,mode:e.localMode||l}}}}),"xml","javascript","css"),e.defineMIME("text/html","htmlmixed")}(n(0),n(3),n(2),n(5))},function(e,t,n){!function(e){"use strict";function t(e,t,n,r){this.state=e,this.mode=t,this.depth=n,this.prev=r}function n(r){return new t(e.copyState(r.mode,r.state),r.mode,r.depth,r.prev&&n(r.prev))}e.defineMode("jsx",(function(r,i){var o=e.getMode(r,{name:"xml",allowMissing:!0,multilineTagIndentPastTag:!1,allowMissingTagName:!0}),a=e.getMode(r,i&&i.base||"javascript");function l(e){var t=e.tagName;e.tagName=null;var n=o.indent(e,"","");return e.tagName=t,n}function s(n,i){return i.context.mode==o?function(n,i,c){if(2==c.depth)return n.match(/^.*?\*\//)?c.depth=1:n.skipToEnd(),"comment";if("{"==n.peek()){o.skipAttribute(c.state);var u=l(c.state),f=c.state.context;if(f&&n.match(/^[^>]*>\s*$/,!1)){for(;f.prev&&!f.startOfLine;)f=f.prev;f.startOfLine?u-=r.indentUnit:c.prev.state.lexical&&(u=c.prev.state.lexical.indented)}else 1==c.depth&&(u+=r.indentUnit);return i.context=new t(e.startState(a,u),a,0,i.context),null}if(1==c.depth){if("<"==n.peek())return o.skipAttribute(c.state),i.context=new t(e.startState(o,l(c.state)),o,0,i.context),null;if(n.match("//"))return n.skipToEnd(),"comment";if(n.match("/*"))return c.depth=2,s(n,i)}var d,h=o.token(n,c.state),p=n.current();return/\btag\b/.test(h)?/>$/.test(p)?c.state.context?c.depth=0:i.context=i.context.prev:/^</.test(p)&&(c.depth=1):!h&&(d=p.indexOf("{"))>-1&&n.backUp(p.length-d),h}(n,i,i.context):function(n,r,i){if("<"==n.peek()&&a.expressionAllowed(n,i.state))return a.skipExpression(i.state),r.context=new t(e.startState(o,a.indent(i.state,"","")),o,0,r.context),null;var l=a.token(n,i.state);if(!l&&null!=i.depth){var s=n.current();"{"==s?i.depth++:"}"==s&&0==--i.depth&&(r.context=r.context.prev)}return l}(n,i,i.context)}return{startState:function(){return{context:new t(e.startState(a),a)}},copyState:function(e){return{context:n(e.context)}},token:s,indent:function(e,t,n){return e.context.mode.indent(e.context.state,t,n)},innerMode:function(e){return e.context}}}),"xml","javascript"),e.defineMIME("text/jsx","jsx"),e.defineMIME("text/typescript-jsx",{name:"jsx",base:{name:"javascript",typescript:!0}})}(n(0),n(3),n(2))},function(e,t,n){!function(e){"use strict";e.defineMode("coffeescript",(function(e,t){function n(e){return new RegExp("^(("+e.join(")|(")+"))\\b")}var r=/^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/,i=/^(?:[()\[\]{},:`=;]|\.\.?\.?)/,o=/^[_A-Za-z$][_A-Za-z$0-9]*/,a=/^@[_A-Za-z$][_A-Za-z$0-9]*/,l=n(["and","or","not","is","isnt","in","instanceof","typeof"]),s=["for","while","loop","if","unless","else","switch","try","catch","finally","class"],c=n(s.concat(["break","by","continue","debugger","delete","do","in","of","new","return","then","this","@","throw","when","until","extends"]));s=n(s);var u=/^('{3}|\"{3}|['\"])/,f=/^(\/{3}|\/)/,d=n(["Infinity","NaN","undefined","null","true","false","on","off","yes","no"]);function h(e,t){if(e.sol()){null===t.scope.align&&(t.scope.align=!1);var n=t.scope.offset;if(e.eatSpace()){var s=e.indentation();return s>n&&"coffee"==t.scope.type?"indent":s<n?"dedent":null}n>0&&v(e,t)}if(e.eatSpace())return null;var h=e.peek();if(e.match("####"))return e.skipToEnd(),"comment";if(e.match("###"))return t.tokenize=m,t.tokenize(e,t);if("#"===h)return e.skipToEnd(),"comment";if(e.match(/^-?[0-9\.]/,!1)){var g=!1;if(e.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)&&(g=!0),e.match(/^-?\d+\.\d*/)&&(g=!0),e.match(/^-?\.\d+/)&&(g=!0),g)return"."==e.peek()&&e.backUp(1),"number";var y=!1;if(e.match(/^-?0x[0-9a-f]+/i)&&(y=!0),e.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)&&(y=!0),e.match(/^-?0(?![\dx])/i)&&(y=!0),y)return"number"}if(e.match(u))return t.tokenize=p(e.current(),!1,"string"),t.tokenize(e,t);if(e.match(f)){if("/"!=e.current()||e.match(/^.*\//,!1))return t.tokenize=p(e.current(),!0,"string-2"),t.tokenize(e,t);e.backUp(1)}return e.match(r)||e.match(l)?"operator":e.match(i)?"punctuation":e.match(d)?"atom":e.match(a)||t.prop&&e.match(o)?"property":e.match(c)?"keyword":e.match(o)?"variable":(e.next(),"error")}function p(e,n,r){return function(i,o){for(;!i.eol();)if(i.eatWhile(/[^'"\/\\]/),i.eat("\\")){if(i.next(),n&&i.eol())return r}else{if(i.match(e))return o.tokenize=h,r;i.eat(/['"\/]/)}return n&&(t.singleLineStringErrors?r="error":o.tokenize=h),r}}function m(e,t){for(;!e.eol();){if(e.eatWhile(/[^#]/),e.match("###")){t.tokenize=h;break}e.eatWhile("#")}return"comment"}function g(t,n,r){r=r||"coffee";for(var i=0,o=!1,a=null,l=n.scope;l;l=l.prev)if("coffee"===l.type||"}"==l.type){i=l.offset+e.indentUnit;break}"coffee"!==r?(o=null,a=t.column()+t.current().length):n.scope.align&&(n.scope.align=!1),n.scope={offset:i,type:r,prev:n.scope,align:o,alignOffset:a}}function v(e,t){if(t.scope.prev){if("coffee"===t.scope.type){for(var n=e.indentation(),r=!1,i=t.scope;i;i=i.prev)if(n===i.offset){r=!0;break}if(!r)return!0;for(;t.scope.prev&&t.scope.offset!==n;)t.scope=t.scope.prev;return!1}return t.scope=t.scope.prev,!1}}return{startState:function(e){return{tokenize:h,scope:{offset:e||0,type:"coffee",prev:null,align:!1},prop:!1,dedent:0}},token:function(e,t){var n=null===t.scope.align&&t.scope;n&&e.sol()&&(n.align=!1);var r=function(e,t){var n=t.tokenize(e,t),r=e.current();"return"===r&&(t.dedent=!0),(("->"===r||"=>"===r)&&e.eol()||"indent"===n)&&g(e,t);var i="[({".indexOf(r);if(-1!==i&&g(e,t,"])}".slice(i,i+1)),s.exec(r)&&g(e,t),"then"==r&&v(e,t),"dedent"===n&&v(e,t))return"error";if(-1!==(i="])}".indexOf(r))){for(;"coffee"==t.scope.type&&t.scope.prev;)t.scope=t.scope.prev;t.scope.type==r&&(t.scope=t.scope.prev)}return t.dedent&&e.eol()&&("coffee"==t.scope.type&&t.scope.prev&&(t.scope=t.scope.prev),t.dedent=!1),n}(e,t);return r&&"comment"!=r&&(n&&(n.align=!0),t.prop="punctuation"==r&&"."==e.current()),r},indent:function(e,t){if(e.tokenize!=h)return 0;var n=e.scope,r=t&&"])}".indexOf(t.charAt(0))>-1;if(r)for(;"coffee"==n.type&&n.prev;)n=n.prev;var i=r&&n.type===t.charAt(0);return n.align?n.alignOffset-(i?1:0):(i?n.prev:n).offset},lineComment:"#",fold:"indent"}})),e.defineMIME("application/vnd.coffeescript","coffeescript"),e.defineMIME("text/x-coffeescript","coffeescript"),e.defineMIME("text/coffeescript","coffeescript")}(n(0))},function(e,t,n){!function(e){"use strict";e.defineMode("elm",(function(){function e(e,t,n){return t(n),n(e,t)}var t=/[a-z_]/,n=/[A-Z]/,r=/[0-9]/,i=/[0-9A-Fa-f]/,o=/[0-7]/,a=/[a-z_A-Z0-9\']/,l=/[-!#$%&*+.\/<=>?@\\^|~:\u03BB\u2192]/,s=/[(),;[\]`{}]/,c=/[ \t\v\f]/;function u(){return function(d,h){if(d.eatWhile(c))return null;var p=d.next();if(s.test(p)){if("{"==p&&d.eat("-")){var m="comment";return d.eat("#")&&(m="meta"),e(d,h,function e(t,n){return 0==n?u():function(r,i){for(var o=n;!r.eol();){var a=r.next();if("{"==a&&r.eat("-"))++o;else if("-"==a&&r.eat("}")&&0==--o)return i(u()),t}return i(e(t,o)),t}}(m,1))}return null}if("'"==p)return d.eat("\\"),d.next(),d.eat("'")?"string":"error";if('"'==p)return e(d,h,f);if(n.test(p))return d.eatWhile(a),d.eat(".")?"qualifier":"variable-2";if(t.test(p)){var g=1===d.pos;return d.eatWhile(a),g?"type":"variable"}if(r.test(p)){if("0"==p){if(d.eat(/[xX]/))return d.eatWhile(i),"integer";if(d.eat(/[oO]/))return d.eatWhile(o),"number"}return d.eatWhile(r),m="number",d.eat(".")&&(m="number",d.eatWhile(r)),d.eat(/[eE]/)&&(m="number",d.eat(/[-+]/),d.eatWhile(r)),m}return l.test(p)?"-"==p&&d.eat(/-/)&&(d.eatWhile(/-/),!d.eat(l))?(d.skipToEnd(),"comment"):(d.eatWhile(l),"builtin"):"error"}}function f(e,t){for(;!e.eol();){var n=e.next();if('"'==n)return t(u()),"string";if("\\"==n){if(e.eol()||e.eat(c))return t(d),"string";e.eat("&")||e.next()}}return t(u()),"error"}function d(t,n){return t.eat("\\")?e(t,n,f):(t.next(),n(u()),"error")}var h=function(){for(var e={},t=["case","of","as","if","then","else","let","in","infix","infixl","infixr","type","alias","input","output","foreign","loopback","module","where","import","exposing","_","..","|",":","=","\\",'"',"->","<-"],n=t.length;n--;)e[t[n]]="keyword";return e}();return{startState:function(){return{f:u()}},copyState:function(e){return{f:e.f}},token:function(e,t){var n=t.f(e,(function(e){t.f=e})),r=e.current();return h.hasOwnProperty(r)?h[r]:n}}})),e.defineMIME("text/x-elm","elm")}(n(0))},function(e,t,n){!function(e){"use strict";function t(e,t,n,r,i,o){this.indented=e,this.column=t,this.type=n,this.info=r,this.align=i,this.prev=o}function n(e,n,r,i){var o=e.indented;return e.context&&"statement"==e.context.type&&"statement"!=r&&(o=e.context.indented),e.context=new t(o,n,r,i,null,e.context)}function r(e){var t=e.context.type;return")"!=t&&"]"!=t&&"}"!=t||(e.indented=e.context.indented),e.context=e.context.prev}function i(e,t,n){return"variable"==t.prevToken||"type"==t.prevToken||!!/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(e.string.slice(0,n))||!(!t.typeAtEndOfLine||e.column()!=e.indentation())||void 0}function o(e){for(;;){if(!e||"top"==e.type)return!0;if("}"==e.type&&"namespace"!=e.prev.info)return!1;e=e.prev}}function a(e){for(var t={},n=e.split(" "),r=0;r<n.length;++r)t[n[r]]=!0;return t}function l(e,t){return"function"==typeof e?e(t):e.propertyIsEnumerable(t)}e.defineMode("clike",(function(a,s){var c,u,f=a.indentUnit,d=s.statementIndentUnit||f,h=s.dontAlignCalls,p=s.keywords||{},m=s.types||{},g=s.builtin||{},v=s.blockKeywords||{},y=s.defKeywords||{},b=s.atoms||{},x=s.hooks||{},w=s.multiLineStrings,k=!1!==s.indentStatements,C=!1!==s.indentSwitch,S=s.namespaceSeparator,T=s.isPunctuationChar||/[\[\]{}\(\),;\:\.]/,L=s.numberStart||/[\d\.]/,M=s.number||/^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,O=s.isOperatorChar||/[+\-*&%=<>!?|\/]/,N=s.isIdentifierChar||/[\w\$_\xa1-\uffff]/,A=s.isReservedIdentifier||!1;function E(e,t){var n,r=e.next();if(x[r]){var i=x[r](e,t);if(!1!==i)return i}if('"'==r||"'"==r)return t.tokenize=(n=r,function(e,t){for(var r,i=!1,o=!1;null!=(r=e.next());){if(r==n&&!i){o=!0;break}i=!i&&"\\"==r}return(o||!i&&!w)&&(t.tokenize=null),"string"}),t.tokenize(e,t);if(T.test(r))return c=r,null;if(L.test(r)){if(e.backUp(1),e.match(M))return"number";e.next()}if("/"==r){if(e.eat("*"))return t.tokenize=z,z(e,t);if(e.eat("/"))return e.skipToEnd(),"comment"}if(O.test(r)){for(;!e.match(/^\/[\/*]/,!1)&&e.eat(O););return"operator"}if(e.eatWhile(N),S)for(;e.match(S);)e.eatWhile(N);var o=e.current();return l(p,o)?(l(v,o)&&(c="newstatement"),l(y,o)&&(u=!0),"keyword"):l(m,o)?"type":l(g,o)||A&&A(o)?(l(v,o)&&(c="newstatement"),"builtin"):l(b,o)?"atom":"variable"}function z(e,t){for(var n,r=!1;n=e.next();){if("/"==n&&r){t.tokenize=null;break}r="*"==n}return"comment"}function P(e,t){s.typeFirstDefinitions&&e.eol()&&o(t.context)&&(t.typeAtEndOfLine=i(e,t,e.pos))}return{startState:function(e){return{tokenize:null,context:new t((e||0)-f,0,"top",null,!1),indented:0,startOfLine:!0,prevToken:null}},token:function(e,t){var a=t.context;if(e.sol()&&(null==a.align&&(a.align=!1),t.indented=e.indentation(),t.startOfLine=!0),e.eatSpace())return P(e,t),null;c=u=null;var l=(t.tokenize||E)(e,t);if("comment"==l||"meta"==l)return l;if(null==a.align&&(a.align=!0),";"==c||":"==c||","==c&&e.match(/^\s*(?:\/\/.*)?$/,!1))for(;"statement"==t.context.type;)r(t);else if("{"==c)n(t,e.column(),"}");else if("["==c)n(t,e.column(),"]");else if("("==c)n(t,e.column(),")");else if("}"==c){for(;"statement"==a.type;)a=r(t);for("}"==a.type&&(a=r(t));"statement"==a.type;)a=r(t)}else c==a.type?r(t):k&&(("}"==a.type||"top"==a.type)&&";"!=c||"statement"==a.type&&"newstatement"==c)&&n(t,e.column(),"statement",e.current());if("variable"==l&&("def"==t.prevToken||s.typeFirstDefinitions&&i(e,t,e.start)&&o(t.context)&&e.match(/^\s*\(/,!1))&&(l="def"),x.token){var f=x.token(e,t,l);void 0!==f&&(l=f)}return"def"==l&&!1===s.styleDefs&&(l="variable"),t.startOfLine=!1,t.prevToken=u?"def":l||c,P(e,t),l},indent:function(t,n){if(t.tokenize!=E&&null!=t.tokenize||t.typeAtEndOfLine)return e.Pass;var r=t.context,i=n&&n.charAt(0),o=i==r.type;if("statement"==r.type&&"}"==i&&(r=r.prev),s.dontIndentStatements)for(;"statement"==r.type&&s.dontIndentStatements.test(r.info);)r=r.prev;if(x.indent){var a=x.indent(t,r,n,f);if("number"==typeof a)return a}var l=r.prev&&"switch"==r.prev.info;if(s.allmanIndentation&&/[{(]/.test(i)){for(;"top"!=r.type&&"}"!=r.type;)r=r.prev;return r.indented}return"statement"==r.type?r.indented+("{"==i?0:d):!r.align||h&&")"==r.type?")"!=r.type||o?r.indented+(o?0:f)+(o||!l||/^(?:case|default)\b/.test(n)?0:f):r.indented+d:r.column+(o?0:1)},electricInput:C?/^\s*(?:case .*?:|default:|\{\}?|\})$/:/^\s*[{}]$/,blockCommentStart:"/*",blockCommentEnd:"*/",blockCommentContinue:" * ",lineComment:"//",fold:"brace"}}));var s="auto if break case register continue return default do sizeof static else struct switch extern typedef union for goto while enum const volatile inline restrict asm fortran",c=a("int long char short double float unsigned signed void bool"),u=a("SEL instancetype id Class Protocol BOOL");function f(e){return l(c,e)||/.+_t$/.test(e)}var d="case do else for if switch while struct enum union";function h(e,t){if(!t.startOfLine)return!1;for(var n,r=null;n=e.peek();){if("\\"==n&&e.match(/^.$/)){r=h;break}if("/"==n&&e.match(/^\/[\/\*]/,!1))break;e.next()}return t.tokenize=r,"meta"}function p(e,t){return"type"==t.prevToken&&"type"}function m(e){return!(!e||e.length<2||"_"!=e[0]||"_"!=e[1]&&e[1]===e[1].toLowerCase())}function g(e){return e.eatWhile(/[\w\.']/),"number"}function v(e,t){if(e.backUp(1),e.match(/(R|u8R|uR|UR|LR)/)){var n=e.match(/"([^\s\\()]{0,16})\(/);return!!n&&(t.cpp11RawStringDelim=n[1],t.tokenize=b,b(e,t))}return e.match(/(u8|u|U|L)/)?!!e.match(/["']/,!1)&&"string":(e.next(),!1)}function y(e,t){for(var n;null!=(n=e.next());)if('"'==n&&!e.eat('"')){t.tokenize=null;break}return"string"}function b(e,t){var n=t.cpp11RawStringDelim.replace(/[^\w\s]/g,"\\$&");return e.match(new RegExp(".*?\\)"+n+'"'))?t.tokenize=null:e.skipToEnd(),"string"}function x(t,n){"string"==typeof t&&(t=[t]);var r=[];function i(e){if(e)for(var t in e)e.hasOwnProperty(t)&&r.push(t)}i(n.keywords),i(n.types),i(n.builtin),i(n.atoms),r.length&&(n.helperType=t[0],e.registerHelper("hintWords",t[0],r));for(var o=0;o<t.length;++o)e.defineMIME(t[o],n)}function w(e,t){for(var n=!1;!e.eol();){if(!n&&e.match('"""')){t.tokenize=null;break}n="\\"==e.next()&&!n}return"string"}function k(e){return function(t,n){for(var r;r=t.next();){if("*"==r&&t.eat("/")){if(1==e){n.tokenize=null;break}return n.tokenize=k(e-1),n.tokenize(t,n)}if("/"==r&&t.eat("*"))return n.tokenize=k(e+1),n.tokenize(t,n)}return"comment"}}x(["text/x-csrc","text/x-c","text/x-chdr"],{name:"clike",keywords:a(s),types:f,blockKeywords:a(d),defKeywords:a("struct enum union"),typeFirstDefinitions:!0,atoms:a("NULL true false"),isReservedIdentifier:m,hooks:{"#":h,"*":p},modeProps:{fold:["brace","include"]}}),x(["text/x-c++src","text/x-c++hdr"],{name:"clike",keywords:a(s+"alignas alignof and and_eq audit axiom bitand bitor catch class compl concept constexpr const_cast decltype delete dynamic_cast explicit export final friend import module mutable namespace new noexcept not not_eq operator or or_eq override private protected public reinterpret_cast requires static_assert static_cast template this thread_local throw try typeid typename using virtual xor xor_eq"),types:f,blockKeywords:a(d+" class try catch"),defKeywords:a("struct enum union class namespace"),typeFirstDefinitions:!0,atoms:a("true false NULL nullptr"),dontIndentStatements:/^template$/,isIdentifierChar:/[\w\$_~\xa1-\uffff]/,isReservedIdentifier:m,hooks:{"#":h,"*":p,u:v,U:v,L:v,R:v,0:g,1:g,2:g,3:g,4:g,5:g,6:g,7:g,8:g,9:g,token:function(e,t,n){if("variable"==n&&"("==e.peek()&&(";"==t.prevToken||null==t.prevToken||"}"==t.prevToken)&&(r=e.current(),(i=/(\w+)::~?(\w+)$/.exec(r))&&i[1]==i[2]))return"def";var r,i}},namespaceSeparator:"::",modeProps:{fold:["brace","include"]}}),x("text/x-java",{name:"clike",keywords:a("abstract assert break case catch class const continue default do else enum extends final finally for goto if implements import instanceof interface native new package private protected public return static strictfp super switch synchronized this throw throws transient try volatile while @interface"),types:a("byte short int long float double boolean char void Boolean Byte Character Double Float Integer Long Number Object Short String StringBuffer StringBuilder Void"),blockKeywords:a("catch class do else finally for if switch try while"),defKeywords:a("class interface enum @interface"),typeFirstDefinitions:!0,atoms:a("true false null"),number:/^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,hooks:{"@":function(e){return!e.match("interface",!1)&&(e.eatWhile(/[\w\$_]/),"meta")}},modeProps:{fold:["brace","import"]}}),x("text/x-csharp",{name:"clike",keywords:a("abstract as async await base break case catch checked class const continue default delegate do else enum event explicit extern finally fixed for foreach goto if implicit in interface internal is lock namespace new operator out override params private protected public readonly ref return sealed sizeof stackalloc static struct switch this throw try typeof unchecked unsafe using virtual void volatile while add alias ascending descending dynamic from get global group into join let orderby partial remove select set value var yield"),types:a("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32 UInt64 bool byte char decimal double short int long object sbyte float string ushort uint ulong"),blockKeywords:a("catch class do else finally for foreach if struct switch try while"),defKeywords:a("class interface namespace struct var"),typeFirstDefinitions:!0,atoms:a("true false null"),hooks:{"@":function(e,t){return e.eat('"')?(t.tokenize=y,y(e,t)):(e.eatWhile(/[\w\$_]/),"meta")}}}),x("text/x-scala",{name:"clike",keywords:a("abstract case catch class def do else extends final finally for forSome if implicit import lazy match new null object override package private protected return sealed super this throw trait try type val var while with yield _ assert assume require print println printf readLine readBoolean readByte readShort readChar readInt readLong readFloat readDouble"),types:a("AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"),multiLineStrings:!0,blockKeywords:a("catch class enum do else finally for forSome if match switch try while"),defKeywords:a("class enum def object package trait type val var"),atoms:a("true false null"),indentStatements:!1,indentSwitch:!1,isOperatorChar:/[+\-*&%=<>!?|\/#:@]/,hooks:{"@":function(e){return e.eatWhile(/[\w\$_]/),"meta"},'"':function(e,t){return!!e.match('""')&&(t.tokenize=w,t.tokenize(e,t))},"'":function(e){return e.eatWhile(/[\w\$_\xa1-\uffff]/),"atom"},"=":function(e,n){var r=n.context;return!("}"!=r.type||!r.align||!e.eat(">"))&&(n.context=new t(r.indented,r.column,r.type,r.info,null,r.prev),"operator")},"/":function(e,t){return!!e.eat("*")&&(t.tokenize=k(1),t.tokenize(e,t))}},modeProps:{closeBrackets:{pairs:'()[]{}""',triples:'"'}}}),x("text/x-kotlin",{name:"clike",keywords:a("package as typealias class interface this super val operator var fun for is in This throw return annotation break continue object if else while do try when !in !is as? file import where by get set abstract enum open inner override private public internal protected catch finally out final vararg reified dynamic companion constructor init sealed field property receiver param sparam lateinit data inline noinline tailrec external annotation crossinline const operator infix suspend actual expect setparam"),types:a("Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable Compiler Double Exception Float Integer Long Math Number Object Package Pair Process Runtime Runnable SecurityManager Short StackTraceElement StrictMath String StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy LazyThreadSafetyMode LongArray Nothing ShortArray Unit"),intendSwitch:!1,indentStatements:!1,multiLineStrings:!0,number:/^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,blockKeywords:a("catch class do else finally for if where try while enum"),defKeywords:a("class val var object interface fun"),atoms:a("true false null this"),hooks:{"@":function(e){return e.eatWhile(/[\w\$_]/),"meta"},"*":function(e,t){return"."==t.prevToken?"variable":"operator"},'"':function(e,t){var n;return t.tokenize=(n=e.match('""'),function(e,t){for(var r,i=!1,o=!1;!e.eol();){if(!n&&!i&&e.match('"')){o=!0;break}if(n&&e.match('"""')){o=!0;break}r=e.next(),!i&&"$"==r&&e.match("{")&&e.skipTo("}"),i=!i&&"\\"==r&&!n}return!o&&n||(t.tokenize=null),"string"}),t.tokenize(e,t)},"/":function(e,t){return!!e.eat("*")&&(t.tokenize=k(1),t.tokenize(e,t))},indent:function(e,t,n,r){var i=n&&n.charAt(0);return"}"!=e.prevToken&&")"!=e.prevToken||""!=n?"operator"==e.prevToken&&"}"!=n&&"}"!=e.context.type||"variable"==e.prevToken&&"."==i||("}"==e.prevToken||")"==e.prevToken)&&"."==i?2*r+t.indented:t.align&&"}"==t.type?t.indented+(e.context.type==(n||"").charAt(0)?0:r):void 0:e.indented}},modeProps:{closeBrackets:{triples:'"'}}}),x(["x-shader/x-vertex","x-shader/x-fragment"],{name:"clike",keywords:a("sampler1D sampler2D sampler3D samplerCube sampler1DShadow sampler2DShadow const attribute uniform varying break continue discard return for while do if else struct in out inout"),types:a("float int bool void vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 mat2 mat3 mat4"),blockKeywords:a("for while do if else struct"),builtin:a("radians degrees sin cos tan asin acos atan pow exp log exp2 sqrt inversesqrt abs sign floor ceil fract mod min max clamp mix step smoothstep length distance dot cross normalize ftransform faceforward reflect refract matrixCompMult lessThan lessThanEqual greaterThan greaterThanEqual equal notEqual any all not texture1D texture1DProj texture1DLod texture1DProjLod texture2D texture2DProj texture2DLod texture2DProjLod texture3D texture3DProj texture3DLod texture3DProjLod textureCube textureCubeLod shadow1D shadow2D shadow1DProj shadow2DProj shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod dFdx dFdy fwidth noise1 noise2 noise3 noise4"),atoms:a("true false gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 gl_FogCoord gl_PointCoord gl_Position gl_PointSize gl_ClipVertex gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor gl_TexCoord gl_FogFragCoord gl_FragCoord gl_FrontFacing gl_FragData gl_FragDepth gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose gl_ProjectionMatrixInverseTranspose gl_ModelViewProjectionMatrixInverseTranspose gl_TextureMatrixInverseTranspose gl_NormalScale gl_DepthRange gl_ClipPlane gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel gl_FrontLightModelProduct gl_BackLightModelProduct gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ gl_FogParameters gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits gl_MaxDrawBuffers"),indentSwitch:!1,hooks:{"#":h},modeProps:{fold:["brace","include"]}}),x("text/x-nesc",{name:"clike",keywords:a(s+" as atomic async call command component components configuration event generic implementation includes interface module new norace nx_struct nx_union post provides signal task uses abstract extends"),types:f,blockKeywords:a(d),atoms:a("null true false"),hooks:{"#":h},modeProps:{fold:["brace","include"]}}),x("text/x-objectivec",{name:"clike",keywords:a(s+" bycopy byref in inout oneway out self super atomic nonatomic retain copy readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd @interface @implementation @end @protocol @encode @property @synthesize @dynamic @class @public @package @private @protected @required @optional @try @catch @finally @import @selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available"),types:function(e){return f(e)||l(u,e)},builtin:a("FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION NS_RETURNS_RETAINED NS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER NS_DESIGNATED_INITIALIZER NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT"),blockKeywords:a(d+" @synthesize @try @catch @finally @autoreleasepool @synchronized"),defKeywords:a("struct enum union @interface @implementation @protocol @class"),dontIndentStatements:/^@.*$/,typeFirstDefinitions:!0,atoms:a("YES NO NULL Nil nil true false nullptr"),isReservedIdentifier:m,hooks:{"#":h,"*":p},modeProps:{fold:["brace","include"]}}),x("text/x-squirrel",{name:"clike",keywords:a("base break clone continue const default delete enum extends function in class foreach local resume return this throw typeof yield constructor instanceof static"),types:f,blockKeywords:a("case catch class else for foreach if switch try while"),defKeywords:a("function local class"),typeFirstDefinitions:!0,atoms:a("true false null"),hooks:{"#":h},modeProps:{fold:["brace","include"]}});var C=null;x("text/x-ceylon",{name:"clike",keywords:a("abstracts alias assembly assert assign break case catch class continue dynamic else exists extends finally for function given if import in interface is let module new nonempty object of out outer package return satisfies super switch then this throw try value void while"),types:function(e){var t=e.charAt(0);return t===t.toUpperCase()&&t!==t.toLowerCase()},blockKeywords:a("case catch class dynamic else finally for function if interface module new object switch try while"),defKeywords:a("class dynamic function interface module object package value"),builtin:a("abstract actual aliased annotation by default deprecated doc final formal late license native optional sealed see serializable shared suppressWarnings tagged throws variable"),isPunctuationChar:/[\[\]{}\(\),;\:\.`]/,isOperatorChar:/[+\-*&%=<>!?|^~:\/]/,numberStart:/[\d#$]/,number:/^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,multiLineStrings:!0,typeFirstDefinitions:!0,atoms:a("true false null larger smaller equal empty finished"),indentSwitch:!1,styleDefs:!1,hooks:{"@":function(e){return e.eatWhile(/[\w\$_]/),"meta"},'"':function(e,t){return t.tokenize=function e(t){return function(n,r){for(var i,o=!1,a=!1;!n.eol();){if(!o&&n.match('"')&&("single"==t||n.match('""'))){a=!0;break}if(!o&&n.match("``")){C=e(t),a=!0;break}i=n.next(),o="single"==t&&!o&&"\\"==i}return a&&(r.tokenize=null),"string"}}(e.match('""')?"triple":"single"),t.tokenize(e,t)},"`":function(e,t){return!(!C||!e.match("`"))&&(t.tokenize=C,C=null,t.tokenize(e,t))},"'":function(e){return e.eatWhile(/[\w\$_\xa1-\uffff]/),"atom"},token:function(e,t,n){if(("variable"==n||"type"==n)&&"."==t.prevToken)return"variable-2"}},modeProps:{fold:["brace","import"],closeBrackets:{triples:'"'}}})}(n(0))},function(e,t,n){!function(e){"use strict";e.defineSimpleMode("rust",{start:[{regex:/b?"/,token:"string",next:"string"},{regex:/b?r"/,token:"string",next:"string_raw"},{regex:/b?r#+"/,token:"string",next:"string_raw_hash"},{regex:/'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/,token:"string-2"},{regex:/b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/,token:"string-2"},{regex:/(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/,token:"number"},{regex:/(let(?:\s+mut)?|fn|enum|mod|struct|type)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,token:["keyword",null,"def"]},{regex:/(?:abstract|alignof|as|box|break|continue|const|crate|do|else|enum|extern|fn|for|final|if|impl|in|loop|macro|match|mod|move|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\b/,token:"keyword"},{regex:/\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|f16|f32|f64|i8|i16|i32|i64|str|Option)\b/,token:"atom"},{regex:/\b(?:true|false|Some|None|Ok|Err)\b/,token:"builtin"},{regex:/\b(fn)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,token:["keyword",null,"def"]},{regex:/#!?\[.*\]/,token:"meta"},{regex:/\/\/.*/,token:"comment"},{regex:/\/\*/,token:"comment",next:"comment"},{regex:/[-+\/*=<>!]+/,token:"operator"},{regex:/[a-zA-Z_]\w*!/,token:"variable-3"},{regex:/[a-zA-Z_]\w*/,token:"variable"},{regex:/[\{\[\(]/,indent:!0},{regex:/[\}\]\)]/,dedent:!0}],string:[{regex:/"/,token:"string",next:"start"},{regex:/(?:[^\\"]|\\(?:.|$))*/,token:"string"}],string_raw:[{regex:/"/,token:"string",next:"start"},{regex:/[^"]*/,token:"string"}],string_raw_hash:[{regex:/"#+/,token:"string",next:"start"},{regex:/(?:[^"]|"(?!#))*/,token:"string"}],comment:[{regex:/.*?\*\//,token:"comment",next:"start"},{regex:/.*/,token:"comment"}],meta:{dontIndentStates:["comment"],electricInput:/^\s*\}$/,blockCommentStart:"/*",blockCommentEnd:"*/",lineComment:"//",fold:"brace"}}),e.defineMIME("text/x-rustsrc","rust"),e.defineMIME("text/rust","rust")}(n(0),n(25))},function(e,t,n){!function(e){"use strict";function t(e,t){if(!e.hasOwnProperty(t))throw new Error("Undefined state "+t+" in simple mode")}function n(e,t){if(!e)return/(?:)/;var n="";return e instanceof RegExp?(e.ignoreCase&&(n="i"),e=e.source):e=String(e),new RegExp((!1===t?"":"^")+"(?:"+e+")",n)}function r(e,r){(e.next||e.push)&&t(r,e.next||e.push),this.regex=n(e.regex),this.token=function(e){if(!e)return null;if(e.apply)return e;if("string"==typeof e)return e.replace(/\./g," ");for(var t=[],n=0;n<e.length;n++)t.push(e[n]&&e[n].replace(/\./g," "));return t}(e.token),this.data=e}function i(e,t){return function(n,r){if(r.pending){var i=r.pending.shift();return 0==r.pending.length&&(r.pending=null),n.pos+=i.text.length,i.token}if(r.local){if(r.local.end&&n.match(r.local.end)){var o=r.local.endToken||null;return r.local=r.localState=null,o}var l;return o=r.local.mode.token(n,r.localState),r.local.endScan&&(l=r.local.endScan.exec(n.current()))&&(n.pos=n.start+l.index),o}for(var s=e[r.state],c=0;c<s.length;c++){var u=s[c],f=(!u.data.sol||n.sol())&&n.match(u.regex);if(f){u.data.next?r.state=u.data.next:u.data.push?((r.stack||(r.stack=[])).push(r.state),r.state=u.data.push):u.data.pop&&r.stack&&r.stack.length&&(r.state=r.stack.pop()),u.data.mode&&a(t,r,u.data.mode,u.token),u.data.indent&&r.indent.push(n.indentation()+t.indentUnit),u.data.dedent&&r.indent.pop();var d=u.token;if(d&&d.apply&&(d=d(f)),f.length>2&&u.token&&"string"!=typeof u.token){r.pending=[];for(var h=2;h<f.length;h++)f[h]&&r.pending.push({text:f[h],token:u.token[h-1]});return n.backUp(f[0].length-(f[1]?f[1].length:0)),d[0]}return d&&d.join?d[0]:d}}return n.next(),null}}function o(e,t){if(e===t)return!0;if(!e||"object"!=typeof e||!t||"object"!=typeof t)return!1;var n=0;for(var r in e)if(e.hasOwnProperty(r)){if(!t.hasOwnProperty(r)||!o(e[r],t[r]))return!1;n++}for(var r in t)t.hasOwnProperty(r)&&n--;return 0==n}function a(t,r,i,a){var l;if(i.persistent)for(var s=r.persistentStates;s&&!l;s=s.next)(i.spec?o(i.spec,s.spec):i.mode==s.mode)&&(l=s);var c=l?l.mode:i.mode||e.getMode(t,i.spec),u=l?l.state:e.startState(c);i.persistent&&!l&&(r.persistentStates={mode:c,spec:i.spec,state:u,next:r.persistentStates}),r.localState=u,r.local={mode:c,end:i.end&&n(i.end),endScan:i.end&&!1!==i.forceEnd&&n(i.end,!1),endToken:a&&a.join?a[a.length-1]:a}}function l(t,n){return function(r,i,o){if(r.local&&r.local.mode.indent)return r.local.mode.indent(r.localState,i,o);if(null==r.indent||r.local||n.dontIndentStates&&function(e,t){for(var n=0;n<t.length;n++)if(t[n]===e)return!0}(r.state,n.dontIndentStates)>-1)return e.Pass;var a=r.indent.length-1,l=t[r.state];e:for(;;){for(var s=0;s<l.length;s++){var c=l[s];if(c.data.dedent&&!1!==c.data.dedentIfLineStart){var u=c.regex.exec(i);if(u&&u[0]){a--,(c.next||c.push)&&(l=t[c.next||c.push]),i=i.slice(u[0].length);continue e}}}break}return a<0?0:r.indent[a]}}e.defineSimpleMode=function(t,n){e.defineMode(t,(function(t){return e.simpleMode(t,n)}))},e.simpleMode=function(n,o){t(o,"start");var a={},s=o.meta||{},c=!1;for(var u in o)if(u!=s&&o.hasOwnProperty(u))for(var f=a[u]=[],d=o[u],h=0;h<d.length;h++){var p=d[h];f.push(new r(p,o)),(p.indent||p.dedent)&&(c=!0)}var m={startState:function(){return{state:"start",pending:null,local:null,localState:null,indent:c?[]:null}},copyState:function(t){var n={state:t.state,pending:t.pending,local:t.local,localState:null,indent:t.indent&&t.indent.slice(0)};t.localState&&(n.localState=e.copyState(t.local.mode,t.localState)),t.stack&&(n.stack=t.stack.slice(0));for(var r=t.persistentStates;r;r=r.next)n.persistentStates={mode:r.mode,spec:r.spec,state:r.state==t.localState?n.localState:e.copyState(r.mode,r.state),next:n.persistentStates};return n},token:i(a,n),innerMode:function(e){return e.local&&{mode:e.local.mode,state:e.localState}},indent:l(a,s)};if(s)for(var g in s)s.hasOwnProperty(g)&&(m[g]=s[g]);return m}}(n(0))},function(e,t,n){var r,i,o;i=[n(0)],void 0===(o="function"==typeof(r=function(e){"use strict";var t=/[\w\$_\.\\\/@]/;function n(e){var t=Object.create(null);return e.forEach((function(e){t[e]=!0})),t}e.defineMode("wasm",(function(){var e=n(["function","import","export","table","memory","segment","as","type","of","from","typeof","br","br_if","loop","br_table","if","else","call","call_import","call_indirect","nop","unreachable","var","align","select","return"]),r=n(["i32:8","i32:8u","i32:8s","i32:16","i32:16u","i32:16s","i64:8","i64:8u","i64:8s","i64:16","i64:16u","i64:16s","i64:32","i64:32u","i64:32s","i32.add","i32.sub","i32.mul","i32.div_s","i32.div_u","i32.rem_s","i32.rem_u","i32.and","i32.or","i32.xor","i32.shl","i32.shr_u","i32.shr_s","i32.rotr","i32.rotl","i32.eq","i32.ne","i32.lt_s","i32.le_s","i32.lt_u","i32.le_u","i32.gt_s","i32.ge_s","i32.gt_u","i32.ge_u","i32.clz","i32.ctz","i32.popcnt","i32.eqz","i64.add","i64.sub","i64.mul","i64.div_s","i64.div_u","i64.rem_s","i64.rem_u","i64.and","i64.or","i64.xor","i64.shl","i64.shr_u","i64.shr_s","i64.rotr","i64.rotl","i64.eq","i64.ne","i64.lt_s","i64.le_s","i64.lt_u","i64.le_u","i64.gt_s","i64.ge_s","i64.gt_u","i64.ge_u","i64.clz","i64.ctz","i64.popcnt","i64.eqz","f32.add","f32.sub","f32.mul","f32.div","f32.min","f32.max","f32.abs","f32.neg","f32.copysign","f32.ceil","f32.floor","f32.trunc","f32.nearest","f32.sqrt","f32.eq","f32.ne","f32.lt","f32.le","f32.gt","f32.ge","f64.add","f64.sub","f64.mul","f64.div","f64.min","f64.max","f64.abs","f64.neg","f64.copysign","f64.ceil","f64.floor","f64.trunc","f64.nearest","f64.sqrt","f64.eq","f64.ne","f64.lt","f64.le","f64.gt","f64.ge","i32.trunc_s/f32","i32.trunc_s/f64","i32.trunc_u/f32","i32.trunc_u/f64","i32.wrap/i64","i64.trunc_s/f32","i64.trunc_s/f64","i64.trunc_u/f32","i64.trunc_u/f64","i64.extend_s/i32","i64.extend_u/i32","f32.convert_s/i32","f32.convert_u/i32","f32.convert_s/i64","f32.convert_u/i64","f32.demote/f64","f32.reinterpret/i32","f64.convert_s/i32","f64.convert_u/i32","f64.convert_s/i64","f64.convert_u/i64","f64.promote/f32","f64.reinterpret/i64","i32.reinterpret/f32","i64.reinterpret/f64"]),i=n(["i32","i64","f32","f64"]),o=/[\-!]/,a=n(["+","-","*","/","/s","/u","%","%s","%u","<<",">>u",">>s",">=","<=","==","!=","<s","<u","<=s","<=u",">=s",">=u",">s",">u","<",">","=","&","|","^","!"]);function l(n,l){var u,f=n.next();if("$"===f)return n.eatWhile(t),"variable";if("@"===f)return n.eatWhile(t),"meta";if('"'===f)return l.tokenize=(u=f,function(e,t){for(var n,r=!1;null!=(n=e.next());){if(n==u&&!r)return t.tokenize=null,"string";r=!r&&"\\"===n}return"string"}),l.tokenize(n,l);if("/"==f){if(n.eat("*"))return l.tokenize=s,s(n,l);if(n.eat("/"))return n.skipToEnd(),"comment"}if(/\d/.test(f)||("-"===f||"+"===f)&&/\d/.test(n.peek()))return n.eatWhile(/[\w\._\-+]/),"number";if(/[\[\]\(\)\{\},:]/.test(f))return null;if(o.test(f))return"operator";n.eatWhile(t);var d=n.current();if(d in a)return"operator";if(d in e)return"keyword";if(d in i){if(!n.eat(":"))return"builtin";n.eatWhile(t),d=n.current()}return d in r?"builtin":"Temporary"===d?(l.tokenize=c,l.tokenize(n,l)):null}function s(e,t){var n;for(t.commentDepth=1;null!=(n=e.next());){if("*"===n&&e.eat("/")&&0==--t.commentDepth)return t.tokenize=null,"comment";"/"===n&&e.eat("*")&&t.commentDepth++}return"comment"}function c(e,t){for(var n,r=t.commentState;null!=(n=e.next());)if(0===r&&"t"===n)r=1;else if(1===r&&":"===n)return t.tokenize=null,t.commentState=0,r=2,"comment";return t.commentState=r,"comment"}return{startState:function(){return{tokenize:null,commentState:0,commentDepth:0}},token:function(e,t){return e.eatSpace()?null:(t.tokenize||l)(e,t)}}})),e.registerHelper("wordChars","wasm",t),e.defineMIME("text/wasm","wasm")})?r.apply(t,i):r)||(e.exports=o)},function(e,t,n){!function(e){"use strict";var t="CodeMirror-activeline-background";function n(e){for(var n=0;n<e.state.activeLines.length;n++)e.removeLineClass(e.state.activeLines[n],"wrap","CodeMirror-activeline"),e.removeLineClass(e.state.activeLines[n],"background",t),e.removeLineClass(e.state.activeLines[n],"gutter","CodeMirror-activeline-gutter")}function r(e,r){for(var i=[],o=0;o<r.length;o++){var a=r[o],l=e.getOption("styleActiveLine");if("object"==typeof l&&l.nonEmpty?a.anchor.line==a.head.line:a.empty()){var s=e.getLineHandleVisualStart(a.head.line);i[i.length-1]!=s&&i.push(s)}}(function(e,t){if(e.length!=t.length)return!1;for(var n=0;n<e.length;n++)if(e[n]!=t[n])return!1;return!0})(e.state.activeLines,i)||e.operation((function(){n(e);for(var r=0;r<i.length;r++)e.addLineClass(i[r],"wrap","CodeMirror-activeline"),e.addLineClass(i[r],"background",t),e.addLineClass(i[r],"gutter","CodeMirror-activeline-gutter");e.state.activeLines=i}))}function i(e,t){r(e,t.ranges)}e.defineOption("styleActiveLine",!1,(function(t,o,a){var l=a!=e.Init&&a;o!=l&&(l&&(t.off("beforeSelectionChange",i),n(t),delete t.state.activeLines),o&&(t.state.activeLines=[],r(t,t.listSelections()),t.on("beforeSelectionChange",i)))}))}(n(0))},function(e,t,n){!function(e){e.defineOption("showTrailingSpace",!1,(function(t,n,r){r==e.Init&&(r=!1),r&&!n?t.removeOverlay("trailingspace"):!r&&n&&t.addOverlay({token:function(e){for(var t=e.string.length,n=t;n&&/\s/.test(e.string.charAt(n-1));--n);return n>e.pos?(e.pos=n,null):(e.pos=t,"trailingspace")},name:"trailingspace"})}))}(n(0))},function(e,t,n){!function(e){"use strict";e.registerHelper("fold","brace",(function(t,n){var r,i=n.line,o=t.getLine(i);function a(a){for(var l=n.ch,s=0;;){var c=l<=0?-1:o.lastIndexOf(a,l-1);if(-1!=c){if(1==s&&c<n.ch)break;if(r=t.getTokenTypeAt(e.Pos(i,c+1)),!/^(comment|string)/.test(r))return c+1;l=c-1}else{if(1==s)break;s=1,l=o.length}}}var l="{",s="}",c=a("{");if(null==c&&(l="[",s="]",c=a("[")),null!=c){var u,f,d=1,h=t.lastLine();e:for(var p=i;p<=h;++p)for(var m=t.getLine(p),g=p==i?c:0;;){var v=m.indexOf(l,g),y=m.indexOf(s,g);if(v<0&&(v=m.length),y<0&&(y=m.length),(g=Math.min(v,y))==m.length)break;if(t.getTokenTypeAt(e.Pos(p,g+1))==r)if(g==v)++d;else if(!--d){u=p,f=g;break e}++g}if(null!=u&&i!=u)return{from:e.Pos(i,c),to:e.Pos(u,f)}}})),e.registerHelper("fold","import",(function(t,n){function r(n){if(n<t.firstLine()||n>t.lastLine())return null;var r=t.getTokenAt(e.Pos(n,1));if(/\S/.test(r.string)||(r=t.getTokenAt(e.Pos(n,r.end+1))),"keyword"!=r.type||"import"!=r.string)return null;for(var i=n,o=Math.min(t.lastLine(),n+10);i<=o;++i){var a=t.getLine(i).indexOf(";");if(-1!=a)return{startCh:r.end,end:e.Pos(i,a)}}}var i,o=n.line,a=r(o);if(!a||r(o-1)||(i=r(o-2))&&i.end.line==o-1)return null;for(var l=a.end;;){var s=r(l.line+1);if(null==s)break;l=s.end}return{from:t.clipPos(e.Pos(o,a.startCh+1)),to:l}})),e.registerHelper("fold","include",(function(t,n){function r(n){if(n<t.firstLine()||n>t.lastLine())return null;var r=t.getTokenAt(e.Pos(n,1));return/\S/.test(r.string)||(r=t.getTokenAt(e.Pos(n,r.end+1))),"meta"==r.type&&"#include"==r.string.slice(0,8)?r.start+8:void 0}var i=n.line,o=r(i);if(null==o||null!=r(i-1))return null;for(var a=i;null!=r(a+1);)++a;return{from:e.Pos(i,o+1),to:t.clipPos(e.Pos(a))}}))}(n(0))},function(e,t,n){!function(e){"use strict";e.registerGlobalHelper("fold","comment",(function(e){return e.blockCommentStart&&e.blockCommentEnd}),(function(t,n){var r=t.getModeAt(n),i=r.blockCommentStart,o=r.blockCommentEnd;if(i&&o){for(var a,l=n.line,s=t.getLine(l),c=n.ch,u=0;;){var f=c<=0?-1:s.lastIndexOf(i,c-1);if(-1!=f){if(1==u&&f<n.ch)return;if(/comment/.test(t.getTokenTypeAt(e.Pos(l,f+1)))&&(0==f||s.slice(f-o.length,f)==o||!/comment/.test(t.getTokenTypeAt(e.Pos(l,f))))){a=f+i.length;break}c=f-1}else{if(1==u)return;u=1,c=s.length}}var d,h,p=1,m=t.lastLine();e:for(var g=l;g<=m;++g)for(var v=t.getLine(g),y=g==l?a:0;;){var b=v.indexOf(i,y),x=v.indexOf(o,y);if(b<0&&(b=v.length),x<0&&(x=v.length),(y=Math.min(b,x))==v.length)break;if(y==b)++p;else if(!--p){d=g,h=y;break e}++y}if(null!=d&&(l!=d||h!=a))return{from:e.Pos(l,a),to:e.Pos(d,h)}}}))}(n(0))},function(e,t,n){!function(e){"use strict";var t=e.Pos;function n(e,t){return e.line-t.line||e.ch-t.ch}var r="A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD",i=new RegExp("<(/?)(["+r+"]["+r+"-:.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040]*)","g");function o(e,t,n,r){this.line=t,this.ch=n,this.cm=e,this.text=e.getLine(t),this.min=r?Math.max(r.from,e.firstLine()):e.firstLine(),this.max=r?Math.min(r.to-1,e.lastLine()):e.lastLine()}function a(e,n){var r=e.cm.getTokenTypeAt(t(e.line,n));return r&&/\btag\b/.test(r)}function l(e){if(!(e.line>=e.max))return e.ch=0,e.text=e.cm.getLine(++e.line),!0}function s(e){if(!(e.line<=e.min))return e.text=e.cm.getLine(--e.line),e.ch=e.text.length,!0}function c(e){for(;;){var t=e.text.indexOf(">",e.ch);if(-1==t){if(l(e))continue;return}if(a(e,t+1)){var n=e.text.lastIndexOf("/",t),r=n>-1&&!/\S/.test(e.text.slice(n+1,t));return e.ch=t+1,r?"selfClose":"regular"}e.ch=t+1}}function u(e){for(;;){var t=e.ch?e.text.lastIndexOf("<",e.ch-1):-1;if(-1==t){if(s(e))continue;return}if(a(e,t+1)){i.lastIndex=t,e.ch=t;var n=i.exec(e.text);if(n&&n.index==t)return n}else e.ch=t}}function f(e){for(;;){i.lastIndex=e.ch;var t=i.exec(e.text);if(!t){if(l(e))continue;return}if(a(e,t.index+1))return e.ch=t.index+t[0].length,t;e.ch=t.index+1}}function d(e){for(;;){var t=e.ch?e.text.lastIndexOf(">",e.ch-1):-1;if(-1==t){if(s(e))continue;return}if(a(e,t+1)){var n=e.text.lastIndexOf("/",t),r=n>-1&&!/\S/.test(e.text.slice(n+1,t));return e.ch=t+1,r?"selfClose":"regular"}e.ch=t}}function h(e,n){for(var r=[];;){var i,o=f(e),a=e.line,l=e.ch-(o?o[0].length:0);if(!o||!(i=c(e)))return;if("selfClose"!=i)if(o[1]){for(var s=r.length-1;s>=0;--s)if(r[s]==o[2]){r.length=s;break}if(s<0&&(!n||n==o[2]))return{tag:o[2],from:t(a,l),to:t(e.line,e.ch)}}else r.push(o[2])}}function p(e,n){for(var r=[];;){var i=d(e);if(!i)return;if("selfClose"!=i){var o=e.line,a=e.ch,l=u(e);if(!l)return;if(l[1])r.push(l[2]);else{for(var s=r.length-1;s>=0;--s)if(r[s]==l[2]){r.length=s;break}if(s<0&&(!n||n==l[2]))return{tag:l[2],from:t(e.line,e.ch),to:t(o,a)}}}else u(e)}}e.registerHelper("fold","xml",(function(e,r){for(var i=new o(e,r.line,0);;){var a=f(i);if(!a||i.line!=r.line)return;var l=c(i);if(!l)return;if(!a[1]&&"selfClose"!=l){var s=t(i.line,i.ch),u=h(i,a[2]);return u&&n(u.from,s)>0?{from:s,to:u.from}:null}}})),e.findMatchingTag=function(e,r,i){var a=new o(e,r.line,r.ch,i);if(-1!=a.text.indexOf(">")||-1!=a.text.indexOf("<")){var l=c(a),s=l&&t(a.line,a.ch),f=l&&u(a);if(l&&f&&!(n(a,r)>0)){var d={from:t(a.line,a.ch),to:s,tag:f[2]};return"selfClose"==l?{open:d,close:null,at:"open"}:f[1]?{open:p(a,f[2]),close:d,at:"close"}:{open:d,close:h(a=new o(e,s.line,s.ch,i),f[2]),at:"open"}}}},e.findEnclosingTag=function(e,t,n,r){for(var i=new o(e,t.line,t.ch,n);;){var a=p(i,r);if(!a)break;var l=h(new o(e,t.line,t.ch,n),a.tag);if(l)return{open:a,close:l}}},e.scanForClosingTag=function(e,t,n,r){return h(new o(e,t.line,t.ch,r?{from:0,to:r}:null),n)}}(n(0))},function(e,t,n){!function(e){"use strict";e.defineOption("foldGutter",!1,(function(t,r,i){var o;i&&i!=e.Init&&(t.clearGutter(t.state.foldGutter.options.gutter),t.state.foldGutter=null,t.off("gutterClick",l),t.off("changes",s),t.off("viewportChange",c),t.off("fold",u),t.off("unfold",u),t.off("swapDoc",s)),r&&(t.state.foldGutter=new n((!0===(o=r)&&(o={}),null==o.gutter&&(o.gutter="CodeMirror-foldgutter"),null==o.indicatorOpen&&(o.indicatorOpen="CodeMirror-foldgutter-open"),null==o.indicatorFolded&&(o.indicatorFolded="CodeMirror-foldgutter-folded"),o)),a(t),t.on("gutterClick",l),t.on("changes",s),t.on("viewportChange",c),t.on("fold",u),t.on("unfold",u),t.on("swapDoc",s))}));var t=e.Pos;function n(e){this.options=e,this.from=this.to=0}function r(e,n){for(var r=e.findMarks(t(n,0),t(n+1,0)),i=0;i<r.length;++i)if(r[i].__isFold){var o=r[i].find(-1);if(o&&o.line===n)return r[i]}}function i(e){if("string"==typeof e){var t=document.createElement("div");return t.className=e+" CodeMirror-guttermarker-subtle",t}return e.cloneNode(!0)}function o(e,n,o){var a=e.state.foldGutter.options,l=n,s=e.foldOption(a,"minFoldSize"),c=e.foldOption(a,"rangeFinder");e.eachLine(n,o,(function(n){var o=null;if(r(e,l))o=i(a.indicatorFolded);else{var u=t(l,0),f=c&&c(e,u);f&&f.to.line-f.from.line>=s&&(o=i(a.indicatorOpen))}e.setGutterMarker(n,a.gutter,o),++l}))}function a(e){var t=e.getViewport(),n=e.state.foldGutter;n&&(e.operation((function(){o(e,t.from,t.to)})),n.from=t.from,n.to=t.to)}function l(e,n,i){var o=e.state.foldGutter;if(o){var a=o.options;if(i==a.gutter){var l=r(e,n);l?l.clear():e.foldCode(t(n,0),a)}}}function s(e){var t=e.state.foldGutter;if(t){var n=t.options;t.from=t.to=0,clearTimeout(t.changeUpdate),t.changeUpdate=setTimeout((function(){a(e)}),n.foldOnChangeTimeSpan||600)}}function c(e){var t=e.state.foldGutter;if(t){var n=t.options;clearTimeout(t.changeUpdate),t.changeUpdate=setTimeout((function(){var n=e.getViewport();t.from==t.to||n.from-t.to>20||t.from-n.to>20?a(e):e.operation((function(){n.from<t.from&&(o(e,n.from,t.from),t.from=n.from),n.to>t.to&&(o(e,t.to,n.to),t.to=n.to)}))}),n.updateViewportTimeSpan||400)}}function u(e,t){var n=e.state.foldGutter;if(n){var r=t.line;r>=n.from&&r<n.to&&o(e,r,r+1)}}}(n(0),n(6))},function(e,t,n){!function(e){"use strict";e.runMode=function(t,n,r,i){var o=e.getMode(e.defaults,n),a=/MSIE \d/.test(navigator.userAgent)&&(null==document.documentMode||document.documentMode<9);if(r.appendChild){var l=i&&i.tabSize||e.defaults.tabSize,s=r,c=0;s.innerHTML="",r=function(e,t){if("\n"==e)return s.appendChild(document.createTextNode(a?"\r":e)),void(c=0);for(var n="",r=0;;){var i=e.indexOf("\t",r);if(-1==i){n+=e.slice(r),c+=e.length-r;break}c+=i-r,n+=e.slice(r,i);var o=l-c%l;c+=o;for(var u=0;u<o;++u)n+=" ";r=i+1}if(t){var f=s.appendChild(document.createElement("span"));f.className="cm-"+t.replace(/ +/g," cm-"),f.appendChild(document.createTextNode(n))}else s.appendChild(document.createTextNode(n))}}for(var u=e.splitLines(t),f=i&&i.state||e.startState(o),d=0,h=u.length;d<h;++d){d&&r("\n");var p=new e.StringStream(u[d]);for(!p.string&&o.blankLine&&o.blankLine(f);!p.eol();){var m=o.token(p,f);r(p.current(),m,d,p.start,f),p.start=p.pos}}}}(n(0))},function(e,t,n){!function(e){function t(e){e.state.placeholder&&(e.state.placeholder.parentNode.removeChild(e.state.placeholder),e.state.placeholder=null)}function n(e){t(e);var n=e.state.placeholder=document.createElement("pre");n.style.cssText="height: 0; overflow: visible",n.style.direction=e.getOption("direction"),n.className="CodeMirror-placeholder CodeMirror-line-like";var r=e.getOption("placeholder");"string"==typeof r&&(r=document.createTextNode(r)),n.appendChild(r),e.display.lineSpace.insertBefore(n,e.display.lineSpace.firstChild)}function r(e){o(e)&&n(e)}function i(e){var r=e.getWrapperElement(),i=o(e);r.className=r.className.replace(" CodeMirror-empty","")+(i?" CodeMirror-empty":""),i?n(e):t(e)}function o(e){return 1===e.lineCount()&&""===e.getLine(0)}e.defineOption("placeholder","",(function(n,o,a){var l=a&&a!=e.Init;if(o&&!l)n.on("blur",r),n.on("change",i),n.on("swapDoc",i),i(n);else if(!o&&l){n.off("blur",r),n.off("change",i),n.off("swapDoc",i),t(n);var s=n.getWrapperElement();s.className=s.className.replace(" CodeMirror-empty","")}o&&!n.hasFocus()&&r(n)}))}(n(0))}]); \ No newline at end of file
diff --git a/devtools/client/shared/sourceeditor/codemirror/keymap/emacs.js b/devtools/client/shared/sourceeditor/codemirror/keymap/emacs.js
new file mode 100644
index 0000000000..dd1db727e0
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/keymap/emacs.js
@@ -0,0 +1,418 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var Pos = CodeMirror.Pos;
+ function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }
+
+ // Kill 'ring'
+
+ var killRing = [];
+ function addToRing(str) {
+ killRing.push(str);
+ if (killRing.length > 50) killRing.shift();
+ }
+ function growRingTop(str) {
+ if (!killRing.length) return addToRing(str);
+ killRing[killRing.length - 1] += str;
+ }
+ function getFromRing(n) { return killRing[killRing.length - (n ? Math.min(n, 1) : 1)] || ""; }
+ function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); }
+
+ var lastKill = null;
+
+ function kill(cm, from, to, ring, text) {
+ if (text == null) text = cm.getRange(from, to);
+
+ if (ring == "grow" && lastKill && lastKill.cm == cm && posEq(from, lastKill.pos) && cm.isClean(lastKill.gen))
+ growRingTop(text);
+ else if (ring !== false)
+ addToRing(text);
+ cm.replaceRange("", from, to, "+delete");
+
+ if (ring == "grow") lastKill = {cm: cm, pos: from, gen: cm.changeGeneration()};
+ else lastKill = null;
+ }
+
+ // Boundaries of various units
+
+ function byChar(cm, pos, dir) {
+ return cm.findPosH(pos, dir, "char", true);
+ }
+
+ function byWord(cm, pos, dir) {
+ return cm.findPosH(pos, dir, "word", true);
+ }
+
+ function byLine(cm, pos, dir) {
+ return cm.findPosV(pos, dir, "line", cm.doc.sel.goalColumn);
+ }
+
+ function byPage(cm, pos, dir) {
+ return cm.findPosV(pos, dir, "page", cm.doc.sel.goalColumn);
+ }
+
+ function byParagraph(cm, pos, dir) {
+ var no = pos.line, line = cm.getLine(no);
+ var sawText = /\S/.test(dir < 0 ? line.slice(0, pos.ch) : line.slice(pos.ch));
+ var fst = cm.firstLine(), lst = cm.lastLine();
+ for (;;) {
+ no += dir;
+ if (no < fst || no > lst)
+ return cm.clipPos(Pos(no - dir, dir < 0 ? 0 : null));
+ line = cm.getLine(no);
+ var hasText = /\S/.test(line);
+ if (hasText) sawText = true;
+ else if (sawText) return Pos(no, 0);
+ }
+ }
+
+ function bySentence(cm, pos, dir) {
+ var line = pos.line, ch = pos.ch;
+ var text = cm.getLine(pos.line), sawWord = false;
+ for (;;) {
+ var next = text.charAt(ch + (dir < 0 ? -1 : 0));
+ if (!next) { // End/beginning of line reached
+ if (line == (dir < 0 ? cm.firstLine() : cm.lastLine())) return Pos(line, ch);
+ text = cm.getLine(line + dir);
+ if (!/\S/.test(text)) return Pos(line, ch);
+ line += dir;
+ ch = dir < 0 ? text.length : 0;
+ continue;
+ }
+ if (sawWord && /[!?.]/.test(next)) return Pos(line, ch + (dir > 0 ? 1 : 0));
+ if (!sawWord) sawWord = /\w/.test(next);
+ ch += dir;
+ }
+ }
+
+ function byExpr(cm, pos, dir) {
+ var wrap;
+ if (cm.findMatchingBracket && (wrap = cm.findMatchingBracket(pos, {strict: true}))
+ && wrap.match && (wrap.forward ? 1 : -1) == dir)
+ return dir > 0 ? Pos(wrap.to.line, wrap.to.ch + 1) : wrap.to;
+
+ for (var first = true;; first = false) {
+ var token = cm.getTokenAt(pos);
+ var after = Pos(pos.line, dir < 0 ? token.start : token.end);
+ if (first && dir > 0 && token.end == pos.ch || !/\w/.test(token.string)) {
+ var newPos = cm.findPosH(after, dir, "char");
+ if (posEq(after, newPos)) return pos;
+ else pos = newPos;
+ } else {
+ return after;
+ }
+ }
+ }
+
+ // Prefixes (only crudely supported)
+
+ function getPrefix(cm, precise) {
+ var digits = cm.state.emacsPrefix;
+ if (!digits) return precise ? null : 1;
+ clearPrefix(cm);
+ return digits == "-" ? -1 : Number(digits);
+ }
+
+ function repeated(cmd) {
+ var f = typeof cmd == "string" ? function(cm) { cm.execCommand(cmd); } : cmd;
+ return function(cm) {
+ var prefix = getPrefix(cm);
+ f(cm);
+ for (var i = 1; i < prefix; ++i) f(cm);
+ };
+ }
+
+ function findEnd(cm, pos, by, dir) {
+ var prefix = getPrefix(cm);
+ if (prefix < 0) { dir = -dir; prefix = -prefix; }
+ for (var i = 0; i < prefix; ++i) {
+ var newPos = by(cm, pos, dir);
+ if (posEq(newPos, pos)) break;
+ pos = newPos;
+ }
+ return pos;
+ }
+
+ function move(by, dir) {
+ var f = function(cm) {
+ cm.extendSelection(findEnd(cm, cm.getCursor(), by, dir));
+ };
+ f.motion = true;
+ return f;
+ }
+
+ function killTo(cm, by, dir, ring) {
+ var selections = cm.listSelections(), cursor;
+ var i = selections.length;
+ while (i--) {
+ cursor = selections[i].head;
+ kill(cm, cursor, findEnd(cm, cursor, by, dir), ring);
+ }
+ }
+
+ function killRegion(cm, ring) {
+ if (cm.somethingSelected()) {
+ var selections = cm.listSelections(), selection;
+ var i = selections.length;
+ while (i--) {
+ selection = selections[i];
+ kill(cm, selection.anchor, selection.head, ring);
+ }
+ return true;
+ }
+ }
+
+ function addPrefix(cm, digit) {
+ if (cm.state.emacsPrefix) {
+ if (digit != "-") cm.state.emacsPrefix += digit;
+ return;
+ }
+ // Not active yet
+ cm.state.emacsPrefix = digit;
+ cm.on("keyHandled", maybeClearPrefix);
+ cm.on("inputRead", maybeDuplicateInput);
+ }
+
+ var prefixPreservingKeys = {"Alt-G": true, "Ctrl-X": true, "Ctrl-Q": true, "Ctrl-U": true};
+
+ function maybeClearPrefix(cm, arg) {
+ if (!cm.state.emacsPrefixMap && !prefixPreservingKeys.hasOwnProperty(arg))
+ clearPrefix(cm);
+ }
+
+ function clearPrefix(cm) {
+ cm.state.emacsPrefix = null;
+ cm.off("keyHandled", maybeClearPrefix);
+ cm.off("inputRead", maybeDuplicateInput);
+ }
+
+ function maybeDuplicateInput(cm, event) {
+ var dup = getPrefix(cm);
+ if (dup > 1 && event.origin == "+input") {
+ var one = event.text.join("\n"), txt = "";
+ for (var i = 1; i < dup; ++i) txt += one;
+ cm.replaceSelection(txt);
+ }
+ }
+
+ function addPrefixMap(cm) {
+ cm.state.emacsPrefixMap = true;
+ cm.addKeyMap(prefixMap);
+ cm.on("keyHandled", maybeRemovePrefixMap);
+ cm.on("inputRead", maybeRemovePrefixMap);
+ }
+
+ function maybeRemovePrefixMap(cm, arg) {
+ if (typeof arg == "string" && (/^\d$/.test(arg) || arg == "Ctrl-U")) return;
+ cm.removeKeyMap(prefixMap);
+ cm.state.emacsPrefixMap = false;
+ cm.off("keyHandled", maybeRemovePrefixMap);
+ cm.off("inputRead", maybeRemovePrefixMap);
+ }
+
+ // Utilities
+
+ function setMark(cm) {
+ cm.setCursor(cm.getCursor());
+ cm.setExtending(!cm.getExtending());
+ cm.on("change", function() { cm.setExtending(false); });
+ }
+
+ function clearMark(cm) {
+ cm.setExtending(false);
+ cm.setCursor(cm.getCursor());
+ }
+
+ function getInput(cm, msg, f) {
+ if (cm.openDialog)
+ cm.openDialog(msg + ": <input type=\"text\" style=\"width: 10em\"/>", f, {bottom: true});
+ else
+ f(prompt(msg, ""));
+ }
+
+ function operateOnWord(cm, op) {
+ var start = cm.getCursor(), end = cm.findPosH(start, 1, "word");
+ cm.replaceRange(op(cm.getRange(start, end)), start, end);
+ cm.setCursor(end);
+ }
+
+ function toEnclosingExpr(cm) {
+ var pos = cm.getCursor(), line = pos.line, ch = pos.ch;
+ var stack = [];
+ while (line >= cm.firstLine()) {
+ var text = cm.getLine(line);
+ for (var i = ch == null ? text.length : ch; i > 0;) {
+ var ch = text.charAt(--i);
+ if (ch == ")")
+ stack.push("(");
+ else if (ch == "]")
+ stack.push("[");
+ else if (ch == "}")
+ stack.push("{");
+ else if (/[\(\{\[]/.test(ch) && (!stack.length || stack.pop() != ch))
+ return cm.extendSelection(Pos(line, i));
+ }
+ --line; ch = null;
+ }
+ }
+
+ function quit(cm) {
+ cm.execCommand("clearSearch");
+ clearMark(cm);
+ }
+
+ CodeMirror.emacs = {kill: kill, killRegion: killRegion, repeated: repeated};
+
+ // Actual keymap
+
+ var keyMap = CodeMirror.keyMap.emacs = CodeMirror.normalizeKeyMap({
+ "Ctrl-W": function(cm) {kill(cm, cm.getCursor("start"), cm.getCursor("end"), true);},
+ "Ctrl-K": repeated(function(cm) {
+ var start = cm.getCursor(), end = cm.clipPos(Pos(start.line));
+ var text = cm.getRange(start, end);
+ if (!/\S/.test(text)) {
+ text += "\n";
+ end = Pos(start.line + 1, 0);
+ }
+ kill(cm, start, end, "grow", text);
+ }),
+ "Alt-W": function(cm) {
+ addToRing(cm.getSelection());
+ clearMark(cm);
+ },
+ "Ctrl-Y": function(cm) {
+ var start = cm.getCursor();
+ cm.replaceRange(getFromRing(getPrefix(cm)), start, start, "paste");
+ cm.setSelection(start, cm.getCursor());
+ },
+ "Alt-Y": function(cm) {cm.replaceSelection(popFromRing(), "around", "paste");},
+
+ "Ctrl-Space": setMark, "Ctrl-Shift-2": setMark,
+
+ "Ctrl-F": move(byChar, 1), "Ctrl-B": move(byChar, -1),
+ "Right": move(byChar, 1), "Left": move(byChar, -1),
+ "Ctrl-D": function(cm) { killTo(cm, byChar, 1, false); },
+ "Delete": function(cm) { killRegion(cm, false) || killTo(cm, byChar, 1, false); },
+ "Ctrl-H": function(cm) { killTo(cm, byChar, -1, false); },
+ "Backspace": function(cm) { killRegion(cm, false) || killTo(cm, byChar, -1, false); },
+
+ "Alt-F": move(byWord, 1), "Alt-B": move(byWord, -1),
+ "Alt-Right": move(byWord, 1), "Alt-Left": move(byWord, -1),
+ "Alt-D": function(cm) { killTo(cm, byWord, 1, "grow"); },
+ "Alt-Backspace": function(cm) { killTo(cm, byWord, -1, "grow"); },
+
+ "Ctrl-N": move(byLine, 1), "Ctrl-P": move(byLine, -1),
+ "Down": move(byLine, 1), "Up": move(byLine, -1),
+ "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+ "End": "goLineEnd", "Home": "goLineStart",
+
+ "Alt-V": move(byPage, -1), "Ctrl-V": move(byPage, 1),
+ "PageUp": move(byPage, -1), "PageDown": move(byPage, 1),
+
+ "Ctrl-Up": move(byParagraph, -1), "Ctrl-Down": move(byParagraph, 1),
+
+ "Alt-A": move(bySentence, -1), "Alt-E": move(bySentence, 1),
+ "Alt-K": function(cm) { killTo(cm, bySentence, 1, "grow"); },
+
+ "Ctrl-Alt-K": function(cm) { killTo(cm, byExpr, 1, "grow"); },
+ "Ctrl-Alt-Backspace": function(cm) { killTo(cm, byExpr, -1, "grow"); },
+ "Ctrl-Alt-F": move(byExpr, 1), "Ctrl-Alt-B": move(byExpr, -1, "grow"),
+
+ "Shift-Ctrl-Alt-2": function(cm) {
+ var cursor = cm.getCursor();
+ cm.setSelection(findEnd(cm, cursor, byExpr, 1), cursor);
+ },
+ "Ctrl-Alt-T": function(cm) {
+ var leftStart = byExpr(cm, cm.getCursor(), -1), leftEnd = byExpr(cm, leftStart, 1);
+ var rightEnd = byExpr(cm, leftEnd, 1), rightStart = byExpr(cm, rightEnd, -1);
+ cm.replaceRange(cm.getRange(rightStart, rightEnd) + cm.getRange(leftEnd, rightStart) +
+ cm.getRange(leftStart, leftEnd), leftStart, rightEnd);
+ },
+ "Ctrl-Alt-U": repeated(toEnclosingExpr),
+
+ "Alt-Space": function(cm) {
+ var pos = cm.getCursor(), from = pos.ch, to = pos.ch, text = cm.getLine(pos.line);
+ while (from && /\s/.test(text.charAt(from - 1))) --from;
+ while (to < text.length && /\s/.test(text.charAt(to))) ++to;
+ cm.replaceRange(" ", Pos(pos.line, from), Pos(pos.line, to));
+ },
+ "Ctrl-O": repeated(function(cm) { cm.replaceSelection("\n", "start"); }),
+ "Ctrl-T": repeated(function(cm) {
+ cm.execCommand("transposeChars");
+ }),
+
+ "Alt-C": repeated(function(cm) {
+ operateOnWord(cm, function(w) {
+ var letter = w.search(/\w/);
+ if (letter == -1) return w;
+ return w.slice(0, letter) + w.charAt(letter).toUpperCase() + w.slice(letter + 1).toLowerCase();
+ });
+ }),
+ "Alt-U": repeated(function(cm) {
+ operateOnWord(cm, function(w) { return w.toUpperCase(); });
+ }),
+ "Alt-L": repeated(function(cm) {
+ operateOnWord(cm, function(w) { return w.toLowerCase(); });
+ }),
+
+ "Alt-;": "toggleComment",
+
+ "Ctrl-/": repeated("undo"), "Shift-Ctrl--": repeated("undo"),
+ "Ctrl-Z": repeated("undo"), "Cmd-Z": repeated("undo"),
+ "Shift-Ctrl-Z": "redo",
+ "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd",
+ "Ctrl-S": "findPersistentNext", "Ctrl-R": "findPersistentPrev", "Ctrl-G": quit, "Shift-Alt-5": "replace",
+ "Alt-/": "autocomplete",
+ "Enter": "newlineAndIndent",
+ "Ctrl-J": repeated(function(cm) { cm.replaceSelection("\n", "end"); }),
+ "Tab": "indentAuto",
+
+ "Alt-G G": function(cm) {
+ var prefix = getPrefix(cm, true);
+ if (prefix != null && prefix > 0) return cm.setCursor(prefix - 1);
+
+ getInput(cm, "Goto line", function(str) {
+ var num;
+ if (str && !isNaN(num = Number(str)) && num == (num|0) && num > 0)
+ cm.setCursor(num - 1);
+ });
+ },
+
+ "Ctrl-X Tab": function(cm) {
+ cm.indentSelection(getPrefix(cm, true) || cm.getOption("indentUnit"));
+ },
+ "Ctrl-X Ctrl-X": function(cm) {
+ cm.setSelection(cm.getCursor("head"), cm.getCursor("anchor"));
+ },
+ "Ctrl-X Ctrl-S": "save",
+ "Ctrl-X Ctrl-W": "save",
+ "Ctrl-X S": "saveAll",
+ "Ctrl-X F": "open",
+ "Ctrl-X U": repeated("undo"),
+ "Ctrl-X K": "close",
+ "Ctrl-X Delete": function(cm) { kill(cm, cm.getCursor(), bySentence(cm, cm.getCursor(), 1), "grow"); },
+ "Ctrl-X H": "selectAll",
+
+ "Ctrl-Q Tab": repeated("insertTab"),
+ "Ctrl-U": addPrefixMap
+ });
+
+ var prefixMap = {"Ctrl-G": clearPrefix};
+ function regPrefix(d) {
+ prefixMap[d] = function(cm) { addPrefix(cm, d); };
+ keyMap["Ctrl-" + d] = function(cm) { addPrefix(cm, d); };
+ prefixPreservingKeys["Ctrl-" + d] = true;
+ }
+ for (var i = 0; i < 10; ++i) regPrefix(String(i));
+ regPrefix("-");
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/keymap/sublime.js b/devtools/client/shared/sourceeditor/codemirror/keymap/sublime.js
new file mode 100644
index 0000000000..6bb92edbd9
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/keymap/sublime.js
@@ -0,0 +1,691 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// A rough approximation of Sublime Text's keybindings
+// Depends on addon/search/searchcursor.js and optionally addon/dialog/dialogs.js
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/search/searchcursor.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/edit/matchbrackets.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/edit/matchbrackets"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var cmds = CodeMirror.commands;
+ var Pos = CodeMirror.Pos;
+
+ // This is not exactly Sublime's algorithm. I couldn't make heads or tails of that.
+ function findPosSubword(doc, start, dir) {
+ if (dir < 0 && start.ch == 0) return doc.clipPos(Pos(start.line - 1));
+ var line = doc.getLine(start.line);
+ if (dir > 0 && start.ch >= line.length) return doc.clipPos(Pos(start.line + 1, 0));
+ var state = "start", type;
+ for (var pos = start.ch, e = dir < 0 ? 0 : line.length, i = 0; pos != e; pos += dir, i++) {
+ var next = line.charAt(dir < 0 ? pos - 1 : pos);
+ var cat = next != "_" && CodeMirror.isWordChar(next) ? "w" : "o";
+ if (cat == "w" && next.toUpperCase() == next) cat = "W";
+ if (state == "start") {
+ if (cat != "o") { state = "in"; type = cat; }
+ } else if (state == "in") {
+ if (type != cat) {
+ if (type == "w" && cat == "W" && dir < 0) pos--;
+ if (type == "W" && cat == "w" && dir > 0) { type = "w"; continue; }
+ break;
+ }
+ }
+ }
+ return Pos(start.line, pos);
+ }
+
+ function moveSubword(cm, dir) {
+ cm.extendSelectionsBy(function(range) {
+ if (cm.display.shift || cm.doc.extend || range.empty())
+ return findPosSubword(cm.doc, range.head, dir);
+ else
+ return dir < 0 ? range.from() : range.to();
+ });
+ }
+
+ cmds.goSubwordLeft = function(cm) { moveSubword(cm, -1); };
+ cmds.goSubwordRight = function(cm) { moveSubword(cm, 1); };
+
+ cmds.scrollLineUp = function(cm) {
+ var info = cm.getScrollInfo();
+ if (!cm.somethingSelected()) {
+ var visibleBottomLine = cm.lineAtHeight(info.top + info.clientHeight, "local");
+ if (cm.getCursor().line >= visibleBottomLine)
+ cm.execCommand("goLineUp");
+ }
+ cm.scrollTo(null, info.top - cm.defaultTextHeight());
+ };
+ cmds.scrollLineDown = function(cm) {
+ var info = cm.getScrollInfo();
+ if (!cm.somethingSelected()) {
+ var visibleTopLine = cm.lineAtHeight(info.top, "local")+1;
+ if (cm.getCursor().line <= visibleTopLine)
+ cm.execCommand("goLineDown");
+ }
+ cm.scrollTo(null, info.top + cm.defaultTextHeight());
+ };
+
+ cmds.splitSelectionByLine = function(cm) {
+ var ranges = cm.listSelections(), lineRanges = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var from = ranges[i].from(), to = ranges[i].to();
+ for (var line = from.line; line <= to.line; ++line)
+ if (!(to.line > from.line && line == to.line && to.ch == 0))
+ lineRanges.push({anchor: line == from.line ? from : Pos(line, 0),
+ head: line == to.line ? to : Pos(line)});
+ }
+ cm.setSelections(lineRanges, 0);
+ };
+
+ cmds.singleSelectionTop = function(cm) {
+ var range = cm.listSelections()[0];
+ cm.setSelection(range.anchor, range.head, {scroll: false});
+ };
+
+ cmds.selectLine = function(cm) {
+ var ranges = cm.listSelections(), extended = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ extended.push({anchor: Pos(range.from().line, 0),
+ head: Pos(range.to().line + 1, 0)});
+ }
+ cm.setSelections(extended);
+ };
+
+ function insertLine(cm, above) {
+ if (cm.isReadOnly()) return CodeMirror.Pass
+ cm.operation(function() {
+ var len = cm.listSelections().length, newSelection = [], last = -1;
+ for (var i = 0; i < len; i++) {
+ var head = cm.listSelections()[i].head;
+ if (head.line <= last) continue;
+ var at = Pos(head.line + (above ? 0 : 1), 0);
+ cm.replaceRange("\n", at, null, "+insertLine");
+ cm.indentLine(at.line, null, true);
+ newSelection.push({head: at, anchor: at});
+ last = head.line + 1;
+ }
+ cm.setSelections(newSelection);
+ });
+ cm.execCommand("indentAuto");
+ }
+
+ cmds.insertLineAfter = function(cm) { return insertLine(cm, false); };
+
+ cmds.insertLineBefore = function(cm) { return insertLine(cm, true); };
+
+ function wordAt(cm, pos) {
+ var start = pos.ch, end = start, line = cm.getLine(pos.line);
+ while (start && CodeMirror.isWordChar(line.charAt(start - 1))) --start;
+ while (end < line.length && CodeMirror.isWordChar(line.charAt(end))) ++end;
+ return {from: Pos(pos.line, start), to: Pos(pos.line, end), word: line.slice(start, end)};
+ }
+
+ cmds.selectNextOccurrence = function(cm) {
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
+ var fullWord = cm.state.sublimeFindFullWord == cm.doc.sel;
+ if (CodeMirror.cmpPos(from, to) == 0) {
+ var word = wordAt(cm, from);
+ if (!word.word) return;
+ cm.setSelection(word.from, word.to);
+ fullWord = true;
+ } else {
+ var text = cm.getRange(from, to);
+ var query = fullWord ? new RegExp("\\b" + text + "\\b") : text;
+ var cur = cm.getSearchCursor(query, to);
+ var found = cur.findNext();
+ if (!found) {
+ cur = cm.getSearchCursor(query, Pos(cm.firstLine(), 0));
+ found = cur.findNext();
+ }
+ if (!found || isSelectedRange(cm.listSelections(), cur.from(), cur.to()))
+ return CodeMirror.Pass
+ cm.addSelection(cur.from(), cur.to());
+ }
+ if (fullWord)
+ cm.state.sublimeFindFullWord = cm.doc.sel;
+ };
+
+ function addCursorToSelection(cm, dir) {
+ var ranges = cm.listSelections(), newRanges = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ var newAnchor = cm.findPosV(
+ range.anchor, dir, "line", range.anchor.goalColumn);
+ var newHead = cm.findPosV(
+ range.head, dir, "line", range.head.goalColumn);
+ newAnchor.goalColumn = range.anchor.goalColumn != null ?
+ range.anchor.goalColumn : cm.cursorCoords(range.anchor, "div").left;
+ newHead.goalColumn = range.head.goalColumn != null ?
+ range.head.goalColumn : cm.cursorCoords(range.head, "div").left;
+ var newRange = {anchor: newAnchor, head: newHead};
+ newRanges.push(range);
+ newRanges.push(newRange);
+ }
+ cm.setSelections(newRanges);
+ }
+ cmds.addCursorToPrevLine = function(cm) { addCursorToSelection(cm, -1); };
+ cmds.addCursorToNextLine = function(cm) { addCursorToSelection(cm, 1); };
+
+ function isSelectedRange(ranges, from, to) {
+ for (var i = 0; i < ranges.length; i++)
+ if (ranges[i].from() == from && ranges[i].to() == to) return true
+ return false
+ }
+
+ var mirror = "(){}[]";
+ function selectBetweenBrackets(cm) {
+ var ranges = cm.listSelections(), newRanges = []
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], pos = range.head, opening = cm.scanForBracket(pos, -1);
+ if (!opening) return false;
+ for (;;) {
+ var closing = cm.scanForBracket(pos, 1);
+ if (!closing) return false;
+ if (closing.ch == mirror.charAt(mirror.indexOf(opening.ch) + 1)) {
+ var startPos = Pos(opening.pos.line, opening.pos.ch + 1);
+ if (CodeMirror.cmpPos(startPos, range.from()) == 0 &&
+ CodeMirror.cmpPos(closing.pos, range.to()) == 0) {
+ opening = cm.scanForBracket(opening.pos, -1);
+ if (!opening) return false;
+ } else {
+ newRanges.push({anchor: startPos, head: closing.pos});
+ break;
+ }
+ }
+ pos = Pos(closing.pos.line, closing.pos.ch + 1);
+ }
+ }
+ cm.setSelections(newRanges);
+ return true;
+ }
+
+ cmds.selectScope = function(cm) {
+ selectBetweenBrackets(cm) || cm.execCommand("selectAll");
+ };
+ cmds.selectBetweenBrackets = function(cm) {
+ if (!selectBetweenBrackets(cm)) return CodeMirror.Pass;
+ };
+
+ cmds.goToBracket = function(cm) {
+ cm.extendSelectionsBy(function(range) {
+ var next = cm.scanForBracket(range.head, 1);
+ if (next && CodeMirror.cmpPos(next.pos, range.head) != 0) return next.pos;
+ var prev = cm.scanForBracket(range.head, -1);
+ return prev && Pos(prev.pos.line, prev.pos.ch + 1) || range.head;
+ });
+ };
+
+ cmds.swapLineUp = function(cm) {
+ if (cm.isReadOnly()) return CodeMirror.Pass
+ var ranges = cm.listSelections(), linesToMove = [], at = cm.firstLine() - 1, newSels = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], from = range.from().line - 1, to = range.to().line;
+ newSels.push({anchor: Pos(range.anchor.line - 1, range.anchor.ch),
+ head: Pos(range.head.line - 1, range.head.ch)});
+ if (range.to().ch == 0 && !range.empty()) --to;
+ if (from > at) linesToMove.push(from, to);
+ else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
+ at = to;
+ }
+ cm.operation(function() {
+ for (var i = 0; i < linesToMove.length; i += 2) {
+ var from = linesToMove[i], to = linesToMove[i + 1];
+ var line = cm.getLine(from);
+ cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
+ if (to > cm.lastLine())
+ cm.replaceRange("\n" + line, Pos(cm.lastLine()), null, "+swapLine");
+ else
+ cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
+ }
+ cm.setSelections(newSels);
+ cm.scrollIntoView();
+ });
+ };
+
+ cmds.swapLineDown = function(cm) {
+ if (cm.isReadOnly()) return CodeMirror.Pass
+ var ranges = cm.listSelections(), linesToMove = [], at = cm.lastLine() + 1;
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var range = ranges[i], from = range.to().line + 1, to = range.from().line;
+ if (range.to().ch == 0 && !range.empty()) from--;
+ if (from < at) linesToMove.push(from, to);
+ else if (linesToMove.length) linesToMove[linesToMove.length - 1] = to;
+ at = to;
+ }
+ cm.operation(function() {
+ for (var i = linesToMove.length - 2; i >= 0; i -= 2) {
+ var from = linesToMove[i], to = linesToMove[i + 1];
+ var line = cm.getLine(from);
+ if (from == cm.lastLine())
+ cm.replaceRange("", Pos(from - 1), Pos(from), "+swapLine");
+ else
+ cm.replaceRange("", Pos(from, 0), Pos(from + 1, 0), "+swapLine");
+ cm.replaceRange(line + "\n", Pos(to, 0), null, "+swapLine");
+ }
+ cm.scrollIntoView();
+ });
+ };
+
+ cmds.toggleCommentIndented = function(cm) {
+ cm.toggleComment({ indent: true });
+ }
+
+ cmds.joinLines = function(cm) {
+ var ranges = cm.listSelections(), joined = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], from = range.from();
+ var start = from.line, end = range.to().line;
+ while (i < ranges.length - 1 && ranges[i + 1].from().line == end)
+ end = ranges[++i].to().line;
+ joined.push({start: start, end: end, anchor: !range.empty() && from});
+ }
+ cm.operation(function() {
+ var offset = 0, ranges = [];
+ for (var i = 0; i < joined.length; i++) {
+ var obj = joined[i];
+ var anchor = obj.anchor && Pos(obj.anchor.line - offset, obj.anchor.ch), head;
+ for (var line = obj.start; line <= obj.end; line++) {
+ var actual = line - offset;
+ if (line == obj.end) head = Pos(actual, cm.getLine(actual).length + 1);
+ if (actual < cm.lastLine()) {
+ cm.replaceRange(" ", Pos(actual), Pos(actual + 1, /^\s*/.exec(cm.getLine(actual + 1))[0].length));
+ ++offset;
+ }
+ }
+ ranges.push({anchor: anchor || head, head: head});
+ }
+ cm.setSelections(ranges, 0);
+ });
+ };
+
+ cmds.duplicateLine = function(cm) {
+ cm.operation(function() {
+ var rangeCount = cm.listSelections().length;
+ for (var i = 0; i < rangeCount; i++) {
+ var range = cm.listSelections()[i];
+ if (range.empty())
+ cm.replaceRange(cm.getLine(range.head.line) + "\n", Pos(range.head.line, 0));
+ else
+ cm.replaceRange(cm.getRange(range.from(), range.to()), range.from());
+ }
+ cm.scrollIntoView();
+ });
+ };
+
+
+ function sortLines(cm, caseSensitive) {
+ if (cm.isReadOnly()) return CodeMirror.Pass
+ var ranges = cm.listSelections(), toSort = [], selected;
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ if (range.empty()) continue;
+ var from = range.from().line, to = range.to().line;
+ while (i < ranges.length - 1 && ranges[i + 1].from().line == to)
+ to = ranges[++i].to().line;
+ if (!ranges[i].to().ch) to--;
+ toSort.push(from, to);
+ }
+ if (toSort.length) selected = true;
+ else toSort.push(cm.firstLine(), cm.lastLine());
+
+ cm.operation(function() {
+ var ranges = [];
+ for (var i = 0; i < toSort.length; i += 2) {
+ var from = toSort[i], to = toSort[i + 1];
+ var start = Pos(from, 0), end = Pos(to);
+ var lines = cm.getRange(start, end, false);
+ if (caseSensitive)
+ lines.sort();
+ else
+ lines.sort(function(a, b) {
+ var au = a.toUpperCase(), bu = b.toUpperCase();
+ if (au != bu) { a = au; b = bu; }
+ return a < b ? -1 : a == b ? 0 : 1;
+ });
+ cm.replaceRange(lines, start, end);
+ if (selected) ranges.push({anchor: start, head: Pos(to + 1, 0)});
+ }
+ if (selected) cm.setSelections(ranges, 0);
+ });
+ }
+
+ cmds.sortLines = function(cm) { sortLines(cm, true); };
+ cmds.sortLinesInsensitive = function(cm) { sortLines(cm, false); };
+
+ cmds.nextBookmark = function(cm) {
+ var marks = cm.state.sublimeBookmarks;
+ if (marks) while (marks.length) {
+ var current = marks.shift();
+ var found = current.find();
+ if (found) {
+ marks.push(current);
+ return cm.setSelection(found.from, found.to);
+ }
+ }
+ };
+
+ cmds.prevBookmark = function(cm) {
+ var marks = cm.state.sublimeBookmarks;
+ if (marks) while (marks.length) {
+ marks.unshift(marks.pop());
+ var found = marks[marks.length - 1].find();
+ if (!found)
+ marks.pop();
+ else
+ return cm.setSelection(found.from, found.to);
+ }
+ };
+
+ cmds.toggleBookmark = function(cm) {
+ var ranges = cm.listSelections();
+ var marks = cm.state.sublimeBookmarks || (cm.state.sublimeBookmarks = []);
+ for (var i = 0; i < ranges.length; i++) {
+ var from = ranges[i].from(), to = ranges[i].to();
+ var found = ranges[i].empty() ? cm.findMarksAt(from) : cm.findMarks(from, to);
+ for (var j = 0; j < found.length; j++) {
+ if (found[j].sublimeBookmark) {
+ found[j].clear();
+ for (var k = 0; k < marks.length; k++)
+ if (marks[k] == found[j])
+ marks.splice(k--, 1);
+ break;
+ }
+ }
+ if (j == found.length)
+ marks.push(cm.markText(from, to, {sublimeBookmark: true, clearWhenEmpty: false}));
+ }
+ };
+
+ cmds.clearBookmarks = function(cm) {
+ var marks = cm.state.sublimeBookmarks;
+ if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
+ marks.length = 0;
+ };
+
+ cmds.selectBookmarks = function(cm) {
+ var marks = cm.state.sublimeBookmarks, ranges = [];
+ if (marks) for (var i = 0; i < marks.length; i++) {
+ var found = marks[i].find();
+ if (!found)
+ marks.splice(i--, 0);
+ else
+ ranges.push({anchor: found.from, head: found.to});
+ }
+ if (ranges.length)
+ cm.setSelections(ranges, 0);
+ };
+
+ function modifyWordOrSelection(cm, mod) {
+ cm.operation(function() {
+ var ranges = cm.listSelections(), indices = [], replacements = [];
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ if (range.empty()) { indices.push(i); replacements.push(""); }
+ else replacements.push(mod(cm.getRange(range.from(), range.to())));
+ }
+ cm.replaceSelections(replacements, "around", "case");
+ for (var i = indices.length - 1, at; i >= 0; i--) {
+ var range = ranges[indices[i]];
+ if (at && CodeMirror.cmpPos(range.head, at) > 0) continue;
+ var word = wordAt(cm, range.head);
+ at = word.from;
+ cm.replaceRange(mod(word.word), word.from, word.to);
+ }
+ });
+ }
+
+ cmds.smartBackspace = function(cm) {
+ if (cm.somethingSelected()) return CodeMirror.Pass;
+
+ cm.operation(function() {
+ var cursors = cm.listSelections();
+ var indentUnit = cm.getOption("indentUnit");
+
+ for (var i = cursors.length - 1; i >= 0; i--) {
+ var cursor = cursors[i].head;
+ var toStartOfLine = cm.getRange({line: cursor.line, ch: 0}, cursor);
+ var column = CodeMirror.countColumn(toStartOfLine, null, cm.getOption("tabSize"));
+
+ // Delete by one character by default
+ var deletePos = cm.findPosH(cursor, -1, "char", false);
+
+ if (toStartOfLine && !/\S/.test(toStartOfLine) && column % indentUnit == 0) {
+ var prevIndent = new Pos(cursor.line,
+ CodeMirror.findColumn(toStartOfLine, column - indentUnit, indentUnit));
+
+ // Smart delete only if we found a valid prevIndent location
+ if (prevIndent.ch != cursor.ch) deletePos = prevIndent;
+ }
+
+ cm.replaceRange("", deletePos, cursor, "+delete");
+ }
+ });
+ };
+
+ cmds.delLineRight = function(cm) {
+ cm.operation(function() {
+ var ranges = cm.listSelections();
+ for (var i = ranges.length - 1; i >= 0; i--)
+ cm.replaceRange("", ranges[i].anchor, Pos(ranges[i].to().line), "+delete");
+ cm.scrollIntoView();
+ });
+ };
+
+ cmds.upcaseAtCursor = function(cm) {
+ modifyWordOrSelection(cm, function(str) { return str.toUpperCase(); });
+ };
+ cmds.downcaseAtCursor = function(cm) {
+ modifyWordOrSelection(cm, function(str) { return str.toLowerCase(); });
+ };
+
+ cmds.setSublimeMark = function(cm) {
+ if (cm.state.sublimeMark) cm.state.sublimeMark.clear();
+ cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
+ };
+ cmds.selectToSublimeMark = function(cm) {
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
+ if (found) cm.setSelection(cm.getCursor(), found);
+ };
+ cmds.deleteToSublimeMark = function(cm) {
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
+ if (found) {
+ var from = cm.getCursor(), to = found;
+ if (CodeMirror.cmpPos(from, to) > 0) { var tmp = to; to = from; from = tmp; }
+ cm.state.sublimeKilled = cm.getRange(from, to);
+ cm.replaceRange("", from, to);
+ }
+ };
+ cmds.swapWithSublimeMark = function(cm) {
+ var found = cm.state.sublimeMark && cm.state.sublimeMark.find();
+ if (found) {
+ cm.state.sublimeMark.clear();
+ cm.state.sublimeMark = cm.setBookmark(cm.getCursor());
+ cm.setCursor(found);
+ }
+ };
+ cmds.sublimeYank = function(cm) {
+ if (cm.state.sublimeKilled != null)
+ cm.replaceSelection(cm.state.sublimeKilled, null, "paste");
+ };
+
+ cmds.showInCenter = function(cm) {
+ var pos = cm.cursorCoords(null, "local");
+ cm.scrollTo(null, (pos.top + pos.bottom) / 2 - cm.getScrollInfo().clientHeight / 2);
+ };
+
+ function getTarget(cm) {
+ var from = cm.getCursor("from"), to = cm.getCursor("to");
+ if (CodeMirror.cmpPos(from, to) == 0) {
+ var word = wordAt(cm, from);
+ if (!word.word) return;
+ from = word.from;
+ to = word.to;
+ }
+ return {from: from, to: to, query: cm.getRange(from, to), word: word};
+ }
+
+ function findAndGoTo(cm, forward) {
+ var target = getTarget(cm);
+ if (!target) return;
+ var query = target.query;
+ var cur = cm.getSearchCursor(query, forward ? target.to : target.from);
+
+ if (forward ? cur.findNext() : cur.findPrevious()) {
+ cm.setSelection(cur.from(), cur.to());
+ } else {
+ cur = cm.getSearchCursor(query, forward ? Pos(cm.firstLine(), 0)
+ : cm.clipPos(Pos(cm.lastLine())));
+ if (forward ? cur.findNext() : cur.findPrevious())
+ cm.setSelection(cur.from(), cur.to());
+ else if (target.word)
+ cm.setSelection(target.from, target.to);
+ }
+ };
+ cmds.findUnder = function(cm) { findAndGoTo(cm, true); };
+ cmds.findUnderPrevious = function(cm) { findAndGoTo(cm,false); };
+ cmds.findAllUnder = function(cm) {
+ var target = getTarget(cm);
+ if (!target) return;
+ var cur = cm.getSearchCursor(target.query);
+ var matches = [];
+ var primaryIndex = -1;
+ while (cur.findNext()) {
+ matches.push({anchor: cur.from(), head: cur.to()});
+ if (cur.from().line <= target.from.line && cur.from().ch <= target.from.ch)
+ primaryIndex++;
+ }
+ cm.setSelections(matches, primaryIndex);
+ };
+
+
+ var keyMap = CodeMirror.keyMap;
+ keyMap.macSublime = {
+ "Cmd-Left": "goLineStartSmart",
+ "Shift-Tab": "indentLess",
+ "Shift-Ctrl-K": "deleteLine",
+ "Alt-Q": "wrapLines",
+ "Ctrl-Left": "goSubwordLeft",
+ "Ctrl-Right": "goSubwordRight",
+ "Ctrl-Alt-Up": "scrollLineUp",
+ "Ctrl-Alt-Down": "scrollLineDown",
+ "Cmd-L": "selectLine",
+ "Shift-Cmd-L": "splitSelectionByLine",
+ "Esc": "singleSelectionTop",
+ "Cmd-Enter": "insertLineAfter",
+ "Shift-Cmd-Enter": "insertLineBefore",
+ "Cmd-D": "selectNextOccurrence",
+ "Shift-Cmd-Space": "selectScope",
+ "Shift-Cmd-M": "selectBetweenBrackets",
+ "Cmd-M": "goToBracket",
+ "Cmd-Ctrl-Up": "swapLineUp",
+ "Cmd-Ctrl-Down": "swapLineDown",
+ "Cmd-/": "toggleCommentIndented",
+ "Cmd-J": "joinLines",
+ "Shift-Cmd-D": "duplicateLine",
+ "F5": "sortLines",
+ "Cmd-F5": "sortLinesInsensitive",
+ "F2": "nextBookmark",
+ "Shift-F2": "prevBookmark",
+ "Cmd-F2": "toggleBookmark",
+ "Shift-Cmd-F2": "clearBookmarks",
+ "Alt-F2": "selectBookmarks",
+ "Backspace": "smartBackspace",
+ "Cmd-K Cmd-K": "delLineRight",
+ "Cmd-K Cmd-U": "upcaseAtCursor",
+ "Cmd-K Cmd-L": "downcaseAtCursor",
+ "Cmd-K Cmd-Space": "setSublimeMark",
+ "Cmd-K Cmd-A": "selectToSublimeMark",
+ "Cmd-K Cmd-W": "deleteToSublimeMark",
+ "Cmd-K Cmd-X": "swapWithSublimeMark",
+ "Cmd-K Cmd-Y": "sublimeYank",
+ "Cmd-K Cmd-C": "showInCenter",
+ "Cmd-K Cmd-G": "clearBookmarks",
+ "Cmd-K Cmd-Backspace": "delLineLeft",
+ "Cmd-K Cmd-0": "unfoldAll",
+ "Cmd-K Cmd-J": "unfoldAll",
+ "Ctrl-Shift-Up": "addCursorToPrevLine",
+ "Ctrl-Shift-Down": "addCursorToNextLine",
+ "Cmd-F3": "findUnder",
+ "Shift-Cmd-F3": "findUnderPrevious",
+ "Alt-F3": "findAllUnder",
+ "Shift-Cmd-[": "fold",
+ "Shift-Cmd-]": "unfold",
+ "Cmd-I": "findIncremental",
+ "Shift-Cmd-I": "findIncrementalReverse",
+ "Cmd-H": "replace",
+ "F3": "findNext",
+ "Shift-F3": "findPrev",
+ "fallthrough": "macDefault"
+ };
+ CodeMirror.normalizeKeyMap(keyMap.macSublime);
+
+ keyMap.pcSublime = {
+ "Shift-Tab": "indentLess",
+ "Shift-Ctrl-K": "deleteLine",
+ "Alt-Q": "wrapLines",
+ "Ctrl-T": "transposeChars",
+ "Alt-Left": "goSubwordLeft",
+ "Alt-Right": "goSubwordRight",
+ "Ctrl-Up": "scrollLineUp",
+ "Ctrl-Down": "scrollLineDown",
+ "Ctrl-L": "selectLine",
+ "Shift-Ctrl-L": "splitSelectionByLine",
+ "Esc": "singleSelectionTop",
+ "Ctrl-Enter": "insertLineAfter",
+ "Shift-Ctrl-Enter": "insertLineBefore",
+ "Ctrl-D": "selectNextOccurrence",
+ "Shift-Ctrl-Space": "selectScope",
+ "Shift-Ctrl-M": "selectBetweenBrackets",
+ "Ctrl-M": "goToBracket",
+ "Shift-Ctrl-Up": "swapLineUp",
+ "Shift-Ctrl-Down": "swapLineDown",
+ "Ctrl-/": "toggleCommentIndented",
+ "Ctrl-J": "joinLines",
+ "Shift-Ctrl-D": "duplicateLine",
+ "F9": "sortLines",
+ "Ctrl-F9": "sortLinesInsensitive",
+ "F2": "nextBookmark",
+ "Shift-F2": "prevBookmark",
+ "Ctrl-F2": "toggleBookmark",
+ "Shift-Ctrl-F2": "clearBookmarks",
+ "Alt-F2": "selectBookmarks",
+ "Backspace": "smartBackspace",
+ "Ctrl-K Ctrl-K": "delLineRight",
+ "Ctrl-K Ctrl-U": "upcaseAtCursor",
+ "Ctrl-K Ctrl-L": "downcaseAtCursor",
+ "Ctrl-K Ctrl-Space": "setSublimeMark",
+ "Ctrl-K Ctrl-A": "selectToSublimeMark",
+ "Ctrl-K Ctrl-W": "deleteToSublimeMark",
+ "Ctrl-K Ctrl-X": "swapWithSublimeMark",
+ "Ctrl-K Ctrl-Y": "sublimeYank",
+ "Ctrl-K Ctrl-C": "showInCenter",
+ "Ctrl-K Ctrl-G": "clearBookmarks",
+ "Ctrl-K Ctrl-Backspace": "delLineLeft",
+ "Ctrl-K Ctrl-0": "unfoldAll",
+ "Ctrl-K Ctrl-J": "unfoldAll",
+ "Ctrl-Alt-Up": "addCursorToPrevLine",
+ "Ctrl-Alt-Down": "addCursorToNextLine",
+ "Ctrl-F3": "findUnder",
+ "Shift-Ctrl-F3": "findUnderPrevious",
+ "Alt-F3": "findAllUnder",
+ "Shift-Ctrl-[": "fold",
+ "Shift-Ctrl-]": "unfold",
+ "Ctrl-I": "findIncremental",
+ "Shift-Ctrl-I": "findIncrementalReverse",
+ "Ctrl-H": "replace",
+ "F3": "findNext",
+ "Shift-F3": "findPrev",
+ "fallthrough": "pcDefault"
+ };
+ CodeMirror.normalizeKeyMap(keyMap.pcSublime);
+
+ var mac = keyMap.default == keyMap.macDefault;
+ keyMap.sublime = mac ? keyMap.macSublime : keyMap.pcSublime;
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/keymap/vim.js b/devtools/client/shared/sourceeditor/codemirror/keymap/vim.js
new file mode 100644
index 0000000000..e08ad11808
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/keymap/vim.js
@@ -0,0 +1,5494 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/**
+ * Supported keybindings:
+ * Too many to list. Refer to defaultKeymap below.
+ *
+ * Supported Ex commands:
+ * Refer to defaultExCommandMap below.
+ *
+ * Registers: unnamed, -, a-z, A-Z, 0-9
+ * (Does not respect the special case for number registers when delete
+ * operator is made with these commands: %, (, ), , /, ?, n, N, {, } )
+ * TODO: Implement the remaining registers.
+ *
+ * Marks: a-z, A-Z, and 0-9
+ * TODO: Implement the remaining special marks. They have more complex
+ * behavior.
+ *
+ * Events:
+ * 'vim-mode-change' - raised on the editor anytime the current mode changes,
+ * Event object: {mode: "visual", subMode: "linewise"}
+ *
+ * Code structure:
+ * 1. Default keymap
+ * 2. Variable declarations and short basic helpers
+ * 3. Instance (External API) implementation
+ * 4. Internal state tracking objects (input state, counter) implementation
+ * and instantiation
+ * 5. Key handler (the main command dispatcher) implementation
+ * 6. Motion, operator, and action implementations
+ * 7. Helper functions for the key handler, motions, operators, and actions
+ * 8. Set up Vim to work as a keymap for CodeMirror.
+ * 9. Ex command implementations.
+ */
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/search/searchcursor.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/dialog/dialog.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/addon/edit/matchbrackets.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../lib/codemirror", "../addon/search/searchcursor", "../addon/dialog/dialog", "../addon/edit/matchbrackets"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ 'use strict';
+
+ var defaultKeymap = [
+ // Key to key mapping. This goes first to make it possible to override
+ // existing mappings.
+ { keys: '<Left>', type: 'keyToKey', toKeys: 'h' },
+ { keys: '<Right>', type: 'keyToKey', toKeys: 'l' },
+ { keys: '<Up>', type: 'keyToKey', toKeys: 'k' },
+ { keys: '<Down>', type: 'keyToKey', toKeys: 'j' },
+ { keys: '<Space>', type: 'keyToKey', toKeys: 'l' },
+ { keys: '<BS>', type: 'keyToKey', toKeys: 'h', context: 'normal'},
+ { keys: '<Del>', type: 'keyToKey', toKeys: 'x', context: 'normal'},
+ { keys: '<C-Space>', type: 'keyToKey', toKeys: 'W' },
+ { keys: '<C-BS>', type: 'keyToKey', toKeys: 'B', context: 'normal' },
+ { keys: '<S-Space>', type: 'keyToKey', toKeys: 'w' },
+ { keys: '<S-BS>', type: 'keyToKey', toKeys: 'b', context: 'normal' },
+ { keys: '<C-n>', type: 'keyToKey', toKeys: 'j' },
+ { keys: '<C-p>', type: 'keyToKey', toKeys: 'k' },
+ { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>' },
+ { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>' },
+ { keys: '<C-[>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
+ { keys: '<C-c>', type: 'keyToKey', toKeys: '<Esc>', context: 'insert' },
+ { keys: 's', type: 'keyToKey', toKeys: 'cl', context: 'normal' },
+ { keys: 's', type: 'keyToKey', toKeys: 'c', context: 'visual'},
+ { keys: 'S', type: 'keyToKey', toKeys: 'cc', context: 'normal' },
+ { keys: 'S', type: 'keyToKey', toKeys: 'VdO', context: 'visual' },
+ { keys: '<Home>', type: 'keyToKey', toKeys: '0' },
+ { keys: '<End>', type: 'keyToKey', toKeys: '$' },
+ { keys: '<PageUp>', type: 'keyToKey', toKeys: '<C-b>' },
+ { keys: '<PageDown>', type: 'keyToKey', toKeys: '<C-f>' },
+ { keys: '<CR>', type: 'keyToKey', toKeys: 'j^', context: 'normal' },
+ { keys: '<Ins>', type: 'action', action: 'toggleOverwrite', context: 'insert' },
+ // Motions
+ { keys: 'H', type: 'motion', motion: 'moveToTopLine', motionArgs: { linewise: true, toJumplist: true }},
+ { keys: 'M', type: 'motion', motion: 'moveToMiddleLine', motionArgs: { linewise: true, toJumplist: true }},
+ { keys: 'L', type: 'motion', motion: 'moveToBottomLine', motionArgs: { linewise: true, toJumplist: true }},
+ { keys: 'h', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: false }},
+ { keys: 'l', type: 'motion', motion: 'moveByCharacters', motionArgs: { forward: true }},
+ { keys: 'j', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, linewise: true }},
+ { keys: 'k', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, linewise: true }},
+ { keys: 'gj', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: true }},
+ { keys: 'gk', type: 'motion', motion: 'moveByDisplayLines', motionArgs: { forward: false }},
+ { keys: 'w', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false }},
+ { keys: 'W', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: false, bigWord: true }},
+ { keys: 'e', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, inclusive: true }},
+ { keys: 'E', type: 'motion', motion: 'moveByWords', motionArgs: { forward: true, wordEnd: true, bigWord: true, inclusive: true }},
+ { keys: 'b', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }},
+ { keys: 'B', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false, bigWord: true }},
+ { keys: 'ge', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, inclusive: true }},
+ { keys: 'gE', type: 'motion', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: true, bigWord: true, inclusive: true }},
+ { keys: '{', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: false, toJumplist: true }},
+ { keys: '}', type: 'motion', motion: 'moveByParagraph', motionArgs: { forward: true, toJumplist: true }},
+ { keys: '(', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: false }},
+ { keys: ')', type: 'motion', motion: 'moveBySentence', motionArgs: { forward: true }},
+ { keys: '<C-f>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: true }},
+ { keys: '<C-b>', type: 'motion', motion: 'moveByPage', motionArgs: { forward: false }},
+ { keys: '<C-d>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: true, explicitRepeat: true }},
+ { keys: '<C-u>', type: 'motion', motion: 'moveByScroll', motionArgs: { forward: false, explicitRepeat: true }},
+ { keys: 'gg', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: false, explicitRepeat: true, linewise: true, toJumplist: true }},
+ { keys: 'G', type: 'motion', motion: 'moveToLineOrEdgeOfDocument', motionArgs: { forward: true, explicitRepeat: true, linewise: true, toJumplist: true }},
+ { keys: '0', type: 'motion', motion: 'moveToStartOfLine' },
+ { keys: '^', type: 'motion', motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: '+', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true }},
+ { keys: '-', type: 'motion', motion: 'moveByLines', motionArgs: { forward: false, toFirstChar:true }},
+ { keys: '_', type: 'motion', motion: 'moveByLines', motionArgs: { forward: true, toFirstChar:true, repeatOffset:-1 }},
+ { keys: '$', type: 'motion', motion: 'moveToEol', motionArgs: { inclusive: true }},
+ { keys: '%', type: 'motion', motion: 'moveToMatchedSymbol', motionArgs: { inclusive: true, toJumplist: true }},
+ { keys: 'f<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: true , inclusive: true }},
+ { keys: 'F<character>', type: 'motion', motion: 'moveToCharacter', motionArgs: { forward: false }},
+ { keys: 't<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: true, inclusive: true }},
+ { keys: 'T<character>', type: 'motion', motion: 'moveTillCharacter', motionArgs: { forward: false }},
+ { keys: ';', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: true }},
+ { keys: ',', type: 'motion', motion: 'repeatLastCharacterSearch', motionArgs: { forward: false }},
+ { keys: '\'<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true, linewise: true}},
+ { keys: '`<character>', type: 'motion', motion: 'goToMark', motionArgs: {toJumplist: true}},
+ { keys: ']`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true } },
+ { keys: '[`', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false } },
+ { keys: ']\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: true, linewise: true } },
+ { keys: '[\'', type: 'motion', motion: 'jumpToMark', motionArgs: { forward: false, linewise: true } },
+ // the next two aren't motions but must come before more general motion declarations
+ { keys: ']p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true, matchIndent: true}},
+ { keys: '[p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true, matchIndent: true}},
+ { keys: ']<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: true, toJumplist: true}},
+ { keys: '[<character>', type: 'motion', motion: 'moveToSymbol', motionArgs: { forward: false, toJumplist: true}},
+ { keys: '|', type: 'motion', motion: 'moveToColumn'},
+ { keys: 'o', type: 'motion', motion: 'moveToOtherHighlightedEnd', context:'visual'},
+ { keys: 'O', type: 'motion', motion: 'moveToOtherHighlightedEnd', motionArgs: {sameLine: true}, context:'visual'},
+ // Operators
+ { keys: 'd', type: 'operator', operator: 'delete' },
+ { keys: 'y', type: 'operator', operator: 'yank' },
+ { keys: 'c', type: 'operator', operator: 'change' },
+ { keys: '=', type: 'operator', operator: 'indentAuto' },
+ { keys: '>', type: 'operator', operator: 'indent', operatorArgs: { indentRight: true }},
+ { keys: '<', type: 'operator', operator: 'indent', operatorArgs: { indentRight: false }},
+ { keys: 'g~', type: 'operator', operator: 'changeCase' },
+ { keys: 'gu', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, isEdit: true },
+ { keys: 'gU', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, isEdit: true },
+ { keys: 'n', type: 'motion', motion: 'findNext', motionArgs: { forward: true, toJumplist: true }},
+ { keys: 'N', type: 'motion', motion: 'findNext', motionArgs: { forward: false, toJumplist: true }},
+ // Operator-Motion dual commands
+ { keys: 'x', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorMotionArgs: { visualLine: false }},
+ { keys: 'X', type: 'operatorMotion', operator: 'delete', motion: 'moveByCharacters', motionArgs: { forward: false }, operatorMotionArgs: { visualLine: true }},
+ { keys: 'D', type: 'operatorMotion', operator: 'delete', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
+ { keys: 'D', type: 'operator', operator: 'delete', operatorArgs: { linewise: true }, context: 'visual'},
+ { keys: 'Y', type: 'operatorMotion', operator: 'yank', motion: 'expandToLine', motionArgs: { linewise: true }, context: 'normal'},
+ { keys: 'Y', type: 'operator', operator: 'yank', operatorArgs: { linewise: true }, context: 'visual'},
+ { keys: 'C', type: 'operatorMotion', operator: 'change', motion: 'moveToEol', motionArgs: { inclusive: true }, context: 'normal'},
+ { keys: 'C', type: 'operator', operator: 'change', operatorArgs: { linewise: true }, context: 'visual'},
+ { keys: '~', type: 'operatorMotion', operator: 'changeCase', motion: 'moveByCharacters', motionArgs: { forward: true }, operatorArgs: { shouldMoveCursor: true }, context: 'normal'},
+ { keys: '~', type: 'operator', operator: 'changeCase', context: 'visual'},
+ { keys: '<C-w>', type: 'operatorMotion', operator: 'delete', motion: 'moveByWords', motionArgs: { forward: false, wordEnd: false }, context: 'insert' },
+ //ignore C-w in normal mode
+ { keys: '<C-w>', type: 'idle', context: 'normal' },
+ // Actions
+ { keys: '<C-i>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: true }},
+ { keys: '<C-o>', type: 'action', action: 'jumpListWalk', actionArgs: { forward: false }},
+ { keys: '<C-e>', type: 'action', action: 'scroll', actionArgs: { forward: true, linewise: true }},
+ { keys: '<C-y>', type: 'action', action: 'scroll', actionArgs: { forward: false, linewise: true }},
+ { keys: 'a', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'charAfter' }, context: 'normal' },
+ { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'eol' }, context: 'normal' },
+ { keys: 'A', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'endOfSelectedArea' }, context: 'visual' },
+ { keys: 'i', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'inplace' }, context: 'normal' },
+ { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'firstNonBlank'}, context: 'normal' },
+ { keys: 'I', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { insertAt: 'startOfSelectedArea' }, context: 'visual' },
+ { keys: 'o', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: true }, context: 'normal' },
+ { keys: 'O', type: 'action', action: 'newLineAndEnterInsertMode', isEdit: true, interlaceInsertRepeat: true, actionArgs: { after: false }, context: 'normal' },
+ { keys: 'v', type: 'action', action: 'toggleVisualMode' },
+ { keys: 'V', type: 'action', action: 'toggleVisualMode', actionArgs: { linewise: true }},
+ { keys: '<C-v>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},
+ { keys: '<C-q>', type: 'action', action: 'toggleVisualMode', actionArgs: { blockwise: true }},
+ { keys: 'gv', type: 'action', action: 'reselectLastSelection' },
+ { keys: 'J', type: 'action', action: 'joinLines', isEdit: true },
+ { keys: 'p', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: true, isEdit: true }},
+ { keys: 'P', type: 'action', action: 'paste', isEdit: true, actionArgs: { after: false, isEdit: true }},
+ { keys: 'r<character>', type: 'action', action: 'replace', isEdit: true },
+ { keys: '@<character>', type: 'action', action: 'replayMacro' },
+ { keys: 'q<character>', type: 'action', action: 'enterMacroRecordMode' },
+ // Handle Replace-mode as a special case of insert mode.
+ { keys: 'R', type: 'action', action: 'enterInsertMode', isEdit: true, actionArgs: { replace: true }},
+ { keys: 'u', type: 'action', action: 'undo', context: 'normal' },
+ { keys: 'u', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: true}, context: 'visual', isEdit: true },
+ { keys: 'U', type: 'operator', operator: 'changeCase', operatorArgs: {toLower: false}, context: 'visual', isEdit: true },
+ { keys: '<C-r>', type: 'action', action: 'redo' },
+ { keys: 'm<character>', type: 'action', action: 'setMark' },
+ { keys: '"<character>', type: 'action', action: 'setRegister' },
+ { keys: 'zz', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }},
+ { keys: 'z.', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'center' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: 'zt', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }},
+ { keys: 'z<CR>', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'top' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: 'z-', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }},
+ { keys: 'zb', type: 'action', action: 'scrollToCursor', actionArgs: { position: 'bottom' }, motion: 'moveToFirstNonWhiteSpaceCharacter' },
+ { keys: '.', type: 'action', action: 'repeatLastEdit' },
+ { keys: '<C-a>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: true, backtrack: false}},
+ { keys: '<C-x>', type: 'action', action: 'incrementNumberToken', isEdit: true, actionArgs: {increase: false, backtrack: false}},
+ { keys: '<C-t>', type: 'action', action: 'indent', actionArgs: { indentRight: true }, context: 'insert' },
+ { keys: '<C-d>', type: 'action', action: 'indent', actionArgs: { indentRight: false }, context: 'insert' },
+ // Text object motions
+ { keys: 'a<character>', type: 'motion', motion: 'textObjectManipulation' },
+ { keys: 'i<character>', type: 'motion', motion: 'textObjectManipulation', motionArgs: { textObjectInner: true }},
+ // Search
+ { keys: '/', type: 'search', searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }},
+ { keys: '?', type: 'search', searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }},
+ { keys: '*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
+ { keys: '#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', wholeWordOnly: true, toJumplist: true }},
+ { keys: 'g*', type: 'search', searchArgs: { forward: true, querySrc: 'wordUnderCursor', toJumplist: true }},
+ { keys: 'g#', type: 'search', searchArgs: { forward: false, querySrc: 'wordUnderCursor', toJumplist: true }},
+ // Ex command
+ { keys: ':', type: 'ex' }
+ ];
+ var defaultKeymapLength = defaultKeymap.length;
+
+ /**
+ * Ex commands
+ * Care must be taken when adding to the default Ex command map. For any
+ * pair of commands that have a shared prefix, at least one of their
+ * shortNames must not match the prefix of the other command.
+ */
+ var defaultExCommandMap = [
+ { name: 'colorscheme', shortName: 'colo' },
+ { name: 'map' },
+ { name: 'imap', shortName: 'im' },
+ { name: 'nmap', shortName: 'nm' },
+ { name: 'vmap', shortName: 'vm' },
+ { name: 'unmap' },
+ { name: 'write', shortName: 'w' },
+ { name: 'undo', shortName: 'u' },
+ { name: 'redo', shortName: 'red' },
+ { name: 'set', shortName: 'se' },
+ { name: 'set', shortName: 'se' },
+ { name: 'setlocal', shortName: 'setl' },
+ { name: 'setglobal', shortName: 'setg' },
+ { name: 'sort', shortName: 'sor' },
+ { name: 'substitute', shortName: 's', possiblyAsync: true },
+ { name: 'nohlsearch', shortName: 'noh' },
+ { name: 'yank', shortName: 'y' },
+ { name: 'delmarks', shortName: 'delm' },
+ { name: 'registers', shortName: 'reg', excludeFromCommandHistory: true },
+ { name: 'global', shortName: 'g' }
+ ];
+
+ var Pos = CodeMirror.Pos;
+
+ var Vim = function() {
+ function enterVimMode(cm) {
+ cm.setOption('disableInput', true);
+ cm.setOption('showCursorWhenSelecting', false);
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
+ cm.on('cursorActivity', onCursorActivity);
+ maybeInitVimState(cm);
+ CodeMirror.on(cm.getInputField(), 'paste', getOnPasteFn(cm));
+ }
+
+ function leaveVimMode(cm) {
+ cm.setOption('disableInput', false);
+ cm.off('cursorActivity', onCursorActivity);
+ CodeMirror.off(cm.getInputField(), 'paste', getOnPasteFn(cm));
+ cm.state.vim = null;
+ }
+
+ function detachVimMap(cm, next) {
+ if (this == CodeMirror.keyMap.vim) {
+ CodeMirror.rmClass(cm.getWrapperElement(), "cm-fat-cursor");
+ if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) {
+ disableFatCursorMark(cm);
+ cm.getInputField().style.caretColor = "";
+ }
+ }
+
+ if (!next || next.attach != attachVimMap)
+ leaveVimMode(cm);
+ }
+ function attachVimMap(cm, prev) {
+ if (this == CodeMirror.keyMap.vim) {
+ CodeMirror.addClass(cm.getWrapperElement(), "cm-fat-cursor");
+ if (cm.getOption("inputStyle") == "contenteditable" && document.body.style.caretColor != null) {
+ enableFatCursorMark(cm);
+ cm.getInputField().style.caretColor = "transparent";
+ }
+ }
+
+ if (!prev || prev.attach != attachVimMap)
+ enterVimMode(cm);
+ }
+
+ function updateFatCursorMark(cm) {
+ if (!cm.state.fatCursorMarks) return;
+ clearFatCursorMark(cm);
+ var ranges = cm.listSelections(), result = []
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i]
+ if (range.empty()) {
+ if (range.anchor.ch < cm.getLine(range.anchor.line).length) {
+ result.push(cm.markText(range.anchor, Pos(range.anchor.line, range.anchor.ch + 1),
+ {className: "cm-fat-cursor-mark"}))
+ } else {
+ var widget = document.createElement("span")
+ widget.textContent = "\u00a0"
+ widget.className = "cm-fat-cursor-mark"
+ result.push(cm.setBookmark(range.anchor, {widget: widget}))
+ }
+ }
+ }
+ cm.state.fatCursorMarks = result;
+ }
+
+ function clearFatCursorMark(cm) {
+ var marks = cm.state.fatCursorMarks;
+ if (marks) for (var i = 0; i < marks.length; i++) marks[i].clear();
+ }
+
+ function enableFatCursorMark(cm) {
+ cm.state.fatCursorMarks = [];
+ updateFatCursorMark(cm)
+ cm.on("cursorActivity", updateFatCursorMark)
+ }
+
+ function disableFatCursorMark(cm) {
+ clearFatCursorMark(cm);
+ cm.off("cursorActivity", updateFatCursorMark);
+ // explicitly set fatCursorMarks to null because event listener above
+ // can be invoke after removing it, if off is called from operation
+ cm.state.fatCursorMarks = null;
+ }
+
+ // Deprecated, simply setting the keymap works again.
+ CodeMirror.defineOption('vimMode', false, function(cm, val, prev) {
+ if (val && cm.getOption("keyMap") != "vim")
+ cm.setOption("keyMap", "vim");
+ else if (!val && prev != CodeMirror.Init && /^vim/.test(cm.getOption("keyMap")))
+ cm.setOption("keyMap", "default");
+ });
+
+ function cmKey(key, cm) {
+ if (!cm) { return undefined; }
+ if (this[key]) { return this[key]; }
+ var vimKey = cmKeyToVimKey(key);
+ if (!vimKey) {
+ return false;
+ }
+ var cmd = CodeMirror.Vim.findKey(cm, vimKey);
+ if (typeof cmd == 'function') {
+ CodeMirror.signal(cm, 'vim-keypress', vimKey);
+ }
+ return cmd;
+ }
+
+ var modifiers = {'Shift': 'S', 'Ctrl': 'C', 'Alt': 'A', 'Cmd': 'D', 'Mod': 'A'};
+ var specialKeys = {Enter:'CR',Backspace:'BS',Delete:'Del',Insert:'Ins'};
+ function cmKeyToVimKey(key) {
+ if (key.charAt(0) == '\'') {
+ // Keypress character binding of format "'a'"
+ return key.charAt(1);
+ }
+ var pieces = key.split(/-(?!$)/);
+ var lastPiece = pieces[pieces.length - 1];
+ if (pieces.length == 1 && pieces[0].length == 1) {
+ // No-modifier bindings use literal character bindings above. Skip.
+ return false;
+ } else if (pieces.length == 2 && pieces[0] == 'Shift' && lastPiece.length == 1) {
+ // Ignore Shift+char bindings as they should be handled by literal character.
+ return false;
+ }
+ var hasCharacter = false;
+ for (var i = 0; i < pieces.length; i++) {
+ var piece = pieces[i];
+ if (piece in modifiers) { pieces[i] = modifiers[piece]; }
+ else { hasCharacter = true; }
+ if (piece in specialKeys) { pieces[i] = specialKeys[piece]; }
+ }
+ if (!hasCharacter) {
+ // Vim does not support modifier only keys.
+ return false;
+ }
+ // TODO: Current bindings expect the character to be lower case, but
+ // it looks like vim key notation uses upper case.
+ if (isUpperCase(lastPiece)) {
+ pieces[pieces.length - 1] = lastPiece.toLowerCase();
+ }
+ return '<' + pieces.join('-') + '>';
+ }
+
+ function getOnPasteFn(cm) {
+ var vim = cm.state.vim;
+ if (!vim.onPasteFn) {
+ vim.onPasteFn = function() {
+ if (!vim.insertMode) {
+ cm.setCursor(offsetCursor(cm.getCursor(), 0, 1));
+ actions.enterInsertMode(cm, {}, vim);
+ }
+ };
+ }
+ return vim.onPasteFn;
+ }
+
+ var numberRegex = /[\d]/;
+ var wordCharTest = [CodeMirror.isWordChar, function(ch) {
+ return ch && !CodeMirror.isWordChar(ch) && !/\s/.test(ch);
+ }], bigWordCharTest = [function(ch) {
+ return /\S/.test(ch);
+ }];
+ function makeKeyRange(start, size) {
+ var keys = [];
+ for (var i = start; i < start + size; i++) {
+ keys.push(String.fromCharCode(i));
+ }
+ return keys;
+ }
+ var upperCaseAlphabet = makeKeyRange(65, 26);
+ var lowerCaseAlphabet = makeKeyRange(97, 26);
+ var numbers = makeKeyRange(48, 10);
+ var validMarks = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['<', '>']);
+ var validRegisters = [].concat(upperCaseAlphabet, lowerCaseAlphabet, numbers, ['-', '"', '.', ':', '/']);
+
+ function isLine(cm, line) {
+ return line >= cm.firstLine() && line <= cm.lastLine();
+ }
+ function isLowerCase(k) {
+ return (/^[a-z]$/).test(k);
+ }
+ function isMatchableSymbol(k) {
+ return '()[]{}'.indexOf(k) != -1;
+ }
+ function isNumber(k) {
+ return numberRegex.test(k);
+ }
+ function isUpperCase(k) {
+ return (/^[A-Z]$/).test(k);
+ }
+ function isWhiteSpaceString(k) {
+ return (/^\s*$/).test(k);
+ }
+ function isEndOfSentenceSymbol(k) {
+ return '.?!'.indexOf(k) != -1;
+ }
+ function inArray(val, arr) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arr[i] == val) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ var options = {};
+ function defineOption(name, defaultValue, type, aliases, callback) {
+ if (defaultValue === undefined && !callback) {
+ throw Error('defaultValue is required unless callback is provided');
+ }
+ if (!type) { type = 'string'; }
+ options[name] = {
+ type: type,
+ defaultValue: defaultValue,
+ callback: callback
+ };
+ if (aliases) {
+ for (var i = 0; i < aliases.length; i++) {
+ options[aliases[i]] = options[name];
+ }
+ }
+ if (defaultValue) {
+ setOption(name, defaultValue);
+ }
+ }
+
+ function setOption(name, value, cm, cfg) {
+ var option = options[name];
+ cfg = cfg || {};
+ var scope = cfg.scope;
+ if (!option) {
+ return new Error('Unknown option: ' + name);
+ }
+ if (option.type == 'boolean') {
+ if (value && value !== true) {
+ return new Error('Invalid argument: ' + name + '=' + value);
+ } else if (value !== false) {
+ // Boolean options are set to true if value is not defined.
+ value = true;
+ }
+ }
+ if (option.callback) {
+ if (scope !== 'local') {
+ option.callback(value, undefined);
+ }
+ if (scope !== 'global' && cm) {
+ option.callback(value, cm);
+ }
+ } else {
+ if (scope !== 'local') {
+ option.value = option.type == 'boolean' ? !!value : value;
+ }
+ if (scope !== 'global' && cm) {
+ cm.state.vim.options[name] = {value: value};
+ }
+ }
+ }
+
+ function getOption(name, cm, cfg) {
+ var option = options[name];
+ cfg = cfg || {};
+ var scope = cfg.scope;
+ if (!option) {
+ return new Error('Unknown option: ' + name);
+ }
+ if (option.callback) {
+ var local = cm && option.callback(undefined, cm);
+ if (scope !== 'global' && local !== undefined) {
+ return local;
+ }
+ if (scope !== 'local') {
+ return option.callback();
+ }
+ return;
+ } else {
+ var local = (scope !== 'global') && (cm && cm.state.vim.options[name]);
+ return (local || (scope !== 'local') && option || {}).value;
+ }
+ }
+
+ defineOption('filetype', undefined, 'string', ['ft'], function(name, cm) {
+ // Option is local. Do nothing for global.
+ if (cm === undefined) {
+ return;
+ }
+ // The 'filetype' option proxies to the CodeMirror 'mode' option.
+ if (name === undefined) {
+ var mode = cm.getOption('mode');
+ return mode == 'null' ? '' : mode;
+ } else {
+ var mode = name == '' ? 'null' : name;
+ cm.setOption('mode', mode);
+ }
+ });
+
+ var createCircularJumpList = function() {
+ var size = 100;
+ var pointer = -1;
+ var head = 0;
+ var tail = 0;
+ var buffer = new Array(size);
+ function add(cm, oldCur, newCur) {
+ var current = pointer % size;
+ var curMark = buffer[current];
+ function useNextSlot(cursor) {
+ var next = ++pointer % size;
+ var trashMark = buffer[next];
+ if (trashMark) {
+ trashMark.clear();
+ }
+ buffer[next] = cm.setBookmark(cursor);
+ }
+ if (curMark) {
+ var markPos = curMark.find();
+ // avoid recording redundant cursor position
+ if (markPos && !cursorEqual(markPos, oldCur)) {
+ useNextSlot(oldCur);
+ }
+ } else {
+ useNextSlot(oldCur);
+ }
+ useNextSlot(newCur);
+ head = pointer;
+ tail = pointer - size + 1;
+ if (tail < 0) {
+ tail = 0;
+ }
+ }
+ function move(cm, offset) {
+ pointer += offset;
+ if (pointer > head) {
+ pointer = head;
+ } else if (pointer < tail) {
+ pointer = tail;
+ }
+ var mark = buffer[(size + pointer) % size];
+ // skip marks that are temporarily removed from text buffer
+ if (mark && !mark.find()) {
+ var inc = offset > 0 ? 1 : -1;
+ var newCur;
+ var oldCur = cm.getCursor();
+ do {
+ pointer += inc;
+ mark = buffer[(size + pointer) % size];
+ // skip marks that are the same as current position
+ if (mark &&
+ (newCur = mark.find()) &&
+ !cursorEqual(oldCur, newCur)) {
+ break;
+ }
+ } while (pointer < head && pointer > tail);
+ }
+ return mark;
+ }
+ return {
+ cachedCursor: undefined, //used for # and * jumps
+ add: add,
+ move: move
+ };
+ };
+
+ // Returns an object to track the changes associated insert mode. It
+ // clones the object that is passed in, or creates an empty object one if
+ // none is provided.
+ var createInsertModeChanges = function(c) {
+ if (c) {
+ // Copy construction
+ return {
+ changes: c.changes,
+ expectCursorActivityForChange: c.expectCursorActivityForChange
+ };
+ }
+ return {
+ // Change list
+ changes: [],
+ // Set to true on change, false on cursorActivity.
+ expectCursorActivityForChange: false
+ };
+ };
+
+ function MacroModeState() {
+ this.latestRegister = undefined;
+ this.isPlaying = false;
+ this.isRecording = false;
+ this.replaySearchQueries = [];
+ this.onRecordingDone = undefined;
+ this.lastInsertModeChanges = createInsertModeChanges();
+ }
+ MacroModeState.prototype = {
+ exitMacroRecordMode: function() {
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.onRecordingDone) {
+ macroModeState.onRecordingDone(); // close dialog
+ }
+ macroModeState.onRecordingDone = undefined;
+ macroModeState.isRecording = false;
+ },
+ enterMacroRecordMode: function(cm, registerName) {
+ var register =
+ vimGlobalState.registerController.getRegister(registerName);
+ if (register) {
+ register.clear();
+ this.latestRegister = registerName;
+ if (cm.openDialog) {
+ this.onRecordingDone = cm.openDialog(
+ '(recording)['+registerName+']', null, {bottom:true});
+ }
+ this.isRecording = true;
+ }
+ }
+ };
+
+ function maybeInitVimState(cm) {
+ if (!cm.state.vim) {
+ // Store instance state in the CodeMirror object.
+ cm.state.vim = {
+ inputState: new InputState(),
+ // Vim's input state that triggered the last edit, used to repeat
+ // motions and operators with '.'.
+ lastEditInputState: undefined,
+ // Vim's action command before the last edit, used to repeat actions
+ // with '.' and insert mode repeat.
+ lastEditActionCommand: undefined,
+ // When using jk for navigation, if you move from a longer line to a
+ // shorter line, the cursor may clip to the end of the shorter line.
+ // If j is pressed again and cursor goes to the next line, the
+ // cursor should go back to its horizontal position on the longer
+ // line if it can. This is to keep track of the horizontal position.
+ lastHPos: -1,
+ // Doing the same with screen-position for gj/gk
+ lastHSPos: -1,
+ // The last motion command run. Cleared if a non-motion command gets
+ // executed in between.
+ lastMotion: null,
+ marks: {},
+ // Mark for rendering fake cursor for visual mode.
+ fakeCursor: null,
+ insertMode: false,
+ // Repeat count for changes made in insert mode, triggered by key
+ // sequences like 3,i. Only exists when insertMode is true.
+ insertModeRepeat: undefined,
+ visualMode: false,
+ // If we are in visual line mode. No effect if visualMode is false.
+ visualLine: false,
+ visualBlock: false,
+ lastSelection: null,
+ lastPastedText: null,
+ sel: {},
+ // Buffer-local/window-local values of vim options.
+ options: {}
+ };
+ }
+ return cm.state.vim;
+ }
+ var vimGlobalState;
+ function resetVimGlobalState() {
+ vimGlobalState = {
+ // The current search query.
+ searchQuery: null,
+ // Whether we are searching backwards.
+ searchIsReversed: false,
+ // Replace part of the last substituted pattern
+ lastSubstituteReplacePart: undefined,
+ jumpList: createCircularJumpList(),
+ macroModeState: new MacroModeState,
+ // Recording latest f, t, F or T motion command.
+ lastCharacterSearch: {increment:0, forward:true, selectedCharacter:''},
+ registerController: new RegisterController({}),
+ // search history buffer
+ searchHistoryController: new HistoryController(),
+ // ex Command history buffer
+ exCommandHistoryController : new HistoryController()
+ };
+ for (var optionName in options) {
+ var option = options[optionName];
+ option.value = option.defaultValue;
+ }
+ }
+
+ var lastInsertModeKeyTimer;
+ var vimApi= {
+ buildKeyMap: function() {
+ // TODO: Convert keymap into dictionary format for fast lookup.
+ },
+ // Testing hook, though it might be useful to expose the register
+ // controller anyways.
+ getRegisterController: function() {
+ return vimGlobalState.registerController;
+ },
+ // Testing hook.
+ resetVimGlobalState_: resetVimGlobalState,
+
+ // Testing hook.
+ getVimGlobalState_: function() {
+ return vimGlobalState;
+ },
+
+ // Testing hook.
+ maybeInitVimState_: maybeInitVimState,
+
+ suppressErrorLogging: false,
+
+ InsertModeKey: InsertModeKey,
+ map: function(lhs, rhs, ctx) {
+ // Add user defined key bindings.
+ exCommandDispatcher.map(lhs, rhs, ctx);
+ },
+ unmap: function(lhs, ctx) {
+ exCommandDispatcher.unmap(lhs, ctx);
+ },
+ // Non-recursive map function.
+ // NOTE: This will not create mappings to key maps that aren't present
+ // in the default key map. See TODO at bottom of function.
+ noremap: function(lhs, rhs, ctx) {
+ function toCtxArray(ctx) {
+ return ctx ? [ctx] : ['normal', 'insert', 'visual'];
+ }
+ var ctxsToMap = toCtxArray(ctx);
+ // Look through all actual defaults to find a map candidate.
+ var actualLength = defaultKeymap.length, origLength = defaultKeymapLength;
+ for (var i = actualLength - origLength;
+ i < actualLength && ctxsToMap.length;
+ i++) {
+ var mapping = defaultKeymap[i];
+ // Omit mappings that operate in the wrong context(s) and those of invalid type.
+ if (mapping.keys == rhs &&
+ (!ctx || !mapping.context || mapping.context === ctx) &&
+ mapping.type.substr(0, 2) !== 'ex' &&
+ mapping.type.substr(0, 3) !== 'key') {
+ // Make a shallow copy of the original keymap entry.
+ var newMapping = {};
+ for (var key in mapping) {
+ newMapping[key] = mapping[key];
+ }
+ // Modify it point to the new mapping with the proper context.
+ newMapping.keys = lhs;
+ if (ctx && !newMapping.context) {
+ newMapping.context = ctx;
+ }
+ // Add it to the keymap with a higher priority than the original.
+ this._mapCommand(newMapping);
+ // Record the mapped contexts as complete.
+ var mappedCtxs = toCtxArray(mapping.context);
+ ctxsToMap = ctxsToMap.filter(function(el) { return mappedCtxs.indexOf(el) === -1; });
+ }
+ }
+ // TODO: Create non-recursive keyToKey mappings for the unmapped contexts once those exist.
+ },
+ // Remove all user-defined mappings for the provided context.
+ mapclear: function(ctx) {
+ // Partition the existing keymap into user-defined and true defaults.
+ var actualLength = defaultKeymap.length,
+ origLength = defaultKeymapLength;
+ var userKeymap = defaultKeymap.slice(0, actualLength - origLength);
+ defaultKeymap = defaultKeymap.slice(actualLength - origLength);
+ if (ctx) {
+ // If a specific context is being cleared, we need to keep mappings
+ // from all other contexts.
+ for (var i = userKeymap.length - 1; i >= 0; i--) {
+ var mapping = userKeymap[i];
+ if (ctx !== mapping.context) {
+ if (mapping.context) {
+ this._mapCommand(mapping);
+ } else {
+ // `mapping` applies to all contexts so create keymap copies
+ // for each context except the one being cleared.
+ var contexts = ['normal', 'insert', 'visual'];
+ for (var j in contexts) {
+ if (contexts[j] !== ctx) {
+ var newMapping = {};
+ for (var key in mapping) {
+ newMapping[key] = mapping[key];
+ }
+ newMapping.context = contexts[j];
+ this._mapCommand(newMapping);
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ // TODO: Expose setOption and getOption as instance methods. Need to decide how to namespace
+ // them, or somehow make them work with the existing CodeMirror setOption/getOption API.
+ setOption: setOption,
+ getOption: getOption,
+ defineOption: defineOption,
+ defineEx: function(name, prefix, func){
+ if (!prefix) {
+ prefix = name;
+ } else if (name.indexOf(prefix) !== 0) {
+ throw new Error('(Vim.defineEx) "'+prefix+'" is not a prefix of "'+name+'", command not registered');
+ }
+ exCommands[name]=func;
+ exCommandDispatcher.commandMap_[prefix]={name:name, shortName:prefix, type:'api'};
+ },
+ handleKey: function (cm, key, origin) {
+ var command = this.findKey(cm, key, origin);
+ if (typeof command === 'function') {
+ return command();
+ }
+ },
+ /**
+ * This is the outermost function called by CodeMirror, after keys have
+ * been mapped to their Vim equivalents.
+ *
+ * Finds a command based on the key (and cached keys if there is a
+ * multi-key sequence). Returns `undefined` if no key is matched, a noop
+ * function if a partial match is found (multi-key), and a function to
+ * execute the bound command if a a key is matched. The function always
+ * returns true.
+ */
+ findKey: function(cm, key, origin) {
+ var vim = maybeInitVimState(cm);
+ function handleMacroRecording() {
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isRecording) {
+ if (key == 'q') {
+ macroModeState.exitMacroRecordMode();
+ clearInputState(cm);
+ return true;
+ }
+ if (origin != 'mapping') {
+ logKey(macroModeState, key);
+ }
+ }
+ }
+ function handleEsc() {
+ if (key == '<Esc>') {
+ // Clear input state and get back to normal mode.
+ clearInputState(cm);
+ if (vim.visualMode) {
+ exitVisualMode(cm);
+ } else if (vim.insertMode) {
+ exitInsertMode(cm);
+ }
+ return true;
+ }
+ }
+ function doKeyToKey(keys) {
+ // TODO: prevent infinite recursion.
+ var match;
+ while (keys) {
+ // Pull off one command key, which is either a single character
+ // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.
+ match = (/<\w+-.+?>|<\w+>|./).exec(keys);
+ key = match[0];
+ keys = keys.substring(match.index + key.length);
+ CodeMirror.Vim.handleKey(cm, key, 'mapping');
+ }
+ }
+
+ function handleKeyInsertMode() {
+ if (handleEsc()) { return true; }
+ var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
+ var keysAreChars = key.length == 1;
+ var match = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
+ // Need to check all key substrings in insert mode.
+ while (keys.length > 1 && match.type != 'full') {
+ var keys = vim.inputState.keyBuffer = keys.slice(1);
+ var thisMatch = commandDispatcher.matchCommand(keys, defaultKeymap, vim.inputState, 'insert');
+ if (thisMatch.type != 'none') { match = thisMatch; }
+ }
+ if (match.type == 'none') { clearInputState(cm); return false; }
+ else if (match.type == 'partial') {
+ if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
+ lastInsertModeKeyTimer = window.setTimeout(
+ function() { if (vim.insertMode && vim.inputState.keyBuffer) { clearInputState(cm); } },
+ getOption('insertModeEscKeysTimeout'));
+ return !keysAreChars;
+ }
+
+ if (lastInsertModeKeyTimer) { window.clearTimeout(lastInsertModeKeyTimer); }
+ if (keysAreChars) {
+ var selections = cm.listSelections();
+ for (var i = 0; i < selections.length; i++) {
+ var here = selections[i].head;
+ cm.replaceRange('', offsetCursor(here, 0, -(keys.length - 1)), here, '+input');
+ }
+ vimGlobalState.macroModeState.lastInsertModeChanges.changes.pop();
+ }
+ clearInputState(cm);
+ return match.command;
+ }
+
+ function handleKeyNonInsertMode() {
+ if (handleMacroRecording() || handleEsc()) { return true; }
+
+ var keys = vim.inputState.keyBuffer = vim.inputState.keyBuffer + key;
+ if (/^[1-9]\d*$/.test(keys)) { return true; }
+
+ var keysMatcher = /^(\d*)(.*)$/.exec(keys);
+ if (!keysMatcher) { clearInputState(cm); return false; }
+ var context = vim.visualMode ? 'visual' :
+ 'normal';
+ var match = commandDispatcher.matchCommand(keysMatcher[2] || keysMatcher[1], defaultKeymap, vim.inputState, context);
+ if (match.type == 'none') { clearInputState(cm); return false; }
+ else if (match.type == 'partial') { return true; }
+
+ vim.inputState.keyBuffer = '';
+ var keysMatcher = /^(\d*)(.*)$/.exec(keys);
+ if (keysMatcher[1] && keysMatcher[1] != '0') {
+ vim.inputState.pushRepeatDigit(keysMatcher[1]);
+ }
+ return match.command;
+ }
+
+ var command;
+ if (vim.insertMode) { command = handleKeyInsertMode(); }
+ else { command = handleKeyNonInsertMode(); }
+ if (command === false) {
+ return !vim.insertMode && key.length === 1 ? function() { return true; } : undefined;
+ } else if (command === true) {
+ // TODO: Look into using CodeMirror's multi-key handling.
+ // Return no-op since we are caching the key. Counts as handled, but
+ // don't want act on it just yet.
+ return function() { return true; };
+ } else {
+ return function() {
+ return cm.operation(function() {
+ cm.curOp.isVimOp = true;
+ try {
+ if (command.type == 'keyToKey') {
+ doKeyToKey(command.toKeys);
+ } else {
+ commandDispatcher.processCommand(cm, vim, command);
+ }
+ } catch (e) {
+ // clear VIM state in case it's in a bad state.
+ cm.state.vim = undefined;
+ maybeInitVimState(cm);
+ if (!CodeMirror.Vim.suppressErrorLogging) {
+ console['log'](e);
+ }
+ throw e;
+ }
+ return true;
+ });
+ };
+ }
+ },
+ handleEx: function(cm, input) {
+ exCommandDispatcher.processCommand(cm, input);
+ },
+
+ defineMotion: defineMotion,
+ defineAction: defineAction,
+ defineOperator: defineOperator,
+ mapCommand: mapCommand,
+ _mapCommand: _mapCommand,
+
+ defineRegister: defineRegister,
+
+ exitVisualMode: exitVisualMode,
+ exitInsertMode: exitInsertMode
+ };
+
+ // Represents the current input state.
+ function InputState() {
+ this.prefixRepeat = [];
+ this.motionRepeat = [];
+
+ this.operator = null;
+ this.operatorArgs = null;
+ this.motion = null;
+ this.motionArgs = null;
+ this.keyBuffer = []; // For matching multi-key commands.
+ this.registerName = null; // Defaults to the unnamed register.
+ }
+ InputState.prototype.pushRepeatDigit = function(n) {
+ if (!this.operator) {
+ this.prefixRepeat = this.prefixRepeat.concat(n);
+ } else {
+ this.motionRepeat = this.motionRepeat.concat(n);
+ }
+ };
+ InputState.prototype.getRepeat = function() {
+ var repeat = 0;
+ if (this.prefixRepeat.length > 0 || this.motionRepeat.length > 0) {
+ repeat = 1;
+ if (this.prefixRepeat.length > 0) {
+ repeat *= parseInt(this.prefixRepeat.join(''), 10);
+ }
+ if (this.motionRepeat.length > 0) {
+ repeat *= parseInt(this.motionRepeat.join(''), 10);
+ }
+ }
+ return repeat;
+ };
+
+ function clearInputState(cm, reason) {
+ cm.state.vim.inputState = new InputState();
+ CodeMirror.signal(cm, 'vim-command-done', reason);
+ }
+
+ /*
+ * Register stores information about copy and paste registers. Besides
+ * text, a register must store whether it is linewise (i.e., when it is
+ * pasted, should it insert itself into a new line, or should the text be
+ * inserted at the cursor position.)
+ */
+ function Register(text, linewise, blockwise) {
+ this.clear();
+ this.keyBuffer = [text || ''];
+ this.insertModeChanges = [];
+ this.searchQueries = [];
+ this.linewise = !!linewise;
+ this.blockwise = !!blockwise;
+ }
+ Register.prototype = {
+ setText: function(text, linewise, blockwise) {
+ this.keyBuffer = [text || ''];
+ this.linewise = !!linewise;
+ this.blockwise = !!blockwise;
+ },
+ pushText: function(text, linewise) {
+ // if this register has ever been set to linewise, use linewise.
+ if (linewise) {
+ if (!this.linewise) {
+ this.keyBuffer.push('\n');
+ }
+ this.linewise = true;
+ }
+ this.keyBuffer.push(text);
+ },
+ pushInsertModeChanges: function(changes) {
+ this.insertModeChanges.push(createInsertModeChanges(changes));
+ },
+ pushSearchQuery: function(query) {
+ this.searchQueries.push(query);
+ },
+ clear: function() {
+ this.keyBuffer = [];
+ this.insertModeChanges = [];
+ this.searchQueries = [];
+ this.linewise = false;
+ },
+ toString: function() {
+ return this.keyBuffer.join('');
+ }
+ };
+
+ /**
+ * Defines an external register.
+ *
+ * The name should be a single character that will be used to reference the register.
+ * The register should support setText, pushText, clear, and toString(). See Register
+ * for a reference implementation.
+ */
+ function defineRegister(name, register) {
+ var registers = vimGlobalState.registerController.registers;
+ if (!name || name.length != 1) {
+ throw Error('Register name must be 1 character');
+ }
+ if (registers[name]) {
+ throw Error('Register already defined ' + name);
+ }
+ registers[name] = register;
+ validRegisters.push(name);
+ }
+
+ /*
+ * vim registers allow you to keep many independent copy and paste buffers.
+ * See http://usevim.com/2012/04/13/registers/ for an introduction.
+ *
+ * RegisterController keeps the state of all the registers. An initial
+ * state may be passed in. The unnamed register '"' will always be
+ * overridden.
+ */
+ function RegisterController(registers) {
+ this.registers = registers;
+ this.unnamedRegister = registers['"'] = new Register();
+ registers['.'] = new Register();
+ registers[':'] = new Register();
+ registers['/'] = new Register();
+ }
+ RegisterController.prototype = {
+ pushText: function(registerName, operator, text, linewise, blockwise) {
+ if (linewise && text.charAt(text.length - 1) !== '\n'){
+ text += '\n';
+ }
+ // Lowercase and uppercase registers refer to the same register.
+ // Uppercase just means append.
+ var register = this.isValidRegister(registerName) ?
+ this.getRegister(registerName) : null;
+ // if no register/an invalid register was specified, things go to the
+ // default registers
+ if (!register) {
+ switch (operator) {
+ case 'yank':
+ // The 0 register contains the text from the most recent yank.
+ this.registers['0'] = new Register(text, linewise, blockwise);
+ break;
+ case 'delete':
+ case 'change':
+ if (text.indexOf('\n') == -1) {
+ // Delete less than 1 line. Update the small delete register.
+ this.registers['-'] = new Register(text, linewise);
+ } else {
+ // Shift down the contents of the numbered registers and put the
+ // deleted text into register 1.
+ this.shiftNumericRegisters_();
+ this.registers['1'] = new Register(text, linewise);
+ }
+ break;
+ }
+ // Make sure the unnamed register is set to what just happened
+ this.unnamedRegister.setText(text, linewise, blockwise);
+ return;
+ }
+
+ // If we've gotten to this point, we've actually specified a register
+ var append = isUpperCase(registerName);
+ if (append) {
+ register.pushText(text, linewise);
+ } else {
+ register.setText(text, linewise, blockwise);
+ }
+ // The unnamed register always has the same value as the last used
+ // register.
+ this.unnamedRegister.setText(register.toString(), linewise);
+ },
+ // Gets the register named @name. If one of @name doesn't already exist,
+ // create it. If @name is invalid, return the unnamedRegister.
+ getRegister: function(name) {
+ if (!this.isValidRegister(name)) {
+ return this.unnamedRegister;
+ }
+ name = name.toLowerCase();
+ if (!this.registers[name]) {
+ this.registers[name] = new Register();
+ }
+ return this.registers[name];
+ },
+ isValidRegister: function(name) {
+ return name && inArray(name, validRegisters);
+ },
+ shiftNumericRegisters_: function() {
+ for (var i = 9; i >= 2; i--) {
+ this.registers[i] = this.getRegister('' + (i - 1));
+ }
+ }
+ };
+ function HistoryController() {
+ this.historyBuffer = [];
+ this.iterator = 0;
+ this.initialPrefix = null;
+ }
+ HistoryController.prototype = {
+ // the input argument here acts a user entered prefix for a small time
+ // until we start autocompletion in which case it is the autocompleted.
+ nextMatch: function (input, up) {
+ var historyBuffer = this.historyBuffer;
+ var dir = up ? -1 : 1;
+ if (this.initialPrefix === null) this.initialPrefix = input;
+ for (var i = this.iterator + dir; up ? i >= 0 : i < historyBuffer.length; i+= dir) {
+ var element = historyBuffer[i];
+ for (var j = 0; j <= element.length; j++) {
+ if (this.initialPrefix == element.substring(0, j)) {
+ this.iterator = i;
+ return element;
+ }
+ }
+ }
+ // should return the user input in case we reach the end of buffer.
+ if (i >= historyBuffer.length) {
+ this.iterator = historyBuffer.length;
+ return this.initialPrefix;
+ }
+ // return the last autocompleted query or exCommand as it is.
+ if (i < 0 ) return input;
+ },
+ pushInput: function(input) {
+ var index = this.historyBuffer.indexOf(input);
+ if (index > -1) this.historyBuffer.splice(index, 1);
+ if (input.length) this.historyBuffer.push(input);
+ },
+ reset: function() {
+ this.initialPrefix = null;
+ this.iterator = this.historyBuffer.length;
+ }
+ };
+ var commandDispatcher = {
+ matchCommand: function(keys, keyMap, inputState, context) {
+ var matches = commandMatches(keys, keyMap, context, inputState);
+ if (!matches.full && !matches.partial) {
+ return {type: 'none'};
+ } else if (!matches.full && matches.partial) {
+ return {type: 'partial'};
+ }
+
+ var bestMatch;
+ for (var i = 0; i < matches.full.length; i++) {
+ var match = matches.full[i];
+ if (!bestMatch) {
+ bestMatch = match;
+ }
+ }
+ if (bestMatch.keys.slice(-11) == '<character>') {
+ var character = lastChar(keys);
+ if (!character) return {type: 'none'};
+ inputState.selectedCharacter = character;
+ }
+ return {type: 'full', command: bestMatch};
+ },
+ processCommand: function(cm, vim, command) {
+ vim.inputState.repeatOverride = command.repeatOverride;
+ switch (command.type) {
+ case 'motion':
+ this.processMotion(cm, vim, command);
+ break;
+ case 'operator':
+ this.processOperator(cm, vim, command);
+ break;
+ case 'operatorMotion':
+ this.processOperatorMotion(cm, vim, command);
+ break;
+ case 'action':
+ this.processAction(cm, vim, command);
+ break;
+ case 'search':
+ this.processSearch(cm, vim, command);
+ break;
+ case 'ex':
+ case 'keyToEx':
+ this.processEx(cm, vim, command);
+ break;
+ default:
+ break;
+ }
+ },
+ processMotion: function(cm, vim, command) {
+ vim.inputState.motion = command.motion;
+ vim.inputState.motionArgs = copyArgs(command.motionArgs);
+ this.evalInput(cm, vim);
+ },
+ processOperator: function(cm, vim, command) {
+ var inputState = vim.inputState;
+ if (inputState.operator) {
+ if (inputState.operator == command.operator) {
+ // Typing an operator twice like 'dd' makes the operator operate
+ // linewise
+ inputState.motion = 'expandToLine';
+ inputState.motionArgs = { linewise: true };
+ this.evalInput(cm, vim);
+ return;
+ } else {
+ // 2 different operators in a row doesn't make sense.
+ clearInputState(cm);
+ }
+ }
+ inputState.operator = command.operator;
+ inputState.operatorArgs = copyArgs(command.operatorArgs);
+ if (vim.visualMode) {
+ // Operating on a selection in visual mode. We don't need a motion.
+ this.evalInput(cm, vim);
+ }
+ },
+ processOperatorMotion: function(cm, vim, command) {
+ var visualMode = vim.visualMode;
+ var operatorMotionArgs = copyArgs(command.operatorMotionArgs);
+ if (operatorMotionArgs) {
+ // Operator motions may have special behavior in visual mode.
+ if (visualMode && operatorMotionArgs.visualLine) {
+ vim.visualLine = true;
+ }
+ }
+ this.processOperator(cm, vim, command);
+ if (!visualMode) {
+ this.processMotion(cm, vim, command);
+ }
+ },
+ processAction: function(cm, vim, command) {
+ var inputState = vim.inputState;
+ var repeat = inputState.getRepeat();
+ var repeatIsExplicit = !!repeat;
+ var actionArgs = copyArgs(command.actionArgs) || {};
+ if (inputState.selectedCharacter) {
+ actionArgs.selectedCharacter = inputState.selectedCharacter;
+ }
+ // Actions may or may not have motions and operators. Do these first.
+ if (command.operator) {
+ this.processOperator(cm, vim, command);
+ }
+ if (command.motion) {
+ this.processMotion(cm, vim, command);
+ }
+ if (command.motion || command.operator) {
+ this.evalInput(cm, vim);
+ }
+ actionArgs.repeat = repeat || 1;
+ actionArgs.repeatIsExplicit = repeatIsExplicit;
+ actionArgs.registerName = inputState.registerName;
+ clearInputState(cm);
+ vim.lastMotion = null;
+ if (command.isEdit) {
+ this.recordLastEdit(vim, inputState, command);
+ }
+ actions[command.action](cm, actionArgs, vim);
+ },
+ processSearch: function(cm, vim, command) {
+ if (!cm.getSearchCursor) {
+ // Search depends on SearchCursor.
+ return;
+ }
+ var forward = command.searchArgs.forward;
+ var wholeWordOnly = command.searchArgs.wholeWordOnly;
+ getSearchState(cm).setReversed(!forward);
+ var promptPrefix = (forward) ? '/' : '?';
+ var originalQuery = getSearchState(cm).getQuery();
+ var originalScrollPos = cm.getScrollInfo();
+ function handleQuery(query, ignoreCase, smartCase) {
+ vimGlobalState.searchHistoryController.pushInput(query);
+ vimGlobalState.searchHistoryController.reset();
+ try {
+ updateSearchQuery(cm, query, ignoreCase, smartCase);
+ } catch (e) {
+ showConfirm(cm, 'Invalid regex: ' + query);
+ clearInputState(cm);
+ return;
+ }
+ commandDispatcher.processMotion(cm, vim, {
+ type: 'motion',
+ motion: 'findNext',
+ motionArgs: { forward: true, toJumplist: command.searchArgs.toJumplist }
+ });
+ }
+ function onPromptClose(query) {
+ cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
+ handleQuery(query, true /** ignoreCase */, true /** smartCase */);
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isRecording) {
+ logSearchQuery(macroModeState, query);
+ }
+ }
+ function onPromptKeyUp(e, query, close) {
+ var keyName = CodeMirror.keyName(e), up, offset;
+ if (keyName == 'Up' || keyName == 'Down') {
+ up = keyName == 'Up' ? true : false;
+ offset = e.target ? e.target.selectionEnd : 0;
+ query = vimGlobalState.searchHistoryController.nextMatch(query, up) || '';
+ close(query);
+ if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);
+ } else {
+ if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
+ vimGlobalState.searchHistoryController.reset();
+ }
+ var parsedQuery;
+ try {
+ parsedQuery = updateSearchQuery(cm, query,
+ true /** ignoreCase */, true /** smartCase */);
+ } catch (e) {
+ // Swallow bad regexes for incremental search.
+ }
+ if (parsedQuery) {
+ cm.scrollIntoView(findNext(cm, !forward, parsedQuery), 30);
+ } else {
+ clearSearchHighlight(cm);
+ cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
+ }
+ }
+ function onPromptKeyDown(e, query, close) {
+ var keyName = CodeMirror.keyName(e);
+ if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||
+ (keyName == 'Backspace' && query == '')) {
+ vimGlobalState.searchHistoryController.pushInput(query);
+ vimGlobalState.searchHistoryController.reset();
+ updateSearchQuery(cm, originalQuery);
+ clearSearchHighlight(cm);
+ cm.scrollTo(originalScrollPos.left, originalScrollPos.top);
+ CodeMirror.e_stop(e);
+ clearInputState(cm);
+ close();
+ cm.focus();
+ } else if (keyName == 'Up' || keyName == 'Down') {
+ CodeMirror.e_stop(e);
+ } else if (keyName == 'Ctrl-U') {
+ // Ctrl-U clears input.
+ CodeMirror.e_stop(e);
+ close('');
+ }
+ }
+ switch (command.searchArgs.querySrc) {
+ case 'prompt':
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isPlaying) {
+ var query = macroModeState.replaySearchQueries.shift();
+ handleQuery(query, true /** ignoreCase */, false /** smartCase */);
+ } else {
+ showPrompt(cm, {
+ onClose: onPromptClose,
+ prefix: promptPrefix,
+ desc: searchPromptDesc,
+ onKeyUp: onPromptKeyUp,
+ onKeyDown: onPromptKeyDown
+ });
+ }
+ break;
+ case 'wordUnderCursor':
+ var word = expandWordUnderCursor(cm, false /** inclusive */,
+ true /** forward */, false /** bigWord */,
+ true /** noSymbol */);
+ var isKeyword = true;
+ if (!word) {
+ word = expandWordUnderCursor(cm, false /** inclusive */,
+ true /** forward */, false /** bigWord */,
+ false /** noSymbol */);
+ isKeyword = false;
+ }
+ if (!word) {
+ return;
+ }
+ var query = cm.getLine(word.start.line).substring(word.start.ch,
+ word.end.ch);
+ if (isKeyword && wholeWordOnly) {
+ query = '\\b' + query + '\\b';
+ } else {
+ query = escapeRegex(query);
+ }
+
+ // cachedCursor is used to save the old position of the cursor
+ // when * or # causes vim to seek for the nearest word and shift
+ // the cursor before entering the motion.
+ vimGlobalState.jumpList.cachedCursor = cm.getCursor();
+ cm.setCursor(word.start);
+
+ handleQuery(query, true /** ignoreCase */, false /** smartCase */);
+ break;
+ }
+ },
+ processEx: function(cm, vim, command) {
+ function onPromptClose(input) {
+ // Give the prompt some time to close so that if processCommand shows
+ // an error, the elements don't overlap.
+ vimGlobalState.exCommandHistoryController.pushInput(input);
+ vimGlobalState.exCommandHistoryController.reset();
+ exCommandDispatcher.processCommand(cm, input);
+ }
+ function onPromptKeyDown(e, input, close) {
+ var keyName = CodeMirror.keyName(e), up, offset;
+ if (keyName == 'Esc' || keyName == 'Ctrl-C' || keyName == 'Ctrl-[' ||
+ (keyName == 'Backspace' && input == '')) {
+ vimGlobalState.exCommandHistoryController.pushInput(input);
+ vimGlobalState.exCommandHistoryController.reset();
+ CodeMirror.e_stop(e);
+ clearInputState(cm);
+ close();
+ cm.focus();
+ }
+ if (keyName == 'Up' || keyName == 'Down') {
+ CodeMirror.e_stop(e);
+ up = keyName == 'Up' ? true : false;
+ offset = e.target ? e.target.selectionEnd : 0;
+ input = vimGlobalState.exCommandHistoryController.nextMatch(input, up) || '';
+ close(input);
+ if (offset && e.target) e.target.selectionEnd = e.target.selectionStart = Math.min(offset, e.target.value.length);
+ } else if (keyName == 'Ctrl-U') {
+ // Ctrl-U clears input.
+ CodeMirror.e_stop(e);
+ close('');
+ } else {
+ if ( keyName != 'Left' && keyName != 'Right' && keyName != 'Ctrl' && keyName != 'Alt' && keyName != 'Shift')
+ vimGlobalState.exCommandHistoryController.reset();
+ }
+ }
+ if (command.type == 'keyToEx') {
+ // Handle user defined Ex to Ex mappings
+ exCommandDispatcher.processCommand(cm, command.exArgs.input);
+ } else {
+ if (vim.visualMode) {
+ showPrompt(cm, { onClose: onPromptClose, prefix: ':', value: '\'<,\'>',
+ onKeyDown: onPromptKeyDown, selectValueOnOpen: false});
+ } else {
+ showPrompt(cm, { onClose: onPromptClose, prefix: ':',
+ onKeyDown: onPromptKeyDown});
+ }
+ }
+ },
+ evalInput: function(cm, vim) {
+ // If the motion command is set, execute both the operator and motion.
+ // Otherwise return.
+ var inputState = vim.inputState;
+ var motion = inputState.motion;
+ var motionArgs = inputState.motionArgs || {};
+ var operator = inputState.operator;
+ var operatorArgs = inputState.operatorArgs || {};
+ var registerName = inputState.registerName;
+ var sel = vim.sel;
+ // TODO: Make sure cm and vim selections are identical outside visual mode.
+ var origHead = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.head): cm.getCursor('head'));
+ var origAnchor = copyCursor(vim.visualMode ? clipCursorToContent(cm, sel.anchor) : cm.getCursor('anchor'));
+ var oldHead = copyCursor(origHead);
+ var oldAnchor = copyCursor(origAnchor);
+ var newHead, newAnchor;
+ var repeat;
+ if (operator) {
+ this.recordLastEdit(vim, inputState);
+ }
+ if (inputState.repeatOverride !== undefined) {
+ // If repeatOverride is specified, that takes precedence over the
+ // input state's repeat. Used by Ex mode and can be user defined.
+ repeat = inputState.repeatOverride;
+ } else {
+ repeat = inputState.getRepeat();
+ }
+ if (repeat > 0 && motionArgs.explicitRepeat) {
+ motionArgs.repeatIsExplicit = true;
+ } else if (motionArgs.noRepeat ||
+ (!motionArgs.explicitRepeat && repeat === 0)) {
+ repeat = 1;
+ motionArgs.repeatIsExplicit = false;
+ }
+ if (inputState.selectedCharacter) {
+ // If there is a character input, stick it in all of the arg arrays.
+ motionArgs.selectedCharacter = operatorArgs.selectedCharacter =
+ inputState.selectedCharacter;
+ }
+ motionArgs.repeat = repeat;
+ clearInputState(cm);
+ if (motion) {
+ var motionResult = motions[motion](cm, origHead, motionArgs, vim);
+ vim.lastMotion = motions[motion];
+ if (!motionResult) {
+ return;
+ }
+ if (motionArgs.toJumplist) {
+ var jumpList = vimGlobalState.jumpList;
+ // if the current motion is # or *, use cachedCursor
+ var cachedCursor = jumpList.cachedCursor;
+ if (cachedCursor) {
+ recordJumpPosition(cm, cachedCursor, motionResult);
+ delete jumpList.cachedCursor;
+ } else {
+ recordJumpPosition(cm, origHead, motionResult);
+ }
+ }
+ if (motionResult instanceof Array) {
+ newAnchor = motionResult[0];
+ newHead = motionResult[1];
+ } else {
+ newHead = motionResult;
+ }
+ // TODO: Handle null returns from motion commands better.
+ if (!newHead) {
+ newHead = copyCursor(origHead);
+ }
+ if (vim.visualMode) {
+ if (!(vim.visualBlock && newHead.ch === Infinity)) {
+ newHead = clipCursorToContent(cm, newHead, vim.visualBlock);
+ }
+ if (newAnchor) {
+ newAnchor = clipCursorToContent(cm, newAnchor, true);
+ }
+ newAnchor = newAnchor || oldAnchor;
+ sel.anchor = newAnchor;
+ sel.head = newHead;
+ updateCmSelection(cm);
+ updateMark(cm, vim, '<',
+ cursorIsBefore(newAnchor, newHead) ? newAnchor
+ : newHead);
+ updateMark(cm, vim, '>',
+ cursorIsBefore(newAnchor, newHead) ? newHead
+ : newAnchor);
+ } else if (!operator) {
+ newHead = clipCursorToContent(cm, newHead);
+ cm.setCursor(newHead.line, newHead.ch);
+ }
+ }
+ if (operator) {
+ if (operatorArgs.lastSel) {
+ // Replaying a visual mode operation
+ newAnchor = oldAnchor;
+ var lastSel = operatorArgs.lastSel;
+ var lineOffset = Math.abs(lastSel.head.line - lastSel.anchor.line);
+ var chOffset = Math.abs(lastSel.head.ch - lastSel.anchor.ch);
+ if (lastSel.visualLine) {
+ // Linewise Visual mode: The same number of lines.
+ newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
+ } else if (lastSel.visualBlock) {
+ // Blockwise Visual mode: The same number of lines and columns.
+ newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch + chOffset);
+ } else if (lastSel.head.line == lastSel.anchor.line) {
+ // Normal Visual mode within one line: The same number of characters.
+ newHead = Pos(oldAnchor.line, oldAnchor.ch + chOffset);
+ } else {
+ // Normal Visual mode with several lines: The same number of lines, in the
+ // last line the same number of characters as in the last line the last time.
+ newHead = Pos(oldAnchor.line + lineOffset, oldAnchor.ch);
+ }
+ vim.visualMode = true;
+ vim.visualLine = lastSel.visualLine;
+ vim.visualBlock = lastSel.visualBlock;
+ sel = vim.sel = {
+ anchor: newAnchor,
+ head: newHead
+ };
+ updateCmSelection(cm);
+ } else if (vim.visualMode) {
+ operatorArgs.lastSel = {
+ anchor: copyCursor(sel.anchor),
+ head: copyCursor(sel.head),
+ visualBlock: vim.visualBlock,
+ visualLine: vim.visualLine
+ };
+ }
+ var curStart, curEnd, linewise, mode;
+ var cmSel;
+ if (vim.visualMode) {
+ // Init visual op
+ curStart = cursorMin(sel.head, sel.anchor);
+ curEnd = cursorMax(sel.head, sel.anchor);
+ linewise = vim.visualLine || operatorArgs.linewise;
+ mode = vim.visualBlock ? 'block' :
+ linewise ? 'line' :
+ 'char';
+ cmSel = makeCmSelection(cm, {
+ anchor: curStart,
+ head: curEnd
+ }, mode);
+ if (linewise) {
+ var ranges = cmSel.ranges;
+ if (mode == 'block') {
+ // Linewise operators in visual block mode extend to end of line
+ for (var i = 0; i < ranges.length; i++) {
+ ranges[i].head.ch = lineLength(cm, ranges[i].head.line);
+ }
+ } else if (mode == 'line') {
+ ranges[0].head = Pos(ranges[0].head.line + 1, 0);
+ }
+ }
+ } else {
+ // Init motion op
+ curStart = copyCursor(newAnchor || oldAnchor);
+ curEnd = copyCursor(newHead || oldHead);
+ if (cursorIsBefore(curEnd, curStart)) {
+ var tmp = curStart;
+ curStart = curEnd;
+ curEnd = tmp;
+ }
+ linewise = motionArgs.linewise || operatorArgs.linewise;
+ if (linewise) {
+ // Expand selection to entire line.
+ expandSelectionToLine(cm, curStart, curEnd);
+ } else if (motionArgs.forward) {
+ // Clip to trailing newlines only if the motion goes forward.
+ clipToLine(cm, curStart, curEnd);
+ }
+ mode = 'char';
+ var exclusive = !motionArgs.inclusive || linewise;
+ cmSel = makeCmSelection(cm, {
+ anchor: curStart,
+ head: curEnd
+ }, mode, exclusive);
+ }
+ cm.setSelections(cmSel.ranges, cmSel.primary);
+ vim.lastMotion = null;
+ operatorArgs.repeat = repeat; // For indent in visual mode.
+ operatorArgs.registerName = registerName;
+ // Keep track of linewise as it affects how paste and change behave.
+ operatorArgs.linewise = linewise;
+ var operatorMoveTo = operators[operator](
+ cm, operatorArgs, cmSel.ranges, oldAnchor, newHead);
+ if (vim.visualMode) {
+ exitVisualMode(cm, operatorMoveTo != null);
+ }
+ if (operatorMoveTo) {
+ cm.setCursor(operatorMoveTo);
+ }
+ }
+ },
+ recordLastEdit: function(vim, inputState, actionCommand) {
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isPlaying) { return; }
+ vim.lastEditInputState = inputState;
+ vim.lastEditActionCommand = actionCommand;
+ macroModeState.lastInsertModeChanges.changes = [];
+ macroModeState.lastInsertModeChanges.expectCursorActivityForChange = false;
+ macroModeState.lastInsertModeChanges.visualBlock = vim.visualBlock ? vim.sel.head.line - vim.sel.anchor.line : 0;
+ }
+ };
+
+ /**
+ * typedef {Object{line:number,ch:number}} Cursor An object containing the
+ * position of the cursor.
+ */
+ // All of the functions below return Cursor objects.
+ var motions = {
+ moveToTopLine: function(cm, _head, motionArgs) {
+ var line = getUserVisibleLines(cm).top + motionArgs.repeat -1;
+ return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
+ },
+ moveToMiddleLine: function(cm) {
+ var range = getUserVisibleLines(cm);
+ var line = Math.floor((range.top + range.bottom) * 0.5);
+ return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
+ },
+ moveToBottomLine: function(cm, _head, motionArgs) {
+ var line = getUserVisibleLines(cm).bottom - motionArgs.repeat +1;
+ return Pos(line, findFirstNonWhiteSpaceCharacter(cm.getLine(line)));
+ },
+ expandToLine: function(_cm, head, motionArgs) {
+ // Expands forward to end of line, and then to next line if repeat is
+ // >1. Does not handle backward motion!
+ var cur = head;
+ return Pos(cur.line + motionArgs.repeat - 1, Infinity);
+ },
+ findNext: function(cm, _head, motionArgs) {
+ var state = getSearchState(cm);
+ var query = state.getQuery();
+ if (!query) {
+ return;
+ }
+ var prev = !motionArgs.forward;
+ // If search is initiated with ? instead of /, negate direction.
+ prev = (state.isReversed()) ? !prev : prev;
+ highlightSearchMatches(cm, query);
+ return findNext(cm, prev/** prev */, query, motionArgs.repeat);
+ },
+ goToMark: function(cm, _head, motionArgs, vim) {
+ var pos = getMarkPos(cm, vim, motionArgs.selectedCharacter);
+ if (pos) {
+ return motionArgs.linewise ? { line: pos.line, ch: findFirstNonWhiteSpaceCharacter(cm.getLine(pos.line)) } : pos;
+ }
+ return null;
+ },
+ moveToOtherHighlightedEnd: function(cm, _head, motionArgs, vim) {
+ if (vim.visualBlock && motionArgs.sameLine) {
+ var sel = vim.sel;
+ return [
+ clipCursorToContent(cm, Pos(sel.anchor.line, sel.head.ch)),
+ clipCursorToContent(cm, Pos(sel.head.line, sel.anchor.ch))
+ ];
+ } else {
+ return ([vim.sel.head, vim.sel.anchor]);
+ }
+ },
+ jumpToMark: function(cm, head, motionArgs, vim) {
+ var best = head;
+ for (var i = 0; i < motionArgs.repeat; i++) {
+ var cursor = best;
+ for (var key in vim.marks) {
+ if (!isLowerCase(key)) {
+ continue;
+ }
+ var mark = vim.marks[key].find();
+ var isWrongDirection = (motionArgs.forward) ?
+ cursorIsBefore(mark, cursor) : cursorIsBefore(cursor, mark);
+
+ if (isWrongDirection) {
+ continue;
+ }
+ if (motionArgs.linewise && (mark.line == cursor.line)) {
+ continue;
+ }
+
+ var equal = cursorEqual(cursor, best);
+ var between = (motionArgs.forward) ?
+ cursorIsBetween(cursor, mark, best) :
+ cursorIsBetween(best, mark, cursor);
+
+ if (equal || between) {
+ best = mark;
+ }
+ }
+ }
+
+ if (motionArgs.linewise) {
+ // Vim places the cursor on the first non-whitespace character of
+ // the line if there is one, else it places the cursor at the end
+ // of the line, regardless of whether a mark was found.
+ best = Pos(best.line, findFirstNonWhiteSpaceCharacter(cm.getLine(best.line)));
+ }
+ return best;
+ },
+ moveByCharacters: function(_cm, head, motionArgs) {
+ var cur = head;
+ var repeat = motionArgs.repeat;
+ var ch = motionArgs.forward ? cur.ch + repeat : cur.ch - repeat;
+ return Pos(cur.line, ch);
+ },
+ moveByLines: function(cm, head, motionArgs, vim) {
+ var cur = head;
+ var endCh = cur.ch;
+ // Depending what our last motion was, we may want to do different
+ // things. If our last motion was moving vertically, we want to
+ // preserve the HPos from our last horizontal move. If our last motion
+ // was going to the end of a line, moving vertically we should go to
+ // the end of the line, etc.
+ switch (vim.lastMotion) {
+ case this.moveByLines:
+ case this.moveByDisplayLines:
+ case this.moveByScroll:
+ case this.moveToColumn:
+ case this.moveToEol:
+ endCh = vim.lastHPos;
+ break;
+ default:
+ vim.lastHPos = endCh;
+ }
+ var repeat = motionArgs.repeat+(motionArgs.repeatOffset||0);
+ var line = motionArgs.forward ? cur.line + repeat : cur.line - repeat;
+ var first = cm.firstLine();
+ var last = cm.lastLine();
+ // Vim go to line begin or line end when cursor at first/last line and
+ // move to previous/next line is triggered.
+ if (line < first && cur.line == first){
+ return this.moveToStartOfLine(cm, head, motionArgs, vim);
+ }else if (line > last && cur.line == last){
+ return this.moveToEol(cm, head, motionArgs, vim, true);
+ }
+ if (motionArgs.toFirstChar){
+ endCh=findFirstNonWhiteSpaceCharacter(cm.getLine(line));
+ vim.lastHPos = endCh;
+ }
+ vim.lastHSPos = cm.charCoords(Pos(line, endCh),'div').left;
+ return Pos(line, endCh);
+ },
+ moveByDisplayLines: function(cm, head, motionArgs, vim) {
+ var cur = head;
+ switch (vim.lastMotion) {
+ case this.moveByDisplayLines:
+ case this.moveByScroll:
+ case this.moveByLines:
+ case this.moveToColumn:
+ case this.moveToEol:
+ break;
+ default:
+ vim.lastHSPos = cm.charCoords(cur,'div').left;
+ }
+ var repeat = motionArgs.repeat;
+ var res=cm.findPosV(cur,(motionArgs.forward ? repeat : -repeat),'line',vim.lastHSPos);
+ if (res.hitSide) {
+ if (motionArgs.forward) {
+ var lastCharCoords = cm.charCoords(res, 'div');
+ var goalCoords = { top: lastCharCoords.top + 8, left: vim.lastHSPos };
+ var res = cm.coordsChar(goalCoords, 'div');
+ } else {
+ var resCoords = cm.charCoords(Pos(cm.firstLine(), 0), 'div');
+ resCoords.left = vim.lastHSPos;
+ res = cm.coordsChar(resCoords, 'div');
+ }
+ }
+ vim.lastHPos = res.ch;
+ return res;
+ },
+ moveByPage: function(cm, head, motionArgs) {
+ // CodeMirror only exposes functions that move the cursor page down, so
+ // doing this bad hack to move the cursor and move it back. evalInput
+ // will move the cursor to where it should be in the end.
+ var curStart = head;
+ var repeat = motionArgs.repeat;
+ return cm.findPosV(curStart, (motionArgs.forward ? repeat : -repeat), 'page');
+ },
+ moveByParagraph: function(cm, head, motionArgs) {
+ var dir = motionArgs.forward ? 1 : -1;
+ return findParagraph(cm, head, motionArgs.repeat, dir);
+ },
+ moveBySentence: function(cm, head, motionArgs) {
+ var dir = motionArgs.forward ? 1 : -1;
+ return findSentence(cm, head, motionArgs.repeat, dir);
+ },
+ moveByScroll: function(cm, head, motionArgs, vim) {
+ var scrollbox = cm.getScrollInfo();
+ var curEnd = null;
+ var repeat = motionArgs.repeat;
+ if (!repeat) {
+ repeat = scrollbox.clientHeight / (2 * cm.defaultTextHeight());
+ }
+ var orig = cm.charCoords(head, 'local');
+ motionArgs.repeat = repeat;
+ var curEnd = motions.moveByDisplayLines(cm, head, motionArgs, vim);
+ if (!curEnd) {
+ return null;
+ }
+ var dest = cm.charCoords(curEnd, 'local');
+ cm.scrollTo(null, scrollbox.top + dest.top - orig.top);
+ return curEnd;
+ },
+ moveByWords: function(cm, head, motionArgs) {
+ return moveToWord(cm, head, motionArgs.repeat, !!motionArgs.forward,
+ !!motionArgs.wordEnd, !!motionArgs.bigWord);
+ },
+ moveTillCharacter: function(cm, _head, motionArgs) {
+ var repeat = motionArgs.repeat;
+ var curEnd = moveToCharacter(cm, repeat, motionArgs.forward,
+ motionArgs.selectedCharacter);
+ var increment = motionArgs.forward ? -1 : 1;
+ recordLastCharacterSearch(increment, motionArgs);
+ if (!curEnd) return null;
+ curEnd.ch += increment;
+ return curEnd;
+ },
+ moveToCharacter: function(cm, head, motionArgs) {
+ var repeat = motionArgs.repeat;
+ recordLastCharacterSearch(0, motionArgs);
+ return moveToCharacter(cm, repeat, motionArgs.forward,
+ motionArgs.selectedCharacter) || head;
+ },
+ moveToSymbol: function(cm, head, motionArgs) {
+ var repeat = motionArgs.repeat;
+ return findSymbol(cm, repeat, motionArgs.forward,
+ motionArgs.selectedCharacter) || head;
+ },
+ moveToColumn: function(cm, head, motionArgs, vim) {
+ var repeat = motionArgs.repeat;
+ // repeat is equivalent to which column we want to move to!
+ vim.lastHPos = repeat - 1;
+ vim.lastHSPos = cm.charCoords(head,'div').left;
+ return moveToColumn(cm, repeat);
+ },
+ moveToEol: function(cm, head, motionArgs, vim, keepHPos) {
+ var cur = head;
+ var retval= Pos(cur.line + motionArgs.repeat - 1, Infinity);
+ var end=cm.clipPos(retval);
+ end.ch--;
+ if (!keepHPos) {
+ vim.lastHPos = Infinity;
+ vim.lastHSPos = cm.charCoords(end,'div').left;
+ }
+ return retval;
+ },
+ moveToFirstNonWhiteSpaceCharacter: function(cm, head) {
+ // Go to the start of the line where the text begins, or the end for
+ // whitespace-only lines
+ var cursor = head;
+ return Pos(cursor.line,
+ findFirstNonWhiteSpaceCharacter(cm.getLine(cursor.line)));
+ },
+ moveToMatchedSymbol: function(cm, head) {
+ var cursor = head;
+ var line = cursor.line;
+ var ch = cursor.ch;
+ var lineText = cm.getLine(line);
+ var symbol;
+ for (; ch < lineText.length; ch++) {
+ symbol = lineText.charAt(ch);
+ if (symbol && isMatchableSymbol(symbol)) {
+ var style = cm.getTokenTypeAt(Pos(line, ch + 1));
+ if (style !== "string" && style !== "comment") {
+ break;
+ }
+ }
+ }
+ if (ch < lineText.length) {
+ // Only include angle brackets in analysis if they are being matched.
+ var re = (ch === '<' || ch === '>') ? /[(){}[\]<>]/ : /[(){}[\]]/;
+ var matched = cm.findMatchingBracket(Pos(line, ch), {bracketRegex: re});
+ return matched.to;
+ } else {
+ return cursor;
+ }
+ },
+ moveToStartOfLine: function(_cm, head) {
+ return Pos(head.line, 0);
+ },
+ moveToLineOrEdgeOfDocument: function(cm, _head, motionArgs) {
+ var lineNum = motionArgs.forward ? cm.lastLine() : cm.firstLine();
+ if (motionArgs.repeatIsExplicit) {
+ lineNum = motionArgs.repeat - cm.getOption('firstLineNumber');
+ }
+ return Pos(lineNum,
+ findFirstNonWhiteSpaceCharacter(cm.getLine(lineNum)));
+ },
+ textObjectManipulation: function(cm, head, motionArgs, vim) {
+ // TODO: lots of possible exceptions that can be thrown here. Try da(
+ // outside of a () block.
+ var mirroredPairs = {'(': ')', ')': '(',
+ '{': '}', '}': '{',
+ '[': ']', ']': '[',
+ '<': '>', '>': '<'};
+ var selfPaired = {'\'': true, '"': true, '`': true};
+
+ var character = motionArgs.selectedCharacter;
+ // 'b' refers to '()' block.
+ // 'B' refers to '{}' block.
+ if (character == 'b') {
+ character = '(';
+ } else if (character == 'B') {
+ character = '{';
+ }
+
+ // Inclusive is the difference between a and i
+ // TODO: Instead of using the additional text object map to perform text
+ // object operations, merge the map into the defaultKeyMap and use
+ // motionArgs to define behavior. Define separate entries for 'aw',
+ // 'iw', 'a[', 'i[', etc.
+ var inclusive = !motionArgs.textObjectInner;
+
+ var tmp;
+ if (mirroredPairs[character]) {
+ tmp = selectCompanionObject(cm, head, character, inclusive);
+ } else if (selfPaired[character]) {
+ tmp = findBeginningAndEnd(cm, head, character, inclusive);
+ } else if (character === 'W') {
+ tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
+ true /** bigWord */);
+ } else if (character === 'w') {
+ tmp = expandWordUnderCursor(cm, inclusive, true /** forward */,
+ false /** bigWord */);
+ } else if (character === 'p') {
+ tmp = findParagraph(cm, head, motionArgs.repeat, 0, inclusive);
+ motionArgs.linewise = true;
+ if (vim.visualMode) {
+ if (!vim.visualLine) { vim.visualLine = true; }
+ } else {
+ var operatorArgs = vim.inputState.operatorArgs;
+ if (operatorArgs) { operatorArgs.linewise = true; }
+ tmp.end.line--;
+ }
+ } else {
+ // No text object defined for this, don't move.
+ return null;
+ }
+
+ if (!cm.state.vim.visualMode) {
+ return [tmp.start, tmp.end];
+ } else {
+ return expandSelection(cm, tmp.start, tmp.end);
+ }
+ },
+
+ repeatLastCharacterSearch: function(cm, head, motionArgs) {
+ var lastSearch = vimGlobalState.lastCharacterSearch;
+ var repeat = motionArgs.repeat;
+ var forward = motionArgs.forward === lastSearch.forward;
+ var increment = (lastSearch.increment ? 1 : 0) * (forward ? -1 : 1);
+ cm.moveH(-increment, 'char');
+ motionArgs.inclusive = forward ? true : false;
+ var curEnd = moveToCharacter(cm, repeat, forward, lastSearch.selectedCharacter);
+ if (!curEnd) {
+ cm.moveH(increment, 'char');
+ return head;
+ }
+ curEnd.ch += increment;
+ return curEnd;
+ }
+ };
+
+ function defineMotion(name, fn) {
+ motions[name] = fn;
+ }
+
+ function fillArray(val, times) {
+ var arr = [];
+ for (var i = 0; i < times; i++) {
+ arr.push(val);
+ }
+ return arr;
+ }
+ /**
+ * An operator acts on a text selection. It receives the list of selections
+ * as input. The corresponding CodeMirror selection is guaranteed to
+ * match the input selection.
+ */
+ var operators = {
+ change: function(cm, args, ranges) {
+ var finalHead, text;
+ var vim = cm.state.vim;
+ if (!vim.visualMode) {
+ var anchor = ranges[0].anchor,
+ head = ranges[0].head;
+ text = cm.getRange(anchor, head);
+ var lastState = vim.lastEditInputState || {};
+ if (lastState.motion == "moveByWords" && !isWhiteSpaceString(text)) {
+ // Exclude trailing whitespace if the range is not all whitespace.
+ var match = (/\s+$/).exec(text);
+ if (match && lastState.motionArgs && lastState.motionArgs.forward) {
+ head = offsetCursor(head, 0, - match[0].length);
+ text = text.slice(0, - match[0].length);
+ }
+ }
+ var prevLineEnd = new Pos(anchor.line - 1, Number.MAX_VALUE);
+ var wasLastLine = cm.firstLine() == cm.lastLine();
+ if (head.line > cm.lastLine() && args.linewise && !wasLastLine) {
+ cm.replaceRange('', prevLineEnd, head);
+ } else {
+ cm.replaceRange('', anchor, head);
+ }
+ if (args.linewise) {
+ // Push the next line back down, if there is a next line.
+ if (!wasLastLine) {
+ cm.setCursor(prevLineEnd);
+ CodeMirror.commands.newlineAndIndent(cm);
+ }
+ // make sure cursor ends up at the end of the line.
+ anchor.ch = Number.MAX_VALUE;
+ }
+ finalHead = anchor;
+ } else {
+ text = cm.getSelection();
+ var replacement = fillArray('', ranges.length);
+ cm.replaceSelections(replacement);
+ finalHead = cursorMin(ranges[0].head, ranges[0].anchor);
+ }
+ vimGlobalState.registerController.pushText(
+ args.registerName, 'change', text,
+ args.linewise, ranges.length > 1);
+ actions.enterInsertMode(cm, {head: finalHead}, cm.state.vim);
+ },
+ // delete is a javascript keyword.
+ 'delete': function(cm, args, ranges) {
+ var finalHead, text;
+ var vim = cm.state.vim;
+ if (!vim.visualBlock) {
+ var anchor = ranges[0].anchor,
+ head = ranges[0].head;
+ if (args.linewise &&
+ head.line != cm.firstLine() &&
+ anchor.line == cm.lastLine() &&
+ anchor.line == head.line - 1) {
+ // Special case for dd on last line (and first line).
+ if (anchor.line == cm.firstLine()) {
+ anchor.ch = 0;
+ } else {
+ anchor = Pos(anchor.line - 1, lineLength(cm, anchor.line - 1));
+ }
+ }
+ text = cm.getRange(anchor, head);
+ cm.replaceRange('', anchor, head);
+ finalHead = anchor;
+ if (args.linewise) {
+ finalHead = motions.moveToFirstNonWhiteSpaceCharacter(cm, anchor);
+ }
+ } else {
+ text = cm.getSelection();
+ var replacement = fillArray('', ranges.length);
+ cm.replaceSelections(replacement);
+ finalHead = ranges[0].anchor;
+ }
+ vimGlobalState.registerController.pushText(
+ args.registerName, 'delete', text,
+ args.linewise, vim.visualBlock);
+ var includeLineBreak = vim.insertMode
+ return clipCursorToContent(cm, finalHead, includeLineBreak);
+ },
+ indent: function(cm, args, ranges) {
+ var vim = cm.state.vim;
+ var startLine = ranges[0].anchor.line;
+ var endLine = vim.visualBlock ?
+ ranges[ranges.length - 1].anchor.line :
+ ranges[0].head.line;
+ // In visual mode, n> shifts the selection right n times, instead of
+ // shifting n lines right once.
+ var repeat = (vim.visualMode) ? args.repeat : 1;
+ if (args.linewise) {
+ // The only way to delete a newline is to delete until the start of
+ // the next line, so in linewise mode evalInput will include the next
+ // line. We don't want this in indent, so we go back a line.
+ endLine--;
+ }
+ for (var i = startLine; i <= endLine; i++) {
+ for (var j = 0; j < repeat; j++) {
+ cm.indentLine(i, args.indentRight);
+ }
+ }
+ return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
+ },
+ indentAuto: function(cm, _args, ranges) {
+ cm.execCommand("indentAuto");
+ return motions.moveToFirstNonWhiteSpaceCharacter(cm, ranges[0].anchor);
+ },
+ changeCase: function(cm, args, ranges, oldAnchor, newHead) {
+ var selections = cm.getSelections();
+ var swapped = [];
+ var toLower = args.toLower;
+ for (var j = 0; j < selections.length; j++) {
+ var toSwap = selections[j];
+ var text = '';
+ if (toLower === true) {
+ text = toSwap.toLowerCase();
+ } else if (toLower === false) {
+ text = toSwap.toUpperCase();
+ } else {
+ for (var i = 0; i < toSwap.length; i++) {
+ var character = toSwap.charAt(i);
+ text += isUpperCase(character) ? character.toLowerCase() :
+ character.toUpperCase();
+ }
+ }
+ swapped.push(text);
+ }
+ cm.replaceSelections(swapped);
+ if (args.shouldMoveCursor){
+ return newHead;
+ } else if (!cm.state.vim.visualMode && args.linewise && ranges[0].anchor.line + 1 == ranges[0].head.line) {
+ return motions.moveToFirstNonWhiteSpaceCharacter(cm, oldAnchor);
+ } else if (args.linewise){
+ return oldAnchor;
+ } else {
+ return cursorMin(ranges[0].anchor, ranges[0].head);
+ }
+ },
+ yank: function(cm, args, ranges, oldAnchor) {
+ var vim = cm.state.vim;
+ var text = cm.getSelection();
+ var endPos = vim.visualMode
+ ? cursorMin(vim.sel.anchor, vim.sel.head, ranges[0].head, ranges[0].anchor)
+ : oldAnchor;
+ vimGlobalState.registerController.pushText(
+ args.registerName, 'yank',
+ text, args.linewise, vim.visualBlock);
+ return endPos;
+ }
+ };
+
+ function defineOperator(name, fn) {
+ operators[name] = fn;
+ }
+
+ var actions = {
+ jumpListWalk: function(cm, actionArgs, vim) {
+ if (vim.visualMode) {
+ return;
+ }
+ var repeat = actionArgs.repeat;
+ var forward = actionArgs.forward;
+ var jumpList = vimGlobalState.jumpList;
+
+ var mark = jumpList.move(cm, forward ? repeat : -repeat);
+ var markPos = mark ? mark.find() : undefined;
+ markPos = markPos ? markPos : cm.getCursor();
+ cm.setCursor(markPos);
+ },
+ scroll: function(cm, actionArgs, vim) {
+ if (vim.visualMode) {
+ return;
+ }
+ var repeat = actionArgs.repeat || 1;
+ var lineHeight = cm.defaultTextHeight();
+ var top = cm.getScrollInfo().top;
+ var delta = lineHeight * repeat;
+ var newPos = actionArgs.forward ? top + delta : top - delta;
+ var cursor = copyCursor(cm.getCursor());
+ var cursorCoords = cm.charCoords(cursor, 'local');
+ if (actionArgs.forward) {
+ if (newPos > cursorCoords.top) {
+ cursor.line += (newPos - cursorCoords.top) / lineHeight;
+ cursor.line = Math.ceil(cursor.line);
+ cm.setCursor(cursor);
+ cursorCoords = cm.charCoords(cursor, 'local');
+ cm.scrollTo(null, cursorCoords.top);
+ } else {
+ // Cursor stays within bounds. Just reposition the scroll window.
+ cm.scrollTo(null, newPos);
+ }
+ } else {
+ var newBottom = newPos + cm.getScrollInfo().clientHeight;
+ if (newBottom < cursorCoords.bottom) {
+ cursor.line -= (cursorCoords.bottom - newBottom) / lineHeight;
+ cursor.line = Math.floor(cursor.line);
+ cm.setCursor(cursor);
+ cursorCoords = cm.charCoords(cursor, 'local');
+ cm.scrollTo(
+ null, cursorCoords.bottom - cm.getScrollInfo().clientHeight);
+ } else {
+ // Cursor stays within bounds. Just reposition the scroll window.
+ cm.scrollTo(null, newPos);
+ }
+ }
+ },
+ scrollToCursor: function(cm, actionArgs) {
+ var lineNum = cm.getCursor().line;
+ var charCoords = cm.charCoords(Pos(lineNum, 0), 'local');
+ var height = cm.getScrollInfo().clientHeight;
+ var y = charCoords.top;
+ var lineHeight = charCoords.bottom - y;
+ switch (actionArgs.position) {
+ case 'center': y = y - (height / 2) + lineHeight;
+ break;
+ case 'bottom': y = y - height + lineHeight;
+ break;
+ }
+ cm.scrollTo(null, y);
+ },
+ replayMacro: function(cm, actionArgs, vim) {
+ var registerName = actionArgs.selectedCharacter;
+ var repeat = actionArgs.repeat;
+ var macroModeState = vimGlobalState.macroModeState;
+ if (registerName == '@') {
+ registerName = macroModeState.latestRegister;
+ } else {
+ macroModeState.latestRegister = registerName;
+ }
+ while(repeat--){
+ executeMacroRegister(cm, vim, macroModeState, registerName);
+ }
+ },
+ enterMacroRecordMode: function(cm, actionArgs) {
+ var macroModeState = vimGlobalState.macroModeState;
+ var registerName = actionArgs.selectedCharacter;
+ if (vimGlobalState.registerController.isValidRegister(registerName)) {
+ macroModeState.enterMacroRecordMode(cm, registerName);
+ }
+ },
+ toggleOverwrite: function(cm) {
+ if (!cm.state.overwrite) {
+ cm.toggleOverwrite(true);
+ cm.setOption('keyMap', 'vim-replace');
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});
+ } else {
+ cm.toggleOverwrite(false);
+ cm.setOption('keyMap', 'vim-insert');
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
+ }
+ },
+ enterInsertMode: function(cm, actionArgs, vim) {
+ if (cm.getOption('readOnly')) { return; }
+ vim.insertMode = true;
+ vim.insertModeRepeat = actionArgs && actionArgs.repeat || 1;
+ var insertAt = (actionArgs) ? actionArgs.insertAt : null;
+ var sel = vim.sel;
+ var head = actionArgs.head || cm.getCursor('head');
+ var height = cm.listSelections().length;
+ if (insertAt == 'eol') {
+ head = Pos(head.line, lineLength(cm, head.line));
+ } else if (insertAt == 'charAfter') {
+ head = offsetCursor(head, 0, 1);
+ } else if (insertAt == 'firstNonBlank') {
+ head = motions.moveToFirstNonWhiteSpaceCharacter(cm, head);
+ } else if (insertAt == 'startOfSelectedArea') {
+ if (!vim.visualMode)
+ return;
+ if (!vim.visualBlock) {
+ if (sel.head.line < sel.anchor.line) {
+ head = sel.head;
+ } else {
+ head = Pos(sel.anchor.line, 0);
+ }
+ } else {
+ head = Pos(
+ Math.min(sel.head.line, sel.anchor.line),
+ Math.min(sel.head.ch, sel.anchor.ch));
+ height = Math.abs(sel.head.line - sel.anchor.line) + 1;
+ }
+ } else if (insertAt == 'endOfSelectedArea') {
+ if (!vim.visualMode)
+ return;
+ if (!vim.visualBlock) {
+ if (sel.head.line >= sel.anchor.line) {
+ head = offsetCursor(sel.head, 0, 1);
+ } else {
+ head = Pos(sel.anchor.line, 0);
+ }
+ } else {
+ head = Pos(
+ Math.min(sel.head.line, sel.anchor.line),
+ Math.max(sel.head.ch + 1, sel.anchor.ch));
+ height = Math.abs(sel.head.line - sel.anchor.line) + 1;
+ }
+ } else if (insertAt == 'inplace') {
+ if (vim.visualMode){
+ return;
+ }
+ }
+ cm.setOption('disableInput', false);
+ if (actionArgs && actionArgs.replace) {
+ // Handle Replace-mode as a special case of insert mode.
+ cm.toggleOverwrite(true);
+ cm.setOption('keyMap', 'vim-replace');
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "replace"});
+ } else {
+ cm.toggleOverwrite(false);
+ cm.setOption('keyMap', 'vim-insert');
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "insert"});
+ }
+ if (!vimGlobalState.macroModeState.isPlaying) {
+ // Only record if not replaying.
+ cm.on('change', onChange);
+ CodeMirror.on(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
+ }
+ if (vim.visualMode) {
+ exitVisualMode(cm);
+ }
+ selectForInsert(cm, head, height);
+ },
+ toggleVisualMode: function(cm, actionArgs, vim) {
+ var repeat = actionArgs.repeat;
+ var anchor = cm.getCursor();
+ var head;
+ // TODO: The repeat should actually select number of characters/lines
+ // equal to the repeat times the size of the previous visual
+ // operation.
+ if (!vim.visualMode) {
+ // Entering visual mode
+ vim.visualMode = true;
+ vim.visualLine = !!actionArgs.linewise;
+ vim.visualBlock = !!actionArgs.blockwise;
+ head = clipCursorToContent(
+ cm, Pos(anchor.line, anchor.ch + repeat - 1),
+ true /** includeLineBreak */);
+ vim.sel = {
+ anchor: anchor,
+ head: head
+ };
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
+ updateCmSelection(cm);
+ updateMark(cm, vim, '<', cursorMin(anchor, head));
+ updateMark(cm, vim, '>', cursorMax(anchor, head));
+ } else if (vim.visualLine ^ actionArgs.linewise ||
+ vim.visualBlock ^ actionArgs.blockwise) {
+ // Toggling between modes
+ vim.visualLine = !!actionArgs.linewise;
+ vim.visualBlock = !!actionArgs.blockwise;
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual", subMode: vim.visualLine ? "linewise" : vim.visualBlock ? "blockwise" : ""});
+ updateCmSelection(cm);
+ } else {
+ exitVisualMode(cm);
+ }
+ },
+ reselectLastSelection: function(cm, _actionArgs, vim) {
+ var lastSelection = vim.lastSelection;
+ if (vim.visualMode) {
+ updateLastSelection(cm, vim);
+ }
+ if (lastSelection) {
+ var anchor = lastSelection.anchorMark.find();
+ var head = lastSelection.headMark.find();
+ if (!anchor || !head) {
+ // If the marks have been destroyed due to edits, do nothing.
+ return;
+ }
+ vim.sel = {
+ anchor: anchor,
+ head: head
+ };
+ vim.visualMode = true;
+ vim.visualLine = lastSelection.visualLine;
+ vim.visualBlock = lastSelection.visualBlock;
+ updateCmSelection(cm);
+ updateMark(cm, vim, '<', cursorMin(anchor, head));
+ updateMark(cm, vim, '>', cursorMax(anchor, head));
+ CodeMirror.signal(cm, 'vim-mode-change', {
+ mode: 'visual',
+ subMode: vim.visualLine ? 'linewise' :
+ vim.visualBlock ? 'blockwise' : ''});
+ }
+ },
+ joinLines: function(cm, actionArgs, vim) {
+ var curStart, curEnd;
+ if (vim.visualMode) {
+ curStart = cm.getCursor('anchor');
+ curEnd = cm.getCursor('head');
+ if (cursorIsBefore(curEnd, curStart)) {
+ var tmp = curEnd;
+ curEnd = curStart;
+ curStart = tmp;
+ }
+ curEnd.ch = lineLength(cm, curEnd.line) - 1;
+ } else {
+ // Repeat is the number of lines to join. Minimum 2 lines.
+ var repeat = Math.max(actionArgs.repeat, 2);
+ curStart = cm.getCursor();
+ curEnd = clipCursorToContent(cm, Pos(curStart.line + repeat - 1,
+ Infinity));
+ }
+ var finalCh = 0;
+ for (var i = curStart.line; i < curEnd.line; i++) {
+ finalCh = lineLength(cm, curStart.line);
+ var tmp = Pos(curStart.line + 1,
+ lineLength(cm, curStart.line + 1));
+ var text = cm.getRange(curStart, tmp);
+ text = text.replace(/\n\s*/g, ' ');
+ cm.replaceRange(text, curStart, tmp);
+ }
+ var curFinalPos = Pos(curStart.line, finalCh);
+ if (vim.visualMode) {
+ exitVisualMode(cm, false);
+ }
+ cm.setCursor(curFinalPos);
+ },
+ newLineAndEnterInsertMode: function(cm, actionArgs, vim) {
+ vim.insertMode = true;
+ var insertAt = copyCursor(cm.getCursor());
+ if (insertAt.line === cm.firstLine() && !actionArgs.after) {
+ // Special case for inserting newline before start of document.
+ cm.replaceRange('\n', Pos(cm.firstLine(), 0));
+ cm.setCursor(cm.firstLine(), 0);
+ } else {
+ insertAt.line = (actionArgs.after) ? insertAt.line :
+ insertAt.line - 1;
+ insertAt.ch = lineLength(cm, insertAt.line);
+ cm.setCursor(insertAt);
+ var newlineFn = CodeMirror.commands.newlineAndIndentContinueComment ||
+ CodeMirror.commands.newlineAndIndent;
+ newlineFn(cm);
+ }
+ this.enterInsertMode(cm, { repeat: actionArgs.repeat }, vim);
+ },
+ paste: function(cm, actionArgs, vim) {
+ var cur = copyCursor(cm.getCursor());
+ var register = vimGlobalState.registerController.getRegister(
+ actionArgs.registerName);
+ var text = register.toString();
+ if (!text) {
+ return;
+ }
+ if (actionArgs.matchIndent) {
+ var tabSize = cm.getOption("tabSize");
+ // length that considers tabs and tabSize
+ var whitespaceLength = function(str) {
+ var tabs = (str.split("\t").length - 1);
+ var spaces = (str.split(" ").length - 1);
+ return tabs * tabSize + spaces * 1;
+ };
+ var currentLine = cm.getLine(cm.getCursor().line);
+ var indent = whitespaceLength(currentLine.match(/^\s*/)[0]);
+ // chomp last newline b/c don't want it to match /^\s*/gm
+ var chompedText = text.replace(/\n$/, '');
+ var wasChomped = text !== chompedText;
+ var firstIndent = whitespaceLength(text.match(/^\s*/)[0]);
+ var text = chompedText.replace(/^\s*/gm, function(wspace) {
+ var newIndent = indent + (whitespaceLength(wspace) - firstIndent);
+ if (newIndent < 0) {
+ return "";
+ }
+ else if (cm.getOption("indentWithTabs")) {
+ var quotient = Math.floor(newIndent / tabSize);
+ return Array(quotient + 1).join('\t');
+ }
+ else {
+ return Array(newIndent + 1).join(' ');
+ }
+ });
+ text += wasChomped ? "\n" : "";
+ }
+ if (actionArgs.repeat > 1) {
+ var text = Array(actionArgs.repeat + 1).join(text);
+ }
+ var linewise = register.linewise;
+ var blockwise = register.blockwise;
+ if (blockwise) {
+ text = text.split('\n');
+ if (linewise) {
+ text.pop();
+ }
+ for (var i = 0; i < text.length; i++) {
+ text[i] = (text[i] == '') ? ' ' : text[i];
+ }
+ cur.ch += actionArgs.after ? 1 : 0;
+ cur.ch = Math.min(lineLength(cm, cur.line), cur.ch);
+ } else if (linewise) {
+ if(vim.visualMode) {
+ text = vim.visualLine ? text.slice(0, -1) : '\n' + text.slice(0, text.length - 1) + '\n';
+ } else if (actionArgs.after) {
+ // Move the newline at the end to the start instead, and paste just
+ // before the newline character of the line we are on right now.
+ text = '\n' + text.slice(0, text.length - 1);
+ cur.ch = lineLength(cm, cur.line);
+ } else {
+ cur.ch = 0;
+ }
+ } else {
+ cur.ch += actionArgs.after ? 1 : 0;
+ }
+ var curPosFinal;
+ var idx;
+ if (vim.visualMode) {
+ // save the pasted text for reselection if the need arises
+ vim.lastPastedText = text;
+ var lastSelectionCurEnd;
+ var selectedArea = getSelectedAreaRange(cm, vim);
+ var selectionStart = selectedArea[0];
+ var selectionEnd = selectedArea[1];
+ var selectedText = cm.getSelection();
+ var selections = cm.listSelections();
+ var emptyStrings = new Array(selections.length).join('1').split('1');
+ // save the curEnd marker before it get cleared due to cm.replaceRange.
+ if (vim.lastSelection) {
+ lastSelectionCurEnd = vim.lastSelection.headMark.find();
+ }
+ // push the previously selected text to unnamed register
+ vimGlobalState.registerController.unnamedRegister.setText(selectedText);
+ if (blockwise) {
+ // first delete the selected text
+ cm.replaceSelections(emptyStrings);
+ // Set new selections as per the block length of the yanked text
+ selectionEnd = Pos(selectionStart.line + text.length-1, selectionStart.ch);
+ cm.setCursor(selectionStart);
+ selectBlock(cm, selectionEnd);
+ cm.replaceSelections(text);
+ curPosFinal = selectionStart;
+ } else if (vim.visualBlock) {
+ cm.replaceSelections(emptyStrings);
+ cm.setCursor(selectionStart);
+ cm.replaceRange(text, selectionStart, selectionStart);
+ curPosFinal = selectionStart;
+ } else {
+ cm.replaceRange(text, selectionStart, selectionEnd);
+ curPosFinal = cm.posFromIndex(cm.indexFromPos(selectionStart) + text.length - 1);
+ }
+ // restore the the curEnd marker
+ if(lastSelectionCurEnd) {
+ vim.lastSelection.headMark = cm.setBookmark(lastSelectionCurEnd);
+ }
+ if (linewise) {
+ curPosFinal.ch=0;
+ }
+ } else {
+ if (blockwise) {
+ cm.setCursor(cur);
+ for (var i = 0; i < text.length; i++) {
+ var line = cur.line+i;
+ if (line > cm.lastLine()) {
+ cm.replaceRange('\n', Pos(line, 0));
+ }
+ var lastCh = lineLength(cm, line);
+ if (lastCh < cur.ch) {
+ extendLineToColumn(cm, line, cur.ch);
+ }
+ }
+ cm.setCursor(cur);
+ selectBlock(cm, Pos(cur.line + text.length-1, cur.ch));
+ cm.replaceSelections(text);
+ curPosFinal = cur;
+ } else {
+ cm.replaceRange(text, cur);
+ // Now fine tune the cursor to where we want it.
+ if (linewise && actionArgs.after) {
+ curPosFinal = Pos(
+ cur.line + 1,
+ findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line + 1)));
+ } else if (linewise && !actionArgs.after) {
+ curPosFinal = Pos(
+ cur.line,
+ findFirstNonWhiteSpaceCharacter(cm.getLine(cur.line)));
+ } else if (!linewise && actionArgs.after) {
+ idx = cm.indexFromPos(cur);
+ curPosFinal = cm.posFromIndex(idx + text.length - 1);
+ } else {
+ idx = cm.indexFromPos(cur);
+ curPosFinal = cm.posFromIndex(idx + text.length);
+ }
+ }
+ }
+ if (vim.visualMode) {
+ exitVisualMode(cm, false);
+ }
+ cm.setCursor(curPosFinal);
+ },
+ undo: function(cm, actionArgs) {
+ cm.operation(function() {
+ repeatFn(cm, CodeMirror.commands.undo, actionArgs.repeat)();
+ cm.setCursor(cm.getCursor('anchor'));
+ });
+ },
+ redo: function(cm, actionArgs) {
+ repeatFn(cm, CodeMirror.commands.redo, actionArgs.repeat)();
+ },
+ setRegister: function(_cm, actionArgs, vim) {
+ vim.inputState.registerName = actionArgs.selectedCharacter;
+ },
+ setMark: function(cm, actionArgs, vim) {
+ var markName = actionArgs.selectedCharacter;
+ updateMark(cm, vim, markName, cm.getCursor());
+ },
+ replace: function(cm, actionArgs, vim) {
+ var replaceWith = actionArgs.selectedCharacter;
+ var curStart = cm.getCursor();
+ var replaceTo;
+ var curEnd;
+ var selections = cm.listSelections();
+ if (vim.visualMode) {
+ curStart = cm.getCursor('start');
+ curEnd = cm.getCursor('end');
+ } else {
+ var line = cm.getLine(curStart.line);
+ replaceTo = curStart.ch + actionArgs.repeat;
+ if (replaceTo > line.length) {
+ replaceTo=line.length;
+ }
+ curEnd = Pos(curStart.line, replaceTo);
+ }
+ if (replaceWith=='\n') {
+ if (!vim.visualMode) cm.replaceRange('', curStart, curEnd);
+ // special case, where vim help says to replace by just one line-break
+ (CodeMirror.commands.newlineAndIndentContinueComment || CodeMirror.commands.newlineAndIndent)(cm);
+ } else {
+ var replaceWithStr = cm.getRange(curStart, curEnd);
+ //replace all characters in range by selected, but keep linebreaks
+ replaceWithStr = replaceWithStr.replace(/[^\n]/g, replaceWith);
+ if (vim.visualBlock) {
+ // Tabs are split in visua block before replacing
+ var spaces = new Array(cm.getOption("tabSize")+1).join(' ');
+ replaceWithStr = cm.getSelection();
+ replaceWithStr = replaceWithStr.replace(/\t/g, spaces).replace(/[^\n]/g, replaceWith).split('\n');
+ cm.replaceSelections(replaceWithStr);
+ } else {
+ cm.replaceRange(replaceWithStr, curStart, curEnd);
+ }
+ if (vim.visualMode) {
+ curStart = cursorIsBefore(selections[0].anchor, selections[0].head) ?
+ selections[0].anchor : selections[0].head;
+ cm.setCursor(curStart);
+ exitVisualMode(cm, false);
+ } else {
+ cm.setCursor(offsetCursor(curEnd, 0, -1));
+ }
+ }
+ },
+ incrementNumberToken: function(cm, actionArgs) {
+ var cur = cm.getCursor();
+ var lineStr = cm.getLine(cur.line);
+ var re = /(-?)(?:(0x)([\da-f]+)|(0b|0|)(\d+))/gi;
+ var match;
+ var start;
+ var end;
+ var numberStr;
+ while ((match = re.exec(lineStr)) !== null) {
+ start = match.index;
+ end = start + match[0].length;
+ if (cur.ch < end)break;
+ }
+ if (!actionArgs.backtrack && (end <= cur.ch))return;
+ if (match) {
+ var baseStr = match[2] || match[4]
+ var digits = match[3] || match[5]
+ var increment = actionArgs.increase ? 1 : -1;
+ var base = {'0b': 2, '0': 8, '': 10, '0x': 16}[baseStr.toLowerCase()];
+ var number = parseInt(match[1] + digits, base) + (increment * actionArgs.repeat);
+ numberStr = number.toString(base);
+ var zeroPadding = baseStr ? new Array(digits.length - numberStr.length + 1 + match[1].length).join('0') : ''
+ if (numberStr.charAt(0) === '-') {
+ numberStr = '-' + baseStr + zeroPadding + numberStr.substr(1);
+ } else {
+ numberStr = baseStr + zeroPadding + numberStr;
+ }
+ var from = Pos(cur.line, start);
+ var to = Pos(cur.line, end);
+ cm.replaceRange(numberStr, from, to);
+ } else {
+ return;
+ }
+ cm.setCursor(Pos(cur.line, start + numberStr.length - 1));
+ },
+ repeatLastEdit: function(cm, actionArgs, vim) {
+ var lastEditInputState = vim.lastEditInputState;
+ if (!lastEditInputState) { return; }
+ var repeat = actionArgs.repeat;
+ if (repeat && actionArgs.repeatIsExplicit) {
+ vim.lastEditInputState.repeatOverride = repeat;
+ } else {
+ repeat = vim.lastEditInputState.repeatOverride || repeat;
+ }
+ repeatLastEdit(cm, vim, repeat, false /** repeatForInsert */);
+ },
+ indent: function(cm, actionArgs) {
+ cm.indentLine(cm.getCursor().line, actionArgs.indentRight);
+ },
+ exitInsertMode: exitInsertMode
+ };
+
+ function defineAction(name, fn) {
+ actions[name] = fn;
+ }
+
+ /*
+ * Below are miscellaneous utility functions used by vim.js
+ */
+
+ /**
+ * Clips cursor to ensure that line is within the buffer's range
+ * If includeLineBreak is true, then allow cur.ch == lineLength.
+ */
+ function clipCursorToContent(cm, cur, includeLineBreak) {
+ var line = Math.min(Math.max(cm.firstLine(), cur.line), cm.lastLine() );
+ var maxCh = lineLength(cm, line) - 1;
+ maxCh = (includeLineBreak) ? maxCh + 1 : maxCh;
+ var ch = Math.min(Math.max(0, cur.ch), maxCh);
+ return Pos(line, ch);
+ }
+ function copyArgs(args) {
+ var ret = {};
+ for (var prop in args) {
+ if (args.hasOwnProperty(prop)) {
+ ret[prop] = args[prop];
+ }
+ }
+ return ret;
+ }
+ function offsetCursor(cur, offsetLine, offsetCh) {
+ if (typeof offsetLine === 'object') {
+ offsetCh = offsetLine.ch;
+ offsetLine = offsetLine.line;
+ }
+ return Pos(cur.line + offsetLine, cur.ch + offsetCh);
+ }
+ function commandMatches(keys, keyMap, context, inputState) {
+ // Partial matches are not applied. They inform the key handler
+ // that the current key sequence is a subsequence of a valid key
+ // sequence, so that the key buffer is not cleared.
+ var match, partial = [], full = [];
+ for (var i = 0; i < keyMap.length; i++) {
+ var command = keyMap[i];
+ if (context == 'insert' && command.context != 'insert' ||
+ command.context && command.context != context ||
+ inputState.operator && command.type == 'action' ||
+ !(match = commandMatch(keys, command.keys))) { continue; }
+ if (match == 'partial') { partial.push(command); }
+ if (match == 'full') { full.push(command); }
+ }
+ return {
+ partial: partial.length && partial,
+ full: full.length && full
+ };
+ }
+ function commandMatch(pressed, mapped) {
+ if (mapped.slice(-11) == '<character>') {
+ // Last character matches anything.
+ var prefixLen = mapped.length - 11;
+ var pressedPrefix = pressed.slice(0, prefixLen);
+ var mappedPrefix = mapped.slice(0, prefixLen);
+ return pressedPrefix == mappedPrefix && pressed.length > prefixLen ? 'full' :
+ mappedPrefix.indexOf(pressedPrefix) == 0 ? 'partial' : false;
+ } else {
+ return pressed == mapped ? 'full' :
+ mapped.indexOf(pressed) == 0 ? 'partial' : false;
+ }
+ }
+ function lastChar(keys) {
+ var match = /^.*(<[^>]+>)$/.exec(keys);
+ var selectedCharacter = match ? match[1] : keys.slice(-1);
+ if (selectedCharacter.length > 1){
+ switch(selectedCharacter){
+ case '<CR>':
+ selectedCharacter='\n';
+ break;
+ case '<Space>':
+ selectedCharacter=' ';
+ break;
+ default:
+ selectedCharacter='';
+ break;
+ }
+ }
+ return selectedCharacter;
+ }
+ function repeatFn(cm, fn, repeat) {
+ return function() {
+ for (var i = 0; i < repeat; i++) {
+ fn(cm);
+ }
+ };
+ }
+ function copyCursor(cur) {
+ return Pos(cur.line, cur.ch);
+ }
+ function cursorEqual(cur1, cur2) {
+ return cur1.ch == cur2.ch && cur1.line == cur2.line;
+ }
+ function cursorIsBefore(cur1, cur2) {
+ if (cur1.line < cur2.line) {
+ return true;
+ }
+ if (cur1.line == cur2.line && cur1.ch < cur2.ch) {
+ return true;
+ }
+ return false;
+ }
+ function cursorMin(cur1, cur2) {
+ if (arguments.length > 2) {
+ cur2 = cursorMin.apply(undefined, Array.prototype.slice.call(arguments, 1));
+ }
+ return cursorIsBefore(cur1, cur2) ? cur1 : cur2;
+ }
+ function cursorMax(cur1, cur2) {
+ if (arguments.length > 2) {
+ cur2 = cursorMax.apply(undefined, Array.prototype.slice.call(arguments, 1));
+ }
+ return cursorIsBefore(cur1, cur2) ? cur2 : cur1;
+ }
+ function cursorIsBetween(cur1, cur2, cur3) {
+ // returns true if cur2 is between cur1 and cur3.
+ var cur1before2 = cursorIsBefore(cur1, cur2);
+ var cur2before3 = cursorIsBefore(cur2, cur3);
+ return cur1before2 && cur2before3;
+ }
+ function lineLength(cm, lineNum) {
+ return cm.getLine(lineNum).length;
+ }
+ function trim(s) {
+ if (s.trim) {
+ return s.trim();
+ }
+ return s.replace(/^\s+|\s+$/g, '');
+ }
+ function escapeRegex(s) {
+ return s.replace(/([.?*+$\[\]\/\\(){}|\-])/g, '\\$1');
+ }
+ function extendLineToColumn(cm, lineNum, column) {
+ var endCh = lineLength(cm, lineNum);
+ var spaces = new Array(column-endCh+1).join(' ');
+ cm.setCursor(Pos(lineNum, endCh));
+ cm.replaceRange(spaces, cm.getCursor());
+ }
+ // This functions selects a rectangular block
+ // of text with selectionEnd as any of its corner
+ // Height of block:
+ // Difference in selectionEnd.line and first/last selection.line
+ // Width of the block:
+ // Distance between selectionEnd.ch and any(first considered here) selection.ch
+ function selectBlock(cm, selectionEnd) {
+ var selections = [], ranges = cm.listSelections();
+ var head = copyCursor(cm.clipPos(selectionEnd));
+ var isClipped = !cursorEqual(selectionEnd, head);
+ var curHead = cm.getCursor('head');
+ var primIndex = getIndex(ranges, curHead);
+ var wasClipped = cursorEqual(ranges[primIndex].head, ranges[primIndex].anchor);
+ var max = ranges.length - 1;
+ var index = max - primIndex > primIndex ? max : 0;
+ var base = ranges[index].anchor;
+
+ var firstLine = Math.min(base.line, head.line);
+ var lastLine = Math.max(base.line, head.line);
+ var baseCh = base.ch, headCh = head.ch;
+
+ var dir = ranges[index].head.ch - baseCh;
+ var newDir = headCh - baseCh;
+ if (dir > 0 && newDir <= 0) {
+ baseCh++;
+ if (!isClipped) { headCh--; }
+ } else if (dir < 0 && newDir >= 0) {
+ baseCh--;
+ if (!wasClipped) { headCh++; }
+ } else if (dir < 0 && newDir == -1) {
+ baseCh--;
+ headCh++;
+ }
+ for (var line = firstLine; line <= lastLine; line++) {
+ var range = {anchor: new Pos(line, baseCh), head: new Pos(line, headCh)};
+ selections.push(range);
+ }
+ cm.setSelections(selections);
+ selectionEnd.ch = headCh;
+ base.ch = baseCh;
+ return base;
+ }
+ function selectForInsert(cm, head, height) {
+ var sel = [];
+ for (var i = 0; i < height; i++) {
+ var lineHead = offsetCursor(head, i, 0);
+ sel.push({anchor: lineHead, head: lineHead});
+ }
+ cm.setSelections(sel, 0);
+ }
+ // getIndex returns the index of the cursor in the selections.
+ function getIndex(ranges, cursor, end) {
+ for (var i = 0; i < ranges.length; i++) {
+ var atAnchor = end != 'head' && cursorEqual(ranges[i].anchor, cursor);
+ var atHead = end != 'anchor' && cursorEqual(ranges[i].head, cursor);
+ if (atAnchor || atHead) {
+ return i;
+ }
+ }
+ return -1;
+ }
+ function getSelectedAreaRange(cm, vim) {
+ var lastSelection = vim.lastSelection;
+ var getCurrentSelectedAreaRange = function() {
+ var selections = cm.listSelections();
+ var start = selections[0];
+ var end = selections[selections.length-1];
+ var selectionStart = cursorIsBefore(start.anchor, start.head) ? start.anchor : start.head;
+ var selectionEnd = cursorIsBefore(end.anchor, end.head) ? end.head : end.anchor;
+ return [selectionStart, selectionEnd];
+ };
+ var getLastSelectedAreaRange = function() {
+ var selectionStart = cm.getCursor();
+ var selectionEnd = cm.getCursor();
+ var block = lastSelection.visualBlock;
+ if (block) {
+ var width = block.width;
+ var height = block.height;
+ selectionEnd = Pos(selectionStart.line + height, selectionStart.ch + width);
+ var selections = [];
+ // selectBlock creates a 'proper' rectangular block.
+ // We do not want that in all cases, so we manually set selections.
+ for (var i = selectionStart.line; i < selectionEnd.line; i++) {
+ var anchor = Pos(i, selectionStart.ch);
+ var head = Pos(i, selectionEnd.ch);
+ var range = {anchor: anchor, head: head};
+ selections.push(range);
+ }
+ cm.setSelections(selections);
+ } else {
+ var start = lastSelection.anchorMark.find();
+ var end = lastSelection.headMark.find();
+ var line = end.line - start.line;
+ var ch = end.ch - start.ch;
+ selectionEnd = {line: selectionEnd.line + line, ch: line ? selectionEnd.ch : ch + selectionEnd.ch};
+ if (lastSelection.visualLine) {
+ selectionStart = Pos(selectionStart.line, 0);
+ selectionEnd = Pos(selectionEnd.line, lineLength(cm, selectionEnd.line));
+ }
+ cm.setSelection(selectionStart, selectionEnd);
+ }
+ return [selectionStart, selectionEnd];
+ };
+ if (!vim.visualMode) {
+ // In case of replaying the action.
+ return getLastSelectedAreaRange();
+ } else {
+ return getCurrentSelectedAreaRange();
+ }
+ }
+ // Updates the previous selection with the current selection's values. This
+ // should only be called in visual mode.
+ function updateLastSelection(cm, vim) {
+ var anchor = vim.sel.anchor;
+ var head = vim.sel.head;
+ // To accommodate the effect of lastPastedText in the last selection
+ if (vim.lastPastedText) {
+ head = cm.posFromIndex(cm.indexFromPos(anchor) + vim.lastPastedText.length);
+ vim.lastPastedText = null;
+ }
+ vim.lastSelection = {'anchorMark': cm.setBookmark(anchor),
+ 'headMark': cm.setBookmark(head),
+ 'anchor': copyCursor(anchor),
+ 'head': copyCursor(head),
+ 'visualMode': vim.visualMode,
+ 'visualLine': vim.visualLine,
+ 'visualBlock': vim.visualBlock};
+ }
+ function expandSelection(cm, start, end) {
+ var sel = cm.state.vim.sel;
+ var head = sel.head;
+ var anchor = sel.anchor;
+ var tmp;
+ if (cursorIsBefore(end, start)) {
+ tmp = end;
+ end = start;
+ start = tmp;
+ }
+ if (cursorIsBefore(head, anchor)) {
+ head = cursorMin(start, head);
+ anchor = cursorMax(anchor, end);
+ } else {
+ anchor = cursorMin(start, anchor);
+ head = cursorMax(head, end);
+ head = offsetCursor(head, 0, -1);
+ if (head.ch == -1 && head.line != cm.firstLine()) {
+ head = Pos(head.line - 1, lineLength(cm, head.line - 1));
+ }
+ }
+ return [anchor, head];
+ }
+ /**
+ * Updates the CodeMirror selection to match the provided vim selection.
+ * If no arguments are given, it uses the current vim selection state.
+ */
+ function updateCmSelection(cm, sel, mode) {
+ var vim = cm.state.vim;
+ sel = sel || vim.sel;
+ var mode = mode ||
+ vim.visualLine ? 'line' : vim.visualBlock ? 'block' : 'char';
+ var cmSel = makeCmSelection(cm, sel, mode);
+ cm.setSelections(cmSel.ranges, cmSel.primary);
+ updateFakeCursor(cm);
+ }
+ function makeCmSelection(cm, sel, mode, exclusive) {
+ var head = copyCursor(sel.head);
+ var anchor = copyCursor(sel.anchor);
+ if (mode == 'char') {
+ var headOffset = !exclusive && !cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
+ var anchorOffset = cursorIsBefore(sel.head, sel.anchor) ? 1 : 0;
+ head = offsetCursor(sel.head, 0, headOffset);
+ anchor = offsetCursor(sel.anchor, 0, anchorOffset);
+ return {
+ ranges: [{anchor: anchor, head: head}],
+ primary: 0
+ };
+ } else if (mode == 'line') {
+ if (!cursorIsBefore(sel.head, sel.anchor)) {
+ anchor.ch = 0;
+
+ var lastLine = cm.lastLine();
+ if (head.line > lastLine) {
+ head.line = lastLine;
+ }
+ head.ch = lineLength(cm, head.line);
+ } else {
+ head.ch = 0;
+ anchor.ch = lineLength(cm, anchor.line);
+ }
+ return {
+ ranges: [{anchor: anchor, head: head}],
+ primary: 0
+ };
+ } else if (mode == 'block') {
+ var top = Math.min(anchor.line, head.line),
+ left = Math.min(anchor.ch, head.ch),
+ bottom = Math.max(anchor.line, head.line),
+ right = Math.max(anchor.ch, head.ch) + 1;
+ var height = bottom - top + 1;
+ var primary = head.line == top ? 0 : height - 1;
+ var ranges = [];
+ for (var i = 0; i < height; i++) {
+ ranges.push({
+ anchor: Pos(top + i, left),
+ head: Pos(top + i, right)
+ });
+ }
+ return {
+ ranges: ranges,
+ primary: primary
+ };
+ }
+ }
+ function getHead(cm) {
+ var cur = cm.getCursor('head');
+ if (cm.getSelection().length == 1) {
+ // Small corner case when only 1 character is selected. The "real"
+ // head is the left of head and anchor.
+ cur = cursorMin(cur, cm.getCursor('anchor'));
+ }
+ return cur;
+ }
+
+ /**
+ * If moveHead is set to false, the CodeMirror selection will not be
+ * touched. The caller assumes the responsibility of putting the cursor
+ * in the right place.
+ */
+ function exitVisualMode(cm, moveHead) {
+ var vim = cm.state.vim;
+ if (moveHead !== false) {
+ cm.setCursor(clipCursorToContent(cm, vim.sel.head));
+ }
+ updateLastSelection(cm, vim);
+ vim.visualMode = false;
+ vim.visualLine = false;
+ vim.visualBlock = false;
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
+ if (vim.fakeCursor) {
+ vim.fakeCursor.clear();
+ }
+ }
+
+ // Remove any trailing newlines from the selection. For
+ // example, with the caret at the start of the last word on the line,
+ // 'dw' should word, but not the newline, while 'w' should advance the
+ // caret to the first character of the next line.
+ function clipToLine(cm, curStart, curEnd) {
+ var selection = cm.getRange(curStart, curEnd);
+ // Only clip if the selection ends with trailing newline + whitespace
+ if (/\n\s*$/.test(selection)) {
+ var lines = selection.split('\n');
+ // We know this is all whitespace.
+ lines.pop();
+
+ // Cases:
+ // 1. Last word is an empty line - do not clip the trailing '\n'
+ // 2. Last word is not an empty line - clip the trailing '\n'
+ var line;
+ // Find the line containing the last word, and clip all whitespace up
+ // to it.
+ for (var line = lines.pop(); lines.length > 0 && line && isWhiteSpaceString(line); line = lines.pop()) {
+ curEnd.line--;
+ curEnd.ch = 0;
+ }
+ // If the last word is not an empty line, clip an additional newline
+ if (line) {
+ curEnd.line--;
+ curEnd.ch = lineLength(cm, curEnd.line);
+ } else {
+ curEnd.ch = 0;
+ }
+ }
+ }
+
+ // Expand the selection to line ends.
+ function expandSelectionToLine(_cm, curStart, curEnd) {
+ curStart.ch = 0;
+ curEnd.ch = 0;
+ curEnd.line++;
+ }
+
+ function findFirstNonWhiteSpaceCharacter(text) {
+ if (!text) {
+ return 0;
+ }
+ var firstNonWS = text.search(/\S/);
+ return firstNonWS == -1 ? text.length : firstNonWS;
+ }
+
+ function expandWordUnderCursor(cm, inclusive, _forward, bigWord, noSymbol) {
+ var cur = getHead(cm);
+ var line = cm.getLine(cur.line);
+ var idx = cur.ch;
+
+ // Seek to first word or non-whitespace character, depending on if
+ // noSymbol is true.
+ var test = noSymbol ? wordCharTest[0] : bigWordCharTest [0];
+ while (!test(line.charAt(idx))) {
+ idx++;
+ if (idx >= line.length) { return null; }
+ }
+
+ if (bigWord) {
+ test = bigWordCharTest[0];
+ } else {
+ test = wordCharTest[0];
+ if (!test(line.charAt(idx))) {
+ test = wordCharTest[1];
+ }
+ }
+
+ var end = idx, start = idx;
+ while (test(line.charAt(end)) && end < line.length) { end++; }
+ while (test(line.charAt(start)) && start >= 0) { start--; }
+ start++;
+
+ if (inclusive) {
+ // If present, include all whitespace after word.
+ // Otherwise, include all whitespace before word, except indentation.
+ var wordEnd = end;
+ while (/\s/.test(line.charAt(end)) && end < line.length) { end++; }
+ if (wordEnd == end) {
+ var wordStart = start;
+ while (/\s/.test(line.charAt(start - 1)) && start > 0) { start--; }
+ if (!start) { start = wordStart; }
+ }
+ }
+ return { start: Pos(cur.line, start), end: Pos(cur.line, end) };
+ }
+
+ function recordJumpPosition(cm, oldCur, newCur) {
+ if (!cursorEqual(oldCur, newCur)) {
+ vimGlobalState.jumpList.add(cm, oldCur, newCur);
+ }
+ }
+
+ function recordLastCharacterSearch(increment, args) {
+ vimGlobalState.lastCharacterSearch.increment = increment;
+ vimGlobalState.lastCharacterSearch.forward = args.forward;
+ vimGlobalState.lastCharacterSearch.selectedCharacter = args.selectedCharacter;
+ }
+
+ var symbolToMode = {
+ '(': 'bracket', ')': 'bracket', '{': 'bracket', '}': 'bracket',
+ '[': 'section', ']': 'section',
+ '*': 'comment', '/': 'comment',
+ 'm': 'method', 'M': 'method',
+ '#': 'preprocess'
+ };
+ var findSymbolModes = {
+ bracket: {
+ isComplete: function(state) {
+ if (state.nextCh === state.symb) {
+ state.depth++;
+ if (state.depth >= 1)return true;
+ } else if (state.nextCh === state.reverseSymb) {
+ state.depth--;
+ }
+ return false;
+ }
+ },
+ section: {
+ init: function(state) {
+ state.curMoveThrough = true;
+ state.symb = (state.forward ? ']' : '[') === state.symb ? '{' : '}';
+ },
+ isComplete: function(state) {
+ return state.index === 0 && state.nextCh === state.symb;
+ }
+ },
+ comment: {
+ isComplete: function(state) {
+ var found = state.lastCh === '*' && state.nextCh === '/';
+ state.lastCh = state.nextCh;
+ return found;
+ }
+ },
+ // TODO: The original Vim implementation only operates on level 1 and 2.
+ // The current implementation doesn't check for code block level and
+ // therefore it operates on any levels.
+ method: {
+ init: function(state) {
+ state.symb = (state.symb === 'm' ? '{' : '}');
+ state.reverseSymb = state.symb === '{' ? '}' : '{';
+ },
+ isComplete: function(state) {
+ if (state.nextCh === state.symb)return true;
+ return false;
+ }
+ },
+ preprocess: {
+ init: function(state) {
+ state.index = 0;
+ },
+ isComplete: function(state) {
+ if (state.nextCh === '#') {
+ var token = state.lineText.match(/#(\w+)/)[1];
+ if (token === 'endif') {
+ if (state.forward && state.depth === 0) {
+ return true;
+ }
+ state.depth++;
+ } else if (token === 'if') {
+ if (!state.forward && state.depth === 0) {
+ return true;
+ }
+ state.depth--;
+ }
+ if (token === 'else' && state.depth === 0)return true;
+ }
+ return false;
+ }
+ }
+ };
+ function findSymbol(cm, repeat, forward, symb) {
+ var cur = copyCursor(cm.getCursor());
+ var increment = forward ? 1 : -1;
+ var endLine = forward ? cm.lineCount() : -1;
+ var curCh = cur.ch;
+ var line = cur.line;
+ var lineText = cm.getLine(line);
+ var state = {
+ lineText: lineText,
+ nextCh: lineText.charAt(curCh),
+ lastCh: null,
+ index: curCh,
+ symb: symb,
+ reverseSymb: (forward ? { ')': '(', '}': '{' } : { '(': ')', '{': '}' })[symb],
+ forward: forward,
+ depth: 0,
+ curMoveThrough: false
+ };
+ var mode = symbolToMode[symb];
+ if (!mode)return cur;
+ var init = findSymbolModes[mode].init;
+ var isComplete = findSymbolModes[mode].isComplete;
+ if (init) { init(state); }
+ while (line !== endLine && repeat) {
+ state.index += increment;
+ state.nextCh = state.lineText.charAt(state.index);
+ if (!state.nextCh) {
+ line += increment;
+ state.lineText = cm.getLine(line) || '';
+ if (increment > 0) {
+ state.index = 0;
+ } else {
+ var lineLen = state.lineText.length;
+ state.index = (lineLen > 0) ? (lineLen-1) : 0;
+ }
+ state.nextCh = state.lineText.charAt(state.index);
+ }
+ if (isComplete(state)) {
+ cur.line = line;
+ cur.ch = state.index;
+ repeat--;
+ }
+ }
+ if (state.nextCh || state.curMoveThrough) {
+ return Pos(line, state.index);
+ }
+ return cur;
+ }
+
+ /*
+ * Returns the boundaries of the next word. If the cursor in the middle of
+ * the word, then returns the boundaries of the current word, starting at
+ * the cursor. If the cursor is at the start/end of a word, and we are going
+ * forward/backward, respectively, find the boundaries of the next word.
+ *
+ * @param {CodeMirror} cm CodeMirror object.
+ * @param {Cursor} cur The cursor position.
+ * @param {boolean} forward True to search forward. False to search
+ * backward.
+ * @param {boolean} bigWord True if punctuation count as part of the word.
+ * False if only [a-zA-Z0-9] characters count as part of the word.
+ * @param {boolean} emptyLineIsWord True if empty lines should be treated
+ * as words.
+ * @return {Object{from:number, to:number, line: number}} The boundaries of
+ * the word, or null if there are no more words.
+ */
+ function findWord(cm, cur, forward, bigWord, emptyLineIsWord) {
+ var lineNum = cur.line;
+ var pos = cur.ch;
+ var line = cm.getLine(lineNum);
+ var dir = forward ? 1 : -1;
+ var charTests = bigWord ? bigWordCharTest: wordCharTest;
+
+ if (emptyLineIsWord && line == '') {
+ lineNum += dir;
+ line = cm.getLine(lineNum);
+ if (!isLine(cm, lineNum)) {
+ return null;
+ }
+ pos = (forward) ? 0 : line.length;
+ }
+
+ while (true) {
+ if (emptyLineIsWord && line == '') {
+ return { from: 0, to: 0, line: lineNum };
+ }
+ var stop = (dir > 0) ? line.length : -1;
+ var wordStart = stop, wordEnd = stop;
+ // Find bounds of next word.
+ while (pos != stop) {
+ var foundWord = false;
+ for (var i = 0; i < charTests.length && !foundWord; ++i) {
+ if (charTests[i](line.charAt(pos))) {
+ wordStart = pos;
+ // Advance to end of word.
+ while (pos != stop && charTests[i](line.charAt(pos))) {
+ pos += dir;
+ }
+ wordEnd = pos;
+ foundWord = wordStart != wordEnd;
+ if (wordStart == cur.ch && lineNum == cur.line &&
+ wordEnd == wordStart + dir) {
+ // We started at the end of a word. Find the next one.
+ continue;
+ } else {
+ return {
+ from: Math.min(wordStart, wordEnd + 1),
+ to: Math.max(wordStart, wordEnd),
+ line: lineNum };
+ }
+ }
+ }
+ if (!foundWord) {
+ pos += dir;
+ }
+ }
+ // Advance to next/prev line.
+ lineNum += dir;
+ if (!isLine(cm, lineNum)) {
+ return null;
+ }
+ line = cm.getLine(lineNum);
+ pos = (dir > 0) ? 0 : line.length;
+ }
+ }
+
+ /**
+ * @param {CodeMirror} cm CodeMirror object.
+ * @param {Pos} cur The position to start from.
+ * @param {int} repeat Number of words to move past.
+ * @param {boolean} forward True to search forward. False to search
+ * backward.
+ * @param {boolean} wordEnd True to move to end of word. False to move to
+ * beginning of word.
+ * @param {boolean} bigWord True if punctuation count as part of the word.
+ * False if only alphabet characters count as part of the word.
+ * @return {Cursor} The position the cursor should move to.
+ */
+ function moveToWord(cm, cur, repeat, forward, wordEnd, bigWord) {
+ var curStart = copyCursor(cur);
+ var words = [];
+ if (forward && !wordEnd || !forward && wordEnd) {
+ repeat++;
+ }
+ // For 'e', empty lines are not considered words, go figure.
+ var emptyLineIsWord = !(forward && wordEnd);
+ for (var i = 0; i < repeat; i++) {
+ var word = findWord(cm, cur, forward, bigWord, emptyLineIsWord);
+ if (!word) {
+ var eodCh = lineLength(cm, cm.lastLine());
+ words.push(forward
+ ? {line: cm.lastLine(), from: eodCh, to: eodCh}
+ : {line: 0, from: 0, to: 0});
+ break;
+ }
+ words.push(word);
+ cur = Pos(word.line, forward ? (word.to - 1) : word.from);
+ }
+ var shortCircuit = words.length != repeat;
+ var firstWord = words[0];
+ var lastWord = words.pop();
+ if (forward && !wordEnd) {
+ // w
+ if (!shortCircuit && (firstWord.from != curStart.ch || firstWord.line != curStart.line)) {
+ // We did not start in the middle of a word. Discard the extra word at the end.
+ lastWord = words.pop();
+ }
+ return Pos(lastWord.line, lastWord.from);
+ } else if (forward && wordEnd) {
+ return Pos(lastWord.line, lastWord.to - 1);
+ } else if (!forward && wordEnd) {
+ // ge
+ if (!shortCircuit && (firstWord.to != curStart.ch || firstWord.line != curStart.line)) {
+ // We did not start in the middle of a word. Discard the extra word at the end.
+ lastWord = words.pop();
+ }
+ return Pos(lastWord.line, lastWord.to);
+ } else {
+ // b
+ return Pos(lastWord.line, lastWord.from);
+ }
+ }
+
+ function moveToCharacter(cm, repeat, forward, character) {
+ var cur = cm.getCursor();
+ var start = cur.ch;
+ var idx;
+ for (var i = 0; i < repeat; i ++) {
+ var line = cm.getLine(cur.line);
+ idx = charIdxInLine(start, line, character, forward, true);
+ if (idx == -1) {
+ return null;
+ }
+ start = idx;
+ }
+ return Pos(cm.getCursor().line, idx);
+ }
+
+ function moveToColumn(cm, repeat) {
+ // repeat is always >= 1, so repeat - 1 always corresponds
+ // to the column we want to go to.
+ var line = cm.getCursor().line;
+ return clipCursorToContent(cm, Pos(line, repeat - 1));
+ }
+
+ function updateMark(cm, vim, markName, pos) {
+ if (!inArray(markName, validMarks)) {
+ return;
+ }
+ if (vim.marks[markName]) {
+ vim.marks[markName].clear();
+ }
+ vim.marks[markName] = cm.setBookmark(pos);
+ }
+
+ function charIdxInLine(start, line, character, forward, includeChar) {
+ // Search for char in line.
+ // motion_options: {forward, includeChar}
+ // If includeChar = true, include it too.
+ // If forward = true, search forward, else search backwards.
+ // If char is not found on this line, do nothing
+ var idx;
+ if (forward) {
+ idx = line.indexOf(character, start + 1);
+ if (idx != -1 && !includeChar) {
+ idx -= 1;
+ }
+ } else {
+ idx = line.lastIndexOf(character, start - 1);
+ if (idx != -1 && !includeChar) {
+ idx += 1;
+ }
+ }
+ return idx;
+ }
+
+ function findParagraph(cm, head, repeat, dir, inclusive) {
+ var line = head.line;
+ var min = cm.firstLine();
+ var max = cm.lastLine();
+ var start, end, i = line;
+ function isEmpty(i) { return !cm.getLine(i); }
+ function isBoundary(i, dir, any) {
+ if (any) { return isEmpty(i) != isEmpty(i + dir); }
+ return !isEmpty(i) && isEmpty(i + dir);
+ }
+ if (dir) {
+ while (min <= i && i <= max && repeat > 0) {
+ if (isBoundary(i, dir)) { repeat--; }
+ i += dir;
+ }
+ return new Pos(i, 0);
+ }
+
+ var vim = cm.state.vim;
+ if (vim.visualLine && isBoundary(line, 1, true)) {
+ var anchor = vim.sel.anchor;
+ if (isBoundary(anchor.line, -1, true)) {
+ if (!inclusive || anchor.line != line) {
+ line += 1;
+ }
+ }
+ }
+ var startState = isEmpty(line);
+ for (i = line; i <= max && repeat; i++) {
+ if (isBoundary(i, 1, true)) {
+ if (!inclusive || isEmpty(i) != startState) {
+ repeat--;
+ }
+ }
+ }
+ end = new Pos(i, 0);
+ // select boundary before paragraph for the last one
+ if (i > max && !startState) { startState = true; }
+ else { inclusive = false; }
+ for (i = line; i > min; i--) {
+ if (!inclusive || isEmpty(i) == startState || i == line) {
+ if (isBoundary(i, -1, true)) { break; }
+ }
+ }
+ start = new Pos(i, 0);
+ return { start: start, end: end };
+ }
+
+ function findSentence(cm, cur, repeat, dir) {
+
+ /*
+ Takes an index object
+ {
+ line: the line string,
+ ln: line number,
+ pos: index in line,
+ dir: direction of traversal (-1 or 1)
+ }
+ and modifies the line, ln, and pos members to represent the
+ next valid position or sets them to null if there are
+ no more valid positions.
+ */
+ function nextChar(cm, idx) {
+ if (idx.pos + idx.dir < 0 || idx.pos + idx.dir >= idx.line.length) {
+ idx.ln += idx.dir;
+ if (!isLine(cm, idx.ln)) {
+ idx.line = null;
+ idx.ln = null;
+ idx.pos = null;
+ return;
+ }
+ idx.line = cm.getLine(idx.ln);
+ idx.pos = (idx.dir > 0) ? 0 : idx.line.length - 1;
+ }
+ else {
+ idx.pos += idx.dir;
+ }
+ }
+
+ /*
+ Performs one iteration of traversal in forward direction
+ Returns an index object of the new location
+ */
+ function forward(cm, ln, pos, dir) {
+ var line = cm.getLine(ln);
+ var stop = (line === "");
+
+ var curr = {
+ line: line,
+ ln: ln,
+ pos: pos,
+ dir: dir,
+ }
+
+ var last_valid = {
+ ln: curr.ln,
+ pos: curr.pos,
+ }
+
+ var skip_empty_lines = (curr.line === "");
+
+ // Move one step to skip character we start on
+ nextChar(cm, curr);
+
+ while (curr.line !== null) {
+ last_valid.ln = curr.ln;
+ last_valid.pos = curr.pos;
+
+ if (curr.line === "" && !skip_empty_lines) {
+ return { ln: curr.ln, pos: curr.pos, };
+ }
+ else if (stop && curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {
+ return { ln: curr.ln, pos: curr.pos, };
+ }
+ else if (isEndOfSentenceSymbol(curr.line[curr.pos])
+ && !stop
+ && (curr.pos === curr.line.length - 1
+ || isWhiteSpaceString(curr.line[curr.pos + 1]))) {
+ stop = true;
+ }
+
+ nextChar(cm, curr);
+ }
+
+ /*
+ Set the position to the last non whitespace character on the last
+ valid line in the case that we reach the end of the document.
+ */
+ var line = cm.getLine(last_valid.ln);
+ last_valid.pos = 0;
+ for(var i = line.length - 1; i >= 0; --i) {
+ if (!isWhiteSpaceString(line[i])) {
+ last_valid.pos = i;
+ break;
+ }
+ }
+
+ return last_valid;
+
+ }
+
+ /*
+ Performs one iteration of traversal in reverse direction
+ Returns an index object of the new location
+ */
+ function reverse(cm, ln, pos, dir) {
+ var line = cm.getLine(ln);
+
+ var curr = {
+ line: line,
+ ln: ln,
+ pos: pos,
+ dir: dir,
+ }
+
+ var last_valid = {
+ ln: curr.ln,
+ pos: null,
+ };
+
+ var skip_empty_lines = (curr.line === "");
+
+ // Move one step to skip character we start on
+ nextChar(cm, curr);
+
+ while (curr.line !== null) {
+
+ if (curr.line === "" && !skip_empty_lines) {
+ if (last_valid.pos !== null) {
+ return last_valid;
+ }
+ else {
+ return { ln: curr.ln, pos: curr.pos };
+ }
+ }
+ else if (isEndOfSentenceSymbol(curr.line[curr.pos])
+ && last_valid.pos !== null
+ && !(curr.ln === last_valid.ln && curr.pos + 1 === last_valid.pos)) {
+ return last_valid;
+ }
+ else if (curr.line !== "" && !isWhiteSpaceString(curr.line[curr.pos])) {
+ skip_empty_lines = false;
+ last_valid = { ln: curr.ln, pos: curr.pos }
+ }
+
+ nextChar(cm, curr);
+ }
+
+ /*
+ Set the position to the first non whitespace character on the last
+ valid line in the case that we reach the beginning of the document.
+ */
+ var line = cm.getLine(last_valid.ln);
+ last_valid.pos = 0;
+ for(var i = 0; i < line.length; ++i) {
+ if (!isWhiteSpaceString(line[i])) {
+ last_valid.pos = i;
+ break;
+ }
+ }
+ return last_valid;
+ }
+
+ var curr_index = {
+ ln: cur.line,
+ pos: cur.ch,
+ };
+
+ while (repeat > 0) {
+ if (dir < 0) {
+ curr_index = reverse(cm, curr_index.ln, curr_index.pos, dir);
+ }
+ else {
+ curr_index = forward(cm, curr_index.ln, curr_index.pos, dir);
+ }
+ repeat--;
+ }
+
+ return Pos(curr_index.ln, curr_index.pos);
+ }
+
+ // TODO: perhaps this finagling of start and end positions belonds
+ // in codemirror/replaceRange?
+ function selectCompanionObject(cm, head, symb, inclusive) {
+ var cur = head, start, end;
+
+ var bracketRegexp = ({
+ '(': /[()]/, ')': /[()]/,
+ '[': /[[\]]/, ']': /[[\]]/,
+ '{': /[{}]/, '}': /[{}]/,
+ '<': /[<>]/, '>': /[<>]/})[symb];
+ var openSym = ({
+ '(': '(', ')': '(',
+ '[': '[', ']': '[',
+ '{': '{', '}': '{',
+ '<': '<', '>': '<'})[symb];
+ var curChar = cm.getLine(cur.line).charAt(cur.ch);
+ // Due to the behavior of scanForBracket, we need to add an offset if the
+ // cursor is on a matching open bracket.
+ var offset = curChar === openSym ? 1 : 0;
+
+ start = cm.scanForBracket(Pos(cur.line, cur.ch + offset), -1, undefined, {'bracketRegex': bracketRegexp});
+ end = cm.scanForBracket(Pos(cur.line, cur.ch + offset), 1, undefined, {'bracketRegex': bracketRegexp});
+
+ if (!start || !end) {
+ return { start: cur, end: cur };
+ }
+
+ start = start.pos;
+ end = end.pos;
+
+ if ((start.line == end.line && start.ch > end.ch)
+ || (start.line > end.line)) {
+ var tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ if (inclusive) {
+ end.ch += 1;
+ } else {
+ start.ch += 1;
+ }
+
+ return { start: start, end: end };
+ }
+
+ // Takes in a symbol and a cursor and tries to simulate text objects that
+ // have identical opening and closing symbols
+ // TODO support across multiple lines
+ function findBeginningAndEnd(cm, head, symb, inclusive) {
+ var cur = copyCursor(head);
+ var line = cm.getLine(cur.line);
+ var chars = line.split('');
+ var start, end, i, len;
+ var firstIndex = chars.indexOf(symb);
+
+ // the decision tree is to always look backwards for the beginning first,
+ // but if the cursor is in front of the first instance of the symb,
+ // then move the cursor forward
+ if (cur.ch < firstIndex) {
+ cur.ch = firstIndex;
+ // Why is this line even here???
+ // cm.setCursor(cur.line, firstIndex+1);
+ }
+ // otherwise if the cursor is currently on the closing symbol
+ else if (firstIndex < cur.ch && chars[cur.ch] == symb) {
+ end = cur.ch; // assign end to the current cursor
+ --cur.ch; // make sure to look backwards
+ }
+
+ // if we're currently on the symbol, we've got a start
+ if (chars[cur.ch] == symb && !end) {
+ start = cur.ch + 1; // assign start to ahead of the cursor
+ } else {
+ // go backwards to find the start
+ for (i = cur.ch; i > -1 && !start; i--) {
+ if (chars[i] == symb) {
+ start = i + 1;
+ }
+ }
+ }
+
+ // look forwards for the end symbol
+ if (start && !end) {
+ for (i = start, len = chars.length; i < len && !end; i++) {
+ if (chars[i] == symb) {
+ end = i;
+ }
+ }
+ }
+
+ // nothing found
+ if (!start || !end) {
+ return { start: cur, end: cur };
+ }
+
+ // include the symbols
+ if (inclusive) {
+ --start; ++end;
+ }
+
+ return {
+ start: Pos(cur.line, start),
+ end: Pos(cur.line, end)
+ };
+ }
+
+ // Search functions
+ defineOption('pcre', true, 'boolean');
+ function SearchState() {}
+ SearchState.prototype = {
+ getQuery: function() {
+ return vimGlobalState.query;
+ },
+ setQuery: function(query) {
+ vimGlobalState.query = query;
+ },
+ getOverlay: function() {
+ return this.searchOverlay;
+ },
+ setOverlay: function(overlay) {
+ this.searchOverlay = overlay;
+ },
+ isReversed: function() {
+ return vimGlobalState.isReversed;
+ },
+ setReversed: function(reversed) {
+ vimGlobalState.isReversed = reversed;
+ },
+ getScrollbarAnnotate: function() {
+ return this.annotate;
+ },
+ setScrollbarAnnotate: function(annotate) {
+ this.annotate = annotate;
+ }
+ };
+ function getSearchState(cm) {
+ var vim = cm.state.vim;
+ return vim.searchState_ || (vim.searchState_ = new SearchState());
+ }
+ function dialog(cm, template, shortText, onClose, options) {
+ if (cm.openDialog) {
+ cm.openDialog(template, onClose, { bottom: true, value: options.value,
+ onKeyDown: options.onKeyDown, onKeyUp: options.onKeyUp,
+ selectValueOnOpen: false});
+ }
+ else {
+ onClose(prompt(shortText, ''));
+ }
+ }
+ function splitBySlash(argString) {
+ return splitBySeparator(argString, '/');
+ }
+
+ function findUnescapedSlashes(argString) {
+ return findUnescapedSeparators(argString, '/');
+ }
+
+ function splitBySeparator(argString, separator) {
+ var slashes = findUnescapedSeparators(argString, separator) || [];
+ if (!slashes.length) return [];
+ var tokens = [];
+ // in case of strings like foo/bar
+ if (slashes[0] !== 0) return;
+ for (var i = 0; i < slashes.length; i++) {
+ if (typeof slashes[i] == 'number')
+ tokens.push(argString.substring(slashes[i] + 1, slashes[i+1]));
+ }
+ return tokens;
+ }
+
+ function findUnescapedSeparators(str, separator) {
+ if (!separator)
+ separator = '/';
+
+ var escapeNextChar = false;
+ var slashes = [];
+ for (var i = 0; i < str.length; i++) {
+ var c = str.charAt(i);
+ if (!escapeNextChar && c == separator) {
+ slashes.push(i);
+ }
+ escapeNextChar = !escapeNextChar && (c == '\\');
+ }
+ return slashes;
+ }
+
+ // Translates a search string from ex (vim) syntax into javascript form.
+ function translateRegex(str) {
+ // When these match, add a '\' if unescaped or remove one if escaped.
+ var specials = '|(){';
+ // Remove, but never add, a '\' for these.
+ var unescape = '}';
+ var escapeNextChar = false;
+ var out = [];
+ for (var i = -1; i < str.length; i++) {
+ var c = str.charAt(i) || '';
+ var n = str.charAt(i+1) || '';
+ var specialComesNext = (n && specials.indexOf(n) != -1);
+ if (escapeNextChar) {
+ if (c !== '\\' || !specialComesNext) {
+ out.push(c);
+ }
+ escapeNextChar = false;
+ } else {
+ if (c === '\\') {
+ escapeNextChar = true;
+ // Treat the unescape list as special for removing, but not adding '\'.
+ if (n && unescape.indexOf(n) != -1) {
+ specialComesNext = true;
+ }
+ // Not passing this test means removing a '\'.
+ if (!specialComesNext || n === '\\') {
+ out.push(c);
+ }
+ } else {
+ out.push(c);
+ if (specialComesNext && n !== '\\') {
+ out.push('\\');
+ }
+ }
+ }
+ }
+ return out.join('');
+ }
+
+ // Translates the replace part of a search and replace from ex (vim) syntax into
+ // javascript form. Similar to translateRegex, but additionally fixes back references
+ // (translates '\[0..9]' to '$[0..9]') and follows different rules for escaping '$'.
+ var charUnescapes = {'\\n': '\n', '\\r': '\r', '\\t': '\t'};
+ function translateRegexReplace(str) {
+ var escapeNextChar = false;
+ var out = [];
+ for (var i = -1; i < str.length; i++) {
+ var c = str.charAt(i) || '';
+ var n = str.charAt(i+1) || '';
+ if (charUnescapes[c + n]) {
+ out.push(charUnescapes[c+n]);
+ i++;
+ } else if (escapeNextChar) {
+ // At any point in the loop, escapeNextChar is true if the previous
+ // character was a '\' and was not escaped.
+ out.push(c);
+ escapeNextChar = false;
+ } else {
+ if (c === '\\') {
+ escapeNextChar = true;
+ if ((isNumber(n) || n === '$')) {
+ out.push('$');
+ } else if (n !== '/' && n !== '\\') {
+ out.push('\\');
+ }
+ } else {
+ if (c === '$') {
+ out.push('$');
+ }
+ out.push(c);
+ if (n === '/') {
+ out.push('\\');
+ }
+ }
+ }
+ }
+ return out.join('');
+ }
+
+ // Unescape \ and / in the replace part, for PCRE mode.
+ var unescapes = {'\\/': '/', '\\\\': '\\', '\\n': '\n', '\\r': '\r', '\\t': '\t', '\\&':'&'};
+ function unescapeRegexReplace(str) {
+ var stream = new CodeMirror.StringStream(str);
+ var output = [];
+ while (!stream.eol()) {
+ // Search for \.
+ while (stream.peek() && stream.peek() != '\\') {
+ output.push(stream.next());
+ }
+ var matched = false;
+ for (var matcher in unescapes) {
+ if (stream.match(matcher, true)) {
+ matched = true;
+ output.push(unescapes[matcher]);
+ break;
+ }
+ }
+ if (!matched) {
+ // Don't change anything
+ output.push(stream.next());
+ }
+ }
+ return output.join('');
+ }
+
+ /**
+ * Extract the regular expression from the query and return a Regexp object.
+ * Returns null if the query is blank.
+ * If ignoreCase is passed in, the Regexp object will have the 'i' flag set.
+ * If smartCase is passed in, and the query contains upper case letters,
+ * then ignoreCase is overridden, and the 'i' flag will not be set.
+ * If the query contains the /i in the flag part of the regular expression,
+ * then both ignoreCase and smartCase are ignored, and 'i' will be passed
+ * through to the Regex object.
+ */
+ function parseQuery(query, ignoreCase, smartCase) {
+ // First update the last search register
+ var lastSearchRegister = vimGlobalState.registerController.getRegister('/');
+ lastSearchRegister.setText(query);
+ // Check if the query is already a regex.
+ if (query instanceof RegExp) { return query; }
+ // First try to extract regex + flags from the input. If no flags found,
+ // extract just the regex. IE does not accept flags directly defined in
+ // the regex string in the form /regex/flags
+ var slashes = findUnescapedSlashes(query);
+ var regexPart;
+ var forceIgnoreCase;
+ if (!slashes.length) {
+ // Query looks like 'regexp'
+ regexPart = query;
+ } else {
+ // Query looks like 'regexp/...'
+ regexPart = query.substring(0, slashes[0]);
+ var flagsPart = query.substring(slashes[0]);
+ forceIgnoreCase = (flagsPart.indexOf('i') != -1);
+ }
+ if (!regexPart) {
+ return null;
+ }
+ if (!getOption('pcre')) {
+ regexPart = translateRegex(regexPart);
+ }
+ if (smartCase) {
+ ignoreCase = (/^[^A-Z]*$/).test(regexPart);
+ }
+ var regexp = new RegExp(regexPart,
+ (ignoreCase || forceIgnoreCase) ? 'i' : undefined);
+ return regexp;
+ }
+ function showConfirm(cm, text) {
+ if (cm.openNotification) {
+ cm.openNotification('<span style="color: red">' + text + '</span>',
+ {bottom: true, duration: 5000});
+ } else {
+ alert(text);
+ }
+ }
+ function makePrompt(prefix, desc) {
+ var raw = '<span style="font-family: monospace; white-space: pre">' +
+ (prefix || "") + '<input type="text"></span>';
+ if (desc)
+ raw += ' <span style="color: #888">' + desc + '</span>';
+ return raw;
+ }
+ var searchPromptDesc = '(Javascript regexp)';
+ function showPrompt(cm, options) {
+ var shortText = (options.prefix || '') + ' ' + (options.desc || '');
+ var prompt = makePrompt(options.prefix, options.desc);
+ dialog(cm, prompt, shortText, options.onClose, options);
+ }
+ function regexEqual(r1, r2) {
+ if (r1 instanceof RegExp && r2 instanceof RegExp) {
+ var props = ['global', 'multiline', 'ignoreCase', 'source'];
+ for (var i = 0; i < props.length; i++) {
+ var prop = props[i];
+ if (r1[prop] !== r2[prop]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ // Returns true if the query is valid.
+ function updateSearchQuery(cm, rawQuery, ignoreCase, smartCase) {
+ if (!rawQuery) {
+ return;
+ }
+ var state = getSearchState(cm);
+ var query = parseQuery(rawQuery, !!ignoreCase, !!smartCase);
+ if (!query) {
+ return;
+ }
+ highlightSearchMatches(cm, query);
+ if (regexEqual(query, state.getQuery())) {
+ return query;
+ }
+ state.setQuery(query);
+ return query;
+ }
+ function searchOverlay(query) {
+ if (query.source.charAt(0) == '^') {
+ var matchSol = true;
+ }
+ return {
+ token: function(stream) {
+ if (matchSol && !stream.sol()) {
+ stream.skipToEnd();
+ return;
+ }
+ var match = stream.match(query, false);
+ if (match) {
+ if (match[0].length == 0) {
+ // Matched empty string, skip to next.
+ stream.next();
+ return 'searching';
+ }
+ if (!stream.sol()) {
+ // Backtrack 1 to match \b
+ stream.backUp(1);
+ if (!query.exec(stream.next() + match[0])) {
+ stream.next();
+ return null;
+ }
+ }
+ stream.match(query);
+ return 'searching';
+ }
+ while (!stream.eol()) {
+ stream.next();
+ if (stream.match(query, false)) break;
+ }
+ },
+ query: query
+ };
+ }
+ var highlightTimeout = 0;
+ function highlightSearchMatches(cm, query) {
+ clearTimeout(highlightTimeout);
+ highlightTimeout = setTimeout(function() {
+ var searchState = getSearchState(cm);
+ var overlay = searchState.getOverlay();
+ if (!overlay || query != overlay.query) {
+ if (overlay) {
+ cm.removeOverlay(overlay);
+ }
+ overlay = searchOverlay(query);
+ cm.addOverlay(overlay);
+ if (cm.showMatchesOnScrollbar) {
+ if (searchState.getScrollbarAnnotate()) {
+ searchState.getScrollbarAnnotate().clear();
+ }
+ searchState.setScrollbarAnnotate(cm.showMatchesOnScrollbar(query));
+ }
+ searchState.setOverlay(overlay);
+ }
+ }, 50);
+ }
+ function findNext(cm, prev, query, repeat) {
+ if (repeat === undefined) { repeat = 1; }
+ return cm.operation(function() {
+ var pos = cm.getCursor();
+ var cursor = cm.getSearchCursor(query, pos);
+ for (var i = 0; i < repeat; i++) {
+ var found = cursor.find(prev);
+ if (i == 0 && found && cursorEqual(cursor.from(), pos)) { found = cursor.find(prev); }
+ if (!found) {
+ // SearchCursor may have returned null because it hit EOF, wrap
+ // around and try again.
+ cursor = cm.getSearchCursor(query,
+ (prev) ? Pos(cm.lastLine()) : Pos(cm.firstLine(), 0) );
+ if (!cursor.find(prev)) {
+ return;
+ }
+ }
+ }
+ return cursor.from();
+ });
+ }
+ function clearSearchHighlight(cm) {
+ var state = getSearchState(cm);
+ cm.removeOverlay(getSearchState(cm).getOverlay());
+ state.setOverlay(null);
+ if (state.getScrollbarAnnotate()) {
+ state.getScrollbarAnnotate().clear();
+ state.setScrollbarAnnotate(null);
+ }
+ }
+ /**
+ * Check if pos is in the specified range, INCLUSIVE.
+ * Range can be specified with 1 or 2 arguments.
+ * If the first range argument is an array, treat it as an array of line
+ * numbers. Match pos against any of the lines.
+ * If the first range argument is a number,
+ * if there is only 1 range argument, check if pos has the same line
+ * number
+ * if there are 2 range arguments, then check if pos is in between the two
+ * range arguments.
+ */
+ function isInRange(pos, start, end) {
+ if (typeof pos != 'number') {
+ // Assume it is a cursor position. Get the line number.
+ pos = pos.line;
+ }
+ if (start instanceof Array) {
+ return inArray(pos, start);
+ } else {
+ if (end) {
+ return (pos >= start && pos <= end);
+ } else {
+ return pos == start;
+ }
+ }
+ }
+ function getUserVisibleLines(cm) {
+ var scrollInfo = cm.getScrollInfo();
+ var occludeToleranceTop = 6;
+ var occludeToleranceBottom = 10;
+ var from = cm.coordsChar({left:0, top: occludeToleranceTop + scrollInfo.top}, 'local');
+ var bottomY = scrollInfo.clientHeight - occludeToleranceBottom + scrollInfo.top;
+ var to = cm.coordsChar({left:0, top: bottomY}, 'local');
+ return {top: from.line, bottom: to.line};
+ }
+
+ function getMarkPos(cm, vim, markName) {
+ if (markName == '\'') {
+ var history = cm.doc.history.done;
+ var event = history[history.length - 2];
+ return event && event.ranges && event.ranges[0].head;
+ } else if (markName == '.') {
+ if (cm.doc.history.lastModTime == 0) {
+ return // If no changes, bail out; don't bother to copy or reverse history array.
+ } else {
+ var changeHistory = cm.doc.history.done.filter(function(el){ if (el.changes !== undefined) { return el } });
+ changeHistory.reverse();
+ var lastEditPos = changeHistory[0].changes[0].to;
+ }
+ return lastEditPos;
+ }
+
+ var mark = vim.marks[markName];
+ return mark && mark.find();
+ }
+
+ var ExCommandDispatcher = function() {
+ this.buildCommandMap_();
+ };
+ ExCommandDispatcher.prototype = {
+ processCommand: function(cm, input, opt_params) {
+ var that = this;
+ cm.operation(function () {
+ cm.curOp.isVimOp = true;
+ that._processCommand(cm, input, opt_params);
+ });
+ },
+ _processCommand: function(cm, input, opt_params) {
+ var vim = cm.state.vim;
+ var commandHistoryRegister = vimGlobalState.registerController.getRegister(':');
+ var previousCommand = commandHistoryRegister.toString();
+ if (vim.visualMode) {
+ exitVisualMode(cm);
+ }
+ var inputStream = new CodeMirror.StringStream(input);
+ // update ": with the latest command whether valid or invalid
+ commandHistoryRegister.setText(input);
+ var params = opt_params || {};
+ params.input = input;
+ try {
+ this.parseInput_(cm, inputStream, params);
+ } catch(e) {
+ showConfirm(cm, e);
+ throw e;
+ }
+ var command;
+ var commandName;
+ if (!params.commandName) {
+ // If only a line range is defined, move to the line.
+ if (params.line !== undefined) {
+ commandName = 'move';
+ }
+ } else {
+ command = this.matchCommand_(params.commandName);
+ if (command) {
+ commandName = command.name;
+ if (command.excludeFromCommandHistory) {
+ commandHistoryRegister.setText(previousCommand);
+ }
+ this.parseCommandArgs_(inputStream, params, command);
+ if (command.type == 'exToKey') {
+ // Handle Ex to Key mapping.
+ for (var i = 0; i < command.toKeys.length; i++) {
+ CodeMirror.Vim.handleKey(cm, command.toKeys[i], 'mapping');
+ }
+ return;
+ } else if (command.type == 'exToEx') {
+ // Handle Ex to Ex mapping.
+ this.processCommand(cm, command.toInput);
+ return;
+ }
+ }
+ }
+ if (!commandName) {
+ showConfirm(cm, 'Not an editor command ":' + input + '"');
+ return;
+ }
+ try {
+ exCommands[commandName](cm, params);
+ // Possibly asynchronous commands (e.g. substitute, which might have a
+ // user confirmation), are responsible for calling the callback when
+ // done. All others have it taken care of for them here.
+ if ((!command || !command.possiblyAsync) && params.callback) {
+ params.callback();
+ }
+ } catch(e) {
+ showConfirm(cm, e);
+ throw e;
+ }
+ },
+ parseInput_: function(cm, inputStream, result) {
+ inputStream.eatWhile(':');
+ // Parse range.
+ if (inputStream.eat('%')) {
+ result.line = cm.firstLine();
+ result.lineEnd = cm.lastLine();
+ } else {
+ result.line = this.parseLineSpec_(cm, inputStream);
+ if (result.line !== undefined && inputStream.eat(',')) {
+ result.lineEnd = this.parseLineSpec_(cm, inputStream);
+ }
+ }
+
+ // Parse command name.
+ var commandMatch = inputStream.match(/^(\w+)/);
+ if (commandMatch) {
+ result.commandName = commandMatch[1];
+ } else {
+ result.commandName = inputStream.match(/.*/)[0];
+ }
+
+ return result;
+ },
+ parseLineSpec_: function(cm, inputStream) {
+ var numberMatch = inputStream.match(/^(\d+)/);
+ if (numberMatch) {
+ // Absolute line number plus offset (N+M or N-M) is probably a typo,
+ // not something the user actually wanted. (NB: vim does allow this.)
+ return parseInt(numberMatch[1], 10) - 1;
+ }
+ switch (inputStream.next()) {
+ case '.':
+ return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
+ case '$':
+ return this.parseLineSpecOffset_(inputStream, cm.lastLine());
+ case '\'':
+ var markName = inputStream.next();
+ var markPos = getMarkPos(cm, cm.state.vim, markName);
+ if (!markPos) throw new Error('Mark not set');
+ return this.parseLineSpecOffset_(inputStream, markPos.line);
+ case '-':
+ case '+':
+ inputStream.backUp(1);
+ // Offset is relative to current line if not otherwise specified.
+ return this.parseLineSpecOffset_(inputStream, cm.getCursor().line);
+ default:
+ inputStream.backUp(1);
+ return undefined;
+ }
+ },
+ parseLineSpecOffset_: function(inputStream, line) {
+ var offsetMatch = inputStream.match(/^([+-])?(\d+)/);
+ if (offsetMatch) {
+ var offset = parseInt(offsetMatch[2], 10);
+ if (offsetMatch[1] == "-") {
+ line -= offset;
+ } else {
+ line += offset;
+ }
+ }
+ return line;
+ },
+ parseCommandArgs_: function(inputStream, params, command) {
+ if (inputStream.eol()) {
+ return;
+ }
+ params.argString = inputStream.match(/.*/)[0];
+ // Parse command-line arguments
+ var delim = command.argDelimiter || /\s+/;
+ var args = trim(params.argString).split(delim);
+ if (args.length && args[0]) {
+ params.args = args;
+ }
+ },
+ matchCommand_: function(commandName) {
+ // Return the command in the command map that matches the shortest
+ // prefix of the passed in command name. The match is guaranteed to be
+ // unambiguous if the defaultExCommandMap's shortNames are set up
+ // correctly. (see @code{defaultExCommandMap}).
+ for (var i = commandName.length; i > 0; i--) {
+ var prefix = commandName.substring(0, i);
+ if (this.commandMap_[prefix]) {
+ var command = this.commandMap_[prefix];
+ if (command.name.indexOf(commandName) === 0) {
+ return command;
+ }
+ }
+ }
+ return null;
+ },
+ buildCommandMap_: function() {
+ this.commandMap_ = {};
+ for (var i = 0; i < defaultExCommandMap.length; i++) {
+ var command = defaultExCommandMap[i];
+ var key = command.shortName || command.name;
+ this.commandMap_[key] = command;
+ }
+ },
+ map: function(lhs, rhs, ctx) {
+ if (lhs != ':' && lhs.charAt(0) == ':') {
+ if (ctx) { throw Error('Mode not supported for ex mappings'); }
+ var commandName = lhs.substring(1);
+ if (rhs != ':' && rhs.charAt(0) == ':') {
+ // Ex to Ex mapping
+ this.commandMap_[commandName] = {
+ name: commandName,
+ type: 'exToEx',
+ toInput: rhs.substring(1),
+ user: true
+ };
+ } else {
+ // Ex to key mapping
+ this.commandMap_[commandName] = {
+ name: commandName,
+ type: 'exToKey',
+ toKeys: rhs,
+ user: true
+ };
+ }
+ } else {
+ if (rhs != ':' && rhs.charAt(0) == ':') {
+ // Key to Ex mapping.
+ var mapping = {
+ keys: lhs,
+ type: 'keyToEx',
+ exArgs: { input: rhs.substring(1) }
+ };
+ if (ctx) { mapping.context = ctx; }
+ defaultKeymap.unshift(mapping);
+ } else {
+ // Key to key mapping
+ var mapping = {
+ keys: lhs,
+ type: 'keyToKey',
+ toKeys: rhs
+ };
+ if (ctx) { mapping.context = ctx; }
+ defaultKeymap.unshift(mapping);
+ }
+ }
+ },
+ unmap: function(lhs, ctx) {
+ if (lhs != ':' && lhs.charAt(0) == ':') {
+ // Ex to Ex or Ex to key mapping
+ if (ctx) { throw Error('Mode not supported for ex mappings'); }
+ var commandName = lhs.substring(1);
+ if (this.commandMap_[commandName] && this.commandMap_[commandName].user) {
+ delete this.commandMap_[commandName];
+ return;
+ }
+ } else {
+ // Key to Ex or key to key mapping
+ var keys = lhs;
+ for (var i = 0; i < defaultKeymap.length; i++) {
+ if (keys == defaultKeymap[i].keys
+ && defaultKeymap[i].context === ctx) {
+ defaultKeymap.splice(i, 1);
+ return;
+ }
+ }
+ }
+ throw Error('No such mapping.');
+ }
+ };
+
+ var exCommands = {
+ colorscheme: function(cm, params) {
+ if (!params.args || params.args.length < 1) {
+ showConfirm(cm, cm.getOption('theme'));
+ return;
+ }
+ cm.setOption('theme', params.args[0]);
+ },
+ map: function(cm, params, ctx) {
+ var mapArgs = params.args;
+ if (!mapArgs || mapArgs.length < 2) {
+ if (cm) {
+ showConfirm(cm, 'Invalid mapping: ' + params.input);
+ }
+ return;
+ }
+ exCommandDispatcher.map(mapArgs[0], mapArgs[1], ctx);
+ },
+ imap: function(cm, params) { this.map(cm, params, 'insert'); },
+ nmap: function(cm, params) { this.map(cm, params, 'normal'); },
+ vmap: function(cm, params) { this.map(cm, params, 'visual'); },
+ unmap: function(cm, params, ctx) {
+ var mapArgs = params.args;
+ if (!mapArgs || mapArgs.length < 1) {
+ if (cm) {
+ showConfirm(cm, 'No such mapping: ' + params.input);
+ }
+ return;
+ }
+ exCommandDispatcher.unmap(mapArgs[0], ctx);
+ },
+ move: function(cm, params) {
+ commandDispatcher.processCommand(cm, cm.state.vim, {
+ type: 'motion',
+ motion: 'moveToLineOrEdgeOfDocument',
+ motionArgs: { forward: false, explicitRepeat: true,
+ linewise: true },
+ repeatOverride: params.line+1});
+ },
+ set: function(cm, params) {
+ var setArgs = params.args;
+ // Options passed through to the setOption/getOption calls. May be passed in by the
+ // local/global versions of the set command
+ var setCfg = params.setCfg || {};
+ if (!setArgs || setArgs.length < 1) {
+ if (cm) {
+ showConfirm(cm, 'Invalid mapping: ' + params.input);
+ }
+ return;
+ }
+ var expr = setArgs[0].split('=');
+ var optionName = expr[0];
+ var value = expr[1];
+ var forceGet = false;
+
+ if (optionName.charAt(optionName.length - 1) == '?') {
+ // If post-fixed with ?, then the set is actually a get.
+ if (value) { throw Error('Trailing characters: ' + params.argString); }
+ optionName = optionName.substring(0, optionName.length - 1);
+ forceGet = true;
+ }
+ if (value === undefined && optionName.substring(0, 2) == 'no') {
+ // To set boolean options to false, the option name is prefixed with
+ // 'no'.
+ optionName = optionName.substring(2);
+ value = false;
+ }
+
+ var optionIsBoolean = options[optionName] && options[optionName].type == 'boolean';
+ if (optionIsBoolean && value == undefined) {
+ // Calling set with a boolean option sets it to true.
+ value = true;
+ }
+ // If no value is provided, then we assume this is a get.
+ if (!optionIsBoolean && value === undefined || forceGet) {
+ var oldValue = getOption(optionName, cm, setCfg);
+ if (oldValue instanceof Error) {
+ showConfirm(cm, oldValue.message);
+ } else if (oldValue === true || oldValue === false) {
+ showConfirm(cm, ' ' + (oldValue ? '' : 'no') + optionName);
+ } else {
+ showConfirm(cm, ' ' + optionName + '=' + oldValue);
+ }
+ } else {
+ var setOptionReturn = setOption(optionName, value, cm, setCfg);
+ if (setOptionReturn instanceof Error) {
+ showConfirm(cm, setOptionReturn.message);
+ }
+ }
+ },
+ setlocal: function (cm, params) {
+ // setCfg is passed through to setOption
+ params.setCfg = {scope: 'local'};
+ this.set(cm, params);
+ },
+ setglobal: function (cm, params) {
+ // setCfg is passed through to setOption
+ params.setCfg = {scope: 'global'};
+ this.set(cm, params);
+ },
+ registers: function(cm, params) {
+ var regArgs = params.args;
+ var registers = vimGlobalState.registerController.registers;
+ var regInfo = '----------Registers----------<br><br>';
+ if (!regArgs) {
+ for (var registerName in registers) {
+ var text = registers[registerName].toString();
+ if (text.length) {
+ regInfo += '"' + registerName + ' ' + text + '<br>';
+ }
+ }
+ } else {
+ var registerName;
+ regArgs = regArgs.join('');
+ for (var i = 0; i < regArgs.length; i++) {
+ registerName = regArgs.charAt(i);
+ if (!vimGlobalState.registerController.isValidRegister(registerName)) {
+ continue;
+ }
+ var register = registers[registerName] || new Register();
+ regInfo += '"' + registerName + ' ' + register.toString() + '<br>';
+ }
+ }
+ showConfirm(cm, regInfo);
+ },
+ sort: function(cm, params) {
+ var reverse, ignoreCase, unique, number, pattern;
+ function parseArgs() {
+ if (params.argString) {
+ var args = new CodeMirror.StringStream(params.argString);
+ if (args.eat('!')) { reverse = true; }
+ if (args.eol()) { return; }
+ if (!args.eatSpace()) { return 'Invalid arguments'; }
+ var opts = args.match(/([dinuox]+)?\s*(\/.+\/)?\s*/);
+ if (!opts && !args.eol()) { return 'Invalid arguments'; }
+ if (opts[1]) {
+ ignoreCase = opts[1].indexOf('i') != -1;
+ unique = opts[1].indexOf('u') != -1;
+ var decimal = opts[1].indexOf('d') != -1 || opts[1].indexOf('n') != -1 && 1;
+ var hex = opts[1].indexOf('x') != -1 && 1;
+ var octal = opts[1].indexOf('o') != -1 && 1;
+ if (decimal + hex + octal > 1) { return 'Invalid arguments'; }
+ number = decimal && 'decimal' || hex && 'hex' || octal && 'octal';
+ }
+ if (opts[2]) {
+ pattern = new RegExp(opts[2].substr(1, opts[2].length - 2), ignoreCase ? 'i' : '');
+ }
+ }
+ }
+ var err = parseArgs();
+ if (err) {
+ showConfirm(cm, err + ': ' + params.argString);
+ return;
+ }
+ var lineStart = params.line || cm.firstLine();
+ var lineEnd = params.lineEnd || params.line || cm.lastLine();
+ if (lineStart == lineEnd) { return; }
+ var curStart = Pos(lineStart, 0);
+ var curEnd = Pos(lineEnd, lineLength(cm, lineEnd));
+ var text = cm.getRange(curStart, curEnd).split('\n');
+ var numberRegex = pattern ? pattern :
+ (number == 'decimal') ? /(-?)([\d]+)/ :
+ (number == 'hex') ? /(-?)(?:0x)?([0-9a-f]+)/i :
+ (number == 'octal') ? /([0-7]+)/ : null;
+ var radix = (number == 'decimal') ? 10 : (number == 'hex') ? 16 : (number == 'octal') ? 8 : null;
+ var numPart = [], textPart = [];
+ if (number || pattern) {
+ for (var i = 0; i < text.length; i++) {
+ var matchPart = pattern ? text[i].match(pattern) : null;
+ if (matchPart && matchPart[0] != '') {
+ numPart.push(matchPart);
+ } else if (!pattern && numberRegex.exec(text[i])) {
+ numPart.push(text[i]);
+ } else {
+ textPart.push(text[i]);
+ }
+ }
+ } else {
+ textPart = text;
+ }
+ function compareFn(a, b) {
+ if (reverse) { var tmp; tmp = a; a = b; b = tmp; }
+ if (ignoreCase) { a = a.toLowerCase(); b = b.toLowerCase(); }
+ var anum = number && numberRegex.exec(a);
+ var bnum = number && numberRegex.exec(b);
+ if (!anum) { return a < b ? -1 : 1; }
+ anum = parseInt((anum[1] + anum[2]).toLowerCase(), radix);
+ bnum = parseInt((bnum[1] + bnum[2]).toLowerCase(), radix);
+ return anum - bnum;
+ }
+ function comparePatternFn(a, b) {
+ if (reverse) { var tmp; tmp = a; a = b; b = tmp; }
+ if (ignoreCase) { a[0] = a[0].toLowerCase(); b[0] = b[0].toLowerCase(); }
+ return (a[0] < b[0]) ? -1 : 1;
+ }
+ numPart.sort(pattern ? comparePatternFn : compareFn);
+ if (pattern) {
+ for (var i = 0; i < numPart.length; i++) {
+ numPart[i] = numPart[i].input;
+ }
+ } else if (!number) { textPart.sort(compareFn); }
+ text = (!reverse) ? textPart.concat(numPart) : numPart.concat(textPart);
+ if (unique) { // Remove duplicate lines
+ var textOld = text;
+ var lastLine;
+ text = [];
+ for (var i = 0; i < textOld.length; i++) {
+ if (textOld[i] != lastLine) {
+ text.push(textOld[i]);
+ }
+ lastLine = textOld[i];
+ }
+ }
+ cm.replaceRange(text.join('\n'), curStart, curEnd);
+ },
+ global: function(cm, params) {
+ // a global command is of the form
+ // :[range]g/pattern/[cmd]
+ // argString holds the string /pattern/[cmd]
+ var argString = params.argString;
+ if (!argString) {
+ showConfirm(cm, 'Regular Expression missing from global');
+ return;
+ }
+ // range is specified here
+ var lineStart = (params.line !== undefined) ? params.line : cm.firstLine();
+ var lineEnd = params.lineEnd || params.line || cm.lastLine();
+ // get the tokens from argString
+ var tokens = splitBySlash(argString);
+ var regexPart = argString, cmd;
+ if (tokens.length) {
+ regexPart = tokens[0];
+ cmd = tokens.slice(1, tokens.length).join('/');
+ }
+ if (regexPart) {
+ // If regex part is empty, then use the previous query. Otherwise
+ // use the regex part as the new query.
+ try {
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
+ true /** smartCase */);
+ } catch (e) {
+ showConfirm(cm, 'Invalid regex: ' + regexPart);
+ return;
+ }
+ }
+ // now that we have the regexPart, search for regex matches in the
+ // specified range of lines
+ var query = getSearchState(cm).getQuery();
+ var matchedLines = [], content = '';
+ for (var i = lineStart; i <= lineEnd; i++) {
+ var matched = query.test(cm.getLine(i));
+ if (matched) {
+ matchedLines.push(i+1);
+ content+= cm.getLine(i) + '<br>';
+ }
+ }
+ // if there is no [cmd], just display the list of matched lines
+ if (!cmd) {
+ showConfirm(cm, content);
+ return;
+ }
+ var index = 0;
+ var nextCommand = function() {
+ if (index < matchedLines.length) {
+ var command = matchedLines[index] + cmd;
+ exCommandDispatcher.processCommand(cm, command, {
+ callback: nextCommand
+ });
+ }
+ index++;
+ };
+ nextCommand();
+ },
+ substitute: function(cm, params) {
+ if (!cm.getSearchCursor) {
+ throw new Error('Search feature not available. Requires searchcursor.js or ' +
+ 'any other getSearchCursor implementation.');
+ }
+ var argString = params.argString;
+ var tokens = argString ? splitBySeparator(argString, argString[0]) : [];
+ var regexPart, replacePart = '', trailing, flagsPart, count;
+ var confirm = false; // Whether to confirm each replace.
+ var global = false; // True to replace all instances on a line, false to replace only 1.
+ if (tokens.length) {
+ regexPart = tokens[0];
+ if (getOption('pcre') && regexPart !== '') {
+ regexPart = new RegExp(regexPart).source; //normalize not escaped characters
+ }
+ replacePart = tokens[1];
+ if (regexPart && regexPart[regexPart.length - 1] === '$') {
+ regexPart = regexPart.slice(0, regexPart.length - 1) + '\\n';
+ replacePart = replacePart ? replacePart + '\n' : '\n';
+ }
+ if (replacePart !== undefined) {
+ if (getOption('pcre')) {
+ replacePart = unescapeRegexReplace(replacePart.replace(/([^\\])&/g,"$1$$&"));
+ } else {
+ replacePart = translateRegexReplace(replacePart);
+ }
+ vimGlobalState.lastSubstituteReplacePart = replacePart;
+ }
+ trailing = tokens[2] ? tokens[2].split(' ') : [];
+ } else {
+ // either the argString is empty or its of the form ' hello/world'
+ // actually splitBySlash returns a list of tokens
+ // only if the string starts with a '/'
+ if (argString && argString.length) {
+ showConfirm(cm, 'Substitutions should be of the form ' +
+ ':s/pattern/replace/');
+ return;
+ }
+ }
+ // After the 3rd slash, we can have flags followed by a space followed
+ // by count.
+ if (trailing) {
+ flagsPart = trailing[0];
+ count = parseInt(trailing[1]);
+ if (flagsPart) {
+ if (flagsPart.indexOf('c') != -1) {
+ confirm = true;
+ flagsPart.replace('c', '');
+ }
+ if (flagsPart.indexOf('g') != -1) {
+ global = true;
+ flagsPart.replace('g', '');
+ }
+ if (getOption('pcre')) {
+ regexPart = regexPart + '/' + flagsPart;
+ } else {
+ regexPart = regexPart.replace(/\//g, "\\/") + '/' + flagsPart;
+ }
+ }
+ }
+ if (regexPart) {
+ // If regex part is empty, then use the previous query. Otherwise use
+ // the regex part as the new query.
+ try {
+ updateSearchQuery(cm, regexPart, true /** ignoreCase */,
+ true /** smartCase */);
+ } catch (e) {
+ showConfirm(cm, 'Invalid regex: ' + regexPart);
+ return;
+ }
+ }
+ replacePart = replacePart || vimGlobalState.lastSubstituteReplacePart;
+ if (replacePart === undefined) {
+ showConfirm(cm, 'No previous substitute regular expression');
+ return;
+ }
+ var state = getSearchState(cm);
+ var query = state.getQuery();
+ var lineStart = (params.line !== undefined) ? params.line : cm.getCursor().line;
+ var lineEnd = params.lineEnd || lineStart;
+ if (lineStart == cm.firstLine() && lineEnd == cm.lastLine()) {
+ lineEnd = Infinity;
+ }
+ if (count) {
+ lineStart = lineEnd;
+ lineEnd = lineStart + count - 1;
+ }
+ var startPos = clipCursorToContent(cm, Pos(lineStart, 0));
+ var cursor = cm.getSearchCursor(query, startPos);
+ doReplace(cm, confirm, global, lineStart, lineEnd, cursor, query, replacePart, params.callback);
+ },
+ redo: CodeMirror.commands.redo,
+ undo: CodeMirror.commands.undo,
+ write: function(cm) {
+ if (CodeMirror.commands.save) {
+ // If a save command is defined, call it.
+ CodeMirror.commands.save(cm);
+ } else if (cm.save) {
+ // Saves to text area if no save command is defined and cm.save() is available.
+ cm.save();
+ }
+ },
+ nohlsearch: function(cm) {
+ clearSearchHighlight(cm);
+ },
+ yank: function (cm) {
+ var cur = copyCursor(cm.getCursor());
+ var line = cur.line;
+ var lineText = cm.getLine(line);
+ vimGlobalState.registerController.pushText(
+ '0', 'yank', lineText, true, true);
+ },
+ delmarks: function(cm, params) {
+ if (!params.argString || !trim(params.argString)) {
+ showConfirm(cm, 'Argument required');
+ return;
+ }
+
+ var state = cm.state.vim;
+ var stream = new CodeMirror.StringStream(trim(params.argString));
+ while (!stream.eol()) {
+ stream.eatSpace();
+
+ // Record the streams position at the beginning of the loop for use
+ // in error messages.
+ var count = stream.pos;
+
+ if (!stream.match(/[a-zA-Z]/, false)) {
+ showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
+ return;
+ }
+
+ var sym = stream.next();
+ // Check if this symbol is part of a range
+ if (stream.match('-', true)) {
+ // This symbol is part of a range.
+
+ // The range must terminate at an alphabetic character.
+ if (!stream.match(/[a-zA-Z]/, false)) {
+ showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
+ return;
+ }
+
+ var startMark = sym;
+ var finishMark = stream.next();
+ // The range must terminate at an alphabetic character which
+ // shares the same case as the start of the range.
+ if (isLowerCase(startMark) && isLowerCase(finishMark) ||
+ isUpperCase(startMark) && isUpperCase(finishMark)) {
+ var start = startMark.charCodeAt(0);
+ var finish = finishMark.charCodeAt(0);
+ if (start >= finish) {
+ showConfirm(cm, 'Invalid argument: ' + params.argString.substring(count));
+ return;
+ }
+
+ // Because marks are always ASCII values, and we have
+ // determined that they are the same case, we can use
+ // their char codes to iterate through the defined range.
+ for (var j = 0; j <= finish - start; j++) {
+ var mark = String.fromCharCode(start + j);
+ delete state.marks[mark];
+ }
+ } else {
+ showConfirm(cm, 'Invalid argument: ' + startMark + '-');
+ return;
+ }
+ } else {
+ // This symbol is a valid mark, and is not part of a range.
+ delete state.marks[sym];
+ }
+ }
+ }
+ };
+
+ var exCommandDispatcher = new ExCommandDispatcher();
+
+ /**
+ * @param {CodeMirror} cm CodeMirror instance we are in.
+ * @param {boolean} confirm Whether to confirm each replace.
+ * @param {Cursor} lineStart Line to start replacing from.
+ * @param {Cursor} lineEnd Line to stop replacing at.
+ * @param {RegExp} query Query for performing matches with.
+ * @param {string} replaceWith Text to replace matches with. May contain $1,
+ * $2, etc for replacing captured groups using Javascript replace.
+ * @param {function()} callback A callback for when the replace is done.
+ */
+ function doReplace(cm, confirm, global, lineStart, lineEnd, searchCursor, query,
+ replaceWith, callback) {
+ // Set up all the functions.
+ cm.state.vim.exMode = true;
+ var done = false;
+ var lastPos = searchCursor.from();
+ function replaceAll() {
+ cm.operation(function() {
+ while (!done) {
+ replace();
+ next();
+ }
+ stop();
+ });
+ }
+ function replace() {
+ var text = cm.getRange(searchCursor.from(), searchCursor.to());
+ var newText = text.replace(query, replaceWith);
+ searchCursor.replace(newText);
+ }
+ function next() {
+ // The below only loops to skip over multiple occurrences on the same
+ // line when 'global' is not true.
+ while(searchCursor.findNext() &&
+ isInRange(searchCursor.from(), lineStart, lineEnd)) {
+ if (!global && lastPos && searchCursor.from().line == lastPos.line) {
+ continue;
+ }
+ cm.scrollIntoView(searchCursor.from(), 30);
+ cm.setSelection(searchCursor.from(), searchCursor.to());
+ lastPos = searchCursor.from();
+ done = false;
+ return;
+ }
+ done = true;
+ }
+ function stop(close) {
+ if (close) { close(); }
+ cm.focus();
+ if (lastPos) {
+ cm.setCursor(lastPos);
+ var vim = cm.state.vim;
+ vim.exMode = false;
+ vim.lastHPos = vim.lastHSPos = lastPos.ch;
+ }
+ if (callback) { callback(); }
+ }
+ function onPromptKeyDown(e, _value, close) {
+ // Swallow all keys.
+ CodeMirror.e_stop(e);
+ var keyName = CodeMirror.keyName(e);
+ switch (keyName) {
+ case 'Y':
+ replace(); next(); break;
+ case 'N':
+ next(); break;
+ case 'A':
+ // replaceAll contains a call to close of its own. We don't want it
+ // to fire too early or multiple times.
+ var savedCallback = callback;
+ callback = undefined;
+ cm.operation(replaceAll);
+ callback = savedCallback;
+ break;
+ case 'L':
+ replace();
+ // fall through and exit.
+ case 'Q':
+ case 'Esc':
+ case 'Ctrl-C':
+ case 'Ctrl-[':
+ stop(close);
+ break;
+ }
+ if (done) { stop(close); }
+ return true;
+ }
+
+ // Actually do replace.
+ next();
+ if (done) {
+ showConfirm(cm, 'No matches for ' + query.source);
+ return;
+ }
+ if (!confirm) {
+ replaceAll();
+ if (callback) { callback(); }
+ return;
+ }
+ showPrompt(cm, {
+ prefix: 'replace with <strong>' + replaceWith + '</strong> (y/n/a/q/l)',
+ onKeyDown: onPromptKeyDown
+ });
+ }
+
+ CodeMirror.keyMap.vim = {
+ attach: attachVimMap,
+ detach: detachVimMap,
+ call: cmKey
+ };
+
+ function exitInsertMode(cm) {
+ var vim = cm.state.vim;
+ var macroModeState = vimGlobalState.macroModeState;
+ var insertModeChangeRegister = vimGlobalState.registerController.getRegister('.');
+ var isPlaying = macroModeState.isPlaying;
+ var lastChange = macroModeState.lastInsertModeChanges;
+ if (!isPlaying) {
+ cm.off('change', onChange);
+ CodeMirror.off(cm.getInputField(), 'keydown', onKeyEventTargetKeyDown);
+ }
+ if (!isPlaying && vim.insertModeRepeat > 1) {
+ // Perform insert mode repeat for commands like 3,a and 3,o.
+ repeatLastEdit(cm, vim, vim.insertModeRepeat - 1,
+ true /** repeatForInsert */);
+ vim.lastEditInputState.repeatOverride = vim.insertModeRepeat;
+ }
+ delete vim.insertModeRepeat;
+ vim.insertMode = false;
+ cm.setCursor(cm.getCursor().line, cm.getCursor().ch-1);
+ cm.setOption('keyMap', 'vim');
+ cm.setOption('disableInput', true);
+ cm.toggleOverwrite(false); // exit replace mode if we were in it.
+ // update the ". register before exiting insert mode
+ insertModeChangeRegister.setText(lastChange.changes.join(''));
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "normal"});
+ if (macroModeState.isRecording) {
+ logInsertModeChange(macroModeState);
+ }
+ }
+
+ function _mapCommand(command) {
+ defaultKeymap.unshift(command);
+ }
+
+ function mapCommand(keys, type, name, args, extra) {
+ var command = {keys: keys, type: type};
+ command[type] = name;
+ command[type + "Args"] = args;
+ for (var key in extra)
+ command[key] = extra[key];
+ _mapCommand(command);
+ }
+
+ // The timeout in milliseconds for the two-character ESC keymap should be
+ // adjusted according to your typing speed to prevent false positives.
+ defineOption('insertModeEscKeysTimeout', 200, 'number');
+
+ CodeMirror.keyMap['vim-insert'] = {
+ // TODO: override navigation keys so that Esc will cancel automatic
+ // indentation from o, O, i_<CR>
+ fallthrough: ['default'],
+ attach: attachVimMap,
+ detach: detachVimMap,
+ call: cmKey
+ };
+
+ CodeMirror.keyMap['vim-replace'] = {
+ 'Backspace': 'goCharLeft',
+ fallthrough: ['vim-insert'],
+ attach: attachVimMap,
+ detach: detachVimMap,
+ call: cmKey
+ };
+
+ function executeMacroRegister(cm, vim, macroModeState, registerName) {
+ var register = vimGlobalState.registerController.getRegister(registerName);
+ if (registerName == ':') {
+ // Read-only register containing last Ex command.
+ if (register.keyBuffer[0]) {
+ exCommandDispatcher.processCommand(cm, register.keyBuffer[0]);
+ }
+ macroModeState.isPlaying = false;
+ return;
+ }
+ var keyBuffer = register.keyBuffer;
+ var imc = 0;
+ macroModeState.isPlaying = true;
+ macroModeState.replaySearchQueries = register.searchQueries.slice(0);
+ for (var i = 0; i < keyBuffer.length; i++) {
+ var text = keyBuffer[i];
+ var match, key;
+ while (text) {
+ // Pull off one command key, which is either a single character
+ // or a special sequence wrapped in '<' and '>', e.g. '<Space>'.
+ match = (/<\w+-.+?>|<\w+>|./).exec(text);
+ key = match[0];
+ text = text.substring(match.index + key.length);
+ CodeMirror.Vim.handleKey(cm, key, 'macro');
+ if (vim.insertMode) {
+ var changes = register.insertModeChanges[imc++].changes;
+ vimGlobalState.macroModeState.lastInsertModeChanges.changes =
+ changes;
+ repeatInsertModeChanges(cm, changes, 1);
+ exitInsertMode(cm);
+ }
+ }
+ }
+ macroModeState.isPlaying = false;
+ }
+
+ function logKey(macroModeState, key) {
+ if (macroModeState.isPlaying) { return; }
+ var registerName = macroModeState.latestRegister;
+ var register = vimGlobalState.registerController.getRegister(registerName);
+ if (register) {
+ register.pushText(key);
+ }
+ }
+
+ function logInsertModeChange(macroModeState) {
+ if (macroModeState.isPlaying) { return; }
+ var registerName = macroModeState.latestRegister;
+ var register = vimGlobalState.registerController.getRegister(registerName);
+ if (register && register.pushInsertModeChanges) {
+ register.pushInsertModeChanges(macroModeState.lastInsertModeChanges);
+ }
+ }
+
+ function logSearchQuery(macroModeState, query) {
+ if (macroModeState.isPlaying) { return; }
+ var registerName = macroModeState.latestRegister;
+ var register = vimGlobalState.registerController.getRegister(registerName);
+ if (register && register.pushSearchQuery) {
+ register.pushSearchQuery(query);
+ }
+ }
+
+ /**
+ * Listens for changes made in insert mode.
+ * Should only be active in insert mode.
+ */
+ function onChange(cm, changeObj) {
+ var macroModeState = vimGlobalState.macroModeState;
+ var lastChange = macroModeState.lastInsertModeChanges;
+ if (!macroModeState.isPlaying) {
+ while(changeObj) {
+ lastChange.expectCursorActivityForChange = true;
+ if (lastChange.ignoreCount > 1) {
+ lastChange.ignoreCount--;
+ } else if (changeObj.origin == '+input' || changeObj.origin == 'paste'
+ || changeObj.origin === undefined /* only in testing */) {
+ var selectionCount = cm.listSelections().length;
+ if (selectionCount > 1)
+ lastChange.ignoreCount = selectionCount;
+ var text = changeObj.text.join('\n');
+ if (lastChange.maybeReset) {
+ lastChange.changes = [];
+ lastChange.maybeReset = false;
+ }
+ if (text) {
+ if (cm.state.overwrite && !/\n/.test(text)) {
+ lastChange.changes.push([text]);
+ } else {
+ lastChange.changes.push(text);
+ }
+ }
+ }
+ // Change objects may be chained with next.
+ changeObj = changeObj.next;
+ }
+ }
+ }
+
+ /**
+ * Listens for any kind of cursor activity on CodeMirror.
+ */
+ function onCursorActivity(cm) {
+ var vim = cm.state.vim;
+ if (vim.insertMode) {
+ // Tracking cursor activity in insert mode (for macro support).
+ var macroModeState = vimGlobalState.macroModeState;
+ if (macroModeState.isPlaying) { return; }
+ var lastChange = macroModeState.lastInsertModeChanges;
+ if (lastChange.expectCursorActivityForChange) {
+ lastChange.expectCursorActivityForChange = false;
+ } else {
+ // Cursor moved outside the context of an edit. Reset the change.
+ lastChange.maybeReset = true;
+ }
+ } else if (!cm.curOp.isVimOp) {
+ handleExternalSelection(cm, vim);
+ }
+ if (vim.visualMode) {
+ updateFakeCursor(cm);
+ }
+ }
+ function updateFakeCursor(cm) {
+ var vim = cm.state.vim;
+ var from = clipCursorToContent(cm, copyCursor(vim.sel.head));
+ var to = offsetCursor(from, 0, 1);
+ if (vim.fakeCursor) {
+ vim.fakeCursor.clear();
+ }
+ vim.fakeCursor = cm.markText(from, to, {className: 'cm-animate-fat-cursor'});
+ }
+ function handleExternalSelection(cm, vim) {
+ var anchor = cm.getCursor('anchor');
+ var head = cm.getCursor('head');
+ // Enter or exit visual mode to match mouse selection.
+ if (vim.visualMode && !cm.somethingSelected()) {
+ exitVisualMode(cm, false);
+ } else if (!vim.visualMode && !vim.insertMode && cm.somethingSelected()) {
+ vim.visualMode = true;
+ vim.visualLine = false;
+ CodeMirror.signal(cm, "vim-mode-change", {mode: "visual"});
+ }
+ if (vim.visualMode) {
+ // Bind CodeMirror selection model to vim selection model.
+ // Mouse selections are considered visual characterwise.
+ var headOffset = !cursorIsBefore(head, anchor) ? -1 : 0;
+ var anchorOffset = cursorIsBefore(head, anchor) ? -1 : 0;
+ head = offsetCursor(head, 0, headOffset);
+ anchor = offsetCursor(anchor, 0, anchorOffset);
+ vim.sel = {
+ anchor: anchor,
+ head: head
+ };
+ updateMark(cm, vim, '<', cursorMin(head, anchor));
+ updateMark(cm, vim, '>', cursorMax(head, anchor));
+ } else if (!vim.insertMode) {
+ // Reset lastHPos if selection was modified by something outside of vim mode e.g. by mouse.
+ vim.lastHPos = cm.getCursor().ch;
+ }
+ }
+
+ /** Wrapper for special keys pressed in insert mode */
+ function InsertModeKey(keyName) {
+ this.keyName = keyName;
+ }
+
+ /**
+ * Handles raw key down events from the text area.
+ * - Should only be active in insert mode.
+ * - For recording deletes in insert mode.
+ */
+ function onKeyEventTargetKeyDown(e) {
+ var macroModeState = vimGlobalState.macroModeState;
+ var lastChange = macroModeState.lastInsertModeChanges;
+ var keyName = CodeMirror.keyName(e);
+ if (!keyName) { return; }
+ function onKeyFound() {
+ if (lastChange.maybeReset) {
+ lastChange.changes = [];
+ lastChange.maybeReset = false;
+ }
+ lastChange.changes.push(new InsertModeKey(keyName));
+ return true;
+ }
+ if (keyName.indexOf('Delete') != -1 || keyName.indexOf('Backspace') != -1) {
+ CodeMirror.lookupKey(keyName, 'vim-insert', onKeyFound);
+ }
+ }
+
+ /**
+ * Repeats the last edit, which includes exactly 1 command and at most 1
+ * insert. Operator and motion commands are read from lastEditInputState,
+ * while action commands are read from lastEditActionCommand.
+ *
+ * If repeatForInsert is true, then the function was called by
+ * exitInsertMode to repeat the insert mode changes the user just made. The
+ * corresponding enterInsertMode call was made with a count.
+ */
+ function repeatLastEdit(cm, vim, repeat, repeatForInsert) {
+ var macroModeState = vimGlobalState.macroModeState;
+ macroModeState.isPlaying = true;
+ var isAction = !!vim.lastEditActionCommand;
+ var cachedInputState = vim.inputState;
+ function repeatCommand() {
+ if (isAction) {
+ commandDispatcher.processAction(cm, vim, vim.lastEditActionCommand);
+ } else {
+ commandDispatcher.evalInput(cm, vim);
+ }
+ }
+ function repeatInsert(repeat) {
+ if (macroModeState.lastInsertModeChanges.changes.length > 0) {
+ // For some reason, repeat cw in desktop VIM does not repeat
+ // insert mode changes. Will conform to that behavior.
+ repeat = !vim.lastEditActionCommand ? 1 : repeat;
+ var changeObject = macroModeState.lastInsertModeChanges;
+ repeatInsertModeChanges(cm, changeObject.changes, repeat);
+ }
+ }
+ vim.inputState = vim.lastEditInputState;
+ if (isAction && vim.lastEditActionCommand.interlaceInsertRepeat) {
+ // o and O repeat have to be interlaced with insert repeats so that the
+ // insertions appear on separate lines instead of the last line.
+ for (var i = 0; i < repeat; i++) {
+ repeatCommand();
+ repeatInsert(1);
+ }
+ } else {
+ if (!repeatForInsert) {
+ // Hack to get the cursor to end up at the right place. If I is
+ // repeated in insert mode repeat, cursor will be 1 insert
+ // change set left of where it should be.
+ repeatCommand();
+ }
+ repeatInsert(repeat);
+ }
+ vim.inputState = cachedInputState;
+ if (vim.insertMode && !repeatForInsert) {
+ // Don't exit insert mode twice. If repeatForInsert is set, then we
+ // were called by an exitInsertMode call lower on the stack.
+ exitInsertMode(cm);
+ }
+ macroModeState.isPlaying = false;
+ }
+
+ function repeatInsertModeChanges(cm, changes, repeat) {
+ function keyHandler(binding) {
+ if (typeof binding == 'string') {
+ CodeMirror.commands[binding](cm);
+ } else {
+ binding(cm);
+ }
+ return true;
+ }
+ var head = cm.getCursor('head');
+ var visualBlock = vimGlobalState.macroModeState.lastInsertModeChanges.visualBlock;
+ if (visualBlock) {
+ // Set up block selection again for repeating the changes.
+ selectForInsert(cm, head, visualBlock + 1);
+ repeat = cm.listSelections().length;
+ cm.setCursor(head);
+ }
+ for (var i = 0; i < repeat; i++) {
+ if (visualBlock) {
+ cm.setCursor(offsetCursor(head, i, 0));
+ }
+ for (var j = 0; j < changes.length; j++) {
+ var change = changes[j];
+ if (change instanceof InsertModeKey) {
+ CodeMirror.lookupKey(change.keyName, 'vim-insert', keyHandler);
+ } else if (typeof change == "string") {
+ var cur = cm.getCursor();
+ cm.replaceRange(change, cur, cur);
+ } else {
+ var start = cm.getCursor();
+ var end = offsetCursor(start, 0, change[0].length);
+ cm.replaceRange(change[0], start, end);
+ }
+ }
+ }
+ if (visualBlock) {
+ cm.setCursor(offsetCursor(head, 0, 1));
+ }
+ }
+
+ resetVimGlobalState();
+ return vimApi;
+ };
+ // Initialize Vim and make it available as an API.
+ CodeMirror.Vim = Vim();
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.css b/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.css
new file mode 100644
index 0000000000..a64f97c777
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.css
@@ -0,0 +1,350 @@
+/* BASICS */
+
+.CodeMirror {
+ /* Set height, width, borders, and global font properties here */
+ font-family: monospace;
+ height: 300px;
+ color: black;
+ direction: ltr;
+}
+
+/* PADDING */
+
+.CodeMirror-lines {
+ padding: 4px 0; /* Vertical padding around content */
+}
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
+ padding: 0 4px; /* Horizontal padding of content */
+}
+
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ background-color: white; /* The little square between H and V scrollbars */
+}
+
+/* GUTTER */
+
+.CodeMirror-gutters {
+ border-right: 1px solid #ddd;
+ background-color: #f7f7f7;
+ white-space: nowrap;
+}
+.CodeMirror-linenumbers {}
+.CodeMirror-linenumber {
+ padding: 0 3px 0 5px;
+ min-width: 20px;
+ text-align: right;
+ color: #999;
+ white-space: nowrap;
+}
+
+.CodeMirror-guttermarker { color: black; }
+.CodeMirror-guttermarker-subtle { color: #999; }
+
+/* CURSOR */
+
+.CodeMirror-cursor {
+ border-left: 1px solid black;
+ border-right: none;
+ width: 0;
+}
+/* Shown when moving in bi-directional text */
+.CodeMirror div.CodeMirror-secondarycursor {
+ border-left: 1px solid silver;
+}
+.cm-fat-cursor .CodeMirror-cursor {
+ width: auto;
+ border: 0 !important;
+ background: #7e7;
+}
+.cm-fat-cursor div.CodeMirror-cursors {
+ z-index: 1;
+}
+.cm-fat-cursor-mark {
+ background-color: rgba(20, 255, 20, 0.5);
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+}
+.cm-animate-fat-cursor {
+ width: auto;
+ border: 0;
+ -webkit-animation: blink 1.06s steps(1) infinite;
+ -moz-animation: blink 1.06s steps(1) infinite;
+ animation: blink 1.06s steps(1) infinite;
+ background-color: #7e7;
+}
+@-moz-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+@-webkit-keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+@keyframes blink {
+ 0% {}
+ 50% { background-color: transparent; }
+ 100% {}
+}
+
+/* Can style cursor different in overwrite (non-insert) mode */
+.CodeMirror-overwrite .CodeMirror-cursor {}
+
+.cm-tab { display: inline-block; text-decoration: inherit; }
+
+.CodeMirror-rulers {
+ position: absolute;
+ left: 0; right: 0; top: -50px; bottom: 0;
+ overflow: hidden;
+}
+.CodeMirror-ruler {
+ border-left: 1px solid #ccc;
+ top: 0; bottom: 0;
+ position: absolute;
+}
+
+/* DEFAULT THEME */
+
+.cm-s-default .cm-header {color: blue;}
+.cm-s-default .cm-quote {color: #090;}
+.cm-negative {color: #d44;}
+.cm-positive {color: #292;}
+.cm-header, .cm-strong {font-weight: bold;}
+.cm-em {font-style: italic;}
+.cm-link {text-decoration: underline;}
+.cm-strikethrough {text-decoration: line-through;}
+
+.cm-s-default .cm-keyword {color: #708;}
+.cm-s-default .cm-atom {color: #219;}
+.cm-s-default .cm-number {color: #164;}
+.cm-s-default .cm-def {color: #00f;}
+.cm-s-default .cm-variable,
+.cm-s-default .cm-punctuation,
+.cm-s-default .cm-property,
+.cm-s-default .cm-operator {}
+.cm-s-default .cm-variable-2 {color: #05a;}
+.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
+.cm-s-default .cm-comment {color: #a50;}
+.cm-s-default .cm-string {color: #a11;}
+.cm-s-default .cm-string-2 {color: #f50;}
+.cm-s-default .cm-meta {color: #555;}
+.cm-s-default .cm-qualifier {color: #555;}
+.cm-s-default .cm-builtin {color: #30a;}
+.cm-s-default .cm-bracket {color: #997;}
+.cm-s-default .cm-tag {color: #170;}
+.cm-s-default .cm-attribute {color: #00c;}
+.cm-s-default .cm-hr {color: #999;}
+.cm-s-default .cm-link {color: #00c;}
+
+.cm-s-default .cm-error {color: #f00;}
+.cm-invalidchar {color: #f00;}
+
+.CodeMirror-composing { border-bottom: 2px solid; }
+
+/* Default styles for common addons */
+
+div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
+div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
+.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
+.CodeMirror-activeline-background {background: #e8f2ff;}
+
+/* STOP */
+
+/* The rest of this file contains styles related to the mechanics of
+ the editor. You probably shouldn't touch them. */
+
+.CodeMirror {
+ position: relative;
+ overflow: hidden;
+ background: white;
+}
+
+.CodeMirror-scroll {
+ overflow: scroll !important; /* Things will break if this is overridden */
+ /* 50px is the magic margin used to hide the element's real scrollbars */
+ /* See overflow: hidden in .CodeMirror */
+ margin-bottom: -50px; margin-right: -50px;
+ padding-bottom: 50px;
+ height: 100%;
+ outline: none; /* Prevent dragging from highlighting the element */
+ position: relative;
+}
+.CodeMirror-sizer {
+ position: relative;
+ border-right: 50px solid transparent;
+}
+
+/* The fake, visible scrollbars. Used to force redraw during scrolling
+ before actual scrolling happens, thus preventing shaking and
+ flickering artifacts. */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ position: absolute;
+ z-index: 6;
+ display: none;
+ outline: none;
+}
+.CodeMirror-vscrollbar {
+ right: 0; top: 0;
+ overflow-x: hidden;
+ overflow-y: scroll;
+}
+.CodeMirror-hscrollbar {
+ bottom: 0; left: 0;
+ overflow-y: hidden;
+ overflow-x: scroll;
+}
+.CodeMirror-scrollbar-filler {
+ right: 0; bottom: 0;
+}
+.CodeMirror-gutter-filler {
+ left: 0; bottom: 0;
+}
+
+.CodeMirror-gutters {
+ position: absolute; left: 0; top: 0;
+ min-height: 100%;
+ z-index: 3;
+}
+.CodeMirror-gutter {
+ white-space: normal;
+ height: 100%;
+ display: inline-block;
+ vertical-align: top;
+ margin-bottom: -50px;
+}
+.CodeMirror-gutter-wrapper {
+ position: absolute;
+ z-index: 4;
+ background: none !important;
+ border: none !important;
+}
+.CodeMirror-gutter-background {
+ position: absolute;
+ top: 0; bottom: 0;
+ z-index: 4;
+}
+.CodeMirror-gutter-elt {
+ position: absolute;
+ cursor: default;
+ z-index: 4;
+}
+.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
+.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
+
+.CodeMirror-lines {
+ cursor: text;
+ min-height: 1px; /* prevents collapsing before first draw */
+}
+.CodeMirror pre.CodeMirror-line,
+.CodeMirror pre.CodeMirror-line-like {
+ /* Reset some styles that the rest of the page might have set */
+ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
+ border-width: 0;
+ background: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ margin: 0;
+ white-space: pre;
+ word-wrap: normal;
+ line-height: inherit;
+ color: inherit;
+ z-index: 2;
+ position: relative;
+ overflow: visible;
+ -webkit-tap-highlight-color: transparent;
+ -webkit-font-variant-ligatures: contextual;
+ font-variant-ligatures: contextual;
+}
+.CodeMirror-wrap pre.CodeMirror-line,
+.CodeMirror-wrap pre.CodeMirror-line-like {
+ word-wrap: break-word;
+ white-space: pre-wrap;
+ word-break: normal;
+}
+
+.CodeMirror-linebackground {
+ position: absolute;
+ left: 0; right: 0; top: 0; bottom: 0;
+ z-index: 0;
+}
+
+.CodeMirror-linewidget {
+ position: relative;
+ z-index: 2;
+ padding: 0.1px; /* Force widget margins to stay inside of the container */
+}
+
+.CodeMirror-widget {}
+
+.CodeMirror-rtl pre { direction: rtl; }
+
+.CodeMirror-code {
+ outline: none;
+}
+
+/* Force content-box sizing for the elements where we expect it */
+.CodeMirror-scroll,
+.CodeMirror-sizer,
+.CodeMirror-gutter,
+.CodeMirror-gutters,
+.CodeMirror-linenumber {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+}
+
+.CodeMirror-measure {
+ position: absolute;
+ width: 100%;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+}
+
+.CodeMirror-cursor {
+ position: absolute;
+ pointer-events: none;
+}
+.CodeMirror-measure pre { position: static; }
+
+div.CodeMirror-cursors {
+ visibility: hidden;
+ position: relative;
+ z-index: 3;
+}
+div.CodeMirror-dragcursors {
+ visibility: visible;
+}
+
+.CodeMirror-focused div.CodeMirror-cursors {
+ visibility: visible;
+}
+
+.CodeMirror-selected { background: #d9d9d9; }
+.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
+.CodeMirror-crosshair { cursor: crosshair; }
+.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
+.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
+
+.cm-searching {
+ background-color: #ffa;
+ background-color: rgba(255, 255, 0, .4);
+}
+
+/* Used to force a border model for a node */
+.cm-force-border { padding-right: .1px; }
+
+@media print {
+ /* Hide the cursor when printing */
+ .CodeMirror div.CodeMirror-cursors {
+ visibility: hidden;
+ }
+}
+
+/* See issue #2901 */
+.cm-tab-wrap-hack:after { content: ''; }
+
+/* Help users use markselection to safely style text background */
+span.CodeMirror-selectedtext { background: none; }
diff --git a/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js b/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js
new file mode 100644
index 0000000000..88a93243f2
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js
@@ -0,0 +1,9788 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// This is CodeMirror (https://codemirror.net), a code editor
+// implemented in JavaScript on top of the browser's DOM.
+//
+// You can find some technical background for some of the code below
+// at http://marijnhaverbeke.nl/blog/#cm-internals .
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global = global || self, global.CodeMirror = factory());
+}(this, (function () { 'use strict';
+
+ // Kludges for bugs and behavior differences that can't be feature
+ // detected are enabled based on userAgent etc sniffing.
+ var userAgent = navigator.userAgent;
+ var platform = navigator.platform;
+
+ var gecko = /gecko\/\d/i.test(userAgent);
+ var ie_upto10 = /MSIE \d/.test(userAgent);
+ var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent);
+ var edge = /Edge\/(\d+)/.exec(userAgent);
+ var ie = ie_upto10 || ie_11up || edge;
+ var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]);
+ var webkit = !edge && /WebKit\//.test(userAgent);
+ var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent);
+ var chrome = !edge && /Chrome\//.test(userAgent);
+ var presto = /Opera\//.test(userAgent);
+ var safari = /Apple Computer/.test(navigator.vendor);
+ var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent);
+ var phantom = /PhantomJS/.test(userAgent);
+
+ var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent);
+ var android = /Android/.test(userAgent);
+ // This is woefully incomplete. Suggestions for alternative methods welcome.
+ var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent);
+ var mac = ios || /Mac/.test(platform);
+ var chromeOS = /\bCrOS\b/.test(userAgent);
+ var windows = /win/i.test(platform);
+
+ var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/);
+ if (presto_version) { presto_version = Number(presto_version[1]); }
+ if (presto_version && presto_version >= 15) { presto = false; webkit = true; }
+ // Some browsers use the wrong event properties to signal cmd/ctrl on OS X
+ var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11));
+ var captureRightClick = gecko || (ie && ie_version >= 9);
+
+ function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
+
+ var rmClass = function(node, cls) {
+ var current = node.className;
+ var match = classTest(cls).exec(current);
+ if (match) {
+ var after = current.slice(match.index + match[0].length);
+ node.className = current.slice(0, match.index) + (after ? match[1] + after : "");
+ }
+ };
+
+ function removeChildren(e) {
+ for (var count = e.childNodes.length; count > 0; --count)
+ { e.removeChild(e.firstChild); }
+ return e
+ }
+
+ function removeChildrenAndAdd(parent, e) {
+ return removeChildren(parent).appendChild(e)
+ }
+
+ function elt(tag, content, className, style) {
+ var e = document.createElement(tag);
+ if (className) { e.className = className; }
+ if (style) { e.style.cssText = style; }
+ if (typeof content == "string") { e.appendChild(document.createTextNode(content)); }
+ else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } }
+ return e
+ }
+ // wrapper for elt, which removes the elt from the accessibility tree
+ function eltP(tag, content, className, style) {
+ var e = elt(tag, content, className, style);
+ e.setAttribute("role", "presentation");
+ return e
+ }
+
+ var range;
+ if (document.createRange) { range = function(node, start, end, endNode) {
+ var r = document.createRange();
+ r.setEnd(endNode || node, end);
+ r.setStart(node, start);
+ return r
+ }; }
+ else { range = function(node, start, end) {
+ var r = document.body.createTextRange();
+ try { r.moveToElementText(node.parentNode); }
+ catch(e) { return r }
+ r.collapse(true);
+ r.moveEnd("character", end);
+ r.moveStart("character", start);
+ return r
+ }; }
+
+ function contains(parent, child) {
+ if (child.nodeType == 3) // Android browser always returns false when child is a textnode
+ { child = child.parentNode; }
+ if (parent.contains)
+ { return parent.contains(child) }
+ do {
+ if (child.nodeType == 11) { child = child.host; }
+ if (child == parent) { return true }
+ } while (child = child.parentNode)
+ }
+
+ function activeElt() {
+ // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement.
+ // IE < 10 will throw when accessed while the page is loading or in an iframe.
+ // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable.
+ var activeElement;
+ try {
+ activeElement = document.activeElement;
+ } catch(e) {
+ activeElement = document.body || null;
+ }
+ while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement)
+ { activeElement = activeElement.shadowRoot.activeElement; }
+ return activeElement
+ }
+
+ function addClass(node, cls) {
+ var current = node.className;
+ if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; }
+ }
+ function joinClasses(a, b) {
+ var as = a.split(" ");
+ for (var i = 0; i < as.length; i++)
+ { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } }
+ return b
+ }
+
+ var selectInput = function(node) { node.select(); };
+ if (ios) // Mobile Safari apparently has a bug where select() is broken.
+ { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; }
+ else if (ie) // Suppress mysterious IE10 errors
+ { selectInput = function(node) { try { node.select(); } catch(_e) {} }; }
+
+ function bind(f) {
+ var args = Array.prototype.slice.call(arguments, 1);
+ return function(){return f.apply(null, args)}
+ }
+
+ function copyObj(obj, target, overwrite) {
+ if (!target) { target = {}; }
+ for (var prop in obj)
+ { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
+ { target[prop] = obj[prop]; } }
+ return target
+ }
+
+ // Counts the column offset in a string, taking tabs into account.
+ // Used mostly to find indentation.
+ function countColumn(string, end, tabSize, startIndex, startValue) {
+ if (end == null) {
+ end = string.search(/[^\s\u00a0]/);
+ if (end == -1) { end = string.length; }
+ }
+ for (var i = startIndex || 0, n = startValue || 0;;) {
+ var nextTab = string.indexOf("\t", i);
+ if (nextTab < 0 || nextTab >= end)
+ { return n + (end - i) }
+ n += nextTab - i;
+ n += tabSize - (n % tabSize);
+ i = nextTab + 1;
+ }
+ }
+
+ var Delayed = function() {
+ this.id = null;
+ this.f = null;
+ this.time = 0;
+ this.handler = bind(this.onTimeout, this);
+ };
+ Delayed.prototype.onTimeout = function (self) {
+ self.id = 0;
+ if (self.time <= +new Date) {
+ self.f();
+ } else {
+ setTimeout(self.handler, self.time - +new Date);
+ }
+ };
+ Delayed.prototype.set = function (ms, f) {
+ this.f = f;
+ var time = +new Date + ms;
+ if (!this.id || time < this.time) {
+ clearTimeout(this.id);
+ this.id = setTimeout(this.handler, ms);
+ this.time = time;
+ }
+ };
+
+ function indexOf(array, elt) {
+ for (var i = 0; i < array.length; ++i)
+ { if (array[i] == elt) { return i } }
+ return -1
+ }
+
+ // Number of pixels added to scroller and sizer to hide scrollbar
+ var scrollerGap = 50;
+
+ // Returned or thrown by various protocols to signal 'I'm not
+ // handling this'.
+ var Pass = {toString: function(){return "CodeMirror.Pass"}};
+
+ // Reused option objects for setSelection & friends
+ var sel_dontScroll = {scroll: false}, sel_mouse = {origin: "*mouse"}, sel_move = {origin: "+move"};
+
+ // The inverse of countColumn -- find the offset that corresponds to
+ // a particular column.
+ function findColumn(string, goal, tabSize) {
+ for (var pos = 0, col = 0;;) {
+ var nextTab = string.indexOf("\t", pos);
+ if (nextTab == -1) { nextTab = string.length; }
+ var skipped = nextTab - pos;
+ if (nextTab == string.length || col + skipped >= goal)
+ { return pos + Math.min(skipped, goal - col) }
+ col += nextTab - pos;
+ col += tabSize - (col % tabSize);
+ pos = nextTab + 1;
+ if (col >= goal) { return pos }
+ }
+ }
+
+ var spaceStrs = [""];
+ function spaceStr(n) {
+ while (spaceStrs.length <= n)
+ { spaceStrs.push(lst(spaceStrs) + " "); }
+ return spaceStrs[n]
+ }
+
+ function lst(arr) { return arr[arr.length-1] }
+
+ function map(array, f) {
+ var out = [];
+ for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); }
+ return out
+ }
+
+ function insertSorted(array, value, score) {
+ var pos = 0, priority = score(value);
+ while (pos < array.length && score(array[pos]) <= priority) { pos++; }
+ array.splice(pos, 0, value);
+ }
+
+ function nothing() {}
+
+ function createObj(base, props) {
+ var inst;
+ if (Object.create) {
+ inst = Object.create(base);
+ } else {
+ nothing.prototype = base;
+ inst = new nothing();
+ }
+ if (props) { copyObj(props, inst); }
+ return inst
+ }
+
+ var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;
+ function isWordCharBasic(ch) {
+ return /\w/.test(ch) || ch > "\x80" &&
+ (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch))
+ }
+ function isWordChar(ch, helper) {
+ if (!helper) { return isWordCharBasic(ch) }
+ if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true }
+ return helper.test(ch)
+ }
+
+ function isEmpty(obj) {
+ for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } }
+ return true
+ }
+
+ // Extending unicode characters. A series of a non-extending char +
+ // any number of extending chars is treated as a single unit as far
+ // as editing and measuring is concerned. This is not fully correct,
+ // since some scripts/fonts/browsers also treat other configurations
+ // of code points as a group.
+ var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;
+ function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) }
+
+ // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range.
+ function skipExtendingChars(str, pos, dir) {
+ while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; }
+ return pos
+ }
+
+ // Returns the value from the range [`from`; `to`] that satisfies
+ // `pred` and is closest to `from`. Assumes that at least `to`
+ // satisfies `pred`. Supports `from` being greater than `to`.
+ function findFirst(pred, from, to) {
+ // At any point we are certain `to` satisfies `pred`, don't know
+ // whether `from` does.
+ var dir = from > to ? -1 : 1;
+ for (;;) {
+ if (from == to) { return from }
+ var midF = (from + to) / 2, mid = dir < 0 ? Math.ceil(midF) : Math.floor(midF);
+ if (mid == from) { return pred(mid) ? from : to }
+ if (pred(mid)) { to = mid; }
+ else { from = mid + dir; }
+ }
+ }
+
+ // BIDI HELPERS
+
+ function iterateBidiSections(order, from, to, f) {
+ if (!order) { return f(from, to, "ltr", 0) }
+ var found = false;
+ for (var i = 0; i < order.length; ++i) {
+ var part = order[i];
+ if (part.from < to && part.to > from || from == to && part.to == from) {
+ f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr", i);
+ found = true;
+ }
+ }
+ if (!found) { f(from, to, "ltr"); }
+ }
+
+ var bidiOther = null;
+ function getBidiPartAt(order, ch, sticky) {
+ var found;
+ bidiOther = null;
+ for (var i = 0; i < order.length; ++i) {
+ var cur = order[i];
+ if (cur.from < ch && cur.to > ch) { return i }
+ if (cur.to == ch) {
+ if (cur.from != cur.to && sticky == "before") { found = i; }
+ else { bidiOther = i; }
+ }
+ if (cur.from == ch) {
+ if (cur.from != cur.to && sticky != "before") { found = i; }
+ else { bidiOther = i; }
+ }
+ }
+ return found != null ? found : bidiOther
+ }
+
+ // Bidirectional ordering algorithm
+ // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm
+ // that this (partially) implements.
+
+ // One-char codes used for character types:
+ // L (L): Left-to-Right
+ // R (R): Right-to-Left
+ // r (AL): Right-to-Left Arabic
+ // 1 (EN): European Number
+ // + (ES): European Number Separator
+ // % (ET): European Number Terminator
+ // n (AN): Arabic Number
+ // , (CS): Common Number Separator
+ // m (NSM): Non-Spacing Mark
+ // b (BN): Boundary Neutral
+ // s (B): Paragraph Separator
+ // t (S): Segment Separator
+ // w (WS): Whitespace
+ // N (ON): Other Neutrals
+
+ // Returns null if characters are ordered as they appear
+ // (left-to-right), or an array of sections ({from, to, level}
+ // objects) in the order in which they occur visually.
+ var bidiOrdering = (function() {
+ // Character types for codepoints 0 to 0xff
+ var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN";
+ // Character types for codepoints 0x600 to 0x6f9
+ var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111";
+ function charType(code) {
+ if (code <= 0xf7) { return lowTypes.charAt(code) }
+ else if (0x590 <= code && code <= 0x5f4) { return "R" }
+ else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
+ else if (0x6ee <= code && code <= 0x8ac) { return "r" }
+ else if (0x2000 <= code && code <= 0x200b) { return "w" }
+ else if (code == 0x200c) { return "b" }
+ else { return "L" }
+ }
+
+ var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
+ var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/;
+
+ function BidiSpan(level, from, to) {
+ this.level = level;
+ this.from = from; this.to = to;
+ }
+
+ return function(str, direction) {
+ var outerType = direction == "ltr" ? "L" : "R";
+
+ if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false }
+ var len = str.length, types = [];
+ for (var i = 0; i < len; ++i)
+ { types.push(charType(str.charCodeAt(i))); }
+
+ // W1. Examine each non-spacing mark (NSM) in the level run, and
+ // change the type of the NSM to the type of the previous
+ // character. If the NSM is at the start of the level run, it will
+ // get the type of sor.
+ for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) {
+ var type = types[i$1];
+ if (type == "m") { types[i$1] = prev; }
+ else { prev = type; }
+ }
+
+ // W2. Search backwards from each instance of a European number
+ // until the first strong type (R, L, AL, or sor) is found. If an
+ // AL is found, change the type of the European number to Arabic
+ // number.
+ // W3. Change all ALs to R.
+ for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) {
+ var type$1 = types[i$2];
+ if (type$1 == "1" && cur == "r") { types[i$2] = "n"; }
+ else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } }
+ }
+
+ // W4. A single European separator between two European numbers
+ // changes to a European number. A single common separator between
+ // two numbers of the same type changes to that type.
+ for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) {
+ var type$2 = types[i$3];
+ if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; }
+ else if (type$2 == "," && prev$1 == types[i$3+1] &&
+ (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; }
+ prev$1 = type$2;
+ }
+
+ // W5. A sequence of European terminators adjacent to European
+ // numbers changes to all European numbers.
+ // W6. Otherwise, separators and terminators change to Other
+ // Neutral.
+ for (var i$4 = 0; i$4 < len; ++i$4) {
+ var type$3 = types[i$4];
+ if (type$3 == ",") { types[i$4] = "N"; }
+ else if (type$3 == "%") {
+ var end = (void 0);
+ for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {}
+ var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N";
+ for (var j = i$4; j < end; ++j) { types[j] = replace; }
+ i$4 = end - 1;
+ }
+ }
+
+ // W7. Search backwards from each instance of a European number
+ // until the first strong type (R, L, or sor) is found. If an L is
+ // found, then change the type of the European number to L.
+ for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) {
+ var type$4 = types[i$5];
+ if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; }
+ else if (isStrong.test(type$4)) { cur$1 = type$4; }
+ }
+
+ // N1. A sequence of neutrals takes the direction of the
+ // surrounding strong text if the text on both sides has the same
+ // direction. European and Arabic numbers act as if they were R in
+ // terms of their influence on neutrals. Start-of-level-run (sor)
+ // and end-of-level-run (eor) are used at level run boundaries.
+ // N2. Any remaining neutrals take the embedding direction.
+ for (var i$6 = 0; i$6 < len; ++i$6) {
+ if (isNeutral.test(types[i$6])) {
+ var end$1 = (void 0);
+ for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {}
+ var before = (i$6 ? types[i$6-1] : outerType) == "L";
+ var after = (end$1 < len ? types[end$1] : outerType) == "L";
+ var replace$1 = before == after ? (before ? "L" : "R") : outerType;
+ for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; }
+ i$6 = end$1 - 1;
+ }
+ }
+
+ // Here we depart from the documented algorithm, in order to avoid
+ // building up an actual levels array. Since there are only three
+ // levels (0, 1, 2) in an implementation that doesn't take
+ // explicit embedding into account, we can build up the order on
+ // the fly, without following the level-based algorithm.
+ var order = [], m;
+ for (var i$7 = 0; i$7 < len;) {
+ if (countsAsLeft.test(types[i$7])) {
+ var start = i$7;
+ for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {}
+ order.push(new BidiSpan(0, start, i$7));
+ } else {
+ var pos = i$7, at = order.length, isRTL = direction == "rtl" ? 1 : 0;
+ for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {}
+ for (var j$2 = pos; j$2 < i$7;) {
+ if (countsAsNum.test(types[j$2])) {
+ if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); at += isRTL; }
+ var nstart = j$2;
+ for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {}
+ order.splice(at, 0, new BidiSpan(2, nstart, j$2));
+ at += isRTL;
+ pos = j$2;
+ } else { ++j$2; }
+ }
+ if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); }
+ }
+ }
+ if (direction == "ltr") {
+ if (order[0].level == 1 && (m = str.match(/^\s+/))) {
+ order[0].from = m[0].length;
+ order.unshift(new BidiSpan(0, 0, m[0].length));
+ }
+ if (lst(order).level == 1 && (m = str.match(/\s+$/))) {
+ lst(order).to -= m[0].length;
+ order.push(new BidiSpan(0, len - m[0].length, len));
+ }
+ }
+
+ return direction == "rtl" ? order.reverse() : order
+ }
+ })();
+
+ // Get the bidi ordering for the given line (and cache it). Returns
+ // false for lines that are fully left-to-right, and an array of
+ // BidiSpan objects otherwise.
+ function getOrder(line, direction) {
+ var order = line.order;
+ if (order == null) { order = line.order = bidiOrdering(line.text, direction); }
+ return order
+ }
+
+ // EVENT HANDLING
+
+ // Lightweight event framework. on/off also work on DOM nodes,
+ // registering native DOM handlers.
+
+ var noHandlers = [];
+
+ var on = function(emitter, type, f) {
+ if (emitter.addEventListener) {
+ emitter.addEventListener(type, f, false);
+ } else if (emitter.attachEvent) {
+ emitter.attachEvent("on" + type, f);
+ } else {
+ var map = emitter._handlers || (emitter._handlers = {});
+ map[type] = (map[type] || noHandlers).concat(f);
+ }
+ };
+
+ function getHandlers(emitter, type) {
+ return emitter._handlers && emitter._handlers[type] || noHandlers
+ }
+
+ function off(emitter, type, f) {
+ if (emitter.removeEventListener) {
+ emitter.removeEventListener(type, f, false);
+ } else if (emitter.detachEvent) {
+ emitter.detachEvent("on" + type, f);
+ } else {
+ var map = emitter._handlers, arr = map && map[type];
+ if (arr) {
+ var index = indexOf(arr, f);
+ if (index > -1)
+ { map[type] = arr.slice(0, index).concat(arr.slice(index + 1)); }
+ }
+ }
+ }
+
+ function signal(emitter, type /*, values...*/) {
+ var handlers = getHandlers(emitter, type);
+ if (!handlers.length) { return }
+ var args = Array.prototype.slice.call(arguments, 2);
+ for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); }
+ }
+
+ // The DOM events that CodeMirror handles can be overridden by
+ // registering a (non-DOM) handler on the editor for the event name,
+ // and preventDefault-ing the event in that handler.
+ function signalDOMEvent(cm, e, override) {
+ if (typeof e == "string")
+ { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; }
+ signal(cm, override || e.type, cm, e);
+ return e_defaultPrevented(e) || e.codemirrorIgnore
+ }
+
+ function signalCursorActivity(cm) {
+ var arr = cm._handlers && cm._handlers.cursorActivity;
+ if (!arr) { return }
+ var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []);
+ for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1)
+ { set.push(arr[i]); } }
+ }
+
+ function hasHandler(emitter, type) {
+ return getHandlers(emitter, type).length > 0
+ }
+
+ // Add on and off methods to a constructor's prototype, to make
+ // registering events on such objects more convenient.
+ function eventMixin(ctor) {
+ ctor.prototype.on = function(type, f) {on(this, type, f);};
+ ctor.prototype.off = function(type, f) {off(this, type, f);};
+ }
+
+ // Due to the fact that we still support jurassic IE versions, some
+ // compatibility wrappers are needed.
+
+ function e_preventDefault(e) {
+ if (e.preventDefault) { e.preventDefault(); }
+ else { e.returnValue = false; }
+ }
+ function e_stopPropagation(e) {
+ if (e.stopPropagation) { e.stopPropagation(); }
+ else { e.cancelBubble = true; }
+ }
+ function e_defaultPrevented(e) {
+ return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false
+ }
+ function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);}
+
+ function e_target(e) {return e.target || e.srcElement}
+ function e_button(e) {
+ var b = e.which;
+ if (b == null) {
+ if (e.button & 1) { b = 1; }
+ else if (e.button & 2) { b = 3; }
+ else if (e.button & 4) { b = 2; }
+ }
+ if (mac && e.ctrlKey && b == 1) { b = 3; }
+ return b
+ }
+
+ // Detect drag-and-drop
+ var dragAndDrop = function() {
+ // There is *some* kind of drag-and-drop support in IE6-8, but I
+ // couldn't get it to work yet.
+ if (ie && ie_version < 9) { return false }
+ var div = elt('div');
+ return "draggable" in div || "dragDrop" in div
+ }();
+
+ var zwspSupported;
+ function zeroWidthElement(measure) {
+ if (zwspSupported == null) {
+ var test = elt("span", "\u200b");
+ removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")]));
+ if (measure.firstChild.offsetHeight != 0)
+ { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); }
+ }
+ var node = zwspSupported ? elt("span", "\u200b") :
+ elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px");
+ node.setAttribute("cm-text", "");
+ return node
+ }
+
+ // Feature-detect IE's crummy client rect reporting for bidi text
+ var badBidiRects;
+ function hasBadBidiRects(measure) {
+ if (badBidiRects != null) { return badBidiRects }
+ var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA"));
+ var r0 = range(txt, 0, 1).getBoundingClientRect();
+ var r1 = range(txt, 1, 2).getBoundingClientRect();
+ removeChildren(measure);
+ if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780)
+ return badBidiRects = (r1.right - r0.right < 3)
+ }
+
+ // See if "".split is the broken IE version, if so, provide an
+ // alternative way to split lines.
+ var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) {
+ var pos = 0, result = [], l = string.length;
+ while (pos <= l) {
+ var nl = string.indexOf("\n", pos);
+ if (nl == -1) { nl = string.length; }
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
+ var rt = line.indexOf("\r");
+ if (rt != -1) {
+ result.push(line.slice(0, rt));
+ pos += rt + 1;
+ } else {
+ result.push(line);
+ pos = nl + 1;
+ }
+ }
+ return result
+ } : function (string) { return string.split(/\r\n?|\n/); };
+
+ var hasSelection = window.getSelection ? function (te) {
+ try { return te.selectionStart != te.selectionEnd }
+ catch(e) { return false }
+ } : function (te) {
+ var range;
+ try {range = te.ownerDocument.selection.createRange();}
+ catch(e) {}
+ if (!range || range.parentElement() != te) { return false }
+ return range.compareEndPoints("StartToEnd", range) != 0
+ };
+
+ var hasCopyEvent = (function () {
+ var e = elt("div");
+ if ("oncopy" in e) { return true }
+ e.setAttribute("oncopy", "return;");
+ return typeof e.oncopy == "function"
+ })();
+
+ var badZoomedRects = null;
+ function hasBadZoomedRects(measure) {
+ if (badZoomedRects != null) { return badZoomedRects }
+ var node = removeChildrenAndAdd(measure, elt("span", "x"));
+ var normal = node.getBoundingClientRect();
+ var fromRange = range(node, 0, 1).getBoundingClientRect();
+ return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1
+ }
+
+ // Known modes, by name and by MIME
+ var modes = {}, mimeModes = {};
+
+ // Extra arguments are stored as the mode's dependencies, which is
+ // used by (legacy) mechanisms like loadmode.js to automatically
+ // load a mode. (Preferred mechanism is the require/define calls.)
+ function defineMode(name, mode) {
+ if (arguments.length > 2)
+ { mode.dependencies = Array.prototype.slice.call(arguments, 2); }
+ modes[name] = mode;
+ }
+
+ function defineMIME(mime, spec) {
+ mimeModes[mime] = spec;
+ }
+
+ // Given a MIME type, a {name, ...options} config object, or a name
+ // string, return a mode config object.
+ function resolveMode(spec) {
+ if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
+ spec = mimeModes[spec];
+ } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+ var found = mimeModes[spec.name];
+ if (typeof found == "string") { found = {name: found}; }
+ spec = createObj(found, spec);
+ spec.name = found.name;
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) {
+ return resolveMode("application/xml")
+ } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) {
+ return resolveMode("application/json")
+ }
+ if (typeof spec == "string") { return {name: spec} }
+ else { return spec || {name: "null"} }
+ }
+
+ // Given a mode spec (anything that resolveMode accepts), find and
+ // initialize an actual mode object.
+ function getMode(options, spec) {
+ spec = resolveMode(spec);
+ var mfactory = modes[spec.name];
+ if (!mfactory) { return getMode(options, "text/plain") }
+ var modeObj = mfactory(options, spec);
+ if (modeExtensions.hasOwnProperty(spec.name)) {
+ var exts = modeExtensions[spec.name];
+ for (var prop in exts) {
+ if (!exts.hasOwnProperty(prop)) { continue }
+ if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; }
+ modeObj[prop] = exts[prop];
+ }
+ }
+ modeObj.name = spec.name;
+ if (spec.helperType) { modeObj.helperType = spec.helperType; }
+ if (spec.modeProps) { for (var prop$1 in spec.modeProps)
+ { modeObj[prop$1] = spec.modeProps[prop$1]; } }
+
+ return modeObj
+ }
+
+ // This can be used to attach properties to mode objects from
+ // outside the actual mode definition.
+ var modeExtensions = {};
+ function extendMode(mode, properties) {
+ var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+ copyObj(properties, exts);
+ }
+
+ function copyState(mode, state) {
+ if (state === true) { return state }
+ if (mode.copyState) { return mode.copyState(state) }
+ var nstate = {};
+ for (var n in state) {
+ var val = state[n];
+ if (val instanceof Array) { val = val.concat([]); }
+ nstate[n] = val;
+ }
+ return nstate
+ }
+
+ // Given a mode and a state (for that mode), find the inner mode and
+ // state at the position that the state refers to.
+ function innerMode(mode, state) {
+ var info;
+ while (mode.innerMode) {
+ info = mode.innerMode(state);
+ if (!info || info.mode == mode) { break }
+ state = info.state;
+ mode = info.mode;
+ }
+ return info || {mode: mode, state: state}
+ }
+
+ function startState(mode, a1, a2) {
+ return mode.startState ? mode.startState(a1, a2) : true
+ }
+
+ // STRING STREAM
+
+ // Fed to the mode parsers, provides helper functions to make
+ // parsers more succinct.
+
+ var StringStream = function(string, tabSize, lineOracle) {
+ this.pos = this.start = 0;
+ this.string = string;
+ this.tabSize = tabSize || 8;
+ this.lastColumnPos = this.lastColumnValue = 0;
+ this.lineStart = 0;
+ this.lineOracle = lineOracle;
+ };
+
+ StringStream.prototype.eol = function () {return this.pos >= this.string.length};
+ StringStream.prototype.sol = function () {return this.pos == this.lineStart};
+ StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined};
+ StringStream.prototype.next = function () {
+ if (this.pos < this.string.length)
+ { return this.string.charAt(this.pos++) }
+ };
+ StringStream.prototype.eat = function (match) {
+ var ch = this.string.charAt(this.pos);
+ var ok;
+ if (typeof match == "string") { ok = ch == match; }
+ else { ok = ch && (match.test ? match.test(ch) : match(ch)); }
+ if (ok) {++this.pos; return ch}
+ };
+ StringStream.prototype.eatWhile = function (match) {
+ var start = this.pos;
+ while (this.eat(match)){}
+ return this.pos > start
+ };
+ StringStream.prototype.eatSpace = function () {
+ var start = this.pos;
+ while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this.pos; }
+ return this.pos > start
+ };
+ StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;};
+ StringStream.prototype.skipTo = function (ch) {
+ var found = this.string.indexOf(ch, this.pos);
+ if (found > -1) {this.pos = found; return true}
+ };
+ StringStream.prototype.backUp = function (n) {this.pos -= n;};
+ StringStream.prototype.column = function () {
+ if (this.lastColumnPos < this.start) {
+ this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
+ this.lastColumnPos = this.start;
+ }
+ return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+ };
+ StringStream.prototype.indentation = function () {
+ return countColumn(this.string, null, this.tabSize) -
+ (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0)
+ };
+ StringStream.prototype.match = function (pattern, consume, caseInsensitive) {
+ if (typeof pattern == "string") {
+ var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; };
+ var substr = this.string.substr(this.pos, pattern.length);
+ if (cased(substr) == cased(pattern)) {
+ if (consume !== false) { this.pos += pattern.length; }
+ return true
+ }
+ } else {
+ var match = this.string.slice(this.pos).match(pattern);
+ if (match && match.index > 0) { return null }
+ if (match && consume !== false) { this.pos += match[0].length; }
+ return match
+ }
+ };
+ StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)};
+ StringStream.prototype.hideFirstChars = function (n, inner) {
+ this.lineStart += n;
+ try { return inner() }
+ finally { this.lineStart -= n; }
+ };
+ StringStream.prototype.lookAhead = function (n) {
+ var oracle = this.lineOracle;
+ return oracle && oracle.lookAhead(n)
+ };
+ StringStream.prototype.baseToken = function () {
+ var oracle = this.lineOracle;
+ return oracle && oracle.baseToken(this.pos)
+ };
+
+ // Find the line object corresponding to the given line number.
+ function getLine(doc, n) {
+ n -= doc.first;
+ if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") }
+ var chunk = doc;
+ while (!chunk.lines) {
+ for (var i = 0;; ++i) {
+ var child = chunk.children[i], sz = child.chunkSize();
+ if (n < sz) { chunk = child; break }
+ n -= sz;
+ }
+ }
+ return chunk.lines[n]
+ }
+
+ // Get the part of a document between two positions, as an array of
+ // strings.
+ function getBetween(doc, start, end) {
+ var out = [], n = start.line;
+ doc.iter(start.line, end.line + 1, function (line) {
+ var text = line.text;
+ if (n == end.line) { text = text.slice(0, end.ch); }
+ if (n == start.line) { text = text.slice(start.ch); }
+ out.push(text);
+ ++n;
+ });
+ return out
+ }
+ // Get the lines between from and to, as array of strings.
+ function getLines(doc, from, to) {
+ var out = [];
+ doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value
+ return out
+ }
+
+ // Update the height of a line, propagating the height change
+ // upwards to parent nodes.
+ function updateLineHeight(line, height) {
+ var diff = height - line.height;
+ if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } }
+ }
+
+ // Given a line object, find its line number by walking up through
+ // its parent links.
+ function lineNo(line) {
+ if (line.parent == null) { return null }
+ var cur = line.parent, no = indexOf(cur.lines, line);
+ for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) {
+ for (var i = 0;; ++i) {
+ if (chunk.children[i] == cur) { break }
+ no += chunk.children[i].chunkSize();
+ }
+ }
+ return no + cur.first
+ }
+
+ // Find the line at the given vertical position, using the height
+ // information in the document tree.
+ function lineAtHeight(chunk, h) {
+ var n = chunk.first;
+ outer: do {
+ for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) {
+ var child = chunk.children[i$1], ch = child.height;
+ if (h < ch) { chunk = child; continue outer }
+ h -= ch;
+ n += child.chunkSize();
+ }
+ return n
+ } while (!chunk.lines)
+ var i = 0;
+ for (; i < chunk.lines.length; ++i) {
+ var line = chunk.lines[i], lh = line.height;
+ if (h < lh) { break }
+ h -= lh;
+ }
+ return n + i
+ }
+
+ function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size}
+
+ function lineNumberFor(options, i) {
+ return String(options.lineNumberFormatter(i + options.firstLineNumber))
+ }
+
+ // A Pos instance represents a position within the text.
+ function Pos(line, ch, sticky) {
+ if ( sticky === void 0 ) sticky = null;
+
+ if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) }
+ this.line = line;
+ this.ch = ch;
+ this.sticky = sticky;
+ }
+
+ // Compare two positions, return 0 if they are the same, a negative
+ // number when a is less, and a positive number otherwise.
+ function cmp(a, b) { return a.line - b.line || a.ch - b.ch }
+
+ function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 }
+
+ function copyPos(x) {return Pos(x.line, x.ch)}
+ function maxPos(a, b) { return cmp(a, b) < 0 ? b : a }
+ function minPos(a, b) { return cmp(a, b) < 0 ? a : b }
+
+ // Most of the external API clips given positions to make sure they
+ // actually exist within the document.
+ function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))}
+ function clipPos(doc, pos) {
+ if (pos.line < doc.first) { return Pos(doc.first, 0) }
+ var last = doc.first + doc.size - 1;
+ if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) }
+ return clipToLen(pos, getLine(doc, pos.line).text.length)
+ }
+ function clipToLen(pos, linelen) {
+ var ch = pos.ch;
+ if (ch == null || ch > linelen) { return Pos(pos.line, linelen) }
+ else if (ch < 0) { return Pos(pos.line, 0) }
+ else { return pos }
+ }
+ function clipPosArray(doc, array) {
+ var out = [];
+ for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); }
+ return out
+ }
+
+ var SavedContext = function(state, lookAhead) {
+ this.state = state;
+ this.lookAhead = lookAhead;
+ };
+
+ var Context = function(doc, state, line, lookAhead) {
+ this.state = state;
+ this.doc = doc;
+ this.line = line;
+ this.maxLookAhead = lookAhead || 0;
+ this.baseTokens = null;
+ this.baseTokenPos = 1;
+ };
+
+ Context.prototype.lookAhead = function (n) {
+ var line = this.doc.getLine(this.line + n);
+ if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; }
+ return line
+ };
+
+ Context.prototype.baseToken = function (n) {
+ if (!this.baseTokens) { return null }
+ while (this.baseTokens[this.baseTokenPos] <= n)
+ { this.baseTokenPos += 2; }
+ var type = this.baseTokens[this.baseTokenPos + 1];
+ return {type: type && type.replace(/( |^)overlay .*/, ""),
+ size: this.baseTokens[this.baseTokenPos] - n}
+ };
+
+ Context.prototype.nextLine = function () {
+ this.line++;
+ if (this.maxLookAhead > 0) { this.maxLookAhead--; }
+ };
+
+ Context.fromSaved = function (doc, saved, line) {
+ if (saved instanceof SavedContext)
+ { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) }
+ else
+ { return new Context(doc, copyState(doc.mode, saved), line) }
+ };
+
+ Context.prototype.save = function (copy) {
+ var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state;
+ return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state
+ };
+
+
+ // Compute a style array (an array starting with a mode generation
+ // -- for invalidation -- followed by pairs of end positions and
+ // style strings), which is used to highlight the tokens on the
+ // line.
+ function highlightLine(cm, line, context, forceToEnd) {
+ // A styles array always starts with a number identifying the
+ // mode/overlays that it is based on (for easy invalidation).
+ var st = [cm.state.modeGen], lineClasses = {};
+ // Compute the base array of styles
+ runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); },
+ lineClasses, forceToEnd);
+ var state = context.state;
+
+ // Run overlays, adjust style array.
+ var loop = function ( o ) {
+ context.baseTokens = st;
+ var overlay = cm.state.overlays[o], i = 1, at = 0;
+ context.state = true;
+ runMode(cm, line.text, overlay.mode, context, function (end, style) {
+ var start = i;
+ // Ensure there's a token end at the current position, and that i points at it
+ while (at < end) {
+ var i_end = st[i];
+ if (i_end > end)
+ { st.splice(i, 1, end, st[i+1], i_end); }
+ i += 2;
+ at = Math.min(end, i_end);
+ }
+ if (!style) { return }
+ if (overlay.opaque) {
+ st.splice(start, i - start, end, "overlay " + style);
+ i = start + 2;
+ } else {
+ for (; start < i; start += 2) {
+ var cur = st[start+1];
+ st[start+1] = (cur ? cur + " " : "") + "overlay " + style;
+ }
+ }
+ }, lineClasses);
+ context.state = state;
+ context.baseTokens = null;
+ context.baseTokenPos = 1;
+ };
+
+ for (var o = 0; o < cm.state.overlays.length; ++o) loop( o );
+
+ return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null}
+ }
+
+ function getLineStyles(cm, line, updateFrontier) {
+ if (!line.styles || line.styles[0] != cm.state.modeGen) {
+ var context = getContextBefore(cm, lineNo(line));
+ var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state);
+ var result = highlightLine(cm, line, context);
+ if (resetState) { context.state = resetState; }
+ line.stateAfter = context.save(!resetState);
+ line.styles = result.styles;
+ if (result.classes) { line.styleClasses = result.classes; }
+ else if (line.styleClasses) { line.styleClasses = null; }
+ if (updateFrontier === cm.doc.highlightFrontier)
+ { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); }
+ }
+ return line.styles
+ }
+
+ function getContextBefore(cm, n, precise) {
+ var doc = cm.doc, display = cm.display;
+ if (!doc.mode.startState) { return new Context(doc, true, n) }
+ var start = findStartLine(cm, n, precise);
+ var saved = start > doc.first && getLine(doc, start - 1).stateAfter;
+ var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start);
+
+ doc.iter(start, n, function (line) {
+ processLine(cm, line.text, context);
+ var pos = context.line;
+ line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null;
+ context.nextLine();
+ });
+ if (precise) { doc.modeFrontier = context.line; }
+ return context
+ }
+
+ // Lightweight form of highlight -- proceed over this line and
+ // update state, but don't save a style array. Used for lines that
+ // aren't currently visible.
+ function processLine(cm, text, context, startAt) {
+ var mode = cm.doc.mode;
+ var stream = new StringStream(text, cm.options.tabSize, context);
+ stream.start = stream.pos = startAt || 0;
+ if (text == "") { callBlankLine(mode, context.state); }
+ while (!stream.eol()) {
+ readToken(mode, stream, context.state);
+ stream.start = stream.pos;
+ }
+ }
+
+ function callBlankLine(mode, state) {
+ if (mode.blankLine) { return mode.blankLine(state) }
+ if (!mode.innerMode) { return }
+ var inner = innerMode(mode, state);
+ if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) }
+ }
+
+ function readToken(mode, stream, state, inner) {
+ for (var i = 0; i < 10; i++) {
+ if (inner) { inner[0] = innerMode(mode, state).mode; }
+ var style = mode.token(stream, state);
+ if (stream.pos > stream.start) { return style }
+ }
+ throw new Error("Mode " + mode.name + " failed to advance stream.")
+ }
+
+ var Token = function(stream, type, state) {
+ this.start = stream.start; this.end = stream.pos;
+ this.string = stream.current();
+ this.type = type || null;
+ this.state = state;
+ };
+
+ // Utility for getTokenAt and getLineTokens
+ function takeToken(cm, pos, precise, asArray) {
+ var doc = cm.doc, mode = doc.mode, style;
+ pos = clipPos(doc, pos);
+ var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise);
+ var stream = new StringStream(line.text, cm.options.tabSize, context), tokens;
+ if (asArray) { tokens = []; }
+ while ((asArray || stream.pos < pos.ch) && !stream.eol()) {
+ stream.start = stream.pos;
+ style = readToken(mode, stream, context.state);
+ if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); }
+ }
+ return asArray ? tokens : new Token(stream, style, context.state)
+ }
+
+ function extractLineClasses(type, output) {
+ if (type) { for (;;) {
+ var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/);
+ if (!lineClass) { break }
+ type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length);
+ var prop = lineClass[1] ? "bgClass" : "textClass";
+ if (output[prop] == null)
+ { output[prop] = lineClass[2]; }
+ else if (!(new RegExp("(?:^|\\s)" + lineClass[2] + "(?:$|\\s)")).test(output[prop]))
+ { output[prop] += " " + lineClass[2]; }
+ } }
+ return type
+ }
+
+ // Run the given mode's parser over a line, calling f for each token.
+ function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) {
+ var flattenSpans = mode.flattenSpans;
+ if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; }
+ var curStart = 0, curStyle = null;
+ var stream = new StringStream(text, cm.options.tabSize, context), style;
+ var inner = cm.options.addModeClass && [null];
+ if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); }
+ while (!stream.eol()) {
+ if (stream.pos > cm.options.maxHighlightLength) {
+ flattenSpans = false;
+ if (forceToEnd) { processLine(cm, text, context, stream.pos); }
+ stream.pos = text.length;
+ style = null;
+ } else {
+ style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses);
+ }
+ if (inner) {
+ var mName = inner[0].name;
+ if (mName) { style = "m-" + (style ? mName + " " + style : mName); }
+ }
+ if (!flattenSpans || curStyle != style) {
+ while (curStart < stream.start) {
+ curStart = Math.min(stream.start, curStart + 5000);
+ f(curStart, curStyle);
+ }
+ curStyle = style;
+ }
+ stream.start = stream.pos;
+ }
+ while (curStart < stream.pos) {
+ // Webkit seems to refuse to render text nodes longer than 57444
+ // characters, and returns inaccurate measurements in nodes
+ // starting around 5000 chars.
+ var pos = Math.min(stream.pos, curStart + 5000);
+ f(pos, curStyle);
+ curStart = pos;
+ }
+ }
+
+ // Finds the line to start with when starting a parse. Tries to
+ // find a line with a stateAfter, so that it can start with a
+ // valid state. If that fails, it returns the line with the
+ // smallest indentation, which tends to need the least context to
+ // parse correctly.
+ function findStartLine(cm, n, precise) {
+ var minindent, minline, doc = cm.doc;
+ var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100);
+ for (var search = n; search > lim; --search) {
+ if (search <= doc.first) { return doc.first }
+ var line = getLine(doc, search - 1), after = line.stateAfter;
+ if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier))
+ { return search }
+ var indented = countColumn(line.text, null, cm.options.tabSize);
+ if (minline == null || minindent > indented) {
+ minline = search - 1;
+ minindent = indented;
+ }
+ }
+ return minline
+ }
+
+ function retreatFrontier(doc, n) {
+ doc.modeFrontier = Math.min(doc.modeFrontier, n);
+ if (doc.highlightFrontier < n - 10) { return }
+ var start = doc.first;
+ for (var line = n - 1; line > start; line--) {
+ var saved = getLine(doc, line).stateAfter;
+ // change is on 3
+ // state on line 1 looked ahead 2 -- so saw 3
+ // test 1 + 2 < 3 should cover this
+ if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) {
+ start = line + 1;
+ break
+ }
+ }
+ doc.highlightFrontier = Math.min(doc.highlightFrontier, start);
+ }
+
+ // Optimize some code when these features are not used.
+ var sawReadOnlySpans = false, sawCollapsedSpans = false;
+
+ function seeReadOnlySpans() {
+ sawReadOnlySpans = true;
+ }
+
+ function seeCollapsedSpans() {
+ sawCollapsedSpans = true;
+ }
+
+ // TEXTMARKER SPANS
+
+ function MarkedSpan(marker, from, to) {
+ this.marker = marker;
+ this.from = from; this.to = to;
+ }
+
+ // Search an array of spans for a span matching the given marker.
+ function getMarkedSpanFor(spans, marker) {
+ if (spans) { for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i];
+ if (span.marker == marker) { return span }
+ } }
+ }
+ // Remove a span from an array, returning undefined if no spans are
+ // left (we don't store arrays for lines without spans).
+ function removeMarkedSpan(spans, span) {
+ var r;
+ for (var i = 0; i < spans.length; ++i)
+ { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } }
+ return r
+ }
+ // Add a span to a line.
+ function addMarkedSpan(line, span) {
+ line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span];
+ span.marker.attachLine(line);
+ }
+
+ // Used for the algorithm that adjusts markers for a change in the
+ // document. These functions cut an array of spans at a given
+ // character position, returning an array of remaining chunks (or
+ // undefined if nothing remains).
+ function markedSpansBefore(old, startCh, isInsert) {
+ var nw;
+ if (old) { for (var i = 0; i < old.length; ++i) {
+ var span = old[i], marker = span.marker;
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh);
+ if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) {
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh)
+ ;(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to));
+ }
+ } }
+ return nw
+ }
+ function markedSpansAfter(old, endCh, isInsert) {
+ var nw;
+ if (old) { for (var i = 0; i < old.length; ++i) {
+ var span = old[i], marker = span.marker;
+ var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh);
+ if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) {
+ var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh)
+ ;(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh,
+ span.to == null ? null : span.to - endCh));
+ }
+ } }
+ return nw
+ }
+
+ // Given a change object, compute the new set of marker spans that
+ // cover the line in which the change took place. Removes spans
+ // entirely within the change, reconnects spans belonging to the
+ // same marker that appear on both sides of the change, and cuts off
+ // spans partially within the change. Returns an array of span
+ // arrays with one element for each line in (after) the change.
+ function stretchSpansOverChange(doc, change) {
+ if (change.full) { return null }
+ var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans;
+ var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans;
+ if (!oldFirst && !oldLast) { return null }
+
+ var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0;
+ // Get the spans that 'stick out' on both sides
+ var first = markedSpansBefore(oldFirst, startCh, isInsert);
+ var last = markedSpansAfter(oldLast, endCh, isInsert);
+
+ // Next, merge those two ends
+ var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0);
+ if (first) {
+ // Fix up .to properties of first
+ for (var i = 0; i < first.length; ++i) {
+ var span = first[i];
+ if (span.to == null) {
+ var found = getMarkedSpanFor(last, span.marker);
+ if (!found) { span.to = startCh; }
+ else if (sameLine) { span.to = found.to == null ? null : found.to + offset; }
+ }
+ }
+ }
+ if (last) {
+ // Fix up .from in last (or move them into first in case of sameLine)
+ for (var i$1 = 0; i$1 < last.length; ++i$1) {
+ var span$1 = last[i$1];
+ if (span$1.to != null) { span$1.to += offset; }
+ if (span$1.from == null) {
+ var found$1 = getMarkedSpanFor(first, span$1.marker);
+ if (!found$1) {
+ span$1.from = offset;
+ if (sameLine) { (first || (first = [])).push(span$1); }
+ }
+ } else {
+ span$1.from += offset;
+ if (sameLine) { (first || (first = [])).push(span$1); }
+ }
+ }
+ }
+ // Make sure we didn't create any zero-length spans
+ if (first) { first = clearEmptySpans(first); }
+ if (last && last != first) { last = clearEmptySpans(last); }
+
+ var newMarkers = [first];
+ if (!sameLine) {
+ // Fill gap with whole-line-spans
+ var gap = change.text.length - 2, gapMarkers;
+ if (gap > 0 && first)
+ { for (var i$2 = 0; i$2 < first.length; ++i$2)
+ { if (first[i$2].to == null)
+ { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } }
+ for (var i$3 = 0; i$3 < gap; ++i$3)
+ { newMarkers.push(gapMarkers); }
+ newMarkers.push(last);
+ }
+ return newMarkers
+ }
+
+ // Remove spans that are empty and don't have a clearWhenEmpty
+ // option of false.
+ function clearEmptySpans(spans) {
+ for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i];
+ if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false)
+ { spans.splice(i--, 1); }
+ }
+ if (!spans.length) { return null }
+ return spans
+ }
+
+ // Used to 'clip' out readOnly ranges when making a change.
+ function removeReadOnlyRanges(doc, from, to) {
+ var markers = null;
+ doc.iter(from.line, to.line + 1, function (line) {
+ if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
+ var mark = line.markedSpans[i].marker;
+ if (mark.readOnly && (!markers || indexOf(markers, mark) == -1))
+ { (markers || (markers = [])).push(mark); }
+ } }
+ });
+ if (!markers) { return null }
+ var parts = [{from: from, to: to}];
+ for (var i = 0; i < markers.length; ++i) {
+ var mk = markers[i], m = mk.find(0);
+ for (var j = 0; j < parts.length; ++j) {
+ var p = parts[j];
+ if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue }
+ var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to);
+ if (dfrom < 0 || !mk.inclusiveLeft && !dfrom)
+ { newParts.push({from: p.from, to: m.from}); }
+ if (dto > 0 || !mk.inclusiveRight && !dto)
+ { newParts.push({from: m.to, to: p.to}); }
+ parts.splice.apply(parts, newParts);
+ j += newParts.length - 3;
+ }
+ }
+ return parts
+ }
+
+ // Connect or disconnect spans from a line.
+ function detachMarkedSpans(line) {
+ var spans = line.markedSpans;
+ if (!spans) { return }
+ for (var i = 0; i < spans.length; ++i)
+ { spans[i].marker.detachLine(line); }
+ line.markedSpans = null;
+ }
+ function attachMarkedSpans(line, spans) {
+ if (!spans) { return }
+ for (var i = 0; i < spans.length; ++i)
+ { spans[i].marker.attachLine(line); }
+ line.markedSpans = spans;
+ }
+
+ // Helpers used when computing which overlapping collapsed span
+ // counts as the larger one.
+ function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 }
+ function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 }
+
+ // Returns a number indicating which of two overlapping collapsed
+ // spans is larger (and thus includes the other). Falls back to
+ // comparing ids when the spans cover exactly the same range.
+ function compareCollapsedMarkers(a, b) {
+ var lenDiff = a.lines.length - b.lines.length;
+ if (lenDiff != 0) { return lenDiff }
+ var aPos = a.find(), bPos = b.find();
+ var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b);
+ if (fromCmp) { return -fromCmp }
+ var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b);
+ if (toCmp) { return toCmp }
+ return b.id - a.id
+ }
+
+ // Find out whether a line ends or starts in a collapsed span. If
+ // so, return the marker for that span.
+ function collapsedSpanAtSide(line, start) {
+ var sps = sawCollapsedSpans && line.markedSpans, found;
+ if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
+ sp = sps[i];
+ if (sp.marker.collapsed && (start ? sp.from : sp.to) == null &&
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0))
+ { found = sp.marker; }
+ } }
+ return found
+ }
+ function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) }
+ function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) }
+
+ function collapsedSpanAround(line, ch) {
+ var sps = sawCollapsedSpans && line.markedSpans, found;
+ if (sps) { for (var i = 0; i < sps.length; ++i) {
+ var sp = sps[i];
+ if (sp.marker.collapsed && (sp.from == null || sp.from < ch) && (sp.to == null || sp.to > ch) &&
+ (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; }
+ } }
+ return found
+ }
+
+ // Test whether there exists a collapsed span that partially
+ // overlaps (covers the start or end, but not both) of a new span.
+ // Such overlap is not allowed.
+ function conflictingCollapsedRange(doc, lineNo, from, to, marker) {
+ var line = getLine(doc, lineNo);
+ var sps = sawCollapsedSpans && line.markedSpans;
+ if (sps) { for (var i = 0; i < sps.length; ++i) {
+ var sp = sps[i];
+ if (!sp.marker.collapsed) { continue }
+ var found = sp.marker.find(0);
+ var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker);
+ var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker);
+ if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue }
+ if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) ||
+ fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0))
+ { return true }
+ } }
+ }
+
+ // A visual line is a line as drawn on the screen. Folding, for
+ // example, can cause multiple logical lines to appear on the same
+ // visual line. This finds the start of the visual line that the
+ // given line is part of (usually that is the line itself).
+ function visualLine(line) {
+ var merged;
+ while (merged = collapsedSpanAtStart(line))
+ { line = merged.find(-1, true).line; }
+ return line
+ }
+
+ function visualLineEnd(line) {
+ var merged;
+ while (merged = collapsedSpanAtEnd(line))
+ { line = merged.find(1, true).line; }
+ return line
+ }
+
+ // Returns an array of logical lines that continue the visual line
+ // started by the argument, or undefined if there are no such lines.
+ function visualLineContinued(line) {
+ var merged, lines;
+ while (merged = collapsedSpanAtEnd(line)) {
+ line = merged.find(1, true).line
+ ;(lines || (lines = [])).push(line);
+ }
+ return lines
+ }
+
+ // Get the line number of the start of the visual line that the
+ // given line number is part of.
+ function visualLineNo(doc, lineN) {
+ var line = getLine(doc, lineN), vis = visualLine(line);
+ if (line == vis) { return lineN }
+ return lineNo(vis)
+ }
+
+ // Get the line number of the start of the next visual line after
+ // the given line.
+ function visualLineEndNo(doc, lineN) {
+ if (lineN > doc.lastLine()) { return lineN }
+ var line = getLine(doc, lineN), merged;
+ if (!lineIsHidden(doc, line)) { return lineN }
+ while (merged = collapsedSpanAtEnd(line))
+ { line = merged.find(1, true).line; }
+ return lineNo(line) + 1
+ }
+
+ // Compute whether a line is hidden. Lines count as hidden when they
+ // are part of a visual line that starts with another line, or when
+ // they are entirely covered by collapsed, non-widget span.
+ function lineIsHidden(doc, line) {
+ var sps = sawCollapsedSpans && line.markedSpans;
+ if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) {
+ sp = sps[i];
+ if (!sp.marker.collapsed) { continue }
+ if (sp.from == null) { return true }
+ if (sp.marker.widgetNode) { continue }
+ if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp))
+ { return true }
+ } }
+ }
+ function lineIsHiddenInner(doc, line, span) {
+ if (span.to == null) {
+ var end = span.marker.find(1, true);
+ return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker))
+ }
+ if (span.marker.inclusiveRight && span.to == line.text.length)
+ { return true }
+ for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) {
+ sp = line.markedSpans[i];
+ if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to &&
+ (sp.to == null || sp.to != span.from) &&
+ (sp.marker.inclusiveLeft || span.marker.inclusiveRight) &&
+ lineIsHiddenInner(doc, line, sp)) { return true }
+ }
+ }
+
+ // Find the height above the given line.
+ function heightAtLine(lineObj) {
+ lineObj = visualLine(lineObj);
+
+ var h = 0, chunk = lineObj.parent;
+ for (var i = 0; i < chunk.lines.length; ++i) {
+ var line = chunk.lines[i];
+ if (line == lineObj) { break }
+ else { h += line.height; }
+ }
+ for (var p = chunk.parent; p; chunk = p, p = chunk.parent) {
+ for (var i$1 = 0; i$1 < p.children.length; ++i$1) {
+ var cur = p.children[i$1];
+ if (cur == chunk) { break }
+ else { h += cur.height; }
+ }
+ }
+ return h
+ }
+
+ // Compute the character length of a line, taking into account
+ // collapsed ranges (see markText) that might hide parts, and join
+ // other lines onto it.
+ function lineLength(line) {
+ if (line.height == 0) { return 0 }
+ var len = line.text.length, merged, cur = line;
+ while (merged = collapsedSpanAtStart(cur)) {
+ var found = merged.find(0, true);
+ cur = found.from.line;
+ len += found.from.ch - found.to.ch;
+ }
+ cur = line;
+ while (merged = collapsedSpanAtEnd(cur)) {
+ var found$1 = merged.find(0, true);
+ len -= cur.text.length - found$1.from.ch;
+ cur = found$1.to.line;
+ len += cur.text.length - found$1.to.ch;
+ }
+ return len
+ }
+
+ // Find the longest line in the document.
+ function findMaxLine(cm) {
+ var d = cm.display, doc = cm.doc;
+ d.maxLine = getLine(doc, doc.first);
+ d.maxLineLength = lineLength(d.maxLine);
+ d.maxLineChanged = true;
+ doc.iter(function (line) {
+ var len = lineLength(line);
+ if (len > d.maxLineLength) {
+ d.maxLineLength = len;
+ d.maxLine = line;
+ }
+ });
+ }
+
+ // LINE DATA STRUCTURE
+
+ // Line objects. These hold state related to a line, including
+ // highlighting info (the styles array).
+ var Line = function(text, markedSpans, estimateHeight) {
+ this.text = text;
+ attachMarkedSpans(this, markedSpans);
+ this.height = estimateHeight ? estimateHeight(this) : 1;
+ };
+
+ Line.prototype.lineNo = function () { return lineNo(this) };
+ eventMixin(Line);
+
+ // Change the content (text, markers) of a line. Automatically
+ // invalidates cached information and tries to re-estimate the
+ // line's height.
+ function updateLine(line, text, markedSpans, estimateHeight) {
+ line.text = text;
+ if (line.stateAfter) { line.stateAfter = null; }
+ if (line.styles) { line.styles = null; }
+ if (line.order != null) { line.order = null; }
+ detachMarkedSpans(line);
+ attachMarkedSpans(line, markedSpans);
+ var estHeight = estimateHeight ? estimateHeight(line) : 1;
+ if (estHeight != line.height) { updateLineHeight(line, estHeight); }
+ }
+
+ // Detach a line from the document tree and its markers.
+ function cleanUpLine(line) {
+ line.parent = null;
+ detachMarkedSpans(line);
+ }
+
+ // Convert a style as returned by a mode (either null, or a string
+ // containing one or more styles) to a CSS style. This is cached,
+ // and also looks for line-wide styles.
+ var styleToClassCache = {}, styleToClassCacheWithMode = {};
+ function interpretTokenStyle(style, options) {
+ if (!style || /^\s*$/.test(style)) { return null }
+ var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache;
+ return cache[style] ||
+ (cache[style] = style.replace(/\S+/g, "cm-$&"))
+ }
+
+ // Render the DOM representation of the text of a line. Also builds
+ // up a 'line map', which points at the DOM nodes that represent
+ // specific stretches of text, and is used by the measuring code.
+ // The returned object contains the DOM node, this map, and
+ // information about line-wide styles that were set by the mode.
+ function buildLineContent(cm, lineView) {
+ // The padding-right forces the element to have a 'border', which
+ // is needed on Webkit to be able to get line-level bounding
+ // rectangles for it (in measureChar).
+ var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null);
+ var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content,
+ col: 0, pos: 0, cm: cm,
+ trailingSpace: false,
+ splitSpaces: cm.getOption("lineWrapping")};
+ lineView.measure = {};
+
+ // Iterate over the logical lines that make up this visual line.
+ for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) {
+ var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0);
+ builder.pos = 0;
+ builder.addToken = buildToken;
+ // Optionally wire in some hacks into the token-rendering
+ // algorithm, to deal with browser quirks.
+ if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction)))
+ { builder.addToken = buildTokenBadBidi(builder.addToken, order); }
+ builder.map = [];
+ var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line);
+ insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate));
+ if (line.styleClasses) {
+ if (line.styleClasses.bgClass)
+ { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); }
+ if (line.styleClasses.textClass)
+ { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); }
+ }
+
+ // Ensure at least a single node is present, for measuring.
+ if (builder.map.length == 0)
+ { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); }
+
+ // Store the map and a cache object for the current logical line
+ if (i == 0) {
+ lineView.measure.map = builder.map;
+ lineView.measure.cache = {};
+ } else {
+ (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map)
+ ;(lineView.measure.caches || (lineView.measure.caches = [])).push({});
+ }
+ }
+
+ // See issue #2901
+ if (webkit) {
+ var last = builder.content.lastChild;
+ if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab")))
+ { builder.content.className = "cm-tab-wrap-hack"; }
+ }
+
+ signal(cm, "renderLine", cm, lineView.line, builder.pre);
+ if (builder.pre.className)
+ { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); }
+
+ return builder
+ }
+
+ function defaultSpecialCharPlaceholder(ch) {
+ var token = elt("span", "\u2022", "cm-invalidchar");
+ token.title = "\\u" + ch.charCodeAt(0).toString(16);
+ token.setAttribute("aria-label", token.title);
+ return token
+ }
+
+ // Build up the DOM representation for a single token, and add it to
+ // the line map. Takes care to render special characters separately.
+ function buildToken(builder, text, style, startStyle, endStyle, css, attributes) {
+ if (!text) { return }
+ var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text;
+ var special = builder.cm.state.specialChars, mustWrap = false;
+ var content;
+ if (!special.test(text)) {
+ builder.col += text.length;
+ content = document.createTextNode(displayText);
+ builder.map.push(builder.pos, builder.pos + text.length, content);
+ if (ie && ie_version < 9) { mustWrap = true; }
+ builder.pos += text.length;
+ } else {
+ content = document.createDocumentFragment();
+ var pos = 0;
+ while (true) {
+ special.lastIndex = pos;
+ var m = special.exec(text);
+ var skipped = m ? m.index - pos : text.length - pos;
+ if (skipped) {
+ var txt = document.createTextNode(displayText.slice(pos, pos + skipped));
+ if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); }
+ else { content.appendChild(txt); }
+ builder.map.push(builder.pos, builder.pos + skipped, txt);
+ builder.col += skipped;
+ builder.pos += skipped;
+ }
+ if (!m) { break }
+ pos += skipped + 1;
+ var txt$1 = (void 0);
+ if (m[0] == "\t") {
+ var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize;
+ txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab"));
+ txt$1.setAttribute("role", "presentation");
+ txt$1.setAttribute("cm-text", "\t");
+ builder.col += tabWidth;
+ } else if (m[0] == "\r" || m[0] == "\n") {
+ txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar"));
+ txt$1.setAttribute("cm-text", m[0]);
+ builder.col += 1;
+ } else {
+ txt$1 = builder.cm.options.specialCharPlaceholder(m[0]);
+ txt$1.setAttribute("cm-text", m[0]);
+ if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); }
+ else { content.appendChild(txt$1); }
+ builder.col += 1;
+ }
+ builder.map.push(builder.pos, builder.pos + 1, txt$1);
+ builder.pos++;
+ }
+ }
+ builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32;
+ if (style || startStyle || endStyle || mustWrap || css || attributes) {
+ var fullStyle = style || "";
+ if (startStyle) { fullStyle += startStyle; }
+ if (endStyle) { fullStyle += endStyle; }
+ var token = elt("span", [content], fullStyle, css);
+ if (attributes) {
+ for (var attr in attributes) { if (attributes.hasOwnProperty(attr) && attr != "style" && attr != "class")
+ { token.setAttribute(attr, attributes[attr]); } }
+ }
+ return builder.content.appendChild(token)
+ }
+ builder.content.appendChild(content);
+ }
+
+ // Change some spaces to NBSP to prevent the browser from collapsing
+ // trailing spaces at the end of a line when rendering text (issue #1362).
+ function splitSpaces(text, trailingBefore) {
+ if (text.length > 1 && !/ /.test(text)) { return text }
+ var spaceBefore = trailingBefore, result = "";
+ for (var i = 0; i < text.length; i++) {
+ var ch = text.charAt(i);
+ if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32))
+ { ch = "\u00a0"; }
+ result += ch;
+ spaceBefore = ch == " ";
+ }
+ return result
+ }
+
+ // Work around nonsense dimensions being reported for stretches of
+ // right-to-left text.
+ function buildTokenBadBidi(inner, order) {
+ return function (builder, text, style, startStyle, endStyle, css, attributes) {
+ style = style ? style + " cm-force-border" : "cm-force-border";
+ var start = builder.pos, end = start + text.length;
+ for (;;) {
+ // Find the part that overlaps with the start of this text
+ var part = (void 0);
+ for (var i = 0; i < order.length; i++) {
+ part = order[i];
+ if (part.to > start && part.from <= start) { break }
+ }
+ if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, css, attributes) }
+ inner(builder, text.slice(0, part.to - start), style, startStyle, null, css, attributes);
+ startStyle = null;
+ text = text.slice(part.to - start);
+ start = part.to;
+ }
+ }
+ }
+
+ function buildCollapsedSpan(builder, size, marker, ignoreWidget) {
+ var widget = !ignoreWidget && marker.widgetNode;
+ if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); }
+ if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) {
+ if (!widget)
+ { widget = builder.content.appendChild(document.createElement("span")); }
+ widget.setAttribute("cm-marker", marker.id);
+ }
+ if (widget) {
+ builder.cm.display.input.setUneditable(widget);
+ builder.content.appendChild(widget);
+ }
+ builder.pos += size;
+ builder.trailingSpace = false;
+ }
+
+ // Outputs a number of spans to make up a line, taking highlighting
+ // and marked text into account.
+ function insertLineContent(line, builder, styles) {
+ var spans = line.markedSpans, allText = line.text, at = 0;
+ if (!spans) {
+ for (var i$1 = 1; i$1 < styles.length; i$1+=2)
+ { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); }
+ return
+ }
+
+ var len = allText.length, pos = 0, i = 1, text = "", style, css;
+ var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, collapsed, attributes;
+ for (;;) {
+ if (nextChange == pos) { // Update current marker set
+ spanStyle = spanEndStyle = spanStartStyle = css = "";
+ attributes = null;
+ collapsed = null; nextChange = Infinity;
+ var foundBookmarks = [], endStyles = (void 0);
+ for (var j = 0; j < spans.length; ++j) {
+ var sp = spans[j], m = sp.marker;
+ if (m.type == "bookmark" && sp.from == pos && m.widgetNode) {
+ foundBookmarks.push(m);
+ } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) {
+ if (sp.to != null && sp.to != pos && nextChange > sp.to) {
+ nextChange = sp.to;
+ spanEndStyle = "";
+ }
+ if (m.className) { spanStyle += " " + m.className; }
+ if (m.css) { css = (css ? css + ";" : "") + m.css; }
+ if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; }
+ if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); }
+ // support for the old title property
+ // https://github.com/codemirror/CodeMirror/pull/5673
+ if (m.title) { (attributes || (attributes = {})).title = m.title; }
+ if (m.attributes) {
+ for (var attr in m.attributes)
+ { (attributes || (attributes = {}))[attr] = m.attributes[attr]; }
+ }
+ if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0))
+ { collapsed = sp; }
+ } else if (sp.from > pos && nextChange > sp.from) {
+ nextChange = sp.from;
+ }
+ }
+ if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2)
+ { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } }
+
+ if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2)
+ { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } }
+ if (collapsed && (collapsed.from || 0) == pos) {
+ buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos,
+ collapsed.marker, collapsed.from == null);
+ if (collapsed.to == null) { return }
+ if (collapsed.to == pos) { collapsed = false; }
+ }
+ }
+ if (pos >= len) { break }
+
+ var upto = Math.min(len, nextChange);
+ while (true) {
+ if (text) {
+ var end = pos + text.length;
+ if (!collapsed) {
+ var tokenText = end > upto ? text.slice(0, upto - pos) : text;
+ builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle,
+ spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", css, attributes);
+ }
+ if (end >= upto) {text = text.slice(upto - pos); pos = upto; break}
+ pos = end;
+ spanStartStyle = "";
+ }
+ text = allText.slice(at, at = styles[i++]);
+ style = interpretTokenStyle(styles[i++], builder.cm.options);
+ }
+ }
+ }
+
+
+ // These objects are used to represent the visible (currently drawn)
+ // part of the document. A LineView may correspond to multiple
+ // logical lines, if those are connected by collapsed ranges.
+ function LineView(doc, line, lineN) {
+ // The starting line
+ this.line = line;
+ // Continuing lines, if any
+ this.rest = visualLineContinued(line);
+ // Number of logical lines in this visual line
+ this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1;
+ this.node = this.text = null;
+ this.hidden = lineIsHidden(doc, line);
+ }
+
+ // Create a range of LineView objects for the given lines.
+ function buildViewArray(cm, from, to) {
+ var array = [], nextPos;
+ for (var pos = from; pos < to; pos = nextPos) {
+ var view = new LineView(cm.doc, getLine(cm.doc, pos), pos);
+ nextPos = pos + view.size;
+ array.push(view);
+ }
+ return array
+ }
+
+ var operationGroup = null;
+
+ function pushOperation(op) {
+ if (operationGroup) {
+ operationGroup.ops.push(op);
+ } else {
+ op.ownsGroup = operationGroup = {
+ ops: [op],
+ delayedCallbacks: []
+ };
+ }
+ }
+
+ function fireCallbacksForOps(group) {
+ // Calls delayed callbacks and cursorActivity handlers until no
+ // new ones appear
+ var callbacks = group.delayedCallbacks, i = 0;
+ do {
+ for (; i < callbacks.length; i++)
+ { callbacks[i].call(null); }
+ for (var j = 0; j < group.ops.length; j++) {
+ var op = group.ops[j];
+ if (op.cursorActivityHandlers)
+ { while (op.cursorActivityCalled < op.cursorActivityHandlers.length)
+ { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } }
+ }
+ } while (i < callbacks.length)
+ }
+
+ function finishOperation(op, endCb) {
+ var group = op.ownsGroup;
+ if (!group) { return }
+
+ try { fireCallbacksForOps(group); }
+ finally {
+ operationGroup = null;
+ endCb(group);
+ }
+ }
+
+ var orphanDelayedCallbacks = null;
+
+ // Often, we want to signal events at a point where we are in the
+ // middle of some work, but don't want the handler to start calling
+ // other methods on the editor, which might be in an inconsistent
+ // state or simply not expect any other events to happen.
+ // signalLater looks whether there are any handlers, and schedules
+ // them to be executed when the last operation ends, or, if no
+ // operation is active, when a timeout fires.
+ function signalLater(emitter, type /*, values...*/) {
+ var arr = getHandlers(emitter, type);
+ if (!arr.length) { return }
+ var args = Array.prototype.slice.call(arguments, 2), list;
+ if (operationGroup) {
+ list = operationGroup.delayedCallbacks;
+ } else if (orphanDelayedCallbacks) {
+ list = orphanDelayedCallbacks;
+ } else {
+ list = orphanDelayedCallbacks = [];
+ setTimeout(fireOrphanDelayed, 0);
+ }
+ var loop = function ( i ) {
+ list.push(function () { return arr[i].apply(null, args); });
+ };
+
+ for (var i = 0; i < arr.length; ++i)
+ loop( i );
+ }
+
+ function fireOrphanDelayed() {
+ var delayed = orphanDelayedCallbacks;
+ orphanDelayedCallbacks = null;
+ for (var i = 0; i < delayed.length; ++i) { delayed[i](); }
+ }
+
+ // When an aspect of a line changes, a string is added to
+ // lineView.changes. This updates the relevant part of the line's
+ // DOM structure.
+ function updateLineForChanges(cm, lineView, lineN, dims) {
+ for (var j = 0; j < lineView.changes.length; j++) {
+ var type = lineView.changes[j];
+ if (type == "text") { updateLineText(cm, lineView); }
+ else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); }
+ else if (type == "class") { updateLineClasses(cm, lineView); }
+ else if (type == "widget") { updateLineWidgets(cm, lineView, dims); }
+ }
+ lineView.changes = null;
+ }
+
+ // Lines with gutter elements, widgets or a background class need to
+ // be wrapped, and have the extra elements added to the wrapper div
+ function ensureLineWrapped(lineView) {
+ if (lineView.node == lineView.text) {
+ lineView.node = elt("div", null, null, "position: relative");
+ if (lineView.text.parentNode)
+ { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); }
+ lineView.node.appendChild(lineView.text);
+ if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; }
+ }
+ return lineView.node
+ }
+
+ function updateLineBackground(cm, lineView) {
+ var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass;
+ if (cls) { cls += " CodeMirror-linebackground"; }
+ if (lineView.background) {
+ if (cls) { lineView.background.className = cls; }
+ else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; }
+ } else if (cls) {
+ var wrap = ensureLineWrapped(lineView);
+ lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild);
+ cm.display.input.setUneditable(lineView.background);
+ }
+ }
+
+ // Wrapper around buildLineContent which will reuse the structure
+ // in display.externalMeasured when possible.
+ function getLineContent(cm, lineView) {
+ var ext = cm.display.externalMeasured;
+ if (ext && ext.line == lineView.line) {
+ cm.display.externalMeasured = null;
+ lineView.measure = ext.measure;
+ return ext.built
+ }
+ return buildLineContent(cm, lineView)
+ }
+
+ // Redraw the line's text. Interacts with the background and text
+ // classes because the mode may output tokens that influence these
+ // classes.
+ function updateLineText(cm, lineView) {
+ var cls = lineView.text.className;
+ var built = getLineContent(cm, lineView);
+ if (lineView.text == lineView.node) { lineView.node = built.pre; }
+ lineView.text.parentNode.replaceChild(built.pre, lineView.text);
+ lineView.text = built.pre;
+ if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) {
+ lineView.bgClass = built.bgClass;
+ lineView.textClass = built.textClass;
+ updateLineClasses(cm, lineView);
+ } else if (cls) {
+ lineView.text.className = cls;
+ }
+ }
+
+ function updateLineClasses(cm, lineView) {
+ updateLineBackground(cm, lineView);
+ if (lineView.line.wrapClass)
+ { ensureLineWrapped(lineView).className = lineView.line.wrapClass; }
+ else if (lineView.node != lineView.text)
+ { lineView.node.className = ""; }
+ var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass;
+ lineView.text.className = textClass || "";
+ }
+
+ function updateLineGutter(cm, lineView, lineN, dims) {
+ if (lineView.gutter) {
+ lineView.node.removeChild(lineView.gutter);
+ lineView.gutter = null;
+ }
+ if (lineView.gutterBackground) {
+ lineView.node.removeChild(lineView.gutterBackground);
+ lineView.gutterBackground = null;
+ }
+ if (lineView.line.gutterClass) {
+ var wrap = ensureLineWrapped(lineView);
+ lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass,
+ ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px"));
+ cm.display.input.setUneditable(lineView.gutterBackground);
+ wrap.insertBefore(lineView.gutterBackground, lineView.text);
+ }
+ var markers = lineView.line.gutterMarkers;
+ if (cm.options.lineNumbers || markers) {
+ var wrap$1 = ensureLineWrapped(lineView);
+ var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px"));
+ cm.display.input.setUneditable(gutterWrap);
+ wrap$1.insertBefore(gutterWrap, lineView.text);
+ if (lineView.line.gutterClass)
+ { gutterWrap.className += " " + lineView.line.gutterClass; }
+ if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"]))
+ { lineView.lineNumber = gutterWrap.appendChild(
+ elt("div", lineNumberFor(cm.options, lineN),
+ "CodeMirror-linenumber CodeMirror-gutter-elt",
+ ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); }
+ if (markers) { for (var k = 0; k < cm.display.gutterSpecs.length; ++k) {
+ var id = cm.display.gutterSpecs[k].className, found = markers.hasOwnProperty(id) && markers[id];
+ if (found)
+ { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt",
+ ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); }
+ } }
+ }
+ }
+
+ function updateLineWidgets(cm, lineView, dims) {
+ if (lineView.alignable) { lineView.alignable = null; }
+ var isWidget = classTest("CodeMirror-linewidget");
+ for (var node = lineView.node.firstChild, next = (void 0); node; node = next) {
+ next = node.nextSibling;
+ if (isWidget.test(node.className)) { lineView.node.removeChild(node); }
+ }
+ insertLineWidgets(cm, lineView, dims);
+ }
+
+ // Build a line's DOM representation from scratch
+ function buildLineElement(cm, lineView, lineN, dims) {
+ var built = getLineContent(cm, lineView);
+ lineView.text = lineView.node = built.pre;
+ if (built.bgClass) { lineView.bgClass = built.bgClass; }
+ if (built.textClass) { lineView.textClass = built.textClass; }
+
+ updateLineClasses(cm, lineView);
+ updateLineGutter(cm, lineView, lineN, dims);
+ insertLineWidgets(cm, lineView, dims);
+ return lineView.node
+ }
+
+ // A lineView may contain multiple logical lines (when merged by
+ // collapsed spans). The widgets for all of them need to be drawn.
+ function insertLineWidgets(cm, lineView, dims) {
+ insertLineWidgetsFor(cm, lineView.line, lineView, dims, true);
+ if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
+ { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } }
+ }
+
+ function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) {
+ if (!line.widgets) { return }
+ var wrap = ensureLineWrapped(lineView);
+ for (var i = 0, ws = line.widgets; i < ws.length; ++i) {
+ var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget" + (widget.className ? " " + widget.className : ""));
+ if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); }
+ positionLineWidget(widget, node, lineView, dims);
+ cm.display.input.setUneditable(node);
+ if (allowAbove && widget.above)
+ { wrap.insertBefore(node, lineView.gutter || lineView.text); }
+ else
+ { wrap.appendChild(node); }
+ signalLater(widget, "redraw");
+ }
+ }
+
+ function positionLineWidget(widget, node, lineView, dims) {
+ if (widget.noHScroll) {
+ (lineView.alignable || (lineView.alignable = [])).push(node);
+ var width = dims.wrapperWidth;
+ node.style.left = dims.fixedPos + "px";
+ if (!widget.coverGutter) {
+ width -= dims.gutterTotalWidth;
+ node.style.paddingLeft = dims.gutterTotalWidth + "px";
+ }
+ node.style.width = width + "px";
+ }
+ if (widget.coverGutter) {
+ node.style.zIndex = 5;
+ node.style.position = "relative";
+ if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; }
+ }
+ }
+
+ function widgetHeight(widget) {
+ if (widget.height != null) { return widget.height }
+ var cm = widget.doc.cm;
+ if (!cm) { return 0 }
+ if (!contains(document.body, widget.node)) {
+ var parentStyle = "position: relative;";
+ if (widget.coverGutter)
+ { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; }
+ if (widget.noHScroll)
+ { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; }
+ removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle));
+ }
+ return widget.height = widget.node.parentNode.offsetHeight
+ }
+
+ // Return true when the given mouse event happened in a widget
+ function eventInWidget(display, e) {
+ for (var n = e_target(e); n != display.wrapper; n = n.parentNode) {
+ if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") ||
+ (n.parentNode == display.sizer && n != display.mover))
+ { return true }
+ }
+ }
+
+ // POSITION MEASUREMENT
+
+ function paddingTop(display) {return display.lineSpace.offsetTop}
+ function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight}
+ function paddingH(display) {
+ if (display.cachedPaddingH) { return display.cachedPaddingH }
+ var e = removeChildrenAndAdd(display.measure, elt("pre", "x", "CodeMirror-line-like"));
+ var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle;
+ var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)};
+ if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; }
+ return data
+ }
+
+ function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth }
+ function displayWidth(cm) {
+ return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth
+ }
+ function displayHeight(cm) {
+ return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight
+ }
+
+ // Ensure the lineView.wrapping.heights array is populated. This is
+ // an array of bottom offsets for the lines that make up a drawn
+ // line. When lineWrapping is on, there might be more than one
+ // height.
+ function ensureLineHeights(cm, lineView, rect) {
+ var wrapping = cm.options.lineWrapping;
+ var curWidth = wrapping && displayWidth(cm);
+ if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) {
+ var heights = lineView.measure.heights = [];
+ if (wrapping) {
+ lineView.measure.width = curWidth;
+ var rects = lineView.text.firstChild.getClientRects();
+ for (var i = 0; i < rects.length - 1; i++) {
+ var cur = rects[i], next = rects[i + 1];
+ if (Math.abs(cur.bottom - next.bottom) > 2)
+ { heights.push((cur.bottom + next.top) / 2 - rect.top); }
+ }
+ }
+ heights.push(rect.bottom - rect.top);
+ }
+ }
+
+ // Find a line map (mapping character offsets to text nodes) and a
+ // measurement cache for the given line number. (A line view might
+ // contain multiple lines when collapsed ranges are present.)
+ function mapFromLineView(lineView, line, lineN) {
+ if (lineView.line == line)
+ { return {map: lineView.measure.map, cache: lineView.measure.cache} }
+ for (var i = 0; i < lineView.rest.length; i++)
+ { if (lineView.rest[i] == line)
+ { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } }
+ for (var i$1 = 0; i$1 < lineView.rest.length; i$1++)
+ { if (lineNo(lineView.rest[i$1]) > lineN)
+ { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } }
+ }
+
+ // Render a line into the hidden node display.externalMeasured. Used
+ // when measurement is needed for a line that's not in the viewport.
+ function updateExternalMeasurement(cm, line) {
+ line = visualLine(line);
+ var lineN = lineNo(line);
+ var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN);
+ view.lineN = lineN;
+ var built = view.built = buildLineContent(cm, view);
+ view.text = built.pre;
+ removeChildrenAndAdd(cm.display.lineMeasure, built.pre);
+ return view
+ }
+
+ // Get a {top, bottom, left, right} box (in line-local coordinates)
+ // for a given character.
+ function measureChar(cm, line, ch, bias) {
+ return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias)
+ }
+
+ // Find a line view that corresponds to the given line number.
+ function findViewForLine(cm, lineN) {
+ if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo)
+ { return cm.display.view[findViewIndex(cm, lineN)] }
+ var ext = cm.display.externalMeasured;
+ if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size)
+ { return ext }
+ }
+
+ // Measurement can be split in two steps, the set-up work that
+ // applies to the whole line, and the measurement of the actual
+ // character. Functions like coordsChar, that need to do a lot of
+ // measurements in a row, can thus ensure that the set-up work is
+ // only done once.
+ function prepareMeasureForLine(cm, line) {
+ var lineN = lineNo(line);
+ var view = findViewForLine(cm, lineN);
+ if (view && !view.text) {
+ view = null;
+ } else if (view && view.changes) {
+ updateLineForChanges(cm, view, lineN, getDimensions(cm));
+ cm.curOp.forceUpdate = true;
+ }
+ if (!view)
+ { view = updateExternalMeasurement(cm, line); }
+
+ var info = mapFromLineView(view, line, lineN);
+ return {
+ line: line, view: view, rect: null,
+ map: info.map, cache: info.cache, before: info.before,
+ hasHeights: false
+ }
+ }
+
+ // Given a prepared measurement object, measures the position of an
+ // actual character (or fetches it from the cache).
+ function measureCharPrepared(cm, prepared, ch, bias, varHeight) {
+ if (prepared.before) { ch = -1; }
+ var key = ch + (bias || ""), found;
+ if (prepared.cache.hasOwnProperty(key)) {
+ found = prepared.cache[key];
+ } else {
+ if (!prepared.rect)
+ { prepared.rect = prepared.view.text.getBoundingClientRect(); }
+ if (!prepared.hasHeights) {
+ ensureLineHeights(cm, prepared.view, prepared.rect);
+ prepared.hasHeights = true;
+ }
+ found = measureCharInner(cm, prepared, ch, bias);
+ if (!found.bogus) { prepared.cache[key] = found; }
+ }
+ return {left: found.left, right: found.right,
+ top: varHeight ? found.rtop : found.top,
+ bottom: varHeight ? found.rbottom : found.bottom}
+ }
+
+ var nullRect = {left: 0, right: 0, top: 0, bottom: 0};
+
+ function nodeAndOffsetInLineMap(map, ch, bias) {
+ var node, start, end, collapse, mStart, mEnd;
+ // First, search the line map for the text node corresponding to,
+ // or closest to, the target character.
+ for (var i = 0; i < map.length; i += 3) {
+ mStart = map[i];
+ mEnd = map[i + 1];
+ if (ch < mStart) {
+ start = 0; end = 1;
+ collapse = "left";
+ } else if (ch < mEnd) {
+ start = ch - mStart;
+ end = start + 1;
+ } else if (i == map.length - 3 || ch == mEnd && map[i + 3] > ch) {
+ end = mEnd - mStart;
+ start = end - 1;
+ if (ch >= mEnd) { collapse = "right"; }
+ }
+ if (start != null) {
+ node = map[i + 2];
+ if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right"))
+ { collapse = bias; }
+ if (bias == "left" && start == 0)
+ { while (i && map[i - 2] == map[i - 3] && map[i - 1].insertLeft) {
+ node = map[(i -= 3) + 2];
+ collapse = "left";
+ } }
+ if (bias == "right" && start == mEnd - mStart)
+ { while (i < map.length - 3 && map[i + 3] == map[i + 4] && !map[i + 5].insertLeft) {
+ node = map[(i += 3) + 2];
+ collapse = "right";
+ } }
+ break
+ }
+ }
+ return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd}
+ }
+
+ function getUsefulRect(rects, bias) {
+ var rect = nullRect;
+ if (bias == "left") { for (var i = 0; i < rects.length; i++) {
+ if ((rect = rects[i]).left != rect.right) { break }
+ } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) {
+ if ((rect = rects[i$1]).left != rect.right) { break }
+ } }
+ return rect
+ }
+
+ function measureCharInner(cm, prepared, ch, bias) {
+ var place = nodeAndOffsetInLineMap(prepared.map, ch, bias);
+ var node = place.node, start = place.start, end = place.end, collapse = place.collapse;
+
+ var rect;
+ if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates.
+ for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned
+ while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; }
+ while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; }
+ if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart)
+ { rect = node.parentNode.getBoundingClientRect(); }
+ else
+ { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); }
+ if (rect.left || rect.right || start == 0) { break }
+ end = start;
+ start = start - 1;
+ collapse = "right";
+ }
+ if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); }
+ } else { // If it is a widget, simply get the box for the whole widget.
+ if (start > 0) { collapse = bias = "right"; }
+ var rects;
+ if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1)
+ { rect = rects[bias == "right" ? rects.length - 1 : 0]; }
+ else
+ { rect = node.getBoundingClientRect(); }
+ }
+ if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) {
+ var rSpan = node.parentNode.getClientRects()[0];
+ if (rSpan)
+ { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; }
+ else
+ { rect = nullRect; }
+ }
+
+ var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top;
+ var mid = (rtop + rbot) / 2;
+ var heights = prepared.view.measure.heights;
+ var i = 0;
+ for (; i < heights.length - 1; i++)
+ { if (mid < heights[i]) { break } }
+ var top = i ? heights[i - 1] : 0, bot = heights[i];
+ var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left,
+ right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left,
+ top: top, bottom: bot};
+ if (!rect.left && !rect.right) { result.bogus = true; }
+ if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; }
+
+ return result
+ }
+
+ // Work around problem with bounding client rects on ranges being
+ // returned incorrectly when zoomed on IE10 and below.
+ function maybeUpdateRectForZooming(measure, rect) {
+ if (!window.screen || screen.logicalXDPI == null ||
+ screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure))
+ { return rect }
+ var scaleX = screen.logicalXDPI / screen.deviceXDPI;
+ var scaleY = screen.logicalYDPI / screen.deviceYDPI;
+ return {left: rect.left * scaleX, right: rect.right * scaleX,
+ top: rect.top * scaleY, bottom: rect.bottom * scaleY}
+ }
+
+ function clearLineMeasurementCacheFor(lineView) {
+ if (lineView.measure) {
+ lineView.measure.cache = {};
+ lineView.measure.heights = null;
+ if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++)
+ { lineView.measure.caches[i] = {}; } }
+ }
+ }
+
+ function clearLineMeasurementCache(cm) {
+ cm.display.externalMeasure = null;
+ removeChildren(cm.display.lineMeasure);
+ for (var i = 0; i < cm.display.view.length; i++)
+ { clearLineMeasurementCacheFor(cm.display.view[i]); }
+ }
+
+ function clearCaches(cm) {
+ clearLineMeasurementCache(cm);
+ cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null;
+ if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; }
+ cm.display.lineNumChars = null;
+ }
+
+ function pageScrollX() {
+ // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206
+ // which causes page_Offset and bounding client rects to use
+ // different reference viewports and invalidate our calculations.
+ if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) }
+ return window.pageXOffset || (document.documentElement || document.body).scrollLeft
+ }
+ function pageScrollY() {
+ if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) }
+ return window.pageYOffset || (document.documentElement || document.body).scrollTop
+ }
+
+ function widgetTopHeight(lineObj) {
+ var height = 0;
+ if (lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above)
+ { height += widgetHeight(lineObj.widgets[i]); } } }
+ return height
+ }
+
+ // Converts a {top, bottom, left, right} box from line-local
+ // coordinates into another coordinate system. Context may be one of
+ // "line", "div" (display.lineDiv), "local"./null (editor), "window",
+ // or "page".
+ function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) {
+ if (!includeWidgets) {
+ var height = widgetTopHeight(lineObj);
+ rect.top += height; rect.bottom += height;
+ }
+ if (context == "line") { return rect }
+ if (!context) { context = "local"; }
+ var yOff = heightAtLine(lineObj);
+ if (context == "local") { yOff += paddingTop(cm.display); }
+ else { yOff -= cm.display.viewOffset; }
+ if (context == "page" || context == "window") {
+ var lOff = cm.display.lineSpace.getBoundingClientRect();
+ yOff += lOff.top + (context == "window" ? 0 : pageScrollY());
+ var xOff = lOff.left + (context == "window" ? 0 : pageScrollX());
+ rect.left += xOff; rect.right += xOff;
+ }
+ rect.top += yOff; rect.bottom += yOff;
+ return rect
+ }
+
+ // Coverts a box from "div" coords to another coordinate system.
+ // Context may be "window", "page", "div", or "local"./null.
+ function fromCoordSystem(cm, coords, context) {
+ if (context == "div") { return coords }
+ var left = coords.left, top = coords.top;
+ // First move into "page" coordinate system
+ if (context == "page") {
+ left -= pageScrollX();
+ top -= pageScrollY();
+ } else if (context == "local" || !context) {
+ var localBox = cm.display.sizer.getBoundingClientRect();
+ left += localBox.left;
+ top += localBox.top;
+ }
+
+ var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect();
+ return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top}
+ }
+
+ function charCoords(cm, pos, context, lineObj, bias) {
+ if (!lineObj) { lineObj = getLine(cm.doc, pos.line); }
+ return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context)
+ }
+
+ // Returns a box for a given cursor position, which may have an
+ // 'other' property containing the position of the secondary cursor
+ // on a bidi boundary.
+ // A cursor Pos(line, char, "before") is on the same visual line as `char - 1`
+ // and after `char - 1` in writing order of `char - 1`
+ // A cursor Pos(line, char, "after") is on the same visual line as `char`
+ // and before `char` in writing order of `char`
+ // Examples (upper-case letters are RTL, lower-case are LTR):
+ // Pos(0, 1, ...)
+ // before after
+ // ab a|b a|b
+ // aB a|B aB|
+ // Ab |Ab A|b
+ // AB B|A B|A
+ // Every position after the last character on a line is considered to stick
+ // to the last character on the line.
+ function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) {
+ lineObj = lineObj || getLine(cm.doc, pos.line);
+ if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
+ function get(ch, right) {
+ var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight);
+ if (right) { m.left = m.right; } else { m.right = m.left; }
+ return intoCoordSystem(cm, lineObj, m, context)
+ }
+ var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky;
+ if (ch >= lineObj.text.length) {
+ ch = lineObj.text.length;
+ sticky = "before";
+ } else if (ch <= 0) {
+ ch = 0;
+ sticky = "after";
+ }
+ if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") }
+
+ function getBidi(ch, partPos, invert) {
+ var part = order[partPos], right = part.level == 1;
+ return get(invert ? ch - 1 : ch, right != invert)
+ }
+ var partPos = getBidiPartAt(order, ch, sticky);
+ var other = bidiOther;
+ var val = getBidi(ch, partPos, sticky == "before");
+ if (other != null) { val.other = getBidi(ch, other, sticky != "before"); }
+ return val
+ }
+
+ // Used to cheaply estimate the coordinates for a position. Used for
+ // intermediate scroll updates.
+ function estimateCoords(cm, pos) {
+ var left = 0;
+ pos = clipPos(cm.doc, pos);
+ if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; }
+ var lineObj = getLine(cm.doc, pos.line);
+ var top = heightAtLine(lineObj) + paddingTop(cm.display);
+ return {left: left, right: left, top: top, bottom: top + lineObj.height}
+ }
+
+ // Positions returned by coordsChar contain some extra information.
+ // xRel is the relative x position of the input coordinates compared
+ // to the found position (so xRel > 0 means the coordinates are to
+ // the right of the character position, for example). When outside
+ // is true, that means the coordinates lie outside the line's
+ // vertical range.
+ function PosWithInfo(line, ch, sticky, outside, xRel) {
+ var pos = Pos(line, ch, sticky);
+ pos.xRel = xRel;
+ if (outside) { pos.outside = outside; }
+ return pos
+ }
+
+ // Compute the character position closest to the given coordinates.
+ // Input must be lineSpace-local ("div" coordinate system).
+ function coordsChar(cm, x, y) {
+ var doc = cm.doc;
+ y += cm.display.viewOffset;
+ if (y < 0) { return PosWithInfo(doc.first, 0, null, -1, -1) }
+ var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1;
+ if (lineN > last)
+ { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, 1, 1) }
+ if (x < 0) { x = 0; }
+
+ var lineObj = getLine(doc, lineN);
+ for (;;) {
+ var found = coordsCharInner(cm, lineObj, lineN, x, y);
+ var collapsed = collapsedSpanAround(lineObj, found.ch + (found.xRel > 0 || found.outside > 0 ? 1 : 0));
+ if (!collapsed) { return found }
+ var rangeEnd = collapsed.find(1);
+ if (rangeEnd.line == lineN) { return rangeEnd }
+ lineObj = getLine(doc, lineN = rangeEnd.line);
+ }
+ }
+
+ function wrappedLineExtent(cm, lineObj, preparedMeasure, y) {
+ y -= widgetTopHeight(lineObj);
+ var end = lineObj.text.length;
+ var begin = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch - 1).bottom <= y; }, end, 0);
+ end = findFirst(function (ch) { return measureCharPrepared(cm, preparedMeasure, ch).top > y; }, begin, end);
+ return {begin: begin, end: end}
+ }
+
+ function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) {
+ if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); }
+ var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top;
+ return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop)
+ }
+
+ // Returns true if the given side of a box is after the given
+ // coordinates, in top-to-bottom, left-to-right order.
+ function boxIsAfter(box, x, y, left) {
+ return box.bottom <= y ? false : box.top > y ? true : (left ? box.left : box.right) > x
+ }
+
+ function coordsCharInner(cm, lineObj, lineNo, x, y) {
+ // Move y into line-local coordinate space
+ y -= heightAtLine(lineObj);
+ var preparedMeasure = prepareMeasureForLine(cm, lineObj);
+ // When directly calling `measureCharPrepared`, we have to adjust
+ // for the widgets at this line.
+ var widgetHeight = widgetTopHeight(lineObj);
+ var begin = 0, end = lineObj.text.length, ltr = true;
+
+ var order = getOrder(lineObj, cm.doc.direction);
+ // If the line isn't plain left-to-right text, first figure out
+ // which bidi section the coordinates fall into.
+ if (order) {
+ var part = (cm.options.lineWrapping ? coordsBidiPartWrapped : coordsBidiPart)
+ (cm, lineObj, lineNo, preparedMeasure, order, x, y);
+ ltr = part.level != 1;
+ // The awkward -1 offsets are needed because findFirst (called
+ // on these below) will treat its first bound as inclusive,
+ // second as exclusive, but we want to actually address the
+ // characters in the part's range
+ begin = ltr ? part.from : part.to - 1;
+ end = ltr ? part.to : part.from - 1;
+ }
+
+ // A binary search to find the first character whose bounding box
+ // starts after the coordinates. If we run across any whose box wrap
+ // the coordinates, store that.
+ var chAround = null, boxAround = null;
+ var ch = findFirst(function (ch) {
+ var box = measureCharPrepared(cm, preparedMeasure, ch);
+ box.top += widgetHeight; box.bottom += widgetHeight;
+ if (!boxIsAfter(box, x, y, false)) { return false }
+ if (box.top <= y && box.left <= x) {
+ chAround = ch;
+ boxAround = box;
+ }
+ return true
+ }, begin, end);
+
+ var baseX, sticky, outside = false;
+ // If a box around the coordinates was found, use that
+ if (boxAround) {
+ // Distinguish coordinates nearer to the left or right side of the box
+ var atLeft = x - boxAround.left < boxAround.right - x, atStart = atLeft == ltr;
+ ch = chAround + (atStart ? 0 : 1);
+ sticky = atStart ? "after" : "before";
+ baseX = atLeft ? boxAround.left : boxAround.right;
+ } else {
+ // (Adjust for extended bound, if necessary.)
+ if (!ltr && (ch == end || ch == begin)) { ch++; }
+ // To determine which side to associate with, get the box to the
+ // left of the character and compare it's vertical position to the
+ // coordinates
+ sticky = ch == 0 ? "after" : ch == lineObj.text.length ? "before" :
+ (measureCharPrepared(cm, preparedMeasure, ch - (ltr ? 1 : 0)).bottom + widgetHeight <= y) == ltr ?
+ "after" : "before";
+ // Now get accurate coordinates for this place, in order to get a
+ // base X position
+ var coords = cursorCoords(cm, Pos(lineNo, ch, sticky), "line", lineObj, preparedMeasure);
+ baseX = coords.left;
+ outside = y < coords.top ? -1 : y >= coords.bottom ? 1 : 0;
+ }
+
+ ch = skipExtendingChars(lineObj.text, ch, 1);
+ return PosWithInfo(lineNo, ch, sticky, outside, x - baseX)
+ }
+
+ function coordsBidiPart(cm, lineObj, lineNo, preparedMeasure, order, x, y) {
+ // Bidi parts are sorted left-to-right, and in a non-line-wrapping
+ // situation, we can take this ordering to correspond to the visual
+ // ordering. This finds the first part whose end is after the given
+ // coordinates.
+ var index = findFirst(function (i) {
+ var part = order[i], ltr = part.level != 1;
+ return boxIsAfter(cursorCoords(cm, Pos(lineNo, ltr ? part.to : part.from, ltr ? "before" : "after"),
+ "line", lineObj, preparedMeasure), x, y, true)
+ }, 0, order.length - 1);
+ var part = order[index];
+ // If this isn't the first part, the part's start is also after
+ // the coordinates, and the coordinates aren't on the same line as
+ // that start, move one part back.
+ if (index > 0) {
+ var ltr = part.level != 1;
+ var start = cursorCoords(cm, Pos(lineNo, ltr ? part.from : part.to, ltr ? "after" : "before"),
+ "line", lineObj, preparedMeasure);
+ if (boxIsAfter(start, x, y, true) && start.top > y)
+ { part = order[index - 1]; }
+ }
+ return part
+ }
+
+ function coordsBidiPartWrapped(cm, lineObj, _lineNo, preparedMeasure, order, x, y) {
+ // In a wrapped line, rtl text on wrapping boundaries can do things
+ // that don't correspond to the ordering in our `order` array at
+ // all, so a binary search doesn't work, and we want to return a
+ // part that only spans one line so that the binary search in
+ // coordsCharInner is safe. As such, we first find the extent of the
+ // wrapped line, and then do a flat search in which we discard any
+ // spans that aren't on the line.
+ var ref = wrappedLineExtent(cm, lineObj, preparedMeasure, y);
+ var begin = ref.begin;
+ var end = ref.end;
+ if (/\s/.test(lineObj.text.charAt(end - 1))) { end--; }
+ var part = null, closestDist = null;
+ for (var i = 0; i < order.length; i++) {
+ var p = order[i];
+ if (p.from >= end || p.to <= begin) { continue }
+ var ltr = p.level != 1;
+ var endX = measureCharPrepared(cm, preparedMeasure, ltr ? Math.min(end, p.to) - 1 : Math.max(begin, p.from)).right;
+ // Weigh against spans ending before this, so that they are only
+ // picked if nothing ends after
+ var dist = endX < x ? x - endX + 1e9 : endX - x;
+ if (!part || closestDist > dist) {
+ part = p;
+ closestDist = dist;
+ }
+ }
+ if (!part) { part = order[order.length - 1]; }
+ // Clip the part to the wrapped line.
+ if (part.from < begin) { part = {from: begin, to: part.to, level: part.level}; }
+ if (part.to > end) { part = {from: part.from, to: end, level: part.level}; }
+ return part
+ }
+
+ var measureText;
+ // Compute the default text height.
+ function textHeight(display) {
+ if (display.cachedTextHeight != null) { return display.cachedTextHeight }
+ if (measureText == null) {
+ measureText = elt("pre", null, "CodeMirror-line-like");
+ // Measure a bunch of lines, for browsers that compute
+ // fractional heights.
+ for (var i = 0; i < 49; ++i) {
+ measureText.appendChild(document.createTextNode("x"));
+ measureText.appendChild(elt("br"));
+ }
+ measureText.appendChild(document.createTextNode("x"));
+ }
+ removeChildrenAndAdd(display.measure, measureText);
+ var height = measureText.offsetHeight / 50;
+ if (height > 3) { display.cachedTextHeight = height; }
+ removeChildren(display.measure);
+ return height || 1
+ }
+
+ // Compute the default character width.
+ function charWidth(display) {
+ if (display.cachedCharWidth != null) { return display.cachedCharWidth }
+ var anchor = elt("span", "xxxxxxxxxx");
+ var pre = elt("pre", [anchor], "CodeMirror-line-like");
+ removeChildrenAndAdd(display.measure, pre);
+ var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10;
+ if (width > 2) { display.cachedCharWidth = width; }
+ return width || 10
+ }
+
+ // Do a bulk-read of the DOM positions and sizes needed to draw the
+ // view, so that we don't interleave reading and writing to the DOM.
+ function getDimensions(cm) {
+ var d = cm.display, left = {}, width = {};
+ var gutterLeft = d.gutters.clientLeft;
+ for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) {
+ var id = cm.display.gutterSpecs[i].className;
+ left[id] = n.offsetLeft + n.clientLeft + gutterLeft;
+ width[id] = n.clientWidth;
+ }
+ return {fixedPos: compensateForHScroll(d),
+ gutterTotalWidth: d.gutters.offsetWidth,
+ gutterLeft: left,
+ gutterWidth: width,
+ wrapperWidth: d.wrapper.clientWidth}
+ }
+
+ // Computes display.scroller.scrollLeft + display.gutters.offsetWidth,
+ // but using getBoundingClientRect to get a sub-pixel-accurate
+ // result.
+ function compensateForHScroll(display) {
+ return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left
+ }
+
+ // Returns a function that estimates the height of a line, to use as
+ // first approximation until the line becomes visible (and is thus
+ // properly measurable).
+ function estimateHeight(cm) {
+ var th = textHeight(cm.display), wrapping = cm.options.lineWrapping;
+ var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3);
+ return function (line) {
+ if (lineIsHidden(cm.doc, line)) { return 0 }
+
+ var widgetsHeight = 0;
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) {
+ if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; }
+ } }
+
+ if (wrapping)
+ { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th }
+ else
+ { return widgetsHeight + th }
+ }
+ }
+
+ function estimateLineHeights(cm) {
+ var doc = cm.doc, est = estimateHeight(cm);
+ doc.iter(function (line) {
+ var estHeight = est(line);
+ if (estHeight != line.height) { updateLineHeight(line, estHeight); }
+ });
+ }
+
+ // Given a mouse event, find the corresponding position. If liberal
+ // is false, it checks whether a gutter or scrollbar was clicked,
+ // and returns null if it was. forRect is used by rectangular
+ // selections, and tries to estimate a character position even for
+ // coordinates beyond the right of the text.
+ function posFromMouse(cm, e, liberal, forRect) {
+ var display = cm.display;
+ if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null }
+
+ var x, y, space = display.lineSpace.getBoundingClientRect();
+ // Fails unpredictably on IE[67] when mouse is dragged around quickly.
+ try { x = e.clientX - space.left; y = e.clientY - space.top; }
+ catch (e$1) { return null }
+ var coords = coordsChar(cm, x, y), line;
+ if (forRect && coords.xRel > 0 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) {
+ var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length;
+ coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff));
+ }
+ return coords
+ }
+
+ // Find the view element corresponding to a given line. Return null
+ // when the line isn't visible.
+ function findViewIndex(cm, n) {
+ if (n >= cm.display.viewTo) { return null }
+ n -= cm.display.viewFrom;
+ if (n < 0) { return null }
+ var view = cm.display.view;
+ for (var i = 0; i < view.length; i++) {
+ n -= view[i].size;
+ if (n < 0) { return i }
+ }
+ }
+
+ // Updates the display.view data structure for a given change to the
+ // document. From and to are in pre-change coordinates. Lendiff is
+ // the amount of lines added or subtracted by the change. This is
+ // used for changes that span multiple lines, or change the way
+ // lines are divided into visual lines. regLineChange (below)
+ // registers single-line changes.
+ function regChange(cm, from, to, lendiff) {
+ if (from == null) { from = cm.doc.first; }
+ if (to == null) { to = cm.doc.first + cm.doc.size; }
+ if (!lendiff) { lendiff = 0; }
+
+ var display = cm.display;
+ if (lendiff && to < display.viewTo &&
+ (display.updateLineNumbers == null || display.updateLineNumbers > from))
+ { display.updateLineNumbers = from; }
+
+ cm.curOp.viewChanged = true;
+
+ if (from >= display.viewTo) { // Change after
+ if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo)
+ { resetView(cm); }
+ } else if (to <= display.viewFrom) { // Change before
+ if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) {
+ resetView(cm);
+ } else {
+ display.viewFrom += lendiff;
+ display.viewTo += lendiff;
+ }
+ } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap
+ resetView(cm);
+ } else if (from <= display.viewFrom) { // Top overlap
+ var cut = viewCuttingPoint(cm, to, to + lendiff, 1);
+ if (cut) {
+ display.view = display.view.slice(cut.index);
+ display.viewFrom = cut.lineN;
+ display.viewTo += lendiff;
+ } else {
+ resetView(cm);
+ }
+ } else if (to >= display.viewTo) { // Bottom overlap
+ var cut$1 = viewCuttingPoint(cm, from, from, -1);
+ if (cut$1) {
+ display.view = display.view.slice(0, cut$1.index);
+ display.viewTo = cut$1.lineN;
+ } else {
+ resetView(cm);
+ }
+ } else { // Gap in the middle
+ var cutTop = viewCuttingPoint(cm, from, from, -1);
+ var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1);
+ if (cutTop && cutBot) {
+ display.view = display.view.slice(0, cutTop.index)
+ .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN))
+ .concat(display.view.slice(cutBot.index));
+ display.viewTo += lendiff;
+ } else {
+ resetView(cm);
+ }
+ }
+
+ var ext = display.externalMeasured;
+ if (ext) {
+ if (to < ext.lineN)
+ { ext.lineN += lendiff; }
+ else if (from < ext.lineN + ext.size)
+ { display.externalMeasured = null; }
+ }
+ }
+
+ // Register a change to a single line. Type must be one of "text",
+ // "gutter", "class", "widget"
+ function regLineChange(cm, line, type) {
+ cm.curOp.viewChanged = true;
+ var display = cm.display, ext = cm.display.externalMeasured;
+ if (ext && line >= ext.lineN && line < ext.lineN + ext.size)
+ { display.externalMeasured = null; }
+
+ if (line < display.viewFrom || line >= display.viewTo) { return }
+ var lineView = display.view[findViewIndex(cm, line)];
+ if (lineView.node == null) { return }
+ var arr = lineView.changes || (lineView.changes = []);
+ if (indexOf(arr, type) == -1) { arr.push(type); }
+ }
+
+ // Clear the view.
+ function resetView(cm) {
+ cm.display.viewFrom = cm.display.viewTo = cm.doc.first;
+ cm.display.view = [];
+ cm.display.viewOffset = 0;
+ }
+
+ function viewCuttingPoint(cm, oldN, newN, dir) {
+ var index = findViewIndex(cm, oldN), diff, view = cm.display.view;
+ if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size)
+ { return {index: index, lineN: newN} }
+ var n = cm.display.viewFrom;
+ for (var i = 0; i < index; i++)
+ { n += view[i].size; }
+ if (n != oldN) {
+ if (dir > 0) {
+ if (index == view.length - 1) { return null }
+ diff = (n + view[index].size) - oldN;
+ index++;
+ } else {
+ diff = n - oldN;
+ }
+ oldN += diff; newN += diff;
+ }
+ while (visualLineNo(cm.doc, newN) != newN) {
+ if (index == (dir < 0 ? 0 : view.length - 1)) { return null }
+ newN += dir * view[index - (dir < 0 ? 1 : 0)].size;
+ index += dir;
+ }
+ return {index: index, lineN: newN}
+ }
+
+ // Force the view to cover a given range, adding empty view element
+ // or clipping off existing ones as needed.
+ function adjustView(cm, from, to) {
+ var display = cm.display, view = display.view;
+ if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) {
+ display.view = buildViewArray(cm, from, to);
+ display.viewFrom = from;
+ } else {
+ if (display.viewFrom > from)
+ { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); }
+ else if (display.viewFrom < from)
+ { display.view = display.view.slice(findViewIndex(cm, from)); }
+ display.viewFrom = from;
+ if (display.viewTo < to)
+ { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); }
+ else if (display.viewTo > to)
+ { display.view = display.view.slice(0, findViewIndex(cm, to)); }
+ }
+ display.viewTo = to;
+ }
+
+ // Count the number of lines in the view whose DOM representation is
+ // out of date (or nonexistent).
+ function countDirtyView(cm) {
+ var view = cm.display.view, dirty = 0;
+ for (var i = 0; i < view.length; i++) {
+ var lineView = view[i];
+ if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; }
+ }
+ return dirty
+ }
+
+ function updateSelection(cm) {
+ cm.display.input.showSelection(cm.display.input.prepareSelection());
+ }
+
+ function prepareSelection(cm, primary) {
+ if ( primary === void 0 ) primary = true;
+
+ var doc = cm.doc, result = {};
+ var curFragment = result.cursors = document.createDocumentFragment();
+ var selFragment = result.selection = document.createDocumentFragment();
+
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
+ if (!primary && i == doc.sel.primIndex) { continue }
+ var range = doc.sel.ranges[i];
+ if (range.from().line >= cm.display.viewTo || range.to().line < cm.display.viewFrom) { continue }
+ var collapsed = range.empty();
+ if (collapsed || cm.options.showCursorWhenSelecting)
+ { drawSelectionCursor(cm, range.head, curFragment); }
+ if (!collapsed)
+ { drawSelectionRange(cm, range, selFragment); }
+ }
+ return result
+ }
+
+ // Draws a cursor for the given range
+ function drawSelectionCursor(cm, head, output) {
+ var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine);
+
+ var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor"));
+ cursor.style.left = pos.left + "px";
+ cursor.style.top = pos.top + "px";
+ cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px";
+
+ if (pos.other) {
+ // Secondary cursor, shown when on a 'jump' in bi-directional text
+ var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor"));
+ otherCursor.style.display = "";
+ otherCursor.style.left = pos.other.left + "px";
+ otherCursor.style.top = pos.other.top + "px";
+ otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px";
+ }
+ }
+
+ function cmpCoords(a, b) { return a.top - b.top || a.left - b.left }
+
+ // Draws the given range as a highlighted selection
+ function drawSelectionRange(cm, range, output) {
+ var display = cm.display, doc = cm.doc;
+ var fragment = document.createDocumentFragment();
+ var padding = paddingH(cm.display), leftSide = padding.left;
+ var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right;
+ var docLTR = doc.direction == "ltr";
+
+ function add(left, top, width, bottom) {
+ if (top < 0) { top = 0; }
+ top = Math.round(top);
+ bottom = Math.round(bottom);
+ fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px")));
+ }
+
+ function drawForLine(line, fromArg, toArg) {
+ var lineObj = getLine(doc, line);
+ var lineLen = lineObj.text.length;
+ var start, end;
+ function coords(ch, bias) {
+ return charCoords(cm, Pos(line, ch), "div", lineObj, bias)
+ }
+
+ function wrapX(pos, dir, side) {
+ var extent = wrappedLineExtentChar(cm, lineObj, null, pos);
+ var prop = (dir == "ltr") == (side == "after") ? "left" : "right";
+ var ch = side == "after" ? extent.begin : extent.end - (/\s/.test(lineObj.text.charAt(extent.end - 1)) ? 2 : 1);
+ return coords(ch, prop)[prop]
+ }
+
+ var order = getOrder(lineObj, doc.direction);
+ iterateBidiSections(order, fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir, i) {
+ var ltr = dir == "ltr";
+ var fromPos = coords(from, ltr ? "left" : "right");
+ var toPos = coords(to - 1, ltr ? "right" : "left");
+
+ var openStart = fromArg == null && from == 0, openEnd = toArg == null && to == lineLen;
+ var first = i == 0, last = !order || i == order.length - 1;
+ if (toPos.top - fromPos.top <= 3) { // Single line
+ var openLeft = (docLTR ? openStart : openEnd) && first;
+ var openRight = (docLTR ? openEnd : openStart) && last;
+ var left = openLeft ? leftSide : (ltr ? fromPos : toPos).left;
+ var right = openRight ? rightSide : (ltr ? toPos : fromPos).right;
+ add(left, fromPos.top, right - left, fromPos.bottom);
+ } else { // Multiple lines
+ var topLeft, topRight, botLeft, botRight;
+ if (ltr) {
+ topLeft = docLTR && openStart && first ? leftSide : fromPos.left;
+ topRight = docLTR ? rightSide : wrapX(from, dir, "before");
+ botLeft = docLTR ? leftSide : wrapX(to, dir, "after");
+ botRight = docLTR && openEnd && last ? rightSide : toPos.right;
+ } else {
+ topLeft = !docLTR ? leftSide : wrapX(from, dir, "before");
+ topRight = !docLTR && openStart && first ? rightSide : fromPos.right;
+ botLeft = !docLTR && openEnd && last ? leftSide : toPos.left;
+ botRight = !docLTR ? rightSide : wrapX(to, dir, "after");
+ }
+ add(topLeft, fromPos.top, topRight - topLeft, fromPos.bottom);
+ if (fromPos.bottom < toPos.top) { add(leftSide, fromPos.bottom, null, toPos.top); }
+ add(botLeft, toPos.top, botRight - botLeft, toPos.bottom);
+ }
+
+ if (!start || cmpCoords(fromPos, start) < 0) { start = fromPos; }
+ if (cmpCoords(toPos, start) < 0) { start = toPos; }
+ if (!end || cmpCoords(fromPos, end) < 0) { end = fromPos; }
+ if (cmpCoords(toPos, end) < 0) { end = toPos; }
+ });
+ return {start: start, end: end}
+ }
+
+ var sFrom = range.from(), sTo = range.to();
+ if (sFrom.line == sTo.line) {
+ drawForLine(sFrom.line, sFrom.ch, sTo.ch);
+ } else {
+ var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line);
+ var singleVLine = visualLine(fromLine) == visualLine(toLine);
+ var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end;
+ var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start;
+ if (singleVLine) {
+ if (leftEnd.top < rightStart.top - 2) {
+ add(leftEnd.right, leftEnd.top, null, leftEnd.bottom);
+ add(leftSide, rightStart.top, rightStart.left, rightStart.bottom);
+ } else {
+ add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom);
+ }
+ }
+ if (leftEnd.bottom < rightStart.top)
+ { add(leftSide, leftEnd.bottom, null, rightStart.top); }
+ }
+
+ output.appendChild(fragment);
+ }
+
+ // Cursor-blinking
+ function restartBlink(cm) {
+ if (!cm.state.focused) { return }
+ var display = cm.display;
+ clearInterval(display.blinker);
+ var on = true;
+ display.cursorDiv.style.visibility = "";
+ if (cm.options.cursorBlinkRate > 0)
+ { display.blinker = setInterval(function () {
+ if (!cm.hasFocus()) { onBlur(cm); }
+ display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden";
+ }, cm.options.cursorBlinkRate); }
+ else if (cm.options.cursorBlinkRate < 0)
+ { display.cursorDiv.style.visibility = "hidden"; }
+ }
+
+ function ensureFocus(cm) {
+ if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); }
+ }
+
+ function delayBlurEvent(cm) {
+ cm.state.delayingBlurEvent = true;
+ setTimeout(function () { if (cm.state.delayingBlurEvent) {
+ cm.state.delayingBlurEvent = false;
+ onBlur(cm);
+ } }, 100);
+ }
+
+ function onFocus(cm, e) {
+ if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; }
+
+ if (cm.options.readOnly == "nocursor") { return }
+ if (!cm.state.focused) {
+ signal(cm, "focus", cm, e);
+ cm.state.focused = true;
+ addClass(cm.display.wrapper, "CodeMirror-focused");
+ // This test prevents this from firing when a context
+ // menu is closed (since the input reset would kill the
+ // select-all detection hack)
+ if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) {
+ cm.display.input.reset();
+ if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730
+ }
+ cm.display.input.receivedFocus();
+ }
+ restartBlink(cm);
+ }
+ function onBlur(cm, e) {
+ if (cm.state.delayingBlurEvent) { return }
+
+ if (cm.state.focused) {
+ signal(cm, "blur", cm, e);
+ cm.state.focused = false;
+ rmClass(cm.display.wrapper, "CodeMirror-focused");
+ }
+ clearInterval(cm.display.blinker);
+ setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150);
+ }
+
+ // Read the actual heights of the rendered lines, and update their
+ // stored heights to match.
+ function updateHeightsInViewport(cm) {
+ var display = cm.display;
+ var prevBottom = display.lineDiv.offsetTop;
+ for (var i = 0; i < display.view.length; i++) {
+ var cur = display.view[i], wrapping = cm.options.lineWrapping;
+ var height = (void 0), width = 0;
+ if (cur.hidden) { continue }
+ if (ie && ie_version < 8) {
+ var bot = cur.node.offsetTop + cur.node.offsetHeight;
+ height = bot - prevBottom;
+ prevBottom = bot;
+ } else {
+ var box = cur.node.getBoundingClientRect();
+ height = box.bottom - box.top;
+ // Check that lines don't extend past the right of the current
+ // editor width
+ if (!wrapping && cur.text.firstChild)
+ { width = cur.text.firstChild.getBoundingClientRect().right - box.left - 1; }
+ }
+ var diff = cur.line.height - height;
+ if (diff > .005 || diff < -.005) {
+ updateLineHeight(cur.line, height);
+ updateWidgetHeight(cur.line);
+ if (cur.rest) { for (var j = 0; j < cur.rest.length; j++)
+ { updateWidgetHeight(cur.rest[j]); } }
+ }
+ if (width > cm.display.sizerWidth) {
+ var chWidth = Math.ceil(width / charWidth(cm.display));
+ if (chWidth > cm.display.maxLineLength) {
+ cm.display.maxLineLength = chWidth;
+ cm.display.maxLine = cur.line;
+ cm.display.maxLineChanged = true;
+ }
+ }
+ }
+ }
+
+ // Read and store the height of line widgets associated with the
+ // given line.
+ function updateWidgetHeight(line) {
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) {
+ var w = line.widgets[i], parent = w.node.parentNode;
+ if (parent) { w.height = parent.offsetHeight; }
+ } }
+ }
+
+ // Compute the lines that are visible in a given viewport (defaults
+ // the the current scroll position). viewport may contain top,
+ // height, and ensure (see op.scrollToPos) properties.
+ function visibleLines(display, doc, viewport) {
+ var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop;
+ top = Math.floor(top - paddingTop(display));
+ var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight;
+
+ var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom);
+ // Ensure is a {from: {line, ch}, to: {line, ch}} object, and
+ // forces those lines into the viewport (if possible).
+ if (viewport && viewport.ensure) {
+ var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line;
+ if (ensureFrom < from) {
+ from = ensureFrom;
+ to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight);
+ } else if (Math.min(ensureTo, doc.lastLine()) >= to) {
+ from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight);
+ to = ensureTo;
+ }
+ }
+ return {from: from, to: Math.max(to, from + 1)}
+ }
+
+ // SCROLLING THINGS INTO VIEW
+
+ // If an editor sits on the top or bottom of the window, partially
+ // scrolled out of view, this ensures that the cursor is visible.
+ function maybeScrollWindow(cm, rect) {
+ if (signalDOMEvent(cm, "scrollCursorIntoView")) { return }
+
+ var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null;
+ if (rect.top + box.top < 0) { doScroll = true; }
+ else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; }
+ if (doScroll != null && !phantom) {
+ var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;"));
+ cm.display.lineSpace.appendChild(scrollNode);
+ scrollNode.scrollIntoView(doScroll);
+ cm.display.lineSpace.removeChild(scrollNode);
+ }
+ }
+
+ // Scroll a given position into view (immediately), verifying that
+ // it actually became visible (as line heights are accurately
+ // measured, the position of something may 'drift' during drawing).
+ function scrollPosIntoView(cm, pos, end, margin) {
+ if (margin == null) { margin = 0; }
+ var rect;
+ if (!cm.options.lineWrapping && pos == end) {
+ // Set pos and end to the cursor positions around the character pos sticks to
+ // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch
+ // If pos == Pos(_, 0, "before"), pos and end are unchanged
+ pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos;
+ end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos;
+ }
+ for (var limit = 0; limit < 5; limit++) {
+ var changed = false;
+ var coords = cursorCoords(cm, pos);
+ var endCoords = !end || end == pos ? coords : cursorCoords(cm, end);
+ rect = {left: Math.min(coords.left, endCoords.left),
+ top: Math.min(coords.top, endCoords.top) - margin,
+ right: Math.max(coords.left, endCoords.left),
+ bottom: Math.max(coords.bottom, endCoords.bottom) + margin};
+ var scrollPos = calculateScrollPos(cm, rect);
+ var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft;
+ if (scrollPos.scrollTop != null) {
+ updateScrollTop(cm, scrollPos.scrollTop);
+ if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; }
+ }
+ if (scrollPos.scrollLeft != null) {
+ setScrollLeft(cm, scrollPos.scrollLeft);
+ if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; }
+ }
+ if (!changed) { break }
+ }
+ return rect
+ }
+
+ // Scroll a given set of coordinates into view (immediately).
+ function scrollIntoView(cm, rect) {
+ var scrollPos = calculateScrollPos(cm, rect);
+ if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); }
+ if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); }
+ }
+
+ // Calculate a new scroll position needed to scroll the given
+ // rectangle into view. Returns an object with scrollTop and
+ // scrollLeft properties. When these are undefined, the
+ // vertical/horizontal position does not need to be adjusted.
+ function calculateScrollPos(cm, rect) {
+ var display = cm.display, snapMargin = textHeight(cm.display);
+ if (rect.top < 0) { rect.top = 0; }
+ var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop;
+ var screen = displayHeight(cm), result = {};
+ if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; }
+ var docBottom = cm.doc.height + paddingVert(display);
+ var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin;
+ if (rect.top < screentop) {
+ result.scrollTop = atTop ? 0 : rect.top;
+ } else if (rect.bottom > screentop + screen) {
+ var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen);
+ if (newTop != screentop) { result.scrollTop = newTop; }
+ }
+
+ var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft;
+ var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0);
+ var tooWide = rect.right - rect.left > screenw;
+ if (tooWide) { rect.right = rect.left + screenw; }
+ if (rect.left < 10)
+ { result.scrollLeft = 0; }
+ else if (rect.left < screenleft)
+ { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); }
+ else if (rect.right > screenw + screenleft - 3)
+ { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; }
+ return result
+ }
+
+ // Store a relative adjustment to the scroll position in the current
+ // operation (to be applied when the operation finishes).
+ function addToScrollTop(cm, top) {
+ if (top == null) { return }
+ resolveScrollToPos(cm);
+ cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top;
+ }
+
+ // Make sure that at the end of the operation the current cursor is
+ // shown.
+ function ensureCursorVisible(cm) {
+ resolveScrollToPos(cm);
+ var cur = cm.getCursor();
+ cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin};
+ }
+
+ function scrollToCoords(cm, x, y) {
+ if (x != null || y != null) { resolveScrollToPos(cm); }
+ if (x != null) { cm.curOp.scrollLeft = x; }
+ if (y != null) { cm.curOp.scrollTop = y; }
+ }
+
+ function scrollToRange(cm, range) {
+ resolveScrollToPos(cm);
+ cm.curOp.scrollToPos = range;
+ }
+
+ // When an operation has its scrollToPos property set, and another
+ // scroll action is applied before the end of the operation, this
+ // 'simulates' scrolling that position into view in a cheap way, so
+ // that the effect of intermediate scroll commands is not ignored.
+ function resolveScrollToPos(cm) {
+ var range = cm.curOp.scrollToPos;
+ if (range) {
+ cm.curOp.scrollToPos = null;
+ var from = estimateCoords(cm, range.from), to = estimateCoords(cm, range.to);
+ scrollToCoordsRange(cm, from, to, range.margin);
+ }
+ }
+
+ function scrollToCoordsRange(cm, from, to, margin) {
+ var sPos = calculateScrollPos(cm, {
+ left: Math.min(from.left, to.left),
+ top: Math.min(from.top, to.top) - margin,
+ right: Math.max(from.right, to.right),
+ bottom: Math.max(from.bottom, to.bottom) + margin
+ });
+ scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop);
+ }
+
+ // Sync the scrollable area and scrollbars, ensure the viewport
+ // covers the visible area.
+ function updateScrollTop(cm, val) {
+ if (Math.abs(cm.doc.scrollTop - val) < 2) { return }
+ if (!gecko) { updateDisplaySimple(cm, {top: val}); }
+ setScrollTop(cm, val, true);
+ if (gecko) { updateDisplaySimple(cm); }
+ startWorker(cm, 100);
+ }
+
+ function setScrollTop(cm, val, forceScroll) {
+ val = Math.max(0, Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val));
+ if (cm.display.scroller.scrollTop == val && !forceScroll) { return }
+ cm.doc.scrollTop = val;
+ cm.display.scrollbars.setScrollTop(val);
+ if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; }
+ }
+
+ // Sync scroller and scrollbar, ensure the gutter elements are
+ // aligned.
+ function setScrollLeft(cm, val, isScroller, forceScroll) {
+ val = Math.max(0, Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth));
+ if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return }
+ cm.doc.scrollLeft = val;
+ alignHorizontally(cm);
+ if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; }
+ cm.display.scrollbars.setScrollLeft(val);
+ }
+
+ // SCROLLBARS
+
+ // Prepare DOM reads needed to update the scrollbars. Done in one
+ // shot to minimize update/measure roundtrips.
+ function measureForScrollbars(cm) {
+ var d = cm.display, gutterW = d.gutters.offsetWidth;
+ var docH = Math.round(cm.doc.height + paddingVert(cm.display));
+ return {
+ clientHeight: d.scroller.clientHeight,
+ viewHeight: d.wrapper.clientHeight,
+ scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth,
+ viewWidth: d.wrapper.clientWidth,
+ barLeft: cm.options.fixedGutter ? gutterW : 0,
+ docHeight: docH,
+ scrollHeight: docH + scrollGap(cm) + d.barHeight,
+ nativeBarWidth: d.nativeBarWidth,
+ gutterWidth: gutterW
+ }
+ }
+
+ var NativeScrollbars = function(place, scroll, cm) {
+ this.cm = cm;
+ var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar");
+ var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar");
+ vert.tabIndex = horiz.tabIndex = -1;
+ place(vert); place(horiz);
+
+ on(vert, "scroll", function () {
+ if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); }
+ });
+ on(horiz, "scroll", function () {
+ if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); }
+ });
+
+ this.checkedZeroWidth = false;
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
+ if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; }
+ };
+
+ NativeScrollbars.prototype.update = function (measure) {
+ var needsH = measure.scrollWidth > measure.clientWidth + 1;
+ var needsV = measure.scrollHeight > measure.clientHeight + 1;
+ var sWidth = measure.nativeBarWidth;
+
+ if (needsV) {
+ this.vert.style.display = "block";
+ this.vert.style.bottom = needsH ? sWidth + "px" : "0";
+ var totalHeight = measure.viewHeight - (needsH ? sWidth : 0);
+ // A bug in IE8 can cause this value to be negative, so guard it.
+ this.vert.firstChild.style.height =
+ Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px";
+ } else {
+ this.vert.style.display = "";
+ this.vert.firstChild.style.height = "0";
+ }
+
+ if (needsH) {
+ this.horiz.style.display = "block";
+ this.horiz.style.right = needsV ? sWidth + "px" : "0";
+ this.horiz.style.left = measure.barLeft + "px";
+ var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0);
+ this.horiz.firstChild.style.width =
+ Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px";
+ } else {
+ this.horiz.style.display = "";
+ this.horiz.firstChild.style.width = "0";
+ }
+
+ if (!this.checkedZeroWidth && measure.clientHeight > 0) {
+ if (sWidth == 0) { this.zeroWidthHack(); }
+ this.checkedZeroWidth = true;
+ }
+
+ return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
+ };
+
+ NativeScrollbars.prototype.setScrollLeft = function (pos) {
+ if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; }
+ if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); }
+ };
+
+ NativeScrollbars.prototype.setScrollTop = function (pos) {
+ if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; }
+ if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); }
+ };
+
+ NativeScrollbars.prototype.zeroWidthHack = function () {
+ var w = mac && !mac_geMountainLion ? "12px" : "18px";
+ this.horiz.style.height = this.vert.style.width = w;
+ this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none";
+ this.disableHoriz = new Delayed;
+ this.disableVert = new Delayed;
+ };
+
+ NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) {
+ bar.style.pointerEvents = "auto";
+ function maybeDisable() {
+ // To find out whether the scrollbar is still visible, we
+ // check whether the element under the pixel in the bottom
+ // right corner of the scrollbar box is the scrollbar box
+ // itself (when the bar is still visible) or its filler child
+ // (when the bar is hidden). If it is still visible, we keep
+ // it enabled, if it's hidden, we disable pointer events.
+ var box = bar.getBoundingClientRect();
+ var elt = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2)
+ : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1);
+ if (elt != bar) { bar.style.pointerEvents = "none"; }
+ else { delay.set(1000, maybeDisable); }
+ }
+ delay.set(1000, maybeDisable);
+ };
+
+ NativeScrollbars.prototype.clear = function () {
+ var parent = this.horiz.parentNode;
+ parent.removeChild(this.horiz);
+ parent.removeChild(this.vert);
+ };
+
+ var NullScrollbars = function () {};
+
+ NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
+ NullScrollbars.prototype.setScrollLeft = function () {};
+ NullScrollbars.prototype.setScrollTop = function () {};
+ NullScrollbars.prototype.clear = function () {};
+
+ function updateScrollbars(cm, measure) {
+ if (!measure) { measure = measureForScrollbars(cm); }
+ var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight;
+ updateScrollbarsInner(cm, measure);
+ for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) {
+ if (startWidth != cm.display.barWidth && cm.options.lineWrapping)
+ { updateHeightsInViewport(cm); }
+ updateScrollbarsInner(cm, measureForScrollbars(cm));
+ startWidth = cm.display.barWidth; startHeight = cm.display.barHeight;
+ }
+ }
+
+ // Re-synchronize the fake scrollbars with the actual size of the
+ // content.
+ function updateScrollbarsInner(cm, measure) {
+ var d = cm.display;
+ var sizes = d.scrollbars.update(measure);
+
+ d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px";
+ d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px";
+ d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent";
+
+ if (sizes.right && sizes.bottom) {
+ d.scrollbarFiller.style.display = "block";
+ d.scrollbarFiller.style.height = sizes.bottom + "px";
+ d.scrollbarFiller.style.width = sizes.right + "px";
+ } else { d.scrollbarFiller.style.display = ""; }
+ if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) {
+ d.gutterFiller.style.display = "block";
+ d.gutterFiller.style.height = sizes.bottom + "px";
+ d.gutterFiller.style.width = measure.gutterWidth + "px";
+ } else { d.gutterFiller.style.display = ""; }
+ }
+
+ var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars};
+
+ function initScrollbars(cm) {
+ if (cm.display.scrollbars) {
+ cm.display.scrollbars.clear();
+ if (cm.display.scrollbars.addClass)
+ { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
+ }
+
+ cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) {
+ cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller);
+ // Prevent clicks in the scrollbars from killing focus
+ on(node, "mousedown", function () {
+ if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); }
+ });
+ node.setAttribute("cm-not-content", "true");
+ }, function (pos, axis) {
+ if (axis == "horizontal") { setScrollLeft(cm, pos); }
+ else { updateScrollTop(cm, pos); }
+ }, cm);
+ if (cm.display.scrollbars.addClass)
+ { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); }
+ }
+
+ // Operations are used to wrap a series of changes to the editor
+ // state in such a way that each change won't have to update the
+ // cursor and display (which would be awkward, slow, and
+ // error-prone). Instead, display updates are batched and then all
+ // combined and executed at once.
+
+ var nextOpId = 0;
+ // Start a new operation.
+ function startOperation(cm) {
+ cm.curOp = {
+ cm: cm,
+ viewChanged: false, // Flag that indicates that lines might need to be redrawn
+ startHeight: cm.doc.height, // Used to detect need to update scrollbar
+ forceUpdate: false, // Used to force a redraw
+ updateInput: 0, // Whether to reset the input textarea
+ typing: false, // Whether this reset should be careful to leave existing text (for compositing)
+ changeObjs: null, // Accumulated changes, for firing change events
+ cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on
+ cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already
+ selectionChanged: false, // Whether the selection needs to be redrawn
+ updateMaxLine: false, // Set when the widest line needs to be determined anew
+ scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet
+ scrollToPos: null, // Used to scroll to a specific position
+ focus: false,
+ id: ++nextOpId // Unique ID
+ };
+ pushOperation(cm.curOp);
+ }
+
+ // Finish an operation, updating the display and signalling delayed events
+ function endOperation(cm) {
+ var op = cm.curOp;
+ if (op) { finishOperation(op, function (group) {
+ for (var i = 0; i < group.ops.length; i++)
+ { group.ops[i].cm.curOp = null; }
+ endOperations(group);
+ }); }
+ }
+
+ // The DOM updates done when an operation finishes are batched so
+ // that the minimum number of relayouts are required.
+ function endOperations(group) {
+ var ops = group.ops;
+ for (var i = 0; i < ops.length; i++) // Read DOM
+ { endOperation_R1(ops[i]); }
+ for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe)
+ { endOperation_W1(ops[i$1]); }
+ for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM
+ { endOperation_R2(ops[i$2]); }
+ for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe)
+ { endOperation_W2(ops[i$3]); }
+ for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM
+ { endOperation_finish(ops[i$4]); }
+ }
+
+ function endOperation_R1(op) {
+ var cm = op.cm, display = cm.display;
+ maybeClipScrollbars(cm);
+ if (op.updateMaxLine) { findMaxLine(cm); }
+
+ op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null ||
+ op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom ||
+ op.scrollToPos.to.line >= display.viewTo) ||
+ display.maxLineChanged && cm.options.lineWrapping;
+ op.update = op.mustUpdate &&
+ new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate);
+ }
+
+ function endOperation_W1(op) {
+ op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update);
+ }
+
+ function endOperation_R2(op) {
+ var cm = op.cm, display = cm.display;
+ if (op.updatedDisplay) { updateHeightsInViewport(cm); }
+
+ op.barMeasure = measureForScrollbars(cm);
+
+ // If the max line changed since it was last measured, measure it,
+ // and ensure the document's width matches it.
+ // updateDisplay_W2 will use these properties to do the actual resizing
+ if (display.maxLineChanged && !cm.options.lineWrapping) {
+ op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3;
+ cm.display.sizerWidth = op.adjustWidthTo;
+ op.barMeasure.scrollWidth =
+ Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth);
+ op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm));
+ }
+
+ if (op.updatedDisplay || op.selectionChanged)
+ { op.preparedSelection = display.input.prepareSelection(); }
+ }
+
+ function endOperation_W2(op) {
+ var cm = op.cm;
+
+ if (op.adjustWidthTo != null) {
+ cm.display.sizer.style.minWidth = op.adjustWidthTo + "px";
+ if (op.maxScrollLeft < cm.doc.scrollLeft)
+ { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); }
+ cm.display.maxLineChanged = false;
+ }
+
+ var takeFocus = op.focus && op.focus == activeElt();
+ if (op.preparedSelection)
+ { cm.display.input.showSelection(op.preparedSelection, takeFocus); }
+ if (op.updatedDisplay || op.startHeight != cm.doc.height)
+ { updateScrollbars(cm, op.barMeasure); }
+ if (op.updatedDisplay)
+ { setDocumentHeight(cm, op.barMeasure); }
+
+ if (op.selectionChanged) { restartBlink(cm); }
+
+ if (cm.state.focused && op.updateInput)
+ { cm.display.input.reset(op.typing); }
+ if (takeFocus) { ensureFocus(op.cm); }
+ }
+
+ function endOperation_finish(op) {
+ var cm = op.cm, display = cm.display, doc = cm.doc;
+
+ if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); }
+
+ // Abort mouse wheel delta measurement, when scrolling explicitly
+ if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos))
+ { display.wheelStartX = display.wheelStartY = null; }
+
+ // Propagate the scroll position to the actual DOM scroller
+ if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); }
+
+ if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); }
+ // If we need to scroll a specific position into view, do so.
+ if (op.scrollToPos) {
+ var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from),
+ clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin);
+ maybeScrollWindow(cm, rect);
+ }
+
+ // Fire events for markers that are hidden/unidden by editing or
+ // undoing
+ var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers;
+ if (hidden) { for (var i = 0; i < hidden.length; ++i)
+ { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } }
+ if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1)
+ { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } }
+
+ if (display.wrapper.offsetHeight)
+ { doc.scrollTop = cm.display.scroller.scrollTop; }
+
+ // Fire change events, and delayed event handlers
+ if (op.changeObjs)
+ { signal(cm, "changes", cm, op.changeObjs); }
+ if (op.update)
+ { op.update.finish(); }
+ }
+
+ // Run the given function in an operation
+ function runInOp(cm, f) {
+ if (cm.curOp) { return f() }
+ startOperation(cm);
+ try { return f() }
+ finally { endOperation(cm); }
+ }
+ // Wraps a function in an operation. Returns the wrapped function.
+ function operation(cm, f) {
+ return function() {
+ if (cm.curOp) { return f.apply(cm, arguments) }
+ startOperation(cm);
+ try { return f.apply(cm, arguments) }
+ finally { endOperation(cm); }
+ }
+ }
+ // Used to add methods to editor and doc instances, wrapping them in
+ // operations.
+ function methodOp(f) {
+ return function() {
+ if (this.curOp) { return f.apply(this, arguments) }
+ startOperation(this);
+ try { return f.apply(this, arguments) }
+ finally { endOperation(this); }
+ }
+ }
+ function docMethodOp(f) {
+ return function() {
+ var cm = this.cm;
+ if (!cm || cm.curOp) { return f.apply(this, arguments) }
+ startOperation(cm);
+ try { return f.apply(this, arguments) }
+ finally { endOperation(cm); }
+ }
+ }
+
+ // HIGHLIGHT WORKER
+
+ function startWorker(cm, time) {
+ if (cm.doc.highlightFrontier < cm.display.viewTo)
+ { cm.state.highlight.set(time, bind(highlightWorker, cm)); }
+ }
+
+ function highlightWorker(cm) {
+ var doc = cm.doc;
+ if (doc.highlightFrontier >= cm.display.viewTo) { return }
+ var end = +new Date + cm.options.workTime;
+ var context = getContextBefore(cm, doc.highlightFrontier);
+ var changedLines = [];
+
+ doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) {
+ if (context.line >= cm.display.viewFrom) { // Visible
+ var oldStyles = line.styles;
+ var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null;
+ var highlighted = highlightLine(cm, line, context, true);
+ if (resetState) { context.state = resetState; }
+ line.styles = highlighted.styles;
+ var oldCls = line.styleClasses, newCls = highlighted.classes;
+ if (newCls) { line.styleClasses = newCls; }
+ else if (oldCls) { line.styleClasses = null; }
+ var ischange = !oldStyles || oldStyles.length != line.styles.length ||
+ oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass);
+ for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; }
+ if (ischange) { changedLines.push(context.line); }
+ line.stateAfter = context.save();
+ context.nextLine();
+ } else {
+ if (line.text.length <= cm.options.maxHighlightLength)
+ { processLine(cm, line.text, context); }
+ line.stateAfter = context.line % 5 == 0 ? context.save() : null;
+ context.nextLine();
+ }
+ if (+new Date > end) {
+ startWorker(cm, cm.options.workDelay);
+ return true
+ }
+ });
+ doc.highlightFrontier = context.line;
+ doc.modeFrontier = Math.max(doc.modeFrontier, context.line);
+ if (changedLines.length) { runInOp(cm, function () {
+ for (var i = 0; i < changedLines.length; i++)
+ { regLineChange(cm, changedLines[i], "text"); }
+ }); }
+ }
+
+ // DISPLAY DRAWING
+
+ var DisplayUpdate = function(cm, viewport, force) {
+ var display = cm.display;
+
+ this.viewport = viewport;
+ // Store some values that we'll need later (but don't want to force a relayout for)
+ this.visible = visibleLines(display, cm.doc, viewport);
+ this.editorIsHidden = !display.wrapper.offsetWidth;
+ this.wrapperHeight = display.wrapper.clientHeight;
+ this.wrapperWidth = display.wrapper.clientWidth;
+ this.oldDisplayWidth = displayWidth(cm);
+ this.force = force;
+ this.dims = getDimensions(cm);
+ this.events = [];
+ };
+
+ DisplayUpdate.prototype.signal = function (emitter, type) {
+ if (hasHandler(emitter, type))
+ { this.events.push(arguments); }
+ };
+ DisplayUpdate.prototype.finish = function () {
+ for (var i = 0; i < this.events.length; i++)
+ { signal.apply(null, this.events[i]); }
+ };
+
+ function maybeClipScrollbars(cm) {
+ var display = cm.display;
+ if (!display.scrollbarsClipped && display.scroller.offsetWidth) {
+ display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth;
+ display.heightForcer.style.height = scrollGap(cm) + "px";
+ display.sizer.style.marginBottom = -display.nativeBarWidth + "px";
+ display.sizer.style.borderRightWidth = scrollGap(cm) + "px";
+ display.scrollbarsClipped = true;
+ }
+ }
+
+ function selectionSnapshot(cm) {
+ if (cm.hasFocus()) { return null }
+ var active = activeElt();
+ if (!active || !contains(cm.display.lineDiv, active)) { return null }
+ var result = {activeElt: active};
+ if (window.getSelection) {
+ var sel = window.getSelection();
+ if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) {
+ result.anchorNode = sel.anchorNode;
+ result.anchorOffset = sel.anchorOffset;
+ result.focusNode = sel.focusNode;
+ result.focusOffset = sel.focusOffset;
+ }
+ }
+ return result
+ }
+
+ function restoreSelection(snapshot) {
+ if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return }
+ snapshot.activeElt.focus();
+ if (!/^(INPUT|TEXTAREA)$/.test(snapshot.activeElt.nodeName) &&
+ snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) {
+ var sel = window.getSelection(), range = document.createRange();
+ range.setEnd(snapshot.anchorNode, snapshot.anchorOffset);
+ range.collapse(false);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ sel.extend(snapshot.focusNode, snapshot.focusOffset);
+ }
+ }
+
+ // Does the actual updating of the line display. Bails out
+ // (returning false) when there is nothing to be done and forced is
+ // false.
+ function updateDisplayIfNeeded(cm, update) {
+ var display = cm.display, doc = cm.doc;
+
+ if (update.editorIsHidden) {
+ resetView(cm);
+ return false
+ }
+
+ // Bail out if the visible area is already rendered and nothing changed.
+ if (!update.force &&
+ update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo &&
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) &&
+ display.renderedView == display.view && countDirtyView(cm) == 0)
+ { return false }
+
+ if (maybeUpdateLineNumberWidth(cm)) {
+ resetView(cm);
+ update.dims = getDimensions(cm);
+ }
+
+ // Compute a suitable new viewport (from & to)
+ var end = doc.first + doc.size;
+ var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first);
+ var to = Math.min(end, update.visible.to + cm.options.viewportMargin);
+ if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); }
+ if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); }
+ if (sawCollapsedSpans) {
+ from = visualLineNo(cm.doc, from);
+ to = visualLineEndNo(cm.doc, to);
+ }
+
+ var different = from != display.viewFrom || to != display.viewTo ||
+ display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth;
+ adjustView(cm, from, to);
+
+ display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom));
+ // Position the mover div to align with the current scroll position
+ cm.display.mover.style.top = display.viewOffset + "px";
+
+ var toUpdate = countDirtyView(cm);
+ if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view &&
+ (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo))
+ { return false }
+
+ // For big changes, we hide the enclosing element during the
+ // update, since that speeds up the operations on most browsers.
+ var selSnapshot = selectionSnapshot(cm);
+ if (toUpdate > 4) { display.lineDiv.style.display = "none"; }
+ patchDisplay(cm, display.updateLineNumbers, update.dims);
+ if (toUpdate > 4) { display.lineDiv.style.display = ""; }
+ display.renderedView = display.view;
+ // There might have been a widget with a focused element that got
+ // hidden or updated, if so re-focus it.
+ restoreSelection(selSnapshot);
+
+ // Prevent selection and cursors from interfering with the scroll
+ // width and height.
+ removeChildren(display.cursorDiv);
+ removeChildren(display.selectionDiv);
+ display.gutters.style.height = display.sizer.style.minHeight = 0;
+
+ if (different) {
+ display.lastWrapHeight = update.wrapperHeight;
+ display.lastWrapWidth = update.wrapperWidth;
+ startWorker(cm, 400);
+ }
+
+ display.updateLineNumbers = null;
+
+ return true
+ }
+
+ function postUpdateDisplay(cm, update) {
+ var viewport = update.viewport;
+
+ for (var first = true;; first = false) {
+ if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) {
+ // Clip forced viewport to actual scrollable area.
+ if (viewport && viewport.top != null)
+ { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; }
+ // Updated line heights might result in the drawn area not
+ // actually covering the viewport. Keep looping until it does.
+ update.visible = visibleLines(cm.display, cm.doc, viewport);
+ if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo)
+ { break }
+ } else if (first) {
+ update.visible = visibleLines(cm.display, cm.doc, viewport);
+ }
+ if (!updateDisplayIfNeeded(cm, update)) { break }
+ updateHeightsInViewport(cm);
+ var barMeasure = measureForScrollbars(cm);
+ updateSelection(cm);
+ updateScrollbars(cm, barMeasure);
+ setDocumentHeight(cm, barMeasure);
+ update.force = false;
+ }
+
+ update.signal(cm, "update", cm);
+ if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) {
+ update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo);
+ cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo;
+ }
+ }
+
+ function updateDisplaySimple(cm, viewport) {
+ var update = new DisplayUpdate(cm, viewport);
+ if (updateDisplayIfNeeded(cm, update)) {
+ updateHeightsInViewport(cm);
+ postUpdateDisplay(cm, update);
+ var barMeasure = measureForScrollbars(cm);
+ updateSelection(cm);
+ updateScrollbars(cm, barMeasure);
+ setDocumentHeight(cm, barMeasure);
+ update.finish();
+ }
+ }
+
+ // Sync the actual display DOM structure with display.view, removing
+ // nodes for lines that are no longer in view, and creating the ones
+ // that are not there yet, and updating the ones that are out of
+ // date.
+ function patchDisplay(cm, updateNumbersFrom, dims) {
+ var display = cm.display, lineNumbers = cm.options.lineNumbers;
+ var container = display.lineDiv, cur = container.firstChild;
+
+ function rm(node) {
+ var next = node.nextSibling;
+ // Works around a throw-scroll bug in OS X Webkit
+ if (webkit && mac && cm.display.currentWheelTarget == node)
+ { node.style.display = "none"; }
+ else
+ { node.parentNode.removeChild(node); }
+ return next
+ }
+
+ var view = display.view, lineN = display.viewFrom;
+ // Loop over the elements in the view, syncing cur (the DOM nodes
+ // in display.lineDiv) with the view as we go.
+ for (var i = 0; i < view.length; i++) {
+ var lineView = view[i];
+ if (lineView.hidden) ; else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet
+ var node = buildLineElement(cm, lineView, lineN, dims);
+ container.insertBefore(node, cur);
+ } else { // Already drawn
+ while (cur != lineView.node) { cur = rm(cur); }
+ var updateNumber = lineNumbers && updateNumbersFrom != null &&
+ updateNumbersFrom <= lineN && lineView.lineNumber;
+ if (lineView.changes) {
+ if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; }
+ updateLineForChanges(cm, lineView, lineN, dims);
+ }
+ if (updateNumber) {
+ removeChildren(lineView.lineNumber);
+ lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN)));
+ }
+ cur = lineView.node.nextSibling;
+ }
+ lineN += lineView.size;
+ }
+ while (cur) { cur = rm(cur); }
+ }
+
+ function updateGutterSpace(display) {
+ var width = display.gutters.offsetWidth;
+ display.sizer.style.marginLeft = width + "px";
+ }
+
+ function setDocumentHeight(cm, measure) {
+ cm.display.sizer.style.minHeight = measure.docHeight + "px";
+ cm.display.heightForcer.style.top = measure.docHeight + "px";
+ cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px";
+ }
+
+ // Re-align line numbers and gutter marks to compensate for
+ // horizontal scrolling.
+ function alignHorizontally(cm) {
+ var display = cm.display, view = display.view;
+ if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return }
+ var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft;
+ var gutterW = display.gutters.offsetWidth, left = comp + "px";
+ for (var i = 0; i < view.length; i++) { if (!view[i].hidden) {
+ if (cm.options.fixedGutter) {
+ if (view[i].gutter)
+ { view[i].gutter.style.left = left; }
+ if (view[i].gutterBackground)
+ { view[i].gutterBackground.style.left = left; }
+ }
+ var align = view[i].alignable;
+ if (align) { for (var j = 0; j < align.length; j++)
+ { align[j].style.left = left; } }
+ } }
+ if (cm.options.fixedGutter)
+ { display.gutters.style.left = (comp + gutterW) + "px"; }
+ }
+
+ // Used to ensure that the line number gutter is still the right
+ // size for the current document size. Returns true when an update
+ // is needed.
+ function maybeUpdateLineNumberWidth(cm) {
+ if (!cm.options.lineNumbers) { return false }
+ var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display;
+ if (last.length != display.lineNumChars) {
+ var test = display.measure.appendChild(elt("div", [elt("div", last)],
+ "CodeMirror-linenumber CodeMirror-gutter-elt"));
+ var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW;
+ display.lineGutter.style.width = "";
+ display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1;
+ display.lineNumWidth = display.lineNumInnerWidth + padding;
+ display.lineNumChars = display.lineNumInnerWidth ? last.length : -1;
+ display.lineGutter.style.width = display.lineNumWidth + "px";
+ updateGutterSpace(cm.display);
+ return true
+ }
+ return false
+ }
+
+ function getGutters(gutters, lineNumbers) {
+ var result = [], sawLineNumbers = false;
+ for (var i = 0; i < gutters.length; i++) {
+ var name = gutters[i], style = null;
+ if (typeof name != "string") { style = name.style; name = name.className; }
+ if (name == "CodeMirror-linenumbers") {
+ if (!lineNumbers) { continue }
+ else { sawLineNumbers = true; }
+ }
+ result.push({className: name, style: style});
+ }
+ if (lineNumbers && !sawLineNumbers) { result.push({className: "CodeMirror-linenumbers", style: null}); }
+ return result
+ }
+
+ // Rebuild the gutter elements, ensure the margin to the left of the
+ // code matches their width.
+ function renderGutters(display) {
+ var gutters = display.gutters, specs = display.gutterSpecs;
+ removeChildren(gutters);
+ display.lineGutter = null;
+ for (var i = 0; i < specs.length; ++i) {
+ var ref = specs[i];
+ var className = ref.className;
+ var style = ref.style;
+ var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + className));
+ if (style) { gElt.style.cssText = style; }
+ if (className == "CodeMirror-linenumbers") {
+ display.lineGutter = gElt;
+ gElt.style.width = (display.lineNumWidth || 1) + "px";
+ }
+ }
+ gutters.style.display = specs.length ? "" : "none";
+ updateGutterSpace(display);
+ }
+
+ function updateGutters(cm) {
+ renderGutters(cm.display);
+ regChange(cm);
+ alignHorizontally(cm);
+ }
+
+ // The display handles the DOM integration, both for input reading
+ // and content drawing. It holds references to DOM nodes and
+ // display-related state.
+
+ function Display(place, doc, input, options) {
+ var d = this;
+ this.input = input;
+
+ // Covers bottom-right square when both scrollbars are present.
+ d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler");
+ d.scrollbarFiller.setAttribute("cm-not-content", "true");
+ // Covers bottom of gutter when coverGutterNextToScrollbar is on
+ // and h scrollbar is present.
+ d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler");
+ d.gutterFiller.setAttribute("cm-not-content", "true");
+ // Will contain the actual code, positioned to cover the viewport.
+ d.lineDiv = eltP("div", null, "CodeMirror-code");
+ // Elements are added to these to represent selection and cursors.
+ d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1");
+ d.cursorDiv = elt("div", null, "CodeMirror-cursors");
+ // A visibility: hidden element used to find the size of things.
+ d.measure = elt("div", null, "CodeMirror-measure");
+ // When lines outside of the viewport are measured, they are drawn in this.
+ d.lineMeasure = elt("div", null, "CodeMirror-measure");
+ // Wraps everything that needs to exist inside the vertically-padded coordinate system
+ d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv],
+ null, "position: relative; outline: none");
+ var lines = eltP("div", [d.lineSpace], "CodeMirror-lines");
+ // Moved around its parent to cover visible view.
+ d.mover = elt("div", [lines], null, "position: relative");
+ // Set to the height of the document, allowing scrolling.
+ d.sizer = elt("div", [d.mover], "CodeMirror-sizer");
+ d.sizerWidth = null;
+ // Behavior of elts with overflow: auto and padding is
+ // inconsistent across browsers. This is used to ensure the
+ // scrollable area is big enough.
+ d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;");
+ // Will contain the gutters, if any.
+ d.gutters = elt("div", null, "CodeMirror-gutters");
+ d.lineGutter = null;
+ // Actual scrollable element.
+ d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll");
+ d.scroller.setAttribute("tabIndex", "-1");
+ // The element in which the editor lives.
+ d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror");
+
+ // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported)
+ if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; }
+ if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; }
+
+ if (place) {
+ if (place.appendChild) { place.appendChild(d.wrapper); }
+ else { place(d.wrapper); }
+ }
+
+ // Current rendered range (may be bigger than the view window).
+ d.viewFrom = d.viewTo = doc.first;
+ d.reportedViewFrom = d.reportedViewTo = doc.first;
+ // Information about the rendered lines.
+ d.view = [];
+ d.renderedView = null;
+ // Holds info about a single rendered line when it was rendered
+ // for measurement, while not in view.
+ d.externalMeasured = null;
+ // Empty space (in pixels) above the view
+ d.viewOffset = 0;
+ d.lastWrapHeight = d.lastWrapWidth = 0;
+ d.updateLineNumbers = null;
+
+ d.nativeBarWidth = d.barHeight = d.barWidth = 0;
+ d.scrollbarsClipped = false;
+
+ // Used to only resize the line number gutter when necessary (when
+ // the amount of lines crosses a boundary that makes its width change)
+ d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null;
+ // Set to true when a non-horizontal-scrolling line widget is
+ // added. As an optimization, line widget aligning is skipped when
+ // this is false.
+ d.alignWidgets = false;
+
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
+
+ // Tracks the maximum line length so that the horizontal scrollbar
+ // can be kept static when scrolling.
+ d.maxLine = null;
+ d.maxLineLength = 0;
+ d.maxLineChanged = false;
+
+ // Used for measuring wheel scrolling granularity
+ d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null;
+
+ // True when shift is held down.
+ d.shift = false;
+
+ // Used to track whether anything happened since the context menu
+ // was opened.
+ d.selForContextMenu = null;
+
+ d.activeTouch = null;
+
+ d.gutterSpecs = getGutters(options.gutters, options.lineNumbers);
+ renderGutters(d);
+
+ input.init(d);
+ }
+
+ // Since the delta values reported on mouse wheel events are
+ // unstandardized between browsers and even browser versions, and
+ // generally horribly unpredictable, this code starts by measuring
+ // the scroll effect that the first few mouse wheel events have,
+ // and, from that, detects the way it can convert deltas to pixel
+ // offsets afterwards.
+ //
+ // The reason we want to know the amount a wheel event will scroll
+ // is that it gives us a chance to update the display before the
+ // actual scrolling happens, reducing flickering.
+
+ var wheelSamples = 0, wheelPixelsPerUnit = null;
+ // Fill in a browser-detected starting value on browsers where we
+ // know one. These don't have to be accurate -- the result of them
+ // being wrong would just be a slight flicker on the first wheel
+ // scroll (if it is large enough).
+ if (ie) { wheelPixelsPerUnit = -.53; }
+ else if (gecko) { wheelPixelsPerUnit = 15; }
+ else if (chrome) { wheelPixelsPerUnit = -.7; }
+ else if (safari) { wheelPixelsPerUnit = -1/3; }
+
+ function wheelEventDelta(e) {
+ var dx = e.wheelDeltaX, dy = e.wheelDeltaY;
+ if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; }
+ if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; }
+ else if (dy == null) { dy = e.wheelDelta; }
+ return {x: dx, y: dy}
+ }
+ function wheelEventPixels(e) {
+ var delta = wheelEventDelta(e);
+ delta.x *= wheelPixelsPerUnit;
+ delta.y *= wheelPixelsPerUnit;
+ return delta
+ }
+
+ function onScrollWheel(cm, e) {
+ var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y;
+
+ var display = cm.display, scroll = display.scroller;
+ // Quit if there's nothing to scroll here
+ var canScrollX = scroll.scrollWidth > scroll.clientWidth;
+ var canScrollY = scroll.scrollHeight > scroll.clientHeight;
+ if (!(dx && canScrollX || dy && canScrollY)) { return }
+
+ // Webkit browsers on OS X abort momentum scrolls when the target
+ // of the scroll event is removed from the scrollable element.
+ // This hack (see related code in patchDisplay) makes sure the
+ // element is kept around.
+ if (dy && mac && webkit) {
+ outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) {
+ for (var i = 0; i < view.length; i++) {
+ if (view[i].node == cur) {
+ cm.display.currentWheelTarget = cur;
+ break outer
+ }
+ }
+ }
+ }
+
+ // On some browsers, horizontal scrolling will cause redraws to
+ // happen before the gutter has been realigned, causing it to
+ // wriggle around in a most unseemly way. When we have an
+ // estimated pixels/delta value, we just handle horizontal
+ // scrolling entirely here. It'll be slightly off from native, but
+ // better than glitching out.
+ if (dx && !gecko && !presto && wheelPixelsPerUnit != null) {
+ if (dy && canScrollY)
+ { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); }
+ setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit));
+ // Only prevent default scrolling if vertical scrolling is
+ // actually possible. Otherwise, it causes vertical scroll
+ // jitter on OSX trackpads when deltaX is small and deltaY
+ // is large (issue #3579)
+ if (!dy || (dy && canScrollY))
+ { e_preventDefault(e); }
+ display.wheelStartX = null; // Abort measurement, if in progress
+ return
+ }
+
+ // 'Project' the visible viewport to cover the area that is being
+ // scrolled into view (if we know enough to estimate it).
+ if (dy && wheelPixelsPerUnit != null) {
+ var pixels = dy * wheelPixelsPerUnit;
+ var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight;
+ if (pixels < 0) { top = Math.max(0, top + pixels - 50); }
+ else { bot = Math.min(cm.doc.height, bot + pixels + 50); }
+ updateDisplaySimple(cm, {top: top, bottom: bot});
+ }
+
+ if (wheelSamples < 20) {
+ if (display.wheelStartX == null) {
+ display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop;
+ display.wheelDX = dx; display.wheelDY = dy;
+ setTimeout(function () {
+ if (display.wheelStartX == null) { return }
+ var movedX = scroll.scrollLeft - display.wheelStartX;
+ var movedY = scroll.scrollTop - display.wheelStartY;
+ var sample = (movedY && display.wheelDY && movedY / display.wheelDY) ||
+ (movedX && display.wheelDX && movedX / display.wheelDX);
+ display.wheelStartX = display.wheelStartY = null;
+ if (!sample) { return }
+ wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1);
+ ++wheelSamples;
+ }, 200);
+ } else {
+ display.wheelDX += dx; display.wheelDY += dy;
+ }
+ }
+ }
+
+ // Selection objects are immutable. A new one is created every time
+ // the selection changes. A selection is one or more non-overlapping
+ // (and non-touching) ranges, sorted, and an integer that indicates
+ // which one is the primary selection (the one that's scrolled into
+ // view, that getCursor returns, etc).
+ var Selection = function(ranges, primIndex) {
+ this.ranges = ranges;
+ this.primIndex = primIndex;
+ };
+
+ Selection.prototype.primary = function () { return this.ranges[this.primIndex] };
+
+ Selection.prototype.equals = function (other) {
+ if (other == this) { return true }
+ if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false }
+ for (var i = 0; i < this.ranges.length; i++) {
+ var here = this.ranges[i], there = other.ranges[i];
+ if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false }
+ }
+ return true
+ };
+
+ Selection.prototype.deepCopy = function () {
+ var out = [];
+ for (var i = 0; i < this.ranges.length; i++)
+ { out[i] = new Range(copyPos(this.ranges[i].anchor), copyPos(this.ranges[i].head)); }
+ return new Selection(out, this.primIndex)
+ };
+
+ Selection.prototype.somethingSelected = function () {
+ for (var i = 0; i < this.ranges.length; i++)
+ { if (!this.ranges[i].empty()) { return true } }
+ return false
+ };
+
+ Selection.prototype.contains = function (pos, end) {
+ if (!end) { end = pos; }
+ for (var i = 0; i < this.ranges.length; i++) {
+ var range = this.ranges[i];
+ if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0)
+ { return i }
+ }
+ return -1
+ };
+
+ var Range = function(anchor, head) {
+ this.anchor = anchor; this.head = head;
+ };
+
+ Range.prototype.from = function () { return minPos(this.anchor, this.head) };
+ Range.prototype.to = function () { return maxPos(this.anchor, this.head) };
+ Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch };
+
+ // Take an unsorted, potentially overlapping set of ranges, and
+ // build a selection out of it. 'Consumes' ranges array (modifying
+ // it).
+ function normalizeSelection(cm, ranges, primIndex) {
+ var mayTouch = cm && cm.options.selectionsMayTouch;
+ var prim = ranges[primIndex];
+ ranges.sort(function (a, b) { return cmp(a.from(), b.from()); });
+ primIndex = indexOf(ranges, prim);
+ for (var i = 1; i < ranges.length; i++) {
+ var cur = ranges[i], prev = ranges[i - 1];
+ var diff = cmp(prev.to(), cur.from());
+ if (mayTouch && !cur.empty() ? diff > 0 : diff >= 0) {
+ var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to());
+ var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head;
+ if (i <= primIndex) { --primIndex; }
+ ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to));
+ }
+ }
+ return new Selection(ranges, primIndex)
+ }
+
+ function simpleSelection(anchor, head) {
+ return new Selection([new Range(anchor, head || anchor)], 0)
+ }
+
+ // Compute the position of the end of a change (its 'to' property
+ // refers to the pre-change end).
+ function changeEnd(change) {
+ if (!change.text) { return change.to }
+ return Pos(change.from.line + change.text.length - 1,
+ lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0))
+ }
+
+ // Adjust a position to refer to the post-change position of the
+ // same text, or the end of the change if the change covers it.
+ function adjustForChange(pos, change) {
+ if (cmp(pos, change.from) < 0) { return pos }
+ if (cmp(pos, change.to) <= 0) { return changeEnd(change) }
+
+ var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch;
+ if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; }
+ return Pos(line, ch)
+ }
+
+ function computeSelAfterChange(doc, change) {
+ var out = [];
+ for (var i = 0; i < doc.sel.ranges.length; i++) {
+ var range = doc.sel.ranges[i];
+ out.push(new Range(adjustForChange(range.anchor, change),
+ adjustForChange(range.head, change)));
+ }
+ return normalizeSelection(doc.cm, out, doc.sel.primIndex)
+ }
+
+ function offsetPos(pos, old, nw) {
+ if (pos.line == old.line)
+ { return Pos(nw.line, pos.ch - old.ch + nw.ch) }
+ else
+ { return Pos(nw.line + (pos.line - old.line), pos.ch) }
+ }
+
+ // Used by replaceSelections to allow moving the selection to the
+ // start or around the replaced test. Hint may be "start" or "around".
+ function computeReplacedSel(doc, changes, hint) {
+ var out = [];
+ var oldPrev = Pos(doc.first, 0), newPrev = oldPrev;
+ for (var i = 0; i < changes.length; i++) {
+ var change = changes[i];
+ var from = offsetPos(change.from, oldPrev, newPrev);
+ var to = offsetPos(changeEnd(change), oldPrev, newPrev);
+ oldPrev = change.to;
+ newPrev = to;
+ if (hint == "around") {
+ var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0;
+ out[i] = new Range(inv ? to : from, inv ? from : to);
+ } else {
+ out[i] = new Range(from, from);
+ }
+ }
+ return new Selection(out, doc.sel.primIndex)
+ }
+
+ // Used to get the editor into a consistent state again when options change.
+
+ function loadMode(cm) {
+ cm.doc.mode = getMode(cm.options, cm.doc.modeOption);
+ resetModeState(cm);
+ }
+
+ function resetModeState(cm) {
+ cm.doc.iter(function (line) {
+ if (line.stateAfter) { line.stateAfter = null; }
+ if (line.styles) { line.styles = null; }
+ });
+ cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first;
+ startWorker(cm, 100);
+ cm.state.modeGen++;
+ if (cm.curOp) { regChange(cm); }
+ }
+
+ // DOCUMENT DATA STRUCTURE
+
+ // By default, updates that start and end at the beginning of a line
+ // are treated specially, in order to make the association of line
+ // widgets and marker elements with the text behave more intuitive.
+ function isWholeLineUpdate(doc, change) {
+ return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" &&
+ (!doc.cm || doc.cm.options.wholeLineUpdateBefore)
+ }
+
+ // Perform a change on the document data structure.
+ function updateDoc(doc, change, markedSpans, estimateHeight) {
+ function spansFor(n) {return markedSpans ? markedSpans[n] : null}
+ function update(line, text, spans) {
+ updateLine(line, text, spans, estimateHeight);
+ signalLater(line, "change", line, change);
+ }
+ function linesFor(start, end) {
+ var result = [];
+ for (var i = start; i < end; ++i)
+ { result.push(new Line(text[i], spansFor(i), estimateHeight)); }
+ return result
+ }
+
+ var from = change.from, to = change.to, text = change.text;
+ var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line);
+ var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line;
+
+ // Adjust the line structure
+ if (change.full) {
+ doc.insert(0, linesFor(0, text.length));
+ doc.remove(text.length, doc.size - text.length);
+ } else if (isWholeLineUpdate(doc, change)) {
+ // This is a whole-line replace. Treated specially to make
+ // sure line objects move the way they are supposed to.
+ var added = linesFor(0, text.length - 1);
+ update(lastLine, lastLine.text, lastSpans);
+ if (nlines) { doc.remove(from.line, nlines); }
+ if (added.length) { doc.insert(from.line, added); }
+ } else if (firstLine == lastLine) {
+ if (text.length == 1) {
+ update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans);
+ } else {
+ var added$1 = linesFor(1, text.length - 1);
+ added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight));
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+ doc.insert(from.line + 1, added$1);
+ }
+ } else if (text.length == 1) {
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0));
+ doc.remove(from.line + 1, nlines);
+ } else {
+ update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0));
+ update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans);
+ var added$2 = linesFor(1, text.length - 1);
+ if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); }
+ doc.insert(from.line + 1, added$2);
+ }
+
+ signalLater(doc, "change", doc, change);
+ }
+
+ // Call f for all linked documents.
+ function linkedDocs(doc, f, sharedHistOnly) {
+ function propagate(doc, skip, sharedHist) {
+ if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) {
+ var rel = doc.linked[i];
+ if (rel.doc == skip) { continue }
+ var shared = sharedHist && rel.sharedHist;
+ if (sharedHistOnly && !shared) { continue }
+ f(rel.doc, shared);
+ propagate(rel.doc, doc, shared);
+ } }
+ }
+ propagate(doc, null, true);
+ }
+
+ // Attach a document to an editor.
+ function attachDoc(cm, doc) {
+ if (doc.cm) { throw new Error("This document is already in use.") }
+ cm.doc = doc;
+ doc.cm = cm;
+ estimateLineHeights(cm);
+ loadMode(cm);
+ setDirectionClass(cm);
+ if (!cm.options.lineWrapping) { findMaxLine(cm); }
+ cm.options.mode = doc.modeOption;
+ regChange(cm);
+ }
+
+ function setDirectionClass(cm) {
+ (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl");
+ }
+
+ function directionChanged(cm) {
+ runInOp(cm, function () {
+ setDirectionClass(cm);
+ regChange(cm);
+ });
+ }
+
+ function History(startGen) {
+ // Arrays of change events and selections. Doing something adds an
+ // event to done and clears undo. Undoing moves events from done
+ // to undone, redoing moves them in the other direction.
+ this.done = []; this.undone = [];
+ this.undoDepth = Infinity;
+ // Used to track when changes can be merged into a single undo
+ // event
+ this.lastModTime = this.lastSelTime = 0;
+ this.lastOp = this.lastSelOp = null;
+ this.lastOrigin = this.lastSelOrigin = null;
+ // Used by the isClean() method
+ this.generation = this.maxGeneration = startGen || 1;
+ }
+
+ // Create a history change event from an updateDoc-style change
+ // object.
+ function historyChangeFromChange(doc, change) {
+ var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)};
+ attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1);
+ linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true);
+ return histChange
+ }
+
+ // Pop all selection events off the end of a history array. Stop at
+ // a change event.
+ function clearSelectionEvents(array) {
+ while (array.length) {
+ var last = lst(array);
+ if (last.ranges) { array.pop(); }
+ else { break }
+ }
+ }
+
+ // Find the top change event in the history. Pop off selection
+ // events that are in the way.
+ function lastChangeEvent(hist, force) {
+ if (force) {
+ clearSelectionEvents(hist.done);
+ return lst(hist.done)
+ } else if (hist.done.length && !lst(hist.done).ranges) {
+ return lst(hist.done)
+ } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) {
+ hist.done.pop();
+ return lst(hist.done)
+ }
+ }
+
+ // Register a change in the history. Merges changes that are within
+ // a single operation, or are close together with an origin that
+ // allows merging (starting with "+") into a single event.
+ function addChangeToHistory(doc, change, selAfter, opId) {
+ var hist = doc.history;
+ hist.undone.length = 0;
+ var time = +new Date, cur;
+ var last;
+
+ if ((hist.lastOp == opId ||
+ hist.lastOrigin == change.origin && change.origin &&
+ ((change.origin.charAt(0) == "+" && hist.lastModTime > time - (doc.cm ? doc.cm.options.historyEventDelay : 500)) ||
+ change.origin.charAt(0) == "*")) &&
+ (cur = lastChangeEvent(hist, hist.lastOp == opId))) {
+ // Merge this change into the last event
+ last = lst(cur.changes);
+ if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) {
+ // Optimized case for simple insertion -- don't want to add
+ // new changesets for every character typed
+ last.to = changeEnd(change);
+ } else {
+ // Add new sub-event
+ cur.changes.push(historyChangeFromChange(doc, change));
+ }
+ } else {
+ // Can not be merged, start a new event.
+ var before = lst(hist.done);
+ if (!before || !before.ranges)
+ { pushSelectionToHistory(doc.sel, hist.done); }
+ cur = {changes: [historyChangeFromChange(doc, change)],
+ generation: hist.generation};
+ hist.done.push(cur);
+ while (hist.done.length > hist.undoDepth) {
+ hist.done.shift();
+ if (!hist.done[0].ranges) { hist.done.shift(); }
+ }
+ }
+ hist.done.push(selAfter);
+ hist.generation = ++hist.maxGeneration;
+ hist.lastModTime = hist.lastSelTime = time;
+ hist.lastOp = hist.lastSelOp = opId;
+ hist.lastOrigin = hist.lastSelOrigin = change.origin;
+
+ if (!last) { signal(doc, "historyAdded"); }
+ }
+
+ function selectionEventCanBeMerged(doc, origin, prev, sel) {
+ var ch = origin.charAt(0);
+ return ch == "*" ||
+ ch == "+" &&
+ prev.ranges.length == sel.ranges.length &&
+ prev.somethingSelected() == sel.somethingSelected() &&
+ new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500)
+ }
+
+ // Called whenever the selection changes, sets the new selection as
+ // the pending selection in the history, and pushes the old pending
+ // selection into the 'done' array when it was significantly
+ // different (in number of selected ranges, emptiness, or time).
+ function addSelectionToHistory(doc, sel, opId, options) {
+ var hist = doc.history, origin = options && options.origin;
+
+ // A new event is started when the previous origin does not match
+ // the current, or the origins don't allow matching. Origins
+ // starting with * are always merged, those starting with + are
+ // merged when similar and close together in time.
+ if (opId == hist.lastSelOp ||
+ (origin && hist.lastSelOrigin == origin &&
+ (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin ||
+ selectionEventCanBeMerged(doc, origin, lst(hist.done), sel))))
+ { hist.done[hist.done.length - 1] = sel; }
+ else
+ { pushSelectionToHistory(sel, hist.done); }
+
+ hist.lastSelTime = +new Date;
+ hist.lastSelOrigin = origin;
+ hist.lastSelOp = opId;
+ if (options && options.clearRedo !== false)
+ { clearSelectionEvents(hist.undone); }
+ }
+
+ function pushSelectionToHistory(sel, dest) {
+ var top = lst(dest);
+ if (!(top && top.ranges && top.equals(sel)))
+ { dest.push(sel); }
+ }
+
+ // Used to store marked span information in the history.
+ function attachLocalSpans(doc, change, from, to) {
+ var existing = change["spans_" + doc.id], n = 0;
+ doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) {
+ if (line.markedSpans)
+ { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; }
+ ++n;
+ });
+ }
+
+ // When un/re-doing restores text containing marked spans, those
+ // that have been explicitly cleared should not be restored.
+ function removeClearedSpans(spans) {
+ if (!spans) { return null }
+ var out;
+ for (var i = 0; i < spans.length; ++i) {
+ if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } }
+ else if (out) { out.push(spans[i]); }
+ }
+ return !out ? spans : out.length ? out : null
+ }
+
+ // Retrieve and filter the old marked spans stored in a change event.
+ function getOldSpans(doc, change) {
+ var found = change["spans_" + doc.id];
+ if (!found) { return null }
+ var nw = [];
+ for (var i = 0; i < change.text.length; ++i)
+ { nw.push(removeClearedSpans(found[i])); }
+ return nw
+ }
+
+ // Used for un/re-doing changes from the history. Combines the
+ // result of computing the existing spans with the set of spans that
+ // existed in the history (so that deleting around a span and then
+ // undoing brings back the span).
+ function mergeOldSpans(doc, change) {
+ var old = getOldSpans(doc, change);
+ var stretched = stretchSpansOverChange(doc, change);
+ if (!old) { return stretched }
+ if (!stretched) { return old }
+
+ for (var i = 0; i < old.length; ++i) {
+ var oldCur = old[i], stretchCur = stretched[i];
+ if (oldCur && stretchCur) {
+ spans: for (var j = 0; j < stretchCur.length; ++j) {
+ var span = stretchCur[j];
+ for (var k = 0; k < oldCur.length; ++k)
+ { if (oldCur[k].marker == span.marker) { continue spans } }
+ oldCur.push(span);
+ }
+ } else if (stretchCur) {
+ old[i] = stretchCur;
+ }
+ }
+ return old
+ }
+
+ // Used both to provide a JSON-safe object in .getHistory, and, when
+ // detaching a document, to split the history in two
+ function copyHistoryArray(events, newGroup, instantiateSel) {
+ var copy = [];
+ for (var i = 0; i < events.length; ++i) {
+ var event = events[i];
+ if (event.ranges) {
+ copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event);
+ continue
+ }
+ var changes = event.changes, newChanges = [];
+ copy.push({changes: newChanges});
+ for (var j = 0; j < changes.length; ++j) {
+ var change = changes[j], m = (void 0);
+ newChanges.push({from: change.from, to: change.to, text: change.text});
+ if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) {
+ if (indexOf(newGroup, Number(m[1])) > -1) {
+ lst(newChanges)[prop] = change[prop];
+ delete change[prop];
+ }
+ } } }
+ }
+ }
+ return copy
+ }
+
+ // The 'scroll' parameter given to many of these indicated whether
+ // the new cursor position should be scrolled into view after
+ // modifying the selection.
+
+ // If shift is held or the extend flag is set, extends a range to
+ // include a given position (and optionally a second position).
+ // Otherwise, simply returns the range between the given positions.
+ // Used for cursor motion and such.
+ function extendRange(range, head, other, extend) {
+ if (extend) {
+ var anchor = range.anchor;
+ if (other) {
+ var posBefore = cmp(head, anchor) < 0;
+ if (posBefore != (cmp(other, anchor) < 0)) {
+ anchor = head;
+ head = other;
+ } else if (posBefore != (cmp(head, other) < 0)) {
+ head = other;
+ }
+ }
+ return new Range(anchor, head)
+ } else {
+ return new Range(other || head, head)
+ }
+ }
+
+ // Extend the primary selection range, discard the rest.
+ function extendSelection(doc, head, other, options, extend) {
+ if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); }
+ setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options);
+ }
+
+ // Extend all selections (pos is an array of selections with length
+ // equal the number of selections)
+ function extendSelections(doc, heads, options) {
+ var out = [];
+ var extend = doc.cm && (doc.cm.display.shift || doc.extend);
+ for (var i = 0; i < doc.sel.ranges.length; i++)
+ { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); }
+ var newSel = normalizeSelection(doc.cm, out, doc.sel.primIndex);
+ setSelection(doc, newSel, options);
+ }
+
+ // Updates a single range in the selection.
+ function replaceOneSelection(doc, i, range, options) {
+ var ranges = doc.sel.ranges.slice(0);
+ ranges[i] = range;
+ setSelection(doc, normalizeSelection(doc.cm, ranges, doc.sel.primIndex), options);
+ }
+
+ // Reset the selection to a single range.
+ function setSimpleSelection(doc, anchor, head, options) {
+ setSelection(doc, simpleSelection(anchor, head), options);
+ }
+
+ // Give beforeSelectionChange handlers a change to influence a
+ // selection update.
+ function filterSelectionChange(doc, sel, options) {
+ var obj = {
+ ranges: sel.ranges,
+ update: function(ranges) {
+ this.ranges = [];
+ for (var i = 0; i < ranges.length; i++)
+ { this.ranges[i] = new Range(clipPos(doc, ranges[i].anchor),
+ clipPos(doc, ranges[i].head)); }
+ },
+ origin: options && options.origin
+ };
+ signal(doc, "beforeSelectionChange", doc, obj);
+ if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); }
+ if (obj.ranges != sel.ranges) { return normalizeSelection(doc.cm, obj.ranges, obj.ranges.length - 1) }
+ else { return sel }
+ }
+
+ function setSelectionReplaceHistory(doc, sel, options) {
+ var done = doc.history.done, last = lst(done);
+ if (last && last.ranges) {
+ done[done.length - 1] = sel;
+ setSelectionNoUndo(doc, sel, options);
+ } else {
+ setSelection(doc, sel, options);
+ }
+ }
+
+ // Set a new selection.
+ function setSelection(doc, sel, options) {
+ setSelectionNoUndo(doc, sel, options);
+ addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options);
+ }
+
+ function setSelectionNoUndo(doc, sel, options) {
+ if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange"))
+ { sel = filterSelectionChange(doc, sel, options); }
+
+ var bias = options && options.bias ||
+ (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1);
+ setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true));
+
+ if (!(options && options.scroll === false) && doc.cm)
+ { ensureCursorVisible(doc.cm); }
+ }
+
+ function setSelectionInner(doc, sel) {
+ if (sel.equals(doc.sel)) { return }
+
+ doc.sel = sel;
+
+ if (doc.cm) {
+ doc.cm.curOp.updateInput = 1;
+ doc.cm.curOp.selectionChanged = true;
+ signalCursorActivity(doc.cm);
+ }
+ signalLater(doc, "cursorActivity", doc);
+ }
+
+ // Verify that the selection does not partially select any atomic
+ // marked ranges.
+ function reCheckSelection(doc) {
+ setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false));
+ }
+
+ // Return a selection that does not partially select any atomic
+ // ranges.
+ function skipAtomicInSelection(doc, sel, bias, mayClear) {
+ var out;
+ for (var i = 0; i < sel.ranges.length; i++) {
+ var range = sel.ranges[i];
+ var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i];
+ var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear);
+ var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear);
+ if (out || newAnchor != range.anchor || newHead != range.head) {
+ if (!out) { out = sel.ranges.slice(0, i); }
+ out[i] = new Range(newAnchor, newHead);
+ }
+ }
+ return out ? normalizeSelection(doc.cm, out, sel.primIndex) : sel
+ }
+
+ function skipAtomicInner(doc, pos, oldPos, dir, mayClear) {
+ var line = getLine(doc, pos.line);
+ if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) {
+ var sp = line.markedSpans[i], m = sp.marker;
+
+ // Determine if we should prevent the cursor being placed to the left/right of an atomic marker
+ // Historically this was determined using the inclusiveLeft/Right option, but the new way to control it
+ // is with selectLeft/Right
+ var preventCursorLeft = ("selectLeft" in m) ? !m.selectLeft : m.inclusiveLeft;
+ var preventCursorRight = ("selectRight" in m) ? !m.selectRight : m.inclusiveRight;
+
+ if ((sp.from == null || (preventCursorLeft ? sp.from <= pos.ch : sp.from < pos.ch)) &&
+ (sp.to == null || (preventCursorRight ? sp.to >= pos.ch : sp.to > pos.ch))) {
+ if (mayClear) {
+ signal(m, "beforeCursorEnter");
+ if (m.explicitlyCleared) {
+ if (!line.markedSpans) { break }
+ else {--i; continue}
+ }
+ }
+ if (!m.atomic) { continue }
+
+ if (oldPos) {
+ var near = m.find(dir < 0 ? 1 : -1), diff = (void 0);
+ if (dir < 0 ? preventCursorRight : preventCursorLeft)
+ { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); }
+ if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0))
+ { return skipAtomicInner(doc, near, pos, dir, mayClear) }
+ }
+
+ var far = m.find(dir < 0 ? -1 : 1);
+ if (dir < 0 ? preventCursorLeft : preventCursorRight)
+ { far = movePos(doc, far, dir, far.line == pos.line ? line : null); }
+ return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null
+ }
+ } }
+ return pos
+ }
+
+ // Ensure a given position is not inside an atomic range.
+ function skipAtomic(doc, pos, oldPos, bias, mayClear) {
+ var dir = bias || 1;
+ var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) ||
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) ||
+ skipAtomicInner(doc, pos, oldPos, -dir, mayClear) ||
+ (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true));
+ if (!found) {
+ doc.cantEdit = true;
+ return Pos(doc.first, 0)
+ }
+ return found
+ }
+
+ function movePos(doc, pos, dir, line) {
+ if (dir < 0 && pos.ch == 0) {
+ if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) }
+ else { return null }
+ } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) {
+ if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) }
+ else { return null }
+ } else {
+ return new Pos(pos.line, pos.ch + dir)
+ }
+ }
+
+ function selectAll(cm) {
+ cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll);
+ }
+
+ // UPDATING
+
+ // Allow "beforeChange" event handlers to influence a change
+ function filterChange(doc, change, update) {
+ var obj = {
+ canceled: false,
+ from: change.from,
+ to: change.to,
+ text: change.text,
+ origin: change.origin,
+ cancel: function () { return obj.canceled = true; }
+ };
+ if (update) { obj.update = function (from, to, text, origin) {
+ if (from) { obj.from = clipPos(doc, from); }
+ if (to) { obj.to = clipPos(doc, to); }
+ if (text) { obj.text = text; }
+ if (origin !== undefined) { obj.origin = origin; }
+ }; }
+ signal(doc, "beforeChange", doc, obj);
+ if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); }
+
+ if (obj.canceled) {
+ if (doc.cm) { doc.cm.curOp.updateInput = 2; }
+ return null
+ }
+ return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin}
+ }
+
+ // Apply a change to a document, and add it to the document's
+ // history, and propagating it to all linked documents.
+ function makeChange(doc, change, ignoreReadOnly) {
+ if (doc.cm) {
+ if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) }
+ if (doc.cm.state.suppressEdits) { return }
+ }
+
+ if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) {
+ change = filterChange(doc, change, true);
+ if (!change) { return }
+ }
+
+ // Possibly split or suppress the update based on the presence
+ // of read-only spans in its range.
+ var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to);
+ if (split) {
+ for (var i = split.length - 1; i >= 0; --i)
+ { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text, origin: change.origin}); }
+ } else {
+ makeChangeInner(doc, change);
+ }
+ }
+
+ function makeChangeInner(doc, change) {
+ if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return }
+ var selAfter = computeSelAfterChange(doc, change);
+ addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN);
+
+ makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change));
+ var rebased = [];
+
+ linkedDocs(doc, function (doc, sharedHist) {
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+ rebaseHist(doc.history, change);
+ rebased.push(doc.history);
+ }
+ makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change));
+ });
+ }
+
+ // Revert a change stored in a document's history.
+ function makeChangeFromHistory(doc, type, allowSelectionOnly) {
+ var suppress = doc.cm && doc.cm.state.suppressEdits;
+ if (suppress && !allowSelectionOnly) { return }
+
+ var hist = doc.history, event, selAfter = doc.sel;
+ var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done;
+
+ // Verify that there is a useable event (so that ctrl-z won't
+ // needlessly clear selection events)
+ var i = 0;
+ for (; i < source.length; i++) {
+ event = source[i];
+ if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges)
+ { break }
+ }
+ if (i == source.length) { return }
+ hist.lastOrigin = hist.lastSelOrigin = null;
+
+ for (;;) {
+ event = source.pop();
+ if (event.ranges) {
+ pushSelectionToHistory(event, dest);
+ if (allowSelectionOnly && !event.equals(doc.sel)) {
+ setSelection(doc, event, {clearRedo: false});
+ return
+ }
+ selAfter = event;
+ } else if (suppress) {
+ source.push(event);
+ return
+ } else { break }
+ }
+
+ // Build up a reverse change object to add to the opposite history
+ // stack (redo when undoing, and vice versa).
+ var antiChanges = [];
+ pushSelectionToHistory(selAfter, dest);
+ dest.push({changes: antiChanges, generation: hist.generation});
+ hist.generation = event.generation || ++hist.maxGeneration;
+
+ var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange");
+
+ var loop = function ( i ) {
+ var change = event.changes[i];
+ change.origin = type;
+ if (filter && !filterChange(doc, change, false)) {
+ source.length = 0;
+ return {}
+ }
+
+ antiChanges.push(historyChangeFromChange(doc, change));
+
+ var after = i ? computeSelAfterChange(doc, change) : lst(source);
+ makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change));
+ if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); }
+ var rebased = [];
+
+ // Propagate to the linked documents
+ linkedDocs(doc, function (doc, sharedHist) {
+ if (!sharedHist && indexOf(rebased, doc.history) == -1) {
+ rebaseHist(doc.history, change);
+ rebased.push(doc.history);
+ }
+ makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change));
+ });
+ };
+
+ for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) {
+ var returned = loop( i$1 );
+
+ if ( returned ) return returned.v;
+ }
+ }
+
+ // Sub-views need their line numbers shifted when text is added
+ // above or below them in the parent document.
+ function shiftDoc(doc, distance) {
+ if (distance == 0) { return }
+ doc.first += distance;
+ doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range(
+ Pos(range.anchor.line + distance, range.anchor.ch),
+ Pos(range.head.line + distance, range.head.ch)
+ ); }), doc.sel.primIndex);
+ if (doc.cm) {
+ regChange(doc.cm, doc.first, doc.first - distance, distance);
+ for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++)
+ { regLineChange(doc.cm, l, "gutter"); }
+ }
+ }
+
+ // More lower-level change function, handling only a single document
+ // (not linked ones).
+ function makeChangeSingleDoc(doc, change, selAfter, spans) {
+ if (doc.cm && !doc.cm.curOp)
+ { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) }
+
+ if (change.to.line < doc.first) {
+ shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line));
+ return
+ }
+ if (change.from.line > doc.lastLine()) { return }
+
+ // Clip the change to the size of this doc
+ if (change.from.line < doc.first) {
+ var shift = change.text.length - 1 - (doc.first - change.from.line);
+ shiftDoc(doc, shift);
+ change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch),
+ text: [lst(change.text)], origin: change.origin};
+ }
+ var last = doc.lastLine();
+ if (change.to.line > last) {
+ change = {from: change.from, to: Pos(last, getLine(doc, last).text.length),
+ text: [change.text[0]], origin: change.origin};
+ }
+
+ change.removed = getBetween(doc, change.from, change.to);
+
+ if (!selAfter) { selAfter = computeSelAfterChange(doc, change); }
+ if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); }
+ else { updateDoc(doc, change, spans); }
+ setSelectionNoUndo(doc, selAfter, sel_dontScroll);
+
+ if (doc.cantEdit && skipAtomic(doc, Pos(doc.firstLine(), 0)))
+ { doc.cantEdit = false; }
+ }
+
+ // Handle the interaction of a change to a document with the editor
+ // that this document is part of.
+ function makeChangeSingleDocInEditor(cm, change, spans) {
+ var doc = cm.doc, display = cm.display, from = change.from, to = change.to;
+
+ var recomputeMaxLength = false, checkWidthStart = from.line;
+ if (!cm.options.lineWrapping) {
+ checkWidthStart = lineNo(visualLine(getLine(doc, from.line)));
+ doc.iter(checkWidthStart, to.line + 1, function (line) {
+ if (line == display.maxLine) {
+ recomputeMaxLength = true;
+ return true
+ }
+ });
+ }
+
+ if (doc.sel.contains(change.from, change.to) > -1)
+ { signalCursorActivity(cm); }
+
+ updateDoc(doc, change, spans, estimateHeight(cm));
+
+ if (!cm.options.lineWrapping) {
+ doc.iter(checkWidthStart, from.line + change.text.length, function (line) {
+ var len = lineLength(line);
+ if (len > display.maxLineLength) {
+ display.maxLine = line;
+ display.maxLineLength = len;
+ display.maxLineChanged = true;
+ recomputeMaxLength = false;
+ }
+ });
+ if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; }
+ }
+
+ retreatFrontier(doc, from.line);
+ startWorker(cm, 400);
+
+ var lendiff = change.text.length - (to.line - from.line) - 1;
+ // Remember that these lines changed, for updating the display
+ if (change.full)
+ { regChange(cm); }
+ else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change))
+ { regLineChange(cm, from.line, "text"); }
+ else
+ { regChange(cm, from.line, to.line + 1, lendiff); }
+
+ var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change");
+ if (changeHandler || changesHandler) {
+ var obj = {
+ from: from, to: to,
+ text: change.text,
+ removed: change.removed,
+ origin: change.origin
+ };
+ if (changeHandler) { signalLater(cm, "change", cm, obj); }
+ if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); }
+ }
+ cm.display.selForContextMenu = null;
+ }
+
+ function replaceRange(doc, code, from, to, origin) {
+ var assign;
+
+ if (!to) { to = from; }
+ if (cmp(to, from) < 0) { (assign = [to, from], from = assign[0], to = assign[1]); }
+ if (typeof code == "string") { code = doc.splitLines(code); }
+ makeChange(doc, {from: from, to: to, text: code, origin: origin});
+ }
+
+ // Rebasing/resetting history to deal with externally-sourced changes
+
+ function rebaseHistSelSingle(pos, from, to, diff) {
+ if (to < pos.line) {
+ pos.line += diff;
+ } else if (from < pos.line) {
+ pos.line = from;
+ pos.ch = 0;
+ }
+ }
+
+ // Tries to rebase an array of history events given a change in the
+ // document. If the change touches the same lines as the event, the
+ // event, and everything 'behind' it, is discarded. If the change is
+ // before the event, the event's positions are updated. Uses a
+ // copy-on-write scheme for the positions, to avoid having to
+ // reallocate them all on every rebase, but also avoid problems with
+ // shared position objects being unsafely updated.
+ function rebaseHistArray(array, from, to, diff) {
+ for (var i = 0; i < array.length; ++i) {
+ var sub = array[i], ok = true;
+ if (sub.ranges) {
+ if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; }
+ for (var j = 0; j < sub.ranges.length; j++) {
+ rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff);
+ rebaseHistSelSingle(sub.ranges[j].head, from, to, diff);
+ }
+ continue
+ }
+ for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) {
+ var cur = sub.changes[j$1];
+ if (to < cur.from.line) {
+ cur.from = Pos(cur.from.line + diff, cur.from.ch);
+ cur.to = Pos(cur.to.line + diff, cur.to.ch);
+ } else if (from <= cur.to.line) {
+ ok = false;
+ break
+ }
+ }
+ if (!ok) {
+ array.splice(0, i + 1);
+ i = 0;
+ }
+ }
+ }
+
+ function rebaseHist(hist, change) {
+ var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1;
+ rebaseHistArray(hist.done, from, to, diff);
+ rebaseHistArray(hist.undone, from, to, diff);
+ }
+
+ // Utility for applying a change to a line by handle or number,
+ // returning the number and optionally registering the line as
+ // changed.
+ function changeLine(doc, handle, changeType, op) {
+ var no = handle, line = handle;
+ if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); }
+ else { no = lineNo(handle); }
+ if (no == null) { return null }
+ if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); }
+ return line
+ }
+
+ // The document is represented as a BTree consisting of leaves, with
+ // chunk of lines in them, and branches, with up to ten leaves or
+ // other branch nodes below them. The top node is always a branch
+ // node, and is the document object itself (meaning it has
+ // additional methods and properties).
+ //
+ // All nodes have parent links. The tree is used both to go from
+ // line numbers to line objects, and to go from objects to numbers.
+ // It also indexes by height, and is used to convert between height
+ // and line object, and to find the total height of the document.
+ //
+ // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html
+
+ function LeafChunk(lines) {
+ this.lines = lines;
+ this.parent = null;
+ var height = 0;
+ for (var i = 0; i < lines.length; ++i) {
+ lines[i].parent = this;
+ height += lines[i].height;
+ }
+ this.height = height;
+ }
+
+ LeafChunk.prototype = {
+ chunkSize: function() { return this.lines.length },
+
+ // Remove the n lines at offset 'at'.
+ removeInner: function(at, n) {
+ for (var i = at, e = at + n; i < e; ++i) {
+ var line = this.lines[i];
+ this.height -= line.height;
+ cleanUpLine(line);
+ signalLater(line, "delete");
+ }
+ this.lines.splice(at, n);
+ },
+
+ // Helper used to collapse a small branch into a single leaf.
+ collapse: function(lines) {
+ lines.push.apply(lines, this.lines);
+ },
+
+ // Insert the given array of lines at offset 'at', count them as
+ // having the given height.
+ insertInner: function(at, lines, height) {
+ this.height += height;
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
+ for (var i = 0; i < lines.length; ++i) { lines[i].parent = this; }
+ },
+
+ // Used to iterate over a part of the tree.
+ iterN: function(at, n, op) {
+ for (var e = at + n; at < e; ++at)
+ { if (op(this.lines[at])) { return true } }
+ }
+ };
+
+ function BranchChunk(children) {
+ this.children = children;
+ var size = 0, height = 0;
+ for (var i = 0; i < children.length; ++i) {
+ var ch = children[i];
+ size += ch.chunkSize(); height += ch.height;
+ ch.parent = this;
+ }
+ this.size = size;
+ this.height = height;
+ this.parent = null;
+ }
+
+ BranchChunk.prototype = {
+ chunkSize: function() { return this.size },
+
+ removeInner: function(at, n) {
+ this.size -= n;
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var rm = Math.min(n, sz - at), oldHeight = child.height;
+ child.removeInner(at, rm);
+ this.height -= oldHeight - child.height;
+ if (sz == rm) { this.children.splice(i--, 1); child.parent = null; }
+ if ((n -= rm) == 0) { break }
+ at = 0;
+ } else { at -= sz; }
+ }
+ // If the result is smaller than 25 lines, ensure that it is a
+ // single leaf node.
+ if (this.size - n < 25 &&
+ (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) {
+ var lines = [];
+ this.collapse(lines);
+ this.children = [new LeafChunk(lines)];
+ this.children[0].parent = this;
+ }
+ },
+
+ collapse: function(lines) {
+ for (var i = 0; i < this.children.length; ++i) { this.children[i].collapse(lines); }
+ },
+
+ insertInner: function(at, lines, height) {
+ this.size += lines.length;
+ this.height += height;
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at <= sz) {
+ child.insertInner(at, lines, height);
+ if (child.lines && child.lines.length > 50) {
+ // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced.
+ // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest.
+ var remaining = child.lines.length % 25 + 25;
+ for (var pos = remaining; pos < child.lines.length;) {
+ var leaf = new LeafChunk(child.lines.slice(pos, pos += 25));
+ child.height -= leaf.height;
+ this.children.splice(++i, 0, leaf);
+ leaf.parent = this;
+ }
+ child.lines = child.lines.slice(0, remaining);
+ this.maybeSpill();
+ }
+ break
+ }
+ at -= sz;
+ }
+ },
+
+ // When a node has grown, check whether it should be split.
+ maybeSpill: function() {
+ if (this.children.length <= 10) { return }
+ var me = this;
+ do {
+ var spilled = me.children.splice(me.children.length - 5, 5);
+ var sibling = new BranchChunk(spilled);
+ if (!me.parent) { // Become the parent node
+ var copy = new BranchChunk(me.children);
+ copy.parent = me;
+ me.children = [copy, sibling];
+ me = copy;
+ } else {
+ me.size -= sibling.size;
+ me.height -= sibling.height;
+ var myIndex = indexOf(me.parent.children, me);
+ me.parent.children.splice(myIndex + 1, 0, sibling);
+ }
+ sibling.parent = me.parent;
+ } while (me.children.length > 10)
+ me.parent.maybeSpill();
+ },
+
+ iterN: function(at, n, op) {
+ for (var i = 0; i < this.children.length; ++i) {
+ var child = this.children[i], sz = child.chunkSize();
+ if (at < sz) {
+ var used = Math.min(n, sz - at);
+ if (child.iterN(at, used, op)) { return true }
+ if ((n -= used) == 0) { break }
+ at = 0;
+ } else { at -= sz; }
+ }
+ }
+ };
+
+ // Line widgets are block elements displayed above or below a line.
+
+ var LineWidget = function(doc, node, options) {
+ if (options) { for (var opt in options) { if (options.hasOwnProperty(opt))
+ { this[opt] = options[opt]; } } }
+ this.doc = doc;
+ this.node = node;
+ };
+
+ LineWidget.prototype.clear = function () {
+ var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line);
+ if (no == null || !ws) { return }
+ for (var i = 0; i < ws.length; ++i) { if (ws[i] == this) { ws.splice(i--, 1); } }
+ if (!ws.length) { line.widgets = null; }
+ var height = widgetHeight(this);
+ updateLineHeight(line, Math.max(0, line.height - height));
+ if (cm) {
+ runInOp(cm, function () {
+ adjustScrollWhenAboveVisible(cm, line, -height);
+ regLineChange(cm, no, "widget");
+ });
+ signalLater(cm, "lineWidgetCleared", cm, this, no);
+ }
+ };
+
+ LineWidget.prototype.changed = function () {
+ var this$1 = this;
+
+ var oldH = this.height, cm = this.doc.cm, line = this.line;
+ this.height = null;
+ var diff = widgetHeight(this) - oldH;
+ if (!diff) { return }
+ if (!lineIsHidden(this.doc, line)) { updateLineHeight(line, line.height + diff); }
+ if (cm) {
+ runInOp(cm, function () {
+ cm.curOp.forceUpdate = true;
+ adjustScrollWhenAboveVisible(cm, line, diff);
+ signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line));
+ });
+ }
+ };
+ eventMixin(LineWidget);
+
+ function adjustScrollWhenAboveVisible(cm, line, diff) {
+ if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop))
+ { addToScrollTop(cm, diff); }
+ }
+
+ function addLineWidget(doc, handle, node, options) {
+ var widget = new LineWidget(doc, node, options);
+ var cm = doc.cm;
+ if (cm && widget.noHScroll) { cm.display.alignWidgets = true; }
+ changeLine(doc, handle, "widget", function (line) {
+ var widgets = line.widgets || (line.widgets = []);
+ if (widget.insertAt == null) { widgets.push(widget); }
+ else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); }
+ widget.line = line;
+ if (cm && !lineIsHidden(doc, line)) {
+ var aboveVisible = heightAtLine(line) < doc.scrollTop;
+ updateLineHeight(line, line.height + widgetHeight(widget));
+ if (aboveVisible) { addToScrollTop(cm, widget.height); }
+ cm.curOp.forceUpdate = true;
+ }
+ return true
+ });
+ if (cm) { signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); }
+ return widget
+ }
+
+ // TEXTMARKERS
+
+ // Created with markText and setBookmark methods. A TextMarker is a
+ // handle that can be used to clear or find a marked position in the
+ // document. Line objects hold arrays (markedSpans) containing
+ // {from, to, marker} object pointing to such marker objects, and
+ // indicating that such a marker is present on that line. Multiple
+ // lines may point to the same marker when it spans across lines.
+ // The spans will have null for their from/to properties when the
+ // marker continues beyond the start/end of the line. Markers have
+ // links back to the lines they currently touch.
+
+ // Collapsed markers have unique ids, in order to be able to order
+ // them, which is needed for uniquely determining an outer marker
+ // when they overlap (they may nest, but not partially overlap).
+ var nextMarkerId = 0;
+
+ var TextMarker = function(doc, type) {
+ this.lines = [];
+ this.type = type;
+ this.doc = doc;
+ this.id = ++nextMarkerId;
+ };
+
+ // Clear the marker.
+ TextMarker.prototype.clear = function () {
+ if (this.explicitlyCleared) { return }
+ var cm = this.doc.cm, withOp = cm && !cm.curOp;
+ if (withOp) { startOperation(cm); }
+ if (hasHandler(this, "clear")) {
+ var found = this.find();
+ if (found) { signalLater(this, "clear", found.from, found.to); }
+ }
+ var min = null, max = null;
+ for (var i = 0; i < this.lines.length; ++i) {
+ var line = this.lines[i];
+ var span = getMarkedSpanFor(line.markedSpans, this);
+ if (cm && !this.collapsed) { regLineChange(cm, lineNo(line), "text"); }
+ else if (cm) {
+ if (span.to != null) { max = lineNo(line); }
+ if (span.from != null) { min = lineNo(line); }
+ }
+ line.markedSpans = removeMarkedSpan(line.markedSpans, span);
+ if (span.from == null && this.collapsed && !lineIsHidden(this.doc, line) && cm)
+ { updateLineHeight(line, textHeight(cm.display)); }
+ }
+ if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) {
+ var visual = visualLine(this.lines[i$1]), len = lineLength(visual);
+ if (len > cm.display.maxLineLength) {
+ cm.display.maxLine = visual;
+ cm.display.maxLineLength = len;
+ cm.display.maxLineChanged = true;
+ }
+ } }
+
+ if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); }
+ this.lines.length = 0;
+ this.explicitlyCleared = true;
+ if (this.atomic && this.doc.cantEdit) {
+ this.doc.cantEdit = false;
+ if (cm) { reCheckSelection(cm.doc); }
+ }
+ if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); }
+ if (withOp) { endOperation(cm); }
+ if (this.parent) { this.parent.clear(); }
+ };
+
+ // Find the position of the marker in the document. Returns a {from,
+ // to} object by default. Side can be passed to get a specific side
+ // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the
+ // Pos objects returned contain a line object, rather than a line
+ // number (used to prevent looking up the same line twice).
+ TextMarker.prototype.find = function (side, lineObj) {
+ if (side == null && this.type == "bookmark") { side = 1; }
+ var from, to;
+ for (var i = 0; i < this.lines.length; ++i) {
+ var line = this.lines[i];
+ var span = getMarkedSpanFor(line.markedSpans, this);
+ if (span.from != null) {
+ from = Pos(lineObj ? line : lineNo(line), span.from);
+ if (side == -1) { return from }
+ }
+ if (span.to != null) {
+ to = Pos(lineObj ? line : lineNo(line), span.to);
+ if (side == 1) { return to }
+ }
+ }
+ return from && {from: from, to: to}
+ };
+
+ // Signals that the marker's widget changed, and surrounding layout
+ // should be recomputed.
+ TextMarker.prototype.changed = function () {
+ var this$1 = this;
+
+ var pos = this.find(-1, true), widget = this, cm = this.doc.cm;
+ if (!pos || !cm) { return }
+ runInOp(cm, function () {
+ var line = pos.line, lineN = lineNo(pos.line);
+ var view = findViewForLine(cm, lineN);
+ if (view) {
+ clearLineMeasurementCacheFor(view);
+ cm.curOp.selectionChanged = cm.curOp.forceUpdate = true;
+ }
+ cm.curOp.updateMaxLine = true;
+ if (!lineIsHidden(widget.doc, line) && widget.height != null) {
+ var oldHeight = widget.height;
+ widget.height = null;
+ var dHeight = widgetHeight(widget) - oldHeight;
+ if (dHeight)
+ { updateLineHeight(line, line.height + dHeight); }
+ }
+ signalLater(cm, "markerChanged", cm, this$1);
+ });
+ };
+
+ TextMarker.prototype.attachLine = function (line) {
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp;
+ if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1)
+ { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); }
+ }
+ this.lines.push(line);
+ };
+
+ TextMarker.prototype.detachLine = function (line) {
+ this.lines.splice(indexOf(this.lines, line), 1);
+ if (!this.lines.length && this.doc.cm) {
+ var op = this.doc.cm.curOp
+ ;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this);
+ }
+ };
+ eventMixin(TextMarker);
+
+ // Create a marker, wire it up to the right lines, and
+ function markText(doc, from, to, options, type) {
+ // Shared markers (across linked documents) are handled separately
+ // (markTextShared will call out to this again, once per
+ // document).
+ if (options && options.shared) { return markTextShared(doc, from, to, options, type) }
+ // Ensure we are in an operation.
+ if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) }
+
+ var marker = new TextMarker(doc, type), diff = cmp(from, to);
+ if (options) { copyObj(options, marker, false); }
+ // Don't connect empty markers unless clearWhenEmpty is false
+ if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false)
+ { return marker }
+ if (marker.replacedWith) {
+ // Showing up as a widget implies collapsed (widget replaces text)
+ marker.collapsed = true;
+ marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget");
+ if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); }
+ if (options.insertLeft) { marker.widgetNode.insertLeft = true; }
+ }
+ if (marker.collapsed) {
+ if (conflictingCollapsedRange(doc, from.line, from, to, marker) ||
+ from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker))
+ { throw new Error("Inserting collapsed marker partially overlapping an existing one") }
+ seeCollapsedSpans();
+ }
+
+ if (marker.addToHistory)
+ { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); }
+
+ var curLine = from.line, cm = doc.cm, updateMaxLine;
+ doc.iter(curLine, to.line + 1, function (line) {
+ if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine)
+ { updateMaxLine = true; }
+ if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); }
+ addMarkedSpan(line, new MarkedSpan(marker,
+ curLine == from.line ? from.ch : null,
+ curLine == to.line ? to.ch : null));
+ ++curLine;
+ });
+ // lineIsHidden depends on the presence of the spans, so needs a second pass
+ if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) {
+ if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); }
+ }); }
+
+ if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); }
+
+ if (marker.readOnly) {
+ seeReadOnlySpans();
+ if (doc.history.done.length || doc.history.undone.length)
+ { doc.clearHistory(); }
+ }
+ if (marker.collapsed) {
+ marker.id = ++nextMarkerId;
+ marker.atomic = true;
+ }
+ if (cm) {
+ // Sync editor state
+ if (updateMaxLine) { cm.curOp.updateMaxLine = true; }
+ if (marker.collapsed)
+ { regChange(cm, from.line, to.line + 1); }
+ else if (marker.className || marker.startStyle || marker.endStyle || marker.css ||
+ marker.attributes || marker.title)
+ { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } }
+ if (marker.atomic) { reCheckSelection(cm.doc); }
+ signalLater(cm, "markerAdded", cm, marker);
+ }
+ return marker
+ }
+
+ // SHARED TEXTMARKERS
+
+ // A shared marker spans multiple linked documents. It is
+ // implemented as a meta-marker-object controlling multiple normal
+ // markers.
+ var SharedTextMarker = function(markers, primary) {
+ this.markers = markers;
+ this.primary = primary;
+ for (var i = 0; i < markers.length; ++i)
+ { markers[i].parent = this; }
+ };
+
+ SharedTextMarker.prototype.clear = function () {
+ if (this.explicitlyCleared) { return }
+ this.explicitlyCleared = true;
+ for (var i = 0; i < this.markers.length; ++i)
+ { this.markers[i].clear(); }
+ signalLater(this, "clear");
+ };
+
+ SharedTextMarker.prototype.find = function (side, lineObj) {
+ return this.primary.find(side, lineObj)
+ };
+ eventMixin(SharedTextMarker);
+
+ function markTextShared(doc, from, to, options, type) {
+ options = copyObj(options);
+ options.shared = false;
+ var markers = [markText(doc, from, to, options, type)], primary = markers[0];
+ var widget = options.widgetNode;
+ linkedDocs(doc, function (doc) {
+ if (widget) { options.widgetNode = widget.cloneNode(true); }
+ markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type));
+ for (var i = 0; i < doc.linked.length; ++i)
+ { if (doc.linked[i].isParent) { return } }
+ primary = lst(markers);
+ });
+ return new SharedTextMarker(markers, primary)
+ }
+
+ function findSharedMarkers(doc) {
+ return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; })
+ }
+
+ function copySharedMarkers(doc, markers) {
+ for (var i = 0; i < markers.length; i++) {
+ var marker = markers[i], pos = marker.find();
+ var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to);
+ if (cmp(mFrom, mTo)) {
+ var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type);
+ marker.markers.push(subMark);
+ subMark.parent = marker;
+ }
+ }
+ }
+
+ function detachSharedMarkers(markers) {
+ var loop = function ( i ) {
+ var marker = markers[i], linked = [marker.primary.doc];
+ linkedDocs(marker.primary.doc, function (d) { return linked.push(d); });
+ for (var j = 0; j < marker.markers.length; j++) {
+ var subMarker = marker.markers[j];
+ if (indexOf(linked, subMarker.doc) == -1) {
+ subMarker.parent = null;
+ marker.markers.splice(j--, 1);
+ }
+ }
+ };
+
+ for (var i = 0; i < markers.length; i++) loop( i );
+ }
+
+ var nextDocId = 0;
+ var Doc = function(text, mode, firstLine, lineSep, direction) {
+ if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) }
+ if (firstLine == null) { firstLine = 0; }
+
+ BranchChunk.call(this, [new LeafChunk([new Line("", null)])]);
+ this.first = firstLine;
+ this.scrollTop = this.scrollLeft = 0;
+ this.cantEdit = false;
+ this.cleanGeneration = 1;
+ this.modeFrontier = this.highlightFrontier = firstLine;
+ var start = Pos(firstLine, 0);
+ this.sel = simpleSelection(start);
+ this.history = new History(null);
+ this.id = ++nextDocId;
+ this.modeOption = mode;
+ this.lineSep = lineSep;
+ this.direction = (direction == "rtl") ? "rtl" : "ltr";
+ this.extend = false;
+
+ if (typeof text == "string") { text = this.splitLines(text); }
+ updateDoc(this, {from: start, to: start, text: text});
+ setSelection(this, simpleSelection(start), sel_dontScroll);
+ };
+
+ Doc.prototype = createObj(BranchChunk.prototype, {
+ constructor: Doc,
+ // Iterate over the document. Supports two forms -- with only one
+ // argument, it calls that for each line in the document. With
+ // three, it iterates over the range given by the first two (with
+ // the second being non-inclusive).
+ iter: function(from, to, op) {
+ if (op) { this.iterN(from - this.first, to - from, op); }
+ else { this.iterN(this.first, this.first + this.size, from); }
+ },
+
+ // Non-public interface for adding and removing lines.
+ insert: function(at, lines) {
+ var height = 0;
+ for (var i = 0; i < lines.length; ++i) { height += lines[i].height; }
+ this.insertInner(at - this.first, lines, height);
+ },
+ remove: function(at, n) { this.removeInner(at - this.first, n); },
+
+ // From here, the methods are part of the public interface. Most
+ // are also available from CodeMirror (editor) instances.
+
+ getValue: function(lineSep) {
+ var lines = getLines(this, this.first, this.first + this.size);
+ if (lineSep === false) { return lines }
+ return lines.join(lineSep || this.lineSeparator())
+ },
+ setValue: docMethodOp(function(code) {
+ var top = Pos(this.first, 0), last = this.first + this.size - 1;
+ makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length),
+ text: this.splitLines(code), origin: "setValue", full: true}, true);
+ if (this.cm) { scrollToCoords(this.cm, 0, 0); }
+ setSelection(this, simpleSelection(top), sel_dontScroll);
+ }),
+ replaceRange: function(code, from, to, origin) {
+ from = clipPos(this, from);
+ to = to ? clipPos(this, to) : from;
+ replaceRange(this, code, from, to, origin);
+ },
+ getRange: function(from, to, lineSep) {
+ var lines = getBetween(this, clipPos(this, from), clipPos(this, to));
+ if (lineSep === false) { return lines }
+ return lines.join(lineSep || this.lineSeparator())
+ },
+
+ getLine: function(line) {var l = this.getLineHandle(line); return l && l.text},
+
+ getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }},
+ getLineNumber: function(line) {return lineNo(line)},
+
+ getLineHandleVisualStart: function(line) {
+ if (typeof line == "number") { line = getLine(this, line); }
+ return visualLine(line)
+ },
+
+ lineCount: function() {return this.size},
+ firstLine: function() {return this.first},
+ lastLine: function() {return this.first + this.size - 1},
+
+ clipPos: function(pos) {return clipPos(this, pos)},
+
+ getCursor: function(start) {
+ var range = this.sel.primary(), pos;
+ if (start == null || start == "head") { pos = range.head; }
+ else if (start == "anchor") { pos = range.anchor; }
+ else if (start == "end" || start == "to" || start === false) { pos = range.to(); }
+ else { pos = range.from(); }
+ return pos
+ },
+ listSelections: function() { return this.sel.ranges },
+ somethingSelected: function() {return this.sel.somethingSelected()},
+
+ setCursor: docMethodOp(function(line, ch, options) {
+ setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options);
+ }),
+ setSelection: docMethodOp(function(anchor, head, options) {
+ setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options);
+ }),
+ extendSelection: docMethodOp(function(head, other, options) {
+ extendSelection(this, clipPos(this, head), other && clipPos(this, other), options);
+ }),
+ extendSelections: docMethodOp(function(heads, options) {
+ extendSelections(this, clipPosArray(this, heads), options);
+ }),
+ extendSelectionsBy: docMethodOp(function(f, options) {
+ var heads = map(this.sel.ranges, f);
+ extendSelections(this, clipPosArray(this, heads), options);
+ }),
+ setSelections: docMethodOp(function(ranges, primary, options) {
+ if (!ranges.length) { return }
+ var out = [];
+ for (var i = 0; i < ranges.length; i++)
+ { out[i] = new Range(clipPos(this, ranges[i].anchor),
+ clipPos(this, ranges[i].head)); }
+ if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); }
+ setSelection(this, normalizeSelection(this.cm, out, primary), options);
+ }),
+ addSelection: docMethodOp(function(anchor, head, options) {
+ var ranges = this.sel.ranges.slice(0);
+ ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor)));
+ setSelection(this, normalizeSelection(this.cm, ranges, ranges.length - 1), options);
+ }),
+
+ getSelection: function(lineSep) {
+ var ranges = this.sel.ranges, lines;
+ for (var i = 0; i < ranges.length; i++) {
+ var sel = getBetween(this, ranges[i].from(), ranges[i].to());
+ lines = lines ? lines.concat(sel) : sel;
+ }
+ if (lineSep === false) { return lines }
+ else { return lines.join(lineSep || this.lineSeparator()) }
+ },
+ getSelections: function(lineSep) {
+ var parts = [], ranges = this.sel.ranges;
+ for (var i = 0; i < ranges.length; i++) {
+ var sel = getBetween(this, ranges[i].from(), ranges[i].to());
+ if (lineSep !== false) { sel = sel.join(lineSep || this.lineSeparator()); }
+ parts[i] = sel;
+ }
+ return parts
+ },
+ replaceSelection: function(code, collapse, origin) {
+ var dup = [];
+ for (var i = 0; i < this.sel.ranges.length; i++)
+ { dup[i] = code; }
+ this.replaceSelections(dup, collapse, origin || "+input");
+ },
+ replaceSelections: docMethodOp(function(code, collapse, origin) {
+ var changes = [], sel = this.sel;
+ for (var i = 0; i < sel.ranges.length; i++) {
+ var range = sel.ranges[i];
+ changes[i] = {from: range.from(), to: range.to(), text: this.splitLines(code[i]), origin: origin};
+ }
+ var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse);
+ for (var i$1 = changes.length - 1; i$1 >= 0; i$1--)
+ { makeChange(this, changes[i$1]); }
+ if (newSel) { setSelectionReplaceHistory(this, newSel); }
+ else if (this.cm) { ensureCursorVisible(this.cm); }
+ }),
+ undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}),
+ redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}),
+ undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}),
+ redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}),
+
+ setExtending: function(val) {this.extend = val;},
+ getExtending: function() {return this.extend},
+
+ historySize: function() {
+ var hist = this.history, done = 0, undone = 0;
+ for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } }
+ for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } }
+ return {undo: done, redo: undone}
+ },
+ clearHistory: function() {
+ var this$1 = this;
+
+ this.history = new History(this.history.maxGeneration);
+ linkedDocs(this, function (doc) { return doc.history = this$1.history; }, true);
+ },
+
+ markClean: function() {
+ this.cleanGeneration = this.changeGeneration(true);
+ },
+ changeGeneration: function(forceSplit) {
+ if (forceSplit)
+ { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; }
+ return this.history.generation
+ },
+ isClean: function (gen) {
+ return this.history.generation == (gen || this.cleanGeneration)
+ },
+
+ getHistory: function() {
+ return {done: copyHistoryArray(this.history.done),
+ undone: copyHistoryArray(this.history.undone)}
+ },
+ setHistory: function(histData) {
+ var hist = this.history = new History(this.history.maxGeneration);
+ hist.done = copyHistoryArray(histData.done.slice(0), null, true);
+ hist.undone = copyHistoryArray(histData.undone.slice(0), null, true);
+ },
+
+ setGutterMarker: docMethodOp(function(line, gutterID, value) {
+ return changeLine(this, line, "gutter", function (line) {
+ var markers = line.gutterMarkers || (line.gutterMarkers = {});
+ markers[gutterID] = value;
+ if (!value && isEmpty(markers)) { line.gutterMarkers = null; }
+ return true
+ })
+ }),
+
+ clearGutter: docMethodOp(function(gutterID) {
+ var this$1 = this;
+
+ this.iter(function (line) {
+ if (line.gutterMarkers && line.gutterMarkers[gutterID]) {
+ changeLine(this$1, line, "gutter", function () {
+ line.gutterMarkers[gutterID] = null;
+ if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; }
+ return true
+ });
+ }
+ });
+ }),
+
+ lineInfo: function(line) {
+ var n;
+ if (typeof line == "number") {
+ if (!isLine(this, line)) { return null }
+ n = line;
+ line = getLine(this, line);
+ if (!line) { return null }
+ } else {
+ n = lineNo(line);
+ if (n == null) { return null }
+ }
+ return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers,
+ textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass,
+ widgets: line.widgets}
+ },
+
+ addLineClass: docMethodOp(function(handle, where, cls) {
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
+ var prop = where == "text" ? "textClass"
+ : where == "background" ? "bgClass"
+ : where == "gutter" ? "gutterClass" : "wrapClass";
+ if (!line[prop]) { line[prop] = cls; }
+ else if (classTest(cls).test(line[prop])) { return false }
+ else { line[prop] += " " + cls; }
+ return true
+ })
+ }),
+ removeLineClass: docMethodOp(function(handle, where, cls) {
+ return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) {
+ var prop = where == "text" ? "textClass"
+ : where == "background" ? "bgClass"
+ : where == "gutter" ? "gutterClass" : "wrapClass";
+ var cur = line[prop];
+ if (!cur) { return false }
+ else if (cls == null) { line[prop] = null; }
+ else {
+ var found = cur.match(classTest(cls));
+ if (!found) { return false }
+ var end = found.index + found[0].length;
+ line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null;
+ }
+ return true
+ })
+ }),
+
+ addLineWidget: docMethodOp(function(handle, node, options) {
+ return addLineWidget(this, handle, node, options)
+ }),
+ removeLineWidget: function(widget) { widget.clear(); },
+
+ markText: function(from, to, options) {
+ return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range")
+ },
+ setBookmark: function(pos, options) {
+ var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options),
+ insertLeft: options && options.insertLeft,
+ clearWhenEmpty: false, shared: options && options.shared,
+ handleMouseEvents: options && options.handleMouseEvents};
+ pos = clipPos(this, pos);
+ return markText(this, pos, pos, realOpts, "bookmark")
+ },
+ findMarksAt: function(pos) {
+ pos = clipPos(this, pos);
+ var markers = [], spans = getLine(this, pos.line).markedSpans;
+ if (spans) { for (var i = 0; i < spans.length; ++i) {
+ var span = spans[i];
+ if ((span.from == null || span.from <= pos.ch) &&
+ (span.to == null || span.to >= pos.ch))
+ { markers.push(span.marker.parent || span.marker); }
+ } }
+ return markers
+ },
+ findMarks: function(from, to, filter) {
+ from = clipPos(this, from); to = clipPos(this, to);
+ var found = [], lineNo = from.line;
+ this.iter(from.line, to.line + 1, function (line) {
+ var spans = line.markedSpans;
+ if (spans) { for (var i = 0; i < spans.length; i++) {
+ var span = spans[i];
+ if (!(span.to != null && lineNo == from.line && from.ch >= span.to ||
+ span.from == null && lineNo != from.line ||
+ span.from != null && lineNo == to.line && span.from >= to.ch) &&
+ (!filter || filter(span.marker)))
+ { found.push(span.marker.parent || span.marker); }
+ } }
+ ++lineNo;
+ });
+ return found
+ },
+ getAllMarks: function() {
+ var markers = [];
+ this.iter(function (line) {
+ var sps = line.markedSpans;
+ if (sps) { for (var i = 0; i < sps.length; ++i)
+ { if (sps[i].from != null) { markers.push(sps[i].marker); } } }
+ });
+ return markers
+ },
+
+ posFromIndex: function(off) {
+ var ch, lineNo = this.first, sepSize = this.lineSeparator().length;
+ this.iter(function (line) {
+ var sz = line.text.length + sepSize;
+ if (sz > off) { ch = off; return true }
+ off -= sz;
+ ++lineNo;
+ });
+ return clipPos(this, Pos(lineNo, ch))
+ },
+ indexFromPos: function (coords) {
+ coords = clipPos(this, coords);
+ var index = coords.ch;
+ if (coords.line < this.first || coords.ch < 0) { return 0 }
+ var sepSize = this.lineSeparator().length;
+ this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value
+ index += line.text.length + sepSize;
+ });
+ return index
+ },
+
+ copy: function(copyHistory) {
+ var doc = new Doc(getLines(this, this.first, this.first + this.size),
+ this.modeOption, this.first, this.lineSep, this.direction);
+ doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft;
+ doc.sel = this.sel;
+ doc.extend = false;
+ if (copyHistory) {
+ doc.history.undoDepth = this.history.undoDepth;
+ doc.setHistory(this.getHistory());
+ }
+ return doc
+ },
+
+ linkedDoc: function(options) {
+ if (!options) { options = {}; }
+ var from = this.first, to = this.first + this.size;
+ if (options.from != null && options.from > from) { from = options.from; }
+ if (options.to != null && options.to < to) { to = options.to; }
+ var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction);
+ if (options.sharedHist) { copy.history = this.history
+ ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist});
+ copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}];
+ copySharedMarkers(copy, findSharedMarkers(this));
+ return copy
+ },
+ unlinkDoc: function(other) {
+ if (other instanceof CodeMirror) { other = other.doc; }
+ if (this.linked) { for (var i = 0; i < this.linked.length; ++i) {
+ var link = this.linked[i];
+ if (link.doc != other) { continue }
+ this.linked.splice(i, 1);
+ other.unlinkDoc(this);
+ detachSharedMarkers(findSharedMarkers(this));
+ break
+ } }
+ // If the histories were shared, split them again
+ if (other.history == this.history) {
+ var splitIds = [other.id];
+ linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true);
+ other.history = new History(null);
+ other.history.done = copyHistoryArray(this.history.done, splitIds);
+ other.history.undone = copyHistoryArray(this.history.undone, splitIds);
+ }
+ },
+ iterLinkedDocs: function(f) {linkedDocs(this, f);},
+
+ getMode: function() {return this.mode},
+ getEditor: function() {return this.cm},
+
+ splitLines: function(str) {
+ if (this.lineSep) { return str.split(this.lineSep) }
+ return splitLinesAuto(str)
+ },
+ lineSeparator: function() { return this.lineSep || "\n" },
+
+ setDirection: docMethodOp(function (dir) {
+ if (dir != "rtl") { dir = "ltr"; }
+ if (dir == this.direction) { return }
+ this.direction = dir;
+ this.iter(function (line) { return line.order = null; });
+ if (this.cm) { directionChanged(this.cm); }
+ })
+ });
+
+ // Public alias.
+ Doc.prototype.eachLine = Doc.prototype.iter;
+
+ // Kludge to work around strange IE behavior where it'll sometimes
+ // re-fire a series of drag-related events right after the drop (#1551)
+ var lastDrop = 0;
+
+ function onDrop(e) {
+ var cm = this;
+ clearDragCursor(cm);
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e))
+ { return }
+ e_preventDefault(e);
+ if (ie) { lastDrop = +new Date; }
+ var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files;
+ if (!pos || cm.isReadOnly()) { return }
+ // Might be a file drop, in which case we simply extract the text
+ // and insert it.
+ if (files && files.length && window.FileReader && window.File) {
+ var n = files.length, text = Array(n), read = 0;
+ var markAsReadAndPasteIfAllFilesAreRead = function () {
+ if (++read == n) {
+ operation(cm, function () {
+ pos = clipPos(cm.doc, pos);
+ var change = {from: pos, to: pos,
+ text: cm.doc.splitLines(
+ text.filter(function (t) { return t != null; }).join(cm.doc.lineSeparator())),
+ origin: "paste"};
+ makeChange(cm.doc, change);
+ setSelectionReplaceHistory(cm.doc, simpleSelection(clipPos(cm.doc, pos), clipPos(cm.doc, changeEnd(change))));
+ })();
+ }
+ };
+ var readTextFromFile = function (file, i) {
+ if (cm.options.allowDropFileTypes &&
+ indexOf(cm.options.allowDropFileTypes, file.type) == -1) {
+ markAsReadAndPasteIfAllFilesAreRead();
+ return
+ }
+ var reader = new FileReader;
+ reader.onerror = function () { return markAsReadAndPasteIfAllFilesAreRead(); };
+ reader.onload = function () {
+ var content = reader.result;
+ if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) {
+ markAsReadAndPasteIfAllFilesAreRead();
+ return
+ }
+ text[i] = content;
+ markAsReadAndPasteIfAllFilesAreRead();
+ };
+ reader.readAsText(file);
+ };
+ for (var i = 0; i < files.length; i++) { readTextFromFile(files[i], i); }
+ } else { // Normal drop
+ // Don't do a replace if the drop happened inside of the selected text.
+ if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) {
+ cm.state.draggingText(e);
+ // Ensure the editor is re-focused
+ setTimeout(function () { return cm.display.input.focus(); }, 20);
+ return
+ }
+ try {
+ var text$1 = e.dataTransfer.getData("Text");
+ if (text$1) {
+ var selected;
+ if (cm.state.draggingText && !cm.state.draggingText.copy)
+ { selected = cm.listSelections(); }
+ setSelectionNoUndo(cm.doc, simpleSelection(pos, pos));
+ if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1)
+ { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } }
+ cm.replaceSelection(text$1, "around", "paste");
+ cm.display.input.focus();
+ }
+ }
+ catch(e$1){}
+ }
+ }
+
+ function onDragStart(cm, e) {
+ if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return }
+ if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return }
+
+ e.dataTransfer.setData("Text", cm.getSelection());
+ e.dataTransfer.effectAllowed = "copyMove";
+
+ // Use dummy image instead of default browsers image.
+ // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there.
+ if (e.dataTransfer.setDragImage && !safari) {
+ var img = elt("img", null, null, "position: fixed; left: 0; top: 0;");
+ img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
+ if (presto) {
+ img.width = img.height = 1;
+ cm.display.wrapper.appendChild(img);
+ // Force a relayout, or Opera won't use our image for some obscure reason
+ img._top = img.offsetTop;
+ }
+ e.dataTransfer.setDragImage(img, 0, 0);
+ if (presto) { img.parentNode.removeChild(img); }
+ }
+ }
+
+ function onDragOver(cm, e) {
+ var pos = posFromMouse(cm, e);
+ if (!pos) { return }
+ var frag = document.createDocumentFragment();
+ drawSelectionCursor(cm, pos, frag);
+ if (!cm.display.dragCursor) {
+ cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors");
+ cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv);
+ }
+ removeChildrenAndAdd(cm.display.dragCursor, frag);
+ }
+
+ function clearDragCursor(cm) {
+ if (cm.display.dragCursor) {
+ cm.display.lineSpace.removeChild(cm.display.dragCursor);
+ cm.display.dragCursor = null;
+ }
+ }
+
+ // These must be handled carefully, because naively registering a
+ // handler for each editor will cause the editors to never be
+ // garbage collected.
+
+ function forEachCodeMirror(f) {
+ if (!document.getElementsByClassName) { return }
+ var byClass = document.getElementsByClassName("CodeMirror"), editors = [];
+ for (var i = 0; i < byClass.length; i++) {
+ var cm = byClass[i].CodeMirror;
+ if (cm) { editors.push(cm); }
+ }
+ if (editors.length) { editors[0].operation(function () {
+ for (var i = 0; i < editors.length; i++) { f(editors[i]); }
+ }); }
+ }
+
+ var globalsRegistered = false;
+ function ensureGlobalHandlers() {
+ if (globalsRegistered) { return }
+ registerGlobalHandlers();
+ globalsRegistered = true;
+ }
+ function registerGlobalHandlers() {
+ // When the window resizes, we need to refresh active editors.
+ var resizeTimer;
+ on(window, "resize", function () {
+ if (resizeTimer == null) { resizeTimer = setTimeout(function () {
+ resizeTimer = null;
+ forEachCodeMirror(onResize);
+ }, 100); }
+ });
+ // When the window loses focus, we want to show the editor as blurred
+ on(window, "blur", function () { return forEachCodeMirror(onBlur); });
+ }
+ // Called when the window resizes
+ function onResize(cm) {
+ var d = cm.display;
+ // Might be a text scaling operation, clear size caches.
+ d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null;
+ d.scrollbarsClipped = false;
+ cm.setSize();
+ }
+
+ var keyNames = {
+ 3: "Pause", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
+ 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
+ 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
+ 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod",
+ 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 145: "ScrollLock",
+ 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
+ 221: "]", 222: "'", 224: "Mod", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete",
+ 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert"
+ };
+
+ // Number keys
+ for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); }
+ // Alphabetic keys
+ for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); }
+ // Function keys
+ for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; }
+
+ var keyMap = {};
+
+ keyMap.basic = {
+ "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
+ "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
+ "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore",
+ "Tab": "defaultTab", "Shift-Tab": "indentAuto",
+ "Enter": "newlineAndIndent", "Insert": "toggleOverwrite",
+ "Esc": "singleSelection"
+ };
+ // Note that the save and find-related commands aren't defined by
+ // default. User code or addons can define them. Unknown commands
+ // are simply ignored.
+ keyMap.pcDefault = {
+ "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo",
+ "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown",
+ "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd",
+ "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find",
+ "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll",
+ "Ctrl-[": "indentLess", "Ctrl-]": "indentMore",
+ "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection",
+ "fallthrough": "basic"
+ };
+ // Very basic readline/emacs-style bindings, which are standard on Mac.
+ keyMap.emacsy = {
+ "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown",
+ "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd",
+ "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore",
+ "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars",
+ "Ctrl-O": "openLine"
+ };
+ keyMap.macDefault = {
+ "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo",
+ "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft",
+ "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore",
+ "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find",
+ "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll",
+ "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight",
+ "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd",
+ "fallthrough": ["basic", "emacsy"]
+ };
+ keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault;
+
+ // KEYMAP DISPATCH
+
+ function normalizeKeyName(name) {
+ var parts = name.split(/-(?!$)/);
+ name = parts[parts.length - 1];
+ var alt, ctrl, shift, cmd;
+ for (var i = 0; i < parts.length - 1; i++) {
+ var mod = parts[i];
+ if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; }
+ else if (/^a(lt)?$/i.test(mod)) { alt = true; }
+ else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; }
+ else if (/^s(hift)?$/i.test(mod)) { shift = true; }
+ else { throw new Error("Unrecognized modifier name: " + mod) }
+ }
+ if (alt) { name = "Alt-" + name; }
+ if (ctrl) { name = "Ctrl-" + name; }
+ if (cmd) { name = "Cmd-" + name; }
+ if (shift) { name = "Shift-" + name; }
+ return name
+ }
+
+ // This is a kludge to keep keymaps mostly working as raw objects
+ // (backwards compatibility) while at the same time support features
+ // like normalization and multi-stroke key bindings. It compiles a
+ // new normalized keymap, and then updates the old object to reflect
+ // this.
+ function normalizeKeyMap(keymap) {
+ var copy = {};
+ for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) {
+ var value = keymap[keyname];
+ if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue }
+ if (value == "...") { delete keymap[keyname]; continue }
+
+ var keys = map(keyname.split(" "), normalizeKeyName);
+ for (var i = 0; i < keys.length; i++) {
+ var val = (void 0), name = (void 0);
+ if (i == keys.length - 1) {
+ name = keys.join(" ");
+ val = value;
+ } else {
+ name = keys.slice(0, i + 1).join(" ");
+ val = "...";
+ }
+ var prev = copy[name];
+ if (!prev) { copy[name] = val; }
+ else if (prev != val) { throw new Error("Inconsistent bindings for " + name) }
+ }
+ delete keymap[keyname];
+ } }
+ for (var prop in copy) { keymap[prop] = copy[prop]; }
+ return keymap
+ }
+
+ function lookupKey(key, map, handle, context) {
+ map = getKeyMap(map);
+ var found = map.call ? map.call(key, context) : map[key];
+ if (found === false) { return "nothing" }
+ if (found === "...") { return "multi" }
+ if (found != null && handle(found)) { return "handled" }
+
+ if (map.fallthrough) {
+ if (Object.prototype.toString.call(map.fallthrough) != "[object Array]")
+ { return lookupKey(key, map.fallthrough, handle, context) }
+ for (var i = 0; i < map.fallthrough.length; i++) {
+ var result = lookupKey(key, map.fallthrough[i], handle, context);
+ if (result) { return result }
+ }
+ }
+ }
+
+ // Modifier key presses don't count as 'real' key presses for the
+ // purpose of keymap fallthrough.
+ function isModifierKey(value) {
+ var name = typeof value == "string" ? value : keyNames[value.keyCode];
+ return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"
+ }
+
+ function addModifierNames(name, event, noShift) {
+ var base = name;
+ if (event.altKey && base != "Alt") { name = "Alt-" + name; }
+ if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; }
+ if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Mod") { name = "Cmd-" + name; }
+ if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; }
+ return name
+ }
+
+ // Look up the name of a key as indicated by an event object.
+ function keyName(event, noShift) {
+ if (presto && event.keyCode == 34 && event["char"]) { return false }
+ var name = keyNames[event.keyCode];
+ if (name == null || event.altGraphKey) { return false }
+ // Ctrl-ScrollLock has keyCode 3, same as Ctrl-Pause,
+ // so we'll use event.code when available (Chrome 48+, FF 38+, Safari 10.1+)
+ if (event.keyCode == 3 && event.code) { name = event.code; }
+ return addModifierNames(name, event, noShift)
+ }
+
+ function getKeyMap(val) {
+ return typeof val == "string" ? keyMap[val] : val
+ }
+
+ // Helper for deleting text near the selection(s), used to implement
+ // backspace, delete, and similar functionality.
+ function deleteNearSelection(cm, compute) {
+ var ranges = cm.doc.sel.ranges, kill = [];
+ // Build up a set of ranges to kill first, merging overlapping
+ // ranges.
+ for (var i = 0; i < ranges.length; i++) {
+ var toKill = compute(ranges[i]);
+ while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) {
+ var replaced = kill.pop();
+ if (cmp(replaced.from, toKill.from) < 0) {
+ toKill.from = replaced.from;
+ break
+ }
+ }
+ kill.push(toKill);
+ }
+ // Next, remove those actual ranges.
+ runInOp(cm, function () {
+ for (var i = kill.length - 1; i >= 0; i--)
+ { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); }
+ ensureCursorVisible(cm);
+ });
+ }
+
+ function moveCharLogically(line, ch, dir) {
+ var target = skipExtendingChars(line.text, ch + dir, dir);
+ return target < 0 || target > line.text.length ? null : target
+ }
+
+ function moveLogically(line, start, dir) {
+ var ch = moveCharLogically(line, start.ch, dir);
+ return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before")
+ }
+
+ function endOfLine(visually, cm, lineObj, lineNo, dir) {
+ if (visually) {
+ if (cm.doc.direction == "rtl") { dir = -dir; }
+ var order = getOrder(lineObj, cm.doc.direction);
+ if (order) {
+ var part = dir < 0 ? lst(order) : order[0];
+ var moveInStorageOrder = (dir < 0) == (part.level == 1);
+ var sticky = moveInStorageOrder ? "after" : "before";
+ var ch;
+ // With a wrapped rtl chunk (possibly spanning multiple bidi parts),
+ // it could be that the last bidi part is not on the last visual line,
+ // since visual lines contain content order-consecutive chunks.
+ // Thus, in rtl, we are looking for the first (content-order) character
+ // in the rtl chunk that is on the last line (that is, the same line
+ // as the last (content-order) character).
+ if (part.level > 0 || cm.doc.direction == "rtl") {
+ var prep = prepareMeasureForLine(cm, lineObj);
+ ch = dir < 0 ? lineObj.text.length - 1 : 0;
+ var targetTop = measureCharPrepared(cm, prep, ch).top;
+ ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch);
+ if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); }
+ } else { ch = dir < 0 ? part.to : part.from; }
+ return new Pos(lineNo, ch, sticky)
+ }
+ }
+ return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after")
+ }
+
+ function moveVisually(cm, line, start, dir) {
+ var bidi = getOrder(line, cm.doc.direction);
+ if (!bidi) { return moveLogically(line, start, dir) }
+ if (start.ch >= line.text.length) {
+ start.ch = line.text.length;
+ start.sticky = "before";
+ } else if (start.ch <= 0) {
+ start.ch = 0;
+ start.sticky = "after";
+ }
+ var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos];
+ if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) {
+ // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines,
+ // nothing interesting happens.
+ return moveLogically(line, start, dir)
+ }
+
+ var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); };
+ var prep;
+ var getWrappedLineExtent = function (ch) {
+ if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} }
+ prep = prep || prepareMeasureForLine(cm, line);
+ return wrappedLineExtentChar(cm, line, prep, ch)
+ };
+ var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch);
+
+ if (cm.doc.direction == "rtl" || part.level == 1) {
+ var moveInStorageOrder = (part.level == 1) == (dir < 0);
+ var ch = mv(start, moveInStorageOrder ? 1 : -1);
+ if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) {
+ // Case 2: We move within an rtl part or in an rtl editor on the same visual line
+ var sticky = moveInStorageOrder ? "before" : "after";
+ return new Pos(start.line, ch, sticky)
+ }
+ }
+
+ // Case 3: Could not move within this bidi part in this visual line, so leave
+ // the current bidi part
+
+ var searchInVisualLine = function (partPos, dir, wrappedLineExtent) {
+ var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder
+ ? new Pos(start.line, mv(ch, 1), "before")
+ : new Pos(start.line, ch, "after"); };
+
+ for (; partPos >= 0 && partPos < bidi.length; partPos += dir) {
+ var part = bidi[partPos];
+ var moveInStorageOrder = (dir > 0) == (part.level != 1);
+ var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1);
+ if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) }
+ ch = moveInStorageOrder ? part.from : mv(part.to, -1);
+ if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) }
+ }
+ };
+
+ // Case 3a: Look for other bidi parts on the same visual line
+ var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent);
+ if (res) { return res }
+
+ // Case 3b: Look for other bidi parts on the next visual line
+ var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1);
+ if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) {
+ res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh));
+ if (res) { return res }
+ }
+
+ // Case 4: Nowhere to move
+ return null
+ }
+
+ // Commands are parameter-less actions that can be performed on an
+ // editor, mostly used for keybindings.
+ var commands = {
+ selectAll: selectAll,
+ singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); },
+ killLine: function (cm) { return deleteNearSelection(cm, function (range) {
+ if (range.empty()) {
+ var len = getLine(cm.doc, range.head.line).text.length;
+ if (range.head.ch == len && range.head.line < cm.lastLine())
+ { return {from: range.head, to: Pos(range.head.line + 1, 0)} }
+ else
+ { return {from: range.head, to: Pos(range.head.line, len)} }
+ } else {
+ return {from: range.from(), to: range.to()}
+ }
+ }); },
+ deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({
+ from: Pos(range.from().line, 0),
+ to: clipPos(cm.doc, Pos(range.to().line + 1, 0))
+ }); }); },
+ delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({
+ from: Pos(range.from().line, 0), to: range.from()
+ }); }); },
+ delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ var leftPos = cm.coordsChar({left: 0, top: top}, "div");
+ return {from: leftPos, to: range.from()}
+ }); },
+ delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) {
+ var top = cm.charCoords(range.head, "div").top + 5;
+ var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div");
+ return {from: range.from(), to: rightPos }
+ }); },
+ undo: function (cm) { return cm.undo(); },
+ redo: function (cm) { return cm.redo(); },
+ undoSelection: function (cm) { return cm.undoSelection(); },
+ redoSelection: function (cm) { return cm.redoSelection(); },
+ goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); },
+ goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); },
+ goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); },
+ {origin: "+move", bias: 1}
+ ); },
+ goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); },
+ {origin: "+move", bias: 1}
+ ); },
+ goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); },
+ {origin: "+move", bias: -1}
+ ); },
+ goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) {
+ var top = cm.cursorCoords(range.head, "div").top + 5;
+ return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div")
+ }, sel_move); },
+ goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) {
+ var top = cm.cursorCoords(range.head, "div").top + 5;
+ return cm.coordsChar({left: 0, top: top}, "div")
+ }, sel_move); },
+ goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) {
+ var top = cm.cursorCoords(range.head, "div").top + 5;
+ var pos = cm.coordsChar({left: 0, top: top}, "div");
+ if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) }
+ return pos
+ }, sel_move); },
+ goLineUp: function (cm) { return cm.moveV(-1, "line"); },
+ goLineDown: function (cm) { return cm.moveV(1, "line"); },
+ goPageUp: function (cm) { return cm.moveV(-1, "page"); },
+ goPageDown: function (cm) { return cm.moveV(1, "page"); },
+ goCharLeft: function (cm) { return cm.moveH(-1, "char"); },
+ goCharRight: function (cm) { return cm.moveH(1, "char"); },
+ goColumnLeft: function (cm) { return cm.moveH(-1, "column"); },
+ goColumnRight: function (cm) { return cm.moveH(1, "column"); },
+ goWordLeft: function (cm) { return cm.moveH(-1, "word"); },
+ goGroupRight: function (cm) { return cm.moveH(1, "group"); },
+ goGroupLeft: function (cm) { return cm.moveH(-1, "group"); },
+ goWordRight: function (cm) { return cm.moveH(1, "word"); },
+ delCharBefore: function (cm) { return cm.deleteH(-1, "codepoint"); },
+ delCharAfter: function (cm) { return cm.deleteH(1, "char"); },
+ delWordBefore: function (cm) { return cm.deleteH(-1, "word"); },
+ delWordAfter: function (cm) { return cm.deleteH(1, "word"); },
+ delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); },
+ delGroupAfter: function (cm) { return cm.deleteH(1, "group"); },
+ indentAuto: function (cm) { return cm.indentSelection("smart"); },
+ indentMore: function (cm) { return cm.indentSelection("add"); },
+ indentLess: function (cm) { return cm.indentSelection("subtract"); },
+ insertTab: function (cm) { return cm.replaceSelection("\t"); },
+ insertSoftTab: function (cm) {
+ var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize;
+ for (var i = 0; i < ranges.length; i++) {
+ var pos = ranges[i].from();
+ var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize);
+ spaces.push(spaceStr(tabSize - col % tabSize));
+ }
+ cm.replaceSelections(spaces);
+ },
+ defaultTab: function (cm) {
+ if (cm.somethingSelected()) { cm.indentSelection("add"); }
+ else { cm.execCommand("insertTab"); }
+ },
+ // Swap the two chars left and right of each selection's head.
+ // Move cursor behind the two swapped characters afterwards.
+ //
+ // Doesn't consider line feeds a character.
+ // Doesn't scan more than one line above to find a character.
+ // Doesn't do anything on an empty line.
+ // Doesn't do anything with non-empty selections.
+ transposeChars: function (cm) { return runInOp(cm, function () {
+ var ranges = cm.listSelections(), newSel = [];
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) { continue }
+ var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text;
+ if (line) {
+ if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); }
+ if (cur.ch > 0) {
+ cur = new Pos(cur.line, cur.ch + 1);
+ cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2),
+ Pos(cur.line, cur.ch - 2), cur, "+transpose");
+ } else if (cur.line > cm.doc.first) {
+ var prev = getLine(cm.doc, cur.line - 1).text;
+ if (prev) {
+ cur = new Pos(cur.line, 1);
+ cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() +
+ prev.charAt(prev.length - 1),
+ Pos(cur.line - 1, prev.length - 1), cur, "+transpose");
+ }
+ }
+ }
+ newSel.push(new Range(cur, cur));
+ }
+ cm.setSelections(newSel);
+ }); },
+ newlineAndIndent: function (cm) { return runInOp(cm, function () {
+ var sels = cm.listSelections();
+ for (var i = sels.length - 1; i >= 0; i--)
+ { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); }
+ sels = cm.listSelections();
+ for (var i$1 = 0; i$1 < sels.length; i$1++)
+ { cm.indentLine(sels[i$1].from().line, null, true); }
+ ensureCursorVisible(cm);
+ }); },
+ openLine: function (cm) { return cm.replaceSelection("\n", "start"); },
+ toggleOverwrite: function (cm) { return cm.toggleOverwrite(); }
+ };
+
+
+ function lineStart(cm, lineN) {
+ var line = getLine(cm.doc, lineN);
+ var visual = visualLine(line);
+ if (visual != line) { lineN = lineNo(visual); }
+ return endOfLine(true, cm, visual, lineN, 1)
+ }
+ function lineEnd(cm, lineN) {
+ var line = getLine(cm.doc, lineN);
+ var visual = visualLineEnd(line);
+ if (visual != line) { lineN = lineNo(visual); }
+ return endOfLine(true, cm, line, lineN, -1)
+ }
+ function lineStartSmart(cm, pos) {
+ var start = lineStart(cm, pos.line);
+ var line = getLine(cm.doc, start.line);
+ var order = getOrder(line, cm.doc.direction);
+ if (!order || order[0].level == 0) {
+ var firstNonWS = Math.max(start.ch, line.text.search(/\S/));
+ var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch;
+ return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky)
+ }
+ return start
+ }
+
+ // Run a handler that was bound to a key.
+ function doHandleBinding(cm, bound, dropShift) {
+ if (typeof bound == "string") {
+ bound = commands[bound];
+ if (!bound) { return false }
+ }
+ // Ensure previous input has been read, so that the handler sees a
+ // consistent view of the document
+ cm.display.input.ensurePolled();
+ var prevShift = cm.display.shift, done = false;
+ try {
+ if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
+ if (dropShift) { cm.display.shift = false; }
+ done = bound(cm) != Pass;
+ } finally {
+ cm.display.shift = prevShift;
+ cm.state.suppressEdits = false;
+ }
+ return done
+ }
+
+ function lookupKeyForEditor(cm, name, handle) {
+ for (var i = 0; i < cm.state.keyMaps.length; i++) {
+ var result = lookupKey(name, cm.state.keyMaps[i], handle, cm);
+ if (result) { return result }
+ }
+ return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm))
+ || lookupKey(name, cm.options.keyMap, handle, cm)
+ }
+
+ // Note that, despite the name, this function is also used to check
+ // for bound mouse clicks.
+
+ var stopSeq = new Delayed;
+
+ function dispatchKey(cm, name, e, handle) {
+ var seq = cm.state.keySeq;
+ if (seq) {
+ if (isModifierKey(name)) { return "handled" }
+ if (/\'$/.test(name))
+ { cm.state.keySeq = null; }
+ else
+ { stopSeq.set(50, function () {
+ if (cm.state.keySeq == seq) {
+ cm.state.keySeq = null;
+ cm.display.input.reset();
+ }
+ }); }
+ if (dispatchKeyInner(cm, seq + " " + name, e, handle)) { return true }
+ }
+ return dispatchKeyInner(cm, name, e, handle)
+ }
+
+ function dispatchKeyInner(cm, name, e, handle) {
+ var result = lookupKeyForEditor(cm, name, handle);
+
+ if (result == "multi")
+ { cm.state.keySeq = name; }
+ if (result == "handled")
+ { signalLater(cm, "keyHandled", cm, name, e); }
+
+ if (result == "handled" || result == "multi") {
+ e_preventDefault(e);
+ restartBlink(cm);
+ }
+
+ return !!result
+ }
+
+ // Handle a key from the keydown event.
+ function handleKeyBinding(cm, e) {
+ var name = keyName(e, true);
+ if (!name) { return false }
+
+ if (e.shiftKey && !cm.state.keySeq) {
+ // First try to resolve full name (including 'Shift-'). Failing
+ // that, see if there is a cursor-motion command (starting with
+ // 'go') bound to the keyname without 'Shift-'.
+ return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); })
+ || dispatchKey(cm, name, e, function (b) {
+ if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion)
+ { return doHandleBinding(cm, b) }
+ })
+ } else {
+ return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); })
+ }
+ }
+
+ // Handle a key from the keypress event
+ function handleCharBinding(cm, e, ch) {
+ return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); })
+ }
+
+ var lastStoppedKey = null;
+ function onKeyDown(e) {
+ var cm = this;
+ if (e.target && e.target != cm.display.input.getField()) { return }
+ cm.curOp.focus = activeElt();
+ if (signalDOMEvent(cm, e)) { return }
+ // IE does strange things with escape.
+ if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; }
+ var code = e.keyCode;
+ cm.display.shift = code == 16 || e.shiftKey;
+ var handled = handleKeyBinding(cm, e);
+ if (presto) {
+ lastStoppedKey = handled ? code : null;
+ // Opera has no cut event... we try to at least catch the key combo
+ if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey))
+ { cm.replaceSelection("", null, "cut"); }
+ }
+ if (gecko && !mac && !handled && code == 46 && e.shiftKey && !e.ctrlKey && document.execCommand)
+ { document.execCommand("cut"); }
+
+ // Turn mouse into crosshair when Alt is held on Mac.
+ if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className))
+ { showCrossHair(cm); }
+ }
+
+ function showCrossHair(cm) {
+ var lineDiv = cm.display.lineDiv;
+ addClass(lineDiv, "CodeMirror-crosshair");
+
+ function up(e) {
+ if (e.keyCode == 18 || !e.altKey) {
+ rmClass(lineDiv, "CodeMirror-crosshair");
+ off(document, "keyup", up);
+ off(document, "mouseover", up);
+ }
+ }
+ on(document, "keyup", up);
+ on(document, "mouseover", up);
+ }
+
+ function onKeyUp(e) {
+ if (e.keyCode == 16) { this.doc.sel.shift = false; }
+ signalDOMEvent(this, e);
+ }
+
+ function onKeyPress(e) {
+ var cm = this;
+ if (e.target && e.target != cm.display.input.getField()) { return }
+ if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return }
+ var keyCode = e.keyCode, charCode = e.charCode;
+ if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return}
+ if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return }
+ var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
+ // Some browsers fire keypress events for backspace
+ if (ch == "\x08") { return }
+ if (handleCharBinding(cm, e, ch)) { return }
+ cm.display.input.onKeyPress(e);
+ }
+
+ var DOUBLECLICK_DELAY = 400;
+
+ var PastClick = function(time, pos, button) {
+ this.time = time;
+ this.pos = pos;
+ this.button = button;
+ };
+
+ PastClick.prototype.compare = function (time, pos, button) {
+ return this.time + DOUBLECLICK_DELAY > time &&
+ cmp(pos, this.pos) == 0 && button == this.button
+ };
+
+ var lastClick, lastDoubleClick;
+ function clickRepeat(pos, button) {
+ var now = +new Date;
+ if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) {
+ lastClick = lastDoubleClick = null;
+ return "triple"
+ } else if (lastClick && lastClick.compare(now, pos, button)) {
+ lastDoubleClick = new PastClick(now, pos, button);
+ lastClick = null;
+ return "double"
+ } else {
+ lastClick = new PastClick(now, pos, button);
+ lastDoubleClick = null;
+ return "single"
+ }
+ }
+
+ // A mouse down can be a single click, double click, triple click,
+ // start of selection drag, start of text drag, new cursor
+ // (ctrl-click), rectangle drag (alt-drag), or xwin
+ // middle-click-paste. Or it might be a click on something we should
+ // not interfere with, such as a scrollbar or widget.
+ function onMouseDown(e) {
+ var cm = this, display = cm.display;
+ if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return }
+ display.input.ensurePolled();
+ display.shift = e.shiftKey;
+
+ if (eventInWidget(display, e)) {
+ if (!webkit) {
+ // Briefly turn off draggability, to allow widgets to do
+ // normal dragging things.
+ display.scroller.draggable = false;
+ setTimeout(function () { return display.scroller.draggable = true; }, 100);
+ }
+ return
+ }
+ if (clickInGutter(cm, e)) { return }
+ var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single";
+ window.focus();
+
+ // #3261: make sure, that we're not starting a second selection
+ if (button == 1 && cm.state.selectingText)
+ { cm.state.selectingText(e); }
+
+ if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return }
+
+ if (button == 1) {
+ if (pos) { leftButtonDown(cm, pos, repeat, e); }
+ else if (e_target(e) == display.scroller) { e_preventDefault(e); }
+ } else if (button == 2) {
+ if (pos) { extendSelection(cm.doc, pos); }
+ setTimeout(function () { return display.input.focus(); }, 20);
+ } else if (button == 3) {
+ if (captureRightClick) { cm.display.input.onContextMenu(e); }
+ else { delayBlurEvent(cm); }
+ }
+ }
+
+ function handleMappedButton(cm, button, pos, repeat, event) {
+ var name = "Click";
+ if (repeat == "double") { name = "Double" + name; }
+ else if (repeat == "triple") { name = "Triple" + name; }
+ name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name;
+
+ return dispatchKey(cm, addModifierNames(name, event), event, function (bound) {
+ if (typeof bound == "string") { bound = commands[bound]; }
+ if (!bound) { return false }
+ var done = false;
+ try {
+ if (cm.isReadOnly()) { cm.state.suppressEdits = true; }
+ done = bound(cm, pos) != Pass;
+ } finally {
+ cm.state.suppressEdits = false;
+ }
+ return done
+ })
+ }
+
+ function configureMouse(cm, repeat, event) {
+ var option = cm.getOption("configureMouse");
+ var value = option ? option(cm, repeat, event) : {};
+ if (value.unit == null) {
+ var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey;
+ value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line";
+ }
+ if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; }
+ if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; }
+ if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); }
+ return value
+ }
+
+ function leftButtonDown(cm, pos, repeat, event) {
+ if (ie) { setTimeout(bind(ensureFocus, cm), 0); }
+ else { cm.curOp.focus = activeElt(); }
+
+ var behavior = configureMouse(cm, repeat, event);
+
+ var sel = cm.doc.sel, contained;
+ if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() &&
+ repeat == "single" && (contained = sel.contains(pos)) > -1 &&
+ (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) &&
+ (cmp(contained.to(), pos) > 0 || pos.xRel < 0))
+ { leftButtonStartDrag(cm, event, pos, behavior); }
+ else
+ { leftButtonSelect(cm, event, pos, behavior); }
+ }
+
+ // Start a text drag. When it ends, see if any dragging actually
+ // happen, and treat as a click if it didn't.
+ function leftButtonStartDrag(cm, event, pos, behavior) {
+ var display = cm.display, moved = false;
+ var dragEnd = operation(cm, function (e) {
+ if (webkit) { display.scroller.draggable = false; }
+ cm.state.draggingText = false;
+ off(display.wrapper.ownerDocument, "mouseup", dragEnd);
+ off(display.wrapper.ownerDocument, "mousemove", mouseMove);
+ off(display.scroller, "dragstart", dragStart);
+ off(display.scroller, "drop", dragEnd);
+ if (!moved) {
+ e_preventDefault(e);
+ if (!behavior.addNew)
+ { extendSelection(cm.doc, pos, null, null, behavior.extend); }
+ // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081)
+ if ((webkit && !safari) || ie && ie_version == 9)
+ { setTimeout(function () {display.wrapper.ownerDocument.body.focus({preventScroll: true}); display.input.focus();}, 20); }
+ else
+ { display.input.focus(); }
+ }
+ });
+ var mouseMove = function(e2) {
+ moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10;
+ };
+ var dragStart = function () { return moved = true; };
+ // Let the drag handler handle this.
+ if (webkit) { display.scroller.draggable = true; }
+ cm.state.draggingText = dragEnd;
+ dragEnd.copy = !behavior.moveOnDrag;
+ // IE's approach to draggable
+ if (display.scroller.dragDrop) { display.scroller.dragDrop(); }
+ on(display.wrapper.ownerDocument, "mouseup", dragEnd);
+ on(display.wrapper.ownerDocument, "mousemove", mouseMove);
+ on(display.scroller, "dragstart", dragStart);
+ on(display.scroller, "drop", dragEnd);
+
+ delayBlurEvent(cm);
+ setTimeout(function () { return display.input.focus(); }, 20);
+ }
+
+ function rangeForUnit(cm, pos, unit) {
+ if (unit == "char") { return new Range(pos, pos) }
+ if (unit == "word") { return cm.findWordAt(pos) }
+ if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) }
+ var result = unit(cm, pos);
+ return new Range(result.from, result.to)
+ }
+
+ // Normal selection, as opposed to text dragging.
+ function leftButtonSelect(cm, event, start, behavior) {
+ var display = cm.display, doc = cm.doc;
+ e_preventDefault(event);
+
+ var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges;
+ if (behavior.addNew && !behavior.extend) {
+ ourIndex = doc.sel.contains(start);
+ if (ourIndex > -1)
+ { ourRange = ranges[ourIndex]; }
+ else
+ { ourRange = new Range(start, start); }
+ } else {
+ ourRange = doc.sel.primary();
+ ourIndex = doc.sel.primIndex;
+ }
+
+ if (behavior.unit == "rectangle") {
+ if (!behavior.addNew) { ourRange = new Range(start, start); }
+ start = posFromMouse(cm, event, true, true);
+ ourIndex = -1;
+ } else {
+ var range = rangeForUnit(cm, start, behavior.unit);
+ if (behavior.extend)
+ { ourRange = extendRange(ourRange, range.anchor, range.head, behavior.extend); }
+ else
+ { ourRange = range; }
+ }
+
+ if (!behavior.addNew) {
+ ourIndex = 0;
+ setSelection(doc, new Selection([ourRange], 0), sel_mouse);
+ startSel = doc.sel;
+ } else if (ourIndex == -1) {
+ ourIndex = ranges.length;
+ setSelection(doc, normalizeSelection(cm, ranges.concat([ourRange]), ourIndex),
+ {scroll: false, origin: "*mouse"});
+ } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) {
+ setSelection(doc, normalizeSelection(cm, ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0),
+ {scroll: false, origin: "*mouse"});
+ startSel = doc.sel;
+ } else {
+ replaceOneSelection(doc, ourIndex, ourRange, sel_mouse);
+ }
+
+ var lastPos = start;
+ function extendTo(pos) {
+ if (cmp(lastPos, pos) == 0) { return }
+ lastPos = pos;
+
+ if (behavior.unit == "rectangle") {
+ var ranges = [], tabSize = cm.options.tabSize;
+ var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize);
+ var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize);
+ var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol);
+ for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line));
+ line <= end; line++) {
+ var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize);
+ if (left == right)
+ { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); }
+ else if (text.length > leftPos)
+ { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); }
+ }
+ if (!ranges.length) { ranges.push(new Range(start, start)); }
+ setSelection(doc, normalizeSelection(cm, startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex),
+ {origin: "*mouse", scroll: false});
+ cm.scrollIntoView(pos);
+ } else {
+ var oldRange = ourRange;
+ var range = rangeForUnit(cm, pos, behavior.unit);
+ var anchor = oldRange.anchor, head;
+ if (cmp(range.anchor, anchor) > 0) {
+ head = range.head;
+ anchor = minPos(oldRange.from(), range.anchor);
+ } else {
+ head = range.anchor;
+ anchor = maxPos(oldRange.to(), range.head);
+ }
+ var ranges$1 = startSel.ranges.slice(0);
+ ranges$1[ourIndex] = bidiSimplify(cm, new Range(clipPos(doc, anchor), head));
+ setSelection(doc, normalizeSelection(cm, ranges$1, ourIndex), sel_mouse);
+ }
+ }
+
+ var editorSize = display.wrapper.getBoundingClientRect();
+ // Used to ensure timeout re-tries don't fire when another extend
+ // happened in the meantime (clearTimeout isn't reliable -- at
+ // least on Chrome, the timeouts still happen even when cleared,
+ // if the clear happens after their scheduled firing time).
+ var counter = 0;
+
+ function extend(e) {
+ var curCount = ++counter;
+ var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle");
+ if (!cur) { return }
+ if (cmp(cur, lastPos) != 0) {
+ cm.curOp.focus = activeElt();
+ extendTo(cur);
+ var visible = visibleLines(display, doc);
+ if (cur.line >= visible.to || cur.line < visible.from)
+ { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); }
+ } else {
+ var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0;
+ if (outside) { setTimeout(operation(cm, function () {
+ if (counter != curCount) { return }
+ display.scroller.scrollTop += outside;
+ extend(e);
+ }), 50); }
+ }
+ }
+
+ function done(e) {
+ cm.state.selectingText = false;
+ counter = Infinity;
+ // If e is null or undefined we interpret this as someone trying
+ // to explicitly cancel the selection rather than the user
+ // letting go of the mouse button.
+ if (e) {
+ e_preventDefault(e);
+ display.input.focus();
+ }
+ off(display.wrapper.ownerDocument, "mousemove", move);
+ off(display.wrapper.ownerDocument, "mouseup", up);
+ doc.history.lastSelOrigin = null;
+ }
+
+ var move = operation(cm, function (e) {
+ if (e.buttons === 0 || !e_button(e)) { done(e); }
+ else { extend(e); }
+ });
+ var up = operation(cm, done);
+ cm.state.selectingText = up;
+ on(display.wrapper.ownerDocument, "mousemove", move);
+ on(display.wrapper.ownerDocument, "mouseup", up);
+ }
+
+ // Used when mouse-selecting to adjust the anchor to the proper side
+ // of a bidi jump depending on the visual position of the head.
+ function bidiSimplify(cm, range) {
+ var anchor = range.anchor;
+ var head = range.head;
+ var anchorLine = getLine(cm.doc, anchor.line);
+ if (cmp(anchor, head) == 0 && anchor.sticky == head.sticky) { return range }
+ var order = getOrder(anchorLine);
+ if (!order) { return range }
+ var index = getBidiPartAt(order, anchor.ch, anchor.sticky), part = order[index];
+ if (part.from != anchor.ch && part.to != anchor.ch) { return range }
+ var boundary = index + ((part.from == anchor.ch) == (part.level != 1) ? 0 : 1);
+ if (boundary == 0 || boundary == order.length) { return range }
+
+ // Compute the relative visual position of the head compared to the
+ // anchor (<0 is to the left, >0 to the right)
+ var leftSide;
+ if (head.line != anchor.line) {
+ leftSide = (head.line - anchor.line) * (cm.doc.direction == "ltr" ? 1 : -1) > 0;
+ } else {
+ var headIndex = getBidiPartAt(order, head.ch, head.sticky);
+ var dir = headIndex - index || (head.ch - anchor.ch) * (part.level == 1 ? -1 : 1);
+ if (headIndex == boundary - 1 || headIndex == boundary)
+ { leftSide = dir < 0; }
+ else
+ { leftSide = dir > 0; }
+ }
+
+ var usePart = order[boundary + (leftSide ? -1 : 0)];
+ var from = leftSide == (usePart.level == 1);
+ var ch = from ? usePart.from : usePart.to, sticky = from ? "after" : "before";
+ return anchor.ch == ch && anchor.sticky == sticky ? range : new Range(new Pos(anchor.line, ch, sticky), head)
+ }
+
+
+ // Determines whether an event happened in the gutter, and fires the
+ // handlers for the corresponding event.
+ function gutterEvent(cm, e, type, prevent) {
+ var mX, mY;
+ if (e.touches) {
+ mX = e.touches[0].clientX;
+ mY = e.touches[0].clientY;
+ } else {
+ try { mX = e.clientX; mY = e.clientY; }
+ catch(e$1) { return false }
+ }
+ if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false }
+ if (prevent) { e_preventDefault(e); }
+
+ var display = cm.display;
+ var lineBox = display.lineDiv.getBoundingClientRect();
+
+ if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) }
+ mY -= lineBox.top - display.viewOffset;
+
+ for (var i = 0; i < cm.display.gutterSpecs.length; ++i) {
+ var g = display.gutters.childNodes[i];
+ if (g && g.getBoundingClientRect().right >= mX) {
+ var line = lineAtHeight(cm.doc, mY);
+ var gutter = cm.display.gutterSpecs[i];
+ signal(cm, type, cm, line, gutter.className, e);
+ return e_defaultPrevented(e)
+ }
+ }
+ }
+
+ function clickInGutter(cm, e) {
+ return gutterEvent(cm, e, "gutterClick", true)
+ }
+
+ // CONTEXT MENU HANDLING
+
+ // To make the context menu work, we need to briefly unhide the
+ // textarea (making it as unobtrusive as possible) to let the
+ // right-click take effect on it.
+ function onContextMenu(cm, e) {
+ if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return }
+ if (signalDOMEvent(cm, e, "contextmenu")) { return }
+ if (!captureRightClick) { cm.display.input.onContextMenu(e); }
+ }
+
+ function contextMenuInGutter(cm, e) {
+ if (!hasHandler(cm, "gutterContextMenu")) { return false }
+ return gutterEvent(cm, e, "gutterContextMenu", false)
+ }
+
+ function themeChanged(cm) {
+ cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") +
+ cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-");
+ clearCaches(cm);
+ }
+
+ var Init = {toString: function(){return "CodeMirror.Init"}};
+
+ var defaults = {};
+ var optionHandlers = {};
+
+ function defineOptions(CodeMirror) {
+ var optionHandlers = CodeMirror.optionHandlers;
+
+ function option(name, deflt, handle, notOnInit) {
+ CodeMirror.defaults[name] = deflt;
+ if (handle) { optionHandlers[name] =
+ notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; }
+ }
+
+ CodeMirror.defineOption = option;
+
+ // Passed to option handlers when there is no old value.
+ CodeMirror.Init = Init;
+
+ // These two are, on init, called from the constructor because they
+ // have to be initialized before the editor can start at all.
+ option("value", "", function (cm, val) { return cm.setValue(val); }, true);
+ option("mode", null, function (cm, val) {
+ cm.doc.modeOption = val;
+ loadMode(cm);
+ }, true);
+
+ option("indentUnit", 2, loadMode, true);
+ option("indentWithTabs", false);
+ option("smartIndent", true);
+ option("tabSize", 4, function (cm) {
+ resetModeState(cm);
+ clearCaches(cm);
+ regChange(cm);
+ }, true);
+
+ option("lineSeparator", null, function (cm, val) {
+ cm.doc.lineSep = val;
+ if (!val) { return }
+ var newBreaks = [], lineNo = cm.doc.first;
+ cm.doc.iter(function (line) {
+ for (var pos = 0;;) {
+ var found = line.text.indexOf(val, pos);
+ if (found == -1) { break }
+ pos = found + val.length;
+ newBreaks.push(Pos(lineNo, found));
+ }
+ lineNo++;
+ });
+ for (var i = newBreaks.length - 1; i >= 0; i--)
+ { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); }
+ });
+ option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200c\u200e\u200f\u2028\u2029\ufeff\ufff9-\ufffc]/g, function (cm, val, old) {
+ cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g");
+ if (old != Init) { cm.refresh(); }
+ });
+ option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true);
+ option("electricChars", true);
+ option("inputStyle", mobile ? "contenteditable" : "textarea", function () {
+ throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME
+ }, true);
+ option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true);
+ option("autocorrect", false, function (cm, val) { return cm.getInputField().autocorrect = val; }, true);
+ option("autocapitalize", false, function (cm, val) { return cm.getInputField().autocapitalize = val; }, true);
+ option("rtlMoveVisually", !windows);
+ option("wholeLineUpdateBefore", true);
+
+ option("theme", "default", function (cm) {
+ themeChanged(cm);
+ updateGutters(cm);
+ }, true);
+ option("keyMap", "default", function (cm, val, old) {
+ var next = getKeyMap(val);
+ var prev = old != Init && getKeyMap(old);
+ if (prev && prev.detach) { prev.detach(cm, next); }
+ if (next.attach) { next.attach(cm, prev || null); }
+ });
+ option("extraKeys", null);
+ option("configureMouse", null);
+
+ option("lineWrapping", false, wrappingChanged, true);
+ option("gutters", [], function (cm, val) {
+ cm.display.gutterSpecs = getGutters(val, cm.options.lineNumbers);
+ updateGutters(cm);
+ }, true);
+ option("fixedGutter", true, function (cm, val) {
+ cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0";
+ cm.refresh();
+ }, true);
+ option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true);
+ option("scrollbarStyle", "native", function (cm) {
+ initScrollbars(cm);
+ updateScrollbars(cm);
+ cm.display.scrollbars.setScrollTop(cm.doc.scrollTop);
+ cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft);
+ }, true);
+ option("lineNumbers", false, function (cm, val) {
+ cm.display.gutterSpecs = getGutters(cm.options.gutters, val);
+ updateGutters(cm);
+ }, true);
+ option("firstLineNumber", 1, updateGutters, true);
+ option("lineNumberFormatter", function (integer) { return integer; }, updateGutters, true);
+ option("showCursorWhenSelecting", false, updateSelection, true);
+
+ option("resetSelectionOnContextMenu", true);
+ option("lineWiseCopyCut", true);
+ option("pasteLinesPerSelection", true);
+ option("selectionsMayTouch", false);
+
+ option("readOnly", false, function (cm, val) {
+ if (val == "nocursor") {
+ onBlur(cm);
+ cm.display.input.blur();
+ }
+ cm.display.input.readOnlyChanged(val);
+ });
+
+ option("screenReaderLabel", null, function (cm, val) {
+ val = (val === '') ? null : val;
+ cm.display.input.screenReaderLabelChanged(val);
+ });
+
+ option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true);
+ option("dragDrop", true, dragDropChanged);
+ option("allowDropFileTypes", null);
+
+ option("cursorBlinkRate", 530);
+ option("cursorScrollMargin", 0);
+ option("cursorHeight", 1, updateSelection, true);
+ option("singleCursorHeightPerLine", true, updateSelection, true);
+ option("workTime", 100);
+ option("workDelay", 100);
+ option("flattenSpans", true, resetModeState, true);
+ option("addModeClass", false, resetModeState, true);
+ option("pollInterval", 100);
+ option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; });
+ option("historyEventDelay", 1250);
+ option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true);
+ option("maxHighlightLength", 10000, resetModeState, true);
+ option("moveInputWithCursor", true, function (cm, val) {
+ if (!val) { cm.display.input.resetPosition(); }
+ });
+
+ option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; });
+ option("autofocus", null);
+ option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true);
+ option("phrases", null);
+ }
+
+ function dragDropChanged(cm, value, old) {
+ var wasOn = old && old != Init;
+ if (!value != !wasOn) {
+ var funcs = cm.display.dragFunctions;
+ var toggle = value ? on : off;
+ toggle(cm.display.scroller, "dragstart", funcs.start);
+ toggle(cm.display.scroller, "dragenter", funcs.enter);
+ toggle(cm.display.scroller, "dragover", funcs.over);
+ toggle(cm.display.scroller, "dragleave", funcs.leave);
+ toggle(cm.display.scroller, "drop", funcs.drop);
+ }
+ }
+
+ function wrappingChanged(cm) {
+ if (cm.options.lineWrapping) {
+ addClass(cm.display.wrapper, "CodeMirror-wrap");
+ cm.display.sizer.style.minWidth = "";
+ cm.display.sizerWidth = null;
+ } else {
+ rmClass(cm.display.wrapper, "CodeMirror-wrap");
+ findMaxLine(cm);
+ }
+ estimateLineHeights(cm);
+ regChange(cm);
+ clearCaches(cm);
+ setTimeout(function () { return updateScrollbars(cm); }, 100);
+ }
+
+ // A CodeMirror instance represents an editor. This is the object
+ // that user code is usually dealing with.
+
+ function CodeMirror(place, options) {
+ var this$1 = this;
+
+ if (!(this instanceof CodeMirror)) { return new CodeMirror(place, options) }
+
+ this.options = options = options ? copyObj(options) : {};
+ // Determine effective options based on given values and defaults.
+ copyObj(defaults, options, false);
+
+ var doc = options.value;
+ if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); }
+ else if (options.mode) { doc.modeOption = options.mode; }
+ this.doc = doc;
+
+ var input = new CodeMirror.inputStyles[options.inputStyle](this);
+ var display = this.display = new Display(place, doc, input, options);
+ display.wrapper.CodeMirror = this;
+ themeChanged(this);
+ if (options.lineWrapping)
+ { this.display.wrapper.className += " CodeMirror-wrap"; }
+ initScrollbars(this);
+
+ this.state = {
+ keyMaps: [], // stores maps added by addKeyMap
+ overlays: [], // highlighting overlays, as added by addOverlay
+ modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info
+ overwrite: false,
+ delayingBlurEvent: false,
+ focused: false,
+ suppressEdits: false, // used to disable editing during key handlers when in readOnly mode
+ pasteIncoming: -1, cutIncoming: -1, // help recognize paste/cut edits in input.poll
+ selectingText: false,
+ draggingText: false,
+ highlight: new Delayed(), // stores highlight worker timeout
+ keySeq: null, // Unfinished key sequence
+ specialChars: null
+ };
+
+ if (options.autofocus && !mobile) { display.input.focus(); }
+
+ // Override magic textarea content restore that IE sometimes does
+ // on our hidden textarea on reload
+ if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); }
+
+ registerEventHandlers(this);
+ ensureGlobalHandlers();
+
+ startOperation(this);
+ this.curOp.forceUpdate = true;
+ attachDoc(this, doc);
+
+ if ((options.autofocus && !mobile) || this.hasFocus())
+ { setTimeout(function () {
+ if (this$1.hasFocus() && !this$1.state.focused) { onFocus(this$1); }
+ }, 20); }
+ else
+ { onBlur(this); }
+
+ for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt))
+ { optionHandlers[opt](this, options[opt], Init); } }
+ maybeUpdateLineNumberWidth(this);
+ if (options.finishInit) { options.finishInit(this); }
+ for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this); }
+ endOperation(this);
+ // Suppress optimizelegibility in Webkit, since it breaks text
+ // measuring on line wrapping boundaries.
+ if (webkit && options.lineWrapping &&
+ getComputedStyle(display.lineDiv).textRendering == "optimizelegibility")
+ { display.lineDiv.style.textRendering = "auto"; }
+ }
+
+ // The default configuration options.
+ CodeMirror.defaults = defaults;
+ // Functions to run when options are changed.
+ CodeMirror.optionHandlers = optionHandlers;
+
+ // Attach the necessary event handlers when initializing the editor
+ function registerEventHandlers(cm) {
+ var d = cm.display;
+ on(d.scroller, "mousedown", operation(cm, onMouseDown));
+ // Older IE's will not fire a second mousedown for a double click
+ if (ie && ie_version < 11)
+ { on(d.scroller, "dblclick", operation(cm, function (e) {
+ if (signalDOMEvent(cm, e)) { return }
+ var pos = posFromMouse(cm, e);
+ if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return }
+ e_preventDefault(e);
+ var word = cm.findWordAt(pos);
+ extendSelection(cm.doc, word.anchor, word.head);
+ })); }
+ else
+ { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); }
+ // Some browsers fire contextmenu *after* opening the menu, at
+ // which point we can't mess with it anymore. Context menu is
+ // handled in onMouseDown for these browsers.
+ on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); });
+ on(d.input.getField(), "contextmenu", function (e) {
+ if (!d.scroller.contains(e.target)) { onContextMenu(cm, e); }
+ });
+
+ // Used to suppress mouse event handling when a touch happens
+ var touchFinished, prevTouch = {end: 0};
+ function finishTouch() {
+ if (d.activeTouch) {
+ touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000);
+ prevTouch = d.activeTouch;
+ prevTouch.end = +new Date;
+ }
+ }
+ function isMouseLikeTouchEvent(e) {
+ if (e.touches.length != 1) { return false }
+ var touch = e.touches[0];
+ return touch.radiusX <= 1 && touch.radiusY <= 1
+ }
+ function farAway(touch, other) {
+ if (other.left == null) { return true }
+ var dx = other.left - touch.left, dy = other.top - touch.top;
+ return dx * dx + dy * dy > 20 * 20
+ }
+ on(d.scroller, "touchstart", function (e) {
+ if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e) && !clickInGutter(cm, e)) {
+ d.input.ensurePolled();
+ clearTimeout(touchFinished);
+ var now = +new Date;
+ d.activeTouch = {start: now, moved: false,
+ prev: now - prevTouch.end <= 300 ? prevTouch : null};
+ if (e.touches.length == 1) {
+ d.activeTouch.left = e.touches[0].pageX;
+ d.activeTouch.top = e.touches[0].pageY;
+ }
+ }
+ });
+ on(d.scroller, "touchmove", function () {
+ if (d.activeTouch) { d.activeTouch.moved = true; }
+ });
+ on(d.scroller, "touchend", function (e) {
+ var touch = d.activeTouch;
+ if (touch && !eventInWidget(d, e) && touch.left != null &&
+ !touch.moved && new Date - touch.start < 300) {
+ var pos = cm.coordsChar(d.activeTouch, "page"), range;
+ if (!touch.prev || farAway(touch, touch.prev)) // Single tap
+ { range = new Range(pos, pos); }
+ else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap
+ { range = cm.findWordAt(pos); }
+ else // Triple tap
+ { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); }
+ cm.setSelection(range.anchor, range.head);
+ cm.focus();
+ e_preventDefault(e);
+ }
+ finishTouch();
+ });
+ on(d.scroller, "touchcancel", finishTouch);
+
+ // Sync scrolling between fake scrollbars and real scrollable
+ // area, ensure viewport is updated when scrolling.
+ on(d.scroller, "scroll", function () {
+ if (d.scroller.clientHeight) {
+ updateScrollTop(cm, d.scroller.scrollTop);
+ setScrollLeft(cm, d.scroller.scrollLeft, true);
+ signal(cm, "scroll", cm);
+ }
+ });
+
+ // Listen to wheel events in order to try and update the viewport on time.
+ on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); });
+ on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); });
+
+ // Prevent wrapper from ever scrolling
+ on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; });
+
+ d.dragFunctions = {
+ enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }},
+ over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }},
+ start: function (e) { return onDragStart(cm, e); },
+ drop: operation(cm, onDrop),
+ leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }}
+ };
+
+ var inp = d.input.getField();
+ on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); });
+ on(inp, "keydown", operation(cm, onKeyDown));
+ on(inp, "keypress", operation(cm, onKeyPress));
+ on(inp, "focus", function (e) { return onFocus(cm, e); });
+ on(inp, "blur", function (e) { return onBlur(cm, e); });
+ }
+
+ var initHooks = [];
+ CodeMirror.defineInitHook = function (f) { return initHooks.push(f); };
+
+ // Indent the given line. The how parameter can be "smart",
+ // "add"/null, "subtract", or "prev". When aggressive is false
+ // (typically set to true for forced single-line indents), empty
+ // lines are not indented, and places where the mode returns Pass
+ // are left alone.
+ function indentLine(cm, n, how, aggressive) {
+ var doc = cm.doc, state;
+ if (how == null) { how = "add"; }
+ if (how == "smart") {
+ // Fall back to "prev" when the mode doesn't have an indentation
+ // method.
+ if (!doc.mode.indent) { how = "prev"; }
+ else { state = getContextBefore(cm, n).state; }
+ }
+
+ var tabSize = cm.options.tabSize;
+ var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize);
+ if (line.stateAfter) { line.stateAfter = null; }
+ var curSpaceString = line.text.match(/^\s*/)[0], indentation;
+ if (!aggressive && !/\S/.test(line.text)) {
+ indentation = 0;
+ how = "not";
+ } else if (how == "smart") {
+ indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text);
+ if (indentation == Pass || indentation > 150) {
+ if (!aggressive) { return }
+ how = "prev";
+ }
+ }
+ if (how == "prev") {
+ if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); }
+ else { indentation = 0; }
+ } else if (how == "add") {
+ indentation = curSpace + cm.options.indentUnit;
+ } else if (how == "subtract") {
+ indentation = curSpace - cm.options.indentUnit;
+ } else if (typeof how == "number") {
+ indentation = curSpace + how;
+ }
+ indentation = Math.max(0, indentation);
+
+ var indentString = "", pos = 0;
+ if (cm.options.indentWithTabs)
+ { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} }
+ if (pos < indentation) { indentString += spaceStr(indentation - pos); }
+
+ if (indentString != curSpaceString) {
+ replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input");
+ line.stateAfter = null;
+ return true
+ } else {
+ // Ensure that, if the cursor was in the whitespace at the start
+ // of the line, it is moved to the end of that space.
+ for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) {
+ var range = doc.sel.ranges[i$1];
+ if (range.head.line == n && range.head.ch < curSpaceString.length) {
+ var pos$1 = Pos(n, curSpaceString.length);
+ replaceOneSelection(doc, i$1, new Range(pos$1, pos$1));
+ break
+ }
+ }
+ }
+ }
+
+ // This will be set to a {lineWise: bool, text: [string]} object, so
+ // that, when pasting, we know what kind of selections the copied
+ // text was made out of.
+ var lastCopied = null;
+
+ function setLastCopied(newLastCopied) {
+ lastCopied = newLastCopied;
+ }
+
+ function applyTextInput(cm, inserted, deleted, sel, origin) {
+ var doc = cm.doc;
+ cm.display.shift = false;
+ if (!sel) { sel = doc.sel; }
+
+ var recent = +new Date - 200;
+ var paste = origin == "paste" || cm.state.pasteIncoming > recent;
+ var textLines = splitLinesAuto(inserted), multiPaste = null;
+ // When pasting N lines into N selections, insert one line per selection
+ if (paste && sel.ranges.length > 1) {
+ if (lastCopied && lastCopied.text.join("\n") == inserted) {
+ if (sel.ranges.length % lastCopied.text.length == 0) {
+ multiPaste = [];
+ for (var i = 0; i < lastCopied.text.length; i++)
+ { multiPaste.push(doc.splitLines(lastCopied.text[i])); }
+ }
+ } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) {
+ multiPaste = map(textLines, function (l) { return [l]; });
+ }
+ }
+
+ var updateInput = cm.curOp.updateInput;
+ // Normal behavior is to insert the new text into every selection
+ for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) {
+ var range = sel.ranges[i$1];
+ var from = range.from(), to = range.to();
+ if (range.empty()) {
+ if (deleted && deleted > 0) // Handle deletion
+ { from = Pos(from.line, from.ch - deleted); }
+ else if (cm.state.overwrite && !paste) // Handle overwrite
+ { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); }
+ else if (paste && lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == textLines.join("\n"))
+ { from = to = Pos(from.line, 0); }
+ }
+ var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines,
+ origin: origin || (paste ? "paste" : cm.state.cutIncoming > recent ? "cut" : "+input")};
+ makeChange(cm.doc, changeEvent);
+ signalLater(cm, "inputRead", cm, changeEvent);
+ }
+ if (inserted && !paste)
+ { triggerElectric(cm, inserted); }
+
+ ensureCursorVisible(cm);
+ if (cm.curOp.updateInput < 2) { cm.curOp.updateInput = updateInput; }
+ cm.curOp.typing = true;
+ cm.state.pasteIncoming = cm.state.cutIncoming = -1;
+ }
+
+ function handlePaste(e, cm) {
+ var pasted = e.clipboardData && e.clipboardData.getData("Text");
+ if (pasted) {
+ e.preventDefault();
+ if (!cm.isReadOnly() && !cm.options.disableInput)
+ { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); }
+ return true
+ }
+ }
+
+ function triggerElectric(cm, inserted) {
+ // When an 'electric' character is inserted, immediately trigger a reindent
+ if (!cm.options.electricChars || !cm.options.smartIndent) { return }
+ var sel = cm.doc.sel;
+
+ for (var i = sel.ranges.length - 1; i >= 0; i--) {
+ var range = sel.ranges[i];
+ if (range.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range.head.line)) { continue }
+ var mode = cm.getModeAt(range.head);
+ var indented = false;
+ if (mode.electricChars) {
+ for (var j = 0; j < mode.electricChars.length; j++)
+ { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) {
+ indented = indentLine(cm, range.head.line, "smart");
+ break
+ } }
+ } else if (mode.electricInput) {
+ if (mode.electricInput.test(getLine(cm.doc, range.head.line).text.slice(0, range.head.ch)))
+ { indented = indentLine(cm, range.head.line, "smart"); }
+ }
+ if (indented) { signalLater(cm, "electricInput", cm, range.head.line); }
+ }
+ }
+
+ function copyableRanges(cm) {
+ var text = [], ranges = [];
+ for (var i = 0; i < cm.doc.sel.ranges.length; i++) {
+ var line = cm.doc.sel.ranges[i].head.line;
+ var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)};
+ ranges.push(lineRange);
+ text.push(cm.getRange(lineRange.anchor, lineRange.head));
+ }
+ return {text: text, ranges: ranges}
+ }
+
+ function disableBrowserMagic(field, spellcheck, autocorrect, autocapitalize) {
+ field.setAttribute("autocorrect", autocorrect ? "" : "off");
+ field.setAttribute("autocapitalize", autocapitalize ? "" : "off");
+ field.setAttribute("spellcheck", !!spellcheck);
+ }
+
+ function hiddenTextarea() {
+ var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none");
+ var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;");
+ // The textarea is kept positioned near the cursor to prevent the
+ // fact that it'll be scrolled into view on input from scrolling
+ // our fake cursor out of view. On webkit, when wrap=off, paste is
+ // very slow. So make the area wide instead.
+ if (webkit) { te.style.width = "1000px"; }
+ else { te.setAttribute("wrap", "off"); }
+ // If border: 0; -- iOS fails to open keyboard (issue #1287)
+ if (ios) { te.style.border = "1px solid black"; }
+ disableBrowserMagic(te);
+ return div
+ }
+
+ // The publicly visible API. Note that methodOp(f) means
+ // 'wrap f in an operation, performed on its `this` parameter'.
+
+ // This is not the complete set of editor methods. Most of the
+ // methods defined on the Doc type are also injected into
+ // CodeMirror.prototype, for backwards compatibility and
+ // convenience.
+
+ function addEditorMethods(CodeMirror) {
+ var optionHandlers = CodeMirror.optionHandlers;
+
+ var helpers = CodeMirror.helpers = {};
+
+ CodeMirror.prototype = {
+ constructor: CodeMirror,
+ focus: function(){window.focus(); this.display.input.focus();},
+
+ setOption: function(option, value) {
+ var options = this.options, old = options[option];
+ if (options[option] == value && option != "mode") { return }
+ options[option] = value;
+ if (optionHandlers.hasOwnProperty(option))
+ { operation(this, optionHandlers[option])(this, value, old); }
+ signal(this, "optionChange", this, option);
+ },
+
+ getOption: function(option) {return this.options[option]},
+ getDoc: function() {return this.doc},
+
+ addKeyMap: function(map, bottom) {
+ this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map));
+ },
+ removeKeyMap: function(map) {
+ var maps = this.state.keyMaps;
+ for (var i = 0; i < maps.length; ++i)
+ { if (maps[i] == map || maps[i].name == map) {
+ maps.splice(i, 1);
+ return true
+ } }
+ },
+
+ addOverlay: methodOp(function(spec, options) {
+ var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec);
+ if (mode.startState) { throw new Error("Overlays may not be stateful.") }
+ insertSorted(this.state.overlays,
+ {mode: mode, modeSpec: spec, opaque: options && options.opaque,
+ priority: (options && options.priority) || 0},
+ function (overlay) { return overlay.priority; });
+ this.state.modeGen++;
+ regChange(this);
+ }),
+ removeOverlay: methodOp(function(spec) {
+ var overlays = this.state.overlays;
+ for (var i = 0; i < overlays.length; ++i) {
+ var cur = overlays[i].modeSpec;
+ if (cur == spec || typeof spec == "string" && cur.name == spec) {
+ overlays.splice(i, 1);
+ this.state.modeGen++;
+ regChange(this);
+ return
+ }
+ }
+ }),
+
+ indentLine: methodOp(function(n, dir, aggressive) {
+ if (typeof dir != "string" && typeof dir != "number") {
+ if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; }
+ else { dir = dir ? "add" : "subtract"; }
+ }
+ if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); }
+ }),
+ indentSelection: methodOp(function(how) {
+ var ranges = this.doc.sel.ranges, end = -1;
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i];
+ if (!range.empty()) {
+ var from = range.from(), to = range.to();
+ var start = Math.max(end, from.line);
+ end = Math.min(this.lastLine(), to.line - (to.ch ? 0 : 1)) + 1;
+ for (var j = start; j < end; ++j)
+ { indentLine(this, j, how); }
+ var newRanges = this.doc.sel.ranges;
+ if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0)
+ { replaceOneSelection(this.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); }
+ } else if (range.head.line > end) {
+ indentLine(this, range.head.line, how, true);
+ end = range.head.line;
+ if (i == this.doc.sel.primIndex) { ensureCursorVisible(this); }
+ }
+ }
+ }),
+
+ // Fetch the parser token for a given character. Useful for hacks
+ // that want to inspect the mode state (say, for completion).
+ getTokenAt: function(pos, precise) {
+ return takeToken(this, pos, precise)
+ },
+
+ getLineTokens: function(line, precise) {
+ return takeToken(this, Pos(line), precise, true)
+ },
+
+ getTokenTypeAt: function(pos) {
+ pos = clipPos(this.doc, pos);
+ var styles = getLineStyles(this, getLine(this.doc, pos.line));
+ var before = 0, after = (styles.length - 1) / 2, ch = pos.ch;
+ var type;
+ if (ch == 0) { type = styles[2]; }
+ else { for (;;) {
+ var mid = (before + after) >> 1;
+ if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; }
+ else if (styles[mid * 2 + 1] < ch) { before = mid + 1; }
+ else { type = styles[mid * 2 + 2]; break }
+ } }
+ var cut = type ? type.indexOf("overlay ") : -1;
+ return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1)
+ },
+
+ getModeAt: function(pos) {
+ var mode = this.doc.mode;
+ if (!mode.innerMode) { return mode }
+ return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode
+ },
+
+ getHelper: function(pos, type) {
+ return this.getHelpers(pos, type)[0]
+ },
+
+ getHelpers: function(pos, type) {
+ var found = [];
+ if (!helpers.hasOwnProperty(type)) { return found }
+ var help = helpers[type], mode = this.getModeAt(pos);
+ if (typeof mode[type] == "string") {
+ if (help[mode[type]]) { found.push(help[mode[type]]); }
+ } else if (mode[type]) {
+ for (var i = 0; i < mode[type].length; i++) {
+ var val = help[mode[type][i]];
+ if (val) { found.push(val); }
+ }
+ } else if (mode.helperType && help[mode.helperType]) {
+ found.push(help[mode.helperType]);
+ } else if (help[mode.name]) {
+ found.push(help[mode.name]);
+ }
+ for (var i$1 = 0; i$1 < help._global.length; i$1++) {
+ var cur = help._global[i$1];
+ if (cur.pred(mode, this) && indexOf(found, cur.val) == -1)
+ { found.push(cur.val); }
+ }
+ return found
+ },
+
+ getStateAfter: function(line, precise) {
+ var doc = this.doc;
+ line = clipLine(doc, line == null ? doc.first + doc.size - 1: line);
+ return getContextBefore(this, line + 1, precise).state
+ },
+
+ cursorCoords: function(start, mode) {
+ var pos, range = this.doc.sel.primary();
+ if (start == null) { pos = range.head; }
+ else if (typeof start == "object") { pos = clipPos(this.doc, start); }
+ else { pos = start ? range.from() : range.to(); }
+ return cursorCoords(this, pos, mode || "page")
+ },
+
+ charCoords: function(pos, mode) {
+ return charCoords(this, clipPos(this.doc, pos), mode || "page")
+ },
+
+ coordsChar: function(coords, mode) {
+ coords = fromCoordSystem(this, coords, mode || "page");
+ return coordsChar(this, coords.left, coords.top)
+ },
+
+ lineAtHeight: function(height, mode) {
+ height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top;
+ return lineAtHeight(this.doc, height + this.display.viewOffset)
+ },
+ heightAtLine: function(line, mode, includeWidgets) {
+ var end = false, lineObj;
+ if (typeof line == "number") {
+ var last = this.doc.first + this.doc.size - 1;
+ if (line < this.doc.first) { line = this.doc.first; }
+ else if (line > last) { line = last; end = true; }
+ lineObj = getLine(this.doc, line);
+ } else {
+ lineObj = line;
+ }
+ return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top +
+ (end ? this.doc.height - heightAtLine(lineObj) : 0)
+ },
+
+ defaultTextHeight: function() { return textHeight(this.display) },
+ defaultCharWidth: function() { return charWidth(this.display) },
+
+ getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}},
+
+ addWidget: function(pos, node, scroll, vert, horiz) {
+ var display = this.display;
+ pos = cursorCoords(this, clipPos(this.doc, pos));
+ var top = pos.bottom, left = pos.left;
+ node.style.position = "absolute";
+ node.setAttribute("cm-ignore-events", "true");
+ this.display.input.setUneditable(node);
+ display.sizer.appendChild(node);
+ if (vert == "over") {
+ top = pos.top;
+ } else if (vert == "above" || vert == "near") {
+ var vspace = Math.max(display.wrapper.clientHeight, this.doc.height),
+ hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth);
+ // Default to positioning above (if specified and possible); otherwise default to positioning below
+ if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight)
+ { top = pos.top - node.offsetHeight; }
+ else if (pos.bottom + node.offsetHeight <= vspace)
+ { top = pos.bottom; }
+ if (left + node.offsetWidth > hspace)
+ { left = hspace - node.offsetWidth; }
+ }
+ node.style.top = top + "px";
+ node.style.left = node.style.right = "";
+ if (horiz == "right") {
+ left = display.sizer.clientWidth - node.offsetWidth;
+ node.style.right = "0px";
+ } else {
+ if (horiz == "left") { left = 0; }
+ else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; }
+ node.style.left = left + "px";
+ }
+ if (scroll)
+ { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); }
+ },
+
+ triggerOnKeyDown: methodOp(onKeyDown),
+ triggerOnKeyPress: methodOp(onKeyPress),
+ triggerOnKeyUp: onKeyUp,
+ triggerOnMouseDown: methodOp(onMouseDown),
+
+ execCommand: function(cmd) {
+ if (commands.hasOwnProperty(cmd))
+ { return commands[cmd].call(null, this) }
+ },
+
+ triggerElectric: methodOp(function(text) { triggerElectric(this, text); }),
+
+ findPosH: function(from, amount, unit, visually) {
+ var dir = 1;
+ if (amount < 0) { dir = -1; amount = -amount; }
+ var cur = clipPos(this.doc, from);
+ for (var i = 0; i < amount; ++i) {
+ cur = findPosH(this.doc, cur, dir, unit, visually);
+ if (cur.hitSide) { break }
+ }
+ return cur
+ },
+
+ moveH: methodOp(function(dir, unit) {
+ var this$1 = this;
+
+ this.extendSelectionsBy(function (range) {
+ if (this$1.display.shift || this$1.doc.extend || range.empty())
+ { return findPosH(this$1.doc, range.head, dir, unit, this$1.options.rtlMoveVisually) }
+ else
+ { return dir < 0 ? range.from() : range.to() }
+ }, sel_move);
+ }),
+
+ deleteH: methodOp(function(dir, unit) {
+ var sel = this.doc.sel, doc = this.doc;
+ if (sel.somethingSelected())
+ { doc.replaceSelection("", null, "+delete"); }
+ else
+ { deleteNearSelection(this, function (range) {
+ var other = findPosH(doc, range.head, dir, unit, false);
+ return dir < 0 ? {from: other, to: range.head} : {from: range.head, to: other}
+ }); }
+ }),
+
+ findPosV: function(from, amount, unit, goalColumn) {
+ var dir = 1, x = goalColumn;
+ if (amount < 0) { dir = -1; amount = -amount; }
+ var cur = clipPos(this.doc, from);
+ for (var i = 0; i < amount; ++i) {
+ var coords = cursorCoords(this, cur, "div");
+ if (x == null) { x = coords.left; }
+ else { coords.left = x; }
+ cur = findPosV(this, coords, dir, unit);
+ if (cur.hitSide) { break }
+ }
+ return cur
+ },
+
+ moveV: methodOp(function(dir, unit) {
+ var this$1 = this;
+
+ var doc = this.doc, goals = [];
+ var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected();
+ doc.extendSelectionsBy(function (range) {
+ if (collapse)
+ { return dir < 0 ? range.from() : range.to() }
+ var headPos = cursorCoords(this$1, range.head, "div");
+ if (range.goalColumn != null) { headPos.left = range.goalColumn; }
+ goals.push(headPos.left);
+ var pos = findPosV(this$1, headPos, dir, unit);
+ if (unit == "page" && range == doc.sel.primary())
+ { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); }
+ return pos
+ }, sel_move);
+ if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++)
+ { doc.sel.ranges[i].goalColumn = goals[i]; } }
+ }),
+
+ // Find the word at the given position (as returned by coordsChar).
+ findWordAt: function(pos) {
+ var doc = this.doc, line = getLine(doc, pos.line).text;
+ var start = pos.ch, end = pos.ch;
+ if (line) {
+ var helper = this.getHelper(pos, "wordChars");
+ if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; }
+ var startChar = line.charAt(start);
+ var check = isWordChar(startChar, helper)
+ ? function (ch) { return isWordChar(ch, helper); }
+ : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); }
+ : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); };
+ while (start > 0 && check(line.charAt(start - 1))) { --start; }
+ while (end < line.length && check(line.charAt(end))) { ++end; }
+ }
+ return new Range(Pos(pos.line, start), Pos(pos.line, end))
+ },
+
+ toggleOverwrite: function(value) {
+ if (value != null && value == this.state.overwrite) { return }
+ if (this.state.overwrite = !this.state.overwrite)
+ { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
+ else
+ { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); }
+
+ signal(this, "overwriteToggle", this, this.state.overwrite);
+ },
+ hasFocus: function() { return this.display.input.getField() == activeElt() },
+ isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) },
+
+ scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }),
+ getScrollInfo: function() {
+ var scroller = this.display.scroller;
+ return {left: scroller.scrollLeft, top: scroller.scrollTop,
+ height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight,
+ width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth,
+ clientHeight: displayHeight(this), clientWidth: displayWidth(this)}
+ },
+
+ scrollIntoView: methodOp(function(range, margin) {
+ if (range == null) {
+ range = {from: this.doc.sel.primary().head, to: null};
+ if (margin == null) { margin = this.options.cursorScrollMargin; }
+ } else if (typeof range == "number") {
+ range = {from: Pos(range, 0), to: null};
+ } else if (range.from == null) {
+ range = {from: range, to: null};
+ }
+ if (!range.to) { range.to = range.from; }
+ range.margin = margin || 0;
+
+ if (range.from.line != null) {
+ scrollToRange(this, range);
+ } else {
+ scrollToCoordsRange(this, range.from, range.to, range.margin);
+ }
+ }),
+
+ setSize: methodOp(function(width, height) {
+ var this$1 = this;
+
+ var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; };
+ if (width != null) { this.display.wrapper.style.width = interpret(width); }
+ if (height != null) { this.display.wrapper.style.height = interpret(height); }
+ if (this.options.lineWrapping) { clearLineMeasurementCache(this); }
+ var lineNo = this.display.viewFrom;
+ this.doc.iter(lineNo, this.display.viewTo, function (line) {
+ if (line.widgets) { for (var i = 0; i < line.widgets.length; i++)
+ { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo, "widget"); break } } }
+ ++lineNo;
+ });
+ this.curOp.forceUpdate = true;
+ signal(this, "refresh", this);
+ }),
+
+ operation: function(f){return runInOp(this, f)},
+ startOperation: function(){return startOperation(this)},
+ endOperation: function(){return endOperation(this)},
+
+ refresh: methodOp(function() {
+ var oldHeight = this.display.cachedTextHeight;
+ regChange(this);
+ this.curOp.forceUpdate = true;
+ clearCaches(this);
+ scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop);
+ updateGutterSpace(this.display);
+ if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5 || this.options.lineWrapping)
+ { estimateLineHeights(this); }
+ signal(this, "refresh", this);
+ }),
+
+ swapDoc: methodOp(function(doc) {
+ var old = this.doc;
+ old.cm = null;
+ // Cancel the current text selection if any (#5821)
+ if (this.state.selectingText) { this.state.selectingText(); }
+ attachDoc(this, doc);
+ clearCaches(this);
+ this.display.input.reset();
+ scrollToCoords(this, doc.scrollLeft, doc.scrollTop);
+ this.curOp.forceScroll = true;
+ signalLater(this, "swapDoc", this, old);
+ return old
+ }),
+
+ phrase: function(phraseText) {
+ var phrases = this.options.phrases;
+ return phrases && Object.prototype.hasOwnProperty.call(phrases, phraseText) ? phrases[phraseText] : phraseText
+ },
+
+ getInputField: function(){return this.display.input.getField()},
+ getWrapperElement: function(){return this.display.wrapper},
+ getScrollerElement: function(){return this.display.scroller},
+ getGutterElement: function(){return this.display.gutters}
+ };
+ eventMixin(CodeMirror);
+
+ CodeMirror.registerHelper = function(type, name, value) {
+ if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; }
+ helpers[type][name] = value;
+ };
+ CodeMirror.registerGlobalHelper = function(type, name, predicate, value) {
+ CodeMirror.registerHelper(type, name, value);
+ helpers[type]._global.push({pred: predicate, val: value});
+ };
+ }
+
+ // Used for horizontal relative motion. Dir is -1 or 1 (left or
+ // right), unit can be "codepoint", "char", "column" (like char, but
+ // doesn't cross line boundaries), "word" (across next word), or
+ // "group" (to the start of next group of word or
+ // non-word-non-whitespace chars). The visually param controls
+ // whether, in right-to-left text, direction 1 means to move towards
+ // the next index in the string, or towards the character to the right
+ // of the current position. The resulting position will have a
+ // hitSide=true property if it reached the end of the document.
+ function findPosH(doc, pos, dir, unit, visually) {
+ var oldPos = pos;
+ var origDir = dir;
+ var lineObj = getLine(doc, pos.line);
+ var lineDir = visually && doc.direction == "rtl" ? -dir : dir;
+ function findNextLine() {
+ var l = pos.line + lineDir;
+ if (l < doc.first || l >= doc.first + doc.size) { return false }
+ pos = new Pos(l, pos.ch, pos.sticky);
+ return lineObj = getLine(doc, l)
+ }
+ function moveOnce(boundToLine) {
+ var next;
+ if (unit == "codepoint") {
+ var ch = lineObj.text.charCodeAt(pos.ch + (unit > 0 ? 0 : -1));
+ if (isNaN(ch)) { next = null; }
+ else { next = new Pos(pos.line, Math.max(0, Math.min(lineObj.text.length, pos.ch + dir * (ch >= 0xD800 && ch < 0xDC00 ? 2 : 1))),
+ -dir); }
+ } else if (visually) {
+ next = moveVisually(doc.cm, lineObj, pos, dir);
+ } else {
+ next = moveLogically(lineObj, pos, dir);
+ }
+ if (next == null) {
+ if (!boundToLine && findNextLine())
+ { pos = endOfLine(visually, doc.cm, lineObj, pos.line, lineDir); }
+ else
+ { return false }
+ } else {
+ pos = next;
+ }
+ return true
+ }
+
+ if (unit == "char" || unit == "codepoint") {
+ moveOnce();
+ } else if (unit == "column") {
+ moveOnce(true);
+ } else if (unit == "word" || unit == "group") {
+ var sawType = null, group = unit == "group";
+ var helper = doc.cm && doc.cm.getHelper(pos, "wordChars");
+ for (var first = true;; first = false) {
+ if (dir < 0 && !moveOnce(!first)) { break }
+ var cur = lineObj.text.charAt(pos.ch) || "\n";
+ var type = isWordChar(cur, helper) ? "w"
+ : group && cur == "\n" ? "n"
+ : !group || /\s/.test(cur) ? null
+ : "p";
+ if (group && !first && !type) { type = "s"; }
+ if (sawType && sawType != type) {
+ if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";}
+ break
+ }
+
+ if (type) { sawType = type; }
+ if (dir > 0 && !moveOnce(!first)) { break }
+ }
+ }
+ var result = skipAtomic(doc, pos, oldPos, origDir, true);
+ if (equalCursorPos(oldPos, result)) { result.hitSide = true; }
+ return result
+ }
+
+ // For relative vertical movement. Dir may be -1 or 1. Unit can be
+ // "page" or "line". The resulting position will have a hitSide=true
+ // property if it reached the end of the document.
+ function findPosV(cm, pos, dir, unit) {
+ var doc = cm.doc, x = pos.left, y;
+ if (unit == "page") {
+ var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight);
+ var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3);
+ y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount;
+
+ } else if (unit == "line") {
+ y = dir > 0 ? pos.bottom + 3 : pos.top - 3;
+ }
+ var target;
+ for (;;) {
+ target = coordsChar(cm, x, y);
+ if (!target.outside) { break }
+ if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break }
+ y += dir * 5;
+ }
+ return target
+ }
+
+ // CONTENTEDITABLE INPUT STYLE
+
+ var ContentEditableInput = function(cm) {
+ this.cm = cm;
+ this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null;
+ this.polling = new Delayed();
+ this.composing = null;
+ this.gracePeriod = false;
+ this.readDOMTimeout = null;
+ };
+
+ ContentEditableInput.prototype.init = function (display) {
+ var this$1 = this;
+
+ var input = this, cm = input.cm;
+ var div = input.div = display.lineDiv;
+ disableBrowserMagic(div, cm.options.spellcheck, cm.options.autocorrect, cm.options.autocapitalize);
+
+ function belongsToInput(e) {
+ for (var t = e.target; t; t = t.parentNode) {
+ if (t == div) { return true }
+ if (/\bCodeMirror-(?:line)?widget\b/.test(t.className)) { break }
+ }
+ return false
+ }
+
+ on(div, "paste", function (e) {
+ if (!belongsToInput(e) || signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
+ // IE doesn't fire input events, so we schedule a read for the pasted content in this way
+ if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); }
+ });
+
+ on(div, "compositionstart", function (e) {
+ this$1.composing = {data: e.data, done: false};
+ });
+ on(div, "compositionupdate", function (e) {
+ if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; }
+ });
+ on(div, "compositionend", function (e) {
+ if (this$1.composing) {
+ if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); }
+ this$1.composing.done = true;
+ }
+ });
+
+ on(div, "touchstart", function () { return input.forceCompositionEnd(); });
+
+ on(div, "input", function () {
+ if (!this$1.composing) { this$1.readFromDOMSoon(); }
+ });
+
+ function onCopyCut(e) {
+ if (!belongsToInput(e) || signalDOMEvent(cm, e)) { return }
+ if (cm.somethingSelected()) {
+ setLastCopied({lineWise: false, text: cm.getSelections()});
+ if (e.type == "cut") { cm.replaceSelection("", null, "cut"); }
+ } else if (!cm.options.lineWiseCopyCut) {
+ return
+ } else {
+ var ranges = copyableRanges(cm);
+ setLastCopied({lineWise: true, text: ranges.text});
+ if (e.type == "cut") {
+ cm.operation(function () {
+ cm.setSelections(ranges.ranges, 0, sel_dontScroll);
+ cm.replaceSelection("", null, "cut");
+ });
+ }
+ }
+ if (e.clipboardData) {
+ e.clipboardData.clearData();
+ var content = lastCopied.text.join("\n");
+ // iOS exposes the clipboard API, but seems to discard content inserted into it
+ e.clipboardData.setData("Text", content);
+ if (e.clipboardData.getData("Text") == content) {
+ e.preventDefault();
+ return
+ }
+ }
+ // Old-fashioned briefly-focus-a-textarea hack
+ var kludge = hiddenTextarea(), te = kludge.firstChild;
+ cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild);
+ te.value = lastCopied.text.join("\n");
+ var hadFocus = document.activeElement;
+ selectInput(te);
+ setTimeout(function () {
+ cm.display.lineSpace.removeChild(kludge);
+ hadFocus.focus();
+ if (hadFocus == div) { input.showPrimarySelection(); }
+ }, 50);
+ }
+ on(div, "copy", onCopyCut);
+ on(div, "cut", onCopyCut);
+ };
+
+ ContentEditableInput.prototype.screenReaderLabelChanged = function (label) {
+ // Label for screenreaders, accessibility
+ if(label) {
+ this.div.setAttribute('aria-label', label);
+ } else {
+ this.div.removeAttribute('aria-label');
+ }
+ };
+
+ ContentEditableInput.prototype.prepareSelection = function () {
+ var result = prepareSelection(this.cm, false);
+ result.focus = document.activeElement == this.div;
+ return result
+ };
+
+ ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
+ if (!info || !this.cm.display.view.length) { return }
+ if (info.focus || takeFocus) { this.showPrimarySelection(); }
+ this.showMultipleSelections(info);
+ };
+
+ ContentEditableInput.prototype.getSelection = function () {
+ return this.cm.display.wrapper.ownerDocument.getSelection()
+ };
+
+ ContentEditableInput.prototype.showPrimarySelection = function () {
+ var sel = this.getSelection(), cm = this.cm, prim = cm.doc.sel.primary();
+ var from = prim.from(), to = prim.to();
+
+ if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) {
+ sel.removeAllRanges();
+ return
+ }
+
+ var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
+ var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset);
+ if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
+ cmp(minPos(curAnchor, curFocus), from) == 0 &&
+ cmp(maxPos(curAnchor, curFocus), to) == 0)
+ { return }
+
+ var view = cm.display.view;
+ var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) ||
+ {node: view[0].measure.map[2], offset: 0};
+ var end = to.line < cm.display.viewTo && posToDOM(cm, to);
+ if (!end) {
+ var measure = view[view.length - 1].measure;
+ var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map;
+ end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]};
+ }
+
+ if (!start || !end) {
+ sel.removeAllRanges();
+ return
+ }
+
+ var old = sel.rangeCount && sel.getRangeAt(0), rng;
+ try { rng = range(start.node, start.offset, end.offset, end.node); }
+ catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
+ if (rng) {
+ if (!gecko && cm.state.focused) {
+ sel.collapse(start.node, start.offset);
+ if (!rng.collapsed) {
+ sel.removeAllRanges();
+ sel.addRange(rng);
+ }
+ } else {
+ sel.removeAllRanges();
+ sel.addRange(rng);
+ }
+ if (old && sel.anchorNode == null) { sel.addRange(old); }
+ else if (gecko) { this.startGracePeriod(); }
+ }
+ this.rememberSelection();
+ };
+
+ ContentEditableInput.prototype.startGracePeriod = function () {
+ var this$1 = this;
+
+ clearTimeout(this.gracePeriod);
+ this.gracePeriod = setTimeout(function () {
+ this$1.gracePeriod = false;
+ if (this$1.selectionChanged())
+ { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); }
+ }, 20);
+ };
+
+ ContentEditableInput.prototype.showMultipleSelections = function (info) {
+ removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors);
+ removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection);
+ };
+
+ ContentEditableInput.prototype.rememberSelection = function () {
+ var sel = this.getSelection();
+ this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset;
+ this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset;
+ };
+
+ ContentEditableInput.prototype.selectionInEditor = function () {
+ var sel = this.getSelection();
+ if (!sel.rangeCount) { return false }
+ var node = sel.getRangeAt(0).commonAncestorContainer;
+ return contains(this.div, node)
+ };
+
+ ContentEditableInput.prototype.focus = function () {
+ if (this.cm.options.readOnly != "nocursor") {
+ if (!this.selectionInEditor() || document.activeElement != this.div)
+ { this.showSelection(this.prepareSelection(), true); }
+ this.div.focus();
+ }
+ };
+ ContentEditableInput.prototype.blur = function () { this.div.blur(); };
+ ContentEditableInput.prototype.getField = function () { return this.div };
+
+ ContentEditableInput.prototype.supportsTouch = function () { return true };
+
+ ContentEditableInput.prototype.receivedFocus = function () {
+ var input = this;
+ if (this.selectionInEditor())
+ { this.pollSelection(); }
+ else
+ { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); }
+
+ function poll() {
+ if (input.cm.state.focused) {
+ input.pollSelection();
+ input.polling.set(input.cm.options.pollInterval, poll);
+ }
+ }
+ this.polling.set(this.cm.options.pollInterval, poll);
+ };
+
+ ContentEditableInput.prototype.selectionChanged = function () {
+ var sel = this.getSelection();
+ return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
+ sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
+ };
+
+ ContentEditableInput.prototype.pollSelection = function () {
+ if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return }
+ var sel = this.getSelection(), cm = this.cm;
+ // On Android Chrome (version 56, at least), backspacing into an
+ // uneditable block element will put the cursor in that element,
+ // and then, because it's not editable, hide the virtual keyboard.
+ // Because Android doesn't allow us to actually detect backspace
+ // presses in a sane way, this code checks for when that happens
+ // and simulates a backspace press in this case.
+ if (android && chrome && this.cm.display.gutterSpecs.length && isInGutter(sel.anchorNode)) {
+ this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs});
+ this.blur();
+ this.focus();
+ return
+ }
+ if (this.composing) { return }
+ this.rememberSelection();
+ var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset);
+ var head = domToPos(cm, sel.focusNode, sel.focusOffset);
+ if (anchor && head) { runInOp(cm, function () {
+ setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll);
+ if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; }
+ }); }
+ };
+
+ ContentEditableInput.prototype.pollContent = function () {
+ if (this.readDOMTimeout != null) {
+ clearTimeout(this.readDOMTimeout);
+ this.readDOMTimeout = null;
+ }
+
+ var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary();
+ var from = sel.from(), to = sel.to();
+ if (from.ch == 0 && from.line > cm.firstLine())
+ { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); }
+ if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
+ { to = Pos(to.line + 1, 0); }
+ if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
+
+ var fromIndex, fromLine, fromNode;
+ if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
+ fromLine = lineNo(display.view[0].line);
+ fromNode = display.view[0].node;
+ } else {
+ fromLine = lineNo(display.view[fromIndex].line);
+ fromNode = display.view[fromIndex - 1].node.nextSibling;
+ }
+ var toIndex = findViewIndex(cm, to.line);
+ var toLine, toNode;
+ if (toIndex == display.view.length - 1) {
+ toLine = display.viewTo - 1;
+ toNode = display.lineDiv.lastChild;
+ } else {
+ toLine = lineNo(display.view[toIndex + 1].line) - 1;
+ toNode = display.view[toIndex + 1].node.previousSibling;
+ }
+
+ if (!fromNode) { return false }
+ var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine));
+ var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length));
+ while (newText.length > 1 && oldText.length > 1) {
+ if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; }
+ else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; }
+ else { break }
+ }
+
+ var cutFront = 0, cutEnd = 0;
+ var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length);
+ while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
+ { ++cutFront; }
+ var newBot = lst(newText), oldBot = lst(oldText);
+ var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
+ oldBot.length - (oldText.length == 1 ? cutFront : 0));
+ while (cutEnd < maxCutEnd &&
+ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
+ { ++cutEnd; }
+ // Try to move start of change to start of selection if ambiguous
+ if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) {
+ while (cutFront && cutFront > from.ch &&
+ newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) {
+ cutFront--;
+ cutEnd++;
+ }
+ }
+
+ newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "");
+ newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "");
+
+ var chFrom = Pos(fromLine, cutFront);
+ var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0);
+ if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
+ replaceRange(cm.doc, newText, chFrom, chTo, "+input");
+ return true
+ }
+ };
+
+ ContentEditableInput.prototype.ensurePolled = function () {
+ this.forceCompositionEnd();
+ };
+ ContentEditableInput.prototype.reset = function () {
+ this.forceCompositionEnd();
+ };
+ ContentEditableInput.prototype.forceCompositionEnd = function () {
+ if (!this.composing) { return }
+ clearTimeout(this.readDOMTimeout);
+ this.composing = null;
+ this.updateFromDOM();
+ this.div.blur();
+ this.div.focus();
+ };
+ ContentEditableInput.prototype.readFromDOMSoon = function () {
+ var this$1 = this;
+
+ if (this.readDOMTimeout != null) { return }
+ this.readDOMTimeout = setTimeout(function () {
+ this$1.readDOMTimeout = null;
+ if (this$1.composing) {
+ if (this$1.composing.done) { this$1.composing = null; }
+ else { return }
+ }
+ this$1.updateFromDOM();
+ }, 80);
+ };
+
+ ContentEditableInput.prototype.updateFromDOM = function () {
+ var this$1 = this;
+
+ if (this.cm.isReadOnly() || !this.pollContent())
+ { runInOp(this.cm, function () { return regChange(this$1.cm); }); }
+ };
+
+ ContentEditableInput.prototype.setUneditable = function (node) {
+ node.contentEditable = "false";
+ };
+
+ ContentEditableInput.prototype.onKeyPress = function (e) {
+ if (e.charCode == 0 || this.composing) { return }
+ e.preventDefault();
+ if (!this.cm.isReadOnly())
+ { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); }
+ };
+
+ ContentEditableInput.prototype.readOnlyChanged = function (val) {
+ this.div.contentEditable = String(val != "nocursor");
+ };
+
+ ContentEditableInput.prototype.onContextMenu = function () {};
+ ContentEditableInput.prototype.resetPosition = function () {};
+
+ ContentEditableInput.prototype.needsContentAttribute = true;
+
+ function posToDOM(cm, pos) {
+ var view = findViewForLine(cm, pos.line);
+ if (!view || view.hidden) { return null }
+ var line = getLine(cm.doc, pos.line);
+ var info = mapFromLineView(view, line, pos.line);
+
+ var order = getOrder(line, cm.doc.direction), side = "left";
+ if (order) {
+ var partPos = getBidiPartAt(order, pos.ch);
+ side = partPos % 2 ? "right" : "left";
+ }
+ var result = nodeAndOffsetInLineMap(info.map, pos.ch, side);
+ result.offset = result.collapse == "right" ? result.end : result.start;
+ return result
+ }
+
+ function isInGutter(node) {
+ for (var scan = node; scan; scan = scan.parentNode)
+ { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } }
+ return false
+ }
+
+ function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos }
+
+ function domTextBetween(cm, from, to, fromLine, toLine) {
+ var text = "", closing = false, lineSep = cm.doc.lineSeparator(), extraLinebreak = false;
+ function recognizeMarker(id) { return function (marker) { return marker.id == id; } }
+ function close() {
+ if (closing) {
+ text += lineSep;
+ if (extraLinebreak) { text += lineSep; }
+ closing = extraLinebreak = false;
+ }
+ }
+ function addText(str) {
+ if (str) {
+ close();
+ text += str;
+ }
+ }
+ function walk(node) {
+ if (node.nodeType == 1) {
+ var cmText = node.getAttribute("cm-text");
+ if (cmText) {
+ addText(cmText);
+ return
+ }
+ var markerID = node.getAttribute("cm-marker"), range;
+ if (markerID) {
+ var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID));
+ if (found.length && (range = found[0].find(0)))
+ { addText(getBetween(cm.doc, range.from, range.to).join(lineSep)); }
+ return
+ }
+ if (node.getAttribute("contenteditable") == "false") { return }
+ var isBlock = /^(pre|div|p|li|table|br)$/i.test(node.nodeName);
+ if (!/^br$/i.test(node.nodeName) && node.textContent.length == 0) { return }
+
+ if (isBlock) { close(); }
+ for (var i = 0; i < node.childNodes.length; i++)
+ { walk(node.childNodes[i]); }
+
+ if (/^(pre|p)$/i.test(node.nodeName)) { extraLinebreak = true; }
+ if (isBlock) { closing = true; }
+ } else if (node.nodeType == 3) {
+ addText(node.nodeValue.replace(/\u200b/g, "").replace(/\u00a0/g, " "));
+ }
+ }
+ for (;;) {
+ walk(from);
+ if (from == to) { break }
+ from = from.nextSibling;
+ extraLinebreak = false;
+ }
+ return text
+ }
+
+ function domToPos(cm, node, offset) {
+ var lineNode;
+ if (node == cm.display.lineDiv) {
+ lineNode = cm.display.lineDiv.childNodes[offset];
+ if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) }
+ node = null; offset = 0;
+ } else {
+ for (lineNode = node;; lineNode = lineNode.parentNode) {
+ if (!lineNode || lineNode == cm.display.lineDiv) { return null }
+ if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break }
+ }
+ }
+ for (var i = 0; i < cm.display.view.length; i++) {
+ var lineView = cm.display.view[i];
+ if (lineView.node == lineNode)
+ { return locateNodeInLineView(lineView, node, offset) }
+ }
+ }
+
+ function locateNodeInLineView(lineView, node, offset) {
+ var wrapper = lineView.text.firstChild, bad = false;
+ if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) }
+ if (node == wrapper) {
+ bad = true;
+ node = wrapper.childNodes[offset];
+ offset = 0;
+ if (!node) {
+ var line = lineView.rest ? lst(lineView.rest) : lineView.line;
+ return badPos(Pos(lineNo(line), line.text.length), bad)
+ }
+ }
+
+ var textNode = node.nodeType == 3 ? node : null, topNode = node;
+ if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) {
+ textNode = node.firstChild;
+ if (offset) { offset = textNode.nodeValue.length; }
+ }
+ while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; }
+ var measure = lineView.measure, maps = measure.maps;
+
+ function find(textNode, topNode, offset) {
+ for (var i = -1; i < (maps ? maps.length : 0); i++) {
+ var map = i < 0 ? measure.map : maps[i];
+ for (var j = 0; j < map.length; j += 3) {
+ var curNode = map[j + 2];
+ if (curNode == textNode || curNode == topNode) {
+ var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]);
+ var ch = map[j] + offset;
+ if (offset < 0 || curNode != textNode) { ch = map[j + (offset ? 1 : 0)]; }
+ return Pos(line, ch)
+ }
+ }
+ }
+ }
+ var found = find(textNode, topNode, offset);
+ if (found) { return badPos(found, bad) }
+
+ // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems
+ for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) {
+ found = find(after, after.firstChild, 0);
+ if (found)
+ { return badPos(Pos(found.line, found.ch - dist), bad) }
+ else
+ { dist += after.textContent.length; }
+ }
+ for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) {
+ found = find(before, before.firstChild, -1);
+ if (found)
+ { return badPos(Pos(found.line, found.ch + dist$1), bad) }
+ else
+ { dist$1 += before.textContent.length; }
+ }
+ }
+
+ // TEXTAREA INPUT STYLE
+
+ var TextareaInput = function(cm) {
+ this.cm = cm;
+ // See input.poll and input.reset
+ this.prevInput = "";
+
+ // Flag that indicates whether we expect input to appear real soon
+ // now (after some event like 'keypress' or 'input') and are
+ // polling intensively.
+ this.pollingFast = false;
+ // Self-resetting timeout for the poller
+ this.polling = new Delayed();
+ // Used to work around IE issue with selection being forgotten when focus moves away from textarea
+ this.hasSelection = false;
+ this.composing = null;
+ };
+
+ TextareaInput.prototype.init = function (display) {
+ var this$1 = this;
+
+ var input = this, cm = this.cm;
+ this.createField(display);
+ var te = this.textarea;
+
+ display.wrapper.insertBefore(this.wrapper, display.wrapper.firstChild);
+
+ // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
+ if (ios) { te.style.width = "0px"; }
+
+ on(te, "input", function () {
+ if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; }
+ input.poll();
+ });
+
+ on(te, "paste", function (e) {
+ if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
+
+ cm.state.pasteIncoming = +new Date;
+ input.fastPoll();
+ });
+
+ function prepareCopyCut(e) {
+ if (signalDOMEvent(cm, e)) { return }
+ if (cm.somethingSelected()) {
+ setLastCopied({lineWise: false, text: cm.getSelections()});
+ } else if (!cm.options.lineWiseCopyCut) {
+ return
+ } else {
+ var ranges = copyableRanges(cm);
+ setLastCopied({lineWise: true, text: ranges.text});
+ if (e.type == "cut") {
+ cm.setSelections(ranges.ranges, null, sel_dontScroll);
+ } else {
+ input.prevInput = "";
+ te.value = ranges.text.join("\n");
+ selectInput(te);
+ }
+ }
+ if (e.type == "cut") { cm.state.cutIncoming = +new Date; }
+ }
+ on(te, "cut", prepareCopyCut);
+ on(te, "copy", prepareCopyCut);
+
+ on(display.scroller, "paste", function (e) {
+ if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
+ if (!te.dispatchEvent) {
+ cm.state.pasteIncoming = +new Date;
+ input.focus();
+ return
+ }
+
+ // Pass the `paste` event to the textarea so it's handled by its event listener.
+ var event = new Event("paste");
+ event.clipboardData = e.clipboardData;
+ te.dispatchEvent(event);
+ });
+
+ // Prevent normal selection in the editor (we handle our own)
+ on(display.lineSpace, "selectstart", function (e) {
+ if (!eventInWidget(display, e)) { e_preventDefault(e); }
+ });
+
+ on(te, "compositionstart", function () {
+ var start = cm.getCursor("from");
+ if (input.composing) { input.composing.range.clear(); }
+ input.composing = {
+ start: start,
+ range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
+ };
+ });
+ on(te, "compositionend", function () {
+ if (input.composing) {
+ input.poll();
+ input.composing.range.clear();
+ input.composing = null;
+ }
+ });
+ };
+
+ TextareaInput.prototype.createField = function (_display) {
+ // Wraps and hides input textarea
+ this.wrapper = hiddenTextarea();
+ // The semihidden textarea that is focused when the editor is
+ // focused, and receives input.
+ this.textarea = this.wrapper.firstChild;
+ };
+
+ TextareaInput.prototype.screenReaderLabelChanged = function (label) {
+ // Label for screenreaders, accessibility
+ if(label) {
+ this.textarea.setAttribute('aria-label', label);
+ } else {
+ this.textarea.removeAttribute('aria-label');
+ }
+ };
+
+ TextareaInput.prototype.prepareSelection = function () {
+ // Redraw the selection and/or cursor
+ var cm = this.cm, display = cm.display, doc = cm.doc;
+ var result = prepareSelection(cm);
+
+ // Move the hidden textarea near the cursor to prevent scrolling artifacts
+ if (cm.options.moveInputWithCursor) {
+ var headPos = cursorCoords(cm, doc.sel.primary().head, "div");
+ var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect();
+ result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
+ headPos.top + lineOff.top - wrapOff.top));
+ result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
+ headPos.left + lineOff.left - wrapOff.left));
+ }
+
+ return result
+ };
+
+ TextareaInput.prototype.showSelection = function (drawn) {
+ var cm = this.cm, display = cm.display;
+ removeChildrenAndAdd(display.cursorDiv, drawn.cursors);
+ removeChildrenAndAdd(display.selectionDiv, drawn.selection);
+ if (drawn.teTop != null) {
+ this.wrapper.style.top = drawn.teTop + "px";
+ this.wrapper.style.left = drawn.teLeft + "px";
+ }
+ };
+
+ // Reset the input to correspond to the selection (or to be empty,
+ // when not typing and nothing is selected)
+ TextareaInput.prototype.reset = function (typing) {
+ if (this.contextMenuPending || this.composing) { return }
+ var cm = this.cm;
+ if (cm.somethingSelected()) {
+ this.prevInput = "";
+ var content = cm.getSelection();
+ this.textarea.value = content;
+ if (cm.state.focused) { selectInput(this.textarea); }
+ if (ie && ie_version >= 9) { this.hasSelection = content; }
+ } else if (!typing) {
+ this.prevInput = this.textarea.value = "";
+ if (ie && ie_version >= 9) { this.hasSelection = null; }
+ }
+ };
+
+ TextareaInput.prototype.getField = function () { return this.textarea };
+
+ TextareaInput.prototype.supportsTouch = function () { return false };
+
+ TextareaInput.prototype.focus = function () {
+ if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
+ try { this.textarea.focus(); }
+ catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
+ }
+ };
+
+ TextareaInput.prototype.blur = function () { this.textarea.blur(); };
+
+ TextareaInput.prototype.resetPosition = function () {
+ this.wrapper.style.top = this.wrapper.style.left = 0;
+ };
+
+ TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); };
+
+ // Poll for input changes, using the normal rate of polling. This
+ // runs as long as the editor is focused.
+ TextareaInput.prototype.slowPoll = function () {
+ var this$1 = this;
+
+ if (this.pollingFast) { return }
+ this.polling.set(this.cm.options.pollInterval, function () {
+ this$1.poll();
+ if (this$1.cm.state.focused) { this$1.slowPoll(); }
+ });
+ };
+
+ // When an event has just come in that is likely to add or change
+ // something in the input textarea, we poll faster, to ensure that
+ // the change appears on the screen quickly.
+ TextareaInput.prototype.fastPoll = function () {
+ var missed = false, input = this;
+ input.pollingFast = true;
+ function p() {
+ var changed = input.poll();
+ if (!changed && !missed) {missed = true; input.polling.set(60, p);}
+ else {input.pollingFast = false; input.slowPoll();}
+ }
+ input.polling.set(20, p);
+ };
+
+ // Read input from the textarea, and update the document to match.
+ // When something is selected, it is present in the textarea, and
+ // selected (unless it is huge, in which case a placeholder is
+ // used). When nothing is selected, the cursor sits after previously
+ // seen text (can be empty), which is stored in prevInput (we must
+ // not reset the textarea when typing, because that breaks IME).
+ TextareaInput.prototype.poll = function () {
+ var this$1 = this;
+
+ var cm = this.cm, input = this.textarea, prevInput = this.prevInput;
+ // Since this is called a *lot*, try to bail out as cheaply as
+ // possible when it is clear that nothing happened. hasSelection
+ // will be the case when there is a lot of text in the textarea,
+ // in which case reading its value would be expensive.
+ if (this.contextMenuPending || !cm.state.focused ||
+ (hasSelection(input) && !prevInput && !this.composing) ||
+ cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
+ { return false }
+
+ var text = input.value;
+ // If nothing changed, bail.
+ if (text == prevInput && !cm.somethingSelected()) { return false }
+ // Work around nonsensical selection resetting in IE9/10, and
+ // inexplicable appearance of private area unicode characters on
+ // some key combos in Mac (#2689).
+ if (ie && ie_version >= 9 && this.hasSelection === text ||
+ mac && /[\uf700-\uf7ff]/.test(text)) {
+ cm.display.input.reset();
+ return false
+ }
+
+ if (cm.doc.sel == cm.display.selForContextMenu) {
+ var first = text.charCodeAt(0);
+ if (first == 0x200b && !prevInput) { prevInput = "\u200b"; }
+ if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
+ }
+ // Find the part of the input that is actually new
+ var same = 0, l = Math.min(prevInput.length, text.length);
+ while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; }
+
+ runInOp(cm, function () {
+ applyTextInput(cm, text.slice(same), prevInput.length - same,
+ null, this$1.composing ? "*compose" : null);
+
+ // Don't leave long text in the textarea, since it makes further polling slow
+ if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; }
+ else { this$1.prevInput = text; }
+
+ if (this$1.composing) {
+ this$1.composing.range.clear();
+ this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
+ {className: "CodeMirror-composing"});
+ }
+ });
+ return true
+ };
+
+ TextareaInput.prototype.ensurePolled = function () {
+ if (this.pollingFast && this.poll()) { this.pollingFast = false; }
+ };
+
+ TextareaInput.prototype.onKeyPress = function () {
+ if (ie && ie_version >= 9) { this.hasSelection = null; }
+ this.fastPoll();
+ };
+
+ TextareaInput.prototype.onContextMenu = function (e) {
+ var input = this, cm = input.cm, display = cm.display, te = input.textarea;
+ if (input.contextMenuPending) { input.contextMenuPending(); }
+ var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop;
+ if (!pos || presto) { return } // Opera is difficult.
+
+ // Reset the current text selection only if the click is done outside of the selection
+ // and 'resetSelectionOnContextMenu' option is true.
+ var reset = cm.options.resetSelectionOnContextMenu;
+ if (reset && cm.doc.sel.contains(pos) == -1)
+ { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); }
+
+ var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText;
+ var wrapperBox = input.wrapper.offsetParent.getBoundingClientRect();
+ input.wrapper.style.cssText = "position: static";
+ te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);";
+ var oldScrollY;
+ if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712)
+ display.input.focus();
+ if (webkit) { window.scrollTo(null, oldScrollY); }
+ display.input.reset();
+ // Adds "Select all" to context menu in FF
+ if (!cm.somethingSelected()) { te.value = input.prevInput = " "; }
+ input.contextMenuPending = rehide;
+ display.selForContextMenu = cm.doc.sel;
+ clearTimeout(display.detectingSelectAll);
+
+ // Select-all will be greyed out if there's nothing to select, so
+ // this adds a zero-width space so that we can later check whether
+ // it got selected.
+ function prepareSelectAllHack() {
+ if (te.selectionStart != null) {
+ var selected = cm.somethingSelected();
+ var extval = "\u200b" + (selected ? te.value : "");
+ te.value = "\u21da"; // Used to catch context-menu undo
+ te.value = extval;
+ input.prevInput = selected ? "" : "\u200b";
+ te.selectionStart = 1; te.selectionEnd = extval.length;
+ // Re-set this, in case some other handler touched the
+ // selection in the meantime.
+ display.selForContextMenu = cm.doc.sel;
+ }
+ }
+ function rehide() {
+ if (input.contextMenuPending != rehide) { return }
+ input.contextMenuPending = false;
+ input.wrapper.style.cssText = oldWrapperCSS;
+ te.style.cssText = oldCSS;
+ if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); }
+
+ // Try to detect the user choosing select-all
+ if (te.selectionStart != null) {
+ if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); }
+ var i = 0, poll = function () {
+ if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
+ te.selectionEnd > 0 && input.prevInput == "\u200b") {
+ operation(cm, selectAll)(cm);
+ } else if (i++ < 10) {
+ display.detectingSelectAll = setTimeout(poll, 500);
+ } else {
+ display.selForContextMenu = null;
+ display.input.reset();
+ }
+ };
+ display.detectingSelectAll = setTimeout(poll, 200);
+ }
+ }
+
+ if (ie && ie_version >= 9) { prepareSelectAllHack(); }
+ if (captureRightClick) {
+ e_stop(e);
+ var mouseup = function () {
+ off(window, "mouseup", mouseup);
+ setTimeout(rehide, 20);
+ };
+ on(window, "mouseup", mouseup);
+ } else {
+ setTimeout(rehide, 50);
+ }
+ };
+
+ TextareaInput.prototype.readOnlyChanged = function (val) {
+ if (!val) { this.reset(); }
+ this.textarea.disabled = val == "nocursor";
+ this.textarea.readOnly = !!val;
+ };
+
+ TextareaInput.prototype.setUneditable = function () {};
+
+ TextareaInput.prototype.needsContentAttribute = false;
+
+ function fromTextArea(textarea, options) {
+ options = options ? copyObj(options) : {};
+ options.value = textarea.value;
+ if (!options.tabindex && textarea.tabIndex)
+ { options.tabindex = textarea.tabIndex; }
+ if (!options.placeholder && textarea.placeholder)
+ { options.placeholder = textarea.placeholder; }
+ // Set autofocus to true if this textarea is focused, or if it has
+ // autofocus and no other element is focused.
+ if (options.autofocus == null) {
+ var hasFocus = activeElt();
+ options.autofocus = hasFocus == textarea ||
+ textarea.getAttribute("autofocus") != null && hasFocus == document.body;
+ }
+
+ function save() {textarea.value = cm.getValue();}
+
+ var realSubmit;
+ if (textarea.form) {
+ on(textarea.form, "submit", save);
+ // Deplorable hack to make the submit method do the right thing.
+ if (!options.leaveSubmitMethodAlone) {
+ var form = textarea.form;
+ realSubmit = form.submit;
+ try {
+ var wrappedSubmit = form.submit = function () {
+ save();
+ form.submit = realSubmit;
+ form.submit();
+ form.submit = wrappedSubmit;
+ };
+ } catch(e) {}
+ }
+ }
+
+ options.finishInit = function (cm) {
+ cm.save = save;
+ cm.getTextArea = function () { return textarea; };
+ cm.toTextArea = function () {
+ cm.toTextArea = isNaN; // Prevent this from being ran twice
+ save();
+ textarea.parentNode.removeChild(cm.getWrapperElement());
+ textarea.style.display = "";
+ if (textarea.form) {
+ off(textarea.form, "submit", save);
+ if (!options.leaveSubmitMethodAlone && typeof textarea.form.submit == "function")
+ { textarea.form.submit = realSubmit; }
+ }
+ };
+ };
+
+ textarea.style.display = "none";
+ var cm = CodeMirror(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); },
+ options);
+ return cm
+ }
+
+ function addLegacyProps(CodeMirror) {
+ CodeMirror.off = off;
+ CodeMirror.on = on;
+ CodeMirror.wheelEventPixels = wheelEventPixels;
+ CodeMirror.Doc = Doc;
+ CodeMirror.splitLines = splitLinesAuto;
+ CodeMirror.countColumn = countColumn;
+ CodeMirror.findColumn = findColumn;
+ CodeMirror.isWordChar = isWordCharBasic;
+ CodeMirror.Pass = Pass;
+ CodeMirror.signal = signal;
+ CodeMirror.Line = Line;
+ CodeMirror.changeEnd = changeEnd;
+ CodeMirror.scrollbarModel = scrollbarModel;
+ CodeMirror.Pos = Pos;
+ CodeMirror.cmpPos = cmp;
+ CodeMirror.modes = modes;
+ CodeMirror.mimeModes = mimeModes;
+ CodeMirror.resolveMode = resolveMode;
+ CodeMirror.getMode = getMode;
+ CodeMirror.modeExtensions = modeExtensions;
+ CodeMirror.extendMode = extendMode;
+ CodeMirror.copyState = copyState;
+ CodeMirror.startState = startState;
+ CodeMirror.innerMode = innerMode;
+ CodeMirror.commands = commands;
+ CodeMirror.keyMap = keyMap;
+ CodeMirror.keyName = keyName;
+ CodeMirror.isModifierKey = isModifierKey;
+ CodeMirror.lookupKey = lookupKey;
+ CodeMirror.normalizeKeyMap = normalizeKeyMap;
+ CodeMirror.StringStream = StringStream;
+ CodeMirror.SharedTextMarker = SharedTextMarker;
+ CodeMirror.TextMarker = TextMarker;
+ CodeMirror.LineWidget = LineWidget;
+ CodeMirror.e_preventDefault = e_preventDefault;
+ CodeMirror.e_stopPropagation = e_stopPropagation;
+ CodeMirror.e_stop = e_stop;
+ CodeMirror.addClass = addClass;
+ CodeMirror.contains = contains;
+ CodeMirror.rmClass = rmClass;
+ CodeMirror.keyNames = keyNames;
+ }
+
+ // EDITOR CONSTRUCTOR
+
+ defineOptions(CodeMirror);
+
+ addEditorMethods(CodeMirror);
+
+ // Set up methods on CodeMirror's prototype to redirect to the editor's document.
+ var dontDelegate = "iter insert remove copy getEditor constructor".split(" ");
+ for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0)
+ { CodeMirror.prototype[prop] = (function(method) {
+ return function() {return method.apply(this.doc, arguments)}
+ })(Doc.prototype[prop]); } }
+
+ eventMixin(Doc);
+ CodeMirror.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput};
+
+ // Extra arguments are stored as the mode's dependencies, which is
+ // used by (legacy) mechanisms like loadmode.js to automatically
+ // load a mode. (Preferred mechanism is the require/define calls.)
+ CodeMirror.defineMode = function(name/*, mode, …*/) {
+ if (!CodeMirror.defaults.mode && name != "null") { CodeMirror.defaults.mode = name; }
+ defineMode.apply(this, arguments);
+ };
+
+ CodeMirror.defineMIME = defineMIME;
+
+ // Minimal default mode.
+ CodeMirror.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); });
+ CodeMirror.defineMIME("text/plain", "null");
+
+ // EXTENSIONS
+
+ CodeMirror.defineExtension = function (name, func) {
+ CodeMirror.prototype[name] = func;
+ };
+ CodeMirror.defineDocExtension = function (name, func) {
+ Doc.prototype[name] = func;
+ };
+
+ CodeMirror.fromTextArea = fromTextArea;
+
+ addLegacyProps(CodeMirror);
+
+ CodeMirror.version = "5.58.1";
+
+ return CodeMirror;
+
+})));
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/clike/clike.js b/devtools/client/shared/sourceeditor/codemirror/mode/clike/clike.js
new file mode 100644
index 0000000000..b3f099149f
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/clike/clike.js
@@ -0,0 +1,889 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+function Context(indented, column, type, info, align, prev) {
+ this.indented = indented;
+ this.column = column;
+ this.type = type;
+ this.info = info;
+ this.align = align;
+ this.prev = prev;
+}
+function pushContext(state, col, type, info) {
+ var indent = state.indented;
+ if (state.context && state.context.type == "statement" && type != "statement")
+ indent = state.context.indented;
+ return state.context = new Context(indent, col, type, info, null, state.context);
+}
+function popContext(state) {
+ var t = state.context.type;
+ if (t == ")" || t == "]" || t == "}")
+ state.indented = state.context.indented;
+ return state.context = state.context.prev;
+}
+
+function typeBefore(stream, state, pos) {
+ if (state.prevToken == "variable" || state.prevToken == "type") return true;
+ if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true;
+ if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;
+}
+
+function isTopScope(context) {
+ for (;;) {
+ if (!context || context.type == "top") return true;
+ if (context.type == "}" && context.prev.info != "namespace") return false;
+ context = context.prev;
+ }
+}
+
+CodeMirror.defineMode("clike", function(config, parserConfig) {
+ var indentUnit = config.indentUnit,
+ statementIndentUnit = parserConfig.statementIndentUnit || indentUnit,
+ dontAlignCalls = parserConfig.dontAlignCalls,
+ keywords = parserConfig.keywords || {},
+ types = parserConfig.types || {},
+ builtin = parserConfig.builtin || {},
+ blockKeywords = parserConfig.blockKeywords || {},
+ defKeywords = parserConfig.defKeywords || {},
+ atoms = parserConfig.atoms || {},
+ hooks = parserConfig.hooks || {},
+ multiLineStrings = parserConfig.multiLineStrings,
+ indentStatements = parserConfig.indentStatements !== false,
+ indentSwitch = parserConfig.indentSwitch !== false,
+ namespaceSeparator = parserConfig.namespaceSeparator,
+ isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/,
+ numberStart = parserConfig.numberStart || /[\d\.]/,
+ number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
+ isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
+ isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/,
+ // An optional function that takes a {string} token and returns true if it
+ // should be treated as a builtin.
+ isReservedIdentifier = parserConfig.isReservedIdentifier || false;
+
+ var curPunc, isDefKeyword;
+
+ function tokenBase(stream, state) {
+ var ch = stream.next();
+ if (hooks[ch]) {
+ var result = hooks[ch](stream, state);
+ if (result !== false) return result;
+ }
+ if (ch == '"' || ch == "'") {
+ state.tokenize = tokenString(ch);
+ return state.tokenize(stream, state);
+ }
+ if (isPunctuationChar.test(ch)) {
+ curPunc = ch;
+ return null;
+ }
+ if (numberStart.test(ch)) {
+ stream.backUp(1)
+ if (stream.match(number)) return "number"
+ stream.next()
+ }
+ if (ch == "/") {
+ if (stream.eat("*")) {
+ state.tokenize = tokenComment;
+ return tokenComment(stream, state);
+ }
+ if (stream.eat("/")) {
+ stream.skipToEnd();
+ return "comment";
+ }
+ }
+ if (isOperatorChar.test(ch)) {
+ while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
+ return "operator";
+ }
+ stream.eatWhile(isIdentifierChar);
+ if (namespaceSeparator) while (stream.match(namespaceSeparator))
+ stream.eatWhile(isIdentifierChar);
+
+ var cur = stream.current();
+ if (contains(keywords, cur)) {
+ if (contains(blockKeywords, cur)) curPunc = "newstatement";
+ if (contains(defKeywords, cur)) isDefKeyword = true;
+ return "keyword";
+ }
+ if (contains(types, cur)) return "type";
+ if (contains(builtin, cur)
+ || (isReservedIdentifier && isReservedIdentifier(cur))) {
+ if (contains(blockKeywords, cur)) curPunc = "newstatement";
+ return "builtin";
+ }
+ if (contains(atoms, cur)) return "atom";
+ return "variable";
+ }
+
+ function tokenString(quote) {
+ return function(stream, state) {
+ var escaped = false, next, end = false;
+ while ((next = stream.next()) != null) {
+ if (next == quote && !escaped) {end = true; break;}
+ escaped = !escaped && next == "\\";
+ }
+ if (end || !(escaped || multiLineStrings))
+ state.tokenize = null;
+ return "string";
+ };
+ }
+
+ function tokenComment(stream, state) {
+ var maybeEnd = false, ch;
+ while (ch = stream.next()) {
+ if (ch == "/" && maybeEnd) {
+ state.tokenize = null;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return "comment";
+ }
+
+ function maybeEOL(stream, state) {
+ if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context))
+ state.typeAtEndOfLine = typeBefore(stream, state, stream.pos)
+ }
+
+ // Interface
+
+ return {
+ startState: function(basecolumn) {
+ return {
+ tokenize: null,
+ context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false),
+ indented: 0,
+ startOfLine: true,
+ prevToken: null
+ };
+ },
+
+ token: function(stream, state) {
+ var ctx = state.context;
+ if (stream.sol()) {
+ if (ctx.align == null) ctx.align = false;
+ state.indented = stream.indentation();
+ state.startOfLine = true;
+ }
+ if (stream.eatSpace()) { maybeEOL(stream, state); return null; }
+ curPunc = isDefKeyword = null;
+ var style = (state.tokenize || tokenBase)(stream, state);
+ if (style == "comment" || style == "meta") return style;
+ if (ctx.align == null) ctx.align = true;
+
+ if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false)))
+ while (state.context.type == "statement") popContext(state);
+ else if (curPunc == "{") pushContext(state, stream.column(), "}");
+ else if (curPunc == "[") pushContext(state, stream.column(), "]");
+ else if (curPunc == "(") pushContext(state, stream.column(), ")");
+ else if (curPunc == "}") {
+ while (ctx.type == "statement") ctx = popContext(state);
+ if (ctx.type == "}") ctx = popContext(state);
+ while (ctx.type == "statement") ctx = popContext(state);
+ }
+ else if (curPunc == ctx.type) popContext(state);
+ else if (indentStatements &&
+ (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") ||
+ (ctx.type == "statement" && curPunc == "newstatement"))) {
+ pushContext(state, stream.column(), "statement", stream.current());
+ }
+
+ if (style == "variable" &&
+ ((state.prevToken == "def" ||
+ (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) &&
+ isTopScope(state.context) && stream.match(/^\s*\(/, false)))))
+ style = "def";
+
+ if (hooks.token) {
+ var result = hooks.token(stream, state, style);
+ if (result !== undefined) style = result;
+ }
+
+ if (style == "def" && parserConfig.styleDefs === false) style = "variable";
+
+ state.startOfLine = false;
+ state.prevToken = isDefKeyword ? "def" : style || curPunc;
+ maybeEOL(stream, state);
+ return style;
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
+ var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
+ var closing = firstChar == ctx.type;
+ if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
+ if (parserConfig.dontIndentStatements)
+ while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
+ ctx = ctx.prev
+ if (hooks.indent) {
+ var hook = hooks.indent(state, ctx, textAfter, indentUnit);
+ if (typeof hook == "number") return hook
+ }
+ var switchBlock = ctx.prev && ctx.prev.info == "switch";
+ if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
+ while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
+ return ctx.indented
+ }
+ if (ctx.type == "statement")
+ return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit);
+ if (ctx.align && (!dontAlignCalls || ctx.type != ")"))
+ return ctx.column + (closing ? 0 : 1);
+ if (ctx.type == ")" && !closing)
+ return ctx.indented + statementIndentUnit;
+
+ return ctx.indented + (closing ? 0 : indentUnit) +
+ (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0);
+ },
+
+ electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ blockCommentContinue: " * ",
+ lineComment: "//",
+ fold: "brace"
+ };
+});
+
+ function words(str) {
+ var obj = {}, words = str.split(" ");
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+ return obj;
+ }
+ function contains(words, word) {
+ if (typeof words === "function") {
+ return words(word);
+ } else {
+ return words.propertyIsEnumerable(word);
+ }
+ }
+ var cKeywords = "auto if break case register continue return default do sizeof " +
+ "static else struct switch extern typedef union for goto while enum const " +
+ "volatile inline restrict asm fortran";
+
+ // Do not use this. Use the cTypes function below. This is global just to avoid
+ // excessive calls when cTypes is being called multiple times during a parse.
+ var basicCTypes = words("int long char short double float unsigned signed " +
+ "void bool");
+
+ // Do not use this. Use the objCTypes function below. This is global just to avoid
+ // excessive calls when objCTypes is being called multiple times during a parse.
+ var basicObjCTypes = words("SEL instancetype id Class Protocol BOOL");
+
+ // Returns true if identifier is a "C" type.
+ // C type is defined as those that are reserved by the compiler (basicTypes),
+ // and those that end in _t (Reserved by POSIX for types)
+ // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html
+ function cTypes(identifier) {
+ return contains(basicCTypes, identifier) || /.+_t$/.test(identifier);
+ }
+
+ // Returns true if identifier is a "Objective C" type.
+ function objCTypes(identifier) {
+ return cTypes(identifier) || contains(basicObjCTypes, identifier);
+ }
+
+ var cBlockKeywords = "case do else for if switch while struct enum union";
+ var cDefKeywords = "struct enum union";
+
+ function cppHook(stream, state) {
+ if (!state.startOfLine) return false
+ for (var ch, next = null; ch = stream.peek();) {
+ if (ch == "\\" && stream.match(/^.$/)) {
+ next = cppHook
+ break
+ } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) {
+ break
+ }
+ stream.next()
+ }
+ state.tokenize = next
+ return "meta"
+ }
+
+ function pointerHook(_stream, state) {
+ if (state.prevToken == "type") return "type";
+ return false;
+ }
+
+ // For C and C++ (and ObjC): identifiers starting with __
+ // or _ followed by a capital letter are reserved for the compiler.
+ function cIsReservedIdentifier(token) {
+ if (!token || token.length < 2) return false;
+ if (token[0] != '_') return false;
+ return (token[1] == '_') || (token[1] !== token[1].toLowerCase());
+ }
+
+ function cpp14Literal(stream) {
+ stream.eatWhile(/[\w\.']/);
+ return "number";
+ }
+
+ function cpp11StringHook(stream, state) {
+ stream.backUp(1);
+ // Raw strings.
+ if (stream.match(/(R|u8R|uR|UR|LR)/)) {
+ var match = stream.match(/"([^\s\\()]{0,16})\(/);
+ if (!match) {
+ return false;
+ }
+ state.cpp11RawStringDelim = match[1];
+ state.tokenize = tokenRawString;
+ return tokenRawString(stream, state);
+ }
+ // Unicode strings/chars.
+ if (stream.match(/(u8|u|U|L)/)) {
+ if (stream.match(/["']/, /* eat */ false)) {
+ return "string";
+ }
+ return false;
+ }
+ // Ignore this hook.
+ stream.next();
+ return false;
+ }
+
+ function cppLooksLikeConstructor(word) {
+ var lastTwo = /(\w+)::~?(\w+)$/.exec(word);
+ return lastTwo && lastTwo[1] == lastTwo[2];
+ }
+
+ // C#-style strings where "" escapes a quote.
+ function tokenAtString(stream, state) {
+ var next;
+ while ((next = stream.next()) != null) {
+ if (next == '"' && !stream.eat('"')) {
+ state.tokenize = null;
+ break;
+ }
+ }
+ return "string";
+ }
+
+ // C++11 raw string literal is <prefix>"<delim>( anything )<delim>", where
+ // <delim> can be a string up to 16 characters long.
+ function tokenRawString(stream, state) {
+ // Escape characters that have special regex meanings.
+ var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&');
+ var match = stream.match(new RegExp(".*?\\)" + delim + '"'));
+ if (match)
+ state.tokenize = null;
+ else
+ stream.skipToEnd();
+ return "string";
+ }
+
+ function def(mimes, mode) {
+ if (typeof mimes == "string") mimes = [mimes];
+ var words = [];
+ function add(obj) {
+ if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop))
+ words.push(prop);
+ }
+ add(mode.keywords);
+ add(mode.types);
+ add(mode.builtin);
+ add(mode.atoms);
+ if (words.length) {
+ mode.helperType = mimes[0];
+ CodeMirror.registerHelper("hintWords", mimes[0], words);
+ }
+
+ for (var i = 0; i < mimes.length; ++i)
+ CodeMirror.defineMIME(mimes[i], mode);
+ }
+
+ def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
+ name: "clike",
+ keywords: words(cKeywords),
+ types: cTypes,
+ blockKeywords: words(cBlockKeywords),
+ defKeywords: words(cDefKeywords),
+ typeFirstDefinitions: true,
+ atoms: words("NULL true false"),
+ isReservedIdentifier: cIsReservedIdentifier,
+ hooks: {
+ "#": cppHook,
+ "*": pointerHook,
+ },
+ modeProps: {fold: ["brace", "include"]}
+ });
+
+ def(["text/x-c++src", "text/x-c++hdr"], {
+ name: "clike",
+ // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20.
+ keywords: words(cKeywords + "alignas alignof and and_eq audit axiom bitand bitor catch " +
+ "class compl concept constexpr const_cast decltype delete dynamic_cast " +
+ "explicit export final friend import module mutable namespace new noexcept " +
+ "not not_eq operator or or_eq override private protected public " +
+ "reinterpret_cast requires static_assert static_cast template this " +
+ "thread_local throw try typeid typename using virtual xor xor_eq"),
+ types: cTypes,
+ blockKeywords: words(cBlockKeywords + " class try catch"),
+ defKeywords: words(cDefKeywords + " class namespace"),
+ typeFirstDefinitions: true,
+ atoms: words("true false NULL nullptr"),
+ dontIndentStatements: /^template$/,
+ isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
+ isReservedIdentifier: cIsReservedIdentifier,
+ hooks: {
+ "#": cppHook,
+ "*": pointerHook,
+ "u": cpp11StringHook,
+ "U": cpp11StringHook,
+ "L": cpp11StringHook,
+ "R": cpp11StringHook,
+ "0": cpp14Literal,
+ "1": cpp14Literal,
+ "2": cpp14Literal,
+ "3": cpp14Literal,
+ "4": cpp14Literal,
+ "5": cpp14Literal,
+ "6": cpp14Literal,
+ "7": cpp14Literal,
+ "8": cpp14Literal,
+ "9": cpp14Literal,
+ token: function(stream, state, style) {
+ if (style == "variable" && stream.peek() == "(" &&
+ (state.prevToken == ";" || state.prevToken == null ||
+ state.prevToken == "}") &&
+ cppLooksLikeConstructor(stream.current()))
+ return "def";
+ }
+ },
+ namespaceSeparator: "::",
+ modeProps: {fold: ["brace", "include"]}
+ });
+
+ def("text/x-java", {
+ name: "clike",
+ keywords: words("abstract assert break case catch class const continue default " +
+ "do else enum extends final finally for goto if implements import " +
+ "instanceof interface native new package private protected public " +
+ "return static strictfp super switch synchronized this throw throws transient " +
+ "try volatile while @interface"),
+ types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
+ "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
+ blockKeywords: words("catch class do else finally for if switch try while"),
+ defKeywords: words("class interface enum @interface"),
+ typeFirstDefinitions: true,
+ atoms: words("true false null"),
+ number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+ hooks: {
+ "@": function(stream) {
+ // Don't match the @interface keyword.
+ if (stream.match('interface', false)) return false;
+
+ stream.eatWhile(/[\w\$_]/);
+ return "meta";
+ }
+ },
+ modeProps: {fold: ["brace", "import"]}
+ });
+
+ def("text/x-csharp", {
+ name: "clike",
+ keywords: words("abstract as async await base break case catch checked class const continue" +
+ " default delegate do else enum event explicit extern finally fixed for" +
+ " foreach goto if implicit in interface internal is lock namespace new" +
+ " operator out override params private protected public readonly ref return sealed" +
+ " sizeof stackalloc static struct switch this throw try typeof unchecked" +
+ " unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
+ " global group into join let orderby partial remove select set value var yield"),
+ types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
+ " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
+ " UInt64 bool byte char decimal double short int long object" +
+ " sbyte float string ushort uint ulong"),
+ blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
+ defKeywords: words("class interface namespace struct var"),
+ typeFirstDefinitions: true,
+ atoms: words("true false null"),
+ hooks: {
+ "@": function(stream, state) {
+ if (stream.eat('"')) {
+ state.tokenize = tokenAtString;
+ return tokenAtString(stream, state);
+ }
+ stream.eatWhile(/[\w\$_]/);
+ return "meta";
+ }
+ }
+ });
+
+ function tokenTripleString(stream, state) {
+ var escaped = false;
+ while (!stream.eol()) {
+ if (!escaped && stream.match('"""')) {
+ state.tokenize = null;
+ break;
+ }
+ escaped = stream.next() == "\\" && !escaped;
+ }
+ return "string";
+ }
+
+ function tokenNestedComment(depth) {
+ return function (stream, state) {
+ var ch
+ while (ch = stream.next()) {
+ if (ch == "*" && stream.eat("/")) {
+ if (depth == 1) {
+ state.tokenize = null
+ break
+ } else {
+ state.tokenize = tokenNestedComment(depth - 1)
+ return state.tokenize(stream, state)
+ }
+ } else if (ch == "/" && stream.eat("*")) {
+ state.tokenize = tokenNestedComment(depth + 1)
+ return state.tokenize(stream, state)
+ }
+ }
+ return "comment"
+ }
+ }
+
+ def("text/x-scala", {
+ name: "clike",
+ keywords: words(
+ /* scala */
+ "abstract case catch class def do else extends final finally for forSome if " +
+ "implicit import lazy match new null object override package private protected return " +
+ "sealed super this throw trait try type val var while with yield _ " +
+
+ /* package scala */
+ "assert assume require print println printf readLine readBoolean readByte readShort " +
+ "readChar readInt readLong readFloat readDouble"
+ ),
+ types: words(
+ "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
+ "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " +
+ "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " +
+ "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " +
+ "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " +
+
+ /* package java.lang */
+ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+ "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+ "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+ "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+ ),
+ multiLineStrings: true,
+ blockKeywords: words("catch class enum do else finally for forSome if match switch try while"),
+ defKeywords: words("class enum def object package trait type val var"),
+ atoms: words("true false null"),
+ indentStatements: false,
+ indentSwitch: false,
+ isOperatorChar: /[+\-*&%=<>!?|\/#:@]/,
+ hooks: {
+ "@": function(stream) {
+ stream.eatWhile(/[\w\$_]/);
+ return "meta";
+ },
+ '"': function(stream, state) {
+ if (!stream.match('""')) return false;
+ state.tokenize = tokenTripleString;
+ return state.tokenize(stream, state);
+ },
+ "'": function(stream) {
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+ return "atom";
+ },
+ "=": function(stream, state) {
+ var cx = state.context
+ if (cx.type == "}" && cx.align && stream.eat(">")) {
+ state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev)
+ return "operator"
+ } else {
+ return false
+ }
+ },
+
+ "/": function(stream, state) {
+ if (!stream.eat("*")) return false
+ state.tokenize = tokenNestedComment(1)
+ return state.tokenize(stream, state)
+ }
+ },
+ modeProps: {closeBrackets: {pairs: '()[]{}""', triples: '"'}}
+ });
+
+ function tokenKotlinString(tripleString){
+ return function (stream, state) {
+ var escaped = false, next, end = false;
+ while (!stream.eol()) {
+ if (!tripleString && !escaped && stream.match('"') ) {end = true; break;}
+ if (tripleString && stream.match('"""')) {end = true; break;}
+ next = stream.next();
+ if(!escaped && next == "$" && stream.match('{'))
+ stream.skipTo("}");
+ escaped = !escaped && next == "\\" && !tripleString;
+ }
+ if (end || !tripleString)
+ state.tokenize = null;
+ return "string";
+ }
+ }
+
+ def("text/x-kotlin", {
+ name: "clike",
+ keywords: words(
+ /*keywords*/
+ "package as typealias class interface this super val operator " +
+ "var fun for is in This throw return annotation " +
+ "break continue object if else while do try when !in !is as? " +
+
+ /*soft keywords*/
+ "file import where by get set abstract enum open inner override private public internal " +
+ "protected catch finally out final vararg reified dynamic companion constructor init " +
+ "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
+ "external annotation crossinline const operator infix suspend actual expect setparam"
+ ),
+ types: words(
+ /* package java.lang */
+ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
+ "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
+ "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
+ "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
+ "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
+ "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
+ ),
+ intendSwitch: false,
+ indentStatements: false,
+ multiLineStrings: true,
+ number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
+ blockKeywords: words("catch class do else finally for if where try while enum"),
+ defKeywords: words("class val var object interface fun"),
+ atoms: words("true false null this"),
+ hooks: {
+ "@": function(stream) {
+ stream.eatWhile(/[\w\$_]/);
+ return "meta";
+ },
+ '*': function(_stream, state) {
+ return state.prevToken == '.' ? 'variable' : 'operator';
+ },
+ '"': function(stream, state) {
+ state.tokenize = tokenKotlinString(stream.match('""'));
+ return state.tokenize(stream, state);
+ },
+ "/": function(stream, state) {
+ if (!stream.eat("*")) return false;
+ state.tokenize = tokenNestedComment(1);
+ return state.tokenize(stream, state)
+ },
+ indent: function(state, ctx, textAfter, indentUnit) {
+ var firstChar = textAfter && textAfter.charAt(0);
+ if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "")
+ return state.indented;
+ if ((state.prevToken == "operator" && textAfter != "}" && state.context.type != "}") ||
+ state.prevToken == "variable" && firstChar == "." ||
+ (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".")
+ return indentUnit * 2 + ctx.indented;
+ if (ctx.align && ctx.type == "}")
+ return ctx.indented + (state.context.type == (textAfter || "").charAt(0) ? 0 : indentUnit);
+ }
+ },
+ modeProps: {closeBrackets: {triples: '"'}}
+ });
+
+ def(["x-shader/x-vertex", "x-shader/x-fragment"], {
+ name: "clike",
+ keywords: words("sampler1D sampler2D sampler3D samplerCube " +
+ "sampler1DShadow sampler2DShadow " +
+ "const attribute uniform varying " +
+ "break continue discard return " +
+ "for while do if else struct " +
+ "in out inout"),
+ types: words("float int bool void " +
+ "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " +
+ "mat2 mat3 mat4"),
+ blockKeywords: words("for while do if else struct"),
+ builtin: words("radians degrees sin cos tan asin acos atan " +
+ "pow exp log exp2 sqrt inversesqrt " +
+ "abs sign floor ceil fract mod min max clamp mix step smoothstep " +
+ "length distance dot cross normalize ftransform faceforward " +
+ "reflect refract matrixCompMult " +
+ "lessThan lessThanEqual greaterThan greaterThanEqual " +
+ "equal notEqual any all not " +
+ "texture1D texture1DProj texture1DLod texture1DProjLod " +
+ "texture2D texture2DProj texture2DLod texture2DProjLod " +
+ "texture3D texture3DProj texture3DLod texture3DProjLod " +
+ "textureCube textureCubeLod " +
+ "shadow1D shadow2D shadow1DProj shadow2DProj " +
+ "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " +
+ "dFdx dFdy fwidth " +
+ "noise1 noise2 noise3 noise4"),
+ atoms: words("true false " +
+ "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " +
+ "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " +
+ "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " +
+ "gl_FogCoord gl_PointCoord " +
+ "gl_Position gl_PointSize gl_ClipVertex " +
+ "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " +
+ "gl_TexCoord gl_FogFragCoord " +
+ "gl_FragCoord gl_FrontFacing " +
+ "gl_FragData gl_FragDepth " +
+ "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " +
+ "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " +
+ "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " +
+ "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " +
+ "gl_ProjectionMatrixInverseTranspose " +
+ "gl_ModelViewProjectionMatrixInverseTranspose " +
+ "gl_TextureMatrixInverseTranspose " +
+ "gl_NormalScale gl_DepthRange gl_ClipPlane " +
+ "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " +
+ "gl_FrontLightModelProduct gl_BackLightModelProduct " +
+ "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " +
+ "gl_FogParameters " +
+ "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " +
+ "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " +
+ "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " +
+ "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " +
+ "gl_MaxDrawBuffers"),
+ indentSwitch: false,
+ hooks: {"#": cppHook},
+ modeProps: {fold: ["brace", "include"]}
+ });
+
+ def("text/x-nesc", {
+ name: "clike",
+ keywords: words(cKeywords + " as atomic async call command component components configuration event generic " +
+ "implementation includes interface module new norace nx_struct nx_union post provides " +
+ "signal task uses abstract extends"),
+ types: cTypes,
+ blockKeywords: words(cBlockKeywords),
+ atoms: words("null true false"),
+ hooks: {"#": cppHook},
+ modeProps: {fold: ["brace", "include"]}
+ });
+
+ def("text/x-objectivec", {
+ name: "clike",
+ keywords: words(cKeywords + " bycopy byref in inout oneway out self super atomic nonatomic retain copy " +
+ "readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd " +
+ "@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class " +
+ "@public @package @private @protected @required @optional @try @catch @finally @import " +
+ "@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available"),
+ types: objCTypes,
+ builtin: words("FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION NS_RETURNS_RETAINED " +
+ "NS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER NS_DESIGNATED_INITIALIZER " +
+ "NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION NS_ASSUME_NONNULL_BEGIN " +
+ "NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT"),
+ blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized"),
+ defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class"),
+ dontIndentStatements: /^@.*$/,
+ typeFirstDefinitions: true,
+ atoms: words("YES NO NULL Nil nil true false nullptr"),
+ isReservedIdentifier: cIsReservedIdentifier,
+ hooks: {
+ "#": cppHook,
+ "*": pointerHook,
+ },
+ modeProps: {fold: ["brace", "include"]}
+ });
+
+ def("text/x-squirrel", {
+ name: "clike",
+ keywords: words("base break clone continue const default delete enum extends function in class" +
+ " foreach local resume return this throw typeof yield constructor instanceof static"),
+ types: cTypes,
+ blockKeywords: words("case catch class else for foreach if switch try while"),
+ defKeywords: words("function local class"),
+ typeFirstDefinitions: true,
+ atoms: words("true false null"),
+ hooks: {"#": cppHook},
+ modeProps: {fold: ["brace", "include"]}
+ });
+
+ // Ceylon Strings need to deal with interpolation
+ var stringTokenizer = null;
+ function tokenCeylonString(type) {
+ return function(stream, state) {
+ var escaped = false, next, end = false;
+ while (!stream.eol()) {
+ if (!escaped && stream.match('"') &&
+ (type == "single" || stream.match('""'))) {
+ end = true;
+ break;
+ }
+ if (!escaped && stream.match('``')) {
+ stringTokenizer = tokenCeylonString(type);
+ end = true;
+ break;
+ }
+ next = stream.next();
+ escaped = type == "single" && !escaped && next == "\\";
+ }
+ if (end)
+ state.tokenize = null;
+ return "string";
+ }
+ }
+
+ def("text/x-ceylon", {
+ name: "clike",
+ keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" +
+ " exists extends finally for function given if import in interface is let module new" +
+ " nonempty object of out outer package return satisfies super switch then this throw" +
+ " try value void while"),
+ types: function(word) {
+ // In Ceylon all identifiers that start with an uppercase are types
+ var first = word.charAt(0);
+ return (first === first.toUpperCase() && first !== first.toLowerCase());
+ },
+ blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"),
+ defKeywords: words("class dynamic function interface module object package value"),
+ builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" +
+ " native optional sealed see serializable shared suppressWarnings tagged throws variable"),
+ isPunctuationChar: /[\[\]{}\(\),;\:\.`]/,
+ isOperatorChar: /[+\-*&%=<>!?|^~:\/]/,
+ numberStart: /[\d#$]/,
+ number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i,
+ multiLineStrings: true,
+ typeFirstDefinitions: true,
+ atoms: words("true false null larger smaller equal empty finished"),
+ indentSwitch: false,
+ styleDefs: false,
+ hooks: {
+ "@": function(stream) {
+ stream.eatWhile(/[\w\$_]/);
+ return "meta";
+ },
+ '"': function(stream, state) {
+ state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single");
+ return state.tokenize(stream, state);
+ },
+ '`': function(stream, state) {
+ if (!stringTokenizer || !stream.match('`')) return false;
+ state.tokenize = stringTokenizer;
+ stringTokenizer = null;
+ return state.tokenize(stream, state);
+ },
+ "'": function(stream) {
+ stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+ return "atom";
+ },
+ token: function(_stream, state, style) {
+ if ((style == "variable" || style == "type") &&
+ state.prevToken == ".") {
+ return "variable-2";
+ }
+ }
+ },
+ modeProps: {
+ fold: ["brace", "import"],
+ closeBrackets: {triples: '"'}
+ }
+ });
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/clojure/clojure.js b/devtools/client/shared/sourceeditor/codemirror/mode/clojure/clojure.js
new file mode 100644
index 0000000000..5f14c55d19
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/clojure/clojure.js
@@ -0,0 +1,292 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports === "object" && typeof module === "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define === "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("clojure", function (options) {
+ var atoms = ["false", "nil", "true"];
+ var specialForms = [".", "catch", "def", "do", "if", "monitor-enter",
+ "monitor-exit", "new", "quote", "recur", "set!", "throw", "try", "var"];
+ var coreSymbols = ["*", "*'", "*1", "*2", "*3", "*agent*",
+ "*allow-unresolved-vars*", "*assert*", "*clojure-version*",
+ "*command-line-args*", "*compile-files*", "*compile-path*",
+ "*compiler-options*", "*data-readers*", "*default-data-reader-fn*", "*e",
+ "*err*", "*file*", "*flush-on-newline*", "*fn-loader*", "*in*",
+ "*math-context*", "*ns*", "*out*", "*print-dup*", "*print-length*",
+ "*print-level*", "*print-meta*", "*print-namespace-maps*",
+ "*print-readably*", "*read-eval*", "*reader-resolver*", "*source-path*",
+ "*suppress-read*", "*unchecked-math*", "*use-context-classloader*",
+ "*verbose-defrecords*", "*warn-on-reflection*", "+", "+'", "-", "-'",
+ "->", "->>", "->ArrayChunk", "->Eduction", "->Vec", "->VecNode",
+ "->VecSeq", "-cache-protocol-fn", "-reset-methods", "..", "/", "<", "<=",
+ "=", "==", ">", ">=", "EMPTY-NODE", "Inst", "StackTraceElement->vec",
+ "Throwable->map", "accessor", "aclone", "add-classpath", "add-watch",
+ "agent", "agent-error", "agent-errors", "aget", "alength", "alias",
+ "all-ns", "alter", "alter-meta!", "alter-var-root", "amap", "ancestors",
+ "and", "any?", "apply", "areduce", "array-map", "as->", "aset",
+ "aset-boolean", "aset-byte", "aset-char", "aset-double", "aset-float",
+ "aset-int", "aset-long", "aset-short", "assert", "assoc", "assoc!",
+ "assoc-in", "associative?", "atom", "await", "await-for", "await1",
+ "bases", "bean", "bigdec", "bigint", "biginteger", "binding", "bit-and",
+ "bit-and-not", "bit-clear", "bit-flip", "bit-not", "bit-or", "bit-set",
+ "bit-shift-left", "bit-shift-right", "bit-test", "bit-xor", "boolean",
+ "boolean-array", "boolean?", "booleans", "bound-fn", "bound-fn*",
+ "bound?", "bounded-count", "butlast", "byte", "byte-array", "bytes",
+ "bytes?", "case", "cast", "cat", "char", "char-array",
+ "char-escape-string", "char-name-string", "char?", "chars", "chunk",
+ "chunk-append", "chunk-buffer", "chunk-cons", "chunk-first", "chunk-next",
+ "chunk-rest", "chunked-seq?", "class", "class?", "clear-agent-errors",
+ "clojure-version", "coll?", "comment", "commute", "comp", "comparator",
+ "compare", "compare-and-set!", "compile", "complement", "completing",
+ "concat", "cond", "cond->", "cond->>", "condp", "conj", "conj!", "cons",
+ "constantly", "construct-proxy", "contains?", "count", "counted?",
+ "create-ns", "create-struct", "cycle", "dec", "dec'", "decimal?",
+ "declare", "dedupe", "default-data-readers", "definline", "definterface",
+ "defmacro", "defmethod", "defmulti", "defn", "defn-", "defonce",
+ "defprotocol", "defrecord", "defstruct", "deftype", "delay", "delay?",
+ "deliver", "denominator", "deref", "derive", "descendants", "destructure",
+ "disj", "disj!", "dissoc", "dissoc!", "distinct", "distinct?", "doall",
+ "dorun", "doseq", "dosync", "dotimes", "doto", "double", "double-array",
+ "double?", "doubles", "drop", "drop-last", "drop-while", "eduction",
+ "empty", "empty?", "ensure", "ensure-reduced", "enumeration-seq",
+ "error-handler", "error-mode", "eval", "even?", "every-pred", "every?",
+ "ex-data", "ex-info", "extend", "extend-protocol", "extend-type",
+ "extenders", "extends?", "false?", "ffirst", "file-seq", "filter",
+ "filterv", "find", "find-keyword", "find-ns", "find-protocol-impl",
+ "find-protocol-method", "find-var", "first", "flatten", "float",
+ "float-array", "float?", "floats", "flush", "fn", "fn?", "fnext", "fnil",
+ "for", "force", "format", "frequencies", "future", "future-call",
+ "future-cancel", "future-cancelled?", "future-done?", "future?",
+ "gen-class", "gen-interface", "gensym", "get", "get-in", "get-method",
+ "get-proxy-class", "get-thread-bindings", "get-validator", "group-by",
+ "halt-when", "hash", "hash-combine", "hash-map", "hash-ordered-coll",
+ "hash-set", "hash-unordered-coll", "ident?", "identical?", "identity",
+ "if-let", "if-not", "if-some", "ifn?", "import", "in-ns", "inc", "inc'",
+ "indexed?", "init-proxy", "inst-ms", "inst-ms*", "inst?", "instance?",
+ "int", "int-array", "int?", "integer?", "interleave", "intern",
+ "interpose", "into", "into-array", "ints", "io!", "isa?", "iterate",
+ "iterator-seq", "juxt", "keep", "keep-indexed", "key", "keys", "keyword",
+ "keyword?", "last", "lazy-cat", "lazy-seq", "let", "letfn", "line-seq",
+ "list", "list*", "list?", "load", "load-file", "load-reader",
+ "load-string", "loaded-libs", "locking", "long", "long-array", "longs",
+ "loop", "macroexpand", "macroexpand-1", "make-array", "make-hierarchy",
+ "map", "map-entry?", "map-indexed", "map?", "mapcat", "mapv", "max",
+ "max-key", "memfn", "memoize", "merge", "merge-with", "meta",
+ "method-sig", "methods", "min", "min-key", "mix-collection-hash", "mod",
+ "munge", "name", "namespace", "namespace-munge", "nat-int?", "neg-int?",
+ "neg?", "newline", "next", "nfirst", "nil?", "nnext", "not", "not-any?",
+ "not-empty", "not-every?", "not=", "ns", "ns-aliases", "ns-imports",
+ "ns-interns", "ns-map", "ns-name", "ns-publics", "ns-refers",
+ "ns-resolve", "ns-unalias", "ns-unmap", "nth", "nthnext", "nthrest",
+ "num", "number?", "numerator", "object-array", "odd?", "or", "parents",
+ "partial", "partition", "partition-all", "partition-by", "pcalls", "peek",
+ "persistent!", "pmap", "pop", "pop!", "pop-thread-bindings", "pos-int?",
+ "pos?", "pr", "pr-str", "prefer-method", "prefers",
+ "primitives-classnames", "print", "print-ctor", "print-dup",
+ "print-method", "print-simple", "print-str", "printf", "println",
+ "println-str", "prn", "prn-str", "promise", "proxy",
+ "proxy-call-with-super", "proxy-mappings", "proxy-name", "proxy-super",
+ "push-thread-bindings", "pvalues", "qualified-ident?",
+ "qualified-keyword?", "qualified-symbol?", "quot", "rand", "rand-int",
+ "rand-nth", "random-sample", "range", "ratio?", "rational?",
+ "rationalize", "re-find", "re-groups", "re-matcher", "re-matches",
+ "re-pattern", "re-seq", "read", "read-line", "read-string",
+ "reader-conditional", "reader-conditional?", "realized?", "record?",
+ "reduce", "reduce-kv", "reduced", "reduced?", "reductions", "ref",
+ "ref-history-count", "ref-max-history", "ref-min-history", "ref-set",
+ "refer", "refer-clojure", "reify", "release-pending-sends", "rem",
+ "remove", "remove-all-methods", "remove-method", "remove-ns",
+ "remove-watch", "repeat", "repeatedly", "replace", "replicate", "require",
+ "reset!", "reset-meta!", "reset-vals!", "resolve", "rest",
+ "restart-agent", "resultset-seq", "reverse", "reversible?", "rseq",
+ "rsubseq", "run!", "satisfies?", "second", "select-keys", "send",
+ "send-off", "send-via", "seq", "seq?", "seqable?", "seque", "sequence",
+ "sequential?", "set", "set-agent-send-executor!",
+ "set-agent-send-off-executor!", "set-error-handler!", "set-error-mode!",
+ "set-validator!", "set?", "short", "short-array", "shorts", "shuffle",
+ "shutdown-agents", "simple-ident?", "simple-keyword?", "simple-symbol?",
+ "slurp", "some", "some->", "some->>", "some-fn", "some?", "sort",
+ "sort-by", "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by",
+ "sorted?", "special-symbol?", "spit", "split-at", "split-with", "str",
+ "string?", "struct", "struct-map", "subs", "subseq", "subvec", "supers",
+ "swap!", "swap-vals!", "symbol", "symbol?", "sync", "tagged-literal",
+ "tagged-literal?", "take", "take-last", "take-nth", "take-while", "test",
+ "the-ns", "thread-bound?", "time", "to-array", "to-array-2d",
+ "trampoline", "transduce", "transient", "tree-seq", "true?", "type",
+ "unchecked-add", "unchecked-add-int", "unchecked-byte", "unchecked-char",
+ "unchecked-dec", "unchecked-dec-int", "unchecked-divide-int",
+ "unchecked-double", "unchecked-float", "unchecked-inc",
+ "unchecked-inc-int", "unchecked-int", "unchecked-long",
+ "unchecked-multiply", "unchecked-multiply-int", "unchecked-negate",
+ "unchecked-negate-int", "unchecked-remainder-int", "unchecked-short",
+ "unchecked-subtract", "unchecked-subtract-int", "underive", "unquote",
+ "unquote-splicing", "unreduced", "unsigned-bit-shift-right", "update",
+ "update-in", "update-proxy", "uri?", "use", "uuid?", "val", "vals",
+ "var-get", "var-set", "var?", "vary-meta", "vec", "vector", "vector-of",
+ "vector?", "volatile!", "volatile?", "vreset!", "vswap!", "when",
+ "when-first", "when-let", "when-not", "when-some", "while",
+ "with-bindings", "with-bindings*", "with-in-str", "with-loading-context",
+ "with-local-vars", "with-meta", "with-open", "with-out-str",
+ "with-precision", "with-redefs", "with-redefs-fn", "xml-seq", "zero?",
+ "zipmap"];
+ var haveBodyParameter = [
+ "->", "->>", "as->", "binding", "bound-fn", "case", "catch", "comment",
+ "cond", "cond->", "cond->>", "condp", "def", "definterface", "defmethod",
+ "defn", "defmacro", "defprotocol", "defrecord", "defstruct", "deftype",
+ "do", "doseq", "dotimes", "doto", "extend", "extend-protocol",
+ "extend-type", "fn", "for", "future", "if", "if-let", "if-not", "if-some",
+ "let", "letfn", "locking", "loop", "ns", "proxy", "reify", "struct-map",
+ "some->", "some->>", "try", "when", "when-first", "when-let", "when-not",
+ "when-some", "while", "with-bindings", "with-bindings*", "with-in-str",
+ "with-loading-context", "with-local-vars", "with-meta", "with-open",
+ "with-out-str", "with-precision", "with-redefs", "with-redefs-fn"];
+
+ CodeMirror.registerHelper("hintWords", "clojure",
+ [].concat(atoms, specialForms, coreSymbols));
+
+ var atom = createLookupMap(atoms);
+ var specialForm = createLookupMap(specialForms);
+ var coreSymbol = createLookupMap(coreSymbols);
+ var hasBodyParameter = createLookupMap(haveBodyParameter);
+ var delimiter = /^(?:[\\\[\]\s"(),;@^`{}~]|$)/;
+ var numberLiteral = /^(?:[+\-]?\d+(?:(?:N|(?:[eE][+\-]?\d+))|(?:\.?\d*(?:M|(?:[eE][+\-]?\d+))?)|\/\d+|[xX][0-9a-fA-F]+|r[0-9a-zA-Z]+)?(?=[\\\[\]\s"#'(),;@^`{}~]|$))/;
+ var characterLiteral = /^(?:\\(?:backspace|formfeed|newline|return|space|tab|o[0-7]{3}|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{4}|.)?(?=[\\\[\]\s"(),;@^`{}~]|$))/;
+
+ // simple-namespace := /^[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*/
+ // simple-symbol := /^(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)/
+ // qualified-symbol := (<simple-namespace>(<.><simple-namespace>)*</>)?<simple-symbol>
+ var qualifiedSymbol = /^(?:(?:[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*(?:\.[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*\/)?(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*(?=[\\\[\]\s"(),;@^`{}~]|$))/;
+
+ function base(stream, state) {
+ if (stream.eatSpace() || stream.eat(",")) return ["space", null];
+ if (stream.match(numberLiteral)) return [null, "number"];
+ if (stream.match(characterLiteral)) return [null, "string-2"];
+ if (stream.eat(/^"/)) return (state.tokenize = inString)(stream, state);
+ if (stream.eat(/^[(\[{]/)) return ["open", "bracket"];
+ if (stream.eat(/^[)\]}]/)) return ["close", "bracket"];
+ if (stream.eat(/^;/)) {stream.skipToEnd(); return ["space", "comment"];}
+ if (stream.eat(/^[#'@^`~]/)) return [null, "meta"];
+
+ var matches = stream.match(qualifiedSymbol);
+ var symbol = matches && matches[0];
+
+ if (!symbol) {
+ // advance stream by at least one character so we don't get stuck.
+ stream.next();
+ stream.eatWhile(function (c) {return !is(c, delimiter);});
+ return [null, "error"];
+ }
+
+ if (symbol === "comment" && state.lastToken === "(")
+ return (state.tokenize = inComment)(stream, state);
+ if (is(symbol, atom) || symbol.charAt(0) === ":") return ["symbol", "atom"];
+ if (is(symbol, specialForm) || is(symbol, coreSymbol)) return ["symbol", "keyword"];
+ if (state.lastToken === "(") return ["symbol", "builtin"]; // other operator
+
+ return ["symbol", "variable"];
+ }
+
+ function inString(stream, state) {
+ var escaped = false, next;
+
+ while (next = stream.next()) {
+ if (next === "\"" && !escaped) {state.tokenize = base; break;}
+ escaped = !escaped && next === "\\";
+ }
+
+ return [null, "string"];
+ }
+
+ function inComment(stream, state) {
+ var parenthesisCount = 1;
+ var next;
+
+ while (next = stream.next()) {
+ if (next === ")") parenthesisCount--;
+ if (next === "(") parenthesisCount++;
+ if (parenthesisCount === 0) {
+ stream.backUp(1);
+ state.tokenize = base;
+ break;
+ }
+ }
+
+ return ["space", "comment"];
+ }
+
+ function createLookupMap(words) {
+ var obj = {};
+
+ for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+
+ return obj;
+ }
+
+ function is(value, test) {
+ if (test instanceof RegExp) return test.test(value);
+ if (test instanceof Object) return test.propertyIsEnumerable(value);
+ }
+
+ return {
+ startState: function () {
+ return {
+ ctx: {prev: null, start: 0, indentTo: 0},
+ lastToken: null,
+ tokenize: base
+ };
+ },
+
+ token: function (stream, state) {
+ if (stream.sol() && (typeof state.ctx.indentTo !== "number"))
+ state.ctx.indentTo = state.ctx.start + 1;
+
+ var typeStylePair = state.tokenize(stream, state);
+ var type = typeStylePair[0];
+ var style = typeStylePair[1];
+ var current = stream.current();
+
+ if (type !== "space") {
+ if (state.lastToken === "(" && state.ctx.indentTo === null) {
+ if (type === "symbol" && is(current, hasBodyParameter))
+ state.ctx.indentTo = state.ctx.start + options.indentUnit;
+ else state.ctx.indentTo = "next";
+ } else if (state.ctx.indentTo === "next") {
+ state.ctx.indentTo = stream.column();
+ }
+
+ state.lastToken = current;
+ }
+
+ if (type === "open")
+ state.ctx = {prev: state.ctx, start: stream.column(), indentTo: null};
+ else if (type === "close") state.ctx = state.ctx.prev || state.ctx;
+
+ return style;
+ },
+
+ indent: function (state) {
+ var i = state.ctx.indentTo;
+
+ return (typeof i === "number") ?
+ i :
+ state.ctx.start + 1;
+ },
+
+ closeBrackets: {pairs: "()[]{}\"\""},
+ lineComment: ";;"
+ };
+});
+
+CodeMirror.defineMIME("text/x-clojure", "clojure");
+CodeMirror.defineMIME("text/x-clojurescript", "clojure");
+CodeMirror.defineMIME("application/edn", "clojure");
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/coffeescript/coffeescript.js b/devtools/client/shared/sourceeditor/codemirror/mode/coffeescript/coffeescript.js
new file mode 100644
index 0000000000..c513a3bb81
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/coffeescript/coffeescript.js
@@ -0,0 +1,359 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/**
+ * Link to the project's GitHub page:
+ * https://github.com/pickhardt/coffeescript-codemirror-mode
+ */
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
+ var ERRORCLASS = "error";
+
+ function wordRegexp(words) {
+ return new RegExp("^((" + words.join(")|(") + "))\\b");
+ }
+
+ var operators = /^(?:->|=>|\+[+=]?|-[\-=]?|\*[\*=]?|\/[\/=]?|[=!]=|<[><]?=?|>>?=?|%=?|&=?|\|=?|\^=?|\~|!|\?|(or|and|\|\||&&|\?)=)/;
+ var delimiters = /^(?:[()\[\]{},:`=;]|\.\.?\.?)/;
+ var identifiers = /^[_A-Za-z$][_A-Za-z$0-9]*/;
+ var atProp = /^@[_A-Za-z$][_A-Za-z$0-9]*/;
+
+ var wordOperators = wordRegexp(["and", "or", "not",
+ "is", "isnt", "in",
+ "instanceof", "typeof"]);
+ var indentKeywords = ["for", "while", "loop", "if", "unless", "else",
+ "switch", "try", "catch", "finally", "class"];
+ var commonKeywords = ["break", "by", "continue", "debugger", "delete",
+ "do", "in", "of", "new", "return", "then",
+ "this", "@", "throw", "when", "until", "extends"];
+
+ var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
+
+ indentKeywords = wordRegexp(indentKeywords);
+
+
+ var stringPrefixes = /^('{3}|\"{3}|['\"])/;
+ var regexPrefixes = /^(\/{3}|\/)/;
+ var commonConstants = ["Infinity", "NaN", "undefined", "null", "true", "false", "on", "off", "yes", "no"];
+ var constants = wordRegexp(commonConstants);
+
+ // Tokenizers
+ function tokenBase(stream, state) {
+ // Handle scope changes
+ if (stream.sol()) {
+ if (state.scope.align === null) state.scope.align = false;
+ var scopeOffset = state.scope.offset;
+ if (stream.eatSpace()) {
+ var lineOffset = stream.indentation();
+ if (lineOffset > scopeOffset && state.scope.type == "coffee") {
+ return "indent";
+ } else if (lineOffset < scopeOffset) {
+ return "dedent";
+ }
+ return null;
+ } else {
+ if (scopeOffset > 0) {
+ dedent(stream, state);
+ }
+ }
+ }
+ if (stream.eatSpace()) {
+ return null;
+ }
+
+ var ch = stream.peek();
+
+ // Handle docco title comment (single line)
+ if (stream.match("####")) {
+ stream.skipToEnd();
+ return "comment";
+ }
+
+ // Handle multi line comments
+ if (stream.match("###")) {
+ state.tokenize = longComment;
+ return state.tokenize(stream, state);
+ }
+
+ // Single line comment
+ if (ch === "#") {
+ stream.skipToEnd();
+ return "comment";
+ }
+
+ // Handle number literals
+ if (stream.match(/^-?[0-9\.]/, false)) {
+ var floatLiteral = false;
+ // Floats
+ if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
+ floatLiteral = true;
+ }
+ if (stream.match(/^-?\d+\.\d*/)) {
+ floatLiteral = true;
+ }
+ if (stream.match(/^-?\.\d+/)) {
+ floatLiteral = true;
+ }
+
+ if (floatLiteral) {
+ // prevent from getting extra . on 1..
+ if (stream.peek() == "."){
+ stream.backUp(1);
+ }
+ return "number";
+ }
+ // Integers
+ var intLiteral = false;
+ // Hex
+ if (stream.match(/^-?0x[0-9a-f]+/i)) {
+ intLiteral = true;
+ }
+ // Decimal
+ if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
+ intLiteral = true;
+ }
+ // Zero by itself with no other piece of number.
+ if (stream.match(/^-?0(?![\dx])/i)) {
+ intLiteral = true;
+ }
+ if (intLiteral) {
+ return "number";
+ }
+ }
+
+ // Handle strings
+ if (stream.match(stringPrefixes)) {
+ state.tokenize = tokenFactory(stream.current(), false, "string");
+ return state.tokenize(stream, state);
+ }
+ // Handle regex literals
+ if (stream.match(regexPrefixes)) {
+ if (stream.current() != "/" || stream.match(/^.*\//, false)) { // prevent highlight of division
+ state.tokenize = tokenFactory(stream.current(), true, "string-2");
+ return state.tokenize(stream, state);
+ } else {
+ stream.backUp(1);
+ }
+ }
+
+
+
+ // Handle operators and delimiters
+ if (stream.match(operators) || stream.match(wordOperators)) {
+ return "operator";
+ }
+ if (stream.match(delimiters)) {
+ return "punctuation";
+ }
+
+ if (stream.match(constants)) {
+ return "atom";
+ }
+
+ if (stream.match(atProp) || state.prop && stream.match(identifiers)) {
+ return "property";
+ }
+
+ if (stream.match(keywords)) {
+ return "keyword";
+ }
+
+ if (stream.match(identifiers)) {
+ return "variable";
+ }
+
+ // Handle non-detected items
+ stream.next();
+ return ERRORCLASS;
+ }
+
+ function tokenFactory(delimiter, singleline, outclass) {
+ return function(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^'"\/\\]/);
+ if (stream.eat("\\")) {
+ stream.next();
+ if (singleline && stream.eol()) {
+ return outclass;
+ }
+ } else if (stream.match(delimiter)) {
+ state.tokenize = tokenBase;
+ return outclass;
+ } else {
+ stream.eat(/['"\/]/);
+ }
+ }
+ if (singleline) {
+ if (parserConf.singleLineStringErrors) {
+ outclass = ERRORCLASS;
+ } else {
+ state.tokenize = tokenBase;
+ }
+ }
+ return outclass;
+ };
+ }
+
+ function longComment(stream, state) {
+ while (!stream.eol()) {
+ stream.eatWhile(/[^#]/);
+ if (stream.match("###")) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ stream.eatWhile("#");
+ }
+ return "comment";
+ }
+
+ function indent(stream, state, type) {
+ type = type || "coffee";
+ var offset = 0, align = false, alignOffset = null;
+ for (var scope = state.scope; scope; scope = scope.prev) {
+ if (scope.type === "coffee" || scope.type == "}") {
+ offset = scope.offset + conf.indentUnit;
+ break;
+ }
+ }
+ if (type !== "coffee") {
+ align = null;
+ alignOffset = stream.column() + stream.current().length;
+ } else if (state.scope.align) {
+ state.scope.align = false;
+ }
+ state.scope = {
+ offset: offset,
+ type: type,
+ prev: state.scope,
+ align: align,
+ alignOffset: alignOffset
+ };
+ }
+
+ function dedent(stream, state) {
+ if (!state.scope.prev) return;
+ if (state.scope.type === "coffee") {
+ var _indent = stream.indentation();
+ var matched = false;
+ for (var scope = state.scope; scope; scope = scope.prev) {
+ if (_indent === scope.offset) {
+ matched = true;
+ break;
+ }
+ }
+ if (!matched) {
+ return true;
+ }
+ while (state.scope.prev && state.scope.offset !== _indent) {
+ state.scope = state.scope.prev;
+ }
+ return false;
+ } else {
+ state.scope = state.scope.prev;
+ return false;
+ }
+ }
+
+ function tokenLexer(stream, state) {
+ var style = state.tokenize(stream, state);
+ var current = stream.current();
+
+ // Handle scope changes.
+ if (current === "return") {
+ state.dedent = true;
+ }
+ if (((current === "->" || current === "=>") && stream.eol())
+ || style === "indent") {
+ indent(stream, state);
+ }
+ var delimiter_index = "[({".indexOf(current);
+ if (delimiter_index !== -1) {
+ indent(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
+ }
+ if (indentKeywords.exec(current)){
+ indent(stream, state);
+ }
+ if (current == "then"){
+ dedent(stream, state);
+ }
+
+
+ if (style === "dedent") {
+ if (dedent(stream, state)) {
+ return ERRORCLASS;
+ }
+ }
+ delimiter_index = "])}".indexOf(current);
+ if (delimiter_index !== -1) {
+ while (state.scope.type == "coffee" && state.scope.prev)
+ state.scope = state.scope.prev;
+ if (state.scope.type == current)
+ state.scope = state.scope.prev;
+ }
+ if (state.dedent && stream.eol()) {
+ if (state.scope.type == "coffee" && state.scope.prev)
+ state.scope = state.scope.prev;
+ state.dedent = false;
+ }
+
+ return style;
+ }
+
+ var external = {
+ startState: function(basecolumn) {
+ return {
+ tokenize: tokenBase,
+ scope: {offset:basecolumn || 0, type:"coffee", prev: null, align: false},
+ prop: false,
+ dedent: 0
+ };
+ },
+
+ token: function(stream, state) {
+ var fillAlign = state.scope.align === null && state.scope;
+ if (fillAlign && stream.sol()) fillAlign.align = false;
+
+ var style = tokenLexer(stream, state);
+ if (style && style != "comment") {
+ if (fillAlign) fillAlign.align = true;
+ state.prop = style == "punctuation" && stream.current() == "."
+ }
+
+ return style;
+ },
+
+ indent: function(state, text) {
+ if (state.tokenize != tokenBase) return 0;
+ var scope = state.scope;
+ var closer = text && "])}".indexOf(text.charAt(0)) > -1;
+ if (closer) while (scope.type == "coffee" && scope.prev) scope = scope.prev;
+ var closes = closer && scope.type === text.charAt(0);
+ if (scope.align)
+ return scope.alignOffset - (closes ? 1 : 0);
+ else
+ return (closes ? scope.prev : scope).offset;
+ },
+
+ lineComment: "#",
+ fold: "indent"
+ };
+ return external;
+});
+
+// IANA registered media type
+// https://www.iana.org/assignments/media-types/
+CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript");
+
+CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
+CodeMirror.defineMIME("text/coffeescript", "coffeescript");
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/css/css.js b/devtools/client/shared/sourceeditor/codemirror/mode/css/css.js
new file mode 100644
index 0000000000..ad4911bcda
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/css/css.js
@@ -0,0 +1,831 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("css", function(config, parserConfig) {
+ var inline = parserConfig.inline
+ if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
+
+ var indentUnit = config.indentUnit,
+ tokenHooks = parserConfig.tokenHooks,
+ documentTypes = parserConfig.documentTypes || {},
+ mediaTypes = parserConfig.mediaTypes || {},
+ mediaFeatures = parserConfig.mediaFeatures || {},
+ mediaValueKeywords = parserConfig.mediaValueKeywords || {},
+ propertyKeywords = parserConfig.propertyKeywords || {},
+ nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
+ fontProperties = parserConfig.fontProperties || {},
+ counterDescriptors = parserConfig.counterDescriptors || {},
+ colorKeywords = parserConfig.colorKeywords || {},
+ valueKeywords = parserConfig.valueKeywords || {},
+ allowNested = parserConfig.allowNested,
+ lineComment = parserConfig.lineComment,
+ supportsAtComponent = parserConfig.supportsAtComponent === true;
+
+ var type, override;
+ function ret(style, tp) { type = tp; return style; }
+
+ // Tokenizers
+
+ function tokenBase(stream, state) {
+ var ch = stream.next();
+ if (tokenHooks[ch]) {
+ var result = tokenHooks[ch](stream, state);
+ if (result !== false) return result;
+ }
+ if (ch == "@") {
+ stream.eatWhile(/[\w\\\-]/);
+ return ret("def", stream.current());
+ } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
+ return ret(null, "compare");
+ } else if (ch == "\"" || ch == "'") {
+ state.tokenize = tokenString(ch);
+ return state.tokenize(stream, state);
+ } else if (ch == "#") {
+ stream.eatWhile(/[\w\\\-]/);
+ return ret("atom", "hash");
+ } else if (ch == "!") {
+ stream.match(/^\s*\w*/);
+ return ret("keyword", "important");
+ } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
+ stream.eatWhile(/[\w.%]/);
+ return ret("number", "unit");
+ } else if (ch === "-") {
+ if (/[\d.]/.test(stream.peek())) {
+ stream.eatWhile(/[\w.%]/);
+ return ret("number", "unit");
+ } else if (stream.match(/^-[\w\\\-]*/)) {
+ stream.eatWhile(/[\w\\\-]/);
+ if (stream.match(/^\s*:/, false))
+ return ret("variable-2", "variable-definition");
+ return ret("variable-2", "variable");
+ } else if (stream.match(/^\w+-/)) {
+ return ret("meta", "meta");
+ }
+ } else if (/[,+>*\/]/.test(ch)) {
+ return ret(null, "select-op");
+ } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
+ return ret("qualifier", "qualifier");
+ } else if (/[:;{}\[\]\(\)]/.test(ch)) {
+ return ret(null, ch);
+ } else if (stream.match(/[\w-.]+(?=\()/)) {
+ if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
+ state.tokenize = tokenParenthesized;
+ }
+ return ret("variable callee", "variable");
+ } else if (/[\w\\\-]/.test(ch)) {
+ stream.eatWhile(/[\w\\\-]/);
+ return ret("property", "word");
+ } else {
+ return ret(null, null);
+ }
+ }
+
+ function tokenString(quote) {
+ return function(stream, state) {
+ var escaped = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == quote && !escaped) {
+ if (quote == ")") stream.backUp(1);
+ break;
+ }
+ escaped = !escaped && ch == "\\";
+ }
+ if (ch == quote || !escaped && quote != ")") state.tokenize = null;
+ return ret("string", "string");
+ };
+ }
+
+ function tokenParenthesized(stream, state) {
+ stream.next(); // Must be '('
+ if (!stream.match(/\s*[\"\')]/, false))
+ state.tokenize = tokenString(")");
+ else
+ state.tokenize = null;
+ return ret(null, "(");
+ }
+
+ // Context management
+
+ function Context(type, indent, prev) {
+ this.type = type;
+ this.indent = indent;
+ this.prev = prev;
+ }
+
+ function pushContext(state, stream, type, indent) {
+ state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
+ return type;
+ }
+
+ function popContext(state) {
+ if (state.context.prev)
+ state.context = state.context.prev;
+ return state.context.type;
+ }
+
+ function pass(type, stream, state) {
+ return states[state.context.type](type, stream, state);
+ }
+ function popAndPass(type, stream, state, n) {
+ for (var i = n || 1; i > 0; i--)
+ state.context = state.context.prev;
+ return pass(type, stream, state);
+ }
+
+ // Parser
+
+ function wordAsValue(stream) {
+ var word = stream.current().toLowerCase();
+ if (valueKeywords.hasOwnProperty(word))
+ override = "atom";
+ else if (colorKeywords.hasOwnProperty(word))
+ override = "keyword";
+ else
+ override = "variable";
+ }
+
+ var states = {};
+
+ states.top = function(type, stream, state) {
+ if (type == "{") {
+ return pushContext(state, stream, "block");
+ } else if (type == "}" && state.context.prev) {
+ return popContext(state);
+ } else if (supportsAtComponent && /@component/i.test(type)) {
+ return pushContext(state, stream, "atComponentBlock");
+ } else if (/^@(-moz-)?document$/i.test(type)) {
+ return pushContext(state, stream, "documentTypes");
+ } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
+ return pushContext(state, stream, "atBlock");
+ } else if (/^@(font-face|counter-style)/i.test(type)) {
+ state.stateArg = type;
+ return "restricted_atBlock_before";
+ } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
+ return "keyframes";
+ } else if (type && type.charAt(0) == "@") {
+ return pushContext(state, stream, "at");
+ } else if (type == "hash") {
+ override = "builtin";
+ } else if (type == "word") {
+ override = "tag";
+ } else if (type == "variable-definition") {
+ return "maybeprop";
+ } else if (type == "interpolation") {
+ return pushContext(state, stream, "interpolation");
+ } else if (type == ":") {
+ return "pseudo";
+ } else if (allowNested && type == "(") {
+ return pushContext(state, stream, "parens");
+ }
+ return state.context.type;
+ };
+
+ states.block = function(type, stream, state) {
+ if (type == "word") {
+ var word = stream.current().toLowerCase();
+ if (propertyKeywords.hasOwnProperty(word)) {
+ override = "property";
+ return "maybeprop";
+ } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
+ override = "string-2";
+ return "maybeprop";
+ } else if (allowNested) {
+ override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
+ return "block";
+ } else {
+ override += " error";
+ return "maybeprop";
+ }
+ } else if (type == "meta") {
+ return "block";
+ } else if (!allowNested && (type == "hash" || type == "qualifier")) {
+ override = "error";
+ return "block";
+ } else {
+ return states.top(type, stream, state);
+ }
+ };
+
+ states.maybeprop = function(type, stream, state) {
+ if (type == ":") return pushContext(state, stream, "prop");
+ return pass(type, stream, state);
+ };
+
+ states.prop = function(type, stream, state) {
+ if (type == ";") return popContext(state);
+ if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
+ if (type == "}" || type == "{") return popAndPass(type, stream, state);
+ if (type == "(") return pushContext(state, stream, "parens");
+
+ if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
+ override += " error";
+ } else if (type == "word") {
+ wordAsValue(stream);
+ } else if (type == "interpolation") {
+ return pushContext(state, stream, "interpolation");
+ }
+ return "prop";
+ };
+
+ states.propBlock = function(type, _stream, state) {
+ if (type == "}") return popContext(state);
+ if (type == "word") { override = "property"; return "maybeprop"; }
+ return state.context.type;
+ };
+
+ states.parens = function(type, stream, state) {
+ if (type == "{" || type == "}") return popAndPass(type, stream, state);
+ if (type == ")") return popContext(state);
+ if (type == "(") return pushContext(state, stream, "parens");
+ if (type == "interpolation") return pushContext(state, stream, "interpolation");
+ if (type == "word") wordAsValue(stream);
+ return "parens";
+ };
+
+ states.pseudo = function(type, stream, state) {
+ if (type == "meta") return "pseudo";
+
+ if (type == "word") {
+ override = "variable-3";
+ return state.context.type;
+ }
+ return pass(type, stream, state);
+ };
+
+ states.documentTypes = function(type, stream, state) {
+ if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
+ override = "tag";
+ return state.context.type;
+ } else {
+ return states.atBlock(type, stream, state);
+ }
+ };
+
+ states.atBlock = function(type, stream, state) {
+ if (type == "(") return pushContext(state, stream, "atBlock_parens");
+ if (type == "}" || type == ";") return popAndPass(type, stream, state);
+ if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
+
+ if (type == "interpolation") return pushContext(state, stream, "interpolation");
+
+ if (type == "word") {
+ var word = stream.current().toLowerCase();
+ if (word == "only" || word == "not" || word == "and" || word == "or")
+ override = "keyword";
+ else if (mediaTypes.hasOwnProperty(word))
+ override = "attribute";
+ else if (mediaFeatures.hasOwnProperty(word))
+ override = "property";
+ else if (mediaValueKeywords.hasOwnProperty(word))
+ override = "keyword";
+ else if (propertyKeywords.hasOwnProperty(word))
+ override = "property";
+ else if (nonStandardPropertyKeywords.hasOwnProperty(word))
+ override = "string-2";
+ else if (valueKeywords.hasOwnProperty(word))
+ override = "atom";
+ else if (colorKeywords.hasOwnProperty(word))
+ override = "keyword";
+ else
+ override = "error";
+ }
+ return state.context.type;
+ };
+
+ states.atComponentBlock = function(type, stream, state) {
+ if (type == "}")
+ return popAndPass(type, stream, state);
+ if (type == "{")
+ return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
+ if (type == "word")
+ override = "error";
+ return state.context.type;
+ };
+
+ states.atBlock_parens = function(type, stream, state) {
+ if (type == ")") return popContext(state);
+ if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
+ return states.atBlock(type, stream, state);
+ };
+
+ states.restricted_atBlock_before = function(type, stream, state) {
+ if (type == "{")
+ return pushContext(state, stream, "restricted_atBlock");
+ if (type == "word" && state.stateArg == "@counter-style") {
+ override = "variable";
+ return "restricted_atBlock_before";
+ }
+ return pass(type, stream, state);
+ };
+
+ states.restricted_atBlock = function(type, stream, state) {
+ if (type == "}") {
+ state.stateArg = null;
+ return popContext(state);
+ }
+ if (type == "word") {
+ if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
+ (state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
+ override = "error";
+ else
+ override = "property";
+ return "maybeprop";
+ }
+ return "restricted_atBlock";
+ };
+
+ states.keyframes = function(type, stream, state) {
+ if (type == "word") { override = "variable"; return "keyframes"; }
+ if (type == "{") return pushContext(state, stream, "top");
+ return pass(type, stream, state);
+ };
+
+ states.at = function(type, stream, state) {
+ if (type == ";") return popContext(state);
+ if (type == "{" || type == "}") return popAndPass(type, stream, state);
+ if (type == "word") override = "tag";
+ else if (type == "hash") override = "builtin";
+ return "at";
+ };
+
+ states.interpolation = function(type, stream, state) {
+ if (type == "}") return popContext(state);
+ if (type == "{" || type == ";") return popAndPass(type, stream, state);
+ if (type == "word") override = "variable";
+ else if (type != "variable" && type != "(" && type != ")") override = "error";
+ return "interpolation";
+ };
+
+ return {
+ startState: function(base) {
+ return {tokenize: null,
+ state: inline ? "block" : "top",
+ stateArg: null,
+ context: new Context(inline ? "block" : "top", base || 0, null)};
+ },
+
+ token: function(stream, state) {
+ if (!state.tokenize && stream.eatSpace()) return null;
+ var style = (state.tokenize || tokenBase)(stream, state);
+ if (style && typeof style == "object") {
+ type = style[1];
+ style = style[0];
+ }
+ override = style;
+ if (type != "comment")
+ state.state = states[state.state](type, stream, state);
+ return override;
+ },
+
+ indent: function(state, textAfter) {
+ var cx = state.context, ch = textAfter && textAfter.charAt(0);
+ var indent = cx.indent;
+ if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
+ if (cx.prev) {
+ if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
+ cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
+ // Resume indentation from parent context.
+ cx = cx.prev;
+ indent = cx.indent;
+ } else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
+ ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
+ // Dedent relative to current context.
+ indent = Math.max(0, cx.indent - indentUnit);
+ }
+ }
+ return indent;
+ },
+
+ electricChars: "}",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ blockCommentContinue: " * ",
+ lineComment: lineComment,
+ fold: "brace"
+ };
+});
+
+ function keySet(array) {
+ var keys = {};
+ for (var i = 0; i < array.length; ++i) {
+ keys[array[i].toLowerCase()] = true;
+ }
+ return keys;
+ }
+
+ var documentTypes_ = [
+ "domain", "regexp", "url", "url-prefix"
+ ], documentTypes = keySet(documentTypes_);
+
+ var mediaTypes_ = [
+ "all", "aural", "braille", "handheld", "print", "projection", "screen",
+ "tty", "tv", "embossed"
+ ], mediaTypes = keySet(mediaTypes_);
+
+ var mediaFeatures_ = [
+ "width", "min-width", "max-width", "height", "min-height", "max-height",
+ "device-width", "min-device-width", "max-device-width", "device-height",
+ "min-device-height", "max-device-height", "aspect-ratio",
+ "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
+ "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
+ "max-color", "color-index", "min-color-index", "max-color-index",
+ "monochrome", "min-monochrome", "max-monochrome", "resolution",
+ "min-resolution", "max-resolution", "scan", "grid", "orientation",
+ "device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
+ "pointer", "any-pointer", "hover", "any-hover"
+ ], mediaFeatures = keySet(mediaFeatures_);
+
+ var mediaValueKeywords_ = [
+ "landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
+ "interlace", "progressive"
+ ], mediaValueKeywords = keySet(mediaValueKeywords_);
+
+ var propertyKeywords_ = [
+ "align-content", "align-items", "align-self", "alignment-adjust",
+ "alignment-baseline", "anchor-point", "animation", "animation-delay",
+ "animation-direction", "animation-duration", "animation-fill-mode",
+ "animation-iteration-count", "animation-name", "animation-play-state",
+ "animation-timing-function", "appearance", "azimuth", "backface-visibility",
+ "background", "background-attachment", "background-blend-mode", "background-clip",
+ "background-color", "background-image", "background-origin", "background-position",
+ "background-repeat", "background-size", "baseline-shift", "binding",
+ "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
+ "bookmark-target", "border", "border-bottom", "border-bottom-color",
+ "border-bottom-left-radius", "border-bottom-right-radius",
+ "border-bottom-style", "border-bottom-width", "border-collapse",
+ "border-color", "border-image", "border-image-outset",
+ "border-image-repeat", "border-image-slice", "border-image-source",
+ "border-image-width", "border-left", "border-left-color",
+ "border-left-style", "border-left-width", "border-radius", "border-right",
+ "border-right-color", "border-right-style", "border-right-width",
+ "border-spacing", "border-style", "border-top", "border-top-color",
+ "border-top-left-radius", "border-top-right-radius", "border-top-style",
+ "border-top-width", "border-width", "bottom", "box-decoration-break",
+ "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
+ "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
+ "column-fill", "column-gap", "column-rule", "column-rule-color",
+ "column-rule-style", "column-rule-width", "column-span", "column-width",
+ "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
+ "cue-after", "cue-before", "cursor", "direction", "display",
+ "dominant-baseline", "drop-initial-after-adjust",
+ "drop-initial-after-align", "drop-initial-before-adjust",
+ "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
+ "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
+ "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
+ "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
+ "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
+ "font-stretch", "font-style", "font-synthesis", "font-variant",
+ "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
+ "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
+ "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
+ "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-gap",
+ "grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
+ "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
+ "grid-template-rows", "hanging-punctuation", "height", "hyphens",
+ "icon", "image-orientation", "image-rendering", "image-resolution",
+ "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
+ "line-break", "line-height", "line-stacking", "line-stacking-ruby",
+ "line-stacking-shift", "line-stacking-strategy", "list-style",
+ "list-style-image", "list-style-position", "list-style-type", "margin",
+ "margin-bottom", "margin-left", "margin-right", "margin-top",
+ "marks", "marquee-direction", "marquee-loop",
+ "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
+ "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index",
+ "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
+ "opacity", "order", "orphans", "outline",
+ "outline-color", "outline-offset", "outline-style", "outline-width",
+ "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
+ "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
+ "page", "page-break-after", "page-break-before", "page-break-inside",
+ "page-policy", "pause", "pause-after", "pause-before", "perspective",
+ "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
+ "presentation-level", "punctuation-trim", "quotes", "region-break-after",
+ "region-break-before", "region-break-inside", "region-fragment",
+ "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
+ "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
+ "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
+ "shape-outside", "size", "speak", "speak-as", "speak-header",
+ "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
+ "tab-size", "table-layout", "target", "target-name", "target-new",
+ "target-position", "text-align", "text-align-last", "text-decoration",
+ "text-decoration-color", "text-decoration-line", "text-decoration-skip",
+ "text-decoration-style", "text-emphasis", "text-emphasis-color",
+ "text-emphasis-position", "text-emphasis-style", "text-height",
+ "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
+ "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
+ "text-wrap", "top", "transform", "transform-origin", "transform-style",
+ "transition", "transition-delay", "transition-duration",
+ "transition-property", "transition-timing-function", "unicode-bidi",
+ "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
+ "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
+ "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
+ "word-spacing", "word-wrap", "z-index",
+ // SVG-specific
+ "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
+ "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
+ "color-interpolation", "color-interpolation-filters",
+ "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
+ "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
+ "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
+ "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
+ "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
+ "glyph-orientation-vertical", "text-anchor", "writing-mode"
+ ], propertyKeywords = keySet(propertyKeywords_);
+
+ var nonStandardPropertyKeywords_ = [
+ "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
+ "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
+ "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
+ "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
+ "searchfield-results-decoration", "zoom"
+ ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
+
+ var fontProperties_ = [
+ "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
+ "font-stretch", "font-weight", "font-style"
+ ], fontProperties = keySet(fontProperties_);
+
+ var counterDescriptors_ = [
+ "additive-symbols", "fallback", "negative", "pad", "prefix", "range",
+ "speak-as", "suffix", "symbols", "system"
+ ], counterDescriptors = keySet(counterDescriptors_);
+
+ var colorKeywords_ = [
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
+ "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
+ "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
+ "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
+ "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
+ "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+ "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
+ "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
+ "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+ "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
+ "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
+ "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
+ "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
+ "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
+ "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
+ "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
+ "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
+ "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+ "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
+ "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
+ "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
+ "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
+ "whitesmoke", "yellow", "yellowgreen"
+ ], colorKeywords = keySet(colorKeywords_);
+
+ var valueKeywords_ = [
+ "above", "absolute", "activeborder", "additive", "activecaption", "afar",
+ "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
+ "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
+ "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
+ "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
+ "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
+ "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
+ "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "calc", "cambodian",
+ "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
+ "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
+ "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
+ "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
+ "compact", "condensed", "contain", "content", "contents",
+ "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
+ "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
+ "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
+ "destination-in", "destination-out", "destination-over", "devanagari", "difference",
+ "disc", "discard", "disclosure-closed", "disclosure-open", "document",
+ "dot-dash", "dot-dot-dash",
+ "dotted", "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
+ "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
+ "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
+ "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
+ "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
+ "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
+ "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
+ "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "ethiopic-halehame-tig",
+ "ethiopic-numeric", "ew-resize", "exclusion", "expanded", "extends", "extra-condensed",
+ "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
+ "forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
+ "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
+ "help", "hidden", "hide", "higher", "highlight", "highlighttext",
+ "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "hwb", "icon", "ignore",
+ "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
+ "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
+ "inline-block", "inline-flex", "inline-grid", "inline-table", "inset", "inside", "intrinsic", "invert",
+ "italic", "japanese-formal", "japanese-informal", "justify", "kannada",
+ "katakana", "katakana-iroha", "keep-all", "khmer",
+ "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal",
+ "landscape", "lao", "large", "larger", "left", "level", "lighter", "lighten",
+ "line-through", "linear", "linear-gradient", "lines", "list-item", "listbox", "listitem",
+ "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
+ "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
+ "lower-roman", "lowercase", "ltr", "luminosity", "malayalam", "match", "matrix", "matrix3d",
+ "media-controls-background", "media-current-time-display",
+ "media-fullscreen-button", "media-mute-button", "media-play-button",
+ "media-return-to-realtime-button", "media-rewind-button",
+ "media-seek-back-button", "media-seek-forward-button", "media-slider",
+ "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
+ "media-volume-slider-container", "media-volume-sliderthumb", "medium",
+ "menu", "menulist", "menulist-button", "menulist-text",
+ "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
+ "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
+ "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
+ "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
+ "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
+ "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
+ "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
+ "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
+ "pointer", "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d",
+ "progress", "push-button", "radial-gradient", "radio", "read-only",
+ "read-write", "read-write-plaintext-only", "rectangle", "region",
+ "relative", "repeat", "repeating-linear-gradient",
+ "repeating-radial-gradient", "repeat-x", "repeat-y", "reset", "reverse",
+ "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
+ "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
+ "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
+ "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
+ "searchfield-cancel-button", "searchfield-decoration",
+ "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
+ "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
+ "simp-chinese-formal", "simp-chinese-informal", "single",
+ "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
+ "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
+ "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
+ "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
+ "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
+ "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
+ "table-caption", "table-cell", "table-column", "table-column-group",
+ "table-footer-group", "table-header-group", "table-row", "table-row-group",
+ "tamil",
+ "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
+ "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
+ "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
+ "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
+ "trad-chinese-formal", "trad-chinese-informal", "transform",
+ "translate", "translate3d", "translateX", "translateY", "translateZ",
+ "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
+ "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
+ "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
+ "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
+ "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
+ "window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
+ "xx-large", "xx-small"
+ ], valueKeywords = keySet(valueKeywords_);
+
+ var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
+ .concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
+ .concat(valueKeywords_);
+ CodeMirror.registerHelper("hintWords", "css", allWords);
+
+ function tokenCComment(stream, state) {
+ var maybeEnd = false, ch;
+ while ((ch = stream.next()) != null) {
+ if (maybeEnd && ch == "/") {
+ state.tokenize = null;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return ["comment", "comment"];
+ }
+
+ CodeMirror.defineMIME("text/css", {
+ documentTypes: documentTypes,
+ mediaTypes: mediaTypes,
+ mediaFeatures: mediaFeatures,
+ mediaValueKeywords: mediaValueKeywords,
+ propertyKeywords: propertyKeywords,
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+ fontProperties: fontProperties,
+ counterDescriptors: counterDescriptors,
+ colorKeywords: colorKeywords,
+ valueKeywords: valueKeywords,
+ tokenHooks: {
+ "/": function(stream, state) {
+ if (!stream.eat("*")) return false;
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+ }
+ },
+ name: "css"
+ });
+
+ CodeMirror.defineMIME("text/x-scss", {
+ mediaTypes: mediaTypes,
+ mediaFeatures: mediaFeatures,
+ mediaValueKeywords: mediaValueKeywords,
+ propertyKeywords: propertyKeywords,
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+ colorKeywords: colorKeywords,
+ valueKeywords: valueKeywords,
+ fontProperties: fontProperties,
+ allowNested: true,
+ lineComment: "//",
+ tokenHooks: {
+ "/": function(stream, state) {
+ if (stream.eat("/")) {
+ stream.skipToEnd();
+ return ["comment", "comment"];
+ } else if (stream.eat("*")) {
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+ } else {
+ return ["operator", "operator"];
+ }
+ },
+ ":": function(stream) {
+ if (stream.match(/\s*\{/, false))
+ return [null, null]
+ return false;
+ },
+ "$": function(stream) {
+ stream.match(/^[\w-]+/);
+ if (stream.match(/^\s*:/, false))
+ return ["variable-2", "variable-definition"];
+ return ["variable-2", "variable"];
+ },
+ "#": function(stream) {
+ if (!stream.eat("{")) return false;
+ return [null, "interpolation"];
+ }
+ },
+ name: "css",
+ helperType: "scss"
+ });
+
+ CodeMirror.defineMIME("text/x-less", {
+ mediaTypes: mediaTypes,
+ mediaFeatures: mediaFeatures,
+ mediaValueKeywords: mediaValueKeywords,
+ propertyKeywords: propertyKeywords,
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+ colorKeywords: colorKeywords,
+ valueKeywords: valueKeywords,
+ fontProperties: fontProperties,
+ allowNested: true,
+ lineComment: "//",
+ tokenHooks: {
+ "/": function(stream, state) {
+ if (stream.eat("/")) {
+ stream.skipToEnd();
+ return ["comment", "comment"];
+ } else if (stream.eat("*")) {
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+ } else {
+ return ["operator", "operator"];
+ }
+ },
+ "@": function(stream) {
+ if (stream.eat("{")) return [null, "interpolation"];
+ if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
+ stream.eatWhile(/[\w\\\-]/);
+ if (stream.match(/^\s*:/, false))
+ return ["variable-2", "variable-definition"];
+ return ["variable-2", "variable"];
+ },
+ "&": function() {
+ return ["atom", "atom"];
+ }
+ },
+ name: "css",
+ helperType: "less"
+ });
+
+ CodeMirror.defineMIME("text/x-gss", {
+ documentTypes: documentTypes,
+ mediaTypes: mediaTypes,
+ mediaFeatures: mediaFeatures,
+ propertyKeywords: propertyKeywords,
+ nonStandardPropertyKeywords: nonStandardPropertyKeywords,
+ fontProperties: fontProperties,
+ counterDescriptors: counterDescriptors,
+ colorKeywords: colorKeywords,
+ valueKeywords: valueKeywords,
+ supportsAtComponent: true,
+ tokenHooks: {
+ "/": function(stream, state) {
+ if (!stream.eat("*")) return false;
+ state.tokenize = tokenCComment;
+ return tokenCComment(stream, state);
+ }
+ },
+ name: "css",
+ helperType: "gss"
+ });
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/elm/elm.js b/devtools/client/shared/sourceeditor/codemirror/mode/elm/elm.js
new file mode 100644
index 0000000000..d51d0f6467
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/elm/elm.js
@@ -0,0 +1,205 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineMode("elm", function() {
+
+ function switchState(source, setState, f) {
+ setState(f);
+ return f(source, setState);
+ }
+
+ // These should all be Unicode extended, as per the Haskell 2010 report
+ var smallRE = /[a-z_]/;
+ var largeRE = /[A-Z]/;
+ var digitRE = /[0-9]/;
+ var hexitRE = /[0-9A-Fa-f]/;
+ var octitRE = /[0-7]/;
+ var idRE = /[a-z_A-Z0-9\']/;
+ var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:\u03BB\u2192]/;
+ var specialRE = /[(),;[\]`{}]/;
+ var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer
+
+ function normal() {
+ return function (source, setState) {
+ if (source.eatWhile(whiteCharRE)) {
+ return null;
+ }
+
+ var ch = source.next();
+ if (specialRE.test(ch)) {
+ if (ch == '{' && source.eat('-')) {
+ var t = "comment";
+ if (source.eat('#')) t = "meta";
+ return switchState(source, setState, ncomment(t, 1));
+ }
+ return null;
+ }
+
+ if (ch == '\'') {
+ if (source.eat('\\'))
+ source.next(); // should handle other escapes here
+ else
+ source.next();
+
+ if (source.eat('\''))
+ return "string";
+ return "error";
+ }
+
+ if (ch == '"') {
+ return switchState(source, setState, stringLiteral);
+ }
+
+ if (largeRE.test(ch)) {
+ source.eatWhile(idRE);
+ if (source.eat('.'))
+ return "qualifier";
+ return "variable-2";
+ }
+
+ if (smallRE.test(ch)) {
+ var isDef = source.pos === 1;
+ source.eatWhile(idRE);
+ return isDef ? "type" : "variable";
+ }
+
+ if (digitRE.test(ch)) {
+ if (ch == '0') {
+ if (source.eat(/[xX]/)) {
+ source.eatWhile(hexitRE); // should require at least 1
+ return "integer";
+ }
+ if (source.eat(/[oO]/)) {
+ source.eatWhile(octitRE); // should require at least 1
+ return "number";
+ }
+ }
+ source.eatWhile(digitRE);
+ var t = "number";
+ if (source.eat('.')) {
+ t = "number";
+ source.eatWhile(digitRE); // should require at least 1
+ }
+ if (source.eat(/[eE]/)) {
+ t = "number";
+ source.eat(/[-+]/);
+ source.eatWhile(digitRE); // should require at least 1
+ }
+ return t;
+ }
+
+ if (symbolRE.test(ch)) {
+ if (ch == '-' && source.eat(/-/)) {
+ source.eatWhile(/-/);
+ if (!source.eat(symbolRE)) {
+ source.skipToEnd();
+ return "comment";
+ }
+ }
+ source.eatWhile(symbolRE);
+ return "builtin";
+ }
+
+ return "error";
+ }
+ }
+
+ function ncomment(type, nest) {
+ if (nest == 0) {
+ return normal();
+ }
+ return function(source, setState) {
+ var currNest = nest;
+ while (!source.eol()) {
+ var ch = source.next();
+ if (ch == '{' && source.eat('-')) {
+ ++currNest;
+ } else if (ch == '-' && source.eat('}')) {
+ --currNest;
+ if (currNest == 0) {
+ setState(normal());
+ return type;
+ }
+ }
+ }
+ setState(ncomment(type, currNest));
+ return type;
+ }
+ }
+
+ function stringLiteral(source, setState) {
+ while (!source.eol()) {
+ var ch = source.next();
+ if (ch == '"') {
+ setState(normal());
+ return "string";
+ }
+ if (ch == '\\') {
+ if (source.eol() || source.eat(whiteCharRE)) {
+ setState(stringGap);
+ return "string";
+ }
+ if (!source.eat('&')) source.next(); // should handle other escapes here
+ }
+ }
+ setState(normal());
+ return "error";
+ }
+
+ function stringGap(source, setState) {
+ if (source.eat('\\')) {
+ return switchState(source, setState, stringLiteral);
+ }
+ source.next();
+ setState(normal());
+ return "error";
+ }
+
+
+ var wellKnownWords = (function() {
+ var wkw = {};
+
+ var keywords = [
+ "case", "of", "as",
+ "if", "then", "else",
+ "let", "in",
+ "infix", "infixl", "infixr",
+ "type", "alias",
+ "input", "output", "foreign", "loopback",
+ "module", "where", "import", "exposing",
+ "_", "..", "|", ":", "=", "\\", "\"", "->", "<-"
+ ];
+
+ for (var i = keywords.length; i--;)
+ wkw[keywords[i]] = "keyword";
+
+ return wkw;
+ })();
+
+
+
+ return {
+ startState: function () { return { f: normal() }; },
+ copyState: function (s) { return { f: s.f }; },
+
+ token: function(stream, state) {
+ var t = state.f(stream, function(s) { state.f = s; });
+ var w = stream.current();
+ return (wellKnownWords.hasOwnProperty(w)) ? wellKnownWords[w] : t;
+ }
+ };
+
+ });
+
+ CodeMirror.defineMIME("text/x-elm", "elm");
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/haxe/haxe.js b/devtools/client/shared/sourceeditor/codemirror/mode/haxe/haxe.js
new file mode 100644
index 0000000000..8ddf420a68
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/haxe/haxe.js
@@ -0,0 +1,515 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("haxe", function(config, parserConfig) {
+ var indentUnit = config.indentUnit;
+
+ // Tokenizer
+
+ function kw(type) {return {type: type, style: "keyword"};}
+ var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
+ var operator = kw("operator"), atom = {type: "atom", style: "atom"}, attribute = {type:"attribute", style: "attribute"};
+ var type = kw("typedef");
+ var keywords = {
+ "if": A, "while": A, "else": B, "do": B, "try": B,
+ "return": C, "break": C, "continue": C, "new": C, "throw": C,
+ "var": kw("var"), "inline":attribute, "static": attribute, "using":kw("import"),
+ "public": attribute, "private": attribute, "cast": kw("cast"), "import": kw("import"), "macro": kw("macro"),
+ "function": kw("function"), "catch": kw("catch"), "untyped": kw("untyped"), "callback": kw("cb"),
+ "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+ "in": operator, "never": kw("property_access"), "trace":kw("trace"),
+ "class": type, "abstract":type, "enum":type, "interface":type, "typedef":type, "extends":type, "implements":type, "dynamic":type,
+ "true": atom, "false": atom, "null": atom
+ };
+
+ var isOperatorChar = /[+\-*&%=<>!?|]/;
+
+ function chain(stream, state, f) {
+ state.tokenize = f;
+ return f(stream, state);
+ }
+
+ function toUnescaped(stream, end) {
+ var escaped = false, next;
+ while ((next = stream.next()) != null) {
+ if (next == end && !escaped)
+ return true;
+ escaped = !escaped && next == "\\";
+ }
+ }
+
+ // Used as scratch variables to communicate multiple values without
+ // consing up tons of objects.
+ var type, content;
+ function ret(tp, style, cont) {
+ type = tp; content = cont;
+ return style;
+ }
+
+ function haxeTokenBase(stream, state) {
+ var ch = stream.next();
+ if (ch == '"' || ch == "'") {
+ return chain(stream, state, haxeTokenString(ch));
+ } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+ return ret(ch);
+ } else if (ch == "0" && stream.eat(/x/i)) {
+ stream.eatWhile(/[\da-f]/i);
+ return ret("number", "number");
+ } else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) {
+ stream.match(/^\d*(?:\.\d*(?!\.))?(?:[eE][+\-]?\d+)?/);
+ return ret("number", "number");
+ } else if (state.reAllowed && (ch == "~" && stream.eat(/\//))) {
+ toUnescaped(stream, "/");
+ stream.eatWhile(/[gimsu]/);
+ return ret("regexp", "string-2");
+ } else if (ch == "/") {
+ if (stream.eat("*")) {
+ return chain(stream, state, haxeTokenComment);
+ } else if (stream.eat("/")) {
+ stream.skipToEnd();
+ return ret("comment", "comment");
+ } else {
+ stream.eatWhile(isOperatorChar);
+ return ret("operator", null, stream.current());
+ }
+ } else if (ch == "#") {
+ stream.skipToEnd();
+ return ret("conditional", "meta");
+ } else if (ch == "@") {
+ stream.eat(/:/);
+ stream.eatWhile(/[\w_]/);
+ return ret ("metadata", "meta");
+ } else if (isOperatorChar.test(ch)) {
+ stream.eatWhile(isOperatorChar);
+ return ret("operator", null, stream.current());
+ } else {
+ var word;
+ if(/[A-Z]/.test(ch)) {
+ stream.eatWhile(/[\w_<>]/);
+ word = stream.current();
+ return ret("type", "variable-3", word);
+ } else {
+ stream.eatWhile(/[\w_]/);
+ var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
+ return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
+ ret("variable", "variable", word);
+ }
+ }
+ }
+
+ function haxeTokenString(quote) {
+ return function(stream, state) {
+ if (toUnescaped(stream, quote))
+ state.tokenize = haxeTokenBase;
+ return ret("string", "string");
+ };
+ }
+
+ function haxeTokenComment(stream, state) {
+ var maybeEnd = false, ch;
+ while (ch = stream.next()) {
+ if (ch == "/" && maybeEnd) {
+ state.tokenize = haxeTokenBase;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return ret("comment", "comment");
+ }
+
+ // Parser
+
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
+
+ function HaxeLexical(indented, column, type, align, prev, info) {
+ this.indented = indented;
+ this.column = column;
+ this.type = type;
+ this.prev = prev;
+ this.info = info;
+ if (align != null) this.align = align;
+ }
+
+ function inScope(state, varname) {
+ for (var v = state.localVars; v; v = v.next)
+ if (v.name == varname) return true;
+ }
+
+ function parseHaxe(state, style, type, content, stream) {
+ var cc = state.cc;
+ // Communicate our context to the combinators.
+ // (Less wasteful than consing up a hundred closures on every call.)
+ cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
+
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = true;
+
+ while(true) {
+ var combinator = cc.length ? cc.pop() : statement;
+ if (combinator(type, content)) {
+ while(cc.length && cc[cc.length - 1].lex)
+ cc.pop()();
+ if (cx.marked) return cx.marked;
+ if (type == "variable" && inScope(state, content)) return "variable-2";
+ if (type == "variable" && imported(state, content)) return "variable-3";
+ return style;
+ }
+ }
+ }
+
+ function imported(state, typename) {
+ if (/[a-z]/.test(typename.charAt(0)))
+ return false;
+ var len = state.importedtypes.length;
+ for (var i = 0; i<len; i++)
+ if(state.importedtypes[i]==typename) return true;
+ }
+
+ function registerimport(importname) {
+ var state = cx.state;
+ for (var t = state.importedtypes; t; t = t.next)
+ if(t.name == importname) return;
+ state.importedtypes = { name: importname, next: state.importedtypes };
+ }
+ // Combinator utils
+
+ var cx = {state: null, column: null, marked: null, cc: null};
+ function pass() {
+ for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+ }
+ function cont() {
+ pass.apply(null, arguments);
+ return true;
+ }
+ function inList(name, list) {
+ for (var v = list; v; v = v.next)
+ if (v.name == name) return true;
+ return false;
+ }
+ function register(varname) {
+ var state = cx.state;
+ if (state.context) {
+ cx.marked = "def";
+ if (inList(varname, state.localVars)) return;
+ state.localVars = {name: varname, next: state.localVars};
+ } else if (state.globalVars) {
+ if (inList(varname, state.globalVars)) return;
+ state.globalVars = {name: varname, next: state.globalVars};
+ }
+ }
+
+ // Combinators
+
+ var defaultVars = {name: "this", next: null};
+ function pushcontext() {
+ if (!cx.state.context) cx.state.localVars = defaultVars;
+ cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
+ }
+ function popcontext() {
+ cx.state.localVars = cx.state.context.vars;
+ cx.state.context = cx.state.context.prev;
+ }
+ popcontext.lex = true;
+ function pushlex(type, info) {
+ var result = function() {
+ var state = cx.state;
+ state.lexical = new HaxeLexical(state.indented, cx.stream.column(), type, null, state.lexical, info);
+ };
+ result.lex = true;
+ return result;
+ }
+ function poplex() {
+ var state = cx.state;
+ if (state.lexical.prev) {
+ if (state.lexical.type == ")")
+ state.indented = state.lexical.indented;
+ state.lexical = state.lexical.prev;
+ }
+ }
+ poplex.lex = true;
+
+ function expect(wanted) {
+ function f(type) {
+ if (type == wanted) return cont();
+ else if (wanted == ";") return pass();
+ else return cont(f);
+ }
+ return f;
+ }
+
+ function statement(type) {
+ if (type == "@") return cont(metadef);
+ if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
+ if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+ if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+ if (type == "{") return cont(pushlex("}"), pushcontext, block, poplex, popcontext);
+ if (type == ";") return cont();
+ if (type == "attribute") return cont(maybeattribute);
+ if (type == "function") return cont(functiondef);
+ if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
+ poplex, statement, poplex);
+ if (type == "variable") return cont(pushlex("stat"), maybelabel);
+ if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
+ block, poplex, poplex);
+ if (type == "case") return cont(expression, expect(":"));
+ if (type == "default") return cont(expect(":"));
+ if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
+ statement, poplex, popcontext);
+ if (type == "import") return cont(importdef, expect(";"));
+ if (type == "typedef") return cont(typedef);
+ return pass(pushlex("stat"), expression, expect(";"), poplex);
+ }
+ function expression(type) {
+ if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
+ if (type == "type" ) return cont(maybeoperator);
+ if (type == "function") return cont(functiondef);
+ if (type == "keyword c") return cont(maybeexpression);
+ if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
+ if (type == "operator") return cont(expression);
+ if (type == "[") return cont(pushlex("]"), commasep(maybeexpression, "]"), poplex, maybeoperator);
+ if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
+ return cont();
+ }
+ function maybeexpression(type) {
+ if (type.match(/[;\}\)\],]/)) return pass();
+ return pass(expression);
+ }
+
+ function maybeoperator(type, value) {
+ if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
+ if (type == "operator" || type == ":") return cont(expression);
+ if (type == ";") return;
+ if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
+ if (type == ".") return cont(property, maybeoperator);
+ if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
+ }
+
+ function maybeattribute(type) {
+ if (type == "attribute") return cont(maybeattribute);
+ if (type == "function") return cont(functiondef);
+ if (type == "var") return cont(vardef1);
+ }
+
+ function metadef(type) {
+ if(type == ":") return cont(metadef);
+ if(type == "variable") return cont(metadef);
+ if(type == "(") return cont(pushlex(")"), commasep(metaargs, ")"), poplex, statement);
+ }
+ function metaargs(type) {
+ if(type == "variable") return cont();
+ }
+
+ function importdef (type, value) {
+ if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
+ else if(type == "variable" || type == "property" || type == "." || value == "*") return cont(importdef);
+ }
+
+ function typedef (type, value)
+ {
+ if(type == "variable" && /[A-Z]/.test(value.charAt(0))) { registerimport(value); return cont(); }
+ else if (type == "type" && /[A-Z]/.test(value.charAt(0))) { return cont(); }
+ }
+
+ function maybelabel(type) {
+ if (type == ":") return cont(poplex, statement);
+ return pass(maybeoperator, expect(";"), poplex);
+ }
+ function property(type) {
+ if (type == "variable") {cx.marked = "property"; return cont();}
+ }
+ function objprop(type) {
+ if (type == "variable") cx.marked = "property";
+ if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
+ }
+ function commasep(what, end) {
+ function proceed(type) {
+ if (type == ",") return cont(what, proceed);
+ if (type == end) return cont();
+ return cont(expect(end));
+ }
+ return function(type) {
+ if (type == end) return cont();
+ else return pass(what, proceed);
+ };
+ }
+ function block(type) {
+ if (type == "}") return cont();
+ return pass(statement, block);
+ }
+ function vardef1(type, value) {
+ if (type == "variable"){register(value); return cont(typeuse, vardef2);}
+ return cont();
+ }
+ function vardef2(type, value) {
+ if (value == "=") return cont(expression, vardef2);
+ if (type == ",") return cont(vardef1);
+ }
+ function forspec1(type, value) {
+ if (type == "variable") {
+ register(value);
+ return cont(forin, expression)
+ } else {
+ return pass()
+ }
+ }
+ function forin(_type, value) {
+ if (value == "in") return cont();
+ }
+ function functiondef(type, value) {
+ //function names starting with upper-case letters are recognised as types, so cludging them together here.
+ if (type == "variable" || type == "type") {register(value); return cont(functiondef);}
+ if (value == "new") return cont(functiondef);
+ if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, typeuse, statement, popcontext);
+ }
+ function typeuse(type) {
+ if(type == ":") return cont(typestring);
+ }
+ function typestring(type) {
+ if(type == "type") return cont();
+ if(type == "variable") return cont();
+ if(type == "{") return cont(pushlex("}"), commasep(typeprop, "}"), poplex);
+ }
+ function typeprop(type) {
+ if(type == "variable") return cont(typeuse);
+ }
+ function funarg(type, value) {
+ if (type == "variable") {register(value); return cont(typeuse);}
+ }
+
+ // Interface
+ return {
+ startState: function(basecolumn) {
+ var defaulttypes = ["Int", "Float", "String", "Void", "Std", "Bool", "Dynamic", "Array"];
+ var state = {
+ tokenize: haxeTokenBase,
+ reAllowed: true,
+ kwAllowed: true,
+ cc: [],
+ lexical: new HaxeLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+ localVars: parserConfig.localVars,
+ importedtypes: defaulttypes,
+ context: parserConfig.localVars && {vars: parserConfig.localVars},
+ indented: 0
+ };
+ if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
+ state.globalVars = parserConfig.globalVars;
+ return state;
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = false;
+ state.indented = stream.indentation();
+ }
+ if (stream.eatSpace()) return null;
+ var style = state.tokenize(stream, state);
+ if (type == "comment") return style;
+ state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
+ state.kwAllowed = type != '.';
+ return parseHaxe(state, style, type, content, stream);
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize != haxeTokenBase) return 0;
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+ if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+ var type = lexical.type, closing = firstChar == type;
+ if (type == "vardef") return lexical.indented + 4;
+ else if (type == "form" && firstChar == "{") return lexical.indented;
+ else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
+ else if (lexical.info == "switch" && !closing)
+ return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+ else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+ else return lexical.indented + (closing ? 0 : indentUnit);
+ },
+
+ electricChars: "{}",
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ lineComment: "//"
+ };
+});
+
+CodeMirror.defineMIME("text/x-haxe", "haxe");
+
+CodeMirror.defineMode("hxml", function () {
+
+ return {
+ startState: function () {
+ return {
+ define: false,
+ inString: false
+ };
+ },
+ token: function (stream, state) {
+ var ch = stream.peek();
+ var sol = stream.sol();
+
+ ///* comments */
+ if (ch == "#") {
+ stream.skipToEnd();
+ return "comment";
+ }
+ if (sol && ch == "-") {
+ var style = "variable-2";
+
+ stream.eat(/-/);
+
+ if (stream.peek() == "-") {
+ stream.eat(/-/);
+ style = "keyword a";
+ }
+
+ if (stream.peek() == "D") {
+ stream.eat(/[D]/);
+ style = "keyword c";
+ state.define = true;
+ }
+
+ stream.eatWhile(/[A-Z]/i);
+ return style;
+ }
+
+ var ch = stream.peek();
+
+ if (state.inString == false && ch == "'") {
+ state.inString = true;
+ stream.next();
+ }
+
+ if (state.inString == true) {
+ if (stream.skipTo("'")) {
+
+ } else {
+ stream.skipToEnd();
+ }
+
+ if (stream.peek() == "'") {
+ stream.next();
+ state.inString = false;
+ }
+
+ return "string";
+ }
+
+ stream.next();
+ return null;
+ },
+ lineComment: "#"
+ };
+});
+
+CodeMirror.defineMIME("text/x-hxml", "hxml");
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/htmlmixed/htmlmixed.js b/devtools/client/shared/sourceeditor/codemirror/mode/htmlmixed/htmlmixed.js
new file mode 100644
index 0000000000..ca1bccdbdd
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/htmlmixed/htmlmixed.js
@@ -0,0 +1,152 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/mode/xml/xml.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/mode/javascript/javascript.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/mode/css/css.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ var defaultTags = {
+ script: [
+ ["lang", /(javascript|babel)/i, "javascript"],
+ ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
+ ["type", /./, "text/plain"],
+ [null, null, "javascript"]
+ ],
+ style: [
+ ["lang", /^css$/i, "css"],
+ ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
+ ["type", /./, "text/plain"],
+ [null, null, "css"]
+ ]
+ };
+
+ function maybeBackup(stream, pat, style) {
+ var cur = stream.current(), close = cur.search(pat);
+ if (close > -1) {
+ stream.backUp(cur.length - close);
+ } else if (cur.match(/<\/?$/)) {
+ stream.backUp(cur.length);
+ if (!stream.match(pat, false)) stream.match(cur);
+ }
+ return style;
+ }
+
+ var attrRegexpCache = {};
+ function getAttrRegexp(attr) {
+ var regexp = attrRegexpCache[attr];
+ if (regexp) return regexp;
+ return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
+ }
+
+ function getAttrValue(text, attr) {
+ var match = text.match(getAttrRegexp(attr))
+ return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
+ }
+
+ function getTagRegexp(tagName, anchored) {
+ return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
+ }
+
+ function addTags(from, to) {
+ for (var tag in from) {
+ var dest = to[tag] || (to[tag] = []);
+ var source = from[tag];
+ for (var i = source.length - 1; i >= 0; i--)
+ dest.unshift(source[i])
+ }
+ }
+
+ function findMatchingMode(tagInfo, tagText) {
+ for (var i = 0; i < tagInfo.length; i++) {
+ var spec = tagInfo[i];
+ if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
+ }
+ }
+
+ CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
+ var htmlMode = CodeMirror.getMode(config, {
+ name: "xml",
+ htmlMode: true,
+ multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
+ multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
+ });
+
+ var tags = {};
+ var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
+ addTags(defaultTags, tags);
+ if (configTags) addTags(configTags, tags);
+ if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
+ tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
+
+ function html(stream, state) {
+ var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
+ if (tag && !/[<>\s\/]/.test(stream.current()) &&
+ (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
+ tags.hasOwnProperty(tagName)) {
+ state.inTag = tagName + " "
+ } else if (state.inTag && tag && />$/.test(stream.current())) {
+ var inTag = /^([\S]+) (.*)/.exec(state.inTag)
+ state.inTag = null
+ var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
+ var mode = CodeMirror.getMode(config, modeSpec)
+ var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
+ state.token = function (stream, state) {
+ if (stream.match(endTagA, false)) {
+ state.token = html;
+ state.localState = state.localMode = null;
+ return null;
+ }
+ return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
+ };
+ state.localMode = mode;
+ state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
+ } else if (state.inTag) {
+ state.inTag += stream.current()
+ if (stream.eol()) state.inTag += " "
+ }
+ return style;
+ };
+
+ return {
+ startState: function () {
+ var state = CodeMirror.startState(htmlMode);
+ return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
+ },
+
+ copyState: function (state) {
+ var local;
+ if (state.localState) {
+ local = CodeMirror.copyState(state.localMode, state.localState);
+ }
+ return {token: state.token, inTag: state.inTag,
+ localMode: state.localMode, localState: local,
+ htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
+ },
+
+ token: function (stream, state) {
+ return state.token(stream, state);
+ },
+
+ indent: function (state, textAfter, line) {
+ if (!state.localMode || /^\s*<\//.test(textAfter))
+ return htmlMode.indent(state.htmlState, textAfter, line);
+ else if (state.localMode.indent)
+ return state.localMode.indent(state.localState, textAfter, line);
+ else
+ return CodeMirror.Pass;
+ },
+
+ innerMode: function (state) {
+ return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
+ }
+ };
+ }, "xml", "javascript", "css");
+
+ CodeMirror.defineMIME("text/html", "htmlmixed");
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/http/http.js b/devtools/client/shared/sourceeditor/codemirror/mode/http/http.js
new file mode 100644
index 0000000000..092353259f
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/http/http.js
@@ -0,0 +1,113 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("http", function() {
+ function failFirstLine(stream, state) {
+ stream.skipToEnd();
+ state.cur = header;
+ return "error";
+ }
+
+ function start(stream, state) {
+ if (stream.match(/^HTTP\/\d\.\d/)) {
+ state.cur = responseStatusCode;
+ return "keyword";
+ } else if (stream.match(/^[A-Z]+/) && /[ \t]/.test(stream.peek())) {
+ state.cur = requestPath;
+ return "keyword";
+ } else {
+ return failFirstLine(stream, state);
+ }
+ }
+
+ function responseStatusCode(stream, state) {
+ var code = stream.match(/^\d+/);
+ if (!code) return failFirstLine(stream, state);
+
+ state.cur = responseStatusText;
+ var status = Number(code[0]);
+ if (status >= 100 && status < 200) {
+ return "positive informational";
+ } else if (status >= 200 && status < 300) {
+ return "positive success";
+ } else if (status >= 300 && status < 400) {
+ return "positive redirect";
+ } else if (status >= 400 && status < 500) {
+ return "negative client-error";
+ } else if (status >= 500 && status < 600) {
+ return "negative server-error";
+ } else {
+ return "error";
+ }
+ }
+
+ function responseStatusText(stream, state) {
+ stream.skipToEnd();
+ state.cur = header;
+ return null;
+ }
+
+ function requestPath(stream, state) {
+ stream.eatWhile(/\S/);
+ state.cur = requestProtocol;
+ return "string-2";
+ }
+
+ function requestProtocol(stream, state) {
+ if (stream.match(/^HTTP\/\d\.\d$/)) {
+ state.cur = header;
+ return "keyword";
+ } else {
+ return failFirstLine(stream, state);
+ }
+ }
+
+ function header(stream) {
+ if (stream.sol() && !stream.eat(/[ \t]/)) {
+ if (stream.match(/^.*?:/)) {
+ return "atom";
+ } else {
+ stream.skipToEnd();
+ return "error";
+ }
+ } else {
+ stream.skipToEnd();
+ return "string";
+ }
+ }
+
+ function body(stream) {
+ stream.skipToEnd();
+ return null;
+ }
+
+ return {
+ token: function(stream, state) {
+ var cur = state.cur;
+ if (cur != header && cur != body && stream.eatSpace()) return null;
+ return cur(stream, state);
+ },
+
+ blankLine: function(state) {
+ state.cur = body;
+ },
+
+ startState: function() {
+ return {cur: start};
+ }
+ };
+});
+
+CodeMirror.defineMIME("message/http", "http");
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/javascript/javascript.js b/devtools/client/shared/sourceeditor/codemirror/mode/javascript/javascript.js
new file mode 100644
index 0000000000..a34b4f9bb9
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/javascript/javascript.js
@@ -0,0 +1,934 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("javascript", function(config, parserConfig) {
+ var indentUnit = config.indentUnit;
+ var statementIndent = parserConfig.statementIndent;
+ var jsonldMode = parserConfig.jsonld;
+ var jsonMode = parserConfig.json || jsonldMode;
+ var isTS = parserConfig.typescript;
+ var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
+
+ // Tokenizer
+
+ var keywords = function(){
+ function kw(type) {return {type: type, style: "keyword"};}
+ var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
+ var operator = kw("operator"), atom = {type: "atom", style: "atom"};
+
+ return {
+ "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
+ "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
+ "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
+ "function": kw("function"), "catch": kw("catch"),
+ "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
+ "in": operator, "typeof": operator, "instanceof": operator,
+ "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
+ "this": kw("this"), "class": kw("class"), "super": kw("atom"),
+ "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
+ "await": C
+ };
+ }();
+
+ var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
+ var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
+
+ function readRegexp(stream) {
+ var escaped = false, next, inSet = false;
+ while ((next = stream.next()) != null) {
+ if (!escaped) {
+ if (next == "/" && !inSet) return;
+ if (next == "[") inSet = true;
+ else if (inSet && next == "]") inSet = false;
+ }
+ escaped = !escaped && next == "\\";
+ }
+ }
+
+ // Used as scratch variables to communicate multiple values without
+ // consing up tons of objects.
+ var type, content;
+ function ret(tp, style, cont) {
+ type = tp; content = cont;
+ return style;
+ }
+ function tokenBase(stream, state) {
+ var ch = stream.next();
+ if (ch == '"' || ch == "'") {
+ state.tokenize = tokenString(ch);
+ return state.tokenize(stream, state);
+ } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
+ return ret("number", "number");
+ } else if (ch == "." && stream.match("..")) {
+ return ret("spread", "meta");
+ } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
+ return ret(ch);
+ } else if (ch == "=" && stream.eat(">")) {
+ return ret("=>", "operator");
+ } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
+ return ret("number", "number");
+ } else if (/\d/.test(ch)) {
+ stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
+ return ret("number", "number");
+ } else if (ch == "/") {
+ if (stream.eat("*")) {
+ state.tokenize = tokenComment;
+ return tokenComment(stream, state);
+ } else if (stream.eat("/")) {
+ stream.skipToEnd();
+ return ret("comment", "comment");
+ } else if (expressionAllowed(stream, state, 1)) {
+ readRegexp(stream);
+ stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
+ return ret("regexp", "string-2");
+ } else {
+ stream.eat("=");
+ return ret("operator", "operator", stream.current());
+ }
+ } else if (ch == "`") {
+ state.tokenize = tokenQuasi;
+ return tokenQuasi(stream, state);
+ } else if (ch == "#" && stream.peek() == "!") {
+ stream.skipToEnd();
+ return ret("meta", "meta");
+ } else if (ch == "#" && stream.eatWhile(wordRE)) {
+ return ret("variable", "property")
+ } else if (ch == "<" && stream.match("!--") ||
+ (ch == "-" && stream.match("->") && !/\S/.test(stream.string.slice(0, stream.start)))) {
+ stream.skipToEnd()
+ return ret("comment", "comment")
+ } else if (isOperatorChar.test(ch)) {
+ if (ch != ">" || !state.lexical || state.lexical.type != ">") {
+ if (stream.eat("=")) {
+ if (ch == "!" || ch == "=") stream.eat("=")
+ } else if (/[<>*+\-|&?]/.test(ch)) {
+ stream.eat(ch)
+ if (ch == ">") stream.eat(ch)
+ }
+ }
+ if (ch == "?" && stream.eat(".")) return ret(".")
+ return ret("operator", "operator", stream.current());
+ } else if (wordRE.test(ch)) {
+ stream.eatWhile(wordRE);
+ var word = stream.current()
+ if (state.lastType != ".") {
+ if (keywords.propertyIsEnumerable(word)) {
+ var kw = keywords[word]
+ return ret(kw.type, kw.style, word)
+ }
+ if (word == "async" && stream.match(/^(\s|\/\*([^*]|\*(?!\/))*?\*\/)*[\[\(\w]/, false))
+ return ret("async", "keyword", word)
+ }
+ return ret("variable", "variable", word)
+ }
+ }
+
+ function tokenString(quote) {
+ return function(stream, state) {
+ var escaped = false, next;
+ if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
+ state.tokenize = tokenBase;
+ return ret("jsonld-keyword", "meta");
+ }
+ while ((next = stream.next()) != null) {
+ if (next == quote && !escaped) break;
+ escaped = !escaped && next == "\\";
+ }
+ if (!escaped) state.tokenize = tokenBase;
+ return ret("string", "string");
+ };
+ }
+
+ function tokenComment(stream, state) {
+ var maybeEnd = false, ch;
+ while (ch = stream.next()) {
+ if (ch == "/" && maybeEnd) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ maybeEnd = (ch == "*");
+ }
+ return ret("comment", "comment");
+ }
+
+ function tokenQuasi(stream, state) {
+ var escaped = false, next;
+ while ((next = stream.next()) != null) {
+ if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
+ state.tokenize = tokenBase;
+ break;
+ }
+ escaped = !escaped && next == "\\";
+ }
+ return ret("quasi", "string-2", stream.current());
+ }
+
+ var brackets = "([{}])";
+ // This is a crude lookahead trick to try and notice that we're
+ // parsing the argument patterns for a fat-arrow function before we
+ // actually hit the arrow token. It only works if the arrow is on
+ // the same line as the arguments and there's no strange noise
+ // (comments) in between. Fallback is to only notice when we hit the
+ // arrow, and not declare the arguments as locals for the arrow
+ // body.
+ function findFatArrow(stream, state) {
+ if (state.fatArrowAt) state.fatArrowAt = null;
+ var arrow = stream.string.indexOf("=>", stream.start);
+ if (arrow < 0) return;
+
+ if (isTS) { // Try to skip TypeScript return type declarations after the arguments
+ var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
+ if (m) arrow = m.index
+ }
+
+ var depth = 0, sawSomething = false;
+ for (var pos = arrow - 1; pos >= 0; --pos) {
+ var ch = stream.string.charAt(pos);
+ var bracket = brackets.indexOf(ch);
+ if (bracket >= 0 && bracket < 3) {
+ if (!depth) { ++pos; break; }
+ if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
+ } else if (bracket >= 3 && bracket < 6) {
+ ++depth;
+ } else if (wordRE.test(ch)) {
+ sawSomething = true;
+ } else if (/["'\/`]/.test(ch)) {
+ for (;; --pos) {
+ if (pos == 0) return
+ var next = stream.string.charAt(pos - 1)
+ if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
+ }
+ } else if (sawSomething && !depth) {
+ ++pos;
+ break;
+ }
+ }
+ if (sawSomething && !depth) state.fatArrowAt = pos;
+ }
+
+ // Parser
+
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
+
+ function JSLexical(indented, column, type, align, prev, info) {
+ this.indented = indented;
+ this.column = column;
+ this.type = type;
+ this.prev = prev;
+ this.info = info;
+ if (align != null) this.align = align;
+ }
+
+ function inScope(state, varname) {
+ for (var v = state.localVars; v; v = v.next)
+ if (v.name == varname) return true;
+ for (var cx = state.context; cx; cx = cx.prev) {
+ for (var v = cx.vars; v; v = v.next)
+ if (v.name == varname) return true;
+ }
+ }
+
+ function parseJS(state, style, type, content, stream) {
+ var cc = state.cc;
+ // Communicate our context to the combinators.
+ // (Less wasteful than consing up a hundred closures on every call.)
+ cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
+
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = true;
+
+ while(true) {
+ var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
+ if (combinator(type, content)) {
+ while(cc.length && cc[cc.length - 1].lex)
+ cc.pop()();
+ if (cx.marked) return cx.marked;
+ if (type == "variable" && inScope(state, content)) return "variable-2";
+ return style;
+ }
+ }
+ }
+
+ // Combinator utils
+
+ var cx = {state: null, column: null, marked: null, cc: null};
+ function pass() {
+ for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
+ }
+ function cont() {
+ pass.apply(null, arguments);
+ return true;
+ }
+ function inList(name, list) {
+ for (var v = list; v; v = v.next) if (v.name == name) return true
+ return false;
+ }
+ function register(varname) {
+ var state = cx.state;
+ cx.marked = "def";
+ if (state.context) {
+ if (state.lexical.info == "var" && state.context && state.context.block) {
+ // FIXME function decls are also not block scoped
+ var newContext = registerVarScoped(varname, state.context)
+ if (newContext != null) {
+ state.context = newContext
+ return
+ }
+ } else if (!inList(varname, state.localVars)) {
+ state.localVars = new Var(varname, state.localVars)
+ return
+ }
+ }
+ // Fall through means this is global
+ if (parserConfig.globalVars && !inList(varname, state.globalVars))
+ state.globalVars = new Var(varname, state.globalVars)
+ }
+ function registerVarScoped(varname, context) {
+ if (!context) {
+ return null
+ } else if (context.block) {
+ var inner = registerVarScoped(varname, context.prev)
+ if (!inner) return null
+ if (inner == context.prev) return context
+ return new Context(inner, context.vars, true)
+ } else if (inList(varname, context.vars)) {
+ return context
+ } else {
+ return new Context(context.prev, new Var(varname, context.vars), false)
+ }
+ }
+
+ function isModifier(name) {
+ return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
+ }
+
+ // Combinators
+
+ function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
+ function Var(name, next) { this.name = name; this.next = next }
+
+ var defaultVars = new Var("this", new Var("arguments", null))
+ function pushcontext() {
+ cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
+ cx.state.localVars = defaultVars
+ }
+ function pushblockcontext() {
+ cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
+ cx.state.localVars = null
+ }
+ function popcontext() {
+ cx.state.localVars = cx.state.context.vars
+ cx.state.context = cx.state.context.prev
+ }
+ popcontext.lex = true
+ function pushlex(type, info) {
+ var result = function() {
+ var state = cx.state, indent = state.indented;
+ if (state.lexical.type == "stat") indent = state.lexical.indented;
+ else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
+ indent = outer.indented;
+ state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
+ };
+ result.lex = true;
+ return result;
+ }
+ function poplex() {
+ var state = cx.state;
+ if (state.lexical.prev) {
+ if (state.lexical.type == ")")
+ state.indented = state.lexical.indented;
+ state.lexical = state.lexical.prev;
+ }
+ }
+ poplex.lex = true;
+
+ function expect(wanted) {
+ function exp(type) {
+ if (type == wanted) return cont();
+ else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
+ else return cont(exp);
+ };
+ return exp;
+ }
+
+ function statement(type, value) {
+ if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
+ if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
+ if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
+ if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
+ if (type == "debugger") return cont(expect(";"));
+ if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
+ if (type == ";") return cont();
+ if (type == "if") {
+ if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
+ cx.state.cc.pop()();
+ return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
+ }
+ if (type == "function") return cont(functiondef);
+ if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
+ if (type == "class" || (isTS && value == "interface")) {
+ cx.marked = "keyword"
+ return cont(pushlex("form", type == "class" ? type : value), className, poplex)
+ }
+ if (type == "variable") {
+ if (isTS && value == "declare") {
+ cx.marked = "keyword"
+ return cont(statement)
+ } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
+ cx.marked = "keyword"
+ if (value == "enum") return cont(enumdef);
+ else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
+ else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+ } else if (isTS && value == "namespace") {
+ cx.marked = "keyword"
+ return cont(pushlex("form"), expression, statement, poplex)
+ } else if (isTS && value == "abstract") {
+ cx.marked = "keyword"
+ return cont(statement)
+ } else {
+ return cont(pushlex("stat"), maybelabel);
+ }
+ }
+ if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
+ block, poplex, poplex, popcontext);
+ if (type == "case") return cont(expression, expect(":"));
+ if (type == "default") return cont(expect(":"));
+ if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
+ if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
+ if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
+ if (type == "async") return cont(statement)
+ if (value == "@") return cont(expression, statement)
+ return pass(pushlex("stat"), expression, expect(";"), poplex);
+ }
+ function maybeCatchBinding(type) {
+ if (type == "(") return cont(funarg, expect(")"))
+ }
+ function expression(type, value) {
+ return expressionInner(type, value, false);
+ }
+ function expressionNoComma(type, value) {
+ return expressionInner(type, value, true);
+ }
+ function parenExpr(type) {
+ if (type != "(") return pass()
+ return cont(pushlex(")"), maybeexpression, expect(")"), poplex)
+ }
+ function expressionInner(type, value, noComma) {
+ if (cx.state.fatArrowAt == cx.stream.start) {
+ var body = noComma ? arrowBodyNoComma : arrowBody;
+ if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
+ else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
+ }
+
+ var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
+ if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
+ if (type == "function") return cont(functiondef, maybeop);
+ if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
+ if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
+ if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
+ if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
+ if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
+ if (type == "{") return contCommasep(objprop, "}", null, maybeop);
+ if (type == "quasi") return pass(quasi, maybeop);
+ if (type == "new") return cont(maybeTarget(noComma));
+ if (type == "import") return cont(expression);
+ return cont();
+ }
+ function maybeexpression(type) {
+ if (type.match(/[;\}\)\],]/)) return pass();
+ return pass(expression);
+ }
+
+ function maybeoperatorComma(type, value) {
+ if (type == ",") return cont(maybeexpression);
+ return maybeoperatorNoComma(type, value, false);
+ }
+ function maybeoperatorNoComma(type, value, noComma) {
+ var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
+ var expr = noComma == false ? expression : expressionNoComma;
+ if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
+ if (type == "operator") {
+ if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
+ if (isTS && value == "<" && cx.stream.match(/^([^<>]|<[^<>]*>)*>\s*\(/, false))
+ return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
+ if (value == "?") return cont(expression, expect(":"), expr);
+ return cont(expr);
+ }
+ if (type == "quasi") { return pass(quasi, me); }
+ if (type == ";") return;
+ if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
+ if (type == ".") return cont(property, me);
+ if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+ if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
+ if (type == "regexp") {
+ cx.state.lastType = cx.marked = "operator"
+ cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
+ return cont(expr)
+ }
+ }
+ function quasi(type, value) {
+ if (type != "quasi") return pass();
+ if (value.slice(value.length - 2) != "${") return cont(quasi);
+ return cont(expression, continueQuasi);
+ }
+ function continueQuasi(type) {
+ if (type == "}") {
+ cx.marked = "string-2";
+ cx.state.tokenize = tokenQuasi;
+ return cont(quasi);
+ }
+ }
+ function arrowBody(type) {
+ findFatArrow(cx.stream, cx.state);
+ return pass(type == "{" ? statement : expression);
+ }
+ function arrowBodyNoComma(type) {
+ findFatArrow(cx.stream, cx.state);
+ return pass(type == "{" ? statement : expressionNoComma);
+ }
+ function maybeTarget(noComma) {
+ return function(type) {
+ if (type == ".") return cont(noComma ? targetNoComma : target);
+ else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
+ else return pass(noComma ? expressionNoComma : expression);
+ };
+ }
+ function target(_, value) {
+ if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
+ }
+ function targetNoComma(_, value) {
+ if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
+ }
+ function maybelabel(type) {
+ if (type == ":") return cont(poplex, statement);
+ return pass(maybeoperatorComma, expect(";"), poplex);
+ }
+ function property(type) {
+ if (type == "variable") {cx.marked = "property"; return cont();}
+ }
+ function objprop(type, value) {
+ if (type == "async") {
+ cx.marked = "property";
+ return cont(objprop);
+ } else if (type == "variable" || cx.style == "keyword") {
+ cx.marked = "property";
+ if (value == "get" || value == "set") return cont(getterSetter);
+ var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
+ if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
+ cx.state.fatArrowAt = cx.stream.pos + m[0].length
+ return cont(afterprop);
+ } else if (type == "number" || type == "string") {
+ cx.marked = jsonldMode ? "property" : (cx.style + " property");
+ return cont(afterprop);
+ } else if (type == "jsonld-keyword") {
+ return cont(afterprop);
+ } else if (isTS && isModifier(value)) {
+ cx.marked = "keyword"
+ return cont(objprop)
+ } else if (type == "[") {
+ return cont(expression, maybetype, expect("]"), afterprop);
+ } else if (type == "spread") {
+ return cont(expressionNoComma, afterprop);
+ } else if (value == "*") {
+ cx.marked = "keyword";
+ return cont(objprop);
+ } else if (type == ":") {
+ return pass(afterprop)
+ }
+ }
+ function getterSetter(type) {
+ if (type != "variable") return pass(afterprop);
+ cx.marked = "property";
+ return cont(functiondef);
+ }
+ function afterprop(type) {
+ if (type == ":") return cont(expressionNoComma);
+ if (type == "(") return pass(functiondef);
+ }
+ function commasep(what, end, sep) {
+ function proceed(type, value) {
+ if (sep ? sep.indexOf(type) > -1 : type == ",") {
+ var lex = cx.state.lexical;
+ if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
+ return cont(function(type, value) {
+ if (type == end || value == end) return pass()
+ return pass(what)
+ }, proceed);
+ }
+ if (type == end || value == end) return cont();
+ if (sep && sep.indexOf(";") > -1) return pass(what)
+ return cont(expect(end));
+ }
+ return function(type, value) {
+ if (type == end || value == end) return cont();
+ return pass(what, proceed);
+ };
+ }
+ function contCommasep(what, end, info) {
+ for (var i = 3; i < arguments.length; i++)
+ cx.cc.push(arguments[i]);
+ return cont(pushlex(end, info), commasep(what, end), poplex);
+ }
+ function block(type) {
+ if (type == "}") return cont();
+ return pass(statement, block);
+ }
+ function maybetype(type, value) {
+ if (isTS) {
+ if (type == ":") return cont(typeexpr);
+ if (value == "?") return cont(maybetype);
+ }
+ }
+ function maybetypeOrIn(type, value) {
+ if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
+ }
+ function mayberettype(type) {
+ if (isTS && type == ":") {
+ if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
+ else return cont(typeexpr)
+ }
+ }
+ function isKW(_, value) {
+ if (value == "is") {
+ cx.marked = "keyword"
+ return cont()
+ }
+ }
+ function typeexpr(type, value) {
+ if (value == "keyof" || value == "typeof" || value == "infer") {
+ cx.marked = "keyword"
+ return cont(value == "typeof" ? expressionNoComma : typeexpr)
+ }
+ if (type == "variable" || value == "void") {
+ cx.marked = "type"
+ return cont(afterType)
+ }
+ if (value == "|" || value == "&") return cont(typeexpr)
+ if (type == "string" || type == "number" || type == "atom") return cont(afterType);
+ if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
+ if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
+ if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
+ if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
+ }
+ function maybeReturnType(type) {
+ if (type == "=>") return cont(typeexpr)
+ }
+ function typeprop(type, value) {
+ if (type == "variable" || cx.style == "keyword") {
+ cx.marked = "property"
+ return cont(typeprop)
+ } else if (value == "?" || type == "number" || type == "string") {
+ return cont(typeprop)
+ } else if (type == ":") {
+ return cont(typeexpr)
+ } else if (type == "[") {
+ return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
+ } else if (type == "(") {
+ return pass(functiondecl, typeprop)
+ }
+ }
+ function typearg(type, value) {
+ if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
+ if (type == ":") return cont(typeexpr)
+ if (type == "spread") return cont(typearg)
+ return pass(typeexpr)
+ }
+ function afterType(type, value) {
+ if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ if (value == "|" || type == "." || value == "&") return cont(typeexpr)
+ if (type == "[") return cont(typeexpr, expect("]"), afterType)
+ if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
+ if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
+ }
+ function maybeTypeArgs(_, value) {
+ if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+ }
+ function typeparam() {
+ return pass(typeexpr, maybeTypeDefault)
+ }
+ function maybeTypeDefault(_, value) {
+ if (value == "=") return cont(typeexpr)
+ }
+ function vardef(_, value) {
+ if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
+ return pass(pattern, maybetype, maybeAssign, vardefCont);
+ }
+ function pattern(type, value) {
+ if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
+ if (type == "variable") { register(value); return cont(); }
+ if (type == "spread") return cont(pattern);
+ if (type == "[") return contCommasep(eltpattern, "]");
+ if (type == "{") return contCommasep(proppattern, "}");
+ }
+ function proppattern(type, value) {
+ if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
+ register(value);
+ return cont(maybeAssign);
+ }
+ if (type == "variable") cx.marked = "property";
+ if (type == "spread") return cont(pattern);
+ if (type == "}") return pass();
+ if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
+ return cont(expect(":"), pattern, maybeAssign);
+ }
+ function eltpattern() {
+ return pass(pattern, maybeAssign)
+ }
+ function maybeAssign(_type, value) {
+ if (value == "=") return cont(expressionNoComma);
+ }
+ function vardefCont(type) {
+ if (type == ",") return cont(vardef);
+ }
+ function maybeelse(type, value) {
+ if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
+ }
+ function forspec(type, value) {
+ if (value == "await") return cont(forspec);
+ if (type == "(") return cont(pushlex(")"), forspec1, poplex);
+ }
+ function forspec1(type) {
+ if (type == "var") return cont(vardef, forspec2);
+ if (type == "variable") return cont(forspec2);
+ return pass(forspec2)
+ }
+ function forspec2(type, value) {
+ if (type == ")") return cont()
+ if (type == ";") return cont(forspec2)
+ if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
+ return pass(expression, forspec2)
+ }
+ function functiondef(type, value) {
+ if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
+ if (type == "variable") {register(value); return cont(functiondef);}
+ if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
+ if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
+ }
+ function functiondecl(type, value) {
+ if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
+ if (type == "variable") {register(value); return cont(functiondecl);}
+ if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
+ if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
+ }
+ function typename(type, value) {
+ if (type == "keyword" || type == "variable") {
+ cx.marked = "type"
+ return cont(typename)
+ } else if (value == "<") {
+ return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
+ }
+ }
+ function funarg(type, value) {
+ if (value == "@") cont(expression, funarg)
+ if (type == "spread") return cont(funarg);
+ if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
+ if (isTS && type == "this") return cont(maybetype, maybeAssign)
+ return pass(pattern, maybetype, maybeAssign);
+ }
+ function classExpression(type, value) {
+ // Class expressions may have an optional name.
+ if (type == "variable") return className(type, value);
+ return classNameAfter(type, value);
+ }
+ function className(type, value) {
+ if (type == "variable") {register(value); return cont(classNameAfter);}
+ }
+ function classNameAfter(type, value) {
+ if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+ if (value == "extends" || value == "implements" || (isTS && type == ",")) {
+ if (value == "implements") cx.marked = "keyword";
+ return cont(isTS ? typeexpr : expression, classNameAfter);
+ }
+ if (type == "{") return cont(pushlex("}"), classBody, poplex);
+ }
+ function classBody(type, value) {
+ if (type == "async" ||
+ (type == "variable" &&
+ (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
+ cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
+ cx.marked = "keyword";
+ return cont(classBody);
+ }
+ if (type == "variable" || cx.style == "keyword") {
+ cx.marked = "property";
+ return cont(classfield, classBody);
+ }
+ if (type == "number" || type == "string") return cont(classfield, classBody);
+ if (type == "[")
+ return cont(expression, maybetype, expect("]"), classfield, classBody)
+ if (value == "*") {
+ cx.marked = "keyword";
+ return cont(classBody);
+ }
+ if (isTS && type == "(") return pass(functiondecl, classBody)
+ if (type == ";" || type == ",") return cont(classBody);
+ if (type == "}") return cont();
+ if (value == "@") return cont(expression, classBody)
+ }
+ function classfield(type, value) {
+ if (value == "?") return cont(classfield)
+ if (type == ":") return cont(typeexpr, maybeAssign)
+ if (value == "=") return cont(expressionNoComma)
+ var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
+ return pass(isInterface ? functiondecl : functiondef)
+ }
+ function afterExport(type, value) {
+ if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
+ if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
+ if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
+ return pass(statement);
+ }
+ function exportField(type, value) {
+ if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
+ if (type == "variable") return pass(expressionNoComma, exportField);
+ }
+ function afterImport(type) {
+ if (type == "string") return cont();
+ if (type == "(") return pass(expression);
+ return pass(importSpec, maybeMoreImports, maybeFrom);
+ }
+ function importSpec(type, value) {
+ if (type == "{") return contCommasep(importSpec, "}");
+ if (type == "variable") register(value);
+ if (value == "*") cx.marked = "keyword";
+ return cont(maybeAs);
+ }
+ function maybeMoreImports(type) {
+ if (type == ",") return cont(importSpec, maybeMoreImports)
+ }
+ function maybeAs(_type, value) {
+ if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
+ }
+ function maybeFrom(_type, value) {
+ if (value == "from") { cx.marked = "keyword"; return cont(expression); }
+ }
+ function arrayLiteral(type) {
+ if (type == "]") return cont();
+ return pass(commasep(expressionNoComma, "]"));
+ }
+ function enumdef() {
+ return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
+ }
+ function enummember() {
+ return pass(pattern, maybeAssign);
+ }
+
+ function isContinuedStatement(state, textAfter) {
+ return state.lastType == "operator" || state.lastType == "," ||
+ isOperatorChar.test(textAfter.charAt(0)) ||
+ /[,.]/.test(textAfter.charAt(0));
+ }
+
+ function expressionAllowed(stream, state, backUp) {
+ return state.tokenize == tokenBase &&
+ /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
+ (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
+ }
+
+ // Interface
+
+ return {
+ startState: function(basecolumn) {
+ var state = {
+ tokenize: tokenBase,
+ lastType: "sof",
+ cc: [],
+ lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
+ localVars: parserConfig.localVars,
+ context: parserConfig.localVars && new Context(null, null, false),
+ indented: basecolumn || 0
+ };
+ if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
+ state.globalVars = parserConfig.globalVars;
+ return state;
+ },
+
+ token: function(stream, state) {
+ if (stream.sol()) {
+ if (!state.lexical.hasOwnProperty("align"))
+ state.lexical.align = false;
+ state.indented = stream.indentation();
+ findFatArrow(stream, state);
+ }
+ if (state.tokenize != tokenComment && stream.eatSpace()) return null;
+ var style = state.tokenize(stream, state);
+ if (type == "comment") return style;
+ state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
+ return parseJS(state, style, type, content, stream);
+ },
+
+ indent: function(state, textAfter) {
+ if (state.tokenize == tokenComment || state.tokenize == tokenQuasi) return CodeMirror.Pass;
+ if (state.tokenize != tokenBase) return 0;
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
+ // Kludge to prevent 'maybelse' from blocking lexical scope pops
+ if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
+ var c = state.cc[i];
+ if (c == poplex) lexical = lexical.prev;
+ else if (c != maybeelse) break;
+ }
+ while ((lexical.type == "stat" || lexical.type == "form") &&
+ (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
+ (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
+ !/^[,\.=+\-*:?[\(]/.test(textAfter))))
+ lexical = lexical.prev;
+ if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
+ lexical = lexical.prev;
+ var type = lexical.type, closing = firstChar == type;
+
+ if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
+ else if (type == "form" && firstChar == "{") return lexical.indented;
+ else if (type == "form") return lexical.indented + indentUnit;
+ else if (type == "stat")
+ return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
+ else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
+ return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
+ else if (lexical.align) return lexical.column + (closing ? 0 : 1);
+ else return lexical.indented + (closing ? 0 : indentUnit);
+ },
+
+ electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
+ blockCommentStart: jsonMode ? null : "/*",
+ blockCommentEnd: jsonMode ? null : "*/",
+ blockCommentContinue: jsonMode ? null : " * ",
+ lineComment: jsonMode ? null : "//",
+ fold: "brace",
+ closeBrackets: "()[]{}''\"\"``",
+
+ helperType: jsonMode ? "json" : "javascript",
+ jsonldMode: jsonldMode,
+ jsonMode: jsonMode,
+
+ expressionAllowed: expressionAllowed,
+
+ skipExpression: function(state) {
+ var top = state.cc[state.cc.length - 1]
+ if (top == expression || top == expressionNoComma) state.cc.pop()
+ }
+ };
+});
+
+CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
+
+CodeMirror.defineMIME("text/javascript", "javascript");
+CodeMirror.defineMIME("text/ecmascript", "javascript");
+CodeMirror.defineMIME("application/javascript", "javascript");
+CodeMirror.defineMIME("application/x-javascript", "javascript");
+CodeMirror.defineMIME("application/ecmascript", "javascript");
+CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
+CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
+CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
+CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/jsx/jsx.js b/devtools/client/shared/sourceeditor/codemirror/mode/jsx/jsx.js
new file mode 100644
index 0000000000..3a69cbbf3b
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/jsx/jsx.js
@@ -0,0 +1,148 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/mode/xml/xml.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/mode/javascript/javascript.js"))
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript"], mod)
+ else // Plain browser env
+ mod(CodeMirror)
+})(function(CodeMirror) {
+ "use strict"
+
+ // Depth means the amount of open braces in JS context, in XML
+ // context 0 means not in tag, 1 means in tag, and 2 means in tag
+ // and js block comment.
+ function Context(state, mode, depth, prev) {
+ this.state = state; this.mode = mode; this.depth = depth; this.prev = prev
+ }
+
+ function copyContext(context) {
+ return new Context(CodeMirror.copyState(context.mode, context.state),
+ context.mode,
+ context.depth,
+ context.prev && copyContext(context.prev))
+ }
+
+ CodeMirror.defineMode("jsx", function(config, modeConfig) {
+ var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
+ var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
+
+ function flatXMLIndent(state) {
+ var tagName = state.tagName
+ state.tagName = null
+ var result = xmlMode.indent(state, "", "")
+ state.tagName = tagName
+ return result
+ }
+
+ function token(stream, state) {
+ if (state.context.mode == xmlMode)
+ return xmlToken(stream, state, state.context)
+ else
+ return jsToken(stream, state, state.context)
+ }
+
+ function xmlToken(stream, state, cx) {
+ if (cx.depth == 2) { // Inside a JS /* */ comment
+ if (stream.match(/^.*?\*\//)) cx.depth = 1
+ else stream.skipToEnd()
+ return "comment"
+ }
+
+ if (stream.peek() == "{") {
+ xmlMode.skipAttribute(cx.state)
+
+ var indent = flatXMLIndent(cx.state), xmlContext = cx.state.context
+ // If JS starts on same line as tag
+ if (xmlContext && stream.match(/^[^>]*>\s*$/, false)) {
+ while (xmlContext.prev && !xmlContext.startOfLine)
+ xmlContext = xmlContext.prev
+ // If tag starts the line, use XML indentation level
+ if (xmlContext.startOfLine) indent -= config.indentUnit
+ // Else use JS indentation level
+ else if (cx.prev.state.lexical) indent = cx.prev.state.lexical.indented
+ // Else if inside of tag
+ } else if (cx.depth == 1) {
+ indent += config.indentUnit
+ }
+
+ state.context = new Context(CodeMirror.startState(jsMode, indent),
+ jsMode, 0, state.context)
+ return null
+ }
+
+ if (cx.depth == 1) { // Inside of tag
+ if (stream.peek() == "<") { // Tag inside of tag
+ xmlMode.skipAttribute(cx.state)
+ state.context = new Context(CodeMirror.startState(xmlMode, flatXMLIndent(cx.state)),
+ xmlMode, 0, state.context)
+ return null
+ } else if (stream.match("//")) {
+ stream.skipToEnd()
+ return "comment"
+ } else if (stream.match("/*")) {
+ cx.depth = 2
+ return token(stream, state)
+ }
+ }
+
+ var style = xmlMode.token(stream, cx.state), cur = stream.current(), stop
+ if (/\btag\b/.test(style)) {
+ if (/>$/.test(cur)) {
+ if (cx.state.context) cx.depth = 0
+ else state.context = state.context.prev
+ } else if (/^</.test(cur)) {
+ cx.depth = 1
+ }
+ } else if (!style && (stop = cur.indexOf("{")) > -1) {
+ stream.backUp(cur.length - stop)
+ }
+ return style
+ }
+
+ function jsToken(stream, state, cx) {
+ if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) {
+ jsMode.skipExpression(cx.state)
+ state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "", "")),
+ xmlMode, 0, state.context)
+ return null
+ }
+
+ var style = jsMode.token(stream, cx.state)
+ if (!style && cx.depth != null) {
+ var cur = stream.current()
+ if (cur == "{") {
+ cx.depth++
+ } else if (cur == "}") {
+ if (--cx.depth == 0) state.context = state.context.prev
+ }
+ }
+ return style
+ }
+
+ return {
+ startState: function() {
+ return {context: new Context(CodeMirror.startState(jsMode), jsMode)}
+ },
+
+ copyState: function(state) {
+ return {context: copyContext(state.context)}
+ },
+
+ token: token,
+
+ indent: function(state, textAfter, fullLine) {
+ return state.context.mode.indent(state.context.state, textAfter, fullLine)
+ },
+
+ innerMode: function(state) {
+ return state.context
+ }
+ }
+ }, "xml", "javascript")
+
+ CodeMirror.defineMIME("text/jsx", "jsx")
+ CodeMirror.defineMIME("text/typescript-jsx", {name: "jsx", base: {name: "javascript", typescript: true}})
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/rust/rust.js b/devtools/client/shared/sourceeditor/codemirror/mode/rust/rust.js
new file mode 100644
index 0000000000..8139f5a1da
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/rust/rust.js
@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"), require("resource://devtools/client/shared/sourceeditor/codemirror/mode/simple/simple.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror", "../../mode/simple/simple"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineSimpleMode("rust",{
+ start: [
+ // string and byte string
+ {regex: /b?"/, token: "string", next: "string"},
+ // raw string and raw byte string
+ {regex: /b?r"/, token: "string", next: "string_raw"},
+ {regex: /b?r#+"/, token: "string", next: "string_raw_hash"},
+ // character
+ {regex: /'(?:[^'\\]|\\(?:[nrt0'"]|x[\da-fA-F]{2}|u\{[\da-fA-F]{6}\}))'/, token: "string-2"},
+ // byte
+ {regex: /b'(?:[^']|\\(?:['\\nrt0]|x[\da-fA-F]{2}))'/, token: "string-2"},
+
+ {regex: /(?:(?:[0-9][0-9_]*)(?:(?:[Ee][+-]?[0-9_]+)|\.[0-9_]+(?:[Ee][+-]?[0-9_]+)?)(?:f32|f64)?)|(?:0(?:b[01_]+|(?:o[0-7_]+)|(?:x[0-9a-fA-F_]+))|(?:[0-9][0-9_]*))(?:u8|u16|u32|u64|i8|i16|i32|i64|isize|usize)?/,
+ token: "number"},
+ {regex: /(let(?:\s+mut)?|fn|enum|mod|struct|type)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/, token: ["keyword", null, "def"]},
+ {regex: /(?:abstract|alignof|as|box|break|continue|const|crate|do|else|enum|extern|fn|for|final|if|impl|in|loop|macro|match|mod|move|offsetof|override|priv|proc|pub|pure|ref|return|self|sizeof|static|struct|super|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\b/, token: "keyword"},
+ {regex: /\b(?:Self|isize|usize|char|bool|u8|u16|u32|u64|f16|f32|f64|i8|i16|i32|i64|str|Option)\b/, token: "atom"},
+ {regex: /\b(?:true|false|Some|None|Ok|Err)\b/, token: "builtin"},
+ {regex: /\b(fn)(\s+)([a-zA-Z_][a-zA-Z0-9_]*)/,
+ token: ["keyword", null ,"def"]},
+ {regex: /#!?\[.*\]/, token: "meta"},
+ {regex: /\/\/.*/, token: "comment"},
+ {regex: /\/\*/, token: "comment", next: "comment"},
+ {regex: /[-+\/*=<>!]+/, token: "operator"},
+ {regex: /[a-zA-Z_]\w*!/,token: "variable-3"},
+ {regex: /[a-zA-Z_]\w*/, token: "variable"},
+ {regex: /[\{\[\(]/, indent: true},
+ {regex: /[\}\]\)]/, dedent: true}
+ ],
+ string: [
+ {regex: /"/, token: "string", next: "start"},
+ {regex: /(?:[^\\"]|\\(?:.|$))*/, token: "string"}
+ ],
+ string_raw: [
+ {regex: /"/, token: "string", next: "start"},
+ {regex: /[^"]*/, token: "string"}
+ ],
+ string_raw_hash: [
+ {regex: /"#+/, token: "string", next: "start"},
+ {regex: /(?:[^"]|"(?!#))*/, token: "string"}
+ ],
+ comment: [
+ {regex: /.*?\*\//, token: "comment", next: "start"},
+ {regex: /.*/, token: "comment"}
+ ],
+ meta: {
+ dontIndentStates: ["comment"],
+ electricInput: /^\s*\}$/,
+ blockCommentStart: "/*",
+ blockCommentEnd: "*/",
+ lineComment: "//",
+ fold: "brace"
+ }
+});
+
+
+CodeMirror.defineMIME("text/x-rustsrc", "rust");
+CodeMirror.defineMIME("text/rust", "rust");
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/simple/simple.js b/devtools/client/shared/sourceeditor/codemirror/mode/simple/simple.js
new file mode 100644
index 0000000000..bc2f7b5670
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/simple/simple.js
@@ -0,0 +1,216 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineSimpleMode = function(name, states) {
+ CodeMirror.defineMode(name, function(config) {
+ return CodeMirror.simpleMode(config, states);
+ });
+ };
+
+ CodeMirror.simpleMode = function(config, states) {
+ ensureState(states, "start");
+ var states_ = {}, meta = states.meta || {}, hasIndentation = false;
+ for (var state in states) if (state != meta && states.hasOwnProperty(state)) {
+ var list = states_[state] = [], orig = states[state];
+ for (var i = 0; i < orig.length; i++) {
+ var data = orig[i];
+ list.push(new Rule(data, states));
+ if (data.indent || data.dedent) hasIndentation = true;
+ }
+ }
+ var mode = {
+ startState: function() {
+ return {state: "start", pending: null,
+ local: null, localState: null,
+ indent: hasIndentation ? [] : null};
+ },
+ copyState: function(state) {
+ var s = {state: state.state, pending: state.pending,
+ local: state.local, localState: null,
+ indent: state.indent && state.indent.slice(0)};
+ if (state.localState)
+ s.localState = CodeMirror.copyState(state.local.mode, state.localState);
+ if (state.stack)
+ s.stack = state.stack.slice(0);
+ for (var pers = state.persistentStates; pers; pers = pers.next)
+ s.persistentStates = {mode: pers.mode,
+ spec: pers.spec,
+ state: pers.state == state.localState ? s.localState : CodeMirror.copyState(pers.mode, pers.state),
+ next: s.persistentStates};
+ return s;
+ },
+ token: tokenFunction(states_, config),
+ innerMode: function(state) { return state.local && {mode: state.local.mode, state: state.localState}; },
+ indent: indentFunction(states_, meta)
+ };
+ if (meta) for (var prop in meta) if (meta.hasOwnProperty(prop))
+ mode[prop] = meta[prop];
+ return mode;
+ };
+
+ function ensureState(states, name) {
+ if (!states.hasOwnProperty(name))
+ throw new Error("Undefined state " + name + " in simple mode");
+ }
+
+ function toRegex(val, caret) {
+ if (!val) return /(?:)/;
+ var flags = "";
+ if (val instanceof RegExp) {
+ if (val.ignoreCase) flags = "i";
+ val = val.source;
+ } else {
+ val = String(val);
+ }
+ return new RegExp((caret === false ? "" : "^") + "(?:" + val + ")", flags);
+ }
+
+ function asToken(val) {
+ if (!val) return null;
+ if (val.apply) return val
+ if (typeof val == "string") return val.replace(/\./g, " ");
+ var result = [];
+ for (var i = 0; i < val.length; i++)
+ result.push(val[i] && val[i].replace(/\./g, " "));
+ return result;
+ }
+
+ function Rule(data, states) {
+ if (data.next || data.push) ensureState(states, data.next || data.push);
+ this.regex = toRegex(data.regex);
+ this.token = asToken(data.token);
+ this.data = data;
+ }
+
+ function tokenFunction(states, config) {
+ return function(stream, state) {
+ if (state.pending) {
+ var pend = state.pending.shift();
+ if (state.pending.length == 0) state.pending = null;
+ stream.pos += pend.text.length;
+ return pend.token;
+ }
+
+ if (state.local) {
+ if (state.local.end && stream.match(state.local.end)) {
+ var tok = state.local.endToken || null;
+ state.local = state.localState = null;
+ return tok;
+ } else {
+ var tok = state.local.mode.token(stream, state.localState), m;
+ if (state.local.endScan && (m = state.local.endScan.exec(stream.current())))
+ stream.pos = stream.start + m.index;
+ return tok;
+ }
+ }
+
+ var curState = states[state.state];
+ for (var i = 0; i < curState.length; i++) {
+ var rule = curState[i];
+ var matches = (!rule.data.sol || stream.sol()) && stream.match(rule.regex);
+ if (matches) {
+ if (rule.data.next) {
+ state.state = rule.data.next;
+ } else if (rule.data.push) {
+ (state.stack || (state.stack = [])).push(state.state);
+ state.state = rule.data.push;
+ } else if (rule.data.pop && state.stack && state.stack.length) {
+ state.state = state.stack.pop();
+ }
+
+ if (rule.data.mode)
+ enterLocalMode(config, state, rule.data.mode, rule.token);
+ if (rule.data.indent)
+ state.indent.push(stream.indentation() + config.indentUnit);
+ if (rule.data.dedent)
+ state.indent.pop();
+ var token = rule.token
+ if (token && token.apply) token = token(matches)
+ if (matches.length > 2 && rule.token && typeof rule.token != "string") {
+ state.pending = [];
+ for (var j = 2; j < matches.length; j++)
+ if (matches[j])
+ state.pending.push({text: matches[j], token: rule.token[j - 1]});
+ stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));
+ return token[0];
+ } else if (token && token.join) {
+ return token[0];
+ } else {
+ return token;
+ }
+ }
+ }
+ stream.next();
+ return null;
+ };
+ }
+
+ function cmp(a, b) {
+ if (a === b) return true;
+ if (!a || typeof a != "object" || !b || typeof b != "object") return false;
+ var props = 0;
+ for (var prop in a) if (a.hasOwnProperty(prop)) {
+ if (!b.hasOwnProperty(prop) || !cmp(a[prop], b[prop])) return false;
+ props++;
+ }
+ for (var prop in b) if (b.hasOwnProperty(prop)) props--;
+ return props == 0;
+ }
+
+ function enterLocalMode(config, state, spec, token) {
+ var pers;
+ if (spec.persistent) for (var p = state.persistentStates; p && !pers; p = p.next)
+ if (spec.spec ? cmp(spec.spec, p.spec) : spec.mode == p.mode) pers = p;
+ var mode = pers ? pers.mode : spec.mode || CodeMirror.getMode(config, spec.spec);
+ var lState = pers ? pers.state : CodeMirror.startState(mode);
+ if (spec.persistent && !pers)
+ state.persistentStates = {mode: mode, spec: spec.spec, state: lState, next: state.persistentStates};
+
+ state.localState = lState;
+ state.local = {mode: mode,
+ end: spec.end && toRegex(spec.end),
+ endScan: spec.end && spec.forceEnd !== false && toRegex(spec.end, false),
+ endToken: token && token.join ? token[token.length - 1] : token};
+ }
+
+ function indexOf(val, arr) {
+ for (var i = 0; i < arr.length; i++) if (arr[i] === val) return true;
+ }
+
+ function indentFunction(states, meta) {
+ return function(state, textAfter, line) {
+ if (state.local && state.local.mode.indent)
+ return state.local.mode.indent(state.localState, textAfter, line);
+ if (state.indent == null || state.local || meta.dontIndentStates && indexOf(state.state, meta.dontIndentStates) > -1)
+ return CodeMirror.Pass;
+
+ var pos = state.indent.length - 1, rules = states[state.state];
+ scan: for (;;) {
+ for (var i = 0; i < rules.length; i++) {
+ var rule = rules[i];
+ if (rule.data.dedent && rule.data.dedentIfLineStart !== false) {
+ var m = rule.regex.exec(textAfter);
+ if (m && m[0]) {
+ pos--;
+ if (rule.next || rule.push) rules = states[rule.next || rule.push];
+ textAfter = textAfter.slice(m[0].length);
+ continue scan;
+ }
+ }
+ }
+ break;
+ }
+ return pos < 0 ? 0 : state.indent[pos];
+ };
+ }
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/wasm/wasm.js b/devtools/client/shared/sourceeditor/codemirror/mode/wasm/wasm.js
new file mode 100644
index 0000000000..39122d4bd4
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/wasm/wasm.js
@@ -0,0 +1,203 @@
+/* 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/. */
+
+// WebAssembly experimental syntax highlight add-on for CodeMirror.
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define(["../../lib/codemirror"], factory);
+ } else if (typeof exports !== 'undefined') {
+ factory(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ } else {
+ factory(root.CodeMirror);
+ }
+}(this, function (CodeMirror) {
+"use strict";
+
+var isWordChar = /[\w\$_\.\\\/@]/;
+
+function createLookupTable(list) {
+ var obj = Object.create(null);
+ list.forEach(function (key) {
+ obj[key] = true;
+ });
+ return obj;
+}
+
+CodeMirror.defineMode("wasm", function() {
+ var keywords = createLookupTable([
+ "function", "import", "export", "table", "memory", "segment", "as", "type",
+ "of", "from", "typeof", "br", "br_if", "loop", "br_table", "if", "else",
+ "call", "call_import", "call_indirect", "nop", "unreachable", "var",
+ "align", "select", "return"]);
+ var builtins = createLookupTable([
+ "i32:8", "i32:8u", "i32:8s", "i32:16", "i32:16u", "i32:16s",
+ "i64:8", "i64:8u", "i64:8s", "i64:16", "i64:16u", "i64:16s",
+ "i64:32", "i64:32u", "i64:32s",
+ "i32.add", "i32.sub", "i32.mul", "i32.div_s", "i32.div_u",
+ "i32.rem_s", "i32.rem_u", "i32.and", "i32.or", "i32.xor",
+ "i32.shl", "i32.shr_u", "i32.shr_s", "i32.rotr", "i32.rotl",
+ "i32.eq", "i32.ne", "i32.lt_s", "i32.le_s", "i32.lt_u",
+ "i32.le_u", "i32.gt_s", "i32.ge_s", "i32.gt_u", "i32.ge_u",
+ "i32.clz", "i32.ctz", "i32.popcnt", "i32.eqz", "i64.add",
+ "i64.sub", "i64.mul", "i64.div_s", "i64.div_u", "i64.rem_s",
+ "i64.rem_u", "i64.and", "i64.or", "i64.xor", "i64.shl",
+ "i64.shr_u", "i64.shr_s", "i64.rotr", "i64.rotl", "i64.eq",
+ "i64.ne", "i64.lt_s", "i64.le_s", "i64.lt_u", "i64.le_u",
+ "i64.gt_s", "i64.ge_s", "i64.gt_u", "i64.ge_u", "i64.clz",
+ "i64.ctz", "i64.popcnt", "i64.eqz", "f32.add", "f32.sub",
+ "f32.mul", "f32.div", "f32.min", "f32.max", "f32.abs",
+ "f32.neg", "f32.copysign", "f32.ceil", "f32.floor", "f32.trunc",
+ "f32.nearest", "f32.sqrt", "f32.eq", "f32.ne", "f32.lt",
+ "f32.le", "f32.gt", "f32.ge", "f64.add", "f64.sub", "f64.mul",
+ "f64.div", "f64.min", "f64.max", "f64.abs", "f64.neg",
+ "f64.copysign", "f64.ceil", "f64.floor", "f64.trunc", "f64.nearest",
+ "f64.sqrt", "f64.eq", "f64.ne", "f64.lt", "f64.le", "f64.gt",
+ "f64.ge", "i32.trunc_s/f32", "i32.trunc_s/f64", "i32.trunc_u/f32",
+ "i32.trunc_u/f64", "i32.wrap/i64", "i64.trunc_s/f32",
+ "i64.trunc_s/f64", "i64.trunc_u/f32", "i64.trunc_u/f64",
+ "i64.extend_s/i32", "i64.extend_u/i32", "f32.convert_s/i32",
+ "f32.convert_u/i32", "f32.convert_s/i64", "f32.convert_u/i64",
+ "f32.demote/f64", "f32.reinterpret/i32", "f64.convert_s/i32",
+ "f64.convert_u/i32", "f64.convert_s/i64", "f64.convert_u/i64",
+ "f64.promote/f32", "f64.reinterpret/i64", "i32.reinterpret/f32",
+ "i64.reinterpret/f64"]);
+ var dataTypes = createLookupTable(["i32", "i64", "f32", "f64"]);
+ var isUnaryOperator = /[\-!]/;
+ var operators = createLookupTable([
+ "+", "-", "*", "/", "/s", "/u", "%", "%s", "%u",
+ "<<", ">>u", ">>s", ">=", "<=", "==", "!=",
+ "<s", "<u", "<=s", "<=u", ">=s", ">=u", ">s", ">u",
+ "<", ">", "=", "&", "|", "^", "!"]);
+
+ function tokenBase(stream, state) {
+ var ch = stream.next();
+ if (ch === "$") {
+ stream.eatWhile(isWordChar);
+ return "variable";
+ }
+ if (ch === "@") {
+ stream.eatWhile(isWordChar);
+ return "meta";
+ }
+ if (ch === '"') {
+ state.tokenize = tokenString(ch);
+ return state.tokenize(stream, state);
+ }
+ if (ch == "/") {
+ if (stream.eat("*")) {
+ state.tokenize = tokenComment;
+ return tokenComment(stream, state);
+ } else if (stream.eat("/")) {
+ stream.skipToEnd();
+ return "comment";
+ }
+ }
+ if (/\d/.test(ch) ||
+ ((ch === "-" || ch === "+") && /\d/.test(stream.peek()))) {
+ stream.eatWhile(/[\w\._\-+]/);
+ return "number";
+ }
+ if (/[\[\]\(\)\{\},:]/.test(ch)) {
+ return null;
+ }
+ if (isUnaryOperator.test(ch)) {
+ return "operator";
+ }
+ stream.eatWhile(isWordChar);
+ var word = stream.current();
+
+ if (word in operators) {
+ return "operator";
+ }
+ if (word in keywords){
+ return "keyword";
+ }
+ if (word in dataTypes) {
+ if (!stream.eat(":")) {
+ return "builtin";
+ }
+ stream.eatWhile(isWordChar);
+ word = stream.current();
+ // fall thru for "builtin" check
+ }
+ if (word in builtins) {
+ return "builtin";
+ }
+
+ if (word === "Temporary") {
+ // Nightly has header with some text graphics -- skipping it.
+ state.tokenize = tokenTemporary;
+ return state.tokenize(stream, state);
+ }
+ return null;
+ }
+
+ function tokenComment(stream, state) {
+ state.commentDepth = 1;
+ var next;
+ while ((next = stream.next()) != null) {
+ if (next === "*" && stream.eat("/")) {
+ if (--state.commentDepth === 0) {
+ state.tokenize = null;
+ return "comment";
+ }
+ }
+ if (next === "/" && stream.eat("*")) {
+ // Nested comment
+ state.commentDepth++;
+ }
+ }
+ return "comment";
+ }
+
+ function tokenTemporary(stream, state) {
+ var next, endState = state.commentState;
+ // Skipping until "text support (Work In Progress):" is found.
+ while ((next = stream.next()) != null) {
+ if (endState === 0 && next === "t") {
+ endState = 1;
+ } else if (endState === 1 && next === ":") {
+ state.tokenize = null;
+ state.commentState = 0;
+ endState = 2;
+ return "comment";
+ }
+ }
+ state.commentState = endState;
+ return "comment";
+ }
+
+ function tokenString(quote) {
+ return function(stream, state) {
+ var escaped = false, next, end = false;
+ while ((next = stream.next()) != null) {
+ if (next == quote && !escaped) {
+ state.tokenize = null;
+ return "string";
+ }
+ escaped = !escaped && next === "\\";
+ }
+ return "string";
+ };
+ }
+
+ return {
+ startState: function() {
+ return {tokenize: null, commentState: 0, commentDepth: 0};
+ },
+
+ token: function(stream, state) {
+ if (stream.eatSpace()) return null;
+ var style = (state.tokenize || tokenBase)(stream, state);
+ return style;
+ }
+ };
+});
+
+CodeMirror.registerHelper("wordChars", "wasm", isWordChar);
+
+CodeMirror.defineMIME("text/wasm", "wasm");
+
+}));
diff --git a/devtools/client/shared/sourceeditor/codemirror/mode/xml/xml.js b/devtools/client/shared/sourceeditor/codemirror/mode/xml/xml.js
new file mode 100644
index 0000000000..5b3320297a
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mode/xml/xml.js
@@ -0,0 +1,413 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/sourceeditor/codemirror/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+var htmlConfig = {
+ autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
+ 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
+ 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
+ 'track': true, 'wbr': true, 'menuitem': true},
+ implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
+ 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
+ 'th': true, 'tr': true},
+ contextGrabbers: {
+ 'dd': {'dd': true, 'dt': true},
+ 'dt': {'dd': true, 'dt': true},
+ 'li': {'li': true},
+ 'option': {'option': true, 'optgroup': true},
+ 'optgroup': {'optgroup': true},
+ 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
+ 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
+ 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
+ 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
+ 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
+ 'rp': {'rp': true, 'rt': true},
+ 'rt': {'rp': true, 'rt': true},
+ 'tbody': {'tbody': true, 'tfoot': true},
+ 'td': {'td': true, 'th': true},
+ 'tfoot': {'tbody': true},
+ 'th': {'td': true, 'th': true},
+ 'thead': {'tbody': true, 'tfoot': true},
+ 'tr': {'tr': true}
+ },
+ doNotIndent: {"pre": true},
+ allowUnquoted: true,
+ allowMissing: true,
+ caseFold: true
+}
+
+var xmlConfig = {
+ autoSelfClosers: {},
+ implicitlyClosed: {},
+ contextGrabbers: {},
+ doNotIndent: {},
+ allowUnquoted: false,
+ allowMissing: false,
+ allowMissingTagName: false,
+ caseFold: false
+}
+
+CodeMirror.defineMode("xml", function(editorConf, config_) {
+ var indentUnit = editorConf.indentUnit
+ var config = {}
+ var defaults = config_.htmlMode ? htmlConfig : xmlConfig
+ for (var prop in defaults) config[prop] = defaults[prop]
+ for (var prop in config_) config[prop] = config_[prop]
+
+ // Return variables for tokenizers
+ var type, setStyle;
+
+ function inText(stream, state) {
+ function chain(parser) {
+ state.tokenize = parser;
+ return parser(stream, state);
+ }
+
+ var ch = stream.next();
+ if (ch == "<") {
+ if (stream.eat("!")) {
+ if (stream.eat("[")) {
+ if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
+ else return null;
+ } else if (stream.match("--")) {
+ return chain(inBlock("comment", "-->"));
+ } else if (stream.match("DOCTYPE", true, true)) {
+ stream.eatWhile(/[\w\._\-]/);
+ return chain(doctype(1));
+ } else {
+ return null;
+ }
+ } else if (stream.eat("?")) {
+ stream.eatWhile(/[\w\._\-]/);
+ state.tokenize = inBlock("meta", "?>");
+ return "meta";
+ } else {
+ type = stream.eat("/") ? "closeTag" : "openTag";
+ state.tokenize = inTag;
+ return "tag bracket";
+ }
+ } else if (ch == "&") {
+ var ok;
+ if (stream.eat("#")) {
+ if (stream.eat("x")) {
+ ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
+ } else {
+ ok = stream.eatWhile(/[\d]/) && stream.eat(";");
+ }
+ } else {
+ ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
+ }
+ return ok ? "atom" : "error";
+ } else {
+ stream.eatWhile(/[^&<]/);
+ return null;
+ }
+ }
+ inText.isInText = true;
+
+ function inTag(stream, state) {
+ var ch = stream.next();
+ if (ch == ">" || (ch == "/" && stream.eat(">"))) {
+ state.tokenize = inText;
+ type = ch == ">" ? "endTag" : "selfcloseTag";
+ return "tag bracket";
+ } else if (ch == "=") {
+ type = "equals";
+ return null;
+ } else if (ch == "<") {
+ state.tokenize = inText;
+ state.state = baseState;
+ state.tagName = state.tagStart = null;
+ var next = state.tokenize(stream, state);
+ return next ? next + " tag error" : "tag error";
+ } else if (/[\'\"]/.test(ch)) {
+ state.tokenize = inAttribute(ch);
+ state.stringStartCol = stream.column();
+ return state.tokenize(stream, state);
+ } else {
+ stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
+ return "word";
+ }
+ }
+
+ function inAttribute(quote) {
+ var closure = function(stream, state) {
+ while (!stream.eol()) {
+ if (stream.next() == quote) {
+ state.tokenize = inTag;
+ break;
+ }
+ }
+ return "string";
+ };
+ closure.isInAttribute = true;
+ return closure;
+ }
+
+ function inBlock(style, terminator) {
+ return function(stream, state) {
+ while (!stream.eol()) {
+ if (stream.match(terminator)) {
+ state.tokenize = inText;
+ break;
+ }
+ stream.next();
+ }
+ return style;
+ }
+ }
+
+ function doctype(depth) {
+ return function(stream, state) {
+ var ch;
+ while ((ch = stream.next()) != null) {
+ if (ch == "<") {
+ state.tokenize = doctype(depth + 1);
+ return state.tokenize(stream, state);
+ } else if (ch == ">") {
+ if (depth == 1) {
+ state.tokenize = inText;
+ break;
+ } else {
+ state.tokenize = doctype(depth - 1);
+ return state.tokenize(stream, state);
+ }
+ }
+ }
+ return "meta";
+ };
+ }
+
+ function Context(state, tagName, startOfLine) {
+ this.prev = state.context;
+ this.tagName = tagName;
+ this.indent = state.indented;
+ this.startOfLine = startOfLine;
+ if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
+ this.noIndent = true;
+ }
+ function popContext(state) {
+ if (state.context) state.context = state.context.prev;
+ }
+ function maybePopContext(state, nextTagName) {
+ var parentTagName;
+ while (true) {
+ if (!state.context) {
+ return;
+ }
+ parentTagName = state.context.tagName;
+ if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
+ !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
+ return;
+ }
+ popContext(state);
+ }
+ }
+
+ function baseState(type, stream, state) {
+ if (type == "openTag") {
+ state.tagStart = stream.column();
+ return tagNameState;
+ } else if (type == "closeTag") {
+ return closeTagNameState;
+ } else {
+ return baseState;
+ }
+ }
+ function tagNameState(type, stream, state) {
+ if (type == "word") {
+ state.tagName = stream.current();
+ setStyle = "tag";
+ return attrState;
+ } else if (config.allowMissingTagName && type == "endTag") {
+ setStyle = "tag bracket";
+ return attrState(type, stream, state);
+ } else {
+ setStyle = "error";
+ return tagNameState;
+ }
+ }
+ function closeTagNameState(type, stream, state) {
+ if (type == "word") {
+ var tagName = stream.current();
+ if (state.context && state.context.tagName != tagName &&
+ config.implicitlyClosed.hasOwnProperty(state.context.tagName))
+ popContext(state);
+ if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
+ setStyle = "tag";
+ return closeState;
+ } else {
+ setStyle = "tag error";
+ return closeStateErr;
+ }
+ } else if (config.allowMissingTagName && type == "endTag") {
+ setStyle = "tag bracket";
+ return closeState(type, stream, state);
+ } else {
+ setStyle = "error";
+ return closeStateErr;
+ }
+ }
+
+ function closeState(type, _stream, state) {
+ if (type != "endTag") {
+ setStyle = "error";
+ return closeState;
+ }
+ popContext(state);
+ return baseState;
+ }
+ function closeStateErr(type, stream, state) {
+ setStyle = "error";
+ return closeState(type, stream, state);
+ }
+
+ function attrState(type, _stream, state) {
+ if (type == "word") {
+ setStyle = "attribute";
+ return attrEqState;
+ } else if (type == "endTag" || type == "selfcloseTag") {
+ var tagName = state.tagName, tagStart = state.tagStart;
+ state.tagName = state.tagStart = null;
+ if (type == "selfcloseTag" ||
+ config.autoSelfClosers.hasOwnProperty(tagName)) {
+ maybePopContext(state, tagName);
+ } else {
+ maybePopContext(state, tagName);
+ state.context = new Context(state, tagName, tagStart == state.indented);
+ }
+ return baseState;
+ }
+ setStyle = "error";
+ return attrState;
+ }
+ function attrEqState(type, stream, state) {
+ if (type == "equals") return attrValueState;
+ if (!config.allowMissing) setStyle = "error";
+ return attrState(type, stream, state);
+ }
+ function attrValueState(type, stream, state) {
+ if (type == "string") return attrContinuedState;
+ if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
+ setStyle = "error";
+ return attrState(type, stream, state);
+ }
+ function attrContinuedState(type, stream, state) {
+ if (type == "string") return attrContinuedState;
+ return attrState(type, stream, state);
+ }
+
+ return {
+ startState: function(baseIndent) {
+ var state = {tokenize: inText,
+ state: baseState,
+ indented: baseIndent || 0,
+ tagName: null, tagStart: null,
+ context: null}
+ if (baseIndent != null) state.baseIndent = baseIndent
+ return state
+ },
+
+ token: function(stream, state) {
+ if (!state.tagName && stream.sol())
+ state.indented = stream.indentation();
+
+ if (stream.eatSpace()) return null;
+ type = null;
+ var style = state.tokenize(stream, state);
+ if ((style || type) && style != "comment") {
+ setStyle = null;
+ state.state = state.state(type || style, stream, state);
+ if (setStyle)
+ style = setStyle == "error" ? style + " error" : setStyle;
+ }
+ return style;
+ },
+
+ indent: function(state, textAfter, fullLine) {
+ var context = state.context;
+ // Indent multi-line strings (e.g. css).
+ if (state.tokenize.isInAttribute) {
+ if (state.tagStart == state.indented)
+ return state.stringStartCol + 1;
+ else
+ return state.indented + indentUnit;
+ }
+ if (context && context.noIndent) return CodeMirror.Pass;
+ if (state.tokenize != inTag && state.tokenize != inText)
+ return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
+ // Indent the starts of attribute names.
+ if (state.tagName) {
+ if (config.multilineTagIndentPastTag !== false)
+ return state.tagStart + state.tagName.length + 2;
+ else
+ return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
+ }
+ if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
+ var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
+ if (tagAfter && tagAfter[1]) { // Closing tag spotted
+ while (context) {
+ if (context.tagName == tagAfter[2]) {
+ context = context.prev;
+ break;
+ } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
+ context = context.prev;
+ } else {
+ break;
+ }
+ }
+ } else if (tagAfter) { // Opening tag spotted
+ while (context) {
+ var grabbers = config.contextGrabbers[context.tagName];
+ if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
+ context = context.prev;
+ else
+ break;
+ }
+ }
+ while (context && context.prev && !context.startOfLine)
+ context = context.prev;
+ if (context) return context.indent + indentUnit;
+ else return state.baseIndent || 0;
+ },
+
+ electricInput: /<\/[\s\w:]+>$/,
+ blockCommentStart: "<!--",
+ blockCommentEnd: "-->",
+
+ configuration: config.htmlMode ? "html" : "xml",
+ helperType: config.htmlMode ? "html" : "xml",
+
+ skipAttribute: function(state) {
+ if (state.state == attrValueState)
+ state.state = attrState
+ },
+
+ xmlCurrentTag: function(state) {
+ return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
+ },
+
+ xmlCurrentContext: function(state) {
+ var context = []
+ for (var cx = state.context; cx; cx = cx.prev)
+ if (cx.tagName) context.push(cx.tagName)
+ return context.reverse()
+ }
+ };
+});
+
+CodeMirror.defineMIME("text/xml", "xml");
+CodeMirror.defineMIME("application/xml", "xml");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
+ CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
+
+});
diff --git a/devtools/client/shared/sourceeditor/codemirror/mozilla.css b/devtools/client/shared/sourceeditor/codemirror/mozilla.css
new file mode 100644
index 0000000000..419f29cf68
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror/mozilla.css
@@ -0,0 +1,364 @@
+/* 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/. */
+
+
+:root {
+ --breakpoint-active-color: rgba(44,187,15,.2);
+ --breakpoint-active-color-hover: rgba(44,187,15,.5);
+}
+
+.theme-dark:root {
+ --breakpoint-active-color: rgba(0,255,175,.4);
+ --breakpoint-active-color-hover: rgba(0,255,175,.7);
+}
+
+/* We don't want the focus style to be displayed on the CodeMirror scrollbars.
+ * See Bug 1590707.
+ */
+.CodeMirror-vscrollbar, .CodeMirror-hscrollbar {
+ outline: none !important;
+}
+
+.CodeMirror .errors {
+ width: 16px;
+}
+
+.CodeMirror .error {
+ display: inline-block;
+ margin-left: 5px;
+ width: 12px;
+ height: 12px;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ opacity: 0.75;
+}
+
+.CodeMirror .hit-counts {
+ width: 6px;
+}
+
+.CodeMirror .hit-count {
+ display: inline-block;
+ height: 12px;
+ border: solid rgba(0,0,0,0.2);
+ border-width: 1px 1px 1px 0;
+ border-radius: 0 3px 3px 0;
+ padding: 0 3px;
+ font-size: 10px;
+ pointer-events: none;
+}
+
+.CodeMirror-linenumber:before {
+ content: " ";
+ display: block;
+ width: calc(100% - 3px);
+ position: absolute;
+ top: 1px;
+ left: 0;
+ height: 12px;
+ z-index: -1;
+ background-size: calc(100% - 2px) 12px;
+ background-repeat: no-repeat;
+ background-position: right center;
+ padding-inline-end: 9px;
+}
+
+.breakpoint .CodeMirror-linenumber {
+ color: var(--theme-body-background);
+}
+
+.debug-line .CodeMirror-linenumber {
+ background-color: var(--breakpoint-active-color);
+}
+
+.theme-dark .debug-line .CodeMirror-linenumber {
+ color: #c0c0c0;
+}
+
+.debug-line .CodeMirror-line {
+ background-color: var(--breakpoint-active-color) !important;
+}
+
+/* Don't display the highlight color since the debug line
+ is already highlighted */
+.debug-line .CodeMirror-activeline-background {
+ display: none;
+}
+
+.CodeMirror {
+ cursor: text;
+ height: 100%;
+}
+
+.CodeMirror-gutters {
+ cursor: default;
+}
+
+.cm-trailingspace {
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAYAAAB/qH1jAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3QUXCToH00Y1UgAAACFJREFUCNdjPMDBUc/AwNDAAAFMTAwMDA0OP34wQgX/AQBYgwYEx4f9lQAAAABJRU5ErkJggg==");
+ opacity: 0.75;
+ background-position: left bottom;
+ background-repeat: repeat-x;
+}
+
+/* Search highlight style
+ * cm-searching is used in Style Editor, and cm-highlight in Debugger */
+.cm-searching,
+.cm-highlight {
+ border-bottom: 1px solid var(--theme-contrast-border);
+ /* Use the translucent yellow background, because we want the text selection
+ background (CodeMirror-selected) to show through this. */
+ background-color: var(--theme-contrast-background-alpha);
+}
+
+/* CodeMirror dialogs styling */
+
+.CodeMirror-dialog {
+ padding: 4px 3px;
+}
+
+.CodeMirror-dialog,
+.CodeMirror-dialog input {
+ font: message-box;
+}
+
+/* Fold addon */
+
+.CodeMirror-foldmarker {
+ display: inline-block;
+ font-family: sans-serif;
+ line-height: 10px;
+ margin: 0 1px;
+ padding: 0 2px;
+ border-radius: 3px;
+ border: solid 1px var(--theme-splitter-color);
+ color: var(--theme-body-color);
+ background-color: var(--theme-sidebar-background);
+ cursor: pointer;
+}
+
+.CodeMirror-foldgutter {
+ width: 16px; /* Same as breakpoints gutter above */
+}
+
+.CodeMirror-foldgutter-open,
+.CodeMirror-foldgutter-folded {
+ height: 14px;
+ margin-top: 1px;
+ background-image: url("chrome://devtools/skin/images/arrow.svg");
+ background-position: center;
+ background-repeat: no-repeat;
+ -moz-context-properties: fill;
+ fill: var(--theme-icon-dimmed-color);
+ /* make the icons smaller than regular twistys (10->8px) */
+ background-size: 8px;
+ cursor: pointer;
+}
+
+.CodeMirror-foldgutter-folded {
+ transform: rotate(-90deg);
+}
+
+.CodeMirror-hints {
+ position: absolute;
+ z-index: 10;
+ overflow: hidden;
+ list-style: none;
+ margin: 0;
+ padding: 2px;
+ border-radius: 3px;
+ font-size: 90%;
+ max-height: 20em;
+ overflow-y: auto;
+}
+
+.CodeMirror-hint {
+ margin: 0;
+ padding: 0 4px;
+ border-radius: 2px;
+ max-width: 19em;
+ overflow: hidden;
+ white-space: pre;
+ cursor: pointer;
+}
+
+.CodeMirror-Tern-completion {
+ padding-inline-start: 22px;
+ position: relative;
+ line-height: 18px;
+}
+
+.CodeMirror-Tern-completion:before {
+ position: absolute;
+ left: 2px;
+ bottom: 2px;
+ border-radius: 50%;
+ font-size: 12px;
+ font-weight: bold;
+ height: 15px;
+ width: 15px;
+ line-height: 16px;
+ text-align: center;
+ color: #ffffff;
+ box-sizing: border-box;
+}
+
+.CodeMirror-Tern-completion-unknown:before {
+ content: "?";
+}
+
+.CodeMirror-Tern-completion-object:before {
+ content: "O";
+}
+
+.CodeMirror-Tern-completion-fn:before {
+ content: "F";
+}
+
+.CodeMirror-Tern-completion-array:before {
+ content: "A";
+}
+
+.CodeMirror-Tern-completion-number:before {
+ content: "N";
+}
+
+.CodeMirror-Tern-completion-string:before {
+ content: "S";
+}
+
+.CodeMirror-Tern-completion-bool:before {
+ content: "B";
+}
+
+.CodeMirror-Tern-completion-guess {
+ color: #999;
+}
+
+.CodeMirror-Tern-tooltip {
+ border-radius: 3px;
+ padding: 2px 5px;
+ white-space: pre-wrap;
+ max-width: 40em;
+ position: absolute;
+ z-index: 10;
+}
+
+.CodeMirror-Tern-hint-doc {
+ max-width: 25em;
+}
+
+.CodeMirror-Tern-farg-current {
+ text-decoration: underline;
+}
+
+.CodeMirror-Tern-fhint-guess {
+ opacity: .7;
+}
+
+/* Override the background-color: white applied to filler elements in codemirror.css */
+.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
+ appearance: auto;
+ -moz-default-appearance: scrollcorner;
+}
+
+/* Markup view and Event tooltip styles */
+
+.CodeMirror.cm-s-markup-view pre {
+ font-size: var(--theme-code-font-size);
+ line-height: var(--theme-code-line-height);
+ min-height: 15px;
+}
+
+/* Make our own cursor-blink animation */
+@keyframes cursor-blink {
+ 0% {}
+ 50% { opacity: 0; }
+ 100% {}
+}
+
+.CodeMirror-focused .CodeMirror-cursor {
+ /*
+ * We're using the --caret-blink-time custom property for the animation duration.
+ * It is set in editor.js when we setup the CodeMirror instance, and will map either
+ * to the value of the ui.caretBlinkTime preference, or to CodeMirror's default for
+ * cursorBlinkRate. (See Bug 1609567 for more information on why this is needed).
+ */
+ /*
+ * The animation should only be set when the editor is focused, otherwise the
+ * animation will continue even when devtools is in a background tab.
+ * This happens because the cursor is hidden using visiblity: hidden rather than
+ * display: none in codemirror.css. See bug 1661054 for details.
+ */
+ animation: cursor-blink calc(var(--caret-blink-time, 0.53s) * 2) steps(1) infinite;
+}
+
+
+/*
+ * Always align CodeMirror lines to the left.
+ * See Bug 1651443 for more information.
+ */
+.CodeMirror-lines {
+ text-align: left;
+}
+
+/* CodeMirror matchhighlight styling */
+.cm-matchhighlight {
+ outline: 1px solid var(--theme-contrast-border);
+ border-radius: 2px;
+}
+
+.cm-non-printable-char {
+ color: var(--theme-comment);
+ font-size: 0.9em;
+ border-radius: 2px;
+ outline: 1px solid currentColor;
+ margin-inline: 2px;
+ padding-inline: 2px;
+}
+
+/****************************************/
+/***** CodeMirror 6 specific styles *****/
+/****************************************/
+
+.cm-editor {
+ max-height: 100%;
+ height: 100%;
+}
+.cm-editor .cm-selectionBackground {
+ background: var(--theme-selection-background) !important;
+}
+
+.cm6-dt-foldgutter__toggle-button {
+ height: 14px;
+ width: 14px;
+ margin-inline: 3px;
+ /* By default, the icon is a bit too low */
+ translate: 0px -4px;
+ background-image: url("chrome://devtools/skin/images/arrow.svg");
+ background-position: center;
+ background-repeat: no-repeat;
+ background-color: transparent;
+ border: none;
+ -moz-context-properties: fill;
+ fill: var(--theme-icon-dimmed-color);
+ /* make the icons smaller than regular twistys (10->8px) */
+ background-size: 8px;
+ cursor: pointer;
+
+ &[aria-expanded=false] {
+ transform: rotate(-90deg);
+ }
+}
+
+.cm-editor .cm-foldPlaceholder {
+ font-family: sans-serif;
+ padding: 0 2px;
+ border-radius: 3px;
+ border: solid 1px var(--theme-splitter-color);
+ color: var(--theme-body-color);
+ background-color: var(--theme-sidebar-background);
+ vertical-align: middle;
+}
diff --git a/devtools/client/shared/sourceeditor/codemirror6/codemirror6.bundle.js b/devtools/client/shared/sourceeditor/codemirror6/codemirror6.bundle.js
new file mode 100644
index 0000000000..cbedb3ca53
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror6/codemirror6.bundle.js
@@ -0,0 +1 @@
+var CodeMirror=function(t){"use strict";class e{lineAt(t){if(t<0||t>this.length)throw new RangeError(`Invalid position ${t} in document of length ${this.length}`);return this.lineInner(t,!1,1,0)}line(t){if(t<1||t>this.lines)throw new RangeError(`Invalid line number ${t} in ${this.lines}-line document`);return this.lineInner(t,!0,1,0)}replace(t,e,i){[t,e]=c(this,t,e);let s=[];return this.decompose(0,t,s,2),i.length&&i.decompose(0,i.length,s,3),this.decompose(e,this.length,s,1),n.from(s,this.length-(e-t)+i.length)}append(t){return this.replace(this.length,this.length,t)}slice(t,e=this.length){[t,e]=c(this,t,e);let i=[];return this.decompose(t,e,i,0),n.from(i,e-t)}eq(t){if(t==this)return!0;if(t.length!=this.length||t.lines!=this.lines)return!1;let e=this.scanIdentical(t,1),i=this.length-this.scanIdentical(t,-1),n=new o(this),s=new o(t);for(let t=e,r=e;;){if(n.next(t),s.next(t),t=0,n.lineBreak!=s.lineBreak||n.done!=s.done||n.value!=s.value)return!1;if(r+=n.value.length,n.done||r>=i)return!0}}iter(t=1){return new o(this,t)}iterRange(t,e=this.length){return new a(this,t,e)}iterLines(t,e){let i;if(null==t)i=this.iter();else{null==e&&(e=this.lines+1);let n=this.line(t).from;i=this.iterRange(n,Math.max(n,e==this.lines+1?this.length:e<=1?0:this.line(e-1).to))}return new l(i)}toString(){return this.sliceString(0)}toJSON(){let t=[];return this.flatten(t),t}constructor(){}static of(t){if(0==t.length)throw new RangeError("A document must have at least one line");return 1!=t.length||t[0]?t.length<=32?new i(t):n.from(i.split(t,[])):e.empty}}class i extends e{constructor(t,e=function(t){let e=-1;for(let i of t)e+=i.length+1;return e}(t)){super(),this.text=t,this.length=e}get lines(){return this.text.length}get children(){return null}lineInner(t,e,i,n){for(let s=0;;s++){let r=this.text[s],o=n+r.length;if((e?i:o)>=t)return new h(n,o,i,r);n=o+1,i++}}decompose(t,e,n,o){let a=t<=0&&e>=this.length?this:new i(r(this.text,t,e),Math.min(e,this.length)-Math.max(0,t));if(1&o){let t=n.pop(),e=s(a.text,t.text.slice(),0,a.length);if(e.length<=32)n.push(new i(e,t.length+a.length));else{let t=e.length>>1;n.push(new i(e.slice(0,t)),new i(e.slice(t)))}}else n.push(a)}replace(t,e,o){if(!(o instanceof i))return super.replace(t,e,o);[t,e]=c(this,t,e);let a=s(this.text,s(o.text,r(this.text,0,t)),e),l=this.length+o.length-(e-t);return a.length<=32?new i(a,l):n.from(i.split(a,[]),l)}sliceString(t,e=this.length,i="\n"){[t,e]=c(this,t,e);let n="";for(let s=0,r=0;s<=e&&r<this.text.length;r++){let o=this.text[r],a=s+o.length;s>t&&r&&(n+=i),t<a&&e>s&&(n+=o.slice(Math.max(0,t-s),e-s)),s=a+1}return n}flatten(t){for(let e of this.text)t.push(e)}scanIdentical(){return 0}static split(t,e){let n=[],s=-1;for(let r of t)n.push(r),s+=r.length+1,32==n.length&&(e.push(new i(n,s)),n=[],s=-1);return s>-1&&e.push(new i(n,s)),e}}class n extends e{constructor(t,e){super(),this.children=t,this.length=e,this.lines=0;for(let e of t)this.lines+=e.lines}lineInner(t,e,i,n){for(let s=0;;s++){let r=this.children[s],o=n+r.length,a=i+r.lines-1;if((e?a:o)>=t)return r.lineInner(t,e,i,n);n=o+1,i=a+1}}decompose(t,e,i,n){for(let s=0,r=0;r<=e&&s<this.children.length;s++){let o=this.children[s],a=r+o.length;if(t<=a&&e>=r){let s=n&((r<=t?1:0)|(a>=e?2:0));r>=t&&a<=e&&!s?i.push(o):o.decompose(t-r,e-r,i,s)}r=a+1}}replace(t,e,i){if([t,e]=c(this,t,e),i.lines<this.lines)for(let s=0,r=0;s<this.children.length;s++){let o=this.children[s],a=r+o.length;if(t>=r&&e<=a){let l=o.replace(t-r,e-r,i),h=this.lines-o.lines+l.lines;if(l.lines<h>>4&&l.lines>h>>6){let r=this.children.slice();return r[s]=l,new n(r,this.length-(e-t)+i.length)}return super.replace(r,a,l)}r=a+1}return super.replace(t,e,i)}sliceString(t,e=this.length,i="\n"){[t,e]=c(this,t,e);let n="";for(let s=0,r=0;s<this.children.length&&r<=e;s++){let o=this.children[s],a=r+o.length;r>t&&s&&(n+=i),t<a&&e>r&&(n+=o.sliceString(t-r,e-r,i)),r=a+1}return n}flatten(t){for(let e of this.children)e.flatten(t)}scanIdentical(t,e){if(!(t instanceof n))return 0;let i=0,[s,r,o,a]=e>0?[0,0,this.children.length,t.children.length]:[this.children.length-1,t.children.length-1,-1,-1];for(;;s+=e,r+=e){if(s==o||r==a)return i;let n=this.children[s],l=t.children[r];if(n!=l)return i+n.scanIdentical(l,e);i+=n.length+1}}static from(t,e=t.reduce(((t,e)=>t+e.length+1),-1)){let s=0;for(let e of t)s+=e.lines;if(s<32){let n=[];for(let e of t)e.flatten(n);return new i(n,e)}let r=Math.max(32,s>>5),o=r<<1,a=r>>1,l=[],h=0,c=-1,f=[];function u(t){let e;if(t.lines>o&&t instanceof n)for(let e of t.children)u(e);else t.lines>a&&(h>a||!h)?(d(),l.push(t)):t instanceof i&&h&&(e=f[f.length-1])instanceof i&&t.lines+e.lines<=32?(h+=t.lines,c+=t.length+1,f[f.length-1]=new i(e.text.concat(t.text),e.length+1+t.length)):(h+t.lines>r&&d(),h+=t.lines,c+=t.length+1,f.push(t))}function d(){0!=h&&(l.push(1==f.length?f[0]:n.from(f,c)),c=-1,h=f.length=0)}for(let e of t)u(e);return d(),1==l.length?l[0]:new n(l,e)}}function s(t,e,i=0,n=1e9){for(let s=0,r=0,o=!0;r<t.length&&s<=n;r++){let a=t[r],l=s+a.length;l>=i&&(l>n&&(a=a.slice(0,n-s)),s<i&&(a=a.slice(i-s)),o?(e[e.length-1]+=a,o=!1):e.push(a)),s=l+1}return e}function r(t,e,i){return s(t,[""],e,i)}e.empty=new i([""],0);class o{constructor(t,e=1){this.dir=e,this.done=!1,this.lineBreak=!1,this.value="",this.nodes=[t],this.offsets=[e>0?1:(t instanceof i?t.text.length:t.children.length)<<1]}nextInner(t,e){for(this.done=this.lineBreak=!1;;){let n=this.nodes.length-1,s=this.nodes[n],r=this.offsets[n],o=r>>1,a=s instanceof i?s.text.length:s.children.length;if(o==(e>0?a:0)){if(0==n)return this.done=!0,this.value="",this;e>0&&this.offsets[n-1]++,this.nodes.pop(),this.offsets.pop()}else if((1&r)==(e>0?0:1)){if(this.offsets[n]+=e,0==t)return this.lineBreak=!0,this.value="\n",this;t--}else if(s instanceof i){let i=s.text[o+(e<0?-1:0)];if(this.offsets[n]+=e,i.length>Math.max(0,t))return this.value=0==t?i:e>0?i.slice(t):i.slice(0,i.length-t),this;t-=i.length}else{let r=s.children[o+(e<0?-1:0)];t>r.length?(t-=r.length,this.offsets[n]+=e):(e<0&&this.offsets[n]--,this.nodes.push(r),this.offsets.push(e>0?1:(r instanceof i?r.text.length:r.children.length)<<1))}}}next(t=0){return t<0&&(this.nextInner(-t,-this.dir),t=this.value.length),this.nextInner(t,this.dir)}}class a{constructor(t,e,i){this.value="",this.done=!1,this.cursor=new o(t,e>i?-1:1),this.pos=e>i?t.length:0,this.from=Math.min(e,i),this.to=Math.max(e,i)}nextInner(t,e){if(e<0?this.pos<=this.from:this.pos>=this.to)return this.value="",this.done=!0,this;t+=Math.max(0,e<0?this.pos-this.to:this.from-this.pos);let i=e<0?this.pos-this.from:this.to-this.pos;t>i&&(t=i),i-=t;let{value:n}=this.cursor.next(t);return this.pos+=(n.length+t)*e,this.value=n.length<=i?n:e<0?n.slice(n.length-i):n.slice(0,i),this.done=!this.value,this}next(t=0){return t<0?t=Math.max(t,this.from-this.pos):t>0&&(t=Math.min(t,this.to-this.pos)),this.nextInner(t,this.cursor.dir)}get lineBreak(){return this.cursor.lineBreak&&""!=this.value}}class l{constructor(t){this.inner=t,this.afterBreak=!0,this.value="",this.done=!1}next(t=0){let{done:e,lineBreak:i,value:n}=this.inner.next(t);return e&&this.afterBreak?(this.value="",this.afterBreak=!1):e?(this.done=!0,this.value=""):i?this.afterBreak?this.value="":(this.afterBreak=!0,this.next()):(this.value=n,this.afterBreak=!1),this}get lineBreak(){return!1}}"undefined"!=typeof Symbol&&(e.prototype[Symbol.iterator]=function(){return this.iter()},o.prototype[Symbol.iterator]=a.prototype[Symbol.iterator]=l.prototype[Symbol.iterator]=function(){return this});class h{constructor(t,e,i,n){this.from=t,this.to=e,this.number=i,this.text=n}get length(){return this.to-this.from}}function c(t,e,i){return[e=Math.max(0,Math.min(t.length,e)),Math.max(e,Math.min(t.length,i))]}let f="lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,3b,f,,w,1j,,,,4,8,4,,3,7,a,2,t,,1m,,,,2,4,8,,9,,a,2,q,,2,2,1l,,4,2,4,2,2,3,3,,u,2,3,,b,2,1l,,4,5,,2,4,,k,2,m,6,,,1m,,,2,,4,8,,7,3,a,2,u,,1n,,,,c,,9,,14,,3,,1l,3,5,3,,4,7,2,b,2,t,,1m,,2,,2,,3,,5,2,7,2,b,2,s,2,1l,2,,,2,4,8,,9,,a,2,t,,20,,4,,2,3,,,8,,29,,2,7,c,8,2q,,2,9,b,6,22,2,r,,,,,,1j,e,,5,,2,5,b,,10,9,,2u,4,,6,,2,2,2,p,2,4,3,g,4,d,,2,2,6,,f,,jj,3,qa,3,t,3,t,2,u,2,1s,2,,7,8,,2,b,9,,19,3,3b,2,y,,3a,3,4,2,9,,6,3,63,2,2,,1m,,,7,,,,,2,8,6,a,2,,1c,h,1r,4,1c,7,,,5,,14,9,c,2,w,4,2,2,,3,1k,,,2,3,,,3,1m,8,2,2,48,3,,d,,7,4,,6,,3,2,5i,1m,,5,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,p,2,5,,47,2,q,i,d,,12,8,p,b,1a,3,1c,,2,4,2,2,13,,1v,6,2,2,2,2,c,,8,,1b,,1f,,,3,2,2,5,2,,,16,2,8,,6m,,2,,4,,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,ar,2,49,b,4w,,1i,f,1k,3,1d,4,2,2,1x,3,10,5,,8,1q,,c,2,1g,9,a,4,2,,2n,3,2,,,2,6,,4g,,3,8,l,2,1l,2,,,,,m,,e,7,3,5,5f,8,2,3,,,n,,29,,2,6,,,2,,,2,,2,6j,,2,4,6,2,,2,r,2,2d,8,2,,,2,2y,,,,2,6,,,2t,3,2,4,,5,77,9,,2,6t,,a,2,,,4,,40,4,2,2,4,,w,a,14,6,2,4,8,,9,6,2,3,1a,d,,2,ba,7,,6,,,2a,m,2,7,,2,,2,3e,6,3,,,2,,7,,,20,2,3,,,,9n,2,f0b,5,1n,7,t4,,1r,4,29,,f5k,2,43q,,,3,4,5,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,79,7,c5,4,15s,7,31,7,240,5,gx7k,2o,3k,6o".split(",").map((t=>t?parseInt(t,36):1));for(let t=1;t<f.length;t++)f[t]+=f[t-1];function u(t){for(let e=1;e<f.length;e+=2)if(f[e]>t)return f[e-1]<=t;return!1}function d(t){return t>=127462&&t<=127487}const p=8205;function O(t,e,i=!0,n=!0){return(i?g:m)(t,e,n)}function g(t,e,i){if(e==t.length)return e;e&&w(t.charCodeAt(e))&&v(t.charCodeAt(e-1))&&e--;let n=b(t,e);for(e+=S(n);e<t.length;){let s=b(t,e);if(n==p||s==p||i&&u(s))e+=S(s),n=s;else{if(!d(s))break;{let i=0,n=e-2;for(;n>=0&&d(b(t,n));)i++,n-=2;if(i%2==0)break;e+=2}}}return e}function m(t,e,i){for(;e>0;){let n=g(t,e-2,i);if(n<e)return n;e--}return 0}function w(t){return t>=56320&&t<57344}function v(t){return t>=55296&&t<56320}function b(t,e){let i=t.charCodeAt(e);if(!v(i)||e+1==t.length)return i;let n=t.charCodeAt(e+1);return w(n)?n-56320+(i-55296<<10)+65536:i}function y(t){return t<=65535?String.fromCharCode(t):(t-=65536,String.fromCharCode(55296+(t>>10),56320+(1023&t)))}function S(t){return t<65536?1:2}const x=/\r\n?|\n/;var k=function(t){return t[t.Simple=0]="Simple",t[t.TrackDel=1]="TrackDel",t[t.TrackBefore=2]="TrackBefore",t[t.TrackAfter=3]="TrackAfter",t}(k||(k={}));class Q{constructor(t){this.sections=t}get length(){let t=0;for(let e=0;e<this.sections.length;e+=2)t+=this.sections[e];return t}get newLength(){let t=0;for(let e=0;e<this.sections.length;e+=2){let i=this.sections[e+1];t+=i<0?this.sections[e]:i}return t}get empty(){return 0==this.sections.length||2==this.sections.length&&this.sections[1]<0}iterGaps(t){for(let e=0,i=0,n=0;e<this.sections.length;){let s=this.sections[e++],r=this.sections[e++];r<0?(t(i,n,s),n+=s):n+=r,i+=s}}iterChangedRanges(t,e=!1){C(this,t,e)}get invertedDesc(){let t=[];for(let e=0;e<this.sections.length;){let i=this.sections[e++],n=this.sections[e++];n<0?t.push(i,n):t.push(n,i)}return new Q(t)}composeDesc(t){return this.empty?t:t.empty?this:A(this,t)}mapDesc(t,e=!1){return t.empty?this:T(this,t,e)}mapPos(t,e=-1,i=k.Simple){let n=0,s=0;for(let r=0;r<this.sections.length;){let o=this.sections[r++],a=this.sections[r++],l=n+o;if(a<0){if(l>t)return s+(t-n);s+=o}else{if(i!=k.Simple&&l>=t&&(i==k.TrackDel&&n<t&&l>t||i==k.TrackBefore&&n<t||i==k.TrackAfter&&l>t))return null;if(l>t||l==t&&e<0&&!o)return t==n||e<0?s:s+a;s+=a}n=l}if(t>n)throw new RangeError(`Position ${t} is out of range for changeset of length ${n}`);return s}touchesRange(t,e=t){for(let i=0,n=0;i<this.sections.length&&n<=e;){let s=n+this.sections[i++];if(this.sections[i++]>=0&&n<=e&&s>=t)return!(n<t&&s>e)||"cover";n=s}return!1}toString(){let t="";for(let e=0;e<this.sections.length;){let i=this.sections[e++],n=this.sections[e++];t+=(t?" ":"")+i+(n>=0?":"+n:"")}return t}toJSON(){return this.sections}static fromJSON(t){if(!Array.isArray(t)||t.length%2||t.some((t=>"number"!=typeof t)))throw new RangeError("Invalid JSON representation of ChangeDesc");return new Q(t)}static create(t){return new Q(t)}}class $ extends Q{constructor(t,e){super(t),this.inserted=e}apply(t){if(this.length!=t.length)throw new RangeError("Applying change set to a document with the wrong length");return C(this,((e,i,n,s,r)=>t=t.replace(n,n+(i-e),r)),!1),t}mapDesc(t,e=!1){return T(this,t,e,!0)}invert(t){let i=this.sections.slice(),n=[];for(let s=0,r=0;s<i.length;s+=2){let o=i[s],a=i[s+1];if(a>=0){i[s]=a,i[s+1]=o;let l=s>>1;for(;n.length<l;)n.push(e.empty);n.push(o?t.slice(r,r+o):e.empty)}r+=o}return new $(i,n)}compose(t){return this.empty?t:t.empty?this:A(this,t,!0)}map(t,e=!1){return t.empty?this:T(this,t,e,!0)}iterChanges(t,e=!1){C(this,t,e)}get desc(){return Q.create(this.sections)}filter(t){let e=[],i=[],n=[],s=new R(this);t:for(let r=0,o=0;;){let a=r==t.length?1e9:t[r++];for(;o<a||o==a&&0==s.len;){if(s.done)break t;let t=Math.min(s.len,a-o);P(n,t,-1);let r=-1==s.ins?-1:0==s.off?s.ins:0;P(e,t,r),r>0&&Z(i,e,s.text),s.forward(t),o+=t}let l=t[r++];for(;o<l;){if(s.done)break t;let t=Math.min(s.len,l-o);P(e,t,-1),P(n,t,-1==s.ins?-1:0==s.off?s.ins:0),s.forward(t),o+=t}}return{changes:new $(e,i),filtered:Q.create(n)}}toJSON(){let t=[];for(let e=0;e<this.sections.length;e+=2){let i=this.sections[e],n=this.sections[e+1];n<0?t.push(i):0==n?t.push([i]):t.push([i].concat(this.inserted[e>>1].toJSON()))}return t}static of(t,i,n){let s=[],r=[],o=0,a=null;function l(t=!1){if(!t&&!s.length)return;o<i&&P(s,i-o,-1);let e=new $(s,r);a=a?a.compose(e.map(a)):e,s=[],r=[],o=0}return function t(h){if(Array.isArray(h))for(let e of h)t(e);else if(h instanceof $){if(h.length!=i)throw new RangeError(`Mismatched change set length (got ${h.length}, expected ${i})`);l(),a=a?a.compose(h.map(a)):h}else{let{from:t,to:a=t,insert:c}=h;if(t>a||t<0||a>i)throw new RangeError(`Invalid change range ${t} to ${a} (in doc of length ${i})`);let f=c?"string"==typeof c?e.of(c.split(n||x)):c:e.empty,u=f.length;if(t==a&&0==u)return;t<o&&l(),t>o&&P(s,t-o,-1),P(s,a-t,u),Z(r,s,f),o=a}}(t),l(!a),a}static empty(t){return new $(t?[t,-1]:[],[])}static fromJSON(t){if(!Array.isArray(t))throw new RangeError("Invalid JSON representation of ChangeSet");let i=[],n=[];for(let s=0;s<t.length;s++){let r=t[s];if("number"==typeof r)i.push(r,-1);else{if(!Array.isArray(r)||"number"!=typeof r[0]||r.some(((t,e)=>e&&"string"!=typeof t)))throw new RangeError("Invalid JSON representation of ChangeSet");if(1==r.length)i.push(r[0],0);else{for(;n.length<s;)n.push(e.empty);n[s]=e.of(r.slice(1)),i.push(r[0],n[s].length)}}}return new $(i,n)}static createSet(t,e){return new $(t,e)}}function P(t,e,i,n=!1){if(0==e&&i<=0)return;let s=t.length-2;s>=0&&i<=0&&i==t[s+1]?t[s]+=e:0==e&&0==t[s]?t[s+1]+=i:n?(t[s]+=e,t[s+1]+=i):t.push(e,i)}function Z(t,i,n){if(0==n.length)return;let s=i.length-2>>1;if(s<t.length)t[t.length-1]=t[t.length-1].append(n);else{for(;t.length<s;)t.push(e.empty);t.push(n)}}function C(t,i,n){let s=t.inserted;for(let r=0,o=0,a=0;a<t.sections.length;){let l=t.sections[a++],h=t.sections[a++];if(h<0)r+=l,o+=l;else{let c=r,f=o,u=e.empty;for(;c+=l,f+=h,h&&s&&(u=u.append(s[a-2>>1])),!(n||a==t.sections.length||t.sections[a+1]<0);)l=t.sections[a++],h=t.sections[a++];i(r,c,o,f,u),r=c,o=f}}}function T(t,e,i,n=!1){let s=[],r=n?[]:null,o=new R(t),a=new R(e);for(let t=-1;;)if(-1==o.ins&&-1==a.ins){let t=Math.min(o.len,a.len);P(s,t,-1),o.forward(t),a.forward(t)}else if(a.ins>=0&&(o.ins<0||t==o.i||0==o.off&&(a.len<o.len||a.len==o.len&&!i))){let e=a.len;for(P(s,a.ins,-1);e;){let i=Math.min(o.len,e);o.ins>=0&&t<o.i&&o.len<=i&&(P(s,0,o.ins),r&&Z(r,s,o.text),t=o.i),o.forward(i),e-=i}a.next()}else{if(!(o.ins>=0)){if(o.done&&a.done)return r?$.createSet(s,r):Q.create(s);throw new Error("Mismatched change set lengths")}{let e=0,i=o.len;for(;i;)if(-1==a.ins){let t=Math.min(i,a.len);e+=t,i-=t,a.forward(t)}else{if(!(0==a.ins&&a.len<i))break;i-=a.len,a.next()}P(s,e,t<o.i?o.ins:0),r&&t<o.i&&Z(r,s,o.text),t=o.i,o.forward(o.len-i)}}}function A(t,e,i=!1){let n=[],s=i?[]:null,r=new R(t),o=new R(e);for(let t=!1;;){if(r.done&&o.done)return s?$.createSet(n,s):Q.create(n);if(0==r.ins)P(n,r.len,0,t),r.next();else if(0!=o.len||o.done){if(r.done||o.done)throw new Error("Mismatched change set lengths");{let e=Math.min(r.len2,o.len),i=n.length;if(-1==r.ins){let i=-1==o.ins?-1:o.off?0:o.ins;P(n,e,i,t),s&&i&&Z(s,n,o.text)}else-1==o.ins?(P(n,r.off?0:r.len,e,t),s&&Z(s,n,r.textBit(e))):(P(n,r.off?0:r.len,o.off?0:o.ins,t),s&&!o.off&&Z(s,n,o.text));t=(r.ins>e||o.ins>=0&&o.len>e)&&(t||n.length>i),r.forward2(e),o.forward(e)}}else P(n,0,o.ins,t),s&&Z(s,n,o.text),o.next()}}class R{constructor(t){this.set=t,this.i=0,this.next()}next(){let{sections:t}=this.set;this.i<t.length?(this.len=t[this.i++],this.ins=t[this.i++]):(this.len=0,this.ins=-2),this.off=0}get done(){return-2==this.ins}get len2(){return this.ins<0?this.len:this.ins}get text(){let{inserted:t}=this.set,i=this.i-2>>1;return i>=t.length?e.empty:t[i]}textBit(t){let{inserted:i}=this.set,n=this.i-2>>1;return n>=i.length&&!t?e.empty:i[n].slice(this.off,null==t?void 0:this.off+t)}forward(t){t==this.len?this.next():(this.len-=t,this.off+=t)}forward2(t){-1==this.ins?this.forward(t):t==this.ins?this.next():(this.ins-=t,this.off+=t)}}class X{constructor(t,e,i){this.from=t,this.to=e,this.flags=i}get anchor(){return 32&this.flags?this.to:this.from}get head(){return 32&this.flags?this.from:this.to}get empty(){return this.from==this.to}get assoc(){return 8&this.flags?-1:16&this.flags?1:0}get bidiLevel(){let t=7&this.flags;return 7==t?null:t}get goalColumn(){let t=this.flags>>6;return 16777215==t?void 0:t}map(t,e=-1){let i,n;return this.empty?i=n=t.mapPos(this.from,e):(i=t.mapPos(this.from,1),n=t.mapPos(this.to,-1)),i==this.from&&n==this.to?this:new X(i,n,this.flags)}extend(t,e=t){if(t<=this.anchor&&e>=this.anchor)return Y.range(t,e);let i=Math.abs(t-this.anchor)>Math.abs(e-this.anchor)?t:e;return Y.range(this.anchor,i)}eq(t,e=!1){return!(this.anchor!=t.anchor||this.head!=t.head||e&&this.empty&&this.assoc!=t.assoc)}toJSON(){return{anchor:this.anchor,head:this.head}}static fromJSON(t){if(!t||"number"!=typeof t.anchor||"number"!=typeof t.head)throw new RangeError("Invalid JSON representation for SelectionRange");return Y.range(t.anchor,t.head)}static create(t,e,i){return new X(t,e,i)}}class Y{constructor(t,e){this.ranges=t,this.mainIndex=e}map(t,e=-1){return t.empty?this:Y.create(this.ranges.map((i=>i.map(t,e))),this.mainIndex)}eq(t,e=!1){if(this.ranges.length!=t.ranges.length||this.mainIndex!=t.mainIndex)return!1;for(let i=0;i<this.ranges.length;i++)if(!this.ranges[i].eq(t.ranges[i],e))return!1;return!0}get main(){return this.ranges[this.mainIndex]}asSingle(){return 1==this.ranges.length?this:new Y([this.main],0)}addRange(t,e=!0){return Y.create([t].concat(this.ranges),e?0:this.mainIndex+1)}replaceRange(t,e=this.mainIndex){let i=this.ranges.slice();return i[e]=t,Y.create(i,this.mainIndex)}toJSON(){return{ranges:this.ranges.map((t=>t.toJSON())),main:this.mainIndex}}static fromJSON(t){if(!t||!Array.isArray(t.ranges)||"number"!=typeof t.main||t.main>=t.ranges.length)throw new RangeError("Invalid JSON representation for EditorSelection");return new Y(t.ranges.map((t=>X.fromJSON(t))),t.main)}static single(t,e=t){return new Y([Y.range(t,e)],0)}static create(t,e=0){if(0==t.length)throw new RangeError("A selection needs at least one range");for(let i=0,n=0;n<t.length;n++){let s=t[n];if(s.empty?s.from<=i:s.from<i)return Y.normalized(t.slice(),e);i=s.to}return new Y(t,e)}static cursor(t,e=0,i,n){return X.create(t,t,(0==e?0:e<0?8:16)|(null==i?7:Math.min(6,i))|(null!=n?n:16777215)<<6)}static range(t,e,i,n){let s=(null!=i?i:16777215)<<6|(null==n?7:Math.min(6,n));return e<t?X.create(e,t,48|s):X.create(t,e,(e>t?8:0)|s)}static normalized(t,e=0){let i=t[e];t.sort(((t,e)=>t.from-e.from)),e=t.indexOf(i);for(let i=1;i<t.length;i++){let n=t[i],s=t[i-1];if(n.empty?n.from<=s.to:n.from<s.to){let r=s.from,o=Math.max(n.to,s.to);i<=e&&e--,t.splice(--i,2,n.anchor>n.head?Y.range(o,r):Y.range(r,o))}}return new Y(t,e)}}function W(t,e){for(let i of t.ranges)if(i.to>e)throw new RangeError("Selection points outside of document")}let M=0;class j{constructor(t,e,i,n,s){this.combine=t,this.compareInput=e,this.compare=i,this.isStatic=n,this.id=M++,this.default=t([]),this.extensions="function"==typeof s?s(this):s}get reader(){return this}static define(t={}){return new j(t.combine||(t=>t),t.compareInput||((t,e)=>t===e),t.compare||(t.combine?(t,e)=>t===e:D),!!t.static,t.enables)}of(t){return new E([],this,0,t)}compute(t,e){if(this.isStatic)throw new Error("Can't compute a static facet");return new E(t,this,1,e)}computeN(t,e){if(this.isStatic)throw new Error("Can't compute a static facet");return new E(t,this,2,e)}from(t,e){return e||(e=t=>t),this.compute([t],(i=>e(i.field(t))))}}function D(t,e){return t==e||t.length==e.length&&t.every(((t,i)=>t===e[i]))}class E{constructor(t,e,i,n){this.dependencies=t,this.facet=e,this.type=i,this.value=n,this.id=M++}dynamicSlot(t){var e;let i=this.value,n=this.facet.compareInput,s=this.id,r=t[s]>>1,o=2==this.type,a=!1,l=!1,h=[];for(let i of this.dependencies)"doc"==i?a=!0:"selection"==i?l=!0:0==(1&(null!==(e=t[i.id])&&void 0!==e?e:1))&&h.push(t[i.id]);return{create:t=>(t.values[r]=i(t),1),update(t,e){if(a&&e.docChanged||l&&(e.docChanged||e.selection)||_(t,h)){let e=i(t);if(o?!q(e,t.values[r],n):!n(e,t.values[r]))return t.values[r]=e,1}return 0},reconfigure:(t,e)=>{let a,l=e.config.address[s];if(null!=l){let s=it(e,l);if(this.dependencies.every((i=>i instanceof j?e.facet(i)===t.facet(i):!(i instanceof z)||e.field(i,!1)==t.field(i,!1)))||(o?q(a=i(t),s,n):n(a=i(t),s)))return t.values[r]=s,0}else a=i(t);return t.values[r]=a,1}}}}function q(t,e,i){if(t.length!=e.length)return!1;for(let n=0;n<t.length;n++)if(!i(t[n],e[n]))return!1;return!0}function _(t,e){let i=!1;for(let n of e)1&et(t,n)&&(i=!0);return i}function V(t,e,i){let n=i.map((e=>t[e.id])),s=i.map((t=>t.type)),r=n.filter((t=>!(1&t))),o=t[e.id]>>1;function a(t){let i=[];for(let e=0;e<n.length;e++){let r=it(t,n[e]);if(2==s[e])for(let t of r)i.push(t);else i.push(r)}return e.combine(i)}return{create(t){for(let e of n)et(t,e);return t.values[o]=a(t),1},update(t,i){if(!_(t,r))return 0;let n=a(t);return e.compare(n,t.values[o])?0:(t.values[o]=n,1)},reconfigure(t,s){let r=_(t,n),l=s.config.facets[e.id],h=s.facet(e);if(l&&!r&&D(i,l))return t.values[o]=h,0;let c=a(t);return e.compare(c,h)?(t.values[o]=h,0):(t.values[o]=c,1)}}}const I=j.define({static:!0});class z{constructor(t,e,i,n,s){this.id=t,this.createF=e,this.updateF=i,this.compareF=n,this.spec=s,this.provides=void 0}static define(t){let e=new z(M++,t.create,t.update,t.compare||((t,e)=>t===e),t);return t.provide&&(e.provides=t.provide(e)),e}create(t){let e=t.facet(I).find((t=>t.field==this));return((null==e?void 0:e.create)||this.createF)(t)}slot(t){let e=t[this.id]>>1;return{create:t=>(t.values[e]=this.create(t),1),update:(t,i)=>{let n=t.values[e],s=this.updateF(n,i);return this.compareF(n,s)?0:(t.values[e]=s,1)},reconfigure:(t,i)=>null!=i.config.address[this.id]?(t.values[e]=i.field(this),0):(t.values[e]=this.create(t),1)}}init(t){return[this,I.of({field:this,create:t})]}get extension(){return this}}const B=4,G=3,L=2,N=1;function U(t){return e=>new F(e,t)}const H={highest:U(0),high:U(N),default:U(L),low:U(G),lowest:U(B)};class F{constructor(t,e){this.inner=t,this.prec=e}}class J{of(t){return new K(this,t)}reconfigure(t){return J.reconfigure.of({compartment:this,extension:t})}get(t){return t.config.compartments.get(this)}}class K{constructor(t,e){this.compartment=t,this.inner=e}}class tt{constructor(t,e,i,n,s,r){for(this.base=t,this.compartments=e,this.dynamicSlots=i,this.address=n,this.staticValues=s,this.facets=r,this.statusTemplate=[];this.statusTemplate.length<i.length;)this.statusTemplate.push(0)}staticFacet(t){let e=this.address[t.id];return null==e?t.default:this.staticValues[e>>1]}static resolve(t,e,i){let n=[],s=Object.create(null),r=new Map;for(let i of function(t,e,i){let n=[[],[],[],[],[]],s=new Map;function r(t,o){let a=s.get(t);if(null!=a){if(a<=o)return;let e=n[a].indexOf(t);e>-1&&n[a].splice(e,1),t instanceof K&&i.delete(t.compartment)}if(s.set(t,o),Array.isArray(t))for(let e of t)r(e,o);else if(t instanceof K){if(i.has(t.compartment))throw new RangeError("Duplicate use of compartment in extensions");let n=e.get(t.compartment)||t.inner;i.set(t.compartment,n),r(n,o)}else if(t instanceof F)r(t.inner,t.prec);else if(t instanceof z)n[o].push(t),t.provides&&r(t.provides,o);else if(t instanceof E)n[o].push(t),t.facet.extensions&&r(t.facet.extensions,L);else{let e=t.extension;if(!e)throw new Error(`Unrecognized extension value in extension set (${t}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);r(e,o)}}return r(t,L),n.reduce(((t,e)=>t.concat(e)))}(t,e,r))i instanceof z?n.push(i):(s[i.facet.id]||(s[i.facet.id]=[])).push(i);let o=Object.create(null),a=[],l=[];for(let t of n)o[t.id]=l.length<<1,l.push((e=>t.slot(e)));let h=null==i?void 0:i.config.facets;for(let t in s){let e=s[t],n=e[0].facet,r=h&&h[t]||[];if(e.every((t=>0==t.type)))if(o[n.id]=a.length<<1|1,D(r,e))a.push(i.facet(n));else{let t=n.combine(e.map((t=>t.value)));a.push(i&&n.compare(t,i.facet(n))?i.facet(n):t)}else{for(let t of e)0==t.type?(o[t.id]=a.length<<1|1,a.push(t.value)):(o[t.id]=l.length<<1,l.push((e=>t.dynamicSlot(e))));o[n.id]=l.length<<1,l.push((t=>V(t,n,e)))}}let c=l.map((t=>t(o)));return new tt(t,r,c,o,a,s)}}function et(t,e){if(1&e)return 2;let i=e>>1,n=t.status[i];if(4==n)throw new Error("Cyclic dependency between fields and/or facets");if(2&n)return n;t.status[i]=4;let s=t.computeSlot(t,t.config.dynamicSlots[i]);return t.status[i]=2|s}function it(t,e){return 1&e?t.config.staticValues[e>>1]:t.values[e>>1]}const nt=j.define(),st=j.define({combine:t=>t.some((t=>t)),static:!0}),rt=j.define({combine:t=>t.length?t[0]:void 0,static:!0}),ot=j.define(),at=j.define(),lt=j.define(),ht=j.define({combine:t=>!!t.length&&t[0]});class ct{constructor(t,e){this.type=t,this.value=e}static define(){return new ft}}class ft{of(t){return new ct(this,t)}}class ut{constructor(t){this.map=t}of(t){return new dt(this,t)}}class dt{constructor(t,e){this.type=t,this.value=e}map(t){let e=this.type.map(this.value,t);return void 0===e?void 0:e==this.value?this:new dt(this.type,e)}is(t){return this.type==t}static define(t={}){return new ut(t.map||(t=>t))}static mapEffects(t,e){if(!t.length)return t;let i=[];for(let n of t){let t=n.map(e);t&&i.push(t)}return i}}dt.reconfigure=dt.define(),dt.appendConfig=dt.define();class pt{constructor(t,e,i,n,s,r){this.startState=t,this.changes=e,this.selection=i,this.effects=n,this.annotations=s,this.scrollIntoView=r,this._doc=null,this._state=null,i&&W(i,e.newLength),s.some((t=>t.type==pt.time))||(this.annotations=s.concat(pt.time.of(Date.now())))}static create(t,e,i,n,s,r){return new pt(t,e,i,n,s,r)}get newDoc(){return this._doc||(this._doc=this.changes.apply(this.startState.doc))}get newSelection(){return this.selection||this.startState.selection.map(this.changes)}get state(){return this._state||this.startState.applyTransaction(this),this._state}annotation(t){for(let e of this.annotations)if(e.type==t)return e.value}get docChanged(){return!this.changes.empty}get reconfigured(){return this.startState.config!=this.state.config}isUserEvent(t){let e=this.annotation(pt.userEvent);return!(!e||!(e==t||e.length>t.length&&e.slice(0,t.length)==t&&"."==e[t.length]))}}function Ot(t,e){let i=[];for(let n=0,s=0;;){let r,o;if(n<t.length&&(s==e.length||e[s]>=t[n]))r=t[n++],o=t[n++];else{if(!(s<e.length))return i;r=e[s++],o=e[s++]}!i.length||i[i.length-1]<r?i.push(r,o):i[i.length-1]<o&&(i[i.length-1]=o)}}function gt(t,e,i){var n;let s,r,o;return i?(s=e.changes,r=$.empty(e.changes.length),o=t.changes.compose(e.changes)):(s=e.changes.map(t.changes),r=t.changes.mapDesc(e.changes,!0),o=t.changes.compose(s)),{changes:o,selection:e.selection?e.selection.map(r):null===(n=t.selection)||void 0===n?void 0:n.map(s),effects:dt.mapEffects(t.effects,s).concat(dt.mapEffects(e.effects,r)),annotations:t.annotations.length?t.annotations.concat(e.annotations):e.annotations,scrollIntoView:t.scrollIntoView||e.scrollIntoView}}function mt(t,e,i){let n=e.selection,s=bt(e.annotations);return e.userEvent&&(s=s.concat(pt.userEvent.of(e.userEvent))),{changes:e.changes instanceof $?e.changes:$.of(e.changes||[],i,t.facet(rt)),selection:n&&(n instanceof Y?n:Y.single(n.anchor,n.head)),effects:bt(e.effects),annotations:s,scrollIntoView:!!e.scrollIntoView}}function wt(t,e,i){let n=mt(t,e.length?e[0]:{},t.doc.length);e.length&&!1===e[0].filter&&(i=!1);for(let s=1;s<e.length;s++){!1===e[s].filter&&(i=!1);let r=!!e[s].sequential;n=gt(n,mt(t,e[s],r?n.changes.newLength:t.doc.length),r)}let s=pt.create(t,n.changes,n.selection,n.effects,n.annotations,n.scrollIntoView);return function(t){let e=t.startState,i=e.facet(lt),n=t;for(let s=i.length-1;s>=0;s--){let r=i[s](t);r&&Object.keys(r).length&&(n=gt(n,mt(e,r,t.changes.newLength),!0))}return n==t?t:pt.create(e,t.changes,t.selection,n.effects,n.annotations,n.scrollIntoView)}(i?function(t){let e=t.startState,i=!0;for(let n of e.facet(ot)){let e=n(t);if(!1===e){i=!1;break}Array.isArray(e)&&(i=!0===i?e:Ot(i,e))}if(!0!==i){let n,s;if(!1===i)s=t.changes.invertedDesc,n=$.empty(e.doc.length);else{let e=t.changes.filter(i);n=e.changes,s=e.filtered.mapDesc(e.changes).invertedDesc}t=pt.create(e,n,t.selection&&t.selection.map(s),dt.mapEffects(t.effects,s),t.annotations,t.scrollIntoView)}let n=e.facet(at);for(let i=n.length-1;i>=0;i--){let s=n[i](t);t=s instanceof pt?s:Array.isArray(s)&&1==s.length&&s[0]instanceof pt?s[0]:wt(e,bt(s),!1)}return t}(s):s)}pt.time=ct.define(),pt.userEvent=ct.define(),pt.addToHistory=ct.define(),pt.remote=ct.define();const vt=[];function bt(t){return null==t?vt:Array.isArray(t)?t:[t]}var yt=function(t){return t[t.Word=0]="Word",t[t.Space=1]="Space",t[t.Other=2]="Other",t}(yt||(yt={}));const St=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;let xt;try{xt=new RegExp("[\\p{Alphabetic}\\p{Number}_]","u")}catch(t){}function kt(t){return e=>{if(!/\S/.test(e))return yt.Space;if(function(t){if(xt)return xt.test(t);for(let e=0;e<t.length;e++){let i=t[e];if(/\w/.test(i)||i>"€"&&(i.toUpperCase()!=i.toLowerCase()||St.test(i)))return!0}return!1}(e))return yt.Word;for(let i=0;i<t.length;i++)if(e.indexOf(t[i])>-1)return yt.Word;return yt.Other}}class Qt{constructor(t,e,i,n,s,r){this.config=t,this.doc=e,this.selection=i,this.values=n,this.status=t.statusTemplate.slice(),this.computeSlot=s,r&&(r._state=this);for(let t=0;t<this.config.dynamicSlots.length;t++)et(this,t<<1);this.computeSlot=null}field(t,e=!0){let i=this.config.address[t.id];if(null!=i)return et(this,i),it(this,i);if(e)throw new RangeError("Field is not present in this state")}update(...t){return wt(this,t,!0)}applyTransaction(t){let e,i=this.config,{base:n,compartments:s}=i;for(let e of t.effects)e.is(J.reconfigure)?(i&&(s=new Map,i.compartments.forEach(((t,e)=>s.set(e,t))),i=null),s.set(e.value.compartment,e.value.extension)):e.is(dt.reconfigure)?(i=null,n=e.value):e.is(dt.appendConfig)&&(i=null,n=bt(n).concat(e.value));if(i)e=t.startState.values.slice();else{i=tt.resolve(n,s,this),e=new Qt(i,this.doc,this.selection,i.dynamicSlots.map((()=>null)),((t,e)=>e.reconfigure(t,this)),null).values}let r=t.startState.facet(st)?t.newSelection:t.newSelection.asSingle();new Qt(i,t.newDoc,r,e,((e,i)=>i.update(e,t)),t)}replaceSelection(t){return"string"==typeof t&&(t=this.toText(t)),this.changeByRange((e=>({changes:{from:e.from,to:e.to,insert:t},range:Y.cursor(e.from+t.length)})))}changeByRange(t){let e=this.selection,i=t(e.ranges[0]),n=this.changes(i.changes),s=[i.range],r=bt(i.effects);for(let i=1;i<e.ranges.length;i++){let o=t(e.ranges[i]),a=this.changes(o.changes),l=a.map(n);for(let t=0;t<i;t++)s[t]=s[t].map(l);let h=n.mapDesc(a,!0);s.push(o.range.map(h)),n=n.compose(l),r=dt.mapEffects(r,l).concat(dt.mapEffects(bt(o.effects),h))}return{changes:n,selection:Y.create(s,e.mainIndex),effects:r}}changes(t=[]){return t instanceof $?t:$.of(t,this.doc.length,this.facet(Qt.lineSeparator))}toText(t){return e.of(t.split(this.facet(Qt.lineSeparator)||x))}sliceDoc(t=0,e=this.doc.length){return this.doc.sliceString(t,e,this.lineBreak)}facet(t){let e=this.config.address[t.id];return null==e?t.default:(et(this,e),it(this,e))}toJSON(t){let e={doc:this.sliceDoc(),selection:this.selection.toJSON()};if(t)for(let i in t){let n=t[i];n instanceof z&&null!=this.config.address[n.id]&&(e[i]=n.spec.toJSON(this.field(t[i]),this))}return e}static fromJSON(t,e={},i){if(!t||"string"!=typeof t.doc)throw new RangeError("Invalid JSON representation for EditorState");let n=[];if(i)for(let e in i)if(Object.prototype.hasOwnProperty.call(t,e)){let s=i[e],r=t[e];n.push(s.init((t=>s.spec.fromJSON(r,t))))}return Qt.create({doc:t.doc,selection:Y.fromJSON(t.selection),extensions:e.extensions?n.concat([e.extensions]):n})}static create(t={}){let i=tt.resolve(t.extensions||[],new Map),n=t.doc instanceof e?t.doc:e.of((t.doc||"").split(i.staticFacet(Qt.lineSeparator)||x)),s=t.selection?t.selection instanceof Y?t.selection:Y.single(t.selection.anchor,t.selection.head):Y.single(0);return W(s,n.length),i.staticFacet(st)||(s=s.asSingle()),new Qt(i,n,s,i.dynamicSlots.map((()=>null)),((t,e)=>e.create(t)),null)}get tabSize(){return this.facet(Qt.tabSize)}get lineBreak(){return this.facet(Qt.lineSeparator)||"\n"}get readOnly(){return this.facet(ht)}phrase(t,...e){for(let e of this.facet(Qt.phrases))if(Object.prototype.hasOwnProperty.call(e,t)){t=e[t];break}return e.length&&(t=t.replace(/\$(\$|\d*)/g,((t,i)=>{if("$"==i)return"$";let n=+(i||1);return!n||n>e.length?t:e[n-1]}))),t}languageDataAt(t,e,i=-1){let n=[];for(let s of this.facet(nt))for(let r of s(this,e,i))Object.prototype.hasOwnProperty.call(r,t)&&n.push(r[t]);return n}charCategorizer(t){return kt(this.languageDataAt("wordChars",t).join(""))}wordAt(t){let{text:e,from:i,length:n}=this.doc.lineAt(t),s=this.charCategorizer(t),r=t-i,o=t-i;for(;r>0;){let t=O(e,r,!1);if(s(e.slice(t,r))!=yt.Word)break;r=t}for(;o<n;){let t=O(e,o);if(s(e.slice(o,t))!=yt.Word)break;o=t}return r==o?null:Y.range(r+i,o+i)}}function $t(t,e,i={}){let n={};for(let e of t)for(let t of Object.keys(e)){let s=e[t],r=n[t];if(void 0===r)n[t]=s;else if(r===s||void 0===s);else{if(!Object.hasOwnProperty.call(i,t))throw new Error("Config merge conflict for field "+t);n[t]=i[t](r,s)}}for(let t in e)void 0===n[t]&&(n[t]=e[t]);return n}Qt.allowMultipleSelections=st,Qt.tabSize=j.define({combine:t=>t.length?t[0]:4}),Qt.lineSeparator=rt,Qt.readOnly=ht,Qt.phrases=j.define({compare(t,e){let i=Object.keys(t),n=Object.keys(e);return i.length==n.length&&i.every((i=>t[i]==e[i]))}}),Qt.languageData=nt,Qt.changeFilter=ot,Qt.transactionFilter=at,Qt.transactionExtender=lt,J.reconfigure=dt.define();class Pt{eq(t){return this==t}range(t,e=t){return Zt.create(t,e,this)}}Pt.prototype.startSide=Pt.prototype.endSide=0,Pt.prototype.point=!1,Pt.prototype.mapMode=k.TrackDel;let Zt=class t{constructor(t,e,i){this.from=t,this.to=e,this.value=i}static create(e,i,n){return new t(e,i,n)}};function Ct(t,e){return t.from-e.from||t.value.startSide-e.value.startSide}class Tt{constructor(t,e,i,n){this.from=t,this.to=e,this.value=i,this.maxPoint=n}get length(){return this.to[this.to.length-1]}findIndex(t,e,i,n=0){let s=i?this.to:this.from;for(let r=n,o=s.length;;){if(r==o)return r;let n=r+o>>1,a=s[n]-t||(i?this.value[n].endSide:this.value[n].startSide)-e;if(n==r)return a>=0?r:o;a>=0?o=n:r=n+1}}between(t,e,i,n){for(let s=this.findIndex(e,-1e9,!0),r=this.findIndex(i,1e9,!1,s);s<r;s++)if(!1===n(this.from[s]+t,this.to[s]+t,this.value[s]))return!1}map(t,e){let i=[],n=[],s=[],r=-1,o=-1;for(let a=0;a<this.value.length;a++){let l,h,c=this.value[a],f=this.from[a]+t,u=this.to[a]+t;if(f==u){let t=e.mapPos(f,c.startSide,c.mapMode);if(null==t)continue;if(l=h=t,c.startSide!=c.endSide&&(h=e.mapPos(f,c.endSide),h<l))continue}else if(l=e.mapPos(f,c.startSide),h=e.mapPos(u,c.endSide),l>h||l==h&&c.startSide>0&&c.endSide<=0)continue;(h-l||c.endSide-c.startSide)<0||(r<0&&(r=l),c.point&&(o=Math.max(o,h-l)),i.push(c),n.push(l-r),s.push(h-r))}return{mapped:i.length?new Tt(n,s,i,o):null,pos:r}}}class At{constructor(t,e,i,n){this.chunkPos=t,this.chunk=e,this.nextLayer=i,this.maxPoint=n}static create(t,e,i,n){return new At(t,e,i,n)}get length(){let t=this.chunk.length-1;return t<0?0:Math.max(this.chunkEnd(t),this.nextLayer.length)}get size(){if(this.isEmpty)return 0;let t=this.nextLayer.size;for(let e of this.chunk)t+=e.value.length;return t}chunkEnd(t){return this.chunkPos[t]+this.chunk[t].length}update(t){let{add:e=[],sort:i=!1,filterFrom:n=0,filterTo:s=this.length}=t,r=t.filter;if(0==e.length&&!r)return this;if(i&&(e=e.slice().sort(Ct)),this.isEmpty)return e.length?At.of(e):this;let o=new Yt(this,null,-1).goto(0),a=0,l=[],h=new Rt;for(;o.value||a<e.length;)if(a<e.length&&(o.from-e[a].from||o.startSide-e[a].value.startSide)>=0){let t=e[a++];h.addInner(t.from,t.to,t.value)||l.push(t)}else 1==o.rangeIndex&&o.chunkIndex<this.chunk.length&&(a==e.length||this.chunkEnd(o.chunkIndex)<e[a].from)&&(!r||n>this.chunkEnd(o.chunkIndex)||s<this.chunkPos[o.chunkIndex])&&h.addChunk(this.chunkPos[o.chunkIndex],this.chunk[o.chunkIndex])?o.nextChunk():((!r||n>o.to||s<o.from||r(o.from,o.to,o.value))&&(h.addInner(o.from,o.to,o.value)||l.push(Zt.create(o.from,o.to,o.value))),o.next());return h.finishInner(this.nextLayer.isEmpty&&!l.length?At.empty:this.nextLayer.update({add:l,filter:r,filterFrom:n,filterTo:s}))}map(t){if(t.empty||this.isEmpty)return this;let e=[],i=[],n=-1;for(let s=0;s<this.chunk.length;s++){let r=this.chunkPos[s],o=this.chunk[s],a=t.touchesRange(r,r+o.length);if(!1===a)n=Math.max(n,o.maxPoint),e.push(o),i.push(t.mapPos(r));else if(!0===a){let{mapped:s,pos:a}=o.map(r,t);s&&(n=Math.max(n,s.maxPoint),e.push(s),i.push(a))}}let s=this.nextLayer.map(t);return 0==e.length?s:new At(i,e,s||At.empty,n)}between(t,e,i){if(!this.isEmpty){for(let n=0;n<this.chunk.length;n++){let s=this.chunkPos[n],r=this.chunk[n];if(e>=s&&t<=s+r.length&&!1===r.between(s,t-s,e-s,i))return}this.nextLayer.between(t,e,i)}}iter(t=0){return Wt.from([this]).goto(t)}get isEmpty(){return this.nextLayer==this}static iter(t,e=0){return Wt.from(t).goto(e)}static compare(t,e,i,n,s=-1){let r=t.filter((t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=s)),o=e.filter((t=>t.maxPoint>0||!t.isEmpty&&t.maxPoint>=s)),a=Xt(r,o,i),l=new jt(r,a,s),h=new jt(o,a,s);i.iterGaps(((t,e,i)=>Dt(l,t,h,e,i,n))),i.empty&&0==i.length&&Dt(l,0,h,0,0,n)}static eq(t,e,i=0,n){null==n&&(n=999999999);let s=t.filter((t=>!t.isEmpty&&e.indexOf(t)<0)),r=e.filter((e=>!e.isEmpty&&t.indexOf(e)<0));if(s.length!=r.length)return!1;if(!s.length)return!0;let o=Xt(s,r),a=new jt(s,o,0).goto(i),l=new jt(r,o,0).goto(i);for(;;){if(a.to!=l.to||!Et(a.active,l.active)||a.point&&(!l.point||!a.point.eq(l.point)))return!1;if(a.to>n)return!0;a.next(),l.next()}}static spans(t,e,i,n,s=-1){let r=new jt(t,null,s).goto(e),o=e,a=r.openStart;for(;;){let t=Math.min(r.to,i);if(r.point){let i=r.activeForPoint(r.to),s=r.pointFrom<e?i.length+1:Math.min(i.length,a);n.point(o,t,r.point,i,s,r.pointRank),a=Math.min(r.openEnd(t),i.length)}else t>o&&(n.span(o,t,r.active,a),a=r.openEnd(t));if(r.to>i)return a+(r.point&&r.to>i?1:0);o=r.to,r.next()}}static of(t,e=!1){let i=new Rt;for(let n of t instanceof Zt?[t]:e?function(t){if(t.length>1)for(let e=t[0],i=1;i<t.length;i++){let n=t[i];if(Ct(e,n)>0)return t.slice().sort(Ct);e=n}return t}(t):t)i.add(n.from,n.to,n.value);return i.finish()}static join(t){if(!t.length)return At.empty;let e=t[t.length-1];for(let i=t.length-2;i>=0;i--)for(let n=t[i];n!=At.empty;n=n.nextLayer)e=new At(n.chunkPos,n.chunk,e,Math.max(n.maxPoint,e.maxPoint));return e}}At.empty=new At([],[],null,-1),At.empty.nextLayer=At.empty;class Rt{finishChunk(t){this.chunks.push(new Tt(this.from,this.to,this.value,this.maxPoint)),this.chunkPos.push(this.chunkStart),this.chunkStart=-1,this.setMaxPoint=Math.max(this.setMaxPoint,this.maxPoint),this.maxPoint=-1,t&&(this.from=[],this.to=[],this.value=[])}constructor(){this.chunks=[],this.chunkPos=[],this.chunkStart=-1,this.last=null,this.lastFrom=-1e9,this.lastTo=-1e9,this.from=[],this.to=[],this.value=[],this.maxPoint=-1,this.setMaxPoint=-1,this.nextLayer=null}add(t,e,i){this.addInner(t,e,i)||(this.nextLayer||(this.nextLayer=new Rt)).add(t,e,i)}addInner(t,e,i){let n=t-this.lastTo||i.startSide-this.last.endSide;if(n<=0&&(t-this.lastFrom||i.startSide-this.last.startSide)<0)throw new Error("Ranges must be added sorted by `from` position and `startSide`");return!(n<0)&&(250==this.from.length&&this.finishChunk(!0),this.chunkStart<0&&(this.chunkStart=t),this.from.push(t-this.chunkStart),this.to.push(e-this.chunkStart),this.last=i,this.lastFrom=t,this.lastTo=e,this.value.push(i),i.point&&(this.maxPoint=Math.max(this.maxPoint,e-t)),!0)}addChunk(t,e){if((t-this.lastTo||e.value[0].startSide-this.last.endSide)<0)return!1;this.from.length&&this.finishChunk(!0),this.setMaxPoint=Math.max(this.setMaxPoint,e.maxPoint),this.chunks.push(e),this.chunkPos.push(t);let i=e.value.length-1;return this.last=e.value[i],this.lastFrom=e.from[i]+t,this.lastTo=e.to[i]+t,!0}finish(){return this.finishInner(At.empty)}finishInner(t){if(this.from.length&&this.finishChunk(!1),0==this.chunks.length)return t;let e=At.create(this.chunkPos,this.chunks,this.nextLayer?this.nextLayer.finishInner(t):t,this.setMaxPoint);return this.from=null,e}}function Xt(t,e,i){let n=new Map;for(let e of t)for(let t=0;t<e.chunk.length;t++)e.chunk[t].maxPoint<=0&&n.set(e.chunk[t],e.chunkPos[t]);let s=new Set;for(let t of e)for(let e=0;e<t.chunk.length;e++){let r=n.get(t.chunk[e]);null==r||(i?i.mapPos(r):r)!=t.chunkPos[e]||(null==i?void 0:i.touchesRange(r,r+t.chunk[e].length))||s.add(t.chunk[e])}return s}class Yt{constructor(t,e,i,n=0){this.layer=t,this.skip=e,this.minPoint=i,this.rank=n}get startSide(){return this.value?this.value.startSide:0}get endSide(){return this.value?this.value.endSide:0}goto(t,e=-1e9){return this.chunkIndex=this.rangeIndex=0,this.gotoInner(t,e,!1),this}gotoInner(t,e,i){for(;this.chunkIndex<this.layer.chunk.length;){let e=this.layer.chunk[this.chunkIndex];if(!(this.skip&&this.skip.has(e)||this.layer.chunkEnd(this.chunkIndex)<t||e.maxPoint<this.minPoint))break;this.chunkIndex++,i=!1}if(this.chunkIndex<this.layer.chunk.length){let n=this.layer.chunk[this.chunkIndex].findIndex(t-this.layer.chunkPos[this.chunkIndex],e,!0);(!i||this.rangeIndex<n)&&this.setRangeIndex(n)}this.next()}forward(t,e){(this.to-t||this.endSide-e)<0&&this.gotoInner(t,e,!0)}next(){for(;;){if(this.chunkIndex==this.layer.chunk.length){this.from=this.to=1e9,this.value=null;break}{let t=this.layer.chunkPos[this.chunkIndex],e=this.layer.chunk[this.chunkIndex],i=t+e.from[this.rangeIndex];if(this.from=i,this.to=t+e.to[this.rangeIndex],this.value=e.value[this.rangeIndex],this.setRangeIndex(this.rangeIndex+1),this.minPoint<0||this.value.point&&this.to-this.from>=this.minPoint)break}}}setRangeIndex(t){if(t==this.layer.chunk[this.chunkIndex].value.length){if(this.chunkIndex++,this.skip)for(;this.chunkIndex<this.layer.chunk.length&&this.skip.has(this.layer.chunk[this.chunkIndex]);)this.chunkIndex++;this.rangeIndex=0}else this.rangeIndex=t}nextChunk(){this.chunkIndex++,this.rangeIndex=0,this.next()}compare(t){return this.from-t.from||this.startSide-t.startSide||this.rank-t.rank||this.to-t.to||this.endSide-t.endSide}}class Wt{constructor(t){this.heap=t}static from(t,e=null,i=-1){let n=[];for(let s=0;s<t.length;s++)for(let r=t[s];!r.isEmpty;r=r.nextLayer)r.maxPoint>=i&&n.push(new Yt(r,e,i,s));return 1==n.length?n[0]:new Wt(n)}get startSide(){return this.value?this.value.startSide:0}goto(t,e=-1e9){for(let i of this.heap)i.goto(t,e);for(let t=this.heap.length>>1;t>=0;t--)Mt(this.heap,t);return this.next(),this}forward(t,e){for(let i of this.heap)i.forward(t,e);for(let t=this.heap.length>>1;t>=0;t--)Mt(this.heap,t);(this.to-t||this.value.endSide-e)<0&&this.next()}next(){if(0==this.heap.length)this.from=this.to=1e9,this.value=null,this.rank=-1;else{let t=this.heap[0];this.from=t.from,this.to=t.to,this.value=t.value,this.rank=t.rank,t.value&&t.next(),Mt(this.heap,0)}}}function Mt(t,e){for(let i=t[e];;){let n=1+(e<<1);if(n>=t.length)break;let s=t[n];if(n+1<t.length&&s.compare(t[n+1])>=0&&(s=t[n+1],n++),i.compare(s)<0)break;t[n]=i,t[e]=s,e=n}}class jt{constructor(t,e,i){this.minPoint=i,this.active=[],this.activeTo=[],this.activeRank=[],this.minActive=-1,this.point=null,this.pointFrom=0,this.pointRank=0,this.to=-1e9,this.endSide=0,this.openStart=-1,this.cursor=Wt.from(t,e,i)}goto(t,e=-1e9){return this.cursor.goto(t,e),this.active.length=this.activeTo.length=this.activeRank.length=0,this.minActive=-1,this.to=t,this.endSide=e,this.openStart=-1,this.next(),this}forward(t,e){for(;this.minActive>-1&&(this.activeTo[this.minActive]-t||this.active[this.minActive].endSide-e)<0;)this.removeActive(this.minActive);this.cursor.forward(t,e)}removeActive(t){qt(this.active,t),qt(this.activeTo,t),qt(this.activeRank,t),this.minActive=Vt(this.active,this.activeTo)}addActive(t){let e=0,{value:i,to:n,rank:s}=this.cursor;for(;e<this.activeRank.length&&(s-this.activeRank[e]||n-this.activeTo[e])>0;)e++;_t(this.active,e,i),_t(this.activeTo,e,n),_t(this.activeRank,e,s),t&&_t(t,e,this.cursor.from),this.minActive=Vt(this.active,this.activeTo)}next(){let t=this.to,e=this.point;this.point=null;let i=this.openStart<0?[]:null;for(;;){let n=this.minActive;if(n>-1&&(this.activeTo[n]-this.cursor.from||this.active[n].endSide-this.cursor.startSide)<0){if(this.activeTo[n]>t){this.to=this.activeTo[n],this.endSide=this.active[n].endSide;break}this.removeActive(n),i&&qt(i,n)}else{if(!this.cursor.value){this.to=this.endSide=1e9;break}if(this.cursor.from>t){this.to=this.cursor.from,this.endSide=this.cursor.startSide;break}{let t=this.cursor.value;if(t.point){if(!(e&&this.cursor.to==this.to&&this.cursor.from<this.cursor.to)){this.point=t,this.pointFrom=this.cursor.from,this.pointRank=this.cursor.rank,this.to=this.cursor.to,this.endSide=t.endSide,this.cursor.next(),this.forward(this.to,this.endSide);break}this.cursor.next()}else this.addActive(i),this.cursor.next()}}}if(i){this.openStart=0;for(let e=i.length-1;e>=0&&i[e]<t;e--)this.openStart++}}activeForPoint(t){if(!this.active.length)return this.active;let e=[];for(let i=this.active.length-1;i>=0&&!(this.activeRank[i]<this.pointRank);i--)(this.activeTo[i]>t||this.activeTo[i]==t&&this.active[i].endSide>=this.point.endSide)&&e.push(this.active[i]);return e.reverse()}openEnd(t){let e=0;for(let i=this.activeTo.length-1;i>=0&&this.activeTo[i]>t;i--)e++;return e}}function Dt(t,e,i,n,s,r){t.goto(e),i.goto(n);let o=n+s,a=n,l=n-e;for(;;){let e=t.to+l-i.to||t.endSide-i.endSide,n=e<0?t.to+l:i.to,s=Math.min(n,o);if(t.point||i.point?t.point&&i.point&&(t.point==i.point||t.point.eq(i.point))&&Et(t.activeForPoint(t.to),i.activeForPoint(i.to))||r.comparePoint(a,s,t.point,i.point):s>a&&!Et(t.active,i.active)&&r.compareRange(a,s,t.active,i.active),n>o)break;a=n,e<=0&&t.next(),e>=0&&i.next()}}function Et(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++)if(t[i]!=e[i]&&!t[i].eq(e[i]))return!1;return!0}function qt(t,e){for(let i=e,n=t.length-1;i<n;i++)t[i]=t[i+1];t.pop()}function _t(t,e,i){for(let i=t.length-1;i>=e;i--)t[i+1]=t[i];t[e]=i}function Vt(t,e){let i=-1,n=1e9;for(let s=0;s<e.length;s++)(e[s]-n||t[s].endSide-t[i].endSide)<0&&(i=s,n=e[s]);return i}function It(t,e,i=t.length){let n=0;for(let s=0;s<i;)9==t.charCodeAt(s)?(n+=e-n%e,s++):(n++,s=O(t,s));return n}function zt(t,e,i,n){for(let n=0,s=0;;){if(s>=e)return n;if(n==t.length)break;s+=9==t.charCodeAt(n)?i-s%i:1,n=O(t,n)}return!0===n?-1:t.length}var Bt=Object.freeze({__proto__:null,Annotation:ct,AnnotationType:ft,ChangeDesc:Q,ChangeSet:$,get CharCategory(){return yt},Compartment:J,EditorSelection:Y,EditorState:Qt,Facet:j,Line:h,get MapMode(){return k},Prec:H,Range:Zt,RangeSet:At,RangeSetBuilder:Rt,RangeValue:Pt,SelectionRange:X,StateEffect:dt,StateEffectType:ut,StateField:z,Text:e,Transaction:pt,codePointAt:b,codePointSize:S,combineConfig:$t,countColumn:It,findClusterBreak:O,findColumn:zt,fromCodePoint:y});const Gt="undefined"==typeof Symbol?"__ͼ":Symbol.for("ͼ"),Lt="undefined"==typeof Symbol?"__styleSet"+Math.floor(1e8*Math.random()):Symbol("styleSet"),Nt="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:{};class Ut{constructor(t,e){this.rules=[];let{finish:i}=e||{};function n(t){return/^@/.test(t)?[t]:t.split(/,\s*/)}function s(t,e,r,o){let a=[],l=/^@(\w+)\b/.exec(t[0]),h=l&&"keyframes"==l[1];if(l&&null==e)return r.push(t[0]+";");for(let i in e){let o=e[i];if(/&/.test(i))s(i.split(/,\s*/).map((e=>t.map((t=>e.replace(/&/,t))))).reduce(((t,e)=>t.concat(e))),o,r);else if(o&&"object"==typeof o){if(!l)throw new RangeError("The value of a property ("+i+") should be a primitive value.");s(n(i),o,a,h)}else null!=o&&a.push(i.replace(/_.*/,"").replace(/[A-Z]/g,(t=>"-"+t.toLowerCase()))+": "+o+";")}(a.length||h)&&r.push((!i||l||o?t:t.map(i)).join(", ")+" {"+a.join(" ")+"}")}for(let e in t)s(n(e),t[e],this.rules)}getRules(){return this.rules.join("\n")}static newName(){let t=Nt[Gt]||1;return Nt[Gt]=t+1,"ͼ"+t.toString(36)}static mount(t,e,i){let n=t[Lt],s=i&&i.nonce;n?s&&n.setNonce(s):n=new Ft(t,s),n.mount(Array.isArray(e)?e:[e])}}let Ht=new Map;class Ft{constructor(t,e){let i=t.ownerDocument||t,n=i.defaultView;if(!t.head&&t.adoptedStyleSheets&&n.CSSStyleSheet){let e=Ht.get(i);if(e)return t.adoptedStyleSheets=[e.sheet,...t.adoptedStyleSheets],t[Lt]=e;this.sheet=new n.CSSStyleSheet,t.adoptedStyleSheets=[this.sheet,...t.adoptedStyleSheets],Ht.set(i,this)}else{this.styleTag=i.createElement("style"),e&&this.styleTag.setAttribute("nonce",e);let n=t.head||t;n.insertBefore(this.styleTag,n.firstChild)}this.modules=[],t[Lt]=this}mount(t){let e=this.sheet,i=0,n=0;for(let s=0;s<t.length;s++){let r=t[s],o=this.modules.indexOf(r);if(o<n&&o>-1&&(this.modules.splice(o,1),n--,o=-1),-1==o){if(this.modules.splice(n++,0,r),e)for(let t=0;t<r.rules.length;t++)e.insertRule(r.rules[t],i++)}else{for(;n<o;)i+=this.modules[n++].rules.length;i+=r.rules.length,n++}}if(!e){let t="";for(let e=0;e<this.modules.length;e++)t+=this.modules[e].getRules()+"\n";this.styleTag.textContent=t}}setNonce(t){this.styleTag&&this.styleTag.getAttribute("nonce")!=t&&this.styleTag.setAttribute("nonce",t)}}for(var Jt={8:"Backspace",9:"Tab",10:"Enter",12:"NumLock",13:"Enter",16:"Shift",17:"Control",18:"Alt",20:"CapsLock",27:"Escape",32:" ",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"ArrowLeft",38:"ArrowUp",39:"ArrowRight",40:"ArrowDown",44:"PrintScreen",45:"Insert",46:"Delete",59:";",61:"=",91:"Meta",92:"Meta",106:"*",107:"+",108:",",109:"-",110:".",111:"/",144:"NumLock",145:"ScrollLock",160:"Shift",161:"Shift",162:"Control",163:"Control",164:"Alt",165:"Alt",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},Kt={48:")",49:"!",50:"@",51:"#",52:"$",53:"%",54:"^",55:"&",56:"*",57:"(",59:":",61:"+",173:"_",186:":",187:"+",188:"<",189:"_",190:">",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},te="undefined"!=typeof navigator&&/Mac/.test(navigator.platform),ee="undefined"!=typeof navigator&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent),ie=0;ie<10;ie++)Jt[48+ie]=Jt[96+ie]=String(ie);for(ie=1;ie<=24;ie++)Jt[ie+111]="F"+ie;for(ie=65;ie<=90;ie++)Jt[ie]=String.fromCharCode(ie+32),Kt[ie]=String.fromCharCode(ie);for(var ne in Jt)Kt.hasOwnProperty(ne)||(Kt[ne]=Jt[ne]);function se(t){let e;return e=11==t.nodeType?t.getSelection?t:t.ownerDocument:t,e.getSelection()}function re(t,e){return!!e&&(t==e||t.contains(1!=e.nodeType?e.parentNode:e))}function oe(t,e){if(!e.anchorNode)return!1;try{return re(t,e.anchorNode)}catch(t){return!1}}function ae(t){return 3==t.nodeType?ve(t,0,t.nodeValue.length).getClientRects():1==t.nodeType?t.getClientRects():[]}function le(t,e,i,n){return!!i&&(ce(t,e,i,n,-1)||ce(t,e,i,n,1))}function he(t){for(var e=0;;e++)if(!(t=t.previousSibling))return e}function ce(t,e,i,n,s){for(;;){if(t==i&&e==n)return!0;if(e==(s<0?0:fe(t))){if("DIV"==t.nodeName)return!1;let i=t.parentNode;if(!i||1!=i.nodeType)return!1;e=he(t)+(s<0?0:1),t=i}else{if(1!=t.nodeType)return!1;if(1==(t=t.childNodes[e+(s<0?-1:0)]).nodeType&&"false"==t.contentEditable)return!1;e=s<0?fe(t):0}}}function fe(t){return 3==t.nodeType?t.nodeValue.length:t.childNodes.length}function ue(t,e){let i=e?t.left:t.right;return{left:i,right:i,top:t.top,bottom:t.bottom}}function de(t){return{left:0,right:t.innerWidth,top:0,bottom:t.innerHeight}}function pe(t,e){let i=e.width/t.offsetWidth,n=e.height/t.offsetHeight;return(i>.995&&i<1.005||!isFinite(i)||Math.abs(e.width-t.offsetWidth)<1)&&(i=1),(n>.995&&n<1.005||!isFinite(n)||Math.abs(e.height-t.offsetHeight)<1)&&(n=1),{scaleX:i,scaleY:n}}class Oe{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}eq(t){return this.anchorNode==t.anchorNode&&this.anchorOffset==t.anchorOffset&&this.focusNode==t.focusNode&&this.focusOffset==t.focusOffset}setRange(t){let{anchorNode:e,focusNode:i}=t;this.set(e,Math.min(t.anchorOffset,e?fe(e):0),i,Math.min(t.focusOffset,i?fe(i):0))}set(t,e,i,n){this.anchorNode=t,this.anchorOffset=e,this.focusNode=i,this.focusOffset=n}}let ge,me=null;function we(t){if(t.setActive)return t.setActive();if(me)return t.focus(me);let e=[];for(let i=t;i&&(e.push(i,i.scrollTop,i.scrollLeft),i!=i.ownerDocument);i=i.parentNode);if(t.focus(null==me?{get preventScroll(){return me={preventScroll:!0},!0}}:void 0),!me){me=!1;for(let t=0;t<e.length;){let i=e[t++],n=e[t++],s=e[t++];i.scrollTop!=n&&(i.scrollTop=n),i.scrollLeft!=s&&(i.scrollLeft=s)}}}function ve(t,e,i=e){let n=ge||(ge=document.createRange());return n.setEnd(t,i),n.setStart(t,e),n}function be(t,e,i){let n={key:e,code:e,keyCode:i,which:i,cancelable:!0},s=new KeyboardEvent("keydown",n);s.synthetic=!0,t.dispatchEvent(s);let r=new KeyboardEvent("keyup",n);return r.synthetic=!0,t.dispatchEvent(r),s.defaultPrevented||r.defaultPrevented}function ye(t){for(;t.attributes.length;)t.removeAttributeNode(t.attributes[0])}function Se(t){return t.scrollTop>Math.max(1,t.scrollHeight-t.clientHeight-4)}class xe{constructor(t,e,i=!0){this.node=t,this.offset=e,this.precise=i}static before(t,e){return new xe(t.parentNode,he(t),e)}static after(t,e){return new xe(t.parentNode,he(t)+1,e)}}const ke=[];class Qe{constructor(){this.parent=null,this.dom=null,this.flags=2}get overrideDOMText(){return null}get posAtStart(){return this.parent?this.parent.posBefore(this):0}get posAtEnd(){return this.posAtStart+this.length}posBefore(t){let e=this.posAtStart;for(let i of this.children){if(i==t)return e;e+=i.length+i.breakAfter}throw new RangeError("Invalid child in posBefore")}posAfter(t){return this.posBefore(t)+t.length}sync(t,e){if(2&this.flags){let i,n=this.dom,s=null;for(let r of this.children){if(7&r.flags){if(!r.dom&&(i=s?s.nextSibling:n.firstChild)){let t=Qe.get(i);(!t||!t.parent&&t.canReuseDOM(r))&&r.reuseDOM(i)}r.sync(t,e),r.flags&=-8}if(i=s?s.nextSibling:n.firstChild,e&&!e.written&&e.node==n&&i!=r.dom&&(e.written=!0),r.dom.parentNode==n)for(;i&&i!=r.dom;)i=$e(i);else n.insertBefore(r.dom,i);s=r.dom}for(i=s?s.nextSibling:n.firstChild,i&&e&&e.node==n&&(e.written=!0);i;)i=$e(i)}else if(1&this.flags)for(let i of this.children)7&i.flags&&(i.sync(t,e),i.flags&=-8)}reuseDOM(t){}localPosFromDOM(t,e){let i;if(t==this.dom)i=this.dom.childNodes[e];else{let n=0==fe(t)?0:0==e?-1:1;for(;;){let e=t.parentNode;if(e==this.dom)break;0==n&&e.firstChild!=e.lastChild&&(n=t==e.firstChild?-1:1),t=e}i=n<0?t:t.nextSibling}if(i==this.dom.firstChild)return 0;for(;i&&!Qe.get(i);)i=i.nextSibling;if(!i)return this.length;for(let t=0,e=0;;t++){let n=this.children[t];if(n.dom==i)return e;e+=n.length+n.breakAfter}}domBoundsAround(t,e,i=0){let n=-1,s=-1,r=-1,o=-1;for(let a=0,l=i,h=i;a<this.children.length;a++){let i=this.children[a],c=l+i.length;if(l<t&&c>e)return i.domBoundsAround(t,e,l);if(c>=t&&-1==n&&(n=a,s=l),l>e&&i.dom.parentNode==this.dom){r=a,o=h;break}h=c,l=c+i.breakAfter}return{from:s,to:o<0?i+this.length:o,startDOM:(n?this.children[n-1].dom.nextSibling:null)||this.dom.firstChild,endDOM:r<this.children.length&&r>=0?this.children[r].dom:null}}markDirty(t=!1){this.flags|=2,this.markParentsDirty(t)}markParentsDirty(t){for(let e=this.parent;e;e=e.parent){if(t&&(e.flags|=2),1&e.flags)return;e.flags|=1,t=!1}}setParent(t){this.parent!=t&&(this.parent=t,7&this.flags&&this.markParentsDirty(!0))}setDOM(t){this.dom!=t&&(this.dom&&(this.dom.cmView=null),this.dom=t,t.cmView=this)}get rootView(){for(let t=this;;){let e=t.parent;if(!e)return t;t=e}}replaceChildren(t,e,i=ke){this.markDirty();for(let n=t;n<e;n++){let t=this.children[n];t.parent==this&&i.indexOf(t)<0&&t.destroy()}this.children.splice(t,e-t,...i);for(let t=0;t<i.length;t++)i[t].setParent(this)}ignoreMutation(t){return!1}ignoreEvent(t){return!1}childCursor(t=this.length){return new Pe(this.children,t,this.children.length)}childPos(t,e=1){return this.childCursor().findPos(t,e)}toString(){let t=this.constructor.name.replace("View","");return t+(this.children.length?"("+this.children.join()+")":this.length?"["+("Text"==t?this.text:this.length)+"]":"")+(this.breakAfter?"#":"")}static get(t){return t.cmView}get isEditable(){return!0}get isWidget(){return!1}get isHidden(){return!1}merge(t,e,i,n,s,r){return!1}become(t){return!1}canReuseDOM(t){return t.constructor==this.constructor&&!(8&(this.flags|t.flags))}getSide(){return 0}destroy(){for(let t of this.children)t.parent==this&&t.destroy();this.parent=null}}function $e(t){let e=t.nextSibling;return t.parentNode.removeChild(t),e}Qe.prototype.breakAfter=0;class Pe{constructor(t,e,i){this.children=t,this.pos=e,this.i=i,this.off=0}findPos(t,e=1){for(;;){if(t>this.pos||t==this.pos&&(e>0||0==this.i||this.children[this.i-1].breakAfter))return this.off=t-this.pos,this;let i=this.children[--this.i];this.pos-=i.length+i.breakAfter}}}function Ze(t,e,i,n,s,r,o,a,l){let{children:h}=t,c=h.length?h[e]:null,f=r.length?r[r.length-1]:null,u=f?f.breakAfter:o;if(!(e==n&&c&&!o&&!u&&r.length<2&&c.merge(i,s,r.length?f:null,0==i,a,l))){if(n<h.length){let t=h[n];t&&(s<t.length||t.breakAfter&&(null==f?void 0:f.breakAfter))?(e==n&&(t=t.split(s),s=0),!u&&f&&t.merge(0,s,f,!0,0,l)?r[r.length-1]=t:((s||t.children.length&&!t.children[0].length)&&t.merge(0,s,null,!1,0,l),r.push(t))):(null==t?void 0:t.breakAfter)&&(f?f.breakAfter=1:o=1),n++}for(c&&(c.breakAfter=o,i>0&&(!o&&r.length&&c.merge(i,c.length,r[0],!1,a,0)?c.breakAfter=r.shift().breakAfter:(i<c.length||c.children.length&&0==c.children[c.children.length-1].length)&&c.merge(i,c.length,null,!1,a,0),e++));e<n&&r.length;)if(h[n-1].become(r[r.length-1]))n--,r.pop(),l=r.length?0:a;else{if(!h[e].become(r[0]))break;e++,r.shift(),a=r.length?0:l}!r.length&&e&&n<h.length&&!h[e-1].breakAfter&&h[n].merge(0,0,h[e-1],!1,a,l)&&e--,(e<n||r.length)&&t.replaceChildren(e,n,r)}}function Ce(t,e,i,n,s,r){let o=t.childCursor(),{i:a,off:l}=o.findPos(i,1),{i:h,off:c}=o.findPos(e,-1),f=e-i;for(let t of n)f+=t.length;t.length+=f,Ze(t,h,c,a,l,n,0,s,r)}let Te="undefined"!=typeof navigator?navigator:{userAgent:"",vendor:"",platform:""},Ae="undefined"!=typeof document?document:{documentElement:{style:{}}};const Re=/Edge\/(\d+)/.exec(Te.userAgent),Xe=/MSIE \d/.test(Te.userAgent),Ye=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(Te.userAgent),We=!!(Xe||Ye||Re),Me=!We&&/gecko\/(\d+)/i.test(Te.userAgent),je=!We&&/Chrome\/(\d+)/.exec(Te.userAgent),De="webkitFontSmoothing"in Ae.documentElement.style,Ee=!We&&/Apple Computer/.test(Te.vendor),qe=Ee&&(/Mobile\/\w+/.test(Te.userAgent)||Te.maxTouchPoints>2);var _e={mac:qe||/Mac/.test(Te.platform),windows:/Win/.test(Te.platform),linux:/Linux|X11/.test(Te.platform),ie:We,ie_version:Xe?Ae.documentMode||6:Ye?+Ye[1]:Re?+Re[1]:0,gecko:Me,gecko_version:Me?+(/Firefox\/(\d+)/.exec(Te.userAgent)||[0,0])[1]:0,chrome:!!je,chrome_version:je?+je[1]:0,ios:qe,android:/Android\b/.test(Te.userAgent),webkit:De,safari:Ee,webkit_version:De?+(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent)||[0,0])[1]:0,tabSize:null!=Ae.documentElement.style.tabSize?"tab-size":"-moz-tab-size"};class Ve extends Qe{constructor(t){super(),this.text=t}get length(){return this.text.length}createDOM(t){this.setDOM(t||document.createTextNode(this.text))}sync(t,e){this.dom||this.createDOM(),this.dom.nodeValue!=this.text&&(e&&e.node==this.dom&&(e.written=!0),this.dom.nodeValue=this.text)}reuseDOM(t){3==t.nodeType&&this.createDOM(t)}merge(t,e,i){return!(8&this.flags||i&&(!(i instanceof Ve)||this.length-(e-t)+i.length>256||8&i.flags))&&(this.text=this.text.slice(0,t)+(i?i.text:"")+this.text.slice(e),this.markDirty(),!0)}split(t){let e=new Ve(this.text.slice(t));return this.text=this.text.slice(0,t),this.markDirty(),e.flags|=8&this.flags,e}localPosFromDOM(t,e){return t==this.dom?e:e?this.text.length:0}domAtPos(t){return new xe(this.dom,t)}domBoundsAround(t,e,i){return{from:i,to:i+this.length,startDOM:this.dom,endDOM:this.dom.nextSibling}}coordsAt(t,e){return function(t,e,i){let n=t.nodeValue.length;e>n&&(e=n);let s=e,r=e,o=0;0==e&&i<0||e==n&&i>=0?_e.chrome||_e.gecko||(e?(s--,o=1):r<n&&(r++,o=-1)):i<0?s--:r<n&&r++;let a=ve(t,s,r).getClientRects();if(!a.length)return null;let l=a[(o?o<0:i>=0)?0:a.length-1];_e.safari&&!o&&0==l.width&&(l=Array.prototype.find.call(a,(t=>t.width))||l);return o?ue(l,o<0):l||null}(this.dom,t,e)}}class Ie extends Qe{constructor(t,e=[],i=0){super(),this.mark=t,this.children=e,this.length=i;for(let t of e)t.setParent(this)}setAttrs(t){if(ye(t),this.mark.class&&(t.className=this.mark.class),this.mark.attrs)for(let e in this.mark.attrs)t.setAttribute(e,this.mark.attrs[e]);return t}canReuseDOM(t){return super.canReuseDOM(t)&&!(8&(this.flags|t.flags))}reuseDOM(t){t.nodeName==this.mark.tagName.toUpperCase()&&(this.setDOM(t),this.flags|=6)}sync(t,e){this.dom?4&this.flags&&this.setAttrs(this.dom):this.setDOM(this.setAttrs(document.createElement(this.mark.tagName))),super.sync(t,e)}merge(t,e,i,n,s,r){return(!i||!(!(i instanceof Ie&&i.mark.eq(this.mark))||t&&s<=0||e<this.length&&r<=0))&&(Ce(this,t,e,i?i.children.slice():[],s-1,r-1),this.markDirty(),!0)}split(t){let e=[],i=0,n=-1,s=0;for(let r of this.children){let o=i+r.length;o>t&&e.push(i<t?r.split(t-i):r),n<0&&i>=t&&(n=s),i=o,s++}let r=this.length-t;return this.length=t,n>-1&&(this.children.length=n,this.markDirty()),new Ie(this.mark,e,r)}domAtPos(t){return Ge(this,t)}coordsAt(t,e){return Ne(this,t,e)}}class ze extends Qe{static create(t,e,i){return new ze(t,e,i)}constructor(t,e,i){super(),this.widget=t,this.length=e,this.side=i,this.prevWidget=null}split(t){let e=ze.create(this.widget,this.length-t,this.side);return this.length-=t,e}sync(t){this.dom&&this.widget.updateDOM(this.dom,t)||(this.dom&&this.prevWidget&&this.prevWidget.destroy(this.dom),this.prevWidget=null,this.setDOM(this.widget.toDOM(t)),this.widget.editable||(this.dom.contentEditable="false"))}getSide(){return this.side}merge(t,e,i,n,s,r){return!(i&&(!(i instanceof ze&&this.widget.compare(i.widget))||t>0&&s<=0||e<this.length&&r<=0))&&(this.length=t+(i?i.length:0)+(this.length-e),!0)}become(t){return t instanceof ze&&t.side==this.side&&this.widget.constructor==t.widget.constructor&&(this.widget.compare(t.widget)||this.markDirty(!0),this.dom&&!this.prevWidget&&(this.prevWidget=this.widget),this.widget=t.widget,this.length=t.length,!0)}ignoreMutation(){return!0}ignoreEvent(t){return this.widget.ignoreEvent(t)}get overrideDOMText(){if(0==this.length)return e.empty;let t=this;for(;t.parent;)t=t.parent;let{view:i}=t,n=i&&i.state.doc,s=this.posAtStart;return n?n.slice(s,s+this.length):e.empty}domAtPos(t){return(this.length?0==t:this.side>0)?xe.before(this.dom):xe.after(this.dom,t==this.length)}domBoundsAround(){return null}coordsAt(t,e){let i=this.widget.coordsAt(this.dom,t,e);if(i)return i;let n=this.dom.getClientRects(),s=null;if(!n.length)return null;let r=this.side?this.side<0:t>0;for(let e=r?n.length-1:0;s=n[e],!(t>0?0==e:e==n.length-1||s.top<s.bottom);e+=r?-1:1);return ue(s,!r)}get isEditable(){return!1}get isWidget(){return!0}get isHidden(){return this.widget.isHidden}destroy(){super.destroy(),this.dom&&this.widget.destroy(this.dom)}}class Be extends Qe{constructor(t){super(),this.side=t}get length(){return 0}merge(){return!1}become(t){return t instanceof Be&&t.side==this.side}split(){return new Be(this.side)}sync(){if(!this.dom){let t=document.createElement("img");t.className="cm-widgetBuffer",t.setAttribute("aria-hidden","true"),this.setDOM(t)}}getSide(){return this.side}domAtPos(t){return this.side>0?xe.before(this.dom):xe.after(this.dom)}localPosFromDOM(){return 0}domBoundsAround(){return null}coordsAt(t){return this.dom.getBoundingClientRect()}get overrideDOMText(){return e.empty}get isHidden(){return!0}}function Ge(t,e){let i=t.dom,{children:n}=t,s=0;for(let t=0;s<n.length;s++){let r=n[s],o=t+r.length;if(!(o==t&&r.getSide()<=0)){if(e>t&&e<o&&r.dom.parentNode==i)return r.domAtPos(e-t);if(e<=t)break;t=o}}for(let t=s;t>0;t--){let e=n[t-1];if(e.dom.parentNode==i)return e.domAtPos(e.length)}for(let t=s;t<n.length;t++){let e=n[t];if(e.dom.parentNode==i)return e.domAtPos(0)}return new xe(i,0)}function Le(t,e,i){let n,{children:s}=t;i>0&&e instanceof Ie&&s.length&&(n=s[s.length-1])instanceof Ie&&n.mark.eq(e.mark)?Le(n,e.children[0],i-1):(s.push(e),e.setParent(t)),t.length+=e.length}function Ne(t,e,i){let n=null,s=-1,r=null,o=-1;!function t(e,a){for(let l=0,h=0;l<e.children.length&&h<=a;l++){let c=e.children[l],f=h+c.length;f>=a&&(c.children.length?t(c,a-h):(!r||r.isHidden&&i>0)&&(f>a||h==f&&c.getSide()>0)?(r=c,o=a-h):(h<a||h==f&&c.getSide()<0&&!c.isHidden)&&(n=c,s=a-h)),h=f}}(t,e);let a=(i<0?n:r)||n||r;return a?a.coordsAt(Math.max(0,a==n?s:o),i):function(t){let e=t.dom.lastChild;if(!e)return t.dom.getBoundingClientRect();let i=ae(e);return i[i.length-1]||null}(t)}function Ue(t,e){for(let i in t)"class"==i&&e.class?e.class+=" "+t.class:"style"==i&&e.style?e.style+=";"+t.style:e[i]=t[i];return e}Ve.prototype.children=ze.prototype.children=Be.prototype.children=ke;const He=Object.create(null);function Fe(t,e,i){if(t==e)return!0;t||(t=He),e||(e=He);let n=Object.keys(t),s=Object.keys(e);if(n.length-(i&&n.indexOf(i)>-1?1:0)!=s.length-(i&&s.indexOf(i)>-1?1:0))return!1;for(let r of n)if(r!=i&&(-1==s.indexOf(r)||t[r]!==e[r]))return!1;return!0}function Je(t,e,i){let n=!1;if(e)for(let s in e)i&&s in i||(n=!0,"style"==s?t.style.cssText="":t.removeAttribute(s));if(i)for(let s in i)e&&e[s]==i[s]||(n=!0,"style"==s?t.style.cssText=i[s]:t.setAttribute(s,i[s]));return n}function Ke(t){let e=Object.create(null);for(let i=0;i<t.attributes.length;i++){let n=t.attributes[i];e[n.name]=n.value}return e}class ti extends Qe{constructor(){super(...arguments),this.children=[],this.length=0,this.prevAttrs=void 0,this.attrs=null,this.breakAfter=0}merge(t,e,i,n,s,r){if(i){if(!(i instanceof ti))return!1;this.dom||i.transferDOM(this)}return n&&this.setDeco(i?i.attrs:null),Ce(this,t,e,i?i.children.slice():[],s,r),!0}split(t){let e=new ti;if(e.breakAfter=this.breakAfter,0==this.length)return e;let{i:i,off:n}=this.childPos(t);n&&(e.append(this.children[i].split(n),0),this.children[i].merge(n,this.children[i].length,null,!1,0,0),i++);for(let t=i;t<this.children.length;t++)e.append(this.children[t],0);for(;i>0&&0==this.children[i-1].length;)this.children[--i].destroy();return this.children.length=i,this.markDirty(),this.length=t,e}transferDOM(t){this.dom&&(this.markDirty(),t.setDOM(this.dom),t.prevAttrs=void 0===this.prevAttrs?this.attrs:this.prevAttrs,this.prevAttrs=void 0,this.dom=null)}setDeco(t){Fe(this.attrs,t)||(this.dom&&(this.prevAttrs=this.attrs,this.markDirty()),this.attrs=t)}append(t,e){Le(this,t,e)}addLineDeco(t){let e=t.spec.attributes,i=t.spec.class;e&&(this.attrs=Ue(e,this.attrs||{})),i&&(this.attrs=Ue({class:i},this.attrs||{}))}domAtPos(t){return Ge(this,t)}reuseDOM(t){"DIV"==t.nodeName&&(this.setDOM(t),this.flags|=6)}sync(t,e){var i;this.dom?4&this.flags&&(ye(this.dom),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0):(this.setDOM(document.createElement("div")),this.dom.className="cm-line",this.prevAttrs=this.attrs?null:void 0),void 0!==this.prevAttrs&&(Je(this.dom,this.prevAttrs,this.attrs),this.dom.classList.add("cm-line"),this.prevAttrs=void 0),super.sync(t,e);let n=this.dom.lastChild;for(;n&&Qe.get(n)instanceof Ie;)n=n.lastChild;if(!(n&&this.length&&("BR"==n.nodeName||0!=(null===(i=Qe.get(n))||void 0===i?void 0:i.isEditable)||_e.ios&&this.children.some((t=>t instanceof Ve))))){let t=document.createElement("BR");t.cmIgnore=!0,this.dom.appendChild(t)}}measureTextSize(){if(0==this.children.length||this.length>20)return null;let t,e=0;for(let i of this.children){if(!(i instanceof Ve)||/[^ -~]/.test(i.text))return null;let n=ae(i.dom);if(1!=n.length)return null;e+=n[0].width,t=n[0].height}return e?{lineHeight:this.dom.getBoundingClientRect().height,charWidth:e/this.length,textHeight:t}:null}coordsAt(t,e){let i=Ne(this,t,e);if(!this.children.length&&i&&this.parent){let{heightOracle:t}=this.parent.view.viewState,e=i.bottom-i.top;if(Math.abs(e-t.lineHeight)<2&&t.textHeight<e){let n=(e-t.textHeight)/2;return{top:i.top+n,bottom:i.bottom-n,left:i.left,right:i.left}}}return i}become(t){return!1}covers(){return!0}static find(t,e){for(let i=0,n=0;i<t.children.length;i++){let s=t.children[i],r=n+s.length;if(r>=e){if(s instanceof ti)return s;if(r>e)break}n=r+s.breakAfter}return null}}class ei extends Qe{constructor(t,e,i){super(),this.widget=t,this.length=e,this.deco=i,this.breakAfter=0,this.prevWidget=null}merge(t,e,i,n,s,r){return!(i&&(!(i instanceof ei&&this.widget.compare(i.widget))||t>0&&s<=0||e<this.length&&r<=0))&&(this.length=t+(i?i.length:0)+(this.length-e),!0)}domAtPos(t){return 0==t?xe.before(this.dom):xe.after(this.dom,t==this.length)}split(t){let e=this.length-t;this.length=t;let i=new ei(this.widget,e,this.deco);return i.breakAfter=this.breakAfter,i}get children(){return ke}sync(t){this.dom&&this.widget.updateDOM(this.dom,t)||(this.dom&&this.prevWidget&&this.prevWidget.destroy(this.dom),this.prevWidget=null,this.setDOM(this.widget.toDOM(t)),this.widget.editable||(this.dom.contentEditable="false"))}get overrideDOMText(){return this.parent?this.parent.view.state.doc.slice(this.posAtStart,this.posAtEnd):e.empty}domBoundsAround(){return null}become(t){return t instanceof ei&&t.widget.constructor==this.widget.constructor&&(t.widget.compare(this.widget)||this.markDirty(!0),this.dom&&!this.prevWidget&&(this.prevWidget=this.widget),this.widget=t.widget,this.length=t.length,this.deco=t.deco,this.breakAfter=t.breakAfter,!0)}ignoreMutation(){return!0}ignoreEvent(t){return this.widget.ignoreEvent(t)}get isEditable(){return!1}get isWidget(){return!0}coordsAt(t,e){return this.widget.coordsAt(this.dom,t,e)}destroy(){super.destroy(),this.dom&&this.widget.destroy(this.dom)}covers(t){let{startSide:e,endSide:i}=this.deco;return e!=i&&(t<0?e<0:i>0)}}class ii{eq(t){return!1}updateDOM(t,e){return!1}compare(t){return this==t||this.constructor==t.constructor&&this.eq(t)}get estimatedHeight(){return-1}get lineBreaks(){return 0}ignoreEvent(t){return!0}coordsAt(t,e,i){return null}get isHidden(){return!1}get editable(){return!1}destroy(t){}}var ni=function(t){return t[t.Text=0]="Text",t[t.WidgetBefore=1]="WidgetBefore",t[t.WidgetAfter=2]="WidgetAfter",t[t.WidgetRange=3]="WidgetRange",t}(ni||(ni={}));class si extends Pt{constructor(t,e,i,n){super(),this.startSide=t,this.endSide=e,this.widget=i,this.spec=n}get heightRelevant(){return!1}static mark(t){return new ri(t)}static widget(t){let e=Math.max(-1e4,Math.min(1e4,t.side||0)),i=!!t.block;return e+=i&&!t.inlineOrder?e>0?3e8:-4e8:e>0?1e8:-1e8,new ai(t,e,e,i,t.widget||null,!1)}static replace(t){let e,i,n=!!t.block;if(t.isBlockGap)e=-5e8,i=4e8;else{let{start:s,end:r}=li(t,n);e=(s?n?-3e8:-1:5e8)-1,i=1+(r?n?2e8:1:-6e8)}return new ai(t,e,i,n,t.widget||null,!0)}static line(t){return new oi(t)}static set(t,e=!1){return At.of(t,e)}hasHeight(){return!!this.widget&&this.widget.estimatedHeight>-1}}si.none=At.empty;class ri extends si{constructor(t){let{start:e,end:i}=li(t);super(e?-1:5e8,i?1:-6e8,null,t),this.tagName=t.tagName||"span",this.class=t.class||"",this.attrs=t.attributes||null}eq(t){var e,i;return this==t||t instanceof ri&&this.tagName==t.tagName&&(this.class||(null===(e=this.attrs)||void 0===e?void 0:e.class))==(t.class||(null===(i=t.attrs)||void 0===i?void 0:i.class))&&Fe(this.attrs,t.attrs,"class")}range(t,e=t){if(t>=e)throw new RangeError("Mark decorations may not be empty");return super.range(t,e)}}ri.prototype.point=!1;class oi extends si{constructor(t){super(-2e8,-2e8,null,t)}eq(t){return t instanceof oi&&this.spec.class==t.spec.class&&Fe(this.spec.attributes,t.spec.attributes)}range(t,e=t){if(e!=t)throw new RangeError("Line decoration ranges must be zero-length");return super.range(t,e)}}oi.prototype.mapMode=k.TrackBefore,oi.prototype.point=!0;class ai extends si{constructor(t,e,i,n,s,r){super(e,i,s,t),this.block=n,this.isReplace=r,this.mapMode=n?e<=0?k.TrackBefore:k.TrackAfter:k.TrackDel}get type(){return this.startSide!=this.endSide?ni.WidgetRange:this.startSide<=0?ni.WidgetBefore:ni.WidgetAfter}get heightRelevant(){return this.block||!!this.widget&&(this.widget.estimatedHeight>=5||this.widget.lineBreaks>0)}eq(t){return t instanceof ai&&(e=this.widget,i=t.widget,e==i||!!(e&&i&&e.compare(i)))&&this.block==t.block&&this.startSide==t.startSide&&this.endSide==t.endSide;var e,i}range(t,e=t){if(this.isReplace&&(t>e||t==e&&this.startSide>0&&this.endSide<=0))throw new RangeError("Invalid range for replacement decoration");if(!this.isReplace&&e!=t)throw new RangeError("Widget decorations can only have zero-length ranges");return super.range(t,e)}}function li(t,e=!1){let{inclusiveStart:i,inclusiveEnd:n}=t;return null==i&&(i=t.inclusive),null==n&&(n=t.inclusive),{start:null!=i?i:e,end:null!=n?n:e}}function hi(t,e,i,n=0){let s=i.length-1;s>=0&&i[s]+n>=t?i[s]=Math.max(i[s],e):i.push(t,e)}ai.prototype.point=!0;class ci{constructor(t,e,i,n){this.doc=t,this.pos=e,this.end=i,this.disallowBlockEffectsFor=n,this.content=[],this.curLine=null,this.breakAtStart=0,this.pendingBuffer=0,this.bufferMarks=[],this.atCursorPos=!0,this.openStart=-1,this.openEnd=-1,this.text="",this.textOff=0,this.cursor=t.iter(),this.skip=e}posCovered(){if(0==this.content.length)return!this.breakAtStart&&this.doc.lineAt(this.pos).from!=this.pos;let t=this.content[this.content.length-1];return!(t.breakAfter||t instanceof ei&&t.deco.endSide<0)}getLine(){return this.curLine||(this.content.push(this.curLine=new ti),this.atCursorPos=!0),this.curLine}flushBuffer(t=this.bufferMarks){this.pendingBuffer&&(this.curLine.append(fi(new Be(-1),t),t.length),this.pendingBuffer=0)}addBlockWidget(t){this.flushBuffer(),this.curLine=null,this.content.push(t)}finish(t){this.pendingBuffer&&t<=this.bufferMarks.length?this.flushBuffer():this.pendingBuffer=0,this.posCovered()||t&&this.content.length&&this.content[this.content.length-1]instanceof ei||this.getLine()}buildText(t,e,i){for(;t>0;){if(this.textOff==this.text.length){let{value:e,lineBreak:i,done:n}=this.cursor.next(this.skip);if(this.skip=0,n)throw new Error("Ran out of text content when drawing inline views");if(i){this.posCovered()||this.getLine(),this.content.length?this.content[this.content.length-1].breakAfter=1:this.breakAtStart=1,this.flushBuffer(),this.curLine=null,this.atCursorPos=!0,t--;continue}this.text=e,this.textOff=0}let n=Math.min(this.text.length-this.textOff,t,512);this.flushBuffer(e.slice(e.length-i)),this.getLine().append(fi(new Ve(this.text.slice(this.textOff,this.textOff+n)),e),i),this.atCursorPos=!0,this.textOff+=n,t-=n,i=0}}span(t,e,i,n){this.buildText(e-t,i,n),this.pos=e,this.openStart<0&&(this.openStart=n)}point(t,e,i,n,s,r){if(this.disallowBlockEffectsFor[r]&&i instanceof ai){if(i.block)throw new RangeError("Block decorations may not be specified via plugins");if(e>this.doc.lineAt(this.pos).to)throw new RangeError("Decorations that replace line breaks may not be specified via plugins")}let o=e-t;if(i instanceof ai)if(i.block)i.startSide>0&&!this.posCovered()&&this.getLine(),this.addBlockWidget(new ei(i.widget||new ui("div"),o,i));else{let r=ze.create(i.widget||new ui("span"),o,o?0:i.startSide),a=this.atCursorPos&&!r.isEditable&&s<=n.length&&(t<e||i.startSide>0),l=!r.isEditable&&(t<e||s>n.length||i.startSide<=0),h=this.getLine();2!=this.pendingBuffer||a||r.isEditable||(this.pendingBuffer=0),this.flushBuffer(n),a&&(h.append(fi(new Be(1),n),s),s=n.length+Math.max(0,s-n.length)),h.append(fi(r,n),s),this.atCursorPos=l,this.pendingBuffer=l?t<e||s>n.length?1:2:0,this.pendingBuffer&&(this.bufferMarks=n.slice())}else this.doc.lineAt(this.pos).from==this.pos&&this.getLine().addLineDeco(i);o&&(this.textOff+o<=this.text.length?this.textOff+=o:(this.skip+=o-(this.text.length-this.textOff),this.text="",this.textOff=0),this.pos=e),this.openStart<0&&(this.openStart=s)}static build(t,e,i,n,s){let r=new ci(t,e,i,s);return r.openEnd=At.spans(n,e,i,r),r.openStart<0&&(r.openStart=r.openEnd),r.finish(r.openEnd),r}}function fi(t,e){for(let i of e)t=new Ie(i,[t],t.length);return t}class ui extends ii{constructor(t){super(),this.tag=t}eq(t){return t.tag==this.tag}toDOM(){return document.createElement(this.tag)}updateDOM(t){return t.nodeName.toLowerCase()==this.tag}get isHidden(){return!0}}var di=function(t){return t[t.LTR=0]="LTR",t[t.RTL=1]="RTL",t}(di||(di={}));const pi=di.LTR,Oi=di.RTL;function gi(t){let e=[];for(let i=0;i<t.length;i++)e.push(1<<+t[i]);return e}const mi=gi("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008"),wi=gi("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333"),vi=Object.create(null),bi=[];for(let t of["()","[]","{}"]){let e=t.charCodeAt(0),i=t.charCodeAt(1);vi[e]=i,vi[i]=-e}function yi(t){return t<=247?mi[t]:1424<=t&&t<=1524?2:1536<=t&&t<=1785?wi[t-1536]:1774<=t&&t<=2220?4:8192<=t&&t<=8204?256:64336<=t&&t<=65023?4:1}const Si=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/;class xi{get dir(){return this.level%2?Oi:pi}constructor(t,e,i){this.from=t,this.to=e,this.level=i}side(t,e){return this.dir==e==t?this.to:this.from}forward(t,e){return t==(this.dir==e)}static find(t,e,i,n){let s=-1;for(let r=0;r<t.length;r++){let o=t[r];if(o.from<=e&&o.to>=e){if(o.level==i)return r;(s<0||(0!=n?n<0?o.from<e:o.to>e:t[s].level>o.level))&&(s=r)}}if(s<0)throw new RangeError("Index out of range");return s}}function ki(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++){let n=t[i],s=e[i];if(n.from!=s.from||n.to!=s.to||n.direction!=s.direction||!ki(n.inner,s.inner))return!1}return!0}const Qi=[];function $i(t,e,i,n,s,r,o){let a=n%2?2:1;if(n%2==s%2)for(let l=e,h=0;l<i;){let e=!0,c=!1;if(h==r.length||l<r[h].from){let t=Qi[l];t!=a&&(e=!1,c=16==t)}let f=e||1!=a?null:[],u=e?n:n+1,d=l;t:for(;;)if(h<r.length&&d==r[h].from){if(c)break t;let p=r[h];if(!e)for(let t=p.to,e=h+1;;){if(t==i)break t;if(!(e<r.length&&r[e].from==t)){if(Qi[t]==a)break t;break}t=r[e++].to}if(h++,f)f.push(p);else{p.from>l&&o.push(new xi(l,p.from,u)),Pi(t,p.direction==pi!=!(u%2)?n+1:n,s,p.inner,p.from,p.to,o),l=p.to}d=p.to}else{if(d==i||(e?Qi[d]!=a:Qi[d]==a))break;d++}f?$i(t,l,d,n+1,s,f,o):l<d&&o.push(new xi(l,d,u)),l=d}else for(let l=i,h=r.length;l>e;){let i=!0,c=!1;if(!h||l>r[h-1].to){let t=Qi[l-1];t!=a&&(i=!1,c=16==t)}let f=i||1!=a?null:[],u=i?n:n+1,d=l;t:for(;;)if(h&&d==r[h-1].to){if(c)break t;let p=r[--h];if(!i)for(let t=p.from,i=h;;){if(t==e)break t;if(!i||r[i-1].to!=t){if(Qi[t-1]==a)break t;break}t=r[--i].from}if(f)f.push(p);else{p.to<l&&o.push(new xi(p.to,l,u)),Pi(t,p.direction==pi!=!(u%2)?n+1:n,s,p.inner,p.from,p.to,o),l=p.from}d=p.from}else{if(d==e||(i?Qi[d-1]!=a:Qi[d-1]==a))break;d--}f?$i(t,d,l,n+1,s,f,o):d<l&&o.push(new xi(d,l,u)),l=d}}function Pi(t,e,i,n,s,r,o){let a=e%2?2:1;!function(t,e,i,n,s){for(let r=0;r<=n.length;r++){let o=r?n[r-1].to:e,a=r<n.length?n[r].from:i,l=r?256:s;for(let e=o,i=l,n=l;e<a;e++){let s=yi(t.charCodeAt(e));512==s?s=i:8==s&&4==n&&(s=16),Qi[e]=4==s?2:s,7&s&&(n=s),i=s}for(let t=o,e=l,n=l;t<a;t++){let s=Qi[t];if(128==s)t<a-1&&e==Qi[t+1]&&24&e?s=Qi[t]=e:Qi[t]=256;else if(64==s){let s=t+1;for(;s<a&&64==Qi[s];)s++;let r=t&&8==e||s<i&&8==Qi[s]?1==n?1:8:256;for(let e=t;e<s;e++)Qi[e]=r;t=s-1}else 8==s&&1==n&&(Qi[t]=1);e=s,7&s&&(n=s)}}}(t,s,r,n,a),function(t,e,i,n,s){let r=1==s?2:1;for(let o=0,a=0,l=0;o<=n.length;o++){let h=o?n[o-1].to:e,c=o<n.length?n[o].from:i;for(let e,i,n,o=h;o<c;o++)if(i=vi[e=t.charCodeAt(o)])if(i<0){for(let t=a-3;t>=0;t-=3)if(bi[t+1]==-i){let e=bi[t+2],i=2&e?s:4&e?1&e?r:s:0;i&&(Qi[o]=Qi[bi[t]]=i),a=t;break}}else{if(189==bi.length)break;bi[a++]=o,bi[a++]=e,bi[a++]=l}else if(2==(n=Qi[o])||1==n){let t=n==s;l=t?0:1;for(let e=a-3;e>=0;e-=3){let i=bi[e+2];if(2&i)break;if(t)bi[e+2]|=2;else{if(4&i)break;bi[e+2]|=4}}}}}(t,s,r,n,a),function(t,e,i,n){for(let s=0,r=n;s<=i.length;s++){let o=s?i[s-1].to:t,a=s<i.length?i[s].from:e;for(let l=o;l<a;){let o=Qi[l];if(256==o){let o=l+1;for(;;)if(o==a){if(s==i.length)break;o=i[s++].to,a=s<i.length?i[s].from:e}else{if(256!=Qi[o])break;o++}let h=1==r,c=h==(1==(o<e?Qi[o]:n))?h?1:2:n;for(let e=o,n=s,r=n?i[n-1].to:t;e>l;)e==r&&(e=i[--n].from,r=n?i[n-1].to:t),Qi[--e]=c;l=o}else r=o,l++}}}(s,r,n,a),$i(t,s,r,e,i,n,o)}function Zi(t,e,i){if(!t)return[new xi(0,0,e==Oi?1:0)];if(e==pi&&!i.length&&!Si.test(t))return Ci(t.length);if(i.length)for(;t.length>Qi.length;)Qi[Qi.length]=256;let n=[],s=e==pi?0:1;return Pi(t,s,s,i,0,t.length,n),n}function Ci(t){return[new xi(0,t,0)]}let Ti="";function Ai(t,e,i,n,s){var r;let o=n.head-t.from,a=xi.find(e,o,null!==(r=n.bidiLevel)&&void 0!==r?r:-1,n.assoc),l=e[a],h=l.side(s,i);if(o==h){let t=a+=s?1:-1;if(t<0||t>=e.length)return null;l=e[a=t],o=l.side(!s,i),h=l.side(s,i)}let c=O(t.text,o,l.forward(s,i));(c<l.from||c>l.to)&&(c=h),Ti=t.text.slice(Math.min(o,c),Math.max(o,c));let f=a==(s?e.length-1:0)?null:e[a+(s?1:-1)];return f&&c==h&&f.level+(s?0:1)<l.level?Y.cursor(f.side(!s,i)+t.from,f.forward(s,i)?1:-1,f.level):Y.cursor(c+t.from,l.forward(s,i)?-1:1,l.level)}function Ri(t,e,i){for(let n=e;n<i;n++){let e=yi(t.charCodeAt(n));if(1==e)return pi;if(2==e||4==e)return Oi}return pi}const Xi=j.define(),Yi=j.define(),Wi=j.define(),Mi=j.define(),ji=j.define(),Di=j.define(),Ei=j.define(),qi=j.define({combine:t=>t.some((t=>t))}),_i=j.define({combine:t=>t.some((t=>t))});class Vi{constructor(t,e="nearest",i="nearest",n=5,s=5,r=!1){this.range=t,this.y=e,this.x=i,this.yMargin=n,this.xMargin=s,this.isSnapshot=r}map(t){return t.empty?this:new Vi(this.range.map(t),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}clip(t){return this.range.to<=t.doc.length?this:new Vi(Y.cursor(t.doc.length),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}}const Ii=dt.define({map:(t,e)=>t.map(e)});function zi(t,e,i){let n=t.facet(Mi);n.length?n[0](e):window.onerror?window.onerror(String(e),i,void 0,void 0,e):i?console.error(i+":",e):console.error(e)}const Bi=j.define({combine:t=>!t.length||t[0]});let Gi=0;const Li=j.define();class Ni{constructor(t,e,i,n,s){this.id=t,this.create=e,this.domEventHandlers=i,this.domEventObservers=n,this.extension=s(this)}static define(t,e){const{eventHandlers:i,eventObservers:n,provide:s,decorations:r}=e||{};return new Ni(Gi++,t,i,n,(t=>{let e=[Li.of(t)];return r&&e.push(Ji.of((e=>{let i=e.plugin(t);return i?r(i):si.none}))),s&&e.push(s(t)),e}))}static fromClass(t,e){return Ni.define((e=>new t(e)),e)}}class Ui{constructor(t){this.spec=t,this.mustUpdate=null,this.value=null}update(t){if(this.value){if(this.mustUpdate){let t=this.mustUpdate;if(this.mustUpdate=null,this.value.update)try{this.value.update(t)}catch(e){if(zi(t.state,e,"CodeMirror plugin crashed"),this.value.destroy)try{this.value.destroy()}catch(t){}this.deactivate()}}}else if(this.spec)try{this.value=this.spec.create(t)}catch(e){zi(t.state,e,"CodeMirror plugin crashed"),this.deactivate()}return this}destroy(t){var e;if(null===(e=this.value)||void 0===e?void 0:e.destroy)try{this.value.destroy()}catch(e){zi(t.state,e,"CodeMirror plugin crashed")}}deactivate(){this.spec=this.value=null}}const Hi=j.define(),Fi=j.define(),Ji=j.define(),Ki=j.define(),tn=j.define(),en=j.define();function nn(t,e){let i=t.state.facet(en);if(!i.length)return i;let n=i.map((e=>e instanceof Function?e(t):e)),s=[];return At.spans(n,e.from,e.to,{point(){},span(t,i,n,r){let o=t-e.from,a=i-e.from,l=s;for(let t=n.length-1;t>=0;t--,r--){let i,s=n[t].spec.bidiIsolate;if(null==s&&(s=Ri(e.text,o,a)),r>0&&l.length&&(i=l[l.length-1]).to==o&&i.direction==s)i.to=a,l=i.inner;else{let t={from:o,to:a,direction:s,inner:[]};l.push(t),l=t.inner}}}}),s}const sn=j.define();function rn(t){let e=0,i=0,n=0,s=0;for(let r of t.state.facet(sn)){let o=r(t);o&&(null!=o.left&&(e=Math.max(e,o.left)),null!=o.right&&(i=Math.max(i,o.right)),null!=o.top&&(n=Math.max(n,o.top)),null!=o.bottom&&(s=Math.max(s,o.bottom)))}return{left:e,right:i,top:n,bottom:s}}const on=j.define();class an{constructor(t,e,i,n){this.fromA=t,this.toA=e,this.fromB=i,this.toB=n}join(t){return new an(Math.min(this.fromA,t.fromA),Math.max(this.toA,t.toA),Math.min(this.fromB,t.fromB),Math.max(this.toB,t.toB))}addToSet(t){let e=t.length,i=this;for(;e>0;e--){let n=t[e-1];if(!(n.fromA>i.toA)){if(n.toA<i.fromA)break;i=i.join(n),t.splice(e-1,1)}}return t.splice(e,0,i),t}static extendWithRanges(t,e){if(0==e.length)return t;let i=[];for(let n=0,s=0,r=0,o=0;;n++){let a=n==t.length?null:t[n],l=r-o,h=a?a.fromB:1e9;for(;s<e.length&&e[s]<h;){let t=e[s],n=e[s+1],r=Math.max(o,t),a=Math.min(h,n);if(r<=a&&new an(r+l,a+l,r,a).addToSet(i),n>h)break;s+=2}if(!a)return i;new an(a.fromA,a.toA,a.fromB,a.toB).addToSet(i),r=a.toA,o=a.toB}}}class ln{constructor(t,e,i){this.view=t,this.state=e,this.transactions=i,this.flags=0,this.startState=t.state,this.changes=$.empty(this.startState.doc.length);for(let t of i)this.changes=this.changes.compose(t.changes);let n=[];this.changes.iterChangedRanges(((t,e,i,s)=>n.push(new an(t,e,i,s)))),this.changedRanges=n}static create(t,e,i){return new ln(t,e,i)}get viewportChanged(){return(4&this.flags)>0}get heightChanged(){return(2&this.flags)>0}get geometryChanged(){return this.docChanged||(10&this.flags)>0}get focusChanged(){return(1&this.flags)>0}get docChanged(){return!this.changes.empty}get selectionSet(){return this.transactions.some((t=>t.selection))}get empty(){return 0==this.flags&&0==this.transactions.length}}class hn extends Qe{get length(){return this.view.state.doc.length}constructor(t){super(),this.view=t,this.decorations=[],this.dynamicDecorationMap=[],this.domChanged=null,this.hasComposition=null,this.markedForComposition=new Set,this.minWidth=0,this.minWidthFrom=0,this.minWidthTo=0,this.impreciseAnchor=null,this.impreciseHead=null,this.forceSelection=!1,this.lastUpdate=Date.now(),this.setDOM(t.contentDOM),this.children=[new ti],this.children[0].setParent(this),this.updateDeco(),this.updateInner([new an(0,0,0,t.state.doc.length)],0,null)}update(t){var e;let i=t.changedRanges;this.minWidth>0&&i.length&&(i.every((({fromA:t,toA:e})=>e<this.minWidthFrom||t>this.minWidthTo))?(this.minWidthFrom=t.changes.mapPos(this.minWidthFrom,1),this.minWidthTo=t.changes.mapPos(this.minWidthTo,1)):this.minWidth=this.minWidthFrom=this.minWidthTo=0);let n=-1;this.view.inputState.composing>=0&&((null===(e=this.domChanged)||void 0===e?void 0:e.newSel)?n=this.domChanged.newSel.head:function(t,e){let i=!1;e&&t.iterChangedRanges(((t,n)=>{t<e.to&&n>e.from&&(i=!0)}));return i}(t.changes,this.hasComposition)||t.selectionSet||(n=t.state.selection.main.head));let s=n>-1?function(t,e,i){let n=fn(t,i);if(!n)return null;let{node:s,from:r,to:o}=n,a=s.nodeValue;if(/[\n\r]/.test(a))return null;if(t.state.doc.sliceString(n.from,n.to)!=a)return null;let l=e.invertedDesc,h=new an(l.mapPos(r),l.mapPos(o),r,o),c=[];for(let e=s.parentNode;;e=e.parentNode){let i=Qe.get(e);if(i instanceof Ie)c.push({node:e,deco:i.mark});else{if(i instanceof ti||"DIV"==e.nodeName&&e.parentNode==t.contentDOM)return{range:h,text:s,marks:c,line:e};if(e==t.contentDOM)return null;c.push({node:e,deco:new ri({inclusive:!0,attributes:Ke(e),tagName:e.tagName.toLowerCase()})})}}}(this.view,t.changes,n):null;if(this.domChanged=null,this.hasComposition){this.markedForComposition.clear();let{from:e,to:n}=this.hasComposition;i=new an(e,n,t.changes.mapPos(e,-1),t.changes.mapPos(n,1)).addToSet(i.slice())}this.hasComposition=s?{from:s.range.fromB,to:s.range.toB}:null,(_e.ie||_e.chrome)&&!s&&t&&t.state.doc.lines!=t.startState.doc.lines&&(this.forceSelection=!0);let r=function(t,e,i){let n=new dn;return At.compare(t,e,i,n),n.changes}(this.decorations,this.updateDeco(),t.changes);return i=an.extendWithRanges(i,r),!!(7&this.flags||0!=i.length)&&(this.updateInner(i,t.startState.doc.length,s),t.transactions.length&&(this.lastUpdate=Date.now()),!0)}updateInner(t,e,i){this.view.viewState.mustMeasureContent=!0,this.updateChildren(t,e,i);let{observer:n}=this.view;n.ignore((()=>{this.dom.style.height=this.view.viewState.contentHeight/this.view.scaleY+"px",this.dom.style.flexBasis=this.minWidth?this.minWidth+"px":"";let t=_e.chrome||_e.ios?{node:n.selectionRange.focusNode,written:!1}:void 0;this.sync(this.view,t),this.flags&=-8,t&&(t.written||n.selectionRange.focusNode!=t.node)&&(this.forceSelection=!0),this.dom.style.height=""})),this.markedForComposition.forEach((t=>t.flags&=-9));let s=[];if(this.view.viewport.from||this.view.viewport.to<this.view.state.doc.length)for(let t of this.children)t instanceof ei&&t.widget instanceof cn&&s.push(t.dom);n.updateGaps(s)}updateChildren(t,e,i){let n=i?i.range.addToSet(t.slice()):t,s=this.childCursor(e);for(let t=n.length-1;;t--){let e=t>=0?n[t]:null;if(!e)break;let r,o,a,l,{fromA:h,toA:c,fromB:f,toB:u}=e;if(i&&i.range.fromB<u&&i.range.toB>f){let t=ci.build(this.view.state.doc,f,i.range.fromB,this.decorations,this.dynamicDecorationMap),e=ci.build(this.view.state.doc,i.range.toB,u,this.decorations,this.dynamicDecorationMap);o=t.breakAtStart,a=t.openStart,l=e.openEnd;let n=this.compositionView(i);e.breakAtStart?n.breakAfter=1:e.content.length&&n.merge(n.length,n.length,e.content[0],!1,e.openStart,0)&&(n.breakAfter=e.content[0].breakAfter,e.content.shift()),t.content.length&&n.merge(0,0,t.content[t.content.length-1],!0,0,t.openEnd)&&t.content.pop(),r=t.content.concat(n).concat(e.content)}else({content:r,breakAtStart:o,openStart:a,openEnd:l}=ci.build(this.view.state.doc,f,u,this.decorations,this.dynamicDecorationMap));let{i:d,off:p}=s.findPos(c,1),{i:O,off:g}=s.findPos(h,-1);Ze(this,O,g,d,p,r,o,a,l)}i&&this.fixCompositionDOM(i)}compositionView(t){let e=new Ve(t.text.nodeValue);e.flags|=8;for(let{deco:i}of t.marks)e=new Ie(i,[e],e.length);let i=new ti;return i.append(e,0),i}fixCompositionDOM(t){let e=(t,e)=>{e.flags|=8|(e.children.some((t=>7&t.flags))?1:0),this.markedForComposition.add(e);let i=Qe.get(t);i&&i!=e&&(i.dom=null),e.setDOM(t)},i=this.childPos(t.range.fromB,1),n=this.children[i.i];e(t.line,n);for(let s=t.marks.length-1;s>=-1;s--)i=n.childPos(i.off,1),n=n.children[i.i],e(s>=0?t.marks[s].node:t.text,n)}updateSelection(t=!1,e=!1){!t&&this.view.observer.selectionRange.focusNode||this.view.observer.readSelectionRange();let i=this.view.root.activeElement,n=i==this.dom,s=!n&&oe(this.dom,this.view.observer.selectionRange)&&!(i&&this.dom.contains(i));if(!(n||e||s))return;let r=this.forceSelection;this.forceSelection=!1;let o=this.view.state.selection.main,a=this.moveToLine(this.domAtPos(o.anchor)),l=o.empty?a:this.moveToLine(this.domAtPos(o.head));if(_e.gecko&&o.empty&&!this.hasComposition&&(1==(h=a).node.nodeType&&h.node.firstChild&&(0==h.offset||"false"==h.node.childNodes[h.offset-1].contentEditable)&&(h.offset==h.node.childNodes.length||"false"==h.node.childNodes[h.offset].contentEditable))){let t=document.createTextNode("");this.view.observer.ignore((()=>a.node.insertBefore(t,a.node.childNodes[a.offset]||null))),a=l=new xe(t,0),r=!0}var h;let c=this.view.observer.selectionRange;!r&&c.focusNode&&(le(a.node,a.offset,c.anchorNode,c.anchorOffset)&&le(l.node,l.offset,c.focusNode,c.focusOffset)||this.suppressWidgetCursorChange(c,o))||(this.view.observer.ignore((()=>{_e.android&&_e.chrome&&this.dom.contains(c.focusNode)&&function(t,e){for(let i=t;i&&i!=e;i=i.assignedSlot||i.parentNode)if(1==i.nodeType&&"false"==i.contentEditable)return!0;return!1}(c.focusNode,this.dom)&&(this.dom.blur(),this.dom.focus({preventScroll:!0}));let t=se(this.view.root);if(t)if(o.empty){if(_e.gecko){let t=(e=a.node,n=a.offset,1!=e.nodeType?0:(n&&"false"==e.childNodes[n-1].contentEditable?1:0)|(n<e.childNodes.length&&"false"==e.childNodes[n].contentEditable?2:0));if(t&&3!=t){let e=un(a.node,a.offset,1==t?1:-1);e&&(a=new xe(e.node,e.offset))}}t.collapse(a.node,a.offset),null!=o.bidiLevel&&void 0!==t.caretBidiLevel&&(t.caretBidiLevel=o.bidiLevel)}else if(t.extend){t.collapse(a.node,a.offset);try{t.extend(l.node,l.offset)}catch(t){}}else{let e=document.createRange();o.anchor>o.head&&([a,l]=[l,a]),e.setEnd(l.node,l.offset),e.setStart(a.node,a.offset),t.removeAllRanges(),t.addRange(e)}else;var e,n;s&&this.view.root.activeElement==this.dom&&(this.dom.blur(),i&&i.focus())})),this.view.observer.setSelectionRange(a,l)),this.impreciseAnchor=a.precise?null:new xe(c.anchorNode,c.anchorOffset),this.impreciseHead=l.precise?null:new xe(c.focusNode,c.focusOffset)}suppressWidgetCursorChange(t,e){return this.hasComposition&&e.empty&&le(t.focusNode,t.focusOffset,t.anchorNode,t.anchorOffset)&&this.posFromDOM(t.focusNode,t.focusOffset)==e.head}enforceCursorAssoc(){if(this.hasComposition)return;let{view:t}=this,e=t.state.selection.main,i=se(t.root),{anchorNode:n,anchorOffset:s}=t.observer.selectionRange;if(!(i&&e.empty&&e.assoc&&i.modify))return;let r=ti.find(this,e.head);if(!r)return;let o=r.posAtStart;if(e.head==o||e.head==o+r.length)return;let a=this.coordsAt(e.head,-1),l=this.coordsAt(e.head,1);if(!a||!l||a.bottom>l.top)return;let h=this.domAtPos(e.head+e.assoc);i.collapse(h.node,h.offset),i.modify("move",e.assoc<0?"forward":"backward","lineboundary"),t.observer.readSelectionRange();let c=t.observer.selectionRange;t.docView.posFromDOM(c.anchorNode,c.anchorOffset)!=e.from&&i.collapse(n,s)}moveToLine(t){let e,i=this.dom;if(t.node!=i)return t;for(let n=t.offset;!e&&n<i.childNodes.length;n++){let t=Qe.get(i.childNodes[n]);t instanceof ti&&(e=t.domAtPos(0))}for(let n=t.offset-1;!e&&n>=0;n--){let t=Qe.get(i.childNodes[n]);t instanceof ti&&(e=t.domAtPos(t.length))}return e?new xe(e.node,e.offset,!0):t}nearest(t){for(let e=t;e;){let t=Qe.get(e);if(t&&t.rootView==this)return t;e=e.parentNode}return null}posFromDOM(t,e){let i=this.nearest(t);if(!i)throw new RangeError("Trying to find position for a DOM position outside of the document");return i.localPosFromDOM(t,e)+i.posAtStart}domAtPos(t){let{i:e,off:i}=this.childCursor().findPos(t,-1);for(;e<this.children.length-1;){let t=this.children[e];if(i<t.length||t instanceof ti)break;e++,i=0}return this.children[e].domAtPos(i)}coordsAt(t,e){let i=null,n=0;for(let s=this.length,r=this.children.length-1;r>=0;r--){let o=this.children[r],a=s-o.breakAfter,l=a-o.length;if(a<t)break;l<=t&&(l<t||o.covers(-1))&&(a>t||o.covers(1))&&(!i||o instanceof ti&&!(i instanceof ti&&e>=0))&&(i=o,n=l),s=l}return i?i.coordsAt(t-n,e):null}coordsForChar(t){let{i:e,off:i}=this.childPos(t,1),n=this.children[e];if(!(n instanceof ti))return null;for(;n.children.length;){let{i:t,off:e}=n.childPos(i,1);for(;;t++){if(t==n.children.length)return null;if((n=n.children[t]).length)break}i=e}if(!(n instanceof Ve))return null;let s=O(n.text,i);if(s==i)return null;let r=ve(n.dom,i,s).getClientRects();for(let t=0;t<r.length;t++){let e=r[t];if(t==r.length-1||e.top<e.bottom&&e.left<e.right)return e}return null}measureVisibleLineHeights(t){let e=[],{from:i,to:n}=t,s=this.view.contentDOM.clientWidth,r=s>Math.max(this.view.scrollDOM.clientWidth,this.minWidth)+1,o=-1,a=this.view.textDirection==di.LTR;for(let t=0,l=0;l<this.children.length;l++){let h=this.children[l],c=t+h.length;if(c>n)break;if(t>=i){let i=h.dom.getBoundingClientRect();if(e.push(i.height),r){let e=h.dom.lastChild,n=e?ae(e):[];if(n.length){let e=n[n.length-1],r=a?e.right-i.left:i.right-e.left;r>o&&(o=r,this.minWidth=s,this.minWidthFrom=t,this.minWidthTo=c)}}}t=c+h.breakAfter}return e}textDirectionAt(t){let{i:e}=this.childPos(t,1);return"rtl"==getComputedStyle(this.children[e].dom).direction?di.RTL:di.LTR}measureTextSize(){for(let t of this.children)if(t instanceof ti){let e=t.measureTextSize();if(e)return e}let t,e,i,n=document.createElement("div");return n.className="cm-line",n.style.width="99999px",n.style.position="absolute",n.textContent="abc def ghi jkl mno pqr stu",this.view.observer.ignore((()=>{this.dom.appendChild(n);let s=ae(n.firstChild)[0];t=n.getBoundingClientRect().height,e=s?s.width/27:7,i=s?s.height:t,n.remove()})),{lineHeight:t,charWidth:e,textHeight:i}}childCursor(t=this.length){let e=this.children.length;return e&&(t-=this.children[--e].length),new Pe(this.children,t,e)}computeBlockGapDeco(){let t=[],e=this.view.viewState;for(let i=0,n=0;;n++){let s=n==e.viewports.length?null:e.viewports[n],r=s?s.from-1:this.length;if(r>i){let n=(e.lineBlockAt(r).bottom-e.lineBlockAt(i).top)/this.view.scaleY;t.push(si.replace({widget:new cn(n),block:!0,inclusive:!0,isBlockGap:!0}).range(i,r))}if(!s)break;i=s.to+1}return si.set(t)}updateDeco(){let t=this.view.state.facet(Ji).map(((t,e)=>(this.dynamicDecorationMap[e]="function"==typeof t)?t(this.view):t)),e=!1,i=this.view.state.facet(Ki).map(((t,i)=>{let n="function"==typeof t;return n&&(e=!0),n?t(this.view):t}));i.length&&(this.dynamicDecorationMap[t.length]=e,t.push(At.join(i)));for(let e=t.length;e<t.length+3;e++)this.dynamicDecorationMap[e]=!1;return this.decorations=[...t,this.computeBlockGapDeco(),this.view.viewState.lineGapDeco]}scrollIntoView(t){if(t.isSnapshot){let e=this.view.viewState.lineBlockAt(t.range.head);return this.view.scrollDOM.scrollTop=e.top-t.yMargin,void(this.view.scrollDOM.scrollLeft=t.xMargin)}let e,{range:i}=t,n=this.coordsAt(i.head,i.empty?i.assoc:i.head>i.anchor?-1:1);if(!n)return;!i.empty&&(e=this.coordsAt(i.anchor,i.anchor>i.head?-1:1))&&(n={left:Math.min(n.left,e.left),top:Math.min(n.top,e.top),right:Math.max(n.right,e.right),bottom:Math.max(n.bottom,e.bottom)});let s=rn(this.view),r={left:n.left-s.left,top:n.top-s.top,right:n.right+s.right,bottom:n.bottom+s.bottom},{offsetWidth:o,offsetHeight:a}=this.view.scrollDOM;!function(t,e,i,n,s,r,o,a){let l=t.ownerDocument,h=l.defaultView||window;for(let c=t,f=!1;c&&!f;)if(1==c.nodeType){let t,u=c==l.body,d=1,p=1;if(u)t=de(h);else{if(/^(fixed|sticky)$/.test(getComputedStyle(c).position)&&(f=!0),c.scrollHeight<=c.clientHeight&&c.scrollWidth<=c.clientWidth){c=c.assignedSlot||c.parentNode;continue}let e=c.getBoundingClientRect();({scaleX:d,scaleY:p}=pe(c,e)),t={left:e.left,right:e.left+c.clientWidth*d,top:e.top,bottom:e.top+c.clientHeight*p}}let O=0,g=0;if("nearest"==s)e.top<t.top?(g=-(t.top-e.top+o),i>0&&e.bottom>t.bottom+g&&(g=e.bottom-t.bottom+g+o)):e.bottom>t.bottom&&(g=e.bottom-t.bottom+o,i<0&&e.top-g<t.top&&(g=-(t.top+g-e.top+o)));else{let n=e.bottom-e.top,r=t.bottom-t.top;g=("center"==s&&n<=r?e.top+n/2-r/2:"start"==s||"center"==s&&i<0?e.top-o:e.bottom-r+o)-t.top}if("nearest"==n?e.left<t.left?(O=-(t.left-e.left+r),i>0&&e.right>t.right+O&&(O=e.right-t.right+O+r)):e.right>t.right&&(O=e.right-t.right+r,i<0&&e.left<t.left+O&&(O=-(t.left+O-e.left+r))):O=("center"==n?e.left+(e.right-e.left)/2-(t.right-t.left)/2:"start"==n==a?e.left-r:e.right-(t.right-t.left)+r)-t.left,O||g)if(u)h.scrollBy(O,g);else{let t=0,i=0;if(g){let t=c.scrollTop;c.scrollTop+=g/p,i=(c.scrollTop-t)*p}if(O){let e=c.scrollLeft;c.scrollLeft+=O/d,t=(c.scrollLeft-e)*d}e={left:e.left-t,top:e.top-i,right:e.right-t,bottom:e.bottom-i},t&&Math.abs(t-O)<1&&(n="nearest"),i&&Math.abs(i-g)<1&&(s="nearest")}if(u)break;c=c.assignedSlot||c.parentNode}else{if(11!=c.nodeType)break;c=c.host}}(this.view.scrollDOM,r,i.head<i.anchor?-1:1,t.x,t.y,Math.max(Math.min(t.xMargin,o),-o),Math.max(Math.min(t.yMargin,a),-a),this.view.textDirection==di.LTR)}}class cn extends ii{constructor(t){super(),this.height=t}toDOM(){let t=document.createElement("div");return t.className="cm-gap",this.updateDOM(t),t}eq(t){return t.height==this.height}updateDOM(t){return t.style.height=this.height+"px",!0}get editable(){return!0}get estimatedHeight(){return this.height}}function fn(t,e){let i=t.observer.selectionRange,n=i.focusNode&&un(i.focusNode,i.focusOffset,0);if(!n)return null;let s=e-n.offset;return{from:s,to:s+n.node.nodeValue.length,node:n.node}}function un(t,e,i){if(i<=0)for(let i=t,n=e;;){if(3==i.nodeType)return{node:i,offset:n};if(!(1==i.nodeType&&n>0))break;i=i.childNodes[n-1],n=fe(i)}if(i>=0)for(let n=t,s=e;;){if(3==n.nodeType)return{node:n,offset:s};if(!(1==n.nodeType&&s<n.childNodes.length&&i>=0))break;n=n.childNodes[s],s=0}return null}let dn=class{constructor(){this.changes=[]}compareRange(t,e){hi(t,e,this.changes)}comparePoint(t,e){hi(t,e,this.changes)}};function pn(t,e){return e.left>t?e.left-t:Math.max(0,t-e.right)}function On(t,e){return e.top>t?e.top-t:Math.max(0,t-e.bottom)}function gn(t,e){return t.top<e.bottom-1&&t.bottom>e.top+1}function mn(t,e){return e<t.top?{top:e,left:t.left,right:t.right,bottom:t.bottom}:t}function wn(t,e){return e>t.bottom?{top:t.top,left:t.left,right:t.right,bottom:e}:t}function vn(t,e,i){let n,s,r,o,a,l,h,c,f=!1;for(let u=t.firstChild;u;u=u.nextSibling){let t=ae(u);for(let d=0;d<t.length;d++){let p=t[d];s&&gn(s,p)&&(p=mn(wn(p,s.bottom),s.top));let O=pn(e,p),g=On(i,p);if(0==O&&0==g)return 3==u.nodeType?bn(u,e,i):vn(u,e,i);if(!n||o>g||o==g&&r>O){n=u,s=p,r=O,o=g;let a=g?i<p.top?-1:1:O?e<p.left?-1:1:0;f=!a||(a>0?d<t.length-1:d>0)}0==O?i>p.bottom&&(!h||h.bottom<p.bottom)?(a=u,h=p):i<p.top&&(!c||c.top>p.top)&&(l=u,c=p):h&&gn(h,p)?h=wn(h,p.bottom):c&&gn(c,p)&&(c=mn(c,p.top))}}if(h&&h.bottom>=i?(n=a,s=h):c&&c.top<=i&&(n=l,s=c),!n)return{node:t,offset:0};let u=Math.max(s.left,Math.min(s.right,e));return 3==n.nodeType?bn(n,u,i):f&&"false"!=n.contentEditable?vn(n,u,i):{node:t,offset:Array.prototype.indexOf.call(t.childNodes,n)+(e>=(s.left+s.right)/2?1:0)}}function bn(t,e,i){let n=t.nodeValue.length,s=-1,r=1e9,o=0;for(let a=0;a<n;a++){let n=ve(t,a,a+1).getClientRects();for(let l=0;l<n.length;l++){let h=n[l];if(h.top==h.bottom)continue;o||(o=e-h.left);let c=(h.top>i?h.top-i:i-h.bottom)-1;if(h.left-1<=e&&h.right+1>=e&&c<r){let i=e>=(h.left+h.right)/2,n=i;if(_e.chrome||_e.gecko){ve(t,a).getBoundingClientRect().left==h.right&&(n=!i)}if(c<=0)return{node:t,offset:a+(n?1:0)};s=a+(n?1:0),r=c}}}return{node:t,offset:s>-1?s:o>0?t.nodeValue.length:0}}function yn(t,e,i,n=-1){var s,r;let o,a=t.contentDOM.getBoundingClientRect(),l=a.top+t.viewState.paddingTop,{docHeight:h}=t.viewState,{x:c,y:f}=e,u=f-l;if(u<0)return 0;if(u>h)return t.state.doc.length;for(let e=t.viewState.heightOracle.textHeight/2,s=!1;o=t.elementAtHeight(u),o.type!=ni.Text;)for(;u=n>0?o.bottom+e:o.top-e,!(u>=0&&u<=h);){if(s)return i?null:0;s=!0,n=-n}f=l+u;let d=o.from;if(d<t.viewport.from)return 0==t.viewport.from?0:i?null:Sn(t,a,o,c,f);if(d>t.viewport.to)return t.viewport.to==t.state.doc.length?t.state.doc.length:i?null:Sn(t,a,o,c,f);let p=t.dom.ownerDocument,O=t.root.elementFromPoint?t.root:p,g=O.elementFromPoint(c,f);g&&!t.contentDOM.contains(g)&&(g=null),g||(c=Math.max(a.left+1,Math.min(a.right-1,c)),g=O.elementFromPoint(c,f),g&&!t.contentDOM.contains(g)&&(g=null));let m,w=-1;if(g&&0!=(null===(s=t.docView.nearest(g))||void 0===s?void 0:s.isEditable))if(p.caretPositionFromPoint){let t=p.caretPositionFromPoint(c,f);t&&({offsetNode:m,offset:w}=t)}else if(p.caretRangeFromPoint){let e=p.caretRangeFromPoint(c,f);e&&(({startContainer:m,startOffset:w}=e),(!t.contentDOM.contains(m)||_e.safari&&function(t,e,i){let n;if(3!=t.nodeType||e!=(n=t.nodeValue.length))return!1;for(let e=t.nextSibling;e;e=e.nextSibling)if(1!=e.nodeType||"BR"!=e.nodeName)return!1;return ve(t,n-1,n).getBoundingClientRect().left>i}(m,w,c)||_e.chrome&&function(t,e,i){if(0!=e)return!1;for(let e=t;;){let t=e.parentNode;if(!t||1!=t.nodeType||t.firstChild!=e)return!1;if(t.classList.contains("cm-line"))break;e=t}let n=1==t.nodeType?t.getBoundingClientRect():ve(t,0,Math.max(t.nodeValue.length,1)).getBoundingClientRect();return i-n.left>5}(m,w,c))&&(m=void 0))}if(!m||!t.docView.dom.contains(m)){let e=ti.find(t.docView,d);if(!e)return u>o.top+o.height/2?o.to:o.from;({node:m,offset:w}=vn(e.dom,c,f))}let v=t.docView.nearest(m);if(!v)return null;if(v.isWidget&&1==(null===(r=v.dom)||void 0===r?void 0:r.nodeType)){let t=v.dom.getBoundingClientRect();return e.y<t.top||e.y<=t.bottom&&e.x<=(t.left+t.right)/2?v.posAtStart:v.posAtEnd}return v.localPosFromDOM(m,w)+v.posAtStart}function Sn(t,e,i,n,s){let r=Math.round((n-e.left)*t.defaultCharacterWidth);if(t.lineWrapping&&i.height>1.5*t.defaultLineHeight){let e=t.viewState.heightOracle.textHeight;r+=Math.floor((s-i.top-.5*(t.defaultLineHeight-e))/e)*t.viewState.heightOracle.lineLength}let o=t.state.sliceDoc(i.from,i.to);return i.from+zt(o,r,t.state.tabSize)}function xn(t,e){let i=t.lineBlockAt(e);if(Array.isArray(i.type))for(let t of i.type)if(t.to>e||t.to==e&&(t.to==i.to||t.type==ni.Text))return t;return i}function kn(t,e,i,n){let s=t.state.doc.lineAt(e.head),r=t.bidiSpans(s),o=t.textDirectionAt(s.from);for(let a=e,l=null;;){let e=Ai(s,r,o,a,i),h=Ti;if(!e){if(s.number==(i?t.state.doc.lines:1))return a;h="\n",s=t.state.doc.line(s.number+(i?1:-1)),r=t.bidiSpans(s),e=t.visualLineSide(s,!i)}if(l){if(!l(h))return a}else{if(!n)return e;l=n(h)}a=e}}function Qn(t,e,i){for(;;){let n=0;for(let s of t)s.between(e-1,e+1,((t,s,r)=>{if(e>t&&e<s){let r=n||i||(e-t<s-e?-1:1);e=r<0?t:s,n=r}}));if(!n)return e}}function $n(t,e,i){let n=Qn(t.state.facet(tn).map((e=>e(t))),i.from,e.head>i.from?-1:1);return n==i.from?i:Y.cursor(n,n<i.from?1:-1)}class Pn{setSelectionOrigin(t){this.lastSelectionOrigin=t,this.lastSelectionTime=Date.now()}constructor(t){this.view=t,this.lastKeyCode=0,this.lastKeyTime=0,this.lastTouchTime=0,this.lastFocusTime=0,this.lastScrollTop=0,this.lastScrollLeft=0,this.pendingIOSKey=void 0,this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastEscPress=0,this.lastContextMenu=0,this.scrollHandlers=[],this.handlers=Object.create(null),this.composing=-1,this.compositionFirstChange=null,this.compositionEndedAt=0,this.compositionPendingKey=!1,this.compositionPendingChange=!1,this.mouseSelection=null,this.draggedContent=null,this.handleEvent=this.handleEvent.bind(this),this.notifiedFocused=t.hasFocus,_e.safari&&t.contentDOM.addEventListener("input",(()=>null)),_e.gecko&&function(t){ts.has(t)||(ts.add(t),t.addEventListener("copy",(()=>{})),t.addEventListener("cut",(()=>{})))}(t.contentDOM.ownerDocument)}handleEvent(t){(function(t,e){if(!e.bubbles)return!0;if(e.defaultPrevented)return!1;for(let i,n=e.target;n!=t.contentDOM;n=n.parentNode)if(!n||11==n.nodeType||(i=Qe.get(n))&&i.ignoreEvent(e))return!1;return!0})(this.view,t)&&!this.ignoreDuringComposition(t)&&("keydown"==t.type&&this.keydown(t)||this.runHandlers(t.type,t))}runHandlers(t,e){let i=this.handlers[t];if(i){for(let t of i.observers)t(this.view,e);for(let t of i.handlers){if(e.defaultPrevented)break;if(t(this.view,e)){e.preventDefault();break}}}}ensureHandlers(t){let e=Cn(t),i=this.handlers,n=this.view.contentDOM;for(let t in e)if("scroll"!=t){let s=!e[t].handlers.length,r=i[t];r&&s!=!r.handlers.length&&(n.removeEventListener(t,this.handleEvent),r=null),r||n.addEventListener(t,this.handleEvent,{passive:s})}for(let t in i)"scroll"==t||e[t]||n.removeEventListener(t,this.handleEvent);this.handlers=e}keydown(t){if(this.lastKeyCode=t.keyCode,this.lastKeyTime=Date.now(),9==t.keyCode&&Date.now()<this.lastEscPress+2e3)return!0;if(27!=t.keyCode&&Rn.indexOf(t.keyCode)<0&&(this.view.inputState.lastEscPress=0),_e.android&&_e.chrome&&!t.synthetic&&(13==t.keyCode||8==t.keyCode))return this.view.observer.delayAndroidKey(t.key,t.keyCode),!0;let e;return!_e.ios||t.synthetic||t.altKey||t.metaKey||!((e=Tn.find((e=>e.keyCode==t.keyCode)))&&!t.ctrlKey||An.indexOf(t.key)>-1&&t.ctrlKey&&!t.shiftKey)?(229!=t.keyCode&&this.view.observer.forceFlush(),!1):(this.pendingIOSKey=e||t,setTimeout((()=>this.flushIOSKey()),250),!0)}flushIOSKey(){let t=this.pendingIOSKey;return!!t&&(this.pendingIOSKey=void 0,be(this.view.contentDOM,t.key,t.keyCode))}ignoreDuringComposition(t){return!!/^key/.test(t.type)&&(this.composing>0||!!(_e.safari&&!_e.ios&&this.compositionPendingKey&&Date.now()-this.compositionEndedAt<100)&&(this.compositionPendingKey=!1,!0))}startMouseSelection(t){this.mouseSelection&&this.mouseSelection.destroy(),this.mouseSelection=t}update(t){this.mouseSelection&&this.mouseSelection.update(t),this.draggedContent&&t.docChanged&&(this.draggedContent=this.draggedContent.map(t.changes)),t.transactions.length&&(this.lastKeyCode=this.lastSelectionTime=0)}destroy(){this.mouseSelection&&this.mouseSelection.destroy()}}function Zn(t,e){return(i,n)=>{try{return e.call(t,n,i)}catch(t){zi(i.state,t)}}}function Cn(t){let e=Object.create(null);function i(t){return e[t]||(e[t]={observers:[],handlers:[]})}for(let e of t){let t=e.spec;if(t&&t.domEventHandlers)for(let n in t.domEventHandlers){let s=t.domEventHandlers[n];s&&i(n).handlers.push(Zn(e.value,s))}if(t&&t.domEventObservers)for(let n in t.domEventObservers){let s=t.domEventObservers[n];s&&i(n).observers.push(Zn(e.value,s))}}for(let t in Wn)i(t).handlers.push(Wn[t]);for(let t in Mn)i(t).observers.push(Mn[t]);return e}const Tn=[{key:"Backspace",keyCode:8,inputType:"deleteContentBackward"},{key:"Enter",keyCode:13,inputType:"insertParagraph"},{key:"Enter",keyCode:13,inputType:"insertLineBreak"},{key:"Delete",keyCode:46,inputType:"deleteContentForward"}],An="dthko",Rn=[16,17,18,20,91,92,224,225];function Xn(t){return.7*Math.max(0,t)+8}class Yn{constructor(t,e,i,n){this.view=t,this.startEvent=e,this.style=i,this.mustSelect=n,this.scrollSpeed={x:0,y:0},this.scrolling=-1,this.lastEvent=e,this.scrollParent=function(t){let e=t.ownerDocument;for(let i=t.parentNode;i&&i!=e.body;)if(1==i.nodeType){if(i.scrollHeight>i.clientHeight||i.scrollWidth>i.clientWidth)return i;i=i.assignedSlot||i.parentNode}else{if(11!=i.nodeType)break;i=i.host}return null}(t.contentDOM),this.atoms=t.state.facet(tn).map((e=>e(t)));let s=t.contentDOM.ownerDocument;s.addEventListener("mousemove",this.move=this.move.bind(this)),s.addEventListener("mouseup",this.up=this.up.bind(this)),this.extend=e.shiftKey,this.multiple=t.state.facet(Qt.allowMultipleSelections)&&function(t,e){let i=t.state.facet(Xi);return i.length?i[0](e):_e.mac?e.metaKey:e.ctrlKey}(t,e),this.dragging=!(!function(t,e){let{main:i}=t.state.selection;if(i.empty)return!1;let n=se(t.root);if(!n||0==n.rangeCount)return!0;let s=n.getRangeAt(0).getClientRects();for(let t=0;t<s.length;t++){let i=s[t];if(i.left<=e.clientX&&i.right>=e.clientX&&i.top<=e.clientY&&i.bottom>=e.clientY)return!0}return!1}(t,e)||1!=Nn(e))&&null}start(t){!1===this.dragging&&this.select(t)}move(t){var e,i,n;if(0==t.buttons)return this.destroy();if(this.dragging||null==this.dragging&&(i=this.startEvent,n=t,Math.max(Math.abs(i.clientX-n.clientX),Math.abs(i.clientY-n.clientY))<10))return;this.select(this.lastEvent=t);let s=0,r=0,o=(null===(e=this.scrollParent)||void 0===e?void 0:e.getBoundingClientRect())||{left:0,top:0,right:this.view.win.innerWidth,bottom:this.view.win.innerHeight},a=rn(this.view);t.clientX-a.left<=o.left+6?s=-Xn(o.left-t.clientX):t.clientX+a.right>=o.right-6&&(s=Xn(t.clientX-o.right)),t.clientY-a.top<=o.top+6?r=-Xn(o.top-t.clientY):t.clientY+a.bottom>=o.bottom-6&&(r=Xn(t.clientY-o.bottom)),this.setScrollSpeed(s,r)}up(t){null==this.dragging&&this.select(this.lastEvent),this.dragging||t.preventDefault(),this.destroy()}destroy(){this.setScrollSpeed(0,0);let t=this.view.contentDOM.ownerDocument;t.removeEventListener("mousemove",this.move),t.removeEventListener("mouseup",this.up),this.view.inputState.mouseSelection=this.view.inputState.draggedContent=null}setScrollSpeed(t,e){this.scrollSpeed={x:t,y:e},t||e?this.scrolling<0&&(this.scrolling=setInterval((()=>this.scroll()),50)):this.scrolling>-1&&(clearInterval(this.scrolling),this.scrolling=-1)}scroll(){this.scrollParent?(this.scrollParent.scrollLeft+=this.scrollSpeed.x,this.scrollParent.scrollTop+=this.scrollSpeed.y):this.view.win.scrollBy(this.scrollSpeed.x,this.scrollSpeed.y),!1===this.dragging&&this.select(this.lastEvent)}skipAtoms(t){let e=null;for(let i=0;i<t.ranges.length;i++){let n=t.ranges[i],s=null;if(n.empty){let t=Qn(this.atoms,n.from,0);t!=n.from&&(s=Y.cursor(t,-1))}else{let t=Qn(this.atoms,n.from,-1),e=Qn(this.atoms,n.to,1);t==n.from&&e==n.to||(s=Y.range(n.from==n.anchor?t:e,n.from==n.head?t:e))}s&&(e||(e=t.ranges.slice()),e[i]=s)}return e?Y.create(e,t.mainIndex):t}select(t){let{view:e}=this,i=this.skipAtoms(this.style.get(t,this.extend,this.multiple));!this.mustSelect&&i.eq(e.state.selection,!1===this.dragging)||this.view.dispatch({selection:i,userEvent:"select.pointer"}),this.mustSelect=!1}update(t){this.style.update(t)&&setTimeout((()=>this.select(this.lastEvent)),20)}}const Wn=Object.create(null),Mn=Object.create(null),jn=_e.ie&&_e.ie_version<15||_e.ios&&_e.webkit_version<604;function Dn(t,e){let i,{state:n}=t,s=1,r=n.toText(e),o=r.lines==n.selection.ranges.length;if(null!=Hn&&n.selection.ranges.every((t=>t.empty))&&Hn==r.toString()){let t=-1;i=n.changeByRange((i=>{let a=n.doc.lineAt(i.from);if(a.from==t)return{range:i};t=a.from;let l=n.toText((o?r.line(s++).text:e)+n.lineBreak);return{changes:{from:a.from,insert:l},range:Y.cursor(i.from+l.length)}}))}else i=o?n.changeByRange((t=>{let e=r.line(s++);return{changes:{from:t.from,to:t.to,insert:e.text},range:Y.cursor(t.from+e.length)}})):n.replaceSelection(r);t.dispatch(i,{userEvent:"input.paste",scrollIntoView:!0})}function En(t,e,i,n){if(1==n)return Y.cursor(e,i);if(2==n)return function(t,e,i=1){let n=t.charCategorizer(e),s=t.doc.lineAt(e),r=e-s.from;if(0==s.length)return Y.cursor(e);0==r?i=1:r==s.length&&(i=-1);let o=r,a=r;i<0?o=O(s.text,r,!1):a=O(s.text,r);let l=n(s.text.slice(o,a));for(;o>0;){let t=O(s.text,o,!1);if(n(s.text.slice(t,o))!=l)break;o=t}for(;a<s.length;){let t=O(s.text,a);if(n(s.text.slice(a,t))!=l)break;a=t}return Y.range(o+s.from,a+s.from)}(t.state,e,i);{let i=ti.find(t.docView,e),n=t.state.doc.lineAt(i?i.posAtEnd:e),s=i?i.posAtStart:n.from,r=i?i.posAtEnd:n.to;return r<t.state.doc.length&&r==n.to&&r++,Y.range(s,r)}}Mn.scroll=t=>{t.inputState.lastScrollTop=t.scrollDOM.scrollTop,t.inputState.lastScrollLeft=t.scrollDOM.scrollLeft},Wn.keydown=(t,e)=>(t.inputState.setSelectionOrigin("select"),27==e.keyCode&&(t.inputState.lastEscPress=Date.now()),!1),Mn.touchstart=(t,e)=>{t.inputState.lastTouchTime=Date.now(),t.inputState.setSelectionOrigin("select.pointer")},Mn.touchmove=t=>{t.inputState.setSelectionOrigin("select.pointer")},Wn.mousedown=(t,e)=>{if(t.observer.flush(),t.inputState.lastTouchTime>Date.now()-2e3)return!1;let i=null;for(let n of t.state.facet(Wi))if(i=n(t,e),i)break;if(i||0!=e.button||(i=function(t,e){let i=In(t,e),n=Nn(e),s=t.state.selection;return{update(t){t.docChanged&&(i.pos=t.changes.mapPos(i.pos),s=s.map(t.changes))},get(e,r,o){let a,l=In(t,e),h=En(t,l.pos,l.bias,n);if(i.pos!=l.pos&&!r){let e=En(t,i.pos,i.bias,n),s=Math.min(e.from,h.from),r=Math.max(e.to,h.to);h=s<h.from?Y.range(s,r):Y.range(r,s)}return r?s.replaceRange(s.main.extend(h.from,h.to)):o&&1==n&&s.ranges.length>1&&(a=function(t,e){for(let i=0;i<t.ranges.length;i++){let{from:n,to:s}=t.ranges[i];if(n<=e&&s>=e)return Y.create(t.ranges.slice(0,i).concat(t.ranges.slice(i+1)),t.mainIndex==i?0:t.mainIndex-(t.mainIndex>i?1:0))}return null}(s,l.pos))?a:o?s.addRange(h):Y.create([h])}}}(t,e)),i){let n=!t.hasFocus;t.inputState.startMouseSelection(new Yn(t,e,i,n)),n&&t.observer.ignore((()=>we(t.contentDOM)));let s=t.inputState.mouseSelection;if(s)return s.start(e),!1===s.dragging}return!1};let qn=(t,e)=>t>=e.top&&t<=e.bottom,_n=(t,e,i)=>qn(e,i)&&t>=i.left&&t<=i.right;function Vn(t,e,i,n){let s=ti.find(t.docView,e);if(!s)return 1;let r=e-s.posAtStart;if(0==r)return 1;if(r==s.length)return-1;let o=s.coordsAt(r,-1);if(o&&_n(i,n,o))return-1;let a=s.coordsAt(r,1);return a&&_n(i,n,a)?1:o&&qn(n,o)?-1:1}function In(t,e){let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1);return{pos:i,bias:Vn(t,i,e.clientX,e.clientY)}}const zn=_e.ie&&_e.ie_version<=11;let Bn=null,Gn=0,Ln=0;function Nn(t){if(!zn)return t.detail;let e=Bn,i=Ln;return Bn=t,Ln=Date.now(),Gn=!e||i>Date.now()-400&&Math.abs(e.clientX-t.clientX)<2&&Math.abs(e.clientY-t.clientY)<2?(Gn+1)%3:1}function Un(t,e,i,n){if(!i)return;let s=t.posAtCoords({x:e.clientX,y:e.clientY},!1),{draggedContent:r}=t.inputState,o=n&&r&&function(t,e){let i=t.state.facet(Yi);return i.length?i[0](e):_e.mac?!e.altKey:!e.ctrlKey}(t,e)?{from:r.from,to:r.to}:null,a={from:s,insert:i},l=t.state.changes(o?[o,a]:a);t.focus(),t.dispatch({changes:l,selection:{anchor:l.mapPos(s,-1),head:l.mapPos(s,1)},userEvent:o?"move.drop":"input.drop"}),t.inputState.draggedContent=null}Wn.dragstart=(t,e)=>{let{selection:{main:i}}=t.state;if(e.target.draggable){let n=t.docView.nearest(e.target);if(n&&n.isWidget){let t=n.posAtStart,e=t+n.length;(t>=i.to||e<=i.from)&&(i=Y.range(t,e))}}let{inputState:n}=t;return n.mouseSelection&&(n.mouseSelection.dragging=!0),n.draggedContent=i,e.dataTransfer&&(e.dataTransfer.setData("Text",t.state.sliceDoc(i.from,i.to)),e.dataTransfer.effectAllowed="copyMove"),!1},Wn.dragend=t=>(t.inputState.draggedContent=null,!1),Wn.drop=(t,e)=>{if(!e.dataTransfer)return!1;if(t.state.readOnly)return!0;let i=e.dataTransfer.files;if(i&&i.length){let n=Array(i.length),s=0,r=()=>{++s==i.length&&Un(t,e,n.filter((t=>null!=t)).join(t.state.lineBreak),!1)};for(let t=0;t<i.length;t++){let e=new FileReader;e.onerror=r,e.onload=()=>{/[\x00-\x08\x0e-\x1f]{2}/.test(e.result)||(n[t]=e.result),r()},e.readAsText(i[t])}return!0}{let i=e.dataTransfer.getData("Text");if(i)return Un(t,e,i,!0),!0}return!1},Wn.paste=(t,e)=>{if(t.state.readOnly)return!0;t.observer.flush();let i=jn?null:e.clipboardData;return i?(Dn(t,i.getData("text/plain")||i.getData("text/uri-text")),!0):(function(t){let e=t.dom.parentNode;if(!e)return;let i=e.appendChild(document.createElement("textarea"));i.style.cssText="position: fixed; left: -10000px; top: 10px",i.focus(),setTimeout((()=>{t.focus(),i.remove(),Dn(t,i.value)}),50)}(t),!1)};let Hn=null;Wn.copy=Wn.cut=(t,e)=>{let{text:i,ranges:n,linewise:s}=function(t){let e=[],i=[],n=!1;for(let n of t.selection.ranges)n.empty||(e.push(t.sliceDoc(n.from,n.to)),i.push(n));if(!e.length){let s=-1;for(let{from:n}of t.selection.ranges){let r=t.doc.lineAt(n);r.number>s&&(e.push(r.text),i.push({from:r.from,to:Math.min(t.doc.length,r.to+1)})),s=r.number}n=!0}return{text:e.join(t.lineBreak),ranges:i,linewise:n}}(t.state);if(!i&&!s)return!1;Hn=s?i:null,"cut"!=e.type||t.state.readOnly||t.dispatch({changes:n,scrollIntoView:!0,userEvent:"delete.cut"});let r=jn?null:e.clipboardData;return r?(r.clearData(),r.setData("text/plain",i),!0):(function(t,e){let i=t.dom.parentNode;if(!i)return;let n=i.appendChild(document.createElement("textarea"));n.style.cssText="position: fixed; left: -10000px; top: 10px",n.value=e,n.focus(),n.selectionEnd=e.length,n.selectionStart=0,setTimeout((()=>{n.remove(),t.focus()}),50)}(t,i),!1)};const Fn=ct.define();function Jn(t,e){let i=[];for(let n of t.facet(Ei)){let s=n(t,e);s&&i.push(s)}return i?t.update({effects:i,annotations:Fn.of(!0)}):null}function Kn(t){setTimeout((()=>{let e=t.hasFocus;if(e!=t.inputState.notifiedFocused){let i=Jn(t.state,e);i?t.dispatch(i):t.update([])}}),10)}Mn.focus=t=>{t.inputState.lastFocusTime=Date.now(),t.scrollDOM.scrollTop||!t.inputState.lastScrollTop&&!t.inputState.lastScrollLeft||(t.scrollDOM.scrollTop=t.inputState.lastScrollTop,t.scrollDOM.scrollLeft=t.inputState.lastScrollLeft),Kn(t)},Mn.blur=t=>{t.observer.clearSelectionRange(),Kn(t)},Mn.compositionstart=Mn.compositionupdate=t=>{null==t.inputState.compositionFirstChange&&(t.inputState.compositionFirstChange=!0),t.inputState.composing<0&&(t.inputState.composing=0)},Mn.compositionend=t=>{t.inputState.composing=-1,t.inputState.compositionEndedAt=Date.now(),t.inputState.compositionPendingKey=!0,t.inputState.compositionPendingChange=t.observer.pendingRecords().length>0,t.inputState.compositionFirstChange=null,_e.chrome&&_e.android?t.observer.flushSoon():t.inputState.compositionPendingChange?Promise.resolve().then((()=>t.observer.flush())):setTimeout((()=>{t.inputState.composing<0&&t.docView.hasComposition&&t.update([])}),50)},Mn.contextmenu=t=>{t.inputState.lastContextMenu=Date.now()},Wn.beforeinput=(t,e)=>{var i;let n;if(_e.chrome&&_e.android&&(n=Tn.find((t=>t.inputType==e.inputType)))&&(t.observer.delayAndroidKey(n.key,n.keyCode),"Backspace"==n.key||"Delete"==n.key)){let e=(null===(i=window.visualViewport)||void 0===i?void 0:i.height)||0;setTimeout((()=>{var i;((null===(i=window.visualViewport)||void 0===i?void 0:i.height)||0)>e+10&&t.hasFocus&&(t.contentDOM.blur(),t.focus())}),100)}return!1};const ts=new Set;const es=["pre-wrap","normal","pre-line","break-spaces"];class is{constructor(t){this.lineWrapping=t,this.doc=e.empty,this.heightSamples={},this.lineHeight=14,this.charWidth=7,this.textHeight=14,this.lineLength=30,this.heightChanged=!1}heightForGap(t,e){let i=this.doc.lineAt(e).number-this.doc.lineAt(t).number+1;return this.lineWrapping&&(i+=Math.max(0,Math.ceil((e-t-i*this.lineLength*.5)/this.lineLength))),this.lineHeight*i}heightForLine(t){if(!this.lineWrapping)return this.lineHeight;return(1+Math.max(0,Math.ceil((t-this.lineLength)/(this.lineLength-5))))*this.lineHeight}setDoc(t){return this.doc=t,this}mustRefreshForWrapping(t){return es.indexOf(t)>-1!=this.lineWrapping}mustRefreshForHeights(t){let e=!1;for(let i=0;i<t.length;i++){let n=t[i];n<0?i++:this.heightSamples[Math.floor(10*n)]||(e=!0,this.heightSamples[Math.floor(10*n)]=!0)}return e}refresh(t,e,i,n,s,r){let o=es.indexOf(t)>-1,a=Math.round(e)!=Math.round(this.lineHeight)||this.lineWrapping!=o;if(this.lineWrapping=o,this.lineHeight=e,this.charWidth=i,this.textHeight=n,this.lineLength=s,a){this.heightSamples={};for(let t=0;t<r.length;t++){let e=r[t];e<0?t++:this.heightSamples[Math.floor(10*e)]=!0}}return a}}class ns{constructor(t,e){this.from=t,this.heights=e,this.index=0}get more(){return this.index<this.heights.length}}class ss{constructor(t,e,i,n,s){this.from=t,this.length=e,this.top=i,this.height=n,this._content=s}get type(){return"number"==typeof this._content?ni.Text:Array.isArray(this._content)?this._content:this._content.type}get to(){return this.from+this.length}get bottom(){return this.top+this.height}get widget(){return this._content instanceof ai?this._content.widget:null}get widgetLineBreaks(){return"number"==typeof this._content?this._content:0}join(t){let e=(Array.isArray(this._content)?this._content:[this]).concat(Array.isArray(t._content)?t._content:[t]);return new ss(this.from,this.length+t.length,this.top,this.height+t.height,e)}}var rs=function(t){return t[t.ByPos=0]="ByPos",t[t.ByHeight=1]="ByHeight",t[t.ByPosNoHeight=2]="ByPosNoHeight",t}(rs||(rs={}));const os=.001;class as{constructor(t,e,i=2){this.length=t,this.height=e,this.flags=i}get outdated(){return(2&this.flags)>0}set outdated(t){this.flags=(t?2:0)|-3&this.flags}setHeight(t,e){this.height!=e&&(Math.abs(this.height-e)>os&&(t.heightChanged=!0),this.height=e)}replace(t,e,i){return as.of(i)}decomposeLeft(t,e){e.push(this)}decomposeRight(t,e){e.push(this)}applyChanges(t,e,i,n){let s=this,r=i.doc;for(let o=n.length-1;o>=0;o--){let{fromA:a,toA:l,fromB:h,toB:c}=n[o],f=s.lineAt(a,rs.ByPosNoHeight,i.setDoc(e),0,0),u=f.to>=l?f:s.lineAt(l,rs.ByPosNoHeight,i,0,0);for(c+=u.to-l,l=u.to;o>0&&f.from<=n[o-1].toA;)a=n[o-1].fromA,h=n[o-1].fromB,o--,a<f.from&&(f=s.lineAt(a,rs.ByPosNoHeight,i,0,0));h+=f.from-a,a=f.from;let d=ds.build(i.setDoc(r),t,h,c);s=s.replace(a,l,d)}return s.updateHeight(i,0)}static empty(){return new hs(0,0)}static of(t){if(1==t.length)return t[0];let e=0,i=t.length,n=0,s=0;for(;;)if(e==i)if(n>2*s){let s=t[e-1];s.break?t.splice(--e,1,s.left,null,s.right):t.splice(--e,1,s.left,s.right),i+=1+s.break,n-=s.size}else{if(!(s>2*n))break;{let e=t[i];e.break?t.splice(i,1,e.left,null,e.right):t.splice(i,1,e.left,e.right),i+=2+e.break,s-=e.size}}else if(n<s){let i=t[e++];i&&(n+=i.size)}else{let e=t[--i];e&&(s+=e.size)}let r=0;return null==t[e-1]?(r=1,e--):null==t[e]&&(r=1,i++),new fs(as.of(t.slice(0,e)),r,as.of(t.slice(i)))}}as.prototype.size=1;class ls extends as{constructor(t,e,i){super(t,e),this.deco=i}blockAt(t,e,i,n){return new ss(n,this.length,i,this.height,this.deco||0)}lineAt(t,e,i,n,s){return this.blockAt(0,i,n,s)}forEachLine(t,e,i,n,s,r){t<=s+this.length&&e>=s&&r(this.blockAt(0,i,n,s))}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more&&this.setHeight(t,n.heights[n.index++]),this.outdated=!1,this}toString(){return`block(${this.length})`}}class hs extends ls{constructor(t,e){super(t,e,null),this.collapsed=0,this.widgetHeight=0,this.breaks=0}blockAt(t,e,i,n){return new ss(n,this.length,i,this.height,this.breaks)}replace(t,e,i){let n=i[0];return 1==i.length&&(n instanceof hs||n instanceof cs&&4&n.flags)&&Math.abs(this.length-n.length)<10?(n instanceof cs?n=new hs(n.length,this.height):n.height=this.height,this.outdated||(n.outdated=!1),n):as.of(i)}updateHeight(t,e=0,i=!1,n){return n&&n.from<=e&&n.more?this.setHeight(t,n.heights[n.index++]):(i||this.outdated)&&this.setHeight(t,Math.max(this.widgetHeight,t.heightForLine(this.length-this.collapsed))+this.breaks*t.lineHeight),this.outdated=!1,this}toString(){return`line(${this.length}${this.collapsed?-this.collapsed:""}${this.widgetHeight?":"+this.widgetHeight:""})`}}class cs extends as{constructor(t){super(t,0)}heightMetrics(t,e){let i,n=t.doc.lineAt(e).number,s=t.doc.lineAt(e+this.length).number,r=s-n+1,o=0;if(t.lineWrapping){let e=Math.min(this.height,t.lineHeight*r);i=e/r,this.length>r+1&&(o=(this.height-e)/(this.length-r-1))}else i=this.height/r;return{firstLine:n,lastLine:s,perLine:i,perChar:o}}blockAt(t,e,i,n){let{firstLine:s,lastLine:r,perLine:o,perChar:a}=this.heightMetrics(e,n);if(e.lineWrapping){let s=n+Math.round(Math.max(0,Math.min(1,(t-i)/this.height))*this.length),r=e.doc.lineAt(s),l=o+r.length*a,h=Math.max(i,t-l/2);return new ss(r.from,r.length,h,l,0)}{let n=Math.max(0,Math.min(r-s,Math.floor((t-i)/o))),{from:a,length:l}=e.doc.line(s+n);return new ss(a,l,i+o*n,o,0)}}lineAt(t,e,i,n,s){if(e==rs.ByHeight)return this.blockAt(t,i,n,s);if(e==rs.ByPosNoHeight){let{from:e,to:n}=i.doc.lineAt(t);return new ss(e,n-e,0,0,0)}let{firstLine:r,perLine:o,perChar:a}=this.heightMetrics(i,s),l=i.doc.lineAt(t),h=o+l.length*a,c=l.number-r,f=n+o*c+a*(l.from-s-c);return new ss(l.from,l.length,Math.max(n,Math.min(f,n+this.height-h)),h,0)}forEachLine(t,e,i,n,s,r){t=Math.max(t,s),e=Math.min(e,s+this.length);let{firstLine:o,perLine:a,perChar:l}=this.heightMetrics(i,s);for(let h=t,c=n;h<=e;){let e=i.doc.lineAt(h);if(h==t){let i=e.number-o;c+=a*i+l*(t-s-i)}let n=a+l*e.length;r(new ss(e.from,e.length,c,n,0)),c+=n,h=e.to+1}}replace(t,e,i){let n=this.length-e;if(n>0){let t=i[i.length-1];t instanceof cs?i[i.length-1]=new cs(t.length+n):i.push(null,new cs(n-1))}if(t>0){let e=i[0];e instanceof cs?i[0]=new cs(t+e.length):i.unshift(new cs(t-1),null)}return as.of(i)}decomposeLeft(t,e){e.push(new cs(t-1),null)}decomposeRight(t,e){e.push(null,new cs(this.length-t-1))}updateHeight(t,e=0,i=!1,n){let s=e+this.length;if(n&&n.from<=e+this.length&&n.more){let i=[],r=Math.max(e,n.from),o=-1;for(n.from>e&&i.push(new cs(n.from-e-1).updateHeight(t,e));r<=s&&n.more;){let e=t.doc.lineAt(r).length;i.length&&i.push(null);let s=n.heights[n.index++];-1==o?o=s:Math.abs(s-o)>=os&&(o=-2);let a=new hs(e,s);a.outdated=!1,i.push(a),r+=e+1}r<=s&&i.push(null,new cs(s-r).updateHeight(t,r));let a=as.of(i);return(o<0||Math.abs(a.height-this.height)>=os||Math.abs(o-this.heightMetrics(t,e).perLine)>=os)&&(t.heightChanged=!0),a}return(i||this.outdated)&&(this.setHeight(t,t.heightForGap(e,e+this.length)),this.outdated=!1),this}toString(){return`gap(${this.length})`}}class fs extends as{constructor(t,e,i){super(t.length+e+i.length,t.height+i.height,e|(t.outdated||i.outdated?2:0)),this.left=t,this.right=i,this.size=t.size+i.size}get break(){return 1&this.flags}blockAt(t,e,i,n){let s=i+this.left.height;return t<s?this.left.blockAt(t,e,i,n):this.right.blockAt(t,e,s,n+this.left.length+this.break)}lineAt(t,e,i,n,s){let r=n+this.left.height,o=s+this.left.length+this.break,a=e==rs.ByHeight?t<r:t<o,l=a?this.left.lineAt(t,e,i,n,s):this.right.lineAt(t,e,i,r,o);if(this.break||(a?l.to<o:l.from>o))return l;let h=e==rs.ByPosNoHeight?rs.ByPosNoHeight:rs.ByPos;return a?l.join(this.right.lineAt(o,h,i,r,o)):this.left.lineAt(o,h,i,n,s).join(l)}forEachLine(t,e,i,n,s,r){let o=n+this.left.height,a=s+this.left.length+this.break;if(this.break)t<a&&this.left.forEachLine(t,e,i,n,s,r),e>=a&&this.right.forEachLine(t,e,i,o,a,r);else{let l=this.lineAt(a,rs.ByPos,i,n,s);t<l.from&&this.left.forEachLine(t,l.from-1,i,n,s,r),l.to>=t&&l.from<=e&&r(l),e>l.to&&this.right.forEachLine(l.to+1,e,i,o,a,r)}}replace(t,e,i){let n=this.left.length+this.break;if(e<n)return this.balanced(this.left.replace(t,e,i),this.right);if(t>this.left.length)return this.balanced(this.left,this.right.replace(t-n,e-n,i));let s=[];t>0&&this.decomposeLeft(t,s);let r=s.length;for(let t of i)s.push(t);if(t>0&&us(s,r-1),e<this.length){let t=s.length;this.decomposeRight(e,s),us(s,t)}return as.of(s)}decomposeLeft(t,e){let i=this.left.length;if(t<=i)return this.left.decomposeLeft(t,e);e.push(this.left),this.break&&(i++,t>=i&&e.push(null)),t>i&&this.right.decomposeLeft(t-i,e)}decomposeRight(t,e){let i=this.left.length,n=i+this.break;if(t>=n)return this.right.decomposeRight(t-n,e);t<i&&this.left.decomposeRight(t,e),this.break&&t<n&&e.push(null),e.push(this.right)}balanced(t,e){return t.size>2*e.size||e.size>2*t.size?as.of(this.break?[t,null,e]:[t,e]):(this.left=t,this.right=e,this.height=t.height+e.height,this.outdated=t.outdated||e.outdated,this.size=t.size+e.size,this.length=t.length+this.break+e.length,this)}updateHeight(t,e=0,i=!1,n){let{left:s,right:r}=this,o=e+s.length+this.break,a=null;return n&&n.from<=e+s.length&&n.more?a=s=s.updateHeight(t,e,i,n):s.updateHeight(t,e,i),n&&n.from<=o+r.length&&n.more?a=r=r.updateHeight(t,o,i,n):r.updateHeight(t,o,i),a?this.balanced(s,r):(this.height=this.left.height+this.right.height,this.outdated=!1,this)}toString(){return this.left+(this.break?" ":"-")+this.right}}function us(t,e){let i,n;null==t[e]&&(i=t[e-1])instanceof cs&&(n=t[e+1])instanceof cs&&t.splice(e-1,3,new cs(i.length+1+n.length))}class ds{constructor(t,e){this.pos=t,this.oracle=e,this.nodes=[],this.lineStart=-1,this.lineEnd=-1,this.covering=null,this.writtenTo=t}get isCovered(){return this.covering&&this.nodes[this.nodes.length-1]==this.covering}span(t,e){if(this.lineStart>-1){let t=Math.min(e,this.lineEnd),i=this.nodes[this.nodes.length-1];i instanceof hs?i.length+=t-this.pos:(t>this.pos||!this.isCovered)&&this.nodes.push(new hs(t-this.pos,-1)),this.writtenTo=t,e>t&&(this.nodes.push(null),this.writtenTo++,this.lineStart=-1)}this.pos=e}point(t,e,i){if(t<e||i.heightRelevant){let n=i.widget?i.widget.estimatedHeight:0,s=i.widget?i.widget.lineBreaks:0;n<0&&(n=this.oracle.lineHeight);let r=e-t;i.block?this.addBlock(new ls(r,n,i)):(r||s||n>=5)&&this.addLineDeco(n,s,r)}else e>t&&this.span(t,e);this.lineEnd>-1&&this.lineEnd<this.pos&&(this.lineEnd=this.oracle.doc.lineAt(this.pos).to)}enterLine(){if(this.lineStart>-1)return;let{from:t,to:e}=this.oracle.doc.lineAt(this.pos);this.lineStart=t,this.lineEnd=e,this.writtenTo<t&&((this.writtenTo<t-1||null==this.nodes[this.nodes.length-1])&&this.nodes.push(this.blankContent(this.writtenTo,t-1)),this.nodes.push(null)),this.pos>t&&this.nodes.push(new hs(this.pos-t,-1)),this.writtenTo=this.pos}blankContent(t,e){let i=new cs(e-t);return this.oracle.doc.lineAt(t).to==e&&(i.flags|=4),i}ensureLine(){this.enterLine();let t=this.nodes.length?this.nodes[this.nodes.length-1]:null;if(t instanceof hs)return t;let e=new hs(0,-1);return this.nodes.push(e),e}addBlock(t){this.enterLine();let e=t.deco;e&&e.startSide>0&&!this.isCovered&&this.ensureLine(),this.nodes.push(t),this.writtenTo=this.pos=this.pos+t.length,e&&e.endSide>0&&(this.covering=t)}addLineDeco(t,e,i){let n=this.ensureLine();n.length+=i,n.collapsed+=i,n.widgetHeight=Math.max(n.widgetHeight,t),n.breaks+=e,this.writtenTo=this.pos=this.pos+i}finish(t){let e=0==this.nodes.length?null:this.nodes[this.nodes.length-1];!(this.lineStart>-1)||e instanceof hs||this.isCovered?(this.writtenTo<this.pos||null==e)&&this.nodes.push(this.blankContent(this.writtenTo,this.pos)):this.nodes.push(new hs(0,-1));let i=t;for(let t of this.nodes)t instanceof hs&&t.updateHeight(this.oracle,i),i+=t?t.length:1;return this.nodes}static build(t,e,i,n){let s=new ds(i,t);return At.spans(e,i,n,s,0),s.finish(i)}}class ps{constructor(){this.changes=[]}compareRange(){}comparePoint(t,e,i,n){(t<e||i&&i.heightRelevant||n&&n.heightRelevant)&&hi(t,e,this.changes,5)}}function Os(t,e){let i=t.getBoundingClientRect(),n=t.ownerDocument,s=n.defaultView||window,r=Math.max(0,i.left),o=Math.min(s.innerWidth,i.right),a=Math.max(0,i.top),l=Math.min(s.innerHeight,i.bottom);for(let e=t.parentNode;e&&e!=n.body;)if(1==e.nodeType){let i=e,n=window.getComputedStyle(i);if((i.scrollHeight>i.clientHeight||i.scrollWidth>i.clientWidth)&&"visible"!=n.overflow){let n=i.getBoundingClientRect();r=Math.max(r,n.left),o=Math.min(o,n.right),a=Math.max(a,n.top),l=e==t.parentNode?n.bottom:Math.min(l,n.bottom)}e="absolute"==n.position||"fixed"==n.position?i.offsetParent:i.parentNode}else{if(11!=e.nodeType)break;e=e.host}return{left:r-i.left,right:Math.max(r,o)-i.left,top:a-(i.top+e),bottom:Math.max(a,l)-(i.top+e)}}function gs(t,e){let i=t.getBoundingClientRect();return{left:0,right:i.right-i.left,top:e,bottom:i.bottom-(i.top+e)}}class ms{constructor(t,e,i){this.from=t,this.to=e,this.size=i}static same(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++){let n=t[i],s=e[i];if(n.from!=s.from||n.to!=s.to||n.size!=s.size)return!1}return!0}draw(t,e){return si.replace({widget:new ws(this.size*(e?t.scaleY:t.scaleX),e)}).range(this.from,this.to)}}class ws extends ii{constructor(t,e){super(),this.size=t,this.vertical=e}eq(t){return t.size==this.size&&t.vertical==this.vertical}toDOM(){let t=document.createElement("div");return this.vertical?t.style.height=this.size+"px":(t.style.width=this.size+"px",t.style.height="2px",t.style.display="inline-block"),t}get estimatedHeight(){return this.vertical?this.size:-1}}class vs{constructor(t){this.state=t,this.pixelViewport={left:0,right:window.innerWidth,top:0,bottom:0},this.inView=!0,this.paddingTop=0,this.paddingBottom=0,this.contentDOMWidth=0,this.contentDOMHeight=0,this.editorHeight=0,this.editorWidth=0,this.scrollTop=0,this.scrolledToBottom=!0,this.scaleX=1,this.scaleY=1,this.scrollAnchorPos=0,this.scrollAnchorHeight=-1,this.scaler=ks,this.scrollTarget=null,this.printing=!1,this.mustMeasureContent=!0,this.defaultTextDirection=di.LTR,this.visibleRanges=[],this.mustEnforceCursorAssoc=!1;let i=t.facet(Fi).some((t=>"function"!=typeof t&&"cm-lineWrapping"==t.class));this.heightOracle=new is(i),this.stateDeco=t.facet(Ji).filter((t=>"function"!=typeof t)),this.heightMap=as.empty().applyChanges(this.stateDeco,e.empty,this.heightOracle.setDoc(t.doc),[new an(0,0,0,t.doc.length)]),this.viewport=this.getViewport(0,null),this.updateViewportLines(),this.updateForViewport(),this.lineGaps=this.ensureLineGaps([]),this.lineGapDeco=si.set(this.lineGaps.map((t=>t.draw(this,!1)))),this.computeVisibleRanges()}updateForViewport(){let t=[this.viewport],{main:e}=this.state.selection;for(let i=0;i<=1;i++){let n=i?e.head:e.anchor;if(!t.some((({from:t,to:e})=>n>=t&&n<=e))){let{from:e,to:i}=this.lineBlockAt(n);t.push(new bs(e,i))}}this.viewports=t.sort(((t,e)=>t.from-e.from)),this.scaler=this.heightMap.height<=7e6?ks:new Qs(this.heightOracle,this.heightMap,this.viewports)}updateViewportLines(){this.viewportLines=[],this.heightMap.forEachLine(this.viewport.from,this.viewport.to,this.heightOracle.setDoc(this.state.doc),0,0,(t=>{this.viewportLines.push(1==this.scaler.scale?t:$s(t,this.scaler))}))}update(t,e=null){this.state=t.state;let i=this.stateDeco;this.stateDeco=this.state.facet(Ji).filter((t=>"function"!=typeof t));let n=t.changedRanges,s=an.extendWithRanges(n,function(t,e,i){let n=new ps;return At.compare(t,e,i,n,0),n.changes}(i,this.stateDeco,t?t.changes:$.empty(this.state.doc.length))),r=this.heightMap.height,o=this.scrolledToBottom?null:this.scrollAnchorAt(this.scrollTop);this.heightMap=this.heightMap.applyChanges(this.stateDeco,t.startState.doc,this.heightOracle.setDoc(this.state.doc),s),this.heightMap.height!=r&&(t.flags|=2),o?(this.scrollAnchorPos=t.changes.mapPos(o.from,-1),this.scrollAnchorHeight=o.top):(this.scrollAnchorPos=-1,this.scrollAnchorHeight=this.heightMap.height);let a=s.length?this.mapViewport(this.viewport,t.changes):this.viewport;(e&&(e.range.head<a.from||e.range.head>a.to)||!this.viewportIsAppropriate(a))&&(a=this.getViewport(0,e));let l=!t.changes.empty||2&t.flags||a.from!=this.viewport.from||a.to!=this.viewport.to;this.viewport=a,this.updateForViewport(),l&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps,t.changes))),t.flags|=this.computeVisibleRanges(),e&&(this.scrollTarget=e),!this.mustEnforceCursorAssoc&&t.selectionSet&&t.view.lineWrapping&&t.state.selection.main.empty&&t.state.selection.main.assoc&&!t.state.facet(_i)&&(this.mustEnforceCursorAssoc=!0)}measure(t){let i=t.contentDOM,n=window.getComputedStyle(i),s=this.heightOracle,r=n.whiteSpace;this.defaultTextDirection="rtl"==n.direction?di.RTL:di.LTR;let o=this.heightOracle.mustRefreshForWrapping(r),a=i.getBoundingClientRect(),l=o||this.mustMeasureContent||this.contentDOMHeight!=a.height;this.contentDOMHeight=a.height,this.mustMeasureContent=!1;let h=0,c=0;if(a.width&&a.height){let{scaleX:t,scaleY:e}=pe(i,a);this.scaleX==t&&this.scaleY==e||(this.scaleX=t,this.scaleY=e,h|=8,o=l=!0)}let f=(parseInt(n.paddingTop)||0)*this.scaleY,u=(parseInt(n.paddingBottom)||0)*this.scaleY;this.paddingTop==f&&this.paddingBottom==u||(this.paddingTop=f,this.paddingBottom=u,h|=10),this.editorWidth!=t.scrollDOM.clientWidth&&(s.lineWrapping&&(l=!0),this.editorWidth=t.scrollDOM.clientWidth,h|=8);let d=t.scrollDOM.scrollTop*this.scaleY;this.scrollTop!=d&&(this.scrollAnchorHeight=-1,this.scrollTop=d),this.scrolledToBottom=Se(t.scrollDOM);let p=(this.printing?gs:Os)(i,this.paddingTop),O=p.top-this.pixelViewport.top,g=p.bottom-this.pixelViewport.bottom;this.pixelViewport=p;let m=this.pixelViewport.bottom>this.pixelViewport.top&&this.pixelViewport.right>this.pixelViewport.left;if(m!=this.inView&&(this.inView=m,m&&(l=!0)),!this.inView&&!this.scrollTarget)return 0;let w=a.width;if(this.contentDOMWidth==w&&this.editorHeight==t.scrollDOM.clientHeight||(this.contentDOMWidth=a.width,this.editorHeight=t.scrollDOM.clientHeight,h|=8),l){let i=t.docView.measureVisibleLineHeights(this.viewport);if(s.mustRefreshForHeights(i)&&(o=!0),o||s.lineWrapping&&Math.abs(w-this.contentDOMWidth)>s.charWidth){let{lineHeight:e,charWidth:n,textHeight:a}=t.docView.measureTextSize();o=e>0&&s.refresh(r,e,n,a,w/n,i),o&&(t.docView.minWidth=0,h|=8)}O>0&&g>0?c=Math.max(O,g):O<0&&g<0&&(c=Math.min(O,g)),s.heightChanged=!1;for(let n of this.viewports){let r=n.from==this.viewport.from?i:t.docView.measureVisibleLineHeights(n);this.heightMap=(o?as.empty().applyChanges(this.stateDeco,e.empty,this.heightOracle,[new an(0,0,0,t.state.doc.length)]):this.heightMap).updateHeight(s,0,o,new ns(n.from,r))}s.heightChanged&&(h|=2)}let v=!this.viewportIsAppropriate(this.viewport,c)||this.scrollTarget&&(this.scrollTarget.range.head<this.viewport.from||this.scrollTarget.range.head>this.viewport.to);return v&&(this.viewport=this.getViewport(c,this.scrollTarget)),this.updateForViewport(),(2&h||v)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(o?[]:this.lineGaps,t)),h|=this.computeVisibleRanges(),this.mustEnforceCursorAssoc&&(this.mustEnforceCursorAssoc=!1,t.docView.enforceCursorAssoc()),h}get visibleTop(){return this.scaler.fromDOM(this.pixelViewport.top)}get visibleBottom(){return this.scaler.fromDOM(this.pixelViewport.bottom)}getViewport(t,e){let i=.5-Math.max(-.5,Math.min(.5,t/1e3/2)),n=this.heightMap,s=this.heightOracle,{visibleTop:r,visibleBottom:o}=this,a=new bs(n.lineAt(r-1e3*i,rs.ByHeight,s,0,0).from,n.lineAt(o+1e3*(1-i),rs.ByHeight,s,0,0).to);if(e){let{head:t}=e.range;if(t<a.from||t>a.to){let i,r=Math.min(this.editorHeight,this.pixelViewport.bottom-this.pixelViewport.top),o=n.lineAt(t,rs.ByPos,s,0,0);i="center"==e.y?(o.top+o.bottom)/2-r/2:"start"==e.y||"nearest"==e.y&&t<a.from?o.top:o.bottom-r,a=new bs(n.lineAt(i-500,rs.ByHeight,s,0,0).from,n.lineAt(i+r+500,rs.ByHeight,s,0,0).to)}}return a}mapViewport(t,e){let i=e.mapPos(t.from,-1),n=e.mapPos(t.to,1);return new bs(this.heightMap.lineAt(i,rs.ByPos,this.heightOracle,0,0).from,this.heightMap.lineAt(n,rs.ByPos,this.heightOracle,0,0).to)}viewportIsAppropriate({from:t,to:e},i=0){if(!this.inView)return!0;let{top:n}=this.heightMap.lineAt(t,rs.ByPos,this.heightOracle,0,0),{bottom:s}=this.heightMap.lineAt(e,rs.ByPos,this.heightOracle,0,0),{visibleTop:r,visibleBottom:o}=this;return(0==t||n<=r-Math.max(10,Math.min(-i,250)))&&(e==this.state.doc.length||s>=o+Math.max(10,Math.min(i,250)))&&n>r-2e3&&s<o+2e3}mapLineGaps(t,e){if(!t.length||e.empty)return t;let i=[];for(let n of t)e.touchesRange(n.from,n.to)||i.push(new ms(e.mapPos(n.from),e.mapPos(n.to),n.size));return i}ensureLineGaps(t,e){let i=this.heightOracle.lineWrapping,n=i?1e4:2e3,s=n>>1,r=n<<1;if(this.defaultTextDirection!=di.LTR&&!i)return[];let o=[],a=(n,r,l,h)=>{if(r-n<s)return;let c=this.state.selection.main,f=[c.from];c.empty||f.push(c.to);for(let t of f)if(t>n&&t<r)return a(n,t-10,l,h),void a(t+10,r,l,h);let u=function(t,e){for(let i of t)if(e(i))return i;return}(t,(t=>t.from>=l.from&&t.to<=l.to&&Math.abs(t.from-n)<s&&Math.abs(t.to-r)<s&&!f.some((e=>t.from<e&&t.to>e))));if(!u){if(r<l.to&&e&&i&&e.visibleRanges.some((t=>t.from<=r&&t.to>=r))){let t=e.moveToLineBoundary(Y.cursor(r),!1,!0).head;t>n&&(r=t)}u=new ms(n,r,this.gapSize(l,n,r,h))}o.push(u)};for(let t of this.viewportLines){if(t.length<r)continue;let e=ys(t.from,t.to,this.stateDeco);if(e.total<r)continue;let s,o,l=this.scrollTarget?this.scrollTarget.range.head:null;if(i){let i,r,a=n/this.heightOracle.lineLength*this.heightOracle.lineHeight;if(null!=l){let n=xs(e,l),s=((this.visibleBottom-this.visibleTop)/2+a)/t.height;i=n-s,r=n+s}else i=(this.visibleTop-t.top-a)/t.height,r=(this.visibleBottom-t.top+a)/t.height;s=Ss(e,i),o=Ss(e,r)}else{let t,i,r=e.total*this.heightOracle.charWidth,a=n*this.heightOracle.charWidth;if(null!=l){let n=xs(e,l),s=((this.pixelViewport.right-this.pixelViewport.left)/2+a)/r;t=n-s,i=n+s}else t=(this.pixelViewport.left-a)/r,i=(this.pixelViewport.right+a)/r;s=Ss(e,t),o=Ss(e,i)}s>t.from&&a(t.from,s,t,e),o<t.to&&a(o,t.to,t,e)}return o}gapSize(t,e,i,n){let s=xs(n,i)-xs(n,e);return this.heightOracle.lineWrapping?t.height*s:n.total*this.heightOracle.charWidth*s}updateLineGaps(t){ms.same(t,this.lineGaps)||(this.lineGaps=t,this.lineGapDeco=si.set(t.map((t=>t.draw(this,this.heightOracle.lineWrapping)))))}computeVisibleRanges(){let t=this.stateDeco;this.lineGaps.length&&(t=t.concat(this.lineGapDeco));let e=[];At.spans(t,this.viewport.from,this.viewport.to,{span(t,i){e.push({from:t,to:i})},point(){}},20);let i=e.length!=this.visibleRanges.length||this.visibleRanges.some(((t,i)=>t.from!=e[i].from||t.to!=e[i].to));return this.visibleRanges=e,i?4:0}lineBlockAt(t){return t>=this.viewport.from&&t<=this.viewport.to&&this.viewportLines.find((e=>e.from<=t&&e.to>=t))||$s(this.heightMap.lineAt(t,rs.ByPos,this.heightOracle,0,0),this.scaler)}lineBlockAtHeight(t){return $s(this.heightMap.lineAt(this.scaler.fromDOM(t),rs.ByHeight,this.heightOracle,0,0),this.scaler)}scrollAnchorAt(t){let e=this.lineBlockAtHeight(t+8);return e.from>=this.viewport.from||this.viewportLines[0].top-t>200?e:this.viewportLines[0]}elementAtHeight(t){return $s(this.heightMap.blockAt(this.scaler.fromDOM(t),this.heightOracle,0,0),this.scaler)}get docHeight(){return this.scaler.toDOM(this.heightMap.height)}get contentHeight(){return this.docHeight+this.paddingTop+this.paddingBottom}}class bs{constructor(t,e){this.from=t,this.to=e}}function ys(t,e,i){let n=[],s=t,r=0;return At.spans(i,t,e,{span(){},point(t,e){t>s&&(n.push({from:s,to:t}),r+=t-s),s=e}},20),s<e&&(n.push({from:s,to:e}),r+=e-s),{total:r,ranges:n}}function Ss({total:t,ranges:e},i){if(i<=0)return e[0].from;if(i>=1)return e[e.length-1].to;let n=Math.floor(t*i);for(let t=0;;t++){let{from:i,to:s}=e[t],r=s-i;if(n<=r)return i+n;n-=r}}function xs(t,e){let i=0;for(let{from:n,to:s}of t.ranges){if(e<=s){i+=e-n;break}i+=s-n}return i/t.total}const ks={toDOM:t=>t,fromDOM:t=>t,scale:1};class Qs{constructor(t,e,i){let n=0,s=0,r=0;this.viewports=i.map((({from:i,to:s})=>{let r=e.lineAt(i,rs.ByPos,t,0,0).top,o=e.lineAt(s,rs.ByPos,t,0,0).bottom;return n+=o-r,{from:i,to:s,top:r,bottom:o,domTop:0,domBottom:0}})),this.scale=(7e6-n)/(e.height-n);for(let t of this.viewports)t.domTop=r+(t.top-s)*this.scale,r=t.domBottom=t.domTop+(t.bottom-t.top),s=t.bottom}toDOM(t){for(let e=0,i=0,n=0;;e++){let s=e<this.viewports.length?this.viewports[e]:null;if(!s||t<s.top)return n+(t-i)*this.scale;if(t<=s.bottom)return s.domTop+(t-s.top);i=s.bottom,n=s.domBottom}}fromDOM(t){for(let e=0,i=0,n=0;;e++){let s=e<this.viewports.length?this.viewports[e]:null;if(!s||t<s.domTop)return i+(t-n)/this.scale;if(t<=s.domBottom)return s.top+(t-s.domTop);i=s.bottom,n=s.domBottom}}}function $s(t,e){if(1==e.scale)return t;let i=e.toDOM(t.top),n=e.toDOM(t.bottom);return new ss(t.from,t.length,i,n-i,Array.isArray(t._content)?t._content.map((t=>$s(t,e))):t._content)}const Ps=j.define({combine:t=>t.join(" ")}),Zs=j.define({combine:t=>t.indexOf(!0)>-1}),Cs=Ut.newName(),Ts=Ut.newName(),As=Ut.newName(),Rs={"&light":"."+Ts,"&dark":"."+As};function Xs(t,e,i){return new Ut(e,{finish:e=>/&/.test(e)?e.replace(/&\w*/,(e=>{if("&"==e)return t;if(!i||!i[e])throw new RangeError(`Unsupported selector: ${e}`);return i[e]})):t+" "+e})}const Ys=Xs("."+Cs,{"&":{position:"relative !important",boxSizing:"border-box","&.cm-focused":{outline:"1px dotted #212121"},display:"flex !important",flexDirection:"column"},".cm-scroller":{display:"flex !important",alignItems:"flex-start !important",fontFamily:"monospace",lineHeight:1.4,height:"100%",overflowX:"auto",position:"relative",zIndex:0},".cm-content":{margin:0,flexGrow:2,flexShrink:0,display:"block",whiteSpace:"pre",wordWrap:"normal",boxSizing:"border-box",minHeight:"100%",padding:"4px 0",outline:"none","&[contenteditable=true]":{WebkitUserModify:"read-write-plaintext-only"}},".cm-lineWrapping":{whiteSpace_fallback:"pre-wrap",whiteSpace:"break-spaces",wordBreak:"break-word",overflowWrap:"anywhere",flexShrink:1},"&light .cm-content":{caretColor:"black"},"&dark .cm-content":{caretColor:"white"},".cm-line":{display:"block",padding:"0 2px 0 6px"},".cm-layer":{position:"absolute",left:0,top:0,contain:"size style","& > *":{position:"absolute"}},"&light .cm-selectionBackground":{background:"#d9d9d9"},"&dark .cm-selectionBackground":{background:"#222"},"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#d7d4f0"},"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#233"},".cm-cursorLayer":{pointerEvents:"none"},"&.cm-focused > .cm-scroller > .cm-cursorLayer":{animation:"steps(1) cm-blink 1.2s infinite"},"@keyframes cm-blink":{"0%":{},"50%":{opacity:0},"100%":{}},"@keyframes cm-blink2":{"0%":{},"50%":{opacity:0},"100%":{}},".cm-cursor, .cm-dropCursor":{borderLeft:"1.2px solid black",marginLeft:"-0.6px",pointerEvents:"none"},".cm-cursor":{display:"none"},"&dark .cm-cursor":{borderLeftColor:"#444"},".cm-dropCursor":{position:"absolute"},"&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor":{display:"block"},".cm-iso":{unicodeBidi:"isolate"},".cm-announced":{position:"fixed",top:"-10000px"},"@media print":{".cm-announced":{display:"none"}},"&light .cm-activeLine":{backgroundColor:"#cceeff44"},"&dark .cm-activeLine":{backgroundColor:"#99eeff33"},"&light .cm-specialChar":{color:"red"},"&dark .cm-specialChar":{color:"#f78"},".cm-gutters":{flexShrink:0,display:"flex",height:"100%",boxSizing:"border-box",insetInlineStart:0,zIndex:200},"&light .cm-gutters":{backgroundColor:"#f5f5f5",color:"#6c6c6c",borderRight:"1px solid #ddd"},"&dark .cm-gutters":{backgroundColor:"#333338",color:"#ccc"},".cm-gutter":{display:"flex !important",flexDirection:"column",flexShrink:0,boxSizing:"border-box",minHeight:"100%",overflow:"hidden"},".cm-gutterElement":{boxSizing:"border-box"},".cm-lineNumbers .cm-gutterElement":{padding:"0 3px 0 5px",minWidth:"20px",textAlign:"right",whiteSpace:"nowrap"},"&light .cm-activeLineGutter":{backgroundColor:"#e2f2ff"},"&dark .cm-activeLineGutter":{backgroundColor:"#222227"},".cm-panels":{boxSizing:"border-box",position:"sticky",left:0,right:0},"&light .cm-panels":{backgroundColor:"#f5f5f5",color:"black"},"&light .cm-panels-top":{borderBottom:"1px solid #ddd"},"&light .cm-panels-bottom":{borderTop:"1px solid #ddd"},"&dark .cm-panels":{backgroundColor:"#333338",color:"white"},".cm-tab":{display:"inline-block",overflow:"hidden",verticalAlign:"bottom"},".cm-widgetBuffer":{verticalAlign:"text-top",height:"1em",width:0,display:"inline"},".cm-placeholder":{color:"#888",display:"inline-block",verticalAlign:"top"},".cm-highlightSpace:before":{content:"attr(data-display)",position:"absolute",pointerEvents:"none",color:"#888"},".cm-highlightTab":{backgroundImage:'url(\'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="200" height="20"><path stroke="%23888" stroke-width="1" fill="none" d="M1 10H196L190 5M190 15L196 10M197 4L197 16"/></svg>\')',backgroundSize:"auto 100%",backgroundPosition:"right 90%",backgroundRepeat:"no-repeat"},".cm-trailingSpace":{backgroundColor:"#ff332255"},".cm-button":{verticalAlign:"middle",color:"inherit",fontSize:"70%",padding:".2em 1em",borderRadius:"1px"},"&light .cm-button":{backgroundImage:"linear-gradient(#eff1f5, #d9d9df)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#b4b4b4, #d0d3d6)"}},"&dark .cm-button":{backgroundImage:"linear-gradient(#393939, #111)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#111, #333)"}},".cm-textfield":{verticalAlign:"middle",color:"inherit",fontSize:"70%",border:"1px solid silver",padding:".2em .5em"},"&light .cm-textfield":{backgroundColor:"white"},"&dark .cm-textfield":{border:"1px solid #555",backgroundColor:"inherit"}},Rs),Ws="￿";class Ms{constructor(t,e){this.points=t,this.text="",this.lineSeparator=e.facet(Qt.lineSeparator)}append(t){this.text+=t}lineBreak(){this.text+=Ws}readRange(t,e){if(!t)return this;let i=t.parentNode;for(let n=t;;){this.findPointBefore(i,n);let t=this.text.length;this.readNode(n);let s=n.nextSibling;if(s==e)break;let r=Qe.get(n),o=Qe.get(s);(r&&o?r.breakAfter:(r?r.breakAfter:Ds(n))||Ds(s)&&("BR"!=n.nodeName||n.cmIgnore)&&this.text.length>t)&&this.lineBreak(),n=s}return this.findPointBefore(i,e),this}readTextNode(t){let e=t.nodeValue;for(let i of this.points)i.node==t&&(i.pos=this.text.length+Math.min(i.offset,e.length));for(let i=0,n=this.lineSeparator?null:/\r\n?|\n/g;;){let s,r=-1,o=1;if(this.lineSeparator?(r=e.indexOf(this.lineSeparator,i),o=this.lineSeparator.length):(s=n.exec(e))&&(r=s.index,o=s[0].length),this.append(e.slice(i,r<0?e.length:r)),r<0)break;if(this.lineBreak(),o>1)for(let e of this.points)e.node==t&&e.pos>this.text.length&&(e.pos-=o-1);i=r+o}}readNode(t){if(t.cmIgnore)return;let e=Qe.get(t),i=e&&e.overrideDOMText;if(null!=i){this.findPointInside(t,i.length);for(let t=i.iter();!t.next().done;)t.lineBreak?this.lineBreak():this.append(t.value)}else 3==t.nodeType?this.readTextNode(t):"BR"==t.nodeName?t.nextSibling&&this.lineBreak():1==t.nodeType&&this.readRange(t.firstChild,null)}findPointBefore(t,e){for(let i of this.points)i.node==t&&t.childNodes[i.offset]==e&&(i.pos=this.text.length)}findPointInside(t,e){for(let i of this.points)(3==t.nodeType?i.node==t:t.contains(i.node))&&(i.pos=this.text.length+(js(t,i.node,i.offset)?e:0))}}function js(t,e,i){for(;;){if(!e||i<fe(e))return!1;if(e==t)return!0;i=he(e)+1,e=e.parentNode}}function Ds(t){return 1==t.nodeType&&/^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(t.nodeName)}class Es{constructor(t,e){this.node=t,this.offset=e,this.pos=-1}}class qs{constructor(t,e,i,n){this.typeOver=n,this.bounds=null,this.text="";let{impreciseHead:s,impreciseAnchor:r}=t.docView;if(t.state.readOnly&&e>-1)this.newSel=null;else if(e>-1&&(this.bounds=t.docView.domBoundsAround(e,i,0))){let e=s||r?[]:function(t){let e=[];if(t.root.activeElement!=t.contentDOM)return e;let{anchorNode:i,anchorOffset:n,focusNode:s,focusOffset:r}=t.observer.selectionRange;i&&(e.push(new Es(i,n)),s==i&&r==n||e.push(new Es(s,r)));return e}(t),i=new Ms(e,t.state);i.readRange(this.bounds.startDOM,this.bounds.endDOM),this.text=i.text,this.newSel=function(t,e){if(0==t.length)return null;let i=t[0].pos,n=2==t.length?t[1].pos:i;return i>-1&&n>-1?Y.single(i+e,n+e):null}(e,this.bounds.from)}else{let e=t.observer.selectionRange,i=s&&s.node==e.focusNode&&s.offset==e.focusOffset||!re(t.contentDOM,e.focusNode)?t.state.selection.main.head:t.docView.posFromDOM(e.focusNode,e.focusOffset),n=r&&r.node==e.anchorNode&&r.offset==e.anchorOffset||!re(t.contentDOM,e.anchorNode)?t.state.selection.main.anchor:t.docView.posFromDOM(e.anchorNode,e.anchorOffset),o=t.viewport;if(_e.ios&&t.state.selection.main.empty&&i!=n&&(o.from>0||o.to<t.state.doc.length)){let e=o.from-Math.min(i,n),s=o.to-Math.max(i,n);0!=e&&1!=e||0!=s&&-1!=s||(i=0,n=t.state.doc.length)}this.newSel=Y.single(n,i)}}}function _s(t,i){let n,{newSel:s}=i,r=t.state.selection.main,o=t.inputState.lastKeyTime>Date.now()-100?t.inputState.lastKeyCode:-1;if(i.bounds){let{from:s,to:a}=i.bounds,l=r.from,h=null;(8===o||_e.android&&i.text.length<a-s)&&(l=r.to,h="end");let c=function(t,e,i,n){let s=Math.min(t.length,e.length),r=0;for(;r<s&&t.charCodeAt(r)==e.charCodeAt(r);)r++;if(r==s&&t.length==e.length)return null;let o=t.length,a=e.length;for(;o>0&&a>0&&t.charCodeAt(o-1)==e.charCodeAt(a-1);)o--,a--;if("end"==n){i-=o+Math.max(0,r-Math.min(o,a))-r}if(o<r&&t.length<e.length){r-=i<=r&&i>=o?r-i:0,a=r+(a-o),o=r}else if(a<r){r-=i<=r&&i>=a?r-i:0,o=r+(o-a),a=r}return{from:r,toA:o,toB:a}}(t.state.doc.sliceString(s,a,Ws),i.text,l-s,h);c&&(_e.chrome&&13==o&&c.toB==c.from+2&&i.text.slice(c.from,c.toB)==Ws+Ws&&c.toB--,n={from:s+c.from,to:s+c.toA,insert:e.of(i.text.slice(c.from,c.toB).split(Ws))})}else s&&(!t.hasFocus&&t.state.facet(Bi)||s.main.eq(r))&&(s=null);if(!n&&!s)return!1;if(!n&&i.typeOver&&!r.empty&&s&&s.main.empty?n={from:r.from,to:r.to,insert:t.state.doc.slice(r.from,r.to)}:n&&n.from>=r.from&&n.to<=r.to&&(n.from!=r.from||n.to!=r.to)&&r.to-r.from-(n.to-n.from)<=4?n={from:r.from,to:r.to,insert:t.state.doc.slice(r.from,n.from).append(n.insert).append(t.state.doc.slice(n.to,r.to))}:(_e.mac||_e.android)&&n&&n.from==n.to&&n.from==r.head-1&&/^\. ?$/.test(n.insert.toString())&&"off"==t.contentDOM.getAttribute("autocorrect")?(s&&2==n.insert.length&&(s=Y.single(s.main.anchor-1,s.main.head-1)),n={from:r.from,to:r.to,insert:e.of([" "])}):_e.chrome&&n&&n.from==n.to&&n.from==r.head&&"\n "==n.insert.toString()&&t.lineWrapping&&(s&&(s=Y.single(s.main.anchor-1,s.main.head-1)),n={from:r.from,to:r.to,insert:e.of([" "])}),n){if(_e.ios&&t.inputState.flushIOSKey())return!0;if(_e.android&&(n.from==r.from&&n.to==r.to&&1==n.insert.length&&2==n.insert.lines&&be(t.contentDOM,"Enter",13)||(n.from==r.from-1&&n.to==r.to&&0==n.insert.length||8==o&&n.insert.length<n.to-n.from&&n.to>r.head)&&be(t.contentDOM,"Backspace",8)||n.from==r.from&&n.to==r.to+1&&0==n.insert.length&&be(t.contentDOM,"Delete",46)))return!0;let e,i=n.insert.toString();t.inputState.composing>=0&&t.inputState.composing++;let a=()=>e||(e=function(t,e,i){let n,s=t.state,r=s.selection.main;if(e.from>=r.from&&e.to<=r.to&&e.to-e.from>=(r.to-r.from)/3&&(!i||i.main.empty&&i.main.from==e.from+e.insert.length)&&t.inputState.composing<0){let i=r.from<e.from?s.sliceDoc(r.from,e.from):"",o=r.to>e.to?s.sliceDoc(e.to,r.to):"";n=s.replaceSelection(t.state.toText(i+e.insert.sliceString(0,void 0,t.state.lineBreak)+o))}else{let o=s.changes(e),a=i&&i.main.to<=o.newLength?i.main:void 0;if(s.selection.ranges.length>1&&t.inputState.composing>=0&&e.to<=r.to&&e.to>=r.to-10){let l,h=t.state.sliceDoc(e.from,e.to),c=i&&fn(t,i.main.head);if(c){let t=e.insert.length-(e.to-e.from);l={from:c.from,to:c.to-t}}else l=t.state.doc.lineAt(r.head);let f=r.to-e.to,u=r.to-r.from;n=s.changeByRange((i=>{if(i.from==r.from&&i.to==r.to)return{changes:o,range:a||i.map(o)};let n=i.to-f,c=n-h.length;if(i.to-i.from!=u||t.state.sliceDoc(c,n)!=h||i.to>=l.from&&i.from<=l.to)return{range:i};let d=s.changes({from:c,to:n,insert:e.insert}),p=i.to-r.to;return{changes:d,range:a?Y.range(Math.max(0,a.anchor+p),Math.max(0,a.head+p)):i.map(d)}}))}else n={changes:o,selection:a&&s.selection.replaceRange(a)}}let o="input.type";(t.composing||t.inputState.compositionPendingChange&&t.inputState.compositionEndedAt>Date.now()-50)&&(t.inputState.compositionPendingChange=!1,o+=".compose",t.inputState.compositionFirstChange&&(o+=".start",t.inputState.compositionFirstChange=!1));return s.update(n,{userEvent:o,scrollIntoView:!0})}(t,n,s));return t.state.facet(Di).some((e=>e(t,n.from,n.to,i,a)))||t.dispatch(a()),!0}if(s&&!s.main.eq(r)){let e=!1,i="select";return t.inputState.lastSelectionTime>Date.now()-50&&("select"==t.inputState.lastSelectionOrigin&&(e=!0),i=t.inputState.lastSelectionOrigin),t.dispatch({selection:s,scrollIntoView:e,userEvent:i}),!0}return!1}const Vs={childList:!0,characterData:!0,subtree:!0,attributes:!0,characterDataOldValue:!0},Is=_e.ie&&_e.ie_version<=11;class zs{constructor(t){this.view=t,this.active=!1,this.selectionRange=new Oe,this.selectionChanged=!1,this.delayedFlush=-1,this.resizeTimeout=-1,this.queue=[],this.delayedAndroidKey=null,this.flushingAndroidKey=-1,this.lastChange=0,this.scrollTargets=[],this.intersection=null,this.resizeScroll=null,this.intersecting=!1,this.gapIntersection=null,this.gaps=[],this.parentCheck=-1,this.dom=t.contentDOM,this.observer=new MutationObserver((e=>{for(let t of e)this.queue.push(t);(_e.ie&&_e.ie_version<=11||_e.ios&&t.composing)&&e.some((t=>"childList"==t.type&&t.removedNodes.length||"characterData"==t.type&&t.oldValue.length>t.target.nodeValue.length))?this.flushSoon():this.flush()})),Is&&(this.onCharData=t=>{this.queue.push({target:t.target,type:"characterData",oldValue:t.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.onResize=this.onResize.bind(this),this.onPrint=this.onPrint.bind(this),this.onScroll=this.onScroll.bind(this),"function"==typeof ResizeObserver&&(this.resizeScroll=new ResizeObserver((()=>{var t;(null===(t=this.view.docView)||void 0===t?void 0:t.lastUpdate)<Date.now()-75&&this.onResize()})),this.resizeScroll.observe(t.scrollDOM)),this.addWindowListeners(this.win=t.win),this.start(),"function"==typeof IntersectionObserver&&(this.intersection=new IntersectionObserver((t=>{this.parentCheck<0&&(this.parentCheck=setTimeout(this.listenForScroll.bind(this),1e3)),t.length>0&&t[t.length-1].intersectionRatio>0!=this.intersecting&&(this.intersecting=!this.intersecting,this.intersecting!=this.view.inView&&this.onScrollChanged(document.createEvent("Event")))}),{threshold:[0,.001]}),this.intersection.observe(this.dom),this.gapIntersection=new IntersectionObserver((t=>{t.length>0&&t[t.length-1].intersectionRatio>0&&this.onScrollChanged(document.createEvent("Event"))}),{})),this.listenForScroll(),this.readSelectionRange()}onScrollChanged(t){this.view.inputState.runHandlers("scroll",t),this.intersecting&&this.view.measure()}onScroll(t){this.intersecting&&this.flush(!1),this.onScrollChanged(t)}onResize(){this.resizeTimeout<0&&(this.resizeTimeout=setTimeout((()=>{this.resizeTimeout=-1,this.view.requestMeasure()}),50))}onPrint(){this.view.viewState.printing=!0,this.view.measure(),setTimeout((()=>{this.view.viewState.printing=!1,this.view.requestMeasure()}),500)}updateGaps(t){if(this.gapIntersection&&(t.length!=this.gaps.length||this.gaps.some(((e,i)=>e!=t[i])))){this.gapIntersection.disconnect();for(let e of t)this.gapIntersection.observe(e);this.gaps=t}}onSelectionChange(t){let e=this.selectionChanged;if(!this.readSelectionRange()||this.delayedAndroidKey)return;let{view:i}=this,n=this.selectionRange;if(i.state.facet(Bi)?i.root.activeElement!=this.dom:!oe(i.dom,n))return;let s=n.anchorNode&&i.docView.nearest(n.anchorNode);s&&s.ignoreEvent(t)?e||(this.selectionChanged=!1):(_e.ie&&_e.ie_version<=11||_e.android&&_e.chrome)&&!i.state.selection.main.empty&&n.focusNode&&le(n.focusNode,n.focusOffset,n.anchorNode,n.anchorOffset)?this.flushSoon():this.flush(!1)}readSelectionRange(){let{view:t}=this,e=_e.safari&&11==t.root.nodeType&&function(t){let e=t.activeElement;for(;e&&e.shadowRoot;)e=e.shadowRoot.activeElement;return e}(this.dom.ownerDocument)==this.dom&&function(t){let e=null;function i(t){t.preventDefault(),t.stopImmediatePropagation(),e=t.getTargetRanges()[0]}if(t.contentDOM.addEventListener("beforeinput",i,!0),t.dom.ownerDocument.execCommand("indent"),t.contentDOM.removeEventListener("beforeinput",i,!0),!e)return null;let n=e.startContainer,s=e.startOffset,r=e.endContainer,o=e.endOffset,a=t.docView.domAtPos(t.state.selection.main.anchor);le(a.node,a.offset,r,o)&&([n,s,r,o]=[r,o,n,s]);return{anchorNode:n,anchorOffset:s,focusNode:r,focusOffset:o}}(this.view)||se(t.root);if(!e||this.selectionRange.eq(e))return!1;let i=oe(this.dom,e);return i&&!this.selectionChanged&&t.inputState.lastFocusTime>Date.now()-200&&t.inputState.lastTouchTime<Date.now()-300&&function(t,e){let i=e.focusNode,n=e.focusOffset;if(!i||e.anchorNode!=i||e.anchorOffset!=n)return!1;for(n=Math.min(n,fe(i));;)if(n){if(1!=i.nodeType)return!1;let t=i.childNodes[n-1];"false"==t.contentEditable?n--:(i=t,n=fe(i))}else{if(i==t)return!0;n=he(i),i=i.parentNode}}(this.dom,e)?(this.view.inputState.lastFocusTime=0,t.docView.updateSelection(),!1):(this.selectionRange.setRange(e),i&&(this.selectionChanged=!0),!0)}setSelectionRange(t,e){this.selectionRange.set(t.node,t.offset,e.node,e.offset),this.selectionChanged=!1}clearSelectionRange(){this.selectionRange.set(null,0,null,0)}listenForScroll(){this.parentCheck=-1;let t=0,e=null;for(let i=this.dom;i;)if(1==i.nodeType)!e&&t<this.scrollTargets.length&&this.scrollTargets[t]==i?t++:e||(e=this.scrollTargets.slice(0,t)),e&&e.push(i),i=i.assignedSlot||i.parentNode;else{if(11!=i.nodeType)break;i=i.host}if(t<this.scrollTargets.length&&!e&&(e=this.scrollTargets.slice(0,t)),e){for(let t of this.scrollTargets)t.removeEventListener("scroll",this.onScroll);for(let t of this.scrollTargets=e)t.addEventListener("scroll",this.onScroll)}}ignore(t){if(!this.active)return t();try{return this.stop(),t()}finally{this.start(),this.clear()}}start(){this.active||(this.observer.observe(this.dom,Vs),Is&&this.dom.addEventListener("DOMCharacterDataModified",this.onCharData),this.active=!0)}stop(){this.active&&(this.active=!1,this.observer.disconnect(),Is&&this.dom.removeEventListener("DOMCharacterDataModified",this.onCharData))}clear(){this.processRecords(),this.queue.length=0,this.selectionChanged=!1}delayAndroidKey(t,e){var i;if(!this.delayedAndroidKey){let t=()=>{let t=this.delayedAndroidKey;if(t){this.clearDelayedAndroidKey(),this.view.inputState.lastKeyCode=t.keyCode,this.view.inputState.lastKeyTime=Date.now(),!this.flush()&&t.force&&be(this.dom,t.key,t.keyCode)}};this.flushingAndroidKey=this.view.win.requestAnimationFrame(t)}this.delayedAndroidKey&&"Enter"!=t||(this.delayedAndroidKey={key:t,keyCode:e,force:this.lastChange<Date.now()-50||!!(null===(i=this.delayedAndroidKey)||void 0===i?void 0:i.force)})}clearDelayedAndroidKey(){this.win.cancelAnimationFrame(this.flushingAndroidKey),this.delayedAndroidKey=null,this.flushingAndroidKey=-1}flushSoon(){this.delayedFlush<0&&(this.delayedFlush=this.view.win.requestAnimationFrame((()=>{this.delayedFlush=-1,this.flush()})))}forceFlush(){this.delayedFlush>=0&&(this.view.win.cancelAnimationFrame(this.delayedFlush),this.delayedFlush=-1),this.flush()}pendingRecords(){for(let t of this.observer.takeRecords())this.queue.push(t);return this.queue}processRecords(){let t=this.pendingRecords();t.length&&(this.queue=[]);let e=-1,i=-1,n=!1;for(let s of t){let t=this.readMutation(s);t&&(t.typeOver&&(n=!0),-1==e?({from:e,to:i}=t):(e=Math.min(t.from,e),i=Math.max(t.to,i)))}return{from:e,to:i,typeOver:n}}readChange(){let{from:t,to:e,typeOver:i}=this.processRecords(),n=this.selectionChanged&&oe(this.dom,this.selectionRange);if(t<0&&!n)return null;t>-1&&(this.lastChange=Date.now()),this.view.inputState.lastFocusTime=0,this.selectionChanged=!1;let s=new qs(this.view,t,e,i);return this.view.docView.domChanged={newSel:s.newSel?s.newSel.main:null},s}flush(t=!0){if(this.delayedFlush>=0||this.delayedAndroidKey)return!1;t&&this.readSelectionRange();let e=this.readChange();if(!e)return this.view.requestMeasure(),!1;let i=this.view.state,n=_s(this.view,e);return this.view.state==i&&this.view.update([]),n}readMutation(t){let e=this.view.docView.nearest(t.target);if(!e||e.ignoreMutation(t))return null;if(e.markDirty("attributes"==t.type),"attributes"==t.type&&(e.flags|=4),"childList"==t.type){let i=Bs(e,t.previousSibling||t.target.previousSibling,-1),n=Bs(e,t.nextSibling||t.target.nextSibling,1);return{from:i?e.posAfter(i):e.posAtStart,to:n?e.posBefore(n):e.posAtEnd,typeOver:!1}}return"characterData"==t.type?{from:e.posAtStart,to:e.posAtEnd,typeOver:t.target.nodeValue==t.oldValue}:null}setWindow(t){t!=this.win&&(this.removeWindowListeners(this.win),this.win=t,this.addWindowListeners(this.win))}addWindowListeners(t){t.addEventListener("resize",this.onResize),t.addEventListener("beforeprint",this.onPrint),t.addEventListener("scroll",this.onScroll),t.document.addEventListener("selectionchange",this.onSelectionChange)}removeWindowListeners(t){t.removeEventListener("scroll",this.onScroll),t.removeEventListener("resize",this.onResize),t.removeEventListener("beforeprint",this.onPrint),t.document.removeEventListener("selectionchange",this.onSelectionChange)}destroy(){var t,e,i;this.stop(),null===(t=this.intersection)||void 0===t||t.disconnect(),null===(e=this.gapIntersection)||void 0===e||e.disconnect(),null===(i=this.resizeScroll)||void 0===i||i.disconnect();for(let t of this.scrollTargets)t.removeEventListener("scroll",this.onScroll);this.removeWindowListeners(this.win),clearTimeout(this.parentCheck),clearTimeout(this.resizeTimeout),this.win.cancelAnimationFrame(this.delayedFlush),this.win.cancelAnimationFrame(this.flushingAndroidKey)}}function Bs(t,e,i){for(;e;){let n=Qe.get(e);if(n&&n.parent==t)return n;let s=e.parentNode;e=s!=t.dom?s:i>0?e.nextSibling:e.previousSibling}return null}class Gs{get state(){return this.viewState.state}get viewport(){return this.viewState.viewport}get visibleRanges(){return this.viewState.visibleRanges}get inView(){return this.viewState.inView}get composing(){return this.inputState.composing>0}get compositionStarted(){return this.inputState.composing>=0}get root(){return this._root}get win(){return this.dom.ownerDocument.defaultView||window}constructor(t={}){this.plugins=[],this.pluginMap=new Map,this.editorAttrs={},this.contentAttrs={},this.bidiCache=[],this.destroyed=!1,this.updateState=2,this.measureScheduled=-1,this.measureRequests=[],this.contentDOM=document.createElement("div"),this.scrollDOM=document.createElement("div"),this.scrollDOM.tabIndex=-1,this.scrollDOM.className="cm-scroller",this.scrollDOM.appendChild(this.contentDOM),this.announceDOM=document.createElement("div"),this.announceDOM.className="cm-announced",this.announceDOM.setAttribute("aria-live","polite"),this.dom=document.createElement("div"),this.dom.appendChild(this.announceDOM),this.dom.appendChild(this.scrollDOM),t.parent&&t.parent.appendChild(this.dom);let{dispatch:e}=t;this.dispatchTransactions=t.dispatchTransactions||e&&(t=>t.forEach((t=>e(t,this))))||(t=>this.update(t)),this.dispatch=this.dispatch.bind(this),this._root=t.root||function(t){for(;t;){if(t&&(9==t.nodeType||11==t.nodeType&&t.host))return t;t=t.assignedSlot||t.parentNode}return null}(t.parent)||document,this.viewState=new vs(t.state||Qt.create(t)),t.scrollTo&&t.scrollTo.is(Ii)&&(this.viewState.scrollTarget=t.scrollTo.value.clip(this.viewState.state)),this.plugins=this.state.facet(Li).map((t=>new Ui(t)));for(let t of this.plugins)t.update(this);this.observer=new zs(this),this.inputState=new Pn(this),this.inputState.ensureHandlers(this.plugins),this.docView=new hn(this),this.mountStyles(),this.updateAttrs(),this.updateState=0,this.requestMeasure()}dispatch(...t){let e=1==t.length&&t[0]instanceof pt?t:1==t.length&&Array.isArray(t[0])?t[0]:[this.state.update(...t)];this.dispatchTransactions(e,this)}update(t){if(0!=this.updateState)throw new Error("Calls to EditorView.update are not allowed while an update is in progress");let e,i=!1,n=!1,s=this.state;for(let e of t){if(e.startState!=s)throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");s=e.state}if(this.destroyed)return void(this.viewState.state=s);let r=this.hasFocus,o=0,a=null;t.some((t=>t.annotation(Fn)))?(this.inputState.notifiedFocused=r,o=1):r!=this.inputState.notifiedFocused&&(this.inputState.notifiedFocused=r,a=Jn(s,r),a||(o=1));let l=this.observer.delayedAndroidKey,h=null;if(l?(this.observer.clearDelayedAndroidKey(),h=this.observer.readChange(),(h&&!this.state.doc.eq(s.doc)||!this.state.selection.eq(s.selection))&&(h=null)):this.observer.clear(),s.facet(Qt.phrases)!=this.state.facet(Qt.phrases))return this.setState(s);e=ln.create(this,s,t),e.flags|=o;let c=this.viewState.scrollTarget;try{this.updateState=2;for(let e of t){if(c&&(c=c.map(e.changes)),e.scrollIntoView){let{main:t}=e.state.selection;c=new Vi(t.empty?t:Y.cursor(t.head,t.head>t.anchor?-1:1))}for(let t of e.effects)t.is(Ii)&&(c=t.value.clip(this.state))}this.viewState.update(e,c),this.bidiCache=Us.update(this.bidiCache,e.changes),e.empty||(this.updatePlugins(e),this.inputState.update(e)),i=this.docView.update(e),this.state.facet(on)!=this.styleModules&&this.mountStyles(),n=this.updateAttrs(),this.showAnnouncements(t),this.docView.updateSelection(i,t.some((t=>t.isUserEvent("select.pointer"))))}finally{this.updateState=0}if(e.startState.facet(Ps)!=e.state.facet(Ps)&&(this.viewState.mustMeasureContent=!0),(i||n||c||this.viewState.mustEnforceCursorAssoc||this.viewState.mustMeasureContent)&&this.requestMeasure(),!e.empty)for(let t of this.state.facet(ji))try{t(e)}catch(t){zi(this.state,t,"update listener")}(a||h)&&Promise.resolve().then((()=>{a&&this.state==a.startState&&this.dispatch(a),h&&!_s(this,h)&&l.force&&be(this.contentDOM,l.key,l.keyCode)}))}setState(t){if(0!=this.updateState)throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");if(this.destroyed)return void(this.viewState.state=t);this.updateState=2;let e=this.hasFocus;try{for(let t of this.plugins)t.destroy(this);this.viewState=new vs(t),this.plugins=t.facet(Li).map((t=>new Ui(t))),this.pluginMap.clear();for(let t of this.plugins)t.update(this);this.docView.destroy(),this.docView=new hn(this),this.inputState.ensureHandlers(this.plugins),this.mountStyles(),this.updateAttrs(),this.bidiCache=[]}finally{this.updateState=0}e&&this.focus(),this.requestMeasure()}updatePlugins(t){let e=t.startState.facet(Li),i=t.state.facet(Li);if(e!=i){let n=[];for(let s of i){let i=e.indexOf(s);if(i<0)n.push(new Ui(s));else{let e=this.plugins[i];e.mustUpdate=t,n.push(e)}}for(let e of this.plugins)e.mustUpdate!=t&&e.destroy(this);this.plugins=n,this.pluginMap.clear()}else for(let e of this.plugins)e.mustUpdate=t;for(let t=0;t<this.plugins.length;t++)this.plugins[t].update(this);e!=i&&this.inputState.ensureHandlers(this.plugins)}measure(t=!0){if(this.destroyed)return;if(this.measureScheduled>-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.observer.delayedAndroidKey)return this.measureScheduled=-1,void this.requestMeasure();this.measureScheduled=0,t&&this.observer.forceFlush();let e=null,i=this.scrollDOM,n=i.scrollTop*this.scaleY,{scrollAnchorPos:s,scrollAnchorHeight:r}=this.viewState;Math.abs(n-this.viewState.scrollTop)>1&&(r=-1),this.viewState.scrollAnchorHeight=-1;try{for(let t=0;;t++){if(r<0)if(Se(i))s=-1,r=this.viewState.heightMap.height;else{let t=this.viewState.scrollAnchorAt(n);s=t.from,r=t.top}this.updateState=1;let o=this.viewState.measure(this);if(!o&&!this.measureRequests.length&&null==this.viewState.scrollTarget)break;if(t>5){console.warn(this.measureRequests.length?"Measure loop restarted more than 5 times":"Viewport failed to stabilize");break}let a=[];4&o||([this.measureRequests,a]=[a,this.measureRequests]);let l=a.map((t=>{try{return t.read(this)}catch(t){return zi(this.state,t),Ns}})),h=ln.create(this,this.state,[]),c=!1;h.flags|=o,e?e.flags|=o:e=h,this.updateState=2,h.empty||(this.updatePlugins(h),this.inputState.update(h),this.updateAttrs(),c=this.docView.update(h));for(let t=0;t<a.length;t++)if(l[t]!=Ns)try{let e=a[t];e.write&&e.write(l[t],this)}catch(t){zi(this.state,t)}if(c&&this.docView.updateSelection(!0),!h.viewportChanged&&0==this.measureRequests.length){if(this.viewState.editorHeight){if(this.viewState.scrollTarget){this.docView.scrollIntoView(this.viewState.scrollTarget),this.viewState.scrollTarget=null,r=-1;continue}{let t=(s<0?this.viewState.heightMap.height:this.viewState.lineBlockAt(s).top)-r;if(t>1||t<-1){n+=t,i.scrollTop=n/this.scaleY,r=-1;continue}}}break}}}finally{this.updateState=0,this.measureScheduled=-1}if(e&&!e.empty)for(let t of this.state.facet(ji))t(e)}get themeClasses(){return Cs+" "+(this.state.facet(Zs)?As:Ts)+" "+this.state.facet(Ps)}updateAttrs(){let t=Hs(this,Hi,{class:"cm-editor"+(this.hasFocus?" cm-focused ":" ")+this.themeClasses}),e={spellcheck:"false",autocorrect:"off",autocapitalize:"off",translate:"no",contenteditable:this.state.facet(Bi)?"true":"false",class:"cm-content",style:`${_e.tabSize}: ${this.state.tabSize}`,role:"textbox","aria-multiline":"true"};this.state.readOnly&&(e["aria-readonly"]="true"),Hs(this,Fi,e);let i=this.observer.ignore((()=>{let i=Je(this.contentDOM,this.contentAttrs,e),n=Je(this.dom,this.editorAttrs,t);return i||n}));return this.editorAttrs=t,this.contentAttrs=e,i}showAnnouncements(t){let e=!0;for(let i of t)for(let t of i.effects)if(t.is(Gs.announce)){e&&(this.announceDOM.textContent=""),e=!1,this.announceDOM.appendChild(document.createElement("div")).textContent=t.value}}mountStyles(){this.styleModules=this.state.facet(on);let t=this.state.facet(Gs.cspNonce);Ut.mount(this.root,this.styleModules.concat(Ys).reverse(),t?{nonce:t}:void 0)}readMeasured(){if(2==this.updateState)throw new Error("Reading the editor layout isn't allowed during an update");0==this.updateState&&this.measureScheduled>-1&&this.measure(!1)}requestMeasure(t){if(this.measureScheduled<0&&(this.measureScheduled=this.win.requestAnimationFrame((()=>this.measure()))),t){if(this.measureRequests.indexOf(t)>-1)return;if(null!=t.key)for(let e=0;e<this.measureRequests.length;e++)if(this.measureRequests[e].key===t.key)return void(this.measureRequests[e]=t);this.measureRequests.push(t)}}plugin(t){let e=this.pluginMap.get(t);return(void 0===e||e&&e.spec!=t)&&this.pluginMap.set(t,e=this.plugins.find((e=>e.spec==t))||null),e&&e.update(this).value}get documentTop(){return this.contentDOM.getBoundingClientRect().top+this.viewState.paddingTop}get documentPadding(){return{top:this.viewState.paddingTop,bottom:this.viewState.paddingBottom}}get scaleX(){return this.viewState.scaleX}get scaleY(){return this.viewState.scaleY}elementAtHeight(t){return this.readMeasured(),this.viewState.elementAtHeight(t)}lineBlockAtHeight(t){return this.readMeasured(),this.viewState.lineBlockAtHeight(t)}get viewportLineBlocks(){return this.viewState.viewportLines}lineBlockAt(t){return this.viewState.lineBlockAt(t)}get contentHeight(){return this.viewState.contentHeight}moveByChar(t,e,i){return $n(this,t,kn(this,t,e,i))}moveByGroup(t,e){return $n(this,t,kn(this,t,e,(e=>function(t,e,i){let n=t.state.charCategorizer(e),s=n(i);return t=>{let e=n(t);return s==yt.Space&&(s=e),s==e}}(this,t.head,e))))}visualLineSide(t,e){let i=this.bidiSpans(t),n=this.textDirectionAt(t.from),s=i[e?i.length-1:0];return Y.cursor(s.side(e,n)+t.from,s.forward(!e,n)?1:-1)}moveToLineBoundary(t,e,i=!0){return function(t,e,i,n){let s=xn(t,e.head),r=n&&s.type==ni.Text&&(t.lineWrapping||s.widgetLineBreaks)?t.coordsAtPos(e.assoc<0&&e.head>s.from?e.head-1:e.head):null;if(r){let e=t.dom.getBoundingClientRect(),n=t.textDirectionAt(s.from),o=t.posAtCoords({x:i==(n==di.LTR)?e.right-1:e.left+1,y:(r.top+r.bottom)/2});if(null!=o)return Y.cursor(o,i?-1:1)}return Y.cursor(i?s.to:s.from,i?-1:1)}(this,t,e,i)}moveVertically(t,e,i){return $n(this,t,function(t,e,i,n){let s=e.head,r=i?1:-1;if(s==(i?t.state.doc.length:0))return Y.cursor(s,e.assoc);let o,a=e.goalColumn,l=t.contentDOM.getBoundingClientRect(),h=t.coordsAtPos(s,e.assoc||-1),c=t.documentTop;if(h)null==a&&(a=h.left-l.left),o=r<0?h.top:h.bottom;else{let e=t.viewState.lineBlockAt(s);null==a&&(a=Math.min(l.right-l.left,t.defaultCharacterWidth*(s-e.from))),o=(r<0?e.top:e.bottom)+c}let f=l.left+a,u=null!=n?n:t.viewState.heightOracle.textHeight>>1;for(let e=0;;e+=10){let i=o+(u+e)*r,n=yn(t,{x:f,y:i},!1,r);if(i<l.top||i>l.bottom||(r<0?n<s:n>s)){let e=t.docView.coordsForChar(n),s=!e||i<e.top?-1:1;return Y.cursor(n,s,void 0,a)}}}(this,t,e,i))}domAtPos(t){return this.docView.domAtPos(t)}posAtDOM(t,e=0){return this.docView.posFromDOM(t,e)}posAtCoords(t,e=!0){return this.readMeasured(),yn(this,t,e)}coordsAtPos(t,e=1){this.readMeasured();let i=this.docView.coordsAt(t,e);if(!i||i.left==i.right)return i;let n=this.state.doc.lineAt(t),s=this.bidiSpans(n);return ue(i,s[xi.find(s,t-n.from,-1,e)].dir==di.LTR==e>0)}coordsForChar(t){return this.readMeasured(),this.docView.coordsForChar(t)}get defaultCharacterWidth(){return this.viewState.heightOracle.charWidth}get defaultLineHeight(){return this.viewState.heightOracle.lineHeight}get textDirection(){return this.viewState.defaultTextDirection}textDirectionAt(t){return!this.state.facet(qi)||t<this.viewport.from||t>this.viewport.to?this.textDirection:(this.readMeasured(),this.docView.textDirectionAt(t))}get lineWrapping(){return this.viewState.heightOracle.lineWrapping}bidiSpans(t){if(t.length>Ls)return Ci(t.length);let e,i=this.textDirectionAt(t.from);for(let n of this.bidiCache)if(n.from==t.from&&n.dir==i&&(n.fresh||ki(n.isolates,e=nn(this,t))))return n.order;e||(e=nn(this,t));let n=Zi(t.text,i,e);return this.bidiCache.push(new Us(t.from,t.to,i,e,!0,n)),n}get hasFocus(){var t;return(this.dom.ownerDocument.hasFocus()||_e.safari&&(null===(t=this.inputState)||void 0===t?void 0:t.lastContextMenu)>Date.now()-3e4)&&this.root.activeElement==this.contentDOM}focus(){this.observer.ignore((()=>{we(this.contentDOM),this.docView.updateSelection()}))}setRoot(t){this._root!=t&&(this._root=t,this.observer.setWindow((9==t.nodeType?t:t.ownerDocument).defaultView||window),this.mountStyles())}destroy(){for(let t of this.plugins)t.destroy(this);this.plugins=[],this.inputState.destroy(),this.docView.destroy(),this.dom.remove(),this.observer.destroy(),this.measureScheduled>-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.destroyed=!0}static scrollIntoView(t,e={}){return Ii.of(new Vi("number"==typeof t?Y.cursor(t):t,e.y,e.x,e.yMargin,e.xMargin))}scrollSnapshot(){let{scrollTop:t,scrollLeft:e}=this.scrollDOM,i=this.viewState.scrollAnchorAt(t);return Ii.of(new Vi(Y.cursor(i.from),"start","start",i.top-t,e,!0))}static domEventHandlers(t){return Ni.define((()=>({})),{eventHandlers:t})}static domEventObservers(t){return Ni.define((()=>({})),{eventObservers:t})}static theme(t,e){let i=Ut.newName(),n=[Ps.of(i),on.of(Xs(`.${i}`,t))];return e&&e.dark&&n.push(Zs.of(!0)),n}static baseTheme(t){return H.lowest(on.of(Xs("."+Cs,t,Rs)))}static findFromDOM(t){var e;let i=t.querySelector(".cm-content"),n=i&&Qe.get(i)||Qe.get(t);return(null===(e=null==n?void 0:n.rootView)||void 0===e?void 0:e.view)||null}}Gs.styleModule=on,Gs.inputHandler=Di,Gs.focusChangeEffect=Ei,Gs.perLineTextDirection=qi,Gs.exceptionSink=Mi,Gs.updateListener=ji,Gs.editable=Bi,Gs.mouseSelectionStyle=Wi,Gs.dragMovesSelection=Yi,Gs.clickAddsSelectionRange=Xi,Gs.decorations=Ji,Gs.outerDecorations=Ki,Gs.atomicRanges=tn,Gs.bidiIsolatedRanges=en,Gs.scrollMargins=sn,Gs.darkTheme=Zs,Gs.cspNonce=j.define({combine:t=>t.length?t[0]:""}),Gs.contentAttributes=Fi,Gs.editorAttributes=Hi,Gs.lineWrapping=Gs.contentAttributes.of({class:"cm-lineWrapping"}),Gs.announce=dt.define();const Ls=4096,Ns={};class Us{constructor(t,e,i,n,s,r){this.from=t,this.to=e,this.dir=i,this.isolates=n,this.fresh=s,this.order=r}static update(t,e){if(e.empty&&!t.some((t=>t.fresh)))return t;let i=[],n=t.length?t[t.length-1].dir:di.LTR;for(let s=Math.max(0,t.length-10);s<t.length;s++){let r=t[s];r.dir!=n||e.touchesRange(r.from,r.to)||i.push(new Us(e.mapPos(r.from,1),e.mapPos(r.to,-1),r.dir,r.isolates,!1,r.order))}return i}}function Hs(t,e,i){for(let n=t.state.facet(e),s=n.length-1;s>=0;s--){let e=n[s],r="function"==typeof e?e(t):e;r&&Ue(r,i)}return i}const Fs=_e.mac?"mac":_e.windows?"win":_e.linux?"linux":"key";function Js(t,e,i){return e.altKey&&(t="Alt-"+t),e.ctrlKey&&(t="Ctrl-"+t),e.metaKey&&(t="Meta-"+t),!1!==i&&e.shiftKey&&(t="Shift-"+t),t}const Ks=H.default(Gs.domEventHandlers({keydown:(t,e)=>or(ir(e.state),t,e,"editor")})),tr=j.define({enables:Ks}),er=new WeakMap;function ir(t){let e=t.facet(tr),i=er.get(e);return i||er.set(e,i=function(t,e=Fs){let i=Object.create(null),n=Object.create(null),s=(t,e)=>{let i=n[t];if(null==i)n[t]=e;else if(i!=e)throw new Error("Key binding "+t+" is used both as a regular binding and as a multi-stroke prefix")},r=(t,n,r,o,a)=>{var l,h;let c=i[t]||(i[t]=Object.create(null)),f=n.split(/ (?!$)/).map((t=>function(t,e){const i=t.split(/-(?!$)/);let n,s,r,o,a=i[i.length-1];"Space"==a&&(a=" ");for(let t=0;t<i.length-1;++t){const a=i[t];if(/^(cmd|meta|m)$/i.test(a))o=!0;else if(/^a(lt)?$/i.test(a))n=!0;else if(/^(c|ctrl|control)$/i.test(a))s=!0;else if(/^s(hift)?$/i.test(a))r=!0;else{if(!/^mod$/i.test(a))throw new Error("Unrecognized modifier name: "+a);"mac"==e?o=!0:s=!0}}return n&&(a="Alt-"+a),s&&(a="Ctrl-"+a),o&&(a="Meta-"+a),r&&(a="Shift-"+a),a}(t,e)));for(let e=1;e<f.length;e++){let i=f.slice(0,e).join(" ");s(i,!0),c[i]||(c[i]={preventDefault:!0,stopPropagation:!1,run:[e=>{let n=sr={view:e,prefix:i,scope:t};return setTimeout((()=>{sr==n&&(sr=null)}),rr),!0}]})}let u=f.join(" ");s(u,!1);let d=c[u]||(c[u]={preventDefault:!1,stopPropagation:!1,run:(null===(h=null===(l=c._any)||void 0===l?void 0:l.run)||void 0===h?void 0:h.slice())||[]});r&&d.run.push(r),o&&(d.preventDefault=!0),a&&(d.stopPropagation=!0)};for(let n of t){let t=n.scope?n.scope.split(" "):["editor"];if(n.any)for(let e of t){let t=i[e]||(i[e]=Object.create(null));t._any||(t._any={preventDefault:!1,stopPropagation:!1,run:[]});for(let e in t)t[e].run.push(n.any)}let s=n[e]||n.key;if(s)for(let e of t)r(e,s,n.run,n.preventDefault,n.stopPropagation),n.shift&&r(e,"Shift-"+s,n.shift,n.preventDefault,n.stopPropagation)}return i}(e.reduce(((t,e)=>t.concat(e)),[]))),i}function nr(t,e,i){return or(ir(t.state),e,t,i)}let sr=null;const rr=4e3;function or(t,e,i,n){let s=function(t){var e=!(te&&t.metaKey&&t.shiftKey&&!t.ctrlKey&&!t.altKey||ee&&t.shiftKey&&t.key&&1==t.key.length||"Unidentified"==t.key)&&t.key||(t.shiftKey?Kt:Jt)[t.keyCode]||t.key||"Unidentified";return"Esc"==e&&(e="Escape"),"Del"==e&&(e="Delete"),"Left"==e&&(e="ArrowLeft"),"Up"==e&&(e="ArrowUp"),"Right"==e&&(e="ArrowRight"),"Down"==e&&(e="ArrowDown"),e}(e),r=S(b(s,0))==s.length&&" "!=s,o="",a=!1,l=!1,h=!1;sr&&sr.view==i&&sr.scope==n&&(o=sr.prefix+" ",Rn.indexOf(e.keyCode)<0&&(l=!0,sr=null));let c,f,u=new Set,d=t=>{if(t){for(let n of t.run)if(!u.has(n)&&(u.add(n),n(i,e)))return t.stopPropagation&&(h=!0),!0;t.preventDefault&&(t.stopPropagation&&(h=!0),l=!0)}return!1},p=t[n];return p&&(d(p[o+Js(s,e,!r)])?a=!0:r&&(e.altKey||e.metaKey||e.ctrlKey)&&!(_e.windows&&e.ctrlKey&&e.altKey)&&(c=Jt[e.keyCode])&&c!=s?(d(p[o+Js(c,e,!0)])||e.shiftKey&&(f=Kt[e.keyCode])!=s&&f!=c&&d(p[o+Js(f,e,!1)]))&&(a=!0):r&&e.shiftKey&&d(p[o+Js(s,e,!0)])&&(a=!0),!a&&d(p._any)&&(a=!0)),l&&(a=!0),a&&h&&e.stopPropagation(),a}class ar{constructor(t,e,i,n,s){this.className=t,this.left=e,this.top=i,this.width=n,this.height=s}draw(){let t=document.createElement("div");return t.className=this.className,this.adjust(t),t}update(t,e){return e.className==this.className&&(this.adjust(t),!0)}adjust(t){t.style.left=this.left+"px",t.style.top=this.top+"px",null!=this.width&&(t.style.width=this.width+"px"),t.style.height=this.height+"px"}eq(t){return this.left==t.left&&this.top==t.top&&this.width==t.width&&this.height==t.height&&this.className==t.className}static forRange(t,e,i){if(i.empty){let n=t.coordsAtPos(i.head,i.assoc||1);if(!n)return[];let s=lr(t);return[new ar(e,n.left-s.left,n.top-s.top,null,n.bottom-n.top)]}return function(t,e,i){if(i.to<=t.viewport.from||i.from>=t.viewport.to)return[];let n=Math.max(i.from,t.viewport.from),s=Math.min(i.to,t.viewport.to),r=t.textDirection==di.LTR,o=t.contentDOM,a=o.getBoundingClientRect(),l=lr(t),h=o.querySelector(".cm-line"),c=h&&window.getComputedStyle(h),f=a.left+(c?parseInt(c.paddingLeft)+Math.min(0,parseInt(c.textIndent)):0),u=a.right-(c?parseInt(c.paddingRight):0),d=xn(t,n),p=xn(t,s),O=d.type==ni.Text?d:null,g=p.type==ni.Text?p:null;O&&(t.lineWrapping||d.widgetLineBreaks)&&(O=hr(t,n,O));g&&(t.lineWrapping||p.widgetLineBreaks)&&(g=hr(t,s,g));if(O&&g&&O.from==g.from)return w(v(i.from,i.to,O));{let e=O?v(i.from,null,O):b(d,!1),n=g?v(null,i.to,g):b(p,!0),s=[];return(O||d).to<(g||p).from-(O&&g?1:0)||d.widgetLineBreaks>1&&e.bottom+t.defaultLineHeight/2<n.top?s.push(m(f,e.bottom,u,n.top)):e.bottom<n.top&&t.elementAtHeight((e.bottom+n.top)/2).type==ni.Text&&(e.bottom=n.top=(e.bottom+n.top)/2),w(e).concat(s).concat(w(n))}function m(t,i,n,s){return new ar(e,t-l.left,i-l.top-.01,n-t,s-i+.01)}function w({top:t,bottom:e,horizontal:i}){let n=[];for(let s=0;s<i.length;s+=2)n.push(m(i[s],t,i[s+1],e));return n}function v(e,i,n){let s=1e9,o=-1e9,a=[];function l(e,i,l,h,c){let d=t.coordsAtPos(e,e==n.to?-2:2),p=t.coordsAtPos(l,l==n.from?2:-2);d&&p&&(s=Math.min(d.top,p.top,s),o=Math.max(d.bottom,p.bottom,o),c==di.LTR?a.push(r&&i?f:d.left,r&&h?u:p.right):a.push(!r&&h?f:p.left,!r&&i?u:d.right))}let h=null!=e?e:n.from,c=null!=i?i:n.to;for(let n of t.visibleRanges)if(n.to>h&&n.from<c)for(let s=Math.max(n.from,h),r=Math.min(n.to,c);;){let n=t.state.doc.lineAt(s);for(let o of t.bidiSpans(n)){let t=o.from+n.from,a=o.to+n.from;if(t>=r)break;a>s&&l(Math.max(t,s),null==e&&t<=h,Math.min(a,r),null==i&&a>=c,o.dir)}if(s=n.to+1,s>=r)break}return 0==a.length&&l(h,null==e,c,null==i,t.textDirection),{top:s,bottom:o,horizontal:a}}function b(t,e){let i=a.top+(e?t.top:t.bottom);return{top:i,bottom:i,horizontal:[]}}}(t,e,i)}}function lr(t){let e=t.scrollDOM.getBoundingClientRect();return{left:(t.textDirection==di.LTR?e.left:e.right-t.scrollDOM.clientWidth*t.scaleX)-t.scrollDOM.scrollLeft*t.scaleX,top:e.top-t.scrollDOM.scrollTop*t.scaleY}}function hr(t,e,i){let n=Y.cursor(e);return{from:Math.max(i.from,t.moveToLineBoundary(n,!1,!0).from),to:Math.min(i.to,t.moveToLineBoundary(n,!0,!0).from),type:ni.Text}}class cr{constructor(t,e){this.view=t,this.layer=e,this.drawn=[],this.scaleX=1,this.scaleY=1,this.measureReq={read:this.measure.bind(this),write:this.draw.bind(this)},this.dom=t.scrollDOM.appendChild(document.createElement("div")),this.dom.classList.add("cm-layer"),e.above&&this.dom.classList.add("cm-layer-above"),e.class&&this.dom.classList.add(e.class),this.scale(),this.dom.setAttribute("aria-hidden","true"),this.setOrder(t.state),t.requestMeasure(this.measureReq),e.mount&&e.mount(this.dom,t)}update(t){t.startState.facet(fr)!=t.state.facet(fr)&&this.setOrder(t.state),(this.layer.update(t,this.dom)||t.geometryChanged)&&(this.scale(),t.view.requestMeasure(this.measureReq))}setOrder(t){let e=0,i=t.facet(fr);for(;e<i.length&&i[e]!=this.layer;)e++;this.dom.style.zIndex=String((this.layer.above?150:-1)-e)}measure(){return this.layer.markers(this.view)}scale(){let{scaleX:t,scaleY:e}=this.view;t==this.scaleX&&e==this.scaleY||(this.scaleX=t,this.scaleY=e,this.dom.style.transform=`scale(${1/t}, ${1/e})`)}draw(t){if(t.length!=this.drawn.length||t.some(((t,e)=>{return i=t,n=this.drawn[e],!(i.constructor==n.constructor&&i.eq(n));var i,n}))){let e=this.dom.firstChild,i=0;for(let n of t)n.update&&e&&n.constructor&&this.drawn[i].constructor&&n.update(e,this.drawn[i])?(e=e.nextSibling,i++):this.dom.insertBefore(n.draw(),e);for(;e;){let t=e.nextSibling;e.remove(),e=t}this.drawn=t}}destroy(){this.layer.destroy&&this.layer.destroy(this.dom,this.view),this.dom.remove()}}const fr=j.define();function ur(t){return[Ni.define((e=>new cr(e,t))),fr.of(t)]}const dr=!_e.ios,pr=j.define({combine:t=>$t(t,{cursorBlinkRate:1200,drawRangeCursor:!0},{cursorBlinkRate:(t,e)=>Math.min(t,e),drawRangeCursor:(t,e)=>t||e})});function Or(t={}){return[pr.of(t),mr,vr,yr,_i.of(!0)]}function gr(t){return t.startState.facet(pr)!=t.state.facet(pr)}const mr=ur({above:!0,markers(t){let{state:e}=t,i=e.facet(pr),n=[];for(let s of e.selection.ranges){let r=s==e.selection.main;if(s.empty?!r||dr:i.drawRangeCursor){let e=r?"cm-cursor cm-cursor-primary":"cm-cursor cm-cursor-secondary",i=s.empty?s:Y.cursor(s.head,s.head>s.anchor?-1:1);for(let s of ar.forRange(t,e,i))n.push(s)}}return n},update(t,e){t.transactions.some((t=>t.selection))&&(e.style.animationName="cm-blink"==e.style.animationName?"cm-blink2":"cm-blink");let i=gr(t);return i&&wr(t.state,e),t.docChanged||t.selectionSet||i},mount(t,e){wr(e.state,t)},class:"cm-cursorLayer"});function wr(t,e){e.style.animationDuration=t.facet(pr).cursorBlinkRate+"ms"}const vr=ur({above:!1,markers:t=>t.state.selection.ranges.map((e=>e.empty?[]:ar.forRange(t,"cm-selectionBackground",e))).reduce(((t,e)=>t.concat(e))),update:(t,e)=>t.docChanged||t.selectionSet||t.viewportChanged||gr(t),class:"cm-selectionLayer"}),br={".cm-line":{"& ::selection":{backgroundColor:"transparent !important"},"&::selection":{backgroundColor:"transparent !important"}}};dr&&(br[".cm-line"].caretColor="transparent !important",br[".cm-content"]={caretColor:"transparent !important"});const yr=H.highest(Gs.theme(br)),Sr=dt.define({map:(t,e)=>null==t?null:e.mapPos(t)}),xr=z.define({create:()=>null,update:(t,e)=>(null!=t&&(t=e.changes.mapPos(t)),e.effects.reduce(((t,e)=>e.is(Sr)?e.value:t),t))}),kr=Ni.fromClass(class{constructor(t){this.view=t,this.cursor=null,this.measureReq={read:this.readPos.bind(this),write:this.drawCursor.bind(this)}}update(t){var e;let i=t.state.field(xr);null==i?null!=this.cursor&&(null===(e=this.cursor)||void 0===e||e.remove(),this.cursor=null):(this.cursor||(this.cursor=this.view.scrollDOM.appendChild(document.createElement("div")),this.cursor.className="cm-dropCursor"),(t.startState.field(xr)!=i||t.docChanged||t.geometryChanged)&&this.view.requestMeasure(this.measureReq))}readPos(){let{view:t}=this,e=t.state.field(xr),i=null!=e&&t.coordsAtPos(e);if(!i)return null;let n=t.scrollDOM.getBoundingClientRect();return{left:i.left-n.left+t.scrollDOM.scrollLeft*t.scaleX,top:i.top-n.top+t.scrollDOM.scrollTop*t.scaleY,height:i.bottom-i.top}}drawCursor(t){if(this.cursor){let{scaleX:e,scaleY:i}=this.view;t?(this.cursor.style.left=t.left/e+"px",this.cursor.style.top=t.top/i+"px",this.cursor.style.height=t.height/i+"px"):this.cursor.style.left="-100000px"}}destroy(){this.cursor&&this.cursor.remove()}setDropPos(t){this.view.state.field(xr)!=t&&this.view.dispatch({effects:Sr.of(t)})}},{eventObservers:{dragover(t){this.setDropPos(this.view.posAtCoords({x:t.clientX,y:t.clientY}))},dragleave(t){t.target!=this.view.contentDOM&&this.view.contentDOM.contains(t.relatedTarget)||this.setDropPos(null)},dragend(){this.setDropPos(null)},drop(){this.setDropPos(null)}}});function Qr(){return[xr,kr]}function $r(t,e,i,n,s){e.lastIndex=0;for(let r,o=t.iterRange(i,n),a=i;!o.next().done;a+=o.value.length)if(!o.lineBreak)for(;r=e.exec(o.value);)s(a+r.index,r)}class Pr{constructor(t){const{regexp:e,decoration:i,decorate:n,boundary:s,maxLength:r=1e3}=t;if(!e.global)throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set");if(this.regexp=e,n)this.addMatch=(t,e,i,s)=>n(s,i,i+t[0].length,t,e);else if("function"==typeof i)this.addMatch=(t,e,n,s)=>{let r=i(t,e,n);r&&s(n,n+t[0].length,r)};else{if(!i)throw new RangeError("Either 'decorate' or 'decoration' should be provided to MatchDecorator");this.addMatch=(t,e,n,s)=>s(n,n+t[0].length,i)}this.boundary=s,this.maxLength=r}createDeco(t){let e=new Rt,i=e.add.bind(e);for(let{from:e,to:n}of function(t,e){let i=t.visibleRanges;if(1==i.length&&i[0].from==t.viewport.from&&i[0].to==t.viewport.to)return i;let n=[];for(let{from:s,to:r}of i)s=Math.max(t.state.doc.lineAt(s).from,s-e),r=Math.min(t.state.doc.lineAt(r).to,r+e),n.length&&n[n.length-1].to>=s?n[n.length-1].to=r:n.push({from:s,to:r});return n}(t,this.maxLength))$r(t.state.doc,this.regexp,e,n,((e,n)=>this.addMatch(n,t,e,i)));return e.finish()}updateDeco(t,e){let i=1e9,n=-1;return t.docChanged&&t.changes.iterChanges(((e,s,r,o)=>{o>t.view.viewport.from&&r<t.view.viewport.to&&(i=Math.min(r,i),n=Math.max(o,n))})),t.viewportChanged||n-i>1e3?this.createDeco(t.view):n>-1?this.updateRange(t.view,e.map(t.changes),i,n):e}updateRange(t,e,i,n){for(let s of t.visibleRanges){let r=Math.max(s.from,i),o=Math.min(s.to,n);if(o>r){let i=t.state.doc.lineAt(r),n=i.to<o?t.state.doc.lineAt(o):i,a=Math.max(s.from,i.from),l=Math.min(s.to,n.to);if(this.boundary){for(;r>i.from;r--)if(this.boundary.test(i.text[r-1-i.from])){a=r;break}for(;o<n.to;o++)if(this.boundary.test(n.text[o-n.from])){l=o;break}}let h,c=[],f=(t,e,i)=>c.push(i.range(t,e));if(i==n)for(this.regexp.lastIndex=a-i.from;(h=this.regexp.exec(i.text))&&h.index<l-i.from;)this.addMatch(h,t,h.index+i.from,f);else $r(t.state.doc,this.regexp,a,l,((e,i)=>this.addMatch(i,t,e,f)));e=e.update({filterFrom:a,filterTo:l,filter:(t,e)=>t<a||e>l,add:c})}}return e}}const Zr=null!=/x/.unicode?"gu":"g",Cr=new RegExp("[\0-\b\n--Ÿ­؜​‎‏\u2028\u2029‭‮⁦⁧⁩\ufeff-]",Zr),Tr={0:"null",7:"bell",8:"backspace",10:"newline",11:"vertical tab",13:"carriage return",27:"escape",8203:"zero width space",8204:"zero width non-joiner",8205:"zero width joiner",8206:"left-to-right mark",8207:"right-to-left mark",8232:"line separator",8237:"left-to-right override",8238:"right-to-left override",8294:"left-to-right isolate",8295:"right-to-left isolate",8297:"pop directional isolate",8233:"paragraph separator",65279:"zero width no-break space",65532:"object replacement"};let Ar=null;const Rr=j.define({combine(t){let e=$t(t,{render:null,specialChars:Cr,addSpecialChars:null});return(e.replaceTabs=!function(){var t;if(null==Ar&&"undefined"!=typeof document&&document.body){let e=document.body.style;Ar=null!=(null!==(t=e.tabSize)&&void 0!==t?t:e.MozTabSize)}return Ar||!1}())&&(e.specialChars=new RegExp("\t|"+e.specialChars.source,Zr)),e.addSpecialChars&&(e.specialChars=new RegExp(e.specialChars.source+"|"+e.addSpecialChars.source,Zr)),e}});function Xr(t={}){return[Rr.of(t),Yr||(Yr=Ni.fromClass(class{constructor(t){this.view=t,this.decorations=si.none,this.decorationCache=Object.create(null),this.decorator=this.makeDecorator(t.state.facet(Rr)),this.decorations=this.decorator.createDeco(t)}makeDecorator(t){return new Pr({regexp:t.specialChars,decoration:(e,i,n)=>{let{doc:s}=i.state,r=b(e[0],0);if(9==r){let t=s.lineAt(n),e=i.state.tabSize,r=It(t.text,e,n-t.from);return si.replace({widget:new Mr((e-r%e)*this.view.defaultCharacterWidth/this.view.scaleX)})}return this.decorationCache[r]||(this.decorationCache[r]=si.replace({widget:new Wr(t,r)}))},boundary:t.replaceTabs?void 0:/[^]/})}update(t){let e=t.state.facet(Rr);t.startState.facet(Rr)!=e?(this.decorator=this.makeDecorator(e),this.decorations=this.decorator.createDeco(t.view)):this.decorations=this.decorator.updateDeco(t,this.decorations)}},{decorations:t=>t.decorations}))]}let Yr=null;class Wr extends ii{constructor(t,e){super(),this.options=t,this.code=e}eq(t){return t.code==this.code}toDOM(t){let e=function(t){return t>=32?"•":10==t?"␤":String.fromCharCode(9216+t)}(this.code),i=t.state.phrase("Control character")+" "+(Tr[this.code]||"0x"+this.code.toString(16)),n=this.options.render&&this.options.render(this.code,i,e);if(n)return n;let s=document.createElement("span");return s.textContent=e,s.title=i,s.setAttribute("aria-label",i),s.className="cm-specialChar",s}ignoreEvent(){return!1}}class Mr extends ii{constructor(t){super(),this.width=t}eq(t){return t.width==this.width}toDOM(){let t=document.createElement("span");return t.textContent="\t",t.className="cm-tab",t.style.width=this.width+"px",t}ignoreEvent(){return!1}}const jr=Ni.fromClass(class{constructor(){this.height=1e3,this.attrs={style:"padding-bottom: 1000px"}}update(t){let{view:e}=t,i=e.viewState.editorHeight*e.scaleY-e.defaultLineHeight-e.documentPadding.top-.5;i>=0&&i!=this.height&&(this.height=i,this.attrs={style:`padding-bottom: ${i}px`})}});function Dr(){return qr}const Er=si.line({class:"cm-activeLine"}),qr=Ni.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.docChanged||t.selectionSet)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=-1,i=[];for(let n of t.state.selection.ranges){let s=t.lineBlockAt(n.head);s.from>e&&(i.push(Er.range(s.from)),e=s.from)}return si.set(i)}},{decorations:t=>t.decorations});class _r extends ii{constructor(t){super(),this.content=t}toDOM(){let t=document.createElement("span");return t.className="cm-placeholder",t.style.pointerEvents="none",t.appendChild("string"==typeof this.content?document.createTextNode(this.content):this.content),"string"==typeof this.content?t.setAttribute("aria-label","placeholder "+this.content):t.setAttribute("aria-hidden","true"),t}coordsAt(t){let e=t.firstChild?ae(t.firstChild):[];if(!e.length)return null;let i=window.getComputedStyle(t.parentNode),n=ue(e[0],"rtl"!=i.direction),s=parseInt(i.lineHeight);return n.bottom-n.top>1.5*s?{left:n.left,right:n.right,top:n.top,bottom:n.top+s}:n}ignoreEvent(){return!1}}const Vr=2e3;function Ir(t,e){let i=t.posAtCoords({x:e.clientX,y:e.clientY},!1),n=t.state.doc.lineAt(i),s=i-n.from,r=s>Vr?-1:s==n.length?function(t,e){let i=t.coordsAtPos(t.viewport.from);return i?Math.round(Math.abs((i.left-e)/t.defaultCharacterWidth)):-1}(t,e.clientX):It(n.text,t.state.tabSize,i-n.from);return{line:n.number,col:r,off:s}}function zr(t,e){let i=Ir(t,e),n=t.state.selection;return i?{update(t){if(t.docChanged){let e=t.changes.mapPos(t.startState.doc.line(i.line).from),s=t.state.doc.lineAt(e);i={line:s.number,col:i.col,off:Math.min(i.off,s.length)},n=n.map(t.changes)}},get(e,s,r){let o=Ir(t,e);if(!o)return n;let a=function(t,e,i){let n=Math.min(e.line,i.line),s=Math.max(e.line,i.line),r=[];if(e.off>Vr||i.off>Vr||e.col<0||i.col<0){let o=Math.min(e.off,i.off),a=Math.max(e.off,i.off);for(let e=n;e<=s;e++){let i=t.doc.line(e);i.length<=a&&r.push(Y.range(i.from+o,i.to+a))}}else{let o=Math.min(e.col,i.col),a=Math.max(e.col,i.col);for(let e=n;e<=s;e++){let i=t.doc.line(e),n=zt(i.text,o,t.tabSize,!0);if(n<0)r.push(Y.cursor(i.to));else{let e=zt(i.text,a,t.tabSize);r.push(Y.range(i.from+n,i.from+e))}}}return r}(t.state,i,o);return a.length?r?Y.create(a.concat(n.ranges)):Y.create(a):n}}:null}function Br(t){let e=(null==t?void 0:t.eventFilter)||(t=>t.altKey&&0==t.button);return Gs.mouseSelectionStyle.of(((t,i)=>e(i)?zr(t,i):null))}const Gr={Alt:[18,t=>!!t.altKey],Control:[17,t=>!!t.ctrlKey],Shift:[16,t=>!!t.shiftKey],Meta:[91,t=>!!t.metaKey]},Lr={style:"cursor: crosshair"};function Nr(t={}){let[e,i]=Gr[t.key||"Alt"],n=Ni.fromClass(class{constructor(t){this.view=t,this.isDown=!1}set(t){this.isDown!=t&&(this.isDown=t,this.view.update([]))}},{eventObservers:{keydown(t){this.set(t.keyCode==e||i(t))},keyup(t){t.keyCode!=e&&i(t)||this.set(!1)},mousemove(t){this.set(i(t))}}});return[n,Gs.contentAttributes.of((t=>{var e;return(null===(e=t.plugin(n))||void 0===e?void 0:e.isDown)?Lr:null}))]}const Ur="-10000px";class Hr{constructor(t,e,i,n){this.facet=e,this.createTooltipView=i,this.removeTooltipView=n,this.input=t.state.facet(e),this.tooltips=this.input.filter((t=>t)),this.tooltipViews=this.tooltips.map(i)}update(t,e){var i;let n=t.state.facet(this.facet),s=n.filter((t=>t));if(n===this.input){for(let e of this.tooltipViews)e.update&&e.update(t);return!1}let r=[],o=e?[]:null;for(let i=0;i<s.length;i++){let n=s[i],a=-1;if(n){for(let t=0;t<this.tooltips.length;t++){let e=this.tooltips[t];e&&e.create==n.create&&(a=t)}if(a<0)r[i]=this.createTooltipView(n),o&&(o[i]=!!n.above);else{let n=r[i]=this.tooltipViews[a];o&&(o[i]=e[a]),n.update&&n.update(t)}}}for(let t of this.tooltipViews)r.indexOf(t)<0&&(this.removeTooltipView(t),null===(i=t.destroy)||void 0===i||i.call(t));return e&&(o.forEach(((t,i)=>e[i]=t)),e.length=o.length),this.input=n,this.tooltips=s,this.tooltipViews=r,!0}}function Fr(t){let{win:e}=t;return{top:0,left:0,bottom:e.innerHeight,right:e.innerWidth}}const Jr=j.define({combine:t=>{var e,i,n;return{position:_e.ios?"absolute":(null===(e=t.find((t=>t.position)))||void 0===e?void 0:e.position)||"fixed",parent:(null===(i=t.find((t=>t.parent)))||void 0===i?void 0:i.parent)||null,tooltipSpace:(null===(n=t.find((t=>t.tooltipSpace)))||void 0===n?void 0:n.tooltipSpace)||Fr}}}),Kr=new WeakMap,to=Ni.fromClass(class{constructor(t){this.view=t,this.above=[],this.inView=!0,this.madeAbsolute=!1,this.lastTransaction=0,this.measureTimeout=-1;let e=t.state.facet(Jr);this.position=e.position,this.parent=e.parent,this.classes=t.themeClasses,this.createContainer(),this.measureReq={read:this.readMeasure.bind(this),write:this.writeMeasure.bind(this),key:this},this.resizeObserver="function"==typeof ResizeObserver?new ResizeObserver((()=>this.measureSoon())):null,this.manager=new Hr(t,no,(t=>this.createTooltip(t)),(t=>{this.resizeObserver&&this.resizeObserver.unobserve(t.dom),t.dom.remove()})),this.above=this.manager.tooltips.map((t=>!!t.above)),this.intersectionObserver="function"==typeof IntersectionObserver?new IntersectionObserver((t=>{Date.now()>this.lastTransaction-50&&t.length>0&&t[t.length-1].intersectionRatio<1&&this.measureSoon()}),{threshold:[1]}):null,this.observeIntersection(),t.win.addEventListener("resize",this.measureSoon=this.measureSoon.bind(this)),this.maybeMeasure()}createContainer(){this.parent?(this.container=document.createElement("div"),this.container.style.position="relative",this.container.className=this.view.themeClasses,this.parent.appendChild(this.container)):this.container=this.view.dom}observeIntersection(){if(this.intersectionObserver){this.intersectionObserver.disconnect();for(let t of this.manager.tooltipViews)this.intersectionObserver.observe(t.dom)}}measureSoon(){this.measureTimeout<0&&(this.measureTimeout=setTimeout((()=>{this.measureTimeout=-1,this.maybeMeasure()}),50))}update(t){t.transactions.length&&(this.lastTransaction=Date.now());let e=this.manager.update(t,this.above);e&&this.observeIntersection();let i=e||t.geometryChanged,n=t.state.facet(Jr);if(n.position!=this.position&&!this.madeAbsolute){this.position=n.position;for(let t of this.manager.tooltipViews)t.dom.style.position=this.position;i=!0}if(n.parent!=this.parent){this.parent&&this.container.remove(),this.parent=n.parent,this.createContainer();for(let t of this.manager.tooltipViews)this.container.appendChild(t.dom);i=!0}else this.parent&&this.view.themeClasses!=this.classes&&(this.classes=this.container.className=this.view.themeClasses);i&&this.maybeMeasure()}createTooltip(t){let e=t.create(this.view);if(e.dom.classList.add("cm-tooltip"),t.arrow&&!e.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")){let t=document.createElement("div");t.className="cm-tooltip-arrow",e.dom.appendChild(t)}return e.dom.style.position=this.position,e.dom.style.top=Ur,e.dom.style.left="0px",this.container.appendChild(e.dom),e.mount&&e.mount(this.view),this.resizeObserver&&this.resizeObserver.observe(e.dom),e}destroy(){var t,e,i;this.view.win.removeEventListener("resize",this.measureSoon);for(let e of this.manager.tooltipViews)e.dom.remove(),null===(t=e.destroy)||void 0===t||t.call(e);this.parent&&this.container.remove(),null===(e=this.resizeObserver)||void 0===e||e.disconnect(),null===(i=this.intersectionObserver)||void 0===i||i.disconnect(),clearTimeout(this.measureTimeout)}readMeasure(){let t=this.view.dom.getBoundingClientRect(),e=1,i=1,n=!1;if("fixed"==this.position&&this.manager.tooltipViews.length){let{dom:t}=this.manager.tooltipViews[0];if(_e.gecko)n=t.offsetParent!=this.container.ownerDocument.body;else if(t.style.top==Ur&&"0px"==t.style.left){let e=t.getBoundingClientRect();n=Math.abs(e.top+1e4)>1||Math.abs(e.left)>1}}if(n||"absolute"==this.position)if(this.parent){let t=this.parent.getBoundingClientRect();t.width&&t.height&&(e=t.width/this.parent.offsetWidth,i=t.height/this.parent.offsetHeight)}else({scaleX:e,scaleY:i}=this.view.viewState);return{editor:t,parent:this.parent?this.container.getBoundingClientRect():t,pos:this.manager.tooltips.map(((t,e)=>{let i=this.manager.tooltipViews[e];return i.getCoords?i.getCoords(t.pos):this.view.coordsAtPos(t.pos)})),size:this.manager.tooltipViews.map((({dom:t})=>t.getBoundingClientRect())),space:this.view.state.facet(Jr).tooltipSpace(this.view),scaleX:e,scaleY:i,makeAbsolute:n}}writeMeasure(t){var e;if(t.makeAbsolute){this.madeAbsolute=!0,this.position="absolute";for(let t of this.manager.tooltipViews)t.dom.style.position="absolute"}let{editor:i,space:n,scaleX:s,scaleY:r}=t,o=[];for(let a=0;a<this.manager.tooltips.length;a++){let l=this.manager.tooltips[a],h=this.manager.tooltipViews[a],{dom:c}=h,f=t.pos[a],u=t.size[a];if(!f||f.bottom<=Math.max(i.top,n.top)||f.top>=Math.min(i.bottom,n.bottom)||f.right<Math.max(i.left,n.left)-.1||f.left>Math.min(i.right,n.right)+.1){c.style.top=Ur;continue}let d=l.arrow?h.dom.querySelector(".cm-tooltip-arrow"):null,p=d?7:0,O=u.right-u.left,g=null!==(e=Kr.get(h))&&void 0!==e?e:u.bottom-u.top,m=h.offset||io,w=this.view.textDirection==di.LTR,v=u.width>n.right-n.left?w?n.left:n.right-u.width:w?Math.min(f.left-(d?14:0)+m.x,n.right-O):Math.max(n.left,f.left-O+(d?14:0)-m.x),b=this.above[a];!l.strictSide&&(b?f.top-(u.bottom-u.top)-m.y<n.top:f.bottom+(u.bottom-u.top)+m.y>n.bottom)&&b==n.bottom-f.bottom>f.top-n.top&&(b=this.above[a]=!b);let y=(b?f.top-n.top:n.bottom-f.bottom)-p;if(y<g&&!1!==h.resize){if(y<this.view.defaultLineHeight){c.style.top=Ur;continue}Kr.set(h,g),c.style.height=(g=y)/r+"px"}else c.style.height&&(c.style.height="");let S=b?f.top-g-p-m.y:f.bottom+p+m.y,x=v+O;if(!0!==h.overlap)for(let t of o)t.left<x&&t.right>v&&t.top<S+g&&t.bottom>S&&(S=b?t.top-g-2-p:t.bottom+p+2);if("absolute"==this.position?(c.style.top=(S-t.parent.top)/r+"px",c.style.left=(v-t.parent.left)/s+"px"):(c.style.top=S/r+"px",c.style.left=v/s+"px"),d){let t=f.left+(w?m.x:-m.x)-(v+14-7);d.style.left=t/s+"px"}!0!==h.overlap&&o.push({left:v,top:S,right:x,bottom:S+g}),c.classList.toggle("cm-tooltip-above",b),c.classList.toggle("cm-tooltip-below",!b),h.positioned&&h.positioned(t.space)}}maybeMeasure(){if(this.manager.tooltips.length&&(this.view.inView&&this.view.requestMeasure(this.measureReq),this.inView!=this.view.inView&&(this.inView=this.view.inView,!this.inView)))for(let t of this.manager.tooltipViews)t.dom.style.top=Ur}},{eventObservers:{scroll(){this.maybeMeasure()}}}),eo=Gs.baseTheme({".cm-tooltip":{zIndex:100,boxSizing:"border-box"},"&light .cm-tooltip":{border:"1px solid #bbb",backgroundColor:"#f5f5f5"},"&light .cm-tooltip-section:not(:first-child)":{borderTop:"1px solid #bbb"},"&dark .cm-tooltip":{backgroundColor:"#333338",color:"white"},".cm-tooltip-arrow":{height:"7px",width:"14px",position:"absolute",zIndex:-1,overflow:"hidden","&:before, &:after":{content:"''",position:"absolute",width:0,height:0,borderLeft:"7px solid transparent",borderRight:"7px solid transparent"},".cm-tooltip-above &":{bottom:"-7px","&:before":{borderTop:"7px solid #bbb"},"&:after":{borderTop:"7px solid #f5f5f5",bottom:"1px"}},".cm-tooltip-below &":{top:"-7px","&:before":{borderBottom:"7px solid #bbb"},"&:after":{borderBottom:"7px solid #f5f5f5",top:"1px"}}},"&dark .cm-tooltip .cm-tooltip-arrow":{"&:before":{borderTopColor:"#333338",borderBottomColor:"#333338"},"&:after":{borderTopColor:"transparent",borderBottomColor:"transparent"}}}),io={x:0,y:0},no=j.define({enables:[to,eo]}),so=j.define();class ro{static create(t){return new ro(t)}constructor(t){this.view=t,this.mounted=!1,this.dom=document.createElement("div"),this.dom.classList.add("cm-tooltip-hover"),this.manager=new Hr(t,so,(t=>this.createHostedView(t)),(t=>t.dom.remove()))}createHostedView(t){let e=t.create(this.view);return e.dom.classList.add("cm-tooltip-section"),this.dom.appendChild(e.dom),this.mounted&&e.mount&&e.mount(this.view),e}mount(t){for(let e of this.manager.tooltipViews)e.mount&&e.mount(t);this.mounted=!0}positioned(t){for(let e of this.manager.tooltipViews)e.positioned&&e.positioned(t)}update(t){this.manager.update(t)}destroy(){var t;for(let e of this.manager.tooltipViews)null===(t=e.destroy)||void 0===t||t.call(e)}passProp(t){let e;for(let i of this.manager.tooltipViews){let n=i[t];if(void 0!==n)if(void 0===e)e=n;else if(e!==n)return}return e}get offset(){return this.passProp("offset")}get getCoords(){return this.passProp("getCoords")}get overlap(){return this.passProp("overlap")}get resize(){return this.passProp("resize")}}const oo=no.compute([so],(t=>{let e=t.facet(so).filter((t=>t));return 0===e.length?null:{pos:Math.min(...e.map((t=>t.pos))),end:Math.max(...e.map((t=>{var e;return null!==(e=t.end)&&void 0!==e?e:t.pos}))),create:ro.create,above:e[0].above,arrow:e.some((t=>t.arrow))}}));class ao{constructor(t,e,i,n,s){this.view=t,this.source=e,this.field=i,this.setHover=n,this.hoverTime=s,this.hoverTimeout=-1,this.restartTimeout=-1,this.pending=null,this.lastMove={x:0,y:0,target:t.dom,time:0},this.checkHover=this.checkHover.bind(this),t.dom.addEventListener("mouseleave",this.mouseleave=this.mouseleave.bind(this)),t.dom.addEventListener("mousemove",this.mousemove=this.mousemove.bind(this))}update(){this.pending&&(this.pending=null,clearTimeout(this.restartTimeout),this.restartTimeout=setTimeout((()=>this.startHover()),20))}get active(){return this.view.state.field(this.field)}checkHover(){if(this.hoverTimeout=-1,this.active)return;let t=Date.now()-this.lastMove.time;t<this.hoverTime?this.hoverTimeout=setTimeout(this.checkHover,this.hoverTime-t):this.startHover()}startHover(){clearTimeout(this.restartTimeout);let{view:t,lastMove:e}=this,i=t.docView.nearest(e.target);if(!i)return;let n,s=1;if(i instanceof ze)n=i.posAtStart;else{if(n=t.posAtCoords(e),null==n)return;let i=t.coordsAtPos(n);if(!i||e.y<i.top||e.y>i.bottom||e.x<i.left-t.defaultCharacterWidth||e.x>i.right+t.defaultCharacterWidth)return;let r=t.bidiSpans(t.state.doc.lineAt(n)).find((t=>t.from<=n&&t.to>=n)),o=r&&r.dir==di.RTL?-1:1;s=e.x<i.left?-o:o}let r=this.source(t,n,s);if(null==r?void 0:r.then){let e=this.pending={pos:n};r.then((i=>{this.pending==e&&(this.pending=null,i&&t.dispatch({effects:this.setHover.of(i)}))}),(e=>zi(t.state,e,"hover tooltip")))}else r&&t.dispatch({effects:this.setHover.of(r)})}get tooltip(){let t=this.view.plugin(to),e=t?t.manager.tooltips.findIndex((t=>t.create==ro.create)):-1;return e>-1?t.manager.tooltipViews[e]:null}mousemove(t){var e;this.lastMove={x:t.clientX,y:t.clientY,target:t.target,time:Date.now()},this.hoverTimeout<0&&(this.hoverTimeout=setTimeout(this.checkHover,this.hoverTime));let{active:i,tooltip:n}=this;if(i&&n&&!function(t,e){let i=t.getBoundingClientRect();return e.clientX>=i.left-lo&&e.clientX<=i.right+lo&&e.clientY>=i.top-lo&&e.clientY<=i.bottom+lo}(n.dom,t)||this.pending){let{pos:n}=i||this.pending,s=null!==(e=null==i?void 0:i.end)&&void 0!==e?e:n;(n==s?this.view.posAtCoords(this.lastMove)==n:function(t,e,i,n,s,r){let o=t.scrollDOM.getBoundingClientRect(),a=t.documentTop+t.documentPadding.top+t.contentHeight;if(o.left>n||o.right<n||o.top>s||Math.min(o.bottom,a)<s)return!1;let l=t.posAtCoords({x:n,y:s},!1);return l>=e&&l<=i}(this.view,n,s,t.clientX,t.clientY))||(this.view.dispatch({effects:this.setHover.of(null)}),this.pending=null)}}mouseleave(t){clearTimeout(this.hoverTimeout),this.hoverTimeout=-1;let{active:e}=this;if(e){let{tooltip:e}=this;e&&e.dom.contains(t.relatedTarget)?this.watchTooltipLeave(e.dom):this.view.dispatch({effects:this.setHover.of(null)})}}watchTooltipLeave(t){let e=i=>{t.removeEventListener("mouseleave",e),this.active&&!this.view.dom.contains(i.relatedTarget)&&this.view.dispatch({effects:this.setHover.of(null)})};t.addEventListener("mouseleave",e)}destroy(){clearTimeout(this.hoverTimeout),this.view.dom.removeEventListener("mouseleave",this.mouseleave),this.view.dom.removeEventListener("mousemove",this.mousemove)}}const lo=4;function ho(t,e={}){let i=dt.define(),n=z.define({create:()=>null,update(t,n){if(t&&(e.hideOnChange&&(n.docChanged||n.selection)||e.hideOn&&e.hideOn(n,t)))return null;if(t&&n.docChanged){let e=n.changes.mapPos(t.pos,-1,k.TrackDel);if(null==e)return null;let i=Object.assign(Object.create(null),t);i.pos=e,null!=t.end&&(i.end=n.changes.mapPos(t.end)),t=i}for(let e of n.effects)e.is(i)&&(t=e.value),e.is(fo)&&(t=null);return t},provide:t=>so.from(t)});return[n,Ni.define((s=>new ao(s,t,n,i,e.hoverTime||300))),oo]}function co(t,e){let i=t.plugin(to);if(!i)return null;let n=i.manager.tooltips.indexOf(e);return n<0?null:i.manager.tooltipViews[n]}const fo=dt.define(),uo=fo.of(null);const po=j.define({combine(t){let e,i;for(let n of t)e=e||n.topContainer,i=i||n.bottomContainer;return{topContainer:e,bottomContainer:i}}});function Oo(t,e){let i=t.plugin(go),n=i?i.specs.indexOf(e):-1;return n>-1?i.panels[n]:null}const go=Ni.fromClass(class{constructor(t){this.input=t.state.facet(vo),this.specs=this.input.filter((t=>t)),this.panels=this.specs.map((e=>e(t)));let e=t.state.facet(po);this.top=new mo(t,!0,e.topContainer),this.bottom=new mo(t,!1,e.bottomContainer),this.top.sync(this.panels.filter((t=>t.top))),this.bottom.sync(this.panels.filter((t=>!t.top)));for(let t of this.panels)t.dom.classList.add("cm-panel"),t.mount&&t.mount()}update(t){let e=t.state.facet(po);this.top.container!=e.topContainer&&(this.top.sync([]),this.top=new mo(t.view,!0,e.topContainer)),this.bottom.container!=e.bottomContainer&&(this.bottom.sync([]),this.bottom=new mo(t.view,!1,e.bottomContainer)),this.top.syncClasses(),this.bottom.syncClasses();let i=t.state.facet(vo);if(i!=this.input){let e=i.filter((t=>t)),n=[],s=[],r=[],o=[];for(let i of e){let e,a=this.specs.indexOf(i);a<0?(e=i(t.view),o.push(e)):(e=this.panels[a],e.update&&e.update(t)),n.push(e),(e.top?s:r).push(e)}this.specs=e,this.panels=n,this.top.sync(s),this.bottom.sync(r);for(let t of o)t.dom.classList.add("cm-panel"),t.mount&&t.mount()}else for(let e of this.panels)e.update&&e.update(t)}destroy(){this.top.sync([]),this.bottom.sync([])}},{provide:t=>Gs.scrollMargins.of((e=>{let i=e.plugin(t);return i&&{top:i.top.scrollMargin(),bottom:i.bottom.scrollMargin()}}))});class mo{constructor(t,e,i){this.view=t,this.top=e,this.container=i,this.dom=void 0,this.classes="",this.panels=[],this.syncClasses()}sync(t){for(let e of this.panels)e.destroy&&t.indexOf(e)<0&&e.destroy();this.panels=t,this.syncDOM()}syncDOM(){if(0==this.panels.length)return void(this.dom&&(this.dom.remove(),this.dom=void 0));if(!this.dom){this.dom=document.createElement("div"),this.dom.className=this.top?"cm-panels cm-panels-top":"cm-panels cm-panels-bottom",this.dom.style[this.top?"top":"bottom"]="0";let t=this.container||this.view.dom;t.insertBefore(this.dom,this.top?t.firstChild:null)}let t=this.dom.firstChild;for(let e of this.panels)if(e.dom.parentNode==this.dom){for(;t!=e.dom;)t=wo(t);t=t.nextSibling}else this.dom.insertBefore(e.dom,t);for(;t;)t=wo(t)}scrollMargin(){return!this.dom||this.container?0:Math.max(0,this.top?this.dom.getBoundingClientRect().bottom-Math.max(0,this.view.scrollDOM.getBoundingClientRect().top):Math.min(innerHeight,this.view.scrollDOM.getBoundingClientRect().bottom)-this.dom.getBoundingClientRect().top)}syncClasses(){if(this.container&&this.classes!=this.view.themeClasses){for(let t of this.classes.split(" "))t&&this.container.classList.remove(t);for(let t of(this.classes=this.view.themeClasses).split(" "))t&&this.container.classList.add(t)}}}function wo(t){let e=t.nextSibling;return t.remove(),e}const vo=j.define({enables:go});class bo extends Pt{compare(t){return this==t||this.constructor==t.constructor&&this.eq(t)}eq(t){return!1}destroy(t){}}bo.prototype.elementClass="",bo.prototype.toDOM=void 0,bo.prototype.mapMode=k.TrackBefore,bo.prototype.startSide=bo.prototype.endSide=-1,bo.prototype.point=!0;const yo=j.define(),So={class:"",renderEmptyElements:!1,elementStyle:"",markers:()=>At.empty,lineMarker:()=>null,widgetMarker:()=>null,lineMarkerChange:null,initialSpacer:null,updateSpacer:null,domEventHandlers:{}},xo=j.define();function ko(t){return[$o(),xo.of(Object.assign(Object.assign({},So),t))]}const Qo=j.define({combine:t=>t.some((t=>t))});function $o(t){let e=[Po];return t&&!1===t.fixed&&e.push(Qo.of(!0)),e}const Po=Ni.fromClass(class{constructor(t){this.view=t,this.prevViewport=t.viewport,this.dom=document.createElement("div"),this.dom.className="cm-gutters",this.dom.setAttribute("aria-hidden","true"),this.dom.style.minHeight=this.view.contentHeight/this.view.scaleY+"px",this.gutters=t.state.facet(xo).map((e=>new Ao(t,e)));for(let t of this.gutters)this.dom.appendChild(t.dom);this.fixed=!t.state.facet(Qo),this.fixed&&(this.dom.style.position="sticky"),this.syncGutters(!1),t.scrollDOM.insertBefore(this.dom,t.contentDOM)}update(t){if(this.updateGutters(t)){let e=this.prevViewport,i=t.view.viewport,n=Math.min(e.to,i.to)-Math.max(e.from,i.from);this.syncGutters(n<.8*(i.to-i.from))}t.geometryChanged&&(this.dom.style.minHeight=this.view.contentHeight+"px"),this.view.state.facet(Qo)!=!this.fixed&&(this.fixed=!this.fixed,this.dom.style.position=this.fixed?"sticky":""),this.prevViewport=t.view.viewport}syncGutters(t){let e=this.dom.nextSibling;t&&this.dom.remove();let i=At.iter(this.view.state.facet(yo),this.view.viewport.from),n=[],s=this.gutters.map((t=>new To(t,this.view.viewport,-this.view.documentPadding.top)));for(let t of this.view.viewportLineBlocks)if(n.length&&(n=[]),Array.isArray(t.type)){let e=!0;for(let r of t.type)if(r.type==ni.Text&&e){Co(i,n,r.from);for(let t of s)t.line(this.view,r,n);e=!1}else if(r.widget)for(let t of s)t.widget(this.view,r)}else if(t.type==ni.Text){Co(i,n,t.from);for(let e of s)e.line(this.view,t,n)}else if(t.widget)for(let e of s)e.widget(this.view,t);for(let t of s)t.finish();t&&this.view.scrollDOM.insertBefore(this.dom,e)}updateGutters(t){let e=t.startState.facet(xo),i=t.state.facet(xo),n=t.docChanged||t.heightChanged||t.viewportChanged||!At.eq(t.startState.facet(yo),t.state.facet(yo),t.view.viewport.from,t.view.viewport.to);if(e==i)for(let e of this.gutters)e.update(t)&&(n=!0);else{n=!0;let s=[];for(let n of i){let i=e.indexOf(n);i<0?s.push(new Ao(this.view,n)):(this.gutters[i].update(t),s.push(this.gutters[i]))}for(let t of this.gutters)t.dom.remove(),s.indexOf(t)<0&&t.destroy();for(let t of s)this.dom.appendChild(t.dom);this.gutters=s}return n}destroy(){for(let t of this.gutters)t.destroy();this.dom.remove()}},{provide:t=>Gs.scrollMargins.of((e=>{let i=e.plugin(t);return i&&0!=i.gutters.length&&i.fixed?e.textDirection==di.LTR?{left:i.dom.offsetWidth*e.scaleX}:{right:i.dom.offsetWidth*e.scaleX}:null}))});function Zo(t){return Array.isArray(t)?t:[t]}function Co(t,e,i){for(;t.value&&t.from<=i;)t.from==i&&e.push(t.value),t.next()}class To{constructor(t,e,i){this.gutter=t,this.height=i,this.i=0,this.cursor=At.iter(t.markers,e.from)}addElement(t,e,i){let{gutter:n}=this,s=(e.top-this.height)/t.scaleY,r=e.height/t.scaleY;if(this.i==n.elements.length){let e=new Ro(t,r,s,i);n.elements.push(e),n.dom.appendChild(e.dom)}else n.elements[this.i].update(t,r,s,i);this.height=e.bottom,this.i++}line(t,e,i){let n=[];Co(this.cursor,n,e.from),i.length&&(n=n.concat(i));let s=this.gutter.config.lineMarker(t,e,n);s&&n.unshift(s);let r=this.gutter;(0!=n.length||r.config.renderEmptyElements)&&this.addElement(t,e,n)}widget(t,e){let i=this.gutter.config.widgetMarker(t,e.widget,e);i&&this.addElement(t,e,[i])}finish(){let t=this.gutter;for(;t.elements.length>this.i;){let e=t.elements.pop();t.dom.removeChild(e.dom),e.destroy()}}}class Ao{constructor(t,e){this.view=t,this.config=e,this.elements=[],this.spacer=null,this.dom=document.createElement("div"),this.dom.className="cm-gutter"+(this.config.class?" "+this.config.class:"");for(let i in e.domEventHandlers)this.dom.addEventListener(i,(n=>{let s,r=n.target;if(r!=this.dom&&this.dom.contains(r)){for(;r.parentNode!=this.dom;)r=r.parentNode;let t=r.getBoundingClientRect();s=(t.top+t.bottom)/2}else s=n.clientY;let o=t.lineBlockAtHeight(s-t.documentTop);e.domEventHandlers[i](t,o,n)&&n.preventDefault()}));this.markers=Zo(e.markers(t)),e.initialSpacer&&(this.spacer=new Ro(t,0,0,[e.initialSpacer(t)]),this.dom.appendChild(this.spacer.dom),this.spacer.dom.style.cssText+="visibility: hidden; pointer-events: none")}update(t){let e=this.markers;if(this.markers=Zo(this.config.markers(t.view)),this.spacer&&this.config.updateSpacer){let e=this.config.updateSpacer(this.spacer.markers[0],t);e!=this.spacer.markers[0]&&this.spacer.update(t.view,0,0,[e])}let i=t.view.viewport;return!At.eq(this.markers,e,i.from,i.to)||!!this.config.lineMarkerChange&&this.config.lineMarkerChange(t)}destroy(){for(let t of this.elements)t.destroy()}}class Ro{constructor(t,e,i,n){this.height=-1,this.above=0,this.markers=[],this.dom=document.createElement("div"),this.dom.className="cm-gutterElement",this.update(t,e,i,n)}update(t,e,i,n){this.height!=e&&(this.height=e,this.dom.style.height=e+"px"),this.above!=i&&(this.dom.style.marginTop=(this.above=i)?i+"px":""),function(t,e){if(t.length!=e.length)return!1;for(let i=0;i<t.length;i++)if(!t[i].compare(e[i]))return!1;return!0}(this.markers,n)||this.setMarkers(t,n)}setMarkers(t,e){let i="cm-gutterElement",n=this.dom.firstChild;for(let s=0,r=0;;){let o=r,a=s<e.length?e[s++]:null,l=!1;if(a){let t=a.elementClass;t&&(i+=" "+t);for(let t=r;t<this.markers.length;t++)if(this.markers[t].compare(a)){o=t,l=!0;break}}else o=this.markers.length;for(;r<o;){let t=this.markers[r++];if(t.toDOM){t.destroy(n);let e=n.nextSibling;n.remove(),n=e}}if(!a)break;a.toDOM&&(l?n=n.nextSibling:this.dom.insertBefore(a.toDOM(t),n)),l&&r++}this.dom.className=i,this.markers=e}destroy(){this.setMarkers(null,[])}}const Xo=j.define(),Yo=j.define({combine:t=>$t(t,{formatNumber:String,domEventHandlers:{}},{domEventHandlers(t,e){let i=Object.assign({},t);for(let t in e){let n=i[t],s=e[t];i[t]=n?(t,e,i)=>n(t,e,i)||s(t,e,i):s}return i}})});class Wo extends bo{constructor(t){super(),this.number=t}eq(t){return this.number==t.number}toDOM(){return document.createTextNode(this.number)}}function Mo(t,e){return t.state.facet(Yo).formatNumber(e,t.state)}const jo=xo.compute([Yo],(t=>({class:"cm-lineNumbers",renderEmptyElements:!1,markers:t=>t.state.facet(Xo),lineMarker:(t,e,i)=>i.some((t=>t.toDOM))?null:new Wo(Mo(t,t.state.doc.lineAt(e.from).number)),widgetMarker:()=>null,lineMarkerChange:t=>t.startState.facet(Yo)!=t.state.facet(Yo),initialSpacer:t=>new Wo(Mo(t,Eo(t.state.doc.lines))),updateSpacer(t,e){let i=Mo(e.view,Eo(e.view.state.doc.lines));return i==t.number?t:new Wo(i)},domEventHandlers:t.facet(Yo).domEventHandlers})));function Do(t={}){return[Yo.of(t),$o(),jo]}function Eo(t){let e=9;for(;e<t;)e=10*e+9;return e}const qo=new class extends bo{constructor(){super(...arguments),this.elementClass="cm-activeLineGutter"}},_o=yo.compute(["selection"],(t=>{let e=[],i=-1;for(let n of t.selection.ranges){let s=t.doc.lineAt(n.head).from;s>i&&(i=s,e.push(qo.range(s)))}return At.of(e)}));function Vo(){return _o}const Io=new Map;function zo(t){return Ni.define((e=>({decorations:t.createDeco(e),update(e){this.decorations=t.updateDeco(e,this.decorations)}})),{decorations:t=>t.decorations})}const Bo=zo(new Pr({regexp:/\t| +/g,decoration:t=>function(t){let e=Io.get(t);return e||Io.set(t,e=si.mark({attributes:"\t"===t?{class:"cm-highlightTab"}:{class:"cm-highlightSpace","data-display":t.replace(/ /g,"·")}})),e}(t[0]),boundary:/\S/}));const Go=zo(new Pr({regexp:/\s+$/g,decoration:si.mark({class:"cm-trailingSpace"}),boundary:/\S/}));const Lo={HeightMap:as,HeightOracle:is,MeasuredHeights:ns,QueryType:rs,ChangedRange:an,computeOrder:Zi,moveVisually:Ai};var No=Object.freeze({__proto__:null,BidiSpan:xi,BlockInfo:ss,get BlockType(){return ni},Decoration:si,get Direction(){return di},EditorView:Gs,GutterMarker:bo,MatchDecorator:Pr,RectangleMarker:ar,ViewPlugin:Ni,ViewUpdate:ln,WidgetType:ii,__test:Lo,closeHoverTooltips:uo,crosshairCursor:Nr,drawSelection:Or,dropCursor:Qr,getDrawSelectionConfig:function(t){return t.facet(pr)},getPanel:Oo,getTooltip:co,gutter:ko,gutterLineClass:yo,gutters:$o,hasHoverTooltips:function(t){return t.facet(so).some((t=>t))},highlightActiveLine:Dr,highlightActiveLineGutter:Vo,highlightSpecialChars:Xr,highlightTrailingWhitespace:function(){return Go},highlightWhitespace:function(){return Bo},hoverTooltip:ho,keymap:tr,layer:ur,lineNumberMarkers:Xo,lineNumbers:Do,logException:zi,panels:function(t){return t?[po.of(t)]:[]},placeholder:function(t){return Ni.fromClass(class{constructor(e){this.view=e,this.placeholder=t?si.set([si.widget({widget:new _r(t),side:1}).range(0)]):si.none}get decorations(){return this.view.state.doc.length?si.none:this.placeholder}},{decorations:t=>t.decorations})},rectangularSelection:Br,repositionTooltips:function(t){let e=t.plugin(to);e&&e.maybeMeasure()},runScopeHandlers:nr,scrollPastEnd:function(){return[jr,Fi.of((t=>{var e;return(null===(e=t.plugin(jr))||void 0===e?void 0:e.attrs)||null}))]},showPanel:vo,showTooltip:no,tooltips:function(t={}){return Jr.of(t)}});const Uo=1024;let Ho=0;class Fo{constructor(t,e){this.from=t,this.to=e}}class Jo{constructor(t={}){this.id=Ho++,this.perNode=!!t.perNode,this.deserialize=t.deserialize||(()=>{throw new Error("This node type doesn't define a deserialize function")})}add(t){if(this.perNode)throw new RangeError("Can't add per-node props to node types");return"function"!=typeof t&&(t=ea.match(t)),e=>{let i=t(e);return void 0===i?null:[this,i]}}}Jo.closedBy=new Jo({deserialize:t=>t.split(" ")}),Jo.openedBy=new Jo({deserialize:t=>t.split(" ")}),Jo.group=new Jo({deserialize:t=>t.split(" ")}),Jo.isolate=new Jo({deserialize:t=>{if(t&&"rtl"!=t&&"ltr"!=t&&"auto"!=t)throw new RangeError("Invalid value for isolate: "+t);return t||"auto"}}),Jo.contextHash=new Jo({perNode:!0}),Jo.lookAhead=new Jo({perNode:!0}),Jo.mounted=new Jo({perNode:!0});class Ko{constructor(t,e,i){this.tree=t,this.overlay=e,this.parser=i}static get(t){return t&&t.props&&t.props[Jo.mounted.id]}}const ta=Object.create(null);class ea{constructor(t,e,i,n=0){this.name=t,this.props=e,this.id=i,this.flags=n}static define(t){let e=t.props&&t.props.length?Object.create(null):ta,i=(t.top?1:0)|(t.skipped?2:0)|(t.error?4:0)|(null==t.name?8:0),n=new ea(t.name||"",e,t.id,i);if(t.props)for(let i of t.props)if(Array.isArray(i)||(i=i(n)),i){if(i[0].perNode)throw new RangeError("Can't store a per-node prop on a node type");e[i[0].id]=i[1]}return n}prop(t){return this.props[t.id]}get isTop(){return(1&this.flags)>0}get isSkipped(){return(2&this.flags)>0}get isError(){return(4&this.flags)>0}get isAnonymous(){return(8&this.flags)>0}is(t){if("string"==typeof t){if(this.name==t)return!0;let e=this.prop(Jo.group);return!!e&&e.indexOf(t)>-1}return this.id==t}static match(t){let e=Object.create(null);for(let i in t)for(let n of i.split(" "))e[n]=t[i];return t=>{for(let i=t.prop(Jo.group),n=-1;n<(i?i.length:0);n++){let s=e[n<0?t.name:i[n]];if(s)return s}}}}ea.none=new ea("",Object.create(null),0,8);class ia{constructor(t){this.types=t;for(let e=0;e<t.length;e++)if(t[e].id!=e)throw new RangeError("Node type ids should correspond to array positions when creating a node set")}extend(...t){let e=[];for(let i of this.types){let n=null;for(let e of t){let t=e(i);t&&(n||(n=Object.assign({},i.props)),n[t[0].id]=t[1])}e.push(n?new ea(i.name,n,i.id,i.flags):i)}return new ia(e)}}const na=new WeakMap,sa=new WeakMap;var ra;!function(t){t[t.ExcludeBuffers=1]="ExcludeBuffers",t[t.IncludeAnonymous=2]="IncludeAnonymous",t[t.IgnoreMounts=4]="IgnoreMounts",t[t.IgnoreOverlays=8]="IgnoreOverlays"}(ra||(ra={}));class oa{constructor(t,e,i,n,s){if(this.type=t,this.children=e,this.positions=i,this.length=n,this.props=null,s&&s.length){this.props=Object.create(null);for(let[t,e]of s)this.props["number"==typeof t?t:t.id]=e}}toString(){let t=Ko.get(this);if(t&&!t.overlay)return t.tree.toString();let e="";for(let t of this.children){let i=t.toString();i&&(e&&(e+=","),e+=i)}return this.type.name?(/\W/.test(this.type.name)&&!this.type.isError?JSON.stringify(this.type.name):this.type.name)+(e.length?"("+e+")":""):e}cursor(t=0){return new va(this.topNode,t)}cursorAt(t,e=0,i=0){let n=na.get(this)||this.topNode,s=new va(n);return s.moveTo(t,e),na.set(this,s._tree),s}get topNode(){return new ua(this,0,0,null)}resolve(t,e=0){let i=ca(na.get(this)||this.topNode,t,e,!1);return na.set(this,i),i}resolveInner(t,e=0){let i=ca(sa.get(this)||this.topNode,t,e,!0);return sa.set(this,i),i}resolveStack(t,e=0){return function(t,e,i){let n=t.resolveInner(e,i),s=null;for(let t=n instanceof ua?n:n.context.parent;t;t=t.parent)if(t.index<0){let r=t.parent;(s||(s=[n])).push(r.resolve(e,i)),t=r}else{let r=Ko.get(t.tree);if(r&&r.overlay&&r.overlay[0].from<=e&&r.overlay[r.overlay.length-1].to>=e){let o=new ua(r.tree,r.overlay[0].from+t.from,-1,t);(s||(s=[n])).push(ca(o,e,i,!1))}}return s?ma(s):n}(this,t,e)}iterate(t){let{enter:e,leave:i,from:n=0,to:s=this.length}=t,r=t.mode||0,o=(r&ra.IncludeAnonymous)>0;for(let t=this.cursor(r|ra.IncludeAnonymous);;){let r=!1;if(t.from<=s&&t.to>=n&&(!o&&t.type.isAnonymous||!1!==e(t))){if(t.firstChild())continue;r=!0}for(;r&&i&&(o||!t.type.isAnonymous)&&i(t),!t.nextSibling();){if(!t.parent())return;r=!0}}}prop(t){return t.perNode?this.props?this.props[t.id]:void 0:this.type.prop(t)}get propValues(){let t=[];if(this.props)for(let e in this.props)t.push([+e,this.props[e]]);return t}balance(t={}){return this.children.length<=8?this:xa(ea.none,this.children,this.positions,0,this.children.length,0,this.length,((t,e,i)=>new oa(this.type,t,e,i,this.propValues)),t.makeTree||((t,e,i)=>new oa(ea.none,t,e,i)))}static build(t){return function(t){var e;let{buffer:i,nodeSet:n,maxBufferLength:s=Uo,reused:r=[],minRepeatType:o=n.types.length}=t,a=Array.isArray(i)?new aa(i,i.length):i,l=n.types,h=0,c=0;function f(t,e,i,w,v,b){let{id:y,start:S,end:x,size:k}=a,Q=c;for(;k<0;){if(a.next(),-1==k){let e=r[y];return i.push(e),void w.push(S-t)}if(-3==k)return void(h=y);if(-4==k)return void(c=y);throw new RangeError(`Unrecognized record size: ${k}`)}let $,P,Z=l[y],C=S-t;if(x-S<=s&&(P=g(a.pos-e,v))){let e=new Uint16Array(P.size-P.skip),i=a.pos-P.size,s=e.length;for(;a.pos>i;)s=m(P.start,e,s);$=new la(e,x-P.start,n),C=P.start-t}else{let t=a.pos-k;a.next();let e=[],i=[],n=y>=o?y:-1,r=0,l=x;for(;a.pos>t;)n>=0&&a.id==n&&a.size>=0?(a.end<=l-s&&(p(e,i,S,r,a.end,l,n,Q),r=e.length,l=a.end),a.next()):b>2500?u(S,t,e,i):f(S,t,e,i,n,b+1);if(n>=0&&r>0&&r<e.length&&p(e,i,S,r,S,l,n,Q),e.reverse(),i.reverse(),n>-1&&r>0){let t=d(Z);$=xa(Z,e,i,0,e.length,0,x-S,t,t)}else $=O(Z,e,i,x-S,Q-x)}i.push($),w.push(C)}function u(t,e,i,r){let o=[],l=0,h=-1;for(;a.pos>e;){let{id:t,start:e,end:i,size:n}=a;if(n>4)a.next();else{if(h>-1&&e<h)break;h<0&&(h=i-s),o.push(t,e,i),l++,a.next()}}if(l){let e=new Uint16Array(4*l),s=o[o.length-2];for(let t=o.length-3,i=0;t>=0;t-=3)e[i++]=o[t],e[i++]=o[t+1]-s,e[i++]=o[t+2]-s,e[i++]=i;i.push(new la(e,o[2]-s,n)),r.push(s-t)}}function d(t){return(e,i,n)=>{let s,r,o=0,a=e.length-1;if(a>=0&&(s=e[a])instanceof oa){if(!a&&s.type==t&&s.length==n)return s;(r=s.prop(Jo.lookAhead))&&(o=i[a]+s.length+r)}return O(t,e,i,n,o)}}function p(t,e,i,s,r,o,a,l){let h=[],c=[];for(;t.length>s;)h.push(t.pop()),c.push(e.pop()+i-r);t.push(O(n.types[a],h,c,o-r,l-o)),e.push(r-i)}function O(t,e,i,n,s=0,r){if(h){let t=[Jo.contextHash,h];r=r?[t].concat(r):[t]}if(s>25){let t=[Jo.lookAhead,s];r=r?[t].concat(r):[t]}return new oa(t,e,i,n,r)}function g(t,e){let i=a.fork(),n=0,r=0,l=0,h=i.end-s,c={size:0,start:0,skip:0};t:for(let s=i.pos-t;i.pos>s;){let t=i.size;if(i.id==e&&t>=0){c.size=n,c.start=r,c.skip=l,l+=4,n+=4,i.next();continue}let a=i.pos-t;if(t<0||a<s||i.start<h)break;let f=i.id>=o?4:0,u=i.start;for(i.next();i.pos>a;){if(i.size<0){if(-3!=i.size)break t;f+=4}else i.id>=o&&(f+=4);i.next()}r=u,n+=t,l+=f}return(e<0||n==t)&&(c.size=n,c.start=r,c.skip=l),c.size>4?c:void 0}function m(t,e,i){let{id:n,start:s,end:r,size:l}=a;if(a.next(),l>=0&&n<o){let o=i;if(l>4){let n=a.pos-(l-4);for(;a.pos>n;)i=m(t,e,i)}e[--i]=o,e[--i]=r-t,e[--i]=s-t,e[--i]=n}else-3==l?h=n:-4==l&&(c=n);return i}let w=[],v=[];for(;a.pos>0;)f(t.start||0,t.bufferStart||0,w,v,-1,0);let b=null!==(e=t.length)&&void 0!==e?e:w.length?v[0]+w[0].length:0;return new oa(l[t.topID],w.reverse(),v.reverse(),b)}(t)}}oa.empty=new oa(ea.none,[],[],0);class aa{constructor(t,e){this.buffer=t,this.index=e}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}get pos(){return this.index}next(){this.index-=4}fork(){return new aa(this.buffer,this.index)}}class la{constructor(t,e,i){this.buffer=t,this.length=e,this.set=i}get type(){return ea.none}toString(){let t=[];for(let e=0;e<this.buffer.length;)t.push(this.childString(e)),e=this.buffer[e+3];return t.join(",")}childString(t){let e=this.buffer[t],i=this.buffer[t+3],n=this.set.types[e],s=n.name;if(/\W/.test(s)&&!n.isError&&(s=JSON.stringify(s)),i==(t+=4))return s;let r=[];for(;t<i;)r.push(this.childString(t)),t=this.buffer[t+3];return s+"("+r.join(",")+")"}findChild(t,e,i,n,s){let{buffer:r}=this,o=-1;for(let a=t;a!=e&&!(ha(s,n,r[a+1],r[a+2])&&(o=a,i>0));a=r[a+3]);return o}slice(t,e,i){let n=this.buffer,s=new Uint16Array(e-t),r=0;for(let o=t,a=0;o<e;){s[a++]=n[o++],s[a++]=n[o++]-i;let e=s[a++]=n[o++]-i;s[a++]=n[o++]-t,r=Math.max(r,e)}return new la(s,r,this.set)}}function ha(t,e,i,n){switch(t){case-2:return i<e;case-1:return n>=e&&i<e;case 0:return i<e&&n>e;case 1:return i<=e&&n>e;case 2:return n>e;case 4:return!0}}function ca(t,e,i,n){for(var s;t.from==t.to||(i<1?t.from>=e:t.from>e)||(i>-1?t.to<=e:t.to<e);){let e=!n&&t instanceof ua&&t.index<0?null:t.parent;if(!e)return t;t=e}let r=n?0:ra.IgnoreOverlays;if(n)for(let n=t,o=n.parent;o;n=o,o=n.parent)n instanceof ua&&n.index<0&&(null===(s=o.enter(e,i,r))||void 0===s?void 0:s.from)!=n.from&&(t=o);for(;;){let n=t.enter(e,i,r);if(!n)return t;t=n}}class fa{cursor(t=0){return new va(this,t)}getChild(t,e=null,i=null){let n=da(this,t,e,i);return n.length?n[0]:null}getChildren(t,e=null,i=null){return da(this,t,e,i)}resolve(t,e=0){return ca(this,t,e,!1)}resolveInner(t,e=0){return ca(this,t,e,!0)}matchContext(t){return pa(this,t)}enterUnfinishedNodesBefore(t){let e=this.childBefore(t),i=this;for(;e;){let t=e.lastChild;if(!t||t.to!=e.to)break;t.type.isError&&t.from==t.to?(i=e,e=t.prevSibling):e=t}return i}get node(){return this}get next(){return this.parent}}class ua extends fa{constructor(t,e,i,n){super(),this._tree=t,this.from=e,this.index=i,this._parent=n}get type(){return this._tree.type}get name(){return this._tree.type.name}get to(){return this.from+this._tree.length}nextChild(t,e,i,n,s=0){for(let r=this;;){for(let{children:o,positions:a}=r._tree,l=e>0?o.length:-1;t!=l;t+=e){let l=o[t],h=a[t]+r.from;if(ha(n,i,h,h+l.length))if(l instanceof la){if(s&ra.ExcludeBuffers)continue;let o=l.findChild(0,l.buffer.length,e,i-h,n);if(o>-1)return new ga(new Oa(r,l,t,h),null,o)}else if(s&ra.IncludeAnonymous||!l.type.isAnonymous||ba(l)){let o;if(!(s&ra.IgnoreMounts)&&(o=Ko.get(l))&&!o.overlay)return new ua(o.tree,h,t,r);let a=new ua(l,h,t,r);return s&ra.IncludeAnonymous||!a.type.isAnonymous?a:a.nextChild(e<0?l.children.length-1:0,e,i,n)}}if(s&ra.IncludeAnonymous||!r.type.isAnonymous)return null;if(t=r.index>=0?r.index+e:e<0?-1:r._parent._tree.children.length,r=r._parent,!r)return null}}get firstChild(){return this.nextChild(0,1,0,4)}get lastChild(){return this.nextChild(this._tree.children.length-1,-1,0,4)}childAfter(t){return this.nextChild(0,1,t,2)}childBefore(t){return this.nextChild(this._tree.children.length-1,-1,t,-2)}enter(t,e,i=0){let n;if(!(i&ra.IgnoreOverlays)&&(n=Ko.get(this._tree))&&n.overlay){let i=t-this.from;for(let{from:t,to:s}of n.overlay)if((e>0?t<=i:t<i)&&(e<0?s>=i:s>i))return new ua(n.tree,n.overlay[0].from+this.from,-1,this)}return this.nextChild(0,1,t,e,i)}nextSignificantParent(){let t=this;for(;t.type.isAnonymous&&t._parent;)t=t._parent;return t}get parent(){return this._parent?this._parent.nextSignificantParent():null}get nextSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index+1,1,0,4):null}get prevSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index-1,-1,0,4):null}get tree(){return this._tree}toTree(){return this._tree}toString(){return this._tree.toString()}}function da(t,e,i,n){let s=t.cursor(),r=[];if(!s.firstChild())return r;if(null!=i)for(let t=!1;!t;)if(t=s.type.is(i),!s.nextSibling())return r;for(;;){if(null!=n&&s.type.is(n))return r;if(s.type.is(e)&&r.push(s.node),!s.nextSibling())return null==n?r:[]}}function pa(t,e,i=e.length-1){for(let n=t.parent;i>=0;n=n.parent){if(!n)return!1;if(!n.type.isAnonymous){if(e[i]&&e[i]!=n.name)return!1;i--}}return!0}class Oa{constructor(t,e,i,n){this.parent=t,this.buffer=e,this.index=i,this.start=n}}class ga extends fa{get name(){return this.type.name}get from(){return this.context.start+this.context.buffer.buffer[this.index+1]}get to(){return this.context.start+this.context.buffer.buffer[this.index+2]}constructor(t,e,i){super(),this.context=t,this._parent=e,this.index=i,this.type=t.buffer.set.types[t.buffer.buffer[i]]}child(t,e,i){let{buffer:n}=this.context,s=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.context.start,i);return s<0?null:new ga(this.context,this,s)}get firstChild(){return this.child(1,0,4)}get lastChild(){return this.child(-1,0,4)}childAfter(t){return this.child(1,t,2)}childBefore(t){return this.child(-1,t,-2)}enter(t,e,i=0){if(i&ra.ExcludeBuffers)return null;let{buffer:n}=this.context,s=n.findChild(this.index+4,n.buffer[this.index+3],e>0?1:-1,t-this.context.start,e);return s<0?null:new ga(this.context,this,s)}get parent(){return this._parent||this.context.parent.nextSignificantParent()}externalSibling(t){return this._parent?null:this.context.parent.nextChild(this.context.index+t,t,0,4)}get nextSibling(){let{buffer:t}=this.context,e=t.buffer[this.index+3];return e<(this._parent?t.buffer[this._parent.index+3]:t.buffer.length)?new ga(this.context,this._parent,e):this.externalSibling(1)}get prevSibling(){let{buffer:t}=this.context,e=this._parent?this._parent.index+4:0;return this.index==e?this.externalSibling(-1):new ga(this.context,this._parent,t.findChild(e,this.index,-1,0,4))}get tree(){return null}toTree(){let t=[],e=[],{buffer:i}=this.context,n=this.index+4,s=i.buffer[this.index+3];if(s>n){let r=i.buffer[this.index+1];t.push(i.slice(n,s,r)),e.push(0)}return new oa(this.type,t,e,this.to-this.from)}toString(){return this.context.buffer.childString(this.index)}}function ma(t){if(!t.length)return null;let e=0,i=t[0];for(let n=1;n<t.length;n++){let s=t[n];(s.from>i.from||s.to<i.to)&&(i=s,e=n)}let n=i instanceof ua&&i.index<0?null:i.parent,s=t.slice();return n?s[e]=n:s.splice(e,1),new wa(s,i)}class wa{constructor(t,e){this.heads=t,this.node=e}get next(){return ma(this.heads)}}class va{get name(){return this.type.name}constructor(t,e=0){if(this.mode=e,this.buffer=null,this.stack=[],this.index=0,this.bufferNode=null,t instanceof ua)this.yieldNode(t);else{this._tree=t.context.parent,this.buffer=t.context;for(let e=t._parent;e;e=e._parent)this.stack.unshift(e.index);this.bufferNode=t,this.yieldBuf(t.index)}}yieldNode(t){return!!t&&(this._tree=t,this.type=t.type,this.from=t.from,this.to=t.to,!0)}yieldBuf(t,e){this.index=t;let{start:i,buffer:n}=this.buffer;return this.type=e||n.set.types[n.buffer[t]],this.from=i+n.buffer[t+1],this.to=i+n.buffer[t+2],!0}yield(t){return!!t&&(t instanceof ua?(this.buffer=null,this.yieldNode(t)):(this.buffer=t.context,this.yieldBuf(t.index,t.type)))}toString(){return this.buffer?this.buffer.buffer.childString(this.index):this._tree.toString()}enterChild(t,e,i){if(!this.buffer)return this.yield(this._tree.nextChild(t<0?this._tree._tree.children.length-1:0,t,e,i,this.mode));let{buffer:n}=this.buffer,s=n.findChild(this.index+4,n.buffer[this.index+3],t,e-this.buffer.start,i);return!(s<0)&&(this.stack.push(this.index),this.yieldBuf(s))}firstChild(){return this.enterChild(1,0,4)}lastChild(){return this.enterChild(-1,0,4)}childAfter(t){return this.enterChild(1,t,2)}childBefore(t){return this.enterChild(-1,t,-2)}enter(t,e,i=this.mode){return this.buffer?!(i&ra.ExcludeBuffers)&&this.enterChild(1,t,e):this.yield(this._tree.enter(t,e,i))}parent(){if(!this.buffer)return this.yieldNode(this.mode&ra.IncludeAnonymous?this._tree._parent:this._tree.parent);if(this.stack.length)return this.yieldBuf(this.stack.pop());let t=this.mode&ra.IncludeAnonymous?this.buffer.parent:this.buffer.parent.nextSignificantParent();return this.buffer=null,this.yieldNode(t)}sibling(t){if(!this.buffer)return!!this._tree._parent&&this.yield(this._tree.index<0?null:this._tree._parent.nextChild(this._tree.index+t,t,0,4,this.mode));let{buffer:e}=this.buffer,i=this.stack.length-1;if(t<0){let t=i<0?0:this.stack[i]+4;if(this.index!=t)return this.yieldBuf(e.findChild(t,this.index,-1,0,4))}else{let t=e.buffer[this.index+3];if(t<(i<0?e.buffer.length:e.buffer[this.stack[i]+3]))return this.yieldBuf(t)}return i<0&&this.yield(this.buffer.parent.nextChild(this.buffer.index+t,t,0,4,this.mode))}nextSibling(){return this.sibling(1)}prevSibling(){return this.sibling(-1)}atLastNode(t){let e,i,{buffer:n}=this;if(n){if(t>0){if(this.index<n.buffer.buffer.length)return!1}else for(let t=0;t<this.index;t++)if(n.buffer.buffer[t+3]<this.index)return!1;({index:e,parent:i}=n)}else({index:e,_parent:i}=this._tree);for(;i;({index:e,_parent:i}=i))if(e>-1)for(let n=e+t,s=t<0?-1:i._tree.children.length;n!=s;n+=t){let t=i._tree.children[n];if(this.mode&ra.IncludeAnonymous||t instanceof la||!t.type.isAnonymous||ba(t))return!1}return!0}move(t,e){if(e&&this.enterChild(t,0,4))return!0;for(;;){if(this.sibling(t))return!0;if(this.atLastNode(t)||!this.parent())return!1}}next(t=!0){return this.move(1,t)}prev(t=!0){return this.move(-1,t)}moveTo(t,e=0){for(;(this.from==this.to||(e<1?this.from>=t:this.from>t)||(e>-1?this.to<=t:this.to<t))&&this.parent(););for(;this.enterChild(1,t,e););return this}get node(){if(!this.buffer)return this._tree;let t=this.bufferNode,e=null,i=0;if(t&&t.context==this.buffer)t:for(let n=this.index,s=this.stack.length;s>=0;){for(let r=t;r;r=r._parent)if(r.index==n){if(n==this.index)return r;e=r,i=s+1;break t}n=this.stack[--s]}for(let t=i;t<this.stack.length;t++)e=new ga(this.buffer,e,this.stack[t]);return this.bufferNode=new ga(this.buffer,e,this.index)}get tree(){return this.buffer?null:this._tree._tree}iterate(t,e){for(let i=0;;){let n=!1;if(this.type.isAnonymous||!1!==t(this)){if(this.firstChild()){i++;continue}this.type.isAnonymous||(n=!0)}for(;n&&e&&e(this),n=this.type.isAnonymous,!this.nextSibling();){if(!i)return;this.parent(),i--,n=!0}}}matchContext(t){if(!this.buffer)return pa(this.node,t);let{buffer:e}=this.buffer,{types:i}=e.set;for(let n=t.length-1,s=this.stack.length-1;n>=0;s--){if(s<0)return pa(this.node,t,n);let r=i[e.buffer[this.stack[s]]];if(!r.isAnonymous){if(t[n]&&t[n]!=r.name)return!1;n--}}return!0}}function ba(t){return t.children.some((t=>t instanceof la||!t.type.isAnonymous||ba(t)))}const ya=new WeakMap;function Sa(t,e){if(!t.isAnonymous||e instanceof la||e.type!=t)return 1;let i=ya.get(e);if(null==i){i=1;for(let n of e.children){if(n.type!=t||!(n instanceof oa)){i=1;break}i+=Sa(t,n)}ya.set(e,i)}return i}function xa(t,e,i,n,s,r,o,a,l){let h=0;for(let i=n;i<s;i++)h+=Sa(t,e[i]);let c=Math.ceil(1.5*h/8),f=[],u=[];return function e(i,n,s,o,a){for(let h=s;h<o;){let s=h,d=n[h],p=Sa(t,i[h]);for(h++;h<o;h++){let e=Sa(t,i[h]);if(p+e>=c)break;p+=e}if(h==s+1){if(p>c){let t=i[s];e(t.children,t.positions,0,t.children.length,n[s]+a);continue}f.push(i[s])}else{let e=n[h-1]+i[h-1].length-d;f.push(xa(t,i,n,s,h,d,e,null,l))}u.push(d+a-r)}}(e,i,n,s,0),(a||l)(f,u,o)}class ka{constructor(){this.map=new WeakMap}setBuffer(t,e,i){let n=this.map.get(t);n||this.map.set(t,n=new Map),n.set(e,i)}getBuffer(t,e){let i=this.map.get(t);return i&&i.get(e)}set(t,e){t instanceof ga?this.setBuffer(t.context.buffer,t.index,e):t instanceof ua&&this.map.set(t.tree,e)}get(t){return t instanceof ga?this.getBuffer(t.context.buffer,t.index):t instanceof ua?this.map.get(t.tree):void 0}cursorSet(t,e){t.buffer?this.setBuffer(t.buffer.buffer,t.index,e):this.map.set(t.tree,e)}cursorGet(t){return t.buffer?this.getBuffer(t.buffer.buffer,t.index):this.map.get(t.tree)}}class Qa{constructor(t,e,i,n,s=!1,r=!1){this.from=t,this.to=e,this.tree=i,this.offset=n,this.open=(s?1:0)|(r?2:0)}get openStart(){return(1&this.open)>0}get openEnd(){return(2&this.open)>0}static addTree(t,e=[],i=!1){let n=[new Qa(0,t.length,t,0,!1,i)];for(let i of e)i.to>t.length&&n.push(i);return n}static applyChanges(t,e,i=128){if(!e.length)return t;let n=[],s=1,r=t.length?t[0]:null;for(let o=0,a=0,l=0;;o++){let h=o<e.length?e[o]:null,c=h?h.fromA:1e9;if(c-a>=i)for(;r&&r.from<c;){let e=r;if(a>=e.from||c<=e.to||l){let t=Math.max(e.from,a)-l,i=Math.min(e.to,c)-l;e=t>=i?null:new Qa(t,i,e.tree,e.offset+l,o>0,!!h)}if(e&&n.push(e),r.to>c)break;r=s<t.length?t[s++]:null}if(!h)break;a=h.toA,l=h.toA-h.toB}return n}}class $a{startParse(t,e,i){return"string"==typeof t&&(t=new Pa(t)),i=i?i.length?i.map((t=>new Fo(t.from,t.to))):[new Fo(0,0)]:[new Fo(0,t.length)],this.createParse(t,e||[],i)}parse(t,e,i){let n=this.startParse(t,e,i);for(;;){let t=n.advance();if(t)return t}}}class Pa{constructor(t){this.string=t}get length(){return this.string.length}chunk(t){return this.string.slice(t)}get lineChunks(){return!1}read(t,e){return this.string.slice(t,e)}}new Jo({perNode:!0});let Za=0;class Ca{constructor(t,e,i){this.set=t,this.base=e,this.modified=i,this.id=Za++}static define(t){if(null==t?void 0:t.base)throw new Error("Can not derive from a modified tag");let e=new Ca([],null,[]);if(e.set.push(e),t)for(let i of t.set)e.set.push(i);return e}static defineModifier(){let t=new Aa;return e=>e.modified.indexOf(t)>-1?e:Aa.get(e.base||e,e.modified.concat(t).sort(((t,e)=>t.id-e.id)))}}let Ta=0;class Aa{constructor(){this.instances=[],this.id=Ta++}static get(t,e){if(!e.length)return t;let i=e[0].instances.find((i=>{return i.base==t&&(n=e,s=i.modified,n.length==s.length&&n.every(((t,e)=>t==s[e])));var n,s}));if(i)return i;let n=[],s=new Ca(n,t,e);for(let t of e)t.instances.push(s);let r=function(t){let e=[[]];for(let i=0;i<t.length;i++)for(let n=0,s=e.length;n<s;n++)e.push(e[n].concat(t[i]));return e.sort(((t,e)=>e.length-t.length))}(e);for(let e of t.set)if(!e.modified.length)for(let t of r)n.push(Aa.get(e,t));return s}}function Ra(t){let e=Object.create(null);for(let i in t){let n=t[i];Array.isArray(n)||(n=[n]);for(let t of i.split(" "))if(t){let i=[],s=2,r=t;for(let e=0;;){if("..."==r&&e>0&&e+3==t.length){s=1;break}let n=/^"(?:[^"\\]|\\.)*?"|[^\/!]+/.exec(r);if(!n)throw new RangeError("Invalid path: "+t);if(i.push("*"==n[0]?"":'"'==n[0][0]?JSON.parse(n[0]):n[0]),e+=n[0].length,e==t.length)break;let o=t[e++];if(e==t.length&&"!"==o){s=0;break}if("/"!=o)throw new RangeError("Invalid path: "+t);r=t.slice(e)}let o=i.length-1,a=i[o];if(!a)throw new RangeError("Invalid path: "+t);let l=new Ya(n,s,o>0?i.slice(0,o):null);e[a]=l.sort(e[a])}}return Xa.add(e)}const Xa=new Jo;class Ya{constructor(t,e,i,n){this.tags=t,this.mode=e,this.context=i,this.next=n}get opaque(){return 0==this.mode}get inherit(){return 1==this.mode}sort(t){return!t||t.depth<this.depth?(this.next=t,this):(t.next=this.sort(t.next),t)}get depth(){return this.context?this.context.length:0}}function Wa(t,e){let i=Object.create(null);for(let e of t)if(Array.isArray(e.tag))for(let t of e.tag)i[t.id]=e.class;else i[e.tag.id]=e.class;let{scope:n,all:s=null}=e||{};return{style:t=>{let e=s;for(let n of t)for(let t of n.set){let n=i[t.id];if(n){e=e?e+" "+n:n;break}}return e},scope:n}}function Ma(t,e,i,n=0,s=t.length){let r=new ja(n,Array.isArray(e)?e:[e],i);r.highlightRange(t.cursor(),n,s,"",r.highlighters),r.flush(s)}Ya.empty=new Ya([],2,null);class ja{constructor(t,e,i){this.at=t,this.highlighters=e,this.span=i,this.class=""}startSpan(t,e){e!=this.class&&(this.flush(t),t>this.at&&(this.at=t),this.class=e)}flush(t){t>this.at&&this.class&&this.span(this.at,t,this.class)}highlightRange(t,e,i,n,s){let{type:r,from:o,to:a}=t;if(o>=i||a<=e)return;r.isTop&&(s=this.highlighters.filter((t=>!t.scope||t.scope(r))));let l=n,h=Da(t)||Ya.empty,c=function(t,e){let i=null;for(let n of t){let t=n.style(e);t&&(i=i?i+" "+t:t)}return i}(s,h.tags);if(c&&(l&&(l+=" "),l+=c,1==h.mode&&(n+=(n?" ":"")+c)),this.startSpan(Math.max(e,o),l),h.opaque)return;let f=t.tree&&t.tree.prop(Jo.mounted);if(f&&f.overlay){let r=t.node.enter(f.overlay[0].from+o,1),h=this.highlighters.filter((t=>!t.scope||t.scope(f.tree.type))),c=t.firstChild();for(let u=0,d=o;;u++){let p=u<f.overlay.length?f.overlay[u]:null,O=p?p.from+o:a,g=Math.max(e,d),m=Math.min(i,O);if(g<m&&c)for(;t.from<m&&(this.highlightRange(t,g,m,n,s),this.startSpan(Math.min(m,t.to),l),!(t.to>=O)&&t.nextSibling()););if(!p||O>i)break;d=p.to+o,d>e&&(this.highlightRange(r.cursor(),Math.max(e,p.from+o),Math.min(i,d),"",h),this.startSpan(Math.min(i,d),l))}c&&t.parent()}else if(t.firstChild()){f&&(n="");do{if(!(t.to<=e)){if(t.from>=i)break;this.highlightRange(t,e,i,n,s),this.startSpan(Math.min(i,t.to),l)}}while(t.nextSibling());t.parent()}}}function Da(t){let e=t.type.prop(Xa);for(;e&&e.context&&!t.matchContext(e.context);)e=e.next;return e||null}const Ea=Ca.define,qa=Ea(),_a=Ea(),Va=Ea(_a),Ia=Ea(_a),za=Ea(),Ba=Ea(za),Ga=Ea(za),La=Ea(),Na=Ea(La),Ua=Ea(),Ha=Ea(),Fa=Ea(),Ja=Ea(Fa),Ka=Ea(),tl={comment:qa,lineComment:Ea(qa),blockComment:Ea(qa),docComment:Ea(qa),name:_a,variableName:Ea(_a),typeName:Va,tagName:Ea(Va),propertyName:Ia,attributeName:Ea(Ia),className:Ea(_a),labelName:Ea(_a),namespace:Ea(_a),macroName:Ea(_a),literal:za,string:Ba,docString:Ea(Ba),character:Ea(Ba),attributeValue:Ea(Ba),number:Ga,integer:Ea(Ga),float:Ea(Ga),bool:Ea(za),regexp:Ea(za),escape:Ea(za),color:Ea(za),url:Ea(za),keyword:Ua,self:Ea(Ua),null:Ea(Ua),atom:Ea(Ua),unit:Ea(Ua),modifier:Ea(Ua),operatorKeyword:Ea(Ua),controlKeyword:Ea(Ua),definitionKeyword:Ea(Ua),moduleKeyword:Ea(Ua),operator:Ha,derefOperator:Ea(Ha),arithmeticOperator:Ea(Ha),logicOperator:Ea(Ha),bitwiseOperator:Ea(Ha),compareOperator:Ea(Ha),updateOperator:Ea(Ha),definitionOperator:Ea(Ha),typeOperator:Ea(Ha),controlOperator:Ea(Ha),punctuation:Fa,separator:Ea(Fa),bracket:Ja,angleBracket:Ea(Ja),squareBracket:Ea(Ja),paren:Ea(Ja),brace:Ea(Ja),content:La,heading:Na,heading1:Ea(Na),heading2:Ea(Na),heading3:Ea(Na),heading4:Ea(Na),heading5:Ea(Na),heading6:Ea(Na),contentSeparator:Ea(La),list:Ea(La),quote:Ea(La),emphasis:Ea(La),strong:Ea(La),link:Ea(La),monospace:Ea(La),strikethrough:Ea(La),inserted:Ea(),deleted:Ea(),changed:Ea(),invalid:Ea(),meta:Ka,documentMeta:Ea(Ka),annotation:Ea(Ka),processingInstruction:Ea(Ka),definition:Ca.defineModifier(),constant:Ca.defineModifier(),function:Ca.defineModifier(),standard:Ca.defineModifier(),local:Ca.defineModifier(),special:Ca.defineModifier()},el=Wa([{tag:tl.link,class:"tok-link"},{tag:tl.heading,class:"tok-heading"},{tag:tl.emphasis,class:"tok-emphasis"},{tag:tl.strong,class:"tok-strong"},{tag:tl.keyword,class:"tok-keyword"},{tag:tl.atom,class:"tok-atom"},{tag:tl.bool,class:"tok-bool"},{tag:tl.url,class:"tok-url"},{tag:tl.labelName,class:"tok-labelName"},{tag:tl.inserted,class:"tok-inserted"},{tag:tl.deleted,class:"tok-deleted"},{tag:tl.literal,class:"tok-literal"},{tag:tl.string,class:"tok-string"},{tag:tl.number,class:"tok-number"},{tag:[tl.regexp,tl.escape,tl.special(tl.string)],class:"tok-string2"},{tag:tl.variableName,class:"tok-variableName"},{tag:tl.local(tl.variableName),class:"tok-variableName tok-local"},{tag:tl.definition(tl.variableName),class:"tok-variableName tok-definition"},{tag:tl.special(tl.variableName),class:"tok-variableName2"},{tag:tl.definition(tl.propertyName),class:"tok-propertyName tok-definition"},{tag:tl.typeName,class:"tok-typeName"},{tag:tl.namespace,class:"tok-namespace"},{tag:tl.className,class:"tok-className"},{tag:tl.macroName,class:"tok-macroName"},{tag:tl.propertyName,class:"tok-propertyName"},{tag:tl.operator,class:"tok-operator"},{tag:tl.comment,class:"tok-comment"},{tag:tl.meta,class:"tok-meta"},{tag:tl.invalid,class:"tok-invalid"},{tag:tl.punctuation,class:"tok-punctuation"}]);var il,nl=Object.freeze({__proto__:null,Tag:Ca,classHighlighter:el,getStyleTags:Da,highlightCode:function(t,e,i,n,s,r=0,o=t.length){let a=r;function l(e,i){if(!(e<=a)){for(let r=t.slice(a,e),o=0;;){let t=r.indexOf("\n",o),e=t<0?r.length:t;if(e>o&&n(r.slice(o,e),i),t<0)break;s(),o=t+1}a=e}}Ma(e,i,((t,e,i)=>{l(t,""),l(e,i)}),r,o),l(o,"")},highlightTree:Ma,styleTags:Ra,tagHighlighter:Wa,tags:tl});const sl=new Jo;function rl(t){return j.define({combine:t?e=>e.concat(t):void 0})}const ol=new Jo;class al{constructor(t,e,i=[],n=""){this.data=t,this.name=n,Qt.prototype.hasOwnProperty("tree")||Object.defineProperty(Qt.prototype,"tree",{get(){return cl(this)}}),this.parser=e,this.extension=[bl.of(this),Qt.languageData.of(((t,e,i)=>{let n=ll(t,e,i),s=n.type.prop(sl);if(!s)return[];let r=t.facet(s),o=n.type.prop(ol);if(o){let s=n.resolve(e-n.from,i);for(let e of o)if(e.test(s,t)){let i=t.facet(e.facet);return"replace"==e.type?i:i.concat(r)}}return r}))].concat(i)}isActiveAt(t,e,i=-1){return ll(t,e,i).type.prop(sl)==this.data}findRegions(t){let e=t.facet(bl);if((null==e?void 0:e.data)==this.data)return[{from:0,to:t.doc.length}];if(!e||!e.allowsNesting)return[];let i=[],n=(t,e)=>{if(t.prop(sl)==this.data)return void i.push({from:e,to:e+t.length});let s=t.prop(Jo.mounted);if(s){if(s.tree.prop(sl)==this.data){if(s.overlay)for(let t of s.overlay)i.push({from:t.from+e,to:t.to+e});else i.push({from:e,to:e+t.length});return}if(s.overlay){let t=i.length;if(n(s.tree,s.overlay[0].from+e),i.length>t)return}}for(let i=0;i<t.children.length;i++){let s=t.children[i];s instanceof oa&&n(s,t.positions[i]+e)}};return n(cl(t),0),i}get allowsNesting(){return!0}}function ll(t,e,i){let n=t.facet(bl),s=cl(t).topNode;if(!n||n.allowsNesting)for(let t=s;t;t=t.enter(e,i,ra.ExcludeBuffers))t.type.isTop&&(s=t);return s}al.setState=dt.define();class hl extends al{constructor(t,e,i){super(t,e,[],i),this.parser=e}static define(t){let e=rl(t.languageData);return new hl(e,t.parser.configure({props:[sl.add((t=>t.isTop?e:void 0))]}),t.name)}configure(t,e){return new hl(this.data,this.parser.configure(t),e||this.name)}get allowsNesting(){return this.parser.hasWrappers()}}function cl(t){let e=t.field(al.state,!1);return e?e.tree:oa.empty}function fl(t,e,i=50){var n;let s=null===(n=t.field(al.state,!1))||void 0===n?void 0:n.context;if(!s)return null;let r=s.viewport;s.updateViewport({from:0,to:e});let o=s.isDone(e)||s.work(i,e)?s.tree:null;return s.updateViewport(r),o}class ul{constructor(t){this.doc=t,this.cursorPos=0,this.string="",this.cursor=t.iter()}get length(){return this.doc.length}syncTo(t){return this.string=this.cursor.next(t-this.cursorPos).value,this.cursorPos=t+this.string.length,this.cursorPos-this.string.length}chunk(t){return this.syncTo(t),this.string}get lineChunks(){return!0}read(t,e){let i=this.cursorPos-this.string.length;return t<i||e>=this.cursorPos?this.doc.sliceString(t,e):this.string.slice(t-i,e-i)}}let dl=null;class pl{constructor(t,e,i=[],n,s,r,o,a){this.parser=t,this.state=e,this.fragments=i,this.tree=n,this.treeLen=s,this.viewport=r,this.skipped=o,this.scheduleOn=a,this.parse=null,this.tempSkipped=[]}static create(t,e,i){return new pl(t,e,[],oa.empty,0,i,[],null)}startParse(){return this.parser.startParse(new ul(this.state.doc),this.fragments)}work(t,e){return null!=e&&e>=this.state.doc.length&&(e=void 0),this.tree!=oa.empty&&this.isDone(null!=e?e:this.state.doc.length)?(this.takeTree(),!0):this.withContext((()=>{var i;if("number"==typeof t){let e=Date.now()+t;t=()=>Date.now()>e}for(this.parse||(this.parse=this.startParse()),null!=e&&(null==this.parse.stoppedAt||this.parse.stoppedAt>e)&&e<this.state.doc.length&&this.parse.stopAt(e);;){let n=this.parse.advance();if(n){if(this.fragments=this.withoutTempSkipped(Qa.addTree(n,this.fragments,null!=this.parse.stoppedAt)),this.treeLen=null!==(i=this.parse.stoppedAt)&&void 0!==i?i:this.state.doc.length,this.tree=n,this.parse=null,!(this.treeLen<(null!=e?e:this.state.doc.length)))return!0;this.parse=this.startParse()}if(t())return!1}}))}takeTree(){let t,e;this.parse&&(t=this.parse.parsedPos)>=this.treeLen&&((null==this.parse.stoppedAt||this.parse.stoppedAt>t)&&this.parse.stopAt(t),this.withContext((()=>{for(;!(e=this.parse.advance()););})),this.treeLen=t,this.tree=e,this.fragments=this.withoutTempSkipped(Qa.addTree(this.tree,this.fragments,!0)),this.parse=null)}withContext(t){let e=dl;dl=this;try{return t()}finally{dl=e}}withoutTempSkipped(t){for(let e;e=this.tempSkipped.pop();)t=Ol(t,e.from,e.to);return t}changes(t,e){let{fragments:i,tree:n,treeLen:s,viewport:r,skipped:o}=this;if(this.takeTree(),!t.empty){let e=[];if(t.iterChangedRanges(((t,i,n,s)=>e.push({fromA:t,toA:i,fromB:n,toB:s}))),i=Qa.applyChanges(i,e),n=oa.empty,s=0,r={from:t.mapPos(r.from,-1),to:t.mapPos(r.to,1)},this.skipped.length){o=[];for(let e of this.skipped){let i=t.mapPos(e.from,1),n=t.mapPos(e.to,-1);i<n&&o.push({from:i,to:n})}}}return new pl(this.parser,e,i,n,s,r,o,this.scheduleOn)}updateViewport(t){if(this.viewport.from==t.from&&this.viewport.to==t.to)return!1;this.viewport=t;let e=this.skipped.length;for(let e=0;e<this.skipped.length;e++){let{from:i,to:n}=this.skipped[e];i<t.to&&n>t.from&&(this.fragments=Ol(this.fragments,i,n),this.skipped.splice(e--,1))}return!(this.skipped.length>=e)&&(this.reset(),!0)}reset(){this.parse&&(this.takeTree(),this.parse=null)}skipUntilInView(t,e){this.skipped.push({from:t,to:e})}static getSkippingParser(t){return new class extends $a{createParse(e,i,n){let s=n[0].from,r=n[n.length-1].to;return{parsedPos:s,advance(){let e=dl;if(e){for(let t of n)e.tempSkipped.push(t);t&&(e.scheduleOn=e.scheduleOn?Promise.all([e.scheduleOn,t]):t)}return this.parsedPos=r,new oa(ea.none,[],[],r-s)},stoppedAt:null,stopAt(){}}}}}isDone(t){t=Math.min(t,this.state.doc.length);let e=this.fragments;return this.treeLen>=t&&e.length&&0==e[0].from&&e[0].to>=t}static get(){return dl}}function Ol(t,e,i){return Qa.applyChanges(t,[{fromA:e,toA:i,fromB:e,toB:i}])}class gl{constructor(t){this.context=t,this.tree=t.tree}apply(t){if(!t.docChanged&&this.tree==this.context.tree)return this;let e=this.context.changes(t.changes,t.state),i=this.context.treeLen==t.startState.doc.length?void 0:Math.max(t.changes.mapPos(this.context.treeLen),e.viewport.to);return e.work(20,i)||e.takeTree(),new gl(e)}static init(t){let e=Math.min(3e3,t.doc.length),i=pl.create(t.facet(bl).parser,t,{from:0,to:e});return i.work(20,e)||i.takeTree(),new gl(i)}}al.state=z.define({create:gl.init,update(t,e){for(let t of e.effects)if(t.is(al.setState))return t.value;return e.startState.facet(bl)!=e.state.facet(bl)?gl.init(e.state):t.apply(e)}});let ml=t=>{let e=setTimeout((()=>t()),500);return()=>clearTimeout(e)};"undefined"!=typeof requestIdleCallback&&(ml=t=>{let e=-1,i=setTimeout((()=>{e=requestIdleCallback(t,{timeout:400})}),100);return()=>e<0?clearTimeout(i):cancelIdleCallback(e)});const wl="undefined"!=typeof navigator&&(null===(il=navigator.scheduling)||void 0===il?void 0:il.isInputPending)?()=>navigator.scheduling.isInputPending():null,vl=Ni.fromClass(class{constructor(t){this.view=t,this.working=null,this.workScheduled=0,this.chunkEnd=-1,this.chunkBudget=-1,this.work=this.work.bind(this),this.scheduleWork()}update(t){let e=this.view.state.field(al.state).context;(e.updateViewport(t.view.viewport)||this.view.viewport.to>e.treeLen)&&this.scheduleWork(),(t.docChanged||t.selectionSet)&&(this.view.hasFocus&&(this.chunkBudget+=50),this.scheduleWork()),this.checkAsyncSchedule(e)}scheduleWork(){if(this.working)return;let{state:t}=this.view,e=t.field(al.state);e.tree==e.context.tree&&e.context.isDone(t.doc.length)||(this.working=ml(this.work))}work(t){this.working=null;let e=Date.now();if(this.chunkEnd<e&&(this.chunkEnd<0||this.view.hasFocus)&&(this.chunkEnd=e+3e4,this.chunkBudget=3e3),this.chunkBudget<=0)return;let{state:i,viewport:{to:n}}=this.view,s=i.field(al.state);if(s.tree==s.context.tree&&s.context.isDone(n+1e5))return;let r=Date.now()+Math.min(this.chunkBudget,100,t&&!wl?Math.max(25,t.timeRemaining()-5):1e9),o=s.context.treeLen<n&&i.doc.length>n+1e3,a=s.context.work((()=>wl&&wl()||Date.now()>r),n+(o?0:1e5));this.chunkBudget-=Date.now()-e,(a||this.chunkBudget<=0)&&(s.context.takeTree(),this.view.dispatch({effects:al.setState.of(new gl(s.context))})),this.chunkBudget>0&&(!a||o)&&this.scheduleWork(),this.checkAsyncSchedule(s.context)}checkAsyncSchedule(t){t.scheduleOn&&(this.workScheduled++,t.scheduleOn.then((()=>this.scheduleWork())).catch((t=>zi(this.view.state,t))).then((()=>this.workScheduled--)),t.scheduleOn=null)}destroy(){this.working&&this.working()}isWorking(){return!!(this.working||this.workScheduled>0)}},{eventHandlers:{focus(){this.scheduleWork()}}}),bl=j.define({combine:t=>t.length?t[0]:null,enables:t=>[al.state,vl,Gs.contentAttributes.compute([t],(e=>{let i=e.facet(t);return i&&i.name?{"data-language":i.name}:{}}))]});class yl{constructor(t,e=[]){this.language=t,this.support=e,this.extension=[t,e]}}class Sl{constructor(t,e,i,n,s,r=void 0){this.name=t,this.alias=e,this.extensions=i,this.filename=n,this.loadFunc=s,this.support=r,this.loading=null}load(){return this.loading||(this.loading=this.loadFunc().then((t=>this.support=t),(t=>{throw this.loading=null,t})))}static of(t){let{load:e,support:i}=t;if(!e){if(!i)throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");e=()=>Promise.resolve(i)}return new Sl(t.name,(t.alias||[]).concat(t.name).map((t=>t.toLowerCase())),t.extensions||[],t.filename,e,i)}static matchFilename(t,e){for(let i of t)if(i.filename&&i.filename.test(e))return i;let i=/\.([^.]+)$/.exec(e);if(i)for(let e of t)if(e.extensions.indexOf(i[1])>-1)return e;return null}static matchLanguageName(t,e,i=!0){e=e.toLowerCase();for(let i of t)if(i.alias.some((t=>t==e)))return i;if(i)for(let i of t)for(let t of i.alias){let n=e.indexOf(t);if(n>-1&&(t.length>2||!/\w/.test(e[n-1])&&!/\w/.test(e[n+t.length])))return i}return null}}const xl=j.define(),kl=j.define({combine:t=>{if(!t.length)return" ";let e=t[0];if(!e||/\S/.test(e)||Array.from(e).some((t=>t!=e[0])))throw new Error("Invalid indent unit: "+JSON.stringify(t[0]));return e}});function Ql(t){let e=t.facet(kl);return 9==e.charCodeAt(0)?t.tabSize*e.length:e.length}function $l(t,e){let i="",n=t.tabSize,s=t.facet(kl)[0];if("\t"==s){for(;e>=n;)i+="\t",e-=n;s=" "}for(let t=0;t<e;t++)i+=s;return i}function Pl(t,e){t instanceof Qt&&(t=new Zl(t));for(let i of t.state.facet(xl)){let n=i(t,e);if(void 0!==n)return n}let i=cl(t.state);return i.length>=e?function(t,e,i){let n=e.resolveStack(i),s=n.node.enterUnfinishedNodesBefore(i);if(s!=n.node){let t=[];for(let e=s;e!=n.node;e=e.parent)t.push(e);for(let e=t.length-1;e>=0;e--)n={node:t[e],next:n}}return Tl(n,t,i)}(t,i,e):null}class Zl{constructor(t,e={}){this.state=t,this.options=e,this.unit=Ql(t)}lineAt(t,e=1){let i=this.state.doc.lineAt(t),{simulateBreak:n,simulateDoubleBreak:s}=this.options;return null!=n&&n>=i.from&&n<=i.to?s&&n==t?{text:"",from:t}:(e<0?n<t:n<=t)?{text:i.text.slice(n-i.from),from:n}:{text:i.text.slice(0,n-i.from),from:i.from}:i}textAfterPos(t,e=1){if(this.options.simulateDoubleBreak&&t==this.options.simulateBreak)return"";let{text:i,from:n}=this.lineAt(t,e);return i.slice(t-n,Math.min(i.length,t+100-n))}column(t,e=1){let{text:i,from:n}=this.lineAt(t,e),s=this.countColumn(i,t-n),r=this.options.overrideIndentation?this.options.overrideIndentation(n):-1;return r>-1&&(s+=r-this.countColumn(i,i.search(/\S|$/))),s}countColumn(t,e=t.length){return It(t,this.state.tabSize,e)}lineIndent(t,e=1){let{text:i,from:n}=this.lineAt(t,e),s=this.options.overrideIndentation;if(s){let t=s(n);if(t>-1)return t}return this.countColumn(i,i.search(/\S|$/))}get simulatedBreak(){return this.options.simulateBreak||null}}const Cl=new Jo;function Tl(t,e,i){for(let n=t;n;n=n.next){let t=Al(n.node);if(t)return t(Xl.create(e,i,n))}return 0}function Al(t){let e=t.type.prop(Cl);if(e)return e;let i,n=t.firstChild;if(n&&(i=n.type.prop(Jo.closedBy))){let e=t.lastChild,n=e&&i.indexOf(e.name)>-1;return t=>Ml(t,!0,1,void 0,n&&!function(t){return t.pos==t.options.simulateBreak&&t.options.simulateDoubleBreak}(t)?e.from:void 0)}return null==t.parent?Rl:null}function Rl(){return 0}class Xl extends Zl{constructor(t,e,i){super(t.state,t.options),this.base=t,this.pos=e,this.context=i}get node(){return this.context.node}static create(t,e,i){return new Xl(t,e,i)}get textAfter(){return this.textAfterPos(this.pos)}get baseIndent(){return this.baseIndentFor(this.node)}baseIndentFor(t){let e=this.state.doc.lineAt(t.from);for(;;){let i=t.resolve(e.from);for(;i.parent&&i.parent.from==i.from;)i=i.parent;if(Yl(i,t))break;e=this.state.doc.lineAt(i.from)}return this.lineIndent(e.from)}continue(){return Tl(this.context.next,this.base,this.pos)}}function Yl(t,e){for(let i=e;i;i=i.parent)if(t==i)return!0;return!1}function Wl({closing:t,align:e=!0,units:i=1}){return n=>Ml(n,e,i,t)}function Ml(t,e,i,n,s){let r=t.textAfter,o=r.match(/^\s*/)[0].length,a=n&&r.slice(o,o+n.length)==n||s==t.pos+o,l=e?function(t){let e=t.node,i=e.childAfter(e.from),n=e.lastChild;if(!i)return null;let s=t.options.simulateBreak,r=t.state.doc.lineAt(i.from),o=null==s||s<=r.from?r.to:Math.min(r.to,s);for(let t=i.to;;){let s=e.childAfter(t);if(!s||s==n)return null;if(!s.type.isSkipped)return s.from<o?i:null;t=s.to}}(t):null;return l?a?t.column(l.from):t.column(l.to):t.baseIndent+(a?0:t.unit*i)}const jl=t=>t.baseIndent;function Dl({except:t,units:e=1}={}){return i=>{let n=t&&t.test(i.textAfter);return i.baseIndent+(n?0:e*i.unit)}}function El(){return Qt.transactionFilter.of((t=>{if(!t.docChanged||!t.isUserEvent("input.type")&&!t.isUserEvent("input.complete"))return t;let e=t.startState.languageDataAt("indentOnInput",t.startState.selection.main.head);if(!e.length)return t;let i=t.newDoc,{head:n}=t.newSelection.main,s=i.lineAt(n);if(n>s.from+200)return t;let r=i.sliceString(s.from,n);if(!e.some((t=>t.test(r))))return t;let{state:o}=t,a=-1,l=[];for(let{head:t}of o.selection.ranges){let e=o.doc.lineAt(t);if(e.from==a)continue;a=e.from;let i=Pl(o,e.from);if(null==i)continue;let n=/^\s*/.exec(e.text)[0],s=$l(o,i);n!=s&&l.push({from:e.from,to:e.from+n.length,insert:s})}return l.length?[t,{changes:l,sequential:!0}]:t}))}const ql=j.define(),_l=new Jo;function Vl(t){let e=t.firstChild,i=t.lastChild;return e&&e.to<i.from?{from:e.to,to:i.type.isError?t.to:i.from}:null}function Il(t){let e=t.lastChild;return e&&e.to==t.to&&e.type.isError}function zl(t,e,i){for(let n of t.facet(ql)){let s=n(t,e,i);if(s)return s}return function(t,e,i){let n=cl(t);if(n.length<i)return null;let s=null;for(let r=n.resolveStack(i,1);r;r=r.next){let o=r.node;if(o.to<=i||o.from>i)continue;if(s&&o.from<e)break;let a=o.type.prop(_l);if(a&&(o.to<n.length-50||n.length==t.doc.length||!Il(o))){let n=a(o,t);n&&n.from<=i&&n.from>=e&&n.to>i&&(s=n)}}return s}(t,e,i)}function Bl(t,e){let i=e.mapPos(t.from,1),n=e.mapPos(t.to,-1);return i>=n?void 0:{from:i,to:n}}const Gl=dt.define({map:Bl}),Ll=dt.define({map:Bl});function Nl(t){let e=[];for(let{head:i}of t.state.selection.ranges)e.some((t=>t.from<=i&&t.to>=i))||e.push(t.lineBlockAt(i));return e}const Ul=z.define({create:()=>si.none,update(t,e){t=t.map(e.changes);for(let i of e.effects)if(i.is(Gl)&&!Fl(t,i.value.from,i.value.to)){let{preparePlaceholder:n}=e.state.facet(ah),s=n?si.replace({widget:new fh(n(e.state,i.value))}):ch;t=t.update({add:[s.range(i.value.from,i.value.to)]})}else i.is(Ll)&&(t=t.update({filter:(t,e)=>i.value.from!=t||i.value.to!=e,filterFrom:i.value.from,filterTo:i.value.to}));if(e.selection){let i=!1,{head:n}=e.selection.main;t.between(n,n,((t,e)=>{t<n&&e>n&&(i=!0)})),i&&(t=t.update({filterFrom:n,filterTo:n,filter:(t,e)=>e<=n||t>=n}))}return t},provide:t=>Gs.decorations.from(t),toJSON(t,e){let i=[];return t.between(0,e.doc.length,((t,e)=>{i.push(t,e)})),i},fromJSON(t){if(!Array.isArray(t)||t.length%2)throw new RangeError("Invalid JSON for fold state");let e=[];for(let i=0;i<t.length;){let n=t[i++],s=t[i++];if("number"!=typeof n||"number"!=typeof s)throw new RangeError("Invalid JSON for fold state");e.push(ch.range(n,s))}return si.set(e,!0)}});function Hl(t,e,i){var n;let s=null;return null===(n=t.field(Ul,!1))||void 0===n||n.between(e,i,((t,e)=>{(!s||s.from>t)&&(s={from:t,to:e})})),s}function Fl(t,e,i){let n=!1;return t.between(e,e,((t,s)=>{t==e&&s==i&&(n=!0)})),n}function Jl(t,e){return t.field(Ul,!1)?e:e.concat(dt.appendConfig.of(lh()))}const Kl=t=>{for(let e of Nl(t)){let i=zl(t.state,e.from,e.to);if(i)return t.dispatch({effects:Jl(t.state,[Gl.of(i),eh(t,i)])}),!0}return!1},th=t=>{if(!t.state.field(Ul,!1))return!1;let e=[];for(let i of Nl(t)){let n=Hl(t.state,i.from,i.to);n&&e.push(Ll.of(n),eh(t,n,!1))}return e.length&&t.dispatch({effects:e}),e.length>0};function eh(t,e,i=!0){let n=t.state.doc.lineAt(e.from).number,s=t.state.doc.lineAt(e.to).number;return Gs.announce.of(`${t.state.phrase(i?"Folded lines":"Unfolded lines")} ${n} ${t.state.phrase("to")} ${s}.`)}const ih=t=>{let{state:e}=t,i=[];for(let n=0;n<e.doc.length;){let s=t.lineBlockAt(n),r=zl(e,s.from,s.to);r&&i.push(Gl.of(r)),n=(r?t.lineBlockAt(r.to):s).to+1}return i.length&&t.dispatch({effects:Jl(t.state,i)}),!!i.length},nh=t=>{let e=t.state.field(Ul,!1);if(!e||!e.size)return!1;let i=[];return e.between(0,t.state.doc.length,((t,e)=>{i.push(Ll.of({from:t,to:e}))})),t.dispatch({effects:i}),!0};function sh(t,e){for(let i=e;;){let n=zl(t.state,i.from,i.to);if(n&&n.to>e.from)return n;if(!i.from)return null;i=t.lineBlockAt(i.from-1)}}const rh=[{key:"Ctrl-Shift-[",mac:"Cmd-Alt-[",run:Kl},{key:"Ctrl-Shift-]",mac:"Cmd-Alt-]",run:th},{key:"Ctrl-Alt-[",run:ih},{key:"Ctrl-Alt-]",run:nh}],oh={placeholderDOM:null,preparePlaceholder:null,placeholderText:"…"},ah=j.define({combine:t=>$t(t,oh)});function lh(t){let e=[Ul,Oh];return t&&e.push(ah.of(t)),e}function hh(t,e){let{state:i}=t,n=i.facet(ah),s=e=>{let i=t.lineBlockAt(t.posAtDOM(e.target)),n=Hl(t.state,i.from,i.to);n&&t.dispatch({effects:Ll.of(n)}),e.preventDefault()};if(n.placeholderDOM)return n.placeholderDOM(t,s,e);let r=document.createElement("span");return r.textContent=n.placeholderText,r.setAttribute("aria-label",i.phrase("folded code")),r.title=i.phrase("unfold"),r.className="cm-foldPlaceholder",r.onclick=s,r}const ch=si.replace({widget:new class extends ii{toDOM(t){return hh(t,null)}}});class fh extends ii{constructor(t){super(),this.value=t}eq(t){return this.value==t.value}toDOM(t){return hh(t,this.value)}}const uh={openText:"⌄",closedText:"›",markerDOM:null,domEventHandlers:{},foldingChanged:()=>!1};class dh extends bo{constructor(t,e){super(),this.config=t,this.open=e}eq(t){return this.config==t.config&&this.open==t.open}toDOM(t){if(this.config.markerDOM)return this.config.markerDOM(this.open);let e=document.createElement("span");return e.textContent=this.open?this.config.openText:this.config.closedText,e.title=t.state.phrase(this.open?"Fold line":"Unfold line"),e}}function ph(t={}){let e=Object.assign(Object.assign({},uh),t),i=new dh(e,!0),n=new dh(e,!1),s=Ni.fromClass(class{constructor(t){this.from=t.viewport.from,this.markers=this.buildMarkers(t)}update(t){(t.docChanged||t.viewportChanged||t.startState.facet(bl)!=t.state.facet(bl)||t.startState.field(Ul,!1)!=t.state.field(Ul,!1)||cl(t.startState)!=cl(t.state)||e.foldingChanged(t))&&(this.markers=this.buildMarkers(t.view))}buildMarkers(t){let e=new Rt;for(let s of t.viewportLineBlocks){let r=Hl(t.state,s.from,s.to)?n:zl(t.state,s.from,s.to)?i:null;r&&e.add(s.from,s.from,r)}return e.finish()}}),{domEventHandlers:r}=e;return[s,ko({class:"cm-foldGutter",markers(t){var e;return(null===(e=t.plugin(s))||void 0===e?void 0:e.markers)||At.empty},initialSpacer:()=>new dh(e,!1),domEventHandlers:Object.assign(Object.assign({},r),{click:(t,e,i)=>{if(r.click&&r.click(t,e,i))return!0;let n=Hl(t.state,e.from,e.to);if(n)return t.dispatch({effects:Ll.of(n)}),!0;let s=zl(t.state,e.from,e.to);return!!s&&(t.dispatch({effects:Gl.of(s)}),!0)}})}),lh()]}const Oh=Gs.baseTheme({".cm-foldPlaceholder":{backgroundColor:"#eee",border:"1px solid #ddd",color:"#888",borderRadius:".2em",margin:"0 1px",padding:"0 1px",cursor:"pointer"},".cm-foldGutter span":{padding:"0 1px",cursor:"pointer"}});class gh{constructor(t,e){let i;function n(t){let e=Ut.newName();return(i||(i=Object.create(null)))["."+e]=t,e}this.specs=t;const s="string"==typeof e.all?e.all:e.all?n(e.all):void 0,r=e.scope;this.scope=r instanceof al?t=>t.prop(sl)==r.data:r?t=>t==r:void 0,this.style=Wa(t.map((t=>({tag:t.tag,class:t.class||n(Object.assign({},t,{tag:null}))}))),{all:s}).style,this.module=i?new Ut(i):null,this.themeType=e.themeType}static define(t,e){return new gh(t,e||{})}}const mh=j.define(),wh=j.define({combine:t=>t.length?[t[0]]:null});function vh(t){let e=t.facet(mh);return e.length?e:t.facet(wh)}function bh(t,e){let i,n=[Sh];return t instanceof gh&&(t.module&&n.push(Gs.styleModule.of(t.module)),i=t.themeType),(null==e?void 0:e.fallback)?n.push(wh.of(t)):i?n.push(mh.computeN([Gs.darkTheme],(e=>e.facet(Gs.darkTheme)==("dark"==i)?[t]:[]))):n.push(mh.of(t)),n}class yh{constructor(t){this.markCache=Object.create(null),this.tree=cl(t.state),this.decorations=this.buildDeco(t,vh(t.state)),this.decoratedTo=t.viewport.to}update(t){let e=cl(t.state),i=vh(t.state),n=i!=vh(t.startState),{viewport:s}=t.view,r=t.changes.mapPos(this.decoratedTo,1);e.length<s.to&&!n&&e.type==this.tree.type&&r>=s.to?(this.decorations=this.decorations.map(t.changes),this.decoratedTo=r):(e!=this.tree||t.viewportChanged||n)&&(this.tree=e,this.decorations=this.buildDeco(t.view,i),this.decoratedTo=s.to)}buildDeco(t,e){if(!e||!this.tree.length)return si.none;let i=new Rt;for(let{from:n,to:s}of t.visibleRanges)Ma(this.tree,e,((t,e,n)=>{i.add(t,e,this.markCache[n]||(this.markCache[n]=si.mark({class:n})))}),n,s);return i.finish()}}const Sh=H.high(Ni.fromClass(yh,{decorations:t=>t.decorations})),xh=gh.define([{tag:tl.meta,color:"#404740"},{tag:tl.link,textDecoration:"underline"},{tag:tl.heading,textDecoration:"underline",fontWeight:"bold"},{tag:tl.emphasis,fontStyle:"italic"},{tag:tl.strong,fontWeight:"bold"},{tag:tl.strikethrough,textDecoration:"line-through"},{tag:tl.keyword,color:"#708"},{tag:[tl.atom,tl.bool,tl.url,tl.contentSeparator,tl.labelName],color:"#219"},{tag:[tl.literal,tl.inserted],color:"#164"},{tag:[tl.string,tl.deleted],color:"#a11"},{tag:[tl.regexp,tl.escape,tl.special(tl.string)],color:"#e40"},{tag:tl.definition(tl.variableName),color:"#00f"},{tag:tl.local(tl.variableName),color:"#30a"},{tag:[tl.typeName,tl.namespace],color:"#085"},{tag:tl.className,color:"#167"},{tag:[tl.special(tl.variableName),tl.macroName],color:"#256"},{tag:tl.definition(tl.propertyName),color:"#00c"},{tag:tl.comment,color:"#940"},{tag:tl.invalid,color:"#f00"}]),kh=Gs.baseTheme({"&.cm-focused .cm-matchingBracket":{backgroundColor:"#328c8252"},"&.cm-focused .cm-nonmatchingBracket":{backgroundColor:"#bb555544"}}),Qh=1e4,$h="()[]{}",Ph=j.define({combine:t=>$t(t,{afterCursor:!0,brackets:$h,maxScanDistance:Qh,renderMatch:Th})}),Zh=si.mark({class:"cm-matchingBracket"}),Ch=si.mark({class:"cm-nonmatchingBracket"});function Th(t){let e=[],i=t.matched?Zh:Ch;return e.push(i.range(t.start.from,t.start.to)),t.end&&e.push(i.range(t.end.from,t.end.to)),e}const Ah=z.define({create:()=>si.none,update(t,e){if(!e.docChanged&&!e.selection)return t;let i=[],n=e.state.facet(Ph);for(let t of e.state.selection.ranges){if(!t.empty)continue;let s=jh(e.state,t.head,-1,n)||t.head>0&&jh(e.state,t.head-1,1,n)||n.afterCursor&&(jh(e.state,t.head,1,n)||t.head<e.state.doc.length&&jh(e.state,t.head+1,-1,n));s&&(i=i.concat(n.renderMatch(s,e.state)))}return si.set(i,!0)},provide:t=>Gs.decorations.from(t)}),Rh=[Ah,kh];function Xh(t={}){return[Ph.of(t),Rh]}const Yh=new Jo;function Wh(t,e,i){let n=t.prop(e<0?Jo.openedBy:Jo.closedBy);if(n)return n;if(1==t.name.length){let n=i.indexOf(t.name);if(n>-1&&n%2==(e<0?1:0))return[i[n+e]]}return null}function Mh(t){let e=t.type.prop(Yh);return e?e(t.node):t}function jh(t,e,i,n={}){let s=n.maxScanDistance||Qh,r=n.brackets||$h,o=cl(t),a=o.resolveInner(e,i);for(let n=a;n;n=n.parent){let s=Wh(n.type,i,r);if(s&&n.from<n.to){let o=Mh(n);if(o&&(i>0?e>=o.from&&e<o.to:e>o.from&&e<=o.to))return Dh(t,e,i,n,o,s,r)}}return function(t,e,i,n,s,r,o){let a=i<0?t.sliceDoc(e-1,e):t.sliceDoc(e,e+1),l=o.indexOf(a);if(l<0||l%2==0!=i>0)return null;let h={from:i<0?e-1:e,to:i>0?e+1:e},c=t.doc.iterRange(e,i>0?t.doc.length:0),f=0;for(let t=0;!c.next().done&&t<=r;){let r=c.value;i<0&&(t+=r.length);let a=e+t*i;for(let t=i>0?0:r.length-1,e=i>0?r.length:-1;t!=e;t+=i){let e=o.indexOf(r[t]);if(!(e<0||n.resolveInner(a+t,1).type!=s))if(e%2==0==i>0)f++;else{if(1==f)return{start:h,end:{from:a+t,to:a+t+1},matched:e>>1==l>>1};f--}}i>0&&(t+=r.length)}return c.done?{start:h,matched:!1}:null}(t,e,i,o,a.type,s,r)}function Dh(t,e,i,n,s,r,o){let a=n.parent,l={from:s.from,to:s.to},h=0,c=null==a?void 0:a.cursor();if(c&&(i<0?c.childBefore(n.from):c.childAfter(n.to)))do{if(i<0?c.to<=n.from:c.from>=n.to){if(0==h&&r.indexOf(c.type.name)>-1&&c.from<c.to){let t=Mh(c);return{start:l,end:t?{from:t.from,to:t.to}:void 0,matched:!0}}if(Wh(c.type,i,o))h++;else if(Wh(c.type,-i,o)){if(0==h){let t=Mh(c);return{start:l,end:t&&t.from<t.to?{from:t.from,to:t.to}:void 0,matched:!1}}h--}}}while(i<0?c.prevSibling():c.nextSibling());return{start:l,matched:!1}}function Eh(t,e,i,n=0,s=0){null==e&&-1==(e=t.search(/[^\s\u00a0]/))&&(e=t.length);let r=s;for(let s=n;s<e;s++)9==t.charCodeAt(s)?r+=i-r%i:r++;return r}class qh{constructor(t,e,i,n){this.string=t,this.tabSize=e,this.indentUnit=i,this.overrideIndent=n,this.pos=0,this.start=0,this.lastColumnPos=0,this.lastColumnValue=0}eol(){return this.pos>=this.string.length}sol(){return 0==this.pos}peek(){return this.string.charAt(this.pos)||void 0}next(){if(this.pos<this.string.length)return this.string.charAt(this.pos++)}eat(t){let e,i=this.string.charAt(this.pos);if(e="string"==typeof t?i==t:i&&(t instanceof RegExp?t.test(i):t(i)),e)return++this.pos,i}eatWhile(t){let e=this.pos;for(;this.eat(t););return this.pos>e}eatSpace(){let t=this.pos;for(;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>t}skipToEnd(){this.pos=this.string.length}skipTo(t){let e=this.string.indexOf(t,this.pos);if(e>-1)return this.pos=e,!0}backUp(t){this.pos-=t}column(){return this.lastColumnPos<this.start&&(this.lastColumnValue=Eh(this.string,this.start,this.tabSize,this.lastColumnPos,this.lastColumnValue),this.lastColumnPos=this.start),this.lastColumnValue}indentation(){var t;return null!==(t=this.overrideIndent)&&void 0!==t?t:Eh(this.string,null,this.tabSize)}match(t,e,i){if("string"==typeof t){let n=t=>i?t.toLowerCase():t;return n(this.string.substr(this.pos,t.length))==n(t)?(!1!==e&&(this.pos+=t.length),!0):null}{let i=this.string.slice(this.pos).match(t);return i&&i.index>0?null:(i&&!1!==e&&(this.pos+=i[0].length),i)}}current(){return this.string.slice(this.start,this.pos)}}function _h(t){if("object"!=typeof t)return t;let e={};for(let i in t){let n=t[i];e[i]=n instanceof Array?n.slice():n}return e}const Vh=new WeakMap;class Ih extends al{constructor(t){let e,i=rl(t.languageData),n={name:(s=t).name||"",token:s.token,blankLine:s.blankLine||(()=>{}),startState:s.startState||(()=>!0),copyState:s.copyState||_h,indent:s.indent||(()=>null),languageData:s.languageData||{},tokenTable:s.tokenTable||Nh};var s;super(i,new class extends $a{createParse(t,i,n){return new Gh(e,t,i,n)}},[xl.of(((t,e)=>this.getIndent(t,e)))],t.name),this.topNode=function(t){let e=ea.define({id:Uh.length,name:"Document",props:[sl.add((()=>t))],top:!0});return Uh.push(e),e}(i),e=this,this.streamParser=n,this.stateAfter=new Jo({perNode:!0}),this.tokenTable=t.tokenTable?new tc(n.tokenTable):ec}static define(t){return new Ih(t)}getIndent(t,e){let i,n=cl(t.state),s=n.resolve(e);for(;s&&s.type!=this.topNode;)s=s.parent;if(!s)return null;let{overrideIndentation:r}=t.options;r&&(i=Vh.get(t.state),null!=i&&i<e-1e4&&(i=void 0));let o,a,l=zh(this,n,0,s.from,null!=i?i:e);if(l?(a=l.state,o=l.pos+1):(a=this.streamParser.startState(t.unit),o=0),e-o>1e4)return null;for(;o<e;){let i=t.state.doc.lineAt(o),n=Math.min(e,i.to);if(i.length){let e=r?r(i.from):-1,s=new qh(i.text,t.state.tabSize,t.unit,e<0?void 0:e);for(;s.pos<n-i.from;)Lh(this.streamParser.token,s,a)}else this.streamParser.blankLine(a,t.unit);if(n==e)break;o=i.to+1}let h=t.lineAt(e);return r&&null==i&&Vh.set(t.state,h.from),this.streamParser.indent(a,/^\s*(.*)/.exec(h.text)[1],t)}get allowsNesting(){return!1}}function zh(t,e,i,n,s){let r=i>=n&&i+e.length<=s&&e.prop(t.stateAfter);if(r)return{state:t.streamParser.copyState(r),pos:i+e.length};for(let r=e.children.length-1;r>=0;r--){let o=e.children[r],a=i+e.positions[r],l=o instanceof oa&&a<s&&zh(t,o,a,n,s);if(l)return l}return null}function Bh(t,e,i,n,s){if(s&&i<=0&&n>=e.length)return e;s||e.type!=t.topNode||(s=!0);for(let r=e.children.length-1;r>=0;r--){let o,a=e.positions[r],l=e.children[r];if(a<n&&l instanceof oa){if(!(o=Bh(t,l,i-a,n-a,s)))break;return s?new oa(e.type,e.children.slice(0,r).concat(o),e.positions.slice(0,r+1),a+o.length):o}}return null}let Gh=class{constructor(t,e,i,n){this.lang=t,this.input=e,this.fragments=i,this.ranges=n,this.stoppedAt=null,this.chunks=[],this.chunkPos=[],this.chunk=[],this.chunkReused=void 0,this.rangeIndex=0,this.to=n[n.length-1].to;let s=pl.get(),r=n[0].from,{state:o,tree:a}=function(t,e,i,n){for(let n of e){let e,s=n.from+(n.openStart?25:0),r=n.to-(n.openEnd?25:0),o=s<=i&&r>i&&zh(t,n.tree,0-n.offset,i,r);if(o&&(e=Bh(t,n.tree,i+n.offset,o.pos+n.offset,!1)))return{state:o.state,tree:e}}return{state:t.streamParser.startState(n?Ql(n):4),tree:oa.empty}}(t,i,r,null==s?void 0:s.state);this.state=o,this.parsedPos=this.chunkStart=r+a.length;for(let t=0;t<a.children.length;t++)this.chunks.push(a.children[t]),this.chunkPos.push(a.positions[t]);s&&this.parsedPos<s.viewport.from-1e5&&(this.state=this.lang.streamParser.startState(Ql(s.state)),s.skipUntilInView(this.parsedPos,s.viewport.from),this.parsedPos=s.viewport.from),this.moveRangeIndex()}advance(){let t=pl.get(),e=null==this.stoppedAt?this.to:Math.min(this.to,this.stoppedAt),i=Math.min(e,this.chunkStart+2048);for(t&&(i=Math.min(i,t.viewport.to));this.parsedPos<i;)this.parseLine(t);return this.chunkStart<this.parsedPos&&this.finishChunk(),this.parsedPos>=e?this.finish():t&&this.parsedPos>=t.viewport.to?(t.skipUntilInView(this.parsedPos,e),this.finish()):null}stopAt(t){this.stoppedAt=t}lineAfter(t){let e=this.input.chunk(t);if(this.input.lineChunks)"\n"==e&&(e="");else{let t=e.indexOf("\n");t>-1&&(e=e.slice(0,t))}return t+e.length<=this.to?e:e.slice(0,this.to-t)}nextLine(){let t=this.parsedPos,e=this.lineAfter(t),i=t+e.length;for(let t=this.rangeIndex;;){let n=this.ranges[t].to;if(n>=i)break;if(e=e.slice(0,n-(i-e.length)),t++,t==this.ranges.length)break;let s=this.ranges[t].from,r=this.lineAfter(s);e+=r,i=s+r.length}return{line:e,end:i}}skipGapsTo(t,e,i){for(;;){let n=this.ranges[this.rangeIndex].to,s=t+e;if(i>0?n>s:n>=s)break;e+=this.ranges[++this.rangeIndex].from-n}return e}moveRangeIndex(){for(;this.ranges[this.rangeIndex].to<this.parsedPos;)this.rangeIndex++}emitToken(t,e,i,n,s){if(this.ranges.length>1){e+=s=this.skipGapsTo(e,s,1);let t=this.chunk.length;i+=s=this.skipGapsTo(i,s,-1),n+=this.chunk.length-t}return this.chunk.push(t,e,i,n),s}parseLine(t){let{line:e,end:i}=this.nextLine(),n=0,{streamParser:s}=this.lang,r=new qh(e,t?t.state.tabSize:4,t?Ql(t.state):2);if(r.eol())s.blankLine(this.state,r.indentUnit);else for(;!r.eol();){let t=Lh(s.token,r,this.state);if(t&&(n=this.emitToken(this.lang.tokenTable.resolve(t),this.parsedPos+r.start,this.parsedPos+r.pos,4,n)),r.start>1e4)break}this.parsedPos=i,this.moveRangeIndex(),this.parsedPos<this.to&&this.parsedPos++}finishChunk(){let t=oa.build({buffer:this.chunk,start:this.chunkStart,length:this.parsedPos-this.chunkStart,nodeSet:Hh,topID:0,maxBufferLength:2048,reused:this.chunkReused});t=new oa(t.type,t.children,t.positions,t.length,[[this.lang.stateAfter,this.lang.streamParser.copyState(this.state)]]),this.chunks.push(t),this.chunkPos.push(this.chunkStart-this.ranges[0].from),this.chunk=[],this.chunkReused=void 0,this.chunkStart=this.parsedPos}finish(){return new oa(this.lang.topNode,this.chunks,this.chunkPos,this.parsedPos-this.ranges[0].from).balance()}};function Lh(t,e,i){e.start=e.pos;for(let n=0;n<10;n++){let n=t(e,i);if(e.pos>e.start)return n}throw new Error("Stream parser failed to advance stream.")}const Nh=Object.create(null),Uh=[ea.none],Hh=new ia(Uh),Fh=[],Jh=Object.create(null),Kh=Object.create(null);for(let[t,e]of[["variable","variableName"],["variable-2","variableName.special"],["string-2","string.special"],["def","variableName.definition"],["tag","tagName"],["attribute","attributeName"],["type","typeName"],["builtin","variableName.standard"],["qualifier","modifier"],["error","invalid"],["header","heading"],["property","propertyName"]])Kh[t]=nc(Nh,e);class tc{constructor(t){this.extra=t,this.table=Object.assign(Object.create(null),Kh)}resolve(t){return t?this.table[t]||(this.table[t]=nc(this.extra,t)):0}}const ec=new tc(Nh);function ic(t,e){Fh.indexOf(t)>-1||(Fh.push(t),console.warn(e))}function nc(t,e){let i=[];for(let n of e.split(" ")){let e=[];for(let i of n.split(".")){let n=t[i]||tl[i];n?"function"==typeof n?e.length?e=e.map(n):ic(i,`Modifier ${i} used at start of tag`):e.length?ic(i,`Tag ${i} used as modifier`):e=Array.isArray(n)?n:[n]:ic(i,`Unknown highlighting tag ${i}`)}for(let t of e)i.push(t)}if(!i.length)return 0;let n=e.replace(/ /g,"_"),s=n+" "+i.map((t=>t.id)),r=Jh[s];if(r)return r.id;let o=Jh[s]=ea.define({id:Uh.length,name:n,props:[Ra({[n]:i})]});return Uh.push(o),o.id}function sc(t){return t.length<=4096&&/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac\ufb50-\ufdff]/.test(t)}function rc(t){for(let e=t.iter();!e.next().done;)if(sc(e.value))return!0;return!1}const oc=j.define({combine:t=>t.some((t=>t))});const ac=Ni.fromClass(class{constructor(t){this.always=t.state.facet(oc)||t.textDirection!=di.LTR||t.state.facet(Gs.perLineTextDirection),this.hasRTL=!this.always&&rc(t.state.doc),this.tree=cl(t.state),this.decorations=this.always||this.hasRTL?lc(t,this.tree,this.always):si.none}update(t){let e=t.state.facet(oc)||t.view.textDirection!=di.LTR||t.state.facet(Gs.perLineTextDirection);if(e||this.hasRTL||!function(t){let e=!1;return t.iterChanges(((t,i,n,s,r)=>{!e&&rc(r)&&(e=!0)})),e}(t.changes)||(this.hasRTL=!0),!e&&!this.hasRTL)return;let i=cl(t.state);(e!=this.always||i!=this.tree||t.docChanged||t.viewportChanged)&&(this.tree=i,this.always=e,this.decorations=lc(t.view,i,e))}},{provide:t=>{function e(e){var i,n;return null!==(n=null===(i=e.plugin(t))||void 0===i?void 0:i.decorations)&&void 0!==n?n:si.none}return[Gs.outerDecorations.of(e),H.lowest(Gs.bidiIsolatedRanges.of(e))]}});function lc(t,e,i){let n=new Rt,s=t.visibleRanges;i||(s=function(t,e){let i=e.iter(),n=0,s=[],r=null;for(let{from:e,to:o}of t)for(e!=n&&(n<e&&i.next(e-n),n=e);;){let t=n,e=n+i.value.length;if(!i.lineBreak&&sc(i.value)&&(r&&r.to>t-10?r.to=Math.min(o,e):s.push(r={from:t,to:Math.min(o,e)})),n>=o)break;n=e,i.next()}return s}(s,t.state.doc));for(let{from:t,to:i}of s)e.iterate({enter:t=>{let e=t.type.prop(Jo.isolate);e&&n.add(t.from,t.to,hc[e])},from:t,to:i});return n.finish()}const hc={rtl:si.mark({class:"cm-iso",inclusive:!0,attributes:{dir:"rtl"},bidiIsolate:di.RTL}),ltr:si.mark({class:"cm-iso",inclusive:!0,attributes:{dir:"ltr"},bidiIsolate:di.LTR}),auto:si.mark({class:"cm-iso",inclusive:!0,attributes:{dir:"auto"},bidiIsolate:null})};var cc=Object.freeze({__proto__:null,DocInput:ul,HighlightStyle:gh,IndentContext:Zl,LRLanguage:hl,Language:al,LanguageDescription:Sl,LanguageSupport:yl,ParseContext:pl,StreamLanguage:Ih,StringStream:qh,TreeIndentContext:Xl,bidiIsolates:function(t={}){let e=[ac];return t.alwaysIsolate&&e.push(oc.of(!0)),e},bracketMatching:Xh,bracketMatchingHandle:Yh,codeFolding:lh,continuedIndent:Dl,defaultHighlightStyle:xh,defineLanguageFacet:rl,delimitedIndent:Wl,ensureSyntaxTree:fl,flatIndent:jl,foldAll:ih,foldCode:Kl,foldEffect:Gl,foldGutter:ph,foldInside:Vl,foldKeymap:rh,foldNodeProp:_l,foldService:ql,foldState:Ul,foldable:zl,foldedRanges:function(t){return t.field(Ul,!1)||At.empty},forceParsing:function(t,e=t.viewport.to,i=100){let n=fl(t.state,e,i);return n!=cl(t.state)&&t.dispatch({}),!!n},getIndentUnit:Ql,getIndentation:Pl,highlightingFor:function(t,e,i){let n=vh(t),s=null;if(n)for(let t of n)if(!t.scope||i&&t.scope(i)){let i=t.style(e);i&&(s=s?s+" "+i:i)}return s},indentNodeProp:Cl,indentOnInput:El,indentRange:function(t,e,i){let n=Object.create(null),s=new Zl(t,{overrideIndentation:t=>{var e;return null!==(e=n[t])&&void 0!==e?e:-1}}),r=[];for(let o=e;o<=i;){let e=t.doc.lineAt(o);o=e.to+1;let i=Pl(s,e.from);if(null==i)continue;/\S/.test(e.text)||(i=0);let a=/^\s*/.exec(e.text)[0],l=$l(t,i);a!=l&&(n[e.from]=i,r.push({from:e.from,to:e.from+a.length,insert:l}))}return t.changes(r)},indentService:xl,indentString:$l,indentUnit:kl,language:bl,languageDataProp:sl,matchBrackets:jh,sublanguageProp:ol,syntaxHighlighting:bh,syntaxParserRunning:function(t){var e;return(null===(e=t.plugin(vl))||void 0===e?void 0:e.isWorking())||!1},syntaxTree:cl,syntaxTreeAvailable:function(t,e=t.doc.length){var i;return(null===(i=t.field(al.state,!1))||void 0===i?void 0:i.context.isDone(e))||!1},toggleFold:t=>{let e=[];for(let i of Nl(t)){let n=Hl(t.state,i.from,i.to);if(n)e.push(Ll.of(n),eh(t,n,!1));else{let n=sh(t,i);n&&e.push(Gl.of(n),eh(t,n))}}return e.length>0&&t.dispatch({effects:Jl(t.state,e)}),!!e.length},unfoldAll:nh,unfoldCode:th,unfoldEffect:Ll});function fc(t,e){return({state:i,dispatch:n})=>{if(i.readOnly)return!1;let s=t(e,i);return!!s&&(n(i.update(s)),!0)}}const uc=fc(wc,0),dc=fc(mc,0),pc=fc(((t,e)=>mc(t,e,function(t){let e=[];for(let i of t.selection.ranges){let n=t.doc.lineAt(i.from),s=i.to<=n.to?n:t.doc.lineAt(i.to),r=e.length-1;r>=0&&e[r].to>n.from?e[r].to=s.to:e.push({from:n.from+/^\s*/.exec(n.text)[0].length,to:s.to})}return e}(e))),0);function Oc(t,e){let i=t.languageDataAt("commentTokens",e);return i.length?i[0]:{}}const gc=50;function mc(t,e,i=e.selection.ranges){let n=i.map((t=>Oc(e,t.from).block));if(!n.every((t=>t)))return null;let s=i.map(((t,i)=>function(t,{open:e,close:i},n,s){let r,o,a=t.sliceDoc(n-gc,n),l=t.sliceDoc(s,s+gc),h=/\s*$/.exec(a)[0].length,c=/^\s*/.exec(l)[0].length,f=a.length-h;if(a.slice(f-e.length,f)==e&&l.slice(c,c+i.length)==i)return{open:{pos:n-h,margin:h&&1},close:{pos:s+c,margin:c&&1}};s-n<=2*gc?r=o=t.sliceDoc(n,s):(r=t.sliceDoc(n,n+gc),o=t.sliceDoc(s-gc,s));let u=/^\s*/.exec(r)[0].length,d=/\s*$/.exec(o)[0].length,p=o.length-d-i.length;return r.slice(u,u+e.length)==e&&o.slice(p,p+i.length)==i?{open:{pos:n+u+e.length,margin:/\s/.test(r.charAt(u+e.length))?1:0},close:{pos:s-d-i.length,margin:/\s/.test(o.charAt(p-1))?1:0}}:null}(e,n[i],t.from,t.to)));if(2!=t&&!s.every((t=>t)))return{changes:e.changes(i.map(((t,e)=>s[e]?[]:[{from:t.from,insert:n[e].open+" "},{from:t.to,insert:" "+n[e].close}])))};if(1!=t&&s.some((t=>t))){let t=[];for(let e,i=0;i<s.length;i++)if(e=s[i]){let s=n[i],{open:r,close:o}=e;t.push({from:r.pos-s.open.length,to:r.pos+r.margin},{from:o.pos-o.margin,to:o.pos+s.close.length})}return{changes:t}}return null}function wc(t,e,i=e.selection.ranges){let n=[],s=-1;for(let{from:t,to:r}of i){let i=n.length,o=1e9,a=Oc(e,t).line;if(a){for(let i=t;i<=r;){let l=e.doc.lineAt(i);if(l.from>s&&(t==r||r>l.from)){s=l.from;let t=/^\s*/.exec(l.text)[0].length,e=t==l.length,i=l.text.slice(t,t+a.length)==a?t:-1;t<l.text.length&&t<o&&(o=t),n.push({line:l,comment:i,token:a,indent:t,empty:e,single:!1})}i=l.to+1}if(o<1e9)for(let t=i;t<n.length;t++)n[t].indent<n[t].line.text.length&&(n[t].indent=o);n.length==i+1&&(n[i].single=!0)}}if(2!=t&&n.some((t=>t.comment<0&&(!t.empty||t.single)))){let t=[];for(let{line:e,token:i,indent:s,empty:r,single:o}of n)!o&&r||t.push({from:e.from+s,insert:i+" "});let i=e.changes(t);return{changes:i,selection:e.selection.map(i,1)}}if(1!=t&&n.some((t=>t.comment>=0))){let t=[];for(let{line:e,comment:i,token:s}of n)if(i>=0){let n=e.from+i,r=n+s.length;" "==e.text[r-e.from]&&r++,t.push({from:n,to:r})}return{changes:t}}return null}const vc=ct.define(),bc=ct.define(),yc=j.define(),Sc=j.define({combine:t=>$t(t,{minDepth:100,newGroupDelay:500,joinToEvent:(t,e)=>e},{minDepth:Math.max,newGroupDelay:Math.min,joinToEvent:(t,e)=>(i,n)=>t(i,n)||e(i,n)})}),xc=z.define({create:()=>qc.empty,update(t,e){let i=e.state.facet(Sc),n=e.annotation(vc);if(n){let s=Tc.fromTransaction(e,n.selection),r=n.side,o=0==r?t.undone:t.done;return o=s?Ac(o,o.length,i.minDepth,s):Wc(o,e.startState.selection),new qc(0==r?n.rest:o,0==r?o:n.rest)}let s=e.annotation(bc);if("full"!=s&&"before"!=s||(t=t.isolate()),!1===e.annotation(pt.addToHistory))return e.changes.empty?t:t.addMapping(e.changes.desc);let r=Tc.fromTransaction(e),o=e.annotation(pt.time),a=e.annotation(pt.userEvent);return r?t=t.addChanges(r,o,a,i,e):e.selection&&(t=t.addSelection(e.startState.selection,o,a,i.newGroupDelay)),"full"!=s&&"after"!=s||(t=t.isolate()),t},toJSON:t=>({done:t.done.map((t=>t.toJSON())),undone:t.undone.map((t=>t.toJSON()))}),fromJSON:t=>new qc(t.done.map(Tc.fromJSON),t.undone.map(Tc.fromJSON))});function kc(t={}){return[xc,Sc.of(t),Gs.domEventHandlers({beforeinput(t,e){let i="historyUndo"==t.inputType?$c:"historyRedo"==t.inputType?Pc:null;return!!i&&(t.preventDefault(),i(e))}})]}function Qc(t,e){return function({state:i,dispatch:n}){if(!e&&i.readOnly)return!1;let s=i.field(xc,!1);if(!s)return!1;let r=s.pop(t,i,e);return!!r&&(n(r),!0)}}const $c=Qc(0,!1),Pc=Qc(1,!1),Zc=Qc(0,!0),Cc=Qc(1,!0);class Tc{constructor(t,e,i,n,s){this.changes=t,this.effects=e,this.mapped=i,this.startSelection=n,this.selectionsAfter=s}setSelAfter(t){return new Tc(this.changes,this.effects,this.mapped,this.startSelection,t)}toJSON(){var t,e,i;return{changes:null===(t=this.changes)||void 0===t?void 0:t.toJSON(),mapped:null===(e=this.mapped)||void 0===e?void 0:e.toJSON(),startSelection:null===(i=this.startSelection)||void 0===i?void 0:i.toJSON(),selectionsAfter:this.selectionsAfter.map((t=>t.toJSON()))}}static fromJSON(t){return new Tc(t.changes&&$.fromJSON(t.changes),[],t.mapped&&Q.fromJSON(t.mapped),t.startSelection&&Y.fromJSON(t.startSelection),t.selectionsAfter.map(Y.fromJSON))}static fromTransaction(t,e){let i=Xc;for(let e of t.startState.facet(yc)){let n=e(t);n.length&&(i=i.concat(n))}return!i.length&&t.changes.empty?null:new Tc(t.changes.invert(t.startState.doc),i,void 0,e||t.startState.selection,Xc)}static selection(t){return new Tc(void 0,Xc,void 0,void 0,t)}}function Ac(t,e,i,n){let s=e+1>i+20?e-i-1:0,r=t.slice(s,e);return r.push(n),r}function Rc(t,e){return t.length?e.length?t.concat(e):t:e}const Xc=[],Yc=200;function Wc(t,e){if(t.length){let i=t[t.length-1],n=i.selectionsAfter.slice(Math.max(0,i.selectionsAfter.length-Yc));return n.length&&n[n.length-1].eq(e)?t:(n.push(e),Ac(t,t.length-1,1e9,i.setSelAfter(n)))}return[Tc.selection([e])]}function Mc(t){let e=t[t.length-1],i=t.slice();return i[t.length-1]=e.setSelAfter(e.selectionsAfter.slice(0,e.selectionsAfter.length-1)),i}function jc(t,e){if(!t.length)return t;let i=t.length,n=Xc;for(;i;){let s=Dc(t[i-1],e,n);if(s.changes&&!s.changes.empty||s.effects.length){let e=t.slice(0,i);return e[i-1]=s,e}e=s.mapped,i--,n=s.selectionsAfter}return n.length?[Tc.selection(n)]:Xc}function Dc(t,e,i){let n=Rc(t.selectionsAfter.length?t.selectionsAfter.map((t=>t.map(e))):Xc,i);if(!t.changes)return Tc.selection(n);let s=t.changes.map(e),r=e.mapDesc(t.changes,!0),o=t.mapped?t.mapped.composeDesc(r):r;return new Tc(s,dt.mapEffects(t.effects,e),o,t.startSelection.map(r),n)}const Ec=/^(input\.type|delete)($|\.)/;class qc{constructor(t,e,i=0,n=void 0){this.done=t,this.undone=e,this.prevTime=i,this.prevUserEvent=n}isolate(){return this.prevTime?new qc(this.done,this.undone):this}addChanges(t,e,i,n,s){let r=this.done,o=r[r.length-1];return r=o&&o.changes&&!o.changes.empty&&t.changes&&(!i||Ec.test(i))&&(!o.selectionsAfter.length&&e-this.prevTime<n.newGroupDelay&&n.joinToEvent(s,function(t,e){let i=[],n=!1;return t.iterChangedRanges(((t,e)=>i.push(t,e))),e.iterChangedRanges(((t,e,s,r)=>{for(let t=0;t<i.length;){let e=i[t++],o=i[t++];r>=e&&s<=o&&(n=!0)}})),n}(o.changes,t.changes))||"input.type.compose"==i)?Ac(r,r.length-1,n.minDepth,new Tc(t.changes.compose(o.changes),Rc(t.effects,o.effects),o.mapped,o.startSelection,Xc)):Ac(r,r.length,n.minDepth,t),new qc(r,Xc,e,i)}addSelection(t,e,i,n){let s=this.done.length?this.done[this.done.length-1].selectionsAfter:Xc;return s.length>0&&e-this.prevTime<n&&i==this.prevUserEvent&&i&&/^select($|\.)/.test(i)&&(r=s[s.length-1],o=t,r.ranges.length==o.ranges.length&&0===r.ranges.filter(((t,e)=>t.empty!=o.ranges[e].empty)).length)?this:new qc(Wc(this.done,t),this.undone,e,i);var r,o}addMapping(t){return new qc(jc(this.done,t),jc(this.undone,t),this.prevTime,this.prevUserEvent)}pop(t,e,i){let n=0==t?this.done:this.undone;if(0==n.length)return null;let s=n[n.length-1],r=s.selectionsAfter[0]||e.selection;if(i&&s.selectionsAfter.length)return e.update({selection:s.selectionsAfter[s.selectionsAfter.length-1],annotations:vc.of({side:t,rest:Mc(n),selection:r}),userEvent:0==t?"select.undo":"select.redo",scrollIntoView:!0});if(s.changes){let i=1==n.length?Xc:n.slice(0,n.length-1);return s.mapped&&(i=jc(i,s.mapped)),e.update({changes:s.changes,selection:s.startSelection,effects:s.effects,annotations:vc.of({side:t,rest:i,selection:r}),filter:!1,userEvent:0==t?"undo":"redo",scrollIntoView:!0})}return null}}qc.empty=new qc(Xc,Xc);const _c=[{key:"Mod-z",run:$c,preventDefault:!0},{key:"Mod-y",mac:"Mod-Shift-z",run:Pc,preventDefault:!0},{linux:"Ctrl-Shift-z",run:Pc,preventDefault:!0},{key:"Mod-u",run:Zc,preventDefault:!0},{key:"Alt-u",mac:"Mod-Shift-u",run:Cc,preventDefault:!0}];function Vc(t,e){return Y.create(t.ranges.map(e),t.mainIndex)}function Ic(t,e){return t.update({selection:e,scrollIntoView:!0,userEvent:"select"})}function zc({state:t,dispatch:e},i){let n=Vc(t.selection,i);return!n.eq(t.selection,!0)&&(e(Ic(t,n)),!0)}function Bc(t,e){return Y.cursor(e?t.to:t.from)}function Gc(t,e){return zc(t,(i=>i.empty?t.moveByChar(i,e):Bc(i,e)))}function Lc(t){return t.textDirectionAt(t.state.selection.main.head)==di.LTR}const Nc=t=>Gc(t,!Lc(t)),Uc=t=>Gc(t,Lc(t));function Hc(t,e){return zc(t,(i=>i.empty?t.moveByGroup(i,e):Bc(i,e)))}function Fc(t,e,i){if(e.type.prop(i))return!0;let n=e.to-e.from;return n&&(n>2||/[^\s,.;:]/.test(t.sliceDoc(e.from,e.to)))||e.firstChild}function Jc(t,e,i){let n,s,r=cl(t).resolveInner(e.head),o=i?Jo.closedBy:Jo.openedBy;for(let n=e.head;;){let e=i?r.childAfter(n):r.childBefore(n);if(!e)break;Fc(t,e,o)?r=e:n=i?e.to:e.from}return s=r.type.prop(o)&&(n=i?jh(t,r.from,1):jh(t,r.to,-1))&&n.matched?i?n.end.to:n.end.from:i?r.to:r.from,Y.cursor(s,i?-1:1)}function Kc(t,e){return zc(t,(i=>{if(!i.empty)return Bc(i,e);let n=t.moveVertically(i,e);return n.head!=i.head?n:t.moveToLineBoundary(i,e)}))}const tf=t=>Kc(t,!1),ef=t=>Kc(t,!0);function nf(t){let e,i=t.scrollDOM.clientHeight<t.scrollDOM.scrollHeight-2,n=0,s=0;if(i){for(let e of t.state.facet(Gs.scrollMargins)){let i=e(t);(null==i?void 0:i.top)&&(n=Math.max(null==i?void 0:i.top,n)),(null==i?void 0:i.bottom)&&(s=Math.max(null==i?void 0:i.bottom,s))}e=t.scrollDOM.clientHeight-n-s}else e=(t.dom.ownerDocument.defaultView||window).innerHeight;return{marginTop:n,marginBottom:s,selfScroll:i,height:Math.max(t.defaultLineHeight,e-5)}}function sf(t,e){let i,n=nf(t),{state:s}=t,r=Vc(s.selection,(i=>i.empty?t.moveVertically(i,e,n.height):Bc(i,e)));if(r.eq(s.selection))return!1;if(n.selfScroll){let e=t.coordsAtPos(s.selection.main.head),o=t.scrollDOM.getBoundingClientRect(),a=o.top+n.marginTop,l=o.bottom-n.marginBottom;e&&e.top>a&&e.bottom<l&&(i=Gs.scrollIntoView(r.main.head,{y:"start",yMargin:e.top-a}))}return t.dispatch(Ic(s,r),{effects:i}),!0}const rf=t=>sf(t,!1),of=t=>sf(t,!0);function af(t,e,i){let n=t.lineBlockAt(e.head),s=t.moveToLineBoundary(e,i);if(s.head==e.head&&s.head!=(i?n.to:n.from)&&(s=t.moveToLineBoundary(e,i,!1)),!i&&s.head==n.from&&n.length){let i=/^\s*/.exec(t.state.sliceDoc(n.from,Math.min(n.from+100,n.to)))[0].length;i&&e.head!=n.from+i&&(s=Y.cursor(n.from+i))}return s}function lf(t,e){let i=Vc(t.state.selection,(t=>{let i=e(t);return Y.range(t.anchor,i.head,i.goalColumn,i.bidiLevel||void 0)}));return!i.eq(t.state.selection)&&(t.dispatch(Ic(t.state,i)),!0)}function hf(t,e){return lf(t,(i=>t.moveByChar(i,e)))}const cf=t=>hf(t,!Lc(t)),ff=t=>hf(t,Lc(t));function uf(t,e){return lf(t,(i=>t.moveByGroup(i,e)))}function df(t,e){return lf(t,(i=>t.moveVertically(i,e)))}const pf=t=>df(t,!1),Of=t=>df(t,!0);function gf(t,e){return lf(t,(i=>t.moveVertically(i,e,nf(t).height)))}const mf=t=>gf(t,!1),wf=t=>gf(t,!0),vf=({state:t,dispatch:e})=>(e(Ic(t,{anchor:0})),!0),bf=({state:t,dispatch:e})=>(e(Ic(t,{anchor:t.doc.length})),!0),yf=({state:t,dispatch:e})=>(e(Ic(t,{anchor:t.selection.main.anchor,head:0})),!0),Sf=({state:t,dispatch:e})=>(e(Ic(t,{anchor:t.selection.main.anchor,head:t.doc.length})),!0);function xf(t,e){if(t.state.readOnly)return!1;let i="delete.selection",{state:n}=t,s=n.changeByRange((n=>{let{from:s,to:r}=n;if(s==r){let o=e(n);o<s?(i="delete.backward",o=kf(t,o,!1)):o>s&&(i="delete.forward",o=kf(t,o,!0)),s=Math.min(s,o),r=Math.max(r,o)}else s=kf(t,s,!1),r=kf(t,r,!0);return s==r?{range:n}:{changes:{from:s,to:r},range:Y.cursor(s,s<n.head?-1:1)}}));return!s.changes.empty&&(t.dispatch(n.update(s,{scrollIntoView:!0,userEvent:i,effects:"delete.selection"==i?Gs.announce.of(n.phrase("Selection deleted")):void 0})),!0)}function kf(t,e,i){if(t instanceof Gs)for(let n of t.state.facet(Gs.atomicRanges).map((e=>e(t))))n.between(e,e,((t,n)=>{t<e&&n>e&&(e=i?n:t)}));return e}const Qf=(t,e)=>xf(t,(i=>{let n,s,r=i.from,{state:o}=t,a=o.doc.lineAt(r);if(!e&&r>a.from&&r<a.from+200&&!/[^ \t]/.test(n=a.text.slice(0,r-a.from))){if("\t"==n[n.length-1])return r-1;let t=It(n,o.tabSize)%Ql(o)||Ql(o);for(let e=0;e<t&&" "==n[n.length-1-e];e++)r--;s=r}else s=O(a.text,r-a.from,e,e)+a.from,s==r&&a.number!=(e?o.doc.lines:1)?s+=e?1:-1:!e&&/[\ufe00-\ufe0f]/.test(a.text.slice(s-a.from,r-a.from))&&(s=O(a.text,s-a.from,!1,!1)+a.from);return s})),$f=t=>Qf(t,!1),Pf=t=>Qf(t,!0),Zf=(t,e)=>xf(t,(i=>{let n=i.head,{state:s}=t,r=s.doc.lineAt(n),o=s.charCategorizer(n);for(let t=null;;){if(n==(e?r.to:r.from)){n==i.head&&r.number!=(e?s.doc.lines:1)&&(n+=e?1:-1);break}let a=O(r.text,n-r.from,e)+r.from,l=r.text.slice(Math.min(n,a)-r.from,Math.max(n,a)-r.from),h=o(l);if(null!=t&&h!=t)break;" "==l&&n==i.head||(t=h),n=a}return n})),Cf=t=>Zf(t,!1);function Tf(t){let e=[],i=-1;for(let n of t.selection.ranges){let s=t.doc.lineAt(n.from),r=t.doc.lineAt(n.to);if(n.empty||n.to!=r.from||(r=t.doc.lineAt(n.to-1)),i>=s.number){let t=e[e.length-1];t.to=r.to,t.ranges.push(n)}else e.push({from:s.from,to:r.to,ranges:[n]});i=r.number+1}return e}function Af(t,e,i){if(t.readOnly)return!1;let n=[],s=[];for(let e of Tf(t)){if(i?e.to==t.doc.length:0==e.from)continue;let r=t.doc.lineAt(i?e.to+1:e.from-1),o=r.length+1;if(i){n.push({from:e.to,to:r.to},{from:e.from,insert:r.text+t.lineBreak});for(let i of e.ranges)s.push(Y.range(Math.min(t.doc.length,i.anchor+o),Math.min(t.doc.length,i.head+o)))}else{n.push({from:r.from,to:e.from},{from:e.to,insert:t.lineBreak+r.text});for(let t of e.ranges)s.push(Y.range(t.anchor-o,t.head-o))}}return!!n.length&&(e(t.update({changes:n,scrollIntoView:!0,selection:Y.create(s,t.selection.mainIndex),userEvent:"move.line"})),!0)}function Rf(t,e,i){if(t.readOnly)return!1;let n=[];for(let e of Tf(t))i?n.push({from:e.from,insert:t.doc.slice(e.from,e.to)+t.lineBreak}):n.push({from:e.to,insert:t.lineBreak+t.doc.slice(e.from,e.to)});return e(t.update({changes:n,scrollIntoView:!0,userEvent:"input.copyline"})),!0}const Xf=Yf(!1);function Yf(t){return({state:i,dispatch:n})=>{if(i.readOnly)return!1;let s=i.changeByRange((n=>{let{from:s,to:r}=n,o=i.doc.lineAt(s),a=!t&&s==r&&function(t,e){if(/\(\)|\[\]|\{\}/.test(t.sliceDoc(e-1,e+1)))return{from:e,to:e};let i,n=cl(t).resolveInner(e),s=n.childBefore(e),r=n.childAfter(e);return s&&r&&s.to<=e&&r.from>=e&&(i=s.type.prop(Jo.closedBy))&&i.indexOf(r.name)>-1&&t.doc.lineAt(s.to).from==t.doc.lineAt(r.from).from&&!/\S/.test(t.sliceDoc(s.to,r.from))?{from:s.to,to:r.from}:null}(i,s);t&&(s=r=(r<=o.to?o:i.doc.lineAt(r)).to);let l=new Zl(i,{simulateBreak:s,simulateDoubleBreak:!!a}),h=Pl(l,s);for(null==h&&(h=It(/^\s*/.exec(i.doc.lineAt(s).text)[0],i.tabSize));r<o.to&&/\s/.test(o.text[r-o.from]);)r++;a?({from:s,to:r}=a):s>o.from&&s<o.from+100&&!/\S/.test(o.text.slice(0,s))&&(s=o.from);let c=["",$l(i,h)];return a&&c.push($l(i,l.lineIndent(o.from,-1))),{changes:{from:s,to:r,insert:e.of(c)},range:Y.cursor(s+1+c[1].length)}}));return n(i.update(s,{scrollIntoView:!0,userEvent:"input"})),!0}}function Wf(t,e){let i=-1;return t.changeByRange((n=>{let s=[];for(let r=n.from;r<=n.to;){let o=t.doc.lineAt(r);o.number>i&&(n.empty||n.to>o.from)&&(e(o,s,n),i=o.number),r=o.to+1}let r=t.changes(s);return{changes:s,range:Y.range(r.mapPos(n.anchor,1),r.mapPos(n.head,1))}}))}const Mf=[{key:"Alt-ArrowLeft",mac:"Ctrl-ArrowLeft",run:t=>zc(t,(e=>Jc(t.state,e,!Lc(t)))),shift:t=>lf(t,(e=>Jc(t.state,e,!Lc(t))))},{key:"Alt-ArrowRight",mac:"Ctrl-ArrowRight",run:t=>zc(t,(e=>Jc(t.state,e,Lc(t)))),shift:t=>lf(t,(e=>Jc(t.state,e,Lc(t))))},{key:"Alt-ArrowUp",run:({state:t,dispatch:e})=>Af(t,e,!1)},{key:"Shift-Alt-ArrowUp",run:({state:t,dispatch:e})=>Rf(t,e,!1)},{key:"Alt-ArrowDown",run:({state:t,dispatch:e})=>Af(t,e,!0)},{key:"Shift-Alt-ArrowDown",run:({state:t,dispatch:e})=>Rf(t,e,!0)},{key:"Escape",run:({state:t,dispatch:e})=>{let i=t.selection,n=null;return i.ranges.length>1?n=Y.create([i.main]):i.main.empty||(n=Y.create([Y.cursor(i.main.head)])),!!n&&(e(Ic(t,n)),!0)}},{key:"Mod-Enter",run:Yf(!0)},{key:"Alt-l",mac:"Ctrl-l",run:({state:t,dispatch:e})=>{let i=Tf(t).map((({from:e,to:i})=>Y.range(e,Math.min(i+1,t.doc.length))));return e(t.update({selection:Y.create(i),userEvent:"select"})),!0}},{key:"Mod-i",run:({state:t,dispatch:e})=>{let i=Vc(t.selection,(e=>{var i;for(let n=cl(t).resolveStack(e.from,1);n;n=n.next){let{node:t}=n;if((t.from<e.from&&t.to>=e.to||t.to>e.to&&t.from<=e.from)&&(null===(i=t.parent)||void 0===i?void 0:i.parent))return Y.range(t.to,t.from)}return e}));return e(Ic(t,i)),!0},preventDefault:!0},{key:"Mod-[",run:({state:t,dispatch:e})=>!t.readOnly&&(e(t.update(Wf(t,((e,i)=>{let n=/^\s*/.exec(e.text)[0];if(!n)return;let s=It(n,t.tabSize),r=0,o=$l(t,Math.max(0,s-Ql(t)));for(;r<n.length&&r<o.length&&n.charCodeAt(r)==o.charCodeAt(r);)r++;i.push({from:e.from+r,to:e.from+n.length,insert:o.slice(r)})})),{userEvent:"delete.dedent"})),!0)},{key:"Mod-]",run:({state:t,dispatch:e})=>!t.readOnly&&(e(t.update(Wf(t,((e,i)=>{i.push({from:e.from,insert:t.facet(kl)})})),{userEvent:"input.indent"})),!0)},{key:"Mod-Alt-\\",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=Object.create(null),n=new Zl(t,{overrideIndentation:t=>{let e=i[t];return null==e?-1:e}}),s=Wf(t,((e,s,r)=>{let o=Pl(n,e.from);if(null==o)return;/\S/.test(e.text)||(o=0);let a=/^\s*/.exec(e.text)[0],l=$l(t,o);(a!=l||r.from<e.from+a.length)&&(i[e.from]=o,s.push({from:e.from,to:e.from+a.length,insert:l}))}));return s.changes.empty||e(t.update(s,{userEvent:"indent"})),!0}},{key:"Shift-Mod-k",run:t=>{if(t.state.readOnly)return!1;let{state:e}=t,i=e.changes(Tf(e).map((({from:t,to:i})=>(t>0?t--:i<e.doc.length&&i++,{from:t,to:i})))),n=Vc(e.selection,(e=>t.moveVertically(e,!0))).map(i);return t.dispatch({changes:i,selection:n,scrollIntoView:!0,userEvent:"delete.line"}),!0}},{key:"Shift-Mod-\\",run:({state:t,dispatch:e})=>function(t,e,i){let n=!1,s=Vc(t.selection,(e=>{let s=jh(t,e.head,-1)||jh(t,e.head,1)||e.head>0&&jh(t,e.head-1,1)||e.head<t.doc.length&&jh(t,e.head+1,-1);if(!s||!s.end)return e;n=!0;let r=s.start.from==e.head?s.end.to:s.end.from;return i?Y.range(e.anchor,r):Y.cursor(r)}));return!!n&&(e(Ic(t,s)),!0)}(t,e,!1)},{key:"Mod-/",run:t=>{let{state:e}=t,i=e.doc.lineAt(e.selection.main.from),n=Oc(t.state,i.from);return n.line?uc(t):!!n.block&&pc(t)}},{key:"Alt-A",run:dc}].concat([{key:"ArrowLeft",run:Nc,shift:cf,preventDefault:!0},{key:"Mod-ArrowLeft",mac:"Alt-ArrowLeft",run:t=>Hc(t,!Lc(t)),shift:t=>uf(t,!Lc(t)),preventDefault:!0},{mac:"Cmd-ArrowLeft",run:t=>zc(t,(e=>af(t,e,!Lc(t)))),shift:t=>lf(t,(e=>af(t,e,!Lc(t)))),preventDefault:!0},{key:"ArrowRight",run:Uc,shift:ff,preventDefault:!0},{key:"Mod-ArrowRight",mac:"Alt-ArrowRight",run:t=>Hc(t,Lc(t)),shift:t=>uf(t,Lc(t)),preventDefault:!0},{mac:"Cmd-ArrowRight",run:t=>zc(t,(e=>af(t,e,Lc(t)))),shift:t=>lf(t,(e=>af(t,e,Lc(t)))),preventDefault:!0},{key:"ArrowUp",run:tf,shift:pf,preventDefault:!0},{mac:"Cmd-ArrowUp",run:vf,shift:yf},{mac:"Ctrl-ArrowUp",run:rf,shift:mf},{key:"ArrowDown",run:ef,shift:Of,preventDefault:!0},{mac:"Cmd-ArrowDown",run:bf,shift:Sf},{mac:"Ctrl-ArrowDown",run:of,shift:wf},{key:"PageUp",run:rf,shift:mf},{key:"PageDown",run:of,shift:wf},{key:"Home",run:t=>zc(t,(e=>af(t,e,!1))),shift:t=>lf(t,(e=>af(t,e,!1))),preventDefault:!0},{key:"Mod-Home",run:vf,shift:yf},{key:"End",run:t=>zc(t,(e=>af(t,e,!0))),shift:t=>lf(t,(e=>af(t,e,!0))),preventDefault:!0},{key:"Mod-End",run:bf,shift:Sf},{key:"Enter",run:Xf},{key:"Mod-a",run:({state:t,dispatch:e})=>(e(t.update({selection:{anchor:0,head:t.doc.length},userEvent:"select"})),!0)},{key:"Backspace",run:$f,shift:$f},{key:"Delete",run:Pf},{key:"Mod-Backspace",mac:"Alt-Backspace",run:Cf},{key:"Mod-Delete",mac:"Alt-Delete",run:t=>Zf(t,!0)},{mac:"Mod-Backspace",run:t=>xf(t,(e=>{let i=t.moveToLineBoundary(e,!1).head;return e.head>i?i:Math.max(0,e.head-1)}))},{mac:"Mod-Delete",run:t=>xf(t,(e=>{let i=t.moveToLineBoundary(e,!0).head;return e.head<i?i:Math.min(t.state.doc.length,e.head+1)}))}].concat([{key:"Ctrl-b",run:Nc,shift:cf,preventDefault:!0},{key:"Ctrl-f",run:Uc,shift:ff},{key:"Ctrl-p",run:tf,shift:pf},{key:"Ctrl-n",run:ef,shift:Of},{key:"Ctrl-a",run:t=>zc(t,(e=>Y.cursor(t.lineBlockAt(e.head).from,1))),shift:t=>lf(t,(e=>Y.cursor(t.lineBlockAt(e.head).from)))},{key:"Ctrl-e",run:t=>zc(t,(e=>Y.cursor(t.lineBlockAt(e.head).to,-1))),shift:t=>lf(t,(e=>Y.cursor(t.lineBlockAt(e.head).to)))},{key:"Ctrl-d",run:Pf},{key:"Ctrl-h",run:$f},{key:"Ctrl-k",run:t=>xf(t,(e=>{let i=t.lineBlockAt(e.head).to;return e.head<i?i:Math.min(t.state.doc.length,e.head+1)}))},{key:"Ctrl-Alt-h",run:Cf},{key:"Ctrl-o",run:({state:t,dispatch:i})=>{if(t.readOnly)return!1;let n=t.changeByRange((t=>({changes:{from:t.from,to:t.to,insert:e.of(["",""])},range:Y.cursor(t.from)})));return i(t.update(n,{scrollIntoView:!0,userEvent:"input"})),!0}},{key:"Ctrl-t",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=t.changeByRange((e=>{if(!e.empty||0==e.from||e.from==t.doc.length)return{range:e};let i=e.from,n=t.doc.lineAt(i),s=i==n.from?i-1:O(n.text,i-n.from,!1)+n.from,r=i==n.to?i+1:O(n.text,i-n.from,!0)+n.from;return{changes:{from:s,to:r,insert:t.doc.slice(i,r).append(t.doc.slice(s,i))},range:Y.cursor(r)}}));return!i.changes.empty&&(e(t.update(i,{scrollIntoView:!0,userEvent:"move.character"})),!0)}},{key:"Ctrl-v",run:of}].map((t=>({mac:t.key,run:t.run,shift:t.shift})))));function jf(){var t=arguments[0];"string"==typeof t&&(t=document.createElement(t));var e=1,i=arguments[1];if(i&&"object"==typeof i&&null==i.nodeType&&!Array.isArray(i)){for(var n in i)if(Object.prototype.hasOwnProperty.call(i,n)){var s=i[n];"string"==typeof s?t.setAttribute(n,s):null!=s&&(t[n]=s)}e++}for(;e<arguments.length;e++)Df(t,arguments[e]);return t}function Df(t,e){if("string"==typeof e)t.appendChild(document.createTextNode(e));else if(null==e);else if(null!=e.nodeType)t.appendChild(e);else{if(!Array.isArray(e))throw new RangeError("Unsupported child node: "+e);for(var i=0;i<e.length;i++)Df(t,e[i])}}const Ef="function"==typeof String.prototype.normalize?t=>t.normalize("NFKD"):t=>t;class qf{constructor(t,e,i=0,n=t.length,s,r){this.test=r,this.value={from:0,to:0},this.done=!1,this.matches=[],this.buffer="",this.bufferPos=0,this.iter=t.iterRange(i,n),this.bufferStart=i,this.normalize=s?t=>s(Ef(t)):Ef,this.query=this.normalize(e)}peek(){if(this.bufferPos==this.buffer.length){if(this.bufferStart+=this.buffer.length,this.iter.next(),this.iter.done)return-1;this.bufferPos=0,this.buffer=this.iter.value}return b(this.buffer,this.bufferPos)}next(){for(;this.matches.length;)this.matches.pop();return this.nextOverlapping()}nextOverlapping(){for(;;){let t=this.peek();if(t<0)return this.done=!0,this;let e=y(t),i=this.bufferStart+this.bufferPos;this.bufferPos+=S(t);let n=this.normalize(e);for(let t=0,s=i;;t++){let r=n.charCodeAt(t),o=this.match(r,s);if(t==n.length-1){if(o)return this.value=o,this;break}s==i&&t<e.length&&e.charCodeAt(t)==r&&s++}}}match(t,e){let i=null;for(let n=0;n<this.matches.length;n+=2){let s=this.matches[n],r=!1;this.query.charCodeAt(s)==t&&(s==this.query.length-1?i={from:this.matches[n+1],to:e+1}:(this.matches[n]++,r=!0)),r||(this.matches.splice(n,2),n-=2)}return this.query.charCodeAt(0)==t&&(1==this.query.length?i={from:e,to:e+1}:this.matches.push(1,e)),i&&this.test&&!this.test(i.from,i.to,this.buffer,this.bufferStart)&&(i=null),i}}"undefined"!=typeof Symbol&&(qf.prototype[Symbol.iterator]=function(){return this});const _f={from:-1,to:-1,match:/.*/.exec("")},Vf="gm"+(null==/x/.unicode?"":"u");class If{constructor(t,e,i,n=0,s=t.length){if(this.text=t,this.to=s,this.curLine="",this.done=!1,this.value=_f,/\\[sWDnr]|\n|\r|\[\^/.test(e))return new Gf(t,e,i,n,s);this.re=new RegExp(e,Vf+((null==i?void 0:i.ignoreCase)?"i":"")),this.test=null==i?void 0:i.test,this.iter=t.iter();let r=t.lineAt(n);this.curLineStart=r.from,this.matchPos=Lf(t,n),this.getLine(this.curLineStart)}getLine(t){this.iter.next(t),this.iter.lineBreak?this.curLine="":(this.curLine=this.iter.value,this.curLineStart+this.curLine.length>this.to&&(this.curLine=this.curLine.slice(0,this.to-this.curLineStart)),this.iter.next())}nextLine(){this.curLineStart=this.curLineStart+this.curLine.length+1,this.curLineStart>this.to?this.curLine="":this.getLine(0)}next(){for(let t=this.matchPos-this.curLineStart;;){this.re.lastIndex=t;let e=this.matchPos<=this.to&&this.re.exec(this.curLine);if(e){let i=this.curLineStart+e.index,n=i+e[0].length;if(this.matchPos=Lf(this.text,n+(i==n?1:0)),i==this.curLineStart+this.curLine.length&&this.nextLine(),(i<n||i>this.value.to)&&(!this.test||this.test(i,n,e)))return this.value={from:i,to:n,match:e},this;t=this.matchPos-this.curLineStart}else{if(!(this.curLineStart+this.curLine.length<this.to))return this.done=!0,this;this.nextLine(),t=0}}}}const zf=new WeakMap;class Bf{constructor(t,e){this.from=t,this.text=e}get to(){return this.from+this.text.length}static get(t,e,i){let n=zf.get(t);if(!n||n.from>=i||n.to<=e){let n=new Bf(e,t.sliceString(e,i));return zf.set(t,n),n}if(n.from==e&&n.to==i)return n;let{text:s,from:r}=n;return r>e&&(s=t.sliceString(e,r)+s,r=e),n.to<i&&(s+=t.sliceString(n.to,i)),zf.set(t,new Bf(r,s)),new Bf(e,s.slice(e-r,i-r))}}class Gf{constructor(t,e,i,n,s){this.text=t,this.to=s,this.done=!1,this.value=_f,this.matchPos=Lf(t,n),this.re=new RegExp(e,Vf+((null==i?void 0:i.ignoreCase)?"i":"")),this.test=null==i?void 0:i.test,this.flat=Bf.get(t,n,this.chunkEnd(n+5e3))}chunkEnd(t){return t>=this.to?this.to:this.text.lineAt(t).to}next(){for(;;){let t=this.re.lastIndex=this.matchPos-this.flat.from,e=this.re.exec(this.flat.text);if(e&&!e[0]&&e.index==t&&(this.re.lastIndex=t+1,e=this.re.exec(this.flat.text)),e){let t=this.flat.from+e.index,i=t+e[0].length;if((this.flat.to>=this.to||e.index+e[0].length<=this.flat.text.length-10)&&(!this.test||this.test(t,i,e)))return this.value={from:t,to:i,match:e},this.matchPos=Lf(this.text,i+(t==i?1:0)),this}if(this.flat.to==this.to)return this.done=!0,this;this.flat=Bf.get(this.text,this.flat.from,this.chunkEnd(this.flat.from+2*this.flat.text.length))}}}function Lf(t,e){if(e>=t.length)return e;let i,n=t.lineAt(e);for(;e<n.to&&(i=n.text.charCodeAt(e-n.from))>=56320&&i<57344;)e++;return e}function Nf(t){let e=jf("input",{class:"cm-textfield",name:"line",value:String(t.state.doc.lineAt(t.state.selection.main.head).number)});function i(){let i=/^([+-])?(\d+)?(:\d+)?(%)?$/.exec(e.value);if(!i)return;let{state:n}=t,s=n.doc.lineAt(n.selection.main.head),[,r,o,a,l]=i,h=a?+a.slice(1):0,c=o?+o:s.number;if(o&&l){let t=c/100;r&&(t=t*("-"==r?-1:1)+s.number/n.doc.lines),c=Math.round(n.doc.lines*t)}else o&&r&&(c=c*("-"==r?-1:1)+s.number);let f=n.doc.line(Math.max(1,Math.min(n.doc.lines,c))),u=Y.cursor(f.from+Math.max(0,Math.min(h,f.length)));t.dispatch({effects:[Uf.of(!1),Gs.scrollIntoView(u.from,{y:"center"})],selection:u}),t.focus()}return{dom:jf("form",{class:"cm-gotoLine",onkeydown:e=>{27==e.keyCode?(e.preventDefault(),t.dispatch({effects:Uf.of(!1)}),t.focus()):13==e.keyCode&&(e.preventDefault(),i())},onsubmit:t=>{t.preventDefault(),i()}},jf("label",t.state.phrase("Go to line"),": ",e)," ",jf("button",{class:"cm-button",type:"submit"},t.state.phrase("go")))}}"undefined"!=typeof Symbol&&(If.prototype[Symbol.iterator]=Gf.prototype[Symbol.iterator]=function(){return this});const Uf=dt.define(),Hf=z.define({create:()=>!0,update(t,e){for(let i of e.effects)i.is(Uf)&&(t=i.value);return t},provide:t=>vo.from(t,(t=>t?Nf:null))}),Ff=Gs.baseTheme({".cm-panel.cm-gotoLine":{padding:"2px 6px 4px","& label":{fontSize:"80%"}}}),Jf={highlightWordAroundCursor:!1,minSelectionLength:1,maxMatches:100,wholeWords:!1},Kf=j.define({combine:t=>$t(t,Jf,{highlightWordAroundCursor:(t,e)=>t||e,minSelectionLength:Math.min,maxMatches:Math.min})});function tu(t){let e=[ru,su];return t&&e.push(Kf.of(t)),e}const eu=si.mark({class:"cm-selectionMatch"}),iu=si.mark({class:"cm-selectionMatch cm-selectionMatch-main"});function nu(t,e,i,n){return!(0!=i&&t(e.sliceDoc(i-1,i))==yt.Word||n!=e.doc.length&&t(e.sliceDoc(n,n+1))==yt.Word)}const su=Ni.fromClass(class{constructor(t){this.decorations=this.getDeco(t)}update(t){(t.selectionSet||t.docChanged||t.viewportChanged)&&(this.decorations=this.getDeco(t.view))}getDeco(t){let e=t.state.facet(Kf),{state:i}=t,n=i.selection;if(n.ranges.length>1)return si.none;let s,r=n.main,o=null;if(r.empty){if(!e.highlightWordAroundCursor)return si.none;let t=i.wordAt(r.head);if(!t)return si.none;o=i.charCategorizer(r.head),s=i.sliceDoc(t.from,t.to)}else{let t=r.to-r.from;if(t<e.minSelectionLength||t>200)return si.none;if(e.wholeWords){if(s=i.sliceDoc(r.from,r.to),o=i.charCategorizer(r.head),!nu(o,i,r.from,r.to)||!function(t,e,i,n){return t(e.sliceDoc(i,i+1))==yt.Word&&t(e.sliceDoc(n-1,n))==yt.Word}(o,i,r.from,r.to))return si.none}else if(s=i.sliceDoc(r.from,r.to).trim(),!s)return si.none}let a=[];for(let n of t.visibleRanges){let t=new qf(i.doc,s,n.from,n.to);for(;!t.next().done;){let{from:n,to:s}=t.value;if((!o||nu(o,i,n,s))&&(r.empty&&n<=r.from&&s>=r.to?a.push(iu.range(n,s)):(n>=r.to||s<=r.from)&&a.push(eu.range(n,s)),a.length>e.maxMatches))return si.none}}return si.set(a)}},{decorations:t=>t.decorations}),ru=Gs.baseTheme({".cm-selectionMatch":{backgroundColor:"#99ff7780"},".cm-searchMatch .cm-selectionMatch":{backgroundColor:"transparent"}});const ou=j.define({combine:t=>$t(t,{top:!1,caseSensitive:!1,literal:!1,regexp:!1,wholeWord:!1,createPanel:t=>new Wu(t),scrollToMatch:t=>Gs.scrollIntoView(t)})});class au{constructor(t){this.search=t.search,this.caseSensitive=!!t.caseSensitive,this.literal=!!t.literal,this.regexp=!!t.regexp,this.replace=t.replace||"",this.valid=!!this.search&&(!this.regexp||function(t){try{return new RegExp(t,Vf),!0}catch(t){return!1}}(this.search)),this.unquoted=this.unquote(this.search),this.wholeWord=!!t.wholeWord}unquote(t){return this.literal?t:t.replace(/\\([nrt\\])/g,((t,e)=>"n"==e?"\n":"r"==e?"\r":"t"==e?"\t":"\\"))}eq(t){return this.search==t.search&&this.replace==t.replace&&this.caseSensitive==t.caseSensitive&&this.regexp==t.regexp&&this.wholeWord==t.wholeWord}create(){return this.regexp?new pu(this):new cu(this)}getCursor(t,e=0,i){let n=t.doc?t:Qt.create({doc:t});return null==i&&(i=n.doc.length),this.regexp?fu(this,n,e,i):hu(this,n,e,i)}}class lu{constructor(t){this.spec=t}}function hu(t,e,i,n){return new qf(e.doc,t.unquoted,i,n,t.caseSensitive?void 0:t=>t.toLowerCase(),t.wholeWord?function(t,e){return(i,n,s,r)=>((r>i||r+s.length<n)&&(r=Math.max(0,i-2),s=t.sliceString(r,Math.min(t.length,n+2))),!(e(uu(s,i-r))==yt.Word&&e(du(s,i-r))==yt.Word||e(du(s,n-r))==yt.Word&&e(uu(s,n-r))==yt.Word))}(e.doc,e.charCategorizer(e.selection.main.head)):void 0)}class cu extends lu{constructor(t){super(t)}nextMatch(t,e,i){let n=hu(this.spec,t,i,t.doc.length).nextOverlapping();return n.done&&(n=hu(this.spec,t,0,e).nextOverlapping()),n.done?null:n.value}prevMatchInRange(t,e,i){for(let n=i;;){let i=Math.max(e,n-1e4-this.spec.unquoted.length),s=hu(this.spec,t,i,n),r=null;for(;!s.nextOverlapping().done;)r=s.value;if(r)return r;if(i==e)return null;n-=1e4}}prevMatch(t,e,i){return this.prevMatchInRange(t,0,e)||this.prevMatchInRange(t,i,t.doc.length)}getReplacement(t){return this.spec.unquote(this.spec.replace)}matchAll(t,e){let i=hu(this.spec,t,0,t.doc.length),n=[];for(;!i.next().done;){if(n.length>=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let s=hu(this.spec,t,Math.max(0,e-this.spec.unquoted.length),Math.min(i+this.spec.unquoted.length,t.doc.length));for(;!s.next().done;)n(s.value.from,s.value.to)}}function fu(t,e,i,n){return new If(e.doc,t.search,{ignoreCase:!t.caseSensitive,test:t.wholeWord?(s=e.charCategorizer(e.selection.main.head),(t,e,i)=>!i[0].length||(s(uu(i.input,i.index))!=yt.Word||s(du(i.input,i.index))!=yt.Word)&&(s(du(i.input,i.index+i[0].length))!=yt.Word||s(uu(i.input,i.index+i[0].length))!=yt.Word)):void 0},i,n);var s}function uu(t,e){return t.slice(O(t,e,!1),e)}function du(t,e){return t.slice(e,O(t,e))}class pu extends lu{nextMatch(t,e,i){let n=fu(this.spec,t,i,t.doc.length).next();return n.done&&(n=fu(this.spec,t,0,e).next()),n.done?null:n.value}prevMatchInRange(t,e,i){for(let n=1;;n++){let s=Math.max(e,i-1e4*n),r=fu(this.spec,t,s,i),o=null;for(;!r.next().done;)o=r.value;if(o&&(s==e||o.from>s+10))return o;if(s==e)return null}}prevMatch(t,e,i){return this.prevMatchInRange(t,0,e)||this.prevMatchInRange(t,i,t.doc.length)}getReplacement(t){return this.spec.unquote(this.spec.replace).replace(/\$([$&\d+])/g,((e,i)=>"$"==i?"$":"&"==i?t.match[0]:"0"!=i&&+i<t.match.length?t.match[i]:e))}matchAll(t,e){let i=fu(this.spec,t,0,t.doc.length),n=[];for(;!i.next().done;){if(n.length>=e)return null;n.push(i.value)}return n}highlight(t,e,i,n){let s=fu(this.spec,t,Math.max(0,e-250),Math.min(i+250,t.doc.length));for(;!s.next().done;)n(s.value.from,s.value.to)}}const Ou=dt.define(),gu=dt.define(),mu=z.define({create:t=>new wu(Cu(t).create(),null),update(t,e){for(let i of e.effects)i.is(Ou)?t=new wu(i.value.create(),t.panel):i.is(gu)&&(t=new wu(t.query,i.value?Zu:null));return t},provide:t=>vo.from(t,(t=>t.panel))});class wu{constructor(t,e){this.query=t,this.panel=e}}const vu=si.mark({class:"cm-searchMatch"}),bu=si.mark({class:"cm-searchMatch cm-searchMatch-selected"}),yu=Ni.fromClass(class{constructor(t){this.view=t,this.decorations=this.highlight(t.state.field(mu))}update(t){let e=t.state.field(mu);(e!=t.startState.field(mu)||t.docChanged||t.selectionSet||t.viewportChanged)&&(this.decorations=this.highlight(e))}highlight({query:t,panel:e}){if(!e||!t.spec.valid)return si.none;let{view:i}=this,n=new Rt;for(let e=0,s=i.visibleRanges,r=s.length;e<r;e++){let{from:o,to:a}=s[e];for(;e<r-1&&a>s[e+1].from-500;)a=s[++e].to;t.highlight(i.state,o,a,((t,e)=>{let s=i.state.selection.ranges.some((i=>i.from==t&&i.to==e));n.add(t,e,s?bu:vu)}))}return n.finish()}},{decorations:t=>t.decorations});function Su(t){return e=>{let i=e.state.field(mu,!1);return i&&i.query.spec.valid?t(e,i):Ru(e)}}const xu=Su(((t,{query:e})=>{let{to:i}=t.state.selection.main,n=e.nextMatch(t.state,i,i);if(!n)return!1;let s=Y.single(n.from,n.to),r=t.state.facet(ou);return t.dispatch({selection:s,effects:[Eu(t,n),r.scrollToMatch(s.main,t)],userEvent:"select.search"}),Au(t),!0})),ku=Su(((t,{query:e})=>{let{state:i}=t,{from:n}=i.selection.main,s=e.prevMatch(i,n,n);if(!s)return!1;let r=Y.single(s.from,s.to),o=t.state.facet(ou);return t.dispatch({selection:r,effects:[Eu(t,s),o.scrollToMatch(r.main,t)],userEvent:"select.search"}),Au(t),!0})),Qu=Su(((t,{query:e})=>{let i=e.matchAll(t.state,1e3);return!(!i||!i.length)&&(t.dispatch({selection:Y.create(i.map((t=>Y.range(t.from,t.to)))),userEvent:"select.search.matches"}),!0)})),$u=Su(((t,{query:e})=>{let{state:i}=t,{from:n,to:s}=i.selection.main;if(i.readOnly)return!1;let r=e.nextMatch(i,n,n);if(!r)return!1;let o,a,l=[],h=[];if(r.from==n&&r.to==s&&(a=i.toText(e.getReplacement(r)),l.push({from:r.from,to:r.to,insert:a}),r=e.nextMatch(i,r.from,r.to),h.push(Gs.announce.of(i.phrase("replaced match on line $",i.doc.lineAt(n).number)+"."))),r){let e=0==l.length||l[0].from>=r.to?0:r.to-r.from-a.length;o=Y.single(r.from-e,r.to-e),h.push(Eu(t,r)),h.push(i.facet(ou).scrollToMatch(o.main,t))}return t.dispatch({changes:l,selection:o,effects:h,userEvent:"input.replace"}),!0})),Pu=Su(((t,{query:e})=>{if(t.state.readOnly)return!1;let i=e.matchAll(t.state,1e9).map((t=>{let{from:i,to:n}=t;return{from:i,to:n,insert:e.getReplacement(t)}}));if(!i.length)return!1;let n=t.state.phrase("replaced $ matches",i.length)+".";return t.dispatch({changes:i,effects:Gs.announce.of(n),userEvent:"input.replace.all"}),!0}));function Zu(t){return t.state.facet(ou).createPanel(t)}function Cu(t,e){var i,n,s,r,o;let a=t.selection.main,l=a.empty||a.to>a.from+100?"":t.sliceDoc(a.from,a.to);if(e&&!l)return e;let h=t.facet(ou);return new au({search:(null!==(i=null==e?void 0:e.literal)&&void 0!==i?i:h.literal)?l:l.replace(/\n/g,"\\n"),caseSensitive:null!==(n=null==e?void 0:e.caseSensitive)&&void 0!==n?n:h.caseSensitive,literal:null!==(s=null==e?void 0:e.literal)&&void 0!==s?s:h.literal,regexp:null!==(r=null==e?void 0:e.regexp)&&void 0!==r?r:h.regexp,wholeWord:null!==(o=null==e?void 0:e.wholeWord)&&void 0!==o?o:h.wholeWord})}function Tu(t){let e=Oo(t,Zu);return e&&e.dom.querySelector("[main-field]")}function Au(t){let e=Tu(t);e&&e==t.root.activeElement&&e.select()}const Ru=t=>{let e=t.state.field(mu,!1);if(e&&e.panel){let i=Tu(t);if(i&&i!=t.root.activeElement){let n=Cu(t.state,e.query.spec);n.valid&&t.dispatch({effects:Ou.of(n)}),i.focus(),i.select()}}else t.dispatch({effects:[gu.of(!0),e?Ou.of(Cu(t.state,e.query.spec)):dt.appendConfig.of(_u)]});return!0},Xu=t=>{let e=t.state.field(mu,!1);if(!e||!e.panel)return!1;let i=Oo(t,Zu);return i&&i.dom.contains(t.root.activeElement)&&t.focus(),t.dispatch({effects:gu.of(!1)}),!0},Yu=[{key:"Mod-f",run:Ru,scope:"editor search-panel"},{key:"F3",run:xu,shift:ku,scope:"editor search-panel",preventDefault:!0},{key:"Mod-g",run:xu,shift:ku,scope:"editor search-panel",preventDefault:!0},{key:"Escape",run:Xu,scope:"editor search-panel"},{key:"Mod-Shift-l",run:({state:t,dispatch:e})=>{let i=t.selection;if(i.ranges.length>1||i.main.empty)return!1;let{from:n,to:s}=i.main,r=[],o=0;for(let e=new qf(t.doc,t.sliceDoc(n,s));!e.next().done;){if(r.length>1e3)return!1;e.value.from==n&&(o=r.length),r.push(Y.range(e.value.from,e.value.to))}return e(t.update({selection:Y.create(r,o),userEvent:"select.search.matches"})),!0}},{key:"Mod-Alt-g",run:t=>{let e=Oo(t,Nf);if(!e){let i=[Uf.of(!0)];null==t.state.field(Hf,!1)&&i.push(dt.appendConfig.of([Hf,Ff])),t.dispatch({effects:i}),e=Oo(t,Nf)}return e&&e.dom.querySelector("input").select(),!0}},{key:"Mod-d",run:({state:t,dispatch:e})=>{let{ranges:i}=t.selection;if(i.some((t=>t.from===t.to)))return(({state:t,dispatch:e})=>{let{selection:i}=t,n=Y.create(i.ranges.map((e=>t.wordAt(e.head)||Y.cursor(e.head))),i.mainIndex);return!n.eq(i)&&(e(t.update({selection:n})),!0)})({state:t,dispatch:e});let n=t.sliceDoc(i[0].from,i[0].to);if(t.selection.ranges.some((e=>t.sliceDoc(e.from,e.to)!=n)))return!1;let s=function(t,e){let{main:i,ranges:n}=t.selection,s=t.wordAt(i.head),r=s&&s.from==i.from&&s.to==i.to;for(let i=!1,s=new qf(t.doc,e,n[n.length-1].to);;){if(s.next(),!s.done){if(i&&n.some((t=>t.from==s.value.from)))continue;if(r){let e=t.wordAt(s.value.from);if(!e||e.from!=s.value.from||e.to!=s.value.to)continue}return s.value}if(i)return null;s=new qf(t.doc,e,0,Math.max(0,n[n.length-1].from-1)),i=!0}}(t,n);return!!s&&(e(t.update({selection:t.selection.addRange(Y.range(s.from,s.to),!1),effects:Gs.scrollIntoView(s.to)})),!0)},preventDefault:!0}];class Wu{constructor(t){this.view=t;let e=this.query=t.state.field(mu).query.spec;function i(t,e,i){return jf("button",{class:"cm-button",name:t,onclick:e,type:"button"},i)}this.commit=this.commit.bind(this),this.searchField=jf("input",{value:e.search,placeholder:Mu(t,"Find"),"aria-label":Mu(t,"Find"),class:"cm-textfield",name:"search",form:"","main-field":"true",onchange:this.commit,onkeyup:this.commit}),this.replaceField=jf("input",{value:e.replace,placeholder:Mu(t,"Replace"),"aria-label":Mu(t,"Replace"),class:"cm-textfield",name:"replace",form:"",onchange:this.commit,onkeyup:this.commit}),this.caseField=jf("input",{type:"checkbox",name:"case",form:"",checked:e.caseSensitive,onchange:this.commit}),this.reField=jf("input",{type:"checkbox",name:"re",form:"",checked:e.regexp,onchange:this.commit}),this.wordField=jf("input",{type:"checkbox",name:"word",form:"",checked:e.wholeWord,onchange:this.commit}),this.dom=jf("div",{onkeydown:t=>this.keydown(t),class:"cm-search"},[this.searchField,i("next",(()=>xu(t)),[Mu(t,"next")]),i("prev",(()=>ku(t)),[Mu(t,"previous")]),i("select",(()=>Qu(t)),[Mu(t,"all")]),jf("label",null,[this.caseField,Mu(t,"match case")]),jf("label",null,[this.reField,Mu(t,"regexp")]),jf("label",null,[this.wordField,Mu(t,"by word")]),...t.state.readOnly?[]:[jf("br"),this.replaceField,i("replace",(()=>$u(t)),[Mu(t,"replace")]),i("replaceAll",(()=>Pu(t)),[Mu(t,"replace all")])],jf("button",{name:"close",onclick:()=>Xu(t),"aria-label":Mu(t,"close"),type:"button"},["×"])])}commit(){let t=new au({search:this.searchField.value,caseSensitive:this.caseField.checked,regexp:this.reField.checked,wholeWord:this.wordField.checked,replace:this.replaceField.value});t.eq(this.query)||(this.query=t,this.view.dispatch({effects:Ou.of(t)}))}keydown(t){nr(this.view,t,"search-panel")?t.preventDefault():13==t.keyCode&&t.target==this.searchField?(t.preventDefault(),(t.shiftKey?ku:xu)(this.view)):13==t.keyCode&&t.target==this.replaceField&&(t.preventDefault(),$u(this.view))}update(t){for(let e of t.transactions)for(let t of e.effects)t.is(Ou)&&!t.value.eq(this.query)&&this.setQuery(t.value)}setQuery(t){this.query=t,this.searchField.value=t.search,this.replaceField.value=t.replace,this.caseField.checked=t.caseSensitive,this.reField.checked=t.regexp,this.wordField.checked=t.wholeWord}mount(){this.searchField.select()}get pos(){return 80}get top(){return this.view.state.facet(ou).top}}function Mu(t,e){return t.state.phrase(e)}const ju=30,Du=/[\s\.,:;?!]/;function Eu(t,{from:e,to:i}){let n=t.state.doc.lineAt(e),s=t.state.doc.lineAt(i).to,r=Math.max(n.from,e-ju),o=Math.min(s,i+ju),a=t.state.sliceDoc(r,o);if(r!=n.from)for(let t=0;t<ju;t++)if(!Du.test(a[t+1])&&Du.test(a[t])){a=a.slice(t);break}if(o!=s)for(let t=a.length-1;t>a.length-ju;t--)if(!Du.test(a[t-1])&&Du.test(a[t])){a=a.slice(0,t);break}return Gs.announce.of(`${t.state.phrase("current match")}. ${a} ${t.state.phrase("on line")} ${n.number}.`)}const qu=Gs.baseTheme({".cm-panel.cm-search":{padding:"2px 6px 4px",position:"relative","& [name=close]":{position:"absolute",top:"0",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",padding:0,margin:0},"& input, & button, & label":{margin:".2em .6em .2em 0"},"& input[type=checkbox]":{marginRight:".2em"},"& label":{fontSize:"80%",whiteSpace:"pre"}},"&light .cm-searchMatch":{backgroundColor:"#ffff0054"},"&dark .cm-searchMatch":{backgroundColor:"#00ffff8a"},"&light .cm-searchMatch-selected":{backgroundColor:"#ff6a0054"},"&dark .cm-searchMatch-selected":{backgroundColor:"#ff00ff8a"}}),_u=[mu,H.low(yu),qu];class Vu{constructor(t,e,i){this.state=t,this.pos=e,this.explicit=i,this.abortListeners=[]}tokenBefore(t){let e=cl(this.state).resolveInner(this.pos,-1);for(;e&&t.indexOf(e.name)<0;)e=e.parent;return e?{from:e.from,to:this.pos,text:this.state.sliceDoc(e.from,this.pos),type:e.type}:null}matchBefore(t){let e=this.state.doc.lineAt(this.pos),i=Math.max(e.from,this.pos-250),n=e.text.slice(i-e.from,this.pos-e.from),s=n.search(Lu(t,!1));return s<0?null:{from:i+s,to:this.pos,text:n.slice(s)}}get aborted(){return null==this.abortListeners}addEventListener(t,e){"abort"==t&&this.abortListeners&&this.abortListeners.push(e)}}function Iu(t){let e=Object.keys(t).join(""),i=/\w/.test(e);return i&&(e=e.replace(/\w/g,"")),`[${i?"\\w":""}${e.replace(/[^\w\s]/g,"\\$&")}]`}function zu(t){let e=t.map((t=>"string"==typeof t?{label:t}:t)),[i,n]=e.every((t=>/^\w+$/.test(t.label)))?[/\w*$/,/\w+$/]:function(t){let e=Object.create(null),i=Object.create(null);for(let{label:n}of t){e[n[0]]=!0;for(let t=1;t<n.length;t++)i[n[t]]=!0}let n=Iu(e)+Iu(i)+"*$";return[new RegExp("^"+n),new RegExp(n)]}(e);return t=>{let s=t.matchBefore(n);return s||t.explicit?{from:s?s.from:t.pos,options:e,validFor:i}:null}}class Bu{constructor(t,e,i,n){this.completion=t,this.source=e,this.match=i,this.score=n}}function Gu(t){return t.selection.main.from}function Lu(t,e){var i;let{source:n}=t,s=e&&"^"!=n[0],r="$"!=n[n.length-1];return s||r?new RegExp(`${s?"^":""}(?:${n})${r?"$":""}`,null!==(i=t.flags)&&void 0!==i?i:t.ignoreCase?"i":""):t}const Nu=ct.define();const Uu=new WeakMap;function Hu(t){if(!Array.isArray(t))return t;let e=Uu.get(t);return e||Uu.set(t,e=zu(t)),e}const Fu=dt.define(),Ju=dt.define();class Ku{constructor(t){this.pattern=t,this.chars=[],this.folded=[],this.any=[],this.precise=[],this.byWord=[],this.score=0,this.matched=[];for(let e=0;e<t.length;){let i=b(t,e),n=S(i);this.chars.push(i);let s=t.slice(e,e+n),r=s.toUpperCase();this.folded.push(b(r==s?s.toLowerCase():r,0)),e+=n}this.astral=t.length!=this.chars.length}ret(t,e){return this.score=t,this.matched=e,!0}match(t){if(0==this.pattern.length)return this.ret(-100,[]);if(t.length<this.pattern.length)return!1;let{chars:e,folded:i,any:n,precise:s,byWord:r}=this;if(1==e.length){let n=b(t,0),s=S(n),r=s==t.length?0:-100;if(n==e[0]);else{if(n!=i[0])return!1;r+=-200}return this.ret(r,[0,s])}let o=t.indexOf(this.pattern);if(0==o)return this.ret(t.length==this.pattern.length?0:-100,[0,this.pattern.length]);let a=e.length,l=0;if(o<0){for(let s=0,r=Math.min(t.length,200);s<r&&l<a;){let r=b(t,s);r!=e[l]&&r!=i[l]||(n[l++]=s),s+=S(r)}if(l<a)return!1}let h=0,c=0,f=!1,u=0,d=-1,p=-1,O=/[a-z]/.test(t),g=!0;for(let n=0,l=Math.min(t.length,200),m=0;n<l&&c<a;){let l=b(t,n);o<0&&(h<a&&l==e[h]&&(s[h++]=n),u<a&&(l==e[u]||l==i[u]?(0==u&&(d=n),p=n+1,u++):u=0));let w,v=l<255?l>=48&&l<=57||l>=97&&l<=122?2:l>=65&&l<=90?1:0:(w=y(l))!=w.toLowerCase()?1:w!=w.toUpperCase()?2:0;(!n||1==v&&O||0==m&&0!=v)&&(e[c]==l||i[c]==l&&(f=!0)?r[c++]=n:r.length&&(g=!1)),m=v,n+=S(l)}return c==a&&0==r[0]&&g?this.result((f?-200:0)-100,r,t):u==a&&0==d?this.ret(-200-t.length+(p==t.length?0:-100),[0,p]):o>-1?this.ret(-700-t.length,[o,o+this.pattern.length]):u==a?this.ret(-900-t.length,[d,p]):c==a?this.result((f?-200:0)-100-700+(g?0:-1100),r,t):2!=e.length&&this.result((n[0]?-700:0)-200-1100,n,t)}result(t,e,i){let n=[],s=0;for(let t of e){let e=t+(this.astral?S(b(i,t)):1);s&&n[s-1]==t?n[s-1]=e:(n[s++]=t,n[s++]=e)}return this.ret(t-i.length,n)}}const td=j.define({combine:t=>$t(t,{activateOnTyping:!0,activateOnTypingDelay:100,selectOnOpen:!0,override:null,closeOnBlur:!0,maxRenderedOptions:100,defaultKeymap:!0,tooltipClass:()=>"",optionClass:()=>"",aboveCursor:!1,icons:!0,addToOptions:[],positionInfo:id,compareCompletions:(t,e)=>t.label.localeCompare(e.label),interactionDelay:75,updateSyncTime:100},{defaultKeymap:(t,e)=>t&&e,closeOnBlur:(t,e)=>t&&e,icons:(t,e)=>t&&e,tooltipClass:(t,e)=>i=>ed(t(i),e(i)),optionClass:(t,e)=>i=>ed(t(i),e(i)),addToOptions:(t,e)=>t.concat(e)})});function ed(t,e){return t?e?t+" "+e:t:e}function id(t,e,i,n,s,r){let o,a,l=t.textDirection==di.RTL,h=l,c=!1,f="top",u=e.left-s.left,d=s.right-e.right,p=n.right-n.left,O=n.bottom-n.top;if(h&&u<Math.min(p,d)?h=!1:!h&&d<Math.min(p,u)&&(h=!0),p<=(h?u:d))o=Math.max(s.top,Math.min(i.top,s.bottom-O))-e.top,a=Math.min(400,h?u:d);else{c=!0,a=Math.min(400,(l?e.right:s.right-e.left)-30);let t=s.bottom-e.bottom;t>=O||t>e.top?o=i.bottom-e.top:(f="bottom",o=e.bottom-i.top)}return{style:`${f}: ${o/((e.bottom-e.top)/r.offsetHeight)}px; max-width: ${a/((e.right-e.left)/r.offsetWidth)}px`,class:"cm-completionInfo-"+(c?l?"left-narrow":"right-narrow":h?"left":"right")}}function nd(t,e,i){if(t<=i)return{from:0,to:t};if(e<0&&(e=0),e<=t>>1){let t=Math.floor(e/i);return{from:t*i,to:(t+1)*i}}let n=Math.floor((t-e)/i);return{from:t-(n+1)*i,to:t-n*i}}class sd{constructor(t,e,i){this.view=t,this.stateField=e,this.applyCompletion=i,this.info=null,this.infoDestroy=null,this.placeInfoReq={read:()=>this.measureInfo(),write:t=>this.placeInfo(t),key:this},this.space=null,this.currentClass="";let n=t.state.field(e),{options:s,selected:r}=n.open,o=t.state.facet(td);this.optionContent=function(t){let e=t.addToOptions.slice();return t.icons&&e.push({render(t){let e=document.createElement("div");return e.classList.add("cm-completionIcon"),t.type&&e.classList.add(...t.type.split(/\s+/g).map((t=>"cm-completionIcon-"+t))),e.setAttribute("aria-hidden","true"),e},position:20}),e.push({render(t,e,i,n){let s=document.createElement("span");s.className="cm-completionLabel";let r=t.displayLabel||t.label,o=0;for(let t=0;t<n.length;){let e=n[t++],i=n[t++];e>o&&s.appendChild(document.createTextNode(r.slice(o,e)));let a=s.appendChild(document.createElement("span"));a.appendChild(document.createTextNode(r.slice(e,i))),a.className="cm-completionMatchedText",o=i}return o<r.length&&s.appendChild(document.createTextNode(r.slice(o))),s},position:50},{render(t){if(!t.detail)return null;let e=document.createElement("span");return e.className="cm-completionDetail",e.textContent=t.detail,e},position:80}),e.sort(((t,e)=>t.position-e.position)).map((t=>t.render))}(o),this.optionClass=o.optionClass,this.tooltipClass=o.tooltipClass,this.range=nd(s.length,r,o.maxRenderedOptions),this.dom=document.createElement("div"),this.dom.className="cm-tooltip-autocomplete",this.updateTooltipClass(t.state),this.dom.addEventListener("mousedown",(i=>{let{options:n}=t.state.field(e).open;for(let e,s=i.target;s&&s!=this.dom;s=s.parentNode)if("LI"==s.nodeName&&(e=/-(\d+)$/.exec(s.id))&&+e[1]<n.length)return this.applyCompletion(t,n[+e[1]]),void i.preventDefault()})),this.dom.addEventListener("focusout",(e=>{let i=t.state.field(this.stateField,!1);i&&i.tooltip&&t.state.facet(td).closeOnBlur&&e.relatedTarget!=t.contentDOM&&t.dispatch({effects:Ju.of(null)})})),this.showOptions(s,n.id)}mount(){this.updateSel()}showOptions(t,e){this.list&&this.list.remove(),this.list=this.dom.appendChild(this.createListBox(t,e,this.range)),this.list.addEventListener("scroll",(()=>{this.info&&this.view.requestMeasure(this.placeInfoReq)}))}update(t){var e;let i=t.state.field(this.stateField),n=t.startState.field(this.stateField);if(this.updateTooltipClass(t.state),i!=n){let{options:s,selected:r,disabled:o}=i.open;n.open&&n.open.options==s||(this.range=nd(s.length,r,t.state.facet(td).maxRenderedOptions),this.showOptions(s,i.id)),this.updateSel(),o!=(null===(e=n.open)||void 0===e?void 0:e.disabled)&&this.dom.classList.toggle("cm-tooltip-autocomplete-disabled",!!o)}}updateTooltipClass(t){let e=this.tooltipClass(t);if(e!=this.currentClass){for(let t of this.currentClass.split(" "))t&&this.dom.classList.remove(t);for(let t of e.split(" "))t&&this.dom.classList.add(t);this.currentClass=e}}positioned(t){this.space=t,this.info&&this.view.requestMeasure(this.placeInfoReq)}updateSel(){let t=this.view.state.field(this.stateField),e=t.open;if((e.selected>-1&&e.selected<this.range.from||e.selected>=this.range.to)&&(this.range=nd(e.options.length,e.selected,this.view.state.facet(td).maxRenderedOptions),this.showOptions(e.options,t.id)),this.updateSelectedOption(e.selected)){this.destroyInfo();let{completion:i}=e.options[e.selected],{info:n}=i;if(!n)return;let s="string"==typeof n?document.createTextNode(n):n(i);if(!s)return;"then"in s?s.then((e=>{e&&this.view.state.field(this.stateField,!1)==t&&this.addInfoPane(e,i)})).catch((t=>zi(this.view.state,t,"completion info"))):this.addInfoPane(s,i)}}addInfoPane(t,e){this.destroyInfo();let i=this.info=document.createElement("div");if(i.className="cm-tooltip cm-completionInfo",null!=t.nodeType)i.appendChild(t),this.infoDestroy=null;else{let{dom:e,destroy:n}=t;i.appendChild(e),this.infoDestroy=n||null}this.dom.appendChild(i),this.view.requestMeasure(this.placeInfoReq)}updateSelectedOption(t){let e=null;for(let i=this.list.firstChild,n=this.range.from;i;i=i.nextSibling,n++)"LI"==i.nodeName&&i.id?n==t?i.hasAttribute("aria-selected")||(i.setAttribute("aria-selected","true"),e=i):i.hasAttribute("aria-selected")&&i.removeAttribute("aria-selected"):n--;return e&&function(t,e){let i=t.getBoundingClientRect(),n=e.getBoundingClientRect(),s=i.height/t.offsetHeight;n.top<i.top?t.scrollTop-=(i.top-n.top)/s:n.bottom>i.bottom&&(t.scrollTop+=(n.bottom-i.bottom)/s)}(this.list,e),e}measureInfo(){let t=this.dom.querySelector("[aria-selected]");if(!t||!this.info)return null;let e=this.dom.getBoundingClientRect(),i=this.info.getBoundingClientRect(),n=t.getBoundingClientRect(),s=this.space;if(!s){let t=this.dom.ownerDocument.defaultView||window;s={left:0,top:0,right:t.innerWidth,bottom:t.innerHeight}}return n.top>Math.min(s.bottom,e.bottom)-10||n.bottom<Math.max(s.top,e.top)+10?null:this.view.state.facet(td).positionInfo(this.view,e,n,i,s,this.dom)}placeInfo(t){this.info&&(t?(t.style&&(this.info.style.cssText=t.style),this.info.className="cm-tooltip cm-completionInfo "+(t.class||"")):this.info.style.cssText="top: -1e6px")}createListBox(t,e,i){const n=document.createElement("ul");n.id=e,n.setAttribute("role","listbox"),n.setAttribute("aria-expanded","true"),n.setAttribute("aria-label",this.view.state.phrase("Completions"));let s=null;for(let r=i.from;r<i.to;r++){let{completion:o,match:a}=t[r],{section:l}=o;if(l){let t="string"==typeof l?l:l.name;if(t!=s&&(r>i.from||0==i.from))if(s=t,"string"!=typeof l&&l.header)n.appendChild(l.header(l));else{n.appendChild(document.createElement("completion-section")).textContent=t}}const h=n.appendChild(document.createElement("li"));h.id=e+"-"+r,h.setAttribute("role","option");let c=this.optionClass(o);c&&(h.className=c);for(let t of this.optionContent){let e=t(o,this.view.state,this.view,a);e&&h.appendChild(e)}}return i.from&&n.classList.add("cm-completionListIncompleteTop"),i.to<t.length&&n.classList.add("cm-completionListIncompleteBottom"),n}destroyInfo(){this.info&&(this.infoDestroy&&this.infoDestroy(),this.info.remove(),this.info=null)}destroy(){this.destroyInfo()}}function rd(t,e){return i=>new sd(i,t,e)}function od(t){return 100*(t.boost||0)+(t.apply?10:0)+(t.info?5:0)+(t.type?1:0)}class ad{constructor(t,e,i,n,s,r){this.options=t,this.attrs=e,this.tooltip=i,this.timestamp=n,this.selected=s,this.disabled=r}setSelected(t,e){return t==this.selected||t>=this.options.length?this:new ad(this.options,cd(e,t),this.tooltip,this.timestamp,t,this.disabled)}static build(t,e,i,n,s){let r=function(t,e){let i=[],n=null,s=t=>{i.push(t);let{section:e}=t.completion;if(e){n||(n=[]);let t="string"==typeof e?e:e.name;n.some((e=>e.name==t))||n.push("string"==typeof e?{name:t}:e)}};for(let n of t)if(n.hasResult()){let t=n.result.getMatch;if(!1===n.result.filter)for(let e of n.result.options)s(new Bu(e,n.source,t?t(e):[],1e9-i.length));else{let i=new Ku(e.sliceDoc(n.from,n.to));for(let e of n.result.options)if(i.match(e.label)){let r=e.displayLabel?t?t(e,i.matched):[]:i.matched;s(new Bu(e,n.source,r,i.score+(e.boost||0)))}}}if(n){let t=Object.create(null),e=0,s=(t,e)=>{var i,n;return(null!==(i=t.rank)&&void 0!==i?i:1e9)-(null!==(n=e.rank)&&void 0!==n?n:1e9)||(t.name<e.name?-1:1)};for(let i of n.sort(s))e-=1e5,t[i.name]=e;for(let e of i){let{section:i}=e.completion;i&&(e.score+=t["string"==typeof i?i:i.name])}}let r=[],o=null,a=e.facet(td).compareCompletions;for(let t of i.sort(((t,e)=>e.score-t.score||a(t.completion,e.completion)))){let e=t.completion;!o||o.label!=e.label||o.detail!=e.detail||null!=o.type&&null!=e.type&&o.type!=e.type||o.apply!=e.apply||o.boost!=e.boost?r.push(t):od(t.completion)>od(o)&&(r[r.length-1]=t),o=t.completion}return r}(t,e);if(!r.length)return n&&t.some((t=>1==t.state))?new ad(n.options,n.attrs,n.tooltip,n.timestamp,n.selected,!0):null;let o=e.facet(td).selectOnOpen?0:-1;if(n&&n.selected!=o&&-1!=n.selected){let t=n.options[n.selected].completion;for(let e=0;e<r.length;e++)if(r[e].completion==t){o=e;break}}return new ad(r,cd(i,o),{pos:t.reduce(((t,e)=>e.hasResult()?Math.min(t,e.from):t),1e8),create:vd,above:s.aboveCursor},n?n.timestamp:Date.now(),o,!1)}map(t){return new ad(this.options,this.attrs,Object.assign(Object.assign({},this.tooltip),{pos:t.mapPos(this.tooltip.pos)}),this.timestamp,this.selected,this.disabled)}}class ld{constructor(t,e,i){this.active=t,this.id=e,this.open=i}static start(){return new ld(fd,"cm-ac-"+Math.floor(2e6*Math.random()).toString(36),null)}update(t){let{state:e}=t,i=e.facet(td),n=(i.override||e.languageDataAt("autocomplete",Gu(e)).map(Hu)).map((e=>(this.active.find((t=>t.source==e))||new dd(e,this.active.some((t=>0!=t.state))?1:0)).update(t,i)));n.length==this.active.length&&n.every(((t,e)=>t==this.active[e]))&&(n=this.active);let s=this.open;s&&t.docChanged&&(s=s.map(t.changes)),t.selection||n.some((e=>e.hasResult()&&t.changes.touchesRange(e.from,e.to)))||!function(t,e){if(t==e)return!0;for(let i=0,n=0;;){for(;i<t.length&&!t[i].hasResult;)i++;for(;n<e.length&&!e[n].hasResult;)n++;let s=i==t.length,r=n==e.length;if(s||r)return s==r;if(t[i++].result!=e[n++].result)return!1}}(n,this.active)?s=ad.build(n,e,this.id,s,i):s&&s.disabled&&!n.some((t=>1==t.state))&&(s=null),!s&&n.every((t=>1!=t.state))&&n.some((t=>t.hasResult()))&&(n=n.map((t=>t.hasResult()?new dd(t.source,0):t)));for(let e of t.effects)e.is(gd)&&(s=s&&s.setSelected(e.value,this.id));return n==this.active&&s==this.open?this:new ld(n,this.id,s)}get tooltip(){return this.open?this.open.tooltip:null}get attrs(){return this.open?this.open.attrs:hd}}const hd={"aria-autocomplete":"list"};function cd(t,e){let i={"aria-autocomplete":"list","aria-haspopup":"listbox","aria-controls":t};return e>-1&&(i["aria-activedescendant"]=t+"-"+e),i}const fd=[];function ud(t){return t.isUserEvent("input.type")?"input":t.isUserEvent("delete.backward")?"delete":null}class dd{constructor(t,e,i=-1){this.source=t,this.state=e,this.explicitPos=i}hasResult(){return!1}update(t,e){let i=ud(t),n=this;i?n=n.handleUserEvent(t,i,e):t.docChanged?n=n.handleChange(t):t.selection&&0!=n.state&&(n=new dd(n.source,0));for(let e of t.effects)if(e.is(Fu))n=new dd(n.source,1,e.value?Gu(t.state):-1);else if(e.is(Ju))n=new dd(n.source,0);else if(e.is(Od))for(let t of e.value)t.source==n.source&&(n=t);return n}handleUserEvent(t,e,i){return"delete"!=e&&i.activateOnTyping?new dd(this.source,1):this.map(t.changes)}handleChange(t){return t.changes.touchesRange(Gu(t.startState))?new dd(this.source,0):this.map(t.changes)}map(t){return t.empty||this.explicitPos<0?this:new dd(this.source,this.state,t.mapPos(this.explicitPos))}}class pd extends dd{constructor(t,e,i,n,s){super(t,2,e),this.result=i,this.from=n,this.to=s}hasResult(){return!0}handleUserEvent(t,e,i){var n;let s=t.changes.mapPos(this.from),r=t.changes.mapPos(this.to,1),o=Gu(t.state);if((this.explicitPos<0?o<=s:o<this.from)||o>r||"delete"==e&&Gu(t.startState)==this.from)return new dd(this.source,"input"==e&&i.activateOnTyping?1:0);let a,l=this.explicitPos<0?-1:t.changes.mapPos(this.explicitPos);return function(t,e,i,n){if(!t)return!1;let s=e.sliceDoc(i,n);return"function"==typeof t?t(s,i,n,e):Lu(t,!0).test(s)}(this.result.validFor,t.state,s,r)?new pd(this.source,l,this.result,s,r):this.result.update&&(a=this.result.update(this.result,s,r,new Vu(t.state,o,l>=0)))?new pd(this.source,l,a,a.from,null!==(n=a.to)&&void 0!==n?n:Gu(t.state)):new dd(this.source,1,l)}handleChange(t){return t.changes.touchesRange(this.from,this.to)?new dd(this.source,0):this.map(t.changes)}map(t){return t.empty?this:new pd(this.source,this.explicitPos<0?-1:t.mapPos(this.explicitPos),this.result,t.mapPos(this.from),t.mapPos(this.to,1))}}const Od=dt.define({map:(t,e)=>t.map((t=>t.map(e)))}),gd=dt.define(),md=z.define({create:()=>ld.start(),update:(t,e)=>t.update(e),provide:t=>[no.from(t,(t=>t.tooltip)),Gs.contentAttributes.from(t,(t=>t.attrs))]});function wd(t,e){const i=e.completion.apply||e.completion.label;let n=t.state.field(md).active.find((t=>t.source==e.source));return n instanceof pd&&("string"==typeof i?t.dispatch(Object.assign(Object.assign({},function(t,e,i,n){let{main:s}=t.selection,r=i-s.from,o=n-s.from;return Object.assign(Object.assign({},t.changeByRange((a=>a!=s&&i!=n&&t.sliceDoc(a.from+r,a.from+o)!=t.sliceDoc(i,n)?{range:a}:{changes:{from:a.from+r,to:n==s.from?a.to:a.from+o,insert:e},range:Y.cursor(a.from+r+e.length)}))),{scrollIntoView:!0,userEvent:"input.complete"})}(t.state,i,n.from,n.to)),{annotations:Nu.of(e.completion)})):i(t,e.completion,n.from,n.to),!0)}const vd=rd(md,wd);function bd(t,e="option"){return i=>{let n=i.state.field(md,!1);if(!n||!n.open||n.open.disabled||Date.now()-n.open.timestamp<i.state.facet(td).interactionDelay)return!1;let s,r=1;"page"==e&&(s=co(i,n.open.tooltip))&&(r=Math.max(2,Math.floor(s.dom.offsetHeight/s.dom.querySelector("li").offsetHeight)-1));let{length:o}=n.open.options,a=n.open.selected>-1?n.open.selected+r*(t?1:-1):t?0:o-1;return a<0?a="page"==e?0:o-1:a>=o&&(a="page"==e?o-1:0),i.dispatch({effects:gd.of(a)}),!0}}class yd{constructor(t,e){this.active=t,this.context=e,this.time=Date.now(),this.updates=[],this.done=void 0}}const Sd=Ni.fromClass(class{constructor(t){this.view=t,this.debounceUpdate=-1,this.running=[],this.debounceAccept=-1,this.pendingStart=!1,this.composing=0;for(let e of t.state.field(md).active)1==e.state&&this.startQuery(e)}update(t){let e=t.state.field(md);if(!t.selectionSet&&!t.docChanged&&t.startState.field(md)==e)return;let i=t.transactions.some((t=>(t.selection||t.docChanged)&&!ud(t)));for(let e=0;e<this.running.length;e++){let n=this.running[e];if(i||n.updates.length+t.transactions.length>50&&Date.now()-n.time>1e3){for(let t of n.context.abortListeners)try{t()}catch(t){zi(this.view.state,t)}n.context.abortListeners=null,this.running.splice(e--,1)}else n.updates.push(...t.transactions)}this.debounceUpdate>-1&&clearTimeout(this.debounceUpdate),t.transactions.some((t=>t.effects.some((t=>t.is(Fu)))))&&(this.pendingStart=!0);let n=this.pendingStart?50:t.state.facet(td).activateOnTypingDelay;if(this.debounceUpdate=e.active.some((t=>1==t.state&&!this.running.some((e=>e.active.source==t.source))))?setTimeout((()=>this.startUpdate()),n):-1,0!=this.composing)for(let e of t.transactions)"input"==ud(e)?this.composing=2:2==this.composing&&e.selection&&(this.composing=3)}startUpdate(){this.debounceUpdate=-1,this.pendingStart=!1;let{state:t}=this.view,e=t.field(md);for(let t of e.active)1!=t.state||this.running.some((e=>e.active.source==t.source))||this.startQuery(t)}startQuery(t){let{state:e}=this.view,i=Gu(e),n=new Vu(e,i,t.explicitPos==i),s=new yd(t,n);this.running.push(s),Promise.resolve(t.source(n)).then((t=>{s.context.aborted||(s.done=t||null,this.scheduleAccept())}),(t=>{this.view.dispatch({effects:Ju.of(null)}),zi(this.view.state,t)}))}scheduleAccept(){this.running.every((t=>void 0!==t.done))?this.accept():this.debounceAccept<0&&(this.debounceAccept=setTimeout((()=>this.accept()),this.view.state.facet(td).updateSyncTime))}accept(){var t;this.debounceAccept>-1&&clearTimeout(this.debounceAccept),this.debounceAccept=-1;let e=[],i=this.view.state.facet(td);for(let n=0;n<this.running.length;n++){let s=this.running[n];if(void 0===s.done)continue;if(this.running.splice(n--,1),s.done){let n=new pd(s.active.source,s.active.explicitPos,s.done,s.done.from,null!==(t=s.done.to)&&void 0!==t?t:Gu(s.updates.length?s.updates[0].startState:this.view.state));for(let t of s.updates)n=n.update(t,i);if(n.hasResult()){e.push(n);continue}}let r=this.view.state.field(md).active.find((t=>t.source==s.active.source));if(r&&1==r.state)if(null==s.done){let t=new dd(s.active.source,0);for(let e of s.updates)t=t.update(e,i);1!=t.state&&e.push(t)}else this.startQuery(r)}e.length&&this.view.dispatch({effects:Od.of(e)})}},{eventHandlers:{blur(t){let e=this.view.state.field(md,!1);if(e&&e.tooltip&&this.view.state.facet(td).closeOnBlur){let i=e.open&&co(this.view,e.open.tooltip);i&&i.dom.contains(t.relatedTarget)||setTimeout((()=>this.view.dispatch({effects:Ju.of(null)})),10)}},compositionstart(){this.composing=1},compositionend(){3==this.composing&&setTimeout((()=>this.view.dispatch({effects:Fu.of(!1)})),20),this.composing=0}}}),xd=Gs.baseTheme({".cm-tooltip.cm-tooltip-autocomplete":{"& > ul":{fontFamily:"monospace",whiteSpace:"nowrap",overflow:"hidden auto",maxWidth_fallback:"700px",maxWidth:"min(700px, 95vw)",minWidth:"250px",maxHeight:"10em",height:"100%",listStyle:"none",margin:0,padding:0,"& > li, & > completion-section":{padding:"1px 3px",lineHeight:1.2},"& > li":{overflowX:"hidden",textOverflow:"ellipsis",cursor:"pointer"},"& > completion-section":{display:"list-item",borderBottom:"1px solid silver",paddingLeft:"0.5em",opacity:.7}}},"&light .cm-tooltip-autocomplete ul li[aria-selected]":{background:"#17c",color:"white"},"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]":{background:"#777"},"&dark .cm-tooltip-autocomplete ul li[aria-selected]":{background:"#347",color:"white"},"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]":{background:"#444"},".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after":{content:'"···"',opacity:.5,display:"block",textAlign:"center"},".cm-tooltip.cm-completionInfo":{position:"absolute",padding:"3px 9px",width:"max-content",maxWidth:"400px",boxSizing:"border-box"},".cm-completionInfo.cm-completionInfo-left":{right:"100%"},".cm-completionInfo.cm-completionInfo-right":{left:"100%"},".cm-completionInfo.cm-completionInfo-left-narrow":{right:"30px"},".cm-completionInfo.cm-completionInfo-right-narrow":{left:"30px"},"&light .cm-snippetField":{backgroundColor:"#00000022"},"&dark .cm-snippetField":{backgroundColor:"#ffffff22"},".cm-snippetFieldPosition":{verticalAlign:"text-top",width:0,height:"1.15em",display:"inline-block",margin:"0 -0.7px -.7em",borderLeft:"1.4px dotted #888"},".cm-completionMatchedText":{textDecoration:"underline"},".cm-completionDetail":{marginLeft:"0.5em",fontStyle:"italic"},".cm-completionIcon":{fontSize:"90%",width:".8em",display:"inline-block",textAlign:"center",paddingRight:".6em",opacity:"0.6",boxSizing:"content-box"},".cm-completionIcon-function, .cm-completionIcon-method":{"&:after":{content:"'ƒ'"}},".cm-completionIcon-class":{"&:after":{content:"'○'"}},".cm-completionIcon-interface":{"&:after":{content:"'◌'"}},".cm-completionIcon-variable":{"&:after":{content:"'𝑥'"}},".cm-completionIcon-constant":{"&:after":{content:"'𝐶'"}},".cm-completionIcon-type":{"&:after":{content:"'𝑡'"}},".cm-completionIcon-enum":{"&:after":{content:"'∪'"}},".cm-completionIcon-property":{"&:after":{content:"'□'"}},".cm-completionIcon-keyword":{"&:after":{content:"'🔑︎'"}},".cm-completionIcon-namespace":{"&:after":{content:"'▢'"}},".cm-completionIcon-text":{"&:after":{content:"'abc'",fontSize:"50%",verticalAlign:"middle"}}});class kd{constructor(t,e,i,n){this.field=t,this.line=e,this.from=i,this.to=n}}class Qd{constructor(t,e,i){this.field=t,this.from=e,this.to=i}map(t){let e=t.mapPos(this.from,-1,k.TrackDel),i=t.mapPos(this.to,1,k.TrackDel);return null==e||null==i?null:new Qd(this.field,e,i)}}class $d{constructor(t,e){this.lines=t,this.fieldPositions=e}instantiate(t,e){let i=[],n=[e],s=t.doc.lineAt(e),r=/^\s*/.exec(s.text)[0];for(let s of this.lines){if(i.length){let i=r,o=/^\t*/.exec(s)[0].length;for(let e=0;e<o;e++)i+=t.facet(kl);n.push(e+i.length-o),s=i+s.slice(o)}i.push(s),e+=s.length+1}let o=this.fieldPositions.map((t=>new Qd(t.field,n[t.line]+t.from,n[t.line]+t.to)));return{text:i,ranges:o}}static parse(t){let e,i=[],n=[],s=[];for(let r of t.split(/\r\n?|\n/)){for(;e=/[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(r);){let t=e[1]?+e[1]:null,o=e[2]||e[3]||"",a=-1;for(let e=0;e<i.length;e++)(null!=t?i[e].seq==t:o&&i[e].name==o)&&(a=e);if(a<0){let e=0;for(;e<i.length&&(null==t||null!=i[e].seq&&i[e].seq<t);)e++;i.splice(e,0,{seq:t,name:o}),a=e;for(let t of s)t.field>=a&&t.field++}s.push(new kd(a,n.length,e.index,e.index+o.length)),r=r.slice(0,e.index)+o+r.slice(e.index+e[0].length)}for(let t;t=/\\([{}])/.exec(r);){r=r.slice(0,t.index)+t[1]+r.slice(t.index+t[0].length);for(let e of s)e.line==n.length&&e.from>t.index&&(e.from--,e.to--)}n.push(r)}return new $d(n,s)}}let Pd=si.widget({widget:new class extends ii{toDOM(){let t=document.createElement("span");return t.className="cm-snippetFieldPosition",t}ignoreEvent(){return!1}}}),Zd=si.mark({class:"cm-snippetField"});class Cd{constructor(t,e){this.ranges=t,this.active=e,this.deco=si.set(t.map((t=>(t.from==t.to?Pd:Zd).range(t.from,t.to))))}map(t){let e=[];for(let i of this.ranges){let n=i.map(t);if(!n)return null;e.push(n)}return new Cd(e,this.active)}selectionInsideField(t){return t.ranges.every((t=>this.ranges.some((e=>e.field==this.active&&e.from<=t.from&&e.to>=t.to))))}}const Td=dt.define({map:(t,e)=>t&&t.map(e)}),Ad=dt.define(),Rd=z.define({create:()=>null,update(t,e){for(let i of e.effects){if(i.is(Td))return i.value;if(i.is(Ad)&&t)return new Cd(t.ranges,i.value)}return t&&e.docChanged&&(t=t.map(e.changes)),t&&e.selection&&!t.selectionInsideField(e.selection)&&(t=null),t},provide:t=>Gs.decorations.from(t,(t=>t?t.deco:si.none))});function Xd(t,e){return Y.create(t.filter((t=>t.field==e)).map((t=>Y.range(t.from,t.to))))}function Yd(t){let i=$d.parse(t);return(t,n,s,r)=>{let{text:o,ranges:a}=i.instantiate(t.state,s),l={changes:{from:s,to:r,insert:e.of(o)},scrollIntoView:!0,annotations:n?[Nu.of(n),pt.userEvent.of("input.complete")]:void 0};if(a.length&&(l.selection=Xd(a,0)),a.some((t=>t.field>0))){let e=new Cd(a,0),i=l.effects=[Td.of(e)];void 0===t.state.field(Rd,!1)&&i.push(dt.appendConfig.of([Rd,Dd,qd,xd]))}t.dispatch(t.state.update(l))}}function Wd(t){return({state:e,dispatch:i})=>{let n=e.field(Rd,!1);if(!n||t<0&&0==n.active)return!1;let s=n.active+t,r=t>0&&!n.ranges.some((e=>e.field==s+t));return i(e.update({selection:Xd(n.ranges,s),effects:Td.of(r?null:new Cd(n.ranges,s)),scrollIntoView:!0})),!0}}const Md=[{key:"Tab",run:Wd(1),shift:Wd(-1)},{key:"Escape",run:({state:t,dispatch:e})=>!!t.field(Rd,!1)&&(e(t.update({effects:Td.of(null)})),!0)}],jd=j.define({combine:t=>t.length?t[0]:Md}),Dd=H.highest(tr.compute([jd],(t=>t.facet(jd))));function Ed(t,e){return Object.assign(Object.assign({},e),{apply:Yd(t)})}const qd=Gs.domEventHandlers({mousedown(t,e){let i,n=e.state.field(Rd,!1);if(!n||null==(i=e.posAtCoords({x:t.clientX,y:t.clientY})))return!1;let s=n.ranges.find((t=>t.from<=i&&t.to>=i));return!(!s||s.field==n.active)&&(e.dispatch({selection:Xd(n.ranges,s.field),effects:Td.of(n.ranges.some((t=>t.field>s.field))?new Cd(n.ranges,s.field):null),scrollIntoView:!0}),!0)}}),_d={brackets:["(","[","{","'",'"'],before:")]}:;>",stringPrefixes:[]},Vd=dt.define({map(t,e){let i=e.mapPos(t,-1,k.TrackAfter);return null==i?void 0:i}}),Id=new class extends Pt{};Id.startSide=1,Id.endSide=-1;const zd=z.define({create:()=>At.empty,update(t,e){if(t=t.map(e.changes),e.selection){let i=e.state.doc.lineAt(e.selection.main.head);t=t.update({filter:t=>t>=i.from&&t<=i.to})}for(let i of e.effects)i.is(Vd)&&(t=t.update({add:[Id.range(i.value,i.value+1)]}));return t}});const Bd="()[]{}<>";function Gd(t){for(let e=0;e<Bd.length;e+=2)if(Bd.charCodeAt(e)==t)return Bd.charAt(e+1);return y(t<128?t:t+1)}function Ld(t,e){return t.languageDataAt("closeBrackets",e)[0]||_d}const Nd="object"==typeof navigator&&/Android\b/.test(navigator.userAgent),Ud=Gs.inputHandler.of(((t,e,i,n)=>{if((Nd?t.composing:t.compositionStarted)||t.state.readOnly)return!1;let s=t.state.selection.main;if(n.length>2||2==n.length&&1==S(b(n,0))||e!=s.from||i!=s.to)return!1;let r=function(t,e){let i=Ld(t,t.selection.main.head),n=i.brackets||_d.brackets;for(let s of n){let r=Gd(b(s,0));if(e==s)return r==s?ep(t,s,n.indexOf(s+s+s)>-1,i):Kd(t,s,r,i.before||_d.before);if(e==r&&Fd(t,t.selection.main.from))return tp(t,s,r)}return null}(t.state,n);return!!r&&(t.dispatch(r),!0)})),Hd=[{key:"Backspace",run:({state:t,dispatch:e})=>{if(t.readOnly)return!1;let i=Ld(t,t.selection.main.head).brackets||_d.brackets,n=null,s=t.changeByRange((e=>{if(e.empty){let n=function(t,e){let i=t.sliceString(e-2,e);return S(b(i,0))==i.length?i:i.slice(1)}(t.doc,e.head);for(let s of i)if(s==n&&Jd(t.doc,e.head)==Gd(b(s,0)))return{changes:{from:e.head-s.length,to:e.head+s.length},range:Y.cursor(e.head-s.length)}}return{range:n=e}}));return n||e(t.update(s,{scrollIntoView:!0,userEvent:"delete.backward"})),!n}}];function Fd(t,e){let i=!1;return t.field(zd).between(0,t.doc.length,(t=>{t==e&&(i=!0)})),i}function Jd(t,e){let i=t.sliceString(e,e+2);return i.slice(0,S(b(i,0)))}function Kd(t,e,i,n){let s=null,r=t.changeByRange((r=>{if(!r.empty)return{changes:[{insert:e,from:r.from},{insert:i,from:r.to}],effects:Vd.of(r.to+e.length),range:Y.range(r.anchor+e.length,r.head+e.length)};let o=Jd(t.doc,r.head);return!o||/\s/.test(o)||n.indexOf(o)>-1?{changes:{insert:e+i,from:r.head},effects:Vd.of(r.head+e.length),range:Y.cursor(r.head+e.length)}:{range:s=r}}));return s?null:t.update(r,{scrollIntoView:!0,userEvent:"input.type"})}function tp(t,e,i){let n=null,s=t.changeByRange((e=>e.empty&&Jd(t.doc,e.head)==i?{changes:{from:e.head,to:e.head+i.length,insert:i},range:Y.cursor(e.head+i.length)}:n={range:e}));return n?null:t.update(s,{scrollIntoView:!0,userEvent:"input.type"})}function ep(t,e,i,n){let s=n.stringPrefixes||_d.stringPrefixes,r=null,o=t.changeByRange((n=>{if(!n.empty)return{changes:[{insert:e,from:n.from},{insert:e,from:n.to}],effects:Vd.of(n.to+e.length),range:Y.range(n.anchor+e.length,n.head+e.length)};let o,a=n.head,l=Jd(t.doc,a);if(l==e){if(ip(t,a))return{changes:{insert:e+e,from:a},effects:Vd.of(a+e.length),range:Y.cursor(a+e.length)};if(Fd(t,a)){let n=i&&t.sliceDoc(a,a+3*e.length)==e+e+e?e+e+e:e;return{changes:{from:a,to:a+n.length,insert:n},range:Y.cursor(a+n.length)}}}else{if(i&&t.sliceDoc(a-2*e.length,a)==e+e&&(o=np(t,a-2*e.length,s))>-1&&ip(t,o))return{changes:{insert:e+e+e+e,from:a},effects:Vd.of(a+e.length),range:Y.cursor(a+e.length)};if(t.charCategorizer(a)(l)!=yt.Word&&np(t,a,s)>-1&&!function(t,e,i,n){let s=cl(t).resolveInner(e,-1),r=n.reduce(((t,e)=>Math.max(t,e.length)),0);for(let o=0;o<5;o++){let o=t.sliceDoc(s.from,Math.min(s.to,s.from+i.length+r)),a=o.indexOf(i);if(!a||a>-1&&n.indexOf(o.slice(0,a))>-1){let e=s.firstChild;for(;e&&e.from==s.from&&e.to-e.from>i.length+a;){if(t.sliceDoc(e.to-i.length,e.to)==i)return!1;e=e.firstChild}return!0}let l=s.to==e&&s.parent;if(!l)break;s=l}return!1}(t,a,e,s))return{changes:{insert:e+e,from:a},effects:Vd.of(a+e.length),range:Y.cursor(a+e.length)}}return{range:r=n}}));return r?null:t.update(o,{scrollIntoView:!0,userEvent:"input.type"})}function ip(t,e){let i=cl(t).resolveInner(e+1);return i.parent&&i.from==e}function np(t,e,i){let n=t.charCategorizer(e);if(n(t.sliceDoc(e-1,e))!=yt.Word)return e;for(let s of i){let i=e-s.length;if(t.sliceDoc(i,e)==s&&n(t.sliceDoc(i-1,i))!=yt.Word)return i}return-1}function sp(t={}){return[md,td.of(t),Sd,op,xd]}const rp=[{key:"Ctrl-Space",run:t=>!!t.state.field(md,!1)&&(t.dispatch({effects:Fu.of(!0)}),!0)},{key:"Escape",run:t=>{let e=t.state.field(md,!1);return!(!e||!e.active.some((t=>0!=t.state)))&&(t.dispatch({effects:Ju.of(null)}),!0)}},{key:"ArrowDown",run:bd(!0)},{key:"ArrowUp",run:bd(!1)},{key:"PageDown",run:bd(!0,"page")},{key:"PageUp",run:bd(!1,"page")},{key:"Enter",run:t=>{let e=t.state.field(md,!1);return!(t.state.readOnly||!e||!e.open||e.open.selected<0||e.open.disabled||Date.now()-e.open.timestamp<t.state.facet(td).interactionDelay)&&wd(t,e.open.options[e.open.selected])}}],op=H.highest(tr.computeN([td],(t=>t.facet(td).defaultKeymap?[rp]:[])));class ap{constructor(t,e,i){this.from=t,this.to=e,this.diagnostic=i}}class lp{constructor(t,e,i){this.diagnostics=t,this.panel=e,this.selected=i}static init(t,e,i){let n=t,s=i.facet(vp).markerFilter;s&&(n=s(n,i));let r=si.set(n.map((t=>t.from==t.to||t.from==t.to-1&&i.doc.lineAt(t.from).to==t.from?si.widget({widget:new Sp(t),diagnostic:t}).range(t.from):si.mark({attributes:{class:"cm-lintRange cm-lintRange-"+t.severity+(t.markClass?" "+t.markClass:"")},diagnostic:t,inclusive:!0}).range(t.from,t.to))),!0);return new lp(r,e,hp(r))}}function hp(t,e=null,i=0){let n=null;return t.between(i,1e9,((t,i,{spec:s})=>{if(!e||s.diagnostic==e)return n=new ap(t,i,s.diagnostic),!1})),n}const cp=dt.define(),fp=dt.define(),up=dt.define(),dp=z.define({create:()=>new lp(si.none,null,null),update(t,e){if(e.docChanged){let i=t.diagnostics.map(e.changes),n=null;if(t.selected){let s=e.changes.mapPos(t.selected.from,1);n=hp(i,t.selected.diagnostic,s)||hp(i,null,s)}t=new lp(i,t.panel,n)}for(let i of e.effects)i.is(cp)?t=lp.init(i.value,t.panel,e.state):i.is(fp)?t=new lp(t.diagnostics,i.value?kp.open:null,t.selected):i.is(up)&&(t=new lp(t.diagnostics,t.panel,i.value));return t},provide:t=>[vo.from(t,(t=>t.panel)),Gs.decorations.from(t,(t=>t.diagnostics))]}),pp=si.mark({class:"cm-lintRange cm-lintRange-active",inclusive:!0});function Op(t,e,i){let{diagnostics:n}=t.state.field(dp),s=[],r=2e8,o=0;n.between(e-(i<0?1:0),e+(i>0?1:0),((t,n,{spec:a})=>{e>=t&&e<=n&&(t==n||(e>t||i>0)&&(e<n||i<0))&&(s.push(a.diagnostic),r=Math.min(t,r),o=Math.max(n,o))}));let a=t.state.facet(vp).tooltipFilter;return a&&(s=a(s,t.state)),s.length?{pos:r,end:o,above:t.state.doc.lineAt(r).to<o,create:()=>({dom:gp(t,s)})}:null}function gp(t,e){return jf("ul",{class:"cm-tooltip-lint"},e.map((e=>yp(t,e,!1))))}const mp=t=>{let e=t.state.field(dp,!1);return!(!e||!e.panel)&&(t.dispatch({effects:fp.of(!1)}),!0)},wp=[{key:"Mod-Shift-m",run:t=>{let e=t.state.field(dp,!1);var i,n;e&&e.panel||t.dispatch({effects:(i=t.state,n=[fp.of(!0)],i.field(dp,!1)?n:n.concat(dt.appendConfig.of(Pp)))});let s=Oo(t,kp.open);return s&&s.dom.querySelector(".cm-panel-lint ul").focus(),!0},preventDefault:!0},{key:"F8",run:t=>{let e=t.state.field(dp,!1);if(!e)return!1;let i=t.state.selection.main,n=e.diagnostics.iter(i.to+1);return!(!n.value&&(n=e.diagnostics.iter(0),!n.value||n.from==i.from&&n.to==i.to))&&(t.dispatch({selection:{anchor:n.from,head:n.to},scrollIntoView:!0}),!0)}}],vp=j.define({combine:t=>Object.assign({sources:t.map((t=>t.source)).filter((t=>null!=t))},$t(t.map((t=>t.config)),{delay:750,markerFilter:null,tooltipFilter:null,needsRefresh:null},{needsRefresh:(t,e)=>t?e?i=>t(i)||e(i):t:e}))});function bp(t){let e=[];if(t)t:for(let{name:i}of t){for(let t=0;t<i.length;t++){let n=i[t];if(/[a-zA-Z]/.test(n)&&!e.some((t=>t.toLowerCase()==n.toLowerCase()))){e.push(n);continue t}}e.push("")}return e}function yp(t,e,i){var n;let s=i?bp(e.actions):[];return jf("li",{class:"cm-diagnostic cm-diagnostic-"+e.severity},jf("span",{class:"cm-diagnosticText"},e.renderMessage?e.renderMessage():e.message),null===(n=e.actions)||void 0===n?void 0:n.map(((i,n)=>{let r=!1,o=n=>{if(n.preventDefault(),r)return;r=!0;let s=hp(t.state.field(dp).diagnostics,e);s&&i.apply(t,s.from,s.to)},{name:a}=i,l=s[n]?a.indexOf(s[n]):-1,h=l<0?a:[a.slice(0,l),jf("u",a.slice(l,l+1)),a.slice(l+1)];return jf("button",{type:"button",class:"cm-diagnosticAction",onclick:o,onmousedown:o,"aria-label":` Action: ${a}${l<0?"":` (access key "${s[n]})"`}.`},h)})),e.source&&jf("div",{class:"cm-diagnosticSource"},e.source))}class Sp extends ii{constructor(t){super(),this.diagnostic=t}eq(t){return t.diagnostic==this.diagnostic}toDOM(){return jf("span",{class:"cm-lintPoint cm-lintPoint-"+this.diagnostic.severity})}}class xp{constructor(t,e){this.diagnostic=e,this.id="item_"+Math.floor(4294967295*Math.random()).toString(16),this.dom=yp(t,e,!0),this.dom.id=this.id,this.dom.setAttribute("role","option")}}class kp{constructor(t){this.view=t,this.items=[];this.list=jf("ul",{tabIndex:0,role:"listbox","aria-label":this.view.state.phrase("Diagnostics"),onkeydown:e=>{if(27==e.keyCode)mp(this.view),this.view.focus();else if(38==e.keyCode||33==e.keyCode)this.moveSelection((this.selectedIndex-1+this.items.length)%this.items.length);else if(40==e.keyCode||34==e.keyCode)this.moveSelection((this.selectedIndex+1)%this.items.length);else if(36==e.keyCode)this.moveSelection(0);else if(35==e.keyCode)this.moveSelection(this.items.length-1);else if(13==e.keyCode)this.view.focus();else{if(!(e.keyCode>=65&&e.keyCode<=90&&this.selectedIndex>=0))return;{let{diagnostic:i}=this.items[this.selectedIndex],n=bp(i.actions);for(let s=0;s<n.length;s++)if(n[s].toUpperCase().charCodeAt(0)==e.keyCode){let e=hp(this.view.state.field(dp).diagnostics,i);e&&i.actions[s].apply(t,e.from,e.to)}}}e.preventDefault()},onclick:t=>{for(let e=0;e<this.items.length;e++)this.items[e].dom.contains(t.target)&&this.moveSelection(e)}}),this.dom=jf("div",{class:"cm-panel-lint"},this.list,jf("button",{type:"button",name:"close","aria-label":this.view.state.phrase("close"),onclick:()=>mp(this.view)},"×")),this.update()}get selectedIndex(){let t=this.view.state.field(dp).selected;if(!t)return-1;for(let e=0;e<this.items.length;e++)if(this.items[e].diagnostic==t.diagnostic)return e;return-1}update(){let{diagnostics:t,selected:e}=this.view.state.field(dp),i=0,n=!1,s=null;for(t.between(0,this.view.state.doc.length,((t,r,{spec:o})=>{let a,l=-1;for(let t=i;t<this.items.length;t++)if(this.items[t].diagnostic==o.diagnostic){l=t;break}l<0?(a=new xp(this.view,o.diagnostic),this.items.splice(i,0,a),n=!0):(a=this.items[l],l>i&&(this.items.splice(i,l-i),n=!0)),e&&a.diagnostic==e.diagnostic?a.dom.hasAttribute("aria-selected")||(a.dom.setAttribute("aria-selected","true"),s=a):a.dom.hasAttribute("aria-selected")&&a.dom.removeAttribute("aria-selected"),i++}));i<this.items.length&&!(1==this.items.length&&this.items[0].diagnostic.from<0);)n=!0,this.items.pop();0==this.items.length&&(this.items.push(new xp(this.view,{from:-1,to:-1,severity:"info",message:this.view.state.phrase("No diagnostics")})),n=!0),s?(this.list.setAttribute("aria-activedescendant",s.id),this.view.requestMeasure({key:this,read:()=>({sel:s.dom.getBoundingClientRect(),panel:this.list.getBoundingClientRect()}),write:({sel:t,panel:e})=>{let i=e.height/this.list.offsetHeight;t.top<e.top?this.list.scrollTop-=(e.top-t.top)/i:t.bottom>e.bottom&&(this.list.scrollTop+=(t.bottom-e.bottom)/i)}})):this.selectedIndex<0&&this.list.removeAttribute("aria-activedescendant"),n&&this.sync()}sync(){let t=this.list.firstChild;function e(){let e=t;t=e.nextSibling,e.remove()}for(let i of this.items)if(i.dom.parentNode==this.list){for(;t!=i.dom;)e();t=i.dom.nextSibling}else this.list.insertBefore(i.dom,t);for(;t;)e()}moveSelection(t){if(this.selectedIndex<0)return;let e=hp(this.view.state.field(dp).diagnostics,this.items[t].diagnostic);e&&this.view.dispatch({selection:{anchor:e.from,head:e.to},scrollIntoView:!0,effects:up.of(e)})}static open(t){return new kp(t)}}function Qp(t){return function(t,e='viewBox="0 0 40 40"'){return`url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" ${e}>${encodeURIComponent(t)}</svg>')`}(`<path d="m0 2.5 l2 -1.5 l1 0 l2 1.5 l1 0" stroke="${t}" fill="none" stroke-width=".7"/>`,'width="6" height="3"')}const $p=Gs.baseTheme({".cm-diagnostic":{padding:"3px 6px 3px 8px",marginLeft:"-1px",display:"block",whiteSpace:"pre-wrap"},".cm-diagnostic-error":{borderLeft:"5px solid #d11"},".cm-diagnostic-warning":{borderLeft:"5px solid orange"},".cm-diagnostic-info":{borderLeft:"5px solid #999"},".cm-diagnostic-hint":{borderLeft:"5px solid #66d"},".cm-diagnosticAction":{font:"inherit",border:"none",padding:"2px 4px",backgroundColor:"#444",color:"white",borderRadius:"3px",marginLeft:"8px",cursor:"pointer"},".cm-diagnosticSource":{fontSize:"70%",opacity:.7},".cm-lintRange":{backgroundPosition:"left bottom",backgroundRepeat:"repeat-x",paddingBottom:"0.7px"},".cm-lintRange-error":{backgroundImage:Qp("#d11")},".cm-lintRange-warning":{backgroundImage:Qp("orange")},".cm-lintRange-info":{backgroundImage:Qp("#999")},".cm-lintRange-hint":{backgroundImage:Qp("#66d")},".cm-lintRange-active":{backgroundColor:"#ffdd9980"},".cm-tooltip-lint":{padding:0,margin:0},".cm-lintPoint":{position:"relative","&:after":{content:'""',position:"absolute",bottom:0,left:"-2px",borderLeft:"3px solid transparent",borderRight:"3px solid transparent",borderBottom:"4px solid #d11"}},".cm-lintPoint-warning":{"&:after":{borderBottomColor:"orange"}},".cm-lintPoint-info":{"&:after":{borderBottomColor:"#999"}},".cm-lintPoint-hint":{"&:after":{borderBottomColor:"#66d"}},".cm-panel.cm-panel-lint":{position:"relative","& ul":{maxHeight:"100px",overflowY:"auto","& [aria-selected]":{backgroundColor:"#ddd","& u":{textDecoration:"underline"}},"&:focus [aria-selected]":{background_fallback:"#bdf",backgroundColor:"Highlight",color_fallback:"white",color:"HighlightText"},"& u":{textDecoration:"none"},padding:0,margin:0},"& [name=close]":{position:"absolute",top:"0",right:"2px",background:"inherit",border:"none",font:"inherit",padding:0,margin:0}}}),Pp=[dp,Gs.decorations.compute([dp],(t=>{let{selected:e,panel:i}=t.field(dp);return e&&i&&e.from!=e.to?si.set([pp.range(e.from,e.to)]):si.none})),ho(Op,{hideOn:function(t,e){let i=t.startState.doc.lineAt(e.pos);return!(!t.effects.some((t=>t.is(cp)))&&!t.changes.touchesRange(i.from,i.to))}}),$p],Zp=(()=>[Do(),Vo(),Xr(),kc(),ph(),Or(),Qr(),Qt.allowMultipleSelections.of(!0),El(),bh(xh,{fallback:!0}),Xh(),[Ud,zd],sp(),Br(),Nr(),Dr(),tu(),tr.of([...Hd,...Mf,...Yu,..._c,...rh,...rp,...wp])])(),Cp=(()=>[Xr(),kc(),Or(),bh(xh,{fallback:!0}),tr.of([...Mf,..._c])])();var Tp=Object.freeze({__proto__:null,EditorView:Gs,basicSetup:Zp,minimalSetup:Cp});class Ap{constructor(t,e,i,n,s,r,o,a,l,h=0,c){this.p=t,this.stack=e,this.state=i,this.reducePos=n,this.pos=s,this.score=r,this.buffer=o,this.bufferBase=a,this.curContext=l,this.lookAhead=h,this.parent=c}toString(){return`[${this.stack.filter(((t,e)=>e%3==0)).concat(this.state)}]@${this.pos}${this.score?"!"+this.score:""}`}static start(t,e,i=0){let n=t.parser.context;return new Ap(t,[],e,i,i,0,[],0,n?new Rp(n,n.start):null,0,null)}get context(){return this.curContext?this.curContext.context:null}pushState(t,e){this.stack.push(this.state,e,this.bufferBase+this.buffer.length),this.state=t}reduce(t){var e;let i=t>>19,n=65535&t,{parser:s}=this.p,r=s.dynamicPrecedence(n);if(r&&(this.score+=r),0==i)return this.pushState(s.getGoto(this.state,n,!0),this.reducePos),n<s.minRepeatTerm&&this.storeNode(n,this.reducePos,this.reducePos,4,!0),void this.reduceContext(n,this.reducePos);let o=this.stack.length-3*(i-1)-(262144&t?6:0),a=o?this.stack[o-2]:this.p.ranges[0].from,l=this.reducePos-a;l>=2e3&&!(null===(e=this.p.parser.nodeSet.types[n])||void 0===e?void 0:e.isAnonymous)&&(a==this.p.lastBigReductionStart?(this.p.bigReductionCount++,this.p.lastBigReductionSize=l):this.p.lastBigReductionSize<l&&(this.p.bigReductionCount=1,this.p.lastBigReductionStart=a,this.p.lastBigReductionSize=l));let h=o?this.stack[o-1]:0,c=this.bufferBase+this.buffer.length-h;if(n<s.minRepeatTerm||131072&t){let t=s.stateFlag(this.state,1)?this.pos:this.reducePos;this.storeNode(n,a,t,c+4,!0)}if(262144&t)this.state=this.stack[o];else{let t=this.stack[o-3];this.state=s.getGoto(t,n,!0)}for(;this.stack.length>o;)this.stack.pop();this.reduceContext(n,a)}storeNode(t,e,i,n=4,s=!1){if(0==t&&(!this.stack.length||this.stack[this.stack.length-1]<this.buffer.length+this.bufferBase)){let t=this,n=this.buffer.length;if(0==n&&t.parent&&(n=t.bufferBase-t.parent.bufferBase,t=t.parent),n>0&&0==t.buffer[n-4]&&t.buffer[n-1]>-1){if(e==i)return;if(t.buffer[n-2]>=e)return void(t.buffer[n-2]=i)}}if(s&&this.pos!=i){let s=this.buffer.length;if(s>0&&0!=this.buffer[s-4])for(;s>0&&this.buffer[s-2]>i;)this.buffer[s]=this.buffer[s-4],this.buffer[s+1]=this.buffer[s-3],this.buffer[s+2]=this.buffer[s-2],this.buffer[s+3]=this.buffer[s-1],s-=4,n>4&&(n-=4);this.buffer[s]=t,this.buffer[s+1]=e,this.buffer[s+2]=i,this.buffer[s+3]=n}else this.buffer.push(t,e,i,n)}shift(t,e,i,n){if(131072&t)this.pushState(65535&t,this.pos);else if(0==(262144&t)){let s=t,{parser:r}=this.p;(n>this.pos||e<=r.maxNode)&&(this.pos=n,r.stateFlag(s,1)||(this.reducePos=n)),this.pushState(s,i),this.shiftContext(e,i),e<=r.maxNode&&this.buffer.push(e,i,n,4)}else this.pos=n,this.shiftContext(e,i),e<=this.p.parser.maxNode&&this.buffer.push(e,i,n,4)}apply(t,e,i,n){65536&t?this.reduce(t):this.shift(t,e,i,n)}useNode(t,e){let i=this.p.reused.length-1;(i<0||this.p.reused[i]!=t)&&(this.p.reused.push(t),i++);let n=this.pos;this.reducePos=this.pos=n+t.length,this.pushState(e,n),this.buffer.push(i,n,this.reducePos,-1),this.curContext&&this.updateContext(this.curContext.tracker.reuse(this.curContext.context,t,this,this.p.stream.reset(this.pos-t.length)))}split(){let t=this,e=t.buffer.length;for(;e>0&&t.buffer[e-2]>t.reducePos;)e-=4;let i=t.buffer.slice(e),n=t.bufferBase+e;for(;t&&n==t.bufferBase;)t=t.parent;return new Ap(this.p,this.stack.slice(),this.state,this.reducePos,this.pos,this.score,i,n,this.curContext,this.lookAhead,t)}recoverByDelete(t,e){let i=t<=this.p.parser.maxNode;i&&this.storeNode(t,this.pos,e,4),this.storeNode(0,this.pos,e,i?8:4),this.pos=this.reducePos=e,this.score-=190}canShift(t){for(let e=new Xp(this);;){let i=this.p.parser.stateSlot(e.state,4)||this.p.parser.hasAction(e.state,t);if(0==i)return!1;if(0==(65536&i))return!0;e.reduce(i)}}recoverByInsert(t){if(this.stack.length>=300)return[];let e=this.p.parser.nextStates(this.state);if(e.length>8||this.stack.length>=120){let i=[];for(let n,s=0;s<e.length;s+=2)(n=e[s+1])!=this.state&&this.p.parser.hasAction(n,t)&&i.push(e[s],n);if(this.stack.length<120)for(let t=0;i.length<8&&t<e.length;t+=2){let n=e[t+1];i.some(((t,e)=>1&e&&t==n))||i.push(e[t],n)}e=i}let i=[];for(let t=0;t<e.length&&i.length<4;t+=2){let n=e[t+1];if(n==this.state)continue;let s=this.split();s.pushState(n,this.pos),s.storeNode(0,s.pos,s.pos,4,!0),s.shiftContext(e[t],this.pos),s.reducePos=this.pos,s.score-=200,i.push(s)}return i}forceReduce(){let{parser:t}=this.p,e=t.stateSlot(this.state,5);if(0==(65536&e))return!1;if(!t.validAction(this.state,e)){let i=e>>19,n=65535&e,s=this.stack.length-3*i;if(s<0||t.getGoto(this.stack[s],n,!1)<0){let t=this.findForcedReduction();if(null==t)return!1;e=t}this.storeNode(0,this.pos,this.pos,4,!0),this.score-=100}return this.reducePos=this.pos,this.reduce(e),!0}findForcedReduction(){let{parser:t}=this.p,e=[],i=(n,s)=>{if(!e.includes(n))return e.push(n),t.allActions(n,(e=>{if(393216&e);else if(65536&e){let i=(e>>19)-s;if(i>1){let n=65535&e,s=this.stack.length-3*i;if(s>=0&&t.getGoto(this.stack[s],n,!1)>=0)return i<<19|65536|n}}else{let t=i(e,s+1);if(null!=t)return t}}))};return i(this.state,0)}forceAll(){for(;!this.p.parser.stateFlag(this.state,2);)if(!this.forceReduce()){this.storeNode(0,this.pos,this.pos,4,!0);break}return this}get deadEnd(){if(3!=this.stack.length)return!1;let{parser:t}=this.p;return 65535==t.data[t.stateSlot(this.state,1)]&&!t.stateSlot(this.state,4)}restart(){this.storeNode(0,this.pos,this.pos,4,!0),this.state=this.stack[0],this.stack.length=0}sameState(t){if(this.state!=t.state||this.stack.length!=t.stack.length)return!1;for(let e=0;e<this.stack.length;e+=3)if(this.stack[e]!=t.stack[e])return!1;return!0}get parser(){return this.p.parser}dialectEnabled(t){return this.p.parser.dialect.flags[t]}shiftContext(t,e){this.curContext&&this.updateContext(this.curContext.tracker.shift(this.curContext.context,t,this,this.p.stream.reset(e)))}reduceContext(t,e){this.curContext&&this.updateContext(this.curContext.tracker.reduce(this.curContext.context,t,this,this.p.stream.reset(e)))}emitContext(){let t=this.buffer.length-1;(t<0||-3!=this.buffer[t])&&this.buffer.push(this.curContext.hash,this.pos,this.pos,-3)}emitLookAhead(){let t=this.buffer.length-1;(t<0||-4!=this.buffer[t])&&this.buffer.push(this.lookAhead,this.pos,this.pos,-4)}updateContext(t){if(t!=this.curContext.context){let e=new Rp(this.curContext.tracker,t);e.hash!=this.curContext.hash&&this.emitContext(),this.curContext=e}}setLookAhead(t){t>this.lookAhead&&(this.emitLookAhead(),this.lookAhead=t)}close(){this.curContext&&this.curContext.tracker.strict&&this.emitContext(),this.lookAhead>0&&this.emitLookAhead()}}class Rp{constructor(t,e){this.tracker=t,this.context=e,this.hash=t.strict?t.hash(e):0}}class Xp{constructor(t){this.start=t,this.state=t.state,this.stack=t.stack,this.base=this.stack.length}reduce(t){let e=65535&t,i=t>>19;0==i?(this.stack==this.start.stack&&(this.stack=this.stack.slice()),this.stack.push(this.state,0,0),this.base+=3):this.base-=3*(i-1);let n=this.start.p.parser.getGoto(this.stack[this.base-3],e,!0);this.state=n}}class Yp{constructor(t,e,i){this.stack=t,this.pos=e,this.index=i,this.buffer=t.buffer,0==this.index&&this.maybeNext()}static create(t,e=t.bufferBase+t.buffer.length){return new Yp(t,e,e-t.bufferBase)}maybeNext(){let t=this.stack.parent;null!=t&&(this.index=this.stack.bufferBase-t.bufferBase,this.stack=t,this.buffer=t.buffer)}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}next(){this.index-=4,this.pos-=4,0==this.index&&this.maybeNext()}fork(){return new Yp(this.stack,this.pos,this.index)}}function Wp(t,e=Uint16Array){if("string"!=typeof t)return t;let i=null;for(let n=0,s=0;n<t.length;){let r=0;for(;;){let e=t.charCodeAt(n++),i=!1;if(126==e){r=65535;break}e>=92&&e--,e>=34&&e--;let s=e-32;if(s>=46&&(s-=46,i=!0),r+=s,i)break;r*=46}i?i[s++]=r:i=new e(r)}return i}class Mp{constructor(){this.start=-1,this.value=-1,this.end=-1,this.extended=-1,this.lookAhead=0,this.mask=0,this.context=0}}const jp=new Mp;class Dp{constructor(t,e){this.input=t,this.ranges=e,this.chunk="",this.chunkOff=0,this.chunk2="",this.chunk2Pos=0,this.next=-1,this.token=jp,this.rangeIndex=0,this.pos=this.chunkPos=e[0].from,this.range=e[0],this.end=e[e.length-1].to,this.readNext()}resolveOffset(t,e){let i=this.range,n=this.rangeIndex,s=this.pos+t;for(;s<i.from;){if(!n)return null;let t=this.ranges[--n];s-=i.from-t.to,i=t}for(;e<0?s>i.to:s>=i.to;){if(n==this.ranges.length-1)return null;let t=this.ranges[++n];s+=t.from-i.to,i=t}return s}clipPos(t){if(t>=this.range.from&&t<this.range.to)return t;for(let e of this.ranges)if(e.to>t)return Math.max(t,e.from);return this.end}peek(t){let e,i,n=this.chunkOff+t;if(n>=0&&n<this.chunk.length)e=this.pos+t,i=this.chunk.charCodeAt(n);else{let n=this.resolveOffset(t,1);if(null==n)return-1;if(e=n,e>=this.chunk2Pos&&e<this.chunk2Pos+this.chunk2.length)i=this.chunk2.charCodeAt(e-this.chunk2Pos);else{let t=this.rangeIndex,n=this.range;for(;n.to<=e;)n=this.ranges[++t];this.chunk2=this.input.chunk(this.chunk2Pos=e),e+this.chunk2.length>n.to&&(this.chunk2=this.chunk2.slice(0,n.to-e)),i=this.chunk2.charCodeAt(0)}}return e>=this.token.lookAhead&&(this.token.lookAhead=e+1),i}acceptToken(t,e=0){let i=e?this.resolveOffset(e,-1):this.pos;if(null==i||i<this.token.start)throw new RangeError("Token end out of bounds");this.token.value=t,this.token.end=i}acceptTokenTo(t,e){this.token.value=t,this.token.end=e}getChunk(){if(this.pos>=this.chunk2Pos&&this.pos<this.chunk2Pos+this.chunk2.length){let{chunk:t,chunkPos:e}=this;this.chunk=this.chunk2,this.chunkPos=this.chunk2Pos,this.chunk2=t,this.chunk2Pos=e,this.chunkOff=this.pos-this.chunkPos}else{this.chunk2=this.chunk,this.chunk2Pos=this.chunkPos;let t=this.input.chunk(this.pos),e=this.pos+t.length;this.chunk=e>this.range.to?t.slice(0,this.range.to-this.pos):t,this.chunkPos=this.pos,this.chunkOff=0}}readNext(){return this.chunkOff>=this.chunk.length&&(this.getChunk(),this.chunkOff==this.chunk.length)?this.next=-1:this.next=this.chunk.charCodeAt(this.chunkOff)}advance(t=1){for(this.chunkOff+=t;this.pos+t>=this.range.to;){if(this.rangeIndex==this.ranges.length-1)return this.setDone();t-=this.range.to-this.pos,this.range=this.ranges[++this.rangeIndex],this.pos=this.range.from}return this.pos+=t,this.pos>=this.token.lookAhead&&(this.token.lookAhead=this.pos+1),this.readNext()}setDone(){return this.pos=this.chunkPos=this.end,this.range=this.ranges[this.rangeIndex=this.ranges.length-1],this.chunk="",this.next=-1}reset(t,e){if(e?(this.token=e,e.start=t,e.lookAhead=t+1,e.value=e.extended=-1):this.token=jp,this.pos!=t){if(this.pos=t,t==this.end)return this.setDone(),this;for(;t<this.range.from;)this.range=this.ranges[--this.rangeIndex];for(;t>=this.range.to;)this.range=this.ranges[++this.rangeIndex];t>=this.chunkPos&&t<this.chunkPos+this.chunk.length?this.chunkOff=t-this.chunkPos:(this.chunk="",this.chunkOff=0),this.readNext()}return this}read(t,e){if(t>=this.chunkPos&&e<=this.chunkPos+this.chunk.length)return this.chunk.slice(t-this.chunkPos,e-this.chunkPos);if(t>=this.chunk2Pos&&e<=this.chunk2Pos+this.chunk2.length)return this.chunk2.slice(t-this.chunk2Pos,e-this.chunk2Pos);if(t>=this.range.from&&e<=this.range.to)return this.input.read(t,e);let i="";for(let n of this.ranges){if(n.from>=e)break;n.to>t&&(i+=this.input.read(Math.max(n.from,t),Math.min(n.to,e)))}return i}}class Ep{constructor(t,e){this.data=t,this.id=e}token(t,e){let{parser:i}=e.p;Vp(this.data,t,e,this.id,i.data,i.tokenPrecTable)}}Ep.prototype.contextual=Ep.prototype.fallback=Ep.prototype.extend=!1;class qp{constructor(t,e,i){this.precTable=e,this.elseToken=i,this.data="string"==typeof t?Wp(t):t}token(t,e){let i=t.pos,n=0;for(;;){let i=t.next<0,s=t.resolveOffset(1,1);if(Vp(this.data,t,e,0,this.data,this.precTable),t.token.value>-1)break;if(null==this.elseToken)return;if(i||n++,null==s)break;t.reset(s,t.token)}n&&(t.reset(i,t.token),t.acceptToken(this.elseToken,n))}}qp.prototype.contextual=Ep.prototype.fallback=Ep.prototype.extend=!1;class _p{constructor(t,e={}){this.token=t,this.contextual=!!e.contextual,this.fallback=!!e.fallback,this.extend=!!e.extend}}function Vp(t,e,i,n,s,r){let o=0,a=1<<n,{dialect:l}=i.p.parser;t:for(;0!=(a&t[o]);){let i=t[o+1];for(let n=o+3;n<i;n+=2)if((t[n+1]&a)>0){let i=t[n];if(l.allows(i)&&(-1==e.token.value||e.token.value==i||zp(i,e.token.value,s,r))){e.acceptToken(i);break}}let n=e.next,h=0,c=t[o+2];if(!(e.next<0&&c>h&&65535==t[i+3*c-3])){for(;h<c;){let s=h+c>>1,r=i+s+(s<<1),a=t[r],l=t[r+1]||65536;if(n<a)c=s;else{if(!(n>=l)){o=t[r+2],e.advance();continue t}h=s+1}}break}o=t[i+3*c-1]}}function Ip(t,e,i){for(let n,s=e;65535!=(n=t[s]);s++)if(n==i)return s-e;return-1}function zp(t,e,i,n){let s=Ip(i,n,e);return s<0||Ip(i,n,t)<s}const Bp="undefined"!=typeof process&&process.env&&/\bparse\b/.test(process.env.LOG);let Gp=null;function Lp(t,e,i){let n=t.cursor(ra.IncludeAnonymous);for(n.moveTo(e);;)if(!(i<0?n.childBefore(e):n.childAfter(e)))for(;;){if((i<0?n.to<e:n.from>e)&&!n.type.isError)return i<0?Math.max(0,Math.min(n.to-1,e-25)):Math.min(t.length,Math.max(n.from+1,e+25));if(i<0?n.prevSibling():n.nextSibling())break;if(!n.parent())return i<0?0:t.length}}class Np{constructor(t,e){this.fragments=t,this.nodeSet=e,this.i=0,this.fragment=null,this.safeFrom=-1,this.safeTo=-1,this.trees=[],this.start=[],this.index=[],this.nextFragment()}nextFragment(){let t=this.fragment=this.i==this.fragments.length?null:this.fragments[this.i++];if(t){for(this.safeFrom=t.openStart?Lp(t.tree,t.from+t.offset,1)-t.offset:t.from,this.safeTo=t.openEnd?Lp(t.tree,t.to+t.offset,-1)-t.offset:t.to;this.trees.length;)this.trees.pop(),this.start.pop(),this.index.pop();this.trees.push(t.tree),this.start.push(-t.offset),this.index.push(0),this.nextStart=this.safeFrom}else this.nextStart=1e9}nodeAt(t){if(t<this.nextStart)return null;for(;this.fragment&&this.safeTo<=t;)this.nextFragment();if(!this.fragment)return null;for(;;){let e=this.trees.length-1;if(e<0)return this.nextFragment(),null;let i=this.trees[e],n=this.index[e];if(n==i.children.length){this.trees.pop(),this.start.pop(),this.index.pop();continue}let s=i.children[n],r=this.start[e]+i.positions[n];if(r>t)return this.nextStart=r,null;if(s instanceof oa){if(r==t){if(r<this.safeFrom)return null;let t=r+s.length;if(t<=this.safeTo){let e=s.prop(Jo.lookAhead);if(!e||t+e<this.fragment.to)return s}}this.index[e]++,r+s.length>=Math.max(this.safeFrom,t)&&(this.trees.push(s),this.start.push(r),this.index.push(0))}else this.index[e]++,this.nextStart=r+s.length}}}class Up{constructor(t,e){this.stream=e,this.tokens=[],this.mainToken=null,this.actions=[],this.tokens=t.tokenizers.map((t=>new Mp))}getActions(t){let e=0,i=null,{parser:n}=t.p,{tokenizers:s}=n,r=n.stateSlot(t.state,3),o=t.curContext?t.curContext.hash:0,a=0;for(let n=0;n<s.length;n++){if(0==(1<<n&r))continue;let l=s[n],h=this.tokens[n];if((!i||l.fallback)&&((l.contextual||h.start!=t.pos||h.mask!=r||h.context!=o)&&(this.updateCachedToken(h,l,t),h.mask=r,h.context=o),h.lookAhead>h.end+25&&(a=Math.max(h.lookAhead,a)),0!=h.value)){let n=e;if(h.extended>-1&&(e=this.addActions(t,h.extended,h.end,e)),e=this.addActions(t,h.value,h.end,e),!l.extend&&(i=h,e>n))break}}for(;this.actions.length>e;)this.actions.pop();return a&&t.setLookAhead(a),i||t.pos!=this.stream.end||(i=new Mp,i.value=t.p.parser.eofTerm,i.start=i.end=t.pos,e=this.addActions(t,i.value,i.end,e)),this.mainToken=i,this.actions}getMainToken(t){if(this.mainToken)return this.mainToken;let e=new Mp,{pos:i,p:n}=t;return e.start=i,e.end=Math.min(i+1,n.stream.end),e.value=i==n.stream.end?n.parser.eofTerm:0,e}updateCachedToken(t,e,i){let n=this.stream.clipPos(i.pos);if(e.token(this.stream.reset(n,t),i),t.value>-1){let{parser:e}=i.p;for(let n=0;n<e.specialized.length;n++)if(e.specialized[n]==t.value){let s=e.specializers[n](this.stream.read(t.start,t.end),i);if(s>=0&&i.p.parser.dialect.allows(s>>1)){0==(1&s)?t.value=s>>1:t.extended=s>>1;break}}}else t.value=0,t.end=this.stream.clipPos(n+1)}putAction(t,e,i,n){for(let e=0;e<n;e+=3)if(this.actions[e]==t)return n;return this.actions[n++]=t,this.actions[n++]=e,this.actions[n++]=i,n}addActions(t,e,i,n){let{state:s}=t,{parser:r}=t.p,{data:o}=r;for(let t=0;t<2;t++)for(let a=r.stateSlot(s,t?2:1);;a+=3){if(65535==o[a]){if(1!=o[a+1]){0==n&&2==o[a+1]&&(n=this.putAction(eO(o,a+2),e,i,n));break}a=eO(o,a+2)}o[a]==e&&(n=this.putAction(eO(o,a+1),e,i,n))}return n}}class Hp{constructor(t,e,i,n){this.parser=t,this.input=e,this.ranges=n,this.recovering=0,this.nextStackID=9812,this.minStackPos=0,this.reused=[],this.stoppedAt=null,this.lastBigReductionStart=-1,this.lastBigReductionSize=0,this.bigReductionCount=0,this.stream=new Dp(e,n),this.tokens=new Up(t,this.stream),this.topTerm=t.top[1];let{from:s}=n[0];this.stacks=[Ap.start(this,t.top[0],s)],this.fragments=i.length&&this.stream.end-s>4*t.bufferLength?new Np(i,t.nodeSet):null}get parsedPos(){return this.minStackPos}advance(){let t,e,i=this.stacks,n=this.minStackPos,s=this.stacks=[];if(this.bigReductionCount>300&&1==i.length){let[t]=i;for(;t.forceReduce()&&t.stack.length&&t.stack[t.stack.length-2]>=this.lastBigReductionStart;);this.bigReductionCount=this.lastBigReductionSize=0}for(let r=0;r<i.length;r++){let o=i[r];for(;;){if(this.tokens.mainToken=null,o.pos>n)s.push(o);else{if(this.advanceStack(o,s,i))continue;{t||(t=[],e=[]),t.push(o);let i=this.tokens.getMainToken(o);e.push(i.value,i.end)}}break}}if(!s.length){let e=t&&function(t){let e=null;for(let i of t){let t=i.p.stoppedAt;(i.pos==i.p.stream.end||null!=t&&i.pos>t)&&i.p.parser.stateFlag(i.state,2)&&(!e||e.score<i.score)&&(e=i)}return e}(t);if(e)return Bp&&console.log("Finish with "+this.stackID(e)),this.stackToTree(e);if(this.parser.strict)throw Bp&&t&&console.log("Stuck with token "+(this.tokens.mainToken?this.parser.getName(this.tokens.mainToken.value):"none")),new SyntaxError("No parse at "+n);this.recovering||(this.recovering=5)}if(this.recovering&&t){let i=null!=this.stoppedAt&&t[0].pos>this.stoppedAt?t[0]:this.runRecovery(t,e,s);if(i)return Bp&&console.log("Force-finish "+this.stackID(i)),this.stackToTree(i.forceAll())}if(this.recovering){let t=1==this.recovering?1:3*this.recovering;if(s.length>t)for(s.sort(((t,e)=>e.score-t.score));s.length>t;)s.pop();s.some((t=>t.reducePos>n))&&this.recovering--}else if(s.length>1){t:for(let t=0;t<s.length-1;t++){let e=s[t];for(let i=t+1;i<s.length;i++){let n=s[i];if(e.sameState(n)||e.buffer.length>500&&n.buffer.length>500){if(!((e.score-n.score||e.buffer.length-n.buffer.length)>0)){s.splice(t--,1);continue t}s.splice(i--,1)}}}s.length>12&&s.splice(12,s.length-12)}this.minStackPos=s[0].pos;for(let t=1;t<s.length;t++)s[t].pos<this.minStackPos&&(this.minStackPos=s[t].pos);return null}stopAt(t){if(null!=this.stoppedAt&&this.stoppedAt<t)throw new RangeError("Can't move stoppedAt forward");this.stoppedAt=t}advanceStack(t,e,i){let n=t.pos,{parser:s}=this,r=Bp?this.stackID(t)+" -> ":"";if(null!=this.stoppedAt&&n>this.stoppedAt)return t.forceReduce()?t:null;if(this.fragments){let e=t.curContext&&t.curContext.tracker.strict,i=e?t.curContext.hash:0;for(let o=this.fragments.nodeAt(n);o;){let n=this.parser.nodeSet.types[o.type.id]==o.type?s.getGoto(t.state,o.type.id):-1;if(n>-1&&o.length&&(!e||(o.prop(Jo.contextHash)||0)==i))return t.useNode(o,n),Bp&&console.log(r+this.stackID(t)+` (via reuse of ${s.getName(o.type.id)})`),!0;if(!(o instanceof oa)||0==o.children.length||o.positions[0]>0)break;let a=o.children[0];if(!(a instanceof oa&&0==o.positions[0]))break;o=a}}let o=s.stateSlot(t.state,4);if(o>0)return t.reduce(o),Bp&&console.log(r+this.stackID(t)+` (via always-reduce ${s.getName(65535&o)})`),!0;if(t.stack.length>=8400)for(;t.stack.length>6e3&&t.forceReduce(););let a=this.tokens.getActions(t);for(let o=0;o<a.length;){let l=a[o++],h=a[o++],c=a[o++],f=o==a.length||!i,u=f?t:t.split(),d=this.tokens.mainToken;if(u.apply(l,h,d?d.start:u.pos,c),Bp&&console.log(r+this.stackID(u)+` (via ${0==(65536&l)?"shift":`reduce of ${s.getName(65535&l)}`} for ${s.getName(h)} @ ${n}${u==t?"":", split"})`),f)return!0;u.pos>n?e.push(u):i.push(u)}return!1}advanceFully(t,e){let i=t.pos;for(;;){if(!this.advanceStack(t,null,null))return!1;if(t.pos>i)return Fp(t,e),!0}}runRecovery(t,e,i){let n=null,s=!1;for(let r=0;r<t.length;r++){let o=t[r],a=e[r<<1],l=e[1+(r<<1)],h=Bp?this.stackID(o)+" -> ":"";if(o.deadEnd){if(s)continue;if(s=!0,o.restart(),Bp&&console.log(h+this.stackID(o)+" (restarted)"),this.advanceFully(o,i))continue}let c=o.split(),f=h;for(let t=0;c.forceReduce()&&t<10;t++){if(Bp&&console.log(f+this.stackID(c)+" (via force-reduce)"),this.advanceFully(c,i))break;Bp&&(f=this.stackID(c)+" -> ")}for(let t of o.recoverByInsert(a))Bp&&console.log(h+this.stackID(t)+" (via recover-insert)"),this.advanceFully(t,i);this.stream.end>o.pos?(l==o.pos&&(l++,a=0),o.recoverByDelete(a,l),Bp&&console.log(h+this.stackID(o)+` (via recover-delete ${this.parser.getName(a)})`),Fp(o,i)):(!n||n.score<o.score)&&(n=o)}return n}stackToTree(t){return t.close(),oa.build({buffer:Yp.create(t),nodeSet:this.parser.nodeSet,topID:this.topTerm,maxBufferLength:this.parser.bufferLength,reused:this.reused,start:this.ranges[0].from,length:t.pos-this.ranges[0].from,minRepeatType:this.parser.minRepeatTerm})}stackID(t){let e=(Gp||(Gp=new WeakMap)).get(t);return e||Gp.set(t,e=String.fromCodePoint(this.nextStackID++)),e+t}}function Fp(t,e){for(let i=0;i<e.length;i++){let n=e[i];if(n.pos==t.pos&&n.sameState(t))return void(e[i].score<t.score&&(e[i]=t))}e.push(t)}class Jp{constructor(t,e,i){this.source=t,this.flags=e,this.disabled=i}allows(t){return!this.disabled||0==this.disabled[t]}}const Kp=t=>t;class tO extends $a{constructor(t){if(super(),this.wrappers=[],14!=t.version)throw new RangeError(`Parser version (${t.version}) doesn't match runtime version (14)`);let e=t.nodeNames.split(" ");this.minRepeatTerm=e.length;for(let i=0;i<t.repeatNodeCount;i++)e.push("");let i=Object.keys(t.topRules).map((e=>t.topRules[e][1])),n=[];for(let t=0;t<e.length;t++)n.push([]);function s(t,e,i){n[t].push([e,e.deserialize(String(i))])}if(t.nodeProps)for(let e of t.nodeProps){let t=e[0];"string"==typeof t&&(t=Jo[t]);for(let i=1;i<e.length;){let n=e[i++];if(n>=0)s(n,t,e[i++]);else{let r=e[i+-n];for(let o=-n;o>0;o--)s(e[i++],t,r);i++}}}this.nodeSet=new ia(e.map(((e,s)=>ea.define({name:s>=this.minRepeatTerm?void 0:e,id:s,props:n[s],top:i.indexOf(s)>-1,error:0==s,skipped:t.skippedNodes&&t.skippedNodes.indexOf(s)>-1})))),t.propSources&&(this.nodeSet=this.nodeSet.extend(...t.propSources)),this.strict=!1,this.bufferLength=Uo;let r=Wp(t.tokenData);this.context=t.context,this.specializerSpecs=t.specialized||[],this.specialized=new Uint16Array(this.specializerSpecs.length);for(let t=0;t<this.specializerSpecs.length;t++)this.specialized[t]=this.specializerSpecs[t].term;this.specializers=this.specializerSpecs.map(iO),this.states=Wp(t.states,Uint32Array),this.data=Wp(t.stateData),this.goto=Wp(t.goto),this.maxTerm=t.maxTerm,this.tokenizers=t.tokenizers.map((t=>"number"==typeof t?new Ep(r,t):t)),this.topRules=t.topRules,this.dialects=t.dialects||{},this.dynamicPrecedences=t.dynamicPrecedences||null,this.tokenPrecTable=t.tokenPrec,this.termNames=t.termNames||null,this.maxNode=this.nodeSet.types.length-1,this.dialect=this.parseDialect(),this.top=this.topRules[Object.keys(this.topRules)[0]]}createParse(t,e,i){let n=new Hp(this,t,e,i);for(let s of this.wrappers)n=s(n,t,e,i);return n}getGoto(t,e,i=!1){let n=this.goto;if(e>=n[0])return-1;for(let s=n[e+1];;){let e=n[s++],r=1&e,o=n[s++];if(r&&i)return o;for(let i=s+(e>>1);s<i;s++)if(n[s]==t)return o;if(r)return-1}}hasAction(t,e){let i=this.data;for(let n=0;n<2;n++)for(let s,r=this.stateSlot(t,n?2:1);;r+=3){if(65535==(s=i[r])){if(1!=i[r+1]){if(2==i[r+1])return eO(i,r+2);break}s=i[r=eO(i,r+2)]}if(s==e||0==s)return eO(i,r+1)}return 0}stateSlot(t,e){return this.states[6*t+e]}stateFlag(t,e){return(this.stateSlot(t,0)&e)>0}validAction(t,e){return!!this.allActions(t,(t=>t==e||null))}allActions(t,e){let i=this.stateSlot(t,4),n=i?e(i):void 0;for(let i=this.stateSlot(t,1);null==n;i+=3){if(65535==this.data[i]){if(1!=this.data[i+1])break;i=eO(this.data,i+2)}n=e(eO(this.data,i+1))}return n}nextStates(t){let e=[];for(let i=this.stateSlot(t,1);;i+=3){if(65535==this.data[i]){if(1!=this.data[i+1])break;i=eO(this.data,i+2)}if(0==(1&this.data[i+2])){let t=this.data[i+1];e.some(((e,i)=>1&i&&e==t))||e.push(this.data[i],t)}}return e}configure(t){let e=Object.assign(Object.create(tO.prototype),this);if(t.props&&(e.nodeSet=this.nodeSet.extend(...t.props)),t.top){let i=this.topRules[t.top];if(!i)throw new RangeError(`Invalid top rule name ${t.top}`);e.top=i}return t.tokenizers&&(e.tokenizers=this.tokenizers.map((e=>{let i=t.tokenizers.find((t=>t.from==e));return i?i.to:e}))),t.specializers&&(e.specializers=this.specializers.slice(),e.specializerSpecs=this.specializerSpecs.map(((i,n)=>{let s=t.specializers.find((t=>t.from==i.external));if(!s)return i;let r=Object.assign(Object.assign({},i),{external:s.to});return e.specializers[n]=iO(r),r}))),t.contextTracker&&(e.context=t.contextTracker),t.dialect&&(e.dialect=this.parseDialect(t.dialect)),null!=t.strict&&(e.strict=t.strict),t.wrap&&(e.wrappers=e.wrappers.concat(t.wrap)),null!=t.bufferLength&&(e.bufferLength=t.bufferLength),e}hasWrappers(){return this.wrappers.length>0}getName(t){return this.termNames?this.termNames[t]:String(t<=this.maxNode&&this.nodeSet.types[t].name||t)}get eofTerm(){return this.maxNode+1}get topNode(){return this.nodeSet.types[this.top[1]]}dynamicPrecedence(t){let e=this.dynamicPrecedences;return null==e?0:e[t]||0}parseDialect(t){let e=Object.keys(this.dialects),i=e.map((()=>!1));if(t)for(let n of t.split(" ")){let t=e.indexOf(n);t>=0&&(i[t]=!0)}let n=null;for(let t=0;t<e.length;t++)if(!i[t])for(let i,s=this.dialects[e[t]];65535!=(i=this.data[s++]);)(n||(n=new Uint8Array(this.maxTerm+1)))[i]=1;return new Jp(t,i,n)}static deserialize(t){return new tO(t)}}function eO(t,e){return t[e]|t[e+1]<<16}function iO(t){if(t.external){let e=t.extend?1:0;return(i,n)=>t.external(i,n)<<1|e}return t.get}const nO=[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288],sO=new class{constructor(t){this.start=t.start,this.shift=t.shift||Kp,this.reduce=t.reduce||Kp,this.reuse=t.reuse||Kp,this.hash=t.hash||(()=>0),this.strict=!1!==t.strict}}({start:!1,shift:(t,e)=>4==e||5==e||312==e?t:313==e,strict:!1}),rO=new _p(((t,e)=>{let{next:i}=t;(125==i||-1==i||e.context)&&t.acceptToken(310)}),{contextual:!0,fallback:!0}),oO=new _p(((t,e)=>{let i,{next:n}=t;nO.indexOf(n)>-1||(47!=n||47!=(i=t.peek(1))&&42!=i)&&(125==n||59==n||-1==n||e.context||t.acceptToken(309))}),{contextual:!0}),aO=new _p(((t,e)=>{let{next:i}=t;if((43==i||45==i)&&(t.advance(),i==t.next)){t.advance();let i=!e.context&&e.canShift(1);t.acceptToken(i?1:2)}}),{contextual:!0});function lO(t,e){return t>=65&&t<=90||t>=97&&t<=122||95==t||t>=192||!e&&t>=48&&t<=57}const hO=new _p(((t,e)=>{if(60!=t.next||!e.dialectEnabled(0))return;if(t.advance(),47==t.next)return;let i=0;for(;nO.indexOf(t.next)>-1;)t.advance(),i++;if(lO(t.next,!0)){for(t.advance(),i++;lO(t.next,!1);)t.advance(),i++;for(;nO.indexOf(t.next)>-1;)t.advance(),i++;if(44==t.next)return;for(let e=0;;e++){if(7==e){if(!lO(t.next,!0))return;break}if(t.next!="extends".charCodeAt(e))break;t.advance(),i++}}t.acceptToken(3,-i)})),cO=Ra({"get set async static":tl.modifier,"for while do if else switch try catch finally return throw break continue default case":tl.controlKeyword,"in of await yield void typeof delete instanceof":tl.operatorKeyword,"let var const using function class extends":tl.definitionKeyword,"import export from":tl.moduleKeyword,"with debugger as new":tl.keyword,TemplateString:tl.special(tl.string),super:tl.atom,BooleanLiteral:tl.bool,this:tl.self,null:tl.null,Star:tl.modifier,VariableName:tl.variableName,"CallExpression/VariableName TaggedTemplateExpression/VariableName":tl.function(tl.variableName),VariableDefinition:tl.definition(tl.variableName),Label:tl.labelName,PropertyName:tl.propertyName,PrivatePropertyName:tl.special(tl.propertyName),"CallExpression/MemberExpression/PropertyName":tl.function(tl.propertyName),"FunctionDeclaration/VariableDefinition":tl.function(tl.definition(tl.variableName)),"ClassDeclaration/VariableDefinition":tl.definition(tl.className),PropertyDefinition:tl.definition(tl.propertyName),PrivatePropertyDefinition:tl.definition(tl.special(tl.propertyName)),UpdateOp:tl.updateOperator,"LineComment Hashbang":tl.lineComment,BlockComment:tl.blockComment,Number:tl.number,String:tl.string,Escape:tl.escape,ArithOp:tl.arithmeticOperator,LogicOp:tl.logicOperator,BitOp:tl.bitwiseOperator,CompareOp:tl.compareOperator,RegExp:tl.regexp,Equals:tl.definitionOperator,Arrow:tl.function(tl.punctuation),": Spread":tl.punctuation,"( )":tl.paren,"[ ]":tl.squareBracket,"{ }":tl.brace,"InterpolationStart InterpolationEnd":tl.special(tl.brace),".":tl.derefOperator,", ;":tl.separator,"@":tl.meta,TypeName:tl.typeName,TypeDefinition:tl.definition(tl.typeName),"type enum interface implements namespace module declare":tl.definitionKeyword,"abstract global Privacy readonly override":tl.modifier,"is keyof unique infer":tl.operatorKeyword,JSXAttributeValue:tl.attributeValue,JSXText:tl.content,"JSXStartTag JSXStartCloseTag JSXSelfCloseEndTag JSXEndTag":tl.angleBracket,"JSXIdentifier JSXNameSpacedName":tl.tagName,"JSXAttribute/JSXIdentifier JSXAttribute/JSXNameSpacedName":tl.attributeName,"JSXBuiltin/JSXIdentifier":tl.standard(tl.tagName)}),fO={__proto__:null,export:18,as:23,from:31,default:34,async:39,function:40,extends:52,this:56,true:64,false:64,null:76,void:80,typeof:84,super:102,new:136,delete:152,yield:161,await:165,class:170,public:227,private:227,protected:227,readonly:229,instanceof:248,satisfies:251,in:252,const:254,import:286,keyof:339,unique:343,infer:349,is:385,abstract:405,implements:407,type:409,let:412,var:414,using:417,interface:423,enum:427,namespace:433,module:435,declare:439,global:443,for:462,of:471,while:474,with:478,do:482,if:486,else:488,switch:492,case:498,try:504,catch:508,finally:512,return:516,throw:520,break:524,continue:528,debugger:532},uO={__proto__:null,async:123,get:125,set:127,declare:187,public:189,private:189,protected:189,static:191,abstract:193,override:195,readonly:201,accessor:203,new:389},dO={__proto__:null,"<":143},pO=tO.deserialize({version:14,states:"$<UO%TQ^OOO%[Q^OOO'_Q`OOP(lOWOOO*zQ08SO'#ChO+RO!bO'#CiO+aO#tO'#CiO+oO?MpO'#D^O.QQ^O'#DdO.bQ^O'#DoO%[Q^O'#DyO0fQ^O'#EROOQ07b'#EZ'#EZO1PQWO'#EWOOQO'#El'#ElOOQO'#Ie'#IeO1XQWO'#GmO1dQWO'#EkO1iQWO'#EkO3kQ08SO'#JiO6[Q08SO'#JjO6xQWO'#FZO6}Q&jO'#FqOOQ07b'#Fc'#FcO7YO,YO'#FcO7hQ7[O'#FxO9UQWO'#FwOOQ07b'#Jj'#JjOOQ07`'#Ji'#JiO9ZQWO'#GqOOQU'#KU'#KUO9fQWO'#IRO9kQ07hO'#ISOOQU'#JW'#JWOOQU'#IW'#IWQ`Q^OOO`Q^OOO%[Q^O'#DqO9sQ^O'#D}O9zQ^O'#EPO9aQWO'#GmO:RQ7[O'#CnO:aQWO'#EjO:lQWO'#EuO:qQ7[O'#FbO;`QWO'#GmOOQO'#KV'#KVO;eQWO'#KVO;sQWO'#GuO;sQWO'#GvO;sQWO'#GxO9aQWO'#G{O<jQWO'#HOO>RQWO'#CdO>cQWO'#H[O>kQWO'#HbO>kQWO'#HdO`Q^O'#HfO>kQWO'#HhO>kQWO'#HkO>pQWO'#HqO>uQ07iO'#HwO%[Q^O'#HyO?QQ07iO'#H{O?]Q07iO'#H}O9kQ07hO'#IPO?hQ08SO'#ChO@jQ`O'#DiQOQWOOO%[Q^O'#EPOAQQWO'#ESO:RQ7[O'#EjOA]QWO'#EjOAhQpO'#FbOOQU'#Cf'#CfOOQ07`'#Dn'#DnOOQ07`'#Jm'#JmO%[Q^O'#JmOOQO'#Jq'#JqOOQO'#Ib'#IbOBhQ`O'#EcOOQ07`'#Eb'#EbOCdQ07pO'#EcOCnQ`O'#EVOOQO'#Jp'#JpODSQ`O'#JqOEaQ`O'#EVOCnQ`O'#EcPEnO!0LbO'#CaPOOO)CDu)CDuOOOO'#IX'#IXOEyO!bO,59TOOQ07b,59T,59TOOOO'#IY'#IYOFXO#tO,59TO%[Q^O'#D`OOOO'#I['#I[OFgO?MpO,59xOOQ07b,59x,59xOFuQ^O'#I]OGYQWO'#JkOI[QrO'#JkO+}Q^O'#JkOIcQWO,5:OOIyQWO'#ElOJWQWO'#JyOJcQWO'#JxOJcQWO'#JxOJkQWO,5;YOJpQWO'#JwOOQ07f,5:Z,5:ZOJwQ^O,5:ZOLxQ08SO,5:eOMiQWO,5:mONSQ07hO'#JvONZQWO'#JuO9ZQWO'#JuONoQWO'#JuONwQWO,5;XON|QWO'#JuO!#UQrO'#JjOOQ07b'#Ch'#ChO%[Q^O'#ERO!#tQpO,5:rOOQO'#Jr'#JrOOQO-E<c-E<cO9aQWO,5=XO!$[QWO,5=XO!$aQ^O,5;VO!&dQ7[O'#EgO!'}QWO,5;VO!)mQ7[O'#DsO!)tQ^O'#DxO!*OQ`O,5;`O!*WQ`O,5;`O%[Q^O,5;`OOQU'#FR'#FROOQU'#FT'#FTO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aO%[Q^O,5;aOOQU'#FX'#FXO!*fQ^O,5;rOOQ07b,5;w,5;wOOQ07b,5;x,5;xO!,iQWO,5;xOOQ07b,5;y,5;yO%[Q^O'#IiO!,qQ07hO,5<eO!&dQ7[O,5;aO!-`Q7[O,5;aO%[Q^O,5;uO!-gQ&jO'#FgO!.dQ&jO'#J}O!.OQ&jO'#J}O!.kQ&jO'#J}OOQO'#J}'#J}O!/PQ&jO,5<POOOS,5<],5<]O!/bQ^O'#FsOOOS'#Ih'#IhO7YO,YO,5;}O!/iQ&jO'#FuOOQ07b,5;},5;}O!0YQMhO'#CuOOQ07b'#Cy'#CyO!0mQWO'#CyO!0rO?MpO'#C}O!1`Q7[O,5<bO!1gQWO,5<dO!3SQ!LQO'#GSO!3aQWO'#GTO!3fQWO'#GTO!3kQ!LQO'#GXO!4jQ`O'#G]OOQO'#Gh'#GhO!(SQ7[O'#GgOOQO'#Gj'#GjO!(SQ7[O'#GiO!5]QMhO'#JdOOQ07b'#Jd'#JdO!5gQWO'#JcO!5uQWO'#JbO!5}QWO'#CtOOQ07b'#Cw'#CwOOQ07b'#DR'#DROOQ07b'#DT'#DTO1SQWO'#DVO!(SQ7[O'#FzO!(SQ7[O'#F|O!6VQWO'#GOO!6[QWO'#GPO!3fQWO'#GVO!(SQ7[O'#G[O!6aQWO'#EmO!7OQWO,5<cOOQ07`'#Cq'#CqO!7WQWO'#EnO!8QQ`O'#EoOOQ07`'#Jw'#JwO!8XQ07hO'#KWO9kQ07hO,5=]O`Q^O,5>mOOQU'#J`'#J`OOQU,5>n,5>nOOQU-E<U-E<UO!:ZQ08SO,5:]O!<wQ08SO,5:iO%[Q^O,5:iO!?bQ08SO,5:kOOQO,5@q,5@qO!@RQ7[O,5=XO!@aQ07hO'#JaO9UQWO'#JaO!@rQ07hO,59YO!@}Q`O,59YO!AVQ7[O,59YO:RQ7[O,59YO!AbQWO,5;VO!AjQWO'#HZO!BOQWO'#KZO%[Q^O,5;zO!7{Q`O,5;|O!BWQWO,5=tO!B]QWO,5=tO!BbQWO,5=tO9kQ07hO,5=tO;sQWO,5=dOOQO'#Cu'#CuO!BpQ`O,5=aO!BxQ7[O,5=bO!CTQWO,5=dO!CYQpO,5=gO!CbQWO'#KVO>pQWO'#HQO9aQWO'#HSO!CgQWO'#HSO:RQ7[O'#HUO!ClQWO'#HUOOQU,5=j,5=jO!CqQWO'#HVO!DSQWO'#CnO!DXQWO,59OO!DcQWO,59OO!FhQ^O,59OOOQU,59O,59OO!FxQ07hO,59OO%[Q^O,59OO!ITQ^O'#H^OOQU'#H_'#H_OOQU'#H`'#H`O`Q^O,5=vO!IkQWO,5=vO`Q^O,5=|O`Q^O,5>OO!IpQWO,5>QO`Q^O,5>SO!IuQWO,5>VO!IzQ^O,5>]OOQU,5>c,5>cO%[Q^O,5>cO9kQ07hO,5>eOOQU,5>g,5>gO!NUQWO,5>gOOQU,5>i,5>iO!NUQWO,5>iOOQU,5>k,5>kO!NZQ`O'#D[O%[Q^O'#JmO!NxQ`O'#JmO# gQ`O'#DjO# xQ`O'#DjO#$ZQ^O'#DjO#$bQWO'#JlO#$jQWO,5:TO#$oQWO'#EpO#$}QWO'#JzO#%VQWO,5;ZO#%[Q`O'#DjO#%iQ`O'#EUOOQ07b,5:n,5:nO%[Q^O,5:nO#%pQWO,5:nO>pQWO,5;UO!@}Q`O,5;UO!AVQ7[O,5;UO:RQ7[O,5;UO#%xQWO,5@XO#%}Q$ISO,5:rOOQO-E<`-E<`O#'TQ07pO,5:}OCnQ`O,5:qO#'_Q`O,5:qOCnQ`O,5:}O!@rQ07hO,5:qOOQ07`'#Ef'#EfOOQO,5:},5:}O%[Q^O,5:}O#'lQ07hO,5:}O#'wQ07hO,5:}O!@}Q`O,5:qOOQO,5;T,5;TO#(VQ07hO,5:}POOO'#IV'#IVP#(kO!0LbO,58{POOO,58{,58{OOOO-E<V-E<VOOQ07b1G.o1G.oOOOO-E<W-E<WO#(vQpO,59zOOOO-E<Y-E<YOOQ07b1G/d1G/dO#({QrO,5>wO+}Q^O,5>wOOQO,5>},5>}O#)VQ^O'#I]OOQO-E<Z-E<ZO#)dQWO,5@VO#)lQrO,5@VO#)sQWO,5@dOOQ07b1G/j1G/jO%[Q^O,5@eO#){QWO'#IcOOQO-E<a-E<aO#)sQWO,5@dOOQ07`1G0t1G0tOOQ07f1G/u1G/uOOQ07f1G0X1G0XO%[Q^O,5@bO#*aQ07hO,5@bO#*rQ07hO,5@bO#*yQWO,5@aO9ZQWO,5@aO#+RQWO,5@aO#+aQWO'#IfO#*yQWO,5@aOOQ07`1G0s1G0sO!*OQ`O,5:tO!*ZQ`O,5:tOOQO,5:v,5:vO#,RQWO,5:vO#,ZQ7[O1G2sO9aQWO1G2sOOQ07b1G0q1G0qO#,iQ08SO1G0qO#-nQ08QO,5;ROOQ07b'#GR'#GRO#.[Q08SO'#JdO!$aQ^O1G0qO#0dQ7[O'#JnO#0nQWO,5:_O#0sQrO'#JoO%[Q^O'#JoO#0}QWO,5:dOOQ07b'#D['#D[OOQ07b1G0z1G0zO%[Q^O1G0zOOQ07b1G1d1G1dO#1SQWO1G0zO#3kQ08SO1G0{O#3rQ08SO1G0{O#6]Q08SO1G0{O#6dQ08SO1G0{O#8nQ08SO1G0{O#9UQ08SO1G0{O#<OQ08SO1G0{O#<VQ08SO1G0{O#>jQ08SO1G0{O#>wQ08SO1G0{O#@uQ08SO1G0{O#CuQ(CYO'#ChO#EsQ(CYO1G1^O#EzQ(CYO'#JjO!,lQWO1G1dO#F[Q08SO,5?TOOQ07`-E<g-E<gO#GOQ08SO1G0{OOQ07b1G0{1G0{O#IZQ08SO1G1aO#I}Q&jO,5<TO#JVQ&jO,5<UO#J_Q&jO'#FlO#JvQWO'#FkOOQO'#KO'#KOOOQO'#Ig'#IgO#J{Q&jO1G1kOOQ07b1G1k1G1kOOOS1G1v1G1vO#K^Q(CYO'#JiO#KhQWO,5<_O!*fQ^O,5<_OOOS-E<f-E<fOOQ07b1G1i1G1iO#KmQ`O'#J}OOQ07b,5<a,5<aO#KuQ`O,5<aOOQ07b,59e,59eO!&dQ7[O'#DPOOOO'#IZ'#IZO#KzO?MpO,59iOOQ07b,59i,59iO%[Q^O1G1|O!6[QWO'#IkO#LVQ7[O,5<uOOQ07b,5<r,5<rO!(SQ7[O'#InO#LuQ7[O,5=RO!(SQ7[O'#IpO#MhQ7[O,5=TO!&dQ7[O,5=VOOQO1G2O1G2OO#MrQpO'#CqO#NVQpO,5<nO#N^QWO'#KRO9aQWO'#KRO#NlQWO,5<pO!(SQ7[O,5<oO#NqQWO'#GUO#N|QWO,5<oO$ RQpO'#GRO$ `QpO'#KSO$ jQWO'#KSO!&dQ7[O'#KSO$ oQWO,5<sO$ tQ`O'#G^O!4eQ`O'#G^O$!VQWO'#G`O$![QWO'#GbO!3fQWO'#GeO$!aQ07hO'#ImO$!lQ`O,5<wOOQ07f,5<w,5<wO$!sQ`O'#G^O$#RQ`O'#G_O$#ZQ`O'#G_O$#`Q7[O,5=RO$#pQ7[O,5=TOOQ07b,5=W,5=WO!(SQ7[O,5?}O!(SQ7[O,5?}O$$QQWO'#IrO$$]QWO,5?|O$$eQWO,59`O$%UQ7[O,59qOOQ07b,59q,59qO$%wQ7[O,5<fO$&jQ7[O,5<hO@bQWO,5<jOOQ07b,5<k,5<kO$&tQWO,5<qO$&yQ7[O,5<vO$'ZQWO'#JuO!$aQ^O1G1}O$'`QWO1G1}O9ZQWO'#JxO9ZQWO'#EpO%[Q^O'#EpO9ZQWO'#ItO$'eQ07hO,5@rOOQU1G2w1G2wOOQU1G4X1G4XOOQ07b1G/w1G/wO!,iQWO1G/wO$)jQ08SO1G0TOOQU1G2s1G2sO!&dQ7[O1G2sO%[Q^O1G2sO#,^QWO1G2sO$+nQ7[O'#EgOOQ07`,5?{,5?{O$+xQ07hO,5?{OOQU1G.t1G.tO!@rQ07hO1G.tO!@}Q`O1G.tO!AVQ7[O1G.tO$,ZQWO1G0qO$,`QWO'#ChO$,kQWO'#K[O$,sQWO,5=uO$,xQWO'#K[O$,}QWO'#K[O$-]QWO'#IzO$-kQWO,5@uO$-sQrO1G1fOOQ07b1G1h1G1hO9aQWO1G3`O@bQWO1G3`O$-zQWO1G3`O$.PQWO1G3`OOQU1G3`1G3`O!CTQWO1G3OO!&dQ7[O1G2{O$.UQWO1G2{OOQU1G2|1G2|O!&dQ7[O1G2|O$.ZQWO1G2|O$.cQ`O'#GzOOQU1G3O1G3OO!4eQ`O'#IvO!CYQpO1G3ROOQU1G3R1G3ROOQU,5=l,5=lO$.kQ7[O,5=nO9aQWO,5=nO$![QWO,5=pO9UQWO,5=pO!@}Q`O,5=pO!AVQ7[O,5=pO:RQ7[O,5=pO$.yQWO'#KYO$/UQWO,5=qOOQU1G.j1G.jO$/ZQ07hO1G.jO@bQWO1G.jO$/fQWO1G.jO9kQ07hO1G.jO$1kQrO,5@wO$1{QWO,5@wO9ZQWO,5@wO$2WQ^O,5=xO$2_QWO,5=xOOQU1G3b1G3bO`Q^O1G3bOOQU1G3h1G3hOOQU1G3j1G3jO>kQWO1G3lO$2dQ^O1G3nO$6hQ^O'#HmOOQU1G3q1G3qO$6uQWO'#HsO>pQWO'#HuOOQU1G3w1G3wO$6}Q^O1G3wO9kQ07hO1G3}OOQU1G4P1G4POOQ07`'#GY'#GYO9kQ07hO1G4RO9kQ07hO1G4TO$;UQWO,5@XO!*fQ^O,5;[O9ZQWO,5;[O>pQWO,5:UO!*fQ^O,5:UO!@}Q`O,5:UO$;ZQ(CYO,5:UOOQO,5;[,5;[O$;eQ`O'#I^O$;{QWO,5@WOOQ07b1G/o1G/oO$<TQ`O'#IdO$<_QWO,5@fOOQ07`1G0u1G0uO# xQ`O,5:UOOQO'#Ia'#IaO$<gQ`O,5:pOOQ07f,5:p,5:pO#%sQWO1G0YOOQ07b1G0Y1G0YO%[Q^O1G0YOOQ07b1G0p1G0pO>pQWO1G0pO!@}Q`O1G0pO!AVQ7[O1G0pOOQ07`1G5s1G5sO!@rQ07hO1G0]OOQO1G0i1G0iO%[Q^O1G0iO$<nQ07hO1G0iO$<yQ07hO1G0iO!@}Q`O1G0]OCnQ`O1G0]O$=XQ07hO1G0iOOQO1G0]1G0]O$=mQ08SO1G0iPOOO-E<T-E<TPOOO1G.g1G.gOOOO1G/f1G/fO$=wQpO,5<eO$>PQrO1G4cOOQO1G4i1G4iO%[Q^O,5>wO$>ZQWO1G5qO$>cQWO1G6OO$>kQrO1G6PO9ZQWO,5>}O$>uQ08SO1G5|O%[Q^O1G5|O$?VQ07hO1G5|O$?hQWO1G5{O$?hQWO1G5{O9ZQWO1G5{O$?pQWO,5?QO9ZQWO,5?QOOQO,5?Q,5?QO$@UQWO,5?QO$'ZQWO,5?QOOQO-E<d-E<dOOQO1G0`1G0`OOQO1G0b1G0bO!,lQWO1G0bOOQU7+(_7+(_O!&dQ7[O7+(_O%[Q^O7+(_O$@dQWO7+(_O$@oQ7[O7+(_O$@}Q08SO,5=RO$CYQ08SO,5=TO$EeQ08SO,5=RO$GvQ08SO,5=TO$JXQ08SO,59qO$LaQ08SO,5<fO$NlQ08SO,5<hO%!wQ08SO,5<vOOQ07b7+&]7+&]O%%YQ08SO7+&]O%%|Q7[O'#I_O%&WQWO,5@YOOQ07b1G/y1G/yO%&`Q^O'#I`O%&mQWO,5@ZO%&uQrO,5@ZOOQ07b1G0O1G0OO%'PQWO7+&fOOQ07b7+&f7+&fO%'UQ(CYO,5:eO%[Q^O7+&xO%'`Q(CYO,5:]O%'mQ(CYO,5:iO%'wQ(CYO,5:kOOQ07b7+'O7+'OOOQO1G1o1G1oOOQO1G1p1G1pO%(RQtO,5<WO!*fQ^O,5<VOOQO-E<e-E<eOOQ07b7+'V7+'VOOOS7+'b7+'bOOOS1G1y1G1yO%(^QWO1G1yOOQ07b1G1{1G1{O%(cQpO,59kOOOO-E<X-E<XOOQ07b1G/T1G/TO%(jQ08SO7+'hOOQ07b,5?V,5?VO%)^QpO,5?VOOQ07b1G2a1G2aP!&dQ7[O'#IkPOQ07b-E<i-E<iO%)|Q7[O,5?YOOQ07b-E<l-E<lO%*oQ7[O,5?[OOQ07b-E<n-E<nO%*yQpO1G2qOOQ07b1G2Y1G2YO%+QQWO'#IjO%+`QWO,5@mO%+`QWO,5@mO%+hQWO,5@mO%+sQWO,5@mOOQO1G2[1G2[O%,RQ7[O1G2ZO!(SQ7[O1G2ZO%,cQ!LQO'#IlO%,sQWO,5@nO!&dQ7[O,5@nO%,{QpO,5@nOOQ07b1G2_1G2_OOQ07`,5<x,5<xOOQ07`,5<y,5<yO$'ZQWO,5<yOC_QWO,5<yO!@}Q`O,5<xOOQO'#Ga'#GaO%-VQWO,5<zOOQ07`,5<|,5<|O$'ZQWO,5=POOQO,5?X,5?XOOQO-E<k-E<kOOQ07f1G2c1G2cO!4eQ`O,5<xO%-_QWO,5<yO$!VQWO,5<zO!4eQ`O,5<yO!(SQ7[O'#InO%.RQ7[O1G2mO!(SQ7[O'#IpO%.tQ7[O1G2oO%/OQ7[O1G5iO%/YQ7[O1G5iOOQO,5?^,5?^OOQO-E<p-E<pOOQO1G.z1G.zO!7{Q`O,59sO%[Q^O,59sO%/gQWO1G2UO!(SQ7[O1G2]O%/lQ08SO7+'iOOQ07b7+'i7+'iO!$aQ^O7+'iO%0`QWO,5;[OOQ07`,5?`,5?`OOQ07`-E<r-E<rOOQ07b7+%c7+%cO%0eQpO'#KTO#%sQWO7+(_O%0oQrO7+(_O$@gQWO7+(_O%0vQ08QO'#ChO%1ZQ08QO,5<}O%1{QWO,5<}OOQ07`1G5g1G5gOOQU7+$`7+$`O!@rQ07hO7+$`O!@}Q`O7+$`O!$aQ^O7+&]O%2QQWO'#IyO%2iQWO,5@vOOQO1G3a1G3aO9aQWO,5@vO%2iQWO,5@vO%2qQWO,5@vOOQO,5?f,5?fOOQO-E<x-E<xOOQ07b7+'Q7+'QO%2vQWO7+(zO9kQ07hO7+(zO9aQWO7+(zO@bQWO7+(zOOQU7+(j7+(jO%2{Q08QO7+(gO!&dQ7[O7+(gO%3VQpO7+(hOOQU7+(h7+(hO!&dQ7[O7+(hO%3^QWO'#KXO%3iQWO,5=fOOQO,5?b,5?bOOQO-E<t-E<tOOQU7+(m7+(mO%4xQ`O'#HTOOQU1G3Y1G3YO!&dQ7[O1G3YO%[Q^O1G3YO%5PQWO1G3YO%5[Q7[O1G3YO9kQ07hO1G3[O$![QWO1G3[O9UQWO1G3[O!@}Q`O1G3[O!AVQ7[O1G3[O%5jQWO'#IxO%6OQWO,5@tO%6WQ`O,5@tOOQ07`1G3]1G3]OOQU7+$U7+$UO@bQWO7+$UO9kQ07hO7+$UO%6cQWO7+$UO%[Q^O1G6cO%[Q^O1G6dO%6hQ07hO1G6cO%6rQ^O1G3dO%6yQWO1G3dO%7OQ^O1G3dOOQU7+(|7+(|O9kQ07hO7+)WO`Q^O7+)YOOQU'#K_'#K_OOQU'#I{'#I{O%7VQ^O,5>XOOQU,5>X,5>XO%[Q^O'#HnO%7dQWO'#HpOOQU,5>_,5>_O9ZQWO,5>_OOQU,5>a,5>aOOQU7+)c7+)cOOQU7+)i7+)iOOQU7+)m7+)mOOQU7+)o7+)oO%7iQ`O1G5sO%7}Q(CYO1G0vO%8XQWO1G0vOOQO1G/p1G/pO%8dQ(CYO1G/pO>pQWO1G/pO!*fQ^O'#DjOOQO,5>x,5>xOOQO-E<[-E<[OOQO,5?O,5?OOOQO-E<b-E<bO!@}Q`O1G/pOOQO-E<_-E<_OOQ07f1G0[1G0[OOQ07b7+%t7+%tO#%sQWO7+%tOOQ07b7+&[7+&[O>pQWO7+&[O!@}Q`O7+&[OOQO7+%w7+%wO$=mQ08SO7+&TOOQO7+&T7+&TO%[Q^O7+&TO%8nQ07hO7+&TO!@rQ07hO7+%wO!@}Q`O7+%wO%8yQ07hO7+&TO%9XQ08SO7++hO%[Q^O7++hO%9iQWO7++gO%9iQWO7++gOOQO1G4l1G4lO9ZQWO1G4lO%9qQWO1G4lOOQO7+%|7+%|O#%sQWO<<KyO%0oQrO<<KyO%:PQWO<<KyOOQU<<Ky<<KyO!&dQ7[O<<KyO%[Q^O<<KyO%:XQWO<<KyO%:dQ08SO,5?YO%<oQ08SO,5?[O%>zQ08SO1G2ZO%A]Q08SO1G2mO%ChQ08SO1G2oO%EsQ7[O,5>yOOQO-E<]-E<]O%E}QrO,5>zO%[Q^O,5>zOOQO-E<^-E<^O%FXQWO1G5uOOQ07b<<JQ<<JQO%FaQ(CYO1G0qO%HkQ(CYO1G0{O%HrQ(CYO1G0{O%JvQ(CYO1G0{O%J}Q(CYO1G0{O%LrQ(CYO1G0{O%MYQ(CYO1G0{O& mQ(CYO1G0{O& tQ(CYO1G0{O&#rQ(CYO1G0{O&$PQ(CYO1G0{O&%}Q(CYO1G0{O&&bQ08SO<<JdO&'gQ(CYO1G0{O&)]Q(CYO'#JdO&+`Q(CYO1G1aO&+mQ(CYO1G0TO!*fQ^O'#FnOOQO'#KP'#KPOOQO1G1r1G1rO&+wQWO1G1qO&+|Q(CYO,5?TOOOS7+'e7+'eOOOO1G/V1G/VOOQ07b1G4q1G4qO!(SQ7[O7+(]O&,WQWO,5?UO9aQWO,5?UOOQO-E<h-E<hO&,fQWO1G6XO&,fQWO1G6XO&,nQWO1G6XO&,yQ7[O7+'uO&-ZQpO,5?WO&-eQWO,5?WO!&dQ7[O,5?WOOQO-E<j-E<jO&-jQpO1G6YO&-tQWO1G6YOOQ07`1G2e1G2eO$'ZQWO1G2eOOQ07`1G2d1G2dO&-|QWO1G2fO!&dQ7[O1G2fOOQ07`1G2k1G2kO!@}Q`O1G2dOC_QWO1G2eO&.RQWO1G2fO&.ZQWO1G2eO&.}Q7[O,5?YOOQ07b-E<m-E<mO&/pQ7[O,5?[OOQ07b-E<o-E<oO!(SQ7[O7++TOOQ07b1G/_1G/_O&/zQWO1G/_OOQ07b7+'p7+'pO&0PQ7[O7+'wO&0aQ08SO<<KTOOQ07b<<KT<<KTO&1TQWO1G0vO!&dQ7[O'#IsO&1YQWO,5@oO!&dQ7[O1G2iOOQU<<Gz<<GzO!@rQ07hO<<GzO&1bQ08SO<<IwOOQ07b<<Iw<<IwOOQO,5?e,5?eO&2UQWO,5?eO&2ZQWO,5?eOOQO-E<w-E<wO&2iQWO1G6bO&2iQWO1G6bO9aQWO1G6bO@bQWO<<LfOOQU<<Lf<<LfO&2qQWO<<LfO9kQ07hO<<LfOOQU<<LR<<LRO%2{Q08QO<<LROOQU<<LS<<LSO%3VQpO<<LSO&2vQ`O'#IuO&3RQWO,5@sO!*fQ^O,5@sOOQU1G3Q1G3QO&3ZQ^O'#JmOOQO'#Iw'#IwO9kQ07hO'#IwO&3eQ`O,5=oOOQU,5=o,5=oO&3lQ`O'#EcO&4QQWO7+(tO&4VQWO7+(tOOQU7+(t7+(tO!&dQ7[O7+(tO%[Q^O7+(tO&4_QWO7+(tOOQU7+(v7+(vO9kQ07hO7+(vO$![QWO7+(vO9UQWO7+(vO!@}Q`O7+(vO&4jQWO,5?dOOQO-E<v-E<vOOQO'#HW'#HWO&4uQWO1G6`O9kQ07hO<<GpOOQU<<Gp<<GpO@bQWO<<GpO&4}QWO7++}O&5SQWO7+,OO%[Q^O7++}O%[Q^O7+,OOOQU7+)O7+)OO&5XQWO7+)OO&5^Q^O7+)OO&5eQWO7+)OOOQU<<Lr<<LrOOQU<<Lt<<LtOOQU-E<y-E<yOOQU1G3s1G3sO&5jQWO,5>YOOQU,5>[,5>[O&5oQWO1G3yO9ZQWO7+&bO!*fQ^O7+&bOOQO7+%[7+%[O&5tQ(CYO1G6PO>pQWO7+%[OOQ07b<<I`<<I`OOQ07b<<Iv<<IvO>pQWO<<IvOOQO<<Io<<IoO$=mQ08SO<<IoO%[Q^O<<IoOOQO<<Ic<<IcO!@rQ07hO<<IcO&6OQ07hO<<IoO&6ZQ08SO<= SO&6kQWO<= ROOQO7+*W7+*WO9ZQWO7+*WOOQUANAeANAeO&6sQWOANAeO!&dQ7[OANAeO#%sQWOANAeO%0oQrOANAeO%[Q^OANAeO&6{Q08SO7+'uO&9^Q08SO,5?YO&;iQ08SO,5?[O&=tQ08SO7+'wO&@VQrO1G4fO&@aQ(CYO7+&]O&BeQ(CYO,5=RO&DlQ(CYO,5=TO&D|Q(CYO,5=RO&E^Q(CYO,5=TO&EnQ(CYO,59qO&GqQ(CYO,5<fO&ItQ(CYO,5<hO&KwQ(CYO,5<vO&MmQ(CYO7+'hO&MzQ(CYO7+'iO&NXQWO,5<YOOQO7+']7+']O&N^Q7[O<<KwOOQO1G4p1G4pO&NeQWO1G4pO&NpQWO1G4pO' OQWO7++sO' OQWO7++sO!&dQ7[O1G4rO' WQpO1G4rO' bQWO7++tOOQ07`7+(P7+(PO$'ZQWO7+(QO' jQpO7+(QOOQ07`7+(O7+(OO$'ZQWO7+(PO' qQWO7+(QO!&dQ7[O7+(QOC_QWO7+(PO' vQ7[O<<NoOOQ07b7+$y7+$yO'!QQpO,5?_OOQO-E<q-E<qO'![Q08QO7+(TOOQUAN=fAN=fO9aQWO1G5POOQO1G5P1G5PO'!lQWO1G5PO'!qQWO7++|O'!qQWO7++|O9kQ07hOANBQO@bQWOANBQOOQUANBQANBQOOQUANAmANAmOOQUANAnANAnO'!yQWO,5?aOOQO-E<s-E<sO'#UQ(CYO1G6_O'%fQrO'#ChOOQO,5?c,5?cOOQO-E<u-E<uOOQU1G3Z1G3ZO&3ZQ^O,5<zOOQU<<L`<<L`O!&dQ7[O<<L`O&4QQWO<<L`O'%pQWO<<L`O%[Q^O<<L`OOQU<<Lb<<LbO9kQ07hO<<LbO$![QWO<<LbO9UQWO<<LbO'%xQ`O1G5OO'&TQWO7++zOOQUAN=[AN=[O9kQ07hOAN=[OOQU<= i<= iOOQU<= j<= jO'&]QWO<= iO'&bQWO<= jOOQU<<Lj<<LjO'&gQWO<<LjO'&lQ^O<<LjOOQU1G3t1G3tO>pQWO7+)eO'&sQWO<<I|O''OQ(CYO<<I|OOQO<<Hv<<HvOOQ07bAN?bAN?bOOQOAN?ZAN?ZO$=mQ08SOAN?ZOOQOAN>}AN>}O%[Q^OAN?ZOOQO<<Mr<<MrOOQUG27PG27PO!&dQ7[OG27PO#%sQWOG27PO''YQWOG27PO%0oQrOG27PO''bQ(CYO<<JdO''oQ(CYO1G2ZO')eQ(CYO,5?YO'+hQ(CYO,5?[O'-kQ(CYO1G2mO'/nQ(CYO1G2oO'1qQ(CYO<<KTO'2OQ(CYO<<IwOOQO1G1t1G1tO!(SQ7[OANAcOOQO7+*[7+*[O'2]QWO7+*[O'2hQWO<= _O'2pQpO7+*^OOQ07`<<Kl<<KlO$'ZQWO<<KlOOQ07`<<Kk<<KkO'2zQpO<<KlO$'ZQWO<<KkOOQO7+*k7+*kO9aQWO7+*kO'3RQWO<= hOOQUG27lG27lO9kQ07hOG27lO!*fQ^O1G4{O'3ZQWO7++yO&4QQWOANAzOOQUANAzANAzO!&dQ7[OANAzO'3cQWOANAzOOQUANA|ANA|O9kQ07hOANA|O$![QWOANA|OOQO'#HX'#HXOOQO7+*j7+*jOOQUG22vG22vOOQUANETANETOOQUANEUANEUOOQUANBUANBUO'3kQWOANBUOOQU<<MP<<MPO!*fQ^OAN?hOOQOG24uG24uO$=mQ08SOG24uO#%sQWOLD,kOOQULD,kLD,kO!&dQ7[OLD,kO'3pQWOLD,kO'3xQ(CYO7+'uO'5nQ(CYO,5?YO'7qQ(CYO,5?[O'9tQ(CYO7+'wO';jQ7[OG26}OOQO<<Mv<<MvOOQ07`ANAWANAWO$'ZQWOANAWOOQ07`ANAVANAVOOQO<<NV<<NVOOQULD-WLD-WO';zQ(CYO7+*gOOQUG27fG27fO&4QQWOG27fO!&dQ7[OG27fOOQUG27hG27hO9kQ07hOG27hOOQUG27pG27pO'<UQ(CYOG25SOOQOLD*aLD*aOOQU!$(!V!$(!VO#%sQWO!$(!VO!&dQ7[O!$(!VO'<`Q08SOG26}OOQ07`G26rG26rOOQULD-QLD-QO&4QQWOLD-QOOQULD-SLD-SOOQU!)9Eq!)9EqO#%sQWO!)9EqOOQU!$(!l!$(!lOOQU!.K;]!.K;]O'>qQ(CYOG26}O!*fQ^O'#DyO1PQWO'#EWO'@gQrO'#JiO!*fQ^O'#DqO'@nQ^O'#D}O'@uQrO'#ChO'C]QrO'#ChO!*fQ^O'#EPO'CmQ^O,5;VO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O,5;aO!*fQ^O'#IiO'EpQWO,5<eO'ExQ7[O,5;aO'GcQ7[O,5;aO!*fQ^O,5;uO!&dQ7[O'#GgO'ExQ7[O'#GgO!&dQ7[O'#GiO'ExQ7[O'#GiO1SQWO'#DVO1SQWO'#DVO!&dQ7[O'#FzO'ExQ7[O'#FzO!&dQ7[O'#F|O'ExQ7[O'#F|O!&dQ7[O'#G[O'ExQ7[O'#G[O!*fQ^O,5:iO!*fQ^O,5@eO'CmQ^O1G0qO'GjQ(CYO'#ChO!*fQ^O1G1|O!&dQ7[O'#InO'ExQ7[O'#InO!&dQ7[O'#IpO'ExQ7[O'#IpO!&dQ7[O,5<oO'ExQ7[O,5<oO'CmQ^O1G1}O!*fQ^O7+&xO!&dQ7[O1G2ZO'ExQ7[O1G2ZO!&dQ7[O'#InO'ExQ7[O'#InO!&dQ7[O'#IpO'ExQ7[O'#IpO!&dQ7[O1G2]O'ExQ7[O1G2]O'CmQ^O7+'iO'CmQ^O7+&]O!&dQ7[OANAcO'ExQ7[OANAcO'GtQWO'#EkO'GyQWO'#EkO'HRQWO'#FZO'HWQWO'#EuO'H]QWO'#JyO'HhQWO'#JwO'HsQWO,5;VO'HxQ7[O,5<bO'IPQWO'#GTO'IUQWO'#GTO'IZQWO,5<cO'IcQWO,5;VO'IkQ(CYO1G1^O'IrQWO,5<oO'IwQWO,5<oO'I|QWO,5<qO'JRQWO,5<qO'JWQWO1G1}O'J]QWO1G0qO'JbQ7[O<<KwO'JiQ7[O<<KwO7hQ7[O'#FxO9UQWO'#FwOA]QWO'#EjO!*fQ^O,5;rO!3fQWO'#GTO!3fQWO'#GTO!3fQWO'#GVO!3fQWO'#GVO!(SQ7[O7+(]O!(SQ7[O7+(]O%*yQpO1G2qO%*yQpO1G2qO!&dQ7[O,5=VO!&dQ7[O,5=V",stateData:"'Km~O'tOS'uOSSOS'vRQ~OPYOQYORfOX!VO`qOczOdyOlkOnYOokOpkOvkOxYOzYO!PWO!TkO!UkO![XO!fuO!kZO!nYO!oYO!pYO!rvO!twO!wxO!{]O#s!PO$T|O%b}O%d!QO%f!OO%g!OO%h!OO%k!RO%m!SO%p!TO%q!TO%s!UO&P!WO&V!XO&X!YO&Z!ZO&]![O&`!]O&f!^O&l!_O&n!`O&p!aO&r!bO&t!cO'{SO'}TO(QUO(XVO(g[O(tiO~OVtO~P`OPYOQYORfOc!jOd!iOlkOnYOokOpkOvkOxYOzYO!PWO!TkO!UkO![!eO!fuO!kZO!nYO!oYO!pYO!rvO!t!gO!w!hO$T!kO'{!dO'}TO(QUO(XVO(g[O(tiO~O`!vOo!nO!P!oO!_!xO!`!uO!a!uO!{:dO#P!pO#Q!pO#R!wO#S!pO#T!pO#W!yO#X!yO'|!lO'}TO(QUO([!mO(g!sO~O'v!zO~OP[XZ[X`[Xn[X|[X}[X!P[X!Y[X!h[X!i[X!k[X!o[X#[[X#geX#j[X#k[X#l[X#m[X#n[X#o[X#p[X#q[X#r[X#t[X#v[X#x[X#y[X$O[X'r[X(X[X(h[X(o[X(p[X~O!d$|X~P(qO^!|O'}#OO(O!|O(P#OO~O^#PO(P#OO(Q#OO(R#PO~Ot#RO!R#SO(Y#SO(Z#UO~OPYOQYORfOc!jOd!iOlkOnYOokOpkOvkOxYOzYO!PWO!TkO!UkO![!eO!fuO!kZO!nYO!oYO!pYO!rvO!t!gO!w!hO$T!kO'{:hO'}TO(QUO(XVO(g[O(tiO~O!X#YO!Y#VO!V(_P!V(lP~P+}O!Z#bO~P`OPYOQYORfOc!jOd!iOnYOokOpkOvkOxYOzYO!PWO!TkO!UkO![!eO!fuO!kZO!nYO!oYO!pYO!rvO!t!gO!w!hO$T!kO'}TO(QUO(XVO(g[O(tiO~Ol#lO!X#hO!{]O#e#kO#f#hO'{:iO!j(iP~P.iO!k#nO'{#mO~O!w#rO!{]O%b#sO~O#g#tO~O!d#uO#g#tO~OP$]OZ$dOn$QO|#yO}#zO!P#{O!Y$aO!h$SO!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO#n$RO#o$SO#p$SO#q$cO#r$SO#t$TO#v$VO#x$XO#y$YO(XVO(h$ZO(o#|O(p#}O~O`(]X'r(]X'p(]X!j(]X!V(]X![(]X%c(]X!d(]X~P1qO#[$eO$O$eOP(^XZ(^Xn(^X|(^X}(^X!P(^X!Y(^X!h(^X!k(^X!o(^X#j(^X#k(^X#l(^X#m(^X#n(^X#o(^X#p(^X#q(^X#r(^X#t(^X#v(^X#x(^X#y(^X(X(^X(h(^X(o(^X(p(^X![(^X%c(^X~O`(^X!i(^X'r(^X'p(^X!V(^X!j(^Xr(^X!d(^X~P4XO#[$eO~O$Y$gO$[$fO$c$lO~ORfO![$mO$f$nO$h$pO~Og%VOl%WOn$tOo$sOp$sOv%XOx%YOz%ZO!P${O![$|O!f%`O!k$xO#f%aO$T%^O$o%[O$q%]O$t%_O'{$rO'}TO(QUO(X$uO(o$}O(p%POf(UP~O!k%bO~O!P%eO![%fO'{%dO~O!d%jO~O`%kO'r%kO~O'|!lO~P%[O%h%rO~P%[Og%VO!k%bO'{%dO'|!lO~Od%yO!k%bO'{%dO~O#r$SO~O|&OO![%{O!k%}O%d&RO'{%dO'|!lO'}TO(QUO_(}P~O!w#rO~O%m&TO!P(yX![(yX'{(yX~O'{&UO~O!t&ZO#s!PO%d!QO%f!OO%g!OO%h!OO%k!RO%m!SO%p!TO%q!TO~Oc&`Od&_O!w&]O%b&^O%u&[O~P;xOc&cOdyO![&bO!t&ZO!wxO!{]O#s!PO%b}O%f!OO%g!OO%h!OO%k!RO%m!SO%p!TO%q!TO%s!UO~Oa&fO#[&iO%d&dO'|!lO~P<}O!k&jO!t&nO~O!k#nO~O![XO~O`%kO'q&vO'r%kO~O`%kO'q&yO'r%kO~O`%kO'q&{O'r%kO~O'p[X!V[Xr[X!j[X&T[X![[X%c[X!d[X~P(qO!_'YO!`'RO!a'RO'|!lO'}TO(QUO~Oo'PO!P'OO!X'SO([&}O!Z(`P!Z(nP~P@UOj']O!['ZO'{%dO~Od'bO!k%bO'{%dO~O|&OO!k%}O~Oo!nO!P!oO!{:dO#P!pO#Q!pO#S!pO#T!pO'|!lO'}TO(QUO([!mO(g!sO~O!_'hO!`'gO!a'gO#R!pO#W'iO#X'iO~PApO`%kOg%VO!d#uO!k%bO'r%kO(h'kO~O!o'oO#['mO~PCOOo!nO!P!oO'}TO(QUO([!mO(g!sO~O![XOo(eX!P(eX!_(eX!`(eX!a(eX!{(eX#P(eX#Q(eX#R(eX#S(eX#T(eX#W(eX#X(eX'|(eX'}(eX(Q(eX([(eX(g(eX~O!`'gO!a'gO'|!lO~PCnO'w'sO'x'sO'y'uO~O^!|O'}'wO(O!|O(P'wO~O^#PO(P'wO(Q'wO(R#PO~Ot#RO!R#SO(Y#SO(Z'{O~O!X'}O!V'PX!V'VX!Y'PX!Y'VX~P+}O!Y(PO!V(_X~OP$]OZ$dOn$QO|#yO}#zO!P#{O!Y(PO!h$SO!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO#n$RO#o$SO#p$SO#q$cO#r$SO#t$TO#v$VO#x$XO#y$YO(XVO(h$ZO(o#|O(p#}O~O!V(_X~PGbO!V(UO~O!V(kX!Y(kX!d(kX!j(kX(h(kX~O#[(kX#g#`X!Z(kX~PIhO#[(VO!V(mX!Y(mX~O!Y(WO!V(lX~O!V(ZO~O#[$eO~PIhO!Z([O~P`O|#yO}#zO!P#{O!i#wO!k#xO(XVOP!maZ!man!ma!Y!ma!h!ma!o!ma#j!ma#k!ma#l!ma#m!ma#n!ma#o!ma#p!ma#q!ma#r!ma#t!ma#v!ma#x!ma#y!ma(h!ma(o!ma(p!ma~O`!ma'r!ma'p!ma!V!ma!j!mar!ma![!ma%c!ma!d!ma~PKOO!j(]O~O!d#uO#[(^O(h'kO!Y(jX`(jX'r(jX~O!j(jX~PMnO!P%eO![%fO!{]O#e(cO#f(bO'{%dO~O!Y(dO!j(iX~O!j(fO~O!P%eO![%fO#f(bO'{%dO~OP(^XZ(^Xn(^X|(^X}(^X!P(^X!Y(^X!h(^X!i(^X!k(^X!o(^X#j(^X#k(^X#l(^X#m(^X#n(^X#o(^X#p(^X#q(^X#r(^X#t(^X#v(^X#x(^X#y(^X(X(^X(h(^X(o(^X(p(^X~O!d#uO!j(^X~P! [O|(gO}(hO!i#wO!k#xO!{!za!P!za~O!w!za%b!za![!za#e!za#f!za'{!za~P!#`O!w(lO~OPYOQYORfOc!jOd!iOlkOnYOokOpkOvkOxYOzYO!PWO!TkO!UkO![XO!fuO!kZO!nYO!oYO!pYO!rvO!t!gO!w!hO$T!kO'{!dO'}TO(QUO(XVO(g[O(tiO~Og%VOl%WOn$tOo$sOp$sOv%XOx%YOz;QO!P${O![$|O!f<`O!k$xO#f;WO$T%^O$o;SO$q;UO$t%_O'{(pO'}TO(QUO(X$uO(o$}O(p%PO~O#g(rO~Og%VOl%WOn$tOo$sOp$sOv%XOx%YOz%ZO!P${O![$|O!f%`O!k$xO#f%aO$T%^O$o%[O$q%]O$t%_O'{(pO'}TO(QUO(X$uO(o$}O(p%PO~Of(bP~P!(SO!X(vO!j(cP~P%[O([(xO(g[O~O!P(zO!k#xO([(xO(g[O~OP:cOQ:cORfOc<[Od!iOlkOn:cOokOpkOvkOx:cOz:cO!PWO!TkO!UkO![!eO!f:fO!kZO!n:cO!o:cO!p:cO!r:gO!t:jO!w!hO$T!kO'{)YO'}TO(QUO(XVO(g[O(t<YO~O})]O!k#xO~O!Y$aO`$ma'r$ma'p$ma!j$ma!V$ma![$ma%c$ma!d$ma~O#s)aO~P!&dO|)dO!d)cO![$ZX$W$ZX$Y$ZX$[$ZX$c$ZX~O!d)cO![(qX$W(qX$Y(qX$[(qX$c(qX~O|)dO~P!.OO|)dO![(qX$W(qX$Y(qX$[(qX$c(qX~O![)fO$W)jO$Y)eO$[)eO$c)kO~O!X)nO~P!*fO$Y$gO$[$fO$c)rO~Oj$uX|$uX!P$uX!i$uX(o$uX(p$uX~OfiXf$uXjiX!YiX#[iX~P!/tOo)tO~Ot)uO(Y)vO(Z)xO~Oj*RO|)zO!P){O(o$}O(p%PO~Of)yO~P!0}Of*SO~Og%VOl%WOn$tOo$sOp$sOv%XOx%YOz;QO!P${O![$|O!f<`O!k$xO#f;WO$T%^O$o;SO$q;UO$t%_O'}TO(QUO(X$uO(o$}O(p%PO~O!X*WO'{*TO!j(uP~P!1lO#g*YO~O!k*ZO~O!X*`O'{*]O!V(vP~P!1lOn*lO!P*dO!_*jO!`*cO!a*cO!k*ZO#W*kO%Y*fO'|!lO([!mO~O!Z*iO~P!3xO!i#wOj(WX|(WX!P(WX(o(WX(p(WX!Y(WX#[(WX~Of(WX#|(WX~P!4qOj*qO#[*pOf(VX!Y(VX~O!Y*rOf(UX~O'{&UOf(UP~O!k*yO~O'{(pO~Ol*}O!P%eO!X#hO![%fO!{]O#e#kO#f#hO'{%dO!j(iP~O!d#uO#g+OO~O!P%eO!X+QO!Y(WO![%fO'{%dO!V(lP~Oo'VO!P+SO!X+RO'}TO(QUO([(xO~O!Z(nP~P!7lO!Y+TO`(zX'r(zX~OP$]OZ$dOn$QO|#yO}#zO!P#{O!h$SO!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO#n$RO#o$SO#p$SO#q$cO#r$SO#t$TO#v$VO#x$XO#y$YO(XVO(h$ZO(o#|O(p#}O~O`!ea!Y!ea'r!ea'p!ea!V!ea!j!ear!ea![!ea%c!ea!d!ea~P!8dO|#yO}#zO!P#{O!i#wO!k#xO(XVOP!qaZ!qan!qa!Y!qa!h!qa!o!qa#j!qa#k!qa#l!qa#m!qa#n!qa#o!qa#p!qa#q!qa#r!qa#t!qa#v!qa#x!qa#y!qa(h!qa(o!qa(p!qa~O`!qa'r!qa'p!qa!V!qa!j!qar!qa![!qa%c!qa!d!qa~P!:}O|#yO}#zO!P#{O!i#wO!k#xO(XVOP!saZ!san!sa!Y!sa!h!sa!o!sa#j!sa#k!sa#l!sa#m!sa#n!sa#o!sa#p!sa#q!sa#r!sa#t!sa#v!sa#x!sa#y!sa(h!sa(o!sa(p!sa~O`!sa'r!sa'p!sa!V!sa!j!sar!sa![!sa%c!sa!d!sa~P!=hOg%VOj+^O!['ZO%c+]O~O!d+`O`(TX![(TX'r(TX!Y(TX~O`%kO![XO'r%kO~Og%VO!k%bO~Og%VO!k%bO'{%dO~O!d#uO#g(rO~Oa+kO%d+lO'{+hO'}TO(QUO!Z)OP~O!Y+mO_(}X~OZ+qO~O_+rO~O![%{O'{%dO'|!lO_(}P~Og%VO#[+wO~Og%VOj+zO![$|O~O![+|O~O|,OO![XO~O%h%rO~O!w,TO~Od,YO~Oa,ZO'{#mO'}TO(QUO!Z(|P~Od%yO~O%d!QO'{&UO~P<}OZ,`O_,_O~OPYOQYORfOczOdyOlkOnYOokOpkOvkOxYOzYO!PWO!TkO!UkO!fuO!kZO!nYO!oYO!pYO!rvO!wxO!{]O%b}O'}TO(QUO(XVO(g[O(tiO~O![!eO!t!gO$T!kO'{!dO~P!DkO_,_O`%kO'r%kO~OPYOQYORfOc!jOd!iOlkOnYOokOpkOvkOxYOzYO!PWO!TkO!UkO![!eO!fuO!kZO!nYO!oYO!pYO!rvO!w!hO$T!kO'{!dO'}TO(QUO(XVO(g[O(tiO~O`,eO!twO#s!OO%f!OO%g!OO%h!OO~P!GTO!k&jO~O&V,kO~O![,mO~O&h,oO&j,pOP&eaQ&eaR&eaX&ea`&eac&ead&eal&ean&eao&eap&eav&eax&eaz&ea!P&ea!T&ea!U&ea![&ea!f&ea!k&ea!n&ea!o&ea!p&ea!r&ea!t&ea!w&ea!{&ea#s&ea$T&ea%b&ea%d&ea%f&ea%g&ea%h&ea%k&ea%m&ea%p&ea%q&ea%s&ea&P&ea&V&ea&X&ea&Z&ea&]&ea&`&ea&f&ea&l&ea&n&ea&p&ea&r&ea&t&ea'p&ea'{&ea'}&ea(Q&ea(X&ea(g&ea(t&ea!Z&ea&^&eaa&ea&c&ea~O'{,uO~Og!bX!Y!OX!Y!bX!Z!OX!Z!bX!d!OX!d!bX!k!bX#[!OX~O!d,zO#[,yOg(aX!Y#dX!Y(aX!Z#dX!Z(aX!d(aX!k(aX~Og%VO!d,|O!k%bO!Y!^X!Z!^X~Oo!nO!P!oO'}TO(QUO([!mO~OP:cOQ:cORfOc<[Od!iOlkOn:cOokOpkOvkOx:cOz:cO!PWO!TkO!UkO![!eO!f:fO!kZO!n:cO!o:cO!p:cO!r:gO!t:jO!w!hO$T!kO'}TO(QUO(XVO(g[O(t<YO~O'{;]O~P#!ZO!Y-QO!Z(`X~O!Z-SO~O!d,zO#[,yO!Y#dX!Z#dX~O!Y-TO!Z(nX~O!Z-VO~O!`-WO!a-WO'|!lO~P# xO!Z-ZO~P'_Oj-^O!['ZO~O!V-cO~Oo!za!_!za!`!za!a!za#P!za#Q!za#R!za#S!za#T!za#W!za#X!za'|!za'}!za(Q!za([!za(g!za~P!#`O!o-hO#[-fO~PCOO!`-jO!a-jO'|!lO~PCnO`%kO#[-fO'r%kO~O`%kO!d#uO#[-fO'r%kO~O`%kO!d#uO!o-hO#[-fO'r%kO(h'kO~O'w'sO'x'sO'y-oO~Or-pO~O!V'Pa!Y'Pa~P!8dO!X-tO!V'PX!Y'PX~P%[O!Y(PO!V(_a~O!V(_a~PGbO!Y(WO!V(la~O!P%eO!X-xO![%fO'{%dO!V'VX!Y'VX~O#[-zO!Y(ja!j(ja`(ja'r(ja~O!d#uO~P#*aO!Y(dO!j(ia~O!P%eO![%fO#f.OO'{%dO~Ol.TO!P%eO!X.QO![%fO!{]O#e.SO#f.QO'{%dO!Y'YX!j'YX~O}.XO!k#xO~Og%VOj.[O!['ZO%c.ZO~O`#_i!Y#_i'r#_i'p#_i!V#_i!j#_ir#_i![#_i%c#_i!d#_i~P!8dOj<fO|)zO!P){O(o$}O(p%PO~O#g#Za`#Za#[#Za'r#Za!Y#Za!j#Za![#Za!V#Za~P#-]O#g(WXP(WXZ(WX`(WXn(WX}(WX!h(WX!k(WX!o(WX#j(WX#k(WX#l(WX#m(WX#n(WX#o(WX#p(WX#q(WX#r(WX#t(WX#v(WX#x(WX#y(WX'r(WX(X(WX(h(WX!j(WX!V(WX'p(WXr(WX![(WX%c(WX!d(WX~P!4qO!Y.iOf(bX~P!0}Of.kO~O!Y.lO!j(cX~P!8dO!j.oO~O!V.qO~OP$]O|#yO}#zO!P#{O!i#wO!k#xO!o$]O(XVOZ#ii`#iin#ii!Y#ii!h#ii#k#ii#l#ii#m#ii#n#ii#o#ii#p#ii#q#ii#r#ii#t#ii#v#ii#x#ii#y#ii'r#ii(h#ii(o#ii(p#ii'p#ii!V#ii!j#iir#ii![#ii%c#ii!d#ii~O#j#ii~P#1XO#j$OO~P#1XOP$]O|#yO}#zO!P#{O!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO(XVOZ#ii`#ii!Y#ii!h#ii#n#ii#o#ii#p#ii#q#ii#r#ii#t#ii#v#ii#x#ii#y#ii'r#ii(h#ii(o#ii(p#ii'p#ii!V#ii!j#iir#ii![#ii%c#ii!d#ii~On#ii~P#3yOn$QO~P#3yOP$]On$QO|#yO}#zO!P#{O!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO#n$RO(XVO`#ii!Y#ii#t#ii#v#ii#x#ii#y#ii'r#ii(h#ii(o#ii(p#ii'p#ii!V#ii!j#iir#ii![#ii%c#ii!d#ii~OZ#ii!h#ii#o#ii#p#ii#q#ii#r#ii~P#6kOZ$dO!h$SO#o$SO#p$SO#q$cO#r$SO~P#6kOP$]OZ$dOn$QO|#yO}#zO!P#{O!h$SO!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO#n$RO#o$SO#p$SO#q$cO#r$SO#t$TO(XVO(p#}O`#ii!Y#ii#x#ii#y#ii'r#ii(h#ii(o#ii'p#ii!V#ii!j#iir#ii![#ii%c#ii!d#ii~O#v$VO~P#9lO#v#ii~P#9lOP$]OZ$dOn$QO|#yO}#zO!P#{O!h$SO!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO#n$RO#o$SO#p$SO#q$cO#r$SO#t$TO(XVO`#ii!Y#ii#x#ii#y#ii'r#ii(h#ii'p#ii!V#ii!j#iir#ii![#ii%c#ii!d#ii~O#v#ii(o#ii(p#ii~P#<^O#v$VO(o#|O(p#}O~P#<^OP$]OZ$dOn$QO|#yO}#zO!P#{O!h$SO!i#wO!k#xO!o$]O#j$OO#k$PO#l$PO#m$PO#n$RO#o$SO#p$SO#q$cO#r$SO#t$TO#v$VO#x$XO(XVO(o#|O(p#}O~O`#ii!Y#ii#y#ii'r#ii(h#ii'p#ii!V#ii!j#iir#ii![#ii%c#ii!d#ii~P#?UOP[XZ[Xn[X|[X}[X!P[X!h[X!i[X!k[X!o[X#[[X#geX#j[X#k[X#l[X#m[X#n[X#o[X#p[X#q[X#r[X#t[X#v[X#x[X#y[X$O[X(X[X(h[X(o[X(p[X!Y[X!Z[X~O#|[X~P#AoOP$]OZ:zOn:nO|#yO}#zO!P#{O!h:pO!i#wO!k#xO!o$]O#j:lO#k:mO#l:mO#m:mO#n:oO#o:pO#p:pO#q:yO#r:pO#t:qO#v:sO#x:uO#y:vO(XVO(h$ZO(o#|O(p#}O~O#|.sO~P#C|O#[:{O$O:{O#|(^X!Z(^X~P! [O`']a!Y']a'r']a'p']a!j']a!V']ar']a![']a%c']a!d']a~P!8dOP#iiZ#ii`#iin#ii}#ii!Y#ii!h#ii!i#ii!k#ii!o#ii#j#ii#k#ii#l#ii#m#ii#n#ii#o#ii#p#ii#q#ii#r#ii#t#ii#v#ii#x#ii#y#ii'r#ii(X#ii(h#ii'p#ii!V#ii!j#iir#ii![#ii%c#ii!d#ii~P#-]O`#}i!Y#}i'r#}i'p#}i!V#}i!j#}ir#}i![#}i%c#}i!d#}i~P!8dO$Y.xO$[.xO~O$Y.yO$[.yO~O!d)cO#[.zO![$`X$W$`X$Y$`X$[$`X$c$`X~O!X.{O~O![)fO$W.}O$Y)eO$[)eO$c/OO~O!Y:wO!Z(]X~P#C|O!Z/PO~O!d)cO$c(qX~O$c/RO~Ot)uO(Y)vO(Z/UO~O!V/YO~P!&dO(o$}Oj%Za|%Za!P%Za(p%Za!Y%Za#[%Za~Of%Za#|%Za~P#L^O(p%POj%]a|%]a!P%]a(o%]a!Y%]a#[%]a~Of%]a#|%]a~P#MPO!YeX!deX!jeX!j$uX(heX~P!/tO!j/bO~P#-]O!Y/cO!d#uO(h'kO!j(uX~O!j/hO~O!X*WO'{%dO!j(uP~O#g/jO~O!V$uX!Y$uX!d$|X~P!/tO!Y/kO!V(vX~P#-]O!d/mO~O!V/oO~Og%VOn/sO!d#uO!k%bO(h'kO~O'{/uO~O!d+`O~O`%kO!Y/yO'r%kO~O!Z/{O~P!3xO!`/|O!a/|O'|!lO([!mO~O!P0OO([!mO~O#W0PO~Of%Za!Y%Za#[%Za#|%Za~P!0}Of%]a!Y%]a#[%]a#|%]a~P!0}O'{&UOf'fX!Y'fX~O!Y*rOf(Ua~Of0YO~O|0ZO}0ZO!P0[Ojya(oya(pya!Yya#[ya~Ofya#|ya~P$$jO|)zO!P){Oj$na(o$na(p$na!Y$na#[$na~Of$na#|$na~P$%`O|)zO!P){Oj$pa(o$pa(p$pa!Y$pa#[$pa~Of$pa#|$pa~P$&RO#g0^O~Of%Oa!Y%Oa#[%Oa#|%Oa~P!0}O!d#uO~O#g0aO~O!Y+TO`(za'r(za~O|#yO}#zO!P#{O!i#wO!k#xO(XVOP!qiZ!qin!qi!Y!qi!h!qi!o!qi#j!qi#k!qi#l!qi#m!qi#n!qi#o!qi#p!qi#q!qi#r!qi#t!qi#v!qi#x!qi#y!qi(h!qi(o!qi(p!qi~O`!qi'r!qi'p!qi!V!qi!j!qir!qi![!qi%c!qi!d!qi~P$'pOg%VOn$tOo$sOp$sOv%XOx%YOz;QO!P${O![$|O!f<`O!k$xO#f;WO$T%^O$o;SO$q;UO$t%_O'}TO(QUO(X$uO(o$}O(p%PO~Ol0kO'{0jO~P$*ZO!d+`O`(Ta![(Ta'r(Ta!Y(Ta~O#g0qO~OZ[X!YeX!ZeX~O!Y0rO!Z)OX~O!Z0tO~OZ0uO~Oa0wO'{+hO'}TO(QUO~O![%{O'{%dO_'nX!Y'nX~O!Y+mO_(}a~O!j0zO~P!8dOZ0}O~O_1OO~O#[1RO~Oj1UO![$|O~O([(xO!Z({P~Og%VOj1_O![1[O%c1^O~OZ1iO!Y1gO!Z(|X~O!Z1jO~O_1lO`%kO'r%kO~O'{#mO'}TO(QUO~O#[$eO$O$eOP(^XZ(^Xn(^X|(^X}(^X!P(^X!Y(^X!h(^X!k(^X!o(^X#j(^X#k(^X#l(^X#m(^X#n(^X#o(^X#p(^X#q(^X#t(^X#v(^X#x(^X#y(^X(X(^X(h(^X(o(^X(p(^X~O#r1oO&T1pO`(^X!i(^X~P$/qO#[$eO#r1oO&T1pO~O`1rO~P%[O`1tO~O&^1wOP&[iQ&[iR&[iX&[i`&[ic&[id&[il&[in&[io&[ip&[iv&[ix&[iz&[i!P&[i!T&[i!U&[i![&[i!f&[i!k&[i!n&[i!o&[i!p&[i!r&[i!t&[i!w&[i!{&[i#s&[i$T&[i%b&[i%d&[i%f&[i%g&[i%h&[i%k&[i%m&[i%p&[i%q&[i%s&[i&P&[i&V&[i&X&[i&Z&[i&]&[i&`&[i&f&[i&l&[i&n&[i&p&[i&r&[i&t&[i'p&[i'{&[i'}&[i(Q&[i(X&[i(g&[i(t&[i!Z&[ia&[i&c&[i~Oa1}O!Z1{O&c1|O~P`O![XO!k2PO~O&j,pOP&eiQ&eiR&eiX&ei`&eic&eid&eil&ein&eio&eip&eiv&eix&eiz&ei!P&ei!T&ei!U&ei![&ei!f&ei!k&ei!n&ei!o&ei!p&ei!r&ei!t&ei!w&ei!{&ei#s&ei$T&ei%b&ei%d&ei%f&ei%g&ei%h&ei%k&ei%m&ei%p&ei%q&ei%s&ei&P&ei&V&ei&X&ei&Z&ei&]&ei&`&ei&f&ei&l&ei&n&ei&p&ei&r&ei&t&ei'p&ei'{&ei'}&ei(Q&ei(X&ei(g&ei(t&ei!Z&ei&^&eia&ei&c&ei~O!V2VO~O!Y!^a!Z!^a~P#C|Oo!nO!P!oO!X2]O([!mO!Y'QX!Z'QX~P@UO!Y-QO!Z(`a~O!Y'WX!Z'WX~P!7lO!Y-TO!Z(na~O!Z2dO~P'_O`%kO#[2mO'r%kO~O`%kO!d#uO#[2mO'r%kO~O`%kO!d#uO!o2qO#[2mO'r%kO(h'kO~O`%kO'r%kO~P!8dO!Y$aOr$ma~O!V'Pi!Y'Pi~P!8dO!Y(PO!V(_i~O!Y(WO!V(li~O!V(mi!Y(mi~P!8dO!Y(ji!j(ji`(ji'r(ji~P!8dO#[2sO!Y(ji!j(ji`(ji'r(ji~O!Y(dO!j(ii~O!P%eO![%fO!{]O#e2xO#f2wO'{%dO~O!P%eO![%fO#f2wO'{%dO~Oj3PO!['ZO%c3OO~Og%VOj3PO!['ZO%c3OO~O#g%ZaP%ZaZ%Za`%Zan%Za}%Za!h%Za!i%Za!k%Za!o%Za#j%Za#k%Za#l%Za#m%Za#n%Za#o%Za#p%Za#q%Za#r%Za#t%Za#v%Za#x%Za#y%Za'r%Za(X%Za(h%Za!j%Za!V%Za'p%Zar%Za![%Za%c%Za!d%Za~P#L^O#g%]aP%]aZ%]a`%]an%]a}%]a!h%]a!i%]a!k%]a!o%]a#j%]a#k%]a#l%]a#m%]a#n%]a#o%]a#p%]a#q%]a#r%]a#t%]a#v%]a#x%]a#y%]a'r%]a(X%]a(h%]a!j%]a!V%]a'p%]ar%]a![%]a%c%]a!d%]a~P#MPO#g%ZaP%ZaZ%Za`%Zan%Za}%Za!Y%Za!h%Za!i%Za!k%Za!o%Za#j%Za#k%Za#l%Za#m%Za#n%Za#o%Za#p%Za#q%Za#r%Za#t%Za#v%Za#x%Za#y%Za'r%Za(X%Za(h%Za!j%Za!V%Za'p%Za#[%Zar%Za![%Za%c%Za!d%Za~P#-]O#g%]aP%]aZ%]a`%]an%]a}%]a!Y%]a!h%]a!i%]a!k%]a!o%]a#j%]a#k%]a#l%]a#m%]a#n%]a#o%]a#p%]a#q%]a#r%]a#t%]a#v%]a#x%]a#y%]a'r%]a(X%]a(h%]a!j%]a!V%]a'p%]a#[%]ar%]a![%]a%c%]a!d%]a~P#-]O#gyaPyaZya`yanya!hya!iya!kya!oya#jya#kya#lya#mya#nya#oya#pya#qya#rya#tya#vya#xya#yya'rya(Xya(hya!jya!Vya'pyarya![ya%cya!dya~P$$jO#g$naP$naZ$na`$nan$na}$na!h$na!i$na!k$na!o$na#j$na#k$na#l$na#m$na#n$na#o$na#p$na#q$na#r$na#t$na#v$na#x$na#y$na'r$na(X$na(h$na!j$na!V$na'p$nar$na![$na%c$na!d$na~P$%`O#g$paP$paZ$pa`$pan$pa}$pa!h$pa!i$pa!k$pa!o$pa#j$pa#k$pa#l$pa#m$pa#n$pa#o$pa#p$pa#q$pa#r$pa#t$pa#v$pa#x$pa#y$pa'r$pa(X$pa(h$pa!j$pa!V$pa'p$par$pa![$pa%c$pa!d$pa~P$&RO#g%OaP%OaZ%Oa`%Oan%Oa}%Oa!Y%Oa!h%Oa!i%Oa!k%Oa!o%Oa#j%Oa#k%Oa#l%Oa#m%Oa#n%Oa#o%Oa#p%Oa#q%Oa#r%Oa#t%Oa#v%Oa#x%Oa#y%Oa'r%Oa(X%Oa(h%Oa!j%Oa!V%Oa'p%Oa#[%Oar%Oa![%Oa%c%Oa!d%Oa~P#-]O`#_q!Y#_q'r#_q'p#_q!V#_q!j#_qr#_q![#_q%c#_q!d#_q~P!8dOf'RX!Y'RX~P!(SO!Y.iOf(ba~O!X3ZO!Y'SX!j'SX~P%[O!Y.lO!j(ca~O!Y.lO!j(ca~P!8dO!V3^O~O#|!ma!Z!ma~PKOO#|!ea!Y!ea!Z!ea~P#C|O#|!qa!Z!qa~P!:}O#|!sa!Z!sa~P!=hORfO![3pO$a3qO~O!Z3uO~Or3vO~P#-]O`$jq!Y$jq'r$jq'p$jq!V$jq!j$jqr$jq![$jq%c$jq!d$jq~P!8dO!V3wO~P#-]O|)zO!P){O(p%POj'ba(o'ba!Y'ba#['ba~Of'ba#|'ba~P%)eO|)zO!P){Oj'da(o'da(p'da!Y'da#['da~Of'da#|'da~P%*WO(h$ZO~P#-]O!X3zO'{%dO!Y'^X!j'^X~O!Y/cO!j(ua~O!Y/cO!d#uO!j(ua~O!Y/cO!d#uO(h'kO!j(ua~Of$wi!Y$wi#[$wi#|$wi~P!0}O!X4SO'{*]O!V'`X!Y'`X~P!1lO!Y/kO!V(va~O!Y/kO!V(va~P#-]O!d#uO#r4[O~On4_O!d#uO(h'kO~O(o$}Oj%Zi|%Zi!P%Zi(p%Zi!Y%Zi#[%Zi~Of%Zi#|%Zi~P%-jO(p%POj%]i|%]i!P%]i(o%]i!Y%]i#[%]i~Of%]i#|%]i~P%.]Of(Vi!Y(Vi~P!0}O#[4fOf(Vi!Y(Vi~P!0}O!j4iO~O`$kq!Y$kq'r$kq'p$kq!V$kq!j$kqr$kq![$kq%c$kq!d$kq~P!8dO!V4mO~O!Y4nO![(wX~P#-]O!i#wO~P4XO`$uX![$uX%W[X'r$uX!Y$uX~P!/tO%W4pO`kXjkX|kX!PkX![kX'rkX(okX(pkX!YkX~O%W4pO~Oa4vO%d4wO'{+hO'}TO(QUO!Y'mX!Z'mX~O!Y0rO!Z)Oa~OZ4{O~O_4|O~O`%kO'r%kO~P#-]O![$|O~P#-]O!Y5UO#[5WO!Z({X~O!Z5XO~Oo!nO!P5YO!_!xO!`!uO!a!uO!{:dO#P!pO#Q!pO#R!pO#S!pO#T!pO#W5_O#X!yO'|!lO'}TO(QUO([!mO(g!sO~O!Z5^O~P%3nOj5dO![1[O%c5cO~Og%VOj5dO![1[O%c5cO~Oa5kO'{#mO'}TO(QUO!Y'lX!Z'lX~O!Y1gO!Z(|a~O'}TO(QUO([5mO~O_5qO~O#r5tO&T5uO~PMnO!j5vO~P%[O`5xO~O`5xO~P%[Oa1}O!Z5}O&c1|O~P`O!d6PO~O!d6ROg(ai!Y(ai!Z(ai!d(ai!k(ai~O!Y#di!Z#di~P#C|O#[6SO!Y#di!Z#di~O!Y!^i!Z!^i~P#C|O`%kO#[6]O'r%kO~O`%kO!d#uO#[6]O'r%kO~O!Y(jq!j(jq`(jq'r(jq~P!8dO!Y(dO!j(iq~O!P%eO![%fO#f6dO'{%dO~O!['ZO%c6gO~Oj6jO!['ZO%c6gO~O#g'baP'baZ'ba`'ban'ba}'ba!h'ba!i'ba!k'ba!o'ba#j'ba#k'ba#l'ba#m'ba#n'ba#o'ba#p'ba#q'ba#r'ba#t'ba#v'ba#x'ba#y'ba'r'ba(X'ba(h'ba!j'ba!V'ba'p'bar'ba!['ba%c'ba!d'ba~P%)eO#g'daP'daZ'da`'dan'da}'da!h'da!i'da!k'da!o'da#j'da#k'da#l'da#m'da#n'da#o'da#p'da#q'da#r'da#t'da#v'da#x'da#y'da'r'da(X'da(h'da!j'da!V'da'p'dar'da!['da%c'da!d'da~P%*WO#g$wiP$wiZ$wi`$win$wi}$wi!Y$wi!h$wi!i$wi!k$wi!o$wi#j$wi#k$wi#l$wi#m$wi#n$wi#o$wi#p$wi#q$wi#r$wi#t$wi#v$wi#x$wi#y$wi'r$wi(X$wi(h$wi!j$wi!V$wi'p$wi#[$wir$wi![$wi%c$wi!d$wi~P#-]O#g%ZiP%ZiZ%Zi`%Zin%Zi}%Zi!h%Zi!i%Zi!k%Zi!o%Zi#j%Zi#k%Zi#l%Zi#m%Zi#n%Zi#o%Zi#p%Zi#q%Zi#r%Zi#t%Zi#v%Zi#x%Zi#y%Zi'r%Zi(X%Zi(h%Zi!j%Zi!V%Zi'p%Zir%Zi![%Zi%c%Zi!d%Zi~P%-jO#g%]iP%]iZ%]i`%]in%]i}%]i!h%]i!i%]i!k%]i!o%]i#j%]i#k%]i#l%]i#m%]i#n%]i#o%]i#p%]i#q%]i#r%]i#t%]i#v%]i#x%]i#y%]i'r%]i(X%]i(h%]i!j%]i!V%]i'p%]ir%]i![%]i%c%]i!d%]i~P%.]Of'Ra!Y'Ra~P!0}O!Y'Sa!j'Sa~P!8dO!Y.lO!j(ci~O#|#_i!Y#_i!Z#_i~P#C|OP$]O|#yO}#zO!P#{O!i#wO!k#xO!o$]O(XVOZ#iin#ii!h#ii#k#ii#l#ii#m#ii#n#ii#o#ii#p#ii#q#ii#r#ii#t#ii#v#ii#x#ii#y#ii#|#ii(h#ii(o#ii(p#ii!Y#ii!Z#ii~O#j#ii~P%FnO#j:lO~P%FnOP$]O|#yO}#zO!P#{O!i#wO!k#xO!o$]O#j:lO#k:mO#l:mO#m:mO(XVOZ#ii!h#ii#n#ii#o#ii#p#ii#q#ii#r#ii#t#ii#v#ii#x#ii#y#ii#|#ii(h#ii(o#ii(p#ii!Y#ii!Z#ii~On#ii~P%HyOn:nO~P%HyOP$]On:nO|#yO}#zO!P#{O!i#wO!k#xO!o$]O#j:lO#k:mO#l:mO#m:mO#n:oO(XVO#t#ii#v#ii#x#ii#y#ii#|#ii(h#ii(o#ii(p#ii!Y#ii!Z#ii~OZ#ii!h#ii#o#ii#p#ii#q#ii#r#ii~P%KUOZ:zO!h:pO#o:pO#p:pO#q:yO#r:pO~P%KUOP$]OZ:zOn:nO|#yO}#zO!P#{O!h:pO!i#wO!k#xO!o$]O#j:lO#k:mO#l:mO#m:mO#n:oO#o:pO#p:pO#q:yO#r:pO#t:qO(XVO(p#}O#x#ii#y#ii#|#ii(h#ii(o#ii!Y#ii!Z#ii~O#v:sO~P%MpO#v#ii~P%MpOP$]OZ:zOn:nO|#yO}#zO!P#{O!h:pO!i#wO!k#xO!o$]O#j:lO#k:mO#l:mO#m:mO#n:oO#o:pO#p:pO#q:yO#r:pO#t:qO(XVO#x#ii#y#ii#|#ii(h#ii!Y#ii!Z#ii~O#v#ii(o#ii(p#ii~P& {O#v:sO(o#|O(p#}O~P& {OP$]OZ:zOn:nO|#yO}#zO!P#{O!h:pO!i#wO!k#xO!o$]O#j:lO#k:mO#l:mO#m:mO#n:oO#o:pO#p:pO#q:yO#r:pO#t:qO#v:sO#x:uO(XVO(o#|O(p#}O~O#y#ii#|#ii(h#ii!Y#ii!Z#ii~P&$^O`#zy!Y#zy'r#zy'p#zy!V#zy!j#zyr#zy![#zy%c#zy!d#zy~P!8dOj<gO|)zO!P){O(o$}O(p%PO~OP#iiZ#iin#ii}#ii!h#ii!i#ii!k#ii!o#ii#j#ii#k#ii#l#ii#m#ii#n#ii#o#ii#p#ii#q#ii#r#ii#t#ii#v#ii#x#ii#y#ii#|#ii(X#ii(h#ii!Y#ii!Z#ii~P&'UO!i#wOP(WXZ(WXj(WXn(WX|(WX}(WX!P(WX!h(WX!k(WX!o(WX#j(WX#k(WX#l(WX#m(WX#n(WX#o(WX#p(WX#q(WX#r(WX#t(WX#v(WX#x(WX#y(WX#|(WX(X(WX(h(WX(o(WX(p(WX!Y(WX!Z(WX~O#|#}i!Y#}i!Z#}i~P#C|O#|!qi!Z!qi~P$'pO!Z6|O~O!Y']a!Z']a~P#C|O!d#uO(h'kO!Y'^a!j'^a~O!Y/cO!j(ui~O!Y/cO!d#uO!j(ui~Of$wq!Y$wq#[$wq#|$wq~P!0}O!V'`a!Y'`a~P#-]O!d7TO~O!Y/kO!V(vi~P#-]O!Y/kO!V(vi~O!V7XO~O!d#uO#r7^O~On7_O!d#uO(h'kO~O|)zO!P){O(p%POj'ca(o'ca!Y'ca#['ca~Of'ca#|'ca~P&.fO|)zO!P){Oj'ea(o'ea(p'ea!Y'ea#['ea~Of'ea#|'ea~P&/XO!V7aO~Of$yq!Y$yq#[$yq#|$yq~P!0}O`$ky!Y$ky'r$ky'p$ky!V$ky!j$kyr$ky![$ky%c$ky!d$ky~P!8dO!d6RO~O!Y4nO![(wa~O`#_y!Y#_y'r#_y'p#_y!V#_y!j#_yr#_y![#_y%c#_y!d#_y~P!8dOZ7fO~Oa7hO'{+hO'}TO(QUO~O!Y0rO!Z)Oi~O_7lO~O([(xO!Y'iX!Z'iX~O!Y5UO!Z({a~OlkO'{7sO~P.iO!Z7vO~P%3nOo!nO!P7wO'}TO(QUO([!mO(g!sO~O![1[O~O![1[O%c7yO~Oj7|O![1[O%c7yO~OZ8RO!Y'la!Z'la~O!Y1gO!Z(|i~O!j8VO~O!j8WO~O!j8ZO~O!j8ZO~P%[O`8]O~O!d8^O~O!j8_O~O!Y(mi!Z(mi~P#C|O`%kO#[8gO'r%kO~O!Y(jy!j(jy`(jy'r(jy~P!8dO!Y(dO!j(iy~O!['ZO%c8jO~O#g$wqP$wqZ$wq`$wqn$wq}$wq!Y$wq!h$wq!i$wq!k$wq!o$wq#j$wq#k$wq#l$wq#m$wq#n$wq#o$wq#p$wq#q$wq#r$wq#t$wq#v$wq#x$wq#y$wq'r$wq(X$wq(h$wq!j$wq!V$wq'p$wq#[$wqr$wq![$wq%c$wq!d$wq~P#-]O#g'caP'caZ'ca`'can'ca}'ca!h'ca!i'ca!k'ca!o'ca#j'ca#k'ca#l'ca#m'ca#n'ca#o'ca#p'ca#q'ca#r'ca#t'ca#v'ca#x'ca#y'ca'r'ca(X'ca(h'ca!j'ca!V'ca'p'car'ca!['ca%c'ca!d'ca~P&.fO#g'eaP'eaZ'ea`'ean'ea}'ea!h'ea!i'ea!k'ea!o'ea#j'ea#k'ea#l'ea#m'ea#n'ea#o'ea#p'ea#q'ea#r'ea#t'ea#v'ea#x'ea#y'ea'r'ea(X'ea(h'ea!j'ea!V'ea'p'ear'ea!['ea%c'ea!d'ea~P&/XO#g$yqP$yqZ$yq`$yqn$yq}$yq!Y$yq!h$yq!i$yq!k$yq!o$yq#j$yq#k$yq#l$yq#m$yq#n$yq#o$yq#p$yq#q$yq#r$yq#t$yq#v$yq#x$yq#y$yq'r$yq(X$yq(h$yq!j$yq!V$yq'p$yq#[$yqr$yq![$yq%c$yq!d$yq~P#-]O!Y'Si!j'Si~P!8dO#|#_q!Y#_q!Z#_q~P#C|O(o$}OP%ZaZ%Zan%Za}%Za!h%Za!i%Za!k%Za!o%Za#j%Za#k%Za#l%Za#m%Za#n%Za#o%Za#p%Za#q%Za#r%Za#t%Za#v%Za#x%Za#y%Za#|%Za(X%Za(h%Za!Y%Za!Z%Za~Oj%Za|%Za!P%Za(p%Za~P&@nO(p%POP%]aZ%]an%]a}%]a!h%]a!i%]a!k%]a!o%]a#j%]a#k%]a#l%]a#m%]a#n%]a#o%]a#p%]a#q%]a#r%]a#t%]a#v%]a#x%]a#y%]a#|%]a(X%]a(h%]a!Y%]a!Z%]a~Oj%]a|%]a!P%]a(o%]a~P&BuOj<gO|)zO!P){O(p%PO~P&@nOj<gO|)zO!P){O(o$}O~P&BuO|0ZO}0ZO!P0[OPyaZyajyanya!hya!iya!kya!oya#jya#kya#lya#mya#nya#oya#pya#qya#rya#tya#vya#xya#yya#|ya(Xya(hya(oya(pya!Yya!Zya~O|)zO!P){OP$naZ$naj$nan$na}$na!h$na!i$na!k$na!o$na#j$na#k$na#l$na#m$na#n$na#o$na#p$na#q$na#r$na#t$na#v$na#x$na#y$na#|$na(X$na(h$na(o$na(p$na!Y$na!Z$na~O|)zO!P){OP$paZ$paj$pan$pa}$pa!h$pa!i$pa!k$pa!o$pa#j$pa#k$pa#l$pa#m$pa#n$pa#o$pa#p$pa#q$pa#r$pa#t$pa#v$pa#x$pa#y$pa#|$pa(X$pa(h$pa(o$pa(p$pa!Y$pa!Z$pa~OP%OaZ%Oan%Oa}%Oa!h%Oa!i%Oa!k%Oa!o%Oa#j%Oa#k%Oa#l%Oa#m%Oa#n%Oa#o%Oa#p%Oa#q%Oa#r%Oa#t%Oa#v%Oa#x%Oa#y%Oa#|%Oa(X%Oa(h%Oa!Y%Oa!Z%Oa~P&'UO#|$jq!Y$jq!Z$jq~P#C|O#|$kq!Y$kq!Z$kq~P#C|O!Z8vO~O#|8wO~P!0}O!d#uO!Y'^i!j'^i~O!d#uO(h'kO!Y'^i!j'^i~O!Y/cO!j(uq~O!V'`i!Y'`i~P#-]O!Y/kO!V(vq~O!V8}O~P#-]O!V8}O~Of(Vy!Y(Vy~P!0}O!Y'ga!['ga~P#-]O`%Vq![%Vq'r%Vq!Y%Vq~P#-]OZ9SO~O!Y0rO!Z)Oq~O#[9WO!Y'ia!Z'ia~O!Y5UO!Z({i~P#C|OP[XZ[Xn[X|[X}[X!P[X!V[X!Y[X!h[X!i[X!k[X!o[X#[[X#geX#j[X#k[X#l[X#m[X#n[X#o[X#p[X#q[X#r[X#t[X#v[X#x[X#y[X$O[X(X[X(h[X(o[X(p[X~O!d%TX#r%TX~P'#`O![1[O%c9[O~O'}TO(QUO([9aO~O!Y1gO!Z(|q~O!j9dO~O!j9eO~O!j9fO~O!j9fO~P%[O#[9iO!Y#dy!Z#dy~O!Y#dy!Z#dy~P#C|O!['ZO%c9nO~O#|#zy!Y#zy!Z#zy~P#C|OP$wiZ$win$wi}$wi!h$wi!i$wi!k$wi!o$wi#j$wi#k$wi#l$wi#m$wi#n$wi#o$wi#p$wi#q$wi#r$wi#t$wi#v$wi#x$wi#y$wi#|$wi(X$wi(h$wi!Y$wi!Z$wi~P&'UO|)zO!P){O(p%POP'baZ'baj'ban'ba}'ba!h'ba!i'ba!k'ba!o'ba#j'ba#k'ba#l'ba#m'ba#n'ba#o'ba#p'ba#q'ba#r'ba#t'ba#v'ba#x'ba#y'ba#|'ba(X'ba(h'ba(o'ba!Y'ba!Z'ba~O|)zO!P){OP'daZ'daj'dan'da}'da!h'da!i'da!k'da!o'da#j'da#k'da#l'da#m'da#n'da#o'da#p'da#q'da#r'da#t'da#v'da#x'da#y'da#|'da(X'da(h'da(o'da(p'da!Y'da!Z'da~O(o$}OP%ZiZ%Zij%Zin%Zi|%Zi}%Zi!P%Zi!h%Zi!i%Zi!k%Zi!o%Zi#j%Zi#k%Zi#l%Zi#m%Zi#n%Zi#o%Zi#p%Zi#q%Zi#r%Zi#t%Zi#v%Zi#x%Zi#y%Zi#|%Zi(X%Zi(h%Zi(p%Zi!Y%Zi!Z%Zi~O(p%POP%]iZ%]ij%]in%]i|%]i}%]i!P%]i!h%]i!i%]i!k%]i!o%]i#j%]i#k%]i#l%]i#m%]i#n%]i#o%]i#p%]i#q%]i#r%]i#t%]i#v%]i#x%]i#y%]i#|%]i(X%]i(h%]i(o%]i!Y%]i!Z%]i~O#|$ky!Y$ky!Z$ky~P#C|O#|#_y!Y#_y!Z#_y~P#C|O!d#uO!Y'^q!j'^q~O!Y/cO!j(uy~O!V'`q!Y'`q~P#-]O!V9wO~P#-]O!Y0rO!Z)Oy~O!Y5UO!Z({q~O![1[O%c:OO~O!j:RO~O!['ZO%c:WO~OP$wqZ$wqn$wq}$wq!h$wq!i$wq!k$wq!o$wq#j$wq#k$wq#l$wq#m$wq#n$wq#o$wq#p$wq#q$wq#r$wq#t$wq#v$wq#x$wq#y$wq#|$wq(X$wq(h$wq!Y$wq!Z$wq~P&'UO|)zO!P){O(p%POP'caZ'caj'can'ca}'ca!h'ca!i'ca!k'ca!o'ca#j'ca#k'ca#l'ca#m'ca#n'ca#o'ca#p'ca#q'ca#r'ca#t'ca#v'ca#x'ca#y'ca#|'ca(X'ca(h'ca(o'ca!Y'ca!Z'ca~O|)zO!P){OP'eaZ'eaj'ean'ea}'ea!h'ea!i'ea!k'ea!o'ea#j'ea#k'ea#l'ea#m'ea#n'ea#o'ea#p'ea#q'ea#r'ea#t'ea#v'ea#x'ea#y'ea#|'ea(X'ea(h'ea(o'ea(p'ea!Y'ea!Z'ea~OP$yqZ$yqn$yq}$yq!h$yq!i$yq!k$yq!o$yq#j$yq#k$yq#l$yq#m$yq#n$yq#o$yq#p$yq#q$yq#r$yq#t$yq#v$yq#x$yq#y$yq#|$yq(X$yq(h$yq!Y$yq!Z$yq~P&'UOf%_!Z!Y%_!Z#[%_!Z#|%_!Z~P!0}O!Y'iq!Z'iq~P#C|O!Y#d!Z!Z#d!Z~P#C|O#g%_!ZP%_!ZZ%_!Z`%_!Zn%_!Z}%_!Z!Y%_!Z!h%_!Z!i%_!Z!k%_!Z!o%_!Z#j%_!Z#k%_!Z#l%_!Z#m%_!Z#n%_!Z#o%_!Z#p%_!Z#q%_!Z#r%_!Z#t%_!Z#v%_!Z#x%_!Z#y%_!Z'r%_!Z(X%_!Z(h%_!Z!j%_!Z!V%_!Z'p%_!Z#[%_!Zr%_!Z![%_!Z%c%_!Z!d%_!Z~P#-]OP%_!ZZ%_!Zn%_!Z}%_!Z!h%_!Z!i%_!Z!k%_!Z!o%_!Z#j%_!Z#k%_!Z#l%_!Z#m%_!Z#n%_!Z#o%_!Z#p%_!Z#q%_!Z#r%_!Z#t%_!Z#v%_!Z#x%_!Z#y%_!Z#|%_!Z(X%_!Z(h%_!Z!Y%_!Z!Z%_!Z~P&'UOr(]X~P1qO'|!lO~P!*fO!VeX!YeX#[eX~P'#`OP[XZ[Xn[X|[X}[X!P[X!Y[X!YeX!h[X!i[X!k[X!o[X#[[X#[eX#geX#j[X#k[X#l[X#m[X#n[X#o[X#p[X#q[X#r[X#t[X#v[X#x[X#y[X$O[X(X[X(h[X(o[X(p[X~O!deX!j[X!jeX(heX~P'ASOP:cOQ:cORfOc<[Od!iOlkOn:cOokOpkOvkOx:cOz:cO!PWO!TkO!UkO![XO!f:fO!kZO!n:cO!o:cO!p:cO!r:gO!t:jO!w!hO$T!kO'{)YO'}TO(QUO(XVO(g[O(t<YO~O!Y:wO!Z$ma~Og%VOl%WOn$tOo$sOp$sOv%XOx%YOz;RO!P${O![$|O!f<aO!k$xO#f;XO$T%^O$o;TO$q;VO$t%_O'{(pO'}TO(QUO(X$uO(o$}O(p%PO~O#s)aO~P'ExO!Z[X!ZeX~P'ASO#g:kO~O!d#uO#g:kO~O#[:{O~O#r:pO~O#[;ZO!Y(mX!Z(mX~O#[:{O!Y(kX!Z(kX~O#g;[O~Of;^O~P!0}O#g;cO~O#g;dO~O!d#uO#g;eO~O!d#uO#g;[O~O#|;fO~P#C|O#g;gO~O#g;hO~O#g;mO~O#g;nO~O#g;oO~O#g;pO~O#|;qO~P!0}O#|;rO~P!0}O!i#P#Q#S#T#W#e#f#q(t$o$q$t%W%b%c%d%k%m%p%q%s%u~'vS#k!U't'|#lo#j#mn|'u$Y'u'{$[([~",goto:"$2p)SPPPPP)TPP)WP)iP*x.|PPPP5pPP6WPP<S?gP?zP?zPPP?zPAxP?zP?zP?zPA|PPBRPBlPGdPPPGhPPPPGhJiPPPJoKjPGhPMxPPPP!!WGhPPPGhPGhP!$fGhP!'z!(|!)VP!)y!)}!)yPPPPP!-Y!(|PP!-v!.pP!1dGhGh!1i!4s!9Y!9Y!=OPPP!=VGhPPPPPPPPPPP!@dP!AqPPGh!CSPGhPGhGhGhGhPGh!DfP!GnP!JrP!Jv!KQ!KU!KUP!GkP!KY!KYP!N^P!NbGhGh!Nh##k?zP?zP?z?zP#$v?z?z#'O?z#)k?z#+m?z?z#,[#.f#.f#.j#.r#.f#.zP#.fP?z#/d?z#3R?z?z5pPPP#6vPPP#7a#7aP#7aP#7w#7aPP#7}P#7tP#7t#8b#7t#8|#9S5m)W#9V)WP#9^#9^#9^P)WP)WP)WP)WPP)WP#9d#9gP#9g)WP#9kP#9nP)WP)WP)WP)WP)WP)W)WPP#9t#9z#:V#:]#:c#:i#:o#:}#;T#;Z#;e#;k#;u#<U#<[#<|#=`#=f#=l#=z#>a#@O#@^#@d#Ax#BW#Cr#DQ#DW#D^#Dd#Dn#Dt#Dz#EU#Eh#EnPPPPPPPPPP#EtPPPPPPP#Fi#Ip#KP#KW#K`PPPP$!d$%Z$+r$+u$+x$,q$,t$,w$-O$-WPP$-^$-b$.Y$/X$/]$/qPP$/u$/{$0PP$0S$0W$0Z$1P$1h$2P$2T$2W$2Z$2a$2d$2h$2lR!{RoqOXst!Z#c%j&m&o&p&r,h,m1w1zY!uQ'Z-Y1[5]Q%pvQ%xyQ&P|Q&e!VS'R!e-QQ'a!iS'g!r!xS*c$|*hQ+f%yQ+s&RQ,X&_Q-W'YQ-b'bQ-j'hQ/|*jQ1f,YR;Y:g%OdOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$a$e%j%p%}&f&i&m&o&p&r&v'O']'m'}(P(V(^(r(v(z)y+O+S,e,h,m-^-f-t-z.l.s0[0a0q1_1o1p1r1t1w1z1|2m2s3Z5Y5d5t5u5x6]7w7|8]8gS#p]:d!r)[$[$m'S)n,y,|.{2]3p5W6S9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]Q*u%ZQ+k%{Q,Z&bQ,b&jQ.c;QQ0h+^Q0l+`Q0w+lQ1n,`Q2{.[Q4v0rQ5k1gQ6i3PQ6u;RQ7h4wR8m6j&|kOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m%j%p%}&f&i&j&m&o&p&r&v'O'S']'m'}(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]t!nQ!r!u!x!y'R'Y'Z'g'h'i-Q-W-Y-j1[5]5_$v$si#u#w$c$d$x${%O%Q%[%]%a)u){)}*P*R*Y*`*p*q+]+`+w+z.Z.i/Z/j/k/m0Q0S0^1R1U1^3O3x4S4[4f4n4p5c6g7T7^7y8j8w9[9n:O:W:y:z:|:};O;P;S;T;U;V;W;X;_;`;a;b;c;d;g;h;i;j;k;l;m;n;q;r<Y<b<c<f<gQ&S|Q'P!eS'V%f-TQ+k%{Q,Z&bQ0]*yQ0w+lQ0|+rQ1m,_Q1n,`Q4v0rQ5P1OQ5k1gQ5n1iQ5o1lQ7h4wQ7k4|Q8U5qQ9V7lR9b8RrnOXst!V!Z#c%j&d&m&o&p&r,h,m1w1zR,]&f&v^OPXYstuvwz!Z!`!g!j!o#R#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m%j%p%}&f&i&j&m&o&p&r&v'O']'m(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<[<][#[WZ#V#Y'S'}!S%gm#g#h#k%b%e(W(b(c(d+Q+R+T,d,z-x.O.P.Q.S2P2w2x6R6dQ%sxQ%wyS%||&RQ&Y!TQ'^!hQ'`!iQ(k#rS*V$x*ZS+e%x%yQ+i%{Q,S&]Q,W&_S-a'a'bQ.^(lQ/g*WQ0p+fQ0v+lQ0x+mQ0{+qQ1a,TS1e,X,YQ2i-bQ3y/cQ4u0rQ4y0uQ5O0}Q5j1fQ7Q3zQ7g4wQ7j4{Q9R7fR9y9S!O$zi#w%O%Q%[%]%a)}*P*Y*p*q.i/j0Q0S0^3x4f8w<Y<b<c!S%uy!i!t%w%x%y'Q'`'a'b'f'p*b+e+f,}-a-b-i/t0p2b2i2p4^Q+_%sQ+x&VQ+{&WQ,V&_Q.](kQ1`,SU1d,W,X,YQ3Q.^Q5e1aS5i1e1fQ8Q5j#W<^#u$c$d$x${)u){*R*`+]+`+w+z.Z/Z/k/m1R1U1^3O4S4[4n4p5c6g7T7^7y8j9[9n:O:W:|;O;S;U;W;_;a;c;g;i;k;m;q<f<go<_:y:z:};P;T;V;X;`;b;d;h;j;l;n;rW%Ti%V*r<YS&V!Q&dQ&W!RQ&X!SR+v&T$w%Si#u#w$c$d$x${%O%Q%[%]%a)u){)}*P*R*Y*`*p*q+]+`+w+z.Z.i/Z/j/k/m0Q0S0^1R1U1^3O3x4S4[4f4n4p5c6g7T7^7y8j8w9[9n:O:W:y:z:|:};O;P;S;T;U;V;W;X;_;`;a;b;c;d;g;h;i;j;k;l;m;n;q;r<Y<b<c<f<gT)v$u)wV*v%Z;Q;RU'V!e%f-TS(y#y#zQ+p&OS.V(g(hQ1V+|Q4g0ZR7p5U&|kOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m%j%p%}&f&i&j&m&o&p&r&v'O'S']'m'}(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]$i$`c#X#d%n%o%q'|(S(n(u(})O)P)Q)R)S)T)U)V)W)X)Z)^)b)l+Z+o-O-m-r-w-y.h.n.r.t.u.v/V0_2W2Z2k2r3Y3_3`3a3b3c3d3e3f3g3h3i3j3k3n3o3t4k4s6U6[6a6o6p6y6z7r8a8e8n8t8u9k9{:S:e<PT#SV#T&}kOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m%j%p%}&f&i&j&m&o&p&r&v'O'S']'m'}(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]Q'T!eR2^-Qv!nQ!e!r!u!x!y'R'Y'Z'g'h'i-Q-W-Y-j1[5]5_S*b$|*hS/t*c*jQ/}*kQ1X,OQ4^/|R4a0PnqOXst!Z#c%j&m&o&p&r,h,m1w1zQ&t!^Q'q!wS(m#t:kQ+c%vQ,Q&YQ,R&[Q-_'_Q-l'jS.g(r;[S0`+O;eQ0n+dQ1Z,PQ2O,oQ2Q,pQ2Y,{Q2g-`Q2j-dS4l0a;oQ4q0oS4t0q;pQ6T2[Q6X2hQ6^2oQ7e4rQ8b6VQ8c6YQ8f6_R9h8_$d$_c#X#d%o%q'|(S(n(u(})O)P)Q)R)S)T)U)V)W)X)Z)^)b)l+Z+o-O-m-r-w-y.h.n.r.u.v/V0_2W2Z2k2r3Y3_3`3a3b3c3d3e3f3g3h3i3j3k3n3o3t4k4s6U6[6a6o6p6y6z7r8a8e8n8t8u9k9{:S:e<PS(j#o'dU*o%R(q3mS+Y%n.tQ2|0hQ6f2{Q8l6iR9o8m$d$^c#X#d%o%q'|(S(n(u(})O)P)Q)R)S)T)U)V)W)X)Z)^)b)l+Z+o-O-m-r-w-y.h.n.r.u.v/V0_2W2Z2k2r3Y3_3`3a3b3c3d3e3f3g3h3i3j3k3n3o3t4k4s6U6[6a6o6p6y6z7r8a8e8n8t8u9k9{:S:e<PS(i#o'dS({#z$_S+X%n.tS.W(h(jQ.w)]Q0e+YR2y.X&|kOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m%j%p%}&f&i&j&m&o&p&r&v'O'S']'m'}(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]S#p]:dQ&o!XQ&p!YQ&r![Q&s!]R1v,kQ'[!hQ+[%sQ-]'^S.Y(k+_Q2e-[W2}.].^0g0iQ6W2fU6e2z2|3QS8i6f6hS9m8k8lS:U9l9oQ:^:VR:a:_U!vQ'Z-YT5Z1[5]!Q_OXZ`st!V!Z#c#g%b%j&d&f&m&o&p&r(d,h,m.P1w1z]!pQ!r'Z-Y1[5]T#p]:d%Y{OPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$a$e%j%p%}&f&i&j&m&o&p&r&v'O']'m'}(P(V(^(r(v(z)y+O+S+^,e,h,m-^-f-t-z.[.l.s0[0a0q1_1o1p1r1t1w1z1|2m2s3P3Z5Y5d5t5u5x6]6j7w7|8]8gS(y#y#zS.V(g(h!s;v$[$m'S)n,y,|.{2]3p5W6S9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]Y!tQ'Z-Y1[5]Q'f!rS'p!u!xS'r!y5_S-i'g'hQ-k'iR2p-jQ'o!tS(`#f1qS-h'f'rQ/f*VQ/r*bQ2q-kQ4O/gS4X/s/}Q7P3yS7[4_4aQ8y7QR9Q7_Q#vbQ'n!tS(_#f1qS(a#l*}Q+P%cQ+a%tQ+g%zU-g'f'o'rQ-{(`Q/e*VQ/q*bQ/w*eQ0m+bQ1b,US2n-h-kQ2v.TS3}/f/gS4W/r/}Q4Z/vQ4]/xQ5g1cQ6`2qQ7O3yQ7S4OS7W4X4aQ7]4`Q8O5hS8x7P7QQ8|7XQ9O7[Q9_8PQ9u8yQ9v8}Q9x9QQ:Q9`Q:Y9wQ;y;tQ<U;}R<V<OV!vQ'Z-Y%YaOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$a$e%j%p%}&f&i&j&m&o&p&r&v'O']'m'}(P(V(^(r(v(z)y+O+S+^,e,h,m-^-f-t-z.[.l.s0[0a0q1_1o1p1r1t1w1z1|2m2s3P3Z5Y5d5t5u5x6]6j7w7|8]8gS#vz!j!r;s$[$m'S)n,y,|.{2]3p5W6S9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]R;y<[%YbOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$a$e%j%p%}&f&i&j&m&o&p&r&v'O']'m'}(P(V(^(r(v(z)y+O+S+^,e,h,m-^-f-t-z.[.l.s0[0a0q1_1o1p1r1t1w1z1|2m2s3P3Z5Y5d5t5u5x6]6j7w7|8]8gQ%cj!S%ty!i!t%w%x%y'Q'`'a'b'f'p*b+e+f,}-a-b-i/t0p2b2i2p4^S%zz!jQ+b%uQ,U&_W1c,V,W,X,YU5h1d1e1fS8P5i5jQ9`8Q!r;t$[$m'S)n,y,|.{2]3p5W6S9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]Q;}<ZR<O<[$|eOPXYstuvw!Z!`!g!o#R#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$a$e%j%p%}&f&i&m&o&p&r&v'O']'m(P(V(^(r(v(z)y+O+S+^,e,h,m-^-f-t-z.[.l.s0[0a0q1_1o1p1r1t1w1z1|2m2s3P3Z5Y5d5t5u5x6]6j7w7|8]8gY#aWZ#V#Y'}!S%gm#g#h#k%b%e(W(b(c(d+Q+R+T,d,z-x.O.P.Q.S2P2w2x6R6dQ,c&j!p;u$[$m)n,y,|.{2]3p5W6S9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]R;x'SS'W!e%fR2`-T%OdOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$a$e%j%p%}&f&i&m&o&p&r&v'O']'m'}(P(V(^(r(v(z)y+O+S,e,h,m-^-f-t-z.l.s0[0a0q1_1o1p1r1t1w1z1|2m2s3Z5Y5d5t5u5x6]7w7|8]8g!r)[$[$m'S)n,y,|.{2]3p5W6S9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]Q,b&jQ0h+^Q2{.[Q6i3PR8m6j!b$Uc#X%n'|(S(n(u)W)X)^)b+o-m-r-w-y.h.n/V0_2k2r3Y3k4k4s6[6a6o8e9k:e!P:r)Z)l-O.t2W2Z3_3i3j3n3t6U6p6y6z7r8a8n8t8u9{:S<P!f$Wc#X%n'|(S(n(u)T)U)W)X)^)b+o-m-r-w-y.h.n/V0_2k2r3Y3k4k4s6[6a6o8e9k:e!T:t)Z)l-O.t2W2Z3_3f3g3i3j3n3t6U6p6y6z7r8a8n8t8u9{:S<P!^$[c#X%n'|(S(n(u)^)b+o-m-r-w-y.h.n/V0_2k2r3Y3k4k4s6[6a6o8e9k:eQ3x/az<])Z)l-O.t2W2Z3_3n3t6U6p6y6z7r8a8n8t8u9{:S<PQ<b<dR<c<e&|kOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m%j%p%}&f&i&j&m&o&p&r&v'O'S']'m'}(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]S$nh$oR3q.z'TgOPWXYZhstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m$o%j%p%}&f&i&j&m&o&p&r&v'O'S']'m'}(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.z.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]T$jf$pQ$hfS)e$k)iR)q$pT$if$pT)g$k)i'ThOPWXYZhstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$[$a$e$m$o%j%p%}&f&i&j&m&o&p&r&v'O'S']'m'}(P(V(^(r(v(z)n)y+O+S+^,e,h,m,y,|-^-f-t-z.[.l.s.z.{0[0a0q1_1o1p1r1t1w1z1|2]2m2s3P3Z3p5W5Y5d5t5u5x6S6]6j7w7|8]8g9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]T$nh$oQ$qhR)p$o%YjOPWXYZstuvw!Z!`!g!o#R#V#Y#c#n#t#x#{$O$P$Q$R$S$T$U$V$W$X$Y$a$e%j%p%}&f&i&j&m&o&p&r&v'O']'m'}(P(V(^(r(v(z)y+O+S+^,e,h,m-^-f-t-z.[.l.s0[0a0q1_1o1p1r1t1w1z1|2m2s3P3Z5Y5d5t5u5x6]6j7w7|8]8g!s<Z$[$m'S)n,y,|.{2]3p5W6S9W9i:c:f:g:j:k:l:m:n:o:p:q:r:s:t:u:v:w:{;Y;Z;[;^;e;f;o;p<]#clOPXZst!Z!`!o#R#c#n#{$m%j&f&i&j&m&o&p&r&v'O'](z)n+S+^,e,h,m-^.[.{0[1_1o1p1r1t1w1z1|3P3p5Y5d5t5u5x6j7w7|8]!O%Ri#w%O%Q%[%]%a)}*P*Y*p*q.i/j0Q0S0^3x4f8w<Y<b<c#W(q#u$c$d$x${)u){*R*`+]+`+w+z.Z/Z/k/m1R1U1^3O4S4[4n4p5c6g7T7^7y8j9[9n:O:W:|;O;S;U;W;_;a;c;g;i;k;m;q<f<gQ*z%_Q/W)zo3m:y:z:};P;T;V;X;`;b;d;h;j;l;n;r!O$yi#w%O%Q%[%]%a)}*P*Y*p*q.i/j0Q0S0^3x4f8w<Y<b<cQ*[$zS*e$|*hQ*{%`Q/x*f#W;{#u$c$d$x${)u){*R*`+]+`+w+z.Z/Z/k/m1R1U1^3O4S4[4n4p5c6g7T7^7y8j9[9n:O:W:|;O;S;U;W;_;a;c;g;i;k;m;q<f<gn;|:y:z:};P;T;V;X;`;b;d;h;j;l;n;rQ<Q<^Q<R<_Q<S<`R<T<a!O%Ri#w%O%Q%[%]%a)}*P*Y*p*q.i/j0Q0S0^3x4f8w<Y<b<c#W(q#u$c$d$x${)u){*R*`+]+`+w+z.Z/Z/k/m1R1U1^3O4S4[4n4p5c6g7T7^7y8j9[9n:O:W:|;O;S;U;W;_;a;c;g;i;k;m;q<f<go3m:y:z:};P;T;V;X;`;b;d;h;j;l;n;rnoOXst!Z#c%j&m&o&p&r,h,m1w1zQ*_${Q,v&yQ,w&{R4R/k$v%Si#u#w$c$d$x${%O%Q%[%]%a)u){)}*P*R*Y*`*p*q+]+`+w+z.Z.i/Z/j/k/m0Q0S0^1R1U1^3O3x4S4[4f4n4p5c6g7T7^7y8j8w9[9n:O:W:y:z:|:};O;P;S;T;U;V;W;X;_;`;a;b;c;d;g;h;i;j;k;l;m;n;q;r<Y<b<c<f<gQ+y&WQ1T+{Q5S1SR7o5TT*g$|*hS*g$|*hT5[1[5]S/v*d5YT4`0O7wQ+a%tQ/w*eQ0m+bQ1b,UQ5g1cQ8O5hQ9_8PR:Q9`!O%Oi#w%O%Q%[%]%a)}*P*Y*p*q.i/j0Q0S0^3x4f8w<Y<b<cr)}$v(s*O*n*|/i0U0V3W4P4j6}7`9t;z<W<XS0Q*m0R#W:|#u$c$d$x${)u){*R*`+]+`+w+z.Z/Z/k/m1R1U1^3O4S4[4n4p5c6g7T7^7y8j9[9n:O:W:|;O;S;U;W;_;a;c;g;i;k;m;q<f<gn:}:y:z:};P;T;V;X;`;b;d;h;j;l;n;r!^;_(o)`*U*^._.b.f/S/X/a/n0f1Q1S3T4Q4U5R5T6k6n7U7Y7b7d8{9P:X<d<e`;`3l6q6t6x8o9p9s:bS;i.a3UT;j6s8r!O%Qi#w%O%Q%[%]%a)}*P*Y*p*q.i/j0Q0S0^3x4f8w<Y<b<cv*P$v(s*Q*m*|/]/i0U0V3W4P4b4j6}7`9t;z<W<XS0S*n0T#W;O#u$c$d$x${)u){*R*`+]+`+w+z.Z/Z/k/m1R1U1^3O4S4[4n4p5c6g7T7^7y8j9[9n:O:W:|;O;S;U;W;_;a;c;g;i;k;m;q<f<gn;P:y:z:};P;T;V;X;`;b;d;h;j;l;n;r!b;a(o)`*U*^.`.a.f/S/X/a/n0f1Q1S3R3T4Q4U5R5T6k6l6n7U7Y7b7d8{9P:X<d<ed;b3l6r6s6x8o8p9p9q9s:bS;k.b3VT;l6t8srnOXst!V!Z#c%j&d&m&o&p&r,h,m1w1zQ&a!UR,e&jrnOXst!V!Z#c%j&d&m&o&p&r,h,m1w1zR&a!UQ+}&XR1P+vsnOXst!V!Z#c%j&d&m&o&p&r,h,m1w1zQ1],SS5b1`1aU7x5`5a5eS9Z7z7{S9|9Y9]Q:Z9}R:`:[Q&h!VR,^&dR5n1iS%||&RR0x+mQ&m!WR,h&nR,n&sT1x,m1zR,r&tQ,q&tR2R,rQ't!zR-n'tSsOtQ#cXT%ms#cQ!}TR'v!}Q#QUR'x#QQ)w$uR/T)wQ#TVR'z#TQ#WWU(Q#W(R-uQ(R#XR-u(SQ-R'TR2_-RQ.j(sR3X.jQ.m(uS3[.m3]R3].nQ-Y'ZR2c-YY!rQ'Z-Y1[5]R'e!rS#^W%eU(X#^(Y-vQ(Y#_R-v(TQ-U'WR2a-Ut`OXst!V!Z#c%j&d&f&m&o&p&r,h,m1w1zS#gZ%bU#q`#g.PR.P(dQ(e#iQ-|(aW.U(e-|2t6bQ2t-}R6b2uQ)i$kR.|)iQ$ohR)o$oQ$bcU)_$b-q:xQ-q:eR:x)lQ/d*VW3{/d3|7R8zU3|/e/f/gS7R3}4OR8z7S$X)|$v(o(s)`*U*^*m*n*w*x*|.a.b.d.e.f/S/X/]/_/a/i/n0U0V0f1Q1S3R3S3T3W3l4P4Q4U4b4d4j5R5T6k6l6m6n6s6t6v6w6x6}7U7Y7`7b7d8o8p8q8{9P9p9q9r9s9t:X:b;z<W<X<d<eQ/l*^U4T/l4V7VQ4V/nR7V4UQ*h$|R/z*hr*O$v(s*m*n*|/i0U0V3W4P4j6}7`9t;z<W<X!^._(o)`*U*^.a.b.f/S/X/a/n0f1Q1S3T4Q4U5R5T6k6n7U7Y7b7d8{9P:X<d<eU/^*O._6qa6q3l6s6t6x8o9p9s:bQ0R*mQ3U.aU4c0R3U8rR8r6sv*Q$v(s*m*n*|/]/i0U0V3W4P4b4j6}7`9t;z<W<X!b.`(o)`*U*^.a.b.f/S/X/a/n0f1Q1S3R3T4Q4U5R5T6k6l6n7U7Y7b7d8{9P:X<d<eU/`*Q.`6re6r3l6s6t6x8o8p9p9q9s:bQ0T*nQ3V.bU4e0T3V8sR8s6tQ*s%UR0X*sQ4o0fR7c4oQ+U%hR0d+UQ5V1VS7q5V9XR9X7rQ,P&YR1Y,PQ5]1[R7u5]Q1h,ZS5l1h8SR8S5nQ0s+iW4x0s4z7i9TQ4z0vQ7i4yR9T7jQ+n%|R0y+nQ1z,mR5|1zYrOXst#cQ&q!ZQ+W%jQ,g&mQ,i&oQ,j&pQ,l&rQ1u,hS1x,m1zR5{1wQ%lpQ&u!_Q&x!aQ&z!bQ&|!cQ'l!tQ+V%iQ+c%vQ+u&SQ,]&hQ,t&wW-e'f'n'o'rQ-l'jQ/y*gQ0n+dS1k,^,aQ2S,sQ2T,vQ2U,wQ2j-dW2l-g-h-k-mQ4q0oQ4}0|Q5Q1QQ5f1bQ5p1mQ5z1vU6Z2k2n2qQ6^2oQ7e4rQ7m5PQ7n5RQ7t5[Q7}5gQ8T5oS8d6[6`Q8f6_Q9U7kQ9^8OQ9c8UQ9j8eQ9z9VQ:P9_Q:T9kR:]:QQ%vyQ'_!iQ'j!tU+d%w%x%yQ,{'QU-`'`'a'bS-d'f'pQ/p*bS0o+e+fQ2[,}S2h-a-bQ2o-iQ4Y/tQ4r0pQ6V2bQ6Y2iQ6_2pR7Z4^S$wi<YR*t%VU%Ui%V<YR0W*rQ$viS(o#u+`Q(s#wS)`$c$dQ*U$xQ*^${Q*m%OQ*n%QQ*w%[Q*x%]Q*|%aQ.a:|Q.b;OQ.d;SQ.e;UQ.f;WQ/S)uS/X){/ZQ/])}Q/_*PQ/a*RQ/i*YQ/n*`Q0U*pQ0V*qh0f+].Z1^3O5c6g7y8j9[9n:O:WQ1Q+wQ1S+zQ3R;_Q3S;aQ3T;cQ3W.iS3l:y:zQ4P/jQ4Q/kQ4U/mQ4b0QQ4d0SQ4j0^Q5R1RQ5T1UQ6k;gQ6l;iQ6m;kQ6n;mQ6s:}Q6t;PQ6v;TQ6w;VQ6x;XQ6}3xQ7U4SQ7Y4[Q7`4fQ7b4nQ7d4pQ8o;dQ8p;`Q8q;bQ8{7TQ9P7^Q9p;hQ9q;jQ9r;lQ9s;nQ9t8wQ:X;qQ:b;rQ;z<YQ<W<bQ<X<cQ<d<fR<e<gnpOXst!Z#c%j&m&o&p&r,h,m1w1zQ!fPS#eZ#nQ&w!`U'c!o5Y7wQ'y#RQ(|#{Q)m$mS,a&f&iQ,f&jQ,s&vQ,x'OQ-[']Q.p(zQ/Q)nQ0b+SQ0i+^Q1s,eQ2f-^Q2|.[Q3s.{Q4h0[Q5a1_Q5r1oQ5s1pQ5w1rQ5y1tQ6O1|Q6f3PQ6{3pQ7{5dQ8X5tQ8Y5uQ8[5xQ8l6jQ9]7|R9g8]#WcOPXZst!Z!`!o#c#n#{%j&f&i&j&m&o&p&r&v'O'](z+S+^,e,h,m-^.[0[1_1o1p1r1t1w1z1|3P5Y5d5t5u5x6j7w7|8]Q#XWQ#dYQ%nuQ%ovS%qw!gS'|#V(PQ(S#YQ(n#tQ(u#xQ(}$OQ)O$PQ)P$QQ)Q$RQ)R$SQ)S$TQ)T$UQ)U$VQ)V$WQ)W$XQ)X$YQ)Z$[Q)^$aQ)b$eW)l$m)n.{3pQ+Z%pQ+o%}S-O'S2]Q-m'mS-r'}-tQ-w(VQ-y(^Q.h(rQ.n(vQ.r:cQ.t:fQ.u:gQ.v:jQ/V)yQ0_+OQ2W,yQ2Z,|Q2k-fQ2r-zQ3Y.lQ3_:kQ3`:lQ3a:mQ3b:nQ3c:oQ3d:pQ3e:qQ3f:rQ3g:sQ3h:tQ3i:uQ3j:vQ3k.sQ3n:{Q3o;YQ3t:wQ4k0aQ4s0qQ6U;ZQ6[2mQ6a2sQ6o3ZQ6p;[Q6y;^Q6z;eQ7r5WQ8a6SQ8e6]Q8n;fQ8t;oQ8u;pQ9k8gQ9{9WQ:S9iQ:e#RR<P<]R#ZWR'U!eY!tQ'Z-Y1[5]S'Q!e-QQ'f!rS'p!u!xS'r!y5_S,}'R'YS-i'g'hQ-k'iQ2b-WR2p-jR(t#wR(w#xQ!fQT-X'Z-Y]!qQ!r'Z-Y1[5]Q#o]R'd:dT#jZ%bS#iZ%bS%hm,dU(a#g#h#kS-}(b(cQ.R(dQ0c+TQ2u.OU2v.P.Q.SS6c2w2xR8h6d`#]W#V#Y%e'}(W+Q-xr#fZm#g#h#k%b(b(c(d+T.O.P.Q.S2w2x6dQ1q,dQ2X,zQ6Q2PQ8`6RT;w'S+RT#`W%eS#_W%eS(O#V(WS(T#Y+QS-P'S+RT-s'}-xT'X!e%fQ$kfR)s$pT)h$k)iR3r.zT*X$x*ZR*a${Q0g+]Q2z.ZQ5`1^Q6h3OQ7z5cQ8k6gQ9Y7yQ9l8jQ9}9[Q:V9nQ:[:OR:_:WnqOXst!Z#c%j&m&o&p&r,h,m1w1zQ&g!VR,]&dtmOXst!U!V!Z#c%j&d&m&o&p&r,h,m1w1zR,d&jT%im,dR1W+|R,[&bQ&Q|R+t&RR+j%{T&k!W&nT&l!W&nT1y,m1z",nodeNames:"⚠ ArithOp ArithOp JSXStartTag LineComment BlockComment Script Hashbang ExportDeclaration export Star as VariableName String Escape from ; default FunctionDeclaration async function VariableDefinition > < TypeParamList TypeDefinition extends ThisType this LiteralType ArithOp Number BooleanLiteral TemplateType InterpolationEnd Interpolation InterpolationStart NullType null VoidType void TypeofType typeof MemberExpression . ?. PropertyName [ TemplateString Escape Interpolation super RegExp ] ArrayExpression Spread , } { ObjectExpression Property async get set PropertyDefinition Block : NewExpression new TypeArgList CompareOp < ) ( ArgList UnaryExpression delete LogicOp BitOp YieldExpression yield AwaitExpression await ParenthesizedExpression ClassExpression class ClassBody MethodDeclaration Decorator @ MemberExpression PrivatePropertyName CallExpression declare Privacy static abstract override PrivatePropertyDefinition PropertyDeclaration readonly accessor Optional TypeAnnotation Equals StaticBlock FunctionExpression ArrowFunction ParamList ParamList ArrayPattern ObjectPattern PatternProperty Privacy readonly Arrow MemberExpression BinaryExpression ArithOp ArithOp ArithOp ArithOp BitOp CompareOp instanceof satisfies in const CompareOp BitOp BitOp BitOp LogicOp LogicOp ConditionalExpression LogicOp LogicOp AssignmentExpression UpdateOp PostfixExpression CallExpression TaggedTemplateExpression DynamicImport import ImportMeta JSXElement JSXSelfCloseEndTag JSXSelfClosingTag JSXIdentifier JSXBuiltin JSXIdentifier JSXNamespacedName JSXMemberExpression JSXSpreadAttribute JSXAttribute JSXAttributeValue JSXEscape JSXEndTag JSXOpenTag JSXFragmentTag JSXText JSXEscape JSXStartCloseTag JSXCloseTag PrefixCast ArrowFunction TypeParamList SequenceExpression KeyofType keyof UniqueType unique ImportType InferredType infer TypeName ParenthesizedType FunctionSignature ParamList NewSignature IndexedType TupleType Label ArrayType ReadonlyType ObjectType MethodType PropertyType IndexSignature PropertyDefinition CallSignature TypePredicate is NewSignature new UnionType LogicOp IntersectionType LogicOp ConditionalType ParameterizedType ClassDeclaration abstract implements type VariableDeclaration let var using TypeAliasDeclaration InterfaceDeclaration interface EnumDeclaration enum EnumBody NamespaceDeclaration namespace module AmbientDeclaration declare GlobalDeclaration global ClassDeclaration ClassBody AmbientFunctionDeclaration ExportGroup VariableName VariableName ImportDeclaration ImportGroup ForStatement for ForSpec ForInSpec ForOfSpec of WhileStatement while WithStatement with DoStatement do IfStatement if else SwitchStatement switch SwitchBody CaseLabel case DefaultLabel TryStatement try CatchClause catch FinallyClause finally ReturnStatement return ThrowStatement throw BreakStatement break ContinueStatement continue DebuggerStatement debugger LabeledStatement ExpressionStatement SingleExpression SingleClassItem",maxTerm:371,context:sO,nodeProps:[["isolate",-8,4,5,13,33,35,48,50,52,""],["group",-26,8,16,18,65,201,205,209,210,212,215,218,228,230,236,238,240,242,245,251,257,259,261,263,265,267,268,"Statement",-32,12,13,28,31,32,38,48,51,52,54,59,67,75,79,81,83,84,106,107,116,117,134,137,139,140,141,142,144,145,164,165,167,"Expression",-23,27,29,33,37,39,41,168,170,172,173,175,176,177,179,180,181,183,184,185,195,197,199,200,"Type",-3,87,99,105,"ClassItem"],["openedBy",22,"<",34,"InterpolationStart",53,"[",57,"{",72,"(",157,"JSXStartCloseTag"],["closedBy",23,">",36,"InterpolationEnd",47,"]",58,"}",73,")",162,"JSXEndTag"]],propSources:[cO],skippedNodes:[0,4,5,271],repeatNodeCount:37,tokenData:"$Fj(CSR!bOX%ZXY+gYZ-yZ[+g[]%Z]^.c^p%Zpq+gqr/mrs3cst:_tuEruvJSvwLkwx! Yxy!'iyz!(sz{!)}{|!,q|}!.O}!O!,q!O!P!/Y!P!Q!9j!Q!R#8g!R![#:v![!]#Gv!]!^#IS!^!_#J^!_!`#Ns!`!a$#_!a!b$(l!b!c$,k!c!}Er!}#O$-u#O#P$/P#P#Q$4h#Q#R$5r#R#SEr#S#T$7P#T#o$8Z#o#p$<k#p#q$=a#q#r$>q#r#s$?}#s$f%Z$f$g+g$g#BYEr#BY#BZ$AX#BZ$ISEr$IS$I_$AX$I_$I|Er$I|$I}$Dd$I}$JO$Dd$JO$JTEr$JT$JU$AX$JU$KVEr$KV$KW$AX$KW&FUEr&FU&FV$AX&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$AX?HUOEr(n%d_$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&j&hT$f&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c&j&zP;=`<%l&c'|'U]$f&j(R!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!b(SU(R!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!b(iP;=`<%l'}'|(oP;=`<%l&}'[(y]$f&j(OpOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(rp)wU(OpOY)rZr)rs#O)r#P;'S)r;'S;=`*Z<%lO)rp*^P;=`<%l)r'[*dP;=`<%l(r#S*nX(Op(R!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g#S+^P;=`<%l*g(n+dP;=`<%l%Z(CS+rq$f&j(Op(R!b't(;dOX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p$f%Z$f$g+g$g#BY%Z#BY#BZ+g#BZ$IS%Z$IS$I_+g$I_$JT%Z$JT$JU+g$JU$KV%Z$KV$KW+g$KW&FU%Z&FU&FV+g&FV;'S%Z;'S;=`+a<%l?HT%Z?HT?HU+g?HUO%Z(CS.ST(P#S$f&j'u(;dO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c(CS.n_$f&j(Op(R!b'u(;dOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#`/x`$f&j!o$Ip(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`0z!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S1V`#t$Id$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`2X!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S2d_#t$Id$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/|3l_'}$(n$f&j(R!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k(^4r_$f&j(R!bOY4kYZ5qZr4krs7nsw4kwx5qx!^4k!^!_8p!_#O4k#O#P5q#P#o4k#o#p8p#p;'S4k;'S;=`:X<%lO4k&z5vX$f&jOr5qrs6cs!^5q!^!_6y!_#o5q#o#p6y#p;'S5q;'S;=`7h<%lO5q&z6jT$a`$f&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c`6|TOr6yrs7]s;'S6y;'S;=`7b<%lO6y`7bO$a``7eP;=`<%l6y&z7kP;=`<%l5q(^7w]$a`$f&j(R!bOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!r8uZ(R!bOY8pYZ6yZr8prs9hsw8pwx6yx#O8p#O#P6y#P;'S8p;'S;=`:R<%lO8p!r9oU$a`(R!bOY'}Zw'}x#O'}#P;'S'};'S;=`(f<%lO'}!r:UP;=`<%l8p(^:[P;=`<%l4k#%|:hh$f&j(Op(R!bOY%ZYZ&cZq%Zqr<Srs&}st%ZtuCruw%Zwx(rx!^%Z!^!_*g!_!c%Z!c!}Cr!}#O%Z#O#P&c#P#R%Z#R#SCr#S#T%Z#T#oCr#o#p*g#p$g%Z$g;'SCr;'S;=`El<%lOCr(r<__VS$f&j(Op(R!bOY<SYZ&cZr<Srs=^sw<Swx@nx!^<S!^!_Bm!_#O<S#O#P>`#P#o<S#o#pBm#p;'S<S;'S;=`Cl<%lO<S(Q=g]VS$f&j(R!bOY=^YZ&cZw=^wx>`x!^=^!^!_?q!_#O=^#O#P>`#P#o=^#o#p?q#p;'S=^;'S;=`@h<%lO=^&n>gXVS$f&jOY>`YZ&cZ!^>`!^!_?S!_#o>`#o#p?S#p;'S>`;'S;=`?k<%lO>`S?XSVSOY?SZ;'S?S;'S;=`?e<%lO?SS?hP;=`<%l?S&n?nP;=`<%l>`!f?xWVS(R!bOY?qZw?qwx?Sx#O?q#O#P?S#P;'S?q;'S;=`@b<%lO?q!f@eP;=`<%l?q(Q@kP;=`<%l=^'`@w]VS$f&j(OpOY@nYZ&cZr@nrs>`s!^@n!^!_Ap!_#O@n#O#P>`#P#o@n#o#pAp#p;'S@n;'S;=`Bg<%lO@ntAwWVS(OpOYApZrAprs?Ss#OAp#O#P?S#P;'SAp;'S;=`Ba<%lOAptBdP;=`<%lAp'`BjP;=`<%l@n#WBvYVS(Op(R!bOYBmZrBmrs?qswBmwxApx#OBm#O#P?S#P;'SBm;'S;=`Cf<%lOBm#WCiP;=`<%lBm(rCoP;=`<%l<S#%|C}i$f&j(g!L^(Op(R!bOY%ZYZ&cZr%Zrs&}st%ZtuCruw%Zwx(rx!Q%Z!Q![Cr![!^%Z!^!_*g!_!c%Z!c!}Cr!}#O%Z#O#P&c#P#R%Z#R#SCr#S#T%Z#T#oCr#o#p*g#p$g%Z$g;'SCr;'S;=`El<%lOCr#%|EoP;=`<%lCr(CSFRk$f&j(Op(R!b$Y#t'{&;d([!LYOY%ZYZ&cZr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$g%Z$g;'SEr;'S;=`I|<%lOEr+dHRk$f&j(Op(R!b$Y#tOY%ZYZ&cZr%Zrs&}st%ZtuGvuw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Gv![!^%Z!^!_*g!_!c%Z!c!}Gv!}#O%Z#O#P&c#P#R%Z#R#SGv#S#T%Z#T#oGv#o#p*g#p$g%Z$g;'SGv;'S;=`Iv<%lOGv+dIyP;=`<%lGv(CSJPP;=`<%lEr%#SJ_`$f&j(Op(R!b#l$IdOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#SKl_$f&j$O$Id(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&COLva(p&;`$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sv%ZvwM{wx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#SNW`$f&j#x$Id(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/|! c_(Q$)`$f&j(OpOY!!bYZ!#hZr!!brs!#hsw!!bwx!$xx!^!!b!^!_!%z!_#O!!b#O#P!#h#P#o!!b#o#p!%z#p;'S!!b;'S;=`!'c<%lO!!b'l!!i_$f&j(OpOY!!bYZ!#hZr!!brs!#hsw!!bwx!$xx!^!!b!^!_!%z!_#O!!b#O#P!#h#P#o!!b#o#p!%z#p;'S!!b;'S;=`!'c<%lO!!b&z!#mX$f&jOw!#hwx6cx!^!#h!^!_!$Y!_#o!#h#o#p!$Y#p;'S!#h;'S;=`!$r<%lO!#h`!$]TOw!$Ywx7]x;'S!$Y;'S;=`!$l<%lO!$Y`!$oP;=`<%l!$Y&z!$uP;=`<%l!#h'l!%R]$a`$f&j(OpOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(r!Q!&PZ(OpOY!%zYZ!$YZr!%zrs!$Ysw!%zwx!&rx#O!%z#O#P!$Y#P;'S!%z;'S;=`!']<%lO!%z!Q!&yU$a`(OpOY)rZr)rs#O)r#P;'S)r;'S;=`*Z<%lO)r!Q!'`P;=`<%l!%z'l!'fP;=`<%l!!b(*Q!'t_!k(!b$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z!'l!)O_!jM|$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'+h!*[b$f&j(Op(R!b'|#)d#m$IdOY%ZYZ&cZr%Zrs&}sw%Zwx(rxz%Zz{!+d{!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S!+o`$f&j(Op(R!b#j$IdOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&-O!,|`$f&j(Op(R!bn&%`OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&C[!.Z_!Y&;l$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(CS!/ec$f&j(Op(R!b|'<nOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!0p!P!Q%Z!Q![!3Y![!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z!'d!0ya$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!2O!P!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z!'d!2Z_!XMt$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l!3eg$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![!3Y![!^%Z!^!_*g!_!g%Z!g!h!4|!h#O%Z#O#P&c#P#R%Z#R#S!3Y#S#X%Z#X#Y!4|#Y#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l!5Vg$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx{%Z{|!6n|}%Z}!O!6n!O!Q%Z!Q![!8S![!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S!8S#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l!6wc$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![!8S![!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S!8S#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l!8_c$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![!8S![!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S!8S#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(CS!9uf$f&j(Op(R!b#k$IdOY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Kpxz!;Zz{#,f{!P!;Z!P!Q#-{!Q!^!;Z!^!_#'Z!_!`#5k!`!a#7Q!a!}!;Z!}#O#*}#O#P!Dj#P#o!;Z#o#p#'Z#p;'S!;Z;'S;=`#,`<%lO!;Z(r!;fb$f&j(Op(R!b!USOY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Kpx!P!;Z!P!Q#%Z!Q!^!;Z!^!_#'Z!_!}!;Z!}#O#*}#O#P!Dj#P#o!;Z#o#p#'Z#p;'S!;Z;'S;=`#,`<%lO!;Z(Q!<w`$f&j(R!b!USOY!<nYZ&cZw!<nwx!=yx!P!<n!P!Q!Eb!Q!^!<n!^!_!GY!_!}!<n!}#O!Ja#O#P!Dj#P#o!<n#o#p!GY#p;'S!<n;'S;=`!Kj<%lO!<n&n!>Q^$f&j!USOY!=yYZ&cZ!P!=y!P!Q!>|!Q!^!=y!^!_!@Y!_!}!=y!}#O!Bw#O#P!Dj#P#o!=y#o#p!@Y#p;'S!=y;'S;=`!E[<%lO!=y&n!?Ta$f&j!USO!^&c!_#Z&c#Z#[!>|#[#]&c#]#^!>|#^#a&c#a#b!>|#b#g&c#g#h!>|#h#i&c#i#j!>|#j#m&c#m#n!>|#n#o&c#p;'S&c;'S;=`&w<%lO&cS!@_X!USOY!@YZ!P!@Y!P!Q!@z!Q!}!@Y!}#O!Ac#O#P!Bb#P;'S!@Y;'S;=`!Bq<%lO!@YS!APU!US#Z#[!@z#]#^!@z#a#b!@z#g#h!@z#i#j!@z#m#n!@zS!AfVOY!AcZ#O!Ac#O#P!A{#P#Q!@Y#Q;'S!Ac;'S;=`!B[<%lO!AcS!BOSOY!AcZ;'S!Ac;'S;=`!B[<%lO!AcS!B_P;=`<%l!AcS!BeSOY!@YZ;'S!@Y;'S;=`!Bq<%lO!@YS!BtP;=`<%l!@Y&n!B|[$f&jOY!BwYZ&cZ!^!Bw!^!_!Ac!_#O!Bw#O#P!Cr#P#Q!=y#Q#o!Bw#o#p!Ac#p;'S!Bw;'S;=`!Dd<%lO!Bw&n!CwX$f&jOY!BwYZ&cZ!^!Bw!^!_!Ac!_#o!Bw#o#p!Ac#p;'S!Bw;'S;=`!Dd<%lO!Bw&n!DgP;=`<%l!Bw&n!DoX$f&jOY!=yYZ&cZ!^!=y!^!_!@Y!_#o!=y#o#p!@Y#p;'S!=y;'S;=`!E[<%lO!=y&n!E_P;=`<%l!=y(Q!Eki$f&j(R!b!USOY&}YZ&cZw&}wx&cx!^&}!^!_'}!_#O&}#O#P&c#P#Z&}#Z#[!Eb#[#]&}#]#^!Eb#^#a&}#a#b!Eb#b#g&}#g#h!Eb#h#i&}#i#j!Eb#j#m&}#m#n!Eb#n#o&}#o#p'}#p;'S&};'S;=`(l<%lO&}!f!GaZ(R!b!USOY!GYZw!GYwx!@Yx!P!GY!P!Q!HS!Q!}!GY!}#O!Ic#O#P!Bb#P;'S!GY;'S;=`!JZ<%lO!GY!f!HZb(R!b!USOY'}Zw'}x#O'}#P#Z'}#Z#[!HS#[#]'}#]#^!HS#^#a'}#a#b!HS#b#g'}#g#h!HS#h#i'}#i#j!HS#j#m'}#m#n!HS#n;'S'};'S;=`(f<%lO'}!f!IhX(R!bOY!IcZw!Icwx!Acx#O!Ic#O#P!A{#P#Q!GY#Q;'S!Ic;'S;=`!JT<%lO!Ic!f!JWP;=`<%l!Ic!f!J^P;=`<%l!GY(Q!Jh^$f&j(R!bOY!JaYZ&cZw!Jawx!Bwx!^!Ja!^!_!Ic!_#O!Ja#O#P!Cr#P#Q!<n#Q#o!Ja#o#p!Ic#p;'S!Ja;'S;=`!Kd<%lO!Ja(Q!KgP;=`<%l!Ja(Q!KmP;=`<%l!<n'`!Ky`$f&j(Op!USOY!KpYZ&cZr!Kprs!=ys!P!Kp!P!Q!L{!Q!^!Kp!^!_!Ns!_!}!Kp!}#O##z#O#P!Dj#P#o!Kp#o#p!Ns#p;'S!Kp;'S;=`#%T<%lO!Kp'`!MUi$f&j(Op!USOY(rYZ&cZr(rrs&cs!^(r!^!_)r!_#O(r#O#P&c#P#Z(r#Z#[!L{#[#](r#]#^!L{#^#a(r#a#b!L{#b#g(r#g#h!L{#h#i(r#i#j!L{#j#m(r#m#n!L{#n#o(r#o#p)r#p;'S(r;'S;=`*a<%lO(rt!NzZ(Op!USOY!NsZr!Nsrs!@Ys!P!Ns!P!Q# m!Q!}!Ns!}#O#!|#O#P!Bb#P;'S!Ns;'S;=`##t<%lO!Nst# tb(Op!USOY)rZr)rs#O)r#P#Z)r#Z#[# m#[#])r#]#^# m#^#a)r#a#b# m#b#g)r#g#h# m#h#i)r#i#j# m#j#m)r#m#n# m#n;'S)r;'S;=`*Z<%lO)rt##RX(OpOY#!|Zr#!|rs!Acs#O#!|#O#P!A{#P#Q!Ns#Q;'S#!|;'S;=`##n<%lO#!|t##qP;=`<%l#!|t##wP;=`<%l!Ns'`#$R^$f&j(OpOY##zYZ&cZr##zrs!Bws!^##z!^!_#!|!_#O##z#O#P!Cr#P#Q!Kp#Q#o##z#o#p#!|#p;'S##z;'S;=`#$}<%lO##z'`#%QP;=`<%l##z'`#%WP;=`<%l!Kp(r#%fk$f&j(Op(R!b!USOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#Z%Z#Z#[#%Z#[#]%Z#]#^#%Z#^#a%Z#a#b#%Z#b#g%Z#g#h#%Z#h#i%Z#i#j#%Z#j#m%Z#m#n#%Z#n#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z#W#'d](Op(R!b!USOY#'ZZr#'Zrs!GYsw#'Zwx!Nsx!P#'Z!P!Q#(]!Q!}#'Z!}#O#)w#O#P!Bb#P;'S#'Z;'S;=`#*w<%lO#'Z#W#(fe(Op(R!b!USOY*gZr*grs'}sw*gwx)rx#O*g#P#Z*g#Z#[#(]#[#]*g#]#^#(]#^#a*g#a#b#(]#b#g*g#g#h#(]#h#i*g#i#j#(]#j#m*g#m#n#(]#n;'S*g;'S;=`+Z<%lO*g#W#*OZ(Op(R!bOY#)wZr#)wrs!Icsw#)wwx#!|x#O#)w#O#P!A{#P#Q#'Z#Q;'S#)w;'S;=`#*q<%lO#)w#W#*tP;=`<%l#)w#W#*zP;=`<%l#'Z(r#+W`$f&j(Op(R!bOY#*}YZ&cZr#*}rs!Jasw#*}wx##zx!^#*}!^!_#)w!_#O#*}#O#P!Cr#P#Q!;Z#Q#o#*}#o#p#)w#p;'S#*};'S;=`#,Y<%lO#*}(r#,]P;=`<%l#*}(r#,cP;=`<%l!;Z(CS#,sb$f&j(Op(R!b'v(;d!USOY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Kpx!P!;Z!P!Q#%Z!Q!^!;Z!^!_#'Z!_!}!;Z!}#O#*}#O#P!Dj#P#o!;Z#o#p#'Z#p;'S!;Z;'S;=`#,`<%lO!;Z(CS#.W_$f&j(Op(R!bS(;dOY#-{YZ&cZr#-{rs#/Vsw#-{wx#2gx!^#-{!^!_#4f!_#O#-{#O#P#0X#P#o#-{#o#p#4f#p;'S#-{;'S;=`#5e<%lO#-{(Bb#/`]$f&j(R!bS(;dOY#/VYZ&cZw#/Vwx#0Xx!^#/V!^!_#1j!_#O#/V#O#P#0X#P#o#/V#o#p#1j#p;'S#/V;'S;=`#2a<%lO#/V(AO#0`X$f&jS(;dOY#0XYZ&cZ!^#0X!^!_#0{!_#o#0X#o#p#0{#p;'S#0X;'S;=`#1d<%lO#0X(;d#1QSS(;dOY#0{Z;'S#0{;'S;=`#1^<%lO#0{(;d#1aP;=`<%l#0{(AO#1gP;=`<%l#0X(<v#1qW(R!bS(;dOY#1jZw#1jwx#0{x#O#1j#O#P#0{#P;'S#1j;'S;=`#2Z<%lO#1j(<v#2^P;=`<%l#1j(Bb#2dP;=`<%l#/V(Ap#2p]$f&j(OpS(;dOY#2gYZ&cZr#2grs#0Xs!^#2g!^!_#3i!_#O#2g#O#P#0X#P#o#2g#o#p#3i#p;'S#2g;'S;=`#4`<%lO#2g(<U#3pW(OpS(;dOY#3iZr#3irs#0{s#O#3i#O#P#0{#P;'S#3i;'S;=`#4Y<%lO#3i(<U#4]P;=`<%l#3i(Ap#4cP;=`<%l#2g(=h#4oY(Op(R!bS(;dOY#4fZr#4frs#1jsw#4fwx#3ix#O#4f#O#P#0{#P;'S#4f;'S;=`#5_<%lO#4f(=h#5bP;=`<%l#4f(CS#5hP;=`<%l#-{%#W#5xb$f&j$O$Id(Op(R!b!USOY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Kpx!P!;Z!P!Q#%Z!Q!^!;Z!^!_#'Z!_!}!;Z!}#O#*}#O#P!Dj#P#o!;Z#o#p#'Z#p;'S!;Z;'S;=`#,`<%lO!;Z+h#7_b$W#t$f&j(Op(R!b!USOY!;ZYZ&cZr!;Zrs!<nsw!;Zwx!Kpx!P!;Z!P!Q#%Z!Q!^!;Z!^!_#'Z!_!}!;Z!}#O#*}#O#P!Dj#P#o!;Z#o#p#'Z#p;'S!;Z;'S;=`#,`<%lO!;Z$/l#8rp$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!3Y!P!Q%Z!Q![#:v![!^%Z!^!_*g!_!g%Z!g!h!4|!h#O%Z#O#P&c#P#R%Z#R#S#:v#S#U%Z#U#V#>Q#V#X%Z#X#Y!4|#Y#b%Z#b#c#<v#c#d#AY#d#l%Z#l#m#D[#m#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#;Rk$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P!3Y!P!Q%Z!Q![#:v![!^%Z!^!_*g!_!g%Z!g!h!4|!h#O%Z#O#P&c#P#R%Z#R#S#:v#S#X%Z#X#Y!4|#Y#b%Z#b#c#<v#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#=R_$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#>Zd$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#?i!R!S#?i!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#?i#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#?tf$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!R#?i!R!S#?i!S!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#?i#S#b%Z#b#c#<v#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#Acc$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#Bn!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#Bn#S#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#Bye$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q!Y#Bn!Y!^%Z!^!_*g!_#O%Z#O#P&c#P#R%Z#R#S#Bn#S#b%Z#b#c#<v#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#Deg$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#E|![!^%Z!^!_*g!_!c%Z!c!i#E|!i#O%Z#O#P&c#P#R%Z#R#S#E|#S#T%Z#T#Z#E|#Z#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z$/l#FXi$f&j(Op(R!bo$'|OY%ZYZ&cZr%Zrs&}sw%Zwx(rx!Q%Z!Q![#E|![!^%Z!^!_*g!_!c%Z!c!i#E|!i#O%Z#O#P&c#P#R%Z#R#S#E|#S#T%Z#T#Z#E|#Z#b%Z#b#c#<v#c#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%Gh#HT_!d$b$f&j#|%<f(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z)[#I__`l$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(@^#Jk^g!*v!h'.r(Op(R!b(tSOY*gZr*grs'}sw*gwx)rx!P*g!P!Q#Kg!Q!^*g!^!_#L]!_!`#M}!`#O*g#P;'S*g;'S;=`+Z<%lO*g(n#KpX$h&j(Op(R!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g$Kh#LfZ#n$Id(Op(R!bOY*gZr*grs'}sw*gwx)rx!_*g!_!`#MX!`#O*g#P;'S*g;'S;=`+Z<%lO*g$Kh#MbX$O$Id(Op(R!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g$Kh#NWX#o$Id(Op(R!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g%Gh$ Oa#[%?x$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`0z!`!a$!T!a#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#W$!`_#g$Ih$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%Gh$#nafBf#o$Id$c#|$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`$$s!`!a$%}!a#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S$%O_#o$Id$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S$&Ya#n$Id$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`!a$'_!a#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S$'j`#n$Id$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'+h$(wc(h$Ip$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!O%Z!O!P$*S!P!^%Z!^!_*g!_!a%Z!a!b$+^!b#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z'+`$*__}'#p$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S$+i`$f&j#y$Id(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z#&^$,v_!{!Ln$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(@^$.Q_!P(8n$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(n$/UZ$f&jO!^$/w!^!_$0_!_#i$/w#i#j$0d#j#l$/w#l#m$2V#m#o$/w#o#p$0_#p;'S$/w;'S;=`$4b<%lO$/w(n$0OT^#S$f&jO!^&c!_#o&c#p;'S&c;'S;=`&w<%lO&c#S$0dO^#S(n$0i[$f&jO!Q&c!Q![$1_![!^&c!_!c&c!c!i$1_!i#T&c#T#Z$1_#Z#o&c#o#p$3u#p;'S&c;'S;=`&w<%lO&c(n$1dZ$f&jO!Q&c!Q![$2V![!^&c!_!c&c!c!i$2V!i#T&c#T#Z$2V#Z#o&c#p;'S&c;'S;=`&w<%lO&c(n$2[Z$f&jO!Q&c!Q![$2}![!^&c!_!c&c!c!i$2}!i#T&c#T#Z$2}#Z#o&c#p;'S&c;'S;=`&w<%lO&c(n$3SZ$f&jO!Q&c!Q![$/w![!^&c!_!c&c!c!i$/w!i#T&c#T#Z$/w#Z#o&c#p;'S&c;'S;=`&w<%lO&c#S$3xR!Q![$4R!c!i$4R#T#Z$4R#S$4US!Q![$4R!c!i$4R#T#Z$4R#q#r$0_(n$4eP;=`<%l$/w!2r$4s_!V!+S$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z%#S$5}`#v$Id$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z&,v$7[_$f&j(Op(R!b(X&%WOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(CS$8jk$f&j(Op(R!b'{&;d$[#t([!LYOY%ZYZ&cZr%Zrs&}st%Ztu$8Zuw%Zwx(rx}%Z}!O$:_!O!Q%Z!Q![$8Z![!^%Z!^!_*g!_!c%Z!c!}$8Z!}#O%Z#O#P&c#P#R%Z#R#S$8Z#S#T%Z#T#o$8Z#o#p*g#p$g%Z$g;'S$8Z;'S;=`$<e<%lO$8Z+d$:jk$f&j(Op(R!b$[#tOY%ZYZ&cZr%Zrs&}st%Ztu$:_uw%Zwx(rx}%Z}!O$:_!O!Q%Z!Q![$:_![!^%Z!^!_*g!_!c%Z!c!}$:_!}#O%Z#O#P&c#P#R%Z#R#S$:_#S#T%Z#T#o$:_#o#p*g#p$g%Z$g;'S$:_;'S;=`$<_<%lO$:_+d$<bP;=`<%l$:_(CS$<hP;=`<%l$8Z!5p$<tX![!3l(Op(R!bOY*gZr*grs'}sw*gwx)rx#O*g#P;'S*g;'S;=`+Z<%lO*g&CO$=la(o&;`$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_!`Ka!`#O%Z#O#P&c#P#o%Z#o#p*g#p#q$+^#q;'S%Z;'S;=`+a<%lO%Z%#`$?O_!Z$I`r`$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(r$@Y_!pS$f&j(Op(R!bOY%ZYZ&cZr%Zrs&}sw%Zwx(rx!^%Z!^!_*g!_#O%Z#O#P&c#P#o%Z#o#p*g#p;'S%Z;'S;=`+a<%lO%Z(CS$Aj|$f&j(Op(R!b't(;d$Y#t'{&;d([!LYOX%ZXY+gYZ&cZ[+g[p%Zpq+gqr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$f%Z$f$g+g$g#BYEr#BY#BZ$AX#BZ$ISEr$IS$I_$AX$I_$JTEr$JT$JU$AX$JU$KVEr$KV$KW$AX$KW&FUEr&FU&FV$AX&FV;'SEr;'S;=`I|<%l?HTEr?HT?HU$AX?HUOEr(CS$Duk$f&j(Op(R!b'u(;d$Y#t'{&;d([!LYOY%ZYZ&cZr%Zrs&}st%ZtuEruw%Zwx(rx}%Z}!OGv!O!Q%Z!Q![Er![!^%Z!^!_*g!_!c%Z!c!}Er!}#O%Z#O#P&c#P#R%Z#R#SEr#S#T%Z#T#oEr#o#p*g#p$g%Z$g;'SEr;'S;=`I|<%lOEr",tokenizers:[oO,aO,hO,2,3,4,5,6,7,8,9,10,11,12,13,rO,new qp("$S~RRtu[#O#Pg#S#T#|~_P#o#pb~gOt~~jVO#i!P#i#j!U#j#l!P#l#m!q#m;'S!P;'S;=`#v<%lO!P~!UO!R~~!XS!Q![!e!c!i!e#T#Z!e#o#p#Z~!hR!Q![!q!c!i!q#T#Z!q~!tR!Q![!}!c!i!}#T#Z!}~#QR!Q![!P!c!i!P#T#Z!P~#^R!Q![#g!c!i#g#T#Z#g~#jS!Q![#g!c!i#g#T#Z#g#q#r!P~#yP;=`<%l!P~$RO(Z~~",141,332),new qp("j~RQYZXz{^~^O'x~~aP!P!Qd~iO'y~~",25,315)],topRules:{Script:[0,6],SingleExpression:[1,269],SingleClassItem:[2,270]},dialects:{jsx:0,ts:14614},dynamicPrecedences:{69:1,79:1,81:1,165:1,193:1},specialized:[{term:319,get:t=>fO[t]||-1},{term:334,get:t=>uO[t]||-1},{term:70,get:t=>dO[t]||-1}],tokenPrec:14638}),OO=[Ed("function ${name}(${params}) {\n\t${}\n}",{label:"function",detail:"definition",type:"keyword"}),Ed("for (let ${index} = 0; ${index} < ${bound}; ${index}++) {\n\t${}\n}",{label:"for",detail:"loop",type:"keyword"}),Ed("for (let ${name} of ${collection}) {\n\t${}\n}",{label:"for",detail:"of loop",type:"keyword"}),Ed("do {\n\t${}\n} while (${})",{label:"do",detail:"loop",type:"keyword"}),Ed("while (${}) {\n\t${}\n}",{label:"while",detail:"loop",type:"keyword"}),Ed("try {\n\t${}\n} catch (${error}) {\n\t${}\n}",{label:"try",detail:"/ catch block",type:"keyword"}),Ed("if (${}) {\n\t${}\n}",{label:"if",detail:"block",type:"keyword"}),Ed("if (${}) {\n\t${}\n} else {\n\t${}\n}",{label:"if",detail:"/ else block",type:"keyword"}),Ed("class ${name} {\n\tconstructor(${params}) {\n\t\t${}\n\t}\n}",{label:"class",detail:"definition",type:"keyword"}),Ed('import {${names}} from "${module}"\n${}',{label:"import",detail:"named",type:"keyword"}),Ed('import ${name} from "${module}"\n${}',{label:"import",detail:"default",type:"keyword"})],gO=OO.concat([Ed("interface ${name} {\n\t${}\n}",{label:"interface",detail:"definition",type:"keyword"}),Ed("type ${name} = ${type}",{label:"type",detail:"definition",type:"keyword"}),Ed("enum ${name} {\n\t${}\n}",{label:"enum",detail:"definition",type:"keyword"})]),mO=new ka,wO=new Set(["Script","Block","FunctionExpression","FunctionDeclaration","ArrowFunction","MethodDeclaration","ForStatement"]);function vO(t){return(e,i)=>{let n=e.node.getChild("VariableDefinition");return n&&i(n,t),!0}}const bO=["FunctionDeclaration"],yO={FunctionDeclaration:vO("function"),ClassDeclaration:vO("class"),ClassExpression:()=>!0,EnumDeclaration:vO("constant"),TypeAliasDeclaration:vO("type"),NamespaceDeclaration:vO("namespace"),VariableDefinition(t,e){t.matchContext(bO)||e(t,"variable")},TypeDefinition(t,e){e(t,"type")},__proto__:null};function SO(t,e){let i=mO.get(e);if(i)return i;let n=[],s=!0;function r(e,i){let s=t.sliceString(e.from,e.to);n.push({label:s,type:i})}return e.cursor(ra.IncludeAnonymous).iterate((e=>{if(s)s=!1;else if(e.name){let t=yO[e.name];if(t&&t(e,r)||wO.has(e.name))return!1}else if(e.to-e.from>8192){for(let i of SO(t,e.node))n.push(i);return!1}})),mO.set(e,n),n}const xO=/^[\w$\xa1-\uffff][\w$\d\xa1-\uffff]*$/,kO=["TemplateString","String","RegExp","LineComment","BlockComment","VariableDefinition","TypeDefinition","Label","PropertyDefinition","PropertyName","PrivatePropertyDefinition","PrivatePropertyName",".","?."];function QO(t){let e=cl(t.state).resolveInner(t.pos,-1);if(kO.indexOf(e.name)>-1)return null;let i="VariableName"==e.name||e.to-e.from<20&&xO.test(t.state.sliceDoc(e.from,e.to));if(!i&&!t.explicit)return null;let n=[];for(let i=e;i;i=i.parent)wO.has(i.name)&&(n=n.concat(SO(t.state.doc,i)));return{options:n,from:i?e.from:t.pos,validFor:xO}}function $O(t,e,i){var n;let s=[];for(;;){let r,o=e.firstChild;if("VariableName"==(null==o?void 0:o.name))return s.push(t(o)),{path:s.reverse(),name:i};if("MemberExpression"!=(null==o?void 0:o.name)||"PropertyName"!=(null===(n=r=o.lastChild)||void 0===n?void 0:n.name))return null;s.push(t(r)),e=o}}function PO(t){let e=e=>t.state.doc.sliceString(e.from,e.to),i=cl(t.state).resolveInner(t.pos,-1);return"PropertyName"==i.name?$O(e,i.parent,e(i)):"."!=i.name&&"?."!=i.name||"MemberExpression"!=i.parent.name?kO.indexOf(i.name)>-1?null:"VariableName"==i.name||i.to-i.from<20&&xO.test(e(i))?{path:[],name:e(i)}:"MemberExpression"==i.name?$O(e,i,""):t.explicit?{path:[],name:""}:null:$O(e,i.parent,"")}const ZO=hl.define({name:"javascript",parser:pO.configure({props:[Cl.add({IfStatement:Dl({except:/^\s*({|else\b)/}),TryStatement:Dl({except:/^\s*({|catch\b|finally\b)/}),LabeledStatement:jl,SwitchBody:t=>{let e=t.textAfter,i=/^\s*\}/.test(e),n=/^\s*(case|default)\b/.test(e);return t.baseIndent+(i?0:n?1:2)*t.unit},Block:Wl({closing:"}"}),ArrowFunction:t=>t.baseIndent+t.unit,"TemplateString BlockComment":()=>null,"Statement Property":Dl({except:/^{/}),JSXElement(t){let e=/^\s*<\//.test(t.textAfter);return t.lineIndent(t.node.from)+(e?0:t.unit)},JSXEscape(t){let e=/\s*\}/.test(t.textAfter);return t.lineIndent(t.node.from)+(e?0:t.unit)},"JSXOpenTag JSXSelfClosingTag":t=>t.column(t.node.from)+t.unit}),_l.add({"Block ClassBody SwitchBody EnumBody ObjectExpression ArrayExpression ObjectType":Vl,BlockComment:t=>({from:t.from+2,to:t.to-2})})]}),languageData:{closeBrackets:{brackets:["(","[","{","'",'"',"`"]},commentTokens:{line:"//",block:{open:"/*",close:"*/"}},indentOnInput:/^\s*(?:case |default:|\{|\}|<\/)$/,wordChars:"$"}}),CO={test:t=>/^JSX/.test(t.name),facet:rl({commentTokens:{block:{open:"{/*",close:"*/}"}}})},TO=ZO.configure({dialect:"ts"},"typescript"),AO=ZO.configure({dialect:"jsx",props:[ol.add((t=>t.isTop?[CO]:void 0))]}),RO=ZO.configure({dialect:"jsx ts",props:[ol.add((t=>t.isTop?[CO]:void 0))]},"typescript");let XO=t=>({label:t,type:"keyword"});const YO="break case const continue default delete export extends false finally in instanceof let new return static super switch this throw true typeof var yield".split(" ").map(XO),WO=YO.concat(["declare","implements","private","protected","public"].map(XO));function MO(t,e,i=t.length){for(let n=null==e?void 0:e.firstChild;n;n=n.nextSibling)if("JSXIdentifier"==n.name||"JSXBuiltin"==n.name||"JSXNamespacedName"==n.name||"JSXMemberExpression"==n.name)return t.sliceString(n.from,Math.min(n.to,i));return""}const jO="object"==typeof navigator&&/Android\b/.test(navigator.userAgent),DO=Gs.inputHandler.of(((t,e,i,n,s)=>{if((jO?t.composing:t.compositionStarted)||t.state.readOnly||e!=i||">"!=n&&"/"!=n||!ZO.isActiveAt(t.state,e,-1))return!1;let r=s(),{state:o}=r,a=o.changeByRange((t=>{var e;let i,{head:s}=t,r=cl(o).resolveInner(s-1,-1);if("JSXStartTag"==r.name&&(r=r.parent),o.doc.sliceString(s-1,s)!=n||"JSXAttributeValue"==r.name&&r.to>s);else{if(">"==n&&"JSXFragmentTag"==r.name)return{range:t,changes:{from:s,insert:"</>"}};if("/"==n&&"JSXStartCloseTag"==r.name){let t=r.parent,n=t.parent;if(n&&t.from==s-2&&((i=MO(o.doc,n.firstChild,s))||"JSXFragmentTag"==(null===(e=n.firstChild)||void 0===e?void 0:e.name))){let t=`${i}>`;return{range:Y.cursor(s+t.length,-1),changes:{from:s,insert:t}}}}else if(">"==n){let e=function(t){for(;;){if("JSXOpenTag"==t.name||"JSXSelfClosingTag"==t.name||"JSXFragmentTag"==t.name)return t;if("JSXEscape"==t.name||!t.parent)return null;t=t.parent}}(r);if(e&&!/^\/?>|^<\//.test(o.doc.sliceString(s,s+2))&&(i=MO(o.doc,e,s)))return{range:t,changes:{from:s,insert:`</${i}>`}}}}return{range:t}}));return!a.changes.empty&&(t.dispatch([r,o.update(a,{userEvent:"input.complete",scrollIntoView:!0})]),!0)}));function EO(t,e,i,n){return i.line(t+n.line).from+e+(1==t?n.col-1:-1)}function qO(t,e,i){let n=EO(t.line,t.column,e,i),s={from:n,to:null!=t.endLine&&1!=t.endColumn?EO(t.endLine,t.endColumn,e,i):n,message:t.message,source:t.ruleId?"eslint:"+t.ruleId:"eslint",severity:1==t.severity?"warning":"error"};if(t.fix){let{range:e,text:r}=t.fix,o=e[0]+i.pos-n,a=e[1]+i.pos-n;s.actions=[{name:"fix",apply(t,e){t.dispatch({changes:{from:e+o,to:e+a,insert:r},scrollIntoView:!0})}}]}return s}var _O=Object.freeze({__proto__:null,autoCloseTags:DO,completionPath:PO,esLint:function(t,e){return e||(e={parserOptions:{ecmaVersion:2019,sourceType:"module"},env:{browser:!0,node:!0,es6:!0,es2015:!0,es2017:!0,es2020:!0},rules:{}},t.getRules().forEach(((t,i)=>{t.meta.docs.recommended&&(e.rules[i]=2)}))),i=>{let{state:n}=i,s=[];for(let{from:i,to:r}of ZO.findRegions(n)){let o=n.doc.lineAt(i),a={line:o.number-1,col:i-o.from,pos:i};for(let o of t.verify(n.sliceDoc(i,r),e))s.push(qO(o,n.doc,a))}return s}},javascript:function(t={}){let e=t.jsx?t.typescript?RO:AO:t.typescript?TO:ZO,i=t.typescript?gO.concat(WO):OO.concat(YO);return new yl(e,[ZO.data.of({autocomplete:(n=kO,s=zu(i),t=>{for(let e=cl(t.state).resolveInner(t.pos,-1);e;e=e.parent){if(n.indexOf(e.name)>-1)return null;if(e.type.isTop)break}return s(t)})}),ZO.data.of({autocomplete:QO}),t.jsx?DO:[]]);var n,s},javascriptLanguage:ZO,jsxLanguage:AO,localCompletionSource:QO,scopeCompletionSource:function(t){let e=new Map;return i=>{let n=PO(i);if(!n)return null;let s=t;for(let t of n.path)if(s=s[t],!s)return null;let r=e.get(s);return r||e.set(s,r=function(t,e){let i=[],n=new Set;for(let s=0;;s++){for(let r of(Object.getOwnPropertyNames||Object.keys)(t)){if(!/^[a-zA-Z_$\xaa-\uffdc][\w$\xaa-\uffdc]*$/.test(r)||n.has(r))continue;let o;n.add(r);try{o=t[r]}catch(t){continue}i.push({label:r,type:"function"==typeof o?/^[A-Z]/.test(r)?"class":e?"function":"method":e?"variable":"property",boost:-s})}let r=Object.getPrototypeOf(t);if(!r)return i;t=r}}(s,!n.path.length)),{from:i.pos-n.name.length,options:r,validFor:xO}}},snippets:OO,tsxLanguage:RO,typescriptLanguage:TO,typescriptSnippets:gO});return t.codemirror=Tp,t.codemirrorLangJavascript=_O,t.codemirrorLanguage=cc,t.codemirrorState=Bt,t.codemirrorView=No,t.lezerHighlight=nl,t}({});
diff --git a/devtools/client/shared/sourceeditor/codemirror6/index.mjs b/devtools/client/shared/sourceeditor/codemirror6/index.mjs
new file mode 100644
index 0000000000..a1a03124ce
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror6/index.mjs
@@ -0,0 +1,22 @@
+/* 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/. */
+
+import * as codemirror from "codemirror";
+import * as codemirrorView from "@codemirror/view";
+import * as codemirrorState from "@codemirror/state";
+import * as codemirrorLanguage from "@codemirror/language";
+import * as codemirrorLangJavascript from "@codemirror/lang-javascript";
+import * as lezerHighlight from "@lezer/highlight";
+
+// XXX When adding new exports, you need to update the codemirror6.bundle.js file,
+// running `npm install && npm run build-cm6` from the devtools/client/shared/sourceeditor folder
+
+export {
+ codemirror,
+ codemirrorView,
+ codemirrorState,
+ codemirrorLanguage,
+ codemirrorLangJavascript,
+ lezerHighlight,
+};
diff --git a/devtools/client/shared/sourceeditor/codemirror6/moz.build b/devtools/client/shared/sourceeditor/codemirror6/moz.build
new file mode 100644
index 0000000000..4d66cfabea
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/codemirror6/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "codemirror6.bundle.js",
+)
+
+with Files("**"):
+ BUG_COMPONENT = ("DevTools", "Source Editor")
diff --git a/devtools/client/shared/sourceeditor/css-autocompleter.js b/devtools/client/shared/sourceeditor/css-autocompleter.js
new file mode 100644
index 0000000000..7db3cbbc0f
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/css-autocompleter.js
@@ -0,0 +1,1248 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ cssTokenizer,
+ cssTokenizerWithLineColumn,
+} = require("resource://devtools/shared/css/parsing-utils.js");
+
+/**
+ * Here is what this file (+ css-parsing-utils.js) do.
+ *
+ * The main objective here is to provide as much suggestions to the user editing
+ * a stylesheet in Style Editor. The possible things that can be suggested are:
+ * - CSS property names
+ * - CSS property values
+ * - CSS Selectors
+ * - Some other known CSS keywords
+ *
+ * Gecko provides a list of both property names and their corresponding values.
+ * We take out a list of matching selectors using the Inspector actor's
+ * `getSuggestionsForQuery` method. Now the only thing is to parse the CSS being
+ * edited by the user, figure out what token or word is being written and last
+ * but the most difficult, what is being edited.
+ *
+ * The file 'css-parsing-utils' helps to convert the CSS into meaningful tokens,
+ * each having a certain type associated with it. These tokens help us to figure
+ * out the currently edited word and to write a CSS state machine to figure out
+ * what the user is currently editing. By that, I mean, whether he is editing a
+ * selector or a property or a value, or even fine grained information like an
+ * id in the selector.
+ *
+ * The `resolveState` method iterated over the tokens spitted out by the
+ * tokenizer, using switch cases, follows a state machine logic and finally
+ * figures out these informations:
+ * - The state of the CSS at the cursor (one out of CSS_STATES)
+ * - The current token that is being edited `cmpleting`
+ * - If the state is "selector", the selector state (one of SELECTOR_STATES)
+ * - If the state is "selector", the current selector till the cursor
+ * - If the state is "value", the corresponding property name
+ *
+ * In case of "value" and "property" states, we simply use the information
+ * provided by Gecko to filter out the possible suggestions.
+ * For "selector" state, we request the Inspector actor to query the page DOM
+ * and filter out the possible suggestions.
+ * For "media" and "keyframes" state, the only possible suggestions for now are
+ * "media" and "keyframes" respectively, although "media" can have suggestions
+ * like "max-width", "orientation" etc. Similarly "value" state can also have
+ * much better logical suggestions if we fine grain identify a sub state just
+ * like we do for the "selector" state.
+ */
+
+// Autocompletion types.
+
+const CSS_STATES = {
+ null: "null",
+ property: "property", // foo { bar|: … }
+ value: "value", // foo {bar: baz|}
+ selector: "selector", // f| {bar: baz}
+ media: "media", // @med| , or , @media scr| { }
+ keyframes: "keyframes", // @keyf|
+ frame: "frame", // @keyframs foobar { t|
+};
+
+const SELECTOR_STATES = {
+ null: "null",
+ id: "id", // #f|
+ class: "class", // #foo.b|
+ tag: "tag", // fo|
+ pseudo: "pseudo", // foo:|
+ attribute: "attribute", // foo[b|
+ value: "value", // foo[bar=b|
+};
+
+/**
+ * Constructor for the autocompletion object.
+ *
+ * @param options {Object} An options object containing the following options:
+ * - walker {Object} The object used for query selecting from the current
+ * target's DOM.
+ * - maxEntries {Number} Maximum selectors suggestions to display.
+ * - cssProperties {Object} The database of CSS properties.
+ */
+function CSSCompleter(options = {}) {
+ this.walker = options.walker;
+ this.maxEntries = options.maxEntries || 15;
+ this.cssProperties = options.cssProperties;
+
+ this.propertyNames = this.cssProperties.getNames().sort();
+
+ // Array containing the [line, ch, scopeStack] for the locations where the
+ // CSS state is "null"
+ this.nullStates = [];
+}
+
+CSSCompleter.prototype = {
+ /**
+ * Returns a list of suggestions based on the caret position.
+ *
+ * @param source {String} String of the source code.
+ * @param caret {Object} Cursor location with line and ch properties.
+ *
+ * @returns [{object}] A sorted list of objects containing the following
+ * peroperties:
+ * - label {String} Full keyword for the suggestion
+ * - preLabel {String} Already entered part of the label
+ */
+ complete(source, caret) {
+ // Getting the context from the caret position.
+ if (!this.resolveState(source, caret)) {
+ // We couldn't resolve the context, we won't be able to complete.
+ return Promise.resolve([]);
+ }
+
+ // Properly suggest based on the state.
+ switch (this.state) {
+ case CSS_STATES.property:
+ return this.completeProperties(this.completing);
+
+ case CSS_STATES.value:
+ return this.completeValues(this.propertyName, this.completing);
+
+ case CSS_STATES.selector:
+ return this.suggestSelectors();
+
+ case CSS_STATES.media:
+ case CSS_STATES.keyframes:
+ if ("media".startsWith(this.completing)) {
+ return Promise.resolve([
+ {
+ label: "media",
+ preLabel: this.completing,
+ text: "media",
+ },
+ ]);
+ } else if ("keyframes".startsWith(this.completing)) {
+ return Promise.resolve([
+ {
+ label: "keyframes",
+ preLabel: this.completing,
+ text: "keyframes",
+ },
+ ]);
+ }
+ }
+ return Promise.resolve([]);
+ },
+
+ /**
+ * Resolves the state of CSS at the cursor location. This method implements a
+ * custom written CSS state machine. The various switch statements provide the
+ * transition rules for the state. It also finds out various informatino about
+ * the nearby CSS like the property name being completed, the complete
+ * selector, etc.
+ *
+ * @param source {String} String of the source code.
+ * @param caret {Object} Cursor location with line and ch properties.
+ *
+ * @returns CSS_STATE
+ * One of CSS_STATE enum or null if the state cannot be resolved.
+ */
+ // eslint-disable-next-line complexity
+ resolveState(source, { line, ch }) {
+ // Function to return the last element of an array
+ const peek = arr => arr[arr.length - 1];
+ // _state can be one of CSS_STATES;
+ let _state = CSS_STATES.null;
+ let selector = "";
+ let selectorState = SELECTOR_STATES.null;
+ let propertyName = null;
+ let scopeStack = [];
+ let selectors = [];
+
+ // Fetch the closest null state line, ch from cached null state locations
+ const matchedStateIndex = this.findNearestNullState(line);
+ if (matchedStateIndex > -1) {
+ const state = this.nullStates[matchedStateIndex];
+ line -= state[0];
+ if (line == 0) {
+ ch -= state[1];
+ }
+ source = source.split("\n").slice(state[0]);
+ source[0] = source[0].slice(state[1]);
+ source = source.join("\n");
+ scopeStack = [...state[2]];
+ this.nullStates.length = matchedStateIndex + 1;
+ } else {
+ this.nullStates = [];
+ }
+ const tokens = cssTokenizerWithLineColumn(source);
+ const tokIndex = tokens.length - 1;
+ if (
+ tokIndex >= 0 &&
+ (tokens[tokIndex].loc.end.line < line ||
+ (tokens[tokIndex].loc.end.line === line &&
+ tokens[tokIndex].loc.end.column < ch))
+ ) {
+ // If the last token ends before the cursor location, we didn't
+ // tokenize it correctly. This special case can happen if the
+ // final token is a comment.
+ return null;
+ }
+
+ let cursor = 0;
+ // This will maintain a stack of paired elements like { & }, @m & }, : & ;
+ // etc
+ let token = null;
+ let selectorBeforeNot = null;
+ while (cursor <= tokIndex && (token = tokens[cursor++])) {
+ switch (_state) {
+ case CSS_STATES.property:
+ // From CSS_STATES.property, we can either go to CSS_STATES.value
+ // state when we hit the first ':' or CSS_STATES.selector if "}" is
+ // reached.
+ if (token.tokenType === "symbol") {
+ switch (token.text) {
+ case ":":
+ scopeStack.push(":");
+ if (tokens[cursor - 2].tokenType != "whitespace") {
+ propertyName = tokens[cursor - 2].text;
+ } else {
+ propertyName = tokens[cursor - 3].text;
+ }
+ _state = CSS_STATES.value;
+ break;
+
+ case "}":
+ if (/[{f]/.test(peek(scopeStack))) {
+ const popped = scopeStack.pop();
+ if (popped == "f") {
+ _state = CSS_STATES.frame;
+ } else {
+ selector = "";
+ selectors = [];
+ _state = CSS_STATES.null;
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case CSS_STATES.value:
+ // From CSS_STATES.value, we can go to one of CSS_STATES.property,
+ // CSS_STATES.frame, CSS_STATES.selector and CSS_STATES.null
+ if (token.tokenType === "symbol") {
+ switch (token.text) {
+ case ";":
+ if (/[:]/.test(peek(scopeStack))) {
+ scopeStack.pop();
+ _state = CSS_STATES.property;
+ }
+ break;
+
+ case "}":
+ if (peek(scopeStack) == ":") {
+ scopeStack.pop();
+ }
+
+ if (/[{f]/.test(peek(scopeStack))) {
+ const popped = scopeStack.pop();
+ if (popped == "f") {
+ _state = CSS_STATES.frame;
+ } else {
+ selector = "";
+ selectors = [];
+ _state = CSS_STATES.null;
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case CSS_STATES.selector:
+ // From CSS_STATES.selector, we can only go to CSS_STATES.property
+ // when we hit "{"
+ if (token.tokenType === "symbol" && token.text == "{") {
+ scopeStack.push("{");
+ _state = CSS_STATES.property;
+ selectors.push(selector);
+ selector = "";
+ break;
+ }
+
+ switch (selectorState) {
+ case SELECTOR_STATES.id:
+ case SELECTOR_STATES.class:
+ case SELECTOR_STATES.tag:
+ switch (token.tokenType) {
+ case "hash":
+ case "id":
+ selectorState = SELECTOR_STATES.id;
+ selector += "#" + token.text;
+ break;
+
+ case "symbol":
+ if (token.text == ".") {
+ selectorState = SELECTOR_STATES.class;
+ selector += ".";
+ if (
+ cursor <= tokIndex &&
+ tokens[cursor].tokenType == "ident"
+ ) {
+ token = tokens[cursor++];
+ selector += token.text;
+ }
+ } else if (token.text == "#") {
+ selectorState = SELECTOR_STATES.id;
+ selector += "#";
+ } else if (/[>~+]/.test(token.text)) {
+ selectorState = SELECTOR_STATES.null;
+ selector += token.text;
+ } else if (token.text == ",") {
+ selectorState = SELECTOR_STATES.null;
+ selectors.push(selector);
+ selector = "";
+ } else if (token.text == ":") {
+ selectorState = SELECTOR_STATES.pseudo;
+ selector += ":";
+ if (cursor > tokIndex) {
+ break;
+ }
+
+ token = tokens[cursor++];
+ switch (token.tokenType) {
+ case "function":
+ if (token.text == "not") {
+ selectorBeforeNot = selector;
+ selector = "";
+ scopeStack.push("(");
+ } else {
+ selector += token.text + "(";
+ }
+ selectorState = SELECTOR_STATES.null;
+ break;
+
+ case "ident":
+ selector += token.text;
+ break;
+ }
+ } else if (token.text == "[") {
+ selectorState = SELECTOR_STATES.attribute;
+ scopeStack.push("[");
+ selector += "[";
+ } else if (token.text == ")") {
+ if (peek(scopeStack) == "(") {
+ scopeStack.pop();
+ selector = selectorBeforeNot + "not(" + selector + ")";
+ selectorBeforeNot = null;
+ } else {
+ selector += ")";
+ }
+ selectorState = SELECTOR_STATES.null;
+ }
+ break;
+
+ case "whitespace":
+ selectorState = SELECTOR_STATES.null;
+ selector && (selector += " ");
+ break;
+ }
+ break;
+
+ case SELECTOR_STATES.null:
+ // From SELECTOR_STATES.null state, we can go to one of
+ // SELECTOR_STATES.id, SELECTOR_STATES.class or
+ // SELECTOR_STATES.tag
+ switch (token.tokenType) {
+ case "hash":
+ case "id":
+ selectorState = SELECTOR_STATES.id;
+ selector += "#" + token.text;
+ break;
+
+ case "ident":
+ selectorState = SELECTOR_STATES.tag;
+ selector += token.text;
+ break;
+
+ case "symbol":
+ if (token.text == ".") {
+ selectorState = SELECTOR_STATES.class;
+ selector += ".";
+ if (
+ cursor <= tokIndex &&
+ tokens[cursor].tokenType == "ident"
+ ) {
+ token = tokens[cursor++];
+ selector += token.text;
+ }
+ } else if (token.text == "#") {
+ selectorState = SELECTOR_STATES.id;
+ selector += "#";
+ } else if (token.text == "*") {
+ selectorState = SELECTOR_STATES.tag;
+ selector += "*";
+ } else if (/[>~+]/.test(token.text)) {
+ selector += token.text;
+ } else if (token.text == ",") {
+ selectorState = SELECTOR_STATES.null;
+ selectors.push(selector);
+ selector = "";
+ } else if (token.text == ":") {
+ selectorState = SELECTOR_STATES.pseudo;
+ selector += ":";
+ if (cursor > tokIndex) {
+ break;
+ }
+
+ token = tokens[cursor++];
+ switch (token.tokenType) {
+ case "function":
+ if (token.text == "not") {
+ selectorBeforeNot = selector;
+ selector = "";
+ scopeStack.push("(");
+ } else {
+ selector += token.text + "(";
+ }
+ selectorState = SELECTOR_STATES.null;
+ break;
+
+ case "ident":
+ selector += token.text;
+ break;
+ }
+ } else if (token.text == "[") {
+ selectorState = SELECTOR_STATES.attribute;
+ scopeStack.push("[");
+ selector += "[";
+ } else if (token.text == ")") {
+ if (peek(scopeStack) == "(") {
+ scopeStack.pop();
+ selector = selectorBeforeNot + "not(" + selector + ")";
+ selectorBeforeNot = null;
+ } else {
+ selector += ")";
+ }
+ selectorState = SELECTOR_STATES.null;
+ }
+ break;
+
+ case "whitespace":
+ selector && (selector += " ");
+ break;
+ }
+ break;
+
+ case SELECTOR_STATES.pseudo:
+ switch (token.tokenType) {
+ case "symbol":
+ if (/[>~+]/.test(token.text)) {
+ selectorState = SELECTOR_STATES.null;
+ selector += token.text;
+ } else if (token.text == ",") {
+ selectorState = SELECTOR_STATES.null;
+ selectors.push(selector);
+ selector = "";
+ } else if (token.text == ":") {
+ selectorState = SELECTOR_STATES.pseudo;
+ selector += ":";
+ if (cursor > tokIndex) {
+ break;
+ }
+
+ token = tokens[cursor++];
+ switch (token.tokenType) {
+ case "function":
+ if (token.text == "not") {
+ selectorBeforeNot = selector;
+ selector = "";
+ scopeStack.push("(");
+ } else {
+ selector += token.text + "(";
+ }
+ selectorState = SELECTOR_STATES.null;
+ break;
+
+ case "ident":
+ selector += token.text;
+ break;
+ }
+ } else if (token.text == "[") {
+ selectorState = SELECTOR_STATES.attribute;
+ scopeStack.push("[");
+ selector += "[";
+ }
+ break;
+
+ case "whitespace":
+ selectorState = SELECTOR_STATES.null;
+ selector && (selector += " ");
+ break;
+ }
+ break;
+
+ case SELECTOR_STATES.attribute:
+ switch (token.tokenType) {
+ case "symbol":
+ if (/[~|^$*]/.test(token.text)) {
+ selector += token.text;
+ token = tokens[cursor++];
+ } else if (token.text == "=") {
+ selectorState = SELECTOR_STATES.value;
+ selector += token.text;
+ } else if (token.text == "]") {
+ if (peek(scopeStack) == "[") {
+ scopeStack.pop();
+ }
+
+ selectorState = SELECTOR_STATES.null;
+ selector += "]";
+ }
+ break;
+
+ case "ident":
+ case "string":
+ selector += token.text;
+ break;
+
+ case "whitespace":
+ selector && (selector += " ");
+ break;
+ }
+ break;
+
+ case SELECTOR_STATES.value:
+ switch (token.tokenType) {
+ case "string":
+ case "ident":
+ selector += token.text;
+ break;
+
+ case "symbol":
+ if (token.text == "]") {
+ if (peek(scopeStack) == "[") {
+ scopeStack.pop();
+ }
+
+ selectorState = SELECTOR_STATES.null;
+ selector += "]";
+ }
+ break;
+
+ case "whitespace":
+ selector && (selector += " ");
+ break;
+ }
+ break;
+ }
+ break;
+
+ case CSS_STATES.null:
+ // From CSS_STATES.null state, we can go to either CSS_STATES.media or
+ // CSS_STATES.selector.
+ switch (token.tokenType) {
+ case "hash":
+ case "id":
+ selectorState = SELECTOR_STATES.id;
+ selector = "#" + token.text;
+ _state = CSS_STATES.selector;
+ break;
+
+ case "ident":
+ selectorState = SELECTOR_STATES.tag;
+ selector = token.text;
+ _state = CSS_STATES.selector;
+ break;
+
+ case "symbol":
+ if (token.text == ".") {
+ selectorState = SELECTOR_STATES.class;
+ selector = ".";
+ _state = CSS_STATES.selector;
+ if (cursor <= tokIndex && tokens[cursor].tokenType == "ident") {
+ token = tokens[cursor++];
+ selector += token.text;
+ }
+ } else if (token.text == "#") {
+ selectorState = SELECTOR_STATES.id;
+ selector = "#";
+ _state = CSS_STATES.selector;
+ } else if (token.text == "*") {
+ selectorState = SELECTOR_STATES.tag;
+ selector = "*";
+ _state = CSS_STATES.selector;
+ } else if (token.text == ":") {
+ _state = CSS_STATES.selector;
+ selectorState = SELECTOR_STATES.pseudo;
+ selector += ":";
+ if (cursor > tokIndex) {
+ break;
+ }
+
+ token = tokens[cursor++];
+ switch (token.tokenType) {
+ case "function":
+ if (token.text == "not") {
+ selectorBeforeNot = selector;
+ selector = "";
+ scopeStack.push("(");
+ } else {
+ selector += token.text + "(";
+ }
+ selectorState = SELECTOR_STATES.null;
+ break;
+
+ case "ident":
+ selector += token.text;
+ break;
+ }
+ } else if (token.text == "[") {
+ _state = CSS_STATES.selector;
+ selectorState = SELECTOR_STATES.attribute;
+ scopeStack.push("[");
+ selector += "[";
+ } else if (token.text == "}") {
+ if (peek(scopeStack) == "@m") {
+ scopeStack.pop();
+ }
+ }
+ break;
+
+ case "at":
+ _state = token.text.startsWith("m")
+ ? CSS_STATES.media
+ : CSS_STATES.keyframes;
+ break;
+ }
+ break;
+
+ case CSS_STATES.media:
+ // From CSS_STATES.media, we can only go to CSS_STATES.null state when
+ // we hit the first '{'
+ if (token.tokenType == "symbol" && token.text == "{") {
+ scopeStack.push("@m");
+ _state = CSS_STATES.null;
+ }
+ break;
+
+ case CSS_STATES.keyframes:
+ // From CSS_STATES.keyframes, we can only go to CSS_STATES.frame state
+ // when we hit the first '{'
+ if (token.tokenType == "symbol" && token.text == "{") {
+ scopeStack.push("@k");
+ _state = CSS_STATES.frame;
+ }
+ break;
+
+ case CSS_STATES.frame:
+ // From CSS_STATES.frame, we can either go to CSS_STATES.property
+ // state when we hit the first '{' or to CSS_STATES.selector when we
+ // hit '}'
+ if (token.tokenType == "symbol") {
+ if (token.text == "{") {
+ scopeStack.push("f");
+ _state = CSS_STATES.property;
+ } else if (token.text == "}") {
+ if (peek(scopeStack) == "@k") {
+ scopeStack.pop();
+ }
+
+ _state = CSS_STATES.null;
+ }
+ }
+ break;
+ }
+ if (_state == CSS_STATES.null) {
+ if (!this.nullStates.length) {
+ this.nullStates.push([
+ token.loc.end.line,
+ token.loc.end.column,
+ [...scopeStack],
+ ]);
+ continue;
+ }
+ let tokenLine = token.loc.end.line;
+ const tokenCh = token.loc.end.column;
+ if (tokenLine == 0) {
+ continue;
+ }
+ if (matchedStateIndex > -1) {
+ tokenLine += this.nullStates[matchedStateIndex][0];
+ }
+ this.nullStates.push([tokenLine, tokenCh, [...scopeStack]]);
+ }
+ }
+ this.state = _state;
+ this.propertyName = _state == CSS_STATES.value ? propertyName : null;
+ this.selectorState = _state == CSS_STATES.selector ? selectorState : null;
+ this.selectorBeforeNot =
+ selectorBeforeNot == null ? null : selectorBeforeNot;
+ if (token) {
+ selector = selector.slice(0, selector.length + token.loc.end.column - ch);
+ this.selector = selector;
+ } else {
+ this.selector = "";
+ }
+ this.selectors = selectors;
+
+ if (token && token.tokenType != "whitespace") {
+ let text;
+ if (token.tokenType == "dimension" || !token.text) {
+ text = source.substring(token.startOffset, token.endOffset);
+ } else {
+ text = token.text;
+ }
+ this.completing = text
+ .slice(0, ch - token.loc.start.column)
+ .replace(/^[.#]$/, "");
+ } else {
+ this.completing = "";
+ }
+ // Special case the situation when the user just entered ":" after typing a
+ // property name.
+ if (this.completing == ":" && _state == CSS_STATES.value) {
+ this.completing = "";
+ }
+
+ // Special check for !important; case.
+ if (
+ token &&
+ tokens[cursor - 2] &&
+ tokens[cursor - 2].text == "!" &&
+ this.completing == "important".slice(0, this.completing.length)
+ ) {
+ this.completing = "!" + this.completing;
+ }
+ return _state;
+ },
+
+ /**
+ * Queries the DOM Walker actor for suggestions regarding the selector being
+ * completed
+ */
+ suggestSelectors() {
+ const walker = this.walker;
+ if (!walker) {
+ return Promise.resolve([]);
+ }
+
+ let query = this.selector;
+ // Even though the selector matched atleast one node, there is still
+ // possibility of suggestions.
+ switch (this.selectorState) {
+ case SELECTOR_STATES.null:
+ if (this.completing === ",") {
+ return Promise.resolve([]);
+ }
+
+ query += "*";
+ break;
+
+ case SELECTOR_STATES.tag:
+ query = query.slice(0, query.length - this.completing.length);
+ break;
+
+ case SELECTOR_STATES.id:
+ case SELECTOR_STATES.class:
+ case SELECTOR_STATES.pseudo:
+ if (/^[.:#]$/.test(this.completing)) {
+ query = query.slice(0, query.length - this.completing.length);
+ this.completing = "";
+ } else {
+ query = query.slice(0, query.length - this.completing.length - 1);
+ }
+ break;
+ }
+
+ if (
+ /[\s+>~]$/.test(query) &&
+ this.selectorState != SELECTOR_STATES.attribute &&
+ this.selectorState != SELECTOR_STATES.value
+ ) {
+ query += "*";
+ }
+
+ // Set the values that this request was supposed to suggest to.
+ this._currentQuery = query;
+ return walker
+ .getSuggestionsForQuery(query, this.completing, this.selectorState)
+ .then(result => this.prepareSelectorResults(result));
+ },
+
+ /**
+ * Prepares the selector suggestions returned by the walker actor.
+ */
+ prepareSelectorResults(result) {
+ if (this._currentQuery != result.query) {
+ return [];
+ }
+
+ result = result.suggestions;
+ const query = this.selector;
+ const completion = [];
+ for (let [value, count, state] of result) {
+ switch (this.selectorState) {
+ case SELECTOR_STATES.id:
+ case SELECTOR_STATES.class:
+ case SELECTOR_STATES.pseudo:
+ if (/^[.:#]$/.test(this.completing)) {
+ value =
+ query.slice(0, query.length - this.completing.length) + value;
+ } else {
+ value =
+ query.slice(0, query.length - this.completing.length - 1) + value;
+ }
+ break;
+
+ case SELECTOR_STATES.tag:
+ value = query.slice(0, query.length - this.completing.length) + value;
+ break;
+
+ case SELECTOR_STATES.null:
+ value = query + value;
+ break;
+
+ default:
+ value = query.slice(0, query.length - this.completing.length) + value;
+ }
+
+ const item = {
+ label: value,
+ preLabel: query,
+ text: value,
+ score: count,
+ };
+
+ // In case the query's state is tag and the item's state is id or class
+ // adjust the preLabel
+ if (
+ this.selectorState === SELECTOR_STATES.tag &&
+ state === SELECTOR_STATES.class
+ ) {
+ item.preLabel = "." + item.preLabel;
+ }
+ if (
+ this.selectorState === SELECTOR_STATES.tag &&
+ state === SELECTOR_STATES.id
+ ) {
+ item.preLabel = "#" + item.preLabel;
+ }
+
+ completion.push(item);
+
+ if (completion.length > this.maxEntries - 1) {
+ break;
+ }
+ }
+ return completion;
+ },
+
+ /**
+ * Returns CSS property name suggestions based on the input.
+ *
+ * @param startProp {String} Initial part of the property being completed.
+ */
+ completeProperties(startProp) {
+ const finalList = [];
+ if (!startProp) {
+ return Promise.resolve(finalList);
+ }
+
+ const length = this.propertyNames.length;
+ let i = 0,
+ count = 0;
+ for (; i < length && count < this.maxEntries; i++) {
+ if (this.propertyNames[i].startsWith(startProp)) {
+ count++;
+ const propName = this.propertyNames[i];
+ finalList.push({
+ preLabel: startProp,
+ label: propName,
+ text: propName + ": ",
+ });
+ } else if (this.propertyNames[i] > startProp) {
+ // We have crossed all possible matches alphabetically.
+ break;
+ }
+ }
+ return Promise.resolve(finalList);
+ },
+
+ /**
+ * Returns CSS value suggestions based on the corresponding property.
+ *
+ * @param propName {String} The property to which the value being completed
+ * belongs.
+ * @param startValue {String} Initial part of the value being completed.
+ */
+ completeValues(propName, startValue) {
+ const finalList = [];
+ const list = ["!important;", ...this.cssProperties.getValues(propName)];
+ // If there is no character being completed, we are showing an initial list
+ // of possible values. Skipping '!important' in this case.
+ if (!startValue) {
+ list.splice(0, 1);
+ }
+
+ const length = list.length;
+ let i = 0,
+ count = 0;
+ for (; i < length && count < this.maxEntries; i++) {
+ if (list[i].startsWith(startValue)) {
+ count++;
+ const value = list[i];
+ finalList.push({
+ preLabel: startValue,
+ label: value,
+ text: value,
+ });
+ } else if (list[i] > startValue) {
+ // We have crossed all possible matches alphabetically.
+ break;
+ }
+ }
+ return Promise.resolve(finalList);
+ },
+
+ /**
+ * A biased binary search in a sorted array where the middle element is
+ * calculated based on the values at the lower and the upper index in each
+ * iteration.
+ *
+ * This method returns the index of the closest null state from the passed
+ * `line` argument. Once we have the closest null state, we can start applying
+ * the state machine logic from that location instead of the absolute starting
+ * of the CSS source. This speeds up the tokenizing and the state machine a
+ * lot while using autocompletion at high line numbers in a CSS source.
+ */
+ findNearestNullState(line) {
+ const arr = this.nullStates;
+ let high = arr.length - 1;
+ let low = 0;
+ let target = 0;
+
+ if (high < 0) {
+ return -1;
+ }
+ if (arr[high][0] <= line) {
+ return high;
+ }
+ if (arr[low][0] > line) {
+ return -1;
+ }
+
+ while (high > low) {
+ if (arr[low][0] <= line && arr[low[0] + 1] > line) {
+ return low;
+ }
+ if (arr[high][0] > line && arr[high - 1][0] <= line) {
+ return high - 1;
+ }
+
+ target =
+ (((line - arr[low][0]) / (arr[high][0] - arr[low][0])) * (high - low)) |
+ 0;
+
+ if (arr[target][0] <= line && arr[target + 1][0] > line) {
+ return target;
+ } else if (line > arr[target][0]) {
+ low = target + 1;
+ high--;
+ } else {
+ high = target - 1;
+ low++;
+ }
+ }
+
+ return -1;
+ },
+
+ /**
+ * Invalidates the state cache for and above the line.
+ */
+ invalidateCache(line) {
+ this.nullStates.length = this.findNearestNullState(line) + 1;
+ },
+
+ /**
+ * Get the state information about a token surrounding the {line, ch} position
+ *
+ * @param {string} source
+ * The complete source of the CSS file. Unlike resolve state method,
+ * this method requires the full source.
+ * @param {object} caret
+ * The line, ch position of the caret.
+ *
+ * @returns {object}
+ * An object containing the state of token covered by the caret.
+ * The object has following properties when the the state is
+ * "selector", "value" or "property", null otherwise:
+ * - state {string} one of CSS_STATES - "selector", "value" etc.
+ * - selector {string} The selector at the caret when `state` is
+ * selector. OR
+ * - selectors {[string]} Array of selector strings in case when
+ * `state` is "value" or "property"
+ * - propertyName {string} The property name at the current caret or
+ * the property name corresponding to the value at
+ * the caret.
+ * - value {string} The css value at the current caret.
+ * - loc {object} An object containing the starting and the ending
+ * caret position of the whole selector, value or property.
+ * - { start: {line, ch}, end: {line, ch}}
+ */
+ getInfoAt(source, caret) {
+ // Limits the input source till the {line, ch} caret position
+ function limit(sourceArg, { line, ch }) {
+ line++;
+ const list = sourceArg.split("\n");
+ if (list.length < line) {
+ return sourceArg;
+ }
+ if (line == 1) {
+ return list[0].slice(0, ch);
+ }
+ return [...list.slice(0, line - 1), list[line - 1].slice(0, ch)].join(
+ "\n"
+ );
+ }
+
+ // Get the state at the given line, ch
+ const state = this.resolveState(limit(source, caret), caret);
+ const propertyName = this.propertyName;
+ let { line, ch } = caret;
+ const sourceArray = source.split("\n");
+ let limitedSource = limit(source, caret);
+
+ /**
+ * Method to traverse forwards from the caret location to figure out the
+ * ending point of a selector or css value.
+ *
+ * @param {function} check
+ * A method which takes the current state as an input and determines
+ * whether the state changed or not.
+ */
+ const traverseForward = check => {
+ let location;
+ // Backward loop to determine the beginning location of the selector.
+ do {
+ let lineText = sourceArray[line];
+ if (line == caret.line) {
+ lineText = lineText.substring(caret.ch);
+ }
+
+ let prevToken = undefined;
+ const tokens = cssTokenizer(lineText);
+ let found = false;
+ const ech = line == caret.line ? caret.ch : 0;
+ for (let token of tokens) {
+ // If the line is completely spaces, handle it differently
+ if (lineText.trim() == "") {
+ limitedSource += lineText;
+ } else {
+ limitedSource += sourceArray[line].substring(
+ ech + token.startOffset,
+ ech + token.endOffset
+ );
+ }
+
+ // Whitespace cannot change state.
+ if (token.tokenType == "whitespace") {
+ prevToken = token;
+ continue;
+ }
+
+ const forwState = this.resolveState(limitedSource, {
+ line,
+ ch: token.endOffset + ech,
+ });
+ if (check(forwState)) {
+ if (prevToken && prevToken.tokenType == "whitespace") {
+ token = prevToken;
+ }
+ location = {
+ line,
+ ch: token.startOffset + ech,
+ };
+ found = true;
+ break;
+ }
+ prevToken = token;
+ }
+ limitedSource += "\n";
+ if (found) {
+ break;
+ }
+ } while (line++ < sourceArray.length);
+ return location;
+ };
+
+ /**
+ * Method to traverse backwards from the caret location to figure out the
+ * starting point of a selector or css value.
+ *
+ * @param {function} check
+ * A method which takes the current state as an input and determines
+ * whether the state changed or not.
+ * @param {boolean} isValue
+ * true if the traversal is being done for a css value state.
+ */
+ const traverseBackwards = (check, isValue) => {
+ let location;
+ // Backward loop to determine the beginning location of the selector.
+ do {
+ let lineText = sourceArray[line];
+ if (line == caret.line) {
+ lineText = lineText.substring(0, caret.ch);
+ }
+
+ const tokens = Array.from(cssTokenizer(lineText));
+ let found = false;
+ for (let i = tokens.length - 1; i >= 0; i--) {
+ let token = tokens[i];
+ // If the line is completely spaces, handle it differently
+ if (lineText.trim() == "") {
+ limitedSource = limitedSource.slice(0, -1 * lineText.length);
+ } else {
+ const length = token.endOffset - token.startOffset;
+ limitedSource = limitedSource.slice(0, -1 * length);
+ }
+
+ // Whitespace cannot change state.
+ if (token.tokenType == "whitespace") {
+ continue;
+ }
+
+ const backState = this.resolveState(limitedSource, {
+ line,
+ ch: token.startOffset,
+ });
+ if (check(backState)) {
+ if (tokens[i + 1] && tokens[i + 1].tokenType == "whitespace") {
+ token = tokens[i + 1];
+ }
+ location = {
+ line,
+ ch: isValue ? token.endOffset : token.startOffset,
+ };
+ found = true;
+ break;
+ }
+ }
+ limitedSource = limitedSource.slice(0, -1);
+ if (found) {
+ break;
+ }
+ } while (line-- >= 0);
+ return location;
+ };
+
+ if (state == CSS_STATES.selector) {
+ // For selector state, the ending and starting point of the selector is
+ // either when the state changes or the selector becomes empty and a
+ // single selector can span multiple lines.
+ // Backward loop to determine the beginning location of the selector.
+ const start = traverseBackwards(backState => {
+ return (
+ backState != CSS_STATES.selector ||
+ (this.selector == "" && this.selectorBeforeNot == null)
+ );
+ });
+
+ line = caret.line;
+ limitedSource = limit(source, caret);
+ // Forward loop to determine the ending location of the selector.
+ const end = traverseForward(forwState => {
+ return (
+ forwState != CSS_STATES.selector ||
+ (this.selector == "" && this.selectorBeforeNot == null)
+ );
+ });
+
+ // Since we have start and end positions, figure out the whole selector.
+ let selector = source.split("\n").slice(start.line, end.line + 1);
+ selector[selector.length - 1] = selector[selector.length - 1].substring(
+ 0,
+ end.ch
+ );
+ selector[0] = selector[0].substring(start.ch);
+ selector = selector.join("\n");
+ return {
+ state,
+ selector,
+ loc: {
+ start,
+ end,
+ },
+ };
+ } else if (state == CSS_STATES.property) {
+ // A property can only be a single word and thus very easy to calculate.
+ const tokens = cssTokenizer(sourceArray[line]);
+ for (const token of tokens) {
+ // Note that, because we're tokenizing a single line, the
+ // token's offset is also the column number.
+ if (token.startOffset <= ch && token.endOffset >= ch) {
+ return {
+ state,
+ propertyName: token.text,
+ selectors: this.selectors,
+ loc: {
+ start: {
+ line,
+ ch: token.startOffset,
+ },
+ end: {
+ line,
+ ch: token.endOffset,
+ },
+ },
+ };
+ }
+ }
+ } else if (state == CSS_STATES.value) {
+ // CSS value can be multiline too, so we go forward and backwards to
+ // determine the bounds of the value at caret
+ const start = traverseBackwards(
+ backState => backState != CSS_STATES.value,
+ true
+ );
+
+ line = caret.line;
+ limitedSource = limit(source, caret);
+ const end = traverseForward(forwState => forwState != CSS_STATES.value);
+
+ let value = source.split("\n").slice(start.line, end.line + 1);
+ value[value.length - 1] = value[value.length - 1].substring(0, end.ch);
+ value[0] = value[0].substring(start.ch);
+ value = value.join("\n");
+ return {
+ state,
+ propertyName,
+ selectors: this.selectors,
+ value,
+ loc: {
+ start,
+ end,
+ },
+ };
+ }
+ return null;
+ },
+};
+
+module.exports = CSSCompleter;
diff --git a/devtools/client/shared/sourceeditor/editor-commands-controller.js b/devtools/client/shared/sourceeditor/editor-commands-controller.js
new file mode 100644
index 0000000000..25efad0850
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/editor-commands-controller.js
@@ -0,0 +1,97 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * The source editor exposes XUL commands that can be used when embedded in a XUL
+ * document. This controller drives the availability and behavior of the commands. When
+ * the editor input field is focused, this controller will update the matching menu item
+ * entries found in application menus or context menus.
+ */
+
+/**
+ * Returns a controller object that can be used for editor-specific commands:
+ * - find
+ * - find again
+ * - go to line
+ * - undo
+ * - redo
+ * - delete
+ * - select all
+ */
+function createController(ed) {
+ return {
+ supportsCommand(cmd) {
+ switch (cmd) {
+ case "cmd_find":
+ case "cmd_findAgain":
+ case "cmd_gotoLine":
+ case "cmd_undo":
+ case "cmd_redo":
+ case "cmd_delete":
+ case "cmd_selectAll":
+ return true;
+ }
+
+ return false;
+ },
+
+ isCommandEnabled(cmd) {
+ const cm = ed.codeMirror;
+
+ switch (cmd) {
+ case "cmd_find":
+ case "cmd_gotoLine":
+ case "cmd_selectAll":
+ return true;
+ case "cmd_findAgain":
+ return cm.state.search != null && cm.state.search.query != null;
+ case "cmd_undo":
+ return ed.canUndo();
+ case "cmd_redo":
+ return ed.canRedo();
+ case "cmd_delete":
+ return ed.somethingSelected();
+ }
+
+ return false;
+ },
+
+ doCommand(cmd) {
+ const cm = ed.codeMirror;
+
+ const map = {
+ cmd_selectAll: "selectAll",
+ cmd_find: "find",
+ cmd_undo: "undo",
+ cmd_redo: "redo",
+ cmd_delete: "delCharAfter",
+ cmd_findAgain: "findNext",
+ };
+
+ if (map[cmd]) {
+ cm.execCommand(map[cmd]);
+ return;
+ }
+
+ if (cmd == "cmd_gotoLine") {
+ ed.jumpToLine();
+ }
+ },
+
+ onEvent() {},
+ };
+}
+
+/**
+ * Create and insert a commands controller for the provided SourceEditor instance.
+ */
+function insertCommandsController(sourceEditor) {
+ const input = sourceEditor.codeMirror.getInputField();
+ const controller = createController(sourceEditor);
+ input.controllers.insertControllerAt(0, controller);
+}
+
+module.exports = { insertCommandsController };
diff --git a/devtools/client/shared/sourceeditor/editor.js b/devtools/client/shared/sourceeditor/editor.js
new file mode 100644
index 0000000000..c379a6b411
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/editor.js
@@ -0,0 +1,1818 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ EXPAND_TAB,
+ TAB_SIZE,
+ DETECT_INDENT,
+ getIndentationFromIteration,
+} = require("resource://devtools/shared/indentation.js");
+
+const ENABLE_CODE_FOLDING = "devtools.editor.enableCodeFolding";
+const KEYMAP_PREF = "devtools.editor.keymap";
+const AUTO_CLOSE = "devtools.editor.autoclosebrackets";
+const AUTOCOMPLETE = "devtools.editor.autocomplete";
+const CARET_BLINK_TIME = "ui.caretBlinkTime";
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+const VALID_KEYMAPS = new Map([
+ [
+ "emacs",
+ "chrome://devtools/content/shared/sourceeditor/codemirror/keymap/emacs.js",
+ ],
+ [
+ "vim",
+ "chrome://devtools/content/shared/sourceeditor/codemirror/keymap/vim.js",
+ ],
+ [
+ "sublime",
+ "chrome://devtools/content/shared/sourceeditor/codemirror/keymap/sublime.js",
+ ],
+]);
+
+// Maximum allowed margin (in number of lines) from top or bottom of the editor
+// while shifting to a line which was initially out of view.
+const MAX_VERTICAL_OFFSET = 3;
+
+const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
+const AUTOCOMPLETE_MARK_CLASSNAME = "cm-auto-complete-shadow-text";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const { PrefObserver } = require("resource://devtools/client/shared/prefs.js");
+const KeyShortcuts = require("resource://devtools/client/shared/key-shortcuts.js");
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/sourceeditor.properties"
+);
+
+loader.lazyRequireGetter(
+ this,
+ "wasm",
+ "resource://devtools/client/shared/sourceeditor/wasm.js"
+);
+
+const { OS } = Services.appinfo;
+
+// CM_BUNDLE and CM_IFRAME represent the HTML and JavaScript that is
+// injected into an iframe in order to initialize a CodeMirror instance.
+
+const CM_BUNDLE =
+ "chrome://devtools/content/shared/sourceeditor/codemirror/codemirror.bundle.js";
+const CM6_BUNDLE =
+ "resource://devtools/client/shared/sourceeditor/codemirror6/codemirror6.bundle.js";
+
+const CM_IFRAME =
+ "chrome://devtools/content/shared/sourceeditor/codemirror/cmiframe.html";
+
+const CM_MAPPING = [
+ "clearHistory",
+ "defaultCharWidth",
+ "extendSelection",
+ "focus",
+ "getCursor",
+ "getLine",
+ "getScrollInfo",
+ "getSelection",
+ "getViewport",
+ "hasFocus",
+ "lineCount",
+ "openDialog",
+ "redo",
+ "refresh",
+ "replaceSelection",
+ "setSelection",
+ "somethingSelected",
+ "undo",
+];
+
+const editors = new WeakMap();
+
+/**
+ * A very thin wrapper around CodeMirror. Provides a number
+ * of helper methods to make our use of CodeMirror easier and
+ * another method, appendTo, to actually create and append
+ * the CodeMirror instance.
+ *
+ * Note that Editor doesn't expose CodeMirror instance to the
+ * outside world.
+ *
+ * Constructor accepts one argument, config. It is very
+ * similar to the CodeMirror configuration object so for most
+ * properties go to CodeMirror's documentation (see below).
+ *
+ * Other than that, it accepts one additional and optional
+ * property contextMenu. This property should be an element, or
+ * an ID of an element that we can use as a context menu.
+ *
+ * This object is also an event emitter.
+ *
+ * CodeMirror docs: http://codemirror.net/doc/manual.html
+ */
+class Editor extends EventEmitter {
+ // Static methods on the Editor object itself.
+
+ /**
+ * Returns a string representation of a shortcut 'key' with
+ * a OS specific modifier. Cmd- for Macs, Ctrl- for other
+ * platforms. Useful with extraKeys configuration option.
+ *
+ * CodeMirror defines all keys with modifiers in the following
+ * order: Shift - Ctrl/Cmd - Alt - Key
+ */
+ static accel(key, modifiers = {}) {
+ return (
+ (modifiers.shift ? "Shift-" : "") +
+ (Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") +
+ (modifiers.alt ? "Alt-" : "") +
+ key
+ );
+ }
+
+ /**
+ * Returns a string representation of a shortcut for a
+ * specified command 'cmd'. Append Cmd- for macs, Ctrl- for other
+ * platforms unless noaccel is specified in the options. Useful when overwriting
+ * or disabling default shortcuts.
+ */
+ static keyFor(cmd, opts = { noaccel: false }) {
+ const key = L10N.getStr(cmd + ".commandkey");
+ return opts.noaccel ? key : Editor.accel(key);
+ }
+
+ static modes = {
+ cljs: { name: "text/x-clojure" },
+ css: { name: "css" },
+ fs: { name: "x-shader/x-fragment" },
+ haxe: { name: "haxe" },
+ http: { name: "http" },
+ html: { name: "htmlmixed" },
+ js: { name: "javascript" },
+ text: { name: "text" },
+ vs: { name: "x-shader/x-vertex" },
+ wasm: { name: "wasm" },
+ };
+
+ container = null;
+ version = null;
+ config = null;
+ Doc = null;
+
+ #compartments;
+ #lastDirty;
+ #loadedKeyMaps;
+ #ownerDoc;
+ #prefObserver;
+
+ constructor(config) {
+ super();
+
+ const tabSize = Services.prefs.getIntPref(TAB_SIZE);
+ const useTabs = !Services.prefs.getBoolPref(EXPAND_TAB);
+ const useAutoClose = Services.prefs.getBoolPref(AUTO_CLOSE);
+
+ this.version = null;
+ this.config = {
+ cm6: false,
+ value: "",
+ mode: Editor.modes.text,
+ indentUnit: tabSize,
+ tabSize,
+ contextMenu: null,
+ matchBrackets: true,
+ highlightSelectionMatches: {
+ wordsOnly: true,
+ },
+ extraKeys: {},
+ indentWithTabs: useTabs,
+ inputStyle: "accessibleTextArea",
+ // This is set to the biggest value for setTimeout (See https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value)
+ // This is because codeMirror queries the underlying textArea for some things that
+ // can't be retrieved with events in some browser (but we're fine in Firefox).
+ pollInterval: Math.pow(2, 31) - 1,
+ styleActiveLine: true,
+ autoCloseBrackets: "()[]{}''\"\"``",
+ autoCloseEnabled: useAutoClose,
+ theme: "mozilla",
+ themeSwitching: true,
+ autocomplete: false,
+ autocompleteOpts: {},
+ // Expect a CssProperties object (see devtools/client/fronts/css-properties.js)
+ cssProperties: null,
+ // Set to true to prevent the search addon to be activated.
+ disableSearchAddon: false,
+ maxHighlightLength: 1000,
+ // Disable codeMirror setTimeout-based cursor blinking (will be replaced by a CSS animation)
+ cursorBlinkRate: 0,
+ // List of non-printable chars that will be displayed in the editor, showing their
+ // unicode version. We only add a few characters to the default list:
+ // - \u202d LEFT-TO-RIGHT OVERRIDE
+ // - \u202e RIGHT-TO-LEFT OVERRIDE
+ // - \u2066 LEFT-TO-RIGHT ISOLATE
+ // - \u2067 RIGHT-TO-LEFT ISOLATE
+ // - \u2069 POP DIRECTIONAL ISOLATE
+ specialChars:
+ // eslint-disable-next-line no-control-regex
+ /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/,
+ specialCharPlaceholder: char => {
+ // Use the doc provided to the setup function if we don't have a reference to a codeMirror
+ // editor yet (this can happen when an Editor is being created with existing content)
+ const doc = this.#ownerDoc;
+ const el = doc.createElement("span");
+ el.classList.add("cm-non-printable-char");
+ el.append(doc.createTextNode(`\\u${char.codePointAt(0).toString(16)}`));
+ return el;
+ },
+ };
+
+ // Additional shortcuts.
+ this.config.extraKeys[Editor.keyFor("jumpToLine")] = () =>
+ this.jumpToLine();
+ this.config.extraKeys[Editor.keyFor("moveLineUp", { noaccel: true })] =
+ () => this.moveLineUp();
+ this.config.extraKeys[Editor.keyFor("moveLineDown", { noaccel: true })] =
+ () => this.moveLineDown();
+ this.config.extraKeys[Editor.keyFor("toggleComment")] = "toggleComment";
+
+ // Disable ctrl-[ and ctrl-] because toolbox uses those shortcuts.
+ this.config.extraKeys[Editor.keyFor("indentLess")] = false;
+ this.config.extraKeys[Editor.keyFor("indentMore")] = false;
+
+ // Disable Alt-B and Alt-F to navigate groups (respectively previous and next) since:
+ // - it's not standard in input fields
+ // - it also inserts a character which feels weird
+ this.config.extraKeys["Alt-B"] = false;
+ this.config.extraKeys["Alt-F"] = false;
+
+ // Disable Ctrl/Cmd + U as it's used for "View Source". It's okay to disable Ctrl+U as
+ // the underlying command, `undoSelection`, isn't standard in input fields and isn't
+ // widely known.
+ this.config.extraKeys[Editor.accel("U")] = false;
+
+ // Disable keys that trigger events with a null-string `which` property.
+ // It looks like some of those (e.g. the Function key), can trigger a poll
+ // which fails to see that there's a selection, which end up replacing the
+ // selected text with an empty string.
+ // TODO: We should investigate the root cause.
+ this.config.extraKeys["'\u0000'"] = false;
+
+ // Overwrite default config with user-provided, if needed.
+ Object.keys(config).forEach(k => {
+ if (k != "extraKeys") {
+ this.config[k] = config[k];
+ return;
+ }
+
+ if (!config.extraKeys) {
+ return;
+ }
+
+ Object.keys(config.extraKeys).forEach(key => {
+ this.config.extraKeys[key] = config.extraKeys[key];
+ });
+ });
+
+ if (!this.config.gutters) {
+ this.config.gutters = [];
+ }
+ if (
+ this.config.lineNumbers &&
+ !this.config.gutters.includes("CodeMirror-linenumbers")
+ ) {
+ this.config.gutters.push("CodeMirror-linenumbers");
+ }
+
+ // Remember the initial value of autoCloseBrackets.
+ this.config.autoCloseBracketsSaved = this.config.autoCloseBrackets;
+
+ // Overwrite default tab behavior. If something is selected,
+ // indent those lines. If nothing is selected and we're
+ // indenting with tabs, insert one tab. Otherwise insert N
+ // whitespaces where N == indentUnit option.
+ this.config.extraKeys.Tab = cm => {
+ if (config.extraKeys?.Tab) {
+ // If a consumer registers its own extraKeys.Tab, we execute it before doing
+ // anything else. If it returns false, that mean that all the key handling work is
+ // done, so we can do an early return.
+ const res = config.extraKeys.Tab(cm);
+ if (res === false) {
+ return;
+ }
+ }
+
+ if (cm.somethingSelected()) {
+ cm.indentSelection("add");
+ return;
+ }
+
+ if (this.config.indentWithTabs) {
+ cm.replaceSelection("\t", "end", "+input");
+ return;
+ }
+
+ let num = cm.getOption("indentUnit");
+ if (cm.getCursor().ch !== 0) {
+ num -= cm.getCursor().ch % num;
+ }
+ cm.replaceSelection(" ".repeat(num), "end", "+input");
+ };
+
+ if (this.config.cssProperties) {
+ // Ensure that autocompletion has cssProperties if it's passed in via the options.
+ this.config.autocompleteOpts.cssProperties = this.config.cssProperties;
+ }
+ }
+
+ /**
+ * Exposes the CodeMirror class. We want to be able to
+ * invoke static commands such as runMode for syntax highlighting.
+ */
+ get CodeMirror() {
+ const codeMirror = editors.get(this);
+ return codeMirror?.constructor;
+ }
+
+ /**
+ * Exposes the CodeMirror instance. We want to get away from trying to
+ * abstract away the API entirely, and this makes it easier to integrate in
+ * various environments and do complex things.
+ */
+ get codeMirror() {
+ if (!editors.has(this)) {
+ throw new Error(
+ "CodeMirror instance does not exist. You must wait " +
+ "for it to be appended to the DOM."
+ );
+ }
+ return editors.get(this);
+ }
+
+ /**
+ * Return whether there is a CodeMirror instance associated with this Editor.
+ */
+ get hasCodeMirror() {
+ return editors.has(this);
+ }
+
+ /**
+ * Appends the current Editor instance to the element specified by
+ * 'el'. You can also provide your own iframe to host the editor as
+ * an optional second parameter. This method actually creates and
+ * loads CodeMirror and all its dependencies.
+ *
+ * This method is asynchronous and returns a promise.
+ */
+ appendTo(el, env) {
+ return new Promise(resolve => {
+ const cm = editors.get(this);
+
+ if (!env) {
+ env = el.ownerDocument.createElementNS(XHTML_NS, "iframe");
+ env.className = "source-editor-frame";
+ }
+
+ if (cm) {
+ throw new Error("You can append an editor only once.");
+ }
+
+ const onLoad = () => {
+ // Prevent flickering by showing the iframe once loaded.
+ // See https://github.com/w3c/csswg-drafts/issues/9624
+ env.style.visibility = "";
+ const win = env.contentWindow.wrappedJSObject;
+ this.container = env;
+
+ const editorEl = win.document.body;
+ const editorDoc = el.ownerDocument;
+ if (this.config.cm6) {
+ this.#setupCm6(editorEl, editorDoc);
+ } else {
+ this.#setup(editorEl, editorDoc);
+ }
+ resolve();
+ };
+
+ env.style.visibility = "hidden";
+ env.addEventListener("load", onLoad, { capture: true, once: true });
+ env.src = CM_IFRAME;
+ el.appendChild(env);
+
+ this.once("destroy", () => el.removeChild(env));
+ });
+ }
+
+ appendToLocalElement(el) {
+ if (this.config.cm6) {
+ this.#setupCm6(el);
+ } else {
+ this.#setup(el);
+ }
+ }
+
+ /**
+ * Do the actual appending and configuring of the CodeMirror instance. This is
+ * used by both append functions above, and does all the hard work to
+ * configure CodeMirror with all the right options/modes/etc.
+ */
+ #setup(el, doc) {
+ this.#ownerDoc = doc || el.ownerDocument;
+ const win = el.ownerDocument.defaultView;
+
+ Services.scriptloader.loadSubScript(CM_BUNDLE, win);
+
+ if (this.config.cssProperties) {
+ // Replace the propertyKeywords, colorKeywords and valueKeywords
+ // properties of the CSS MIME type with the values provided by the CSS properties
+ // database.
+ const { propertyKeywords, colorKeywords, valueKeywords } = getCSSKeywords(
+ this.config.cssProperties
+ );
+
+ const cssSpec = win.CodeMirror.resolveMode("text/css");
+ cssSpec.propertyKeywords = propertyKeywords;
+ cssSpec.colorKeywords = colorKeywords;
+ cssSpec.valueKeywords = valueKeywords;
+ win.CodeMirror.defineMIME("text/css", cssSpec);
+
+ const scssSpec = win.CodeMirror.resolveMode("text/x-scss");
+ scssSpec.propertyKeywords = propertyKeywords;
+ scssSpec.colorKeywords = colorKeywords;
+ scssSpec.valueKeywords = valueKeywords;
+ win.CodeMirror.defineMIME("text/x-scss", scssSpec);
+ }
+
+ win.CodeMirror.commands.save = () => this.emit("saveRequested");
+
+ // Create a CodeMirror instance add support for context menus,
+ // overwrite the default controller (otherwise items in the top and
+ // context menus won't work).
+
+ const cm = win.CodeMirror(el, this.config);
+ this.Doc = win.CodeMirror.Doc;
+
+ // Disable APZ for source editors. It currently causes the line numbers to
+ // "tear off" and swim around on top of the content. Bug 1160601 tracks
+ // finding a solution that allows APZ to work with CodeMirror.
+ cm.getScrollerElement().addEventListener("wheel", ev => {
+ // By handling the wheel events ourselves, we force the platform to
+ // scroll synchronously, like it did before APZ. However, we lose smooth
+ // scrolling for users with mouse wheels. This seems acceptible vs.
+ // doing nothing and letting the gutter slide around.
+ ev.preventDefault();
+
+ let { deltaX, deltaY } = ev;
+
+ if (ev.deltaMode == ev.DOM_DELTA_LINE) {
+ deltaX *= cm.defaultCharWidth();
+ deltaY *= cm.defaultTextHeight();
+ } else if (ev.deltaMode == ev.DOM_DELTA_PAGE) {
+ deltaX *= cm.getWrapperElement().clientWidth;
+ deltaY *= cm.getWrapperElement().clientHeight;
+ }
+
+ cm.getScrollerElement().scrollBy(deltaX, deltaY);
+ });
+
+ cm.getWrapperElement().addEventListener("contextmenu", ev => {
+ if (!this.config.contextMenu) {
+ return;
+ }
+
+ ev.stopPropagation();
+ ev.preventDefault();
+
+ let popup = this.config.contextMenu;
+ if (typeof popup == "string") {
+ popup = this.#ownerDoc.getElementById(this.config.contextMenu);
+ }
+
+ this.emit("popupOpen", ev, popup);
+ popup.openPopupAtScreen(ev.screenX, ev.screenY, true);
+ });
+
+ const pipedEvents = [
+ "beforeChange",
+ "blur",
+ "changes",
+ "cursorActivity",
+ "focus",
+ "keyHandled",
+ "scroll",
+ ];
+ for (const eventName of pipedEvents) {
+ cm.on(eventName, (...args) => this.emit(eventName, ...args));
+ }
+
+ cm.on("change", () => {
+ this.emit("change");
+ if (!this.#lastDirty) {
+ this.#lastDirty = true;
+ this.emit("dirty-change");
+ }
+ });
+
+ cm.on("gutterClick", (cmArg, line, gutter, ev) => {
+ const lineOrOffset = !this.isWasm ? line : this.lineToWasmOffset(line);
+ this.emit("gutterClick", lineOrOffset, ev.button);
+ });
+
+ win.CodeMirror.defineExtension("l10n", name => {
+ return L10N.getStr(name);
+ });
+
+ if (!this.config.disableSearchAddon) {
+ this.#initSearchShortcuts(win);
+ } else {
+ // Hotfix for Bug 1527898. We should remove those overrides as part of Bug 1527903.
+ Object.assign(win.CodeMirror.commands, {
+ find: null,
+ findPersistent: null,
+ findPersistentNext: null,
+ findPersistentPrev: null,
+ findNext: null,
+ findPrev: null,
+ clearSearch: null,
+ replace: null,
+ replaceAll: null,
+ });
+ }
+
+ // Retrieve the cursor blink rate from user preference, or fall back to CodeMirror's
+ // default value.
+ let cursorBlinkingRate = win.CodeMirror.defaults.cursorBlinkRate;
+ if (Services.prefs.prefHasUserValue(CARET_BLINK_TIME)) {
+ cursorBlinkingRate = Services.prefs.getIntPref(
+ CARET_BLINK_TIME,
+ cursorBlinkingRate
+ );
+ }
+ // This will be used in the animation-duration property we set on the cursor to
+ // implement the blinking animation. If cursorBlinkingRate is 0 or less, the cursor
+ // won't blink.
+ cm.getWrapperElement().style.setProperty(
+ "--caret-blink-time",
+ `${Math.max(0, cursorBlinkingRate)}ms`
+ );
+
+ editors.set(this, cm);
+
+ this.reloadPreferences = this.reloadPreferences.bind(this);
+ this.setKeyMap = this.setKeyMap.bind(this, win);
+
+ this.#prefObserver = new PrefObserver("devtools.editor.");
+ this.#prefObserver.on(TAB_SIZE, this.reloadPreferences);
+ this.#prefObserver.on(EXPAND_TAB, this.reloadPreferences);
+ this.#prefObserver.on(AUTO_CLOSE, this.reloadPreferences);
+ this.#prefObserver.on(AUTOCOMPLETE, this.reloadPreferences);
+ this.#prefObserver.on(DETECT_INDENT, this.reloadPreferences);
+ this.#prefObserver.on(ENABLE_CODE_FOLDING, this.reloadPreferences);
+
+ this.reloadPreferences();
+
+ // Init a map of the loaded keymap files. Should be of the form Map<String->Boolean>.
+ this.#loadedKeyMaps = new Set();
+ this.#prefObserver.on(KEYMAP_PREF, this.setKeyMap);
+ this.setKeyMap();
+
+ win.editor = this;
+ const editorReadyEvent = new win.CustomEvent("editorReady");
+ win.dispatchEvent(editorReadyEvent);
+ }
+
+ /**
+ * Do the actual appending and configuring of the CodeMirror 6 instance.
+ * This is used by appendTo and appendToLocalElement, and does all the hard work to
+ * configure CodeMirror 6 with all the right options/modes/etc.
+ * This should be kept in sync with #setup.
+ *
+ * @param {Element} el: Element into which the codeMirror editor should be appended.
+ * @param {Document} document: Optional document, if not set, will default to el.ownerDocument
+ */
+ #setupCm6(el, doc) {
+ this.#ownerDoc = doc || el.ownerDocument;
+ const win = el.ownerDocument.defaultView;
+
+ Services.scriptloader.loadSubScript(CM6_BUNDLE, win);
+
+ const {
+ codemirror,
+ codemirrorView: { EditorView, lineNumbers },
+ codemirrorState: { EditorState, Compartment },
+ codemirrorLanguage,
+ codemirrorLangJavascript,
+ lezerHighlight,
+ } = win.CodeMirror;
+
+ const tabSizeCompartment = new Compartment();
+ const indentCompartment = new Compartment();
+ this.#compartments = {
+ tabSizeCompartment,
+ indentCompartment,
+ };
+
+ const indentStr = (this.config.indentWithTabs ? "\t" : " ").repeat(
+ this.config.indentUnit || 2
+ );
+
+ const extensions = [
+ indentCompartment.of(codemirrorLanguage.indentUnit.of(indentStr)),
+ tabSizeCompartment.of(EditorState.tabSize.of(this.config.tabSize)),
+ EditorState.readOnly.of(this.config.readOnly),
+ codemirrorLanguage.codeFolding({
+ placeholderText: "↔",
+ }),
+ codemirrorLanguage.foldGutter({
+ class: "cm6-dt-foldgutter",
+ markerDOM: open => {
+ const button = doc.createElement("button");
+ button.classList.add("cm6-dt-foldgutter__toggle-button");
+ button.setAttribute("aria-expanded", open);
+ return button;
+ },
+ }),
+ codemirrorLanguage.syntaxHighlighting(lezerHighlight.classHighlighter),
+ // keep last so other extension take precedence
+ codemirror.minimalSetup,
+ ];
+
+ if (this.config.mode === Editor.modes.js) {
+ extensions.push(codemirrorLangJavascript.javascript());
+ }
+
+ if (this.config.lineNumbers) {
+ extensions.push(lineNumbers());
+ }
+
+ if (this.config.lineWrapping) {
+ extensions.push(EditorView.lineWrapping);
+ }
+
+ const cm = new EditorView({
+ parent: el,
+ extensions,
+ });
+
+ editors.set(this, cm);
+ }
+
+ /**
+ * Returns a boolean indicating whether the editor is ready to
+ * use. Use appendTo(el).then(() => {}) for most cases
+ */
+ isAppended() {
+ return editors.has(this);
+ }
+
+ /**
+ * Returns the currently active highlighting mode.
+ * See Editor.modes for the list of all suppoert modes.
+ */
+ getMode() {
+ return this.getOption("mode");
+ }
+
+ /**
+ * Loads a script into editor's containing window.
+ */
+ loadScript(url) {
+ if (!this.container) {
+ throw new Error("Can't load a script until the editor is loaded.");
+ }
+ const win = this.container.contentWindow.wrappedJSObject;
+ Services.scriptloader.loadSubScript(url, win);
+ }
+
+ /**
+ * Returns the container content window
+ * @returns {Window}
+ */
+ getContainerWindow() {
+ return this.container.contentWindow.wrappedJSObject;
+ }
+
+ /**
+ * Creates a CodeMirror Document
+ *
+ * @param {String} text: Initial text of the document
+ * @param {Object|String} mode: Mode of the document. See https://codemirror.net/5/doc/manual.html#option_mode
+ * @returns CodeMirror.Doc
+ */
+ createDocument(text = "", mode) {
+ return new this.Doc(text, mode);
+ }
+
+ /**
+ * Replaces the current document with a new source document
+ */
+ replaceDocument(doc) {
+ const cm = editors.get(this);
+ cm.swapDoc(doc);
+ }
+
+ /**
+ * Changes the value of a currently used highlighting mode.
+ * See Editor.modes for the list of all supported modes.
+ */
+ setMode(value) {
+ this.setOption("mode", value);
+
+ // If autocomplete was set up and the mode is changing, then
+ // turn it off and back on again so the proper mode can be used.
+ if (this.config.autocomplete) {
+ this.setOption("autocomplete", false);
+ this.setOption("autocomplete", true);
+ }
+ }
+
+ /**
+ * The source editor can expose several commands linked from system and context menus.
+ * Kept for backward compatibility with styleeditor.
+ */
+ insertCommandsController() {
+ const {
+ insertCommandsController,
+ } = require("resource://devtools/client/shared/sourceeditor/editor-commands-controller.js");
+ insertCommandsController(this);
+ }
+
+ /**
+ * Returns text from the text area. If line argument is provided
+ * the method returns only that line.
+ */
+ getText(line) {
+ const cm = editors.get(this);
+
+ if (line == null) {
+ return this.config.cm6 ? cm.state.doc.toString() : cm.getValue();
+ }
+
+ const info = this.lineInfo(line);
+ return info ? info.text : "";
+ }
+
+ getDoc() {
+ const cm = editors.get(this);
+ return cm.getDoc();
+ }
+
+ get isWasm() {
+ return wasm.isWasm(this.getDoc());
+ }
+
+ wasmOffsetToLine(offset) {
+ return wasm.wasmOffsetToLine(this.getDoc(), offset);
+ }
+
+ lineToWasmOffset(number) {
+ return wasm.lineToWasmOffset(this.getDoc(), number);
+ }
+
+ toLineIfWasmOffset(maybeOffset) {
+ if (typeof maybeOffset !== "number" || !this.isWasm) {
+ return maybeOffset;
+ }
+ return this.wasmOffsetToLine(maybeOffset);
+ }
+
+ lineInfo(lineOrOffset) {
+ const line = this.toLineIfWasmOffset(lineOrOffset);
+ if (line == undefined) {
+ return null;
+ }
+ const cm = editors.get(this);
+
+ if (this.config.cm6) {
+ return {
+ // cm6 lines are 1-based, while cm5 are 0-based
+ text: cm.state.doc.lineAt(line + 1)?.text,
+ // TODO: Expose those, or see usage for those and do things differently
+ line: null,
+ handle: null,
+ gutterMarkers: null,
+ textClass: null,
+ bgClass: null,
+ wrapClass: null,
+ widgets: null,
+ };
+ }
+
+ return cm.lineInfo(line);
+ }
+
+ getLineOrOffset(line) {
+ return this.isWasm ? this.lineToWasmOffset(line) : line;
+ }
+
+ /**
+ * Replaces whatever is in the text area with the contents of
+ * the 'value' argument.
+ */
+ setText(value) {
+ const cm = editors.get(this);
+
+ if (typeof value !== "string" && "binary" in value) {
+ // wasm?
+ // binary does not survive as Uint8Array, converting from string
+ const binary = value.binary;
+ const data = new Uint8Array(binary.length);
+ for (let i = 0; i < data.length; i++) {
+ data[i] = binary.charCodeAt(i);
+ }
+ const { lines, done } = wasm.getWasmText(this.getDoc(), data);
+ const MAX_LINES = 10000000;
+ if (lines.length > MAX_LINES) {
+ lines.splice(MAX_LINES, lines.length - MAX_LINES);
+ lines.push(";; .... text is truncated due to the size");
+ }
+ if (!done) {
+ lines.push(";; .... possible error during wast conversion");
+ }
+ // cm will try to split into lines anyway, saving memory
+ value = { split: () => lines };
+ }
+
+ if (this.config.cm6) {
+ cm.dispatch({
+ changes: { from: 0, to: cm.state.doc.length, insert: value },
+ });
+ } else {
+ cm.setValue(value);
+ }
+
+ this.resetIndentUnit();
+ }
+
+ /**
+ * Reloads the state of the editor based on all current preferences.
+ * This is called automatically when any of the relevant preferences
+ * change.
+ */
+ reloadPreferences() {
+ // Restore the saved autoCloseBrackets value if it is preffed on.
+ const useAutoClose = Services.prefs.getBoolPref(AUTO_CLOSE);
+ this.setOption(
+ "autoCloseBrackets",
+ useAutoClose ? this.config.autoCloseBracketsSaved : false
+ );
+
+ this.updateCodeFoldingGutter();
+
+ this.resetIndentUnit();
+ this.setupAutoCompletion();
+ }
+
+ /**
+ * Set the current keyMap for CodeMirror, and load the support file if needed.
+ *
+ * @param {Window} win: The window on which the keymap files should be loaded.
+ */
+ setKeyMap(win) {
+ if (this.config.isReadOnly) {
+ return;
+ }
+
+ const keyMap = Services.prefs.getCharPref(KEYMAP_PREF);
+
+ // If alternative keymap is provided, use it.
+ if (VALID_KEYMAPS.has(keyMap)) {
+ if (!this.#loadedKeyMaps.has(keyMap)) {
+ Services.scriptloader.loadSubScript(VALID_KEYMAPS.get(keyMap), win);
+ this.#loadedKeyMaps.add(keyMap);
+ }
+ this.setOption("keyMap", keyMap);
+ } else {
+ this.setOption("keyMap", "default");
+ }
+ }
+
+ /**
+ * Sets the editor's indentation based on the current prefs and
+ * re-detect indentation if we should.
+ */
+ resetIndentUnit() {
+ const cm = editors.get(this);
+
+ const iterFn = (start, maxEnd, callback) => {
+ if (!this.config.cm6) {
+ cm.eachLine(start, maxEnd, line => {
+ return callback(line.text);
+ });
+ } else {
+ const iterator = cm.state.doc.iterLines(
+ start + 1,
+ Math.min(cm.state.doc.lines, maxEnd) + 1
+ );
+ let callbackRes;
+ do {
+ iterator.next();
+ callbackRes = callback(iterator.value);
+ } while (iterator.done !== true && !callbackRes);
+ }
+ };
+
+ const { indentUnit, indentWithTabs } = getIndentationFromIteration(iterFn);
+
+ if (!this.config.cm6) {
+ cm.setOption("tabSize", indentUnit);
+ cm.setOption("indentUnit", indentUnit);
+ cm.setOption("indentWithTabs", indentWithTabs);
+ } else {
+ const {
+ codemirrorState: { EditorState },
+ codemirrorLanguage,
+ } = this.getContainerWindow().CodeMirror;
+
+ cm.dispatch({
+ effects: this.#compartments.tabSizeCompartment.reconfigure(
+ EditorState.tabSize.of(indentUnit)
+ ),
+ });
+ cm.dispatch({
+ effects: this.#compartments.indentCompartment.reconfigure(
+ codemirrorLanguage.indentUnit.of(
+ (indentWithTabs ? "\t" : " ").repeat(indentUnit)
+ )
+ ),
+ });
+ }
+ }
+
+ /**
+ * Replaces contents of a text area within the from/to {line, ch}
+ * range. If neither `from` nor `to` arguments are provided works
+ * exactly like setText. If only `from` object is provided, inserts
+ * text at that point, *overwriting* as many characters as needed.
+ */
+ replaceText(value, from, to) {
+ const cm = editors.get(this);
+
+ if (!from) {
+ this.setText(value);
+ return;
+ }
+
+ if (!to) {
+ const text = cm.getRange({ line: 0, ch: 0 }, from);
+ this.setText(text + value);
+ return;
+ }
+
+ cm.replaceRange(value, from, to);
+ }
+
+ /**
+ * Inserts text at the specified {line, ch} position, shifting existing
+ * contents as necessary.
+ */
+ insertText(value, at) {
+ const cm = editors.get(this);
+ cm.replaceRange(value, at, at);
+ }
+
+ /**
+ * Deselects contents of the text area.
+ */
+ dropSelection() {
+ if (!this.somethingSelected()) {
+ return;
+ }
+
+ this.setCursor(this.getCursor());
+ }
+
+ /**
+ * Returns true if there is more than one selection in the editor.
+ */
+ hasMultipleSelections() {
+ const cm = editors.get(this);
+ return cm.listSelections().length > 1;
+ }
+
+ /**
+ * Gets the first visible line number in the editor.
+ */
+ getFirstVisibleLine() {
+ const cm = editors.get(this);
+ return cm.lineAtHeight(0, "local");
+ }
+
+ /**
+ * Scrolls the view such that the given line number is the first visible line.
+ */
+ setFirstVisibleLine(line) {
+ const cm = editors.get(this);
+ const { top } = cm.charCoords({ line, ch: 0 }, "local");
+ cm.scrollTo(0, top);
+ }
+
+ /**
+ * Sets the cursor to the specified {line, ch} position with an additional
+ * option to align the line at the "top", "center" or "bottom" of the editor
+ * with "top" being default value.
+ */
+ setCursor({ line, ch }, align) {
+ const cm = editors.get(this);
+ this.alignLine(line, align);
+ cm.setCursor({ line, ch });
+ this.emit("cursorActivity");
+ }
+
+ /**
+ * Aligns the provided line to either "top", "center" or "bottom" of the
+ * editor view with a maximum margin of MAX_VERTICAL_OFFSET lines from top or
+ * bottom.
+ */
+ alignLine(line, align) {
+ const cm = editors.get(this);
+ const from = cm.lineAtHeight(0, "page");
+ const to = cm.lineAtHeight(cm.getWrapperElement().clientHeight, "page");
+ const linesVisible = to - from;
+ const halfVisible = Math.round(linesVisible / 2);
+
+ // If the target line is in view, skip the vertical alignment part.
+ if (line <= to && line >= from) {
+ return;
+ }
+
+ // Setting the offset so that the line always falls in the upper half
+ // of visible lines (lower half for bottom aligned).
+ // MAX_VERTICAL_OFFSET is the maximum allowed value.
+ const offset = Math.min(halfVisible, MAX_VERTICAL_OFFSET);
+
+ let topLine =
+ {
+ center: Math.max(line - halfVisible, 0),
+ bottom: Math.max(line - linesVisible + offset, 0),
+ top: Math.max(line - offset, 0),
+ }[align || "top"] || offset;
+
+ // Bringing down the topLine to total lines in the editor if exceeding.
+ topLine = Math.min(topLine, this.lineCount());
+ this.setFirstVisibleLine(topLine);
+ }
+
+ /**
+ * Returns whether a marker of a specified class exists in a line's gutter.
+ */
+ hasMarker(line, gutterName, markerClass) {
+ const marker = this.getMarker(line, gutterName);
+ if (!marker) {
+ return false;
+ }
+
+ return marker.classList.contains(markerClass);
+ }
+
+ /**
+ * Adds a marker with a specified class to a line's gutter. If another marker
+ * exists on that line, the new marker class is added to its class list.
+ */
+ addMarker(line, gutterName, markerClass) {
+ const cm = editors.get(this);
+ const info = this.lineInfo(line);
+ if (!info) {
+ return;
+ }
+
+ const gutterMarkers = info.gutterMarkers;
+ let marker;
+ if (gutterMarkers) {
+ marker = gutterMarkers[gutterName];
+ if (marker) {
+ marker.classList.add(markerClass);
+ return;
+ }
+ }
+
+ marker = cm.getWrapperElement().ownerDocument.createElement("div");
+ marker.className = markerClass;
+ cm.setGutterMarker(info.line, gutterName, marker);
+ }
+
+ /**
+ * The reverse of addMarker. Removes a marker of a specified class from a
+ * line's gutter.
+ */
+ removeMarker(line, gutterName, markerClass) {
+ if (!this.hasMarker(line, gutterName, markerClass)) {
+ return;
+ }
+
+ this.lineInfo(line).gutterMarkers[gutterName].classList.remove(markerClass);
+ }
+
+ /**
+ * Adds a marker with a specified class and an HTML content to a line's
+ * gutter. If another marker exists on that line, it is overwritten by a new
+ * marker.
+ */
+ addContentMarker(line, gutterName, markerClass, content) {
+ const cm = editors.get(this);
+ const info = this.lineInfo(line);
+ if (!info) {
+ return;
+ }
+
+ const marker = cm.getWrapperElement().ownerDocument.createElement("div");
+ marker.className = markerClass;
+ // eslint-disable-next-line no-unsanitized/property
+ marker.innerHTML = content;
+ cm.setGutterMarker(info.line, gutterName, marker);
+ }
+
+ /**
+ * The reverse of addContentMarker. Removes any line's markers in the
+ * specified gutter.
+ */
+ removeContentMarker(line, gutterName) {
+ const cm = editors.get(this);
+ const info = this.lineInfo(line);
+ if (!info) {
+ return;
+ }
+
+ cm.setGutterMarker(info.line, gutterName, null);
+ }
+
+ getMarker(line, gutterName) {
+ const info = this.lineInfo(line);
+ if (!info) {
+ return null;
+ }
+
+ const gutterMarkers = info.gutterMarkers;
+ if (!gutterMarkers) {
+ return null;
+ }
+
+ return gutterMarkers[gutterName];
+ }
+
+ /**
+ * Removes all gutter markers in the gutter with the given name.
+ */
+ removeAllMarkers(gutterName) {
+ const cm = editors.get(this);
+ cm.clearGutter(gutterName);
+ }
+
+ /**
+ * Handles attaching a set of events listeners on a marker. They should
+ * be passed as an object literal with keys as event names and values as
+ * function listeners. The line number, marker node and optional data
+ * will be passed as arguments to the function listener.
+ *
+ * You don't need to worry about removing these event listeners.
+ * They're automatically orphaned when clearing markers.
+ */
+ setMarkerListeners(line, gutterName, markerClass, eventsArg, data) {
+ if (!this.hasMarker(line, gutterName, markerClass)) {
+ return;
+ }
+
+ const cm = editors.get(this);
+ const marker = cm.lineInfo(line).gutterMarkers[gutterName];
+
+ for (const name in eventsArg) {
+ const listener = eventsArg[name].bind(this, line, marker, data);
+ marker.addEventListener(name, listener);
+ }
+ }
+
+ /**
+ * Returns whether a line is decorated using the specified class name.
+ */
+ hasLineClass(line, className) {
+ const info = this.lineInfo(line);
+
+ if (!info || !info.wrapClass) {
+ return false;
+ }
+
+ return info.wrapClass.split(" ").includes(className);
+ }
+
+ /**
+ * Sets a CSS class name for the given line, including the text and gutter.
+ */
+ addLineClass(lineOrOffset, className) {
+ const cm = editors.get(this);
+ const line = this.toLineIfWasmOffset(lineOrOffset);
+ cm.addLineClass(line, "wrap", className);
+ }
+
+ /**
+ * The reverse of addLineClass.
+ */
+ removeLineClass(lineOrOffset, className) {
+ const cm = editors.get(this);
+ const line = this.toLineIfWasmOffset(lineOrOffset);
+ cm.removeLineClass(line, "wrap", className);
+ }
+
+ /**
+ * Mark a range of text inside the two {line, ch} bounds. Since the range may
+ * be modified, for example, when typing text, this method returns a function
+ * that can be used to remove the mark.
+ */
+ markText(from, to, className = "marked-text") {
+ const cm = editors.get(this);
+ const text = cm.getRange(from, to);
+ const span = cm.getWrapperElement().ownerDocument.createElement("span");
+ span.className = className;
+ span.textContent = text;
+
+ const mark = cm.markText(from, to, { replacedWith: span });
+ return {
+ anchor: span,
+ clear: () => mark.clear(),
+ };
+ }
+
+ /**
+ * Calculates and returns one or more {line, ch} objects for
+ * a zero-based index who's value is relative to the start of
+ * the editor's text.
+ *
+ * If only one argument is given, this method returns a single
+ * {line,ch} object. Otherwise it returns an array.
+ */
+ getPosition(...args) {
+ const cm = editors.get(this);
+ const res = args.map(ind => cm.posFromIndex(ind));
+ return args.length === 1 ? res[0] : res;
+ }
+
+ /**
+ * The reverse of getPosition. Similarly to getPosition this
+ * method returns a single value if only one argument was given
+ * and an array otherwise.
+ */
+ getOffset(...args) {
+ const cm = editors.get(this);
+ const res = args.map(pos => cm.indexFromPos(pos));
+ return args.length > 1 ? res : res[0];
+ }
+
+ /**
+ * Returns a {line, ch} object that corresponds to the
+ * left, top coordinates.
+ */
+ getPositionFromCoords({ left, top }) {
+ const cm = editors.get(this);
+ return cm.coordsChar({ left, top });
+ }
+
+ /**
+ * The reverse of getPositionFromCoords. Similarly, returns a {left, top}
+ * object that corresponds to the specified line and character number.
+ */
+ getCoordsFromPosition({ line, ch }) {
+ const cm = editors.get(this);
+ return cm.charCoords({ line: ~~line, ch: ~~ch });
+ }
+
+ /**
+ * Returns true if there's something to undo and false otherwise.
+ */
+ canUndo() {
+ const cm = editors.get(this);
+ return cm.historySize().undo > 0;
+ }
+
+ /**
+ * Returns true if there's something to redo and false otherwise.
+ */
+ canRedo() {
+ const cm = editors.get(this);
+ return cm.historySize().redo > 0;
+ }
+
+ /**
+ * Marks the contents as clean and returns the current
+ * version number.
+ */
+ setClean() {
+ const cm = editors.get(this);
+ this.version = cm.changeGeneration();
+ this.#lastDirty = false;
+ this.emit("dirty-change");
+ return this.version;
+ }
+
+ /**
+ * Returns true if contents of the text area are
+ * clean i.e. no changes were made since the last version.
+ */
+ isClean() {
+ const cm = editors.get(this);
+ return cm.isClean(this.version);
+ }
+
+ /**
+ * This method opens an in-editor dialog asking for a line to
+ * jump to. Once given, it changes cursor to that line.
+ */
+ jumpToLine() {
+ const doc = editors.get(this).getWrapperElement().ownerDocument;
+ const div = doc.createElement("div");
+ const inp = doc.createElement("input");
+ const txt = doc.createTextNode(L10N.getStr("gotoLineCmd.promptTitle"));
+
+ inp.type = "text";
+ inp.style.width = "10em";
+ inp.style.marginInlineStart = "1em";
+
+ div.appendChild(txt);
+ div.appendChild(inp);
+
+ this.openDialog(div, line => {
+ // Handle LINE:COLUMN as well as LINE
+ const match = line.toString().match(RE_JUMP_TO_LINE);
+ if (match) {
+ const [, matchLine, column] = match;
+ this.setCursor({ line: matchLine - 1, ch: column ? column - 1 : 0 });
+ }
+ });
+ }
+
+ /**
+ * Moves the content of the current line or the lines selected up a line.
+ */
+ moveLineUp() {
+ const cm = editors.get(this);
+ const start = cm.getCursor("start");
+ const end = cm.getCursor("end");
+
+ if (start.line === 0) {
+ return;
+ }
+
+ // Get the text in the lines selected or the current line of the cursor
+ // and append the text of the previous line.
+ let value;
+ if (start.line !== end.line) {
+ value =
+ cm.getRange(
+ { line: start.line, ch: 0 },
+ { line: end.line, ch: cm.getLine(end.line).length }
+ ) + "\n";
+ } else {
+ value = cm.getLine(start.line) + "\n";
+ }
+ value += cm.getLine(start.line - 1);
+
+ // Replace the previous line and the currently selected lines with the new
+ // value and maintain the selection of the text.
+ cm.replaceRange(
+ value,
+ { line: start.line - 1, ch: 0 },
+ { line: end.line, ch: cm.getLine(end.line).length }
+ );
+ cm.setSelection(
+ { line: start.line - 1, ch: start.ch },
+ { line: end.line - 1, ch: end.ch }
+ );
+ }
+
+ /**
+ * Moves the content of the current line or the lines selected down a line.
+ */
+ moveLineDown() {
+ const cm = editors.get(this);
+ const start = cm.getCursor("start");
+ const end = cm.getCursor("end");
+
+ if (end.line + 1 === cm.lineCount()) {
+ return;
+ }
+
+ // Get the text of next line and append the text in the lines selected
+ // or the current line of the cursor.
+ let value = cm.getLine(end.line + 1) + "\n";
+ if (start.line !== end.line) {
+ value += cm.getRange(
+ { line: start.line, ch: 0 },
+ { line: end.line, ch: cm.getLine(end.line).length }
+ );
+ } else {
+ value += cm.getLine(start.line);
+ }
+
+ // Replace the currently selected lines and the next line with the new
+ // value and maintain the selection of the text.
+ cm.replaceRange(
+ value,
+ { line: start.line, ch: 0 },
+ { line: end.line + 1, ch: cm.getLine(end.line + 1).length }
+ );
+ cm.setSelection(
+ { line: start.line + 1, ch: start.ch },
+ { line: end.line + 1, ch: end.ch }
+ );
+ }
+
+ /**
+ * Intercept CodeMirror's Find and replace key shortcut to select the search input
+ */
+ findOrReplace(node, isReplaceAll) {
+ const cm = editors.get(this);
+ const isInput = node.tagName === "INPUT";
+ const isSearchInput = isInput && node.type === "search";
+ // replace box is a different input instance than search, and it is
+ // located in a code mirror dialog
+ const isDialogInput =
+ isInput &&
+ node.parentNode &&
+ node.parentNode.classList.contains("CodeMirror-dialog");
+ if (!(isSearchInput || isDialogInput)) {
+ return;
+ }
+
+ if (isSearchInput || isReplaceAll) {
+ // select the search input
+ // it's the precise reason why we reimplement these key shortcuts
+ node.select();
+ }
+
+ // need to call it since we prevent the propagation of the event and
+ // cancel codemirror's key handling
+ cm.execCommand("find");
+ }
+
+ /**
+ * Intercept CodeMirror's findNext and findPrev key shortcut to allow
+ * immediately search for next occurance after typing a word to search.
+ */
+ findNextOrPrev(node, isFindPrev) {
+ const cm = editors.get(this);
+ const isInput = node.tagName === "INPUT";
+ const isSearchInput = isInput && node.type === "search";
+ if (!isSearchInput) {
+ return;
+ }
+ const query = node.value;
+ // cm.state.search allows to automatically start searching for the next occurance
+ // it's the precise reason why we reimplement these key shortcuts
+ if (!cm.state.search || cm.state.search.query !== query) {
+ cm.state.search = {
+ posFrom: null,
+ posTo: null,
+ overlay: null,
+ query,
+ };
+ }
+
+ // need to call it since we prevent the propagation of the event and
+ // cancel codemirror's key handling
+ if (isFindPrev) {
+ cm.execCommand("findPrev");
+ } else {
+ cm.execCommand("findNext");
+ }
+ }
+
+ /**
+ * Returns current font size for the editor area, in pixels.
+ */
+ getFontSize() {
+ const cm = editors.get(this);
+ const el = cm.getWrapperElement();
+ const win = el.ownerDocument.defaultView;
+
+ return parseInt(win.getComputedStyle(el).getPropertyValue("font-size"), 10);
+ }
+
+ /**
+ * Sets font size for the editor area.
+ */
+ setFontSize(size) {
+ const cm = editors.get(this);
+ cm.getWrapperElement().style.fontSize = parseInt(size, 10) + "px";
+ cm.refresh();
+ }
+
+ /**
+ * Sets an option for the editor. For most options it just defers to
+ * CodeMirror.setOption, but certain ones are maintained within the editor
+ * instance.
+ */
+ setOption(o, v) {
+ const cm = editors.get(this);
+
+ // Save the state of a valid autoCloseBrackets string, so we can reset
+ // it if it gets preffed off and back on.
+ if (o === "autoCloseBrackets" && v) {
+ this.config.autoCloseBracketsSaved = v;
+ }
+
+ if (o === "autocomplete") {
+ this.config.autocomplete = v;
+ this.setupAutoCompletion();
+ } else {
+ cm.setOption(o, v);
+ this.config[o] = v;
+ }
+
+ if (o === "enableCodeFolding") {
+ // The new value maybe explicitly force foldGUtter on or off, ignoring
+ // the prefs service.
+ this.updateCodeFoldingGutter();
+ }
+ }
+
+ /**
+ * Gets an option for the editor. For most options it just defers to
+ * CodeMirror.getOption, but certain ones are maintained within the editor
+ * instance.
+ */
+ getOption(o) {
+ const cm = editors.get(this);
+ if (o === "autocomplete") {
+ return this.config.autocomplete;
+ }
+
+ return cm.getOption(o);
+ }
+
+ /**
+ * Sets up autocompletion for the editor. Lazily imports the required
+ * dependencies because they vary by editor mode.
+ *
+ * Autocompletion is special, because we don't want to automatically use
+ * it just because it is preffed on (it still needs to be requested by the
+ * editor), but we do want to always disable it if it is preffed off.
+ */
+ setupAutoCompletion() {
+ if (!this.config.autocomplete && !this.initializeAutoCompletion) {
+ // Do nothing since there is no autocomplete config and no autocompletion have
+ // been initialized.
+ return;
+ }
+ // The autocomplete module will overwrite this.initializeAutoCompletion
+ // with a mode specific autocompletion handler.
+ if (!this.initializeAutoCompletion) {
+ this.extend(
+ require("resource://devtools/client/shared/sourceeditor/autocomplete.js")
+ );
+ }
+
+ if (this.config.autocomplete && Services.prefs.getBoolPref(AUTOCOMPLETE)) {
+ this.initializeAutoCompletion(this.config.autocompleteOpts);
+ } else {
+ this.destroyAutoCompletion();
+ }
+ }
+
+ getAutoCompletionText() {
+ const cm = editors.get(this);
+ const mark = cm
+ .getAllMarks()
+ .find(m => m.className === AUTOCOMPLETE_MARK_CLASSNAME);
+ if (!mark) {
+ return "";
+ }
+
+ return mark.attributes["data-completion"] || "";
+ }
+
+ setAutoCompletionText(text) {
+ const cursor = this.getCursor();
+ const cm = editors.get(this);
+ const className = AUTOCOMPLETE_MARK_CLASSNAME;
+
+ cm.operation(() => {
+ cm.getAllMarks().forEach(mark => {
+ if (mark.className === className) {
+ mark.clear();
+ }
+ });
+
+ if (text) {
+ cm.markText({ ...cursor, ch: cursor.ch - 1 }, cursor, {
+ className,
+ attributes: {
+ "data-completion": text,
+ },
+ });
+ }
+ });
+ }
+
+ /**
+ * Extends an instance of the Editor object with additional
+ * functions. Each function will be called with context as
+ * the first argument. Context is a {ed, cm} object where
+ * 'ed' is an instance of the Editor object and 'cm' is an
+ * instance of the CodeMirror object. Example:
+ *
+ * function hello(ctx, name) {
+ * let { cm, ed } = ctx;
+ * cm; // CodeMirror instance
+ * ed; // Editor instance
+ * name; // 'Mozilla'
+ * }
+ *
+ * editor.extend({ hello: hello });
+ * editor.hello('Mozilla');
+ */
+ extend(funcs) {
+ Object.keys(funcs).forEach(name => {
+ const cm = editors.get(this);
+ const ctx = { ed: this, cm, Editor };
+
+ if (name === "initialize") {
+ funcs[name](ctx);
+ return;
+ }
+
+ this[name] = funcs[name].bind(null, ctx);
+ });
+ }
+
+ isDestroyed() {
+ return !editors.get(this);
+ }
+
+ destroy() {
+ this.container = null;
+ this.config = null;
+ this.version = null;
+ this.#ownerDoc = null;
+
+ if (this.#prefObserver) {
+ this.#prefObserver.off(KEYMAP_PREF, this.setKeyMap);
+ this.#prefObserver.off(TAB_SIZE, this.reloadPreferences);
+ this.#prefObserver.off(EXPAND_TAB, this.reloadPreferences);
+ this.#prefObserver.off(AUTO_CLOSE, this.reloadPreferences);
+ this.#prefObserver.off(AUTOCOMPLETE, this.reloadPreferences);
+ this.#prefObserver.off(DETECT_INDENT, this.reloadPreferences);
+ this.#prefObserver.off(ENABLE_CODE_FOLDING, this.reloadPreferences);
+ this.#prefObserver.destroy();
+ }
+
+ // Remove the link between the document and code-mirror.
+ const cm = editors.get(this);
+ if (cm?.doc) {
+ cm.doc.cm = null;
+ }
+
+ this.emit("destroy");
+ }
+
+ updateCodeFoldingGutter() {
+ let shouldFoldGutter = this.config.enableCodeFolding;
+ const foldGutterIndex = this.config.gutters.indexOf(
+ "CodeMirror-foldgutter"
+ );
+ const cm = editors.get(this);
+
+ if (shouldFoldGutter === undefined) {
+ shouldFoldGutter = Services.prefs.getBoolPref(ENABLE_CODE_FOLDING);
+ }
+
+ if (shouldFoldGutter) {
+ // Add the gutter before enabling foldGutter
+ if (foldGutterIndex === -1) {
+ const gutters = this.config.gutters.slice();
+ gutters.push("CodeMirror-foldgutter");
+ this.setOption("gutters", gutters);
+ }
+
+ this.setOption("foldGutter", true);
+ } else {
+ // No code should remain folded when folding is off.
+ if (cm) {
+ cm.execCommand("unfoldAll");
+ }
+
+ // Remove the gutter so it doesn't take up space
+ if (foldGutterIndex !== -1) {
+ const gutters = this.config.gutters.slice();
+ gutters.splice(foldGutterIndex, 1);
+ this.setOption("gutters", gutters);
+ }
+
+ this.setOption("foldGutter", false);
+ }
+ }
+
+ /**
+ * Register all key shortcuts.
+ */
+ #initSearchShortcuts(win) {
+ const shortcuts = new KeyShortcuts({
+ window: win,
+ });
+ const keys = ["find.key", "findNext.key", "findPrev.key"];
+
+ if (OS === "Darwin") {
+ keys.push("replaceAllMac.key");
+ } else {
+ keys.push("replaceAll.key");
+ }
+ // Process generic keys:
+ keys.forEach(name => {
+ const key = L10N.getStr(name);
+ shortcuts.on(key, event => this.#onSearchShortcut(name, event));
+ });
+ }
+ /**
+ * Key shortcut listener.
+ */
+ #onSearchShortcut = (name, event) => {
+ if (!this.#isInputOrTextarea(event.target)) {
+ return;
+ }
+ const node = event.originalTarget;
+
+ switch (name) {
+ // replaceAll.key is Alt + find.key
+ case "replaceAllMac.key":
+ this.findOrReplace(node, true);
+ break;
+ // replaceAll.key is Shift + find.key
+ case "replaceAll.key":
+ this.findOrReplace(node, true);
+ break;
+ case "find.key":
+ this.findOrReplace(node, false);
+ break;
+ // findPrev.key is Shift + findNext.key
+ case "findPrev.key":
+ this.findNextOrPrev(node, true);
+ break;
+ case "findNext.key":
+ this.findNextOrPrev(node, false);
+ break;
+ default:
+ console.error("Unexpected editor key shortcut", name);
+ return;
+ }
+ // Prevent default for this action
+ event.stopPropagation();
+ event.preventDefault();
+ };
+
+ /**
+ * Check if a node is an input or textarea
+ */
+ #isInputOrTextarea(element) {
+ const name = element.tagName.toLowerCase();
+ return name === "input" || name === "textarea";
+ }
+}
+
+// Since Editor is a thin layer over CodeMirror some methods
+// are mapped directly—without any changes.
+
+CM_MAPPING.forEach(name => {
+ Editor.prototype[name] = function (...args) {
+ const cm = editors.get(this);
+ return cm[name].apply(cm, args);
+ };
+});
+
+/**
+ * We compute the CSS property names, values, and color names to be used with
+ * CodeMirror to more closely reflect what is supported by the target platform.
+ * The database is used to replace the values used in CodeMirror while initiating
+ * an editor object. This is done here instead of the file codemirror/css.js so
+ * as to leave that file untouched and easily upgradable.
+ */
+function getCSSKeywords(cssProperties) {
+ function keySet(array) {
+ const keys = {};
+ for (let i = 0; i < array.length; ++i) {
+ keys[array[i]] = true;
+ }
+ return keys;
+ }
+
+ const propertyKeywords = cssProperties.getNames();
+ const colorKeywords = {};
+ const valueKeywords = {};
+
+ propertyKeywords.forEach(property => {
+ if (property.includes("color")) {
+ cssProperties.getValues(property).forEach(value => {
+ colorKeywords[value] = true;
+ });
+ } else {
+ cssProperties.getValues(property).forEach(value => {
+ valueKeywords[value] = true;
+ });
+ }
+ });
+
+ return {
+ propertyKeywords: keySet(propertyKeywords),
+ colorKeywords,
+ valueKeywords,
+ };
+}
+
+module.exports = Editor;
diff --git a/devtools/client/shared/sourceeditor/moz.build b/devtools/client/shared/sourceeditor/moz.build
new file mode 100644
index 0000000000..23eb4dbc46
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "codemirror6",
+]
+
+DevToolsModules(
+ "autocomplete.js",
+ "css-autocompleter.js",
+ "editor-commands-controller.js",
+ "editor.js",
+ "wasm.js",
+)
+
+BROWSER_CHROME_MANIFESTS += ["test/browser.toml"]
+
+with Files("**"):
+ BUG_COMPONENT = ("DevTools", "Source Editor")
diff --git a/devtools/client/shared/sourceeditor/package.json b/devtools/client/shared/sourceeditor/package.json
new file mode 100644
index 0000000000..f1eeea83fc
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "sourceeditor",
+ "version": "0.0.2",
+ "description": "Build a minified codeMirror bundle including useful addons",
+ "main": "",
+ "scripts": {
+ "build": "webpack",
+ "build-unminified": "webpack --optimization.minimizer=false",
+ "build-cm6": "rollup -c --minified",
+ "build-cm6-unminified": "rollup -c"
+ },
+ "author": "",
+ "license": "",
+ "dependencies": {
+ "codemirror": "6.0.1",
+ "@codemirror/language": "^6.10.0",
+ "@codemirror/lang-javascript": "^6.2.1"
+ },
+ "devDependencies": {
+ "webpack": "^4.28.4",
+ "webpack-cli": "^3.2.1",
+ "rollup": "^4.9.6",
+ "@rollup/plugin-terser": "^0.4.4",
+ "@rollup/plugin-node-resolve": "^15.2.3"
+ }
+}
diff --git a/devtools/client/shared/sourceeditor/rollup.config.mjs b/devtools/client/shared/sourceeditor/rollup.config.mjs
new file mode 100644
index 0000000000..520ca87164
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/rollup.config.mjs
@@ -0,0 +1,24 @@
+/* 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/. */
+
+/* eslint-disable import/no-unresolved */
+import terser from "@rollup/plugin-terser";
+import nodeResolve from "@rollup/plugin-node-resolve";
+
+export default function (commandLineArgs) {
+ const plugins = [nodeResolve()];
+ if (commandLineArgs.minified) {
+ plugins.push(terser());
+ }
+
+ return {
+ input: "codemirror6/index.mjs",
+ output: {
+ file: "codemirror6/codemirror6.bundle.js",
+ format: "iife",
+ name: "CodeMirror",
+ },
+ plugins,
+ };
+}
diff --git a/devtools/client/shared/sourceeditor/test/CodeMirrorTestActors.sys.mjs b/devtools/client/shared/sourceeditor/test/CodeMirrorTestActors.sys.mjs
new file mode 100644
index 0000000000..bb0457599b
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/CodeMirrorTestActors.sys.mjs
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let gCallback;
+
+export class CodeMirrorTestParent extends JSWindowActorParent {
+ static setCallback(callback) {
+ gCallback = callback;
+ }
+
+ receiveMessage(message) {
+ if (gCallback) {
+ gCallback(message.name, message.data);
+ }
+ }
+}
+
+export class CodeMirrorTestChild extends JSWindowActorChild {
+ handleEvent(event) {
+ if (event.type == "DOMWindowCreated") {
+ this.contentWindow.wrappedJSObject.mozilla_setStatus = (
+ statusMsg,
+ type,
+ customMsg
+ ) => {
+ this.sendAsyncMessage("setStatus", {
+ statusMsg,
+ type,
+ customMsg,
+ });
+ };
+
+ this.check();
+ }
+ }
+
+ check() {
+ const doc = this.contentWindow.document;
+ const out = doc.getElementById("status");
+ if (!out || !out.classList.contains("done")) {
+ this.contentWindow.setTimeout(() => this.check(), 100);
+ return;
+ }
+
+ this.sendAsyncMessage("done", {
+ failed: this.contentWindow.wrappedJSObject.failed,
+ });
+ }
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser.toml b/devtools/client/shared/sourceeditor/test/browser.toml
new file mode 100644
index 0000000000..2c3f5ab374
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser.toml
@@ -0,0 +1,70 @@
+[DEFAULT]
+tags = "devtools"
+subsuite = "devtools"
+support-files = [
+ "codemirror/comment_test.js",
+ "codemirror/doc_test.js",
+ "codemirror/driver.js",
+ "codemirror/emacs_test.js",
+ "codemirror/mode_test.css",
+ "codemirror/mode_test.js",
+ "codemirror/multi_test.js",
+ "codemirror/search_test.js",
+ "codemirror/sublime_test.js",
+ "codemirror/test.js",
+ "codemirror/vim_test.js",
+ "codemirror/codemirror.html",
+ "codemirror/vimemacs.html",
+ "codemirror/mode/javascript/test.js",
+ "css_statemachine_testcases.css",
+ "css_statemachine_tests.json",
+ "css_autocompletion_tests.json",
+ "head.js",
+ "head.xhtml",
+ "cm_mode_ruby.js",
+ "cm_script_injection_test.js",
+ "CodeMirrorTestActors.sys.mjs",
+ "!/devtools/client/shared/test/shared-head.js",
+ "!/devtools/client/shared/test/telemetry-test-helpers.js",
+]
+
+["browser_codemirror.js"]
+
+["browser_css_autocompletion.js"]
+
+["browser_css_getInfo.js"]
+
+["browser_css_statemachine.js"]
+
+["browser_detectindent.js"]
+
+["browser_editor_addons.js"]
+
+["browser_editor_alt_b_f.js"]
+
+["browser_editor_autocomplete_basic.js"]
+
+["browser_editor_autocomplete_events.js"]
+
+["browser_editor_basic.js"]
+
+["browser_editor_cursor.js"]
+
+["browser_editor_cursor_blink.js"]
+
+["browser_editor_disableSearchAddon.js"]
+
+["browser_editor_find_again.js"]
+
+["browser_editor_goto_line.js"]
+
+["browser_editor_history.js"]
+
+["browser_editor_markers.js"]
+
+["browser_editor_movelines.js"]
+
+["browser_editor_prefs.js"]
+
+["browser_vimemacs.js"]
+skip-if = ["os == 'linux' && debug"] # bug 981707
diff --git a/devtools/client/shared/sourceeditor/test/browser_codemirror.js b/devtools/client/shared/sourceeditor/test/browser_codemirror.js
new file mode 100644
index 0000000000..ca19c20d9f
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_codemirror.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const URI =
+ "chrome://mochitests/content/browser/devtools/client/shared/sourceeditor/" +
+ "test/codemirror/codemirror.html";
+
+add_task(async function test() {
+ requestLongerTimeout(3);
+
+ /*
+ * In devtools/client/shared/sourceeditor/test/codemirror/search_test.js there is a test
+ * multilineInsensitiveSlow which assumes an operation takes less than 100ms.
+ * With a precision of 100ms, if we get unlikely and begin execution towards the
+ * end of one spot (e.g. at 95 ms) we will clamp down, take (e.g.) 10ms to execute
+ * and it will appear to take 100ms.
+ *
+ * To avoid this, we hardcode to 2ms of precision.
+ *
+ * In theory we don't need to set the pref for all of CodeMirror, in practice
+ * it seems very difficult to set a pref for just one of the tests.
+ */
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["privacy.reduceTimerPrecision", true],
+ ["privacy.resistFingerprinting.reduceTimerPrecision.microseconds", 2000],
+ ],
+ });
+
+ await runCodeMirrorTest(URI);
+});
diff --git a/devtools/client/shared/sourceeditor/test/browser_css_autocompletion.js b/devtools/client/shared/sourceeditor/test/browser_css_autocompletion.js
new file mode 100644
index 0000000000..768dbe1ac7
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_css_autocompletion.js
@@ -0,0 +1,172 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const CSSCompleter = require("resource://devtools/client/shared/sourceeditor/css-autocompleter.js");
+
+const CSS_URI =
+ "http://mochi.test:8888/browser/devtools/client/shared/sourceeditor" +
+ "/test/css_statemachine_testcases.css";
+const TESTS_URI =
+ "http://mochi.test:8888/browser/devtools/client" +
+ "/shared/sourceeditor/test/css_autocompletion_tests.json";
+
+const source = read(CSS_URI);
+const { tests } = JSON.parse(read(TESTS_URI));
+
+const TEST_URI =
+ "data:text/html;charset=UTF-8," +
+ encodeURIComponent(
+ [
+ "<!DOCTYPE html>",
+ "<html>",
+ " <head>",
+ " <title>CSS State machine tests.</title>",
+ " <style type='text/css'>",
+ "#progress {",
+ " width: 500px; height: 30px;",
+ " border: 1px solid black;",
+ " position: relative",
+ "}",
+ "#progress div {",
+ " width: 0%; height: 100%;",
+ " background: green;",
+ " position: absolute;",
+ " z-index: -1; top: 0",
+ "}",
+ "#progress.failed div {",
+ " background: red !important;",
+ "}",
+ "#progress.failed:after {",
+ " content: 'Some tests failed';",
+ " color: white",
+ "}",
+ "#progress:before {",
+ " content: 'Running test ' attr(data-progress) ' of " +
+ tests.length +
+ "';",
+ " color: white;",
+ " text-shadow: 0 0 2px darkgreen;",
+ "}",
+ " </style>",
+ " </head>",
+ " <body>",
+ " <h2>State machine tests for CSS autocompleter.</h2><br>",
+ " <div id='progress' data-progress='0'>",
+ " <div></div>",
+ " </div>",
+ " <div id='devtools-menu' class='devtools-toolbarbutton'></div>",
+ " <div id='devtools-toolbarbutton' class='devtools-menulist'></div>",
+ " <div id='devtools-anotherone'></div>",
+ " <div id='devtools-yetagain'></div>",
+ " <div id='devtools-itjustgoeson'></div>",
+ " <div id='devtools-okstopitnow'></div>",
+ " <div class='hidden-labels-box devtools-toolbarbutton devtools-menulist'></div>",
+ " <div class='devtools-menulist'></div>",
+ " <div class='devtools-menulist'></div>",
+ /* eslint-disable max-len */
+ " <tabs class='devtools-toolbarbutton'><tab></tab><tab></tab><tab></tab></tabs><tabs></tabs>",
+ /* eslint-enable max-len */
+ " <button class='category-name visible'></button>",
+ " <div class='devtools-toolbarbutton' label='true'>",
+ " <hbox class='toolbarbutton-menubutton-button'></hbox></div>",
+ " </body>",
+ " </html>",
+ ].join("\n")
+ );
+
+let browser;
+let index = 0;
+let completer = null;
+let inspector;
+
+add_task(async function test() {
+ const tab = await addTab(TEST_URI);
+ browser = tab.linkedBrowser;
+ await runTests();
+ browser = null;
+ gBrowser.removeCurrentTab();
+});
+
+async function runTests() {
+ const target = await createAndAttachTargetForTab(gBrowser.selectedTab);
+ inspector = await target.getFront("inspector");
+ const walker = inspector.walker;
+ const cssPropertiesFront = await target.getFront("cssProperties");
+ completer = new CSSCompleter({
+ walker,
+ cssProperties: cssPropertiesFront.cssProperties,
+ });
+ await checkStateAndMoveOn();
+ await completer.walker.release();
+ await target.destroy();
+ inspector = null;
+ completer = null;
+}
+
+async function checkStateAndMoveOn() {
+ if (index == tests.length) {
+ return;
+ }
+
+ const [lineCh, expectedSuggestions] = tests[index];
+ const [line, ch] = lineCh;
+
+ ++index;
+ await SpecialPowers.spawn(
+ browser,
+ [[index, tests.length]],
+ function ([idx, len]) {
+ const progress = content.document.getElementById("progress");
+ const progressDiv = content.document.querySelector("#progress > div");
+ progress.dataset.progress = idx;
+ progressDiv.style.width = (100 * idx) / len + "%";
+ }
+ );
+
+ const actualSuggestions = await completer.complete(limit(source, lineCh), {
+ line,
+ ch,
+ });
+ await checkState(expectedSuggestions, actualSuggestions);
+ await checkStateAndMoveOn();
+}
+
+async function checkState(expected, actual) {
+ if (expected.length != actual.length) {
+ ok(
+ false,
+ "Number of suggestions did not match up for state " +
+ index +
+ ". Expected: " +
+ expected.length +
+ ", Actual: " +
+ actual.length
+ );
+ await SpecialPowers.spawn(browser, [], function () {
+ const progress = content.document.getElementById("progress");
+ progress.classList.add("failed");
+ });
+ return;
+ }
+
+ for (let i = 0; i < actual.length; i++) {
+ if (expected[i] != actual[i].label) {
+ ok(
+ false,
+ "Suggestion " +
+ i +
+ " of state " +
+ index +
+ " did not match up" +
+ ". Expected: " +
+ expected[i] +
+ ". Actual: " +
+ actual[i].label
+ );
+ return;
+ }
+ }
+ ok(true, "Test " + index + " passed. ");
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_css_getInfo.js b/devtools/client/shared/sourceeditor/test/browser_css_getInfo.js
new file mode 100644
index 0000000000..c4d9385edf
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_css_getInfo.js
@@ -0,0 +1,250 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const CSSCompleter = require("resource://devtools/client/shared/sourceeditor/css-autocompleter.js");
+
+const source = [
+ ".devtools-toolbar {",
+ " -moz-appearance: none;",
+ " padding:4px 3px;border-bottom-width: 1px;",
+ " border-bottom-style: solid;",
+ "}",
+ "",
+ "#devtools-menu.devtools-menulist,",
+ ".devtools-toolbarbutton#devtools-menu {",
+ " -moz-appearance: none;",
+ " align-items: center;",
+ " min-width: 78px;",
+ " min-height: 22px;",
+ " text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);",
+ " border: 1px solid hsla(210,8%,5%,.45);",
+ " border-radius: 3px;",
+ " background: linear-gradient(hsla(212,7%,57%,.35),",
+ " hsla(212,7%,57%,.1)) padding-box;",
+ " margin: 0 3px;",
+ " color: inherit;",
+ "}",
+ "",
+ ".devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button {",
+ " flex-direction: row;",
+ "}",
+ "",
+ ".devtools-menulist:active,",
+ "#devtools-toolbarbutton:focus {",
+ " outline: 1px dotted hsla(210,30%,85%,0.7);",
+ " outline-offset : -4px;",
+ "}",
+ "",
+ ".devtools-toolbarbutton:not([label]) {",
+ " min-width: 32px;",
+ "}",
+ "",
+ ".devtools-toolbarbutton:not([label]) > .toolbarbutton-text, .devtools-toolbar {",
+ " display: none;",
+ "}",
+].join("\n");
+
+// Format of test cases :
+// [
+// {line, ch}, - The caret position at which the getInfo call should be made
+// expectedState, - The expected state at the caret
+// expectedSelector, - The expected selector for the state
+// expectedProperty, - The expected property name for states value and property
+// expectedValue, - If state is value, then the expected value
+// ]
+
+/* eslint-disable max-len */
+const tests = [
+ [{ line: 0, ch: 13 }, "selector", ".devtools-toolbar"],
+ [
+ { line: 8, ch: 13 },
+ "property",
+ [
+ "#devtools-menu.devtools-menulist",
+ ".devtools-toolbarbutton#devtools-menu ",
+ ],
+ "-moz-appearance",
+ ],
+ [
+ { line: 28, ch: 25 },
+ "value",
+ [".devtools-menulist:active", "#devtools-toolbarbutton:focus "],
+ "outline-offset",
+ "-4px",
+ ],
+ [{ line: 4, ch: 1 }, "null"],
+ [{ line: 5, ch: 0 }, "null"],
+ [{ line: 31, ch: 13 }, "selector", ".devtools-toolbarbutton:not([label])"],
+ [
+ { line: 35, ch: 23 },
+ "selector",
+ ".devtools-toolbarbutton:not([label]) > .toolbarbutton-text",
+ ],
+ [{ line: 35, ch: 70 }, "selector", ".devtools-toolbar"],
+ [
+ { line: 27, ch: 14 },
+ "value",
+ [".devtools-menulist:active", "#devtools-toolbarbutton:focus "],
+ "outline",
+ "1px dotted hsla(210,30%,85%,0.7)",
+ ],
+ [
+ { line: 16, ch: 16 },
+ "value",
+ [
+ "#devtools-menu.devtools-menulist",
+ ".devtools-toolbarbutton#devtools-menu ",
+ ],
+ "background",
+ "linear-gradient(hsla(212,7%,57%,.35),\n hsla(212,7%,57%,.1)) padding-box",
+ ],
+ [
+ { line: 16, ch: 3 },
+ "value",
+ [
+ "#devtools-menu.devtools-menulist",
+ ".devtools-toolbarbutton#devtools-menu ",
+ ],
+ "background",
+ "linear-gradient(hsla(212,7%,57%,.35),\n hsla(212,7%,57%,.1)) padding-box",
+ ],
+ [
+ { line: 15, ch: 25 },
+ "value",
+ [
+ "#devtools-menu.devtools-menulist",
+ ".devtools-toolbarbutton#devtools-menu ",
+ ],
+ "background",
+ "linear-gradient(hsla(212,7%,57%,.35),\n hsla(212,7%,57%,.1)) padding-box",
+ ],
+];
+/* eslint-enable max-len */
+
+const TEST_URI =
+ "data:text/html;charset=UTF-8," +
+ encodeURIComponent(
+ [
+ "<!DOCTYPE html>",
+ "<html>",
+ " <head>",
+ " <title>CSS contextual information tests.</title>",
+ " <style type='text/css'>",
+ "#progress {",
+ " width: 500px; height: 30px;",
+ " border: 1px solid black;",
+ " position: relative",
+ "}",
+ "#progress div {",
+ " width: 0%; height: 100%;",
+ " background: green;",
+ " position: absolute;",
+ " z-index: -1; top: 0",
+ "}",
+ "#progress.failed div {",
+ " background: red !important;",
+ "}",
+ "#progress.failed:after {",
+ " content: 'Some tests failed';",
+ " color: white",
+ "}",
+ "#progress:before {",
+ " content: 'Running test ' attr(data-progress) ' of " +
+ tests.length +
+ "';",
+ " color: white;",
+ " text-shadow: 0 0 2px darkgreen;",
+ "}",
+ " </style>",
+ " </head>",
+ " <body>",
+ " <h2>State machine tests for CSS autocompleter.</h2><br>",
+ " <div id='progress' data-progress='0'>",
+ " <div></div>",
+ " </div>",
+ " </body>",
+ " </html>",
+ ].join("\n")
+ );
+
+add_task(async function test() {
+ const tab = await addTab(TEST_URI);
+ const browser = tab.linkedBrowser;
+
+ const completer = new CSSCompleter({
+ cssProperties: getClientCssProperties(),
+ });
+ const matches = (arr, toCheck) => !arr.some((x, i) => x != toCheck[i]);
+ const checkState = (expected, actual) => {
+ if (expected[0] == "null" && actual == null) {
+ return true;
+ } else if (
+ expected[0] == actual.state &&
+ expected[0] == "selector" &&
+ expected[1] == actual.selector
+ ) {
+ return true;
+ } else if (
+ expected[0] == actual.state &&
+ expected[0] == "property" &&
+ matches(expected[1], actual.selectors) &&
+ expected[2] == actual.propertyName
+ ) {
+ return true;
+ } else if (
+ expected[0] == actual.state &&
+ expected[0] == "value" &&
+ matches(expected[1], actual.selectors) &&
+ expected[2] == actual.propertyName &&
+ expected[3] == actual.value
+ ) {
+ return true;
+ }
+ return false;
+ };
+
+ let i = 0;
+ for (const expected of tests) {
+ ++i;
+ const caret = expected.splice(0, 1)[0];
+ await SpecialPowers.spawn(
+ browser,
+ [[i, tests.length]],
+ function ([idx, len]) {
+ const progress = content.document.getElementById("progress");
+ const progressDiv = content.document.querySelector("#progress > div");
+ progress.dataset.progress = idx;
+ progressDiv.style.width = (100 * idx) / len + "%";
+ }
+ );
+ const actual = completer.getInfoAt(source, caret);
+ if (checkState(expected, actual)) {
+ ok(true, "Test " + i + " passed. ");
+ } else {
+ ok(
+ false,
+ "Test " +
+ i +
+ " failed. Expected state : [" +
+ expected +
+ "] " +
+ "but found [" +
+ actual.state +
+ ", " +
+ (actual.selector || actual.selectors) +
+ ", " +
+ actual.propertyName +
+ ", " +
+ actual.value +
+ "]."
+ );
+ await SpecialPowers.spawn(browser, [], function () {
+ const progress = content.document.getElementById("progress");
+ progress.classList.add("failed");
+ });
+ }
+ }
+ gBrowser.removeCurrentTab();
+});
diff --git a/devtools/client/shared/sourceeditor/test/browser_css_statemachine.js b/devtools/client/shared/sourceeditor/test/browser_css_statemachine.js
new file mode 100644
index 0000000000..103322904f
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_css_statemachine.js
@@ -0,0 +1,144 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const CSSCompleter = require("resource://devtools/client/shared/sourceeditor/css-autocompleter.js");
+
+const CSS_URI =
+ "http://mochi.test:8888/browser/devtools/client/shared/sourceeditor" +
+ "/test/css_statemachine_testcases.css";
+const TESTS_URI =
+ "http://mochi.test:8888/browser/devtools/client" +
+ "/shared/sourceeditor/test/css_statemachine_tests.json";
+
+const source = read(CSS_URI);
+const { tests } = JSON.parse(read(TESTS_URI));
+
+const TEST_URI =
+ "data:text/html;charset=UTF-8," +
+ encodeURIComponent(
+ [
+ "<!DOCTYPE html>",
+ "<html>",
+ " <head>",
+ " <title>CSS State machine tests.</title>",
+ " <style type='text/css'>",
+ "#progress {",
+ " width: 500px; height: 30px;",
+ " border: 1px solid black;",
+ " position: relative",
+ "}",
+ "#progress div {",
+ " width: 0%; height: 100%;",
+ " background: green;",
+ " position: absolute;",
+ " z-index: -1; top: 0",
+ "}",
+ "#progress.failed div {",
+ " background: red !important;",
+ "}",
+ "#progress.failed:after {",
+ " content: 'Some tests failed';",
+ " color: white",
+ "}",
+ "#progress:before {",
+ " content: 'Running test ' attr(data-progress) ' of " +
+ tests.length +
+ "';",
+ " color: white;",
+ " text-shadow: 0 0 2px darkgreen;",
+ "}",
+ " </style>",
+ " </head>",
+ " <body>",
+ " <h2>State machine tests for CSS autocompleter.</h2><br>",
+ " <div id='progress' data-progress='0'>",
+ " <div></div>",
+ " </div>",
+ " </body>",
+ " </html>",
+ ].join("\n")
+ );
+
+add_task(async function test() {
+ const tab = await addTab(TEST_URI);
+ const browser = tab.linkedBrowser;
+
+ const completer = new CSSCompleter({
+ cssProperties: getClientCssProperties(),
+ });
+ const checkState = state => {
+ if (state[0] == "null" && (!completer.state || completer.state == "null")) {
+ return true;
+ } else if (
+ state[0] == completer.state &&
+ state[0] == "selector" &&
+ state[1] == completer.selectorState &&
+ state[2] == completer.completing &&
+ state[3] == completer.selector
+ ) {
+ return true;
+ } else if (
+ state[0] == completer.state &&
+ state[0] == "value" &&
+ state[2] == completer.completing &&
+ state[3] == completer.propertyName
+ ) {
+ return true;
+ } else if (
+ state[0] == completer.state &&
+ state[2] == completer.completing &&
+ state[0] != "selector" &&
+ state[0] != "value"
+ ) {
+ return true;
+ }
+ return false;
+ };
+
+ let i = 0;
+ for (const testcase of tests) {
+ ++i;
+ await SpecialPowers.spawn(
+ browser,
+ [[i, tests.length]],
+ function ([idx, len]) {
+ const progress = content.document.getElementById("progress");
+ const progressDiv = content.document.querySelector("#progress > div");
+ progress.dataset.progress = idx;
+ progressDiv.style.width = (100 * idx) / len + "%";
+ }
+ );
+ completer.resolveState(limit(source, testcase[0]), {
+ line: testcase[0][0],
+ ch: testcase[0][1],
+ });
+ if (checkState(testcase[1])) {
+ ok(true, "Test " + i + " passed. ");
+ } else {
+ ok(
+ false,
+ "Test " +
+ i +
+ " failed. Expected state : [" +
+ testcase[1] +
+ "] " +
+ "but found [" +
+ completer.state +
+ ", " +
+ completer.selectorState +
+ ", " +
+ completer.completing +
+ ", " +
+ (completer.propertyName || completer.selector) +
+ "]."
+ );
+ await SpecialPowers.spawn(browser, [], function () {
+ const progress = content.document.getElementById("progress");
+ progress.classList.add("failed");
+ });
+ }
+ }
+ gBrowser.removeCurrentTab();
+});
diff --git a/devtools/client/shared/sourceeditor/test/browser_detectindent.js b/devtools/client/shared/sourceeditor/test/browser_detectindent.js
new file mode 100644
index 0000000000..80f2487417
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_detectindent.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TWO_SPACES_CODE = [
+ "/*",
+ " * tricky comment block",
+ " */",
+ "div {",
+ " color: red;",
+ " background: blue;",
+ "}",
+ " ",
+ "span {",
+ " padding-left: 10px;",
+ "}",
+].join("\n");
+
+const FOUR_SPACES_CODE = [
+ "var obj = {",
+ " addNumbers: function() {",
+ " var x = 5;",
+ " var y = 18;",
+ " return x + y;",
+ " },",
+ " ",
+ " /*",
+ " * Do some stuff to two numbers",
+ " * ",
+ " * @param x",
+ " * @param y",
+ " * ",
+ " * @return the result of doing stuff",
+ " */",
+ " subtractNumbers: function(x, y) {",
+ " var x += 7;",
+ " var y += 18;",
+ " var result = x - y;",
+ " result %= 2;",
+ " }",
+ "}",
+].join("\n");
+
+const TABS_CODE = [
+ "/*",
+ " * tricky comment block",
+ " */",
+ "div {",
+ "\tcolor: red;",
+ "\tbackground: blue;",
+ "}",
+ "",
+ "span {",
+ "\tpadding-left: 10px;",
+ "}",
+].join("\n");
+
+const NONE_CODE = [
+ "var x = 0;",
+ " // stray thing",
+ "var y = 9;",
+ " ",
+ "",
+].join("\n");
+
+async function test() {
+ waitForExplicitFinish();
+
+ const { ed, win } = await setup();
+ is(ed.getOption("indentUnit"), 2, "2 spaces before code added");
+ is(ed.getOption("indentWithTabs"), false, "spaces is default");
+
+ ed.setText(NONE_CODE);
+ is(ed.getOption("indentUnit"), 2, "2 spaces after un-detectable code");
+ is(
+ ed.getOption("indentWithTabs"),
+ false,
+ "spaces still set after un-detectable code"
+ );
+
+ ed.setText(FOUR_SPACES_CODE);
+ is(ed.getOption("indentUnit"), 4, "4 spaces detected in 4 space code");
+ is(ed.getOption("indentWithTabs"), false, "spaces detected in 4 space code");
+
+ ed.setText(TWO_SPACES_CODE);
+ is(ed.getOption("indentUnit"), 2, "2 spaces detected in 2 space code");
+ is(ed.getOption("indentWithTabs"), false, "spaces detected in 2 space code");
+
+ ed.setText(TABS_CODE);
+ is(ed.getOption("indentUnit"), 2, "2 space indentation unit");
+ is(
+ ed.getOption("indentWithTabs"),
+ true,
+ "tabs detected in majority tabs code"
+ );
+
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_addons.js b/devtools/client/shared/sourceeditor/test/browser_editor_addons.js
new file mode 100644
index 0000000000..85aec38ec3
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_addons.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function test() {
+ waitForExplicitFinish();
+
+ const { ed, win } = await setup();
+ const doc = win.document.querySelector("iframe").contentWindow.document;
+
+ // trailingspace.js
+ ed.setText("Hello ");
+ ed.setOption("showTrailingSpace", false);
+ ok(!doc.querySelector(".cm-trailingspace"));
+ ed.setOption("showTrailingSpace", true);
+ ok(doc.querySelector(".cm-trailingspace"));
+
+ // foldcode.js and foldgutter.js
+ ed.setMode(Editor.modes.js);
+ ed.setText("function main() {\nreturn 'Hello, World!';\n}");
+ executeSoon(() => testFold(doc, ed, win));
+}
+
+function testFold(doc, ed, win) {
+ // Wait until folding arrow is there.
+ if (!doc.querySelector(".CodeMirror-foldgutter-open")) {
+ executeSoon(() => testFold(doc, ed, win));
+ return;
+ }
+
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_alt_b_f.js b/devtools/client/shared/sourceeditor/test/browser_editor_alt_b_f.js
new file mode 100644
index 0000000000..cacefd8dac
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_alt_b_f.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Ensure Alt-B and Alt-F keyboard shortcuts work as expected in the source editor.
+// See Bug 1481443.
+
+add_task(async function () {
+ const { ed, win } = await setup();
+ const editorDoc = ed.container.contentDocument;
+ await promiseWaitForFocus();
+ const isMacOS = Services.appinfo.OS === "Darwin";
+
+ ed.focus();
+
+ const initialText = "a b c d e";
+ ed.setText(initialText);
+
+ ed.setCursor({ line: 1, ch: initialText.length });
+
+ EventUtils.synthesizeKey("b", { altKey: true }, editorDoc.defaultView);
+
+ // A character is added only on OSX.
+ let expectedText = isMacOS ? initialText + "b" : initialText;
+ is(
+ ed.getCursor().ch,
+ expectedText.length,
+ "Cursor is at expected position after Alt-B"
+ );
+ is(ed.getText(), expectedText, "Editor has expected content after Alt-B");
+
+ EventUtils.synthesizeKey("f", { altKey: true }, editorDoc.defaultView);
+
+ // A character is added only on OSX.
+ expectedText = isMacOS ? expectedText + "f" : initialText;
+ is(
+ ed.getCursor().ch,
+ expectedText.length,
+ "Cursor is at expected position after Alt-F"
+ );
+ is(ed.getText(), expectedText, "Editor has expected content after Alt-F");
+
+ ed.destroy();
+ win.close();
+});
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_basic.js b/devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_basic.js
new file mode 100644
index 0000000000..c7dc9c8a97
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_basic.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const AUTOCOMPLETION_PREF = "devtools.editor.autocomplete";
+
+// Test to make sure that different autocompletion modes can be created,
+// switched, and destroyed. This doesn't test the actual autocompletion
+// popups, only their integration with the editor.
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ const edWin = ed.container.contentWindow.wrappedJSObject;
+ testJS(ed, edWin);
+ testCSS(ed, edWin);
+ testPref(ed, edWin);
+ teardown(ed, win);
+}
+
+function testJS(ed, win) {
+ ok(!ed.getOption("autocomplete"), "Autocompletion is not set");
+
+ ed.setMode(Editor.modes.js);
+ ed.setOption("autocomplete", true);
+
+ ok(ed.getOption("autocomplete"), "Autocompletion is set");
+}
+
+function testCSS(ed, win) {
+ ok(ed.getOption("autocomplete"), "Autocompletion is set");
+
+ ed.setMode(Editor.modes.css);
+ ed.setOption("autocomplete", true);
+
+ ok(ed.getOption("autocomplete"), "Autocompletion is still set");
+}
+
+function testPref(ed, win) {
+ ed.setMode(Editor.modes.js);
+ ed.setOption("autocomplete", true);
+
+ ok(ed.getOption("autocomplete"), "Autocompletion is set");
+
+ info("Preffing autocompletion off");
+ Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
+
+ ok(ed.getOption("autocomplete"), "Autocompletion is still set");
+
+ Services.prefs.clearUserPref(AUTOCOMPLETION_PREF);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_events.js b/devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_events.js
new file mode 100644
index 0000000000..88fbcdbf1d
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_autocomplete_events.js
@@ -0,0 +1,158 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=UTF-8,<html><body><bar></bar>" +
+ "<div id='baz'></div><body></html>";
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ await runTests();
+});
+
+async function runTests() {
+ const target = await createAndAttachTargetForTab(gBrowser.selectedTab);
+ const inspector = await target.getFront("inspector");
+ const walker = inspector.walker;
+ const cssPropertiesFront = await target.getFront("cssProperties");
+ const { ed, win, edWin } = await setup({
+ autocomplete: true,
+ mode: Editor.modes.css,
+ autocompleteOpts: {
+ walker,
+ cssProperties: cssPropertiesFront.cssProperties,
+ },
+ });
+ await testMouse(ed, edWin);
+ await testKeyboard(ed, edWin);
+ await testKeyboardCycle(ed, edWin);
+ await testKeyboardCycleForPrefixedString(ed, edWin);
+ await testKeyboardCSSComma(ed, edWin);
+ await testCloseOnEscape(ed, edWin);
+ teardown(ed, win);
+}
+
+async function testKeyboard(ed, win) {
+ ed.focus();
+ ed.setText("b");
+ ed.setCursor({ line: 1, ch: 1 });
+
+ const popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+
+ const autocompleteKey = Editor.keyFor("autocompletion", {
+ noaccel: true,
+ }).toUpperCase();
+ EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+
+ info("Waiting for popup to be opened");
+ await popupOpened;
+
+ EventUtils.synthesizeKey("VK_RETURN", {}, win);
+ is(ed.getText(), "bar", "Editor text has been updated");
+}
+
+async function testKeyboardCycle(ed, win) {
+ ed.focus();
+ ed.setText("b");
+ ed.setCursor({ line: 1, ch: 1 });
+
+ const popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+
+ const autocompleteKey = Editor.keyFor("autocompletion", {
+ noaccel: true,
+ }).toUpperCase();
+ EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+
+ info("Waiting for popup to be opened");
+ await popupOpened;
+
+ EventUtils.synthesizeKey("VK_DOWN", {}, win);
+ is(ed.getText(), "bar", "Editor text has been updated");
+
+ EventUtils.synthesizeKey("VK_DOWN", {}, win);
+ is(ed.getText(), "body", "Editor text has been updated");
+
+ EventUtils.synthesizeKey("VK_DOWN", {}, win);
+ is(ed.getText(), "#baz", "Editor text has been updated");
+}
+
+async function testKeyboardCycleForPrefixedString(ed, win) {
+ ed.focus();
+ ed.setText("#b");
+ ed.setCursor({ line: 1, ch: 2 });
+
+ const popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+
+ const autocompleteKey = Editor.keyFor("autocompletion", {
+ noaccel: true,
+ }).toUpperCase();
+ EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+
+ info("Waiting for popup to be opened");
+ await popupOpened;
+
+ EventUtils.synthesizeKey("VK_DOWN", {}, win);
+ is(ed.getText(), "#baz", "Editor text has been updated");
+}
+
+async function testKeyboardCSSComma(ed, win) {
+ ed.focus();
+ ed.setText("b");
+ ed.setCursor({ line: 1, ch: 1 });
+
+ let isPopupOpened = false;
+ const popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+ popupOpened.then(() => {
+ isPopupOpened = true;
+ });
+
+ EventUtils.synthesizeKey(",", {}, win);
+
+ await wait(500);
+
+ ok(!isPopupOpened, "Autocompletion shouldn't be opened");
+}
+
+async function testMouse(ed, win) {
+ ed.focus();
+ ed.setText("b");
+ ed.setCursor({ line: 1, ch: 1 });
+
+ const popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+
+ const autocompleteKey = Editor.keyFor("autocompletion", {
+ noaccel: true,
+ }).toUpperCase();
+ EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+
+ info("Waiting for popup to be opened");
+ await popupOpened;
+ ed.getAutocompletionPopup()._list.children[2].click();
+ is(ed.getText(), "#baz", "Editor text has been updated");
+}
+
+async function testCloseOnEscape(ed, win) {
+ ed.focus();
+ ed.setText("b");
+ ed.setCursor({ line: 1, ch: 1 });
+
+ const popupOpened = ed.getAutocompletionPopup().once("popup-opened");
+
+ const autocompleteKey = Editor.keyFor("autocompletion", {
+ noaccel: true,
+ }).toUpperCase();
+ EventUtils.synthesizeKey("VK_" + autocompleteKey, { ctrlKey: true }, win);
+
+ info("Waiting for popup to be opened");
+ await popupOpened;
+
+ is(ed.getAutocompletionPopup().isOpen, true, "The popup is open");
+
+ const popupClosed = ed.getAutocompletionPopup().once("popup-closed");
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, win);
+
+ await popupClosed;
+ is(ed.getAutocompletionPopup().isOpen, false, "Escape key closed popup");
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_basic.js b/devtools/client/shared/sourceeditor/test/browser_editor_basic.js
new file mode 100644
index 0000000000..9373990bf7
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_basic.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ // appendTo
+ const cmFrame = win.document.querySelector("iframe");
+ const cmStyle = cmFrame.contentDocument.getElementById("cmBaseStyle");
+ ok(~cmStyle.innerHTML.indexOf(".CodeMirror"), "correct iframe is there");
+
+ // getOption/setOption
+ ok(ed.getOption("styleActiveLine"), "getOption works");
+ ed.setOption("styleActiveLine", false);
+ ok(!ed.getOption("styleActiveLine"), "setOption works");
+
+ // Language modes
+ is(ed.getMode(), Editor.modes.text, "getMode");
+ ed.setMode(Editor.modes.js);
+ is(ed.getMode(), Editor.modes.js, "setMode");
+
+ // Content
+ is(ed.getText(), "Hello.", "getText");
+ ed.setText("Hi.\nHow are you?");
+ is(ed.getText(), "Hi.\nHow are you?", "setText");
+ is(ed.getText(1), "How are you?", "getText(num)");
+ is(ed.getText(5), "", "getText(num) when num is out of scope");
+
+ ed.replaceText("YOU", { line: 1, ch: 8 }, { line: 1, ch: 11 });
+ is(ed.getText(1), "How are YOU?", "replaceText(str, from, to)");
+ ed.replaceText("you?", { line: 1, ch: 8 });
+ is(ed.getText(1), "How are you?", "replaceText(str, from)");
+ ed.replaceText("Hello.");
+ is(ed.getText(), "Hello.", "replaceText(str)");
+
+ ed.insertText(", sir/madam", { line: 0, ch: 5 });
+ is(ed.getText(), "Hello, sir/madam.", "insertText");
+
+ // Add-ons
+ ed.extend({ whoami: () => "Anton", whereami: () => "Mozilla" });
+ is(ed.whoami(), "Anton", "extend/1");
+ is(ed.whereami(), "Mozilla", "extend/2");
+
+ // Line classes
+ ed.setText("Hello!\nHow are you?");
+ ok(!ed.hasLineClass(0, "test"), "no test line class");
+ ed.addLineClass(0, "test");
+ ok(ed.hasLineClass(0, "test"), "test line class is there");
+ ed.removeLineClass(0, "test");
+ ok(!ed.hasLineClass(0, "test"), "test line class is gone");
+
+ // Font size
+ const size = ed.getFontSize();
+ is("number", typeof size, "we have the default font size");
+ ed.setFontSize(ed.getFontSize() + 1);
+ is(ed.getFontSize(), size + 1, "new font size was set");
+
+ info("Check that we display unicode values for non-printable characters");
+ ed.setText("> \u202e \u2066 - \u2069 \u2066 <");
+
+ const doc = win.document.querySelector("iframe").contentWindow.document;
+ const nonPrintableCharElements = Array.from(
+ doc.querySelectorAll(".cm-non-printable-char")
+ );
+
+ Assert.deepEqual(
+ nonPrintableCharElements.map(el => el.textContent),
+ ["\\u202e", "\\u2066", "\\u2069", "\\u2066"],
+ "non printable chars are displayed as expected"
+ );
+
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_cursor.js b/devtools/client/shared/sourceeditor/test/browser_editor_cursor.js
new file mode 100644
index 0000000000..c4e71424d5
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_cursor.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ ch(ed.getCursor(), { line: 0, ch: 0 }, "default cursor position is ok");
+ ed.setText("Hello.\nHow are you?");
+
+ ed.setCursor({ line: 1, ch: 5 });
+ ch(ed.getCursor(), { line: 1, ch: 5 }, "setCursor({ line, ch })");
+
+ ch(ed.getPosition(7), { line: 1, ch: 0 }, "getPosition(num)");
+ ch(ed.getPosition(7, 1)[0], { line: 1, ch: 0 }, "getPosition(num, num)[0]");
+ ch(ed.getPosition(7, 1)[1], { line: 0, ch: 1 }, "getPosition(num, num)[1]");
+
+ ch(ed.getOffset({ line: 1, ch: 0 }), 7, "getOffset(num)");
+ ch(
+ ed.getOffset({ line: 1, ch: 0 }, { line: 0, ch: 1 })[0],
+ 7,
+ "getOffset(num, num)[0]"
+ );
+ ch(
+ ed.getOffset({ line: 1, ch: 0 }, { line: 0, ch: 1 })[0],
+ 2,
+ "getOffset(num, num)[1]"
+ );
+
+ is(ed.getSelection(), "", "nothing is selected");
+ ed.setSelection({ line: 0, ch: 0 }, { line: 0, ch: 5 });
+ is(ed.getSelection(), "Hello", "setSelection");
+
+ ed.dropSelection();
+ is(ed.getSelection(), "", "dropSelection");
+
+ // Check that shift-click on a gutter selects the whole line (bug 919707)
+ const iframe = win.document.querySelector("iframe");
+ const gutter = iframe.contentWindow.document.querySelector(
+ ".CodeMirror-gutters"
+ );
+
+ EventUtils.sendMouseEvent(
+ { type: "mousedown", shiftKey: true },
+ gutter,
+ iframe.contentWindow
+ );
+ is(ed.getSelection(), "", "shift-click");
+
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_cursor_blink.js b/devtools/client/shared/sourceeditor/test/browser_editor_cursor_blink.js
new file mode 100644
index 0000000000..5005afc0fd
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_cursor_blink.js
@@ -0,0 +1,73 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test to make sure that the editor reacts to preference changes
+
+const CARET_BLINK_TIME = "ui.caretBlinkTime";
+
+add_task(async function () {
+ Services.prefs.clearUserPref(CARET_BLINK_TIME);
+
+ info(`Test when "${CARET_BLINK_TIME}" isn't set`);
+ let { ed, win } = await setup();
+ checkCssCustomPropertyValue(
+ ed,
+ 530,
+ "When preference isn't set, blink time is set to codeMirror default"
+ );
+ ed.destroy();
+ win.close();
+
+ info(`Test with a positive value for "${CARET_BLINK_TIME}"`);
+ let blinkTime = 200;
+ Services.prefs.setIntPref(CARET_BLINK_TIME, blinkTime);
+ ({ ed, win } = await setup());
+
+ checkCssCustomPropertyValue(
+ ed,
+ blinkTime,
+ "When preference is set, blink time reflects the pref value"
+ );
+ ed.destroy();
+ win.close();
+
+ info(`Test when "${CARET_BLINK_TIME}" is 0`);
+ blinkTime = 0;
+ Services.prefs.setIntPref(CARET_BLINK_TIME, blinkTime);
+ ({ ed, win } = await setup());
+
+ checkCssCustomPropertyValue(
+ ed,
+ blinkTime,
+ "When preference value is 0, blink time is also 0"
+ );
+ ed.destroy();
+ win.close();
+
+ info(`Test when "${CARET_BLINK_TIME}" is -1`);
+ blinkTime = -1;
+ Services.prefs.setIntPref(CARET_BLINK_TIME, blinkTime);
+ ({ ed, win } = await setup());
+
+ checkCssCustomPropertyValue(
+ ed,
+ 0,
+ "When preference value is negative, blink time is 0"
+ );
+ ed.destroy();
+ win.close();
+
+ Services.prefs.clearUserPref(CARET_BLINK_TIME);
+});
+
+function checkCssCustomPropertyValue(editor, expectedMsValue, assertionText) {
+ is(
+ editor.codeMirror
+ .getWrapperElement()
+ .style.getPropertyValue("--caret-blink-time"),
+ `${expectedMsValue}ms`,
+ assertionText
+ );
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_disableSearchAddon.js b/devtools/client/shared/sourceeditor/test/browser_editor_disableSearchAddon.js
new file mode 100644
index 0000000000..8c803786e6
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_disableSearchAddon.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Ensure disableSearchAddon config works as expected in the source editor.
+
+const isMacOS = Services.appinfo.OS === "Darwin";
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/sourceeditor.properties"
+);
+
+const FIND_KEY = L10N.getStr("find.key");
+const REPLACE_KEY = L10N.getStr(
+ isMacOS ? "replaceAllMac.key" : "replaceAll.key"
+);
+
+add_task(async function () {
+ const { ed, win } = await setup({
+ disableSearchAddon: true,
+ });
+
+ const edDoc = ed.container.contentDocument;
+ const edWin = edDoc.defaultView;
+
+ await promiseWaitForFocus();
+ ed.focus();
+
+ synthesizeKeyShortcut(FIND_KEY, edWin);
+ const searchInput = edDoc.querySelector("input[type=search]");
+ ok(!searchInput, "the search input is not displayed");
+
+ synthesizeKeyShortcut(REPLACE_KEY, edWin);
+ const replaceInput = edDoc.querySelector(".CodeMirror-dialog > input");
+ ok(!replaceInput, "the replace input is not displayed");
+
+ teardown(ed, win);
+});
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_find_again.js b/devtools/client/shared/sourceeditor/test/browser_editor_find_again.js
new file mode 100644
index 0000000000..637c6de980
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_find_again.js
@@ -0,0 +1,217 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/sourceeditor.properties"
+);
+
+const { OS } = Services.appinfo;
+
+// On linux, getting immediately the selection's range here fails, returning
+const FIND_KEY = L10N.getStr("find.key");
+const FINDNEXT_KEY = L10N.getStr("findNext.key");
+const FINDPREV_KEY = L10N.getStr("findPrev.key");
+// the replace's key with the appropriate modifiers based on OS
+const REPLACE_KEY =
+ OS == "Darwin"
+ ? L10N.getStr("replaceAllMac.key")
+ : L10N.getStr("replaceAll.key");
+
+// values like it's not selected – even if the selection is visible.
+// For the record, setting the selection's range immediately doesn't have
+// any effect.
+// It's like the <input> is not ready yet.
+// Therefore, we trigger the UI focus event to the <input>, waiting for the
+// response.
+// Using a timeout could also work, but that is more precise, ensuring also
+// the execution of the listeners added to the <input>'s focus.
+const dispatchAndWaitForFocus = target =>
+ new Promise(resolve => {
+ target.addEventListener(
+ "focus",
+ function () {
+ resolve(target);
+ },
+ { once: true }
+ );
+
+ target.dispatchEvent(new UIEvent("focus"));
+ });
+
+function openSearchBox(ed) {
+ const edDoc = ed.container.contentDocument;
+ const edWin = edDoc.defaultView;
+
+ let input = edDoc.querySelector("input[type=search]");
+ ok(!input, "search box closed");
+
+ // The editor needs the focus to properly receive the `synthesizeKey`
+ ed.focus();
+
+ synthesizeKeyShortcut(FINDNEXT_KEY, edWin);
+ input = edDoc.querySelector("input[type=search]");
+ ok(input, "find again command key opens the search box");
+}
+
+function testFindAgain(ed, inputLine, expectCursor, isFindPrev = false) {
+ const edDoc = ed.container.contentDocument;
+ const edWin = edDoc.defaultView;
+
+ const input = edDoc.querySelector("input[type=search]");
+ input.value = inputLine;
+
+ // Ensure the input has the focus before send the key – necessary on Linux,
+ // it seems that during the tests can be lost
+ input.focus();
+
+ if (isFindPrev) {
+ synthesizeKeyShortcut(FINDPREV_KEY, edWin);
+ } else {
+ synthesizeKeyShortcut(FINDNEXT_KEY, edWin);
+ }
+
+ ch(
+ ed.getCursor(),
+ expectCursor,
+ "find: " + inputLine + " expects cursor: " + expectCursor.toSource()
+ );
+}
+
+const testSearchBoxTextIsSelected = async function (ed) {
+ const edDoc = ed.container.contentDocument;
+ const edWin = edDoc.defaultView;
+
+ let input = edDoc.querySelector("input[type=search]");
+ ok(input, "search box is opened");
+
+ // Ensure the input has the focus before send the key – necessary on Linux,
+ // it seems that during the tests can be lost
+ input.focus();
+
+ // Close search box
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+
+ input = edDoc.querySelector("input[type=search]");
+ ok(!input, "search box is closed");
+
+ // Re-open the search box
+ synthesizeKeyShortcut(FIND_KEY, edWin);
+
+ input = edDoc.querySelector("input[type=search]");
+ ok(input, "find command key opens the search box");
+
+ await dispatchAndWaitForFocus(input);
+
+ let { selectionStart, selectionEnd, value } = input;
+
+ ok(
+ selectionStart === 0 && selectionEnd === value.length,
+ "search box's text is selected when re-opened"
+ );
+
+ // Removing selection
+ input.setSelectionRange(0, 0);
+
+ synthesizeKeyShortcut(FIND_KEY, edWin);
+
+ ({ selectionStart, selectionEnd } = input);
+
+ ok(
+ selectionStart === 0 && selectionEnd === value.length,
+ "search box's text is selected when find key is pressed"
+ );
+
+ // Close search box
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+};
+
+const testReplaceBoxTextIsSelected = async function (ed) {
+ const edDoc = ed.container.contentDocument;
+ const edWin = edDoc.defaultView;
+
+ let input = edDoc.querySelector(".CodeMirror-dialog > input");
+ ok(!input, "dialog box with replace is closed");
+
+ // The editor needs the focus to properly receive the `synthesizeKey`
+ ed.focus();
+
+ synthesizeKeyShortcut(REPLACE_KEY, edWin);
+
+ input = edDoc.querySelector(".CodeMirror-dialog > input");
+ ok(input, "dialog box with replace is opened");
+
+ input.value = "line 5";
+
+ // Ensure the input has the focus before send the key – necessary on Linux,
+ // it seems that during the tests can be lost
+ input.focus();
+
+ await dispatchAndWaitForFocus(input);
+
+ let { selectionStart, selectionEnd, value } = input;
+
+ ok(
+ !(selectionStart === 0 && selectionEnd === value.length),
+ "Text in dialog box is not selected"
+ );
+
+ synthesizeKeyShortcut(REPLACE_KEY, edWin);
+
+ ({ selectionStart, selectionEnd } = input);
+
+ ok(
+ selectionStart === 0 && selectionEnd === value.length,
+ "dialog box's text is selected when replace key is pressed"
+ );
+
+ // Close dialog box
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, edWin);
+};
+
+add_task(async function () {
+ const { ed, win } = await setup();
+
+ ed.setText(
+ [
+ "// line 1",
+ "// line 2",
+ "// line 3",
+ "// line 4",
+ "// line 5",
+ ].join("\n")
+ );
+
+ await promiseWaitForFocus();
+
+ openSearchBox(ed);
+
+ const testVectors = [
+ // Starting here expect data needs to get updated for length changes to
+ // "textLines" above.
+ ["line", { line: 0, ch: 7 }],
+ ["line", { line: 1, ch: 8 }],
+ ["line", { line: 2, ch: 9 }],
+ ["line", { line: 3, ch: 10 }],
+ ["line", { line: 4, ch: 11 }],
+ ["ne 3", { line: 2, ch: 11 }],
+ ["line 1", { line: 0, ch: 9 }],
+ // Testing find prev
+ ["line", { line: 4, ch: 11 }, true],
+ ["line", { line: 3, ch: 10 }, true],
+ ["line", { line: 2, ch: 9 }, true],
+ ["line", { line: 1, ch: 8 }, true],
+ ["line", { line: 0, ch: 7 }, true],
+ ];
+
+ for (const v of testVectors) {
+ await testFindAgain(ed, ...v);
+ }
+
+ await testSearchBoxTextIsSelected(ed);
+
+ await testReplaceBoxTextIsSelected(ed);
+
+ teardown(ed, win);
+});
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_goto_line.js b/devtools/client/shared/sourceeditor/test/browser_editor_goto_line.js
new file mode 100644
index 0000000000..325c8c6dd5
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_goto_line.js
@@ -0,0 +1,91 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function testJumpToLine(ed, inputLine, expectCursor) {
+ ed.jumpToLine();
+ const editorDoc = ed.container.contentDocument;
+ const lineInput = editorDoc.querySelector("input");
+ lineInput.value = inputLine;
+ EventUtils.synthesizeKey("VK_RETURN", {}, editorDoc.defaultView);
+ // CodeMirror lines and columns are 0-based.
+ ch(
+ ed.getCursor(),
+ expectCursor,
+ "jumpToLine " + inputLine + " expects cursor " + expectCursor.toSource()
+ );
+}
+
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ const textLines = [
+ "// line 1",
+ "// line 2",
+ "// line 3",
+ "// line 4",
+ "// line 5",
+ "",
+ ];
+ ed.setText(textLines.join("\n"));
+ await promiseWaitForFocus();
+
+ const testVectors = [
+ // Various useless inputs go to line 0, column 0 or do nothing.
+ ["", { line: 0, ch: 0 }],
+ [":", { line: 0, ch: 0 }],
+ [" ", { line: 0, ch: 0 }],
+ [" : ", { line: 0, ch: 0 }],
+ ["a:b", { line: 0, ch: 0 }],
+ ["LINE: COLUMN ", { line: 0, ch: 0 }],
+ ["-1", { line: 0, ch: 0 }],
+ [":-1", { line: 0, ch: 0 }],
+ ["-1:-1", { line: 0, ch: 0 }],
+ ["0", { line: 0, ch: 0 }],
+ [":0", { line: 0, ch: 0 }],
+ ["0:0", { line: 0, ch: 0 }],
+ // Starting here expect data needs to get updated for length changes to
+ // "textLines" above.
+ // Just jump to line
+ ["1", { line: 0, ch: 0 }],
+ // Jump to second character in line
+ ["1:2", { line: 0, ch: 1 }],
+ // Jump to last character on line
+ ["1:9", { line: 0, ch: 8 }],
+ // Jump just after last character on line (end of line)
+ ["1:10", { line: 0, ch: 9 }],
+ // Jump one character past end of line (gets clamped to end of line)
+ ["1:11", { line: 0, ch: 9 }],
+ ["2", { line: 1, ch: 0 }],
+ ["2:2", { line: 1, ch: 1 }],
+ ["2:10", { line: 1, ch: 9 }],
+ ["2:11", { line: 1, ch: 10 }],
+ ["2:12", { line: 1, ch: 10 }],
+ ["3", { line: 2, ch: 0 }],
+ ["3:2", { line: 2, ch: 1 }],
+ ["3:11", { line: 2, ch: 10 }],
+ ["3:12", { line: 2, ch: 11 }],
+ ["3:13", { line: 2, ch: 11 }],
+ ["4", { line: 3, ch: 0 }],
+ ["4:2", { line: 3, ch: 1 }],
+ ["4:12", { line: 3, ch: 11 }],
+ ["4:13", { line: 3, ch: 12 }],
+ ["4:14", { line: 3, ch: 12 }],
+ ["5", { line: 4, ch: 0 }],
+ ["5:2", { line: 4, ch: 1 }],
+ ["5:13", { line: 4, ch: 12 }],
+ ["5:14", { line: 4, ch: 13 }],
+ ["5:15", { line: 4, ch: 13 }],
+ // One line beyond last newline in editor text:
+ ["6", { line: 5, ch: 0 }],
+ ["6:2", { line: 5, ch: 0 }],
+ // Two line beyond last newline in editor text (gets clamped):
+ ["7", { line: 5, ch: 0 }],
+ ["7:2", { line: 5, ch: 0 }],
+ ];
+ testVectors.forEach(vector => {
+ testJumpToLine(ed, vector[0], vector[1]);
+ });
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_history.js b/devtools/client/shared/sourceeditor/test/browser_editor_history.js
new file mode 100644
index 0000000000..2602c28236
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_history.js
@@ -0,0 +1,30 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ ok(ed.isClean(), "default isClean");
+ ok(!ed.canUndo(), "default canUndo");
+ ok(!ed.canRedo(), "default canRedo");
+
+ ed.setText("Hello, World!");
+ ok(!ed.isClean(), "isClean");
+ ok(ed.canUndo(), "canUndo");
+ ok(!ed.canRedo(), "canRedo");
+
+ ed.undo();
+ ok(ed.isClean(), "isClean after undo");
+ ok(!ed.canUndo(), "canUndo after undo");
+ ok(ed.canRedo(), "canRedo after undo");
+
+ ed.setText("What's up?");
+ ed.setClean();
+ ok(ed.isClean(), "isClean after setClean");
+ ok(ed.canUndo(), "canUndo after setClean");
+ ok(!ed.canRedo(), "canRedo after setClean");
+
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_markers.js b/devtools/client/shared/sourceeditor/test/browser_editor_markers.js
new file mode 100644
index 0000000000..e56ea3a425
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_markers.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ ok(!ed.hasMarker(0, "breakpoints", "test"), "default is ok");
+ ed.addMarker(0, "breakpoints", "test");
+ ed.addMarker(0, "breakpoints", "test2");
+ ok(ed.hasMarker(0, "breakpoints", "test"), "addMarker/1");
+ ok(ed.hasMarker(0, "breakpoints", "test2"), "addMarker/2");
+
+ ed.removeMarker(0, "breakpoints", "test");
+ ok(!ed.hasMarker(0, "breakpoints", "test"), "removeMarker/1");
+ ok(ed.hasMarker(0, "breakpoints", "test2"), "removeMarker/2");
+
+ ed.removeAllMarkers("breakpoints");
+ ok(!ed.hasMarker(0, "breakpoints", "test"), "removeAllMarkers/1");
+ ok(!ed.hasMarker(0, "breakpoints", "test2"), "removeAllMarkers/2");
+
+ ed.addMarker(0, "breakpoints", "breakpoint");
+ ed.setMarkerListeners(
+ 0,
+ "breakpoints",
+ "breakpoint",
+ {
+ click: (line, marker, param) => {
+ is(line, 0, "line is ok");
+ is(marker.className, "breakpoint", "marker is ok");
+ ok(param, "click is ok");
+
+ teardown(ed, win);
+ },
+ },
+ [true]
+ );
+
+ const env = win.document.querySelector("iframe").contentWindow;
+ const div = env.document.querySelector("div.breakpoint");
+ div.click();
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_movelines.js b/devtools/client/shared/sourceeditor/test/browser_editor_movelines.js
new file mode 100644
index 0000000000..b95139b5a9
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_movelines.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ const simpleProg =
+ "function foo() {\n let i = 1;\n let j = 2;\n " + "return bar;\n}";
+ ed.setText(simpleProg);
+
+ // Move first line up
+ ed.setCursor({ line: 0, ch: 0 });
+ ed.moveLineUp();
+ is(ed.getText(0), "function foo() {", "getText(num)");
+ ch(ed.getCursor(), { line: 0, ch: 0 }, "getCursor");
+
+ // Move last line down
+ ed.setCursor({ line: 4, ch: 0 });
+ ed.moveLineDown();
+ is(ed.getText(4), "}", "getText(num)");
+ ch(ed.getCursor(), { line: 4, ch: 0 }, "getCursor");
+
+ // Move line 2 up
+ ed.setCursor({ line: 1, ch: 5 });
+ ed.moveLineUp();
+ is(ed.getText(0), " let i = 1;", "getText(num)");
+ is(ed.getText(1), "function foo() {", "getText(num)");
+ ch(ed.getCursor(), { line: 0, ch: 5 }, "getCursor");
+
+ // Undo previous move by moving line 1 down
+ ed.moveLineDown();
+ is(ed.getText(0), "function foo() {", "getText(num)");
+ is(ed.getText(1), " let i = 1;", "getText(num)");
+ ch(ed.getCursor(), { line: 1, ch: 5 }, "getCursor");
+
+ // Move line 2 and 3 up
+ ed.setSelection({ line: 1, ch: 0 }, { line: 2, ch: 0 });
+ ed.moveLineUp();
+ is(ed.getText(0), " let i = 1;", "getText(num)");
+ is(ed.getText(1), " let j = 2;", "getText(num)");
+ is(ed.getText(2), "function foo() {", "getText(num)");
+ ch(ed.getCursor("start"), { line: 0, ch: 0 }, "getCursor(string)");
+ ch(ed.getCursor("end"), { line: 1, ch: 0 }, "getCursor(string)");
+
+ // Move line 1 to 3 down twice
+ ed.dropSelection();
+ ed.setSelection({ line: 0, ch: 7 }, { line: 2, ch: 5 });
+ ed.moveLineDown();
+ ed.moveLineDown();
+ is(ed.getText(0), " return bar;", "getText(num)");
+ is(ed.getText(1), "}", "getText(num)");
+ is(ed.getText(2), " let i = 1;", "getText(num)");
+ is(ed.getText(3), " let j = 2;", "getText(num)");
+ is(ed.getText(4), "function foo() {", "getText(num)");
+ ch(ed.getCursor("start"), { line: 2, ch: 7 }, "getCursor(string)");
+ ch(ed.getCursor("end"), { line: 4, ch: 5 }, "getCursor(string)");
+
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_editor_prefs.js b/devtools/client/shared/sourceeditor/test/browser_editor_prefs.js
new file mode 100644
index 0000000000..2063796f76
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_editor_prefs.js
@@ -0,0 +1,139 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test to make sure that the editor reacts to preference changes
+
+const TAB_SIZE = "devtools.editor.tabsize";
+const ENABLE_CODE_FOLDING = "devtools.editor.enableCodeFolding";
+const EXPAND_TAB = "devtools.editor.expandtab";
+const KEYMAP = "devtools.editor.keymap";
+const AUTO_CLOSE = "devtools.editor.autoclosebrackets";
+const AUTOCOMPLETE = "devtools.editor.autocomplete";
+const DETECT_INDENT = "devtools.editor.detectindentation";
+
+async function test() {
+ waitForExplicitFinish();
+ const { ed, win } = await setup();
+ Assert.deepEqual(
+ ed.getOption("gutters"),
+ ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-foldgutter"],
+ "gutters is correct"
+ );
+
+ ed.setText("Checking preferences.");
+
+ info("Turning prefs off");
+
+ ed.setOption("autocomplete", true);
+
+ Services.prefs.setIntPref(TAB_SIZE, 2);
+ Services.prefs.setBoolPref(ENABLE_CODE_FOLDING, false);
+ Services.prefs.setBoolPref(EXPAND_TAB, false);
+ Services.prefs.setCharPref(KEYMAP, "default");
+ Services.prefs.setBoolPref(AUTO_CLOSE, false);
+ Services.prefs.setBoolPref(AUTOCOMPLETE, false);
+ Services.prefs.setBoolPref(DETECT_INDENT, false);
+
+ Assert.deepEqual(
+ ed.getOption("gutters"),
+ ["CodeMirror-linenumbers", "breakpoints"],
+ "gutters is correct"
+ );
+
+ is(ed.getOption("tabSize"), 2, "tabSize is correct");
+ is(ed.getOption("indentUnit"), 2, "indentUnit is correct");
+ is(ed.getOption("foldGutter"), false, "foldGutter is correct");
+ is(
+ ed.getOption("enableCodeFolding"),
+ undefined,
+ "enableCodeFolding is correct"
+ );
+ is(ed.getOption("indentWithTabs"), true, "indentWithTabs is correct");
+ is(ed.getOption("keyMap"), "default", "keyMap is correct");
+ is(ed.getOption("autoCloseBrackets"), false, "autoCloseBrackets is correct");
+ is(ed.getOption("autocomplete"), true, "autocomplete is correct");
+ ok(!ed.isAutocompletionEnabled(), "Autocompletion is not enabled");
+
+ info("Turning prefs on");
+
+ Services.prefs.setIntPref(TAB_SIZE, 4);
+ Services.prefs.setBoolPref(ENABLE_CODE_FOLDING, true);
+ Services.prefs.setBoolPref(EXPAND_TAB, true);
+ Services.prefs.setCharPref(KEYMAP, "sublime");
+ Services.prefs.setBoolPref(AUTO_CLOSE, true);
+ Services.prefs.setBoolPref(AUTOCOMPLETE, true);
+
+ Assert.deepEqual(
+ ed.getOption("gutters"),
+ ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-foldgutter"],
+ "gutters is correct"
+ );
+
+ is(ed.getOption("tabSize"), 4, "tabSize is correct");
+ is(ed.getOption("indentUnit"), 4, "indentUnit is correct");
+ is(ed.getOption("foldGutter"), true, "foldGutter is correct");
+ is(
+ ed.getOption("enableCodeFolding"),
+ undefined,
+ "enableCodeFolding is correct"
+ );
+ is(ed.getOption("indentWithTabs"), false, "indentWithTabs is correct");
+ is(
+ ed.getOption("autoCloseBrackets"),
+ "()[]{}''\"\"``",
+ "autoCloseBrackets is correct"
+ );
+ is(ed.getOption("autocomplete"), true, "autocomplete is correct");
+ ok(ed.isAutocompletionEnabled(), "Autocompletion is enabled");
+
+ // Since the keyMap files are lazily loaded, this can take some time. We need to wait
+ // until the option has the expected value.
+ info("Wait for the keyMap option to be updated");
+ await waitUntil(() => ed.getOption("keyMap") === "sublime");
+ is(ed.getOption("keyMap"), "sublime", "keyMap is correct");
+
+ info("Forcing foldGutter off using enableCodeFolding");
+ ed.setOption("enableCodeFolding", false);
+
+ is(ed.getOption("foldGutter"), false, "foldGutter is correct");
+ is(ed.getOption("enableCodeFolding"), false, "enableCodeFolding is correct");
+ Assert.deepEqual(
+ ed.getOption("gutters"),
+ ["CodeMirror-linenumbers", "breakpoints"],
+ "gutters is correct"
+ );
+
+ info("Forcing foldGutter on using enableCodeFolding");
+ ed.setOption("enableCodeFolding", true);
+
+ is(ed.getOption("foldGutter"), true, "foldGutter is correct");
+ is(ed.getOption("enableCodeFolding"), true, "enableCodeFolding is correct");
+ Assert.deepEqual(
+ ed.getOption("gutters"),
+ ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-foldgutter"],
+ "gutters is correct"
+ );
+
+ info("Checking indentation detection");
+
+ Services.prefs.setBoolPref(DETECT_INDENT, true);
+
+ ed.setText("Detecting\n\tTabs");
+ is(ed.getOption("indentWithTabs"), true, "indentWithTabs is correct");
+ is(ed.getOption("indentUnit"), 4, "indentUnit is correct");
+
+ ed.setText("body {\n color:red;\n a:b;\n}");
+ is(ed.getOption("indentWithTabs"), false, "indentWithTabs is correct");
+ is(ed.getOption("indentUnit"), 2, "indentUnit is correct");
+
+ Services.prefs.clearUserPref(TAB_SIZE);
+ Services.prefs.clearUserPref(EXPAND_TAB);
+ Services.prefs.clearUserPref(KEYMAP);
+ Services.prefs.clearUserPref(AUTO_CLOSE);
+ Services.prefs.clearUserPref(AUTOCOMPLETE);
+ Services.prefs.clearUserPref(DETECT_INDENT);
+
+ teardown(ed, win);
+}
diff --git a/devtools/client/shared/sourceeditor/test/browser_vimemacs.js b/devtools/client/shared/sourceeditor/test/browser_vimemacs.js
new file mode 100644
index 0000000000..cda5899b70
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/browser_vimemacs.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const URI =
+ "chrome://mochitests/content/browser/devtools/client" +
+ "/shared/sourceeditor/test/codemirror/vimemacs.html";
+
+add_task(async function test() {
+ requestLongerTimeout(4);
+ await runCodeMirrorTest(URI);
+});
diff --git a/devtools/client/shared/sourceeditor/test/cm_mode_ruby.js b/devtools/client/shared/sourceeditor/test/cm_mode_ruby.js
new file mode 100644
index 0000000000..991dede14a
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/cm_mode_ruby.js
@@ -0,0 +1,285 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function (mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("resource://devtools/client/shared/lib/codemirror.js"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function (CodeMirror) {
+ "use strict";
+
+ CodeMirror.defineMode("ruby", function (config) {
+ function wordObj(words) {
+ var o = {};
+ for (var i = 0, e = words.length; i < e; ++i) o[words[i]] = true;
+ return o;
+ }
+ var keywords = wordObj([
+ "alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined?", "do", "else",
+ "elsif", "END", "end", "ensure", "false", "for", "if", "in", "module", "next", "not", "or",
+ "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless",
+ "until", "when", "while", "yield", "nil", "raise", "throw", "catch", "fail", "loop", "callcc",
+ "caller", "lambda", "proc", "public", "protected", "private", "require", "load",
+ "require_relative", "extend", "autoload", "__END__", "__FILE__", "__LINE__", "__dir__"
+ ]);
+ var indentWords = wordObj(["def", "class", "case", "for", "while", "module", "then",
+ "catch", "loop", "proc", "begin"]);
+ var dedentWords = wordObj(["end", "until"]);
+ var matching = {"[": "]", "{": "}", "(": ")"};
+ var curPunc;
+
+ function chain(newtok, stream, state) {
+ state.tokenize.push(newtok);
+ return newtok(stream, state);
+ }
+
+ function tokenBase(stream, state) {
+ curPunc = null;
+ if (stream.sol() && stream.match("=begin") && stream.eol()) {
+ state.tokenize.push(readBlockComment);
+ return "comment";
+ }
+ if (stream.eatSpace()) return null;
+ var ch = stream.next(), m;
+ if (ch == "`" || ch == "'" || ch == '"') {
+ return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
+ } else if (ch == "/") {
+ var currentIndex = stream.current().length;
+ if (stream.skipTo("/")) {
+ var search_till = stream.current().length;
+ stream.backUp(stream.current().length - currentIndex);
+ var balance = 0; // balance brackets
+ while (stream.current().length < search_till) {
+ var chchr = stream.next();
+ if (chchr == "(") balance += 1;
+ else if (chchr == ")") balance -= 1;
+ if (balance < 0) break;
+ }
+ stream.backUp(stream.current().length - currentIndex);
+ if (balance == 0)
+ return chain(readQuoted(ch, "string-2", true), stream, state);
+ }
+ return "operator";
+ } else if (ch == "%") {
+ var style = "string", embed = true;
+ if (stream.eat("s")) style = "atom";
+ else if (stream.eat(/[WQ]/)) style = "string";
+ else if (stream.eat(/[r]/)) style = "string-2";
+ else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; }
+ var delim = stream.eat(/[^\w\s=]/);
+ if (!delim) return "operator";
+ if (matching.propertyIsEnumerable(delim)) delim = matching[delim];
+ return chain(readQuoted(delim, style, embed, true), stream, state);
+ } else if (ch == "#") {
+ stream.skipToEnd();
+ return "comment";
+ } else if (ch == "<" && (m = stream.match(/^<-?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
+ return chain(readHereDoc(m[1]), stream, state);
+ } else if (ch == "0") {
+ if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/);
+ else if (stream.eat("b")) stream.eatWhile(/[01]/);
+ else stream.eatWhile(/[0-7]/);
+ return "number";
+ } else if (/\d/.test(ch)) {
+ stream.match(/^[\d_]*(?:\.[\d_]+)?(?:[eE][+\-]?[\d_]+)?/);
+ return "number";
+ } else if (ch == "?") {
+ while (stream.match(/^\\[CM]-/)) {}
+ if (stream.eat("\\")) stream.eatWhile(/\w/);
+ else stream.next();
+ return "string";
+ } else if (ch == ":") {
+ if (stream.eat("'")) return chain(readQuoted("'", "atom", false), stream, state);
+ if (stream.eat('"')) return chain(readQuoted('"', "atom", true), stream, state);
+
+ // :> :>> :< :<< are valid symbols
+ if (stream.eat(/[\<\>]/)) {
+ stream.eat(/[\<\>]/);
+ return "atom";
+ }
+
+ // :+ :- :/ :* :| :& :! are valid symbols
+ if (stream.eat(/[\+\-\*\/\&\|\:\!]/)) {
+ return "atom";
+ }
+
+ // Symbols can't start by a digit
+ if (stream.eat(/[a-zA-Z$@_\xa1-\uffff]/)) {
+ stream.eatWhile(/[\w$\xa1-\uffff]/);
+ // Only one ? ! = is allowed and only as the last character
+ stream.eat(/[\?\!\=]/);
+ return "atom";
+ }
+ return "operator";
+ } else if (ch == "@" && stream.match(/^@?[a-zA-Z_\xa1-\uffff]/)) {
+ stream.eat("@");
+ stream.eatWhile(/[\w\xa1-\uffff]/);
+ return "variable-2";
+ } else if (ch == "$") {
+ if (stream.eat(/[a-zA-Z_]/)) {
+ stream.eatWhile(/[\w]/);
+ } else if (stream.eat(/\d/)) {
+ stream.eat(/\d/);
+ } else {
+ stream.next(); // Must be a special global like $: or $!
+ }
+ return "variable-3";
+ } else if (/[a-zA-Z_\xa1-\uffff]/.test(ch)) {
+ stream.eatWhile(/[\w\xa1-\uffff]/);
+ stream.eat(/[\?\!]/);
+ if (stream.eat(":")) return "atom";
+ return "ident";
+ } else if (ch == "|" && (state.varList || state.lastTok == "{" || state.lastTok == "do")) {
+ curPunc = "|";
+ return null;
+ } else if (/[\(\)\[\]{}\\;]/.test(ch)) {
+ curPunc = ch;
+ return null;
+ } else if (ch == "-" && stream.eat(">")) {
+ return "arrow";
+ } else if (/[=+\-\/*:\.^%<>~|]/.test(ch)) {
+ var more = stream.eatWhile(/[=+\-\/*:\.^%<>~|]/);
+ if (ch == "." && !more) curPunc = ".";
+ return "operator";
+ } else {
+ return null;
+ }
+ }
+
+ function tokenBaseUntilBrace(depth) {
+ if (!depth) depth = 1;
+ return function (stream, state) {
+ if (stream.peek() == "}") {
+ if (depth == 1) {
+ state.tokenize.pop();
+ return state.tokenize[state.tokenize.length - 1](stream, state);
+ } else {
+ state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth - 1);
+ }
+ } else if (stream.peek() == "{") {
+ state.tokenize[state.tokenize.length - 1] = tokenBaseUntilBrace(depth + 1);
+ }
+ return tokenBase(stream, state);
+ };
+ }
+ function tokenBaseOnce() {
+ var alreadyCalled = false;
+ return function (stream, state) {
+ if (alreadyCalled) {
+ state.tokenize.pop();
+ return state.tokenize[state.tokenize.length - 1](stream, state);
+ }
+ alreadyCalled = true;
+ return tokenBase(stream, state);
+ };
+ }
+ function readQuoted(quote, style, embed, unescaped) {
+ return function (stream, state) {
+ var escaped = false, ch;
+
+ if (state.context.type === "read-quoted-paused") {
+ state.context = state.context.prev;
+ stream.eat("}");
+ }
+
+ while ((ch = stream.next()) != null) {
+ if (ch == quote && (unescaped || !escaped)) {
+ state.tokenize.pop();
+ break;
+ }
+ if (embed && ch == "#" && !escaped) {
+ if (stream.eat("{")) {
+ if (quote == "}") {
+ state.context = {prev: state.context, type: "read-quoted-paused"};
+ }
+ state.tokenize.push(tokenBaseUntilBrace());
+ break;
+ } else if (/[@\$]/.test(stream.peek())) {
+ state.tokenize.push(tokenBaseOnce());
+ break;
+ }
+ }
+ escaped = !escaped && ch == "\\";
+ }
+ return style;
+ };
+ }
+ function readHereDoc(phrase) {
+ return function (stream, state) {
+ if (stream.match(phrase)) state.tokenize.pop();
+ else stream.skipToEnd();
+ return "string";
+ };
+ }
+ function readBlockComment(stream, state) {
+ if (stream.sol() && stream.match("=end") && stream.eol())
+ state.tokenize.pop();
+ stream.skipToEnd();
+ return "comment";
+ }
+
+ return {
+ startState: function () {
+ return {tokenize: [tokenBase],
+ indented: 0,
+ context: {type: "top", indented: -config.indentUnit},
+ continuedLine: false,
+ lastTok: null,
+ varList: false};
+ },
+
+ token: function (stream, state) {
+ if (stream.sol()) state.indented = stream.indentation();
+ var style = state.tokenize[state.tokenize.length - 1](stream, state), kwtype;
+ var thisTok = curPunc;
+ if (style == "ident") {
+ var word = stream.current();
+ style = state.lastTok == "." ? "property"
+ : keywords.propertyIsEnumerable(stream.current()) ? "keyword"
+ : /^[A-Z]/.test(word) ? "tag"
+ : (state.lastTok == "def" || state.lastTok == "class" || state.varList) ? "def"
+ : "variable";
+ if (style == "keyword") {
+ thisTok = word;
+ if (indentWords.propertyIsEnumerable(word)) kwtype = "indent";
+ else if (dedentWords.propertyIsEnumerable(word)) kwtype = "dedent";
+ else if ((word == "if" || word == "unless") && stream.column() == stream.indentation())
+ kwtype = "indent";
+ else if (word == "do" && state.context.indented < state.indented)
+ kwtype = "indent";
+ }
+ }
+ if (curPunc || (style && style != "comment")) state.lastTok = thisTok;
+ if (curPunc == "|") state.varList = !state.varList;
+
+ if (kwtype == "indent" || /[\(\[\{]/.test(curPunc))
+ state.context = {prev: state.context, type: curPunc || style, indented: state.indented};
+ else if ((kwtype == "dedent" || /[\)\]\}]/.test(curPunc)) && state.context.prev)
+ state.context = state.context.prev;
+
+ if (stream.eol())
+ state.continuedLine = (curPunc == "\\" || style == "operator");
+ return style;
+ },
+
+ indent: function (state, textAfter) {
+ if (state.tokenize[state.tokenize.length - 1] != tokenBase) return 0;
+ var firstChar = textAfter && textAfter.charAt(0);
+ var ct = state.context;
+ var closing = ct.type == matching[firstChar] ||
+ ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter);
+ return ct.indented + (closing ? 0 : config.indentUnit) +
+ (state.continuedLine ? config.indentUnit : 0);
+ },
+
+ electricChars: "}de", // enD and rescuE
+ lineComment: "#"
+ };
+ });
+
+ CodeMirror.defineMIME("text/x-ruby", "ruby");
+
+});
diff --git a/devtools/client/shared/sourceeditor/test/cm_script_injection_test.js b/devtools/client/shared/sourceeditor/test/cm_script_injection_test.js
new file mode 100644
index 0000000000..85d0794682
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/cm_script_injection_test.js
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* global editor */
+
+"use strict";
+
+window.addEventListener("editorReady", function () {
+ editor.setText("Script successfully injected!");
+});
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/codemirror.html b/devtools/client/shared/sourceeditor/test/codemirror/codemirror.html
new file mode 100644
index 0000000000..950747c490
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/codemirror.html
@@ -0,0 +1,213 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>CodeMirror: Basic Tests</title>
+ <link rel="stylesheet" href="chrome://devtools/content/shared/sourceeditor/codemirror/lib/codemirror.css">
+ <link rel="stylesheet" href="cm_mode_test.css">
+ <!--<link rel="stylesheet" href="../doc/docs.css">-->
+
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/codemirror.bundle.js"></script>
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/keymap/emacs.js"></script>
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/keymap/sublime.js"></script>
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/keymap/vim.js"></script>
+
+ <style type="text/css">
+ .ok {color: #090;}
+ .fail {color: #e00;}
+ .error {color: #c90;}
+ .done {font-weight: bold;}
+ #progress {
+ background: #45d;
+ color: white;
+ text-shadow: 0 0 1px #45d, 0 0 2px #45d, 0 0 3px #45d;
+ font-weight: bold;
+ white-space: pre;
+ }
+ #testground {
+ visibility: hidden;
+ }
+ #testground.offscreen {
+ visibility: visible;
+ position: absolute;
+ left: -10000px;
+ top: -10000px;
+ }
+ .CodeMirror { border: 1px solid black; }
+ </style>
+ </head>
+ <body>
+ <h1>CodeMirror: Basic Tests</h1>
+
+ <p>A limited set of programmatic sanity tests for CodeMirror.</p>
+
+ <div style="border: 1px solid black; padding: 1px; max-width: 700px;">
+ <div style="width: 0px;" id=progress><div style="padding: 3px;">Ran <span id="progress_ran">0</span><span id="progress_total"> of 0</span> tests</div></div>
+ </div>
+ <p id=status>Please enable JavaScript...</p>
+ <div id=output></div>
+
+ <div id=testground></div>
+
+ <script src="driver.js"></script>
+ <script src="test.js"></script>
+ <script src="comment_test.js"></script>
+ <script src="doc_test.js"></script>
+ <script src="driver.js"></script>
+ <script src="emacs_test.js"></script>
+ <script src="mode_test.js"></script>
+ <script src="mode/javascript/test.js"></script>
+ <script src="multi_test.js"></script>
+ <script src="search_test.js"></script>
+
+ <!-- VIM and Emacs mode tests are in vimemacs.html
+ <script src="cm_sublime_test.js"></script>
+ <script src="cm_vim_test.js"></script>
+ <script src="cm_emacs_test.js"></script>
+ -->
+
+ <!-- These modes/addons are not used by Editor
+ <script src="doc_test.js"></script>
+ <script src="../mode/css/css.js"></script>
+ <script src="../mode/css/test.js"></script>
+ <script src="../mode/css/scss_test.js"></script>
+ <script src="../mode/xml/xml.js"></script>
+ <script src="../mode/htmlmixed/htmlmixed.js"></script>
+ <script src="../mode/ruby/ruby.js"></script>
+ <script src="../mode/haml/haml.js"></script>
+ <script src="../mode/haml/test.js"></script>
+ <script src="../mode/markdown/markdown.js"></script>
+ <script src="../mode/markdown/test.js"></script>
+ <script src="../mode/gfm/gfm.js"></script>
+ <script src="../mode/gfm/test.js"></script>
+ <script src="../mode/stex/stex.js"></script>
+ <script src="../mode/stex/test.js"></script>
+ <script src="../mode/xquery/xquery.js"></script>
+ <script src="../mode/xquery/test.js"></script>
+ <script src="../addon/mode/multiplex_test.js"></script>-->
+
+ <script>
+ window.onload = runHarness;
+ CodeMirror.on(window, 'hashchange', runHarness);
+
+ function esc(str) {
+ return str.replace(/[<&]/, function(ch) { return ch == "<" ? "&lt;" : "&amp;"; });
+ }
+
+ var output = document.getElementById("output"),
+ progress = document.getElementById("progress"),
+ progressRan = document.getElementById("progress_ran").childNodes[0],
+ progressTotal = document.getElementById("progress_total").childNodes[0];
+
+ var count = 0,
+ failed = 0,
+ skipped = 0,
+ bad = "",
+ running = false, // Flag that states tests are running
+ quit = false, // Flag to quit tests ASAP
+ verbose = false, // Adds message for *every* test to output
+ phantom = false;
+
+ function runHarness(){
+ if (running) {
+ quit = true;
+ setStatus("Restarting tests...", '', true);
+ setTimeout(function(){runHarness();}, 500);
+ return;
+ }
+ filters = [];
+ verbose = false;
+ if (window.location.hash.substr(1)){
+ var strings = window.location.hash.substr(1).split(",");
+ while (strings.length) {
+ var s = strings.shift();
+ if (s === "verbose")
+ verbose = true;
+ else
+ filters.push(parseTestFilter(decodeURIComponent(s)));
+ }
+ }
+ quit = false;
+ running = true;
+ setStatus("Loading tests...");
+ count = 0;
+ failed = 0;
+ skipped = 0;
+ bad = "";
+ totalTests = countTests();
+ progressTotal.nodeValue = " of " + totalTests;
+ progressRan.nodeValue = count;
+ output.innerHTML = '';
+ document.getElementById("testground").innerHTML = "<form>" +
+ "<textarea id=\"code\" name=\"code\"></textarea>" +
+ "<input type=submit value=ok name=submit>" +
+ "</form>";
+ runTests(displayTest);
+ }
+
+ function setStatus(message, className, force){
+ if (quit && !force) return;
+ if (!message) throw("must provide message");
+ var status = document.getElementById("status").childNodes[0];
+ status.nodeValue = message;
+ status.parentNode.className = className;
+ }
+ function addOutput(name, className, code){
+ var newOutput = document.createElement("dl");
+ var newTitle = document.createElement("dt");
+ newTitle.className = className;
+ newTitle.appendChild(document.createTextNode(name));
+ newOutput.appendChild(newTitle);
+ var newMessage = document.createElement("dd");
+ newMessage.innerHTML = code;
+ newOutput.appendChild(newTitle);
+ newOutput.appendChild(newMessage);
+ output.appendChild(newOutput);
+ }
+ function displayTest(type, name, customMessage) {
+ var message = "???";
+ if (type != "done" && type != "skipped") ++count;
+ progress.style.width = (count * (progress.parentNode.clientWidth - 2) / totalTests) + "px";
+ progressRan.nodeValue = count;
+ if (type == "ok") {
+ message = "Test '" + name + "' succeeded";
+ if (!verbose) customMessage = false;
+ } else if (type == "skipped") {
+ message = "Test '" + name + "' skipped";
+ ++skipped;
+ if (!verbose) customMessage = false;
+ } else if (type == "expected") {
+ message = "Test '" + name + "' failed as expected";
+ if (!verbose) customMessage = false;
+ } else if (type == "error" || type == "fail") {
+ ++failed;
+ message = "Test '" + name + "' failed";
+ } else if (type == "done") {
+ if (failed) {
+ type += " fail";
+ message = failed + " failure" + (failed > 1 ? "s" : "");
+ } else if (count < totalTests) {
+ failed = totalTests - count;
+ type += " fail";
+ message = failed + " failure" + (failed > 1 ? "s" : "");
+ } else {
+ type += " ok";
+ message = "All passed";
+ if (skipped) {
+ message += " (" + skipped + " skipped)";
+ }
+ }
+ progressTotal.nodeValue = '';
+ customMessage = true; // Hack to avoid adding to output
+ }
+ if (window.mozilla_setStatus)
+ mozilla_setStatus(message, type, customMessage);
+ if (verbose && !customMessage) customMessage = message;
+ setStatus(message, type);
+ if (customMessage && customMessage.length > 0) {
+ addOutput(name, type, customMessage);
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/comment_test.js b/devtools/client/shared/sourceeditor/test/codemirror/comment_test.js
new file mode 100644
index 0000000000..c6b9fe8109
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/comment_test.js
@@ -0,0 +1,114 @@
+namespace = "comment_";
+
+(function() {
+ function test(name, mode, run, before, after) {
+ return testCM(name, function(cm) {
+ run(cm);
+ eq(cm.getValue(), after);
+ }, {value: before, mode: mode});
+ }
+
+ var simpleProg = "function foo() {\n return bar;\n}";
+ var inlineBlock = "foo(/* bar */ true);";
+ var inlineBlocks = "foo(/* bar */ true, /* baz */ false);";
+ var multiLineInlineBlock = ["above();", "foo(/* bar */ true);", "below();"];
+
+ test("block", "javascript", function(cm) {
+ cm.blockComment(Pos(0, 3), Pos(3, 0), {blockCommentLead: " *"});
+ }, simpleProg + "\n", "/* function foo() {\n * return bar;\n * }\n */");
+
+ test("blockToggle", "javascript", function(cm) {
+ cm.blockComment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"});
+ cm.uncomment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"});
+ }, simpleProg, simpleProg);
+
+ test("blockToggle2", "javascript", function(cm) {
+ cm.setCursor({line: 0, ch: 7 /* inside the block comment */});
+ cm.execCommand("toggleComment");
+ }, inlineBlock, "foo(bar true);");
+
+ // This test should work but currently fails.
+ // test("blockToggle3", "javascript", function(cm) {
+ // cm.setCursor({line: 0, ch: 7 /* inside the first block comment */});
+ // cm.execCommand("toggleComment");
+ // }, inlineBlocks, "foo(bar true, /* baz */ false);");
+
+ test("line", "javascript", function(cm) {
+ cm.lineComment(Pos(1, 1), Pos(1, 1));
+ }, simpleProg, "function foo() {\n// return bar;\n}");
+
+ test("lineToggle", "javascript", function(cm) {
+ cm.lineComment(Pos(0, 0), Pos(2, 1));
+ cm.uncomment(Pos(0, 0), Pos(2, 1));
+ }, simpleProg, simpleProg);
+
+ test("fallbackToBlock", "css", function(cm) {
+ cm.lineComment(Pos(0, 0), Pos(2, 1));
+ }, "html {\n border: none;\n}", "/* html {\n border: none;\n} */");
+
+ test("fallbackToLine", "ruby", function(cm) {
+ cm.blockComment(Pos(0, 0), Pos(1));
+ }, "def blah()\n return hah\n", "# def blah()\n# return hah\n");
+
+ test("ignoreExternalBlockComments", "javascript", function(cm) {
+ cm.execCommand("toggleComment");
+ }, inlineBlocks, "// " + inlineBlocks);
+
+ test("ignoreExternalBlockComments2", "javascript", function(cm) {
+ cm.setCursor({line: 0, ch: null /* eol */});
+ cm.execCommand("toggleComment");
+ }, inlineBlocks, "// " + inlineBlocks);
+
+ test("ignoreExternalBlockCommentsMultiLineAbove", "javascript", function(cm) {
+ cm.setSelection({line: 0, ch: 0}, {line: 1, ch: 1});
+ cm.execCommand("toggleComment");
+ }, multiLineInlineBlock.join("\n"), ["// " + multiLineInlineBlock[0],
+ "// " + multiLineInlineBlock[1],
+ multiLineInlineBlock[2]].join("\n"));
+
+ test("ignoreExternalBlockCommentsMultiLineBelow", "javascript", function(cm) {
+ cm.setSelection({line: 1, ch: 13 /* after end of block comment */}, {line: 2, ch: 1});
+ cm.execCommand("toggleComment");
+ }, multiLineInlineBlock.join("\n"), [multiLineInlineBlock[0],
+ "// " + multiLineInlineBlock[1],
+ "// " + multiLineInlineBlock[2]].join("\n"));
+
+ test("commentRange", "javascript", function(cm) {
+ cm.blockComment(Pos(1, 2), Pos(1, 13), {fullLines: false});
+ }, simpleProg, "function foo() {\n /*return bar;*/\n}");
+
+ test("indented", "javascript", function(cm) {
+ cm.lineComment(Pos(1, 0), Pos(2), {indent: true});
+ }, simpleProg, "function foo() {\n// return bar;\n// }");
+
+ test("singleEmptyLine", "javascript", function(cm) {
+ cm.setCursor(1);
+ cm.execCommand("toggleComment");
+ }, "a;\n\nb;", "a;\n// \nb;");
+
+ test("dontMessWithStrings", "javascript", function(cm) {
+ cm.execCommand("toggleComment");
+ }, "console.log(\"/*string*/\");", "// console.log(\"/*string*/\");");
+
+ test("dontMessWithStrings2", "javascript", function(cm) {
+ cm.execCommand("toggleComment");
+ }, "console.log(\"// string\");", "// console.log(\"// string\");");
+
+ test("dontMessWithStrings3", "javascript", function(cm) {
+ cm.execCommand("toggleComment");
+ }, "// console.log(\"// string\");", "console.log(\"// string\");");
+
+ test("includeLastLine", "javascript", function(cm) {
+ cm.execCommand("selectAll")
+ cm.execCommand("toggleComment")
+ }, "// foo\n// bar\nbaz", "// // foo\n// // bar\n// baz")
+
+ test("uncommentWithTrailingBlockEnd", "xml", function(cm) {
+ cm.execCommand("toggleComment")
+ }, "<!-- foo --> -->", "foo -->")
+
+ test("dontCommentInComment", "xml", function(cm) {
+ cm.setCursor(1, 0)
+ cm.execCommand("toggleComment")
+ }, "<!-- foo\nbar -->", "<!-- foo\nbar -->")
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/doc_test.js b/devtools/client/shared/sourceeditor/test/codemirror/doc_test.js
new file mode 100644
index 0000000000..3af20ff98d
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/doc_test.js
@@ -0,0 +1,371 @@
+(function() {
+ // A minilanguage for instantiating linked CodeMirror instances and Docs
+ function instantiateSpec(spec, place, opts) {
+ var names = {}, pos = 0, l = spec.length, editors = [];
+ while (spec) {
+ var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/);
+ var name = m[1], isDoc = m[2], cur;
+ if (m[3]) {
+ cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]}));
+ } else {
+ var other = m[5];
+ if (!names.hasOwnProperty(other)) {
+ names[other] = editors.length;
+ editors.push(CodeMirror(place, opts));
+ }
+ var doc = editors[names[other]].linkedDoc({
+ sharedHist: !m[4],
+ from: m[6] ? Number(m[6]) : null,
+ to: m[7] ? Number(m[7]) : null
+ });
+ cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc}));
+ }
+ names[name] = editors.length;
+ editors.push(cur);
+ spec = spec.slice(m[0].length);
+ }
+ return editors;
+ }
+
+ function clone(obj, props) {
+ if (!obj) return;
+ clone.prototype = obj;
+ var inst = new clone();
+ if (props) for (var n in props) if (props.hasOwnProperty(n))
+ inst[n] = props[n];
+ return inst;
+ }
+
+ function eqAll(val) {
+ var end = arguments.length, msg = null;
+ if (typeof arguments[end-1] == "string")
+ msg = arguments[--end];
+ if (i == end) throw new Error("No editors provided to eqAll");
+ for (var i = 1; i < end; ++i)
+ eq(arguments[i].getValue(), val, msg)
+ }
+
+ function testDoc(name, spec, run, opts, expectFail) {
+ if (!opts) opts = {};
+
+ return test("doc_" + name, function() {
+ var place = document.getElementById("testground");
+ var editors = instantiateSpec(spec, place, opts);
+ var successful = false;
+
+ try {
+ run.apply(null, editors);
+ successful = true;
+ } finally {
+ if (!successful || verbose) {
+ place.style.visibility = "visible";
+ } else {
+ for (var i = 0; i < editors.length; ++i)
+ if (editors[i] instanceof CodeMirror)
+ place.removeChild(editors[i].getWrapperElement());
+ }
+ }
+ }, expectFail);
+ }
+
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
+
+ function testBasic(a, b) {
+ eqAll("x", a, b);
+ a.setValue("hey");
+ eqAll("hey", a, b);
+ b.setValue("wow");
+ eqAll("wow", a, b);
+ a.replaceRange("u\nv\nw", Pos(0, 3));
+ b.replaceRange("i", Pos(0, 4));
+ b.replaceRange("j", Pos(2, 1));
+ eqAll("wowui\nv\nwj", a, b);
+ }
+
+ testDoc("basic", "A='x' B<A", testBasic);
+ testDoc("basicSeparate", "A='x' B<~A", testBasic);
+
+ testDoc("sharedHist", "A='ab\ncd\nef' B<A", function(a, b) {
+ a.replaceRange("x", Pos(0));
+ b.replaceRange("y", Pos(1));
+ a.replaceRange("z", Pos(2));
+ eqAll("abx\ncdy\nefz", a, b);
+ a.undo();
+ a.undo();
+ eqAll("abx\ncd\nef", a, b);
+ a.redo();
+ eqAll("abx\ncdy\nef", a, b);
+ b.redo();
+ eqAll("abx\ncdy\nefz", a, b);
+ a.undo(); b.undo(); a.undo(); a.undo();
+ eqAll("ab\ncd\nef", a, b);
+ }, null, ie_lt8);
+
+ testDoc("undoIntact", "A='ab\ncd\nef' B<~A", function(a, b) {
+ a.replaceRange("x", Pos(0));
+ b.replaceRange("y", Pos(1));
+ a.replaceRange("z", Pos(2));
+ a.replaceRange("q", Pos(0));
+ eqAll("abxq\ncdy\nefz", a, b);
+ a.undo();
+ a.undo();
+ eqAll("abx\ncdy\nef", a, b);
+ b.undo();
+ eqAll("abx\ncd\nef", a, b);
+ a.redo();
+ eqAll("abx\ncd\nefz", a, b);
+ a.redo();
+ eqAll("abxq\ncd\nefz", a, b);
+ a.undo(); a.undo(); a.undo(); a.undo();
+ eqAll("ab\ncd\nef", a, b);
+ b.redo();
+ eqAll("ab\ncdy\nef", a, b);
+ });
+
+ testDoc("undoConflict", "A='ab\ncd\nef' B<~A", function(a, b) {
+ a.replaceRange("x", Pos(0));
+ a.replaceRange("z", Pos(2));
+ // This should clear the first undo event in a, but not the second
+ b.replaceRange("y", Pos(0));
+ a.undo(); a.undo();
+ eqAll("abxy\ncd\nef", a, b);
+ a.replaceRange("u", Pos(2));
+ a.replaceRange("v", Pos(0));
+ // This should clear both events in a
+ b.replaceRange("w", Pos(0));
+ a.undo(); a.undo();
+ eqAll("abxyvw\ncd\nefu", a, b);
+ });
+
+ testDoc("doubleRebase", "A='ab\ncd\nef\ng' B<~A C<B", function(a, b, c) {
+ c.replaceRange("u", Pos(3));
+ a.replaceRange("", Pos(0, 0), Pos(1, 0));
+ c.undo();
+ eqAll("cd\nef\ng", a, b, c);
+ });
+
+ testDoc("undoUpdate", "A='ab\ncd\nef' B<~A", function(a, b) {
+ a.replaceRange("x", Pos(2));
+ b.replaceRange("u\nv\nw\n", Pos(0, 0));
+ a.undo();
+ eqAll("u\nv\nw\nab\ncd\nef", a, b);
+ a.redo();
+ eqAll("u\nv\nw\nab\ncd\nefx", a, b);
+ a.undo();
+ eqAll("u\nv\nw\nab\ncd\nef", a, b);
+ b.undo();
+ a.redo();
+ eqAll("ab\ncd\nefx", a, b);
+ a.undo();
+ eqAll("ab\ncd\nef", a, b);
+ });
+
+ testDoc("undoKeepRanges", "A='abcdefg' B<A", function(a, b) {
+ var m = a.markText(Pos(0, 1), Pos(0, 3), {className: "foo"});
+ b.replaceRange("x", Pos(0, 0));
+ eqCharPos(m.find().from, Pos(0, 2));
+ b.replaceRange("yzzy", Pos(0, 1), Pos(0));
+ eq(m.find(), null);
+ b.undo();
+ eqCharPos(m.find().from, Pos(0, 2));
+ b.undo();
+ eqCharPos(m.find().from, Pos(0, 1));
+ });
+
+ testDoc("longChain", "A='uv' B<A C<B D<C", function(a, b, c, d) {
+ a.replaceSelection("X");
+ eqAll("Xuv", a, b, c, d);
+ d.replaceRange("Y", Pos(0));
+ eqAll("XuvY", a, b, c, d);
+ });
+
+ testDoc("broadCast", "B<A C<A D<A E<A", function(a, b, c, d, e) {
+ b.setValue("uu");
+ eqAll("uu", a, b, c, d, e);
+ a.replaceRange("v", Pos(0, 1));
+ eqAll("uvu", a, b, c, d, e);
+ });
+
+ // A and B share a history, C and D share a separate one
+ testDoc("islands", "A='x\ny\nz' B<A C<~A D<C", function(a, b, c, d) {
+ a.replaceRange("u", Pos(0));
+ d.replaceRange("v", Pos(2));
+ b.undo();
+ eqAll("x\ny\nzv", a, b, c, d);
+ c.undo();
+ eqAll("x\ny\nz", a, b, c, d);
+ a.redo();
+ eqAll("xu\ny\nz", a, b, c, d);
+ d.redo();
+ eqAll("xu\ny\nzv", a, b, c, d);
+ });
+
+ testDoc("unlink", "B<A C<A D<B", function(a, b, c, d) {
+ a.setValue("hi");
+ b.unlinkDoc(a);
+ d.setValue("aye");
+ eqAll("hi", a, c);
+ eqAll("aye", b, d);
+ a.setValue("oo");
+ eqAll("oo", a, c);
+ eqAll("aye", b, d);
+ });
+
+ testDoc("bareDoc", "A*='foo' B*<A C<B", function(a, b, c) {
+ is(a instanceof CodeMirror.Doc);
+ is(b instanceof CodeMirror.Doc);
+ is(c instanceof CodeMirror);
+ eqAll("foo", a, b, c);
+ a.replaceRange("hey", Pos(0, 0), Pos(0));
+ c.replaceRange("!", Pos(0));
+ eqAll("hey!", a, b, c);
+ b.unlinkDoc(a);
+ b.setValue("x");
+ eqAll("x", b, c);
+ eqAll("hey!", a);
+ });
+
+ testDoc("swapDoc", "A='a' B*='b' C<A", function(a, b, c) {
+ var d = a.swapDoc(b);
+ d.setValue("x");
+ eqAll("x", c, d);
+ eqAll("b", a, b);
+ });
+
+ testDoc("docKeepsScroll", "A='x' B*='y'", function(a, b) {
+ addDoc(a, 200, 200);
+ a.scrollIntoView(Pos(199, 200));
+ var c = a.swapDoc(b);
+ a.swapDoc(c);
+ var pos = a.getScrollInfo();
+ is(pos.left > 0, "not at left");
+ is(pos.top > 0, "not at top");
+ });
+
+ testDoc("copyDoc", "A='u'", function(a) {
+ var copy = a.getDoc().copy(true);
+ a.setValue("foo");
+ copy.setValue("bar");
+ var old = a.swapDoc(copy);
+ eq(a.getValue(), "bar");
+ a.undo();
+ eq(a.getValue(), "u");
+ a.swapDoc(old);
+ eq(a.getValue(), "foo");
+ eq(old.historySize().undo, 1);
+ eq(old.copy(false).historySize().undo, 0);
+ });
+
+ testDoc("docKeepsMode", "A='1+1'", function(a) {
+ var other = CodeMirror.Doc("hi", "text/x-markdown");
+ a.setOption("mode", "text/javascript");
+ var old = a.swapDoc(other);
+ eq(a.getOption("mode"), "text/x-markdown");
+ eq(a.getMode().name, "markdown");
+ a.swapDoc(old);
+ eq(a.getOption("mode"), "text/javascript");
+ eq(a.getMode().name, "javascript");
+ });
+
+ testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) {
+ eq(b.getValue(), "2\n3");
+ eq(b.firstLine(), 1);
+ b.setCursor(Pos(4));
+ eqCharPos(b.getCursor(), Pos(2, 1));
+ a.replaceRange("-1\n0\n", Pos(0, 0));
+ eq(b.firstLine(), 3);
+ eqCharPos(b.getCursor(), Pos(4, 1));
+ a.undo();
+ eqCharPos(b.getCursor(), Pos(2, 1));
+ b.replaceRange("oyoy\n", Pos(2, 0));
+ eq(a.getValue(), "1\n2\noyoy\n3\n4\n5");
+ b.undo();
+ eq(a.getValue(), "1\n2\n3\n4\n5");
+ });
+
+ testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) {
+ a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1));
+ eq(b.firstLine(), 2);
+ eq(b.lineCount(), 2);
+ eq(b.getValue(), "z3\n44");
+ a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1));
+ eq(b.firstLine(), 2);
+ eq(b.getValue(), "z3\n4q");
+ eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5");
+ a.execCommand("selectAll");
+ a.replaceSelection("!");
+ eqAll("!", a, b);
+ });
+
+
+ testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) {
+ var mark = b.markText(Pos(0, 1), Pos(3, 1),
+ {className: "cm-searching", shared: true});
+ var found = a.findMarksAt(Pos(0, 2));
+ eq(found.length, 1);
+ eq(found[0], mark);
+ eq(c.findMarksAt(Pos(1, 1)).length, 1);
+ eqCharPos(mark.find().from, Pos(0, 1));
+ eqCharPos(mark.find().to, Pos(3, 1));
+ b.replaceRange("x\ny\n", Pos(0, 0));
+ eqCharPos(mark.find().from, Pos(2, 1));
+ eqCharPos(mark.find().to, Pos(5, 1));
+ var cleared = 0;
+ CodeMirror.on(mark, "clear", function() {++cleared;});
+ b.operation(function(){mark.clear();});
+ eq(a.findMarksAt(Pos(3, 1)).length, 0);
+ eq(b.findMarksAt(Pos(3, 1)).length, 0);
+ eq(c.findMarksAt(Pos(3, 1)).length, 0);
+ eq(mark.find(), null);
+ eq(cleared, 1);
+ });
+
+ testDoc("sharedMarkerCopy", "A='abcde'", function(a) {
+ var shared = a.markText(Pos(0, 1), Pos(0, 3), {shared: true});
+ var b = a.linkedDoc();
+ var found = b.findMarksAt(Pos(0, 2));
+ eq(found.length, 1);
+ eq(found[0], shared);
+ shared.clear();
+ eq(b.findMarksAt(Pos(0, 2)), 0);
+ });
+
+ testDoc("sharedMarkerDetach", "A='abcde' B<A C<B", function(a, b, c) {
+ var shared = a.markText(Pos(0, 1), Pos(0, 3), {shared: true});
+ a.unlinkDoc(b);
+ var inB = b.findMarksAt(Pos(0, 2));
+ eq(inB.length, 1);
+ is(inB[0] != shared);
+ var inC = c.findMarksAt(Pos(0, 2));
+ eq(inC.length, 1);
+ is(inC[0] != shared);
+ inC[0].clear();
+ is(shared.find());
+ });
+
+ testDoc("sharedBookmark", "A='ab\ncd\nef\ngh' B<A C<~A/1-2", function(a, b, c) {
+ var mark = b.setBookmark(Pos(1, 1), {shared: true});
+ var found = a.findMarksAt(Pos(1, 1));
+ eq(found.length, 1);
+ eq(found[0], mark);
+ eq(c.findMarksAt(Pos(1, 1)).length, 1);
+ eqCharPos(mark.find(), Pos(1, 1));
+ b.replaceRange("x\ny\n", Pos(0, 0));
+ eqCharPos(mark.find(), Pos(3, 1));
+ var cleared = 0;
+ CodeMirror.on(mark, "clear", function() {++cleared;});
+ b.operation(function() {mark.clear();});
+ eq(a.findMarks(Pos(0, 0), Pos(5)).length, 0);
+ eq(b.findMarks(Pos(0, 0), Pos(5)).length, 0);
+ eq(c.findMarks(Pos(0, 0), Pos(5)).length, 0);
+ eq(mark.find(), null);
+ eq(cleared, 1);
+ });
+
+ testDoc("undoInSubview", "A='line 0\nline 1\nline 2\nline 3\nline 4' B<A/1-4", function(a, b) {
+ b.replaceRange("x", Pos(2, 0));
+ a.undo();
+ eq(a.getValue(), "line 0\nline 1\nline 2\nline 3\nline 4");
+ eq(b.getValue(), "line 1\nline 2\nline 3");
+ });
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/driver.js b/devtools/client/shared/sourceeditor/test/codemirror/driver.js
new file mode 100644
index 0000000000..8789f73be9
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/driver.js
@@ -0,0 +1,142 @@
+var tests = [], filters = [], allNames = [];
+
+function Failure(why) {this.message = why;}
+Failure.prototype.toString = function() { return this.message; };
+
+function indexOf(collection, elt) {
+ if (collection.indexOf) return collection.indexOf(elt);
+ for (var i = 0, e = collection.length; i < e; ++i)
+ if (collection[i] == elt) return i;
+ return -1;
+}
+
+function test(name, run, expectedFail) {
+ // Force unique names
+ var originalName = name;
+ var i = 2; // Second function would be NAME_2
+ while (indexOf(allNames, name) !== -1){
+ name = originalName + "_" + i;
+ i++;
+ }
+ allNames.push(name);
+ // Add test
+ tests.push({name: name, func: run, expectedFail: expectedFail});
+ return name;
+}
+var namespace = "";
+function testCM(name, run, opts, expectedFail) {
+ return test(namespace + name, function() {
+ var place = document.getElementById("testground"), cm = window.cm = CodeMirror(place, opts);
+ var successful = false;
+ try {
+ run(cm);
+ successful = true;
+ } finally {
+ if (!successful || verbose) {
+ place.style.visibility = "visible";
+ } else {
+ place.removeChild(cm.getWrapperElement());
+ }
+ }
+ }, expectedFail);
+}
+
+function runTests(callback) {
+ var totalTime = 0;
+ function step(i) {
+ for (;;) {
+ if (i === tests.length) {
+ running = false;
+ return callback("done");
+ }
+ var test = tests[i], skip = false;
+ if (filters.length) {
+ skip = true;
+ for (var j = 0; j < filters.length; j++)
+ if (test.name.match(filters[j])) skip = false;
+ }
+ if (skip) {
+ callback("skipped", test.name, message);
+ i++;
+ } else {
+ break;
+ }
+ }
+ var expFail = test.expectedFail, startTime = +new Date, threw = false;
+ try {
+ var message = test.func();
+ } catch(e) {
+ threw = true;
+ if (expFail) callback("expected", test.name);
+ else if (e instanceof Failure) callback("fail", test.name, e.message);
+ else {
+ var pos = /(?:\bat |@).*?([^\/:]+):(\d+)/.exec(e.stack);
+ if (pos) console["log"](e.stack);
+ callback("error", test.name, e.toString() + (pos ? " (" + pos[1] + ":" + pos[2] + ")" : ""));
+ }
+ }
+ if (!threw) {
+ if (expFail) callback("fail", test.name, message || "expected failure, but passed");
+ else callback("ok", test.name, message);
+ }
+ if (!quit) { // Run next test
+ var delay = 0;
+ totalTime += (+new Date) - startTime;
+ if (totalTime > 500){
+ totalTime = 0;
+ delay = 50;
+ }
+ setTimeout(function(){step(i + 1);}, delay);
+ } else { // Quit tests
+ running = false;
+ return null;
+ }
+ }
+ step(0);
+}
+
+function label(str, msg) {
+ if (msg) return str + " (" + msg + ")";
+ return str;
+}
+function eq(a, b, msg) {
+ if (a != b) throw new Failure(label(a + " != " + b, msg));
+}
+function near(a, b, margin, msg) {
+ if (Math.abs(a - b) > margin)
+ throw new Failure(label(a + " is not close to " + b + " (" + margin + ")", msg));
+}
+function eqCharPos(a, b, msg) {
+ function str(p) { return "{line:" + p.line + ",ch:" + p.ch + ",sticky:" + p.sticky + "}"; }
+ if (a == b) return;
+ if (a == null) throw new Failure(label("comparing null to " + str(b), msg));
+ if (b == null) throw new Failure(label("comparing " + str(a) + " to null", msg));
+ if (a.line != b.line || a.ch != b.ch) throw new Failure(label(str(a) + " != " + str(b), msg));
+}
+function eqCursorPos(a, b, msg) {
+ eqCharPos(a, b, msg);
+ if (a) eq(a.sticky, b.sticky, msg ? msg + ' (sticky)' : 'sticky');
+}
+function is(a, msg) {
+ if (!a) throw new Failure(label("assertion failed", msg));
+}
+
+function countTests() {
+ if (!filters.length) return tests.length;
+ var sum = 0;
+ for (var i = 0; i < tests.length; ++i) {
+ var name = tests[i].name;
+ for (var j = 0; j < filters.length; j++) {
+ if (name.match(filters[j])) {
+ ++sum;
+ break;
+ }
+ }
+ }
+ return sum;
+}
+
+function parseTestFilter(s) {
+ if (/_\*$/.test(s)) return new RegExp("^" + s.slice(0, s.length - 2), "i");
+ else return new RegExp(s, "i");
+}
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/emacs_test.js b/devtools/client/shared/sourceeditor/test/codemirror/emacs_test.js
new file mode 100644
index 0000000000..412dba4b42
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/emacs_test.js
@@ -0,0 +1,149 @@
+(function() {
+ "use strict";
+
+ var Pos = CodeMirror.Pos;
+ namespace = "emacs_";
+
+ var eventCache = {};
+ function fakeEvent(keyName) {
+ var event = eventCache[key];
+ if (event) return event;
+
+ var ctrl, shift, alt;
+ var key = keyName.replace(/\w+-/g, function(type) {
+ if (type == "Ctrl-") ctrl = true;
+ else if (type == "Alt-") alt = true;
+ else if (type == "Shift-") shift = true;
+ return "";
+ });
+ var code;
+ for (var c in CodeMirror.keyNames)
+ if (CodeMirror.keyNames[c] == key) { code = c; break; }
+ if (code == null) throw new Error("Unknown key: " + key);
+
+ return eventCache[keyName] = {
+ type: "keydown", keyCode: code, ctrlKey: ctrl, shiftKey: shift, altKey: alt,
+ preventDefault: function(){}, stopPropagation: function(){}
+ };
+ }
+
+ function sim(name, start /*, actions... */) {
+ var keys = Array.prototype.slice.call(arguments, 2);
+ testCM(name, function(cm) {
+ for (var i = 0; i < keys.length; ++i) {
+ var cur = keys[i];
+ if (cur instanceof Pos) cm.setCursor(cur);
+ else if (cur.call) cur(cm);
+ else cm.triggerOnKeyDown(fakeEvent(cur));
+ }
+ }, {keyMap: "emacs", value: start, mode: "javascript"});
+ }
+
+ function at(line, ch, sticky) { return function(cm) { eqCursorPos(cm.getCursor(), Pos(line, ch, sticky)); }; }
+ function txt(str) { return function(cm) { eq(cm.getValue(), str); }; }
+
+ sim("motionHSimple", "abc", "Ctrl-F", "Ctrl-F", "Ctrl-B", at(0, 1, "after"));
+ sim("motionHMulti", "abcde",
+ "Ctrl-4", "Ctrl-F", at(0, 4, "before"), "Ctrl--", "Ctrl-2", "Ctrl-F", at(0, 2, "after"),
+ "Ctrl-5", "Ctrl-B", at(0, 0, "after"));
+
+ sim("motionHWord", "abc. def ghi",
+ "Alt-F", at(0, 3, "before"), "Alt-F", at(0, 8, "before"),
+ "Ctrl-B", "Alt-B", at(0, 5, "after"), "Alt-B", at(0, 0, "after"));
+ sim("motionHWordMulti", "abc. def ghi ",
+ "Ctrl-3", "Alt-F", at(0, 12, "before"), "Ctrl-2", "Alt-B", at(0, 5, "after"),
+ "Ctrl--", "Alt-B", at(0, 8, "before"));
+
+ sim("motionVSimple", "a\nb\nc\n", "Ctrl-N", "Ctrl-N", "Ctrl-P", at(1, 0, "after"));
+ sim("motionVMulti", "a\nb\nc\nd\ne\n",
+ "Ctrl-2", "Ctrl-N", at(2, 0, "after"), "Ctrl-F", "Ctrl--", "Ctrl-N", at(1, 1, "before"),
+ "Ctrl--", "Ctrl-3", "Ctrl-P", at(4, 1, "before"));
+
+ sim("killYank", "abc\ndef\nghi",
+ "Ctrl-F", "Ctrl-Space", "Ctrl-N", "Ctrl-N", "Ctrl-W", "Ctrl-E", "Ctrl-Y",
+ txt("ahibc\ndef\ng"));
+ sim("killRing", "abcdef",
+ "Ctrl-Space", "Ctrl-F", "Ctrl-W", "Ctrl-Space", "Ctrl-F", "Ctrl-W",
+ "Ctrl-Y", "Alt-Y",
+ txt("acdef"));
+ sim("copyYank", "abcd",
+ "Ctrl-Space", "Ctrl-E", "Alt-W", "Ctrl-Y",
+ txt("abcdabcd"));
+
+ sim("killLineSimple", "foo\nbar", "Ctrl-F", "Ctrl-K", txt("f\nbar"));
+ sim("killLineEmptyLine", "foo\n \nbar", "Ctrl-N", "Ctrl-K", txt("foo\nbar"));
+ sim("killLineMulti", "foo\nbar\nbaz",
+ "Ctrl-F", "Ctrl-F", "Ctrl-K", "Ctrl-K", "Ctrl-K", "Ctrl-A", "Ctrl-Y",
+ txt("o\nbarfo\nbaz"));
+
+ sim("moveByParagraph", "abc\ndef\n\n\nhij\nklm\n\n",
+ "Ctrl-F", "Ctrl-Down", at(2, 0), "Ctrl-Down", at(6, 0),
+ "Ctrl-N", "Ctrl-Up", at(3, 0), "Ctrl-Up", at(0, 0),
+ Pos(1, 2), "Ctrl-Down", at(2, 0), Pos(4, 2), "Ctrl-Up", at(3, 0));
+ sim("moveByParagraphMulti", "abc\n\ndef\n\nhij\n\nklm",
+ "Ctrl-U", "2", "Ctrl-Down", at(3, 0),
+ "Shift-Alt-.", "Ctrl-3", "Ctrl-Up", at(1, 0));
+
+ sim("moveBySentence", "sentence one! sentence\ntwo\n\nparagraph two",
+ "Alt-E", at(0, 13), "Alt-E", at(1, 3), "Ctrl-F", "Alt-A", at(0, 13));
+
+ sim("moveByExpr", "function foo(a, b) {}",
+ "Ctrl-Alt-F", at(0, 8), "Ctrl-Alt-F", at(0, 12), "Ctrl-Alt-F", at(0, 18),
+ "Ctrl-Alt-B", at(0, 12), "Ctrl-Alt-B", at(0, 9));
+ sim("moveByExprMulti", "foo bar baz bug",
+ "Ctrl-2", "Ctrl-Alt-F", at(0, 7),
+ "Ctrl--", "Ctrl-Alt-F", at(0, 4),
+ "Ctrl--", "Ctrl-2", "Ctrl-Alt-B", at(0, 11));
+ sim("delExpr", "var x = [\n a,\n b\n c\n];",
+ Pos(0, 8), "Ctrl-Alt-K", txt("var x = ;"), "Ctrl-/",
+ Pos(4, 1), "Ctrl-Alt-Backspace", txt("var x = ;"));
+ sim("delExprMulti", "foo bar baz",
+ "Ctrl-2", "Ctrl-Alt-K", txt(" baz"),
+ "Ctrl-/", "Ctrl-E", "Ctrl-2", "Ctrl-Alt-Backspace", txt("foo "));
+
+ sim("justOneSpace", "hi bye ",
+ Pos(0, 4), "Alt-Space", txt("hi bye "),
+ Pos(0, 4), "Alt-Space", txt("hi b ye "),
+ "Ctrl-A", "Alt-Space", "Ctrl-E", "Alt-Space", txt(" hi b ye "));
+
+ sim("openLine", "foo bar", "Alt-F", "Ctrl-O", txt("foo\n bar"))
+
+ sim("transposeChar", "abcd\ne",
+ "Ctrl-F", "Ctrl-T", "Ctrl-T", txt("bcad\ne"), at(0, 3),
+ "Ctrl-F", "Ctrl-T", "Ctrl-T", "Ctrl-T", txt("bcda\ne"), at(0, 4),
+ "Ctrl-F", "Ctrl-T", txt("bcde\na"), at(1, 1));
+
+ sim("manipWordCase", "foo BAR bAZ",
+ "Alt-C", "Alt-L", "Alt-U", txt("Foo bar BAZ"),
+ "Ctrl-A", "Alt-U", "Alt-L", "Alt-C", txt("FOO bar Baz"));
+ sim("manipWordCaseMulti", "foo Bar bAz",
+ "Ctrl-2", "Alt-U", txt("FOO BAR bAz"),
+ "Ctrl-A", "Ctrl-3", "Alt-C", txt("Foo Bar Baz"));
+
+ sim("upExpr", "foo {\n bar[];\n baz(blah);\n}",
+ Pos(2, 7), "Ctrl-Alt-U", at(2, 5), "Ctrl-Alt-U", at(0, 4));
+ sim("transposeExpr", "do foo[bar] dah",
+ Pos(0, 6), "Ctrl-Alt-T", txt("do [bar]foo dah"));
+
+ sim("clearMark", "abcde", Pos(0, 2), "Ctrl-Space", "Ctrl-F", "Ctrl-F",
+ "Ctrl-G", "Ctrl-W", txt("abcde"));
+
+ sim("delRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Delete", txt("cde"));
+ sim("backspaceRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Backspace", txt("cde"));
+
+ sim("backspaceDoesntAddToRing", "foobar", "Ctrl-F", "Ctrl-F", "Ctrl-F", "Ctrl-K", "Backspace", "Backspace", "Ctrl-Y", txt("fbar"));
+
+ testCM("save", function(cm) {
+ var saved = false;
+ CodeMirror.commands.save = function(cm) { saved = cm.getValue(); };
+ cm.triggerOnKeyDown(fakeEvent("Ctrl-X"));
+ cm.triggerOnKeyDown(fakeEvent("Ctrl-S"));
+ is(saved, "hi");
+ }, {value: "hi", keyMap: "emacs"});
+
+ testCM("gotoInvalidLineFloat", function(cm) {
+ cm.openDialog = function(_, cb) { cb("2.2"); };
+ cm.triggerOnKeyDown(fakeEvent("Alt-G"));
+ cm.triggerOnKeyDown(fakeEvent("G"));
+ }, {value: "1\n2\n3\n4", keyMap: "emacs"});
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/mode/javascript/test.js b/devtools/client/shared/sourceeditor/test/codemirror/mode/javascript/test.js
new file mode 100644
index 0000000000..327eac76b9
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/mode/javascript/test.js
@@ -0,0 +1,513 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function() {
+ var mode = CodeMirror.getMode({indentUnit: 2}, "javascript");
+ function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+ MT("locals",
+ "[keyword function] [def foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }");
+
+ MT("comma-and-binop",
+ "[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }");
+
+ MT("destructuring",
+ "([keyword function]([def a], [[[def b], [def c] ]]) {",
+ " [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);",
+ " [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
+ "})();");
+
+ MT("destructure_trailing_comma",
+ "[keyword let] {[def a], [def b],} [operator =] [variable foo];",
+ "[keyword let] [def c];"); // Parser still in good state?
+
+ MT("class_body",
+ "[keyword class] [def Foo] {",
+ " [property constructor]() {}",
+ " [property sayName]() {",
+ " [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];",
+ " }",
+ "}");
+
+ MT("class",
+ "[keyword class] [def Point] [keyword extends] [variable SuperThing] {",
+ " [keyword get] [property prop]() { [keyword return] [number 24]; }",
+ " [property constructor]([def x], [def y]) {",
+ " [keyword super]([string 'something']);",
+ " [keyword this].[property x] [operator =] [variable-2 x];",
+ " }",
+ "}");
+
+ MT("anonymous_class_expression",
+ "[keyword const] [def Adder] [operator =] [keyword class] [keyword extends] [variable Arithmetic] {",
+ " [property add]([def a], [def b]) {}",
+ "};");
+
+ MT("named_class_expression",
+ "[keyword const] [def Subber] [operator =] [keyword class] [def Subtract] {",
+ " [property sub]([def a], [def b]) {}",
+ "};");
+
+ MT("class_async_method",
+ "[keyword class] [def Foo] {",
+ " [property sayName1]() {}",
+ " [keyword async] [property sayName2]() {}",
+ "}");
+
+ MT("import",
+ "[keyword function] [def foo]() {",
+ " [keyword import] [def $] [keyword from] [string 'jquery'];",
+ " [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
+ "}");
+
+ MT("import_trailing_comma",
+ "[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']")
+
+ MT("import_dynamic",
+ "[keyword import]([string 'baz']).[property then]")
+
+ MT("import_dynamic",
+ "[keyword const] [def t] [operator =] [keyword import]([string 'baz']).[property then]")
+
+ MT("const",
+ "[keyword function] [def f]() {",
+ " [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
+ "}");
+
+ MT("for/of",
+ "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}");
+
+ MT("for await",
+ "[keyword for] [keyword await]([keyword let] [def of] [keyword of] [variable something]) {}");
+
+ MT("generator",
+ "[keyword function*] [def repeat]([def n]) {",
+ " [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
+ " [keyword yield] [variable-2 i];",
+ "}");
+
+ MT("let_scoping",
+ "[keyword function] [def scoped]([def n]) {",
+ " { [keyword var] [def i]; } [variable-2 i];",
+ " { [keyword let] [def j]; [variable-2 j]; } [variable j];",
+ " [keyword if] ([atom true]) { [keyword const] [def k]; [variable-2 k]; } [variable k];",
+ "}");
+
+ MT("switch_scoping",
+ "[keyword switch] ([variable x]) {",
+ " [keyword default]:",
+ " [keyword let] [def j];",
+ " [keyword return] [variable-2 j]",
+ "}",
+ "[variable j];")
+
+ MT("leaving_scope",
+ "[keyword function] [def a]() {",
+ " {",
+ " [keyword const] [def x] [operator =] [number 1]",
+ " [keyword if] ([atom true]) {",
+ " [keyword let] [def y] [operator =] [number 2]",
+ " [keyword var] [def z] [operator =] [number 3]",
+ " [variable console].[property log]([variable-2 x], [variable-2 y], [variable-2 z])",
+ " }",
+ " [variable console].[property log]([variable-2 x], [variable y], [variable-2 z])",
+ " }",
+ " [variable console].[property log]([variable x], [variable y], [variable-2 z])",
+ "}")
+
+ MT("quotedStringAddition",
+ "[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
+
+ MT("quotedFatArrow",
+ "[keyword let] [def f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];");
+
+ MT("fatArrow",
+ "[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);",
+ "[variable a];", // No longer in scope
+ "[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];",
+ "[variable c];");
+
+ MT("fatArrow_stringDefault",
+ "([def a], [def b] [operator =] [string 'x\\'y']) [operator =>] [variable-2 a] [operator +] [variable-2 b]")
+
+ MT("spread",
+ "[keyword function] [def f]([def a], [meta ...][def b]) {",
+ " [variable something]([variable-2 a], [meta ...][variable-2 b]);",
+ "}");
+
+ MT("quasi",
+ "[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
+
+ MT("quasi_no_function",
+ "[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
+
+ MT("indent_statement",
+ "[keyword var] [def x] [operator =] [number 10]",
+ "[variable x] [operator +=] [variable y] [operator +]",
+ " [atom Infinity]",
+ "[keyword debugger];");
+
+ MT("indent_if",
+ "[keyword if] ([number 1])",
+ " [keyword break];",
+ "[keyword else] [keyword if] ([number 2])",
+ " [keyword continue];",
+ "[keyword else]",
+ " [number 10];",
+ "[keyword if] ([number 1]) {",
+ " [keyword break];",
+ "} [keyword else] [keyword if] ([number 2]) {",
+ " [keyword continue];",
+ "} [keyword else] {",
+ " [number 10];",
+ "}");
+
+ MT("indent_for",
+ "[keyword for] ([keyword var] [def i] [operator =] [number 0];",
+ " [variable i] [operator <] [number 100];",
+ " [variable i][operator ++])",
+ " [variable doSomething]([variable i]);",
+ "[keyword debugger];");
+
+ MT("indent_c_style",
+ "[keyword function] [def foo]()",
+ "{",
+ " [keyword debugger];",
+ "}");
+
+ MT("indent_else",
+ "[keyword for] (;;)",
+ " [keyword if] ([variable foo])",
+ " [keyword if] ([variable bar])",
+ " [number 1];",
+ " [keyword else]",
+ " [number 2];",
+ " [keyword else]",
+ " [number 3];");
+
+ MT("indent_funarg",
+ "[variable foo]([number 10000],",
+ " [keyword function]([def a]) {",
+ " [keyword debugger];",
+ "};");
+
+ MT("indent_below_if",
+ "[keyword for] (;;)",
+ " [keyword if] ([variable foo])",
+ " [number 1];",
+ "[number 2];");
+
+ MT("indent_semicolonless_if",
+ "[keyword function] [def foo]() {",
+ " [keyword if] ([variable x])",
+ " [variable foo]()",
+ "}")
+
+ MT("indent_semicolonless_if_with_statement",
+ "[keyword function] [def foo]() {",
+ " [keyword if] ([variable x])",
+ " [variable foo]()",
+ " [variable bar]()",
+ "}")
+
+ MT("multilinestring",
+ "[keyword var] [def x] [operator =] [string 'foo\\]",
+ "[string bar'];");
+
+ MT("scary_regexp",
+ "[string-2 /foo[[/]]bar/];");
+
+ MT("indent_strange_array",
+ "[keyword var] [def x] [operator =] [[",
+ " [number 1],,",
+ " [number 2],",
+ "]];",
+ "[number 10];");
+
+ MT("param_default",
+ "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {",
+ " [keyword return] [variable-2 x];",
+ "}");
+
+ MT(
+ "param_destructuring",
+ "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {",
+ " [keyword return] [variable-2 x];",
+ "}");
+
+ MT("new_target",
+ "[keyword function] [def F]([def target]) {",
+ " [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {",
+ " [keyword return] [keyword new]",
+ " .[keyword target];",
+ " }",
+ "}");
+
+ MT("async",
+ "[keyword async] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }");
+
+ MT("async_assignment",
+ "[keyword const] [def foo] [operator =] [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; };");
+
+ MT("async_object",
+ "[keyword let] [def obj] [operator =] { [property async]: [atom false] };");
+
+ // async be highlighet as keyword and foo as def, but it requires potentially expensive look-ahead. See #4173
+ MT("async_object_function",
+ "[keyword let] [def obj] [operator =] { [property async] [property foo]([def args]) { [keyword return] [atom true]; } };");
+
+ MT("async_object_properties",
+ "[keyword let] [def obj] [operator =] {",
+ " [property prop1]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },",
+ " [property prop2]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },",
+ " [property prop3]: [keyword async] [keyword function] [def prop3]([def args]) { [keyword return] [atom true]; },",
+ "};");
+
+ MT("async_arrow",
+ "[keyword const] [def foo] [operator =] [keyword async] ([def args]) [operator =>] { [keyword return] [atom true]; };");
+
+ MT("async_jquery",
+ "[variable $].[property ajax]({",
+ " [property url]: [variable url],",
+ " [property async]: [atom true],",
+ " [property method]: [string 'GET']",
+ "});");
+
+ MT("async_variable",
+ "[keyword const] [def async] [operator =] {[property a]: [number 1]};",
+ "[keyword const] [def foo] [operator =] [string-2 `bar ${][variable async].[property a][string-2 }`];")
+
+ MT("bigint", "[number 1n] [operator +] [number 0x1afn] [operator +] [number 0o064n] [operator +] [number 0b100n];")
+
+ MT("async_comment",
+ "[keyword async] [comment /**/] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }");
+
+ MT("indent_switch",
+ "[keyword switch] ([variable x]) {",
+ " [keyword default]:",
+ " [keyword return] [number 2]",
+ "}")
+
+ MT("regexp_corner_case",
+ "[operator +]{} [operator /] [atom undefined];",
+ "[[[meta ...][string-2 /\\//] ]];",
+ "[keyword void] [string-2 /\\//];",
+ "[keyword do] [string-2 /\\//]; [keyword while] ([number 0]);",
+ "[keyword if] ([number 0]) {} [keyword else] [string-2 /\\//];",
+ "[string-2 `${][variable async][operator ++][string-2 }//`];",
+ "[string-2 `${]{} [operator /] [string-2 /\\//}`];")
+
+ MT("return_eol",
+ "[keyword return]",
+ "{} [string-2 /5/]")
+
+ MT("numeric separator",
+ "[number 123_456];",
+ "[number 0xdead_c0de];",
+ "[number 0o123_456];",
+ "[number 0b1101_1101];",
+ "[number .123_456e0_1];",
+ "[number 1E+123_456];",
+ "[number 12_34_56n];")
+
+ MT("underscore property",
+ "[variable something].[property _property];",
+ "[variable something].[property _123];",
+ "[variable something].[property _for];",
+ "[variable _for];",
+ "[variable _123];")
+
+ var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript")
+ function TS(name) {
+ test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1))
+ }
+
+ TS("typescript_extend_type",
+ "[keyword class] [def Foo] [keyword extends] [type Some][operator <][type Type][operator >] {}")
+
+ TS("typescript_arrow_type",
+ "[keyword let] [def x]: ([variable arg]: [type Type]) [operator =>] [type ReturnType]")
+
+ TS("typescript_class",
+ "[keyword class] [def Foo] {",
+ " [keyword public] [keyword static] [property main]() {}",
+ " [keyword private] [property _foo]: [type string];",
+ "}")
+
+ TS("typescript_literal_types",
+ "[keyword import] [keyword *] [keyword as] [def Sequelize] [keyword from] [string 'sequelize'];",
+ "[keyword interface] [def MyAttributes] {",
+ " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];",
+ " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];",
+ "}",
+ "[keyword interface] [def MyInstance] [keyword extends] [type Sequelize].[type Instance] [operator <] [type MyAttributes] [operator >] {",
+ " [property rawAttributes]: [type MyAttributes];",
+ " [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];",
+ " [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];",
+ "}")
+
+ TS("typescript_extend_operators",
+ "[keyword export] [keyword interface] [def UserModel] [keyword extends]",
+ " [type Sequelize].[type Model] [operator <] [type UserInstance], [type UserAttributes] [operator >] {",
+ " [property findById]: (",
+ " [variable userId]: [type number]",
+ " ) [operator =>] [type Promise] [operator <] [type Array] [operator <] { [property id], [property name] } [operator >>];",
+ " [property updateById]: (",
+ " [variable userId]: [type number],",
+ " [variable isActive]: [type boolean]",
+ " ) [operator =>] [type Promise] [operator <] [type AccountHolderNotificationPreferenceInstance] [operator >];",
+ " }")
+
+ TS("typescript_interface_with_const",
+ "[keyword const] [def hello]: {",
+ " [property prop1][operator ?]: [type string];",
+ " [property prop2][operator ?]: [type string];",
+ "} [operator =] {};")
+
+ TS("typescript_double_extend",
+ "[keyword export] [keyword interface] [def UserAttributes] {",
+ " [property id][operator ?]: [type number];",
+ " [property createdAt][operator ?]: [type Date];",
+ "}",
+ "[keyword export] [keyword interface] [def UserInstance] [keyword extends] [type Sequelize].[type Instance][operator <][type UserAttributes][operator >], [type UserAttributes] {",
+ " [property id]: [type number];",
+ " [property createdAt]: [type Date];",
+ "}");
+
+ TS("typescript_index_signature",
+ "[keyword interface] [def A] {",
+ " [[ [variable prop]: [type string] ]]: [type any];",
+ " [property prop1]: [type any];",
+ "}");
+
+ TS("typescript_generic_class",
+ "[keyword class] [def Foo][operator <][type T][operator >] {",
+ " [property bar]() {}",
+ " [property foo](): [type Foo] {}",
+ "}")
+
+ TS("typescript_type_when_keyword",
+ "[keyword export] [keyword type] [type AB] [operator =] [type A] [operator |] [type B];",
+ "[keyword type] [type Flags] [operator =] {",
+ " [property p1]: [type string];",
+ " [property p2]: [type boolean];",
+ "};")
+
+ TS("typescript_type_when_not_keyword",
+ "[keyword class] [def HasType] {",
+ " [property type]: [type string];",
+ " [property constructor]([def type]: [type string]) {",
+ " [keyword this].[property type] [operator =] [variable-2 type];",
+ " }",
+ " [property setType]({ [def type] }: { [property type]: [type string]; }) {",
+ " [keyword this].[property type] [operator =] [variable-2 type];",
+ " }",
+ "}")
+
+ TS("typescript_function_generics",
+ "[keyword function] [def a]() {}",
+ "[keyword function] [def b][operator <][type IA] [keyword extends] [type object], [type IB] [keyword extends] [type object][operator >]() {}",
+ "[keyword function] [def c]() {}")
+
+ TS("typescript_complex_return_type",
+ "[keyword function] [def A]() {",
+ " [keyword return] [keyword this].[property property];",
+ "}",
+ "[keyword function] [def B](): [type Promise][operator <]{ [[ [variable key]: [type string] ]]: [type any] } [operator |] [atom null][operator >] {",
+ " [keyword return] [keyword this].[property property];",
+ "}")
+
+ TS("typescript_complex_type_casting",
+ "[keyword const] [def giftpay] [operator =] [variable config].[property get]([string 'giftpay']) [keyword as] { [[ [variable platformUuid]: [type string] ]]: { [property version]: [type number]; [property apiCode]: [type string]; } };")
+
+ TS("typescript_keyof",
+ "[keyword function] [def x][operator <][type T] [keyword extends] [keyword keyof] [type X][operator >]([def a]: [type T]) {",
+ " [keyword return]")
+
+ TS("typescript_new_typeargs",
+ "[keyword let] [def x] [operator =] [keyword new] [variable Map][operator <][type string], [type Date][operator >]([string-2 `foo${][variable bar][string-2 }`])")
+
+ TS("modifiers",
+ "[keyword class] [def Foo] {",
+ " [keyword public] [keyword abstract] [property bar]() {}",
+ " [property constructor]([keyword readonly] [keyword private] [def x]) {}",
+ "}")
+
+ TS("arrow prop",
+ "({[property a]: [def p] [operator =>] [variable-2 p]})")
+
+ TS("generic in function call",
+ "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);",
+ "[keyword this].[property a][operator <][variable Type][operator >][variable foo];")
+
+ TS("type guard",
+ "[keyword class] [def Appler] {",
+ " [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {",
+ " [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))",
+ " [keyword throw] [keyword new] [variable Error]();",
+ " }",
+ "}")
+
+ TS("type as variable",
+ "[variable type] [operator =] [variable x] [keyword as] [type Bar];");
+
+ TS("enum body",
+ "[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {",
+ " [def ERROR] [operator =] [string 'problem_type_error'],",
+ " [def WARNING] [operator =] [string 'problem_type_warning'],",
+ " [def META],",
+ "}")
+
+ TS("parenthesized type",
+ "[keyword class] [def Foo] {",
+ " [property x] [operator =] [keyword new] [variable A][operator <][type B], [type string][operator |](() [operator =>] [type void])[operator >]();",
+ " [keyword private] [property bar]();",
+ "}")
+
+ TS("abstract class",
+ "[keyword export] [keyword abstract] [keyword class] [def Foo] {}")
+
+ TS("interface without semicolons",
+ "[keyword interface] [def Foo] {",
+ " [property greet]([def x]: [type int]): [type blah]",
+ " [property bar]: [type void]",
+ "}")
+
+ var jsonld_mode = CodeMirror.getMode(
+ {indentUnit: 2},
+ {name: "javascript", jsonld: true}
+ );
+ function LD(name) {
+ test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
+ }
+
+ LD("json_ld_keywords",
+ '{',
+ ' [meta "@context"]: {',
+ ' [meta "@base"]: [string "http://example.com"],',
+ ' [meta "@vocab"]: [string "http://xmlns.com/foaf/0.1/"],',
+ ' [property "likesFlavor"]: {',
+ ' [meta "@container"]: [meta "@list"]',
+ ' [meta "@reverse"]: [string "@beFavoriteOf"]',
+ ' },',
+ ' [property "nick"]: { [meta "@container"]: [meta "@set"] },',
+ ' [property "nick"]: { [meta "@container"]: [meta "@index"] }',
+ ' },',
+ ' [meta "@graph"]: [[ {',
+ ' [meta "@id"]: [string "http://dbpedia.org/resource/John_Lennon"],',
+ ' [property "name"]: [string "John Lennon"],',
+ ' [property "modified"]: {',
+ ' [meta "@value"]: [string "2010-05-29T14:17:39+02:00"],',
+ ' [meta "@type"]: [string "http://www.w3.org/2001/XMLSchema#dateTime"]',
+ ' }',
+ ' } ]]',
+ '}');
+
+ LD("json_ld_fake",
+ '{',
+ ' [property "@fake"]: [string "@fake"],',
+ ' [property "@contextual"]: [string "@identifier"],',
+ ' [property "user@domain.com"]: [string "@graphical"],',
+ ' [property "@ID"]: [string "@@ID"]',
+ '}');
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/mode_test.css b/devtools/client/shared/sourceeditor/test/codemirror/mode_test.css
new file mode 100644
index 0000000000..f83271b4e2
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/mode_test.css
@@ -0,0 +1,23 @@
+.mt-output .mt-token {
+ border: 1px solid #ddd;
+ white-space: pre;
+ font-family: "Consolas", monospace;
+ text-align: center;
+}
+
+.mt-output .mt-style {
+ font-size: x-small;
+}
+
+.mt-output .mt-state {
+ font-size: x-small;
+ vertical-align: top;
+}
+
+.mt-output .mt-state-row {
+ display: none;
+}
+
+.mt-state-unhide .mt-output .mt-state-row {
+ display: table-row;
+}
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/mode_test.js b/devtools/client/shared/sourceeditor/test/codemirror/mode_test.js
new file mode 100644
index 0000000000..e7c0cf92ae
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/mode_test.js
@@ -0,0 +1,193 @@
+/**
+ * Helper to test CodeMirror highlighting modes. It pretty prints output of the
+ * highlighter and can check against expected styles.
+ *
+ * Mode tests are registered by calling test.mode(testName, mode,
+ * tokens), where mode is a mode object as returned by
+ * CodeMirror.getMode, and tokens is an array of lines that make up
+ * the test.
+ *
+ * These lines are strings, in which styled stretches of code are
+ * enclosed in brackets `[]`, and prefixed by their style. For
+ * example, `[keyword if]`. Brackets in the code itself must be
+ * duplicated to prevent them from being interpreted as token
+ * boundaries. For example `a[[i]]` for `a[i]`. If a token has
+ * multiple styles, the styles must be separated by ampersands, for
+ * example `[tag&error </hmtl>]`.
+ *
+ * See the test.js files in the css, markdown, gfm, and stex mode
+ * directories for examples.
+ */
+(function() {
+ function findSingle(str, pos, ch) {
+ for (;;) {
+ var found = str.indexOf(ch, pos);
+ if (found == -1) return null;
+ if (str.charAt(found + 1) != ch) return found;
+ pos = found + 2;
+ }
+ }
+
+ var styleName = /[\w&-_]+/g;
+ function parseTokens(strs) {
+ var tokens = [], plain = "";
+ for (var i = 0; i < strs.length; ++i) {
+ if (i) plain += "\n";
+ var str = strs[i], pos = 0;
+ while (pos < str.length) {
+ var style = null, text;
+ if (str.charAt(pos) == "[" && str.charAt(pos+1) != "[") {
+ styleName.lastIndex = pos + 1;
+ var m = styleName.exec(str);
+ style = m[0].replace(/&/g, " ");
+ var textStart = pos + style.length + 2;
+ var end = findSingle(str, textStart, "]");
+ if (end == null) throw new Error("Unterminated token at " + pos + " in '" + str + "'" + style);
+ text = str.slice(textStart, end);
+ pos = end + 1;
+ } else {
+ var end = findSingle(str, pos, "[");
+ if (end == null) end = str.length;
+ text = str.slice(pos, end);
+ pos = end;
+ }
+ text = text.replace(/\[\[|\]\]/g, function(s) {return s.charAt(0);});
+ tokens.push({style: style, text: text});
+ plain += text;
+ }
+ }
+ return {tokens: tokens, plain: plain};
+ }
+
+ test.mode = function(name, mode, tokens, modeName) {
+ var data = parseTokens(tokens);
+ return test((modeName || mode.name) + "_" + name, function() {
+ return compare(data.plain, data.tokens, mode);
+ });
+ };
+
+ function esc(str) {
+ return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
+ }
+
+ function compare(text, expected, mode) {
+
+ var expectedOutput = [];
+ for (var i = 0; i < expected.length; ++i) {
+ var sty = expected[i].style;
+ if (sty && sty.indexOf(" ")) sty = sty.split(' ').sort().join(' ');
+ expectedOutput.push({style: sty, text: expected[i].text});
+ }
+
+ var observedOutput = highlight(text, mode);
+
+ var s = "";
+ var diff = highlightOutputsDifferent(expectedOutput, observedOutput);
+ if (diff != null) {
+ s += '<div class="mt-test mt-fail">';
+ s += '<pre>' + esc(text) + '</pre>';
+ s += '<div class="cm-s-default">';
+ s += 'expected:';
+ s += prettyPrintOutputTable(expectedOutput, diff);
+ s += 'observed: [<a onclick="this.parentElement.className+=\' mt-state-unhide\'">display states</a>]';
+ s += prettyPrintOutputTable(observedOutput, diff);
+ s += '</div>';
+ s += '</div>';
+ }
+ if (observedOutput.indentFailures) {
+ for (var i = 0; i < observedOutput.indentFailures.length; i++)
+ s += "<div class='mt-test mt-fail'>" + esc(observedOutput.indentFailures[i]) + "</div>";
+ }
+ if (s) throw new Failure(s);
+ }
+
+ function stringify(obj) {
+ function replacer(key, obj) {
+ if (typeof obj == "function") {
+ var m = obj.toString().match(/function\s*[^\s(]*/);
+ return m ? m[0] : "function";
+ }
+ return obj;
+ }
+ if (window.JSON && JSON.stringify)
+ return JSON.stringify(obj, replacer, 2);
+ return "[unsupported]"; // Fail safely if no native JSON.
+ }
+
+ function highlight(string, mode) {
+ var state = mode.startState();
+
+ var lines = string.replace(/\r\n/g,'\n').split('\n');
+ var st = [], pos = 0;
+ for (var i = 0; i < lines.length; ++i) {
+ var line = lines[i], newLine = true;
+ if (mode.indent) {
+ var ws = line.match(/^\s*/)[0];
+ var indent = mode.indent(state, line.slice(ws.length), line);
+ if (indent != CodeMirror.Pass && indent != ws.length)
+ (st.indentFailures || (st.indentFailures = [])).push(
+ "Indentation of line " + (i + 1) + " is " + indent + " (expected " + ws.length + ")");
+ }
+ var stream = new CodeMirror.StringStream(line, 4, {
+ lookAhead: function(n) { return lines[i + n] }
+ });
+ if (line == "" && mode.blankLine) mode.blankLine(state);
+ /* Start copied code from CodeMirror.highlight */
+ while (!stream.eol()) {
+ for (var j = 0; j < 10 && stream.start >= stream.pos; j++)
+ var compare = mode.token(stream, state);
+ if (j == 10)
+ throw new Failure("Failed to advance the stream." + stream.string + " " + stream.pos);
+ var substr = stream.current();
+ if (compare && compare.indexOf(" ") > -1) compare = compare.split(' ').sort().join(' ');
+ stream.start = stream.pos;
+ if (pos && st[pos-1].style == compare && !newLine) {
+ st[pos-1].text += substr;
+ } else if (substr) {
+ st[pos++] = {style: compare, text: substr, state: stringify(state)};
+ }
+ // Give up when line is ridiculously long
+ if (stream.pos > 5000) {
+ st[pos++] = {style: null, text: this.text.slice(stream.pos)};
+ break;
+ }
+ newLine = false;
+ }
+ }
+
+ return st;
+ }
+
+ function highlightOutputsDifferent(o1, o2) {
+ var minLen = Math.min(o1.length, o2.length);
+ for (var i = 0; i < minLen; ++i)
+ if (o1[i].style != o2[i].style || o1[i].text != o2[i].text) return i;
+ if (o1.length > minLen || o2.length > minLen) return minLen;
+ }
+
+ function prettyPrintOutputTable(output, diffAt) {
+ var s = '<table class="mt-output">';
+ s += '<tr>';
+ for (var i = 0; i < output.length; ++i) {
+ var style = output[i].style, val = output[i].text;
+ s +=
+ '<td class="mt-token"' + (i == diffAt ? " style='background: pink'" : "") + '>' +
+ '<span class="cm-' + esc(String(style)) + '">' +
+ esc(val.replace(/ /g,'\xb7')) + // · MIDDLE DOT
+ '</span>' +
+ '</td>';
+ }
+ s += '</tr><tr>';
+ for (var i = 0; i < output.length; ++i) {
+ s += '<td class="mt-style"><span>' + (output[i].style || null) + '</span></td>';
+ }
+ if(output[0].state) {
+ s += '</tr><tr class="mt-state-row" title="State AFTER each token">';
+ for (var i = 0; i < output.length; ++i) {
+ s += '<td class="mt-state"><pre>' + esc(output[i].state) + '</pre></td>';
+ }
+ }
+ s += '</tr></table>';
+ return s;
+ }
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/multi_test.js b/devtools/client/shared/sourceeditor/test/codemirror/multi_test.js
new file mode 100644
index 0000000000..cc042f7399
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/multi_test.js
@@ -0,0 +1,295 @@
+(function() {
+ namespace = "multi_";
+
+ function hasSelections(cm) {
+ var sels = cm.listSelections();
+ var given = (arguments.length - 1) / 4;
+ if (sels.length != given)
+ throw new Failure("expected " + given + " selections, found " + sels.length);
+ for (var i = 0, p = 1; i < given; i++, p += 4) {
+ var anchor = Pos(arguments[p], arguments[p + 1]);
+ var head = Pos(arguments[p + 2], arguments[p + 3]);
+ eqCharPos(sels[i].anchor, anchor, "anchor of selection " + i);
+ eqCharPos(sels[i].head, head, "head of selection " + i);
+ }
+ }
+ function hasCursors(cm) {
+ var sels = cm.listSelections();
+ var given = (arguments.length - 1) / 2;
+ if (sels.length != given)
+ throw new Failure("expected " + given + " selections, found " + sels.length);
+ for (var i = 0, p = 1; i < given; i++, p += 2) {
+ eqCursorPos(sels[i].anchor, sels[i].head, "something selected for " + i);
+ var head = Pos(arguments[p], arguments[p + 1]);
+ eqCharPos(sels[i].head, head, "selection " + i);
+ }
+ }
+
+ testCM("getSelection", function(cm) {
+ select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)}, {anchor: Pos(2, 2), head: Pos(2, 0)});
+ eq(cm.getSelection(), "1234\n56\n90");
+ eq(cm.getSelection(false).join("|"), "1234|56|90");
+ eq(cm.getSelections().join("|"), "1234\n56|90");
+ }, {value: "1234\n5678\n90"});
+
+ testCM("setSelection", function(cm) {
+ select(cm, Pos(3, 0), Pos(0, 0), {anchor: Pos(2, 5), head: Pos(1, 0)});
+ hasSelections(cm, 0, 0, 0, 0,
+ 2, 5, 1, 0,
+ 3, 0, 3, 0);
+ cm.setSelection(Pos(1, 2), Pos(1, 1));
+ hasSelections(cm, 1, 2, 1, 1);
+ select(cm, {anchor: Pos(1, 1), head: Pos(2, 4)},
+ {anchor: Pos(0, 0), head: Pos(1, 3)},
+ Pos(3, 0), Pos(2, 2));
+ hasSelections(cm, 0, 0, 2, 4,
+ 3, 0, 3, 0);
+ cm.setSelections([{anchor: Pos(0, 1), head: Pos(0, 2)},
+ {anchor: Pos(1, 1), head: Pos(1, 2)},
+ {anchor: Pos(2, 1), head: Pos(2, 2)}], 1);
+ eqCharPos(cm.getCursor("head"), Pos(1, 2));
+ eqCharPos(cm.getCursor("anchor"), Pos(1, 1));
+ eqCharPos(cm.getCursor("from"), Pos(1, 1));
+ eqCharPos(cm.getCursor("to"), Pos(1, 2));
+ cm.setCursor(Pos(1, 1));
+ hasCursors(cm, 1, 1);
+ }, {value: "abcde\nabcde\nabcde\n"});
+
+ testCM("somethingSelected", function(cm) {
+ select(cm, Pos(0, 1), {anchor: Pos(0, 3), head: Pos(0, 5)});
+ eq(cm.somethingSelected(), true);
+ select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5));
+ eq(cm.somethingSelected(), false);
+ }, {value: "123456789"});
+
+ testCM("extendSelection", function(cm) {
+ select(cm, Pos(0, 1), Pos(1, 1), Pos(2, 1));
+ cm.setExtending(true);
+ cm.extendSelections([Pos(0, 2), Pos(1, 0), Pos(2, 3)]);
+ hasSelections(cm, 0, 1, 0, 2,
+ 1, 1, 1, 0,
+ 2, 1, 2, 3);
+ cm.extendSelection(Pos(2, 4), Pos(2, 0));
+ hasSelections(cm, 2, 4, 2, 0);
+ }, {value: "1234\n1234\n1234"});
+
+ testCM("addSelection", function(cm) {
+ select(cm, Pos(0, 1), Pos(1, 1));
+ cm.addSelection(Pos(0, 0), Pos(0, 4));
+ hasSelections(cm, 0, 0, 0, 4,
+ 1, 1, 1, 1);
+ cm.addSelection(Pos(2, 2));
+ hasSelections(cm, 0, 0, 0, 4,
+ 1, 1, 1, 1,
+ 2, 2, 2, 2);
+ }, {value: "1234\n1234\n1234"});
+
+ testCM("replaceSelection", function(cm) {
+ var selections = [{anchor: Pos(0, 0), head: Pos(0, 1)},
+ {anchor: Pos(0, 2), head: Pos(0, 3)},
+ {anchor: Pos(0, 4), head: Pos(0, 5)},
+ {anchor: Pos(2, 1), head: Pos(2, 4)},
+ {anchor: Pos(2, 5), head: Pos(2, 6)}];
+ var val = "123456\n123456\n123456";
+ cm.setValue(val);
+ cm.setSelections(selections);
+ cm.replaceSelection("ab", "around");
+ eq(cm.getValue(), "ab2ab4ab6\n123456\n1ab5ab");
+ hasSelections(cm, 0, 0, 0, 2,
+ 0, 3, 0, 5,
+ 0, 6, 0, 8,
+ 2, 1, 2, 3,
+ 2, 4, 2, 6);
+ cm.setValue(val);
+ cm.setSelections(selections);
+ cm.replaceSelection("", "around");
+ eq(cm.getValue(), "246\n123456\n15");
+ hasSelections(cm, 0, 0, 0, 0,
+ 0, 1, 0, 1,
+ 0, 2, 0, 2,
+ 2, 1, 2, 1,
+ 2, 2, 2, 2);
+ cm.setValue(val);
+ cm.setSelections(selections);
+ cm.replaceSelection("X\nY\nZ", "around");
+ hasSelections(cm, 0, 0, 2, 1,
+ 2, 2, 4, 1,
+ 4, 2, 6, 1,
+ 8, 1, 10, 1,
+ 10, 2, 12, 1);
+ cm.replaceSelection("a", "around");
+ hasSelections(cm, 0, 0, 0, 1,
+ 0, 2, 0, 3,
+ 0, 4, 0, 5,
+ 2, 1, 2, 2,
+ 2, 3, 2, 4);
+ cm.replaceSelection("xy", "start");
+ hasSelections(cm, 0, 0, 0, 0,
+ 0, 3, 0, 3,
+ 0, 6, 0, 6,
+ 2, 1, 2, 1,
+ 2, 4, 2, 4);
+ cm.replaceSelection("z\nf");
+ hasSelections(cm, 1, 1, 1, 1,
+ 2, 1, 2, 1,
+ 3, 1, 3, 1,
+ 6, 1, 6, 1,
+ 7, 1, 7, 1);
+ eq(cm.getValue(), "z\nfxy2z\nfxy4z\nfxy6\n123456\n1z\nfxy5z\nfxy");
+ });
+
+ function select(cm) {
+ var sels = [];
+ for (var i = 1; i < arguments.length; i++) {
+ var arg = arguments[i];
+ if (arg.head) sels.push(arg);
+ else sels.push({head: arg, anchor: arg});
+ }
+ cm.setSelections(sels, sels.length - 1);
+ }
+
+ testCM("indentSelection", function(cm) {
+ select(cm, Pos(0, 1), Pos(1, 1));
+ cm.indentSelection(4);
+ eq(cm.getValue(), " foo\n bar\nbaz");
+
+ select(cm, Pos(0, 2), Pos(0, 3), Pos(0, 4));
+ cm.indentSelection(-2);
+ eq(cm.getValue(), " foo\n bar\nbaz");
+
+ select(cm, {anchor: Pos(0, 0), head: Pos(1, 2)},
+ {anchor: Pos(1, 3), head: Pos(2, 0)});
+ cm.indentSelection(-2);
+ eq(cm.getValue(), "foo\n bar\nbaz");
+ }, {value: "foo\nbar\nbaz"});
+
+ testCM("killLine", function(cm) {
+ select(cm, Pos(0, 1), Pos(0, 2), Pos(1, 1));
+ cm.execCommand("killLine");
+ eq(cm.getValue(), "f\nb\nbaz");
+ cm.execCommand("killLine");
+ eq(cm.getValue(), "fbbaz");
+ cm.setValue("foo\nbar\nbaz");
+ select(cm, Pos(0, 1), {anchor: Pos(0, 2), head: Pos(2, 1)});
+ cm.execCommand("killLine");
+ eq(cm.getValue(), "faz");
+ }, {value: "foo\nbar\nbaz"});
+
+ testCM("deleteLine", function(cm) {
+ select(cm, Pos(0, 0),
+ {head: Pos(0, 1), anchor: Pos(2, 0)},
+ Pos(4, 0));
+ cm.execCommand("deleteLine");
+ eq(cm.getValue(), "4\n6\n7");
+ select(cm, Pos(2, 1));
+ cm.execCommand("deleteLine");
+ eq(cm.getValue(), "4\n6\n");
+ }, {value: "1\n2\n3\n4\n5\n6\n7"});
+
+ testCM("deleteH", function(cm) {
+ select(cm, Pos(0, 4), {anchor: Pos(1, 4), head: Pos(1, 5)});
+ cm.execCommand("delWordAfter");
+ eq(cm.getValue(), "foo bar baz\nabc ef ghi\n");
+ cm.execCommand("delWordAfter");
+ eq(cm.getValue(), "foo baz\nabc ghi\n");
+ cm.execCommand("delCharBefore");
+ cm.execCommand("delCharBefore");
+ eq(cm.getValue(), "fo baz\nab ghi\n");
+ select(cm, Pos(0, 3), Pos(0, 4), Pos(0, 5));
+ cm.execCommand("delWordAfter");
+ eq(cm.getValue(), "fo \nab ghi\n");
+ }, {value: "foo bar baz\nabc def ghi\n"});
+
+ testCM("goLineStart", function(cm) {
+ select(cm, Pos(0, 2), Pos(0, 3), Pos(1, 1));
+ cm.execCommand("goLineStart");
+ hasCursors(cm, 0, 0, 1, 0);
+ select(cm, Pos(1, 1), Pos(0, 1));
+ cm.setExtending(true);
+ cm.execCommand("goLineStart");
+ hasSelections(cm, 0, 1, 0, 0,
+ 1, 1, 1, 0);
+ }, {value: "foo\nbar\nbaz"});
+
+ testCM("moveV", function(cm) {
+ select(cm, Pos(0, 2), Pos(1, 2));
+ cm.execCommand("goLineDown");
+ hasCursors(cm, 1, 2, 2, 2);
+ cm.execCommand("goLineUp");
+ hasCursors(cm, 0, 2, 1, 2);
+ cm.execCommand("goLineUp");
+ hasCursors(cm, 0, 0, 0, 2);
+ cm.execCommand("goLineUp");
+ hasCursors(cm, 0, 0);
+ select(cm, Pos(0, 2), Pos(1, 2));
+ cm.setExtending(true);
+ cm.execCommand("goLineDown");
+ hasSelections(cm, 0, 2, 2, 2);
+ }, {value: "12345\n12345\n12345"});
+
+ testCM("moveH", function(cm) {
+ select(cm, Pos(0, 1), Pos(0, 3), Pos(0, 5), Pos(2, 3));
+ cm.execCommand("goCharRight");
+ hasCursors(cm, 0, 2, 0, 4, 1, 0, 2, 4);
+ cm.execCommand("goCharLeft");
+ hasCursors(cm, 0, 1, 0, 3, 0, 5, 2, 3);
+ for (var i = 0; i < 15; i++)
+ cm.execCommand("goCharRight");
+ hasCursors(cm, 2, 4, 2, 5);
+ }, {value: "12345\n12345\n12345"});
+
+ testCM("newlineAndIndent", function(cm) {
+ select(cm, Pos(0, 5), Pos(1, 5));
+ cm.execCommand("newlineAndIndent");
+ hasCursors(cm, 1, 2, 3, 2);
+ eq(cm.getValue(), "x = [\n 1];\ny = [\n 2];");
+ cm.undo();
+ eq(cm.getValue(), "x = [1];\ny = [2];");
+ hasCursors(cm, 0, 5, 1, 5);
+ select(cm, Pos(0, 5), Pos(0, 6));
+ cm.execCommand("newlineAndIndent");
+ hasCursors(cm, 1, 2, 2, 0);
+ eq(cm.getValue(), "x = [\n 1\n];\ny = [2];");
+ }, {value: "x = [1];\ny = [2];", mode: "javascript"});
+
+ testCM("goDocStartEnd", function(cm) {
+ select(cm, Pos(0, 1), Pos(1, 1));
+ cm.execCommand("goDocStart");
+ hasCursors(cm, 0, 0);
+ select(cm, Pos(0, 1), Pos(1, 1));
+ cm.execCommand("goDocEnd");
+ hasCursors(cm, 1, 3);
+ select(cm, Pos(0, 1), Pos(1, 1));
+ cm.setExtending(true);
+ cm.execCommand("goDocEnd");
+ hasSelections(cm, 1, 1, 1, 3);
+ }, {value: "abc\ndef"});
+
+ testCM("selectionHistory", function(cm) {
+ for (var i = 0; i < 3; ++i)
+ cm.addSelection(Pos(0, i * 2), Pos(0, i * 2 + 1));
+ cm.execCommand("undoSelection");
+ eq(cm.getSelection(), "1\n2");
+ cm.execCommand("undoSelection");
+ eq(cm.getSelection(), "1");
+ cm.execCommand("undoSelection");
+ eq(cm.getSelection(), "");
+ eqCharPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("redoSelection");
+ eq(cm.getSelection(), "1");
+ cm.execCommand("redoSelection");
+ eq(cm.getSelection(), "1\n2");
+ cm.execCommand("redoSelection");
+ eq(cm.getSelection(), "1\n2\n3");
+ }, {value: "1 2 3"});
+
+ testCM("selectionsMayTouch", function(cm) {
+ select(cm, Pos(0, 0), Pos(0, 2))
+ cm.setExtending(true);
+ cm.extendSelections([Pos(0, 2), Pos(0, 4)])
+ hasSelections(cm, 0, 0, 0, 2,
+ 0, 2, 0, 4)
+ cm.extendSelections([Pos(0, 3), Pos(0, 4)])
+ hasSelections(cm, 0, 0, 0, 4)
+ }, {selectionsMayTouch: true, value: "1234"})
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/search_test.js b/devtools/client/shared/sourceeditor/test/codemirror/search_test.js
new file mode 100644
index 0000000000..e3188de529
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/search_test.js
@@ -0,0 +1,85 @@
+(function() {
+ "use strict";
+
+ function run(doc, query, options) {
+ var cursor = doc.getSearchCursor(query, null, options);
+ for (var i = 3; i < arguments.length; i += 4) {
+ var found = cursor.findNext();
+ is(found, "not enough results (forward)");
+ eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, forward, " + (i - 3) / 4);
+ eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, forward, " + (i - 3) / 4);
+ }
+ is(!cursor.findNext(), "too many matches (forward)");
+ for (var i = arguments.length - 4; i >= 3; i -= 4) {
+ var found = cursor.findPrevious();
+ is(found, "not enough results (backwards)");
+ eqCharPos(Pos(arguments[i], arguments[i + 1]), cursor.from(), "from, backwards, " + (i - 3) / 4);
+ eqCharPos(Pos(arguments[i + 2], arguments[i + 3]), cursor.to(), "to, backwards, " + (i - 3) / 4);
+ }
+ is(!cursor.findPrevious(), "too many matches (backwards)");
+ }
+
+ function test(name, f) { window.test("search_" + name, f) }
+
+ test("simple", function() {
+ var doc = new CodeMirror.Doc("abcdefg\nabcdefg")
+ run(doc, "cde", false, 0, 2, 0, 5, 1, 2, 1, 5);
+ });
+
+ test("multiline", function() {
+ var doc = new CodeMirror.Doc("hallo\na\nb\ngoodbye")
+ run(doc, "llo\na\nb\ngoo", false, 0, 2, 3, 3);
+ run(doc, "blah\na\nb\nhall", false);
+ run(doc, "bye\nx\neye", false);
+ });
+
+ test("regexp", function() {
+ var doc = new CodeMirror.Doc("abcde\nabcde")
+ run(doc, /bcd/, false, 0, 1, 0, 4, 1, 1, 1, 4);
+ run(doc, /BCD/, false);
+ run(doc, /BCD/i, false, 0, 1, 0, 4, 1, 1, 1, 4);
+ });
+
+ test("regexpMultiline", function() {
+ var doc = new CodeMirror.Doc("foo foo\nbar\nbaz")
+ run(doc, /fo[^]*az/, {multiline: true}, 0, 0, 2, 3)
+ run(doc, /[oa][^u]/, {multiline: true}, 0, 1, 0, 3, 0, 5, 0, 7, 1, 1, 1, 3, 2, 1, 2, 3)
+ run(doc, /[a][^u]{2}/, {multiline: true}, 1, 1, 2, 0)
+ })
+
+ test("insensitive", function() {
+ var doc = new CodeMirror.Doc("hallo\nHALLO\noink\nhAllO")
+ run(doc, "All", false, 3, 1, 3, 4);
+ run(doc, "All", true, 0, 1, 0, 4, 1, 1, 1, 4, 3, 1, 3, 4);
+ });
+
+ test("multilineInsensitive", function() {
+ var doc = new CodeMirror.Doc("zie ginds komT\nDe Stoomboot\nuit Spanje weer aan")
+ run(doc, "komt\nde stoomboot\nuit", false);
+ run(doc, "komt\nde stoomboot\nuit", {caseFold: true}, 0, 10, 2, 3);
+ run(doc, "kOMt\ndE stOOmboot\nuiT", {caseFold: true}, 0, 10, 2, 3);
+ });
+
+ test("multilineInsensitiveSlow", function() {
+ var text = ""
+ for (var i = 0; i < 1000; i++) text += "foo\nbar\n"
+ var doc = new CodeMirror.Doc("find\nme\n" + text + "find\nme\n")
+ var t0 = +new Date
+ run(doc, /find\nme/, {multiline: true}, 0, 0, 1, 2, 2002, 0, 2003, 2)
+ is(+new Date - t0 < 100)
+ })
+
+ test("expandingCaseFold", function() {
+ var doc = new CodeMirror.Doc("<b>İİ İİ</b>\n<b>uu uu</b>")
+ run(doc, "</b>", true, 0, 8, 0, 12, 1, 8, 1, 12);
+ run(doc, "İİ", true, 0, 3, 0, 5, 0, 6, 0, 8);
+ });
+
+ test("normalize", function() {
+ if (!String.prototype.normalize) return
+ var doc = new CodeMirror.Doc("yılbaşı\n수 있을까\nLe taux d'humidité à London")
+ run(doc, "s", false, 0, 5, 0, 6)
+ run(doc, "이", false, 1, 2, 1, 3)
+ run(doc, "a", false, 0, 4, 0, 5, 2, 4, 2, 5, 2, 19, 2, 20)
+ })
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/sublime_test.js b/devtools/client/shared/sourceeditor/test/codemirror/sublime_test.js
new file mode 100644
index 0000000000..09bb951247
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/sublime_test.js
@@ -0,0 +1,284 @@
+(function() {
+ "use strict";
+
+ var Pos = CodeMirror.Pos;
+ namespace = "sublime_";
+
+ function stTest(name) {
+ var actions = Array.prototype.slice.call(arguments, 1);
+ testCM(name, function(cm) {
+ for (var i = 0; i < actions.length; i++) {
+ var action = actions[i];
+ if (typeof action == "string" && i == 0)
+ cm.setValue(action);
+ else if (typeof action == "string")
+ cm.execCommand(action);
+ else if (action instanceof Pos)
+ cm.setCursor(action);
+ else
+ action(cm);
+ }
+ });
+ }
+
+ function at(line, ch, msg) {
+ return function(cm) {
+ eq(cm.listSelections().length, 1);
+ eqCursorPos(cm.getCursor("head"), Pos(line, ch), msg);
+ eqCursorPos(cm.getCursor("anchor"), Pos(line, ch), msg);
+ };
+ }
+
+ function val(content, msg) {
+ return function(cm) { eq(cm.getValue(), content, msg); };
+ }
+
+ function argsToRanges(args) {
+ if (args.length % 4) throw new Error("Wrong number of arguments for ranges.");
+ var ranges = [];
+ for (var i = 0; i < args.length; i += 4)
+ ranges.push({anchor: Pos(args[i], args[i + 1]),
+ head: Pos(args[i + 2], args[i + 3])});
+ return ranges;
+ }
+
+ function setSel() {
+ var ranges = argsToRanges(arguments);
+ return function(cm) { cm.setSelections(ranges, 0); };
+ }
+
+ function hasSel() {
+ var ranges = argsToRanges(arguments);
+ return function(cm) {
+ var sels = cm.listSelections();
+ if (sels.length != ranges.length)
+ throw new Failure("Expected " + ranges.length + " selections, but found " + sels.length);
+ for (var i = 0; i < sels.length; i++) {
+ eqCharPos(sels[i].anchor, ranges[i].anchor, "anchor " + i);
+ eqCharPos(sels[i].head, ranges[i].head, "head " + i);
+ }
+ };
+ }
+
+ stTest("bySubword", "the foo_bar DooDahBah \n a",
+ "goSubwordLeft", at(0, 0),
+ "goSubwordRight", at(0, 3),
+ "goSubwordRight", at(0, 7),
+ "goSubwordRight", at(0, 11),
+ "goSubwordRight", at(0, 15),
+ "goSubwordRight", at(0, 18),
+ "goSubwordRight", at(0, 21),
+ "goSubwordRight", at(0, 22),
+ "goSubwordRight", at(1, 0),
+ "goSubwordRight", at(1, 2),
+ "goSubwordRight", at(1, 2),
+ "goSubwordLeft", at(1, 1),
+ "goSubwordLeft", at(1, 0),
+ "goSubwordLeft", at(0, 22),
+ "goSubwordLeft", at(0, 18),
+ "goSubwordLeft", at(0, 15),
+ "goSubwordLeft", at(0, 12),
+ "goSubwordLeft", at(0, 8),
+ "goSubwordLeft", at(0, 4),
+ "goSubwordLeft", at(0, 0));
+
+ stTest("splitSelectionByLine", "abc\ndef\nghi",
+ setSel(0, 1, 2, 2),
+ "splitSelectionByLine",
+ hasSel(0, 1, 0, 3,
+ 1, 0, 1, 3,
+ 2, 0, 2, 2));
+
+ stTest("splitSelectionByLineMulti", "abc\ndef\nghi\njkl",
+ setSel(0, 1, 1, 1,
+ 1, 2, 3, 2,
+ 3, 3, 3, 3),
+ "splitSelectionByLine",
+ hasSel(0, 1, 0, 3,
+ 1, 0, 1, 1,
+ 1, 2, 1, 3,
+ 2, 0, 2, 3,
+ 3, 0, 3, 2,
+ 3, 3, 3, 3));
+
+ stTest("selectLine", "abc\ndef\nghi",
+ setSel(0, 1, 0, 1,
+ 2, 0, 2, 1),
+ "selectLine",
+ hasSel(0, 0, 1, 0,
+ 2, 0, 2, 3),
+ setSel(0, 1, 1, 0),
+ "selectLine",
+ hasSel(0, 0, 2, 0));
+
+ stTest("insertLineAfter", "abcde\nfghijkl\nmn",
+ setSel(0, 1, 0, 1,
+ 0, 3, 0, 3,
+ 1, 2, 1, 2,
+ 1, 3, 1, 5), "insertLineAfter",
+ hasSel(1, 0, 1, 0,
+ 3, 0, 3, 0), val("abcde\n\nfghijkl\n\nmn"));
+
+ stTest("insertLineBefore", "abcde\nfghijkl\nmn",
+ setSel(0, 1, 0, 1,
+ 0, 3, 0, 3,
+ 1, 2, 1, 2,
+ 1, 3, 1, 5), "insertLineBefore",
+ hasSel(0, 0, 0, 0,
+ 2, 0, 2, 0), val("\nabcde\n\nfghijkl\nmn"));
+
+ stTest("selectNextOccurrence", "a foo bar\nfoobar foo",
+ setSel(0, 2, 0, 5),
+ "selectNextOccurrence", hasSel(0, 2, 0, 5,
+ 1, 0, 1, 3),
+ "selectNextOccurrence", hasSel(0, 2, 0, 5,
+ 1, 0, 1, 3,
+ 1, 7, 1, 10),
+ "selectNextOccurrence", hasSel(0, 2, 0, 5,
+ 1, 0, 1, 3,
+ 1, 7, 1, 10),
+ Pos(0, 3), "selectNextOccurrence", hasSel(0, 2, 0, 5),
+ "selectNextOccurrence", hasSel(0, 2, 0, 5,
+ 1, 7, 1, 10),
+ setSel(0, 6, 0, 9),
+ "selectNextOccurrence", hasSel(0, 6, 0, 9,
+ 1, 3, 1, 6));
+
+ stTest("selectScope", "foo(a) {\n bar[1, 2];\n}",
+ "selectScope", hasSel(0, 0, 2, 1),
+ Pos(0, 4), "selectScope", hasSel(0, 4, 0, 5),
+ Pos(0, 5), "selectScope", hasSel(0, 4, 0, 5),
+ Pos(0, 6), "selectScope", hasSel(0, 0, 2, 1),
+ Pos(0, 8), "selectScope", hasSel(0, 8, 2, 0),
+ Pos(1, 2), "selectScope", hasSel(0, 8, 2, 0),
+ Pos(1, 6), "selectScope", hasSel(1, 6, 1, 10),
+ Pos(1, 9), "selectScope", hasSel(1, 6, 1, 10),
+ "selectScope", hasSel(0, 8, 2, 0),
+ "selectScope", hasSel(0, 0, 2, 1));
+
+ stTest("goToBracket", "foo(a) {\n bar[1, 2];\n}",
+ Pos(0, 0), "goToBracket", at(0, 0),
+ Pos(0, 4), "goToBracket", at(0, 5), "goToBracket", at(0, 4),
+ Pos(0, 8), "goToBracket", at(2, 0), "goToBracket", at(0, 8),
+ Pos(1, 2), "goToBracket", at(2, 0),
+ Pos(1, 7), "goToBracket", at(1, 10), "goToBracket", at(1, 6));
+
+ stTest("swapLine", "1\n2\n3---\n4\n5",
+ "swapLineDown", val("2\n1\n3---\n4\n5"),
+ "swapLineUp", val("1\n2\n3---\n4\n5"),
+ "swapLineUp", val("1\n2\n3---\n4\n5"),
+ Pos(4, 1), "swapLineDown", val("1\n2\n3---\n4\n5"),
+ setSel(0, 1, 0, 1,
+ 1, 0, 2, 0,
+ 2, 2, 2, 2),
+ "swapLineDown", val("4\n1\n2\n3---\n5"),
+ hasSel(1, 1, 1, 1,
+ 2, 0, 3, 0,
+ 3, 2, 3, 2),
+ "swapLineUp", val("1\n2\n3---\n4\n5"),
+ hasSel(0, 1, 0, 1,
+ 1, 0, 2, 0,
+ 2, 2, 2, 2));
+
+ stTest("swapLineEmptyBottomSel", "1\n2\n3",
+ setSel(0, 1, 1, 0),
+ "swapLineDown", val("2\n1\n3"), hasSel(1, 1, 2, 0),
+ "swapLineUp", val("1\n2\n3"), hasSel(0, 1, 1, 0),
+ "swapLineUp", val("1\n2\n3"), hasSel(0, 0, 0, 0));
+
+ stTest("swapLineUpFromEnd", "a\nb\nc",
+ Pos(2, 1), "swapLineUp",
+ hasSel(1, 1, 1, 1), val("a\nc\nb"));
+
+ stTest("joinLines", "abc\ndef\nghi\njkl",
+ "joinLines", val("abc def\nghi\njkl"), at(0, 4),
+ "undo",
+ setSel(0, 2, 1, 1), "joinLines",
+ val("abc def ghi\njkl"), hasSel(0, 2, 0, 8),
+ "undo",
+ setSel(0, 1, 0, 1,
+ 1, 1, 1, 1,
+ 3, 1, 3, 1), "joinLines",
+ val("abc def ghi\njkl"), hasSel(0, 4, 0, 4,
+ 0, 8, 0, 8,
+ 1, 3, 1, 3));
+
+ stTest("duplicateLine", "abc\ndef\nghi",
+ Pos(1, 0), "duplicateLine", val("abc\ndef\ndef\nghi"), at(2, 0),
+ "undo",
+ setSel(0, 1, 0, 1,
+ 1, 1, 1, 1,
+ 2, 1, 2, 1), "duplicateLine",
+ val("abc\nabc\ndef\ndef\nghi\nghi"), hasSel(1, 1, 1, 1,
+ 3, 1, 3, 1,
+ 5, 1, 5, 1));
+ stTest("duplicateLineSelection", "abcdef",
+ setSel(0, 1, 0, 1,
+ 0, 2, 0, 4,
+ 0, 5, 0, 5),
+ "duplicateLine",
+ val("abcdef\nabcdcdef\nabcdcdef"), hasSel(2, 1, 2, 1,
+ 2, 4, 2, 6,
+ 2, 7, 2, 7));
+
+ stTest("sortLines", "c\nb\na\nC\nB\nA",
+ "sortLines", val("A\nB\nC\na\nb\nc"),
+ "undo",
+ setSel(0, 0, 2, 0,
+ 3, 0, 5, 0),
+ "sortLines", val("b\nc\na\nB\nC\nA"),
+ hasSel(0, 0, 2, 0,
+ 3, 0, 5, 0),
+ "undo",
+ setSel(1, 0, 5, 0), "sortLinesInsensitive", val("c\na\nB\nb\nC\nA"));
+
+ stTest("bookmarks", "abc\ndef\nghi\njkl",
+ Pos(0, 1), "toggleBookmark",
+ setSel(1, 1, 1, 2), "toggleBookmark",
+ setSel(2, 1, 2, 2), "toggleBookmark",
+ "nextBookmark", hasSel(0, 1, 0, 1),
+ "nextBookmark", hasSel(1, 1, 1, 2),
+ "nextBookmark", hasSel(2, 1, 2, 2),
+ "prevBookmark", hasSel(1, 1, 1, 2),
+ "prevBookmark", hasSel(0, 1, 0, 1),
+ "prevBookmark", hasSel(2, 1, 2, 2),
+ "prevBookmark", hasSel(1, 1, 1, 2),
+ "toggleBookmark",
+ "prevBookmark", hasSel(2, 1, 2, 2),
+ "prevBookmark", hasSel(0, 1, 0, 1),
+ "selectBookmarks", hasSel(0, 1, 0, 1,
+ 2, 1, 2, 2),
+ "clearBookmarks",
+ Pos(0, 0), "selectBookmarks", at(0, 0));
+
+ stTest("smartBackspace", " foo\n bar",
+ setSel(0, 2, 0, 2, 1, 4, 1, 4, 1, 6, 1, 6), "smartBackspace",
+ val("foo\n br"))
+
+ stTest("upAndDowncaseAtCursor", "abc\ndef x\nghI",
+ setSel(0, 1, 0, 3,
+ 1, 1, 1, 1,
+ 1, 4, 1, 4), "upcaseAtCursor",
+ val("aBC\nDEF x\nghI"), hasSel(0, 1, 0, 3,
+ 1, 3, 1, 3,
+ 1, 4, 1, 4),
+ "downcaseAtCursor",
+ val("abc\ndef x\nghI"), hasSel(0, 1, 0, 3,
+ 1, 3, 1, 3,
+ 1, 4, 1, 4));
+
+ stTest("mark", "abc\ndef\nghi",
+ Pos(1, 1), "setSublimeMark",
+ Pos(2, 1), "selectToSublimeMark", hasSel(2, 1, 1, 1),
+ Pos(0, 1), "swapWithSublimeMark", at(1, 1), "swapWithSublimeMark", at(0, 1),
+ "deleteToSublimeMark", val("aef\nghi"),
+ "sublimeYank", val("abc\ndef\nghi"), at(1, 1));
+
+ stTest("findUnder", "foo foobar a",
+ "findUnder", hasSel(0, 4, 0, 7),
+ "findUnder", hasSel(0, 0, 0, 3),
+ "findUnderPrevious", hasSel(0, 4, 0, 7),
+ "findUnderPrevious", hasSel(0, 0, 0, 3),
+ Pos(0, 4), "findUnder", hasSel(0, 4, 0, 10),
+ Pos(0, 11), "findUnder", hasSel(0, 11, 0, 11));
+})();
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/test.js b/devtools/client/shared/sourceeditor/test/codemirror/test.js
new file mode 100644
index 0000000000..846d94e8a9
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/test.js
@@ -0,0 +1,2686 @@
+var Pos = CodeMirror.Pos;
+
+CodeMirror.defaults.rtlMoveVisually = true;
+
+function forEach(arr, f) {
+ for (var i = 0, e = arr.length; i < e; ++i) f(arr[i], i);
+}
+
+function addDoc(cm, width, height) {
+ var content = [], line = "";
+ for (var i = 0; i < width; ++i) line += "x";
+ for (var i = 0; i < height; ++i) content.push(line);
+ cm.setValue(content.join("\n"));
+}
+
+function byClassName(elt, cls) {
+ if (elt.getElementsByClassName) return elt.getElementsByClassName(cls);
+ var found = [], re = new RegExp("\\b" + cls + "\\b");
+ function search(elt) {
+ if (elt.nodeType == 3) return;
+ if (re.test(elt.className)) found.push(elt);
+ for (var i = 0, e = elt.childNodes.length; i < e; ++i)
+ search(elt.childNodes[i]);
+ }
+ search(elt);
+ return found;
+}
+
+var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
+var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
+var mac = /Mac/.test(navigator.platform);
+var phantom = /PhantomJS/.test(navigator.userAgent);
+var opera = /Opera\/\./.test(navigator.userAgent);
+var opera_version = opera && navigator.userAgent.match(/Version\/(\d+\.\d+)/);
+if (opera_version) opera_version = Number(opera_version);
+var opera_lt10 = opera && (!opera_version || opera_version < 10);
+
+namespace = "core_";
+
+test("core_fromTextArea", function() {
+ var te = document.getElementById("code");
+ te.value = "CONTENT";
+ var cm = CodeMirror.fromTextArea(te);
+ is(!te.offsetHeight);
+ eq(cm.getValue(), "CONTENT");
+ cm.setValue("foo\nbar");
+ eq(cm.getValue(), "foo\nbar");
+ cm.save();
+ is(/^foo\r?\nbar$/.test(te.value));
+ cm.setValue("xxx");
+ cm.toTextArea();
+ is(te.offsetHeight);
+ eq(te.value, "xxx");
+});
+
+testCM("getRange", function(cm) {
+ eq(cm.getLine(0), "1234");
+ eq(cm.getLine(1), "5678");
+ eq(cm.getLine(2), null);
+ eq(cm.getLine(-1), null);
+ eq(cm.getRange(Pos(0, 0), Pos(0, 3)), "123");
+ eq(cm.getRange(Pos(0, -1), Pos(0, 200)), "1234");
+ eq(cm.getRange(Pos(0, 2), Pos(1, 2)), "34\n56");
+ eq(cm.getRange(Pos(1, 2), Pos(100, 0)), "78");
+}, {value: "1234\n5678"});
+
+testCM("replaceRange", function(cm) {
+ eq(cm.getValue(), "");
+ cm.replaceRange("foo\n", Pos(0, 0));
+ eq(cm.getValue(), "foo\n");
+ cm.replaceRange("a\nb", Pos(0, 1));
+ eq(cm.getValue(), "fa\nboo\n");
+ eq(cm.lineCount(), 3);
+ cm.replaceRange("xyzzy", Pos(0, 0), Pos(1, 1));
+ eq(cm.getValue(), "xyzzyoo\n");
+ cm.replaceRange("abc", Pos(0, 0), Pos(10, 0));
+ eq(cm.getValue(), "abc");
+ eq(cm.lineCount(), 1);
+});
+
+testCM("selection", function(cm) {
+ cm.setSelection(Pos(0, 4), Pos(2, 2));
+ is(cm.somethingSelected());
+ eq(cm.getSelection(), "11\n222222\n33");
+ eqCursorPos(cm.getCursor(false), Pos(2, 2));
+ eqCursorPos(cm.getCursor(true), Pos(0, 4));
+ cm.setSelection(Pos(1, 0));
+ is(!cm.somethingSelected());
+ eq(cm.getSelection(), "");
+ eqCursorPos(cm.getCursor(true), Pos(1, 0));
+ cm.replaceSelection("abc", "around");
+ eq(cm.getSelection(), "abc");
+ eq(cm.getValue(), "111111\nabc222222\n333333");
+ cm.replaceSelection("def", "end");
+ eq(cm.getSelection(), "");
+ eqCursorPos(cm.getCursor(true), Pos(1, 3));
+ cm.setCursor(Pos(2, 1));
+ eqCursorPos(cm.getCursor(true), Pos(2, 1));
+ cm.setCursor(1, 2);
+ eqCursorPos(cm.getCursor(true), Pos(1, 2));
+}, {value: "111111\n222222\n333333"});
+
+testCM("extendSelection", function(cm) {
+ cm.setExtending(true);
+ addDoc(cm, 10, 10);
+ cm.setSelection(Pos(3, 5));
+ eqCursorPos(cm.getCursor("head"), Pos(3, 5));
+ eqCursorPos(cm.getCursor("anchor"), Pos(3, 5));
+ cm.setSelection(Pos(2, 5), Pos(5, 5));
+ eqCursorPos(cm.getCursor("head"), Pos(5, 5));
+ eqCursorPos(cm.getCursor("anchor"), Pos(2, 5));
+ eqCursorPos(cm.getCursor("start"), Pos(2, 5));
+ eqCursorPos(cm.getCursor("end"), Pos(5, 5));
+ cm.setSelection(Pos(5, 5), Pos(2, 5));
+ eqCursorPos(cm.getCursor("head"), Pos(2, 5));
+ eqCursorPos(cm.getCursor("anchor"), Pos(5, 5));
+ eqCursorPos(cm.getCursor("start"), Pos(2, 5));
+ eqCursorPos(cm.getCursor("end"), Pos(5, 5));
+ cm.extendSelection(Pos(3, 2));
+ eqCursorPos(cm.getCursor("head"), Pos(3, 2));
+ eqCursorPos(cm.getCursor("anchor"), Pos(5, 5));
+ cm.extendSelection(Pos(6, 2));
+ eqCursorPos(cm.getCursor("head"), Pos(6, 2));
+ eqCursorPos(cm.getCursor("anchor"), Pos(5, 5));
+ cm.extendSelection(Pos(6, 3), Pos(6, 4));
+ eqCursorPos(cm.getCursor("head"), Pos(6, 4));
+ eqCursorPos(cm.getCursor("anchor"), Pos(5, 5));
+ cm.extendSelection(Pos(0, 3), Pos(0, 4));
+ eqCursorPos(cm.getCursor("head"), Pos(0, 3));
+ eqCursorPos(cm.getCursor("anchor"), Pos(5, 5));
+ cm.extendSelection(Pos(4, 5), Pos(6, 5));
+ eqCursorPos(cm.getCursor("head"), Pos(6, 5));
+ eqCursorPos(cm.getCursor("anchor"), Pos(4, 5));
+ cm.setExtending(false);
+ cm.extendSelection(Pos(0, 3), Pos(0, 4));
+ eqCursorPos(cm.getCursor("head"), Pos(0, 3));
+ eqCursorPos(cm.getCursor("anchor"), Pos(0, 4));
+});
+
+testCM("lines", function(cm) {
+ eq(cm.getLine(0), "111111");
+ eq(cm.getLine(1), "222222");
+ eq(cm.getLine(-1), null);
+ cm.replaceRange("", Pos(1, 0), Pos(2, 0))
+ cm.replaceRange("abc", Pos(1, 0), Pos(1));
+ eq(cm.getValue(), "111111\nabc");
+}, {value: "111111\n222222\n333333"});
+
+testCM("indent", function(cm) {
+ cm.indentLine(1);
+ eq(cm.getLine(1), " blah();");
+ cm.setOption("indentUnit", 8);
+ cm.indentLine(1);
+ eq(cm.getLine(1), "\tblah();");
+ cm.setOption("indentUnit", 10);
+ cm.setOption("tabSize", 4);
+ cm.indentLine(1);
+ eq(cm.getLine(1), "\t\t blah();");
+}, {value: "if (x) {\nblah();\n}", indentUnit: 3, indentWithTabs: true, tabSize: 8});
+
+testCM("indentByNumber", function(cm) {
+ cm.indentLine(0, 2);
+ eq(cm.getLine(0), " foo");
+ cm.indentLine(0, -200);
+ eq(cm.getLine(0), "foo");
+ cm.setSelection(Pos(0, 0), Pos(1, 2));
+ cm.indentSelection(3);
+ eq(cm.getValue(), " foo\n bar\nbaz");
+}, {value: "foo\nbar\nbaz"});
+
+test("core_defaults", function() {
+ var defsCopy = {}, defs = CodeMirror.defaults;
+ for (var opt in defs) defsCopy[opt] = defs[opt];
+ defs.indentUnit = 5;
+ defs.value = "uu";
+ defs.indentWithTabs = true;
+ defs.tabindex = 55;
+ var place = document.getElementById("testground"), cm = CodeMirror(place);
+ try {
+ eq(cm.getOption("indentUnit"), 5);
+ cm.setOption("indentUnit", 10);
+ eq(defs.indentUnit, 5);
+ eq(cm.getValue(), "uu");
+ eq(cm.getOption("indentWithTabs"), true);
+ eq(cm.getInputField().tabIndex, 55);
+ }
+ finally {
+ for (var opt in defsCopy) defs[opt] = defsCopy[opt];
+ place.removeChild(cm.getWrapperElement());
+ }
+});
+
+testCM("lineInfo", function(cm) {
+ eq(cm.lineInfo(-1), null);
+ var mark = document.createElement("span");
+ var lh = cm.setGutterMarker(1, "FOO", mark);
+ var info = cm.lineInfo(1);
+ eq(info.text, "222222");
+ eq(info.gutterMarkers.FOO, mark);
+ eq(info.line, 1);
+ eq(cm.lineInfo(2).gutterMarkers, null);
+ cm.setGutterMarker(lh, "FOO", null);
+ eq(cm.lineInfo(1).gutterMarkers, null);
+ cm.setGutterMarker(1, "FOO", mark);
+ cm.setGutterMarker(0, "FOO", mark);
+ cm.clearGutter("FOO");
+ eq(cm.lineInfo(0).gutterMarkers, null);
+ eq(cm.lineInfo(1).gutterMarkers, null);
+}, {value: "111111\n222222\n333333"});
+
+testCM("coords", function(cm) {
+ cm.setSize(null, 100);
+ addDoc(cm, 32, 200);
+ var top = cm.charCoords(Pos(0, 0));
+ var bot = cm.charCoords(Pos(200, 30));
+ is(top.left < bot.left);
+ is(top.top < bot.top);
+ is(top.top < top.bottom);
+ cm.scrollTo(null, 100);
+ var top2 = cm.charCoords(Pos(0, 0));
+ is(top.top > top2.top);
+ eq(top.left, top2.left);
+});
+
+testCM("coordsChar", function(cm) {
+ addDoc(cm, 35, 70);
+ for (var i = 0; i < 2; ++i) {
+ var sys = i ? "local" : "page";
+ for (var ch = 0; ch <= 35; ch += 5) {
+ for (var line = 0; line < 70; line += 5) {
+ cm.setCursor(line, ch);
+ var coords = cm.charCoords(Pos(line, ch), sys);
+ var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys);
+ eqCharPos(pos, Pos(line, ch));
+ }
+ }
+ }
+}, {lineNumbers: true});
+
+testCM("coordsCharBidi", function(cm) {
+ addDoc(cm, 35, 70);
+ // Put an rtl character into each line to trigger the bidi code path in coordsChar
+ cm.setValue(cm.getValue().replace(/\bx/g, 'و'))
+ for (var i = 0; i < 2; ++i) {
+ var sys = i ? "local" : "page";
+ for (var ch = 2; ch <= 35; ch += 5) {
+ for (var line = 0; line < 70; line += 5) {
+ cm.setCursor(line, ch);
+ var coords = cm.charCoords(Pos(line, ch), sys);
+ var pos = cm.coordsChar({left: coords.left + 1, top: coords.top + 1}, sys);
+ eqCharPos(pos, Pos(line, ch));
+ }
+ }
+ }
+}, {lineNumbers: true});
+
+testCM("badBidiOptimization", function(cm) {
+ var coords = cm.charCoords(Pos(0, 34))
+ eqCharPos(cm.coordsChar({left: coords.right, top: coords.top + 2}), Pos(0, 34))
+}, {value: "----------<p class=\"title\">هل يمكنك اختيار مستوى قسط التأمين الذي ترغب بدفعه؟</p>"})
+
+testCM("posFromIndex", function(cm) {
+ cm.setValue(
+ "This function should\n" +
+ "convert a zero based index\n" +
+ "to line and ch."
+ );
+
+ var examples = [
+ { index: -1, line: 0, ch: 0 }, // <- Tests clipping
+ { index: 0, line: 0, ch: 0 },
+ { index: 10, line: 0, ch: 10 },
+ { index: 39, line: 1, ch: 18 },
+ { index: 55, line: 2, ch: 7 },
+ { index: 63, line: 2, ch: 15 },
+ { index: 64, line: 2, ch: 15 } // <- Tests clipping
+ ];
+
+ for (var i = 0; i < examples.length; i++) {
+ var example = examples[i];
+ var pos = cm.posFromIndex(example.index);
+ eq(pos.line, example.line);
+ eq(pos.ch, example.ch);
+ if (example.index >= 0 && example.index < 64)
+ eq(cm.indexFromPos(pos), example.index);
+ }
+});
+
+testCM("undo", function(cm) {
+ cm.replaceRange("def", Pos(0, 0), Pos(0));
+ eq(cm.historySize().undo, 1);
+ cm.undo();
+ eq(cm.getValue(), "abc");
+ eq(cm.historySize().undo, 0);
+ eq(cm.historySize().redo, 1);
+ cm.redo();
+ eq(cm.getValue(), "def");
+ eq(cm.historySize().undo, 1);
+ eq(cm.historySize().redo, 0);
+ cm.setValue("1\n\n\n2");
+ cm.clearHistory();
+ eq(cm.historySize().undo, 0);
+ for (var i = 0; i < 20; ++i) {
+ cm.replaceRange("a", Pos(0, 0));
+ cm.replaceRange("b", Pos(3, 0));
+ }
+ eq(cm.historySize().undo, 40);
+ for (var i = 0; i < 40; ++i)
+ cm.undo();
+ eq(cm.historySize().redo, 40);
+ eq(cm.getValue(), "1\n\n\n2");
+}, {value: "abc"});
+
+testCM("undoDepth", function(cm) {
+ cm.replaceRange("d", Pos(0));
+ cm.replaceRange("e", Pos(0));
+ cm.replaceRange("f", Pos(0));
+ cm.undo(); cm.undo(); cm.undo();
+ eq(cm.getValue(), "abcd");
+}, {value: "abc", undoDepth: 4});
+
+testCM("undoDoesntClearValue", function(cm) {
+ cm.undo();
+ eq(cm.getValue(), "x");
+}, {value: "x"});
+
+testCM("undoMultiLine", function(cm) {
+ cm.operation(function() {
+ cm.replaceRange("x", Pos(0, 0));
+ cm.replaceRange("y", Pos(1, 0));
+ });
+ cm.undo();
+ eq(cm.getValue(), "abc\ndef\nghi");
+ cm.operation(function() {
+ cm.replaceRange("y", Pos(1, 0));
+ cm.replaceRange("x", Pos(0, 0));
+ });
+ cm.undo();
+ eq(cm.getValue(), "abc\ndef\nghi");
+ cm.operation(function() {
+ cm.replaceRange("y", Pos(2, 0));
+ cm.replaceRange("x", Pos(1, 0));
+ cm.replaceRange("z", Pos(2, 0));
+ });
+ cm.undo();
+ eq(cm.getValue(), "abc\ndef\nghi", 3);
+}, {value: "abc\ndef\nghi"});
+
+testCM("undoComposite", function(cm) {
+ cm.replaceRange("y", Pos(1));
+ cm.operation(function() {
+ cm.replaceRange("x", Pos(0));
+ cm.replaceRange("z", Pos(2));
+ });
+ eq(cm.getValue(), "ax\nby\ncz\n");
+ cm.undo();
+ eq(cm.getValue(), "a\nby\nc\n");
+ cm.undo();
+ eq(cm.getValue(), "a\nb\nc\n");
+ cm.redo(); cm.redo();
+ eq(cm.getValue(), "ax\nby\ncz\n");
+}, {value: "a\nb\nc\n"});
+
+testCM("undoSelection", function(cm) {
+ cm.setSelection(Pos(0, 2), Pos(0, 4));
+ cm.replaceSelection("");
+ cm.setCursor(Pos(1, 0));
+ cm.undo();
+ eqCursorPos(cm.getCursor(true), Pos(0, 2));
+ eqCursorPos(cm.getCursor(false), Pos(0, 4));
+ cm.setCursor(Pos(1, 0));
+ cm.redo();
+ eqCursorPos(cm.getCursor(true), Pos(0, 2));
+ eqCursorPos(cm.getCursor(false), Pos(0, 2));
+}, {value: "abcdefgh\n"});
+
+testCM("undoSelectionAsBefore", function(cm) {
+ cm.replaceSelection("abc", "around");
+ cm.undo();
+ cm.redo();
+ eq(cm.getSelection(), "abc");
+});
+
+testCM("selectionChangeConfusesHistory", function(cm) {
+ cm.replaceSelection("abc", null, "dontmerge");
+ cm.operation(function() {
+ cm.setCursor(Pos(0, 0));
+ cm.replaceSelection("abc", null, "dontmerge");
+ });
+ eq(cm.historySize().undo, 2);
+});
+
+testCM("markTextSingleLine", function(cm) {
+ forEach([{a: 0, b: 1, c: "", f: 2, t: 5},
+ {a: 0, b: 4, c: "", f: 0, t: 2},
+ {a: 1, b: 2, c: "x", f: 3, t: 6},
+ {a: 4, b: 5, c: "", f: 3, t: 5},
+ {a: 4, b: 5, c: "xx", f: 3, t: 7},
+ {a: 2, b: 5, c: "", f: 2, t: 3},
+ {a: 2, b: 5, c: "abcd", f: 6, t: 7},
+ {a: 2, b: 6, c: "x", f: null, t: null},
+ {a: 3, b: 6, c: "", f: null, t: null},
+ {a: 0, b: 9, c: "hallo", f: null, t: null},
+ {a: 4, b: 6, c: "x", f: 3, t: 4},
+ {a: 4, b: 8, c: "", f: 3, t: 4},
+ {a: 6, b: 6, c: "a", f: 3, t: 6},
+ {a: 8, b: 9, c: "", f: 3, t: 6}], function(test) {
+ cm.setValue("1234567890");
+ var r = cm.markText(Pos(0, 3), Pos(0, 6), {className: "foo"});
+ cm.replaceRange(test.c, Pos(0, test.a), Pos(0, test.b));
+ var f = r.find();
+ eq(f && f.from.ch, test.f); eq(f && f.to.ch, test.t);
+ });
+});
+
+testCM("markTextMultiLine", function(cm) {
+ function p(v) { return v && Pos(v[0], v[1]); }
+ forEach([{a: [0, 0], b: [0, 5], c: "", f: [0, 0], t: [2, 5]},
+ {a: [0, 0], b: [0, 5], c: "foo\n", f: [1, 0], t: [3, 5]},
+ {a: [0, 1], b: [0, 10], c: "", f: [0, 1], t: [2, 5]},
+ {a: [0, 5], b: [0, 6], c: "x", f: [0, 6], t: [2, 5]},
+ {a: [0, 0], b: [1, 0], c: "", f: [0, 0], t: [1, 5]},
+ {a: [0, 6], b: [2, 4], c: "", f: [0, 5], t: [0, 7]},
+ {a: [0, 6], b: [2, 4], c: "aa", f: [0, 5], t: [0, 9]},
+ {a: [1, 2], b: [1, 8], c: "", f: [0, 5], t: [2, 5]},
+ {a: [0, 5], b: [2, 5], c: "xx", f: null, t: null},
+ {a: [0, 0], b: [2, 10], c: "x", f: null, t: null},
+ {a: [1, 5], b: [2, 5], c: "", f: [0, 5], t: [1, 5]},
+ {a: [2, 0], b: [2, 3], c: "", f: [0, 5], t: [2, 2]},
+ {a: [2, 5], b: [3, 0], c: "a\nb", f: [0, 5], t: [2, 5]},
+ {a: [2, 3], b: [3, 0], c: "x", f: [0, 5], t: [2, 3]},
+ {a: [1, 1], b: [1, 9], c: "1\n2\n3", f: [0, 5], t: [4, 5]}], function(test) {
+ cm.setValue("aaaaaaaaaa\nbbbbbbbbbb\ncccccccccc\ndddddddd\n");
+ var r = cm.markText(Pos(0, 5), Pos(2, 5),
+ {className: "CodeMirror-matchingbracket"});
+ cm.replaceRange(test.c, p(test.a), p(test.b));
+ var f = r.find();
+ eqCursorPos(f && f.from, p(test.f)); eqCursorPos(f && f.to, p(test.t));
+ });
+});
+
+testCM("markTextUndo", function(cm) {
+ var marker1, marker2, bookmark;
+ marker1 = cm.markText(Pos(0, 1), Pos(0, 3),
+ {className: "CodeMirror-matchingbracket"});
+ marker2 = cm.markText(Pos(0, 0), Pos(2, 1),
+ {className: "CodeMirror-matchingbracket"});
+ bookmark = cm.setBookmark(Pos(1, 5));
+ cm.operation(function(){
+ cm.replaceRange("foo", Pos(0, 2));
+ cm.replaceRange("bar\nbaz\nbug\n", Pos(2, 0), Pos(3, 0));
+ });
+ var v1 = cm.getValue();
+ cm.setValue("");
+ eq(marker1.find(), null); eq(marker2.find(), null); eq(bookmark.find(), null);
+ cm.undo();
+ eqCursorPos(bookmark.find(), Pos(1, 5), "still there");
+ cm.undo();
+ var m1Pos = marker1.find(), m2Pos = marker2.find();
+ eqCursorPos(m1Pos.from, Pos(0, 1)); eqCursorPos(m1Pos.to, Pos(0, 3));
+ eqCursorPos(m2Pos.from, Pos(0, 0)); eqCursorPos(m2Pos.to, Pos(2, 1));
+ eqCursorPos(bookmark.find(), Pos(1, 5));
+ cm.redo(); cm.redo();
+ eq(bookmark.find(), null);
+ cm.undo();
+ eqCursorPos(bookmark.find(), Pos(1, 5));
+ eq(cm.getValue(), v1);
+}, {value: "1234\n56789\n00\n"});
+
+testCM("markTextStayGone", function(cm) {
+ var m1 = cm.markText(Pos(0, 0), Pos(0, 1));
+ cm.replaceRange("hi", Pos(0, 2));
+ m1.clear();
+ cm.undo();
+ eq(m1.find(), null);
+}, {value: "hello"});
+
+testCM("markTextAllowEmpty", function(cm) {
+ var m1 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false});
+ is(m1.find());
+ cm.replaceRange("x", Pos(0, 0));
+ is(m1.find());
+ cm.replaceRange("y", Pos(0, 2));
+ is(m1.find());
+ cm.replaceRange("z", Pos(0, 3), Pos(0, 4));
+ is(!m1.find());
+ var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {clearWhenEmpty: false,
+ inclusiveLeft: true,
+ inclusiveRight: true});
+ cm.replaceRange("q", Pos(0, 1), Pos(0, 2));
+ is(m2.find());
+ cm.replaceRange("", Pos(0, 0), Pos(0, 3));
+ is(!m2.find());
+ var m3 = cm.markText(Pos(0, 1), Pos(0, 1), {clearWhenEmpty: false});
+ cm.replaceRange("a", Pos(0, 3));
+ is(m3.find());
+ cm.replaceRange("b", Pos(0, 1));
+ is(!m3.find());
+}, {value: "abcde"});
+
+testCM("markTextStacked", function(cm) {
+ var m1 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
+ var m2 = cm.markText(Pos(0, 0), Pos(0, 0), {clearWhenEmpty: false});
+ cm.replaceRange("B", Pos(0, 1));
+ is(m1.find() && m2.find());
+}, {value: "A"});
+
+testCM("undoPreservesNewMarks", function(cm) {
+ cm.markText(Pos(0, 3), Pos(0, 4));
+ cm.markText(Pos(1, 1), Pos(1, 3));
+ cm.replaceRange("", Pos(0, 3), Pos(3, 1));
+ var mBefore = cm.markText(Pos(0, 0), Pos(0, 1));
+ var mAfter = cm.markText(Pos(0, 5), Pos(0, 6));
+ var mAround = cm.markText(Pos(0, 2), Pos(0, 4));
+ cm.undo();
+ eqCursorPos(mBefore.find().from, Pos(0, 0));
+ eqCursorPos(mBefore.find().to, Pos(0, 1));
+ eqCursorPos(mAfter.find().from, Pos(3, 3));
+ eqCursorPos(mAfter.find().to, Pos(3, 4));
+ eqCursorPos(mAround.find().from, Pos(0, 2));
+ eqCursorPos(mAround.find().to, Pos(3, 2));
+ var found = cm.findMarksAt(Pos(2, 2));
+ eq(found.length, 1);
+ eq(found[0], mAround);
+}, {value: "aaaa\nbbbb\ncccc\ndddd"});
+
+testCM("markClearBetween", function(cm) {
+ cm.setValue("aaa\nbbb\nccc\nddd\n");
+ cm.markText(Pos(0, 0), Pos(2));
+ cm.replaceRange("aaa\nbbb\nccc", Pos(0, 0), Pos(2));
+ eq(cm.findMarksAt(Pos(1, 1)).length, 0);
+});
+
+testCM("findMarksMiddle", function(cm) {
+ var mark = cm.markText(Pos(1, 1), Pos(3, 1));
+ var found = cm.findMarks(Pos(2, 1), Pos(2, 2));
+ eq(found.length, 1);
+ eq(found[0], mark);
+}, {value: "line 0\nline 1\nline 2\nline 3"});
+
+testCM("deleteSpanCollapsedInclusiveLeft", function(cm) {
+ var from = Pos(1, 0), to = Pos(1, 1);
+ var m = cm.markText(from, to, {collapsed: true, inclusiveLeft: true});
+ // Delete collapsed span.
+ cm.replaceRange("", from, to);
+}, {value: "abc\nX\ndef"});
+
+testCM("markTextCSS", function(cm) {
+ function present() {
+ var spans = cm.display.lineDiv.getElementsByTagName("span");
+ for (var i = 0; i < spans.length; i++)
+ if (spans[i].style.color && spans[i].textContent == "cdef") return true;
+ }
+ var m = cm.markText(Pos(0, 2), Pos(0, 6), {css: "color: cyan"});
+ is(present());
+ m.clear();
+ is(!present());
+}, {value: "abcdefgh"});
+
+testCM("markTextWithAttributes", function(cm) {
+ function present() {
+ var spans = cm.display.lineDiv.getElementsByTagName("span");
+ for (var i = 0; i < spans.length; i++)
+ if (spans[i].getAttribute("label") == "label" && spans[i].textContent == "cdef") return true;
+ }
+ var m = cm.markText(Pos(0, 2), Pos(0, 6), {attributes: {label: "label"}});
+ is(present());
+ m.clear();
+ is(!present());
+}, {value: "abcdefgh"});
+
+testCM("bookmark", function(cm) {
+ function p(v) { return v && Pos(v[0], v[1]); }
+ forEach([{a: [1, 0], b: [1, 1], c: "", d: [1, 4]},
+ {a: [1, 1], b: [1, 1], c: "xx", d: [1, 7]},
+ {a: [1, 4], b: [1, 5], c: "ab", d: [1, 6]},
+ {a: [1, 4], b: [1, 6], c: "", d: null},
+ {a: [1, 5], b: [1, 6], c: "abc", d: [1, 5]},
+ {a: [1, 6], b: [1, 8], c: "", d: [1, 5]},
+ {a: [1, 4], b: [1, 4], c: "\n\n", d: [3, 1]},
+ {bm: [1, 9], a: [1, 1], b: [1, 1], c: "\n", d: [2, 8]}], function(test) {
+ cm.setValue("1234567890\n1234567890\n1234567890");
+ var b = cm.setBookmark(p(test.bm) || Pos(1, 5));
+ cm.replaceRange(test.c, p(test.a), p(test.b));
+ eqCursorPos(b.find(), p(test.d));
+ });
+});
+
+testCM("bookmarkInsertLeft", function(cm) {
+ var br = cm.setBookmark(Pos(0, 2), {insertLeft: false});
+ var bl = cm.setBookmark(Pos(0, 2), {insertLeft: true});
+ cm.setCursor(Pos(0, 2));
+ cm.replaceSelection("hi");
+ eqCursorPos(br.find(), Pos(0, 2));
+ eqCursorPos(bl.find(), Pos(0, 4));
+ cm.replaceRange("", Pos(0, 4), Pos(0, 5));
+ cm.replaceRange("", Pos(0, 2), Pos(0, 4));
+ cm.replaceRange("", Pos(0, 1), Pos(0, 2));
+ // Verify that deleting next to bookmarks doesn't kill them
+ eqCursorPos(br.find(), Pos(0, 1));
+ eqCursorPos(bl.find(), Pos(0, 1));
+}, {value: "abcdef"});
+
+testCM("bookmarkCursor", function(cm) {
+ var pos01 = cm.cursorCoords(Pos(0, 1)), pos11 = cm.cursorCoords(Pos(1, 1)),
+ pos20 = cm.cursorCoords(Pos(2, 0)), pos30 = cm.cursorCoords(Pos(3, 0)),
+ pos41 = cm.cursorCoords(Pos(4, 1));
+ cm.setBookmark(Pos(0, 1), {widget: document.createTextNode("←"), insertLeft: true});
+ cm.setBookmark(Pos(2, 0), {widget: document.createTextNode("←"), insertLeft: true});
+ cm.setBookmark(Pos(1, 1), {widget: document.createTextNode("→")});
+ cm.setBookmark(Pos(3, 0), {widget: document.createTextNode("→")});
+ var new01 = cm.cursorCoords(Pos(0, 1)), new11 = cm.cursorCoords(Pos(1, 1)),
+ new20 = cm.cursorCoords(Pos(2, 0)), new30 = cm.cursorCoords(Pos(3, 0));
+ near(new01.left, pos01.left, 1);
+ near(new01.top, pos01.top, 1);
+ is(new11.left > pos11.left, "at right, middle of line");
+ near(new11.top == pos11.top, 1);
+ near(new20.left, pos20.left, 1);
+ near(new20.top, pos20.top, 1);
+ is(new30.left > pos30.left, "at right, empty line");
+ near(new30.top, pos30, 1);
+ cm.setBookmark(Pos(4, 0), {widget: document.createTextNode("→")});
+ is(cm.cursorCoords(Pos(4, 1)).left > pos41.left, "single-char bug");
+}, {value: "foo\nbar\n\n\nx\ny"});
+
+testCM("multiBookmarkCursor", function(cm) {
+ if (phantom) return;
+ var ms = [], m;
+ function add(insertLeft) {
+ for (var i = 0; i < 3; ++i) {
+ var node = document.createElement("span");
+ node.innerHTML = "X";
+ ms.push(cm.setBookmark(Pos(0, 1), {widget: node, insertLeft: insertLeft}));
+ }
+ }
+ var base1 = cm.cursorCoords(Pos(0, 1)).left, base4 = cm.cursorCoords(Pos(0, 4)).left;
+ add(true);
+ near(base1, cm.cursorCoords(Pos(0, 1)).left, 1);
+ while (m = ms.pop()) m.clear();
+ add(false);
+ near(base4, cm.cursorCoords(Pos(0, 1)).left, 1);
+}, {value: "abcdefg"});
+
+testCM("getAllMarks", function(cm) {
+ addDoc(cm, 10, 10);
+ var m1 = cm.setBookmark(Pos(0, 2));
+ var m2 = cm.markText(Pos(0, 2), Pos(3, 2));
+ var m3 = cm.markText(Pos(1, 2), Pos(1, 8));
+ var m4 = cm.markText(Pos(8, 0), Pos(9, 0));
+ eq(cm.getAllMarks().length, 4);
+ m1.clear();
+ m3.clear();
+ eq(cm.getAllMarks().length, 2);
+});
+
+testCM("setValueClears", function(cm) {
+ cm.addLineClass(0, "wrap", "foo");
+ var mark = cm.markText(Pos(0, 0), Pos(1, 1), {inclusiveLeft: true, inclusiveRight: true});
+ cm.setValue("foo");
+ is(!cm.lineInfo(0).wrapClass);
+ is(!mark.find());
+}, {value: "a\nb"});
+
+testCM("bug577", function(cm) {
+ cm.setValue("a\nb");
+ cm.clearHistory();
+ cm.setValue("fooooo");
+ cm.undo();
+});
+
+testCM("scrollSnap", function(cm) {
+ cm.setSize(100, 100);
+ addDoc(cm, 200, 200);
+ cm.setCursor(Pos(100, 180));
+ var info = cm.getScrollInfo();
+ is(info.left > 0 && info.top > 0);
+ cm.setCursor(Pos(0, 0));
+ info = cm.getScrollInfo();
+ is(info.left == 0 && info.top == 0, "scrolled clean to top");
+ cm.setCursor(Pos(100, 180));
+ cm.setCursor(Pos(199, 0));
+ info = cm.getScrollInfo();
+ is(info.left == 0 && info.top + 2 > info.height - cm.getScrollerElement().clientHeight, "scrolled clean to bottom");
+});
+
+testCM("scrollIntoView", function(cm) {
+ if (phantom) return;
+ function test(line, ch, msg) {
+ var pos = Pos(line, ch);
+ cm.scrollIntoView(pos);
+ var outer = cm.getWrapperElement().getBoundingClientRect();
+ var box = cm.charCoords(pos, "window");
+ is(box.left >= outer.left, msg + " (left)");
+ is(box.right <= outer.right, msg + " (right)");
+ is(box.top >= outer.top, msg + " (top)");
+ is(box.bottom <= outer.bottom, msg + " (bottom)");
+ }
+ addDoc(cm, 200, 200);
+ test(199, 199, "bottom right");
+ test(0, 0, "top left");
+ test(100, 100, "center");
+ test(199, 0, "bottom left");
+ test(0, 199, "top right");
+ test(100, 100, "center again");
+});
+
+testCM("scrollBackAndForth", function(cm) {
+ addDoc(cm, 1, 200);
+ cm.operation(function() {
+ cm.scrollIntoView(Pos(199, 0));
+ cm.scrollIntoView(Pos(4, 0));
+ });
+ is(cm.getScrollInfo().top > 0);
+});
+
+testCM("selectAllNoScroll", function(cm) {
+ addDoc(cm, 1, 200);
+ cm.execCommand("selectAll");
+ eq(cm.getScrollInfo().top, 0);
+ cm.setCursor(199);
+ cm.execCommand("selectAll");
+ is(cm.getScrollInfo().top > 0);
+});
+
+testCM("selectionPos", function(cm) {
+ if (phantom || cm.getOption("inputStyle") != "textarea") return;
+ cm.setSize(100, 100);
+ addDoc(cm, 200, 100);
+ cm.setSelection(Pos(1, 100), Pos(98, 100));
+ var lineWidth = cm.charCoords(Pos(0, 200), "local").left;
+ var lineHeight = (cm.charCoords(Pos(99)).top - cm.charCoords(Pos(0)).top) / 100;
+ cm.scrollTo(0, 0);
+ var selElt = byClassName(cm.getWrapperElement(), "CodeMirror-selected");
+ var outer = cm.getWrapperElement().getBoundingClientRect();
+ var sawMiddle, sawTop, sawBottom;
+ for (var i = 0, e = selElt.length; i < e; ++i) {
+ var box = selElt[i].getBoundingClientRect();
+ var atLeft = box.left - outer.left < 30;
+ var width = box.right - box.left;
+ var atRight = box.right - outer.left > .8 * lineWidth;
+ if (atLeft && atRight) {
+ sawMiddle = true;
+ is(box.bottom - box.top > 90 * lineHeight, "middle high");
+ is(width > .9 * lineWidth, "middle wide");
+ } else {
+ is(width > .4 * lineWidth, "top/bot wide enough");
+ is(width < .6 * lineWidth, "top/bot slim enough");
+ if (atLeft) {
+ sawBottom = true;
+ is(box.top - outer.top > 96 * lineHeight, "bot below");
+ } else if (atRight) {
+ sawTop = true;
+ is(box.top - outer.top < 2.1 * lineHeight, "top above");
+ }
+ }
+ }
+ is(sawTop && sawBottom && sawMiddle, "all parts");
+}, null);
+
+testCM("restoreHistory", function(cm) {
+ cm.setValue("abc\ndef");
+ cm.replaceRange("hello", Pos(1, 0), Pos(1));
+ cm.replaceRange("goop", Pos(0, 0), Pos(0));
+ cm.undo();
+ var storedVal = cm.getValue(), storedHist = cm.getHistory();
+ if (window.JSON) storedHist = JSON.parse(JSON.stringify(storedHist));
+ eq(storedVal, "abc\nhello");
+ cm.setValue("");
+ cm.clearHistory();
+ eq(cm.historySize().undo, 0);
+ cm.setValue(storedVal);
+ cm.setHistory(storedHist);
+ cm.redo();
+ eq(cm.getValue(), "goop\nhello");
+ cm.undo(); cm.undo();
+ eq(cm.getValue(), "abc\ndef");
+});
+
+testCM("doubleScrollbar", function(cm) {
+ var dummy = document.body.appendChild(document.createElement("p"));
+ dummy.style.cssText = "height: 50px; overflow: scroll; width: 50px";
+ var scrollbarWidth = dummy.offsetWidth + 1 - dummy.clientWidth;
+ document.body.removeChild(dummy);
+ if (scrollbarWidth < 2) return;
+ cm.setSize(null, 100);
+ addDoc(cm, 1, 300);
+ var wrap = cm.getWrapperElement();
+ is(wrap.offsetWidth - byClassName(wrap, "CodeMirror-lines")[0].offsetWidth <= scrollbarWidth * 1.5);
+});
+
+testCM("weirdLinebreaks", function(cm) {
+ cm.setValue("foo\nbar\rbaz\r\nquux\n\rplop");
+ is(cm.getValue(), "foo\nbar\nbaz\nquux\n\nplop");
+ is(cm.lineCount(), 6);
+ cm.setValue("\n\n");
+ is(cm.lineCount(), 3);
+});
+
+testCM("setSize", function(cm) {
+ cm.setSize(100, 100);
+ var wrap = cm.getWrapperElement();
+ is(wrap.offsetWidth, 100);
+ is(wrap.offsetHeight, 100);
+ cm.setSize("100%", "3em");
+ is(wrap.style.width, "100%");
+ is(wrap.style.height, "3em");
+ cm.setSize(null, 40);
+ is(wrap.style.width, "100%");
+ is(wrap.style.height, "40px");
+});
+
+function foldLines(cm, start, end, autoClear) {
+ return cm.markText(Pos(start, 0), Pos(end - 1), {
+ inclusiveLeft: true,
+ inclusiveRight: true,
+ collapsed: true,
+ clearOnEnter: autoClear
+ });
+}
+
+testCM("collapsedLines", function(cm) {
+ addDoc(cm, 4, 10);
+ var range = foldLines(cm, 4, 5), cleared = 0;
+ CodeMirror.on(range, "clear", function() {cleared++;});
+ cm.setCursor(Pos(3, 0));
+ CodeMirror.commands.goLineDown(cm);
+ eqCharPos(cm.getCursor(), Pos(5, 0));
+ cm.replaceRange("abcdefg", Pos(3, 0), Pos(3));
+ cm.setCursor(Pos(3, 6));
+ CodeMirror.commands.goLineDown(cm);
+ eqCharPos(cm.getCursor(), Pos(5, 4));
+ cm.replaceRange("ab", Pos(3, 0), Pos(3));
+ cm.setCursor(Pos(3, 2));
+ CodeMirror.commands.goLineDown(cm);
+ eqCharPos(cm.getCursor(), Pos(5, 2));
+ cm.operation(function() {range.clear(); range.clear();});
+ eq(cleared, 1);
+});
+
+testCM("collapsedRangeCoordsChar", function(cm) {
+ var pos_1_3 = cm.charCoords(Pos(1, 3));
+ pos_1_3.left += 2; pos_1_3.top += 2;
+ var opts = {collapsed: true, inclusiveLeft: true, inclusiveRight: true};
+ var m1 = cm.markText(Pos(0, 0), Pos(2, 0), opts);
+ eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3));
+ m1.clear();
+ var m1 = cm.markText(Pos(0, 0), Pos(1, 1), {collapsed: true, inclusiveLeft: true});
+ var m2 = cm.markText(Pos(1, 1), Pos(2, 0), {collapsed: true, inclusiveRight: true});
+ eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3));
+ m1.clear(); m2.clear();
+ var m1 = cm.markText(Pos(0, 0), Pos(1, 6), opts);
+ eqCharPos(cm.coordsChar(pos_1_3), Pos(3, 3));
+}, {value: "123456\nabcdef\nghijkl\nmnopqr\n"});
+
+testCM("collapsedRangeBetweenLinesSelected", function(cm) {
+ if (cm.getOption("inputStyle") != "textarea") return;
+ var widget = document.createElement("span");
+ widget.textContent = "\u2194";
+ cm.markText(Pos(0, 3), Pos(1, 0), {replacedWith: widget});
+ cm.setSelection(Pos(0, 3), Pos(1, 0));
+ var selElts = byClassName(cm.getWrapperElement(), "CodeMirror-selected");
+ for (var i = 0, w = 0; i < selElts.length; i++)
+ w += selElts[i].offsetWidth;
+ is(w > 0);
+}, {value: "one\ntwo"});
+
+testCM("randomCollapsedRanges", function(cm) {
+ addDoc(cm, 20, 500);
+ cm.operation(function() {
+ for (var i = 0; i < 200; i++) {
+ var start = Pos(Math.floor(Math.random() * 500), Math.floor(Math.random() * 20));
+ if (i % 4)
+ try { cm.markText(start, Pos(start.line + 2, 1), {collapsed: true}); }
+ catch(e) { if (!/overlapping/.test(String(e))) throw e; }
+ else
+ cm.markText(start, Pos(start.line, start.ch + 4), {"className": "foo"});
+ }
+ });
+});
+
+testCM("hiddenLinesAutoUnfold", function(cm) {
+ var range = foldLines(cm, 1, 3, true), cleared = 0;
+ CodeMirror.on(range, "clear", function() {cleared++;});
+ cm.setCursor(Pos(3, 0));
+ eq(cleared, 0);
+ cm.execCommand("goCharLeft");
+ eq(cleared, 1);
+ range = foldLines(cm, 1, 3, true);
+ CodeMirror.on(range, "clear", function() {cleared++;});
+ eqCursorPos(cm.getCursor(), Pos(3, 0));
+ cm.setCursor(Pos(0, 3));
+ cm.execCommand("goCharRight");
+ eq(cleared, 2);
+}, {value: "abc\ndef\nghi\njkl"});
+
+testCM("hiddenLinesSelectAll", function(cm) { // Issue #484
+ addDoc(cm, 4, 20);
+ foldLines(cm, 0, 10);
+ foldLines(cm, 11, 20);
+ CodeMirror.commands.selectAll(cm);
+ eqCursorPos(cm.getCursor(true), Pos(10, 0));
+ eqCursorPos(cm.getCursor(false), Pos(10, 4));
+});
+
+testCM("clickFold", function(cm) { // Issue #5392
+ cm.setValue("foo { bar }")
+ var widget = document.createElement("span")
+ widget.textContent = "<>"
+ cm.markText(Pos(0, 5), Pos(0, 10), {replacedWith: widget})
+ var after = cm.charCoords(Pos(0, 10))
+ var foundOn = cm.coordsChar({left: after.left - 1, top: after.top + 4})
+ is(foundOn.ch <= 5 || foundOn.ch >= 10, "Position is not inside the folded range")
+})
+
+testCM("everythingFolded", function(cm) {
+ addDoc(cm, 2, 2);
+ function enterPress() {
+ cm.triggerOnKeyDown({type: "keydown", keyCode: 13, preventDefault: function(){}, stopPropagation: function(){}});
+ }
+ var fold = foldLines(cm, 0, 2);
+ enterPress();
+ eq(cm.getValue(), "xx\nxx");
+ fold.clear();
+ fold = foldLines(cm, 0, 2, true);
+ eq(fold.find(), null);
+ enterPress();
+ eq(cm.getValue(), "\nxx\nxx");
+});
+
+testCM("structuredFold", function(cm) {
+ if (phantom) return;
+ addDoc(cm, 4, 8);
+ var range = cm.markText(Pos(1, 2), Pos(6, 2), {
+ replacedWith: document.createTextNode("Q")
+ });
+ cm.setCursor(0, 3);
+ CodeMirror.commands.goLineDown(cm);
+ eqCharPos(cm.getCursor(), Pos(6, 2));
+ CodeMirror.commands.goCharLeft(cm);
+ eqCharPos(cm.getCursor(), Pos(1, 2));
+ CodeMirror.commands.delCharAfter(cm);
+ eq(cm.getValue(), "xxxx\nxxxx\nxxxx");
+ addDoc(cm, 4, 8);
+ range = cm.markText(Pos(1, 2), Pos(6, 2), {
+ replacedWith: document.createTextNode("M"),
+ clearOnEnter: true
+ });
+ var cleared = 0;
+ CodeMirror.on(range, "clear", function(){++cleared;});
+ cm.setCursor(0, 3);
+ CodeMirror.commands.goLineDown(cm);
+ eqCharPos(cm.getCursor(), Pos(6, 2));
+ CodeMirror.commands.goCharLeft(cm);
+ eqCharPos(cm.getCursor(), Pos(6, 1));
+ eq(cleared, 1);
+ range.clear();
+ eq(cleared, 1);
+ range = cm.markText(Pos(1, 2), Pos(6, 2), {
+ replacedWith: document.createTextNode("Q"),
+ clearOnEnter: true
+ });
+ range.clear();
+ cm.setCursor(1, 2);
+ CodeMirror.commands.goCharRight(cm);
+ eqCharPos(cm.getCursor(), Pos(1, 3));
+ range = cm.markText(Pos(2, 0), Pos(4, 4), {
+ replacedWith: document.createTextNode("M")
+ });
+ cm.setCursor(1, 0);
+ CodeMirror.commands.goLineDown(cm);
+ eqCharPos(cm.getCursor(), Pos(2, 0));
+}, null);
+
+testCM("nestedFold", function(cm) {
+ addDoc(cm, 10, 3);
+ function fold(ll, cl, lr, cr) {
+ return cm.markText(Pos(ll, cl), Pos(lr, cr), {collapsed: true});
+ }
+ var inner1 = fold(0, 6, 1, 3), inner2 = fold(0, 2, 1, 8), outer = fold(0, 1, 2, 3), inner0 = fold(0, 5, 0, 6);
+ cm.setCursor(0, 1);
+ CodeMirror.commands.goCharRight(cm);
+ eqCursorPos(cm.getCursor(), Pos(2, 3));
+ inner0.clear();
+ CodeMirror.commands.goCharLeft(cm);
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ outer.clear();
+ CodeMirror.commands.goCharRight(cm);
+ eqCursorPos(cm.getCursor(), Pos(0, 2, "before"));
+ CodeMirror.commands.goCharRight(cm);
+ eqCursorPos(cm.getCursor(), Pos(1, 8));
+ inner2.clear();
+ CodeMirror.commands.goCharLeft(cm);
+ eqCursorPos(cm.getCursor(), Pos(1, 7, "after"));
+ cm.setCursor(0, 5);
+ CodeMirror.commands.goCharRight(cm);
+ eqCursorPos(cm.getCursor(), Pos(0, 6, "before"));
+ CodeMirror.commands.goCharRight(cm);
+ eqCursorPos(cm.getCursor(), Pos(1, 3));
+});
+
+testCM("badNestedFold", function(cm) {
+ addDoc(cm, 4, 4);
+ cm.markText(Pos(0, 2), Pos(3, 2), {collapsed: true});
+ var caught;
+ try {cm.markText(Pos(0, 1), Pos(0, 3), {collapsed: true});}
+ catch(e) {caught = e;}
+ is(caught instanceof Error, "no error");
+ is(/overlap/i.test(caught.message), "wrong error");
+});
+
+testCM("nestedFoldOnSide", function(cm) {
+ var m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true, inclusiveRight: true});
+ var m2 = cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true});
+ cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true}).clear();
+ try { cm.markText(Pos(0, 1), Pos(0, 2), {collapsed: true, inclusiveLeft: true}); }
+ catch(e) { var caught = e; }
+ is(caught && /overlap/i.test(caught.message));
+ var m3 = cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true});
+ var m4 = cm.markText(Pos(2, 0), Pos(2, 1), {collapse: true, inclusiveRight: true});
+ m1.clear(); m4.clear();
+ m1 = cm.markText(Pos(0, 1), Pos(2, 1), {collapsed: true});
+ cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true}).clear();
+ try { cm.markText(Pos(2, 0), Pos(2, 1), {collapsed: true, inclusiveRight: true}); }
+ catch(e) { var caught = e; }
+ is(caught && /overlap/i.test(caught.message));
+}, {value: "ab\ncd\ef"});
+
+testCM("editInFold", function(cm) {
+ addDoc(cm, 4, 6);
+ var m = cm.markText(Pos(1, 2), Pos(3, 2), {collapsed: true});
+ cm.replaceRange("", Pos(0, 0), Pos(1, 3));
+ cm.replaceRange("", Pos(2, 1), Pos(3, 3));
+ cm.replaceRange("a\nb\nc\nd", Pos(0, 1), Pos(1, 0));
+ cm.cursorCoords(Pos(0, 0));
+});
+
+testCM("wrappingInlineWidget", function(cm) {
+ cm.setSize("11em");
+ var w = document.createElement("span");
+ w.style.color = "red";
+ w.innerHTML = "one two three four";
+ cm.markText(Pos(0, 6), Pos(0, 9), {replacedWith: w});
+ var cur0 = cm.cursorCoords(Pos(0, 0)), cur1 = cm.cursorCoords(Pos(0, 10));
+ is(cur0.top < cur1.top);
+ is(cur0.bottom < cur1.bottom);
+ var curL = cm.cursorCoords(Pos(0, 6)), curR = cm.cursorCoords(Pos(0, 9));
+ eq(curL.top, cur0.top);
+ eq(curL.bottom, cur0.bottom);
+ eq(curR.top, cur1.top);
+ eq(curR.bottom, cur1.bottom);
+ cm.replaceRange("", Pos(0, 9), Pos(0));
+ curR = cm.cursorCoords(Pos(0, 9));
+ if (phantom) return;
+ eq(curR.top, cur1.top);
+ eq(curR.bottom, cur1.bottom);
+}, {value: "1 2 3 xxx 4", lineWrapping: true});
+
+testCM("showEmptyWidgetSpan", function(cm) {
+ var marker = cm.markText(Pos(0, 2), Pos(0, 2), {
+ clearWhenEmpty: false,
+ replacedWith: document.createTextNode("X")
+ });
+ var text = cm.display.view[0].text;
+ eq(text.textContent || text.innerText, "abXc");
+}, {value: "abc"});
+
+testCM("changedInlineWidget", function(cm) {
+ cm.setSize("10em");
+ var w = document.createElement("span");
+ w.innerHTML = "x";
+ var m = cm.markText(Pos(0, 4), Pos(0, 5), {replacedWith: w});
+ w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed";
+ m.changed();
+ var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0];
+ is(hScroll.scrollWidth > hScroll.clientWidth);
+}, {value: "hello there"});
+
+testCM("changedBookmark", function(cm) {
+ cm.setSize("10em");
+ var w = document.createElement("span");
+ w.innerHTML = "x";
+ var m = cm.setBookmark(Pos(0, 4), {widget: w});
+ w.innerHTML = "and now the widget is really really long all of a sudden and a scrollbar is needed";
+ m.changed();
+ var hScroll = byClassName(cm.getWrapperElement(), "CodeMirror-hscrollbar")[0];
+ is(hScroll.scrollWidth > hScroll.clientWidth);
+}, {value: "abcdefg"});
+
+testCM("inlineWidget", function(cm) {
+ var w = cm.setBookmark(Pos(0, 2), {widget: document.createTextNode("uu")});
+ cm.setCursor(0, 2);
+ CodeMirror.commands.goLineDown(cm);
+ eqCharPos(cm.getCursor(), Pos(1, 4));
+ cm.setCursor(0, 2);
+ cm.replaceSelection("hi");
+ eqCharPos(w.find(), Pos(0, 2));
+ cm.setCursor(0, 1);
+ cm.replaceSelection("ay");
+ eqCharPos(w.find(), Pos(0, 4));
+ eq(cm.getLine(0), "uayuhiuu");
+}, {value: "uuuu\nuuuuuu"});
+
+testCM("wrappingAndResizing", function(cm) {
+ cm.setSize(null, "auto");
+ cm.setOption("lineWrapping", true);
+ var wrap = cm.getWrapperElement(), h0 = wrap.offsetHeight;
+ var doc = "xxx xxx xxx xxx xxx";
+ cm.setValue(doc);
+ for (var step = 10, w = cm.charCoords(Pos(0, 18), "div").right;; w += step) {
+ cm.setSize(w);
+ if (wrap.offsetHeight <= h0 * (opera_lt10 ? 1.2 : 1.5)) {
+ if (step == 10) { w -= 10; step = 1; }
+ else break;
+ }
+ }
+ // Ensure that putting the cursor at the end of the maximally long
+ // line doesn't cause wrapping to happen.
+ cm.setCursor(Pos(0, doc.length));
+ eq(wrap.offsetHeight, h0);
+ cm.replaceSelection("x");
+ is(wrap.offsetHeight > h0, "wrapping happens");
+ // Now add a max-height and, in a document consisting of
+ // almost-wrapped lines, go over it so that a scrollbar appears.
+ cm.setValue(doc + "\n" + doc + "\n");
+ cm.getScrollerElement().style.maxHeight = "100px";
+ cm.replaceRange("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n!\n", Pos(2, 0));
+ forEach([Pos(0, doc.length), Pos(0, doc.length - 1),
+ Pos(0, 0), Pos(1, doc.length), Pos(1, doc.length - 1)],
+ function(pos) {
+ var coords = cm.charCoords(pos);
+ eqCharPos(pos, cm.coordsChar({left: coords.left + 2, top: coords.top + 5}));
+ });
+}, null, ie_lt8);
+
+testCM("measureEndOfLine", function(cm) {
+ if (phantom) return;
+ cm.setSize(null, "auto");
+ var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild;
+ var lh = inner.offsetHeight;
+ for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) {
+ cm.setSize(w);
+ if (inner.offsetHeight < 2.5 * lh) {
+ if (step == 10) { w -= 10; step = 1; }
+ else break;
+ }
+ }
+ cm.setValue(cm.getValue() + "\n\n");
+ var endPos = cm.charCoords(Pos(0, 18), "local");
+ is(endPos.top > lh * .8, "not at top");
+ is(endPos.left > w - 20, "at right");
+ endPos = cm.charCoords(Pos(0, 18));
+ eqCursorPos(cm.coordsChar({left: endPos.left, top: endPos.top + 5}), Pos(0, 18, "before"));
+
+ var wrapPos = cm.cursorCoords(Pos(0, 9, "before"));
+ is(wrapPos.top < endPos.top, "wrapPos is actually in first line");
+ eqCursorPos(cm.coordsChar({left: wrapPos.left + 10, top: wrapPos.top}), Pos(0, 9, "before"));
+}, {mode: "text/html", value: "<!-- foo barrr -->", lineWrapping: true}, ie_lt8 || opera_lt10);
+
+testCM("measureWrappedEndOfLine", function(cm) {
+ if (phantom) return;
+ cm.setSize(null, "auto");
+ var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild;
+ var lh = inner.offsetHeight;
+ for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) {
+ cm.setSize(w);
+ if (inner.offsetHeight < 2.5 * lh) {
+ if (step == 10) { w -= 10; step = 1; }
+ else break;
+ }
+ }
+ for (var i = 0; i < 3; ++i) {
+ var endPos = cm.charCoords(Pos(0, 12)); // Next-to-last since last would wrap (#1862)
+ endPos.left += w; // Add width of editor just to be sure that we are behind last character
+ eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before"));
+ endPos.left += w * 100;
+ eqCursorPos(cm.coordsChar(endPos), Pos(0, 13, "before"));
+ cm.setValue("0123456789abcابجابجابجابج");
+ if (i == 1) {
+ var node = document.createElement("div");
+ node.innerHTML = "hi"; node.style.height = "30px";
+ cm.addLineWidget(0, node, {above: true});
+ }
+ }
+}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true}, ie_lt8 || opera_lt10);
+
+testCM("measureEndOfLineBidi", function(cm) {
+ eqCursorPos(cm.coordsChar({left: 5000, top: cm.charCoords(Pos(0, 0)).top}), Pos(0, 8, "after"))
+}, {value: "إإإإuuuuإإإإ"})
+
+testCM("measureWrappedBidiLevel2", function(cm) {
+ cm.setSize(cm.charCoords(Pos(0, 6), "editor").right + 60)
+ var c9 = cm.charCoords(Pos(0, 9))
+ eqCharPos(cm.coordsChar({left: c9.right - 1, top: c9.top + 1}), Pos(0, 9))
+}, {value: "foobar إإ إإ إإ إإ 555 بببببب", lineWrapping: true})
+
+testCM("measureWrappedBeginOfLine", function(cm) {
+ if (phantom) return;
+ cm.setSize(null, "auto");
+ var inner = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild;
+ var lh = inner.offsetHeight;
+ for (var step = 10, w = cm.charCoords(Pos(0, 7), "div").right;; w += step) {
+ cm.setSize(w);
+ if (inner.offsetHeight < 2.5 * lh) {
+ if (step == 10) { w -= 10; step = 1; }
+ else break;
+ }
+ }
+ var beginOfSecondLine = Pos(0, 13, "after");
+ for (var i = 0; i < 2; ++i) {
+ var beginPos = cm.charCoords(Pos(0, 0));
+ beginPos.left -= w;
+ eqCursorPos(cm.coordsChar(beginPos), Pos(0, 0, "after"));
+ beginPos = cm.cursorCoords(beginOfSecondLine);
+ beginPos.left = 0;
+ eqCursorPos(cm.coordsChar(beginPos), beginOfSecondLine);
+ cm.setValue("0123456789abcابجابجابجابج");
+ beginOfSecondLine = Pos(0, 25, "before");
+ }
+}, {mode: "text/html", value: "0123456789abcde0123456789", lineWrapping: true});
+
+testCM("scrollVerticallyAndHorizontally", function(cm) {
+ if (cm.getOption("inputStyle") != "textarea") return;
+ cm.setSize(100, 100);
+ addDoc(cm, 40, 40);
+ cm.setCursor(39);
+ var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-vscrollbar")[0];
+ is(bar.offsetHeight < wrap.offsetHeight, "vertical scrollbar limited by horizontal one");
+ var cursorBox = byClassName(wrap, "CodeMirror-cursor")[0].getBoundingClientRect();
+ var editorBox = wrap.getBoundingClientRect();
+ is(cursorBox.bottom < editorBox.top + cm.getScrollerElement().clientHeight,
+ "bottom line visible");
+}, {lineNumbers: true});
+
+testCM("moveVstuck", function(cm) {
+ var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight;
+ var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n";
+ cm.setValue(val);
+ for (var w = cm.charCoords(Pos(0, 26), "div").right * 2.8;; w += 5) {
+ cm.setSize(w);
+ if (lines.offsetHeight <= 3.5 * h0) break;
+ }
+ cm.setCursor(Pos(0, val.length - 1));
+ cm.moveV(-1, "line");
+ eqCursorPos(cm.getCursor(), Pos(0, 27, "before"));
+ is(cm.cursorCoords(null, "local").top < h0, "cursor is in first visual line");
+}, {lineWrapping: true}, ie_lt8 || opera_lt10);
+
+testCM("collapseOnMove", function(cm) {
+ cm.setSelection(Pos(0, 1), Pos(2, 4));
+ cm.execCommand("goLineUp");
+ is(!cm.somethingSelected());
+ eqCharPos(cm.getCursor(), Pos(0, 1));
+ cm.setSelection(Pos(0, 1), Pos(2, 4));
+ cm.execCommand("goPageDown");
+ is(!cm.somethingSelected());
+ eqCharPos(cm.getCursor(), Pos(2, 4));
+ cm.execCommand("goLineUp");
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(0, 4));
+ cm.setSelection(Pos(0, 1), Pos(2, 4));
+ cm.execCommand("goCharLeft");
+ is(!cm.somethingSelected());
+ eqCharPos(cm.getCursor(), Pos(0, 1));
+}, {value: "aaaaa\nb\nccccc"});
+
+testCM("clickTab", function(cm) {
+ var p0 = cm.charCoords(Pos(0, 0));
+ eqCharPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0));
+ eqCharPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), Pos(0, 1));
+}, {value: "\t\n\n", lineWrapping: true, tabSize: 8});
+
+testCM("verticalScroll", function(cm) {
+ cm.setSize(100, 200);
+ cm.setValue("foo\nbar\nbaz\n");
+ var sc = cm.getScrollerElement(), baseWidth = sc.scrollWidth;
+ cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0));
+ is(sc.scrollWidth > baseWidth, "scrollbar present");
+ cm.replaceRange("foo", Pos(0, 0), Pos(0));
+ if (!phantom) eq(sc.scrollWidth, baseWidth, "scrollbar gone");
+ cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0));
+ cm.replaceRange("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh", Pos(1, 0), Pos(1));
+ is(sc.scrollWidth > baseWidth, "present again");
+ var curWidth = sc.scrollWidth;
+ cm.replaceRange("foo", Pos(0, 0), Pos(0));
+ is(sc.scrollWidth < curWidth, "scrollbar smaller");
+ is(sc.scrollWidth > baseWidth, "but still present");
+});
+
+testCM("extraKeys", function(cm) {
+ var outcome;
+ function fakeKey(expected, code, props) {
+ if (typeof code == "string") code = code.charCodeAt(0);
+ var e = {type: "keydown", keyCode: code, preventDefault: function(){}, stopPropagation: function(){}};
+ if (props) for (var n in props) e[n] = props[n];
+ outcome = null;
+ cm.triggerOnKeyDown(e);
+ eq(outcome, expected);
+ }
+ CodeMirror.commands.testCommand = function() {outcome = "tc";};
+ CodeMirror.commands.goTestCommand = function() {outcome = "gtc";};
+ cm.setOption("extraKeys", {"Shift-X": function() {outcome = "sx";},
+ "X": function() {outcome = "x";},
+ "Ctrl-Alt-U": function() {outcome = "cau";},
+ "End": "testCommand",
+ "Home": "goTestCommand",
+ "Tab": false});
+ fakeKey(null, "U");
+ fakeKey("cau", "U", {ctrlKey: true, altKey: true});
+ fakeKey(null, "U", {shiftKey: true, ctrlKey: true, altKey: true});
+ fakeKey("x", "X");
+ fakeKey("sx", "X", {shiftKey: true});
+ fakeKey("tc", 35);
+ fakeKey(null, 35, {shiftKey: true});
+ fakeKey("gtc", 36);
+ fakeKey("gtc", 36, {shiftKey: true});
+ fakeKey(null, 9);
+}, null, window.opera && mac);
+
+testCM("wordMovementCommands", function(cm) {
+ cm.execCommand("goWordLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 7, "before"));
+ cm.execCommand("goWordLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 5, "after"));
+ cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 12, "before"));
+ cm.execCommand("goWordLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 9, "after"));
+ cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 24, "before"));
+ cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 9, "before"));
+ cm.execCommand("goWordRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 13, "before"));
+ cm.execCommand("goWordRight"); cm.execCommand("goWordRight");
+ eqCharPos(cm.getCursor(), Pos(2, 0));
+}, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"});
+
+testCM("groupMovementCommands", function(cm) {
+ cm.execCommand("goGroupLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 4, "before"));
+ cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 7, "before"));
+ cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 10, "before"));
+ cm.execCommand("goGroupLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 7, "after"));
+ cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 15, "before"));
+ cm.setCursor(Pos(0, 17));
+ cm.execCommand("goGroupLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 16, "after"));
+ cm.execCommand("goGroupLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 14, "after"));
+ cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 20, "before"));
+ cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 0, "after"));
+ cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 2, "before"));
+ cm.execCommand("goGroupRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 5, "before"));
+ cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft");
+ eqCursorPos(cm.getCursor(), Pos(1, 0, "after"));
+ cm.execCommand("goGroupLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 20, "after"));
+ cm.execCommand("goGroupLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 16, "after"));
+}, {value: "booo ba---quux. ffff\n abc d"});
+
+testCM("groupsAndWhitespace", function(cm) {
+ var positions = [Pos(0, 0), Pos(0, 2), Pos(0, 5), Pos(0, 9), Pos(0, 11),
+ Pos(1, 0), Pos(1, 2), Pos(1, 5)];
+ for (var i = 1; i < positions.length; i++) {
+ cm.execCommand("goGroupRight");
+ eqCharPos(cm.getCursor(), positions[i]);
+ }
+ for (var i = positions.length - 2; i >= 0; i--) {
+ cm.execCommand("goGroupLeft");
+ eqCharPos(cm.getCursor(), i == 2 ? Pos(0, 6, "before") : positions[i]);
+ }
+}, {value: " foo +++ \n bar"});
+
+testCM("charMovementCommands", function(cm) {
+ cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goCharRight"); cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 2, "before"));
+ cm.setCursor(Pos(1, 0));
+ cm.execCommand("goColumnLeft");
+ eqCursorPos(cm.getCursor(), Pos(1, 0));
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 5, "before"));
+ cm.execCommand("goColumnRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 5, "before"));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 0, "after"));
+ cm.execCommand("goLineEnd");
+ eqCursorPos(cm.getCursor(), Pos(1, 5, "before"));
+ cm.execCommand("goLineStartSmart");
+ eqCursorPos(cm.getCursor(), Pos(1, 1, "after"));
+ cm.execCommand("goLineStartSmart");
+ eqCursorPos(cm.getCursor(), Pos(1, 0, "after"));
+ cm.setCursor(Pos(2, 0));
+ cm.execCommand("goCharRight"); cm.execCommand("goColumnRight");
+ eqCursorPos(cm.getCursor(), Pos(2, 0));
+}, {value: "line1\n ine2\n"});
+
+testCM("verticalMovementCommands", function(cm) {
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goLineDown");
+ if (!phantom) // This fails in PhantomJS, though not in a real Webkit
+ eqCharPos(cm.getCursor(), Pos(1, 0));
+ cm.setCursor(Pos(1, 12));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(2, 5));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(3, 0));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(2, 5));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(1, 12));
+ cm.execCommand("goPageDown");
+ eqCharPos(cm.getCursor(), Pos(5, 0));
+ cm.execCommand("goPageDown"); cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(5, 0));
+ cm.execCommand("goPageUp");
+ eqCharPos(cm.getCursor(), Pos(0, 0));
+}, {value: "line1\nlong long line2\nline3\n\nline5\n"});
+
+testCM("verticalMovementCommandsWrapping", function(cm) {
+ cm.setSize(120);
+ cm.setCursor(Pos(0, 5));
+ cm.execCommand("goLineDown");
+ eq(cm.getCursor().line, 0);
+ is(cm.getCursor().ch > 5, "moved beyond wrap");
+ for (var i = 0; ; ++i) {
+ is(i < 20, "no endless loop");
+ cm.execCommand("goLineDown");
+ var cur = cm.getCursor();
+ if (cur.line == 1) eq(cur.ch, 5);
+ if (cur.line == 2) { eq(cur.ch, 1); break; }
+ }
+}, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk",
+ lineWrapping: true});
+
+testCM("verticalMovementCommandsSingleLine", function(cm) {
+ cm.display.wrapper.style.height = "auto";
+ cm.refresh();
+ cm.execCommand("goLineUp");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(0, 11));
+ cm.setCursor(Pos(0, 5));
+ cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goLineUp");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goLineUp");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goPageDown");
+ eqCursorPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goPageDown"); cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(0, 11));
+ cm.execCommand("goPageUp");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.setCursor(Pos(0, 5));
+ cm.execCommand("goPageUp");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.setCursor(Pos(0, 5));
+ cm.execCommand("goPageDown");
+ eqCursorPos(cm.getCursor(), Pos(0, 11));
+}, {value: "single line"});
+
+
+testCM("rtlMovement", function(cm) {
+ if (cm.getOption("inputStyle") != "textarea") return;
+ forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج",
+ "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق",
+ "<img src=\"/בדיקה3.jpg\">", "يتم السحب في 05 فبراير 2014"], function(line) {
+ cm.setValue(line + "\n"); cm.execCommand("goLineStart");
+ var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0];
+ var cursor = cursors.firstChild;
+ var prevX = cursor.offsetLeft, prevY = cursor.offsetTop;
+ for (var i = 0; i <= line.length; ++i) {
+ cm.execCommand("goCharRight");
+ cursor = cursors.firstChild;
+ if (i == line.length) is(cursor.offsetTop > prevY, "next line");
+ else is(cursor.offsetLeft > prevX, "moved right");
+ prevX = cursor.offsetLeft; prevY = cursor.offsetTop;
+ }
+ cm.setCursor(0, 0); cm.execCommand("goLineEnd");
+ prevX = cursors.firstChild.offsetLeft;
+ for (var i = 0; i < line.length; ++i) {
+ cm.execCommand("goCharLeft");
+ cursor = cursors.firstChild;
+ is(cursor.offsetLeft < prevX, "moved left");
+ prevX = cursor.offsetLeft;
+ }
+ });
+}, null, ie_lt9);
+
+// Verify that updating a line clears its bidi ordering
+testCM("bidiUpdate", function(cm) {
+ cm.setCursor(Pos(0, 2, "before"));
+ cm.replaceSelection("خحج", "start");
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 6, "before"));
+}, {value: "abcd\n"});
+
+testCM("movebyTextUnit", function(cm) {
+ cm.setValue("בְּרֵאשִ\nééé́\n");
+ cm.execCommand("goLineStart");
+ for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 0, "after"));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 0, "after"));
+ cm.execCommand("goCharRight");
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 4, "before"));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(1, 7, "before"));
+});
+
+testCM("lineChangeEvents", function(cm) {
+ addDoc(cm, 3, 5);
+ var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"];
+ for (var i = 0; i < 5; ++i) {
+ CodeMirror.on(cm.getLineHandle(i), "delete", function(i) {
+ return function() {log.push("del " + i);};
+ }(i));
+ CodeMirror.on(cm.getLineHandle(i), "change", function(i) {
+ return function() {log.push("ch " + i);};
+ }(i));
+ }
+ cm.replaceRange("x", Pos(0, 1));
+ cm.replaceRange("xy", Pos(1, 1), Pos(2));
+ cm.replaceRange("foo\nbar", Pos(0, 1));
+ cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount()));
+ eq(log.length, want.length, "same length");
+ for (var i = 0; i < log.length; ++i)
+ eq(log[i], want[i]);
+});
+
+testCM("scrollEntirelyToRight", function(cm) {
+ if (phantom || cm.getOption("inputStyle") != "textarea") return;
+ addDoc(cm, 500, 2);
+ cm.setCursor(Pos(0, 500));
+ var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0];
+ is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left);
+});
+
+testCM("lineWidgets", function(cm) {
+ addDoc(cm, 500, 3);
+ var last = cm.charCoords(Pos(2, 0));
+ var node = document.createElement("div");
+ node.innerHTML = "hi";
+ var widget = cm.addLineWidget(1, node);
+ is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space");
+ cm.setCursor(Pos(1, 1));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(2, 1));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(1, 1));
+});
+
+testCM("lineWidgetFocus", function(cm) {
+ var place = document.getElementById("testground");
+ place.className = "offscreen";
+ try {
+ addDoc(cm, 500, 10);
+ var node = document.createElement("input");
+ var widget = cm.addLineWidget(1, node);
+ node.focus();
+ eq(document.activeElement, node);
+ cm.replaceRange("new stuff", Pos(1, 0));
+ eq(document.activeElement, node);
+ } finally {
+ place.className = "";
+ }
+});
+
+testCM("lineWidgetCautiousRedraw", function(cm) {
+ var node = document.createElement("div");
+ node.innerHTML = "hahah";
+ var w = cm.addLineWidget(0, node);
+ var redrawn = false;
+ w.on("redraw", function() { redrawn = true; });
+ cm.replaceSelection("0");
+ is(!redrawn);
+}, {value: "123\n456"});
+
+
+var knownScrollbarWidth;
+function scrollbarWidth(measure) {
+ if (knownScrollbarWidth != null) return knownScrollbarWidth;
+ var div = document.createElement('div');
+ div.style.cssText = "width: 50px; height: 50px; overflow-x: scroll";
+ document.body.appendChild(div);
+ knownScrollbarWidth = div.offsetHeight - div.clientHeight;
+ document.body.removeChild(div);
+ return knownScrollbarWidth || 0;
+}
+
+testCM("lineWidgetChanged", function(cm) {
+ addDoc(cm, 2, 300);
+ var halfScrollbarWidth = scrollbarWidth(cm.display.measure)/2;
+ cm.setOption('lineNumbers', true);
+ cm.setSize(600, cm.defaultTextHeight() * 50);
+ cm.scrollTo(null, cm.heightAtLine(125, "local"));
+
+ var expectedWidgetHeight = 60;
+ var expectedLinesInWidget = 3;
+ function w() {
+ var node = document.createElement("div");
+ // we use these children with just under half width of the line to check measurements are made with correct width
+ // when placed in the measure div.
+ // If the widget is measured at a width much narrower than it is displayed at, the underHalf children will span two lines and break the test.
+ // If the widget is measured at a width much wider than it is displayed at, the overHalf children will combine and break the test.
+ // Note that this test only checks widgets where coverGutter is true, because these require extra styling to get the width right.
+ // It may also be worthwhile to check this for non-coverGutter widgets.
+ // Visually:
+ // Good:
+ // | ------------- display width ------------- |
+ // | ------- widget-width when measured ------ |
+ // | | -- under-half -- | | -- under-half -- | |
+ // | | --- over-half --- | |
+ // | | --- over-half --- | |
+ // Height: measured as 3 lines, same as it will be when actually displayed
+
+ // Bad (too narrow):
+ // | ------------- display width ------------- |
+ // | ------ widget-width when measured ----- | < -- uh oh
+ // | | -- under-half -- | |
+ // | | -- under-half -- | | < -- when measured, shoved to next line
+ // | | --- over-half --- | |
+ // | | --- over-half --- | |
+ // Height: measured as 4 lines, more than expected . Will be displayed as 3 lines!
+
+ // Bad (too wide):
+ // | ------------- display width ------------- |
+ // | -------- widget-width when measured ------- | < -- uh oh
+ // | | -- under-half -- | | -- under-half -- | |
+ // | | --- over-half --- | | --- over-half --- | | < -- when measured, combined on one line
+ // Height: measured as 2 lines, less than expected. Will be displayed as 3 lines!
+
+ var barelyUnderHalfWidthHtml = '<div style="display: inline-block; height: 1px; width: '+(285 - halfScrollbarWidth)+'px;"></div>';
+ var barelyOverHalfWidthHtml = '<div style="display: inline-block; height: 1px; width: '+(305 - halfScrollbarWidth)+'px;"></div>';
+ node.innerHTML = new Array(3).join(barelyUnderHalfWidthHtml) + new Array(3).join(barelyOverHalfWidthHtml);
+ node.style.cssText = "background: yellow;font-size:0;line-height: " + (expectedWidgetHeight/expectedLinesInWidget) + "px;";
+ return node;
+ }
+ var info0 = cm.getScrollInfo();
+ var w0 = cm.addLineWidget(0, w(), { coverGutter: true });
+ var w150 = cm.addLineWidget(150, w(), { coverGutter: true });
+ var w300 = cm.addLineWidget(300, w(), { coverGutter: true });
+ var info1 = cm.getScrollInfo();
+ eq(info0.height + (3 * expectedWidgetHeight), info1.height);
+ eq(info0.top + expectedWidgetHeight, info1.top);
+ expectedWidgetHeight = 12;
+ w0.node.style.lineHeight = w150.node.style.lineHeight = w300.node.style.lineHeight = (expectedWidgetHeight/expectedLinesInWidget) + "px";
+ w0.changed(); w150.changed(); w300.changed();
+ var info2 = cm.getScrollInfo();
+ eq(info0.height + (3 * expectedWidgetHeight), info2.height);
+ eq(info0.top + expectedWidgetHeight, info2.top);
+});
+
+testCM("lineWidgetIssue5486", function(cm) {
+ // [prepare]
+ // 2nd line is combined to 1st line due to markText
+ // 2nd line has a lineWidget below
+
+ cm.setValue("Lorem\nIpsue\nDollar")
+
+ var el = document.createElement('div')
+ el.style.height='50px'
+ el.textContent = '[[LINE WIDGET]]'
+
+ var lineWidget = cm.addLineWidget(1, el, {
+ above: false,
+ coverGutter: false,
+ noHScroll: false,
+ showIfHidden: false,
+ })
+
+ var marker = document.createElement('span')
+ marker.textContent = '[--]'
+
+ cm.markText({line:0, ch: 1}, {line:1, ch: 4}, {
+ replacedWith: marker
+ })
+
+ // before resizing the lineWidget, measure 3rd line position
+
+ var measure_1 = Math.round(cm.charCoords({line:2, ch:0}).top)
+
+ // resize lineWidget, height + 50 px
+
+ el.style.height='100px'
+ el.textContent += "\nlineWidget size changed.\nTry moving cursor to line 3?"
+
+ lineWidget.changed()
+
+ // re-measure 3rd line position
+ var measure_2 = Math.round(cm.charCoords({line:2, ch:0}).top)
+ eq(measure_2, measure_1 + 50)
+
+ // (extra test)
+ //
+ // add char to the right of the folded marker
+ // and re-measure 3rd line position
+
+ cm.replaceRange('-', {line:1, ch: 5})
+ var measure_3 = Math.round(cm.charCoords({line:2, ch:0}).top)
+ eq(measure_3, measure_2)
+});
+
+testCM("getLineNumber", function(cm) {
+ addDoc(cm, 2, 20);
+ var h1 = cm.getLineHandle(1);
+ eq(cm.getLineNumber(h1), 1);
+ cm.replaceRange("hi\nbye\n", Pos(0, 0));
+ eq(cm.getLineNumber(h1), 3);
+ cm.setValue("");
+ eq(cm.getLineNumber(h1), null);
+});
+
+testCM("jumpTheGap", function(cm) {
+ if (phantom) return;
+ var longLine = "abcdef ghiklmnop qrstuvw xyz ";
+ longLine += longLine; longLine += longLine; longLine += longLine;
+ cm.replaceRange(longLine, Pos(2, 0), Pos(2));
+ cm.setSize("200px", null);
+ cm.getWrapperElement().style.lineHeight = 2;
+ cm.refresh();
+ cm.setCursor(Pos(0, 1));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(2, 1));
+ cm.execCommand("goLineDown");
+ eq(cm.getCursor().line, 2);
+ is(cm.getCursor().ch > 1);
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(2, 1));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(1, 1));
+ var node = document.createElement("div");
+ node.innerHTML = "hi"; node.style.height = "30px";
+ cm.addLineWidget(0, node);
+ cm.addLineWidget(1, node.cloneNode(true), {above: true});
+ cm.setCursor(Pos(0, 2));
+ cm.execCommand("goLineDown");
+ eqCharPos(cm.getCursor(), Pos(1, 2));
+ cm.execCommand("goLineUp");
+ eqCharPos(cm.getCursor(), Pos(0, 2));
+}, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"});
+
+testCM("addLineClass", function(cm) {
+ function cls(line, text, bg, wrap, gutter) {
+ var i = cm.lineInfo(line);
+ eq(i.textClass, text);
+ eq(i.bgClass, bg);
+ eq(i.wrapClass, wrap);
+ if (typeof i.handle.gutterClass !== 'undefined') {
+ eq(i.handle.gutterClass, gutter);
+ }
+ }
+ cm.addLineClass(0, "text", "foo");
+ cm.addLineClass(0, "text", "bar");
+ cm.addLineClass(1, "background", "baz");
+ cm.addLineClass(1, "wrap", "foo");
+ cm.addLineClass(1, "gutter", "gutter-class");
+ cls(0, "foo bar", null, null, null);
+ cls(1, null, "baz", "foo", "gutter-class");
+ var lines = cm.display.lineDiv;
+ eq(byClassName(lines, "foo").length, 2);
+ eq(byClassName(lines, "bar").length, 1);
+ eq(byClassName(lines, "baz").length, 1);
+ eq(byClassName(lines, "gutter-class").length, 2); // Gutter classes are reflected in 2 nodes
+ cm.removeLineClass(0, "text", "foo");
+ cls(0, "bar", null, null, null);
+ cm.removeLineClass(0, "text", "foo");
+ cls(0, "bar", null, null, null);
+ cm.removeLineClass(0, "text", "bar");
+ cls(0, null, null, null);
+
+ cm.addLineClass(1, "wrap", "quux");
+ cls(1, null, "baz", "foo quux", "gutter-class");
+ cm.removeLineClass(1, "wrap");
+ cls(1, null, "baz", null, "gutter-class");
+ cm.removeLineClass(1, "gutter", "gutter-class");
+ eq(byClassName(lines, "gutter-class").length, 0);
+ cls(1, null, "baz", null, null);
+
+ cm.addLineClass(1, "gutter", "gutter-class");
+ cls(1, null, "baz", null, "gutter-class");
+ cm.removeLineClass(1, "gutter", "gutter-class");
+ cls(1, null, "baz", null, null);
+
+}, {value: "hohoho\n", lineNumbers: true});
+
+testCM("atomicMarker", function(cm) {
+ addDoc(cm, 10, 10);
+
+ function atom(ll, cl, lr, cr, li, ri, ls, rs) {
+ var options = {
+ atomic: true,
+ inclusiveLeft: li,
+ inclusiveRight: ri
+ };
+
+ if (ls === true || ls === false) options["selectLeft"] = ls;
+ if (rs === true || rs === false) options["selectRight"] = rs;
+
+ return cm.markText(Pos(ll, cl), Pos(lr, cr), options);
+ }
+
+ // Can cursor to the left and right of a normal marker by jumping across it
+ var m = atom(0, 1, 0, 5);
+ cm.setCursor(Pos(0, 1));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 5));
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ m.clear();
+
+ // Can't cursor to the left of a marker when inclusiveLeft=true
+ m = atom(0, 0, 0, 5, true);
+ eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 5));
+ m.clear();
+
+ // Can't cursor to the left of a marker when inclusiveLeft=false and selectLeft=false
+ m = atom(0, 0, 0, 5, false, false, false);
+ cm.setCursor(Pos(0, 5));
+ eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 5));
+ m.clear();
+
+ // Can cursor to the left of a marker when inclusiveLeft=false and selectLeft=True
+ m = atom(0, 0, 0, 5, false, false, true);
+ cm.setCursor(Pos(0, 5));
+ eqCursorPos(cm.getCursor(), Pos(0, 5), "pushed out");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ m.clear();
+
+ // Can't cursor to the right of a marker when inclusiveRight=true
+ m = atom(0, 0, 0, 5, false, true);
+ cm.setCursor(Pos(0, 0));
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 6));
+ m.clear();
+
+ // Can't cursor to the right of a marker when inclusiveRight=false and selectRight=false
+ m = atom(0, 0, 0, 5, false, false, true, false);
+ cm.setCursor(Pos(0, 0));
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 6));
+ m.clear();
+
+ // Can cursor to the right of a marker when inclusiveRight=false and selectRight=True
+ m = atom(0, 0, 0, 5, false, false, true, true);
+ cm.setCursor(Pos(0, 0));
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(0, 5));
+ m.clear();
+
+ // Can't cursor to the right of a multiline marker when inclusiveRight=true
+ m = atom(8, 4, 9, 10, false, true);
+ cm.setCursor(Pos(9, 8));
+ eqCursorPos(cm.getCursor(), Pos(8, 4), "set");
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(8, 4), "char right");
+ cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(8, 4), "line down");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(8, 3, "after"));
+ m.clear();
+
+ // Cursor jumps across a multiline atomic marker,
+ // and backspace deletes the entire marker
+ m = atom(1, 1, 3, 8);
+ cm.setCursor(Pos(0, 0));
+ cm.setCursor(Pos(2, 0));
+ eqCursorPos(cm.getCursor(), Pos(3, 8));
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goCharRight");
+ eqCursorPos(cm.getCursor(), Pos(3, 8));
+ cm.execCommand("goLineUp");
+ eqCursorPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goLineDown");
+ eqCursorPos(cm.getCursor(), Pos(3, 8));
+ cm.execCommand("delCharBefore");
+ eq(cm.getValue().length, 80, "del chunk");
+ m.clear();
+ addDoc(cm, 10, 10);
+
+ // Delete before an atomic marker deletes the entire marker
+ m = atom(3, 0, 5, 5);
+ cm.setCursor(Pos(3, 0));
+ cm.execCommand("delWordAfter");
+ eq(cm.getValue().length, 82, "del chunk");
+ m.clear();
+ addDoc(cm, 10, 10);
+});
+
+testCM("selectionBias", function(cm) {
+ cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true});
+ cm.setCursor(Pos(0, 2));
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ cm.setCursor(Pos(0, 2));
+ eqCursorPos(cm.getCursor(), Pos(0, 3));
+ cm.setCursor(Pos(0, 2));
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ cm.setCursor(Pos(0, 2), null, {bias: -1});
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ cm.setCursor(Pos(0, 4));
+ cm.setCursor(Pos(0, 2), null, {bias: 1});
+ eqCursorPos(cm.getCursor(), Pos(0, 3));
+}, {value: "12345"});
+
+testCM("selectionHomeEnd", function(cm) {
+ cm.markText(Pos(1, 0), Pos(1, 1), {atomic: true, inclusiveLeft: true});
+ cm.markText(Pos(1, 3), Pos(1, 4), {atomic: true, inclusiveRight: true});
+ cm.setCursor(Pos(1, 2));
+ cm.execCommand("goLineStart");
+ eqCursorPos(cm.getCursor(), Pos(1, 1));
+ cm.execCommand("goLineEnd");
+ eqCursorPos(cm.getCursor(), Pos(1, 3));
+}, {value: "ab\ncdef\ngh"});
+
+testCM("readOnlyMarker", function(cm) {
+ function mark(ll, cl, lr, cr, at) {
+ return cm.markText(Pos(ll, cl), Pos(lr, cr),
+ {readOnly: true, atomic: at});
+ }
+ var m = mark(0, 1, 0, 4);
+ cm.setCursor(Pos(0, 2));
+ cm.replaceSelection("hi", "end");
+ eqCursorPos(cm.getCursor(), Pos(0, 2));
+ eq(cm.getLine(0), "abcde");
+ cm.execCommand("selectAll");
+ cm.replaceSelection("oops", "around");
+ eq(cm.getValue(), "oopsbcd");
+ cm.undo();
+ eqCursorPos(m.find().from, Pos(0, 1));
+ eqCursorPos(m.find().to, Pos(0, 4));
+ m.clear();
+ cm.setCursor(Pos(0, 2));
+ cm.replaceSelection("hi", "around");
+ eq(cm.getLine(0), "abhicde");
+ eqCursorPos(cm.getCursor(), Pos(0, 4));
+ m = mark(0, 2, 2, 2, true);
+ cm.setSelection(Pos(1, 1), Pos(2, 4));
+ cm.replaceSelection("t", "end");
+ eqCursorPos(cm.getCursor(), Pos(2, 3));
+ eq(cm.getLine(2), "klto");
+ cm.execCommand("goCharLeft");
+ cm.execCommand("goCharLeft");
+ eqCursorPos(cm.getCursor(), Pos(0, 2));
+ cm.setSelection(Pos(0, 1), Pos(0, 3));
+ cm.replaceSelection("xx", "around");
+ eqCursorPos(cm.getCursor(), Pos(0, 3));
+ eq(cm.getLine(0), "axxhicde");
+}, {value: "abcde\nfghij\nklmno\n"});
+
+testCM("dirtyBit", function(cm) {
+ eq(cm.isClean(), true);
+ cm.replaceSelection("boo", null, "test");
+ eq(cm.isClean(), false);
+ cm.undo();
+ eq(cm.isClean(), true);
+ cm.replaceSelection("boo", null, "test");
+ cm.replaceSelection("baz", null, "test");
+ cm.undo();
+ eq(cm.isClean(), false);
+ cm.markClean();
+ eq(cm.isClean(), true);
+ cm.undo();
+ eq(cm.isClean(), false);
+ cm.redo();
+ eq(cm.isClean(), true);
+});
+
+testCM("changeGeneration", function(cm) {
+ cm.replaceSelection("x");
+ var softGen = cm.changeGeneration();
+ cm.replaceSelection("x");
+ cm.undo();
+ eq(cm.getValue(), "");
+ is(!cm.isClean(softGen));
+ cm.replaceSelection("x");
+ var hardGen = cm.changeGeneration(true);
+ cm.replaceSelection("x");
+ cm.undo();
+ eq(cm.getValue(), "x");
+ is(cm.isClean(hardGen));
+});
+
+testCM("addKeyMap", function(cm) {
+ function sendKey(code) {
+ cm.triggerOnKeyDown({type: "keydown", keyCode: code,
+ preventDefault: function(){}, stopPropagation: function(){}});
+ }
+
+ sendKey(39);
+ eqCursorPos(cm.getCursor(), Pos(0, 1, "before"));
+ var test = 0;
+ var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }}
+ cm.addKeyMap(map1);
+ sendKey(39);
+ eqCursorPos(cm.getCursor(), Pos(0, 1, "before"));
+ eq(test, 1);
+ cm.addKeyMap(map2, true);
+ sendKey(39);
+ eq(test, 2);
+ cm.removeKeyMap(map1);
+ sendKey(39);
+ eq(test, 12);
+ cm.removeKeyMap(map2);
+ sendKey(39);
+ eq(test, 12);
+ eqCursorPos(cm.getCursor(), Pos(0, 2, "before"));
+ cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"});
+ sendKey(39);
+ eq(test, 55);
+ cm.removeKeyMap("mymap");
+ sendKey(39);
+ eqCursorPos(cm.getCursor(), Pos(0, 3, "before"));
+}, {value: "abc"});
+
+function mouseDown(cm, button, pos, mods) {
+ var coords = cm.charCoords(pos, "window")
+ var event = {type: "mousedown",
+ preventDefault: Math.min,
+ which: button,
+ target: cm.display.lineDiv,
+ clientX: coords.left, clientY: coords.top}
+ if (mods) for (var prop in mods) event[prop] = mods[prop]
+ cm.triggerOnMouseDown(event)
+}
+
+testCM("mouseBinding", function(cm) {
+ var fired = []
+ cm.addKeyMap({
+ "Shift-LeftClick": function(_cm, pos) {
+ eqCharPos(pos, Pos(1, 2))
+ fired.push("a")
+ },
+ "Shift-LeftDoubleClick": function() { fired.push("b") },
+ "Shift-LeftTripleClick": function() { fired.push("c") }
+ })
+
+ function send(button, mods) { mouseDown(cm, button, Pos(1, 2), mods) }
+ send(1, {shiftKey: true})
+ send(1, {shiftKey: true})
+ send(1, {shiftKey: true})
+ send(1, {})
+ send(2, {ctrlKey: true})
+ send(2, {ctrlKey: true})
+ eq(fired.join(" "), "a b c")
+}, {value: "foo\nbar\nbaz"})
+
+testCM("configureMouse", function(cm) {
+ cm.setOption("configureMouse", function() { return {unit: "word"} })
+ mouseDown(cm, 1, Pos(0, 5))
+ eqCharPos(cm.getCursor("from"), Pos(0, 4))
+ eqCharPos(cm.getCursor("to"), Pos(0, 7))
+ cm.setOption("configureMouse", function() { return {extend: true} })
+ mouseDown(cm, 1, Pos(0, 0))
+ eqCharPos(cm.getCursor("from"), Pos(0, 0))
+ eqCharPos(cm.getCursor("to"), Pos(0, 4))
+}, {value: "foo bar baz"})
+
+testCM("findPosH", function(cm) {
+ forEach([{from: Pos(0, 0), to: Pos(0, 1, "before"), by: 1},
+ {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true},
+ {from: Pos(0, 0), to: Pos(0, 4, "before"), by: 1, unit: "word"},
+ {from: Pos(0, 0), to: Pos(0, 8, "before"), by: 2, unit: "word"},
+ {from: Pos(0, 0), to: Pos(2, 0, "after"), by: 20, unit: "word", hitSide: true},
+ {from: Pos(0, 7), to: Pos(0, 5, "after"), by: -1, unit: "word"},
+ {from: Pos(0, 4), to: Pos(0, 8, "before"), by: 1, unit: "word"},
+ {from: Pos(1, 0), to: Pos(1, 18, "before"), by: 3, unit: "word"},
+ {from: Pos(1, 22), to: Pos(1, 5, "after"), by: -3, unit: "word"},
+ {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5},
+ {from: Pos(1, 15), to: Pos(1, 10, "after"), by: -5, unit: "column"},
+ {from: Pos(1, 15), to: Pos(1, 0, "after"), by: -50, unit: "column", hitSide: true},
+ {from: Pos(1, 15), to: Pos(1, 24, "before"), by: 50, unit: "column", hitSide: true},
+ {from: Pos(1, 15), to: Pos(2, 0, "after"), by: 50, hitSide: true}], function(t) {
+ var r = cm.findPosH(t.from, t.by, t.unit || "char");
+ eqCursorPos(r, t.to);
+ eq(!!r.hitSide, !!t.hitSide);
+ });
+}, {value: "line one\nline two.something.other\n"});
+
+testCM("beforeChange", function(cm) {
+ cm.on("beforeChange", function(cm, change) {
+ var text = [];
+ for (var i = 0; i < change.text.length; ++i)
+ text.push(change.text[i].replace(/\s/g, "_"));
+ change.update(null, null, text);
+ });
+ cm.setValue("hello, i am a\nnew document\n");
+ eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
+ CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) {
+ if (change.from.line == 0) change.cancel();
+ });
+ cm.setValue("oops"); // Canceled
+ eq(cm.getValue(), "hello,_i_am_a\nnew_document\n");
+ cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0));
+ eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey");
+}, {value: "abcdefghijk"});
+
+testCM("beforeChangeUndo", function(cm) {
+ cm.replaceRange("hi", Pos(0, 0), Pos(0));
+ cm.replaceRange("bye", Pos(0, 0), Pos(0));
+ eq(cm.historySize().undo, 2);
+ cm.on("beforeChange", function(cm, change) {
+ is(!change.update);
+ change.cancel();
+ });
+ cm.undo();
+ eq(cm.historySize().undo, 0);
+ eq(cm.getValue(), "bye\ntwo");
+}, {value: "one\ntwo"});
+
+testCM("beforeSelectionChange", function(cm) {
+ function notAtEnd(cm, pos) {
+ var len = cm.getLine(pos.line).length;
+ if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1);
+ return pos;
+ }
+ cm.on("beforeSelectionChange", function(cm, obj) {
+ obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor),
+ head: notAtEnd(cm, obj.ranges[0].head)}]);
+ });
+
+ addDoc(cm, 10, 10);
+ cm.execCommand("goLineEnd");
+ eqCursorPos(cm.getCursor(), Pos(0, 9));
+ cm.execCommand("selectAll");
+ eqCursorPos(cm.getCursor("start"), Pos(0, 0));
+ eqCursorPos(cm.getCursor("end"), Pos(9, 9));
+});
+
+testCM("change_removedText", function(cm) {
+ cm.setValue("abc\ndef");
+
+ var removedText = [];
+ cm.on("change", function(cm, change) {
+ removedText.push(change.removed);
+ });
+
+ cm.operation(function() {
+ cm.replaceRange("xyz", Pos(0, 0), Pos(1,1));
+ cm.replaceRange("123", Pos(0,0));
+ });
+
+ eq(removedText.length, 2);
+ eq(removedText[0].join("\n"), "abc\nd");
+ eq(removedText[1].join("\n"), "");
+
+ var removedText = [];
+ cm.undo();
+ eq(removedText.length, 2);
+ eq(removedText[0].join("\n"), "123");
+ eq(removedText[1].join("\n"), "xyz");
+
+ var removedText = [];
+ cm.redo();
+ eq(removedText.length, 2);
+ eq(removedText[0].join("\n"), "abc\nd");
+ eq(removedText[1].join("\n"), "");
+});
+
+testCM("lineStyleFromMode", function(cm) {
+ CodeMirror.defineMode("test_mode", function() {
+ return {token: function(stream) {
+ if (stream.match(/^\[[^\]]*\]/)) return " line-brackets ";
+ if (stream.match(/^\([^\)]*\)/)) return " line-background-parens ";
+ if (stream.match(/^<[^>]*>/)) return " span line-line line-background-bg ";
+ stream.match(/^\s+|^\S+/);
+ }};
+ });
+ cm.setOption("mode", "test_mode");
+ var bracketElts = byClassName(cm.getWrapperElement(), "brackets");
+ eq(bracketElts.length, 1, "brackets count");
+ eq(bracketElts[0].nodeName, "PRE");
+ is(!/brackets.*brackets/.test(bracketElts[0].className));
+ var parenElts = byClassName(cm.getWrapperElement(), "parens");
+ eq(parenElts.length, 1, "parens count");
+ eq(parenElts[0].nodeName, "DIV");
+ is(!/parens.*parens/.test(parenElts[0].className));
+ eq(parenElts[0].parentElement.nodeName, "DIV");
+
+ is(byClassName(cm.getWrapperElement(), "bg").length > 0);
+ is(byClassName(cm.getWrapperElement(), "line").length > 0);
+ var spanElts = byClassName(cm.getWrapperElement(), "cm-span");
+ eq(spanElts.length, 2);
+ is(/^\s*cm-span\s*$/.test(spanElts[0].className));
+}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: <tag> <tag>"});
+
+testCM("lineStyleFromBlankLine", function(cm) {
+ CodeMirror.defineMode("lineStyleFromBlankLine_mode", function() {
+ return {token: function(stream) { stream.skipToEnd(); return "comment"; },
+ blankLine: function() { return "line-blank"; }};
+ });
+ cm.setOption("mode", "lineStyleFromBlankLine_mode");
+ var blankElts = byClassName(cm.getWrapperElement(), "blank");
+ eq(blankElts.length, 1);
+ eq(blankElts[0].nodeName, "PRE");
+ cm.replaceRange("x", Pos(1, 0));
+ blankElts = byClassName(cm.getWrapperElement(), "blank");
+ eq(blankElts.length, 0);
+}, {value: "foo\n\nbar"});
+
+CodeMirror.registerHelper("xxx", "a", "A");
+CodeMirror.registerHelper("xxx", "b", "B");
+CodeMirror.defineMode("yyy", function() {
+ return {
+ token: function(stream) { stream.skipToEnd(); },
+ xxx: ["a", "b", "q"]
+ };
+});
+CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C");
+
+testCM("helpers", function(cm) {
+ cm.setOption("mode", "yyy");
+ eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B");
+ cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}});
+ eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C");
+ cm.setOption("mode", "javascript");
+ eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "");
+});
+
+testCM("selectionHistory", function(cm) {
+ for (var i = 0; i < 3; i++) {
+ cm.setExtending(true);
+ cm.execCommand("goCharRight");
+ cm.setExtending(false);
+ cm.execCommand("goCharRight");
+ cm.execCommand("goCharRight");
+ }
+ cm.execCommand("undoSelection");
+ eq(cm.getSelection(), "c");
+ cm.execCommand("undoSelection");
+ eq(cm.getSelection(), "");
+ eqCursorPos(cm.getCursor(), Pos(0, 4, "before"));
+ cm.execCommand("undoSelection");
+ eq(cm.getSelection(), "b");
+ cm.execCommand("redoSelection");
+ eq(cm.getSelection(), "");
+ eqCursorPos(cm.getCursor(), Pos(0, 4, "before"));
+ cm.execCommand("redoSelection");
+ eq(cm.getSelection(), "c");
+ cm.execCommand("redoSelection");
+ eq(cm.getSelection(), "");
+ eqCursorPos(cm.getCursor(), Pos(0, 6, "before"));
+}, {value: "a b c d"});
+
+testCM("selectionChangeReducesRedo", function(cm) {
+ cm.replaceSelection("X");
+ cm.execCommand("goCharRight");
+ cm.undoSelection();
+ cm.execCommand("selectAll");
+ cm.undoSelection();
+ eq(cm.getValue(), "Xabc");
+ eqCursorPos(cm.getCursor(), Pos(0, 1));
+ cm.undoSelection();
+ eq(cm.getValue(), "abc");
+}, {value: "abc"});
+
+testCM("selectionHistoryNonOverlapping", function(cm) {
+ cm.setSelection(Pos(0, 0), Pos(0, 1));
+ cm.setSelection(Pos(0, 2), Pos(0, 3));
+ cm.execCommand("undoSelection");
+ eqCursorPos(cm.getCursor("anchor"), Pos(0, 0));
+ eqCursorPos(cm.getCursor("head"), Pos(0, 1));
+}, {value: "1234"});
+
+testCM("cursorMotionSplitsHistory", function(cm) {
+ cm.replaceSelection("a");
+ cm.execCommand("goCharRight");
+ cm.replaceSelection("b");
+ cm.replaceSelection("c");
+ cm.undo();
+ eq(cm.getValue(), "a1234");
+ eqCursorPos(cm.getCursor(), Pos(0, 2, "before"));
+ cm.undo();
+ eq(cm.getValue(), "1234");
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+}, {value: "1234"});
+
+testCM("selChangeInOperationDoesNotSplit", function(cm) {
+ for (var i = 0; i < 4; i++) {
+ cm.operation(function() {
+ cm.replaceSelection("x");
+ cm.setCursor(Pos(0, cm.getCursor().ch - 1));
+ });
+ }
+ eqCursorPos(cm.getCursor(), Pos(0, 0));
+ eq(cm.getValue(), "xxxxa");
+ cm.undo();
+ eq(cm.getValue(), "a");
+}, {value: "a"});
+
+testCM("alwaysMergeSelEventWithChangeOrigin", function(cm) {
+ cm.replaceSelection("U", null, "foo");
+ cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "foo"});
+ cm.undoSelection();
+ eq(cm.getValue(), "a");
+ cm.replaceSelection("V", null, "foo");
+ cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "bar"});
+ cm.undoSelection();
+ eq(cm.getValue(), "Va");
+}, {value: "a"});
+
+testCM("getTokenAt", function(cm) {
+ var tokPlus = cm.getTokenAt(Pos(0, 2));
+ eq(tokPlus.type, "operator");
+ eq(tokPlus.string, "+");
+ var toks = cm.getLineTokens(0);
+ eq(toks.length, 3);
+ forEach([["number", "1"], ["operator", "+"], ["number", "2"]], function(expect, i) {
+ eq(toks[i].type, expect[0]);
+ eq(toks[i].string, expect[1]);
+ });
+}, {value: "1+2", mode: "javascript"});
+
+testCM("getTokenTypeAt", function(cm) {
+ eq(cm.getTokenTypeAt(Pos(0, 0)), "number");
+ eq(cm.getTokenTypeAt(Pos(0, 6)), "string");
+ cm.addOverlay({
+ token: function(stream) {
+ if (stream.match("foo")) return "foo";
+ else stream.next();
+ }
+ });
+ eq(byClassName(cm.getWrapperElement(), "cm-foo").length, 1);
+ eq(cm.getTokenTypeAt(Pos(0, 6)), "string");
+}, {value: "1 + 'foo'", mode: "javascript"});
+
+testCM("addOverlay", function(cm) {
+ cm.addOverlay({
+ token: function(stream) {
+ var base = stream.baseToken()
+ if (!/comment/.test(base.type) && stream.match(/\d+/)) return "x"
+ stream.next()
+ }
+ })
+ var x = byClassName(cm.getWrapperElement(), "cm-x")
+ is(x.length, 1)
+ is(x[0].textContent, "233")
+ cm.replaceRange("", Pos(0, 4), Pos(0, 6))
+ is(byClassName(cm.getWrapperElement(), "cm-x").length, 2)
+}, {value: "foo /* 100 */\nbar + 233;\nbaz", mode: "javascript"})
+
+testCM("resizeLineWidget", function(cm) {
+ addDoc(cm, 200, 3);
+ var widget = document.createElement("pre");
+ widget.innerHTML = "imwidget";
+ widget.style.background = "yellow";
+ cm.addLineWidget(1, widget, {noHScroll: true});
+ cm.setSize(40);
+ is(widget.parentNode.offsetWidth < 42);
+});
+
+testCM("combinedOperations", function(cm) {
+ var place = document.getElementById("testground");
+ var other = CodeMirror(place, {value: "123"});
+ try {
+ cm.operation(function() {
+ cm.addLineClass(0, "wrap", "foo");
+ other.addLineClass(0, "wrap", "foo");
+ });
+ eq(byClassName(cm.getWrapperElement(), "foo").length, 1);
+ eq(byClassName(other.getWrapperElement(), "foo").length, 1);
+ cm.operation(function() {
+ cm.removeLineClass(0, "wrap", "foo");
+ other.removeLineClass(0, "wrap", "foo");
+ });
+ eq(byClassName(cm.getWrapperElement(), "foo").length, 0);
+ eq(byClassName(other.getWrapperElement(), "foo").length, 0);
+ } finally {
+ place.removeChild(other.getWrapperElement());
+ }
+}, {value: "abc"});
+
+testCM("eventOrder", function(cm) {
+ var seen = [];
+ cm.on("change", function() {
+ if (!seen.length) cm.replaceSelection(".");
+ seen.push("change");
+ });
+ cm.on("cursorActivity", function() {
+ cm.replaceSelection("!");
+ seen.push("activity");
+ });
+ cm.replaceSelection("/");
+ eq(seen.join(","), "change,change,activity,change");
+});
+
+testCM("splitSpaces_nonspecial", function(cm) {
+ eq(byClassName(cm.getWrapperElement(), "cm-invalidchar").length, 0);
+}, {
+ specialChars: /[\u00a0]/,
+ value: "spaces -> <- between"
+});
+
+test("core_rmClass", function() {
+ var node = document.createElement("div");
+ node.className = "foo-bar baz-quux yadda";
+ CodeMirror.rmClass(node, "quux");
+ eq(node.className, "foo-bar baz-quux yadda");
+ CodeMirror.rmClass(node, "baz-quux");
+ eq(node.className, "foo-bar yadda");
+ CodeMirror.rmClass(node, "yadda");
+ eq(node.className, "foo-bar");
+ CodeMirror.rmClass(node, "foo-bar");
+ eq(node.className, "");
+ node.className = " foo ";
+ CodeMirror.rmClass(node, "foo");
+ eq(node.className, "");
+});
+
+test("core_addClass", function() {
+ var node = document.createElement("div");
+ CodeMirror.addClass(node, "a");
+ eq(node.className, "a");
+ CodeMirror.addClass(node, "a");
+ eq(node.className, "a");
+ CodeMirror.addClass(node, "b");
+ eq(node.className, "a b");
+ CodeMirror.addClass(node, "a");
+ CodeMirror.addClass(node, "b");
+ eq(node.className, "a b");
+});
+
+testCM("lineSeparator", function(cm) {
+ eq(cm.lineCount(), 3);
+ eq(cm.getLine(1), "bar\r");
+ eq(cm.getLine(2), "baz\rquux");
+ cm.setOption("lineSeparator", "\r");
+ eq(cm.lineCount(), 5);
+ eq(cm.getLine(4), "quux");
+ eq(cm.getValue(), "foo\rbar\r\rbaz\rquux");
+ eq(cm.getValue("\n"), "foo\nbar\n\nbaz\nquux");
+ cm.setOption("lineSeparator", null);
+ cm.setValue("foo\nbar\r\nbaz\rquux");
+ eq(cm.lineCount(), 4);
+}, {value: "foo\nbar\r\nbaz\rquux",
+ lineSeparator: "\n"});
+
+var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/
+var getChar = function (noExtending) { var res; do {res = String.fromCharCode(Math.floor(Math.random()*0x8ac)); } while ([0x90].indexOf(res.charCodeAt(0)) != -1 || (noExtending && extendingChars.test(res))); return res }
+var getString = function (n) { var res = getChar(true); while (--n > 0) res += getChar(); return res }
+
+function makeItWrapAfter(cm, pos) {
+ var firstLineTop = cm.cursorCoords(Pos(0, 0)).top;
+ for(var w = 0, posTop; posTop != firstLineTop; ++w) {
+ cm.setSize(w);
+ posTop = cm.charCoords(pos).top;
+ }
+}
+
+function countIf(arr, f) {
+ var result = 0
+ for (var i = 0; i < arr.length; i++) if (f[arr[i]]) result++
+ return result
+}
+
+function testMoveBidi(str) {
+ testCM("move_bidi_" + str, function(cm) {
+ if (cm.getOption("inputStyle") != "textarea" || !cm.getOption("rtlMoveVisually")) return;
+ cm.getScrollerElement().style.fontFamily = "monospace";
+ makeItWrapAfter(cm, Pos(0, 5));
+
+ var steps = str.length - countIf(str.split(""), function(ch) { return extendingChars.test(ch) });
+ var lineBreaks = {}
+ lineBreaks[6 - countIf(str.substr(0, 5).split(""), function(ch) { return extendingChars.test(ch) })] = 'w';
+ if (str.indexOf("\n") != -1) {
+ lineBreaks[steps - 2] = 'n';
+ }
+
+ // Make sure we are at the visual beginning of the first line
+ cm.execCommand("goLineStart");
+
+ var prevCoords = cm.cursorCoords(), coords;
+ for(var i = 0; i < steps; ++i) {
+ cm.execCommand("goCharRight");
+ coords = cm.cursorCoords();
+ if ((i >= 10 && i <= 12) && !lineBreaks[i] && coords.left < prevCoords.left && coords.top > prevCoords.top) {
+ // The first line wraps twice
+ lineBreaks[i] = 'w';
+ }
+ if (!lineBreaks[i]) {
+ is(coords.left > prevCoords.left, "In step " + i + ", cursor didn't move right");
+ eq(coords.top, prevCoords.top, "In step " + i + ", cursor moved out of line");
+ } else {
+ is(coords.left < prevCoords.left, i);
+ is(coords.top > prevCoords.top, i);
+ }
+ prevCoords = coords;
+ }
+
+ cm.execCommand("goCharRight");
+ coords = cm.cursorCoords();
+ eq(coords.left, prevCoords.left, "Moving " + steps + " steps right didn't reach the end");
+ eq(coords.top, prevCoords.top, "Moving " + steps + " steps right didn't reach the end");
+
+ for(i = steps - 1; i >= 0; --i) {
+ cm.execCommand("goCharLeft");
+ coords = cm.cursorCoords();
+ if (!(lineBreaks[i] == 'n' || lineBreaks[i + 1] == 'w')) {
+ is(coords.left < prevCoords.left, "In step " + i + ", cursor didn't move left");
+ eq(coords.top, prevCoords.top, "In step " + i + ", cursor is not at the same line anymore");
+ } else {
+ is(coords.left > prevCoords.left, i);
+ is(coords.top < prevCoords.top, i);
+ }
+ prevCoords = coords;
+ }
+
+ cm.execCommand("goCharLeft");
+ coords = cm.cursorCoords();
+ eq(coords.left, prevCoords.left, "Moving " + steps + " steps left didn't reach the beginning");
+ eq(coords.top, prevCoords.top, "Moving " + steps + " steps left didn't reach the beginning");
+ }, {value: str, lineWrapping: true})
+};
+
+function testMoveEndBidi(str) {
+ testCM("move_end_bidi_" + str, function(cm) {
+ cm.getScrollerElement().style.fontFamily = "monospace";
+ makeItWrapAfter(cm, Pos(0, 5));
+
+ cm.execCommand("goLineStart");
+ var pos = cm.doc.getCursor();
+ cm.execCommand("goCharLeft");
+ eqCursorPos(pos, cm.doc.getCursor());
+
+ cm.execCommand("goLineEnd");
+ pos = cm.doc.getCursor();
+ cm.execCommand("goColumnRight");
+ eqCursorPos(pos, cm.doc.getCursor());
+ }, {value: str, lineWrapping: true})
+};
+
+var bidiTests = [];
+
+// We don't correctly implement L1 UBA
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1331501
+// and https://bugs.chromium.org/p/chromium/issues/detail?id=673405
+/*
+bidiTests.push("Say ا ب جabj\nS");
+bidiTests.push("Sayyy ا ا ب ج");
+*/
+
+if (!phantom) {
+ bidiTests.push("Όȝǝڪȉۥ״ۺ׆ɀҩۏ\nҳ");
+ bidiTests.push("ŌӰтقȤ؁ƥ؅٣ĎȺ١\nϚ");
+ bidiTests.push("ٻоҤѕѽΩ־؉ïίքdz\nٵ");
+ bidiTests.push("؅؁ĆՕƿɁǞϮؠȩóć\nď");
+ bidiTests.push("RŨďңŪzϢŎƏԖڇڦ\nӈ");
+ bidiTests.push("ό׊۷٢ԜһОצЉيčǟ\nѩ");
+ bidiTests.push("ۑÚҳҕڬġڹհяųKV\nr");
+ bidiTests.push("źڻғúہ4ם1Ƞc1a\nԁ");
+ bidiTests.push("ҒȨҟփƞ٦ԓȦڰғâƥ\nڤ");
+ bidiTests.push("ϖسՉȏŧΔԛdžĎӟیڡ\nέ");
+ bidiTests.push("۹ؼL۵ĺȧКԙػא7״\nم");
+ bidiTests.push("ن (ي)\u2009أقواس"); // thin space to throw off Firefox 51's broken white-space compressing behavior
+}
+
+bidiTests.push("քմѧǮßپüŢҍҞўڳ\nӧ");
+
+//bidiTests.push("Count ١ ٢ ٣ ٤");
+//bidiTests.push("ӣאƦϰ؊ȓېÛوը٬ز\nϪ");
+//bidiTests.push("ҾճٳџIՖӻ٥׭֐؜ڏ\nێ");
+//bidiTests.push("ҬÓФ؜ڂį٦Ͽɓڐͳٵ\nՈ");
+//bidiTests.push("aѴNijȻهˇ҃ڱӧǻֵ\na");
+//bidiTests.push(" a٧ا٢ ب جa\nS");
+
+for (var i = 0; i < bidiTests.length; ++i) {
+ testMoveBidi(bidiTests[i]);
+ testMoveEndBidi(bidiTests[i]);
+}
+
+/*
+for (var i = 0; i < 5; ++i) {
+ testMoveBidi(getString(12) + "\n" + getString(1));
+}
+*/
+
+function testCoordsWrappedBidi(str) {
+ testCM("coords_wrapped_bidi_" + str, function(cm) {
+ cm.getScrollerElement().style.fontFamily = "monospace";
+ makeItWrapAfter(cm, Pos(0, 5));
+
+ // Make sure we are at the visual beginning of the first line
+ var pos = Pos(0, 0), lastPos;
+ cm.doc.setCursor(pos);
+ do {
+ lastPos = pos;
+ cm.execCommand("goCharLeft");
+ pos = cm.doc.getCursor();
+ } while (pos != lastPos)
+
+ var top = cm.charCoords(Pos(0, 0)).top, lastTop;
+ for (var i = 1; i < str.length; ++i) {
+ lastTop = top;
+ top = cm.charCoords(Pos(0, i)).top;
+ is(top >= lastTop);
+ }
+ }, {value: str, lineWrapping: true})
+};
+
+testCoordsWrappedBidi("Count ١ ٢ ٣ ٤");
+/*
+for (var i = 0; i < 5; ++i) {
+ testCoordsWrappedBidi(getString(50));
+}
+*/
+
+testCM("rtl_wrapped_selection", function(cm) {
+ cm.setSelection(Pos(0, 10), Pos(0, 190))
+ is(byClassName(cm.getWrapperElement(), "CodeMirror-selected").length >= 3)
+}, {value: new Array(10).join(" فتي تم تضمينها فتي تم"), lineWrapping: true})
+
+testCM("bidi_wrapped_selection", function(cm) {
+ if (phantom) return
+ cm.setSize(cm.charCoords(Pos(0, 10), "editor").left)
+ cm.setSelection(Pos(0, 37), Pos(0, 80))
+ var blocks = byClassName(cm.getWrapperElement(), "CodeMirror-selected")
+ is(blocks.length >= 2)
+ is(blocks.length <= 3)
+ var boxTop = blocks[0].getBoundingClientRect(), boxBot = blocks[blocks.length - 1].getBoundingClientRect()
+ is(boxTop.left > cm.charCoords(Pos(0, 1)).right)
+ is(boxBot.right < cm.charCoords(Pos(0, cm.getLine(0).length - 2)).left)
+}, {value: "<p>مفتي11 تم تضمينهفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تفتي تم تضمينها فتي تا فت10ي ت</p>", lineWrapping: true})
+
+testCM("delete_wrapped", function(cm) {
+ makeItWrapAfter(cm, Pos(0, 2));
+ cm.doc.setCursor(Pos(0, 3, "after"));
+ cm.deleteH(-1, "char");
+ eq(cm.getLine(0), "1245");
+}, {value: "12345", lineWrapping: true})
+
+testCM("issue_4878", function(cm) {
+ if (phantom) return
+ cm.setCursor(Pos(1, 12, "after"));
+ cm.moveH(-1, "char");
+ eqCursorPos(cm.getCursor(), Pos(0, 113, "before"));
+}, {value: " في تطبيق السمات مرة واحدة https://github.com/codemirror/CodeMirror/issues/4878#issuecomment-330550964على سبيل المثال <code>\"foo bar\"</code>\n" +
+" سيتم تعيين", direction: "rtl", lineWrapping: true});
+
+CodeMirror.defineMode("lookahead_mode", function() {
+ // Colors text as atom if the line two lines down has an x in it
+ return {
+ token: function(stream) {
+ stream.skipToEnd()
+ return /x/.test(stream.lookAhead(2)) ? "atom" : null
+ }
+ }
+})
+
+testCM("mode_lookahead", function(cm) {
+ eq(cm.getTokenAt(Pos(0, 1)).type, "atom")
+ eq(cm.getTokenAt(Pos(1, 1)).type, "atom")
+ eq(cm.getTokenAt(Pos(2, 1)).type, null)
+ cm.replaceRange("\n", Pos(2, 0))
+ eq(cm.getTokenAt(Pos(0, 1)).type, null)
+ eq(cm.getTokenAt(Pos(1, 1)).type, "atom")
+}, {value: "foo\na\nx\nx\n", mode: "lookahead_mode"})
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/vim_test.js b/devtools/client/shared/sourceeditor/test/codemirror/vim_test.js
new file mode 100644
index 0000000000..72a9896b59
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/vim_test.js
@@ -0,0 +1,4729 @@
+var Pos = CodeMirror.Pos;
+CodeMirror.Vim.suppressErrorLogging = true;
+
+var code = '' +
+' wOrd1 (#%\n' +
+' word3] \n' +
+'aopop pop 0 1 2 3 4\n' +
+' (a) [b] {c} \n' +
+'int getchar(void) {\n' +
+' static char buf[BUFSIZ];\n' +
+' static char *bufp = buf;\n' +
+' if (n == 0) { /* buffer is empty */\n' +
+' n = read(0, buf, sizeof buf);\n' +
+' bufp = buf;\n' +
+' }\n' +
+'\n' +
+' return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n' +
+' \n' +
+'}\n';
+
+var lines = (function() {
+ lineText = code.split('\n');
+ var ret = [];
+ for (var i = 0; i < lineText.length; i++) {
+ ret[i] = {
+ line: i,
+ length: lineText[i].length,
+ lineText: lineText[i],
+ textStart: /^\s*/.exec(lineText[i])[0].length
+ };
+ }
+ return ret;
+})();
+var endOfDocument = makeCursor(lines.length - 1,
+ lines[lines.length - 1].length);
+var wordLine = lines[0];
+var bigWordLine = lines[1];
+var charLine = lines[2];
+var bracesLine = lines[3];
+var seekBraceLine = lines[4];
+
+var word1 = {
+ start: new Pos(wordLine.line, 1),
+ end: new Pos(wordLine.line, 5)
+};
+var word2 = {
+ start: new Pos(wordLine.line, word1.end.ch + 2),
+ end: new Pos(wordLine.line, word1.end.ch + 4)
+};
+var word3 = {
+ start: new Pos(bigWordLine.line, 1),
+ end: new Pos(bigWordLine.line, 5)
+};
+var bigWord1 = word1;
+var bigWord2 = word2;
+var bigWord3 = {
+ start: new Pos(bigWordLine.line, 1),
+ end: new Pos(bigWordLine.line, 7)
+};
+var bigWord4 = {
+ start: new Pos(bigWordLine.line, bigWord1.end.ch + 3),
+ end: new Pos(bigWordLine.line, bigWord1.end.ch + 7)
+};
+
+var oChars = [ new Pos(charLine.line, 1),
+ new Pos(charLine.line, 3),
+ new Pos(charLine.line, 7) ];
+var pChars = [ new Pos(charLine.line, 2),
+ new Pos(charLine.line, 4),
+ new Pos(charLine.line, 6),
+ new Pos(charLine.line, 8) ];
+var numChars = [ new Pos(charLine.line, 10),
+ new Pos(charLine.line, 12),
+ new Pos(charLine.line, 14),
+ new Pos(charLine.line, 16),
+ new Pos(charLine.line, 18)];
+var parens1 = {
+ start: new Pos(bracesLine.line, 1),
+ end: new Pos(bracesLine.line, 3)
+};
+var squares1 = {
+ start: new Pos(bracesLine.line, 5),
+ end: new Pos(bracesLine.line, 7)
+};
+var curlys1 = {
+ start: new Pos(bracesLine.line, 9),
+ end: new Pos(bracesLine.line, 11)
+};
+var seekOutside = {
+ start: new Pos(seekBraceLine.line, 1),
+ end: new Pos(seekBraceLine.line, 16)
+};
+var seekInside = {
+ start: new Pos(seekBraceLine.line, 14),
+ end: new Pos(seekBraceLine.line, 11)
+};
+
+function copyCursor(cur) {
+ return new Pos(cur.line, cur.ch);
+}
+
+function forEach(arr, func) {
+ for (var i = 0; i < arr.length; i++) {
+ func(arr[i], i, arr);
+ }
+}
+
+function expectFail(fn) {
+ try {
+ fn();
+ } catch(expected) {
+ return;
+ };
+ throw new Error("Expected to throw an error");
+}
+
+function testVim(name, run, opts, expectedFail) {
+ var vimOpts = {
+ lineNumbers: true,
+ vimMode: true,
+ showCursorWhenSelecting: true,
+ value: code
+ };
+ for (var prop in opts) {
+ if (opts.hasOwnProperty(prop)) {
+ vimOpts[prop] = opts[prop];
+ }
+ }
+ return test('vim_' + name, function() {
+ var place = document.getElementById("testground");
+ var cm = CodeMirror(place, vimOpts);
+ var vim = CodeMirror.Vim.maybeInitVimState_(cm);
+
+ function doKeysFn(cm) {
+ return function(args) {
+ if (args instanceof Array) {
+ arguments = args;
+ }
+ for (var i = 0; i < arguments.length; i++) {
+ var result = CodeMirror.Vim.handleKey(cm, arguments[i]);
+ if (!result && cm.state.vim.insertMode) {
+ cm.replaceSelections(fillArray(arguments[i], cm.listSelections().length));
+ }
+ }
+ }
+ }
+ function doInsertModeKeysFn(cm) {
+ return function(args) {
+ if (args instanceof Array) { arguments = args; }
+ function executeHandler(handler) {
+ if (typeof handler == 'string') {
+ CodeMirror.commands[handler](cm);
+ } else {
+ handler(cm);
+ }
+ return true;
+ }
+ for (var i = 0; i < arguments.length; i++) {
+ var key = arguments[i];
+ // Find key in keymap and handle.
+ var handled = CodeMirror.lookupKey(key, cm.getOption('keyMap'), executeHandler, cm);
+ // Record for insert mode.
+ if (handled == "handled" && cm.state.vim.insertMode && arguments[i] != 'Esc') {
+ var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges;
+ if (lastChange && (key.indexOf('Delete') != -1 || key.indexOf('Backspace') != -1)) {
+ lastChange.changes.push(new CodeMirror.Vim.InsertModeKey(key));
+ }
+ }
+ }
+ }
+ }
+ function doExFn(cm) {
+ return function(command) {
+ cm.openDialog = helpers.fakeOpenDialog(command);
+ helpers.doKeys(':');
+ }
+ }
+ function assertCursorAtFn(cm) {
+ return function(line, ch) {
+ var pos;
+ if (ch == null && typeof line.line == 'number') {
+ pos = line;
+ } else {
+ pos = makeCursor(line, ch);
+ }
+ eqCursorPos(cm.getCursor(), pos);
+ }
+ }
+ function fakeOpenDialog(result) {
+ return function(text, callback) {
+ return callback(result);
+ }
+ }
+ function fakeOpenNotification(matcher) {
+ return function(text) {
+ matcher(text);
+ }
+ }
+ var helpers = {
+ doKeys: doKeysFn(cm),
+ // Warning: Only emulates keymap events, not character insertions. Use
+ // replaceRange to simulate character insertions.
+ // Keys are in CodeMirror format, NOT vim format.
+ doInsertModeKeys: doInsertModeKeysFn(cm),
+ doEx: doExFn(cm),
+ assertCursorAt: assertCursorAtFn(cm),
+ fakeOpenDialog: fakeOpenDialog,
+ fakeOpenNotification: fakeOpenNotification,
+ getRegisterController: function() {
+ return CodeMirror.Vim.getRegisterController();
+ }
+ }
+ CodeMirror.Vim.resetVimGlobalState_();
+ var successful = false;
+ var savedOpenNotification = cm.openNotification;
+ var savedOpenDialog = cm.openDialog;
+ try {
+ run(cm, vim, helpers);
+ successful = true;
+ } finally {
+ cm.openNotification = savedOpenNotification;
+ cm.openDialog = savedOpenDialog;
+ if (!successful || verbose) {
+ place.style.visibility = "visible";
+ } else {
+ place.removeChild(cm.getWrapperElement());
+ }
+ }
+ }, expectedFail);
+};
+testVim('qq@q', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'q', 'l', 'l', 'q');
+ helpers.assertCursorAt(0,2);
+ helpers.doKeys('@', 'q');
+ helpers.assertCursorAt(0,4);
+}, { value: ' '});
+testVim('@@', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'q', 'l', 'l', 'q');
+ helpers.assertCursorAt(0,2);
+ helpers.doKeys('@', 'q');
+ helpers.assertCursorAt(0,4);
+ helpers.doKeys('@', '@');
+ helpers.assertCursorAt(0,6);
+}, { value: ' '});
+var jumplistScene = ''+
+ 'word\n'+
+ '(word)\n'+
+ '{word\n'+
+ 'word.\n'+
+ '\n'+
+ 'word search\n'+
+ '}word\n'+
+ 'word\n'+
+ 'word\n';
+function testJumplist(name, keys, endPos, startPos, dialog) {
+ endPos = makeCursor(endPos[0], endPos[1]);
+ startPos = makeCursor(startPos[0], startPos[1]);
+ testVim(name, function(cm, vim, helpers) {
+ CodeMirror.Vim.resetVimGlobalState_();
+ if(dialog)cm.openDialog = helpers.fakeOpenDialog('word');
+ cm.setCursor(startPos);
+ helpers.doKeys.apply(null, keys);
+ helpers.assertCursorAt(endPos);
+ }, {value: jumplistScene});
+}
+testJumplist('jumplist_H', ['H', '<C-o>'], [5,2], [5,2]);
+testJumplist('jumplist_M', ['M', '<C-o>'], [2,2], [2,2]);
+testJumplist('jumplist_L', ['L', '<C-o>'], [2,2], [2,2]);
+testJumplist('jumplist_[[', ['[', '[', '<C-o>'], [5,2], [5,2]);
+testJumplist('jumplist_]]', [']', ']', '<C-o>'], [2,2], [2,2]);
+testJumplist('jumplist_G', ['G', '<C-o>'], [5,2], [5,2]);
+testJumplist('jumplist_gg', ['g', 'g', '<C-o>'], [5,2], [5,2]);
+testJumplist('jumplist_%', ['%', '<C-o>'], [1,5], [1,5]);
+testJumplist('jumplist_{', ['{', '<C-o>'], [1,5], [1,5]);
+testJumplist('jumplist_}', ['}', '<C-o>'], [1,5], [1,5]);
+testJumplist('jumplist_\'', ['m', 'a', 'h', '\'', 'a', 'h', '<C-i>'], [1,0], [1,5]);
+testJumplist('jumplist_`', ['m', 'a', 'h', '`', 'a', 'h', '<C-i>'], [1,5], [1,5]);
+testJumplist('jumplist_*_cachedCursor', ['*', '<C-o>'], [1,3], [1,3]);
+testJumplist('jumplist_#_cachedCursor', ['#', '<C-o>'], [1,3], [1,3]);
+testJumplist('jumplist_n', ['#', 'n', '<C-o>'], [1,1], [2,3]);
+testJumplist('jumplist_N', ['#', 'N', '<C-o>'], [1,1], [2,3]);
+testJumplist('jumplist_repeat_<c-o>', ['*', '*', '*', '3', '<C-o>'], [2,3], [2,3]);
+testJumplist('jumplist_repeat_<c-i>', ['*', '*', '*', '3', '<C-o>', '2', '<C-i>'], [5,0], [2,3]);
+testJumplist('jumplist_repeated_motion', ['3', '*', '<C-o>'], [2,3], [2,3]);
+testJumplist('jumplist_/', ['/', '<C-o>'], [2,3], [2,3], 'dialog');
+testJumplist('jumplist_?', ['?', '<C-o>'], [2,3], [2,3], 'dialog');
+testJumplist('jumplist_skip_deleted_mark<c-o>',
+ ['*', 'n', 'n', 'k', 'd', 'k', '<C-o>', '<C-o>', '<C-o>'],
+ [0,2], [0,2]);
+testJumplist('jumplist_skip_deleted_mark<c-i>',
+ ['*', 'n', 'n', 'k', 'd', 'k', '<C-o>', '<C-i>', '<C-i>'],
+ [1,0], [0,2]);
+
+/**
+ * @param name Name of the test
+ * @param keys An array of keys or a string with a single key to simulate.
+ * @param endPos The expected end position of the cursor.
+ * @param startPos The position the cursor should start at, defaults to 0, 0.
+ */
+function testMotion(name, keys, endPos, startPos) {
+ testVim(name, function(cm, vim, helpers) {
+ if (!startPos) {
+ startPos = new Pos(0, 0);
+ }
+ cm.setCursor(startPos);
+ helpers.doKeys(keys);
+ helpers.assertCursorAt(endPos);
+ });
+}
+
+function makeCursor(line, ch) {
+ return new Pos(line, ch);
+}
+
+function offsetCursor(cur, offsetLine, offsetCh) {
+ return new Pos(cur.line + offsetLine, cur.ch + offsetCh);
+}
+
+// Motion tests
+testMotion('|', '|', makeCursor(0, 0), makeCursor(0,4));
+testMotion('|_repeat', ['3', '|'], makeCursor(0, 2), makeCursor(0,4));
+testMotion('h', 'h', makeCursor(0, 0), word1.start);
+testMotion('h_repeat', ['3', 'h'], offsetCursor(word1.end, 0, -3), word1.end);
+testMotion('l', 'l', makeCursor(0, 1));
+testMotion('l_repeat', ['2', 'l'], makeCursor(0, 2));
+testMotion('j', 'j', offsetCursor(word1.end, 1, 0), word1.end);
+testMotion('j_repeat', ['2', 'j'], offsetCursor(word1.end, 2, 0), word1.end);
+testMotion('j_repeat_clip', ['1000', 'j'], endOfDocument);
+testMotion('k', 'k', offsetCursor(word3.end, -1, 0), word3.end);
+testMotion('k_repeat', ['2', 'k'], makeCursor(0, 4), makeCursor(2, 4));
+testMotion('k_repeat_clip', ['1000', 'k'], makeCursor(0, 4), makeCursor(2, 4));
+testMotion('w', 'w', word1.start);
+testMotion('keepHPos', ['5', 'j', 'j', '7', 'k'], makeCursor(8, 12), makeCursor(12, 12));
+testMotion('keepHPosEol', ['$', '2', 'j'], makeCursor(2, 18));
+testMotion('w_multiple_newlines_no_space', 'w', makeCursor(12, 2), makeCursor(11, 2));
+testMotion('w_multiple_newlines_with_space', 'w', makeCursor(14, 0), makeCursor(12, 51));
+testMotion('w_repeat', ['2', 'w'], word2.start);
+testMotion('w_wrap', ['w'], word3.start, word2.start);
+testMotion('w_endOfDocument', 'w', endOfDocument, endOfDocument);
+testMotion('w_start_to_end', ['1000', 'w'], endOfDocument, makeCursor(0, 0));
+testMotion('W', 'W', bigWord1.start);
+testMotion('W_repeat', ['2', 'W'], bigWord3.start, bigWord1.start);
+testMotion('e', 'e', word1.end);
+testMotion('e_repeat', ['2', 'e'], word2.end);
+testMotion('e_wrap', 'e', word3.end, word2.end);
+testMotion('e_endOfDocument', 'e', endOfDocument, endOfDocument);
+testMotion('e_start_to_end', ['1000', 'e'], endOfDocument, makeCursor(0, 0));
+testMotion('b', 'b', word3.start, word3.end);
+testMotion('b_repeat', ['2', 'b'], word2.start, word3.end);
+testMotion('b_wrap', 'b', word2.start, word3.start);
+testMotion('b_startOfDocument', 'b', makeCursor(0, 0), makeCursor(0, 0));
+testMotion('b_end_to_start', ['1000', 'b'], makeCursor(0, 0), endOfDocument);
+testMotion('ge', ['g', 'e'], word2.end, word3.end);
+testMotion('ge_repeat', ['2', 'g', 'e'], word1.end, word3.start);
+testMotion('ge_wrap', ['g', 'e'], word2.end, word3.start);
+testMotion('ge_startOfDocument', ['g', 'e'], makeCursor(0, 0),
+ makeCursor(0, 0));
+testMotion('ge_end_to_start', ['1000', 'g', 'e'], makeCursor(0, 0), endOfDocument);
+testMotion('gg', ['g', 'g'], makeCursor(lines[0].line, lines[0].textStart),
+ makeCursor(3, 1));
+testMotion('gg_repeat', ['3', 'g', 'g'],
+ makeCursor(lines[2].line, lines[2].textStart));
+testMotion('G', 'G',
+ makeCursor(lines[lines.length - 1].line, lines[lines.length - 1].textStart),
+ makeCursor(3, 1));
+testMotion('G_repeat', ['3', 'G'], makeCursor(lines[2].line,
+ lines[2].textStart));
+// TODO: Make the test code long enough to test Ctrl-F and Ctrl-B.
+testMotion('0', '0', makeCursor(0, 0), makeCursor(0, 8));
+testMotion('^', '^', makeCursor(0, lines[0].textStart), makeCursor(0, 8));
+testMotion('+', '+', makeCursor(1, lines[1].textStart), makeCursor(0, 8));
+testMotion('-', '-', makeCursor(0, lines[0].textStart), makeCursor(1, 4));
+testMotion('_', ['6','_'], makeCursor(5, lines[5].textStart), makeCursor(0, 8));
+testMotion('$', '$', makeCursor(0, lines[0].length - 1), makeCursor(0, 1));
+testMotion('$_repeat', ['2', '$'], makeCursor(1, lines[1].length - 1),
+ makeCursor(0, 3));
+testMotion('f', ['f', 'p'], pChars[0], makeCursor(charLine.line, 0));
+testMotion('f_repeat', ['2', 'f', 'p'], pChars[2], pChars[0]);
+testMotion('f_num', ['f', '2'], numChars[2], makeCursor(charLine.line, 0));
+testMotion('t', ['t','p'], offsetCursor(pChars[0], 0, -1),
+ makeCursor(charLine.line, 0));
+testMotion('t_repeat', ['2', 't', 'p'], offsetCursor(pChars[2], 0, -1),
+ pChars[0]);
+testMotion('F', ['F', 'p'], pChars[0], pChars[1]);
+testMotion('F_repeat', ['2', 'F', 'p'], pChars[0], pChars[2]);
+testMotion('T', ['T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[1]);
+testMotion('T_repeat', ['2', 'T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[2]);
+testMotion('%_parens', ['%'], parens1.end, parens1.start);
+testMotion('%_squares', ['%'], squares1.end, squares1.start);
+testMotion('%_braces', ['%'], curlys1.end, curlys1.start);
+testMotion('%_seek_outside', ['%'], seekOutside.end, seekOutside.start);
+testMotion('%_seek_inside', ['%'], seekInside.end, seekInside.start);
+testVim('%_seek_skip', function(cm, vim, helpers) {
+ cm.setCursor(0,0);
+ helpers.doKeys(['%']);
+ helpers.assertCursorAt(0,9);
+}, {value:'01234"("()'});
+testVim('%_skip_string', function(cm, vim, helpers) {
+ cm.setCursor(0,0);
+ helpers.doKeys(['%']);
+ helpers.assertCursorAt(0,4);
+ cm.setCursor(0,2);
+ helpers.doKeys(['%']);
+ helpers.assertCursorAt(0,0);
+}, {value:'(")")'});
+testVim('%_skip_comment', function(cm, vim, helpers) {
+ cm.setCursor(0,0);
+ helpers.doKeys(['%']);
+ helpers.assertCursorAt(0,6);
+ cm.setCursor(0,3);
+ helpers.doKeys(['%']);
+ helpers.assertCursorAt(0,0);
+}, {value:'(/*)*/)'});
+// Make sure that moving down after going to the end of a line always leaves you
+// at the end of a line, but preserves the offset in other cases
+testVim('Changing lines after Eol operation', function(cm, vim, helpers) {
+ cm.setCursor(0,0);
+ helpers.doKeys(['$']);
+ helpers.doKeys(['j']);
+ // After moving to Eol and then down, we should be at Eol of line 2
+ helpers.assertCursorAt(new Pos(1, lines[1].length - 1));
+ helpers.doKeys(['j']);
+ // After moving down, we should be at Eol of line 3
+ helpers.assertCursorAt(new Pos(2, lines[2].length - 1));
+ helpers.doKeys(['h']);
+ helpers.doKeys(['j']);
+ // After moving back one space and then down, since line 4 is shorter than line 2, we should
+ // be at Eol of line 2 - 1
+ helpers.assertCursorAt(new Pos(3, lines[3].length - 1));
+ helpers.doKeys(['j']);
+ helpers.doKeys(['j']);
+ // After moving down again, since line 3 has enough characters, we should be back to the
+ // same place we were at on line 1
+ helpers.assertCursorAt(new Pos(5, lines[2].length - 2));
+});
+//making sure gj and gk recover from clipping
+testVim('gj_gk_clipping', function(cm,vim,helpers){
+ cm.setCursor(0, 1);
+ helpers.doKeys('g','j','g','j');
+ helpers.assertCursorAt(2, 1);
+ helpers.doKeys('g','k','g','k');
+ helpers.assertCursorAt(0, 1);
+},{value: 'line 1\n\nline 2'});
+//testing a mix of j/k and gj/gk
+testVim('j_k_and_gj_gk', function(cm,vim,helpers){
+ cm.setSize(120);
+ cm.setCursor(0, 0);
+ //go to the last character on the first line
+ helpers.doKeys('$');
+ //move up/down on the column within the wrapped line
+ //side-effect: cursor is not locked to eol anymore
+ helpers.doKeys('g','k');
+ var cur=cm.getCursor();
+ eq(cur.line,0);
+ is((cur.ch<176),'gk didn\'t move cursor back (1)');
+ helpers.doKeys('g','j');
+ helpers.assertCursorAt(0, 176);
+ //should move to character 177 on line 2 (j/k preserve character index within line)
+ helpers.doKeys('j');
+ //due to different line wrapping, the cursor can be on a different screen-x now
+ //gj and gk preserve screen-x on movement, much like moveV
+ helpers.doKeys('3','g','k');
+ cur=cm.getCursor();
+ eq(cur.line,1);
+ is((cur.ch<176),'gk didn\'t move cursor back (2)');
+ helpers.doKeys('g','j','2','g','j');
+ //should return to the same character-index
+ helpers.doKeys('k');
+ helpers.assertCursorAt(0, 176);
+},{ lineWrapping:true, value: 'This line is intentially long to test movement of gj and gk over wrapped lines. I will start on the end of this line, then make a step up and back to set the origin for j and k.\nThis line is supposed to be even longer than the previous. I will jump here and make another wiggle with gj and gk, before I jump back to the line above. Both wiggles should not change my cursor\'s target character but both j/k and gj/gk change each other\'s reference position.'});
+testVim('gj_gk', function(cm, vim, helpers) {
+ if (phantom) return;
+ cm.setSize(120);
+ // Test top of document edge case.
+ cm.setCursor(0, 4);
+ helpers.doKeys('g', 'j');
+ helpers.doKeys('10', 'g', 'k');
+ helpers.assertCursorAt(0, 4);
+
+ // Test moving down preserves column position.
+ helpers.doKeys('g', 'j');
+ var pos1 = cm.getCursor();
+ var expectedPos2 = new Pos(0, (pos1.ch - 4) * 2 + 4);
+ helpers.doKeys('g', 'j');
+ helpers.assertCursorAt(expectedPos2);
+
+ // Move to the last character
+ cm.setCursor(0, 0);
+ // Move left to reset HSPos
+ helpers.doKeys('h');
+ // Test bottom of document edge case.
+ helpers.doKeys('100', 'g', 'j');
+ var endingPos = cm.getCursor();
+ is(endingPos != 0, 'gj should not be on wrapped line 0');
+ var topLeftCharCoords = cm.charCoords(makeCursor(0, 0));
+ var endingCharCoords = cm.charCoords(endingPos);
+ is(topLeftCharCoords.left == endingCharCoords.left, 'gj should end up on column 0');
+},{ lineNumbers: false, lineWrapping:true, value: 'Thislineisintentionallylongtotestmovementofgjandgkoverwrappedlines.' });
+testVim('}', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('}');
+ helpers.assertCursorAt(1, 0);
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', '}');
+ helpers.assertCursorAt(4, 0);
+ cm.setCursor(0, 0);
+ helpers.doKeys('6', '}');
+ helpers.assertCursorAt(5, 0);
+}, { value: 'a\n\nb\nc\n\nd' });
+testVim('{', function(cm, vim, helpers) {
+ cm.setCursor(5, 0);
+ helpers.doKeys('{');
+ helpers.assertCursorAt(4, 0);
+ cm.setCursor(5, 0);
+ helpers.doKeys('2', '{');
+ helpers.assertCursorAt(1, 0);
+ cm.setCursor(5, 0);
+ helpers.doKeys('6', '{');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'a\n\nb\nc\n\nd' });
+testVim('(', function(cm, vim, helpers) {
+ cm.setCursor(6, 23);
+ helpers.doKeys('(');
+ helpers.assertCursorAt(6, 14);
+ helpers.doKeys('2', '(');
+ helpers.assertCursorAt(5, 0);
+ helpers.doKeys('(');
+ helpers.assertCursorAt(4, 0);
+ helpers.doKeys('(');
+ helpers.assertCursorAt(3, 0);
+ helpers.doKeys('(');
+ helpers.assertCursorAt(2, 0);
+ helpers.doKeys('(');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('(');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'sentence1.\n\n\nsentence2\n\nsentence3. sentence4\n sentence5? sentence6!' });
+testVim(')', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', ')');
+ helpers.assertCursorAt(3, 0);
+ helpers.doKeys(')');
+ helpers.assertCursorAt(4, 0);
+ helpers.doKeys(')');
+ helpers.assertCursorAt(5, 0);
+ helpers.doKeys(')');
+ helpers.assertCursorAt(5, 11);
+ helpers.doKeys(')');
+ helpers.assertCursorAt(6, 14);
+ helpers.doKeys(')');
+ helpers.assertCursorAt(6, 23);
+ helpers.doKeys(')');
+ helpers.assertCursorAt(6, 23);
+}, { value: 'sentence1.\n\n\nsentence2\n\nsentence3. sentence4\n sentence5? sentence6!' });
+testVim('paragraph_motions', function(cm, vim, helpers) {
+ cm.setCursor(10, 0);
+ helpers.doKeys('{');
+ helpers.assertCursorAt(4, 0);
+ helpers.doKeys('{');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('2', '}');
+ helpers.assertCursorAt(7, 0);
+ helpers.doKeys('2', '}');
+ helpers.assertCursorAt(16, 0);
+
+ cm.setCursor(9, 0);
+ helpers.doKeys('}');
+ helpers.assertCursorAt(14, 0);
+
+ cm.setCursor(6, 0);
+ helpers.doKeys('}');
+ helpers.assertCursorAt(7, 0);
+
+ // ip inside empty space
+ cm.setCursor(10, 0);
+ helpers.doKeys('v', 'i', 'p');
+ eqCursorPos(Pos(7, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(12, 0), cm.getCursor('head'));
+ helpers.doKeys('i', 'p');
+ eqCursorPos(Pos(7, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(13, 1), cm.getCursor('head'));
+ helpers.doKeys('2', 'i', 'p');
+ eqCursorPos(Pos(7, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(16, 1), cm.getCursor('head'));
+
+ // should switch to visualLine mode
+ cm.setCursor(14, 0);
+ helpers.doKeys('<Esc>', 'v', 'i', 'p');
+ helpers.assertCursorAt(14, 0);
+
+ cm.setCursor(14, 0);
+ helpers.doKeys('<Esc>', 'V', 'i', 'p');
+ eqCursorPos(Pos(16, 1), cm.getCursor('head'));
+
+ // ap inside empty space
+ cm.setCursor(10, 0);
+ helpers.doKeys('<Esc>', 'v', 'a', 'p');
+ eqCursorPos(Pos(7, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(13, 1), cm.getCursor('head'));
+ helpers.doKeys('a', 'p');
+ eqCursorPos(Pos(7, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(16, 1), cm.getCursor('head'));
+
+ cm.setCursor(13, 0);
+ helpers.doKeys('v', 'a', 'p');
+ eqCursorPos(Pos(13, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(14, 0), cm.getCursor('head'));
+
+ cm.setCursor(16, 0);
+ helpers.doKeys('v', 'a', 'p');
+ eqCursorPos(Pos(14, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(16, 1), cm.getCursor('head'));
+
+ cm.setCursor(0, 0);
+ helpers.doKeys('v', 'a', 'p');
+ eqCursorPos(Pos(0, 0), cm.getCursor('anchor'));
+ eqCursorPos(Pos(4, 0), cm.getCursor('head'));
+
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'i', 'p');
+ var register = helpers.getRegisterController().getRegister();
+ eq('a\na\n', register.toString());
+ is(register.linewise);
+ helpers.doKeys('3', 'j', 'p');
+ helpers.doKeys('y', 'i', 'p');
+ is(register.linewise);
+ eq('b\na\na\nc\n', register.toString());
+}, { value: 'a\na\n\n\n\nb\nc\n\n\n\n\n\n\nd\n\ne\nf' });
+
+// Operator tests
+testVim('dl', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 0);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'l');
+ eq('word1 ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' ', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1 ' });
+testVim('dl_eol', function(cm, vim, helpers) {
+ cm.setCursor(0, 6);
+ helpers.doKeys('d', 'l');
+ eq(' word1', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' ', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 5);
+}, { value: ' word1 ' });
+testVim('dl_repeat', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 0);
+ cm.setCursor(curStart);
+ helpers.doKeys('2', 'd', 'l');
+ eq('ord1 ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' w', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1 ' });
+testVim('dh', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'h');
+ eq(' wrd1 ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('o', register.toString());
+ is(!register.linewise);
+ eqCursorPos(offsetCursor(curStart, 0 , -1), cm.getCursor());
+}, { value: ' word1 ' });
+testVim('dj', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'j');
+ eq(' word3', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' word1\nword2\n', register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, 1);
+}, { value: ' word1\nword2\n word3' });
+testVim('dj_end_of_document', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'j');
+ eq('', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' word1 \n', register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, 0);
+}, { value: ' word1 ' });
+testVim('dk', function(cm, vim, helpers) {
+ var curStart = makeCursor(1, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'k');
+ eq(' word3', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' word1\nword2\n', register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, 1);
+}, { value: ' word1\nword2\n word3' });
+testVim('dk_start_of_document', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'k');
+ eq('', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' word1 \n', register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, 0);
+}, { value: ' word1 ' });
+testVim('dw_space', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 0);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'w');
+ eq('word1 ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' ', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1 ' });
+testVim('dw_word', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 1);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'w');
+ eq(' word2', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1 ', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1 word2' });
+testVim('dw_unicode_word', function(cm, vim, helpers) {
+ helpers.doKeys('d', 'w');
+ eq(cm.getValue().length, 10);
+ helpers.doKeys('d', 'w');
+ eq(cm.getValue().length, 6);
+ helpers.doKeys('d', 'w');
+ eq(cm.getValue().length, 5);
+ helpers.doKeys('d', 'e');
+ eq(cm.getValue().length, 2);
+}, { value: ' \u0562\u0561\u0580\u0587\xbbe\xb5g ' });
+testVim('dw_only_word', function(cm, vim, helpers) {
+ // Test that if there is only 1 word left, dw deletes till the end of the
+ // line.
+ cm.setCursor(0, 1);
+ helpers.doKeys('d', 'w');
+ eq(' ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1 ', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 0);
+}, { value: ' word1 ' });
+testVim('dw_eol', function(cm, vim, helpers) {
+ // Assert that dw does not delete the newline if last word to delete is at end
+ // of line.
+ cm.setCursor(0, 1);
+ helpers.doKeys('d', 'w');
+ eq(' \nword2', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 0);
+}, { value: ' word1\nword2' });
+testVim('dw_eol_with_multiple_newlines', function(cm, vim, helpers) {
+ // Assert that dw does not delete the newline if last word to delete is at end
+ // of line and it is followed by multiple newlines.
+ cm.setCursor(0, 1);
+ helpers.doKeys('d', 'w');
+ eq(' \n\nword2', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 0);
+}, { value: ' word1\n\nword2' });
+testVim('dw_empty_line_followed_by_whitespace', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'w');
+ eq(' \nword', cm.getValue());
+}, { value: '\n \nword' });
+testVim('dw_empty_line_followed_by_word', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'w');
+ eq('word', cm.getValue());
+}, { value: '\nword' });
+testVim('dw_empty_line_followed_by_empty_line', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'w');
+ eq('\n', cm.getValue());
+}, { value: '\n\n' });
+testVim('dw_whitespace_followed_by_whitespace', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'w');
+ eq('\n \n', cm.getValue());
+}, { value: ' \n \n' });
+testVim('dw_whitespace_followed_by_empty_line', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'w');
+ eq('\n\n', cm.getValue());
+}, { value: ' \n\n' });
+testVim('dw_word_whitespace_word', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'w');
+ eq('\n \nword2', cm.getValue());
+}, { value: 'word1\n \nword2'})
+testVim('dw_end_of_document', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('d', 'w');
+ eq('\nab', cm.getValue());
+}, { value: '\nabc' });
+testVim('dw_repeat', function(cm, vim, helpers) {
+ // Assert that dw does delete newline if it should go to the next line, and
+ // that repeat works properly.
+ cm.setCursor(0, 1);
+ helpers.doKeys('d', '2', 'w');
+ eq(' ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1\nword2', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 0);
+}, { value: ' word1\nword2' });
+testVim('de_word_start_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'e');
+ eq('\n\n', cm.getValue());
+}, { value: 'word\n\n' });
+testVim('de_word_end_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ helpers.doKeys('d', 'e');
+ eq('wor', cm.getValue());
+}, { value: 'word\n\n\n' });
+testVim('de_whitespace_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'e');
+ eq('', cm.getValue());
+}, { value: ' \n\n\n' });
+testVim('de_end_of_document', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('d', 'e');
+ eq('\nab', cm.getValue());
+}, { value: '\nabc' });
+testVim('db_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('d', 'b');
+ eq('\n\n', cm.getValue());
+}, { value: '\n\n\n' });
+testVim('db_word_start_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('d', 'b');
+ eq('\nword', cm.getValue());
+}, { value: '\n\nword' });
+testVim('db_word_end_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(2, 3);
+ helpers.doKeys('d', 'b');
+ eq('\n\nd', cm.getValue());
+}, { value: '\n\nword' });
+testVim('db_whitespace_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('d', 'b');
+ eq('', cm.getValue());
+}, { value: '\n \n' });
+testVim('db_start_of_document', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'b');
+ eq('abc\n', cm.getValue());
+}, { value: 'abc\n' });
+testVim('dge_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ helpers.doKeys('d', 'g', 'e');
+ // Note: In real VIM the result should be '', but it's not quite consistent,
+ // since 2 newlines are deleted. But in the similar case of word\n\n, only
+ // 1 newline is deleted. We'll diverge from VIM's behavior since it's much
+ // easier this way.
+ eq('\n', cm.getValue());
+}, { value: '\n\n' });
+testVim('dge_word_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ helpers.doKeys('d', 'g', 'e');
+ eq('wor\n', cm.getValue());
+}, { value: 'word\n\n'});
+testVim('dge_whitespace_and_empty_lines', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('d', 'g', 'e');
+ eq('', cm.getValue());
+}, { value: '\n \n' });
+testVim('dge_start_of_document', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', 'g', 'e');
+ eq('bc\n', cm.getValue());
+}, { value: 'abc\n' });
+testVim('d_inclusive', function(cm, vim, helpers) {
+ // Assert that when inclusive is set, the character the cursor is on gets
+ // deleted too.
+ var curStart = makeCursor(0, 1);
+ cm.setCursor(curStart);
+ helpers.doKeys('d', 'e');
+ eq(' ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1 ' });
+testVim('d_reverse', function(cm, vim, helpers) {
+ // Test that deleting in reverse works.
+ cm.setCursor(1, 0);
+ helpers.doKeys('d', 'b');
+ eq(' word2 ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1\n', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 1);
+}, { value: ' word1\nword2 ' });
+testVim('dd', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedBuffer = cm.getRange(new Pos(0, 0),
+ new Pos(1, 0));
+ var expectedLineCount = cm.lineCount() - 1;
+ helpers.doKeys('d', 'd');
+ eq(expectedLineCount, cm.lineCount());
+ var register = helpers.getRegisterController().getRegister();
+ eq(expectedBuffer, register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, lines[1].textStart);
+});
+testVim('dd_prefix_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedBuffer = cm.getRange(new Pos(0, 0),
+ new Pos(2, 0));
+ var expectedLineCount = cm.lineCount() - 2;
+ helpers.doKeys('2', 'd', 'd');
+ eq(expectedLineCount, cm.lineCount());
+ var register = helpers.getRegisterController().getRegister();
+ eq(expectedBuffer, register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, lines[2].textStart);
+});
+testVim('dd_motion_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedBuffer = cm.getRange(new Pos(0, 0),
+ new Pos(2, 0));
+ var expectedLineCount = cm.lineCount() - 2;
+ helpers.doKeys('d', '2', 'd');
+ eq(expectedLineCount, cm.lineCount());
+ var register = helpers.getRegisterController().getRegister();
+ eq(expectedBuffer, register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, lines[2].textStart);
+});
+testVim('dd_multiply_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedBuffer = cm.getRange(new Pos(0, 0),
+ new Pos(6, 0));
+ var expectedLineCount = cm.lineCount() - 6;
+ helpers.doKeys('2', 'd', '3', 'd');
+ eq(expectedLineCount, cm.lineCount());
+ var register = helpers.getRegisterController().getRegister();
+ eq(expectedBuffer, register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, lines[6].textStart);
+});
+testVim('dd_lastline', function(cm, vim, helpers) {
+ cm.setCursor(cm.lineCount(), 0);
+ var expectedLineCount = cm.lineCount() - 1;
+ helpers.doKeys('d', 'd');
+ eq(expectedLineCount, cm.lineCount());
+ helpers.assertCursorAt(cm.lineCount() - 1, 0);
+});
+testVim('dd_only_line', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ var expectedRegister = cm.getValue() + "\n";
+ helpers.doKeys('d','d');
+ eq(1, cm.lineCount());
+ eq('', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(expectedRegister, register.toString());
+}, { value: "thisistheonlyline" });
+// Yank commands should behave the exact same as d commands, expect that nothing
+// gets deleted.
+testVim('yw_repeat', function(cm, vim, helpers) {
+ // Assert that yw does yank newline if it should go to the next line, and
+ // that repeat works properly.
+ var curStart = makeCursor(0, 1);
+ cm.setCursor(curStart);
+ helpers.doKeys('y', '2', 'w');
+ eq(' word1\nword2', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1\nword2', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1\nword2' });
+testVim('yy_multiply_repeat', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ var expectedBuffer = cm.getRange(new Pos(0, 0),
+ new Pos(6, 0));
+ var expectedLineCount = cm.lineCount();
+ helpers.doKeys('2', 'y', '3', 'y');
+ eq(expectedLineCount, cm.lineCount());
+ var register = helpers.getRegisterController().getRegister();
+ eq(expectedBuffer, register.toString());
+ is(register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+});
+testVim('2dd_blank_P', function(cm, vim, helpers) {
+ helpers.doKeys('2', 'd', 'd', 'P');
+ eq('\na\n\n', cm.getValue());
+}, { value: '\na\n\n' });
+// Change commands behave like d commands except that it also enters insert
+// mode. In addition, when the change is linewise, an additional newline is
+// inserted so that insert mode starts on that line.
+testVim('cw', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('c', '2', 'w');
+ eq(' word3', cm.getValue());
+ helpers.assertCursorAt(0, 0);
+}, { value: 'word1 word2 word3'});
+testVim('cw_repeat', function(cm, vim, helpers) {
+ // Assert that cw does delete newline if it should go to the next line, and
+ // that repeat works properly.
+ var curStart = makeCursor(0, 1);
+ cm.setCursor(curStart);
+ helpers.doKeys('c', '2', 'w');
+ eq(' ', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word1\nword2', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+ eq('vim-insert', cm.getOption('keyMap'));
+}, { value: ' word1\nword2' });
+testVim('cc_multiply_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedBuffer = cm.getRange(new Pos(0, 0),
+ new Pos(6, 0));
+ var expectedLineCount = cm.lineCount() - 5;
+ helpers.doKeys('2', 'c', '3', 'c');
+ eq(expectedLineCount, cm.lineCount());
+ var register = helpers.getRegisterController().getRegister();
+ eq(expectedBuffer, register.toString());
+ is(register.linewise);
+ eq('vim-insert', cm.getOption('keyMap'));
+});
+testVim('ct', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('c', 't', 'w');
+ eq(' word1 word3', cm.getValue());
+ helpers.doKeys('<Esc>', 'c', '|');
+ eq(' word3', cm.getValue());
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('<Esc>', '2', 'u', 'w', 'h');
+ helpers.doKeys('c', '2', 'g', 'e');
+ eq(' wordword3', cm.getValue());
+}, { value: ' word1 word2 word3'});
+testVim('cc_should_not_append_to_document', function(cm, vim, helpers) {
+ var expectedLineCount = cm.lineCount();
+ cm.setCursor(cm.lastLine(), 0);
+ helpers.doKeys('c', 'c');
+ eq(expectedLineCount, cm.lineCount());
+});
+function fillArray(val, times) {
+ var arr = [];
+ for (var i = 0; i < times; i++) {
+ arr.push(val);
+ }
+ return arr;
+}
+testVim('c_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'l', 'l', 'c');
+ helpers.doKeys('hello');
+ eq('1hello\n5hello\nahellofg', cm.getValue());
+ helpers.doKeys('<Esc>');
+ cm.setCursor(2, 3);
+ helpers.doKeys('<C-v>', '2', 'k', 'h', 'C');
+ helpers.doKeys('world');
+ eq('1hworld\n5hworld\nahworld', cm.getValue());
+}, {value: '1234\n5678\nabcdefg'});
+testVim('c_visual_block_replay', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'c');
+ helpers.doKeys('fo');
+ eq('1fo4\n5fo8\nafodefg', cm.getValue());
+ helpers.doKeys('<Esc>');
+ cm.setCursor(0, 0);
+ helpers.doKeys('.');
+ eq('foo4\nfoo8\nfoodefg', cm.getValue());
+}, {value: '1234\n5678\nabcdefg'});
+testVim('I_visual_block_replay', function(cm, vim, helpers) {
+ cm.setCursor(0, 2);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'I');
+ helpers.doKeys('+-')
+ eq('12+-34\n56+-78\nab+-cdefg\nxyz', cm.getValue());
+ helpers.doKeys('<Esc>');
+ // ensure that repeat location doesn't depend on last selection
+ cm.setCursor(3, 2);
+ helpers.doKeys('g', 'v')
+ eq("+-34\n+-78\n+-cd", cm.getSelection())
+ cm.setCursor(0, 3);
+ helpers.doKeys('<C-v>', '1', 'j', '2', 'l');
+ eq("-34\n-78", cm.getSelection());
+ cm.setCursor(0, 0);
+ eq("", cm.getSelection());
+ helpers.doKeys('g', 'v');
+ eq("-34\n-78", cm.getSelection());
+ cm.setCursor(1, 1);
+ helpers.doKeys('.');
+ eq('12+-34\n5+-6+-78\na+-b+-cdefg\nx+-yz', cm.getValue());
+}, {value: '1234\n5678\nabcdefg\nxyz'});
+
+testVim('d_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'l', 'l', 'd');
+ eq('1\n5\nafg', cm.getValue());
+}, {value: '1234\n5678\nabcdefg'});
+testVim('D_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'D');
+ eq('1\n5\na', cm.getValue());
+}, {value: '1234\n5678\nabcdefg'});
+
+testVim('s_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'l', 'l', 's');
+ helpers.doKeys('hello{');
+ eq('1hello{\n5hello{\nahello{fg\n', cm.getValue());
+ helpers.doKeys('<Esc>');
+ cm.setCursor(2, 3);
+ helpers.doKeys('<C-v>', '1', 'k', 'h', 'S');
+ helpers.doKeys('world');
+ eq('1hello{\n world\n', cm.getValue());
+}, {value: '1234\n5678\nabcdefg\n'});
+
+// Swapcase commands edit in place and do not modify registers.
+testVim('g~w_repeat', function(cm, vim, helpers) {
+ // Assert that dw does delete newline if it should go to the next line, and
+ // that repeat works properly.
+ var curStart = makeCursor(0, 1);
+ cm.setCursor(curStart);
+ helpers.doKeys('g', '~', '2', 'w');
+ eq(' WORD1\nWORD2', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1\nword2' });
+testVim('g~g~', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ var expectedLineCount = cm.lineCount();
+ var expectedValue = cm.getValue().toUpperCase();
+ helpers.doKeys('2', 'g', '~', '3', 'g', '~');
+ eq(expectedValue, cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+}, { value: ' word1\nword2\nword3\nword4\nword5\nword6' });
+testVim('gu_and_gU', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 7);
+ var value = cm.getValue();
+ cm.setCursor(curStart);
+ helpers.doKeys('2', 'g', 'U', 'w');
+ eq(cm.getValue(), 'wa wb xX WC wd');
+ eqCursorPos(curStart, cm.getCursor());
+ helpers.doKeys('2', 'g', 'u', 'w');
+ eq(cm.getValue(), value);
+
+ helpers.doKeys('2', 'g', 'U', 'B');
+ eq(cm.getValue(), 'wa WB Xx wc wd');
+ eqCursorPos(makeCursor(0, 3), cm.getCursor());
+
+ cm.setCursor(makeCursor(0, 4));
+ helpers.doKeys('g', 'u', 'i', 'w');
+ eq(cm.getValue(), 'wa wb Xx wc wd');
+ eqCursorPos(makeCursor(0, 3), cm.getCursor());
+
+ // TODO: support gUgU guu
+ // eqCursorPos(makeCursor(0, 0), cm.getCursor());
+
+ var register = helpers.getRegisterController().getRegister();
+ eq('', register.toString());
+ is(!register.linewise);
+}, { value: 'wa wb xx wc wd' });
+testVim('visual_block_~', function(cm, vim, helpers) {
+ cm.setCursor(1, 1);
+ helpers.doKeys('<C-v>', 'l', 'l', 'j', '~');
+ helpers.assertCursorAt(1, 1);
+ eq('hello\nwoRLd\naBCDe', cm.getValue());
+ cm.setCursor(2, 0);
+ helpers.doKeys('v', 'l', 'l', '~');
+ helpers.assertCursorAt(2, 0);
+ eq('hello\nwoRLd\nAbcDe', cm.getValue());
+},{value: 'hello\nwOrld\nabcde' });
+testVim('._swapCase_visualBlock', function(cm, vim, helpers) {
+ helpers.doKeys('<C-v>', 'j', 'j', 'l', '~');
+ cm.setCursor(0, 3);
+ helpers.doKeys('.');
+ eq('HelLO\nWorLd\nAbcdE', cm.getValue());
+},{value: 'hEllo\nwOrlD\naBcDe' });
+testVim('._delete_visualBlock', function(cm, vim, helpers) {
+ helpers.doKeys('<C-v>', 'j', 'x');
+ eq('ive\ne\nsome\nsugar', cm.getValue());
+ helpers.doKeys('.');
+ eq('ve\n\nsome\nsugar', cm.getValue());
+ helpers.doKeys('j', 'j', '.');
+ eq('ve\n\nome\nugar', cm.getValue());
+ helpers.doKeys('u', '<C-r>', '.');
+ eq('ve\n\nme\ngar', cm.getValue());
+},{value: 'give\nme\nsome\nsugar' });
+testVim('>{motion}', function(cm, vim, helpers) {
+ cm.setCursor(1, 3);
+ var expectedLineCount = cm.lineCount();
+ var expectedValue = ' word1\n word2\nword3 ';
+ helpers.doKeys('>', 'k');
+ eq(expectedValue, cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 3);
+}, { value: ' word1\nword2\nword3 ', indentUnit: 2 });
+testVim('>>', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedLineCount = cm.lineCount();
+ var expectedValue = ' word1\n word2\nword3 ';
+ helpers.doKeys('2', '>', '>');
+ eq(expectedValue, cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 3);
+}, { value: ' word1\nword2\nword3 ', indentUnit: 2 });
+testVim('<{motion}', function(cm, vim, helpers) {
+ cm.setCursor(1, 3);
+ var expectedLineCount = cm.lineCount();
+ var expectedValue = ' word1\nword2\nword3 ';
+ helpers.doKeys('<', 'k');
+ eq(expectedValue, cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 1);
+}, { value: ' word1\n word2\nword3 ', indentUnit: 2 });
+testVim('<<', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedLineCount = cm.lineCount();
+ var expectedValue = ' word1\nword2\nword3 ';
+ helpers.doKeys('2', '<', '<');
+ eq(expectedValue, cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 1);
+}, { value: ' word1\n word2\nword3 ', indentUnit: 2 });
+testVim('=', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ helpers.doKeys('<C-v>', 'j', 'j');
+ var expectedValue = 'word1\nword2\nword3';
+ helpers.doKeys('=');
+ eq(expectedValue, cm.getValue());
+}, { value: ' word1\n word2\n word3', indentUnit: 2 });
+
+// Edit tests
+function testEdit(name, before, pos, edit, after) {
+ return testVim(name, function(cm, vim, helpers) {
+ var ch = before.search(pos)
+ var line = before.substring(0, ch).split('\n').length - 1;
+ if (line) {
+ ch = before.substring(0, ch).split('\n').pop().length;
+ }
+ cm.setCursor(line, ch);
+ helpers.doKeys.apply(this, edit.split(''));
+ eq(after, cm.getValue());
+ }, {value: before});
+}
+
+// These Delete tests effectively cover word-wise Change, Visual & Yank.
+// Tabs are used as differentiated whitespace to catch edge cases.
+// Normal word:
+testEdit('diw_mid_spc', 'foo \tbAr\t baz', /A/, 'diw', 'foo \t\t baz');
+testEdit('daw_mid_spc', 'foo \tbAr\t baz', /A/, 'daw', 'foo \tbaz');
+testEdit('diw_mid_punct', 'foo \tbAr.\t baz', /A/, 'diw', 'foo \t.\t baz');
+testEdit('daw_mid_punct', 'foo \tbAr.\t baz', /A/, 'daw', 'foo.\t baz');
+testEdit('diw_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'diw', 'foo \t,.\t baz');
+testEdit('daw_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'daw', 'foo \t,.\t baz');
+testEdit('diw_start_spc', 'bAr \tbaz', /A/, 'diw', ' \tbaz');
+testEdit('daw_start_spc', 'bAr \tbaz', /A/, 'daw', 'baz');
+testEdit('diw_start_punct', 'bAr. \tbaz', /A/, 'diw', '. \tbaz');
+testEdit('daw_start_punct', 'bAr. \tbaz', /A/, 'daw', '. \tbaz');
+testEdit('diw_end_spc', 'foo \tbAr', /A/, 'diw', 'foo \t');
+testEdit('daw_end_spc', 'foo \tbAr', /A/, 'daw', 'foo');
+testEdit('diw_end_punct', 'foo \tbAr.', /A/, 'diw', 'foo \t.');
+testEdit('daw_end_punct', 'foo \tbAr.', /A/, 'daw', 'foo.');
+// Big word:
+testEdit('diW_mid_spc', 'foo \tbAr\t baz', /A/, 'diW', 'foo \t\t baz');
+testEdit('daW_mid_spc', 'foo \tbAr\t baz', /A/, 'daW', 'foo \tbaz');
+testEdit('diW_mid_punct', 'foo \tbAr.\t baz', /A/, 'diW', 'foo \t\t baz');
+testEdit('daW_mid_punct', 'foo \tbAr.\t baz', /A/, 'daW', 'foo \tbaz');
+testEdit('diW_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'diW', 'foo \t\t baz');
+testEdit('daW_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'daW', 'foo \tbaz');
+testEdit('diW_start_spc', 'bAr\t baz', /A/, 'diW', '\t baz');
+testEdit('daW_start_spc', 'bAr\t baz', /A/, 'daW', 'baz');
+testEdit('diW_start_punct', 'bAr.\t baz', /A/, 'diW', '\t baz');
+testEdit('daW_start_punct', 'bAr.\t baz', /A/, 'daW', 'baz');
+testEdit('diW_end_spc', 'foo \tbAr', /A/, 'diW', 'foo \t');
+testEdit('daW_end_spc', 'foo \tbAr', /A/, 'daW', 'foo');
+testEdit('diW_end_punct', 'foo \tbAr.', /A/, 'diW', 'foo \t');
+testEdit('daW_end_punct', 'foo \tbAr.', /A/, 'daW', 'foo');
+// Deleting text objects
+// Open and close on same line
+testEdit('di(_open_spc', 'foo (bAr) baz', /\(/, 'di(', 'foo () baz');
+testEdit('di)_open_spc', 'foo (bAr) baz', /\(/, 'di)', 'foo () baz');
+testEdit('dib_open_spc', 'foo (bAr) baz', /\(/, 'dib', 'foo () baz');
+testEdit('da(_open_spc', 'foo (bAr) baz', /\(/, 'da(', 'foo baz');
+testEdit('da)_open_spc', 'foo (bAr) baz', /\(/, 'da)', 'foo baz');
+
+testEdit('di(_middle_spc', 'foo (bAr) baz', /A/, 'di(', 'foo () baz');
+testEdit('di)_middle_spc', 'foo (bAr) baz', /A/, 'di)', 'foo () baz');
+testEdit('da(_middle_spc', 'foo (bAr) baz', /A/, 'da(', 'foo baz');
+testEdit('da)_middle_spc', 'foo (bAr) baz', /A/, 'da)', 'foo baz');
+
+testEdit('di(_close_spc', 'foo (bAr) baz', /\)/, 'di(', 'foo () baz');
+testEdit('di)_close_spc', 'foo (bAr) baz', /\)/, 'di)', 'foo () baz');
+testEdit('da(_close_spc', 'foo (bAr) baz', /\)/, 'da(', 'foo baz');
+testEdit('da)_close_spc', 'foo (bAr) baz', /\)/, 'da)', 'foo baz');
+
+testEdit('di`', 'foo `bAr` baz', /`/, 'di`', 'foo `` baz');
+testEdit('di>', 'foo <bAr> baz', /</, 'di>', 'foo <> baz');
+testEdit('da<', 'foo <bAr> baz', /</, 'da<', 'foo baz');
+
+// delete around and inner b.
+testEdit('dab_on_(_should_delete_around_()block', 'o( in(abc) )', /\(a/, 'dab', 'o( in )');
+
+// delete around and inner B.
+testEdit('daB_on_{_should_delete_around_{}block', 'o{ in{abc} }', /{a/, 'daB', 'o{ in }');
+testEdit('diB_on_{_should_delete_inner_{}block', 'o{ in{abc} }', /{a/, 'diB', 'o{ in{} }');
+
+testEdit('da{_on_{_should_delete_inner_block', 'o{ in{abc} }', /{a/, 'da{', 'o{ in }');
+testEdit('di[_on_(_should_not_delete', 'foo (bAr) baz', /\(/, 'di[', 'foo (bAr) baz');
+testEdit('di[_on_)_should_not_delete', 'foo (bAr) baz', /\)/, 'di[', 'foo (bAr) baz');
+testEdit('da[_on_(_should_not_delete', 'foo (bAr) baz', /\(/, 'da[', 'foo (bAr) baz');
+testEdit('da[_on_)_should_not_delete', 'foo (bAr) baz', /\)/, 'da[', 'foo (bAr) baz');
+testMotion('di(_outside_should_stay', ['d', 'i', '('], new Pos(0, 0), new Pos(0, 0));
+
+// Open and close on different lines, equally indented
+testEdit('di{_middle_spc', 'a{\n\tbar\n}b', /r/, 'di{', 'a{}b');
+testEdit('di}_middle_spc', 'a{\n\tbar\n}b', /r/, 'di}', 'a{}b');
+testEdit('da{_middle_spc', 'a{\n\tbar\n}b', /r/, 'da{', 'ab');
+testEdit('da}_middle_spc', 'a{\n\tbar\n}b', /r/, 'da}', 'ab');
+testEdit('daB_middle_spc', 'a{\n\tbar\n}b', /r/, 'daB', 'ab');
+
+// open and close on diff lines, open indented less than close
+testEdit('di{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di{', 'a{}b');
+testEdit('di}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di}', 'a{}b');
+testEdit('da{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da{', 'ab');
+testEdit('da}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da}', 'ab');
+
+// open and close on diff lines, open indented more than close
+testEdit('di[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di[', 'a\t[]b');
+testEdit('di]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di]', 'a\t[]b');
+testEdit('da[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da[', 'a\tb');
+testEdit('da]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da]', 'a\tb');
+
+// open and close on diff lines, open indented more than close
+testEdit('di<_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'di<', 'a\t<>b');
+testEdit('di>_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'di>', 'a\t<>b');
+testEdit('da<_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'da<', 'a\tb');
+testEdit('da>_middle_spc', 'a\t<\n\tbar\n>b', /r/, 'da>', 'a\tb');
+
+function testSelection(name, before, pos, keys, sel) {
+ return testVim(name, function(cm, vim, helpers) {
+ var ch = before.search(pos)
+ var line = before.substring(0, ch).split('\n').length - 1;
+ if (line) {
+ ch = before.substring(0, ch).split('\n').pop().length;
+ }
+ cm.setCursor(line, ch);
+ helpers.doKeys.apply(this, keys.split(''));
+ eq(sel, cm.getSelection());
+ }, {value: before});
+}
+testSelection('viw_middle_spc', 'foo \tbAr\t baz', /A/, 'viw', 'bAr');
+testSelection('vaw_middle_spc', 'foo \tbAr\t baz', /A/, 'vaw', 'bAr\t ');
+testSelection('viw_middle_punct', 'foo \tbAr,\t baz', /A/, 'viw', 'bAr');
+testSelection('vaW_middle_punct', 'foo \tbAr,\t baz', /A/, 'vaW', 'bAr,\t ');
+testSelection('viw_start_spc', 'foo \tbAr\t baz', /b/, 'viw', 'bAr');
+testSelection('viw_end_spc', 'foo \tbAr\t baz', /r/, 'viw', 'bAr');
+testSelection('viw_eol', 'foo \tbAr', /r/, 'viw', 'bAr');
+testSelection('vi{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'vi{', '\n\tbar\n\t');
+testSelection('va{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'va{', '{\n\tbar\n\t}');
+
+testVim('mouse_select', function(cm, vim, helpers) {
+ cm.setSelection(Pos(0, 2), Pos(0, 4), {origin: '*mouse'});
+ is(cm.state.vim.visualMode);
+ is(!cm.state.vim.visualLine);
+ is(!cm.state.vim.visualBlock);
+ helpers.doKeys('<Esc>');
+ is(!cm.somethingSelected());
+ helpers.doKeys('g', 'v');
+ eq('cd', cm.getSelection());
+}, {value: 'abcdef'});
+
+// Operator-motion tests
+testVim('D', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ helpers.doKeys('D');
+ eq(' wo\nword2\n word3', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('rd1', register.toString());
+ is(!register.linewise);
+ helpers.assertCursorAt(0, 2);
+}, { value: ' word1\nword2\n word3' });
+testVim('C', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('C');
+ eq(' wo\nword2\n word3', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('rd1', register.toString());
+ is(!register.linewise);
+ eqCursorPos(curStart, cm.getCursor());
+ eq('vim-insert', cm.getOption('keyMap'));
+}, { value: ' word1\nword2\n word3' });
+testVim('Y', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('Y');
+ eq(' word1\nword2\n word3', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq(' word1\n', register.toString());
+ is(register.linewise);
+ helpers.assertCursorAt(0, 3);
+}, { value: ' word1\nword2\n word3' });
+testVim('Yy_blockwise', function(cm, vim, helpers) {
+ helpers.doKeys('<C-v>', 'j', '2', 'l', 'Y');
+ helpers.doKeys('G', 'p', 'g', 'g');
+ helpers.doKeys('<C-v>', 'j', '2', 'l', 'y');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('$', 'p');
+ eq('123456123\n123456123\n123456\n123456', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('123\n123', register.toString());
+ is(register.blockwise);
+ helpers.assertCursorAt(0, 6);
+ helpers.doKeys('$', 'j', 'p');
+ helpers.doKeys('$', 'j', 'P');
+ eq("123456123\n123456123123\n123456 121233\n123456 123", cm.getValue());
+}, { value: '123456\n123456\n' });
+testVim('~', function(cm, vim, helpers) {
+ helpers.doKeys('3', '~');
+ eq('ABCdefg', cm.getValue());
+ helpers.assertCursorAt(0, 3);
+}, { value: 'abcdefg' });
+
+// Action tests
+testVim('ctrl-a', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-a>');
+ eq('-9', cm.getValue());
+ helpers.assertCursorAt(0, 1);
+ helpers.doKeys('2','<C-a>');
+ eq('-7', cm.getValue());
+}, {value: '-10'});
+testVim('ctrl-x', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-x>');
+ eq('-1', cm.getValue());
+ helpers.assertCursorAt(0, 1);
+ helpers.doKeys('2','<C-x>');
+ eq('-3', cm.getValue());
+}, {value: '0'});
+testVim('<C-x>/<C-a> search forward', function(cm, vim, helpers) {
+ forEach(['<C-x>', '<C-a>'], function(key) {
+ cm.setCursor(0, 0);
+ helpers.doKeys(key);
+ helpers.assertCursorAt(0, 5);
+ helpers.doKeys('l');
+ helpers.doKeys(key);
+ helpers.assertCursorAt(0, 10);
+ cm.setCursor(0, 11);
+ helpers.doKeys(key);
+ helpers.assertCursorAt(0, 11);
+ });
+}, {value: '__jmp1 jmp2 jmp'});
+testVim('insert_ctrl_w', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 10);
+ cm.setCursor(curStart);
+ helpers.doKeys('a');
+ helpers.doKeys('<C-w>');
+ eq('word1/', cm.getValue());
+ var register = helpers.getRegisterController().getRegister();
+ eq('word2', register.toString());
+ is(!register.linewise);
+ var curEnd = makeCursor(0, 6);
+ eqCursorPos(curEnd, cm.getCursor());
+ eq('vim-insert', cm.getOption('keyMap'));
+}, { value: 'word1/word2' });
+testVim('normal_ctrl_w', function(cm, vim, helpers) {
+ var curStart = makeCursor(0, 3);
+ cm.setCursor(curStart);
+ helpers.doKeys('<C-w>');
+ eq('word', cm.getValue());
+ var curEnd = makeCursor(0, 3);
+ helpers.assertCursorAt(0,3);
+ eqCursorPos(curEnd, cm.getCursor());
+ eq('vim', cm.getOption('keyMap'));
+}, {value: 'word'});
+testVim('a', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('a');
+ helpers.assertCursorAt(0, 2);
+ eq('vim-insert', cm.getOption('keyMap'));
+});
+testVim('a_eol', function(cm, vim, helpers) {
+ cm.setCursor(0, lines[0].length - 1);
+ helpers.doKeys('a');
+ helpers.assertCursorAt(0, lines[0].length);
+ eq('vim-insert', cm.getOption('keyMap'));
+});
+testVim('A_endOfSelectedArea', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('v', 'j', 'l');
+ helpers.doKeys('A');
+ helpers.assertCursorAt(1, 2);
+ eq('vim-insert', cm.getOption('keyMap'));
+}, {value: 'foo\nbar'});
+testVim('i', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('i');
+ helpers.assertCursorAt(0, 1);
+ eq('vim-insert', cm.getOption('keyMap'));
+});
+testVim('i_repeat', function(cm, vim, helpers) {
+ helpers.doKeys('3', 'i');
+ helpers.doKeys('test')
+ helpers.doKeys('<Esc>');
+ eq('testtesttest', cm.getValue());
+ helpers.assertCursorAt(0, 11);
+}, { value: '' });
+testVim('i_repeat_delete', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('2', 'i');
+ helpers.doKeys('z')
+ helpers.doInsertModeKeys('Backspace', 'Backspace');
+ helpers.doKeys('<Esc>');
+ eq('abe', cm.getValue());
+ helpers.assertCursorAt(0, 1);
+}, { value: 'abcde' });
+testVim('insert', function(cm, vim, helpers) {
+ helpers.doKeys('i');
+ eq('vim-insert', cm.getOption('keyMap'));
+ eq(false, cm.state.overwrite);
+ helpers.doKeys('<Ins>');
+ eq('vim-replace', cm.getOption('keyMap'));
+ eq(true, cm.state.overwrite);
+ helpers.doKeys('<Ins>');
+ eq('vim-insert', cm.getOption('keyMap'));
+ eq(false, cm.state.overwrite);
+});
+testVim('i_backspace', function(cm, vim, helpers) {
+ cm.setCursor(0, 10);
+ helpers.doKeys('i');
+ helpers.doInsertModeKeys('Backspace');
+ helpers.assertCursorAt(0, 9);
+ eq('012345678', cm.getValue());
+}, { value: '0123456789'});
+testVim('i_overwrite_backspace', function(cm, vim, helpers) {
+ cm.setCursor(0, 10);
+ helpers.doKeys('i');
+ helpers.doKeys('<Ins>');
+ helpers.doInsertModeKeys('Backspace');
+ helpers.assertCursorAt(Pos(0, 9, "after"));
+ eq('0123456789', cm.getValue());
+}, { value: '0123456789'});
+testVim('i_forward_delete', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ helpers.doKeys('i');
+ helpers.doInsertModeKeys('Delete');
+ helpers.assertCursorAt(0, 3);
+ eq('A124\nBCD', cm.getValue());
+ helpers.doInsertModeKeys('Delete');
+ helpers.assertCursorAt(0, 3);
+ eq('A12\nBCD', cm.getValue());
+ helpers.doInsertModeKeys('Delete');
+ helpers.assertCursorAt(0, 3);
+ eq('A12BCD', cm.getValue());
+}, { value: 'A1234\nBCD'});
+testVim('forward_delete', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ helpers.doInsertModeKeys('Delete');
+ helpers.assertCursorAt(0, 3);
+ eq('A124\nBCD', cm.getValue());
+ helpers.doInsertModeKeys('Delete');
+ helpers.assertCursorAt(0, 2);
+ eq('A12\nBCD', cm.getValue());
+ helpers.doInsertModeKeys('Delete');
+ helpers.assertCursorAt(0, 1);
+ eq('A1\nBCD', cm.getValue());
+}, { value: 'A1234\nBCD'});
+testVim('A', function(cm, vim, helpers) {
+ helpers.doKeys('A');
+ helpers.assertCursorAt(0, lines[0].length);
+ eq('vim-insert', cm.getOption('keyMap'));
+});
+testVim('A_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'l', 'A');
+ helpers.doKeys('hello');
+ eq('testhello\nmehello\npleahellose', cm.getValue());
+ helpers.doKeys('<Esc>');
+ cm.setCursor(0, 0);
+ helpers.doKeys('.');
+ // TODO this doesn't work yet
+ // eq('teshellothello\nme hello hello\nplehelloahellose', cm.getValue());
+}, {value: 'test\nme\nplease'});
+testVim('I', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('I');
+ helpers.assertCursorAt(0, lines[0].textStart);
+ eq('vim-insert', cm.getOption('keyMap'));
+});
+testVim('I_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('3', 'I');
+ helpers.doKeys('test')
+ helpers.doKeys('<Esc>');
+ eq('testtesttestblah', cm.getValue());
+ helpers.assertCursorAt(0, 11);
+}, { value: 'blah' });
+testVim('I_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'l', 'I');
+ helpers.doKeys('hello');
+ eq('hellotest\nhellome\nhelloplease', cm.getValue());
+}, {value: 'test\nme\nplease'});
+testVim('o', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('o');
+ eq('word1\n\nword2', cm.getValue());
+ helpers.assertCursorAt(1, 0);
+ eq('vim-insert', cm.getOption('keyMap'));
+}, { value: 'word1\nword2' });
+testVim('o_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('3', 'o');
+ helpers.doKeys('test')
+ helpers.doKeys('<Esc>');
+ eq('\ntest\ntest\ntest', cm.getValue());
+ helpers.assertCursorAt(3, 3);
+}, { value: '' });
+testVim('O', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('O');
+ eq('\nword1\nword2', cm.getValue());
+ helpers.assertCursorAt(0, 0);
+ eq('vim-insert', cm.getOption('keyMap'));
+}, { value: 'word1\nword2' });
+testVim('J', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('J');
+ var expectedValue = 'word1 word2\nword3\n word4';
+ eq(expectedValue, cm.getValue());
+ helpers.assertCursorAt(0, expectedValue.indexOf('word2') - 1);
+}, { value: 'word1 \n word2\nword3\n word4' });
+testVim('J_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('3', 'J');
+ var expectedValue = 'word1 word2 word3\n word4';
+ eq(expectedValue, cm.getValue());
+ helpers.assertCursorAt(0, expectedValue.indexOf('word3') - 1);
+}, { value: 'word1 \n word2\nword3\n word4' });
+testVim('p', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false);
+ helpers.doKeys('p');
+ eq('__abc\ndef_', cm.getValue());
+ helpers.assertCursorAt(1, 2);
+}, { value: '___' });
+testVim('p_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.getRegisterController().getRegister('a').setText('abc\ndef', false);
+ helpers.doKeys('"', 'a', 'p');
+ eq('__abc\ndef_', cm.getValue());
+ helpers.assertCursorAt(1, 2);
+}, { value: '___' });
+testVim('p_wrong_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.getRegisterController().getRegister('a').setText('abc\ndef', false);
+ helpers.doKeys('p');
+ eq('___', cm.getValue());
+ helpers.assertCursorAt(0, 1);
+}, { value: '___' });
+testVim('p_line', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true);
+ helpers.doKeys('2', 'p');
+ eq('___\n a\nd\n a\nd', cm.getValue());
+ helpers.assertCursorAt(1, 2);
+}, { value: '___' });
+testVim('p_lastline', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.getRegisterController().pushText('"', 'yank', ' a\nd', true);
+ helpers.doKeys('2', 'p');
+ eq('___\n a\nd\n a\nd', cm.getValue());
+ helpers.assertCursorAt(1, 2);
+}, { value: '___' });
+testVim(']p_first_indent_is_smaller', function(cm, vim, helpers) {
+ helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true);
+ helpers.doKeys(']', 'p');
+ eq(' ___\n abc\n def', cm.getValue());
+}, { value: ' ___' });
+testVim(']p_first_indent_is_larger', function(cm, vim, helpers) {
+ helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true);
+ helpers.doKeys(']', 'p');
+ eq(' ___\n abc\ndef', cm.getValue());
+}, { value: ' ___' });
+testVim(']p_with_tab_indents', function(cm, vim, helpers) {
+ helpers.getRegisterController().pushText('"', 'yank', '\t\tabc\n\t\t\tdef\n', true);
+ helpers.doKeys(']', 'p');
+ eq('\t___\n\tabc\n\t\tdef', cm.getValue());
+}, { value: '\t___', indentWithTabs: true});
+testVim(']p_with_spaces_translated_to_tabs', function(cm, vim, helpers) {
+ helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true);
+ helpers.doKeys(']', 'p');
+ eq('\t___\n\tabc\n\t\tdef', cm.getValue());
+}, { value: '\t___', indentWithTabs: true, tabSize: 2 });
+testVim('[p', function(cm, vim, helpers) {
+ helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true);
+ helpers.doKeys('[', 'p');
+ eq(' abc\n def\n ___', cm.getValue());
+}, { value: ' ___' });
+testVim('P', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false);
+ helpers.doKeys('P');
+ eq('_abc\ndef__', cm.getValue());
+ helpers.assertCursorAt(1, 3);
+}, { value: '___' });
+testVim('P_line', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true);
+ helpers.doKeys('2', 'P');
+ eq(' a\nd\n a\nd\n___', cm.getValue());
+ helpers.assertCursorAt(0, 2);
+}, { value: '___' });
+testVim('r', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('3', 'r', 'u');
+ eq('wuuuet\nanother', cm.getValue(),'3r failed');
+ helpers.assertCursorAt(0, 3);
+ cm.setCursor(0, 4);
+ helpers.doKeys('v', 'j', 'h', 'r', '<Space>');
+ eq('wuuu \n her', cm.getValue(),'Replacing selection by space-characters failed');
+ cm.setValue("ox");
+ helpers.doKeys('r', '<C-c>');
+ eq('ox', cm.getValue());
+ helpers.doKeys('r', '<Del>');
+ eq('ox', cm.getValue());
+ helpers.doKeys('r', '<CR>');
+ eq('\nx', cm.getValue());
+}, { value: 'wordet\nanother' });
+testVim('r_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(2, 3);
+ helpers.doKeys('<C-v>', 'k', 'k', 'h', 'h', 'r', 'l');
+ eq('1lll\n5lll\nalllefg', cm.getValue());
+ helpers.doKeys('<C-v>', 'l', 'j', 'r', '<Space>');
+ eq('1 l\n5 l\nalllefg', cm.getValue());
+ cm.setCursor(2, 0);
+ helpers.doKeys('o');
+ helpers.doKeys('\t\t')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('<C-v>', 'h', 'h', 'r', 'r');
+ eq('1 l\n5 l\nalllefg\nrrrrrrrr', cm.getValue());
+}, {value: '1234\n5678\nabcdefg'});
+testVim('R', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('R');
+ helpers.assertCursorAt(0, 1);
+ eq('vim-replace', cm.getOption('keyMap'));
+ is(cm.state.overwrite, 'Setting overwrite state failed');
+});
+testVim('mark', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 't');
+ cm.setCursor(0, 0);
+ helpers.doKeys('`', 't');
+ helpers.assertCursorAt(2, 2);
+ cm.setCursor(2, 0);
+ cm.replaceRange(' h', cm.getCursor());
+ cm.setCursor(0, 0);
+ helpers.doKeys('\'', 't');
+ helpers.assertCursorAt(2, 3);
+});
+testVim('mark\'', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ cm.setCursor(0, 0);
+ helpers.doKeys('`', '\'');
+ helpers.assertCursorAt(2, 2);
+ cm.setCursor(2, 0);
+ cm.replaceRange(' h', cm.getCursor());
+ cm.setCursor(0, 0);
+ helpers.doKeys('\'', '\'');
+ helpers.assertCursorAt(2, 3);
+});
+testVim('mark.', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('O', 'testing', '<Esc>');
+ cm.setCursor(3, 3);
+ helpers.doKeys('\'', '.');
+ helpers.assertCursorAt(0, 0);
+ cm.setCursor(4, 4);
+ helpers.doKeys('`', '.');
+ helpers.assertCursorAt(0, 6);
+});
+testVim('jumpToMark_next', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 't');
+ cm.setCursor(0, 0);
+ helpers.doKeys(']', '`');
+ helpers.assertCursorAt(2, 2);
+ cm.setCursor(0, 0);
+ helpers.doKeys(']', '\'');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('jumpToMark_next_repeat', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(3, 2);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(4, 2);
+ helpers.doKeys('m', 'c');
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', ']', '`');
+ helpers.assertCursorAt(3, 2);
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', ']', '\'');
+ helpers.assertCursorAt(3, 1);
+});
+testVim('jumpToMark_next_sameline', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(2, 4);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(2, 2);
+ helpers.doKeys(']', '`');
+ helpers.assertCursorAt(2, 4);
+});
+testVim('jumpToMark_next_onlyprev', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(4, 0);
+ helpers.doKeys(']', '`');
+ helpers.assertCursorAt(4, 0);
+});
+testVim('jumpToMark_next_nomark', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys(']', '`');
+ helpers.assertCursorAt(2, 2);
+ helpers.doKeys(']', '\'');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('jumpToMark_next_linewise_over', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(3, 4);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(2, 1);
+ helpers.doKeys(']', '\'');
+ helpers.assertCursorAt(3, 1);
+});
+testVim('jumpToMark_next_action', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 't');
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', ']', '`');
+ helpers.assertCursorAt(0, 0);
+ var actual = cm.getLine(0);
+ var expected = 'pop pop 0 1 2 3 4';
+ eq(actual, expected, "Deleting while jumping to the next mark failed.");
+});
+testVim('jumpToMark_next_line_action', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 't');
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', ']', '\'');
+ helpers.assertCursorAt(0, 1);
+ var actual = cm.getLine(0);
+ var expected = ' (a) [b] {c} '
+ eq(actual, expected, "Deleting while jumping to the next mark line failed.");
+});
+testVim('jumpToMark_prev', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 't');
+ cm.setCursor(4, 0);
+ helpers.doKeys('[', '`');
+ helpers.assertCursorAt(2, 2);
+ cm.setCursor(4, 0);
+ helpers.doKeys('[', '\'');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('jumpToMark_prev_repeat', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(3, 2);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(4, 2);
+ helpers.doKeys('m', 'c');
+ cm.setCursor(5, 0);
+ helpers.doKeys('2', '[', '`');
+ helpers.assertCursorAt(3, 2);
+ cm.setCursor(5, 0);
+ helpers.doKeys('2', '[', '\'');
+ helpers.assertCursorAt(3, 1);
+});
+testVim('jumpToMark_prev_sameline', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(2, 4);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(2, 2);
+ helpers.doKeys('[', '`');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('jumpToMark_prev_onlynext', function(cm, vim, helpers) {
+ cm.setCursor(4, 4);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(2, 0);
+ helpers.doKeys('[', '`');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('jumpToMark_prev_nomark', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('[', '`');
+ helpers.assertCursorAt(2, 2);
+ helpers.doKeys('[', '\'');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('jumpToMark_prev_linewise_over', function(cm, vim, helpers) {
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(3, 4);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(3, 6);
+ helpers.doKeys('[', '\'');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('delmark_single', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('m', 't');
+ helpers.doEx('delmarks t');
+ cm.setCursor(0, 0);
+ helpers.doKeys('`', 't');
+ helpers.assertCursorAt(0, 0);
+});
+testVim('delmark_range', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(3, 2);
+ helpers.doKeys('m', 'c');
+ cm.setCursor(4, 2);
+ helpers.doKeys('m', 'd');
+ cm.setCursor(5, 2);
+ helpers.doKeys('m', 'e');
+ helpers.doEx('delmarks b-d');
+ cm.setCursor(0, 0);
+ helpers.doKeys('`', 'a');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'b');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'c');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'd');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'e');
+ helpers.assertCursorAt(5, 2);
+});
+testVim('delmark_multi', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(3, 2);
+ helpers.doKeys('m', 'c');
+ cm.setCursor(4, 2);
+ helpers.doKeys('m', 'd');
+ cm.setCursor(5, 2);
+ helpers.doKeys('m', 'e');
+ helpers.doEx('delmarks bcd');
+ cm.setCursor(0, 0);
+ helpers.doKeys('`', 'a');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'b');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'c');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'd');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'e');
+ helpers.assertCursorAt(5, 2);
+});
+testVim('delmark_multi_space', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(3, 2);
+ helpers.doKeys('m', 'c');
+ cm.setCursor(4, 2);
+ helpers.doKeys('m', 'd');
+ cm.setCursor(5, 2);
+ helpers.doKeys('m', 'e');
+ helpers.doEx('delmarks b c d');
+ cm.setCursor(0, 0);
+ helpers.doKeys('`', 'a');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'b');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'c');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'd');
+ helpers.assertCursorAt(1, 2);
+ helpers.doKeys('`', 'e');
+ helpers.assertCursorAt(5, 2);
+});
+testVim('delmark_all', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(2, 2);
+ helpers.doKeys('m', 'b');
+ cm.setCursor(3, 2);
+ helpers.doKeys('m', 'c');
+ cm.setCursor(4, 2);
+ helpers.doKeys('m', 'd');
+ cm.setCursor(5, 2);
+ helpers.doKeys('m', 'e');
+ helpers.doEx('delmarks a b-de');
+ cm.setCursor(0, 0);
+ helpers.doKeys('`', 'a');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('`', 'b');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('`', 'c');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('`', 'd');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('`', 'e');
+ helpers.assertCursorAt(0, 0);
+});
+testVim('visual', function(cm, vim, helpers) {
+ helpers.doKeys('l', 'v', 'l', 'l');
+ helpers.assertCursorAt(0, 4);
+ eqCursorPos(makeCursor(0, 1), cm.getCursor('anchor'));
+ helpers.doKeys('d');
+ eq('15', cm.getValue());
+}, { value: '12345' });
+testVim('visual_yank', function(cm, vim, helpers) {
+ helpers.doKeys('v', '3', 'l', 'y');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('p');
+ eq('aa te test for yank', cm.getValue());
+}, { value: 'a test for yank' })
+testVim('visual_w', function(cm, vim, helpers) {
+ helpers.doKeys('v', 'w');
+ eq(cm.getSelection(), 'motion t');
+}, { value: 'motion test'});
+testVim('visual_initial_selection', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('v');
+ cm.getSelection('n');
+}, { value: 'init'});
+testVim('visual_crossover_left', function(cm, vim, helpers) {
+ cm.setCursor(0, 2);
+ helpers.doKeys('v', 'l', 'h', 'h');
+ cm.getSelection('ro');
+}, { value: 'cross'});
+testVim('visual_crossover_left', function(cm, vim, helpers) {
+ cm.setCursor(0, 2);
+ helpers.doKeys('v', 'h', 'l', 'l');
+ cm.getSelection('os');
+}, { value: 'cross'});
+testVim('visual_crossover_up', function(cm, vim, helpers) {
+ cm.setCursor(3, 2);
+ helpers.doKeys('v', 'j', 'k', 'k');
+ eqCursorPos(Pos(2, 2), cm.getCursor('head'));
+ eqCursorPos(Pos(3, 3), cm.getCursor('anchor'));
+ helpers.doKeys('k');
+ eqCursorPos(Pos(1, 2), cm.getCursor('head'));
+ eqCursorPos(Pos(3, 3), cm.getCursor('anchor'));
+}, { value: 'cross\ncross\ncross\ncross\ncross\n'});
+testVim('visual_crossover_down', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('v', 'k', 'j', 'j');
+ eqCursorPos(Pos(2, 3), cm.getCursor('head'));
+ eqCursorPos(Pos(1, 2), cm.getCursor('anchor'));
+ helpers.doKeys('j');
+ eqCursorPos(Pos(3, 3), cm.getCursor('head'));
+ eqCursorPos(Pos(1, 2), cm.getCursor('anchor'));
+}, { value: 'cross\ncross\ncross\ncross\ncross\n'});
+testVim('visual_exit', function(cm, vim, helpers) {
+ helpers.doKeys('<C-v>', 'l', 'j', 'j', '<Esc>');
+ eqCursorPos(cm.getCursor('anchor'), cm.getCursor('head'));
+ eq(vim.visualMode, false);
+}, { value: 'hello\nworld\nfoo' });
+testVim('visual_line', function(cm, vim, helpers) {
+ helpers.doKeys('l', 'V', 'l', 'j', 'j', 'd');
+ eq(' 4\n 5', cm.getValue());
+}, { value: ' 1\n 2\n 3\n 4\n 5' });
+testVim('visual_block_move_to_eol', function(cm, vim, helpers) {
+ // moveToEol should move all block cursors to end of line
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', 'G', '$');
+ var selections = cm.getSelections().join();
+ eq('123,45,6', selections);
+ // Checks that with cursor at Infinity, finding words backwards still works.
+ helpers.doKeys('2', 'k', 'b');
+ selections = cm.getSelections().join();
+ eq('1', selections);
+}, {value: '123\n45\n6'});
+testVim('visual_block_different_line_lengths', function(cm, vim, helpers) {
+ // test the block selection with lines of different length
+ // i.e. extending the selection
+ // till the end of the longest line.
+ helpers.doKeys('<C-v>', 'l', 'j', 'j', '6', 'l', 'd');
+ helpers.doKeys('d', 'd', 'd', 'd');
+ eq('', cm.getValue());
+}, {value: '1234\n5678\nabcdefg'});
+testVim('visual_block_truncate_on_short_line', function(cm, vim, helpers) {
+ // check for left side selection in case
+ // of moving up to a shorter line.
+ cm.replaceRange('', cm.getCursor());
+ cm.setCursor(3, 4);
+ helpers.doKeys('<C-v>', 'l', 'k', 'k', 'd');
+ eq('hello world\n{\ntis\nsa!', cm.getValue());
+}, {value: 'hello world\n{\nthis is\nsparta!'});
+testVim('visual_block_corners', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('<C-v>', '2', 'l', 'k');
+ // circle around the anchor
+ // and check the selections
+ var selections = cm.getSelections();
+ eq('345891', selections.join(''));
+ helpers.doKeys('4', 'h');
+ selections = cm.getSelections();
+ eq('123678', selections.join(''));
+ helpers.doKeys('j', 'j');
+ selections = cm.getSelections();
+ eq('678abc', selections.join(''));
+ helpers.doKeys('4', 'l');
+ selections = cm.getSelections();
+ eq('891cde', selections.join(''));
+}, {value: '12345\n67891\nabcde'});
+testVim('visual_block_mode_switch', function(cm, vim, helpers) {
+ // switch between visual modes
+ cm.setCursor(1, 1);
+ // blockwise to characterwise visual
+ helpers.doKeys('<C-v>', 'j', 'l', 'v');
+ var selections = cm.getSelections();
+ eq('7891\nabc', selections.join(''));
+ // characterwise to blockwise
+ helpers.doKeys('<C-v>');
+ selections = cm.getSelections();
+ eq('78bc', selections.join(''));
+ // blockwise to linewise visual
+ helpers.doKeys('V');
+ selections = cm.getSelections();
+ eq('67891\nabcde', selections.join(''));
+}, {value: '12345\n67891\nabcde'});
+testVim('visual_block_crossing_short_line', function(cm, vim, helpers) {
+ // visual block with long and short lines
+ cm.setCursor(0, 3);
+ helpers.doKeys('<C-v>', 'j', 'j', 'j');
+ var selections = cm.getSelections().join();
+ eq('4,,d,b', selections);
+ helpers.doKeys('3', 'k');
+ selections = cm.getSelections().join();
+ eq('4', selections);
+ helpers.doKeys('5', 'j', 'k');
+ selections = cm.getSelections().join("");
+ eq(10, selections.length);
+}, {value: '123456\n78\nabcdefg\nfoobar\n}\n'});
+testVim('visual_block_curPos_on_exit', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', '3' , 'l', '<Esc>');
+ eqCursorPos(makeCursor(0, 3), cm.getCursor());
+ helpers.doKeys('h', '<C-v>', '2' , 'j' ,'3' , 'l');
+ eq(cm.getSelections().join(), "3456,,cdef");
+ helpers.doKeys('4' , 'h');
+ eq(cm.getSelections().join(), "23,8,bc");
+ helpers.doKeys('2' , 'l');
+ eq(cm.getSelections().join(), "34,,cd");
+}, {value: '123456\n78\nabcdefg\nfoobar'});
+
+testVim('visual_marks', function(cm, vim, helpers) {
+ helpers.doKeys('l', 'v', 'l', 'l', 'j', 'j', 'v');
+ // Test visual mode marks
+ cm.setCursor(2, 1);
+ helpers.doKeys('\'', '<');
+ helpers.assertCursorAt(0, 1);
+ helpers.doKeys('\'', '>');
+ helpers.assertCursorAt(2, 0);
+});
+testVim('visual_join', function(cm, vim, helpers) {
+ helpers.doKeys('l', 'V', 'l', 'j', 'j', 'J');
+ eq(' 1 2 3\n 4\n 5', cm.getValue());
+ is(!vim.visualMode);
+}, { value: ' 1\n 2\n 3\n 4\n 5' });
+testVim('visual_join_2', function(cm, vim, helpers) {
+ helpers.doKeys('G', 'V', 'g', 'g', 'J');
+ eq('1 2 3 4 5 6 ', cm.getValue());
+ is(!vim.visualMode);
+}, { value: '1\n2\n3\n4\n5\n6\n'});
+testVim('visual_blank', function(cm, vim, helpers) {
+ helpers.doKeys('v', 'k');
+ eq(vim.visualMode, true);
+}, { value: '\n' });
+testVim('reselect_visual', function(cm, vim, helpers) {
+ helpers.doKeys('l', 'v', 'l', 'l', 'l', 'y', 'g', 'v');
+ helpers.assertCursorAt(0, 5);
+ eqCursorPos(makeCursor(0, 1), cm.getCursor('anchor'));
+ helpers.doKeys('v');
+ cm.setCursor(1, 0);
+ helpers.doKeys('v', 'l', 'l', 'p');
+ eq('123456\n2345\nbar', cm.getValue());
+ cm.setCursor(0, 0);
+ helpers.doKeys('g', 'v');
+ // here the fake cursor is at (1, 3)
+ helpers.assertCursorAt(1, 4);
+ eqCursorPos(makeCursor(1, 0), cm.getCursor('anchor'));
+ helpers.doKeys('v');
+ cm.setCursor(2, 0);
+ helpers.doKeys('v', 'l', 'l', 'g', 'v');
+ helpers.assertCursorAt(1, 4);
+ eqCursorPos(makeCursor(1, 0), cm.getCursor('anchor'));
+ helpers.doKeys('g', 'v');
+ helpers.assertCursorAt(2, 3);
+ eqCursorPos(makeCursor(2, 0), cm.getCursor('anchor'));
+ eq('123456\n2345\nbar', cm.getValue());
+}, { value: '123456\nfoo\nbar' });
+testVim('reselect_visual_line', function(cm, vim, helpers) {
+ helpers.doKeys('l', 'V', 'j', 'j', 'V', 'g', 'v', 'd');
+ eq('foo\nand\nbar', cm.getValue());
+ cm.setCursor(1, 0);
+ helpers.doKeys('V', 'y', 'j');
+ helpers.doKeys('V', 'p' , 'g', 'v', 'd');
+ eq('foo\nand', cm.getValue());
+}, { value: 'hello\nthis\nis\nfoo\nand\nbar' });
+testVim('reselect_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(1, 2);
+ helpers.doKeys('<C-v>', 'k', 'h', '<C-v>');
+ cm.setCursor(2, 1);
+ helpers.doKeys('v', 'l', 'g', 'v');
+ eqCursorPos(Pos(1, 2), vim.sel.anchor);
+ eqCursorPos(Pos(0, 1), vim.sel.head);
+ // Ensure selection is done with visual block mode rather than one
+ // continuous range.
+ eq(cm.getSelections().join(''), '23oo')
+ helpers.doKeys('g', 'v');
+ eqCursorPos(Pos(2, 1), vim.sel.anchor);
+ eqCursorPos(Pos(2, 2), vim.sel.head);
+ helpers.doKeys('<Esc>');
+ // Ensure selection of deleted range
+ cm.setCursor(1, 1);
+ helpers.doKeys('v', '<C-v>', 'j', 'd', 'g', 'v');
+ eq(cm.getSelections().join(''), 'or');
+}, { value: '123456\nfoo\nbar' });
+testVim('s_normal', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('s');
+ helpers.doKeys('<Esc>');
+ eq('ac', cm.getValue());
+}, { value: 'abc'});
+testVim('s_visual', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('v', 's');
+ helpers.doKeys('<Esc>');
+ helpers.assertCursorAt(0, 0);
+ eq('ac', cm.getValue());
+}, { value: 'abc'});
+testVim('o_visual', function(cm, vim, helpers) {
+ cm.setCursor(0,0);
+ helpers.doKeys('v','l','l','l','o');
+ helpers.assertCursorAt(0,0);
+ helpers.doKeys('v','v','j','j','j','o');
+ helpers.assertCursorAt(0,0);
+ helpers.doKeys('O');
+ helpers.doKeys('l','l')
+ helpers.assertCursorAt(3, 3);
+ helpers.doKeys('d');
+ eq('p',cm.getValue());
+}, { value: 'abcd\nefgh\nijkl\nmnop'});
+testVim('o_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>','3','j','l','l', 'o');
+ eqCursorPos(Pos(3, 3), vim.sel.anchor);
+ eqCursorPos(Pos(0, 1), vim.sel.head);
+ helpers.doKeys('O');
+ eqCursorPos(Pos(3, 1), vim.sel.anchor);
+ eqCursorPos(Pos(0, 3), vim.sel.head);
+ helpers.doKeys('o');
+ eqCursorPos(Pos(0, 3), vim.sel.anchor);
+ eqCursorPos(Pos(3, 1), vim.sel.head);
+}, { value: 'abcd\nefgh\nijkl\nmnop'});
+testVim('changeCase_visual', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('v', 'l', 'l');
+ helpers.doKeys('U');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('v', 'l', 'l');
+ helpers.doKeys('u');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('l', 'l', 'l', '.');
+ helpers.assertCursorAt(0, 3);
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', 'v', 'j', 'U', 'q');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('j', '@', 'a');
+ helpers.assertCursorAt(1, 0);
+ cm.setCursor(3, 0);
+ helpers.doKeys('V', 'U', 'j', '.');
+ eq('ABCDEF\nGHIJKL\nMnopq\nSHORT LINE\nLONG LINE OF TEXT', cm.getValue());
+}, { value: 'abcdef\nghijkl\nmnopq\nshort line\nlong line of text'});
+testVim('changeCase_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(2, 1);
+ helpers.doKeys('<C-v>', 'k', 'k', 'h', 'U');
+ eq('ABcdef\nGHijkl\nMNopq\nfoo', cm.getValue());
+ cm.setCursor(0, 2);
+ helpers.doKeys('.');
+ eq('ABCDef\nGHIJkl\nMNOPq\nfoo', cm.getValue());
+ // check when last line is shorter.
+ cm.setCursor(2, 2);
+ helpers.doKeys('.');
+ eq('ABCDef\nGHIJkl\nMNOPq\nfoO', cm.getValue());
+}, { value: 'abcdef\nghijkl\nmnopq\nfoo'});
+testVim('visual_paste', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('v', 'l', 'l', 'y');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('3', 'l', 'j', 'v', 'l', 'p');
+ helpers.assertCursorAt(1, 5);
+ eq('this is a\nunithitest for visual paste', cm.getValue());
+ cm.setCursor(0, 0);
+ // in case of pasting whole line
+ helpers.doKeys('y', 'y');
+ cm.setCursor(1, 6);
+ helpers.doKeys('v', 'l', 'l', 'l', 'p');
+ helpers.assertCursorAt(2, 0);
+ eq('this is a\nunithi\nthis is a\n for visual paste', cm.getValue());
+}, { value: 'this is a\nunit test for visual paste'});
+
+// This checks the contents of the register used to paste the text
+testVim('v_paste_from_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('"', 'a', 'y', 'w');
+ cm.setCursor(1, 0);
+ helpers.doKeys('v', 'p');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+register/.test(text));
+ });
+}, { value: 'register contents\nare not erased'});
+testVim('S_normal', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('j', 'S');
+ helpers.doKeys('<Esc>');
+ helpers.assertCursorAt(1, 1);
+ eq('aa{\n \ncc', cm.getValue());
+ helpers.doKeys('j', 'S');
+ eq('aa{\n \n ', cm.getValue());
+ helpers.assertCursorAt(2, 2);
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('d', 'd', 'd', 'd');
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('S');
+ is(vim.insertMode);
+ eq('', cm.getValue());
+}, { value: 'aa{\nbb\ncc'});
+testVim('blockwise_paste', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', '3', 'j', 'l', 'y');
+ cm.setCursor(0, 2);
+ // paste one char after the current cursor position
+ helpers.doKeys('p');
+ eq('helhelo\nworwold\nfoofo\nbarba', cm.getValue());
+ cm.setCursor(0, 0);
+ helpers.doKeys('v', '4', 'l', 'y');
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', '3', 'j', 'p');
+ eq('helheelhelo\norwold\noofo\narba', cm.getValue());
+}, { value: 'hello\nworld\nfoo\nbar'});
+testVim('blockwise_paste_long/short_line', function(cm, vim, helpers) {
+ // extend short lines in case of different line lengths.
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', 'j', 'j', 'y');
+ cm.setCursor(0, 3);
+ helpers.doKeys('p');
+ eq('hellho\nfoo f\nbar b', cm.getValue());
+}, { value: 'hello\nfoo\nbar'});
+testVim('blockwise_paste_cut_paste', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', '2', 'j', 'x');
+ cm.setCursor(0, 0);
+ helpers.doKeys('P');
+ eq('cut\nand\npaste\nme', cm.getValue());
+}, { value: 'cut\nand\npaste\nme'});
+testVim('blockwise_paste_from_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', '2', 'j', '"', 'a', 'y');
+ cm.setCursor(0, 3);
+ helpers.doKeys('"', 'a', 'p');
+ eq('foobfar\nhellho\nworlwd', cm.getValue());
+}, { value: 'foobar\nhello\nworld'});
+testVim('blockwise_paste_last_line', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'y');
+ cm.setCursor(3, 0);
+ helpers.doKeys('p');
+ eq('cut\nand\npaste\nmcue\n an\n pa', cm.getValue());
+}, { value: 'cut\nand\npaste\nme'});
+
+testVim('S_visual', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('v', 'j', 'S');
+ helpers.doKeys('<Esc>');
+ helpers.assertCursorAt(0, 0);
+ eq('\ncc', cm.getValue());
+}, { value: 'aa\nbb\ncc'});
+
+testVim('d_/', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('match');
+ helpers.doKeys('2', 'd', '/');
+ helpers.assertCursorAt(0, 0);
+ eq('match \n next', cm.getValue());
+ cm.openDialog = helpers.fakeOpenDialog('2');
+ helpers.doKeys('d', ':');
+ // TODO eq(' next', cm.getValue());
+}, { value: 'text match match \n next' });
+testVim('/ and n/N', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('match');
+ helpers.doKeys('/');
+ helpers.assertCursorAt(0, 11);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(1, 6);
+ helpers.doKeys('N');
+ helpers.assertCursorAt(0, 11);
+
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', '/');
+ helpers.assertCursorAt(1, 6);
+}, { value: 'match nope match \n nope Match' });
+testVim('/_case', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('Match');
+ helpers.doKeys('/');
+ helpers.assertCursorAt(1, 6);
+}, { value: 'match nope match \n nope Match' });
+testVim('/_2_pcre', function(cm, vim, helpers) {
+ CodeMirror.Vim.setOption('pcre', true);
+ cm.openDialog = helpers.fakeOpenDialog('(word){2}');
+ helpers.doKeys('/');
+ helpers.assertCursorAt(1, 9);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(2, 1);
+}, { value: 'word\n another wordword\n wordwordword\n' });
+testVim('/_2_nopcre', function(cm, vim, helpers) {
+ CodeMirror.Vim.setOption('pcre', false);
+ cm.openDialog = helpers.fakeOpenDialog('\\(word\\)\\{2}');
+ helpers.doKeys('/');
+ helpers.assertCursorAt(1, 9);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(2, 1);
+}, { value: 'word\n another wordword\n wordwordword\n' });
+testVim('/_nongreedy', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('aa');
+ helpers.doKeys('/');
+ helpers.assertCursorAt(0, 4);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(1, 3);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'aaa aa \n a aa'});
+testVim('?_nongreedy', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('aa');
+ helpers.doKeys('?');
+ helpers.assertCursorAt(1, 3);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 4);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'aaa aa \n a aa'});
+testVim('/_greedy', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('a+');
+ helpers.doKeys('/');
+ helpers.assertCursorAt(0, 4);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(1, 1);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(1, 3);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'aaa aa \n a aa'});
+testVim('?_greedy', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('a+');
+ helpers.doKeys('?');
+ helpers.assertCursorAt(1, 3);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(1, 1);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 4);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'aaa aa \n a aa'});
+testVim('/_greedy_0_or_more', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('a*');
+ helpers.doKeys('/');
+ helpers.assertCursorAt(0, 3);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 4);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 5);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(1, 0);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(1, 1);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'aaa aa\n aa'});
+testVim('?_greedy_0_or_more', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('a*');
+ helpers.doKeys('?');
+ helpers.assertCursorAt(1, 1);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 5);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 3);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 0);
+}, { value: 'aaa aa\n aa'});
+testVim('? and n/N', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('match');
+ helpers.doKeys('?');
+ helpers.assertCursorAt(1, 6);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 11);
+ helpers.doKeys('N');
+ helpers.assertCursorAt(1, 6);
+
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', '?');
+ helpers.assertCursorAt(0, 11);
+}, { value: 'match nope match \n nope Match' });
+testVim('*', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('*');
+ helpers.assertCursorAt(0, 22);
+
+ cm.setCursor(0, 9);
+ helpers.doKeys('2', '*');
+ helpers.assertCursorAt(1, 8);
+}, { value: 'nomatch match nomatch match \nnomatch Match' });
+testVim('*_no_word', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('*');
+ helpers.assertCursorAt(0, 0);
+}, { value: ' \n match \n' });
+testVim('*_symbol', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('*');
+ helpers.assertCursorAt(1, 0);
+}, { value: ' /}\n/} match \n' });
+testVim('#', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('#');
+ helpers.assertCursorAt(1, 8);
+
+ cm.setCursor(0, 9);
+ helpers.doKeys('2', '#');
+ helpers.assertCursorAt(0, 22);
+}, { value: 'nomatch match nomatch match \nnomatch Match' });
+testVim('*_seek', function(cm, vim, helpers) {
+ // Should skip over space and symbols.
+ cm.setCursor(0, 3);
+ helpers.doKeys('*');
+ helpers.assertCursorAt(0, 22);
+}, { value: ' := match nomatch match \nnomatch Match' });
+testVim('#', function(cm, vim, helpers) {
+ // Should skip over space and symbols.
+ cm.setCursor(0, 3);
+ helpers.doKeys('#');
+ helpers.assertCursorAt(1, 8);
+}, { value: ' := match nomatch match \nnomatch Match' });
+testVim('g*', function(cm, vim, helpers) {
+ cm.setCursor(0, 8);
+ helpers.doKeys('g', '*');
+ helpers.assertCursorAt(0, 18);
+ cm.setCursor(0, 8);
+ helpers.doKeys('3', 'g', '*');
+ helpers.assertCursorAt(1, 8);
+}, { value: 'matches match alsoMatch\nmatchme matching' });
+testVim('g#', function(cm, vim, helpers) {
+ cm.setCursor(0, 8);
+ helpers.doKeys('g', '#');
+ helpers.assertCursorAt(0, 0);
+ cm.setCursor(0, 8);
+ helpers.doKeys('3', 'g', '#');
+ helpers.assertCursorAt(1, 0);
+}, { value: 'matches match alsoMatch\nmatchme matching' });
+testVim('macro_insert', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', '0', 'i');
+ helpers.doKeys('foo')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('q', '@', 'a');
+ eq('foofoo', cm.getValue());
+}, { value: ''});
+testVim('macro_insert_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', '$', 'a');
+ helpers.doKeys('larry.')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('a');
+ helpers.doKeys('curly.')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('q');
+ helpers.doKeys('a');
+ helpers.doKeys('moe.')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('@', 'a');
+ // At this point, the most recent edit should be the 2nd insert change
+ // inside the macro, i.e. "curly.".
+ helpers.doKeys('.');
+ eq('larry.curly.moe.larry.curly.curly.', cm.getValue());
+}, { value: ''});
+testVim('macro_space', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('<Space>', '<Space>');
+ helpers.assertCursorAt(0, 2);
+ helpers.doKeys('q', 'a', '<Space>', '<Space>', 'q');
+ helpers.assertCursorAt(0, 4);
+ helpers.doKeys('@', 'a');
+ helpers.assertCursorAt(0, 6);
+ helpers.doKeys('@', 'a');
+ helpers.assertCursorAt(0, 8);
+}, { value: 'one line of text.'});
+testVim('macro_t_search', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', 't', 'e', 'q');
+ helpers.assertCursorAt(0, 1);
+ helpers.doKeys('l', '@', 'a');
+ helpers.assertCursorAt(0, 6);
+ helpers.doKeys('l', ';');
+ helpers.assertCursorAt(0, 12);
+}, { value: 'one line of text.'});
+testVim('macro_f_search', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'b', 'f', 'e', 'q');
+ helpers.assertCursorAt(0, 2);
+ helpers.doKeys('@', 'b');
+ helpers.assertCursorAt(0, 7);
+ helpers.doKeys(';');
+ helpers.assertCursorAt(0, 13);
+}, { value: 'one line of text.'});
+testVim('macro_slash_search', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'c');
+ cm.openDialog = helpers.fakeOpenDialog('e');
+ helpers.doKeys('/', 'q');
+ helpers.assertCursorAt(0, 2);
+ helpers.doKeys('@', 'c');
+ helpers.assertCursorAt(0, 7);
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 13);
+}, { value: 'one line of text.'});
+testVim('macro_multislash_search', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'd');
+ cm.openDialog = helpers.fakeOpenDialog('e');
+ helpers.doKeys('/');
+ cm.openDialog = helpers.fakeOpenDialog('t');
+ helpers.doKeys('/', 'q');
+ helpers.assertCursorAt(0, 12);
+ helpers.doKeys('@', 'd');
+ helpers.assertCursorAt(0, 15);
+}, { value: 'one line of text to rule them all.'});
+testVim('macro_last_ex_command_register', function (cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doEx('s/a/b');
+ helpers.doKeys('2', '@', ':');
+ eq('bbbaa', cm.getValue());
+ helpers.assertCursorAt(0, 2);
+}, { value: 'aaaaa'});
+testVim('macro_last_run_macro', function (cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', 'C', 'a', '<Esc>', 'q');
+ helpers.doKeys('q', 'b', 'C', 'b', '<Esc>', 'q');
+ helpers.doKeys('@', 'a');
+ helpers.doKeys('d', 'd');
+ helpers.doKeys('@', '@');
+ eq('a', cm.getValue());
+}, { value: ''});
+testVim('macro_parens', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'z', 'i');
+ helpers.doKeys('(')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('e', 'a');
+ helpers.doKeys(')')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('q');
+ helpers.doKeys('w', '@', 'z');
+ helpers.doKeys('w', '@', 'z');
+ eq('(see) (spot) (run)', cm.getValue());
+}, { value: 'see spot run'});
+testVim('macro_overwrite', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'z', '0', 'i');
+ helpers.doKeys('I ')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('q');
+ helpers.doKeys('e');
+ // Now replace the macro with something else.
+ helpers.doKeys('q', 'z', 'a');
+ helpers.doKeys('.')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('q');
+ helpers.doKeys('e', '@', 'z');
+ helpers.doKeys('e', '@', 'z');
+ eq('I see. spot. run.', cm.getValue());
+}, { value: 'see spot run'});
+testVim('macro_search_f', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', 'f', ' ');
+ helpers.assertCursorAt(0,3);
+ helpers.doKeys('q', '0');
+ helpers.assertCursorAt(0,0);
+ helpers.doKeys('@', 'a');
+ helpers.assertCursorAt(0,3);
+}, { value: 'The quick brown fox jumped over the lazy dog.'});
+testVim('macro_search_2f', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', '2', 'f', ' ');
+ helpers.assertCursorAt(0,9);
+ helpers.doKeys('q', '0');
+ helpers.assertCursorAt(0,0);
+ helpers.doKeys('@', 'a');
+ helpers.assertCursorAt(0,9);
+}, { value: 'The quick brown fox jumped over the lazy dog.'});
+testVim('macro_yank_tick', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ // Start recording a macro into the \' register.
+ helpers.doKeys('q', '\'');
+ helpers.doKeys('y', '<Right>', '<Right>', '<Right>', '<Right>', 'p');
+ helpers.assertCursorAt(0,4);
+ eq('the tex parrot', cm.getValue());
+}, { value: 'the ex parrot'});
+testVim('yank_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('"', 'a', 'y', 'y');
+ helpers.doKeys('j', '"', 'b', 'y', 'y');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+foo/.test(text));
+ is(/b\s+bar/.test(text));
+ });
+ helpers.doKeys(':');
+}, { value: 'foo\nbar'});
+testVim('yank_visual_block', function(cm, vim, helpers) {
+ cm.setCursor(0, 1);
+ helpers.doKeys('<C-v>', 'l', 'j', '"', 'a', 'y');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+oo\nar/.test(text));
+ });
+ helpers.doKeys(':');
+}, { value: 'foo\nbar'});
+testVim('yank_append_line_to_line_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('"', 'a', 'y', 'y');
+ helpers.doKeys('j', '"', 'A', 'y', 'y');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+foo\nbar/.test(text));
+ is(/"\s+foo\nbar/.test(text));
+ });
+ helpers.doKeys(':');
+}, { value: 'foo\nbar'});
+testVim('yank_append_word_to_word_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('"', 'a', 'y', 'w');
+ helpers.doKeys('j', '"', 'A', 'y', 'w');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+foobar/.test(text));
+ is(/"\s+foobar/.test(text));
+ });
+ helpers.doKeys(':');
+}, { value: 'foo\nbar'});
+testVim('yank_append_line_to_word_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('"', 'a', 'y', 'w');
+ helpers.doKeys('j', '"', 'A', 'y', 'y');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+foo\nbar/.test(text));
+ is(/"\s+foo\nbar/.test(text));
+ });
+ helpers.doKeys(':');
+}, { value: 'foo\nbar'});
+testVim('yank_append_word_to_line_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('"', 'a', 'y', 'y');
+ helpers.doKeys('j', '"', 'A', 'y', 'w');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+foo\nbar/.test(text));
+ is(/"\s+foo\nbar/.test(text));
+ });
+ helpers.doKeys(':');
+}, { value: 'foo\nbar'});
+testVim('macro_register', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('q', 'a', 'i');
+ helpers.doKeys('gangnam')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('q');
+ helpers.doKeys('q', 'b', 'o');
+ helpers.doKeys('style')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('q');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/a\s+i/.test(text));
+ is(/b\s+o/.test(text));
+ });
+ helpers.doKeys(':');
+}, { value: ''});
+testVim('._register', function(cm,vim,helpers) {
+ cm.setCursor(0,0);
+ helpers.doKeys('i');
+ helpers.doKeys('foo')
+ helpers.doKeys('<Esc>');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/\.\s+foo/.test(text));
+ });
+ helpers.doKeys(':');
+}, {value: ''});
+testVim(':_register', function(cm,vim,helpers) {
+ helpers.doEx('bar');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/:\s+bar/.test(text));
+ });
+ helpers.doKeys(':');
+}, {value: ''});
+testVim('search_register_escape', function(cm, vim, helpers) {
+ // Check that the register is restored if the user escapes rather than confirms.
+ cm.openDialog = helpers.fakeOpenDialog('waldo');
+ helpers.doKeys('/');
+ var onKeyDown;
+ var onKeyUp;
+ var KEYCODES = {
+ f: 70,
+ o: 79,
+ Esc: 27
+ };
+ cm.openDialog = function(template, callback, options) {
+ onKeyDown = options.onKeyDown;
+ onKeyUp = options.onKeyUp;
+ };
+ var close = function() {};
+ helpers.doKeys('/');
+ // Fake some keyboard events coming in.
+ onKeyDown({keyCode: KEYCODES.f}, '', close);
+ onKeyUp({keyCode: KEYCODES.f}, '', close);
+ onKeyDown({keyCode: KEYCODES.o}, 'f', close);
+ onKeyUp({keyCode: KEYCODES.o}, 'f', close);
+ onKeyDown({keyCode: KEYCODES.o}, 'fo', close);
+ onKeyUp({keyCode: KEYCODES.o}, 'fo', close);
+ onKeyDown({keyCode: KEYCODES.Esc}, 'foo', close);
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/waldo/.test(text));
+ is(!/foo/.test(text));
+ });
+ helpers.doKeys(':');
+}, {value: ''});
+testVim('search_register', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('foo');
+ helpers.doKeys('/');
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ cm.openNotification = helpers.fakeOpenNotification(function(text) {
+ is(/\/\s+foo/.test(text));
+ });
+ helpers.doKeys(':');
+}, {value: ''});
+testVim('search_history', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('this');
+ helpers.doKeys('/');
+ cm.openDialog = helpers.fakeOpenDialog('checks');
+ helpers.doKeys('/');
+ cm.openDialog = helpers.fakeOpenDialog('search');
+ helpers.doKeys('/');
+ cm.openDialog = helpers.fakeOpenDialog('history');
+ helpers.doKeys('/');
+ cm.openDialog = helpers.fakeOpenDialog('checks');
+ helpers.doKeys('/');
+ var onKeyDown;
+ var onKeyUp;
+ var query = '';
+ var keyCodes = {
+ Up: 38,
+ Down: 40
+ };
+ cm.openDialog = function(template, callback, options) {
+ onKeyUp = options.onKeyUp;
+ onKeyDown = options.onKeyDown;
+ };
+ var close = function(newVal) {
+ if (typeof newVal == 'string') query = newVal;
+ }
+ helpers.doKeys('/');
+ onKeyDown({keyCode: keyCodes.Up}, query, close);
+ onKeyUp({keyCode: keyCodes.Up}, query, close);
+ eq(query, 'checks');
+ onKeyDown({keyCode: keyCodes.Up}, query, close);
+ onKeyUp({keyCode: keyCodes.Up}, query, close);
+ eq(query, 'history');
+ onKeyDown({keyCode: keyCodes.Up}, query, close);
+ onKeyUp({keyCode: keyCodes.Up}, query, close);
+ eq(query, 'search');
+ onKeyDown({keyCode: keyCodes.Up}, query, close);
+ onKeyUp({keyCode: keyCodes.Up}, query, close);
+ eq(query, 'this');
+ onKeyDown({keyCode: keyCodes.Down}, query, close);
+ onKeyUp({keyCode: keyCodes.Down}, query, close);
+ eq(query, 'search');
+}, {value: ''});
+testVim('exCommand_history', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('registers');
+ helpers.doKeys(':');
+ cm.openDialog = helpers.fakeOpenDialog('sort');
+ helpers.doKeys(':');
+ cm.openDialog = helpers.fakeOpenDialog('map');
+ helpers.doKeys(':');
+ cm.openDialog = helpers.fakeOpenDialog('invalid');
+ helpers.doKeys(':');
+ var onKeyDown;
+ var onKeyUp;
+ var input = '';
+ var keyCodes = {
+ Up: 38,
+ Down: 40,
+ s: 115
+ };
+ cm.openDialog = function(template, callback, options) {
+ onKeyUp = options.onKeyUp;
+ onKeyDown = options.onKeyDown;
+ };
+ var close = function(newVal) {
+ if (typeof newVal == 'string') input = newVal;
+ }
+ helpers.doKeys(':');
+ onKeyDown({keyCode: keyCodes.Up}, input, close);
+ eq(input, 'invalid');
+ onKeyDown({keyCode: keyCodes.Up}, input, close);
+ eq(input, 'map');
+ onKeyDown({keyCode: keyCodes.Up}, input, close);
+ eq(input, 'sort');
+ onKeyDown({keyCode: keyCodes.Up}, input, close);
+ eq(input, 'registers');
+ onKeyDown({keyCode: keyCodes.s}, '', close);
+ input = 's';
+ onKeyDown({keyCode: keyCodes.Up}, input, close);
+ eq(input, 'sort');
+}, {value: ''});
+testVim('search_clear', function(cm, vim, helpers) {
+ var onKeyDown;
+ var input = '';
+ var keyCodes = {
+ Ctrl: 17,
+ u: 85
+ };
+ cm.openDialog = function(template, callback, options) {
+ onKeyDown = options.onKeyDown;
+ };
+ var close = function(newVal) {
+ if (typeof newVal == 'string') input = newVal;
+ }
+ helpers.doKeys('/');
+ input = 'foo';
+ onKeyDown({keyCode: keyCodes.Ctrl}, input, close);
+ onKeyDown({keyCode: keyCodes.u, ctrlKey: true}, input, close);
+ eq(input, '');
+});
+testVim('exCommand_clear', function(cm, vim, helpers) {
+ var onKeyDown;
+ var input = '';
+ var keyCodes = {
+ Ctrl: 17,
+ u: 85
+ };
+ cm.openDialog = function(template, callback, options) {
+ onKeyDown = options.onKeyDown;
+ };
+ var close = function(newVal) {
+ if (typeof newVal == 'string') input = newVal;
+ }
+ helpers.doKeys(':');
+ input = 'foo';
+ onKeyDown({keyCode: keyCodes.Ctrl}, input, close);
+ onKeyDown({keyCode: keyCodes.u, ctrlKey: true}, input, close);
+ eq(input, '');
+});
+testVim('.', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', 'd', 'w');
+ helpers.doKeys('.');
+ eq('5 6', cm.getValue());
+}, { value: '1 2 3 4 5 6'});
+testVim('._repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('2', 'd', 'w');
+ helpers.doKeys('3', '.');
+ eq('6', cm.getValue());
+}, { value: '1 2 3 4 5 6'});
+testVim('._insert', function(cm, vim, helpers) {
+ helpers.doKeys('i');
+ helpers.doKeys('test')
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('.');
+ eq('testestt', cm.getValue());
+ helpers.assertCursorAt(0, 6);
+ helpers.doKeys('O');
+ helpers.doKeys('xyz')
+ helpers.doInsertModeKeys('Backspace');
+ helpers.doInsertModeKeys('Down');
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('.');
+ eq('xy\nxy\ntestestt', cm.getValue());
+ helpers.assertCursorAt(1, 1);
+}, { value: ''});
+testVim('._insert_repeat', function(cm, vim, helpers) {
+ helpers.doKeys('i');
+ helpers.doKeys('test')
+ cm.setCursor(0, 4);
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('2', '.');
+ eq('testesttestt', cm.getValue());
+ helpers.assertCursorAt(0, 10);
+}, { value: ''});
+testVim('._repeat_insert', function(cm, vim, helpers) {
+ helpers.doKeys('3', 'i');
+ helpers.doKeys('te')
+ cm.setCursor(0, 2);
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('.');
+ eq('tetettetetee', cm.getValue());
+ helpers.assertCursorAt(0, 10);
+}, { value: ''});
+testVim('._insert_o', function(cm, vim, helpers) {
+ helpers.doKeys('o');
+ helpers.doKeys('z')
+ cm.setCursor(1, 1);
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('.');
+ eq('\nz\nz', cm.getValue());
+ helpers.assertCursorAt(2, 0);
+}, { value: ''});
+testVim('._insert_o_repeat', function(cm, vim, helpers) {
+ helpers.doKeys('o');
+ helpers.doKeys('z')
+ helpers.doKeys('<Esc>');
+ cm.setCursor(1, 0);
+ helpers.doKeys('2', '.');
+ eq('\nz\nz\nz', cm.getValue());
+ helpers.assertCursorAt(3, 0);
+}, { value: ''});
+testVim('._insert_o_indent', function(cm, vim, helpers) {
+ helpers.doKeys('o');
+ helpers.doKeys('z')
+ helpers.doKeys('<Esc>');
+ cm.setCursor(1, 2);
+ helpers.doKeys('.');
+ eq('{\n z\n z', cm.getValue());
+ helpers.assertCursorAt(2, 2);
+}, { value: '{'});
+testVim('._insert_cw', function(cm, vim, helpers) {
+ helpers.doKeys('c', 'w');
+ helpers.doKeys('test')
+ helpers.doKeys('<Esc>');
+ cm.setCursor(0, 3);
+ helpers.doKeys('2', 'l');
+ helpers.doKeys('.');
+ eq('test test word3', cm.getValue());
+ helpers.assertCursorAt(0, 8);
+}, { value: 'word1 word2 word3' });
+testVim('._insert_cw_repeat', function(cm, vim, helpers) {
+ // For some reason, repeat cw in desktop VIM will does not repeat insert mode
+ // changes. Will conform to that behavior.
+ helpers.doKeys('c', 'w');
+ helpers.doKeys('test');
+ helpers.doKeys('<Esc>');
+ cm.setCursor(0, 4);
+ helpers.doKeys('l');
+ helpers.doKeys('2', '.');
+ eq('test test', cm.getValue());
+ helpers.assertCursorAt(0, 8);
+}, { value: 'word1 word2 word3' });
+testVim('._delete', function(cm, vim, helpers) {
+ cm.setCursor(0, 5);
+ helpers.doKeys('i');
+ helpers.doInsertModeKeys('Backspace');
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('.');
+ eq('zace', cm.getValue());
+ helpers.assertCursorAt(0, 1);
+}, { value: 'zabcde'});
+testVim('._delete_repeat', function(cm, vim, helpers) {
+ cm.setCursor(0, 6);
+ helpers.doKeys('i');
+ helpers.doInsertModeKeys('Backspace');
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('2', '.');
+ eq('zzce', cm.getValue());
+ helpers.assertCursorAt(0, 1);
+}, { value: 'zzabcde'});
+testVim('._visual_>', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('V', 'j', '>');
+ cm.setCursor(2, 0)
+ helpers.doKeys('.');
+ eq(' 1\n 2\n 3\n 4', cm.getValue());
+ helpers.assertCursorAt(2, 2);
+}, { value: '1\n2\n3\n4'});
+testVim('._replace_repeat', function(cm, vim, helpers) {
+ helpers.doKeys('R');
+ cm.replaceRange('123', cm.getCursor(), offsetCursor(cm.getCursor(), 0, 3));
+ cm.setCursor(0, 3);
+ helpers.doKeys('<Esc>');
+ helpers.doKeys('2', '.');
+ eq('12123123\nabcdefg', cm.getValue());
+ helpers.assertCursorAt(0, 7);
+ cm.setCursor(1, 0);
+ helpers.doKeys('.');
+ eq('12123123\n123123g', cm.getValue());
+ helpers.doKeys('l', '"', '.', 'p');
+ eq('12123123\n123123g123', cm.getValue());
+}, { value: 'abcdef\nabcdefg'});
+testVim('f;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('f', 'x');
+ helpers.doKeys(';');
+ helpers.doKeys('2', ';');
+ eq(9, cm.getCursor().ch);
+}, { value: '01x3xx678x'});
+testVim('F;', function(cm, vim, helpers) {
+ cm.setCursor(0, 8);
+ helpers.doKeys('F', 'x');
+ helpers.doKeys(';');
+ helpers.doKeys('2', ';');
+ eq(2, cm.getCursor().ch);
+}, { value: '01x3xx6x8x'});
+testVim('t;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('t', 'x');
+ helpers.doKeys(';');
+ helpers.doKeys('2', ';');
+ eq(8, cm.getCursor().ch);
+}, { value: '01x3xx678x'});
+testVim('T;', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('T', 'x');
+ helpers.doKeys(';');
+ helpers.doKeys('2', ';');
+ eq(2, cm.getCursor().ch);
+}, { value: '0xx3xx678x'});
+testVim('f,', function(cm, vim, helpers) {
+ cm.setCursor(0, 6);
+ helpers.doKeys('f', 'x');
+ helpers.doKeys(',');
+ helpers.doKeys('2', ',');
+ eq(2, cm.getCursor().ch);
+}, { value: '01x3xx678x'});
+testVim('F,', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ helpers.doKeys('F', 'x');
+ helpers.doKeys(',');
+ helpers.doKeys('2', ',');
+ eq(9, cm.getCursor().ch);
+}, { value: '01x3xx678x'});
+testVim('t,', function(cm, vim, helpers) {
+ cm.setCursor(0, 6);
+ helpers.doKeys('t', 'x');
+ helpers.doKeys(',');
+ helpers.doKeys('2', ',');
+ eq(3, cm.getCursor().ch);
+}, { value: '01x3xx678x'});
+testVim('T,', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('T', 'x');
+ helpers.doKeys(',');
+ helpers.doKeys('2', ',');
+ eq(8, cm.getCursor().ch);
+}, { value: '01x3xx67xx'});
+testVim('fd,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('f', '4');
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', ';');
+ eq('56789', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 9);
+ helpers.doKeys('d', ',');
+ eq('01239', cm.getValue());
+}, { value: '0123456789'});
+testVim('Fd,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('F', '4');
+ cm.setCursor(0, 9);
+ helpers.doKeys('d', ';');
+ eq('01239', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', ',');
+ eq('56789', cm.getValue());
+}, { value: '0123456789'});
+testVim('td,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('t', '4');
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', ';');
+ eq('456789', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 9);
+ helpers.doKeys('d', ',');
+ eq('012349', cm.getValue());
+}, { value: '0123456789'});
+testVim('Td,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('T', '4');
+ cm.setCursor(0, 9);
+ helpers.doKeys('d', ';');
+ eq('012349', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 0);
+ helpers.doKeys('d', ',');
+ eq('456789', cm.getValue());
+}, { value: '0123456789'});
+testVim('fc,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('f', '4');
+ cm.setCursor(0, 0);
+ helpers.doKeys('c', ';', '<Esc>');
+ eq('56789', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 9);
+ helpers.doKeys('c', ',');
+ eq('01239', cm.getValue());
+}, { value: '0123456789'});
+testVim('Fc,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('F', '4');
+ cm.setCursor(0, 9);
+ helpers.doKeys('c', ';', '<Esc>');
+ eq('01239', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 0);
+ helpers.doKeys('c', ',');
+ eq('56789', cm.getValue());
+}, { value: '0123456789'});
+testVim('tc,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('t', '4');
+ cm.setCursor(0, 0);
+ helpers.doKeys('c', ';', '<Esc>');
+ eq('456789', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 9);
+ helpers.doKeys('c', ',');
+ eq('012349', cm.getValue());
+}, { value: '0123456789'});
+testVim('Tc,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('T', '4');
+ cm.setCursor(0, 9);
+ helpers.doKeys('c', ';', '<Esc>');
+ eq('012349', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 0);
+ helpers.doKeys('c', ',');
+ eq('456789', cm.getValue());
+}, { value: '0123456789'});
+testVim('fy,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('f', '4');
+ cm.setCursor(0, 0);
+ helpers.doKeys('y', ';', 'P');
+ eq('012340123456789', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 9);
+ helpers.doKeys('y', ',', 'P');
+ eq('012345678456789', cm.getValue());
+}, { value: '0123456789'});
+testVim('Fy,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('F', '4');
+ cm.setCursor(0, 9);
+ helpers.doKeys('y', ';', 'p');
+ eq('012345678945678', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 0);
+ helpers.doKeys('y', ',', 'P');
+ eq('012340123456789', cm.getValue());
+}, { value: '0123456789'});
+testVim('ty,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys('t', '4');
+ cm.setCursor(0, 0);
+ helpers.doKeys('y', ';', 'P');
+ eq('01230123456789', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 9);
+ helpers.doKeys('y', ',', 'p');
+ eq('01234567895678', cm.getValue());
+}, { value: '0123456789'});
+testVim('Ty,;', function(cm, vim, helpers) {
+ cm.setCursor(0, 9);
+ helpers.doKeys('T', '4');
+ cm.setCursor(0, 9);
+ helpers.doKeys('y', ';', 'p');
+ eq('01234567895678', cm.getValue());
+ helpers.doKeys('u');
+ cm.setCursor(0, 0);
+ helpers.doKeys('y', ',', 'P');
+ eq('01230123456789', cm.getValue());
+}, { value: '0123456789'});
+testVim('HML', function(cm, vim, helpers) {
+ var lines = 35;
+ var textHeight = cm.defaultTextHeight();
+ cm.setSize(600, lines*textHeight);
+ cm.setCursor(120, 0);
+ helpers.doKeys('H');
+ helpers.assertCursorAt(86, 2);
+ helpers.doKeys('L');
+ helpers.assertCursorAt(120, 4);
+ helpers.doKeys('M');
+ helpers.assertCursorAt(103,4);
+}, { value: (function(){
+ var lines = new Array(100);
+ var upper = ' xx\n';
+ var lower = ' xx\n';
+ upper = lines.join(upper);
+ lower = lines.join(lower);
+ return upper + lower;
+})()});
+
+var zVals = [];
+forEach(['zb','zz','zt','z-','z.','z<CR>'], function(e, idx){
+ var lineNum = 250;
+ var lines = 35;
+ testVim(e, function(cm, vim, helpers) {
+ var k1 = e[0];
+ var k2 = e.substring(1);
+ var textHeight = cm.defaultTextHeight();
+ cm.setSize(600, lines*textHeight);
+ cm.setCursor(lineNum, 0);
+ helpers.doKeys(k1, k2);
+ zVals[idx] = cm.getScrollInfo().top;
+ }, { value: (function(){
+ return new Array(500).join('\n');
+ })()});
+});
+testVim('zb_to_bottom', function(cm, vim, helpers){
+ var lineNum = 250;
+ cm.setSize(600, 35*cm.defaultTextHeight());
+ cm.setCursor(lineNum, 0);
+ helpers.doKeys('z', 'b');
+ var scrollInfo = cm.getScrollInfo();
+ eq(scrollInfo.top + scrollInfo.clientHeight, cm.charCoords(Pos(lineNum, 0), 'local').bottom);
+}, { value: (function(){
+ return new Array(500).join('\n');
+})()});
+testVim('zt_to_top', function(cm, vim, helpers){
+ var lineNum = 250;
+ cm.setSize(600, 35*cm.defaultTextHeight());
+ cm.setCursor(lineNum, 0);
+ helpers.doKeys('z', 't');
+ eq(cm.getScrollInfo().top, cm.charCoords(Pos(lineNum, 0), 'local').top);
+}, { value: (function(){
+ return new Array(500).join('\n');
+})()});
+testVim('zb<zz', function(cm, vim, helpers){
+ eq(zVals[0]<zVals[1], true);
+});
+testVim('zz<zt', function(cm, vim, helpers){
+ eq(zVals[1]<zVals[2], true);
+});
+testVim('zb==z-', function(cm, vim, helpers){
+ eq(zVals[0], zVals[3]);
+});
+testVim('zz==z.', function(cm, vim, helpers){
+ eq(zVals[1], zVals[4]);
+});
+testVim('zt==z<CR>', function(cm, vim, helpers){
+ eq(zVals[2], zVals[5]);
+});
+
+var moveTillCharacterSandbox =
+ 'The quick brown fox \n';
+testVim('moveTillCharacter', function(cm, vim, helpers){
+ cm.setCursor(0, 0);
+ // Search for the 'q'.
+ cm.openDialog = helpers.fakeOpenDialog('q');
+ helpers.doKeys('/');
+ eq(4, cm.getCursor().ch);
+ // Jump to just before the first o in the list.
+ helpers.doKeys('t');
+ helpers.doKeys('o');
+ eq('The quick brown fox \n', cm.getValue());
+ // Delete that one character.
+ helpers.doKeys('d');
+ helpers.doKeys('t');
+ helpers.doKeys('o');
+ eq('The quick bown fox \n', cm.getValue());
+ // Delete everything until the next 'o'.
+ helpers.doKeys('.');
+ eq('The quick box \n', cm.getValue());
+ // An unmatched character should have no effect.
+ helpers.doKeys('d');
+ helpers.doKeys('t');
+ helpers.doKeys('q');
+ eq('The quick box \n', cm.getValue());
+ // Matches should only be possible on single lines.
+ helpers.doKeys('d');
+ helpers.doKeys('t');
+ helpers.doKeys('z');
+ eq('The quick box \n', cm.getValue());
+ // After all that, the search for 'q' should still be active, so the 'N' command
+ // can run it again in reverse. Use that to delete everything back to the 'q'.
+ helpers.doKeys('d');
+ helpers.doKeys('N');
+ eq('The ox \n', cm.getValue());
+ eq(4, cm.getCursor().ch);
+}, { value: moveTillCharacterSandbox});
+testVim('searchForPipe', function(cm, vim, helpers){
+ CodeMirror.Vim.setOption('pcre', false);
+ cm.setCursor(0, 0);
+ // Search for the '|'.
+ cm.openDialog = helpers.fakeOpenDialog('|');
+ helpers.doKeys('/');
+ eq(4, cm.getCursor().ch);
+}, { value: 'this|that'});
+
+
+var scrollMotionSandbox =
+ '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n';
+testVim('scrollMotion', function(cm, vim, helpers){
+ var prevCursor, prevScrollInfo;
+ cm.setCursor(0, 0);
+ // ctrl-y at the top of the file should have no effect.
+ helpers.doKeys('<C-y>');
+ eq(0, cm.getCursor().line);
+ prevScrollInfo = cm.getScrollInfo();
+ helpers.doKeys('<C-e>');
+ eq(1, cm.getCursor().line);
+ is(prevScrollInfo.top < cm.getScrollInfo().top);
+ // Jump to the end of the sandbox.
+ cm.setCursor(1000, 0);
+ prevCursor = cm.getCursor();
+ // ctrl-e at the bottom of the file should have no effect.
+ helpers.doKeys('<C-e>');
+ eq(prevCursor.line, cm.getCursor().line);
+ prevScrollInfo = cm.getScrollInfo();
+ helpers.doKeys('<C-y>');
+ eq(prevCursor.line - 1, cm.getCursor().line, "Y");
+ is(prevScrollInfo.top > cm.getScrollInfo().top);
+}, { value: scrollMotionSandbox});
+
+var squareBracketMotionSandbox = ''+
+ '({\n'+//0
+ ' ({\n'+//11
+ ' /*comment {\n'+//2
+ ' */(\n'+//3
+ '#else \n'+//4
+ ' /* )\n'+//5
+ '#if }\n'+//6
+ ' )}*/\n'+//7
+ ')}\n'+//8
+ '{}\n'+//9
+ '#else {{\n'+//10
+ '{}\n'+//11
+ '}\n'+//12
+ '{\n'+//13
+ '#endif\n'+//14
+ '}\n'+//15
+ '}\n'+//16
+ '#else';//17
+testVim('[[, ]]', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys(']', ']');
+ helpers.assertCursorAt(9,0);
+ helpers.doKeys('2', ']', ']');
+ helpers.assertCursorAt(13,0);
+ helpers.doKeys(']', ']');
+ helpers.assertCursorAt(17,0);
+ helpers.doKeys('[', '[');
+ helpers.assertCursorAt(13,0);
+ helpers.doKeys('2', '[', '[');
+ helpers.assertCursorAt(9,0);
+ helpers.doKeys('[', '[');
+ helpers.assertCursorAt(0,0);
+}, { value: squareBracketMotionSandbox});
+testVim('[], ][', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doKeys(']', '[');
+ helpers.assertCursorAt(12,0);
+ helpers.doKeys('2', ']', '[');
+ helpers.assertCursorAt(16,0);
+ helpers.doKeys(']', '[');
+ helpers.assertCursorAt(17,0);
+ helpers.doKeys('[', ']');
+ helpers.assertCursorAt(16,0);
+ helpers.doKeys('2', '[', ']');
+ helpers.assertCursorAt(12,0);
+ helpers.doKeys('[', ']');
+ helpers.assertCursorAt(0,0);
+}, { value: squareBracketMotionSandbox});
+testVim('[{, ]}', function(cm, vim, helpers) {
+ cm.setCursor(4, 10);
+ helpers.doKeys('[', '{');
+ helpers.assertCursorAt(2,12);
+ helpers.doKeys('2', '[', '{');
+ helpers.assertCursorAt(0,1);
+ cm.setCursor(4, 10);
+ helpers.doKeys(']', '}');
+ helpers.assertCursorAt(6,11);
+ helpers.doKeys('2', ']', '}');
+ helpers.assertCursorAt(8,1);
+ cm.setCursor(0,1);
+ helpers.doKeys(']', '}');
+ helpers.assertCursorAt(8,1);
+ helpers.doKeys('[', '{');
+ helpers.assertCursorAt(0,1);
+}, { value: squareBracketMotionSandbox});
+testVim('[(, ])', function(cm, vim, helpers) {
+ cm.setCursor(4, 10);
+ helpers.doKeys('[', '(');
+ helpers.assertCursorAt(3,14);
+ helpers.doKeys('2', '[', '(');
+ helpers.assertCursorAt(0,0);
+ cm.setCursor(4, 10);
+ helpers.doKeys(']', ')');
+ helpers.assertCursorAt(5,11);
+ helpers.doKeys('2', ']', ')');
+ helpers.assertCursorAt(8,0);
+ helpers.doKeys('[', '(');
+ helpers.assertCursorAt(0,0);
+ helpers.doKeys(']', ')');
+ helpers.assertCursorAt(8,0);
+}, { value: squareBracketMotionSandbox});
+testVim('[*, ]*, [/, ]/', function(cm, vim, helpers) {
+ forEach(['*', '/'], function(key){
+ cm.setCursor(7, 0);
+ helpers.doKeys('2', '[', key);
+ helpers.assertCursorAt(2,2);
+ helpers.doKeys('2', ']', key);
+ helpers.assertCursorAt(7,5);
+ });
+}, { value: squareBracketMotionSandbox});
+testVim('[#, ]#', function(cm, vim, helpers) {
+ cm.setCursor(10, 3);
+ helpers.doKeys('2', '[', '#');
+ helpers.assertCursorAt(4,0);
+ helpers.doKeys('5', ']', '#');
+ helpers.assertCursorAt(17,0);
+ cm.setCursor(10, 3);
+ helpers.doKeys(']', '#');
+ helpers.assertCursorAt(14,0);
+}, { value: squareBracketMotionSandbox});
+testVim('[m, ]m, [M, ]M', function(cm, vim, helpers) {
+ cm.setCursor(11, 0);
+ helpers.doKeys('[', 'm');
+ helpers.assertCursorAt(10,7);
+ helpers.doKeys('4', '[', 'm');
+ helpers.assertCursorAt(1,3);
+ helpers.doKeys('5', ']', 'm');
+ helpers.assertCursorAt(11,0);
+ helpers.doKeys('[', 'M');
+ helpers.assertCursorAt(9,1);
+ helpers.doKeys('3', ']', 'M');
+ helpers.assertCursorAt(15,0);
+ helpers.doKeys('5', '[', 'M');
+ helpers.assertCursorAt(7,3);
+}, { value: squareBracketMotionSandbox});
+
+testVim('i_indent_right', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedValue = ' word1\nword2\nword3 ';
+ helpers.doKeys('i', '<C-t>');
+ eq(expectedValue, cm.getValue());
+ helpers.assertCursorAt(0, 5);
+}, { value: ' word1\nword2\nword3 ', indentUnit: 2 });
+testVim('i_indent_left', function(cm, vim, helpers) {
+ cm.setCursor(0, 3);
+ var expectedValue = ' word1\nword2\nword3 ';
+ helpers.doKeys('i', '<C-d>');
+ eq(expectedValue, cm.getValue());
+ helpers.assertCursorAt(0, 1);
+}, { value: ' word1\nword2\nword3 ', indentUnit: 2 });
+
+// Ex mode tests
+testVim('ex_go_to_line', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doEx('4');
+ helpers.assertCursorAt(3, 0);
+}, { value: 'a\nb\nc\nd\ne\n'});
+testVim('ex_go_to_mark', function(cm, vim, helpers) {
+ cm.setCursor(3, 0);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(0, 0);
+ helpers.doEx('\'a');
+ helpers.assertCursorAt(3, 0);
+}, { value: 'a\nb\nc\nd\ne\n'});
+testVim('ex_go_to_line_offset', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doEx('+3');
+ helpers.assertCursorAt(3, 0);
+ helpers.doEx('-1');
+ helpers.assertCursorAt(2, 0);
+ helpers.doEx('.2');
+ helpers.assertCursorAt(4, 0);
+ helpers.doEx('.-3');
+ helpers.assertCursorAt(1, 0);
+}, { value: 'a\nb\nc\nd\ne\n'});
+testVim('ex_go_to_mark_offset', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('m', 'a');
+ cm.setCursor(0, 0);
+ helpers.doEx('\'a1');
+ helpers.assertCursorAt(3, 0);
+ helpers.doEx('\'a-1');
+ helpers.assertCursorAt(1, 0);
+ helpers.doEx('\'a+2');
+ helpers.assertCursorAt(4, 0);
+}, { value: 'a\nb\nc\nd\ne\n'});
+testVim('ex_write', function(cm, vim, helpers) {
+ var tmp = CodeMirror.commands.save;
+ var written;
+ var actualCm;
+ CodeMirror.commands.save = function(cm) {
+ written = true;
+ actualCm = cm;
+ };
+ // Test that w, wr, wri ... write all trigger :write.
+ var command = 'write';
+ for (var i = 1; i < command.length; i++) {
+ written = false;
+ actualCm = null;
+ helpers.doEx(command.substring(0, i));
+ eq(written, true);
+ eq(actualCm, cm);
+ }
+ CodeMirror.commands.save = tmp;
+});
+testVim('ex_sort', function(cm, vim, helpers) {
+ helpers.doEx('sort');
+ eq('Z\na\nb\nc\nd', cm.getValue());
+}, { value: 'b\nZ\nd\nc\na'});
+testVim('ex_sort_reverse', function(cm, vim, helpers) {
+ helpers.doEx('sort!');
+ eq('d\nc\nb\na', cm.getValue());
+}, { value: 'b\nd\nc\na'});
+testVim('ex_sort_range', function(cm, vim, helpers) {
+ helpers.doEx('2,3sort');
+ eq('b\nc\nd\na', cm.getValue());
+}, { value: 'b\nd\nc\na'});
+testVim('ex_sort_oneline', function(cm, vim, helpers) {
+ helpers.doEx('2sort');
+ // Expect no change.
+ eq('b\nd\nc\na', cm.getValue());
+}, { value: 'b\nd\nc\na'});
+testVim('ex_sort_ignoreCase', function(cm, vim, helpers) {
+ helpers.doEx('sort i');
+ eq('a\nb\nc\nd\nZ', cm.getValue());
+}, { value: 'b\nZ\nd\nc\na'});
+testVim('ex_sort_unique', function(cm, vim, helpers) {
+ helpers.doEx('sort u');
+ eq('Z\na\nb\nc\nd', cm.getValue());
+}, { value: 'b\nZ\na\na\nd\na\nc\na'});
+testVim('ex_sort_decimal', function(cm, vim, helpers) {
+ helpers.doEx('sort d');
+ eq('d3\n s5\n6\n.9', cm.getValue());
+}, { value: '6\nd3\n s5\n.9'});
+testVim('ex_sort_decimal_negative', function(cm, vim, helpers) {
+ helpers.doEx('sort d');
+ eq('z-9\nd3\n s5\n6\n.9', cm.getValue());
+}, { value: '6\nd3\n s5\n.9\nz-9'});
+testVim('ex_sort_decimal_reverse', function(cm, vim, helpers) {
+ helpers.doEx('sort! d');
+ eq('.9\n6\n s5\nd3', cm.getValue());
+}, { value: '6\nd3\n s5\n.9'});
+testVim('ex_sort_hex', function(cm, vim, helpers) {
+ helpers.doEx('sort x');
+ eq(' s5\n6\n.9\n&0xB\nd3', cm.getValue());
+}, { value: '6\nd3\n s5\n&0xB\n.9'});
+testVim('ex_sort_octal', function(cm, vim, helpers) {
+ helpers.doEx('sort o');
+ eq('.9\n.8\nd3\n s5\n6', cm.getValue());
+}, { value: '6\nd3\n s5\n.9\n.8'});
+testVim('ex_sort_decimal_mixed', function(cm, vim, helpers) {
+ helpers.doEx('sort d');
+ eq('z\ny\nc1\nb2\na3', cm.getValue());
+}, { value: 'a3\nz\nc1\ny\nb2'});
+testVim('ex_sort_decimal_mixed_reverse', function(cm, vim, helpers) {
+ helpers.doEx('sort! d');
+ eq('a3\nb2\nc1\nz\ny', cm.getValue());
+}, { value: 'a3\nz\nc1\ny\nb2'});
+testVim('ex_sort_pattern_alpha', function(cm, vim, helpers) {
+ helpers.doEx('sort /[a-z]/');
+ eq('a3\nb2\nc1\ny\nz', cm.getValue());
+}, { value: 'z\ny\nc1\nb2\na3'});
+testVim('ex_sort_pattern_alpha_reverse', function(cm, vim, helpers) {
+ helpers.doEx('sort! /[a-z]/');
+ eq('z\ny\nc1\nb2\na3', cm.getValue());
+}, { value: 'z\ny\nc1\nb2\na3'});
+testVim('ex_sort_pattern_alpha_ignoreCase', function(cm, vim, helpers) {
+ helpers.doEx('sort i/[a-z]/');
+ eq('a3\nb2\nC1\nY\nz', cm.getValue());
+}, { value: 'z\nY\nC1\nb2\na3'});
+testVim('ex_sort_pattern_alpha_longer', function(cm, vim, helpers) {
+ helpers.doEx('sort /[a-z]+/');
+ eq('a\naa\nab\nade\nadele\nadelle\nadriana\nalex\nalexandra\nb\nc\ny\nz', cm.getValue());
+}, { value: 'z\nab\naa\nade\nadelle\nalexandra\nalex\nadriana\nadele\ny\nc\nb\na'});
+testVim('ex_sort_pattern_alpha_only', function(cm, vim, helpers) {
+ helpers.doEx('sort /^[a-z]$/');
+ eq('z1\ny2\na3\nb\nc', cm.getValue());
+}, { value: 'z1\ny2\na3\nc\nb'});
+testVim('ex_sort_pattern_alpha_only_reverse', function(cm, vim, helpers) {
+ helpers.doEx('sort! /^[a-z]$/');
+ eq('c\nb\nz1\ny2\na3', cm.getValue());
+}, { value: 'z1\ny2\na3\nc\nb'});
+testVim('ex_sort_pattern_alpha_num', function(cm, vim, helpers) {
+ helpers.doEx('sort /[a-z][0-9]/');
+ eq('c\nb\na3\ny2\nz1', cm.getValue());
+}, { value: 'z1\ny2\na3\nc\nb'});
+// test for :global command
+testVim('ex_global', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doEx('g/one/s//two');
+ eq('two two\n two two\n two two', cm.getValue());
+ helpers.doEx('1,2g/two/s//one');
+ eq('one one\n one one\n two two', cm.getValue());
+}, {value: 'one one\n one one\n one one'});
+testVim('ex_global_confirm', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ var onKeyDown;
+ var openDialogSave = cm.openDialog;
+ var KEYCODES = {
+ a: 65,
+ n: 78,
+ q: 81,
+ y: 89
+ };
+ // Intercept the ex command, 'global'
+ cm.openDialog = function(template, callback, options) {
+ // Intercept the prompt for the embedded ex command, 'substitute'
+ cm.openDialog = function(template, callback, options) {
+ onKeyDown = options.onKeyDown;
+ };
+ callback('g/one/s//two/gc');
+ };
+ helpers.doKeys(':');
+ var close = function() {};
+ onKeyDown({keyCode: KEYCODES.n}, '', close);
+ onKeyDown({keyCode: KEYCODES.y}, '', close);
+ onKeyDown({keyCode: KEYCODES.a}, '', close);
+ onKeyDown({keyCode: KEYCODES.q}, '', close);
+ onKeyDown({keyCode: KEYCODES.y}, '', close);
+ eq('one two\n two two\n one one\n two one\n one one', cm.getValue());
+}, {value: 'one one\n one one\n one one\n one one\n one one'});
+// Basic substitute tests.
+testVim('ex_substitute_same_line', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ helpers.doEx('s/one/two/g');
+ eq('one one\n two two', cm.getValue());
+}, { value: 'one one\n one one'});
+testVim('ex_substitute_alternate_separator', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ helpers.doEx('s#o/e#two#g');
+ eq('o/e o/e\n two two', cm.getValue());
+}, { value: 'o/e o/e\n o/e o/e'});
+testVim('ex_substitute_full_file', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ helpers.doEx('%s/one/two/g');
+ eq('two two\n two two', cm.getValue());
+}, { value: 'one one\n one one'});
+testVim('ex_substitute_input_range', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ helpers.doEx('1,3s/\\d/0/g');
+ eq('0\n0\n0\n4', cm.getValue());
+}, { value: '1\n2\n3\n4' });
+testVim('ex_substitute_range_current_to_input', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ helpers.doEx('.,3s/\\d/0/g');
+ eq('1\n0\n0\n4', cm.getValue());
+}, { value: '1\n2\n3\n4' });
+testVim('ex_substitute_range_input_to_current', function(cm, vim, helpers) {
+ cm.setCursor(3, 0);
+ helpers.doEx('2,.s/\\d/0/g');
+ eq('1\n0\n0\n0\n5', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_range_offset', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doEx('-1,+1s/\\d/0/g');
+ eq('1\n0\n0\n0\n5', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_range_implicit_offset', function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ helpers.doEx('.1,.3s/\\d/0/g');
+ eq('1\n0\n0\n0\n5', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_to_eof', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doEx('.,$s/\\d/0/g');
+ eq('1\n2\n0\n0\n0', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_to_relative_eof', function(cm, vim, helpers) {
+ cm.setCursor(4, 0);
+ helpers.doEx('2,$-2s/\\d/0/g');
+ eq('1\n0\n0\n4\n5', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_range_mark', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('ma');
+ cm.setCursor(0, 0);
+ helpers.doEx('.,\'as/\\d/0/g');
+ eq('0\n0\n0\n4\n5', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_range_mark_offset', function(cm, vim, helpers) {
+ cm.setCursor(2, 0);
+ helpers.doKeys('ma');
+ cm.setCursor(0, 0);
+ helpers.doEx('\'a-1,\'a+1s/\\d/0/g');
+ eq('1\n0\n0\n0\n5', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_visual_range', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ // Set last visual mode selection marks '< and '> at lines 2 and 4
+ helpers.doKeys('V', '2', 'j', 'v');
+ helpers.doEx('\'<,\'>s/\\d/0/g');
+ eq('1\n0\n0\n0\n5', cm.getValue());
+}, { value: '1\n2\n3\n4\n5' });
+testVim('ex_substitute_empty_query', function(cm, vim, helpers) {
+ // If the query is empty, use last query.
+ cm.setCursor(1, 0);
+ cm.openDialog = helpers.fakeOpenDialog('1');
+ helpers.doKeys('/');
+ helpers.doEx('s//b/g');
+ eq('abb ab2 ab3', cm.getValue());
+}, { value: 'a11 a12 a13' });
+testVim('ex_substitute_javascript', function(cm, vim, helpers) {
+ CodeMirror.Vim.setOption('pcre', false);
+ cm.setCursor(1, 0);
+ // Throw all the things that javascript likes to treat as special values
+ // into the replace part. All should be literal (this is VIM).
+ helpers.doEx('s/\\(\\d+\\)/$$ $\' $` $& \\1/g')
+ eq('a $$ $\' $` $& 0 b', cm.getValue());
+}, { value: 'a 0 b' });
+testVim('ex_substitute_empty_arguments', function(cm,vim,helpers) {
+ cm.setCursor(0, 0);
+ helpers.doEx('s/a/b/g');
+ cm.setCursor(1, 0);
+ helpers.doEx('s');
+ eq('b b\nb a', cm.getValue());
+}, {value: 'a a\na a'});
+
+// More complex substitute tests that test both pcre and nopcre options.
+function testSubstitute(name, options) {
+ testVim(name + '_pcre', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ CodeMirror.Vim.setOption('pcre', true);
+ helpers.doEx(options.expr);
+ eq(options.expectedValue, cm.getValue());
+ }, options);
+ // If no noPcreExpr is defined, assume that it's the same as the expr.
+ var noPcreExpr = options.noPcreExpr ? options.noPcreExpr : options.expr;
+ testVim(name + '_nopcre', function(cm, vim, helpers) {
+ cm.setCursor(1, 0);
+ CodeMirror.Vim.setOption('pcre', false);
+ helpers.doEx(noPcreExpr);
+ eq(options.expectedValue, cm.getValue());
+ }, options);
+}
+testSubstitute('ex_substitute_capture', {
+ value: 'a11 a12 a13',
+ expectedValue: 'a1111 a1212 a1313',
+ // $n is a backreference
+ expr: 's/(\\d+)/$1$1/g',
+ // \n is a backreference.
+ noPcreExpr: 's/\\(\\d+\\)/\\1\\1/g'});
+testSubstitute('ex_substitute_capture2', {
+ value: 'a 0 b',
+ expectedValue: 'a $00 b',
+ expr: 's/(\\d+)/$$$1$1/g',
+ noPcreExpr: 's/\\(\\d+\\)/$\\1\\1/g'});
+testSubstitute('ex_substitute_nocapture', {
+ value: 'a11 a12 a13',
+ expectedValue: 'a$1$1 a$1$1 a$1$1',
+ expr: 's/(\\d+)/$$1$$1/g',
+ noPcreExpr: 's/\\(\\d+\\)/$1$1/g'});
+testSubstitute('ex_substitute_nocapture2', {
+ value: 'a 0 b',
+ expectedValue: 'a $10 b',
+ expr: 's/(\\d+)/$$1$1/g',
+ noPcreExpr: 's/\\(\\d+\\)/\\$1\\1/g'});
+testSubstitute('ex_substitute_nocapture', {
+ value: 'a b c',
+ expectedValue: 'a $ c',
+ expr: 's/b/$$/',
+ noPcreExpr: 's/b/$/'});
+testSubstitute('ex_substitute_slash_regex', {
+ value: 'one/two \n three/four',
+ expectedValue: 'one|two \n three|four',
+ expr: '%s/\\//|'});
+testSubstitute('ex_substitute_pipe_regex', {
+ value: 'one|two \n three|four',
+ expectedValue: 'one,two \n three,four',
+ expr: '%s/\\|/,/',
+ noPcreExpr: '%s/|/,/'});
+testSubstitute('ex_substitute_or_regex', {
+ value: 'one|two \n three|four',
+ expectedValue: 'ana|twa \n thraa|faar',
+ expr: '%s/o|e|u/a/g',
+ noPcreExpr: '%s/o\\|e\\|u/a/g'});
+testSubstitute('ex_substitute_or_word_regex', {
+ value: 'one|two \n three|four',
+ expectedValue: 'five|five \n three|four',
+ expr: '%s/(one|two)/five/g',
+ noPcreExpr: '%s/\\(one\\|two\\)/five/g'});
+testSubstitute('ex_substitute_forward_slash_regex', {
+ value: 'forward slash \/ was here',
+ expectedValue: 'forward slash was here',
+ expr: '%s#\\/##g',
+ noPcreExpr: '%s#/##g'});
+testVim("ex_substitute_ampersand_pcre", function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ CodeMirror.Vim.setOption('pcre', true);
+ helpers.doEx('%s/foo/namespace.&/');
+ eq("namespace.foo", cm.getValue());
+ }, { value: 'foo' });
+testVim("ex_substitute_ampersand_multiple_pcre", function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ CodeMirror.Vim.setOption('pcre', true);
+ helpers.doEx('%s/f.o/namespace.&/');
+ eq("namespace.foo\nnamespace.fzo", cm.getValue());
+ }, { value: 'foo\nfzo' });
+testVim("ex_escaped_ampersand_should_not_substitute_pcre", function(cm, vim, helpers) {
+ cm.setCursor(0, 0);
+ CodeMirror.Vim.setOption('pcre', true);
+ helpers.doEx('%s/foo/namespace.\\&/');
+ eq("namespace.&", cm.getValue());
+ }, { value: 'foo' });
+testSubstitute('ex_substitute_backslashslash_regex', {
+ value: 'one\\two \n three\\four',
+ expectedValue: 'one,two \n three,four',
+ expr: '%s/\\\\/,'});
+testSubstitute('ex_substitute_slash_replacement', {
+ value: 'one,two \n three,four',
+ expectedValue: 'one/two \n three/four',
+ expr: '%s/,/\\/'});
+testSubstitute('ex_substitute_backslash_replacement', {
+ value: 'one,two \n three,four',
+ expectedValue: 'one\\two \n three\\four',
+ expr: '%s/,/\\\\/g'});
+testSubstitute('ex_substitute_multibackslash_replacement', {
+ value: 'one,two \n three,four',
+ expectedValue: 'one\\\\\\\\two \n three\\\\\\\\four', // 2*8 backslashes.
+ expr: '%s/,/\\\\\\\\\\\\\\\\/g'}); // 16 backslashes.
+testSubstitute('ex_substitute_dollar_match', {
+ value: 'one,two \n three,four',
+ expectedValue: 'one,two ,\n three,four',
+ expr: '%s/$/,/g'});
+testSubstitute('ex_substitute_newline_match', {
+ value: 'one,two \n three,four',
+ expectedValue: 'one,two , three,four',
+ expr: '%s/\\n/,/g'});
+testSubstitute('ex_substitute_newline_replacement', {
+ value: 'one,two \n three,four',
+ expectedValue: 'one\ntwo \n three\nfour',
+ expr: '%s/,/\\n/g'});
+testSubstitute('ex_substitute_braces_word', {
+ value: 'ababab abb ab{2}',
+ expectedValue: 'ab abb ab{2}',
+ expr: '%s/(ab){2}//g',
+ noPcreExpr: '%s/\\(ab\\)\\{2\\}//g'});
+testSubstitute('ex_substitute_braces_range', {
+ value: 'a aa aaa aaaa',
+ expectedValue: 'a a',
+ expr: '%s/a{2,3}//g',
+ noPcreExpr: '%s/a\\{2,3\\}//g'});
+testSubstitute('ex_substitute_braces_literal', {
+ value: 'ababab abb ab{2}',
+ expectedValue: 'ababab abb ',
+ expr: '%s/ab\\{2\\}//g',
+ noPcreExpr: '%s/ab{2}//g'});
+testSubstitute('ex_substitute_braces_char', {
+ value: 'ababab abb ab{2}',
+ expectedValue: 'ababab ab{2}',
+ expr: '%s/ab{2}//g',
+ noPcreExpr: '%s/ab\\{2\\}//g'});
+testSubstitute('ex_substitute_braces_no_escape', {
+ value: 'ababab abb ab{2}',
+ expectedValue: 'ababab ab{2}',
+ expr: '%s/ab{2}//g',
+ noPcreExpr: '%s/ab\\{2}//g'});
+testSubstitute('ex_substitute_count', {
+ value: '1\n2\n3\n4',
+ expectedValue: '1\n0\n0\n4',
+ expr: 's/\\d/0/i 2'});
+testSubstitute('ex_substitute_count_with_range', {
+ value: '1\n2\n3\n4',
+ expectedValue: '1\n2\n0\n0',
+ expr: '1,3s/\\d/0/ 3'});
+testSubstitute('ex_substitute_not_global', {
+ value: 'aaa\nbaa\ncaa',
+ expectedValue: 'xaa\nbxa\ncxa',
+ expr: '%s/a/x/'});
+function testSubstituteConfirm(name, command, initialValue, expectedValue, keys, finalPos) {
+ testVim(name, function(cm, vim, helpers) {
+ var savedOpenDialog = cm.openDialog;
+ var savedKeyName = CodeMirror.keyName;
+ var onKeyDown;
+ var recordedCallback;
+ var closed = true; // Start out closed, set false on second openDialog.
+ function close() {
+ closed = true;
+ }
+ // First openDialog should save callback.
+ cm.openDialog = function(template, callback, options) {
+ recordedCallback = callback;
+ }
+ // Do first openDialog.
+ helpers.doKeys(':');
+ // Second openDialog should save keyDown handler.
+ cm.openDialog = function(template, callback, options) {
+ onKeyDown = options.onKeyDown;
+ closed = false;
+ };
+ // Return the command to Vim and trigger second openDialog.
+ recordedCallback(command);
+ // The event should really use keyCode, but here just mock it out and use
+ // key and replace keyName to just return key.
+ CodeMirror.keyName = function (e) { return e.key; }
+ keys = keys.toUpperCase();
+ for (var i = 0; i < keys.length; i++) {
+ is(!closed);
+ onKeyDown({ key: keys.charAt(i) }, '', close);
+ }
+ try {
+ eq(expectedValue, cm.getValue());
+ helpers.assertCursorAt(finalPos);
+ is(closed);
+ } catch(e) {
+ throw e
+ } finally {
+ // Restore overridden functions.
+ CodeMirror.keyName = savedKeyName;
+ cm.openDialog = savedOpenDialog;
+ }
+ }, { value: initialValue });
+}
+testSubstituteConfirm('ex_substitute_confirm_emptydoc',
+ '%s/x/b/c', '', '', '', makeCursor(0, 0));
+testSubstituteConfirm('ex_substitute_confirm_nomatch',
+ '%s/x/b/c', 'ba a\nbab', 'ba a\nbab', '', makeCursor(0, 0));
+testSubstituteConfirm('ex_substitute_confirm_accept',
+ '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'yyy', makeCursor(1, 1));
+testSubstituteConfirm('ex_substitute_confirm_random_keys',
+ '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'ysdkywerty', makeCursor(1, 1));
+testSubstituteConfirm('ex_substitute_confirm_some',
+ '%s/a/b/cg', 'ba a\nbab', 'bb a\nbbb', 'yny', makeCursor(1, 1));
+testSubstituteConfirm('ex_substitute_confirm_all',
+ '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'a', makeCursor(1, 1));
+testSubstituteConfirm('ex_substitute_confirm_accept_then_all',
+ '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'ya', makeCursor(1, 1));
+testSubstituteConfirm('ex_substitute_confirm_quit',
+ '%s/a/b/cg', 'ba a\nbab', 'bb a\nbab', 'yq', makeCursor(0, 3));
+testSubstituteConfirm('ex_substitute_confirm_last',
+ '%s/a/b/cg', 'ba a\nbab', 'bb b\nbab', 'yl', makeCursor(0, 3));
+testSubstituteConfirm('ex_substitute_confirm_oneline',
+ '1s/a/b/cg', 'ba a\nbab', 'bb b\nbab', 'yl', makeCursor(0, 3));
+testSubstituteConfirm('ex_substitute_confirm_range_accept',
+ '1,2s/a/b/cg', 'aa\na \na\na', 'bb\nb \na\na', 'yyy', makeCursor(1, 0));
+testSubstituteConfirm('ex_substitute_confirm_range_some',
+ '1,3s/a/b/cg', 'aa\na \na\na', 'ba\nb \nb\na', 'ynyy', makeCursor(2, 0));
+testSubstituteConfirm('ex_substitute_confirm_range_all',
+ '1,3s/a/b/cg', 'aa\na \na\na', 'bb\nb \nb\na', 'a', makeCursor(2, 0));
+testSubstituteConfirm('ex_substitute_confirm_range_last',
+ '1,3s/a/b/cg', 'aa\na \na\na', 'bb\nb \na\na', 'yyl', makeCursor(1, 0));
+//:noh should clear highlighting of search-results but allow to resume search through n
+testVim('ex_noh_clearSearchHighlight', function(cm, vim, helpers) {
+ cm.openDialog = helpers.fakeOpenDialog('match');
+ helpers.doKeys('?');
+ helpers.doEx('noh');
+ eq(vim.searchState_.getOverlay(),null,'match-highlighting wasn\'t cleared');
+ helpers.doKeys('n');
+ helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting');
+}, { value: 'match nope match \n nope Match' });
+testVim('ex_yank', function (cm, vim, helpers) {
+ var curStart = makeCursor(3, 0);
+ cm.setCursor(curStart);
+ helpers.doEx('y');
+ var register = helpers.getRegisterController().getRegister();
+ var line = cm.getLine(3);
+ eq(line + '\n', register.toString());
+});
+testVim('set_boolean', function(cm, vim, helpers) {
+ CodeMirror.Vim.defineOption('testoption', true, 'boolean');
+ // Test default value is set.
+ is(CodeMirror.Vim.getOption('testoption'));
+ // Test fail to set to non-boolean
+ var result = CodeMirror.Vim.setOption('testoption', '5');
+ is(result instanceof Error);
+ // Test setOption
+ CodeMirror.Vim.setOption('testoption', false);
+ is(!CodeMirror.Vim.getOption('testoption'));
+});
+testVim('ex_set_boolean', function(cm, vim, helpers) {
+ CodeMirror.Vim.defineOption('testoption', true, 'boolean');
+ // Test default value is set.
+ is(CodeMirror.Vim.getOption('testoption'));
+ is(!cm.state.currentNotificationClose);
+ // Test fail to set to non-boolean
+ helpers.doEx('set testoption=22');
+ is(cm.state.currentNotificationClose);
+ // Test setOption
+ helpers.doEx('set notestoption');
+ is(!CodeMirror.Vim.getOption('testoption'));
+});
+testVim('set_string', function(cm, vim, helpers) {
+ CodeMirror.Vim.defineOption('testoption', 'a', 'string');
+ // Test default value is set.
+ eq('a', CodeMirror.Vim.getOption('testoption'));
+ // Test no fail to set non-string.
+ var result = CodeMirror.Vim.setOption('testoption', true);
+ is(!result);
+ // Test fail to set 'notestoption'
+ result = CodeMirror.Vim.setOption('notestoption', 'b');
+ is(result instanceof Error);
+ // Test setOption
+ CodeMirror.Vim.setOption('testoption', 'c');
+ eq('c', CodeMirror.Vim.getOption('testoption'));
+});
+testVim('ex_set_string', function(cm, vim, helpers) {
+ CodeMirror.Vim.defineOption('testopt', 'a', 'string');
+ // Test default value is set.
+ eq('a', CodeMirror.Vim.getOption('testopt'));
+ // Test fail to set 'notestopt'
+ is(!cm.state.currentNotificationClose);
+ helpers.doEx('set notestopt=b');
+ is(cm.state.currentNotificationClose);
+ // Test setOption
+ helpers.doEx('set testopt=c')
+ eq('c', CodeMirror.Vim.getOption('testopt'));
+ helpers.doEx('set testopt=c')
+ eq('c', CodeMirror.Vim.getOption('testopt', cm)); //local || global
+ eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); // local
+ eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); // global
+ eq('c', CodeMirror.Vim.getOption('testopt')); // global
+ // Test setOption global
+ helpers.doEx('setg testopt=d')
+ eq('c', CodeMirror.Vim.getOption('testopt', cm));
+ eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'}));
+ eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'}));
+ eq('d', CodeMirror.Vim.getOption('testopt'));
+ // Test setOption local
+ helpers.doEx('setl testopt=e')
+ eq('e', CodeMirror.Vim.getOption('testopt', cm));
+ eq('e', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'}));
+ eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'}));
+ eq('d', CodeMirror.Vim.getOption('testopt'));
+});
+testVim('ex_set_callback', function(cm, vim, helpers) {
+ var global;
+
+ function cb(val, cm, cfg) {
+ if (val === undefined) {
+ // Getter
+ if (cm) {
+ return cm._local;
+ } else {
+ return global;
+ }
+ } else {
+ // Setter
+ if (cm) {
+ cm._local = val;
+ } else {
+ global = val;
+ }
+ }
+ }
+
+ CodeMirror.Vim.defineOption('testopt', 'a', 'string', cb);
+ // Test default value is set.
+ eq('a', CodeMirror.Vim.getOption('testopt'));
+ // Test fail to set 'notestopt'
+ is(!cm.state.currentNotificationClose);
+ helpers.doEx('set notestopt=b');
+ is(cm.state.currentNotificationClose);
+ // Test setOption (Identical to the string tests, but via callback instead)
+ helpers.doEx('set testopt=c')
+ eq('c', CodeMirror.Vim.getOption('testopt', cm)); //local || global
+ eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'})); // local
+ eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'})); // global
+ eq('c', CodeMirror.Vim.getOption('testopt')); // global
+ // Test setOption global
+ helpers.doEx('setg testopt=d')
+ eq('c', CodeMirror.Vim.getOption('testopt', cm));
+ eq('c', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'}));
+ eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'}));
+ eq('d', CodeMirror.Vim.getOption('testopt'));
+ // Test setOption local
+ helpers.doEx('setl testopt=e')
+ eq('e', CodeMirror.Vim.getOption('testopt', cm));
+ eq('e', CodeMirror.Vim.getOption('testopt', cm, {scope: 'local'}));
+ eq('d', CodeMirror.Vim.getOption('testopt', cm, {scope: 'global'}));
+ eq('d', CodeMirror.Vim.getOption('testopt'));
+})
+testVim('ex_set_filetype', function(cm, vim, helpers) {
+ CodeMirror.defineMode('test_mode', function() {
+ return {token: function(stream) {
+ stream.match(/^\s+|^\S+/);
+ }};
+ });
+ CodeMirror.defineMode('test_mode_2', function() {
+ return {token: function(stream) {
+ stream.match(/^\s+|^\S+/);
+ }};
+ });
+ // Test mode is set.
+ helpers.doEx('set filetype=test_mode');
+ eq('test_mode', cm.getMode().name);
+ // Test 'ft' alias also sets mode.
+ helpers.doEx('set ft=test_mode_2');
+ eq('test_mode_2', cm.getMode().name);
+});
+testVim('ex_set_filetype_null', function(cm, vim, helpers) {
+ CodeMirror.defineMode('test_mode', function() {
+ return {token: function(stream) {
+ stream.match(/^\s+|^\S+/);
+ }};
+ });
+ cm.setOption('mode', 'test_mode');
+ // Test mode is set to null.
+ helpers.doEx('set filetype=');
+ eq('null', cm.getMode().name);
+});
+
+testVim('mapclear', function(cm, vim, helpers) {
+ CodeMirror.Vim.map('w', 'l');
+ cm.setCursor(0, 0);
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('w');
+ helpers.assertCursorAt(0, 1);
+ CodeMirror.Vim.mapclear('visual');
+ helpers.doKeys('v', 'w', 'v');
+ helpers.assertCursorAt(0, 4);
+ helpers.doKeys('w');
+ helpers.assertCursorAt(0, 5);
+ CodeMirror.Vim.mapclear();
+}, { value: 'abc abc' });
+testVim('mapclear_context', function(cm, vim, helpers) {
+ CodeMirror.Vim.map('w', 'l', 'normal');
+ cm.setCursor(0, 0);
+ helpers.assertCursorAt(0, 0);
+ helpers.doKeys('w');
+ helpers.assertCursorAt(0, 1);
+ CodeMirror.Vim.mapclear('normal');
+ helpers.doKeys('w');
+ helpers.assertCursorAt(0, 4);
+ CodeMirror.Vim.mapclear();
+}, { value: 'abc abc' });
+
+testVim('ex_map_key2key', function(cm, vim, helpers) {
+ helpers.doEx('map a x');
+ helpers.doKeys('a');
+ helpers.assertCursorAt(0, 0);
+ eq('bc', cm.getValue());
+ CodeMirror.Vim.mapclear();
+}, { value: 'abc' });
+testVim('ex_unmap_key2key', function(cm, vim, helpers) {
+ helpers.doEx('map a x');
+ helpers.doEx('unmap a');
+ helpers.doKeys('a');
+ eq('vim-insert', cm.getOption('keyMap'));
+ CodeMirror.Vim.mapclear();
+}, { value: 'abc' });
+testVim('ex_unmap_key2key_does_not_remove_default', function(cm, vim, helpers) {
+ expectFail(function() {
+ helpers.doEx('unmap a');
+ });
+ helpers.doKeys('a');
+ eq('vim-insert', cm.getOption('keyMap'));
+ CodeMirror.Vim.mapclear();
+}, { value: 'abc' });
+testVim('ex_map_key2key_to_colon', function(cm, vim, helpers) {
+ helpers.doEx('map ; :');
+ var dialogOpened = false;
+ cm.openDialog = function() {
+ dialogOpened = true;
+ }
+ helpers.doKeys(';');
+ eq(dialogOpened, true);
+ CodeMirror.Vim.mapclear();
+});
+testVim('ex_map_ex2key:', function(cm, vim, helpers) {
+ helpers.doEx('map :del x');
+ helpers.doEx('del');
+ helpers.assertCursorAt(0, 0);
+ eq('bc', cm.getValue());
+ CodeMirror.Vim.mapclear();
+}, { value: 'abc' });
+testVim('ex_map_ex2ex', function(cm, vim, helpers) {
+ helpers.doEx('map :del :w');
+ var tmp = CodeMirror.commands.save;
+ var written = false;
+ var actualCm;
+ CodeMirror.commands.save = function(cm) {
+ written = true;
+ actualCm = cm;
+ };
+ helpers.doEx('del');
+ CodeMirror.commands.save = tmp;
+ eq(written, true);
+ eq(actualCm, cm);
+ CodeMirror.Vim.mapclear();
+});
+testVim('ex_map_key2ex', function(cm, vim, helpers) {
+ helpers.doEx('map a :w');
+ var tmp = CodeMirror.commands.save;
+ var written = false;
+ var actualCm;
+ CodeMirror.commands.save = function(cm) {
+ written = true;
+ actualCm = cm;
+ };
+ helpers.doKeys('a');
+ CodeMirror.commands.save = tmp;
+ eq(written, true);
+ eq(actualCm, cm);
+ CodeMirror.Vim.mapclear();
+});
+testVim('ex_map_key2key_visual_api', function(cm, vim, helpers) {
+ CodeMirror.Vim.map('b', ':w', 'visual');
+ var tmp = CodeMirror.commands.save;
+ var written = false;
+ var actualCm;
+ CodeMirror.commands.save = function(cm) {
+ written = true;
+ actualCm = cm;
+ };
+ // Mapping should not work in normal mode.
+ helpers.doKeys('b');
+ eq(written, false);
+ // Mapping should work in visual mode.
+ helpers.doKeys('v', 'b');
+ eq(written, true);
+ eq(actualCm, cm);
+
+ CodeMirror.commands.save = tmp;
+ CodeMirror.Vim.mapclear();
+});
+testVim('ex_imap', function(cm, vim, helpers) {
+ CodeMirror.Vim.map('jk', '<Esc>', 'insert');
+ helpers.doKeys('i');
+ is(vim.insertMode);
+ helpers.doKeys('j', 'k');
+ is(!vim.insertMode);
+ cm.setCursor(0, 1);
+ CodeMirror.Vim.map('jj', '<Esc>', 'insert');
+ helpers.doKeys('<C-v>', '2', 'j', 'l', 'c');
+ helpers.doKeys('f', 'o');
+ eq('1fo4\n5fo8\nafodefg', cm.getValue());
+ helpers.doKeys('j', 'j');
+ cm.setCursor(0, 0);
+ helpers.doKeys('.');
+ eq('foo4\nfoo8\nfoodefg', cm.getValue());
+ CodeMirror.Vim.mapclear();
+}, { value: '1234\n5678\nabcdefg' });
+testVim('ex_unmap_api', function(cm, vim, helpers) {
+ CodeMirror.Vim.map('<Alt-X>', 'gg', 'normal');
+ is(CodeMirror.Vim.handleKey(cm, "<Alt-X>", "normal"), "Alt-X key is mapped");
+ CodeMirror.Vim.unmap("<Alt-X>", "normal");
+ is(!CodeMirror.Vim.handleKey(cm, "<Alt-X>", "normal"), "Alt-X key is unmapped");
+ CodeMirror.Vim.mapclear();
+});
+// Testing registration of functions as ex-commands and mapping to <Key>-keys
+testVim('ex_api_test', function(cm, vim, helpers) {
+ var res=false;
+ var val='from';
+ CodeMirror.Vim.defineEx('extest','ext',function(cm,params){
+ if(params.args)val=params.args[0];
+ else res=true;
+ });
+ helpers.doEx(':ext to');
+ eq(val,'to','Defining ex-command failed');
+ CodeMirror.Vim.map('<C-CR><Space>',':ext');
+ helpers.doKeys('<C-CR>','<Space>');
+ is(res,'Mapping to key failed');
+ CodeMirror.Vim.mapclear();
+});
+// For now, this test needs to be last because it messes up : for future tests.
+testVim('ex_map_key2key_from_colon', function(cm, vim, helpers) {
+ helpers.doEx('map : x');
+ helpers.doKeys(':');
+ helpers.assertCursorAt(0, 0);
+ eq('bc', cm.getValue());
+ CodeMirror.Vim.mapclear();
+}, { value: 'abc' });
+
+testVim('noremap', function(cm, vim, helpers) {
+ CodeMirror.Vim.noremap(';', 'l');
+ cm.setCursor(0, 0);
+ eq('wOrd1', cm.getValue());
+ // Mapping should work in normal mode.
+ helpers.doKeys(';', 'r', '1');
+ eq('w1rd1', cm.getValue());
+ // Mapping will not work in insert mode because of no current fallback
+ // keyToKey mapping support.
+ helpers.doKeys('i', ';', '<Esc>');
+ eq('w;1rd1', cm.getValue());
+ // unmap all mappings
+ CodeMirror.Vim.mapclear();
+}, { value: 'wOrd1' });
+testVim('noremap_swap', function(cm, vim, helpers) {
+ CodeMirror.Vim.noremap('i', 'a', 'normal');
+ CodeMirror.Vim.noremap('a', 'i', 'normal');
+ cm.setCursor(0, 0);
+ // 'a' should act like 'i'.
+ helpers.doKeys('a');
+ eqCursorPos(Pos(0, 0), cm.getCursor());
+ // ...and 'i' should act like 'a'.
+ helpers.doKeys('<Esc>', 'i');
+ eqCursorPos(Pos(0, 1), cm.getCursor());
+ // unmap all mappings
+ CodeMirror.Vim.mapclear();
+}, { value: 'foo' });
+testVim('noremap_map_interaction', function(cm, vim, helpers) {
+ // noremap should clobber map
+ CodeMirror.Vim.map(';', 'l');
+ CodeMirror.Vim.noremap(';', 'l');
+ CodeMirror.Vim.map('l', 'j');
+ cm.setCursor(0, 0);
+ helpers.doKeys(';');
+ eqCursorPos(Pos(0, 1), cm.getCursor());
+ helpers.doKeys('l');
+ eqCursorPos(Pos(1, 1), cm.getCursor());
+ // map should be able to point to a noremap
+ CodeMirror.Vim.map('m', ';');
+ helpers.doKeys('m');
+ eqCursorPos(Pos(1, 2), cm.getCursor());
+ // unmap all mappings
+ CodeMirror.Vim.mapclear();
+}, { value: 'wOrd1\nwOrd2' });
+testVim('noremap_map_interaction2', function(cm, vim, helpers) {
+ // map should point to the most recent noremap
+ CodeMirror.Vim.noremap(';', 'l');
+ CodeMirror.Vim.map('m', ';');
+ CodeMirror.Vim.noremap(';', 'h');
+ cm.setCursor(0, 0);
+ helpers.doKeys('l');
+ eqCursorPos(Pos(0, 1), cm.getCursor());
+ helpers.doKeys('m');
+ eqCursorPos(Pos(0, 0), cm.getCursor());
+ // unmap all mappings
+ CodeMirror.Vim.mapclear();
+}, { value: 'wOrd1\nwOrd2' });
+
+// Test event handlers
+testVim('beforeSelectionChange', function(cm, vim, helpers) {
+ cm.setCursor(0, 100);
+ eqCursorPos(cm.getCursor('head'), cm.getCursor('anchor'));
+}, { value: 'abc' });
+
+testVim('increment_binary', function(cm, vim, helpers) {
+ cm.setCursor(0, 4);
+ helpers.doKeys('<C-a>');
+ eq('0b001', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0b010', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0b001', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0b000', cm.getValue());
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-a>');
+ eq('0b001', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0b010', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0b001', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0b000', cm.getValue());
+}, { value: '0b000' });
+
+testVim('increment_octal', function(cm, vim, helpers) {
+ cm.setCursor(0, 2);
+ helpers.doKeys('<C-a>');
+ eq('001', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('002', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('003', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('004', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('005', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('006', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('007', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('010', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('007', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('006', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('005', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('004', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('003', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('002', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('001', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('000', cm.getValue());
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-a>');
+ eq('001', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('002', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('001', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('000', cm.getValue());
+}, { value: '000' });
+
+testVim('increment_decimal', function(cm, vim, helpers) {
+ cm.setCursor(0, 2);
+ helpers.doKeys('<C-a>');
+ eq('101', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('102', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('103', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('104', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('105', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('106', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('107', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('108', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('109', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('110', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('109', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('108', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('107', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('106', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('105', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('104', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('103', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('102', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('101', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('100', cm.getValue());
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-a>');
+ eq('101', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('102', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('101', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('100', cm.getValue());
+}, { value: '100' });
+
+testVim('increment_decimal_single_zero', function(cm, vim, helpers) {
+ helpers.doKeys('<C-a>');
+ eq('1', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('2', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('3', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('4', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('5', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('6', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('7', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('8', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('9', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('10', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('9', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('8', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('7', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('6', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('5', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('4', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('3', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('2', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('1', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0', cm.getValue());
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-a>');
+ eq('1', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('2', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('1', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0', cm.getValue());
+}, { value: '0' });
+
+testVim('increment_hexadecimal', function(cm, vim, helpers) {
+ cm.setCursor(0, 2);
+ helpers.doKeys('<C-a>');
+ eq('0x1', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x2', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x3', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x4', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x5', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x6', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x7', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x8', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x9', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0xa', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0xb', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0xc', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0xd', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0xe', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0xf', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x10', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x0f', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x0e', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x0d', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x0c', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x0b', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x0a', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x09', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x08', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x07', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x06', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x05', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x04', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x03', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x02', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x01', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x00', cm.getValue());
+ cm.setCursor(0, 0);
+ helpers.doKeys('<C-a>');
+ eq('0x01', cm.getValue());
+ helpers.doKeys('<C-a>');
+ eq('0x02', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x01', cm.getValue());
+ helpers.doKeys('<C-x>');
+ eq('0x00', cm.getValue());
+}, { value: '0x0' });
diff --git a/devtools/client/shared/sourceeditor/test/codemirror/vimemacs.html b/devtools/client/shared/sourceeditor/test/codemirror/vimemacs.html
new file mode 100644
index 0000000000..5d56b57b11
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/codemirror/vimemacs.html
@@ -0,0 +1,215 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>CodeMirror: VIM/Emacs tests</title>
+ <link rel="stylesheet" href="chrome://devtools/content/shared/sourceeditor/codemirror/lib/codemirror.css">
+ <link rel="stylesheet" href="cm_mode_test.css">
+ <!--<link rel="stylesheet" href="../doc/docs.css">-->
+
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/codemirror.bundle.js"></script>
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/keymap/emacs.js"></script>
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/keymap/sublime.js"></script>
+ <script src="chrome://devtools/content/shared/sourceeditor/codemirror/keymap/vim.js"></script>
+
+ <style type="text/css">
+ .ok {color: #090;}
+ .fail {color: #e00;}
+ .error {color: #c90;}
+ .done {font-weight: bold;}
+ #progress {
+ background: #45d;
+ color: white;
+ text-shadow: 0 0 1px #45d, 0 0 2px #45d, 0 0 3px #45d;
+ font-weight: bold;
+ white-space: pre;
+ }
+ #testground {
+ visibility: hidden;
+ }
+ #testground.offscreen {
+ visibility: visible;
+ position: absolute;
+ left: -10000px;
+ top: -10000px;
+ }
+ .CodeMirror { border: 1px solid black; }
+ </style>
+ </head>
+ <body>
+ <h1>CodeMirror: VIM/Emacs tests</h1>
+
+ <p>A limited set of programmatic sanity tests for CodeMirror.</p>
+
+ <div style="border: 1px solid black; padding: 1px; max-width: 700px;">
+ <div style="width: 0px;" id=progress><div style="padding: 3px;">Ran <span id="progress_ran">0</span><span id="progress_total"> of 0</span> tests</div></div>
+ </div>
+ <p id=status>Please enable JavaScript...</p>
+ <div id=output></div>
+
+ <div id=testground></div>
+
+ <script src="driver.js"></script>
+ <script src="sublime_test.js"></script>
+ <script src="vim_test.js"></script>
+ <script src="emacs_test.js"></script>
+
+ <!-- Basic tests are in codemirror.html
+ <script src="cm_driver.js"></script>
+ <script src="cm_test.js"></script>
+ <script src="cm_comment_test.js"></script>
+ <script src="cm_doc_test.js"></script>
+ <script src="cm_driver.js"></script>
+ <script src="cm_emacs_test.js"></script>
+ <script src="cm_mode_test.js"></script>
+ <script src="cm_mode_javascript_test.js"></script>
+ <script src="cm_multi_test.js"></script>
+ <script src="cm_search_test.js"></script>
+ -->
+
+ <!-- These modes/addons are not used by Editor
+ <script src="doc_test.js"></script>
+ <script src="../mode/css/css.js"></script>
+ <script src="../mode/css/test.js"></script>
+ <script src="../mode/css/scss_test.js"></script>
+ <script src="../mode/xml/xml.js"></script>
+ <script src="../mode/htmlmixed/htmlmixed.js"></script>
+ <script src="../mode/ruby/ruby.js"></script>
+ <script src="../mode/haml/haml.js"></script>
+ <script src="../mode/haml/test.js"></script>
+ <script src="../mode/markdown/markdown.js"></script>
+ <script src="../mode/markdown/test.js"></script>
+ <script src="../mode/gfm/gfm.js"></script>
+ <script src="../mode/gfm/test.js"></script>
+ <script src="../mode/stex/stex.js"></script>
+ <script src="../mode/stex/test.js"></script>
+ <script src="../mode/xquery/xquery.js"></script>
+ <script src="../mode/xquery/test.js"></script>
+ <script src="../addon/mode/multiplex_test.js"></script>-->
+
+ <script>
+ window.onload = runHarness;
+ CodeMirror.on(window, 'hashchange', runHarness);
+
+ function esc(str) {
+ return str.replace(/[<&]/, function(ch) { return ch == "<" ? "&lt;" : "&amp;"; });
+ }
+
+ var output = document.getElementById("output"),
+ progress = document.getElementById("progress"),
+ progressRan = document.getElementById("progress_ran").childNodes[0],
+ progressTotal = document.getElementById("progress_total").childNodes[0];
+
+ var count = 0,
+ failed = 0,
+ skipped = 0,
+ bad = "",
+ running = false, // Flag that states tests are running
+ quit = false, // Flag to quit tests ASAP
+ verbose = false, // Adds message for *every* test to output
+ phantom = false,
+ Pos = CodeMirror.Pos; // Required for VIM tests
+
+ function runHarness(){
+ if (running) {
+ quit = true;
+ setStatus("Restarting tests...", '', true);
+ setTimeout(function(){runHarness();}, 500);
+ return;
+ }
+ filters = [];
+ verbose = false;
+ if (window.location.hash.substr(1)){
+ var strings = window.location.hash.substr(1).split(",");
+ while (strings.length) {
+ var s = strings.shift();
+ if (s === "verbose")
+ verbose = true;
+ else
+ filters.push(parseTestFilter(decodeURIComponent(s)));
+ }
+ }
+ quit = false;
+ running = true;
+ setStatus("Loading tests...");
+ count = 0;
+ failed = 0;
+ skipped = 0;
+ bad = "";
+ totalTests = countTests();
+ progressTotal.nodeValue = " of " + totalTests;
+ progressRan.nodeValue = count;
+ output.innerHTML = '';
+ document.getElementById("testground").innerHTML = "<form>" +
+ "<textarea id=\"code\" name=\"code\"></textarea>" +
+ "<input type=submit value=ok name=submit>" +
+ "</form>";
+ runTests(displayTest);
+ }
+
+ function setStatus(message, className, force){
+ if (quit && !force) return;
+ if (!message) throw("must provide message");
+ var status = document.getElementById("status").childNodes[0];
+ status.nodeValue = message;
+ status.parentNode.className = className;
+ }
+ function addOutput(name, className, code){
+ var newOutput = document.createElement("dl");
+ var newTitle = document.createElement("dt");
+ newTitle.className = className;
+ newTitle.appendChild(document.createTextNode(name));
+ newOutput.appendChild(newTitle);
+ var newMessage = document.createElement("dd");
+ newMessage.innerHTML = code;
+ newOutput.appendChild(newTitle);
+ newOutput.appendChild(newMessage);
+ output.appendChild(newOutput);
+ }
+ function displayTest(type, name, customMessage) {
+ var message = "???";
+ if (type != "done" && type != "skipped") ++count;
+ progress.style.width = (count * (progress.parentNode.clientWidth - 2) / totalTests) + "px";
+ progressRan.nodeValue = count;
+ if (type == "ok") {
+ message = "Test '" + name + "' succeeded";
+ if (!verbose) customMessage = false;
+ } else if (type == "skipped") {
+ message = "Test '" + name + "' skipped";
+ ++skipped;
+ if (!verbose) customMessage = false;
+ } else if (type == "expected") {
+ message = "Test '" + name + "' failed as expected";
+ if (!verbose) customMessage = false;
+ } else if (type == "error" || type == "fail") {
+ ++failed;
+ message = "Test '" + name + "' failed";
+ } else if (type == "done") {
+ if (failed) {
+ type += " fail";
+ message = failed + " failure" + (failed > 1 ? "s" : "");
+ } else if (count < totalTests) {
+ failed = totalTests - count;
+ type += " fail";
+ message = failed + " failure" + (failed > 1 ? "s" : "");
+ } else {
+ type += " ok";
+ message = "All passed";
+ if (skipped) {
+ message += " (" + skipped + " skipped)";
+ }
+ }
+ progressTotal.nodeValue = '';
+ customMessage = true; // Hack to avoid adding to output
+ }
+ if (window.mozilla_setStatus)
+ mozilla_setStatus(message, type, customMessage);
+ if (verbose && !customMessage) customMessage = message;
+ setStatus(message, type);
+ if (customMessage && customMessage.length > 0) {
+ addOutput(name, type, customMessage);
+ }
+ }
+ </script>
+ </body>
+</html>
diff --git a/devtools/client/shared/sourceeditor/test/css_autocompletion_tests.json b/devtools/client/shared/sourceeditor/test/css_autocompletion_tests.json
new file mode 100644
index 0000000000..a8f09e0741
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/css_autocompletion_tests.json
@@ -0,0 +1,106 @@
+{
+ "description": [
+ "Test states to be tested for css state machine in css-autocompleter.js file.",
+ "Test cases are of the following format:",
+ "[",
+ " [",
+ " line, # The line location of the cursor",
+ " ch # The column location of the cursor",
+ " ],",
+ " suggestions # Array of expected results",
+ "]"
+ ],
+ "tests": [
+ [[0, 10], []],
+ [
+ [4, 7],
+ [".devtools-menulist", ".devtools-toolbarbutton"]
+ ],
+ [
+ [5, 8],
+ [
+ "-moz-animation",
+ "-moz-animation-delay",
+ "-moz-animation-direction",
+ "-moz-animation-duration",
+ "-moz-animation-fill-mode",
+ "-moz-animation-iteration-count",
+ "-moz-animation-name",
+ "-moz-animation-play-state",
+ "-moz-animation-timing-function",
+ "-moz-appearance"
+ ]
+ ],
+ [
+ [12, 20],
+ ["none", "number-input"]
+ ],
+ [[12, 22], ["none"]],
+ [
+ [17, 22],
+ ["hsl", "hsla"]
+ ],
+ [
+ [19, 10],
+ [
+ "background",
+ "background-attachment",
+ "background-blend-mode",
+ "background-clip",
+ "background-color",
+ "background-image",
+ "background-origin",
+ "background-position",
+ "background-position-x",
+ "background-position-y",
+ "background-repeat",
+ "background-size"
+ ]
+ ],
+ [
+ [21, 9],
+ ["auto", "inherit", "initial", "revert", "revert-layer", "unset"]
+ ],
+ [
+ [25, 26],
+ [
+ ".devtools-toolbarbutton > tab",
+ ".devtools-toolbarbutton > hbox",
+ ".devtools-toolbarbutton > .toolbarbutton-menubutton-button"
+ ]
+ ],
+ [
+ [25, 31],
+ [".devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button"]
+ ],
+ [
+ [29, 20],
+ [".devtools-menulist:after", ".devtools-menulist:active"]
+ ],
+ [
+ [30, 10],
+ [
+ "#devtools-anotherone",
+ "#devtools-itjustgoeson",
+ "#devtools-menu",
+ "#devtools-okstopitnow",
+ "#devtools-toolbarbutton",
+ "#devtools-yetagain"
+ ]
+ ],
+ [[39, 39], [".devtools-toolbarbutton:not([label]) > tab"]],
+ [
+ [43, 51],
+ [
+ ".devtools-toolbarbutton:not([checked=true]):hover:after",
+ ".devtools-toolbarbutton:not([checked=true]):hover:active"
+ ]
+ ],
+ [[58, 36], ["!important;"]],
+ [
+ [73, 42],
+ [":lang(", ":last-of-type", ":link", ":last-child"]
+ ],
+ [[77, 25], [".visible"]]
+ ]
+}
diff --git a/devtools/client/shared/sourceeditor/test/css_statemachine_testcases.css b/devtools/client/shared/sourceeditor/test/css_statemachine_testcases.css
new file mode 100644
index 0000000000..d2c51a8413
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/css_statemachine_testcases.css
@@ -0,0 +1,121 @@
+/* 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/. */
+
+.devtools-toolbar {
+ -moz-appearance: none;
+ padding:4px 3px;border-bottom-width: 1px;
+ border-bottom-style: solid;
+}
+
+#devtools-menu.devtools-menulist,
+.devtools-toolbarbutton#devtools-menu {
+ -moz-appearance: none;
+ align-items: center;
+ min-width: 78px;
+ min-height: 22px;
+ text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
+ border: 1px solid hsla(210,8%,5%,.45);
+ border-radius: 3px;
+ background: linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
+ box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
+ margin: 0 3px;
+ color: inherit;
+}
+
+.devtools-toolbarbutton > hbox.toolbarbutton-menubutton-button {
+ flex-direction: row;
+}
+
+.devtools-menulist:active,
+#devtools-toolbarbutton:focus {
+ outline: 1px dotted hsla(210,30%,85%,0.7);
+ outline-offset : -4px;
+}
+
+.devtools-toolbarbutton:not([label]) {
+ min-width: 32px;
+}
+
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
+ display: none;
+}
+
+.devtools-toolbarbutton:not([checked=true]):hover:active {
+ border-color: hsla(210,8%,5%,.6);
+}
+
+.devtools-menulist["open" ="true"],
+.devtools-toolbarbutton["open" = true],
+.devtools-toolbarbutton[checked= "true"] {
+ border-color: hsla(210,8%,5%,.6) !important;
+}
+
+.devtools-toolbarbutton["checked"="true"] {
+ color: hsl(208,100%,60%);
+}
+
+.devtools-toolbarbutton[checked=true]:hover {
+ background-color: transparent !important;
+}
+
+.devtools-toolbarbutton[checked=true]:hover:active {
+ background-color: hsla(210,8%,5%,.2) !important;
+}
+
+.devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
+ -moz-appearance: none;
+}
+
+.devtools-sidebar-tabs > tabs > tab:first-of-type {
+ margin-inline-start: -3px;
+}
+
+.devtools-sidebar-tabs > tabs > tab:not(:last-of-type) {
+ background-size: calc(100% - 2px) 100%, 1px 100%;
+}
+
+.hidden-labels-box:not(.visible) > label,
+.hidden-labels-box.visible ~ .hidden-labels-box > label:last-child {
+ display: none;
+}
+
+/* Maximize the size of the viewport when the window is small */
+@media (max-width: 800px) {
+ .category-name {
+ display: none;
+ }
+}
+
+@media all and (min-width: 300px) {
+ #error-box {
+ max-width: 50%;
+ margin: 0 auto;
+ background-image: url('chrome://global/skin/icons/information-32.png');
+ min-height: 36px;
+ padding-inline-start: 38px;
+ }
+
+ button {
+ width: auto !important;
+ min-width: 150px;
+ }
+
+ @keyframes downloadsIndicatorNotificationFinish {
+ from { opacity: 0; transform: scale(1); }
+ 20% {
+ opacity: .65;
+ animation-timing-function: ease-in;
+ } to { opacity: 0;
+ transform: scale(8); }
+ }
+}
+
+@keyframes smooth {
+ from { opacity: 0; transform: scale(1); }
+ 20% { opacity: .65; animation-timing-function: ease-in; }
+ to {
+ opacity : 0;
+ transform: scale(8);
+ }
+}
diff --git a/devtools/client/shared/sourceeditor/test/css_statemachine_tests.json b/devtools/client/shared/sourceeditor/test/css_statemachine_tests.json
new file mode 100644
index 0000000000..642c1107ae
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/css_statemachine_tests.json
@@ -0,0 +1,319 @@
+{
+ "description": [
+ "Test states to be tested for css state machine in css-autocompleter.js file.",
+ "Test cases are of the following format:",
+ "[",
+ " [",
+ " line, // The line location of the cursor",
+ " ch // The column locaiton of the cursor",
+ " ],",
+ " [",
+ " state, // one of CSS_STATES",
+ " selectorState, // one of SELECTOR_STATES",
+ " completing, // what is being completed",
+ " propertyName, // what property is being completed in case of value state",
+ " // or the current selector that is being completed",
+ " ]",
+ "]"
+ ],
+ "tests": [
+ [
+ [0, 10],
+ ["null", "", "", ""]
+ ],
+ [
+ [4, 3],
+ ["selector", "class", "de", ".de"]
+ ],
+ [
+ [5, 8],
+ ["property", "null", "-moz-a"]
+ ],
+ [
+ [5, 21],
+ ["value", "null", "no", "-moz-appearance"]
+ ],
+ [
+ [6, 18],
+ ["property", "null", "padding"]
+ ],
+ [
+ [6, 24],
+ ["value", "null", "3", "padding"]
+ ],
+ [
+ [6, 29],
+ ["property", "null", "bo"]
+ ],
+ [
+ [6, 50],
+ ["value", "null", "1p", "border-bottom-width"]
+ ],
+ [
+ [7, 24],
+ ["value", "null", "s", "border-bottom-style"]
+ ],
+ [
+ [9, 0],
+ ["null", "null", "", ""]
+ ],
+ [
+ [10, 6],
+ ["selector", "id", "devto", "#devto"]
+ ],
+ [
+ [10, 17],
+ ["selector", "class", "de", "#devtools-menu.de"]
+ ],
+ [
+ [11, 5],
+ ["selector", "class", "devt", ".devt"]
+ ],
+ [
+ [11, 30],
+ ["selector", "id", "devtoo", ".devtools-toolbarbutton#devtoo"]
+ ],
+ [
+ [12, 10],
+ ["property", "null", "-moz-app"]
+ ],
+ [
+ [16, 27],
+ ["value", "null", "hsl", "text-shadow"]
+ ],
+ [
+ [19, 24],
+ ["value", "null", "linear-gra", "background"]
+ ],
+ [
+ [19, 55],
+ ["value", "null", "hsl", "background"]
+ ],
+ [
+ [19, 79],
+ ["value", "null", "paddin", "background"]
+ ],
+ [
+ [20, 47],
+ ["value", "null", "ins", "box-shadow"]
+ ],
+ [
+ [22, 15],
+ ["value", "null", "inheri", "color"]
+ ],
+ [
+ [25, 26],
+ ["selector", "null", "", ".devtools-toolbarbutton > "]
+ ],
+ [
+ [25, 28],
+ ["selector", "tag", "hb", ".devtools-toolbarbutton > hb"]
+ ],
+ [
+ [25, 41],
+ [
+ "selector",
+ "class",
+ "toolbarbut",
+ ".devtools-toolbarbutton > hbox.toolbarbut"
+ ]
+ ],
+ [
+ [29, 21],
+ ["selector", "pseudo", "ac", ".devtools-menulist:ac"]
+ ],
+ [
+ [30, 27],
+ ["selector", "pseudo", "foc", "#devtools-toolbarbutton:foc"]
+ ],
+ [
+ [31, 18],
+ ["value", "null", "dot", "outline"]
+ ],
+ [
+ [32, 25],
+ ["value", "null", "-4p", "outline-offset"]
+ ],
+ [
+ [35, 26],
+ ["selector", "pseudo", "no", ".devtools-toolbarbutton:no"]
+ ],
+ [
+ [35, 28],
+ ["selector", "null", "not", ""]
+ ],
+ [
+ [35, 30],
+ ["selector", "attribute", "l", "[l"]
+ ],
+ [
+ [39, 46],
+ [
+ "selector",
+ "class",
+ "toolba",
+ ".devtools-toolbarbutton:not([label]) > .toolba"
+ ]
+ ],
+ [
+ [43, 39],
+ ["selector", "value", "tr", "[checked=tr"]
+ ],
+ [
+ [43, 47],
+ [
+ "selector",
+ "pseudo",
+ "hov",
+ ".devtools-toolbarbutton:not([checked=true]):hov"
+ ]
+ ],
+ [
+ [43, 53],
+ [
+ "selector",
+ "pseudo",
+ "act",
+ ".devtools-toolbarbutton:not([checked=true]):hover:act"
+ ]
+ ],
+ [
+ [47, 22],
+ ["selector", "attribute", "op", ".devtools-menulist[op"]
+ ],
+ [
+ [47, 33],
+ ["selector", "value", "tr", ".devtools-menulist[open =tr"]
+ ],
+ [
+ [48, 38],
+ ["selector", "value", "tr", ".devtools-toolbarbutton[open = tr"]
+ ],
+ [
+ [49, 40],
+ ["selector", "value", "true", ".devtools-toolbarbutton[checked= true"]
+ ],
+ [
+ [53, 34],
+ ["selector", "value", "=", ".devtools-toolbarbutton[checked="]
+ ],
+ [
+ [58, 38],
+ ["value", "null", "!impor", "background-color"]
+ ],
+ [
+ [61, 41],
+ ["selector", "pseudo", "hov", ".devtools-toolbarbutton[checked=true]:hov"]
+ ],
+ [
+ [65, 47],
+ [
+ "selector",
+ "class",
+ "to",
+ ".devtools-toolbarbutton[type=menu-button] > .to"
+ ]
+ ],
+ [
+ [69, 44],
+ [
+ "selector",
+ "pseudo",
+ "first-of",
+ ".devtools-sidebar-tabs > tabs > tab:first-of"
+ ]
+ ],
+ [
+ [73, 45],
+ ["selector", "pseudo", "last", ":last"]
+ ],
+ [
+ [77, 27],
+ ["selector", "class", "vis", ".vis"]
+ ],
+ [
+ [78, 34],
+ ["selector", "class", "hidd", ".hidden-labels-box.visible ~ .hidd"]
+ ],
+ [
+ [83, 5],
+ ["media", "null", "medi"]
+ ],
+ [
+ [83, 22],
+ ["media", "null", "800"]
+ ],
+ [
+ [84, 9],
+ ["selector", "class", "catego", ".catego"]
+ ],
+ [
+ [89, 9],
+ ["media", "null", "al"]
+ ],
+ [
+ [90, 6],
+ ["selector", "id", "err", "#err"]
+ ],
+ [
+ [93, 11],
+ ["property", "null", "backgro"]
+ ],
+ [
+ [98, 6],
+ ["selector", "tag", "butt", "butt"]
+ ],
+ [
+ [99, 22],
+ ["value", "null", "!impor", "width"]
+ ],
+ [
+ [103, 5],
+ ["keyframes", "null", "ke"]
+ ],
+ [
+ [104, 7],
+ ["frame", "null", "fro"]
+ ],
+ [
+ [104, 15],
+ ["property", "null", "opac"]
+ ],
+ [
+ [104, 29],
+ ["property", "null", "transf"]
+ ],
+ [
+ [104, 38],
+ ["value", "null", "scal", "transform"]
+ ],
+ [
+ [105, 8],
+ ["frame", "null", ""]
+ ],
+ [
+ [113, 6],
+ ["keyframes", "null", "keyfr"]
+ ],
+ [
+ [114, 4],
+ ["frame", "null", "fr"]
+ ],
+ [
+ [115, 3],
+ ["frame", "null", "2"]
+ ],
+ [
+ [117, 8],
+ ["property", "null", "opac"]
+ ],
+ [
+ [117, 16],
+ ["value", "null", "0", "opacity"]
+ ],
+ [
+ [121, 0],
+ ["null", "", ""]
+ ]
+ ]
+}
diff --git a/devtools/client/shared/sourceeditor/test/head.js b/devtools/client/shared/sourceeditor/test/head.js
new file mode 100644
index 0000000000..7d11fbe475
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/head.js
@@ -0,0 +1,195 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* exported promiseWaitForFocus, setup, ch, teardown, loadHelperScript,
+ limit, ch, read, codemirrorSetStatus */
+
+"use strict";
+
+// shared-head.js handles imports, constants, and utility functions
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+ this
+);
+
+const Editor = require("resource://devtools/client/shared/sourceeditor/editor.js");
+
+function promiseWaitForFocus(el) {
+ return new Promise(resolve => waitForFocus(resolve, el));
+}
+
+async function setup(additionalOpts = {}) {
+ try {
+ const opt = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+ const win = Services.ww.openWindow(
+ null,
+ CHROME_URL_ROOT + "head.xhtml",
+ "_blank",
+ opt,
+ null
+ );
+ const opts = {
+ value: "Hello.",
+ lineNumbers: true,
+ foldGutter: true,
+ gutters: [
+ "CodeMirror-linenumbers",
+ "breakpoints",
+ "CodeMirror-foldgutter",
+ ],
+ cssProperties: getClientCssProperties(),
+ ...additionalOpts,
+ };
+
+ await once(win, "load");
+ await promiseWaitForFocus(win);
+
+ const box = win.document.querySelector("box");
+ const editor = new Editor(opts);
+ await editor.appendTo(box);
+
+ return {
+ ed: editor,
+ win,
+ edWin: editor.container.contentWindow.wrappedJSObject,
+ };
+ } catch (o_O) {
+ ok(false, o_O.message);
+ return null;
+ }
+}
+
+function ch(exp, act, label) {
+ is(exp.line, act.line, label + " (line)");
+ is(exp.ch, act.ch, label + " (ch)");
+}
+
+function teardown(ed, win) {
+ ed.destroy();
+ win.close();
+
+ while (gBrowser.tabs.length > 1) {
+ gBrowser.removeCurrentTab();
+ }
+ finish();
+}
+
+/**
+ * Some tests may need to import one or more of the test helper scripts.
+ * A test helper script is simply a js file that contains common test code that
+ * is either not common-enough to be in head.js, or that is located in a
+ * separate directory.
+ * The script will be loaded synchronously and in the test's scope.
+ * @param {String} filePath The file path, relative to the current directory.
+ * Examples:
+ * - "helper_attributes_test_runner.js"
+ */
+function loadHelperScript(filePath) {
+ const testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+ Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
+}
+
+/**
+ * This method returns the portion of the input string `source` up to the
+ * [line, ch] location.
+ */
+function limit(source, [line, char]) {
+ line++;
+ const list = source.split("\n");
+ if (list.length < line) {
+ return source;
+ }
+ if (line == 1) {
+ return list[0].slice(0, char);
+ }
+ return [...list.slice(0, line - 1), list[line - 1].slice(0, char)].join("\n");
+}
+
+function read(url) {
+ const scriptableStream = Cc[
+ "@mozilla.org/scriptableinputstream;1"
+ ].getService(Ci.nsIScriptableInputStream);
+
+ const channel = NetUtil.newChannel({
+ uri: url,
+ loadUsingSystemPrincipal: true,
+ });
+ const input = channel.open();
+ scriptableStream.init(input);
+
+ let data = "";
+ while (input.available()) {
+ data = data.concat(scriptableStream.read(input.available()));
+ }
+ scriptableStream.close();
+ input.close();
+
+ return data;
+}
+
+/**
+ * This function is called by the CodeMirror test runner to report status
+ * messages from the CM tests.
+ * @see codemirror.html
+ */
+function codemirrorSetStatus(statusMsg, type, customMsg) {
+ switch (type) {
+ case "expected":
+ case "ok":
+ ok(1, statusMsg);
+ break;
+ case "error":
+ case "fail":
+ ok(0, statusMsg);
+ break;
+ default:
+ info(statusMsg);
+ break;
+ }
+
+ if (customMsg && typeof customMsg == "string" && customMsg != statusMsg) {
+ info(customMsg);
+ }
+}
+
+async function runCodeMirrorTest(uri) {
+ const actorURI =
+ "chrome://mochitests/content/browser/devtools/client/shared/sourceeditor/test/CodeMirrorTestActors.sys.mjs";
+
+ const { CodeMirrorTestParent } = ChromeUtils.importESModule(actorURI);
+
+ ChromeUtils.registerWindowActor("CodeMirrorTest", {
+ parent: {
+ esModuleURI: actorURI,
+ },
+ child: {
+ esModuleURI: actorURI,
+ events: {
+ DOMWindowCreated: {},
+ },
+ },
+ });
+
+ const donePromise = new Promise(resolve => {
+ CodeMirrorTestParent.setCallback((name, data) => {
+ switch (name) {
+ case "setStatus":
+ const { statusMsg, type, customMsg } = data;
+ codemirrorSetStatus(statusMsg, type, customMsg);
+ break;
+ case "done":
+ resolve(!data.failed);
+ break;
+ }
+ });
+ });
+
+ await addTab(uri);
+ const result = await donePromise;
+ ok(result, "CodeMirror tests all passed");
+ while (gBrowser.tabs.length > 1) {
+ gBrowser.removeCurrentTab();
+ }
+
+ ChromeUtils.unregisterWindowActor("CodeMirrorTest");
+}
diff --git a/devtools/client/shared/sourceeditor/test/head.xhtml b/devtools/client/shared/sourceeditor/test/head.xhtml
new file mode 100644
index 0000000000..4e0fe90812
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/test/head.xhtml
@@ -0,0 +1,5 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href='chrome://global/skin/global.css'?>
+<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' title='Editor' width='600' height='500'>
+<box flex='1'/>
+</window>
diff --git a/devtools/client/shared/sourceeditor/wasm.js b/devtools/client/shared/sourceeditor/wasm.js
new file mode 100644
index 0000000000..ed3ef4df63
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/wasm.js
@@ -0,0 +1,93 @@
+/* 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/. */
+
+"use strict";
+
+const wasmparser = require("resource://devtools/client/shared/vendor/WasmParser.js");
+const wasmdis = require("resource://devtools/client/shared/vendor/WasmDis.js");
+
+const wasmStates = new WeakMap();
+
+function getWasmText(subject, data) {
+ const parser = new wasmparser.BinaryReader();
+ parser.setData(data.buffer, 0, data.length);
+ const dis = new wasmdis.WasmDisassembler();
+ dis.addOffsets = true;
+ const done = dis.disassembleChunk(parser);
+ let result = dis.getResult();
+ if (result.lines.length === 0) {
+ result = { lines: ["No luck with wast conversion"], offsets: [0], done };
+ }
+
+ const offsets = result.offsets,
+ lines = [];
+ for (let i = 0; i < offsets.length; i++) {
+ lines[offsets[i]] = i;
+ }
+
+ wasmStates.set(subject, { offsets, lines });
+
+ return { lines: result.lines, done: result.done };
+}
+
+function getWasmLineNumberFormatter(subject) {
+ const codeOf0 = 48,
+ codeOfA = 65;
+ const buffer = [
+ codeOf0,
+ codeOf0,
+ codeOf0,
+ codeOf0,
+ codeOf0,
+ codeOf0,
+ codeOf0,
+ codeOf0,
+ ];
+ let last0 = 7;
+ return function (number) {
+ const offset = lineToWasmOffset(subject, number - 1);
+ if (offset === undefined) {
+ return "";
+ }
+ let i = 7;
+ for (let n = offset | 0; n !== 0 && i >= 0; n >>= 4, i--) {
+ const nibble = n & 15;
+ buffer[i] = nibble < 10 ? codeOf0 + nibble : codeOfA - 10 + nibble;
+ }
+ for (let j = i; j > last0; j--) {
+ buffer[j] = codeOf0;
+ }
+ last0 = i;
+ return String.fromCharCode.apply(null, buffer);
+ };
+}
+
+function isWasm(subject) {
+ return wasmStates.has(subject);
+}
+
+function lineToWasmOffset(subject, number) {
+ const wasmState = wasmStates.get(subject);
+ if (!wasmState) {
+ return undefined;
+ }
+ let offset = wasmState.offsets[number];
+ while (offset === undefined && number > 0) {
+ offset = wasmState.offsets[--number];
+ }
+ return offset;
+}
+
+function wasmOffsetToLine(subject, offset) {
+ const wasmState = wasmStates.get(subject);
+ return wasmState.lines[offset];
+}
+
+module.exports = {
+ getWasmText,
+ getWasmLineNumberFormatter,
+ isWasm,
+ lineToWasmOffset,
+ wasmOffsetToLine,
+};
diff --git a/devtools/client/shared/sourceeditor/webpack.config.js b/devtools/client/shared/sourceeditor/webpack.config.js
new file mode 100644
index 0000000000..4754dfff58
--- /dev/null
+++ b/devtools/client/shared/sourceeditor/webpack.config.js
@@ -0,0 +1,61 @@
+/* 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/. */
+
+"use strict";
+
+const path = require("path");
+
+module.exports = (env, argv) => {
+ return {
+ bail: true,
+ entry: [
+ "./codemirror/addon/dialog/dialog.js",
+ "./codemirror/addon/search/searchcursor.js",
+ "./codemirror/addon/search/search.js",
+ "./codemirror/addon/search/match-highlighter.js",
+ "./codemirror/addon/edit/matchbrackets.js",
+ "./codemirror/addon/edit/closebrackets.js",
+ "./codemirror/addon/comment/comment.js",
+ "./codemirror/addon/accessibleTextarea.js",
+ "./codemirror/mode/javascript/javascript.js",
+ "./codemirror/mode/xml/xml.js",
+ "./codemirror/mode/css/css.js",
+ "./codemirror/mode/clojure/clojure.js",
+ "./codemirror/mode/haxe/haxe.js",
+ "./codemirror/mode/http/http.js",
+ "./codemirror/mode/htmlmixed/htmlmixed.js",
+ "./codemirror/mode/jsx/jsx.js",
+ "./codemirror/mode/coffeescript/coffeescript.js",
+ "./codemirror/mode/elm/elm.js",
+ "./codemirror/mode/clike/clike.js",
+ "./codemirror/mode/rust/rust.js",
+ "./codemirror/mode/wasm/wasm.js",
+ "./codemirror/addon/selection/active-line.js",
+ "./codemirror/addon/edit/trailingspace.js",
+ "./codemirror/addon/fold/foldcode.js",
+ "./codemirror/addon/fold/brace-fold.js",
+ "./codemirror/addon/fold/comment-fold.js",
+ "./codemirror/addon/fold/xml-fold.js",
+ "./codemirror/addon/fold/foldgutter.js",
+ "./codemirror/addon/runmode/runmode.js",
+ "./codemirror/addon/display/placeholder.js",
+ "./codemirror/lib/codemirror.js",
+ ],
+ optimization: {
+ minimize: !(argv.optimization && argv.optimization.minimizer === "false"),
+ },
+ output: {
+ path: path.resolve(__dirname, "./codemirror/"),
+ filename: "codemirror.bundle.js",
+ libraryTarget: "var",
+ library: "CodeMirror",
+ },
+ resolve: {
+ alias: {
+ "resource://devtools": "devtools",
+ },
+ modules: [path.resolve(__dirname, "../../../..")],
+ },
+ };
+};
diff --git a/devtools/client/shared/string-utils.js b/devtools/client/shared/string-utils.js
new file mode 100644
index 0000000000..327d3588c6
--- /dev/null
+++ b/devtools/client/shared/string-utils.js
@@ -0,0 +1,40 @@
+/* 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/. */
+"use strict";
+
+const {
+ LongStringFront,
+} = require("resource://devtools/client/fronts/string.js");
+
+/**
+ * Fetches the full text of a LongString.
+ *
+ * @param {DevToolsClient} client
+ * @param {object|string} stringGrip: A long string grip. If the param is a simple string,
+ * it will be returned as is.
+ * @return {Promise<String>} The full string content.
+ */
+async function getLongStringFullText(client, stringGrip) {
+ if (typeof stringGrip !== "object" || stringGrip.type !== "longString") {
+ return stringGrip;
+ }
+
+ const { initial, length } = stringGrip;
+ const longStringFront = new LongStringFront(
+ client
+ // this.commands.targetCommand.targetFront
+ );
+ longStringFront.form(stringGrip);
+ // The front has to be managed to be able to call the actor method.
+ longStringFront.manage(longStringFront);
+
+ const response = await longStringFront.substring(initial.length, length);
+ const payload = initial + response;
+
+ longStringFront.destroy();
+
+ return payload;
+}
+
+exports.getLongStringFullText = getLongStringFullText;
diff --git a/devtools/client/shared/stylesheet-utils.js b/devtools/client/shared/stylesheet-utils.js
new file mode 100644
index 0000000000..5a034debe1
--- /dev/null
+++ b/devtools/client/shared/stylesheet-utils.js
@@ -0,0 +1,70 @@
+/* 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/. */
+
+/* eslint-env browser */
+"use strict";
+
+function stylesheetLoadPromise(styleSheet, url) {
+ return new Promise((resolve, reject) => {
+ styleSheet.addEventListener("load", resolve, { once: true });
+ styleSheet.addEventListener("error", reject, { once: true });
+ });
+}
+
+/*
+ * Put the DevTools theme stylesheet into the provided chrome document.
+ *
+ * @param {Document} doc
+ * The chrome document where the stylesheet should be appended.
+ * @param {String} url
+ * The url of the stylesheet to load.
+ * @return {Object}
+ * - styleSheet {XMLStylesheetProcessingInstruction} the instruction node created.
+ * - loadPromise {Promise} that will resolve/reject when the stylesheet finishes
+ * or fails to load.
+ */
+function appendStyleSheet(doc, url) {
+ const styleSheet = doc.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "link"
+ );
+ styleSheet.setAttribute("rel", "stylesheet");
+ styleSheet.setAttribute("href", url);
+ const loadPromise = stylesheetLoadPromise(styleSheet);
+
+ // In order to make overriding styles sane, we want the order of loaded
+ // sheets to be something like this:
+ // global.css
+ // *-theme.css (the file loaded here)
+ // document-specific-sheet.css
+ // See Bug 1582786 / https://phabricator.services.mozilla.com/D46530#inline-284756.
+
+ // If there is a global sheet then insert after it.
+ const globalSheet = doc.querySelector(
+ "link[href='chrome://global/skin/global.css']"
+ );
+ if (globalSheet) {
+ globalSheet.after(styleSheet);
+ return { styleSheet, loadPromise };
+ }
+
+ // Otherwise insert before any existing link.
+ const links = doc.querySelectorAll("link");
+ if (links.length) {
+ links[0].before(styleSheet);
+ return { styleSheet, loadPromise };
+ }
+
+ // Fall back to putting at the start of the head or in a non-HTML doc the
+ // start of the document.
+ if (doc.head) {
+ doc.head.prepend(styleSheet);
+ } else {
+ doc.documentElement.prepend(styleSheet);
+ }
+
+ return { styleSheet, loadPromise };
+}
+
+exports.appendStyleSheet = appendStyleSheet;
diff --git a/devtools/client/shared/suggestion-picker.js b/devtools/client/shared/suggestion-picker.js
new file mode 100644
index 0000000000..6ad2901030
--- /dev/null
+++ b/devtools/client/shared/suggestion-picker.js
@@ -0,0 +1,176 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Allows to find the lowest ranking index in an index
+ * of suggestions, by comparing it to another array of "most relevant" items
+ * which has been sorted by relevance.
+ *
+ * Example usage:
+ * let sortedBrowsers = ["firefox", "safari", "edge", "chrome"];
+ * let myBrowsers = ["brave", "chrome", "firefox"];
+ * let bestBrowserIndex = findMostRelevantIndex(myBrowsers, sortedBrowsers);
+ * // returns "2", the index of firefox in myBrowsers array
+ *
+ * @param {Array} items
+ * Array of items to compare against sortedItems.
+ * @param {Array} sortedItems
+ * Array of sorted items that suggestions are evaluated against. Array
+ * should be sorted by relevance, most relevant item first.
+ * @return {Number}
+ */
+function findMostRelevantIndex(items, sortedItems) {
+ if (!Array.isArray(items) || !Array.isArray(sortedItems)) {
+ throw new Error("Please provide valid items and sortedItems arrays.");
+ }
+
+ // If the items array is empty, no valid index can be found.
+ if (!items.length) {
+ return -1;
+ }
+
+ // Return 0 if no match was found in the suggestion list.
+ let bestIndex = 0;
+ let lowestIndex = Infinity;
+ items.forEach((item, i) => {
+ const index = sortedItems.indexOf(item);
+ if (index !== -1 && index <= lowestIndex) {
+ lowestIndex = index;
+ bestIndex = i;
+ }
+ });
+
+ return bestIndex;
+}
+
+/**
+ * Top 100 CSS property names sorted by relevance, most relevant first.
+ *
+ * List based on the one used by Chrome devtools :
+ * https://code.google.com/p/chromium/codesearch#chromium/src/third_party/
+ * WebKit/Source/devtools/front_end/sdk/CSSMetadata.js&q=CSSMetadata&
+ * sq=package:chromium&type=cs&l=676
+ *
+ * The data is a mix of https://www.chromestatus.com/metrics/css and usage
+ * metrics from popular sites collected via https://gist.github.com/NV/3751436
+ *
+ * @type {Array}
+ */
+const SORTED_CSS_PROPERTIES = [
+ "width",
+ "margin",
+ "height",
+ "padding",
+ "font-size",
+ "border",
+ "display",
+ "position",
+ "text-align",
+ "background",
+ "background-color",
+ "top",
+ "font-weight",
+ "color",
+ "overflow",
+ "font-family",
+ "margin-top",
+ "float",
+ "opacity",
+ "cursor",
+ "left",
+ "text-decoration",
+ "background-image",
+ "right",
+ "line-height",
+ "margin-left",
+ "visibility",
+ "margin-bottom",
+ "padding-top",
+ "z-index",
+ "margin-right",
+ "background-position",
+ "vertical-align",
+ "padding-left",
+ "background-repeat",
+ "border-bottom",
+ "padding-right",
+ "border-top",
+ "padding-bottom",
+ "clear",
+ "white-space",
+ "bottom",
+ "border-color",
+ "max-width",
+ "border-radius",
+ "border-right",
+ "outline",
+ "border-left",
+ "font-style",
+ "content",
+ "min-width",
+ "min-height",
+ "box-sizing",
+ "list-style",
+ "border-width",
+ "box-shadow",
+ "font",
+ "border-collapse",
+ "text-shadow",
+ "text-indent",
+ "border-style",
+ "max-height",
+ "text-overflow",
+ "background-size",
+ "text-transform",
+ "zoom",
+ "list-style-type",
+ "border-spacing",
+ "word-wrap",
+ "overflow-y",
+ "transition",
+ "border-top-color",
+ "border-bottom-color",
+ "border-top-right-radius",
+ "letter-spacing",
+ "border-top-left-radius",
+ "border-bottom-left-radius",
+ "border-bottom-right-radius",
+ "overflow-x",
+ "pointer-events",
+ "border-right-color",
+ "transform",
+ "border-top-width",
+ "border-bottom-width",
+ "border-right-width",
+ "direction",
+ "animation",
+ "border-left-color",
+ "clip",
+ "border-left-width",
+ "table-layout",
+ "src",
+ "resize",
+ "word-break",
+ "background-clip",
+ "transform-origin",
+ "font-variant",
+ "filter",
+ "quotes",
+ "word-spacing",
+];
+
+/**
+ * Helper to find the most relevant CSS property name in a provided array.
+ *
+ * @param items {Array}
+ * Array of CSS property names.
+ */
+function findMostRelevantCssPropertyIndex(items) {
+ return findMostRelevantIndex(items, SORTED_CSS_PROPERTIES);
+}
+
+exports.findMostRelevantIndex = findMostRelevantIndex;
+exports.findMostRelevantCssPropertyIndex = findMostRelevantCssPropertyIndex;
diff --git a/devtools/client/shared/telemetry.js b/devtools/client/shared/telemetry.js
new file mode 100644
index 0000000000..e88221908a
--- /dev/null
+++ b/devtools/client/shared/telemetry.js
@@ -0,0 +1,819 @@
+/* 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/. */
+
+/**
+ * This is the telemetry module to report metrics for tools.
+ *
+ * Comprehensive documentation is in docs/frontend/telemetry.md
+ */
+
+"use strict";
+
+const TelemetryStopwatch = require("TelemetryStopwatch");
+const {
+ getNthPathExcluding,
+} = require("resource://devtools/shared/platform/stack.js");
+const { TelemetryEnvironment } = ChromeUtils.importESModule(
+ "resource://gre/modules/TelemetryEnvironment.sys.mjs"
+);
+const WeakMapMap = require("resource://devtools/client/shared/WeakMapMap.js");
+
+const CATEGORY = "devtools.main";
+
+// Object to be shared among all instances.
+const PENDING_EVENT_PROPERTIES = new WeakMapMap();
+const PENDING_EVENTS = new WeakMapMap();
+
+/**
+ * Instantiate a new Telemetry helper class.
+ *
+ * @param {Object} options [optional]
+ * @param {Boolean} options.useSessionId [optional]
+ * If true, this instance will automatically generate a unique "sessionId"
+ * and use it to aggregate all records against this unique session.
+ * This helps aggregate all data coming from a single toolbox instance for ex.
+ */
+class Telemetry {
+ constructor({ useSessionId = false } = {}) {
+ // Note that native telemetry APIs expect a string
+ this.sessionId = String(
+ useSessionId ? parseInt(this.msSinceProcessStart(), 10) : -1
+ );
+
+ // Bind pretty much all functions so that callers do not need to.
+ this.msSystemNow = this.msSystemNow.bind(this);
+ this.getHistogramById = this.getHistogramById.bind(this);
+ this.getKeyedHistogramById = this.getKeyedHistogramById.bind(this);
+ this.scalarSet = this.scalarSet.bind(this);
+ this.scalarAdd = this.scalarAdd.bind(this);
+ this.keyedScalarAdd = this.keyedScalarAdd.bind(this);
+ this.keyedScalarSet = this.keyedScalarSet.bind(this);
+ this.recordEvent = this.recordEvent.bind(this);
+ this.setEventRecordingEnabled = this.setEventRecordingEnabled.bind(this);
+ this.preparePendingEvent = this.preparePendingEvent.bind(this);
+ this.addEventProperty = this.addEventProperty.bind(this);
+ this.addEventProperties = this.addEventProperties.bind(this);
+ this.toolOpened = this.toolOpened.bind(this);
+ this.toolClosed = this.toolClosed.bind(this);
+ }
+
+ get osNameAndVersion() {
+ const osInfo = TelemetryEnvironment.currentEnvironment.system.os;
+
+ if (!osInfo) {
+ return "Unknown OS";
+ }
+
+ let osVersion = `${osInfo.name} ${osInfo.version}`;
+
+ if (osInfo.windowsBuildNumber) {
+ osVersion += `.${osInfo.windowsBuildNumber}`;
+ }
+
+ return osVersion;
+ }
+
+ /**
+ * Time since the system wide epoch. This is not a monotonic timer but
+ * can be used across process boundaries.
+ */
+ msSystemNow() {
+ return Services.telemetry.msSystemNow();
+ }
+
+ /**
+ * The number of milliseconds since process start using monotonic
+ * timestamps (unaffected by system clock changes).
+ */
+ msSinceProcessStart() {
+ return Services.telemetry.msSinceProcessStart();
+ }
+
+ /**
+ * Starts a timer associated with a telemetry histogram. The timer can be
+ * directly associated with a histogram, or with a pair of a histogram and
+ * an object.
+ *
+ * @param {String} histogramId
+ * A string which must be a valid histogram name.
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {Object} [options.inSeconds=false]
+ * Record elapsed time for this histogram in seconds instead of
+ * milliseconds. Defaults to false.
+ * @returns {Boolean}
+ * True if the timer was successfully started, false otherwise. If a
+ * timer already exists, it can't be started again.
+ */
+ start(histogramId, obj, { inSeconds } = {}) {
+ if (TelemetryStopwatch.running(histogramId, obj)) {
+ return false;
+ }
+
+ return TelemetryStopwatch.start(histogramId, obj, { inSeconds });
+ }
+
+ /**
+ * Starts a timer associated with a keyed telemetry histogram. The timer can
+ * be directly associated with a histogram and its key. Similarly to
+ * TelemetryStopwatch.start the histogram and its key can be associated
+ * with an object. Each key may have multiple associated objects and each
+ * object can be associated with multiple keys.
+ *
+ * @param {String} histogramId
+ * A string which must be a valid histogram name.
+ * @param {String} key
+ * A string which must be a valid histgram key.
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {Object} [options.inSeconds=false]
+ * Record elapsed time for this histogram in seconds instead of
+ * milliseconds. Defaults to false.
+ *
+ * @returns {Boolean}
+ * True if the timer was successfully started, false otherwise. If a
+ * timer already exists, it can't be started again, and the existing
+ * one will be cleared in order to avoid measurements errors.
+ */
+ startKeyed(histogramId, key, obj, { inSeconds } = {}) {
+ return TelemetryStopwatch.startKeyed(histogramId, key, obj, { inSeconds });
+ }
+
+ /**
+ * Stops the timer associated with the given histogram (and object),
+ * calculates the time delta between start and finish, and adds the value
+ * to the histogram.
+ *
+ * @param {String} histogramId
+ * A string which must be a valid histogram name.
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {Boolean} canceledOkay
+ * Optional parameter which will suppress any warnings that normally
+ * fire when a stopwatch is finished after being canceled.
+ * Defaults to false.
+ *
+ * @returns {Boolean}
+ * True if the timer was succesfully stopped and the data was added
+ * to the histogram, False otherwise.
+ */
+ finish(histogramId, obj, canceledOkay) {
+ return TelemetryStopwatch.finish(histogramId, obj, canceledOkay);
+ }
+
+ /**
+ * Stops the timer associated with the given keyed histogram (and object),
+ * calculates the time delta between start and finish, and adds the value
+ * to the keyed histogram.
+ *
+ * @param {String} histogramId
+ * A string which must be a valid histogram name.
+ * @param {String} key
+ * A string which must be a valid histogram key.
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {Boolean} canceledOkay
+ * Optional parameter which will suppress any warnings that normally
+ * fire when a stopwatch is finished after being canceled.
+ * Defaults to false.
+ *
+ * @returns {Boolean}
+ * True if the timer was succesfully stopped and the data was added
+ * to the histogram, False otherwise.
+ */
+ finishKeyed(histogramId, key, obj, canceledOkay) {
+ return TelemetryStopwatch.finishKeyed(histogramId, key, obj, canceledOkay);
+ }
+
+ /**
+ * Log a value to a histogram.
+ *
+ * @param {String} histogramId
+ * Histogram in which the data is to be stored.
+ */
+ getHistogramById(histogramId) {
+ let histogram = null;
+
+ if (histogramId) {
+ try {
+ histogram = Services.telemetry.getHistogramById(histogramId);
+ } catch (e) {
+ dump(
+ `Warning: An attempt was made to write to the ${histogramId} ` +
+ `histogram, which is not defined in Histograms.json\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+ }
+
+ return (
+ histogram || {
+ add: () => {},
+ }
+ );
+ }
+
+ /**
+ * Get a keyed histogram.
+ *
+ * @param {String} histogramId
+ * Histogram in which the data is to be stored.
+ */
+ getKeyedHistogramById(histogramId) {
+ let histogram = null;
+
+ if (histogramId) {
+ try {
+ histogram = Services.telemetry.getKeyedHistogramById(histogramId);
+ } catch (e) {
+ dump(
+ `Warning: An attempt was made to write to the ${histogramId} ` +
+ `histogram, which is not defined in Histograms.json\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+ }
+ return (
+ histogram || {
+ add: () => {},
+ }
+ );
+ }
+
+ /**
+ * Log a value to a scalar.
+ *
+ * @param {String} scalarId
+ * Scalar in which the data is to be stored.
+ * @param value
+ * Value to store.
+ */
+ scalarSet(scalarId, value) {
+ if (!scalarId) {
+ return;
+ }
+
+ try {
+ if (isNaN(value) && typeof value !== "boolean") {
+ dump(
+ `Warning: An attempt was made to write a non-numeric and ` +
+ `non-boolean value ${value} to the ${scalarId} scalar. Only ` +
+ `numeric and boolean values are allowed.\n` +
+ `CALLER: ${getCaller()}`
+ );
+
+ return;
+ }
+ Services.telemetry.scalarSet(scalarId, value);
+ } catch (e) {
+ dump(
+ `Warning: An attempt was made to write to the ${scalarId} ` +
+ `scalar, which is not defined in Scalars.yaml\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+ }
+
+ /**
+ * Log a value to a count scalar.
+ *
+ * @param {String} scalarId
+ * Scalar in which the data is to be stored.
+ * @param value
+ * Value to store.
+ */
+ scalarAdd(scalarId, value) {
+ if (!scalarId) {
+ return;
+ }
+
+ try {
+ if (isNaN(value)) {
+ dump(
+ `Warning: An attempt was made to write a non-numeric value ` +
+ `${value} to the ${scalarId} scalar. Only numeric values are ` +
+ `allowed.\n` +
+ `CALLER: ${getCaller()}`
+ );
+
+ return;
+ }
+ Services.telemetry.scalarAdd(scalarId, value);
+ } catch (e) {
+ dump(
+ `Warning: An attempt was made to write to the ${scalarId} ` +
+ `scalar, which is not defined in Scalars.yaml\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+ }
+
+ /**
+ * Log a value to a keyed scalar.
+ *
+ * @param {String} scalarId
+ * Scalar in which the data is to be stored.
+ * @param {String} key
+ * The key within the scalar.
+ * @param value
+ * Value to store.
+ */
+ keyedScalarSet(scalarId, key, value) {
+ if (!scalarId) {
+ return;
+ }
+
+ try {
+ if (isNaN(value) && typeof value !== "boolean") {
+ dump(
+ `Warning: An attempt was made to write a non-numeric and ` +
+ `non-boolean value ${value} to the ${scalarId} scalar. Only ` +
+ `numeric and boolean values are allowed.\n` +
+ `CALLER: ${getCaller()}`
+ );
+
+ return;
+ }
+ Services.telemetry.keyedScalarSet(scalarId, key, value);
+ } catch (e) {
+ dump(
+ `Warning: An attempt was made to write to the ${scalarId} ` +
+ `scalar, which is not defined in Scalars.yaml\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+ }
+
+ /**
+ * Log a value to a keyed count scalar.
+ *
+ * @param {String} scalarId
+ * Scalar in which the data is to be stored.
+ * @param {String} key
+ * The key within the scalar.
+ * @param value
+ * Value to store.
+ */
+ keyedScalarAdd(scalarId, key, value) {
+ if (!scalarId) {
+ return;
+ }
+
+ try {
+ if (isNaN(value)) {
+ dump(
+ `Warning: An attempt was made to write a non-numeric value ` +
+ `${value} to the ${scalarId} scalar. Only numeric values are ` +
+ `allowed.\n` +
+ `CALLER: ${getCaller()}`
+ );
+
+ return;
+ }
+ Services.telemetry.keyedScalarAdd(scalarId, key, value);
+ } catch (e) {
+ dump(
+ `Warning: An attempt was made to write to the ${scalarId} ` +
+ `scalar, which is not defined in Scalars.yaml\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+ }
+
+ /**
+ * Event telemetry is disabled by default. Use this method to enable or
+ * disable it.
+ *
+ * @param {Boolean} enabled
+ * Enabled: true or false.
+ */
+ setEventRecordingEnabled(enabled) {
+ return Services.telemetry.setEventRecordingEnabled(CATEGORY, enabled);
+ }
+
+ /**
+ * Telemetry events often need to make use of a number of properties from
+ * completely different codepaths. To make this possible we create a
+ * "pending event" along with an array of property names that we need to wait
+ * for before sending the event.
+ *
+ * As each property is received via addEventProperty() we check if all
+ * properties have been received. Once they have all been received we send the
+ * telemetry event.
+ *
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {String} method
+ * The telemetry event method (describes the type of event that
+ * occurred e.g. "open")
+ * @param {String} object
+ * The telemetry event object name (the name of the object the event
+ * occurred on) e.g. "tools" or "setting"
+ * @param {String|null} value
+ * The telemetry event value (a user defined value, providing context
+ * for the event) e.g. "console"
+ * @param {Array} expected
+ * An array of the properties needed before sending the telemetry
+ * event e.g.
+ * [
+ * "host",
+ * "width"
+ * ]
+ */
+ preparePendingEvent(obj, method, object, value, expected = []) {
+ const sig = `${method},${object},${value}`;
+
+ if (expected.length === 0) {
+ throw new Error(
+ `preparePendingEvent() was called without any expected ` +
+ `properties.\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+
+ const data = {
+ extra: {},
+ expected: new Set(expected),
+ };
+
+ PENDING_EVENTS.set(obj, sig, data);
+
+ const props = PENDING_EVENT_PROPERTIES.get(obj, sig);
+ if (props) {
+ for (const [name, val] of Object.entries(props)) {
+ this.addEventProperty(obj, method, object, value, name, val);
+ }
+ PENDING_EVENT_PROPERTIES.delete(obj, sig);
+ }
+ }
+
+ /**
+ * Adds an expected property for either a current or future pending event.
+ * This means that if preparePendingEvent() is called before or after sending
+ * the event properties they will automatically added to the event.
+ *
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {String} method
+ * The telemetry event method (describes the type of event that
+ * occurred e.g. "open")
+ * @param {String} object
+ * The telemetry event object name (the name of the object the event
+ * occurred on) e.g. "tools" or "setting"
+ * @param {String|null} value
+ * The telemetry event value (a user defined value, providing context
+ * for the event) e.g. "console"
+ * @param {String} pendingPropName
+ * The pending property name
+ * @param {String} pendingPropValue
+ * The pending property value
+ */
+ addEventProperty(
+ obj,
+ method,
+ object,
+ value,
+ pendingPropName,
+ pendingPropValue
+ ) {
+ const sig = `${method},${object},${value}`;
+ const events = PENDING_EVENTS.get(obj, sig);
+
+ // If the pending event has not been created add the property to the pending
+ // list.
+ if (!events) {
+ const props = PENDING_EVENT_PROPERTIES.get(obj, sig);
+
+ if (props) {
+ props[pendingPropName] = pendingPropValue;
+ } else {
+ PENDING_EVENT_PROPERTIES.set(obj, sig, {
+ [pendingPropName]: pendingPropValue,
+ });
+ }
+ return;
+ }
+
+ const { expected, extra } = events;
+
+ if (expected.has(pendingPropName)) {
+ extra[pendingPropName] = pendingPropValue;
+
+ if (expected.size === Object.keys(extra).length) {
+ this._sendPendingEvent(obj, method, object, value);
+ }
+ } else {
+ // The property was not expected, warn and bail.
+ throw new Error(
+ `An attempt was made to add the unexpected property ` +
+ `"${pendingPropName}" to a telemetry event with the ` +
+ `signature "${sig}"\n` +
+ `CALLER: ${getCaller()}`
+ );
+ }
+ }
+
+ /**
+ * Adds expected properties for either a current or future pending event.
+ * This means that if preparePendingEvent() is called before or after sending
+ * the event properties they will automatically added to the event.
+ *
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {String} method
+ * The telemetry event method (describes the type of event that
+ * occurred e.g. "open")
+ * @param {String} object
+ * The telemetry event object name (the name of the object the event
+ * occurred on) e.g. "tools" or "setting"
+ * @param {String|null} value
+ * The telemetry event value (a user defined value, providing context
+ * for the event) e.g. "console"
+ * @param {String} pendingObject
+ * An object containing key, value pairs that should be added to the
+ * event as properties.
+ */
+ addEventProperties(obj, method, object, value, pendingObject) {
+ for (const [key, val] of Object.entries(pendingObject)) {
+ this.addEventProperty(obj, method, object, value, key, val);
+ }
+ }
+
+ /**
+ * A private method that is not to be used externally. This method is used to
+ * prepare a pending telemetry event for sending and then send it via
+ * recordEvent().
+ *
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ * @param {String} method
+ * The telemetry event method (describes the type of event that
+ * occurred e.g. "open")
+ * @param {String} object
+ * The telemetry event object name (the name of the object the event
+ * occurred on) e.g. "tools" or "setting"
+ * @param {String|null} value
+ * The telemetry event value (a user defined value, providing context
+ * for the event) e.g. "console"
+ */
+ _sendPendingEvent(obj, method, object, value) {
+ const sig = `${method},${object},${value}`;
+ const { extra } = PENDING_EVENTS.get(obj, sig);
+
+ PENDING_EVENTS.delete(obj, sig);
+ PENDING_EVENT_PROPERTIES.delete(obj, sig);
+ this.recordEvent(method, object, value, extra);
+ }
+
+ /**
+ * Send a telemetry event.
+ *
+ * @param {String} method
+ * The telemetry event method (describes the type of event that
+ * occurred e.g. "open")
+ * @param {String} object
+ * The telemetry event object name (the name of the object the event
+ * occurred on) e.g. "tools" or "setting"
+ * @param {String|null} [value]
+ * Optional telemetry event value (a user defined value, providing
+ * context for the event) e.g. "console"
+ * @param {Object} [extra]
+ * Optional telemetry event extra object containing the properties that
+ * will be sent with the event e.g.
+ * {
+ * host: "bottom",
+ * width: "1024"
+ * }
+ */
+ recordEvent(method, object, value = null, extra = null) {
+ // Only string values are allowed so cast all values to strings.
+ if (extra) {
+ for (let [name, val] of Object.entries(extra)) {
+ val = val + "";
+
+ if (val.length > 80) {
+ const sig = `${method},${object},${value}`;
+
+ dump(
+ `Warning: The property "${name}" was added to a telemetry ` +
+ `event with the signature ${sig} but it's value "${val}" is ` +
+ `longer than the maximum allowed length of 80 characters.\n` +
+ `The property value has been trimmed to 80 characters before ` +
+ `sending.\nCALLER: ${getCaller()}`
+ );
+
+ val = val.substring(0, 80);
+ }
+
+ extra[name] = val;
+ }
+ }
+ // Automatically flag the record with the session ID
+ // if the current Telemetry instance relates to a toolbox
+ // so that data can be aggregated per toolbox instance.
+ // Note that we also aggregate data per about:debugging instance.
+ if (!extra) {
+ extra = {};
+ }
+ extra.session_id = this.sessionId;
+
+ Services.telemetry.recordEvent(CATEGORY, method, object, value, extra);
+ }
+
+ /**
+ * Sends telemetry pings to indicate that a tool has been opened.
+ *
+ * @param {String} id
+ * The ID of the tool opened.
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ *
+ * NOTE: This method is designed for tools that send multiple probes on open,
+ * one of those probes being a counter and the other a timer. If you
+ * only have one probe you should be using another method.
+ */
+ toolOpened(id, obj) {
+ const charts = getChartsFromToolId(id);
+
+ if (!charts) {
+ return;
+ }
+
+ if (charts.useTimedEvent) {
+ this.preparePendingEvent(obj, "tool_timer", id, null, [
+ "os",
+ "time_open",
+ ]);
+ this.addEventProperty(
+ obj,
+ "tool_timer",
+ id,
+ null,
+ "time_open",
+ this.msSystemNow()
+ );
+ }
+ if (charts.timerHist) {
+ this.start(charts.timerHist, obj, { inSeconds: true });
+ }
+ if (charts.countHist) {
+ this.getHistogramById(charts.countHist).add(true);
+ }
+ if (charts.countScalar) {
+ this.scalarAdd(charts.countScalar, 1);
+ }
+ }
+
+ /**
+ * Sends telemetry pings to indicate that a tool has been closed.
+ *
+ * @param {String} id
+ * The ID of the tool opened.
+ * @param {Object} obj
+ * The telemetry event or ping is associated with this object, meaning
+ * that multiple events or pings for the same histogram may be run
+ * concurrently, as long as they are associated with different objects.
+ *
+ * NOTE: This method is designed for tools that send multiple probes on open,
+ * one of those probes being a counter and the other a timer. If you
+ * only have one probe you should be using another method.
+ */
+ toolClosed(id, obj) {
+ const charts = getChartsFromToolId(id);
+
+ if (!charts) {
+ return;
+ }
+
+ if (charts.useTimedEvent) {
+ const sig = `tool_timer,${id},null`;
+ const event = PENDING_EVENTS.get(obj, sig);
+ const time = this.msSystemNow() - event.extra.time_open;
+
+ this.addEventProperties(obj, "tool_timer", id, null, {
+ time_open: time,
+ os: this.osNameAndVersion,
+ });
+ }
+
+ if (charts.timerHist) {
+ this.finish(charts.timerHist, obj, false);
+ }
+ }
+}
+
+/**
+ * Returns the telemetry charts for a specific tool.
+ *
+ * @param {String} id
+ * The ID of the tool that has been opened.
+ *
+ */
+// eslint-disable-next-line complexity
+function getChartsFromToolId(id) {
+ if (!id) {
+ return null;
+ }
+
+ const lowerCaseId = id.toLowerCase();
+
+ let useTimedEvent = null;
+ let timerHist = null;
+ let countHist = null;
+ let countScalar = null;
+
+ id = id.toUpperCase();
+
+ if (id === "PERFORMANCE") {
+ id = "JSPROFILER";
+ }
+
+ switch (id) {
+ case "ABOUTDEBUGGING":
+ case "BROWSERCONSOLE":
+ case "DOM":
+ case "INSPECTOR":
+ case "JSBROWSERDEBUGGER":
+ case "JSDEBUGGER":
+ case "JSPROFILER":
+ case "MEMORY":
+ case "NETMONITOR":
+ case "OPTIONS":
+ case "RESPONSIVE":
+ case "STORAGE":
+ case "STYLEEDITOR":
+ case "TOOLBOX":
+ case "WEBCONSOLE":
+ timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
+ countHist = `DEVTOOLS_${id}_OPENED_COUNT`;
+ break;
+ case "ACCESSIBILITY":
+ case "APPLICATION":
+ timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
+ countScalar = `devtools.${lowerCaseId}.opened_count`;
+ break;
+ case "ACCESSIBILITY_PICKER":
+ timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
+ countScalar = `devtools.accessibility.picker_used_count`;
+ break;
+ case "CHANGESVIEW":
+ useTimedEvent = true;
+ timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
+ countScalar = `devtools.${lowerCaseId}.opened_count`;
+ break;
+ case "ANIMATIONINSPECTOR":
+ case "COMPATIBILITYVIEW":
+ case "COMPUTEDVIEW":
+ case "FONTINSPECTOR":
+ case "LAYOUTVIEW":
+ case "RULEVIEW":
+ useTimedEvent = true;
+ timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
+ countHist = `DEVTOOLS_${id}_OPENED_COUNT`;
+ break;
+ case "FLEXBOX_HIGHLIGHTER":
+ case "GRID_HIGHLIGHTER":
+ timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
+ break;
+ default:
+ timerHist = `DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS`;
+ countHist = `DEVTOOLS_CUSTOM_OPENED_COUNT`;
+ }
+
+ return {
+ useTimedEvent,
+ timerHist,
+ countHist,
+ countScalar,
+ };
+}
+
+/**
+ * Displays the first caller and calling line outside of this file in the
+ * event of an error. This is the line that made the call that produced the
+ * error.
+ */
+function getCaller() {
+ return getNthPathExcluding(0, "/telemetry.js");
+}
+
+module.exports = Telemetry;
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/ChromeUtils.js b/devtools/client/shared/test-helpers/jest-fixtures/ChromeUtils.js
new file mode 100644
index 0000000000..2fee8bc01c
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/ChromeUtils.js
@@ -0,0 +1,12 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {
+ import: () => ({}),
+ addProfilerMarker: () => {},
+ defineESModuleGetters: () => {},
+ importESModule: () => ({}),
+};
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/Services.js b/devtools/client/shared/test-helpers/jest-fixtures/Services.js
new file mode 100644
index 0000000000..85d0bce5e0
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/Services.js
@@ -0,0 +1,566 @@
+/* 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/. */
+
+"use strict";
+
+/* globals localStorage, window */
+
+// XXX: This file is a copy of the Services shim from devtools-services.
+// See https://github.com/firefox-devtools/devtools-core/blob/a9263b4c3f88ea42879a36cdc3ca8217b4a528ea/packages/devtools-services/index.js
+// Many Jest tests in the debugger rely on preferences, but can't use Services.
+// This fixture is probably doing too much and should be reduced to the minimum
+// needed to pass the tests.
+
+/* eslint-disable mozilla/valid-services */
+
+// Some constants from nsIPrefBranch.idl.
+const PREF_INVALID = 0;
+const PREF_STRING = 32;
+const PREF_INT = 64;
+const PREF_BOOL = 128;
+const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
+
+// We prefix all our local storage items with this.
+const PREFIX = "Services.prefs:";
+
+/**
+ * Create a new preference branch. This object conforms largely to
+ * nsIPrefBranch and nsIPrefService, though it only implements the
+ * subset needed by devtools. A preference branch can hold child
+ * preferences while also holding a preference value itself.
+ *
+ * @param {PrefBranch} parent the parent branch, or null for the root
+ * branch.
+ * @param {String} name the base name of this branch
+ * @param {String} fullName the fully-qualified name of this branch
+ */
+function PrefBranch(parent, name, fullName) {
+ this._parent = parent;
+ this._name = name;
+ this._fullName = fullName;
+ this._observers = {};
+ this._children = {};
+
+ // Properties used when this branch has a value as well.
+ this._defaultValue = null;
+ this._hasUserValue = false;
+ this._userValue = null;
+ this._type = PREF_INVALID;
+}
+
+PrefBranch.prototype = {
+ PREF_INVALID,
+ PREF_STRING,
+ PREF_INT,
+ PREF_BOOL,
+
+ /** @see nsIPrefBranch.root. */
+ get root() {
+ return this._fullName;
+ },
+
+ /** @see nsIPrefBranch.getPrefType. */
+ getPrefType(prefName) {
+ return this._findPref(prefName)._type;
+ },
+
+ /** @see nsIPrefBranch.getBoolPref. */
+ getBoolPref(prefName, defaultValue) {
+ try {
+ const thePref = this._findPref(prefName);
+ if (thePref._type !== PREF_BOOL) {
+ throw new Error(`${prefName} does not have bool type`);
+ }
+ return thePref._get();
+ } catch (e) {
+ if (typeof defaultValue !== "undefined") {
+ return defaultValue;
+ }
+ throw e;
+ }
+ },
+
+ /** @see nsIPrefBranch.setBoolPref. */
+ setBoolPref(prefName, value) {
+ if (typeof value !== "boolean") {
+ throw new Error("non-bool passed to setBoolPref");
+ }
+ const thePref = this._findOrCreatePref(prefName, value, true, value);
+ if (thePref._type !== PREF_BOOL) {
+ throw new Error(`${prefName} does not have bool type`);
+ }
+ thePref._set(value);
+ },
+
+ /** @see nsIPrefBranch.getCharPref. */
+ getCharPref(prefName, defaultValue) {
+ try {
+ const thePref = this._findPref(prefName);
+ if (thePref._type !== PREF_STRING) {
+ throw new Error(`${prefName} does not have string type`);
+ }
+ return thePref._get();
+ } catch (e) {
+ if (typeof defaultValue !== "undefined") {
+ return defaultValue;
+ }
+ throw e;
+ }
+ },
+
+ /** @see nsIPrefBranch.getStringPref. */
+ getStringPref() {
+ return this.getCharPref.apply(this, arguments);
+ },
+
+ /** @see nsIPrefBranch.setCharPref. */
+ setCharPref(prefName, value) {
+ if (typeof value !== "string") {
+ throw new Error("non-string passed to setCharPref");
+ }
+ const thePref = this._findOrCreatePref(prefName, value, true, value);
+ if (thePref._type !== PREF_STRING) {
+ throw new Error(`${prefName} does not have string type`);
+ }
+ thePref._set(value);
+ },
+
+ /** @see nsIPrefBranch.setStringPref. */
+ setStringPref() {
+ return this.setCharPref.apply(this, arguments);
+ },
+
+ /** @see nsIPrefBranch.getIntPref. */
+ getIntPref(prefName, defaultValue) {
+ try {
+ const thePref = this._findPref(prefName);
+ if (thePref._type !== PREF_INT) {
+ throw new Error(`${prefName} does not have int type`);
+ }
+ return thePref._get();
+ } catch (e) {
+ if (typeof defaultValue !== "undefined") {
+ return defaultValue;
+ }
+ throw e;
+ }
+ },
+
+ /** @see nsIPrefBranch.setIntPref. */
+ setIntPref(prefName, value) {
+ if (typeof value !== "number") {
+ throw new Error("non-number passed to setIntPref");
+ }
+ const thePref = this._findOrCreatePref(prefName, value, true, value);
+ if (thePref._type !== PREF_INT) {
+ throw new Error(`${prefName} does not have int type`);
+ }
+ thePref._set(value);
+ },
+
+ /** @see nsIPrefBranch.clearUserPref */
+ clearUserPref(prefName) {
+ const thePref = this._findPref(prefName);
+ thePref._clearUserValue();
+ },
+
+ /** @see nsIPrefBranch.prefHasUserValue */
+ prefHasUserValue(prefName) {
+ const thePref = this._findPref(prefName);
+ return thePref._hasUserValue;
+ },
+
+ /** @see nsIPrefBranch.addObserver */
+ addObserver(domain, observer, holdWeak) {
+ if (holdWeak) {
+ throw new Error("shim prefs only supports strong observers");
+ }
+
+ if (!(domain in this._observers)) {
+ this._observers[domain] = [];
+ }
+ this._observers[domain].push(observer);
+ },
+
+ /** @see nsIPrefBranch.removeObserver */
+ removeObserver(domain, observer) {
+ if (!(domain in this._observers)) {
+ return;
+ }
+ const index = this._observers[domain].indexOf(observer);
+ if (index >= 0) {
+ this._observers[domain].splice(index, 1);
+ }
+ },
+
+ /** @see nsIPrefService.savePrefFile */
+ savePrefFile(file) {
+ if (file) {
+ throw new Error("shim prefs only supports null file in savePrefFile");
+ }
+ // Nothing to do - this implementation always writes back.
+ },
+
+ /** @see nsIPrefService.getBranch */
+ getBranch(prefRoot) {
+ if (!prefRoot) {
+ return this;
+ }
+ if (prefRoot.endsWith(".")) {
+ prefRoot = prefRoot.slice(0, -1);
+ }
+ // This is a bit weird since it could erroneously return a pref,
+ // not a pref branch.
+ return this._findPref(prefRoot);
+ },
+
+ /**
+ * Return this preference's current value.
+ *
+ * @return {Any} The current value of this preference. This may
+ * return a string, a number, or a boolean depending on the
+ * preference's type.
+ */
+ _get() {
+ if (this._hasUserValue) {
+ return this._userValue;
+ }
+ return this._defaultValue;
+ },
+
+ /**
+ * Set the preference's value. The new value is assumed to be a
+ * user value. After setting the value, this function emits a
+ * change notification.
+ *
+ * @param {Any} value the new value
+ */
+ _set(value) {
+ if (!this._hasUserValue || value !== this._userValue) {
+ this._userValue = value;
+ this._hasUserValue = true;
+ this._saveAndNotify();
+ }
+ },
+
+ /**
+ * Set the default value for this preference, and emit a
+ * notification if this results in a visible change.
+ *
+ * @param {Any} value the new default value
+ */
+ _setDefault(value) {
+ if (this._defaultValue !== value) {
+ this._defaultValue = value;
+ if (!this._hasUserValue) {
+ this._saveAndNotify();
+ }
+ }
+ },
+
+ /**
+ * If this preference has a user value, clear it. If a change was
+ * made, emit a change notification.
+ */
+ _clearUserValue() {
+ if (this._hasUserValue) {
+ this._userValue = null;
+ this._hasUserValue = false;
+ this._saveAndNotify();
+ }
+ },
+
+ /**
+ * Helper function to write the preference's value to local storage
+ * and then emit a change notification.
+ */
+ _saveAndNotify() {
+ const store = {
+ type: this._type,
+ defaultValue: this._defaultValue,
+ hasUserValue: this._hasUserValue,
+ userValue: this._userValue,
+ };
+
+ localStorage.setItem(PREFIX + this._fullName, JSON.stringify(store));
+ this._parent._notify(this._name);
+ },
+
+ /**
+ * Change this preference's value without writing it back to local
+ * storage. This is used to handle changes to local storage that
+ * were made externally.
+ *
+ * @param {Number} type one of the PREF_* values
+ * @param {Any} userValue the user value to use if the pref does not exist
+ * @param {Any} defaultValue the default value to use if the pref
+ * does not exist
+ * @param {Boolean} hasUserValue if a new pref is created, whether
+ * the default value is also a user value
+ * @param {Object} store the new value of the preference. It should
+ * be of the form {type, defaultValue, hasUserValue, userValue};
+ * where |type| is one of the PREF_* type constants; |defaultValue|
+ * and |userValue| are the default and user values, respectively;
+ * and |hasUserValue| is a boolean indicating whether the user value
+ * is valid
+ */
+ _storageUpdated(type, userValue, hasUserValue, defaultValue) {
+ this._type = type;
+ this._defaultValue = defaultValue;
+ this._hasUserValue = hasUserValue;
+ this._userValue = userValue;
+ // There's no need to write this back to local storage, since it
+ // came from there; and this avoids infinite event loops.
+ this._parent._notify(this._name);
+ },
+
+ /**
+ * Helper function to find either a Preference or PrefBranch object
+ * given its name. If the name is not found, throws an exception.
+ *
+ * @param {String} prefName the fully-qualified preference name
+ * @return {Object} Either a Preference or PrefBranch object
+ */
+ _findPref(prefName) {
+ const branchNames = prefName.split(".");
+ let branch = this;
+
+ for (const branchName of branchNames) {
+ branch = branch._children[branchName];
+ if (!branch) {
+ // throw new Error(`could not find pref branch ${ prefName}`);
+ return false;
+ }
+ }
+
+ return branch;
+ },
+
+ /**
+ * Helper function to notify any observers when a preference has
+ * changed. This will also notify the parent branch for further
+ * reporting.
+ *
+ * @param {String} relativeName the name of the updated pref,
+ * relative to this branch
+ */
+ _notify(relativeName) {
+ for (const domain in this._observers) {
+ if (
+ relativeName === domain ||
+ domain === "" ||
+ (domain.endsWith(".") && relativeName.startsWith(domain))
+ ) {
+ // Allow mutation while walking.
+ const localList = this._observers[domain].slice();
+ for (const observer of localList) {
+ try {
+ if ("observe" in observer) {
+ observer.observe(
+ this,
+ NS_PREFBRANCH_PREFCHANGE_TOPIC_ID,
+ relativeName
+ );
+ } else {
+ // Function-style observer -- these aren't mentioned in
+ // the IDL, but they're accepted and devtools uses them.
+ observer(this, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, relativeName);
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ }
+ }
+ }
+
+ if (this._parent) {
+ this._parent._notify(`${this._name}.${relativeName}`);
+ }
+ },
+
+ /**
+ * Helper function to create a branch given an array of branch names
+ * representing the path of the new branch.
+ *
+ * @param {Array} branchList an array of strings, one per component
+ * of the branch to be created
+ * @return {PrefBranch} the new branch
+ */
+ _createBranch(branchList) {
+ let parent = this;
+ for (const branch of branchList) {
+ if (!parent._children[branch]) {
+ const isParentRoot = !parent._parent;
+ const branchName = (isParentRoot ? "" : `${parent.root}.`) + branch;
+ parent._children[branch] = new PrefBranch(parent, branch, branchName);
+ }
+ parent = parent._children[branch];
+ }
+ return parent;
+ },
+
+ /**
+ * Create a new preference. The new preference is assumed to be in
+ * local storage already, and the new value is taken from there.
+ *
+ * @param {String} keyName the full-qualified name of the preference.
+ * This is also the name of the key in local storage.
+ * @param {Any} userValue the user value to use if the pref does not exist
+ * @param {Boolean} hasUserValue if a new pref is created, whether
+ * the default value is also a user value
+ * @param {Any} defaultValue the default value to use if the pref
+ * does not exist
+ * @param {Boolean} init if true, then this call is initialization
+ * from local storage and should override the default prefs
+ */
+ _findOrCreatePref(
+ keyName,
+ userValue,
+ hasUserValue,
+ defaultValue,
+ init = false
+ ) {
+ const branch = this._createBranch(keyName.split("."));
+
+ if (hasUserValue && typeof userValue !== typeof defaultValue) {
+ throw new Error(`inconsistent values when creating ${keyName}`);
+ }
+
+ let type;
+ switch (typeof defaultValue) {
+ case "boolean":
+ type = PREF_BOOL;
+ break;
+ case "number":
+ type = PREF_INT;
+ break;
+ case "string":
+ type = PREF_STRING;
+ break;
+ default:
+ throw new Error(`unhandled argument type: ${typeof defaultValue}`);
+ }
+
+ if (init || branch._type === PREF_INVALID) {
+ branch._storageUpdated(type, userValue, hasUserValue, defaultValue);
+ } else if (branch._type !== type) {
+ throw new Error(`attempt to change type of pref ${keyName}`);
+ }
+
+ return branch;
+ },
+
+ getKeyName(keyName) {
+ if (keyName.startsWith(PREFIX)) {
+ return keyName.slice(PREFIX.length);
+ }
+
+ return keyName;
+ },
+
+ /**
+ * Helper function that is called when local storage changes. This
+ * updates the preferences and notifies pref observers as needed.
+ *
+ * @param {StorageEvent} event the event representing the local
+ * storage change
+ */
+ _onStorageChange(event) {
+ if (event.storageArea !== localStorage) {
+ return;
+ }
+
+ const key = this.getKeyName(event.key);
+
+ // Ignore delete events. Not clear what's correct.
+ if (key === null || event.newValue === null) {
+ return;
+ }
+
+ const { type, userValue, hasUserValue, defaultValue } = JSON.parse(
+ event.newValue
+ );
+ if (event.oldValue === null) {
+ this._findOrCreatePref(key, userValue, hasUserValue, defaultValue);
+ } else {
+ const thePref = this._findPref(key);
+ thePref._storageUpdated(type, userValue, hasUserValue, defaultValue);
+ }
+ },
+
+ /**
+ * Helper function to initialize the root PrefBranch.
+ */
+ _initializeRoot() {
+ if (Services._defaultPrefsEnabled) {
+ /* eslint-disable no-eval */
+ // let devtools = require("raw!prefs!devtools/client/preferences/devtools");
+ // eval(devtools);
+ // let all = require("raw!prefs!modules/libpref/init/all");
+ // eval(all);
+ /* eslint-enable no-eval */
+ }
+
+ // Read the prefs from local storage and create the local
+ // representations.
+ for (let i = 0; i < localStorage.length; ++i) {
+ const keyName = localStorage.key(i);
+ if (keyName.startsWith(PREFIX)) {
+ const { userValue, hasUserValue, defaultValue } = JSON.parse(
+ localStorage.getItem(keyName)
+ );
+ this._findOrCreatePref(
+ keyName.slice(PREFIX.length),
+ userValue,
+ hasUserValue,
+ defaultValue,
+ true
+ );
+ }
+ }
+
+ this._onStorageChange = this._onStorageChange.bind(this);
+ window.addEventListener("storage", this._onStorageChange);
+ },
+};
+
+const Services = {
+ _prefs: null,
+
+ _defaultPrefsEnabled: true,
+
+ get prefs() {
+ if (!this._prefs) {
+ this._prefs = new PrefBranch(null, "", "");
+ this._prefs._initializeRoot();
+ }
+ return this._prefs;
+ },
+
+ appinfo: "",
+ obs: { addObserver: () => {} },
+ strings: {
+ createBundle(bundle) {
+ return {
+ GetStringFromName(str) {
+ return "NodeTest";
+ },
+ };
+ },
+ },
+ intl: {
+ stringHasRTLChars: () => false,
+ },
+};
+
+function pref(name, value) {
+ // eslint-disable-next-line mozilla/valid-services-property
+ const thePref = Services.prefs._findOrCreatePref(name, value, true, value);
+ thePref._setDefault(value);
+}
+
+module.exports = Services;
+Services.pref = pref;
+Services.uuid = { generateUUID: () => {} };
+Services.dns = {};
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/devtools-utils.js b/devtools/client/shared/test-helpers/jest-fixtures/devtools-utils.js
new file mode 100644
index 0000000000..baa0647728
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/devtools-utils.js
@@ -0,0 +1,13 @@
+/* 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/>. */
+
+"use strict";
+
+module.exports = {
+ getTopWindow(win) {
+ return win.top;
+ },
+ defineLazyGetter() {},
+ makeInfallible: fn => fn,
+};
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/empty-module.js b/devtools/client/shared/test-helpers/jest-fixtures/empty-module.js
new file mode 100644
index 0000000000..67bbdf35ca
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/empty-module.js
@@ -0,0 +1,7 @@
+/* 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/. */
+
+"use strict";
+
+module.exports = {};
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/fluent-l10n.js b/devtools/client/shared/test-helpers/jest-fixtures/fluent-l10n.js
new file mode 100644
index 0000000000..186ca00342
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/fluent-l10n.js
@@ -0,0 +1,23 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Mock for devtools/client/shared/modules/fluent-l10n/fluent-l10n
+ */
+class FluentL10n {
+ async init() {}
+
+ getBundles() {
+ return [];
+ }
+
+ getString(id, args) {
+ return args ? `${id}__${JSON.stringify(args)}` : id;
+ }
+}
+
+// Export the class
+exports.FluentL10n = FluentL10n;
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/generate-uuid.js b/devtools/client/shared/test-helpers/jest-fixtures/generate-uuid.js
new file mode 100644
index 0000000000..3f53c7e0de
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/generate-uuid.js
@@ -0,0 +1,11 @@
+/* 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/. */
+
+"use strict";
+
+function generateUUID() {
+ return `${Date.now()}-${Math.round(Math.random() * 100)}`;
+}
+
+module.exports = { generateUUID };
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/indexed-db.js b/devtools/client/shared/test-helpers/jest-fixtures/indexed-db.js
new file mode 100644
index 0000000000..32bb957a65
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/indexed-db.js
@@ -0,0 +1,15 @@
+/* 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/>. */
+
+"use strict";
+
+const store = {};
+
+module.exports = {
+ open: () => ({}),
+ getItem: async key => store[key],
+ setItem: async (key, value) => {
+ store[key] = value;
+ },
+};
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/plural-form.js b/devtools/client/shared/test-helpers/jest-fixtures/plural-form.js
new file mode 100644
index 0000000000..c6a3f6cb13
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/plural-form.js
@@ -0,0 +1,11 @@
+/* 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/>. */
+
+"use strict";
+
+module.exports.PluralForm = {
+ get(num, str) {
+ return str.split(";")[1];
+ },
+};
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/promise.js b/devtools/client/shared/test-helpers/jest-fixtures/promise.js
new file mode 100644
index 0000000000..ad42cbd4ec
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/promise.js
@@ -0,0 +1,7 @@
+/* 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/>. */
+
+"use strict";
+
+module.exports = Promise;
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/svgMock.js b/devtools/client/shared/test-helpers/jest-fixtures/svgMock.js
new file mode 100644
index 0000000000..2c2eeed9f4
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/svgMock.js
@@ -0,0 +1,7 @@
+/* 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/>. */
+
+"use strict";
+
+module.exports = "<svg></svg>";
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/telemetry.js b/devtools/client/shared/test-helpers/jest-fixtures/telemetry.js
new file mode 100644
index 0000000000..45796146bf
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/telemetry.js
@@ -0,0 +1,13 @@
+/* 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/>. */
+
+"use strict";
+
+class Telemetry {
+ recordEvent() {}
+ start() {}
+ finish() {}
+ getKeyedHistogramById = () => ({ add: () => {} });
+}
+module.exports = Telemetry;
diff --git a/devtools/client/shared/test-helpers/jest-fixtures/unicode-url.js b/devtools/client/shared/test-helpers/jest-fixtures/unicode-url.js
new file mode 100644
index 0000000000..a000e81cf6
--- /dev/null
+++ b/devtools/client/shared/test-helpers/jest-fixtures/unicode-url.js
@@ -0,0 +1,23 @@
+/* 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/>. */
+
+"use strict";
+
+function getUnicodeHostname(hostname) {
+ return hostname;
+}
+
+function getUnicodeUrlPath(urlPath) {
+ return decodeURIComponent(urlPath);
+}
+
+function getUnicodeUrl(url) {
+ return decodeURIComponent(url);
+}
+
+module.exports = {
+ getUnicodeHostname,
+ getUnicodeUrlPath,
+ getUnicodeUrl,
+};
diff --git a/devtools/client/shared/test-helpers/shared-jest.config.js b/devtools/client/shared/test-helpers/shared-jest.config.js
new file mode 100644
index 0000000000..9f72fbd9fd
--- /dev/null
+++ b/devtools/client/shared/test-helpers/shared-jest.config.js
@@ -0,0 +1,42 @@
+/* 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/. */
+
+"use strict";
+
+const fixturesDir = `${__dirname}/jest-fixtures`;
+
+module.exports = {
+ verbose: true,
+ moduleNameMapper: {
+ // Custom name mappers for modules that require m-c specific API.
+ "^devtools/shared/generate-uuid": `${fixturesDir}/generate-uuid`,
+ "^devtools/shared/DevToolsUtils": `${fixturesDir}/devtools-utils`,
+ // This is needed for the Debugger, for some reason
+ "shared/DevToolsUtils": `${fixturesDir}/devtools-utils`,
+
+ // Mocks only used by node tests.
+ "Services-mock": `${fixturesDir}/Services`,
+ "ChromeUtils-mock": `${fixturesDir}/ChromeUtils`,
+
+ "^promise": `${fixturesDir}/promise`,
+ "^resource://devtools/client/shared/fluent-l10n/fluent-l10n.js": `${fixturesDir}/fluent-l10n`,
+ "^resource://devtools/client/shared/unicode-url.js": `${fixturesDir}/unicode-url`,
+ // This is needed for the Debugger, for some reason
+ "shared/unicode-url.js": `${fixturesDir}/unicode-url`,
+ "shared/telemetry.js": `${fixturesDir}/telemetry`,
+ "^resource://devtools/client/shared/telemetry.js": `${fixturesDir}/telemetry`,
+ // This is needed for the Debugger, for some reason
+ "client/shared/telemetry$": `${fixturesDir}/telemetry`,
+ "devtools/shared/plural-form$": `${fixturesDir}/plural-form`,
+ // Sometimes returning an empty object is enough
+ "^resource://devtools/client/shared/link": `${fixturesDir}/empty-module`,
+ "^devtools/shared/flags": `${fixturesDir}/empty-module`,
+ "^resource://devtools/shared/indexed-db.js": `${fixturesDir}/indexed-db`,
+ "^devtools/shared/layout/utils": `${fixturesDir}/empty-module`,
+ "^devtools/client/shared/components/tree/TreeView": `${fixturesDir}/empty-module`,
+ // Map all require("devtools/...") to the real devtools root.
+ "^devtools/(.*)": `${__dirname}/../../../$1`,
+ "^resource://devtools/(.*)": `${__dirname}/../../../$1`,
+ },
+};
diff --git a/devtools/client/shared/test-helpers/shared-node-helpers.js b/devtools/client/shared/test-helpers/shared-node-helpers.js
new file mode 100644
index 0000000000..ca6a728a8a
--- /dev/null
+++ b/devtools/client/shared/test-helpers/shared-node-helpers.js
@@ -0,0 +1,142 @@
+/* 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/. */
+
+"use strict";
+
+/* global global */
+
+/**
+ * Adds mocks for browser-environment global variables/methods to Node global.
+ */
+function setMocksInGlobal() {
+ global.Cc = new Proxy(
+ {},
+ {
+ get(target, prop, receiver) {
+ if (prop.startsWith("@mozilla.org")) {
+ return { getService: () => ({}) };
+ }
+ return null;
+ },
+ }
+ );
+ global.Ci = {
+ // sw states from
+ // mozilla-central/source/dom/interfaces/base/nsIServiceWorkerManager.idl
+ nsIServiceWorkerInfo: {
+ STATE_PARSED: 0,
+ STATE_INSTALLING: 1,
+ STATE_INSTALLED: 2,
+ STATE_ACTIVATING: 3,
+ STATE_ACTIVATED: 4,
+ STATE_REDUNDANT: 5,
+ STATE_UNKNOWN: 6,
+ },
+ };
+ global.Cu = {
+ isInAutomation: true,
+ now: () => {},
+ };
+
+ global.Services = require("Services-mock");
+ global.ChromeUtils = require("ChromeUtils-mock");
+
+ global.isWorker = false;
+
+ global.loader = {
+ lazyGetter: (context, name, fn) => {
+ Object.defineProperty(global, name, {
+ get() {
+ delete global[name];
+ global[name] = fn.apply(global);
+ return global[name];
+ },
+ configurable: true,
+ enumerable: true,
+ });
+ },
+ lazyRequireGetter: (context, names, module, destructure) => {
+ if (!Array.isArray(names)) {
+ names = [names];
+ }
+
+ for (const name of names) {
+ global.loader.lazyGetter(context, name, () => {
+ return destructure ? require(module)[name] : require(module || name);
+ });
+ }
+ },
+ lazyServiceGetter: () => {},
+ };
+
+ global.define = function () {};
+
+ // Used for the HTMLTooltip component.
+ // And set "isSystemPrincipal: false" because can't support XUL element in node.
+ global.document.nodePrincipal = {
+ isSystemPrincipal: false,
+ };
+
+ global.requestIdleCallback = function () {};
+
+ global.requestAnimationFrame = function (cb) {
+ cb();
+ return null;
+ };
+
+ // Mock getSelection
+ let selection;
+ global.getSelection = function () {
+ return {
+ toString: () => selection,
+ get type() {
+ if (selection === undefined) {
+ return "None";
+ }
+ if (selection === "") {
+ return "Caret";
+ }
+ return "Range";
+ },
+ setMockSelection: str => {
+ selection = str;
+ },
+ };
+ };
+
+ // Array#flatMap is only supported in Node 11+
+ if (!Array.prototype.flatMap) {
+ // eslint-disable-next-line no-extend-native
+ Array.prototype.flatMap = function (cb) {
+ return this.reduce((acc, x, i, arr) => {
+ return acc.concat(cb(x, i, arr));
+ }, []);
+ };
+ }
+
+ if (typeof global.TextEncoder === "undefined") {
+ const { TextEncoder } = require("util");
+ global.TextEncoder = TextEncoder;
+ }
+
+ if (typeof global.TextDecoder === "undefined") {
+ const { TextDecoder } = require("util");
+ global.TextDecoder = TextDecoder;
+ }
+
+ if (!Promise.withResolvers) {
+ Promise.withResolvers = function () {
+ let resolve, reject;
+ const promise = new Promise(function (res, rej) {
+ resolve = res;
+ reject = rej;
+ });
+ return { resolve, reject, promise };
+ };
+ }
+}
+
+module.exports = {
+ setMocksInGlobal,
+};
diff --git a/devtools/client/shared/test/addons/test-addon-1/manifest.json b/devtools/client/shared/test/addons/test-addon-1/manifest.json
new file mode 100644
index 0000000000..2d6ec55940
--- /dev/null
+++ b/devtools/client/shared/test/addons/test-addon-1/manifest.json
@@ -0,0 +1,10 @@
+{
+ "manifest_version": 2,
+ "name": "test-addon-1",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "test-addon-1@mozilla.org"
+ }
+ }
+}
diff --git a/devtools/client/shared/test/addons/test-addon-2/manifest.json b/devtools/client/shared/test/addons/test-addon-2/manifest.json
new file mode 100644
index 0000000000..7d5e2d54ae
--- /dev/null
+++ b/devtools/client/shared/test/addons/test-addon-2/manifest.json
@@ -0,0 +1,10 @@
+{
+ "manifest_version": 2,
+ "name": "test-addon-2",
+ "version": "1.0",
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "test-addon-2@mozilla.org"
+ }
+ }
+}
diff --git a/devtools/client/shared/test/browser.toml b/devtools/client/shared/test/browser.toml
new file mode 100644
index 0000000000..4c55c7fee9
--- /dev/null
+++ b/devtools/client/shared/test/browser.toml
@@ -0,0 +1,323 @@
+[DEFAULT]
+tags = "devtools"
+subsuite = "devtools"
+support-files = [
+ "addons/*",
+ "code_listworkers-worker1.js",
+ "code_listworkers-worker2.js",
+ "code_WorkerTargetActor.attachThread-worker.js",
+ "doc_cubic-bezier-01.html",
+ "doc_cubic-bezier-02.html",
+ "doc_empty-tab-01.html",
+ "doc_empty-tab-02.html",
+ "doc_filter-editor-01.html",
+ "doc_html_tooltip-02.xhtml",
+ "doc_html_tooltip-03.xhtml",
+ "doc_html_tooltip-04.xhtml",
+ "doc_html_tooltip-05.xhtml",
+ "doc_html_tooltip.xhtml",
+ "doc_html_tooltip_arrow-01.xhtml",
+ "doc_html_tooltip_arrow-02.xhtml",
+ "doc_html_tooltip_doorhanger-01.xhtml",
+ "doc_html_tooltip_doorhanger-02.xhtml",
+ "doc_html_tooltip_hover.xhtml",
+ "doc_html_tooltip_rtl.xhtml",
+ "doc_inplace-editor_autocomplete_offset.xhtml",
+ "doc_layoutHelpers_getBoxQuads1.html",
+ "doc_layoutHelpers_getBoxQuads2-a.html",
+ "doc_layoutHelpers_getBoxQuads2-b-and-d.html",
+ "doc_layoutHelpers_getBoxQuads2-c-and-e.html",
+ "doc_layoutHelpers.html",
+ "doc_listworkers-tab.html",
+ "doc_native-event-handler.html",
+ "doc_script-switching-01.html",
+ "doc_script-switching-02.html",
+ "doc_spectrum.html",
+ "doc_tableWidget_basic.html",
+ "doc_tableWidget_keyboard_interaction.xhtml",
+ "doc_tableWidget_mouse_interaction.xhtml",
+ "doc_templater_basic.html",
+ "doc_WorkerTargetActor.attachThread-tab.html",
+ "dummy.html",
+ "head.js",
+ "helper_color_data.js",
+ "helper_html_tooltip.js",
+ "helper_inplace_editor.js",
+ "highlighter-test-actor.js",
+ "leakhunt.js",
+ "shared-head.js",
+ "telemetry-test-helpers.js",
+ "test-mocked-module.js",
+ "testactors.js",
+ "!/devtools/client/debugger/test/mochitest/shared-head.js",
+ "!/gfx/layers/apz/test/mochitest/apz_test_utils.js",
+]
+
+["browser_autocomplete_popup.js"]
+
+["browser_autocomplete_popup_consecutive-show.js"]
+
+["browser_autocomplete_popup_input.js"]
+
+["browser_browserloader_mocks.js"]
+
+["browser_css_angle.js"]
+
+["browser_css_color.js"]
+
+["browser_cubic-bezier-01.js"]
+
+["browser_cubic-bezier-02.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_cubic-bezier-03.js"]
+
+["browser_cubic-bezier-04.js"]
+
+["browser_cubic-bezier-05.js"]
+
+["browser_cubic-bezier-06.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_cubic-bezier-07.js"]
+tags = "addons"
+
+["browser_dbg_listaddons.js"]
+skip-if = ["debug"]
+tags = "addons"
+
+["browser_dbg_listtabs-01.js"]
+
+["browser_dbg_listtabs-02.js"]
+skip-if = ["true"] # Never worked for remote frames, needs a mock DevToolsServerConnection
+
+["browser_dbg_listworkers.js"]
+
+["browser_dbg_multiple-windows.js"]
+
+["browser_dbg_target-scoped-actor-01.js"]
+
+["browser_dbg_target-scoped-actor-02.js"]
+
+["browser_devices.js"]
+skip-if = ["verify"]
+
+["browser_filter-editor-01.js"]
+
+["browser_filter-editor-02.js"]
+
+["browser_filter-editor-03.js"]
+
+["browser_filter-editor-04.js"]
+
+["browser_filter-editor-05.js"]
+
+["browser_filter-editor-06.js"]
+
+["browser_filter-editor-07.js"]
+fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
+
+["browser_filter-editor-08.js"]
+
+["browser_filter-editor-09.js"]
+
+["browser_filter-editor-10.js"]
+
+["browser_filter-presets-01.js"]
+
+["browser_filter-presets-02.js"]
+
+["browser_filter-presets-03.js"]
+
+["browser_html_tooltip-01.js"]
+
+["browser_html_tooltip-02.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+ "a11y_checks && debug", # Bugs 1849028 and 1858041 for causing intermittent test results
+]
+
+["browser_html_tooltip-03.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_html_tooltip-04.js"]
+skip-if = ["os == 'linux' && !asan && !debug && !swgl && !ccov"] # Bug 1721159
+
+["browser_html_tooltip-05.js"]
+
+["browser_html_tooltip_arrow-01.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_html_tooltip_arrow-02.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_html_tooltip_consecutive-show.js"]
+
+["browser_html_tooltip_doorhanger-01.js"]
+
+["browser_html_tooltip_doorhanger-02.js"]
+
+["browser_html_tooltip_height-auto.js"]
+
+["browser_html_tooltip_hover.js"]
+
+["browser_html_tooltip_offset.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_html_tooltip_resize.js"]
+
+["browser_html_tooltip_rtl.js"]
+
+["browser_html_tooltip_screen_edge.js"]
+
+["browser_html_tooltip_variable-height.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_html_tooltip_width-auto.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_html_tooltip_xul-wrapper.js"]
+
+["browser_html_tooltip_zoom.js"]
+
+["browser_inplace-editor-01.js"]
+
+["browser_inplace-editor-02.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_inplace-editor_autoclose_parentheses.js"]
+
+["browser_inplace-editor_autocomplete_01.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_inplace-editor_autocomplete_02.js"]
+
+["browser_inplace-editor_autocomplete_css_variable.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_inplace-editor_autocomplete_offset.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_inplace-editor_focus_closest_editor.js"]
+
+["browser_inplace-editor_maxwidth.js"]
+skip-if = [
+ "apple_catalina", # Bug 1713158
+ "os == 'linux' && !asan && !debug && !swgl && !ccov", # Bug 1721159
+]
+
+["browser_inplace-editor_stop_on_key.js"]
+
+["browser_key_shortcuts.js"]
+
+["browser_keycodes.js"]
+
+["browser_layoutHelpers.js"]
+
+["browser_layoutHelpers_getBoxQuads1.js"]
+skip-if = [
+ "verify",
+]
+
+["browser_layoutHelpers_getBoxQuads2.js"]
+skip-if = [
+ "http3", # Bug 1829298
+ "http2",
+]
+
+["browser_link.js"]
+
+["browser_num-l10n.js"]
+
+["browser_outputparser.js"]
+
+["browser_prefs-01.js"]
+
+["browser_prefs-02.js"]
+
+["browser_require_raw.js"]
+
+["browser_spectrum.js"]
+
+["browser_tableWidget_basic.js"]
+
+["browser_tableWidget_keyboard_interaction.js"]
+
+["browser_tableWidget_mouse_interaction.js"]
+skip-if = ["(os == 'linux' && os_version == '18.04' && bits == 64) && (!debug && !asan)"] #Bug 1118592
+
+["browser_telemetry_button_eyedropper.js"]
+
+["browser_telemetry_button_responsive.js"]
+skip-if = ["os == 'win'"] # Win: bug 1404197
+
+["browser_telemetry_misc.js"]
+
+["browser_telemetry_sidebar.js"]
+
+["browser_telemetry_toolbox.js"]
+
+["browser_telemetry_toolboxtabs_inspector.js"]
+
+["browser_telemetry_toolboxtabs_jsdebugger.js"]
+
+["browser_telemetry_toolboxtabs_jsprofiler.js"]
+
+["browser_telemetry_toolboxtabs_netmonitor.js"]
+
+["browser_telemetry_toolboxtabs_options.js"]
+
+["browser_telemetry_toolboxtabs_storage.js"]
+
+["browser_telemetry_toolboxtabs_styleeditor.js"]
+
+["browser_telemetry_toolboxtabs_webconsole.js"]
+
+["browser_theme.js"]
+
+["browser_theme_switching.js"]
+
+["browser_treeWidget_basic.js"]
+
+["browser_treeWidget_keyboard_interaction.js"]
+fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
+
+["browser_treeWidget_mouse_interaction.js"]
+fail-if = ["a11y_checks"] # Bug 1849028 clicked element may not be focusable and/or labeled
diff --git a/devtools/client/shared/test/browser_autocomplete_popup.js b/devtools/client/shared/test/browser_autocomplete_popup.js
new file mode 100644
index 0000000000..d9d057ea1c
--- /dev/null
+++ b/devtools/client/shared/test/browser_autocomplete_popup.js
@@ -0,0 +1,121 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function () {
+ const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+
+ info("Create an autocompletion popup");
+ const { doc } = await createHost();
+ const input = doc.createElement("input");
+ doc.body.appendChild(input);
+
+ const autocompleteOptions = {
+ position: "top",
+ autoSelect: true,
+ };
+ const popup = new AutocompletePopup(doc, autocompleteOptions);
+ input.focus();
+
+ const items = [
+ { label: "item0", value: "value0" },
+ { label: "item1", value: "value1" },
+ { label: "item2", value: "value2" },
+ ];
+
+ ok(!popup.isOpen, "popup is not open");
+ ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
+
+ const onPopupOpen = popup.once("popup-opened");
+ popup.openPopup(input);
+ await onPopupOpen;
+
+ ok(popup.isOpen, "popup is open");
+ is(popup.itemCount, 0, "no items");
+ ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
+
+ popup.setItems(items);
+
+ is(popup.itemCount, items.length, "items added");
+ is(
+ JSON.stringify(popup.getItems()),
+ JSON.stringify(items),
+ "getItems returns back the same items"
+ );
+ is(popup.selectedIndex, 0, "Index of the first item from top is selected.");
+ is(popup.selectedItem, items[0], "First item from top is selected");
+ // Make sure the list containing the active descendant doesn't get rebuilt
+ // when the selected item changes.
+ const listClone = getListFromActiveDescendant(popup, input);
+ checkActiveDescendant(popup, input, listClone);
+
+ popup.selectItemAtIndex(1);
+
+ is(popup.selectedIndex, 1, "index 1 is selected");
+ is(popup.selectedItem, items[1], "item1 is selected");
+ checkActiveDescendant(popup, input, listClone);
+
+ popup.selectedItem = items[2];
+
+ is(popup.selectedIndex, 2, "index 2 is selected");
+ is(popup.selectedItem, items[2], "item2 is selected");
+ checkActiveDescendant(popup, input, listClone);
+
+ is(popup.selectPreviousItem(), items[1], "selectPreviousItem() works");
+
+ is(popup.selectedIndex, 1, "index 1 is selected");
+ is(popup.selectedItem, items[1], "item1 is selected");
+ checkActiveDescendant(popup, input, listClone);
+
+ is(popup.selectNextItem(), items[2], "selectNextItem() works");
+
+ is(popup.selectedIndex, 2, "index 2 is selected");
+ is(popup.selectedItem, items[2], "item2 is selected");
+ checkActiveDescendant(popup, input, listClone);
+
+ ok(popup.selectNextItem(), "selectNextItem() works");
+
+ is(popup.selectedIndex, 0, "index 0 is selected");
+ is(popup.selectedItem, items[0], "item0 is selected");
+ checkActiveDescendant(popup, input, listClone);
+
+ popup.clearItems();
+ is(popup.itemCount, 0, "items cleared");
+ ok(!input.hasAttribute("aria-activedescendant"), "no aria-activedescendant");
+
+ const onPopupClose = popup.once("popup-closed");
+ popup.hidePopup();
+ await onPopupClose;
+});
+
+function stripNS(text) {
+ return text.replace(RegExp(' xmlns="http://www.w3.org/1999/xhtml"', "g"), "");
+}
+
+function getListFromActiveDescendant(popup, input) {
+ const activeElement = input.ownerDocument.activeElement;
+ const descendantId = activeElement.getAttribute("aria-activedescendant");
+ const cloneItem = input.ownerDocument.querySelector("#" + descendantId);
+ return cloneItem.parentNode;
+}
+
+function checkActiveDescendant(popup, input, list) {
+ const activeElement = input.ownerDocument.activeElement;
+ const descendantId = activeElement.getAttribute("aria-activedescendant");
+ const popupItem = popup._tooltip.panel.querySelector("#" + descendantId);
+ const cloneItem = input.ownerDocument.querySelector("#" + descendantId);
+
+ ok(popupItem, "Active descendant is found in the popup list");
+ ok(cloneItem, "Active descendant is found in the list clone");
+ is(
+ cloneItem.parentNode,
+ list,
+ "Active descendant is a child of the expected list"
+ );
+ is(
+ stripNS(popupItem.outerHTML),
+ cloneItem.outerHTML,
+ "Cloned item has the same HTML as the original element"
+ );
+}
diff --git a/devtools/client/shared/test/browser_autocomplete_popup_consecutive-show.js b/devtools/client/shared/test/browser_autocomplete_popup_consecutive-show.js
new file mode 100644
index 0000000000..1af2df3a1f
--- /dev/null
+++ b/devtools/client/shared/test/browser_autocomplete_popup_consecutive-show.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that calling `showPopup` multiple time does not lead to invalid state.
+
+add_task(async function () {
+ const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+
+ info("Create an autocompletion popup");
+ const { doc } = await createHost();
+ const input = doc.createElement("input");
+ doc.body.appendChild(input);
+
+ const autocompleteOptions = {
+ position: "top",
+ autoSelect: true,
+ useXulWrapper: true,
+ };
+ const popup = new AutocompletePopup(doc, autocompleteOptions);
+ const items = [{ label: "a" }, { label: "b" }, { label: "c" }];
+ popup.setItems(items);
+
+ input.focus();
+
+ let onAllEventsReceived = waitForNEvents(popup, "popup-opened", 3);
+ // Note that the lack of `await` on those function calls are wanted.
+ popup.openPopup(input, 0, 0, 0);
+ popup.openPopup(input, 0, 0, 1);
+ popup.openPopup(input, 0, 0, 2);
+ await onAllEventsReceived;
+
+ ok(popup.isOpen, "popup is open");
+ is(
+ popup.selectedIndex,
+ 2,
+ "Selected index matches the one that was set last when calling openPopup"
+ );
+
+ onAllEventsReceived = waitForNEvents(popup, "popup-opened", 2);
+ // Note that the lack of `await` on those function calls are wanted.
+ popup.openPopup(input, 0, 0, 1);
+ popup.openPopup(input);
+ await onAllEventsReceived;
+
+ ok(popup.isOpen, "popup is open");
+ is(
+ popup.selectedIndex,
+ 0,
+ "First item is selected, as last call to openPopup did not specify an index and autoSelect is true"
+ );
+
+ const onPopupClose = popup.once("popup-closed");
+ popup.hidePopup();
+ await onPopupClose;
+});
diff --git a/devtools/client/shared/test/browser_autocomplete_popup_input.js b/devtools/client/shared/test/browser_autocomplete_popup_input.js
new file mode 100644
index 0000000000..a7d04f1c9c
--- /dev/null
+++ b/devtools/client/shared/test/browser_autocomplete_popup_input.js
@@ -0,0 +1,251 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function () {
+ // Prevent the URL Bar to steal the focus.
+ const preventUrlBarFocus = e => {
+ e.preventDefault();
+ };
+ window.gURLBar.addEventListener("beforefocus", preventUrlBarFocus);
+ registerCleanupFunction(() => {
+ window.gURLBar.removeEventListener("beforefocus", preventUrlBarFocus);
+ });
+
+ const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+
+ info("Create an autocompletion popup and an input that will be bound to it");
+ const { doc } = await createHost();
+
+ const input = doc.createElement("input");
+ doc.body.append(input, doc.createElement("input"));
+
+ const onSelectCalled = [];
+ const onClickCalled = [];
+ const popup = new AutocompletePopup(doc, {
+ input,
+ position: "top",
+ autoSelect: true,
+ onSelect: item => onSelectCalled.push(item),
+ onClick: (e, item) => onClickCalled.push(item),
+ });
+
+ input.focus();
+ ok(hasFocus(input), "input has focus");
+
+ info(
+ "Check that Tab moves the focus out of the input when the popup isn't opened"
+ );
+ EventUtils.synthesizeKey("KEY_Tab");
+ is(onClickCalled.length, 0, "onClick wasn't called");
+ is(hasFocus(input), false, "input does not have the focus anymore");
+ info("Set the focus back to the input and open the popup");
+ input.focus();
+ await new Promise(res => setTimeout(res, 0));
+ ok(hasFocus(input), "input is focused");
+
+ await populateAndOpenPopup(popup);
+
+ const checkSelectedItem = (expected, info) =>
+ checkPopupSelectedItem(popup, input, expected, info);
+
+ checkSelectedItem(popupItems[0], "First item from top is selected");
+ is(
+ onSelectCalled[0].label,
+ popupItems[0].label,
+ "onSelect was called with expected param"
+ );
+
+ info("Check that arrow down/up navigates into the list");
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ checkSelectedItem(popupItems[1], "item-1 is selected");
+ is(
+ onSelectCalled[1].label,
+ popupItems[1].label,
+ "onSelect was called with expected param"
+ );
+
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ checkSelectedItem(popupItems[2], "item-2 is selected");
+ is(
+ onSelectCalled[2].label,
+ popupItems[2].label,
+ "onSelect was called with expected param"
+ );
+
+ EventUtils.synthesizeKey("KEY_ArrowDown");
+ checkSelectedItem(popupItems[0], "item-0 is selected");
+ is(
+ onSelectCalled[3].label,
+ popupItems[0].label,
+ "onSelect was called with expected param"
+ );
+
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ checkSelectedItem(popupItems[2], "item-2 is selected");
+ is(
+ onSelectCalled[4].label,
+ popupItems[2].label,
+ "onSelect was called with expected param"
+ );
+
+ EventUtils.synthesizeKey("KEY_ArrowUp");
+ checkSelectedItem(popupItems[1], "item-2 is selected");
+ is(
+ onSelectCalled[5].label,
+ popupItems[1].label,
+ "onSelect was called with expected param"
+ );
+
+ info("Check that Escape closes the popup");
+ let onPopupClosed = popup.once("popup-closed");
+ EventUtils.synthesizeKey("KEY_Escape");
+ await onPopupClosed;
+ ok(true, "popup was closed with Escape key");
+ ok(hasFocus(input), "input still has the focus");
+ is(onClickCalled.length, 0, "onClick wasn't called");
+
+ info("Fill the input");
+ const value = "item";
+ EventUtils.sendString(value);
+ is(input.value, value, "input has the expected value");
+ is(
+ input.selectionStart,
+ value.length,
+ "input cursor is at expected position"
+ );
+ info("Open the popup again");
+ await populateAndOpenPopup(popup);
+
+ info("Check that Arrow Left + Shift does not close the popup");
+ const timeoutRes = "TIMED_OUT";
+ const onRaceEnded = Promise.race([
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(res => setTimeout(() => res(timeoutRes), 500)),
+ popup.once("popup-closed"),
+ ]);
+ EventUtils.synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
+ const raceResult = await onRaceEnded;
+ is(raceResult, timeoutRes, "popup wasn't closed");
+ ok(popup.isOpen, "popup is still open");
+ is(input.selectionEnd - input.selectionStart, 1, "text was selected");
+ ok(hasFocus(input), "input still has the focus");
+
+ info("Check that Arrow Left closes the popup");
+ onPopupClosed = popup.once("popup-closed");
+ EventUtils.synthesizeKey("KEY_ArrowLeft");
+ await onPopupClosed;
+ is(
+ input.selectionStart,
+ value.length - 1,
+ "input cursor was moved one char back"
+ );
+ is(input.selectionEnd, input.selectionStart, "selection was removed");
+ is(onClickCalled.length, 0, "onClick wasn't called");
+ ok(hasFocus(input), "input still has the focus");
+
+ info("Open the popup again");
+ await populateAndOpenPopup(popup);
+
+ info("Check that Arrow Right + Shift does not trigger onClick");
+ EventUtils.synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ is(onClickCalled.length, 0, "onClick wasn't called");
+ is(input.selectionEnd - input.selectionStart, 1, "input text was selected");
+ ok(hasFocus(input), "input still has the focus");
+
+ info("Check that Arrow Right triggers onClick");
+ EventUtils.synthesizeKey("KEY_ArrowRight");
+ is(onClickCalled.length, 1, "onClick was called");
+ is(
+ onClickCalled[0],
+ popupItems[0],
+ "onClick was called with the selected item"
+ );
+ ok(hasFocus(input), "input still has the focus");
+
+ info("Check that Enter triggers onClick");
+ EventUtils.synthesizeKey("KEY_Enter");
+ is(onClickCalled.length, 2, "onClick was called");
+ is(
+ onClickCalled[1],
+ popupItems[0],
+ "onClick was called with the selected item"
+ );
+ ok(hasFocus(input), "input still has the focus");
+
+ info("Check that Tab triggers onClick");
+ EventUtils.synthesizeKey("KEY_Tab");
+ is(onClickCalled.length, 3, "onClick was called");
+ is(
+ onClickCalled[2],
+ popupItems[0],
+ "onClick was called with the selected item"
+ );
+ ok(hasFocus(input), "input still has the focus");
+
+ info(
+ "Check that Shift+Tab does not trigger onClick and move the focus out of the input"
+ );
+ EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true });
+ is(onClickCalled.length, 3, "onClick wasn't called");
+ is(hasFocus(input), false, "input does not have the focus anymore");
+
+ const onPopupClose = popup.once("popup-closed");
+ popup.hidePopup();
+ await onPopupClose;
+});
+
+const popupItems = [
+ { label: "item-0", value: "value-0" },
+ { label: "item-1", value: "value-1" },
+ { label: "item-2", value: "value-2" },
+];
+
+async function populateAndOpenPopup(popup) {
+ popup.setItems(popupItems);
+ await popup.openPopup();
+}
+
+/**
+ * Returns true if the give node is currently focused.
+ */
+function hasFocus(node) {
+ return (
+ node.ownerDocument.activeElement == node && node.ownerDocument.hasFocus()
+ );
+}
+
+/**
+ * Check that the selected item in the popup is the expected one. Also check that the
+ * active descendant is properly set and that the popup has the focus.
+ *
+ * @param {AutocompletePopup} popup
+ * @param {HTMLInput} input
+ * @param {Object} expectedSelectedItem
+ * @param {String} info
+ */
+function checkPopupSelectedItem(popup, input, expectedSelectedItem, info) {
+ is(popup.selectedItem.label, expectedSelectedItem.label, info);
+ checkActiveDescendant(popup, input);
+ ok(hasFocus(input), "input still has the focus");
+}
+
+function checkActiveDescendant(popup, input) {
+ const activeElement = input.ownerDocument.activeElement;
+ const descendantId = activeElement.getAttribute("aria-activedescendant");
+ const popupItem = popup._tooltip.panel.querySelector(`#${descendantId}`);
+ const cloneItem = input.ownerDocument.querySelector(`#${descendantId}`);
+
+ ok(popupItem, "Active descendant is found in the popup list");
+ ok(cloneItem, "Active descendant is found in the list clone");
+ is(
+ stripNS(popupItem.outerHTML),
+ cloneItem.outerHTML,
+ "Cloned item has the same HTML as the original element"
+ );
+}
+
+function stripNS(text) {
+ return text.replace(RegExp(' xmlns="http://www.w3.org/1999/xhtml"', "g"), "");
+}
diff --git a/devtools/client/shared/test/browser_browserloader_mocks.js b/devtools/client/shared/test/browser_browserloader_mocks.js
new file mode 100644
index 0000000000..6cc38259f3
--- /dev/null
+++ b/devtools/client/shared/test/browser_browserloader_mocks.js
@@ -0,0 +1,162 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { BrowserLoader } = ChromeUtils.import(
+ "resource://devtools/shared/loader/browser-loader.js"
+);
+
+const {
+ getMockedModule,
+ setMockedModule,
+ removeMockedModule,
+} = require("resource://devtools/shared/loader/browser-loader-mocks.js");
+const { require: browserRequire } = BrowserLoader({
+ baseURI: "resource://devtools/client/shared/",
+ window,
+});
+
+// Check that modules can be mocked in the browser loader.
+// Test with a custom test module under the chrome:// scheme.
+function testWithChromeScheme() {
+ // Full chrome URI for the test module.
+ const CHROME_URI = CHROME_URL_ROOT + "test-mocked-module.js";
+ const ORIGINAL_VALUE = "Original value";
+ const MOCKED_VALUE_1 = "Mocked value 1";
+ const MOCKED_VALUE_2 = "Mocked value 2";
+
+ const m1 = browserRequire(CHROME_URI);
+ ok(m1, "Regular module can be required");
+ is(m1.methodToMock(), ORIGINAL_VALUE, "Method returns the expected value");
+ is(m1.someProperty, "someProperty", "Property has the expected value");
+
+ info("Create a simple mocked version of the test module");
+ const mockedModule = {
+ methodToMock: () => MOCKED_VALUE_1,
+ someProperty: "somePropertyMocked",
+ };
+ setMockedModule(mockedModule, CHROME_URI);
+ ok(!!getMockedModule(CHROME_URI), "Has an entry for the chrome URI.");
+
+ const m2 = browserRequire(CHROME_URI);
+ ok(m2, "Mocked module can be required via chrome URI");
+ is(
+ m2.methodToMock(),
+ MOCKED_VALUE_1,
+ "Mocked method returns the expected value"
+ );
+ is(
+ m2.someProperty,
+ "somePropertyMocked",
+ "Mocked property has the expected value"
+ );
+
+ const { methodToMock: requiredMethod } = browserRequire(CHROME_URI);
+ Assert.strictEqual(
+ requiredMethod(),
+ MOCKED_VALUE_1,
+ "Mocked method returns the expected value when imported with destructuring"
+ );
+
+ info("Update the mocked method to return a different value");
+ mockedModule.methodToMock = () => MOCKED_VALUE_2;
+ is(
+ requiredMethod(),
+ MOCKED_VALUE_2,
+ "Mocked method returns the updated value when imported with destructuring"
+ );
+
+ info("Remove the mock for the test module");
+ removeMockedModule(CHROME_URI);
+ ok(!getMockedModule(CHROME_URI), "Has no entry for the chrome URI.");
+
+ const m3 = browserRequire(CHROME_URI);
+ ok(m3, "Regular module can be required after removing the mock");
+ is(
+ m3.methodToMock(),
+ ORIGINAL_VALUE,
+ "Method on module returns the expected value"
+ );
+}
+
+// Similar tests as in testWithChromeScheme, but this time with a devtools module
+// available under the resource:// scheme.
+function testWithRegularDevtoolsModule() {
+ // Testing with devtools/shared/path because it is a simple module, that can be imported
+ // with destructuring. Any other module would do.
+ const DEVTOOLS_MODULE_PATH = "devtools/shared/path";
+ const DEVTOOLS_MODULE_URI = "resource://devtools/shared/path.js";
+
+ const m1 = browserRequire(DEVTOOLS_MODULE_PATH);
+ is(
+ m1.joinURI("https://a", "b"),
+ "https://a/b",
+ "Original module was required"
+ );
+
+ info(
+ "Set a mock for a sub-part of the path, which should not match require calls"
+ );
+ setMockedModule({ joinURI: () => "WRONG_PATH" }, "shared/path");
+
+ ok(
+ !getMockedModule(DEVTOOLS_MODULE_URI),
+ "Has no mock entry for the full URI"
+ );
+ const m2 = browserRequire(DEVTOOLS_MODULE_PATH);
+ is(
+ m2.joinURI("https://a", "b"),
+ "https://a/b",
+ "Original module is still required"
+ );
+
+ info(
+ "Set a mock for the complete path, which should now match require calls"
+ );
+ const mockedModule = {
+ joinURI: () => "MOCKED VALUE",
+ };
+ setMockedModule(mockedModule, DEVTOOLS_MODULE_PATH);
+ ok(
+ !!getMockedModule(DEVTOOLS_MODULE_URI),
+ "Has a mock entry for the full URI."
+ );
+
+ const m3 = browserRequire(DEVTOOLS_MODULE_PATH);
+ is(
+ m3.joinURI("https://a", "b"),
+ "MOCKED VALUE",
+ "The mocked module has been returned"
+ );
+
+ info(
+ "Check that the mocked methods can be updated after a destructuring import"
+ );
+ const { joinURI } = browserRequire(DEVTOOLS_MODULE_PATH);
+ mockedModule.joinURI = () => "UPDATED VALUE";
+ is(
+ joinURI("https://a", "b"),
+ "UPDATED VALUE",
+ "Mocked method was correctly updated"
+ );
+
+ removeMockedModule(DEVTOOLS_MODULE_PATH);
+ ok(
+ !getMockedModule(DEVTOOLS_MODULE_URI),
+ "Has no mock entry for the full URI"
+ );
+ const m4 = browserRequire(DEVTOOLS_MODULE_PATH);
+ is(
+ m4.joinURI("https://a", "b"),
+ "https://a/b",
+ "Original module can be required again"
+ );
+}
+
+function test() {
+ testWithChromeScheme();
+ testWithRegularDevtoolsModule();
+ delete window.getBrowserLoaderForWindow;
+ finish();
+}
diff --git a/devtools/client/shared/test/browser_css_angle.js b/devtools/client/shared/test/browser_css_angle.js
new file mode 100644
index 0000000000..a29d3e4f0e
--- /dev/null
+++ b/devtools/client/shared/test/browser_css_angle.js
@@ -0,0 +1,204 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* import-globals-from head.js */
+"use strict";
+
+var { angleUtils } = require("resource://devtools/client/shared/css-angle.js");
+
+add_task(async function () {
+ await addTab("about:blank");
+ const { host } = await createHost("bottom");
+
+ info("Starting the test");
+ testAngleUtils();
+ testAngleValidity();
+
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function testAngleUtils() {
+ const data = getTestData();
+
+ for (const { authored, deg, rad, grad, turn } of data) {
+ const angle = new angleUtils.CssAngle(authored);
+
+ // Check all values.
+ info("Checking values for " + authored);
+ is(angle.deg, deg, "color.deg === deg");
+ is(angle.rad, rad, "color.rad === rad");
+ is(angle.grad, grad, "color.grad === grad");
+ is(angle.turn, turn, "color.turn === turn");
+
+ testToString(angle, deg, rad, grad, turn);
+ }
+}
+
+function testAngleValidity() {
+ const data = getAngleValidityData();
+
+ for (const { angle, result } of data) {
+ const testAngle = new angleUtils.CssAngle(angle);
+ const validString = testAngle.valid ? " a valid" : "an invalid";
+
+ is(
+ testAngle.valid,
+ result,
+ `Testing that "${angle}" is ${validString} angle`
+ );
+ }
+}
+
+function testToString(angle, deg, rad, grad, turn) {
+ const { ANGLEUNIT } = angleUtils.CssAngle.prototype;
+ angle.angleUnit = ANGLEUNIT.deg;
+ is(angle.toString(), deg, "toString() with deg type");
+
+ angle.angleUnit = ANGLEUNIT.rad;
+ is(angle.toString(), rad, "toString() with rad type");
+
+ angle.angleUnit = ANGLEUNIT.grad;
+ is(angle.toString(), grad, "toString() with grad type");
+
+ angle.angleUnit = ANGLEUNIT.turn;
+ is(angle.toString(), turn, "toString() with turn type");
+}
+
+function getAngleValidityData() {
+ return [
+ {
+ angle: "0.2turn",
+ result: true,
+ },
+ {
+ angle: "-0.2turn",
+ result: true,
+ },
+ {
+ angle: "-.2turn",
+ result: true,
+ },
+ {
+ angle: "1e02turn",
+ result: true,
+ },
+ {
+ angle: "-2e2turn",
+ result: true,
+ },
+ {
+ angle: ".2turn",
+ result: true,
+ },
+ {
+ angle: "0.2aaturn",
+ result: false,
+ },
+ {
+ angle: "2dega",
+ result: false,
+ },
+ {
+ angle: "0.deg",
+ result: false,
+ },
+ {
+ angle: ".deg",
+ result: false,
+ },
+ {
+ angle: "..2turn",
+ result: false,
+ },
+ ];
+}
+
+function getTestData() {
+ return [
+ {
+ authored: "0deg",
+ deg: "0deg",
+ rad: "0rad",
+ grad: "0grad",
+ turn: "0turn",
+ },
+ {
+ authored: "180deg",
+ deg: "180deg",
+ rad: `${Math.round(Math.PI * 10000) / 10000}rad`,
+ grad: "200grad",
+ turn: "0.5turn",
+ },
+ {
+ authored: "180DEG",
+ deg: "180DEG",
+ rad: `${Math.round(Math.PI * 10000) / 10000}RAD`,
+ grad: "200GRAD",
+ turn: "0.5TURN",
+ },
+ {
+ authored: `-${Math.PI}rad`,
+ deg: "-180deg",
+ rad: `-${Math.PI}rad`,
+ grad: "-200grad",
+ turn: "-0.5turn",
+ },
+ {
+ authored: `-${Math.PI}RAD`,
+ deg: "-180DEG",
+ rad: `-${Math.PI}RAD`,
+ grad: "-200GRAD",
+ turn: "-0.5TURN",
+ },
+ {
+ authored: "100grad",
+ deg: "90deg",
+ rad: `${Math.round((Math.PI / 2) * 10000) / 10000}rad`,
+ grad: "100grad",
+ turn: "0.25turn",
+ },
+ {
+ authored: "100GRAD",
+ deg: "90DEG",
+ rad: `${Math.round((Math.PI / 2) * 10000) / 10000}RAD`,
+ grad: "100GRAD",
+ turn: "0.25TURN",
+ },
+ {
+ authored: "-1turn",
+ deg: "-360deg",
+ rad: `${(-1 * Math.round(Math.PI * 2 * 10000)) / 10000}rad`,
+ grad: "-400grad",
+ turn: "-1turn",
+ },
+ {
+ authored: "-10TURN",
+ deg: "-3600DEG",
+ rad: `${(-1 * Math.round(Math.PI * 2 * 10 * 10000)) / 10000}RAD`,
+ grad: "-4000GRAD",
+ turn: "-10TURN",
+ },
+ {
+ authored: "inherit",
+ deg: "inherit",
+ rad: "inherit",
+ grad: "inherit",
+ turn: "inherit",
+ },
+ {
+ authored: "initial",
+ deg: "initial",
+ rad: "initial",
+ grad: "initial",
+ turn: "initial",
+ },
+ {
+ authored: "unset",
+ deg: "unset",
+ rad: "unset",
+ grad: "unset",
+ turn: "unset",
+ },
+ ];
+}
diff --git a/devtools/client/shared/test/browser_css_color.js b/devtools/client/shared/test/browser_css_color.js
new file mode 100644
index 0000000000..d66656ac86
--- /dev/null
+++ b/devtools/client/shared/test/browser_css_color.js
@@ -0,0 +1,106 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var { colorUtils } = require("resource://devtools/shared/css/color.js");
+/* global getFixtureColorData */
+loadHelperScript("helper_color_data.js");
+
+add_task(async function () {
+ await addTab("about:blank");
+ const { host, doc } = await createHost("bottom");
+
+ info("Creating a test canvas element to test colors");
+ const canvas = createTestCanvas(doc);
+ info("Starting the test");
+ testColorUtils(canvas);
+
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function createTestCanvas(doc) {
+ const canvas = doc.createElement("canvas");
+ canvas.width = canvas.height = 10;
+ doc.body.appendChild(canvas);
+ return canvas;
+}
+
+function testColorUtils(canvas) {
+ const data = getFixtureColorData();
+
+ for (const { authored, name, hex, hsl, rgb } of data) {
+ const color = new colorUtils.CssColor(authored);
+
+ // Check all values.
+ info("Checking values for " + authored);
+ is(color.name, name, "color.name === name");
+ is(color.hex, hex, "color.hex === hex");
+ is(color.hsl, hsl, "color.hsl === hsl");
+ is(color.rgb, rgb, "color.rgb === rgb");
+
+ testToString(color, name, hex, hsl, rgb);
+ testColorMatch(name, hex, hsl, rgb, color.rgba, canvas);
+ }
+}
+
+function testToString(color, name, hex, hsl, rgb) {
+ const { COLORUNIT } = colorUtils.CssColor;
+ is(color.toString(COLORUNIT.name), name, "toString() with authored type");
+ is(color.toString(COLORUNIT.hex), hex, "toString() with hex type");
+ is(color.toString(COLORUNIT.hsl), hsl, "toString() with hsl type");
+ is(color.toString(COLORUNIT.rgb), rgb, "toString() with rgb type");
+}
+
+function testColorMatch(name, hex, hsl, rgb, rgba, canvas) {
+ let target;
+ const ctx = canvas.getContext("2d");
+
+ const clearCanvas = function () {
+ canvas.width = 1;
+ };
+ const setColor = function (color) {
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, 1, 1);
+ };
+ const setTargetColor = function () {
+ clearCanvas();
+ // All colors have rgba so we can use this to compare against.
+ setColor(rgba);
+ const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
+ target = { r, g, b, a };
+ };
+ const test = function (color, type) {
+ // hsla -> rgba -> hsla produces inaccurate results so we
+ // need some tolerence here.
+ const tolerance = 3;
+ clearCanvas();
+
+ setColor(color);
+ const [r, g, b, a] = ctx.getImageData(0, 0, 1, 1).data;
+
+ const rgbFail =
+ Math.abs(r - target.r) > tolerance ||
+ Math.abs(g - target.g) > tolerance ||
+ Math.abs(b - target.b) > tolerance;
+ ok(!rgbFail, "color " + rgba + " matches target. Type: " + type);
+ if (rgbFail) {
+ info(
+ `target: ${JSON.stringify(
+ target
+ )}, color: [r: ${r}, g: ${g}, b: ${b}, a: ${a}]`
+ );
+ }
+
+ const alphaFail = a !== target.a;
+ ok(!alphaFail, "color " + rgba + " alpha value matches target.");
+ };
+
+ setTargetColor();
+
+ test(name, "name");
+ test(hex, "hex");
+ test(hsl, "hsl");
+ test(rgb, "rgb");
+}
diff --git a/devtools/client/shared/test/browser_cubic-bezier-01.js b/devtools/client/shared/test/browser_cubic-bezier-01.js
new file mode 100644
index 0000000000..b57e2de25b
--- /dev/null
+++ b/devtools/client/shared/test/browser_cubic-bezier-01.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the CubicBezierWidget generates content in a given parent node
+
+const {
+ CubicBezierWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+
+add_task(async function () {
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ info("Checking that the graph markup is created in the parent");
+ const container = doc.querySelector("#cubic-bezier-container");
+ const w = new CubicBezierWidget(container);
+
+ ok(container.querySelector(".display-wrap"), "The display has been added");
+
+ ok(
+ container.querySelector(".coordinate-plane"),
+ "The coordinate plane has been added"
+ );
+ const buttons = container.querySelectorAll("button");
+ is(buttons.length, 2, "The 2 control points have been added");
+ is(buttons[0].className, "control-point");
+ is(buttons[1].className, "control-point");
+ ok(container.querySelector("canvas"), "The curve canvas has been added");
+
+ info("Destroying the widget");
+ w.destroy();
+ is(container.children.length, 0, "All nodes have been removed");
+
+ host.destroy();
+});
diff --git a/devtools/client/shared/test/browser_cubic-bezier-02.js b/devtools/client/shared/test/browser_cubic-bezier-02.js
new file mode 100644
index 0000000000..3ce0af2f99
--- /dev/null
+++ b/devtools/client/shared/test/browser_cubic-bezier-02.js
@@ -0,0 +1,206 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the CubicBezierWidget events
+
+const {
+ CubicBezierWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+const {
+ PREDEFINED,
+} = require("resource://devtools/client/shared/widgets/CubicBezierPresets.js");
+
+// In this test we have to use a slightly more complete HTML tree, with <body>
+// in order to remove its margin and prevent shifted positions
+const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-02.html";
+
+add_task(async function () {
+ const { host, win, doc } = await createHost("bottom", TEST_URI);
+
+ // Required or widget will be clipped inside of 'bottom'
+ // host by -14. Setting `fixed` zeroes this which is needed for
+ // calculating offsets. Occurs in test env only.
+ doc.body.setAttribute("style", "position: fixed; margin: 0;");
+
+ const container = doc.querySelector("#cubic-bezier-container");
+ const w = new CubicBezierWidget(container, PREDEFINED.linear);
+
+ const rect = w.curve.getBoundingClientRect();
+ rect.graphTop = rect.height * w.bezierCanvas.padding[0];
+ rect.graphBottom = rect.height - rect.graphTop;
+ rect.graphHeight = rect.graphBottom - rect.graphTop;
+
+ await pointsCanBeDragged(w, win, doc, rect);
+ await curveCanBeClicked(w, win, doc, rect);
+ await pointsCanBeMovedWithKeyboard(w, win, doc, rect);
+
+ w.destroy();
+ host.destroy();
+});
+
+async function pointsCanBeDragged(widget, win, doc, offsets) {
+ info("Checking that the control points can be dragged with the mouse");
+
+ info("Listening for the update event");
+ let onUpdated = widget.once("updated");
+
+ info("Generating a mousedown/move/up on P1");
+ widget._onPointMouseDown({ target: widget.p1 });
+ doc.onmousemove({ pageX: offsets.left, pageY: offsets.graphTop });
+ doc.onmouseup();
+
+ let bezier = await onUpdated;
+ ok(true, "The widget fired the updated event");
+ ok(bezier, "The updated event contains a bezier argument");
+ is(bezier.P1[0], 0, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], 1, "The new P1 progress coordinate is correct");
+
+ info("Listening for the update event");
+ onUpdated = widget.once("updated");
+
+ info("Generating a mousedown/move/up on P2");
+ widget._onPointMouseDown({ target: widget.p2 });
+ doc.onmousemove({ pageX: offsets.right, pageY: offsets.graphBottom });
+ doc.onmouseup();
+
+ bezier = await onUpdated;
+ is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+ is(bezier.P2[1], 0, "The new P2 progress coordinate is correct");
+}
+
+async function curveCanBeClicked(widget, win, doc, offsets) {
+ info("Checking that clicking on the curve moves the closest control point");
+
+ info("Listening for the update event");
+ let onUpdated = widget.once("updated");
+
+ info("Click close to P1");
+ let x = offsets.left + offsets.width / 4.0;
+ let y = offsets.graphTop + offsets.graphHeight / 4.0;
+ widget._onCurveClick({ pageX: x, pageY: y });
+
+ let bezier = await onUpdated;
+ ok(true, "The widget fired the updated event");
+ is(bezier.P1[0], 0.25, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+ is(bezier.P2[0], 1, "P2 time coordinate remained unchanged");
+ is(bezier.P2[1], 0, "P2 progress coordinate remained unchanged");
+
+ info("Listening for the update event");
+ onUpdated = widget.once("updated");
+
+ info("Click close to P2");
+ x = offsets.right - offsets.width / 4;
+ y = offsets.graphBottom - offsets.graphHeight / 4;
+ widget._onCurveClick({ pageX: x, pageY: y });
+
+ bezier = await onUpdated;
+ is(bezier.P2[0], 0.75, "The new P2 time coordinate is correct");
+ is(bezier.P2[1], 0.25, "The new P2 progress coordinate is correct");
+ is(bezier.P1[0], 0.25, "P1 time coordinate remained unchanged");
+ is(bezier.P1[1], 0.75, "P1 progress coordinate remained unchanged");
+}
+
+async function pointsCanBeMovedWithKeyboard(widget, win, doc, offsets) {
+ info("Checking that points respond to keyboard events");
+
+ const singleStep = 3;
+ const shiftStep = 30;
+
+ info("Moving P1 to the left");
+ let newOffset = parseInt(widget.p1.style.left, 10) - singleStep;
+ let x = widget.bezierCanvas.offsetsToCoordinates({
+ style: { left: newOffset },
+ })[0];
+
+ let onUpdated = widget.once("updated");
+ widget._onPointKeyDown(getKeyEvent(widget.p1, 37));
+ let bezier = await onUpdated;
+
+ is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+
+ info("Moving P1 to the left, fast");
+ newOffset = parseInt(widget.p1.style.left, 10) - shiftStep;
+ x = widget.bezierCanvas.offsetsToCoordinates({
+ style: { left: newOffset },
+ })[0];
+
+ onUpdated = widget.once("updated");
+ widget._onPointKeyDown(getKeyEvent(widget.p1, 37, true));
+ bezier = await onUpdated;
+ is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+
+ info("Moving P1 to the right, fast");
+ newOffset = parseInt(widget.p1.style.left, 10) + shiftStep;
+ x = widget.bezierCanvas.offsetsToCoordinates({
+ style: { left: newOffset },
+ })[0];
+
+ onUpdated = widget.once("updated");
+ widget._onPointKeyDown(getKeyEvent(widget.p1, 39, true));
+ bezier = await onUpdated;
+ is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], 0.75, "The new P1 progress coordinate is correct");
+
+ info("Moving P1 to the bottom");
+ newOffset = parseInt(widget.p1.style.top, 10) + singleStep;
+ let y = widget.bezierCanvas.offsetsToCoordinates({
+ style: { top: newOffset },
+ })[1];
+
+ onUpdated = widget.once("updated");
+ widget._onPointKeyDown(getKeyEvent(widget.p1, 40));
+ bezier = await onUpdated;
+ is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], y, "The new P1 progress coordinate is correct");
+
+ info("Moving P1 to the bottom, fast");
+ newOffset = parseInt(widget.p1.style.top, 10) + shiftStep;
+ y = widget.bezierCanvas.offsetsToCoordinates({
+ style: { top: newOffset },
+ })[1];
+
+ onUpdated = widget.once("updated");
+ widget._onPointKeyDown(getKeyEvent(widget.p1, 40, true));
+ bezier = await onUpdated;
+ is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], y, "The new P1 progress coordinate is correct");
+
+ info("Moving P1 to the top, fast");
+ newOffset = parseInt(widget.p1.style.top, 10) - shiftStep;
+ y = widget.bezierCanvas.offsetsToCoordinates({
+ style: { top: newOffset },
+ })[1];
+
+ onUpdated = widget.once("updated");
+ widget._onPointKeyDown(getKeyEvent(widget.p1, 38, true));
+ bezier = await onUpdated;
+ is(bezier.P1[0], x, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], y, "The new P1 progress coordinate is correct");
+
+ info("Checking that keyboard events also work with P2");
+ info("Moving P2 to the left");
+ newOffset = parseInt(widget.p2.style.left, 10) - singleStep;
+ x = widget.bezierCanvas.offsetsToCoordinates({
+ style: { left: newOffset },
+ })[0];
+
+ onUpdated = widget.once("updated");
+ widget._onPointKeyDown(getKeyEvent(widget.p2, 37));
+ bezier = await onUpdated;
+ is(bezier.P2[0], x, "The new P2 time coordinate is correct");
+ is(bezier.P2[1], 0.25, "The new P2 progress coordinate is correct");
+}
+
+function getKeyEvent(target, keyCode, shift = false) {
+ return {
+ target,
+ keyCode,
+ shiftKey: shift,
+ preventDefault: () => {},
+ };
+}
diff --git a/devtools/client/shared/test/browser_cubic-bezier-03.js b/devtools/client/shared/test/browser_cubic-bezier-03.js
new file mode 100644
index 0000000000..2c722bbf41
--- /dev/null
+++ b/devtools/client/shared/test/browser_cubic-bezier-03.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that coordinates can be changed programatically in the CubicBezierWidget
+
+const {
+ CubicBezierWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+const {
+ PREDEFINED,
+} = require("resource://devtools/client/shared/widgets/CubicBezierPresets.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+
+add_task(async function () {
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#cubic-bezier-container");
+ const w = new CubicBezierWidget(container, PREDEFINED.linear);
+
+ await coordinatesCanBeChangedByProvidingAnArray(w);
+ await coordinatesCanBeChangedByProvidingAValue(w);
+
+ w.destroy();
+ host.destroy();
+});
+
+async function coordinatesCanBeChangedByProvidingAnArray(widget) {
+ info("Listening for the update event");
+ const onUpdated = widget.once("updated");
+
+ info("Setting new coordinates");
+ widget.coordinates = [0, 1, 1, 0];
+
+ const bezier = await onUpdated;
+ ok(true, "The updated event was fired as a result of setting coordinates");
+
+ is(bezier.P1[0], 0, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], 1, "The new P1 progress coordinate is correct");
+ is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+ is(bezier.P2[1], 0, "The new P2 progress coordinate is correct");
+}
+
+async function coordinatesCanBeChangedByProvidingAValue(widget) {
+ info("Listening for the update event");
+ let onUpdated = widget.once("updated");
+
+ info("Setting linear css value");
+ widget.cssCubicBezierValue = "linear";
+ let bezier = await onUpdated;
+ ok(true, "The updated event was fired as a result of setting cssValue");
+
+ is(bezier.P1[0], 0, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], 0, "The new P1 progress coordinate is correct");
+ is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+ is(bezier.P2[1], 1, "The new P2 progress coordinate is correct");
+
+ info("Setting a custom cubic-bezier css value");
+ onUpdated = widget.once("updated");
+ widget.cssCubicBezierValue = "cubic-bezier(.25,-0.5, 1, 1.25)";
+ bezier = await onUpdated;
+ ok(true, "The updated event was fired as a result of setting cssValue");
+
+ is(bezier.P1[0], 0.25, "The new P1 time coordinate is correct");
+ is(bezier.P1[1], -0.5, "The new P1 progress coordinate is correct");
+ is(bezier.P2[0], 1, "The new P2 time coordinate is correct");
+ is(bezier.P2[1], 1.25, "The new P2 progress coordinate is correct");
+}
diff --git a/devtools/client/shared/test/browser_cubic-bezier-04.js b/devtools/client/shared/test/browser_cubic-bezier-04.js
new file mode 100644
index 0000000000..1fff1821ff
--- /dev/null
+++ b/devtools/client/shared/test/browser_cubic-bezier-04.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the CubicBezierPresetWidget generates markup.
+
+const {
+ CubicBezierPresetWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+const {
+ PRESETS,
+} = require("resource://devtools/client/shared/widgets/CubicBezierPresets.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+
+add_task(async function () {
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#cubic-bezier-container");
+ const w = new CubicBezierPresetWidget(container);
+
+ info("Checking that the presets are created in the parent");
+ ok(container.querySelector(".preset-pane"), "The preset pane has been added");
+
+ ok(
+ container.querySelector("#preset-categories"),
+ "The preset categories have been added"
+ );
+ const categories = container.querySelectorAll(".category");
+ is(
+ categories.length,
+ Object.keys(PRESETS).length,
+ "The preset categories have been added"
+ );
+ Object.keys(PRESETS).forEach(category => {
+ ok(container.querySelector("#" + category), `${category} has been added`);
+ ok(
+ container.querySelector("#preset-category-" + category),
+ `The preset list for ${category} has been added.`
+ );
+ });
+
+ info("Checking that each of the presets and its preview have been added");
+ Object.keys(PRESETS).forEach(category => {
+ Object.keys(PRESETS[category]).forEach(presetLabel => {
+ const preset = container.querySelector("#" + presetLabel);
+ ok(preset, `${presetLabel} has been added`);
+ ok(
+ preset.querySelector("canvas"),
+ `${presetLabel}'s canvas preview has been added`
+ );
+ ok(preset.querySelector("p"), `${presetLabel}'s label has been added`);
+ });
+ });
+
+ w.destroy();
+ host.destroy();
+});
diff --git a/devtools/client/shared/test/browser_cubic-bezier-05.js b/devtools/client/shared/test/browser_cubic-bezier-05.js
new file mode 100644
index 0000000000..2e6659c07d
--- /dev/null
+++ b/devtools/client/shared/test/browser_cubic-bezier-05.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the CubicBezierPresetWidget cycles menus
+
+const {
+ CubicBezierPresetWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+const {
+ PREDEFINED,
+ PRESETS,
+ DEFAULT_PRESET_CATEGORY,
+} = require("resource://devtools/client/shared/widgets/CubicBezierPresets.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+
+add_task(async function () {
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#cubic-bezier-container");
+ const w = new CubicBezierPresetWidget(container);
+
+ info("Checking that preset is selected if coordinates are known");
+
+ w.refreshMenu([0, 0, 0, 0]);
+ is(
+ w.activeCategory,
+ container.querySelector(`#${DEFAULT_PRESET_CATEGORY}`),
+ "The default category is selected"
+ );
+ is(w._activePreset, null, "There is no selected category");
+
+ w.refreshMenu(PREDEFINED.linear);
+ is(
+ w.activeCategory,
+ container.querySelector("#ease-in-out"),
+ "The ease-in-out category is active"
+ );
+ is(
+ w._activePreset,
+ container.querySelector("#ease-in-out-linear"),
+ "The ease-in-out-linear preset is active"
+ );
+
+ w.refreshMenu(PRESETS["ease-out"]["ease-out-sine"]);
+ is(
+ w.activeCategory,
+ container.querySelector("#ease-out"),
+ "The ease-out category is active"
+ );
+ is(
+ w._activePreset,
+ container.querySelector("#ease-out-sine"),
+ "The ease-out-sine preset is active"
+ );
+
+ w.refreshMenu([0, 0, 0, 0]);
+ is(
+ w.activeCategory,
+ container.querySelector("#ease-out"),
+ "The ease-out category is still active"
+ );
+ is(w._activePreset, null, "No preset is active");
+
+ w.destroy();
+ host.destroy();
+});
diff --git a/devtools/client/shared/test/browser_cubic-bezier-06.js b/devtools/client/shared/test/browser_cubic-bezier-06.js
new file mode 100644
index 0000000000..9cb00e8bf7
--- /dev/null
+++ b/devtools/client/shared/test/browser_cubic-bezier-06.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the integration between CubicBezierWidget and CubicBezierPresets
+
+const {
+ CubicBezierWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+const {
+ PRESETS,
+} = require("resource://devtools/client/shared/widgets/CubicBezierPresets.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+
+add_task(async function () {
+ const { host, win, doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#cubic-bezier-container");
+ const w = new CubicBezierWidget(
+ container,
+ PRESETS["ease-in"]["ease-in-sine"]
+ );
+ w.presets.refreshMenu(PRESETS["ease-in"]["ease-in-sine"]);
+
+ const rect = w.curve.getBoundingClientRect();
+ rect.graphTop = rect.height * w.bezierCanvas.padding[0];
+
+ await adjustingBezierUpdatesPreset(w, win, doc, rect);
+ await selectingPresetUpdatesBezier(w, win, doc, rect);
+
+ w.destroy();
+ host.destroy();
+});
+
+function adjustingBezierUpdatesPreset(widget, win, doc, rect) {
+ info("Checking that changing the bezier refreshes the preset menu");
+
+ is(
+ widget.presets.activeCategory,
+ doc.querySelector("#ease-in"),
+ "The selected category is ease-in"
+ );
+
+ is(
+ widget.presets._activePreset,
+ doc.querySelector("#ease-in-sine"),
+ "The selected preset is ease-in-sine"
+ );
+
+ info("Generating custom bezier curve by dragging");
+ widget._onPointMouseDown({ target: widget.p1 });
+ doc.onmousemove({ pageX: rect.left, pageY: rect.graphTop });
+ doc.onmouseup();
+
+ is(
+ widget.presets.activeCategory,
+ doc.querySelector("#ease-in"),
+ "The selected category is still ease-in"
+ );
+
+ is(widget.presets._activePreset, null, "There is no active preset");
+}
+
+async function selectingPresetUpdatesBezier(widget, win, doc, rect) {
+ info("Checking that selecting a preset updates bezier curve");
+
+ info("Listening for the new coordinates event");
+ const onNewCoordinates = widget.presets.once("new-coordinates");
+ const onUpdated = widget.once("updated");
+
+ info("Click a preset");
+ const preset = doc.querySelector("#ease-in-sine");
+ widget.presets._onPresetClick({ currentTarget: preset });
+
+ await onNewCoordinates;
+ ok(true, "The preset widget fired the new-coordinates event");
+
+ const bezier = await onUpdated;
+ ok(true, "The bezier canvas fired the updated event");
+
+ is(
+ bezier.P1[0],
+ preset.coordinates[0],
+ "The new P1 time coordinate is correct"
+ );
+ is(
+ bezier.P1[1],
+ preset.coordinates[1],
+ "The new P1 progress coordinate is correct"
+ );
+ is(bezier.P2[0], preset.coordinates[2], "P2 time coordinate is correct ");
+ is(bezier.P2[1], preset.coordinates[3], "P2 progress coordinate is correct");
+}
diff --git a/devtools/client/shared/test/browser_cubic-bezier-07.js b/devtools/client/shared/test/browser_cubic-bezier-07.js
new file mode 100644
index 0000000000..2c7d81b2e3
--- /dev/null
+++ b/devtools/client/shared/test/browser_cubic-bezier-07.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that changing the cubic-bezier curve in the widget does change the dot animation
+// preview too.
+
+const {
+ CubicBezierWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+const {
+ PREDEFINED,
+} = require("resource://devtools/client/shared/widgets/CubicBezierPresets.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_cubic-bezier-01.html";
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("ui.prefersReducedMotion");
+});
+
+add_task(async function () {
+ const { host, doc } = await createHost("bottom", TEST_URI);
+ // Unset "prefers reduced motion", otherwise the dot animation preview won't be created.
+ // See Bug 1637842
+ // https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion
+ await pushPref("ui.prefersReducedMotion", 0);
+
+ const container = doc.querySelector("#cubic-bezier-container");
+ const w = new CubicBezierWidget(container, PREDEFINED.linear);
+
+ await previewDotReactsToChanges(w, [0.6, -0.28, 0.74, 0.05]);
+ await previewDotReactsToChanges(w, [0.9, 0.03, 0.69, 0.22]);
+ await previewDotReactsToChanges(w, [0.68, -0.55, 0.27, 1.55]);
+ await previewDotReactsToChanges(w, PREDEFINED.ease, "ease");
+ await previewDotReactsToChanges(w, PREDEFINED["ease-in-out"], "ease-in-out");
+
+ w.destroy();
+ host.destroy();
+});
+
+async function previewDotReactsToChanges(widget, coords, expectedEasing) {
+ const onUpdated = widget.once("updated");
+ widget.coordinates = coords;
+ await onUpdated;
+
+ const animatedDot = widget.timingPreview.dot;
+ const animations = animatedDot.getAnimations();
+
+ if (!expectedEasing) {
+ expectedEasing = `cubic-bezier(${coords[0]}, ${coords[1]}, ${coords[2]}, ${coords[3]})`;
+ }
+
+ is(animations.length, 1, "The dot is animated");
+
+ const goingToRight = animations[0].effect.getKeyframes()[2];
+ is(
+ goingToRight.easing,
+ expectedEasing,
+ `The easing when going to the right was set correctly to ${coords}`
+ );
+
+ const goingToLeft = animations[0].effect.getKeyframes()[6];
+ is(
+ goingToLeft.easing,
+ expectedEasing,
+ `The easing when going to the left was set correctly to ${coords}`
+ );
+}
diff --git a/devtools/client/shared/test/browser_dbg_globalactor.js b/devtools/client/shared/test/browser_dbg_globalactor.js
new file mode 100644
index 0000000000..71cda04b47
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_globalactor.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Check extension-added global actor API.
+ */
+
+"use strict";
+
+var {
+ DevToolsServer,
+} = require("resource://devtools/server/devtools-server.js");
+var {
+ ActorRegistry,
+} = require("resource://devtools/server/actors/utils/actor-registry.js");
+var {
+ DevToolsClient,
+} = require("resource://devtools/client/devtools-client.js");
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
+
+add_task(async function () {
+ DevToolsServer.init();
+ DevToolsServer.registerAllActors();
+
+ ActorRegistry.registerModule(ACTORS_URL, {
+ prefix: "testOne",
+ constructor: "TestActor1",
+ type: { global: true },
+ });
+
+ const transport = DevToolsServer.connectPipe();
+ const client = new DevToolsClient(transport);
+ const [type] = await client.connect();
+ is(type, "browser", "Root actor should identify itself as a browser.");
+
+ let response = await client.mainRoot.rootForm;
+ const globalActor = response.testOneActor;
+ ok(globalActor, "Found the test global actor.");
+ ok(
+ globalActor.includes("testOne"),
+ "testGlobalActor1's typeName should be used."
+ );
+
+ response = await client.request({ to: globalActor, type: "ping" });
+ is(response.pong, "pong", "Actor should respond to requests.");
+
+ // Send another ping to see if the same actor is used.
+ response = await client.request({ to: globalActor, type: "ping" });
+ is(response.pong, "pong", "Actor should respond to requests.");
+
+ // Make sure that lazily-created actors are created only once.
+ let count = 0;
+ for (const connID of Object.getOwnPropertyNames(
+ DevToolsServer._connections
+ )) {
+ const conn = DevToolsServer._connections[connID];
+ const computedPrefix = conn._prefix + "testOne";
+ for (const pool of conn._extraPools) {
+ for (const actor of pool.poolChildren()) {
+ if (actor.actorID.startsWith(computedPrefix)) {
+ count++;
+ }
+ }
+ }
+ }
+
+ is(count, 1, "Only one actor exists in all pools. One global actor.");
+
+ await client.close();
+});
diff --git a/devtools/client/shared/test/browser_dbg_listaddons.js b/devtools/client/shared/test/browser_dbg_listaddons.js
new file mode 100644
index 0000000000..d6d302db34
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_listaddons.js
@@ -0,0 +1,137 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var {
+ DevToolsServer,
+} = require("resource://devtools/server/devtools-server.js");
+var {
+ DevToolsClient,
+} = require("resource://devtools/client/devtools-client.js");
+
+/**
+ * Make sure the listAddons request works as specified.
+ */
+const ADDON1_ID = "test-addon-1@mozilla.org";
+const ADDON1_PATH = "addons/test-addon-1/";
+const ADDON2_ID = "test-addon-2@mozilla.org";
+const ADDON2_PATH = "addons/test-addon-2/";
+
+add_task(async function () {
+ DevToolsServer.init();
+ DevToolsServer.registerAllActors();
+
+ const transport = DevToolsServer.connectPipe();
+ const client = new DevToolsClient(transport);
+
+ const [type] = await client.connect();
+ is(type, "browser", "Root actor should identify itself as a browser.");
+
+ let addonListChangedEvents = 0;
+ client.mainRoot.on("addonListChanged", () => addonListChangedEvents++);
+ const addons = await client.mainRoot.getFront("addons");
+
+ const addon1 = await addTemporaryAddon({
+ addons,
+ path: ADDON1_PATH,
+ openDevTools: false,
+ });
+ const addonFront1 = await client.mainRoot.getAddon({ id: ADDON1_ID });
+ is(addonListChangedEvents, 0, "Should not receive addonListChanged yet.");
+ ok(addonFront1, "Should find an addon actor for addon1.");
+
+ const addon2 = await addTemporaryAddon({
+ addons,
+ path: ADDON2_PATH,
+ openDevTools: true,
+ });
+ const front1AfterAddingAddon2 = await client.mainRoot.getAddon({
+ id: ADDON1_ID,
+ });
+ const addonFront2 = await client.mainRoot.getAddon({ id: ADDON2_ID });
+
+ is(
+ addonListChangedEvents,
+ 1,
+ "Should have received an addonListChanged event."
+ );
+ ok(addonFront2, "Should find an addon actor for addon2.");
+ is(
+ addonFront1,
+ front1AfterAddingAddon2,
+ "Front for addon1 should be the same"
+ );
+
+ await removeAddon(addon1);
+ const front1AfterRemove = await client.mainRoot.getAddon({ id: ADDON1_ID });
+ is(
+ addonListChangedEvents,
+ 2,
+ "Should have received an addonListChanged event."
+ );
+ ok(!front1AfterRemove, "Should no longer get a front for addon1");
+
+ await removeAddon(addon2);
+ const front2AfterRemove = await client.mainRoot.getAddon({ id: ADDON2_ID });
+ is(
+ addonListChangedEvents,
+ 3,
+ "Should have received an addonListChanged event."
+ );
+ ok(!front2AfterRemove, "Should no longer get a front for addon1");
+
+ // Check behavior when openDevTools is not passed:
+ const addon2again = await addTemporaryAddon({
+ addons,
+ path: ADDON2_PATH,
+ // openDevTools: null,
+ });
+ const addonFront2again = await client.mainRoot.getAddon({ id: ADDON2_ID });
+ ok(addonFront2again, "Should find an addon actor for addon2.");
+ is(addonListChangedEvents, 4, "Should have seen addonListChanged.");
+ await removeAddon(addon2again);
+ is(addonListChangedEvents, 5, "Should have seen addonListChanged.");
+
+ await client.close();
+});
+
+async function addTemporaryAddon({ addons, path, openDevTools }) {
+ const addonFilePath = getTestFilePath(path);
+ info("Installing addon: " + addonFilePath);
+
+ const onToolboxReady = gDevTools.once("toolbox-ready");
+ const { id } = await addons.installTemporaryAddon(
+ addonFilePath,
+ openDevTools
+ );
+
+ if (openDevTools) {
+ info("Wait for toolbox to be opened");
+ const toolbox = await onToolboxReady;
+ ok(true, "Toolbox was opened when openDevTools option was true");
+ info("Destroying this toolbox");
+ await toolbox.destroy();
+ }
+
+ return AddonManager.getAddonByID(id);
+}
+
+function removeAddon(addon) {
+ return new Promise(resolve => {
+ info("Removing addon.");
+
+ const listener = {
+ onUninstalled(uninstalledAddon) {
+ if (uninstalledAddon != addon) {
+ return;
+ }
+ AddonManager.removeAddonListener(listener);
+ resolve();
+ },
+ };
+
+ AddonManager.addAddonListener(listener);
+ addon.uninstall();
+ });
+}
diff --git a/devtools/client/shared/test/browser_dbg_listtabs-01.js b/devtools/client/shared/test/browser_dbg_listtabs-01.js
new file mode 100644
index 0000000000..e804a6b91c
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_listtabs-01.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Make sure the listTabs request works as specified.
+ */
+
+var {
+ DevToolsServer,
+} = require("resource://devtools/server/devtools-server.js");
+var {
+ DevToolsClient,
+} = require("resource://devtools/client/devtools-client.js");
+
+const TAB1_URL = EXAMPLE_URL + "doc_empty-tab-01.html";
+const TAB2_URL = EXAMPLE_URL + "doc_empty-tab-02.html";
+
+add_task(async function test() {
+ DevToolsServer.init();
+ DevToolsServer.registerAllActors();
+
+ const transport = DevToolsServer.connectPipe();
+ const client = new DevToolsClient(transport);
+ const [aType] = await client.connect();
+ is(aType, "browser", "Root actor should identify itself as a browser.");
+
+ const firstTab = await testFirstTab(client);
+ const secondTab = await testSecondTab(client, firstTab.front);
+ await testRemoveTab(client, firstTab.tab);
+ await testAttachRemovedTab(secondTab.tab, secondTab.front);
+ await client.close();
+});
+
+async function testFirstTab(client) {
+ const tab = await addTab(TAB1_URL);
+
+ const front = await getDescriptorActorForUrl(client, TAB1_URL);
+ ok(front, "Should find a target actor for the first tab.");
+ return { tab, front };
+}
+
+async function testSecondTab(client, firstTabFront) {
+ const tab = await addTab(TAB2_URL);
+
+ const firstFront = await getDescriptorActorForUrl(client, TAB1_URL);
+ const secondFront = await getDescriptorActorForUrl(client, TAB2_URL);
+ is(firstFront, firstTabFront, "First tab's actor shouldn't have changed.");
+ ok(secondFront, "Should find a target actor for the second tab.");
+ return { tab, front: secondFront };
+}
+
+async function testRemoveTab(client, firstTab) {
+ await removeTab(firstTab);
+ const front = await getDescriptorActorForUrl(client, TAB1_URL);
+ ok(!front, "Shouldn't find a target actor for the first tab anymore.");
+}
+
+async function testAttachRemovedTab(secondTab, secondTabFront) {
+ await removeTab(secondTab);
+
+ const { actorID } = secondTabFront;
+ try {
+ await secondTabFront.getFavicon({});
+ ok(
+ false,
+ "any request made to the descriptor for a closed tab should have failed"
+ );
+ } catch (error) {
+ ok(
+ error.message.includes(
+ `Connection closed, pending request to ${actorID}, type getFavicon failed`
+ ),
+ "Actor is gone since the tab was removed."
+ );
+ }
+}
+
+async function getDescriptorActorForUrl(client, url) {
+ const tabDescriptors = await client.mainRoot.listTabs();
+ const tabDescriptor = tabDescriptors.find(front => front.url == url);
+ return tabDescriptor;
+}
diff --git a/devtools/client/shared/test/browser_dbg_listtabs-02.js b/devtools/client/shared/test/browser_dbg_listtabs-02.js
new file mode 100644
index 0000000000..a23981a93b
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_listtabs-02.js
@@ -0,0 +1,248 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Make sure the root actor's live tab list implementation works as specified.
+ */
+
+var {
+ BrowserTabList,
+} = require("resource://devtools/server/actors/webbrowser.js");
+var {
+ DevToolsServer,
+} = require("resource://devtools/server/devtools-server.js");
+
+var gTestPage =
+ "data:text/html;charset=utf-8," +
+ encodeURIComponent(
+ "<title>JS Debugger BrowserTabList test page</title><body>Yo.</body>"
+ );
+
+// The tablist object whose behavior we observe.
+var gTabList;
+var gFirstActor, gActorA;
+var gTabA, gTabB, gTabC;
+var gNewWindow;
+
+// Stock onListChanged handler.
+var onListChangedCount = 0;
+function onListChangedHandler() {
+ onListChangedCount++;
+}
+
+function test() {
+ DevToolsServer.init();
+ DevToolsServer.registerAllActors();
+
+ gTabList = new BrowserTabList("fake DevToolsServerConnection");
+ gTabList._testing = true;
+ gTabList.onListChanged = onListChangedHandler;
+
+ checkSingleTab()
+ .then(addTabA)
+ .then(testTabA)
+ .then(addTabB)
+ .then(testTabB)
+ .then(removeTabA)
+ .then(testTabClosed)
+ .then(addTabC)
+ .then(testTabC)
+ .then(removeTabC)
+ .then(testNewWindow)
+ .then(removeNewWindow)
+ .then(testWindowClosed)
+ .then(removeTabB)
+ .then(checkSingleTab)
+ .then(finishUp);
+}
+
+function checkSingleTab() {
+ return gTabList.getList().then(targetActors => {
+ is(targetActors.length, 1, "initial tab list: contains initial tab");
+ gFirstActor = targetActors[0];
+ is(
+ gFirstActor.url,
+ "about:blank",
+ "initial tab list: initial tab URL is 'about:blank'"
+ );
+ is(
+ gFirstActor.title,
+ "New Tab",
+ "initial tab list: initial tab title is 'New Tab'"
+ );
+ });
+}
+
+function addTabA() {
+ return addTab(gTestPage).then(tab => {
+ gTabA = tab;
+ });
+}
+
+function testTabA() {
+ is(onListChangedCount, 1, "onListChanged handler call count");
+
+ return gTabList.getList().then(targetActors => {
+ targetActors = new Set(targetActors);
+ is(targetActors.size, 2, "gTabA opened: two tabs in list");
+ ok(targetActors.has(gFirstActor), "gTabA opened: initial tab present");
+
+ info("actors: " + [...targetActors].map(a => a.url));
+ gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
+ ok(gActorA.url.match(/^data:text\/html;/), "gTabA opened: new tab URL");
+ is(
+ gActorA.title,
+ "JS Debugger BrowserTabList test page",
+ "gTabA opened: new tab title"
+ );
+ });
+}
+
+function addTabB() {
+ return addTab(gTestPage).then(tab => {
+ gTabB = tab;
+ });
+}
+
+function testTabB() {
+ is(onListChangedCount, 2, "onListChanged handler call count");
+
+ return gTabList.getList().then(targetActors => {
+ targetActors = new Set(targetActors);
+ is(targetActors.size, 3, "gTabB opened: three tabs in list");
+ });
+}
+
+function removeTabA() {
+ return new Promise(resolve => {
+ once(gBrowser.tabContainer, "TabClose").then(event => {
+ ok(!event.detail.adoptedBy, "This was a normal tab close");
+
+ // Let the actor's TabClose handler finish first.
+ executeSoon(resolve);
+ }, false);
+
+ removeTab(gTabA);
+ });
+}
+
+function testTabClosed() {
+ is(onListChangedCount, 3, "onListChanged handler call count");
+
+ gTabList.getList().then(targetActors => {
+ targetActors = new Set(targetActors);
+ is(targetActors.size, 2, "gTabA closed: two tabs in list");
+ ok(targetActors.has(gFirstActor), "gTabA closed: initial tab present");
+
+ info("actors: " + [...targetActors].map(a => a.url));
+ gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
+ ok(gActorA.url.match(/^data:text\/html;/), "gTabA closed: new tab URL");
+ is(
+ gActorA.title,
+ "JS Debugger BrowserTabList test page",
+ "gTabA closed: new tab title"
+ );
+ });
+}
+
+function addTabC() {
+ return addTab(gTestPage).then(tab => {
+ gTabC = tab;
+ });
+}
+
+function testTabC() {
+ is(onListChangedCount, 4, "onListChanged handler call count");
+
+ gTabList.getList().then(targetActors => {
+ targetActors = new Set(targetActors);
+ is(targetActors.size, 3, "gTabC opened: three tabs in list");
+ });
+}
+
+function removeTabC() {
+ return new Promise(resolve => {
+ once(gBrowser.tabContainer, "TabClose").then(event => {
+ ok(event.detail.adoptedBy, "This was a tab closed by moving");
+
+ // Let the actor's TabClose handler finish first.
+ executeSoon(resolve);
+ }, false);
+
+ gNewWindow = gBrowser.replaceTabWithWindow(gTabC);
+ });
+}
+
+function testNewWindow() {
+ is(onListChangedCount, 5, "onListChanged handler call count");
+
+ return gTabList.getList().then(targetActors => {
+ targetActors = new Set(targetActors);
+ is(targetActors.size, 3, "gTabC closed: three tabs in list");
+ ok(targetActors.has(gFirstActor), "gTabC closed: initial tab present");
+
+ info("actors: " + [...targetActors].map(a => a.url));
+ gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
+ ok(gActorA.url.match(/^data:text\/html;/), "gTabC closed: new tab URL");
+ is(
+ gActorA.title,
+ "JS Debugger BrowserTabList test page",
+ "gTabC closed: new tab title"
+ );
+ });
+}
+
+function removeNewWindow() {
+ return new Promise(resolve => {
+ once(gNewWindow, "unload").then(event => {
+ ok(!event.detail, "This was a normal window close");
+
+ // Let the actor's TabClose handler finish first.
+ executeSoon(resolve);
+ }, false);
+
+ gNewWindow.close();
+ });
+}
+
+function testWindowClosed() {
+ is(onListChangedCount, 6, "onListChanged handler call count");
+
+ return gTabList.getList().then(targetActors => {
+ targetActors = new Set(targetActors);
+ is(targetActors.size, 2, "gNewWindow closed: two tabs in list");
+ ok(targetActors.has(gFirstActor), "gNewWindow closed: initial tab present");
+
+ info("actors: " + [...targetActors].map(a => a.url));
+ gActorA = [...targetActors].filter(a => a !== gFirstActor)[0];
+ ok(
+ gActorA.url.match(/^data:text\/html;/),
+ "gNewWindow closed: new tab URL"
+ );
+ is(
+ gActorA.title,
+ "JS Debugger BrowserTabList test page",
+ "gNewWindow closed: new tab title"
+ );
+ });
+}
+
+function removeTabB() {
+ return new Promise(resolve => {
+ once(gBrowser.tabContainer, "TabClose").then(event => {
+ ok(!event.detail.adoptedBy, "This was a normal tab close");
+
+ // Let the actor's TabClose handler finish first.
+ executeSoon(resolve);
+ }, false);
+
+ removeTab(gTabB);
+ });
+}
+
+function finishUp() {
+ gTabList = gFirstActor = gActorA = gTabA = gTabB = gTabC = gNewWindow = null;
+ finish();
+}
diff --git a/devtools/client/shared/test/browser_dbg_listworkers.js b/devtools/client/shared/test/browser_dbg_listworkers.js
new file mode 100644
index 0000000000..fcdcf8e5dd
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_listworkers.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+var TAB_URL = EXAMPLE_URL + "doc_listworkers-tab.html";
+var WORKER1_URL = "code_listworkers-worker1.js";
+var WORKER2_URL = "code_listworkers-worker2.js";
+
+add_task(async function test() {
+ const tab = await addTab(TAB_URL);
+ const target = await createAndAttachTargetForTab(tab);
+
+ let { workers } = await listWorkers(target);
+ is(workers.length, 0);
+
+ let onWorkerListChanged = waitForWorkerListChanged(target);
+ await SpecialPowers.spawn(tab.linkedBrowser, [WORKER1_URL], workerUrl => {
+ content.worker1 = new content.Worker(workerUrl);
+ });
+ await onWorkerListChanged;
+
+ ({ workers } = await listWorkers(target));
+ is(workers.length, 1);
+ is(workers[0].url, WORKER1_URL);
+
+ onWorkerListChanged = waitForWorkerListChanged(target);
+ await SpecialPowers.spawn(tab.linkedBrowser, [WORKER2_URL], workerUrl => {
+ content.worker2 = new content.Worker(workerUrl);
+ });
+ await onWorkerListChanged;
+
+ ({ workers } = await listWorkers(target));
+ is(workers.length, 2);
+ is(workers[0].url, WORKER1_URL);
+ is(workers[1].url, WORKER2_URL);
+
+ onWorkerListChanged = waitForWorkerListChanged(target);
+ await SpecialPowers.spawn(tab.linkedBrowser, [WORKER2_URL], workerUrl => {
+ content.worker1.terminate();
+ });
+ await onWorkerListChanged;
+
+ ({ workers } = await listWorkers(target));
+ is(workers.length, 1);
+ is(workers[0].url, WORKER2_URL);
+
+ onWorkerListChanged = waitForWorkerListChanged(target);
+ await SpecialPowers.spawn(tab.linkedBrowser, [WORKER2_URL], workerUrl => {
+ content.worker2.terminate();
+ });
+ await onWorkerListChanged;
+
+ ({ workers } = await listWorkers(target));
+ is(workers.length, 0);
+
+ await target.destroy();
+ finish();
+});
+
+function listWorkers(targetFront) {
+ info("Listing workers.");
+ return targetFront.listWorkers();
+}
+
+function waitForWorkerListChanged(targetFront) {
+ info("Waiting for worker list to change.");
+ return targetFront.once("workerListChanged");
+}
diff --git a/devtools/client/shared/test/browser_dbg_multiple-windows.js b/devtools/client/shared/test/browser_dbg_multiple-windows.js
new file mode 100644
index 0000000000..2e2013479c
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_multiple-windows.js
@@ -0,0 +1,122 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Make sure that the debugger attaches to the right tab when multiple windows
+ * are open.
+ */
+
+var {
+ DevToolsServer,
+} = require("resource://devtools/server/devtools-server.js");
+var {
+ DevToolsClient,
+} = require("resource://devtools/client/devtools-client.js");
+
+const TAB1_URL = "data:text/html;charset=utf-8,first-tab";
+const TAB2_URL = "data:text/html;charset=utf-8,second-tab";
+
+add_task(async function () {
+ DevToolsServer.init();
+ DevToolsServer.registerAllActors();
+
+ const transport = DevToolsServer.connectPipe();
+ const client = new DevToolsClient(transport);
+ const [type] = await client.connect();
+ is(type, "browser", "Root actor should identify itself as a browser.");
+
+ const tab = await addTab(TAB1_URL);
+ await testFirstTab(client, tab);
+ const win = await addWindow(TAB2_URL);
+ await testNewWindow(client, win);
+ testFocusFirst(client);
+ await testRemoveTab(client, win, tab);
+ await client.close();
+});
+
+async function testFirstTab(client, tab) {
+ ok(!!tab, "Second tab created.");
+
+ const tabs = await client.mainRoot.listTabs();
+ const targetFront = tabs.find(grip => grip.url == TAB1_URL);
+ ok(targetFront, "Should find a target actor for the first tab.");
+
+ ok(!tabs[0].selected, "The previously opened tab isn't selected.");
+ ok(tabs[1].selected, "The first tab is selected.");
+}
+
+async function testNewWindow(client, win) {
+ ok(!!win, "Second window created.");
+
+ win.focus();
+
+ const topWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ is(topWindow, win, "The second window is on top.");
+
+ if (Services.focus.activeWindow != win) {
+ await new Promise(resolve => {
+ win.addEventListener(
+ "activate",
+ function onActivate(event) {
+ if (event.target != win) {
+ return;
+ }
+ win.removeEventListener("activate", onActivate, true);
+ resolve();
+ },
+ true
+ );
+ });
+ }
+
+ const tabs = await client.mainRoot.listTabs();
+ ok(!tabs[0].selected, "The previously opened tab isn't selected.");
+ ok(!tabs[1].selected, "The first tab isn't selected.");
+ ok(tabs[2].selected, "The second tab is selected.");
+}
+
+async function testFocusFirst(client) {
+ const tab = window.gBrowser.selectedTab;
+ await ContentTask.spawn(tab.linkedBrowser, null, async function () {
+ const onFocus = new Promise(resolve => {
+ content.addEventListener("focus", resolve, { once: true });
+ });
+ await onFocus;
+ });
+
+ const tabs = await client.mainRoot.listTabs();
+ ok(!tabs[0].selected, "The previously opened tab isn't selected.");
+ ok(!tabs[1].selected, "The first tab is selected after focusing on i.");
+ ok(tabs[2].selected, "The second tab isn't selected.");
+}
+
+async function testRemoveTab(client, win, tab) {
+ win.close();
+
+ // give it time to close
+ await new Promise(resolve => executeSoon(resolve));
+ await continue_remove_tab(client, tab);
+}
+
+async function continue_remove_tab(client, tab) {
+ removeTab(tab);
+
+ const tabs = await client.mainRoot.listTabs();
+
+ // Verify that tabs are no longer included in listTabs.
+ const foundTab1 = tabs.some(grip => grip.url == TAB1_URL);
+ const foundTab2 = tabs.some(grip => grip.url == TAB2_URL);
+ ok(!foundTab1, "Tab1 should be gone.");
+ ok(!foundTab2, "Tab2 should be gone.");
+
+ ok(tabs[0].selected, "The previously opened tab is selected.");
+}
+
+async function addWindow(url) {
+ info("Adding window: " + url);
+ const onNewWindow = BrowserTestUtils.waitForNewWindow({ url });
+ window.open(url, "_blank", "noopener");
+ return onNewWindow;
+}
diff --git a/devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js b/devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js
new file mode 100644
index 0000000000..004c7bbc9d
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_target-scoped-actor-01.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check target-scoped actor lifetimes.
+ */
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
+const TAB_URL = TEST_URI_ROOT + "doc_empty-tab-01.html";
+
+add_task(async function test() {
+ const tab = await addTab(TAB_URL);
+
+ await registerActorInContentProcess(ACTORS_URL, {
+ prefix: "testOne",
+ constructor: "TestActor1",
+ type: { target: true },
+ });
+
+ const target = await createAndAttachTargetForTab(tab);
+ const { client } = target;
+ const form = target.targetForm;
+
+ await testTargetScopedActor(client, form);
+ await removeTab(gBrowser.selectedTab);
+ await target.destroy();
+});
+
+async function testTargetScopedActor(client, form) {
+ ok(form.testOneActor, "Found the test target-scoped actor.");
+ ok(
+ form.testOneActor.includes("testOne"),
+ "testOneActor's typeName should be used."
+ );
+
+ const response = await client.request({
+ to: form.testOneActor,
+ type: "ping",
+ });
+ is(response.pong, "pong", "Actor should respond to requests.");
+}
diff --git a/devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js b/devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js
new file mode 100644
index 0000000000..c87eda05e0
--- /dev/null
+++ b/devtools/client/shared/test/browser_dbg_target-scoped-actor-02.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check target-scoped actor lifetimes.
+ */
+
+const ACTORS_URL = EXAMPLE_URL + "testactors.js";
+const TAB_URL = TEST_URI_ROOT + "doc_empty-tab-01.html";
+
+add_task(async function () {
+ const tab = await addTab(TAB_URL);
+
+ await registerActorInContentProcess(ACTORS_URL, {
+ prefix: "testOne",
+ constructor: "TestActor1",
+ type: { target: true },
+ });
+
+ const target = await createAndAttachTargetForTab(tab);
+ const { client } = target;
+ const form = target.targetForm;
+
+ await testTargetScopedActor(client, form);
+ await closeTab(client, form);
+ await target.destroy();
+});
+
+async function testTargetScopedActor(client, form) {
+ ok(form.testOneActor, "Found the test target-scoped actor.");
+ ok(
+ form.testOneActor.includes("testOne"),
+ "testOneActor's typeName should be used."
+ );
+
+ const response = await client.request({
+ to: form.testOneActor,
+ type: "ping",
+ });
+ is(response.pong, "pong", "Actor should respond to requests.");
+}
+
+async function closeTab(client, form) {
+ // We need to start listening for the rejection before removing the tab
+ /* eslint-disable-next-line mozilla/rejects-requires-await*/
+ const onReject = Assert.rejects(
+ client.request({ to: form.testOneActor, type: "ping" }),
+ err =>
+ err.message ===
+ `'ping' active request packet to '${form.testOneActor}' ` +
+ `can't be sent as the connection just closed.`,
+ "testOneActor went away."
+ );
+ await removeTab(gBrowser.selectedTab);
+ await onReject;
+}
diff --git a/devtools/client/shared/test/browser_devices.js b/devtools/client/shared/test/browser_devices.js
new file mode 100644
index 0000000000..35a68d2714
--- /dev/null
+++ b/devtools/client/shared/test/browser_devices.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+ getDevices,
+ getDeviceString,
+ addDevice,
+} = require("resource://devtools/client/shared/devices.js");
+
+add_task(async function () {
+ let devices = await getDevices();
+
+ let types = [...devices.keys()];
+ ok(!!types.length, `Found ${types.length} device types.`);
+
+ for (const type of types) {
+ const string = getDeviceString(type);
+ ok(
+ typeof string === "string" &&
+ !!string.length &&
+ string != `device.${type}`,
+ `Able to localize "${type}": "${string}"`
+ );
+
+ ok(
+ !!devices.get(type).length,
+ `Found ${devices.get(type).length} ${type} devices`
+ );
+ }
+
+ const type1 = types[0];
+ const type1DeviceCount = devices.get(type1).length;
+
+ const device1 = {
+ name: "SquarePhone",
+ width: 320,
+ height: 320,
+ pixelRatio: 2,
+ userAgent: "Mozilla/5.0 (Mobile; rv:42.0)",
+ touch: true,
+ firefoxOS: true,
+ };
+ addDevice(device1, types[0]);
+ devices = await getDevices();
+
+ is(
+ devices.get(type1).length,
+ type1DeviceCount + 1,
+ `Added new device of type "${type1}".`
+ );
+ ok(
+ devices.get(type1).find(d => d.name === device1.name),
+ "Found the new device."
+ );
+
+ const type2 = "appliances";
+ const device2 = {
+ name: "Mr Freezer",
+ width: 800,
+ height: 600,
+ pixelRatio: 5,
+ userAgent: "Mozilla/5.0 (Appliance; rv:42.0)",
+ touch: true,
+ firefoxOS: true,
+ };
+
+ const typeCount = types.length;
+ addDevice(device2, type2);
+ devices = await getDevices();
+ types = [...devices.keys()];
+
+ is(types.length, typeCount + 1, `Added device type "${type2}".`);
+ is(devices.get(type2).length, 1, `Added new "${type2}" device`);
+});
diff --git a/devtools/client/shared/test/browser_filter-editor-01.js b/devtools/client/shared/test/browser_filter-editor-01.js
new file mode 100644
index 0000000000..557a02857c
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-01.js
@@ -0,0 +1,150 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the Filter Editor Widget parses filter values correctly (setCssValue)
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+const { getCSSLexer } = require("resource://devtools/shared/css/lexer.js");
+
+// Verify that the given string consists of a valid CSS URL token.
+// Return true on success, false on error.
+function verifyURL(string) {
+ const lexer = getCSSLexer(string);
+
+ const token = lexer.nextToken();
+ if (!token || token.tokenType !== "url") {
+ return false;
+ }
+
+ return lexer.nextToken() === null;
+}
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(container, "none");
+
+ info("Test parsing of a valid CSS Filter value");
+ widget.setCssValue("blur(2px) contrast(200%)");
+ is(
+ widget.getCssValue(),
+ "blur(2px) contrast(200%)",
+ "setCssValue should work for computed values"
+ );
+
+ info("Test parsing of space-filled value");
+ widget.setCssValue("blur( 2px ) contrast( 2 )");
+ is(
+ widget.getCssValue(),
+ "blur(2px) contrast(200%)",
+ "setCssValue should work for spaced values"
+ );
+
+ info("Test parsing of string-typed values");
+ widget.setCssValue(
+ "drop-shadow( 2px 1px 5px black) url( example.svg#filter )"
+ );
+
+ is(
+ widget.getCssValue(),
+ "drop-shadow(2px 1px 5px black) url(example.svg#filter)",
+ "setCssValue should work for string-typed values"
+ );
+
+ info("Test parsing of mixed-case function names");
+ widget.setCssValue("BLUR(2px) Contrast(200%) Drop-Shadow(2px 1px 5px Black)");
+ is(
+ widget.getCssValue(),
+ "BLUR(2px) Contrast(200%) Drop-Shadow(2px 1px 5px Black)",
+ "setCssValue should work for mixed-case function names"
+ );
+
+ info("Test parsing of invalid filter value");
+ widget.setCssValue("totallyinvalid");
+ is(
+ widget.getCssValue(),
+ "none",
+ "setCssValue should turn completely invalid value to 'none'"
+ );
+
+ info("Test parsing of invalid function argument");
+ widget.setCssValue("blur('hello')");
+ is(
+ widget.getCssValue(),
+ "blur(0px)",
+ "setCssValue should replace invalid function argument with default"
+ );
+
+ info("Test parsing of invalid function argument #2");
+ widget.setCssValue("drop-shadow(whatever)");
+ is(
+ widget.getCssValue(),
+ "drop-shadow()",
+ "setCssValue should replace invalid drop-shadow argument with empty string"
+ );
+
+ info("Test parsing of mixed invalid argument");
+ widget.setCssValue("contrast(5%) whatever invert('xxx')");
+ is(
+ widget.getCssValue(),
+ "contrast(5%) invert(0%)",
+ "setCssValue should handle multiple errors"
+ );
+
+ info("Test parsing of 'unset'");
+ widget.setCssValue("unset");
+ is(widget.getCssValue(), "unset", "setCssValue should handle 'unset'");
+ info("Test parsing of 'initial'");
+ widget.setCssValue("initial");
+ is(widget.getCssValue(), "initial", "setCssValue should handle 'initial'");
+ info("Test parsing of 'inherit'");
+ widget.setCssValue("inherit");
+ is(widget.getCssValue(), "inherit", "setCssValue should handle 'inherit'");
+
+ info("Test parsing of quoted URL");
+ widget.setCssValue("url('invalid ) when ) unquoted')");
+ is(
+ widget.getCssValue(),
+ "url('invalid ) when ) unquoted')",
+ "setCssValue should re-quote single-quoted URL contents"
+ );
+ widget.setCssValue('url("invalid ) when ) unquoted")');
+ is(
+ widget.getCssValue(),
+ 'url("invalid ) when ) unquoted")',
+ "setCssValue should re-quote double-quoted URL contents"
+ );
+ widget.setCssValue("url(ordinary)");
+ is(
+ widget.getCssValue(),
+ "url(ordinary)",
+ "setCssValue should not quote ordinary unquoted URL contents"
+ );
+
+ const quotedurl =
+ "url(invalid\\ \\)\\ {\\\twhen\\ }\\ ;\\ \\\\unquoted\\'\\\")";
+ ok(verifyURL(quotedurl), "weird URL is valid");
+ widget.setCssValue(quotedurl);
+ is(
+ widget.getCssValue(),
+ quotedurl,
+ "setCssValue should re-quote weird unquoted URL contents"
+ );
+
+ const dataurl =
+ "url(data:image/svg+xml;utf8,<svg\\ " +
+ 'xmlns=\\"http://www.w3.org/2000/svg\\"><filter\\ id=\\"blur\\">' +
+ '<feGaussianBlur\\ stdDeviation=\\"3\\"/></filter></svg>#blur)';
+ ok(verifyURL(dataurl), "data URL is valid");
+ widget.setCssValue(dataurl);
+ is(widget.getCssValue(), dataurl, "setCssValue should not mangle data urls");
+
+ widget.destroy();
+});
diff --git a/devtools/client/shared/test/browser_filter-editor-02.js b/devtools/client/shared/test/browser_filter-editor-02.js
new file mode 100644
index 0000000000..2a69f5265f
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-02.js
@@ -0,0 +1,114 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the Filter Editor Widget renders filters correctly
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const STRINGS_URI = "devtools/client/locales/filterwidget.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const TEST_DATA = [
+ {
+ cssValue:
+ "blur(2px) contrast(200%) hue-rotate(20.2deg) drop-shadow(5px 5px black)",
+ expected: [
+ {
+ label: "blur",
+ value: "2",
+ unit: "px",
+ },
+ {
+ label: "contrast",
+ value: "200",
+ unit: "%",
+ },
+ {
+ label: "hue-rotate",
+ value: "20.2",
+ unit: "deg",
+ },
+ {
+ label: "drop-shadow",
+ value: "5px 5px black",
+ unit: null,
+ },
+ ],
+ },
+ {
+ cssValue: "hue-rotate(420.2deg)",
+ expected: [
+ {
+ label: "hue-rotate",
+ value: "420.2",
+ unit: "deg",
+ },
+ ],
+ },
+ {
+ cssValue: "url(example.svg)",
+ expected: [
+ {
+ label: "url",
+ value: "example.svg",
+ unit: null,
+ },
+ ],
+ },
+ {
+ cssValue: "none",
+ expected: [],
+ },
+ ];
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(container, "none");
+
+ info("Test rendering of different types");
+
+ for (const { cssValue, expected } of TEST_DATA) {
+ widget.setCssValue(cssValue);
+
+ if (cssValue === "none") {
+ const text = container.querySelector("#filters").textContent;
+ Assert.greater(
+ text.indexOf(L10N.getStr("emptyFilterList")),
+ -1,
+ "Contains |emptyFilterList| string when given value 'none'"
+ );
+ Assert.greater(
+ text.indexOf(L10N.getStr("addUsingList")),
+ -1,
+ "Contains |addUsingList| string when given value 'none'"
+ );
+ continue;
+ }
+ const filters = container.querySelectorAll(".filter");
+ testRenderedFilters(filters, expected);
+ }
+ widget.destroy();
+});
+
+function testRenderedFilters(filters, expected) {
+ for (const [index, filter] of [...filters].entries()) {
+ const [name, value] = filter.children,
+ label = name.children[1],
+ [input, unit] = value.children;
+
+ const eq = expected[index];
+ is(label.textContent, eq.label, "Label should match");
+ is(input.value, eq.value, "Values should match");
+ if (eq.unit) {
+ is(unit.textContent, eq.unit, "Unit should match");
+ }
+ }
+}
diff --git a/devtools/client/shared/test/browser_filter-editor-03.js b/devtools/client/shared/test/browser_filter-editor-03.js
new file mode 100644
index 0000000000..4c66134ea9
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-03.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the Filter Editor Widget add, removeAt, updateAt, getValueAt methods
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+const GRAYSCALE_MAX = 100;
+const INVERT_MIN = 0;
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(container, "none");
+
+ info("Test add method");
+ const blur = widget.add("blur", "10.2px");
+ is(widget.getCssValue(), "blur(10.2px)", "Should add filters");
+
+ const url = widget.add("url", "test.svg");
+ is(
+ widget.getCssValue(),
+ "blur(10.2px) url(test.svg)",
+ "Should add filters in order"
+ );
+
+ info("Test updateValueAt method");
+ widget.updateValueAt(url, "test2.svg");
+ widget.updateValueAt(blur, 5);
+ is(
+ widget.getCssValue(),
+ "blur(5px) url(test2.svg)",
+ "Should update values correctly"
+ );
+
+ info("Test getValueAt method");
+ is(widget.getValueAt(blur), "5px", "Should return value + unit");
+ is(
+ widget.getValueAt(url),
+ "test2.svg",
+ "Should return value for string-type filters"
+ );
+
+ info("Test removeAt method");
+ widget.removeAt(url);
+ is(widget.getCssValue(), "blur(5px)", "Should remove the specified filter");
+
+ info("Test add method applying filter range to value");
+ const grayscale = widget.add("grayscale", GRAYSCALE_MAX + 1);
+ is(
+ widget.getValueAt(grayscale),
+ `${GRAYSCALE_MAX}%`,
+ "Shouldn't allow values higher than max"
+ );
+
+ const invert = widget.add("invert", INVERT_MIN - 1);
+ is(
+ widget.getValueAt(invert),
+ `${INVERT_MIN}%`,
+ "Shouldn't allow values less than INVERT_MIN"
+ );
+
+ info("Test updateValueAt method applying filter range to value");
+ widget.updateValueAt(grayscale, GRAYSCALE_MAX + 1);
+ is(
+ widget.getValueAt(grayscale),
+ `${GRAYSCALE_MAX}%`,
+ "Shouldn't allow values higher than max"
+ );
+
+ widget.updateValueAt(invert, INVERT_MIN - 1);
+ is(
+ widget.getValueAt(invert),
+ `${INVERT_MIN}%`,
+ "Shouldn't allow values less than INVERT_MIN"
+ );
+ widget.destroy();
+});
diff --git a/devtools/client/shared/test/browser_filter-editor-04.js b/devtools/client/shared/test/browser_filter-editor-04.js
new file mode 100644
index 0000000000..fdc966fa7f
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-04.js
@@ -0,0 +1,106 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the Filter Editor Widget's drag-drop re-ordering
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+const LIST_ITEM_HEIGHT = 32;
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const initialValue = "blur(2px) contrast(200%) brightness(200%)";
+ const widget = new CSSFilterEditorWidget(container, initialValue);
+
+ const filters = widget.el.querySelector("#filters");
+ function first() {
+ return filters.children[0];
+ }
+ function mid() {
+ return filters.children[1];
+ }
+ function last() {
+ return filters.children[2];
+ }
+
+ info("Test re-ordering neighbour filters");
+ widget._mouseDown({
+ target: first().querySelector("i"),
+ pageY: 0,
+ });
+ widget._mouseMove({ pageY: LIST_ITEM_HEIGHT });
+
+ // Element re-ordering should be instant
+ is(
+ mid().querySelector("label").textContent,
+ "blur",
+ "Should reorder elements correctly"
+ );
+
+ widget._mouseUp();
+
+ is(
+ widget.getCssValue(),
+ "contrast(200%) blur(2px) brightness(200%)",
+ "Should reorder filters objects correctly"
+ );
+
+ info("Test re-ordering first and last filters");
+ widget._mouseDown({
+ target: first().querySelector("i"),
+ pageY: 0,
+ });
+ widget._mouseMove({ pageY: LIST_ITEM_HEIGHT * 2 });
+
+ // Element re-ordering should be instant
+ is(
+ last().querySelector("label").textContent,
+ "contrast",
+ "Should reorder elements correctly"
+ );
+ widget._mouseUp();
+
+ is(
+ widget.getCssValue(),
+ "brightness(200%) blur(2px) contrast(200%)",
+ "Should reorder filters objects correctly"
+ );
+
+ info("Test dragging first element out of list");
+ const boundaries = filters.getBoundingClientRect();
+
+ widget._mouseDown({
+ target: first().querySelector("i"),
+ pageY: 0,
+ });
+ widget._mouseMove({ pageY: -LIST_ITEM_HEIGHT * 5 });
+ Assert.greaterOrEqual(
+ first().getBoundingClientRect().top,
+ boundaries.top,
+ "First filter should not move outside filter list"
+ );
+
+ widget._mouseUp();
+
+ info("Test dragging last element out of list");
+ widget._mouseDown({
+ target: last().querySelector("i"),
+ pageY: 0,
+ });
+ widget._mouseMove({ pageY: -LIST_ITEM_HEIGHT * 5 });
+ Assert.lessOrEqual(
+ last().getBoundingClientRect().bottom,
+ boundaries.bottom,
+ "Last filter should not move outside filter list"
+ );
+
+ widget._mouseUp();
+ widget.destroy();
+});
diff --git a/devtools/client/shared/test/browser_filter-editor-05.js b/devtools/client/shared/test/browser_filter-editor-05.js
new file mode 100644
index 0000000000..37c53ff6f4
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-05.js
@@ -0,0 +1,166 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+requestLongerTimeout(2);
+
+// Tests the Filter Editor Widget's label-dragging
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const FAST_VALUE_MULTIPLIER = 10;
+const SLOW_VALUE_MULTIPLIER = 0.1;
+const DEFAULT_VALUE_MULTIPLIER = 1;
+
+const GRAYSCALE_MAX = 100,
+ GRAYSCALE_MIN = 0;
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(
+ container,
+ "grayscale(0%) url(test.svg)"
+ );
+
+ const filters = widget.el.querySelector("#filters");
+ const grayscale = filters.children[0];
+ const url = filters.children[1];
+
+ info("Test label-dragging on number-type filters without modifiers");
+ widget._mouseDown({
+ target: grayscale.querySelector("label"),
+ pageX: 0,
+ altKey: false,
+ shiftKey: false,
+ });
+
+ widget._mouseMove({
+ pageX: 12,
+ altKey: false,
+ shiftKey: false,
+ });
+ let expected = DEFAULT_VALUE_MULTIPLIER * 12;
+ is(
+ widget.getValueAt(0),
+ `${expected}%`,
+ "Should update value correctly without modifiers"
+ );
+
+ info("Test label-dragging on number-type filters with alt");
+ widget._mouseMove({
+ // 20 - 12 = 8
+ pageX: 20,
+ altKey: true,
+ shiftKey: false,
+ });
+
+ expected = expected + SLOW_VALUE_MULTIPLIER * 8;
+ is(
+ widget.getValueAt(0),
+ `${expected}%`,
+ "Should update value correctly with alt key"
+ );
+
+ info("Test label-dragging on number-type filters with shift");
+ widget._mouseMove({
+ // 25 - 20 = 5
+ pageX: 25,
+ altKey: false,
+ shiftKey: true,
+ });
+
+ expected = expected + FAST_VALUE_MULTIPLIER * 5;
+ is(
+ widget.getValueAt(0),
+ `${expected}%`,
+ "Should update value correctly with shift key"
+ );
+
+ info("Test releasing mouse and dragging again");
+
+ widget._mouseUp();
+
+ widget._mouseDown({
+ target: grayscale.querySelector("label"),
+ pageX: 0,
+ altKey: false,
+ shiftKey: false,
+ });
+
+ widget._mouseMove({
+ pageX: 5,
+ altKey: false,
+ shiftKey: false,
+ });
+
+ expected = expected + DEFAULT_VALUE_MULTIPLIER * 5;
+ is(
+ widget.getValueAt(0),
+ `${expected}%`,
+ "Should reset multiplier to default"
+ );
+
+ info("Test value ranges");
+
+ widget._mouseMove({
+ // 30 - 25 = 5
+ pageX: 30,
+ altKey: false,
+ shiftKey: true,
+ });
+
+ expected = GRAYSCALE_MAX;
+ is(
+ widget.getValueAt(0),
+ `${expected}%`,
+ "Shouldn't allow values higher than max"
+ );
+
+ widget._mouseMove({
+ pageX: -11,
+ altKey: false,
+ shiftKey: true,
+ });
+
+ expected = GRAYSCALE_MIN;
+ is(
+ widget.getValueAt(0),
+ `${expected}%`,
+ "Shouldn't allow values less than min"
+ );
+
+ widget._mouseUp();
+
+ info("Test label-dragging on string-type filters");
+ widget._mouseDown({
+ target: url.querySelector("label"),
+ pageX: 0,
+ altKey: false,
+ shiftKey: false,
+ });
+
+ ok(
+ !widget.isDraggingLabel,
+ "Label-dragging should not work for string-type filters"
+ );
+
+ widget._mouseMove({
+ pageX: -11,
+ altKey: false,
+ shiftKey: true,
+ });
+
+ is(
+ widget.getValueAt(1),
+ "test.svg",
+ "Label-dragging on string-type filters shouldn't affect their value"
+ );
+ widget.destroy();
+});
diff --git a/devtools/client/shared/test/browser_filter-editor-06.js b/devtools/client/shared/test/browser_filter-editor-06.js
new file mode 100644
index 0000000000..0f5f5abd4c
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-06.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the Filter Editor Widget's add button
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const STRINGS_URI = "devtools/client/locales/filterwidget.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(container, "none");
+
+ const select = widget.el.querySelector("select"),
+ add = widget.el.querySelector("#add-filter");
+
+ const TEST_DATA = [
+ {
+ name: "blur",
+ unit: "px",
+ type: "length",
+ },
+ {
+ name: "contrast",
+ unit: "%",
+ type: "percentage",
+ },
+ {
+ name: "hue-rotate",
+ unit: "deg",
+ type: "angle",
+ },
+ {
+ name: "drop-shadow",
+ placeholder: L10N.getStr("dropShadowPlaceholder"),
+ type: "string",
+ },
+ {
+ name: "url",
+ placeholder: "example.svg#c1",
+ type: "string",
+ },
+ ];
+
+ info("Test adding new filters with different units");
+
+ for (const [index, filter] of TEST_DATA.entries()) {
+ select.value = filter.name;
+ add.click();
+
+ if (filter.unit) {
+ is(
+ widget.getValueAt(index),
+ `0${filter.unit}`,
+ `Should add ${filter.unit} to ${filter.type} filters`
+ );
+ } else if (filter.placeholder) {
+ const i = index + 1;
+ const input = widget.el.querySelector(`.filter:nth-child(${i}) input`);
+ is(
+ input.placeholder,
+ filter.placeholder,
+ "Should set the appropriate placeholder for string-type filters"
+ );
+ }
+ }
+ widget.destroy();
+});
diff --git a/devtools/client/shared/test/browser_filter-editor-07.js b/devtools/client/shared/test/browser_filter-editor-07.js
new file mode 100644
index 0000000000..354d46addc
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-07.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the Filter Editor Widget's remove button
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(
+ container,
+ "blur(2px) contrast(200%)"
+ );
+
+ info("Test removing filters with remove button");
+ widget.el.querySelector(".filter button").click();
+
+ is(
+ widget.getCssValue(),
+ "contrast(200%)",
+ "Should remove the clicked filter"
+ );
+ widget.destroy();
+});
diff --git a/devtools/client/shared/test/browser_filter-editor-08.js b/devtools/client/shared/test/browser_filter-editor-08.js
new file mode 100644
index 0000000000..8759a230a0
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-08.js
@@ -0,0 +1,103 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the Filter Editor Widget inputs increase/decrease value using
+// arrow keys, applying multiplier using alt/shift on number-type filters
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const FAST_VALUE_MULTIPLIER = 10;
+const SLOW_VALUE_MULTIPLIER = 0.1;
+const DEFAULT_VALUE_MULTIPLIER = 1;
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const initialValue = "blur(2px)";
+ const widget = new CSSFilterEditorWidget(container, initialValue);
+
+ let value = 2;
+
+ triggerKey = triggerKey.bind(widget);
+
+ info("Test simple arrow keys");
+ triggerKey(40);
+
+ value -= DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ `${value}px`,
+ "Should decrease value using down arrow"
+ );
+
+ triggerKey(38);
+
+ value += DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ `${value}px`,
+ "Should decrease value using down arrow"
+ );
+
+ info("Test shift key multiplier");
+ triggerKey(38, "shiftKey");
+
+ value += FAST_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ `${value}px`,
+ "Should increase value by fast multiplier using up arrow"
+ );
+
+ triggerKey(40, "shiftKey");
+
+ value -= FAST_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ `${value}px`,
+ "Should decrease value by fast multiplier using down arrow"
+ );
+
+ info("Test alt key multiplier");
+ triggerKey(38, "altKey");
+
+ value += SLOW_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ `${value}px`,
+ "Should increase value by slow multiplier using up arrow"
+ );
+
+ triggerKey(40, "altKey");
+
+ value -= SLOW_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ `${value}px`,
+ "Should decrease value by slow multiplier using down arrow"
+ );
+
+ widget.destroy();
+ triggerKey = null;
+});
+
+// Triggers the specified keyCode and modifier key on
+// first filter's input
+function triggerKey(key, modifier) {
+ const filter = this.el.querySelector("#filters").children[0];
+ const input = filter.querySelector("input");
+
+ this._keyDown({
+ target: input,
+ keyCode: key,
+ [modifier]: true,
+ preventDefault() {},
+ });
+}
diff --git a/devtools/client/shared/test/browser_filter-editor-09.js b/devtools/client/shared/test/browser_filter-editor-09.js
new file mode 100644
index 0000000000..e22898cfd5
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-09.js
@@ -0,0 +1,155 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the Filter Editor Widget inputs increase/decrease value when cursor is
+// on a number using arrow keys, applying multiplier using alt/shift on strings
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const FAST_VALUE_MULTIPLIER = 10;
+const SLOW_VALUE_MULTIPLIER = 0.1;
+const DEFAULT_VALUE_MULTIPLIER = 1;
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const initialValue = "drop-shadow(rgb(0, 0, 0) 1px 1px 0px)";
+ const widget = new CSSFilterEditorWidget(container, initialValue);
+ widget.el.querySelector("#filters input").setSelectionRange(13, 13);
+
+ let value = 1;
+
+ triggerKey = triggerKey.bind(widget);
+
+ info("Test simple arrow keys");
+ triggerKey(40);
+
+ value -= DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should decrease value using down arrow"
+ );
+
+ triggerKey(38);
+
+ value += DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should decrease value using down arrow"
+ );
+
+ info("Test shift key multiplier");
+ triggerKey(38, "shiftKey");
+
+ value += FAST_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should increase value by fast multiplier using up arrow"
+ );
+
+ triggerKey(40, "shiftKey");
+
+ value -= FAST_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should decrease value by fast multiplier using down arrow"
+ );
+
+ info("Test alt key multiplier");
+ triggerKey(38, "altKey");
+
+ value += SLOW_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should increase value by slow multiplier using up arrow"
+ );
+
+ triggerKey(40, "altKey");
+
+ value -= SLOW_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should decrease value by slow multiplier using down arrow"
+ );
+
+ triggerKey(40, "shiftKey");
+
+ value -= FAST_VALUE_MULTIPLIER;
+ is(widget.getValueAt(0), val(value), "Should decrease to negative");
+
+ triggerKey(40);
+
+ value -= DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should decrease negative numbers correctly"
+ );
+
+ triggerKey(38);
+
+ value += DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should increase negative values correctly"
+ );
+
+ triggerKey(40, "altKey");
+ triggerKey(40, "altKey");
+
+ value -= SLOW_VALUE_MULTIPLIER * 2;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should decrease float numbers correctly"
+ );
+
+ triggerKey(38, "altKey");
+
+ value += SLOW_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should increase float numbers correctly"
+ );
+
+ widget.destroy();
+ triggerKey = null;
+});
+
+// Triggers the specified keyCode and modifier key on
+// first filter's input
+function triggerKey(key, modifier) {
+ const filter = this.el.querySelector("#filters").children[0];
+ const input = filter.querySelector("input");
+
+ this._keyDown({
+ target: input,
+ keyCode: key,
+ [modifier]: true,
+ preventDefault() {},
+ });
+}
+
+function val(value) {
+ let v = value.toFixed(1);
+
+ if (v.indexOf(".0") > -1) {
+ v = v.slice(0, -2);
+ }
+ return `rgb(0, 0, 0) ${v}px 1px 0px`;
+}
diff --git a/devtools/client/shared/test/browser_filter-editor-10.js b/devtools/client/shared/test/browser_filter-editor-10.js
new file mode 100644
index 0000000000..248ee0c506
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-editor-10.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the Filter Editor Widget inputs increase/decrease value when cursor is
+// on a number using arrow keys if cursor is behind/mid/after the number strings
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const DEFAULT_VALUE_MULTIPLIER = 1;
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const initialValue = "drop-shadow(rgb(0, 0, 0) 10px 1px 0px)";
+ const widget = new CSSFilterEditorWidget(container, initialValue);
+ const input = widget.el.querySelector("#filters input");
+
+ let value = 10;
+
+ triggerKey = triggerKey.bind(widget);
+
+ info("Test increment/decrement of string-type numbers without selection");
+
+ input.setSelectionRange(14, 14);
+ triggerKey(40);
+
+ value -= DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should work with cursor in the middle of number"
+ );
+
+ input.setSelectionRange(13, 13);
+ triggerKey(38);
+
+ value += DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should work with cursor before the number"
+ );
+
+ input.setSelectionRange(15, 15);
+ triggerKey(40);
+
+ value -= DEFAULT_VALUE_MULTIPLIER;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should work with cursor after the number"
+ );
+
+ info("Test increment/decrement of string-type numbers with a selection");
+
+ input.setSelectionRange(13, 15);
+ triggerKey(38);
+ input.setSelectionRange(13, 18);
+ triggerKey(38);
+
+ value += DEFAULT_VALUE_MULTIPLIER * 2;
+ is(
+ widget.getValueAt(0),
+ val(value),
+ "Should work if a there is a selection, starting with the number"
+ );
+
+ widget.destroy();
+ triggerKey = null;
+});
+
+// Triggers the specified keyCode and modifier key on
+// first filter's input
+function triggerKey(key, modifier) {
+ const filter = this.el.querySelector("#filters").children[0];
+ const input = filter.querySelector("input");
+
+ this._keyDown({
+ target: input,
+ keyCode: key,
+ [modifier]: true,
+ preventDefault() {},
+ });
+}
+
+function val(value) {
+ let v = value.toFixed(1);
+
+ if (v.indexOf(".0") > -1) {
+ v = v.slice(0, -2);
+ }
+ return `rgb(0, 0, 0) ${v}px 1px 0px`;
+}
diff --git a/devtools/client/shared/test/browser_filter-presets-01.js b/devtools/client/shared/test/browser_filter-presets-01.js
new file mode 100644
index 0000000000..3a6d4a93d5
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-presets-01.js
@@ -0,0 +1,117 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests saving presets
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(container, "none");
+ // First render
+ await widget.once("render");
+
+ const VALUE = "blur(2px) contrast(150%)";
+ const NAME = "Test";
+
+ await showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
+
+ const preset = widget.el.querySelector(".preset");
+ is(
+ preset.querySelector("label").textContent,
+ NAME,
+ "Should show preset name correctly"
+ );
+ is(
+ preset.querySelector("span").textContent,
+ VALUE,
+ "Should show preset value preview correctly"
+ );
+
+ let list = await widget.getPresets();
+ const input = widget.el.querySelector(".presets-list .footer input");
+ let data = list[0];
+
+ is(data.name, NAME, "Should add the preset to asyncStorage - name property");
+ is(
+ data.value,
+ VALUE,
+ "Should add the preset to asyncStorage - name property"
+ );
+
+ info("Test overriding preset by using the same name");
+
+ const VALUE_2 = "saturate(50%) brightness(10%)";
+
+ widget.setCssValue(VALUE_2);
+
+ await savePreset(widget);
+
+ is(
+ widget.el.querySelectorAll(".preset").length,
+ 1,
+ "Should override the preset with the same name - render"
+ );
+
+ list = await widget.getPresets();
+ data = list[0];
+
+ is(
+ list.length,
+ 1,
+ "Should override the preset with the same name - asyncStorage"
+ );
+
+ is(
+ data.name,
+ NAME,
+ "Should override the preset with the same name - prop name"
+ );
+ is(
+ data.value,
+ VALUE_2,
+ "Should override the preset with the same name - prop value"
+ );
+
+ await widget.setPresets([]);
+
+ info("Test saving a preset without name");
+ input.value = "";
+
+ await savePreset(widget, "preset-save-error");
+
+ list = await widget.getPresets();
+ is(list.length, 0, "Should not add a preset without name");
+
+ info("Test saving a preset without filters");
+
+ input.value = NAME;
+ widget.setCssValue("none");
+
+ await savePreset(widget, "preset-save-error");
+
+ list = await widget.getPresets();
+ is(list.length, 0, "Should not add a preset without filters (value: none)");
+});
+
+/**
+ * Call savePreset on widget and wait for the specified event to emit
+ * @param {CSSFilterWidget} widget
+ * @param {string} expectEvent="render" The event to listen on
+ * @return {Promise}
+ */
+function savePreset(widget, expectEvent = "render") {
+ const onEvent = widget.once(expectEvent);
+ widget._savePreset({
+ preventDefault: () => {},
+ });
+ return onEvent;
+}
diff --git a/devtools/client/shared/test/browser_filter-presets-02.js b/devtools/client/shared/test/browser_filter-presets-02.js
new file mode 100644
index 0000000000..ef9cb62bbe
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-presets-02.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests loading presets
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(container, "none");
+ // First render
+ await widget.once("render");
+
+ const VALUE = "blur(2px) contrast(150%)";
+ const NAME = "Test";
+
+ await showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
+
+ let onRender = widget.once("render");
+ // reset value
+ widget.setCssValue("saturate(100%) brightness(150%)");
+ await onRender;
+
+ const preset = widget.el.querySelector(".preset");
+
+ onRender = widget.once("render");
+ widget._presetClick({
+ target: preset,
+ });
+
+ await onRender;
+
+ is(widget.getCssValue(), VALUE, "Should set widget's value correctly");
+ is(
+ widget.el.querySelector(".presets-list .footer input").value,
+ NAME,
+ "Should set input's value to name"
+ );
+});
diff --git a/devtools/client/shared/test/browser_filter-presets-03.js b/devtools/client/shared/test/browser_filter-presets-03.js
new file mode 100644
index 0000000000..2c679c3a1b
--- /dev/null
+++ b/devtools/client/shared/test/browser_filter-presets-03.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests deleting presets
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.querySelector("#filter-container");
+ const widget = new CSSFilterEditorWidget(container, "none");
+ // First render
+ await widget.once("render");
+
+ const NAME = "Test";
+ const VALUE = "blur(2px) contrast(150%)";
+
+ await showFilterPopupPresetsAndCreatePreset(widget, NAME, VALUE);
+
+ const removeButton = widget.el.querySelector(".preset .remove-button");
+ const onRender = widget.once("render");
+ widget._presetClick({
+ target: removeButton,
+ });
+
+ await onRender;
+ is(
+ widget.el.querySelector(".preset"),
+ null,
+ "Should re-render after removing preset"
+ );
+
+ const list = await widget.getPresets();
+ is(list.length, 0, "Should remove presets from asyncStorage");
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip-01.js b/devtools/client/shared/test/browser_html_tooltip-01.js
new file mode 100644
index 0000000000..401d9d1c61
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip-01.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip show & hide methods.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+function getTooltipContent(doc) {
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "50px";
+ div.style.boxSizing = "border-box";
+ div.textContent = "tooltip";
+ return div;
+}
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper });
+
+ info("Set tooltip content");
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+
+ is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+ info("Show the tooltip and check the expected events are fired.");
+
+ let shown = 0;
+ tooltip.on("shown", () => shown++);
+
+ const onShown = tooltip.once("shown");
+ tooltip.show(doc.getElementById("box1"));
+ await onShown;
+ is(shown, 1, "Event shown was fired once");
+
+ await waitForReflow(tooltip);
+ is(tooltip.isVisible(), true, "Tooltip is visible");
+
+ info("Hide the tooltip and check the expected events are fired.");
+
+ let hidden = 0;
+ tooltip.on("hidden", () => hidden++);
+
+ const onPopupHidden = tooltip.once("hidden");
+ tooltip.hide();
+
+ await onPopupHidden;
+ is(hidden, 1, "Event hidden was fired once");
+
+ await waitForReflow(tooltip);
+ is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+ tooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip-02.js b/devtools/client/shared/test/browser_html_tooltip-02.js
new file mode 100644
index 0000000000..4a622a7e7a
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip-02.js
@@ -0,0 +1,227 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+"use strict";
+
+/**
+ * Test the HTMLTooltip is closed when clicking outside of its container.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-02.xhtml";
+const PROMISE_TIMEOUT = 3000;
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ await testClickInTooltipContent(doc);
+ await testClickInTooltipIcon(doc);
+ await testConsumeOutsideClicksFalse(doc);
+ await testConsumeOutsideClicksTrue(doc);
+ await testConsumeWithRightClick(doc);
+ await testClickInOuterIframe(doc);
+ await testClickInInnerIframe(doc);
+}
+
+async function testClickInTooltipContent(doc) {
+ info("Test a tooltip is not closed when clicking inside itself");
+
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper });
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ const onTooltipContainerClick = once(tooltip.container, "click");
+ EventUtils.synthesizeMouseAtCenter(tooltip.container, {}, doc.defaultView);
+ await onTooltipContainerClick;
+ is(tooltip.isVisible(), true, "Tooltip is still visible");
+
+ tooltip.destroy();
+}
+
+async function testClickInTooltipIcon(doc) {
+ info("Test a tooltip is not closed when clicking it's icon");
+
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper, noAutoHide: true });
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+
+ const box1 = doc.getElementById("box1");
+ await showTooltip(tooltip, box1);
+
+ const onHidden = once(tooltip, "hidden");
+ box1.click();
+
+ // Hiding the tooltip is async so we need to wait for "hidden" to be emitted
+ // timing out after 3 seconds. If hidden is emitted we need to fail,
+ // otherwise the test passes.
+ const shown = await Promise.race([
+ onHidden,
+ wait(PROMISE_TIMEOUT).then(() => true),
+ ]);
+
+ ok(shown, "Tooltip is still visible");
+
+ tooltip.destroy();
+}
+
+async function testConsumeOutsideClicksFalse(doc) {
+ info("Test closing a tooltip via click with consumeOutsideClicks: false");
+ const box4 = doc.getElementById("box4");
+
+ const tooltip = new HTMLTooltip(doc, {
+ consumeOutsideClicks: false,
+ useXulWrapper,
+ });
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ const onBox4Clicked = once(box4, "click");
+ const onHidden = once(tooltip, "hidden");
+ box4.click();
+ await onHidden;
+ await onBox4Clicked;
+
+ is(tooltip.isVisible(), false, "Tooltip is hidden");
+
+ tooltip.destroy();
+}
+
+async function testConsumeOutsideClicksTrue(doc) {
+ info("Test closing a tooltip via click with consumeOutsideClicks: true");
+ const box4 = doc.getElementById("box4");
+
+ // Count clicks on box4
+ let box4clicks = 0;
+ box4.addEventListener("click", () => box4clicks++);
+
+ const tooltip = new HTMLTooltip(doc, {
+ consumeOutsideClicks: true,
+ useXulWrapper,
+ });
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ const onHidden = once(tooltip, "hidden");
+ box4.click();
+ await onHidden;
+
+ is(box4clicks, 0, "box4 catched no click event");
+ is(tooltip.isVisible(), false, "Tooltip is hidden");
+
+ tooltip.destroy();
+}
+
+async function testConsumeWithRightClick(doc) {
+ info(
+ "Test closing a tooltip with a right-click, with consumeOutsideClicks: true"
+ );
+ const box4 = doc.getElementById("box4");
+
+ const tooltip = new HTMLTooltip(doc, {
+ consumeOutsideClicks: true,
+ useXulWrapper,
+ });
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ // Only left-click events should be consumed, so we expect to catch a click when using
+ // {button: 2}, which simulates a right-click.
+ info(
+ "Right click on box4, expect tooltip to be hidden, event should not be consumed"
+ );
+ const onBox4Clicked = once(box4, "click");
+ const onHidden = once(tooltip, "hidden");
+ EventUtils.synthesizeMouseAtCenter(box4, { button: 2 }, doc.defaultView);
+ await onHidden;
+ await onBox4Clicked;
+
+ is(tooltip.isVisible(), false, "Tooltip is hidden");
+
+ tooltip.destroy();
+}
+
+async function testClickInOuterIframe(doc) {
+ info("Test clicking an iframe outside of the tooltip closes the tooltip");
+ const frame = doc.getElementById("frame");
+
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper });
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ const onHidden = once(tooltip, "hidden");
+ frame.click();
+ await onHidden;
+
+ is(tooltip.isVisible(), false, "Tooltip is hidden");
+ tooltip.destroy();
+}
+
+async function testClickInInnerIframe(doc) {
+ info(
+ "Test clicking an iframe inside the tooltip content does not close the tooltip"
+ );
+
+ const tooltip = new HTMLTooltip(doc, {
+ consumeOutsideClicks: false,
+ useXulWrapper,
+ });
+
+ const iframe = doc.createElementNS(HTML_NS, "iframe");
+ iframe.style.width = "100px";
+ iframe.style.height = "50px";
+
+ tooltip.panel.appendChild(iframe);
+
+ tooltip.setContentSize({ width: 100, height: 50 });
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ iframe.srcdoc = "<div id=test style='height:50px'></div>";
+ await new Promise(r => {
+ const frameLoad = () => {
+ r();
+ };
+ DOMHelpers.onceDOMReady(iframe.contentWindow, frameLoad);
+ });
+
+ await waitUntil(() => iframe.contentWindow.document.getElementById("test"));
+
+ const target = iframe.contentWindow.document.getElementById("test");
+ const onTooltipClick = once(target, "click");
+ target.click();
+ await onTooltipClick;
+
+ is(tooltip.isVisible(), true, "Tooltip is still visible");
+
+ tooltip.destroy();
+}
+
+function getTooltipContent(doc) {
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "50px";
+ div.style.boxSizing = "border-box";
+ div.textContent = "tooltip";
+ return div;
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip-03.js b/devtools/client/shared/test/browser_html_tooltip-03.js
new file mode 100644
index 0000000000..15be3e3382
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip-03.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * This is the sanity test for the HTMLTooltip focus
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-03.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ await focusNode(doc, "#box4-input");
+ ok(doc.activeElement.closest("#box4-input"), "Focus is in the #box4-input");
+
+ info("Test a tooltip will not take focus");
+ const tooltip = await createTooltip(doc);
+
+ await showTooltip(tooltip, doc.getElementById("box1"));
+ ok(
+ doc.activeElement.closest("#box4-input"),
+ "Focus is still in the #box4-input"
+ );
+
+ await hideTooltip(tooltip);
+ await blurNode(doc, "#box4-input");
+
+ tooltip.destroy();
+}
+
+/**
+ * Fpcus the node corresponding to the provided selector in the provided document. Returns
+ * a promise that will resolve when receiving the focus event on the node.
+ */
+function focusNode(doc, selector) {
+ const node = doc.querySelector(selector);
+ const onFocus = once(node, "focus");
+ node.focus();
+ return onFocus;
+}
+
+/**
+ * Blur the node corresponding to the provided selector in the provided document. Returns
+ * a promise that will resolve when receiving the blur event on the node.
+ */
+function blurNode(doc, selector) {
+ const node = doc.querySelector(selector);
+ const onBlur = once(node, "blur");
+ node.blur();
+ return onBlur;
+}
+
+/**
+ * Create an HTMLTooltip instance.
+ *
+ * @param {Document} doc
+ * Document in which the tooltip should be created
+ * @return {Promise} promise that will resolve the HTMLTooltip instance created when the
+ * tooltip content will be ready.
+ */
+function createTooltip(doc) {
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.classList.add("tooltip-content");
+ div.style.height = "50px";
+
+ const input = doc.createElementNS(HTML_NS, "input");
+ input.setAttribute("type", "text");
+ div.appendChild(input);
+
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: 150, height: 50 });
+ return tooltip;
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip-04.js b/devtools/client/shared/test/browser_html_tooltip-04.js
new file mode 100644
index 0000000000..9fe854bee4
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip-04.js
@@ -0,0 +1,100 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip positioning for a small tooltip element (should aways
+ * find a way to fit).
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-04.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+const TOOLTIP_HEIGHT = 30;
+const TOOLTIP_WIDTH = 100;
+
+add_task(async function () {
+ // Force the toolbox to be 400px high;
+ await pushPref("devtools.toolbox.footer.height", 400);
+
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Create HTML tooltip");
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: false });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "100%";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT });
+
+ const box1 = doc.getElementById("box1");
+ const box2 = doc.getElementById("box2");
+ const box3 = doc.getElementById("box3");
+ const box4 = doc.getElementById("box4");
+ const height = TOOLTIP_HEIGHT,
+ width = TOOLTIP_WIDTH;
+
+ // box1: Can only fit below box1
+ info("Display the tooltip on box1.");
+ await showTooltip(tooltip, box1);
+ let expectedTooltipGeometry = { position: "bottom", height, width };
+ checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on top of box1.");
+ await showTooltip(tooltip, box1, { position: "top" });
+ expectedTooltipGeometry = { position: "bottom", height, width };
+ checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ // box2: Can fit above or below, will default to bottom, more height
+ // available.
+ info("Try to display the tooltip on box2.");
+ await showTooltip(tooltip, box2);
+ expectedTooltipGeometry = { position: "bottom", height, width };
+ checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on top of box2.");
+ await showTooltip(tooltip, box2, { position: "top" });
+ expectedTooltipGeometry = { position: "top", height, width };
+ checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ // box3: Can fit above or below, will default to top, more height available.
+ info("Try to display the tooltip on box3.");
+ await showTooltip(tooltip, box3);
+ expectedTooltipGeometry = { position: "top", height, width };
+ checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on bottom of box3.");
+ await showTooltip(tooltip, box3, { position: "bottom" });
+ expectedTooltipGeometry = { position: "bottom", height, width };
+ checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ // box4: Can only fit above box4
+ info("Display the tooltip on box4.");
+ await showTooltip(tooltip, box4);
+ expectedTooltipGeometry = { position: "top", height, width };
+ checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on bottom of box4.");
+ await showTooltip(tooltip, box4, { position: "bottom" });
+ expectedTooltipGeometry = { position: "top", height, width };
+ checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip-05.js b/devtools/client/shared/test/browser_html_tooltip-05.js
new file mode 100644
index 0000000000..7217039cb4
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip-05.js
@@ -0,0 +1,101 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip positioning for a huge tooltip element (can not fit in
+ * the viewport).
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-05.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+const TOOLTIP_HEIGHT = 200;
+const TOOLTIP_WIDTH = 200;
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Create HTML tooltip");
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: false });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "100%";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT });
+
+ const box1 = doc.getElementById("box1");
+ const box2 = doc.getElementById("box2");
+ const box3 = doc.getElementById("box3");
+ const box4 = doc.getElementById("box4");
+ const width = TOOLTIP_WIDTH;
+
+ // box1: Can not fit above or below box1, default to bottom with a reduced
+ // height of 150px.
+ info("Display the tooltip on box1.");
+ await showTooltip(tooltip, box1);
+ let expectedTooltipGeometry = { position: "bottom", height: 150, width };
+ checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on top of box1.");
+ await showTooltip(tooltip, box1, { position: "top" });
+ expectedTooltipGeometry = { position: "bottom", height: 150, width };
+ checkTooltipGeometry(tooltip, box1, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ // box2: Can not fit above or below box2, default to bottom with a reduced
+ // height of 100px.
+ info("Try to display the tooltip on box2.");
+ await showTooltip(tooltip, box2);
+ expectedTooltipGeometry = { position: "bottom", height: 100, width };
+ checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on top of box2.");
+ await showTooltip(tooltip, box2, { position: "top" });
+ expectedTooltipGeometry = { position: "bottom", height: 100, width };
+ checkTooltipGeometry(tooltip, box2, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ // box3: Can not fit above or below box3, default to top with a reduced height
+ // of 100px.
+ info("Try to display the tooltip on box3.");
+ await showTooltip(tooltip, box3);
+ expectedTooltipGeometry = { position: "top", height: 100, width };
+ checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on bottom of box3.");
+ await showTooltip(tooltip, box3, { position: "bottom" });
+ expectedTooltipGeometry = { position: "top", height: 100, width };
+ checkTooltipGeometry(tooltip, box3, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ // box4: Can not fit above or below box4, default to top with a reduced height
+ // of 150px.
+ info("Display the tooltip on box4.");
+ await showTooltip(tooltip, box4);
+ expectedTooltipGeometry = { position: "top", height: 150, width };
+ checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ info("Try to display the tooltip on bottom of box4.");
+ await showTooltip(tooltip, box4, { position: "bottom" });
+ expectedTooltipGeometry = { position: "top", height: 150, width };
+ checkTooltipGeometry(tooltip, box4, expectedTooltipGeometry);
+ await hideTooltip(tooltip);
+
+ is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip_arrow-01.js b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
new file mode 100644
index 0000000000..222d43b696
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_arrow-01.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip "arrow" type on small anchors. The arrow should remain
+ * aligned with the anchors as much as possible
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_arrow-01.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ info("Create HTML tooltip");
+ const tooltip = new HTMLTooltip(doc, { type: "arrow", useXulWrapper });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "35px";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: 200, height: 35 });
+
+ const { right: docRight } = doc.documentElement.getBoundingClientRect();
+
+ const elements = [...doc.querySelectorAll(".anchor")];
+ for (const el of elements) {
+ info("Display the tooltip on an anchor.");
+ await showTooltip(tooltip, el);
+
+ const arrow = tooltip.arrow;
+ ok(arrow, "Tooltip has an arrow");
+
+ // Get the geometry of the anchor, the tooltip panel & arrow.
+ const arrowBounds = arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
+ const panelBounds = tooltip.panel
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+ const anchorBounds = el.getBoxQuads({ relativeTo: doc })[0].getBounds();
+
+ const intersects =
+ arrowBounds.left <= anchorBounds.right &&
+ arrowBounds.right >= anchorBounds.left;
+ const isBlockedByViewport =
+ arrowBounds.left == 0 || arrowBounds.right == docRight;
+ ok(
+ intersects || isBlockedByViewport,
+ "Tooltip arrow is aligned with the anchor, or stuck on viewport's edge."
+ );
+
+ const isInPanel =
+ arrowBounds.left >= panelBounds.left &&
+ arrowBounds.right <= panelBounds.right;
+
+ ok(
+ isInPanel,
+ "The tooltip arrow remains inside the tooltip panel horizontally"
+ );
+
+ await hideTooltip(tooltip);
+ }
+
+ tooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_arrow-02.js b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
new file mode 100644
index 0000000000..07222a6a49
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_arrow-02.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip "arrow" type on wide anchors. The arrow should remain
+ * aligned with the anchors as much as possible
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_arrow-02.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ info("Create HTML tooltip");
+ const tooltip = new HTMLTooltip(doc, { type: "arrow", useXulWrapper });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "35px";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: 200, height: 35 });
+
+ const { right: docRight } = doc.documentElement.getBoundingClientRect();
+
+ const elements = [...doc.querySelectorAll(".anchor")];
+ for (const el of elements) {
+ info("Display the tooltip on an anchor.");
+ await showTooltip(tooltip, el);
+
+ const arrow = tooltip.arrow;
+ ok(arrow, "Tooltip has an arrow");
+
+ // Get the geometry of the anchor, the tooltip panel & arrow.
+ const arrowBounds = arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
+ const panelBounds = tooltip.panel
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+ const anchorBounds = el.getBoxQuads({ relativeTo: doc })[0].getBounds();
+
+ const intersects =
+ arrowBounds.left <= anchorBounds.right &&
+ arrowBounds.right >= anchorBounds.left;
+ const isBlockedByViewport =
+ arrowBounds.left == 0 || arrowBounds.right == docRight;
+ ok(
+ intersects || isBlockedByViewport,
+ "Tooltip arrow is aligned with the anchor, or stuck on viewport's edge."
+ );
+
+ const isInPanel =
+ arrowBounds.left >= panelBounds.left &&
+ arrowBounds.right <= panelBounds.right;
+ ok(
+ isInPanel,
+ "The tooltip arrow remains inside the tooltip panel horizontally"
+ );
+ await hideTooltip(tooltip);
+ }
+
+ tooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js b/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
new file mode 100644
index 0000000000..d61d57caff
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_consecutive-show.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip show can be called several times. It should move according to the
+ * new anchor/options and should not leak event listeners.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+function getTooltipContent(doc) {
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "50px";
+ div.textContent = "tooltip";
+ return div;
+}
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ // Creating a host is not correctly waiting when DevTools run in content frame
+ // See Bug 1571421.
+ await wait(1000);
+
+ const box1 = doc.getElementById("box1");
+ const box2 = doc.getElementById("box2");
+ const box3 = doc.getElementById("box3");
+ const box4 = doc.getElementById("box4");
+
+ const width = 100,
+ height = 50;
+
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: false });
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width, height });
+
+ info(
+ "Show the tooltip on each of the 4 hbox, without calling hide in between"
+ );
+
+ info("Show tooltip on box1");
+ tooltip.show(box1);
+ checkTooltipGeometry(tooltip, box1, { position: "bottom", width, height });
+
+ info("Show tooltip on box2");
+ tooltip.show(box2);
+ checkTooltipGeometry(tooltip, box2, { position: "bottom", width, height });
+
+ info("Show tooltip on box3");
+ tooltip.show(box3);
+ checkTooltipGeometry(tooltip, box3, { position: "top", width, height });
+
+ info("Show tooltip on box4");
+ tooltip.show(box4);
+ checkTooltipGeometry(tooltip, box4, { position: "top", width, height });
+
+ info("Hide tooltip before leaving test");
+ await hideTooltip(tooltip);
+
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip_doorhanger-01.js b/devtools/client/shared/test/browser_html_tooltip_doorhanger-01.js
new file mode 100644
index 0000000000..7e2d9e5657
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_doorhanger-01.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip "doorhanger" type's hang direction. It should hang
+ * along the flow of text e.g. in RTL mode it should hang left and in LTR mode
+ * it should hang right.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_doorhanger-01.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ info("Create HTML tooltip");
+ const tooltip = new HTMLTooltip(doc, { type: "doorhanger", useXulWrapper });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.width = "200px";
+ div.style.height = "35px";
+ tooltip.panel.appendChild(div);
+
+ const anchors = [...doc.querySelectorAll(".anchor")];
+ for (const anchor of anchors) {
+ const hangDirection = anchor.getAttribute("data-hang");
+
+ info("Display the tooltip on an anchor.");
+ await showTooltip(tooltip, anchor);
+
+ const arrow = tooltip.arrow;
+ ok(arrow, "Tooltip has an arrow");
+
+ // Get the geometry of the the tooltip panel & arrow.
+ const panelBounds = tooltip.panel
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+ const arrowBounds = arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
+ const panelBoundsCentre = (panelBounds.left + panelBounds.right) / 2;
+ const arrowCentre = (arrowBounds.left + arrowBounds.right) / 2;
+
+ if (hangDirection === "left") {
+ Assert.greater(
+ arrowCentre,
+ panelBoundsCentre,
+ `tooltip hangs to the left for ${anchor.id}`
+ );
+ } else {
+ Assert.less(
+ arrowCentre,
+ panelBoundsCentre,
+ `tooltip hangs to the right for ${anchor.id}`
+ );
+ }
+
+ await hideTooltip(tooltip);
+ }
+
+ tooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_doorhanger-02.js b/devtools/client/shared/test/browser_html_tooltip_doorhanger-02.js
new file mode 100644
index 0000000000..b1564f698c
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_doorhanger-02.js
@@ -0,0 +1,76 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip "doorhanger" type's arrow tip is precisely centered on
+ * the anchor when the anchor is small.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_doorhanger-02.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ info("Create HTML tooltip");
+ const tooltip = new HTMLTooltip(doc, { type: "doorhanger", useXulWrapper });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.width = "200px";
+ div.style.height = "35px";
+ tooltip.panel.appendChild(div);
+
+ const elements = [...doc.querySelectorAll(".anchor")];
+ for (const el of elements) {
+ info("Display the tooltip on an anchor.");
+ await showTooltip(tooltip, el);
+
+ const arrow = tooltip.arrow;
+ ok(arrow, "Tooltip has an arrow");
+
+ // Get the geometry of the anchor and arrow.
+ const anchorBounds = el.getBoxQuads({ relativeTo: doc })[0].getBounds();
+ const arrowBounds = arrow.getBoxQuads({ relativeTo: doc })[0].getBounds();
+
+ // Compare the centers
+ const center = bounds => bounds.left + bounds.width / 2;
+ const delta = Math.abs(center(anchorBounds) - center(arrowBounds));
+ const describeBounds = bounds =>
+ `${bounds.left}<--[${center(bounds)}]-->${bounds.right}`;
+ const params =
+ `anchor: ${describeBounds(anchorBounds)}, ` +
+ `arrow: ${describeBounds(arrowBounds)}`;
+ Assert.lessOrEqual(
+ delta,
+ 1,
+ `Arrow center is roughly aligned with anchor center (${params})`
+ );
+
+ await hideTooltip(tooltip);
+ }
+
+ tooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_height-auto.js b/devtools/client/shared/test/browser_html_tooltip_height-auto.js
new file mode 100644
index 0000000000..7a42183f80
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_height-auto.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip content can automatically calculate its height based on
+ * content.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper });
+ info("Create tooltip content height to 150px");
+ const tooltipContent = doc.createElementNS(HTML_NS, "div");
+ tooltipContent.style.cssText =
+ "width: 300px; height: 150px; background: red;";
+
+ info("Set tooltip content using width:auto and height:auto");
+ tooltip.panel.appendChild(tooltipContent);
+
+ info("Show the tooltip and check the tooltip container dimensions.");
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ let panelRect = tooltip.container.getBoundingClientRect();
+ is(panelRect.width, 300, "Tooltip container has the expected width.");
+ is(panelRect.height, 150, "Tooltip container has the expected height.");
+
+ await hideTooltip(tooltip);
+
+ info("Set tooltip content using fixed width and height:auto");
+ tooltipContent.style.cssText = "width: auto; height: 160px; background: red;";
+ tooltip.setContentSize({ width: 400 });
+
+ info("Show the tooltip and check the tooltip container height.");
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ panelRect = tooltip.container.getBoundingClientRect();
+ is(panelRect.height, 160, "Tooltip container has the expected height.");
+
+ await hideTooltip(tooltip);
+
+ info("Update the height and show the tooltip again");
+ tooltipContent.style.cssText = "width: auto; height: 165px; background: red;";
+
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ panelRect = tooltip.container.getBoundingClientRect();
+ is(
+ panelRect.height,
+ 165,
+ "Tooltip container has the expected updated height."
+ );
+
+ await hideTooltip(tooltip);
+
+ info(
+ "Check that refreshing the tooltip when it overflows does keep scroll position"
+ );
+ // Set the tooltip panel to overflow. Some consumers of the HTMLTooltip are doing that
+ // via CSS (e.g. the iframe dropdown, the context selector, …).
+ tooltip.panel.style.overflowY = "auto";
+ tooltipContent.style.cssText =
+ "width: auto; height: 3000px; background: tomato;";
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ Assert.greater(
+ tooltip.panel.scrollHeight,
+ tooltip.panel.clientHeight,
+ "Tooltip overflows"
+ );
+
+ const scrollPosition = 500;
+ tooltip.panel.scrollTop = scrollPosition;
+
+ await showTooltip(tooltip, doc.getElementById("box1"));
+ is(
+ tooltip.panel.scrollTop,
+ scrollPosition,
+ "scroll position was kept during the update"
+ );
+ await hideTooltip(tooltip);
+
+ tooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_hover.js b/devtools/client/shared/test/browser_html_tooltip_hover.js
new file mode 100644
index 0000000000..c7d055ba35
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_hover.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the TooltipToggle helper class for HTMLTooltip
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_hover.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+add_task(async function () {
+ const { doc } = await createHost("bottom", TEST_URI);
+ // Wait for full page load before synthesizing events on the page.
+ await waitUntil(() => doc.readyState === "complete");
+
+ const width = 100,
+ height = 50;
+ const tooltipContent = doc.createElementNS(HTML_NS, "div");
+ tooltipContent.textContent = "tooltip";
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: false });
+ tooltip.panel.appendChild(tooltipContent);
+ tooltip.setContentSize({ width, height });
+
+ const container = doc.getElementById("container");
+ tooltip.startTogglingOnHover(container, () => true);
+
+ info("Hover on each of the 4 boxes, expect the tooltip to appear");
+ async function showAndCheck(boxId, position) {
+ info(`Show tooltip on ${boxId}`);
+ const box = doc.getElementById(boxId);
+ const shown = tooltip.once("shown");
+ EventUtils.synthesizeMouseAtCenter(
+ box,
+ { type: "mousemove" },
+ doc.defaultView
+ );
+ await shown;
+ checkTooltipGeometry(tooltip, box, { position, width, height });
+ }
+
+ await showAndCheck("box1", "bottom");
+ await showAndCheck("box2", "bottom");
+ await showAndCheck("box3", "top");
+ await showAndCheck("box4", "top");
+
+ info("Move out of the container");
+ const hidden = tooltip.once("hidden");
+ EventUtils.synthesizeMouseAtCenter(
+ container,
+ { type: "mousemove" },
+ doc.defaultView
+ );
+ await hidden;
+
+ info("Destroy the tooltip and finish");
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip_offset.js b/devtools/client/shared/test/browser_html_tooltip_offset.js
new file mode 100644
index 0000000000..d7d7e1200e
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_offset.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+"use strict";
+
+/**
+ * Test the HTMLTooltip can be displayed with vertical and horizontal offsets.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Test a tooltip is not closed when clicking inside itself");
+
+ const box1 = doc.getElementById("box1");
+ const box2 = doc.getElementById("box2");
+ const box3 = doc.getElementById("box3");
+ const box4 = doc.getElementById("box4");
+
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: false });
+
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "100px";
+ div.style.boxSizing = "border-box";
+ div.textContent = "tooltip";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: 50, height: 100 });
+
+ info("Display the tooltip on box1.");
+ await showTooltip(tooltip, box1, { x: 5, y: 10 });
+
+ let panelRect = tooltip.container.getBoundingClientRect();
+ let anchorRect = box1.getBoundingClientRect();
+
+ // Tooltip will be displayed below box1
+ is(panelRect.top, anchorRect.bottom + 10, "Tooltip top has 10px offset");
+ is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+ is(panelRect.height, 100, "Tooltip height is at 100px as expected");
+
+ info("Display the tooltip on box2.");
+ await showTooltip(tooltip, box2, { x: 5, y: 10 });
+
+ panelRect = tooltip.container.getBoundingClientRect();
+ anchorRect = box2.getBoundingClientRect();
+
+ // Tooltip will be displayed below box2, but can't be fully displayed because of the
+ // offset
+ is(panelRect.top, anchorRect.bottom + 10, "Tooltip top has 10px offset");
+ is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+ is(panelRect.height, 90, "Tooltip height is only 90px");
+
+ info("Display the tooltip on box3.");
+ await showTooltip(tooltip, box3, { x: 5, y: 10 });
+
+ panelRect = tooltip.container.getBoundingClientRect();
+ anchorRect = box3.getBoundingClientRect();
+
+ // Tooltip will be displayed above box3, but can't be fully displayed because of the
+ // offset
+ is(
+ panelRect.bottom,
+ anchorRect.top - 10,
+ "Tooltip bottom is 10px above anchor"
+ );
+ is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+ is(panelRect.height, 90, "Tooltip height is only 90px");
+
+ info("Display the tooltip on box4.");
+ await showTooltip(tooltip, box4, { x: 5, y: 10 });
+
+ panelRect = tooltip.container.getBoundingClientRect();
+ anchorRect = box4.getBoundingClientRect();
+
+ // Tooltip will be displayed above box4
+ is(
+ panelRect.bottom,
+ anchorRect.top - 10,
+ "Tooltip bottom is 10px above anchor"
+ );
+ is(panelRect.left, anchorRect.left + 5, "Tooltip left has 5px offset");
+ is(panelRect.height, 100, "Tooltip height is at 100px as expected");
+
+ await hideTooltip(tooltip);
+
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip_resize.js b/devtools/client/shared/test/browser_html_tooltip_resize.js
new file mode 100644
index 0000000000..145d2582ca
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_resize.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+"use strict";
+
+/**
+ * Test the HTMLTooltip can be resized.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+const TOOLBOX_WIDTH = 500;
+
+add_task(async function () {
+ await pushPref("devtools.toolbox.sidebar.width", TOOLBOX_WIDTH);
+
+ // Open the host on the right so that the doorhangers hang right.
+ const { doc } = await createHost("right", TEST_URI);
+
+ info("Test resizing of a tooltip");
+
+ const tooltip = new HTMLTooltip(doc, {
+ useXulWrapper: true,
+ type: "doorhanger",
+ });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.textContent = "tooltip";
+ div.style.cssText = "width: 100px; height: 40px";
+ tooltip.panel.appendChild(div);
+
+ const box1 = doc.getElementById("box1");
+
+ await showTooltip(tooltip, box1, { position: "top" });
+
+ // Get the original position of the panel and arrow.
+ const originalPanelBounds = tooltip.panel
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+ const originalArrowBounds = tooltip.arrow
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+
+ // Resize the content
+ div.style.cssText = "width: 200px; height: 30px";
+ tooltip.show(box1, { position: "top" });
+
+ // The panel should have moved 100px to the left and 10px down
+ const updatedPanelBounds = tooltip.panel
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+
+ const panelXMovement =
+ `panel right: ${originalPanelBounds.right} -> ` + updatedPanelBounds.right;
+ Assert.strictEqual(
+ Math.round(updatedPanelBounds.right - originalPanelBounds.right),
+ 100,
+ `Panel should have moved 100px to the right (actual: ${panelXMovement})`
+ );
+
+ const panelYMovement =
+ `panel top: ${originalPanelBounds.top} -> ` + updatedPanelBounds.top;
+ Assert.strictEqual(
+ Math.round(updatedPanelBounds.top - originalPanelBounds.top),
+ 10,
+ `Panel should have moved 10px down (actual: ${panelYMovement})`
+ );
+
+ // The arrow should be in the same position
+ const updatedArrowBounds = tooltip.arrow
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+
+ const arrowXMovement =
+ `arrow left: ${originalArrowBounds.left} -> ` + updatedArrowBounds.left;
+ Assert.strictEqual(
+ Math.round(updatedArrowBounds.left - originalArrowBounds.left),
+ 0,
+ `Arrow should not have moved (actual: ${arrowXMovement})`
+ );
+
+ const arrowYMovement =
+ `arrow top: ${originalArrowBounds.top} -> ` + updatedArrowBounds.top;
+ Assert.strictEqual(
+ Math.round(updatedArrowBounds.top - originalArrowBounds.top),
+ 0,
+ `Arrow should not have moved (actual: ${arrowYMovement})`
+ );
+
+ await hideTooltip(tooltip);
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip_rtl.js b/devtools/client/shared/test/browser_html_tooltip_rtl.js
new file mode 100644
index 0000000000..11fe3932f3
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_rtl.js
@@ -0,0 +1,226 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+"use strict";
+
+/**
+ * Test the HTMLTooltip anchor alignment changes with the anchor direction.
+ * - should be aligned to the right of RTL anchors
+ * - should be aligned to the left of LTR anchors
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_rtl.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+const TOOLBOX_WIDTH = 500;
+const TOOLTIP_WIDTH = 150;
+const TOOLTIP_HEIGHT = 30;
+
+add_task(async function () {
+ await pushPref("devtools.toolbox.sidebar.width", TOOLBOX_WIDTH);
+
+ const { doc } = await createHost("right", TEST_URI);
+
+ info("Test the positioning of tooltips in RTL and LTR directions");
+
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: false });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.textContent = "tooltip";
+ div.style.cssText = "box-sizing: border-box; border: 1px solid black";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT });
+
+ await testRtlAnchors(doc, tooltip);
+ await testLtrAnchors(doc, tooltip);
+ await hideTooltip(tooltip);
+
+ tooltip.destroy();
+
+ await testRtlArrow(doc);
+});
+
+async function testRtlAnchors(doc, tooltip) {
+ /*
+ * The layout of the test page is as follows:
+ * _______________________________
+ * | toolbox |
+ * | _____ _____ _____ _____ |
+ * || | | | | | | ||
+ * || box1| | box2| | box3| | box4||
+ * ||_____| |_____| |_____| |_____||
+ * |_______________________________|
+ *
+ * - box1 is aligned with the left edge of the toolbox
+ * - box2 is displayed right after box1
+ * - total toolbox width is 500px so each box is 125px wide
+ */
+
+ const box1 = doc.getElementById("box1");
+ const box2 = doc.getElementById("box2");
+
+ info("Display the tooltip on box1.");
+ await showTooltip(tooltip, box1, { position: "bottom" });
+
+ let panelRect = tooltip.container.getBoundingClientRect();
+ let anchorRect = box1.getBoundingClientRect();
+
+ // box1 uses RTL direction, so the tooltip should be aligned with the right edge of the
+ // anchor, but it is shifted to the right to fit in the toolbox.
+ is(panelRect.left, 0, "Tooltip is aligned with left edge of the toolbox");
+ is(
+ panelRect.top,
+ anchorRect.bottom,
+ "Tooltip aligned with the anchor bottom edge"
+ );
+ is(
+ panelRect.height,
+ TOOLTIP_HEIGHT,
+ "Tooltip height is at 100px as expected"
+ );
+
+ info("Display the tooltip on box2.");
+ await showTooltip(tooltip, box2, { position: "bottom" });
+
+ panelRect = tooltip.container.getBoundingClientRect();
+ anchorRect = box2.getBoundingClientRect();
+
+ // box2 uses RTL direction, so the tooltip is aligned with the right edge of the anchor
+ is(
+ Math.round(panelRect.right),
+ Math.round(anchorRect.right),
+ "Tooltip is aligned with right edge of anchor"
+ );
+ is(
+ panelRect.top,
+ anchorRect.bottom,
+ "Tooltip aligned with the anchor bottom edge"
+ );
+ is(
+ panelRect.height,
+ TOOLTIP_HEIGHT,
+ "Tooltip height is at 100px as expected"
+ );
+}
+
+async function testLtrAnchors(doc, tooltip) {
+ /*
+ * The layout of the test page is as follows:
+ * _______________________________
+ * | toolbox |
+ * | _____ _____ _____ _____ |
+ * || | | | | | | ||
+ * || box1| | box2| | box3| | box4||
+ * ||_____| |_____| |_____| |_____||
+ * |_______________________________|
+ *
+ * - box3 is is displayed right after box2
+ * - box4 is aligned with the right edge of the toolbox
+ * - total toolbox width is 500px so each box is 125px wide
+ */
+
+ const box3 = doc.getElementById("box3");
+ const box4 = doc.getElementById("box4");
+
+ info("Display the tooltip on box3.");
+ await showTooltip(tooltip, box3, { position: "bottom" });
+
+ let panelRect = tooltip.container.getBoundingClientRect();
+ let anchorRect = box3.getBoundingClientRect();
+
+ // box3 uses LTR direction, so the tooltip is aligned with the left edge of the anchor.
+ is(
+ Math.round(panelRect.left),
+ Math.round(anchorRect.left),
+ "Tooltip is aligned with left edge of anchor"
+ );
+ is(
+ panelRect.top,
+ anchorRect.bottom,
+ "Tooltip aligned with the anchor bottom edge"
+ );
+ is(
+ panelRect.height,
+ TOOLTIP_HEIGHT,
+ "Tooltip height is at 100px as expected"
+ );
+
+ info("Display the tooltip on box4.");
+ await showTooltip(tooltip, box4, { position: "bottom" });
+
+ panelRect = tooltip.container.getBoundingClientRect();
+ anchorRect = box4.getBoundingClientRect();
+
+ // box4 uses LTR direction, so the tooltip should be aligned with the left edge of the
+ // anchor, but it is shifted to the left to fit in the toolbox.
+ is(
+ panelRect.right,
+ TOOLBOX_WIDTH,
+ "Tooltip is aligned with right edge of toolbox"
+ );
+ is(
+ panelRect.top,
+ anchorRect.bottom,
+ "Tooltip aligned with the anchor bottom edge"
+ );
+ is(
+ panelRect.height,
+ TOOLTIP_HEIGHT,
+ "Tooltip height is at 100px as expected"
+ );
+}
+
+async function testRtlArrow(doc) {
+ // Set up the arrow-style tooltip
+ const arrowTooltip = new HTMLTooltip(doc, {
+ type: "arrow",
+ useXulWrapper: false,
+ });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.textContent = "tooltip";
+ div.style.cssText = "box-sizing: border-box; border: 1px solid black";
+ arrowTooltip.panel.appendChild(div);
+ arrowTooltip.setContentSize({
+ width: TOOLTIP_WIDTH,
+ height: TOOLTIP_HEIGHT,
+ });
+
+ // box2 uses RTL direction and is far enough from the edge that the arrow
+ // should not be squashed in the wrong direction.
+ const box2 = doc.getElementById("box2");
+
+ info("Display the arrow tooltip on box2.");
+ await showTooltip(arrowTooltip, box2, { position: "top" });
+
+ const arrow = arrowTooltip.arrow;
+ ok(arrow, "Tooltip has an arrow");
+
+ const panelRect = arrowTooltip.container.getBoundingClientRect();
+ const arrowRect = arrow.getBoundingClientRect();
+
+ // The arrow should be offset from the right edge, but still closer to the
+ // right edge than the left edge.
+ Assert.less(
+ arrowRect.right,
+ panelRect.right,
+ "Right edge of the arrow " +
+ `(${arrowRect.right}) is less than the right edge of the panel ` +
+ `(${panelRect.right})`
+ );
+ const rightMargin = panelRect.right - arrowRect.right;
+ const leftMargin = arrowRect.left - panelRect.right;
+ Assert.greater(
+ rightMargin,
+ leftMargin,
+ "Arrow should be closer to the right side of " +
+ ` the panel (margin: ${rightMargin}) than the left side ` +
+ ` (margin: ${leftMargin})`
+ );
+
+ await hideTooltip(arrowTooltip);
+ arrowTooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_screen_edge.js b/devtools/client/shared/test/browser_html_tooltip_screen_edge.js
new file mode 100644
index 0000000000..e27c1e808b
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_screen_edge.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip "doorhanger" when the anchor is near the edge of the
+ * screen and uses a XUL wrapper. The XUL panel cannot be displayed off screen
+ * at all so this verifies that the calculated position of the tooltip always
+ * ensure that the whole tooltip is rendered on the screen
+ *
+ * See Bug 1590408
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip_doorhanger-01.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+
+ const { win, doc } = await createHost("bottom", TEST_URI);
+
+ const originalTop = win.screenTop;
+ const originalLeft = win.screenLeft;
+ const screenWidth = win.screen.width;
+ await moveWindowTo(win, screenWidth - win.outerWidth, originalTop);
+
+ registerCleanupFunction(async () => {
+ info(`Restore original window position. ${originalLeft}, ${originalTop}`);
+ await moveWindowTo(win, originalLeft, originalTop);
+ });
+
+ info("Create a doorhanger HTML tooltip with XULPanel");
+ const tooltip = new HTMLTooltip(doc, {
+ type: "doorhanger",
+ useXulWrapper: true,
+ });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.width = "200px";
+ div.style.height = "35px";
+ tooltip.panel.appendChild(div);
+
+ const anchor = doc.querySelector("#anchor5");
+
+ info("Display the tooltip on an anchor.");
+ await showTooltip(tooltip, anchor);
+
+ const arrow = tooltip.arrow;
+ ok(arrow, "Tooltip has an arrow");
+
+ const panelBounds = tooltip.panel
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+
+ const anchorBounds = anchor.getBoxQuads({ relativeTo: doc })[0].getBounds();
+ ok(
+ anchorBounds.left < panelBounds.right &&
+ panelBounds.left < anchorBounds.right,
+ `The tooltip panel is over (ie intersects) the anchor horizontally: ` +
+ `${anchorBounds.left} < ${panelBounds.right} and ` +
+ `${panelBounds.left} < ${anchorBounds.right}`
+ );
+
+ await hideTooltip(tooltip);
+
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip_variable-height.js b/devtools/client/shared/test/browser_html_tooltip_variable-height.js
new file mode 100644
index 0000000000..69f6d17aed
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_variable-height.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip content can have a variable height.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const CONTAINER_HEIGHT = 300;
+const CONTAINER_WIDTH = 200;
+const TOOLTIP_HEIGHT = 50;
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+add_task(async function () {
+ // Force the toolbox to be 400px tall => 50px for each box.
+ await pushPref("devtools.toolbox.footer.height", 400);
+
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: false });
+ info("Set tooltip content 50px tall, but request a container 200px tall");
+ const tooltipContent = doc.createElementNS(HTML_NS, "div");
+ tooltipContent.style.cssText =
+ "height: " + TOOLTIP_HEIGHT + "px; background: red;";
+ tooltip.panel.appendChild(tooltipContent);
+ tooltip.setContentSize({ width: CONTAINER_WIDTH, height: Infinity });
+
+ info("Show the tooltip and check the container and panel height.");
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ const containerRect = tooltip.container.getBoundingClientRect();
+ const panelRect = tooltip.panel.getBoundingClientRect();
+ is(
+ containerRect.height,
+ CONTAINER_HEIGHT,
+ "Tooltip container has the expected height."
+ );
+ is(
+ panelRect.height,
+ TOOLTIP_HEIGHT,
+ "Tooltip panel has the expected height."
+ );
+
+ info("Click below the tooltip panel but in the tooltip filler element.");
+ let onHidden = once(tooltip, "hidden");
+ EventUtils.synthesizeMouse(tooltip.container, 100, 100, {}, doc.defaultView);
+ await onHidden;
+
+ info("Show the tooltip one more time, and increase the content height");
+ await showTooltip(tooltip, doc.getElementById("box1"));
+ tooltipContent.style.height = 2 * CONTAINER_HEIGHT + "px";
+
+ info(
+ "Click at the same coordinates as earlier, this time it should hit the tooltip."
+ );
+ const onPanelClick = once(tooltip.panel, "click");
+ EventUtils.synthesizeMouse(tooltip.container, 100, 100, {}, doc.defaultView);
+ await onPanelClick;
+ is(tooltip.isVisible(), true, "Tooltip is still visible");
+
+ info("Click above the tooltip container, the tooltip should be closed.");
+ onHidden = once(tooltip, "hidden");
+ EventUtils.synthesizeMouse(tooltip.container, 100, -10, {}, doc.defaultView);
+ await onHidden;
+
+ tooltip.destroy();
+});
diff --git a/devtools/client/shared/test/browser_html_tooltip_width-auto.js b/devtools/client/shared/test/browser_html_tooltip_width-auto.js
new file mode 100644
index 0000000000..8aaf969d36
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_width-auto.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip content can automatically calculate its width based on content.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+let useXulWrapper;
+
+add_task(async function () {
+ await addTab("about:blank");
+ const { doc } = await createHost("bottom", TEST_URI);
+
+ info("Run tests for a Tooltip without using a XUL panel");
+ useXulWrapper = false;
+ await runTests(doc);
+
+ info("Run tests for a Tooltip with a XUL panel");
+ useXulWrapper = true;
+ await runTests(doc);
+});
+
+async function runTests(doc) {
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper });
+ info("Create tooltip content width to 150px");
+ const tooltipContent = doc.createElementNS(HTML_NS, "div");
+ tooltipContent.style.cssText = "height: 100%; width: 150px; background: red;";
+
+ info("Set tooltip content using width:auto");
+ tooltip.panel.appendChild(tooltipContent);
+ tooltip.setContentSize({ width: "auto", height: 50 });
+
+ info("Show the tooltip and check the tooltip panel width.");
+ await showTooltip(tooltip, doc.getElementById("box1"));
+
+ const containerRect = tooltip.container.getBoundingClientRect();
+ is(containerRect.width, 150, "Tooltip container has the expected width.");
+
+ await hideTooltip(tooltip);
+
+ tooltip.destroy();
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js b/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
new file mode 100644
index 0000000000..1eae311d5a
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_xul-wrapper.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip can overflow out of the toolbox when using a XUL panel wrapper.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip-05.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+loadHelperScript("helper_html_tooltip.js");
+
+// The test toolbox will be 200px tall, the anchors are 50px tall, therefore, the maximum
+// tooltip height that could fit in the toolbox is 150px. Setting 160px, the tooltip will
+// either have to overflow or to be resized.
+const TOOLTIP_HEIGHT = 160;
+const TOOLTIP_WIDTH = 200;
+
+add_task(async function () {
+ // Force the toolbox to be 200px high;
+ await pushPref("devtools.toolbox.footer.height", 200);
+
+ const { win, doc } = await createHost("bottom", TEST_URI);
+
+ info("Resize and move the window to have space below.");
+ const originalWidth = win.outerWidth;
+ const originalHeight = win.outerHeight;
+ win.resizeBy(-100, -200);
+
+ const originalTop = win.screenTop;
+ const originalLeft = win.screenLeft;
+ await moveWindowTo(win, 100, 100);
+
+ registerCleanupFunction(async () => {
+ info("Restore original window dimensions and position.");
+ win.resizeTo(originalWidth, originalHeight);
+ await moveWindowTo(win, originalLeft, originalTop);
+ });
+
+ info("Create HTML tooltip");
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: true });
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "200px";
+ div.style.background = "red";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: TOOLTIP_WIDTH, height: TOOLTIP_HEIGHT });
+
+ const box1 = doc.getElementById("box1");
+
+ // Above box1: check that the tooltip can overflow onto the content page.
+ info("Display the tooltip above box1.");
+ await showTooltip(tooltip, box1, { position: "top" });
+ checkTooltip(tooltip, "top", TOOLTIP_HEIGHT);
+ await hideTooltip(tooltip);
+
+ // Below box1: check that the tooltip can overflow out of the browser window.
+ info("Display the tooltip below box1.");
+ await showTooltip(tooltip, box1, { position: "bottom" });
+ checkTooltip(tooltip, "bottom", TOOLTIP_HEIGHT);
+ await hideTooltip(tooltip);
+
+ is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+ tooltip.destroy();
+});
+
+function checkTooltip(tooltip, position, height) {
+ is(tooltip.position, position, "Actual tooltip position is " + position);
+ const rect = tooltip.container.getBoundingClientRect();
+ is(rect.height, height, "Actual tooltip height is " + height);
+ // Testing the actual left/top offsets is not relevant here as it is handled by the XUL
+ // panel.
+}
diff --git a/devtools/client/shared/test/browser_html_tooltip_zoom.js b/devtools/client/shared/test/browser_html_tooltip_zoom.js
new file mode 100644
index 0000000000..f30aa586d3
--- /dev/null
+++ b/devtools/client/shared/test/browser_html_tooltip_zoom.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_html_tooltip.js */
+
+"use strict";
+
+/**
+ * Test the HTMLTooltip is displayed correct position if content is zoomed in.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const TEST_URI = CHROME_URL_ROOT + "doc_html_tooltip.xhtml";
+
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+
+function getTooltipContent(doc) {
+ const div = doc.createElementNS(HTML_NS, "div");
+ div.style.height = "50px";
+ div.style.boxSizing = "border-box";
+ div.style.backgroundColor = "red";
+ div.textContent = "tooltip";
+ return div;
+}
+
+add_task(async function () {
+ const { host, doc } = await createHost("window", TEST_URI);
+
+ // Creating a window host is not correctly waiting when DevTools run in content frame
+ // See Bug 1571421.
+ await wait(1000);
+
+ const zoom = 1.5;
+ await pushPref("devtools.toolbox.zoomValue", zoom.toString(10));
+
+ // Change this xul zoom to the x1.5 since this test doesn't use the toolbox preferences.
+ host.frame.docShell.browsingContext.fullZoom = zoom;
+ const tooltip = new HTMLTooltip(doc, { useXulWrapper: true });
+
+ info("Set tooltip content");
+ tooltip.panel.appendChild(getTooltipContent(doc));
+ tooltip.setContentSize({ width: 100, height: 50 });
+
+ is(tooltip.isVisible(), false, "Tooltip is not visible");
+
+ info("Show the tooltip and check the expected events are fired.");
+ const onShown = tooltip.once("shown");
+ tooltip.show(doc.getElementById("box1"));
+ await onShown;
+
+ const menuRect = doc
+ .querySelector(".tooltip-xul-wrapper > .tooltip-container")
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+ const anchorRect = doc
+ .getElementById("box1")
+ .getBoxQuads({ relativeTo: doc })[0]
+ .getBounds();
+ const xDelta = Math.abs(menuRect.left - anchorRect.left);
+ const yDelta = Math.abs(menuRect.top - anchorRect.bottom);
+
+ Assert.less(xDelta, 1, "xDelta: " + xDelta + ".");
+ Assert.less(yDelta, 1, "yDelta: " + yDelta + ".");
+
+ info("Hide the tooltip and check the expected events are fired.");
+
+ const onPopupHidden = tooltip.once("hidden");
+ tooltip.hide();
+ await onPopupHidden;
+
+ tooltip.destroy();
+ await host.destroy();
+});
diff --git a/devtools/client/shared/test/browser_inplace-editor-01.js b/devtools/client/shared/test/browser_inplace-editor-01.js
new file mode 100644
index 0000000000..b919fca946
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor-01.js
@@ -0,0 +1,202 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor behavior.
+
+add_task(async function () {
+ await addTab("data:text/html;charset=utf-8,inline editor tests");
+ const { host, doc } = await createHost();
+
+ await testMultipleInitialization(doc);
+ await testReturnCommit(doc);
+ await testBlurCommit(doc);
+ await testAdvanceCharCommit(doc);
+ await testAdvanceCharsFunction(doc);
+ await testEscapeCancel(doc);
+ await testInputAriaLabel(doc);
+
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function testMultipleInitialization(doc) {
+ doc.body.innerHTML = "";
+ const options = {};
+ const span = (options.element = createSpan(doc));
+
+ info("Creating multiple inplace-editor fields");
+ editableField(options);
+ editableField(options);
+
+ info("Clicking on the inplace-editor field to turn to edit mode");
+ span.click();
+
+ is(span.style.display, "none", "The original <span> is hidden");
+ is(doc.querySelectorAll("input").length, 1, "Only one <input>");
+ is(
+ doc.querySelectorAll("span").length,
+ 2,
+ "Correct number of <span> elements"
+ );
+ is(
+ doc.querySelectorAll("span.autosizer").length,
+ 1,
+ "There is an autosizer element"
+ );
+}
+
+function testReturnCommit(doc) {
+ info("Testing that pressing return commits the new value");
+ return new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ initial: "explicit initial",
+ start(editor) {
+ is(
+ editor.input.value,
+ "explicit initial",
+ "Explicit initial value should be used."
+ );
+ editor.input.value = "Test Value";
+ EventUtils.sendKey("return");
+ },
+ done: onDone("Test Value", true, resolve),
+ },
+ doc
+ );
+ });
+}
+
+function testBlurCommit(doc) {
+ info("Testing that bluring the field commits the new value");
+ return new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ start(editor) {
+ is(editor.input.value, "Edit Me!", "textContent of the span used.");
+ editor.input.value = "Test Value";
+ editor.input.blur();
+ },
+ done: onDone("Test Value", true, resolve),
+ },
+ doc,
+ "Edit Me!"
+ );
+ });
+}
+
+function testAdvanceCharCommit(doc) {
+ info("Testing that configured advanceChars commit the new value");
+ return new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ advanceChars: ":",
+ start(editor) {
+ EventUtils.sendString("Test:");
+ },
+ done: onDone("Test", true, resolve),
+ },
+ doc
+ );
+ });
+}
+
+function testAdvanceCharsFunction(doc) {
+ info("Testing advanceChars as a function");
+ return new Promise(resolve => {
+ let firstTime = true;
+
+ createInplaceEditorAndClick(
+ {
+ initial: "",
+ advanceChars(charCode, text, insertionPoint) {
+ if (charCode !== KeyboardEvent.DOM_VK_COLON) {
+ return false;
+ }
+ if (firstTime) {
+ firstTime = false;
+ return false;
+ }
+
+ // Just to make sure we check it somehow.
+ return !!text.length;
+ },
+ start(editor) {
+ for (const ch of ":Test:") {
+ EventUtils.sendChar(ch);
+ }
+ },
+ done: onDone(":Test", true, resolve),
+ },
+ doc
+ );
+ });
+}
+
+function testEscapeCancel(doc) {
+ info("Testing that escape cancels the new value");
+ return new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ initial: "initial text",
+ start(editor) {
+ editor.input.value = "Test Value";
+ EventUtils.sendKey("escape");
+ },
+ done: onDone("initial text", false, resolve),
+ },
+ doc
+ );
+ });
+}
+
+function testInputAriaLabel(doc) {
+ info("Testing that inputAriaLabel works as expected");
+ doc.body.innerHTML = "";
+
+ let element = createSpan(doc);
+ editableField({
+ element,
+ inputAriaLabel: "TEST_ARIA_LABEL",
+ });
+
+ info("Clicking on the inplace-editor field to turn to edit mode");
+ element.click();
+ let input = doc.querySelector("input");
+ is(
+ input.getAttribute("aria-label"),
+ "TEST_ARIA_LABEL",
+ "Input has expected aria-label"
+ );
+
+ info("Testing that inputAriaLabelledBy works as expected");
+ doc.body.innerHTML = "";
+ element = createSpan(doc);
+ editableField({
+ element,
+ inputAriaLabelledBy: "TEST_ARIA_LABELLED_BY",
+ });
+
+ info("Clicking on the inplace-editor field to turn to edit mode");
+ element.click();
+ input = doc.querySelector("input");
+ is(
+ input.getAttribute("aria-labelledby"),
+ "TEST_ARIA_LABELLED_BY",
+ "Input has expected aria-labelledby"
+ );
+}
+
+function onDone(value, isCommit, resolve) {
+ return function (actualValue, actualCommit) {
+ info("Inplace-editor's done callback executed, checking its state");
+ is(actualValue, value, "The value is correct");
+ is(actualCommit, isCommit, "The commit boolean is correct");
+ resolve();
+ };
+}
diff --git a/devtools/client/shared/test/browser_inplace-editor-02.js b/devtools/client/shared/test/browser_inplace-editor-02.js
new file mode 100644
index 0000000000..9ecb0bca02
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor-02.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+loadHelperScript("helper_inplace_editor.js");
+
+// Test that the trimOutput option for the inplace editor works correctly.
+
+add_task(async function () {
+ await addTab("data:text/html;charset=utf-8,inline editor tests");
+ const { host, doc } = await createHost();
+
+ await testNonTrimmed(doc);
+ await testTrimmed(doc);
+
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function testNonTrimmed(doc) {
+ info("Testing the trimOutput=false option");
+ return new Promise(resolve => {
+ const initial = "\nMultiple\nLines\n";
+ const changed = " \nMultiple\nLines\n with more whitespace ";
+ createInplaceEditorAndClick(
+ {
+ trimOutput: false,
+ multiline: true,
+ initial,
+ start(editor) {
+ is(
+ editor.input.value,
+ initial,
+ "Explicit initial value should be used."
+ );
+ editor.input.value = changed;
+ EventUtils.sendKey("return");
+ },
+ done: onDone(changed, true, resolve),
+ },
+ doc
+ );
+ });
+}
+
+function testTrimmed(doc) {
+ info("Testing the trimOutput=true option (default value)");
+ return new Promise(resolve => {
+ const initial = "\nMultiple\nLines\n";
+ const changed = " \nMultiple\nLines\n with more whitespace ";
+ createInplaceEditorAndClick(
+ {
+ initial,
+ multiline: true,
+ start(editor) {
+ is(
+ editor.input.value,
+ initial,
+ "Explicit initial value should be used."
+ );
+ editor.input.value = changed;
+ EventUtils.sendKey("return");
+ },
+ done: onDone(changed.trim(), true, resolve),
+ },
+ doc
+ );
+ });
+}
+
+function onDone(value, isCommit, resolve) {
+ return function (actualValue, actualCommit) {
+ info("Inplace-editor's done callback executed, checking its state");
+ is(actualValue, value, "The value is correct");
+ is(actualCommit, isCommit, "The commit boolean is correct");
+ resolve();
+ };
+}
diff --git a/devtools/client/shared/test/browser_inplace-editor_autoclose_parentheses.js b/devtools/client/shared/test/browser_inplace-editor_autoclose_parentheses.js
new file mode 100644
index 0000000000..79f4a2d14a
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_autoclose_parentheses.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+const {
+ InplaceEditor,
+} = require("resource://devtools/client/shared/inplace-editor.js");
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor closes parentheses automatically.
+
+// format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// selected suggestion index (-1 if popup is hidden),
+// number of suggestions in the popup (0 if popup is hidden),
+// ]
+const testData = [
+ ["u", "u", -1, 0],
+ ["r", "ur", -1, 0],
+ ["l", "url", -1, 0],
+ ["(", "url()", -1, 0],
+ ["v", "url(v)", -1, 0],
+ ["a", "url(va)", -1, 0],
+ ["r", "url(var)", -1, 0],
+ ["(", "url(var())", -1, 0],
+ ["-", "url(var(-))", -1, 0],
+ ["-", "url(var(--))", -1, 0],
+ ["a", "url(var(--a))", -1, 0],
+ [")", "url(var(--a))", -1, 0],
+ [")", "url(var(--a))", -1, 0],
+];
+
+add_task(async function () {
+ await addTab(
+ "data:text/html;charset=utf-8," + "inplace editor parentheses autoclose"
+ );
+ const { host, doc } = await createHost();
+
+ const popup = new AutocompletePopup(doc, { autoSelect: true });
+ await new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ start: runPropertyAutocompletionTest,
+ contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
+ property: {
+ name: "background-image",
+ },
+ cssProperties: {
+ // No need to test autocompletion here, return an empty array.
+ getNames: () => [],
+ getValues: () => [],
+ },
+ cssVariables: new Map(),
+ done: resolve,
+ popup,
+ },
+ doc
+ );
+ });
+
+ popup.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+const runPropertyAutocompletionTest = async function (editor) {
+ info("Starting to test for css property completion");
+ for (const data of testData) {
+ await testCompletion(data, editor);
+ }
+ EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+};
diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js
new file mode 100644
index 0000000000..1ed10778c3
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_01.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+const {
+ InplaceEditor,
+} = require("resource://devtools/client/shared/inplace-editor.js");
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor autocomplete popup for CSS properties suggestions.
+// Using a mocked list of CSS properties to avoid test failures linked to
+// engine changes (new property, removed property, ...).
+
+// format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// selected suggestion index (-1 if popup is hidden),
+// number of suggestions in the popup (0 if popup is hidden),
+// ]
+const testData = [
+ ["b", "border", 1, 3],
+ ["VK_DOWN", "box-sizing", 2, 3],
+ ["VK_DOWN", "background", 0, 3],
+ ["VK_DOWN", "border", 1, 3],
+ ["VK_BACK_SPACE", "b", -1, 0],
+ ["VK_BACK_SPACE", "", -1, 0],
+ ["VK_DOWN", "background", 0, 6],
+ ["VK_LEFT", "background", -1, 0],
+];
+
+const mockValues = {
+ background: [],
+ border: [],
+ "box-sizing": [],
+ color: [],
+ display: [],
+ visibility: [],
+};
+
+add_task(async function () {
+ await addTab(
+ "data:text/html;charset=utf-8," + "inplace editor CSS property autocomplete"
+ );
+ const { host, doc } = await createHost();
+
+ const popup = new AutocompletePopup(doc, { autoSelect: true });
+ await new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ start: runPropertyAutocompletionTest,
+ contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY,
+ done: resolve,
+ popup,
+ cssProperties: {
+ getNames: () => Object.keys(mockValues),
+ getValues: propertyName => mockValues[propertyName] || [],
+ },
+ },
+ doc
+ );
+ });
+
+ popup.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+const runPropertyAutocompletionTest = async function (editor) {
+ info("Starting to test for css property completion");
+ for (const data of testData) {
+ await testCompletion(data, editor);
+ }
+
+ EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+};
diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js
new file mode 100644
index 0000000000..44fbffc207
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_02.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+const {
+ InplaceEditor,
+} = require("resource://devtools/client/shared/inplace-editor.js");
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor autocomplete popup for CSS values suggestions.
+// Using a mocked list of CSS properties to avoid test failures linked to
+// engine changes (new property, removed property, ...).
+
+// format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// selected suggestion index (-1 if popup is hidden),
+// number of suggestions in the popup (0 if popup is hidden),
+// ]
+const testData = [
+ ["b", "block", -1, 0],
+ ["VK_BACK_SPACE", "b", -1, 0],
+ ["VK_BACK_SPACE", "", -1, 0],
+ ["i", "inline", 0, 2],
+ ["VK_DOWN", "inline-block", 1, 2],
+ ["VK_DOWN", "inline", 0, 2],
+ ["VK_LEFT", "inline", -1, 0],
+];
+
+const mockValues = {
+ display: ["block", "flex", "inline", "inline-block", "none"],
+};
+
+add_task(async function () {
+ await addTab(
+ "data:text/html;charset=utf-8," + "inplace editor CSS value autocomplete"
+ );
+ const { host, win, doc } = await createHost();
+
+ const xulDocument = win.top.document;
+ const popup = new AutocompletePopup(xulDocument, { autoSelect: true });
+
+ await new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ start: runAutocompletionTest,
+ contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
+ property: {
+ name: "display",
+ },
+ cssProperties: {
+ getNames: () => Object.keys(mockValues),
+ getValues: propertyName => mockValues[propertyName] || [],
+ },
+ done: resolve,
+ popup,
+ },
+ doc
+ );
+ });
+
+ popup.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+const runAutocompletionTest = async function (editor) {
+ info("Starting to test for css property completion");
+ for (const data of testData) {
+ await testCompletion(data, editor);
+ }
+
+ EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+};
diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
new file mode 100644
index 0000000000..8a27778670
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_css_variable.js
@@ -0,0 +1,104 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+const {
+ InplaceEditor,
+} = require("resource://devtools/client/shared/inplace-editor.js");
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor autocomplete popup for variable suggestions.
+// Using a mocked list of CSS variables to avoid test failures linked to
+// engine changes (new property, removed property, ...).
+// Also using a mocked list of CSS properties to avoid autocompletion when
+// typing in "var"
+
+// Used for representing the expectation of a visible color swatch
+const COLORSWATCH = true;
+// format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// selected suggestion index (-1 if popup is hidden),
+// number of suggestions in the popup (0 if popup is hidden),
+// expected post label corresponding with the input box value,
+// boolean representing if there should be a colour swatch visible,
+// ]
+const testData = [
+ ["v", "v", -1, 0, null, !COLORSWATCH],
+ ["a", "va", -1, 0, null, !COLORSWATCH],
+ ["r", "var", -1, 0, null, !COLORSWATCH],
+ ["(", "var()", -1, 0, null, !COLORSWATCH],
+ ["-", "var(--abc)", 0, 9, "inherit", !COLORSWATCH],
+ ["VK_BACK_SPACE", "var(-)", -1, 0, null, !COLORSWATCH],
+ ["-", "var(--abc)", 0, 9, "inherit", !COLORSWATCH],
+ ["VK_DOWN", "var(--def)", 1, 9, "transparent", !COLORSWATCH],
+ ["VK_DOWN", "var(--ghi)", 2, 9, "#00FF00", COLORSWATCH],
+ ["VK_DOWN", "var(--jkl)", 3, 9, "rgb(255, 0, 0)", COLORSWATCH],
+ ["VK_DOWN", "var(--mno)", 4, 9, "hsl(120, 60%, 70%)", COLORSWATCH],
+ ["VK_DOWN", "var(--pqr)", 5, 9, "BlueViolet", COLORSWATCH],
+ ["VK_DOWN", "var(--stu)", 6, 9, "15px", !COLORSWATCH],
+ ["VK_DOWN", "var(--vwx)", 7, 9, "rgba(255, 0, 0, 0.4)", COLORSWATCH],
+ ["VK_DOWN", "var(--yz)", 8, 9, "hsla(120, 60%, 70%, 0.3)", COLORSWATCH],
+ ["VK_DOWN", "var(--abc)", 0, 9, "inherit", !COLORSWATCH],
+ ["VK_DOWN", "var(--def)", 1, 9, "transparent", !COLORSWATCH],
+ ["VK_DOWN", "var(--ghi)", 2, 9, "#00FF00", COLORSWATCH],
+ ["VK_LEFT", "var(--ghi)", -1, 0, null, !COLORSWATCH],
+];
+
+const CSS_VARIABLES = [
+ ["--abc", "inherit"],
+ ["--def", "transparent"],
+ ["--ghi", "#00FF00"],
+ ["--jkl", "rgb(255, 0, 0)"],
+ ["--mno", "hsl(120, 60%, 70%)"],
+ ["--pqr", "BlueViolet"],
+ ["--stu", "15px"],
+ ["--vwx", "rgba(255, 0, 0, 0.4)"],
+ ["--yz", "hsla(120, 60%, 70%, 0.3)"],
+];
+
+add_task(async function () {
+ await addTab(
+ "data:text/html;charset=utf-8,inplace editor CSS variable autocomplete"
+ );
+ const { host, doc } = await createHost();
+
+ const popup = new AutocompletePopup(doc, { autoSelect: true });
+
+ await new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ start: runAutocompletionTest,
+ contentType: InplaceEditor.CONTENT_TYPES.CSS_VALUE,
+ property: {
+ name: "color",
+ },
+ cssProperties: {
+ getNames: () => [],
+ getValues: () => [],
+ },
+ cssVariables: new Map(CSS_VARIABLES),
+ done: resolve,
+ popup,
+ },
+ doc
+ );
+ });
+
+ popup.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+const runAutocompletionTest = async function (editor) {
+ info("Starting to test for css variable completion");
+ for (const data of testData) {
+ await testCompletion(data, editor);
+ }
+
+ EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+};
diff --git a/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js b/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js
new file mode 100644
index 0000000000..7272ad7091
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_autocomplete_offset.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+const AutocompletePopup = require("resource://devtools/client/shared/autocomplete-popup.js");
+const {
+ InplaceEditor,
+} = require("resource://devtools/client/shared/inplace-editor.js");
+loadHelperScript("helper_inplace_editor.js");
+
+const TEST_URI =
+ CHROME_URL_ROOT + "doc_inplace-editor_autocomplete_offset.xhtml";
+
+// Test the inplace-editor autocomplete popup is aligned with the completed query.
+// Which means when completing "style=display:flex; color:" the popup will aim to be
+// aligned with the ":" next to "color".
+
+// format :
+// [
+// what key to press,
+// expected input box value after keypress,
+// selected suggestion index (-1 if popup is hidden),
+// number of suggestions in the popup (0 if popup is hidden),
+// ]
+// or
+// ["checkPopupOffset"]
+// to measure and test the autocomplete popup left offset.
+const testData = [
+ ["VK_RIGHT", "style=", -1, 0],
+ ["d", "style=display", 1, 2],
+ ["checkPopupOffset"],
+ ["VK_RIGHT", "style=display", -1, 0],
+ [":", "style=display:block", 0, 3],
+ ["checkPopupOffset"],
+ ["f", "style=display:flex", -1, 0],
+ ["VK_RIGHT", "style=display:flex", -1, 0],
+ [";", "style=display:flex;", -1, 0],
+ ["c", "style=display:flex;color", 1, 2],
+ ["checkPopupOffset"],
+ ["VK_RIGHT", "style=display:flex;color", -1, 0],
+ [":", "style=display:flex;color:blue", 0, 2],
+ ["checkPopupOffset"],
+];
+
+const mockValues = {
+ clear: [],
+ color: ["blue", "red"],
+ direction: [],
+ display: ["block", "flex", "none"],
+};
+
+add_task(async function () {
+ await addTab(
+ "data:text/html;charset=utf-8,inplace editor CSS value autocomplete"
+ );
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ const popup = new AutocompletePopup(doc, { autoSelect: true });
+
+ info("Create a CSS_MIXED type autocomplete");
+ await new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ initial: "style=",
+ start: runAutocompletionTest,
+ contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
+ done: resolve,
+ popup,
+ cssProperties: {
+ getNames: () => Object.keys(mockValues),
+ getValues: propertyName => mockValues[propertyName] || [],
+ },
+ },
+ doc
+ );
+ });
+
+ popup.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+const runAutocompletionTest = async function (editor) {
+ info("Starting autocomplete test for inplace-editor popup offset");
+ let previousOffset = -1;
+ for (const data of testData) {
+ if (data[0] === "checkPopupOffset") {
+ info("Check the popup offset has been modified");
+ // We are not testing hard coded offset values here, which could be fragile. We only
+ // want to ensure the popup tries to match the position of the query in the editor
+ // input.
+ const offset = getPopupOffset(editor);
+ Assert.greater(
+ offset,
+ previousOffset,
+ "New popup offset is greater than the previous one"
+ );
+ previousOffset = offset;
+ } else {
+ await testCompletion(data, editor);
+ }
+ }
+
+ EventUtils.synthesizeKey("VK_RETURN", {}, editor.input.defaultView);
+};
+
+/**
+ * Get the autocomplete panel left offset, relative to the provided input's left offset.
+ */
+function getPopupOffset({ popup, input }) {
+ const popupQuads = popup._panel.getBoxQuads({ relativeTo: input });
+ return popupQuads[0].getBounds().left;
+}
diff --git a/devtools/client/shared/test/browser_inplace-editor_focus_closest_editor.js b/devtools/client/shared/test/browser_inplace-editor_focus_closest_editor.js
new file mode 100644
index 0000000000..5d721410ac
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_focus_closest_editor.js
@@ -0,0 +1,180 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor behavior with focusEditableFieldAfterApply
+// and focusEditableFieldContainerSelector options
+
+add_task(async function () {
+ await addTab(
+ "data:text/html;charset=utf-8,inline editor focusEditableFieldAfterApply"
+ );
+ const { host, doc } = await createHost();
+
+ testFocusNavigationWithMultipleEditor(doc);
+ testFocusNavigationWithNonMatchingFocusEditableFieldContainerSelector(doc);
+ testMissingFocusEditableFieldContainerSelector(doc);
+
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function testFocusNavigationWithMultipleEditor(doc) {
+ // For some reason <button> or <input> are not rendered, so let's use divs with
+ // tabindex attribute to make them focusable.
+ doc.body.innerHTML = `
+ <main>
+ <header>
+ <div role=button tabindex=0 id="header-button">HEADER</div>
+ </header>
+ <section>
+ <span id="span-1" tabindex=0>SPAN 1</span>
+ <div role=button tabindex=0 id="section-button-1">BUTTON 1</div>
+ <p>
+ <span id="span-2" tabindex=0>SPAN 2</span>
+ <div role=button tabindex=0 id="section-button-2">BUTTON 2</div>
+ </p>
+ <span id="span-3" tabindex=0>SPAN 3</span>
+ <div role=button tabindex=0 id="section-button-3">BUTTON 3</div>
+ </section>
+ <sidebar>
+ <div role=button tabindex=0 id="sidebar-button">SIDEBAR</div>
+ </sidebar>
+ <main>`;
+
+ const span1 = doc.getElementById("span-1");
+ const span2 = doc.getElementById("span-2");
+ const span3 = doc.getElementById("span-3");
+
+ info("Create 3 editable fields for the 3 spans inside the main element");
+ const options = {
+ focusEditableFieldAfterApply: true,
+ focusEditableFieldContainerSelector: "main",
+ };
+ editableField({
+ element: span1,
+ ...options,
+ });
+ editableField({
+ element: span2,
+ ...options,
+ });
+ editableField({
+ element: span3,
+ ...options,
+ });
+
+ span1.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-1",
+ "Visible editable field is the one associated with span-1"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ EventUtils.sendKey("Tab");
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-2",
+ "Using Tab moved focus to span-2 editable field"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ EventUtils.sendKey("Tab");
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-3",
+ "Using Tab moved focus to span-3 editable field"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ EventUtils.sendKey("Tab");
+
+ is(
+ doc.activeElement.id,
+ "sidebar-button",
+ "Using Tab moved focus outside of <main>"
+ );
+}
+
+function testFocusNavigationWithNonMatchingFocusEditableFieldContainerSelector(
+ doc
+) {
+ // For some reason <button> or <input> are not rendered, so let's use divs with
+ // tabindex attribute to make them focusable.
+ doc.body.innerHTML = `
+ <main>
+ <span id="span-1" tabindex=0>SPAN 1</span>
+ <div role=button tabindex=0 id="section-button-1">BUTTON 1</div>
+ <span id="span-2" tabindex=0>SPAN 2</span>
+ <div role=button tabindex=0 id="section-button-2">BUTTON 2</div>
+ <main>`;
+
+ const span1 = doc.getElementById("span-1");
+ const span2 = doc.getElementById("span-2");
+
+ info(
+ "Create editable fields for the 2 spans, but pass a focusEditableFieldContainerSelector that does not match any element"
+ );
+ const options = {
+ focusEditableFieldAfterApply: true,
+ focusEditableFieldContainerSelector: ".does-not-exist",
+ };
+ editableField({
+ element: span1,
+ ...options,
+ });
+ editableField({
+ element: span2,
+ ...options,
+ });
+
+ span1.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-1",
+ "Visible editable field is the one associated with span-1"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ EventUtils.sendKey("Tab");
+
+ is(
+ doc.activeElement.id,
+ "section-button-1",
+ "Using Tab moved focus to section-button-1, non-editable field"
+ );
+ ok(!doc.querySelector("input"), "There's no editable input displayed");
+}
+
+function testMissingFocusEditableFieldContainerSelector(doc) {
+ doc.body.innerHTML = "";
+ const element = createSpan(doc);
+ editableField({
+ element,
+ focusEditableFieldAfterApply: true,
+ });
+
+ element.click();
+ is(
+ element.style.display,
+ "inline-block",
+ "The original <span> was not hidden"
+ );
+ ok(!doc.querySelector("input"), "No input was created");
+}
+
+function assertFocusedElementInplaceEditorInput(doc) {
+ ok(
+ doc.activeElement.matches("input.styleinspector-propertyeditor"),
+ "inplace editor input is focused"
+ );
+}
diff --git a/devtools/client/shared/test/browser_inplace-editor_maxwidth.js b/devtools/client/shared/test/browser_inplace-editor_maxwidth.js
new file mode 100644
index 0000000000..78ac6beba8
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_maxwidth.js
@@ -0,0 +1,138 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+loadHelperScript("helper_inplace_editor.js");
+
+const MAX_WIDTH = 300;
+const START_TEXT = "Start text";
+const LONG_TEXT =
+ "I am a long text and I will not fit in a 300px container. " +
+ "I expect the inplace editor to wrap over more than two lines.";
+
+// Test the inplace-editor behavior with a maxWidth configuration option
+// defined.
+
+add_task(async function () {
+ await addTab("data:text/html;charset=utf-8,inplace editor max width tests");
+ const { host, doc } = await createHost();
+
+ info("Testing the maxWidth option in pixels, to precisely check the size");
+ await new Promise(resolve => {
+ createInplaceEditorAndClick(
+ {
+ multiline: true,
+ maxWidth: MAX_WIDTH,
+ start: testMaxWidth,
+ done: resolve,
+ },
+ doc,
+ START_TEXT
+ );
+ });
+
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+const testMaxWidth = async function (editor) {
+ is(editor.input.value, START_TEXT, "Span text content should be used");
+ Assert.less(
+ editor.input.offsetWidth,
+ MAX_WIDTH,
+ "Input width should be strictly smaller than MAX_WIDTH"
+ );
+ is(getLines(editor.input), 1, "Input should display 1 line of text");
+
+ info("Check a text is on several lines if it does not fit MAX_WIDTH");
+ for (const key of LONG_TEXT) {
+ EventUtils.sendChar(key);
+ checkScrollbars(editor.input);
+ }
+
+ is(editor.input.value, LONG_TEXT, "Long text should be the input value");
+ is(
+ editor.input.offsetWidth,
+ MAX_WIDTH,
+ "Input width should be the same as MAX_WIDTH"
+ );
+ is(getLines(editor.input), 3, "Input should display 3 lines of text");
+ checkScrollbars(editor.input);
+
+ info("Delete all characters on line 3.");
+ while (getLines(editor.input) === 3) {
+ EventUtils.sendKey("BACK_SPACE");
+ checkScrollbars(editor.input);
+ }
+
+ is(
+ editor.input.offsetWidth,
+ MAX_WIDTH,
+ "Input width should be the same as MAX_WIDTH"
+ );
+ is(getLines(editor.input), 2, "Input should display 2 lines of text");
+ checkScrollbars(editor.input);
+
+ info("Delete all characters on line 2.");
+ while (getLines(editor.input) === 2) {
+ EventUtils.sendKey("BACK_SPACE");
+ checkScrollbars(editor.input);
+ }
+
+ is(getLines(editor.input), 1, "Input should display 1 line of text");
+ checkScrollbars(editor.input);
+
+ info("Delete all characters.");
+ while (editor.input.value !== "") {
+ EventUtils.sendKey("BACK_SPACE");
+ checkScrollbars(editor.input);
+ }
+
+ Assert.less(
+ editor.input.offsetWidth,
+ MAX_WIDTH,
+ "Input width should again be strictly smaller than MAX_WIDTH"
+ );
+ Assert.greater(
+ editor.input.offsetWidth,
+ 0,
+ "Even with no content, the input has a non-zero width"
+ );
+ is(getLines(editor.input), 1, "Input should display 1 line of text");
+ checkScrollbars(editor.input);
+
+ info("Leave the inplace-editor");
+ EventUtils.sendKey("RETURN");
+};
+
+/**
+ * Retrieve the current number of lines displayed in the provided textarea.
+ *
+ * @param {DOMNode} textarea
+ * @return {Number} the number of lines
+ */
+function getLines(textarea) {
+ const win = textarea.ownerDocument.defaultView;
+ const style = win.getComputedStyle(textarea);
+ return Math.floor(textarea.clientHeight / parseFloat(style.fontSize));
+}
+
+/**
+ * Verify that the provided textarea has no vertical or horizontal scrollbar.
+ *
+ * @param {DOMNode} textarea
+ */
+function checkScrollbars(textarea) {
+ is(
+ textarea.scrollHeight,
+ textarea.clientHeight,
+ "Textarea should never have vertical scrollbars"
+ );
+ is(
+ textarea.scrollWidth,
+ textarea.clientWidth,
+ "Textarea should never have horizontal scrollbars"
+ );
+}
diff --git a/devtools/client/shared/test/browser_inplace-editor_stop_on_key.js b/devtools/client/shared/test/browser_inplace-editor_stop_on_key.js
new file mode 100644
index 0000000000..50ab5631ae
--- /dev/null
+++ b/devtools/client/shared/test/browser_inplace-editor_stop_on_key.js
@@ -0,0 +1,219 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* import-globals-from helper_inplace_editor.js */
+
+"use strict";
+
+loadHelperScript("helper_inplace_editor.js");
+
+// Test the inplace-editor stopOnX options behavior
+
+add_task(async function () {
+ await addTab("data:text/html;charset=utf-8,inline editor stopOnX");
+ const { host, doc } = await createHost();
+
+ testStopOnReturn(doc);
+ testStopOnTab(doc);
+ testStopOnShiftTab(doc);
+
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function testStopOnReturn(doc) {
+ const { span1El, span2El } = setupMarkupAndCreateInplaceEditors(doc);
+
+ info(`Create an editable field with "stopOnReturn" set to true`);
+ editableField({
+ element: span1El,
+ focusEditableFieldAfterApply: true,
+ focusEditableFieldContainerSelector: "main",
+ stopOnReturn: true,
+ });
+ editableField({
+ element: span2El,
+ });
+
+ info("Activate inplace editor on first span");
+ span1El.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-1",
+ "Visible editable field is the one associated with first span"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ info("Press Return");
+ EventUtils.synthesizeKey("VK_RETURN");
+
+ is(
+ doc.activeElement.id,
+ "span-1",
+ "Using Enter did not advance the editor to the next focusable element"
+ );
+
+ info("Activate inplace editor on first span again");
+ span1El.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-1",
+ "Visible editable field is the one associated with first span"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ const isMacOS = Services.appinfo.OS === "Darwin";
+ info(`Press ${isMacOS ? "Cmd" : "Ctrl"} + Enter`);
+ EventUtils.synthesizeKey("VK_RETURN", {
+ [isMacOS ? "metaKey" : "ctrlKey"]: true,
+ });
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-2",
+ `Using ${
+ isMacOS ? "Cmd" : "Ctrl"
+ } + Enter did advance the editor to the next focusable element`
+ );
+}
+
+function testStopOnTab(doc) {
+ const { span1El, span2El } = setupMarkupAndCreateInplaceEditors(doc);
+
+ info(`Create editable fields with "stopOnTab" set to true`);
+ const options = {
+ focusEditableFieldAfterApply: true,
+ focusEditableFieldContainerSelector: "main",
+ stopOnTab: true,
+ };
+ editableField({
+ element: span1El,
+ ...options,
+ });
+ editableField({
+ element: span2El,
+ ...options,
+ });
+
+ info("Activate inplace editor on first span");
+ span1El.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-1",
+ "Visible editable field is the one associated with first span"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ info("Press Tab");
+ EventUtils.synthesizeKey("VK_TAB");
+
+ is(
+ doc.activeElement.id,
+ "span-1",
+ "Using Tab did not advance the editor to the next focusable element"
+ );
+
+ info(
+ "Activate inplace editor on second span to check that Shift+Tab does work"
+ );
+ span2El.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-2",
+ "Visible editable field is the one associated with second span"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ info("Press Shift+Tab");
+ EventUtils.synthesizeKey("VK_TAB", {
+ shiftKey: true,
+ });
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-1",
+ `Using Shift + Tab did move the editor to the previous focusable element`
+ );
+}
+
+function testStopOnShiftTab(doc) {
+ const { span1El, span2El } = setupMarkupAndCreateInplaceEditors(doc);
+ info(`Create editable fields with "stopOnShiftTab" set to true`);
+ const options = {
+ focusEditableFieldAfterApply: true,
+ focusEditableFieldContainerSelector: "main",
+ stopOnShiftTab: true,
+ };
+ editableField({
+ element: span1El,
+ ...options,
+ });
+ editableField({
+ element: span2El,
+ ...options,
+ });
+
+ info("Activate inplace editor on second span");
+ span2El.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-2",
+ "Visible editable field is the one associated with second span"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ info("Press Shift+Tab");
+ EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+
+ is(
+ doc.activeElement.id,
+ "span-2",
+ "Using Shift+Tab did not move the editor to the previous focusable element"
+ );
+
+ info(
+ "Activate inplace editor on first span to check that Tab is not impacted"
+ );
+ span1El.click();
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-1",
+ "Visible editable field is the one associated with first span"
+ );
+ assertFocusedElementInplaceEditorInput(doc);
+
+ info("Press Tab");
+ EventUtils.synthesizeKey("VK_TAB");
+
+ is(
+ doc.activeElement.inplaceEditor.elt.id,
+ "span-2",
+ `Using Tab did move the editor to the next focusable element`
+ );
+}
+
+function setupMarkupAndCreateInplaceEditors(doc) {
+ // For some reason <button> or <input> are not rendered, so let's use divs with
+ // tabindex attribute to make them focusable.
+ doc.body.innerHTML = `
+ <main>
+ <span id="span-1" tabindex=0>SPAN 1</span>
+ <span id="span-2" tabindex=0>SPAN 2</span>
+ <main>`;
+
+ const span1El = doc.getElementById("span-1");
+ const span2El = doc.getElementById("span-2");
+ return { span1El, span2El };
+}
+
+function assertFocusedElementInplaceEditorInput(doc) {
+ ok(
+ doc.activeElement.matches("input.styleinspector-propertyeditor"),
+ "inplace editor input is focused"
+ );
+}
diff --git a/devtools/client/shared/test/browser_key_shortcuts.js b/devtools/client/shared/test/browser_key_shortcuts.js
new file mode 100644
index 0000000000..d48666ddca
--- /dev/null
+++ b/devtools/client/shared/test/browser_key_shortcuts.js
@@ -0,0 +1,468 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var isOSX = Services.appinfo.OS === "Darwin";
+
+add_task(async function () {
+ const shortcuts = new KeyShortcuts({
+ window,
+ });
+
+ await testSimple(shortcuts);
+ await testNonLetterCharacter(shortcuts);
+ await testPlusCharacter(shortcuts);
+ await testFunctionKey(shortcuts);
+ await testMixup(shortcuts);
+ await testLooseDigits(shortcuts);
+ await testExactModifiers(shortcuts);
+ await testLooseShiftModifier(shortcuts);
+ await testStrictLetterShiftModifier(shortcuts);
+ await testAltModifier(shortcuts);
+ await testCommandOrControlModifier(shortcuts);
+ await testCtrlModifier(shortcuts);
+ await testInvalidShortcutString(shortcuts);
+ await testNullShortcut(shortcuts);
+ await testCmdShiftShortcut(shortcuts);
+ await testTabCharacterShortcut(shortcuts);
+ shortcuts.destroy();
+
+ await testTarget();
+});
+
+// Test helper to listen to the next key press for a given key,
+// returning a promise to help using Tasks.
+function once(shortcuts, key, listener) {
+ let called = false;
+ return new Promise(done => {
+ const onShortcut = event => {
+ shortcuts.off(key, onShortcut);
+ ok(!called, "once listener called only once (i.e. off() works)");
+ called = true;
+ listener(event);
+ done();
+ };
+ shortcuts.on(key, onShortcut);
+ });
+}
+
+async function testSimple(shortcuts) {
+ info("Test simple key shortcuts");
+
+ const onKey = once(shortcuts, "0", event => {
+ is(event.key, "0");
+
+ // Display another key press to ensure that once() correctly stop listening
+ EventUtils.synthesizeKey("0", {}, window);
+ });
+
+ EventUtils.synthesizeKey("0", {}, window);
+ await onKey;
+}
+
+async function testNonLetterCharacter(shortcuts) {
+ info("Test non-naive character key shortcuts");
+
+ const onKey = once(shortcuts, "[", event => {
+ is(event.key, "[");
+ });
+
+ EventUtils.synthesizeKey("[", {}, window);
+ await onKey;
+}
+
+async function testFunctionKey(shortcuts) {
+ info("Test function key shortcuts");
+
+ const onKey = once(shortcuts, "F12", event => {
+ is(event.key, "F12");
+ });
+
+ EventUtils.synthesizeKey("F12", { keyCode: 123 }, window);
+ await onKey;
+}
+
+// Plus is special. It's keycode is the one for "=". That's because it requires
+// shift to be pressed and is behind "=" key. So it should be considered as a
+// character key
+async function testPlusCharacter(shortcuts) {
+ info("Test 'Plus' key shortcuts");
+
+ const onKey = once(shortcuts, "Plus", event => {
+ is(event.key, "+");
+ });
+
+ EventUtils.synthesizeKey("+", { keyCode: 61, shiftKey: true }, window);
+ await onKey;
+}
+
+// Test they listeners are not mixed up between shortcuts
+async function testMixup(shortcuts) {
+ info("Test possible listener mixup");
+
+ let hitFirst = false,
+ hitSecond = false;
+ const onFirstKey = once(shortcuts, "0", event => {
+ is(event.key, "0");
+ hitFirst = true;
+ });
+ const onSecondKey = once(shortcuts, "Alt+A", event => {
+ is(event.key, "a");
+ ok(event.altKey);
+ hitSecond = true;
+ });
+
+ // Dispatch the first shortcut and expect only this one to be notified
+ ok(!hitFirst, "First shortcut isn't notified before firing the key event");
+ EventUtils.synthesizeKey("0", {}, window);
+ await onFirstKey;
+ ok(hitFirst, "Got the first shortcut notified");
+ ok(!hitSecond, "No mixup, second shortcut is still not notified (1/2)");
+
+ // Wait an extra time, just to be sure this isn't racy
+ await new Promise(done => {
+ window.setTimeout(done, 0);
+ });
+ ok(!hitSecond, "No mixup, second shortcut is still not notified (2/2)");
+
+ // Finally dispatch the second shortcut
+ EventUtils.synthesizeKey("a", { altKey: true }, window);
+ await onSecondKey;
+ ok(hitSecond, "Got the second shortcut notified once it is actually fired");
+}
+
+// On azerty keyboard, digits are only available by pressing Shift/Capslock,
+// but we accept them even if we omit doing that.
+async function testLooseDigits(shortcuts) {
+ info("Test Loose digits");
+ let onKey = once(shortcuts, "0", event => {
+ is(event.key, "à");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(!event.shiftKey);
+ });
+ // Simulate a press on the "0" key, without shift pressed on a french
+ // keyboard
+ EventUtils.synthesizeKey("à", { keyCode: 48 }, window);
+ await onKey;
+
+ onKey = once(shortcuts, "0", event => {
+ is(event.key, "0");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(event.shiftKey);
+ });
+ // Simulate the same press with shift pressed
+ EventUtils.synthesizeKey("0", { keyCode: 48, shiftKey: true }, window);
+ await onKey;
+}
+
+// Test that shortcuts is notified only when the modifiers match exactly
+async function testExactModifiers(shortcuts) {
+ info("Test exact modifiers match");
+
+ let hit = false;
+ const onKey = once(shortcuts, "Alt+A", event => {
+ is(event.key, "a");
+ ok(event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(!event.shiftKey);
+ hit = true;
+ });
+
+ // Dispatch with unexpected set of modifiers
+ ok(!hit, "Shortcut isn't notified before firing the key event");
+ EventUtils.synthesizeKey(
+ "a",
+ { accelKey: true, altKey: true, shiftKey: true },
+ window
+ );
+ EventUtils.synthesizeKey(
+ "a",
+ { accelKey: true, altKey: false, shiftKey: false },
+ window
+ );
+ EventUtils.synthesizeKey(
+ "a",
+ { accelKey: false, altKey: false, shiftKey: true },
+ window
+ );
+ EventUtils.synthesizeKey(
+ "a",
+ { accelKey: false, altKey: false, shiftKey: false },
+ window
+ );
+
+ // Wait an extra time to let a chance to call the listener
+ await new Promise(done => {
+ window.setTimeout(done, 0);
+ });
+ ok(!hit, "Listener isn't called when modifiers aren't exactly matching");
+
+ // Dispatch the expected modifiers
+ EventUtils.synthesizeKey(
+ "a",
+ { accelKey: false, altKey: true, shiftKey: false },
+ window
+ );
+ await onKey;
+ ok(hit, "Got shortcut notified once it is actually fired");
+}
+
+// Some keys are only accessible via shift and listener should also be called
+// even if the key didn't explicitely requested Shift modifier.
+// For example, `%` on french keyboards is only accessible via Shift.
+// Same thing for `@` on US keybords.
+async function testLooseShiftModifier(shortcuts) {
+ info("Test Loose shift modifier");
+ let onKey = once(shortcuts, "%", event => {
+ is(event.key, "%");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(event.shiftKey);
+ });
+ EventUtils.synthesizeKey(
+ "%",
+ { accelKey: false, altKey: false, ctrlKey: false, shiftKey: true },
+ window
+ );
+ await onKey;
+
+ onKey = once(shortcuts, "@", event => {
+ is(event.key, "@");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(event.shiftKey);
+ });
+ EventUtils.synthesizeKey(
+ "@",
+ { accelKey: false, altKey: false, ctrlKey: false, shiftKey: true },
+ window
+ );
+ await onKey;
+}
+
+// But Shift modifier is strict on all letter characters (a to Z)
+async function testStrictLetterShiftModifier(shortcuts) {
+ info("Test strict shift modifier on letters");
+ let hitFirst = false;
+ const onKey = once(shortcuts, "a", event => {
+ is(event.key, "a");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(!event.shiftKey);
+ hitFirst = true;
+ });
+ const onShiftKey = once(shortcuts, "Shift+a", event => {
+ is(event.key, "a");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(event.shiftKey);
+ });
+ EventUtils.synthesizeKey("a", { shiftKey: true }, window);
+ await onShiftKey;
+ ok(!hitFirst, "Didn't fire the explicit shift+a");
+
+ EventUtils.synthesizeKey("a", { shiftKey: false }, window);
+ await onKey;
+}
+
+async function testAltModifier(shortcuts) {
+ info("Test Alt modifier");
+ const onKey = once(shortcuts, "Alt+F1", event => {
+ is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+ ok(event.altKey);
+ ok(!event.ctrlKey);
+ ok(!event.metaKey);
+ ok(!event.shiftKey);
+ });
+ EventUtils.synthesizeKey("VK_F1", { altKey: true }, window);
+ await onKey;
+}
+
+async function testCommandOrControlModifier(shortcuts) {
+ info("Test CommandOrControl modifier");
+ const onKey = once(shortcuts, "CommandOrControl+F1", event => {
+ is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+ ok(!event.altKey);
+ if (isOSX) {
+ ok(!event.ctrlKey);
+ ok(event.metaKey);
+ } else {
+ ok(event.ctrlKey);
+ ok(!event.metaKey);
+ }
+ ok(!event.shiftKey);
+ });
+ const onKeyAlias = once(shortcuts, "CmdOrCtrl+F1", event => {
+ is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+ ok(!event.altKey);
+ if (isOSX) {
+ ok(!event.ctrlKey);
+ ok(event.metaKey);
+ } else {
+ ok(event.ctrlKey);
+ ok(!event.metaKey);
+ }
+ ok(!event.shiftKey);
+ });
+ if (isOSX) {
+ EventUtils.synthesizeKey("VK_F1", { metaKey: true }, window);
+ } else {
+ EventUtils.synthesizeKey("VK_F1", { ctrlKey: true }, window);
+ }
+ await onKey;
+ await onKeyAlias;
+}
+
+async function testCtrlModifier(shortcuts) {
+ info("Test Ctrl modifier");
+ const onKey = once(shortcuts, "Ctrl+F1", event => {
+ is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+ ok(!event.altKey);
+ ok(event.ctrlKey);
+ ok(!event.metaKey);
+ ok(!event.shiftKey);
+ });
+ const onKeyAlias = once(shortcuts, "Control+F1", event => {
+ is(event.keyCode, window.KeyboardEvent.DOM_VK_F1);
+ ok(!event.altKey);
+ ok(event.ctrlKey);
+ ok(!event.metaKey);
+ ok(!event.shiftKey);
+ });
+ EventUtils.synthesizeKey("VK_F1", { ctrlKey: true }, window);
+ await onKey;
+ await onKeyAlias;
+}
+
+async function testCmdShiftShortcut(shortcuts) {
+ if (!isOSX) {
+ // This test is OSX only (Bug 1300458).
+ return;
+ }
+
+ const onCmdKey = once(shortcuts, "CmdOrCtrl+[", event => {
+ is(event.key, "[");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(event.metaKey);
+ ok(!event.shiftKey);
+ });
+ const onCmdShiftKey = once(shortcuts, "CmdOrCtrl+Shift+[", event => {
+ is(event.key, "[");
+ ok(!event.altKey);
+ ok(!event.ctrlKey);
+ ok(event.metaKey);
+ ok(event.shiftKey);
+ });
+
+ EventUtils.synthesizeKey("[", { metaKey: true, shiftKey: true }, window);
+ EventUtils.synthesizeKey("[", { metaKey: true }, window);
+
+ await onCmdKey;
+ await onCmdShiftKey;
+}
+
+async function testTarget() {
+ info("Test KeyShortcuts with target argument");
+
+ const target = document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "input"
+ );
+ document.documentElement.appendChild(target);
+ target.focus();
+
+ const shortcuts = new KeyShortcuts({
+ window,
+ target,
+ });
+ const onKey = once(shortcuts, "0", event => {
+ is(event.key, "0");
+ is(event.target, target);
+ });
+ EventUtils.synthesizeKey("0", {}, window);
+ await onKey;
+
+ target.remove();
+
+ shortcuts.destroy();
+}
+
+function testInvalidShortcutString(shortcuts) {
+ info("Test wrong shortcut string");
+
+ const shortcut = KeyShortcuts.parseElectronKey(window, "Cmmd+F");
+ ok(
+ !shortcut,
+ "Passing a invalid shortcut string should return a null object"
+ );
+
+ shortcuts.on("Cmmd+F", function () {});
+ ok(true, "on() shouldn't throw when passing invalid shortcut string");
+}
+
+// Can happen on localized builds where the value of the localized string is
+// empty, eg `toolbox.elementPicker.key=`. See Bug 1569572.
+function testNullShortcut(shortcuts) {
+ info("Test null shortcut");
+
+ const shortcut = KeyShortcuts.parseElectronKey(window, null);
+ ok(!shortcut, "Passing a null object should return a null object");
+
+ const stringified = KeyShortcuts.stringify(shortcut);
+ is(stringified, "", "A null object should be stringified as an empty string");
+
+ shortcuts.on(null, function () {});
+ ok(true, "on() shouldn't throw when passing a null object");
+}
+
+/**
+ * Shift+Alt+I generates ^ key (`event.key`) on OSX and KeyShortcuts module
+ * must ensure that this doesn't interfere with shortcuts CmdOrCtrl+Alt+Shift+I
+ * for opening the Browser Toolbox and CmdOrCtrl+Alt+I for toggling the Toolbox.
+ */
+async function testTabCharacterShortcut(shortcuts) {
+ if (!isOSX) {
+ return;
+ }
+
+ info("Test tab character shortcut");
+
+ once(shortcuts, "CmdOrCtrl+Alt+I", event => {
+ ok(false, "This handler must not be executed");
+ });
+
+ const onKey = once(shortcuts, "CmdOrCtrl+Alt+Shift+I", event => {
+ info("Test for CmdOrCtrl+Alt+Shift+I");
+ is(event.key, "^");
+ is(event.keyCode, 73);
+ });
+
+ // Simulate `CmdOrCtrl+Alt+Shift+I` shortcut. Note that EventUtils doesn't
+ // generate `^` like real keyboard, so we need to pass it explicitly
+ // and use proper keyCode for `I` character.
+ EventUtils.synthesizeKey(
+ "^",
+ {
+ code: "KeyI",
+ key: "^",
+ keyCode: 73,
+ shiftKey: true,
+ altKey: true,
+ metaKey: true,
+ },
+ window
+ );
+
+ await onKey;
+}
diff --git a/devtools/client/shared/test/browser_keycodes.js b/devtools/client/shared/test/browser_keycodes.js
new file mode 100644
index 0000000000..bcfeafdf65
--- /dev/null
+++ b/devtools/client/shared/test/browser_keycodes.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
+
+add_task(async function () {
+ for (const key in KeyCodes) {
+ is(KeyCodes[key], KeyboardEvent[key], "checking value for " + key);
+ }
+});
diff --git a/devtools/client/shared/test/browser_layoutHelpers.js b/devtools/client/shared/test/browser_layoutHelpers.js
new file mode 100644
index 0000000000..5698c47bce
--- /dev/null
+++ b/devtools/client/shared/test/browser_layoutHelpers.js
@@ -0,0 +1,131 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that scrollIntoViewIfNeeded works properly.
+const {
+ scrollIntoViewIfNeeded,
+} = require("resource://devtools/client/shared/scroll.js");
+
+const TEST_URI = CHROME_URL_ROOT + "doc_layoutHelpers.html";
+
+add_task(async function () {
+ const { host, win } = await createHost("bottom", TEST_URI);
+ await runTest(win);
+ host.destroy();
+});
+
+async function runTest(win) {
+ const some = win.document.getElementById("some");
+
+ some.style.top = win.innerHeight + "px";
+ some.style.left = win.innerWidth + "px";
+ // The tests start with a black 2x2 pixels square below bottom right.
+ // Do not resize the window during the tests.
+
+ const xPos = Math.floor(win.innerWidth / 2);
+ // Above the viewport.
+ win.scroll(xPos, win.innerHeight + 2);
+ scrollIntoViewIfNeeded(some);
+ is(
+ win.scrollY,
+ Math.floor(win.innerHeight / 2) + 1,
+ "Element completely hidden above should appear centered."
+ );
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // On the top edge.
+ win.scroll(win.innerWidth / 2, win.innerHeight + 1);
+ scrollIntoViewIfNeeded(some);
+ is(
+ win.scrollY,
+ win.innerHeight,
+ "Element partially visible above should appear above."
+ );
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // Just below the viewport.
+ win.scroll(win.innerWidth / 2, 0);
+ scrollIntoViewIfNeeded(some);
+ is(
+ win.scrollY,
+ Math.floor(win.innerHeight / 2) + 1,
+ "Element completely hidden below should appear centered."
+ );
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // On the bottom edge.
+ win.scroll(win.innerWidth / 2, 1);
+ scrollIntoViewIfNeeded(some);
+ is(win.scrollY, 2, "Element partially visible below should appear below.");
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // Above the viewport.
+ win.scroll(win.innerWidth / 2, win.innerHeight + 2);
+ scrollIntoViewIfNeeded(some, false);
+ is(
+ win.scrollY,
+ win.innerHeight,
+ "Element completely hidden above should appear above " +
+ "if parameter is false."
+ );
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // On the top edge.
+ win.scroll(win.innerWidth / 2, win.innerHeight + 1);
+ scrollIntoViewIfNeeded(some, false);
+ is(
+ win.scrollY,
+ win.innerHeight,
+ "Element partially visible above should appear above " +
+ "if parameter is false."
+ );
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // Below the viewport.
+ win.scroll(win.innerWidth / 2, 0);
+ scrollIntoViewIfNeeded(some, false);
+ is(
+ win.scrollY,
+ 2,
+ "Element completely hidden below should appear below " +
+ "if parameter is false."
+ );
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // On the bottom edge.
+ win.scroll(win.innerWidth / 2, 1);
+ scrollIntoViewIfNeeded(some, false);
+ is(
+ win.scrollY,
+ 2,
+ "Element partially visible below should appear below " +
+ "if parameter is false."
+ );
+ is(win.scrollX, xPos, "scrollX position has not changed.");
+
+ // Check smooth flag (scroll goes below the viewport)
+ await pushPref("ui.prefersReducedMotion", 0);
+
+ info("Checking smooth flag");
+ is(
+ win.matchMedia("(prefers-reduced-motion)").matches,
+ false,
+ "Reduced motion is disabled"
+ );
+
+ const other = win.document.getElementById("other");
+ other.style.top = win.innerHeight + "px";
+ other.style.left = win.innerWidth + "px";
+ win.scroll(0, 0);
+
+ scrollIntoViewIfNeeded(other, false, true);
+ Assert.less(
+ win.scrollY,
+ other.clientHeight,
+ "Window has not instantly scrolled to the final position"
+ );
+ await waitUntil(() => win.scrollY === other.clientHeight);
+ ok(true, "Window did finish scrolling");
+}
diff --git a/devtools/client/shared/test/browser_layoutHelpers_getBoxQuads1.js b/devtools/client/shared/test/browser_layoutHelpers_getBoxQuads1.js
new file mode 100644
index 0000000000..a8119df3fd
--- /dev/null
+++ b/devtools/client/shared/test/browser_layoutHelpers_getBoxQuads1.js
@@ -0,0 +1,353 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests getAdjustedQuads works properly in a variety of use cases including
+// iframes, scroll and zoom
+
+"use strict";
+
+const TEST_URI = TEST_URI_ROOT + "doc_layoutHelpers_getBoxQuads1.html";
+
+add_task(async function () {
+ const tab = await addTab(TEST_URI);
+
+ info("Running tests");
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ // This function allows the Content Task to easily call `FullZoom` API in
+ // the parent process.
+ function sendCommand(cmd) {
+ return SpecialPowers.spawnChrome([cmd], async data => {
+ const window = this.browsingContext.topChromeWindow;
+ switch (data) {
+ case "zoom-enlarge":
+ window.FullZoom.enlarge();
+ break;
+ case "zoom-reset":
+ await window.FullZoom.reset();
+ break;
+ case "zoom-reduce":
+ window.FullZoom.reduce();
+ break;
+ }
+ });
+ }
+
+ const doc = content.document;
+
+ const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+ );
+ const {
+ getAdjustedQuads,
+ } = require("resource://devtools/shared/layout/utils.js");
+
+ Assert.strictEqual(
+ typeof getAdjustedQuads,
+ "function",
+ "getAdjustedQuads is defined"
+ );
+
+ returnsTheRightDataStructure();
+ isEmptyForMissingNode();
+ isEmptyForHiddenNodes();
+ defaultsToBorderBoxIfNoneProvided();
+ returnsLikeGetBoxQuadsInSimpleCase();
+ takesIframesOffsetsIntoAccount();
+ takesScrollingIntoAccount();
+ await takesZoomIntoAccount();
+ returnsMultipleItemsForWrappingInlineElements();
+
+ function returnsTheRightDataStructure() {
+ info("Checks that the returned data contains bounds and 4 points");
+
+ const node = doc.querySelector("body");
+ const [res] = getAdjustedQuads(doc.defaultView, node, "content");
+
+ ok("bounds" in res, "The returned data has a bounds property");
+ ok("p1" in res, "The returned data has a p1 property");
+ ok("p2" in res, "The returned data has a p2 property");
+ ok("p3" in res, "The returned data has a p3 property");
+ ok("p4" in res, "The returned data has a p4 property");
+
+ for (const boundProp of [
+ "bottom",
+ "top",
+ "right",
+ "left",
+ "width",
+ "height",
+ "x",
+ "y",
+ ]) {
+ ok(
+ boundProp in res.bounds,
+ "The bounds has a " + boundProp + " property"
+ );
+ }
+
+ for (const point of ["p1", "p2", "p3", "p4"]) {
+ for (const pointProp of ["x", "y", "z", "w"]) {
+ ok(
+ pointProp in res[point],
+ point + " has a " + pointProp + " property"
+ );
+ }
+ }
+ }
+
+ function isEmptyForMissingNode() {
+ info("Checks that null is returned for invalid nodes");
+
+ for (const input of [null, undefined, "", 0]) {
+ is(
+ getAdjustedQuads(doc.defaultView, input).length,
+ 0,
+ "A 0-length array is returned for input " + input
+ );
+ }
+ }
+
+ function isEmptyForHiddenNodes() {
+ info("Checks that null is returned for nodes that aren't rendered");
+
+ const style = doc.querySelector("#styles");
+ is(
+ getAdjustedQuads(doc.defaultView, style).length,
+ 0,
+ "null is returned for a <style> node"
+ );
+
+ const hidden = doc.querySelector("#hidden-node");
+ is(
+ getAdjustedQuads(doc.defaultView, hidden).length,
+ 0,
+ "null is returned for a hidden node"
+ );
+ }
+
+ function defaultsToBorderBoxIfNoneProvided() {
+ info(
+ "Checks that if no boxtype is passed, then border is the default one"
+ );
+
+ const node = doc.querySelector("#simple-node-with-margin-padding-border");
+ const [withBoxType] = getAdjustedQuads(doc.defaultView, node, "border");
+ const [withoutBoxType] = getAdjustedQuads(doc.defaultView, node);
+
+ for (const boundProp of [
+ "bottom",
+ "top",
+ "right",
+ "left",
+ "width",
+ "height",
+ "x",
+ "y",
+ ]) {
+ is(
+ withBoxType.bounds[boundProp],
+ withoutBoxType.bounds[boundProp],
+ boundProp + " bound is equal with or without the border box type"
+ );
+ }
+
+ for (const point of ["p1", "p2", "p3", "p4"]) {
+ for (const pointProp of ["x", "y", "z", "w"]) {
+ is(
+ withBoxType[point][pointProp],
+ withoutBoxType[point][pointProp],
+ point +
+ "." +
+ pointProp +
+ " is equal with or without the border box type"
+ );
+ }
+ }
+ }
+
+ function returnsLikeGetBoxQuadsInSimpleCase() {
+ info(
+ "Checks that for an element in the main frame, without scroll nor zoom" +
+ "that the returned value is similar to the returned value of getBoxQuads"
+ );
+
+ const node = doc.querySelector("#simple-node-with-margin-padding-border");
+
+ for (const region of ["content", "padding", "border", "margin"]) {
+ const expected = node.getBoxQuads({
+ box: region,
+ })[0];
+ const [actual] = getAdjustedQuads(doc.defaultView, node, region);
+
+ for (const boundProp of [
+ "bottom",
+ "top",
+ "right",
+ "left",
+ "width",
+ "height",
+ "x",
+ "y",
+ ]) {
+ is(
+ actual.bounds[boundProp],
+ expected.getBounds()[boundProp],
+ boundProp +
+ " bound is equal to the one returned by getBoxQuads for " +
+ region +
+ " box"
+ );
+ }
+
+ for (const point of ["p1", "p2", "p3", "p4"]) {
+ for (const pointProp of ["x", "y", "z", "w"]) {
+ is(
+ actual[point][pointProp],
+ expected[point][pointProp],
+ point +
+ "." +
+ pointProp +
+ " is equal to the one returned by getBoxQuads for " +
+ region +
+ " box"
+ );
+ }
+ }
+ }
+ }
+
+ function takesIframesOffsetsIntoAccount() {
+ info(
+ "Checks that the quad returned for a node inside iframes that have " +
+ "margins takes those offsets into account"
+ );
+
+ const rootIframe = doc.querySelector("iframe");
+ const subIframe = rootIframe.contentDocument.querySelector("iframe");
+ const innerNode = subIframe.contentDocument.querySelector("#inner-node");
+
+ const [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
+
+ // rootIframe margin + subIframe margin + node margin + node border + node padding
+ const p1x = 10 + 10 + 10 + 10 + 10;
+ is(quad.p1.x, p1x, "The inner node's p1 x position is correct");
+
+ // Same as p1x + the inner node width
+ const p2x = p1x + 100;
+ is(quad.p2.x, p2x, "The inner node's p2 x position is correct");
+ }
+
+ function takesScrollingIntoAccount() {
+ info(
+ "Checks that the quad returned for a node inside multiple scrolled " +
+ "containers takes the scroll values into account"
+ );
+
+ // For info, the container being tested here is absolutely positioned at 0 0
+ // to simplify asserting the coordinates
+
+ info("Scroll the container nodes down");
+ const scrolledNode = doc.querySelector("#scrolled-node");
+ scrolledNode.scrollTop = 100;
+ const subScrolledNode = doc.querySelector("#sub-scrolled-node");
+ subScrolledNode.scrollTop = 200;
+ const innerNode = doc.querySelector("#inner-scrolled-node");
+
+ let [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
+ is(
+ quad.p1.x,
+ 0,
+ "p1.x of the scrolled node is correct after scrolling down"
+ );
+ is(
+ quad.p1.y,
+ -300,
+ "p1.y of the scrolled node is correct after scrolling down"
+ );
+
+ info("Scrolling back up");
+ scrolledNode.scrollTop = 0;
+ subScrolledNode.scrollTop = 0;
+
+ [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
+ is(
+ quad.p1.x,
+ 0,
+ "p1.x of the scrolled node is correct after scrolling up"
+ );
+ is(
+ quad.p1.y,
+ 0,
+ "p1.y of the scrolled node is correct after scrolling up"
+ );
+ }
+
+ async function takesZoomIntoAccount() {
+ info(
+ "Checks that if the page is zoomed in/out, the quad returned is correct"
+ );
+
+ // Hard-coding coordinates in this zoom test is a bad idea as it can vary
+ // depending on the platform, so we simply test that zooming in produces a
+ // bigger quad and zooming out produces a smaller quad
+
+ const node = doc.querySelector("#simple-node-with-margin-padding-border");
+ const [defaultQuad] = getAdjustedQuads(doc.defaultView, node);
+
+ info("Zoom in");
+ await sendCommand("zoom-enlarge");
+ const [zoomedInQuad] = getAdjustedQuads(doc.defaultView, node);
+
+ Assert.greater(
+ zoomedInQuad.bounds.width,
+ defaultQuad.bounds.width,
+ "The zoomed in quad is bigger than the default one"
+ );
+ Assert.greater(
+ zoomedInQuad.bounds.height,
+ defaultQuad.bounds.height,
+ "The zoomed in quad is bigger than the default one"
+ );
+
+ info("Zoom out");
+ await sendCommand("zoom-reset");
+ await sendCommand("zoom-reduce");
+
+ const [zoomedOutQuad] = getAdjustedQuads(doc.defaultView, node);
+
+ Assert.less(
+ zoomedOutQuad.bounds.width,
+ defaultQuad.bounds.width,
+ "The zoomed out quad is smaller than the default one"
+ );
+ Assert.less(
+ zoomedOutQuad.bounds.height,
+ defaultQuad.bounds.height,
+ "The zoomed out quad is smaller than the default one"
+ );
+
+ await sendCommand("zoom-reset");
+ }
+
+ function returnsMultipleItemsForWrappingInlineElements() {
+ info(
+ "Checks that several quads are returned " +
+ "for inline elements that span line-breaks"
+ );
+
+ const node = doc.querySelector("#inline");
+ const quads = getAdjustedQuads(doc.defaultView, node, "content");
+ // At least 3 because of the 2 <br />, maybe more depending on the window size.
+ Assert.greaterOrEqual(quads.length, 3, "Multiple quads were returned");
+
+ is(
+ quads.length,
+ node.getBoxQuads().length,
+ "The same number of boxes as getBoxQuads was returned"
+ );
+ }
+ });
+
+ gBrowser.removeCurrentTab();
+});
diff --git a/devtools/client/shared/test/browser_layoutHelpers_getBoxQuads2.js b/devtools/client/shared/test/browser_layoutHelpers_getBoxQuads2.js
new file mode 100644
index 0000000000..051b2c5a7a
--- /dev/null
+++ b/devtools/client/shared/test/browser_layoutHelpers_getBoxQuads2.js
@@ -0,0 +1,185 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that getBoxQuadsFromWindowOrigin works across process
+// boundaries.
+//
+// The test forces a fission window, because there is some
+// imprecision in the APZ transforms for non-fission windows,
+// and the getBoxQuadsFromWindowOrigin is designed specifically
+// to be used by a fission browser.
+//
+// This test embeds a number of documents within iframes,
+// with a variety of borders and padding. Each iframe hosts
+// a document in different domain than its container.
+//
+// The innermost documents have a 50px x 50px div with a
+// 50px margin. So relative to its own iframe, the offset
+// for the div should be 50, 50.
+//
+// Here's the embedding diagram:
+// +-- A -----------------------------------------------------+
+// | |
+// | +- div -----+ |
+// | | 100 x 100 | |
+// | +-----------+ |
+// | |
+// | +- div 20px margin ------------------------------------+ |
+// | | | |
+// | | +- B: iframe 10px border -+ +- D: iframe 40px pad -+ | |
+// | | | 250 x 250 | | 250 x 250 | | |
+// | | | 50px margin | | 50px margin | | |
+// | | | | | | | |
+// | | | +- C: iframe ---+ | | +- E: iframe ---+ | | |
+// | | | | 150 x 150 | | | | 150 x 150 | | | |
+// | | | +---------------+ | | +---------------+ | | |
+// | | +-------------------------+ +----------------------+ | |
+// | +------------------------------------------------------+ |
+// +----------------------------------------------------------+
+//
+// The following checks are made:
+// C-div relative to A should have offset 130, 230.
+// E-div relative to A should have offset 430, 260.
+//
+// This tests the most likely cases for the code that handles
+// relativeToTopLevelBrowsingContextId. It does not check these
+// cases:
+// 1) A css-transform'ed iframe.
+// 2) An abspos iframe.
+// 3) An iframe embedded in an SVG.
+
+"use strict";
+/* import-globals-from ../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js */
+
+const TEST_URI = TEST_URI_ROOT_SSL + "doc_layoutHelpers_getBoxQuads2-a.html";
+
+add_task(async function () {
+ info("Opening a fission window.");
+ const fissionWin = await BrowserTestUtils.openNewBrowserWindow({
+ remote: true,
+ fission: true,
+ });
+
+ info("Load APZ test utils.");
+ loadHelperScript(
+ "../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js"
+ );
+ info("Load paint_listener.");
+ loadHelperScript("../../../../../tests/SimpleTest/paint_listener.js");
+
+ info("Open a new tab.");
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ fissionWin.gBrowser,
+ TEST_URI
+ );
+
+ info("Running tests");
+
+ ok(waitUntilApzStable, "waitUntilApzStable is defined.");
+ await waitUntilApzStable();
+
+ await ContentTask.spawn(tab.linkedBrowser, null, async function () {
+ const win = content.window;
+ const doc = content.document;
+ const refNode = doc.documentElement;
+ const iframeB = doc.getElementById("b");
+ const iframeD = doc.getElementById("d");
+
+ // Get the offset of the reference node to the window origin. We'll use
+ // this offset later to adjust the quads we get from the iframes.
+ const refQuad = refNode.getBoxQuadsFromWindowOrigin()[0];
+ const offsetX = refQuad.p1.x;
+ const offsetY = refQuad.p1.y;
+ info(`Reference node is offset (${offsetX}, ${offsetY}) from window.`);
+
+ function postAndReceiveMessage(iframe) {
+ return new Promise(resolve => {
+ const onmessage = event => {
+ if (event.data.quad) {
+ win.removeEventListener("message", onmessage);
+ resolve(event.data.quad);
+ }
+ };
+ win.addEventListener("message", onmessage, { capture: false });
+ iframe.contentWindow.postMessage({ callGetBoxQuads: true }, "*");
+ });
+ }
+
+ // Bug 1624659: Our checks are not always precise, though for these test
+ // cases they should align precisely to css pixels. For now we use an
+ // epsilon and a locally-defined isfuzzy to compensate. We can't use
+ // SimpleTest.isfuzzy, because it's not bridged to the ContentTask.
+ // If that is ever bridged, we can remove the isfuzzy definition here and
+ // everything should "just work".
+ function isfuzzy(actual, expected, epsilon, msg) {
+ if (actual >= expected - epsilon && actual <= expected + epsilon) {
+ ok(true, msg);
+ } else {
+ // This will trigger the usual failure message for is.
+ is(actual, expected, msg);
+ }
+ }
+
+ const ADDITIVE_EPSILON = 1;
+ const checksToMake = [
+ {
+ msg: "C-div",
+ iframe: iframeB,
+ left: 130,
+ top: 230,
+ right: 180,
+ bottom: 280,
+ },
+ {
+ msg: "E-div",
+ iframe: iframeD,
+ left: 430,
+ top: 260,
+ right: 480,
+ bottom: 310,
+ },
+ ];
+
+ for (const { msg, iframe, left, top, right, bottom } of checksToMake) {
+ info("Checking " + msg + ".");
+ const quad = await postAndReceiveMessage(iframe);
+ const bounds = quad.getBounds();
+ info(
+ `Quad bounds is (${bounds.left}, ${bounds.top}) to (${bounds.right}, ${bounds.bottom}).`
+ );
+ isfuzzy(
+ bounds.left - offsetX,
+ left,
+ ADDITIVE_EPSILON,
+ msg + " quad left position is as expected."
+ );
+ isfuzzy(
+ bounds.top - offsetY,
+ top,
+ ADDITIVE_EPSILON,
+ msg + " quad top position is as expected."
+ );
+ isfuzzy(
+ bounds.right - offsetX,
+ right,
+ ADDITIVE_EPSILON,
+ msg + " quad right position is as expected."
+ );
+ isfuzzy(
+ bounds.bottom - offsetY,
+ bottom,
+ ADDITIVE_EPSILON,
+ msg + " quad bottom position is as expected."
+ );
+ }
+ });
+
+ fissionWin.gBrowser.removeCurrentTab();
+
+ await BrowserTestUtils.closeWindow(fissionWin);
+
+ // Clean up the properties added to window by paint_listener.js.
+ delete window.waitForAllPaintsFlushed;
+ delete window.waitForAllPaints;
+ delete window.promiseAllPaintsDone;
+});
diff --git a/devtools/client/shared/test/browser_link.js b/devtools/client/shared/test/browser_link.js
new file mode 100644
index 0000000000..ef566288d9
--- /dev/null
+++ b/devtools/client/shared/test/browser_link.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test link helpers openDocLink, openTrustedLink.
+
+// Use any valid test page here.
+const TEST_URI = TEST_URI_ROOT_SSL + "dummy.html";
+
+const {
+ openDocLink,
+ openTrustedLink,
+} = require("resource://devtools/client/shared/link.js");
+
+add_task(async function () {
+ // Open a link to a page that will not trigger any request.
+ info("Open web link to example.com test page");
+ openDocLink(TEST_URI);
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ TEST_URI,
+ "openDocLink opened a tab with the expected url"
+ );
+
+ info("Open trusted link to about:config");
+ openTrustedLink("about:config");
+ await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+
+ is(
+ gBrowser.selectedBrowser.currentURI.spec,
+ "about:config",
+ "openTrustedLink opened a tab with the expected url"
+ );
+
+ await removeTab(gBrowser.selectedTab);
+ await removeTab(gBrowser.selectedTab);
+});
diff --git a/devtools/client/shared/test/browser_num-l10n.js b/devtools/client/shared/test/browser_num-l10n.js
new file mode 100644
index 0000000000..0bdda44f5d
--- /dev/null
+++ b/devtools/client/shared/test/browser_num-l10n.js
@@ -0,0 +1,70 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the localization utils work properly.
+
+function test() {
+ const l10n = new LocalizationHelper();
+
+ is(
+ l10n.numberWithDecimals(1234.56789, 2),
+ "1,234.57",
+ "The first number was properly localized."
+ );
+ is(
+ l10n.numberWithDecimals(0.0001, 2),
+ "0",
+ "The second number was properly localized."
+ );
+ is(
+ l10n.numberWithDecimals(1.0001, 2),
+ "1",
+ "The third number was properly localized."
+ );
+ is(l10n.numberWithDecimals(NaN, 2), "0", "NaN was properly localized.");
+ is(l10n.numberWithDecimals(null, 2), "0", "`null` was properly localized.");
+ is(
+ l10n.numberWithDecimals(undefined, 2),
+ "0",
+ "`undefined` was properly localized."
+ );
+ is(
+ l10n.numberWithDecimals(-1234.56789, 2),
+ "-1,234.57",
+ "Negative number was properly localized."
+ );
+ is(
+ l10n.numberWithDecimals(1234.56789, 0),
+ "1,235",
+ "Number was properly localized with decimals set 0."
+ );
+ is(
+ l10n.numberWithDecimals(-1234.56789, 0),
+ "-1,235",
+ "Negative number was properly localized with decimals set 0."
+ );
+ is(
+ l10n.numberWithDecimals(12, 2),
+ "12",
+ "The integer was properly localized, without decimals."
+ );
+ is(
+ l10n.numberWithDecimals(-12, 2),
+ "-12",
+ "The negative integer was properly localized, without decimals."
+ );
+ is(
+ l10n.numberWithDecimals(1200, 2),
+ "1,200",
+ "The big integer was properly localized, no decimals but with a separator."
+ );
+ is(
+ l10n.numberWithDecimals(-1200, 2),
+ "-1,200",
+ "The negative big integer was properly localized, no decimals but with a separator."
+ );
+
+ finish();
+}
diff --git a/devtools/client/shared/test/browser_outputparser.js b/devtools/client/shared/test/browser_outputparser.js
new file mode 100644
index 0000000000..226e0bb685
--- /dev/null
+++ b/devtools/client/shared/test/browser_outputparser.js
@@ -0,0 +1,856 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function () {
+ await pushPref("layout.css.backdrop-filter.enabled", true);
+ await pushPref("layout.css.individual-transform.enabled", true);
+ await pushPref("layout.css.motion-path-basic-shapes.enabled", true);
+ await addTab("about:blank");
+ await performTest();
+ gBrowser.removeCurrentTab();
+});
+
+async function performTest() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.allow_unsafe_parent_loads", true]],
+ });
+
+ const OutputParser = require("resource://devtools/client/shared/output-parser.js");
+
+ const { host, doc } = await createHost(
+ "bottom",
+ "data:text/html," + "<h1>browser_outputParser.js</h1><div></div>"
+ );
+
+ const cssProperties = getClientCssProperties();
+
+ const parser = new OutputParser(doc, cssProperties);
+ testParseCssProperty(doc, parser);
+ testParseCssVar(doc, parser);
+ testParseURL(doc, parser);
+ testParseFilter(doc, parser);
+ testParseBackdropFilter(doc, parser);
+ testParseAngle(doc, parser);
+ testParseShape(doc, parser);
+ testParseVariable(doc, parser);
+ testParseColorVariable(doc, parser);
+ testParseFontFamily(doc, parser);
+
+ host.destroy();
+}
+
+// Class name used in color swatch.
+var COLOR_TEST_CLASS = "test-class";
+
+// Create a new CSS color-parsing test. |name| is the name of the CSS
+// property. |value| is the CSS text to use. |segments| is an array
+// describing the expected result. If an element of |segments| is a
+// string, it is simply appended to the expected string. Otherwise,
+// it must be an object with a |name| property, which is the color
+// name as it appears in the input.
+//
+// This approach is taken to reduce boilerplate and to make it simpler
+// to modify the test when the parseCssProperty output changes.
+function makeColorTest(name, value, segments) {
+ const result = {
+ name,
+ value,
+ expected: "",
+ };
+
+ for (const segment of segments) {
+ if (typeof segment === "string") {
+ result.expected += segment;
+ } else {
+ const buttonAttributes = {
+ class: COLOR_TEST_CLASS,
+ style: `background-color:${segment.name}`,
+ tabindex: 0,
+ role: "button",
+ };
+ if (segment.colorFunction) {
+ buttonAttributes["data-color-function"] = segment.colorFunction;
+ }
+ const buttonAttrString = Object.entries(buttonAttributes)
+ .map(([attr, v]) => `${attr}="${v}"`)
+ .join(" ");
+
+ // prettier-ignore
+ result.expected +=
+ `<span data-color="${segment.name}">` +
+ `<span ${buttonAttrString}></span>`+
+ `<span>${segment.name}</span>` +
+ `</span>`;
+ }
+ }
+
+ result.desc = "Testing " + name + ": " + value;
+
+ return result;
+}
+
+function testParseCssProperty(doc, parser) {
+ const tests = [
+ makeColorTest("border", "1px solid red", ["1px solid ", { name: "red" }]),
+
+ makeColorTest(
+ "background-image",
+ "linear-gradient(to right, #F60 10%, rgba(0,0,0,1))",
+ [
+ "linear-gradient(to right, ",
+ { name: "#F60", colorFunction: "linear-gradient" },
+ " 10%, ",
+ { name: "rgba(0,0,0,1)", colorFunction: "linear-gradient" },
+ ")",
+ ]
+ ),
+
+ // In "arial black", "black" is a font, not a color.
+ // (The font-family parser creates a span)
+ makeColorTest("font-family", "arial black", ["<span>arial black</span>"]),
+
+ makeColorTest("box-shadow", "0 0 1em red", ["0 0 1em ", { name: "red" }]),
+
+ makeColorTest("box-shadow", "0 0 1em red, 2px 2px 0 0 rgba(0,0,0,.5)", [
+ "0 0 1em ",
+ { name: "red" },
+ ", 2px 2px 0 0 ",
+ { name: "rgba(0,0,0,.5)" },
+ ]),
+
+ makeColorTest("content", '"red"', ['"red"']),
+
+ // Invalid property names should not cause exceptions.
+ makeColorTest("hellothere", "'red'", ["'red'"]),
+
+ makeColorTest(
+ "filter",
+ "blur(1px) drop-shadow(0 0 0 blue) url(red.svg#blue)",
+ [
+ '<span data-filters="blur(1px) drop-shadow(0 0 0 blue) ',
+ 'url(red.svg#blue)"><span>',
+ "blur(1px) drop-shadow(0 0 0 ",
+ { name: "blue", colorFunction: "drop-shadow" },
+ ") url(red.svg#blue)</span></span>",
+ ]
+ ),
+
+ makeColorTest("color", "currentColor", ["currentColor"]),
+
+ // Test a very long property.
+ makeColorTest(
+ "background-image",
+ "linear-gradient(to left, transparent 0, transparent 5%,#F00 0, #F00 10%,#FF0 0, #FF0 15%,#0F0 0, #0F0 20%,#0FF 0, #0FF 25%,#00F 0, #00F 30%,#800 0, #800 35%,#880 0, #880 40%,#080 0, #080 45%,#088 0, #088 50%,#008 0, #008 55%,#FFF 0, #FFF 60%,#EEE 0, #EEE 65%,#CCC 0, #CCC 70%,#999 0, #999 75%,#666 0, #666 80%,#333 0, #333 85%,#111 0, #111 90%,#000 0, #000 95%,transparent 0, transparent 100%)",
+ [
+ "linear-gradient(to left, ",
+ { name: "transparent", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "transparent", colorFunction: "linear-gradient" },
+ " 5%,",
+ { name: "#F00", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#F00", colorFunction: "linear-gradient" },
+ " 10%,",
+ { name: "#FF0", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#FF0", colorFunction: "linear-gradient" },
+ " 15%,",
+ { name: "#0F0", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#0F0", colorFunction: "linear-gradient" },
+ " 20%,",
+ { name: "#0FF", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#0FF", colorFunction: "linear-gradient" },
+ " 25%,",
+ { name: "#00F", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#00F", colorFunction: "linear-gradient" },
+ " 30%,",
+ { name: "#800", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#800", colorFunction: "linear-gradient" },
+ " 35%,",
+ { name: "#880", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#880", colorFunction: "linear-gradient" },
+ " 40%,",
+ { name: "#080", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#080", colorFunction: "linear-gradient" },
+ " 45%,",
+ { name: "#088", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#088", colorFunction: "linear-gradient" },
+ " 50%,",
+ { name: "#008", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#008", colorFunction: "linear-gradient" },
+ " 55%,",
+ { name: "#FFF", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#FFF", colorFunction: "linear-gradient" },
+ " 60%,",
+ { name: "#EEE", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#EEE", colorFunction: "linear-gradient" },
+ " 65%,",
+ { name: "#CCC", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#CCC", colorFunction: "linear-gradient" },
+ " 70%,",
+ { name: "#999", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#999", colorFunction: "linear-gradient" },
+ " 75%,",
+ { name: "#666", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#666", colorFunction: "linear-gradient" },
+ " 80%,",
+ { name: "#333", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#333", colorFunction: "linear-gradient" },
+ " 85%,",
+ { name: "#111", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#111", colorFunction: "linear-gradient" },
+ " 90%,",
+ { name: "#000", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "#000", colorFunction: "linear-gradient" },
+ " 95%,",
+ { name: "transparent", colorFunction: "linear-gradient" },
+ " 0, ",
+ { name: "transparent", colorFunction: "linear-gradient" },
+ " 100%)",
+ ]
+ ),
+
+ // Note the lack of a space before the color here.
+ makeColorTest("border", "1px dotted#f06", [
+ "1px dotted ",
+ { name: "#f06" },
+ ]),
+
+ makeColorTest("color", "color-mix(in srgb, red, blue)", [
+ "color-mix(in srgb, ",
+ { name: "red", colorFunction: "color-mix" },
+ ", ",
+ { name: "blue", colorFunction: "color-mix" },
+ ")",
+ ]),
+
+ makeColorTest(
+ "background-image",
+ "linear-gradient(to top, color-mix(in srgb, #008000, rgba(255, 255, 0, 0.9)), blue)",
+ [
+ "linear-gradient(to top, ",
+ "color-mix(in srgb, ",
+ { name: "#008000", colorFunction: "color-mix" },
+ ", ",
+ { name: "rgba(255, 255, 0, 0.9)", colorFunction: "color-mix" },
+ "), ",
+ { name: "blue", colorFunction: "linear-gradient" },
+ ")",
+ ]
+ ),
+ ];
+
+ const target = doc.querySelector("div");
+ ok(target, "captain, we have the div");
+
+ for (const test of tests) {
+ info(test.desc);
+
+ const frag = parser.parseCssProperty(test.name, test.value, {
+ colorSwatchClass: COLOR_TEST_CLASS,
+ });
+
+ target.appendChild(frag);
+
+ is(
+ target.innerHTML,
+ test.expected,
+ "CSS property correctly parsed for " + test.name + ": " + test.value
+ );
+
+ target.innerHTML = "";
+ }
+}
+
+function testParseCssVar(doc, parser) {
+ const frag = parser.parseCssProperty("color", "var(--some-kind-of-green)", {
+ colorSwatchClass: "test-colorswatch",
+ });
+
+ const target = doc.querySelector("div");
+ ok(target, "captain, we have the div");
+ target.appendChild(frag);
+
+ is(
+ target.innerHTML,
+ "var(--some-kind-of-green)",
+ "CSS property correctly parsed"
+ );
+
+ target.innerHTML = "";
+}
+
+function testParseURL(doc, parser) {
+ info("Test that URL parsing preserves quoting style");
+
+ const tests = [
+ {
+ desc: "simple test without quotes",
+ leader: "url(",
+ trailer: ")",
+ },
+ {
+ desc: "simple test with single quotes",
+ leader: "url('",
+ trailer: "')",
+ },
+ {
+ desc: "simple test with double quotes",
+ leader: 'url("',
+ trailer: '")',
+ },
+ {
+ desc: "test with single quotes and whitespace",
+ leader: "url( \t'",
+ trailer: "'\r\n\f)",
+ },
+ {
+ desc: "simple test with uppercase",
+ leader: "URL(",
+ trailer: ")",
+ },
+ {
+ desc: "bad url, missing paren",
+ leader: "url(",
+ trailer: "",
+ expectedTrailer: ")",
+ },
+ {
+ desc: "bad url, missing paren, with baseURI",
+ baseURI: "data:text/html,<style></style>",
+ leader: "url(",
+ trailer: "",
+ expectedTrailer: ")",
+ },
+ {
+ desc: "bad url, double quote, missing paren",
+ leader: 'url("',
+ trailer: '"',
+ expectedTrailer: '")',
+ },
+ {
+ desc: "bad url, single quote, missing paren and quote",
+ leader: "url('",
+ trailer: "",
+ expectedTrailer: "')",
+ },
+ ];
+
+ for (const test of tests) {
+ const url = test.leader + "something.jpg" + test.trailer;
+ const frag = parser.parseCssProperty("background", url, {
+ urlClass: "test-urlclass",
+ baseURI: test.baseURI,
+ });
+
+ const target = doc.querySelector("div");
+ target.appendChild(frag);
+
+ const expectedTrailer = test.expectedTrailer || test.trailer;
+
+ const expected =
+ test.leader +
+ '<a target="_blank" class="test-urlclass" ' +
+ 'href="something.jpg">something.jpg</a>' +
+ expectedTrailer;
+
+ is(target.innerHTML, expected, test.desc);
+
+ target.innerHTML = "";
+ }
+}
+
+function testParseFilter(doc, parser) {
+ const frag = parser.parseCssProperty("filter", "something invalid", {
+ filterSwatchClass: "test-filterswatch",
+ });
+
+ const swatchCount = frag.querySelectorAll(".test-filterswatch").length;
+ is(swatchCount, 1, "filter swatch was created");
+}
+
+function testParseBackdropFilter(doc, parser) {
+ const frag = parser.parseCssProperty("backdrop-filter", "something invalid", {
+ filterSwatchClass: "test-filterswatch",
+ });
+
+ const swatchCount = frag.querySelectorAll(".test-filterswatch").length;
+ is(swatchCount, 1, "filter swatch was created for backdrop-filter");
+}
+
+function testParseAngle(doc, parser) {
+ let frag = parser.parseCssProperty("rotate", "90deg", {
+ angleSwatchClass: "test-angleswatch",
+ });
+
+ let swatchCount = frag.querySelectorAll(".test-angleswatch").length;
+ is(swatchCount, 1, "angle swatch was created");
+
+ frag = parser.parseCssProperty(
+ "background-image",
+ "linear-gradient(90deg, red, blue",
+ {
+ angleSwatchClass: "test-angleswatch",
+ }
+ );
+
+ swatchCount = frag.querySelectorAll(".test-angleswatch").length;
+ is(swatchCount, 1, "angle swatch was created");
+}
+
+function testParseShape(doc, parser) {
+ info("Test shape parsing");
+
+ const tests = [
+ {
+ desc: "Polygon shape",
+ definition:
+ "polygon(evenodd, 0px 0px, 10%200px,30%30% , calc(250px - 10px) 0 ,\n " +
+ "12em var(--variable), 100% 100%) margin-box",
+ spanCount: 18,
+ },
+ {
+ desc: "POLYGON()",
+ definition:
+ "POLYGON(evenodd, 0px 0px, 10%200px,30%30% , calc(250px - 10px) 0 ,\n " +
+ "12em var(--variable), 100% 100%) margin-box",
+ spanCount: 18,
+ },
+ {
+ desc: "Invalid polygon shape",
+ definition: "polygon(0px 0px 100px 20px, 20% 20%)",
+ spanCount: 0,
+ },
+ {
+ desc: "Circle shape with all arguments",
+ definition: "circle(25% at\n 30% 200px) border-box",
+ spanCount: 4,
+ },
+ {
+ desc: "Circle shape with only one center",
+ definition: "circle(25em at 40%)",
+ spanCount: 3,
+ },
+ {
+ desc: "Circle shape with no radius",
+ definition: "circle(at 30% 40%)",
+ spanCount: 3,
+ },
+ {
+ desc: "Circle shape with no center",
+ definition: "circle(12em)",
+ spanCount: 1,
+ },
+ {
+ desc: "Circle shape with no arguments",
+ definition: "circle()",
+ spanCount: 0,
+ },
+ {
+ desc: "Circle shape with no space before at",
+ definition: "circle(25%at 30% 30%)",
+ spanCount: 4,
+ },
+ {
+ desc: "CIRCLE",
+ definition: "CIRCLE(12em)",
+ spanCount: 1,
+ },
+ {
+ desc: "Invalid circle shape",
+ definition: "circle(25%at30%30%)",
+ spanCount: 0,
+ },
+ {
+ desc: "Ellipse shape with all arguments",
+ definition: "ellipse(200px 10em at 25% 120px) content-box",
+ spanCount: 5,
+ },
+ {
+ desc: "Ellipse shape with only one center",
+ definition: "ellipse(200px 10% at 120px)",
+ spanCount: 4,
+ },
+ {
+ desc: "Ellipse shape with no radius",
+ definition: "ellipse(at 25% 120px)",
+ spanCount: 3,
+ },
+ {
+ desc: "Ellipse shape with no center",
+ definition: "ellipse(200px\n10em)",
+ spanCount: 2,
+ },
+ {
+ desc: "Ellipse shape with no arguments",
+ definition: "ellipse()",
+ spanCount: 0,
+ },
+ {
+ desc: "ELLIPSE()",
+ definition: "ELLIPSE(200px 10em)",
+ spanCount: 2,
+ },
+ {
+ desc: "Invalid ellipse shape",
+ definition: "ellipse(200px100px at 30$ 20%)",
+ spanCount: 0,
+ },
+ {
+ desc: "Inset shape with 4 arguments",
+ definition: "inset(200px 100px\n 30%15%)",
+ spanCount: 4,
+ },
+ {
+ desc: "Inset shape with 3 arguments",
+ definition: "inset(200px 100px 15%)",
+ spanCount: 3,
+ },
+ {
+ desc: "Inset shape with 2 arguments",
+ definition: "inset(200px 100px)",
+ spanCount: 2,
+ },
+ {
+ desc: "Inset shape with 1 argument",
+ definition: "inset(200px)",
+ spanCount: 1,
+ },
+ {
+ desc: "Inset shape with 0 arguments",
+ definition: "inset()",
+ spanCount: 0,
+ },
+ {
+ desc: "INSET()",
+ definition: "INSET(200px)",
+ spanCount: 1,
+ },
+ {
+ desc: "offset-path property with inset shape value",
+ property: "offset-path",
+ definition: "inset(200px)",
+ spanCount: 1,
+ },
+ ];
+
+ for (const { desc, definition, property = "clip-path", spanCount } of tests) {
+ info(desc);
+ const frag = parser.parseCssProperty(property, definition, {
+ shapeClass: "ruleview-shape",
+ });
+ const spans = frag.querySelectorAll(".ruleview-shape-point");
+ is(spans.length, spanCount, desc + " span count");
+ is(frag.textContent, definition, desc + " text content");
+ }
+}
+
+function testParseVariable(doc, parser) {
+ const TESTS = [
+ {
+ text: "var(--seen)",
+ variables: { "--seen": "chartreuse" },
+ expected:
+ // prettier-ignore
+ '<span data-color="chartreuse">' +
+ "<span>var(" +
+ '<span data-variable="--seen = chartreuse">--seen</span>)' +
+ "</span>" +
+ "</span>",
+ },
+ {
+ text: "var(--not-seen)",
+ variables: {},
+ expected:
+ // prettier-ignore
+ "<span>var(" +
+ '<span class="unmatched-class" data-variable="--not-seen is not set">--not-seen</span>' +
+ ")</span>",
+ },
+ {
+ text: "var(--seen, seagreen)",
+ variables: { "--seen": "chartreuse" },
+ expected:
+ // prettier-ignore
+ '<span data-color="chartreuse">' +
+ "<span>var(" +
+ '<span data-variable="--seen = chartreuse">--seen</span>,' +
+ '<span class="unmatched-class"> ' +
+ '<span data-color="seagreen">' +
+ "<span>seagreen</span>" +
+ "</span>" +
+ "</span>)" +
+ "</span>" +
+ "</span>",
+ },
+ {
+ text: "var(--not-seen, var(--seen))",
+ variables: { "--seen": "chartreuse" },
+ expected:
+ // prettier-ignore
+ "<span>var(" +
+ '<span class="unmatched-class" data-variable="--not-seen is not set">--not-seen</span>,' +
+ "<span> " +
+ '<span data-color="chartreuse">' +
+ "<span>var(" +
+ '<span data-variable="--seen = chartreuse">--seen</span>)' +
+ "</span>" +
+ "</span>" +
+ "</span>)" +
+ "</span>",
+ },
+ {
+ text: "color-mix(in sgrb, var(--x), purple)",
+ variables: { "--x": "yellow" },
+ expected:
+ // prettier-ignore
+ `color-mix(in sgrb, ` +
+ `<span data-color="yellow">` +
+ `<span class="test-class" style="background-color:yellow" tabindex="0" role="button" data-color-function="color-mix">` +
+ `</span>` +
+ `<span>var(<span data-variable="--x = yellow">--x</span>)</span>` +
+ `</span>` +
+ `, ` +
+ `<span data-color="purple">` +
+ `<span class="test-class" style="background-color:purple" tabindex="0" role="button" data-color-function="color-mix">` +
+ `</span>` +
+ `<span>purple</span>` +
+ `</span>` +
+ `)`,
+ parserExtraOptions: {
+ colorSwatchClass: COLOR_TEST_CLASS,
+ },
+ },
+ {
+ text: "1px solid var(--seen, seagreen)",
+ variables: { "--seen": "chartreuse" },
+ expected:
+ // prettier-ignore
+ '1px solid ' +
+ '<span data-color="chartreuse">' +
+ "<span>var(" +
+ '<span data-variable="--seen = chartreuse">--seen</span>,' +
+ '<span class="unmatched-class"> ' +
+ '<span data-color="seagreen">' +
+ "<span>seagreen</span>" +
+ "</span>" +
+ "</span>)" +
+ "</span>" +
+ "</span>",
+ },
+ {
+ text: "1px solid var(--not-seen, seagreen)",
+ variables: {},
+ expected:
+ // prettier-ignore
+ `1px solid ` +
+ `<span>var(` +
+ `<span class="unmatched-class" data-variable="--not-seen is not set">--not-seen</span>,` +
+ `<span> ` +
+ `<span data-color="seagreen">` +
+ `<span>seagreen</span>` +
+ `</span>` +
+ `</span>)` +
+ `</span>`,
+ },
+ ];
+
+ for (const test of TESTS) {
+ const getValue = function (varName) {
+ return test.variables[varName];
+ };
+
+ const frag = parser.parseCssProperty("color", test.text, {
+ getVariableValue: getValue,
+ unmatchedVariableClass: "unmatched-class",
+ ...(test.parserExtraOptions || {}),
+ });
+
+ const target = doc.querySelector("div");
+ target.appendChild(frag);
+
+ is(target.innerHTML, test.expected, test.text);
+ target.innerHTML = "";
+ }
+}
+
+function testParseColorVariable(doc, parser) {
+ const testCategories = [
+ {
+ desc: "Test for CSS variable defining color",
+ tests: [
+ makeColorTest("--test-var", "lime", [{ name: "lime" }]),
+ makeColorTest("--test-var", "#000", [{ name: "#000" }]),
+ ],
+ },
+ {
+ desc: "Test for CSS variable not defining color",
+ tests: [
+ makeColorTest("--foo", "something", ["something"]),
+ makeColorTest("--bar", "Arial Black", ["Arial Black"]),
+ makeColorTest("--baz", "10vmin", ["10vmin"]),
+ ],
+ },
+ {
+ desc: "Test for non CSS variable defining color",
+ tests: [
+ makeColorTest("non-css-variable", "lime", ["lime"]),
+ makeColorTest("-non-css-variable", "#000", ["#000"]),
+ ],
+ },
+ ];
+
+ for (const category of testCategories) {
+ info(category.desc);
+
+ for (const test of category.tests) {
+ info(test.desc);
+ const target = doc.querySelector("div");
+
+ const frag = parser.parseCssProperty(test.name, test.value, {
+ colorSwatchClass: COLOR_TEST_CLASS,
+ });
+
+ target.appendChild(frag);
+
+ is(
+ target.innerHTML,
+ test.expected,
+ `The parsed result for '${test.name}: ${test.value}' is correct`
+ );
+
+ target.innerHTML = "";
+ }
+ }
+}
+
+function testParseFontFamily(doc, parser) {
+ info("Test font-family parsing");
+ const tests = [
+ {
+ desc: "No fonts",
+ definition: "",
+ families: [],
+ },
+ {
+ desc: "List of fonts",
+ definition: "Arial,Helvetica,sans-serif",
+ families: ["Arial", "Helvetica", "sans-serif"],
+ },
+ {
+ desc: "Fonts with spaces",
+ definition: "Open Sans",
+ families: ["Open Sans"],
+ },
+ {
+ desc: "Quoted fonts",
+ definition: "\"Arial\",'Open Sans'",
+ families: ["Arial", "Open Sans"],
+ },
+ {
+ desc: "Fonts with extra whitespace",
+ definition: " Open Sans ",
+ families: ["Open Sans"],
+ },
+ ];
+
+ const textContentTests = [
+ {
+ desc: "No whitespace between fonts",
+ definition: "Arial,Helvetica,sans-serif",
+ output: "Arial,Helvetica,sans-serif",
+ },
+ {
+ desc: "Whitespace between fonts",
+ definition: "Arial , Helvetica, sans-serif",
+ output: "Arial , Helvetica, sans-serif",
+ },
+ {
+ desc: "Whitespace before first font trimmed",
+ definition: " Arial,Helvetica,sans-serif",
+ output: "Arial,Helvetica,sans-serif",
+ },
+ {
+ desc: "Whitespace after last font trimmed",
+ definition: "Arial,Helvetica,sans-serif ",
+ output: "Arial,Helvetica,sans-serif",
+ },
+ {
+ desc: "Whitespace between quoted fonts",
+ definition: "'Arial' , \"Helvetica\" ",
+ output: "'Arial' , \"Helvetica\"",
+ },
+ {
+ desc: "Whitespace within font preserved",
+ definition: "' Ari al '",
+ output: "' Ari al '",
+ },
+ ];
+
+ for (const { desc, definition, families } of tests) {
+ info(desc);
+ const frag = parser.parseCssProperty("font-family", definition, {
+ fontFamilyClass: "ruleview-font-family",
+ });
+ const spans = frag.querySelectorAll(".ruleview-font-family");
+
+ is(spans.length, families.length, desc + " span count");
+ for (let i = 0; i < spans.length; i++) {
+ is(spans[i].textContent, families[i], desc + " span contents");
+ }
+ }
+
+ info("Test font-family text content");
+ for (const { desc, definition, output } of textContentTests) {
+ info(desc);
+ const frag = parser.parseCssProperty("font-family", definition, {});
+ is(frag.textContent, output, desc + " text content matches");
+ }
+
+ info("Test font-family with custom properties");
+ const frag = parser.parseCssProperty(
+ "font-family",
+ "var(--family, Georgia, serif)",
+ {
+ getVariableValue: () => {},
+ unmatchedVariableClass: "unmatched-class",
+ fontFamilyClass: "ruleview-font-family",
+ }
+ );
+ const target = doc.createElement("div");
+ target.appendChild(frag);
+ is(
+ target.innerHTML,
+ // prettier-ignore
+ `<span>var(` +
+ `<span class="unmatched-class" data-variable="--family is not set">` +
+ `--family` +
+ `</span>` +
+ `,` +
+ `<span> ` +
+ `<span class="ruleview-font-family">Georgia</span>` +
+ `, ` +
+ `<span class="ruleview-font-family">serif</span>` +
+ `</span>)` +
+ `</span>`,
+ "Got expected output for font-family with custom properties"
+ );
+}
diff --git a/devtools/client/shared/test/browser_prefs-01.js b/devtools/client/shared/test/browser_prefs-01.js
new file mode 100644
index 0000000000..2989cdee57
--- /dev/null
+++ b/devtools/client/shared/test/browser_prefs-01.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the preference helpers work properly.
+
+const { PrefsHelper } = require("resource://devtools/client/shared/prefs.js");
+
+function test() {
+ const Prefs = new PrefsHelper("devtools.debugger", {
+ foo: ["Bool", "enabled"],
+ });
+
+ const originalPrefValue = Services.prefs.getBoolPref(
+ "devtools.debugger.enabled"
+ );
+ is(Prefs.foo, originalPrefValue, "The pref value was correctly fetched.");
+
+ Prefs.foo = !originalPrefValue;
+ is(Prefs.foo, !originalPrefValue, "The pref was was correctly changed (1).");
+ is(
+ Services.prefs.getBoolPref("devtools.debugger.enabled"),
+ !originalPrefValue,
+ "The pref was was correctly changed (2)."
+ );
+
+ Services.prefs.setBoolPref("devtools.debugger.enabled", originalPrefValue);
+ info("The pref value was reset (1).");
+ is(
+ Prefs.foo,
+ !originalPrefValue,
+ "The cached pref value hasn't changed yet (1)."
+ );
+
+ Services.prefs.setBoolPref("devtools.debugger.enabled", !originalPrefValue);
+ info("The pref value was reset (2).");
+ is(
+ Prefs.foo,
+ !originalPrefValue,
+ "The cached pref value hasn't changed yet (2)."
+ );
+
+ Prefs.registerObserver();
+
+ Services.prefs.setBoolPref("devtools.debugger.enabled", originalPrefValue);
+ info("The pref value was reset (3).");
+ is(Prefs.foo, originalPrefValue, "The cached pref value has changed now.");
+
+ Prefs.unregisterObserver();
+
+ finish();
+}
diff --git a/devtools/client/shared/test/browser_prefs-02.js b/devtools/client/shared/test/browser_prefs-02.js
new file mode 100644
index 0000000000..ad32aa29ce
--- /dev/null
+++ b/devtools/client/shared/test/browser_prefs-02.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that preference helpers work properly with custom types of Float and Json.
+
+const { PrefsHelper } = require("resource://devtools/client/shared/prefs.js");
+
+function test() {
+ const Prefs = new PrefsHelper("prefs.helper.test", {
+ float: ["Float", "float"],
+ json: ["Json", "json"],
+ jsonWithSpecialChar: ["Json", "jsonWithSpecialChar"],
+ });
+
+ Prefs.registerObserver();
+
+ // JSON
+ Services.prefs.setCharPref("prefs.helper.test.json", '{"a":1}');
+ is(Prefs.json.a, 1, "The JSON pref value is correctly casted on get.");
+
+ Prefs.json = { b: 2 };
+ is(
+ Prefs.json.a,
+ undefined,
+ "The JSON pref value is correctly casted on set (1)."
+ );
+ is(Prefs.json.b, 2, "The JSON pref value is correctly casted on set (2).");
+
+ // JSON with special character
+ Services.prefs.setStringPref(
+ "prefs.helper.test.jsonWithSpecialChar",
+ `{"hello":"おはよう皆!"}`
+ );
+ is(
+ Prefs.jsonWithSpecialChar.hello,
+ "おはよう皆!",
+ "The JSON pref value with a special character is correctly stored."
+ );
+
+ Prefs.jsonWithSpecialChar = { bye: "さよなら!" };
+ is(
+ Prefs.jsonWithSpecialChar.hello,
+ undefined,
+ "The JSON with the special characters pref value is correctly set. (1)"
+ );
+ is(
+ Prefs.jsonWithSpecialChar.bye,
+ "さよなら!",
+ "The JSON with the special characters pref value is correctly set. (2)"
+ );
+
+ // Float
+ Services.prefs.setCharPref("prefs.helper.test.float", "3.14");
+ is(Prefs.float, 3.14, "The float pref value is correctly casted on get.");
+
+ Prefs.float = 6.28;
+ is(Prefs.float, 6.28, "The float pref value is correctly casted on set.");
+
+ Prefs.unregisterObserver();
+
+ Services.prefs.clearUserPref("prefs.helper.test.float");
+ Services.prefs.clearUserPref("prefs.helper.test.json");
+ Services.prefs.clearUserPref("prefs.helper.test.jsonWithSpecialChar");
+ finish();
+}
diff --git a/devtools/client/shared/test/browser_require_raw.js b/devtools/client/shared/test/browser_require_raw.js
new file mode 100644
index 0000000000..bcf4afe98f
--- /dev/null
+++ b/devtools/client/shared/test/browser_require_raw.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { BrowserLoader } = ChromeUtils.import(
+ "resource://devtools/shared/loader/browser-loader.js"
+);
+
+const { require: browserRequire } = BrowserLoader({
+ baseURI: "resource://devtools/client/shared/",
+ window,
+});
+
+const variableFileContents = browserRequire(
+ "raw!chrome://devtools/skin/variables.css"
+);
+
+function test() {
+ ok(!!variableFileContents.length, "raw browserRequire worked");
+ delete window.getBrowserLoaderForWindow;
+ finish();
+}
diff --git a/devtools/client/shared/test/browser_spectrum.js b/devtools/client/shared/test/browser_spectrum.js
new file mode 100644
index 0000000000..6189b1f0af
--- /dev/null
+++ b/devtools/client/shared/test/browser_spectrum.js
@@ -0,0 +1,518 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the spectrum color picker works correctly
+
+const Spectrum = require("resource://devtools/client/shared/widgets/Spectrum.js");
+const {
+ accessibility: {
+ SCORES: { FAIL, AAA, AA },
+ },
+} = require("resource://devtools/shared/constants.js");
+
+loader.lazyRequireGetter(
+ this,
+ "cssColors",
+ "resource://devtools/shared/css/color-db.js",
+ true
+);
+
+const TEST_URI = CHROME_URL_ROOT + "doc_spectrum.html";
+const REGULAR_TEXT_PROPS = {
+ "font-size": { value: "11px" },
+ "font-weight": { value: "bold" },
+ opacity: { value: "1" },
+};
+const SINGLE_BG_COLOR = {
+ value: cssColors.white,
+};
+const ZERO_ALPHA_COLOR = [0, 255, 255, 0];
+
+add_task(async function () {
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ const container = doc.getElementById("spectrum-container");
+ await testCreateAndDestroyShouldAppendAndRemoveElements(container);
+ await testPassingAColorAtInitShouldSetThatColor(container);
+ await testSettingAndGettingANewColor(container);
+ await testChangingColorShouldEmitEvents(container, doc);
+ await testSettingColorShoudUpdateTheUI(container);
+ await testChangingColorShouldUpdateColorPreview(container);
+ await testNotSettingTextPropsShouldNotShowContrastSection(container);
+ await testSettingTextPropsAndColorShouldUpdateContrastValue(container);
+ await testOnlySelectingLargeTextWithNonZeroAlphaShouldShowIndicator(
+ container
+ );
+ await testSettingMultiColoredBackgroundShouldShowContrastRange(container);
+
+ host.destroy();
+});
+
+/**
+ * Helper method for extracting the rgba overlay value of the color preview's background
+ * image style.
+ *
+ * @param {String} linearGradientStr
+ * The linear gradient CSS string.
+ * @return {String} Returns the rgba string for the color overlay.
+ */
+function extractRgbaOverlayString(linearGradientStr) {
+ const start = linearGradientStr.indexOf("(");
+ const end = linearGradientStr.indexOf(")");
+
+ return linearGradientStr.substring(start + 1, end + 1);
+}
+
+function testColorPreviewDisplay(
+ spectrum,
+ expectedRgbCssString,
+ expectedBorderColor
+) {
+ const { colorPreview } = spectrum;
+ const colorPreviewStyle = window.getComputedStyle(colorPreview);
+ expectedBorderColor =
+ expectedBorderColor === "transparent"
+ ? "rgba(0, 0, 0, 0)"
+ : expectedBorderColor;
+
+ spectrum.updateUI();
+
+ // Extract the first rgba value from the linear gradient
+ const linearGradientStr =
+ colorPreviewStyle.getPropertyValue("background-image");
+ const colorPreviewValue = extractRgbaOverlayString(linearGradientStr);
+
+ is(
+ colorPreviewValue,
+ expectedRgbCssString,
+ `Color preview should be ${expectedRgbCssString}`
+ );
+
+ info("Test if color preview has a border or not.");
+ // Since border-color is a shorthand CSS property, using getComputedStyle will return
+ // an empty string. Instead, use one of the border sides to find the border-color value
+ // since they will all be the same.
+ const borderColorTop = colorPreviewStyle.getPropertyValue("border-top-color");
+ is(
+ borderColorTop,
+ expectedBorderColor,
+ "Color preview border color is correct."
+ );
+}
+
+async function testCreateAndDestroyShouldAppendAndRemoveElements(container) {
+ ok(container, "We have the root node to append spectrum to");
+ is(container.childElementCount, 0, "Root node is empty");
+
+ const s = await createSpectrum(container, cssColors.white);
+ Assert.greater(
+ container.childElementCount,
+ 0,
+ "Spectrum has appended elements"
+ );
+
+ s.destroy();
+ is(container.childElementCount, 0, "Destroying spectrum removed all nodes");
+}
+
+async function testPassingAColorAtInitShouldSetThatColor(container) {
+ const initRgba = cssColors.white;
+
+ const s = await createSpectrum(container, initRgba);
+
+ const setRgba = s.rgb;
+
+ is(initRgba[0], setRgba[0], "Spectrum initialized with the right color");
+ is(initRgba[1], setRgba[1], "Spectrum initialized with the right color");
+ is(initRgba[2], setRgba[2], "Spectrum initialized with the right color");
+ is(initRgba[3], setRgba[3], "Spectrum initialized with the right color");
+
+ s.destroy();
+}
+
+async function testSettingAndGettingANewColor(container) {
+ const s = await createSpectrum(container, cssColors.black);
+
+ const colorToSet = cssColors.white;
+ s.rgb = colorToSet;
+ const newColor = s.rgb;
+
+ is(colorToSet[0], newColor[0], "Spectrum set with the right color");
+ is(colorToSet[1], newColor[1], "Spectrum set with the right color");
+ is(colorToSet[2], newColor[2], "Spectrum set with the right color");
+ is(colorToSet[3], newColor[3], "Spectrum set with the right color");
+
+ s.destroy();
+}
+
+async function testChangingColorShouldEmitEventsHelper(
+ spectrum,
+ moveFn,
+ expectedColor
+) {
+ const onChanged = spectrum.once("changed", (rgba, color) => {
+ is(rgba[0], expectedColor[0], "New color is correct");
+ is(rgba[1], expectedColor[1], "New color is correct");
+ is(rgba[2], expectedColor[2], "New color is correct");
+ is(rgba[3], expectedColor[3], "New color is correct");
+ is(`rgba(${rgba.join(", ")})`, color, "RGBA and css color correspond");
+ });
+
+ moveFn();
+ await onChanged;
+ ok(true, "Changed event was emitted on color change");
+}
+
+async function testChangingColorShouldEmitEvents(container, doc) {
+ const s = await createSpectrum(container, cssColors.white);
+
+ const sendUpKey = () => EventUtils.sendKey("Up");
+ const sendDownKey = () => EventUtils.sendKey("Down");
+ const sendLeftKey = () => EventUtils.sendKey("Left");
+ const sendRightKey = () => EventUtils.sendKey("Right");
+
+ info(
+ "Test that simulating a mouse drag move event emits color changed event"
+ );
+ const draggerMoveFn = () =>
+ s.onDraggerMove(s.dragger.offsetWidth / 2, s.dragger.offsetHeight / 2);
+ testChangingColorShouldEmitEventsHelper(s, draggerMoveFn, [128, 64, 64, 1]);
+
+ info(
+ "Test that moving the dragger with arrow keys emits color changed event."
+ );
+ // Focus on the spectrum dragger when spectrum is shown
+ s.dragger.focus();
+ is(
+ doc.activeElement.className,
+ "spectrum-color spectrum-box",
+ "Spectrum dragger has successfully received focus."
+ );
+ testChangingColorShouldEmitEventsHelper(s, sendDownKey, [125, 62, 62, 1]);
+ testChangingColorShouldEmitEventsHelper(s, sendLeftKey, [125, 63, 63, 1]);
+ testChangingColorShouldEmitEventsHelper(s, sendUpKey, [128, 64, 64, 1]);
+ testChangingColorShouldEmitEventsHelper(s, sendRightKey, [128, 63, 63, 1]);
+
+ info(
+ "Test that moving the hue slider with arrow keys emits color changed event."
+ );
+ // Tab twice to focus on hue slider
+ EventUtils.sendKey("Tab");
+ is(
+ doc.activeElement.className,
+ "devtools-button",
+ "Eyedropper has focus now."
+ );
+ EventUtils.sendKey("Tab");
+ is(
+ doc.activeElement.className,
+ "spectrum-hue-input",
+ "Hue slider has successfully received focus."
+ );
+ testChangingColorShouldEmitEventsHelper(s, sendRightKey, [128, 66, 63, 1]);
+ testChangingColorShouldEmitEventsHelper(s, sendLeftKey, [128, 63, 63, 1]);
+
+ info(
+ "Test that moving the hue slider with arrow keys emits color changed event."
+ );
+ // Tab to focus on alpha slider
+ EventUtils.sendKey("Tab");
+ is(
+ doc.activeElement.className,
+ "spectrum-alpha-input",
+ "Alpha slider has successfully received focus."
+ );
+ testChangingColorShouldEmitEventsHelper(s, sendLeftKey, [128, 63, 63, 0.99]);
+ testChangingColorShouldEmitEventsHelper(s, sendRightKey, [128, 63, 63, 1]);
+
+ s.destroy();
+}
+
+function setSpectrumProps(spectrum, props, updateUI = true) {
+ for (const prop in props) {
+ spectrum[prop] = props[prop];
+
+ // Setting textProps implies contrast should be enabled for spectrum
+ if (prop === "textProps") {
+ spectrum.contrastEnabled = true;
+ }
+ }
+
+ if (updateUI) {
+ spectrum.updateUI();
+ }
+}
+
+function testAriaAttributesOnSpectrumElements(
+ spectrum,
+ colorName,
+ rgbString,
+ alpha
+) {
+ for (const slider of [spectrum.dragger, spectrum.hueSlider]) {
+ is(
+ slider.getAttribute("aria-describedby"),
+ "spectrum-dragger",
+ "Slider contains the correct describedby text."
+ );
+ is(
+ slider.getAttribute("aria-valuetext"),
+ rgbString,
+ "Slider contains the correct valuetext text."
+ );
+ }
+
+ is(
+ spectrum.colorPreview.title,
+ colorName,
+ "Spectrum element contains the correct title text."
+ );
+}
+
+async function testSettingColorShoudUpdateTheUI(container) {
+ const s = await createSpectrum(container, cssColors.white);
+ const dragHelperOriginalPos = [
+ s.dragHelper.style.top,
+ s.dragHelper.style.left,
+ ];
+ const alphaSliderOriginalVal = s.alphaSlider.value;
+ let hueSliderOriginalVal = s.hueSlider.value;
+
+ setSpectrumProps(s, { rgb: [50, 240, 234, 0.2] });
+
+ Assert.notEqual(
+ s.alphaSlider.value,
+ alphaSliderOriginalVal,
+ "Alpha helper has moved"
+ );
+ Assert.notStrictEqual(
+ s.dragHelper.style.top,
+ dragHelperOriginalPos[0],
+ "Drag helper has moved"
+ );
+ Assert.notStrictEqual(
+ s.dragHelper.style.left,
+ dragHelperOriginalPos[1],
+ "Drag helper has moved"
+ );
+ Assert.notStrictEqual(
+ s.hueSlider.value,
+ hueSliderOriginalVal,
+ "Hue helper has moved"
+ );
+ testAriaAttributesOnSpectrumElements(
+ s,
+ "Closest to: aqua",
+ "rgba(50, 240, 234, 0.2)",
+ 0.2
+ );
+
+ hueSliderOriginalVal = s.hueSlider.value;
+
+ setSpectrumProps(s, { rgb: ZERO_ALPHA_COLOR });
+ is(s.alphaSlider.value, "0", "Alpha range UI has been updated again");
+ Assert.notStrictEqual(
+ hueSliderOriginalVal,
+ s.hueSlider.value,
+ "Hue slider should have move again"
+ );
+ testAriaAttributesOnSpectrumElements(s, "aqua", "rgba(0, 255, 255, 0)", 0);
+
+ s.destroy();
+}
+
+async function testChangingColorShouldUpdateColorPreview(container) {
+ const s = await createSpectrum(container, [0, 0, 1, 1]);
+
+ info("Test that color preview is black.");
+ testColorPreviewDisplay(s, "rgb(0, 0, 1)", "transparent");
+
+ info("Test that color preview is blue.");
+ s.rgb = [0, 0, 255, 1];
+ testColorPreviewDisplay(s, "rgb(0, 0, 255)", "transparent");
+
+ info("Test that color preview is red.");
+ s.rgb = [255, 0, 0, 1];
+ testColorPreviewDisplay(s, "rgb(255, 0, 0)", "transparent");
+
+ info("Test that color preview is white and also has a light grey border.");
+ s.rgb = cssColors.white;
+ testColorPreviewDisplay(s, "rgb(255, 255, 255)", "rgb(204, 204, 204)");
+
+ s.destroy();
+}
+
+async function testNotSettingTextPropsShouldNotShowContrastSection(container) {
+ const s = await createSpectrum(container, cssColors.white);
+
+ setSpectrumProps(s, { rgb: cssColors.black });
+ ok(
+ !s.spectrumContrast.classList.contains("visible"),
+ "Contrast section is not shown."
+ );
+
+ s.destroy();
+}
+
+function testSpectrumContrast(
+ spectrum,
+ contrastValueEl,
+ rgb,
+ expectedValue,
+ expectedBadgeClass = "",
+ expectLargeTextIndicator = false
+) {
+ setSpectrumProps(spectrum, { rgb });
+
+ is(
+ contrastValueEl.textContent,
+ expectedValue,
+ "Contrast value has the correct text."
+ );
+ is(
+ contrastValueEl.className,
+ `accessibility-contrast-value${
+ expectedBadgeClass ? " " + expectedBadgeClass : ""
+ }`,
+ `Contrast value contains ${expectedBadgeClass || "base"} class.`
+ );
+ is(
+ spectrum.contrastLabel.childNodes.length === 3,
+ expectLargeTextIndicator,
+ `Large text indicator is ${expectLargeTextIndicator ? "" : "not"} shown.`
+ );
+}
+
+async function testSettingTextPropsAndColorShouldUpdateContrastValue(
+ container
+) {
+ const s = await createSpectrum(container, cssColors.white);
+
+ ok(
+ !s.spectrumContrast.classList.contains("visible"),
+ "Contrast value is not available yet."
+ );
+
+ info(
+ "Test that contrast ratio is calculated on setting 'textProps' and 'rgb'."
+ );
+ setSpectrumProps(
+ s,
+ { textProps: REGULAR_TEXT_PROPS, backgroundColorData: SINGLE_BG_COLOR },
+ false
+ );
+ testSpectrumContrast(s, s.contrastValue, [50, 240, 234, 0.8], "1.35", FAIL);
+
+ info("Test that contrast ratio is updated when color is changed.");
+ testSpectrumContrast(s, s.contrastValue, cssColors.black, "21.00", AAA);
+
+ info("Test that contrast ratio cannot be calculated with zero alpha.");
+ testSpectrumContrast(
+ s,
+ s.contrastValue,
+ ZERO_ALPHA_COLOR,
+ "Unable to calculate"
+ );
+
+ s.destroy();
+}
+
+async function testOnlySelectingLargeTextWithNonZeroAlphaShouldShowIndicator(
+ container
+) {
+ let s = await createSpectrum(container, cssColors.white);
+
+ Assert.notStrictEqual(
+ s.contrastLabel.childNodes.length,
+ 3,
+ "Large text indicator is initially hidden."
+ );
+
+ info(
+ "Test that selecting large text with non-zero alpha shows large text indicator."
+ );
+ setSpectrumProps(
+ s,
+ {
+ textProps: {
+ "font-size": { value: "24px" },
+ "font-weight": { value: "normal" },
+ opacity: { value: "1" },
+ },
+ backgroundColorData: SINGLE_BG_COLOR,
+ },
+ false
+ );
+ testSpectrumContrast(s, s.contrastValue, cssColors.black, "21.00", AAA, true);
+
+ info(
+ "Test that selecting large text with zero alpha hides large text indicator."
+ );
+ testSpectrumContrast(
+ s,
+ s.contrastValue,
+ ZERO_ALPHA_COLOR,
+ "Unable to calculate"
+ );
+
+ // Spectrum should be closed and opened again to reflect changes in text size
+ s.destroy();
+ s = await createSpectrum(container, cssColors.white);
+
+ info("Test that selecting regular text does not show large text indicator.");
+ setSpectrumProps(
+ s,
+ { textProps: REGULAR_TEXT_PROPS, backgroundColorData: SINGLE_BG_COLOR },
+ false
+ );
+ testSpectrumContrast(s, s.contrastValue, cssColors.black, "21.00", AAA);
+
+ s.destroy();
+}
+
+async function testSettingMultiColoredBackgroundShouldShowContrastRange(
+ container
+) {
+ const s = await createSpectrum(container, cssColors.white);
+
+ info(
+ "Test setting text with non-zero alpha and multi-colored bg shows contrast range and empty single contrast."
+ );
+ setSpectrumProps(
+ s,
+ {
+ textProps: REGULAR_TEXT_PROPS,
+ backgroundColorData: {
+ min: cssColors.yellow,
+ max: cssColors.green,
+ },
+ },
+ false
+ );
+ testSpectrumContrast(s, s.contrastValueMin, cssColors.white, "1.07", FAIL);
+ testSpectrumContrast(s, s.contrastValueMax, cssColors.white, "5.14", AA);
+ testSpectrumContrast(s, s.contrastValue, cssColors.white, "");
+ ok(
+ s.spectrumContrast.classList.contains("range"),
+ "Contrast section contains range class."
+ );
+
+ info("Test setting text with zero alpha shows error in contrast min span.");
+ testSpectrumContrast(
+ s,
+ s.contrastValueMin,
+ ZERO_ALPHA_COLOR,
+ "Unable to calculate"
+ );
+
+ s.destroy();
+}
+
+async function createSpectrum(...spectrumConstructorParams) {
+ const s = new Spectrum(...spectrumConstructorParams);
+ await waitFor(() => s.dragger.offsetHeight > 0);
+ s.show();
+ return s;
+}
diff --git a/devtools/client/shared/test/browser_tableWidget_basic.js b/devtools/client/shared/test/browser_tableWidget_basic.js
new file mode 100644
index 0000000000..a8b737b9d0
--- /dev/null
+++ b/devtools/client/shared/test/browser_tableWidget_basic.js
@@ -0,0 +1,448 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the table widget api works fine
+
+"use strict";
+
+const TEST_URI = CHROME_URL_ROOT + "doc_tableWidget_basic.html";
+
+const {
+ TableWidget,
+} = require("resource://devtools/client/shared/widgets/TableWidget.js");
+
+add_task(async function () {
+ await addTab("about:blank");
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ const table = new TableWidget(doc.querySelector("box"), {
+ initialColumns: {
+ col1: "Column 1",
+ col2: "Column 2",
+ col3: "Column 3",
+ col4: "Column 4",
+ },
+ uniqueId: "col1",
+ emptyText: "dummy-text",
+ highlightUpdated: true,
+ removableColumns: true,
+ firstColumn: "col4",
+ l10n: {
+ setAttributes() {},
+ },
+ });
+
+ startTests(doc, table);
+ endTests(doc, host, table);
+});
+
+function startTests(doc, table) {
+ populateTable(doc, table);
+
+ testTreeItemInsertedCorrectly(doc, table);
+ testAPI(doc, table);
+}
+
+function endTests(doc, host, table) {
+ table.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+ table = null;
+ finish();
+}
+
+function populateTable(doc, table) {
+ table.push({
+ col1: "id1",
+ col2: "value10",
+ col3: "value20",
+ col4: "value30",
+ });
+ table.push({
+ col1: "id2",
+ col2: "value14",
+ col3: "value29",
+ col4: "value32",
+ });
+ table.push({
+ col1: "id3",
+ col2: "value17",
+ col3: "value21",
+ col4: "value31",
+ extraData: "foobar",
+ extraData2: 42,
+ });
+ table.push({
+ col1: "id4",
+ col2: "value12",
+ col3: "value26",
+ col4: "value33",
+ });
+ table.push({
+ col1: "id5",
+ col2: "value19",
+ col3: "value26",
+ col4: "value37",
+ });
+ table.push({
+ col1: "id6",
+ col2: "value15",
+ col3: "value25",
+ col4: "value37",
+ });
+ table.push({
+ col1: "id7",
+ col2: "value18",
+ col3: "value21",
+ col4: "value36",
+ somethingExtra: "Hello World!",
+ });
+ table.push({
+ col1: "id8",
+ col2: "value11",
+ col3: "value27",
+ col4: "value34",
+ });
+
+ const span = doc.createElement("span");
+ span.textContent = "domnode";
+
+ table.push({
+ col1: "id9",
+ col2: "value11",
+ col3: "value23",
+ col4: span,
+ });
+}
+
+/**
+ * Test if the nodes are inserted correctly in the table.
+ */
+function testTreeItemInsertedCorrectly(doc, table) {
+ // double because of splitters
+ is(table.tbody.children.length, 4 * 2, "4 columns exist");
+
+ // Test firstColumn option and check if the nodes are inserted correctly
+ is(
+ table.tbody.children[0].children.length,
+ 9 + 1,
+ "Correct rows in column 4"
+ );
+ is(
+ table.tbody.children[0].firstChild.value,
+ "Column 4",
+ "Correct column header value"
+ );
+
+ for (let i = 1; i < 4; i++) {
+ is(
+ table.tbody.children[i * 2].children.length,
+ 9 + 1,
+ `Correct rows in column ${i}`
+ );
+ is(
+ table.tbody.children[i * 2].firstChild.value,
+ `Column ${i}`,
+ "Correct column header value"
+ );
+ }
+ for (let i = 1; i < 10; i++) {
+ is(
+ table.tbody.children[2].children[i].value,
+ `id${i}`,
+ `Correct value in row ${i}`
+ );
+ }
+
+ // Remove firstColumn option and reset the table
+ info("resetting table");
+ table.clear();
+ table.firstColumn = "";
+ table.setColumns({
+ col1: "Column 1",
+ col2: "Column 2",
+ col3: "Column 3",
+ col4: "Column 4",
+ });
+ populateTable(doc, table);
+
+ // Check if the nodes are inserted correctly without firstColumn option
+ for (let i = 0; i < 4; i++) {
+ is(
+ table.tbody.children[i * 2].children.length,
+ 9 + 1,
+ `Correct rows in column ${i}`
+ );
+ is(
+ table.tbody.children[i * 2].firstChild.value,
+ `Column ${i + 1}`,
+ "Correct column header value"
+ );
+ }
+
+ for (let i = 1; i < 10; i++) {
+ is(
+ table.tbody.firstChild.children[i].value,
+ `id${i}`,
+ `Correct value in row ${i}`
+ );
+ }
+}
+
+/**
+ * Tests if the API exposed by TableWidget works properly
+ */
+function testAPI(doc, table) {
+ info("Testing TableWidget API");
+ // Check if selectRow and selectedRow setter works as expected
+ // Nothing should be selected beforehand
+ ok(!doc.querySelector(".theme-selected"), "Nothing is selected");
+ table.selectRow("id4");
+ const node = doc.querySelector(".theme-selected");
+ ok(!!node, "Somthing got selected");
+ is(node.getAttribute("data-id"), "id4", "Correct node selected");
+
+ table.selectRow("id7");
+ const node2 = doc.querySelector(".theme-selected");
+ ok(!!node2, "Somthing is still selected");
+ isnot(node, node2, "Newly selected node is different from previous");
+ is(node2.getAttribute("data-id"), "id7", "Correct node selected");
+
+ // test if selectedIRow getter works
+ is(table.selectedRow.col1, "id7", "Correct result of selectedRow getter");
+
+ // test if isSelected works
+ ok(table.isSelected("id7"), "isSelected with column id works");
+ ok(
+ table.isSelected({
+ col1: "id7",
+ col2: "value18",
+ col3: "value21",
+ col4: "value36",
+ somethingExtra: "Hello World!",
+ }),
+ "isSelected with json works"
+ );
+
+ table.selectedRow = "id4";
+ const node3 = doc.querySelector(".theme-selected");
+ ok(!!node3, "Somthing is still selected");
+ isnot(node2, node3, "Newly selected node is different from previous");
+ is(node3, node, "First and third selected nodes should be same");
+ is(node3.getAttribute("data-id"), "id4", "Correct node selected");
+
+ // test if selectedRow getter works
+ is(table.selectedRow.col1, "id4", "Correct result of selectedRow getter");
+
+ // test if clear selection works
+ table.clearSelection();
+ ok(
+ !doc.querySelector(".theme-selected"),
+ "Nothing selected after clear selection call"
+ );
+
+ // test if selectNextRow and selectPreviousRow work
+ table.selectedRow = "id7";
+ ok(table.isSelected("id7"), "Correct row selected");
+ table.selectNextRow();
+ ok(table.isSelected("id8"), "Correct row selected after selectNextRow call");
+
+ table.selectNextRow();
+ ok(table.isSelected("id9"), "Correct row selected after selectNextRow call");
+
+ table.selectNextRow();
+ ok(
+ table.isSelected("id1"),
+ "Properly cycled to first row after selectNextRow call on last row"
+ );
+
+ table.selectNextRow();
+ ok(table.isSelected("id2"), "Correct row selected after selectNextRow call");
+
+ table.selectPreviousRow();
+ ok(
+ table.isSelected("id1"),
+ "Correct row selected after selectPreviousRow call"
+ );
+
+ table.selectPreviousRow();
+ ok(
+ table.isSelected("id9"),
+ "Properly cycled to last row after selectPreviousRow call on first row"
+ );
+
+ // test if remove works
+ ok(doc.querySelector("[data-id='id4']"), "id4 row exists before removal");
+ table.remove("id4");
+ ok(
+ !doc.querySelector("[data-id='id4']"),
+ "id4 row does not exist after removal through id"
+ );
+
+ ok(doc.querySelector("[data-id='id6']"), "id6 row exists before removal");
+ table.remove({
+ col1: "id6",
+ col2: "value15",
+ col3: "value25",
+ col4: "value37",
+ });
+ ok(
+ !doc.querySelector("[data-id='id6']"),
+ "id6 row does not exist after removal through json"
+ );
+
+ table.push({
+ col1: "id4",
+ col2: "value12",
+ col3: "value26",
+ col4: "value33",
+ });
+ table.push({
+ col1: "id6",
+ col2: "value15",
+ col3: "value25",
+ col4: "value37",
+ });
+
+ // test if selectedIndex getter setter works
+ table.selectedIndex = 2;
+ ok(table.isSelected("id3"), "Correct row selected by selectedIndex setter");
+
+ table.selectedIndex = 4;
+ ok(table.isSelected("id5"), "Correct row selected by selectedIndex setter");
+
+ table.selectRow("id8");
+ is(table.selectedIndex, 7, "Correct value of selectedIndex getter");
+
+ // testing if clear works
+ table.clear();
+ // double because splitters
+ is(table.tbody.children.length, 4 * 2, "4 columns exist even after clear");
+ for (let i = 0; i < 4; i++) {
+ is(
+ table.tbody.children[i * 2].children.length,
+ 1,
+ `Only header in the column ${i} after clear call`
+ );
+ is(
+ table.tbody.children[i * 2].firstChild.value,
+ `Column ${i + 1}`,
+ "Correct column header value"
+ );
+ }
+
+ // testing if setColumns work
+ table.setColumns({
+ col1: "Foobar",
+ col2: "Testing",
+ });
+
+ // double because splitters
+ is(
+ table.tbody.children.length,
+ 2 * 2,
+ "2 columns exist after setColumn call"
+ );
+ is(
+ table.tbody.children[0].firstChild.getAttribute("value"),
+ "Foobar",
+ "Correct column header value for first column"
+ );
+ is(
+ table.tbody.children[2].firstChild.getAttribute("value"),
+ "Testing",
+ "Correct column header value for second column"
+ );
+
+ table.setColumns({
+ col1: "Column 1",
+ col2: "Column 2",
+ col3: "Column 3",
+ col4: "Column 4",
+ });
+ // double because splitters
+ is(
+ table.tbody.children.length,
+ 4 * 2,
+ "4 columns exist after second setColumn call"
+ );
+
+ populateTable(doc, table);
+
+ // testing if update works
+ is(
+ doc.querySelectorAll("[data-id='id4']")[1].value,
+ "value12",
+ "Correct value before update"
+ );
+ table.update({
+ col1: "id4",
+ col2: "UPDATED",
+ col3: "value26",
+ col4: "value33",
+ });
+ is(
+ doc.querySelectorAll("[data-id='id4']")[1].value,
+ "UPDATED",
+ "Correct value after update"
+ );
+
+ // testing if sorting works by calling it once on an already sorted column
+ // should sort descending
+ table.sortBy("col1");
+ for (let i = 1; i < 10; i++) {
+ is(
+ table.tbody.firstChild.children[i].value,
+ `id${10 - i}`,
+ `Correct value in row ${i} after descending sort by on col1`
+ );
+ }
+ // Calling it on an unsorted column should sort by it in ascending manner
+ table.sortBy("col2");
+ let cell = table.tbody.children[2].firstChild.children[2];
+ checkAscendingOrder(cell);
+
+ // Calling it again should sort by it in descending manner
+ table.sortBy("col2");
+ cell = table.tbody.children[2].lastChild.previousSibling;
+ checkDescendingOrder(cell);
+
+ // Calling it again should sort by it in ascending manner
+ table.sortBy("col2");
+ cell = table.tbody.children[2].firstChild.children[2];
+ checkAscendingOrder(cell);
+
+ table.clear();
+ populateTable(doc, table);
+
+ // testing if sorting works should sort by ascending manner
+ table.sortBy("col4");
+ cell = table.tbody.children[6].children[1];
+ is(cell.textContent, "domnode", "DOMNode sorted correctly");
+ checkAscendingOrder(cell.nextSibling);
+
+ // Calling it again should sort it in descending order
+ table.sortBy("col4");
+ cell = table.tbody.children[6].children[9];
+ is(cell.textContent, "domnode", "DOMNode sorted correctly");
+ checkDescendingOrder(cell.previousSibling);
+}
+
+function checkAscendingOrder(cell) {
+ while (cell) {
+ const currentCell = cell.value || cell.textContent;
+ const prevCell =
+ cell.previousSibling.value || cell.previousSibling.textContent;
+ ok(currentCell >= prevCell, "Sorting is in ascending order");
+ cell = cell.nextSibling;
+ }
+}
+
+function checkDescendingOrder(cell) {
+ while (cell != cell.parentNode.firstChild) {
+ const currentCell = cell.value || cell.textContent;
+ const nextCell = cell.nextSibling.value || cell.nextSibling.textContent;
+ ok(currentCell >= nextCell, "Sorting is in descending order");
+ cell = cell.previousSibling;
+ }
+}
diff --git a/devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js b/devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js
new file mode 100644
index 0000000000..293ac71e7f
--- /dev/null
+++ b/devtools/client/shared/test/browser_tableWidget_keyboard_interaction.js
@@ -0,0 +1,202 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that keyboard interaction works fine with the table widget
+
+"use strict";
+
+const TEST_URI = CHROME_URL_ROOT + "doc_tableWidget_keyboard_interaction.xhtml";
+const TEST_OPT = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+const {
+ TableWidget,
+} = require("resource://devtools/client/shared/widgets/TableWidget.js");
+
+var doc, table;
+
+function test() {
+ waitForExplicitFinish();
+ const win = Services.ww.openWindow(null, TEST_URI, "_blank", TEST_OPT, null);
+
+ win.addEventListener(
+ "load",
+ function () {
+ waitForFocus(function () {
+ doc = win.document;
+ table = new TableWidget(doc.querySelector("box"), {
+ initialColumns: {
+ col1: "Column 1",
+ col2: "Column 2",
+ col3: "Column 3",
+ col4: "Column 4",
+ },
+ uniqueId: "col1",
+ emptyText: "dummy-text",
+ highlightUpdated: true,
+ removableColumns: true,
+ l10n: {
+ setAttributes() {},
+ },
+ });
+ startTests();
+ });
+ },
+ { once: true }
+ );
+}
+
+function endTests() {
+ table.destroy();
+ doc.defaultView.close();
+ doc = table = null;
+ finish();
+}
+
+var startTests = async function () {
+ populateTable();
+ await testKeyboardInteraction();
+ endTests();
+};
+
+function populateTable() {
+ table.push({
+ col1: "id1",
+ col2: "value10",
+ col3: "value20",
+ col4: "value30",
+ });
+ table.push({
+ col1: "id2",
+ col2: "value14",
+ col3: "value29",
+ col4: "value32",
+ });
+ table.push({
+ col1: "id3",
+ col2: "value17",
+ col3: "value21",
+ col4: "value31",
+ extraData: "foobar",
+ extraData2: 42,
+ });
+ table.push({
+ col1: "id4",
+ col2: "value12",
+ col3: "value26",
+ col4: "value33",
+ });
+ table.push({
+ col1: "id5",
+ col2: "value19",
+ col3: "value26",
+ col4: "value37",
+ });
+ table.push({
+ col1: "id6",
+ col2: "value15",
+ col3: "value25",
+ col4: "value37",
+ });
+ table.push({
+ col1: "id7",
+ col2: "value18",
+ col3: "value21",
+ col4: "value36",
+ somethingExtra: "Hello World!",
+ });
+ table.push({
+ col1: "id8",
+ col2: "value11",
+ col3: "value27",
+ col4: "value34",
+ });
+ table.push({
+ col1: "id9",
+ col2: "value11",
+ col3: "value23",
+ col4: "value38",
+ });
+}
+
+// Sends a click event on the passed DOM node in an async manner
+function click(node, button = 0) {
+ if (button == 0) {
+ executeSoon(() =>
+ EventUtils.synthesizeMouseAtCenter(node, {}, doc.defaultView)
+ );
+ } else {
+ executeSoon(() =>
+ EventUtils.synthesizeMouseAtCenter(
+ node,
+ {
+ button,
+ type: "contextmenu",
+ },
+ doc.defaultView
+ )
+ );
+ }
+}
+
+function getNodeByValue(value) {
+ return table.tbody.querySelector("[value=" + value + "]");
+}
+
+/**
+ * Tests if pressing navigation keys on the table items does the expected
+ * behavior.
+ */
+var testKeyboardInteraction = async function () {
+ info("Testing keyboard interaction with the table");
+ info("clicking on the row containing id2");
+ const node = getNodeByValue("id2");
+ const event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+ click(node);
+ await event;
+
+ await testRow("id3", "DOWN", "next row");
+ await testRow("id4", "DOWN", "next row");
+ await testRow("id3", "UP", "previous row");
+ await testRow("id4", "DOWN", "next row");
+ await testRow("id5", "DOWN", "next row");
+ await testRow("id6", "DOWN", "next row");
+ await testRow("id5", "UP", "previous row");
+ await testRow("id4", "UP", "previous row");
+ await testRow("id3", "UP", "previous row");
+
+ // selecting last item node to test edge navigation cycling case
+ table.selectedRow = "id9";
+
+ // pressing down on last row should move to first row.
+ await testRow("id1", "DOWN", "first row");
+
+ // pressing up now should move to last row.
+ await testRow("id9", "UP", "last row");
+};
+
+async function testRow(id, key, destination) {
+ const node = getNodeByValue(id);
+ // node should not have selected class
+ ok(
+ !node.classList.contains("theme-selected"),
+ "Row should not have selected class"
+ );
+ info(`Pressing ${key} to select ${destination}`);
+
+ const event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+ EventUtils.sendKey(key, doc.defaultView);
+
+ const uniqueId = await event;
+ is(id, uniqueId, `Correct row was selected after pressing ${key}`);
+
+ ok(node.classList.contains("theme-selected"), "row has selected class");
+
+ const nodes = doc.querySelectorAll(".theme-selected");
+ for (let i = 0; i < nodes.length; i++) {
+ is(
+ nodes[i].getAttribute("data-id"),
+ id,
+ "Correct cell selected in all columns"
+ );
+ }
+}
diff --git a/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js b/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
new file mode 100644
index 0000000000..91b93ac6d5
--- /dev/null
+++ b/devtools/client/shared/test/browser_tableWidget_mouse_interaction.js
@@ -0,0 +1,359 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that mosue interaction works fine with the table widget
+
+"use strict";
+
+const TEST_URI = CHROME_URL_ROOT + "doc_tableWidget_mouse_interaction.xhtml";
+const TEST_OPT = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+const {
+ TableWidget,
+} = require("resource://devtools/client/shared/widgets/TableWidget.js");
+
+var doc, table;
+
+function test() {
+ waitForExplicitFinish();
+ const win = Services.ww.openWindow(null, TEST_URI, "_blank", TEST_OPT, null);
+
+ win.addEventListener(
+ "load",
+ function () {
+ waitForFocus(function () {
+ doc = win.document;
+ table = new TableWidget(doc.querySelector("box"), {
+ initialColumns: {
+ col1: "Column 1",
+ col2: "Column 2",
+ col3: "Column 3",
+ col4: "Column 4",
+ },
+ uniqueId: "col1",
+ emptyText: "This is dummy empty text",
+ highlightUpdated: true,
+ removableColumns: true,
+ wrapTextInElements: true,
+ l10n: {
+ setAttributes() {},
+ },
+ });
+ startTests();
+ });
+ },
+ { once: true }
+ );
+}
+
+function endTests() {
+ table.destroy();
+ doc.defaultView.close();
+ doc = table = null;
+ finish();
+}
+
+var startTests = async function () {
+ populateTable();
+ await testMouseInteraction();
+ endTests();
+};
+
+function populateTable() {
+ table.push({
+ col1: "id1",
+ col2: "value10",
+ col3: "value20",
+ col4: "value30",
+ });
+ table.push({
+ col1: "id2",
+ col2: "value14",
+ col3: "value29",
+ col4: "value32",
+ });
+ table.push({
+ col1: "id3",
+ col2: "value17",
+ col3: "value21",
+ col4: "value31",
+ extraData: "foobar",
+ extraData2: 42,
+ });
+ table.push({
+ col1: "id4",
+ col2: "value12",
+ col3: "value26",
+ col4: "value33",
+ });
+ table.push({
+ col1: "id5",
+ col2: "value19",
+ col3: "value26",
+ col4: "value37",
+ });
+ table.push({
+ col1: "id6",
+ col2: "value15",
+ col3: "value25",
+ col4: "value37",
+ });
+ table.push({
+ col1: "id7",
+ col2: "value18",
+ col3: "value21",
+ col4: "value36",
+ somethingExtra: "Hello World!",
+ });
+ table.push({
+ col1: "id8",
+ col2: "value11",
+ col3: "value27",
+ col4: "value34",
+ });
+ table.push({
+ col1: "id9",
+ col2: "value11",
+ col3: "value23",
+ col4: "value38",
+ });
+}
+
+// Sends a click event on the passed DOM node in an async manner
+function click(node, button = 0) {
+ if (button == 0) {
+ executeSoon(() =>
+ EventUtils.synthesizeMouseAtCenter(node, {}, doc.defaultView)
+ );
+ } else {
+ executeSoon(() =>
+ EventUtils.synthesizeMouseAtCenter(
+ node,
+ {
+ button,
+ type: "contextmenu",
+ },
+ doc.defaultView
+ )
+ );
+ }
+}
+
+async function showCol(id) {
+ const onPopupHidden = once(table.menupopup, "popuphidden");
+ const event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
+ const menuItem = table.menupopup.querySelector(`[data-id='${id}']`);
+ const column = table.tbody.querySelector(`#${id}`);
+
+ info(`Showing ${id}`);
+ ok(BrowserTestUtils.isHidden(column), "Column is hidden before showing it");
+
+ table.menupopup.activateItem(menuItem);
+ const toShow = await event;
+ await onPopupHidden;
+
+ is(toShow, id, `#${id} was selected to be shown`);
+ ok(
+ BrowserTestUtils.isVisible(column),
+ "Column is not hidden after showing it"
+ );
+}
+
+async function hideCol(id) {
+ const onPopupHidden = once(table.menupopup, "popuphidden");
+ const event = table.once(TableWidget.EVENTS.HEADER_CONTEXT_MENU);
+ const menuItem = table.menupopup.querySelector(`[data-id='${id}']`);
+ const column = table.tbody.querySelector(`#${id}`);
+
+ info(`selecting to hide #${id}`);
+ ok(
+ BrowserTestUtils.isVisible(column),
+ `Column #${id} is not hidden before hiding it`
+ );
+ table.menupopup.activateItem(menuItem);
+ const toHide = await event;
+ await onPopupHidden;
+ is(toHide, id, `#${id} was selected to be hidden`);
+ ok(
+ BrowserTestUtils.isHidden(column),
+ `Column #${id} is hidden after hiding it`
+ );
+}
+
+/**
+ * Tests if clicking the table items does the expected behavior
+ */
+var testMouseInteraction = async function () {
+ info("Testing mouse interaction with the table");
+ ok(!table.selectedRow, "Nothing should be selected beforehand");
+
+ let event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+ const firstColumnFirstRowCell = table.tbody.querySelector("[data-id=id1]");
+ info("clicking on the first row");
+ ok(
+ !firstColumnFirstRowCell.classList.contains("theme-selected"),
+ "Node should not have selected class before clicking"
+ );
+ click(firstColumnFirstRowCell);
+ let id = await event;
+ ok(
+ firstColumnFirstRowCell.classList.contains("theme-selected"),
+ "Node has selected class after click"
+ );
+ is(id, "id1", "Correct row was selected");
+
+ info("clicking on second row to select it");
+ event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+ const firstColumnSecondRowCell = table.tbody.firstChild.children[2];
+ // node should not have selected class
+ ok(
+ !firstColumnSecondRowCell.classList.contains("theme-selected"),
+ "New node should not have selected class before clicking"
+ );
+ click(firstColumnSecondRowCell);
+ id = await event;
+ ok(
+ firstColumnSecondRowCell.classList.contains("theme-selected"),
+ "New node has selected class after clicking"
+ );
+ is(id, "id2", "Correct table path is emitted for new node");
+ isnot(
+ firstColumnFirstRowCell,
+ firstColumnSecondRowCell,
+ "Old and new node are different"
+ );
+ ok(
+ !firstColumnFirstRowCell.classList.contains("theme-selected"),
+ "Old node should not have selected class after the click on new node"
+ );
+
+ info("clicking on the third row cell content to select third row");
+ event = table.once(TableWidget.EVENTS.ROW_SELECTED);
+ const firstColumnThirdRowCell = table.tbody.firstChild.children[3];
+ const firstColumnThirdRowCellInnerNode =
+ firstColumnThirdRowCell.querySelector("span");
+ // node should not have selected class
+ ok(
+ !firstColumnThirdRowCell.classList.contains("theme-selected"),
+ "New node should not have selected class before clicking"
+ );
+ click(firstColumnThirdRowCellInnerNode);
+ id = await event;
+ ok(
+ firstColumnThirdRowCell.classList.contains("theme-selected"),
+ "New node has selected class after clicking the cell content"
+ );
+ is(id, "id3", "Correct table path is emitted for new node");
+
+ // clicking on table header to sort by it
+ event = table.once(TableWidget.EVENTS.COLUMN_SORTED);
+ let node = table.tbody.children[6].children[0];
+ info("clicking on the 4th coulmn header to sort the table by it");
+ ok(
+ !node.hasAttribute("sorted"),
+ "Node should not have sorted attribute before clicking"
+ );
+ ok(
+ doc.querySelector("[sorted]"),
+ "Although, something else should be sorted on"
+ );
+ isnot(doc.querySelector("[sorted]"), node, "Which is not equal to this node");
+ click(node);
+ id = await event;
+ is(id, "col4", "Correct column was sorted on");
+ ok(
+ node.hasAttribute("sorted"),
+ "Node should now have sorted attribute after clicking"
+ );
+ is(
+ doc.querySelectorAll("[sorted]").length,
+ 1,
+ "Now only one column should be sorted on"
+ );
+ is(doc.querySelector("[sorted]"), node, "Which should be this column");
+
+ // test context menu opening.
+ // hiding second column
+ // event listener for popupshown
+ info("right click on the first column header");
+ node = table.tbody.firstChild.firstChild;
+ let onPopupShown = once(table.menupopup, "popupshown");
+ click(node, 2);
+ await onPopupShown;
+
+ is(
+ table.menupopup.querySelectorAll("menuitem[disabled]").length,
+ 1,
+ "Only 1 menuitem is disabled"
+ );
+ is(
+ table.menupopup.querySelector("menuitem[disabled]"),
+ table.menupopup.querySelector("[data-id='col1']"),
+ "Which is the unique column"
+ );
+
+ // popup should be open now
+ // clicking on second column label
+ await hideCol("col2");
+
+ // hiding third column
+ // event listener for popupshown
+ info("right clicking on the first column header");
+ node = table.tbody.firstChild.firstChild;
+ onPopupShown = once(table.menupopup, "popupshown");
+ click(node, 2);
+ await onPopupShown;
+
+ is(
+ table.menupopup.querySelectorAll("menuitem[disabled]").length,
+ 1,
+ "Only 1 menuitem is disabled"
+ );
+
+ await hideCol("col3");
+
+ // opening again to see if 2 items are disabled now
+ // event listener for popupshown
+ info("right clicking on the first column header");
+ node = table.tbody.firstChild.firstChild;
+ onPopupShown = once(table.menupopup, "popupshown");
+ click(node, 2);
+ await onPopupShown;
+
+ is(
+ table.menupopup.querySelectorAll("menuitem[disabled]").length,
+ 2,
+ "2 menuitems are disabled now as only 2 columns remain visible"
+ );
+ is(
+ table.menupopup.querySelectorAll("menuitem[disabled]")[0],
+ table.menupopup.querySelector("[data-id='col1']"),
+ "First is the unique column"
+ );
+ is(
+ table.menupopup.querySelectorAll("menuitem[disabled]")[1],
+ table.menupopup.querySelector("[data-id='col4']"),
+ "Second is the last column"
+ );
+
+ // showing back 2nd column
+ // popup should be open now
+ // clicking on second column label
+ await showCol("col2");
+
+ // showing back 3rd column
+ // event listener for popupshown
+ info("right clicking on the first column header");
+ node = table.tbody.firstChild.firstChild;
+ onPopupShown = once(table.menupopup, "popupshown");
+ click(node, 2);
+ await onPopupShown;
+
+ // popup should be open now
+ // clicking on second column label
+ await showCol("col3");
+
+ // reset table state
+ table.clearSelection();
+ table.sortBy("col1");
+};
diff --git a/devtools/client/shared/test/browser_telemetry_button_eyedropper.js b/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
new file mode 100644
index 0000000000..88d019de91
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_button_eyedropper.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_button_eyedropper.js</p><div>test</div>";
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ const tab = gBrowser.selectedTab;
+ const toolbox = await gDevTools.showToolboxForTab(tab, {
+ toolId: "inspector",
+ });
+
+ info("testing the eyedropper button");
+ await testButton(toolbox);
+
+ await toolbox.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+async function testButton(toolbox, Telemetry) {
+ info("Calling the eyedropper button's callback");
+ // We call the button callback directly because we don't need to test the UI here, we're
+ // only concerned about testing the telemetry probe.
+ await toolbox.getPanel("inspector").showEyeDropper();
+
+ checkResults();
+}
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("devtools.")
+ // here.
+ checkTelemetry("devtools.toolbar.eyedropper.opened", "", 1, "scalar");
+}
diff --git a/devtools/client/shared/test/browser_telemetry_button_responsive.js b/devtools/client/shared/test/browser_telemetry_button_responsive.js
new file mode 100644
index 0000000000..ff4e3837bc
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_button_responsive.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_button_responsive.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+const asyncStorage = require("resource://devtools/shared/async-storage.js");
+
+// Toggling the RDM UI involves several docShell swap operations, which are somewhat slow
+// on debug builds. Usually we are just barely over the limit, so a blanket factor of 2
+// should be enough.
+requestLongerTimeout(2);
+
+Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.responsive.html.displayedDeviceList");
+ asyncStorage.removeItem("devtools.devices.local");
+});
+
+loader.lazyRequireGetter(
+ this,
+ "ResponsiveUIManager",
+ "resource://devtools/client/responsive/manager.js"
+);
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ const tab = gBrowser.selectedTab;
+ const toolbox = await gDevTools.showToolboxForTab(tab, {
+ toolId: "inspector",
+ });
+ info("inspector opened");
+
+ info("testing the responsivedesign button");
+ await testButton(tab, toolbox);
+
+ await toolbox.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+async function testButton(tab, toolbox) {
+ info("Testing command-button-responsive");
+
+ const button = toolbox.doc.querySelector("#command-button-responsive");
+ ok(button, "Captain, we have the button");
+
+ await delayedClicks(tab, button, 4);
+
+ checkResults();
+}
+
+function waitForToggle() {
+ return new Promise(resolve => {
+ const handler = () => {
+ ResponsiveUIManager.off("on", handler);
+ ResponsiveUIManager.off("off", handler);
+ resolve();
+ };
+ ResponsiveUIManager.on("on", handler);
+ ResponsiveUIManager.on("off", handler);
+ });
+}
+
+var delayedClicks = async function (tab, node, clicks) {
+ for (let i = 0; i < clicks; i++) {
+ info("Clicking button " + node.id);
+ const toggled = waitForToggle();
+ node.click();
+ await toggled;
+ // See TOOL_DELAY for why we need setTimeout here
+ await DevToolsUtils.waitForTime(TOOL_DELAY);
+
+ // When opening RDM
+ if (i % 2 == 0) {
+ // wait for RDM to be fully loaded to prevent Promise rejection when closing
+ await waitFor(() => ResponsiveUIManager.isActiveForTab(tab));
+ const rdmUI = ResponsiveUIManager.getResponsiveUIForTab(tab);
+ await waitForRDMLoaded(rdmUI);
+ }
+ }
+};
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_RESPONSIVE_")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_RESPONSIVE_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_RESPONSIVE_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_misc.js b/devtools/client/shared/test/browser_telemetry_misc.js
new file mode 100644
index 0000000000..eeed8c2c41
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_misc.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8,<p>browser_telemetry_misc.js</p>";
+const TOOL_DELAY = 0;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+
+ startTelemetry();
+
+ await openAndCloseToolbox(1, TOOL_DELAY, "inspector");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_")
+ // here.
+ checkTelemetry("DEVTOOLS_TOOLBOX_OPENED_COUNT", "", { 0: 1, 1: 0 }, "array");
+ checkTelemetry(
+ "DEVTOOLS_INSPECTOR_OPENED_COUNT",
+ "",
+ { 0: 1, 1: 0 },
+ "array"
+ );
+ checkTelemetry("DEVTOOLS_RULEVIEW_OPENED_COUNT", "", { 0: 1, 1: 0 }, "array");
+ checkTelemetry(
+ "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+ checkTelemetry(
+ "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+ checkTelemetry(
+ "DEVTOOLS_RULEVIEW_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+ checkTelemetry("DEVTOOLS_TOOLBOX_HOST", "", null, "hasentries");
+}
diff --git a/devtools/client/shared/test/browser_telemetry_sidebar.js b/devtools/client/shared/test/browser_telemetry_sidebar.js
new file mode 100644
index 0000000000..21ef27bc41
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_sidebar.js
@@ -0,0 +1,233 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8,<p>browser_telemetry_sidebar.js</p>";
+const ALL_CHANNELS = Ci.nsITelemetry.DATASET_ALL_CHANNELS;
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+const DATA = [
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "layoutview",
+ newpanel: "animationinspector",
+ },
+ },
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "animationinspector",
+ newpanel: "fontinspector",
+ },
+ },
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "fontinspector",
+ newpanel: "layoutview",
+ },
+ },
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "layoutview",
+ newpanel: "computedview",
+ },
+ },
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "computedview",
+ newpanel: "animationinspector",
+ },
+ },
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "animationinspector",
+ newpanel: "fontinspector",
+ },
+ },
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "fontinspector",
+ newpanel: "layoutview",
+ },
+ },
+ {
+ timestamp: null,
+ category: "devtools.main",
+ method: "sidepanel_changed",
+ object: "inspector",
+ value: null,
+ extra: {
+ oldpanel: "layoutview",
+ newpanel: "computedview",
+ },
+ },
+];
+
+add_task(async function () {
+ // Let's reset the counts.
+ Services.telemetry.clearEvents();
+
+ // Ensure no events have been logged
+ const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
+ ok(!snapshot.parent, "No events have been logged for the main process");
+
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ const tab = gBrowser.selectedTab;
+ const toolbox = await gDevTools.showToolboxForTab(tab, {
+ toolId: "inspector",
+ });
+ info("inspector opened");
+
+ await testSidebar(toolbox);
+ checkResults();
+ checkEventTelemetry();
+
+ await toolbox.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function testSidebar(toolbox) {
+ info("Testing sidebar");
+
+ const inspector = toolbox.getCurrentPanel();
+ let sidebarTools = [
+ "computedview",
+ "layoutview",
+ "fontinspector",
+ "animationinspector",
+ ];
+
+ // Concatenate the array with itself so that we can open each tool twice.
+ sidebarTools = [...sidebarTools, ...sidebarTools];
+
+ return new Promise(resolve => {
+ // See TOOL_DELAY for why we need setTimeout here
+ setTimeout(function selectSidebarTab() {
+ const tool = sidebarTools.pop();
+ if (tool) {
+ inspector.sidebar.select(tool);
+ setTimeout(function () {
+ setTimeout(selectSidebarTab, TOOL_DELAY);
+ }, TOOL_DELAY);
+ } else {
+ resolve();
+ }
+ }, TOOL_DELAY);
+ });
+}
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_INSPECTOR_OPENED_COUNT",
+ "",
+ { 0: 1, 1: 0 },
+ "array"
+ );
+ checkTelemetry("DEVTOOLS_RULEVIEW_OPENED_COUNT", "", { 0: 1, 1: 0 }, "array");
+ checkTelemetry(
+ "DEVTOOLS_COMPUTEDVIEW_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_LAYOUTVIEW_OPENED_COUNT",
+ "",
+ { 0: 3, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_FONTINSPECTOR_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_COMPUTEDVIEW_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+ checkTelemetry(
+ "DEVTOOLS_LAYOUTVIEW_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+ checkTelemetry(
+ "DEVTOOLS_FONTINSPECTOR_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
+
+function checkEventTelemetry() {
+ const snapshot = Services.telemetry.snapshotEvents(ALL_CHANNELS, true);
+ const events = snapshot.parent.filter(
+ event =>
+ event[1] === "devtools.main" &&
+ event[2] === "sidepanel_changed" &&
+ event[3] === "inspector" &&
+ event[4] === null
+ );
+
+ for (const i in DATA) {
+ const [timestamp, category, method, object, value, extra] = events[i];
+ const expected = DATA[i];
+
+ // ignore timestamp
+ Assert.greater(timestamp, 0, "timestamp is greater than 0");
+ is(category, expected.category, "category is correct");
+ is(method, expected.method, "method is correct");
+ is(object, expected.object, "object is correct");
+ is(value, expected.value, "value is correct");
+
+ is(extra.oldpanel, expected.extra.oldpanel, "oldpanel is correct");
+ is(extra.newpanel, expected.extra.newpanel, "newpanel is correct");
+ }
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolbox.js b/devtools/client/shared/test/browser_telemetry_toolbox.js
new file mode 100644
index 0000000000..1150a7f280
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolbox.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," + "<p>browser_telemetry_toolbox.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(3, TOOL_DELAY, "inspector");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_TOOLBOX_")
+ // here.
+ checkTelemetry("DEVTOOLS_TOOLBOX_OPENED_COUNT", "", { 0: 3, 1: 0 }, "array");
+ checkTelemetry(
+ "DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+ checkTelemetry("DEVTOOLS_TOOLBOX_HOST", "", null, "hasentries");
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js
new file mode 100644
index 0000000000..0e756aaf9c
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_inspector.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_inspector.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "inspector");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_INSPECTOR_")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_INSPECTOR_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_INSPECTOR_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js
new file mode 100644
index 0000000000..976af4a00a
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsdebugger.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_jsdebugger.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "jsdebugger");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_JSDEBUGGER_")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_JSDEBUGGER_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_JSDEBUGGER_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js
new file mode 100644
index 0000000000..3cb3da0f26
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_jsprofiler.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_jsprofiler.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "performance");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_JSPROFILER")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_JSPROFILER_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_JSPROFILER_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js
new file mode 100644
index 0000000000..26a1fb89ba
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_netmonitor.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_netmonitor.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "netmonitor");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_NETMONITOR_")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_NETMONITOR_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_NETMONITOR_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js
new file mode 100644
index 0000000000..08ba39d0cf
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_options.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_options.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "options");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_OPTIONS_")
+ // here.
+ checkTelemetry("DEVTOOLS_OPTIONS_OPENED_COUNT", "", { 0: 2, 1: 0 }, "array");
+ checkTelemetry(
+ "DEVTOOLS_OPTIONS_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js
new file mode 100644
index 0000000000..788e38643c
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_storage.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_storage.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 1000;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "storage");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_STORAGE_")
+ // here.
+ checkTelemetry("DEVTOOLS_STORAGE_OPENED_COUNT", "", { 0: 2, 1: 0 }, "array");
+ checkTelemetry(
+ "DEVTOOLS_STORAGE_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js
new file mode 100644
index 0000000000..0a1b6ed5f4
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_styleeditor.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_styleeditor.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "styleeditor");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_STYLEEDITOR_")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_STYLEEDITOR_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_STYLEEDITOR_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js b/devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js
new file mode 100644
index 0000000000..d5f52ed91c
--- /dev/null
+++ b/devtools/client/shared/test/browser_telemetry_toolboxtabs_webconsole.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8," +
+ "<p>browser_telemetry_toolboxtabs_styleeditor_webconsole.js</p>";
+
+// Because we need to gather stats for the period of time that a tool has been
+// opened we make use of setTimeout() to create tool active times.
+const TOOL_DELAY = 200;
+
+add_task(async function () {
+ await addTab(TEST_URI);
+ startTelemetry();
+
+ await openAndCloseToolbox(2, TOOL_DELAY, "webconsole");
+ checkResults();
+
+ gBrowser.removeCurrentTab();
+});
+
+function checkResults() {
+ // For help generating these tests use generateTelemetryTests("DEVTOOLS_WEBCONSOLE_")
+ // here.
+ checkTelemetry(
+ "DEVTOOLS_WEBCONSOLE_OPENED_COUNT",
+ "",
+ { 0: 2, 1: 0 },
+ "array"
+ );
+ checkTelemetry(
+ "DEVTOOLS_WEBCONSOLE_TIME_ACTIVE_SECONDS",
+ "",
+ null,
+ "hasentries"
+ );
+}
diff --git a/devtools/client/shared/test/browser_theme.js b/devtools/client/shared/test/browser_theme.js
new file mode 100644
index 0000000000..b70bafafa1
--- /dev/null
+++ b/devtools/client/shared/test/browser_theme.js
@@ -0,0 +1,145 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that theme utilities work
+
+const {
+ getColor,
+ getTheme,
+ setTheme,
+} = require("resource://devtools/client/shared/theme.js");
+const { PrefObserver } = require("resource://devtools/client/shared/prefs.js");
+
+add_task(async function () {
+ testGetTheme();
+ testSetTheme();
+ testGetColor();
+ testColorExistence();
+});
+
+function testGetTheme() {
+ const originalTheme = getTheme();
+ ok(originalTheme, "has some theme to start with.");
+ Services.prefs.setCharPref("devtools.theme", "light");
+ is(getTheme(), "light", "getTheme() correctly returns light theme");
+ Services.prefs.setCharPref("devtools.theme", "dark");
+ is(getTheme(), "dark", "getTheme() correctly returns dark theme");
+ Services.prefs.setCharPref("devtools.theme", "unknown");
+ is(getTheme(), "unknown", "getTheme() correctly returns an unknown theme");
+ Services.prefs.setCharPref("devtools.theme", originalTheme);
+}
+
+function testSetTheme() {
+ const originalTheme = getTheme();
+ // Put this in a variable rather than hardcoding it because the default
+ // changes between aurora and nightly
+ const otherTheme = originalTheme == "dark" ? "light" : "dark";
+
+ const prefObserver = new PrefObserver("devtools.");
+ prefObserver.once("devtools.theme", () => {
+ const newValue = Services.prefs.getCharPref("devtools.theme");
+ is(
+ newValue,
+ otherTheme,
+ "A preference event triggered by setTheme comes after the value is set."
+ );
+ });
+ setTheme(otherTheme);
+ is(
+ Services.prefs.getCharPref("devtools.theme"),
+ otherTheme,
+ "setTheme() correctly sets another theme."
+ );
+ setTheme(originalTheme);
+ is(
+ Services.prefs.getCharPref("devtools.theme"),
+ originalTheme,
+ "setTheme() correctly sets the original theme."
+ );
+ setTheme("unknown");
+ is(
+ Services.prefs.getCharPref("devtools.theme"),
+ "unknown",
+ "setTheme() correctly sets an unknown theme."
+ );
+ Services.prefs.setCharPref("devtools.theme", originalTheme);
+
+ prefObserver.destroy();
+}
+
+function testGetColor() {
+ const BLUE_DARK = "#75bfff";
+ const BLUE_LIGHT = "#0074e8";
+ const originalTheme = getTheme();
+
+ setTheme("dark");
+ is(
+ getColor("highlight-blue"),
+ BLUE_DARK,
+ "correctly gets color for enabled theme."
+ );
+ setTheme("light");
+ is(
+ getColor("highlight-blue"),
+ BLUE_LIGHT,
+ "correctly gets color for enabled theme."
+ );
+ setTheme("metal");
+ is(
+ getColor("highlight-blue"),
+ BLUE_LIGHT,
+ "correctly uses light for default theme if enabled theme not found"
+ );
+
+ is(
+ getColor("highlight-blue", "dark"),
+ BLUE_DARK,
+ "if provided and found, uses the provided theme."
+ );
+ is(
+ getColor("highlight-blue", "metal"),
+ BLUE_LIGHT,
+ "if provided and not found, defaults to light theme."
+ );
+ is(
+ getColor("somecomponents"),
+ null,
+ "if a type cannot be found, should return null."
+ );
+
+ setTheme(originalTheme);
+}
+
+function testColorExistence() {
+ const vars = [
+ "body-background",
+ "sidebar-background",
+ "contrast-background",
+ "tab-toolbar-background",
+ "toolbar-background",
+ "selection-background",
+ "selection-color",
+ "selection-background-hover",
+ "splitter-color",
+ "comment",
+ "body-color",
+ "text-color-alt",
+ "text-color-inactive",
+ "text-color-strong",
+ "highlight-green",
+ "highlight-blue",
+ "highlight-bluegrey",
+ "highlight-purple",
+ "highlight-lightorange",
+ "highlight-orange",
+ "highlight-red",
+ "highlight-pink",
+ ];
+
+ for (const type of vars) {
+ ok(getColor(type, "light"), `${type} is a valid color in light theme`);
+ ok(getColor(type, "dark"), `${type} is a valid color in dark theme`);
+ }
+}
diff --git a/devtools/client/shared/test/browser_theme_switching.js b/devtools/client/shared/test/browser_theme_switching.js
new file mode 100644
index 0000000000..642938e9af
--- /dev/null
+++ b/devtools/client/shared/test/browser_theme_switching.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function () {
+ await pushPref("devtools.theme", "light");
+
+ // For some reason, mochitest spawn a very special default tab,
+ // whose WindowGlobal is still the initial about:blank document.
+ // This seems to be specific to mochitest, this doesn't reproduce
+ // in regular firefox run. Even having about:blank as home page,
+ // force loading another final about:blank document (which isn't the initial one)
+ //
+ // To workaround this, force opening a dedicated test tab
+ const tab = await addTab("data:text/html;charset=utf-8,Test page");
+
+ const toolbox = await gDevTools.showToolboxForTab(tab);
+ const doc = toolbox.doc;
+ const root = doc.documentElement;
+
+ const platform = root.getAttribute("platform");
+ const expectedPlatform = getPlatform();
+ is(platform, expectedPlatform, ":root[platform] is correct");
+
+ const theme = Services.prefs.getCharPref("devtools.theme");
+ const className = "theme-" + theme;
+ ok(
+ root.classList.contains(className),
+ ":root has " + className + " class (current theme)"
+ );
+
+ const sheetsInDOM = Array.from(
+ doc.querySelectorAll("link[rel='stylesheet']"),
+ l => l.href
+ );
+
+ const sheetsFromTheme = gDevTools.getThemeDefinition(theme).stylesheets;
+ info("Checking for existence of " + sheetsInDOM.length + " sheets");
+ for (const themeSheet of sheetsFromTheme) {
+ ok(
+ sheetsInDOM.some(s => s.includes(themeSheet)),
+ "There is a stylesheet for " + themeSheet
+ );
+ }
+
+ await toolbox.destroy();
+});
+
+function getPlatform() {
+ const { OS } = Services.appinfo;
+ if (OS == "WINNT") {
+ return "win";
+ } else if (OS == "Darwin") {
+ return "mac";
+ }
+ return "linux";
+}
diff --git a/devtools/client/shared/test/browser_treeWidget_basic.js b/devtools/client/shared/test/browser_treeWidget_basic.js
new file mode 100644
index 0000000000..48702b9b8d
--- /dev/null
+++ b/devtools/client/shared/test/browser_treeWidget_basic.js
@@ -0,0 +1,391 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that the tree widget api works fine
+"use strict";
+
+const TEST_URI =
+ "data:text/html;charset=utf-8,<head>" +
+ "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" +
+ "ets.css'></head><body><div></div><span></span></body>";
+const {
+ TreeWidget,
+} = require("resource://devtools/client/shared/widgets/TreeWidget.js");
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.allow_unsafe_parent_loads", true]],
+ });
+ await addTab("about:blank");
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ const tree = new TreeWidget(doc.querySelector("div"), {
+ defaultType: "store",
+ });
+
+ populateTree(tree, doc);
+ testTreeItemInsertedCorrectly(tree, doc);
+ testAPI(tree, doc);
+ populateUnsortedTree(tree, doc);
+ testUnsortedTreeItemInsertedCorrectly(tree, doc);
+
+ tree.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function populateTree(tree, doc) {
+ tree.add([
+ {
+ id: "level1",
+ label: "Level 1",
+ },
+ {
+ id: "level2-1",
+ label: "Level 2",
+ },
+ {
+ id: "level3-1",
+ label: "Level 3 - Child 1",
+ type: "dir",
+ },
+ ]);
+ tree.add([
+ "level1",
+ "level2-1",
+ {
+ id: "level3-2",
+ label: "Level 3 - Child 2",
+ },
+ ]);
+ tree.add([
+ "level1",
+ "level2-1",
+ {
+ id: "level3-3",
+ label: "Level 3 - Child 3",
+ },
+ ]);
+ tree.add([
+ "level1",
+ {
+ id: "level2-2",
+ label: "Level 2.1",
+ },
+ {
+ id: "level3-1",
+ label: "Level 3.1",
+ },
+ ]);
+ tree.add([
+ {
+ id: "level1",
+ label: "Level 1",
+ },
+ {
+ id: "level2",
+ label: "Level 2",
+ },
+ {
+ id: "level3",
+ label: "Level 3",
+ type: "js",
+ },
+ ]);
+ tree.add(["level1.1", "level2", { id: "level3", type: "url" }]);
+}
+
+/**
+ * Test if the nodes are inserted correctly in the tree.
+ */
+function testTreeItemInsertedCorrectly(tree, doc) {
+ is(
+ tree.root.children.children.length,
+ 2,
+ "Number of top level elements match"
+ );
+ is(
+ tree.root.children.firstChild.lastChild.children.length,
+ 3,
+ "Number of first second level elements match"
+ );
+ is(
+ tree.root.children.lastChild.lastChild.children.length,
+ 1,
+ "Number of second second level elements match"
+ );
+
+ ok(tree.root.items.has("level1"), "Level1 top level element exists");
+ is(
+ tree.root.children.firstChild.dataset.id,
+ JSON.stringify(["level1"]),
+ "Data id of first top level element matches"
+ );
+ is(
+ tree.root.children.firstChild.firstChild.textContent,
+ "Level 1",
+ "Text content of first top level element matches"
+ );
+
+ ok(tree.root.items.has("level1.1"), "Level1.1 top level element exists");
+ is(
+ tree.root.children.firstChild.nextSibling.dataset.id,
+ JSON.stringify(["level1.1"]),
+ "Data id of second top level element matches"
+ );
+ is(
+ tree.root.children.firstChild.nextSibling.firstChild.textContent,
+ "level1.1",
+ "Text content of second top level element matches"
+ );
+
+ // Adding a new non text item in the tree.
+ const node = doc.createElement("div");
+ node.textContent = "Foo Bar";
+ node.className = "foo bar";
+ tree.add([
+ {
+ id: "level1.2",
+ node,
+ attachment: {
+ foo: "bar",
+ },
+ },
+ ]);
+
+ is(
+ tree.root.children.children.length,
+ 3,
+ "Number of top level elements match after update"
+ );
+ ok(tree.root.items.has("level1.2"), "New level node got added");
+ ok(
+ tree.attachments.has(JSON.stringify(["level1.2"])),
+ "Attachment is present for newly added node"
+ );
+ // The item should be added before level1 and level 1.1 as lexical sorting
+ is(
+ tree.root.children.firstChild.dataset.id,
+ JSON.stringify(["level1.2"]),
+ "Data id of last top level element matches"
+ );
+ is(
+ tree.root.children.firstChild.firstChild.firstChild,
+ node,
+ "Newly added node is inserted at the right location"
+ );
+}
+
+/**
+ * Populate the unsorted tree.
+ */
+function populateUnsortedTree(tree, doc) {
+ tree.sorted = false;
+
+ tree.add([{ id: "g-1", label: "g-1" }]);
+ tree.add(["g-1", { id: "d-2", label: "d-2.1" }]);
+ tree.add(["g-1", { id: "b-2", label: "b-2.2" }]);
+ tree.add(["g-1", { id: "a-2", label: "a-2.3" }]);
+}
+
+/**
+ * Test if the nodes are inserted correctly in the unsorted tree.
+ */
+function testUnsortedTreeItemInsertedCorrectly(tree, doc) {
+ ok(tree.root.items.has("g-1"), "g-1 top level element exists");
+
+ is(
+ tree.root.children.firstChild.lastChild.children.length,
+ 3,
+ "Number of children for g-1 matches"
+ );
+ is(
+ tree.root.children.firstChild.dataset.id,
+ JSON.stringify(["g-1"]),
+ "Data id of g-1 matches"
+ );
+ is(
+ tree.root.children.firstChild.firstChild.textContent,
+ "g-1",
+ "Text content of g-1 matches"
+ );
+ is(
+ tree.root.children.firstChild.lastChild.firstChild.dataset.id,
+ JSON.stringify(["g-1", "d-2"]),
+ "Data id of d-2 matches"
+ );
+ is(
+ tree.root.children.firstChild.lastChild.firstChild.textContent,
+ "d-2.1",
+ "Text content of d-2 matches"
+ );
+ is(
+ tree.root.children.firstChild.lastChild.firstChild.nextSibling.textContent,
+ "b-2.2",
+ "Text content of b-2 matches"
+ );
+ is(
+ tree.root.children.firstChild.lastChild.lastChild.textContent,
+ "a-2.3",
+ "Text content of a-2 matches"
+ );
+}
+
+/**
+ * Tests if the API exposed by TreeWidget works properly
+ */
+function testAPI(tree, doc) {
+ info("Testing TreeWidget API");
+ // Check if selectItem and selectedItem setter works as expected
+ // Nothing should be selected beforehand
+ ok(!doc.querySelector(".theme-selected"), "Nothing is selected");
+ tree.selectItem(["level1"]);
+ const node = doc.querySelector(".theme-selected");
+ ok(!!node, "Something got selected");
+ is(
+ node.parentNode.dataset.id,
+ JSON.stringify(["level1"]),
+ "Correct node selected"
+ );
+
+ tree.selectItem(["level1", "level2"]);
+ const node2 = doc.querySelector(".theme-selected");
+ ok(!!node2, "Something is still selected");
+ isnot(node, node2, "Newly selected node is different from previous");
+ is(
+ node2.parentNode.dataset.id,
+ JSON.stringify(["level1", "level2"]),
+ "Correct node selected"
+ );
+
+ // test if selectedItem getter works
+ is(tree.selectedItem.length, 2, "Correct length of selected item");
+ is(tree.selectedItem[0], "level1", "Correct selected item");
+ is(tree.selectedItem[1], "level2", "Correct selected item");
+
+ // test if isSelected works
+ ok(tree.isSelected(["level1", "level2"]), "isSelected works");
+
+ tree.selectedItem = ["level1"];
+ const node3 = doc.querySelector(".theme-selected");
+ ok(!!node3, "Something is still selected");
+ isnot(node2, node3, "Newly selected node is different from previous");
+ is(node3, node, "First and third selected nodes should be same");
+ is(
+ node3.parentNode.dataset.id,
+ JSON.stringify(["level1"]),
+ "Correct node selected"
+ );
+
+ // test if selectedItem getter works
+ is(tree.selectedItem.length, 1, "Correct length of selected item");
+ is(tree.selectedItem[0], "level1", "Correct selected item");
+
+ // test if clear selection works
+ tree.clearSelection();
+ ok(
+ !doc.querySelector(".theme-selected"),
+ "Nothing selected after clear selection call"
+ );
+
+ // test if collapseAll/expandAll work
+ ok(!!doc.querySelectorAll("[expanded]").length, "Some nodes are expanded");
+ tree.collapseAll();
+ is(
+ doc.querySelectorAll("[expanded]").length,
+ 0,
+ "Nothing is expanded after collapseAll call"
+ );
+ tree.expandAll();
+ is(
+ doc.querySelectorAll("[expanded]").length,
+ 13,
+ "All tree items expanded after expandAll call"
+ );
+
+ // test if selectNextItem and selectPreviousItem work
+ tree.selectedItem = ["level1", "level2"];
+ ok(tree.isSelected(["level1", "level2"]), "Correct item selected");
+ tree.selectNextItem();
+ ok(
+ tree.isSelected(["level1", "level2", "level3"]),
+ "Correct item selected after selectNextItem call"
+ );
+
+ tree.selectNextItem();
+ ok(
+ tree.isSelected(["level1", "level2-1"]),
+ "Correct item selected after second selectNextItem call"
+ );
+
+ tree.selectNextItem();
+ ok(
+ tree.isSelected(["level1", "level2-1", "level3-1"]),
+ "Correct item selected after third selectNextItem call"
+ );
+
+ tree.selectPreviousItem();
+ ok(
+ tree.isSelected(["level1", "level2-1"]),
+ "Correct item selected after selectPreviousItem call"
+ );
+
+ tree.selectPreviousItem();
+ ok(
+ tree.isSelected(["level1", "level2", "level3"]),
+ "Correct item selected after second selectPreviousItem call"
+ );
+
+ // test if remove works
+ ok(
+ doc.querySelector(
+ "[data-id='" + JSON.stringify(["level1", "level2", "level3"]) + "']"
+ ),
+ "level1-level2-level3 item exists before removing"
+ );
+ tree.remove(["level1", "level2", "level3"]);
+ ok(
+ !doc.querySelector(
+ "[data-id='" + JSON.stringify(["level1", "level2", "level3"]) + "']"
+ ),
+ "level1-level2-level3 item does not exist after removing"
+ );
+ const level2item = doc.querySelector(
+ "[data-id='" +
+ JSON.stringify(["level1", "level2"]) +
+ "'] > .tree-widget-item"
+ );
+ ok(
+ level2item.hasAttribute("empty"),
+ "level1-level2 item is marked as empty after removing"
+ );
+
+ tree.add([
+ {
+ id: "level1",
+ label: "Level 1",
+ },
+ {
+ id: "level2",
+ label: "Level 2",
+ },
+ {
+ id: "level3",
+ label: "Level 3",
+ type: "js",
+ },
+ ]);
+
+ // test if clearing the tree works
+ is(
+ doc.querySelectorAll("[level='1']").length,
+ 3,
+ "Correct number of top level items before clearing"
+ );
+ tree.clear();
+ is(
+ doc.querySelectorAll("[level='1']").length,
+ 0,
+ "No top level item after clearing the tree"
+ );
+}
diff --git a/devtools/client/shared/test/browser_treeWidget_keyboard_interaction.js b/devtools/client/shared/test/browser_treeWidget_keyboard_interaction.js
new file mode 100644
index 0000000000..05f1f95b4e
--- /dev/null
+++ b/devtools/client/shared/test/browser_treeWidget_keyboard_interaction.js
@@ -0,0 +1,291 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that keyboard interaction works fine with the tree widget
+
+const TEST_URI =
+ "data:text/html;charset=utf-8,<head>" +
+ "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" +
+ "ets.css'></head><body><div></div><span></span></body>";
+const {
+ TreeWidget,
+} = require("resource://devtools/client/shared/widgets/TreeWidget.js");
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.allow_unsafe_parent_loads", true]],
+ });
+
+ await addTab("about:blank");
+ const { host, win, doc } = await createHost("bottom", TEST_URI);
+
+ // Creating a host is not correctly waiting when DevTools run in content frame
+ // See Bug 1571421.
+ await wait(1000);
+
+ const tree = new TreeWidget(doc.querySelector("div"), {
+ defaultType: "store",
+ });
+
+ populateTree(tree, doc);
+
+ await testKeyboardInteraction(tree, win);
+
+ tree.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function populateTree(tree, doc) {
+ tree.add([
+ {
+ id: "level1",
+ label: "Level 1",
+ },
+ {
+ id: "level2-1",
+ label: "Level 2",
+ },
+ {
+ id: "level3-1",
+ label: "Level 3 - Child 1",
+ type: "dir",
+ },
+ ]);
+ tree.add([
+ "level1",
+ "level2-1",
+ { id: "level3-2", label: "Level 3 - Child 2" },
+ ]);
+ tree.add([
+ "level1",
+ "level2-1",
+ { id: "level3-3", label: "Level 3 - Child 3" },
+ ]);
+ tree.add([
+ "level1",
+ {
+ id: "level2-2",
+ label: "Level 2.1",
+ },
+ {
+ id: "level3-1",
+ label: "Level 3.1",
+ },
+ ]);
+ tree.add([
+ {
+ id: "level1",
+ label: "Level 1",
+ },
+ {
+ id: "level2",
+ label: "Level 2",
+ },
+ {
+ id: "level3",
+ label: "Level 3",
+ type: "js",
+ },
+ ]);
+ tree.add(["level1.1", "level2", { id: "level3", type: "url" }]);
+
+ // Adding a new non text item in the tree.
+ const node = doc.createElement("div");
+ node.textContent = "Foo Bar";
+ node.className = "foo bar";
+ tree.add([
+ {
+ id: "level1.2",
+ node,
+ attachment: {
+ foo: "bar",
+ },
+ },
+ ]);
+}
+
+// Sends a click event on the passed DOM node in an async manner
+function click(node) {
+ const win = node.ownerDocument.defaultView;
+ executeSoon(() => EventUtils.synthesizeMouseAtCenter(node, {}, win));
+}
+
+/**
+ * Tests if pressing navigation keys on the tree items does the expected behavior
+ */
+async function testKeyboardInteraction(tree, win) {
+ info("Testing keyboard interaction with the tree");
+ const waitForSelect = () =>
+ new Promise(resolve => {
+ tree.once("select", (d, a) => resolve({ data: d, attachment: a }));
+ });
+
+ info("clicking on first top level item");
+ let node = tree.root.children.firstChild.firstChild;
+
+ // The select event handler will be called before the click event hasn't
+ // fully finished, so wait for both of them.
+ const clicked = once(node, "click");
+ let onTreeSelect = waitForSelect();
+ click(node);
+
+ info("Wait for the click event");
+ await clicked;
+
+ info("Wait for the select event on tree");
+ await onTreeSelect;
+
+ node = tree.root.children.firstChild.nextSibling.firstChild;
+ // node should not have selected class
+ ok(
+ !node.classList.contains("theme-selected"),
+ "Node should not have selected class"
+ );
+ ok(!node.hasAttribute("expanded"), "Node is not expanded");
+
+ info("Pressing down key to select next item");
+ onTreeSelect = waitForSelect();
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+
+ let { data, attachment } = await onTreeSelect;
+ is(data.length, 1, "Correct level item was selected after keydown");
+ is(data[0], "level1", "Correct item was selected after pressing down");
+ ok(!attachment, "null attachment was emitted");
+ ok(node.classList.contains("theme-selected"), "Node has selected class");
+ ok(node.hasAttribute("expanded"), "Node is expanded now");
+
+ info("Pressing down key again to select next item");
+ onTreeSelect = waitForSelect();
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+ ({ data, attachment } = await onTreeSelect);
+ is(
+ data.length,
+ 2,
+ "Correct level item was selected after second down keypress"
+ );
+ is(data[0], "level1", "Correct parent level");
+ is(data[1], "level2", "Correct second level");
+
+ info("Pressing down key again to select next item");
+ onTreeSelect = waitForSelect();
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+ ({ data, attachment } = await onTreeSelect);
+ is(
+ data.length,
+ 3,
+ "Correct level item was selected after third down keypress"
+ );
+ is(data[0], "level1", "Correct parent level");
+ is(data[1], "level2", "Correct second level");
+ is(data[2], "level3", "Correct third level");
+
+ info("Pressing down key again to select next item");
+ onTreeSelect = waitForSelect();
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+ ({ data, attachment } = await onTreeSelect);
+ is(
+ data.length,
+ 2,
+ "Correct level item was selected after fourth down keypress"
+ );
+ is(data[0], "level1", "Correct parent level");
+ is(data[1], "level2-1", "Correct second level");
+
+ const waitForKeydown = () =>
+ new Promise(resolve => {
+ tree.root.children.addEventListener(
+ "keydown",
+ () => {
+ // executeSoon so that other listeners on the same method are executed first
+ executeSoon(() => resolve());
+ },
+ { once: true }
+ );
+ });
+
+ // pressing left to check expand collapse feature.
+ // This does not emit any event, so listening for keypress
+ let onTreeKeydown = waitForKeydown();
+ info("Pressing left key to collapse the item");
+ node = tree._selectedLabel;
+ ok(node.hasAttribute("expanded"), "Item is expanded before left keypress");
+ EventUtils.synthesizeKey("KEY_ArrowLeft", {}, win);
+ await onTreeKeydown;
+
+ ok(
+ !node.hasAttribute("expanded"),
+ "Item is not expanded after left keypress"
+ );
+
+ // pressing left on collapsed item should select the previous item
+
+ info("Pressing left key on collapsed item to select previous");
+ onTreeSelect = waitForSelect();
+ // parent node should have no effect of this keypress
+ node = tree.root.children.firstChild.nextSibling.firstChild;
+ ok(node.hasAttribute("expanded"), "Parent is expanded");
+ EventUtils.synthesizeKey("KEY_ArrowLeft", {}, win);
+ ({ data } = await onTreeSelect);
+ is(
+ data.length,
+ 3,
+ "Correct level item was selected after second left keypress"
+ );
+ is(data[0], "level1", "Correct parent level");
+ is(data[1], "level2", "Correct second level");
+ is(data[2], "level3", "Correct third level");
+ ok(
+ node.hasAttribute("expanded"),
+ "Parent is still expanded after left keypress"
+ );
+
+ // pressing down again
+
+ info("Pressing down key to select next item");
+ onTreeSelect = waitForSelect();
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+ ({ data, attachment } = await onTreeSelect);
+ is(
+ data.length,
+ 2,
+ "Correct level item was selected after fifth down keypress"
+ );
+ is(data[0], "level1", "Correct parent level");
+ is(data[1], "level2-1", "Correct second level");
+
+ // collapsing the item to check expand feature.
+ onTreeKeydown = waitForKeydown();
+ info("Pressing left key to collapse the item");
+ node = tree._selectedLabel;
+ ok(node.hasAttribute("expanded"), "Item is expanded before left keypress");
+ EventUtils.synthesizeKey("KEY_ArrowLeft", {}, win);
+ await onTreeKeydown;
+ ok(!node.hasAttribute("expanded"), "Item is collapsed after left keypress");
+
+ // pressing right should expand this now.
+ onTreeKeydown = waitForKeydown();
+ info("Pressing right key to expend the collapsed item");
+ node = tree._selectedLabel;
+ ok(!node.hasAttribute("expanded"), "Item is collapsed before right keypress");
+ EventUtils.synthesizeKey("KEY_ArrowRight", {}, win);
+ await onTreeKeydown;
+ ok(node.hasAttribute("expanded"), "Item is expanded after right keypress");
+
+ // selecting last item node to test edge navigation case
+
+ tree.selectedItem = ["level1.1", "level2", "level3"];
+ node = tree._selectedLabel;
+ // pressing down again should not change selection
+ onTreeKeydown = waitForKeydown();
+ info("Pressing down key on last item of the tree");
+ EventUtils.synthesizeKey("KEY_ArrowDown", {}, win);
+ await onTreeKeydown;
+
+ ok(
+ tree.isSelected(["level1.1", "level2", "level3"]),
+ "Last item is still selected after pressing down on last item of the tree"
+ );
+}
diff --git a/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js b/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
new file mode 100644
index 0000000000..d031845f6f
--- /dev/null
+++ b/devtools/client/shared/test/browser_treeWidget_mouse_interaction.js
@@ -0,0 +1,185 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that mouse interaction works fine with tree widget
+
+const TEST_URI =
+ "data:text/html;charset=utf-8,<head>" +
+ "<link rel='stylesheet' type='text/css' href='chrome://devtools/skin/widg" +
+ "ets.css'></head><body><div></div><span></span></body>";
+const {
+ TreeWidget,
+} = require("resource://devtools/client/shared/widgets/TreeWidget.js");
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["security.allow_unsafe_parent_loads", true]],
+ });
+
+ await addTab("about:blank");
+ const { host, doc } = await createHost("bottom", TEST_URI);
+
+ // Creating a host is not correctly waiting when DevTools run in content frame
+ // See Bug 1571421.
+ await wait(1000);
+
+ const tree = new TreeWidget(doc.querySelector("div"), {
+ defaultType: "store",
+ });
+
+ populateTree(tree, doc);
+ await testMouseInteraction(tree);
+
+ tree.destroy();
+ host.destroy();
+ gBrowser.removeCurrentTab();
+});
+
+function populateTree(tree, doc) {
+ tree.add([
+ {
+ id: "level1",
+ label: "Level 1",
+ },
+ {
+ id: "level2-1",
+ label: "Level 2",
+ },
+ {
+ id: "level3-1",
+ label: "Level 3 - Child 1",
+ type: "dir",
+ },
+ ]);
+ tree.add([
+ "level1",
+ "level2-1",
+ { id: "level3-2", label: "Level 3 - Child 2" },
+ ]);
+ tree.add([
+ "level1",
+ "level2-1",
+ { id: "level3-3", label: "Level 3 - Child 3" },
+ ]);
+ tree.add([
+ "level1",
+ {
+ id: "level2-2",
+ label: "Level 2.1",
+ },
+ {
+ id: "level3-1",
+ label: "Level 3.1",
+ },
+ ]);
+ tree.add([
+ {
+ id: "level1",
+ label: "Level 1",
+ },
+ {
+ id: "level2",
+ label: "Level 2",
+ },
+ {
+ id: "level3",
+ label: "Level 3",
+ type: "js",
+ },
+ ]);
+ tree.add(["level1.1", "level2", { id: "level3", type: "url" }]);
+
+ // Adding a new non text item in the tree.
+ const node = doc.createElement("div");
+ node.textContent = "Foo Bar";
+ node.className = "foo bar";
+ tree.add([
+ {
+ id: "level1.2",
+ node,
+ attachment: {
+ foo: "bar",
+ },
+ },
+ ]);
+}
+
+// Sends a click event on the passed DOM node in an async manner
+function click(node) {
+ const win = node.ownerDocument.defaultView;
+ executeSoon(() => EventUtils.synthesizeMouseAtCenter(node, {}, win));
+}
+
+/**
+ * Tests if clicking the tree items does the expected behavior
+ */
+async function testMouseInteraction(tree) {
+ info("Testing mouse interaction with the tree");
+ const waitForSelect = () =>
+ new Promise(resolve => {
+ tree.once("select", (d, a) => resolve({ data: d, attachment: a }));
+ });
+
+ ok(!tree.selectedItem, "Nothing should be selected beforehand");
+
+ let onTreeSelect = waitForSelect();
+ const node = tree.root.children.firstChild.firstChild;
+ info("clicking on first top level item");
+ ok(
+ !node.classList.contains("theme-selected"),
+ "Node should not have selected class before clicking"
+ );
+ click(node);
+ let { data, attachment } = await onTreeSelect;
+ ok(
+ node.classList.contains("theme-selected"),
+ "Node has selected class after click"
+ );
+ is(data[0], "level1.2", "Correct tree path is emitted");
+ ok(attachment && attachment.foo, "Correct attachment is emitted");
+ is(attachment.foo, "bar", "Correct attachment value is emitted");
+
+ info("clicking second top level item with children to check if it expands");
+ const node2 = tree.root.children.firstChild.nextSibling.firstChild;
+ // node should not have selected class
+ ok(
+ !node2.classList.contains("theme-selected"),
+ "New node should not have selected class before clicking"
+ );
+ ok(
+ !node2.hasAttribute("expanded"),
+ "New node is not expanded before clicking"
+ );
+ onTreeSelect = waitForSelect();
+ click(node2);
+ ({ data, attachment } = await onTreeSelect);
+ ok(
+ node2.classList.contains("theme-selected"),
+ "New node has selected class after clicking"
+ );
+ is(data[0], "level1", "Correct tree path is emitted for new node");
+ ok(!attachment, "null attachment should be emitted for new node");
+ ok(node2.hasAttribute("expanded"), "New node expanded after click");
+
+ ok(
+ !node.classList.contains("theme-selected"),
+ "Old node should not have selected class after the click on new node"
+ );
+
+ // clicking again should just collapse
+ // this will not emit "select" event
+ const onClick = new Promise(resolve => {
+ node2.addEventListener(
+ "click",
+ () => {
+ executeSoon(() => resolve(null));
+ },
+ { once: true }
+ );
+ });
+ click(node2);
+ await onClick;
+ ok(!node2.hasAttribute("expanded"), "New node collapsed after click again");
+}
diff --git a/devtools/client/shared/test/code_WorkerTargetActor.attachThread-worker.js b/devtools/client/shared/test/code_WorkerTargetActor.attachThread-worker.js
new file mode 100644
index 0000000000..ca12b86a59
--- /dev/null
+++ b/devtools/client/shared/test/code_WorkerTargetActor.attachThread-worker.js
@@ -0,0 +1,18 @@
+"use strict";
+
+function f() {
+ const a = 1;
+ const b = 2;
+ const c = 3;
+
+ return [a, b, c];
+}
+
+self.onmessage = function (event) {
+ if (event.data == "ping") {
+ f();
+ postMessage("pong");
+ }
+};
+
+postMessage("load");
diff --git a/devtools/client/shared/test/code_listworkers-worker1.js b/devtools/client/shared/test/code_listworkers-worker1.js
new file mode 100644
index 0000000000..8cee6809e5
--- /dev/null
+++ b/devtools/client/shared/test/code_listworkers-worker1.js
@@ -0,0 +1,3 @@
+"use strict";
+
+self.onmessage = function () {};
diff --git a/devtools/client/shared/test/code_listworkers-worker2.js b/devtools/client/shared/test/code_listworkers-worker2.js
new file mode 100644
index 0000000000..8cee6809e5
--- /dev/null
+++ b/devtools/client/shared/test/code_listworkers-worker2.js
@@ -0,0 +1,3 @@
+"use strict";
+
+self.onmessage = function () {};
diff --git a/devtools/client/shared/test/doc_WorkerTargetActor.attachThread-tab.html b/devtools/client/shared/test/doc_WorkerTargetActor.attachThread-tab.html
new file mode 100644
index 0000000000..62ab9be7d2
--- /dev/null
+++ b/devtools/client/shared/test/doc_WorkerTargetActor.attachThread-tab.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/devtools/client/shared/test/doc_cubic-bezier-01.html b/devtools/client/shared/test/doc_cubic-bezier-01.html
new file mode 100644
index 0000000000..9667395f96
--- /dev/null
+++ b/devtools/client/shared/test/doc_cubic-bezier-01.html
@@ -0,0 +1 @@
+<div id="cubic-bezier-container" />
diff --git a/devtools/client/shared/test/doc_cubic-bezier-02.html b/devtools/client/shared/test/doc_cubic-bezier-02.html
new file mode 100644
index 0000000000..3a909f18a6
--- /dev/null
+++ b/devtools/client/shared/test/doc_cubic-bezier-02.html
@@ -0,0 +1,3 @@
+<html><body>
+ <div id="cubic-bezier-container"/>
+</body></html>
diff --git a/devtools/client/shared/test/doc_empty-tab-01.html b/devtools/client/shared/test/doc_empty-tab-01.html
new file mode 100644
index 0000000000..28398f7768
--- /dev/null
+++ b/devtools/client/shared/test/doc_empty-tab-01.html
@@ -0,0 +1,14 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Empty test page 1</title>
+ </head>
+
+ <body>
+ </body>
+
+</html>
diff --git a/devtools/client/shared/test/doc_empty-tab-02.html b/devtools/client/shared/test/doc_empty-tab-02.html
new file mode 100644
index 0000000000..5db1508447
--- /dev/null
+++ b/devtools/client/shared/test/doc_empty-tab-02.html
@@ -0,0 +1,14 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Empty test page 2</title>
+ </head>
+
+ <body>
+ </body>
+
+</html>
diff --git a/devtools/client/shared/test/doc_event-listeners-01.html b/devtools/client/shared/test/doc_event-listeners-01.html
new file mode 100644
index 0000000000..5a9c2d53b6
--- /dev/null
+++ b/devtools/client/shared/test/doc_event-listeners-01.html
@@ -0,0 +1,45 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Debugger test page</title>
+ </head>
+
+ <body>
+ <button>Click me!</button>
+ <input type="text" onchange="changeHandler()">
+
+ <script type="text/javascript">
+ "use strict";
+
+ window.addEventListener("load", function() {
+ function initialSetup(event) {
+ // eslint-disable-next-line no-debugger
+ debugger;
+ const button = document.querySelector("button");
+ button.onclick = clickHandler;
+ }
+ function clickHandler(event) {
+ window.foobar = "clickHandler";
+ }
+ function changeHandler(event) {
+ window.foobar = "changeHandler";
+ }
+ function keyupHandler(event) {
+ window.foobar = "keyupHandler";
+ }
+
+ const button = document.querySelector("button");
+ button.onclick = initialSetup;
+
+ const input = document.querySelector("input");
+ input.addEventListener("keyup", keyupHandler, true);
+
+ window.changeHandler = changeHandler;
+ }, {once: true});
+ </script>
+ </body>
+
+</html>
diff --git a/devtools/client/shared/test/doc_event-listeners-03.html b/devtools/client/shared/test/doc_event-listeners-03.html
new file mode 100644
index 0000000000..2660f9f141
--- /dev/null
+++ b/devtools/client/shared/test/doc_event-listeners-03.html
@@ -0,0 +1,65 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Bound event listeners test page</title>
+ </head>
+
+ <body>
+ <button id="initialSetup">initialSetup</button>
+ <button id="clicker">clicker</button>
+ <button id="handleEventClick">handleEventClick</button>
+ <button id="boundHandleEventClick">boundHandleEventClick</button>
+
+ <script type="text/javascript">
+ "use strict";
+
+ window.addEventListener("load", function() {
+ function initialSetup(event) {
+ const button = document.getElementById("initialSetup");
+ button.removeEventListener("click", initialSetup);
+ // eslint-disable-next-line no-debugger
+ debugger;
+ }
+
+ function clicker(event) {
+ window.foobar = "clicker";
+ }
+
+ function HandleEventClick() {
+ const button = document.getElementById("handleEventClick");
+ // Create a long prototype chain to test for weird edge cases.
+ button.addEventListener("click", Object.create(Object.create(this)));
+ }
+
+ HandleEventClick.prototype.handleEvent = function() {
+ window.foobar = "HandleEventClick";
+ };
+
+ function BoundHandleEventClick() {
+ const button = document.getElementById("boundHandleEventClick");
+ this.handleEvent = this.handleEvent.bind(this);
+ button.addEventListener("click", this);
+ }
+
+ BoundHandleEventClick.prototype.handleEvent = function() {
+ window.foobar = "BoundHandleEventClick";
+ };
+
+ const button = document.getElementById("clicker");
+ // Bind more than once to test for weird edge cases.
+ const boundClicker = clicker.bind(this).bind(this).bind(this);
+ button.addEventListener("click", boundClicker);
+
+ new HandleEventClick();
+ new BoundHandleEventClick();
+
+ const initButton = document.getElementById("initialSetup");
+ initButton.addEventListener("click", initialSetup);
+ }, {once: true});
+ </script>
+ </body>
+
+</html>
diff --git a/devtools/client/shared/test/doc_filter-editor-01.html b/devtools/client/shared/test/doc_filter-editor-01.html
new file mode 100644
index 0000000000..4f4ad23116
--- /dev/null
+++ b/devtools/client/shared/test/doc_filter-editor-01.html
@@ -0,0 +1 @@
+<div id="filter-container" />
diff --git a/devtools/client/shared/test/doc_html_tooltip-02.xhtml b/devtools/client/shared/test/doc_html_tooltip-02.xhtml
new file mode 100644
index 0000000000..286dc01fdd
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip-02.xhtml
@@ -0,0 +1,15 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ htmlns="http://www.w3.org/1999/xhtml"
+ title="Tooltip test">
+ <vbox flex="1">
+ <hbox id="box1" flex="1">test1</hbox>
+ <hbox id="box2" flex="1">test2</hbox>
+ <hbox id="box3" flex="1">test3</hbox>
+ <hbox id="box4" flex="1">test4</hbox>
+ <iframe id="frame" width="200"></iframe>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip-03.xhtml b/devtools/client/shared/test/doc_html_tooltip-03.xhtml
new file mode 100644
index 0000000000..4510d6e445
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip-03.xhtml
@@ -0,0 +1,19 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Tooltip test">
+ <vbox flex="1">
+ <hbox id="box1" flex="1">
+ <html:input />
+ </hbox>
+ <hbox id="box2" flex="1">test2</hbox>
+ <hbox id="box3" flex="1">
+ <html:input id="box3-input" />
+ </hbox>
+ <hbox id="box4" flex="1">
+ <html:input id="box4-input" />
+ </hbox>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip-04.xhtml b/devtools/client/shared/test/doc_html_tooltip-04.xhtml
new file mode 100644
index 0000000000..f74b53ccdd
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip-04.xhtml
@@ -0,0 +1,15 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tooltip test">
+ <vbox flex="1">
+ <hbox style="height: 10px">spacer</hbox>
+ <hbox id="box1" style="height: 50px">test1</hbox>
+ <hbox id="box2" style="height: 50px">test2</hbox>
+ <hbox flex="1">MIDDLE</hbox>
+ <hbox id="box3" style="height: 50px">test3</hbox>
+ <hbox id="box4" style="height: 50px">test4</hbox>
+ <hbox style="height: 10px">spacer</hbox>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip-05.xhtml b/devtools/client/shared/test/doc_html_tooltip-05.xhtml
new file mode 100644
index 0000000000..0878522968
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip-05.xhtml
@@ -0,0 +1,12 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tooltip test">
+ <vbox flex="1">
+ <hbox id="box1" style="height: 50px">test1</hbox>
+ <hbox id="box2" style="height: 50px">test2</hbox>
+ <hbox id="box3" style="height: 50px">test3</hbox>
+ <hbox id="box4" style="height: 50px">test4</hbox>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip.xhtml b/devtools/client/shared/test/doc_html_tooltip.xhtml
new file mode 100644
index 0000000000..329164acd9
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip.xhtml
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tooltip test">
+ <vbox flex="1">
+ <hbox id="box1" flex="1">test1</hbox>
+ <hbox id="box2" flex="1">test2</hbox>
+ <hbox id="box3" flex="1">test3</hbox>
+ <hbox id="box4" flex="1">test4</hbox>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip_arrow-01.xhtml b/devtools/client/shared/test/doc_html_tooltip_arrow-01.xhtml
new file mode 100644
index 0000000000..f16ac1d67d
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip_arrow-01.xhtml
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
+<window class="theme-light"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Tooltip test">
+
+<vbox flex="1" style="position: relative">
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 25px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 50px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 75px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; left: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; left: 25px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; left: 50px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; left: 75px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 25px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 50px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 75px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; right: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; right: 25px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; right: 50px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; right: 75px;">
+ </html:div>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip_arrow-02.xhtml b/devtools/client/shared/test/doc_html_tooltip_arrow-02.xhtml
new file mode 100644
index 0000000000..a468177bab
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip_arrow-02.xhtml
@@ -0,0 +1,65 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
+
+<window class="theme-light"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Tooltip test">
+ <vbox flex="1" style="position: relative">
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 0; left: 0; width: 50px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 10px; left: 0; width: 100px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 20px; left: 0; width: 150px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 30px; left: 0; width: 200px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 40px; left: 0; width: 250px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 50px; left: 100px; width: 250px;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 100px; width: 50px; right: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 110px; width: 100px; right: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 120px; width: 150px; right: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 130px; width: 200px; right: 0;">
+ </html:div>
+
+ <html:div class="anchor"
+ style="height: 5px; position: absolute; background: red;
+ top: 140px; width: 250px; right: 0;">
+ </html:div>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip_doorhanger-01.xhtml b/devtools/client/shared/test/doc_html_tooltip_doorhanger-01.xhtml
new file mode 100644
index 0000000000..8bdbf3d2b8
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip_doorhanger-01.xhtml
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
+<window class="theme-light"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Tooltip test">
+
+ <vbox flex="1" style="position: relative">
+ <!-- LTR on left edge -->
+ <html:div
+ id="anchor1"
+ class="anchor"
+ data-hang="right"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 0;">
+ </html:div>
+
+ <!-- RTL but too close to the left edge for the doorhanger to hang left -->
+ <html:div
+ id="anchor2"
+ class="anchor"
+ data-hang="right"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 25px; direction: rtl">
+ </html:div>
+
+ <!-- RTL -->
+ <html:div
+ id="anchor3"
+ class="anchor"
+ data-hang="left"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 250px; direction: rtl">
+ </html:div>
+
+ <!-- LTR -->
+ <html:div
+ id="anchor4"
+ class="anchor"
+ data-hang="right"
+ style="width:80%; height: 10px; position: absolute; background: red;
+ top: 0; left: 50px;">
+ </html:div>
+
+ <!-- LTR on right edge -->
+ <html:div
+ id="anchor5"
+ class="anchor"
+ data-hang="left"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 0;">
+ </html:div>
+
+ <!-- RTL near right edge -->
+ <html:div
+ id="anchor6"
+ class="anchor"
+ data-hang="left"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 25px; direction: rtl">
+ </html:div>
+
+ <!-- LTR near left edge due to wide anchor -->
+ <html:div
+ id="anchor7"
+ class="anchor"
+ data-hang="right"
+ style="width:80%; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 50px;">
+ </html:div>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip_doorhanger-02.xhtml b/devtools/client/shared/test/doc_html_tooltip_doorhanger-02.xhtml
new file mode 100644
index 0000000000..d76fe6775c
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip_doorhanger-02.xhtml
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
+<window class="theme-light"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Tooltip test">
+
+<vbox flex="1" style="position: relative">
+ <!-- Towards the left -->
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 25px;">
+ </html:div>
+
+ <!-- Towards the left with RTL direction -->
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ top: 0; left: 50px; direction: rtl;">
+ </html:div>
+
+ <!-- Towards the right -->
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 25px;">
+ </html:div>
+
+ <!-- Towards the right with RTL direction -->
+ <html:div class="anchor"
+ style="width:10px; height: 10px; position: absolute; background: red;
+ bottom: 0; right: 50px; direction: rtl;">
+ </html:div>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip_hover.xhtml b/devtools/client/shared/test/doc_html_tooltip_hover.xhtml
new file mode 100644
index 0000000000..27417aee8c
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip_hover.xhtml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/variables.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ class="theme-light" title="Tooltip hover test">
+ <vbox id="container" flex="1">
+ <hbox id="box1" flex="1"><label>test1</label></hbox>
+ <hbox id="box2" flex="1"><label>test2</label></hbox>
+ <hbox id="box3" flex="1"><label>test3</label></hbox>
+ <hbox id="box4" flex="1"><label>test4</label></hbox>
+ </vbox>
+</window>
diff --git a/devtools/client/shared/test/doc_html_tooltip_rtl.xhtml b/devtools/client/shared/test/doc_html_tooltip_rtl.xhtml
new file mode 100644
index 0000000000..bd8818057a
--- /dev/null
+++ b/devtools/client/shared/test/doc_html_tooltip_rtl.xhtml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ htmlns="http://www.w3.org/1999/xhtml"
+ title="Tooltip test">
+ <hbox style="padding: 90px 0;" flex="1">
+ <hbox id="box1" flex="1" style="background:red; direction: rtl;">test1</hbox>
+ <hbox id="box2" flex="1" style="background:blue; direction: rtl;">test2</hbox>
+ <hbox id="box3" flex="1" style="background:red; direction: ltr;">test3</hbox>
+ <hbox id="box4" flex="1" style="background:blue; direction: ltr;">test4</hbox>
+ </hbox>
+</window>
diff --git a/devtools/client/shared/test/doc_inplace-editor_autocomplete_offset.xhtml b/devtools/client/shared/test/doc_inplace-editor_autocomplete_offset.xhtml
new file mode 100644
index 0000000000..da0ab8bc3a
--- /dev/null
+++ b/devtools/client/shared/test/doc_inplace-editor_autocomplete_offset.xhtml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/common.css"?>
+<?xml-stylesheet href="chrome://devtools/skin/tooltips.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="Tooltip test">
+</window>
diff --git a/devtools/client/shared/test/doc_layoutHelpers.html b/devtools/client/shared/test/doc_layoutHelpers.html
new file mode 100644
index 0000000000..ba31753b09
--- /dev/null
+++ b/devtools/client/shared/test/doc_layoutHelpers.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<meta charset=utf-8>
+<title> Layout Helpers </title>
+
+<style>
+ html {
+ height: 300%;
+ width: 300%;
+ }
+ div#some {
+ position: absolute;
+ background: black;
+ width: 2px;
+ height: 2px;
+ }
+ div#other {
+ position: absolute;
+ background: red;
+ width: 2px;
+ height: 300px;
+ }
+ iframe {
+ position: absolute;
+ width: 40px;
+ height: 40px;
+ border: 0;
+ }
+</style>
+
+<div id=some></div>
+<div id="other"></div>
diff --git a/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads1.html b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads1.html
new file mode 100644
index 0000000000..11c789c508
--- /dev/null
+++ b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads1.html
@@ -0,0 +1,65 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Layout Helpers</title>
+<style id="styles">
+ body {
+ margin: 0;
+ padding: 0;
+ }
+
+ #hidden-node {
+ display: none;
+ }
+
+ #simple-node-with-margin-padding-border {
+ width: 200px;
+ height: 200px;
+ background: #f06;
+
+ padding: 20px;
+ margin: 50px;
+ border: 10px solid black;
+ }
+
+ #scrolled-node {
+ position: absolute;
+ top: 0;
+ left: 0;
+
+ width: 300px;
+ height: 100px;
+ overflow: scroll;
+ background: linear-gradient(red, pink);
+ }
+
+ #sub-scrolled-node {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ background: linear-gradient(yellow, green);
+ }
+
+ #inner-scrolled-node {
+ width: 100px;
+ height: 400px;
+ background: linear-gradient(black, white);
+ }
+</style>
+<div id="hidden-node"></div>
+<div id="simple-node-with-margin-padding-border"></div>
+<!-- The inline encoded code below corresponds to:
+<iframe style="margin:10px;border:0;width:300px;height:300px;">
+ <iframe style="margin:10px;border:0;width:200px;height:200px;">
+ <div id="inner-node" style="width:100px;height:100px;border:10px solid red;margin:10px;padding:10px;"></div>
+ </iframe>
+</iframe>
+ -->
+<iframe src="data:text/html,%3Cstyle%3Ebody%7Bmargin:0;padding:0;%7D%3C/style%3E%3Ciframe%20src=%22data:text/html,%253Cstyle%253Ebody%257Bmargin:0;padding:0;%257D%253C/style%253E%253Cdiv%2520id='inner-node'%2520style='width:100px;height:100px;border:10px%2520solid%2520red;margin:10px;padding:10px;'%253E%253C/div%253E%22%20style=%22margin:10px;border:0;width:200px;height:200px;%22%3E%3C/iframe%3E" style="margin:10px;border:0;width:300px;height:300px;"></iframe>
+<div id="scrolled-node">
+ <div id="sub-scrolled-node">
+ <div id="inner-scrolled-node"></div>
+ </div>
+</div>
+<span id="inline">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus porttitor luctus sem id scelerisque. Cras quis velit sed risus euismod lacinia. Donec viverra enim eu ligula efficitur, quis vulputate metus cursus. Duis sed interdum risus. Ut blandit velit vitae faucibus efficitur. Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br/ >
+Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Sed vitae dolor metus. Aliquam sed velit sit amet libero vestibulum aliquam vel a lorem. Integer eget ex eget justo auctor ullamcorper.<br/ >
+Praesent tristique maximus lacus, nec ultricies neque ultrices non. Phasellus vel lobortis justo. </span>
diff --git a/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-a.html b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-a.html
new file mode 100644
index 0000000000..9e52ebd7de
--- /dev/null
+++ b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-a.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<meta charset=utf-8>
+<style>
+ body {
+ margin: 0;
+ padding: 0;
+ }
+ iframe {
+ vertical-align: top;
+ }
+</style>
+<body>
+<div style="width:100px; height:100px; background:green"></div>
+
+<div style="margin:20px">
+<iframe id="b" style="width:250px; height:250px; border:10px solid black" src="https://example.org/browser/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-b-and-d.html">
+</iframe><iframe id="d" style="width:250px; height:250px; border:0; padding:40px" src="https://example.org/browser/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-b-and-d.html">
+</iframe>
+</div>
+</body>
diff --git a/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-b-and-d.html b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-b-and-d.html
new file mode 100644
index 0000000000..10d91e7b3d
--- /dev/null
+++ b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-b-and-d.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+'use strict';
+window.onmessage = event => {
+ // This is a bi-directional message passthrough. We pass "callGetBoxQuads"
+ // messages down, and everything else back up.
+ const iframe = document.querySelector("iframe");
+ const goDown = !!event.data.callGetBoxQuads;
+ const targetWindow = (goDown ? iframe.contentWindow : window.parent);
+
+ targetWindow.postMessage(event.data, "*");
+};
+</script>
+<style>
+ body {
+ margin: 0;
+ padding: 0;
+ background: lavender;
+ }
+ iframe {
+ width: 150px;
+ height: 150px;
+ margin: 50px;
+ border: 0;
+ }
+</style>
+<iframe src="https://test1.example.com/browser/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-c-and-e.html">
+</iframe>
diff --git a/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-c-and-e.html b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-c-and-e.html
new file mode 100644
index 0000000000..7917c82411
--- /dev/null
+++ b/devtools/client/shared/test/doc_layoutHelpers_getBoxQuads2-c-and-e.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<meta charset=utf-8>
+<script>
+'use strict';
+window.onmessage = event => {
+ const innerNode = window.document.getElementById("inner-node");
+ const wrappedNode = SpecialPowers.wrap(innerNode);
+ const wrappedQuads = wrappedNode.getBoxQuadsFromWindowOrigin();
+ const quad = SpecialPowers.unwrap(wrappedQuads[0]);
+
+ window.parent.postMessage({ quad }, "*");
+};
+</script>
+<style>
+ body {
+ margin: 0;
+ padding: 0;
+ background: pink;
+ }
+ div {
+ margin: 50px;
+ width: 50px;
+ height: 50px;
+ background: green;
+ }
+</style>
+<div id="inner-node"></div>
diff --git a/devtools/client/shared/test/doc_listworkers-tab.html b/devtools/client/shared/test/doc_listworkers-tab.html
new file mode 100644
index 0000000000..62ab9be7d2
--- /dev/null
+++ b/devtools/client/shared/test/doc_listworkers-tab.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/devtools/client/shared/test/doc_native-event-handler.html b/devtools/client/shared/test/doc_native-event-handler.html
new file mode 100644
index 0000000000..f08d7c01f1
--- /dev/null
+++ b/devtools/client/shared/test/doc_native-event-handler.html
@@ -0,0 +1,25 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>A video element with native event handlers</title>
+ <script type="text/javascript">
+ "use strict";
+
+ /* exported initialSetup */
+ function initialSetup(event) {
+ // eslint-disable-next-line no-debugger
+ debugger;
+ }
+ window.addEventListener("load", function() {});
+ </script>
+ </head>
+ <body>
+ <button onclick="initialSetup()">Click me!</button>
+ <!-- the "controls" attribute ensures that there are extra event handlers in
+ the element. -->
+ <video controls></video>
+ </body>
+</html>
diff --git a/devtools/client/shared/test/doc_script-switching-01.html b/devtools/client/shared/test/doc_script-switching-01.html
new file mode 100644
index 0000000000..afb4484b5d
--- /dev/null
+++ b/devtools/client/shared/test/doc_script-switching-01.html
@@ -0,0 +1,18 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Debugger test page</title>
+ </head>
+
+ <body>
+ <button onclick="firstCall()">Click me!</button>
+
+ <script type="text/javascript" src="code_script-switching-01.js"></script>
+ <script type="text/javascript" src="code_script-switching-02.js"></script>
+ </body>
+
+</html>
diff --git a/devtools/client/shared/test/doc_script-switching-02.html b/devtools/client/shared/test/doc_script-switching-02.html
new file mode 100644
index 0000000000..cceeea2c8e
--- /dev/null
+++ b/devtools/client/shared/test/doc_script-switching-02.html
@@ -0,0 +1,18 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Debugger test page</title>
+ </head>
+
+ <body>
+ <button onclick="firstCall()">Click me!</button>
+
+ <script type="text/javascript" src="code_script-switching-01.js"></script>
+ <script type="text/javascript" src="code_script-switching-02.js?foo=bar,baz|lol"></script>
+ </body>
+
+</html>
diff --git a/devtools/client/shared/test/doc_spectrum.html b/devtools/client/shared/test/doc_spectrum.html
new file mode 100644
index 0000000000..07e8f37ecc
--- /dev/null
+++ b/devtools/client/shared/test/doc_spectrum.html
@@ -0,0 +1,2 @@
+<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/spectrum.css" type="text/css"/>
+<div id="spectrum-container" />
diff --git a/devtools/client/shared/test/doc_tableWidget_basic.html b/devtools/client/shared/test/doc_tableWidget_basic.html
new file mode 100644
index 0000000000..c3afa2d88f
--- /dev/null
+++ b/devtools/client/shared/test/doc_tableWidget_basic.html
@@ -0,0 +1,7 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href='chrome://global/skin/global.css'?>
+<?xml-stylesheet href='chrome://devtools/skin/light-theme.css'?>
+<?xml-stylesheet href='chrome://devtools/skin/widgets.css'?>
+<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
+ title='Table Widget' width='600' height='500'>
+<box flex='1' class='theme-light'/></window>
diff --git a/devtools/client/shared/test/doc_tableWidget_keyboard_interaction.xhtml b/devtools/client/shared/test/doc_tableWidget_keyboard_interaction.xhtml
new file mode 100644
index 0000000000..40656a7470
--- /dev/null
+++ b/devtools/client/shared/test/doc_tableWidget_keyboard_interaction.xhtml
@@ -0,0 +1,8 @@
+<?xml version='1.0'?>
+<?xml-stylesheet href='chrome://global/skin/global.css'?>
+<?xml-stylesheet href='chrome://devtools/skin/light-theme.css'?>
+<?xml-stylesheet href='chrome://devtools/skin/widgets.css'?>
+
+<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
+ title='Table Widget' width='600' height='500'>
+<box flex='1' class='theme-light'/></window>
diff --git a/devtools/client/shared/test/doc_tableWidget_mouse_interaction.xhtml b/devtools/client/shared/test/doc_tableWidget_mouse_interaction.xhtml
new file mode 100644
index 0000000000..2a23170230
--- /dev/null
+++ b/devtools/client/shared/test/doc_tableWidget_mouse_interaction.xhtml
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href='chrome://global/skin/global.css'?>
+<?xml-stylesheet href='chrome://devtools/skin/light-theme.css'?>
+<?xml-stylesheet href='chrome://devtools/skin/widgets.css'?>
+
+<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul' title='Table Widget' width='600' height='500'>
+<box flex='1' class='theme-light'/></window>
diff --git a/devtools/client/shared/test/doc_templater_basic.html b/devtools/client/shared/test/doc_templater_basic.html
new file mode 100644
index 0000000000..47b3cd258b
--- /dev/null
+++ b/devtools/client/shared/test/doc_templater_basic.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<!-- Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<html>
+<head>
+ <title>DOM Template Tests</title>
+</head>
+<body>
+
+</body>
+</html>
diff --git a/devtools/client/shared/test/dummy.html b/devtools/client/shared/test/dummy.html
new file mode 100644
index 0000000000..18ecdcb795
--- /dev/null
+++ b/devtools/client/shared/test/dummy.html
@@ -0,0 +1 @@
+<html></html>
diff --git a/devtools/client/shared/test/head.js b/devtools/client/shared/test/head.js
new file mode 100644
index 0000000000..47731bc74a
--- /dev/null
+++ b/devtools/client/shared/test/head.js
@@ -0,0 +1,211 @@
+/* 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/. */
+/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+
+"use strict";
+
+// shared-head.js handles imports, constants, and utility functions
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
+ this
+);
+
+const { DOMHelpers } = require("resource://devtools/shared/dom-helpers.js");
+const {
+ Hosts,
+} = require("resource://devtools/client/framework/toolbox-hosts.js");
+
+const TEST_URI_ROOT = "http://example.com/browser/devtools/client/shared/test/";
+const TEST_URI_ROOT_SSL =
+ "https://example.com/browser/devtools/client/shared/test/";
+
+const EXAMPLE_URL =
+ "chrome://mochitests/content/browser/devtools/client/shared/test/";
+
+function catchFail(func) {
+ return function () {
+ try {
+ return func.apply(null, arguments);
+ } catch (ex) {
+ ok(false, ex);
+ console.error(ex);
+ finish();
+ throw ex;
+ }
+ };
+}
+
+/**
+ * Polls a given function waiting for the given value.
+ *
+ * @param object options
+ * Options object with the following properties:
+ * - validator
+ * A validator function that should return the expected value. This is
+ * called every few milliseconds to check if the result is the expected
+ * one. When the returned result is the expected one, then the |success|
+ * function is called and polling stops. If |validator| never returns
+ * the expected value, then polling timeouts after several tries and
+ * a failure is recorded - the given |failure| function is invoked.
+ * - success
+ * A function called when the validator function returns the expected
+ * value.
+ * - failure
+ * A function called if the validator function timeouts - fails to return
+ * the expected value in the given time.
+ * - name
+ * Name of test. This is used to generate the success and failure
+ * messages.
+ * - timeout
+ * Timeout for validator function, in milliseconds. Default is 5000 ms.
+ * - value
+ * The expected value. If this option is omitted then the |validator|
+ * function must return a trueish value.
+ * Each of the provided callback functions will receive two arguments:
+ * the |options| object and the last value returned by |validator|.
+ */
+function waitForValue(options) {
+ const start = Date.now();
+ const timeout = options.timeout || 5000;
+ let lastValue;
+
+ function wait(validatorFn, successFn, failureFn) {
+ if (Date.now() - start > timeout) {
+ // Log the failure.
+ ok(false, "Timed out while waiting for: " + options.name);
+ const expected =
+ "value" in options ? "'" + options.value + "'" : "a trueish value";
+ info("timeout info :: got '" + lastValue + "', expected " + expected);
+ failureFn(options, lastValue);
+ return;
+ }
+
+ lastValue = validatorFn(options, lastValue);
+ const successful =
+ "value" in options ? lastValue == options.value : lastValue;
+ if (successful) {
+ ok(true, options.name);
+ successFn(options, lastValue);
+ } else {
+ setTimeout(() => {
+ wait(validatorFn, successFn, failureFn);
+ }, 100);
+ }
+ }
+
+ wait(options.validator, options.success, options.failure);
+}
+
+function oneTimeObserve(name, callback) {
+ return new Promise(resolve => {
+ const func = function () {
+ Services.obs.removeObserver(func, name);
+ if (callback) {
+ callback();
+ }
+ resolve();
+ };
+ Services.obs.addObserver(func, name);
+ });
+}
+
+const createHost = async function (
+ type = "bottom",
+ src = CHROME_URL_ROOT + "dummy.html"
+) {
+ const host = new Hosts[type](gBrowser.selectedTab);
+ const iframe = await host.create();
+
+ await new Promise(resolve => {
+ iframe.setAttribute("src", src);
+ DOMHelpers.onceDOMReady(iframe.contentWindow, resolve);
+ });
+
+ // Popup tests fail very frequently on Linux + webrender because they run
+ // too early.
+ await waitForPresShell(iframe);
+
+ return { host, win: iframe.contentWindow, doc: iframe.contentDocument };
+};
+
+/**
+ * Open and close the toolbox in the current browser tab, several times, waiting
+ * some amount of time in between.
+ * @param {Number} nbOfTimes
+ * @param {Number} usageTime in milliseconds
+ * @param {String} toolId
+ */
+async function openAndCloseToolbox(nbOfTimes, usageTime, toolId) {
+ for (let i = 0; i < nbOfTimes; i++) {
+ info("Opening toolbox " + (i + 1));
+
+ const tab = gBrowser.selectedTab;
+ const toolbox = await gDevTools.showToolboxForTab(tab, { toolId });
+
+ // We use a timeout to check the toolbox's active time
+ await new Promise(resolve => setTimeout(resolve, usageTime));
+
+ info("Closing toolbox " + (i + 1));
+ await toolbox.destroy();
+ }
+}
+
+/**
+ * Waits until a predicate returns true.
+ *
+ * @param function predicate
+ * Invoked once in a while until it returns true.
+ * @param number interval [optional]
+ * How often the predicate is invoked, in milliseconds.
+ */
+function waitUntil(predicate, interval = 10) {
+ if (predicate()) {
+ return Promise.resolve(true);
+ }
+ return new Promise(resolve => {
+ setTimeout(function () {
+ waitUntil(predicate).then(() => resolve(true));
+ }, interval);
+ });
+}
+
+/**
+ * Show the presets list sidebar in the cssfilter widget popup
+ * @param {CSSFilterWidget} widget
+ * @return {Promise}
+ */
+function showFilterPopupPresets(widget) {
+ const onRender = widget.once("render");
+ widget._togglePresets();
+ return onRender;
+}
+
+/**
+ * Show presets list and create a sample preset with the name and value provided
+ * @param {CSSFilterWidget} widget
+ * @param {string} name
+ * @param {string} value
+ * @return {Promise}
+ */
+const showFilterPopupPresetsAndCreatePreset = async function (
+ widget,
+ name,
+ value
+) {
+ await showFilterPopupPresets(widget);
+
+ let onRender = widget.once("render");
+ widget.setCssValue(value);
+ await onRender;
+
+ const footer = widget.el.querySelector(".presets-list .footer");
+ footer.querySelector("input").value = name;
+
+ onRender = widget.once("render");
+ widget._savePreset({
+ preventDefault: () => {},
+ });
+
+ await onRender;
+};
diff --git a/devtools/client/shared/test/helper_color_data.js b/devtools/client/shared/test/helper_color_data.js
new file mode 100644
index 0000000000..6429c95530
--- /dev/null
+++ b/devtools/client/shared/test/helper_color_data.js
@@ -0,0 +1,1499 @@
+"use strict";
+
+// This is used to test color.js in browser_css_color.js
+
+/* eslint-disable max-len */
+function getFixtureColorData() {
+ return [
+ {
+ authored: "aliceblue",
+ name: "aliceblue",
+ hex: "#f0f8ff",
+ hsl: "hsl(208, 100%, 97.1%)",
+ rgb: "rgb(240, 248, 255)",
+ hwb: "hwb(208 94.1% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "antiquewhite",
+ name: "antiquewhite",
+ hex: "#faebd7",
+ hsl: "hsl(34.3, 77.8%, 91.2%)",
+ rgb: "rgb(250, 235, 215)",
+ hwb: "hwb(34.3 84.3% 2%)",
+ cycle: 5,
+ },
+ {
+ authored: "aqua",
+ name: "aqua",
+ hex: "#0ff",
+ hsl: "hsl(180, 100%, 50%)",
+ rgb: "rgb(0, 255, 255)",
+ hwb: "hwb(180 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "aquamarine",
+ name: "aquamarine",
+ hex: "#7fffd4",
+ hsl: "hsl(159.8, 100%, 74.9%)",
+ rgb: "rgb(127, 255, 212)",
+ hwb: "hwb(159.8 49.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "azure",
+ name: "azure",
+ hex: "#f0ffff",
+ hsl: "hsl(180, 100%, 97.1%)",
+ rgb: "rgb(240, 255, 255)",
+ hwb: "hwb(180 94.1% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "beige",
+ name: "beige",
+ hex: "#f5f5dc",
+ hsl: "hsl(60, 55.6%, 91.2%)",
+ rgb: "rgb(245, 245, 220)",
+ hwb: "hwb(60 86.3% 3.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "bisque",
+ name: "bisque",
+ hex: "#ffe4c4",
+ hsl: "hsl(32.5, 100%, 88.4%)",
+ rgb: "rgb(255, 228, 196)",
+ hwb: "hwb(32.5 76.9% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "black",
+ name: "black",
+ hex: "#000",
+ hsl: "hsl(0, 0%, 0%)",
+ rgb: "rgb(0, 0, 0)",
+ hwb: "hwb(0 0% 100%)",
+ cycle: 5,
+ },
+ {
+ authored: "blanchedalmond",
+ name: "blanchedalmond",
+ hex: "#ffebcd",
+ hsl: "hsl(36, 100%, 90.2%)",
+ rgb: "rgb(255, 235, 205)",
+ hwb: "hwb(36 80.4% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "blue",
+ name: "blue",
+ hex: "#00f",
+ hsl: "hsl(240, 100%, 50%)",
+ rgb: "rgb(0, 0, 255)",
+ hwb: "hwb(240 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "blueviolet",
+ name: "blueviolet",
+ hex: "#8a2be2",
+ hsl: "hsl(271.1, 75.9%, 52.7%)",
+ rgb: "rgb(138, 43, 226)",
+ hwb: "hwb(271.1 16.9% 11.4%)",
+ cycle: 5,
+ },
+ {
+ authored: "brown",
+ name: "brown",
+ hex: "#a52a2a",
+ hsl: "hsl(0, 59.4%, 40.6%)",
+ rgb: "rgb(165, 42, 42)",
+ hwb: "hwb(0 16.5% 35.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "burlywood",
+ name: "burlywood",
+ hex: "#deb887",
+ hsl: "hsl(33.8, 56.9%, 70%)",
+ rgb: "rgb(222, 184, 135)",
+ hwb: "hwb(33.8 52.9% 12.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "cadetblue",
+ name: "cadetblue",
+ hex: "#5f9ea0",
+ hsl: "hsl(181.8, 25.5%, 50%)",
+ rgb: "rgb(95, 158, 160)",
+ hwb: "hwb(181.8 37.3% 37.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "chartreuse",
+ name: "chartreuse",
+ hex: "#7fff00",
+ hsl: "hsl(90.1, 100%, 50%)",
+ rgb: "rgb(127, 255, 0)",
+ hwb: "hwb(90.1 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "chocolate",
+ name: "chocolate",
+ hex: "#d2691e",
+ hsl: "hsl(25, 75%, 47.1%)",
+ rgb: "rgb(210, 105, 30)",
+ hwb: "hwb(25 11.8% 17.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "coral",
+ name: "coral",
+ hex: "#ff7f50",
+ hsl: "hsl(16.1, 100%, 65.7%)",
+ rgb: "rgb(255, 127, 80)",
+ hwb: "hwb(16.1 31.4% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "cornflowerblue",
+ name: "cornflowerblue",
+ hex: "#6495ed",
+ hsl: "hsl(218.5, 79.2%, 66.1%)",
+ rgb: "rgb(100, 149, 237)",
+ hwb: "hwb(218.5 39.2% 7.1%)",
+ cycle: 5,
+ },
+ {
+ authored: "cornsilk",
+ name: "cornsilk",
+ hex: "#fff8dc",
+ hsl: "hsl(48, 100%, 93.1%)",
+ rgb: "rgb(255, 248, 220)",
+ hwb: "hwb(48 86.3% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "crimson",
+ name: "crimson",
+ hex: "#dc143c",
+ hsl: "hsl(348, 83.3%, 47.1%)",
+ rgb: "rgb(220, 20, 60)",
+ hwb: "hwb(348 7.8% 13.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "cyan",
+ name: "aqua",
+ hex: "#0ff",
+ hsl: "hsl(180, 100%, 50%)",
+ rgb: "rgb(0, 255, 255)",
+ hwb: "hwb(180 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkblue",
+ name: "darkblue",
+ hex: "#00008b",
+ hsl: "hsl(240, 100%, 27.3%)",
+ rgb: "rgb(0, 0, 139)",
+ hwb: "hwb(240 0% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkcyan",
+ name: "darkcyan",
+ hex: "#008b8b",
+ hsl: "hsl(180, 100%, 27.3%)",
+ rgb: "rgb(0, 139, 139)",
+ hwb: "hwb(180 0% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkgoldenrod",
+ name: "darkgoldenrod",
+ hex: "#b8860b",
+ hsl: "hsl(42.7, 88.7%, 38.2%)",
+ rgb: "rgb(184, 134, 11)",
+ hwb: "hwb(42.7 4.3% 27.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkgray",
+ name: "darkgray",
+ hex: "#a9a9a9",
+ hsl: "hsl(0, 0%, 66.3%)",
+ rgb: "rgb(169, 169, 169)",
+ hwb: "hwb(0 66.3% 33.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkgreen",
+ name: "darkgreen",
+ hex: "#006400",
+ hsl: "hsl(120, 100%, 19.6%)",
+ rgb: "rgb(0, 100, 0)",
+ hwb: "hwb(120 0% 60.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkgrey",
+ name: "darkgray",
+ hex: "#a9a9a9",
+ hsl: "hsl(0, 0%, 66.3%)",
+ rgb: "rgb(169, 169, 169)",
+ hwb: "hwb(0 66.3% 33.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkkhaki",
+ name: "darkkhaki",
+ hex: "#bdb76b",
+ hsl: "hsl(55.6, 38.3%, 58%)",
+ rgb: "rgb(189, 183, 107)",
+ hwb: "hwb(55.6 42% 25.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkmagenta",
+ name: "darkmagenta",
+ hex: "#8b008b",
+ hsl: "hsl(300, 100%, 27.3%)",
+ rgb: "rgb(139, 0, 139)",
+ hwb: "hwb(300 0% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkolivegreen",
+ name: "darkolivegreen",
+ hex: "#556b2f",
+ hsl: "hsl(82, 39%, 30.2%)",
+ rgb: "rgb(85, 107, 47)",
+ hwb: "hwb(82 18.4% 58%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkorange",
+ name: "darkorange",
+ hex: "#ff8c00",
+ hsl: "hsl(32.9, 100%, 50%)",
+ rgb: "rgb(255, 140, 0)",
+ hwb: "hwb(32.9 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkorchid",
+ name: "darkorchid",
+ hex: "#9932cc",
+ hsl: "hsl(280.1, 60.6%, 49.8%)",
+ rgb: "rgb(153, 50, 204)",
+ hwb: "hwb(280.1 19.6% 20%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkred",
+ name: "darkred",
+ hex: "#8b0000",
+ hsl: "hsl(0, 100%, 27.3%)",
+ rgb: "rgb(139, 0, 0)",
+ hwb: "hwb(0 0% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "darksalmon",
+ name: "darksalmon",
+ hex: "#e9967a",
+ hsl: "hsl(15.1, 71.6%, 69.6%)",
+ rgb: "rgb(233, 150, 122)",
+ hwb: "hwb(15.1 47.8% 8.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkseagreen",
+ name: "darkseagreen",
+ hex: "#8fbc8f",
+ hsl: "hsl(120, 25.1%, 64.9%)",
+ rgb: "rgb(143, 188, 143)",
+ hwb: "hwb(120 56.1% 26.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkslateblue",
+ name: "darkslateblue",
+ hex: "#483d8b",
+ hsl: "hsl(248.5, 39%, 39.2%)",
+ rgb: "rgb(72, 61, 139)",
+ hwb: "hwb(248.5 23.9% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkslategray",
+ name: "darkslategray",
+ hex: "#2f4f4f",
+ hsl: "hsl(180, 25.4%, 24.7%)",
+ rgb: "rgb(47, 79, 79)",
+ hwb: "hwb(180 18.4% 69%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkslategrey",
+ name: "darkslategray",
+ hex: "#2f4f4f",
+ hsl: "hsl(180, 25.4%, 24.7%)",
+ rgb: "rgb(47, 79, 79)",
+ hwb: "hwb(180 18.4% 69%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkturquoise",
+ name: "darkturquoise",
+ hex: "#00ced1",
+ hsl: "hsl(180.9, 100%, 41%)",
+ rgb: "rgb(0, 206, 209)",
+ hwb: "hwb(180.9 0% 18%)",
+ cycle: 5,
+ },
+ {
+ authored: "darkviolet",
+ name: "darkviolet",
+ hex: "#9400d3",
+ hsl: "hsl(282.1, 100%, 41.4%)",
+ rgb: "rgb(148, 0, 211)",
+ hwb: "hwb(282.1 0% 17.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "deeppink",
+ name: "deeppink",
+ hex: "#ff1493",
+ hsl: "hsl(327.6, 100%, 53.9%)",
+ rgb: "rgb(255, 20, 147)",
+ hwb: "hwb(327.6 7.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "deepskyblue",
+ name: "deepskyblue",
+ hex: "#00bfff",
+ hsl: "hsl(195.1, 100%, 50%)",
+ rgb: "rgb(0, 191, 255)",
+ hwb: "hwb(195.1 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "dimgray",
+ name: "dimgray",
+ hex: "#696969",
+ hsl: "hsl(0, 0%, 41.2%)",
+ rgb: "rgb(105, 105, 105)",
+ hwb: "hwb(0 41.2% 58.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "dodgerblue",
+ name: "dodgerblue",
+ hex: "#1e90ff",
+ hsl: "hsl(209.6, 100%, 55.9%)",
+ rgb: "rgb(30, 144, 255)",
+ hwb: "hwb(209.6 11.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "firebrick",
+ name: "firebrick",
+ hex: "#b22222",
+ hsl: "hsl(0, 67.9%, 41.6%)",
+ rgb: "rgb(178, 34, 34)",
+ hwb: "hwb(0 13.3% 30.2%)",
+ cycle: 5,
+ },
+ {
+ authored: "floralwhite",
+ name: "floralwhite",
+ hex: "#fffaf0",
+ hsl: "hsl(40, 100%, 97.1%)",
+ rgb: "rgb(255, 250, 240)",
+ hwb: "hwb(40 94.1% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "forestgreen",
+ name: "forestgreen",
+ hex: "#228b22",
+ hsl: "hsl(120, 60.7%, 33.9%)",
+ rgb: "rgb(34, 139, 34)",
+ hwb: "hwb(120 13.3% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "fuchsia",
+ name: "fuchsia",
+ hex: "#f0f",
+ hsl: "hsl(300, 100%, 50%)",
+ rgb: "rgb(255, 0, 255)",
+ hwb: "hwb(300 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "gainsboro",
+ name: "gainsboro",
+ hex: "#dcdcdc",
+ hsl: "hsl(0, 0%, 86.3%)",
+ rgb: "rgb(220, 220, 220)",
+ hwb: "hwb(0 86.3% 13.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "ghostwhite",
+ name: "ghostwhite",
+ hex: "#f8f8ff",
+ hsl: "hsl(240, 100%, 98.6%)",
+ rgb: "rgb(248, 248, 255)",
+ hwb: "hwb(240 97.3% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "gold",
+ name: "gold",
+ hex: "#ffd700",
+ hsl: "hsl(50.6, 100%, 50%)",
+ rgb: "rgb(255, 215, 0)",
+ hwb: "hwb(50.6 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "goldenrod",
+ name: "goldenrod",
+ hex: "#daa520",
+ hsl: "hsl(42.9, 74.4%, 49%)",
+ rgb: "rgb(218, 165, 32)",
+ hwb: "hwb(42.9 12.5% 14.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "gray",
+ name: "gray",
+ hex: "#808080",
+ hsl: "hsl(0, 0%, 50.2%)",
+ rgb: "rgb(128, 128, 128)",
+ hwb: "hwb(0 50.2% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "green",
+ name: "green",
+ hex: "#008000",
+ hsl: "hsl(120, 100%, 25.1%)",
+ rgb: "rgb(0, 128, 0)",
+ hwb: "hwb(120 0% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "greenyellow",
+ name: "greenyellow",
+ hex: "#adff2f",
+ hsl: "hsl(83.7, 100%, 59.2%)",
+ rgb: "rgb(173, 255, 47)",
+ hwb: "hwb(83.7 18.4% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "grey",
+ name: "gray",
+ hex: "#808080",
+ hsl: "hsl(0, 0%, 50.2%)",
+ rgb: "rgb(128, 128, 128)",
+ hwb: "hwb(0 50.2% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "honeydew",
+ name: "honeydew",
+ hex: "#f0fff0",
+ hsl: "hsl(120, 100%, 97.1%)",
+ rgb: "rgb(240, 255, 240)",
+ hwb: "hwb(120 94.1% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "hotpink",
+ name: "hotpink",
+ hex: "#ff69b4",
+ hsl: "hsl(330, 100%, 70.6%)",
+ rgb: "rgb(255, 105, 180)",
+ hwb: "hwb(330 41.2% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "indianred",
+ name: "indianred",
+ hex: "#cd5c5c",
+ hsl: "hsl(0, 53.1%, 58.2%)",
+ rgb: "rgb(205, 92, 92)",
+ hwb: "hwb(0 36.1% 19.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "indigo",
+ name: "indigo",
+ hex: "#4b0082",
+ hsl: "hsl(274.6, 100%, 25.5%)",
+ rgb: "rgb(75, 0, 130)",
+ hwb: "hwb(274.6 0% 49%)",
+ cycle: 5,
+ },
+ {
+ authored: "ivory",
+ name: "ivory",
+ hex: "#fffff0",
+ hsl: "hsl(60, 100%, 97.1%)",
+ rgb: "rgb(255, 255, 240)",
+ hwb: "hwb(60 94.1% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "khaki",
+ name: "khaki",
+ hex: "#f0e68c",
+ hsl: "hsl(54, 76.9%, 74.5%)",
+ rgb: "rgb(240, 230, 140)",
+ hwb: "hwb(54 54.9% 5.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "lavender",
+ name: "lavender",
+ hex: "#e6e6fa",
+ hsl: "hsl(240, 66.7%, 94.1%)",
+ rgb: "rgb(230, 230, 250)",
+ hwb: "hwb(240 90.2% 2%)",
+ cycle: 5,
+ },
+ {
+ authored: "lavenderblush",
+ name: "lavenderblush",
+ hex: "#fff0f5",
+ hsl: "hsl(340, 100%, 97.1%)",
+ rgb: "rgb(255, 240, 245)",
+ hwb: "hwb(340 94.1% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "lawngreen",
+ name: "lawngreen",
+ hex: "#7cfc00",
+ hsl: "hsl(90.5, 100%, 49.4%)",
+ rgb: "rgb(124, 252, 0)",
+ hwb: "hwb(90.5 0% 1.2%)",
+ cycle: 5,
+ },
+ {
+ authored: "lemonchiffon",
+ name: "lemonchiffon",
+ hex: "#fffacd",
+ hsl: "hsl(54, 100%, 90.2%)",
+ rgb: "rgb(255, 250, 205)",
+ hwb: "hwb(54 80.4% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightblue",
+ name: "lightblue",
+ hex: "#add8e6",
+ hsl: "hsl(194.7, 53.3%, 79%)",
+ rgb: "rgb(173, 216, 230)",
+ hwb: "hwb(194.7 67.8% 9.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightcoral",
+ name: "lightcoral",
+ hex: "#f08080",
+ hsl: "hsl(0, 78.9%, 72.2%)",
+ rgb: "rgb(240, 128, 128)",
+ hwb: "hwb(0 50.2% 5.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightcyan",
+ name: "lightcyan",
+ hex: "#e0ffff",
+ hsl: "hsl(180, 100%, 93.9%)",
+ rgb: "rgb(224, 255, 255)",
+ hwb: "hwb(180 87.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightgoldenrodyellow",
+ name: "lightgoldenrodyellow",
+ hex: "#fafad2",
+ hsl: "hsl(60, 80%, 90.2%)",
+ rgb: "rgb(250, 250, 210)",
+ hwb: "hwb(60 82.4% 2%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightgray",
+ name: "lightgray",
+ hex: "#d3d3d3",
+ hsl: "hsl(0, 0%, 82.7%)",
+ rgb: "rgb(211, 211, 211)",
+ hwb: "hwb(0 82.7% 17.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightgreen",
+ name: "lightgreen",
+ hex: "#90ee90",
+ hsl: "hsl(120, 73.4%, 74.9%)",
+ rgb: "rgb(144, 238, 144)",
+ hwb: "hwb(120 56.5% 6.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightgrey",
+ name: "lightgray",
+ hex: "#d3d3d3",
+ hsl: "hsl(0, 0%, 82.7%)",
+ rgb: "rgb(211, 211, 211)",
+ hwb: "hwb(0 82.7% 17.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightpink",
+ name: "lightpink",
+ hex: "#ffb6c1",
+ hsl: "hsl(351, 100%, 85.7%)",
+ rgb: "rgb(255, 182, 193)",
+ hwb: "hwb(351 71.4% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightsalmon",
+ name: "lightsalmon",
+ hex: "#ffa07a",
+ hsl: "hsl(17.1, 100%, 73.9%)",
+ rgb: "rgb(255, 160, 122)",
+ hwb: "hwb(17.1 47.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightseagreen",
+ name: "lightseagreen",
+ hex: "#20b2aa",
+ hsl: "hsl(176.7, 69.5%, 41.2%)",
+ rgb: "rgb(32, 178, 170)",
+ hwb: "hwb(176.7 12.5% 30.2%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightskyblue",
+ name: "lightskyblue",
+ hex: "#87cefa",
+ hsl: "hsl(203, 92%, 75.5%)",
+ rgb: "rgb(135, 206, 250)",
+ hwb: "hwb(203 52.9% 2%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightslategray",
+ name: "lightslategray",
+ hex: "#789",
+ hsl: "hsl(210, 14.3%, 53.3%)",
+ rgb: "rgb(119, 136, 153)",
+ hwb: "hwb(210 46.7% 40%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightslategrey",
+ name: "lightslategray",
+ hex: "#789",
+ hsl: "hsl(210, 14.3%, 53.3%)",
+ rgb: "rgb(119, 136, 153)",
+ hwb: "hwb(210 46.7% 40%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightsteelblue",
+ name: "lightsteelblue",
+ hex: "#b0c4de",
+ hsl: "hsl(213.9, 41.1%, 78%)",
+ rgb: "rgb(176, 196, 222)",
+ hwb: "hwb(213.9 69% 12.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "lightyellow",
+ name: "lightyellow",
+ hex: "#ffffe0",
+ hsl: "hsl(60, 100%, 93.9%)",
+ rgb: "rgb(255, 255, 224)",
+ hwb: "hwb(60 87.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "lime",
+ name: "lime",
+ hex: "#0f0",
+ hsl: "hsl(120, 100%, 50%)",
+ rgb: "rgb(0, 255, 0)",
+ hwb: "hwb(120 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "limegreen",
+ name: "limegreen",
+ hex: "#32cd32",
+ hsl: "hsl(120, 60.8%, 50%)",
+ rgb: "rgb(50, 205, 50)",
+ hwb: "hwb(120 19.6% 19.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "linen",
+ name: "linen",
+ hex: "#faf0e6",
+ hsl: "hsl(30, 66.7%, 94.1%)",
+ rgb: "rgb(250, 240, 230)",
+ hwb: "hwb(30 90.2% 2%)",
+ cycle: 5,
+ },
+ {
+ authored: "magenta",
+ name: "fuchsia",
+ hex: "#f0f",
+ hsl: "hsl(300, 100%, 50%)",
+ rgb: "rgb(255, 0, 255)",
+ hwb: "hwb(300 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "maroon",
+ name: "maroon",
+ hex: "#800000",
+ hsl: "hsl(0, 100%, 25.1%)",
+ rgb: "rgb(128, 0, 0)",
+ hwb: "hwb(0 0% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumaquamarine",
+ name: "mediumaquamarine",
+ hex: "#66cdaa",
+ hsl: "hsl(159.6, 50.7%, 60.2%)",
+ rgb: "rgb(102, 205, 170)",
+ hwb: "hwb(159.6 40% 19.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumblue",
+ name: "mediumblue",
+ hex: "#0000cd",
+ hsl: "hsl(240, 100%, 40.2%)",
+ rgb: "rgb(0, 0, 205)",
+ hwb: "hwb(240 0% 19.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumorchid",
+ name: "mediumorchid",
+ hex: "#ba55d3",
+ hsl: "hsl(288.1, 58.9%, 58%)",
+ rgb: "rgb(186, 85, 211)",
+ hwb: "hwb(288.1 33.3% 17.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumpurple",
+ name: "mediumpurple",
+ hex: "#9370db",
+ hsl: "hsl(259.6, 59.8%, 64.9%)",
+ rgb: "rgb(147, 112, 219)",
+ hwb: "hwb(259.6 43.9% 14.1%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumseagreen",
+ name: "mediumseagreen",
+ hex: "#3cb371",
+ hsl: "hsl(146.7, 49.8%, 46.9%)",
+ rgb: "rgb(60, 179, 113)",
+ hwb: "hwb(146.7 23.5% 29.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumslateblue",
+ name: "mediumslateblue",
+ hex: "#7b68ee",
+ hsl: "hsl(248.5, 79.8%, 67.1%)",
+ rgb: "rgb(123, 104, 238)",
+ hwb: "hwb(248.5 40.8% 6.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumspringgreen",
+ name: "mediumspringgreen",
+ hex: "#00fa9a",
+ hsl: "hsl(157, 100%, 49%)",
+ rgb: "rgb(0, 250, 154)",
+ hwb: "hwb(157 0% 2%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumturquoise",
+ name: "mediumturquoise",
+ hex: "#48d1cc",
+ hsl: "hsl(177.8, 59.8%, 55.1%)",
+ rgb: "rgb(72, 209, 204)",
+ hwb: "hwb(177.8 28.2% 18%)",
+ cycle: 5,
+ },
+ {
+ authored: "mediumvioletred",
+ name: "mediumvioletred",
+ hex: "#c71585",
+ hsl: "hsl(322.2, 80.9%, 43.1%)",
+ rgb: "rgb(199, 21, 133)",
+ hwb: "hwb(322.2 8.2% 22%)",
+ cycle: 5,
+ },
+ {
+ authored: "midnightblue",
+ name: "midnightblue",
+ hex: "#191970",
+ hsl: "hsl(240, 63.5%, 26.9%)",
+ rgb: "rgb(25, 25, 112)",
+ hwb: "hwb(240 9.8% 56.1%)",
+ cycle: 5,
+ },
+ {
+ authored: "mintcream",
+ name: "mintcream",
+ hex: "#f5fffa",
+ hsl: "hsl(150, 100%, 98%)",
+ rgb: "rgb(245, 255, 250)",
+ hwb: "hwb(150 96.1% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "mistyrose",
+ name: "mistyrose",
+ hex: "#ffe4e1",
+ hsl: "hsl(6, 100%, 94.1%)",
+ rgb: "rgb(255, 228, 225)",
+ hwb: "hwb(6 88.2% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "moccasin",
+ name: "moccasin",
+ hex: "#ffe4b5",
+ hsl: "hsl(38.1, 100%, 85.5%)",
+ rgb: "rgb(255, 228, 181)",
+ hwb: "hwb(38.1 71% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "navajowhite",
+ name: "navajowhite",
+ hex: "#ffdead",
+ hsl: "hsl(35.9, 100%, 83.9%)",
+ rgb: "rgb(255, 222, 173)",
+ hwb: "hwb(35.9 67.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "navy",
+ name: "navy",
+ hex: "#000080",
+ hsl: "hsl(240, 100%, 25.1%)",
+ rgb: "rgb(0, 0, 128)",
+ hwb: "hwb(240 0% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "oldlace",
+ name: "oldlace",
+ hex: "#fdf5e6",
+ hsl: "hsl(39.1, 85.2%, 94.7%)",
+ rgb: "rgb(253, 245, 230)",
+ hwb: "hwb(39.1 90.2% 0.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "olive",
+ name: "olive",
+ hex: "#808000",
+ hsl: "hsl(60, 100%, 25.1%)",
+ rgb: "rgb(128, 128, 0)",
+ hwb: "hwb(60 0% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "olivedrab",
+ name: "olivedrab",
+ hex: "#6b8e23",
+ hsl: "hsl(79.6, 60.5%, 34.7%)",
+ rgb: "rgb(107, 142, 35)",
+ hwb: "hwb(79.6 13.7% 44.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "orange",
+ name: "orange",
+ hex: "#ffa500",
+ hsl: "hsl(38.8, 100%, 50%)",
+ rgb: "rgb(255, 165, 0)",
+ hwb: "hwb(38.8 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "orangered",
+ name: "orangered",
+ hex: "#ff4500",
+ hsl: "hsl(16.2, 100%, 50%)",
+ rgb: "rgb(255, 69, 0)",
+ hwb: "hwb(16.2 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "orchid",
+ name: "orchid",
+ hex: "#da70d6",
+ hsl: "hsl(302.3, 58.9%, 64.7%)",
+ rgb: "rgb(218, 112, 214)",
+ hwb: "hwb(302.3 43.9% 14.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "palegoldenrod",
+ name: "palegoldenrod",
+ hex: "#eee8aa",
+ hsl: "hsl(54.7, 66.7%, 80%)",
+ rgb: "rgb(238, 232, 170)",
+ hwb: "hwb(54.7 66.7% 6.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "palegreen",
+ name: "palegreen",
+ hex: "#98fb98",
+ hsl: "hsl(120, 92.5%, 79%)",
+ rgb: "rgb(152, 251, 152)",
+ hwb: "hwb(120 59.6% 1.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "paleturquoise",
+ name: "paleturquoise",
+ hex: "#afeeee",
+ hsl: "hsl(180, 64.9%, 81%)",
+ rgb: "rgb(175, 238, 238)",
+ hwb: "hwb(180 68.6% 6.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "palevioletred",
+ name: "palevioletred",
+ hex: "#db7093",
+ hsl: "hsl(340.4, 59.8%, 64.9%)",
+ rgb: "rgb(219, 112, 147)",
+ hwb: "hwb(340.4 43.9% 14.1%)",
+ cycle: 5,
+ },
+ {
+ authored: "papayawhip",
+ name: "papayawhip",
+ hex: "#ffefd5",
+ hsl: "hsl(37.1, 100%, 91.8%)",
+ rgb: "rgb(255, 239, 213)",
+ hwb: "hwb(37.1 83.5% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "peachpuff",
+ name: "peachpuff",
+ hex: "#ffdab9",
+ hsl: "hsl(28.3, 100%, 86.3%)",
+ rgb: "rgb(255, 218, 185)",
+ hwb: "hwb(28.3 72.5% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "peru",
+ name: "peru",
+ hex: "#cd853f",
+ hsl: "hsl(29.6, 58.7%, 52.5%)",
+ rgb: "rgb(205, 133, 63)",
+ hwb: "hwb(29.6 24.7% 19.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "pink",
+ name: "pink",
+ hex: "#ffc0cb",
+ hsl: "hsl(349.5, 100%, 87.6%)",
+ rgb: "rgb(255, 192, 203)",
+ hwb: "hwb(349.5 75.3% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "plum",
+ name: "plum",
+ hex: "#dda0dd",
+ hsl: "hsl(300, 47.3%, 74.7%)",
+ rgb: "rgb(221, 160, 221)",
+ hwb: "hwb(300 62.7% 13.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "powderblue",
+ name: "powderblue",
+ hex: "#b0e0e6",
+ hsl: "hsl(186.7, 51.9%, 79.6%)",
+ rgb: "rgb(176, 224, 230)",
+ hwb: "hwb(186.7 69% 9.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "purple",
+ name: "purple",
+ hex: "#800080",
+ hsl: "hsl(300, 100%, 25.1%)",
+ rgb: "rgb(128, 0, 128)",
+ hwb: "hwb(300 0% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "rebeccapurple",
+ name: "rebeccapurple",
+ hex: "#639",
+ hsl: "hsl(270, 50%, 40%)",
+ rgb: "rgb(102, 51, 153)",
+ hwb: "hwb(270 20% 40%)",
+ cycle: 5,
+ },
+ {
+ authored: "red",
+ name: "red",
+ hex: "#f00",
+ hsl: "hsl(0, 100%, 50%)",
+ rgb: "rgb(255, 0, 0)",
+ hwb: "hwb(0 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "rosybrown",
+ name: "rosybrown",
+ hex: "#bc8f8f",
+ hsl: "hsl(0, 25.1%, 64.9%)",
+ rgb: "rgb(188, 143, 143)",
+ hwb: "hwb(0 56.1% 26.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "royalblue",
+ name: "royalblue",
+ hex: "#4169e1",
+ hsl: "hsl(225, 72.7%, 56.9%)",
+ rgb: "rgb(65, 105, 225)",
+ hwb: "hwb(225 25.5% 11.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "saddlebrown",
+ name: "saddlebrown",
+ hex: "#8b4513",
+ hsl: "hsl(25, 75.9%, 31%)",
+ rgb: "rgb(139, 69, 19)",
+ hwb: "hwb(25 7.5% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "salmon",
+ name: "salmon",
+ hex: "#fa8072",
+ hsl: "hsl(6.2, 93.2%, 71.4%)",
+ rgb: "rgb(250, 128, 114)",
+ hwb: "hwb(6.2 44.7% 2%)",
+ cycle: 5,
+ },
+ {
+ authored: "sandybrown",
+ name: "sandybrown",
+ hex: "#f4a460",
+ hsl: "hsl(27.6, 87.1%, 66.7%)",
+ rgb: "rgb(244, 164, 96)",
+ hwb: "hwb(27.6 37.6% 4.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "seagreen",
+ name: "seagreen",
+ hex: "#2e8b57",
+ hsl: "hsl(146.5, 50.3%, 36.3%)",
+ rgb: "rgb(46, 139, 87)",
+ hwb: "hwb(146.5 18% 45.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "seashell",
+ name: "seashell",
+ hex: "#fff5ee",
+ hsl: "hsl(24.7, 100%, 96.7%)",
+ rgb: "rgb(255, 245, 238)",
+ hwb: "hwb(24.7 93.3% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "sienna",
+ name: "sienna",
+ hex: "#a0522d",
+ hsl: "hsl(19.3, 56.1%, 40.2%)",
+ rgb: "rgb(160, 82, 45)",
+ hwb: "hwb(19.3 17.6% 37.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "silver",
+ name: "silver",
+ hex: "#c0c0c0",
+ hsl: "hsl(0, 0%, 75.3%)",
+ rgb: "rgb(192, 192, 192)",
+ hwb: "hwb(0 75.3% 24.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "skyblue",
+ name: "skyblue",
+ hex: "#87ceeb",
+ hsl: "hsl(197.4, 71.4%, 72.5%)",
+ rgb: "rgb(135, 206, 235)",
+ hwb: "hwb(197.4 52.9% 7.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "slateblue",
+ name: "slateblue",
+ hex: "#6a5acd",
+ hsl: "hsl(248.3, 53.5%, 57.8%)",
+ rgb: "rgb(106, 90, 205)",
+ hwb: "hwb(248.3 35.3% 19.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "slategray",
+ name: "slategray",
+ hex: "#708090",
+ hsl: "hsl(210, 12.6%, 50.2%)",
+ rgb: "rgb(112, 128, 144)",
+ hwb: "hwb(210 43.9% 43.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "slategrey",
+ name: "slategray",
+ hex: "#708090",
+ hsl: "hsl(210, 12.6%, 50.2%)",
+ rgb: "rgb(112, 128, 144)",
+ hwb: "hwb(210 43.9% 43.5%)",
+ cycle: 5,
+ },
+ {
+ authored: "snow",
+ name: "snow",
+ hex: "#fffafa",
+ hsl: "hsl(0, 100%, 99%)",
+ rgb: "rgb(255, 250, 250)",
+ hwb: "hwb(0 98% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "springgreen",
+ name: "springgreen",
+ hex: "#00ff7f",
+ hsl: "hsl(149.9, 100%, 50%)",
+ rgb: "rgb(0, 255, 127)",
+ hwb: "hwb(149.9 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "steelblue",
+ name: "steelblue",
+ hex: "#4682b4",
+ hsl: "hsl(207.3, 44%, 49%)",
+ rgb: "rgb(70, 130, 180)",
+ hwb: "hwb(207.3 27.5% 29.4%)",
+ cycle: 5,
+ },
+ {
+ authored: "tan",
+ name: "tan",
+ hex: "#d2b48c",
+ hsl: "hsl(34.3, 43.7%, 68.6%)",
+ rgb: "rgb(210, 180, 140)",
+ hwb: "hwb(34.3 54.9% 17.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "teal",
+ name: "teal",
+ hex: "#008080",
+ hsl: "hsl(180, 100%, 25.1%)",
+ rgb: "rgb(0, 128, 128)",
+ hwb: "hwb(180 0% 49.8%)",
+ cycle: 5,
+ },
+ {
+ authored: "thistle",
+ name: "thistle",
+ hex: "#d8bfd8",
+ hsl: "hsl(300, 24.3%, 79.8%)",
+ rgb: "rgb(216, 191, 216)",
+ hwb: "hwb(300 74.9% 15.3%)",
+ cycle: 5,
+ },
+ {
+ authored: "tomato",
+ name: "tomato",
+ hex: "#ff6347",
+ hsl: "hsl(9.1, 100%, 63.9%)",
+ rgb: "rgb(255, 99, 71)",
+ hwb: "hwb(9.1 27.8% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "turquoise",
+ name: "turquoise",
+ hex: "#40e0d0",
+ hsl: "hsl(174, 72.1%, 56.5%)",
+ rgb: "rgb(64, 224, 208)",
+ hwb: "hwb(174 25.1% 12.2%)",
+ cycle: 5,
+ },
+ {
+ authored: "violet",
+ name: "violet",
+ hex: "#ee82ee",
+ hsl: "hsl(300, 76.1%, 72.2%)",
+ rgb: "rgb(238, 130, 238)",
+ hwb: "hwb(300 51% 6.7%)",
+ cycle: 5,
+ },
+ {
+ authored: "wheat",
+ name: "wheat",
+ hex: "#f5deb3",
+ hsl: "hsl(39.1, 76.7%, 83.1%)",
+ rgb: "rgb(245, 222, 179)",
+ hwb: "hwb(39.1 70.2% 3.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "white",
+ name: "white",
+ hex: "#fff",
+ hsl: "hsl(0, 0%, 100%)",
+ rgb: "rgb(255, 255, 255)",
+ hwb: "hwb(0 100% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "whitesmoke",
+ name: "whitesmoke",
+ hex: "#f5f5f5",
+ hsl: "hsl(0, 0%, 96.1%)",
+ rgb: "rgb(245, 245, 245)",
+ hwb: "hwb(0 96.1% 3.9%)",
+ cycle: 5,
+ },
+ {
+ authored: "yellow",
+ name: "yellow",
+ hex: "#ff0",
+ hsl: "hsl(60, 100%, 50%)",
+ rgb: "rgb(255, 255, 0)",
+ hwb: "hwb(60 0% 0%)",
+ cycle: 5,
+ },
+ {
+ authored: "yellowgreen",
+ name: "yellowgreen",
+ hex: "#9acd32",
+ hsl: "hsl(79.7, 60.8%, 50%)",
+ rgb: "rgb(154, 205, 50)",
+ hwb: "hwb(79.7 19.6% 19.6%)",
+ cycle: 5,
+ },
+ {
+ authored: "rgba(0, 0, 0, 0)",
+ name: "#0000",
+ hex: "#0000",
+ hsl: "hsla(0, 0%, 0%, 0)",
+ rgb: "rgba(0, 0, 0, 0)",
+ hwb: "hwb(0 0% 100% / 0)",
+ cycle: 4,
+ },
+ {
+ authored: "hsla(0, 0%, 0%, 0)",
+ name: "#0000",
+ hex: "#0000",
+ hsl: "hsla(0, 0%, 0%, 0)",
+ rgb: "rgba(0, 0, 0, 0)",
+ hwb: "hwb(0 0% 100% / 0)",
+ cycle: 4,
+ },
+ {
+ authored: "rgba(50, 60, 70, 0.5)",
+ name: "#323c4680",
+ hex: "#323c4680",
+ hsl: "hsla(210, 16.7%, 23.5%, 0.5)",
+ rgb: "rgba(50, 60, 70, 0.5)",
+ hwb: "hwb(210 19.6% 72.5% / 0.5)",
+ cycle: 4,
+ },
+ {
+ authored: "rgba(0, 0, 0, 0.3)",
+ name: "#0000004d",
+ hex: "#0000004d",
+ hsl: "hsla(0, 0%, 0%, 0.3)",
+ rgb: "rgba(0, 0, 0, 0.3)",
+ hwb: "hwb(0 0% 100% / 0.3)",
+ cycle: 4,
+ },
+ {
+ authored: "rgba(255, 255, 255, 0.6)",
+ name: "#fff9",
+ hex: "#fff9",
+ hsl: "hsla(0, 0%, 100%, 0.6)",
+ rgb: "rgba(255, 255, 255, 0.6)",
+ hwb: "hwb(0 100% 0% / 0.6)",
+ cycle: 4,
+ },
+ {
+ authored: "rgba(127, 89, 45, 1)",
+ name: "#7f592d",
+ hex: "#7f592d",
+ hsl: "hsl(32.2, 47.7%, 33.7%)",
+ rgb: "rgb(127, 89, 45)",
+ hwb: "hwb(32.2 17.6% 50.2%)",
+ cycle: 4,
+ },
+ {
+ authored: "hsla(19.304, 56%, 40%, 1)",
+ name: "#9f522d",
+ hex: "#9f522d",
+ hsl: "hsl(19.5, 55.9%, 40%)",
+ rgb: "rgb(159, 82, 45)",
+ hwb: "hwb(19.5 17.6% 37.6%)",
+ cycle: 4,
+ },
+ {
+ authored: "#f089",
+ name: "#f089",
+ hex: "#f089",
+ hsl: "hsla(328, 100%, 50%, 0.6)",
+ rgb: "rgba(255, 0, 136, 0.6)",
+ hwb: "hwb(328 0% 0% / 0.6)",
+ cycle: 4,
+ },
+ {
+ authored: "#00ff8080",
+ name: "#00ff8080",
+ hex: "#00ff8080",
+ hsl: "hsla(150.1, 100%, 50%, 0.5)",
+ rgb: "rgba(0, 255, 128, 0.5)",
+ hwb: "hwb(150.1 0% 0% / 0.5)",
+ cycle: 4,
+ },
+ {
+ authored: "#aaaaaa08",
+ name: "#aaaaaa08",
+ hex: "#aaaaaa08",
+ hsl: "hsla(0, 0%, 66.7%, 0.03)",
+ rgb: "rgba(170, 170, 170, 0.03)",
+ hwb: "hwb(0 66.7% 33.3% / 0.03)",
+ },
+ {
+ authored: "currentcolor",
+ name: "currentcolor",
+ hex: "currentcolor",
+ hsl: "currentcolor",
+ rgb: "currentcolor",
+ hwb: "currentcolor",
+ cycle: false,
+ },
+ {
+ authored: "inherit",
+ name: "inherit",
+ hex: "inherit",
+ hsl: "inherit",
+ rgb: "inherit",
+ hwb: "inherit",
+ cycle: false,
+ },
+ {
+ authored: "initial",
+ name: "initial",
+ hex: "initial",
+ hsl: "initial",
+ rgb: "initial",
+ hwb: "initial",
+ cycle: false,
+ },
+ {
+ authored: "invalidColor",
+ name: "",
+ hex: "",
+ hsl: "",
+ rgb: "",
+ hwb: "",
+ cycle: false,
+ },
+ {
+ authored: "transparent",
+ name: "transparent",
+ hex: "transparent",
+ hsl: "transparent",
+ rgb: "transparent",
+ hwb: "transparent",
+ cycle: false,
+ },
+ {
+ authored: "unset",
+ name: "unset",
+ hex: "unset",
+ hsl: "unset",
+ rgb: "unset",
+ hwb: "unset",
+ cycle: false,
+ },
+ {
+ authored: "currentcolor",
+ name: "currentcolor",
+ hex: "currentcolor",
+ hsl: "currentcolor",
+ rgb: "currentcolor",
+ hwb: "currentcolor",
+ cycle: false,
+ },
+ {
+ authored: "accentcolor",
+ name: "",
+ hex: "",
+ hsl: "",
+ rgb: "",
+ hwg: "",
+ cycle: false,
+ },
+ ];
+}
+/* eslint-enable max-len */
+
+// Allow this function to be shared on mochitests and xpcshell tests.
+if (typeof module === "object") {
+ module.exports = getFixtureColorData;
+}
diff --git a/devtools/client/shared/test/helper_html_tooltip.js b/devtools/client/shared/test/helper_html_tooltip.js
new file mode 100644
index 0000000000..24b28ad702
--- /dev/null
+++ b/devtools/client/shared/test/helper_html_tooltip.js
@@ -0,0 +1,116 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+/* import-globals-from ../../shared/test/shared-head.js */
+
+"use strict";
+
+/**
+ * Helper methods for the HTMLTooltip integration tests.
+ */
+
+/**
+ * Display an existing HTMLTooltip on an anchor and properly wait for the popup to be
+ * repainted.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The tooltip instance to display
+ * @param {Node} anchor
+ * The anchor that should be used to display the tooltip
+ * @param {Object} see HTMLTooltip:show documentation
+ * @return {Promise} promise that resolves when reflow and repaint are done.
+ */
+async function showTooltip(tooltip, anchor, { position, x, y } = {}) {
+ await tooltip.show(anchor, { position, x, y });
+ await waitForReflow(tooltip);
+
+ // Wait for next tick. Tooltip tests sometimes fail to successively hide and
+ // show tooltips on Win32 debug.
+ await waitForTick();
+}
+
+/**
+ * Hide an existing HTMLTooltip. After the tooltip "hidden" event has been fired
+ * a reflow will be triggered.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The tooltip instance to hide
+ * @return {Promise} promise that resolves when "hidden" has been fired, reflow
+ * and repaint done.
+ */
+async function hideTooltip(tooltip) {
+ const onPopupHidden = tooltip.once("hidden");
+ tooltip.hide();
+ await onPopupHidden;
+ await waitForReflow(tooltip);
+
+ // Wait for next tick. Tooltip tests sometimes fail to successively hide and
+ // show tooltips on Win32 debug.
+ await waitForTick();
+}
+
+/**
+ * Forces the reflow of an HTMLTooltip document and waits for the next repaint.
+ *
+ * @param {HTMLTooltip} the tooltip to reflow
+ * @return {Promise} a promise that will resolve after the reflow and repaint
+ * have been executed.
+ */
+function waitForReflow(tooltip) {
+ const { doc } = tooltip;
+ return new Promise(resolve => {
+ doc.documentElement.offsetWidth;
+ doc.defaultView.requestAnimationFrame(resolve);
+ });
+}
+
+/**
+ * Test helper designed to check that a tooltip is displayed at the expected
+ * position relative to an anchor, given a set of expectations.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The HTMLTooltip instance to check
+ * @param {Node} anchor
+ * The tooltip's anchor
+ * @param {Object} expected
+ * - {String} position : "top" or "bottom"
+ * - {Boolean} leftAligned
+ * - {Number} width: expected tooltip width
+ * - {Number} height: expected tooltip height
+ */
+function checkTooltipGeometry(
+ tooltip,
+ anchor,
+ { position, leftAligned = true, height, width } = {}
+) {
+ info("Check the tooltip geometry matches expected position and dimensions");
+ const tooltipRect = tooltip.container.getBoundingClientRect();
+ const anchorRect = anchor.getBoundingClientRect();
+
+ if (position === "top") {
+ is(
+ tooltipRect.bottom,
+ Math.round(anchorRect.top),
+ "Tooltip is above the anchor"
+ );
+ } else if (position === "bottom") {
+ is(
+ tooltipRect.top,
+ Math.round(anchorRect.bottom),
+ "Tooltip is below the anchor"
+ );
+ } else {
+ ok(false, "Invalid position provided to checkTooltipGeometry");
+ }
+
+ if (leftAligned) {
+ is(
+ tooltipRect.left,
+ Math.round(anchorRect.left),
+ "Tooltip left-aligned with the anchor"
+ );
+ }
+
+ is(tooltipRect.height, height, "Tooltip has the expected height");
+ is(tooltipRect.width, width, "Tooltip has the expected width");
+}
diff --git a/devtools/client/shared/test/helper_inplace_editor.js b/devtools/client/shared/test/helper_inplace_editor.js
new file mode 100644
index 0000000000..a7e544f708
--- /dev/null
+++ b/devtools/client/shared/test/helper_inplace_editor.js
@@ -0,0 +1,164 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+/* import-globals-from head.js */
+
+"use strict";
+
+/**
+ * Helper methods for the HTMLTooltip integration tests.
+ */
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const {
+ editableField,
+} = require("resource://devtools/client/shared/inplace-editor.js");
+const { colorUtils } = require("resource://devtools/shared/css/color.js");
+
+/**
+ * Create an inplace editor linked to a span element and click on the span to
+ * to turn to edit mode.
+ *
+ * @param {Object} options
+ * Options passed to the InplaceEditor/editableField constructor.
+ * @param {Document} doc
+ * Document where the span element will be created.
+ * @param {String} textContent
+ * (optional) String that will be used as the text content of the span.
+ */
+const createInplaceEditorAndClick = async function (options, doc, textContent) {
+ const span = (options.element = createSpan(doc));
+ if (textContent) {
+ span.textContent = textContent;
+ }
+
+ info("Creating an inplace-editor field");
+ editableField(options);
+
+ info("Clicking on the inplace-editor field to turn to edit mode");
+ span.click();
+};
+
+/**
+ * Helper to create a span in the provided document.
+ *
+ * @param {Document} doc
+ * Document where the span element will be created.
+ * @return {Element} the created span element.
+ */
+function createSpan(doc) {
+ info("Creating a new span element");
+ const div = doc.createElementNS(HTML_NS, "div");
+ const span = doc.createElementNS(HTML_NS, "span");
+ span.setAttribute("tabindex", "0");
+ span.style.fontSize = "11px";
+ span.style.display = "inline-block";
+ span.style.width = "100px";
+ span.style.border = "1px solid red";
+ span.style.fontFamily = "monospace";
+
+ div.style.height = "100%";
+ div.style.position = "absolute";
+ div.appendChild(span);
+
+ const parent = doc.querySelector("window") || doc.body;
+ parent.appendChild(div);
+ return span;
+}
+
+/**
+ * Test helper simulating a key event in an InplaceEditor and checking that the
+ * autocompletion works as expected.
+ *
+ * @param {Array} testData
+ * - {String} key, the key to send
+ * - {String} completion, the expected value of the auto-completion
+ * - {Number} index, the index of the selected suggestion in the popup
+ * - {Number} total, the total number of suggestions in the popup
+ * - {String} postLabel, the expected post label for the selected suggestion
+ * - {Boolean} colorSwatch, if there is a swatch of color expected to be visible
+ * @param {InplaceEditor} editor
+ * The InplaceEditor instance being tested
+ */
+async function testCompletion(
+ [key, completion, index, total, postLabel, colorSwatch],
+ editor
+) {
+ info("Pressing key " + key);
+ info("Expecting " + completion);
+
+ let onVisibilityChange = null;
+ const open = total > 0;
+ if (editor.popup.isOpen != open) {
+ onVisibilityChange = editor.popup.once(
+ open ? "popup-opened" : "popup-closed"
+ );
+ }
+
+ let onSuggest;
+ if (/(left|right|back_space|escape)/gi.test(key)) {
+ info("Adding event listener for right|back_space|escape keys");
+ onSuggest = once(editor.input, "keypress");
+ } else {
+ info("Waiting for after-suggest event on the editor");
+ onSuggest = editor.once("after-suggest");
+ }
+
+ info("Synthesizing key " + key);
+ EventUtils.synthesizeKey(key, {}, editor.input.defaultView);
+
+ await onSuggest;
+ await onVisibilityChange;
+ await waitForTime(5);
+
+ info("Checking the state");
+ if (completion !== null) {
+ is(editor.input.value, completion, "Correct value is autocompleted");
+ }
+
+ if (postLabel) {
+ const selectedItem = editor.popup.getItems()[index];
+ const selectedElement = editor.popup.elements.get(selectedItem);
+ ok(
+ selectedElement.textContent.includes(postLabel),
+ "Selected popup element contains the expected post-label"
+ );
+
+ // Determines if there is a color swatch attached to the label
+ // and if the color swatch's background color matches the post label
+ const swatchSpan = selectedElement.getElementsByClassName(
+ "autocomplete-swatch autocomplete-colorswatch"
+ );
+ if (colorSwatch) {
+ Assert.strictEqual(
+ swatchSpan.length,
+ 1,
+ "Displayed the expected color swatch"
+ );
+ const color = new colorUtils.CssColor(
+ swatchSpan[0].style.backgroundColor
+ );
+ const swatchColor = color.rgba;
+ const postColor = new colorUtils.CssColor(postLabel).rgba;
+ Assert.equal(
+ swatchColor,
+ postColor,
+ "Color swatch matches postLabel value"
+ );
+ } else {
+ Assert.strictEqual(
+ swatchSpan.length,
+ 0,
+ "As expected no swatches were available"
+ );
+ }
+ }
+
+ if (total === 0) {
+ ok(!(editor.popup && editor.popup.isOpen), "Popup is closed");
+ } else {
+ ok(editor.popup.isOpen, "Popup is open");
+ is(editor.popup.getItems().length, total, "Number of suggestions match");
+ is(editor.popup.selectedIndex, index, "Expected item is selected");
+ }
+}
diff --git a/devtools/client/shared/test/highlighter-test-actor.js b/devtools/client/shared/test/highlighter-test-actor.js
new file mode 100644
index 0000000000..8a7b404df1
--- /dev/null
+++ b/devtools/client/shared/test/highlighter-test-actor.js
@@ -0,0 +1,939 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* exported HighlighterTestActor, HighlighterTestFront */
+
+"use strict";
+
+// A helper actor for testing highlighters.
+// ⚠️ This should only be used for getting data for objects using CanvasFrameAnonymousContentHelper,
+// that we can't get directly from tests.
+const {
+ getRect,
+ getAdjustedQuads,
+} = require("resource://devtools/shared/layout/utils.js");
+
+// Set up a dummy environment so that EventUtils works. We need to be careful to
+// pass a window object into each EventUtils method we call rather than having
+// it rely on the |window| global.
+const EventUtils = {};
+EventUtils.window = {};
+EventUtils.parent = {};
+/* eslint-disable camelcase */
+EventUtils._EU_Ci = Ci;
+EventUtils._EU_Cc = Cc;
+/* eslint-disable camelcase */
+Services.scriptloader.loadSubScript(
+ "chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
+ EventUtils
+);
+
+// We're an actor so we don't run in the browser test environment, so
+// we need to import TestUtils manually despite what the linter thinks.
+// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
+const { TestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+const protocol = require("resource://devtools/shared/protocol.js");
+const { Arg, RetVal } = protocol;
+
+const dumpn = msg => {
+ dump(msg + "\n");
+};
+
+/**
+ * Get the instance of CanvasFrameAnonymousContentHelper used by a given
+ * highlighter actor.
+ * The instance provides methods to get/set attributes/text/style on nodes of
+ * the highlighter, inserted into the nsCanvasFrame.
+ * @see /devtools/server/actors/highlighters.js
+ * @param {String} actorID
+ */
+function getHighlighterCanvasFrameHelper(conn, actorID) {
+ // Retrieve the CustomHighlighterActor by its actorID:
+ const actor = conn.getActor(actorID);
+ if (!actor) {
+ return null;
+ }
+
+ // Retrieve the sub class instance specific to each highlighter type:
+ let highlighter = actor.instance;
+
+ // SelectorHighlighter and TabbingOrderHighlighter can hold multiple highlighters.
+ // For now, only retrieve the first highlighter.
+ if (
+ highlighter._highlighters &&
+ Array.isArray(highlighter._highlighters) &&
+ highlighter._highlighters.length
+ ) {
+ highlighter = highlighter._highlighters[0];
+ }
+
+ // Now, `highlighter` should be a final highlighter class, exposing
+ // `CanvasFrameAnonymousContentHelper` via a `markup` attribute.
+ if (highlighter.markup) {
+ return highlighter.markup;
+ }
+
+ // Here we didn't find any highlighter; it can happen if the actor is a
+ // FontsHighlighter (which does not use a CanvasFrameAnonymousContentHelper).
+ return null;
+}
+
+var highlighterTestSpec = protocol.generateActorSpec({
+ typeName: "highlighterTest",
+
+ events: {
+ "highlighter-updated": {},
+ },
+
+ methods: {
+ getHighlighterAttribute: {
+ request: {
+ nodeID: Arg(0, "string"),
+ name: Arg(1, "string"),
+ actorID: Arg(2, "string"),
+ },
+ response: {
+ value: RetVal("string"),
+ },
+ },
+ getHighlighterBoundingClientRect: {
+ request: {
+ nodeID: Arg(0, "string"),
+ actorID: Arg(1, "string"),
+ },
+ response: {
+ value: RetVal("json"),
+ },
+ },
+ getHighlighterComputedStyle: {
+ request: {
+ nodeID: Arg(0, "string"),
+ property: Arg(1, "string"),
+ actorID: Arg(2, "string"),
+ },
+ response: {
+ value: RetVal("string"),
+ },
+ },
+ getHighlighterNodeTextContent: {
+ request: {
+ nodeID: Arg(0, "string"),
+ actorID: Arg(1, "string"),
+ },
+ response: {
+ value: RetVal("string"),
+ },
+ },
+ getSelectorHighlighterBoxNb: {
+ request: {
+ highlighter: Arg(0, "string"),
+ },
+ response: {
+ value: RetVal("number"),
+ },
+ },
+ changeHighlightedNodeWaitForUpdate: {
+ request: {
+ name: Arg(0, "string"),
+ value: Arg(1, "string"),
+ actorID: Arg(2, "string"),
+ },
+ response: {},
+ },
+ registerOneTimeHighlighterUpdate: {
+ request: {
+ actorID: Arg(0, "string"),
+ },
+ response: {},
+ },
+ getNodeRect: {
+ request: {
+ selector: Arg(0, "string"),
+ },
+ response: {
+ value: RetVal("json"),
+ },
+ },
+ getTextNodeRect: {
+ request: {
+ parentSelector: Arg(0, "string"),
+ childNodeIndex: Arg(1, "number"),
+ },
+ response: {
+ value: RetVal("json"),
+ },
+ },
+ isPausedDebuggerOverlayVisible: {
+ request: {},
+ response: {
+ value: RetVal("boolean"),
+ },
+ },
+ clickPausedDebuggerOverlayButton: {
+ request: {
+ id: Arg(0, "string"),
+ },
+ response: {},
+ },
+ isEyeDropperVisible: {
+ request: {},
+ response: {
+ value: RetVal("boolean"),
+ },
+ },
+ getEyeDropperElementAttribute: {
+ request: {
+ elementId: Arg(0, "string"),
+ attributeName: Arg(1, "string"),
+ },
+ response: {
+ value: RetVal("string"),
+ },
+ },
+ getEyeDropperColorValue: {
+ request: {},
+ response: {
+ value: RetVal("string"),
+ },
+ },
+ getTabbingOrderHighlighterData: {
+ request: {},
+ response: {
+ value: RetVal("json"),
+ },
+ },
+ },
+});
+
+class HighlighterTestActor extends protocol.Actor {
+ constructor(conn, targetActor, options) {
+ super(conn, highlighterTestSpec);
+
+ this.targetActor = targetActor;
+ }
+
+ get content() {
+ return this.targetActor.window;
+ }
+
+ /**
+ * Helper to retrieve a DOM element.
+ * @param {string | array} selector Either a regular selector string
+ * or a selector array. If an array, each item, except the last one
+ * are considered matching an iframe, so that we can query element
+ * within deep iframes.
+ */
+ _querySelector(selector) {
+ let document = this.content.document;
+ if (Array.isArray(selector)) {
+ const fullSelector = selector.join(" >> ");
+ while (selector.length > 1) {
+ const str = selector.shift();
+ const iframe = document.querySelector(str);
+ if (!iframe) {
+ throw new Error(
+ 'Unable to find element with selector "' +
+ str +
+ '"' +
+ " (full selector:" +
+ fullSelector +
+ ")"
+ );
+ }
+ if (!iframe.contentWindow) {
+ throw new Error(
+ "Iframe selector doesn't target an iframe \"" +
+ str +
+ '"' +
+ " (full selector:" +
+ fullSelector +
+ ")"
+ );
+ }
+ document = iframe.contentWindow.document;
+ }
+ selector = selector.shift();
+ }
+ const node = document.querySelector(selector);
+ if (!node) {
+ throw new Error(
+ 'Unable to find element with selector "' + selector + '"'
+ );
+ }
+ return node;
+ }
+
+ /**
+ * Get a value for a given attribute name, on one of the elements of the box
+ * model highlighter, given its ID.
+ * @param {String} nodeID The full ID of the element to get the attribute for
+ * @param {String} name The name of the attribute to get
+ * @param {String} actorID The highlighter actor ID
+ * @return {String} The value, if found, null otherwise
+ */
+ getHighlighterAttribute(nodeID, name, actorID) {
+ const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);
+
+ if (!helper) {
+ throw new Error(`Highlighter not found`);
+ }
+
+ return helper.getAttributeForElement(nodeID, name);
+ }
+
+ /**
+ * Get the bounding client rect for an highlighter element, given its ID.
+ *
+ * @param {String} nodeID The full ID of the element to get the DOMRect for
+ * @param {String} actorID The highlighter actor ID
+ * @return {DOMRect} The value, if found, null otherwise
+ */
+ getHighlighterBoundingClientRect(nodeID, actorID) {
+ const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);
+
+ if (!helper) {
+ throw new Error(`Highlighter not found`);
+ }
+
+ return helper.getBoundingClientRect(nodeID);
+ }
+
+ /**
+ * Get the computed style for a given property, on one of the elements of the
+ * box model highlighter, given its ID.
+ * @param {String} nodeID The full ID of the element to get the attribute for
+ * @param {String} property The name of the property
+ * @param {String} actorID The highlighter actor ID
+ * @return {String} The computed style of the property
+ */
+ getHighlighterComputedStyle(nodeID, property, actorID) {
+ const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);
+
+ if (!helper) {
+ throw new Error(`Highlighter not found`);
+ }
+
+ return helper.getElement(nodeID).computedStyle.getPropertyValue(property);
+ }
+
+ /**
+ * Get the textcontent of one of the elements of the box model highlighter,
+ * given its ID.
+ * @param {String} nodeID The full ID of the element to get the attribute for
+ * @param {String} actorID The highlighter actor ID
+ * @return {String} The textcontent value
+ */
+ getHighlighterNodeTextContent(nodeID, actorID) {
+ let value;
+ const helper = getHighlighterCanvasFrameHelper(this.conn, actorID);
+ if (helper) {
+ value = helper.getTextContentForElement(nodeID);
+ }
+ return value;
+ }
+
+ /**
+ * Get the number of box-model highlighters created by the SelectorHighlighter
+ * @param {String} actorID The highlighter actor ID
+ * @return {Number} The number of box-model highlighters created, or null if the
+ * SelectorHighlighter was not found.
+ */
+ getSelectorHighlighterBoxNb(actorID) {
+ const highlighter = this.conn.getActor(actorID);
+ const { _highlighter: h } = highlighter;
+ if (!h || !h._highlighters) {
+ return null;
+ }
+ return h._highlighters.length;
+ }
+
+ /**
+ * Subscribe to the box-model highlighter's update event, modify an attribute of
+ * the currently highlighted node and send a message when the highlighter has
+ * updated.
+ * @param {String} the name of the attribute to be changed
+ * @param {String} the new value for the attribute
+ * @param {String} actorID The highlighter actor ID
+ */
+ changeHighlightedNodeWaitForUpdate(name, value, actorID) {
+ return new Promise(resolve => {
+ const highlighter = this.conn.getActor(actorID);
+ const { _highlighter: h } = highlighter;
+
+ h.once("updated", resolve);
+
+ h.currentNode.setAttribute(name, value);
+ });
+ }
+
+ /**
+ * Register a one-time "updated" event listener.
+ * The method does not wait for the "updated" event itself so the response can be sent
+ * back and the client would know the event listener is properly set.
+ * A separate event, "highlighter-updated", will be emitted when the highlighter updates.
+ *
+ * @param {String} actorID The highlighter actor ID
+ */
+ registerOneTimeHighlighterUpdate(actorID) {
+ const { _highlighter } = this.conn.getActor(actorID);
+ _highlighter.once("updated").then(() => this.emit("highlighter-updated"));
+
+ // Return directly so the client knows the event listener is set
+ }
+
+ async getNodeRect(selector) {
+ const node = this._querySelector(selector);
+ return getRect(this.content, node, this.content);
+ }
+
+ async getTextNodeRect(parentSelector, childNodeIndex) {
+ const parentNode = this._querySelector(parentSelector);
+ const node = parentNode.childNodes[childNodeIndex];
+ return getAdjustedQuads(this.content, node)[0].bounds;
+ }
+
+ /**
+ * @returns {PausedDebuggerOverlay} The paused overlay instance
+ */
+ _getPausedDebuggerOverlay() {
+ // We use `_pauseOverlay` since it's the cached value; `pauseOverlay` is a getter that
+ // will create the overlay when called (if it does not exist yet).
+ return this.targetActor?.threadActor?._pauseOverlay;
+ }
+
+ isPausedDebuggerOverlayVisible() {
+ const pauseOverlay = this._getPausedDebuggerOverlay();
+ if (!pauseOverlay) {
+ return false;
+ }
+
+ const root = pauseOverlay.getElement("root");
+ const toolbar = pauseOverlay.getElement("toolbar");
+
+ return (
+ root.getAttribute("hidden") !== "true" &&
+ root.getAttribute("overlay") == "true" &&
+ toolbar.getAttribute("hidden") !== "true" &&
+ !!toolbar.getTextContent()
+ );
+ }
+
+ /**
+ * Simulates a click on a button of the debugger pause overlay.
+ *
+ * @param {String} id: The id of the element (e.g. "paused-dbg-resume-button").
+ */
+ async clickPausedDebuggerOverlayButton(id) {
+ const pauseOverlay = this._getPausedDebuggerOverlay();
+ if (!pauseOverlay) {
+ return;
+ }
+
+ // Because the highlighter markup elements live inside an anonymous content frame which
+ // does not expose an API to dispatch events to them, we can't directly dispatch
+ // events to the nodes themselves.
+ // We're directly calling `handleEvent` on the pause overlay, which is the mouse events
+ // listener callback on the overlay.
+ pauseOverlay.handleEvent({ type: "mousedown", target: { id } });
+ }
+
+ /**
+ * @returns {EyeDropper}
+ */
+ _getEyeDropper() {
+ const form = this.targetActor.form();
+ const inspectorActor = this.conn._getOrCreateActor(form.inspectorActor);
+ return inspectorActor?._eyeDropper;
+ }
+
+ isEyeDropperVisible() {
+ const eyeDropper = this._getEyeDropper();
+ if (!eyeDropper) {
+ return false;
+ }
+
+ return eyeDropper.getElement("root").getAttribute("hidden") !== "true";
+ }
+
+ getEyeDropperElementAttribute(elementId, attributeName) {
+ const eyeDropper = this._getEyeDropper();
+ if (!eyeDropper) {
+ return null;
+ }
+
+ return eyeDropper.getElement(elementId).getAttribute(attributeName);
+ }
+
+ async getEyeDropperColorValue() {
+ const eyeDropper = this._getEyeDropper();
+ if (!eyeDropper) {
+ return null;
+ }
+
+ // It might happen that while the eyedropper isn't hidden anymore, the color-value
+ // is not set yet.
+ const color = await TestUtils.waitForCondition(() => {
+ const colorValueElement = eyeDropper.getElement("color-value");
+ const textContent = colorValueElement.getTextContent();
+ return textContent;
+ }, "Couldn't get a non-empty text content for the color-value element");
+
+ return color;
+ }
+
+ /**
+ * Get the TabbingOrderHighlighter for the associated targetActor
+ *
+ * @returns {TabbingOrderHighlighter}
+ */
+ _getTabbingOrderHighlighter() {
+ const form = this.targetActor.form();
+ const accessibilityActor = this.conn._getOrCreateActor(
+ form.accessibilityActor
+ );
+
+ if (!accessibilityActor) {
+ return null;
+ }
+ // We use `_tabbingOrderHighlighter` since it's the cached value; `tabbingOrderHighlighter`
+ // is a getter that will create the highlighter when called (if it does not exist yet).
+ return accessibilityActor.walker?._tabbingOrderHighlighter;
+ }
+
+ /**
+ * Get a representation of the NodeTabbingOrderHighlighters created by the
+ * TabbingOrderHighlighter of a given targetActor.
+ *
+ * @returns {Array<String>} An array which will contain as many entry as they are
+ * NodeTabbingOrderHighlighters displayed.
+ * Each item will be of the form `nodename[#id]: index`.
+ * For example:
+ * [
+ * `button#top-btn-1 : 1`,
+ * `html : 2`,
+ * `button#iframe-btn-1 : 3`,
+ * `button#iframe-btn-2 : 4`,
+ * `button#top-btn-2 : 5`,
+ * ]
+ */
+ getTabbingOrderHighlighterData() {
+ const highlighter = this._getTabbingOrderHighlighter();
+ if (!highlighter) {
+ return [];
+ }
+
+ const nodeTabbingOrderHighlighters = [
+ ...highlighter._highlighter._highlighters.values(),
+ ].filter(h => h.getElement("root").getAttribute("hidden") !== "true");
+
+ return nodeTabbingOrderHighlighters.map(h => {
+ let nodeStr = h.currentNode.nodeName.toLowerCase();
+ if (h.currentNode.id) {
+ nodeStr = `${nodeStr}#${h.currentNode.id}`;
+ }
+ return `${nodeStr} : ${h.getElement("root").getTextContent()}`;
+ });
+ }
+}
+exports.HighlighterTestActor = HighlighterTestActor;
+
+class HighlighterTestFront extends protocol.FrontClassWithSpec(
+ highlighterTestSpec
+) {
+ constructor(client, targetFront, parentFront) {
+ super(client, targetFront, parentFront);
+ this.formAttributeName = "highlighterTestActor";
+ // The currently active highlighter is obtained by calling a custom getter
+ // provided manually after requesting TestFront. See `getHighlighterTestFront(toolbox)`
+ this._highlighter = null;
+ }
+
+ /**
+ * Override the highlighter getter with a custom method that returns
+ * the currently active highlighter instance.
+ *
+ * @param {Function|Highlighter} _customHighlighterGetter
+ */
+ set highlighter(_customHighlighterGetter) {
+ this._highlighter = _customHighlighterGetter;
+ }
+
+ /**
+ * The currently active highlighter instance.
+ * If there is a custom getter for the highlighter, return its result.
+ *
+ * @return {Highlighter|null}
+ */
+ get highlighter() {
+ return typeof this._highlighter === "function"
+ ? this._highlighter()
+ : this._highlighter;
+ }
+
+ /* eslint-disable max-len */
+ changeHighlightedNodeWaitForUpdate(name, value, highlighter) {
+ /* eslint-enable max-len */
+ return super.changeHighlightedNodeWaitForUpdate(
+ name,
+ value,
+ (highlighter || this.highlighter).actorID
+ );
+ }
+
+ /**
+ * Get the value of an attribute on one of the highlighter's node.
+ * @param {String} nodeID The Id of the node in the highlighter.
+ * @param {String} name The name of the attribute.
+ * @param {Object} highlighter Optional custom highlighter to target
+ * @return {String} value
+ */
+ getHighlighterNodeAttribute(nodeID, name, highlighter) {
+ return this.getHighlighterAttribute(
+ nodeID,
+ name,
+ (highlighter || this.highlighter).actorID
+ );
+ }
+
+ getHighlighterNodeTextContent(nodeID, highlighter) {
+ return super.getHighlighterNodeTextContent(
+ nodeID,
+ (highlighter || this.highlighter).actorID
+ );
+ }
+
+ /**
+ * Get the computed style of a property on one of the highlighter's node.
+ * @param {String} nodeID The Id of the node in the highlighter.
+ * @param {String} property The name of the property.
+ * @param {Object} highlighter Optional custom highlighter to target
+ * @return {String} value
+ */
+ getHighlighterComputedStyle(nodeID, property, highlighter) {
+ return super.getHighlighterComputedStyle(
+ nodeID,
+ property,
+ (highlighter || this.highlighter).actorID
+ );
+ }
+
+ /**
+ * Is the highlighter currently visible on the page?
+ */
+ async isHighlighting() {
+ // Once the highlighter is hidden, the reference to it is lost.
+ // Assume it is not highlighting.
+ if (!this.highlighter) {
+ return false;
+ }
+
+ try {
+ const hidden = await this.getHighlighterNodeAttribute(
+ "box-model-elements",
+ "hidden"
+ );
+ return hidden === null;
+ } catch (e) {
+ if (e.message.match(/Highlighter not found/)) {
+ return false;
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Get the current rect of the border region of the box-model highlighter
+ */
+ async getSimpleBorderRect() {
+ const { border } = await this.getBoxModelStatus();
+ const { p1, p2, p4 } = border.points;
+
+ return {
+ top: p1.y,
+ left: p1.x,
+ width: p2.x - p1.x,
+ height: p4.y - p1.y,
+ };
+ }
+
+ /**
+ * Get the current positions and visibility of the various box-model highlighter
+ * elements.
+ */
+ async getBoxModelStatus() {
+ const isVisible = await this.isHighlighting();
+
+ const ret = {
+ visible: isVisible,
+ };
+
+ for (const region of ["margin", "border", "padding", "content"]) {
+ const points = await this._getPointsForRegion(region);
+ const visible = await this._isRegionHidden(region);
+ ret[region] = { points, visible };
+ }
+
+ ret.guides = {};
+ for (const guide of ["top", "right", "bottom", "left"]) {
+ ret.guides[guide] = await this._getGuideStatus(guide);
+ }
+
+ return ret;
+ }
+
+ /**
+ * Check that the box-model highlighter is currently highlighting the node matching the
+ * given selector.
+ * @param {String} selector
+ * @return {Boolean}
+ */
+ async assertHighlightedNode(selector) {
+ const rect = await this.getNodeRect(selector);
+ return this.isNodeRectHighlighted(rect);
+ }
+
+ /**
+ * Check that the box-model highlighter is currently highlighting the text node that can
+ * be found at a given index within the list of childNodes of a parent element matching
+ * the given selector.
+ * @param {String} parentSelector
+ * @param {Number} childNodeIndex
+ * @return {Boolean}
+ */
+ async assertHighlightedTextNode(parentSelector, childNodeIndex) {
+ const rect = await this.getTextNodeRect(parentSelector, childNodeIndex);
+ return this.isNodeRectHighlighted(rect);
+ }
+
+ /**
+ * Check that the box-model highlighter is currently highlighting the given rect.
+ * @param {Object} rect
+ * @return {Boolean}
+ */
+ async isNodeRectHighlighted({ left, top, width, height }) {
+ const { visible, border } = await this.getBoxModelStatus();
+ let points = border.points;
+ if (!visible) {
+ return false;
+ }
+
+ // Check that the node is within the box model
+ const right = left + width;
+ const bottom = top + height;
+
+ // Converts points dictionnary into an array
+ const list = [];
+ for (let i = 1; i <= 4; i++) {
+ const p = points["p" + i];
+ list.push([p.x, p.y]);
+ }
+ points = list;
+
+ // Check that each point of the node is within the box model
+ return (
+ isInside([left, top], points) &&
+ isInside([right, top], points) &&
+ isInside([right, bottom], points) &&
+ isInside([left, bottom], points)
+ );
+ }
+
+ /**
+ * Get the coordinate (points attribute) from one of the polygon elements in the
+ * box model highlighter.
+ */
+ async _getPointsForRegion(region) {
+ const d = await this.getHighlighterNodeAttribute(
+ "box-model-" + region,
+ "d"
+ );
+
+ if (!d) {
+ return null;
+ }
+
+ const polygons = d.match(/M[^M]+/g);
+ if (!polygons) {
+ return null;
+ }
+
+ const points = polygons[0]
+ .trim()
+ .split(" ")
+ .map(i => {
+ return i.replace(/M|L/, "").split(",");
+ });
+
+ return {
+ p1: {
+ x: parseFloat(points[0][0]),
+ y: parseFloat(points[0][1]),
+ },
+ p2: {
+ x: parseFloat(points[1][0]),
+ y: parseFloat(points[1][1]),
+ },
+ p3: {
+ x: parseFloat(points[2][0]),
+ y: parseFloat(points[2][1]),
+ },
+ p4: {
+ x: parseFloat(points[3][0]),
+ y: parseFloat(points[3][1]),
+ },
+ };
+ }
+
+ /**
+ * Is a given region polygon element of the box-model highlighter currently
+ * hidden?
+ */
+ async _isRegionHidden(region) {
+ const value = await this.getHighlighterNodeAttribute(
+ "box-model-" + region,
+ "hidden"
+ );
+ return value !== null;
+ }
+
+ async _getGuideStatus(location) {
+ const id = "box-model-guide-" + location;
+
+ const hidden = await this.getHighlighterNodeAttribute(id, "hidden");
+ const x1 = await this.getHighlighterNodeAttribute(id, "x1");
+ const y1 = await this.getHighlighterNodeAttribute(id, "y1");
+ const x2 = await this.getHighlighterNodeAttribute(id, "x2");
+ const y2 = await this.getHighlighterNodeAttribute(id, "y2");
+
+ return {
+ visible: !hidden,
+ x1,
+ y1,
+ x2,
+ y2,
+ };
+ }
+
+ /**
+ * Get the coordinates of the rectangle that is defined by the 4 guides displayed
+ * in the toolbox box-model highlighter.
+ * @return {Object} Null if at least one guide is hidden. Otherwise an object
+ * with p1, p2, p3, p4 properties being {x, y} objects.
+ */
+ async getGuidesRectangle() {
+ const tGuide = await this._getGuideStatus("top");
+ const rGuide = await this._getGuideStatus("right");
+ const bGuide = await this._getGuideStatus("bottom");
+ const lGuide = await this._getGuideStatus("left");
+
+ if (
+ !tGuide.visible ||
+ !rGuide.visible ||
+ !bGuide.visible ||
+ !lGuide.visible
+ ) {
+ return null;
+ }
+
+ return {
+ p1: { x: lGuide.x1, y: tGuide.y1 },
+ p2: { x: +rGuide.x1 + 1, y: tGuide.y1 },
+ p3: { x: +rGuide.x1 + 1, y: +bGuide.y1 + 1 },
+ p4: { x: lGuide.x1, y: +bGuide.y1 + 1 },
+ };
+ }
+
+ /**
+ * Get the "d" attribute value for one of the box-model highlighter's region
+ * <path> elements, and parse it to a list of points.
+ * @param {String} region The box model region name.
+ * @param {Front} highlighter The front of the highlighter.
+ * @return {Object} The object returned has the following form:
+ * - d {String} the d attribute value
+ * - points {Array} an array of all the polygons defined by the path. Each box
+ * is itself an Array of points, themselves being [x,y] coordinates arrays.
+ */
+ async getHighlighterRegionPath(region, highlighter) {
+ const d = await this.getHighlighterNodeAttribute(
+ `box-model-${region}`,
+ "d",
+ highlighter
+ );
+ if (!d) {
+ return { d: null };
+ }
+
+ const polygons = d.match(/M[^M]+/g);
+ if (!polygons) {
+ return { d };
+ }
+
+ const points = [];
+ for (const polygon of polygons) {
+ points.push(
+ polygon
+ .trim()
+ .split(" ")
+ .map(i => {
+ return i.replace(/M|L/, "").split(",");
+ })
+ );
+ }
+
+ return { d, points };
+ }
+}
+protocol.registerFront(HighlighterTestFront);
+/**
+ * Check whether a point is included in a polygon.
+ * Taken and tweaked from:
+ * https://github.com/iominh/point-in-polygon-extended/blob/master/src/index.js#L30-L85
+ * @param {Array} point [x,y] coordinates
+ * @param {Array} polygon An array of [x,y] points
+ * @return {Boolean}
+ */
+function isInside(point, polygon) {
+ if (polygon.length === 0) {
+ return false;
+ }
+
+ // Reduce the length of the fractional part because this is likely to cause errors when
+ // the point is on the edge of the polygon.
+ point = point.map(n => n.toFixed(2));
+ polygon = polygon.map(p => p.map(n => n.toFixed(2)));
+
+ const n = polygon.length;
+ const newPoints = polygon.slice(0);
+ newPoints.push(polygon[0]);
+ let wn = 0;
+
+ // loop through all edges of the polygon
+ for (let i = 0; i < n; i++) {
+ // Accept points on the edges
+ const r = isLeft(newPoints[i], newPoints[i + 1], point);
+ if (r === 0) {
+ return true;
+ }
+ if (newPoints[i][1] <= point[1]) {
+ if (newPoints[i + 1][1] > point[1] && r > 0) {
+ wn++;
+ }
+ } else if (newPoints[i + 1][1] <= point[1] && r < 0) {
+ wn--;
+ }
+ }
+ if (wn === 0) {
+ dumpn(JSON.stringify(point) + " is outside of " + JSON.stringify(polygon));
+ }
+ // the point is outside only when this winding number wn===0, otherwise it's inside
+ return wn !== 0;
+}
+
+function isLeft(p0, p1, p2) {
+ const l =
+ (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1]);
+ return l;
+}
diff --git a/devtools/client/shared/test/leakhunt.js b/devtools/client/shared/test/leakhunt.js
new file mode 100644
index 0000000000..40b5fb6792
--- /dev/null
+++ b/devtools/client/shared/test/leakhunt.js
@@ -0,0 +1,173 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Memory leak hunter. Walks a tree of objects looking for DOM nodes.
+ * Usage:
+ * leakHunt({
+ * thing: thing,
+ * otherthing: otherthing
+ * });
+ */
+function leakHunt(root) {
+ const path = [];
+ const seen = [];
+
+ try {
+ const output = leakHunt.inner(root, path, seen);
+ output.forEach(function (line) {
+ dump(line + "\n");
+ });
+ } catch (ex) {
+ dump(ex + "\n");
+ }
+}
+
+leakHunt.inner = function (root, path, seen) {
+ const prefix = new Array(path.length).join(" ");
+
+ const reply = [];
+ function log(msg) {
+ reply.push(msg);
+ }
+
+ let direct;
+ try {
+ direct = Object.keys(root);
+ } catch (ex) {
+ log(prefix + " Error enumerating: " + ex);
+ return reply;
+ }
+
+ try {
+ let index = 0;
+ for (const data of root) {
+ const prop = "" + index;
+ leakHunt.digProperty(prop, data, path, seen, direct, log);
+ index++;
+ }
+ } catch (ex) {
+ /* Ignore things that are not enumerable */
+ }
+
+ for (const prop in root) {
+ let data;
+ try {
+ data = root[prop];
+ } catch (ex) {
+ log(prefix + " " + prop + " = Error: " + ex.toString().substring(0, 30));
+ continue;
+ }
+
+ leakHunt.digProperty(prop, data, path, seen, direct, log);
+ }
+
+ return reply;
+};
+
+leakHunt.hide = [/^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/];
+
+leakHunt.noRecurse = [
+ /^string$/,
+ /^number$/,
+ /^boolean$/,
+ /^null/,
+ /^undefined/,
+ /^Window$/,
+ /^Document$/,
+ /^XULElement$/,
+ /^DOMWindow$/,
+ /^HTMLDocument$/,
+ /^HTML.*Element$/,
+ /^ChromeWindow$/,
+];
+
+leakHunt.digProperty = function (prop, data, path, seen, direct, log) {
+ const newPath = path.slice();
+ newPath.push(prop);
+ const prefix = new Array(newPath.length).join(" ");
+
+ let recurse = true;
+ let message = leakHunt.getType(data);
+
+ if (leakHunt.matchesAnyPattern(message, leakHunt.hide)) {
+ return;
+ }
+
+ if (message === "function" && !direct.includes(prop)) {
+ return;
+ }
+
+ if (message === "string") {
+ const extra = data.length > 10 ? data.substring(0, 9) + "_" : data;
+ message += ' "' + extra.replace(/\n/g, "|") + '"';
+ recurse = false;
+ } else if (leakHunt.matchesAnyPattern(message, leakHunt.noRecurse)) {
+ message += " (no recurse)";
+ recurse = false;
+ } else if (seen.includes(data)) {
+ message += " (already seen)";
+ recurse = false;
+ }
+
+ if (recurse) {
+ seen.push(data);
+ const lines = leakHunt.inner(data, newPath, seen);
+ if (!lines.length) {
+ if (message !== "function") {
+ log(prefix + prop + " = " + message + " { }");
+ }
+ } else {
+ log(prefix + prop + " = " + message + " {");
+ lines.forEach(function (line) {
+ log(line);
+ });
+ log(prefix + "}");
+ }
+ } else {
+ log(prefix + prop + " = " + message);
+ }
+};
+
+leakHunt.matchesAnyPattern = function (str, patterns) {
+ let match = false;
+ patterns.forEach(function (pattern) {
+ if (str.match(pattern)) {
+ match = true;
+ }
+ });
+ return match;
+};
+
+leakHunt.getType = function (data) {
+ if (data === null) {
+ return "null";
+ }
+ if (data === undefined) {
+ return "undefined";
+ }
+
+ let type = typeof data;
+ if (type === "object" || type === "Object") {
+ type = leakHunt.getCtorName(data);
+ }
+
+ return type;
+};
+
+leakHunt.getCtorName = function (obj) {
+ try {
+ if (obj.constructor && obj.constructor.name) {
+ return obj.constructor.name;
+ }
+ } catch (ex) {
+ return "UnknownObject";
+ }
+
+ // If that fails, use Objects toString which sometimes gives something
+ // better than 'Object', and at least defaults to Object if nothing better
+ return Object.prototype.toString.call(obj).slice(8, -1);
+};
diff --git a/devtools/client/shared/test/shared-head.js b/devtools/client/shared/test/shared-head.js
new file mode 100644
index 0000000000..2c8df188a6
--- /dev/null
+++ b/devtools/client/shared/test/shared-head.js
@@ -0,0 +1,2324 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local"}] */
+
+/* import-globals-from ../../inspector/test/shared-head.js */
+
+"use strict";
+
+// This shared-head.js file is used by most mochitests
+// and we start using it in xpcshell tests as well.
+// It contains various common helper functions.
+
+const isMochitest = "gTestPath" in this;
+const isXpcshell = !isMochitest;
+if (isXpcshell) {
+ // gTestPath isn't exposed to xpcshell tests
+ // _TEST_FILE is an array for a unique string
+ /* global _TEST_FILE */
+ this.gTestPath = _TEST_FILE[0];
+}
+
+const { Constructor: CC } = Components;
+
+// Print allocation count if DEBUG_DEVTOOLS_ALLOCATIONS is set to "normal",
+// and allocation sites if DEBUG_DEVTOOLS_ALLOCATIONS is set to "verbose".
+const DEBUG_ALLOCATIONS = Services.env.get("DEBUG_DEVTOOLS_ALLOCATIONS");
+if (DEBUG_ALLOCATIONS) {
+ // Use a custom loader with `invisibleToDebugger` flag for the allocation tracker
+ // as it instantiates custom Debugger API instances and has to be running in a distinct
+ // compartments from DevTools and system scopes (JSMs, XPCOM,...)
+ const {
+ useDistinctSystemPrincipalLoader,
+ releaseDistinctSystemPrincipalLoader,
+ } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/DistinctSystemPrincipalLoader.sys.mjs"
+ );
+ const requester = {};
+ const loader = useDistinctSystemPrincipalLoader(requester);
+ registerCleanupFunction(() =>
+ releaseDistinctSystemPrincipalLoader(requester)
+ );
+
+ const { allocationTracker } = loader.require(
+ "resource://devtools/shared/test-helpers/allocation-tracker.js"
+ );
+ const tracker = allocationTracker({ watchAllGlobals: true });
+ registerCleanupFunction(() => {
+ if (DEBUG_ALLOCATIONS == "normal") {
+ tracker.logCount();
+ } else if (DEBUG_ALLOCATIONS == "verbose") {
+ tracker.logAllocationSites();
+ }
+ tracker.stop();
+ });
+}
+
+const { loader, require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
+const { sinon } = ChromeUtils.importESModule(
+ "resource://testing-common/Sinon.sys.mjs"
+);
+
+// When loaded from xpcshell test, this file is loaded via xpcshell.ini's head property
+// and so it loaded first before anything else and isn't having access to Services global.
+// Whereas many head.js files from mochitest import this file via loadSubScript
+// and already expose Services as a global.
+
+const {
+ gDevTools,
+} = require("resource://devtools/client/framework/devtools.js");
+const {
+ CommandsFactory,
+} = require("resource://devtools/shared/commands/commands-factory.js");
+const DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
+
+const KeyShortcuts = require("resource://devtools/client/shared/key-shortcuts.js");
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+
+loader.lazyRequireGetter(
+ this,
+ "ResponsiveUIManager",
+ "resource://devtools/client/responsive/manager.js"
+);
+loader.lazyRequireGetter(
+ this,
+ "localTypes",
+ "resource://devtools/client/responsive/types.js"
+);
+loader.lazyRequireGetter(
+ this,
+ "ResponsiveMessageHelper",
+ "resource://devtools/client/responsive/utils/message.js"
+);
+
+loader.lazyRequireGetter(
+ this,
+ "FluentReact",
+ "resource://devtools/client/shared/vendor/fluent-react.js"
+);
+
+const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+const CHROME_URL_ROOT = TEST_DIR + "/";
+const URL_ROOT = CHROME_URL_ROOT.replace(
+ "chrome://mochitests/content/",
+ "http://example.com/"
+);
+const URL_ROOT_SSL = CHROME_URL_ROOT.replace(
+ "chrome://mochitests/content/",
+ "https://example.com/"
+);
+
+// Add aliases which make it more explicit that URL_ROOT uses a com TLD.
+const URL_ROOT_COM = URL_ROOT;
+const URL_ROOT_COM_SSL = URL_ROOT_SSL;
+
+// Also expose http://example.org, http://example.net, https://example.org to
+// test Fission scenarios easily.
+// Note: example.net is not available for https.
+const URL_ROOT_ORG = CHROME_URL_ROOT.replace(
+ "chrome://mochitests/content/",
+ "http://example.org/"
+);
+const URL_ROOT_ORG_SSL = CHROME_URL_ROOT.replace(
+ "chrome://mochitests/content/",
+ "https://example.org/"
+);
+const URL_ROOT_NET = CHROME_URL_ROOT.replace(
+ "chrome://mochitests/content/",
+ "http://example.net/"
+);
+const URL_ROOT_NET_SSL = CHROME_URL_ROOT.replace(
+ "chrome://mochitests/content/",
+ "https://example.net/"
+);
+// mochi.test:8888 is the actual primary location where files are served.
+const URL_ROOT_MOCHI_8888 = CHROME_URL_ROOT.replace(
+ "chrome://mochitests/content/",
+ "http://mochi.test:8888/"
+);
+
+try {
+ if (isMochitest) {
+ Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/devtools/client/shared/test/telemetry-test-helpers.js",
+ this
+ );
+ }
+} catch (e) {
+ ok(
+ false,
+ "MISSING DEPENDENCY ON telemetry-test-helpers.js\n" +
+ "Please add the following line in browser.ini:\n" +
+ " !/devtools/client/shared/test/telemetry-test-helpers.js\n"
+ );
+ throw e;
+}
+
+// Force devtools to be initialized so menu items and keyboard shortcuts get installed
+require("resource://devtools/client/framework/devtools-browser.js");
+
+// All tests are asynchronous
+if (isMochitest) {
+ waitForExplicitFinish();
+}
+
+var EXPECTED_DTU_ASSERT_FAILURE_COUNT = 0;
+
+registerCleanupFunction(function () {
+ if (
+ DevToolsUtils.assertionFailureCount !== EXPECTED_DTU_ASSERT_FAILURE_COUNT
+ ) {
+ ok(
+ false,
+ "Should have had the expected number of DevToolsUtils.assert() failures." +
+ " Expected " +
+ EXPECTED_DTU_ASSERT_FAILURE_COUNT +
+ ", got " +
+ DevToolsUtils.assertionFailureCount
+ );
+ }
+});
+
+// Uncomment this pref to dump all devtools emitted events to the console.
+// Services.prefs.setBoolPref("devtools.dump.emit", true);
+
+/**
+ * Watch console messages for failed propType definitions in React components.
+ */
+function onConsoleMessage(subject) {
+ const message = subject.wrappedJSObject.arguments[0];
+
+ if (message && /Failed propType/.test(message.toString())) {
+ ok(false, message);
+ }
+}
+
+const ConsoleAPIStorage = Cc["@mozilla.org/consoleAPI-storage;1"].getService(
+ Ci.nsIConsoleAPIStorage
+);
+
+ConsoleAPIStorage.addLogEventListener(
+ onConsoleMessage,
+ Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal)
+);
+registerCleanupFunction(() => {
+ ConsoleAPIStorage.removeLogEventListener(onConsoleMessage);
+});
+
+Services.prefs.setBoolPref("devtools.inspector.three-pane-enabled", true);
+
+// Disable this preference to reduce exceptions related to pending `listWorkers`
+// requests occuring after a process is created/destroyed. See Bug 1620983.
+Services.prefs.setBoolPref("dom.ipc.processPrelaunch.enabled", false);
+
+// Disable this preference to capture async stacks across all locations during
+// DevTools mochitests. Async stacks provide very valuable information to debug
+// intermittents, but come with a performance overhead, which is why they are
+// only captured in Debuggees by default.
+Services.prefs.setBoolPref(
+ "javascript.options.asyncstack_capture_debuggee_only",
+ false
+);
+
+// On some Linux platforms, prefers-reduced-motion is enabled, which would
+// trigger the notification to be displayed in the toolbox. Dismiss the message
+// by default.
+Services.prefs.setBoolPref(
+ "devtools.inspector.simple-highlighters.message-dismissed",
+ true
+);
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("devtools.dump.emit");
+ Services.prefs.clearUserPref("devtools.inspector.three-pane-enabled");
+ Services.prefs.clearUserPref("dom.ipc.processPrelaunch.enabled");
+ Services.prefs.clearUserPref("devtools.toolbox.host");
+ Services.prefs.clearUserPref("devtools.toolbox.previousHost");
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleEnabled");
+ Services.prefs.clearUserPref("devtools.toolbox.splitconsoleHeight");
+ Services.prefs.clearUserPref(
+ "javascript.options.asyncstack_capture_debuggee_only"
+ );
+ Services.prefs.clearUserPref(
+ "devtools.inspector.simple-highlighters.message-dismissed"
+ );
+});
+
+var {
+ BrowserConsoleManager,
+} = require("resource://devtools/client/webconsole/browser-console-manager.js");
+
+registerCleanupFunction(async function cleanup() {
+ // Closing the browser console if there's one
+ const browserConsole = BrowserConsoleManager.getBrowserConsole();
+ if (browserConsole) {
+ await safeCloseBrowserConsole({ clearOutput: true });
+ }
+
+ // Close any tab opened by the test.
+ // There should be only one tab opened by default when firefox starts the test.
+ while (isMochitest && gBrowser.tabs.length > 1) {
+ await closeTabAndToolbox(gBrowser.selectedTab);
+ }
+
+ // Note that this will run before cleanup functions registered by tests or other head.js files.
+ // So all connections must be cleaned up by the test when the test ends,
+ // before the harness starts invoking the cleanup functions
+ await waitForTick();
+
+ // All connections must be cleaned up by the test when the test ends.
+ const {
+ DevToolsServer,
+ } = require("resource://devtools/server/devtools-server.js");
+ ok(
+ !DevToolsServer.hasConnection(),
+ "The main process DevToolsServer has no pending connection when the test ends"
+ );
+ // If there is still open connection, close all of them so that following tests
+ // could pass.
+ if (DevToolsServer.hasConnection()) {
+ for (const conn of Object.values(DevToolsServer._connections)) {
+ conn.close();
+ }
+ }
+});
+
+async function safeCloseBrowserConsole({ clearOutput = false } = {}) {
+ const hud = BrowserConsoleManager.getBrowserConsole();
+ if (!hud) {
+ return;
+ }
+
+ if (clearOutput) {
+ info("Clear the browser console output");
+ const { ui } = hud;
+ const promises = [ui.once("messages-cleared")];
+ // If there's an object inspector, we need to wait for the actors to be released.
+ if (ui.outputNode.querySelector(".object-inspector")) {
+ promises.push(ui.once("fronts-released"));
+ }
+ await ui.clearOutput(true);
+ await Promise.all(promises);
+ info("Browser console cleared");
+ }
+
+ info("Wait for all Browser Console targets to be attached");
+ // It might happen that waitForAllTargetsToBeAttached does not resolve, so we set a
+ // timeout of 1s before closing
+ await Promise.race([
+ waitForAllTargetsToBeAttached(hud.commands.targetCommand),
+ wait(1000),
+ ]);
+
+ info("Close the Browser Console");
+ await BrowserConsoleManager.closeBrowserConsole();
+ info("Browser Console closed");
+}
+
+/**
+ * Observer code to register the test actor in every DevTools server which
+ * starts registering its own actors.
+ *
+ * We require immediately the highlighter test actor file, because it will force to load and
+ * register the front and the spec for HighlighterTestActor. Normally specs and fronts are
+ * in separate files registered in specs/index.js. But here to simplify the
+ * setup everything is in the same file and we force to load it here.
+ *
+ * DevToolsServer will emit "devtools-server-initialized" after finishing its
+ * initialization. We watch this observable to add our custom actor.
+ *
+ * As a single test may create several DevTools servers, we keep the observer
+ * alive until the test ends.
+ *
+ * To avoid leaks, the observer needs to be removed at the end of each test.
+ * The test cleanup will send the async message "remove-devtools-highlightertestactor-observer",
+ * we listen to this message to cleanup the observer.
+ */
+function highlighterTestActorBootstrap() {
+ /* eslint-env mozilla/process-script */
+ const HIGHLIGHTER_TEST_ACTOR_URL =
+ "chrome://mochitests/content/browser/devtools/client/shared/test/highlighter-test-actor.js";
+
+ const { require: _require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+ );
+ _require(HIGHLIGHTER_TEST_ACTOR_URL);
+
+ const actorRegistryObserver = subject => {
+ const actorRegistry = subject.wrappedJSObject;
+ actorRegistry.registerModule(HIGHLIGHTER_TEST_ACTOR_URL, {
+ prefix: "highlighterTest",
+ constructor: "HighlighterTestActor",
+ type: { target: true },
+ });
+ };
+ Services.obs.addObserver(
+ actorRegistryObserver,
+ "devtools-server-initialized"
+ );
+
+ const unloadListener = () => {
+ Services.cpmm.removeMessageListener(
+ "remove-devtools-testactor-observer",
+ unloadListener
+ );
+ Services.obs.removeObserver(
+ actorRegistryObserver,
+ "devtools-server-initialized"
+ );
+ };
+ Services.cpmm.addMessageListener(
+ "remove-devtools-testactor-observer",
+ unloadListener
+ );
+}
+
+if (isMochitest) {
+ const highlighterTestActorBootstrapScript =
+ "data:,(" + highlighterTestActorBootstrap + ")()";
+ Services.ppmm.loadProcessScript(
+ highlighterTestActorBootstrapScript,
+ // Load this script in all processes (created or to be created)
+ true
+ );
+
+ registerCleanupFunction(() => {
+ Services.ppmm.broadcastAsyncMessage("remove-devtools-testactor-observer");
+ Services.ppmm.removeDelayedProcessScript(
+ highlighterTestActorBootstrapScript
+ );
+ });
+}
+
+/**
+ * Spawn an instance of the highlighter test actor for the given toolbox
+ *
+ * @param {Toolbox} toolbox
+ * @param {Object} options
+ * @param {Function} options.target: Optional target to get the highlighterTestFront for.
+ * If not provided, the top level target will be used.
+ * @returns {HighlighterTestFront}
+ */
+async function getHighlighterTestFront(toolbox, { target } = {}) {
+ // Loading the Inspector panel in order to overwrite the TestActor getter for the
+ // highlighter instance with a method that points to the currently visible
+ // Box Model Highlighter managed by the Inspector panel.
+ const inspector = await toolbox.loadTool("inspector");
+
+ const highlighterTestFront = await (target || toolbox.target).getFront(
+ "highlighterTest"
+ );
+ // Override the highligher getter with a method to return the active box model
+ // highlighter. Adaptation for multi-process scenarios where there can be multiple
+ // highlighters, one per process.
+ highlighterTestFront.highlighter = () => {
+ return inspector.highlighters.getActiveHighlighter(
+ inspector.highlighters.TYPES.BOXMODEL
+ );
+ };
+ return highlighterTestFront;
+}
+
+/**
+ * Spawn an instance of the highlighter test actor for the given tab, when we need the
+ * highlighter test front before opening or without a toolbox.
+ *
+ * @param {Tab} tab
+ * @returns {HighlighterTestFront}
+ */
+async function getHighlighterTestFrontWithoutToolbox(tab) {
+ const commands = await CommandsFactory.forTab(tab);
+ // Initialize the TargetCommands which require some async stuff to be done
+ // before being fully ready. This will define the `targetCommand.targetFront` attribute.
+ await commands.targetCommand.startListening();
+
+ const targetFront = commands.targetCommand.targetFront;
+ return targetFront.getFront("highlighterTest");
+}
+
+/**
+ * Returns a Promise that resolves when all the targets are fully attached.
+ *
+ * @param {TargetCommand} targetCommand
+ */
+function waitForAllTargetsToBeAttached(targetCommand) {
+ return Promise.allSettled(
+ targetCommand
+ .getAllTargets(targetCommand.ALL_TYPES)
+ .map(target => target.initialized)
+ );
+}
+
+/**
+ * Add a new test tab in the browser and load the given url.
+ * @param {String} url The url to be loaded in the new tab
+ * @param {Object} options Object with various optional fields:
+ * - {Boolean} background If true, open the tab in background
+ * - {ChromeWindow} window Firefox top level window we should use to open the tab
+ * - {Number} userContextId The userContextId of the tab.
+ * - {String} preferredRemoteType
+ * - {Boolean} waitForLoad Wait for the page in the new tab to load. (Defaults to true.)
+ * @return a promise that resolves to the tab object when the url is loaded
+ */
+async function addTab(url, options = {}) {
+ info("Adding a new tab with URL: " + url);
+
+ const {
+ background = false,
+ userContextId,
+ preferredRemoteType,
+ waitForLoad = true,
+ } = options;
+ const { gBrowser } = options.window ? options.window : window;
+
+ const tab = BrowserTestUtils.addTab(gBrowser, url, {
+ userContextId,
+ preferredRemoteType,
+ });
+
+ if (!background) {
+ gBrowser.selectedTab = tab;
+ }
+
+ if (waitForLoad) {
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ // Waiting for presShell helps with test timeouts in webrender platforms.
+ await waitForPresShell(tab.linkedBrowser);
+ info("Tab added and finished loading");
+ } else {
+ info("Tab added");
+ }
+
+ return tab;
+}
+
+/**
+ * Remove the given tab.
+ * @param {Object} tab The tab to be removed.
+ * @return Promise<undefined> resolved when the tab is successfully removed.
+ */
+async function removeTab(tab) {
+ info("Removing tab.");
+
+ const { gBrowser } = tab.ownerDocument.defaultView;
+ const onClose = once(gBrowser.tabContainer, "TabClose");
+ gBrowser.removeTab(tab);
+ await onClose;
+
+ info("Tab removed and finished closing");
+}
+
+/**
+ * Alias for navigateTo which will reuse the current URI of the provided browser
+ * to trigger a navigation.
+ */
+async function reloadBrowser({
+ browser = gBrowser.selectedBrowser,
+ isErrorPage = false,
+ waitForLoad = true,
+} = {}) {
+ return navigateTo(browser.currentURI.spec, {
+ browser,
+ isErrorPage,
+ waitForLoad,
+ });
+}
+
+/**
+ * Navigate the currently selected tab to a new URL and wait for it to load.
+ * Also wait for the toolbox to attach to the new target, if we navigated
+ * to a new process.
+ *
+ * @param {String} url The url to be loaded in the current tab.
+ * @param {JSON} options Optional dictionary object with the following keys:
+ * - {XULBrowser} browser
+ * The browser element which should navigate. Defaults to the selected
+ * browser.
+ * - {Boolean} isErrorPage
+ * You may pass `true` if the URL is an error page. Otherwise
+ * BrowserTestUtils.browserLoaded will wait for 'load' event, which
+ * never fires for error pages.
+ * - {Boolean} waitForLoad
+ * You may pass `false` if the page load is expected to be blocked by
+ * a script or a breakpoint.
+ *
+ * @return a promise that resolves when the page has fully loaded.
+ */
+async function navigateTo(
+ uri,
+ {
+ browser = gBrowser.selectedBrowser,
+ isErrorPage = false,
+ waitForLoad = true,
+ } = {}
+) {
+ const waitForDevToolsReload = await watchForDevToolsReload(browser, {
+ isErrorPage,
+ waitForLoad,
+ });
+
+ uri = uri.replaceAll("\n", "");
+ info(`Navigating to "${uri}"`);
+
+ const onBrowserLoaded = BrowserTestUtils.browserLoaded(
+ browser,
+ // includeSubFrames
+ false,
+ // resolve on this specific page to load (if null, it would be any page load)
+ loadedUrl => {
+ // loadedUrl is encoded, while uri might not be.
+ return loadedUrl === uri || decodeURI(loadedUrl) === uri;
+ },
+ isErrorPage
+ );
+
+ // if we're navigating to the same page we're already on, use reloadTab instead as the
+ // behavior slightly differs from loadURI (e.g. scroll position isn't keps with the latter).
+ if (uri === browser.currentURI.spec) {
+ gBrowser.reloadTab(gBrowser.getTabForBrowser(browser));
+ } else {
+ BrowserTestUtils.startLoadingURIString(browser, uri);
+ }
+
+ if (waitForLoad) {
+ info(`Waiting for page to be loaded…`);
+ await onBrowserLoaded;
+ info(`→ page loaded`);
+ }
+
+ await waitForDevToolsReload();
+}
+
+/**
+ * This method should be used to watch for completion of any browser navigation
+ * performed with a DevTools UI.
+ *
+ * It should watch for:
+ * - Toolbox reload
+ * - Toolbox commands reload
+ * - RDM reload
+ * - RDM commands reload
+ *
+ * And it should work both for target switching or old-style navigations.
+ *
+ * This method, similarly to all the other watch* navigation methods in this file,
+ * is async but returns another method which should be called after the navigation
+ * is done. Browser navigation might be monitored differently depending on the
+ * situation, so it's up to the caller to handle it as needed.
+ *
+ * Typically, this would be used as follows:
+ * ```
+ * async function someNavigationHelper(browser) {
+ * const waitForDevToolsFn = await watchForDevToolsReload(browser);
+ *
+ * // This step should wait for the load to be completed from the browser's
+ * // point of view, so that waitForDevToolsFn can compare pIds, browsing
+ * // contexts etc... and check if we should expect a target switch
+ * await performBrowserNavigation(browser);
+ *
+ * await waitForDevToolsFn();
+ * }
+ * ```
+ */
+async function watchForDevToolsReload(
+ browser,
+ { isErrorPage = false, waitForLoad = true } = {}
+) {
+ const waitForToolboxReload = await _watchForToolboxReload(browser, {
+ isErrorPage,
+ waitForLoad,
+ });
+ const waitForResponsiveReload = await _watchForResponsiveReload(browser, {
+ isErrorPage,
+ waitForLoad,
+ });
+
+ return async function () {
+ info("Wait for the toolbox to reload");
+ await waitForToolboxReload();
+
+ info("Wait for Responsive UI to reload");
+ await waitForResponsiveReload();
+ };
+}
+
+/**
+ * Start watching for the toolbox reload to be completed:
+ * - watch for the toolbox's commands to be fully reloaded
+ * - watch for the toolbox's current panel to be reloaded
+ */
+async function _watchForToolboxReload(
+ browser,
+ { isErrorPage, waitForLoad } = {}
+) {
+ const tab = gBrowser.getTabForBrowser(browser);
+
+ const toolbox = gDevTools.getToolboxForTab(tab);
+
+ if (!toolbox) {
+ // No toolbox to wait for
+ return function () {};
+ }
+
+ const waitForCurrentPanelReload = watchForCurrentPanelReload(toolbox);
+ const waitForToolboxCommandsReload = await watchForCommandsReload(
+ toolbox.commands,
+ { isErrorPage, waitForLoad }
+ );
+ const checkTargetSwitching = await watchForTargetSwitching(
+ toolbox.commands,
+ browser
+ );
+
+ return async function () {
+ const isTargetSwitching = checkTargetSwitching();
+
+ info(`Waiting for toolbox commands to be reloaded…`);
+ await waitForToolboxCommandsReload(isTargetSwitching);
+
+ // TODO: We should wait for all loaded panels to reload here, because some
+ // of them might still perform background updates.
+ if (waitForCurrentPanelReload) {
+ info(`Waiting for ${toolbox.currentToolId} to be reloaded…`);
+ await waitForCurrentPanelReload();
+ info(`→ panel reloaded`);
+ }
+ };
+}
+
+/**
+ * Start watching for Responsive UI (RDM) reload to be completed:
+ * - watch for the Responsive UI's commands to be fully reloaded
+ * - watch for the Responsive UI's target switch to be done
+ */
+async function _watchForResponsiveReload(
+ browser,
+ { isErrorPage, waitForLoad } = {}
+) {
+ const tab = gBrowser.getTabForBrowser(browser);
+ const ui = ResponsiveUIManager.getResponsiveUIForTab(tab);
+
+ if (!ui) {
+ // No responsive UI to wait for
+ return function () {};
+ }
+
+ const onResponsiveTargetSwitch = ui.once("responsive-ui-target-switch-done");
+ const waitForResponsiveCommandsReload = await watchForCommandsReload(
+ ui.commands,
+ { isErrorPage, waitForLoad }
+ );
+ const checkTargetSwitching = await watchForTargetSwitching(
+ ui.commands,
+ browser
+ );
+
+ return async function () {
+ const isTargetSwitching = checkTargetSwitching();
+
+ info(`Waiting for responsive ui commands to be reloaded…`);
+ await waitForResponsiveCommandsReload(isTargetSwitching);
+
+ if (isTargetSwitching) {
+ await onResponsiveTargetSwitch;
+ }
+ };
+}
+
+/**
+ * Watch for the current panel selected in the provided toolbox to be reloaded.
+ * Some panels implement custom events that should be expected for every reload.
+ *
+ * Note about returning a method instead of a promise:
+ * In general this pattern is useful so that we can check if a target switch
+ * occurred or not, and decide which events to listen for. So far no panel is
+ * behaving differently whether there was a target switch or not. But to remain
+ * consistent with other watch* methods we still return a function here.
+ *
+ * @param {Toolbox}
+ * The Toolbox instance which is going to experience a reload
+ * @return {function} An async method to be called and awaited after the reload
+ * started. Will return `null` for panels which don't implement any
+ * specific reload event.
+ */
+function watchForCurrentPanelReload(toolbox) {
+ return _watchForPanelReload(toolbox, toolbox.currentToolId);
+}
+
+/**
+ * Watch for all the panels loaded in the provided toolbox to be reloaded.
+ * Some panels implement custom events that should be expected for every reload.
+ *
+ * Note about returning a method instead of a promise:
+ * See comment for watchForCurrentPanelReload
+ *
+ * @param {Toolbox}
+ * The Toolbox instance which is going to experience a reload
+ * @return {function} An async method to be called and awaited after the reload
+ * started.
+ */
+function watchForLoadedPanelsReload(toolbox) {
+ const waitForPanels = [];
+ for (const [id] of toolbox.getToolPanels()) {
+ // Store a watcher method for each panel already loaded.
+ waitForPanels.push(_watchForPanelReload(toolbox, id));
+ }
+
+ return function () {
+ return Promise.all(
+ waitForPanels.map(async watchPanel => {
+ // Wait for all panels to be reloaded.
+ if (watchPanel) {
+ await watchPanel();
+ }
+ })
+ );
+ };
+}
+
+function _watchForPanelReload(toolbox, toolId) {
+ const panel = toolbox.getPanel(toolId);
+
+ if (toolId == "inspector") {
+ const markuploaded = panel.once("markuploaded");
+ const onNewRoot = panel.once("new-root");
+ const onUpdated = panel.once("inspector-updated");
+ const onReloaded = panel.once("reloaded");
+
+ return async function () {
+ info("Waiting for markup view to load after navigation.");
+ await markuploaded;
+
+ info("Waiting for new root.");
+ await onNewRoot;
+
+ info("Waiting for inspector to update after new-root event.");
+ await onUpdated;
+
+ info("Waiting for inspector updates after page reload");
+ await onReloaded;
+ };
+ } else if (
+ ["netmonitor", "accessibility", "webconsole", "jsdebugger"].includes(toolId)
+ ) {
+ const onReloaded = panel.once("reloaded");
+ return async function () {
+ info(`Waiting for ${toolId} updates after page reload`);
+ await onReloaded;
+ };
+ }
+ return null;
+}
+
+/**
+ * Watch for a Commands instance to be reloaded after a navigation.
+ *
+ * As for other navigation watch* methods, this should be called before the
+ * navigation starts, and the function it returns should be called after the
+ * navigation is done from a Browser point of view.
+ *
+ * !!! The wait function expects a `isTargetSwitching` argument to be provided,
+ * which needs to be monitored using watchForTargetSwitching !!!
+ */
+async function watchForCommandsReload(
+ commands,
+ { isErrorPage = false, waitForLoad = true } = {}
+) {
+ // If we're switching origins, we need to wait for the 'switched-target'
+ // event to make sure everything is ready.
+ // Navigating from/to pages loaded in the parent process, like about:robots,
+ // also spawn new targets.
+ // (If target switching is disabled, the toolbox will reboot)
+ const onTargetSwitched = commands.targetCommand.once("switched-target");
+
+ // Wait until we received a page load resource:
+ // - dom-complete if we can wait for a full page load
+ // - dom-loading otherwise
+ // This allows to wait for page load for consumers calling directly
+ // waitForDevTools instead of navigateTo/reloadBrowser.
+ // This is also useful as an alternative to target switching, when no target
+ // switch is supposed to happen.
+ const waitForCompleteLoad = waitForLoad && !isErrorPage;
+ const documentEventName = waitForCompleteLoad
+ ? "dom-complete"
+ : "dom-loading";
+
+ const { onResource: onTopLevelDomEvent } =
+ await commands.resourceCommand.waitForNextResource(
+ commands.resourceCommand.TYPES.DOCUMENT_EVENT,
+ {
+ ignoreExistingResources: true,
+ predicate: resource =>
+ resource.targetFront.isTopLevel &&
+ resource.name === documentEventName,
+ }
+ );
+
+ return async function (isTargetSwitching) {
+ if (typeof isTargetSwitching === "undefined") {
+ throw new Error("isTargetSwitching was not provided to the wait method");
+ }
+
+ if (isTargetSwitching) {
+ info(`Waiting for target switch…`);
+ await onTargetSwitched;
+ info(`→ switched-target emitted`);
+ }
+
+ info(`Waiting for '${documentEventName}' resource…`);
+ await onTopLevelDomEvent;
+ info(`→ '${documentEventName}' resource emitted`);
+
+ return isTargetSwitching;
+ };
+}
+
+/**
+ * Watch if an upcoming navigation will trigger a target switching, for the
+ * provided Commands instance and the provided Browser.
+ *
+ * As for other navigation watch* methods, this should be called before the
+ * navigation starts, and the function it returns should be called after the
+ * navigation is done from a Browser point of view.
+ */
+async function watchForTargetSwitching(commands, browser) {
+ browser = browser || gBrowser.selectedBrowser;
+ const currentPID = browser.browsingContext.currentWindowGlobal.osPid;
+ const currentBrowsingContextID = browser.browsingContext.id;
+
+ // If the current top-level target follows the window global lifecycle, a
+ // target switch will occur regardless of process changes.
+ const targetFollowsWindowLifecycle =
+ commands.targetCommand.targetFront.targetForm.followWindowGlobalLifeCycle;
+
+ return function () {
+ // Compare the PIDs (and not the toolbox's targets) as PIDs are updated also immediately,
+ // while target may be updated slightly later.
+ const switchedProcess =
+ currentPID !== browser.browsingContext.currentWindowGlobal.osPid;
+ const switchedBrowsingContext =
+ currentBrowsingContextID !== browser.browsingContext.id;
+
+ return (
+ targetFollowsWindowLifecycle || switchedProcess || switchedBrowsingContext
+ );
+ };
+}
+
+/**
+ * Create a Target for the provided tab and attach to it before resolving.
+ * This should only be used for tests which don't involve the frontend or a
+ * toolbox. Typically, retrieving the target and attaching to it should be
+ * handled at framework level when a Toolbox is used.
+ *
+ * @param {XULTab} tab
+ * The tab for which a target should be created.
+ * @return {WindowGlobalTargetFront} The attached target front.
+ */
+async function createAndAttachTargetForTab(tab) {
+ info("Creating and attaching to a local tab target");
+
+ const commands = await CommandsFactory.forTab(tab);
+
+ // Initialize the TargetCommands which require some async stuff to be done
+ // before being fully ready. This will define the `targetCommand.targetFront` attribute.
+ await commands.targetCommand.startListening();
+
+ const target = commands.targetCommand.targetFront;
+ return target;
+}
+
+function isFissionEnabled() {
+ return SpecialPowers.useRemoteSubframes;
+}
+
+function isEveryFrameTargetEnabled() {
+ return Services.prefs.getBoolPref(
+ "devtools.every-frame-target.enabled",
+ false
+ );
+}
+
+/**
+ * Open the inspector in a tab with given URL.
+ * @param {string} url The URL to open.
+ * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
+ * @return A promise that is resolved once the tab and inspector have loaded
+ * with an object: { tab, toolbox, inspector, highlighterTestFront }.
+ */
+async function openInspectorForURL(url, hostType) {
+ const tab = await addTab(url);
+ const { inspector, toolbox, highlighterTestFront } = await openInspector(
+ hostType
+ );
+ return { tab, inspector, toolbox, highlighterTestFront };
+}
+
+function getActiveInspector() {
+ const toolbox = gDevTools.getToolboxForTab(gBrowser.selectedTab);
+ return toolbox.getPanel("inspector");
+}
+
+/**
+ * Simulate a key event from an electron key shortcut string:
+ * https://github.com/electron/electron/blob/master/docs/api/accelerator.md
+ *
+ * @param {String} key
+ * @param {DOMWindow} target
+ * Optional window where to fire the key event
+ */
+function synthesizeKeyShortcut(key, target) {
+ // parseElectronKey requires any window, just to access `KeyboardEvent`
+ const window = Services.appShell.hiddenDOMWindow;
+ const shortcut = KeyShortcuts.parseElectronKey(window, key);
+ const keyEvent = {
+ altKey: shortcut.alt,
+ ctrlKey: shortcut.ctrl,
+ metaKey: shortcut.meta,
+ shiftKey: shortcut.shift,
+ };
+ if (shortcut.keyCode) {
+ keyEvent.keyCode = shortcut.keyCode;
+ }
+
+ info("Synthesizing key shortcut: " + key);
+ EventUtils.synthesizeKey(shortcut.key || "", keyEvent, target);
+}
+
+var waitForTime = DevToolsUtils.waitForTime;
+
+/**
+ * Wait for a tick.
+ * @return {Promise}
+ */
+function waitForTick() {
+ return new Promise(resolve => DevToolsUtils.executeSoon(resolve));
+}
+
+/**
+ * This shouldn't be used in the tests, but is useful when writing new tests or
+ * debugging existing tests in order to introduce delays in the test steps
+ *
+ * @param {Number} ms
+ * The time to wait
+ * @return A promise that resolves when the time is passed
+ */
+function wait(ms) {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ info("Waiting " + ms / 1000 + " seconds.");
+ });
+}
+
+/**
+ * Wait for a predicate to return a result.
+ *
+ * @param function condition
+ * Invoked once in a while until it returns a truthy value. This should be an
+ * idempotent function, since we have to run it a second time after it returns
+ * true in order to return the value.
+ * @param string message [optional]
+ * A message to output if the condition fails.
+ * @param number interval [optional]
+ * How often the predicate is invoked, in milliseconds.
+ * Can be set globally for a test via `waitFor.overrideIntervalForTestFile = someNumber;`.
+ * @param number maxTries [optional]
+ * How many times the predicate is invoked before timing out.
+ * Can be set globally for a test via `waitFor.overrideMaxTriesForTestFile = someNumber;`.
+ * @return object
+ * A promise that is resolved with the result of the condition.
+ */
+async function waitFor(condition, message = "", interval = 10, maxTries = 500) {
+ // Update interval & maxTries if overrides are defined on the waitFor object.
+ interval =
+ typeof waitFor.overrideIntervalForTestFile !== "undefined"
+ ? waitFor.overrideIntervalForTestFile
+ : interval;
+ maxTries =
+ typeof waitFor.overrideMaxTriesForTestFile !== "undefined"
+ ? waitFor.overrideMaxTriesForTestFile
+ : maxTries;
+
+ try {
+ const value = await BrowserTestUtils.waitForCondition(
+ condition,
+ message,
+ interval,
+ maxTries
+ );
+ return value;
+ } catch (e) {
+ const errorMessage = `Failed waitFor(): ${message} \nFailed condition: ${condition} \nException Message: ${e}`;
+ throw new Error(errorMessage);
+ }
+}
+
+/**
+ * Wait for eventName on target to be delivered a number of times.
+ *
+ * @param {Object} target
+ * An observable object that either supports on/off or
+ * addEventListener/removeEventListener
+ * @param {String} eventName
+ * @param {Number} numTimes
+ * Number of deliveries to wait for.
+ * @param {Boolean} useCapture
+ * Optional, for addEventListener/removeEventListener
+ * @return A promise that resolves when the event has been handled
+ */
+function waitForNEvents(target, eventName, numTimes, useCapture = false) {
+ info("Waiting for event: '" + eventName + "' on " + target + ".");
+
+ let count = 0;
+
+ return new Promise(resolve => {
+ for (const [add, remove] of [
+ ["on", "off"],
+ ["addEventListener", "removeEventListener"],
+ ["addListener", "removeListener"],
+ ["addMessageListener", "removeMessageListener"],
+ ]) {
+ if (add in target && remove in target) {
+ target[add](
+ eventName,
+ function onEvent(...args) {
+ if (typeof info === "function") {
+ info("Got event: '" + eventName + "' on " + target + ".");
+ }
+
+ if (++count == numTimes) {
+ target[remove](eventName, onEvent, useCapture);
+ resolve(...args);
+ }
+ },
+ useCapture
+ );
+ break;
+ }
+ }
+ });
+}
+
+/**
+ * Wait for DOM change on target.
+ *
+ * @param {Object} target
+ * The Node on which to observe DOM mutations.
+ * @param {String} selector
+ * Given a selector to watch whether the expected element is changed
+ * on target.
+ * @param {Number} expectedLength
+ * Optional, default set to 1
+ * There may be more than one element match an array match the selector,
+ * give an expected length to wait for more elements.
+ * @return A promise that resolves when the event has been handled
+ */
+function waitForDOM(target, selector, expectedLength = 1) {
+ return new Promise(resolve => {
+ const observer = new MutationObserver(mutations => {
+ mutations.forEach(mutation => {
+ const elements = mutation.target.querySelectorAll(selector);
+
+ if (elements.length === expectedLength) {
+ observer.disconnect();
+ resolve(elements);
+ }
+ });
+ });
+
+ observer.observe(target, {
+ attributes: true,
+ childList: true,
+ subtree: true,
+ });
+ });
+}
+
+/**
+ * Wait for eventName on target.
+ *
+ * @param {Object} target
+ * An observable object that either supports on/off or
+ * addEventListener/removeEventListener
+ * @param {String} eventName
+ * @param {Boolean} useCapture
+ * Optional, for addEventListener/removeEventListener
+ * @return A promise that resolves when the event has been handled
+ */
+function once(target, eventName, useCapture = false) {
+ return waitForNEvents(target, eventName, 1, useCapture);
+}
+
+/**
+ * Some tests may need to import one or more of the test helper scripts.
+ * A test helper script is simply a js file that contains common test code that
+ * is either not common-enough to be in head.js, or that is located in a
+ * separate directory.
+ * The script will be loaded synchronously and in the test's scope.
+ * @param {String} filePath The file path, relative to the current directory.
+ * Examples:
+ * - "helper_attributes_test_runner.js"
+ */
+function loadHelperScript(filePath) {
+ const testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
+ Services.scriptloader.loadSubScript(testDir + "/" + filePath, this);
+}
+
+/**
+ * Open the toolbox in a given tab.
+ * @param {XULNode} tab The tab the toolbox should be opened in.
+ * @param {String} toolId Optional. The ID of the tool to be selected.
+ * @param {String} hostType Optional. The type of toolbox host to be used.
+ * @return {Promise} Resolves with the toolbox, when it has been opened.
+ */
+async function openToolboxForTab(tab, toolId, hostType) {
+ info("Opening the toolbox");
+
+ // Check if the toolbox is already loaded.
+ let toolbox = gDevTools.getToolboxForTab(tab);
+ if (toolbox) {
+ if (!toolId || (toolId && toolbox.getPanel(toolId))) {
+ info("Toolbox is already opened");
+ return toolbox;
+ }
+ }
+
+ // If not, load it now.
+ toolbox = await gDevTools.showToolboxForTab(tab, { toolId, hostType });
+
+ // Make sure that the toolbox frame is focused.
+ await new Promise(resolve => waitForFocus(resolve, toolbox.win));
+
+ info("Toolbox opened and focused");
+
+ return toolbox;
+}
+
+/**
+ * Add a new tab and open the toolbox in it.
+ * @param {String} url The URL for the tab to be opened.
+ * @param {String} toolId Optional. The ID of the tool to be selected.
+ * @param {String} hostType Optional. The type of toolbox host to be used.
+ * @return {Promise} Resolves when the tab has been added, loaded and the
+ * toolbox has been opened. Resolves to the toolbox.
+ */
+async function openNewTabAndToolbox(url, toolId, hostType) {
+ const tab = await addTab(url);
+ return openToolboxForTab(tab, toolId, hostType);
+}
+
+/**
+ * Close a tab and if necessary, the toolbox that belongs to it
+ * @param {Tab} tab The tab to close.
+ * @return {Promise} Resolves when the toolbox and tab have been destroyed and
+ * closed.
+ */
+async function closeTabAndToolbox(tab = gBrowser.selectedTab) {
+ if (gDevTools.hasToolboxForTab(tab)) {
+ await gDevTools.closeToolboxForTab(tab);
+ }
+
+ await removeTab(tab);
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+}
+
+/**
+ * Close a toolbox and the current tab.
+ * @param {Toolbox} toolbox The toolbox to close.
+ * @return {Promise} Resolves when the toolbox and tab have been destroyed and
+ * closed.
+ */
+async function closeToolboxAndTab(toolbox) {
+ await toolbox.destroy();
+ await removeTab(gBrowser.selectedTab);
+}
+
+/**
+ * Waits until a predicate returns true.
+ *
+ * @param function predicate
+ * Invoked once in a while until it returns true.
+ * @param number interval [optional]
+ * How often the predicate is invoked, in milliseconds.
+ */
+function waitUntil(predicate, interval = 10) {
+ if (predicate()) {
+ return Promise.resolve(true);
+ }
+ return new Promise(resolve => {
+ setTimeout(function () {
+ waitUntil(predicate, interval).then(() => resolve(true));
+ }, interval);
+ });
+}
+
+/**
+ * Variant of waitUntil that accepts a predicate returning a promise.
+ */
+async function asyncWaitUntil(predicate, interval = 10) {
+ let success = await predicate();
+ while (!success) {
+ // Wait for X milliseconds.
+ await new Promise(resolve => setTimeout(resolve, interval));
+ // Test the predicate again.
+ success = await predicate();
+ }
+}
+
+/**
+ * Wait for a context menu popup to open.
+ *
+ * @param Element popup
+ * The XUL popup you expect to open.
+ * @param Element button
+ * The button/element that receives the contextmenu event. This is
+ * expected to open the popup.
+ * @param function onShown
+ * Function to invoke on popupshown event.
+ * @param function onHidden
+ * Function to invoke on popuphidden event.
+ * @return object
+ * A Promise object that is resolved after the popuphidden event
+ * callback is invoked.
+ */
+function waitForContextMenu(popup, button, onShown, onHidden) {
+ return new Promise(resolve => {
+ function onPopupShown() {
+ info("onPopupShown");
+ popup.removeEventListener("popupshown", onPopupShown);
+
+ onShown && onShown();
+
+ // Use executeSoon() to get out of the popupshown event.
+ popup.addEventListener("popuphidden", onPopupHidden);
+ DevToolsUtils.executeSoon(() => popup.hidePopup());
+ }
+ function onPopupHidden() {
+ info("onPopupHidden");
+ popup.removeEventListener("popuphidden", onPopupHidden);
+
+ onHidden && onHidden();
+
+ resolve(popup);
+ }
+
+ popup.addEventListener("popupshown", onPopupShown);
+
+ info("wait for the context menu to open");
+ synthesizeContextMenuEvent(button);
+ });
+}
+
+function synthesizeContextMenuEvent(el) {
+ el.scrollIntoView();
+ const eventDetails = { type: "contextmenu", button: 2 };
+ EventUtils.synthesizeMouse(
+ el,
+ 5,
+ 2,
+ eventDetails,
+ el.ownerDocument.defaultView
+ );
+}
+
+/**
+ * Promise wrapper around SimpleTest.waitForClipboard
+ */
+function waitForClipboardPromise(setup, expected) {
+ return new Promise((resolve, reject) => {
+ SimpleTest.waitForClipboard(expected, setup, resolve, reject);
+ });
+}
+
+/**
+ * Simple helper to push a temporary preference. Wrapper on SpecialPowers
+ * pushPrefEnv that returns a promise resolving when the preferences have been
+ * updated.
+ *
+ * @param {String} preferenceName
+ * The name of the preference to updated
+ * @param {} value
+ * The preference value, type can vary
+ * @return {Promise} resolves when the preferences have been updated
+ */
+function pushPref(preferenceName, value) {
+ const options = { set: [[preferenceName, value]] };
+ return SpecialPowers.pushPrefEnv(options);
+}
+
+async function closeToolbox() {
+ await gDevTools.closeToolboxForTab(gBrowser.selectedTab);
+}
+
+/**
+ * Clean the logical clipboard content. This method only clears the OS clipboard on
+ * Windows (see Bug 666254).
+ */
+function emptyClipboard() {
+ const clipboard = Services.clipboard;
+ clipboard.emptyClipboard(clipboard.kGlobalClipboard);
+}
+
+/**
+ * Check if the current operating system is Windows.
+ */
+function isWindows() {
+ return Services.appinfo.OS === "WINNT";
+}
+
+/**
+ * Create an HTTP server that can be used to simulate custom requests within
+ * a test. It is automatically cleaned up when the test ends, so no need to
+ * call `destroy`.
+ *
+ * See https://developer.mozilla.org/en-US/docs/Httpd.js/HTTP_server_for_unit_tests
+ * for more information about how to register handlers.
+ *
+ * The server can be accessed like:
+ *
+ * const server = createTestHTTPServer();
+ * let url = "http://localhost: " + server.identity.primaryPort + "/path";
+ * @returns {HttpServer}
+ */
+function createTestHTTPServer() {
+ const { HttpServer } = ChromeUtils.importESModule(
+ "resource://testing-common/httpd.sys.mjs"
+ );
+ const server = new HttpServer();
+
+ registerCleanupFunction(async function cleanup() {
+ await new Promise(resolve => server.stop(resolve));
+ });
+
+ server.start(-1);
+ return server;
+}
+
+/*
+ * Register an actor in the content process of the current tab.
+ *
+ * Calling ActorRegistry.registerModule only registers the actor in the current process.
+ * As all test scripts are ran in the parent process, it is only registered here.
+ * This function helps register them in the content process used for the current tab.
+ *
+ * @param {string} url
+ * Actor module URL or absolute require path
+ * @param {json} options
+ * Arguments to be passed to DevToolsServer.registerModule
+ */
+async function registerActorInContentProcess(url, options) {
+ function convertChromeToFile(uri) {
+ return Cc["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Ci.nsIChromeRegistry)
+ .convertChromeURL(Services.io.newURI(uri)).spec;
+ }
+ // chrome://mochitests URI is registered only in the parent process, so convert these
+ // URLs to file:// one in order to work in the content processes
+ url = url.startsWith("chrome://mochitests") ? convertChromeToFile(url) : url;
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [{ url, options }],
+ args => {
+ // eslint-disable-next-line no-shadow
+ const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+ );
+ const {
+ ActorRegistry,
+ } = require("resource://devtools/server/actors/utils/actor-registry.js");
+ ActorRegistry.registerModule(args.url, args.options);
+ }
+ );
+}
+
+/**
+ * Move the provided Window to the provided left, top coordinates and wait for
+ * the window position to be updated.
+ */
+async function moveWindowTo(win, left, top) {
+ // Check that the expected coordinates are within the window available area.
+ left = Math.max(win.screen.availLeft, left);
+ left = Math.min(win.screen.width, left);
+ top = Math.max(win.screen.availTop, top);
+ top = Math.min(win.screen.height, top);
+
+ info(`Moving window to {${left}, ${top}}`);
+ win.moveTo(left, top);
+
+ // Bug 1600809: window move/resize can be async on Linux sometimes.
+ // Wait so that the anchor's position is correctly measured.
+ return waitUntil(() => {
+ info(
+ `Wait for window screenLeft and screenTop to be updated: (${win.screenLeft}, ${win.screenTop})`
+ );
+ return win.screenLeft === left && win.screenTop === top;
+ });
+}
+
+function getCurrentTestFilePath() {
+ return gTestPath.replace("chrome://mochitests/content/browser/", "");
+}
+
+/**
+ * Unregister all registered service workers.
+ *
+ * @param {DevToolsClient} client
+ */
+async function unregisterAllServiceWorkers(client) {
+ info("Wait until all workers have a valid registrationFront");
+ let workers;
+ await asyncWaitUntil(async function () {
+ workers = await client.mainRoot.listAllWorkers();
+ const allWorkersRegistered = workers.service.every(
+ worker => !!worker.registrationFront
+ );
+ return allWorkersRegistered;
+ });
+
+ info("Unregister all service workers");
+ const promises = [];
+ for (const worker of workers.service) {
+ promises.push(worker.registrationFront.unregister());
+ }
+ await Promise.all(promises);
+}
+
+/**********************
+ * Screenshot helpers *
+ **********************/
+
+/**
+ * Returns an object containing the r,g and b colors of the provided image at
+ * the passed position
+ *
+ * @param {Image} image
+ * @param {Int} x
+ * @param {Int} y
+ * @returns Object with the following properties:
+ * - {Int} r: The red component of the pixel
+ * - {Int} g: The green component of the pixel
+ * - {Int} b: The blue component of the pixel
+ */
+function colorAt(image, x, y) {
+ // Create a test canvas element.
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const canvas = document.createElementNS(HTML_NS, "canvas");
+ canvas.width = image.width;
+ canvas.height = image.height;
+
+ // Draw the image in the canvas
+ const context = canvas.getContext("2d");
+ context.drawImage(image, 0, 0, image.width, image.height);
+
+ // Return the color found at the provided x,y coordinates as a "r, g, b" string.
+ const [r, g, b] = context.getImageData(x, y, 1, 1).data;
+ return { r, g, b };
+}
+
+let allDownloads = [];
+/**
+ * Returns a Promise that resolves when a new screenshot is available in the download folder.
+ *
+ * @param {Object} [options]
+ * @param {Boolean} options.isWindowPrivate: Set to true if the window from which the screenshot
+ * is taken is a private window. This will ensure that we check that the
+ * screenshot appears in the private window, not the non-private one (See Bug 1783373)
+ */
+async function waitUntilScreenshot({ isWindowPrivate = false } = {}) {
+ const { Downloads } = ChromeUtils.importESModule(
+ "resource://gre/modules/Downloads.sys.mjs"
+ );
+ const list = await Downloads.getList(Downloads.ALL);
+
+ return new Promise(function (resolve) {
+ const view = {
+ onDownloadAdded: async download => {
+ await download.whenSucceeded();
+ if (allDownloads.includes(download)) {
+ return;
+ }
+
+ is(
+ !!download.source.isPrivate,
+ isWindowPrivate,
+ `The download occured in the expected${
+ isWindowPrivate ? " private" : ""
+ } window`
+ );
+
+ allDownloads.push(download);
+ resolve(download.target.path);
+ list.removeView(view);
+ },
+ };
+
+ list.addView(view);
+ });
+}
+
+/**
+ * Clear all the download references.
+ */
+async function resetDownloads() {
+ info("Reset downloads");
+ const { Downloads } = ChromeUtils.importESModule(
+ "resource://gre/modules/Downloads.sys.mjs"
+ );
+ const downloadList = await Downloads.getList(Downloads.ALL);
+ const downloads = await downloadList.getAll();
+ for (const download of downloads) {
+ downloadList.remove(download);
+ await download.finalize(true);
+ }
+ allDownloads = [];
+}
+
+/**
+ * Return a screenshot of the currently selected node in the inspector (using the internal
+ * Inspector#screenshotNode method).
+ *
+ * @param {Inspector} inspector
+ * @returns {Image}
+ */
+async function takeNodeScreenshot(inspector) {
+ // Cleanup all downloads at the end of the test.
+ registerCleanupFunction(resetDownloads);
+
+ info(
+ "Call screenshotNode() and wait until the screenshot is found in the Downloads"
+ );
+ const whenScreenshotSucceeded = waitUntilScreenshot();
+ inspector.screenshotNode();
+ const filePath = await whenScreenshotSucceeded;
+
+ info("Create an image using the downloaded fileas source");
+ const image = new Image();
+ const onImageLoad = once(image, "load");
+ image.src = PathUtils.toFileURI(filePath);
+ await onImageLoad;
+
+ info("Remove the downloaded screenshot file");
+ await IOUtils.remove(filePath);
+
+ // See intermittent Bug 1508435. Even after removing the file, tests still manage to
+ // reuse files from the previous test if they have the same name. Since our file name
+ // is based on a timestamp that has "second" precision, wait for one second to make sure
+ // screenshots will have different names.
+ info(
+ "Wait for one second to make sure future screenshots will use a different name"
+ );
+ await new Promise(r => setTimeout(r, 1000));
+
+ return image;
+}
+
+/**
+ * Check that the provided image has the expected width, height, and color.
+ * NOTE: This test assumes that the image is only made of a single color and will only
+ * check one pixel.
+ */
+async function assertSingleColorScreenshotImage(
+ image,
+ width,
+ height,
+ { r, g, b }
+) {
+ info(`Assert ${image.src} content`);
+ const ratio = await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ () => content.wrappedJSObject.devicePixelRatio
+ );
+
+ is(
+ image.width,
+ ratio * width,
+ `node screenshot has the expected width (dpr = ${ratio})`
+ );
+ is(
+ image.height,
+ height * ratio,
+ `node screenshot has the expected height (dpr = ${ratio})`
+ );
+
+ const color = colorAt(image, 0, 0);
+ is(color.r, r, "node screenshot has the expected red component");
+ is(color.g, g, "node screenshot has the expected green component");
+ is(color.b, b, "node screenshot has the expected blue component");
+}
+
+/**
+ * Check that the provided image has the expected color at a given position
+ */
+function checkImageColorAt({ image, x = 0, y, expectedColor, label }) {
+ const color = colorAt(image, x, y);
+ is(`rgb(${Object.values(color).join(", ")})`, expectedColor, label);
+}
+
+/**
+ * Wait until the store has reached a state that matches the predicate.
+ * @param Store store
+ * The Redux store being used.
+ * @param function predicate
+ * A function that returns true when the store has reached the expected
+ * state.
+ * @return Promise
+ * Resolved once the store reaches the expected state.
+ */
+function waitUntilState(store, predicate) {
+ return new Promise(resolve => {
+ const unsubscribe = store.subscribe(check);
+
+ info(`Waiting for state predicate "${predicate}"`);
+ function check() {
+ if (predicate(store.getState())) {
+ info(`Found state predicate "${predicate}"`);
+ unsubscribe();
+ resolve();
+ }
+ }
+
+ // Fire the check immediately in case the action has already occurred
+ check();
+ });
+}
+
+/**
+ * Wait for a specific action type to be dispatched.
+ *
+ * If the action is async and defines a `status` property, this helper will wait
+ * for the status to reach either "error" or "done".
+ *
+ * @param {Object} store
+ * Redux store where the action should be dispatched.
+ * @param {String} actionType
+ * The actionType to wait for.
+ * @param {Number} repeat
+ * Optional, number of time the action is expected to be dispatched.
+ * Defaults to 1
+ * @return {Promise}
+ */
+function waitForDispatch(store, actionType, repeat = 1) {
+ let count = 0;
+ return new Promise(resolve => {
+ store.dispatch({
+ type: "@@service/waitUntil",
+ predicate: action => {
+ const isDone =
+ !action.status ||
+ action.status === "done" ||
+ action.status === "error";
+
+ if (action.type === actionType && isDone && ++count == repeat) {
+ return true;
+ }
+
+ return false;
+ },
+ run: (dispatch, getState, action) => {
+ resolve(action);
+ },
+ });
+ });
+}
+
+/**
+ * Retrieve a browsing context in nested frames.
+ *
+ * @param {BrowsingContext|XULBrowser} browsingContext
+ * The topmost browsing context under which we should search for the
+ * browsing context.
+ * @param {Array<String>} selectors
+ * Array of CSS selectors that form a path to a specific nested frame.
+ * @return {BrowsingContext} The nested browsing context.
+ */
+async function getBrowsingContextInFrames(browsingContext, selectors) {
+ let context = browsingContext;
+
+ if (!Array.isArray(selectors)) {
+ throw new Error(
+ "getBrowsingContextInFrames called with an invalid selectors argument"
+ );
+ }
+
+ if (selectors.length === 0) {
+ throw new Error(
+ "getBrowsingContextInFrames called with an empty selectors array"
+ );
+ }
+
+ const clonedSelectors = [...selectors];
+ while (clonedSelectors.length) {
+ const selector = clonedSelectors.shift();
+ context = await SpecialPowers.spawn(context, [selector], _selector => {
+ return content.document.querySelector(_selector).browsingContext;
+ });
+ }
+
+ return context;
+}
+
+/**
+ * Synthesize a mouse event on an element, after ensuring that it is visible
+ * in the viewport.
+ *
+ * @param {String|Array} selector: The node selector to get the node target for the event.
+ * To target an element in a specific iframe, pass an array of CSS selectors
+ * (e.g. ["iframe", ".el-in-iframe"])
+ * @param {number} x
+ * @param {number} y
+ * @param {object} options: Options that will be passed to BrowserTestUtils.synthesizeMouse
+ */
+async function safeSynthesizeMouseEventInContentPage(
+ selector,
+ x,
+ y,
+ options = {}
+) {
+ let context = gBrowser.selectedBrowser.browsingContext;
+
+ // If an array of selector is passed, we need to retrieve the context in which the node
+ // lives in.
+ if (Array.isArray(selector)) {
+ if (selector.length === 1) {
+ selector = selector[0];
+ } else {
+ context = await getBrowsingContextInFrames(
+ context,
+ // only pass the iframe path
+ selector.slice(0, -1)
+ );
+ // retrieve the last item of the selector, which should be the one for the node we want.
+ selector = selector.at(-1);
+ }
+ }
+
+ await scrollContentPageNodeIntoView(context, selector);
+ BrowserTestUtils.synthesizeMouse(selector, x, y, options, context);
+}
+
+/**
+ * Synthesize a mouse event at the center of an element, after ensuring that it is visible
+ * in the viewport.
+ *
+ * @param {String|Array} selector: The node selector to get the node target for the event.
+ * To target an element in a specific iframe, pass an array of CSS selectors
+ * (e.g. ["iframe", ".el-in-iframe"])
+ * @param {object} options: Options that will be passed to BrowserTestUtils.synthesizeMouse
+ */
+async function safeSynthesizeMouseEventAtCenterInContentPage(
+ selector,
+ options = {}
+) {
+ let context = gBrowser.selectedBrowser.browsingContext;
+
+ // If an array of selector is passed, we need to retrieve the context in which the node
+ // lives in.
+ if (Array.isArray(selector)) {
+ if (selector.length === 1) {
+ selector = selector[0];
+ } else {
+ context = await getBrowsingContextInFrames(
+ context,
+ // only pass the iframe path
+ selector.slice(0, -1)
+ );
+ // retrieve the last item of the selector, which should be the one for the node we want.
+ selector = selector.at(-1);
+ }
+ }
+
+ await scrollContentPageNodeIntoView(context, selector);
+ BrowserTestUtils.synthesizeMouseAtCenter(selector, options, context);
+}
+
+/**
+ * Scroll into view an element in the content page matching the passed selector
+ *
+ * @param {BrowsingContext} browsingContext: The browsing context the element lives in.
+ * @param {String} selector: The node selector to get the node to scroll into view
+ * @returns {Promise}
+ */
+function scrollContentPageNodeIntoView(browsingContext, selector) {
+ return SpecialPowers.spawn(
+ browsingContext,
+ [selector],
+ function (innerSelector) {
+ const node =
+ content.wrappedJSObject.document.querySelector(innerSelector);
+ node.scrollIntoView();
+ }
+ );
+}
+
+/**
+ * Change the zoom level of the selected page.
+ *
+ * @param {Number} zoomLevel
+ */
+function setContentPageZoomLevel(zoomLevel) {
+ gBrowser.selectedBrowser.fullZoom = zoomLevel;
+}
+
+/**
+ * Wait for the next DOCUMENT_EVENT dom-complete resource on a top-level target
+ *
+ * @param {Object} commands
+ * @return {Promise<Object>}
+ * Return a promise which resolves once we fully settle the resource listener.
+ * You should await for its resolution before doing the action which may fire
+ * your resource.
+ * This promise will resolve with an object containing a `onDomCompleteResource` property,
+ * which is also a promise, that will resolve once a "top-level" DOCUMENT_EVENT dom-complete
+ * is received.
+ */
+async function waitForNextTopLevelDomCompleteResource(commands) {
+ const { onResource: onDomCompleteResource } =
+ await commands.resourceCommand.waitForNextResource(
+ commands.resourceCommand.TYPES.DOCUMENT_EVENT,
+ {
+ ignoreExistingResources: true,
+ predicate: resource =>
+ resource.name === "dom-complete" && resource.targetFront.isTopLevel,
+ }
+ );
+ return { onDomCompleteResource };
+}
+
+/**
+ * Wait for the provided context to have a valid presShell. This can be useful
+ * for tests which try to create popup panels or interact with the document very
+ * early.
+ *
+ * @param {BrowsingContext} context
+ **/
+function waitForPresShell(context) {
+ return SpecialPowers.spawn(context, [], async () => {
+ const winUtils = SpecialPowers.getDOMWindowUtils(content);
+ await ContentTaskUtils.waitForCondition(() => {
+ try {
+ return !!winUtils.getPresShellId();
+ } catch (e) {
+ return false;
+ }
+ }, "Waiting for a valid presShell");
+ });
+}
+
+/**
+ * In tests using Fluent localization, it is preferable to match DOM elements using
+ * a message ID rather than the raw string as:
+ *
+ * 1. It allows testing infrastructure to be multilingual if needed.
+ * 2. It isolates the tests from localization changes.
+ *
+ * @param {Array<string>} resourceIds A list of .ftl files to load.
+ * @returns {(id: string, args?: Record<string, FluentVariable>) => string}
+ */
+async function getFluentStringHelper(resourceIds) {
+ const locales = Services.locale.appLocalesAsBCP47;
+ const generator = L10nRegistry.getInstance().generateBundles(
+ locales,
+ resourceIds
+ );
+
+ const bundles = [];
+ for await (const bundle of generator) {
+ bundles.push(bundle);
+ }
+
+ const reactLocalization = new FluentReact.ReactLocalization(bundles);
+
+ /**
+ * Get the string from a message id. It throws when the message is not found.
+ *
+ * @param {string} id
+ * @param {string} attributeName: attribute name if you need to access a specific attribute
+ * defined in the fluent string, e.g. setting "title" for this param
+ * will retrieve the `title` string in
+ * compatibility-issue-browsers-list =
+ * .title = This is the title
+ * @param {Record<string, FluentVariable>} [args] optional
+ * @returns {string}
+ */
+ return (id, attributeName, args) => {
+ let string;
+
+ if (!attributeName) {
+ string = reactLocalization.getString(id, args);
+ } else {
+ for (const bundle of reactLocalization.bundles) {
+ const msg = bundle.getMessage(id);
+ if (msg?.attributes[attributeName]) {
+ string = bundle.formatPattern(
+ msg.attributes[attributeName],
+ args,
+ []
+ );
+ break;
+ }
+ }
+ }
+
+ if (!string) {
+ throw new Error(
+ `Could not find a string for "${id}"${
+ attributeName ? ` and attribute "${attributeName}")` : ""
+ }. Was the correct resource bundle loaded?`
+ );
+ }
+ return string;
+ };
+}
+
+/**
+ * Open responsive design mode for the given tab.
+ */
+async function openRDM(tab, { waitForDeviceList = true } = {}) {
+ info("Opening responsive design mode");
+ const manager = ResponsiveUIManager;
+ const ui = await manager.openIfNeeded(tab.ownerGlobal, tab, {
+ trigger: "test",
+ });
+ info("Responsive design mode opened");
+
+ await ResponsiveMessageHelper.wait(ui.toolWindow, "post-init");
+ info("Responsive design initialized");
+
+ await waitForRDMLoaded(ui, { waitForDeviceList });
+
+ return { ui, manager };
+}
+
+async function waitForRDMLoaded(ui, { waitForDeviceList = true } = {}) {
+ // Always wait for the viewport to be added.
+ const { store } = ui.toolWindow;
+ await waitUntilState(store, state => state.viewports.length == 1);
+
+ if (waitForDeviceList) {
+ // Wait until the device list has been loaded.
+ await waitUntilState(
+ store,
+ state => state.devices.listState == localTypes.loadableState.LOADED
+ );
+ }
+}
+
+/**
+ * Close responsive design mode for the given tab.
+ */
+async function closeRDM(tab, options) {
+ info("Closing responsive design mode");
+ const manager = ResponsiveUIManager;
+ await manager.closeIfNeeded(tab.ownerGlobal, tab, options);
+ info("Responsive design mode closed");
+}
+
+function getInputStream(data) {
+ const BufferStream = Components.Constructor(
+ "@mozilla.org/io/arraybuffer-input-stream;1",
+ "nsIArrayBufferInputStream",
+ "setData"
+ );
+ const buffer = new TextEncoder().encode(data).buffer;
+ return new BufferStream(buffer, 0, buffer.byteLength);
+}
+
+/**
+ * Wait for a specific target to have been fully processed by targetCommand.
+ *
+ * @param {Commands} commands
+ * The commands instance
+ * @param {Function} isExpectedTargetFn
+ * Predicate which will be called with a target front argument. Should
+ * return true if the target front is the expected one, false otherwise.
+ * @return {Promise}
+ * Promise which resolves when a target matching `isExpectedTargetFn`
+ * has been processed by targetCommand.
+ */
+function waitForTargetProcessed(commands, isExpectedTargetFn) {
+ return new Promise(resolve => {
+ const onProcessed = targetFront => {
+ try {
+ if (isExpectedTargetFn(targetFront)) {
+ commands.targetCommand.off("processed-available-target", onProcessed);
+ resolve();
+ }
+ } catch {
+ // Ignore errors from isExpectedTargetFn.
+ }
+ };
+
+ commands.targetCommand.on("processed-available-target", onProcessed);
+ });
+}
+
+/**
+ * Instantiate a HTTP Server that serves files from a given test folder.
+ * The test folder should be made of multiple sub folder named: v1, v2, v3,...
+ * We will serve the content from one of these sub folder
+ * and switch to the next one, each time `httpServer.switchToNextVersion()`
+ * is called.
+ *
+ * @return Object Test server with two functions:
+ * - urlFor(path)
+ * Returns the absolute url for a given file.
+ * - switchToNextVersion()
+ * Start serving files from the next available sub folder.
+ * - backToFirstVersion()
+ * When running more than one test, helps restart from the first folder.
+ */
+function createVersionizedHttpTestServer(testFolderName) {
+ const httpServer = createTestHTTPServer();
+
+ let currentVersion = 1;
+
+ httpServer.registerPrefixHandler("/", async (request, response) => {
+ response.processAsync();
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ if (request.path.endsWith(".js")) {
+ response.setHeader("Content-Type", "application/javascript");
+ } else if (request.path.endsWith(".js.map")) {
+ response.setHeader("Content-Type", "application/json");
+ }
+ if (request.path == "/" || request.path.endsWith(".html")) {
+ response.setHeader("Content-Type", "text/html");
+ }
+ // If a query string is passed, lookup with a matching file, if available
+ // The '?' is replaced by '.'
+ let fetchResponse;
+
+ if (request.queryString) {
+ const url = `${URL_ROOT_SSL}${testFolderName}/v${currentVersion}${request.path}.${request.queryString}`;
+ try {
+ fetchResponse = await fetch(url);
+ // Log this only if the request succeed
+ info(`[test-http-server] serving: ${url}`);
+ } catch (e) {
+ // Ignore any error and proceed without the query string
+ fetchResponse = null;
+ }
+ }
+
+ if (!fetchResponse) {
+ const url = `${URL_ROOT_SSL}${testFolderName}/v${currentVersion}${request.path}`;
+ info(`[test-http-server] serving: ${url}`);
+ fetchResponse = await fetch(url);
+ }
+
+ // Ensure forwarding the response headers generated by the other http server
+ // (this can be especially useful when query .sjs files)
+ for (const [name, value] of fetchResponse.headers.entries()) {
+ response.setHeader(name, value);
+ }
+
+ // Override cache settings so that versionized requests are never cached
+ // and we get brand new content for any request.
+ response.setHeader("Cache-Control", "no-store");
+
+ const text = await fetchResponse.text();
+ response.write(text);
+ response.finish();
+ });
+
+ return {
+ switchToNextVersion() {
+ currentVersion++;
+ },
+ backToFirstVersion() {
+ currentVersion = 1;
+ },
+ urlFor(path) {
+ const port = httpServer.identity.primaryPort;
+ return `http://localhost:${port}/${path}`;
+ },
+ };
+}
+
+/**
+ * Fake clicking a link and return the URL we would have navigated to.
+ * This function should be used to check external links since we can't access
+ * network in tests.
+ * This can also be used to test that a click will not be fired.
+ *
+ * @param ElementNode element
+ * The <a> element we want to simulate click on.
+ * @returns Promise
+ * A Promise that is resolved when the link click simulation occured or
+ * when the click is not dispatched.
+ * The promise resolves with an object that holds the following properties
+ * - link: url of the link or null(if event not fired)
+ * - where: "tab" if tab is active or "tabshifted" if tab is inactive
+ * or null(if event not fired)
+ */
+function simulateLinkClick(element) {
+ const browserWindow = Services.wm.getMostRecentWindow(
+ gDevTools.chromeWindowType
+ );
+
+ const onOpenLink = new Promise(resolve => {
+ const openLinkIn = (link, where) => resolve({ link, where });
+ sinon.replace(browserWindow, "openTrustedLinkIn", openLinkIn);
+ sinon.replace(browserWindow, "openWebLinkIn", openLinkIn);
+ });
+
+ element.click();
+
+ // Declare a timeout Promise that we can use to make sure spied methods were not called.
+ const onTimeout = new Promise(function (resolve) {
+ setTimeout(() => {
+ resolve({ link: null, where: null });
+ }, 1000);
+ });
+
+ const raceResult = Promise.race([onOpenLink, onTimeout]);
+ sinon.restore();
+ return raceResult;
+}
+
+/**
+ * Since the MDN data is updated frequently, it might happen that the properties used in
+ * this test are not in the dataset anymore/now have URLs.
+ * This function will return properties in the dataset that don't have MDN url so you
+ * can easily find a replacement.
+ */
+function logCssCompatDataPropertiesWithoutMDNUrl() {
+ const cssPropertiesCompatData = require("resource://devtools/shared/compatibility/dataset/css-properties.json");
+
+ function walk(node) {
+ for (const propertyName in node) {
+ const property = node[propertyName];
+ if (property.__compat) {
+ if (!property.__compat.mdn_url) {
+ dump(
+ `"${propertyName}" - MDN URL: ${
+ property.__compat.mdn_url || "❌"
+ } - Spec URL: ${property.__compat.spec_url || "❌"}\n`
+ );
+ }
+ } else if (typeof property == "object") {
+ walk(property);
+ }
+ }
+ }
+ walk(cssPropertiesCompatData);
+}
+
+/**
+ * Craft a CssProperties instance without involving RDP for tests
+ * manually spawning OutputParser, CssCompleter, Editor...
+ *
+ * Otherwise this should instead be fetched from CssPropertiesFront.
+ *
+ * @return {CssProperties}
+ */
+function getClientCssProperties() {
+ const {
+ generateCssProperties,
+ } = require("resource://devtools/server/actors/css-properties.js");
+ const {
+ CssProperties,
+ normalizeCssData,
+ } = require("resource://devtools/client/fronts/css-properties.js");
+ return new CssProperties(
+ normalizeCssData({ properties: generateCssProperties(document) })
+ );
+}
+
+/**
+ * Helper method to stop a Service Worker promptly.
+ *
+ * @param {String} workerUrl
+ * Absolute Worker URL to stop.
+ */
+async function stopServiceWorker(workerUrl) {
+ info(`Stop Service Worker: ${workerUrl}\n`);
+
+ // Help the SW to be immediately destroyed after unregistering it.
+ Services.prefs.setIntPref("dom.serviceWorkers.idle_timeout", 0);
+
+ const swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService(
+ Ci.nsIServiceWorkerManager
+ );
+ // Unfortunately we can't use swm.getRegistrationByPrincipal, as it requires a "scope", which doesn't seem to be the worker URL.
+ // So let's use getAllRegistrations to find the nsIServiceWorkerInfo in order to:
+ // - retrieve its active worker,
+ // - call attach+detachDebugger,
+ // - reset the idle timeout.
+ // This way, the unregister instruction is immediate, thanks to the 0 dom.serviceWorkers.idle_timeout we set at the beginning of the function
+ const registrations = swm.getAllRegistrations();
+ let matchedInfo;
+ for (let i = 0; i < registrations.length; i++) {
+ const info = registrations.queryElementAt(
+ i,
+ Ci.nsIServiceWorkerRegistrationInfo
+ );
+ // Lookup for an exact URL match.
+ if (info.scriptSpec === workerUrl) {
+ matchedInfo = info;
+ break;
+ }
+ }
+ ok(!!matchedInfo, "Found the service worker info");
+
+ info("Wait for the worker to be active");
+ await waitFor(() => matchedInfo.activeWorker, "Wait for the SW to be active");
+
+ // We need to attach+detach the debugger in order to reset the idle timeout.
+ // Otherwise the worker would still be waiting for a previously registered timeout
+ // which would be the 0ms one we set by tweaking the preference.
+ function resetWorkerTimeout(worker) {
+ worker.attachDebugger();
+ worker.detachDebugger();
+ }
+ resetWorkerTimeout(matchedInfo.activeWorker);
+ // Also reset all the other possible worker instances
+ if (matchedInfo.evaluatingWorker) {
+ resetWorkerTimeout(matchedInfo.evaluatingWorker);
+ }
+ if (matchedInfo.installingWorker) {
+ resetWorkerTimeout(matchedInfo.installingWorker);
+ }
+ if (matchedInfo.waitingWorker) {
+ resetWorkerTimeout(matchedInfo.waitingWorker);
+ }
+ // Reset this preference in order to ensure other SW are not immediately destroyed.
+ Services.prefs.clearUserPref("dom.serviceWorkers.idle_timeout");
+
+ // Spin the event loop to ensure the worker had time to really be shut down.
+ await wait(0);
+
+ return matchedInfo;
+}
+
+/**
+ * Helper method to stop and unregister a Service Worker promptly.
+ *
+ * @param {String} workerUrl
+ * Absolute Worker URL to unregister.
+ */
+async function unregisterServiceWorker(workerUrl) {
+ const swInfo = await stopServiceWorker(workerUrl);
+
+ info(`Unregister Service Worker: ${workerUrl}\n`);
+ // Now call unregister on that worker so that it can be destroyed immediately
+ const swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService(
+ Ci.nsIServiceWorkerManager
+ );
+ const unregisterSuccess = await new Promise(resolve => {
+ swm.unregister(
+ swInfo.principal,
+ {
+ unregisterSucceeded(success) {
+ resolve(success);
+ },
+ },
+ swInfo.scope
+ );
+ });
+ ok(unregisterSuccess, "Service worker successfully unregistered");
+}
diff --git a/devtools/client/shared/test/telemetry-test-helpers.js b/devtools/client/shared/test/telemetry-test-helpers.js
new file mode 100644
index 0000000000..2a5032f466
--- /dev/null
+++ b/devtools/client/shared/test/telemetry-test-helpers.js
@@ -0,0 +1,273 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* global is ok registerCleanupFunction Services */
+
+"use strict";
+
+// We try to avoid polluting the global scope as far as possible by defining
+// constants in the methods that use them because this script is not sandboxed
+// meaning that it is loaded via Services.scriptloader.loadSubScript()
+
+class TelemetryHelpers {
+ constructor() {
+ this.oldCanRecord = Services.telemetry.canRecordExtended;
+ this.generateTelemetryTests = this.generateTelemetryTests.bind(this);
+ registerCleanupFunction(this.stopTelemetry.bind(this));
+ }
+
+ /**
+ * Allow collection of extended telemetry data.
+ */
+ startTelemetry() {
+ Services.telemetry.canRecordExtended = true;
+ }
+
+ /**
+ * Clear all telemetry types.
+ */
+ stopTelemetry() {
+ // Clear histograms, scalars and Telemetry Events.
+ this.clearHistograms(Services.telemetry.getSnapshotForHistograms);
+ this.clearHistograms(Services.telemetry.getSnapshotForKeyedHistograms);
+ Services.telemetry.clearScalars();
+ Services.telemetry.clearEvents();
+
+ Services.telemetry.canRecordExtended = this.oldCanRecord;
+ }
+
+ /**
+ * Clears Telemetry Histograms.
+ *
+ * @param {Function} snapshotFunc
+ * The function used to take the snapshot. This can be one of the
+ * following:
+ * - Services.telemetry.getSnapshotForHistograms
+ * - Services.telemetry.getSnapshotForKeyedHistograms
+ */
+ clearHistograms(snapshotFunc) {
+ snapshotFunc("main", true);
+ }
+
+ /**
+ * Check the value of a given telemetry histogram.
+ *
+ * @param {String} histId
+ * Histogram id
+ * @param {String} key
+ * Keyed histogram key
+ * @param {Array|Number} expected
+ * Expected value
+ * @param {String} checkType
+ * "array" (default) - Check that an array matches the histogram data.
+ * "hasentries" - For non-enumerated linear and exponential
+ * histograms. This checks for at least one entry.
+ * "scalar" - Telemetry type is a scalar.
+ * "keyedscalar" - Telemetry type is a keyed scalar.
+ */
+ checkTelemetry(histId, key, expected, checkType) {
+ let actual;
+ let msg;
+
+ if (checkType === "array" || checkType === "hasentries") {
+ if (key) {
+ const keyedHistogram = Services.telemetry
+ .getKeyedHistogramById(histId)
+ .snapshot();
+ const result = keyedHistogram[key];
+
+ if (result) {
+ actual = result.values;
+ } else {
+ ok(false, `${histId}[${key}] exists`);
+ return;
+ }
+ } else {
+ actual = Services.telemetry.getHistogramById(histId).snapshot().values;
+ }
+ }
+
+ switch (checkType) {
+ case "array":
+ msg = key ? `${histId}["${key}"] correct.` : `${histId} correct.`;
+ is(JSON.stringify(actual), JSON.stringify(expected), msg);
+ break;
+ case "hasentries":
+ const hasEntry = Object.values(actual).some(num => num > 0);
+ if (key) {
+ ok(hasEntry, `${histId}["${key}"] has at least one entry.`);
+ } else {
+ ok(hasEntry, `${histId} has at least one entry.`);
+ }
+ break;
+ case "scalar":
+ const scalars = Services.telemetry.getSnapshotForScalars(
+ "main",
+ false
+ ).parent;
+
+ is(scalars[histId], expected, `${histId} correct`);
+ break;
+ case "keyedscalar":
+ const keyedScalars = Services.telemetry.getSnapshotForKeyedScalars(
+ "main",
+ false
+ ).parent;
+ const value = keyedScalars[histId][key];
+
+ msg = key ? `${histId}["${key}"] correct.` : `${histId} correct.`;
+ is(value, expected, msg);
+ break;
+ }
+ }
+
+ /**
+ * Generate telemetry tests. You should call generateTelemetryTests("DEVTOOLS_")
+ * from your result checking code in telemetry tests. It logs checkTelemetry
+ * calls for all changed telemetry values.
+ *
+ * @param {String} prefix
+ * Optionally limits results to histogram ids starting with prefix.
+ */
+ generateTelemetryTests(prefix = "") {
+ // Get all histograms and scalars
+ const histograms = Services.telemetry.getSnapshotForHistograms(
+ "main",
+ true
+ ).parent;
+ const keyedHistograms = Services.telemetry.getSnapshotForKeyedHistograms(
+ "main",
+ true
+ ).parent;
+ const scalars = Services.telemetry.getSnapshotForScalars(
+ "main",
+ false
+ ).parent;
+ const keyedScalars = Services.telemetry.getSnapshotForKeyedScalars(
+ "main",
+ false
+ ).parent;
+ const allHistograms = Object.assign(
+ {},
+ histograms,
+ keyedHistograms,
+ scalars,
+ keyedScalars
+ );
+ // Get all keys
+ const histIds = Object.keys(allHistograms).filter(histId =>
+ histId.startsWith(prefix)
+ );
+
+ dump("=".repeat(80) + "\n");
+ for (const histId of histIds) {
+ const snapshot = allHistograms[histId];
+
+ if (histId === histId.toLowerCase()) {
+ if (typeof snapshot === "object") {
+ // Keyed Scalar
+ const keys = Object.keys(snapshot);
+
+ for (const key of keys) {
+ const value = snapshot[key];
+
+ dump(
+ `checkTelemetry("${histId}", "${key}", ${value}, "keyedscalar");\n`
+ );
+ }
+ } else {
+ // Scalar
+ dump(`checkTelemetry("${histId}", "", ${snapshot}, "scalar");\n`);
+ }
+ } else if (
+ typeof snapshot.histogram_type !== "undefined" &&
+ typeof snapshot.values !== "undefined"
+ ) {
+ // Histogram
+ const actual = snapshot.values;
+
+ this.displayDataFromHistogramSnapshot(snapshot, "", histId, actual);
+ } else {
+ // Keyed Histogram
+ const keys = Object.keys(snapshot);
+
+ for (const key of keys) {
+ const value = snapshot[key];
+ const actual = value.counts;
+
+ this.displayDataFromHistogramSnapshot(value, key, histId, actual);
+ }
+ }
+ }
+ dump("=".repeat(80) + "\n");
+ }
+
+ /**
+ * Generates the inner contents of a test's checkTelemetry() method.
+ *
+ * @param {HistogramSnapshot} snapshot
+ * A snapshot of a telemetry chart obtained via getSnapshotForHistograms or
+ * similar.
+ * @param {String} key
+ * Only used for keyed histograms. This is the key we are interested in
+ * checking.
+ * @param {String} histId
+ * The histogram ID.
+ * @param {Array|String|Boolean} actual
+ * The value of the histogram data.
+ */
+ displayDataFromHistogramSnapshot(snapshot, key, histId, actual) {
+ key = key ? `"${key}"` : `""`;
+
+ switch (snapshot.histogram_type) {
+ case Services.telemetry.HISTOGRAM_EXPONENTIAL:
+ case Services.telemetry.HISTOGRAM_LINEAR:
+ let total = 0;
+ for (const val of Object.values(actual)) {
+ total += val;
+ }
+
+ if (histId.endsWith("_ENUMERATED")) {
+ if (total > 0) {
+ actual = actual.toSource();
+ dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`);
+ }
+ return;
+ }
+
+ dump(`checkTelemetry("${histId}", ${key}, null, "hasentries");\n`);
+ break;
+ case Services.telemetry.HISTOGRAM_BOOLEAN:
+ actual = actual.toSource();
+
+ if (actual !== "({})") {
+ dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`);
+ }
+ break;
+ case Services.telemetry.HISTOGRAM_FLAG:
+ actual = actual.toSource();
+
+ if (actual !== "({0:1, 1:0})") {
+ dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`);
+ }
+ break;
+ case Services.telemetry.HISTOGRAM_COUNT:
+ actual = actual.toSource();
+
+ dump(`checkTelemetry("${histId}", ${key}, ${actual}, "array");\n`);
+ break;
+ }
+ }
+}
+
+// "exports"... because this is a helper and not imported via require we need to
+// expose the three main methods that should be used by tests. The reason this
+// is not imported via require is because it needs access to test methods
+// (is, ok etc).
+
+/* eslint-disable no-unused-vars */
+const telemetryHelpers = new TelemetryHelpers();
+const generateTelemetryTests = telemetryHelpers.generateTelemetryTests;
+const checkTelemetry = telemetryHelpers.checkTelemetry;
+const startTelemetry = telemetryHelpers.startTelemetry;
+/* eslint-enable no-unused-vars */
diff --git a/devtools/client/shared/test/test-mocked-module.js b/devtools/client/shared/test/test-mocked-module.js
new file mode 100644
index 0000000000..0063c1c427
--- /dev/null
+++ b/devtools/client/shared/test/test-mocked-module.js
@@ -0,0 +1,11 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const methodToMock = function () {
+ return "Original value";
+};
+
+exports.methodToMock = methodToMock;
+exports.someProperty = "someProperty";
diff --git a/devtools/client/shared/test/testactors.js b/devtools/client/shared/test/testactors.js
new file mode 100644
index 0000000000..79b0240723
--- /dev/null
+++ b/devtools/client/shared/test/testactors.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
+
+class TestActor1 extends Actor {
+ constructor(conn, tab) {
+ super(conn, { typeName: "testOne", methods: [] });
+ this.tab = tab;
+
+ this.requestTypes = {
+ ping: TestActor1.prototype.onPing,
+ };
+ }
+
+ grip() {
+ return { actor: this.actorID, test: "TestActor1" };
+ }
+
+ onPing() {
+ return { pong: "pong" };
+ }
+}
+
+exports.TestActor1 = TestActor1;
diff --git a/devtools/client/shared/test/xpcshell/.eslintrc.js b/devtools/client/shared/test/xpcshell/.eslintrc.js
new file mode 100644
index 0000000000..8611c174f5
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/.eslintrc.js
@@ -0,0 +1,6 @@
+"use strict";
+
+module.exports = {
+ // Extend from the common devtools xpcshell eslintrc config.
+ extends: "../../../../.eslintrc.xpcshell.js",
+};
diff --git a/devtools/client/shared/test/xpcshell/head.js b/devtools/client/shared/test/xpcshell/head.js
new file mode 100644
index 0000000000..e65552771e
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/head.js
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* exported require */
+
+"use strict";
+
+var { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+);
diff --git a/devtools/client/shared/test/xpcshell/test_VariablesView_getString_promise.js b/devtools/client/shared/test/xpcshell/test_VariablesView_getString_promise.js
new file mode 100644
index 0000000000..feaec04fd0
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_VariablesView_getString_promise.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { VariablesView } = ChromeUtils.importESModule(
+ "resource://devtools/client/storage/VariablesView.sys.mjs"
+);
+
+const PENDING = {
+ type: "object",
+ class: "Promise",
+ actor: "conn0.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ promiseState: {
+ state: "pending",
+ },
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {},
+ },
+};
+
+const FULFILLED = {
+ type: "object",
+ class: "Promise",
+ actor: "conn0.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ promiseState: {
+ state: "fulfilled",
+ value: 10,
+ },
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {},
+ },
+};
+
+const REJECTED = {
+ type: "object",
+ class: "Promise",
+ actor: "conn0.obj35",
+ extensible: true,
+ frozen: false,
+ sealed: false,
+ promiseState: {
+ state: "rejected",
+ reason: 10,
+ },
+ preview: {
+ kind: "Object",
+ ownProperties: {},
+ ownPropertiesLength: 0,
+ safeGetterValues: {},
+ },
+};
+
+function run_test() {
+ equal(VariablesView.getString(PENDING, { concise: true }), "Promise");
+ equal(VariablesView.getString(PENDING), 'Promise {<state>: "pending"}');
+
+ equal(VariablesView.getString(FULFILLED, { concise: true }), "Promise");
+ equal(
+ VariablesView.getString(FULFILLED),
+ 'Promise {<state>: "fulfilled", <value>: 10}'
+ );
+
+ equal(VariablesView.getString(REJECTED, { concise: true }), "Promise");
+ equal(
+ VariablesView.getString(REJECTED),
+ 'Promise {<state>: "rejected", <reason>: 10}'
+ );
+}
diff --git a/devtools/client/shared/test/xpcshell/test_WeakMapMap.js b/devtools/client/shared/test/xpcshell/test_WeakMapMap.js
new file mode 100644
index 0000000000..94a006265b
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_WeakMapMap.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test WeakMapMap.
+
+"use strict";
+
+const WeakMapMap = require("resource://devtools/client/shared/WeakMapMap.js");
+
+const myWeakMapMap = new WeakMapMap();
+const key = { randomObject: true };
+
+// eslint-disable-next-line
+function run_test() {
+ test_set();
+ test_has();
+ test_get();
+ test_delete();
+ test_clear();
+}
+
+function test_set() {
+ // myWeakMapMap.set
+ myWeakMapMap.set(key, "text1", "value1");
+ myWeakMapMap.set(key, "text2", "value2");
+ myWeakMapMap.set(key, "text3", "value3");
+}
+
+function test_has() {
+ // myWeakMapMap.has
+ ok(myWeakMapMap.has(key, "text1"), "text1 exists");
+ ok(myWeakMapMap.has(key, "text2"), "text2 exists");
+ ok(myWeakMapMap.has(key, "text3"), "text3 exists");
+ ok(!myWeakMapMap.has(key, "notakey"), "notakey does not exist");
+}
+
+function test_get() {
+ // myWeakMapMap.get
+ const value1 = myWeakMapMap.get(key, "text1");
+ equal(value1, "value1", "test value1");
+
+ const value2 = myWeakMapMap.get(key, "text2");
+ equal(value2, "value2", "test value2");
+
+ const value3 = myWeakMapMap.get(key, "text3");
+ equal(value3, "value3", "test value3");
+
+ const value4 = myWeakMapMap.get(key, "notakey");
+ equal(value4, undefined, "test value4");
+}
+
+function test_delete() {
+ // myWeakMapMap.delete
+ myWeakMapMap.delete(key, "text2");
+
+ // Check that the correct entry was deleted
+ ok(myWeakMapMap.has(key, "text1"), "text1 exists");
+ ok(!myWeakMapMap.has(key, "text2"), "text2 no longer exists");
+ ok(myWeakMapMap.has(key, "text3"), "text3 exists");
+}
+
+function test_clear() {
+ // myWeakMapMap.clear
+ myWeakMapMap.clear();
+
+ // Ensure myWeakMapMap was properly cleared
+ ok(!myWeakMapMap.has(key, "text1"), "text1 no longer exists");
+ ok(!myWeakMapMap.has(key, "text3"), "text3 no longer exists");
+}
diff --git a/devtools/client/shared/test/xpcshell/test_advanceValidate.js b/devtools/client/shared/test/xpcshell/test_advanceValidate.js
new file mode 100644
index 0000000000..47ad4b92c7
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_advanceValidate.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the advanceValidate function from rule-view.js.
+
+const {
+ advanceValidate,
+} = require("resource://devtools/client/inspector/shared/utils.js");
+const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
+
+// 1 2 3
+// 0123456789012345678901234567890
+const sampleInput = '\\symbol "string" url(somewhere)';
+
+function testInsertion(where, result, testName) {
+ info(testName);
+ equal(
+ advanceValidate(KeyCodes.DOM_VK_SEMICOLON, sampleInput, where),
+ result,
+ "testing advanceValidate at " + where
+ );
+}
+
+function run_test() {
+ testInsertion(4, true, "inside a symbol");
+ testInsertion(1, false, "after a backslash");
+ testInsertion(8, true, "after whitespace");
+ testInsertion(11, false, "inside a string");
+ testInsertion(24, false, "inside a URL");
+ testInsertion(31, true, "at the end");
+}
diff --git a/devtools/client/shared/test/xpcshell/test_attribute-parsing-01.js b/devtools/client/shared/test/xpcshell/test_attribute-parsing-01.js
new file mode 100644
index 0000000000..f3c0c159cd
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_attribute-parsing-01.js
@@ -0,0 +1,77 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test splitBy from node-attribute-parser.js
+
+const {
+ splitBy,
+} = require("resource://devtools/client/shared/node-attribute-parser.js");
+
+const TEST_DATA = [
+ {
+ value: "this is a test",
+ splitChar: " ",
+ expected: [
+ { value: "this" },
+ { value: " ", type: "string" },
+ { value: "is" },
+ { value: " ", type: "string" },
+ { value: "a" },
+ { value: " ", type: "string" },
+ { value: "test" },
+ ],
+ },
+ {
+ value: "/path/to/handler",
+ splitChar: " ",
+ expected: [{ value: "/path/to/handler" }],
+ },
+ {
+ value: "test",
+ splitChar: " ",
+ expected: [{ value: "test" }],
+ },
+ {
+ value: " test ",
+ splitChar: " ",
+ expected: [
+ { value: " ", type: "string" },
+ { value: "test" },
+ { value: " ", type: "string" },
+ ],
+ },
+ {
+ value: "",
+ splitChar: " ",
+ expected: [],
+ },
+ {
+ value: " ",
+ splitChar: " ",
+ expected: [
+ { value: " ", type: "string" },
+ { value: " ", type: "string" },
+ { value: " ", type: "string" },
+ ],
+ },
+];
+
+function run_test() {
+ for (const { value, splitChar, expected } of TEST_DATA) {
+ info("Splitting string: " + value);
+ const tokens = splitBy(value, splitChar);
+
+ info("Checking that the number of parsed tokens is correct");
+ Assert.equal(tokens.length, expected.length);
+
+ for (let i = 0; i < tokens.length; i++) {
+ info("Checking the data in token " + i);
+ Assert.equal(tokens[i].value, expected[i].value);
+ if (expected[i].type) {
+ Assert.equal(tokens[i].type, expected[i].type);
+ }
+ }
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/test_attribute-parsing-02.js b/devtools/client/shared/test/xpcshell/test_attribute-parsing-02.js
new file mode 100644
index 0000000000..2cc05574dd
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_attribute-parsing-02.js
@@ -0,0 +1,148 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test parseAttribute from node-attribute-parser.js
+
+const {
+ parseAttribute,
+} = require("resource://devtools/client/shared/node-attribute-parser.js");
+
+const TEST_DATA = [
+ {
+ tagName: "body",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "class",
+ attributeValue: "some css class names",
+ expected: [{ value: "some css class names", type: "string" }],
+ },
+ {
+ tagName: "box",
+ namespaceURI:
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ attributeName: "datasources",
+ attributeValue: "/url/1?test=1#test http://mozilla.org/wow",
+ expected: [
+ { value: "/url/1?test=1#test", type: "uri" },
+ { value: " ", type: "string" },
+ { value: "http://mozilla.org/wow", type: "uri" },
+ ],
+ },
+ {
+ tagName: "form",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "action",
+ attributeValue: "/path/to/handler",
+ expected: [{ value: "/path/to/handler", type: "uri" }],
+ },
+ {
+ tagName: "a",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "ping",
+ attributeValue:
+ "http://analytics.com/track?id=54 http://analytics.com/track?id=55",
+ expected: [
+ { value: "http://analytics.com/track?id=54", type: "uri" },
+ { value: " ", type: "string" },
+ { value: "http://analytics.com/track?id=55", type: "uri" },
+ ],
+ },
+ {
+ tagName: "link",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "href",
+ attributeValue: "styles.css",
+ otherAttributes: [{ name: "rel", value: "stylesheet" }],
+ expected: [{ value: "styles.css", type: "cssresource" }],
+ },
+ {
+ tagName: "link",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "href",
+ attributeValue: "styles.css",
+ expected: [{ value: "styles.css", type: "uri" }],
+ },
+ {
+ tagName: "output",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "for",
+ attributeValue: "element-id something id",
+ expected: [
+ { value: "element-id", type: "idref" },
+ { value: " ", type: "string" },
+ { value: "something", type: "idref" },
+ { value: " ", type: "string" },
+ { value: "id", type: "idref" },
+ ],
+ },
+ {
+ tagName: "img",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "contextmenu",
+ attributeValue: "id-of-menu",
+ expected: [{ value: "id-of-menu", type: "idref" }],
+ },
+ {
+ tagName: "img",
+ namespaceURI: "http://www.w3.org/1999/xhtml",
+ attributeName: "src",
+ attributeValue: "omg-thats-so-funny.gif",
+ expected: [{ value: "omg-thats-so-funny.gif", type: "uri" }],
+ },
+ {
+ tagName: "key",
+ namespaceURI:
+ "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+ attributeName: "command",
+ attributeValue: "some_command_id",
+ expected: [{ value: "some_command_id", type: "idref" }],
+ },
+ {
+ tagName: "script",
+ namespaceURI: "whatever",
+ attributeName: "src",
+ attributeValue: "script.js",
+ expected: [{ value: "script.js", type: "jsresource" }],
+ },
+];
+
+function run_test() {
+ for (const {
+ tagName,
+ namespaceURI,
+ attributeName,
+ otherAttributes,
+ attributeValue,
+ expected,
+ } of TEST_DATA) {
+ info(
+ "Testing <" + tagName + " " + attributeName + "='" + attributeValue + "'>"
+ );
+
+ const attributes = [
+ ...(otherAttributes || []),
+ { name: attributeName, value: attributeValue },
+ ];
+ const tokens = parseAttribute(
+ namespaceURI,
+ tagName,
+ attributes,
+ attributeName,
+ attributeValue
+ );
+ if (!expected) {
+ Assert.ok(!tokens);
+ continue;
+ }
+
+ info("Checking that the number of parsed tokens is correct");
+ Assert.equal(tokens.length, expected.length);
+
+ for (let i = 0; i < tokens.length; i++) {
+ info("Checking the data in token " + i);
+ Assert.equal(tokens[i].value, expected[i].value);
+ Assert.equal(tokens[i].type, expected[i].type);
+ }
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/test_bezierCanvas.js b/devtools/client/shared/test/xpcshell/test_bezierCanvas.js
new file mode 100644
index 0000000000..d7fac599c5
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_bezierCanvas.js
@@ -0,0 +1,122 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the BezierCanvas API in the CubicBezierWidget module
+
+var {
+ CubicBezier,
+ BezierCanvas,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+
+function run_test() {
+ offsetsGetterReturnsData();
+ convertsOffsetsToCoordinates();
+ plotsCanvas();
+}
+
+function offsetsGetterReturnsData() {
+ info("offsets getter returns an array of 2 offset objects");
+
+ let b = new BezierCanvas(getCanvasMock(), getCubicBezier(), [0.25, 0]);
+ let offsets = b.offsets;
+
+ Assert.equal(offsets.length, 2);
+
+ Assert.ok("top" in offsets[0]);
+ Assert.ok("left" in offsets[0]);
+ Assert.ok("top" in offsets[1]);
+ Assert.ok("left" in offsets[1]);
+
+ Assert.equal(offsets[0].top, "300px");
+ Assert.equal(offsets[0].left, "0px");
+ Assert.equal(offsets[1].top, "100px");
+ Assert.equal(offsets[1].left, "200px");
+
+ info("offsets getter returns data according to current padding");
+
+ b = new BezierCanvas(getCanvasMock(), getCubicBezier(), [0, 0]);
+ offsets = b.offsets;
+
+ Assert.equal(offsets[0].top, "400px");
+ Assert.equal(offsets[0].left, "0px");
+ Assert.equal(offsets[1].top, "0px");
+ Assert.equal(offsets[1].left, "200px");
+}
+
+function convertsOffsetsToCoordinates() {
+ info("Converts offsets to coordinates");
+
+ const b = new BezierCanvas(getCanvasMock(), getCubicBezier(), [0.25, 0]);
+
+ let coordinates = b.offsetsToCoordinates({
+ style: {
+ left: "0px",
+ top: "0px",
+ },
+ });
+ Assert.equal(coordinates.length, 2);
+ Assert.equal(coordinates[0], 0);
+ Assert.equal(coordinates[1], 1.5);
+
+ coordinates = b.offsetsToCoordinates({
+ style: {
+ left: "0px",
+ top: "300px",
+ },
+ });
+ Assert.equal(coordinates[0], 0);
+ Assert.equal(coordinates[1], 0);
+
+ coordinates = b.offsetsToCoordinates({
+ style: {
+ left: "200px",
+ top: "100px",
+ },
+ });
+ Assert.equal(coordinates[0], 1);
+ Assert.equal(coordinates[1], 1);
+}
+
+function plotsCanvas() {
+ info("Plots the curve to the canvas");
+
+ let hasDrawnCurve = false;
+ const b = new BezierCanvas(getCanvasMock(), getCubicBezier(), [0.25, 0]);
+ b.ctx.bezierCurveTo = () => {
+ hasDrawnCurve = true;
+ };
+ b.plot();
+
+ Assert.ok(hasDrawnCurve);
+}
+
+function getCubicBezier() {
+ return new CubicBezier([0, 0, 1, 1]);
+}
+
+function getCanvasMock(w = 200, h = 400) {
+ return {
+ getContext() {
+ return {
+ scale: () => {},
+ translate: () => {},
+ clearRect: () => {},
+ beginPath: () => {},
+ closePath: () => {},
+ moveTo: () => {},
+ lineTo: () => {},
+ stroke: () => {},
+ arc: () => {},
+ fill: () => {},
+ bezierCurveTo: () => {},
+ save: () => {},
+ restore: () => {},
+ setTransform: () => {},
+ };
+ },
+ width: w,
+ height: h,
+ };
+}
diff --git a/devtools/client/shared/test/xpcshell/test_classnames.js b/devtools/client/shared/test/xpcshell/test_classnames.js
new file mode 100644
index 0000000000..22a98c47da
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_classnames.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests utility function in `classnames.js`
+ */
+
+const classnames = require("resource://devtools/client/shared/classnames.js");
+
+add_task(async function () {
+ Assert.equal(
+ classnames(),
+ "",
+ "Returns an empty string when called with no params"
+ );
+ Assert.equal(
+ classnames(null, undefined, false),
+ "",
+ "Returns an empty string when called with only falsy params"
+ );
+ Assert.equal(
+ classnames("hello"),
+ "hello",
+ "Returns expected result when string is passed"
+ );
+ Assert.equal(
+ classnames("hello", "", "world"),
+ "hello world",
+ "Doesn't add extra spaces for empty strings"
+ );
+ Assert.equal(
+ classnames("hello", null, undefined, false, "world"),
+ "hello world",
+ "Doesn't add extra spaces for falsy values"
+ );
+ Assert.equal(
+ classnames("hello", { nice: true, blue: 42, world: {} }),
+ "hello nice blue world",
+ "Add property key when property value is truthy"
+ );
+ Assert.equal(
+ classnames("hello", { nice: false, blue: null, world: false }),
+ "hello",
+ "Does not add property key when property value is falsy"
+ );
+ Assert.equal(
+ classnames("hello", { nice: true }, { blue: true }, "world"),
+ "hello nice blue world",
+ "Handles multiple objects"
+ );
+});
diff --git a/devtools/client/shared/test/xpcshell/test_cssAngle.js b/devtools/client/shared/test/xpcshell/test_cssAngle.js
new file mode 100644
index 0000000000..a1ddcdf254
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_cssAngle.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test classifyAngle.
+
+"use strict";
+
+const {
+ angleUtils,
+} = require("resource://devtools/client/shared/css-angle.js");
+
+const CLASSIFY_TESTS = [
+ { input: "180deg", output: "deg" },
+ { input: "-180deg", output: "deg" },
+ { input: "180DEG", output: "deg" },
+ { input: "200rad", output: "rad" },
+ { input: "-200rad", output: "rad" },
+ { input: "200RAD", output: "rad" },
+ { input: "0.5grad", output: "grad" },
+ { input: "-0.5grad", output: "grad" },
+ { input: "0.5GRAD", output: "grad" },
+ { input: "0.33turn", output: "turn" },
+ { input: "0.33TURN", output: "turn" },
+ { input: "-0.33turn", output: "turn" },
+];
+
+function run_test() {
+ for (const test of CLASSIFY_TESTS) {
+ const result = angleUtils.classifyAngle(test.input);
+ equal(result, test.output, "test classifyAngle(" + test.input + ")");
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/test_cssColor-01.js b/devtools/client/shared/test/xpcshell/test_cssColor-01.js
new file mode 100644
index 0000000000..bccd66a0a4
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_cssColor-01.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test classifyColor.
+
+"use strict";
+
+const { colorUtils } = require("resource://devtools/shared/css/color.js");
+
+const CLASSIFY_TESTS = [
+ { input: "rgb(255,0,192)", output: "rgb" },
+ { input: "RGB(255,0,192)", output: "rgb" },
+ { input: "RGB(100%,0%,83%)", output: "rgb" },
+ { input: "rgba(255,0,192, 0.25)", output: "rgb" },
+ { input: "hsl(5, 5%, 5%)", output: "hsl" },
+ { input: "hsla(5, 5%, 5%, 0.25)", output: "hsl" },
+ { input: "hSlA(5, 5%, 5%, 0.25)", output: "hsl" },
+ { input: "#f06", output: "hex" },
+ { input: "#f060", output: "hex" },
+ { input: "#fe01cb", output: "hex" },
+ { input: "#fe01cb80", output: "hex" },
+ { input: "#FE01CB", output: "hex" },
+ { input: "#FE01CB80", output: "hex" },
+ { input: "blue", output: "name" },
+ { input: "orange", output: "name" },
+ // // Once bug 1824400 is closed, these types should be recognized
+ // { input: "oklch(50% 0.3 180)", output: "oklch" },
+ // { input: "oklch(50% 0.3 180 / 0.5)", output: "oklch" },
+ // { input: "oklab(50% -0.3 0.3)", output: "oklab" },
+ // { input: "oklab(50% -0.3 0.3 / 0.5)", output: "oklab" },
+ // { input: "lch(50% 0.3 180)", output: "lch" },
+ // { input: "lch(50% 0.3 180 / 0.5)", output: "lch" },
+ // { input: "lab(50% -0.3 0.3)", output: "lab" },
+ // { input: "lab(50% -0.3 0.3 / 0.5)", output: "lab" },
+ // // But if they are not recognized, they should be classified as "authored"
+ { input: "oklch(50% 0.3 180)", output: "authored" },
+ { input: "oklch(50% 0.3 180 / 0.5)", output: "authored" },
+ { input: "oklab(50% -0.3 0.3)", output: "authored" },
+ { input: "oklab(50% -0.3 0.3 / 0.5)", output: "authored" },
+ { input: "lch(50% 0.3 180)", output: "authored" },
+ { input: "lch(50% 0.3 180 / 0.5)", output: "authored" },
+ { input: "lab(50% -0.3 0.3)", output: "authored" },
+ { input: "lab(50% -0.3 0.3 / 0.5)", output: "authored" },
+];
+
+function run_test() {
+ for (const test of CLASSIFY_TESTS) {
+ const result = colorUtils.classifyColor(test.input);
+ equal(result, test.output, "test classifyColor(" + test.input + ")");
+
+ Assert.notStrictEqual(
+ InspectorUtils.colorToRGBA(test.input),
+ null,
+ "'" + test.input + "' is a color"
+ );
+
+ // check some obvious errors.
+ const invalidColors = ["mumble" + test.input, test.input + "trailingstuff"];
+ for (const invalidColor of invalidColors) {
+ Assert.equal(
+ InspectorUtils.colorToRGBA(invalidColor),
+ null,
+ `'${invalidColor}' is not a color`
+ );
+ }
+ }
+
+ // Regression test for bug 1303826.
+ const black = new colorUtils.CssColor("#000");
+ equal(black.toString("name"), "black", "test non-upper-case color cycling");
+
+ const upper = new colorUtils.CssColor("BLACK");
+ equal(upper.toString("hex"), "#000", "test upper-case color cycling");
+ equal(upper.toString("name"), "BLACK", "test upper-case color preservation");
+}
diff --git a/devtools/client/shared/test/xpcshell/test_cssColor-02.js b/devtools/client/shared/test/xpcshell/test_cssColor-02.js
new file mode 100644
index 0000000000..77b692e6a7
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_cssColor-02.js
@@ -0,0 +1,50 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/**
+ * Test color cycling regression - Bug 1303748.
+ *
+ * Values should cycle from a starting value, back to their original values. This can
+ * potentially be a little flaky due to the precision of different color representations.
+ */
+
+const { colorUtils } = require("resource://devtools/shared/css/color.js");
+const getFixtureColorData = require("resource://test/helper_color_data.js");
+
+function run_test() {
+ getFixtureColorData().forEach(
+ ({ authored, name, hex, hsl, rgb, hwb, cycle }) => {
+ if (cycle) {
+ const nameCycled = runCycle(name, cycle);
+ const hexCycled = runCycle(hex, cycle);
+ const hslCycled = runCycle(hsl, cycle);
+ const rgbCycled = runCycle(rgb, cycle);
+ const hwbCycled = runCycle(hwb, cycle);
+ // Cut down on log output by only reporting a single pass/fail for the color.
+ ok(
+ nameCycled && hexCycled && hslCycled && rgbCycled && hwbCycled,
+ `${authored} was able to cycle back to the original value`
+ );
+ }
+ }
+ );
+}
+
+/**
+ * Test a color cycle to see if a color cycles back to its original value in a fixed
+ * number of steps.
+ *
+ * @param {string} value - The color value, e.g. "#000".
+ * @param {integer) times - The number of times it takes to cycle back to the
+ * original color.
+ */
+function runCycle(value, times) {
+ let color = new colorUtils.CssColor(value);
+ const colorUnit = colorUtils.classifyColor(value);
+ for (let i = 0; i < times; i++) {
+ const newColor = color.nextColorUnit();
+ color = new colorUtils.CssColor(newColor);
+ }
+ return color.toString(colorUnit) === value;
+}
diff --git a/devtools/client/shared/test/xpcshell/test_cssColor-8-digit-hex.js b/devtools/client/shared/test/xpcshell/test_cssColor-8-digit-hex.js
new file mode 100644
index 0000000000..877920294f
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_cssColor-8-digit-hex.js
@@ -0,0 +1,20 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// 8 character hex colors have 256 possible alpha values compared to the
+// standard 100 values possible via rgba() colors. This test ensures that they
+// are stored correctly without any alpha loss.
+
+"use strict";
+
+const { colorUtils } = require("resource://devtools/shared/css/color.js");
+
+const EIGHT_CHARACTER_HEX = "#fefefef0";
+
+// eslint-disable-next-line
+function run_test() {
+ const cssColor = new colorUtils.CssColor(EIGHT_CHARACTER_HEX);
+ const color = cssColor.toString(colorUtils.CssColor.COLORUNIT.hex);
+
+ equal(color, EIGHT_CHARACTER_HEX, "alpha value is correct");
+}
diff --git a/devtools/client/shared/test/xpcshell/test_cssColorDatabase.js b/devtools/client/shared/test/xpcshell/test_cssColorDatabase.js
new file mode 100644
index 0000000000..ec0cc0a4d8
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_cssColorDatabase.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that css-color-db matches platform.
+
+"use strict";
+
+const { cssColors } = require("resource://devtools/shared/css/color-db.js");
+
+add_task(() => {
+ for (const name in cssColors) {
+ ok(
+ InspectorUtils.isValidCSSColor(name),
+ name + " is valid in InspectorUtils"
+ );
+ }
+});
diff --git a/devtools/client/shared/test/xpcshell/test_cubicBezier.js b/devtools/client/shared/test/xpcshell/test_cubicBezier.js
new file mode 100644
index 0000000000..708c910fd2
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_cubicBezier.js
@@ -0,0 +1,152 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests the CubicBezier API in the CubicBezierWidget module
+
+var {
+ CubicBezier,
+ parseTimingFunction,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+
+function run_test() {
+ throwsWhenMissingCoordinates();
+ throwsWhenIncorrectCoordinates();
+ convertsStringCoordinates();
+ coordinatesToStringOutputsAString();
+ pointGettersReturnPointCoordinatesArrays();
+ toStringOutputsCubicBezierValue();
+ toStringOutputsCssPresetValues();
+ testParseTimingFunction();
+}
+
+function throwsWhenMissingCoordinates() {
+ do_check_throws(() => {
+ new CubicBezier();
+ }, "Throws an exception when coordinates are missing");
+}
+
+function throwsWhenIncorrectCoordinates() {
+ do_check_throws(() => {
+ new CubicBezier([]);
+ }, "Throws an exception when coordinates are incorrect (empty array)");
+
+ do_check_throws(() => {
+ new CubicBezier([0, 0]);
+ }, "Throws an exception when coordinates are incorrect (incomplete array)");
+
+ do_check_throws(() => {
+ new CubicBezier(["a", "b", "c", "d"]);
+ }, "Throws an exception when coordinates are incorrect (invalid type)");
+
+ do_check_throws(() => {
+ new CubicBezier([1.5, 0, 1.5, 0]);
+ }, "Throws an exception when coordinates are incorrect (time range invalid)");
+
+ do_check_throws(() => {
+ new CubicBezier([-0.5, 0, -0.5, 0]);
+ }, "Throws an exception when coordinates are incorrect (time range invalid)");
+}
+
+function convertsStringCoordinates() {
+ info("Converts string coordinates to numbers");
+ const c = new CubicBezier(["0", "1", ".5", "-2"]);
+
+ Assert.equal(c.coordinates[0], 0);
+ Assert.equal(c.coordinates[1], 1);
+ Assert.equal(c.coordinates[2], 0.5);
+ Assert.equal(c.coordinates[3], -2);
+}
+
+function coordinatesToStringOutputsAString() {
+ info("coordinates.toString() outputs a string representation");
+
+ let c = new CubicBezier(["0", "1", "0.5", "-2"]);
+ let string = c.coordinates.toString();
+ Assert.equal(string, "0,1,.5,-2");
+
+ c = new CubicBezier([1, 1, 1, 1]);
+ string = c.coordinates.toString();
+ Assert.equal(string, "1,1,1,1");
+}
+
+function pointGettersReturnPointCoordinatesArrays() {
+ info("Points getters return arrays of coordinates");
+
+ const c = new CubicBezier([0, 0.2, 0.5, 1]);
+ Assert.equal(c.P1[0], 0);
+ Assert.equal(c.P1[1], 0.2);
+ Assert.equal(c.P2[0], 0.5);
+ Assert.equal(c.P2[1], 1);
+}
+
+function toStringOutputsCubicBezierValue() {
+ info("toString() outputs the cubic-bezier() value");
+
+ const c = new CubicBezier([0, 1, 1, 0]);
+ Assert.equal(c.toString(), "cubic-bezier(0,1,1,0)");
+}
+
+function toStringOutputsCssPresetValues() {
+ info("toString() outputs the css predefined values");
+
+ let c = new CubicBezier([0, 0, 1, 1]);
+ Assert.equal(c.toString(), "linear");
+
+ c = new CubicBezier([0.25, 0.1, 0.25, 1]);
+ Assert.equal(c.toString(), "ease");
+
+ c = new CubicBezier([0.42, 0, 1, 1]);
+ Assert.equal(c.toString(), "ease-in");
+
+ c = new CubicBezier([0, 0, 0.58, 1]);
+ Assert.equal(c.toString(), "ease-out");
+
+ c = new CubicBezier([0.42, 0, 0.58, 1]);
+ Assert.equal(c.toString(), "ease-in-out");
+}
+
+function testParseTimingFunction() {
+ info("test parseTimingFunction");
+
+ for (const test of ["ease", "linear", "ease-in", "ease-out", "ease-in-out"]) {
+ ok(parseTimingFunction(test), test);
+ }
+
+ ok(!parseTimingFunction("something"), "non-function token");
+ ok(!parseTimingFunction("something()"), "non-cubic-bezier function");
+ ok(
+ !parseTimingFunction(
+ "cubic-bezier(something)",
+ "cubic-bezier with non-numeric argument"
+ )
+ );
+ ok(!parseTimingFunction("cubic-bezier(1,2,3:7)", "did not see comma"));
+ ok(!parseTimingFunction("cubic-bezier(1,2,3,7:", "did not see close paren"));
+ ok(!parseTimingFunction("cubic-bezier(1,2", "early EOF after number"));
+ ok(!parseTimingFunction("cubic-bezier(1,2,", "early EOF after comma"));
+ deepEqual(
+ parseTimingFunction("cubic-bezier(1,2,3,7)"),
+ [1, 2, 3, 7],
+ "correct invocation"
+ );
+ deepEqual(
+ parseTimingFunction("cubic-bezier(1, /* */ 2,3, 7 )"),
+ [1, 2, 3, 7],
+ "correct with comments and whitespace"
+ );
+}
+
+function do_check_throws(cb, details) {
+ info(details);
+
+ let hasThrown = false;
+ try {
+ cb();
+ } catch (e) {
+ hasThrown = true;
+ }
+
+ Assert.ok(hasThrown);
+}
diff --git a/devtools/client/shared/test/xpcshell/test_curl.js b/devtools/client/shared/test/xpcshell/test_curl.js
new file mode 100644
index 0000000000..a2a6c3412e
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_curl.js
@@ -0,0 +1,397 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests utility functions contained in `source-utils.js`
+ */
+
+const curl = require("resource://devtools/client/shared/curl.js");
+const Curl = curl.Curl;
+const CurlUtils = curl.CurlUtils;
+
+// Test `Curl.generateCommand` headers forwarding/filtering
+add_task(async function () {
+ const request = {
+ url: "https://example.com/form/",
+ method: "GET",
+ headers: [
+ { name: "Host", value: "example.com" },
+ {
+ name: "User-Agent",
+ value:
+ "Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0",
+ },
+ { name: "Accept", value: "*/*" },
+ { name: "Accept-Language", value: "en-US,en;q=0.5" },
+ { name: "Accept-Encoding", value: "gzip, deflate, br" },
+ { name: "Origin", value: "https://example.com" },
+ { name: "Connection", value: "keep-alive" },
+ { name: "Referer", value: "https://example.com/home/" },
+ { name: "Content-Type", value: "text/plain" },
+ ],
+ responseHeaders: [],
+ httpVersion: "HTTP/2.0",
+ };
+
+ const cmd = Curl.generateCommand(request);
+ const curlParams = parseCurl(cmd);
+
+ ok(
+ !headerTypeInParams(curlParams, "Host"),
+ "host header ignored - to be generated from url"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Accept: */*"),
+ "accept header present in curl command"
+ );
+ ok(
+ exactHeaderInParams(
+ curlParams,
+ "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"
+ ),
+ "user-agent header present in curl command"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Accept-Language: en-US,en;q=0.5"),
+ "accept-language header present in curl output"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Accept-Encoding: gzip, deflate, br"),
+ "accept-encoding header present in curl output"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Origin: https://example.com"),
+ "origin header present in curl output"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Connection: keep-alive"),
+ "connection header present in curl output"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Referer: https://example.com/home/"),
+ "referer header present in curl output"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Content-Type: text/plain"),
+ "content-type header present in curl output"
+ );
+ ok(!inParams(curlParams, "--data"), "no data param in GET curl output");
+ ok(
+ !inParams(curlParams, "--data-raw"),
+ "no raw data param in GET curl output"
+ );
+});
+
+// Test `Curl.generateCommand` URL glob handling
+add_task(async function () {
+ let request = {
+ url: "https://example.com/",
+ method: "GET",
+ headers: [],
+ responseHeaders: [],
+ httpVersion: "HTTP/2.0",
+ };
+
+ let cmd = Curl.generateCommand(request);
+ let curlParams = parseCurl(cmd);
+
+ ok(
+ !inParams(curlParams, "--globoff"),
+ "no globoff param in curl output when not needed"
+ );
+
+ request = {
+ url: "https://example.com/[]",
+ method: "GET",
+ headers: [],
+ responseHeaders: [],
+ httpVersion: "HTTP/2.0",
+ };
+
+ cmd = Curl.generateCommand(request);
+ curlParams = parseCurl(cmd);
+
+ ok(
+ inParams(curlParams, "--globoff"),
+ "globoff param present in curl output when needed"
+ );
+});
+
+// Test `Curl.generateCommand` data POSTing
+add_task(async function () {
+ const request = {
+ url: "https://example.com/form/",
+ method: "POST",
+ headers: [
+ { name: "Content-Length", value: "1000" },
+ { name: "Content-Type", value: "text/plain" },
+ ],
+ responseHeaders: [],
+ httpVersion: "HTTP/2.0",
+ postDataText: "A piece of plain payload text",
+ };
+
+ const cmd = Curl.generateCommand(request);
+ const curlParams = parseCurl(cmd);
+
+ ok(
+ !headerTypeInParams(curlParams, "Content-Length"),
+ "content-length header ignored - curl generates new one"
+ );
+ ok(
+ exactHeaderInParams(curlParams, "Content-Type: text/plain"),
+ "content-type header present in curl output"
+ );
+ ok(
+ inParams(curlParams, "--data-raw"),
+ '"--data-raw" param present in curl output'
+ );
+ ok(
+ inParams(curlParams, `--data-raw ${quote(request.postDataText)}`),
+ "proper payload data present in output"
+ );
+});
+
+// Test `Curl.generateCommand` data POSTing - not post data
+add_task(async function () {
+ const request = {
+ url: "https://example.com/form/",
+ method: "POST",
+ headers: [
+ { name: "Content-Length", value: "1000" },
+ { name: "Content-Type", value: "text/plain" },
+ ],
+ responseHeaders: [],
+ httpVersion: "HTTP/2.0",
+ };
+
+ const cmd = Curl.generateCommand(request);
+ const curlParams = parseCurl(cmd);
+
+ ok(
+ !inParams(curlParams, "--data-raw"),
+ '"--data-raw" param not present in curl output'
+ );
+
+ const methodIndex = curlParams.indexOf("-X");
+
+ ok(
+ methodIndex !== -1 && curlParams[methodIndex + 1] === "POST",
+ "request method explicit is POST"
+ );
+});
+
+// Test `Curl.generateCommand` multipart data POSTing
+add_task(async function () {
+ const boundary = "----------14808";
+ const request = {
+ url: "https://example.com/form/",
+ method: "POST",
+ headers: [
+ {
+ name: "Content-Type",
+ value: `multipart/form-data; boundary=${boundary}`,
+ },
+ ],
+ responseHeaders: [],
+ httpVersion: "HTTP/2.0",
+ postDataText: [
+ `--${boundary}`,
+ 'Content-Disposition: form-data; name="field_one"',
+ "",
+ "value_one",
+ `--${boundary}`,
+ 'Content-Disposition: form-data; name="field_two"',
+ "",
+ "value two",
+ `--${boundary}--`,
+ "",
+ ].join("\r\n"),
+ };
+
+ const cmd = Curl.generateCommand(request);
+
+ // Check content type
+ const contentTypePos = cmd.indexOf(headerParamPrefix("Content-Type"));
+ const contentTypeParam = headerParam(
+ `Content-Type: multipart/form-data; boundary=${boundary}`
+ );
+ Assert.notStrictEqual(
+ contentTypePos,
+ -1,
+ "content type header present in curl output"
+ );
+ equal(
+ cmd.substr(contentTypePos, contentTypeParam.length),
+ contentTypeParam,
+ "proper content type header present in curl output"
+ );
+
+ // Check binary data
+ const dataBinaryPos = cmd.indexOf("--data-binary");
+ const dataBinaryParam = `--data-binary ${isWin() ? "" : "$"}${escapeNewline(
+ quote(request.postDataText)
+ )}`;
+ Assert.notStrictEqual(
+ dataBinaryPos,
+ -1,
+ "--data-binary param present in curl output"
+ );
+ equal(
+ cmd.substr(dataBinaryPos, dataBinaryParam.length),
+ dataBinaryParam,
+ "proper multipart data present in curl output"
+ );
+});
+
+// Test `CurlUtils.removeBinaryDataFromMultipartText` doesn't change text data
+add_task(async function () {
+ const boundary = "----------14808";
+ const postTextLines = [
+ `--${boundary}`,
+ 'Content-Disposition: form-data; name="field_one"',
+ "",
+ "value_one",
+ `--${boundary}`,
+ 'Content-Disposition: form-data; name="field_two"',
+ "",
+ "value two",
+ `--${boundary}--`,
+ "",
+ ];
+
+ const cleanedText = CurlUtils.removeBinaryDataFromMultipartText(
+ postTextLines.join("\r\n"),
+ boundary
+ );
+ equal(
+ cleanedText,
+ postTextLines.join("\r\n"),
+ "proper non-binary multipart text unchanged"
+ );
+});
+
+// Test `CurlUtils.removeBinaryDataFromMultipartText` removes binary data
+add_task(async function () {
+ const boundary = "----------14808";
+ const postTextLines = [
+ `--${boundary}`,
+ 'Content-Disposition: form-data; name="field_one"',
+ "",
+ "value_one",
+ `--${boundary}`,
+ 'Content-Disposition: form-data; name="field_two"; filename="file_field_two.txt"',
+ "",
+ "file content",
+ `--${boundary}--`,
+ "",
+ ];
+
+ const cleanedText = CurlUtils.removeBinaryDataFromMultipartText(
+ postTextLines.join("\r\n"),
+ boundary
+ );
+ postTextLines.splice(7, 1);
+ equal(
+ cleanedText,
+ postTextLines.join("\r\n"),
+ "file content removed from multipart text"
+ );
+});
+
+// Test `Curl.generateCommand` add --compressed flag
+add_task(async function () {
+ let request = {
+ url: "https://example.com/",
+ method: "GET",
+ headers: [],
+ responseHeaders: [],
+ httpVersion: "HTTP/2.0",
+ };
+
+ let cmd = Curl.generateCommand(request);
+ let curlParams = parseCurl(cmd);
+
+ ok(
+ !inParams(curlParams, "--compressed"),
+ "no compressed param in curl output when not needed"
+ );
+
+ request = {
+ url: "https://example.com/",
+ method: "GET",
+ headers: [],
+ responseHeaders: [{ name: "Content-Encoding", value: "gzip" }],
+ httpVersion: "HTTP/2.0",
+ };
+
+ cmd = Curl.generateCommand(request);
+ curlParams = parseCurl(cmd);
+
+ ok(
+ inParams(curlParams, "--compressed"),
+ "compressed param present in curl output when needed"
+ );
+});
+
+function isWin() {
+ return Services.appinfo.OS === "WINNT";
+}
+
+const QUOTE = isWin() ? '"' : "'";
+
+// Quote a string, escape the quotes inside the string
+function quote(str) {
+ let escaped;
+ if (isWin()) {
+ escaped = str.replace(new RegExp(QUOTE, "g"), `${QUOTE}${QUOTE}`);
+ } else {
+ escaped = str.replace(new RegExp(QUOTE, "g"), `\\${QUOTE}`);
+ }
+ return QUOTE + escaped + QUOTE;
+}
+
+function escapeNewline(txt) {
+ if (isWin()) {
+ // Add `"` to close quote, then escape newline outside of quote, then start new quote
+ return txt.replace(/[\r\n]{1,2}/g, '"^$&$&"');
+ }
+ return txt.replace(/\r/g, "\\r").replace(/\n/g, "\\n");
+}
+
+// Header param is formatted as -H "Header: value" or -H 'Header: value'
+function headerParam(h) {
+ return "-H " + quote(h);
+}
+
+// Header param prefix is formatted as `-H "HeaderName` or `-H 'HeaderName`
+function headerParamPrefix(headerName) {
+ return `-H ${QUOTE}${headerName}`;
+}
+
+// If any params startswith `-H "HeaderName` or `-H 'HeaderName`
+function headerTypeInParams(curlParams, headerName) {
+ return curlParams.some(param =>
+ param.toLowerCase().startsWith(headerParamPrefix(headerName).toLowerCase())
+ );
+}
+
+function exactHeaderInParams(curlParams, header) {
+ return curlParams.some(param => param === headerParam(header));
+}
+
+function inParams(curlParams, param) {
+ return curlParams.some(p => p.startsWith(param));
+}
+
+// Parse complete curl command to array of params. Can be applied to simple headers/data,
+// but will not on WIN with sophisticated values of --data-binary with e.g. escaped quotes
+function parseCurl(curlCmd) {
+ // This monster regexp parses the command line into an array of arguments,
+ // recognizing quoted args with matching quotes and escaped quotes inside:
+ // [ "curl 'url'", "--standalone-arg", "-arg-with-quoted-string 'value\'s'" ]
+ const matchRe = /[-A-Za-z1-9]+(?: \$?([\"'])(?:\\\1|.)*?\1)?/g;
+ return curlCmd.match(matchRe);
+}
diff --git a/devtools/client/shared/test/xpcshell/test_escapeCSSComment.js b/devtools/client/shared/test/xpcshell/test_escapeCSSComment.js
new file mode 100644
index 0000000000..7a77cc7e88
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_escapeCSSComment.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+ escapeCSSComment,
+ unescapeCSSComment,
+} = require("resource://devtools/shared/css/parsing-utils.js");
+
+const TEST_DATA = [
+ {
+ input: "simple",
+ expected: "simple",
+ },
+ {
+ input: "/* comment */",
+ expected: "/\\* comment *\\/",
+ },
+ {
+ input: "/* two *//* comments */",
+ expected: "/\\* two *\\//\\* comments *\\/",
+ },
+ {
+ input: "/* nested /\\* comment *\\/ */",
+ expected: "/\\* nested /\\\\* comment *\\\\/ *\\/",
+ },
+];
+
+function run_test() {
+ let i = 0;
+ for (const test of TEST_DATA) {
+ ++i;
+ info("Test #" + i);
+
+ const escaped = escapeCSSComment(test.input);
+ equal(escaped, test.expected);
+ const unescaped = unescapeCSSComment(escaped);
+ equal(unescaped, test.input);
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/test_hasCSSVariable.js b/devtools/client/shared/test/xpcshell/test_hasCSSVariable.js
new file mode 100644
index 0000000000..168add6abb
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_hasCSSVariable.js
@@ -0,0 +1,60 @@
+/* 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/.
+ */
+
+"use strict";
+
+// Test whether hasCSSVariable function of utils.js works correctly or not.
+
+const {
+ hasCSSVariable,
+} = require("resource://devtools/client/inspector/rules/utils/utils.js");
+
+function run_test() {
+ info("Normal usage");
+ ok(
+ hasCSSVariable("var(--color)", "--color"),
+ "Found --color variable in var(--color)"
+ );
+ ok(
+ !hasCSSVariable("var(--color)", "--col"),
+ "Did not find --col variable in var(--color)"
+ );
+
+ info("Variable with fallback");
+ ok(
+ hasCSSVariable("var(--color, red)", "--color"),
+ "Found --color variable in var(--color)"
+ );
+ ok(
+ !hasCSSVariable("var(--color, red)", "--col"),
+ "Did not find --col variable in var(--color, red)"
+ );
+
+ info("Nested variables");
+ ok(
+ hasCSSVariable("var(--color1, var(--color2, blue))", "--color1"),
+ "Found --color1 variable in var(--color1, var(--color2, blue))"
+ );
+ ok(
+ hasCSSVariable("var(--color1, var(--color2, blue))", "--color2"),
+ "Found --color2 variable in var(--color1, var(--color2, blue))"
+ );
+ ok(
+ !hasCSSVariable("var(--color1, var(--color2, blue))", "--color"),
+ "Did not find --color variable in var(--color1, var(--color2, blue))"
+ );
+
+ info("Invalid variable");
+ ok(
+ !hasCSSVariable("--color", "--color"),
+ "Did not find --color variable in --color"
+ );
+
+ info("Variable with whitespace");
+ ok(
+ hasCSSVariable("var( --color )", "--color"),
+ "Found --color variable in var( --color )"
+ );
+}
diff --git a/devtools/client/shared/test/xpcshell/test_linearEasing.js b/devtools/client/shared/test/xpcshell/test_linearEasing.js
new file mode 100644
index 0000000000..66e487e17b
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_linearEasing.js
@@ -0,0 +1,217 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests methods from the LinearEasingWidget module
+
+const {
+ LinearEasingFunctionWidget,
+ parseTimingFunction,
+} = require("resource://devtools/client/shared/widgets/LinearEasingFunctionWidget.js");
+
+add_task(function testParseTimingFunction() {
+ info("test parseTimingFunction");
+
+ for (const test of ["ease", "linear", "ease-in", "ease-out", "ease-in-out"]) {
+ ok(!parseTimingFunction(test), `"${test}" is not valid`);
+ }
+
+ ok(!parseTimingFunction("something"), "non-function token");
+ ok(!parseTimingFunction("something()"), "non-linear function");
+ ok(
+ !parseTimingFunction(
+ "linear(something)",
+ "linear with non-numeric argument"
+ )
+ );
+
+ ok(!parseTimingFunction("linear(0)", "linear with only 1 point"));
+
+ deepEqual(
+ parseTimingFunction("linear(0, 0.5, 1)"),
+ [
+ { input: 0, output: 0 },
+ { input: 0.5, output: 0.5 },
+ { input: 1, output: 1 },
+ ],
+ "correct invocation"
+ );
+ deepEqual(
+ parseTimingFunction("linear(0, 0.5 /* mid */, 1)"),
+ [
+ { input: 0, output: 0 },
+ { input: 0.5, output: 0.5 },
+ { input: 1, output: 1 },
+ ],
+ "correct with comments and whitespace"
+ );
+ deepEqual(
+ parseTimingFunction("linear(0 10%, 0.5 20%, 1 90%)"),
+ [
+ { input: 0.1, output: 0 },
+ { input: 0.2, output: 0.5 },
+ { input: 0.9, output: 1 },
+ ],
+ "correct invocation with single stop"
+ );
+ deepEqual(
+ parseTimingFunction(
+ "linear(0, 0.1, 0.2, 0.3, 0.4, 0.5 50%, 0.6, 0.7, 0.8, 0.9 70%, 1)"
+ ),
+ [
+ { input: 0, output: 0 },
+ { input: 0.1, output: 0.1 },
+ { input: 0.2, output: 0.2 },
+ { input: 0.3, output: 0.3 },
+ { input: 0.4, output: 0.4 },
+ { input: 0.5, output: 0.5 },
+ { input: 0.55, output: 0.6 },
+ { input: 0.6, output: 0.7 },
+ {
+ // This should be 0.65, but JS doesn't play well with floating points, which makes
+ // the test fail. So re-do the computation here
+ input: 0.5 * 0.25 + 0.7 * 0.75,
+ output: 0.8,
+ },
+ { input: 0.7, output: 0.9 },
+ { input: 1, output: 1 },
+ ],
+ "correct invocation with single stop and run of non-stop values"
+ );
+
+ deepEqual(
+ parseTimingFunction("linear(0, 0.5 80%, 0.75 40%, 1)"),
+ [
+ { input: 0, output: 0 },
+ { input: 0.8, output: 0.5 },
+ { input: 0.8, output: 0.75 },
+ { input: 1, output: 1 },
+ ],
+ "correct invocation with out of order single stop"
+ );
+
+ deepEqual(
+ parseTimingFunction("linear(0.5 10% 40%, 0, 0.2 60% 70%, 0.75 80% 100%)"),
+ [
+ { input: 0.1, output: 0.5 },
+ { input: 0.4, output: 0.5 },
+ { input: 0.5, output: 0 },
+ { input: 0.6, output: 0.2 },
+ { input: 0.7, output: 0.2 },
+ { input: 0.8, output: 0.75 },
+ { input: 1, output: 0.75 },
+ ],
+ "correct invocation with multiple stops"
+ );
+
+ deepEqual(
+ parseTimingFunction("linear(0, 0.2 60% 10%, 1)"),
+ [
+ { input: 0, output: 0 },
+ { input: 0.6, output: 0.2 },
+ { input: 0.6, output: 0.2 },
+ { input: 1, output: 1 },
+ ],
+ "correct invocation with multiple out of order stops"
+ );
+
+ deepEqual(
+ parseTimingFunction("linear(0, 1.5, 1)"),
+ [
+ { input: 0, output: 0 },
+ { input: 0.5, output: 1.5 },
+ { input: 1, output: 1 },
+ ],
+ "linear function easing with output greater than 1"
+ );
+
+ deepEqual(
+ parseTimingFunction("linear(1, -0.5, 0)"),
+ [
+ { input: 0, output: 1 },
+ { input: 0.5, output: -0.5 },
+ { input: 1, output: 0 },
+ ],
+ "linear function easing with output less than 1"
+ );
+
+ deepEqual(
+ parseTimingFunction("linear(0, 0.1 -10%, 1)"),
+ [
+ { input: 0, output: 0 },
+ { input: 0, output: 0.1 },
+ { input: 1, output: 1 },
+ ],
+ "correct invocation, input value being unspecified in the first entry implies zero"
+ );
+
+ deepEqual(
+ parseTimingFunction("linear(0, 0.9 110%, 1)"),
+ [
+ { input: 0, output: 0 },
+ { input: 1.1, output: 0.9 },
+ { input: 1.1, output: 1 },
+ ],
+ "correct invocation, input value being unspecified in the last entry implies max input value"
+ );
+});
+
+add_task(function testGetSetCssLinearValue() {
+ const doc = Services.appShell.createWindowlessBrowser().document;
+ const widget = new LinearEasingFunctionWidget(doc.body);
+
+ widget.setCssLinearValue("linear(0)");
+ ok(!widget.getCssLinearValue(), "no value returned for invalid value");
+
+ widget.setCssLinearValue("linear(0, 0.5, 1)");
+ deepEqual(
+ widget.getCssLinearValue(),
+ "linear(0 0%, 0.5 50%, 1 100%)",
+ "no stops"
+ );
+
+ widget.setCssLinearValue("linear(0 10%, 0.5 20%, 1 90%)");
+ deepEqual(
+ widget.getCssLinearValue(),
+ "linear(0 10%, 0.5 20%, 1 90%)",
+ "with single stops"
+ );
+
+ widget.setCssLinearValue("linear(0, 0.5 80%, 0.75 40%, 1)");
+ deepEqual(
+ widget.getCssLinearValue(),
+ "linear(0 0%, 0.5 80%, 0.75 80%, 1 100%)",
+ "correcting out of order single stops"
+ );
+
+ widget.setCssLinearValue(
+ "linear(0.5 10% 40%, 0, 0.2 60% 70%, 0.75 80% 100%)"
+ );
+ deepEqual(
+ widget.getCssLinearValue(),
+ "linear(0.5 10%, 0.5 40%, 0 50%, 0.2 60%, 0.2 70%, 0.75 80%, 0.75 100%)",
+ "multiple stops"
+ );
+
+ widget.setCssLinearValue("linear(0, 0.2 60% 10%, 1)");
+ deepEqual(
+ widget.getCssLinearValue(),
+ "linear(0 0%, 0.2 60%, 0.2 60%, 1 100%)",
+ "correcting multiple out-of-order stops"
+ );
+
+ widget.setCssLinearValue("linear(1, -0.5, 1.5)");
+ deepEqual(
+ widget.getCssLinearValue(),
+ "linear(1 0%, -0.5 50%, 1.5 100%)",
+ "output outside of [0,1] range"
+ );
+
+ widget.setCssLinearValue("linear(0 -10%, 0.5, 1 130%)");
+ deepEqual(
+ widget.getCssLinearValue(),
+ "linear(0 -10%, 0.5 60%, 1 130%)",
+ "input outside of [0%,100%] range"
+ );
+});
diff --git a/devtools/client/shared/test/xpcshell/test_parseDeclarations.js b/devtools/client/shared/test/xpcshell/test_parseDeclarations.js
new file mode 100644
index 0000000000..593087d46b
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_parseDeclarations.js
@@ -0,0 +1,1641 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+ parseDeclarations,
+ _parseCommentDeclarations,
+ parseNamedDeclarations,
+} = require("resource://devtools/shared/css/parsing-utils.js");
+const {
+ isCssPropertyKnown,
+} = require("resource://devtools/server/actors/css-properties.js");
+
+const TEST_DATA = [
+ // Simple test
+ {
+ input: "p:v;",
+ expected: [
+ {
+ name: "p",
+ value: "v",
+ priority: "",
+ offsets: [0, 4],
+ declarationText: "p:v;",
+ },
+ ],
+ },
+ // Simple test
+ {
+ input: "this:is;a:test;",
+ expected: [
+ {
+ name: "this",
+ value: "is",
+ priority: "",
+ offsets: [0, 8],
+ declarationText: "this:is;",
+ },
+ {
+ name: "a",
+ value: "test",
+ priority: "",
+ offsets: [8, 15],
+ declarationText: "a:test;",
+ },
+ ],
+ },
+ // Test a single declaration with semi-colon
+ {
+ input: "name:value;",
+ expected: [
+ {
+ name: "name",
+ value: "value",
+ priority: "",
+ offsets: [0, 11],
+ declarationText: "name:value;",
+ },
+ ],
+ },
+ // Test a single declaration without semi-colon
+ {
+ input: "name:value",
+ expected: [
+ {
+ name: "name",
+ value: "value",
+ priority: "",
+ offsets: [0, 10],
+ declarationText: "name:value",
+ },
+ ],
+ },
+ // Test multiple declarations separated by whitespaces and carriage
+ // returns and tabs
+ {
+ input: "p1 : v1 ; \t\t \n p2:v2; \n\n\n\n\t p3 : v3;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "p1 : v1 ;",
+ },
+ {
+ name: "p2",
+ value: "v2",
+ priority: "",
+ offsets: [16, 22],
+ declarationText: "p2:v2;",
+ },
+ {
+ name: "p3",
+ value: "v3",
+ priority: "",
+ offsets: [32, 45],
+ declarationText: "p3 : v3;",
+ },
+ ],
+ },
+ // Test simple priority
+ {
+ input: "p1: v1; p2: v2 !important;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1",
+ priority: "",
+ offsets: [0, 7],
+ declarationText: "p1: v1;",
+ },
+ {
+ name: "p2",
+ value: "v2",
+ priority: "important",
+ offsets: [8, 26],
+ declarationText: "p2: v2 !important;",
+ },
+ ],
+ },
+ // Test simple priority
+ {
+ input: "p1: v1 !important; p2: v2",
+ expected: [
+ {
+ name: "p1",
+ value: "v1",
+ priority: "important",
+ offsets: [0, 18],
+ declarationText: "p1: v1 !important;",
+ },
+ {
+ name: "p2",
+ value: "v2",
+ priority: "",
+ offsets: [19, 25],
+ declarationText: "p2: v2",
+ },
+ ],
+ },
+ // Test simple priority
+ {
+ input: "p1: v1 ! important; p2: v2 ! important;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1",
+ priority: "important",
+ offsets: [0, 20],
+ declarationText: "p1: v1 ! important;",
+ },
+ {
+ name: "p2",
+ value: "v2",
+ priority: "important",
+ offsets: [21, 40],
+ declarationText: "p2: v2 ! important;",
+ },
+ ],
+ },
+ // Test simple priority
+ {
+ input: "p1: v1 !/*comment*/important;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1",
+ priority: "important",
+ offsets: [0, 29],
+ declarationText: "p1: v1 !/*comment*/important;",
+ },
+ ],
+ },
+ // Test priority without terminating ";".
+ {
+ input: "p1: v1 !important",
+ expected: [
+ {
+ name: "p1",
+ value: "v1",
+ priority: "important",
+ offsets: [0, 17],
+ declarationText: "p1: v1 !important",
+ },
+ ],
+ },
+ // Test trailing "!" without terminating ";".
+ {
+ input: "p1: v1 !",
+ expected: [
+ {
+ name: "p1",
+ value: "v1 !",
+ priority: "",
+ offsets: [0, 8],
+ declarationText: "p1: v1 !",
+ },
+ ],
+ },
+ // Test invalid priority
+ {
+ input: "p1: v1 important;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1 important",
+ priority: "",
+ offsets: [0, 17],
+ declarationText: "p1: v1 important;",
+ },
+ ],
+ },
+ // Test invalid priority (in the middle of the declaration).
+ // See bug 1462553.
+ {
+ input: "p1: v1 !important v2;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1 !important v2",
+ priority: "",
+ offsets: [0, 21],
+ declarationText: "p1: v1 !important v2;",
+ },
+ ],
+ },
+ {
+ input: "p1: v1 ! important v2;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1 ! important v2",
+ priority: "",
+ offsets: [0, 25],
+ declarationText: "p1: v1 ! important v2;",
+ },
+ ],
+ },
+ {
+ input: "p1: v1 ! /*comment*/ important v2;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1 ! important v2",
+ priority: "",
+ offsets: [0, 36],
+ declarationText: "p1: v1 ! /*comment*/ important v2;",
+ },
+ ],
+ },
+ {
+ input: "p1: v1 !/*hi*/important v2;",
+ expected: [
+ {
+ name: "p1",
+ value: "v1 ! important v2",
+ priority: "",
+ offsets: [0, 27],
+ declarationText: "p1: v1 !/*hi*/important v2;",
+ },
+ ],
+ },
+ // Test various types of background-image urls
+ {
+ input: "background-image: url(../../relative/image.png)",
+ expected: [
+ {
+ name: "background-image",
+ value: "url(../../relative/image.png)",
+ priority: "",
+ offsets: [0, 47],
+ declarationText: "background-image: url(../../relative/image.png)",
+ },
+ ],
+ },
+ {
+ input: "background-image: url(http://site.com/test.png)",
+ expected: [
+ {
+ name: "background-image",
+ value: "url(http://site.com/test.png)",
+ priority: "",
+ offsets: [0, 47],
+ declarationText: "background-image: url(http://site.com/test.png)",
+ },
+ ],
+ },
+ {
+ input: "background-image: url(wow.gif)",
+ expected: [
+ {
+ name: "background-image",
+ value: "url(wow.gif)",
+ priority: "",
+ offsets: [0, 30],
+ declarationText: "background-image: url(wow.gif)",
+ },
+ ],
+ },
+ // Test that urls with :;{} characters in them are parsed correctly
+ {
+ input:
+ 'background: red url("http://site.com/image{}:;.png?id=4#wat") ' +
+ "repeat top right",
+ expected: [
+ {
+ name: "background",
+ value:
+ 'red url("http://site.com/image{}:;.png?id=4#wat") ' +
+ "repeat top right",
+ priority: "",
+ offsets: [0, 78],
+ declarationText:
+ 'background: red url("http://site.com/image{}:;.png?id=4#wat") ' +
+ "repeat top right",
+ },
+ ],
+ },
+ // Test that an empty string results in an empty array
+ { input: "", expected: [] },
+ // Test that a string comprised only of whitespaces results in an empty array
+ { input: " \n \n \n \n \t \t\t\t ", expected: [] },
+ // Test that a null input throws an exception
+ { input: null, throws: true },
+ // Test that a undefined input throws an exception
+ { input: undefined, throws: true },
+ // Test that :;{} characters in quoted content are not parsed as multiple
+ // declarations
+ {
+ input: 'content: ";color:red;}selector{color:yellow;"',
+ expected: [
+ {
+ name: "content",
+ value: '";color:red;}selector{color:yellow;"',
+ priority: "",
+ offsets: [0, 45],
+ declarationText: 'content: ";color:red;}selector{color:yellow;"',
+ },
+ ],
+ },
+ // Test that rules aren't parsed, just declarations.
+ {
+ input: "body {color:red;} p {color: blue;}",
+ expected: [],
+ },
+ // Test unbalanced : and ;
+ {
+ input: "color :red : font : arial;",
+ expected: [
+ {
+ name: "color",
+ value: "red : font : arial",
+ priority: "",
+ offsets: [0, 26],
+ },
+ ],
+ },
+ {
+ input: "background: red;;;;;",
+ expected: [
+ {
+ name: "background",
+ value: "red",
+ priority: "",
+ offsets: [0, 16],
+ declarationText: "background: red;",
+ },
+ ],
+ },
+ {
+ input: "background:;",
+ expected: [
+ { name: "background", value: "", priority: "", offsets: [0, 12] },
+ ],
+ },
+ { input: ";;;;;", expected: [] },
+ { input: ":;:;", expected: [] },
+ // Test name only
+ {
+ input: "color",
+ expected: [{ name: "color", value: "", priority: "", offsets: [0, 5] }],
+ },
+ // Test trailing name without :
+ {
+ input: "color:blue;font",
+ expected: [
+ {
+ name: "color",
+ value: "blue",
+ priority: "",
+ offsets: [0, 11],
+ declarationText: "color:blue;",
+ },
+ { name: "font", value: "", priority: "", offsets: [11, 15] },
+ ],
+ },
+ // Test trailing name with :
+ {
+ input: "color:blue;font:",
+ expected: [
+ {
+ name: "color",
+ value: "blue",
+ priority: "",
+ offsets: [0, 11],
+ declarationText: "color:blue;",
+ },
+ { name: "font", value: "", priority: "", offsets: [11, 16] },
+ ],
+ },
+ // Test leading value
+ {
+ input: "Arial;color:blue;",
+ expected: [
+ { name: "", value: "Arial", priority: "", offsets: [0, 6] },
+ {
+ name: "color",
+ value: "blue",
+ priority: "",
+ offsets: [6, 17],
+ declarationText: "color:blue;",
+ },
+ ],
+ },
+ // Test hex colors
+ {
+ input: "color: #333",
+ expected: [
+ {
+ name: "color",
+ value: "#333",
+ priority: "",
+ offsets: [0, 11],
+ declarationText: "color: #333",
+ },
+ ],
+ },
+ {
+ input: "color: #456789",
+ expected: [
+ {
+ name: "color",
+ value: "#456789",
+ priority: "",
+ offsets: [0, 14],
+ declarationText: "color: #456789",
+ },
+ ],
+ },
+ {
+ input: "wat: #XYZ",
+ expected: [
+ {
+ name: "wat",
+ value: "#XYZ",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "wat: #XYZ",
+ },
+ ],
+ },
+ // Test string/url quotes escaping
+ {
+ input: "content: \"this is a 'string'\"",
+ expected: [
+ {
+ name: "content",
+ value: "\"this is a 'string'\"",
+ priority: "",
+ offsets: [0, 29],
+ declarationText: "content: \"this is a 'string'\"",
+ },
+ ],
+ },
+ {
+ input: 'content: "this is a \\"string\\""',
+ expected: [
+ {
+ name: "content",
+ value: '"this is a \\"string\\""',
+ priority: "",
+ offsets: [0, 31],
+ declarationText: 'content: "this is a \\"string\\""',
+ },
+ ],
+ },
+ {
+ input: "content: 'this is a \"string\"'",
+ expected: [
+ {
+ name: "content",
+ value: "'this is a \"string\"'",
+ priority: "",
+ offsets: [0, 29],
+ declarationText: "content: 'this is a \"string\"'",
+ },
+ ],
+ },
+ {
+ input: "content: 'this is a \\'string\\''",
+ expected: [
+ {
+ name: "content",
+ value: "'this is a \\'string\\''",
+ priority: "",
+ offsets: [0, 31],
+ declarationText: "content: 'this is a \\'string\\''",
+ },
+ ],
+ },
+ {
+ input: "content: 'this \\' is a \" really strange string'",
+ expected: [
+ {
+ name: "content",
+ value: "'this \\' is a \" really strange string'",
+ priority: "",
+ offsets: [0, 47],
+ declarationText: "content: 'this \\' is a \" really strange string'",
+ },
+ ],
+ },
+ {
+ input: 'content: "a not s\\ o very long title"',
+ expected: [
+ {
+ name: "content",
+ value: '"a not s\\ o very long title"',
+ priority: "",
+ offsets: [0, 46],
+ declarationText: 'content: "a not s\\ o very long title"',
+ },
+ ],
+ },
+ // Test calc with nested parentheses
+ {
+ input: "width: calc((100% - 3em) / 2)",
+ expected: [
+ {
+ name: "width",
+ value: "calc((100% - 3em) / 2)",
+ priority: "",
+ offsets: [0, 29],
+ declarationText: "width: calc((100% - 3em) / 2)",
+ },
+ ],
+ },
+
+ // Simple embedded comment test.
+ {
+ parseComments: true,
+ input: "width: 5; /* background: green; */ background: red;",
+ expected: [
+ {
+ name: "width",
+ value: "5",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "width: 5;",
+ },
+ {
+ name: "background",
+ value: "green",
+ priority: "",
+ offsets: [13, 31],
+ declarationText: "background: green;",
+ commentOffsets: [10, 34],
+ },
+ {
+ name: "background",
+ value: "red",
+ priority: "",
+ offsets: [35, 51],
+ declarationText: "background: red;",
+ },
+ ],
+ },
+
+ // Embedded comment where the parsing heuristic fails.
+ {
+ parseComments: true,
+ input: "width: 5; /* background something: green; */ background: red;",
+ expected: [
+ {
+ name: "width",
+ value: "5",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "width: 5;",
+ },
+ {
+ name: "background",
+ value: "red",
+ priority: "",
+ offsets: [45, 61],
+ declarationText: "background: red;",
+ },
+ ],
+ },
+
+ // Embedded comment where the parsing heuristic is a bit funny.
+ {
+ parseComments: true,
+ input: "width: 5; /* background: */ background: red;",
+ expected: [
+ {
+ name: "width",
+ value: "5",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "width: 5;",
+ },
+ {
+ name: "background",
+ value: "",
+ priority: "",
+ offsets: [13, 24],
+ commentOffsets: [10, 27],
+ },
+ {
+ name: "background",
+ value: "red",
+ priority: "",
+ offsets: [28, 44],
+ declarationText: "background: red;",
+ },
+ ],
+ },
+
+ // Another case where the parsing heuristic says not to bother; note
+ // that there is no ";" in the comment.
+ {
+ parseComments: true,
+ input: "width: 5; /* background: yellow */ background: red;",
+ expected: [
+ {
+ name: "width",
+ value: "5",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "width: 5;",
+ },
+ {
+ name: "background",
+ value: "yellow",
+ priority: "",
+ offsets: [13, 31],
+ declarationText: "background: yellow",
+ commentOffsets: [10, 34],
+ },
+ {
+ name: "background",
+ value: "red",
+ priority: "",
+ offsets: [35, 51],
+ declarationText: "background: red;",
+ },
+ ],
+ },
+
+ // Parsing a comment should yield text that has been unescaped, and
+ // the offsets should refer to the original text.
+ {
+ parseComments: true,
+ input: "/* content: '*\\/'; */",
+ expected: [
+ {
+ name: "content",
+ value: "'*/'",
+ priority: "",
+ offsets: [3, 18],
+ declarationText: "content: '*\\/';",
+ commentOffsets: [0, 21],
+ },
+ ],
+ },
+
+ // Parsing a comment should yield text that has been unescaped, and
+ // the offsets should refer to the original text. This variant
+ // tests the no-semicolon path.
+ {
+ parseComments: true,
+ input: "/* content: '*\\/' */",
+ expected: [
+ {
+ name: "content",
+ value: "'*/'",
+ priority: "",
+ offsets: [3, 17],
+ declarationText: "content: '*\\/'",
+ commentOffsets: [0, 20],
+ },
+ ],
+ },
+
+ // A comment-in-a-comment should yield the correct offsets.
+ {
+ parseComments: true,
+ input: "/* color: /\\* comment *\\/ red; */",
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [3, 30],
+ declarationText: "color: /\\* comment *\\/ red;",
+ commentOffsets: [0, 33],
+ },
+ ],
+ },
+
+ // HTML comments are not special -- they are just ordinary tokens.
+ {
+ parseComments: true,
+ input: "<!-- color: red; --> color: blue;",
+ expected: [
+ { name: "<!-- color", value: "red", priority: "", offsets: [0, 16] },
+ { name: "--> color", value: "blue", priority: "", offsets: [17, 33] },
+ ],
+ },
+
+ // Don't error on an empty comment.
+ {
+ parseComments: true,
+ input: "/**/",
+ expected: [],
+ },
+
+ // Parsing our special comments skips the name-check heuristic.
+ {
+ parseComments: true,
+ input: "/*! walrus: zebra; */",
+ expected: [
+ {
+ name: "walrus",
+ value: "zebra",
+ priority: "",
+ offsets: [4, 18],
+ declarationText: "walrus: zebra;",
+ commentOffsets: [0, 21],
+ },
+ ],
+ },
+
+ // Regression test for bug 1287620.
+ {
+ input: "color: blue \\9 no\\_need",
+ expected: [
+ {
+ name: "color",
+ value: "blue \\9 no_need",
+ priority: "",
+ offsets: [0, 23],
+ declarationText: "color: blue \\9 no\\_need",
+ },
+ ],
+ },
+
+ // Regression test for bug 1297890 - don't paste tokens.
+ {
+ parseComments: true,
+ input: "stroke-dasharray: 1/*ThisIsAComment*/2;",
+ expected: [
+ {
+ name: "stroke-dasharray",
+ value: "1 2",
+ priority: "",
+ offsets: [0, 39],
+ declarationText: "stroke-dasharray: 1/*ThisIsAComment*/2;",
+ },
+ ],
+ },
+
+ // Regression test for bug 1384463 - don't trim significant
+ // whitespace.
+ {
+ // \u00a0 is non-breaking space.
+ input: "\u00a0vertical-align: top",
+ expected: [
+ {
+ name: "\u00a0vertical-align",
+ value: "top",
+ priority: "",
+ offsets: [0, 20],
+ declarationText: "\u00a0vertical-align: top",
+ },
+ ],
+ },
+
+ // Regression test for bug 1544223 - take CSS blocks into consideration before handling
+ // ; and : (i.e. don't advance to the property name or value automatically).
+ {
+ input: `--foo: (
+ :doodle {
+ @grid: 30x1 / 18vmin;
+ }
+ :container {
+ perspective: 30vmin;
+ }
+
+ @place-cell: center;
+ @size: 100%;
+
+ :after, :before {
+ content: '';
+ @size: 100%;
+ position: absolute;
+ border: 2.4vmin solid var(--color);
+ border-image: radial-gradient(
+ var(--color), transparent 60%
+ );
+ border-image-width: 4;
+ transform: rotate(@pn(0, 5deg));
+ }
+
+ will-change: transform, opacity;
+ animation: scale-up 15s linear infinite;
+ animation-delay: calc(-15s / @size() * @i());
+ box-shadow: inset 0 0 1em var(--color);
+ border-radius: 50%;
+ filter: var(--filter);
+
+ @keyframes scale-up {
+ 0%, 100% {
+ transform: translateZ(0) scale(0) rotate(0);
+ opacity: 0;
+ }
+ 50% {
+ opacity: 1;
+ }
+ 99% {
+ transform:
+ translateZ(30vmin)
+ scale(1)
+ rotate(-270deg);
+ }
+ }
+ );`,
+ expected: [
+ {
+ name: "--foo",
+ value:
+ "( :doodle { @grid: 30x1 / 18vmin; } :container { perspective: 30vmin; } " +
+ "@place-cell: center; @size: 100%; :after, :before { content: ''; @size: " +
+ "100%; position: absolute; border: 2.4vmin solid var(--color); " +
+ "border-image: radial-gradient( var(--color), transparent 60% ); " +
+ "border-image-width: 4; transform: rotate(@pn(0, 5deg)); } will-change: " +
+ "transform, opacity; animation: scale-up 15s linear infinite; " +
+ "animation-delay: calc(-15s / @size() * @i()); box-shadow: inset 0 0 1em " +
+ "var(--color); border-radius: 50%; filter: var(--filter); @keyframes " +
+ "scale-up { 0%, 100% { transform: translateZ(0) scale(0) rotate(0); " +
+ "opacity: 0; } 50% { opacity: 1; } 99% { transform: translateZ(30vmin) " +
+ "scale(1) rotate(-270deg); } } )",
+ priority: "",
+ offsets: [0, 1036],
+ },
+ ],
+ },
+
+ /***** Testing nested rules *****/
+
+ // Testing basic nesting with tagname selector
+ {
+ input: `
+ color: red;
+ div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with tagname + pseudo selector
+ {
+ input: `
+ color: red;
+ div:hover {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with id selector
+ {
+ input: `
+ color: red;
+ #myEl {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with class selector
+ {
+ input: `
+ color: red;
+ .myEl {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with & + ident selector
+ {
+ input: `
+ color: red;
+ & div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with direct child selector
+ {
+ input: `
+ color: red;
+ > div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with & and :hover pseudo-class selector
+ {
+ input: `
+ color: red;
+ &:hover {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with :not pseudo-class selector and non-leading &
+ {
+ input: `
+ color: red;
+ :not(&) {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with attribute selector
+ {
+ input: `
+ color: red;
+ [class] {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with & compound selector
+ {
+ input: `
+ color: red;
+ &div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with relative + selector
+ {
+ input: `
+ color: red;
+ + div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with relative ~ selector
+ {
+ input: `
+ color: red;
+ ~ div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting with relative * selector
+ {
+ input: `
+ color: red;
+ * div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing basic nesting after a property with !important
+ {
+ input: `
+ color: red !important;
+ & div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "important",
+ offsets: [7, 29],
+ declarationText: "color: red !important;",
+ },
+ ],
+ },
+
+ // Testing basic nesting after a comment
+ {
+ input: `
+ color: red;
+ /* nested rules */
+ & div {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing at-rules (with condition) nesting
+ {
+ input: `
+ color: red;
+ @media (orientation: landscape) {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing at-rules (without condition) nesting
+ {
+ input: `
+ color: red;
+ @media screen {
+ background: blue;
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing multi-level nesting
+ {
+ input: `
+ color: red;
+ &div {
+ &.active {
+ border: 1px;
+ }
+ padding: 10px;
+ }
+ background: gold;
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ {
+ name: "background",
+ value: "gold",
+ priority: "",
+ offsets: [121, 138],
+ declarationText: "background: gold;",
+ },
+ ],
+ },
+
+ // Testing multi-level nesting with at-rules
+ {
+ input: `
+ color: red;
+ @layer {
+ background: yellow;
+ @media screen {
+ & {
+ border-color: blue;
+ }
+ }
+ }
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+
+ // Testing sibling nested rules
+ {
+ input: `
+ color: red;
+ .active {
+ border: 1px;
+ }
+ &div {
+ padding: 10px;
+ }
+ border-color: cyan
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ {
+ name: "border-color",
+ value: "cyan",
+ priority: "",
+ offsets: [114, 132],
+ declarationText: "border-color: cyan",
+ },
+ ],
+ },
+
+ // Testing nesting interwined between property declarations
+ {
+ input: `
+ color: red;
+ .active {
+ border: 1px;
+ }
+ background: gold;
+ &div {
+ padding: 10px;
+ }
+ border-color: cyan
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ {
+ name: "background",
+ value: "gold",
+ priority: "",
+ offsets: [70, 87],
+ declarationText: "background: gold;",
+ },
+ {
+ name: "border-color",
+ value: "cyan",
+ priority: "",
+ offsets: [138, 156],
+ declarationText: "border-color: cyan",
+ },
+ ],
+ },
+
+ // Testing that "}" in content property does not impact the nested state
+ {
+ input: `
+ color: red;
+ &div {
+ content: "}"
+ color: blue;
+ }
+ background: gold;
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ {
+ name: "background",
+ value: "gold",
+ priority: "",
+ offsets: [88, 105],
+ declarationText: "background: gold;",
+ },
+ ],
+ },
+
+ // Testing that "}" in attribute selector does not impact the nested state
+ {
+ input: `
+ color: red;
+ + .foo {
+ [class="}"] {
+ padding: 10px;
+ }
+ }
+ background: gold;
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ {
+ name: "background",
+ value: "gold",
+ priority: "",
+ offsets: [105, 122],
+ declarationText: "background: gold;",
+ },
+ ],
+ },
+
+ // Testing that in function does not impact the nested state
+ {
+ input: `
+ color: red;
+ + .foo {
+ background: url("img.png?x=}")
+ }
+ background: gold;
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ {
+ name: "background",
+ value: "gold",
+ priority: "",
+ offsets: [87, 104],
+ declarationText: "background: gold;",
+ },
+ ],
+ },
+
+ // Testing that "}" in comment does not impact the nested state
+ {
+ input: `
+ color: red;
+ + .foo {
+ /* Check } */
+ padding: 10px;
+ }
+ background: gold;
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ {
+ name: "background",
+ value: "gold",
+ priority: "",
+ offsets: [93, 110],
+ declarationText: "background: gold;",
+ },
+ ],
+ },
+
+ // Testing that nested rules in comments aren't reported
+ {
+ parseComments: true,
+ input: "width: 5; /* div { color: cyan; } */ background: red;",
+ expected: [
+ {
+ name: "width",
+ value: "5",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "width: 5;",
+ },
+ {
+ name: "background",
+ value: "red",
+ priority: "",
+ offsets: [37, 53],
+ declarationText: "background: red;",
+ },
+ ],
+ },
+
+ // Testing that declarations in comments are still handled while nested rule in same comment is ignored
+ {
+ parseComments: true,
+ input:
+ "width: 5; /* padding: 12px; div { color: cyan; } margin: 1em; */ background: red;",
+ expected: [
+ {
+ name: "width",
+ value: "5",
+ priority: "",
+ offsets: [0, 9],
+ declarationText: "width: 5;",
+ },
+ {
+ name: "padding",
+ value: "12px",
+ priority: "",
+ offsets: [13, 27],
+ declarationText: "padding: 12px;",
+ },
+ {
+ name: "margin",
+ value: "1em",
+ priority: "",
+ offsets: [49, 61],
+ declarationText: "margin: 1em;",
+ },
+ {
+ name: "background",
+ value: "red",
+ priority: "",
+ offsets: [65, 81],
+ declarationText: "background: red;",
+ },
+ ],
+ },
+
+ // Testing nesting without closing bracket
+ {
+ input: `
+ color: red;
+ & div {
+ background: blue;
+ `,
+ expected: [
+ {
+ name: "color",
+ value: "red",
+ priority: "",
+ offsets: [7, 18],
+ declarationText: "color: red;",
+ },
+ ],
+ },
+];
+
+function run_test() {
+ run_basic_tests();
+ run_comment_tests();
+ run_named_tests();
+}
+
+// Test parseDeclarations.
+function run_basic_tests() {
+ for (const test of TEST_DATA) {
+ info("Test input string " + test.input);
+ let output;
+ try {
+ output = parseDeclarations(
+ isCssPropertyKnown,
+ test.input,
+ test.parseComments
+ );
+ } catch (e) {
+ info(
+ "parseDeclarations threw an exception with the given input " + "string"
+ );
+ if (test.throws) {
+ info("Exception expected");
+ Assert.ok(true);
+ } else {
+ info("Exception unexpected\n" + e);
+ Assert.ok(false);
+ }
+ }
+ if (output) {
+ assertOutput(test.input, output, test.expected);
+ }
+ }
+}
+
+const COMMENT_DATA = [
+ {
+ input: "content: 'hi",
+ expected: [
+ {
+ name: "content",
+ value: "'hi",
+ priority: "",
+ terminator: "';",
+ offsets: [2, 14],
+ colonOffsets: [9, 11],
+ commentOffsets: [0, 16],
+ },
+ ],
+ },
+ {
+ input: "text that once confounded the parser;",
+ expected: [],
+ },
+];
+
+// Test parseCommentDeclarations.
+function run_comment_tests() {
+ for (const test of COMMENT_DATA) {
+ info("Test input string " + test.input);
+ const output = _parseCommentDeclarations(
+ isCssPropertyKnown,
+ test.input,
+ 0,
+ test.input.length + 4
+ );
+ deepEqual(output, test.expected);
+ }
+}
+
+const NAMED_DATA = [
+ {
+ input: "position:absolute;top50px;height:50px;",
+ expected: [
+ {
+ name: "position",
+ value: "absolute",
+ priority: "",
+ terminator: "",
+ offsets: [0, 18],
+ colonOffsets: [8, 9],
+ },
+ {
+ name: "height",
+ value: "50px",
+ priority: "",
+ terminator: "",
+ offsets: [26, 38],
+ colonOffsets: [32, 33],
+ },
+ ],
+ },
+];
+
+// Test parseNamedDeclarations.
+function run_named_tests() {
+ for (const test of NAMED_DATA) {
+ info("Test input string " + test.input);
+ const output = parseNamedDeclarations(isCssPropertyKnown, test.input, true);
+ info(JSON.stringify(output));
+ deepEqual(output, test.expected);
+ }
+}
+
+function assertOutput(input, actual, expected) {
+ if (actual.length === expected.length) {
+ for (let i = 0; i < expected.length; i++) {
+ Assert.ok(!!actual[i]);
+ info(
+ "Check that the output item has the expected name, " +
+ "value and priority"
+ );
+ Assert.equal(expected[i].name, actual[i].name);
+ Assert.equal(expected[i].value, actual[i].value);
+ Assert.equal(expected[i].priority, actual[i].priority);
+ deepEqual(expected[i].offsets, actual[i].offsets);
+ if ("commentOffsets" in expected[i]) {
+ deepEqual(expected[i].commentOffsets, actual[i].commentOffsets);
+ }
+
+ if (expected[i].declarationText) {
+ Assert.equal(
+ input.substring(expected[i].offsets[0], expected[i].offsets[1]),
+ expected[i].declarationText
+ );
+ }
+ }
+ } else {
+ for (const prop of actual) {
+ info(
+ "Actual output contained: {name: " +
+ prop.name +
+ ", value: " +
+ prop.value +
+ ", priority: " +
+ prop.priority +
+ "}"
+ );
+ }
+ Assert.equal(actual.length, expected.length);
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/test_parsePseudoClassesAndAttributes.js b/devtools/client/shared/test/xpcshell/test_parsePseudoClassesAndAttributes.js
new file mode 100644
index 0000000000..6aa2185c7d
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_parsePseudoClassesAndAttributes.js
@@ -0,0 +1,202 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+ parsePseudoClassesAndAttributes,
+ SELECTOR_ATTRIBUTE,
+ SELECTOR_ELEMENT,
+ SELECTOR_PSEUDO_CLASS,
+} = require("resource://devtools/shared/css/parsing-utils.js");
+
+const TEST_DATA = [
+ // Test that a null input throws an exception
+ {
+ input: null,
+ throws: true,
+ },
+ // Test that a undefined input throws an exception
+ {
+ input: undefined,
+ throws: true,
+ },
+ {
+ input: ":root",
+ expected: [{ value: ":root", type: SELECTOR_PSEUDO_CLASS }],
+ },
+ {
+ input: ".testclass",
+ expected: [{ value: ".testclass", type: SELECTOR_ELEMENT }],
+ },
+ {
+ input: "div p",
+ expected: [{ value: "div p", type: SELECTOR_ELEMENT }],
+ },
+ {
+ input: "div > p",
+ expected: [{ value: "div > p", type: SELECTOR_ELEMENT }],
+ },
+ {
+ input: "a[hidden]",
+ expected: [
+ { value: "a", type: SELECTOR_ELEMENT },
+ { value: "[hidden]", type: SELECTOR_ATTRIBUTE },
+ ],
+ },
+ {
+ input: "a[hidden=true]",
+ expected: [
+ { value: "a", type: SELECTOR_ELEMENT },
+ { value: "[hidden=true]", type: SELECTOR_ATTRIBUTE },
+ ],
+ },
+ {
+ input: "a[hidden=true] p:hover",
+ expected: [
+ { value: "a", type: SELECTOR_ELEMENT },
+ { value: "[hidden=true]", type: SELECTOR_ATTRIBUTE },
+ { value: " p", type: SELECTOR_ELEMENT },
+ { value: ":hover", type: SELECTOR_PSEUDO_CLASS },
+ ],
+ },
+ {
+ input: 'a[checked="true"]',
+ expected: [
+ { value: "a", type: SELECTOR_ELEMENT },
+ { value: '[checked="true"]', type: SELECTOR_ATTRIBUTE },
+ ],
+ },
+ {
+ input: "a[title~=test]",
+ expected: [
+ { value: "a", type: SELECTOR_ELEMENT },
+ { value: "[title~=test]", type: SELECTOR_ATTRIBUTE },
+ ],
+ },
+ {
+ input: 'h1[hidden="true"][title^="Important"]',
+ expected: [
+ { value: "h1", type: SELECTOR_ELEMENT },
+ { value: '[hidden="true"]', type: SELECTOR_ATTRIBUTE },
+ { value: '[title^="Important"]', type: SELECTOR_ATTRIBUTE },
+ ],
+ },
+ {
+ input: "p:hover",
+ expected: [
+ { value: "p", type: SELECTOR_ELEMENT },
+ { value: ":hover", type: SELECTOR_PSEUDO_CLASS },
+ ],
+ },
+ {
+ input: "p + .testclass:hover",
+ expected: [
+ { value: "p + .testclass", type: SELECTOR_ELEMENT },
+ { value: ":hover", type: SELECTOR_PSEUDO_CLASS },
+ ],
+ },
+ {
+ input: "p::before",
+ expected: [
+ { value: "p", type: SELECTOR_ELEMENT },
+ { value: "::before", type: SELECTOR_PSEUDO_CLASS },
+ ],
+ },
+ {
+ input: "p:nth-child(2)",
+ expected: [
+ { value: "p", type: SELECTOR_ELEMENT },
+ { value: ":nth-child(2)", type: SELECTOR_PSEUDO_CLASS },
+ ],
+ },
+ {
+ input: 'p:not([title="test"]) .testclass',
+ expected: [
+ { value: "p", type: SELECTOR_ELEMENT },
+ { value: ':not([title="test"])', type: SELECTOR_PSEUDO_CLASS },
+ { value: " .testclass", type: SELECTOR_ELEMENT },
+ ],
+ },
+ {
+ input: "a\\:hover",
+ expected: [{ value: "a\\:hover", type: SELECTOR_ELEMENT }],
+ },
+ {
+ input: ":not(:lang(it))",
+ expected: [{ value: ":not(:lang(it))", type: SELECTOR_PSEUDO_CLASS }],
+ },
+ {
+ input: "p:not(:lang(it))",
+ expected: [
+ { value: "p", type: SELECTOR_ELEMENT },
+ { value: ":not(:lang(it))", type: SELECTOR_PSEUDO_CLASS },
+ ],
+ },
+ {
+ input: "p:not(p:lang(it))",
+ expected: [
+ { value: "p", type: SELECTOR_ELEMENT },
+ { value: ":not(p:lang(it))", type: SELECTOR_PSEUDO_CLASS },
+ ],
+ },
+ {
+ input: ":not(:lang(it)",
+ expected: [{ value: ":not(:lang(it)", type: SELECTOR_ELEMENT }],
+ },
+ {
+ input: ":not(:lang(it)))",
+ expected: [
+ { value: ":not(:lang(it))", type: SELECTOR_PSEUDO_CLASS },
+ { value: ")", type: SELECTOR_ELEMENT },
+ ],
+ },
+];
+
+function run_test() {
+ for (const test of TEST_DATA) {
+ dump("Test input string " + test.input + "\n");
+ let output;
+
+ try {
+ output = parsePseudoClassesAndAttributes(test.input);
+ } catch (e) {
+ dump(
+ "parsePseudoClassesAndAttributes threw an exception with the " +
+ "given input string\n"
+ );
+ if (test.throws) {
+ ok(true, "Exception expected");
+ } else {
+ dump();
+ ok(false, "Exception unexpected\n" + e);
+ }
+ }
+
+ if (output) {
+ assertOutput(output, test.expected);
+ }
+ }
+}
+
+function assertOutput(actual, expected) {
+ if (actual.length === expected.length) {
+ for (let i = 0; i < expected.length; i++) {
+ dump("Check that the output item has the expected value and type\n");
+ ok(!!actual[i]);
+ equal(expected[i].value, actual[i].value);
+ equal(expected[i].type, actual[i].type);
+ }
+ } else {
+ for (const prop of actual) {
+ dump(
+ "Actual output contained: {value: " +
+ prop.value +
+ ", type: " +
+ prop.type +
+ "}\n"
+ );
+ }
+ equal(actual.length, expected.length);
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/test_parseSingleValue.js b/devtools/client/shared/test/xpcshell/test_parseSingleValue.js
new file mode 100644
index 0000000000..4617c6de2c
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_parseSingleValue.js
@@ -0,0 +1,106 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const {
+ parseSingleValue,
+} = require("resource://devtools/shared/css/parsing-utils.js");
+const {
+ isCssPropertyKnown,
+} = require("resource://devtools/server/actors/css-properties.js");
+
+const TEST_DATA = [
+ { input: null, throws: true },
+ { input: undefined, throws: true },
+ { input: "", expected: { value: "", priority: "" } },
+ { input: " \t \t \n\n ", expected: { value: "", priority: "" } },
+ { input: "blue", expected: { value: "blue", priority: "" } },
+ {
+ input: "blue !important",
+ expected: { value: "blue", priority: "important" },
+ },
+ {
+ input: "blue!important",
+ expected: { value: "blue", priority: "important" },
+ },
+ {
+ input: "blue ! important",
+ expected: { value: "blue", priority: "important" },
+ },
+ {
+ input: "blue ! important",
+ expected: { value: "blue", priority: "important" },
+ },
+ { input: "blue !", expected: { value: "blue !", priority: "" } },
+ {
+ input: "blue !mportant",
+ expected: { value: "blue !mportant", priority: "" },
+ },
+ {
+ input: " blue !important ",
+ expected: { value: "blue", priority: "important" },
+ },
+ {
+ input: 'url("http://url.com/whyWouldYouDoThat!important.png") !important',
+ expected: {
+ value: 'url("http://url.com/whyWouldYouDoThat!important.png")',
+ priority: "important",
+ },
+ },
+ {
+ input: 'url("http://url.com/whyWouldYouDoThat!important.png")',
+ expected: {
+ value: 'url("http://url.com/whyWouldYouDoThat!important.png")',
+ priority: "",
+ },
+ },
+ {
+ input: '"content!important" !important',
+ expected: {
+ value: '"content!important"',
+ priority: "important",
+ },
+ },
+ {
+ input: '"content!important"',
+ expected: {
+ value: '"content!important"',
+ priority: "",
+ },
+ },
+ {
+ input: '"all the \\"\'\\\\ special characters"',
+ expected: {
+ value: '"all the \\"\'\\\\ special characters"',
+ priority: "",
+ },
+ },
+];
+
+function run_test() {
+ for (const test of TEST_DATA) {
+ info("Test input value " + test.input);
+ try {
+ const output = parseSingleValue(isCssPropertyKnown, test.input);
+ assertOutput(output, test.expected);
+ } catch (e) {
+ info(
+ "parseSingleValue threw an exception with the given input " + "value"
+ );
+ if (test.throws) {
+ info("Exception expected");
+ Assert.ok(true);
+ } else {
+ info("Exception unexpected\n" + e);
+ Assert.ok(false);
+ }
+ }
+ }
+}
+
+function assertOutput(actual, expected) {
+ info("Check that the output has the expected value and priority");
+ Assert.equal(expected.value, actual.value);
+ Assert.equal(expected.priority, actual.priority);
+}
diff --git a/devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js b/devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js
new file mode 100644
index 0000000000..228d2dc79d
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_rewriteDeclarations.js
@@ -0,0 +1,816 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const RuleRewriter = require("resource://devtools/client/fronts/inspector/rule-rewriter.js");
+const {
+ isCssPropertyKnown,
+} = require("resource://devtools/server/actors/css-properties.js");
+
+const TEST_DATA = [
+ {
+ desc: "simple set",
+ input: "p:v;",
+ instruction: { type: "set", name: "p", value: "N", priority: "", index: 0 },
+ expected: "p:N;",
+ },
+ {
+ desc: "simple set clearing !important",
+ input: "p:v !important;",
+ instruction: { type: "set", name: "p", value: "N", priority: "", index: 0 },
+ expected: "p:N;",
+ },
+ {
+ desc: "simple set adding !important",
+ input: "p:v;",
+ instruction: {
+ type: "set",
+ name: "p",
+ value: "N",
+ priority: "important",
+ index: 0,
+ },
+ expected: "p:N !important;",
+ },
+ {
+ desc: "simple set between comments",
+ input: "/*color:red;*/ p:v; /*color:green;*/",
+ instruction: { type: "set", name: "p", value: "N", priority: "", index: 1 },
+ expected: "/*color:red;*/ p:N; /*color:green;*/",
+ },
+ // The rule view can generate a "set" with a previously unknown
+ // property index; which should work like "create".
+ {
+ desc: "set at unknown index",
+ input: "a:b; e: f;",
+ instruction: { type: "set", name: "c", value: "d", priority: "", index: 2 },
+ expected: "a:b; e: f;c: d;",
+ },
+ {
+ desc: "simple rename",
+ input: "p:v;",
+ instruction: { type: "rename", name: "p", newName: "q", index: 0 },
+ expected: "q:v;",
+ },
+ // "rename" is passed the name that the user entered, and must do
+ // any escaping necessary to ensure that this is an identifier.
+ {
+ desc: "rename requiring escape",
+ input: "p:v;",
+ instruction: { type: "rename", name: "p", newName: "a b", index: 0 },
+ expected: "a\\ b:v;",
+ },
+ {
+ desc: "simple create",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "v",
+ priority: "important",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: v !important;",
+ },
+ {
+ desc: "create between two properties",
+ input: "a:b; e: f;",
+ instruction: {
+ type: "create",
+ name: "c",
+ value: "d",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "a:b; c: d;e: f;",
+ },
+ // "create" is passed the name that the user entered, and must do
+ // any escaping necessary to ensure that this is an identifier.
+ {
+ desc: "create requiring escape",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "a b",
+ value: "d",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "a\\ b: d;",
+ },
+ {
+ desc: "simple disable",
+ input: "p:v;",
+ instruction: { type: "enable", name: "p", value: false, index: 0 },
+ expected: "/*! p:v; */",
+ },
+ {
+ desc: "simple enable",
+ input: "/* color:v; */",
+ instruction: { type: "enable", name: "color", value: true, index: 0 },
+ expected: "color:v;",
+ },
+ {
+ desc: "enable with following property in comment",
+ input: "/* color:red; color: blue; */",
+ instruction: { type: "enable", name: "color", value: true, index: 0 },
+ expected: "color:red; /* color: blue; */",
+ },
+ {
+ desc: "enable with preceding property in comment",
+ input: "/* color:red; color: blue; */",
+ instruction: { type: "enable", name: "color", value: true, index: 1 },
+ expected: "/* color:red; */ color: blue;",
+ },
+ {
+ desc: "simple remove",
+ input: "a:b;c:d;e:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "a:b;e:f;",
+ },
+ {
+ desc: "disable with comment ender in string",
+ input: "content: '*/';",
+ instruction: { type: "enable", name: "content", value: false, index: 0 },
+ expected: "/*! content: '*\\/'; */",
+ },
+ {
+ desc: "enable with comment ender in string",
+ input: "/* content: '*\\/'; */",
+ instruction: { type: "enable", name: "content", value: true, index: 0 },
+ expected: "content: '*/';",
+ },
+ {
+ desc: "enable requiring semicolon insertion",
+ // Note the lack of a trailing semicolon in the comment.
+ input: "/* color:red */ color: blue;",
+ instruction: { type: "enable", name: "color", value: true, index: 0 },
+ expected: "color:red; color: blue;",
+ },
+ {
+ desc: "create requiring semicolon insertion",
+ // Note the lack of a trailing semicolon.
+ input: "color: red",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "color: red;a: b;",
+ },
+
+ // Newline insertion.
+ {
+ desc: "simple newline insertion",
+ input: "\ncolor: red;\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\ncolor: red;\na: b;\n",
+ },
+ // Newline insertion.
+ {
+ desc: "semicolon insertion before newline",
+ // Note the lack of a trailing semicolon.
+ input: "\ncolor: red\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\ncolor: red;\na: b;\n",
+ },
+ // Newline insertion.
+ {
+ desc: "newline and semicolon insertion",
+ // Note the lack of a trailing semicolon and newline.
+ input: "\ncolor: red",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\ncolor: red;\na: b;\n",
+ },
+
+ // Newline insertion and indentation.
+ {
+ desc: "indentation with create",
+ input: "\n color: red;\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n color: red;\n a: b;\n",
+ },
+ // Newline insertion and indentation.
+ {
+ desc: "indentation plus semicolon insertion before newline",
+ // Note the lack of a trailing semicolon.
+ input: "\n color: red\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n color: red;\n a: b;\n",
+ },
+ {
+ desc: "indentation inserted before trailing whitespace",
+ // Note the trailing whitespace. This could come from a rule
+ // like:
+ // @supports (mumble) {
+ // body {
+ // color: red;
+ // }
+ // }
+ // Here if we create a rule we don't want it to follow
+ // the indentation of the "}".
+ input: "\n color: red;\n ",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n color: red;\n a: b;\n ",
+ },
+ // Newline insertion and indentation.
+ {
+ desc: "indentation comes from preceding comment",
+ // Note how the comment comes before the declaration.
+ input: "\n /* comment */ color: red\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "\n /* comment */ color: red;\n a: b;\n",
+ },
+ // Default indentation.
+ {
+ desc: "use of default indentation",
+ input: "\n",
+ instruction: {
+ type: "create",
+ name: "a",
+ value: "b",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "\n\ta: b;\n",
+ },
+
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion removes newline",
+ input: "a:b;\nc:d;\ne:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "a:b;\ne:f;",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion remove blank line",
+ input: "\n a:b;\n c:d; \ne:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "\n a:b;\ne:f;",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion leaves comment",
+ input: "\n a:b;\n /* something */ c:d; \ne:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "\n a:b;\n /* something */ \ne:f;",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion leaves previous newline",
+ input: "\n a:b;\n c:d; \ne:f;",
+ instruction: { type: "remove", name: "e", index: 2 },
+ expected: "\n a:b;\n c:d; \n",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion removes trailing whitespace",
+ input: "\n a:b;\n c:d; \n e:f;",
+ instruction: { type: "remove", name: "e", index: 2 },
+ expected: "\n a:b;\n c:d; \n",
+ },
+ // Deletion handles newlines properly.
+ {
+ desc: "deletion preserves indentation",
+ input: " a:b;\n c:d; \n e:f;",
+ instruction: { type: "remove", name: "a", index: 0 },
+ expected: " c:d; \n e:f;",
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable single quote termination",
+ input: "/* content: 'hi */ color: red;",
+ instruction: { type: "enable", name: "content", value: true, index: 0 },
+ expected: "content: 'hi'; color: red;",
+ changed: { 0: "'hi'" },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create single quote termination",
+ input: "content: 'hi",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "content: 'hi';color: red;",
+ changed: { 0: "'hi'" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable double quote termination",
+ input: '/* content: "hi */ color: red;',
+ instruction: { type: "enable", name: "content", value: true, index: 0 },
+ expected: 'content: "hi"; color: red;',
+ changed: { 0: '"hi"' },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create double quote termination",
+ input: 'content: "hi',
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: 'content: "hi";color: red;',
+ changed: { 0: '"hi"' },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable url termination",
+ input: "/* background-image: url(something.jpg */ color: red;",
+ instruction: {
+ type: "enable",
+ name: "background-image",
+ value: true,
+ index: 0,
+ },
+ expected: "background-image: url(something.jpg); color: red;",
+ changed: { 0: "url(something.jpg)" },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create url termination",
+ input: "background-image: url(something.jpg",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "background-image: url(something.jpg);color: red;",
+ changed: { 0: "url(something.jpg)" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable url single quote termination",
+ input: "/* background-image: url('something.jpg */ color: red;",
+ instruction: {
+ type: "enable",
+ name: "background-image",
+ value: true,
+ index: 0,
+ },
+ expected: "background-image: url('something.jpg'); color: red;",
+ changed: { 0: "url('something.jpg')" },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "create url single quote termination",
+ input: "background-image: url('something.jpg",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "background-image: url('something.jpg');color: red;",
+ changed: { 0: "url('something.jpg')" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "create url double quote termination",
+ input: '/* background-image: url("something.jpg */ color: red;',
+ instruction: {
+ type: "enable",
+ name: "background-image",
+ value: true,
+ index: 0,
+ },
+ expected: 'background-image: url("something.jpg"); color: red;',
+ changed: { 0: 'url("something.jpg")' },
+ },
+ // Termination insertion corner case.
+ {
+ desc: "enable url double quote termination",
+ input: 'background-image: url("something.jpg',
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: 'background-image: url("something.jpg");color: red;',
+ changed: { 0: 'url("something.jpg")' },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "create backslash termination",
+ input: "something: \\",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "something: \\\\;color: red;",
+ // The lexer rewrites the token before we see it. However this is
+ // so obscure as to be inconsequential.
+ changed: { 0: "\uFFFD\\" },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable backslash single quote termination",
+ input: "something: '\\",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "something: '\\\\';color: red;",
+ changed: { 0: "'\\\\'" },
+ },
+ {
+ desc: "enable backslash double quote termination",
+ input: 'something: "\\',
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: 'something: "\\\\";color: red;',
+ changed: { 0: '"\\\\"' },
+ },
+
+ // Termination insertion corner case.
+ {
+ desc: "enable comment termination",
+ input: "something: blah /* comment ",
+ instruction: {
+ type: "create",
+ name: "color",
+ value: "red",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "something: blah /* comment*/; color: red;",
+ },
+
+ // Rewrite a "heuristic override" comment.
+ {
+ desc: "enable with heuristic override comment",
+ input: "/*! walrus: zebra; */",
+ instruction: { type: "enable", name: "walrus", value: true, index: 0 },
+ expected: "walrus: zebra;",
+ },
+
+ // Sanitize a bad value.
+ {
+ desc: "create sanitize unpaired brace",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "}",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: \\};",
+ changed: { 0: "\\}" },
+ },
+ // Sanitize a bad value.
+ {
+ desc: "set sanitize unpaired brace",
+ input: "walrus: zebra;",
+ instruction: {
+ type: "set",
+ name: "walrus",
+ value: "{{}}}",
+ priority: "",
+ index: 0,
+ },
+ expected: "walrus: {{}}\\};",
+ changed: { 0: "{{}}\\}" },
+ },
+ // Sanitize a bad value.
+ {
+ desc: "enable sanitize unpaired brace",
+ input: "/*! walrus: }*/",
+ instruction: { type: "enable", name: "walrus", value: true, index: 0 },
+ expected: "walrus: \\};",
+ changed: { 0: "\\}" },
+ },
+
+ // Creating a new declaration does not require an attempt to
+ // terminate a previous commented declaration.
+ {
+ desc: "disabled declaration does not need semicolon insertion",
+ input: "/*! no: semicolon */\n",
+ instruction: {
+ type: "create",
+ name: "walrus",
+ value: "zebra",
+ priority: "",
+ index: 1,
+ enabled: true,
+ },
+ expected: "/*! no: semicolon */\nwalrus: zebra;\n",
+ changed: {},
+ },
+
+ {
+ desc: "create commented-out property",
+ input: "p: v",
+ instruction: {
+ type: "create",
+ name: "shoveler",
+ value: "duck",
+ priority: "",
+ index: 1,
+ enabled: false,
+ },
+ expected: "p: v;/*! shoveler: duck; */",
+ },
+ {
+ desc: "disabled create with comment ender in string",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "content",
+ value: "'*/'",
+ priority: "",
+ index: 0,
+ enabled: false,
+ },
+ expected: "/*! content: '*\\/'; */",
+ },
+
+ {
+ desc: "delete disabled property",
+ input: "\n a:b;\n /* color:#f06; */\n e:f;",
+ instruction: { type: "remove", name: "color", index: 1 },
+ expected: "\n a:b;\n e:f;",
+ },
+ {
+ desc: "delete heuristic-disabled property",
+ input: "\n a:b;\n /*! c:d; */\n e:f;",
+ instruction: { type: "remove", name: "c", index: 1 },
+ expected: "\n a:b;\n e:f;",
+ },
+ {
+ desc: "delete disabled property leaving other disabled property",
+ input: "\n a:b;\n /* color:#f06; background-color: seagreen; */\n e:f;",
+ instruction: { type: "remove", name: "color", index: 1 },
+ expected: "\n a:b;\n /* background-color: seagreen; */\n e:f;",
+ },
+
+ {
+ desc: "regression test for bug 1328016",
+ input: "position:absolute;top50px;height:50px;width:50px;",
+ instruction: {
+ type: "set",
+ name: "width",
+ value: "60px",
+ priority: "",
+ index: 2,
+ },
+ expected: "position:absolute;top50px;height:50px;width:60px;",
+ },
+
+ {
+ desc: "url regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "url(",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: url();",
+ changed: { 0: "url()" },
+ },
+
+ {
+ desc: "url semicolon regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "url(;",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: url();",
+ changed: { 0: "url()" },
+ },
+
+ {
+ desc: "basic regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "(",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: \\(;",
+ changed: { 0: "\\(" },
+ },
+
+ {
+ desc: "unbalanced regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "({[})",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: ({\\[});",
+ changed: { 0: "({\\[})" },
+ },
+
+ {
+ desc: "function regression test for bug 1321970",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "func(1,2)",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: func(1,2);",
+ },
+
+ {
+ desc: "function regression test for bug 1355233",
+ input: "",
+ instruction: {
+ type: "create",
+ name: "p",
+ value: "func(",
+ priority: "",
+ index: 0,
+ enabled: true,
+ },
+ expected: "p: func\\(;",
+ changed: { 0: "func\\(" },
+ },
+];
+
+function rewriteDeclarations(inputString, instruction, defaultIndentation) {
+ const rewriter = new RuleRewriter(isCssPropertyKnown, null, inputString);
+ rewriter.defaultIndentation = defaultIndentation;
+
+ switch (instruction.type) {
+ case "rename":
+ rewriter.renameProperty(
+ instruction.index,
+ instruction.name,
+ instruction.newName
+ );
+ break;
+
+ case "enable":
+ rewriter.setPropertyEnabled(
+ instruction.index,
+ instruction.name,
+ instruction.value
+ );
+ break;
+
+ case "create":
+ rewriter.createProperty(
+ instruction.index,
+ instruction.name,
+ instruction.value,
+ instruction.priority,
+ instruction.enabled
+ );
+ break;
+
+ case "set":
+ rewriter.setProperty(
+ instruction.index,
+ instruction.name,
+ instruction.value,
+ instruction.priority
+ );
+ break;
+
+ case "remove":
+ rewriter.removeProperty(instruction.index, instruction.name);
+ break;
+
+ default:
+ throw new Error("unrecognized instruction");
+ }
+
+ return rewriter.getResult();
+}
+
+function run_test() {
+ for (const test of TEST_DATA) {
+ const { changed, text } = rewriteDeclarations(
+ test.input,
+ test.instruction,
+ "\t"
+ );
+ equal(text, test.expected, "output for " + test.desc);
+
+ let expectChanged;
+ if ("changed" in test) {
+ expectChanged = test.changed;
+ } else {
+ expectChanged = {};
+ }
+ deepEqual(changed, expectChanged, "changed result for " + test.desc);
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/test_source-utils.js b/devtools/client/shared/test/xpcshell/test_source-utils.js
new file mode 100644
index 0000000000..dec32eb531
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_source-utils.js
@@ -0,0 +1,249 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests utility functions contained in `source-utils.js`
+ */
+
+const sourceUtils = require("resource://devtools/client/shared/source-utils.js");
+
+const CHROME_URLS = [
+ "chrome://foo",
+ "resource://baz",
+ "jar:file:///Users/root",
+];
+
+const CONTENT_URLS = [
+ "http://mozilla.org",
+ "https://mozilla.org",
+ "file:///Users/root",
+ "app://fxosapp",
+ "blob:http://mozilla.org",
+ "blob:https://mozilla.org",
+];
+
+// Test `sourceUtils.parseURL`
+add_task(async function () {
+ let parsed = sourceUtils.parseURL("https://foo.com:8888/boo/bar.js?q=query");
+ equal(parsed.fileName, "bar.js", "parseURL parsed valid fileName");
+ equal(parsed.host, "foo.com:8888", "parseURL parsed valid host");
+ equal(parsed.hostname, "foo.com", "parseURL parsed valid hostname");
+ equal(parsed.port, "8888", "parseURL parsed valid port");
+ equal(
+ parsed.href,
+ "https://foo.com:8888/boo/bar.js?q=query",
+ "parseURL parsed valid href"
+ );
+
+ parsed = sourceUtils.parseURL("https://foo.com");
+ equal(
+ parsed.host,
+ "foo.com",
+ "parseURL parsed valid host when no port given"
+ );
+ equal(
+ parsed.hostname,
+ "foo.com",
+ "parseURL parsed valid hostname when no port given"
+ );
+
+ equal(
+ sourceUtils.parseURL("self-hosted"),
+ null,
+ "parseURL returns `null` for invalid URLs"
+ );
+});
+
+// Test `sourceUtils.isContentScheme`.
+add_task(async function () {
+ for (const url of CHROME_URLS) {
+ ok(
+ !sourceUtils.isContentScheme(url),
+ `${url} correctly identified as not content scheme`
+ );
+ }
+ for (const url of CONTENT_URLS) {
+ ok(
+ sourceUtils.isContentScheme(url),
+ `${url} correctly identified as content scheme`
+ );
+ }
+});
+
+// Test `sourceUtils.isChromeScheme`.
+add_task(async function () {
+ for (const url of CHROME_URLS) {
+ ok(
+ sourceUtils.isChromeScheme(url),
+ `${url} correctly identified as chrome scheme`
+ );
+ }
+ for (const url of CONTENT_URLS) {
+ ok(
+ !sourceUtils.isChromeScheme(url),
+ `${url} correctly identified as not chrome scheme`
+ );
+ }
+});
+
+// Test `sourceUtils.isWASM`.
+add_task(async function () {
+ ok(
+ sourceUtils.isWASM("wasm-function[66240] (?:13870536)"),
+ "wasm function correctly identified"
+ );
+ ok(
+ !sourceUtils.isWASM(CHROME_URLS[0]),
+ `A chrome url does not identify as wasm.`
+ );
+});
+
+// Test `sourceUtils.isDataScheme`.
+add_task(async function () {
+ const dataURI = "data:text/html;charset=utf-8,<!DOCTYPE html></html>";
+ ok(
+ sourceUtils.isDataScheme(dataURI),
+ `${dataURI} correctly identified as data scheme`
+ );
+
+ for (const url of CHROME_URLS) {
+ ok(
+ !sourceUtils.isDataScheme(url),
+ `${url} correctly identified as not data scheme`
+ );
+ }
+ for (const url of CONTENT_URLS) {
+ ok(
+ !sourceUtils.isDataScheme(url),
+ `${url} correctly identified as not data scheme`
+ );
+ }
+});
+
+// Test `sourceUtils.getSourceNames`.
+add_task(async function () {
+ testAbbreviation(
+ "http://example.com/foo/bar/baz/boo.js",
+ "boo.js",
+ "http://example.com/foo/bar/baz/boo.js",
+ "example.com"
+ );
+});
+
+// Test `sourceUtils.getSourceNames`.
+add_task(async function () {
+ // Check length
+ const longMalformedURL = `example.com${new Array(100)
+ .fill("/a")
+ .join("")}/file.js`;
+ Assert.lessOrEqual(
+ sourceUtils.getSourceNames(longMalformedURL).short.length,
+ 100,
+ "`short` names are capped at 100 characters"
+ );
+
+ testAbbreviation("self-hosted", "self-hosted", "self-hosted");
+ testAbbreviation("", "(unknown)", "(unknown)");
+
+ // Test shortening data URIs, stripping mime/charset
+ testAbbreviation(
+ "data:text/html;charset=utf-8,<!DOCTYPE html></html>",
+ "data:<!DOCTYPE html></html>",
+ "data:text/html;charset=utf-8,<!DOCTYPE html></html>"
+ );
+
+ const longDataURI = `data:image/png;base64,${new Array(100)
+ .fill("a")
+ .join("")}`;
+ const longDataURIShort = sourceUtils.getSourceNames(longDataURI).short;
+
+ // Test shortening data URIs and that the `short` result is capped
+ Assert.lessOrEqual(
+ longDataURIShort.length,
+ 100,
+ "`short` names are capped at 100 characters for data URIs"
+ );
+ equal(
+ longDataURIShort.substr(0, 10),
+ "data:aaaaa",
+ "truncated data URI short names still have `data:...`"
+ );
+
+ // Test simple URL and cache retrieval by calling the same input multiple times.
+ const testUrl = "http://example.com/foo/bar/baz/boo.js";
+ testAbbreviation(testUrl, "boo.js", testUrl, "example.com");
+ testAbbreviation(testUrl, "boo.js", testUrl, "example.com");
+
+ // Check query and hash and port
+ testAbbreviation(
+ "http://example.com:8888/foo/bar/baz.js?q=query#go",
+ "baz.js",
+ "http://example.com:8888/foo/bar/baz.js",
+ "example.com:8888"
+ );
+
+ // Trailing "/" with nothing beyond host
+ testAbbreviation(
+ "http://example.com/",
+ "/",
+ "http://example.com/",
+ "example.com"
+ );
+
+ // Trailing "/"
+ testAbbreviation(
+ "http://example.com/foo/bar/",
+ "bar",
+ "http://example.com/foo/bar/",
+ "example.com"
+ );
+
+ // Non-extension ending
+ testAbbreviation(
+ "http://example.com/bar",
+ "bar",
+ "http://example.com/bar",
+ "example.com"
+ );
+
+ // Check query
+ testAbbreviation(
+ "http://example.com/foo.js?bar=1&baz=2",
+ "foo.js",
+ "http://example.com/foo.js",
+ "example.com"
+ );
+
+ // Check query with trailing slash
+ testAbbreviation(
+ "http://example.com/foo/?bar=1&baz=2",
+ "foo",
+ "http://example.com/foo/",
+ "example.com"
+ );
+});
+
+// Test for source mapped file name
+add_task(async function () {
+ const { getSourceMappedFile } = sourceUtils;
+ const source = "baz.js";
+ const output = getSourceMappedFile(source);
+ equal(output, "baz.js", "correctly formats file name");
+ // Test for OSX file path
+ const source1 = "/foo/bar/baz.js";
+ const output1 = getSourceMappedFile(source1);
+ equal(output1, "baz.js", "correctly formats Linux file path");
+ // Test for Windows file path
+ const source2 = "Z:\\foo\\bar\\baz.js";
+ const output2 = getSourceMappedFile(source2);
+ equal(output2, "baz.js", "correctly formats Windows file path");
+});
+
+function testAbbreviation(source, short, long, host) {
+ const results = sourceUtils.getSourceNames(source);
+ equal(results.short, short, `${source} has correct "short" name`);
+ equal(results.long, long, `${source} has correct "long" name`);
+ equal(results.host, host, `${source} has correct "host" name`);
+}
diff --git a/devtools/client/shared/test/xpcshell/test_suggestion-picker.js b/devtools/client/shared/test/xpcshell/test_suggestion-picker.js
new file mode 100644
index 0000000000..9d08fba4cd
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_suggestion-picker.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Test the suggestion-picker helper methods.
+ */
+
+const {
+ findMostRelevantIndex,
+ findMostRelevantCssPropertyIndex,
+} = require("resource://devtools/client/shared/suggestion-picker.js");
+
+/**
+ * Run all tests defined below.
+ */
+function run_test() {
+ ensureMostRelevantIndexProvidedByHelperFunction();
+ ensureMostRelevantIndexProvidedByClassMethod();
+ ensureErrorThrownWithInvalidArguments();
+}
+
+/**
+ * Generic test data.
+ */
+const TEST_DATA = [
+ {
+ // Match in sortedItems array.
+ items: ["chrome", "edge", "firefox"],
+ sortedItems: ["firefox", "chrome", "edge"],
+ expectedIndex: 2,
+ },
+ {
+ // No match in sortedItems array.
+ items: ["apple", "oranges", "banana"],
+ sortedItems: ["kiwi", "pear", "peach"],
+ expectedIndex: 0,
+ },
+ {
+ // Empty items array.
+ items: [],
+ sortedItems: ["empty", "arrays", "can't", "have", "relevant", "indexes"],
+ expectedIndex: -1,
+ },
+];
+
+function ensureMostRelevantIndexProvidedByHelperFunction() {
+ info("Running ensureMostRelevantIndexProvidedByHelperFunction()");
+
+ for (const testData of TEST_DATA) {
+ const { items, sortedItems, expectedIndex } = testData;
+ const mostRelevantIndex = findMostRelevantIndex(items, sortedItems);
+ strictEqual(mostRelevantIndex, expectedIndex);
+ }
+}
+
+/**
+ * CSS properties test data.
+ */
+const CSS_TEST_DATA = [
+ {
+ items: [
+ "backface-visibility",
+ "background",
+ "background-attachment",
+ "background-blend-mode",
+ "background-clip",
+ "background-color",
+ "background-image",
+ "background-origin",
+ "background-position",
+ "background-repeat",
+ ],
+ expectedIndex: 1,
+ },
+ {
+ items: [
+ "caption-side",
+ "clear",
+ "clip",
+ "clip-path",
+ "clip-rule",
+ "color",
+ "color-interpolation",
+ "color-interpolation-filters",
+ "content",
+ "counter-increment",
+ ],
+ expectedIndex: 5,
+ },
+ {
+ items: ["direction", "display", "dominant-baseline"],
+ expectedIndex: 1,
+ },
+ {
+ items: [
+ "object-fit",
+ "object-position",
+ "offset-block-end",
+ "offset-block-start",
+ "offset-inline-end",
+ "offset-inline-start",
+ "opacity",
+ "order",
+ "orphans",
+ "outline",
+ ],
+ expectedIndex: 6,
+ },
+ {
+ items: [
+ "white-space",
+ "widows",
+ "width",
+ "will-change",
+ "word-break",
+ "word-spacing",
+ "word-wrap",
+ "writing-mode",
+ ],
+ expectedIndex: 2,
+ },
+];
+
+function ensureMostRelevantIndexProvidedByClassMethod() {
+ info("Running ensureMostRelevantIndexProvidedByClassMethod()");
+
+ for (const testData of CSS_TEST_DATA) {
+ const { items, expectedIndex } = testData;
+ const mostRelevantIndex = findMostRelevantCssPropertyIndex(items);
+ strictEqual(mostRelevantIndex, expectedIndex);
+ }
+}
+
+function ensureErrorThrownWithInvalidArguments() {
+ info("Running ensureErrorThrownWithInvalidTypeArgument()");
+
+ const expectedError = /Please provide valid items and sortedItems arrays\./;
+ // No arguments passed.
+ Assert.throws(() => findMostRelevantIndex(), expectedError);
+ // Invalid arguments passed.
+ Assert.throws(() => findMostRelevantIndex([]), expectedError);
+ Assert.throws(() => findMostRelevantIndex(null, []), expectedError);
+ Assert.throws(() => findMostRelevantIndex([], "string"), expectedError);
+ Assert.throws(() => findMostRelevantIndex("string", []), expectedError);
+}
diff --git a/devtools/client/shared/test/xpcshell/test_undoStack.js b/devtools/client/shared/test/xpcshell/test_undoStack.js
new file mode 100644
index 0000000000..dafb007120
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_undoStack.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { UndoStack } = require("resource://devtools/client/shared/undo.js");
+
+const MAX_SIZE = 5;
+
+function run_test() {
+ let str = "";
+ const stack = new UndoStack(MAX_SIZE);
+
+ function add(ch) {
+ stack.do(
+ function () {
+ str += ch;
+ },
+ function () {
+ str = str.slice(0, -1);
+ }
+ );
+ }
+
+ Assert.ok(!stack.canUndo());
+ Assert.ok(!stack.canRedo());
+
+ // Check adding up to the limit of the size
+ add("a");
+ Assert.ok(stack.canUndo());
+ Assert.ok(!stack.canRedo());
+
+ add("b");
+ add("c");
+ add("d");
+ add("e");
+
+ Assert.equal(str, "abcde");
+
+ // Check a simple undo+redo
+ stack.undo();
+
+ Assert.equal(str, "abcd");
+ Assert.ok(stack.canRedo());
+
+ stack.redo();
+ Assert.equal(str, "abcde");
+ Assert.ok(!stack.canRedo());
+
+ // Check an undo followed by a new action
+ stack.undo();
+ Assert.equal(str, "abcd");
+
+ add("q");
+ Assert.equal(str, "abcdq");
+ Assert.ok(!stack.canRedo());
+
+ stack.undo();
+ Assert.equal(str, "abcd");
+ stack.redo();
+ Assert.equal(str, "abcdq");
+
+ // Revert back to the beginning of the queue...
+ while (stack.canUndo()) {
+ stack.undo();
+ }
+ Assert.equal(str, "");
+
+ // Now put it all back....
+ while (stack.canRedo()) {
+ stack.redo();
+ }
+ Assert.equal(str, "abcdq");
+
+ // Now go over the undo limit...
+ add("1");
+ add("2");
+ add("3");
+
+ Assert.equal(str, "abcdq123");
+
+ // And now undoing the whole stack should only undo 5 actions.
+ while (stack.canUndo()) {
+ stack.undo();
+ }
+
+ Assert.equal(str, "abc");
+}
diff --git a/devtools/client/shared/test/xpcshell/test_unicode-url.js b/devtools/client/shared/test/xpcshell/test_unicode-url.js
new file mode 100644
index 0000000000..4fe7d1fdcf
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/test_unicode-url.js
@@ -0,0 +1,258 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests utility functions contained in `unicode-url.js`
+ */
+
+const {
+ getUnicodeUrl,
+ getUnicodeUrlPath,
+ getUnicodeHostname,
+} = require("resource://devtools/client/shared/unicode-url.js");
+
+// List of URLs used to test Unicode URL conversion
+const TEST_URLS = [
+ // Type: Readable ASCII URLs
+ // Expected: All of Unicode versions should equal to the raw.
+ {
+ raw: "https://example.org",
+ expectedUnicode: "https://example.org",
+ },
+ {
+ raw: "http://example.org",
+ expectedUnicode: "http://example.org",
+ },
+ {
+ raw: "ftp://example.org",
+ expectedUnicode: "ftp://example.org",
+ },
+ {
+ raw: "https://example.org.",
+ expectedUnicode: "https://example.org.",
+ },
+ {
+ raw: "https://example.org/",
+ expectedUnicode: "https://example.org/",
+ },
+ {
+ raw: "https://example.org/test",
+ expectedUnicode: "https://example.org/test",
+ },
+ {
+ raw: "https://example.org/test.html",
+ expectedUnicode: "https://example.org/test.html",
+ },
+ {
+ raw: "https://example.org/test.html?one=1&two=2",
+ expectedUnicode: "https://example.org/test.html?one=1&two=2",
+ },
+ {
+ raw: "https://example.org/test.html#here",
+ expectedUnicode: "https://example.org/test.html#here",
+ },
+ {
+ raw: "https://example.org/test.html?one=1&two=2#here",
+ expectedUnicode: "https://example.org/test.html?one=1&two=2#here",
+ },
+ // Type: Unreadable URLs with either Punycode domain names or URI-encoded
+ // paths
+ // Expected: Unreadable domain names and URI-encoded paths should be converted
+ // to readable Unicode.
+ {
+ raw: "https://xn--g6w.xn--8pv/test.html",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "https://\u6e2c.\u672c/test.html",
+ },
+ {
+ raw: "https://example.org/%E6%B8%AC%E8%A9%A6.html",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "https://example.org/\u6e2c\u8a66.html",
+ },
+ {
+ raw: "https://example.org/test.html?One=%E4%B8%80",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "https://example.org/test.html?One=\u4e00",
+ },
+ {
+ raw: "https://example.org/test.html?%E4%B8%80=1",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "https://example.org/test.html?\u4e00=1",
+ },
+ {
+ raw:
+ "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" +
+ "?%E4%B8%80=%E4%B8%80" +
+ "#%E6%AD%A4",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode:
+ "https://\u6e2c.\u672c/\u6e2c\u8a66.html" + "?\u4e00=\u4e00" + "#\u6b64",
+ },
+ // Type: data: URIs
+ // Expected: All should not be converted.
+ {
+ raw: "data:text/plain;charset=UTF-8;Hello%20world",
+ expectedUnicode: "data:text/plain;charset=UTF-8;Hello%20world",
+ },
+ {
+ raw: "data:text/plain;charset=UTF-8;%E6%B8%AC%20%E8%A9%A6",
+ expectedUnicode: "data:text/plain;charset=UTF-8;%E6%B8%AC%20%E8%A9%A6",
+ },
+ {
+ raw:
+ "data:image/png;base64,iVBORw0KGgoAAA" +
+ "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4" +
+ "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU" +
+ "5ErkJggg==",
+ expectedUnicode:
+ "data:image/png;base64,iVBORw0KGgoAAA" +
+ "ANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4" +
+ "//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU" +
+ "5ErkJggg==",
+ },
+ // Type: Malformed URLs
+ // Expected: All should not be converted.
+ {
+ raw: "://example.org/test",
+ expectedUnicode: "://example.org/test",
+ },
+ {
+ raw: "://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" + "?%E4%B8%80=%E4%B8%80",
+ expectedUnicode:
+ "://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9%A6.html" + "?%E4%B8%80=%E4%B8%80",
+ },
+ {
+ // %E8%A9 isn't a valid UTF-8 code, so this URL is malformed.
+ raw: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9",
+ expectedUnicode: "https://xn--g6w.xn--8pv/%E6%B8%AC%E8%A9",
+ },
+];
+
+// List of hostanmes used to test Unicode hostname conversion
+const TEST_HOSTNAMES = [
+ // Type: Readable ASCII hostnames
+ // Expected: All of Unicode versions should equal to the raw.
+ {
+ raw: "example",
+ expectedUnicode: "example",
+ },
+ {
+ raw: "example.org",
+ expectedUnicode: "example.org",
+ },
+ // Type: Unreadable Punycode hostnames
+ // Expected: Punycode should be converted to readable Unicode.
+ {
+ raw: "xn--g6w",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "\u6e2c",
+ },
+ {
+ raw: "xn--g6w.xn--8pv",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "\u6e2c.\u672c",
+ },
+];
+
+// List of URL paths used to test Unicode URL path conversion
+const TEST_URL_PATHS = [
+ // Type: Readable ASCII URL paths
+ // Expected: All of Unicode versions should equal to the raw.
+ {
+ raw: "test",
+ expectedUnicode: "test",
+ },
+ {
+ raw: "/",
+ expectedUnicode: "/",
+ },
+ {
+ raw: "/test",
+ expectedUnicode: "/test",
+ },
+ {
+ raw: "/test.html?one=1&two=2#here",
+ expectedUnicode: "/test.html?one=1&two=2#here",
+ },
+ // Type: Unreadable URI-encoded URL paths
+ // Expected: URL paths should be converted to readable Unicode.
+ {
+ raw: "/%E6%B8%AC%E8%A9%A6",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "/\u6e2c\u8a66",
+ },
+ {
+ raw: "/%E6%B8%AC%E8%A9%A6.html",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode: "/\u6e2c\u8a66.html",
+ },
+ {
+ raw:
+ "/%E6%B8%AC%E8%A9%A6.html" +
+ "?%E4%B8%80=%E4%B8%80&%E4%BA%8C=%E4%BA%8C" +
+ "#%E6%AD%A4",
+ // Do not type Unicode characters directly, because this test file isn't
+ // specified with a known encoding.
+ expectedUnicode:
+ "/\u6e2c\u8a66.html" + "?\u4e00=\u4e00&\u4e8c=\u4e8c" + "#\u6b64",
+ },
+ // Type: Malformed URL paths
+ // Expected: All should not be converted.
+ {
+ // %E8%A9 isn't a valid UTF-8 code, so this URL is malformed.
+ raw: "/%E6%B8%AC%E8%A9",
+ expectedUnicode: "/%E6%B8%AC%E8%A9",
+ },
+];
+
+function run_test() {
+ // Test URLs
+ for (const url of TEST_URLS) {
+ const result = getUnicodeUrl(url.raw);
+ equal(
+ result,
+ url.expectedUnicode,
+ "Test getUnicodeUrl: " +
+ url.raw +
+ " should be unicodized to " +
+ url.expectedUnicode
+ );
+ }
+
+ // Test hostnames
+ for (const hostname of TEST_HOSTNAMES) {
+ const result = getUnicodeHostname(hostname.raw);
+ equal(
+ result,
+ hostname.expectedUnicode,
+ "Test getUnicodeHostname: " +
+ hostname.raw +
+ " should be unicodized to " +
+ hostname.expectedUnicode
+ );
+ }
+
+ // Test URL paths
+ for (const urlPath of TEST_URL_PATHS) {
+ const result = getUnicodeUrlPath(urlPath.raw);
+ equal(
+ result,
+ urlPath.expectedUnicode,
+ "Test getUnicodeUrlPath: " +
+ urlPath.raw +
+ " should be unicodized to " +
+ urlPath.expectedUnicode
+ );
+ }
+}
diff --git a/devtools/client/shared/test/xpcshell/xpcshell.toml b/devtools/client/shared/test/xpcshell/xpcshell.toml
new file mode 100644
index 0000000000..4916be1092
--- /dev/null
+++ b/devtools/client/shared/test/xpcshell/xpcshell.toml
@@ -0,0 +1,57 @@
+[DEFAULT]
+tags = "devtools"
+head = "head.js"
+firefox-appdir = "browser"
+skip-if = ["os == 'android'"]
+
+support-files = ["../helper_color_data.js"]
+
+["test_VariablesView_getString_promise.js"]
+
+["test_WeakMapMap.js"]
+
+["test_advanceValidate.js"]
+
+["test_attribute-parsing-01.js"]
+
+["test_attribute-parsing-02.js"]
+
+["test_bezierCanvas.js"]
+
+["test_classnames.js"]
+
+["test_cssAngle.js"]
+
+["test_cssColor-01.js"]
+
+["test_cssColor-02.js"]
+
+["test_cssColor-8-digit-hex.js"]
+
+["test_cssColorDatabase.js"]
+
+["test_cubicBezier.js"]
+
+["test_curl.js"]
+
+["test_escapeCSSComment.js"]
+
+["test_hasCSSVariable.js"]
+
+["test_linearEasing.js"]
+
+["test_parseDeclarations.js"]
+
+["test_parsePseudoClassesAndAttributes.js"]
+
+["test_parseSingleValue.js"]
+
+["test_rewriteDeclarations.js"]
+
+["test_source-utils.js"]
+
+["test_suggestion-picker.js"]
+
+["test_undoStack.js"]
+
+["test_unicode-url.js"]
diff --git a/devtools/client/shared/theme-switching.js b/devtools/client/shared/theme-switching.js
new file mode 100644
index 0000000000..bc4d481a2f
--- /dev/null
+++ b/devtools/client/shared/theme-switching.js
@@ -0,0 +1,143 @@
+/* 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/. */
+
+/* eslint-env browser */
+"use strict";
+(function () {
+ const { require } = ChromeUtils.importESModule(
+ "resource://devtools/shared/loader/Loader.sys.mjs"
+ );
+ const {
+ gDevTools,
+ } = require("resource://devtools/client/framework/devtools.js");
+ const {
+ appendStyleSheet,
+ } = require("resource://devtools/client/shared/stylesheet-utils.js");
+
+ const {
+ getTheme,
+ getAutoTheme,
+ addThemeObserver,
+ removeThemeObserver,
+ } = require("resource://devtools/client/shared/theme.js");
+
+ const documentElement = document.documentElement;
+
+ let os;
+ const platform = navigator.platform;
+ if (platform.startsWith("Win")) {
+ os = "win";
+ } else if (platform.startsWith("Mac")) {
+ os = "mac";
+ } else {
+ os = "linux";
+ }
+
+ documentElement.setAttribute("platform", os);
+
+ // no-theme attributes allows to just est the platform attribute
+ // to have per-platform CSS working correctly.
+ if (documentElement.getAttribute("no-theme") === "true") {
+ return;
+ }
+
+ const devtoolsStyleSheets = new WeakMap();
+ let gOldTheme = "";
+
+ /*
+ * Notify the window that a theme switch finished so tests can check the DOM
+ */
+ function notifyWindow() {
+ window.dispatchEvent(new CustomEvent("theme-switch-complete", {}));
+ }
+
+ /*
+ * Apply all the sheets from `newTheme` and remove all of the sheets
+ * from `oldTheme`
+ */
+ function switchTheme(newTheme) {
+ if (newTheme === gOldTheme) {
+ return;
+ }
+ const oldTheme = gOldTheme;
+ gOldTheme = newTheme;
+
+ const oldThemeDef = gDevTools.getThemeDefinition(oldTheme);
+ let newThemeDef = gDevTools.getThemeDefinition(newTheme);
+
+ // The theme might not be available anymore (e.g. uninstalled)
+ // Use the default one.
+ if (!newThemeDef) {
+ newThemeDef = gDevTools.getThemeDefinition(getAutoTheme());
+ }
+
+ // Store the sheets in a WeakMap for access later when the theme gets
+ // unapplied. It's hard to query for processing instructions so this
+ // is an easy way to access them later without storing a property on
+ // the window
+ devtoolsStyleSheets.set(newThemeDef, []);
+
+ const loadEvents = [];
+ for (const url of newThemeDef.stylesheets) {
+ const { styleSheet, loadPromise } = appendStyleSheet(document, url);
+ devtoolsStyleSheets.get(newThemeDef).push(styleSheet);
+ loadEvents.push(loadPromise);
+ }
+
+ Promise.all(loadEvents).then(() => {
+ // Unload all stylesheets and classes from the old theme.
+ if (oldThemeDef) {
+ for (const name of oldThemeDef.classList) {
+ documentElement.classList.remove(name);
+ }
+
+ for (const sheet of devtoolsStyleSheets.get(oldThemeDef) || []) {
+ sheet.remove();
+ }
+
+ if (oldThemeDef.onUnapply) {
+ oldThemeDef.onUnapply(window, newTheme);
+ }
+ }
+
+ // Load all stylesheets and classes from the new theme.
+ for (const name of newThemeDef.classList) {
+ documentElement.classList.add(name);
+ }
+
+ if (newThemeDef.onApply) {
+ newThemeDef.onApply(window, oldTheme);
+ }
+
+ // Final notification for further theme-switching related logic.
+ gDevTools.emit("theme-switched", window, newTheme, oldTheme);
+ notifyWindow();
+ }, console.error);
+ }
+
+ function handleThemeChange() {
+ switchTheme(getTheme());
+ }
+
+ // Check if the current document or the embedder of the document enforces a
+ // theme.
+ const forcedTheme =
+ documentElement.getAttribute("force-theme") ||
+ window.top.document.documentElement.getAttribute("force-theme");
+
+ if (forcedTheme) {
+ switchTheme(forcedTheme);
+ } else {
+ switchTheme(getTheme());
+
+ addThemeObserver(handleThemeChange);
+ window.addEventListener(
+ "unload",
+ function () {
+ removeThemeObserver(handleThemeChange);
+ },
+ { once: true }
+ );
+ }
+})();
diff --git a/devtools/client/shared/theme.js b/devtools/client/shared/theme.js
new file mode 100644
index 0000000000..e3c3ff608d
--- /dev/null
+++ b/devtools/client/shared/theme.js
@@ -0,0 +1,102 @@
+/* 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/. */
+
+"use strict";
+
+const variableFileContents = require("raw!chrome://devtools/skin/variables.css");
+
+const THEME_SELECTOR_STRINGS = {
+ light: ":root.theme-light {",
+ dark: ":root.theme-dark {",
+ root: ":root {",
+};
+const THEME_PREF = "devtools.theme";
+
+/**
+ * Takes a theme name and returns the contents of its variable rule block.
+ * The first time this runs fetches the variables CSS file and caches it.
+ */
+function getThemeFile(name) {
+ // If there's no theme expected for this name, use `light` as default.
+ const selector = THEME_SELECTOR_STRINGS[name] || THEME_SELECTOR_STRINGS.light;
+
+ // This is a pretty naive way to find the contents between:
+ // selector {
+ // name: val;
+ // }
+ // There is test coverage for this feature (browser_theme.js)
+ // so if an } is introduced in the variables file it will catch that.
+ let theme = variableFileContents;
+ theme = theme.substring(theme.indexOf(selector));
+ theme = theme.substring(0, theme.indexOf("}"));
+
+ return theme;
+}
+
+/**
+ * Returns the "auto" theme.
+ */
+const getAutoTheme = (exports.getAutoTheme = () => {
+ return Services.appinfo.chromeColorSchemeIsDark ? "dark" : "light";
+});
+
+/**
+ * Returns the string value of the current theme,
+ * like "dark" or "light".
+ */
+const getTheme = (exports.getTheme = () => {
+ const theme = Services.prefs.getCharPref(THEME_PREF);
+ if (theme == "auto") {
+ return getAutoTheme();
+ }
+ return theme;
+});
+
+/**
+ * Returns a color indicated by `type` (like "toolbar-background", or
+ * "highlight-red"), with the ability to specify a theme, or use whatever the
+ * current theme is if left unset. If theme not found, falls back to "light"
+ * theme. Returns null if the type cannot be found for the theme given.
+ */
+/* eslint-disable no-unused-vars */
+const getColor = (exports.getColor = (type, theme) => {
+ const themeName = theme || getTheme();
+ let themeFile = getThemeFile(themeName);
+ let match = themeFile.match(new RegExp("--theme-" + type + ": (.*);"));
+ const variableMatch = match ? match[1].match(/var\((.*)\)/) : null;
+
+ // Check if the match is a color variable and retrieve the value of the color variable
+ // if needed
+ if (variableMatch) {
+ themeFile = getThemeFile("root");
+ match = themeFile.match(new RegExp(`${variableMatch[1]}: (.*);`));
+ }
+
+ // Return the appropriate variable in the theme, or otherwise, null.
+ return match ? match[1] : null;
+});
+
+/**
+ * Set the theme preference.
+ */
+const setTheme = (exports.setTheme = newTheme => {
+ Services.prefs.setCharPref(THEME_PREF, newTheme);
+});
+
+/**
+ * Add an observer for theme changes.
+ */
+const addThemeObserver = (exports.addThemeObserver = observer => {
+ Services.obs.addObserver(observer, "look-and-feel-changed");
+ Services.prefs.addObserver(THEME_PREF, observer);
+});
+
+/**
+ * Remove an observer for theme changes.
+ */
+const removeThemeObserver = (exports.removeThemeObserver = observer => {
+ Services.obs.removeObserver(observer, "look-and-feel-changed");
+ Services.prefs.removeObserver(THEME_PREF, observer);
+});
+/* eslint-enable */
diff --git a/devtools/client/shared/thread-utils.js b/devtools/client/shared/thread-utils.js
new file mode 100644
index 0000000000..9c29681b91
--- /dev/null
+++ b/devtools/client/shared/thread-utils.js
@@ -0,0 +1,89 @@
+/* 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/. */
+"use strict";
+
+const asyncStoreHelper = require("resource://devtools/client/shared/async-store-helper.js");
+const { validateBreakpointLocation } = ChromeUtils.import(
+ "resource://devtools/shared/validate-breakpoint.jsm"
+);
+
+const asyncStore = asyncStoreHelper("debugger", {
+ pendingBreakpoints: ["pending-breakpoints", {}],
+ tabs: ["tabs", []],
+ xhrBreakpoints: ["xhr-breakpoints", []],
+ eventListenerBreakpoints: ["event-listener-breakpoints", undefined],
+ blackboxedRanges: ["blackboxedRanges", {}],
+});
+exports.asyncStore = asyncStore;
+
+exports.getThreadOptions = async function () {
+ return {
+ shouldPauseOnDebuggerStatement: Services.prefs.getBoolPref(
+ "devtools.debugger.pause-on-debugger-statement"
+ ),
+ pauseOnExceptions: Services.prefs.getBoolPref(
+ "devtools.debugger.pause-on-exceptions"
+ ),
+ ignoreCaughtExceptions: Services.prefs.getBoolPref(
+ "devtools.debugger.ignore-caught-exceptions"
+ ),
+ shouldIncludeSavedFrames: Services.prefs.getBoolPref(
+ "devtools.debugger.features.async-captured-stacks"
+ ),
+ shouldIncludeAsyncLiveFrames: Services.prefs.getBoolPref(
+ "devtools.debugger.features.async-live-stacks"
+ ),
+ skipBreakpoints: Services.prefs.getBoolPref(
+ "devtools.debugger.skip-pausing"
+ ),
+ logEventBreakpoints: Services.prefs.getBoolPref(
+ "devtools.debugger.log-event-breakpoints"
+ ),
+ // This option is always true. See Bug 1654590 for removal.
+ observeAsmJS: true,
+ breakpoints: sanitizeBreakpoints(await asyncStore.pendingBreakpoints),
+ // XXX: `event-listener-breakpoints` is a copy of the event-listeners state
+ // of the debugger panel. The `active` property is therefore linked to
+ // the `active` property of the state.
+ // See devtools/client/debugger/src/reducers/event-listeners.js
+ eventBreakpoints:
+ ((await asyncStore.eventListenerBreakpoints) || {}).active || [],
+ };
+};
+
+/**
+ * Bug 1720512 - We used to store invalid breakpoints, leading to blank debugger.
+ * Filter out only the one that look invalid.
+ */
+function sanitizeBreakpoints(breakpoints) {
+ if (typeof breakpoints != "object") {
+ return {};
+ }
+ // We are not doing any assertion against keys,
+ // as it looks like we are never using them anywhere in frontend, nor backend.
+ const validBreakpoints = {};
+ for (const key in breakpoints) {
+ const bp = breakpoints[key];
+ try {
+ if (!bp) {
+ throw new Error("Undefined breakpoint");
+ }
+ // Debugger's main.js's `syncBreakpoints` will only use generatedLocation
+ // when restoring breakpoints.
+ validateBreakpointLocation(bp.generatedLocation);
+ // But Toolbox will still pass location to thread actor's reconfigure
+ // for target that don't support watcher+BreakpointListActor
+ validateBreakpointLocation(bp.location);
+ validBreakpoints[key] = bp;
+ } catch (e) {
+ console.error(
+ "Ignore invalid breakpoint from debugger store",
+ bp,
+ e.message
+ );
+ }
+ }
+ return validBreakpoints;
+}
+exports.sanitizeBreakpoints = sanitizeBreakpoints;
diff --git a/devtools/client/shared/toolbarbutton.css b/devtools/client/shared/toolbarbutton.css
new file mode 100644
index 0000000000..294900c7b7
--- /dev/null
+++ b/devtools/client/shared/toolbarbutton.css
@@ -0,0 +1,86 @@
+/* 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/. */
+
+/**
+ * Legacy `devtools-toolbarbutton` styles, extracted from common.css because
+ * we're only using them in Style Editor,
+ * and it makes maintaining the newer `devtools-button` styles easier.
+ */
+
+.devtools-toolbarbutton {
+ appearance: none;
+ background: transparent;
+ border: none;
+ border-radius: 2px;
+ color: var(--theme-body-color);
+ text-shadow: none;
+ padding: 2px;
+ margin: 1px;
+ white-space: nowrap;
+}
+
+.devtools-toolbarbutton:not([label])[checked=true] > .toolbarbutton-icon,
+.devtools-toolbarbutton:not([label])[open=true] > .toolbarbutton-icon {
+ color: var(--theme-icon-checked-color);
+}
+
+.devtools-toolbarbutton .toolbarbutton-icon {
+ width: 16px;
+ height: 16px;
+ -moz-context-properties: fill;
+ fill: currentColor;
+ color: var(--theme-icon-color);
+ direction: ltr;
+ font-size: 11px;
+}
+
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-icon {
+ margin: 0 3px;
+}
+
+.devtools-toolbarbutton:not([label]) > .toolbarbutton-text {
+ display: none;
+}
+
+.devtools-toolbarbutton[disabled] {
+ opacity: 0.5 !important;
+}
+
+.devtools-toolbarbutton[label] {
+ padding-inline: 6px;
+ background: var(--toolbarbutton-background);
+}
+
+.devtools-toolbarbutton:not([label]):is([checked],[open],:hover,:hover:active) {
+ background: var(--toolbarbutton-hover-background);
+}
+
+/* Selectable button which is unchecked. */
+
+.devtools-toolbarbutton[label]:not([checked=true],[disabled]):hover {
+ background-color: var(--toolbarbutton-hover-background);
+}
+
+.devtools-toolbarbutton:not([checked=true],[disabled])[label]:focus {
+ background-color: var(--toolbarbutton-focus-background);
+ color: var(--toolbarbutton-focus-color);
+}
+
+/* Selectable button which is checked. */
+
+.devtools-toolbarbutton:not([disabled])[label][checked=true],
+.devtools-toolbarbutton:not([disabled])[label][open] {
+ background: var(--toolbarbutton-checked-background);
+ color: var(--toolbarbutton-checked-color);
+}
+
+.devtools-toolbarbutton:not([disabled])[label][checked=true] .toolbarbutton-icon,
+.devtools-toolbarbutton:not([disabled])[label][open] .toolbarbutton-icon {
+ color: inherit;
+}
+
+.devtools-toolbarbutton:not([disabled])[label][checked=true]:focus,
+.devtools-toolbarbutton:not([disabled])[label][open]:focus {
+ background-color: var(--toolbarbutton-checked-focus-background);
+}
diff --git a/devtools/client/shared/undo.js b/devtools/client/shared/undo.js
new file mode 100644
index 0000000000..759d3a4aed
--- /dev/null
+++ b/devtools/client/shared/undo.js
@@ -0,0 +1,190 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * A simple undo stack manager.
+ *
+ * Actions are added along with the necessary code to
+ * reverse the action.
+ *
+ * @param integer maxUndo Maximum number of undo steps.
+ * defaults to 50.
+ */
+function UndoStack(maxUndo) {
+ this.maxUndo = maxUndo || 50;
+ this._stack = [];
+}
+
+exports.UndoStack = UndoStack;
+
+UndoStack.prototype = {
+ // Current index into the undo stack. Is positioned after the last
+ // currently-applied change.
+ _index: 0,
+
+ // The current batch depth (see startBatch() for details)
+ _batchDepth: 0,
+
+ destroy() {
+ this.uninstallController();
+ delete this._stack;
+ },
+
+ /**
+ * Start a collection of related changes. Changes will be batched
+ * together into one undo/redo item until endBatch() is called.
+ *
+ * Batches can be nested, in which case the outer batch will contain
+ * all items from the inner batches. This allows larger user
+ * actions made up of a collection of smaller actions to be
+ * undone as a single action.
+ */
+ startBatch() {
+ if (this._batchDepth++ === 0) {
+ this._batch = [];
+ }
+ },
+
+ /**
+ * End a batch of related changes, performing its action and adding
+ * it to the undo stack.
+ */
+ endBatch() {
+ if (--this._batchDepth > 0) {
+ return;
+ }
+
+ // Cut off the end of the undo stack at the current index,
+ // and the beginning to prevent a stack larger than maxUndo.
+ const start = Math.max(this._index + 1 - this.maxUndo, 0);
+ this._stack = this._stack.slice(start, this._index);
+
+ const batch = this._batch;
+ delete this._batch;
+ const entry = {
+ do() {
+ for (const item of batch) {
+ item.do();
+ }
+ },
+ undo() {
+ for (let i = batch.length - 1; i >= 0; i--) {
+ batch[i].undo();
+ }
+ },
+ };
+ this._stack.push(entry);
+ this._index = this._stack.length;
+ entry.do();
+ },
+
+ /**
+ * Perform an action, adding it to the undo stack.
+ *
+ * @param function toDo Called to perform the action.
+ * @param function undo Called to reverse the action.
+ */
+ do(toDo, undo) {
+ this.startBatch();
+ this._batch.push({ do: toDo, undo });
+ this.endBatch();
+ },
+
+ /*
+ * Returns true if undo() will do anything.
+ */
+ canUndo() {
+ return this._index > 0;
+ },
+
+ /**
+ * Undo the top of the undo stack.
+ *
+ * @return true if an action was undone.
+ */
+ undo() {
+ if (!this.canUndo()) {
+ return false;
+ }
+ this._stack[--this._index].undo();
+ return true;
+ },
+
+ /**
+ * Returns true if redo() will do anything.
+ */
+ canRedo() {
+ return this._stack.length > this._index;
+ },
+
+ /**
+ * Redo the most recently undone action.
+ *
+ * @return true if an action was redone.
+ */
+ redo() {
+ if (!this.canRedo()) {
+ return false;
+ }
+ this._stack[this._index++].do();
+ return true;
+ },
+
+ /**
+ * ViewController implementation for undo/redo.
+ */
+
+ /**
+ * Install this object as a command controller.
+ */
+ installController(controllerWindow) {
+ const controllers = controllerWindow.controllers;
+ // Only available when running in a Firefox panel.
+ if (!controllers || !controllers.appendController) {
+ return;
+ }
+
+ this._controllerWindow = controllerWindow;
+ controllers.appendController(this);
+ },
+
+ /**
+ * Uninstall this object from the command controller.
+ */
+ uninstallController() {
+ if (!this._controllerWindow) {
+ return;
+ }
+ this._controllerWindow.controllers.removeController(this);
+ },
+
+ supportsCommand(command) {
+ return command == "cmd_undo" || command == "cmd_redo";
+ },
+
+ isCommandEnabled(command) {
+ switch (command) {
+ case "cmd_undo":
+ return this.canUndo();
+ case "cmd_redo":
+ return this.canRedo();
+ }
+ return false;
+ },
+
+ doCommand(command) {
+ switch (command) {
+ case "cmd_undo":
+ return this.undo();
+ case "cmd_redo":
+ return this.redo();
+ default:
+ return null;
+ }
+ },
+
+ onEvent(event) {},
+};
diff --git a/devtools/client/shared/unicode-url.js b/devtools/client/shared/unicode-url.js
new file mode 100644
index 0000000000..36fae098eb
--- /dev/null
+++ b/devtools/client/shared/unicode-url.js
@@ -0,0 +1,106 @@
+/* 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/. */
+"use strict";
+
+const idnService = Cc["@mozilla.org/network/idn-service;1"].getService(
+ Ci.nsIIDNService
+);
+
+/**
+ * Gets a readble Unicode hostname from a hostname.
+ *
+ * If the `hostname` is a readable ASCII hostname, such as example.org, then
+ * this function will simply return the original `hostname`.
+ *
+ * If the `hostname` is a Punycode hostname representing a Unicode domain name,
+ * such as xn--g6w.xn--8pv, then this function will return the readable Unicode
+ * domain name by decoding the Punycode hostname.
+ *
+ * @param {string} hostname
+ * the hostname from which the Unicode hostname will be
+ * parsed, such as example.org, xn--g6w.xn--8pv.
+ * @return {string} The Unicode hostname. It may be the same as the `hostname`
+ * passed to this function if the `hostname` itself is
+ * a readable ASCII hostname or a Unicode hostname.
+ */
+function getUnicodeHostname(hostname) {
+ return idnService.convertToDisplayIDN(hostname, {});
+}
+
+/**
+ * Gets a readble Unicode URL pathname from a URL pathname.
+ *
+ * If the `urlPath` is a readable ASCII URL pathname, such as /a/b/c.js, then
+ * this function will simply return the original `urlPath`.
+ *
+ * If the `urlPath` is a URI-encoded pathname, such as %E8%A9%A6/%E6%B8%AC.js,
+ * then this function will return the readable Unicode pathname.
+ *
+ * If the `urlPath` is a malformed URL pathname, then this function will simply
+ * return the original `urlPath`.
+ *
+ * @param {string} urlPath
+ * the URL path from which the Unicode URL path will be parsed,
+ * such as /a/b/c.js, %E8%A9%A6/%E6%B8%AC.js.
+ * @return {string} The Unicode URL Path. It may be the same as the `urlPath`
+ * passed to this function if the `urlPath` itself is a readable
+ * ASCII url or a Unicode url.
+ */
+function getUnicodeUrlPath(urlPath) {
+ try {
+ return decodeURIComponent(urlPath);
+ } catch (err) {}
+ return urlPath;
+}
+
+/**
+ * Gets a readable Unicode URL from a URL.
+ *
+ * If the `url` is a readable ASCII URL, such as http://example.org/a/b/c.js,
+ * then this function will simply return the original `url`.
+ *
+ * If the `url` includes either an unreadable Punycode domain name or an
+ * unreadable URI-encoded pathname, such as
+ * http://xn--g6w.xn--8pv/%E8%A9%A6/%E6%B8%AC.js, then this function will return
+ * the readable URL by decoding all its unreadable URL components to Unicode
+ * characters. The character `#` is not decoded from escape sequences.
+ *
+ * If the `url` is a malformed URL, then this function will return the original
+ * `url`.
+ *
+ * If the `url` is a data: URI, then this function will return the original
+ * `url`.
+ *
+ * @param {string} url
+ * the full URL, or a data: URI. from which the readable URL
+ * will be parsed, such as, http://example.org/a/b/c.js,
+ * http://xn--g6w.xn--8pv/%E8%A9%A6/%E6%B8%AC.js
+ * @return {string} The readable URL. It may be the same as the `url` passed to
+ * this function if the `url` itself is readable.
+ */
+function getUnicodeUrl(url) {
+ try {
+ const { protocol, hostname } = new URL(url);
+ if (protocol === "data:") {
+ // Never convert a data: URI.
+ return url;
+ }
+ const readableHostname = getUnicodeHostname(hostname);
+
+ /* We use `decodeURIComponent` instead of decodeURI as the
+ * later does not decode some characters, it only can decode characters
+ * previously encoded by the encodeURI. See
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI#Description
+ */
+ url = decodeURIComponent(url);
+ return url.replace(hostname, readableHostname);
+ } catch (err) {}
+ return url;
+}
+
+module.exports = {
+ getUnicodeHostname,
+ getUnicodeUrlPath,
+ getUnicodeUrl,
+};
diff --git a/devtools/client/shared/vendor/D3_LICENSE b/devtools/client/shared/vendor/D3_LICENSE
new file mode 100644
index 0000000000..fb7d95d70b
--- /dev/null
+++ b/devtools/client/shared/vendor/D3_LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) 2014, Michael Bostock
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* The name Michael Bostock may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/devtools/client/shared/vendor/DAGRE_D3_LICENSE b/devtools/client/shared/vendor/DAGRE_D3_LICENSE
new file mode 100644
index 0000000000..1d64ed68ce
--- /dev/null
+++ b/devtools/client/shared/vendor/DAGRE_D3_LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2013 Chris Pettitt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/devtools/client/shared/vendor/FLUENT_REACT_UPGRADING b/devtools/client/shared/vendor/FLUENT_REACT_UPGRADING
new file mode 100644
index 0000000000..9aeb6a2a82
--- /dev/null
+++ b/devtools/client/shared/vendor/FLUENT_REACT_UPGRADING
@@ -0,0 +1,33 @@
+[//]: # (
+ 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/.
+)
+
+# Upgrading @fluent/react
+
+## Getting the Source
+
+```bash
+git clone https://github.com/projectfluent/fluent.js
+cd fluent.js
+```
+
+Then checkout the version/tag you want, e.g. `@fluent/react@0.10.0`.
+
+## Building fluent-react
+
+```bash
+npm install
+cd fluent-gecko
+make fluent-react.js
+cp dist/fluent-react.js <gecko-dev>/devtools/client/shared/vendor/fluent-react.js
+```
+
+## Patching fluent-react
+
+- Open `fluent-react.js`
+- Replace `require('react')` with `require('resource://devtools/client/shared/vendor/react.js')`
+- Replace `require('prop-types')` with `require('resource://devtools/client/shared/vendor/react-prop-types.js')`
+
+## Update the version:
+
+The current version is 0.10.0. Update this version number everywhere in this file.
diff --git a/devtools/client/shared/vendor/FUZZALDRIN_PLUS_LICENSE b/devtools/client/shared/vendor/FUZZALDRIN_PLUS_LICENSE
new file mode 100644
index 0000000000..552d54c47b
--- /dev/null
+++ b/devtools/client/shared/vendor/FUZZALDRIN_PLUS_LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015 Jean Christophe Roy
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/devtools/client/shared/vendor/MD5_LICENSE b/devtools/client/shared/vendor/MD5_LICENSE
new file mode 100644
index 0000000000..f476d11e7d
--- /dev/null
+++ b/devtools/client/shared/vendor/MD5_LICENSE
@@ -0,0 +1,27 @@
+Copyright © 2011-2012, Paul Vorbach.
+Copyright © 2009, Jeff Mott.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, this
+ list of conditions and the following disclaimer in the documentation and/or
+ other materials provided with the distribution.
+* Neither the name Crypto-JS nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/devtools/client/shared/vendor/MD5_UPGRADING.md b/devtools/client/shared/vendor/MD5_UPGRADING.md
new file mode 100644
index 0000000000..b3f9b51611
--- /dev/null
+++ b/devtools/client/shared/vendor/MD5_UPGRADING.md
@@ -0,0 +1,29 @@
+[//]: # (
+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/.
+)
+
+# Upgrading MD5
+
+## Introduction
+
+We are using the md5 module to compute hashes within the source map worker.
+From there we don't have easy access to privileged APIs...
+
+## Instructions
+
+```bash
+ $ wget https://github.com/pvorb/node-md5/archive/refs/tags/v2.3.0.tar.gz
+ $ tar zxvf v2.3.0.tar.gz
+ $ cd node-md5-2.3.0/
+```
+
+Here edit webpack.config.js to set `libraryTarget: "umd"`,
+otherwise it is packed into a script bundle instead of being kept as a commonjs.
+
+
+```bash
+ $ yarn webpack
+ $ cp dist/md5.min.js ../md5.js # this will copy it to devtools/client/shared/vendor/md5.js
+ $ cd ..
+ $ rm -rf v2.3.0.tar.gz node-md5-2.3.0
+```
diff --git a/devtools/client/shared/vendor/REACT_PROP_TYPES_UPGRADING.md b/devtools/client/shared/vendor/REACT_PROP_TYPES_UPGRADING.md
new file mode 100644
index 0000000000..71db97622e
--- /dev/null
+++ b/devtools/client/shared/vendor/REACT_PROP_TYPES_UPGRADING.md
@@ -0,0 +1,37 @@
+[//]: # (
+ 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/.
+)
+
+# Upgrading react-prop-types
+
+## Getting the Source
+
+```bash
+git clone git@github.com:facebook/prop-types.git
+cd prop-types
+```
+
+## Building
+
+```bash
+npm install
+NODE_ENV=development browserify index.js -t envify --standalone PropTypes -o react-prop-types-dev.js
+NODE_ENV=production browserify index.js -t envify --standalone PropTypes -o react-prop-types.js
+```
+
+## Copying files to your Firefox repo
+
+```bash
+mv react-prop-types.js /firefox/repo/devtools/client/shared/vendor/react-prop-types.js
+mv react-prop-types-dev.js /firefox/repo/devtools/client/shared/vendor/react-prop-types-dev.js
+```
+
+## Adding Version Info
+
+Add the version to the top of `react-prop-types.js` and `react-prop-types-dev.js`.
+
+```js
+ /**
+ * react-prop-types v15.6.0
+ */
+```
diff --git a/devtools/client/shared/vendor/REACT_REDUX_LICENSE b/devtools/client/shared/vendor/REACT_REDUX_LICENSE
new file mode 100644
index 0000000000..af2353dca4
--- /dev/null
+++ b/devtools/client/shared/vendor/REACT_REDUX_LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Dan Abramov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/devtools/client/shared/vendor/REACT_REDUX_UPGRADING.md b/devtools/client/shared/vendor/REACT_REDUX_UPGRADING.md
new file mode 100644
index 0000000000..2c8786b179
--- /dev/null
+++ b/devtools/client/shared/vendor/REACT_REDUX_UPGRADING.md
@@ -0,0 +1,36 @@
+[//]: # (
+ 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/.
+)
+
+# Upgrading react-redux
+
+## Getting the Source
+
+```bash
+git clone https://github.com/reactjs/react-redux
+cd react-redux
+git checkout v5.0.7 # checkout the right version tag
+```
+
+## Building
+
+```bash
+npm install
+npm run build:umd
+cp dist/react-redux.js <gecko-dev>/devtools/client/shared/vendor/react-redux.js
+```
+
+We no longer need the react-redux repo so feel free to delete it.
+
+## Patching react-redux
+
+- open `react-redux.js`
+- Add the version number to the top of the file:
+ ```
+ /**
+ * react-redux v5.0.7
+ */
+ ```
+- Replace all instances of `'react'` with `'resource://devtools/client/shared/vendor/react.js'` (including the quotes).
+- Replace all instances of `'redux'` with `'resource://devtools/client/shared/vendor/redux.js'` (including the quotes).
+- Replace all instances of `Function('return this')()` with `globalThis`. See Bug 1473549.
diff --git a/devtools/client/shared/vendor/REACT_ROUTER_DOM_LICENSE b/devtools/client/shared/vendor/REACT_ROUTER_DOM_LICENSE
new file mode 100644
index 0000000000..dc15fe3fd4
--- /dev/null
+++ b/devtools/client/shared/vendor/REACT_ROUTER_DOM_LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) React Training 2016-2018
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/devtools/client/shared/vendor/REACT_ROUTER_DOM_UPGRADING.md b/devtools/client/shared/vendor/REACT_ROUTER_DOM_UPGRADING.md
new file mode 100644
index 0000000000..8bd9a8b0c5
--- /dev/null
+++ b/devtools/client/shared/vendor/REACT_ROUTER_DOM_UPGRADING.md
@@ -0,0 +1,23 @@
+[//]: # (
+ 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/.
+)
+
+Follow this steps to upgrade the `react-router-dom` library:
+
+1. Clone the repository (note: this is a monorepository, it includes other libraries too):
+ `git clone git@github.com:ReactTraining/react-router.git`
+
+2. Install build dependencies:
+ `cd react-router`
+ `npm install`
+
+3. Run a build :
+ `npm run build`
+
+4. Grab the UMD build of `react-router-dom`, which is located in `packages/react-router-dom/umd/react-router-dom.js` and copy it into Firefox source tree.
+
+5. Edit `react-router-dom.js` and change `require('react')` for `require('resource://devtools/client/shared/vendor/react.js')`
+
+6. Update the version below:
+
+The current version is 4.3.1
diff --git a/devtools/client/shared/vendor/REACT_UPGRADING.md b/devtools/client/shared/vendor/REACT_UPGRADING.md
new file mode 100644
index 0000000000..95034f2da5
--- /dev/null
+++ b/devtools/client/shared/vendor/REACT_UPGRADING.md
@@ -0,0 +1,160 @@
+[//]: # (
+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/.
+)
+
+# Upgrading React
+
+## Introduction
+
+We use a version of React that has a few minor tweaks. We want to use an un-minified production version anyway so you need to build React yourself.
+
+## First, Upgrade react-prop-types.js
+
+You should start by upgrading our prop-types library to match the latest version of React. Please follow the instructions in `devtools/client/shared/vendor/REACT_PROP_TYPES_UPGRADING.md` before continuing.
+
+## Getting the Source
+
+```bash
+git clone https://github.com/facebook/react.git
+cd react
+git checkout v16.8.6 # or the version you are targetting
+```
+
+## Preparing to Build
+
+We need to disable minification and tree shaking as they overcomplicate the upgrade process without adding any benefits.
+
+- Open `scripts/rollup/build.js`
+- Find a method called `function getRollupOutputOptions()`
+- After `sourcemap: false` add `treeshake: false` and `freeze: false`
+- Remove `freeze: !isProduction,` from the same section.
+- Change this:
+
+ ```js
+ // Apply dead code elimination and/or minification.
+ isProduction &&
+ ```
+
+ To this:
+
+ ```js
+ {
+ transformBundle(source) {
+ return (
+ source.replace(/['"]react['"]/g,
+ "'resource://devtools/client/shared/vendor/react.js'")
+ .replace(/createElementNS\(['"]http:\/\/www\.w3\.org\/1999\/xhtml['"], ['"]resource://devtools\/client\/shared\/vendor\/react.js['"]\)/g,
+ "createElementNS('http://www.w3.org/1999/xhtml', 'react'")
+ .replace(/['"]react-dom['"]/g,
+ "'resource://devtools/client/shared/vendor/react-dom.js'")
+ .replace(/rendererPackageName:\s['"]resource://devtools\/client\/shared\/vendor\/react-dom.js['"]/g,
+ "rendererPackageName: 'react-dom'")
+ .replace(/ocument\.createElement\(/g,
+ "ocument.createElementNS('http://www.w3.org/1999/xhtml', ")
+ );
+ },
+ },
+ // Apply dead code elimination and/or minification.
+ false &&
+ ```
+
+ - Find `await createBundle` and remove all bundles in that block except for `UMD_DEV`, `UMD_PROD` and `NODE_DEV`.
+
+## Building
+
+```bash
+npm install --global yarn
+yarn
+yarn build
+```
+
+### Copy the Files Into your Firefox Repo
+
+```bash
+cd <react repo root>
+cp build/dist/react.production.min.js <gecko-dev>/devtools/client/shared/vendor/react.js
+cp build/dist/react-dom.production.min.js <gecko-dev>/devtools/client/shared/vendor/react-dom.js
+cp build/dist/react-dom-server.browser.production.min.js <gecko-dev>/devtools/client/shared/vendor/react-dom-server.js
+cp build/dist/react-dom-test-utils.production.min.js <gecko-dev>/devtools/client/shared/vendor/react-dom-test-utils.js
+cp build/dist/react.development.js <gecko-dev>/devtools/client/shared/vendor/react-dev.js
+cp build/dist/react-dom.development.js <gecko-dev>/devtools/client/shared/vendor/react-dom-dev.js
+cp build/dist/react-dom-server.browser.development.js <gecko-dev>/devtools/client/shared/vendor/react-dom-server-dev.js
+cp build/dist/react-dom-test-utils.development.js <gecko-dev>/devtools/client/shared/vendor/react-dom-test-utils-dev.js
+cp build/dist/react-test-renderer-shallow.production.min.js <gecko-dev>/devtools/client/shared/vendor/react-test-renderer-shallow.js
+cp build/dist/react-test-renderer.production.min.js <gecko-dev>/devtools/client/shared/vendor/react-test-renderer.js
+```
+
+From this point we will no longer need your react repository so feel free to delete it.
+
+## Debugger
+
+### Update React
+
+- Open `devtools/client/debugger/package.json`
+- Under `dependencies` update `react` and `react-dom` to the required version.
+- Under `devDependencies` you may also need to update `enzyme`, `enzyme-adapter-react-16` and `enzyme-to-json` to versions compatible with the new react version.
+
+### Build the debugger
+
+#### Check your .mozconfig
+
+- Ensure you are not in debug mode (`ac_add_options --disable-debug`).
+- Ensure you are not using the debug version of react (`ac_add_options --disable-debug-js-modules`).
+
+#### First build Firefox
+
+```bash
+cd <srcdir> # where sourcedir is the root of your Firefox repo.
+./mach build
+```
+
+#### Now update the debugger source
+
+```bash
+# Go to the debugger folder.
+cd devtools/client/debugger
+
+# Remove all node_modules folders.
+find . -name "node_modules" -exec rm -rf '{}' +
+
+# Install the new react and enzyme modules.
+yarn
+```
+
+### Run the debugger tests
+
+#### First run locally
+
+```bash
+node bin/try-runner.js
+```
+
+If there any failures fix them.
+
+**NOTE: If there are any jest failures you will get better output by running the jest tests directly using:**
+
+```bash
+yarn test
+```
+
+If any tests fail then fix them.
+
+#### Commit your changes
+
+Use `hg commit` or `hg amend` to commit your changes.
+
+#### Push to try
+
+Just because the tests run fine locally they may still fail on try. You should first ensure that `node bin/try-runner.js` passes on try:
+
+```bash
+cd <srcdir> # where sourcedir is the root of your Firefox repo.
+`./mach try fuzzy`
+```
+
+- When the interface appears type `debugger`.
+- Press `<enter>`.
+
+Once these tests pass on try then push to try as normal e.g. `./mach try -b do -p all -u all -t all -e all`.
+
+If try passes then go celebrate otherwise you are on your own.
diff --git a/devtools/client/shared/vendor/REDUX_LICENSE b/devtools/client/shared/vendor/REDUX_LICENSE
new file mode 100644
index 0000000000..af2353dca4
--- /dev/null
+++ b/devtools/client/shared/vendor/REDUX_LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Dan Abramov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/devtools/client/shared/vendor/REDUX_UPGRADING.md b/devtools/client/shared/vendor/REDUX_UPGRADING.md
new file mode 100644
index 0000000000..232c17e655
--- /dev/null
+++ b/devtools/client/shared/vendor/REDUX_UPGRADING.md
@@ -0,0 +1,32 @@
+[//]: # (
+ 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/.
+)
+
+# Upgrading redux
+
+## Getting the Source
+
+```bash
+git clone https://github.com/reactjs/redux
+cd redux
+git checkout v4.0.5 # checkout the right version tag
+```
+
+## Building
+
+```bash
+npm install
+npm run build
+cp dist/redux.js <gecko-dev>/devtools/client/shared/vendor/redux.js
+```
+
+## Patching react-redux
+
+- open `redux.js`
+- Add the version number to the top of the file:
+ ```
+ /**
+ * react-redux v4.0.5
+ */
+ ```
+- Replace all instances of `Function('return this')()` with `globalThis`. See Bug 1473549. \ No newline at end of file
diff --git a/devtools/client/shared/vendor/RESELECT_LICENSE b/devtools/client/shared/vendor/RESELECT_LICENSE
new file mode 100644
index 0000000000..964e056d8c
--- /dev/null
+++ b/devtools/client/shared/vendor/RESELECT_LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2018 Reselect Contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/devtools/client/shared/vendor/RESELECT_UPGRADING b/devtools/client/shared/vendor/RESELECT_UPGRADING
new file mode 100644
index 0000000000..5bb76178bf
--- /dev/null
+++ b/devtools/client/shared/vendor/RESELECT_UPGRADING
@@ -0,0 +1,12 @@
+/* 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/. */
+
+
+Follow these steps when adding/upgrading the reselect.js module:
+
+1. git clone https://github.com/reactjs/reselect - clone the repo
+2. yarn && yarn run build - compile the sources to a compiled JS module file
+3. cp dist/reselect.js $DEST_DIR - copy the compiled file to Firefox source tree
+
+The current version is 4.1.5 (last update in bug 1758973)
diff --git a/devtools/client/shared/vendor/WASMPARSER_UPGRADING b/devtools/client/shared/vendor/WASMPARSER_UPGRADING
new file mode 100644
index 0000000000..05a9e8abd3
--- /dev/null
+++ b/devtools/client/shared/vendor/WASMPARSER_UPGRADING
@@ -0,0 +1,14 @@
+# wasmparser version
+
+Current version is: 5.9.0
+
+# Upgrade process
+
+1. Pull latest release from npm and extract WasmDis.js and WasmParser.js, e.g.
+
+```
+curl https://registry.npmjs.org/wasmparser/-/wasmparser-5.9.0.tgz | tar -zx --strip-components 3 package/dist/cjs/{WasmDis,WasmParser}.js
+```
+
+2. Remove reference to source maps (last line)
+
diff --git a/devtools/client/shared/vendor/WHATWG_URL_LICENSE b/devtools/client/shared/vendor/WHATWG_URL_LICENSE
new file mode 100644
index 0000000000..54dfac39d9
--- /dev/null
+++ b/devtools/client/shared/vendor/WHATWG_URL_LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015–2016 Sebastian Mayr
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/devtools/client/shared/vendor/WasmDis.js b/devtools/client/shared/vendor/WasmDis.js
new file mode 100644
index 0000000000..a412de6b39
--- /dev/null
+++ b/devtools/client/shared/vendor/WasmDis.js
@@ -0,0 +1,2031 @@
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.DevToolsNameGenerator = exports.DevToolsNameResolver = exports.NameSectionReader = exports.WasmDisassembler = exports.LabelMode = exports.NumericNameResolver = exports.DefaultNameResolver = void 0;
+/* Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var WasmParser_js_1 = require("./WasmParser.js");
+var NAME_SECTION_NAME = "name";
+var INVALID_NAME_SYMBOLS_REGEX = /[^0-9A-Za-z!#$%&'*+.:<=>?@^_`|~\/\-]/;
+var INVALID_NAME_SYMBOLS_REGEX_GLOBAL = new RegExp(INVALID_NAME_SYMBOLS_REGEX.source, "g");
+function formatFloat32(n) {
+ if (n === 0)
+ return 1 / n < 0 ? "-0.0" : "0.0";
+ if (isFinite(n))
+ return n.toString();
+ if (!isNaN(n))
+ return n < 0 ? "-inf" : "inf";
+ var view = new DataView(new ArrayBuffer(8));
+ view.setFloat32(0, n, true);
+ var data = view.getInt32(0, true);
+ var payload = data & 0x7fffff;
+ var canonicalBits = 4194304; // 0x800..0
+ if (data > 0 && payload === canonicalBits)
+ return "nan";
+ // canonical NaN;
+ else if (payload === canonicalBits)
+ return "-nan";
+ return (data < 0 ? "-" : "+") + "nan:0x" + payload.toString(16);
+}
+function formatFloat64(n) {
+ if (n === 0)
+ return 1 / n < 0 ? "-0.0" : "0.0";
+ if (isFinite(n))
+ return n.toString();
+ if (!isNaN(n))
+ return n < 0 ? "-inf" : "inf";
+ var view = new DataView(new ArrayBuffer(8));
+ view.setFloat64(0, n, true);
+ var data1 = view.getUint32(0, true);
+ var data2 = view.getInt32(4, true);
+ var payload = data1 + (data2 & 0xfffff) * 4294967296;
+ var canonicalBits = 524288 * 4294967296; // 0x800..0
+ if (data2 > 0 && payload === canonicalBits)
+ return "nan";
+ // canonical NaN;
+ else if (payload === canonicalBits)
+ return "-nan";
+ return (data2 < 0 ? "-" : "+") + "nan:0x" + payload.toString(16);
+}
+function formatI32Array(bytes, count) {
+ var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ var result = [];
+ for (var i = 0; i < count; i++)
+ result.push("0x".concat(formatHex(dv.getInt32(i << 2, true), 8)));
+ return result.join(" ");
+}
+function formatI8Array(bytes, count) {
+ var dv = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
+ var result = [];
+ for (var i = 0; i < count; i++)
+ result.push("".concat(dv.getInt8(i)));
+ return result.join(" ");
+}
+function memoryAddressToString(address, code) {
+ var defaultAlignFlags;
+ switch (code) {
+ case 1036288 /* OperatorCode.v128_load */:
+ case 1036289 /* OperatorCode.i16x8_load8x8_s */:
+ case 1036290 /* OperatorCode.i16x8_load8x8_u */:
+ case 1036291 /* OperatorCode.i32x4_load16x4_s */:
+ case 1036292 /* OperatorCode.i32x4_load16x4_u */:
+ case 1036293 /* OperatorCode.i64x2_load32x2_s */:
+ case 1036294 /* OperatorCode.i64x2_load32x2_u */:
+ case 1036295 /* OperatorCode.v8x16_load_splat */:
+ case 1036296 /* OperatorCode.v16x8_load_splat */:
+ case 1036297 /* OperatorCode.v32x4_load_splat */:
+ case 1036298 /* OperatorCode.v64x2_load_splat */:
+ case 1036299 /* OperatorCode.v128_store */:
+ defaultAlignFlags = 4;
+ break;
+ case 41 /* OperatorCode.i64_load */:
+ case 55 /* OperatorCode.i64_store */:
+ case 43 /* OperatorCode.f64_load */:
+ case 57 /* OperatorCode.f64_store */:
+ case 65026 /* OperatorCode.memory_atomic_wait64 */:
+ case 65041 /* OperatorCode.i64_atomic_load */:
+ case 65048 /* OperatorCode.i64_atomic_store */:
+ case 65055 /* OperatorCode.i64_atomic_rmw_add */:
+ case 65062 /* OperatorCode.i64_atomic_rmw_sub */:
+ case 65069 /* OperatorCode.i64_atomic_rmw_and */:
+ case 65076 /* OperatorCode.i64_atomic_rmw_or */:
+ case 65083 /* OperatorCode.i64_atomic_rmw_xor */:
+ case 65090 /* OperatorCode.i64_atomic_rmw_xchg */:
+ case 65097 /* OperatorCode.i64_atomic_rmw_cmpxchg */:
+ case 1036381 /* OperatorCode.v128_load64_zero */:
+ case 1036375 /* OperatorCode.v128_load64_lane */:
+ case 1036379 /* OperatorCode.v128_store64_lane */:
+ defaultAlignFlags = 3;
+ break;
+ case 40 /* OperatorCode.i32_load */:
+ case 52 /* OperatorCode.i64_load32_s */:
+ case 53 /* OperatorCode.i64_load32_u */:
+ case 54 /* OperatorCode.i32_store */:
+ case 62 /* OperatorCode.i64_store32 */:
+ case 42 /* OperatorCode.f32_load */:
+ case 56 /* OperatorCode.f32_store */:
+ case 65024 /* OperatorCode.memory_atomic_notify */:
+ case 65025 /* OperatorCode.memory_atomic_wait32 */:
+ case 65040 /* OperatorCode.i32_atomic_load */:
+ case 65046 /* OperatorCode.i64_atomic_load32_u */:
+ case 65047 /* OperatorCode.i32_atomic_store */:
+ case 65053 /* OperatorCode.i64_atomic_store32 */:
+ case 65054 /* OperatorCode.i32_atomic_rmw_add */:
+ case 65060 /* OperatorCode.i64_atomic_rmw32_add_u */:
+ case 65061 /* OperatorCode.i32_atomic_rmw_sub */:
+ case 65067 /* OperatorCode.i64_atomic_rmw32_sub_u */:
+ case 65068 /* OperatorCode.i32_atomic_rmw_and */:
+ case 65074 /* OperatorCode.i64_atomic_rmw32_and_u */:
+ case 65075 /* OperatorCode.i32_atomic_rmw_or */:
+ case 65081 /* OperatorCode.i64_atomic_rmw32_or_u */:
+ case 65082 /* OperatorCode.i32_atomic_rmw_xor */:
+ case 65088 /* OperatorCode.i64_atomic_rmw32_xor_u */:
+ case 65089 /* OperatorCode.i32_atomic_rmw_xchg */:
+ case 65095 /* OperatorCode.i64_atomic_rmw32_xchg_u */:
+ case 65096 /* OperatorCode.i32_atomic_rmw_cmpxchg */:
+ case 65102 /* OperatorCode.i64_atomic_rmw32_cmpxchg_u */:
+ case 1036380 /* OperatorCode.v128_load32_zero */:
+ case 1036374 /* OperatorCode.v128_load32_lane */:
+ case 1036378 /* OperatorCode.v128_store32_lane */:
+ defaultAlignFlags = 2;
+ break;
+ case 46 /* OperatorCode.i32_load16_s */:
+ case 47 /* OperatorCode.i32_load16_u */:
+ case 50 /* OperatorCode.i64_load16_s */:
+ case 51 /* OperatorCode.i64_load16_u */:
+ case 59 /* OperatorCode.i32_store16 */:
+ case 61 /* OperatorCode.i64_store16 */:
+ case 65043 /* OperatorCode.i32_atomic_load16_u */:
+ case 65045 /* OperatorCode.i64_atomic_load16_u */:
+ case 65050 /* OperatorCode.i32_atomic_store16 */:
+ case 65052 /* OperatorCode.i64_atomic_store16 */:
+ case 65057 /* OperatorCode.i32_atomic_rmw16_add_u */:
+ case 65059 /* OperatorCode.i64_atomic_rmw16_add_u */:
+ case 65064 /* OperatorCode.i32_atomic_rmw16_sub_u */:
+ case 65066 /* OperatorCode.i64_atomic_rmw16_sub_u */:
+ case 65071 /* OperatorCode.i32_atomic_rmw16_and_u */:
+ case 65073 /* OperatorCode.i64_atomic_rmw16_and_u */:
+ case 65078 /* OperatorCode.i32_atomic_rmw16_or_u */:
+ case 65080 /* OperatorCode.i64_atomic_rmw16_or_u */:
+ case 65085 /* OperatorCode.i32_atomic_rmw16_xor_u */:
+ case 65087 /* OperatorCode.i64_atomic_rmw16_xor_u */:
+ case 65092 /* OperatorCode.i32_atomic_rmw16_xchg_u */:
+ case 65094 /* OperatorCode.i64_atomic_rmw16_xchg_u */:
+ case 65099 /* OperatorCode.i32_atomic_rmw16_cmpxchg_u */:
+ case 65101 /* OperatorCode.i64_atomic_rmw16_cmpxchg_u */:
+ case 1036373 /* OperatorCode.v128_load16_lane */:
+ case 1036377 /* OperatorCode.v128_store16_lane */:
+ defaultAlignFlags = 1;
+ break;
+ case 44 /* OperatorCode.i32_load8_s */:
+ case 45 /* OperatorCode.i32_load8_u */:
+ case 48 /* OperatorCode.i64_load8_s */:
+ case 49 /* OperatorCode.i64_load8_u */:
+ case 58 /* OperatorCode.i32_store8 */:
+ case 60 /* OperatorCode.i64_store8 */:
+ case 65042 /* OperatorCode.i32_atomic_load8_u */:
+ case 65044 /* OperatorCode.i64_atomic_load8_u */:
+ case 65049 /* OperatorCode.i32_atomic_store8 */:
+ case 65051 /* OperatorCode.i64_atomic_store8 */:
+ case 65056 /* OperatorCode.i32_atomic_rmw8_add_u */:
+ case 65058 /* OperatorCode.i64_atomic_rmw8_add_u */:
+ case 65063 /* OperatorCode.i32_atomic_rmw8_sub_u */:
+ case 65065 /* OperatorCode.i64_atomic_rmw8_sub_u */:
+ case 65070 /* OperatorCode.i32_atomic_rmw8_and_u */:
+ case 65072 /* OperatorCode.i64_atomic_rmw8_and_u */:
+ case 65077 /* OperatorCode.i32_atomic_rmw8_or_u */:
+ case 65079 /* OperatorCode.i64_atomic_rmw8_or_u */:
+ case 65084 /* OperatorCode.i32_atomic_rmw8_xor_u */:
+ case 65086 /* OperatorCode.i64_atomic_rmw8_xor_u */:
+ case 65091 /* OperatorCode.i32_atomic_rmw8_xchg_u */:
+ case 65093 /* OperatorCode.i64_atomic_rmw8_xchg_u */:
+ case 65098 /* OperatorCode.i32_atomic_rmw8_cmpxchg_u */:
+ case 65100 /* OperatorCode.i64_atomic_rmw8_cmpxchg_u */:
+ case 1036372 /* OperatorCode.v128_load8_lane */:
+ case 1036376 /* OperatorCode.v128_store8_lane */:
+ defaultAlignFlags = 0;
+ break;
+ }
+ if (address.flags == defaultAlignFlags)
+ // hide default flags
+ return !address.offset ? null : "offset=".concat(address.offset);
+ if (!address.offset)
+ // hide default offset
+ return "align=".concat(1 << address.flags);
+ return "offset=".concat(address.offset | 0, " align=").concat(1 << address.flags);
+}
+function limitsToString(limits) {
+ return (limits.initial + (limits.maximum !== undefined ? " " + limits.maximum : ""));
+}
+var paddingCache = ["0", "00", "000"];
+function formatHex(n, width) {
+ var s = (n >>> 0).toString(16).toUpperCase();
+ if (width === undefined || s.length >= width)
+ return s;
+ var paddingIndex = width - s.length - 1;
+ while (paddingIndex >= paddingCache.length)
+ paddingCache.push(paddingCache[paddingCache.length - 1] + "0");
+ return paddingCache[paddingIndex] + s;
+}
+var IndentIncrement = " ";
+function isValidName(name) {
+ return !INVALID_NAME_SYMBOLS_REGEX.test(name);
+}
+var DefaultNameResolver = /** @class */ (function () {
+ function DefaultNameResolver() {
+ }
+ DefaultNameResolver.prototype.getTypeName = function (index, isRef) {
+ return "$type" + index;
+ };
+ DefaultNameResolver.prototype.getTableName = function (index, isRef) {
+ return "$table" + index;
+ };
+ DefaultNameResolver.prototype.getMemoryName = function (index, isRef) {
+ return "$memory" + index;
+ };
+ DefaultNameResolver.prototype.getGlobalName = function (index, isRef) {
+ return "$global" + index;
+ };
+ DefaultNameResolver.prototype.getElementName = function (index, isRef) {
+ return "$elem".concat(index);
+ };
+ DefaultNameResolver.prototype.getTagName = function (index, isRef) {
+ return "$event".concat(index);
+ };
+ DefaultNameResolver.prototype.getFunctionName = function (index, isImport, isRef) {
+ return (isImport ? "$import" : "$func") + index;
+ };
+ DefaultNameResolver.prototype.getVariableName = function (funcIndex, index, isRef) {
+ return "$var" + index;
+ };
+ DefaultNameResolver.prototype.getFieldName = function (typeIndex, index, isRef) {
+ return "$field" + index;
+ };
+ DefaultNameResolver.prototype.getLabel = function (index) {
+ return "$label" + index;
+ };
+ return DefaultNameResolver;
+}());
+exports.DefaultNameResolver = DefaultNameResolver;
+var EMPTY_STRING_ARRAY = [];
+var DevToolsExportMetadata = /** @class */ (function () {
+ function DevToolsExportMetadata(functionExportNames, globalExportNames, memoryExportNames, tableExportNames, eventExportNames) {
+ this._functionExportNames = functionExportNames;
+ this._globalExportNames = globalExportNames;
+ this._memoryExportNames = memoryExportNames;
+ this._tableExportNames = tableExportNames;
+ this._eventExportNames = eventExportNames;
+ }
+ DevToolsExportMetadata.prototype.getFunctionExportNames = function (index) {
+ var _a;
+ return (_a = this._functionExportNames[index]) !== null && _a !== void 0 ? _a : EMPTY_STRING_ARRAY;
+ };
+ DevToolsExportMetadata.prototype.getGlobalExportNames = function (index) {
+ var _a;
+ return (_a = this._globalExportNames[index]) !== null && _a !== void 0 ? _a : EMPTY_STRING_ARRAY;
+ };
+ DevToolsExportMetadata.prototype.getMemoryExportNames = function (index) {
+ var _a;
+ return (_a = this._memoryExportNames[index]) !== null && _a !== void 0 ? _a : EMPTY_STRING_ARRAY;
+ };
+ DevToolsExportMetadata.prototype.getTableExportNames = function (index) {
+ var _a;
+ return (_a = this._tableExportNames[index]) !== null && _a !== void 0 ? _a : EMPTY_STRING_ARRAY;
+ };
+ DevToolsExportMetadata.prototype.getTagExportNames = function (index) {
+ var _a;
+ return (_a = this._eventExportNames[index]) !== null && _a !== void 0 ? _a : EMPTY_STRING_ARRAY;
+ };
+ return DevToolsExportMetadata;
+}());
+var NumericNameResolver = /** @class */ (function () {
+ function NumericNameResolver() {
+ }
+ NumericNameResolver.prototype.getTypeName = function (index, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getTableName = function (index, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getMemoryName = function (index, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getGlobalName = function (index, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getElementName = function (index, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getTagName = function (index, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getFunctionName = function (index, isImport, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getVariableName = function (funcIndex, index, isRef) {
+ return isRef ? "" + index : "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getFieldName = function (typeIndex, index, isRef) {
+ return isRef ? "" : index + "(;".concat(index, ";)");
+ };
+ NumericNameResolver.prototype.getLabel = function (index) {
+ return null;
+ };
+ return NumericNameResolver;
+}());
+exports.NumericNameResolver = NumericNameResolver;
+var LabelMode;
+(function (LabelMode) {
+ LabelMode[LabelMode["Depth"] = 0] = "Depth";
+ LabelMode[LabelMode["WhenUsed"] = 1] = "WhenUsed";
+ LabelMode[LabelMode["Always"] = 2] = "Always";
+})(LabelMode = exports.LabelMode || (exports.LabelMode = {}));
+var WasmDisassembler = /** @class */ (function () {
+ function WasmDisassembler() {
+ this._skipTypes = true;
+ this._exportMetadata = null;
+ this._lines = [];
+ this._offsets = [];
+ this._buffer = "";
+ this._indent = null;
+ this._indentLevel = 0;
+ this._addOffsets = false;
+ this._done = false;
+ this._currentPosition = 0;
+ this._nameResolver = new DefaultNameResolver();
+ this._labelMode = LabelMode.WhenUsed;
+ this._functionBodyOffsets = [];
+ this._currentFunctionBodyOffset = 0;
+ this._currentSectionId = -1 /* SectionCode.Unknown */;
+ this._logFirstInstruction = false;
+ this._reset();
+ }
+ WasmDisassembler.prototype._reset = function () {
+ this._types = [];
+ this._funcIndex = 0;
+ this._funcTypes = [];
+ this._importCount = 0;
+ this._globalCount = 0;
+ this._memoryCount = 0;
+ this._eventCount = 0;
+ this._tableCount = 0;
+ this._elementCount = 0;
+ this._expression = [];
+ this._backrefLabels = null;
+ this._labelIndex = 0;
+ };
+ Object.defineProperty(WasmDisassembler.prototype, "addOffsets", {
+ get: function () {
+ return this._addOffsets;
+ },
+ set: function (value) {
+ if (this._currentPosition)
+ throw new Error("Cannot switch addOffsets during processing.");
+ this._addOffsets = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(WasmDisassembler.prototype, "skipTypes", {
+ get: function () {
+ return this._skipTypes;
+ },
+ set: function (skipTypes) {
+ if (this._currentPosition)
+ throw new Error("Cannot switch skipTypes during processing.");
+ this._skipTypes = skipTypes;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(WasmDisassembler.prototype, "labelMode", {
+ get: function () {
+ return this._labelMode;
+ },
+ set: function (value) {
+ if (this._currentPosition)
+ throw new Error("Cannot switch labelMode during processing.");
+ this._labelMode = value;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(WasmDisassembler.prototype, "exportMetadata", {
+ get: function () {
+ return this._exportMetadata;
+ },
+ set: function (exportMetadata) {
+ if (this._currentPosition)
+ throw new Error("Cannot switch exportMetadata during processing.");
+ this._exportMetadata = exportMetadata;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(WasmDisassembler.prototype, "nameResolver", {
+ get: function () {
+ return this._nameResolver;
+ },
+ set: function (resolver) {
+ if (this._currentPosition)
+ throw new Error("Cannot switch nameResolver during processing.");
+ this._nameResolver = resolver;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ WasmDisassembler.prototype.appendBuffer = function (s) {
+ this._buffer += s;
+ };
+ WasmDisassembler.prototype.newLine = function () {
+ if (this.addOffsets)
+ this._offsets.push(this._currentPosition);
+ this._lines.push(this._buffer);
+ this._buffer = "";
+ };
+ WasmDisassembler.prototype.logStartOfFunctionBodyOffset = function () {
+ if (this.addOffsets) {
+ this._currentFunctionBodyOffset = this._currentPosition;
+ }
+ };
+ WasmDisassembler.prototype.logEndOfFunctionBodyOffset = function () {
+ if (this.addOffsets) {
+ this._functionBodyOffsets.push({
+ start: this._currentFunctionBodyOffset,
+ end: this._currentPosition,
+ });
+ }
+ };
+ WasmDisassembler.prototype.typeIndexToString = function (typeIndex) {
+ if (typeIndex >= 0)
+ return this._nameResolver.getTypeName(typeIndex, true);
+ switch (typeIndex) {
+ case -16 /* TypeKind.funcref */:
+ return "func";
+ case -17 /* TypeKind.externref */:
+ return "extern";
+ case -18 /* TypeKind.anyref */:
+ return "any";
+ case -19 /* TypeKind.eqref */:
+ return "eq";
+ case -20 /* TypeKind.i31ref */:
+ return "i31";
+ case -21 /* TypeKind.structref */:
+ return "struct";
+ case -22 /* TypeKind.arrayref */:
+ return "array";
+ case -13 /* TypeKind.nullfuncref */:
+ return "nofunc";
+ case -14 /* TypeKind.nullexternref */:
+ return "noextern";
+ case -15 /* TypeKind.nullref */:
+ return "none";
+ }
+ };
+ WasmDisassembler.prototype.typeToString = function (type) {
+ switch (type.kind) {
+ case -1 /* TypeKind.i32 */:
+ return "i32";
+ case -2 /* TypeKind.i64 */:
+ return "i64";
+ case -3 /* TypeKind.f32 */:
+ return "f32";
+ case -4 /* TypeKind.f64 */:
+ return "f64";
+ case -5 /* TypeKind.v128 */:
+ return "v128";
+ case -8 /* TypeKind.i8 */:
+ return "i8";
+ case -9 /* TypeKind.i16 */:
+ return "i16";
+ case -16 /* TypeKind.funcref */:
+ return "funcref";
+ case -17 /* TypeKind.externref */:
+ return "externref";
+ case -18 /* TypeKind.anyref */:
+ return "anyref";
+ case -19 /* TypeKind.eqref */:
+ return "eqref";
+ case -20 /* TypeKind.i31ref */:
+ return "i31ref";
+ case -21 /* TypeKind.structref */:
+ return "structref";
+ case -22 /* TypeKind.arrayref */:
+ return "arrayref";
+ case -13 /* TypeKind.nullfuncref */:
+ return "nullfuncref";
+ case -14 /* TypeKind.nullexternref */:
+ return "nullexternref";
+ case -15 /* TypeKind.nullref */:
+ return "nullref";
+ case -28 /* TypeKind.ref */:
+ return "(ref ".concat(this.typeIndexToString(type.ref_index), ")");
+ case -29 /* TypeKind.ref_null */:
+ return "(ref null ".concat(this.typeIndexToString(type.ref_index), ")");
+ default:
+ throw new Error("Unexpected type ".concat(JSON.stringify(type)));
+ }
+ };
+ WasmDisassembler.prototype.maybeMut = function (type, mutability) {
+ return mutability ? "(mut ".concat(type, ")") : type;
+ };
+ WasmDisassembler.prototype.globalTypeToString = function (type) {
+ var typeStr = this.typeToString(type.contentType);
+ return this.maybeMut(typeStr, !!type.mutability);
+ };
+ WasmDisassembler.prototype.printFuncType = function (typeIndex) {
+ var type = this._types[typeIndex];
+ if (type.params.length > 0) {
+ this.appendBuffer(" (param");
+ for (var i = 0; i < type.params.length; i++) {
+ this.appendBuffer(" ");
+ this.appendBuffer(this.typeToString(type.params[i]));
+ }
+ this.appendBuffer(")");
+ }
+ if (type.returns.length > 0) {
+ this.appendBuffer(" (result");
+ for (var i = 0; i < type.returns.length; i++) {
+ this.appendBuffer(" ");
+ this.appendBuffer(this.typeToString(type.returns[i]));
+ }
+ this.appendBuffer(")");
+ }
+ };
+ WasmDisassembler.prototype.printStructType = function (typeIndex) {
+ var type = this._types[typeIndex];
+ if (type.fields.length === 0)
+ return;
+ for (var i = 0; i < type.fields.length; i++) {
+ var fieldType = this.maybeMut(this.typeToString(type.fields[i]), type.mutabilities[i]);
+ var fieldName = this._nameResolver.getFieldName(typeIndex, i, false);
+ this.appendBuffer(" (field ".concat(fieldName, " ").concat(fieldType, ")"));
+ }
+ };
+ WasmDisassembler.prototype.printArrayType = function (typeIndex) {
+ var type = this._types[typeIndex];
+ this.appendBuffer(" (field ");
+ this.appendBuffer(this.maybeMut(this.typeToString(type.elementType), type.mutability));
+ };
+ WasmDisassembler.prototype.printBlockType = function (type) {
+ if (type.kind === -64 /* TypeKind.empty_block_type */) {
+ return;
+ }
+ if (type.kind === 0 /* TypeKind.unspecified */) {
+ if (this._types[type.index].form == -32 /* TypeKind.func */) {
+ return this.printFuncType(type.index);
+ }
+ else {
+ // Encoding error.
+ this.appendBuffer(" (type ".concat(type.index, ")"));
+ return;
+ }
+ }
+ this.appendBuffer(" (result ");
+ this.appendBuffer(this.typeToString(type));
+ this.appendBuffer(")");
+ };
+ WasmDisassembler.prototype.printString = function (b) {
+ this.appendBuffer('"');
+ for (var i = 0; i < b.length; i++) {
+ var byte = b[i];
+ if (byte < 0x20 ||
+ byte >= 0x7f ||
+ byte == /* " */ 0x22 ||
+ byte == /* \ */ 0x5c) {
+ this.appendBuffer("\\" + (byte >> 4).toString(16) + (byte & 15).toString(16));
+ }
+ else {
+ this.appendBuffer(String.fromCharCode(byte));
+ }
+ }
+ this.appendBuffer('"');
+ };
+ WasmDisassembler.prototype.printExpression = function (expression) {
+ for (var _i = 0, expression_1 = expression; _i < expression_1.length; _i++) {
+ var operator = expression_1[_i];
+ this.appendBuffer("(");
+ this.printOperator(operator);
+ this.appendBuffer(")");
+ }
+ };
+ // extraDepthOffset is used by "delegate" instructions.
+ WasmDisassembler.prototype.useLabel = function (depth, extraDepthOffset) {
+ if (extraDepthOffset === void 0) { extraDepthOffset = 0; }
+ if (!this._backrefLabels) {
+ return "" + depth;
+ }
+ var i = this._backrefLabels.length - depth - 1 - extraDepthOffset;
+ if (i < 0) {
+ return "" + depth;
+ }
+ var backrefLabel = this._backrefLabels[i];
+ if (!backrefLabel.useLabel) {
+ backrefLabel.useLabel = true;
+ backrefLabel.label = this._nameResolver.getLabel(this._labelIndex);
+ var line = this._lines[backrefLabel.line];
+ this._lines[backrefLabel.line] =
+ line.substring(0, backrefLabel.position) +
+ " " +
+ backrefLabel.label +
+ line.substring(backrefLabel.position);
+ this._labelIndex++;
+ }
+ return backrefLabel.label || "" + depth;
+ };
+ WasmDisassembler.prototype.printOperator = function (operator) {
+ var code = operator.code;
+ this.appendBuffer(WasmParser_js_1.OperatorCodeNames[code]);
+ switch (code) {
+ case 2 /* OperatorCode.block */:
+ case 3 /* OperatorCode.loop */:
+ case 4 /* OperatorCode.if */:
+ case 6 /* OperatorCode.try */:
+ if (this._labelMode !== LabelMode.Depth) {
+ var backrefLabel_1 = {
+ line: this._lines.length,
+ position: this._buffer.length,
+ useLabel: false,
+ label: null,
+ };
+ if (this._labelMode === LabelMode.Always) {
+ backrefLabel_1.useLabel = true;
+ backrefLabel_1.label = this._nameResolver.getLabel(this._labelIndex++);
+ if (backrefLabel_1.label) {
+ this.appendBuffer(" ");
+ this.appendBuffer(backrefLabel_1.label);
+ }
+ }
+ this._backrefLabels.push(backrefLabel_1);
+ }
+ this.printBlockType(operator.blockType);
+ break;
+ case 11 /* OperatorCode.end */:
+ if (this._labelMode === LabelMode.Depth) {
+ break;
+ }
+ var backrefLabel = this._backrefLabels.pop();
+ if (backrefLabel.label) {
+ this.appendBuffer(" ");
+ this.appendBuffer(backrefLabel.label);
+ }
+ break;
+ case 12 /* OperatorCode.br */:
+ case 13 /* OperatorCode.br_if */:
+ case 213 /* OperatorCode.br_on_null */:
+ case 214 /* OperatorCode.br_on_non_null */:
+ this.appendBuffer(" ");
+ this.appendBuffer(this.useLabel(operator.brDepth));
+ break;
+ case 64280 /* OperatorCode.br_on_cast */:
+ case 64281 /* OperatorCode.br_on_cast_fail */:
+ this.appendBuffer(" flags=" + operator.literal);
+ this.appendBuffer(" ");
+ this.appendBuffer(this.typeIndexToString(operator.srcType));
+ this.appendBuffer(" ");
+ this.appendBuffer(this.typeIndexToString(operator.refType));
+ this.appendBuffer(" ");
+ this.appendBuffer(this.useLabel(operator.brDepth));
+ break;
+ case 14 /* OperatorCode.br_table */:
+ for (var i = 0; i < operator.brTable.length; i++) {
+ this.appendBuffer(" ");
+ this.appendBuffer(this.useLabel(operator.brTable[i]));
+ }
+ break;
+ case 9 /* OperatorCode.rethrow */:
+ this.appendBuffer(" ");
+ this.appendBuffer(this.useLabel(operator.relativeDepth));
+ break;
+ case 24 /* OperatorCode.delegate */:
+ this.appendBuffer(" ");
+ this.appendBuffer(this.useLabel(operator.relativeDepth, 1));
+ break;
+ case 7 /* OperatorCode.catch */:
+ case 8 /* OperatorCode.throw */:
+ var tagName = this._nameResolver.getTagName(operator.tagIndex, true);
+ this.appendBuffer(" ".concat(tagName));
+ break;
+ case 208 /* OperatorCode.ref_null */:
+ this.appendBuffer(" ");
+ this.appendBuffer(this.typeIndexToString(operator.refType));
+ break;
+ case 16 /* OperatorCode.call */:
+ case 18 /* OperatorCode.return_call */:
+ case 210 /* OperatorCode.ref_func */:
+ var funcName = this._nameResolver.getFunctionName(operator.funcIndex, operator.funcIndex < this._importCount, true);
+ this.appendBuffer(" ".concat(funcName));
+ break;
+ case 17 /* OperatorCode.call_indirect */:
+ case 19 /* OperatorCode.return_call_indirect */:
+ this.printFuncType(operator.typeIndex);
+ break;
+ case 28 /* OperatorCode.select_with_type */: {
+ var selectType = this.typeToString(operator.selectType);
+ this.appendBuffer(" ".concat(selectType));
+ break;
+ }
+ case 32 /* OperatorCode.local_get */:
+ case 33 /* OperatorCode.local_set */:
+ case 34 /* OperatorCode.local_tee */:
+ var paramName = this._nameResolver.getVariableName(this._funcIndex, operator.localIndex, true);
+ this.appendBuffer(" ".concat(paramName));
+ break;
+ case 35 /* OperatorCode.global_get */:
+ case 36 /* OperatorCode.global_set */:
+ var globalName = this._nameResolver.getGlobalName(operator.globalIndex, true);
+ this.appendBuffer(" ".concat(globalName));
+ break;
+ case 40 /* OperatorCode.i32_load */:
+ case 41 /* OperatorCode.i64_load */:
+ case 42 /* OperatorCode.f32_load */:
+ case 43 /* OperatorCode.f64_load */:
+ case 44 /* OperatorCode.i32_load8_s */:
+ case 45 /* OperatorCode.i32_load8_u */:
+ case 46 /* OperatorCode.i32_load16_s */:
+ case 47 /* OperatorCode.i32_load16_u */:
+ case 48 /* OperatorCode.i64_load8_s */:
+ case 49 /* OperatorCode.i64_load8_u */:
+ case 50 /* OperatorCode.i64_load16_s */:
+ case 51 /* OperatorCode.i64_load16_u */:
+ case 52 /* OperatorCode.i64_load32_s */:
+ case 53 /* OperatorCode.i64_load32_u */:
+ case 54 /* OperatorCode.i32_store */:
+ case 55 /* OperatorCode.i64_store */:
+ case 56 /* OperatorCode.f32_store */:
+ case 57 /* OperatorCode.f64_store */:
+ case 58 /* OperatorCode.i32_store8 */:
+ case 59 /* OperatorCode.i32_store16 */:
+ case 60 /* OperatorCode.i64_store8 */:
+ case 61 /* OperatorCode.i64_store16 */:
+ case 62 /* OperatorCode.i64_store32 */:
+ case 65024 /* OperatorCode.memory_atomic_notify */:
+ case 65025 /* OperatorCode.memory_atomic_wait32 */:
+ case 65026 /* OperatorCode.memory_atomic_wait64 */:
+ case 65040 /* OperatorCode.i32_atomic_load */:
+ case 65041 /* OperatorCode.i64_atomic_load */:
+ case 65042 /* OperatorCode.i32_atomic_load8_u */:
+ case 65043 /* OperatorCode.i32_atomic_load16_u */:
+ case 65044 /* OperatorCode.i64_atomic_load8_u */:
+ case 65045 /* OperatorCode.i64_atomic_load16_u */:
+ case 65046 /* OperatorCode.i64_atomic_load32_u */:
+ case 65047 /* OperatorCode.i32_atomic_store */:
+ case 65048 /* OperatorCode.i64_atomic_store */:
+ case 65049 /* OperatorCode.i32_atomic_store8 */:
+ case 65050 /* OperatorCode.i32_atomic_store16 */:
+ case 65051 /* OperatorCode.i64_atomic_store8 */:
+ case 65052 /* OperatorCode.i64_atomic_store16 */:
+ case 65053 /* OperatorCode.i64_atomic_store32 */:
+ case 65054 /* OperatorCode.i32_atomic_rmw_add */:
+ case 65055 /* OperatorCode.i64_atomic_rmw_add */:
+ case 65056 /* OperatorCode.i32_atomic_rmw8_add_u */:
+ case 65057 /* OperatorCode.i32_atomic_rmw16_add_u */:
+ case 65058 /* OperatorCode.i64_atomic_rmw8_add_u */:
+ case 65059 /* OperatorCode.i64_atomic_rmw16_add_u */:
+ case 65060 /* OperatorCode.i64_atomic_rmw32_add_u */:
+ case 65061 /* OperatorCode.i32_atomic_rmw_sub */:
+ case 65062 /* OperatorCode.i64_atomic_rmw_sub */:
+ case 65063 /* OperatorCode.i32_atomic_rmw8_sub_u */:
+ case 65064 /* OperatorCode.i32_atomic_rmw16_sub_u */:
+ case 65065 /* OperatorCode.i64_atomic_rmw8_sub_u */:
+ case 65066 /* OperatorCode.i64_atomic_rmw16_sub_u */:
+ case 65067 /* OperatorCode.i64_atomic_rmw32_sub_u */:
+ case 65068 /* OperatorCode.i32_atomic_rmw_and */:
+ case 65069 /* OperatorCode.i64_atomic_rmw_and */:
+ case 65070 /* OperatorCode.i32_atomic_rmw8_and_u */:
+ case 65071 /* OperatorCode.i32_atomic_rmw16_and_u */:
+ case 65072 /* OperatorCode.i64_atomic_rmw8_and_u */:
+ case 65073 /* OperatorCode.i64_atomic_rmw16_and_u */:
+ case 65074 /* OperatorCode.i64_atomic_rmw32_and_u */:
+ case 65075 /* OperatorCode.i32_atomic_rmw_or */:
+ case 65076 /* OperatorCode.i64_atomic_rmw_or */:
+ case 65077 /* OperatorCode.i32_atomic_rmw8_or_u */:
+ case 65078 /* OperatorCode.i32_atomic_rmw16_or_u */:
+ case 65079 /* OperatorCode.i64_atomic_rmw8_or_u */:
+ case 65080 /* OperatorCode.i64_atomic_rmw16_or_u */:
+ case 65081 /* OperatorCode.i64_atomic_rmw32_or_u */:
+ case 65082 /* OperatorCode.i32_atomic_rmw_xor */:
+ case 65083 /* OperatorCode.i64_atomic_rmw_xor */:
+ case 65084 /* OperatorCode.i32_atomic_rmw8_xor_u */:
+ case 65085 /* OperatorCode.i32_atomic_rmw16_xor_u */:
+ case 65086 /* OperatorCode.i64_atomic_rmw8_xor_u */:
+ case 65087 /* OperatorCode.i64_atomic_rmw16_xor_u */:
+ case 65088 /* OperatorCode.i64_atomic_rmw32_xor_u */:
+ case 65089 /* OperatorCode.i32_atomic_rmw_xchg */:
+ case 65090 /* OperatorCode.i64_atomic_rmw_xchg */:
+ case 65091 /* OperatorCode.i32_atomic_rmw8_xchg_u */:
+ case 65092 /* OperatorCode.i32_atomic_rmw16_xchg_u */:
+ case 65093 /* OperatorCode.i64_atomic_rmw8_xchg_u */:
+ case 65094 /* OperatorCode.i64_atomic_rmw16_xchg_u */:
+ case 65095 /* OperatorCode.i64_atomic_rmw32_xchg_u */:
+ case 65096 /* OperatorCode.i32_atomic_rmw_cmpxchg */:
+ case 65097 /* OperatorCode.i64_atomic_rmw_cmpxchg */:
+ case 65098 /* OperatorCode.i32_atomic_rmw8_cmpxchg_u */:
+ case 65099 /* OperatorCode.i32_atomic_rmw16_cmpxchg_u */:
+ case 65100 /* OperatorCode.i64_atomic_rmw8_cmpxchg_u */:
+ case 65101 /* OperatorCode.i64_atomic_rmw16_cmpxchg_u */:
+ case 65102 /* OperatorCode.i64_atomic_rmw32_cmpxchg_u */:
+ case 1036288 /* OperatorCode.v128_load */:
+ case 1036289 /* OperatorCode.i16x8_load8x8_s */:
+ case 1036290 /* OperatorCode.i16x8_load8x8_u */:
+ case 1036291 /* OperatorCode.i32x4_load16x4_s */:
+ case 1036292 /* OperatorCode.i32x4_load16x4_u */:
+ case 1036293 /* OperatorCode.i64x2_load32x2_s */:
+ case 1036294 /* OperatorCode.i64x2_load32x2_u */:
+ case 1036295 /* OperatorCode.v8x16_load_splat */:
+ case 1036296 /* OperatorCode.v16x8_load_splat */:
+ case 1036297 /* OperatorCode.v32x4_load_splat */:
+ case 1036298 /* OperatorCode.v64x2_load_splat */:
+ case 1036299 /* OperatorCode.v128_store */:
+ case 1036380 /* OperatorCode.v128_load32_zero */:
+ case 1036381 /* OperatorCode.v128_load64_zero */:
+ var memoryAddress = memoryAddressToString(operator.memoryAddress, operator.code);
+ if (memoryAddress !== null) {
+ this.appendBuffer(" ");
+ this.appendBuffer(memoryAddress);
+ }
+ break;
+ case 63 /* OperatorCode.memory_size */:
+ case 64 /* OperatorCode.memory_grow */:
+ break;
+ case 65 /* OperatorCode.i32_const */:
+ this.appendBuffer(" ".concat(operator.literal.toString()));
+ break;
+ case 66 /* OperatorCode.i64_const */:
+ this.appendBuffer(" ".concat(operator.literal.toString()));
+ break;
+ case 67 /* OperatorCode.f32_const */:
+ this.appendBuffer(" ".concat(formatFloat32(operator.literal)));
+ break;
+ case 68 /* OperatorCode.f64_const */:
+ this.appendBuffer(" ".concat(formatFloat64(operator.literal)));
+ break;
+ case 1036300 /* OperatorCode.v128_const */:
+ this.appendBuffer(" i32x4 ".concat(formatI32Array(operator.literal, 4)));
+ break;
+ case 1036301 /* OperatorCode.i8x16_shuffle */:
+ this.appendBuffer(" ".concat(formatI8Array(operator.lines, 16)));
+ break;
+ case 1036309 /* OperatorCode.i8x16_extract_lane_s */:
+ case 1036310 /* OperatorCode.i8x16_extract_lane_u */:
+ case 1036311 /* OperatorCode.i8x16_replace_lane */:
+ case 1036312 /* OperatorCode.i16x8_extract_lane_s */:
+ case 1036313 /* OperatorCode.i16x8_extract_lane_u */:
+ case 1036314 /* OperatorCode.i16x8_replace_lane */:
+ case 1036315 /* OperatorCode.i32x4_extract_lane */:
+ case 1036316 /* OperatorCode.i32x4_replace_lane */:
+ case 1036319 /* OperatorCode.f32x4_extract_lane */:
+ case 1036320 /* OperatorCode.f32x4_replace_lane */:
+ case 1036317 /* OperatorCode.i64x2_extract_lane */:
+ case 1036318 /* OperatorCode.i64x2_replace_lane */:
+ case 1036321 /* OperatorCode.f64x2_extract_lane */:
+ case 1036322 /* OperatorCode.f64x2_replace_lane */:
+ this.appendBuffer(" ".concat(operator.lineIndex));
+ break;
+ case 1036372 /* OperatorCode.v128_load8_lane */:
+ case 1036373 /* OperatorCode.v128_load16_lane */:
+ case 1036374 /* OperatorCode.v128_load32_lane */:
+ case 1036375 /* OperatorCode.v128_load64_lane */:
+ case 1036376 /* OperatorCode.v128_store8_lane */:
+ case 1036377 /* OperatorCode.v128_store16_lane */:
+ case 1036378 /* OperatorCode.v128_store32_lane */:
+ case 1036379 /* OperatorCode.v128_store64_lane */:
+ var memoryAddress = memoryAddressToString(operator.memoryAddress, operator.code);
+ if (memoryAddress !== null) {
+ this.appendBuffer(" ");
+ this.appendBuffer(memoryAddress);
+ }
+ this.appendBuffer(" ".concat(operator.lineIndex));
+ break;
+ case 64520 /* OperatorCode.memory_init */:
+ case 64521 /* OperatorCode.data_drop */:
+ this.appendBuffer(" ".concat(operator.segmentIndex));
+ break;
+ case 64525 /* OperatorCode.elem_drop */:
+ var elementName = this._nameResolver.getElementName(operator.segmentIndex, true);
+ this.appendBuffer(" ".concat(elementName));
+ break;
+ case 38 /* OperatorCode.table_set */:
+ case 37 /* OperatorCode.table_get */:
+ case 64529 /* OperatorCode.table_fill */: {
+ var tableName = this._nameResolver.getTableName(operator.tableIndex, true);
+ this.appendBuffer(" ".concat(tableName));
+ break;
+ }
+ case 64526 /* OperatorCode.table_copy */: {
+ // Table index might be omitted and defaults to 0.
+ if (operator.tableIndex !== 0 || operator.destinationIndex !== 0) {
+ var tableName = this._nameResolver.getTableName(operator.tableIndex, true);
+ var destinationName = this._nameResolver.getTableName(operator.destinationIndex, true);
+ this.appendBuffer(" ".concat(destinationName, " ").concat(tableName));
+ }
+ break;
+ }
+ case 64524 /* OperatorCode.table_init */: {
+ // Table index might be omitted and defaults to 0.
+ if (operator.tableIndex !== 0) {
+ var tableName = this._nameResolver.getTableName(operator.tableIndex, true);
+ this.appendBuffer(" ".concat(tableName));
+ }
+ var elementName_1 = this._nameResolver.getElementName(operator.segmentIndex, true);
+ this.appendBuffer(" ".concat(elementName_1));
+ break;
+ }
+ case 64258 /* OperatorCode.struct_get */:
+ case 64259 /* OperatorCode.struct_get_s */:
+ case 64260 /* OperatorCode.struct_get_u */:
+ case 64261 /* OperatorCode.struct_set */: {
+ var refType = this.typeIndexToString(operator.refType);
+ var fieldName = this._nameResolver.getFieldName(operator.refType, operator.fieldIndex, true);
+ this.appendBuffer(" ".concat(refType, " ").concat(fieldName));
+ break;
+ }
+ case 64278 /* OperatorCode.ref_cast */:
+ case 64279 /* OperatorCode.ref_cast_null */:
+ case 64276 /* OperatorCode.ref_test */:
+ case 64277 /* OperatorCode.ref_test_null */:
+ case 64257 /* OperatorCode.struct_new_default */:
+ case 64256 /* OperatorCode.struct_new */:
+ case 64263 /* OperatorCode.array_new_default */:
+ case 64262 /* OperatorCode.array_new */:
+ case 64267 /* OperatorCode.array_get */:
+ case 64268 /* OperatorCode.array_get_s */:
+ case 64269 /* OperatorCode.array_get_u */:
+ case 64270 /* OperatorCode.array_set */: {
+ var refType = this.typeIndexToString(operator.refType);
+ this.appendBuffer(" ".concat(refType));
+ break;
+ }
+ case 64272 /* OperatorCode.array_fill */: {
+ var dstType = this.typeIndexToString(operator.refType);
+ this.appendBuffer(" ".concat(dstType));
+ break;
+ }
+ case 64273 /* OperatorCode.array_copy */: {
+ var dstType = this.typeIndexToString(operator.refType);
+ var srcType = this.typeIndexToString(operator.srcType);
+ this.appendBuffer(" ".concat(dstType, " ").concat(srcType));
+ break;
+ }
+ case 64264 /* OperatorCode.array_new_fixed */: {
+ var refType = this.typeIndexToString(operator.refType);
+ var length_1 = operator.len;
+ this.appendBuffer(" ".concat(refType, " ").concat(length_1));
+ break;
+ }
+ }
+ };
+ WasmDisassembler.prototype.printImportSource = function (info) {
+ this.printString(info.module);
+ this.appendBuffer(" ");
+ this.printString(info.field);
+ };
+ WasmDisassembler.prototype.increaseIndent = function () {
+ this._indent += IndentIncrement;
+ this._indentLevel++;
+ };
+ WasmDisassembler.prototype.decreaseIndent = function () {
+ this._indent = this._indent.slice(0, -IndentIncrement.length);
+ this._indentLevel--;
+ };
+ WasmDisassembler.prototype.disassemble = function (reader) {
+ var _this = this;
+ var done = this.disassembleChunk(reader);
+ if (!done)
+ return null;
+ var lines = this._lines;
+ if (this._addOffsets) {
+ lines = lines.map(function (line, index) {
+ var position = formatHex(_this._offsets[index], 4);
+ return line + " ;; @" + position;
+ });
+ }
+ lines.push(""); // we need '\n' after last line
+ var result = lines.join("\n");
+ this._lines.length = 0;
+ this._offsets.length = 0;
+ this._functionBodyOffsets.length = 0;
+ return result;
+ };
+ WasmDisassembler.prototype.getResult = function () {
+ var linesReady = this._lines.length;
+ if (this._backrefLabels && this._labelMode === LabelMode.WhenUsed) {
+ this._backrefLabels.some(function (backrefLabel) {
+ if (backrefLabel.useLabel)
+ return false;
+ linesReady = backrefLabel.line;
+ return true;
+ });
+ }
+ if (linesReady === 0) {
+ return {
+ lines: [],
+ offsets: this._addOffsets ? [] : undefined,
+ done: this._done,
+ functionBodyOffsets: this._addOffsets ? [] : undefined,
+ };
+ }
+ if (linesReady === this._lines.length) {
+ var result_1 = {
+ lines: this._lines,
+ offsets: this._addOffsets ? this._offsets : undefined,
+ done: this._done,
+ functionBodyOffsets: this._addOffsets
+ ? this._functionBodyOffsets
+ : undefined,
+ };
+ this._lines = [];
+ if (this._addOffsets) {
+ this._offsets = [];
+ this._functionBodyOffsets = [];
+ }
+ return result_1;
+ }
+ var result = {
+ lines: this._lines.splice(0, linesReady),
+ offsets: this._addOffsets
+ ? this._offsets.splice(0, linesReady)
+ : undefined,
+ done: false,
+ functionBodyOffsets: this._addOffsets
+ ? this._functionBodyOffsets
+ : undefined,
+ };
+ if (this._backrefLabels) {
+ this._backrefLabels.forEach(function (backrefLabel) {
+ backrefLabel.line -= linesReady;
+ });
+ }
+ return result;
+ };
+ WasmDisassembler.prototype.disassembleChunk = function (reader, offsetInModule) {
+ var _this = this;
+ if (offsetInModule === void 0) { offsetInModule = 0; }
+ if (this._done)
+ throw new Error("Invalid state: disassembly process was already finished.");
+ while (true) {
+ this._currentPosition = reader.position + offsetInModule;
+ if (!reader.read())
+ return false;
+ switch (reader.state) {
+ case 2 /* BinaryReaderState.END_WASM */:
+ this.appendBuffer(")");
+ this.newLine();
+ this._reset();
+ if (!reader.hasMoreBytes()) {
+ this._done = true;
+ return true;
+ }
+ break;
+ case -1 /* BinaryReaderState.ERROR */:
+ throw reader.error;
+ case 1 /* BinaryReaderState.BEGIN_WASM */:
+ this.appendBuffer("(module");
+ this.newLine();
+ break;
+ case 4 /* BinaryReaderState.END_SECTION */:
+ this._currentSectionId = -1 /* SectionCode.Unknown */;
+ break;
+ case 3 /* BinaryReaderState.BEGIN_SECTION */:
+ var sectionInfo = reader.result;
+ switch (sectionInfo.id) {
+ case 1 /* SectionCode.Type */:
+ case 2 /* SectionCode.Import */:
+ case 7 /* SectionCode.Export */:
+ case 6 /* SectionCode.Global */:
+ case 3 /* SectionCode.Function */:
+ case 8 /* SectionCode.Start */:
+ case 10 /* SectionCode.Code */:
+ case 5 /* SectionCode.Memory */:
+ case 11 /* SectionCode.Data */:
+ case 4 /* SectionCode.Table */:
+ case 9 /* SectionCode.Element */:
+ case 13 /* SectionCode.Tag */:
+ this._currentSectionId = sectionInfo.id;
+ this._indent = " ";
+ this._indentLevel = 0;
+ break; // reading known section;
+ default:
+ reader.skipSection();
+ break;
+ }
+ break;
+ case 15 /* BinaryReaderState.MEMORY_SECTION_ENTRY */:
+ var memoryInfo = reader.result;
+ var memoryIndex = this._memoryCount++;
+ var memoryName = this._nameResolver.getMemoryName(memoryIndex, false);
+ this.appendBuffer(" (memory ".concat(memoryName));
+ if (this._exportMetadata !== null) {
+ for (var _i = 0, _a = this._exportMetadata.getMemoryExportNames(memoryIndex); _i < _a.length; _i++) {
+ var exportName = _a[_i];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" ".concat(limitsToString(memoryInfo.limits)));
+ if (memoryInfo.shared) {
+ this.appendBuffer(" shared");
+ }
+ this.appendBuffer(")");
+ this.newLine();
+ break;
+ case 23 /* BinaryReaderState.TAG_SECTION_ENTRY */:
+ var tagInfo = reader.result;
+ var tagIndex = this._eventCount++;
+ var tagName = this._nameResolver.getTagName(tagIndex, false);
+ this.appendBuffer(" (tag ".concat(tagName));
+ if (this._exportMetadata !== null) {
+ for (var _b = 0, _c = this._exportMetadata.getTagExportNames(tagIndex); _b < _c.length; _b++) {
+ var exportName = _c[_b];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.printFuncType(tagInfo.typeIndex);
+ this.appendBuffer(")");
+ this.newLine();
+ break;
+ case 14 /* BinaryReaderState.TABLE_SECTION_ENTRY */:
+ var tableInfo = reader.result;
+ var tableIndex = this._tableCount++;
+ var tableName = this._nameResolver.getTableName(tableIndex, false);
+ this.appendBuffer(" (table ".concat(tableName));
+ if (this._exportMetadata !== null) {
+ for (var _d = 0, _e = this._exportMetadata.getTableExportNames(tableIndex); _d < _e.length; _d++) {
+ var exportName = _e[_d];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" ".concat(limitsToString(tableInfo.limits), " ").concat(this.typeToString(tableInfo.elementType), ")"));
+ this.newLine();
+ break;
+ case 17 /* BinaryReaderState.EXPORT_SECTION_ENTRY */:
+ // Skip printing exports here when we have export metadata
+ // which we can use to print export information inline.
+ if (this._exportMetadata === null) {
+ var exportInfo = reader.result;
+ this.appendBuffer(" (export ");
+ this.printString(exportInfo.field);
+ this.appendBuffer(" ");
+ switch (exportInfo.kind) {
+ case 0 /* ExternalKind.Function */:
+ var funcName = this._nameResolver.getFunctionName(exportInfo.index, exportInfo.index < this._importCount, true);
+ this.appendBuffer("(func ".concat(funcName, ")"));
+ break;
+ case 1 /* ExternalKind.Table */:
+ var tableName = this._nameResolver.getTableName(exportInfo.index, true);
+ this.appendBuffer("(table ".concat(tableName, ")"));
+ break;
+ case 2 /* ExternalKind.Memory */:
+ var memoryName = this._nameResolver.getMemoryName(exportInfo.index, true);
+ this.appendBuffer("(memory ".concat(memoryName, ")"));
+ break;
+ case 3 /* ExternalKind.Global */:
+ var globalName = this._nameResolver.getGlobalName(exportInfo.index, true);
+ this.appendBuffer("(global ".concat(globalName, ")"));
+ break;
+ case 4 /* ExternalKind.Tag */:
+ var tagName = this._nameResolver.getTagName(exportInfo.index, true);
+ this.appendBuffer("(tag ".concat(tagName, ")"));
+ break;
+ default:
+ throw new Error("Unsupported export ".concat(exportInfo.kind));
+ }
+ this.appendBuffer(")");
+ this.newLine();
+ }
+ break;
+ case 12 /* BinaryReaderState.IMPORT_SECTION_ENTRY */:
+ var importInfo = reader.result;
+ switch (importInfo.kind) {
+ case 0 /* ExternalKind.Function */:
+ this._importCount++;
+ var funcIndex = this._funcIndex++;
+ var funcName = this._nameResolver.getFunctionName(funcIndex, true, false);
+ this.appendBuffer(" (func ".concat(funcName));
+ if (this._exportMetadata !== null) {
+ for (var _f = 0, _g = this._exportMetadata.getFunctionExportNames(funcIndex); _f < _g.length; _f++) {
+ var exportName = _g[_f];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" (import ");
+ this.printImportSource(importInfo);
+ this.appendBuffer(")");
+ this.printFuncType(importInfo.funcTypeIndex);
+ this.appendBuffer(")");
+ break;
+ case 3 /* ExternalKind.Global */:
+ var globalImportInfo = importInfo.type;
+ var globalIndex = this._globalCount++;
+ var globalName = this._nameResolver.getGlobalName(globalIndex, false);
+ this.appendBuffer(" (global ".concat(globalName));
+ if (this._exportMetadata !== null) {
+ for (var _h = 0, _j = this._exportMetadata.getGlobalExportNames(globalIndex); _h < _j.length; _h++) {
+ var exportName = _j[_h];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" (import ");
+ this.printImportSource(importInfo);
+ this.appendBuffer(") ".concat(this.globalTypeToString(globalImportInfo), ")"));
+ break;
+ case 2 /* ExternalKind.Memory */:
+ var memoryImportInfo = importInfo.type;
+ var memoryIndex = this._memoryCount++;
+ var memoryName = this._nameResolver.getMemoryName(memoryIndex, false);
+ this.appendBuffer(" (memory ".concat(memoryName));
+ if (this._exportMetadata !== null) {
+ for (var _k = 0, _l = this._exportMetadata.getMemoryExportNames(memoryIndex); _k < _l.length; _k++) {
+ var exportName = _l[_k];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" (import ");
+ this.printImportSource(importInfo);
+ this.appendBuffer(") ".concat(limitsToString(memoryImportInfo.limits)));
+ if (memoryImportInfo.shared) {
+ this.appendBuffer(" shared");
+ }
+ this.appendBuffer(")");
+ break;
+ case 1 /* ExternalKind.Table */:
+ var tableImportInfo = importInfo.type;
+ var tableIndex = this._tableCount++;
+ var tableName = this._nameResolver.getTableName(tableIndex, false);
+ this.appendBuffer(" (table ".concat(tableName));
+ if (this._exportMetadata !== null) {
+ for (var _m = 0, _o = this._exportMetadata.getTableExportNames(tableIndex); _m < _o.length; _m++) {
+ var exportName = _o[_m];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" (import ");
+ this.printImportSource(importInfo);
+ this.appendBuffer(") ".concat(limitsToString(tableImportInfo.limits), " ").concat(this.typeToString(tableImportInfo.elementType), ")"));
+ break;
+ case 4 /* ExternalKind.Tag */:
+ var eventImportInfo = importInfo.type;
+ var tagIndex = this._eventCount++;
+ var tagName = this._nameResolver.getTagName(tagIndex, false);
+ this.appendBuffer(" (tag ".concat(tagName));
+ if (this._exportMetadata !== null) {
+ for (var _p = 0, _q = this._exportMetadata.getTagExportNames(tagIndex); _p < _q.length; _p++) {
+ var exportName = _q[_p];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" (import ");
+ this.printImportSource(importInfo);
+ this.appendBuffer(")");
+ this.printFuncType(eventImportInfo.typeIndex);
+ this.appendBuffer(")");
+ break;
+ default:
+ throw new Error("NYI other import types: ".concat(importInfo.kind));
+ }
+ this.newLine();
+ break;
+ case 33 /* BinaryReaderState.BEGIN_ELEMENT_SECTION_ENTRY */:
+ var elementSegment = reader.result;
+ var elementIndex = this._elementCount++;
+ var elementName = this._nameResolver.getElementName(elementIndex, false);
+ this.appendBuffer(" (elem ".concat(elementName));
+ switch (elementSegment.mode) {
+ case 0 /* ElementMode.Active */:
+ if (elementSegment.tableIndex !== 0) {
+ var tableName_1 = this._nameResolver.getTableName(elementSegment.tableIndex, false);
+ this.appendBuffer(" (table ".concat(tableName_1, ")"));
+ }
+ break;
+ case 1 /* ElementMode.Passive */:
+ break;
+ case 2 /* ElementMode.Declarative */:
+ this.appendBuffer(" declare");
+ break;
+ }
+ break;
+ case 35 /* BinaryReaderState.END_ELEMENT_SECTION_ENTRY */:
+ this.appendBuffer(")");
+ this.newLine();
+ break;
+ case 34 /* BinaryReaderState.ELEMENT_SECTION_ENTRY_BODY */:
+ var elementSegmentBody = reader.result;
+ this.appendBuffer(" ".concat(this.typeToString(elementSegmentBody.elementType)));
+ break;
+ case 39 /* BinaryReaderState.BEGIN_GLOBAL_SECTION_ENTRY */:
+ var globalInfo = reader.result;
+ var globalIndex = this._globalCount++;
+ var globalName = this._nameResolver.getGlobalName(globalIndex, false);
+ this.appendBuffer(" (global ".concat(globalName));
+ if (this._exportMetadata !== null) {
+ for (var _r = 0, _s = this._exportMetadata.getGlobalExportNames(globalIndex); _r < _s.length; _r++) {
+ var exportName = _s[_r];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ this.appendBuffer(" ".concat(this.globalTypeToString(globalInfo.type)));
+ break;
+ case 40 /* BinaryReaderState.END_GLOBAL_SECTION_ENTRY */:
+ this.appendBuffer(")");
+ this.newLine();
+ break;
+ case 11 /* BinaryReaderState.TYPE_SECTION_ENTRY */:
+ var typeEntry = reader.result;
+ var typeIndex = this._types.length;
+ this._types.push(typeEntry);
+ if (!this._skipTypes) {
+ var typeName = this._nameResolver.getTypeName(typeIndex, false);
+ var superTypeName = undefined;
+ if (typeEntry.supertypes !== undefined) {
+ superTypeName = typeEntry.supertypes
+ .map(function (ty) { return _this.typeIndexToString(ty); })
+ .join("+");
+ }
+ this.appendBuffer(this._indent);
+ this.appendBuffer("(type ".concat(typeName, " "));
+ var subtype = typeEntry.supertypes || typeEntry.final;
+ if (subtype) {
+ this.appendBuffer("(sub ");
+ if (typeEntry.final)
+ this.appendBuffer("final ");
+ if (typeEntry.supertypes) {
+ this.appendBuffer(typeEntry.supertypes
+ .map(function (ty) { return _this.typeIndexToString(ty); })
+ .join(" "));
+ this.appendBuffer(" ");
+ }
+ }
+ if (typeEntry.form === -32 /* TypeKind.func */) {
+ this.appendBuffer("(func");
+ this.printFuncType(typeIndex);
+ this.appendBuffer(")");
+ }
+ else if (typeEntry.form === -33 /* TypeKind.struct */) {
+ this.appendBuffer("(struct");
+ this.printStructType(typeIndex);
+ this.appendBuffer(")");
+ }
+ else if (typeEntry.form === -34 /* TypeKind.array */) {
+ this.appendBuffer("(array");
+ this.printArrayType(typeIndex);
+ this.appendBuffer(")");
+ }
+ else {
+ throw new Error("Unknown type form: ".concat(typeEntry.form));
+ }
+ if (subtype) {
+ this.appendBuffer(")");
+ }
+ this.appendBuffer(")");
+ this.newLine();
+ }
+ break;
+ case 22 /* BinaryReaderState.START_SECTION_ENTRY */:
+ var startEntry = reader.result;
+ var funcName = this._nameResolver.getFunctionName(startEntry.index, startEntry.index < this._importCount, true);
+ this.appendBuffer(" (start ".concat(funcName, ")"));
+ this.newLine();
+ break;
+ case 36 /* BinaryReaderState.BEGIN_DATA_SECTION_ENTRY */:
+ this.appendBuffer(" (data");
+ break;
+ case 37 /* BinaryReaderState.DATA_SECTION_ENTRY_BODY */:
+ var body = reader.result;
+ this.appendBuffer(" ");
+ this.printString(body.data);
+ break;
+ case 38 /* BinaryReaderState.END_DATA_SECTION_ENTRY */:
+ this.appendBuffer(")");
+ this.newLine();
+ break;
+ case 25 /* BinaryReaderState.BEGIN_INIT_EXPRESSION_BODY */:
+ case 44 /* BinaryReaderState.BEGIN_OFFSET_EXPRESSION_BODY */:
+ this._expression = [];
+ break;
+ case 26 /* BinaryReaderState.INIT_EXPRESSION_OPERATOR */:
+ case 45 /* BinaryReaderState.OFFSET_EXPRESSION_OPERATOR */:
+ var operator = reader.result;
+ if (operator.code !== 11 /* OperatorCode.end */) {
+ this._expression.push(operator);
+ }
+ break;
+ case 46 /* BinaryReaderState.END_OFFSET_EXPRESSION_BODY */:
+ if (this._expression.length > 1) {
+ this.appendBuffer(" (offset ");
+ this.printExpression(this._expression);
+ this.appendBuffer(")");
+ }
+ else {
+ this.appendBuffer(" ");
+ this.printExpression(this._expression);
+ }
+ this._expression = [];
+ break;
+ case 27 /* BinaryReaderState.END_INIT_EXPRESSION_BODY */:
+ if (this._expression.length > 1 &&
+ this._currentSectionId === 9 /* SectionCode.Element */) {
+ this.appendBuffer(" (item ");
+ this.printExpression(this._expression);
+ this.appendBuffer(")");
+ }
+ else {
+ this.appendBuffer(" ");
+ this.printExpression(this._expression);
+ }
+ this._expression = [];
+ break;
+ case 13 /* BinaryReaderState.FUNCTION_SECTION_ENTRY */:
+ this._funcTypes.push(reader.result.typeIndex);
+ break;
+ case 28 /* BinaryReaderState.BEGIN_FUNCTION_BODY */:
+ var func = reader.result;
+ var type = this._types[this._funcTypes[this._funcIndex - this._importCount]];
+ this.appendBuffer(" (func ");
+ this.appendBuffer(this._nameResolver.getFunctionName(this._funcIndex, false, false));
+ if (this._exportMetadata !== null) {
+ for (var _t = 0, _u = this._exportMetadata.getFunctionExportNames(this._funcIndex); _t < _u.length; _t++) {
+ var exportName = _u[_t];
+ this.appendBuffer(" (export ".concat(JSON.stringify(exportName), ")"));
+ }
+ }
+ for (var i = 0; i < type.params.length; i++) {
+ var paramName = this._nameResolver.getVariableName(this._funcIndex, i, false);
+ this.appendBuffer(" (param ".concat(paramName, " ").concat(this.typeToString(type.params[i]), ")"));
+ }
+ for (var i = 0; i < type.returns.length; i++) {
+ this.appendBuffer(" (result ".concat(this.typeToString(type.returns[i]), ")"));
+ }
+ this.newLine();
+ var localIndex = type.params.length;
+ if (func.locals.length > 0) {
+ this.appendBuffer(" ");
+ for (var _v = 0, _w = func.locals; _v < _w.length; _v++) {
+ var l = _w[_v];
+ for (var i = 0; i < l.count; i++) {
+ var paramName = this._nameResolver.getVariableName(this._funcIndex, localIndex++, false);
+ this.appendBuffer(" (local ".concat(paramName, " ").concat(this.typeToString(l.type), ")"));
+ }
+ }
+ this.newLine();
+ }
+ this._indent = " ";
+ this._indentLevel = 0;
+ this._labelIndex = 0;
+ this._backrefLabels = this._labelMode === LabelMode.Depth ? null : [];
+ this._logFirstInstruction = true;
+ break;
+ case 30 /* BinaryReaderState.CODE_OPERATOR */:
+ if (this._logFirstInstruction) {
+ this.logStartOfFunctionBodyOffset();
+ this._logFirstInstruction = false;
+ }
+ var operator = reader.result;
+ if (operator.code == 11 /* OperatorCode.end */ && this._indentLevel == 0) {
+ // reached of the function, closing function body
+ this.appendBuffer(" )");
+ this.newLine();
+ break;
+ }
+ switch (operator.code) {
+ case 11 /* OperatorCode.end */:
+ case 5 /* OperatorCode.else */:
+ case 7 /* OperatorCode.catch */:
+ case 25 /* OperatorCode.catch_all */:
+ case 10 /* OperatorCode.unwind */:
+ case 24 /* OperatorCode.delegate */:
+ this.decreaseIndent();
+ break;
+ }
+ this.appendBuffer(this._indent);
+ this.printOperator(operator);
+ this.newLine();
+ switch (operator.code) {
+ case 4 /* OperatorCode.if */:
+ case 2 /* OperatorCode.block */:
+ case 3 /* OperatorCode.loop */:
+ case 5 /* OperatorCode.else */:
+ case 6 /* OperatorCode.try */:
+ case 7 /* OperatorCode.catch */:
+ case 25 /* OperatorCode.catch_all */:
+ case 10 /* OperatorCode.unwind */:
+ this.increaseIndent();
+ break;
+ }
+ break;
+ case 31 /* BinaryReaderState.END_FUNCTION_BODY */:
+ this._funcIndex++;
+ this._backrefLabels = null;
+ this.logEndOfFunctionBodyOffset();
+ // See case BinaryReaderState.CODE_OPERATOR for closing of body
+ break;
+ case 47 /* BinaryReaderState.BEGIN_REC_GROUP */:
+ if (!this._skipTypes) {
+ this.appendBuffer(" (rec");
+ this.newLine();
+ this.increaseIndent();
+ }
+ break;
+ case 48 /* BinaryReaderState.END_REC_GROUP */:
+ if (!this._skipTypes) {
+ this.decreaseIndent();
+ this.appendBuffer(" )");
+ this.newLine();
+ }
+ break;
+ default:
+ throw new Error("Expectected state: ".concat(reader.state));
+ }
+ }
+ };
+ return WasmDisassembler;
+}());
+exports.WasmDisassembler = WasmDisassembler;
+var UNKNOWN_FUNCTION_PREFIX = "unknown";
+var NameSectionNameResolver = /** @class */ (function (_super) {
+ __extends(NameSectionNameResolver, _super);
+ function NameSectionNameResolver(functionNames, localNames, tagNames, typeNames, tableNames, memoryNames, globalNames, fieldNames) {
+ var _this = _super.call(this) || this;
+ _this._functionNames = functionNames;
+ _this._localNames = localNames;
+ _this._tagNames = tagNames;
+ _this._typeNames = typeNames;
+ _this._tableNames = tableNames;
+ _this._memoryNames = memoryNames;
+ _this._globalNames = globalNames;
+ _this._fieldNames = fieldNames;
+ return _this;
+ }
+ NameSectionNameResolver.prototype.getTypeName = function (index, isRef) {
+ var name = this._typeNames[index];
+ if (!name)
+ return _super.prototype.getTypeName.call(this, index, isRef);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ NameSectionNameResolver.prototype.getTableName = function (index, isRef) {
+ var name = this._tableNames[index];
+ if (!name)
+ return _super.prototype.getTableName.call(this, index, isRef);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ NameSectionNameResolver.prototype.getMemoryName = function (index, isRef) {
+ var name = this._memoryNames[index];
+ if (!name)
+ return _super.prototype.getMemoryName.call(this, index, isRef);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ NameSectionNameResolver.prototype.getGlobalName = function (index, isRef) {
+ var name = this._globalNames[index];
+ if (!name)
+ return _super.prototype.getGlobalName.call(this, index, isRef);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ NameSectionNameResolver.prototype.getTagName = function (index, isRef) {
+ var name = this._tagNames[index];
+ if (!name)
+ return _super.prototype.getTagName.call(this, index, isRef);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ NameSectionNameResolver.prototype.getFunctionName = function (index, isImport, isRef) {
+ var name = this._functionNames[index];
+ if (!name)
+ return "$".concat(UNKNOWN_FUNCTION_PREFIX).concat(index);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ NameSectionNameResolver.prototype.getVariableName = function (funcIndex, index, isRef) {
+ var name = this._localNames[funcIndex] && this._localNames[funcIndex][index];
+ if (!name)
+ return _super.prototype.getVariableName.call(this, funcIndex, index, isRef);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ NameSectionNameResolver.prototype.getFieldName = function (typeIndex, index, isRef) {
+ var name = this._fieldNames[typeIndex] && this._fieldNames[typeIndex][index];
+ if (!name)
+ return _super.prototype.getFieldName.call(this, typeIndex, index, isRef);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ return NameSectionNameResolver;
+}(DefaultNameResolver));
+var NameSectionReader = /** @class */ (function () {
+ function NameSectionReader() {
+ this._done = false;
+ this._functionsCount = 0;
+ this._functionImportsCount = 0;
+ this._functionNames = null;
+ this._functionLocalNames = null;
+ this._tagNames = null;
+ this._typeNames = null;
+ this._tableNames = null;
+ this._memoryNames = null;
+ this._globalNames = null;
+ this._fieldNames = null;
+ this._hasNames = false;
+ }
+ NameSectionReader.prototype.read = function (reader) {
+ var _this = this;
+ if (this._done)
+ throw new Error("Invalid state: disassembly process was already finished.");
+ while (true) {
+ if (!reader.read())
+ return false;
+ switch (reader.state) {
+ case 2 /* BinaryReaderState.END_WASM */:
+ if (!reader.hasMoreBytes()) {
+ this._done = true;
+ return true;
+ }
+ break;
+ case -1 /* BinaryReaderState.ERROR */:
+ throw reader.error;
+ case 1 /* BinaryReaderState.BEGIN_WASM */:
+ this._functionsCount = 0;
+ this._functionImportsCount = 0;
+ this._functionNames = [];
+ this._functionLocalNames = [];
+ this._tagNames = [];
+ this._typeNames = [];
+ this._tableNames = [];
+ this._memoryNames = [];
+ this._globalNames = [];
+ this._fieldNames = [];
+ this._hasNames = false;
+ break;
+ case 4 /* BinaryReaderState.END_SECTION */:
+ break;
+ case 3 /* BinaryReaderState.BEGIN_SECTION */:
+ var sectionInfo = reader.result;
+ if (sectionInfo.id === 0 /* SectionCode.Custom */ &&
+ (0, WasmParser_js_1.bytesToString)(sectionInfo.name) === NAME_SECTION_NAME) {
+ break;
+ }
+ if (sectionInfo.id === 3 /* SectionCode.Function */ ||
+ sectionInfo.id === 2 /* SectionCode.Import */) {
+ break;
+ }
+ reader.skipSection();
+ break;
+ case 12 /* BinaryReaderState.IMPORT_SECTION_ENTRY */:
+ var importInfo = reader.result;
+ if (importInfo.kind === 0 /* ExternalKind.Function */)
+ this._functionImportsCount++;
+ break;
+ case 13 /* BinaryReaderState.FUNCTION_SECTION_ENTRY */:
+ this._functionsCount++;
+ break;
+ case 19 /* BinaryReaderState.NAME_SECTION_ENTRY */:
+ var nameInfo = reader.result;
+ if (nameInfo.type === 1 /* NameType.Function */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._functionNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ this._hasNames = true;
+ }
+ else if (nameInfo.type === 2 /* NameType.Local */) {
+ var funcs = nameInfo.funcs;
+ funcs.forEach(function (_a) {
+ var index = _a.index, locals = _a.locals;
+ var localNames = (_this._functionLocalNames[index] = []);
+ locals.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ localNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ });
+ this._hasNames = true;
+ }
+ else if (nameInfo.type === 11 /* NameType.Tag */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._tagNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ this._hasNames = true;
+ }
+ else if (nameInfo.type === 4 /* NameType.Type */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._typeNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ this._hasNames = true;
+ }
+ else if (nameInfo.type === 5 /* NameType.Table */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._tableNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ this._hasNames = true;
+ }
+ else if (nameInfo.type === 6 /* NameType.Memory */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._memoryNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ this._hasNames = true;
+ }
+ else if (nameInfo.type === 7 /* NameType.Global */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._globalNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ this._hasNames = true;
+ }
+ else if (nameInfo.type === 10 /* NameType.Field */) {
+ var types = nameInfo.types;
+ types.forEach(function (_a) {
+ var index = _a.index, fields = _a.fields;
+ var fieldNames = (_this._fieldNames[index] = []);
+ fields.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ fieldNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ });
+ }
+ break;
+ default:
+ throw new Error("Expectected state: ".concat(reader.state));
+ }
+ }
+ };
+ NameSectionReader.prototype.hasValidNames = function () {
+ return this._hasNames;
+ };
+ NameSectionReader.prototype.getNameResolver = function () {
+ if (!this.hasValidNames())
+ throw new Error("Has no valid name section");
+ // Fix bad names.
+ var functionNamesLength = this._functionImportsCount + this._functionsCount;
+ var functionNames = this._functionNames.slice(0, functionNamesLength);
+ var usedNameAt = Object.create(null);
+ for (var i = 0; i < functionNames.length; i++) {
+ var name_1 = functionNames[i];
+ if (!name_1)
+ continue;
+ var goodName = !(name_1 in usedNameAt) &&
+ isValidName(name_1) &&
+ name_1.indexOf(UNKNOWN_FUNCTION_PREFIX) !== 0;
+ if (!goodName) {
+ if (usedNameAt[name_1] >= 0) {
+ // Remove all non-unique names.
+ functionNames[usedNameAt[name_1]] = null;
+ usedNameAt[name_1] = -1;
+ }
+ functionNames[i] = null;
+ continue;
+ }
+ usedNameAt[name_1] = i;
+ }
+ return new NameSectionNameResolver(functionNames, this._functionLocalNames, this._tagNames, this._typeNames, this._tableNames, this._memoryNames, this._globalNames, this._fieldNames);
+ };
+ return NameSectionReader;
+}());
+exports.NameSectionReader = NameSectionReader;
+var DevToolsNameResolver = /** @class */ (function (_super) {
+ __extends(DevToolsNameResolver, _super);
+ function DevToolsNameResolver(functionNames, localNames, tagNames, typeNames, tableNames, memoryNames, globalNames, fieldNames) {
+ return _super.call(this, functionNames, localNames, tagNames, typeNames, tableNames, memoryNames, globalNames, fieldNames) || this;
+ }
+ DevToolsNameResolver.prototype.getFunctionName = function (index, isImport, isRef) {
+ var name = this._functionNames[index];
+ if (!name)
+ return isImport ? "$import".concat(index) : "$func".concat(index);
+ return isRef ? "$".concat(name) : "$".concat(name, " (;").concat(index, ";)");
+ };
+ return DevToolsNameResolver;
+}(NameSectionNameResolver));
+exports.DevToolsNameResolver = DevToolsNameResolver;
+var DevToolsNameGenerator = /** @class */ (function () {
+ function DevToolsNameGenerator() {
+ this._done = false;
+ this._functionImportsCount = 0;
+ this._memoryImportsCount = 0;
+ this._tableImportsCount = 0;
+ this._globalImportsCount = 0;
+ this._tagImportsCount = 0;
+ this._functionNames = null;
+ this._functionLocalNames = null;
+ this._tagNames = null;
+ this._memoryNames = null;
+ this._typeNames = null;
+ this._tableNames = null;
+ this._globalNames = null;
+ this._fieldNames = null;
+ this._functionExportNames = null;
+ this._globalExportNames = null;
+ this._memoryExportNames = null;
+ this._tableExportNames = null;
+ this._tagExportNames = null;
+ }
+ DevToolsNameGenerator.prototype._addExportName = function (exportNames, index, name) {
+ var names = exportNames[index];
+ if (names) {
+ names.push(name);
+ }
+ else {
+ exportNames[index] = [name];
+ }
+ };
+ DevToolsNameGenerator.prototype._setName = function (names, index, name, isNameSectionName) {
+ if (!name)
+ return;
+ if (isNameSectionName) {
+ if (!isValidName(name))
+ return;
+ names[index] = name;
+ }
+ else if (!names[index]) {
+ names[index] = name.replace(INVALID_NAME_SYMBOLS_REGEX_GLOBAL, "_");
+ }
+ };
+ DevToolsNameGenerator.prototype.read = function (reader) {
+ var _this = this;
+ if (this._done)
+ throw new Error("Invalid state: disassembly process was already finished.");
+ while (true) {
+ if (!reader.read())
+ return false;
+ switch (reader.state) {
+ case 2 /* BinaryReaderState.END_WASM */:
+ if (!reader.hasMoreBytes()) {
+ this._done = true;
+ return true;
+ }
+ break;
+ case -1 /* BinaryReaderState.ERROR */:
+ throw reader.error;
+ case 1 /* BinaryReaderState.BEGIN_WASM */:
+ this._functionImportsCount = 0;
+ this._memoryImportsCount = 0;
+ this._tableImportsCount = 0;
+ this._globalImportsCount = 0;
+ this._tagImportsCount = 0;
+ this._functionNames = [];
+ this._functionLocalNames = [];
+ this._tagNames = [];
+ this._memoryNames = [];
+ this._typeNames = [];
+ this._tableNames = [];
+ this._globalNames = [];
+ this._fieldNames = [];
+ this._functionExportNames = [];
+ this._globalExportNames = [];
+ this._memoryExportNames = [];
+ this._tableExportNames = [];
+ this._tagExportNames = [];
+ break;
+ case 4 /* BinaryReaderState.END_SECTION */:
+ break;
+ case 3 /* BinaryReaderState.BEGIN_SECTION */:
+ var sectionInfo = reader.result;
+ if (sectionInfo.id === 0 /* SectionCode.Custom */ &&
+ (0, WasmParser_js_1.bytesToString)(sectionInfo.name) === NAME_SECTION_NAME) {
+ break;
+ }
+ switch (sectionInfo.id) {
+ case 2 /* SectionCode.Import */:
+ case 7 /* SectionCode.Export */:
+ break; // reading known section;
+ default:
+ reader.skipSection();
+ break;
+ }
+ break;
+ case 12 /* BinaryReaderState.IMPORT_SECTION_ENTRY */:
+ var importInfo = reader.result;
+ var importName = "".concat((0, WasmParser_js_1.bytesToString)(importInfo.module), ".").concat((0, WasmParser_js_1.bytesToString)(importInfo.field));
+ switch (importInfo.kind) {
+ case 0 /* ExternalKind.Function */:
+ this._setName(this._functionNames, this._functionImportsCount++, importName, false);
+ break;
+ case 1 /* ExternalKind.Table */:
+ this._setName(this._tableNames, this._tableImportsCount++, importName, false);
+ break;
+ case 2 /* ExternalKind.Memory */:
+ this._setName(this._memoryNames, this._memoryImportsCount++, importName, false);
+ break;
+ case 3 /* ExternalKind.Global */:
+ this._setName(this._globalNames, this._globalImportsCount++, importName, false);
+ break;
+ case 4 /* ExternalKind.Tag */:
+ this._setName(this._tagNames, this._tagImportsCount++, importName, false);
+ default:
+ throw new Error("Unsupported export ".concat(importInfo.kind));
+ }
+ break;
+ case 19 /* BinaryReaderState.NAME_SECTION_ENTRY */:
+ var nameInfo = reader.result;
+ if (nameInfo.type === 1 /* NameType.Function */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._setName(_this._functionNames, index, (0, WasmParser_js_1.bytesToString)(name), true);
+ });
+ }
+ else if (nameInfo.type === 2 /* NameType.Local */) {
+ var funcs = nameInfo.funcs;
+ funcs.forEach(function (_a) {
+ var index = _a.index, locals = _a.locals;
+ var localNames = (_this._functionLocalNames[index] = []);
+ locals.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ localNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ });
+ }
+ else if (nameInfo.type === 11 /* NameType.Tag */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._setName(_this._tagNames, index, (0, WasmParser_js_1.bytesToString)(name), true);
+ });
+ }
+ else if (nameInfo.type === 4 /* NameType.Type */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._setName(_this._typeNames, index, (0, WasmParser_js_1.bytesToString)(name), true);
+ });
+ }
+ else if (nameInfo.type === 5 /* NameType.Table */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._setName(_this._tableNames, index, (0, WasmParser_js_1.bytesToString)(name), true);
+ });
+ }
+ else if (nameInfo.type === 6 /* NameType.Memory */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._setName(_this._memoryNames, index, (0, WasmParser_js_1.bytesToString)(name), true);
+ });
+ }
+ else if (nameInfo.type === 7 /* NameType.Global */) {
+ var names = nameInfo.names;
+ names.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ _this._setName(_this._globalNames, index, (0, WasmParser_js_1.bytesToString)(name), true);
+ });
+ }
+ else if (nameInfo.type === 10 /* NameType.Field */) {
+ var types = nameInfo.types;
+ types.forEach(function (_a) {
+ var index = _a.index, fields = _a.fields;
+ var fieldNames = (_this._fieldNames[index] = []);
+ fields.forEach(function (_a) {
+ var index = _a.index, name = _a.name;
+ fieldNames[index] = (0, WasmParser_js_1.bytesToString)(name);
+ });
+ });
+ }
+ break;
+ case 17 /* BinaryReaderState.EXPORT_SECTION_ENTRY */:
+ var exportInfo = reader.result;
+ var exportName = (0, WasmParser_js_1.bytesToString)(exportInfo.field);
+ switch (exportInfo.kind) {
+ case 0 /* ExternalKind.Function */:
+ this._addExportName(this._functionExportNames, exportInfo.index, exportName);
+ this._setName(this._functionNames, exportInfo.index, exportName, false);
+ break;
+ case 3 /* ExternalKind.Global */:
+ this._addExportName(this._globalExportNames, exportInfo.index, exportName);
+ this._setName(this._globalNames, exportInfo.index, exportName, false);
+ break;
+ case 2 /* ExternalKind.Memory */:
+ this._addExportName(this._memoryExportNames, exportInfo.index, exportName);
+ this._setName(this._memoryNames, exportInfo.index, exportName, false);
+ break;
+ case 1 /* ExternalKind.Table */:
+ this._addExportName(this._tableExportNames, exportInfo.index, exportName);
+ this._setName(this._tableNames, exportInfo.index, exportName, false);
+ break;
+ case 4 /* ExternalKind.Tag */:
+ this._addExportName(this._tagExportNames, exportInfo.index, exportName);
+ this._setName(this._tagNames, exportInfo.index, exportName, false);
+ break;
+ default:
+ throw new Error("Unsupported export ".concat(exportInfo.kind));
+ }
+ break;
+ default:
+ throw new Error("Expectected state: ".concat(reader.state));
+ }
+ }
+ };
+ DevToolsNameGenerator.prototype.getExportMetadata = function () {
+ return new DevToolsExportMetadata(this._functionExportNames, this._globalExportNames, this._memoryExportNames, this._tableExportNames, this._tagExportNames);
+ };
+ DevToolsNameGenerator.prototype.getNameResolver = function () {
+ return new DevToolsNameResolver(this._functionNames, this._functionLocalNames, this._tagNames, this._typeNames, this._tableNames, this._memoryNames, this._globalNames, this._fieldNames);
+ };
+ return DevToolsNameGenerator;
+}());
+exports.DevToolsNameGenerator = DevToolsNameGenerator;
diff --git a/devtools/client/shared/vendor/WasmParser.js b/devtools/client/shared/vendor/WasmParser.js
new file mode 100644
index 0000000000..68397b8eeb
--- /dev/null
+++ b/devtools/client/shared/vendor/WasmParser.js
@@ -0,0 +1,3873 @@
+"use strict";
+/* Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ if (typeof b !== "function" && b !== null)
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.bytesToString = exports.BinaryReader = exports.Int64 = exports.TagAttribute = exports.ElementMode = exports.DataMode = exports.BinaryReaderState = exports.NameType = exports.LinkingType = exports.RelocType = exports.RefType = exports.Type = exports.FuncDef = exports.FieldDef = exports.TypeKind = exports.ExternalKind = exports.OperatorCodeNames = exports.OperatorCode = exports.SectionCode = void 0;
+// See https://github.com/WebAssembly/design/blob/master/BinaryEncoding.md
+var WASM_MAGIC_NUMBER = 0x6d736100;
+var WASM_SUPPORTED_EXPERIMENTAL_VERSION = 0xd;
+var WASM_SUPPORTED_VERSION = 0x1;
+var SectionCode;
+(function (SectionCode) {
+ SectionCode[SectionCode["Unknown"] = -1] = "Unknown";
+ SectionCode[SectionCode["Custom"] = 0] = "Custom";
+ SectionCode[SectionCode["Type"] = 1] = "Type";
+ SectionCode[SectionCode["Import"] = 2] = "Import";
+ SectionCode[SectionCode["Function"] = 3] = "Function";
+ SectionCode[SectionCode["Table"] = 4] = "Table";
+ SectionCode[SectionCode["Memory"] = 5] = "Memory";
+ SectionCode[SectionCode["Global"] = 6] = "Global";
+ SectionCode[SectionCode["Export"] = 7] = "Export";
+ SectionCode[SectionCode["Start"] = 8] = "Start";
+ SectionCode[SectionCode["Element"] = 9] = "Element";
+ SectionCode[SectionCode["Code"] = 10] = "Code";
+ SectionCode[SectionCode["Data"] = 11] = "Data";
+ SectionCode[SectionCode["DataCount"] = 12] = "DataCount";
+ SectionCode[SectionCode["Tag"] = 13] = "Tag";
+})(SectionCode = exports.SectionCode || (exports.SectionCode = {}));
+var OperatorCode;
+(function (OperatorCode) {
+ OperatorCode[OperatorCode["unreachable"] = 0] = "unreachable";
+ OperatorCode[OperatorCode["nop"] = 1] = "nop";
+ OperatorCode[OperatorCode["block"] = 2] = "block";
+ OperatorCode[OperatorCode["loop"] = 3] = "loop";
+ OperatorCode[OperatorCode["if"] = 4] = "if";
+ OperatorCode[OperatorCode["else"] = 5] = "else";
+ OperatorCode[OperatorCode["try"] = 6] = "try";
+ OperatorCode[OperatorCode["catch"] = 7] = "catch";
+ OperatorCode[OperatorCode["throw"] = 8] = "throw";
+ OperatorCode[OperatorCode["rethrow"] = 9] = "rethrow";
+ OperatorCode[OperatorCode["unwind"] = 10] = "unwind";
+ OperatorCode[OperatorCode["end"] = 11] = "end";
+ OperatorCode[OperatorCode["br"] = 12] = "br";
+ OperatorCode[OperatorCode["br_if"] = 13] = "br_if";
+ OperatorCode[OperatorCode["br_table"] = 14] = "br_table";
+ OperatorCode[OperatorCode["return"] = 15] = "return";
+ OperatorCode[OperatorCode["call"] = 16] = "call";
+ OperatorCode[OperatorCode["call_indirect"] = 17] = "call_indirect";
+ OperatorCode[OperatorCode["return_call"] = 18] = "return_call";
+ OperatorCode[OperatorCode["return_call_indirect"] = 19] = "return_call_indirect";
+ OperatorCode[OperatorCode["call_ref"] = 20] = "call_ref";
+ OperatorCode[OperatorCode["return_call_ref"] = 21] = "return_call_ref";
+ OperatorCode[OperatorCode["let"] = 23] = "let";
+ OperatorCode[OperatorCode["delegate"] = 24] = "delegate";
+ OperatorCode[OperatorCode["catch_all"] = 25] = "catch_all";
+ OperatorCode[OperatorCode["drop"] = 26] = "drop";
+ OperatorCode[OperatorCode["select"] = 27] = "select";
+ OperatorCode[OperatorCode["select_with_type"] = 28] = "select_with_type";
+ OperatorCode[OperatorCode["local_get"] = 32] = "local_get";
+ OperatorCode[OperatorCode["local_set"] = 33] = "local_set";
+ OperatorCode[OperatorCode["local_tee"] = 34] = "local_tee";
+ OperatorCode[OperatorCode["global_get"] = 35] = "global_get";
+ OperatorCode[OperatorCode["global_set"] = 36] = "global_set";
+ OperatorCode[OperatorCode["i32_load"] = 40] = "i32_load";
+ OperatorCode[OperatorCode["i64_load"] = 41] = "i64_load";
+ OperatorCode[OperatorCode["f32_load"] = 42] = "f32_load";
+ OperatorCode[OperatorCode["f64_load"] = 43] = "f64_load";
+ OperatorCode[OperatorCode["i32_load8_s"] = 44] = "i32_load8_s";
+ OperatorCode[OperatorCode["i32_load8_u"] = 45] = "i32_load8_u";
+ OperatorCode[OperatorCode["i32_load16_s"] = 46] = "i32_load16_s";
+ OperatorCode[OperatorCode["i32_load16_u"] = 47] = "i32_load16_u";
+ OperatorCode[OperatorCode["i64_load8_s"] = 48] = "i64_load8_s";
+ OperatorCode[OperatorCode["i64_load8_u"] = 49] = "i64_load8_u";
+ OperatorCode[OperatorCode["i64_load16_s"] = 50] = "i64_load16_s";
+ OperatorCode[OperatorCode["i64_load16_u"] = 51] = "i64_load16_u";
+ OperatorCode[OperatorCode["i64_load32_s"] = 52] = "i64_load32_s";
+ OperatorCode[OperatorCode["i64_load32_u"] = 53] = "i64_load32_u";
+ OperatorCode[OperatorCode["i32_store"] = 54] = "i32_store";
+ OperatorCode[OperatorCode["i64_store"] = 55] = "i64_store";
+ OperatorCode[OperatorCode["f32_store"] = 56] = "f32_store";
+ OperatorCode[OperatorCode["f64_store"] = 57] = "f64_store";
+ OperatorCode[OperatorCode["i32_store8"] = 58] = "i32_store8";
+ OperatorCode[OperatorCode["i32_store16"] = 59] = "i32_store16";
+ OperatorCode[OperatorCode["i64_store8"] = 60] = "i64_store8";
+ OperatorCode[OperatorCode["i64_store16"] = 61] = "i64_store16";
+ OperatorCode[OperatorCode["i64_store32"] = 62] = "i64_store32";
+ OperatorCode[OperatorCode["memory_size"] = 63] = "memory_size";
+ OperatorCode[OperatorCode["memory_grow"] = 64] = "memory_grow";
+ OperatorCode[OperatorCode["i32_const"] = 65] = "i32_const";
+ OperatorCode[OperatorCode["i64_const"] = 66] = "i64_const";
+ OperatorCode[OperatorCode["f32_const"] = 67] = "f32_const";
+ OperatorCode[OperatorCode["f64_const"] = 68] = "f64_const";
+ OperatorCode[OperatorCode["i32_eqz"] = 69] = "i32_eqz";
+ OperatorCode[OperatorCode["i32_eq"] = 70] = "i32_eq";
+ OperatorCode[OperatorCode["i32_ne"] = 71] = "i32_ne";
+ OperatorCode[OperatorCode["i32_lt_s"] = 72] = "i32_lt_s";
+ OperatorCode[OperatorCode["i32_lt_u"] = 73] = "i32_lt_u";
+ OperatorCode[OperatorCode["i32_gt_s"] = 74] = "i32_gt_s";
+ OperatorCode[OperatorCode["i32_gt_u"] = 75] = "i32_gt_u";
+ OperatorCode[OperatorCode["i32_le_s"] = 76] = "i32_le_s";
+ OperatorCode[OperatorCode["i32_le_u"] = 77] = "i32_le_u";
+ OperatorCode[OperatorCode["i32_ge_s"] = 78] = "i32_ge_s";
+ OperatorCode[OperatorCode["i32_ge_u"] = 79] = "i32_ge_u";
+ OperatorCode[OperatorCode["i64_eqz"] = 80] = "i64_eqz";
+ OperatorCode[OperatorCode["i64_eq"] = 81] = "i64_eq";
+ OperatorCode[OperatorCode["i64_ne"] = 82] = "i64_ne";
+ OperatorCode[OperatorCode["i64_lt_s"] = 83] = "i64_lt_s";
+ OperatorCode[OperatorCode["i64_lt_u"] = 84] = "i64_lt_u";
+ OperatorCode[OperatorCode["i64_gt_s"] = 85] = "i64_gt_s";
+ OperatorCode[OperatorCode["i64_gt_u"] = 86] = "i64_gt_u";
+ OperatorCode[OperatorCode["i64_le_s"] = 87] = "i64_le_s";
+ OperatorCode[OperatorCode["i64_le_u"] = 88] = "i64_le_u";
+ OperatorCode[OperatorCode["i64_ge_s"] = 89] = "i64_ge_s";
+ OperatorCode[OperatorCode["i64_ge_u"] = 90] = "i64_ge_u";
+ OperatorCode[OperatorCode["f32_eq"] = 91] = "f32_eq";
+ OperatorCode[OperatorCode["f32_ne"] = 92] = "f32_ne";
+ OperatorCode[OperatorCode["f32_lt"] = 93] = "f32_lt";
+ OperatorCode[OperatorCode["f32_gt"] = 94] = "f32_gt";
+ OperatorCode[OperatorCode["f32_le"] = 95] = "f32_le";
+ OperatorCode[OperatorCode["f32_ge"] = 96] = "f32_ge";
+ OperatorCode[OperatorCode["f64_eq"] = 97] = "f64_eq";
+ OperatorCode[OperatorCode["f64_ne"] = 98] = "f64_ne";
+ OperatorCode[OperatorCode["f64_lt"] = 99] = "f64_lt";
+ OperatorCode[OperatorCode["f64_gt"] = 100] = "f64_gt";
+ OperatorCode[OperatorCode["f64_le"] = 101] = "f64_le";
+ OperatorCode[OperatorCode["f64_ge"] = 102] = "f64_ge";
+ OperatorCode[OperatorCode["i32_clz"] = 103] = "i32_clz";
+ OperatorCode[OperatorCode["i32_ctz"] = 104] = "i32_ctz";
+ OperatorCode[OperatorCode["i32_popcnt"] = 105] = "i32_popcnt";
+ OperatorCode[OperatorCode["i32_add"] = 106] = "i32_add";
+ OperatorCode[OperatorCode["i32_sub"] = 107] = "i32_sub";
+ OperatorCode[OperatorCode["i32_mul"] = 108] = "i32_mul";
+ OperatorCode[OperatorCode["i32_div_s"] = 109] = "i32_div_s";
+ OperatorCode[OperatorCode["i32_div_u"] = 110] = "i32_div_u";
+ OperatorCode[OperatorCode["i32_rem_s"] = 111] = "i32_rem_s";
+ OperatorCode[OperatorCode["i32_rem_u"] = 112] = "i32_rem_u";
+ OperatorCode[OperatorCode["i32_and"] = 113] = "i32_and";
+ OperatorCode[OperatorCode["i32_or"] = 114] = "i32_or";
+ OperatorCode[OperatorCode["i32_xor"] = 115] = "i32_xor";
+ OperatorCode[OperatorCode["i32_shl"] = 116] = "i32_shl";
+ OperatorCode[OperatorCode["i32_shr_s"] = 117] = "i32_shr_s";
+ OperatorCode[OperatorCode["i32_shr_u"] = 118] = "i32_shr_u";
+ OperatorCode[OperatorCode["i32_rotl"] = 119] = "i32_rotl";
+ OperatorCode[OperatorCode["i32_rotr"] = 120] = "i32_rotr";
+ OperatorCode[OperatorCode["i64_clz"] = 121] = "i64_clz";
+ OperatorCode[OperatorCode["i64_ctz"] = 122] = "i64_ctz";
+ OperatorCode[OperatorCode["i64_popcnt"] = 123] = "i64_popcnt";
+ OperatorCode[OperatorCode["i64_add"] = 124] = "i64_add";
+ OperatorCode[OperatorCode["i64_sub"] = 125] = "i64_sub";
+ OperatorCode[OperatorCode["i64_mul"] = 126] = "i64_mul";
+ OperatorCode[OperatorCode["i64_div_s"] = 127] = "i64_div_s";
+ OperatorCode[OperatorCode["i64_div_u"] = 128] = "i64_div_u";
+ OperatorCode[OperatorCode["i64_rem_s"] = 129] = "i64_rem_s";
+ OperatorCode[OperatorCode["i64_rem_u"] = 130] = "i64_rem_u";
+ OperatorCode[OperatorCode["i64_and"] = 131] = "i64_and";
+ OperatorCode[OperatorCode["i64_or"] = 132] = "i64_or";
+ OperatorCode[OperatorCode["i64_xor"] = 133] = "i64_xor";
+ OperatorCode[OperatorCode["i64_shl"] = 134] = "i64_shl";
+ OperatorCode[OperatorCode["i64_shr_s"] = 135] = "i64_shr_s";
+ OperatorCode[OperatorCode["i64_shr_u"] = 136] = "i64_shr_u";
+ OperatorCode[OperatorCode["i64_rotl"] = 137] = "i64_rotl";
+ OperatorCode[OperatorCode["i64_rotr"] = 138] = "i64_rotr";
+ OperatorCode[OperatorCode["f32_abs"] = 139] = "f32_abs";
+ OperatorCode[OperatorCode["f32_neg"] = 140] = "f32_neg";
+ OperatorCode[OperatorCode["f32_ceil"] = 141] = "f32_ceil";
+ OperatorCode[OperatorCode["f32_floor"] = 142] = "f32_floor";
+ OperatorCode[OperatorCode["f32_trunc"] = 143] = "f32_trunc";
+ OperatorCode[OperatorCode["f32_nearest"] = 144] = "f32_nearest";
+ OperatorCode[OperatorCode["f32_sqrt"] = 145] = "f32_sqrt";
+ OperatorCode[OperatorCode["f32_add"] = 146] = "f32_add";
+ OperatorCode[OperatorCode["f32_sub"] = 147] = "f32_sub";
+ OperatorCode[OperatorCode["f32_mul"] = 148] = "f32_mul";
+ OperatorCode[OperatorCode["f32_div"] = 149] = "f32_div";
+ OperatorCode[OperatorCode["f32_min"] = 150] = "f32_min";
+ OperatorCode[OperatorCode["f32_max"] = 151] = "f32_max";
+ OperatorCode[OperatorCode["f32_copysign"] = 152] = "f32_copysign";
+ OperatorCode[OperatorCode["f64_abs"] = 153] = "f64_abs";
+ OperatorCode[OperatorCode["f64_neg"] = 154] = "f64_neg";
+ OperatorCode[OperatorCode["f64_ceil"] = 155] = "f64_ceil";
+ OperatorCode[OperatorCode["f64_floor"] = 156] = "f64_floor";
+ OperatorCode[OperatorCode["f64_trunc"] = 157] = "f64_trunc";
+ OperatorCode[OperatorCode["f64_nearest"] = 158] = "f64_nearest";
+ OperatorCode[OperatorCode["f64_sqrt"] = 159] = "f64_sqrt";
+ OperatorCode[OperatorCode["f64_add"] = 160] = "f64_add";
+ OperatorCode[OperatorCode["f64_sub"] = 161] = "f64_sub";
+ OperatorCode[OperatorCode["f64_mul"] = 162] = "f64_mul";
+ OperatorCode[OperatorCode["f64_div"] = 163] = "f64_div";
+ OperatorCode[OperatorCode["f64_min"] = 164] = "f64_min";
+ OperatorCode[OperatorCode["f64_max"] = 165] = "f64_max";
+ OperatorCode[OperatorCode["f64_copysign"] = 166] = "f64_copysign";
+ OperatorCode[OperatorCode["i32_wrap_i64"] = 167] = "i32_wrap_i64";
+ OperatorCode[OperatorCode["i32_trunc_f32_s"] = 168] = "i32_trunc_f32_s";
+ OperatorCode[OperatorCode["i32_trunc_f32_u"] = 169] = "i32_trunc_f32_u";
+ OperatorCode[OperatorCode["i32_trunc_f64_s"] = 170] = "i32_trunc_f64_s";
+ OperatorCode[OperatorCode["i32_trunc_f64_u"] = 171] = "i32_trunc_f64_u";
+ OperatorCode[OperatorCode["i64_extend_i32_s"] = 172] = "i64_extend_i32_s";
+ OperatorCode[OperatorCode["i64_extend_i32_u"] = 173] = "i64_extend_i32_u";
+ OperatorCode[OperatorCode["i64_trunc_f32_s"] = 174] = "i64_trunc_f32_s";
+ OperatorCode[OperatorCode["i64_trunc_f32_u"] = 175] = "i64_trunc_f32_u";
+ OperatorCode[OperatorCode["i64_trunc_f64_s"] = 176] = "i64_trunc_f64_s";
+ OperatorCode[OperatorCode["i64_trunc_f64_u"] = 177] = "i64_trunc_f64_u";
+ OperatorCode[OperatorCode["f32_convert_i32_s"] = 178] = "f32_convert_i32_s";
+ OperatorCode[OperatorCode["f32_convert_i32_u"] = 179] = "f32_convert_i32_u";
+ OperatorCode[OperatorCode["f32_convert_i64_s"] = 180] = "f32_convert_i64_s";
+ OperatorCode[OperatorCode["f32_convert_i64_u"] = 181] = "f32_convert_i64_u";
+ OperatorCode[OperatorCode["f32_demote_f64"] = 182] = "f32_demote_f64";
+ OperatorCode[OperatorCode["f64_convert_i32_s"] = 183] = "f64_convert_i32_s";
+ OperatorCode[OperatorCode["f64_convert_i32_u"] = 184] = "f64_convert_i32_u";
+ OperatorCode[OperatorCode["f64_convert_i64_s"] = 185] = "f64_convert_i64_s";
+ OperatorCode[OperatorCode["f64_convert_i64_u"] = 186] = "f64_convert_i64_u";
+ OperatorCode[OperatorCode["f64_promote_f32"] = 187] = "f64_promote_f32";
+ OperatorCode[OperatorCode["i32_reinterpret_f32"] = 188] = "i32_reinterpret_f32";
+ OperatorCode[OperatorCode["i64_reinterpret_f64"] = 189] = "i64_reinterpret_f64";
+ OperatorCode[OperatorCode["f32_reinterpret_i32"] = 190] = "f32_reinterpret_i32";
+ OperatorCode[OperatorCode["f64_reinterpret_i64"] = 191] = "f64_reinterpret_i64";
+ OperatorCode[OperatorCode["i32_extend8_s"] = 192] = "i32_extend8_s";
+ OperatorCode[OperatorCode["i32_extend16_s"] = 193] = "i32_extend16_s";
+ OperatorCode[OperatorCode["i64_extend8_s"] = 194] = "i64_extend8_s";
+ OperatorCode[OperatorCode["i64_extend16_s"] = 195] = "i64_extend16_s";
+ OperatorCode[OperatorCode["i64_extend32_s"] = 196] = "i64_extend32_s";
+ OperatorCode[OperatorCode["prefix_0xfb"] = 251] = "prefix_0xfb";
+ OperatorCode[OperatorCode["prefix_0xfc"] = 252] = "prefix_0xfc";
+ OperatorCode[OperatorCode["prefix_0xfd"] = 253] = "prefix_0xfd";
+ OperatorCode[OperatorCode["prefix_0xfe"] = 254] = "prefix_0xfe";
+ OperatorCode[OperatorCode["i32_trunc_sat_f32_s"] = 64512] = "i32_trunc_sat_f32_s";
+ OperatorCode[OperatorCode["i32_trunc_sat_f32_u"] = 64513] = "i32_trunc_sat_f32_u";
+ OperatorCode[OperatorCode["i32_trunc_sat_f64_s"] = 64514] = "i32_trunc_sat_f64_s";
+ OperatorCode[OperatorCode["i32_trunc_sat_f64_u"] = 64515] = "i32_trunc_sat_f64_u";
+ OperatorCode[OperatorCode["i64_trunc_sat_f32_s"] = 64516] = "i64_trunc_sat_f32_s";
+ OperatorCode[OperatorCode["i64_trunc_sat_f32_u"] = 64517] = "i64_trunc_sat_f32_u";
+ OperatorCode[OperatorCode["i64_trunc_sat_f64_s"] = 64518] = "i64_trunc_sat_f64_s";
+ OperatorCode[OperatorCode["i64_trunc_sat_f64_u"] = 64519] = "i64_trunc_sat_f64_u";
+ OperatorCode[OperatorCode["memory_init"] = 64520] = "memory_init";
+ OperatorCode[OperatorCode["data_drop"] = 64521] = "data_drop";
+ OperatorCode[OperatorCode["memory_copy"] = 64522] = "memory_copy";
+ OperatorCode[OperatorCode["memory_fill"] = 64523] = "memory_fill";
+ OperatorCode[OperatorCode["table_init"] = 64524] = "table_init";
+ OperatorCode[OperatorCode["elem_drop"] = 64525] = "elem_drop";
+ OperatorCode[OperatorCode["table_copy"] = 64526] = "table_copy";
+ OperatorCode[OperatorCode["table_grow"] = 64527] = "table_grow";
+ OperatorCode[OperatorCode["table_size"] = 64528] = "table_size";
+ OperatorCode[OperatorCode["table_fill"] = 64529] = "table_fill";
+ OperatorCode[OperatorCode["table_get"] = 37] = "table_get";
+ OperatorCode[OperatorCode["table_set"] = 38] = "table_set";
+ OperatorCode[OperatorCode["ref_null"] = 208] = "ref_null";
+ OperatorCode[OperatorCode["ref_is_null"] = 209] = "ref_is_null";
+ OperatorCode[OperatorCode["ref_func"] = 210] = "ref_func";
+ OperatorCode[OperatorCode["ref_eq"] = 211] = "ref_eq";
+ OperatorCode[OperatorCode["ref_as_non_null"] = 212] = "ref_as_non_null";
+ OperatorCode[OperatorCode["br_on_null"] = 213] = "br_on_null";
+ OperatorCode[OperatorCode["br_on_non_null"] = 214] = "br_on_non_null";
+ OperatorCode[OperatorCode["memory_atomic_notify"] = 65024] = "memory_atomic_notify";
+ OperatorCode[OperatorCode["memory_atomic_wait32"] = 65025] = "memory_atomic_wait32";
+ OperatorCode[OperatorCode["memory_atomic_wait64"] = 65026] = "memory_atomic_wait64";
+ OperatorCode[OperatorCode["atomic_fence"] = 65027] = "atomic_fence";
+ OperatorCode[OperatorCode["i32_atomic_load"] = 65040] = "i32_atomic_load";
+ OperatorCode[OperatorCode["i64_atomic_load"] = 65041] = "i64_atomic_load";
+ OperatorCode[OperatorCode["i32_atomic_load8_u"] = 65042] = "i32_atomic_load8_u";
+ OperatorCode[OperatorCode["i32_atomic_load16_u"] = 65043] = "i32_atomic_load16_u";
+ OperatorCode[OperatorCode["i64_atomic_load8_u"] = 65044] = "i64_atomic_load8_u";
+ OperatorCode[OperatorCode["i64_atomic_load16_u"] = 65045] = "i64_atomic_load16_u";
+ OperatorCode[OperatorCode["i64_atomic_load32_u"] = 65046] = "i64_atomic_load32_u";
+ OperatorCode[OperatorCode["i32_atomic_store"] = 65047] = "i32_atomic_store";
+ OperatorCode[OperatorCode["i64_atomic_store"] = 65048] = "i64_atomic_store";
+ OperatorCode[OperatorCode["i32_atomic_store8"] = 65049] = "i32_atomic_store8";
+ OperatorCode[OperatorCode["i32_atomic_store16"] = 65050] = "i32_atomic_store16";
+ OperatorCode[OperatorCode["i64_atomic_store8"] = 65051] = "i64_atomic_store8";
+ OperatorCode[OperatorCode["i64_atomic_store16"] = 65052] = "i64_atomic_store16";
+ OperatorCode[OperatorCode["i64_atomic_store32"] = 65053] = "i64_atomic_store32";
+ OperatorCode[OperatorCode["i32_atomic_rmw_add"] = 65054] = "i32_atomic_rmw_add";
+ OperatorCode[OperatorCode["i64_atomic_rmw_add"] = 65055] = "i64_atomic_rmw_add";
+ OperatorCode[OperatorCode["i32_atomic_rmw8_add_u"] = 65056] = "i32_atomic_rmw8_add_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw16_add_u"] = 65057] = "i32_atomic_rmw16_add_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw8_add_u"] = 65058] = "i64_atomic_rmw8_add_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw16_add_u"] = 65059] = "i64_atomic_rmw16_add_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw32_add_u"] = 65060] = "i64_atomic_rmw32_add_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw_sub"] = 65061] = "i32_atomic_rmw_sub";
+ OperatorCode[OperatorCode["i64_atomic_rmw_sub"] = 65062] = "i64_atomic_rmw_sub";
+ OperatorCode[OperatorCode["i32_atomic_rmw8_sub_u"] = 65063] = "i32_atomic_rmw8_sub_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw16_sub_u"] = 65064] = "i32_atomic_rmw16_sub_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw8_sub_u"] = 65065] = "i64_atomic_rmw8_sub_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw16_sub_u"] = 65066] = "i64_atomic_rmw16_sub_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw32_sub_u"] = 65067] = "i64_atomic_rmw32_sub_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw_and"] = 65068] = "i32_atomic_rmw_and";
+ OperatorCode[OperatorCode["i64_atomic_rmw_and"] = 65069] = "i64_atomic_rmw_and";
+ OperatorCode[OperatorCode["i32_atomic_rmw8_and_u"] = 65070] = "i32_atomic_rmw8_and_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw16_and_u"] = 65071] = "i32_atomic_rmw16_and_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw8_and_u"] = 65072] = "i64_atomic_rmw8_and_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw16_and_u"] = 65073] = "i64_atomic_rmw16_and_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw32_and_u"] = 65074] = "i64_atomic_rmw32_and_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw_or"] = 65075] = "i32_atomic_rmw_or";
+ OperatorCode[OperatorCode["i64_atomic_rmw_or"] = 65076] = "i64_atomic_rmw_or";
+ OperatorCode[OperatorCode["i32_atomic_rmw8_or_u"] = 65077] = "i32_atomic_rmw8_or_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw16_or_u"] = 65078] = "i32_atomic_rmw16_or_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw8_or_u"] = 65079] = "i64_atomic_rmw8_or_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw16_or_u"] = 65080] = "i64_atomic_rmw16_or_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw32_or_u"] = 65081] = "i64_atomic_rmw32_or_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw_xor"] = 65082] = "i32_atomic_rmw_xor";
+ OperatorCode[OperatorCode["i64_atomic_rmw_xor"] = 65083] = "i64_atomic_rmw_xor";
+ OperatorCode[OperatorCode["i32_atomic_rmw8_xor_u"] = 65084] = "i32_atomic_rmw8_xor_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw16_xor_u"] = 65085] = "i32_atomic_rmw16_xor_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw8_xor_u"] = 65086] = "i64_atomic_rmw8_xor_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw16_xor_u"] = 65087] = "i64_atomic_rmw16_xor_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw32_xor_u"] = 65088] = "i64_atomic_rmw32_xor_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw_xchg"] = 65089] = "i32_atomic_rmw_xchg";
+ OperatorCode[OperatorCode["i64_atomic_rmw_xchg"] = 65090] = "i64_atomic_rmw_xchg";
+ OperatorCode[OperatorCode["i32_atomic_rmw8_xchg_u"] = 65091] = "i32_atomic_rmw8_xchg_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw16_xchg_u"] = 65092] = "i32_atomic_rmw16_xchg_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw8_xchg_u"] = 65093] = "i64_atomic_rmw8_xchg_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw16_xchg_u"] = 65094] = "i64_atomic_rmw16_xchg_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw32_xchg_u"] = 65095] = "i64_atomic_rmw32_xchg_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw_cmpxchg"] = 65096] = "i32_atomic_rmw_cmpxchg";
+ OperatorCode[OperatorCode["i64_atomic_rmw_cmpxchg"] = 65097] = "i64_atomic_rmw_cmpxchg";
+ OperatorCode[OperatorCode["i32_atomic_rmw8_cmpxchg_u"] = 65098] = "i32_atomic_rmw8_cmpxchg_u";
+ OperatorCode[OperatorCode["i32_atomic_rmw16_cmpxchg_u"] = 65099] = "i32_atomic_rmw16_cmpxchg_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw8_cmpxchg_u"] = 65100] = "i64_atomic_rmw8_cmpxchg_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw16_cmpxchg_u"] = 65101] = "i64_atomic_rmw16_cmpxchg_u";
+ OperatorCode[OperatorCode["i64_atomic_rmw32_cmpxchg_u"] = 65102] = "i64_atomic_rmw32_cmpxchg_u";
+ OperatorCode[OperatorCode["v128_load"] = 1036288] = "v128_load";
+ OperatorCode[OperatorCode["i16x8_load8x8_s"] = 1036289] = "i16x8_load8x8_s";
+ OperatorCode[OperatorCode["i16x8_load8x8_u"] = 1036290] = "i16x8_load8x8_u";
+ OperatorCode[OperatorCode["i32x4_load16x4_s"] = 1036291] = "i32x4_load16x4_s";
+ OperatorCode[OperatorCode["i32x4_load16x4_u"] = 1036292] = "i32x4_load16x4_u";
+ OperatorCode[OperatorCode["i64x2_load32x2_s"] = 1036293] = "i64x2_load32x2_s";
+ OperatorCode[OperatorCode["i64x2_load32x2_u"] = 1036294] = "i64x2_load32x2_u";
+ OperatorCode[OperatorCode["v8x16_load_splat"] = 1036295] = "v8x16_load_splat";
+ OperatorCode[OperatorCode["v16x8_load_splat"] = 1036296] = "v16x8_load_splat";
+ OperatorCode[OperatorCode["v32x4_load_splat"] = 1036297] = "v32x4_load_splat";
+ OperatorCode[OperatorCode["v64x2_load_splat"] = 1036298] = "v64x2_load_splat";
+ OperatorCode[OperatorCode["v128_store"] = 1036299] = "v128_store";
+ OperatorCode[OperatorCode["v128_const"] = 1036300] = "v128_const";
+ OperatorCode[OperatorCode["i8x16_shuffle"] = 1036301] = "i8x16_shuffle";
+ OperatorCode[OperatorCode["i8x16_swizzle"] = 1036302] = "i8x16_swizzle";
+ OperatorCode[OperatorCode["i8x16_splat"] = 1036303] = "i8x16_splat";
+ OperatorCode[OperatorCode["i16x8_splat"] = 1036304] = "i16x8_splat";
+ OperatorCode[OperatorCode["i32x4_splat"] = 1036305] = "i32x4_splat";
+ OperatorCode[OperatorCode["i64x2_splat"] = 1036306] = "i64x2_splat";
+ OperatorCode[OperatorCode["f32x4_splat"] = 1036307] = "f32x4_splat";
+ OperatorCode[OperatorCode["f64x2_splat"] = 1036308] = "f64x2_splat";
+ OperatorCode[OperatorCode["i8x16_extract_lane_s"] = 1036309] = "i8x16_extract_lane_s";
+ OperatorCode[OperatorCode["i8x16_extract_lane_u"] = 1036310] = "i8x16_extract_lane_u";
+ OperatorCode[OperatorCode["i8x16_replace_lane"] = 1036311] = "i8x16_replace_lane";
+ OperatorCode[OperatorCode["i16x8_extract_lane_s"] = 1036312] = "i16x8_extract_lane_s";
+ OperatorCode[OperatorCode["i16x8_extract_lane_u"] = 1036313] = "i16x8_extract_lane_u";
+ OperatorCode[OperatorCode["i16x8_replace_lane"] = 1036314] = "i16x8_replace_lane";
+ OperatorCode[OperatorCode["i32x4_extract_lane"] = 1036315] = "i32x4_extract_lane";
+ OperatorCode[OperatorCode["i32x4_replace_lane"] = 1036316] = "i32x4_replace_lane";
+ OperatorCode[OperatorCode["i64x2_extract_lane"] = 1036317] = "i64x2_extract_lane";
+ OperatorCode[OperatorCode["i64x2_replace_lane"] = 1036318] = "i64x2_replace_lane";
+ OperatorCode[OperatorCode["f32x4_extract_lane"] = 1036319] = "f32x4_extract_lane";
+ OperatorCode[OperatorCode["f32x4_replace_lane"] = 1036320] = "f32x4_replace_lane";
+ OperatorCode[OperatorCode["f64x2_extract_lane"] = 1036321] = "f64x2_extract_lane";
+ OperatorCode[OperatorCode["f64x2_replace_lane"] = 1036322] = "f64x2_replace_lane";
+ OperatorCode[OperatorCode["i8x16_eq"] = 1036323] = "i8x16_eq";
+ OperatorCode[OperatorCode["i8x16_ne"] = 1036324] = "i8x16_ne";
+ OperatorCode[OperatorCode["i8x16_lt_s"] = 1036325] = "i8x16_lt_s";
+ OperatorCode[OperatorCode["i8x16_lt_u"] = 1036326] = "i8x16_lt_u";
+ OperatorCode[OperatorCode["i8x16_gt_s"] = 1036327] = "i8x16_gt_s";
+ OperatorCode[OperatorCode["i8x16_gt_u"] = 1036328] = "i8x16_gt_u";
+ OperatorCode[OperatorCode["i8x16_le_s"] = 1036329] = "i8x16_le_s";
+ OperatorCode[OperatorCode["i8x16_le_u"] = 1036330] = "i8x16_le_u";
+ OperatorCode[OperatorCode["i8x16_ge_s"] = 1036331] = "i8x16_ge_s";
+ OperatorCode[OperatorCode["i8x16_ge_u"] = 1036332] = "i8x16_ge_u";
+ OperatorCode[OperatorCode["i16x8_eq"] = 1036333] = "i16x8_eq";
+ OperatorCode[OperatorCode["i16x8_ne"] = 1036334] = "i16x8_ne";
+ OperatorCode[OperatorCode["i16x8_lt_s"] = 1036335] = "i16x8_lt_s";
+ OperatorCode[OperatorCode["i16x8_lt_u"] = 1036336] = "i16x8_lt_u";
+ OperatorCode[OperatorCode["i16x8_gt_s"] = 1036337] = "i16x8_gt_s";
+ OperatorCode[OperatorCode["i16x8_gt_u"] = 1036338] = "i16x8_gt_u";
+ OperatorCode[OperatorCode["i16x8_le_s"] = 1036339] = "i16x8_le_s";
+ OperatorCode[OperatorCode["i16x8_le_u"] = 1036340] = "i16x8_le_u";
+ OperatorCode[OperatorCode["i16x8_ge_s"] = 1036341] = "i16x8_ge_s";
+ OperatorCode[OperatorCode["i16x8_ge_u"] = 1036342] = "i16x8_ge_u";
+ OperatorCode[OperatorCode["i32x4_eq"] = 1036343] = "i32x4_eq";
+ OperatorCode[OperatorCode["i32x4_ne"] = 1036344] = "i32x4_ne";
+ OperatorCode[OperatorCode["i32x4_lt_s"] = 1036345] = "i32x4_lt_s";
+ OperatorCode[OperatorCode["i32x4_lt_u"] = 1036346] = "i32x4_lt_u";
+ OperatorCode[OperatorCode["i32x4_gt_s"] = 1036347] = "i32x4_gt_s";
+ OperatorCode[OperatorCode["i32x4_gt_u"] = 1036348] = "i32x4_gt_u";
+ OperatorCode[OperatorCode["i32x4_le_s"] = 1036349] = "i32x4_le_s";
+ OperatorCode[OperatorCode["i32x4_le_u"] = 1036350] = "i32x4_le_u";
+ OperatorCode[OperatorCode["i32x4_ge_s"] = 1036351] = "i32x4_ge_s";
+ OperatorCode[OperatorCode["i32x4_ge_u"] = 1036352] = "i32x4_ge_u";
+ OperatorCode[OperatorCode["f32x4_eq"] = 1036353] = "f32x4_eq";
+ OperatorCode[OperatorCode["f32x4_ne"] = 1036354] = "f32x4_ne";
+ OperatorCode[OperatorCode["f32x4_lt"] = 1036355] = "f32x4_lt";
+ OperatorCode[OperatorCode["f32x4_gt"] = 1036356] = "f32x4_gt";
+ OperatorCode[OperatorCode["f32x4_le"] = 1036357] = "f32x4_le";
+ OperatorCode[OperatorCode["f32x4_ge"] = 1036358] = "f32x4_ge";
+ OperatorCode[OperatorCode["f64x2_eq"] = 1036359] = "f64x2_eq";
+ OperatorCode[OperatorCode["f64x2_ne"] = 1036360] = "f64x2_ne";
+ OperatorCode[OperatorCode["f64x2_lt"] = 1036361] = "f64x2_lt";
+ OperatorCode[OperatorCode["f64x2_gt"] = 1036362] = "f64x2_gt";
+ OperatorCode[OperatorCode["f64x2_le"] = 1036363] = "f64x2_le";
+ OperatorCode[OperatorCode["f64x2_ge"] = 1036364] = "f64x2_ge";
+ OperatorCode[OperatorCode["v128_not"] = 1036365] = "v128_not";
+ OperatorCode[OperatorCode["v128_and"] = 1036366] = "v128_and";
+ OperatorCode[OperatorCode["v128_andnot"] = 1036367] = "v128_andnot";
+ OperatorCode[OperatorCode["v128_or"] = 1036368] = "v128_or";
+ OperatorCode[OperatorCode["v128_xor"] = 1036369] = "v128_xor";
+ OperatorCode[OperatorCode["v128_bitselect"] = 1036370] = "v128_bitselect";
+ OperatorCode[OperatorCode["v128_any_true"] = 1036371] = "v128_any_true";
+ OperatorCode[OperatorCode["v128_load8_lane"] = 1036372] = "v128_load8_lane";
+ OperatorCode[OperatorCode["v128_load16_lane"] = 1036373] = "v128_load16_lane";
+ OperatorCode[OperatorCode["v128_load32_lane"] = 1036374] = "v128_load32_lane";
+ OperatorCode[OperatorCode["v128_load64_lane"] = 1036375] = "v128_load64_lane";
+ OperatorCode[OperatorCode["v128_store8_lane"] = 1036376] = "v128_store8_lane";
+ OperatorCode[OperatorCode["v128_store16_lane"] = 1036377] = "v128_store16_lane";
+ OperatorCode[OperatorCode["v128_store32_lane"] = 1036378] = "v128_store32_lane";
+ OperatorCode[OperatorCode["v128_store64_lane"] = 1036379] = "v128_store64_lane";
+ OperatorCode[OperatorCode["v128_load32_zero"] = 1036380] = "v128_load32_zero";
+ OperatorCode[OperatorCode["v128_load64_zero"] = 1036381] = "v128_load64_zero";
+ OperatorCode[OperatorCode["f32x4_demote_f64x2_zero"] = 1036382] = "f32x4_demote_f64x2_zero";
+ OperatorCode[OperatorCode["f64x2_promote_low_f32x4"] = 1036383] = "f64x2_promote_low_f32x4";
+ OperatorCode[OperatorCode["i8x16_abs"] = 1036384] = "i8x16_abs";
+ OperatorCode[OperatorCode["i8x16_neg"] = 1036385] = "i8x16_neg";
+ OperatorCode[OperatorCode["i8x16_popcnt"] = 1036386] = "i8x16_popcnt";
+ OperatorCode[OperatorCode["i8x16_all_true"] = 1036387] = "i8x16_all_true";
+ OperatorCode[OperatorCode["i8x16_bitmask"] = 1036388] = "i8x16_bitmask";
+ OperatorCode[OperatorCode["i8x16_narrow_i16x8_s"] = 1036389] = "i8x16_narrow_i16x8_s";
+ OperatorCode[OperatorCode["i8x16_narrow_i16x8_u"] = 1036390] = "i8x16_narrow_i16x8_u";
+ OperatorCode[OperatorCode["f32x4_ceil"] = 1036391] = "f32x4_ceil";
+ OperatorCode[OperatorCode["f32x4_floor"] = 1036392] = "f32x4_floor";
+ OperatorCode[OperatorCode["f32x4_trunc"] = 1036393] = "f32x4_trunc";
+ OperatorCode[OperatorCode["f32x4_nearest"] = 1036394] = "f32x4_nearest";
+ OperatorCode[OperatorCode["i8x16_shl"] = 1036395] = "i8x16_shl";
+ OperatorCode[OperatorCode["i8x16_shr_s"] = 1036396] = "i8x16_shr_s";
+ OperatorCode[OperatorCode["i8x16_shr_u"] = 1036397] = "i8x16_shr_u";
+ OperatorCode[OperatorCode["i8x16_add"] = 1036398] = "i8x16_add";
+ OperatorCode[OperatorCode["i8x16_add_sat_s"] = 1036399] = "i8x16_add_sat_s";
+ OperatorCode[OperatorCode["i8x16_add_sat_u"] = 1036400] = "i8x16_add_sat_u";
+ OperatorCode[OperatorCode["i8x16_sub"] = 1036401] = "i8x16_sub";
+ OperatorCode[OperatorCode["i8x16_sub_sat_s"] = 1036402] = "i8x16_sub_sat_s";
+ OperatorCode[OperatorCode["i8x16_sub_sat_u"] = 1036403] = "i8x16_sub_sat_u";
+ OperatorCode[OperatorCode["f64x2_ceil"] = 1036404] = "f64x2_ceil";
+ OperatorCode[OperatorCode["f64x2_floor"] = 1036405] = "f64x2_floor";
+ OperatorCode[OperatorCode["i8x16_min_s"] = 1036406] = "i8x16_min_s";
+ OperatorCode[OperatorCode["i8x16_min_u"] = 1036407] = "i8x16_min_u";
+ OperatorCode[OperatorCode["i8x16_max_s"] = 1036408] = "i8x16_max_s";
+ OperatorCode[OperatorCode["i8x16_max_u"] = 1036409] = "i8x16_max_u";
+ OperatorCode[OperatorCode["f64x2_trunc"] = 1036410] = "f64x2_trunc";
+ OperatorCode[OperatorCode["i8x16_avgr_u"] = 1036411] = "i8x16_avgr_u";
+ OperatorCode[OperatorCode["i16x8_extadd_pairwise_i8x16_s"] = 1036412] = "i16x8_extadd_pairwise_i8x16_s";
+ OperatorCode[OperatorCode["i16x8_extadd_pairwise_i8x16_u"] = 1036413] = "i16x8_extadd_pairwise_i8x16_u";
+ OperatorCode[OperatorCode["i32x4_extadd_pairwise_i16x8_s"] = 1036414] = "i32x4_extadd_pairwise_i16x8_s";
+ OperatorCode[OperatorCode["i32x4_extadd_pairwise_i16x8_u"] = 1036415] = "i32x4_extadd_pairwise_i16x8_u";
+ OperatorCode[OperatorCode["i16x8_abs"] = 1036416] = "i16x8_abs";
+ OperatorCode[OperatorCode["i16x8_neg"] = 1036417] = "i16x8_neg";
+ OperatorCode[OperatorCode["i16x8_q15mulr_sat_s"] = 1036418] = "i16x8_q15mulr_sat_s";
+ OperatorCode[OperatorCode["i16x8_all_true"] = 1036419] = "i16x8_all_true";
+ OperatorCode[OperatorCode["i16x8_bitmask"] = 1036420] = "i16x8_bitmask";
+ OperatorCode[OperatorCode["i16x8_narrow_i32x4_s"] = 1036421] = "i16x8_narrow_i32x4_s";
+ OperatorCode[OperatorCode["i16x8_narrow_i32x4_u"] = 1036422] = "i16x8_narrow_i32x4_u";
+ OperatorCode[OperatorCode["i16x8_extend_low_i8x16_s"] = 1036423] = "i16x8_extend_low_i8x16_s";
+ OperatorCode[OperatorCode["i16x8_extend_high_i8x16_s"] = 1036424] = "i16x8_extend_high_i8x16_s";
+ OperatorCode[OperatorCode["i16x8_extend_low_i8x16_u"] = 1036425] = "i16x8_extend_low_i8x16_u";
+ OperatorCode[OperatorCode["i16x8_extend_high_i8x16_u"] = 1036426] = "i16x8_extend_high_i8x16_u";
+ OperatorCode[OperatorCode["i16x8_shl"] = 1036427] = "i16x8_shl";
+ OperatorCode[OperatorCode["i16x8_shr_s"] = 1036428] = "i16x8_shr_s";
+ OperatorCode[OperatorCode["i16x8_shr_u"] = 1036429] = "i16x8_shr_u";
+ OperatorCode[OperatorCode["i16x8_add"] = 1036430] = "i16x8_add";
+ OperatorCode[OperatorCode["i16x8_add_sat_s"] = 1036431] = "i16x8_add_sat_s";
+ OperatorCode[OperatorCode["i16x8_add_sat_u"] = 1036432] = "i16x8_add_sat_u";
+ OperatorCode[OperatorCode["i16x8_sub"] = 1036433] = "i16x8_sub";
+ OperatorCode[OperatorCode["i16x8_sub_sat_s"] = 1036434] = "i16x8_sub_sat_s";
+ OperatorCode[OperatorCode["i16x8_sub_sat_u"] = 1036435] = "i16x8_sub_sat_u";
+ OperatorCode[OperatorCode["f64x2_nearest"] = 1036436] = "f64x2_nearest";
+ OperatorCode[OperatorCode["i16x8_mul"] = 1036437] = "i16x8_mul";
+ OperatorCode[OperatorCode["i16x8_min_s"] = 1036438] = "i16x8_min_s";
+ OperatorCode[OperatorCode["i16x8_min_u"] = 1036439] = "i16x8_min_u";
+ OperatorCode[OperatorCode["i16x8_max_s"] = 1036440] = "i16x8_max_s";
+ OperatorCode[OperatorCode["i16x8_max_u"] = 1036441] = "i16x8_max_u";
+ OperatorCode[OperatorCode["i16x8_avgr_u"] = 1036443] = "i16x8_avgr_u";
+ OperatorCode[OperatorCode["i16x8_extmul_low_i8x16_s"] = 1036444] = "i16x8_extmul_low_i8x16_s";
+ OperatorCode[OperatorCode["i16x8_extmul_high_i8x16_s"] = 1036445] = "i16x8_extmul_high_i8x16_s";
+ OperatorCode[OperatorCode["i16x8_extmul_low_i8x16_u"] = 1036446] = "i16x8_extmul_low_i8x16_u";
+ OperatorCode[OperatorCode["i16x8_extmul_high_i8x16_u"] = 1036447] = "i16x8_extmul_high_i8x16_u";
+ OperatorCode[OperatorCode["i32x4_abs"] = 1036448] = "i32x4_abs";
+ OperatorCode[OperatorCode["i32x4_neg"] = 1036449] = "i32x4_neg";
+ OperatorCode[OperatorCode["i32x4_all_true"] = 1036451] = "i32x4_all_true";
+ OperatorCode[OperatorCode["i32x4_bitmask"] = 1036452] = "i32x4_bitmask";
+ OperatorCode[OperatorCode["i32x4_extend_low_i16x8_s"] = 1036455] = "i32x4_extend_low_i16x8_s";
+ OperatorCode[OperatorCode["i32x4_extend_high_i16x8_s"] = 1036456] = "i32x4_extend_high_i16x8_s";
+ OperatorCode[OperatorCode["i32x4_extend_low_i16x8_u"] = 1036457] = "i32x4_extend_low_i16x8_u";
+ OperatorCode[OperatorCode["i32x4_extend_high_i16x8_u"] = 1036458] = "i32x4_extend_high_i16x8_u";
+ OperatorCode[OperatorCode["i32x4_shl"] = 1036459] = "i32x4_shl";
+ OperatorCode[OperatorCode["i32x4_shr_s"] = 1036460] = "i32x4_shr_s";
+ OperatorCode[OperatorCode["i32x4_shr_u"] = 1036461] = "i32x4_shr_u";
+ OperatorCode[OperatorCode["i32x4_add"] = 1036462] = "i32x4_add";
+ OperatorCode[OperatorCode["i32x4_sub"] = 1036465] = "i32x4_sub";
+ OperatorCode[OperatorCode["i32x4_mul"] = 1036469] = "i32x4_mul";
+ OperatorCode[OperatorCode["i32x4_min_s"] = 1036470] = "i32x4_min_s";
+ OperatorCode[OperatorCode["i32x4_min_u"] = 1036471] = "i32x4_min_u";
+ OperatorCode[OperatorCode["i32x4_max_s"] = 1036472] = "i32x4_max_s";
+ OperatorCode[OperatorCode["i32x4_max_u"] = 1036473] = "i32x4_max_u";
+ OperatorCode[OperatorCode["i32x4_dot_i16x8_s"] = 1036474] = "i32x4_dot_i16x8_s";
+ OperatorCode[OperatorCode["i32x4_extmul_low_i16x8_s"] = 1036476] = "i32x4_extmul_low_i16x8_s";
+ OperatorCode[OperatorCode["i32x4_extmul_high_i16x8_s"] = 1036477] = "i32x4_extmul_high_i16x8_s";
+ OperatorCode[OperatorCode["i32x4_extmul_low_i16x8_u"] = 1036478] = "i32x4_extmul_low_i16x8_u";
+ OperatorCode[OperatorCode["i32x4_extmul_high_i16x8_u"] = 1036479] = "i32x4_extmul_high_i16x8_u";
+ OperatorCode[OperatorCode["i64x2_abs"] = 1036480] = "i64x2_abs";
+ OperatorCode[OperatorCode["i64x2_neg"] = 1036481] = "i64x2_neg";
+ OperatorCode[OperatorCode["i64x2_all_true"] = 1036483] = "i64x2_all_true";
+ OperatorCode[OperatorCode["i64x2_bitmask"] = 1036484] = "i64x2_bitmask";
+ OperatorCode[OperatorCode["i64x2_extend_low_i32x4_s"] = 1036487] = "i64x2_extend_low_i32x4_s";
+ OperatorCode[OperatorCode["i64x2_extend_high_i32x4_s"] = 1036488] = "i64x2_extend_high_i32x4_s";
+ OperatorCode[OperatorCode["i64x2_extend_low_i32x4_u"] = 1036489] = "i64x2_extend_low_i32x4_u";
+ OperatorCode[OperatorCode["i64x2_extend_high_i32x4_u"] = 1036490] = "i64x2_extend_high_i32x4_u";
+ OperatorCode[OperatorCode["i64x2_shl"] = 1036491] = "i64x2_shl";
+ OperatorCode[OperatorCode["i64x2_shr_s"] = 1036492] = "i64x2_shr_s";
+ OperatorCode[OperatorCode["i64x2_shr_u"] = 1036493] = "i64x2_shr_u";
+ OperatorCode[OperatorCode["i64x2_add"] = 1036494] = "i64x2_add";
+ OperatorCode[OperatorCode["i64x2_sub"] = 1036497] = "i64x2_sub";
+ OperatorCode[OperatorCode["i64x2_mul"] = 1036501] = "i64x2_mul";
+ OperatorCode[OperatorCode["i64x2_eq"] = 1036502] = "i64x2_eq";
+ OperatorCode[OperatorCode["i64x2_ne"] = 1036503] = "i64x2_ne";
+ OperatorCode[OperatorCode["i64x2_lt_s"] = 1036504] = "i64x2_lt_s";
+ OperatorCode[OperatorCode["i64x2_gt_s"] = 1036505] = "i64x2_gt_s";
+ OperatorCode[OperatorCode["i64x2_le_s"] = 1036506] = "i64x2_le_s";
+ OperatorCode[OperatorCode["i64x2_ge_s"] = 1036507] = "i64x2_ge_s";
+ OperatorCode[OperatorCode["i64x2_extmul_low_i32x4_s"] = 1036508] = "i64x2_extmul_low_i32x4_s";
+ OperatorCode[OperatorCode["i64x2_extmul_high_i32x4_s"] = 1036509] = "i64x2_extmul_high_i32x4_s";
+ OperatorCode[OperatorCode["i64x2_extmul_low_i32x4_u"] = 1036510] = "i64x2_extmul_low_i32x4_u";
+ OperatorCode[OperatorCode["i64x2_extmul_high_i32x4_u"] = 1036511] = "i64x2_extmul_high_i32x4_u";
+ OperatorCode[OperatorCode["f32x4_abs"] = 1036512] = "f32x4_abs";
+ OperatorCode[OperatorCode["f32x4_neg"] = 1036513] = "f32x4_neg";
+ OperatorCode[OperatorCode["f32x4_sqrt"] = 1036515] = "f32x4_sqrt";
+ OperatorCode[OperatorCode["f32x4_add"] = 1036516] = "f32x4_add";
+ OperatorCode[OperatorCode["f32x4_sub"] = 1036517] = "f32x4_sub";
+ OperatorCode[OperatorCode["f32x4_mul"] = 1036518] = "f32x4_mul";
+ OperatorCode[OperatorCode["f32x4_div"] = 1036519] = "f32x4_div";
+ OperatorCode[OperatorCode["f32x4_min"] = 1036520] = "f32x4_min";
+ OperatorCode[OperatorCode["f32x4_max"] = 1036521] = "f32x4_max";
+ OperatorCode[OperatorCode["f32x4_pmin"] = 1036522] = "f32x4_pmin";
+ OperatorCode[OperatorCode["f32x4_pmax"] = 1036523] = "f32x4_pmax";
+ OperatorCode[OperatorCode["f64x2_abs"] = 1036524] = "f64x2_abs";
+ OperatorCode[OperatorCode["f64x2_neg"] = 1036525] = "f64x2_neg";
+ OperatorCode[OperatorCode["f64x2_sqrt"] = 1036527] = "f64x2_sqrt";
+ OperatorCode[OperatorCode["f64x2_add"] = 1036528] = "f64x2_add";
+ OperatorCode[OperatorCode["f64x2_sub"] = 1036529] = "f64x2_sub";
+ OperatorCode[OperatorCode["f64x2_mul"] = 1036530] = "f64x2_mul";
+ OperatorCode[OperatorCode["f64x2_div"] = 1036531] = "f64x2_div";
+ OperatorCode[OperatorCode["f64x2_min"] = 1036532] = "f64x2_min";
+ OperatorCode[OperatorCode["f64x2_max"] = 1036533] = "f64x2_max";
+ OperatorCode[OperatorCode["f64x2_pmin"] = 1036534] = "f64x2_pmin";
+ OperatorCode[OperatorCode["f64x2_pmax"] = 1036535] = "f64x2_pmax";
+ OperatorCode[OperatorCode["i32x4_trunc_sat_f32x4_s"] = 1036536] = "i32x4_trunc_sat_f32x4_s";
+ OperatorCode[OperatorCode["i32x4_trunc_sat_f32x4_u"] = 1036537] = "i32x4_trunc_sat_f32x4_u";
+ OperatorCode[OperatorCode["f32x4_convert_i32x4_s"] = 1036538] = "f32x4_convert_i32x4_s";
+ OperatorCode[OperatorCode["f32x4_convert_i32x4_u"] = 1036539] = "f32x4_convert_i32x4_u";
+ OperatorCode[OperatorCode["i32x4_trunc_sat_f64x2_s_zero"] = 1036540] = "i32x4_trunc_sat_f64x2_s_zero";
+ OperatorCode[OperatorCode["i32x4_trunc_sat_f64x2_u_zero"] = 1036541] = "i32x4_trunc_sat_f64x2_u_zero";
+ OperatorCode[OperatorCode["f64x2_convert_low_i32x4_s"] = 1036542] = "f64x2_convert_low_i32x4_s";
+ OperatorCode[OperatorCode["f64x2_convert_low_i32x4_u"] = 1036543] = "f64x2_convert_low_i32x4_u";
+ // Relaxed SIMD
+ OperatorCode[OperatorCode["i8x16_relaxed_swizzle"] = 1036544] = "i8x16_relaxed_swizzle";
+ OperatorCode[OperatorCode["i32x4_relaxed_trunc_f32x4_s"] = 1036545] = "i32x4_relaxed_trunc_f32x4_s";
+ OperatorCode[OperatorCode["i32x4_relaxed_trunc_f32x4_u"] = 1036546] = "i32x4_relaxed_trunc_f32x4_u";
+ OperatorCode[OperatorCode["i32x4_relaxed_trunc_f64x2_s_zero"] = 1036547] = "i32x4_relaxed_trunc_f64x2_s_zero";
+ OperatorCode[OperatorCode["i32x4_relaxed_trunc_f64x2_u_zero"] = 1036548] = "i32x4_relaxed_trunc_f64x2_u_zero";
+ OperatorCode[OperatorCode["f32x4_relaxed_madd"] = 1036549] = "f32x4_relaxed_madd";
+ OperatorCode[OperatorCode["f32x4_relaxed_nmadd"] = 1036550] = "f32x4_relaxed_nmadd";
+ OperatorCode[OperatorCode["f64x2_relaxed_madd"] = 1036551] = "f64x2_relaxed_madd";
+ OperatorCode[OperatorCode["f64x2_relaxed_nmadd"] = 1036552] = "f64x2_relaxed_nmadd";
+ OperatorCode[OperatorCode["i8x16_relaxed_laneselect"] = 1036553] = "i8x16_relaxed_laneselect";
+ OperatorCode[OperatorCode["i16x8_relaxed_laneselect"] = 1036554] = "i16x8_relaxed_laneselect";
+ OperatorCode[OperatorCode["i32x4_relaxed_laneselect"] = 1036555] = "i32x4_relaxed_laneselect";
+ OperatorCode[OperatorCode["i64x2_relaxed_laneselect"] = 1036556] = "i64x2_relaxed_laneselect";
+ OperatorCode[OperatorCode["f32x4_relaxed_min"] = 1036557] = "f32x4_relaxed_min";
+ OperatorCode[OperatorCode["f32x4_relaxed_max"] = 1036558] = "f32x4_relaxed_max";
+ OperatorCode[OperatorCode["f64x2_relaxed_min"] = 1036559] = "f64x2_relaxed_min";
+ OperatorCode[OperatorCode["f64x2_relaxed_max"] = 1036560] = "f64x2_relaxed_max";
+ OperatorCode[OperatorCode["i16x8_relaxed_q15mulr_s"] = 1036561] = "i16x8_relaxed_q15mulr_s";
+ OperatorCode[OperatorCode["i16x8_dot_i8x16_i7x16_s"] = 1036562] = "i16x8_dot_i8x16_i7x16_s";
+ OperatorCode[OperatorCode["i32x4_dot_i8x16_i7x16_add_s"] = 1036563] = "i32x4_dot_i8x16_i7x16_add_s";
+ // GC proposal.
+ OperatorCode[OperatorCode["struct_new"] = 64256] = "struct_new";
+ OperatorCode[OperatorCode["struct_new_default"] = 64257] = "struct_new_default";
+ OperatorCode[OperatorCode["struct_get"] = 64258] = "struct_get";
+ OperatorCode[OperatorCode["struct_get_s"] = 64259] = "struct_get_s";
+ OperatorCode[OperatorCode["struct_get_u"] = 64260] = "struct_get_u";
+ OperatorCode[OperatorCode["struct_set"] = 64261] = "struct_set";
+ OperatorCode[OperatorCode["array_new"] = 64262] = "array_new";
+ OperatorCode[OperatorCode["array_new_default"] = 64263] = "array_new_default";
+ OperatorCode[OperatorCode["array_new_fixed"] = 64264] = "array_new_fixed";
+ OperatorCode[OperatorCode["array_new_data"] = 64265] = "array_new_data";
+ OperatorCode[OperatorCode["array_new_elem"] = 64266] = "array_new_elem";
+ OperatorCode[OperatorCode["array_get"] = 64267] = "array_get";
+ OperatorCode[OperatorCode["array_get_s"] = 64268] = "array_get_s";
+ OperatorCode[OperatorCode["array_get_u"] = 64269] = "array_get_u";
+ OperatorCode[OperatorCode["array_set"] = 64270] = "array_set";
+ OperatorCode[OperatorCode["array_len"] = 64271] = "array_len";
+ OperatorCode[OperatorCode["array_fill"] = 64272] = "array_fill";
+ OperatorCode[OperatorCode["array_copy"] = 64273] = "array_copy";
+ OperatorCode[OperatorCode["array_init_data"] = 64274] = "array_init_data";
+ OperatorCode[OperatorCode["array_init_elem"] = 64275] = "array_init_elem";
+ OperatorCode[OperatorCode["ref_test"] = 64276] = "ref_test";
+ OperatorCode[OperatorCode["ref_test_null"] = 64277] = "ref_test_null";
+ OperatorCode[OperatorCode["ref_cast"] = 64278] = "ref_cast";
+ OperatorCode[OperatorCode["ref_cast_null"] = 64279] = "ref_cast_null";
+ OperatorCode[OperatorCode["br_on_cast"] = 64280] = "br_on_cast";
+ OperatorCode[OperatorCode["br_on_cast_fail"] = 64281] = "br_on_cast_fail";
+ OperatorCode[OperatorCode["any_convert_extern"] = 64282] = "any_convert_extern";
+ OperatorCode[OperatorCode["extern_convert_any"] = 64283] = "extern_convert_any";
+ OperatorCode[OperatorCode["ref_i31"] = 64284] = "ref_i31";
+ OperatorCode[OperatorCode["i31_get_s"] = 64285] = "i31_get_s";
+ OperatorCode[OperatorCode["i31_get_u"] = 64286] = "i31_get_u";
+})(OperatorCode = exports.OperatorCode || (exports.OperatorCode = {}));
+exports.OperatorCodeNames = [
+ "unreachable",
+ "nop",
+ "block",
+ "loop",
+ "if",
+ "else",
+ "try",
+ "catch",
+ "throw",
+ "rethrow",
+ "unwind",
+ "end",
+ "br",
+ "br_if",
+ "br_table",
+ "return",
+ "call",
+ "call_indirect",
+ "return_call",
+ "return_call_indirect",
+ "call_ref",
+ "return_call_ref",
+ undefined,
+ "let",
+ "delegate",
+ "catch_all",
+ "drop",
+ "select",
+ "select",
+ undefined,
+ undefined,
+ undefined,
+ "local.get",
+ "local.set",
+ "local.tee",
+ "global.get",
+ "global.set",
+ "table.get",
+ "table.set",
+ undefined,
+ "i32.load",
+ "i64.load",
+ "f32.load",
+ "f64.load",
+ "i32.load8_s",
+ "i32.load8_u",
+ "i32.load16_s",
+ "i32.load16_u",
+ "i64.load8_s",
+ "i64.load8_u",
+ "i64.load16_s",
+ "i64.load16_u",
+ "i64.load32_s",
+ "i64.load32_u",
+ "i32.store",
+ "i64.store",
+ "f32.store",
+ "f64.store",
+ "i32.store8",
+ "i32.store16",
+ "i64.store8",
+ "i64.store16",
+ "i64.store32",
+ "memory.size",
+ "memory.grow",
+ "i32.const",
+ "i64.const",
+ "f32.const",
+ "f64.const",
+ "i32.eqz",
+ "i32.eq",
+ "i32.ne",
+ "i32.lt_s",
+ "i32.lt_u",
+ "i32.gt_s",
+ "i32.gt_u",
+ "i32.le_s",
+ "i32.le_u",
+ "i32.ge_s",
+ "i32.ge_u",
+ "i64.eqz",
+ "i64.eq",
+ "i64.ne",
+ "i64.lt_s",
+ "i64.lt_u",
+ "i64.gt_s",
+ "i64.gt_u",
+ "i64.le_s",
+ "i64.le_u",
+ "i64.ge_s",
+ "i64.ge_u",
+ "f32.eq",
+ "f32.ne",
+ "f32.lt",
+ "f32.gt",
+ "f32.le",
+ "f32.ge",
+ "f64.eq",
+ "f64.ne",
+ "f64.lt",
+ "f64.gt",
+ "f64.le",
+ "f64.ge",
+ "i32.clz",
+ "i32.ctz",
+ "i32.popcnt",
+ "i32.add",
+ "i32.sub",
+ "i32.mul",
+ "i32.div_s",
+ "i32.div_u",
+ "i32.rem_s",
+ "i32.rem_u",
+ "i32.and",
+ "i32.or",
+ "i32.xor",
+ "i32.shl",
+ "i32.shr_s",
+ "i32.shr_u",
+ "i32.rotl",
+ "i32.rotr",
+ "i64.clz",
+ "i64.ctz",
+ "i64.popcnt",
+ "i64.add",
+ "i64.sub",
+ "i64.mul",
+ "i64.div_s",
+ "i64.div_u",
+ "i64.rem_s",
+ "i64.rem_u",
+ "i64.and",
+ "i64.or",
+ "i64.xor",
+ "i64.shl",
+ "i64.shr_s",
+ "i64.shr_u",
+ "i64.rotl",
+ "i64.rotr",
+ "f32.abs",
+ "f32.neg",
+ "f32.ceil",
+ "f32.floor",
+ "f32.trunc",
+ "f32.nearest",
+ "f32.sqrt",
+ "f32.add",
+ "f32.sub",
+ "f32.mul",
+ "f32.div",
+ "f32.min",
+ "f32.max",
+ "f32.copysign",
+ "f64.abs",
+ "f64.neg",
+ "f64.ceil",
+ "f64.floor",
+ "f64.trunc",
+ "f64.nearest",
+ "f64.sqrt",
+ "f64.add",
+ "f64.sub",
+ "f64.mul",
+ "f64.div",
+ "f64.min",
+ "f64.max",
+ "f64.copysign",
+ "i32.wrap_i64",
+ "i32.trunc_f32_s",
+ "i32.trunc_f32_u",
+ "i32.trunc_f64_s",
+ "i32.trunc_f64_u",
+ "i64.extend_i32_s",
+ "i64.extend_i32_u",
+ "i64.trunc_f32_s",
+ "i64.trunc_f32_u",
+ "i64.trunc_f64_s",
+ "i64.trunc_f64_u",
+ "f32.convert_i32_s",
+ "f32.convert_i32_u",
+ "f32.convert_i64_s",
+ "f32.convert_i64_u",
+ "f32.demote_f64",
+ "f64.convert_i32_s",
+ "f64.convert_i32_u",
+ "f64.convert_i64_s",
+ "f64.convert_i64_u",
+ "f64.promote_f32",
+ "i32.reinterpret_f32",
+ "i64.reinterpret_f64",
+ "f32.reinterpret_i32",
+ "f64.reinterpret_i64",
+ "i32.extend8_s",
+ "i32.extend16_s",
+ "i64.extend8_s",
+ "i64.extend16_s",
+ "i64.extend32_s",
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ "ref.null",
+ "ref.is_null",
+ "ref.func",
+ "ref.eq",
+ "ref.as_non_null",
+ "br_on_null",
+ "br_on_non_null",
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+];
+[
+ "i32.trunc_sat_f32_s",
+ "i32.trunc_sat_f32_u",
+ "i32.trunc_sat_f64_s",
+ "i32.trunc_sat_f64_u",
+ "i64.trunc_sat_f32_s",
+ "i64.trunc_sat_f32_u",
+ "i64.trunc_sat_f64_s",
+ "i64.trunc_sat_f64_u",
+ "memory.init",
+ "data.drop",
+ "memory.copy",
+ "memory.fill",
+ "table.init",
+ "elem.drop",
+ "table.copy",
+ "table.grow",
+ "table.size",
+ "table.fill",
+].forEach(function (s, i) {
+ exports.OperatorCodeNames[0xfc00 | i] = s;
+});
+[
+ "v128.load",
+ "i16x8.load8x8_s",
+ "i16x8.load8x8_u",
+ "i32x4.load16x4_s",
+ "i32x4.load16x4_u",
+ "i64x2.load32x2_s",
+ "i64x2.load32x2_u",
+ "v8x16.load_splat",
+ "v16x8.load_splat",
+ "v32x4.load_splat",
+ "v64x2.load_splat",
+ "v128.store",
+ "v128.const",
+ "i8x16.shuffle",
+ "i8x16.swizzle",
+ "i8x16.splat",
+ "i16x8.splat",
+ "i32x4.splat",
+ "i64x2.splat",
+ "f32x4.splat",
+ "f64x2.splat",
+ "i8x16.extract_lane_s",
+ "i8x16.extract_lane_u",
+ "i8x16.replace_lane",
+ "i16x8.extract_lane_s",
+ "i16x8.extract_lane_u",
+ "i16x8.replace_lane",
+ "i32x4.extract_lane",
+ "i32x4.replace_lane",
+ "i64x2.extract_lane",
+ "i64x2.replace_lane",
+ "f32x4.extract_lane",
+ "f32x4.replace_lane",
+ "f64x2.extract_lane",
+ "f64x2.replace_lane",
+ "i8x16.eq",
+ "i8x16.ne",
+ "i8x16.lt_s",
+ "i8x16.lt_u",
+ "i8x16.gt_s",
+ "i8x16.gt_u",
+ "i8x16.le_s",
+ "i8x16.le_u",
+ "i8x16.ge_s",
+ "i8x16.ge_u",
+ "i16x8.eq",
+ "i16x8.ne",
+ "i16x8.lt_s",
+ "i16x8.lt_u",
+ "i16x8.gt_s",
+ "i16x8.gt_u",
+ "i16x8.le_s",
+ "i16x8.le_u",
+ "i16x8.ge_s",
+ "i16x8.ge_u",
+ "i32x4.eq",
+ "i32x4.ne",
+ "i32x4.lt_s",
+ "i32x4.lt_u",
+ "i32x4.gt_s",
+ "i32x4.gt_u",
+ "i32x4.le_s",
+ "i32x4.le_u",
+ "i32x4.ge_s",
+ "i32x4.ge_u",
+ "f32x4.eq",
+ "f32x4.ne",
+ "f32x4.lt",
+ "f32x4.gt",
+ "f32x4.le",
+ "f32x4.ge",
+ "f64x2.eq",
+ "f64x2.ne",
+ "f64x2.lt",
+ "f64x2.gt",
+ "f64x2.le",
+ "f64x2.ge",
+ "v128.not",
+ "v128.and",
+ "v128.andnot",
+ "v128.or",
+ "v128.xor",
+ "v128.bitselect",
+ "v128.any_true",
+ "v128.load8_lane",
+ "v128.load16_lane",
+ "v128.load32_lane",
+ "v128.load64_lane",
+ "v128.store8_lane",
+ "v128.store16_lane",
+ "v128.store32_lane",
+ "v128.store64_lane",
+ "v128.load32_zero",
+ "v128.load64_zero",
+ "f32x4.demote_f64x2_zero",
+ "f64x2.promote_low_f32x4",
+ "i8x16.abs",
+ "i8x16.neg",
+ "i8x16_popcnt",
+ "i8x16.all_true",
+ "i8x16.bitmask",
+ "i8x16.narrow_i16x8_s",
+ "i8x16.narrow_i16x8_u",
+ "f32x4.ceil",
+ "f32x4.floor",
+ "f32x4.trunc",
+ "f32x4.nearest",
+ "i8x16.shl",
+ "i8x16.shr_s",
+ "i8x16.shr_u",
+ "i8x16.add",
+ "i8x16.add_sat_s",
+ "i8x16.add_sat_u",
+ "i8x16.sub",
+ "i8x16.sub_sat_s",
+ "i8x16.sub_sat_u",
+ "f64x2.ceil",
+ "f64x2.floor",
+ "i8x16.min_s",
+ "i8x16.min_u",
+ "i8x16.max_s",
+ "i8x16.max_u",
+ "f64x2.trunc",
+ "i8x16.avgr_u",
+ "i16x8.extadd_pairwise_i8x16_s",
+ "i16x8.extadd_pairwise_i8x16_u",
+ "i32x4.extadd_pairwise_i16x8_s",
+ "i32x4.extadd_pairwise_i16x8_u",
+ "i16x8.abs",
+ "i16x8.neg",
+ "i16x8.q15mulr_sat_s",
+ "i16x8.all_true",
+ "i16x8.bitmask",
+ "i16x8.narrow_i32x4_s",
+ "i16x8.narrow_i32x4_u",
+ "i16x8.extend_low_i8x16_s",
+ "i16x8.extend_high_i8x16_s",
+ "i16x8.extend_low_i8x16_u",
+ "i16x8.extend_high_i8x16_u",
+ "i16x8.shl",
+ "i16x8.shr_s",
+ "i16x8.shr_u",
+ "i16x8.add",
+ "i16x8.add_sat_s",
+ "i16x8.add_sat_u",
+ "i16x8.sub",
+ "i16x8.sub_sat_s",
+ "i16x8.sub_sat_u",
+ "f64x2.nearest",
+ "i16x8.mul",
+ "i16x8.min_s",
+ "i16x8.min_u",
+ "i16x8.max_s",
+ "i16x8.max_u",
+ undefined,
+ "i16x8.avgr_u",
+ "i16x8.extmul_low_i8x16_s",
+ "i16x8.extmul_high_i8x16_s",
+ "i16x8.extmul_low_i8x16_u",
+ "i16x8.extmul_high_i8x16_u",
+ "i32x4.abs",
+ "i32x4.neg",
+ undefined,
+ "i32x4.all_true",
+ "i32x4.bitmask",
+ undefined,
+ undefined,
+ "i32x4.extend_low_i16x8_s",
+ "i32x4.extend_high_i16x8_s",
+ "i32x4.extend_low_i16x8_u",
+ "i32x4.extend_high_i16x8_u",
+ "i32x4.shl",
+ "i32x4.shr_s",
+ "i32x4.shr_u",
+ "i32x4.add",
+ undefined,
+ undefined,
+ "i32x4.sub",
+ undefined,
+ undefined,
+ undefined,
+ "i32x4.mul",
+ "i32x4.min_s",
+ "i32x4.min_u",
+ "i32x4.max_s",
+ "i32x4.max_u",
+ "i32x4.dot_i16x8_s",
+ undefined,
+ "i32x4.extmul_low_i16x8_s",
+ "i32x4.extmul_high_i16x8_s",
+ "i32x4.extmul_low_i16x8_u",
+ "i32x4.extmul_high_i16x8_u",
+ "i64x2.abs",
+ "i64x2.neg",
+ undefined,
+ "i64x2.all_true",
+ "i64x2.bitmask",
+ undefined,
+ undefined,
+ "i64x2.extend_low_i32x4_s",
+ "i64x2.extend_high_i32x4_s",
+ "i64x2.extend_low_i32x4_u",
+ "i64x2.extend_high_i32x4_u",
+ "i64x2.shl",
+ "i64x2.shr_s",
+ "i64x2.shr_u",
+ "i64x2.add",
+ undefined,
+ undefined,
+ "i64x2.sub",
+ undefined,
+ undefined,
+ undefined,
+ "i64x2.mul",
+ "i64x2.eq",
+ "i64x2.ne",
+ "i64x2.lt_s",
+ "i64x2.gt_s",
+ "i64x2.le_s",
+ "i64x2.ge_s",
+ "i64x2.extmul_low_i32x4_s",
+ "i64x2.extmul_high_i32x4_s",
+ "i64x2.extmul_low_i32x4_u",
+ "i64x2.extmul_high_i32x4_u",
+ "f32x4.abs",
+ "f32x4.neg",
+ undefined,
+ "f32x4.sqrt",
+ "f32x4.add",
+ "f32x4.sub",
+ "f32x4.mul",
+ "f32x4.div",
+ "f32x4.min",
+ "f32x4.max",
+ "f32x4.pmin",
+ "f32x4.pmax",
+ "f64x2.abs",
+ "f64x2.neg",
+ undefined,
+ "f64x2.sqrt",
+ "f64x2.add",
+ "f64x2.sub",
+ "f64x2.mul",
+ "f64x2.div",
+ "f64x2.min",
+ "f64x2.max",
+ "f64x2.pmin",
+ "f64x2.pmax",
+ "i32x4.trunc_sat_f32x4_s",
+ "i32x4.trunc_sat_f32x4_u",
+ "f32x4.convert_i32x4_s",
+ "f32x4.convert_i32x4_u",
+ "i32x4.trunc_sat_f64x2_s_zero",
+ "i32x4.trunc_sat_f64x2_u_zero",
+ "f64x2.convert_low_i32x4_s",
+ "f64x2.convert_low_i32x4_u",
+ "i8x16.relaxed_swizzle",
+ "i32x4.relaxed_trunc_f32x4_s",
+ "i32x4.relaxed_trunc_f32x4_u",
+ "i32x4.relaxed_trunc_f64x2_s_zero",
+ "i32x4.relaxed_trunc_f64x2_u_zero",
+ "f32x4.relaxed_madd",
+ "f32x4.relaxed_nmadd",
+ "f64x2.relaxed_madd",
+ "f64x2.relaxed_nmadd",
+ "i8x16.relaxed_laneselect",
+ "i16x8.relaxed_laneselect",
+ "i32x4.relaxed_laneselect",
+ "i64x2.relaxed_laneselect",
+ "f32x4.relaxed_min",
+ "f32x4.relaxed_max",
+ "f64x2.relaxed_min",
+ "f64x2.relaxed_max",
+ "i16x8.relaxed_q15mulr_s",
+ "i16x8.dot_i8x16_i7x16_s",
+ "i32x4.dot_i8x16_i7x16_add_s",
+].forEach(function (s, i) {
+ exports.OperatorCodeNames[0xfd000 | i] = s;
+});
+[
+ "memory.atomic.notify",
+ "memory.atomic.wait32",
+ "memory.atomic.wait64",
+ "atomic.fence",
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ "i32.atomic.load",
+ "i64.atomic.load",
+ "i32.atomic.load8_u",
+ "i32.atomic.load16_u",
+ "i64.atomic.load8_u",
+ "i64.atomic.load16_u",
+ "i64.atomic.load32_u",
+ "i32.atomic.store",
+ "i64.atomic.store",
+ "i32.atomic.store8",
+ "i32.atomic.store16",
+ "i64.atomic.store8",
+ "i64.atomic.store16",
+ "i64.atomic.store32",
+ "i32.atomic.rmw.add",
+ "i64.atomic.rmw.add",
+ "i32.atomic.rmw8.add_u",
+ "i32.atomic.rmw16.add_u",
+ "i64.atomic.rmw8.add_u",
+ "i64.atomic.rmw16.add_u",
+ "i64.atomic.rmw32.add_u",
+ "i32.atomic.rmw.sub",
+ "i64.atomic.rmw.sub",
+ "i32.atomic.rmw8.sub_u",
+ "i32.atomic.rmw16.sub_u",
+ "i64.atomic.rmw8.sub_u",
+ "i64.atomic.rmw16.sub_u",
+ "i64.atomic.rmw32.sub_u",
+ "i32.atomic.rmw.and",
+ "i64.atomic.rmw.and",
+ "i32.atomic.rmw8.and_u",
+ "i32.atomic.rmw16.and_u",
+ "i64.atomic.rmw8.and_u",
+ "i64.atomic.rmw16.and_u",
+ "i64.atomic.rmw32.and_u",
+ "i32.atomic.rmw.or",
+ "i64.atomic.rmw.or",
+ "i32.atomic.rmw8.or_u",
+ "i32.atomic.rmw16.or_u",
+ "i64.atomic.rmw8.or_u",
+ "i64.atomic.rmw16.or_u",
+ "i64.atomic.rmw32.or_u",
+ "i32.atomic.rmw.xor",
+ "i64.atomic.rmw.xor",
+ "i32.atomic.rmw8.xor_u",
+ "i32.atomic.rmw16.xor_u",
+ "i64.atomic.rmw8.xor_u",
+ "i64.atomic.rmw16.xor_u",
+ "i64.atomic.rmw32.xor_u",
+ "i32.atomic.rmw.xchg",
+ "i64.atomic.rmw.xchg",
+ "i32.atomic.rmw8.xchg_u",
+ "i32.atomic.rmw16.xchg_u",
+ "i64.atomic.rmw8.xchg_u",
+ "i64.atomic.rmw16.xchg_u",
+ "i64.atomic.rmw32.xchg_u",
+ "i32.atomic.rmw.cmpxchg",
+ "i64.atomic.rmw.cmpxchg",
+ "i32.atomic.rmw8.cmpxchg_u",
+ "i32.atomic.rmw16.cmpxchg_u",
+ "i64.atomic.rmw8.cmpxchg_u",
+ "i64.atomic.rmw16.cmpxchg_u",
+ "i64.atomic.rmw32.cmpxchg_u",
+].forEach(function (s, i) {
+ exports.OperatorCodeNames[0xfe00 | i] = s;
+});
+[
+ "struct.new",
+ "struct.new_default",
+ "struct.get",
+ "struct.get_s",
+ "struct.get_u",
+ "struct.set",
+ "array.new",
+ "array.new_default",
+ "array.new_fixed",
+ "array.new_data",
+ "array.new_elem",
+ "array.get",
+ "array.get_s",
+ "array.get_u",
+ "array.set",
+ "array.len",
+ "array.fill",
+ "array.copy",
+ "array.init_data",
+ "array.init_elem",
+ "ref.test",
+ "ref.test null",
+ "ref.cast",
+ "ref.cast null",
+ "br_on_cast",
+ "br_on_cast_fail",
+ "any.convert_extern",
+ "extern.convert_any",
+ "ref.i31",
+ "i31.get_s",
+ "i31.get_u",
+].forEach(function (s, i) {
+ exports.OperatorCodeNames[0xfb00 | i] = s;
+});
+var ExternalKind;
+(function (ExternalKind) {
+ ExternalKind[ExternalKind["Function"] = 0] = "Function";
+ ExternalKind[ExternalKind["Table"] = 1] = "Table";
+ ExternalKind[ExternalKind["Memory"] = 2] = "Memory";
+ ExternalKind[ExternalKind["Global"] = 3] = "Global";
+ ExternalKind[ExternalKind["Tag"] = 4] = "Tag";
+})(ExternalKind = exports.ExternalKind || (exports.ExternalKind = {}));
+var TypeKind;
+(function (TypeKind) {
+ TypeKind[TypeKind["unspecified"] = 0] = "unspecified";
+ TypeKind[TypeKind["i32"] = -1] = "i32";
+ TypeKind[TypeKind["i64"] = -2] = "i64";
+ TypeKind[TypeKind["f32"] = -3] = "f32";
+ TypeKind[TypeKind["f64"] = -4] = "f64";
+ TypeKind[TypeKind["v128"] = -5] = "v128";
+ TypeKind[TypeKind["i8"] = -8] = "i8";
+ TypeKind[TypeKind["i16"] = -9] = "i16";
+ TypeKind[TypeKind["nullfuncref"] = -13] = "nullfuncref";
+ TypeKind[TypeKind["nullref"] = -15] = "nullref";
+ TypeKind[TypeKind["nullexternref"] = -14] = "nullexternref";
+ TypeKind[TypeKind["funcref"] = -16] = "funcref";
+ TypeKind[TypeKind["externref"] = -17] = "externref";
+ TypeKind[TypeKind["anyref"] = -18] = "anyref";
+ TypeKind[TypeKind["eqref"] = -19] = "eqref";
+ TypeKind[TypeKind["i31ref"] = -20] = "i31ref";
+ TypeKind[TypeKind["structref"] = -21] = "structref";
+ TypeKind[TypeKind["arrayref"] = -22] = "arrayref";
+ TypeKind[TypeKind["ref"] = -28] = "ref";
+ TypeKind[TypeKind["ref_null"] = -29] = "ref_null";
+ TypeKind[TypeKind["func"] = -32] = "func";
+ TypeKind[TypeKind["struct"] = -33] = "struct";
+ TypeKind[TypeKind["array"] = -34] = "array";
+ TypeKind[TypeKind["subtype"] = -48] = "subtype";
+ TypeKind[TypeKind["subtype_final"] = -49] = "subtype_final";
+ TypeKind[TypeKind["rec_group"] = -50] = "rec_group";
+ TypeKind[TypeKind["empty_block_type"] = -64] = "empty_block_type";
+})(TypeKind = exports.TypeKind || (exports.TypeKind = {}));
+var FieldDef = /** @class */ (function () {
+ function FieldDef() {
+ }
+ return FieldDef;
+}());
+exports.FieldDef = FieldDef;
+var FuncDef = /** @class */ (function () {
+ function FuncDef() {
+ }
+ return FuncDef;
+}());
+exports.FuncDef = FuncDef;
+var Type = exports.Type = /** @class */ (function () {
+ function Type(code) {
+ this.code = code;
+ }
+ Object.defineProperty(Type.prototype, "isIndex", {
+ get: function () {
+ return this.code >= 0;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Type.prototype, "kind", {
+ get: function () {
+ return this.code >= 0 ? 0 /* TypeKind.unspecified */ : this.code;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(Type.prototype, "index", {
+ get: function () {
+ return this.code < 0 ? -1 : this.code;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ // Convenience singletons.
+ Type.funcref = new Type(-16 /* TypeKind.funcref */);
+ Type.externref = new Type(-17 /* TypeKind.externref */);
+ return Type;
+}());
+var RefType = /** @class */ (function (_super) {
+ __extends(RefType, _super);
+ function RefType(kind, ref_index) {
+ var _this = this;
+ if (kind != -28 /* TypeKind.ref */ && kind !== -29 /* TypeKind.ref_null */) {
+ throw new Error("Unexpected type kind: ".concat(kind, "}"));
+ }
+ _this = _super.call(this, kind) || this;
+ _this.ref_index = ref_index;
+ return _this;
+ }
+ Object.defineProperty(RefType.prototype, "isNullable", {
+ get: function () {
+ return this.kind == -29 /* TypeKind.ref_null */;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return RefType;
+}(Type));
+exports.RefType = RefType;
+var RelocType;
+(function (RelocType) {
+ RelocType[RelocType["FunctionIndex_LEB"] = 0] = "FunctionIndex_LEB";
+ RelocType[RelocType["TableIndex_SLEB"] = 1] = "TableIndex_SLEB";
+ RelocType[RelocType["TableIndex_I32"] = 2] = "TableIndex_I32";
+ RelocType[RelocType["GlobalAddr_LEB"] = 3] = "GlobalAddr_LEB";
+ RelocType[RelocType["GlobalAddr_SLEB"] = 4] = "GlobalAddr_SLEB";
+ RelocType[RelocType["GlobalAddr_I32"] = 5] = "GlobalAddr_I32";
+ RelocType[RelocType["TypeIndex_LEB"] = 6] = "TypeIndex_LEB";
+ RelocType[RelocType["GlobalIndex_LEB"] = 7] = "GlobalIndex_LEB";
+})(RelocType = exports.RelocType || (exports.RelocType = {}));
+var LinkingType;
+(function (LinkingType) {
+ LinkingType[LinkingType["StackPointer"] = 1] = "StackPointer";
+})(LinkingType = exports.LinkingType || (exports.LinkingType = {}));
+var NameType;
+(function (NameType) {
+ NameType[NameType["Module"] = 0] = "Module";
+ NameType[NameType["Function"] = 1] = "Function";
+ NameType[NameType["Local"] = 2] = "Local";
+ NameType[NameType["Label"] = 3] = "Label";
+ NameType[NameType["Type"] = 4] = "Type";
+ NameType[NameType["Table"] = 5] = "Table";
+ NameType[NameType["Memory"] = 6] = "Memory";
+ NameType[NameType["Global"] = 7] = "Global";
+ NameType[NameType["Elem"] = 8] = "Elem";
+ NameType[NameType["Data"] = 9] = "Data";
+ NameType[NameType["Field"] = 10] = "Field";
+ NameType[NameType["Tag"] = 11] = "Tag";
+})(NameType = exports.NameType || (exports.NameType = {}));
+var BinaryReaderState;
+(function (BinaryReaderState) {
+ BinaryReaderState[BinaryReaderState["ERROR"] = -1] = "ERROR";
+ BinaryReaderState[BinaryReaderState["INITIAL"] = 0] = "INITIAL";
+ BinaryReaderState[BinaryReaderState["BEGIN_WASM"] = 1] = "BEGIN_WASM";
+ BinaryReaderState[BinaryReaderState["END_WASM"] = 2] = "END_WASM";
+ BinaryReaderState[BinaryReaderState["BEGIN_SECTION"] = 3] = "BEGIN_SECTION";
+ BinaryReaderState[BinaryReaderState["END_SECTION"] = 4] = "END_SECTION";
+ BinaryReaderState[BinaryReaderState["SKIPPING_SECTION"] = 5] = "SKIPPING_SECTION";
+ BinaryReaderState[BinaryReaderState["READING_SECTION_RAW_DATA"] = 6] = "READING_SECTION_RAW_DATA";
+ BinaryReaderState[BinaryReaderState["SECTION_RAW_DATA"] = 7] = "SECTION_RAW_DATA";
+ BinaryReaderState[BinaryReaderState["TYPE_SECTION_ENTRY"] = 11] = "TYPE_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["IMPORT_SECTION_ENTRY"] = 12] = "IMPORT_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["FUNCTION_SECTION_ENTRY"] = 13] = "FUNCTION_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["TABLE_SECTION_ENTRY"] = 14] = "TABLE_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["MEMORY_SECTION_ENTRY"] = 15] = "MEMORY_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["GLOBAL_SECTION_ENTRY"] = 16] = "GLOBAL_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["EXPORT_SECTION_ENTRY"] = 17] = "EXPORT_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["DATA_SECTION_ENTRY"] = 18] = "DATA_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["NAME_SECTION_ENTRY"] = 19] = "NAME_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["ELEMENT_SECTION_ENTRY"] = 20] = "ELEMENT_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["LINKING_SECTION_ENTRY"] = 21] = "LINKING_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["START_SECTION_ENTRY"] = 22] = "START_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["TAG_SECTION_ENTRY"] = 23] = "TAG_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["BEGIN_INIT_EXPRESSION_BODY"] = 25] = "BEGIN_INIT_EXPRESSION_BODY";
+ BinaryReaderState[BinaryReaderState["INIT_EXPRESSION_OPERATOR"] = 26] = "INIT_EXPRESSION_OPERATOR";
+ BinaryReaderState[BinaryReaderState["END_INIT_EXPRESSION_BODY"] = 27] = "END_INIT_EXPRESSION_BODY";
+ BinaryReaderState[BinaryReaderState["BEGIN_FUNCTION_BODY"] = 28] = "BEGIN_FUNCTION_BODY";
+ BinaryReaderState[BinaryReaderState["READING_FUNCTION_HEADER"] = 29] = "READING_FUNCTION_HEADER";
+ BinaryReaderState[BinaryReaderState["CODE_OPERATOR"] = 30] = "CODE_OPERATOR";
+ BinaryReaderState[BinaryReaderState["END_FUNCTION_BODY"] = 31] = "END_FUNCTION_BODY";
+ BinaryReaderState[BinaryReaderState["SKIPPING_FUNCTION_BODY"] = 32] = "SKIPPING_FUNCTION_BODY";
+ BinaryReaderState[BinaryReaderState["BEGIN_ELEMENT_SECTION_ENTRY"] = 33] = "BEGIN_ELEMENT_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["ELEMENT_SECTION_ENTRY_BODY"] = 34] = "ELEMENT_SECTION_ENTRY_BODY";
+ BinaryReaderState[BinaryReaderState["END_ELEMENT_SECTION_ENTRY"] = 35] = "END_ELEMENT_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["BEGIN_DATA_SECTION_ENTRY"] = 36] = "BEGIN_DATA_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["DATA_SECTION_ENTRY_BODY"] = 37] = "DATA_SECTION_ENTRY_BODY";
+ BinaryReaderState[BinaryReaderState["END_DATA_SECTION_ENTRY"] = 38] = "END_DATA_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["BEGIN_GLOBAL_SECTION_ENTRY"] = 39] = "BEGIN_GLOBAL_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["END_GLOBAL_SECTION_ENTRY"] = 40] = "END_GLOBAL_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["RELOC_SECTION_HEADER"] = 41] = "RELOC_SECTION_HEADER";
+ BinaryReaderState[BinaryReaderState["RELOC_SECTION_ENTRY"] = 42] = "RELOC_SECTION_ENTRY";
+ BinaryReaderState[BinaryReaderState["SOURCE_MAPPING_URL"] = 43] = "SOURCE_MAPPING_URL";
+ BinaryReaderState[BinaryReaderState["BEGIN_OFFSET_EXPRESSION_BODY"] = 44] = "BEGIN_OFFSET_EXPRESSION_BODY";
+ BinaryReaderState[BinaryReaderState["OFFSET_EXPRESSION_OPERATOR"] = 45] = "OFFSET_EXPRESSION_OPERATOR";
+ BinaryReaderState[BinaryReaderState["END_OFFSET_EXPRESSION_BODY"] = 46] = "END_OFFSET_EXPRESSION_BODY";
+ BinaryReaderState[BinaryReaderState["BEGIN_REC_GROUP"] = 47] = "BEGIN_REC_GROUP";
+ BinaryReaderState[BinaryReaderState["END_REC_GROUP"] = 48] = "END_REC_GROUP";
+ BinaryReaderState[BinaryReaderState["DATA_COUNT_SECTION_ENTRY"] = 49] = "DATA_COUNT_SECTION_ENTRY";
+})(BinaryReaderState = exports.BinaryReaderState || (exports.BinaryReaderState = {}));
+var DataSegmentType;
+(function (DataSegmentType) {
+ DataSegmentType[DataSegmentType["Active"] = 0] = "Active";
+ DataSegmentType[DataSegmentType["Passive"] = 1] = "Passive";
+ DataSegmentType[DataSegmentType["ActiveWithMemoryIndex"] = 2] = "ActiveWithMemoryIndex";
+})(DataSegmentType || (DataSegmentType = {}));
+function isActiveDataSegmentType(segmentType) {
+ switch (segmentType) {
+ case 0 /* DataSegmentType.Active */:
+ case 2 /* DataSegmentType.ActiveWithMemoryIndex */:
+ return true;
+ default:
+ return false;
+ }
+}
+var DataMode;
+(function (DataMode) {
+ DataMode[DataMode["Active"] = 0] = "Active";
+ DataMode[DataMode["Passive"] = 1] = "Passive";
+})(DataMode = exports.DataMode || (exports.DataMode = {}));
+var ElementSegmentType;
+(function (ElementSegmentType) {
+ ElementSegmentType[ElementSegmentType["LegacyActiveFuncrefExternval"] = 0] = "LegacyActiveFuncrefExternval";
+ ElementSegmentType[ElementSegmentType["PassiveExternval"] = 1] = "PassiveExternval";
+ ElementSegmentType[ElementSegmentType["ActiveExternval"] = 2] = "ActiveExternval";
+ ElementSegmentType[ElementSegmentType["DeclaredExternval"] = 3] = "DeclaredExternval";
+ ElementSegmentType[ElementSegmentType["LegacyActiveFuncrefElemexpr"] = 4] = "LegacyActiveFuncrefElemexpr";
+ ElementSegmentType[ElementSegmentType["PassiveElemexpr"] = 5] = "PassiveElemexpr";
+ ElementSegmentType[ElementSegmentType["ActiveElemexpr"] = 6] = "ActiveElemexpr";
+ ElementSegmentType[ElementSegmentType["DeclaredElemexpr"] = 7] = "DeclaredElemexpr";
+})(ElementSegmentType || (ElementSegmentType = {}));
+function isActiveElementSegmentType(segmentType) {
+ switch (segmentType) {
+ case 0 /* ElementSegmentType.LegacyActiveFuncrefExternval */:
+ case 2 /* ElementSegmentType.ActiveExternval */:
+ case 4 /* ElementSegmentType.LegacyActiveFuncrefElemexpr */:
+ case 6 /* ElementSegmentType.ActiveElemexpr */:
+ return true;
+ default:
+ return false;
+ }
+}
+function isExternvalElementSegmentType(segmentType) {
+ switch (segmentType) {
+ case 0 /* ElementSegmentType.LegacyActiveFuncrefExternval */:
+ case 1 /* ElementSegmentType.PassiveExternval */:
+ case 2 /* ElementSegmentType.ActiveExternval */:
+ case 3 /* ElementSegmentType.DeclaredExternval */:
+ return true;
+ default:
+ return false;
+ }
+}
+var ElementMode;
+(function (ElementMode) {
+ ElementMode[ElementMode["Active"] = 0] = "Active";
+ ElementMode[ElementMode["Passive"] = 1] = "Passive";
+ ElementMode[ElementMode["Declarative"] = 2] = "Declarative";
+})(ElementMode = exports.ElementMode || (exports.ElementMode = {}));
+var DataRange = /** @class */ (function () {
+ function DataRange(start, end) {
+ this.start = start;
+ this.end = end;
+ }
+ DataRange.prototype.offset = function (delta) {
+ this.start += delta;
+ this.end += delta;
+ };
+ return DataRange;
+}());
+var TagAttribute;
+(function (TagAttribute) {
+ TagAttribute[TagAttribute["Exception"] = 0] = "Exception";
+})(TagAttribute = exports.TagAttribute || (exports.TagAttribute = {}));
+var Int64 = /** @class */ (function () {
+ function Int64(data) {
+ this._data = data || new Uint8Array(8);
+ }
+ Int64.prototype.toInt32 = function () {
+ return (this._data[0] |
+ (this._data[1] << 8) |
+ (this._data[2] << 16) |
+ (this._data[3] << 24));
+ };
+ Int64.prototype.toDouble = function () {
+ var power = 1;
+ var sum;
+ if (this._data[7] & 0x80) {
+ sum = -1;
+ for (var i = 0; i < 8; i++, power *= 256)
+ sum -= power * (0xff ^ this._data[i]);
+ }
+ else {
+ sum = 0;
+ for (var i = 0; i < 8; i++, power *= 256)
+ sum += power * this._data[i];
+ }
+ return sum;
+ };
+ Int64.prototype.toString = function () {
+ var low = (this._data[0] |
+ (this._data[1] << 8) |
+ (this._data[2] << 16) |
+ (this._data[3] << 24)) >>>
+ 0;
+ var high = (this._data[4] |
+ (this._data[5] << 8) |
+ (this._data[6] << 16) |
+ (this._data[7] << 24)) >>>
+ 0;
+ if (low === 0 && high === 0) {
+ return "0";
+ }
+ var sign = false;
+ if (high >> 31) {
+ high = 4294967296 - high;
+ if (low > 0) {
+ high--;
+ low = 4294967296 - low;
+ }
+ sign = true;
+ }
+ var buf = [];
+ while (high > 0) {
+ var t = (high % 10) * 4294967296 + low;
+ high = Math.floor(high / 10);
+ buf.unshift((t % 10).toString());
+ low = Math.floor(t / 10);
+ }
+ while (low > 0) {
+ buf.unshift((low % 10).toString());
+ low = Math.floor(low / 10);
+ }
+ if (sign)
+ buf.unshift("-");
+ return buf.join("");
+ };
+ Object.defineProperty(Int64.prototype, "data", {
+ get: function () {
+ return this._data;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ return Int64;
+}());
+exports.Int64 = Int64;
+var BinaryReader = /** @class */ (function () {
+ function BinaryReader() {
+ this._data = null;
+ this._pos = 0;
+ this._length = 0;
+ this._eof = false;
+ this.state = 0 /* BinaryReaderState.INITIAL */;
+ this.result = null;
+ this.error = null;
+ this._sectionEntriesLeft = 0;
+ this._sectionId = -1 /* SectionCode.Unknown */;
+ this._sectionRange = null;
+ this._functionRange = null;
+ this._segmentType = 0;
+ this._segmentEntriesLeft = 0;
+ this._recGroupTypesLeft = 0;
+ }
+ Object.defineProperty(BinaryReader.prototype, "data", {
+ get: function () {
+ return this._data;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BinaryReader.prototype, "position", {
+ get: function () {
+ return this._pos;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ Object.defineProperty(BinaryReader.prototype, "length", {
+ get: function () {
+ return this._length;
+ },
+ enumerable: false,
+ configurable: true
+ });
+ BinaryReader.prototype.setData = function (buffer, pos, length, eof) {
+ var posDelta = pos - this._pos;
+ this._data = new Uint8Array(buffer);
+ this._pos = pos;
+ this._length = length;
+ this._eof = eof === undefined ? true : eof;
+ if (this._sectionRange)
+ this._sectionRange.offset(posDelta);
+ if (this._functionRange)
+ this._functionRange.offset(posDelta);
+ };
+ BinaryReader.prototype.hasBytes = function (n) {
+ return this._pos + n <= this._length;
+ };
+ BinaryReader.prototype.hasMoreBytes = function () {
+ return this.hasBytes(1);
+ };
+ BinaryReader.prototype.readUint8 = function () {
+ return this._data[this._pos++];
+ };
+ BinaryReader.prototype.readInt32 = function () {
+ var b1 = this._data[this._pos++];
+ var b2 = this._data[this._pos++];
+ var b3 = this._data[this._pos++];
+ var b4 = this._data[this._pos++];
+ return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+ };
+ BinaryReader.prototype.readUint32 = function () {
+ return this.readInt32();
+ };
+ BinaryReader.prototype.peekInt32 = function () {
+ var b1 = this._data[this._pos];
+ var b2 = this._data[this._pos + 1];
+ var b3 = this._data[this._pos + 2];
+ var b4 = this._data[this._pos + 3];
+ return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
+ };
+ BinaryReader.prototype.hasVarIntBytes = function () {
+ var pos = this._pos;
+ while (pos < this._length) {
+ if ((this._data[pos++] & 0x80) == 0)
+ return true;
+ }
+ return false;
+ };
+ BinaryReader.prototype.readVarUint1 = function () {
+ return this.readUint8();
+ };
+ BinaryReader.prototype.readVarInt7 = function () {
+ return (this.readUint8() << 25) >> 25;
+ };
+ BinaryReader.prototype.readVarUint7 = function () {
+ return this.readUint8();
+ };
+ BinaryReader.prototype.readVarInt32 = function () {
+ var result = 0;
+ var shift = 0;
+ while (true) {
+ var byte = this.readUint8();
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ if ((byte & 0x80) === 0)
+ break;
+ }
+ if (shift >= 32)
+ return result;
+ var ashift = 32 - shift;
+ return (result << ashift) >> ashift;
+ };
+ BinaryReader.prototype.readVarUint32 = function () {
+ var result = 0;
+ var shift = 0;
+ while (true) {
+ var byte = this.readUint8();
+ result |= (byte & 0x7f) << shift;
+ shift += 7;
+ if ((byte & 0x80) === 0)
+ break;
+ }
+ return result >>> 0;
+ };
+ BinaryReader.prototype.readVarInt64 = function () {
+ var result = new Uint8Array(8);
+ var i = 0;
+ var c = 0;
+ var shift = 0;
+ while (true) {
+ var byte = this.readUint8();
+ c |= (byte & 0x7f) << shift;
+ shift += 7;
+ if (shift > 8) {
+ result[i++] = c & 0xff;
+ c >>= 8;
+ shift -= 8;
+ }
+ if ((byte & 0x80) === 0)
+ break;
+ }
+ var ashift = 32 - shift;
+ c = (c << ashift) >> ashift;
+ while (i < 8) {
+ result[i++] = c & 0xff;
+ c >>= 8;
+ }
+ return new Int64(result);
+ };
+ // Reads any "s33" (signed 33-bit integer) value correctly; no guarantees
+ // outside that range.
+ BinaryReader.prototype.readHeapType = function () {
+ var lsb = this.readUint8();
+ if (lsb & 0x80) {
+ // Has more data than one byte.
+ var tail = this.readVarInt32();
+ return (tail - 1) * 128 + lsb;
+ }
+ else {
+ return (lsb << 25) >> 25;
+ }
+ };
+ BinaryReader.prototype.readType = function () {
+ var kind = this.readHeapType();
+ if (kind >= 0) {
+ return new Type(kind);
+ }
+ switch (kind) {
+ case -29 /* TypeKind.ref_null */:
+ case -28 /* TypeKind.ref */:
+ var index = this.readHeapType();
+ return new RefType(kind, index);
+ case -1 /* TypeKind.i32 */:
+ case -2 /* TypeKind.i64 */:
+ case -3 /* TypeKind.f32 */:
+ case -4 /* TypeKind.f64 */:
+ case -5 /* TypeKind.v128 */:
+ case -8 /* TypeKind.i8 */:
+ case -9 /* TypeKind.i16 */:
+ case -16 /* TypeKind.funcref */:
+ case -17 /* TypeKind.externref */:
+ case -18 /* TypeKind.anyref */:
+ case -19 /* TypeKind.eqref */:
+ case -20 /* TypeKind.i31ref */:
+ case -14 /* TypeKind.nullexternref */:
+ case -13 /* TypeKind.nullfuncref */:
+ case -21 /* TypeKind.structref */:
+ case -22 /* TypeKind.arrayref */:
+ case -15 /* TypeKind.nullref */:
+ case -32 /* TypeKind.func */:
+ case -33 /* TypeKind.struct */:
+ case -34 /* TypeKind.array */:
+ case -48 /* TypeKind.subtype */:
+ case -50 /* TypeKind.rec_group */:
+ case -49 /* TypeKind.subtype_final */:
+ case -64 /* TypeKind.empty_block_type */:
+ return new Type(kind);
+ default:
+ throw new Error("Unknown type kind: ".concat(kind));
+ }
+ };
+ BinaryReader.prototype.readStringBytes = function () {
+ var length = this.readVarUint32();
+ return this.readBytes(length);
+ };
+ BinaryReader.prototype.readBytes = function (length) {
+ var result = this._data.subarray(this._pos, this._pos + length);
+ this._pos += length;
+ return new Uint8Array(result); // making a clone of the data
+ };
+ BinaryReader.prototype.skipBytes = function (length) {
+ this._pos += length;
+ };
+ BinaryReader.prototype.hasStringBytes = function () {
+ if (!this.hasVarIntBytes())
+ return false;
+ var pos = this._pos;
+ var length = this.readVarUint32();
+ var result = this.hasBytes(length);
+ this._pos = pos;
+ return result;
+ };
+ BinaryReader.prototype.hasSectionPayload = function () {
+ return this.hasBytes(this._sectionRange.end - this._pos);
+ };
+ BinaryReader.prototype.readFuncType = function () {
+ var paramCount = this.readVarUint32();
+ var paramTypes = new Array(paramCount);
+ for (var i = 0; i < paramCount; i++)
+ paramTypes[i] = this.readType();
+ var returnCount = this.readVarUint32();
+ var returnTypes = new Array(returnCount);
+ for (var i = 0; i < returnCount; i++)
+ returnTypes[i] = this.readType();
+ return {
+ form: -32 /* TypeKind.func */,
+ params: paramTypes,
+ returns: returnTypes,
+ };
+ };
+ BinaryReader.prototype.readBaseType = function () {
+ var form = this.readVarInt7();
+ switch (form) {
+ case -32 /* TypeKind.func */:
+ return this.readFuncType();
+ case -33 /* TypeKind.struct */:
+ return this.readStructType();
+ case -34 /* TypeKind.array */:
+ return this.readArrayType();
+ default:
+ throw new Error("Unknown type kind: ".concat(form));
+ }
+ };
+ BinaryReader.prototype.readSubtype = function (final) {
+ var supertypesCount = this.readVarUint32();
+ var supertypes = new Array(supertypesCount);
+ for (var i = 0; i < supertypesCount; i++) {
+ supertypes[i] = this.readHeapType();
+ }
+ var result = this.readBaseType();
+ result.supertypes = supertypes;
+ result.final = final;
+ return result;
+ };
+ BinaryReader.prototype.readStructType = function () {
+ var fieldCount = this.readVarUint32();
+ var fieldTypes = new Array(fieldCount);
+ var fieldMutabilities = new Array(fieldCount);
+ for (var i = 0; i < fieldCount; i++) {
+ fieldTypes[i] = this.readType();
+ fieldMutabilities[i] = !!this.readVarUint1();
+ }
+ return {
+ form: -33 /* TypeKind.struct */,
+ fields: fieldTypes,
+ mutabilities: fieldMutabilities,
+ };
+ };
+ BinaryReader.prototype.readArrayType = function () {
+ var elementType = this.readType();
+ var mutability = !!this.readVarUint1();
+ return {
+ form: -34 /* TypeKind.array */,
+ elementType: elementType,
+ mutability: mutability,
+ };
+ };
+ BinaryReader.prototype.readResizableLimits = function (maxPresent) {
+ var initial = this.readVarUint32();
+ var maximum;
+ if (maxPresent) {
+ maximum = this.readVarUint32();
+ }
+ return { initial: initial, maximum: maximum };
+ };
+ BinaryReader.prototype.readTableType = function () {
+ var elementType = this.readType();
+ var flags = this.readVarUint32();
+ var limits = this.readResizableLimits(!!(flags & 0x01));
+ return { elementType: elementType, limits: limits };
+ };
+ BinaryReader.prototype.readMemoryType = function () {
+ var flags = this.readVarUint32();
+ var shared = !!(flags & 0x02);
+ return {
+ limits: this.readResizableLimits(!!(flags & 0x01)),
+ shared: shared,
+ };
+ };
+ BinaryReader.prototype.readGlobalType = function () {
+ if (!this.hasVarIntBytes()) {
+ return null;
+ }
+ var pos = this._pos;
+ var contentType = this.readType();
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return null;
+ }
+ var mutability = this.readVarUint1();
+ return { contentType: contentType, mutability: mutability };
+ };
+ BinaryReader.prototype.readTagType = function () {
+ var attribute = this.readVarUint32();
+ var typeIndex = this.readVarUint32();
+ return {
+ attribute: attribute,
+ typeIndex: typeIndex,
+ };
+ };
+ BinaryReader.prototype.readTypeEntryCommon = function (form) {
+ switch (form) {
+ case -32 /* TypeKind.func */:
+ this.result = this.readFuncType();
+ break;
+ case -48 /* TypeKind.subtype */:
+ this.result = this.readSubtype(false);
+ break;
+ case -49 /* TypeKind.subtype_final */:
+ this.result = this.readSubtype(true);
+ break;
+ case -33 /* TypeKind.struct */:
+ this.result = this.readStructType();
+ break;
+ case -34 /* TypeKind.array */:
+ this.result = this.readArrayType();
+ break;
+ case -1 /* TypeKind.i32 */:
+ case -2 /* TypeKind.i64 */:
+ case -3 /* TypeKind.f32 */:
+ case -4 /* TypeKind.f64 */:
+ case -5 /* TypeKind.v128 */:
+ case -8 /* TypeKind.i8 */:
+ case -9 /* TypeKind.i16 */:
+ case -16 /* TypeKind.funcref */:
+ case -17 /* TypeKind.externref */:
+ case -18 /* TypeKind.anyref */:
+ case -19 /* TypeKind.eqref */:
+ this.result = {
+ form: form,
+ };
+ break;
+ default:
+ throw new Error("Unknown type kind: ".concat(form));
+ }
+ };
+ BinaryReader.prototype.readTypeEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ var form = this.readVarInt7();
+ if (form == -50 /* TypeKind.rec_group */) {
+ this.state = 47 /* BinaryReaderState.BEGIN_REC_GROUP */;
+ this.result = null;
+ this._recGroupTypesLeft = this.readVarUint32();
+ }
+ else {
+ this.state = 11 /* BinaryReaderState.TYPE_SECTION_ENTRY */;
+ this.readTypeEntryCommon(form);
+ this._sectionEntriesLeft--;
+ }
+ return true;
+ };
+ BinaryReader.prototype.readRecGroupEntry = function () {
+ if (this._recGroupTypesLeft === 0) {
+ this.state = 48 /* BinaryReaderState.END_REC_GROUP */;
+ this.result = null;
+ this._sectionEntriesLeft--;
+ this._recGroupTypesLeft = -1;
+ return true;
+ }
+ this.state = 11 /* BinaryReaderState.TYPE_SECTION_ENTRY */;
+ var form = this.readVarInt7();
+ this.readTypeEntryCommon(form);
+ this._recGroupTypesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readImportEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ this.state = 12 /* BinaryReaderState.IMPORT_SECTION_ENTRY */;
+ var module = this.readStringBytes();
+ var field = this.readStringBytes();
+ var kind = this.readUint8();
+ var funcTypeIndex;
+ var type;
+ switch (kind) {
+ case 0 /* ExternalKind.Function */:
+ funcTypeIndex = this.readVarUint32();
+ break;
+ case 1 /* ExternalKind.Table */:
+ type = this.readTableType();
+ break;
+ case 2 /* ExternalKind.Memory */:
+ type = this.readMemoryType();
+ break;
+ case 3 /* ExternalKind.Global */:
+ type = this.readGlobalType();
+ break;
+ case 4 /* ExternalKind.Tag */:
+ type = this.readTagType();
+ break;
+ }
+ this.result = {
+ module: module,
+ field: field,
+ kind: kind,
+ funcTypeIndex: funcTypeIndex,
+ type: type,
+ };
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readExportEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ var field = this.readStringBytes();
+ var kind = this.readUint8();
+ var index = this.readVarUint32();
+ this.state = 17 /* BinaryReaderState.EXPORT_SECTION_ENTRY */;
+ this.result = { field: field, kind: kind, index: index };
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readFunctionEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ var typeIndex = this.readVarUint32();
+ this.state = 13 /* BinaryReaderState.FUNCTION_SECTION_ENTRY */;
+ this.result = { typeIndex: typeIndex };
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readTableEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ this.state = 14 /* BinaryReaderState.TABLE_SECTION_ENTRY */;
+ this.result = this.readTableType();
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readMemoryEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ this.state = 15 /* BinaryReaderState.MEMORY_SECTION_ENTRY */;
+ this.result = this.readMemoryType();
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readTagEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ this.state = 23 /* BinaryReaderState.TAG_SECTION_ENTRY */;
+ this.result = this.readTagType();
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readGlobalEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ var globalType = this.readGlobalType();
+ if (!globalType) {
+ this.state = 16 /* BinaryReaderState.GLOBAL_SECTION_ENTRY */;
+ return false;
+ }
+ this.state = 39 /* BinaryReaderState.BEGIN_GLOBAL_SECTION_ENTRY */;
+ this.result = {
+ type: globalType,
+ };
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readElementEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ var pos = this._pos;
+ if (!this.hasMoreBytes()) {
+ this.state = 20 /* BinaryReaderState.ELEMENT_SECTION_ENTRY */;
+ return false;
+ }
+ var segmentType = this.readUint8();
+ var mode, tableIndex;
+ switch (segmentType) {
+ case 0 /* ElementSegmentType.LegacyActiveFuncrefExternval */:
+ case 4 /* ElementSegmentType.LegacyActiveFuncrefElemexpr */:
+ mode = 0 /* ElementMode.Active */;
+ tableIndex = 0;
+ break;
+ case 1 /* ElementSegmentType.PassiveExternval */:
+ case 5 /* ElementSegmentType.PassiveElemexpr */:
+ mode = 1 /* ElementMode.Passive */;
+ break;
+ case 2 /* ElementSegmentType.ActiveExternval */:
+ case 6 /* ElementSegmentType.ActiveElemexpr */:
+ mode = 0 /* ElementMode.Active */;
+ if (!this.hasVarIntBytes()) {
+ this.state = 20 /* BinaryReaderState.ELEMENT_SECTION_ENTRY */;
+ this._pos = pos;
+ return false;
+ }
+ tableIndex = this.readVarUint32();
+ break;
+ case 3 /* ElementSegmentType.DeclaredExternval */:
+ case 7 /* ElementSegmentType.DeclaredElemexpr */:
+ mode = 2 /* ElementMode.Declarative */;
+ break;
+ default:
+ throw new Error("Unsupported element segment type ".concat(segmentType));
+ }
+ this.state = 33 /* BinaryReaderState.BEGIN_ELEMENT_SECTION_ENTRY */;
+ this.result = { mode: mode, tableIndex: tableIndex };
+ this._sectionEntriesLeft--;
+ this._segmentType = segmentType;
+ return true;
+ };
+ BinaryReader.prototype.readElementEntryBody = function () {
+ var elementType = Type.funcref;
+ switch (this._segmentType) {
+ case 1 /* ElementSegmentType.PassiveExternval */:
+ case 2 /* ElementSegmentType.ActiveExternval */:
+ case 3 /* ElementSegmentType.DeclaredExternval */:
+ if (!this.hasMoreBytes())
+ return false;
+ // We just skip the 0x00 byte, the `elemkind` byte
+ // is reserved for future versions of WebAssembly.
+ this.skipBytes(1);
+ break;
+ case 5 /* ElementSegmentType.PassiveElemexpr */:
+ case 6 /* ElementSegmentType.ActiveElemexpr */:
+ case 7 /* ElementSegmentType.DeclaredElemexpr */:
+ if (!this.hasMoreBytes())
+ return false;
+ elementType = this.readType();
+ break;
+ case 0 /* ElementSegmentType.LegacyActiveFuncrefExternval */:
+ case 4 /* ElementSegmentType.LegacyActiveFuncrefElemexpr */:
+ // The element type is implicitly `funcref`.
+ break;
+ default:
+ throw new Error("Unsupported element segment type ".concat(this._segmentType));
+ }
+ this.state = 34 /* BinaryReaderState.ELEMENT_SECTION_ENTRY_BODY */;
+ this.result = { elementType: elementType };
+ return true;
+ };
+ BinaryReader.prototype.readDataEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ var pos = this._pos;
+ if (!this.hasVarIntBytes()) {
+ this.state = 18 /* BinaryReaderState.DATA_SECTION_ENTRY */;
+ return false;
+ }
+ var segmentType = this.readVarUint32();
+ var mode, memoryIndex;
+ switch (segmentType) {
+ case 0 /* DataSegmentType.Active */:
+ mode = 0 /* DataMode.Active */;
+ memoryIndex = 0;
+ break;
+ case 1 /* DataSegmentType.Passive */:
+ mode = 1 /* DataMode.Passive */;
+ break;
+ case 2 /* DataSegmentType.ActiveWithMemoryIndex */:
+ mode = 0 /* DataMode.Active */;
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ this.state = 18 /* BinaryReaderState.DATA_SECTION_ENTRY */;
+ return false;
+ }
+ memoryIndex = this.readVarUint32();
+ break;
+ default:
+ throw new Error("Unsupported data segment type ".concat(segmentType));
+ }
+ this.state = 36 /* BinaryReaderState.BEGIN_DATA_SECTION_ENTRY */;
+ this.result = { mode: mode, memoryIndex: memoryIndex };
+ this._sectionEntriesLeft--;
+ this._segmentType = segmentType;
+ return true;
+ };
+ BinaryReader.prototype.readDataCountEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ this.state = 49 /* BinaryReaderState.DATA_COUNT_SECTION_ENTRY */;
+ this.result = this.readVarUint32();
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readDataEntryBody = function () {
+ if (!this.hasStringBytes()) {
+ return false;
+ }
+ this.state = 37 /* BinaryReaderState.DATA_SECTION_ENTRY_BODY */;
+ this.result = {
+ data: this.readStringBytes(),
+ };
+ return true;
+ };
+ BinaryReader.prototype.readInitExpressionBody = function () {
+ this.state = 25 /* BinaryReaderState.BEGIN_INIT_EXPRESSION_BODY */;
+ this.result = null;
+ return true;
+ };
+ BinaryReader.prototype.readOffsetExpressionBody = function () {
+ this.state = 44 /* BinaryReaderState.BEGIN_OFFSET_EXPRESSION_BODY */;
+ this.result = null;
+ return true;
+ };
+ BinaryReader.prototype.readMemoryImmediate = function () {
+ var flags = this.readVarUint32();
+ var offset = this.readVarUint32();
+ return { flags: flags, offset: offset };
+ };
+ BinaryReader.prototype.readNameMap = function () {
+ var count = this.readVarUint32();
+ var result = [];
+ for (var i = 0; i < count; i++) {
+ var index = this.readVarUint32();
+ var name = this.readStringBytes();
+ result.push({ index: index, name: name });
+ }
+ return result;
+ };
+ BinaryReader.prototype.readNameEntry = function () {
+ var pos = this._pos;
+ if (pos >= this._sectionRange.end) {
+ this.skipSection();
+ return this.read();
+ }
+ if (!this.hasVarIntBytes())
+ return false;
+ var type = this.readVarUint7();
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ var payloadLength = this.readVarUint32();
+ if (!this.hasBytes(payloadLength)) {
+ this._pos = pos;
+ return false;
+ }
+ var result;
+ switch (type) {
+ case 0 /* NameType.Module */:
+ result = {
+ type: type,
+ moduleName: this.readStringBytes(),
+ };
+ break;
+ case 1 /* NameType.Function */:
+ case 11 /* NameType.Tag */:
+ case 4 /* NameType.Type */:
+ case 5 /* NameType.Table */:
+ case 6 /* NameType.Memory */:
+ case 7 /* NameType.Global */:
+ result = {
+ type: type,
+ names: this.readNameMap(),
+ };
+ break;
+ case 2 /* NameType.Local */:
+ var funcsLength = this.readVarUint32();
+ var funcs = [];
+ for (var i = 0; i < funcsLength; i++) {
+ var funcIndex = this.readVarUint32();
+ funcs.push({
+ index: funcIndex,
+ locals: this.readNameMap(),
+ });
+ }
+ result = {
+ type: type,
+ funcs: funcs,
+ };
+ break;
+ case 10 /* NameType.Field */:
+ var typesLength = this.readVarUint32();
+ var types = [];
+ for (var i = 0; i < typesLength; i++) {
+ var fieldIndex = this.readVarUint32();
+ types.push({
+ index: fieldIndex,
+ fields: this.readNameMap(),
+ });
+ }
+ result = {
+ type: type,
+ types: types,
+ };
+ break;
+ default:
+ // Skip this unknown name subsection (as per specification,
+ // custom section errors shouldn't cause Wasm parsing to fail).
+ this.skipBytes(payloadLength);
+ return this.read();
+ }
+ this.state = 19 /* BinaryReaderState.NAME_SECTION_ENTRY */;
+ this.result = result;
+ return true;
+ };
+ BinaryReader.prototype.readRelocHeader = function () {
+ // See https://github.com/WebAssembly/tool-conventions/blob/master/Linking.md
+ if (!this.hasVarIntBytes()) {
+ return false;
+ }
+ var pos = this._pos;
+ var sectionId = this.readVarUint7();
+ var sectionName;
+ if (sectionId === 0 /* SectionCode.Custom */) {
+ if (!this.hasStringBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ sectionName = this.readStringBytes();
+ }
+ this.state = 41 /* BinaryReaderState.RELOC_SECTION_HEADER */;
+ this.result = {
+ id: sectionId,
+ name: sectionName,
+ };
+ return true;
+ };
+ BinaryReader.prototype.readLinkingEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ if (!this.hasVarIntBytes())
+ return false;
+ var pos = this._pos;
+ var type = this.readVarUint32();
+ var index;
+ switch (type) {
+ case 1 /* LinkingType.StackPointer */:
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ index = this.readVarUint32();
+ break;
+ default:
+ this.error = new Error("Bad linking type: ".concat(type));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ this.state = 21 /* BinaryReaderState.LINKING_SECTION_ENTRY */;
+ this.result = { type: type, index: index };
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readSourceMappingURL = function () {
+ if (!this.hasStringBytes())
+ return false;
+ var url = this.readStringBytes();
+ this.state = 43 /* BinaryReaderState.SOURCE_MAPPING_URL */;
+ this.result = { url: url };
+ return true;
+ };
+ BinaryReader.prototype.readRelocEntry = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ if (!this.hasVarIntBytes())
+ return false;
+ var pos = this._pos;
+ var type = this.readVarUint7();
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ var offset = this.readVarUint32();
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ var index = this.readVarUint32();
+ var addend;
+ switch (type) {
+ case 0 /* RelocType.FunctionIndex_LEB */:
+ case 1 /* RelocType.TableIndex_SLEB */:
+ case 2 /* RelocType.TableIndex_I32 */:
+ case 6 /* RelocType.TypeIndex_LEB */:
+ case 7 /* RelocType.GlobalIndex_LEB */:
+ break;
+ case 3 /* RelocType.GlobalAddr_LEB */:
+ case 4 /* RelocType.GlobalAddr_SLEB */:
+ case 5 /* RelocType.GlobalAddr_I32 */:
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ addend = this.readVarUint32();
+ break;
+ default:
+ this.error = new Error("Bad relocation type: ".concat(type));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ this.state = 42 /* BinaryReaderState.RELOC_SECTION_ENTRY */;
+ this.result = {
+ type: type,
+ offset: offset,
+ index: index,
+ addend: addend,
+ };
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readCodeOperator_0xfb = function () {
+ // The longest instructions have: 2 bytes opcode, 5 bytes type index,
+ // 5 bytes field index.
+ var MAX_CODE_OPERATOR_0XFB_SIZE = 12;
+ if (!this._eof && !this.hasBytes(MAX_CODE_OPERATOR_0XFB_SIZE)) {
+ return false;
+ }
+ var code, brDepth, refType, srcType, fieldIndex, segmentIndex, len, literal;
+ code = this._data[this._pos++] | 0xfb00;
+ switch (code) {
+ case 64280 /* OperatorCode.br_on_cast */:
+ case 64281 /* OperatorCode.br_on_cast_fail */:
+ literal = this.readUint8();
+ brDepth = this.readVarUint32();
+ srcType = this.readHeapType();
+ refType = this.readHeapType();
+ break;
+ case 64267 /* OperatorCode.array_get */:
+ case 64268 /* OperatorCode.array_get_s */:
+ case 64269 /* OperatorCode.array_get_u */:
+ case 64270 /* OperatorCode.array_set */:
+ case 64262 /* OperatorCode.array_new */:
+ case 64263 /* OperatorCode.array_new_default */:
+ case 64256 /* OperatorCode.struct_new */:
+ case 64257 /* OperatorCode.struct_new_default */:
+ refType = this.readVarUint32();
+ break;
+ case 64264 /* OperatorCode.array_new_fixed */:
+ refType = this.readVarUint32();
+ len = this.readVarUint32();
+ break;
+ case 64272 /* OperatorCode.array_fill */:
+ refType = this.readVarUint32();
+ break;
+ case 64273 /* OperatorCode.array_copy */:
+ refType = this.readVarUint32();
+ srcType = this.readVarUint32();
+ break;
+ case 64258 /* OperatorCode.struct_get */:
+ case 64259 /* OperatorCode.struct_get_s */:
+ case 64260 /* OperatorCode.struct_get_u */:
+ case 64261 /* OperatorCode.struct_set */:
+ refType = this.readVarUint32();
+ fieldIndex = this.readVarUint32();
+ break;
+ case 64265 /* OperatorCode.array_new_data */:
+ case 64266 /* OperatorCode.array_new_elem */:
+ case 64274 /* OperatorCode.array_init_data */:
+ case 64275 /* OperatorCode.array_init_elem */:
+ refType = this.readVarUint32();
+ segmentIndex = this.readVarUint32();
+ break;
+ case 64276 /* OperatorCode.ref_test */:
+ case 64277 /* OperatorCode.ref_test_null */:
+ case 64278 /* OperatorCode.ref_cast */:
+ case 64279 /* OperatorCode.ref_cast_null */:
+ refType = this.readHeapType();
+ break;
+ case 64271 /* OperatorCode.array_len */:
+ case 64283 /* OperatorCode.extern_convert_any */:
+ case 64282 /* OperatorCode.any_convert_extern */:
+ case 64284 /* OperatorCode.ref_i31 */:
+ case 64285 /* OperatorCode.i31_get_s */:
+ case 64286 /* OperatorCode.i31_get_u */:
+ break;
+ default:
+ this.error = new Error("Unknown operator: 0x".concat(code.toString(16).padStart(4, "0")));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ this.result = {
+ code: code,
+ blockType: undefined,
+ refType: refType,
+ srcType: srcType,
+ brDepth: brDepth,
+ brTable: undefined,
+ tableIndex: undefined,
+ funcIndex: undefined,
+ typeIndex: undefined,
+ localIndex: undefined,
+ globalIndex: undefined,
+ fieldIndex: fieldIndex,
+ memoryAddress: undefined,
+ literal: literal,
+ segmentIndex: segmentIndex,
+ destinationIndex: undefined,
+ len: len,
+ lines: undefined,
+ lineIndex: undefined,
+ };
+ return true;
+ };
+ BinaryReader.prototype.readCodeOperator_0xfc = function () {
+ if (!this.hasVarIntBytes()) {
+ return false;
+ }
+ var code = this.readVarUint32() | 0xfc00;
+ var reserved, segmentIndex, destinationIndex, tableIndex;
+ switch (code) {
+ case 64512 /* OperatorCode.i32_trunc_sat_f32_s */:
+ case 64513 /* OperatorCode.i32_trunc_sat_f32_u */:
+ case 64514 /* OperatorCode.i32_trunc_sat_f64_s */:
+ case 64515 /* OperatorCode.i32_trunc_sat_f64_u */:
+ case 64516 /* OperatorCode.i64_trunc_sat_f32_s */:
+ case 64517 /* OperatorCode.i64_trunc_sat_f32_u */:
+ case 64518 /* OperatorCode.i64_trunc_sat_f64_s */:
+ case 64519 /* OperatorCode.i64_trunc_sat_f64_u */:
+ break;
+ case 64522 /* OperatorCode.memory_copy */:
+ // Currently memory index must be zero.
+ reserved = this.readVarUint1();
+ reserved = this.readVarUint1();
+ break;
+ case 64523 /* OperatorCode.memory_fill */:
+ reserved = this.readVarUint1();
+ break;
+ case 64524 /* OperatorCode.table_init */:
+ segmentIndex = this.readVarUint32();
+ tableIndex = this.readVarUint32();
+ break;
+ case 64526 /* OperatorCode.table_copy */:
+ tableIndex = this.readVarUint32();
+ destinationIndex = this.readVarUint32();
+ break;
+ case 64527 /* OperatorCode.table_grow */:
+ case 64528 /* OperatorCode.table_size */:
+ case 64529 /* OperatorCode.table_fill */:
+ tableIndex = this.readVarUint32();
+ break;
+ case 64520 /* OperatorCode.memory_init */:
+ segmentIndex = this.readVarUint32();
+ reserved = this.readVarUint1();
+ break;
+ case 64521 /* OperatorCode.data_drop */:
+ case 64525 /* OperatorCode.elem_drop */:
+ segmentIndex = this.readVarUint32();
+ break;
+ default:
+ this.error = new Error("Unknown operator: 0x".concat(code.toString(16).padStart(4, "0")));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ this.result = {
+ code: code,
+ blockType: undefined,
+ selectType: undefined,
+ refType: undefined,
+ srcType: undefined,
+ brDepth: undefined,
+ brTable: undefined,
+ funcIndex: undefined,
+ typeIndex: undefined,
+ tableIndex: tableIndex,
+ localIndex: undefined,
+ globalIndex: undefined,
+ fieldIndex: undefined,
+ memoryAddress: undefined,
+ literal: undefined,
+ segmentIndex: segmentIndex,
+ destinationIndex: destinationIndex,
+ len: undefined,
+ lines: undefined,
+ lineIndex: undefined,
+ };
+ return true;
+ };
+ BinaryReader.prototype.readCodeOperator_0xfd = function () {
+ var MAX_CODE_OPERATOR_0XFD_SIZE = 17;
+ var pos = this._pos;
+ if (!this._eof && pos + MAX_CODE_OPERATOR_0XFD_SIZE > this._length) {
+ return false;
+ }
+ if (!this.hasVarIntBytes()) {
+ return false;
+ }
+ var code = this.readVarUint32() | 0xfd000;
+ var memoryAddress;
+ var literal;
+ var lineIndex;
+ var lines;
+ switch (code) {
+ case 1036288 /* OperatorCode.v128_load */:
+ case 1036289 /* OperatorCode.i16x8_load8x8_s */:
+ case 1036290 /* OperatorCode.i16x8_load8x8_u */:
+ case 1036291 /* OperatorCode.i32x4_load16x4_s */:
+ case 1036292 /* OperatorCode.i32x4_load16x4_u */:
+ case 1036293 /* OperatorCode.i64x2_load32x2_s */:
+ case 1036294 /* OperatorCode.i64x2_load32x2_u */:
+ case 1036295 /* OperatorCode.v8x16_load_splat */:
+ case 1036296 /* OperatorCode.v16x8_load_splat */:
+ case 1036297 /* OperatorCode.v32x4_load_splat */:
+ case 1036298 /* OperatorCode.v64x2_load_splat */:
+ case 1036299 /* OperatorCode.v128_store */:
+ case 1036380 /* OperatorCode.v128_load32_zero */:
+ case 1036381 /* OperatorCode.v128_load64_zero */:
+ memoryAddress = this.readMemoryImmediate();
+ break;
+ case 1036300 /* OperatorCode.v128_const */:
+ literal = this.readBytes(16);
+ break;
+ case 1036301 /* OperatorCode.i8x16_shuffle */:
+ lines = new Uint8Array(16);
+ for (var i = 0; i < lines.length; i++) {
+ lines[i] = this.readUint8();
+ }
+ break;
+ case 1036309 /* OperatorCode.i8x16_extract_lane_s */:
+ case 1036310 /* OperatorCode.i8x16_extract_lane_u */:
+ case 1036311 /* OperatorCode.i8x16_replace_lane */:
+ case 1036312 /* OperatorCode.i16x8_extract_lane_s */:
+ case 1036313 /* OperatorCode.i16x8_extract_lane_u */:
+ case 1036314 /* OperatorCode.i16x8_replace_lane */:
+ case 1036315 /* OperatorCode.i32x4_extract_lane */:
+ case 1036316 /* OperatorCode.i32x4_replace_lane */:
+ case 1036317 /* OperatorCode.i64x2_extract_lane */:
+ case 1036318 /* OperatorCode.i64x2_replace_lane */:
+ case 1036319 /* OperatorCode.f32x4_extract_lane */:
+ case 1036320 /* OperatorCode.f32x4_replace_lane */:
+ case 1036321 /* OperatorCode.f64x2_extract_lane */:
+ case 1036322 /* OperatorCode.f64x2_replace_lane */:
+ lineIndex = this.readUint8();
+ break;
+ case 1036372 /* OperatorCode.v128_load8_lane */:
+ case 1036373 /* OperatorCode.v128_load16_lane */:
+ case 1036374 /* OperatorCode.v128_load32_lane */:
+ case 1036375 /* OperatorCode.v128_load64_lane */:
+ case 1036376 /* OperatorCode.v128_store8_lane */:
+ case 1036377 /* OperatorCode.v128_store16_lane */:
+ case 1036378 /* OperatorCode.v128_store32_lane */:
+ case 1036379 /* OperatorCode.v128_store64_lane */:
+ memoryAddress = this.readMemoryImmediate();
+ lineIndex = this.readUint8();
+ break;
+ case 1036302 /* OperatorCode.i8x16_swizzle */:
+ case 1036303 /* OperatorCode.i8x16_splat */:
+ case 1036304 /* OperatorCode.i16x8_splat */:
+ case 1036305 /* OperatorCode.i32x4_splat */:
+ case 1036306 /* OperatorCode.i64x2_splat */:
+ case 1036307 /* OperatorCode.f32x4_splat */:
+ case 1036308 /* OperatorCode.f64x2_splat */:
+ case 1036323 /* OperatorCode.i8x16_eq */:
+ case 1036324 /* OperatorCode.i8x16_ne */:
+ case 1036325 /* OperatorCode.i8x16_lt_s */:
+ case 1036326 /* OperatorCode.i8x16_lt_u */:
+ case 1036327 /* OperatorCode.i8x16_gt_s */:
+ case 1036328 /* OperatorCode.i8x16_gt_u */:
+ case 1036329 /* OperatorCode.i8x16_le_s */:
+ case 1036330 /* OperatorCode.i8x16_le_u */:
+ case 1036331 /* OperatorCode.i8x16_ge_s */:
+ case 1036332 /* OperatorCode.i8x16_ge_u */:
+ case 1036333 /* OperatorCode.i16x8_eq */:
+ case 1036334 /* OperatorCode.i16x8_ne */:
+ case 1036335 /* OperatorCode.i16x8_lt_s */:
+ case 1036336 /* OperatorCode.i16x8_lt_u */:
+ case 1036337 /* OperatorCode.i16x8_gt_s */:
+ case 1036338 /* OperatorCode.i16x8_gt_u */:
+ case 1036339 /* OperatorCode.i16x8_le_s */:
+ case 1036340 /* OperatorCode.i16x8_le_u */:
+ case 1036341 /* OperatorCode.i16x8_ge_s */:
+ case 1036342 /* OperatorCode.i16x8_ge_u */:
+ case 1036343 /* OperatorCode.i32x4_eq */:
+ case 1036344 /* OperatorCode.i32x4_ne */:
+ case 1036345 /* OperatorCode.i32x4_lt_s */:
+ case 1036346 /* OperatorCode.i32x4_lt_u */:
+ case 1036347 /* OperatorCode.i32x4_gt_s */:
+ case 1036348 /* OperatorCode.i32x4_gt_u */:
+ case 1036349 /* OperatorCode.i32x4_le_s */:
+ case 1036350 /* OperatorCode.i32x4_le_u */:
+ case 1036351 /* OperatorCode.i32x4_ge_s */:
+ case 1036352 /* OperatorCode.i32x4_ge_u */:
+ case 1036353 /* OperatorCode.f32x4_eq */:
+ case 1036354 /* OperatorCode.f32x4_ne */:
+ case 1036355 /* OperatorCode.f32x4_lt */:
+ case 1036356 /* OperatorCode.f32x4_gt */:
+ case 1036357 /* OperatorCode.f32x4_le */:
+ case 1036358 /* OperatorCode.f32x4_ge */:
+ case 1036359 /* OperatorCode.f64x2_eq */:
+ case 1036360 /* OperatorCode.f64x2_ne */:
+ case 1036361 /* OperatorCode.f64x2_lt */:
+ case 1036362 /* OperatorCode.f64x2_gt */:
+ case 1036363 /* OperatorCode.f64x2_le */:
+ case 1036364 /* OperatorCode.f64x2_ge */:
+ case 1036365 /* OperatorCode.v128_not */:
+ case 1036366 /* OperatorCode.v128_and */:
+ case 1036367 /* OperatorCode.v128_andnot */:
+ case 1036368 /* OperatorCode.v128_or */:
+ case 1036369 /* OperatorCode.v128_xor */:
+ case 1036370 /* OperatorCode.v128_bitselect */:
+ case 1036371 /* OperatorCode.v128_any_true */:
+ case 1036382 /* OperatorCode.f32x4_demote_f64x2_zero */:
+ case 1036383 /* OperatorCode.f64x2_promote_low_f32x4 */:
+ case 1036384 /* OperatorCode.i8x16_abs */:
+ case 1036385 /* OperatorCode.i8x16_neg */:
+ case 1036386 /* OperatorCode.i8x16_popcnt */:
+ case 1036387 /* OperatorCode.i8x16_all_true */:
+ case 1036388 /* OperatorCode.i8x16_bitmask */:
+ case 1036389 /* OperatorCode.i8x16_narrow_i16x8_s */:
+ case 1036390 /* OperatorCode.i8x16_narrow_i16x8_u */:
+ case 1036391 /* OperatorCode.f32x4_ceil */:
+ case 1036392 /* OperatorCode.f32x4_floor */:
+ case 1036393 /* OperatorCode.f32x4_trunc */:
+ case 1036394 /* OperatorCode.f32x4_nearest */:
+ case 1036395 /* OperatorCode.i8x16_shl */:
+ case 1036396 /* OperatorCode.i8x16_shr_s */:
+ case 1036397 /* OperatorCode.i8x16_shr_u */:
+ case 1036398 /* OperatorCode.i8x16_add */:
+ case 1036399 /* OperatorCode.i8x16_add_sat_s */:
+ case 1036400 /* OperatorCode.i8x16_add_sat_u */:
+ case 1036401 /* OperatorCode.i8x16_sub */:
+ case 1036402 /* OperatorCode.i8x16_sub_sat_s */:
+ case 1036403 /* OperatorCode.i8x16_sub_sat_u */:
+ case 1036404 /* OperatorCode.f64x2_ceil */:
+ case 1036405 /* OperatorCode.f64x2_floor */:
+ case 1036406 /* OperatorCode.i8x16_min_s */:
+ case 1036407 /* OperatorCode.i8x16_min_u */:
+ case 1036408 /* OperatorCode.i8x16_max_s */:
+ case 1036409 /* OperatorCode.i8x16_max_u */:
+ case 1036410 /* OperatorCode.f64x2_trunc */:
+ case 1036411 /* OperatorCode.i8x16_avgr_u */:
+ case 1036412 /* OperatorCode.i16x8_extadd_pairwise_i8x16_s */:
+ case 1036413 /* OperatorCode.i16x8_extadd_pairwise_i8x16_u */:
+ case 1036414 /* OperatorCode.i32x4_extadd_pairwise_i16x8_s */:
+ case 1036415 /* OperatorCode.i32x4_extadd_pairwise_i16x8_u */:
+ case 1036416 /* OperatorCode.i16x8_abs */:
+ case 1036417 /* OperatorCode.i16x8_neg */:
+ case 1036418 /* OperatorCode.i16x8_q15mulr_sat_s */:
+ case 1036419 /* OperatorCode.i16x8_all_true */:
+ case 1036420 /* OperatorCode.i16x8_bitmask */:
+ case 1036421 /* OperatorCode.i16x8_narrow_i32x4_s */:
+ case 1036422 /* OperatorCode.i16x8_narrow_i32x4_u */:
+ case 1036423 /* OperatorCode.i16x8_extend_low_i8x16_s */:
+ case 1036424 /* OperatorCode.i16x8_extend_high_i8x16_s */:
+ case 1036425 /* OperatorCode.i16x8_extend_low_i8x16_u */:
+ case 1036426 /* OperatorCode.i16x8_extend_high_i8x16_u */:
+ case 1036427 /* OperatorCode.i16x8_shl */:
+ case 1036428 /* OperatorCode.i16x8_shr_s */:
+ case 1036429 /* OperatorCode.i16x8_shr_u */:
+ case 1036430 /* OperatorCode.i16x8_add */:
+ case 1036431 /* OperatorCode.i16x8_add_sat_s */:
+ case 1036432 /* OperatorCode.i16x8_add_sat_u */:
+ case 1036433 /* OperatorCode.i16x8_sub */:
+ case 1036434 /* OperatorCode.i16x8_sub_sat_s */:
+ case 1036435 /* OperatorCode.i16x8_sub_sat_u */:
+ case 1036436 /* OperatorCode.f64x2_nearest */:
+ case 1036437 /* OperatorCode.i16x8_mul */:
+ case 1036438 /* OperatorCode.i16x8_min_s */:
+ case 1036439 /* OperatorCode.i16x8_min_u */:
+ case 1036440 /* OperatorCode.i16x8_max_s */:
+ case 1036441 /* OperatorCode.i16x8_max_u */:
+ case 1036443 /* OperatorCode.i16x8_avgr_u */:
+ case 1036444 /* OperatorCode.i16x8_extmul_low_i8x16_s */:
+ case 1036445 /* OperatorCode.i16x8_extmul_high_i8x16_s */:
+ case 1036446 /* OperatorCode.i16x8_extmul_low_i8x16_u */:
+ case 1036447 /* OperatorCode.i16x8_extmul_high_i8x16_u */:
+ case 1036448 /* OperatorCode.i32x4_abs */:
+ case 1036449 /* OperatorCode.i32x4_neg */:
+ case 1036451 /* OperatorCode.i32x4_all_true */:
+ case 1036452 /* OperatorCode.i32x4_bitmask */:
+ case 1036455 /* OperatorCode.i32x4_extend_low_i16x8_s */:
+ case 1036456 /* OperatorCode.i32x4_extend_high_i16x8_s */:
+ case 1036457 /* OperatorCode.i32x4_extend_low_i16x8_u */:
+ case 1036458 /* OperatorCode.i32x4_extend_high_i16x8_u */:
+ case 1036459 /* OperatorCode.i32x4_shl */:
+ case 1036460 /* OperatorCode.i32x4_shr_s */:
+ case 1036461 /* OperatorCode.i32x4_shr_u */:
+ case 1036462 /* OperatorCode.i32x4_add */:
+ case 1036465 /* OperatorCode.i32x4_sub */:
+ case 1036469 /* OperatorCode.i32x4_mul */:
+ case 1036470 /* OperatorCode.i32x4_min_s */:
+ case 1036471 /* OperatorCode.i32x4_min_u */:
+ case 1036472 /* OperatorCode.i32x4_max_s */:
+ case 1036473 /* OperatorCode.i32x4_max_u */:
+ case 1036474 /* OperatorCode.i32x4_dot_i16x8_s */:
+ case 1036476 /* OperatorCode.i32x4_extmul_low_i16x8_s */:
+ case 1036477 /* OperatorCode.i32x4_extmul_high_i16x8_s */:
+ case 1036478 /* OperatorCode.i32x4_extmul_low_i16x8_u */:
+ case 1036479 /* OperatorCode.i32x4_extmul_high_i16x8_u */:
+ case 1036480 /* OperatorCode.i64x2_abs */:
+ case 1036481 /* OperatorCode.i64x2_neg */:
+ case 1036483 /* OperatorCode.i64x2_all_true */:
+ case 1036484 /* OperatorCode.i64x2_bitmask */:
+ case 1036487 /* OperatorCode.i64x2_extend_low_i32x4_s */:
+ case 1036488 /* OperatorCode.i64x2_extend_high_i32x4_s */:
+ case 1036489 /* OperatorCode.i64x2_extend_low_i32x4_u */:
+ case 1036490 /* OperatorCode.i64x2_extend_high_i32x4_u */:
+ case 1036491 /* OperatorCode.i64x2_shl */:
+ case 1036492 /* OperatorCode.i64x2_shr_s */:
+ case 1036493 /* OperatorCode.i64x2_shr_u */:
+ case 1036494 /* OperatorCode.i64x2_add */:
+ case 1036497 /* OperatorCode.i64x2_sub */:
+ case 1036501 /* OperatorCode.i64x2_mul */:
+ case 1036502 /* OperatorCode.i64x2_eq */:
+ case 1036503 /* OperatorCode.i64x2_ne */:
+ case 1036504 /* OperatorCode.i64x2_lt_s */:
+ case 1036505 /* OperatorCode.i64x2_gt_s */:
+ case 1036506 /* OperatorCode.i64x2_le_s */:
+ case 1036507 /* OperatorCode.i64x2_ge_s */:
+ case 1036508 /* OperatorCode.i64x2_extmul_low_i32x4_s */:
+ case 1036509 /* OperatorCode.i64x2_extmul_high_i32x4_s */:
+ case 1036510 /* OperatorCode.i64x2_extmul_low_i32x4_u */:
+ case 1036511 /* OperatorCode.i64x2_extmul_high_i32x4_u */:
+ case 1036512 /* OperatorCode.f32x4_abs */:
+ case 1036512 /* OperatorCode.f32x4_abs */:
+ case 1036513 /* OperatorCode.f32x4_neg */:
+ case 1036515 /* OperatorCode.f32x4_sqrt */:
+ case 1036516 /* OperatorCode.f32x4_add */:
+ case 1036517 /* OperatorCode.f32x4_sub */:
+ case 1036518 /* OperatorCode.f32x4_mul */:
+ case 1036519 /* OperatorCode.f32x4_div */:
+ case 1036520 /* OperatorCode.f32x4_min */:
+ case 1036521 /* OperatorCode.f32x4_max */:
+ case 1036522 /* OperatorCode.f32x4_pmin */:
+ case 1036523 /* OperatorCode.f32x4_pmax */:
+ case 1036524 /* OperatorCode.f64x2_abs */:
+ case 1036525 /* OperatorCode.f64x2_neg */:
+ case 1036527 /* OperatorCode.f64x2_sqrt */:
+ case 1036528 /* OperatorCode.f64x2_add */:
+ case 1036529 /* OperatorCode.f64x2_sub */:
+ case 1036530 /* OperatorCode.f64x2_mul */:
+ case 1036531 /* OperatorCode.f64x2_div */:
+ case 1036532 /* OperatorCode.f64x2_min */:
+ case 1036533 /* OperatorCode.f64x2_max */:
+ case 1036534 /* OperatorCode.f64x2_pmin */:
+ case 1036535 /* OperatorCode.f64x2_pmax */:
+ case 1036536 /* OperatorCode.i32x4_trunc_sat_f32x4_s */:
+ case 1036537 /* OperatorCode.i32x4_trunc_sat_f32x4_u */:
+ case 1036538 /* OperatorCode.f32x4_convert_i32x4_s */:
+ case 1036539 /* OperatorCode.f32x4_convert_i32x4_u */:
+ case 1036540 /* OperatorCode.i32x4_trunc_sat_f64x2_s_zero */:
+ case 1036541 /* OperatorCode.i32x4_trunc_sat_f64x2_u_zero */:
+ case 1036542 /* OperatorCode.f64x2_convert_low_i32x4_s */:
+ case 1036543 /* OperatorCode.f64x2_convert_low_i32x4_u */:
+ break;
+ case 1036544 /* OperatorCode.i8x16_relaxed_swizzle */:
+ case 1036545 /* OperatorCode.i32x4_relaxed_trunc_f32x4_s */:
+ case 1036546 /* OperatorCode.i32x4_relaxed_trunc_f32x4_u */:
+ case 1036547 /* OperatorCode.i32x4_relaxed_trunc_f64x2_s_zero */:
+ case 1036548 /* OperatorCode.i32x4_relaxed_trunc_f64x2_u_zero */:
+ case 1036549 /* OperatorCode.f32x4_relaxed_madd */:
+ case 1036550 /* OperatorCode.f32x4_relaxed_nmadd */:
+ case 1036551 /* OperatorCode.f64x2_relaxed_madd */:
+ case 1036552 /* OperatorCode.f64x2_relaxed_nmadd */:
+ case 1036553 /* OperatorCode.i8x16_relaxed_laneselect */:
+ case 1036554 /* OperatorCode.i16x8_relaxed_laneselect */:
+ case 1036555 /* OperatorCode.i32x4_relaxed_laneselect */:
+ case 1036556 /* OperatorCode.i64x2_relaxed_laneselect */:
+ case 1036557 /* OperatorCode.f32x4_relaxed_min */:
+ case 1036558 /* OperatorCode.f32x4_relaxed_max */:
+ case 1036559 /* OperatorCode.f64x2_relaxed_min */:
+ case 1036560 /* OperatorCode.f64x2_relaxed_max */:
+ case 1036561 /* OperatorCode.i16x8_relaxed_q15mulr_s */:
+ case 1036562 /* OperatorCode.i16x8_dot_i8x16_i7x16_s */:
+ case 1036563 /* OperatorCode.i32x4_dot_i8x16_i7x16_add_s */:
+ break;
+ default:
+ this.error = new Error("Unknown operator: 0x".concat(code.toString(16).padStart(4, "0")));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ this.result = {
+ code: code,
+ blockType: undefined,
+ selectType: undefined,
+ refType: undefined,
+ srcType: undefined,
+ brDepth: undefined,
+ brTable: undefined,
+ funcIndex: undefined,
+ typeIndex: undefined,
+ localIndex: undefined,
+ globalIndex: undefined,
+ fieldIndex: undefined,
+ memoryAddress: memoryAddress,
+ literal: literal,
+ segmentIndex: undefined,
+ destinationIndex: undefined,
+ len: undefined,
+ lines: lines,
+ lineIndex: lineIndex,
+ };
+ return true;
+ };
+ BinaryReader.prototype.readCodeOperator_0xfe = function () {
+ var MAX_CODE_OPERATOR_0XFE_SIZE = 11;
+ var pos = this._pos;
+ if (!this._eof && pos + MAX_CODE_OPERATOR_0XFE_SIZE > this._length) {
+ return false;
+ }
+ if (!this.hasVarIntBytes()) {
+ return false;
+ }
+ var code = this.readVarUint32() | 0xfe00;
+ var memoryAddress;
+ switch (code) {
+ case 65024 /* OperatorCode.memory_atomic_notify */:
+ case 65025 /* OperatorCode.memory_atomic_wait32 */:
+ case 65026 /* OperatorCode.memory_atomic_wait64 */:
+ case 65040 /* OperatorCode.i32_atomic_load */:
+ case 65041 /* OperatorCode.i64_atomic_load */:
+ case 65042 /* OperatorCode.i32_atomic_load8_u */:
+ case 65043 /* OperatorCode.i32_atomic_load16_u */:
+ case 65044 /* OperatorCode.i64_atomic_load8_u */:
+ case 65045 /* OperatorCode.i64_atomic_load16_u */:
+ case 65046 /* OperatorCode.i64_atomic_load32_u */:
+ case 65047 /* OperatorCode.i32_atomic_store */:
+ case 65048 /* OperatorCode.i64_atomic_store */:
+ case 65049 /* OperatorCode.i32_atomic_store8 */:
+ case 65050 /* OperatorCode.i32_atomic_store16 */:
+ case 65051 /* OperatorCode.i64_atomic_store8 */:
+ case 65052 /* OperatorCode.i64_atomic_store16 */:
+ case 65053 /* OperatorCode.i64_atomic_store32 */:
+ case 65054 /* OperatorCode.i32_atomic_rmw_add */:
+ case 65055 /* OperatorCode.i64_atomic_rmw_add */:
+ case 65056 /* OperatorCode.i32_atomic_rmw8_add_u */:
+ case 65057 /* OperatorCode.i32_atomic_rmw16_add_u */:
+ case 65058 /* OperatorCode.i64_atomic_rmw8_add_u */:
+ case 65059 /* OperatorCode.i64_atomic_rmw16_add_u */:
+ case 65060 /* OperatorCode.i64_atomic_rmw32_add_u */:
+ case 65061 /* OperatorCode.i32_atomic_rmw_sub */:
+ case 65062 /* OperatorCode.i64_atomic_rmw_sub */:
+ case 65063 /* OperatorCode.i32_atomic_rmw8_sub_u */:
+ case 65064 /* OperatorCode.i32_atomic_rmw16_sub_u */:
+ case 65065 /* OperatorCode.i64_atomic_rmw8_sub_u */:
+ case 65066 /* OperatorCode.i64_atomic_rmw16_sub_u */:
+ case 65067 /* OperatorCode.i64_atomic_rmw32_sub_u */:
+ case 65068 /* OperatorCode.i32_atomic_rmw_and */:
+ case 65069 /* OperatorCode.i64_atomic_rmw_and */:
+ case 65070 /* OperatorCode.i32_atomic_rmw8_and_u */:
+ case 65071 /* OperatorCode.i32_atomic_rmw16_and_u */:
+ case 65072 /* OperatorCode.i64_atomic_rmw8_and_u */:
+ case 65073 /* OperatorCode.i64_atomic_rmw16_and_u */:
+ case 65074 /* OperatorCode.i64_atomic_rmw32_and_u */:
+ case 65075 /* OperatorCode.i32_atomic_rmw_or */:
+ case 65076 /* OperatorCode.i64_atomic_rmw_or */:
+ case 65077 /* OperatorCode.i32_atomic_rmw8_or_u */:
+ case 65078 /* OperatorCode.i32_atomic_rmw16_or_u */:
+ case 65079 /* OperatorCode.i64_atomic_rmw8_or_u */:
+ case 65080 /* OperatorCode.i64_atomic_rmw16_or_u */:
+ case 65081 /* OperatorCode.i64_atomic_rmw32_or_u */:
+ case 65082 /* OperatorCode.i32_atomic_rmw_xor */:
+ case 65083 /* OperatorCode.i64_atomic_rmw_xor */:
+ case 65084 /* OperatorCode.i32_atomic_rmw8_xor_u */:
+ case 65085 /* OperatorCode.i32_atomic_rmw16_xor_u */:
+ case 65086 /* OperatorCode.i64_atomic_rmw8_xor_u */:
+ case 65087 /* OperatorCode.i64_atomic_rmw16_xor_u */:
+ case 65088 /* OperatorCode.i64_atomic_rmw32_xor_u */:
+ case 65089 /* OperatorCode.i32_atomic_rmw_xchg */:
+ case 65090 /* OperatorCode.i64_atomic_rmw_xchg */:
+ case 65091 /* OperatorCode.i32_atomic_rmw8_xchg_u */:
+ case 65092 /* OperatorCode.i32_atomic_rmw16_xchg_u */:
+ case 65093 /* OperatorCode.i64_atomic_rmw8_xchg_u */:
+ case 65094 /* OperatorCode.i64_atomic_rmw16_xchg_u */:
+ case 65095 /* OperatorCode.i64_atomic_rmw32_xchg_u */:
+ case 65096 /* OperatorCode.i32_atomic_rmw_cmpxchg */:
+ case 65097 /* OperatorCode.i64_atomic_rmw_cmpxchg */:
+ case 65098 /* OperatorCode.i32_atomic_rmw8_cmpxchg_u */:
+ case 65099 /* OperatorCode.i32_atomic_rmw16_cmpxchg_u */:
+ case 65100 /* OperatorCode.i64_atomic_rmw8_cmpxchg_u */:
+ case 65101 /* OperatorCode.i64_atomic_rmw16_cmpxchg_u */:
+ case 65102 /* OperatorCode.i64_atomic_rmw32_cmpxchg_u */:
+ memoryAddress = this.readMemoryImmediate();
+ break;
+ case 65027 /* OperatorCode.atomic_fence */: {
+ var consistency_model = this.readUint8();
+ if (consistency_model != 0) {
+ this.error = new Error("atomic.fence consistency model must be 0");
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ break;
+ }
+ default:
+ this.error = new Error("Unknown operator: 0x".concat(code.toString(16).padStart(4, "0")));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ this.result = {
+ code: code,
+ blockType: undefined,
+ selectType: undefined,
+ refType: undefined,
+ srcType: undefined,
+ brDepth: undefined,
+ brTable: undefined,
+ funcIndex: undefined,
+ typeIndex: undefined,
+ localIndex: undefined,
+ globalIndex: undefined,
+ fieldIndex: undefined,
+ memoryAddress: memoryAddress,
+ literal: undefined,
+ segmentIndex: undefined,
+ destinationIndex: undefined,
+ len: undefined,
+ lines: undefined,
+ lineIndex: undefined,
+ };
+ return true;
+ };
+ BinaryReader.prototype.readCodeOperator = function () {
+ switch (this.state) {
+ case 30 /* BinaryReaderState.CODE_OPERATOR */:
+ if (this._pos >= this._functionRange.end) {
+ this.skipFunctionBody();
+ return this.read();
+ }
+ break;
+ case 26 /* BinaryReaderState.INIT_EXPRESSION_OPERATOR */:
+ if (this.result &&
+ this.result.code === 11 /* OperatorCode.end */) {
+ this.state = 27 /* BinaryReaderState.END_INIT_EXPRESSION_BODY */;
+ this.result = null;
+ return true;
+ }
+ break;
+ case 45 /* BinaryReaderState.OFFSET_EXPRESSION_OPERATOR */:
+ if (this.result &&
+ this.result.code === 11 /* OperatorCode.end */) {
+ this.state = 46 /* BinaryReaderState.END_OFFSET_EXPRESSION_BODY */;
+ this.result = null;
+ return true;
+ }
+ break;
+ }
+ var code, blockType, selectType, refType, brDepth, brTable, relativeDepth, funcIndex, typeIndex, tableIndex, localIndex, globalIndex, tagIndex, memoryAddress, literal, reserved;
+ if (this.state === 26 /* BinaryReaderState.INIT_EXPRESSION_OPERATOR */ &&
+ this._sectionId === 9 /* SectionCode.Element */ &&
+ isExternvalElementSegmentType(this._segmentType)) {
+ // We are reading a `vec(funcidx)` here, which is a dense encoding
+ // for a sequence of `((ref.func y) end)` instructions.
+ if (this.result &&
+ this.result.code === 210 /* OperatorCode.ref_func */) {
+ code = 11 /* OperatorCode.end */;
+ }
+ else {
+ if (!this.hasVarIntBytes())
+ return false;
+ code = 210 /* OperatorCode.ref_func */;
+ funcIndex = this.readVarUint32();
+ }
+ }
+ else {
+ var MAX_CODE_OPERATOR_SIZE = 11; // i64.const or load/store
+ var pos = this._pos;
+ if (!this._eof && pos + MAX_CODE_OPERATOR_SIZE > this._length) {
+ return false;
+ }
+ code = this._data[this._pos++];
+ switch (code) {
+ case 2 /* OperatorCode.block */:
+ case 3 /* OperatorCode.loop */:
+ case 4 /* OperatorCode.if */:
+ case 6 /* OperatorCode.try */:
+ blockType = this.readType();
+ break;
+ case 12 /* OperatorCode.br */:
+ case 13 /* OperatorCode.br_if */:
+ case 213 /* OperatorCode.br_on_null */:
+ case 214 /* OperatorCode.br_on_non_null */:
+ brDepth = this.readVarUint32();
+ break;
+ case 14 /* OperatorCode.br_table */:
+ var tableCount = this.readVarUint32();
+ if (!this.hasBytes(tableCount + 1)) {
+ // We need at least (tableCount + 1) bytes
+ this._pos = pos;
+ return false;
+ }
+ brTable = [];
+ for (var i = 0; i <= tableCount; i++) {
+ // including default
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ brTable.push(this.readVarUint32());
+ }
+ break;
+ case 9 /* OperatorCode.rethrow */:
+ case 24 /* OperatorCode.delegate */:
+ relativeDepth = this.readVarUint32();
+ break;
+ case 7 /* OperatorCode.catch */:
+ case 8 /* OperatorCode.throw */:
+ tagIndex = this.readVarInt32();
+ break;
+ case 208 /* OperatorCode.ref_null */:
+ refType = this.readHeapType();
+ break;
+ case 16 /* OperatorCode.call */:
+ case 18 /* OperatorCode.return_call */:
+ case 210 /* OperatorCode.ref_func */:
+ funcIndex = this.readVarUint32();
+ break;
+ case 17 /* OperatorCode.call_indirect */:
+ case 19 /* OperatorCode.return_call_indirect */:
+ typeIndex = this.readVarUint32();
+ reserved = this.readVarUint1();
+ break;
+ case 32 /* OperatorCode.local_get */:
+ case 33 /* OperatorCode.local_set */:
+ case 34 /* OperatorCode.local_tee */:
+ localIndex = this.readVarUint32();
+ break;
+ case 35 /* OperatorCode.global_get */:
+ case 36 /* OperatorCode.global_set */:
+ globalIndex = this.readVarUint32();
+ break;
+ case 37 /* OperatorCode.table_get */:
+ case 38 /* OperatorCode.table_set */:
+ tableIndex = this.readVarUint32();
+ break;
+ case 20 /* OperatorCode.call_ref */:
+ case 21 /* OperatorCode.return_call_ref */:
+ typeIndex = this.readHeapType();
+ break;
+ case 40 /* OperatorCode.i32_load */:
+ case 41 /* OperatorCode.i64_load */:
+ case 42 /* OperatorCode.f32_load */:
+ case 43 /* OperatorCode.f64_load */:
+ case 44 /* OperatorCode.i32_load8_s */:
+ case 45 /* OperatorCode.i32_load8_u */:
+ case 46 /* OperatorCode.i32_load16_s */:
+ case 47 /* OperatorCode.i32_load16_u */:
+ case 48 /* OperatorCode.i64_load8_s */:
+ case 49 /* OperatorCode.i64_load8_u */:
+ case 50 /* OperatorCode.i64_load16_s */:
+ case 51 /* OperatorCode.i64_load16_u */:
+ case 52 /* OperatorCode.i64_load32_s */:
+ case 53 /* OperatorCode.i64_load32_u */:
+ case 54 /* OperatorCode.i32_store */:
+ case 55 /* OperatorCode.i64_store */:
+ case 56 /* OperatorCode.f32_store */:
+ case 57 /* OperatorCode.f64_store */:
+ case 58 /* OperatorCode.i32_store8 */:
+ case 59 /* OperatorCode.i32_store16 */:
+ case 60 /* OperatorCode.i64_store8 */:
+ case 61 /* OperatorCode.i64_store16 */:
+ case 62 /* OperatorCode.i64_store32 */:
+ memoryAddress = this.readMemoryImmediate();
+ break;
+ case 63 /* OperatorCode.memory_size */:
+ case 64 /* OperatorCode.memory_grow */:
+ reserved = this.readVarUint1();
+ break;
+ case 65 /* OperatorCode.i32_const */:
+ literal = this.readVarInt32();
+ break;
+ case 66 /* OperatorCode.i64_const */:
+ literal = this.readVarInt64();
+ break;
+ case 67 /* OperatorCode.f32_const */:
+ literal = new DataView(this._data.buffer, this._data.byteOffset).getFloat32(this._pos, true);
+ this._pos += 4;
+ break;
+ case 68 /* OperatorCode.f64_const */:
+ literal = new DataView(this._data.buffer, this._data.byteOffset).getFloat64(this._pos, true);
+ this._pos += 8;
+ break;
+ case 28 /* OperatorCode.select_with_type */:
+ var num_types = this.readVarInt32();
+ // Only 1 is a valid value currently.
+ if (num_types == 1) {
+ selectType = this.readType();
+ }
+ break;
+ case 251 /* OperatorCode.prefix_0xfb */:
+ if (this.readCodeOperator_0xfb()) {
+ return true;
+ }
+ this._pos = pos;
+ return false;
+ case 252 /* OperatorCode.prefix_0xfc */:
+ if (this.readCodeOperator_0xfc()) {
+ return true;
+ }
+ this._pos = pos;
+ return false;
+ case 253 /* OperatorCode.prefix_0xfd */:
+ if (this.readCodeOperator_0xfd()) {
+ return true;
+ }
+ this._pos = pos;
+ return false;
+ case 254 /* OperatorCode.prefix_0xfe */:
+ if (this.readCodeOperator_0xfe()) {
+ return true;
+ }
+ this._pos = pos;
+ return false;
+ case 0 /* OperatorCode.unreachable */:
+ case 1 /* OperatorCode.nop */:
+ case 5 /* OperatorCode.else */:
+ case 10 /* OperatorCode.unwind */:
+ case 11 /* OperatorCode.end */:
+ case 15 /* OperatorCode.return */:
+ case 25 /* OperatorCode.catch_all */:
+ case 26 /* OperatorCode.drop */:
+ case 27 /* OperatorCode.select */:
+ case 69 /* OperatorCode.i32_eqz */:
+ case 70 /* OperatorCode.i32_eq */:
+ case 71 /* OperatorCode.i32_ne */:
+ case 72 /* OperatorCode.i32_lt_s */:
+ case 73 /* OperatorCode.i32_lt_u */:
+ case 74 /* OperatorCode.i32_gt_s */:
+ case 75 /* OperatorCode.i32_gt_u */:
+ case 76 /* OperatorCode.i32_le_s */:
+ case 77 /* OperatorCode.i32_le_u */:
+ case 78 /* OperatorCode.i32_ge_s */:
+ case 79 /* OperatorCode.i32_ge_u */:
+ case 80 /* OperatorCode.i64_eqz */:
+ case 81 /* OperatorCode.i64_eq */:
+ case 82 /* OperatorCode.i64_ne */:
+ case 83 /* OperatorCode.i64_lt_s */:
+ case 84 /* OperatorCode.i64_lt_u */:
+ case 85 /* OperatorCode.i64_gt_s */:
+ case 86 /* OperatorCode.i64_gt_u */:
+ case 87 /* OperatorCode.i64_le_s */:
+ case 88 /* OperatorCode.i64_le_u */:
+ case 89 /* OperatorCode.i64_ge_s */:
+ case 90 /* OperatorCode.i64_ge_u */:
+ case 91 /* OperatorCode.f32_eq */:
+ case 92 /* OperatorCode.f32_ne */:
+ case 93 /* OperatorCode.f32_lt */:
+ case 94 /* OperatorCode.f32_gt */:
+ case 95 /* OperatorCode.f32_le */:
+ case 96 /* OperatorCode.f32_ge */:
+ case 97 /* OperatorCode.f64_eq */:
+ case 98 /* OperatorCode.f64_ne */:
+ case 99 /* OperatorCode.f64_lt */:
+ case 100 /* OperatorCode.f64_gt */:
+ case 101 /* OperatorCode.f64_le */:
+ case 102 /* OperatorCode.f64_ge */:
+ case 103 /* OperatorCode.i32_clz */:
+ case 104 /* OperatorCode.i32_ctz */:
+ case 105 /* OperatorCode.i32_popcnt */:
+ case 106 /* OperatorCode.i32_add */:
+ case 107 /* OperatorCode.i32_sub */:
+ case 108 /* OperatorCode.i32_mul */:
+ case 109 /* OperatorCode.i32_div_s */:
+ case 110 /* OperatorCode.i32_div_u */:
+ case 111 /* OperatorCode.i32_rem_s */:
+ case 112 /* OperatorCode.i32_rem_u */:
+ case 113 /* OperatorCode.i32_and */:
+ case 114 /* OperatorCode.i32_or */:
+ case 115 /* OperatorCode.i32_xor */:
+ case 116 /* OperatorCode.i32_shl */:
+ case 117 /* OperatorCode.i32_shr_s */:
+ case 118 /* OperatorCode.i32_shr_u */:
+ case 119 /* OperatorCode.i32_rotl */:
+ case 120 /* OperatorCode.i32_rotr */:
+ case 121 /* OperatorCode.i64_clz */:
+ case 122 /* OperatorCode.i64_ctz */:
+ case 123 /* OperatorCode.i64_popcnt */:
+ case 124 /* OperatorCode.i64_add */:
+ case 125 /* OperatorCode.i64_sub */:
+ case 126 /* OperatorCode.i64_mul */:
+ case 127 /* OperatorCode.i64_div_s */:
+ case 128 /* OperatorCode.i64_div_u */:
+ case 129 /* OperatorCode.i64_rem_s */:
+ case 130 /* OperatorCode.i64_rem_u */:
+ case 131 /* OperatorCode.i64_and */:
+ case 132 /* OperatorCode.i64_or */:
+ case 133 /* OperatorCode.i64_xor */:
+ case 134 /* OperatorCode.i64_shl */:
+ case 135 /* OperatorCode.i64_shr_s */:
+ case 136 /* OperatorCode.i64_shr_u */:
+ case 137 /* OperatorCode.i64_rotl */:
+ case 138 /* OperatorCode.i64_rotr */:
+ case 139 /* OperatorCode.f32_abs */:
+ case 140 /* OperatorCode.f32_neg */:
+ case 141 /* OperatorCode.f32_ceil */:
+ case 142 /* OperatorCode.f32_floor */:
+ case 143 /* OperatorCode.f32_trunc */:
+ case 144 /* OperatorCode.f32_nearest */:
+ case 145 /* OperatorCode.f32_sqrt */:
+ case 146 /* OperatorCode.f32_add */:
+ case 147 /* OperatorCode.f32_sub */:
+ case 148 /* OperatorCode.f32_mul */:
+ case 149 /* OperatorCode.f32_div */:
+ case 150 /* OperatorCode.f32_min */:
+ case 151 /* OperatorCode.f32_max */:
+ case 152 /* OperatorCode.f32_copysign */:
+ case 153 /* OperatorCode.f64_abs */:
+ case 154 /* OperatorCode.f64_neg */:
+ case 155 /* OperatorCode.f64_ceil */:
+ case 156 /* OperatorCode.f64_floor */:
+ case 157 /* OperatorCode.f64_trunc */:
+ case 158 /* OperatorCode.f64_nearest */:
+ case 159 /* OperatorCode.f64_sqrt */:
+ case 160 /* OperatorCode.f64_add */:
+ case 161 /* OperatorCode.f64_sub */:
+ case 162 /* OperatorCode.f64_mul */:
+ case 163 /* OperatorCode.f64_div */:
+ case 164 /* OperatorCode.f64_min */:
+ case 165 /* OperatorCode.f64_max */:
+ case 166 /* OperatorCode.f64_copysign */:
+ case 167 /* OperatorCode.i32_wrap_i64 */:
+ case 168 /* OperatorCode.i32_trunc_f32_s */:
+ case 169 /* OperatorCode.i32_trunc_f32_u */:
+ case 170 /* OperatorCode.i32_trunc_f64_s */:
+ case 171 /* OperatorCode.i32_trunc_f64_u */:
+ case 172 /* OperatorCode.i64_extend_i32_s */:
+ case 173 /* OperatorCode.i64_extend_i32_u */:
+ case 174 /* OperatorCode.i64_trunc_f32_s */:
+ case 175 /* OperatorCode.i64_trunc_f32_u */:
+ case 176 /* OperatorCode.i64_trunc_f64_s */:
+ case 177 /* OperatorCode.i64_trunc_f64_u */:
+ case 178 /* OperatorCode.f32_convert_i32_s */:
+ case 179 /* OperatorCode.f32_convert_i32_u */:
+ case 180 /* OperatorCode.f32_convert_i64_s */:
+ case 181 /* OperatorCode.f32_convert_i64_u */:
+ case 182 /* OperatorCode.f32_demote_f64 */:
+ case 183 /* OperatorCode.f64_convert_i32_s */:
+ case 184 /* OperatorCode.f64_convert_i32_u */:
+ case 185 /* OperatorCode.f64_convert_i64_s */:
+ case 186 /* OperatorCode.f64_convert_i64_u */:
+ case 187 /* OperatorCode.f64_promote_f32 */:
+ case 188 /* OperatorCode.i32_reinterpret_f32 */:
+ case 189 /* OperatorCode.i64_reinterpret_f64 */:
+ case 190 /* OperatorCode.f32_reinterpret_i32 */:
+ case 191 /* OperatorCode.f64_reinterpret_i64 */:
+ case 192 /* OperatorCode.i32_extend8_s */:
+ case 193 /* OperatorCode.i32_extend16_s */:
+ case 194 /* OperatorCode.i64_extend8_s */:
+ case 195 /* OperatorCode.i64_extend16_s */:
+ case 196 /* OperatorCode.i64_extend32_s */:
+ case 209 /* OperatorCode.ref_is_null */:
+ case 212 /* OperatorCode.ref_as_non_null */:
+ case 211 /* OperatorCode.ref_eq */:
+ break;
+ default:
+ this.error = new Error("Unknown operator: ".concat(code));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ }
+ this.result = {
+ code: code,
+ blockType: blockType,
+ selectType: selectType,
+ refType: refType,
+ srcType: undefined,
+ brDepth: brDepth,
+ brTable: brTable,
+ relativeDepth: relativeDepth,
+ tableIndex: tableIndex,
+ funcIndex: funcIndex,
+ typeIndex: typeIndex,
+ localIndex: localIndex,
+ globalIndex: globalIndex,
+ fieldIndex: undefined,
+ tagIndex: tagIndex,
+ memoryAddress: memoryAddress,
+ literal: literal,
+ segmentIndex: undefined,
+ destinationIndex: undefined,
+ len: undefined,
+ lines: undefined,
+ lineIndex: undefined,
+ };
+ return true;
+ };
+ BinaryReader.prototype.readFunctionBody = function () {
+ if (this._sectionEntriesLeft === 0) {
+ this.skipSection();
+ return this.read();
+ }
+ if (!this.hasVarIntBytes())
+ return false;
+ var pos = this._pos;
+ var size = this.readVarUint32();
+ var bodyEnd = this._pos + size;
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ var localCount = this.readVarUint32();
+ var locals = [];
+ for (var i = 0; i < localCount; i++) {
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ var count = this.readVarUint32();
+ if (!this.hasVarIntBytes()) {
+ this._pos = pos;
+ return false;
+ }
+ var type = this.readType();
+ locals.push({ count: count, type: type });
+ }
+ var bodyStart = this._pos;
+ this.state = 28 /* BinaryReaderState.BEGIN_FUNCTION_BODY */;
+ this.result = {
+ locals: locals,
+ };
+ this._functionRange = new DataRange(bodyStart, bodyEnd);
+ this._sectionEntriesLeft--;
+ return true;
+ };
+ BinaryReader.prototype.readSectionHeader = function () {
+ if (this._pos >= this._length && this._eof) {
+ this._sectionId = -1 /* SectionCode.Unknown */;
+ this._sectionRange = null;
+ this.result = null;
+ this.state = 2 /* BinaryReaderState.END_WASM */;
+ return true;
+ }
+ // TODO: Handle _eof.
+ if (this._pos < this._length - 4) {
+ var magicNumber = this.peekInt32();
+ if (magicNumber === WASM_MAGIC_NUMBER) {
+ this._sectionId = -1 /* SectionCode.Unknown */;
+ this._sectionRange = null;
+ this.result = null;
+ this.state = 2 /* BinaryReaderState.END_WASM */;
+ return true;
+ }
+ }
+ if (!this.hasVarIntBytes())
+ return false;
+ var sectionStart = this._pos;
+ var id = this.readVarUint7();
+ if (!this.hasVarIntBytes()) {
+ this._pos = sectionStart;
+ return false;
+ }
+ var payloadLength = this.readVarUint32();
+ var name = null;
+ var payloadEnd = this._pos + payloadLength;
+ if (id == 0) {
+ if (!this.hasStringBytes()) {
+ this._pos = sectionStart;
+ return false;
+ }
+ name = this.readStringBytes();
+ }
+ this.result = { id: id, name: name };
+ this._sectionId = id;
+ this._sectionRange = new DataRange(this._pos, payloadEnd);
+ this.state = 3 /* BinaryReaderState.BEGIN_SECTION */;
+ return true;
+ };
+ BinaryReader.prototype.readSectionRawData = function () {
+ var payloadLength = this._sectionRange.end - this._sectionRange.start;
+ if (!this.hasBytes(payloadLength)) {
+ return false;
+ }
+ this.state = 7 /* BinaryReaderState.SECTION_RAW_DATA */;
+ this.result = this.readBytes(payloadLength);
+ return true;
+ };
+ BinaryReader.prototype.readSectionBody = function () {
+ if (this._pos >= this._sectionRange.end) {
+ this.result = null;
+ this.state = 4 /* BinaryReaderState.END_SECTION */;
+ this._sectionId = -1 /* SectionCode.Unknown */;
+ this._sectionRange = null;
+ return true;
+ }
+ var currentSection = this.result;
+ switch (currentSection.id) {
+ case 1 /* SectionCode.Type */:
+ if (!this.hasSectionPayload())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ this._recGroupTypesLeft = -1;
+ return this.readTypeEntry();
+ case 2 /* SectionCode.Import */:
+ if (!this.hasSectionPayload())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readImportEntry();
+ case 7 /* SectionCode.Export */:
+ if (!this.hasSectionPayload())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readExportEntry();
+ case 3 /* SectionCode.Function */:
+ if (!this.hasSectionPayload())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readFunctionEntry();
+ case 4 /* SectionCode.Table */:
+ if (!this.hasSectionPayload())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readTableEntry();
+ case 5 /* SectionCode.Memory */:
+ if (!this.hasSectionPayload())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readMemoryEntry();
+ case 6 /* SectionCode.Global */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readGlobalEntry();
+ case 8 /* SectionCode.Start */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this.state = 22 /* BinaryReaderState.START_SECTION_ENTRY */;
+ this.result = { index: this.readVarUint32() };
+ return true;
+ case 10 /* SectionCode.Code */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ this.state = 29 /* BinaryReaderState.READING_FUNCTION_HEADER */;
+ return this.readFunctionBody();
+ case 9 /* SectionCode.Element */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readElementEntry();
+ case 11 /* SectionCode.Data */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readDataEntry();
+ case 12 /* SectionCode.DataCount */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readDataCountEntry();
+ case 13 /* SectionCode.Tag */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readTagEntry();
+ case 0 /* SectionCode.Custom */:
+ var customSectionName = (0, exports.bytesToString)(currentSection.name);
+ if (customSectionName === "name") {
+ return this.readNameEntry();
+ }
+ if (customSectionName.indexOf("reloc.") === 0) {
+ return this.readRelocHeader();
+ }
+ if (customSectionName === "linking") {
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readLinkingEntry();
+ }
+ if (customSectionName === "sourceMappingURL") {
+ return this.readSourceMappingURL();
+ }
+ return this.readSectionRawData();
+ default:
+ this.error = new Error("Unsupported section: ".concat(this._sectionId));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ };
+ BinaryReader.prototype.read = function () {
+ switch (this.state) {
+ case 0 /* BinaryReaderState.INITIAL */:
+ if (!this.hasBytes(8))
+ return false;
+ var magicNumber = this.readUint32();
+ if (magicNumber != WASM_MAGIC_NUMBER) {
+ this.error = new Error("Bad magic number");
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ var version = this.readUint32();
+ if (version != WASM_SUPPORTED_VERSION &&
+ version != WASM_SUPPORTED_EXPERIMENTAL_VERSION) {
+ this.error = new Error("Bad version number ".concat(version));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ this.result = { magicNumber: magicNumber, version: version };
+ this.state = 1 /* BinaryReaderState.BEGIN_WASM */;
+ return true;
+ case 2 /* BinaryReaderState.END_WASM */:
+ this.result = null;
+ this.state = 1 /* BinaryReaderState.BEGIN_WASM */;
+ if (this.hasMoreBytes()) {
+ this.state = 0 /* BinaryReaderState.INITIAL */;
+ return this.read();
+ }
+ return false;
+ case -1 /* BinaryReaderState.ERROR */:
+ return true;
+ case 1 /* BinaryReaderState.BEGIN_WASM */:
+ case 4 /* BinaryReaderState.END_SECTION */:
+ return this.readSectionHeader();
+ case 3 /* BinaryReaderState.BEGIN_SECTION */:
+ return this.readSectionBody();
+ case 5 /* BinaryReaderState.SKIPPING_SECTION */:
+ if (!this.hasSectionPayload()) {
+ return false;
+ }
+ this.state = 4 /* BinaryReaderState.END_SECTION */;
+ this._pos = this._sectionRange.end;
+ this._sectionId = -1 /* SectionCode.Unknown */;
+ this._sectionRange = null;
+ this.result = null;
+ return true;
+ case 32 /* BinaryReaderState.SKIPPING_FUNCTION_BODY */:
+ this.state = 31 /* BinaryReaderState.END_FUNCTION_BODY */;
+ this._pos = this._functionRange.end;
+ this._functionRange = null;
+ this.result = null;
+ return true;
+ case 11 /* BinaryReaderState.TYPE_SECTION_ENTRY */:
+ if (this._recGroupTypesLeft >= 0) {
+ return this.readRecGroupEntry();
+ }
+ return this.readTypeEntry();
+ case 47 /* BinaryReaderState.BEGIN_REC_GROUP */:
+ return this.readRecGroupEntry();
+ case 48 /* BinaryReaderState.END_REC_GROUP */:
+ return this.readTypeEntry();
+ case 12 /* BinaryReaderState.IMPORT_SECTION_ENTRY */:
+ return this.readImportEntry();
+ case 17 /* BinaryReaderState.EXPORT_SECTION_ENTRY */:
+ return this.readExportEntry();
+ case 13 /* BinaryReaderState.FUNCTION_SECTION_ENTRY */:
+ return this.readFunctionEntry();
+ case 14 /* BinaryReaderState.TABLE_SECTION_ENTRY */:
+ return this.readTableEntry();
+ case 15 /* BinaryReaderState.MEMORY_SECTION_ENTRY */:
+ return this.readMemoryEntry();
+ case 23 /* BinaryReaderState.TAG_SECTION_ENTRY */:
+ return this.readTagEntry();
+ case 16 /* BinaryReaderState.GLOBAL_SECTION_ENTRY */:
+ case 40 /* BinaryReaderState.END_GLOBAL_SECTION_ENTRY */:
+ return this.readGlobalEntry();
+ case 39 /* BinaryReaderState.BEGIN_GLOBAL_SECTION_ENTRY */:
+ return this.readInitExpressionBody();
+ case 20 /* BinaryReaderState.ELEMENT_SECTION_ENTRY */:
+ case 35 /* BinaryReaderState.END_ELEMENT_SECTION_ENTRY */:
+ return this.readElementEntry();
+ case 33 /* BinaryReaderState.BEGIN_ELEMENT_SECTION_ENTRY */:
+ if (isActiveElementSegmentType(this._segmentType)) {
+ return this.readOffsetExpressionBody();
+ }
+ else {
+ // passive or declared element segment
+ return this.readElementEntryBody();
+ }
+ case 34 /* BinaryReaderState.ELEMENT_SECTION_ENTRY_BODY */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._segmentEntriesLeft = this.readVarUint32();
+ if (this._segmentEntriesLeft === 0) {
+ this.state = 35 /* BinaryReaderState.END_ELEMENT_SECTION_ENTRY */;
+ this.result = null;
+ return true;
+ }
+ return this.readInitExpressionBody();
+ case 49 /* BinaryReaderState.DATA_COUNT_SECTION_ENTRY */:
+ return this.readDataCountEntry();
+ case 18 /* BinaryReaderState.DATA_SECTION_ENTRY */:
+ case 38 /* BinaryReaderState.END_DATA_SECTION_ENTRY */:
+ return this.readDataEntry();
+ case 36 /* BinaryReaderState.BEGIN_DATA_SECTION_ENTRY */:
+ if (isActiveDataSegmentType(this._segmentType)) {
+ return this.readOffsetExpressionBody();
+ }
+ else {
+ // passive data segment
+ return this.readDataEntryBody();
+ }
+ case 37 /* BinaryReaderState.DATA_SECTION_ENTRY_BODY */:
+ this.state = 38 /* BinaryReaderState.END_DATA_SECTION_ENTRY */;
+ this.result = null;
+ return true;
+ case 27 /* BinaryReaderState.END_INIT_EXPRESSION_BODY */:
+ switch (this._sectionId) {
+ case 6 /* SectionCode.Global */:
+ this.state = 40 /* BinaryReaderState.END_GLOBAL_SECTION_ENTRY */;
+ return true;
+ case 9 /* SectionCode.Element */:
+ if (--this._segmentEntriesLeft > 0) {
+ return this.readInitExpressionBody();
+ }
+ this.state = 35 /* BinaryReaderState.END_ELEMENT_SECTION_ENTRY */;
+ this.result = null;
+ return true;
+ }
+ this.error = new Error("Unexpected section type: ".concat(this._sectionId));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ case 46 /* BinaryReaderState.END_OFFSET_EXPRESSION_BODY */:
+ if (this._sectionId === 11 /* SectionCode.Data */) {
+ return this.readDataEntryBody();
+ }
+ else {
+ return this.readElementEntryBody();
+ }
+ case 19 /* BinaryReaderState.NAME_SECTION_ENTRY */:
+ return this.readNameEntry();
+ case 41 /* BinaryReaderState.RELOC_SECTION_HEADER */:
+ if (!this.hasVarIntBytes())
+ return false;
+ this._sectionEntriesLeft = this.readVarUint32();
+ return this.readRelocEntry();
+ case 21 /* BinaryReaderState.LINKING_SECTION_ENTRY */:
+ return this.readLinkingEntry();
+ case 43 /* BinaryReaderState.SOURCE_MAPPING_URL */:
+ this.state = 4 /* BinaryReaderState.END_SECTION */;
+ this.result = null;
+ return true;
+ case 42 /* BinaryReaderState.RELOC_SECTION_ENTRY */:
+ return this.readRelocEntry();
+ case 29 /* BinaryReaderState.READING_FUNCTION_HEADER */:
+ case 31 /* BinaryReaderState.END_FUNCTION_BODY */:
+ return this.readFunctionBody();
+ case 28 /* BinaryReaderState.BEGIN_FUNCTION_BODY */:
+ this.state = 30 /* BinaryReaderState.CODE_OPERATOR */;
+ return this.readCodeOperator();
+ case 25 /* BinaryReaderState.BEGIN_INIT_EXPRESSION_BODY */:
+ this.state = 26 /* BinaryReaderState.INIT_EXPRESSION_OPERATOR */;
+ return this.readCodeOperator();
+ case 44 /* BinaryReaderState.BEGIN_OFFSET_EXPRESSION_BODY */:
+ this.state = 45 /* BinaryReaderState.OFFSET_EXPRESSION_OPERATOR */;
+ return this.readCodeOperator();
+ case 30 /* BinaryReaderState.CODE_OPERATOR */:
+ case 26 /* BinaryReaderState.INIT_EXPRESSION_OPERATOR */:
+ case 45 /* BinaryReaderState.OFFSET_EXPRESSION_OPERATOR */:
+ return this.readCodeOperator();
+ case 6 /* BinaryReaderState.READING_SECTION_RAW_DATA */:
+ return this.readSectionRawData();
+ case 22 /* BinaryReaderState.START_SECTION_ENTRY */:
+ case 7 /* BinaryReaderState.SECTION_RAW_DATA */:
+ this.state = 4 /* BinaryReaderState.END_SECTION */;
+ this.result = null;
+ return true;
+ default:
+ this.error = new Error("Unsupported state: ".concat(this.state));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return true;
+ }
+ };
+ BinaryReader.prototype.skipSection = function () {
+ if (this.state === -1 /* BinaryReaderState.ERROR */ ||
+ this.state === 0 /* BinaryReaderState.INITIAL */ ||
+ this.state === 4 /* BinaryReaderState.END_SECTION */ ||
+ this.state === 1 /* BinaryReaderState.BEGIN_WASM */ ||
+ this.state === 2 /* BinaryReaderState.END_WASM */)
+ return;
+ this.state = 5 /* BinaryReaderState.SKIPPING_SECTION */;
+ };
+ BinaryReader.prototype.skipFunctionBody = function () {
+ if (this.state !== 28 /* BinaryReaderState.BEGIN_FUNCTION_BODY */ &&
+ this.state !== 30 /* BinaryReaderState.CODE_OPERATOR */)
+ return;
+ this.state = 32 /* BinaryReaderState.SKIPPING_FUNCTION_BODY */;
+ };
+ BinaryReader.prototype.skipInitExpression = function () {
+ while (this.state === 26 /* BinaryReaderState.INIT_EXPRESSION_OPERATOR */)
+ this.readCodeOperator();
+ };
+ BinaryReader.prototype.fetchSectionRawData = function () {
+ if (this.state !== 3 /* BinaryReaderState.BEGIN_SECTION */) {
+ this.error = new Error("Unsupported state: ".concat(this.state));
+ this.state = -1 /* BinaryReaderState.ERROR */;
+ return;
+ }
+ this.state = 6 /* BinaryReaderState.READING_SECTION_RAW_DATA */;
+ };
+ return BinaryReader;
+}());
+exports.BinaryReader = BinaryReader;
+if (typeof TextDecoder !== "undefined") {
+ try {
+ exports.bytesToString = (function () {
+ var utf8Decoder = new TextDecoder("utf-8");
+ utf8Decoder.decode(new Uint8Array([97, 208, 144]));
+ return function (b) { return utf8Decoder.decode(b); };
+ })();
+ }
+ catch (_) {
+ /* ignore */
+ }
+}
+if (!exports.bytesToString) {
+ exports.bytesToString = function (b) {
+ var str = String.fromCharCode.apply(null, b);
+ return decodeURIComponent(escape(str));
+ };
+}
diff --git a/devtools/client/shared/vendor/dagre-d3.js b/devtools/client/shared/vendor/dagre-d3.js
new file mode 100644
index 0000000000..482ce827f9
--- /dev/null
+++ b/devtools/client/shared/vendor/dagre-d3.js
@@ -0,0 +1,4560 @@
+;(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+var global=self;/**
+ * @license
+ * Copyright (c) 2012-2013 Chris Pettitt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+global.dagreD3 = require('./index');
+
+},{"./index":2}],2:[function(require,module,exports){
+/**
+ * @license
+ * Copyright (c) 2012-2013 Chris Pettitt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+module.exports = {
+ Digraph: require('graphlib').Digraph,
+ Renderer: require('./lib/Renderer'),
+ json: require('graphlib').converter.json,
+ layout: require('dagre').layout,
+ version: require('./lib/version')
+};
+
+},{"./lib/Renderer":3,"./lib/version":4,"dagre":11,"graphlib":28}],3:[function(require,module,exports){
+var layout = require('dagre').layout;
+
+var d3;
+try { d3 = require('d3'); } catch (_) { d3 = window.d3; }
+
+module.exports = Renderer;
+
+function Renderer() {
+ // Set up defaults...
+ this._layout = layout();
+
+ this.drawNodes(defaultDrawNodes);
+ this.drawEdgeLabels(defaultDrawEdgeLabels);
+ this.drawEdgePaths(defaultDrawEdgePaths);
+ this.positionNodes(defaultPositionNodes);
+ this.positionEdgeLabels(defaultPositionEdgeLabels);
+ this.positionEdgePaths(defaultPositionEdgePaths);
+ this.transition(defaultTransition);
+ this.postLayout(defaultPostLayout);
+ this.postRender(defaultPostRender);
+
+ this.edgeInterpolate('bundle');
+ this.edgeTension(0.95);
+}
+
+Renderer.prototype.layout = function(layout) {
+ if (!arguments.length) { return this._layout; }
+ this._layout = layout;
+ return this;
+};
+
+Renderer.prototype.drawNodes = function(drawNodes) {
+ if (!arguments.length) { return this._drawNodes; }
+ this._drawNodes = bind(drawNodes, this);
+ return this;
+};
+
+Renderer.prototype.drawEdgeLabels = function(drawEdgeLabels) {
+ if (!arguments.length) { return this._drawEdgeLabels; }
+ this._drawEdgeLabels = bind(drawEdgeLabels, this);
+ return this;
+};
+
+Renderer.prototype.drawEdgePaths = function(drawEdgePaths) {
+ if (!arguments.length) { return this._drawEdgePaths; }
+ this._drawEdgePaths = bind(drawEdgePaths, this);
+ return this;
+};
+
+Renderer.prototype.positionNodes = function(positionNodes) {
+ if (!arguments.length) { return this._positionNodes; }
+ this._positionNodes = bind(positionNodes, this);
+ return this;
+};
+
+Renderer.prototype.positionEdgeLabels = function(positionEdgeLabels) {
+ if (!arguments.length) { return this._positionEdgeLabels; }
+ this._positionEdgeLabels = bind(positionEdgeLabels, this);
+ return this;
+};
+
+Renderer.prototype.positionEdgePaths = function(positionEdgePaths) {
+ if (!arguments.length) { return this._positionEdgePaths; }
+ this._positionEdgePaths = bind(positionEdgePaths, this);
+ return this;
+};
+
+Renderer.prototype.transition = function(transition) {
+ if (!arguments.length) { return this._transition; }
+ this._transition = bind(transition, this);
+ return this;
+};
+
+Renderer.prototype.postLayout = function(postLayout) {
+ if (!arguments.length) { return this._postLayout; }
+ this._postLayout = bind(postLayout, this);
+ return this;
+};
+
+Renderer.prototype.postRender = function(postRender) {
+ if (!arguments.length) { return this._postRender; }
+ this._postRender = bind(postRender, this);
+ return this;
+};
+
+Renderer.prototype.edgeInterpolate = function(edgeInterpolate) {
+ if (!arguments.length) { return this._edgeInterpolate; }
+ this._edgeInterpolate = edgeInterpolate;
+ return this;
+};
+
+Renderer.prototype.edgeTension = function(edgeTension) {
+ if (!arguments.length) { return this._edgeTension; }
+ this._edgeTension = edgeTension;
+ return this;
+};
+
+Renderer.prototype.run = function(graph, svg) {
+ // First copy the input graph so that it is not changed by the rendering
+ // process.
+ graph = copyAndInitGraph(graph);
+
+ // Create layers
+ svg
+ .selectAll('g.edgePaths, g.edgeLabels, g.nodes')
+ .data(['edgePaths', 'edgeLabels', 'nodes'])
+ .enter()
+ .append('g')
+ .attr('class', function(d) { return d; });
+
+
+ // Create node and edge roots, attach labels, and capture dimension
+ // information for use with layout.
+ var svgNodes = this._drawNodes(graph, svg.select('g.nodes'));
+ var svgEdgeLabels = this._drawEdgeLabels(graph, svg.select('g.edgeLabels'));
+
+ svgNodes.each(function(u) { calculateDimensions(this, graph.node(u)); });
+ svgEdgeLabels.each(function(e) { calculateDimensions(this, graph.edge(e)); });
+
+ // Now apply the layout function
+ var result = runLayout(graph, this._layout);
+
+ // Run any user-specified post layout processing
+ this._postLayout(result, svg);
+
+ var svgEdgePaths = this._drawEdgePaths(graph, svg.select('g.edgePaths'));
+
+ // Apply the layout information to the graph
+ this._positionNodes(result, svgNodes);
+ this._positionEdgeLabels(result, svgEdgeLabels);
+ this._positionEdgePaths(result, svgEdgePaths);
+
+ this._postRender(result, svg);
+
+ return result;
+};
+
+function copyAndInitGraph(graph) {
+ var copy = graph.copy();
+
+ // Init labels if they were not present in the source graph
+ copy.nodes().forEach(function(u) {
+ var value = copy.node(u);
+ if (value === undefined) {
+ value = {};
+ copy.node(u, value);
+ }
+ if (!('label' in value)) { value.label = ''; }
+ });
+
+ copy.edges().forEach(function(e) {
+ var value = copy.edge(e);
+ if (value === undefined) {
+ value = {};
+ copy.edge(e, value);
+ }
+ if (!('label' in value)) { value.label = ''; }
+ });
+
+ return copy;
+}
+
+function calculateDimensions(group, value) {
+ var bbox = group.getBBox();
+ value.width = bbox.width;
+ value.height = bbox.height;
+}
+
+function runLayout(graph, layout) {
+ var result = layout.run(graph);
+
+ // Copy labels to the result graph
+ graph.eachNode(function(u, value) { result.node(u).label = value.label; });
+ graph.eachEdge(function(e, u, v, value) { result.edge(e).label = value.label; });
+
+ return result;
+}
+
+function defaultDrawNodes(g, root) {
+ var nodes = g.nodes().filter(function(u) { return !isComposite(g, u); });
+
+ var svgNodes = root
+ .selectAll('g.node')
+ .classed('enter', false)
+ .data(nodes, function(u) { return u; });
+
+ svgNodes.selectAll('*').remove();
+
+ svgNodes
+ .enter()
+ .append('g')
+ .style('opacity', 0)
+ .attr('class', 'node enter');
+
+ svgNodes.each(function(u) { addLabel(g.node(u), d3.select(this), 10, 10); });
+
+ this._transition(svgNodes.exit())
+ .style('opacity', 0)
+ .remove();
+
+ return svgNodes;
+}
+
+function defaultDrawEdgeLabels(g, root) {
+ var svgEdgeLabels = root
+ .selectAll('g.edgeLabel')
+ .classed('enter', false)
+ .data(g.edges(), function (e) { return e; });
+
+ svgEdgeLabels.selectAll('*').remove();
+
+ svgEdgeLabels
+ .enter()
+ .append('g')
+ .style('opacity', 0)
+ .attr('class', 'edgeLabel enter');
+
+ svgEdgeLabels.each(function(e) { addLabel(g.edge(e), d3.select(this), 0, 0); });
+
+ this._transition(svgEdgeLabels.exit())
+ .style('opacity', 0)
+ .remove();
+
+ return svgEdgeLabels;
+}
+
+var defaultDrawEdgePaths = function(g, root) {
+ var svgEdgePaths = root
+ .selectAll('g.edgePath')
+ .classed('enter', false)
+ .data(g.edges(), function(e) { return e; });
+
+ svgEdgePaths
+ .enter()
+ .append('g')
+ .attr('class', 'edgePath enter')
+ .append('path')
+ .style('opacity', 0)
+ .attr('marker-end', 'url(#arrowhead)');
+
+ this._transition(svgEdgePaths.exit())
+ .style('opacity', 0)
+ .remove();
+
+ return svgEdgePaths;
+};
+
+function defaultPositionNodes(g, svgNodes, svgNodesEnter) {
+ function transform(u) {
+ var value = g.node(u);
+ return 'translate(' + value.x + ',' + value.y + ')';
+ }
+
+ // For entering nodes, position immediately without transition
+ svgNodes.filter('.enter').attr('transform', transform);
+
+ this._transition(svgNodes)
+ .style('opacity', 1)
+ .attr('transform', transform);
+}
+
+function defaultPositionEdgeLabels(g, svgEdgeLabels) {
+ function transform(e) {
+ var value = g.edge(e);
+ var point = findMidPoint(value.points);
+ return 'translate(' + point.x + ',' + point.y + ')';
+ }
+
+ // For entering edge labels, position immediately without transition
+ svgEdgeLabels.filter('.enter').attr('transform', transform);
+
+ this._transition(svgEdgeLabels)
+ .style('opacity', 1)
+ .attr('transform', transform);
+}
+
+function defaultPositionEdgePaths(g, svgEdgePaths) {
+ var interpolate = this._edgeInterpolate,
+ tension = this._edgeTension;
+
+ function calcPoints(e) {
+ var value = g.edge(e);
+ var source = g.node(g.incidentNodes(e)[0]);
+ var target = g.node(g.incidentNodes(e)[1]);
+ var points = value.points.slice();
+
+ var p0 = points.length === 0 ? target : points[0];
+ var p1 = points.length === 0 ? source : points[points.length - 1];
+
+ points.unshift(intersectRect(source, p0));
+ // TODO: use bpodgursky's shortening algorithm here
+ points.push(intersectRect(target, p1));
+
+ return d3.svg.line()
+ .x(function(d) { return d.x; })
+ .y(function(d) { return d.y; })
+ .interpolate(interpolate)
+ .tension(tension)
+ (points);
+ }
+
+ svgEdgePaths.filter('.enter').selectAll('path')
+ .attr('d', calcPoints);
+
+ this._transition(svgEdgePaths.selectAll('path'))
+ .attr('d', calcPoints)
+ .style('opacity', 1);
+}
+
+// By default we do not use transitions
+function defaultTransition(selection) {
+ return selection;
+}
+
+function defaultPostLayout() {
+ // Do nothing
+}
+
+function defaultPostRender(graph, root) {
+ if (graph.isDirected() && root.select('#arrowhead').empty()) {
+ root
+ .append('svg:defs')
+ .append('svg:marker')
+ .attr('id', 'arrowhead')
+ .attr('viewBox', '0 0 10 10')
+ .attr('refX', 8)
+ .attr('refY', 5)
+ .attr('markerUnits', 'strokewidth')
+ .attr('markerWidth', 8)
+ .attr('markerHeight', 5)
+ .attr('orient', 'auto')
+ .attr('style', 'fill: #333')
+ .append('svg:path')
+ .attr('d', 'M 0 0 L 10 5 L 0 10 z');
+ }
+}
+
+function addLabel(node, root, marginX, marginY) {
+ // Add the rect first so that it appears behind the label
+ var label = node.label;
+ var rect = root.append('rect');
+ var labelSvg = root.append('g');
+
+ if (label[0] === '<') {
+ addForeignObjectLabel(label, labelSvg);
+ // No margin for HTML elements
+ marginX = marginY = 0;
+ } else {
+ addTextLabel(label,
+ labelSvg,
+ Math.floor(node.labelCols),
+ node.labelCut);
+ }
+
+ var bbox = root.node().getBBox();
+
+ labelSvg.attr('transform',
+ 'translate(' + (-bbox.width / 2) + ',' + (-bbox.height / 2) + ')');
+
+ rect
+ .attr('rx', 5)
+ .attr('ry', 5)
+ .attr('x', -(bbox.width / 2 + marginX))
+ .attr('y', -(bbox.height / 2 + marginY))
+ .attr('width', bbox.width + 2 * marginX)
+ .attr('height', bbox.height + 2 * marginY);
+}
+
+function addForeignObjectLabel(label, root) {
+ var fo = root
+ .append('foreignObject')
+ .attr('width', '100000');
+
+ var w, h;
+ fo
+ .append('xhtml:div')
+ .style('float', 'left')
+ // TODO find a better way to get dimensions for foreignObjects...
+ .html(function() { return label; })
+ .each(function() {
+ w = this.clientWidth;
+ h = this.clientHeight;
+ });
+
+ fo
+ .attr('width', w)
+ .attr('height', h);
+}
+
+function addTextLabel(label, root, labelCols, labelCut) {
+ if (labelCut === undefined) labelCut = "false";
+ labelCut = (labelCut.toString().toLowerCase() === "true");
+
+ var node = root
+ .append('text')
+ .attr('text-anchor', 'left');
+
+ label = label.replace(/\\n/g, "\n");
+
+ var arr = labelCols ? wordwrap(label, labelCols, labelCut) : label;
+ arr = arr.split("\n");
+ for (var i = 0; i < arr.length; i++) {
+ node
+ .append('tspan')
+ .attr('dy', '1em')
+ .attr('x', '1')
+ .text(arr[i]);
+ }
+}
+
+// Thanks to
+// http://james.padolsey.com/javascript/wordwrap-for-javascript/
+function wordwrap (str, width, cut, brk) {
+ brk = brk || '\n';
+ width = width || 75;
+ cut = cut || false;
+
+ if (!str) { return str; }
+
+ var regex = '.{1,' +width+ '}(\\s|$)' + (cut ? '|.{' +width+ '}|.+$' : '|\\S+?(\\s|$)');
+
+ return str.match( RegExp(regex, 'g') ).join( brk );
+}
+
+function findMidPoint(points) {
+ var midIdx = points.length / 2;
+ if (points.length % 2) {
+ return points[Math.floor(midIdx)];
+ } else {
+ var p0 = points[midIdx - 1];
+ var p1 = points[midIdx];
+ return {x: (p0.x + p1.x) / 2, y: (p0.y + p1.y) / 2};
+ }
+}
+
+function intersectRect(rect, point) {
+ var x = rect.x;
+ var y = rect.y;
+
+ // For now we only support rectangles
+
+ // Rectangle intersection algorithm from:
+ // http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes
+ var dx = point.x - x;
+ var dy = point.y - y;
+ var w = rect.width / 2;
+ var h = rect.height / 2;
+
+ var sx, sy;
+ if (Math.abs(dy) * w > Math.abs(dx) * h) {
+ // Intersection is top or bottom of rect.
+ if (dy < 0) {
+ h = -h;
+ }
+ sx = dy === 0 ? 0 : h * dx / dy;
+ sy = h;
+ } else {
+ // Intersection is left or right of rect.
+ if (dx < 0) {
+ w = -w;
+ }
+ sx = w;
+ sy = dx === 0 ? 0 : w * dy / dx;
+ }
+
+ return {x: x + sx, y: y + sy};
+}
+
+function isComposite(g, u) {
+ return 'children' in g && g.children(u).length;
+}
+
+function bind(func, thisArg) {
+ // For some reason PhantomJS occassionally fails when using the builtin bind,
+ // so we check if it is available and if not, use a degenerate polyfill.
+ if (func.bind) {
+ return func.bind(thisArg);
+ }
+
+ return function() {
+ return func.apply(thisArg, arguments);
+ };
+}
+
+},{"d3":10,"dagre":11}],4:[function(require,module,exports){
+module.exports = '0.1.5';
+
+},{}],5:[function(require,module,exports){
+exports.Set = require('./lib/Set');
+exports.PriorityQueue = require('./lib/PriorityQueue');
+exports.version = require('./lib/version');
+
+},{"./lib/PriorityQueue":6,"./lib/Set":7,"./lib/version":9}],6:[function(require,module,exports){
+module.exports = PriorityQueue;
+
+/**
+ * A min-priority queue data structure. This algorithm is derived from Cormen,
+ * et al., "Introduction to Algorithms". The basic idea of a min-priority
+ * queue is that you can efficiently (in O(1) time) get the smallest key in
+ * the queue. Adding and removing elements takes O(log n) time. A key can
+ * have its priority decreased in O(log n) time.
+ */
+function PriorityQueue() {
+ this._arr = [];
+ this._keyIndices = {};
+}
+
+/**
+ * Returns the number of elements in the queue. Takes `O(1)` time.
+ */
+PriorityQueue.prototype.size = function() {
+ return this._arr.length;
+};
+
+/**
+ * Returns the keys that are in the queue. Takes `O(n)` time.
+ */
+PriorityQueue.prototype.keys = function() {
+ return this._arr.map(function(x) { return x.key; });
+};
+
+/**
+ * Returns `true` if **key** is in the queue and `false` if not.
+ */
+PriorityQueue.prototype.has = function(key) {
+ return key in this._keyIndices;
+};
+
+/**
+ * Returns the priority for **key**. If **key** is not present in the queue
+ * then this function returns `undefined`. Takes `O(1)` time.
+ *
+ * @param {Object} key
+ */
+PriorityQueue.prototype.priority = function(key) {
+ var index = this._keyIndices[key];
+ if (index !== undefined) {
+ return this._arr[index].priority;
+ }
+};
+
+/**
+ * Returns the key for the minimum element in this queue. If the queue is
+ * empty this function throws an Error. Takes `O(1)` time.
+ */
+PriorityQueue.prototype.min = function() {
+ if (this.size() === 0) {
+ throw new Error("Queue underflow");
+ }
+ return this._arr[0].key;
+};
+
+/**
+ * Inserts a new key into the priority queue. If the key already exists in
+ * the queue this function returns `false`; otherwise it will return `true`.
+ * Takes `O(n)` time.
+ *
+ * @param {Object} key the key to add
+ * @param {Number} priority the initial priority for the key
+ */
+PriorityQueue.prototype.add = function(key, priority) {
+ var keyIndices = this._keyIndices;
+ if (!(key in keyIndices)) {
+ var arr = this._arr;
+ var index = arr.length;
+ keyIndices[key] = index;
+ arr.push({key: key, priority: priority});
+ this._decrease(index);
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Removes and returns the smallest key in the queue. Takes `O(log n)` time.
+ */
+PriorityQueue.prototype.removeMin = function() {
+ this._swap(0, this._arr.length - 1);
+ var min = this._arr.pop();
+ delete this._keyIndices[min.key];
+ this._heapify(0);
+ return min.key;
+};
+
+/**
+ * Decreases the priority for **key** to **priority**. If the new priority is
+ * greater than the previous priority, this function will throw an Error.
+ *
+ * @param {Object} key the key for which to raise priority
+ * @param {Number} priority the new priority for the key
+ */
+PriorityQueue.prototype.decrease = function(key, priority) {
+ var index = this._keyIndices[key];
+ if (priority > this._arr[index].priority) {
+ throw new Error("New priority is greater than current priority. " +
+ "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority);
+ }
+ this._arr[index].priority = priority;
+ this._decrease(index);
+};
+
+PriorityQueue.prototype._heapify = function(i) {
+ var arr = this._arr;
+ var l = 2 * i,
+ r = l + 1,
+ largest = i;
+ if (l < arr.length) {
+ largest = arr[l].priority < arr[largest].priority ? l : largest;
+ if (r < arr.length) {
+ largest = arr[r].priority < arr[largest].priority ? r : largest;
+ }
+ if (largest !== i) {
+ this._swap(i, largest);
+ this._heapify(largest);
+ }
+ }
+};
+
+PriorityQueue.prototype._decrease = function(index) {
+ var arr = this._arr;
+ var priority = arr[index].priority;
+ var parent;
+ while (index !== 0) {
+ parent = index >> 1;
+ if (arr[parent].priority < priority) {
+ break;
+ }
+ this._swap(index, parent);
+ index = parent;
+ }
+};
+
+PriorityQueue.prototype._swap = function(i, j) {
+ var arr = this._arr;
+ var keyIndices = this._keyIndices;
+ var origArrI = arr[i];
+ var origArrJ = arr[j];
+ arr[i] = origArrJ;
+ arr[j] = origArrI;
+ keyIndices[origArrJ.key] = i;
+ keyIndices[origArrI.key] = j;
+};
+
+},{}],7:[function(require,module,exports){
+var util = require('./util');
+
+module.exports = Set;
+
+/**
+ * Constructs a new Set with an optional set of `initialKeys`.
+ *
+ * It is important to note that keys are coerced to String for most purposes
+ * with this object, similar to the behavior of JavaScript's Object. For
+ * example, the following will add only one key:
+ *
+ * var s = new Set();
+ * s.add(1);
+ * s.add("1");
+ *
+ * However, the type of the key is preserved internally so that `keys` returns
+ * the original key set uncoerced. For the above example, `keys` would return
+ * `[1]`.
+ */
+function Set(initialKeys) {
+ this._size = 0;
+ this._keys = {};
+
+ if (initialKeys) {
+ for (var i = 0, il = initialKeys.length; i < il; ++i) {
+ this.add(initialKeys[i]);
+ }
+ }
+}
+
+/**
+ * Returns a new Set that represents the set intersection of the array of given
+ * sets.
+ */
+Set.intersect = function(sets) {
+ if (sets.length === 0) {
+ return new Set();
+ }
+
+ var result = new Set(!util.isArray(sets[0]) ? sets[0].keys() : sets[0]);
+ for (var i = 1, il = sets.length; i < il; ++i) {
+ var resultKeys = result.keys(),
+ other = !util.isArray(sets[i]) ? sets[i] : new Set(sets[i]);
+ for (var j = 0, jl = resultKeys.length; j < jl; ++j) {
+ var key = resultKeys[j];
+ if (!other.has(key)) {
+ result.remove(key);
+ }
+ }
+ }
+
+ return result;
+};
+
+/**
+ * Returns a new Set that represents the set union of the array of given sets.
+ */
+Set.union = function(sets) {
+ var totalElems = util.reduce(sets, function(lhs, rhs) {
+ return lhs + (rhs.size ? rhs.size() : rhs.length);
+ }, 0);
+ var arr = new Array(totalElems);
+
+ var k = 0;
+ for (var i = 0, il = sets.length; i < il; ++i) {
+ var cur = sets[i],
+ keys = !util.isArray(cur) ? cur.keys() : cur;
+ for (var j = 0, jl = keys.length; j < jl; ++j) {
+ arr[k++] = keys[j];
+ }
+ }
+
+ return new Set(arr);
+};
+
+/**
+ * Returns the size of this set in `O(1)` time.
+ */
+Set.prototype.size = function() {
+ return this._size;
+};
+
+/**
+ * Returns the keys in this set. Takes `O(n)` time.
+ */
+Set.prototype.keys = function() {
+ return values(this._keys);
+};
+
+/**
+ * Tests if a key is present in this Set. Returns `true` if it is and `false`
+ * if not. Takes `O(1)` time.
+ */
+Set.prototype.has = function(key) {
+ return key in this._keys;
+};
+
+/**
+ * Adds a new key to this Set if it is not already present. Returns `true` if
+ * the key was added and `false` if it was already present. Takes `O(1)` time.
+ */
+Set.prototype.add = function(key) {
+ if (!(key in this._keys)) {
+ this._keys[key] = key;
+ ++this._size;
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Removes a key from this Set. If the key was removed this function returns
+ * `true`. If not, it returns `false`. Takes `O(1)` time.
+ */
+Set.prototype.remove = function(key) {
+ if (key in this._keys) {
+ delete this._keys[key];
+ --this._size;
+ return true;
+ }
+ return false;
+};
+
+/*
+ * Returns an array of all values for properties of **o**.
+ */
+function values(o) {
+ var ks = Object.keys(o),
+ len = ks.length,
+ result = new Array(len),
+ i;
+ for (i = 0; i < len; ++i) {
+ result[i] = o[ks[i]];
+ }
+ return result;
+}
+
+},{"./util":8}],8:[function(require,module,exports){
+/*
+ * This polyfill comes from
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
+ */
+if(!Array.isArray) {
+ exports.isArray = function (vArg) {
+ return Object.prototype.toString.call(vArg) === '[object Array]';
+ };
+} else {
+ exports.isArray = Array.isArray;
+}
+
+/*
+ * Slightly adapted polyfill from
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce
+ */
+if ('function' !== typeof Array.prototype.reduce) {
+ exports.reduce = function(array, callback, opt_initialValue) {
+ 'use strict';
+ if (null === array || 'undefined' === typeof array) {
+ // At the moment all modern browsers, that support strict mode, have
+ // native implementation of Array.prototype.reduce. For instance, IE8
+ // does not support strict mode, so this check is actually useless.
+ throw new TypeError(
+ 'Array.prototype.reduce called on null or undefined');
+ }
+ if ('function' !== typeof callback) {
+ throw new TypeError(callback + ' is not a function');
+ }
+ var index, value,
+ length = array.length >>> 0,
+ isValueSet = false;
+ if (1 < arguments.length) {
+ value = opt_initialValue;
+ isValueSet = true;
+ }
+ for (index = 0; length > index; ++index) {
+ if (array.hasOwnProperty(index)) {
+ if (isValueSet) {
+ value = callback(value, array[index], index, array);
+ }
+ else {
+ value = array[index];
+ isValueSet = true;
+ }
+ }
+ }
+ if (!isValueSet) {
+ throw new TypeError('Reduce of empty array with no initial value');
+ }
+ return value;
+ };
+} else {
+ exports.reduce = function(array, callback, opt_initialValue) {
+ return array.reduce(callback, opt_initialValue);
+ };
+}
+
+},{}],9:[function(require,module,exports){
+module.exports = '1.1.3';
+
+},{}],10:[function(require,module,exports){
+require("./d3");
+module.exports = d3;
+(function () { delete this.d3; })(); // unset global
+
+},{}],11:[function(require,module,exports){
+/*
+Copyright (c) 2012-2013 Chris Pettitt
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+exports.Digraph = require("graphlib").Digraph;
+exports.Graph = require("graphlib").Graph;
+exports.layout = require("./lib/layout");
+exports.version = require("./lib/version");
+
+},{"./lib/layout":12,"./lib/version":27,"graphlib":28}],12:[function(require,module,exports){
+var util = require('./util'),
+ rank = require('./rank'),
+ order = require('./order'),
+ CGraph = require('graphlib').CGraph,
+ CDigraph = require('graphlib').CDigraph;
+
+module.exports = function() {
+ // External configuration
+ var config = {
+ // How much debug information to include?
+ debugLevel: 0,
+ // Max number of sweeps to perform in order phase
+ orderMaxSweeps: order.DEFAULT_MAX_SWEEPS,
+ // Use network simplex algorithm in ranking
+ rankSimplex: false,
+ // Rank direction. Valid values are (TB, LR)
+ rankDir: 'TB'
+ };
+
+ // Phase functions
+ var position = require('./position')();
+
+ // This layout object
+ var self = {};
+
+ self.orderIters = util.propertyAccessor(self, config, 'orderMaxSweeps');
+
+ self.rankSimplex = util.propertyAccessor(self, config, 'rankSimplex');
+
+ self.nodeSep = delegateProperty(position.nodeSep);
+ self.edgeSep = delegateProperty(position.edgeSep);
+ self.universalSep = delegateProperty(position.universalSep);
+ self.rankSep = delegateProperty(position.rankSep);
+ self.rankDir = util.propertyAccessor(self, config, 'rankDir');
+ self.debugAlignment = delegateProperty(position.debugAlignment);
+
+ self.debugLevel = util.propertyAccessor(self, config, 'debugLevel', function(x) {
+ util.log.level = x;
+ position.debugLevel(x);
+ });
+
+ self.run = util.time('Total layout', run);
+
+ self._normalize = normalize;
+
+ return self;
+
+ /*
+ * Constructs an adjacency graph using the nodes and edges specified through
+ * config. For each node and edge we add a property `dagre` that contains an
+ * object that will hold intermediate and final layout information. Some of
+ * the contents include:
+ *
+ * 1) A generated ID that uniquely identifies the object.
+ * 2) Dimension information for nodes (copied from the source node).
+ * 3) Optional dimension information for edges.
+ *
+ * After the adjacency graph is constructed the code no longer needs to use
+ * the original nodes and edges passed in via config.
+ */
+ function initLayoutGraph(inputGraph) {
+ var g = new CDigraph();
+
+ inputGraph.eachNode(function(u, value) {
+ if (value === undefined) value = {};
+ g.addNode(u, {
+ width: value.width,
+ height: value.height
+ });
+ if (value.hasOwnProperty('rank')) {
+ g.node(u).prefRank = value.rank;
+ }
+ });
+
+ // Set up subgraphs
+ if (inputGraph.parent) {
+ inputGraph.nodes().forEach(function(u) {
+ g.parent(u, inputGraph.parent(u));
+ });
+ }
+
+ inputGraph.eachEdge(function(e, u, v, value) {
+ if (value === undefined) value = {};
+ var newValue = {
+ e: e,
+ minLen: value.minLen || 1,
+ width: value.width || 0,
+ height: value.height || 0,
+ points: []
+ };
+
+ g.addEdge(null, u, v, newValue);
+ });
+
+ // Initial graph attributes
+ var graphValue = inputGraph.graph() || {};
+ g.graph({
+ rankDir: graphValue.rankDir || config.rankDir,
+ orderRestarts: graphValue.orderRestarts
+ });
+
+ return g;
+ }
+
+ function run(inputGraph) {
+ var rankSep = self.rankSep();
+ var g;
+ try {
+ // Build internal graph
+ g = util.time('initLayoutGraph', initLayoutGraph)(inputGraph);
+
+ if (g.order() === 0) {
+ return g;
+ }
+
+ // Make space for edge labels
+ g.eachEdge(function(e, s, t, a) {
+ a.minLen *= 2;
+ });
+ self.rankSep(rankSep / 2);
+
+ // Determine the rank for each node. Nodes with a lower rank will appear
+ // above nodes of higher rank.
+ util.time('rank.run', rank.run)(g, config.rankSimplex);
+
+ // Normalize the graph by ensuring that every edge is proper (each edge has
+ // a length of 1). We achieve this by adding dummy nodes to long edges,
+ // thus shortening them.
+ util.time('normalize', normalize)(g);
+
+ // Order the nodes so that edge crossings are minimized.
+ util.time('order', order)(g, config.orderMaxSweeps);
+
+ // Find the x and y coordinates for every node in the graph.
+ util.time('position', position.run)(g);
+
+ // De-normalize the graph by removing dummy nodes and augmenting the
+ // original long edges with coordinate information.
+ util.time('undoNormalize', undoNormalize)(g);
+
+ // Reverses points for edges that are in a reversed state.
+ util.time('fixupEdgePoints', fixupEdgePoints)(g);
+
+ // Restore delete edges and reverse edges that were reversed in the rank
+ // phase.
+ util.time('rank.restoreEdges', rank.restoreEdges)(g);
+
+ // Construct final result graph and return it
+ return util.time('createFinalGraph', createFinalGraph)(g, inputGraph.isDirected());
+ } finally {
+ self.rankSep(rankSep);
+ }
+ }
+
+ /*
+ * This function is responsible for 'normalizing' the graph. The process of
+ * normalization ensures that no edge in the graph has spans more than one
+ * rank. To do this it inserts dummy nodes as needed and links them by adding
+ * dummy edges. This function keeps enough information in the dummy nodes and
+ * edges to ensure that the original graph can be reconstructed later.
+ *
+ * This method assumes that the input graph is cycle free.
+ */
+ function normalize(g) {
+ var dummyCount = 0;
+ g.eachEdge(function(e, s, t, a) {
+ var sourceRank = g.node(s).rank;
+ var targetRank = g.node(t).rank;
+ if (sourceRank + 1 < targetRank) {
+ for (var u = s, rank = sourceRank + 1, i = 0; rank < targetRank; ++rank, ++i) {
+ var v = '_D' + (++dummyCount);
+ var node = {
+ width: a.width,
+ height: a.height,
+ edge: { id: e, source: s, target: t, attrs: a },
+ rank: rank,
+ dummy: true
+ };
+
+ // If this node represents a bend then we will use it as a control
+ // point. For edges with 2 segments this will be the center dummy
+ // node. For edges with more than two segments, this will be the
+ // first and last dummy node.
+ if (i === 0) node.index = 0;
+ else if (rank + 1 === targetRank) node.index = 1;
+
+ g.addNode(v, node);
+ g.addEdge(null, u, v, {});
+ u = v;
+ }
+ g.addEdge(null, u, t, {});
+ g.delEdge(e);
+ }
+ });
+ }
+
+ /*
+ * Reconstructs the graph as it was before normalization. The positions of
+ * dummy nodes are used to build an array of points for the original 'long'
+ * edge. Dummy nodes and edges are removed.
+ */
+ function undoNormalize(g) {
+ g.eachNode(function(u, a) {
+ if (a.dummy) {
+ if ('index' in a) {
+ var edge = a.edge;
+ if (!g.hasEdge(edge.id)) {
+ g.addEdge(edge.id, edge.source, edge.target, edge.attrs);
+ }
+ var points = g.edge(edge.id).points;
+ points[a.index] = { x: a.x, y: a.y, ul: a.ul, ur: a.ur, dl: a.dl, dr: a.dr };
+ }
+ g.delNode(u);
+ }
+ });
+ }
+
+ /*
+ * For each edge that was reversed during the `acyclic` step, reverse its
+ * array of points.
+ */
+ function fixupEdgePoints(g) {
+ g.eachEdge(function(e, s, t, a) { if (a.reversed) a.points.reverse(); });
+ }
+
+ function createFinalGraph(g, isDirected) {
+ var out = isDirected ? new CDigraph() : new CGraph();
+ out.graph(g.graph());
+ g.eachNode(function(u, value) { out.addNode(u, value); });
+ g.eachNode(function(u) { out.parent(u, g.parent(u)); });
+ g.eachEdge(function(e, u, v, value) {
+ out.addEdge(value.e, u, v, value);
+ });
+
+ // Attach bounding box information
+ var maxX = 0, maxY = 0;
+ g.eachNode(function(u, value) {
+ if (!g.children(u).length) {
+ maxX = Math.max(maxX, value.x + value.width / 2);
+ maxY = Math.max(maxY, value.y + value.height / 2);
+ }
+ });
+ g.eachEdge(function(e, u, v, value) {
+ var maxXPoints = Math.max.apply(Math, value.points.map(function(p) { return p.x; }));
+ var maxYPoints = Math.max.apply(Math, value.points.map(function(p) { return p.y; }));
+ maxX = Math.max(maxX, maxXPoints + value.width / 2);
+ maxY = Math.max(maxY, maxYPoints + value.height / 2);
+ });
+ out.graph().width = maxX;
+ out.graph().height = maxY;
+
+ return out;
+ }
+
+ /*
+ * Given a function, a new function is returned that invokes the given
+ * function. The return value from the function is always the `self` object.
+ */
+ function delegateProperty(f) {
+ return function() {
+ if (!arguments.length) return f();
+ f.apply(null, arguments);
+ return self;
+ };
+ }
+};
+
+
+},{"./order":13,"./position":18,"./rank":19,"./util":26,"graphlib":28}],13:[function(require,module,exports){
+var util = require('./util'),
+ crossCount = require('./order/crossCount'),
+ initLayerGraphs = require('./order/initLayerGraphs'),
+ initOrder = require('./order/initOrder'),
+ sortLayer = require('./order/sortLayer');
+
+module.exports = order;
+
+// The maximum number of sweeps to perform before finishing the order phase.
+var DEFAULT_MAX_SWEEPS = 24;
+order.DEFAULT_MAX_SWEEPS = DEFAULT_MAX_SWEEPS;
+
+/*
+ * Runs the order phase with the specified `graph, `maxSweeps`, and
+ * `debugLevel`. If `maxSweeps` is not specified we use `DEFAULT_MAX_SWEEPS`.
+ * If `debugLevel` is not set we assume 0.
+ */
+function order(g, maxSweeps) {
+ if (arguments.length < 2) {
+ maxSweeps = DEFAULT_MAX_SWEEPS;
+ }
+
+ var restarts = g.graph().orderRestarts || 0;
+
+ var layerGraphs = initLayerGraphs(g);
+ // TODO: remove this when we add back support for ordering clusters
+ layerGraphs.forEach(function(lg) {
+ lg = lg.filterNodes(function(u) { return !g.children(u).length; });
+ });
+
+ var iters = 0,
+ currentBestCC,
+ allTimeBestCC = Number.MAX_VALUE,
+ allTimeBest = {};
+
+ function saveAllTimeBest() {
+ g.eachNode(function(u, value) { allTimeBest[u] = value.order; });
+ }
+
+ for (var j = 0; j < Number(restarts) + 1 && allTimeBestCC !== 0; ++j) {
+ currentBestCC = Number.MAX_VALUE;
+ initOrder(g, restarts > 0);
+
+ util.log(2, 'Order phase start cross count: ' + g.graph().orderInitCC);
+
+ var i, lastBest, cc;
+ for (i = 0, lastBest = 0; lastBest < 4 && i < maxSweeps && currentBestCC > 0; ++i, ++lastBest, ++iters) {
+ sweep(g, layerGraphs, i);
+ cc = crossCount(g);
+ if (cc < currentBestCC) {
+ lastBest = 0;
+ currentBestCC = cc;
+ if (cc < allTimeBestCC) {
+ saveAllTimeBest();
+ allTimeBestCC = cc;
+ }
+ }
+ util.log(3, 'Order phase start ' + j + ' iter ' + i + ' cross count: ' + cc);
+ }
+ }
+
+ Object.keys(allTimeBest).forEach(function(u) {
+ if (!g.children || !g.children(u).length) {
+ g.node(u).order = allTimeBest[u];
+ }
+ });
+ g.graph().orderCC = allTimeBestCC;
+
+ util.log(2, 'Order iterations: ' + iters);
+ util.log(2, 'Order phase best cross count: ' + g.graph().orderCC);
+}
+
+function predecessorWeights(g, nodes) {
+ var weights = {};
+ nodes.forEach(function(u) {
+ weights[u] = g.inEdges(u).map(function(e) {
+ return g.node(g.source(e)).order;
+ });
+ });
+ return weights;
+}
+
+function successorWeights(g, nodes) {
+ var weights = {};
+ nodes.forEach(function(u) {
+ weights[u] = g.outEdges(u).map(function(e) {
+ return g.node(g.target(e)).order;
+ });
+ });
+ return weights;
+}
+
+function sweep(g, layerGraphs, iter) {
+ if (iter % 2 === 0) {
+ sweepDown(g, layerGraphs, iter);
+ } else {
+ sweepUp(g, layerGraphs, iter);
+ }
+}
+
+function sweepDown(g, layerGraphs) {
+ var cg;
+ for (i = 1; i < layerGraphs.length; ++i) {
+ cg = sortLayer(layerGraphs[i], cg, predecessorWeights(g, layerGraphs[i].nodes()));
+ }
+}
+
+function sweepUp(g, layerGraphs) {
+ var cg;
+ for (i = layerGraphs.length - 2; i >= 0; --i) {
+ sortLayer(layerGraphs[i], cg, successorWeights(g, layerGraphs[i].nodes()));
+ }
+}
+
+},{"./order/crossCount":14,"./order/initLayerGraphs":15,"./order/initOrder":16,"./order/sortLayer":17,"./util":26}],14:[function(require,module,exports){
+var util = require('../util');
+
+module.exports = crossCount;
+
+/*
+ * Returns the cross count for the given graph.
+ */
+function crossCount(g) {
+ var cc = 0;
+ var ordering = util.ordering(g);
+ for (var i = 1; i < ordering.length; ++i) {
+ cc += twoLayerCrossCount(g, ordering[i-1], ordering[i]);
+ }
+ return cc;
+}
+
+/*
+ * This function searches through a ranked and ordered graph and counts the
+ * number of edges that cross. This algorithm is derived from:
+ *
+ * W. Barth et al., Bilayer Cross Counting, JGAA, 8(2) 179–194 (2004)
+ */
+function twoLayerCrossCount(g, layer1, layer2) {
+ var indices = [];
+ layer1.forEach(function(u) {
+ var nodeIndices = [];
+ g.outEdges(u).forEach(function(e) { nodeIndices.push(g.node(g.target(e)).order); });
+ nodeIndices.sort(function(x, y) { return x - y; });
+ indices = indices.concat(nodeIndices);
+ });
+
+ var firstIndex = 1;
+ while (firstIndex < layer2.length) firstIndex <<= 1;
+
+ var treeSize = 2 * firstIndex - 1;
+ firstIndex -= 1;
+
+ var tree = [];
+ for (var i = 0; i < treeSize; ++i) { tree[i] = 0; }
+
+ var cc = 0;
+ indices.forEach(function(i) {
+ var treeIndex = i + firstIndex;
+ ++tree[treeIndex];
+ while (treeIndex > 0) {
+ if (treeIndex % 2) {
+ cc += tree[treeIndex + 1];
+ }
+ treeIndex = (treeIndex - 1) >> 1;
+ ++tree[treeIndex];
+ }
+ });
+
+ return cc;
+}
+
+},{"../util":26}],15:[function(require,module,exports){
+var nodesFromList = require('graphlib').filter.nodesFromList,
+ /* jshint -W079 */
+ Set = require('cp-data').Set;
+
+module.exports = initLayerGraphs;
+
+/*
+ * This function takes a compound layered graph, g, and produces an array of
+ * layer graphs. Each entry in the array represents a subgraph of nodes
+ * relevant for performing crossing reduction on that layer.
+ */
+function initLayerGraphs(g) {
+ var ranks = [];
+
+ function dfs(u) {
+ if (u === null) {
+ g.children(u).forEach(function(v) { dfs(v); });
+ return;
+ }
+
+ var value = g.node(u);
+ value.minRank = ('rank' in value) ? value.rank : Number.MAX_VALUE;
+ value.maxRank = ('rank' in value) ? value.rank : Number.MIN_VALUE;
+ var uRanks = new Set();
+ g.children(u).forEach(function(v) {
+ var rs = dfs(v);
+ uRanks = Set.union([uRanks, rs]);
+ value.minRank = Math.min(value.minRank, g.node(v).minRank);
+ value.maxRank = Math.max(value.maxRank, g.node(v).maxRank);
+ });
+
+ if ('rank' in value) uRanks.add(value.rank);
+
+ uRanks.keys().forEach(function(r) {
+ if (!(r in ranks)) ranks[r] = [];
+ ranks[r].push(u);
+ });
+
+ return uRanks;
+ }
+ dfs(null);
+
+ var layerGraphs = [];
+ ranks.forEach(function(us, rank) {
+ layerGraphs[rank] = g.filterNodes(nodesFromList(us));
+ });
+
+ return layerGraphs;
+}
+
+},{"cp-data":5,"graphlib":28}],16:[function(require,module,exports){
+var crossCount = require('./crossCount'),
+ util = require('../util');
+
+module.exports = initOrder;
+
+/*
+ * Given a graph with a set of layered nodes (i.e. nodes that have a `rank`
+ * attribute) this function attaches an `order` attribute that uniquely
+ * arranges each node of each rank. If no constraint graph is provided the
+ * order of the nodes in each rank is entirely arbitrary.
+ */
+function initOrder(g, random) {
+ var layers = [];
+
+ g.eachNode(function(u, value) {
+ var layer = layers[value.rank];
+ if (g.children && g.children(u).length > 0) return;
+ if (!layer) {
+ layer = layers[value.rank] = [];
+ }
+ layer.push(u);
+ });
+
+ layers.forEach(function(layer) {
+ if (random) {
+ util.shuffle(layer);
+ }
+ layer.forEach(function(u, i) {
+ g.node(u).order = i;
+ });
+ });
+
+ var cc = crossCount(g);
+ g.graph().orderInitCC = cc;
+ g.graph().orderCC = Number.MAX_VALUE;
+}
+
+},{"../util":26,"./crossCount":14}],17:[function(require,module,exports){
+var util = require('../util');
+/*
+ Digraph = require('graphlib').Digraph,
+ topsort = require('graphlib').alg.topsort,
+ nodesFromList = require('graphlib').filter.nodesFromList;
+*/
+
+module.exports = sortLayer;
+
+/*
+function sortLayer(g, cg, weights) {
+ var result = sortLayerSubgraph(g, null, cg, weights);
+ result.list.forEach(function(u, i) {
+ g.node(u).order = i;
+ });
+ return result.constraintGraph;
+}
+*/
+
+function sortLayer(g, cg, weights) {
+ var ordering = [];
+ var bs = {};
+ g.eachNode(function(u, value) {
+ ordering[value.order] = u;
+ var ws = weights[u];
+ if (ws.length) {
+ bs[u] = util.sum(ws) / ws.length;
+ }
+ });
+
+ var toSort = g.nodes().filter(function(u) { return bs[u] !== undefined; });
+ toSort.sort(function(x, y) {
+ return bs[x] - bs[y] || g.node(x).order - g.node(y).order;
+ });
+
+ for (var i = 0, j = 0, jl = toSort.length; j < jl; ++i) {
+ if (bs[ordering[i]] !== undefined) {
+ g.node(toSort[j++]).order = i;
+ }
+ }
+}
+
+// TOOD: re-enable constrained sorting once we have a strategy for handling
+// undefined barycenters.
+/*
+function sortLayerSubgraph(g, sg, cg, weights) {
+ cg = cg ? cg.filterNodes(nodesFromList(g.children(sg))) : new Digraph();
+
+ var nodeData = {};
+ g.children(sg).forEach(function(u) {
+ if (g.children(u).length) {
+ nodeData[u] = sortLayerSubgraph(g, u, cg, weights);
+ nodeData[u].firstSG = u;
+ nodeData[u].lastSG = u;
+ } else {
+ var ws = weights[u];
+ nodeData[u] = {
+ degree: ws.length,
+ barycenter: ws.length > 0 ? util.sum(ws) / ws.length : 0,
+ list: [u]
+ };
+ }
+ });
+
+ resolveViolatedConstraints(g, cg, nodeData);
+
+ var keys = Object.keys(nodeData);
+ keys.sort(function(x, y) {
+ return nodeData[x].barycenter - nodeData[y].barycenter;
+ });
+
+ var result = keys.map(function(u) { return nodeData[u]; })
+ .reduce(function(lhs, rhs) { return mergeNodeData(g, lhs, rhs); });
+ return result;
+}
+
+/*
+function mergeNodeData(g, lhs, rhs) {
+ var cg = mergeDigraphs(lhs.constraintGraph, rhs.constraintGraph);
+
+ if (lhs.lastSG !== undefined && rhs.firstSG !== undefined) {
+ if (cg === undefined) {
+ cg = new Digraph();
+ }
+ if (!cg.hasNode(lhs.lastSG)) { cg.addNode(lhs.lastSG); }
+ cg.addNode(rhs.firstSG);
+ cg.addEdge(null, lhs.lastSG, rhs.firstSG);
+ }
+
+ return {
+ degree: lhs.degree + rhs.degree,
+ barycenter: (lhs.barycenter * lhs.degree + rhs.barycenter * rhs.degree) /
+ (lhs.degree + rhs.degree),
+ list: lhs.list.concat(rhs.list),
+ firstSG: lhs.firstSG !== undefined ? lhs.firstSG : rhs.firstSG,
+ lastSG: rhs.lastSG !== undefined ? rhs.lastSG : lhs.lastSG,
+ constraintGraph: cg
+ };
+}
+
+function mergeDigraphs(lhs, rhs) {
+ if (lhs === undefined) return rhs;
+ if (rhs === undefined) return lhs;
+
+ lhs = lhs.copy();
+ rhs.nodes().forEach(function(u) { lhs.addNode(u); });
+ rhs.edges().forEach(function(e, u, v) { lhs.addEdge(null, u, v); });
+ return lhs;
+}
+
+function resolveViolatedConstraints(g, cg, nodeData) {
+ // Removes nodes `u` and `v` from `cg` and makes any edges incident on them
+ // incident on `w` instead.
+ function collapseNodes(u, v, w) {
+ // TODO original paper removes self loops, but it is not obvious when this would happen
+ cg.inEdges(u).forEach(function(e) {
+ cg.delEdge(e);
+ cg.addEdge(null, cg.source(e), w);
+ });
+
+ cg.outEdges(v).forEach(function(e) {
+ cg.delEdge(e);
+ cg.addEdge(null, w, cg.target(e));
+ });
+
+ cg.delNode(u);
+ cg.delNode(v);
+ }
+
+ var violated;
+ while ((violated = findViolatedConstraint(cg, nodeData)) !== undefined) {
+ var source = cg.source(violated),
+ target = cg.target(violated);
+
+ var v;
+ while ((v = cg.addNode(null)) && g.hasNode(v)) {
+ cg.delNode(v);
+ }
+
+ // Collapse barycenter and list
+ nodeData[v] = mergeNodeData(g, nodeData[source], nodeData[target]);
+ delete nodeData[source];
+ delete nodeData[target];
+
+ collapseNodes(source, target, v);
+ if (cg.incidentEdges(v).length === 0) { cg.delNode(v); }
+ }
+}
+
+function findViolatedConstraint(cg, nodeData) {
+ var us = topsort(cg);
+ for (var i = 0; i < us.length; ++i) {
+ var u = us[i];
+ var inEdges = cg.inEdges(u);
+ for (var j = 0; j < inEdges.length; ++j) {
+ var e = inEdges[j];
+ if (nodeData[cg.source(e)].barycenter >= nodeData[u].barycenter) {
+ return e;
+ }
+ }
+ }
+}
+*/
+
+},{"../util":26}],18:[function(require,module,exports){
+var util = require('./util');
+
+/*
+ * The algorithms here are based on Brandes and Köpf, "Fast and Simple
+ * Horizontal Coordinate Assignment".
+ */
+module.exports = function() {
+ // External configuration
+ var config = {
+ nodeSep: 50,
+ edgeSep: 10,
+ universalSep: null,
+ rankSep: 30
+ };
+
+ var self = {};
+
+ self.nodeSep = util.propertyAccessor(self, config, 'nodeSep');
+ self.edgeSep = util.propertyAccessor(self, config, 'edgeSep');
+ // If not null this separation value is used for all nodes and edges
+ // regardless of their widths. `nodeSep` and `edgeSep` are ignored with this
+ // option.
+ self.universalSep = util.propertyAccessor(self, config, 'universalSep');
+ self.rankSep = util.propertyAccessor(self, config, 'rankSep');
+ self.debugLevel = util.propertyAccessor(self, config, 'debugLevel');
+
+ self.run = run;
+
+ return self;
+
+ function run(g) {
+ g = g.filterNodes(util.filterNonSubgraphs(g));
+
+ var layering = util.ordering(g);
+
+ var conflicts = findConflicts(g, layering);
+
+ var xss = {};
+ ['u', 'd'].forEach(function(vertDir) {
+ if (vertDir === 'd') layering.reverse();
+
+ ['l', 'r'].forEach(function(horizDir) {
+ if (horizDir === 'r') reverseInnerOrder(layering);
+
+ var dir = vertDir + horizDir;
+ var align = verticalAlignment(g, layering, conflicts, vertDir === 'u' ? 'predecessors' : 'successors');
+ xss[dir]= horizontalCompaction(g, layering, align.pos, align.root, align.align);
+
+ if (config.debugLevel >= 3)
+ debugPositioning(vertDir + horizDir, g, layering, xss[dir]);
+
+ if (horizDir === 'r') flipHorizontally(xss[dir]);
+
+ if (horizDir === 'r') reverseInnerOrder(layering);
+ });
+
+ if (vertDir === 'd') layering.reverse();
+ });
+
+ balance(g, layering, xss);
+
+ g.eachNode(function(v) {
+ var xs = [];
+ for (var alignment in xss) {
+ var alignmentX = xss[alignment][v];
+ posXDebug(alignment, g, v, alignmentX);
+ xs.push(alignmentX);
+ }
+ xs.sort(function(x, y) { return x - y; });
+ posX(g, v, (xs[1] + xs[2]) / 2);
+ });
+
+ // Align y coordinates with ranks
+ var y = 0, reverseY = g.graph().rankDir === 'BT' || g.graph().rankDir === 'RL';
+ layering.forEach(function(layer) {
+ var maxHeight = util.max(layer.map(function(u) { return height(g, u); }));
+ y += maxHeight / 2;
+ layer.forEach(function(u) {
+ posY(g, u, reverseY ? -y : y);
+ });
+ y += maxHeight / 2 + config.rankSep;
+ });
+
+ // Translate layout so that top left corner of bounding rectangle has
+ // coordinate (0, 0).
+ var minX = util.min(g.nodes().map(function(u) { return posX(g, u) - width(g, u) / 2; }));
+ var minY = util.min(g.nodes().map(function(u) { return posY(g, u) - height(g, u) / 2; }));
+ g.eachNode(function(u) {
+ posX(g, u, posX(g, u) - minX);
+ posY(g, u, posY(g, u) - minY);
+ });
+ }
+
+ /*
+ * Generate an ID that can be used to represent any undirected edge that is
+ * incident on `u` and `v`.
+ */
+ function undirEdgeId(u, v) {
+ return u < v
+ ? u.toString().length + ':' + u + '-' + v
+ : v.toString().length + ':' + v + '-' + u;
+ }
+
+ function findConflicts(g, layering) {
+ var conflicts = {}, // Set of conflicting edge ids
+ pos = {}, // Position of node in its layer
+ prevLayer,
+ currLayer,
+ k0, // Position of the last inner segment in the previous layer
+ l, // Current position in the current layer (for iteration up to `l1`)
+ k1; // Position of the next inner segment in the previous layer or
+ // the position of the last element in the previous layer
+
+ if (layering.length <= 2) return conflicts;
+
+ function updateConflicts(v) {
+ var k = pos[v];
+ if (k < k0 || k > k1) {
+ conflicts[undirEdgeId(currLayer[l], v)] = true;
+ }
+ }
+
+ layering[1].forEach(function(u, i) { pos[u] = i; });
+ for (var i = 1; i < layering.length - 1; ++i) {
+ prevLayer = layering[i];
+ currLayer = layering[i+1];
+ k0 = 0;
+ l = 0;
+
+ // Scan current layer for next node that is incident to an inner segement
+ // between layering[i+1] and layering[i].
+ for (var l1 = 0; l1 < currLayer.length; ++l1) {
+ var u = currLayer[l1]; // Next inner segment in the current layer or
+ // last node in the current layer
+ pos[u] = l1;
+ k1 = undefined;
+
+ if (g.node(u).dummy) {
+ var uPred = g.predecessors(u)[0];
+ // Note: In the case of self loops and sideways edges it is possible
+ // for a dummy not to have a predecessor.
+ if (uPred !== undefined && g.node(uPred).dummy)
+ k1 = pos[uPred];
+ }
+ if (k1 === undefined && l1 === currLayer.length - 1)
+ k1 = prevLayer.length - 1;
+
+ if (k1 !== undefined) {
+ for (; l <= l1; ++l) {
+ g.predecessors(currLayer[l]).forEach(updateConflicts);
+ }
+ k0 = k1;
+ }
+ }
+ }
+
+ return conflicts;
+ }
+
+ function verticalAlignment(g, layering, conflicts, relationship) {
+ var pos = {}, // Position for a node in its layer
+ root = {}, // Root of the block that the node participates in
+ align = {}; // Points to the next node in the block or, if the last
+ // element in the block, points to the first block's root
+
+ layering.forEach(function(layer) {
+ layer.forEach(function(u, i) {
+ root[u] = u;
+ align[u] = u;
+ pos[u] = i;
+ });
+ });
+
+ layering.forEach(function(layer) {
+ var prevIdx = -1;
+ layer.forEach(function(v) {
+ var related = g[relationship](v), // Adjacent nodes from the previous layer
+ mid; // The mid point in the related array
+
+ if (related.length > 0) {
+ related.sort(function(x, y) { return pos[x] - pos[y]; });
+ mid = (related.length - 1) / 2;
+ related.slice(Math.floor(mid), Math.ceil(mid) + 1).forEach(function(u) {
+ if (align[v] === v) {
+ if (!conflicts[undirEdgeId(u, v)] && prevIdx < pos[u]) {
+ align[u] = v;
+ align[v] = root[v] = root[u];
+ prevIdx = pos[u];
+ }
+ }
+ });
+ }
+ });
+ });
+
+ return { pos: pos, root: root, align: align };
+ }
+
+ // This function deviates from the standard BK algorithm in two ways. First
+ // it takes into account the size of the nodes. Second it includes a fix to
+ // the original algorithm that is described in Carstens, "Node and Label
+ // Placement in a Layered Layout Algorithm".
+ function horizontalCompaction(g, layering, pos, root, align) {
+ var sink = {}, // Mapping of node id -> sink node id for class
+ maybeShift = {}, // Mapping of sink node id -> { class node id, min shift }
+ shift = {}, // Mapping of sink node id -> shift
+ pred = {}, // Mapping of node id -> predecessor node (or null)
+ xs = {}; // Calculated X positions
+
+ layering.forEach(function(layer) {
+ layer.forEach(function(u, i) {
+ sink[u] = u;
+ maybeShift[u] = {};
+ if (i > 0)
+ pred[u] = layer[i - 1];
+ });
+ });
+
+ function updateShift(toShift, neighbor, delta) {
+ if (!(neighbor in maybeShift[toShift])) {
+ maybeShift[toShift][neighbor] = delta;
+ } else {
+ maybeShift[toShift][neighbor] = Math.min(maybeShift[toShift][neighbor], delta);
+ }
+ }
+
+ function placeBlock(v) {
+ if (!(v in xs)) {
+ xs[v] = 0;
+ var w = v;
+ do {
+ if (pos[w] > 0) {
+ var u = root[pred[w]];
+ placeBlock(u);
+ if (sink[v] === v) {
+ sink[v] = sink[u];
+ }
+ var delta = sep(g, pred[w]) + sep(g, w);
+ if (sink[v] !== sink[u]) {
+ updateShift(sink[u], sink[v], xs[v] - xs[u] - delta);
+ } else {
+ xs[v] = Math.max(xs[v], xs[u] + delta);
+ }
+ }
+ w = align[w];
+ } while (w !== v);
+ }
+ }
+
+ // Root coordinates relative to sink
+ util.values(root).forEach(function(v) {
+ placeBlock(v);
+ });
+
+ // Absolute coordinates
+ // There is an assumption here that we've resolved shifts for any classes
+ // that begin at an earlier layer. We guarantee this by visiting layers in
+ // order.
+ layering.forEach(function(layer) {
+ layer.forEach(function(v) {
+ xs[v] = xs[root[v]];
+ if (v === root[v] && v === sink[v]) {
+ var minShift = 0;
+ if (v in maybeShift && Object.keys(maybeShift[v]).length > 0) {
+ minShift = util.min(Object.keys(maybeShift[v])
+ .map(function(u) {
+ return maybeShift[v][u] + (u in shift ? shift[u] : 0);
+ }
+ ));
+ }
+ shift[v] = minShift;
+ }
+ });
+ });
+
+ layering.forEach(function(layer) {
+ layer.forEach(function(v) {
+ xs[v] += shift[sink[root[v]]] || 0;
+ });
+ });
+
+ return xs;
+ }
+
+ function findMinCoord(g, layering, xs) {
+ return util.min(layering.map(function(layer) {
+ var u = layer[0];
+ return xs[u];
+ }));
+ }
+
+ function findMaxCoord(g, layering, xs) {
+ return util.max(layering.map(function(layer) {
+ var u = layer[layer.length - 1];
+ return xs[u];
+ }));
+ }
+
+ function balance(g, layering, xss) {
+ var min = {}, // Min coordinate for the alignment
+ max = {}, // Max coordinate for the alginment
+ smallestAlignment,
+ shift = {}; // Amount to shift a given alignment
+
+ function updateAlignment(v) {
+ xss[alignment][v] += shift[alignment];
+ }
+
+ var smallest = Number.POSITIVE_INFINITY;
+ for (var alignment in xss) {
+ var xs = xss[alignment];
+ min[alignment] = findMinCoord(g, layering, xs);
+ max[alignment] = findMaxCoord(g, layering, xs);
+ var w = max[alignment] - min[alignment];
+ if (w < smallest) {
+ smallest = w;
+ smallestAlignment = alignment;
+ }
+ }
+
+ // Determine how much to adjust positioning for each alignment
+ ['u', 'd'].forEach(function(vertDir) {
+ ['l', 'r'].forEach(function(horizDir) {
+ var alignment = vertDir + horizDir;
+ shift[alignment] = horizDir === 'l'
+ ? min[smallestAlignment] - min[alignment]
+ : max[smallestAlignment] - max[alignment];
+ });
+ });
+
+ // Find average of medians for xss array
+ for (alignment in xss) {
+ g.eachNode(updateAlignment);
+ }
+ }
+
+ function flipHorizontally(xs) {
+ for (var u in xs) {
+ xs[u] = -xs[u];
+ }
+ }
+
+ function reverseInnerOrder(layering) {
+ layering.forEach(function(layer) {
+ layer.reverse();
+ });
+ }
+
+ function width(g, u) {
+ switch (g.graph().rankDir) {
+ case 'LR': return g.node(u).height;
+ case 'RL': return g.node(u).height;
+ default: return g.node(u).width;
+ }
+ }
+
+ function height(g, u) {
+ switch(g.graph().rankDir) {
+ case 'LR': return g.node(u).width;
+ case 'RL': return g.node(u).width;
+ default: return g.node(u).height;
+ }
+ }
+
+ function sep(g, u) {
+ if (config.universalSep !== null) {
+ return config.universalSep;
+ }
+ var w = width(g, u);
+ var s = g.node(u).dummy ? config.edgeSep : config.nodeSep;
+ return (w + s) / 2;
+ }
+
+ function posX(g, u, x) {
+ if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
+ if (arguments.length < 3) {
+ return g.node(u).y;
+ } else {
+ g.node(u).y = x;
+ }
+ } else {
+ if (arguments.length < 3) {
+ return g.node(u).x;
+ } else {
+ g.node(u).x = x;
+ }
+ }
+ }
+
+ function posXDebug(name, g, u, x) {
+ if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
+ if (arguments.length < 3) {
+ return g.node(u)[name];
+ } else {
+ g.node(u)[name] = x;
+ }
+ } else {
+ if (arguments.length < 3) {
+ return g.node(u)[name];
+ } else {
+ g.node(u)[name] = x;
+ }
+ }
+ }
+
+ function posY(g, u, y) {
+ if (g.graph().rankDir === 'LR' || g.graph().rankDir === 'RL') {
+ if (arguments.length < 3) {
+ return g.node(u).x;
+ } else {
+ g.node(u).x = y;
+ }
+ } else {
+ if (arguments.length < 3) {
+ return g.node(u).y;
+ } else {
+ g.node(u).y = y;
+ }
+ }
+ }
+
+ function debugPositioning(align, g, layering, xs) {
+ layering.forEach(function(l, li) {
+ var u, xU;
+ l.forEach(function(v) {
+ var xV = xs[v];
+ if (u) {
+ var s = sep(g, u) + sep(g, v);
+ if (xV - xU < s)
+ console.log('Position phase: sep violation. Align: ' + align + '. Layer: ' + li + '. ' +
+ 'U: ' + u + ' V: ' + v + '. Actual sep: ' + (xV - xU) + ' Expected sep: ' + s);
+ }
+ u = v;
+ xU = xV;
+ });
+ });
+ }
+};
+
+},{"./util":26}],19:[function(require,module,exports){
+var util = require('./util'),
+ acyclic = require('./rank/acyclic'),
+ initRank = require('./rank/initRank'),
+ feasibleTree = require('./rank/feasibleTree'),
+ constraints = require('./rank/constraints'),
+ simplex = require('./rank/simplex'),
+ components = require('graphlib').alg.components,
+ filter = require('graphlib').filter;
+
+exports.run = run;
+exports.restoreEdges = restoreEdges;
+
+/*
+ * Heuristic function that assigns a rank to each node of the input graph with
+ * the intent of minimizing edge lengths, while respecting the `minLen`
+ * attribute of incident edges.
+ *
+ * Prerequisites:
+ *
+ * * Each edge in the input graph must have an assigned 'minLen' attribute
+ */
+function run(g, useSimplex) {
+ expandSelfLoops(g);
+
+ // If there are rank constraints on nodes, then build a new graph that
+ // encodes the constraints.
+ util.time('constraints.apply', constraints.apply)(g);
+
+ expandSidewaysEdges(g);
+
+ // Reverse edges to get an acyclic graph, we keep the graph in an acyclic
+ // state until the very end.
+ util.time('acyclic', acyclic)(g);
+
+ // Convert the graph into a flat graph for ranking
+ var flatGraph = g.filterNodes(util.filterNonSubgraphs(g));
+
+ // Assign an initial ranking using DFS.
+ initRank(flatGraph);
+
+ // For each component improve the assigned ranks.
+ components(flatGraph).forEach(function(cmpt) {
+ var subgraph = flatGraph.filterNodes(filter.nodesFromList(cmpt));
+ rankComponent(subgraph, useSimplex);
+ });
+
+ // Relax original constraints
+ util.time('constraints.relax', constraints.relax(g));
+
+ // When handling nodes with constrained ranks it is possible to end up with
+ // edges that point to previous ranks. Most of the subsequent algorithms assume
+ // that edges are pointing to successive ranks only. Here we reverse any "back
+ // edges" and mark them as such. The acyclic algorithm will reverse them as a
+ // post processing step.
+ util.time('reorientEdges', reorientEdges)(g);
+}
+
+function restoreEdges(g) {
+ acyclic.undo(g);
+}
+
+/*
+ * Expand self loops into three dummy nodes. One will sit above the incident
+ * node, one will be at the same level, and one below. The result looks like:
+ *
+ * /--<--x--->--\
+ * node y
+ * \--<--z--->--/
+ *
+ * Dummy nodes x, y, z give us the shape of a loop and node y is where we place
+ * the label.
+ *
+ * TODO: consolidate knowledge of dummy node construction.
+ * TODO: support minLen = 2
+ */
+function expandSelfLoops(g) {
+ g.eachEdge(function(e, u, v, a) {
+ if (u === v) {
+ var x = addDummyNode(g, e, u, v, a, 0, false),
+ y = addDummyNode(g, e, u, v, a, 1, true),
+ z = addDummyNode(g, e, u, v, a, 2, false);
+ g.addEdge(null, x, u, {minLen: 1, selfLoop: true});
+ g.addEdge(null, x, y, {minLen: 1, selfLoop: true});
+ g.addEdge(null, u, z, {minLen: 1, selfLoop: true});
+ g.addEdge(null, y, z, {minLen: 1, selfLoop: true});
+ g.delEdge(e);
+ }
+ });
+}
+
+function expandSidewaysEdges(g) {
+ g.eachEdge(function(e, u, v, a) {
+ if (u === v) {
+ var origEdge = a.originalEdge,
+ dummy = addDummyNode(g, origEdge.e, origEdge.u, origEdge.v, origEdge.value, 0, true);
+ g.addEdge(null, u, dummy, {minLen: 1});
+ g.addEdge(null, dummy, v, {minLen: 1});
+ g.delEdge(e);
+ }
+ });
+}
+
+function addDummyNode(g, e, u, v, a, index, isLabel) {
+ return g.addNode(null, {
+ width: isLabel ? a.width : 0,
+ height: isLabel ? a.height : 0,
+ edge: { id: e, source: u, target: v, attrs: a },
+ dummy: true,
+ index: index
+ });
+}
+
+function reorientEdges(g) {
+ g.eachEdge(function(e, u, v, value) {
+ if (g.node(u).rank > g.node(v).rank) {
+ g.delEdge(e);
+ value.reversed = true;
+ g.addEdge(e, v, u, value);
+ }
+ });
+}
+
+function rankComponent(subgraph, useSimplex) {
+ var spanningTree = feasibleTree(subgraph);
+
+ if (useSimplex) {
+ util.log(1, 'Using network simplex for ranking');
+ simplex(subgraph, spanningTree);
+ }
+ normalize(subgraph);
+}
+
+function normalize(g) {
+ var m = util.min(g.nodes().map(function(u) { return g.node(u).rank; }));
+ g.eachNode(function(u, node) { node.rank -= m; });
+}
+
+},{"./rank/acyclic":20,"./rank/constraints":21,"./rank/feasibleTree":22,"./rank/initRank":23,"./rank/simplex":25,"./util":26,"graphlib":28}],20:[function(require,module,exports){
+var util = require('../util');
+
+module.exports = acyclic;
+module.exports.undo = undo;
+
+/*
+ * This function takes a directed graph that may have cycles and reverses edges
+ * as appropriate to break these cycles. Each reversed edge is assigned a
+ * `reversed` attribute with the value `true`.
+ *
+ * There should be no self loops in the graph.
+ */
+function acyclic(g) {
+ var onStack = {},
+ visited = {},
+ reverseCount = 0;
+
+ function dfs(u) {
+ if (u in visited) return;
+ visited[u] = onStack[u] = true;
+ g.outEdges(u).forEach(function(e) {
+ var t = g.target(e),
+ value;
+
+ if (u === t) {
+ console.error('Warning: found self loop "' + e + '" for node "' + u + '"');
+ } else if (t in onStack) {
+ value = g.edge(e);
+ g.delEdge(e);
+ value.reversed = true;
+ ++reverseCount;
+ g.addEdge(e, t, u, value);
+ } else {
+ dfs(t);
+ }
+ });
+
+ delete onStack[u];
+ }
+
+ g.eachNode(function(u) { dfs(u); });
+
+ util.log(2, 'Acyclic Phase: reversed ' + reverseCount + ' edge(s)');
+
+ return reverseCount;
+}
+
+/*
+ * Given a graph that has had the acyclic operation applied, this function
+ * undoes that operation. More specifically, any edge with the `reversed`
+ * attribute is again reversed to restore the original direction of the edge.
+ */
+function undo(g) {
+ g.eachEdge(function(e, s, t, a) {
+ if (a.reversed) {
+ delete a.reversed;
+ g.delEdge(e);
+ g.addEdge(e, t, s, a);
+ }
+ });
+}
+
+},{"../util":26}],21:[function(require,module,exports){
+exports.apply = function(g) {
+ function dfs(sg) {
+ var rankSets = {};
+ g.children(sg).forEach(function(u) {
+ if (g.children(u).length) {
+ dfs(u);
+ return;
+ }
+
+ var value = g.node(u),
+ prefRank = value.prefRank;
+ if (prefRank !== undefined) {
+ if (!checkSupportedPrefRank(prefRank)) { return; }
+
+ if (!(prefRank in rankSets)) {
+ rankSets.prefRank = [u];
+ } else {
+ rankSets.prefRank.push(u);
+ }
+
+ var newU = rankSets[prefRank];
+ if (newU === undefined) {
+ newU = rankSets[prefRank] = g.addNode(null, { originalNodes: [] });
+ g.parent(newU, sg);
+ }
+
+ redirectInEdges(g, u, newU, prefRank === 'min');
+ redirectOutEdges(g, u, newU, prefRank === 'max');
+
+ // Save original node and remove it from reduced graph
+ g.node(newU).originalNodes.push({ u: u, value: value, parent: sg });
+ g.delNode(u);
+ }
+ });
+
+ addLightEdgesFromMinNode(g, sg, rankSets.min);
+ addLightEdgesToMaxNode(g, sg, rankSets.max);
+ }
+
+ dfs(null);
+};
+
+function checkSupportedPrefRank(prefRank) {
+ if (prefRank !== 'min' && prefRank !== 'max' && prefRank.indexOf('same_') !== 0) {
+ console.error('Unsupported rank type: ' + prefRank);
+ return false;
+ }
+ return true;
+}
+
+function redirectInEdges(g, u, newU, reverse) {
+ g.inEdges(u).forEach(function(e) {
+ var origValue = g.edge(e),
+ value;
+ if (origValue.originalEdge) {
+ value = origValue;
+ } else {
+ value = {
+ originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue },
+ minLen: g.edge(e).minLen
+ };
+ }
+
+ // Do not reverse edges for self-loops.
+ if (origValue.selfLoop) {
+ reverse = false;
+ }
+
+ if (reverse) {
+ // Ensure that all edges to min are reversed
+ g.addEdge(null, newU, g.source(e), value);
+ value.reversed = true;
+ } else {
+ g.addEdge(null, g.source(e), newU, value);
+ }
+ });
+}
+
+function redirectOutEdges(g, u, newU, reverse) {
+ g.outEdges(u).forEach(function(e) {
+ var origValue = g.edge(e),
+ value;
+ if (origValue.originalEdge) {
+ value = origValue;
+ } else {
+ value = {
+ originalEdge: { e: e, u: g.source(e), v: g.target(e), value: origValue },
+ minLen: g.edge(e).minLen
+ };
+ }
+
+ // Do not reverse edges for self-loops.
+ if (origValue.selfLoop) {
+ reverse = false;
+ }
+
+ if (reverse) {
+ // Ensure that all edges from max are reversed
+ g.addEdge(null, g.target(e), newU, value);
+ value.reversed = true;
+ } else {
+ g.addEdge(null, newU, g.target(e), value);
+ }
+ });
+}
+
+function addLightEdgesFromMinNode(g, sg, minNode) {
+ if (minNode !== undefined) {
+ g.children(sg).forEach(function(u) {
+ // The dummy check ensures we don't add an edge if the node is involved
+ // in a self loop or sideways edge.
+ if (u !== minNode && !g.outEdges(minNode, u).length && !g.node(u).dummy) {
+ g.addEdge(null, minNode, u, { minLen: 0 });
+ }
+ });
+ }
+}
+
+function addLightEdgesToMaxNode(g, sg, maxNode) {
+ if (maxNode !== undefined) {
+ g.children(sg).forEach(function(u) {
+ // The dummy check ensures we don't add an edge if the node is involved
+ // in a self loop or sideways edge.
+ if (u !== maxNode && !g.outEdges(u, maxNode).length && !g.node(u).dummy) {
+ g.addEdge(null, u, maxNode, { minLen: 0 });
+ }
+ });
+ }
+}
+
+/*
+ * This function "relaxes" the constraints applied previously by the "apply"
+ * function. It expands any nodes that were collapsed and assigns the rank of
+ * the collapsed node to each of the expanded nodes. It also restores the
+ * original edges and removes any dummy edges pointing at the collapsed nodes.
+ *
+ * Note that the process of removing collapsed nodes also removes dummy edges
+ * automatically.
+ */
+exports.relax = function(g) {
+ // Save original edges
+ var originalEdges = [];
+ g.eachEdge(function(e, u, v, value) {
+ var originalEdge = value.originalEdge;
+ if (originalEdge) {
+ originalEdges.push(originalEdge);
+ }
+ });
+
+ // Expand collapsed nodes
+ g.eachNode(function(u, value) {
+ var originalNodes = value.originalNodes;
+ if (originalNodes) {
+ originalNodes.forEach(function(originalNode) {
+ originalNode.value.rank = value.rank;
+ g.addNode(originalNode.u, originalNode.value);
+ g.parent(originalNode.u, originalNode.parent);
+ });
+ g.delNode(u);
+ }
+ });
+
+ // Restore original edges
+ originalEdges.forEach(function(edge) {
+ g.addEdge(edge.e, edge.u, edge.v, edge.value);
+ });
+};
+
+},{}],22:[function(require,module,exports){
+/* jshint -W079 */
+var Set = require('cp-data').Set,
+/* jshint +W079 */
+ Digraph = require('graphlib').Digraph,
+ util = require('../util');
+
+module.exports = feasibleTree;
+
+/*
+ * Given an acyclic graph with each node assigned a `rank` attribute, this
+ * function constructs and returns a spanning tree. This function may reduce
+ * the length of some edges from the initial rank assignment while maintaining
+ * the `minLen` specified by each edge.
+ *
+ * Prerequisites:
+ *
+ * * The input graph is acyclic
+ * * Each node in the input graph has an assigned `rank` attribute
+ * * Each edge in the input graph has an assigned `minLen` attribute
+ *
+ * Outputs:
+ *
+ * A feasible spanning tree for the input graph (i.e. a spanning tree that
+ * respects each graph edge's `minLen` attribute) represented as a Digraph with
+ * a `root` attribute on graph.
+ *
+ * Nodes have the same id and value as that in the input graph.
+ *
+ * Edges in the tree have arbitrarily assigned ids. The attributes for edges
+ * include `reversed`. `reversed` indicates that the edge is a
+ * back edge in the input graph.
+ */
+function feasibleTree(g) {
+ var remaining = new Set(g.nodes()),
+ tree = new Digraph();
+
+ if (remaining.size() === 1) {
+ var root = g.nodes()[0];
+ tree.addNode(root, {});
+ tree.graph({ root: root });
+ return tree;
+ }
+
+ function addTightEdges(v) {
+ var continueToScan = true;
+ g.predecessors(v).forEach(function(u) {
+ if (remaining.has(u) && !slack(g, u, v)) {
+ if (remaining.has(v)) {
+ tree.addNode(v, {});
+ remaining.remove(v);
+ tree.graph({ root: v });
+ }
+
+ tree.addNode(u, {});
+ tree.addEdge(null, u, v, { reversed: true });
+ remaining.remove(u);
+ addTightEdges(u);
+ continueToScan = false;
+ }
+ });
+
+ g.successors(v).forEach(function(w) {
+ if (remaining.has(w) && !slack(g, v, w)) {
+ if (remaining.has(v)) {
+ tree.addNode(v, {});
+ remaining.remove(v);
+ tree.graph({ root: v });
+ }
+
+ tree.addNode(w, {});
+ tree.addEdge(null, v, w, {});
+ remaining.remove(w);
+ addTightEdges(w);
+ continueToScan = false;
+ }
+ });
+ return continueToScan;
+ }
+
+ function createTightEdge() {
+ var minSlack = Number.MAX_VALUE;
+ remaining.keys().forEach(function(v) {
+ g.predecessors(v).forEach(function(u) {
+ if (!remaining.has(u)) {
+ var edgeSlack = slack(g, u, v);
+ if (Math.abs(edgeSlack) < Math.abs(minSlack)) {
+ minSlack = -edgeSlack;
+ }
+ }
+ });
+
+ g.successors(v).forEach(function(w) {
+ if (!remaining.has(w)) {
+ var edgeSlack = slack(g, v, w);
+ if (Math.abs(edgeSlack) < Math.abs(minSlack)) {
+ minSlack = edgeSlack;
+ }
+ }
+ });
+ });
+
+ tree.eachNode(function(u) { g.node(u).rank -= minSlack; });
+ }
+
+ while (remaining.size()) {
+ var nodesToSearch = !tree.order() ? remaining.keys() : tree.nodes();
+ for (var i = 0, il = nodesToSearch.length;
+ i < il && addTightEdges(nodesToSearch[i]);
+ ++i);
+ if (remaining.size()) {
+ createTightEdge();
+ }
+ }
+
+ return tree;
+}
+
+function slack(g, u, v) {
+ var rankDiff = g.node(v).rank - g.node(u).rank;
+ var maxMinLen = util.max(g.outEdges(u, v)
+ .map(function(e) { return g.edge(e).minLen; }));
+ return rankDiff - maxMinLen;
+}
+
+},{"../util":26,"cp-data":5,"graphlib":28}],23:[function(require,module,exports){
+var util = require('../util'),
+ topsort = require('graphlib').alg.topsort;
+
+module.exports = initRank;
+
+/*
+ * Assigns a `rank` attribute to each node in the input graph and ensures that
+ * this rank respects the `minLen` attribute of incident edges.
+ *
+ * Prerequisites:
+ *
+ * * The input graph must be acyclic
+ * * Each edge in the input graph must have an assigned 'minLen' attribute
+ */
+function initRank(g) {
+ var sorted = topsort(g);
+
+ sorted.forEach(function(u) {
+ var inEdges = g.inEdges(u);
+ if (inEdges.length === 0) {
+ g.node(u).rank = 0;
+ return;
+ }
+
+ var minLens = inEdges.map(function(e) {
+ return g.node(g.source(e)).rank + g.edge(e).minLen;
+ });
+ g.node(u).rank = util.max(minLens);
+ });
+}
+
+},{"../util":26,"graphlib":28}],24:[function(require,module,exports){
+module.exports = {
+ slack: slack
+};
+
+/*
+ * A helper to calculate the slack between two nodes (`u` and `v`) given a
+ * `minLen` constraint. The slack represents how much the distance between `u`
+ * and `v` could shrink while maintaining the `minLen` constraint. If the value
+ * is negative then the constraint is currently violated.
+ *
+ This function requires that `u` and `v` are in `graph` and they both have a
+ `rank` attribute.
+ */
+function slack(graph, u, v, minLen) {
+ return Math.abs(graph.node(u).rank - graph.node(v).rank) - minLen;
+}
+
+},{}],25:[function(require,module,exports){
+var util = require('../util'),
+ rankUtil = require('./rankUtil');
+
+module.exports = simplex;
+
+function simplex(graph, spanningTree) {
+ // The network simplex algorithm repeatedly replaces edges of
+ // the spanning tree with negative cut values until no such
+ // edge exists.
+ initCutValues(graph, spanningTree);
+ while (true) {
+ var e = leaveEdge(spanningTree);
+ if (e === null) break;
+ var f = enterEdge(graph, spanningTree, e);
+ exchange(graph, spanningTree, e, f);
+ }
+}
+
+/*
+ * Set the cut values of edges in the spanning tree by a depth-first
+ * postorder traversal. The cut value corresponds to the cost, in
+ * terms of a ranking's edge length sum, of lengthening an edge.
+ * Negative cut values typically indicate edges that would yield a
+ * smaller edge length sum if they were lengthened.
+ */
+function initCutValues(graph, spanningTree) {
+ computeLowLim(spanningTree);
+
+ spanningTree.eachEdge(function(id, u, v, treeValue) {
+ treeValue.cutValue = 0;
+ });
+
+ // Propagate cut values up the tree.
+ function dfs(n) {
+ var children = spanningTree.successors(n);
+ for (var c in children) {
+ var child = children[c];
+ dfs(child);
+ }
+ if (n !== spanningTree.graph().root) {
+ setCutValue(graph, spanningTree, n);
+ }
+ }
+ dfs(spanningTree.graph().root);
+}
+
+/*
+ * Perform a DFS postorder traversal, labeling each node v with
+ * its traversal order 'lim(v)' and the minimum traversal number
+ * of any of its descendants 'low(v)'. This provides an efficient
+ * way to test whether u is an ancestor of v since
+ * low(u) <= lim(v) <= lim(u) if and only if u is an ancestor.
+ */
+function computeLowLim(tree) {
+ var postOrderNum = 0;
+
+ function dfs(n) {
+ var children = tree.successors(n);
+ var low = postOrderNum;
+ for (var c in children) {
+ var child = children[c];
+ dfs(child);
+ low = Math.min(low, tree.node(child).low);
+ }
+ tree.node(n).low = low;
+ tree.node(n).lim = postOrderNum++;
+ }
+
+ dfs(tree.graph().root);
+}
+
+/*
+ * To compute the cut value of the edge parent -> child, we consider
+ * it and any other graph edges to or from the child.
+ * parent
+ * |
+ * child
+ * / \
+ * u v
+ */
+function setCutValue(graph, tree, child) {
+ var parentEdge = tree.inEdges(child)[0];
+
+ // List of child's children in the spanning tree.
+ var grandchildren = [];
+ var grandchildEdges = tree.outEdges(child);
+ for (var gce in grandchildEdges) {
+ grandchildren.push(tree.target(grandchildEdges[gce]));
+ }
+
+ var cutValue = 0;
+
+ // TODO: Replace unit increment/decrement with edge weights.
+ var E = 0; // Edges from child to grandchild's subtree.
+ var F = 0; // Edges to child from grandchild's subtree.
+ var G = 0; // Edges from child to nodes outside of child's subtree.
+ var H = 0; // Edges from nodes outside of child's subtree to child.
+
+ // Consider all graph edges from child.
+ var outEdges = graph.outEdges(child);
+ var gc;
+ for (var oe in outEdges) {
+ var succ = graph.target(outEdges[oe]);
+ for (gc in grandchildren) {
+ if (inSubtree(tree, succ, grandchildren[gc])) {
+ E++;
+ }
+ }
+ if (!inSubtree(tree, succ, child)) {
+ G++;
+ }
+ }
+
+ // Consider all graph edges to child.
+ var inEdges = graph.inEdges(child);
+ for (var ie in inEdges) {
+ var pred = graph.source(inEdges[ie]);
+ for (gc in grandchildren) {
+ if (inSubtree(tree, pred, grandchildren[gc])) {
+ F++;
+ }
+ }
+ if (!inSubtree(tree, pred, child)) {
+ H++;
+ }
+ }
+
+ // Contributions depend on the alignment of the parent -> child edge
+ // and the child -> u or v edges.
+ var grandchildCutSum = 0;
+ for (gc in grandchildren) {
+ var cv = tree.edge(grandchildEdges[gc]).cutValue;
+ if (!tree.edge(grandchildEdges[gc]).reversed) {
+ grandchildCutSum += cv;
+ } else {
+ grandchildCutSum -= cv;
+ }
+ }
+
+ if (!tree.edge(parentEdge).reversed) {
+ cutValue += grandchildCutSum - E + F - G + H;
+ } else {
+ cutValue -= grandchildCutSum - E + F - G + H;
+ }
+
+ tree.edge(parentEdge).cutValue = cutValue;
+}
+
+/*
+ * Return whether n is a node in the subtree with the given
+ * root.
+ */
+function inSubtree(tree, n, root) {
+ return (tree.node(root).low <= tree.node(n).lim &&
+ tree.node(n).lim <= tree.node(root).lim);
+}
+
+/*
+ * Return an edge from the tree with a negative cut value, or null if there
+ * is none.
+ */
+function leaveEdge(tree) {
+ var edges = tree.edges();
+ for (var n in edges) {
+ var e = edges[n];
+ var treeValue = tree.edge(e);
+ if (treeValue.cutValue < 0) {
+ return e;
+ }
+ }
+ return null;
+}
+
+/*
+ * The edge e should be an edge in the tree, with an underlying edge
+ * in the graph, with a negative cut value. Of the two nodes incident
+ * on the edge, take the lower one. enterEdge returns an edge with
+ * minimum slack going from outside of that node's subtree to inside
+ * of that node's subtree.
+ */
+function enterEdge(graph, tree, e) {
+ var source = tree.source(e);
+ var target = tree.target(e);
+ var lower = tree.node(target).lim < tree.node(source).lim ? target : source;
+
+ // Is the tree edge aligned with the graph edge?
+ var aligned = !tree.edge(e).reversed;
+
+ var minSlack = Number.POSITIVE_INFINITY;
+ var minSlackEdge;
+ if (aligned) {
+ graph.eachEdge(function(id, u, v, value) {
+ if (id !== e && inSubtree(tree, u, lower) && !inSubtree(tree, v, lower)) {
+ var slack = rankUtil.slack(graph, u, v, value.minLen);
+ if (slack < minSlack) {
+ minSlack = slack;
+ minSlackEdge = id;
+ }
+ }
+ });
+ } else {
+ graph.eachEdge(function(id, u, v, value) {
+ if (id !== e && !inSubtree(tree, u, lower) && inSubtree(tree, v, lower)) {
+ var slack = rankUtil.slack(graph, u, v, value.minLen);
+ if (slack < minSlack) {
+ minSlack = slack;
+ minSlackEdge = id;
+ }
+ }
+ });
+ }
+
+ if (minSlackEdge === undefined) {
+ var outside = [];
+ var inside = [];
+ graph.eachNode(function(id) {
+ if (!inSubtree(tree, id, lower)) {
+ outside.push(id);
+ } else {
+ inside.push(id);
+ }
+ });
+ throw new Error('No edge found from outside of tree to inside');
+ }
+
+ return minSlackEdge;
+}
+
+/*
+ * Replace edge e with edge f in the tree, recalculating the tree root,
+ * the nodes' low and lim properties and the edges' cut values.
+ */
+function exchange(graph, tree, e, f) {
+ tree.delEdge(e);
+ var source = graph.source(f);
+ var target = graph.target(f);
+
+ // Redirect edges so that target is the root of its subtree.
+ function redirect(v) {
+ var edges = tree.inEdges(v);
+ for (var i in edges) {
+ var e = edges[i];
+ var u = tree.source(e);
+ var value = tree.edge(e);
+ redirect(u);
+ tree.delEdge(e);
+ value.reversed = !value.reversed;
+ tree.addEdge(e, v, u, value);
+ }
+ }
+
+ redirect(target);
+
+ var root = source;
+ var edges = tree.inEdges(root);
+ while (edges.length > 0) {
+ root = tree.source(edges[0]);
+ edges = tree.inEdges(root);
+ }
+
+ tree.graph().root = root;
+
+ tree.addEdge(null, source, target, {cutValue: 0});
+
+ initCutValues(graph, tree);
+
+ adjustRanks(graph, tree);
+}
+
+/*
+ * Reset the ranks of all nodes based on the current spanning tree.
+ * The rank of the tree's root remains unchanged, while all other
+ * nodes are set to the sum of minimum length constraints along
+ * the path from the root.
+ */
+function adjustRanks(graph, tree) {
+ function dfs(p) {
+ var children = tree.successors(p);
+ children.forEach(function(c) {
+ var minLen = minimumLength(graph, p, c);
+ graph.node(c).rank = graph.node(p).rank + minLen;
+ dfs(c);
+ });
+ }
+
+ dfs(tree.graph().root);
+}
+
+/*
+ * If u and v are connected by some edges in the graph, return the
+ * minimum length of those edges, as a positive number if v succeeds
+ * u and as a negative number if v precedes u.
+ */
+function minimumLength(graph, u, v) {
+ var outEdges = graph.outEdges(u, v);
+ if (outEdges.length > 0) {
+ return util.max(outEdges.map(function(e) {
+ return graph.edge(e).minLen;
+ }));
+ }
+
+ var inEdges = graph.inEdges(u, v);
+ if (inEdges.length > 0) {
+ return -util.max(inEdges.map(function(e) {
+ return graph.edge(e).minLen;
+ }));
+ }
+}
+
+},{"../util":26,"./rankUtil":24}],26:[function(require,module,exports){
+/*
+ * Returns the smallest value in the array.
+ */
+exports.min = function(values) {
+ return Math.min.apply(Math, values);
+};
+
+/*
+ * Returns the largest value in the array.
+ */
+exports.max = function(values) {
+ return Math.max.apply(Math, values);
+};
+
+/*
+ * Returns `true` only if `f(x)` is `true` for all `x` in `xs`. Otherwise
+ * returns `false`. This function will return immediately if it finds a
+ * case where `f(x)` does not hold.
+ */
+exports.all = function(xs, f) {
+ for (var i = 0; i < xs.length; ++i) {
+ if (!f(xs[i])) {
+ return false;
+ }
+ }
+ return true;
+};
+
+/*
+ * Accumulates the sum of elements in the given array using the `+` operator.
+ */
+exports.sum = function(values) {
+ return values.reduce(function(acc, x) { return acc + x; }, 0);
+};
+
+/*
+ * Returns an array of all values in the given object.
+ */
+exports.values = function(obj) {
+ return Object.keys(obj).map(function(k) { return obj[k]; });
+};
+
+exports.shuffle = function(array) {
+ for (i = array.length - 1; i > 0; --i) {
+ var j = Math.floor(Math.random() * (i + 1));
+ var aj = array[j];
+ array[j] = array[i];
+ array[i] = aj;
+ }
+};
+
+exports.propertyAccessor = function(self, config, field, setHook) {
+ return function(x) {
+ if (!arguments.length) return config[field];
+ config[field] = x;
+ if (setHook) setHook(x);
+ return self;
+ };
+};
+
+/*
+ * Given a layered, directed graph with `rank` and `order` node attributes,
+ * this function returns an array of ordered ranks. Each rank contains an array
+ * of the ids of the nodes in that rank in the order specified by the `order`
+ * attribute.
+ */
+exports.ordering = function(g) {
+ var ordering = [];
+ g.eachNode(function(u, value) {
+ var rank = ordering[value.rank] || (ordering[value.rank] = []);
+ rank[value.order] = u;
+ });
+ return ordering;
+};
+
+/*
+ * A filter that can be used with `filterNodes` to get a graph that only
+ * includes nodes that do not contain others nodes.
+ */
+exports.filterNonSubgraphs = function(g) {
+ return function(u) {
+ return g.children(u).length === 0;
+ };
+};
+
+/*
+ * Returns a new function that wraps `func` with a timer. The wrapper logs the
+ * time it takes to execute the function.
+ *
+ * The timer will be enabled provided `log.level >= 1`.
+ */
+function time(name, func) {
+ return function() {
+ var start = new Date().getTime();
+ try {
+ return func.apply(null, arguments);
+ } finally {
+ log(1, name + ' time: ' + (new Date().getTime() - start) + 'ms');
+ }
+ };
+}
+time.enabled = false;
+
+exports.time = time;
+
+/*
+ * A global logger with the specification `log(level, message, ...)` that
+ * will log a message to the console if `log.level >= level`.
+ */
+function log(level) {
+ if (log.level >= level) {
+ console.log.apply(console, Array.prototype.slice.call(arguments, 1));
+ }
+}
+log.level = 0;
+
+exports.log = log;
+
+},{}],27:[function(require,module,exports){
+module.exports = '0.4.5';
+
+},{}],28:[function(require,module,exports){
+exports.Graph = require("./lib/Graph");
+exports.Digraph = require("./lib/Digraph");
+exports.CGraph = require("./lib/CGraph");
+exports.CDigraph = require("./lib/CDigraph");
+require("./lib/graph-converters");
+
+exports.alg = {
+ isAcyclic: require("./lib/alg/isAcyclic"),
+ components: require("./lib/alg/components"),
+ dijkstra: require("./lib/alg/dijkstra"),
+ dijkstraAll: require("./lib/alg/dijkstraAll"),
+ findCycles: require("./lib/alg/findCycles"),
+ floydWarshall: require("./lib/alg/floydWarshall"),
+ postorder: require("./lib/alg/postorder"),
+ preorder: require("./lib/alg/preorder"),
+ prim: require("./lib/alg/prim"),
+ tarjan: require("./lib/alg/tarjan"),
+ topsort: require("./lib/alg/topsort")
+};
+
+exports.converter = {
+ json: require("./lib/converter/json.js")
+};
+
+var filter = require("./lib/filter");
+exports.filter = {
+ all: filter.all,
+ nodesFromList: filter.nodesFromList
+};
+
+exports.version = require("./lib/version");
+
+},{"./lib/CDigraph":30,"./lib/CGraph":31,"./lib/Digraph":32,"./lib/Graph":33,"./lib/alg/components":34,"./lib/alg/dijkstra":35,"./lib/alg/dijkstraAll":36,"./lib/alg/findCycles":37,"./lib/alg/floydWarshall":38,"./lib/alg/isAcyclic":39,"./lib/alg/postorder":40,"./lib/alg/preorder":41,"./lib/alg/prim":42,"./lib/alg/tarjan":43,"./lib/alg/topsort":44,"./lib/converter/json.js":46,"./lib/filter":47,"./lib/graph-converters":48,"./lib/version":50}],29:[function(require,module,exports){
+/* jshint -W079 */
+var Set = require("cp-data").Set;
+/* jshint +W079 */
+
+module.exports = BaseGraph;
+
+function BaseGraph() {
+ // The value assigned to the graph itself.
+ this._value = undefined;
+
+ // Map of node id -> { id, value }
+ this._nodes = {};
+
+ // Map of edge id -> { id, u, v, value }
+ this._edges = {};
+
+ // Used to generate a unique id in the graph
+ this._nextId = 0;
+}
+
+// Number of nodes
+BaseGraph.prototype.order = function() {
+ return Object.keys(this._nodes).length;
+};
+
+// Number of edges
+BaseGraph.prototype.size = function() {
+ return Object.keys(this._edges).length;
+};
+
+// Accessor for graph level value
+BaseGraph.prototype.graph = function(value) {
+ if (arguments.length === 0) {
+ return this._value;
+ }
+ this._value = value;
+};
+
+BaseGraph.prototype.hasNode = function(u) {
+ return u in this._nodes;
+};
+
+BaseGraph.prototype.node = function(u, value) {
+ var node = this._strictGetNode(u);
+ if (arguments.length === 1) {
+ return node.value;
+ }
+ node.value = value;
+};
+
+BaseGraph.prototype.nodes = function() {
+ var nodes = [];
+ this.eachNode(function(id) { nodes.push(id); });
+ return nodes;
+};
+
+BaseGraph.prototype.eachNode = function(func) {
+ for (var k in this._nodes) {
+ var node = this._nodes[k];
+ func(node.id, node.value);
+ }
+};
+
+BaseGraph.prototype.hasEdge = function(e) {
+ return e in this._edges;
+};
+
+BaseGraph.prototype.edge = function(e, value) {
+ var edge = this._strictGetEdge(e);
+ if (arguments.length === 1) {
+ return edge.value;
+ }
+ edge.value = value;
+};
+
+BaseGraph.prototype.edges = function() {
+ var es = [];
+ this.eachEdge(function(id) { es.push(id); });
+ return es;
+};
+
+BaseGraph.prototype.eachEdge = function(func) {
+ for (var k in this._edges) {
+ var edge = this._edges[k];
+ func(edge.id, edge.u, edge.v, edge.value);
+ }
+};
+
+BaseGraph.prototype.incidentNodes = function(e) {
+ var edge = this._strictGetEdge(e);
+ return [edge.u, edge.v];
+};
+
+BaseGraph.prototype.addNode = function(u, value) {
+ if (u === undefined || u === null) {
+ do {
+ u = "_" + (++this._nextId);
+ } while (this.hasNode(u));
+ } else if (this.hasNode(u)) {
+ throw new Error("Graph already has node '" + u + "'");
+ }
+ this._nodes[u] = { id: u, value: value };
+ return u;
+};
+
+BaseGraph.prototype.delNode = function(u) {
+ this._strictGetNode(u);
+ this.incidentEdges(u).forEach(function(e) { this.delEdge(e); }, this);
+ delete this._nodes[u];
+};
+
+// inMap and outMap are opposite sides of an incidence map. For example, for
+// Graph these would both come from the _incidentEdges map, while for Digraph
+// they would come from _inEdges and _outEdges.
+BaseGraph.prototype._addEdge = function(e, u, v, value, inMap, outMap) {
+ this._strictGetNode(u);
+ this._strictGetNode(v);
+
+ if (e === undefined || e === null) {
+ do {
+ e = "_" + (++this._nextId);
+ } while (this.hasEdge(e));
+ }
+ else if (this.hasEdge(e)) {
+ throw new Error("Graph already has edge '" + e + "'");
+ }
+
+ this._edges[e] = { id: e, u: u, v: v, value: value };
+ addEdgeToMap(inMap[v], u, e);
+ addEdgeToMap(outMap[u], v, e);
+
+ return e;
+};
+
+// See note for _addEdge regarding inMap and outMap.
+BaseGraph.prototype._delEdge = function(e, inMap, outMap) {
+ var edge = this._strictGetEdge(e);
+ delEdgeFromMap(inMap[edge.v], edge.u, e);
+ delEdgeFromMap(outMap[edge.u], edge.v, e);
+ delete this._edges[e];
+};
+
+BaseGraph.prototype.copy = function() {
+ var copy = new this.constructor();
+ copy.graph(this.graph());
+ this.eachNode(function(u, value) { copy.addNode(u, value); });
+ this.eachEdge(function(e, u, v, value) { copy.addEdge(e, u, v, value); });
+ copy._nextId = this._nextId;
+ return copy;
+};
+
+BaseGraph.prototype.filterNodes = function(filter) {
+ var copy = new this.constructor();
+ copy.graph(this.graph());
+ this.eachNode(function(u, value) {
+ if (filter(u)) {
+ copy.addNode(u, value);
+ }
+ });
+ this.eachEdge(function(e, u, v, value) {
+ if (copy.hasNode(u) && copy.hasNode(v)) {
+ copy.addEdge(e, u, v, value);
+ }
+ });
+ return copy;
+};
+
+BaseGraph.prototype._strictGetNode = function(u) {
+ var node = this._nodes[u];
+ if (node === undefined) {
+ throw new Error("Node '" + u + "' is not in graph");
+ }
+ return node;
+};
+
+BaseGraph.prototype._strictGetEdge = function(e) {
+ var edge = this._edges[e];
+ if (edge === undefined) {
+ throw new Error("Edge '" + e + "' is not in graph");
+ }
+ return edge;
+};
+
+function addEdgeToMap(map, v, e) {
+ (map[v] || (map[v] = new Set())).add(e);
+}
+
+function delEdgeFromMap(map, v, e) {
+ var vEntry = map[v];
+ vEntry.remove(e);
+ if (vEntry.size() === 0) {
+ delete map[v];
+ }
+}
+
+
+},{"cp-data":5}],30:[function(require,module,exports){
+var Digraph = require("./Digraph"),
+ compoundify = require("./compoundify");
+
+var CDigraph = compoundify(Digraph);
+
+module.exports = CDigraph;
+
+CDigraph.fromDigraph = function(src) {
+ var g = new CDigraph(),
+ graphValue = src.graph();
+
+ if (graphValue !== undefined) {
+ g.graph(graphValue);
+ }
+
+ src.eachNode(function(u, value) {
+ if (value === undefined) {
+ g.addNode(u);
+ } else {
+ g.addNode(u, value);
+ }
+ });
+ src.eachEdge(function(e, u, v, value) {
+ if (value === undefined) {
+ g.addEdge(null, u, v);
+ } else {
+ g.addEdge(null, u, v, value);
+ }
+ });
+ return g;
+};
+
+CDigraph.prototype.toString = function() {
+ return "CDigraph " + JSON.stringify(this, null, 2);
+};
+
+},{"./Digraph":32,"./compoundify":45}],31:[function(require,module,exports){
+var Graph = require("./Graph"),
+ compoundify = require("./compoundify");
+
+var CGraph = compoundify(Graph);
+
+module.exports = CGraph;
+
+CGraph.fromGraph = function(src) {
+ var g = new CGraph(),
+ graphValue = src.graph();
+
+ if (graphValue !== undefined) {
+ g.graph(graphValue);
+ }
+
+ src.eachNode(function(u, value) {
+ if (value === undefined) {
+ g.addNode(u);
+ } else {
+ g.addNode(u, value);
+ }
+ });
+ src.eachEdge(function(e, u, v, value) {
+ if (value === undefined) {
+ g.addEdge(null, u, v);
+ } else {
+ g.addEdge(null, u, v, value);
+ }
+ });
+ return g;
+};
+
+CGraph.prototype.toString = function() {
+ return "CGraph " + JSON.stringify(this, null, 2);
+};
+
+},{"./Graph":33,"./compoundify":45}],32:[function(require,module,exports){
+/*
+ * This file is organized with in the following order:
+ *
+ * Exports
+ * Graph constructors
+ * Graph queries (e.g. nodes(), edges()
+ * Graph mutators
+ * Helper functions
+ */
+
+var util = require("./util"),
+ BaseGraph = require("./BaseGraph"),
+/* jshint -W079 */
+ Set = require("cp-data").Set;
+/* jshint +W079 */
+
+module.exports = Digraph;
+
+/*
+ * Constructor to create a new directed multi-graph.
+ */
+function Digraph() {
+ BaseGraph.call(this);
+
+ /*! Map of sourceId -> {targetId -> Set of edge ids} */
+ this._inEdges = {};
+
+ /*! Map of targetId -> {sourceId -> Set of edge ids} */
+ this._outEdges = {};
+}
+
+Digraph.prototype = new BaseGraph();
+Digraph.prototype.constructor = Digraph;
+
+/*
+ * Always returns `true`.
+ */
+Digraph.prototype.isDirected = function() {
+ return true;
+};
+
+/*
+ * Returns all successors of the node with the id `u`. That is, all nodes
+ * that have the node `u` as their source are returned.
+ *
+ * If no node `u` exists in the graph this function throws an Error.
+ *
+ * @param {String} u a node id
+ */
+Digraph.prototype.successors = function(u) {
+ this._strictGetNode(u);
+ return Object.keys(this._outEdges[u])
+ .map(function(v) { return this._nodes[v].id; }, this);
+};
+
+/*
+ * Returns all predecessors of the node with the id `u`. That is, all nodes
+ * that have the node `u` as their target are returned.
+ *
+ * If no node `u` exists in the graph this function throws an Error.
+ *
+ * @param {String} u a node id
+ */
+Digraph.prototype.predecessors = function(u) {
+ this._strictGetNode(u);
+ return Object.keys(this._inEdges[u])
+ .map(function(v) { return this._nodes[v].id; }, this);
+};
+
+/*
+ * Returns all nodes that are adjacent to the node with the id `u`. In other
+ * words, this function returns the set of all successors and predecessors of
+ * node `u`.
+ *
+ * @param {String} u a node id
+ */
+Digraph.prototype.neighbors = function(u) {
+ return Set.union([this.successors(u), this.predecessors(u)]).keys();
+};
+
+/*
+ * Returns all nodes in the graph that have no in-edges.
+ */
+Digraph.prototype.sources = function() {
+ var self = this;
+ return this._filterNodes(function(u) {
+ // This could have better space characteristics if we had an inDegree function.
+ return self.inEdges(u).length === 0;
+ });
+};
+
+/*
+ * Returns all nodes in the graph that have no out-edges.
+ */
+Digraph.prototype.sinks = function() {
+ var self = this;
+ return this._filterNodes(function(u) {
+ // This could have better space characteristics if we have an outDegree function.
+ return self.outEdges(u).length === 0;
+ });
+};
+
+/*
+ * Returns the source node incident on the edge identified by the id `e`. If no
+ * such edge exists in the graph this function throws an Error.
+ *
+ * @param {String} e an edge id
+ */
+Digraph.prototype.source = function(e) {
+ return this._strictGetEdge(e).u;
+};
+
+/*
+ * Returns the target node incident on the edge identified by the id `e`. If no
+ * such edge exists in the graph this function throws an Error.
+ *
+ * @param {String} e an edge id
+ */
+Digraph.prototype.target = function(e) {
+ return this._strictGetEdge(e).v;
+};
+
+/*
+ * Returns an array of ids for all edges in the graph that have the node
+ * `target` as their target. If the node `target` is not in the graph this
+ * function raises an Error.
+ *
+ * Optionally a `source` node can also be specified. This causes the results
+ * to be filtered such that only edges from `source` to `target` are included.
+ * If the node `source` is specified but is not in the graph then this function
+ * raises an Error.
+ *
+ * @param {String} target the target node id
+ * @param {String} [source] an optional source node id
+ */
+Digraph.prototype.inEdges = function(target, source) {
+ this._strictGetNode(target);
+ var results = Set.union(util.values(this._inEdges[target])).keys();
+ if (arguments.length > 1) {
+ this._strictGetNode(source);
+ results = results.filter(function(e) { return this.source(e) === source; }, this);
+ }
+ return results;
+};
+
+/*
+ * Returns an array of ids for all edges in the graph that have the node
+ * `source` as their source. If the node `source` is not in the graph this
+ * function raises an Error.
+ *
+ * Optionally a `target` node may also be specified. This causes the results
+ * to be filtered such that only edges from `source` to `target` are included.
+ * If the node `target` is specified but is not in the graph then this function
+ * raises an Error.
+ *
+ * @param {String} source the source node id
+ * @param {String} [target] an optional target node id
+ */
+Digraph.prototype.outEdges = function(source, target) {
+ this._strictGetNode(source);
+ var results = Set.union(util.values(this._outEdges[source])).keys();
+ if (arguments.length > 1) {
+ this._strictGetNode(target);
+ results = results.filter(function(e) { return this.target(e) === target; }, this);
+ }
+ return results;
+};
+
+/*
+ * Returns an array of ids for all edges in the graph that have the `u` as
+ * their source or their target. If the node `u` is not in the graph this
+ * function raises an Error.
+ *
+ * Optionally a `v` node may also be specified. This causes the results to be
+ * filtered such that only edges between `u` and `v` - in either direction -
+ * are included. IF the node `v` is specified but not in the graph then this
+ * function raises an Error.
+ *
+ * @param {String} u the node for which to find incident edges
+ * @param {String} [v] option node that must be adjacent to `u`
+ */
+Digraph.prototype.incidentEdges = function(u, v) {
+ if (arguments.length > 1) {
+ return Set.union([this.outEdges(u, v), this.outEdges(v, u)]).keys();
+ } else {
+ return Set.union([this.inEdges(u), this.outEdges(u)]).keys();
+ }
+};
+
+/*
+ * Returns a string representation of this graph.
+ */
+Digraph.prototype.toString = function() {
+ return "Digraph " + JSON.stringify(this, null, 2);
+};
+
+/*
+ * Adds a new node with the id `u` to the graph and assigns it the value
+ * `value`. If a node with the id is already a part of the graph this function
+ * throws an Error.
+ *
+ * @param {String} u a node id
+ * @param {Object} [value] an optional value to attach to the node
+ */
+Digraph.prototype.addNode = function(u, value) {
+ u = BaseGraph.prototype.addNode.call(this, u, value);
+ this._inEdges[u] = {};
+ this._outEdges[u] = {};
+ return u;
+};
+
+/*
+ * Removes a node from the graph that has the id `u`. Any edges incident on the
+ * node are also removed. If the graph does not contain a node with the id this
+ * function will throw an Error.
+ *
+ * @param {String} u a node id
+ */
+Digraph.prototype.delNode = function(u) {
+ BaseGraph.prototype.delNode.call(this, u);
+ delete this._inEdges[u];
+ delete this._outEdges[u];
+};
+
+/*
+ * Adds a new edge to the graph with the id `e` from a node with the id `source`
+ * to a node with an id `target` and assigns it the value `value`. This graph
+ * allows more than one edge from `source` to `target` as long as the id `e`
+ * is unique in the set of edges. If `e` is `null` the graph will assign a
+ * unique identifier to the edge.
+ *
+ * If `source` or `target` are not present in the graph this function will
+ * throw an Error.
+ *
+ * @param {String} [e] an edge id
+ * @param {String} source the source node id
+ * @param {String} target the target node id
+ * @param {Object} [value] an optional value to attach to the edge
+ */
+Digraph.prototype.addEdge = function(e, source, target, value) {
+ return BaseGraph.prototype._addEdge.call(this, e, source, target, value,
+ this._inEdges, this._outEdges);
+};
+
+/*
+ * Removes an edge in the graph with the id `e`. If no edge in the graph has
+ * the id `e` this function will throw an Error.
+ *
+ * @param {String} e an edge id
+ */
+Digraph.prototype.delEdge = function(e) {
+ BaseGraph.prototype._delEdge.call(this, e, this._inEdges, this._outEdges);
+};
+
+// Unlike BaseGraph.filterNodes, this helper just returns nodes that
+// satisfy a predicate.
+Digraph.prototype._filterNodes = function(pred) {
+ var filtered = [];
+ this.eachNode(function(u) {
+ if (pred(u)) {
+ filtered.push(u);
+ }
+ });
+ return filtered;
+};
+
+
+},{"./BaseGraph":29,"./util":49,"cp-data":5}],33:[function(require,module,exports){
+/*
+ * This file is organized with in the following order:
+ *
+ * Exports
+ * Graph constructors
+ * Graph queries (e.g. nodes(), edges()
+ * Graph mutators
+ * Helper functions
+ */
+
+var util = require("./util"),
+ BaseGraph = require("./BaseGraph"),
+/* jshint -W079 */
+ Set = require("cp-data").Set;
+/* jshint +W079 */
+
+module.exports = Graph;
+
+/*
+ * Constructor to create a new undirected multi-graph.
+ */
+function Graph() {
+ BaseGraph.call(this);
+
+ /*! Map of nodeId -> { otherNodeId -> Set of edge ids } */
+ this._incidentEdges = {};
+}
+
+Graph.prototype = new BaseGraph();
+Graph.prototype.constructor = Graph;
+
+/*
+ * Always returns `false`.
+ */
+Graph.prototype.isDirected = function() {
+ return false;
+};
+
+/*
+ * Returns all nodes that are adjacent to the node with the id `u`.
+ *
+ * @param {String} u a node id
+ */
+Graph.prototype.neighbors = function(u) {
+ this._strictGetNode(u);
+ return Object.keys(this._incidentEdges[u])
+ .map(function(v) { return this._nodes[v].id; }, this);
+};
+
+/*
+ * Returns an array of ids for all edges in the graph that are incident on `u`.
+ * If the node `u` is not in the graph this function raises an Error.
+ *
+ * Optionally a `v` node may also be specified. This causes the results to be
+ * filtered such that only edges between `u` and `v` are included. If the node
+ * `v` is specified but not in the graph then this function raises an Error.
+ *
+ * @param {String} u the node for which to find incident edges
+ * @param {String} [v] option node that must be adjacent to `u`
+ */
+Graph.prototype.incidentEdges = function(u, v) {
+ this._strictGetNode(u);
+ if (arguments.length > 1) {
+ this._strictGetNode(v);
+ return v in this._incidentEdges[u] ? this._incidentEdges[u][v].keys() : [];
+ } else {
+ return Set.union(util.values(this._incidentEdges[u])).keys();
+ }
+};
+
+/*
+ * Returns a string representation of this graph.
+ */
+Graph.prototype.toString = function() {
+ return "Graph " + JSON.stringify(this, null, 2);
+};
+
+/*
+ * Adds a new node with the id `u` to the graph and assigns it the value
+ * `value`. If a node with the id is already a part of the graph this function
+ * throws an Error.
+ *
+ * @param {String} u a node id
+ * @param {Object} [value] an optional value to attach to the node
+ */
+Graph.prototype.addNode = function(u, value) {
+ u = BaseGraph.prototype.addNode.call(this, u, value);
+ this._incidentEdges[u] = {};
+ return u;
+};
+
+/*
+ * Removes a node from the graph that has the id `u`. Any edges incident on the
+ * node are also removed. If the graph does not contain a node with the id this
+ * function will throw an Error.
+ *
+ * @param {String} u a node id
+ */
+Graph.prototype.delNode = function(u) {
+ BaseGraph.prototype.delNode.call(this, u);
+ delete this._incidentEdges[u];
+};
+
+/*
+ * Adds a new edge to the graph with the id `e` between a node with the id `u`
+ * and a node with an id `v` and assigns it the value `value`. This graph
+ * allows more than one edge between `u` and `v` as long as the id `e`
+ * is unique in the set of edges. If `e` is `null` the graph will assign a
+ * unique identifier to the edge.
+ *
+ * If `u` or `v` are not present in the graph this function will throw an
+ * Error.
+ *
+ * @param {String} [e] an edge id
+ * @param {String} u the node id of one of the adjacent nodes
+ * @param {String} v the node id of the other adjacent node
+ * @param {Object} [value] an optional value to attach to the edge
+ */
+Graph.prototype.addEdge = function(e, u, v, value) {
+ return BaseGraph.prototype._addEdge.call(this, e, u, v, value,
+ this._incidentEdges, this._incidentEdges);
+};
+
+/*
+ * Removes an edge in the graph with the id `e`. If no edge in the graph has
+ * the id `e` this function will throw an Error.
+ *
+ * @param {String} e an edge id
+ */
+Graph.prototype.delEdge = function(e) {
+ BaseGraph.prototype._delEdge.call(this, e, this._incidentEdges, this._incidentEdges);
+};
+
+
+},{"./BaseGraph":29,"./util":49,"cp-data":5}],34:[function(require,module,exports){
+/* jshint -W079 */
+var Set = require("cp-data").Set;
+/* jshint +W079 */
+
+module.exports = components;
+
+/**
+ * Finds all [connected components][] in a graph and returns an array of these
+ * components. Each component is itself an array that contains the ids of nodes
+ * in the component.
+ *
+ * This function only works with undirected Graphs.
+ *
+ * [connected components]: http://en.wikipedia.org/wiki/Connected_component_(graph_theory)
+ *
+ * @param {Graph} g the graph to search for components
+ */
+function components(g) {
+ var results = [];
+ var visited = new Set();
+
+ function dfs(v, component) {
+ if (!visited.has(v)) {
+ visited.add(v);
+ component.push(v);
+ g.neighbors(v).forEach(function(w) {
+ dfs(w, component);
+ });
+ }
+ }
+
+ g.nodes().forEach(function(v) {
+ var component = [];
+ dfs(v, component);
+ if (component.length > 0) {
+ results.push(component);
+ }
+ });
+
+ return results;
+}
+
+},{"cp-data":5}],35:[function(require,module,exports){
+var PriorityQueue = require("cp-data").PriorityQueue;
+
+module.exports = dijkstra;
+
+/**
+ * This function is an implementation of [Dijkstra's algorithm][] which finds
+ * the shortest path from **source** to all other nodes in **g**. This
+ * function returns a map of `u -> { distance, predecessor }`. The distance
+ * property holds the sum of the weights from **source** to `u` along the
+ * shortest path or `Number.POSITIVE_INFINITY` if there is no path from
+ * **source**. The predecessor property can be used to walk the individual
+ * elements of the path from **source** to **u** in reverse order.
+ *
+ * This function takes an optional `weightFunc(e)` which returns the
+ * weight of the edge `e`. If no weightFunc is supplied then each edge is
+ * assumed to have a weight of 1. This function throws an Error if any of
+ * the traversed edges have a negative edge weight.
+ *
+ * This function takes an optional `incidentFunc(u)` which returns the ids of
+ * all edges incident to the node `u` for the purposes of shortest path
+ * traversal. By default this function uses the `g.outEdges` for Digraphs and
+ * `g.incidentEdges` for Graphs.
+ *
+ * This function takes `O((|E| + |V|) * log |V|)` time.
+ *
+ * [Dijkstra's algorithm]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
+ *
+ * @param {Graph} g the graph to search for shortest paths from **source**
+ * @param {Object} source the source from which to start the search
+ * @param {Function} [weightFunc] optional weight function
+ * @param {Function} [incidentFunc] optional incident function
+ */
+function dijkstra(g, source, weightFunc, incidentFunc) {
+ var results = {},
+ pq = new PriorityQueue();
+
+ function updateNeighbors(e) {
+ var incidentNodes = g.incidentNodes(e),
+ v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1],
+ vEntry = results[v],
+ weight = weightFunc(e),
+ distance = uEntry.distance + weight;
+
+ if (weight < 0) {
+ throw new Error("dijkstra does not allow negative edge weights. Bad edge: " + e + " Weight: " + weight);
+ }
+
+ if (distance < vEntry.distance) {
+ vEntry.distance = distance;
+ vEntry.predecessor = u;
+ pq.decrease(v, distance);
+ }
+ }
+
+ weightFunc = weightFunc || function() { return 1; };
+ incidentFunc = incidentFunc || (g.isDirected()
+ ? function(u) { return g.outEdges(u); }
+ : function(u) { return g.incidentEdges(u); });
+
+ g.eachNode(function(u) {
+ var distance = u === source ? 0 : Number.POSITIVE_INFINITY;
+ results[u] = { distance: distance };
+ pq.add(u, distance);
+ });
+
+ var u, uEntry;
+ while (pq.size() > 0) {
+ u = pq.removeMin();
+ uEntry = results[u];
+ if (uEntry.distance === Number.POSITIVE_INFINITY) {
+ break;
+ }
+
+ incidentFunc(u).forEach(updateNeighbors);
+ }
+
+ return results;
+}
+
+},{"cp-data":5}],36:[function(require,module,exports){
+var dijkstra = require("./dijkstra");
+
+module.exports = dijkstraAll;
+
+/**
+ * This function finds the shortest path from each node to every other
+ * reachable node in the graph. It is similar to [alg.dijkstra][], but
+ * instead of returning a single-source array, it returns a mapping of
+ * of `source -> alg.dijksta(g, source, weightFunc, incidentFunc)`.
+ *
+ * This function takes an optional `weightFunc(e)` which returns the
+ * weight of the edge `e`. If no weightFunc is supplied then each edge is
+ * assumed to have a weight of 1. This function throws an Error if any of
+ * the traversed edges have a negative edge weight.
+ *
+ * This function takes an optional `incidentFunc(u)` which returns the ids of
+ * all edges incident to the node `u` for the purposes of shortest path
+ * traversal. By default this function uses the `outEdges` function on the
+ * supplied graph.
+ *
+ * This function takes `O(|V| * (|E| + |V|) * log |V|)` time.
+ *
+ * [alg.dijkstra]: dijkstra.js.html#dijkstra
+ *
+ * @param {Graph} g the graph to search for shortest paths from **source**
+ * @param {Function} [weightFunc] optional weight function
+ * @param {Function} [incidentFunc] optional incident function
+ */
+function dijkstraAll(g, weightFunc, incidentFunc) {
+ var results = {};
+ g.eachNode(function(u) {
+ results[u] = dijkstra(g, u, weightFunc, incidentFunc);
+ });
+ return results;
+}
+
+},{"./dijkstra":35}],37:[function(require,module,exports){
+var tarjan = require("./tarjan");
+
+module.exports = findCycles;
+
+/*
+ * Given a Digraph **g** this function returns all nodes that are part of a
+ * cycle. Since there may be more than one cycle in a graph this function
+ * returns an array of these cycles, where each cycle is itself represented
+ * by an array of ids for each node involved in that cycle.
+ *
+ * [alg.isAcyclic][] is more efficient if you only need to determine whether
+ * a graph has a cycle or not.
+ *
+ * [alg.isAcyclic]: isAcyclic.js.html#isAcyclic
+ *
+ * @param {Digraph} g the graph to search for cycles.
+ */
+function findCycles(g) {
+ return tarjan(g).filter(function(cmpt) { return cmpt.length > 1; });
+}
+
+},{"./tarjan":43}],38:[function(require,module,exports){
+module.exports = floydWarshall;
+
+/**
+ * This function is an implementation of the [Floyd-Warshall algorithm][],
+ * which finds the shortest path from each node to every other reachable node
+ * in the graph. It is similar to [alg.dijkstraAll][], but it handles negative
+ * edge weights and is more efficient for some types of graphs. This function
+ * returns a map of `source -> { target -> { distance, predecessor }`. The
+ * distance property holds the sum of the weights from `source` to `target`
+ * along the shortest path of `Number.POSITIVE_INFINITY` if there is no path
+ * from `source`. The predecessor property can be used to walk the individual
+ * elements of the path from `source` to `target` in reverse order.
+ *
+ * This function takes an optional `weightFunc(e)` which returns the
+ * weight of the edge `e`. If no weightFunc is supplied then each edge is
+ * assumed to have a weight of 1.
+ *
+ * This function takes an optional `incidentFunc(u)` which returns the ids of
+ * all edges incident to the node `u` for the purposes of shortest path
+ * traversal. By default this function uses the `outEdges` function on the
+ * supplied graph.
+ *
+ * This algorithm takes O(|V|^3) time.
+ *
+ * [Floyd-Warshall algorithm]: https://en.wikipedia.org/wiki/Floyd-Warshall_algorithm
+ * [alg.dijkstraAll]: dijkstraAll.js.html#dijkstraAll
+ *
+ * @param {Graph} g the graph to search for shortest paths from **source**
+ * @param {Function} [weightFunc] optional weight function
+ * @param {Function} [incidentFunc] optional incident function
+ */
+function floydWarshall(g, weightFunc, incidentFunc) {
+ var results = {},
+ nodes = g.nodes();
+
+ weightFunc = weightFunc || function() { return 1; };
+ incidentFunc = incidentFunc || (g.isDirected()
+ ? function(u) { return g.outEdges(u); }
+ : function(u) { return g.incidentEdges(u); });
+
+ nodes.forEach(function(u) {
+ results[u] = {};
+ results[u][u] = { distance: 0 };
+ nodes.forEach(function(v) {
+ if (u !== v) {
+ results[u][v] = { distance: Number.POSITIVE_INFINITY };
+ }
+ });
+ incidentFunc(u).forEach(function(e) {
+ var incidentNodes = g.incidentNodes(e),
+ v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1],
+ d = weightFunc(e);
+ if (d < results[u][v].distance) {
+ results[u][v] = { distance: d, predecessor: u };
+ }
+ });
+ });
+
+ nodes.forEach(function(k) {
+ var rowK = results[k];
+ nodes.forEach(function(i) {
+ var rowI = results[i];
+ nodes.forEach(function(j) {
+ var ik = rowI[k];
+ var kj = rowK[j];
+ var ij = rowI[j];
+ var altDistance = ik.distance + kj.distance;
+ if (altDistance < ij.distance) {
+ ij.distance = altDistance;
+ ij.predecessor = kj.predecessor;
+ }
+ });
+ });
+ });
+
+ return results;
+}
+
+},{}],39:[function(require,module,exports){
+var topsort = require("./topsort");
+
+module.exports = isAcyclic;
+
+/*
+ * Given a Digraph **g** this function returns `true` if the graph has no
+ * cycles and returns `false` if it does. This algorithm returns as soon as it
+ * detects the first cycle.
+ *
+ * Use [alg.findCycles][] if you need the actual list of cycles in a graph.
+ *
+ * [alg.findCycles]: findCycles.js.html#findCycles
+ *
+ * @param {Digraph} g the graph to test for cycles
+ */
+function isAcyclic(g) {
+ try {
+ topsort(g);
+ } catch (e) {
+ if (e instanceof topsort.CycleException) return false;
+ throw e;
+ }
+ return true;
+}
+
+},{"./topsort":44}],40:[function(require,module,exports){
+/* jshint -W079 */
+var Set = require("cp-data").Set;
+/* jshint +W079 */
+
+module.exports = postorder;
+
+// Postorder traversal of g, calling f for each visited node. Assumes the graph
+// is a tree.
+function postorder(g, root, f) {
+ var visited = new Set();
+ if (g.isDirected()) {
+ throw new Error("This function only works for undirected graphs");
+ }
+ function dfs(u, prev) {
+ if (visited.has(u)) {
+ throw new Error("The input graph is not a tree: " + g);
+ }
+ visited.add(u);
+ g.neighbors(u).forEach(function(v) {
+ if (v !== prev) dfs(v, u);
+ });
+ f(u);
+ }
+ dfs(root);
+}
+
+},{"cp-data":5}],41:[function(require,module,exports){
+/* jshint -W079 */
+var Set = require("cp-data").Set;
+/* jshint +W079 */
+
+module.exports = preorder;
+
+// Preorder traversal of g, calling f for each visited node. Assumes the graph
+// is a tree.
+function preorder(g, root, f) {
+ var visited = new Set();
+ if (g.isDirected()) {
+ throw new Error("This function only works for undirected graphs");
+ }
+ function dfs(u, prev) {
+ if (visited.has(u)) {
+ throw new Error("The input graph is not a tree: " + g);
+ }
+ visited.add(u);
+ f(u);
+ g.neighbors(u).forEach(function(v) {
+ if (v !== prev) dfs(v, u);
+ });
+ }
+ dfs(root);
+}
+
+},{"cp-data":5}],42:[function(require,module,exports){
+var Graph = require("../Graph"),
+ PriorityQueue = require("cp-data").PriorityQueue;
+
+module.exports = prim;
+
+/**
+ * [Prim's algorithm][] takes a connected undirected graph and generates a
+ * [minimum spanning tree][]. This function returns the minimum spanning
+ * tree as an undirected graph. This algorithm is derived from the description
+ * in "Introduction to Algorithms", Third Edition, Cormen, et al., Pg 634.
+ *
+ * This function takes a `weightFunc(e)` which returns the weight of the edge
+ * `e`. It throws an Error if the graph is not connected.
+ *
+ * This function takes `O(|E| log |V|)` time.
+ *
+ * [Prim's algorithm]: https://en.wikipedia.org/wiki/Prim's_algorithm
+ * [minimum spanning tree]: https://en.wikipedia.org/wiki/Minimum_spanning_tree
+ *
+ * @param {Graph} g the graph used to generate the minimum spanning tree
+ * @param {Function} weightFunc the weight function to use
+ */
+function prim(g, weightFunc) {
+ var result = new Graph(),
+ parents = {},
+ pq = new PriorityQueue(),
+ u;
+
+ function updateNeighbors(e) {
+ var incidentNodes = g.incidentNodes(e),
+ v = incidentNodes[0] !== u ? incidentNodes[0] : incidentNodes[1],
+ pri = pq.priority(v);
+ if (pri !== undefined) {
+ var edgeWeight = weightFunc(e);
+ if (edgeWeight < pri) {
+ parents[v] = u;
+ pq.decrease(v, edgeWeight);
+ }
+ }
+ }
+
+ if (g.order() === 0) {
+ return result;
+ }
+
+ g.eachNode(function(u) {
+ pq.add(u, Number.POSITIVE_INFINITY);
+ result.addNode(u);
+ });
+
+ // Start from an arbitrary node
+ pq.decrease(g.nodes()[0], 0);
+
+ var init = false;
+ while (pq.size() > 0) {
+ u = pq.removeMin();
+ if (u in parents) {
+ result.addEdge(null, u, parents[u]);
+ } else if (init) {
+ throw new Error("Input graph is not connected: " + g);
+ } else {
+ init = true;
+ }
+
+ g.incidentEdges(u).forEach(updateNeighbors);
+ }
+
+ return result;
+}
+
+},{"../Graph":33,"cp-data":5}],43:[function(require,module,exports){
+module.exports = tarjan;
+
+/**
+ * This function is an implementation of [Tarjan's algorithm][] which finds
+ * all [strongly connected components][] in the directed graph **g**. Each
+ * strongly connected component is composed of nodes that can reach all other
+ * nodes in the component via directed edges. A strongly connected component
+ * can consist of a single node if that node cannot both reach and be reached
+ * by any other specific node in the graph. Components of more than one node
+ * are guaranteed to have at least one cycle.
+ *
+ * This function returns an array of components. Each component is itself an
+ * array that contains the ids of all nodes in the component.
+ *
+ * [Tarjan's algorithm]: http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
+ * [strongly connected components]: http://en.wikipedia.org/wiki/Strongly_connected_component
+ *
+ * @param {Digraph} g the graph to search for strongly connected components
+ */
+function tarjan(g) {
+ if (!g.isDirected()) {
+ throw new Error("tarjan can only be applied to a directed graph. Bad input: " + g);
+ }
+
+ var index = 0,
+ stack = [],
+ visited = {}, // node id -> { onStack, lowlink, index }
+ results = [];
+
+ function dfs(u) {
+ var entry = visited[u] = {
+ onStack: true,
+ lowlink: index,
+ index: index++
+ };
+ stack.push(u);
+
+ g.successors(u).forEach(function(v) {
+ if (!(v in visited)) {
+ dfs(v);
+ entry.lowlink = Math.min(entry.lowlink, visited[v].lowlink);
+ } else if (visited[v].onStack) {
+ entry.lowlink = Math.min(entry.lowlink, visited[v].index);
+ }
+ });
+
+ if (entry.lowlink === entry.index) {
+ var cmpt = [],
+ v;
+ do {
+ v = stack.pop();
+ visited[v].onStack = false;
+ cmpt.push(v);
+ } while (u !== v);
+ results.push(cmpt);
+ }
+ }
+
+ g.nodes().forEach(function(u) {
+ if (!(u in visited)) {
+ dfs(u);
+ }
+ });
+
+ return results;
+}
+
+},{}],44:[function(require,module,exports){
+module.exports = topsort;
+topsort.CycleException = CycleException;
+
+/*
+ * Given a graph **g**, this function returns an ordered list of nodes such
+ * that for each edge `u -> v`, `u` appears before `v` in the list. If the
+ * graph has a cycle it is impossible to generate such a list and
+ * **CycleException** is thrown.
+ *
+ * See [topological sorting](https://en.wikipedia.org/wiki/Topological_sorting)
+ * for more details about how this algorithm works.
+ *
+ * @param {Digraph} g the graph to sort
+ */
+function topsort(g) {
+ if (!g.isDirected()) {
+ throw new Error("topsort can only be applied to a directed graph. Bad input: " + g);
+ }
+
+ var visited = {};
+ var stack = {};
+ var results = [];
+
+ function visit(node) {
+ if (node in stack) {
+ throw new CycleException();
+ }
+
+ if (!(node in visited)) {
+ stack[node] = true;
+ visited[node] = true;
+ g.predecessors(node).forEach(function(pred) {
+ visit(pred);
+ });
+ delete stack[node];
+ results.push(node);
+ }
+ }
+
+ var sinks = g.sinks();
+ if (g.order() !== 0 && sinks.length === 0) {
+ throw new CycleException();
+ }
+
+ g.sinks().forEach(function(sink) {
+ visit(sink);
+ });
+
+ return results;
+}
+
+function CycleException() {}
+
+CycleException.prototype.toString = function() {
+ return "Graph has at least one cycle";
+};
+
+},{}],45:[function(require,module,exports){
+// This file provides a helper function that mixes-in Dot behavior to an
+// existing graph prototype.
+
+/* jshint -W079 */
+var Set = require("cp-data").Set;
+/* jshint +W079 */
+
+module.exports = compoundify;
+
+// Extends the given SuperConstructor with the ability for nodes to contain
+// other nodes. A special node id `null` is used to indicate the root graph.
+function compoundify(SuperConstructor) {
+ function Constructor() {
+ SuperConstructor.call(this);
+
+ // Map of object id -> parent id (or null for root graph)
+ this._parents = {};
+
+ // Map of id (or null) -> children set
+ this._children = {};
+ this._children[null] = new Set();
+ }
+
+ Constructor.prototype = new SuperConstructor();
+ Constructor.prototype.constructor = Constructor;
+
+ Constructor.prototype.parent = function(u, parent) {
+ this._strictGetNode(u);
+
+ if (arguments.length < 2) {
+ return this._parents[u];
+ }
+
+ if (u === parent) {
+ throw new Error("Cannot make " + u + " a parent of itself");
+ }
+ if (parent !== null) {
+ this._strictGetNode(parent);
+ }
+
+ this._children[this._parents[u]].remove(u);
+ this._parents[u] = parent;
+ this._children[parent].add(u);
+ };
+
+ Constructor.prototype.children = function(u) {
+ if (u !== null) {
+ this._strictGetNode(u);
+ }
+ return this._children[u].keys();
+ };
+
+ Constructor.prototype.addNode = function(u, value) {
+ u = SuperConstructor.prototype.addNode.call(this, u, value);
+ this._parents[u] = null;
+ this._children[u] = new Set();
+ this._children[null].add(u);
+ return u;
+ };
+
+ Constructor.prototype.delNode = function(u) {
+ // Promote all children to the parent of the subgraph
+ var parent = this.parent(u);
+ this._children[u].keys().forEach(function(child) {
+ this.parent(child, parent);
+ }, this);
+
+ this._children[parent].remove(u);
+ delete this._parents[u];
+ delete this._children[u];
+
+ return SuperConstructor.prototype.delNode.call(this, u);
+ };
+
+ Constructor.prototype.copy = function() {
+ var copy = SuperConstructor.prototype.copy.call(this);
+ this.nodes().forEach(function(u) {
+ copy.parent(u, this.parent(u));
+ }, this);
+ return copy;
+ };
+
+ Constructor.prototype.filterNodes = function(filter) {
+ var self = this,
+ copy = SuperConstructor.prototype.filterNodes.call(this, filter);
+
+ var parents = {};
+ function findParent(u) {
+ var parent = self.parent(u);
+ if (parent === null || copy.hasNode(parent)) {
+ parents[u] = parent;
+ return parent;
+ } else if (parent in parents) {
+ return parents[parent];
+ } else {
+ return findParent(parent);
+ }
+ }
+
+ copy.eachNode(function(u) { copy.parent(u, findParent(u)); });
+
+ return copy;
+ };
+
+ return Constructor;
+}
+
+},{"cp-data":5}],46:[function(require,module,exports){
+var Graph = require("../Graph"),
+ Digraph = require("../Digraph"),
+ CGraph = require("../CGraph"),
+ CDigraph = require("../CDigraph");
+
+exports.decode = function(nodes, edges, Ctor) {
+ Ctor = Ctor || Digraph;
+
+ if (typeOf(nodes) !== "Array") {
+ throw new Error("nodes is not an Array");
+ }
+
+ if (typeOf(edges) !== "Array") {
+ throw new Error("edges is not an Array");
+ }
+
+ if (typeof Ctor === "string") {
+ switch(Ctor) {
+ case "graph": Ctor = Graph; break;
+ case "digraph": Ctor = Digraph; break;
+ case "cgraph": Ctor = CGraph; break;
+ case "cdigraph": Ctor = CDigraph; break;
+ default: throw new Error("Unrecognized graph type: " + Ctor);
+ }
+ }
+
+ var graph = new Ctor();
+
+ nodes.forEach(function(u) {
+ graph.addNode(u.id, u.value);
+ });
+
+ // If the graph is compound, set up children...
+ if (graph.parent) {
+ nodes.forEach(function(u) {
+ if (u.children) {
+ u.children.forEach(function(v) {
+ graph.parent(v, u.id);
+ });
+ }
+ });
+ }
+
+ edges.forEach(function(e) {
+ graph.addEdge(e.id, e.u, e.v, e.value);
+ });
+
+ return graph;
+};
+
+exports.encode = function(graph) {
+ var nodes = [];
+ var edges = [];
+
+ graph.eachNode(function(u, value) {
+ var node = {id: u, value: value};
+ if (graph.children) {
+ var children = graph.children(u);
+ if (children.length) {
+ node.children = children;
+ }
+ }
+ nodes.push(node);
+ });
+
+ graph.eachEdge(function(e, u, v, value) {
+ edges.push({id: e, u: u, v: v, value: value});
+ });
+
+ var type;
+ if (graph instanceof CDigraph) {
+ type = "cdigraph";
+ } else if (graph instanceof CGraph) {
+ type = "cgraph";
+ } else if (graph instanceof Digraph) {
+ type = "digraph";
+ } else if (graph instanceof Graph) {
+ type = "graph";
+ } else {
+ throw new Error("Couldn't determine type of graph: " + graph);
+ }
+
+ return { nodes: nodes, edges: edges, type: type };
+};
+
+function typeOf(obj) {
+ return Object.prototype.toString.call(obj).slice(8, -1);
+}
+
+},{"../CDigraph":30,"../CGraph":31,"../Digraph":32,"../Graph":33}],47:[function(require,module,exports){
+/* jshint -W079 */
+var Set = require("cp-data").Set;
+/* jshint +W079 */
+
+exports.all = function() {
+ return function() { return true; };
+};
+
+exports.nodesFromList = function(nodes) {
+ var set = new Set(nodes);
+ return function(u) {
+ return set.has(u);
+ };
+};
+
+},{"cp-data":5}],48:[function(require,module,exports){
+var Graph = require("./Graph"),
+ Digraph = require("./Digraph");
+
+// Side-effect based changes are lousy, but node doesn't seem to resolve the
+// requires cycle.
+
+/**
+ * Returns a new directed graph using the nodes and edges from this graph. The
+ * new graph will have the same nodes, but will have twice the number of edges:
+ * each edge is split into two edges with opposite directions. Edge ids,
+ * consequently, are not preserved by this transformation.
+ */
+Graph.prototype.toDigraph =
+Graph.prototype.asDirected = function() {
+ var g = new Digraph();
+ this.eachNode(function(u, value) { g.addNode(u, value); });
+ this.eachEdge(function(e, u, v, value) {
+ g.addEdge(null, u, v, value);
+ g.addEdge(null, v, u, value);
+ });
+ return g;
+};
+
+/**
+ * Returns a new undirected graph using the nodes and edges from this graph.
+ * The new graph will have the same nodes, but the edges will be made
+ * undirected. Edge ids are preserved in this transformation.
+ */
+Digraph.prototype.toGraph =
+Digraph.prototype.asUndirected = function() {
+ var g = new Graph();
+ this.eachNode(function(u, value) { g.addNode(u, value); });
+ this.eachEdge(function(e, u, v, value) {
+ g.addEdge(e, u, v, value);
+ });
+ return g;
+};
+
+},{"./Digraph":32,"./Graph":33}],49:[function(require,module,exports){
+// Returns an array of all values for properties of **o**.
+exports.values = function(o) {
+ var ks = Object.keys(o),
+ len = ks.length,
+ result = new Array(len),
+ i;
+ for (i = 0; i < len; ++i) {
+ result[i] = o[ks[i]];
+ }
+ return result;
+};
+
+},{}],50:[function(require,module,exports){
+module.exports = '0.7.4';
+
+},{}]},{},[1])
+; \ No newline at end of file
diff --git a/devtools/client/shared/vendor/fluent-react.js b/devtools/client/shared/vendor/fluent-react.js
new file mode 100644
index 0000000000..472c1247a4
--- /dev/null
+++ b/devtools/client/shared/vendor/fluent-react.js
@@ -0,0 +1,686 @@
+/* Copyright 2019 Mozilla Foundation and others
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+'use strict';
+
+/* fluent-react@0.10.0 */
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
+
+const react = require("resource://devtools/client/shared/vendor/react.js");
+const PropTypes = _interopDefault(require("resource://devtools/client/shared/vendor/react-prop-types.js"));
+
+/*
+ * Synchronously map an identifier or an array of identifiers to the best
+ * `FluentBundle` instance(s).
+ *
+ * @param {Iterable} iterable
+ * @param {string|Array<string>} ids
+ * @returns {FluentBundle|Array<FluentBundle>}
+ */
+function mapBundleSync(iterable, ids) {
+ if (!Array.isArray(ids)) {
+ return getBundleForId(iterable, ids);
+ }
+
+ return ids.map(
+ id => getBundleForId(iterable, id)
+ );
+}
+
+/*
+ * Find the best `FluentBundle` with the translation for `id`.
+ */
+function getBundleForId(iterable, id) {
+ for (const bundle of iterable) {
+ if (bundle.hasMessage(id)) {
+ return bundle;
+ }
+ }
+
+ return null;
+}
+
+/*
+ * Asynchronously map an identifier or an array of identifiers to the best
+ * `FluentBundle` instance(s).
+ *
+ * @param {AsyncIterable} iterable
+ * @param {string|Array<string>} ids
+ * @returns {Promise<FluentBundle|Array<FluentBundle>>}
+ */
+
+/*
+ * @module fluent-sequence
+ * @overview Manage ordered sequences of FluentBundles.
+ */
+
+/*
+ * Base CachedIterable class.
+ */
+class CachedIterable extends Array {
+ /**
+ * Create a `CachedIterable` instance from an iterable or, if another
+ * instance of `CachedIterable` is passed, return it without any
+ * modifications.
+ *
+ * @param {Iterable} iterable
+ * @returns {CachedIterable}
+ */
+ static from(iterable) {
+ if (iterable instanceof this) {
+ return iterable;
+ }
+
+ return new this(iterable);
+ }
+}
+
+/*
+ * CachedSyncIterable caches the elements yielded by an iterable.
+ *
+ * It can be used to iterate over an iterable many times without depleting the
+ * iterable.
+ */
+class CachedSyncIterable extends CachedIterable {
+ /**
+ * Create an `CachedSyncIterable` instance.
+ *
+ * @param {Iterable} iterable
+ * @returns {CachedSyncIterable}
+ */
+ constructor(iterable) {
+ super();
+
+ if (Symbol.iterator in Object(iterable)) {
+ this.iterator = iterable[Symbol.iterator]();
+ } else {
+ throw new TypeError("Argument must implement the iteration protocol.");
+ }
+ }
+
+ [Symbol.iterator]() {
+ const cached = this;
+ let cur = 0;
+
+ return {
+ next() {
+ if (cached.length <= cur) {
+ cached.push(cached.iterator.next());
+ }
+ return cached[cur++];
+ }
+ };
+ }
+
+ /**
+ * This method allows user to consume the next element from the iterator
+ * into the cache.
+ *
+ * @param {number} count - number of elements to consume
+ */
+ touchNext(count = 1) {
+ let idx = 0;
+ while (idx++ < count) {
+ const last = this[this.length - 1];
+ if (last && last.done) {
+ break;
+ }
+ this.push(this.iterator.next());
+ }
+ // Return the last cached {value, done} object to allow the calling
+ // code to decide if it needs to call touchNext again.
+ return this[this.length - 1];
+ }
+}
+
+/*
+ * `ReactLocalization` handles translation formatting and fallback.
+ *
+ * The current negotiated fallback chain of languages is stored in the
+ * `ReactLocalization` instance in form of an iterable of `FluentBundle`
+ * instances. This iterable is used to find the best existing translation for
+ * a given identifier.
+ *
+ * `Localized` components must subscribe to the changes of the
+ * `ReactLocalization`'s fallback chain. When the fallback chain changes (the
+ * `bundles` iterable is set anew), all subscribed compontent must relocalize.
+ *
+ * The `ReactLocalization` class instances are exposed to `Localized` elements
+ * via the `LocalizationProvider` component.
+ */
+class ReactLocalization {
+ constructor(bundles) {
+ this.bundles = CachedSyncIterable.from(bundles);
+ this.subs = new Set();
+ }
+
+ /*
+ * Subscribe a `Localized` component to changes of `bundles`.
+ */
+ subscribe(comp) {
+ this.subs.add(comp);
+ }
+
+ /*
+ * Unsubscribe a `Localized` component from `bundles` changes.
+ */
+ unsubscribe(comp) {
+ this.subs.delete(comp);
+ }
+
+ /*
+ * Set a new `bundles` iterable and trigger the retranslation.
+ */
+ setBundles(bundles) {
+ this.bundles = CachedSyncIterable.from(bundles);
+
+ // Update all subscribed Localized components.
+ this.subs.forEach(comp => comp.relocalize());
+ }
+
+ getBundle(id) {
+ return mapBundleSync(this.bundles, id);
+ }
+
+ /*
+ * Find a translation by `id` and format it to a string using `args`.
+ */
+ getString(id, args, fallback) {
+ const bundle = this.getBundle(id);
+ if (bundle) {
+ const msg = bundle.getMessage(id);
+ if (msg && msg.value) {
+ let errors = [];
+ let value = bundle.formatPattern(msg.value, args, errors);
+ for (let error of errors) {
+ this.reportError(error);
+ }
+ return value;
+ }
+ }
+
+ return fallback || id;
+ }
+
+ // XXX Control this via a prop passed to the LocalizationProvider.
+ // See https://github.com/projectfluent/fluent.js/issues/411.
+ reportError(error) {
+ /* global console */
+ // eslint-disable-next-line no-console
+ console.warn(`[@fluent/react] ${error.name}: ${error.message}`);
+ }
+}
+
+function isReactLocalization(props, propName) {
+ const prop = props[propName];
+
+ if (prop instanceof ReactLocalization) {
+ return null;
+ }
+
+ return new Error(
+ `The ${propName} context field must be an instance of ReactLocalization.`
+ );
+}
+
+/* eslint-env browser */
+
+let cachedParseMarkup;
+
+// We use a function creator to make the reference to `document` lazy. At the
+// same time, it's eager enough to throw in <LocalizationProvider> as soon as
+// it's first mounted which reduces the risk of this error making it to the
+// runtime without developers noticing it in development.
+function createParseMarkup() {
+ if (typeof(document) === "undefined") {
+ // We can't use <template> to sanitize translations.
+ throw new Error(
+ "`document` is undefined. Without it, translations cannot " +
+ "be safely sanitized. Consult the documentation at " +
+ "https://github.com/projectfluent/fluent.js/wiki/React-Overlays."
+ );
+ }
+
+ if (!cachedParseMarkup) {
+ const template = document.createElement("template");
+ cachedParseMarkup = function parseMarkup(str) {
+ template.innerHTML = str;
+ return Array.from(template.content.childNodes);
+ };
+ }
+
+ return cachedParseMarkup;
+}
+
+/*
+ * The Provider component for the `ReactLocalization` class.
+ *
+ * Exposes a `ReactLocalization` instance to all descendants via React's
+ * context feature. It makes translations available to all localizable
+ * elements in the descendant's render tree without the need to pass them
+ * explicitly.
+ *
+ * <LocalizationProvider bundles={…}>
+ * …
+ * </LocalizationProvider>
+ *
+ * The `LocalizationProvider` component takes one prop: `bundles`. It should
+ * be an iterable of `FluentBundle` instances in order of the user's
+ * preferred languages. The `FluentBundle` instances will be used by
+ * `ReactLocalization` to format translations. If a translation is missing in
+ * one instance, `ReactLocalization` will fall back to the next one.
+ */
+class LocalizationProvider extends react.Component {
+ constructor(props) {
+ super(props);
+ const {bundles, parseMarkup} = props;
+
+ if (bundles === undefined) {
+ throw new Error("LocalizationProvider must receive the bundles prop.");
+ }
+
+ if (!bundles[Symbol.iterator]) {
+ throw new Error("The bundles prop must be an iterable.");
+ }
+
+ this.l10n = new ReactLocalization(bundles);
+ this.parseMarkup = parseMarkup || createParseMarkup();
+ }
+
+ getChildContext() {
+ return {
+ l10n: this.l10n,
+ parseMarkup: this.parseMarkup,
+ };
+ }
+
+ componentWillReceiveProps(next) {
+ const { bundles } = next;
+
+ if (bundles !== this.props.bundles) {
+ this.l10n.setBundles(bundles);
+ }
+ }
+
+ render() {
+ return react.Children.only(this.props.children);
+ }
+}
+
+LocalizationProvider.childContextTypes = {
+ l10n: isReactLocalization,
+ parseMarkup: PropTypes.func,
+};
+
+LocalizationProvider.propTypes = {
+ children: PropTypes.element.isRequired,
+ bundles: isIterable,
+ parseMarkup: PropTypes.func,
+};
+
+function isIterable(props, propName, componentName) {
+ const prop = props[propName];
+
+ if (Symbol.iterator in Object(prop)) {
+ return null;
+ }
+
+ return new Error(
+ `The ${propName} prop supplied to ${componentName} must be an iterable.`
+ );
+}
+
+function withLocalization(Inner) {
+ class WithLocalization extends react.Component {
+ componentDidMount() {
+ const { l10n } = this.context;
+
+ if (l10n) {
+ l10n.subscribe(this);
+ }
+ }
+
+ componentWillUnmount() {
+ const { l10n } = this.context;
+
+ if (l10n) {
+ l10n.unsubscribe(this);
+ }
+ }
+
+ /*
+ * Rerender this component in a new language.
+ */
+ relocalize() {
+ // When the `ReactLocalization`'s fallback chain changes, update the
+ // component.
+ this.forceUpdate();
+ }
+
+ /*
+ * Find a translation by `id` and format it to a string using `args`.
+ */
+ getString(id, args, fallback) {
+ const { l10n } = this.context;
+
+ if (!l10n) {
+ return fallback || id;
+ }
+
+ return l10n.getString(id, args, fallback);
+ }
+
+ render() {
+ return react.createElement(
+ Inner,
+ Object.assign(
+ // getString needs to be re-bound on updates to trigger a re-render
+ { getString: (...args) => this.getString(...args) },
+ this.props
+ )
+ );
+ }
+ }
+
+ WithLocalization.displayName = `WithLocalization(${displayName(Inner)})`;
+
+ WithLocalization.contextTypes = {
+ l10n: isReactLocalization
+ };
+
+ return WithLocalization;
+}
+
+function displayName(component) {
+ return component.displayName || component.name || "Component";
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in this directory.
+ */
+
+// For HTML, certain tags should omit their close tag. We keep a whitelist for
+// those special-case tags.
+
+var omittedCloseTags = {
+ area: true,
+ base: true,
+ br: true,
+ col: true,
+ embed: true,
+ hr: true,
+ img: true,
+ input: true,
+ keygen: true,
+ link: true,
+ meta: true,
+ param: true,
+ source: true,
+ track: true,
+ wbr: true,
+ // NOTE: menuitem's close tag should be omitted, but that causes problems.
+};
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in this directory.
+ */
+
+// For HTML, certain tags cannot have children. This has the same purpose as
+// `omittedCloseTags` except that `menuitem` should still have its closing tag.
+
+var voidElementTags = {
+ menuitem: true,
+ ...omittedCloseTags,
+};
+
+// Match the opening angle bracket (<) in HTML tags, and HTML entities like
+// &amp;, &#0038;, &#x0026;.
+const reMarkup = /<|&#?\w+;/;
+
+/*
+ * Prepare props passed to `Localized` for formatting.
+ */
+function toArguments(props) {
+ const args = {};
+ const elems = {};
+
+ for (const [propname, propval] of Object.entries(props)) {
+ if (propname.startsWith("$")) {
+ const name = propname.substr(1);
+ args[name] = propval;
+ } else if (react.isValidElement(propval)) {
+ // We'll try to match localNames of elements found in the translation with
+ // names of elements passed as props. localNames are always lowercase.
+ const name = propname.toLowerCase();
+ elems[name] = propval;
+ }
+ }
+
+ return [args, elems];
+}
+
+/*
+ * The `Localized` class renders its child with translated props and children.
+ *
+ * <Localized id="hello-world">
+ * <p>{'Hello, world!'}</p>
+ * </Localized>
+ *
+ * The `id` prop should be the unique identifier of the translation. Any
+ * attributes found in the translation will be applied to the wrapped element.
+ *
+ * Arguments to the translation can be passed as `$`-prefixed props on
+ * `Localized`.
+ *
+ * <Localized id="hello-world" $username={name}>
+ * <p>{'Hello, { $username }!'}</p>
+ * </Localized>
+ *
+ * It's recommended that the contents of the wrapped component be a string
+ * expression. The string will be used as the ultimate fallback if no
+ * translation is available. It also makes it easy to grep for strings in the
+ * source code.
+ */
+class Localized extends react.Component {
+ componentDidMount() {
+ const { l10n } = this.context;
+
+ if (l10n) {
+ l10n.subscribe(this);
+ }
+ }
+
+ componentWillUnmount() {
+ const { l10n } = this.context;
+
+ if (l10n) {
+ l10n.unsubscribe(this);
+ }
+ }
+
+ /*
+ * Rerender this component in a new language.
+ */
+ relocalize() {
+ // When the `ReactLocalization`'s fallback chain changes, update the
+ // component.
+ this.forceUpdate();
+ }
+
+ render() {
+ const { l10n, parseMarkup } = this.context;
+ const { id, attrs, children: child = null } = this.props;
+
+ // Validate that the child element isn't an array
+ if (Array.isArray(child)) {
+ throw new Error("<Localized/> expected to receive a single " +
+ "React node child");
+ }
+
+ if (!l10n) {
+ // Use the wrapped component as fallback.
+ return child;
+ }
+
+ const bundle = l10n.getBundle(id);
+
+ if (bundle === null) {
+ // Use the wrapped component as fallback.
+ return child;
+ }
+
+ const msg = bundle.getMessage(id);
+ const [args, elems] = toArguments(this.props);
+ let errors = [];
+
+ // Check if the child inside <Localized> is a valid element -- if not, then
+ // it's either null or a simple fallback string. No need to localize the
+ // attributes.
+ if (!react.isValidElement(child)) {
+ if (msg.value) {
+ // Replace the fallback string with the message value;
+ let value = bundle.formatPattern(msg.value, args, errors);
+ for (let error of errors) {
+ l10n.reportError(error);
+ }
+ return value;
+ }
+
+ return child;
+ }
+
+ let localizedProps;
+
+ // The default is to forbid all message attributes. If the attrs prop exists
+ // on the Localized instance, only set message attributes which have been
+ // explicitly allowed by the developer.
+ if (attrs && msg.attributes) {
+ localizedProps = {};
+ errors = [];
+ for (const [name, allowed] of Object.entries(attrs)) {
+ if (allowed && name in msg.attributes) {
+ localizedProps[name] = bundle.formatPattern(
+ msg.attributes[name], args, errors);
+ }
+ }
+ for (let error of errors) {
+ l10n.reportError(error);
+ }
+ }
+
+ // If the wrapped component is a known void element, explicitly dismiss the
+ // message value and do not pass it to cloneElement in order to avoid the
+ // "void element tags must neither have `children` nor use
+ // `dangerouslySetInnerHTML`" error.
+ if (child.type in voidElementTags) {
+ return react.cloneElement(child, localizedProps);
+ }
+
+ // If the message has a null value, we're only interested in its attributes.
+ // Do not pass the null value to cloneElement as it would nuke all children
+ // of the wrapped component.
+ if (msg.value === null) {
+ return react.cloneElement(child, localizedProps);
+ }
+
+ errors = [];
+ const messageValue = bundle.formatPattern(msg.value, args, errors);
+ for (let error of errors) {
+ l10n.reportError(error);
+ }
+
+ // If the message value doesn't contain any markup nor any HTML entities,
+ // insert it as the only child of the wrapped component.
+ if (!reMarkup.test(messageValue)) {
+ return react.cloneElement(child, localizedProps, messageValue);
+ }
+
+ // If the message contains markup, parse it and try to match the children
+ // found in the translation with the props passed to this Localized.
+ const translationNodes = parseMarkup(messageValue);
+ const translatedChildren = translationNodes.map(childNode => {
+ if (childNode.nodeType === childNode.TEXT_NODE) {
+ return childNode.textContent;
+ }
+
+ // If the child is not expected just take its textContent.
+ if (!elems.hasOwnProperty(childNode.localName)) {
+ return childNode.textContent;
+ }
+
+ const sourceChild = elems[childNode.localName];
+
+ // If the element passed as a prop to <Localized> is a known void element,
+ // explicitly dismiss any textContent which might have accidentally been
+ // defined in the translation to prevent the "void element tags must not
+ // have children" error.
+ if (sourceChild.type in voidElementTags) {
+ return sourceChild;
+ }
+
+ // TODO Protect contents of elements wrapped in <Localized>
+ // https://github.com/projectfluent/fluent.js/issues/184
+ // TODO Control localizable attributes on elements passed as props
+ // https://github.com/projectfluent/fluent.js/issues/185
+ return react.cloneElement(sourceChild, null, childNode.textContent);
+ });
+
+ return react.cloneElement(child, localizedProps, ...translatedChildren);
+ }
+}
+
+Localized.contextTypes = {
+ l10n: isReactLocalization,
+ parseMarkup: PropTypes.func,
+};
+
+Localized.propTypes = {
+ children: PropTypes.node
+};
+
+/*
+ * @module fluent-react
+ * @overview
+ *
+
+ * `fluent-react` provides React bindings for Fluent. It takes advantage of
+ * React's Components system and the virtual DOM. Translations are exposed to
+ * components via the provider pattern.
+ *
+ * <LocalizationProvider bundles={…}>
+ * <Localized id="hello-world">
+ * <p>{'Hello, world!'}</p>
+ * </Localized>
+ * </LocalizationProvider>
+ *
+ * Consult the documentation of the `LocalizationProvider` and the `Localized`
+ * components for more information.
+ */
+
+exports.LocalizationProvider = LocalizationProvider;
+exports.Localized = Localized;
+exports.ReactLocalization = ReactLocalization;
+exports.isReactLocalization = isReactLocalization;
+exports.withLocalization = withLocalization;
diff --git a/devtools/client/shared/vendor/fuzzaldrin-plus.js b/devtools/client/shared/vendor/fuzzaldrin-plus.js
new file mode 100644
index 0000000000..08334d02fe
--- /dev/null
+++ b/devtools/client/shared/vendor/fuzzaldrin-plus.js
@@ -0,0 +1,1074 @@
+/* fuzzaldrin-plus - v0.5.0 - @license: MIT; @author: Jean Christophe Roy; @site: https://github.com/jeancroy/fuzzaldrin-plus */
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.fuzzaldrin = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+(function() {
+ var Query, pathScorer, pluckCandidates, scorer, sortCandidates;
+
+ scorer = require('./scorer');
+
+ pathScorer = require('./pathScorer');
+
+ Query = require('./query');
+
+ pluckCandidates = function(a) {
+ return a.candidate;
+ };
+
+ sortCandidates = function(a, b) {
+ return b.score - a.score;
+ };
+
+ module.exports = function(candidates, query, options) {
+ var bKey, candidate, key, maxInners, maxResults, score, scoreProvider, scoredCandidates, spotLeft, string, usePathScoring, _i, _len;
+ scoredCandidates = [];
+ key = options.key, maxResults = options.maxResults, maxInners = options.maxInners, usePathScoring = options.usePathScoring;
+ spotLeft = (maxInners != null) && maxInners > 0 ? maxInners : candidates.length + 1;
+ bKey = key != null;
+ scoreProvider = usePathScoring ? pathScorer : scorer;
+ for (_i = 0, _len = candidates.length; _i < _len; _i++) {
+ candidate = candidates[_i];
+ string = bKey ? candidate[key] : candidate;
+ if (!string) {
+ continue;
+ }
+ score = scoreProvider.score(string, query, options);
+ if (score > 0) {
+ scoredCandidates.push({
+ candidate: candidate,
+ score: score
+ });
+ if (!--spotLeft) {
+ break;
+ }
+ }
+ }
+ scoredCandidates.sort(sortCandidates);
+ candidates = scoredCandidates.map(pluckCandidates);
+ if (maxResults != null) {
+ candidates = candidates.slice(0, maxResults);
+ }
+ return candidates;
+ };
+
+}).call(this);
+
+},{"./pathScorer":4,"./query":5,"./scorer":6}],2:[function(require,module,exports){
+(function (process){
+(function() {
+ var Query, defaultPathSeparator, filter, matcher, parseOptions, pathScorer, preparedQueryCache, scorer;
+
+ filter = require('./filter');
+
+ matcher = require('./matcher');
+
+ scorer = require('./scorer');
+
+ pathScorer = require('./pathScorer');
+
+ Query = require('./query');
+
+ preparedQueryCache = null;
+
+ defaultPathSeparator = (typeof process !== "undefined" && process !== null ? process.platform : void 0) === "win32" ? '\\' : '/';
+
+ module.exports = {
+ filter: function(candidates, query, options) {
+ if (options == null) {
+ options = {};
+ }
+ if (!((query != null ? query.length : void 0) && (candidates != null ? candidates.length : void 0))) {
+ return [];
+ }
+ options = parseOptions(options, query);
+ return filter(candidates, query, options);
+ },
+ score: function(string, query, options) {
+ if (options == null) {
+ options = {};
+ }
+ if (!((string != null ? string.length : void 0) && (query != null ? query.length : void 0))) {
+ return 0;
+ }
+ options = parseOptions(options, query);
+ if (options.usePathScoring) {
+ return pathScorer.score(string, query, options);
+ } else {
+ return scorer.score(string, query, options);
+ }
+ },
+ match: function(string, query, options) {
+ var _i, _ref, _results;
+ if (options == null) {
+ options = {};
+ }
+ if (!string) {
+ return [];
+ }
+ if (!query) {
+ return [];
+ }
+ if (string === query) {
+ return (function() {
+ _results = [];
+ for (var _i = 0, _ref = string.length; 0 <= _ref ? _i < _ref : _i > _ref; 0 <= _ref ? _i++ : _i--){ _results.push(_i); }
+ return _results;
+ }).apply(this);
+ }
+ options = parseOptions(options, query);
+ return matcher.match(string, query, options);
+ },
+ wrap: function(string, query, options) {
+ if (options == null) {
+ options = {};
+ }
+ if (!string) {
+ return [];
+ }
+ if (!query) {
+ return [];
+ }
+ options = parseOptions(options, query);
+ return matcher.wrap(string, query, options);
+ },
+ prepareQuery: function(query, options) {
+ if (options == null) {
+ options = {};
+ }
+ options = parseOptions(options, query);
+ return options.preparedQuery;
+ }
+ };
+
+ parseOptions = function(options, query) {
+ if (options.allowErrors == null) {
+ options.allowErrors = false;
+ }
+ if (options.usePathScoring == null) {
+ options.usePathScoring = true;
+ }
+ if (options.useExtensionBonus == null) {
+ options.useExtensionBonus = false;
+ }
+ if (options.pathSeparator == null) {
+ options.pathSeparator = defaultPathSeparator;
+ }
+ if (options.optCharRegEx == null) {
+ options.optCharRegEx = null;
+ }
+ if (options.wrap == null) {
+ options.wrap = null;
+ }
+ if (options.preparedQuery == null) {
+ options.preparedQuery = preparedQueryCache && preparedQueryCache.query === query ? preparedQueryCache : (preparedQueryCache = new Query(query, options));
+ }
+ return options;
+ };
+
+}).call(this);
+
+}).call(this,require('_process'))
+},{"./filter":1,"./matcher":3,"./pathScorer":4,"./query":5,"./scorer":6,"_process":7}],3:[function(require,module,exports){
+(function() {
+ var basenameMatch, computeMatch, isMatch, isWordStart, match, mergeMatches, scoreAcronyms, scoreCharacter, scoreConsecutives, _ref;
+
+ _ref = require('./scorer'), isMatch = _ref.isMatch, isWordStart = _ref.isWordStart, scoreConsecutives = _ref.scoreConsecutives, scoreCharacter = _ref.scoreCharacter, scoreAcronyms = _ref.scoreAcronyms;
+
+ exports.match = match = function(string, query, options) {
+ var allowErrors, baseMatches, matches, pathSeparator, preparedQuery, string_lw;
+ allowErrors = options.allowErrors, preparedQuery = options.preparedQuery, pathSeparator = options.pathSeparator;
+ if (!(allowErrors || isMatch(string, preparedQuery.core_lw, preparedQuery.core_up))) {
+ return [];
+ }
+ string_lw = string.toLowerCase();
+ matches = computeMatch(string, string_lw, preparedQuery);
+ if (matches.length === 0) {
+ return matches;
+ }
+ if (string.indexOf(pathSeparator) > -1) {
+ baseMatches = basenameMatch(string, string_lw, preparedQuery, pathSeparator);
+ matches = mergeMatches(matches, baseMatches);
+ }
+ return matches;
+ };
+
+ exports.wrap = function(string, query, options) {
+ var matchIndex, matchPos, matchPositions, output, strPos, tagClass, tagClose, tagOpen, _ref1;
+ if ((options.wrap != null)) {
+ _ref1 = options.wrap, tagClass = _ref1.tagClass, tagOpen = _ref1.tagOpen, tagClose = _ref1.tagClose;
+ }
+ if (tagClass == null) {
+ tagClass = 'highlight';
+ }
+ if (tagOpen == null) {
+ tagOpen = '<strong class="' + tagClass + '">';
+ }
+ if (tagClose == null) {
+ tagClose = '</strong>';
+ }
+ if (string === query) {
+ return tagOpen + string + tagClose;
+ }
+ matchPositions = match(string, query, options);
+ if (matchPositions.length === 0) {
+ return string;
+ }
+ output = '';
+ matchIndex = -1;
+ strPos = 0;
+ while (++matchIndex < matchPositions.length) {
+ matchPos = matchPositions[matchIndex];
+ if (matchPos > strPos) {
+ output += string.substring(strPos, matchPos);
+ strPos = matchPos;
+ }
+ while (++matchIndex < matchPositions.length) {
+ if (matchPositions[matchIndex] === matchPos + 1) {
+ matchPos++;
+ } else {
+ matchIndex--;
+ break;
+ }
+ }
+ matchPos++;
+ if (matchPos > strPos) {
+ output += tagOpen;
+ output += string.substring(strPos, matchPos);
+ output += tagClose;
+ strPos = matchPos;
+ }
+ }
+ if (strPos <= string.length - 1) {
+ output += string.substring(strPos);
+ }
+ return output;
+ };
+
+ basenameMatch = function(subject, subject_lw, preparedQuery, pathSeparator) {
+ var basePos, depth, end;
+ end = subject.length - 1;
+ while (subject[end] === pathSeparator) {
+ end--;
+ }
+ basePos = subject.lastIndexOf(pathSeparator, end);
+ if (basePos === -1) {
+ return [];
+ }
+ depth = preparedQuery.depth;
+ while (depth-- > 0) {
+ basePos = subject.lastIndexOf(pathSeparator, basePos - 1);
+ if (basePos === -1) {
+ return [];
+ }
+ }
+ basePos++;
+ end++;
+ return computeMatch(subject.slice(basePos, end), subject_lw.slice(basePos, end), preparedQuery, basePos);
+ };
+
+ mergeMatches = function(a, b) {
+ var ai, bj, i, j, m, n, out;
+ m = a.length;
+ n = b.length;
+ if (n === 0) {
+ return a.slice();
+ }
+ if (m === 0) {
+ return b.slice();
+ }
+ i = -1;
+ j = 0;
+ bj = b[j];
+ out = [];
+ while (++i < m) {
+ ai = a[i];
+ while (bj <= ai && ++j < n) {
+ if (bj < ai) {
+ out.push(bj);
+ }
+ bj = b[j];
+ }
+ out.push(ai);
+ }
+ while (j < n) {
+ out.push(b[j++]);
+ }
+ return out;
+ };
+
+ computeMatch = function(subject, subject_lw, preparedQuery, offset) {
+ var DIAGONAL, LEFT, STOP, UP, acro_score, align, backtrack, csc_diag, csc_row, csc_score, i, j, m, matches, move, n, pos, query, query_lw, score, score_diag, score_row, score_up, si_lw, start, trace;
+ if (offset == null) {
+ offset = 0;
+ }
+ query = preparedQuery.query;
+ query_lw = preparedQuery.query_lw;
+ m = subject.length;
+ n = query.length;
+ acro_score = scoreAcronyms(subject, subject_lw, query, query_lw).score;
+ score_row = new Array(n);
+ csc_row = new Array(n);
+ STOP = 0;
+ UP = 1;
+ LEFT = 2;
+ DIAGONAL = 3;
+ trace = new Array(m * n);
+ pos = -1;
+ j = -1;
+ while (++j < n) {
+ score_row[j] = 0;
+ csc_row[j] = 0;
+ }
+ i = -1;
+ while (++i < m) {
+ score = 0;
+ score_up = 0;
+ csc_diag = 0;
+ si_lw = subject_lw[i];
+ j = -1;
+ while (++j < n) {
+ csc_score = 0;
+ align = 0;
+ score_diag = score_up;
+ if (query_lw[j] === si_lw) {
+ start = isWordStart(i, subject, subject_lw);
+ csc_score = csc_diag > 0 ? csc_diag : scoreConsecutives(subject, subject_lw, query, query_lw, i, j, start);
+ align = score_diag + scoreCharacter(i, j, start, acro_score, csc_score);
+ }
+ score_up = score_row[j];
+ csc_diag = csc_row[j];
+ if (score > score_up) {
+ move = LEFT;
+ } else {
+ score = score_up;
+ move = UP;
+ }
+ if (align > score) {
+ score = align;
+ move = DIAGONAL;
+ } else {
+ csc_score = 0;
+ }
+ score_row[j] = score;
+ csc_row[j] = csc_score;
+ trace[++pos] = score > 0 ? move : STOP;
+ }
+ }
+ i = m - 1;
+ j = n - 1;
+ pos = i * n + j;
+ backtrack = true;
+ matches = [];
+ while (backtrack && i >= 0 && j >= 0) {
+ switch (trace[pos]) {
+ case UP:
+ i--;
+ pos -= n;
+ break;
+ case LEFT:
+ j--;
+ pos--;
+ break;
+ case DIAGONAL:
+ matches.push(i + offset);
+ j--;
+ i--;
+ pos -= n + 1;
+ break;
+ default:
+ backtrack = false;
+ }
+ }
+ matches.reverse();
+ return matches;
+ };
+
+}).call(this);
+
+},{"./scorer":6}],4:[function(require,module,exports){
+(function() {
+ var computeScore, countDir, file_coeff, getExtension, getExtensionScore, isMatch, scorePath, scoreSize, tau_depth, _ref;
+
+ _ref = require('./scorer'), isMatch = _ref.isMatch, computeScore = _ref.computeScore, scoreSize = _ref.scoreSize;
+
+ tau_depth = 13;
+
+ file_coeff = 1.5;
+
+ exports.score = function(string, query, options) {
+ var allowErrors, preparedQuery, score, string_lw;
+ preparedQuery = options.preparedQuery, allowErrors = options.allowErrors;
+ if (!(allowErrors || isMatch(string, preparedQuery.core_lw, preparedQuery.core_up))) {
+ return 0;
+ }
+ string_lw = string.toLowerCase();
+ score = computeScore(string, string_lw, preparedQuery);
+ score = scorePath(string, string_lw, score, options);
+ return Math.ceil(score);
+ };
+
+ scorePath = function(subject, subject_lw, fullPathScore, options) {
+ var alpha, basePathScore, basePos, depth, end, extAdjust, fileLength, pathSeparator, preparedQuery, useExtensionBonus;
+ if (fullPathScore === 0) {
+ return 0;
+ }
+ preparedQuery = options.preparedQuery, useExtensionBonus = options.useExtensionBonus, pathSeparator = options.pathSeparator;
+ end = subject.length - 1;
+ while (subject[end] === pathSeparator) {
+ end--;
+ }
+ basePos = subject.lastIndexOf(pathSeparator, end);
+ fileLength = end - basePos;
+ extAdjust = 1.0;
+ if (useExtensionBonus) {
+ extAdjust += getExtensionScore(subject_lw, preparedQuery.ext, basePos, end, 2);
+ fullPathScore *= extAdjust;
+ }
+ if (basePos === -1) {
+ return fullPathScore;
+ }
+ depth = preparedQuery.depth;
+ while (basePos > -1 && depth-- > 0) {
+ basePos = subject.lastIndexOf(pathSeparator, basePos - 1);
+ }
+ basePathScore = basePos === -1 ? fullPathScore : extAdjust * computeScore(subject.slice(basePos + 1, end + 1), subject_lw.slice(basePos + 1, end + 1), preparedQuery);
+ alpha = 0.5 * tau_depth / (tau_depth + countDir(subject, end + 1, pathSeparator));
+ return alpha * basePathScore + (1 - alpha) * fullPathScore * scoreSize(0, file_coeff * fileLength);
+ };
+
+ exports.countDir = countDir = function(path, end, pathSeparator) {
+ var count, i;
+ if (end < 1) {
+ return 0;
+ }
+ count = 0;
+ i = -1;
+ while (++i < end && path[i] === pathSeparator) {
+ continue;
+ }
+ while (++i < end) {
+ if (path[i] === pathSeparator) {
+ count++;
+ while (++i < end && path[i] === pathSeparator) {
+ continue;
+ }
+ }
+ }
+ return count;
+ };
+
+ exports.getExtension = getExtension = function(str) {
+ var pos;
+ pos = str.lastIndexOf(".");
+ if (pos < 0) {
+ return "";
+ } else {
+ return str.substr(pos + 1);
+ }
+ };
+
+ getExtensionScore = function(candidate, ext, startPos, endPos, maxDepth) {
+ var m, matched, n, pos;
+ if (!ext.length) {
+ return 0;
+ }
+ pos = candidate.lastIndexOf(".", endPos);
+ if (!(pos > startPos)) {
+ return 0;
+ }
+ n = ext.length;
+ m = endPos - pos;
+ if (m < n) {
+ n = m;
+ m = ext.length;
+ }
+ pos++;
+ matched = -1;
+ while (++matched < n) {
+ if (candidate[pos + matched] !== ext[matched]) {
+ break;
+ }
+ }
+ if (matched === 0 && maxDepth > 0) {
+ return 0.9 * getExtensionScore(candidate, ext, startPos, pos - 2, maxDepth - 1);
+ }
+ return matched / m;
+ };
+
+}).call(this);
+
+},{"./scorer":6}],5:[function(require,module,exports){
+(function() {
+ var Query, coreChars, countDir, getCharCodes, getExtension, opt_char_re, truncatedUpperCase, _ref;
+
+ _ref = require("./pathScorer"), countDir = _ref.countDir, getExtension = _ref.getExtension;
+
+ module.exports = Query = (function() {
+ function Query(query, _arg) {
+ var optCharRegEx, pathSeparator, _ref1;
+ _ref1 = _arg != null ? _arg : {}, optCharRegEx = _ref1.optCharRegEx, pathSeparator = _ref1.pathSeparator;
+ if (!(query && query.length)) {
+ return null;
+ }
+ this.query = query;
+ this.query_lw = query.toLowerCase();
+ this.core = coreChars(query, optCharRegEx);
+ this.core_lw = this.core.toLowerCase();
+ this.core_up = truncatedUpperCase(this.core);
+ this.depth = countDir(query, query.length, pathSeparator);
+ this.ext = getExtension(this.query_lw);
+ this.charCodes = getCharCodes(this.query_lw);
+ }
+
+ return Query;
+
+ })();
+
+ opt_char_re = /[ _\-:\/\\]/g;
+
+ coreChars = function(query, optCharRegEx) {
+ if (optCharRegEx == null) {
+ optCharRegEx = opt_char_re;
+ }
+ return query.replace(optCharRegEx, '');
+ };
+
+ truncatedUpperCase = function(str) {
+ var char, upper, _i, _len;
+ upper = "";
+ for (_i = 0, _len = str.length; _i < _len; _i++) {
+ char = str[_i];
+ upper += char.toUpperCase()[0];
+ }
+ return upper;
+ };
+
+ getCharCodes = function(str) {
+ var charCodes, i, len;
+ len = str.length;
+ i = -1;
+ charCodes = [];
+ while (++i < len) {
+ charCodes[str.charCodeAt(i)] = true;
+ }
+ return charCodes;
+ };
+
+}).call(this);
+
+},{"./pathScorer":4}],6:[function(require,module,exports){
+(function() {
+ var AcronymResult, computeScore, emptyAcronymResult, isAcronymFullWord, isMatch, isSeparator, isWordEnd, isWordStart, miss_coeff, pos_bonus, scoreAcronyms, scoreCharacter, scoreConsecutives, scoreExact, scoreExactMatch, scorePattern, scorePosition, scoreSize, tau_size, wm;
+
+ wm = 150;
+
+ pos_bonus = 20;
+
+ tau_size = 85;
+
+ miss_coeff = 0.75;
+
+ exports.score = function(string, query, options) {
+ var allowErrors, preparedQuery, score, string_lw;
+ preparedQuery = options.preparedQuery, allowErrors = options.allowErrors;
+ if (!(allowErrors || isMatch(string, preparedQuery.core_lw, preparedQuery.core_up))) {
+ return 0;
+ }
+ string_lw = string.toLowerCase();
+ score = computeScore(string, string_lw, preparedQuery);
+ return Math.ceil(score);
+ };
+
+ exports.isMatch = isMatch = function(subject, query_lw, query_up) {
+ var i, j, m, n, qj_lw, qj_up, si;
+ m = subject.length;
+ n = query_lw.length;
+ if (!m || n > m) {
+ return false;
+ }
+ i = -1;
+ j = -1;
+ while (++j < n) {
+ qj_lw = query_lw.charCodeAt(j);
+ qj_up = query_up.charCodeAt(j);
+ while (++i < m) {
+ si = subject.charCodeAt(i);
+ if (si === qj_lw || si === qj_up) {
+ break;
+ }
+ }
+ if (i === m) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ exports.computeScore = computeScore = function(subject, subject_lw, preparedQuery) {
+ var acro, acro_score, align, csc_diag, csc_row, csc_score, csc_should_rebuild, i, j, m, miss_budget, miss_left, n, pos, query, query_lw, record_miss, score, score_diag, score_row, score_up, si_lw, start, sz;
+ query = preparedQuery.query;
+ query_lw = preparedQuery.query_lw;
+ m = subject.length;
+ n = query.length;
+ acro = scoreAcronyms(subject, subject_lw, query, query_lw);
+ acro_score = acro.score;
+ if (acro.count === n) {
+ return scoreExact(n, m, acro_score, acro.pos);
+ }
+ pos = subject_lw.indexOf(query_lw);
+ if (pos > -1) {
+ return scoreExactMatch(subject, subject_lw, query, query_lw, pos, n, m);
+ }
+ score_row = new Array(n);
+ csc_row = new Array(n);
+ sz = scoreSize(n, m);
+ miss_budget = Math.ceil(miss_coeff * n) + 5;
+ miss_left = miss_budget;
+ csc_should_rebuild = true;
+ j = -1;
+ while (++j < n) {
+ score_row[j] = 0;
+ csc_row[j] = 0;
+ }
+ i = -1;
+ while (++i < m) {
+ si_lw = subject_lw[i];
+ if (!si_lw.charCodeAt(0) in preparedQuery.charCodes) {
+ if (csc_should_rebuild) {
+ j = -1;
+ while (++j < n) {
+ csc_row[j] = 0;
+ }
+ csc_should_rebuild = false;
+ }
+ continue;
+ }
+ score = 0;
+ score_diag = 0;
+ csc_diag = 0;
+ record_miss = true;
+ csc_should_rebuild = true;
+ j = -1;
+ while (++j < n) {
+ score_up = score_row[j];
+ if (score_up > score) {
+ score = score_up;
+ }
+ csc_score = 0;
+ if (query_lw[j] === si_lw) {
+ start = isWordStart(i, subject, subject_lw);
+ csc_score = csc_diag > 0 ? csc_diag : scoreConsecutives(subject, subject_lw, query, query_lw, i, j, start);
+ align = score_diag + scoreCharacter(i, j, start, acro_score, csc_score);
+ if (align > score) {
+ score = align;
+ miss_left = miss_budget;
+ } else {
+ if (record_miss && --miss_left <= 0) {
+ return Math.max(score, score_row[n - 1]) * sz;
+ }
+ record_miss = false;
+ }
+ }
+ score_diag = score_up;
+ csc_diag = csc_row[j];
+ csc_row[j] = csc_score;
+ score_row[j] = score;
+ }
+ }
+ score = score_row[n - 1];
+ return score * sz;
+ };
+
+ exports.isWordStart = isWordStart = function(pos, subject, subject_lw) {
+ var curr_s, prev_s;
+ if (pos === 0) {
+ return true;
+ }
+ curr_s = subject[pos];
+ prev_s = subject[pos - 1];
+ return isSeparator(prev_s) || (curr_s !== subject_lw[pos] && prev_s === subject_lw[pos - 1]);
+ };
+
+ exports.isWordEnd = isWordEnd = function(pos, subject, subject_lw, len) {
+ var curr_s, next_s;
+ if (pos === len - 1) {
+ return true;
+ }
+ curr_s = subject[pos];
+ next_s = subject[pos + 1];
+ return isSeparator(next_s) || (curr_s === subject_lw[pos] && next_s !== subject_lw[pos + 1]);
+ };
+
+ isSeparator = function(c) {
+ return c === ' ' || c === '.' || c === '-' || c === '_' || c === '/' || c === '\\';
+ };
+
+ scorePosition = function(pos) {
+ var sc;
+ if (pos < pos_bonus) {
+ sc = pos_bonus - pos;
+ return 100 + sc * sc;
+ } else {
+ return Math.max(100 + pos_bonus - pos, 0);
+ }
+ };
+
+ exports.scoreSize = scoreSize = function(n, m) {
+ return tau_size / (tau_size + Math.abs(m - n));
+ };
+
+ scoreExact = function(n, m, quality, pos) {
+ return 2 * n * (wm * quality + scorePosition(pos)) * scoreSize(n, m);
+ };
+
+ exports.scorePattern = scorePattern = function(count, len, sameCase, start, end) {
+ var bonus, sz;
+ sz = count;
+ bonus = 6;
+ if (sameCase === count) {
+ bonus += 2;
+ }
+ if (start) {
+ bonus += 3;
+ }
+ if (end) {
+ bonus += 1;
+ }
+ if (count === len) {
+ if (start) {
+ if (sameCase === len) {
+ sz += 2;
+ } else {
+ sz += 1;
+ }
+ }
+ if (end) {
+ bonus += 1;
+ }
+ }
+ return sameCase + sz * (sz + bonus);
+ };
+
+ exports.scoreCharacter = scoreCharacter = function(i, j, start, acro_score, csc_score) {
+ var posBonus;
+ posBonus = scorePosition(i);
+ if (start) {
+ return posBonus + wm * ((acro_score > csc_score ? acro_score : csc_score) + 10);
+ }
+ return posBonus + wm * csc_score;
+ };
+
+ exports.scoreConsecutives = scoreConsecutives = function(subject, subject_lw, query, query_lw, i, j, startOfWord) {
+ var k, m, mi, n, nj, sameCase, sz;
+ m = subject.length;
+ n = query.length;
+ mi = m - i;
+ nj = n - j;
+ k = mi < nj ? mi : nj;
+ sameCase = 0;
+ sz = 0;
+ if (query[j] === subject[i]) {
+ sameCase++;
+ }
+ while (++sz < k && query_lw[++j] === subject_lw[++i]) {
+ if (query[j] === subject[i]) {
+ sameCase++;
+ }
+ }
+ if (sz < k) {
+ i--;
+ }
+ if (sz === 1) {
+ return 1 + 2 * sameCase;
+ }
+ return scorePattern(sz, n, sameCase, startOfWord, isWordEnd(i, subject, subject_lw, m));
+ };
+
+ exports.scoreExactMatch = scoreExactMatch = function(subject, subject_lw, query, query_lw, pos, n, m) {
+ var end, i, pos2, sameCase, start;
+ start = isWordStart(pos, subject, subject_lw);
+ if (!start) {
+ pos2 = subject_lw.indexOf(query_lw, pos + 1);
+ if (pos2 > -1) {
+ start = isWordStart(pos2, subject, subject_lw);
+ if (start) {
+ pos = pos2;
+ }
+ }
+ }
+ i = -1;
+ sameCase = 0;
+ while (++i < n) {
+ if (query[pos + i] === subject[i]) {
+ sameCase++;
+ }
+ }
+ end = isWordEnd(pos + n - 1, subject, subject_lw, m);
+ return scoreExact(n, m, scorePattern(n, n, sameCase, start, end), pos);
+ };
+
+ AcronymResult = (function() {
+ function AcronymResult(score, pos, count) {
+ this.score = score;
+ this.pos = pos;
+ this.count = count;
+ }
+
+ return AcronymResult;
+
+ })();
+
+ emptyAcronymResult = new AcronymResult(0, 0.1, 0);
+
+ exports.scoreAcronyms = scoreAcronyms = function(subject, subject_lw, query, query_lw) {
+ var count, fullWord, i, j, m, n, qj_lw, sameCase, score, sepCount, sumPos;
+ m = subject.length;
+ n = query.length;
+ if (!(m > 1 && n > 1)) {
+ return emptyAcronymResult;
+ }
+ count = 0;
+ sepCount = 0;
+ sumPos = 0;
+ sameCase = 0;
+ i = -1;
+ j = -1;
+ while (++j < n) {
+ qj_lw = query_lw[j];
+ if (isSeparator(qj_lw)) {
+ i = subject_lw.indexOf(qj_lw, i + 1);
+ if (i > -1) {
+ sepCount++;
+ continue;
+ } else {
+ break;
+ }
+ }
+ while (++i < m) {
+ if (qj_lw === subject_lw[i] && isWordStart(i, subject, subject_lw)) {
+ if (query[j] === subject[i]) {
+ sameCase++;
+ }
+ sumPos += i;
+ count++;
+ break;
+ }
+ }
+ if (i === m) {
+ break;
+ }
+ }
+ if (count < 2) {
+ return emptyAcronymResult;
+ }
+ fullWord = count === n ? isAcronymFullWord(subject, subject_lw, query, count) : false;
+ score = scorePattern(count, n, sameCase, true, fullWord);
+ return new AcronymResult(score, sumPos / count, count + sepCount);
+ };
+
+ isAcronymFullWord = function(subject, subject_lw, query, nbAcronymInQuery) {
+ var count, i, m, n;
+ m = subject.length;
+ n = query.length;
+ count = 0;
+ if (m > 12 * n) {
+ return false;
+ }
+ i = -1;
+ while (++i < m) {
+ if (isWordStart(i, subject, subject_lw) && ++count > nbAcronymInQuery) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+}).call(this);
+
+},{}],7:[function(require,module,exports){
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things. But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals. It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+ throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+ throw new Error('clearTimeout has not been defined');
+}
+(function () {
+ try {
+ if (typeof setTimeout === 'function') {
+ cachedSetTimeout = setTimeout;
+ } else {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ } catch (e) {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ try {
+ if (typeof clearTimeout === 'function') {
+ cachedClearTimeout = clearTimeout;
+ } else {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+ } catch (e) {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+} ())
+function runTimeout(fun) {
+ if (cachedSetTimeout === setTimeout) {
+ //normal enviroments in sane situations
+ return setTimeout(fun, 0);
+ }
+ // if setTimeout wasn't available but was latter defined
+ if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+ cachedSetTimeout = setTimeout;
+ return setTimeout(fun, 0);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedSetTimeout(fun, 0);
+ } catch(e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedSetTimeout.call(null, fun, 0);
+ } catch(e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+ return cachedSetTimeout.call(this, fun, 0);
+ }
+ }
+
+
+}
+function runClearTimeout(marker) {
+ if (cachedClearTimeout === clearTimeout) {
+ //normal enviroments in sane situations
+ return clearTimeout(marker);
+ }
+ // if clearTimeout wasn't available but was latter defined
+ if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+ cachedClearTimeout = clearTimeout;
+ return clearTimeout(marker);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedClearTimeout(marker);
+ } catch (e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedClearTimeout.call(null, marker);
+ } catch (e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+ // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+ return cachedClearTimeout.call(this, marker);
+ }
+ }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ if (!draining || !currentQueue) {
+ return;
+ }
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = runTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
+ }
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ runTimeout(drainQueue);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}]},{},[2])(2)
+}); \ No newline at end of file
diff --git a/devtools/client/shared/vendor/immutable.js b/devtools/client/shared/vendor/immutable.js
new file mode 100644
index 0000000000..4903f34b34
--- /dev/null
+++ b/devtools/client/shared/vendor/immutable.js
@@ -0,0 +1,4997 @@
+/**
+ * Copyright (c) 2014-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.Immutable = factory());
+}(this, function () { 'use strict';var SLICE$0 = Array.prototype.slice;
+
+ function createClass(ctor, superClass) {
+ if (superClass) {
+ ctor.prototype = Object.create(superClass.prototype);
+ }
+ ctor.prototype.constructor = ctor;
+ }
+
+ function Iterable(value) {
+ return isIterable(value) ? value : Seq(value);
+ }
+
+
+ createClass(KeyedIterable, Iterable);
+ function KeyedIterable(value) {
+ return isKeyed(value) ? value : KeyedSeq(value);
+ }
+
+
+ createClass(IndexedIterable, Iterable);
+ function IndexedIterable(value) {
+ return isIndexed(value) ? value : IndexedSeq(value);
+ }
+
+
+ createClass(SetIterable, Iterable);
+ function SetIterable(value) {
+ return isIterable(value) && !isAssociative(value) ? value : SetSeq(value);
+ }
+
+
+
+ function isIterable(maybeIterable) {
+ return !!(maybeIterable && maybeIterable[IS_ITERABLE_SENTINEL]);
+ }
+
+ function isKeyed(maybeKeyed) {
+ return !!(maybeKeyed && maybeKeyed[IS_KEYED_SENTINEL]);
+ }
+
+ function isIndexed(maybeIndexed) {
+ return !!(maybeIndexed && maybeIndexed[IS_INDEXED_SENTINEL]);
+ }
+
+ function isAssociative(maybeAssociative) {
+ return isKeyed(maybeAssociative) || isIndexed(maybeAssociative);
+ }
+
+ function isOrdered(maybeOrdered) {
+ return !!(maybeOrdered && maybeOrdered[IS_ORDERED_SENTINEL]);
+ }
+
+ Iterable.isIterable = isIterable;
+ Iterable.isKeyed = isKeyed;
+ Iterable.isIndexed = isIndexed;
+ Iterable.isAssociative = isAssociative;
+ Iterable.isOrdered = isOrdered;
+
+ Iterable.Keyed = KeyedIterable;
+ Iterable.Indexed = IndexedIterable;
+ Iterable.Set = SetIterable;
+
+
+ var IS_ITERABLE_SENTINEL = '@@__IMMUTABLE_ITERABLE__@@';
+ var IS_KEYED_SENTINEL = '@@__IMMUTABLE_KEYED__@@';
+ var IS_INDEXED_SENTINEL = '@@__IMMUTABLE_INDEXED__@@';
+ var IS_ORDERED_SENTINEL = '@@__IMMUTABLE_ORDERED__@@';
+
+ // Used for setting prototype methods that IE8 chokes on.
+ var DELETE = 'delete';
+
+ // Constants describing the size of trie nodes.
+ var SHIFT = 5; // Resulted in best performance after ______?
+ var SIZE = 1 << SHIFT;
+ var MASK = SIZE - 1;
+
+ // A consistent shared value representing "not set" which equals nothing other
+ // than itself, and nothing that could be provided externally.
+ var NOT_SET = {};
+
+ // Boolean references, Rough equivalent of `bool &`.
+ var CHANGE_LENGTH = { value: false };
+ var DID_ALTER = { value: false };
+
+ function MakeRef(ref) {
+ ref.value = false;
+ return ref;
+ }
+
+ function SetRef(ref) {
+ ref && (ref.value = true);
+ }
+
+ // A function which returns a value representing an "owner" for transient writes
+ // to tries. The return value will only ever equal itself, and will not equal
+ // the return of any subsequent call of this function.
+ function OwnerID() {}
+
+ // http://jsperf.com/copy-array-inline
+ function arrCopy(arr, offset) {
+ offset = offset || 0;
+ var len = Math.max(0, arr.length - offset);
+ var newArr = new Array(len);
+ for (var ii = 0; ii < len; ii++) {
+ newArr[ii] = arr[ii + offset];
+ }
+ return newArr;
+ }
+
+ function ensureSize(iter) {
+ if (iter.size === undefined) {
+ iter.size = iter.__iterate(returnTrue);
+ }
+ return iter.size;
+ }
+
+ function wrapIndex(iter, index) {
+ // This implements "is array index" which the ECMAString spec defines as:
+ //
+ // A String property name P is an array index if and only if
+ // ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
+ // to 2^32−1.
+ //
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-array-exotic-objects
+ if (typeof index !== 'number') {
+ var uint32Index = index >>> 0; // N >>> 0 is shorthand for ToUint32
+ if ('' + uint32Index !== index || uint32Index === 4294967295) {
+ return NaN;
+ }
+ index = uint32Index;
+ }
+ return index < 0 ? ensureSize(iter) + index : index;
+ }
+
+ function returnTrue() {
+ return true;
+ }
+
+ function wholeSlice(begin, end, size) {
+ return (begin === 0 || (size !== undefined && begin <= -size)) &&
+ (end === undefined || (size !== undefined && end >= size));
+ }
+
+ function resolveBegin(begin, size) {
+ return resolveIndex(begin, size, 0);
+ }
+
+ function resolveEnd(end, size) {
+ return resolveIndex(end, size, size);
+ }
+
+ function resolveIndex(index, size, defaultIndex) {
+ return index === undefined ?
+ defaultIndex :
+ index < 0 ?
+ Math.max(0, size + index) :
+ size === undefined ?
+ index :
+ Math.min(size, index);
+ }
+
+ /* global Symbol */
+
+ var ITERATE_KEYS = 0;
+ var ITERATE_VALUES = 1;
+ var ITERATE_ENTRIES = 2;
+
+ var REAL_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+ var FAUX_ITERATOR_SYMBOL = '@@iterator';
+
+ var ITERATOR_SYMBOL = REAL_ITERATOR_SYMBOL || FAUX_ITERATOR_SYMBOL;
+
+
+ function Iterator(next) {
+ this.next = next;
+ }
+
+ Iterator.prototype.toString = function() {
+ return '[Iterator]';
+ };
+
+
+ Iterator.KEYS = ITERATE_KEYS;
+ Iterator.VALUES = ITERATE_VALUES;
+ Iterator.ENTRIES = ITERATE_ENTRIES;
+
+ Iterator.prototype.inspect =
+ Iterator.prototype.toSource = function () { return this.toString(); }
+ Iterator.prototype[ITERATOR_SYMBOL] = function () {
+ return this;
+ };
+
+
+ function iteratorValue(type, k, v, iteratorResult) {
+ var value = type === 0 ? k : type === 1 ? v : [k, v];
+ iteratorResult ? (iteratorResult.value = value) : (iteratorResult = {
+ value: value, done: false
+ });
+ return iteratorResult;
+ }
+
+ function iteratorDone() {
+ return { value: undefined, done: true };
+ }
+
+ function hasIterator(maybeIterable) {
+ return !!getIteratorFn(maybeIterable);
+ }
+
+ function isIterator(maybeIterator) {
+ return maybeIterator && typeof maybeIterator.next === 'function';
+ }
+
+ function getIterator(iterable) {
+ var iteratorFn = getIteratorFn(iterable);
+ return iteratorFn && iteratorFn.call(iterable);
+ }
+
+ function getIteratorFn(iterable) {
+ var iteratorFn = iterable && (
+ (REAL_ITERATOR_SYMBOL && iterable[REAL_ITERATOR_SYMBOL]) ||
+ iterable[FAUX_ITERATOR_SYMBOL]
+ );
+ if (typeof iteratorFn === 'function') {
+ return iteratorFn;
+ }
+ }
+
+ function isArrayLike(value) {
+ return value && typeof value.length === 'number';
+ }
+
+ createClass(Seq, Iterable);
+ function Seq(value) {
+ return value === null || value === undefined ? emptySequence() :
+ isIterable(value) ? value.toSeq() : seqFromValue(value);
+ }
+
+ Seq.of = function(/*...values*/) {
+ return Seq(arguments);
+ };
+
+ Seq.prototype.toSeq = function() {
+ return this;
+ };
+
+ Seq.prototype.toString = function() {
+ return this.__toString('Seq {', '}');
+ };
+
+ Seq.prototype.cacheResult = function() {
+ if (!this._cache && this.__iterateUncached) {
+ this._cache = this.entrySeq().toArray();
+ this.size = this._cache.length;
+ }
+ return this;
+ };
+
+ // abstract __iterateUncached(fn, reverse)
+
+ Seq.prototype.__iterate = function(fn, reverse) {
+ return seqIterate(this, fn, reverse, true);
+ };
+
+ // abstract __iteratorUncached(type, reverse)
+
+ Seq.prototype.__iterator = function(type, reverse) {
+ return seqIterator(this, type, reverse, true);
+ };
+
+
+
+ createClass(KeyedSeq, Seq);
+ function KeyedSeq(value) {
+ return value === null || value === undefined ?
+ emptySequence().toKeyedSeq() :
+ isIterable(value) ?
+ (isKeyed(value) ? value.toSeq() : value.fromEntrySeq()) :
+ keyedSeqFromValue(value);
+ }
+
+ KeyedSeq.prototype.toKeyedSeq = function() {
+ return this;
+ };
+
+
+
+ createClass(IndexedSeq, Seq);
+ function IndexedSeq(value) {
+ return value === null || value === undefined ? emptySequence() :
+ !isIterable(value) ? indexedSeqFromValue(value) :
+ isKeyed(value) ? value.entrySeq() : value.toIndexedSeq();
+ }
+
+ IndexedSeq.of = function(/*...values*/) {
+ return IndexedSeq(arguments);
+ };
+
+ IndexedSeq.prototype.toIndexedSeq = function() {
+ return this;
+ };
+
+ IndexedSeq.prototype.toString = function() {
+ return this.__toString('Seq [', ']');
+ };
+
+ IndexedSeq.prototype.__iterate = function(fn, reverse) {
+ return seqIterate(this, fn, reverse, false);
+ };
+
+ IndexedSeq.prototype.__iterator = function(type, reverse) {
+ return seqIterator(this, type, reverse, false);
+ };
+
+
+
+ createClass(SetSeq, Seq);
+ function SetSeq(value) {
+ return (
+ value === null || value === undefined ? emptySequence() :
+ !isIterable(value) ? indexedSeqFromValue(value) :
+ isKeyed(value) ? value.entrySeq() : value
+ ).toSetSeq();
+ }
+
+ SetSeq.of = function(/*...values*/) {
+ return SetSeq(arguments);
+ };
+
+ SetSeq.prototype.toSetSeq = function() {
+ return this;
+ };
+
+
+
+ Seq.isSeq = isSeq;
+ Seq.Keyed = KeyedSeq;
+ Seq.Set = SetSeq;
+ Seq.Indexed = IndexedSeq;
+
+ var IS_SEQ_SENTINEL = '@@__IMMUTABLE_SEQ__@@';
+
+ Seq.prototype[IS_SEQ_SENTINEL] = true;
+
+
+
+ createClass(ArraySeq, IndexedSeq);
+ function ArraySeq(array) {
+ this._array = array;
+ this.size = array.length;
+ }
+
+ ArraySeq.prototype.get = function(index, notSetValue) {
+ return this.has(index) ? this._array[wrapIndex(this, index)] : notSetValue;
+ };
+
+ ArraySeq.prototype.__iterate = function(fn, reverse) {
+ var array = this._array;
+ var maxIndex = array.length - 1;
+ for (var ii = 0; ii <= maxIndex; ii++) {
+ if (fn(array[reverse ? maxIndex - ii : ii], ii, this) === false) {
+ return ii + 1;
+ }
+ }
+ return ii;
+ };
+
+ ArraySeq.prototype.__iterator = function(type, reverse) {
+ var array = this._array;
+ var maxIndex = array.length - 1;
+ var ii = 0;
+ return new Iterator(function()
+ {return ii > maxIndex ?
+ iteratorDone() :
+ iteratorValue(type, ii, array[reverse ? maxIndex - ii++ : ii++])}
+ );
+ };
+
+
+
+ createClass(ObjectSeq, KeyedSeq);
+ function ObjectSeq(object) {
+ var keys = Object.keys(object);
+ this._object = object;
+ this._keys = keys;
+ this.size = keys.length;
+ }
+
+ ObjectSeq.prototype.get = function(key, notSetValue) {
+ if (notSetValue !== undefined && !this.has(key)) {
+ return notSetValue;
+ }
+ return this._object[key];
+ };
+
+ ObjectSeq.prototype.has = function(key) {
+ return this._object.hasOwnProperty(key);
+ };
+
+ ObjectSeq.prototype.__iterate = function(fn, reverse) {
+ var object = this._object;
+ var keys = this._keys;
+ var maxIndex = keys.length - 1;
+ for (var ii = 0; ii <= maxIndex; ii++) {
+ var key = keys[reverse ? maxIndex - ii : ii];
+ if (fn(object[key], key, this) === false) {
+ return ii + 1;
+ }
+ }
+ return ii;
+ };
+
+ ObjectSeq.prototype.__iterator = function(type, reverse) {
+ var object = this._object;
+ var keys = this._keys;
+ var maxIndex = keys.length - 1;
+ var ii = 0;
+ return new Iterator(function() {
+ var key = keys[reverse ? maxIndex - ii : ii];
+ return ii++ > maxIndex ?
+ iteratorDone() :
+ iteratorValue(type, key, object[key]);
+ });
+ };
+
+ ObjectSeq.prototype[IS_ORDERED_SENTINEL] = true;
+
+
+ createClass(IterableSeq, IndexedSeq);
+ function IterableSeq(iterable) {
+ this._iterable = iterable;
+ this.size = iterable.length || iterable.size;
+ }
+
+ IterableSeq.prototype.__iterateUncached = function(fn, reverse) {
+ if (reverse) {
+ return this.cacheResult().__iterate(fn, reverse);
+ }
+ var iterable = this._iterable;
+ var iterator = getIterator(iterable);
+ var iterations = 0;
+ if (isIterator(iterator)) {
+ var step;
+ while (!(step = iterator.next()).done) {
+ if (fn(step.value, iterations++, this) === false) {
+ break;
+ }
+ }
+ }
+ return iterations;
+ };
+
+ IterableSeq.prototype.__iteratorUncached = function(type, reverse) {
+ if (reverse) {
+ return this.cacheResult().__iterator(type, reverse);
+ }
+ var iterable = this._iterable;
+ var iterator = getIterator(iterable);
+ if (!isIterator(iterator)) {
+ return new Iterator(iteratorDone);
+ }
+ var iterations = 0;
+ return new Iterator(function() {
+ var step = iterator.next();
+ return step.done ? step : iteratorValue(type, iterations++, step.value);
+ });
+ };
+
+
+
+ createClass(IteratorSeq, IndexedSeq);
+ function IteratorSeq(iterator) {
+ this._iterator = iterator;
+ this._iteratorCache = [];
+ }
+
+ IteratorSeq.prototype.__iterateUncached = function(fn, reverse) {
+ if (reverse) {
+ return this.cacheResult().__iterate(fn, reverse);
+ }
+ var iterator = this._iterator;
+ var cache = this._iteratorCache;
+ var iterations = 0;
+ while (iterations < cache.length) {
+ if (fn(cache[iterations], iterations++, this) === false) {
+ return iterations;
+ }
+ }
+ var step;
+ while (!(step = iterator.next()).done) {
+ var val = step.value;
+ cache[iterations] = val;
+ if (fn(val, iterations++, this) === false) {
+ break;
+ }
+ }
+ return iterations;
+ };
+
+ IteratorSeq.prototype.__iteratorUncached = function(type, reverse) {
+ if (reverse) {
+ return this.cacheResult().__iterator(type, reverse);
+ }
+ var iterator = this._iterator;
+ var cache = this._iteratorCache;
+ var iterations = 0;
+ return new Iterator(function() {
+ if (iterations >= cache.length) {
+ var step = iterator.next();
+ if (step.done) {
+ return step;
+ }
+ cache[iterations] = step.value;
+ }
+ return iteratorValue(type, iterations, cache[iterations++]);
+ });
+ };
+
+
+
+
+ // # pragma Helper functions
+
+ function isSeq(maybeSeq) {
+ return !!(maybeSeq && maybeSeq[IS_SEQ_SENTINEL]);
+ }
+
+ var EMPTY_SEQ;
+
+ function emptySequence() {
+ return EMPTY_SEQ || (EMPTY_SEQ = new ArraySeq([]));
+ }
+
+ function keyedSeqFromValue(value) {
+ var seq =
+ Array.isArray(value) ? new ArraySeq(value).fromEntrySeq() :
+ isIterator(value) ? new IteratorSeq(value).fromEntrySeq() :
+ hasIterator(value) ? new IterableSeq(value).fromEntrySeq() :
+ typeof value === 'object' ? new ObjectSeq(value) :
+ undefined;
+ if (!seq) {
+ throw new TypeError(
+ 'Expected Array or iterable object of [k, v] entries, '+
+ 'or keyed object: ' + value
+ );
+ }
+ return seq;
+ }
+
+ function indexedSeqFromValue(value) {
+ var seq = maybeIndexedSeqFromValue(value);
+ if (!seq) {
+ throw new TypeError(
+ 'Expected Array or iterable object of values: ' + value
+ );
+ }
+ return seq;
+ }
+
+ function seqFromValue(value) {
+ var seq = maybeIndexedSeqFromValue(value) ||
+ (typeof value === 'object' && new ObjectSeq(value));
+ if (!seq) {
+ throw new TypeError(
+ 'Expected Array or iterable object of values, or keyed object: ' + value
+ );
+ }
+ return seq;
+ }
+
+ function maybeIndexedSeqFromValue(value) {
+ return (
+ isArrayLike(value) ? new ArraySeq(value) :
+ isIterator(value) ? new IteratorSeq(value) :
+ hasIterator(value) ? new IterableSeq(value) :
+ undefined
+ );
+ }
+
+ function seqIterate(seq, fn, reverse, useKeys) {
+ var cache = seq._cache;
+ if (cache) {
+ var maxIndex = cache.length - 1;
+ for (var ii = 0; ii <= maxIndex; ii++) {
+ var entry = cache[reverse ? maxIndex - ii : ii];
+ if (fn(entry[1], useKeys ? entry[0] : ii, seq) === false) {
+ return ii + 1;
+ }
+ }
+ return ii;
+ }
+ return seq.__iterateUncached(fn, reverse);
+ }
+
+ function seqIterator(seq, type, reverse, useKeys) {
+ var cache = seq._cache;
+ if (cache) {
+ var maxIndex = cache.length - 1;
+ var ii = 0;
+ return new Iterator(function() {
+ var entry = cache[reverse ? maxIndex - ii : ii];
+ return ii++ > maxIndex ?
+ iteratorDone() :
+ iteratorValue(type, useKeys ? entry[0] : ii - 1, entry[1]);
+ });
+ }
+ return seq.__iteratorUncached(type, reverse);
+ }
+
+ function fromJS(json, converter) {
+ return converter ?
+ fromJSWith(converter, json, '', {'': json}) :
+ fromJSDefault(json);
+ }
+
+ function fromJSWith(converter, json, key, parentJSON) {
+ if (Array.isArray(json)) {
+ return converter.call(parentJSON, key, IndexedSeq(json).map(function(v, k) {return fromJSWith(converter, v, k, json)}));
+ }
+ if (isPlainObj(json)) {
+ return converter.call(parentJSON, key, KeyedSeq(json).map(function(v, k) {return fromJSWith(converter, v, k, json)}));
+ }
+ return json;
+ }
+
+ function fromJSDefault(json) {
+ if (Array.isArray(json)) {
+ return IndexedSeq(json).map(fromJSDefault).toList();
+ }
+ if (isPlainObj(json)) {
+ return KeyedSeq(json).map(fromJSDefault).toMap();
+ }
+ return json;
+ }
+
+ function isPlainObj(value) {
+ return value && (value.constructor === Object || value.constructor === undefined);
+ }
+
+ /**
+ * An extension of the "same-value" algorithm as [described for use by ES6 Map
+ * and Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Key_equality)
+ *
+ * NaN is considered the same as NaN, however -0 and 0 are considered the same
+ * value, which is different from the algorithm described by
+ * [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is).
+ *
+ * This is extended further to allow Objects to describe the values they
+ * represent, by way of `valueOf` or `equals` (and `hashCode`).
+ *
+ * Note: because of this extension, the key equality of Immutable.Map and the
+ * value equality of Immutable.Set will differ from ES6 Map and Set.
+ *
+ * ### Defining custom values
+ *
+ * The easiest way to describe the value an object represents is by implementing
+ * `valueOf`. For example, `Date` represents a value by returning a unix
+ * timestamp for `valueOf`:
+ *
+ * var date1 = new Date(1234567890000); // Fri Feb 13 2009 ...
+ * var date2 = new Date(1234567890000);
+ * date1.valueOf(); // 1234567890000
+ * assert( date1 !== date2 );
+ * assert( Immutable.is( date1, date2 ) );
+ *
+ * Note: overriding `valueOf` may have other implications if you use this object
+ * where JavaScript expects a primitive, such as implicit string coercion.
+ *
+ * For more complex types, especially collections, implementing `valueOf` may
+ * not be performant. An alternative is to implement `equals` and `hashCode`.
+ *
+ * `equals` takes another object, presumably of similar type, and returns true
+ * if the it is equal. Equality is symmetrical, so the same result should be
+ * returned if this and the argument are flipped.
+ *
+ * assert( a.equals(b) === b.equals(a) );
+ *
+ * `hashCode` returns a 32bit integer number representing the object which will
+ * be used to determine how to store the value object in a Map or Set. You must
+ * provide both or neither methods, one must not exist without the other.
+ *
+ * Also, an important relationship between these methods must be upheld: if two
+ * values are equal, they *must* return the same hashCode. If the values are not
+ * equal, they might have the same hashCode; this is called a hash collision,
+ * and while undesirable for performance reasons, it is acceptable.
+ *
+ * if (a.equals(b)) {
+ * assert( a.hashCode() === b.hashCode() );
+ * }
+ *
+ * All Immutable collections implement `equals` and `hashCode`.
+ *
+ */
+ function is(valueA, valueB) {
+ if (valueA === valueB || (valueA !== valueA && valueB !== valueB)) {
+ return true;
+ }
+ if (!valueA || !valueB) {
+ return false;
+ }
+ if (typeof valueA.valueOf === 'function' &&
+ typeof valueB.valueOf === 'function') {
+ valueA = valueA.valueOf();
+ valueB = valueB.valueOf();
+ if (valueA === valueB || (valueA !== valueA && valueB !== valueB)) {
+ return true;
+ }
+ if (!valueA || !valueB) {
+ return false;
+ }
+ }
+ if (typeof valueA.equals === 'function' &&
+ typeof valueB.equals === 'function' &&
+ valueA.equals(valueB)) {
+ return true;
+ }
+ return false;
+ }
+
+ function deepEqual(a, b) {
+ if (a === b) {
+ return true;
+ }
+
+ if (
+ !isIterable(b) ||
+ a.size !== undefined && b.size !== undefined && a.size !== b.size ||
+ a.__hash !== undefined && b.__hash !== undefined && a.__hash !== b.__hash ||
+ isKeyed(a) !== isKeyed(b) ||
+ isIndexed(a) !== isIndexed(b) ||
+ isOrdered(a) !== isOrdered(b)
+ ) {
+ return false;
+ }
+
+ if (a.size === 0 && b.size === 0) {
+ return true;
+ }
+
+ var notAssociative = !isAssociative(a);
+
+ if (isOrdered(a)) {
+ var entries = a.entries();
+ return b.every(function(v, k) {
+ var entry = entries.next().value;
+ return entry && is(entry[1], v) && (notAssociative || is(entry[0], k));
+ }) && entries.next().done;
+ }
+
+ var flipped = false;
+
+ if (a.size === undefined) {
+ if (b.size === undefined) {
+ if (typeof a.cacheResult === 'function') {
+ a.cacheResult();
+ }
+ } else {
+ flipped = true;
+ var _ = a;
+ a = b;
+ b = _;
+ }
+ }
+
+ var allEqual = true;
+ var bSize = b.__iterate(function(v, k) {
+ if (notAssociative ? !a.has(v) :
+ flipped ? !is(v, a.get(k, NOT_SET)) : !is(a.get(k, NOT_SET), v)) {
+ allEqual = false;
+ return false;
+ }
+ });
+
+ return allEqual && a.size === bSize;
+ }
+
+ createClass(Repeat, IndexedSeq);
+
+ function Repeat(value, times) {
+ if (!(this instanceof Repeat)) {
+ return new Repeat(value, times);
+ }
+ this._value = value;
+ this.size = times === undefined ? Infinity : Math.max(0, times);
+ if (this.size === 0) {
+ if (EMPTY_REPEAT) {
+ return EMPTY_REPEAT;
+ }
+ EMPTY_REPEAT = this;
+ }
+ }
+
+ Repeat.prototype.toString = function() {
+ if (this.size === 0) {
+ return 'Repeat []';
+ }
+ return 'Repeat [ ' + this._value + ' ' + this.size + ' times ]';
+ };
+
+ Repeat.prototype.get = function(index, notSetValue) {
+ return this.has(index) ? this._value : notSetValue;
+ };
+
+ Repeat.prototype.includes = function(searchValue) {
+ return is(this._value, searchValue);
+ };
+
+ Repeat.prototype.slice = function(begin, end) {
+ var size = this.size;
+ return wholeSlice(begin, end, size) ? this :
+ new Repeat(this._value, resolveEnd(end, size) - resolveBegin(begin, size));
+ };
+
+ Repeat.prototype.reverse = function() {
+ return this;
+ };
+
+ Repeat.prototype.indexOf = function(searchValue) {
+ if (is(this._value, searchValue)) {
+ return 0;
+ }
+ return -1;
+ };
+
+ Repeat.prototype.lastIndexOf = function(searchValue) {
+ if (is(this._value, searchValue)) {
+ return this.size;
+ }
+ return -1;
+ };
+
+ Repeat.prototype.__iterate = function(fn, reverse) {
+ for (var ii = 0; ii < this.size; ii++) {
+ if (fn(this._value, ii, this) === false) {
+ return ii + 1;
+ }
+ }
+ return ii;
+ };
+
+ Repeat.prototype.__iterator = function(type, reverse) {var this$0 = this;
+ var ii = 0;
+ return new Iterator(function()
+ {return ii < this$0.size ? iteratorValue(type, ii++, this$0._value) : iteratorDone()}
+ );
+ };
+
+ Repeat.prototype.equals = function(other) {
+ return other instanceof Repeat ?
+ is(this._value, other._value) :
+ deepEqual(other);
+ };
+
+
+ var EMPTY_REPEAT;
+
+ function invariant(condition, error) {
+ if (!condition) throw new Error(error);
+ }
+
+ createClass(Range, IndexedSeq);
+
+ function Range(start, end, step) {
+ if (!(this instanceof Range)) {
+ return new Range(start, end, step);
+ }
+ invariant(step !== 0, 'Cannot step a Range by 0');
+ start = start || 0;
+ if (end === undefined) {
+ end = Infinity;
+ }
+ step = step === undefined ? 1 : Math.abs(step);
+ if (end < start) {
+ step = -step;
+ }
+ this._start = start;
+ this._end = end;
+ this._step = step;
+ this.size = Math.max(0, Math.ceil((end - start) / step - 1) + 1);
+ if (this.size === 0) {
+ if (EMPTY_RANGE) {
+ return EMPTY_RANGE;
+ }
+ EMPTY_RANGE = this;
+ }
+ }
+
+ Range.prototype.toString = function() {
+ if (this.size === 0) {
+ return 'Range []';
+ }
+ return 'Range [ ' +
+ this._start + '...' + this._end +
+ (this._step !== 1 ? ' by ' + this._step : '') +
+ ' ]';
+ };
+
+ Range.prototype.get = function(index, notSetValue) {
+ return this.has(index) ?
+ this._start + wrapIndex(this, index) * this._step :
+ notSetValue;
+ };
+
+ Range.prototype.includes = function(searchValue) {
+ var possibleIndex = (searchValue - this._start) / this._step;
+ return possibleIndex >= 0 &&
+ possibleIndex < this.size &&
+ possibleIndex === Math.floor(possibleIndex);
+ };
+
+ Range.prototype.slice = function(begin, end) {
+ if (wholeSlice(begin, end, this.size)) {
+ return this;
+ }
+ begin = resolveBegin(begin, this.size);
+ end = resolveEnd(end, this.size);
+ if (end <= begin) {
+ return new Range(0, 0);
+ }
+ return new Range(this.get(begin, this._end), this.get(end, this._end), this._step);
+ };
+
+ Range.prototype.indexOf = function(searchValue) {
+ var offsetValue = searchValue - this._start;
+ if (offsetValue % this._step === 0) {
+ var index = offsetValue / this._step;
+ if (index >= 0 && index < this.size) {
+ return index
+ }
+ }
+ return -1;
+ };
+
+ Range.prototype.lastIndexOf = function(searchValue) {
+ return this.indexOf(searchValue);
+ };
+
+ Range.prototype.__iterate = function(fn, reverse) {
+ var maxIndex = this.size - 1;
+ var step = this._step;
+ var value = reverse ? this._start + maxIndex * step : this._start;
+ for (var ii = 0; ii <= maxIndex; ii++) {
+ if (fn(value, ii, this) === false) {
+ return ii + 1;
+ }
+ value += reverse ? -step : step;
+ }
+ return ii;
+ };
+
+ Range.prototype.__iterator = function(type, reverse) {
+ var maxIndex = this.size - 1;
+ var step = this._step;
+ var value = reverse ? this._start + maxIndex * step : this._start;
+ var ii = 0;
+ return new Iterator(function() {
+ var v = value;
+ value += reverse ? -step : step;
+ return ii > maxIndex ? iteratorDone() : iteratorValue(type, ii++, v);
+ });
+ };
+
+ Range.prototype.equals = function(other) {
+ return other instanceof Range ?
+ this._start === other._start &&
+ this._end === other._end &&
+ this._step === other._step :
+ deepEqual(this, other);
+ };
+
+
+ var EMPTY_RANGE;
+
+ createClass(Collection, Iterable);
+ function Collection() {
+ throw TypeError('Abstract');
+ }
+
+
+ createClass(KeyedCollection, Collection);function KeyedCollection() {}
+
+ createClass(IndexedCollection, Collection);function IndexedCollection() {}
+
+ createClass(SetCollection, Collection);function SetCollection() {}
+
+
+ Collection.Keyed = KeyedCollection;
+ Collection.Indexed = IndexedCollection;
+ Collection.Set = SetCollection;
+
+ var imul =
+ typeof Math.imul === 'function' && Math.imul(0xffffffff, 2) === -2 ?
+ Math.imul :
+ function imul(a, b) {
+ a = a | 0; // int
+ b = b | 0; // int
+ var c = a & 0xffff;
+ var d = b & 0xffff;
+ // Shift by 0 fixes the sign on the high part.
+ return (c * d) + ((((a >>> 16) * d + c * (b >>> 16)) << 16) >>> 0) | 0; // int
+ };
+
+ // v8 has an optimization for storing 31-bit signed numbers.
+ // Values which have either 00 or 11 as the high order bits qualify.
+ // This function drops the highest order bit in a signed number, maintaining
+ // the sign bit.
+ function smi(i32) {
+ return ((i32 >>> 1) & 0x40000000) | (i32 & 0xBFFFFFFF);
+ }
+
+ function hash(o) {
+ if (o === false || o === null || o === undefined) {
+ return 0;
+ }
+ if (typeof o.valueOf === 'function') {
+ o = o.valueOf();
+ if (o === false || o === null || o === undefined) {
+ return 0;
+ }
+ }
+ if (o === true) {
+ return 1;
+ }
+ var type = typeof o;
+ if (type === 'number') {
+ var h = o | 0;
+ if (h !== o) {
+ h ^= o * 0xFFFFFFFF;
+ }
+ while (o > 0xFFFFFFFF) {
+ o /= 0xFFFFFFFF;
+ h ^= o;
+ }
+ return smi(h);
+ }
+ if (type === 'string') {
+ return o.length > STRING_HASH_CACHE_MIN_STRLEN ? cachedHashString(o) : hashString(o);
+ }
+ if (typeof o.hashCode === 'function') {
+ return o.hashCode();
+ }
+ if (type === 'object') {
+ return hashJSObj(o);
+ }
+ if (typeof o.toString === 'function') {
+ return hashString(o.toString());
+ }
+ throw new Error('Value type ' + type + ' cannot be hashed.');
+ }
+
+ function cachedHashString(string) {
+ var hash = stringHashCache[string];
+ if (hash === undefined) {
+ hash = hashString(string);
+ if (STRING_HASH_CACHE_SIZE === STRING_HASH_CACHE_MAX_SIZE) {
+ STRING_HASH_CACHE_SIZE = 0;
+ stringHashCache = {};
+ }
+ STRING_HASH_CACHE_SIZE++;
+ stringHashCache[string] = hash;
+ }
+ return hash;
+ }
+
+ // http://jsperf.com/hashing-strings
+ function hashString(string) {
+ // This is the hash from JVM
+ // The hash code for a string is computed as
+ // s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
+ // where s[i] is the ith character of the string and n is the length of
+ // the string. We "mod" the result to make it between 0 (inclusive) and 2^31
+ // (exclusive) by dropping high bits.
+ var hash = 0;
+ for (var ii = 0; ii < string.length; ii++) {
+ hash = 31 * hash + string.charCodeAt(ii) | 0;
+ }
+ return smi(hash);
+ }
+
+ function hashJSObj(obj) {
+ var hash;
+ if (usingWeakMap) {
+ hash = weakMap.get(obj);
+ if (hash !== undefined) {
+ return hash;
+ }
+ }
+
+ hash = obj[UID_HASH_KEY];
+ if (hash !== undefined) {
+ return hash;
+ }
+
+ if (!canDefineProperty) {
+ hash = obj.propertyIsEnumerable && obj.propertyIsEnumerable[UID_HASH_KEY];
+ if (hash !== undefined) {
+ return hash;
+ }
+
+ hash = getIENodeHash(obj);
+ if (hash !== undefined) {
+ return hash;
+ }
+ }
+
+ hash = ++objHashUID;
+ if (objHashUID & 0x40000000) {
+ objHashUID = 0;
+ }
+
+ if (usingWeakMap) {
+ weakMap.set(obj, hash);
+ } else if (isExtensible !== undefined && isExtensible(obj) === false) {
+ throw new Error('Non-extensible objects are not allowed as keys.');
+ } else if (canDefineProperty) {
+ Object.defineProperty(obj, UID_HASH_KEY, {
+ 'enumerable': false,
+ 'configurable': false,
+ 'writable': false,
+ 'value': hash
+ });
+ } else if (obj.propertyIsEnumerable !== undefined &&
+ obj.propertyIsEnumerable === obj.constructor.prototype.propertyIsEnumerable) {
+ // Since we can't define a non-enumerable property on the object
+ // we'll hijack one of the less-used non-enumerable properties to
+ // save our hash on it. Since this is a function it will not show up in
+ // `JSON.stringify` which is what we want.
+ obj.propertyIsEnumerable = function() {
+ return this.constructor.prototype.propertyIsEnumerable.apply(this, arguments);
+ };
+ obj.propertyIsEnumerable[UID_HASH_KEY] = hash;
+ } else if (obj.nodeType !== undefined) {
+ // At this point we couldn't get the IE `uniqueID` to use as a hash
+ // and we couldn't use a non-enumerable property to exploit the
+ // dontEnum bug so we simply add the `UID_HASH_KEY` on the node
+ // itself.
+ obj[UID_HASH_KEY] = hash;
+ } else {
+ throw new Error('Unable to set a non-enumerable property on object.');
+ }
+
+ return hash;
+ }
+
+ // Get references to ES5 object methods.
+ var isExtensible = Object.isExtensible;
+
+ // True if Object.defineProperty works as expected. IE8 fails this test.
+ var canDefineProperty = (function() {
+ try {
+ Object.defineProperty({}, '@', {});
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }());
+
+ // IE has a `uniqueID` property on DOM nodes. We can construct the hash from it
+ // and avoid memory leaks from the IE cloneNode bug.
+ function getIENodeHash(node) {
+ if (node && node.nodeType > 0) {
+ switch (node.nodeType) {
+ case 1: // Element
+ return node.uniqueID;
+ case 9: // Document
+ return node.documentElement && node.documentElement.uniqueID;
+ }
+ }
+ }
+
+ // If possible, use a WeakMap.
+ var usingWeakMap = typeof WeakMap === 'function';
+ var weakMap;
+ if (usingWeakMap) {
+ weakMap = new WeakMap();
+ }
+
+ var objHashUID = 0;
+
+ var UID_HASH_KEY = '__immutablehash__';
+ if (typeof Symbol === 'function') {
+ UID_HASH_KEY = Symbol(UID_HASH_KEY);
+ }
+
+ var STRING_HASH_CACHE_MIN_STRLEN = 16;
+ var STRING_HASH_CACHE_MAX_SIZE = 255;
+ var STRING_HASH_CACHE_SIZE = 0;
+ var stringHashCache = {};
+
+ function assertNotInfinite(size) {
+ invariant(
+ size !== Infinity,
+ 'Cannot perform this action with an infinite size.'
+ );
+ }
+
+ createClass(Map, KeyedCollection);
+
+ // @pragma Construction
+
+ function Map(value) {
+ return value === null || value === undefined ? emptyMap() :
+ isMap(value) && !isOrdered(value) ? value :
+ emptyMap().withMutations(function(map ) {
+ var iter = KeyedIterable(value);
+ assertNotInfinite(iter.size);
+ iter.forEach(function(v, k) {return map.set(k, v)});
+ });
+ }
+
+ Map.of = function() {var keyValues = SLICE$0.call(arguments, 0);
+ return emptyMap().withMutations(function(map ) {
+ for (var i = 0; i < keyValues.length; i += 2) {
+ if (i + 1 >= keyValues.length) {
+ throw new Error('Missing value for key: ' + keyValues[i]);
+ }
+ map.set(keyValues[i], keyValues[i + 1]);
+ }
+ });
+ };
+
+ Map.prototype.toString = function() {
+ return this.__toString('Map {', '}');
+ };
+
+ // @pragma Access
+
+ Map.prototype.get = function(k, notSetValue) {
+ return this._root ?
+ this._root.get(0, undefined, k, notSetValue) :
+ notSetValue;
+ };
+
+ // @pragma Modification
+
+ Map.prototype.set = function(k, v) {
+ return updateMap(this, k, v);
+ };
+
+ Map.prototype.setIn = function(keyPath, v) {
+ return this.updateIn(keyPath, NOT_SET, function() {return v});
+ };
+
+ Map.prototype.remove = function(k) {
+ return updateMap(this, k, NOT_SET);
+ };
+
+ Map.prototype.deleteIn = function(keyPath) {
+ return this.updateIn(keyPath, function() {return NOT_SET});
+ };
+
+ Map.prototype.update = function(k, notSetValue, updater) {
+ return arguments.length === 1 ?
+ k(this) :
+ this.updateIn([k], notSetValue, updater);
+ };
+
+ Map.prototype.updateIn = function(keyPath, notSetValue, updater) {
+ if (!updater) {
+ updater = notSetValue;
+ notSetValue = undefined;
+ }
+ var updatedValue = updateInDeepMap(
+ this,
+ forceIterator(keyPath),
+ notSetValue,
+ updater
+ );
+ return updatedValue === NOT_SET ? undefined : updatedValue;
+ };
+
+ Map.prototype.clear = function() {
+ if (this.size === 0) {
+ return this;
+ }
+ if (this.__ownerID) {
+ this.size = 0;
+ this._root = null;
+ this.__hash = undefined;
+ this.__altered = true;
+ return this;
+ }
+ return emptyMap();
+ };
+
+ // @pragma Composition
+
+ Map.prototype.merge = function(/*...iters*/) {
+ return mergeIntoMapWith(this, undefined, arguments);
+ };
+
+ Map.prototype.mergeWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
+ return mergeIntoMapWith(this, merger, iters);
+ };
+
+ Map.prototype.mergeIn = function(keyPath) {var iters = SLICE$0.call(arguments, 1);
+ return this.updateIn(
+ keyPath,
+ emptyMap(),
+ function(m ) {return typeof m.merge === 'function' ?
+ m.merge.apply(m, iters) :
+ iters[iters.length - 1]}
+ );
+ };
+
+ Map.prototype.mergeDeep = function(/*...iters*/) {
+ return mergeIntoMapWith(this, deepMerger, arguments);
+ };
+
+ Map.prototype.mergeDeepWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
+ return mergeIntoMapWith(this, deepMergerWith(merger), iters);
+ };
+
+ Map.prototype.mergeDeepIn = function(keyPath) {var iters = SLICE$0.call(arguments, 1);
+ return this.updateIn(
+ keyPath,
+ emptyMap(),
+ function(m ) {return typeof m.mergeDeep === 'function' ?
+ m.mergeDeep.apply(m, iters) :
+ iters[iters.length - 1]}
+ );
+ };
+
+ Map.prototype.sort = function(comparator) {
+ // Late binding
+ return OrderedMap(sortFactory(this, comparator));
+ };
+
+ Map.prototype.sortBy = function(mapper, comparator) {
+ // Late binding
+ return OrderedMap(sortFactory(this, comparator, mapper));
+ };
+
+ // @pragma Mutability
+
+ Map.prototype.withMutations = function(fn) {
+ var mutable = this.asMutable();
+ fn(mutable);
+ return mutable.wasAltered() ? mutable.__ensureOwner(this.__ownerID) : this;
+ };
+
+ Map.prototype.asMutable = function() {
+ return this.__ownerID ? this : this.__ensureOwner(new OwnerID());
+ };
+
+ Map.prototype.asImmutable = function() {
+ return this.__ensureOwner();
+ };
+
+ Map.prototype.wasAltered = function() {
+ return this.__altered;
+ };
+
+ Map.prototype.__iterator = function(type, reverse) {
+ return new MapIterator(this, type, reverse);
+ };
+
+ Map.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ var iterations = 0;
+ this._root && this._root.iterate(function(entry ) {
+ iterations++;
+ return fn(entry[1], entry[0], this$0);
+ }, reverse);
+ return iterations;
+ };
+
+ Map.prototype.__ensureOwner = function(ownerID) {
+ if (ownerID === this.__ownerID) {
+ return this;
+ }
+ if (!ownerID) {
+ this.__ownerID = ownerID;
+ this.__altered = false;
+ return this;
+ }
+ return makeMap(this.size, this._root, ownerID, this.__hash);
+ };
+
+
+ function isMap(maybeMap) {
+ return !!(maybeMap && maybeMap[IS_MAP_SENTINEL]);
+ }
+
+ Map.isMap = isMap;
+
+ var IS_MAP_SENTINEL = '@@__IMMUTABLE_MAP__@@';
+
+ var MapPrototype = Map.prototype;
+ MapPrototype[IS_MAP_SENTINEL] = true;
+ MapPrototype[DELETE] = MapPrototype.remove;
+ MapPrototype.removeIn = MapPrototype.deleteIn;
+
+
+ // #pragma Trie Nodes
+
+
+
+ function ArrayMapNode(ownerID, entries) {
+ this.ownerID = ownerID;
+ this.entries = entries;
+ }
+
+ ArrayMapNode.prototype.get = function(shift, keyHash, key, notSetValue) {
+ var entries = this.entries;
+ for (var ii = 0, len = entries.length; ii < len; ii++) {
+ if (is(key, entries[ii][0])) {
+ return entries[ii][1];
+ }
+ }
+ return notSetValue;
+ };
+
+ ArrayMapNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
+ var removed = value === NOT_SET;
+
+ var entries = this.entries;
+ var idx = 0;
+ for (var len = entries.length; idx < len; idx++) {
+ if (is(key, entries[idx][0])) {
+ break;
+ }
+ }
+ var exists = idx < len;
+
+ if (exists ? entries[idx][1] === value : removed) {
+ return this;
+ }
+
+ SetRef(didAlter);
+ (removed || !exists) && SetRef(didChangeSize);
+
+ if (removed && entries.length === 1) {
+ return; // undefined
+ }
+
+ if (!exists && !removed && entries.length >= MAX_ARRAY_MAP_SIZE) {
+ return createNodes(ownerID, entries, key, value);
+ }
+
+ var isEditable = ownerID && ownerID === this.ownerID;
+ var newEntries = isEditable ? entries : arrCopy(entries);
+
+ if (exists) {
+ if (removed) {
+ idx === len - 1 ? newEntries.pop() : (newEntries[idx] = newEntries.pop());
+ } else {
+ newEntries[idx] = [key, value];
+ }
+ } else {
+ newEntries.push([key, value]);
+ }
+
+ if (isEditable) {
+ this.entries = newEntries;
+ return this;
+ }
+
+ return new ArrayMapNode(ownerID, newEntries);
+ };
+
+
+
+
+ function BitmapIndexedNode(ownerID, bitmap, nodes) {
+ this.ownerID = ownerID;
+ this.bitmap = bitmap;
+ this.nodes = nodes;
+ }
+
+ BitmapIndexedNode.prototype.get = function(shift, keyHash, key, notSetValue) {
+ if (keyHash === undefined) {
+ keyHash = hash(key);
+ }
+ var bit = (1 << ((shift === 0 ? keyHash : keyHash >>> shift) & MASK));
+ var bitmap = this.bitmap;
+ return (bitmap & bit) === 0 ? notSetValue :
+ this.nodes[popCount(bitmap & (bit - 1))].get(shift + SHIFT, keyHash, key, notSetValue);
+ };
+
+ BitmapIndexedNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
+ if (keyHash === undefined) {
+ keyHash = hash(key);
+ }
+ var keyHashFrag = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;
+ var bit = 1 << keyHashFrag;
+ var bitmap = this.bitmap;
+ var exists = (bitmap & bit) !== 0;
+
+ if (!exists && value === NOT_SET) {
+ return this;
+ }
+
+ var idx = popCount(bitmap & (bit - 1));
+ var nodes = this.nodes;
+ var node = exists ? nodes[idx] : undefined;
+ var newNode = updateNode(node, ownerID, shift + SHIFT, keyHash, key, value, didChangeSize, didAlter);
+
+ if (newNode === node) {
+ return this;
+ }
+
+ if (!exists && newNode && nodes.length >= MAX_BITMAP_INDEXED_SIZE) {
+ return expandNodes(ownerID, nodes, bitmap, keyHashFrag, newNode);
+ }
+
+ if (exists && !newNode && nodes.length === 2 && isLeafNode(nodes[idx ^ 1])) {
+ return nodes[idx ^ 1];
+ }
+
+ if (exists && newNode && nodes.length === 1 && isLeafNode(newNode)) {
+ return newNode;
+ }
+
+ var isEditable = ownerID && ownerID === this.ownerID;
+ var newBitmap = exists ? newNode ? bitmap : bitmap ^ bit : bitmap | bit;
+ var newNodes = exists ? newNode ?
+ setIn(nodes, idx, newNode, isEditable) :
+ spliceOut(nodes, idx, isEditable) :
+ spliceIn(nodes, idx, newNode, isEditable);
+
+ if (isEditable) {
+ this.bitmap = newBitmap;
+ this.nodes = newNodes;
+ return this;
+ }
+
+ return new BitmapIndexedNode(ownerID, newBitmap, newNodes);
+ };
+
+
+
+
+ function HashArrayMapNode(ownerID, count, nodes) {
+ this.ownerID = ownerID;
+ this.count = count;
+ this.nodes = nodes;
+ }
+
+ HashArrayMapNode.prototype.get = function(shift, keyHash, key, notSetValue) {
+ if (keyHash === undefined) {
+ keyHash = hash(key);
+ }
+ var idx = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;
+ var node = this.nodes[idx];
+ return node ? node.get(shift + SHIFT, keyHash, key, notSetValue) : notSetValue;
+ };
+
+ HashArrayMapNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
+ if (keyHash === undefined) {
+ keyHash = hash(key);
+ }
+ var idx = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;
+ var removed = value === NOT_SET;
+ var nodes = this.nodes;
+ var node = nodes[idx];
+
+ if (removed && !node) {
+ return this;
+ }
+
+ var newNode = updateNode(node, ownerID, shift + SHIFT, keyHash, key, value, didChangeSize, didAlter);
+ if (newNode === node) {
+ return this;
+ }
+
+ var newCount = this.count;
+ if (!node) {
+ newCount++;
+ } else if (!newNode) {
+ newCount--;
+ if (newCount < MIN_HASH_ARRAY_MAP_SIZE) {
+ return packNodes(ownerID, nodes, newCount, idx);
+ }
+ }
+
+ var isEditable = ownerID && ownerID === this.ownerID;
+ var newNodes = setIn(nodes, idx, newNode, isEditable);
+
+ if (isEditable) {
+ this.count = newCount;
+ this.nodes = newNodes;
+ return this;
+ }
+
+ return new HashArrayMapNode(ownerID, newCount, newNodes);
+ };
+
+
+
+
+ function HashCollisionNode(ownerID, keyHash, entries) {
+ this.ownerID = ownerID;
+ this.keyHash = keyHash;
+ this.entries = entries;
+ }
+
+ HashCollisionNode.prototype.get = function(shift, keyHash, key, notSetValue) {
+ var entries = this.entries;
+ for (var ii = 0, len = entries.length; ii < len; ii++) {
+ if (is(key, entries[ii][0])) {
+ return entries[ii][1];
+ }
+ }
+ return notSetValue;
+ };
+
+ HashCollisionNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
+ if (keyHash === undefined) {
+ keyHash = hash(key);
+ }
+
+ var removed = value === NOT_SET;
+
+ if (keyHash !== this.keyHash) {
+ if (removed) {
+ return this;
+ }
+ SetRef(didAlter);
+ SetRef(didChangeSize);
+ return mergeIntoNode(this, ownerID, shift, keyHash, [key, value]);
+ }
+
+ var entries = this.entries;
+ var idx = 0;
+ for (var len = entries.length; idx < len; idx++) {
+ if (is(key, entries[idx][0])) {
+ break;
+ }
+ }
+ var exists = idx < len;
+
+ if (exists ? entries[idx][1] === value : removed) {
+ return this;
+ }
+
+ SetRef(didAlter);
+ (removed || !exists) && SetRef(didChangeSize);
+
+ if (removed && len === 2) {
+ return new ValueNode(ownerID, this.keyHash, entries[idx ^ 1]);
+ }
+
+ var isEditable = ownerID && ownerID === this.ownerID;
+ var newEntries = isEditable ? entries : arrCopy(entries);
+
+ if (exists) {
+ if (removed) {
+ idx === len - 1 ? newEntries.pop() : (newEntries[idx] = newEntries.pop());
+ } else {
+ newEntries[idx] = [key, value];
+ }
+ } else {
+ newEntries.push([key, value]);
+ }
+
+ if (isEditable) {
+ this.entries = newEntries;
+ return this;
+ }
+
+ return new HashCollisionNode(ownerID, this.keyHash, newEntries);
+ };
+
+
+
+
+ function ValueNode(ownerID, keyHash, entry) {
+ this.ownerID = ownerID;
+ this.keyHash = keyHash;
+ this.entry = entry;
+ }
+
+ ValueNode.prototype.get = function(shift, keyHash, key, notSetValue) {
+ return is(key, this.entry[0]) ? this.entry[1] : notSetValue;
+ };
+
+ ValueNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
+ var removed = value === NOT_SET;
+ var keyMatch = is(key, this.entry[0]);
+ if (keyMatch ? value === this.entry[1] : removed) {
+ return this;
+ }
+
+ SetRef(didAlter);
+
+ if (removed) {
+ SetRef(didChangeSize);
+ return; // undefined
+ }
+
+ if (keyMatch) {
+ if (ownerID && ownerID === this.ownerID) {
+ this.entry[1] = value;
+ return this;
+ }
+ return new ValueNode(ownerID, this.keyHash, [key, value]);
+ }
+
+ SetRef(didChangeSize);
+ return mergeIntoNode(this, ownerID, shift, hash(key), [key, value]);
+ };
+
+
+
+ // #pragma Iterators
+
+ ArrayMapNode.prototype.iterate =
+ HashCollisionNode.prototype.iterate = function (fn, reverse) {
+ var entries = this.entries;
+ for (var ii = 0, maxIndex = entries.length - 1; ii <= maxIndex; ii++) {
+ if (fn(entries[reverse ? maxIndex - ii : ii]) === false) {
+ return false;
+ }
+ }
+ }
+
+ BitmapIndexedNode.prototype.iterate =
+ HashArrayMapNode.prototype.iterate = function (fn, reverse) {
+ var nodes = this.nodes;
+ for (var ii = 0, maxIndex = nodes.length - 1; ii <= maxIndex; ii++) {
+ var node = nodes[reverse ? maxIndex - ii : ii];
+ if (node && node.iterate(fn, reverse) === false) {
+ return false;
+ }
+ }
+ }
+
+ ValueNode.prototype.iterate = function (fn, reverse) {
+ return fn(this.entry);
+ }
+
+ createClass(MapIterator, Iterator);
+
+ function MapIterator(map, type, reverse) {
+ this._type = type;
+ this._reverse = reverse;
+ this._stack = map._root && mapIteratorFrame(map._root);
+ }
+
+ MapIterator.prototype.next = function() {
+ var type = this._type;
+ var stack = this._stack;
+ while (stack) {
+ var node = stack.node;
+ var index = stack.index++;
+ var maxIndex;
+ if (node.entry) {
+ if (index === 0) {
+ return mapIteratorValue(type, node.entry);
+ }
+ } else if (node.entries) {
+ maxIndex = node.entries.length - 1;
+ if (index <= maxIndex) {
+ return mapIteratorValue(type, node.entries[this._reverse ? maxIndex - index : index]);
+ }
+ } else {
+ maxIndex = node.nodes.length - 1;
+ if (index <= maxIndex) {
+ var subNode = node.nodes[this._reverse ? maxIndex - index : index];
+ if (subNode) {
+ if (subNode.entry) {
+ return mapIteratorValue(type, subNode.entry);
+ }
+ stack = this._stack = mapIteratorFrame(subNode, stack);
+ }
+ continue;
+ }
+ }
+ stack = this._stack = this._stack.__prev;
+ }
+ return iteratorDone();
+ };
+
+
+ function mapIteratorValue(type, entry) {
+ return iteratorValue(type, entry[0], entry[1]);
+ }
+
+ function mapIteratorFrame(node, prev) {
+ return {
+ node: node,
+ index: 0,
+ __prev: prev
+ };
+ }
+
+ function makeMap(size, root, ownerID, hash) {
+ var map = Object.create(MapPrototype);
+ map.size = size;
+ map._root = root;
+ map.__ownerID = ownerID;
+ map.__hash = hash;
+ map.__altered = false;
+ return map;
+ }
+
+ var EMPTY_MAP;
+ function emptyMap() {
+ return EMPTY_MAP || (EMPTY_MAP = makeMap(0));
+ }
+
+ function updateMap(map, k, v) {
+ var newRoot;
+ var newSize;
+ if (!map._root) {
+ if (v === NOT_SET) {
+ return map;
+ }
+ newSize = 1;
+ newRoot = new ArrayMapNode(map.__ownerID, [[k, v]]);
+ } else {
+ var didChangeSize = MakeRef(CHANGE_LENGTH);
+ var didAlter = MakeRef(DID_ALTER);
+ newRoot = updateNode(map._root, map.__ownerID, 0, undefined, k, v, didChangeSize, didAlter);
+ if (!didAlter.value) {
+ return map;
+ }
+ newSize = map.size + (didChangeSize.value ? v === NOT_SET ? -1 : 1 : 0);
+ }
+ if (map.__ownerID) {
+ map.size = newSize;
+ map._root = newRoot;
+ map.__hash = undefined;
+ map.__altered = true;
+ return map;
+ }
+ return newRoot ? makeMap(newSize, newRoot) : emptyMap();
+ }
+
+ function updateNode(node, ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
+ if (!node) {
+ if (value === NOT_SET) {
+ return node;
+ }
+ SetRef(didAlter);
+ SetRef(didChangeSize);
+ return new ValueNode(ownerID, keyHash, [key, value]);
+ }
+ return node.update(ownerID, shift, keyHash, key, value, didChangeSize, didAlter);
+ }
+
+ function isLeafNode(node) {
+ return node.constructor === ValueNode || node.constructor === HashCollisionNode;
+ }
+
+ function mergeIntoNode(node, ownerID, shift, keyHash, entry) {
+ if (node.keyHash === keyHash) {
+ return new HashCollisionNode(ownerID, keyHash, [node.entry, entry]);
+ }
+
+ var idx1 = (shift === 0 ? node.keyHash : node.keyHash >>> shift) & MASK;
+ var idx2 = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;
+
+ var newNode;
+ var nodes = idx1 === idx2 ?
+ [mergeIntoNode(node, ownerID, shift + SHIFT, keyHash, entry)] :
+ ((newNode = new ValueNode(ownerID, keyHash, entry)), idx1 < idx2 ? [node, newNode] : [newNode, node]);
+
+ return new BitmapIndexedNode(ownerID, (1 << idx1) | (1 << idx2), nodes);
+ }
+
+ function createNodes(ownerID, entries, key, value) {
+ if (!ownerID) {
+ ownerID = new OwnerID();
+ }
+ var node = new ValueNode(ownerID, hash(key), [key, value]);
+ for (var ii = 0; ii < entries.length; ii++) {
+ var entry = entries[ii];
+ node = node.update(ownerID, 0, undefined, entry[0], entry[1]);
+ }
+ return node;
+ }
+
+ function packNodes(ownerID, nodes, count, excluding) {
+ var bitmap = 0;
+ var packedII = 0;
+ var packedNodes = new Array(count);
+ for (var ii = 0, bit = 1, len = nodes.length; ii < len; ii++, bit <<= 1) {
+ var node = nodes[ii];
+ if (node !== undefined && ii !== excluding) {
+ bitmap |= bit;
+ packedNodes[packedII++] = node;
+ }
+ }
+ return new BitmapIndexedNode(ownerID, bitmap, packedNodes);
+ }
+
+ function expandNodes(ownerID, nodes, bitmap, including, node) {
+ var count = 0;
+ var expandedNodes = new Array(SIZE);
+ for (var ii = 0; bitmap !== 0; ii++, bitmap >>>= 1) {
+ expandedNodes[ii] = bitmap & 1 ? nodes[count++] : undefined;
+ }
+ expandedNodes[including] = node;
+ return new HashArrayMapNode(ownerID, count + 1, expandedNodes);
+ }
+
+ function mergeIntoMapWith(map, merger, iterables) {
+ var iters = [];
+ for (var ii = 0; ii < iterables.length; ii++) {
+ var value = iterables[ii];
+ var iter = KeyedIterable(value);
+ if (!isIterable(value)) {
+ iter = iter.map(function(v ) {return fromJS(v)});
+ }
+ iters.push(iter);
+ }
+ return mergeIntoCollectionWith(map, merger, iters);
+ }
+
+ function deepMerger(existing, value, key) {
+ return existing && existing.mergeDeep && isIterable(value) ?
+ existing.mergeDeep(value) :
+ is(existing, value) ? existing : value;
+ }
+
+ function deepMergerWith(merger) {
+ return function(existing, value, key) {
+ if (existing && existing.mergeDeepWith && isIterable(value)) {
+ return existing.mergeDeepWith(merger, value);
+ }
+ var nextValue = merger(existing, value, key);
+ return is(existing, nextValue) ? existing : nextValue;
+ };
+ }
+
+ function mergeIntoCollectionWith(collection, merger, iters) {
+ iters = iters.filter(function(x ) {return x.size !== 0});
+ if (iters.length === 0) {
+ return collection;
+ }
+ if (collection.size === 0 && !collection.__ownerID && iters.length === 1) {
+ return collection.constructor(iters[0]);
+ }
+ return collection.withMutations(function(collection ) {
+ var mergeIntoMap = merger ?
+ function(value, key) {
+ collection.update(key, NOT_SET, function(existing )
+ {return existing === NOT_SET ? value : merger(existing, value, key)}
+ );
+ } :
+ function(value, key) {
+ collection.set(key, value);
+ }
+ for (var ii = 0; ii < iters.length; ii++) {
+ iters[ii].forEach(mergeIntoMap);
+ }
+ });
+ }
+
+ function updateInDeepMap(existing, keyPathIter, notSetValue, updater) {
+ var isNotSet = existing === NOT_SET;
+ var step = keyPathIter.next();
+ if (step.done) {
+ var existingValue = isNotSet ? notSetValue : existing;
+ var newValue = updater(existingValue);
+ return newValue === existingValue ? existing : newValue;
+ }
+ invariant(
+ isNotSet || (existing && existing.set),
+ 'invalid keyPath'
+ );
+ var key = step.value;
+ var nextExisting = isNotSet ? NOT_SET : existing.get(key, NOT_SET);
+ var nextUpdated = updateInDeepMap(
+ nextExisting,
+ keyPathIter,
+ notSetValue,
+ updater
+ );
+ return nextUpdated === nextExisting ? existing :
+ nextUpdated === NOT_SET ? existing.remove(key) :
+ (isNotSet ? emptyMap() : existing).set(key, nextUpdated);
+ }
+
+ function popCount(x) {
+ x = x - ((x >> 1) & 0x55555555);
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+ x = (x + (x >> 4)) & 0x0f0f0f0f;
+ x = x + (x >> 8);
+ x = x + (x >> 16);
+ return x & 0x7f;
+ }
+
+ function setIn(array, idx, val, canEdit) {
+ var newArray = canEdit ? array : arrCopy(array);
+ newArray[idx] = val;
+ return newArray;
+ }
+
+ function spliceIn(array, idx, val, canEdit) {
+ var newLen = array.length + 1;
+ if (canEdit && idx + 1 === newLen) {
+ array[idx] = val;
+ return array;
+ }
+ var newArray = new Array(newLen);
+ var after = 0;
+ for (var ii = 0; ii < newLen; ii++) {
+ if (ii === idx) {
+ newArray[ii] = val;
+ after = -1;
+ } else {
+ newArray[ii] = array[ii + after];
+ }
+ }
+ return newArray;
+ }
+
+ function spliceOut(array, idx, canEdit) {
+ var newLen = array.length - 1;
+ if (canEdit && idx === newLen) {
+ array.pop();
+ return array;
+ }
+ var newArray = new Array(newLen);
+ var after = 0;
+ for (var ii = 0; ii < newLen; ii++) {
+ if (ii === idx) {
+ after = 1;
+ }
+ newArray[ii] = array[ii + after];
+ }
+ return newArray;
+ }
+
+ var MAX_ARRAY_MAP_SIZE = SIZE / 4;
+ var MAX_BITMAP_INDEXED_SIZE = SIZE / 2;
+ var MIN_HASH_ARRAY_MAP_SIZE = SIZE / 4;
+
+ createClass(List, IndexedCollection);
+
+ // @pragma Construction
+
+ function List(value) {
+ var empty = emptyList();
+ if (value === null || value === undefined) {
+ return empty;
+ }
+ if (isList(value)) {
+ return value;
+ }
+ var iter = IndexedIterable(value);
+ var size = iter.size;
+ if (size === 0) {
+ return empty;
+ }
+ assertNotInfinite(size);
+ if (size > 0 && size < SIZE) {
+ return makeList(0, size, SHIFT, null, new VNode(iter.toArray()));
+ }
+ return empty.withMutations(function(list ) {
+ list.setSize(size);
+ iter.forEach(function(v, i) {return list.set(i, v)});
+ });
+ }
+
+ List.of = function(/*...values*/) {
+ return this(arguments);
+ };
+
+ List.prototype.toString = function() {
+ return this.__toString('List [', ']');
+ };
+
+ // @pragma Access
+
+ List.prototype.get = function(index, notSetValue) {
+ index = wrapIndex(this, index);
+ if (index >= 0 && index < this.size) {
+ index += this._origin;
+ var node = listNodeFor(this, index);
+ return node && node.array[index & MASK];
+ }
+ return notSetValue;
+ };
+
+ // @pragma Modification
+
+ List.prototype.set = function(index, value) {
+ return updateList(this, index, value);
+ };
+
+ List.prototype.remove = function(index) {
+ return !this.has(index) ? this :
+ index === 0 ? this.shift() :
+ index === this.size - 1 ? this.pop() :
+ this.splice(index, 1);
+ };
+
+ List.prototype.insert = function(index, value) {
+ return this.splice(index, 0, value);
+ };
+
+ List.prototype.clear = function() {
+ if (this.size === 0) {
+ return this;
+ }
+ if (this.__ownerID) {
+ this.size = this._origin = this._capacity = 0;
+ this._level = SHIFT;
+ this._root = this._tail = null;
+ this.__hash = undefined;
+ this.__altered = true;
+ return this;
+ }
+ return emptyList();
+ };
+
+ List.prototype.push = function(/*...values*/) {
+ var values = arguments;
+ var oldSize = this.size;
+ return this.withMutations(function(list ) {
+ setListBounds(list, 0, oldSize + values.length);
+ for (var ii = 0; ii < values.length; ii++) {
+ list.set(oldSize + ii, values[ii]);
+ }
+ });
+ };
+
+ List.prototype.pop = function() {
+ return setListBounds(this, 0, -1);
+ };
+
+ List.prototype.unshift = function(/*...values*/) {
+ var values = arguments;
+ return this.withMutations(function(list ) {
+ setListBounds(list, -values.length);
+ for (var ii = 0; ii < values.length; ii++) {
+ list.set(ii, values[ii]);
+ }
+ });
+ };
+
+ List.prototype.shift = function() {
+ return setListBounds(this, 1);
+ };
+
+ // @pragma Composition
+
+ List.prototype.merge = function(/*...iters*/) {
+ return mergeIntoListWith(this, undefined, arguments);
+ };
+
+ List.prototype.mergeWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
+ return mergeIntoListWith(this, merger, iters);
+ };
+
+ List.prototype.mergeDeep = function(/*...iters*/) {
+ return mergeIntoListWith(this, deepMerger, arguments);
+ };
+
+ List.prototype.mergeDeepWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
+ return mergeIntoListWith(this, deepMergerWith(merger), iters);
+ };
+
+ List.prototype.setSize = function(size) {
+ return setListBounds(this, 0, size);
+ };
+
+ // @pragma Iteration
+
+ List.prototype.slice = function(begin, end) {
+ var size = this.size;
+ if (wholeSlice(begin, end, size)) {
+ return this;
+ }
+ return setListBounds(
+ this,
+ resolveBegin(begin, size),
+ resolveEnd(end, size)
+ );
+ };
+
+ List.prototype.__iterator = function(type, reverse) {
+ var index = 0;
+ var values = iterateList(this, reverse);
+ return new Iterator(function() {
+ var value = values();
+ return value === DONE ?
+ iteratorDone() :
+ iteratorValue(type, index++, value);
+ });
+ };
+
+ List.prototype.__iterate = function(fn, reverse) {
+ var index = 0;
+ var values = iterateList(this, reverse);
+ var value;
+ while ((value = values()) !== DONE) {
+ if (fn(value, index++, this) === false) {
+ break;
+ }
+ }
+ return index;
+ };
+
+ List.prototype.__ensureOwner = function(ownerID) {
+ if (ownerID === this.__ownerID) {
+ return this;
+ }
+ if (!ownerID) {
+ this.__ownerID = ownerID;
+ return this;
+ }
+ return makeList(this._origin, this._capacity, this._level, this._root, this._tail, ownerID, this.__hash);
+ };
+
+
+ function isList(maybeList) {
+ return !!(maybeList && maybeList[IS_LIST_SENTINEL]);
+ }
+
+ List.isList = isList;
+
+ var IS_LIST_SENTINEL = '@@__IMMUTABLE_LIST__@@';
+
+ var ListPrototype = List.prototype;
+ ListPrototype[IS_LIST_SENTINEL] = true;
+ ListPrototype[DELETE] = ListPrototype.remove;
+ ListPrototype.setIn = MapPrototype.setIn;
+ ListPrototype.deleteIn =
+ ListPrototype.removeIn = MapPrototype.removeIn;
+ ListPrototype.update = MapPrototype.update;
+ ListPrototype.updateIn = MapPrototype.updateIn;
+ ListPrototype.mergeIn = MapPrototype.mergeIn;
+ ListPrototype.mergeDeepIn = MapPrototype.mergeDeepIn;
+ ListPrototype.withMutations = MapPrototype.withMutations;
+ ListPrototype.asMutable = MapPrototype.asMutable;
+ ListPrototype.asImmutable = MapPrototype.asImmutable;
+ ListPrototype.wasAltered = MapPrototype.wasAltered;
+
+
+
+ function VNode(array, ownerID) {
+ this.array = array;
+ this.ownerID = ownerID;
+ }
+
+ // TODO: seems like these methods are very similar
+
+ VNode.prototype.removeBefore = function(ownerID, level, index) {
+ if (index === level ? 1 << level : 0 || this.array.length === 0) {
+ return this;
+ }
+ var originIndex = (index >>> level) & MASK;
+ if (originIndex >= this.array.length) {
+ return new VNode([], ownerID);
+ }
+ var removingFirst = originIndex === 0;
+ var newChild;
+ if (level > 0) {
+ var oldChild = this.array[originIndex];
+ newChild = oldChild && oldChild.removeBefore(ownerID, level - SHIFT, index);
+ if (newChild === oldChild && removingFirst) {
+ return this;
+ }
+ }
+ if (removingFirst && !newChild) {
+ return this;
+ }
+ var editable = editableVNode(this, ownerID);
+ if (!removingFirst) {
+ for (var ii = 0; ii < originIndex; ii++) {
+ editable.array[ii] = undefined;
+ }
+ }
+ if (newChild) {
+ editable.array[originIndex] = newChild;
+ }
+ return editable;
+ };
+
+ VNode.prototype.removeAfter = function(ownerID, level, index) {
+ if (index === (level ? 1 << level : 0) || this.array.length === 0) {
+ return this;
+ }
+ var sizeIndex = ((index - 1) >>> level) & MASK;
+ if (sizeIndex >= this.array.length) {
+ return this;
+ }
+
+ var newChild;
+ if (level > 0) {
+ var oldChild = this.array[sizeIndex];
+ newChild = oldChild && oldChild.removeAfter(ownerID, level - SHIFT, index);
+ if (newChild === oldChild && sizeIndex === this.array.length - 1) {
+ return this;
+ }
+ }
+
+ var editable = editableVNode(this, ownerID);
+ editable.array.splice(sizeIndex + 1);
+ if (newChild) {
+ editable.array[sizeIndex] = newChild;
+ }
+ return editable;
+ };
+
+
+
+ var DONE = {};
+
+ function iterateList(list, reverse) {
+ var left = list._origin;
+ var right = list._capacity;
+ var tailPos = getTailOffset(right);
+ var tail = list._tail;
+
+ return iterateNodeOrLeaf(list._root, list._level, 0);
+
+ function iterateNodeOrLeaf(node, level, offset) {
+ return level === 0 ?
+ iterateLeaf(node, offset) :
+ iterateNode(node, level, offset);
+ }
+
+ function iterateLeaf(node, offset) {
+ var array = offset === tailPos ? tail && tail.array : node && node.array;
+ var from = offset > left ? 0 : left - offset;
+ var to = right - offset;
+ if (to > SIZE) {
+ to = SIZE;
+ }
+ return function() {
+ if (from === to) {
+ return DONE;
+ }
+ var idx = reverse ? --to : from++;
+ return array && array[idx];
+ };
+ }
+
+ function iterateNode(node, level, offset) {
+ var values;
+ var array = node && node.array;
+ var from = offset > left ? 0 : (left - offset) >> level;
+ var to = ((right - offset) >> level) + 1;
+ if (to > SIZE) {
+ to = SIZE;
+ }
+ return function() {
+ do {
+ if (values) {
+ var value = values();
+ if (value !== DONE) {
+ return value;
+ }
+ values = null;
+ }
+ if (from === to) {
+ return DONE;
+ }
+ var idx = reverse ? --to : from++;
+ values = iterateNodeOrLeaf(
+ array && array[idx], level - SHIFT, offset + (idx << level)
+ );
+ } while (true);
+ };
+ }
+ }
+
+ function makeList(origin, capacity, level, root, tail, ownerID, hash) {
+ var list = Object.create(ListPrototype);
+ list.size = capacity - origin;
+ list._origin = origin;
+ list._capacity = capacity;
+ list._level = level;
+ list._root = root;
+ list._tail = tail;
+ list.__ownerID = ownerID;
+ list.__hash = hash;
+ list.__altered = false;
+ return list;
+ }
+
+ var EMPTY_LIST;
+ function emptyList() {
+ return EMPTY_LIST || (EMPTY_LIST = makeList(0, 0, SHIFT));
+ }
+
+ function updateList(list, index, value) {
+ index = wrapIndex(list, index);
+
+ if (index !== index) {
+ return list;
+ }
+
+ if (index >= list.size || index < 0) {
+ return list.withMutations(function(list ) {
+ index < 0 ?
+ setListBounds(list, index).set(0, value) :
+ setListBounds(list, 0, index + 1).set(index, value)
+ });
+ }
+
+ index += list._origin;
+
+ var newTail = list._tail;
+ var newRoot = list._root;
+ var didAlter = MakeRef(DID_ALTER);
+ if (index >= getTailOffset(list._capacity)) {
+ newTail = updateVNode(newTail, list.__ownerID, 0, index, value, didAlter);
+ } else {
+ newRoot = updateVNode(newRoot, list.__ownerID, list._level, index, value, didAlter);
+ }
+
+ if (!didAlter.value) {
+ return list;
+ }
+
+ if (list.__ownerID) {
+ list._root = newRoot;
+ list._tail = newTail;
+ list.__hash = undefined;
+ list.__altered = true;
+ return list;
+ }
+ return makeList(list._origin, list._capacity, list._level, newRoot, newTail);
+ }
+
+ function updateVNode(node, ownerID, level, index, value, didAlter) {
+ var idx = (index >>> level) & MASK;
+ var nodeHas = node && idx < node.array.length;
+ if (!nodeHas && value === undefined) {
+ return node;
+ }
+
+ var newNode;
+
+ if (level > 0) {
+ var lowerNode = node && node.array[idx];
+ var newLowerNode = updateVNode(lowerNode, ownerID, level - SHIFT, index, value, didAlter);
+ if (newLowerNode === lowerNode) {
+ return node;
+ }
+ newNode = editableVNode(node, ownerID);
+ newNode.array[idx] = newLowerNode;
+ return newNode;
+ }
+
+ if (nodeHas && node.array[idx] === value) {
+ return node;
+ }
+
+ SetRef(didAlter);
+
+ newNode = editableVNode(node, ownerID);
+ if (value === undefined && idx === newNode.array.length - 1) {
+ newNode.array.pop();
+ } else {
+ newNode.array[idx] = value;
+ }
+ return newNode;
+ }
+
+ function editableVNode(node, ownerID) {
+ if (ownerID && node && ownerID === node.ownerID) {
+ return node;
+ }
+ return new VNode(node ? node.array.slice() : [], ownerID);
+ }
+
+ function listNodeFor(list, rawIndex) {
+ if (rawIndex >= getTailOffset(list._capacity)) {
+ return list._tail;
+ }
+ if (rawIndex < 1 << (list._level + SHIFT)) {
+ var node = list._root;
+ var level = list._level;
+ while (node && level > 0) {
+ node = node.array[(rawIndex >>> level) & MASK];
+ level -= SHIFT;
+ }
+ return node;
+ }
+ }
+
+ function setListBounds(list, begin, end) {
+ // Sanitize begin & end using this shorthand for ToInt32(argument)
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-toint32
+ if (begin !== undefined) {
+ begin = begin | 0;
+ }
+ if (end !== undefined) {
+ end = end | 0;
+ }
+ var owner = list.__ownerID || new OwnerID();
+ var oldOrigin = list._origin;
+ var oldCapacity = list._capacity;
+ var newOrigin = oldOrigin + begin;
+ var newCapacity = end === undefined ? oldCapacity : end < 0 ? oldCapacity + end : oldOrigin + end;
+ if (newOrigin === oldOrigin && newCapacity === oldCapacity) {
+ return list;
+ }
+
+ // If it's going to end after it starts, it's empty.
+ if (newOrigin >= newCapacity) {
+ return list.clear();
+ }
+
+ var newLevel = list._level;
+ var newRoot = list._root;
+
+ // New origin might need creating a higher root.
+ var offsetShift = 0;
+ while (newOrigin + offsetShift < 0) {
+ newRoot = new VNode(newRoot && newRoot.array.length ? [undefined, newRoot] : [], owner);
+ newLevel += SHIFT;
+ offsetShift += 1 << newLevel;
+ }
+ if (offsetShift) {
+ newOrigin += offsetShift;
+ oldOrigin += offsetShift;
+ newCapacity += offsetShift;
+ oldCapacity += offsetShift;
+ }
+
+ var oldTailOffset = getTailOffset(oldCapacity);
+ var newTailOffset = getTailOffset(newCapacity);
+
+ // New size might need creating a higher root.
+ while (newTailOffset >= 1 << (newLevel + SHIFT)) {
+ newRoot = new VNode(newRoot && newRoot.array.length ? [newRoot] : [], owner);
+ newLevel += SHIFT;
+ }
+
+ // Locate or create the new tail.
+ var oldTail = list._tail;
+ var newTail = newTailOffset < oldTailOffset ?
+ listNodeFor(list, newCapacity - 1) :
+ newTailOffset > oldTailOffset ? new VNode([], owner) : oldTail;
+
+ // Merge Tail into tree.
+ if (oldTail && newTailOffset > oldTailOffset && newOrigin < oldCapacity && oldTail.array.length) {
+ newRoot = editableVNode(newRoot, owner);
+ var node = newRoot;
+ for (var level = newLevel; level > SHIFT; level -= SHIFT) {
+ var idx = (oldTailOffset >>> level) & MASK;
+ node = node.array[idx] = editableVNode(node.array[idx], owner);
+ }
+ node.array[(oldTailOffset >>> SHIFT) & MASK] = oldTail;
+ }
+
+ // If the size has been reduced, there's a chance the tail needs to be trimmed.
+ if (newCapacity < oldCapacity) {
+ newTail = newTail && newTail.removeAfter(owner, 0, newCapacity);
+ }
+
+ // If the new origin is within the tail, then we do not need a root.
+ if (newOrigin >= newTailOffset) {
+ newOrigin -= newTailOffset;
+ newCapacity -= newTailOffset;
+ newLevel = SHIFT;
+ newRoot = null;
+ newTail = newTail && newTail.removeBefore(owner, 0, newOrigin);
+
+ // Otherwise, if the root has been trimmed, garbage collect.
+ } else if (newOrigin > oldOrigin || newTailOffset < oldTailOffset) {
+ offsetShift = 0;
+
+ // Identify the new top root node of the subtree of the old root.
+ while (newRoot) {
+ var beginIndex = (newOrigin >>> newLevel) & MASK;
+ if (beginIndex !== (newTailOffset >>> newLevel) & MASK) {
+ break;
+ }
+ if (beginIndex) {
+ offsetShift += (1 << newLevel) * beginIndex;
+ }
+ newLevel -= SHIFT;
+ newRoot = newRoot.array[beginIndex];
+ }
+
+ // Trim the new sides of the new root.
+ if (newRoot && newOrigin > oldOrigin) {
+ newRoot = newRoot.removeBefore(owner, newLevel, newOrigin - offsetShift);
+ }
+ if (newRoot && newTailOffset < oldTailOffset) {
+ newRoot = newRoot.removeAfter(owner, newLevel, newTailOffset - offsetShift);
+ }
+ if (offsetShift) {
+ newOrigin -= offsetShift;
+ newCapacity -= offsetShift;
+ }
+ }
+
+ if (list.__ownerID) {
+ list.size = newCapacity - newOrigin;
+ list._origin = newOrigin;
+ list._capacity = newCapacity;
+ list._level = newLevel;
+ list._root = newRoot;
+ list._tail = newTail;
+ list.__hash = undefined;
+ list.__altered = true;
+ return list;
+ }
+ return makeList(newOrigin, newCapacity, newLevel, newRoot, newTail);
+ }
+
+ function mergeIntoListWith(list, merger, iterables) {
+ var iters = [];
+ var maxSize = 0;
+ for (var ii = 0; ii < iterables.length; ii++) {
+ var value = iterables[ii];
+ var iter = IndexedIterable(value);
+ if (iter.size > maxSize) {
+ maxSize = iter.size;
+ }
+ if (!isIterable(value)) {
+ iter = iter.map(function(v ) {return fromJS(v)});
+ }
+ iters.push(iter);
+ }
+ if (maxSize > list.size) {
+ list = list.setSize(maxSize);
+ }
+ return mergeIntoCollectionWith(list, merger, iters);
+ }
+
+ function getTailOffset(size) {
+ return size < SIZE ? 0 : (((size - 1) >>> SHIFT) << SHIFT);
+ }
+
+ createClass(OrderedMap, Map);
+
+ // @pragma Construction
+
+ function OrderedMap(value) {
+ return value === null || value === undefined ? emptyOrderedMap() :
+ isOrderedMap(value) ? value :
+ emptyOrderedMap().withMutations(function(map ) {
+ var iter = KeyedIterable(value);
+ assertNotInfinite(iter.size);
+ iter.forEach(function(v, k) {return map.set(k, v)});
+ });
+ }
+
+ OrderedMap.of = function(/*...values*/) {
+ return this(arguments);
+ };
+
+ OrderedMap.prototype.toString = function() {
+ return this.__toString('OrderedMap {', '}');
+ };
+
+ // @pragma Access
+
+ OrderedMap.prototype.get = function(k, notSetValue) {
+ var index = this._map.get(k);
+ return index !== undefined ? this._list.get(index)[1] : notSetValue;
+ };
+
+ // @pragma Modification
+
+ OrderedMap.prototype.clear = function() {
+ if (this.size === 0) {
+ return this;
+ }
+ if (this.__ownerID) {
+ this.size = 0;
+ this._map.clear();
+ this._list.clear();
+ return this;
+ }
+ return emptyOrderedMap();
+ };
+
+ OrderedMap.prototype.set = function(k, v) {
+ return updateOrderedMap(this, k, v);
+ };
+
+ OrderedMap.prototype.remove = function(k) {
+ return updateOrderedMap(this, k, NOT_SET);
+ };
+
+ OrderedMap.prototype.wasAltered = function() {
+ return this._map.wasAltered() || this._list.wasAltered();
+ };
+
+ OrderedMap.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ return this._list.__iterate(
+ function(entry ) {return entry && fn(entry[1], entry[0], this$0)},
+ reverse
+ );
+ };
+
+ OrderedMap.prototype.__iterator = function(type, reverse) {
+ return this._list.fromEntrySeq().__iterator(type, reverse);
+ };
+
+ OrderedMap.prototype.__ensureOwner = function(ownerID) {
+ if (ownerID === this.__ownerID) {
+ return this;
+ }
+ var newMap = this._map.__ensureOwner(ownerID);
+ var newList = this._list.__ensureOwner(ownerID);
+ if (!ownerID) {
+ this.__ownerID = ownerID;
+ this._map = newMap;
+ this._list = newList;
+ return this;
+ }
+ return makeOrderedMap(newMap, newList, ownerID, this.__hash);
+ };
+
+
+ function isOrderedMap(maybeOrderedMap) {
+ return isMap(maybeOrderedMap) && isOrdered(maybeOrderedMap);
+ }
+
+ OrderedMap.isOrderedMap = isOrderedMap;
+
+ OrderedMap.prototype[IS_ORDERED_SENTINEL] = true;
+ OrderedMap.prototype[DELETE] = OrderedMap.prototype.remove;
+
+
+
+ function makeOrderedMap(map, list, ownerID, hash) {
+ var omap = Object.create(OrderedMap.prototype);
+ omap.size = map ? map.size : 0;
+ omap._map = map;
+ omap._list = list;
+ omap.__ownerID = ownerID;
+ omap.__hash = hash;
+ return omap;
+ }
+
+ var EMPTY_ORDERED_MAP;
+ function emptyOrderedMap() {
+ return EMPTY_ORDERED_MAP || (EMPTY_ORDERED_MAP = makeOrderedMap(emptyMap(), emptyList()));
+ }
+
+ function updateOrderedMap(omap, k, v) {
+ var map = omap._map;
+ var list = omap._list;
+ var i = map.get(k);
+ var has = i !== undefined;
+ var newMap;
+ var newList;
+ if (v === NOT_SET) { // removed
+ if (!has) {
+ return omap;
+ }
+ if (list.size >= SIZE && list.size >= map.size * 2) {
+ newList = list.filter(function(entry, idx) {return entry !== undefined && i !== idx});
+ newMap = newList.toKeyedSeq().map(function(entry ) {return entry[0]}).flip().toMap();
+ if (omap.__ownerID) {
+ newMap.__ownerID = newList.__ownerID = omap.__ownerID;
+ }
+ } else {
+ newMap = map.remove(k);
+ newList = i === list.size - 1 ? list.pop() : list.set(i, undefined);
+ }
+ } else {
+ if (has) {
+ if (v === list.get(i)[1]) {
+ return omap;
+ }
+ newMap = map;
+ newList = list.set(i, [k, v]);
+ } else {
+ newMap = map.set(k, list.size);
+ newList = list.set(list.size, [k, v]);
+ }
+ }
+ if (omap.__ownerID) {
+ omap.size = newMap.size;
+ omap._map = newMap;
+ omap._list = newList;
+ omap.__hash = undefined;
+ return omap;
+ }
+ return makeOrderedMap(newMap, newList);
+ }
+
+ createClass(ToKeyedSequence, KeyedSeq);
+ function ToKeyedSequence(indexed, useKeys) {
+ this._iter = indexed;
+ this._useKeys = useKeys;
+ this.size = indexed.size;
+ }
+
+ ToKeyedSequence.prototype.get = function(key, notSetValue) {
+ return this._iter.get(key, notSetValue);
+ };
+
+ ToKeyedSequence.prototype.has = function(key) {
+ return this._iter.has(key);
+ };
+
+ ToKeyedSequence.prototype.valueSeq = function() {
+ return this._iter.valueSeq();
+ };
+
+ ToKeyedSequence.prototype.reverse = function() {var this$0 = this;
+ var reversedSequence = reverseFactory(this, true);
+ if (!this._useKeys) {
+ reversedSequence.valueSeq = function() {return this$0._iter.toSeq().reverse()};
+ }
+ return reversedSequence;
+ };
+
+ ToKeyedSequence.prototype.map = function(mapper, context) {var this$0 = this;
+ var mappedSequence = mapFactory(this, mapper, context);
+ if (!this._useKeys) {
+ mappedSequence.valueSeq = function() {return this$0._iter.toSeq().map(mapper, context)};
+ }
+ return mappedSequence;
+ };
+
+ ToKeyedSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ var ii;
+ return this._iter.__iterate(
+ this._useKeys ?
+ function(v, k) {return fn(v, k, this$0)} :
+ ((ii = reverse ? resolveSize(this) : 0),
+ function(v ) {return fn(v, reverse ? --ii : ii++, this$0)}),
+ reverse
+ );
+ };
+
+ ToKeyedSequence.prototype.__iterator = function(type, reverse) {
+ if (this._useKeys) {
+ return this._iter.__iterator(type, reverse);
+ }
+ var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
+ var ii = reverse ? resolveSize(this) : 0;
+ return new Iterator(function() {
+ var step = iterator.next();
+ return step.done ? step :
+ iteratorValue(type, reverse ? --ii : ii++, step.value, step);
+ });
+ };
+
+ ToKeyedSequence.prototype[IS_ORDERED_SENTINEL] = true;
+
+
+ createClass(ToIndexedSequence, IndexedSeq);
+ function ToIndexedSequence(iter) {
+ this._iter = iter;
+ this.size = iter.size;
+ }
+
+ ToIndexedSequence.prototype.includes = function(value) {
+ return this._iter.includes(value);
+ };
+
+ ToIndexedSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ var iterations = 0;
+ return this._iter.__iterate(function(v ) {return fn(v, iterations++, this$0)}, reverse);
+ };
+
+ ToIndexedSequence.prototype.__iterator = function(type, reverse) {
+ var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
+ var iterations = 0;
+ return new Iterator(function() {
+ var step = iterator.next();
+ return step.done ? step :
+ iteratorValue(type, iterations++, step.value, step)
+ });
+ };
+
+
+
+ createClass(ToSetSequence, SetSeq);
+ function ToSetSequence(iter) {
+ this._iter = iter;
+ this.size = iter.size;
+ }
+
+ ToSetSequence.prototype.has = function(key) {
+ return this._iter.includes(key);
+ };
+
+ ToSetSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ return this._iter.__iterate(function(v ) {return fn(v, v, this$0)}, reverse);
+ };
+
+ ToSetSequence.prototype.__iterator = function(type, reverse) {
+ var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
+ return new Iterator(function() {
+ var step = iterator.next();
+ return step.done ? step :
+ iteratorValue(type, step.value, step.value, step);
+ });
+ };
+
+
+
+ createClass(FromEntriesSequence, KeyedSeq);
+ function FromEntriesSequence(entries) {
+ this._iter = entries;
+ this.size = entries.size;
+ }
+
+ FromEntriesSequence.prototype.entrySeq = function() {
+ return this._iter.toSeq();
+ };
+
+ FromEntriesSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ return this._iter.__iterate(function(entry ) {
+ // Check if entry exists first so array access doesn't throw for holes
+ // in the parent iteration.
+ if (entry) {
+ validateEntry(entry);
+ var indexedIterable = isIterable(entry);
+ return fn(
+ indexedIterable ? entry.get(1) : entry[1],
+ indexedIterable ? entry.get(0) : entry[0],
+ this$0
+ );
+ }
+ }, reverse);
+ };
+
+ FromEntriesSequence.prototype.__iterator = function(type, reverse) {
+ var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
+ return new Iterator(function() {
+ while (true) {
+ var step = iterator.next();
+ if (step.done) {
+ return step;
+ }
+ var entry = step.value;
+ // Check if entry exists first so array access doesn't throw for holes
+ // in the parent iteration.
+ if (entry) {
+ validateEntry(entry);
+ var indexedIterable = isIterable(entry);
+ return iteratorValue(
+ type,
+ indexedIterable ? entry.get(0) : entry[0],
+ indexedIterable ? entry.get(1) : entry[1],
+ step
+ );
+ }
+ }
+ });
+ };
+
+
+ ToIndexedSequence.prototype.cacheResult =
+ ToKeyedSequence.prototype.cacheResult =
+ ToSetSequence.prototype.cacheResult =
+ FromEntriesSequence.prototype.cacheResult =
+ cacheResultThrough;
+
+
+ function flipFactory(iterable) {
+ var flipSequence = makeSequence(iterable);
+ flipSequence._iter = iterable;
+ flipSequence.size = iterable.size;
+ flipSequence.flip = function() {return iterable};
+ flipSequence.reverse = function () {
+ var reversedSequence = iterable.reverse.apply(this); // super.reverse()
+ reversedSequence.flip = function() {return iterable.reverse()};
+ return reversedSequence;
+ };
+ flipSequence.has = function(key ) {return iterable.includes(key)};
+ flipSequence.includes = function(key ) {return iterable.has(key)};
+ flipSequence.cacheResult = cacheResultThrough;
+ flipSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
+ return iterable.__iterate(function(v, k) {return fn(k, v, this$0) !== false}, reverse);
+ }
+ flipSequence.__iteratorUncached = function(type, reverse) {
+ if (type === ITERATE_ENTRIES) {
+ var iterator = iterable.__iterator(type, reverse);
+ return new Iterator(function() {
+ var step = iterator.next();
+ if (!step.done) {
+ var k = step.value[0];
+ step.value[0] = step.value[1];
+ step.value[1] = k;
+ }
+ return step;
+ });
+ }
+ return iterable.__iterator(
+ type === ITERATE_VALUES ? ITERATE_KEYS : ITERATE_VALUES,
+ reverse
+ );
+ }
+ return flipSequence;
+ }
+
+
+ function mapFactory(iterable, mapper, context) {
+ var mappedSequence = makeSequence(iterable);
+ mappedSequence.size = iterable.size;
+ mappedSequence.has = function(key ) {return iterable.has(key)};
+ mappedSequence.get = function(key, notSetValue) {
+ var v = iterable.get(key, NOT_SET);
+ return v === NOT_SET ?
+ notSetValue :
+ mapper.call(context, v, key, iterable);
+ };
+ mappedSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
+ return iterable.__iterate(
+ function(v, k, c) {return fn(mapper.call(context, v, k, c), k, this$0) !== false},
+ reverse
+ );
+ }
+ mappedSequence.__iteratorUncached = function (type, reverse) {
+ var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
+ return new Iterator(function() {
+ var step = iterator.next();
+ if (step.done) {
+ return step;
+ }
+ var entry = step.value;
+ var key = entry[0];
+ return iteratorValue(
+ type,
+ key,
+ mapper.call(context, entry[1], key, iterable),
+ step
+ );
+ });
+ }
+ return mappedSequence;
+ }
+
+
+ function reverseFactory(iterable, useKeys) {
+ var reversedSequence = makeSequence(iterable);
+ reversedSequence._iter = iterable;
+ reversedSequence.size = iterable.size;
+ reversedSequence.reverse = function() {return iterable};
+ if (iterable.flip) {
+ reversedSequence.flip = function () {
+ var flipSequence = flipFactory(iterable);
+ flipSequence.reverse = function() {return iterable.flip()};
+ return flipSequence;
+ };
+ }
+ reversedSequence.get = function(key, notSetValue)
+ {return iterable.get(useKeys ? key : -1 - key, notSetValue)};
+ reversedSequence.has = function(key )
+ {return iterable.has(useKeys ? key : -1 - key)};
+ reversedSequence.includes = function(value ) {return iterable.includes(value)};
+ reversedSequence.cacheResult = cacheResultThrough;
+ reversedSequence.__iterate = function (fn, reverse) {var this$0 = this;
+ return iterable.__iterate(function(v, k) {return fn(v, k, this$0)}, !reverse);
+ };
+ reversedSequence.__iterator =
+ function(type, reverse) {return iterable.__iterator(type, !reverse)};
+ return reversedSequence;
+ }
+
+
+ function filterFactory(iterable, predicate, context, useKeys) {
+ var filterSequence = makeSequence(iterable);
+ if (useKeys) {
+ filterSequence.has = function(key ) {
+ var v = iterable.get(key, NOT_SET);
+ return v !== NOT_SET && !!predicate.call(context, v, key, iterable);
+ };
+ filterSequence.get = function(key, notSetValue) {
+ var v = iterable.get(key, NOT_SET);
+ return v !== NOT_SET && predicate.call(context, v, key, iterable) ?
+ v : notSetValue;
+ };
+ }
+ filterSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
+ var iterations = 0;
+ iterable.__iterate(function(v, k, c) {
+ if (predicate.call(context, v, k, c)) {
+ iterations++;
+ return fn(v, useKeys ? k : iterations - 1, this$0);
+ }
+ }, reverse);
+ return iterations;
+ };
+ filterSequence.__iteratorUncached = function (type, reverse) {
+ var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
+ var iterations = 0;
+ return new Iterator(function() {
+ while (true) {
+ var step = iterator.next();
+ if (step.done) {
+ return step;
+ }
+ var entry = step.value;
+ var key = entry[0];
+ var value = entry[1];
+ if (predicate.call(context, value, key, iterable)) {
+ return iteratorValue(type, useKeys ? key : iterations++, value, step);
+ }
+ }
+ });
+ }
+ return filterSequence;
+ }
+
+
+ function countByFactory(iterable, grouper, context) {
+ var groups = Map().asMutable();
+ iterable.__iterate(function(v, k) {
+ groups.update(
+ grouper.call(context, v, k, iterable),
+ 0,
+ function(a ) {return a + 1}
+ );
+ });
+ return groups.asImmutable();
+ }
+
+
+ function groupByFactory(iterable, grouper, context) {
+ var isKeyedIter = isKeyed(iterable);
+ var groups = (isOrdered(iterable) ? OrderedMap() : Map()).asMutable();
+ iterable.__iterate(function(v, k) {
+ groups.update(
+ grouper.call(context, v, k, iterable),
+ function(a ) {return (a = a || [], a.push(isKeyedIter ? [k, v] : v), a)}
+ );
+ });
+ var coerce = iterableClass(iterable);
+ return groups.map(function(arr ) {return reify(iterable, coerce(arr))});
+ }
+
+
+ function sliceFactory(iterable, begin, end, useKeys) {
+ var originalSize = iterable.size;
+
+ // Sanitize begin & end using this shorthand for ToInt32(argument)
+ // http://www.ecma-international.org/ecma-262/6.0/#sec-toint32
+ if (begin !== undefined) {
+ begin = begin | 0;
+ }
+ if (end !== undefined) {
+ end = end | 0;
+ }
+
+ if (wholeSlice(begin, end, originalSize)) {
+ return iterable;
+ }
+
+ var resolvedBegin = resolveBegin(begin, originalSize);
+ var resolvedEnd = resolveEnd(end, originalSize);
+
+ // begin or end will be NaN if they were provided as negative numbers and
+ // this iterable's size is unknown. In that case, cache first so there is
+ // a known size and these do not resolve to NaN.
+ if (resolvedBegin !== resolvedBegin || resolvedEnd !== resolvedEnd) {
+ return sliceFactory(iterable.toSeq().cacheResult(), begin, end, useKeys);
+ }
+
+ // Note: resolvedEnd is undefined when the original sequence's length is
+ // unknown and this slice did not supply an end and should contain all
+ // elements after resolvedBegin.
+ // In that case, resolvedSize will be NaN and sliceSize will remain undefined.
+ var resolvedSize = resolvedEnd - resolvedBegin;
+ var sliceSize;
+ if (resolvedSize === resolvedSize) {
+ sliceSize = resolvedSize < 0 ? 0 : resolvedSize;
+ }
+
+ var sliceSeq = makeSequence(iterable);
+
+ // If iterable.size is undefined, the size of the realized sliceSeq is
+ // unknown at this point unless the number of items to slice is 0
+ sliceSeq.size = sliceSize === 0 ? sliceSize : iterable.size && sliceSize || undefined;
+
+ if (!useKeys && isSeq(iterable) && sliceSize >= 0) {
+ sliceSeq.get = function (index, notSetValue) {
+ index = wrapIndex(this, index);
+ return index >= 0 && index < sliceSize ?
+ iterable.get(index + resolvedBegin, notSetValue) :
+ notSetValue;
+ }
+ }
+
+ sliceSeq.__iterateUncached = function(fn, reverse) {var this$0 = this;
+ if (sliceSize === 0) {
+ return 0;
+ }
+ if (reverse) {
+ return this.cacheResult().__iterate(fn, reverse);
+ }
+ var skipped = 0;
+ var isSkipping = true;
+ var iterations = 0;
+ iterable.__iterate(function(v, k) {
+ if (!(isSkipping && (isSkipping = skipped++ < resolvedBegin))) {
+ iterations++;
+ return fn(v, useKeys ? k : iterations - 1, this$0) !== false &&
+ iterations !== sliceSize;
+ }
+ });
+ return iterations;
+ };
+
+ sliceSeq.__iteratorUncached = function(type, reverse) {
+ if (sliceSize !== 0 && reverse) {
+ return this.cacheResult().__iterator(type, reverse);
+ }
+ // Don't bother instantiating parent iterator if taking 0.
+ var iterator = sliceSize !== 0 && iterable.__iterator(type, reverse);
+ var skipped = 0;
+ var iterations = 0;
+ return new Iterator(function() {
+ while (skipped++ < resolvedBegin) {
+ iterator.next();
+ }
+ if (++iterations > sliceSize) {
+ return iteratorDone();
+ }
+ var step = iterator.next();
+ if (useKeys || type === ITERATE_VALUES) {
+ return step;
+ } else if (type === ITERATE_KEYS) {
+ return iteratorValue(type, iterations - 1, undefined, step);
+ } else {
+ return iteratorValue(type, iterations - 1, step.value[1], step);
+ }
+ });
+ }
+
+ return sliceSeq;
+ }
+
+
+ function takeWhileFactory(iterable, predicate, context) {
+ var takeSequence = makeSequence(iterable);
+ takeSequence.__iterateUncached = function(fn, reverse) {var this$0 = this;
+ if (reverse) {
+ return this.cacheResult().__iterate(fn, reverse);
+ }
+ var iterations = 0;
+ iterable.__iterate(function(v, k, c)
+ {return predicate.call(context, v, k, c) && ++iterations && fn(v, k, this$0)}
+ );
+ return iterations;
+ };
+ takeSequence.__iteratorUncached = function(type, reverse) {var this$0 = this;
+ if (reverse) {
+ return this.cacheResult().__iterator(type, reverse);
+ }
+ var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
+ var iterating = true;
+ return new Iterator(function() {
+ if (!iterating) {
+ return iteratorDone();
+ }
+ var step = iterator.next();
+ if (step.done) {
+ return step;
+ }
+ var entry = step.value;
+ var k = entry[0];
+ var v = entry[1];
+ if (!predicate.call(context, v, k, this$0)) {
+ iterating = false;
+ return iteratorDone();
+ }
+ return type === ITERATE_ENTRIES ? step :
+ iteratorValue(type, k, v, step);
+ });
+ };
+ return takeSequence;
+ }
+
+
+ function skipWhileFactory(iterable, predicate, context, useKeys) {
+ var skipSequence = makeSequence(iterable);
+ skipSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
+ if (reverse) {
+ return this.cacheResult().__iterate(fn, reverse);
+ }
+ var isSkipping = true;
+ var iterations = 0;
+ iterable.__iterate(function(v, k, c) {
+ if (!(isSkipping && (isSkipping = predicate.call(context, v, k, c)))) {
+ iterations++;
+ return fn(v, useKeys ? k : iterations - 1, this$0);
+ }
+ });
+ return iterations;
+ };
+ skipSequence.__iteratorUncached = function(type, reverse) {var this$0 = this;
+ if (reverse) {
+ return this.cacheResult().__iterator(type, reverse);
+ }
+ var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
+ var skipping = true;
+ var iterations = 0;
+ return new Iterator(function() {
+ var step, k, v;
+ do {
+ step = iterator.next();
+ if (step.done) {
+ if (useKeys || type === ITERATE_VALUES) {
+ return step;
+ } else if (type === ITERATE_KEYS) {
+ return iteratorValue(type, iterations++, undefined, step);
+ } else {
+ return iteratorValue(type, iterations++, step.value[1], step);
+ }
+ }
+ var entry = step.value;
+ k = entry[0];
+ v = entry[1];
+ skipping && (skipping = predicate.call(context, v, k, this$0));
+ } while (skipping);
+ return type === ITERATE_ENTRIES ? step :
+ iteratorValue(type, k, v, step);
+ });
+ };
+ return skipSequence;
+ }
+
+
+ function concatFactory(iterable, values) {
+ var isKeyedIterable = isKeyed(iterable);
+ var iters = [iterable].concat(values).map(function(v ) {
+ if (!isIterable(v)) {
+ v = isKeyedIterable ?
+ keyedSeqFromValue(v) :
+ indexedSeqFromValue(Array.isArray(v) ? v : [v]);
+ } else if (isKeyedIterable) {
+ v = KeyedIterable(v);
+ }
+ return v;
+ }).filter(function(v ) {return v.size !== 0});
+
+ if (iters.length === 0) {
+ return iterable;
+ }
+
+ if (iters.length === 1) {
+ var singleton = iters[0];
+ if (singleton === iterable ||
+ isKeyedIterable && isKeyed(singleton) ||
+ isIndexed(iterable) && isIndexed(singleton)) {
+ return singleton;
+ }
+ }
+
+ var concatSeq = new ArraySeq(iters);
+ if (isKeyedIterable) {
+ concatSeq = concatSeq.toKeyedSeq();
+ } else if (!isIndexed(iterable)) {
+ concatSeq = concatSeq.toSetSeq();
+ }
+ concatSeq = concatSeq.flatten(true);
+ concatSeq.size = iters.reduce(
+ function(sum, seq) {
+ if (sum !== undefined) {
+ var size = seq.size;
+ if (size !== undefined) {
+ return sum + size;
+ }
+ }
+ },
+ 0
+ );
+ return concatSeq;
+ }
+
+
+ function flattenFactory(iterable, depth, useKeys) {
+ var flatSequence = makeSequence(iterable);
+ flatSequence.__iterateUncached = function(fn, reverse) {
+ var iterations = 0;
+ var stopped = false;
+ function flatDeep(iter, currentDepth) {var this$0 = this;
+ iter.__iterate(function(v, k) {
+ if ((!depth || currentDepth < depth) && isIterable(v)) {
+ flatDeep(v, currentDepth + 1);
+ } else if (fn(v, useKeys ? k : iterations++, this$0) === false) {
+ stopped = true;
+ }
+ return !stopped;
+ }, reverse);
+ }
+ flatDeep(iterable, 0);
+ return iterations;
+ }
+ flatSequence.__iteratorUncached = function(type, reverse) {
+ var iterator = iterable.__iterator(type, reverse);
+ var stack = [];
+ var iterations = 0;
+ return new Iterator(function() {
+ while (iterator) {
+ var step = iterator.next();
+ if (step.done !== false) {
+ iterator = stack.pop();
+ continue;
+ }
+ var v = step.value;
+ if (type === ITERATE_ENTRIES) {
+ v = v[1];
+ }
+ if ((!depth || stack.length < depth) && isIterable(v)) {
+ stack.push(iterator);
+ iterator = v.__iterator(type, reverse);
+ } else {
+ return useKeys ? step : iteratorValue(type, iterations++, v, step);
+ }
+ }
+ return iteratorDone();
+ });
+ }
+ return flatSequence;
+ }
+
+
+ function flatMapFactory(iterable, mapper, context) {
+ var coerce = iterableClass(iterable);
+ return iterable.toSeq().map(
+ function(v, k) {return coerce(mapper.call(context, v, k, iterable))}
+ ).flatten(true);
+ }
+
+
+ function interposeFactory(iterable, separator) {
+ var interposedSequence = makeSequence(iterable);
+ interposedSequence.size = iterable.size && iterable.size * 2 -1;
+ interposedSequence.__iterateUncached = function(fn, reverse) {var this$0 = this;
+ var iterations = 0;
+ iterable.__iterate(function(v, k)
+ {return (!iterations || fn(separator, iterations++, this$0) !== false) &&
+ fn(v, iterations++, this$0) !== false},
+ reverse
+ );
+ return iterations;
+ };
+ interposedSequence.__iteratorUncached = function(type, reverse) {
+ var iterator = iterable.__iterator(ITERATE_VALUES, reverse);
+ var iterations = 0;
+ var step;
+ return new Iterator(function() {
+ if (!step || iterations % 2) {
+ step = iterator.next();
+ if (step.done) {
+ return step;
+ }
+ }
+ return iterations % 2 ?
+ iteratorValue(type, iterations++, separator) :
+ iteratorValue(type, iterations++, step.value, step);
+ });
+ };
+ return interposedSequence;
+ }
+
+
+ function sortFactory(iterable, comparator, mapper) {
+ if (!comparator) {
+ comparator = defaultComparator;
+ }
+ var isKeyedIterable = isKeyed(iterable);
+ var index = 0;
+ var entries = iterable.toSeq().map(
+ function(v, k) {return [k, v, index++, mapper ? mapper(v, k, iterable) : v]}
+ ).toArray();
+ entries.sort(function(a, b) {return comparator(a[3], b[3]) || a[2] - b[2]}).forEach(
+ isKeyedIterable ?
+ function(v, i) { entries[i].length = 2; } :
+ function(v, i) { entries[i] = v[1]; }
+ );
+ return isKeyedIterable ? KeyedSeq(entries) :
+ isIndexed(iterable) ? IndexedSeq(entries) :
+ SetSeq(entries);
+ }
+
+
+ function maxFactory(iterable, comparator, mapper) {
+ if (!comparator) {
+ comparator = defaultComparator;
+ }
+ if (mapper) {
+ var entry = iterable.toSeq()
+ .map(function(v, k) {return [v, mapper(v, k, iterable)]})
+ .reduce(function(a, b) {return maxCompare(comparator, a[1], b[1]) ? b : a});
+ return entry && entry[0];
+ } else {
+ return iterable.reduce(function(a, b) {return maxCompare(comparator, a, b) ? b : a});
+ }
+ }
+
+ function maxCompare(comparator, a, b) {
+ var comp = comparator(b, a);
+ // b is considered the new max if the comparator declares them equal, but
+ // they are not equal and b is in fact a nullish value.
+ return (comp === 0 && b !== a && (b === undefined || b === null || b !== b)) || comp > 0;
+ }
+
+
+ function zipWithFactory(keyIter, zipper, iters) {
+ var zipSequence = makeSequence(keyIter);
+ zipSequence.size = new ArraySeq(iters).map(function(i ) {return i.size}).min();
+ // Note: this a generic base implementation of __iterate in terms of
+ // __iterator which may be more generically useful in the future.
+ zipSequence.__iterate = function(fn, reverse) {
+ /* generic:
+ var iterator = this.__iterator(ITERATE_ENTRIES, reverse);
+ var step;
+ var iterations = 0;
+ while (!(step = iterator.next()).done) {
+ iterations++;
+ if (fn(step.value[1], step.value[0], this) === false) {
+ break;
+ }
+ }
+ return iterations;
+ */
+ // indexed:
+ var iterator = this.__iterator(ITERATE_VALUES, reverse);
+ var step;
+ var iterations = 0;
+ while (!(step = iterator.next()).done) {
+ if (fn(step.value, iterations++, this) === false) {
+ break;
+ }
+ }
+ return iterations;
+ };
+ zipSequence.__iteratorUncached = function(type, reverse) {
+ var iterators = iters.map(function(i )
+ {return (i = Iterable(i), getIterator(reverse ? i.reverse() : i))}
+ );
+ var iterations = 0;
+ var isDone = false;
+ return new Iterator(function() {
+ var steps;
+ if (!isDone) {
+ steps = iterators.map(function(i ) {return i.next()});
+ isDone = steps.some(function(s ) {return s.done});
+ }
+ if (isDone) {
+ return iteratorDone();
+ }
+ return iteratorValue(
+ type,
+ iterations++,
+ zipper.apply(null, steps.map(function(s ) {return s.value}))
+ );
+ });
+ };
+ return zipSequence
+ }
+
+
+ // #pragma Helper Functions
+
+ function reify(iter, seq) {
+ return isSeq(iter) ? seq : iter.constructor(seq);
+ }
+
+ function validateEntry(entry) {
+ if (entry !== Object(entry)) {
+ throw new TypeError('Expected [K, V] tuple: ' + entry);
+ }
+ }
+
+ function resolveSize(iter) {
+ assertNotInfinite(iter.size);
+ return ensureSize(iter);
+ }
+
+ function iterableClass(iterable) {
+ return isKeyed(iterable) ? KeyedIterable :
+ isIndexed(iterable) ? IndexedIterable :
+ SetIterable;
+ }
+
+ function makeSequence(iterable) {
+ return Object.create(
+ (
+ isKeyed(iterable) ? KeyedSeq :
+ isIndexed(iterable) ? IndexedSeq :
+ SetSeq
+ ).prototype
+ );
+ }
+
+ function cacheResultThrough() {
+ if (this._iter.cacheResult) {
+ this._iter.cacheResult();
+ this.size = this._iter.size;
+ return this;
+ } else {
+ return Seq.prototype.cacheResult.call(this);
+ }
+ }
+
+ function defaultComparator(a, b) {
+ return a > b ? 1 : a < b ? -1 : 0;
+ }
+
+ function forceIterator(keyPath) {
+ var iter = getIterator(keyPath);
+ if (!iter) {
+ // Array might not be iterable in this environment, so we need a fallback
+ // to our wrapped type.
+ if (!isArrayLike(keyPath)) {
+ throw new TypeError('Expected iterable or array-like: ' + keyPath);
+ }
+ iter = getIterator(Iterable(keyPath));
+ }
+ return iter;
+ }
+
+ createClass(Record, KeyedCollection);
+
+ function Record(defaultValues, name) {
+ var hasInitialized;
+
+ var RecordType = function Record(values) {
+ if (values instanceof RecordType) {
+ return values;
+ }
+ if (!(this instanceof RecordType)) {
+ return new RecordType(values);
+ }
+ if (!hasInitialized) {
+ hasInitialized = true;
+ var keys = Object.keys(defaultValues);
+ setProps(RecordTypePrototype, keys);
+ RecordTypePrototype.size = keys.length;
+ RecordTypePrototype._name = name;
+ RecordTypePrototype._keys = keys;
+ RecordTypePrototype._defaultValues = defaultValues;
+ }
+ this._map = Map(values);
+ };
+
+ var RecordTypePrototype = RecordType.prototype = Object.create(RecordPrototype);
+ RecordTypePrototype.constructor = RecordType;
+
+ return RecordType;
+ }
+
+ Record.prototype.toString = function() {
+ return this.__toString(recordName(this) + ' {', '}');
+ };
+
+ // @pragma Access
+
+ Record.prototype.has = function(k) {
+ return this._defaultValues.hasOwnProperty(k);
+ };
+
+ Record.prototype.get = function(k, notSetValue) {
+ if (!this.has(k)) {
+ return notSetValue;
+ }
+ var defaultVal = this._defaultValues[k];
+ return this._map ? this._map.get(k, defaultVal) : defaultVal;
+ };
+
+ // @pragma Modification
+
+ Record.prototype.clear = function() {
+ if (this.__ownerID) {
+ this._map && this._map.clear();
+ return this;
+ }
+ var RecordType = this.constructor;
+ return RecordType._empty || (RecordType._empty = makeRecord(this, emptyMap()));
+ };
+
+ Record.prototype.set = function(k, v) {
+ if (!this.has(k)) {
+ throw new Error('Cannot set unknown key "' + k + '" on ' + recordName(this));
+ }
+ if (this._map && !this._map.has(k)) {
+ var defaultVal = this._defaultValues[k];
+ if (v === defaultVal) {
+ return this;
+ }
+ }
+ var newMap = this._map && this._map.set(k, v);
+ if (this.__ownerID || newMap === this._map) {
+ return this;
+ }
+ return makeRecord(this, newMap);
+ };
+
+ Record.prototype.remove = function(k) {
+ if (!this.has(k)) {
+ return this;
+ }
+ var newMap = this._map && this._map.remove(k);
+ if (this.__ownerID || newMap === this._map) {
+ return this;
+ }
+ return makeRecord(this, newMap);
+ };
+
+ Record.prototype.wasAltered = function() {
+ return this._map.wasAltered();
+ };
+
+ Record.prototype.__iterator = function(type, reverse) {var this$0 = this;
+ return KeyedIterable(this._defaultValues).map(function(_, k) {return this$0.get(k)}).__iterator(type, reverse);
+ };
+
+ Record.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ return KeyedIterable(this._defaultValues).map(function(_, k) {return this$0.get(k)}).__iterate(fn, reverse);
+ };
+
+ Record.prototype.__ensureOwner = function(ownerID) {
+ if (ownerID === this.__ownerID) {
+ return this;
+ }
+ var newMap = this._map && this._map.__ensureOwner(ownerID);
+ if (!ownerID) {
+ this.__ownerID = ownerID;
+ this._map = newMap;
+ return this;
+ }
+ return makeRecord(this, newMap, ownerID);
+ };
+
+
+ var RecordPrototype = Record.prototype;
+ RecordPrototype[DELETE] = RecordPrototype.remove;
+ RecordPrototype.deleteIn =
+ RecordPrototype.removeIn = MapPrototype.removeIn;
+ RecordPrototype.merge = MapPrototype.merge;
+ RecordPrototype.mergeWith = MapPrototype.mergeWith;
+ RecordPrototype.mergeIn = MapPrototype.mergeIn;
+ RecordPrototype.mergeDeep = MapPrototype.mergeDeep;
+ RecordPrototype.mergeDeepWith = MapPrototype.mergeDeepWith;
+ RecordPrototype.mergeDeepIn = MapPrototype.mergeDeepIn;
+ RecordPrototype.setIn = MapPrototype.setIn;
+ RecordPrototype.update = MapPrototype.update;
+ RecordPrototype.updateIn = MapPrototype.updateIn;
+ RecordPrototype.withMutations = MapPrototype.withMutations;
+ RecordPrototype.asMutable = MapPrototype.asMutable;
+ RecordPrototype.asImmutable = MapPrototype.asImmutable;
+
+
+ function makeRecord(likeRecord, map, ownerID) {
+ var record = Object.create(Object.getPrototypeOf(likeRecord));
+ record._map = map;
+ record.__ownerID = ownerID;
+ return record;
+ }
+
+ function recordName(record) {
+ return record._name || record.constructor.name || 'Record';
+ }
+
+ function setProps(prototype, names) {
+ try {
+ names.forEach(setProp.bind(undefined, prototype));
+ } catch (error) {
+ // Object.defineProperty failed. Probably IE8.
+ }
+ }
+
+ function setProp(prototype, name) {
+ Object.defineProperty(prototype, name, {
+ get: function() {
+ return this.get(name);
+ },
+ set: function(value) {
+ invariant(this.__ownerID, 'Cannot set on an immutable record.');
+ this.set(name, value);
+ }
+ });
+ }
+
+ createClass(Set, SetCollection);
+
+ // @pragma Construction
+
+ function Set(value) {
+ return value === null || value === undefined ? emptySet() :
+ isSet(value) && !isOrdered(value) ? value :
+ emptySet().withMutations(function(set ) {
+ var iter = SetIterable(value);
+ assertNotInfinite(iter.size);
+ iter.forEach(function(v ) {return set.add(v)});
+ });
+ }
+
+ Set.of = function(/*...values*/) {
+ return this(arguments);
+ };
+
+ Set.fromKeys = function(value) {
+ return this(KeyedIterable(value).keySeq());
+ };
+
+ Set.prototype.toString = function() {
+ return this.__toString('Set {', '}');
+ };
+
+ // @pragma Access
+
+ Set.prototype.has = function(value) {
+ return this._map.has(value);
+ };
+
+ // @pragma Modification
+
+ Set.prototype.add = function(value) {
+ return updateSet(this, this._map.set(value, true));
+ };
+
+ Set.prototype.remove = function(value) {
+ return updateSet(this, this._map.remove(value));
+ };
+
+ Set.prototype.clear = function() {
+ return updateSet(this, this._map.clear());
+ };
+
+ // @pragma Composition
+
+ Set.prototype.union = function() {var iters = SLICE$0.call(arguments, 0);
+ iters = iters.filter(function(x ) {return x.size !== 0});
+ if (iters.length === 0) {
+ return this;
+ }
+ if (this.size === 0 && !this.__ownerID && iters.length === 1) {
+ return this.constructor(iters[0]);
+ }
+ return this.withMutations(function(set ) {
+ for (var ii = 0; ii < iters.length; ii++) {
+ SetIterable(iters[ii]).forEach(function(value ) {return set.add(value)});
+ }
+ });
+ };
+
+ Set.prototype.intersect = function() {var iters = SLICE$0.call(arguments, 0);
+ if (iters.length === 0) {
+ return this;
+ }
+ iters = iters.map(function(iter ) {return SetIterable(iter)});
+ var originalSet = this;
+ return this.withMutations(function(set ) {
+ originalSet.forEach(function(value ) {
+ if (!iters.every(function(iter ) {return iter.includes(value)})) {
+ set.remove(value);
+ }
+ });
+ });
+ };
+
+ Set.prototype.subtract = function() {var iters = SLICE$0.call(arguments, 0);
+ if (iters.length === 0) {
+ return this;
+ }
+ iters = iters.map(function(iter ) {return SetIterable(iter)});
+ var originalSet = this;
+ return this.withMutations(function(set ) {
+ originalSet.forEach(function(value ) {
+ if (iters.some(function(iter ) {return iter.includes(value)})) {
+ set.remove(value);
+ }
+ });
+ });
+ };
+
+ Set.prototype.merge = function() {
+ return this.union.apply(this, arguments);
+ };
+
+ Set.prototype.mergeWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
+ return this.union.apply(this, iters);
+ };
+
+ Set.prototype.sort = function(comparator) {
+ // Late binding
+ return OrderedSet(sortFactory(this, comparator));
+ };
+
+ Set.prototype.sortBy = function(mapper, comparator) {
+ // Late binding
+ return OrderedSet(sortFactory(this, comparator, mapper));
+ };
+
+ Set.prototype.wasAltered = function() {
+ return this._map.wasAltered();
+ };
+
+ Set.prototype.__iterate = function(fn, reverse) {var this$0 = this;
+ return this._map.__iterate(function(_, k) {return fn(k, k, this$0)}, reverse);
+ };
+
+ Set.prototype.__iterator = function(type, reverse) {
+ return this._map.map(function(_, k) {return k}).__iterator(type, reverse);
+ };
+
+ Set.prototype.__ensureOwner = function(ownerID) {
+ if (ownerID === this.__ownerID) {
+ return this;
+ }
+ var newMap = this._map.__ensureOwner(ownerID);
+ if (!ownerID) {
+ this.__ownerID = ownerID;
+ this._map = newMap;
+ return this;
+ }
+ return this.__make(newMap, ownerID);
+ };
+
+
+ function isSet(maybeSet) {
+ return !!(maybeSet && maybeSet[IS_SET_SENTINEL]);
+ }
+
+ Set.isSet = isSet;
+
+ var IS_SET_SENTINEL = '@@__IMMUTABLE_SET__@@';
+
+ var SetPrototype = Set.prototype;
+ SetPrototype[IS_SET_SENTINEL] = true;
+ SetPrototype[DELETE] = SetPrototype.remove;
+ SetPrototype.mergeDeep = SetPrototype.merge;
+ SetPrototype.mergeDeepWith = SetPrototype.mergeWith;
+ SetPrototype.withMutations = MapPrototype.withMutations;
+ SetPrototype.asMutable = MapPrototype.asMutable;
+ SetPrototype.asImmutable = MapPrototype.asImmutable;
+
+ SetPrototype.__empty = emptySet;
+ SetPrototype.__make = makeSet;
+
+ function updateSet(set, newMap) {
+ if (set.__ownerID) {
+ set.size = newMap.size;
+ set._map = newMap;
+ return set;
+ }
+ return newMap === set._map ? set :
+ newMap.size === 0 ? set.__empty() :
+ set.__make(newMap);
+ }
+
+ function makeSet(map, ownerID) {
+ var set = Object.create(SetPrototype);
+ set.size = map ? map.size : 0;
+ set._map = map;
+ set.__ownerID = ownerID;
+ return set;
+ }
+
+ var EMPTY_SET;
+ function emptySet() {
+ return EMPTY_SET || (EMPTY_SET = makeSet(emptyMap()));
+ }
+
+ createClass(OrderedSet, Set);
+
+ // @pragma Construction
+
+ function OrderedSet(value) {
+ return value === null || value === undefined ? emptyOrderedSet() :
+ isOrderedSet(value) ? value :
+ emptyOrderedSet().withMutations(function(set ) {
+ var iter = SetIterable(value);
+ assertNotInfinite(iter.size);
+ iter.forEach(function(v ) {return set.add(v)});
+ });
+ }
+
+ OrderedSet.of = function(/*...values*/) {
+ return this(arguments);
+ };
+
+ OrderedSet.fromKeys = function(value) {
+ return this(KeyedIterable(value).keySeq());
+ };
+
+ OrderedSet.prototype.toString = function() {
+ return this.__toString('OrderedSet {', '}');
+ };
+
+
+ function isOrderedSet(maybeOrderedSet) {
+ return isSet(maybeOrderedSet) && isOrdered(maybeOrderedSet);
+ }
+
+ OrderedSet.isOrderedSet = isOrderedSet;
+
+ var OrderedSetPrototype = OrderedSet.prototype;
+ OrderedSetPrototype[IS_ORDERED_SENTINEL] = true;
+
+ OrderedSetPrototype.__empty = emptyOrderedSet;
+ OrderedSetPrototype.__make = makeOrderedSet;
+
+ function makeOrderedSet(map, ownerID) {
+ var set = Object.create(OrderedSetPrototype);
+ set.size = map ? map.size : 0;
+ set._map = map;
+ set.__ownerID = ownerID;
+ return set;
+ }
+
+ var EMPTY_ORDERED_SET;
+ function emptyOrderedSet() {
+ return EMPTY_ORDERED_SET || (EMPTY_ORDERED_SET = makeOrderedSet(emptyOrderedMap()));
+ }
+
+ createClass(Stack, IndexedCollection);
+
+ // @pragma Construction
+
+ function Stack(value) {
+ return value === null || value === undefined ? emptyStack() :
+ isStack(value) ? value :
+ emptyStack().unshiftAll(value);
+ }
+
+ Stack.of = function(/*...values*/) {
+ return this(arguments);
+ };
+
+ Stack.prototype.toString = function() {
+ return this.__toString('Stack [', ']');
+ };
+
+ // @pragma Access
+
+ Stack.prototype.get = function(index, notSetValue) {
+ var head = this._head;
+ index = wrapIndex(this, index);
+ while (head && index--) {
+ head = head.next;
+ }
+ return head ? head.value : notSetValue;
+ };
+
+ Stack.prototype.peek = function() {
+ return this._head && this._head.value;
+ };
+
+ // @pragma Modification
+
+ Stack.prototype.push = function(/*...values*/) {
+ if (arguments.length === 0) {
+ return this;
+ }
+ var newSize = this.size + arguments.length;
+ var head = this._head;
+ for (var ii = arguments.length - 1; ii >= 0; ii--) {
+ head = {
+ value: arguments[ii],
+ next: head
+ };
+ }
+ if (this.__ownerID) {
+ this.size = newSize;
+ this._head = head;
+ this.__hash = undefined;
+ this.__altered = true;
+ return this;
+ }
+ return makeStack(newSize, head);
+ };
+
+ Stack.prototype.pushAll = function(iter) {
+ iter = IndexedIterable(iter);
+ if (iter.size === 0) {
+ return this;
+ }
+ assertNotInfinite(iter.size);
+ var newSize = this.size;
+ var head = this._head;
+ iter.reverse().forEach(function(value ) {
+ newSize++;
+ head = {
+ value: value,
+ next: head
+ };
+ });
+ if (this.__ownerID) {
+ this.size = newSize;
+ this._head = head;
+ this.__hash = undefined;
+ this.__altered = true;
+ return this;
+ }
+ return makeStack(newSize, head);
+ };
+
+ Stack.prototype.pop = function() {
+ return this.slice(1);
+ };
+
+ Stack.prototype.unshift = function(/*...values*/) {
+ return this.push.apply(this, arguments);
+ };
+
+ Stack.prototype.unshiftAll = function(iter) {
+ return this.pushAll(iter);
+ };
+
+ Stack.prototype.shift = function() {
+ return this.pop.apply(this, arguments);
+ };
+
+ Stack.prototype.clear = function() {
+ if (this.size === 0) {
+ return this;
+ }
+ if (this.__ownerID) {
+ this.size = 0;
+ this._head = undefined;
+ this.__hash = undefined;
+ this.__altered = true;
+ return this;
+ }
+ return emptyStack();
+ };
+
+ Stack.prototype.slice = function(begin, end) {
+ if (wholeSlice(begin, end, this.size)) {
+ return this;
+ }
+ var resolvedBegin = resolveBegin(begin, this.size);
+ var resolvedEnd = resolveEnd(end, this.size);
+ if (resolvedEnd !== this.size) {
+ // super.slice(begin, end);
+ return IndexedCollection.prototype.slice.call(this, begin, end);
+ }
+ var newSize = this.size - resolvedBegin;
+ var head = this._head;
+ while (resolvedBegin--) {
+ head = head.next;
+ }
+ if (this.__ownerID) {
+ this.size = newSize;
+ this._head = head;
+ this.__hash = undefined;
+ this.__altered = true;
+ return this;
+ }
+ return makeStack(newSize, head);
+ };
+
+ // @pragma Mutability
+
+ Stack.prototype.__ensureOwner = function(ownerID) {
+ if (ownerID === this.__ownerID) {
+ return this;
+ }
+ if (!ownerID) {
+ this.__ownerID = ownerID;
+ this.__altered = false;
+ return this;
+ }
+ return makeStack(this.size, this._head, ownerID, this.__hash);
+ };
+
+ // @pragma Iteration
+
+ Stack.prototype.__iterate = function(fn, reverse) {
+ if (reverse) {
+ return this.reverse().__iterate(fn);
+ }
+ var iterations = 0;
+ var node = this._head;
+ while (node) {
+ if (fn(node.value, iterations++, this) === false) {
+ break;
+ }
+ node = node.next;
+ }
+ return iterations;
+ };
+
+ Stack.prototype.__iterator = function(type, reverse) {
+ if (reverse) {
+ return this.reverse().__iterator(type);
+ }
+ var iterations = 0;
+ var node = this._head;
+ return new Iterator(function() {
+ if (node) {
+ var value = node.value;
+ node = node.next;
+ return iteratorValue(type, iterations++, value);
+ }
+ return iteratorDone();
+ });
+ };
+
+
+ function isStack(maybeStack) {
+ return !!(maybeStack && maybeStack[IS_STACK_SENTINEL]);
+ }
+
+ Stack.isStack = isStack;
+
+ var IS_STACK_SENTINEL = '@@__IMMUTABLE_STACK__@@';
+
+ var StackPrototype = Stack.prototype;
+ StackPrototype[IS_STACK_SENTINEL] = true;
+ StackPrototype.withMutations = MapPrototype.withMutations;
+ StackPrototype.asMutable = MapPrototype.asMutable;
+ StackPrototype.asImmutable = MapPrototype.asImmutable;
+ StackPrototype.wasAltered = MapPrototype.wasAltered;
+
+
+ function makeStack(size, head, ownerID, hash) {
+ var map = Object.create(StackPrototype);
+ map.size = size;
+ map._head = head;
+ map.__ownerID = ownerID;
+ map.__hash = hash;
+ map.__altered = false;
+ return map;
+ }
+
+ var EMPTY_STACK;
+ function emptyStack() {
+ return EMPTY_STACK || (EMPTY_STACK = makeStack(0));
+ }
+
+ /**
+ * Contributes additional methods to a constructor
+ */
+ function mixin(ctor, methods) {
+ var keyCopier = function(key ) { ctor.prototype[key] = methods[key]; };
+ Object.keys(methods).forEach(keyCopier);
+ Object.getOwnPropertySymbols &&
+ Object.getOwnPropertySymbols(methods).forEach(keyCopier);
+ return ctor;
+ }
+
+ Iterable.Iterator = Iterator;
+
+ mixin(Iterable, {
+
+ // ### Conversion to other types
+
+ toArray: function() {
+ assertNotInfinite(this.size);
+ var array = new Array(this.size || 0);
+ this.valueSeq().__iterate(function(v, i) { array[i] = v; });
+ return array;
+ },
+
+ toIndexedSeq: function() {
+ return new ToIndexedSequence(this);
+ },
+
+ toJS: function() {
+ return this.toSeq().map(
+ function(value ) {return value && typeof value.toJS === 'function' ? value.toJS() : value}
+ ).__toJS();
+ },
+
+ toJSON: function() {
+ return this.toSeq().map(
+ function(value ) {return value && typeof value.toJSON === 'function' ? value.toJSON() : value}
+ ).__toJS();
+ },
+
+ toKeyedSeq: function() {
+ return new ToKeyedSequence(this, true);
+ },
+
+ toMap: function() {
+ // Use Late Binding here to solve the circular dependency.
+ return Map(this.toKeyedSeq());
+ },
+
+ toObject: function() {
+ assertNotInfinite(this.size);
+ var object = {};
+ this.__iterate(function(v, k) { object[k] = v; });
+ return object;
+ },
+
+ toOrderedMap: function() {
+ // Use Late Binding here to solve the circular dependency.
+ return OrderedMap(this.toKeyedSeq());
+ },
+
+ toOrderedSet: function() {
+ // Use Late Binding here to solve the circular dependency.
+ return OrderedSet(isKeyed(this) ? this.valueSeq() : this);
+ },
+
+ toSet: function() {
+ // Use Late Binding here to solve the circular dependency.
+ return Set(isKeyed(this) ? this.valueSeq() : this);
+ },
+
+ toSetSeq: function() {
+ return new ToSetSequence(this);
+ },
+
+ toSeq: function() {
+ return isIndexed(this) ? this.toIndexedSeq() :
+ isKeyed(this) ? this.toKeyedSeq() :
+ this.toSetSeq();
+ },
+
+ toStack: function() {
+ // Use Late Binding here to solve the circular dependency.
+ return Stack(isKeyed(this) ? this.valueSeq() : this);
+ },
+
+ toList: function() {
+ // Use Late Binding here to solve the circular dependency.
+ return List(isKeyed(this) ? this.valueSeq() : this);
+ },
+
+
+ // ### Common JavaScript methods and properties
+
+ toString: function() {
+ return '[Iterable]';
+ },
+
+ __toString: function(head, tail) {
+ if (this.size === 0) {
+ return head + tail;
+ }
+ return head + ' ' + this.toSeq().map(this.__toStringMapper).join(', ') + ' ' + tail;
+ },
+
+
+ // ### ES6 Collection methods (ES6 Array and Map)
+
+ concat: function() {var values = SLICE$0.call(arguments, 0);
+ return reify(this, concatFactory(this, values));
+ },
+
+ includes: function(searchValue) {
+ return this.some(function(value ) {return is(value, searchValue)});
+ },
+
+ entries: function() {
+ return this.__iterator(ITERATE_ENTRIES);
+ },
+
+ every: function(predicate, context) {
+ assertNotInfinite(this.size);
+ var returnValue = true;
+ this.__iterate(function(v, k, c) {
+ if (!predicate.call(context, v, k, c)) {
+ returnValue = false;
+ return false;
+ }
+ });
+ return returnValue;
+ },
+
+ filter: function(predicate, context) {
+ return reify(this, filterFactory(this, predicate, context, true));
+ },
+
+ find: function(predicate, context, notSetValue) {
+ var entry = this.findEntry(predicate, context);
+ return entry ? entry[1] : notSetValue;
+ },
+
+ findEntry: function(predicate, context) {
+ var found;
+ this.__iterate(function(v, k, c) {
+ if (predicate.call(context, v, k, c)) {
+ found = [k, v];
+ return false;
+ }
+ });
+ return found;
+ },
+
+ findLastEntry: function(predicate, context) {
+ return this.toSeq().reverse().findEntry(predicate, context);
+ },
+
+ forEach: function(sideEffect, context) {
+ assertNotInfinite(this.size);
+ return this.__iterate(context ? sideEffect.bind(context) : sideEffect);
+ },
+
+ join: function(separator) {
+ assertNotInfinite(this.size);
+ separator = separator !== undefined ? '' + separator : ',';
+ var joined = '';
+ var isFirst = true;
+ this.__iterate(function(v ) {
+ isFirst ? (isFirst = false) : (joined += separator);
+ joined += v !== null && v !== undefined ? v.toString() : '';
+ });
+ return joined;
+ },
+
+ keys: function() {
+ return this.__iterator(ITERATE_KEYS);
+ },
+
+ map: function(mapper, context) {
+ return reify(this, mapFactory(this, mapper, context));
+ },
+
+ reduce: function(reducer, initialReduction, context) {
+ assertNotInfinite(this.size);
+ var reduction;
+ var useFirst;
+ if (arguments.length < 2) {
+ useFirst = true;
+ } else {
+ reduction = initialReduction;
+ }
+ this.__iterate(function(v, k, c) {
+ if (useFirst) {
+ useFirst = false;
+ reduction = v;
+ } else {
+ reduction = reducer.call(context, reduction, v, k, c);
+ }
+ });
+ return reduction;
+ },
+
+ reduceRight: function(reducer, initialReduction, context) {
+ var reversed = this.toKeyedSeq().reverse();
+ return reversed.reduce.apply(reversed, arguments);
+ },
+
+ reverse: function() {
+ return reify(this, reverseFactory(this, true));
+ },
+
+ slice: function(begin, end) {
+ return reify(this, sliceFactory(this, begin, end, true));
+ },
+
+ some: function(predicate, context) {
+ return !this.every(not(predicate), context);
+ },
+
+ sort: function(comparator) {
+ return reify(this, sortFactory(this, comparator));
+ },
+
+ values: function() {
+ return this.__iterator(ITERATE_VALUES);
+ },
+
+
+ // ### More sequential methods
+
+ butLast: function() {
+ return this.slice(0, -1);
+ },
+
+ isEmpty: function() {
+ return this.size !== undefined ? this.size === 0 : !this.some(function() {return true});
+ },
+
+ count: function(predicate, context) {
+ return ensureSize(
+ predicate ? this.toSeq().filter(predicate, context) : this
+ );
+ },
+
+ countBy: function(grouper, context) {
+ return countByFactory(this, grouper, context);
+ },
+
+ equals: function(other) {
+ return deepEqual(this, other);
+ },
+
+ entrySeq: function() {
+ var iterable = this;
+ if (iterable._cache) {
+ // We cache as an entries array, so we can just return the cache!
+ return new ArraySeq(iterable._cache);
+ }
+ var entriesSequence = iterable.toSeq().map(entryMapper).toIndexedSeq();
+ entriesSequence.fromEntrySeq = function() {return iterable.toSeq()};
+ return entriesSequence;
+ },
+
+ filterNot: function(predicate, context) {
+ return this.filter(not(predicate), context);
+ },
+
+ findLast: function(predicate, context, notSetValue) {
+ return this.toKeyedSeq().reverse().find(predicate, context, notSetValue);
+ },
+
+ first: function() {
+ return this.find(returnTrue);
+ },
+
+ flatMap: function(mapper, context) {
+ return reify(this, flatMapFactory(this, mapper, context));
+ },
+
+ flatten: function(depth) {
+ return reify(this, flattenFactory(this, depth, true));
+ },
+
+ fromEntrySeq: function() {
+ return new FromEntriesSequence(this);
+ },
+
+ get: function(searchKey, notSetValue) {
+ return this.find(function(_, key) {return is(key, searchKey)}, undefined, notSetValue);
+ },
+
+ getIn: function(searchKeyPath, notSetValue) {
+ var nested = this;
+ // Note: in an ES6 environment, we would prefer:
+ // for (var key of searchKeyPath) {
+ var iter = forceIterator(searchKeyPath);
+ var step;
+ while (!(step = iter.next()).done) {
+ var key = step.value;
+ nested = nested && nested.get ? nested.get(key, NOT_SET) : NOT_SET;
+ if (nested === NOT_SET) {
+ return notSetValue;
+ }
+ }
+ return nested;
+ },
+
+ groupBy: function(grouper, context) {
+ return groupByFactory(this, grouper, context);
+ },
+
+ has: function(searchKey) {
+ return this.get(searchKey, NOT_SET) !== NOT_SET;
+ },
+
+ hasIn: function(searchKeyPath) {
+ return this.getIn(searchKeyPath, NOT_SET) !== NOT_SET;
+ },
+
+ isSubset: function(iter) {
+ iter = typeof iter.includes === 'function' ? iter : Iterable(iter);
+ return this.every(function(value ) {return iter.includes(value)});
+ },
+
+ isSuperset: function(iter) {
+ iter = typeof iter.isSubset === 'function' ? iter : Iterable(iter);
+ return iter.isSubset(this);
+ },
+
+ keySeq: function() {
+ return this.toSeq().map(keyMapper).toIndexedSeq();
+ },
+
+ last: function() {
+ return this.toSeq().reverse().first();
+ },
+
+ max: function(comparator) {
+ return maxFactory(this, comparator);
+ },
+
+ maxBy: function(mapper, comparator) {
+ return maxFactory(this, comparator, mapper);
+ },
+
+ min: function(comparator) {
+ return maxFactory(this, comparator ? neg(comparator) : defaultNegComparator);
+ },
+
+ minBy: function(mapper, comparator) {
+ return maxFactory(this, comparator ? neg(comparator) : defaultNegComparator, mapper);
+ },
+
+ rest: function() {
+ return this.slice(1);
+ },
+
+ skip: function(amount) {
+ return this.slice(Math.max(0, amount));
+ },
+
+ skipLast: function(amount) {
+ return reify(this, this.toSeq().reverse().skip(amount).reverse());
+ },
+
+ skipWhile: function(predicate, context) {
+ return reify(this, skipWhileFactory(this, predicate, context, true));
+ },
+
+ skipUntil: function(predicate, context) {
+ return this.skipWhile(not(predicate), context);
+ },
+
+ sortBy: function(mapper, comparator) {
+ return reify(this, sortFactory(this, comparator, mapper));
+ },
+
+ take: function(amount) {
+ return this.slice(0, Math.max(0, amount));
+ },
+
+ takeLast: function(amount) {
+ return reify(this, this.toSeq().reverse().take(amount).reverse());
+ },
+
+ takeWhile: function(predicate, context) {
+ return reify(this, takeWhileFactory(this, predicate, context));
+ },
+
+ takeUntil: function(predicate, context) {
+ return this.takeWhile(not(predicate), context);
+ },
+
+ valueSeq: function() {
+ return this.toIndexedSeq();
+ },
+
+
+ // ### Hashable Object
+
+ hashCode: function() {
+ return this.__hash || (this.__hash = hashIterable(this));
+ }
+
+
+ // ### Internal
+
+ // abstract __iterate(fn, reverse)
+
+ // abstract __iterator(type, reverse)
+ });
+
+ // var IS_ITERABLE_SENTINEL = '@@__IMMUTABLE_ITERABLE__@@';
+ // var IS_KEYED_SENTINEL = '@@__IMMUTABLE_KEYED__@@';
+ // var IS_INDEXED_SENTINEL = '@@__IMMUTABLE_INDEXED__@@';
+ // var IS_ORDERED_SENTINEL = '@@__IMMUTABLE_ORDERED__@@';
+
+ var IterablePrototype = Iterable.prototype;
+ IterablePrototype[IS_ITERABLE_SENTINEL] = true;
+ IterablePrototype[ITERATOR_SYMBOL] = IterablePrototype.values;
+ IterablePrototype.__toJS = IterablePrototype.toArray;
+ IterablePrototype.__toStringMapper = quoteString;
+ IterablePrototype.inspect =
+ IterablePrototype.toSource = function() { return this.toString(); };
+ IterablePrototype.chain = IterablePrototype.flatMap;
+ IterablePrototype.contains = IterablePrototype.includes;
+
+ // Temporary warning about using length
+ (function () {
+ try {
+ Object.defineProperty(IterablePrototype, 'length', {
+ get: function () {
+ if (!Iterable.noLengthWarning) {
+ var stack;
+ try {
+ throw new Error();
+ } catch (error) {
+ stack = error.stack;
+ }
+ if (stack.indexOf('_wrapObject') === -1) {
+ console && console.warn && console.warn(
+ 'iterable.length has been deprecated, '+
+ 'use iterable.size or iterable.count(). '+
+ 'This warning will become a silent error in a future version. ' +
+ stack
+ );
+ return this.size;
+ }
+ }
+ }
+ });
+ } catch (e) {}
+ })();
+
+
+
+ mixin(KeyedIterable, {
+
+ // ### More sequential methods
+
+ flip: function() {
+ return reify(this, flipFactory(this));
+ },
+
+ findKey: function(predicate, context) {
+ var entry = this.findEntry(predicate, context);
+ return entry && entry[0];
+ },
+
+ findLastKey: function(predicate, context) {
+ return this.toSeq().reverse().findKey(predicate, context);
+ },
+
+ keyOf: function(searchValue) {
+ return this.findKey(function(value ) {return is(value, searchValue)});
+ },
+
+ lastKeyOf: function(searchValue) {
+ return this.findLastKey(function(value ) {return is(value, searchValue)});
+ },
+
+ mapEntries: function(mapper, context) {var this$0 = this;
+ var iterations = 0;
+ return reify(this,
+ this.toSeq().map(
+ function(v, k) {return mapper.call(context, [k, v], iterations++, this$0)}
+ ).fromEntrySeq()
+ );
+ },
+
+ mapKeys: function(mapper, context) {var this$0 = this;
+ return reify(this,
+ this.toSeq().flip().map(
+ function(k, v) {return mapper.call(context, k, v, this$0)}
+ ).flip()
+ );
+ }
+
+ });
+
+ var KeyedIterablePrototype = KeyedIterable.prototype;
+ KeyedIterablePrototype[IS_KEYED_SENTINEL] = true;
+ KeyedIterablePrototype[ITERATOR_SYMBOL] = IterablePrototype.entries;
+ KeyedIterablePrototype.__toJS = IterablePrototype.toObject;
+ KeyedIterablePrototype.__toStringMapper = function(v, k) {return JSON.stringify(k) + ': ' + quoteString(v)};
+
+
+
+ mixin(IndexedIterable, {
+
+ // ### Conversion to other types
+
+ toKeyedSeq: function() {
+ return new ToKeyedSequence(this, false);
+ },
+
+
+ // ### ES6 Collection methods (ES6 Array and Map)
+
+ filter: function(predicate, context) {
+ return reify(this, filterFactory(this, predicate, context, false));
+ },
+
+ findIndex: function(predicate, context) {
+ var entry = this.findEntry(predicate, context);
+ return entry ? entry[0] : -1;
+ },
+
+ indexOf: function(searchValue) {
+ var key = this.toKeyedSeq().keyOf(searchValue);
+ return key === undefined ? -1 : key;
+ },
+
+ lastIndexOf: function(searchValue) {
+ var key = this.toKeyedSeq().reverse().keyOf(searchValue);
+ return key === undefined ? -1 : key;
+ },
+
+ reverse: function() {
+ return reify(this, reverseFactory(this, false));
+ },
+
+ slice: function(begin, end) {
+ return reify(this, sliceFactory(this, begin, end, false));
+ },
+
+ splice: function(index, removeNum /*, ...values*/) {
+ var numArgs = arguments.length;
+ removeNum = Math.max(removeNum | 0, 0);
+ if (numArgs === 0 || (numArgs === 2 && !removeNum)) {
+ return this;
+ }
+ // If index is negative, it should resolve relative to the size of the
+ // collection. However size may be expensive to compute if not cached, so
+ // only call count() if the number is in fact negative.
+ index = resolveBegin(index, index < 0 ? this.count() : this.size);
+ var spliced = this.slice(0, index);
+ return reify(
+ this,
+ numArgs === 1 ?
+ spliced :
+ spliced.concat(arrCopy(arguments, 2), this.slice(index + removeNum))
+ );
+ },
+
+
+ // ### More collection methods
+
+ findLastIndex: function(predicate, context) {
+ var key = this.toKeyedSeq().findLastKey(predicate, context);
+ return key === undefined ? -1 : key;
+ },
+
+ first: function() {
+ return this.get(0);
+ },
+
+ flatten: function(depth) {
+ return reify(this, flattenFactory(this, depth, false));
+ },
+
+ get: function(index, notSetValue) {
+ index = wrapIndex(this, index);
+ return (index < 0 || (this.size === Infinity ||
+ (this.size !== undefined && index > this.size))) ?
+ notSetValue :
+ this.find(function(_, key) {return key === index}, undefined, notSetValue);
+ },
+
+ has: function(index) {
+ index = wrapIndex(this, index);
+ return index >= 0 && (this.size !== undefined ?
+ this.size === Infinity || index < this.size :
+ this.indexOf(index) !== -1
+ );
+ },
+
+ interpose: function(separator) {
+ return reify(this, interposeFactory(this, separator));
+ },
+
+ interleave: function(/*...iterables*/) {
+ var iterables = [this].concat(arrCopy(arguments));
+ var zipped = zipWithFactory(this.toSeq(), IndexedSeq.of, iterables);
+ var interleaved = zipped.flatten(true);
+ if (zipped.size) {
+ interleaved.size = zipped.size * iterables.length;
+ }
+ return reify(this, interleaved);
+ },
+
+ last: function() {
+ return this.get(-1);
+ },
+
+ skipWhile: function(predicate, context) {
+ return reify(this, skipWhileFactory(this, predicate, context, false));
+ },
+
+ zip: function(/*, ...iterables */) {
+ var iterables = [this].concat(arrCopy(arguments));
+ return reify(this, zipWithFactory(this, defaultZipper, iterables));
+ },
+
+ zipWith: function(zipper/*, ...iterables */) {
+ var iterables = arrCopy(arguments);
+ iterables[0] = this;
+ return reify(this, zipWithFactory(this, zipper, iterables));
+ }
+
+ });
+
+ IndexedIterable.prototype[IS_INDEXED_SENTINEL] = true;
+ IndexedIterable.prototype[IS_ORDERED_SENTINEL] = true;
+
+
+
+ mixin(SetIterable, {
+
+ // ### ES6 Collection methods (ES6 Array and Map)
+
+ get: function(value, notSetValue) {
+ return this.has(value) ? value : notSetValue;
+ },
+
+ includes: function(value) {
+ return this.has(value);
+ },
+
+
+ // ### More sequential methods
+
+ keySeq: function() {
+ return this.valueSeq();
+ }
+
+ });
+
+ SetIterable.prototype.has = IterablePrototype.includes;
+ SetIterable.prototype.contains = SetIterable.prototype.includes;
+
+
+ // Mixin subclasses
+
+ mixin(KeyedSeq, KeyedIterable.prototype);
+ mixin(IndexedSeq, IndexedIterable.prototype);
+ mixin(SetSeq, SetIterable.prototype);
+
+ mixin(KeyedCollection, KeyedIterable.prototype);
+ mixin(IndexedCollection, IndexedIterable.prototype);
+ mixin(SetCollection, SetIterable.prototype);
+
+
+ // #pragma Helper functions
+
+ function keyMapper(v, k) {
+ return k;
+ }
+
+ function entryMapper(v, k) {
+ return [k, v];
+ }
+
+ function not(predicate) {
+ return function() {
+ return !predicate.apply(this, arguments);
+ }
+ }
+
+ function neg(predicate) {
+ return function() {
+ return -predicate.apply(this, arguments);
+ }
+ }
+
+ function quoteString(value) {
+ return typeof value === 'string' ? JSON.stringify(value) : value;
+ }
+
+ function defaultZipper() {
+ return arrCopy(arguments);
+ }
+
+ function defaultNegComparator(a, b) {
+ return a < b ? 1 : a > b ? -1 : 0;
+ }
+
+ function hashIterable(iterable) {
+ if (iterable.size === Infinity) {
+ return 0;
+ }
+ var ordered = isOrdered(iterable);
+ var keyed = isKeyed(iterable);
+ var h = ordered ? 1 : 0;
+ var size = iterable.__iterate(
+ keyed ?
+ ordered ?
+ function(v, k) { h = 31 * h + hashMerge(hash(v), hash(k)) | 0; } :
+ function(v, k) { h = h + hashMerge(hash(v), hash(k)) | 0; } :
+ ordered ?
+ function(v ) { h = 31 * h + hash(v) | 0; } :
+ function(v ) { h = h + hash(v) | 0; }
+ );
+ return murmurHashOfSize(size, h);
+ }
+
+ function murmurHashOfSize(size, h) {
+ h = imul(h, 0xCC9E2D51);
+ h = imul(h << 15 | h >>> -15, 0x1B873593);
+ h = imul(h << 13 | h >>> -13, 5);
+ h = (h + 0xE6546B64 | 0) ^ size;
+ h = imul(h ^ h >>> 16, 0x85EBCA6B);
+ h = imul(h ^ h >>> 13, 0xC2B2AE35);
+ h = smi(h ^ h >>> 16);
+ return h;
+ }
+
+ function hashMerge(a, b) {
+ return a ^ b + 0x9E3779B9 + (a << 6) + (a >> 2) | 0; // int
+ }
+
+ var Immutable = {
+
+ Iterable: Iterable,
+
+ Seq: Seq,
+ Collection: Collection,
+ Map: Map,
+ OrderedMap: OrderedMap,
+ List: List,
+ Stack: Stack,
+ Set: Set,
+ OrderedSet: OrderedSet,
+
+ Record: Record,
+ Range: Range,
+ Repeat: Repeat,
+
+ is: is,
+ fromJS: fromJS
+
+ };
+
+ return Immutable;
+
+})); \ No newline at end of file
diff --git a/devtools/client/shared/vendor/jszip.js b/devtools/client/shared/vendor/jszip.js
new file mode 100644
index 0000000000..639685c10b
--- /dev/null
+++ b/devtools/client/shared/vendor/jszip.js
@@ -0,0 +1,11367 @@
+/*!
+
+JSZip v3.2.1 - A JavaScript class for generating and reading zip files
+<http://stuartk.com/jszip>
+
+(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
+Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown.
+
+JSZip uses the library pako released under the MIT license :
+https://github.com/nodeca/pako/blob/master/LICENSE
+*/
+
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.JSZip = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+'use strict';
+var utils = require('./utils');
+var support = require('./support');
+// private property
+var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+
+// public method for encoding
+exports.encode = function(input) {
+ var output = [];
+ var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
+ var i = 0, len = input.length, remainingBytes = len;
+
+ var isArray = utils.getTypeOf(input) !== "string";
+ while (i < input.length) {
+ remainingBytes = len - i;
+
+ if (!isArray) {
+ chr1 = input.charCodeAt(i++);
+ chr2 = i < len ? input.charCodeAt(i++) : 0;
+ chr3 = i < len ? input.charCodeAt(i++) : 0;
+ } else {
+ chr1 = input[i++];
+ chr2 = i < len ? input[i++] : 0;
+ chr3 = i < len ? input[i++] : 0;
+ }
+
+ enc1 = chr1 >> 2;
+ enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
+ enc3 = remainingBytes > 1 ? (((chr2 & 15) << 2) | (chr3 >> 6)) : 64;
+ enc4 = remainingBytes > 2 ? (chr3 & 63) : 64;
+
+ output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4));
+
+ }
+
+ return output.join("");
+};
+
+// public method for decoding
+exports.decode = function(input) {
+ var chr1, chr2, chr3;
+ var enc1, enc2, enc3, enc4;
+ var i = 0, resultIndex = 0;
+
+ var dataUrlPrefix = "data:";
+
+ if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) {
+ // This is a common error: people give a data url
+ // (data:image/png;base64,iVBOR...) with a {base64: true} and
+ // wonders why things don't work.
+ // We can detect that the string input looks like a data url but we
+ // *can't* be sure it is one: removing everything up to the comma would
+ // be too dangerous.
+ throw new Error("Invalid base64 input, it looks like a data url.");
+ }
+
+ input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
+
+ var totalLength = input.length * 3 / 4;
+ if(input.charAt(input.length - 1) === _keyStr.charAt(64)) {
+ totalLength--;
+ }
+ if(input.charAt(input.length - 2) === _keyStr.charAt(64)) {
+ totalLength--;
+ }
+ if (totalLength % 1 !== 0) {
+ // totalLength is not an integer, the length does not match a valid
+ // base64 content. That can happen if:
+ // - the input is not a base64 content
+ // - the input is *almost* a base64 content, with a extra chars at the
+ // beginning or at the end
+ // - the input uses a base64 variant (base64url for example)
+ throw new Error("Invalid base64 input, bad content length.");
+ }
+ var output;
+ if (support.uint8array) {
+ output = new Uint8Array(totalLength|0);
+ } else {
+ output = new Array(totalLength|0);
+ }
+
+ while (i < input.length) {
+
+ enc1 = _keyStr.indexOf(input.charAt(i++));
+ enc2 = _keyStr.indexOf(input.charAt(i++));
+ enc3 = _keyStr.indexOf(input.charAt(i++));
+ enc4 = _keyStr.indexOf(input.charAt(i++));
+
+ chr1 = (enc1 << 2) | (enc2 >> 4);
+ chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
+ chr3 = ((enc3 & 3) << 6) | enc4;
+
+ output[resultIndex++] = chr1;
+
+ if (enc3 !== 64) {
+ output[resultIndex++] = chr2;
+ }
+ if (enc4 !== 64) {
+ output[resultIndex++] = chr3;
+ }
+
+ }
+
+ return output;
+};
+
+},{"./support":30,"./utils":32}],2:[function(require,module,exports){
+'use strict';
+
+var external = require("./external");
+var DataWorker = require('./stream/DataWorker');
+var DataLengthProbe = require('./stream/DataLengthProbe');
+var Crc32Probe = require('./stream/Crc32Probe');
+var DataLengthProbe = require('./stream/DataLengthProbe');
+
+/**
+ * Represent a compressed object, with everything needed to decompress it.
+ * @constructor
+ * @param {number} compressedSize the size of the data compressed.
+ * @param {number} uncompressedSize the size of the data after decompression.
+ * @param {number} crc32 the crc32 of the decompressed file.
+ * @param {object} compression the type of compression, see lib/compressions.js.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data.
+ */
+function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) {
+ this.compressedSize = compressedSize;
+ this.uncompressedSize = uncompressedSize;
+ this.crc32 = crc32;
+ this.compression = compression;
+ this.compressedContent = data;
+}
+
+CompressedObject.prototype = {
+ /**
+ * Create a worker to get the uncompressed content.
+ * @return {GenericWorker} the worker.
+ */
+ getContentWorker : function () {
+ var worker = new DataWorker(external.Promise.resolve(this.compressedContent))
+ .pipe(this.compression.uncompressWorker())
+ .pipe(new DataLengthProbe("data_length"));
+
+ var that = this;
+ worker.on("end", function () {
+ if(this.streamInfo['data_length'] !== that.uncompressedSize) {
+ throw new Error("Bug : uncompressed data size mismatch");
+ }
+ });
+ return worker;
+ },
+ /**
+ * Create a worker to get the compressed content.
+ * @return {GenericWorker} the worker.
+ */
+ getCompressedWorker : function () {
+ return new DataWorker(external.Promise.resolve(this.compressedContent))
+ .withStreamInfo("compressedSize", this.compressedSize)
+ .withStreamInfo("uncompressedSize", this.uncompressedSize)
+ .withStreamInfo("crc32", this.crc32)
+ .withStreamInfo("compression", this.compression)
+ ;
+ }
+};
+
+/**
+ * Chain the given worker with other workers to compress the content with the
+ * given compresion.
+ * @param {GenericWorker} uncompressedWorker the worker to pipe.
+ * @param {Object} compression the compression object.
+ * @param {Object} compressionOptions the options to use when compressing.
+ * @return {GenericWorker} the new worker compressing the content.
+ */
+CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) {
+ return uncompressedWorker
+ .pipe(new Crc32Probe())
+ .pipe(new DataLengthProbe("uncompressedSize"))
+ .pipe(compression.compressWorker(compressionOptions))
+ .pipe(new DataLengthProbe("compressedSize"))
+ .withStreamInfo("compression", compression);
+};
+
+module.exports = CompressedObject;
+
+},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(require,module,exports){
+'use strict';
+
+var GenericWorker = require("./stream/GenericWorker");
+
+exports.STORE = {
+ magic: "\x00\x00",
+ compressWorker : function (compressionOptions) {
+ return new GenericWorker("STORE compression");
+ },
+ uncompressWorker : function () {
+ return new GenericWorker("STORE decompression");
+ }
+};
+exports.DEFLATE = require('./flate');
+
+},{"./flate":7,"./stream/GenericWorker":28}],4:[function(require,module,exports){
+'use strict';
+
+var utils = require('./utils');
+
+/**
+ * The following functions come from pako, from pako/lib/zlib/crc32.js
+ * released under the MIT license, see pako https://github.com/nodeca/pako/
+ */
+
+// Use ordinary array, since untyped makes no boost here
+function makeTable() {
+ var c, table = [];
+
+ for(var n =0; n < 256; n++){
+ c = n;
+ for(var k =0; k < 8; k++){
+ c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
+ }
+ table[n] = c;
+ }
+
+ return table;
+}
+
+// Create table on load. Just 255 signed longs. Not a problem.
+var crcTable = makeTable();
+
+
+function crc32(crc, buf, len, pos) {
+ var t = crcTable, end = pos + len;
+
+ crc = crc ^ (-1);
+
+ for (var i = pos; i < end; i++ ) {
+ crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
+ }
+
+ return (crc ^ (-1)); // >>> 0;
+}
+
+// That's all for the pako functions.
+
+/**
+ * Compute the crc32 of a string.
+ * This is almost the same as the function crc32, but for strings. Using the
+ * same function for the two use cases leads to horrible performances.
+ * @param {Number} crc the starting value of the crc.
+ * @param {String} str the string to use.
+ * @param {Number} len the length of the string.
+ * @param {Number} pos the starting position for the crc32 computation.
+ * @return {Number} the computed crc32.
+ */
+function crc32str(crc, str, len, pos) {
+ var t = crcTable, end = pos + len;
+
+ crc = crc ^ (-1);
+
+ for (var i = pos; i < end; i++ ) {
+ crc = (crc >>> 8) ^ t[(crc ^ str.charCodeAt(i)) & 0xFF];
+ }
+
+ return (crc ^ (-1)); // >>> 0;
+}
+
+module.exports = function crc32wrapper(input, crc) {
+ if (typeof input === "undefined" || !input.length) {
+ return 0;
+ }
+
+ var isArray = utils.getTypeOf(input) !== "string";
+
+ if(isArray) {
+ return crc32(crc|0, input, input.length, 0);
+ } else {
+ return crc32str(crc|0, input, input.length, 0);
+ }
+};
+
+},{"./utils":32}],5:[function(require,module,exports){
+'use strict';
+exports.base64 = false;
+exports.binary = false;
+exports.dir = false;
+exports.createFolders = true;
+exports.date = null;
+exports.compression = null;
+exports.compressionOptions = null;
+exports.comment = null;
+exports.unixPermissions = null;
+exports.dosPermissions = null;
+
+},{}],6:[function(require,module,exports){
+/* global Promise */
+'use strict';
+
+// load the global object first:
+// - it should be better integrated in the system (unhandledRejection in node)
+// - the environment may have a custom Promise implementation (see zone.js)
+var ES6Promise = null;
+if (typeof Promise !== "undefined") {
+ ES6Promise = Promise;
+} else {
+ ES6Promise = require("lie");
+}
+
+/**
+ * Let the user use/change some implementations.
+ */
+module.exports = {
+ Promise: ES6Promise
+};
+
+},{"lie":37}],7:[function(require,module,exports){
+'use strict';
+var USE_TYPEDARRAY = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Uint32Array !== 'undefined');
+
+var pako = require("pako");
+var utils = require("./utils");
+var GenericWorker = require("./stream/GenericWorker");
+
+var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array";
+
+exports.magic = "\x08\x00";
+
+/**
+ * Create a worker that uses pako to inflate/deflate.
+ * @constructor
+ * @param {String} action the name of the pako function to call : either "Deflate" or "Inflate".
+ * @param {Object} options the options to use when (de)compressing.
+ */
+function FlateWorker(action, options) {
+ GenericWorker.call(this, "FlateWorker/" + action);
+
+ this._pako = null;
+ this._pakoAction = action;
+ this._pakoOptions = options;
+ // the `meta` object from the last chunk received
+ // this allow this worker to pass around metadata
+ this.meta = {};
+}
+
+utils.inherits(FlateWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+FlateWorker.prototype.processChunk = function (chunk) {
+ this.meta = chunk.meta;
+ if (this._pako === null) {
+ this._createPako();
+ }
+ this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false);
+};
+
+/**
+ * @see GenericWorker.flush
+ */
+FlateWorker.prototype.flush = function () {
+ GenericWorker.prototype.flush.call(this);
+ if (this._pako === null) {
+ this._createPako();
+ }
+ this._pako.push([], true);
+};
+/**
+ * @see GenericWorker.cleanUp
+ */
+FlateWorker.prototype.cleanUp = function () {
+ GenericWorker.prototype.cleanUp.call(this);
+ this._pako = null;
+};
+
+/**
+ * Create the _pako object.
+ * TODO: lazy-loading this object isn't the best solution but it's the
+ * quickest. The best solution is to lazy-load the worker list. See also the
+ * issue #446.
+ */
+FlateWorker.prototype._createPako = function () {
+ this._pako = new pako[this._pakoAction]({
+ raw: true,
+ level: this._pakoOptions.level || -1 // default compression
+ });
+ var self = this;
+ this._pako.onData = function(data) {
+ self.push({
+ data : data,
+ meta : self.meta
+ });
+ };
+};
+
+exports.compressWorker = function (compressionOptions) {
+ return new FlateWorker("Deflate", compressionOptions);
+};
+exports.uncompressWorker = function () {
+ return new FlateWorker("Inflate", {});
+};
+
+},{"./stream/GenericWorker":28,"./utils":32,"pako":38}],8:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var GenericWorker = require('../stream/GenericWorker');
+var utf8 = require('../utf8');
+var crc32 = require('../crc32');
+var signature = require('../signature');
+
+/**
+ * Transform an integer into a string in hexadecimal.
+ * @private
+ * @param {number} dec the number to convert.
+ * @param {number} bytes the number of bytes to generate.
+ * @returns {string} the result.
+ */
+var decToHex = function(dec, bytes) {
+ var hex = "", i;
+ for (i = 0; i < bytes; i++) {
+ hex += String.fromCharCode(dec & 0xff);
+ dec = dec >>> 8;
+ }
+ return hex;
+};
+
+/**
+ * Generate the UNIX part of the external file attributes.
+ * @param {Object} unixPermissions the unix permissions or null.
+ * @param {Boolean} isDir true if the entry is a directory, false otherwise.
+ * @return {Number} a 32 bit integer.
+ *
+ * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute :
+ *
+ * TTTTsstrwxrwxrwx0000000000ADVSHR
+ * ^^^^____________________________ file type, see zipinfo.c (UNX_*)
+ * ^^^_________________________ setuid, setgid, sticky
+ * ^^^^^^^^^________________ permissions
+ * ^^^^^^^^^^______ not used ?
+ * ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only
+ */
+var generateUnixExternalFileAttr = function (unixPermissions, isDir) {
+
+ var result = unixPermissions;
+ if (!unixPermissions) {
+ // I can't use octal values in strict mode, hence the hexa.
+ // 040775 => 0x41fd
+ // 0100664 => 0x81b4
+ result = isDir ? 0x41fd : 0x81b4;
+ }
+ return (result & 0xFFFF) << 16;
+};
+
+/**
+ * Generate the DOS part of the external file attributes.
+ * @param {Object} dosPermissions the dos permissions or null.
+ * @param {Boolean} isDir true if the entry is a directory, false otherwise.
+ * @return {Number} a 32 bit integer.
+ *
+ * Bit 0 Read-Only
+ * Bit 1 Hidden
+ * Bit 2 System
+ * Bit 3 Volume Label
+ * Bit 4 Directory
+ * Bit 5 Archive
+ */
+var generateDosExternalFileAttr = function (dosPermissions, isDir) {
+
+ // the dir flag is already set for compatibility
+ return (dosPermissions || 0) & 0x3F;
+};
+
+/**
+ * Generate the various parts used in the construction of the final zip file.
+ * @param {Object} streamInfo the hash with informations about the compressed file.
+ * @param {Boolean} streamedContent is the content streamed ?
+ * @param {Boolean} streamingEnded is the stream finished ?
+ * @param {number} offset the current offset from the start of the zip file.
+ * @param {String} platform let's pretend we are this platform (change platform dependents fields)
+ * @param {Function} encodeFileName the function to encode the file name / comment.
+ * @return {Object} the zip parts.
+ */
+var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) {
+ var file = streamInfo['file'],
+ compression = streamInfo['compression'],
+ useCustomEncoding = encodeFileName !== utf8.utf8encode,
+ encodedFileName = utils.transformTo("string", encodeFileName(file.name)),
+ utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)),
+ comment = file.comment,
+ encodedComment = utils.transformTo("string", encodeFileName(comment)),
+ utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)),
+ useUTF8ForFileName = utfEncodedFileName.length !== file.name.length,
+ useUTF8ForComment = utfEncodedComment.length !== comment.length,
+ dosTime,
+ dosDate,
+ extraFields = "",
+ unicodePathExtraField = "",
+ unicodeCommentExtraField = "",
+ dir = file.dir,
+ date = file.date;
+
+
+ var dataInfo = {
+ crc32 : 0,
+ compressedSize : 0,
+ uncompressedSize : 0
+ };
+
+ // if the content is streamed, the sizes/crc32 are only available AFTER
+ // the end of the stream.
+ if (!streamedContent || streamingEnded) {
+ dataInfo.crc32 = streamInfo['crc32'];
+ dataInfo.compressedSize = streamInfo['compressedSize'];
+ dataInfo.uncompressedSize = streamInfo['uncompressedSize'];
+ }
+
+ var bitflag = 0;
+ if (streamedContent) {
+ // Bit 3: the sizes/crc32 are set to zero in the local header.
+ // The correct values are put in the data descriptor immediately
+ // following the compressed data.
+ bitflag |= 0x0008;
+ }
+ if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) {
+ // Bit 11: Language encoding flag (EFS).
+ bitflag |= 0x0800;
+ }
+
+
+ var extFileAttr = 0;
+ var versionMadeBy = 0;
+ if (dir) {
+ // dos or unix, we set the dos dir flag
+ extFileAttr |= 0x00010;
+ }
+ if(platform === "UNIX") {
+ versionMadeBy = 0x031E; // UNIX, version 3.0
+ extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir);
+ } else { // DOS or other, fallback to DOS
+ versionMadeBy = 0x0014; // DOS, version 2.0
+ extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir);
+ }
+
+ // date
+ // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
+ // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
+ // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html
+
+ dosTime = date.getUTCHours();
+ dosTime = dosTime << 6;
+ dosTime = dosTime | date.getUTCMinutes();
+ dosTime = dosTime << 5;
+ dosTime = dosTime | date.getUTCSeconds() / 2;
+
+ dosDate = date.getUTCFullYear() - 1980;
+ dosDate = dosDate << 4;
+ dosDate = dosDate | (date.getUTCMonth() + 1);
+ dosDate = dosDate << 5;
+ dosDate = dosDate | date.getUTCDate();
+
+ if (useUTF8ForFileName) {
+ // set the unicode path extra field. unzip needs at least one extra
+ // field to correctly handle unicode path, so using the path is as good
+ // as any other information. This could improve the situation with
+ // other archive managers too.
+ // This field is usually used without the utf8 flag, with a non
+ // unicode path in the header (winrar, winzip). This helps (a bit)
+ // with the messy Windows' default compressed folders feature but
+ // breaks on p7zip which doesn't seek the unicode path extra field.
+ // So for now, UTF-8 everywhere !
+ unicodePathExtraField =
+ // Version
+ decToHex(1, 1) +
+ // NameCRC32
+ decToHex(crc32(encodedFileName), 4) +
+ // UnicodeName
+ utfEncodedFileName;
+
+ extraFields +=
+ // Info-ZIP Unicode Path Extra Field
+ "\x75\x70" +
+ // size
+ decToHex(unicodePathExtraField.length, 2) +
+ // content
+ unicodePathExtraField;
+ }
+
+ if(useUTF8ForComment) {
+
+ unicodeCommentExtraField =
+ // Version
+ decToHex(1, 1) +
+ // CommentCRC32
+ decToHex(crc32(encodedComment), 4) +
+ // UnicodeName
+ utfEncodedComment;
+
+ extraFields +=
+ // Info-ZIP Unicode Path Extra Field
+ "\x75\x63" +
+ // size
+ decToHex(unicodeCommentExtraField.length, 2) +
+ // content
+ unicodeCommentExtraField;
+ }
+
+ var header = "";
+
+ // version needed to extract
+ header += "\x0A\x00";
+ // general purpose bit flag
+ header += decToHex(bitflag, 2);
+ // compression method
+ header += compression.magic;
+ // last mod file time
+ header += decToHex(dosTime, 2);
+ // last mod file date
+ header += decToHex(dosDate, 2);
+ // crc-32
+ header += decToHex(dataInfo.crc32, 4);
+ // compressed size
+ header += decToHex(dataInfo.compressedSize, 4);
+ // uncompressed size
+ header += decToHex(dataInfo.uncompressedSize, 4);
+ // file name length
+ header += decToHex(encodedFileName.length, 2);
+ // extra field length
+ header += decToHex(extraFields.length, 2);
+
+
+ var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields;
+
+ var dirRecord = signature.CENTRAL_FILE_HEADER +
+ // version made by (00: DOS)
+ decToHex(versionMadeBy, 2) +
+ // file header (common to file and central directory)
+ header +
+ // file comment length
+ decToHex(encodedComment.length, 2) +
+ // disk number start
+ "\x00\x00" +
+ // internal file attributes TODO
+ "\x00\x00" +
+ // external file attributes
+ decToHex(extFileAttr, 4) +
+ // relative offset of local header
+ decToHex(offset, 4) +
+ // file name
+ encodedFileName +
+ // extra field
+ extraFields +
+ // file comment
+ encodedComment;
+
+ return {
+ fileRecord: fileRecord,
+ dirRecord: dirRecord
+ };
+};
+
+/**
+ * Generate the EOCD record.
+ * @param {Number} entriesCount the number of entries in the zip file.
+ * @param {Number} centralDirLength the length (in bytes) of the central dir.
+ * @param {Number} localDirLength the length (in bytes) of the local dir.
+ * @param {String} comment the zip file comment as a binary string.
+ * @param {Function} encodeFileName the function to encode the comment.
+ * @return {String} the EOCD record.
+ */
+var generateCentralDirectoryEnd = function (entriesCount, centralDirLength, localDirLength, comment, encodeFileName) {
+ var dirEnd = "";
+ var encodedComment = utils.transformTo("string", encodeFileName(comment));
+
+ // end of central dir signature
+ dirEnd = signature.CENTRAL_DIRECTORY_END +
+ // number of this disk
+ "\x00\x00" +
+ // number of the disk with the start of the central directory
+ "\x00\x00" +
+ // total number of entries in the central directory on this disk
+ decToHex(entriesCount, 2) +
+ // total number of entries in the central directory
+ decToHex(entriesCount, 2) +
+ // size of the central directory 4 bytes
+ decToHex(centralDirLength, 4) +
+ // offset of start of central directory with respect to the starting disk number
+ decToHex(localDirLength, 4) +
+ // .ZIP file comment length
+ decToHex(encodedComment.length, 2) +
+ // .ZIP file comment
+ encodedComment;
+
+ return dirEnd;
+};
+
+/**
+ * Generate data descriptors for a file entry.
+ * @param {Object} streamInfo the hash generated by a worker, containing informations
+ * on the file entry.
+ * @return {String} the data descriptors.
+ */
+var generateDataDescriptors = function (streamInfo) {
+ var descriptor = "";
+ descriptor = signature.DATA_DESCRIPTOR +
+ // crc-32 4 bytes
+ decToHex(streamInfo['crc32'], 4) +
+ // compressed size 4 bytes
+ decToHex(streamInfo['compressedSize'], 4) +
+ // uncompressed size 4 bytes
+ decToHex(streamInfo['uncompressedSize'], 4);
+
+ return descriptor;
+};
+
+
+/**
+ * A worker to concatenate other workers to create a zip file.
+ * @param {Boolean} streamFiles `true` to stream the content of the files,
+ * `false` to accumulate it.
+ * @param {String} comment the comment to use.
+ * @param {String} platform the platform to use, "UNIX" or "DOS".
+ * @param {Function} encodeFileName the function to encode file names and comments.
+ */
+function ZipFileWorker(streamFiles, comment, platform, encodeFileName) {
+ GenericWorker.call(this, "ZipFileWorker");
+ // The number of bytes written so far. This doesn't count accumulated chunks.
+ this.bytesWritten = 0;
+ // The comment of the zip file
+ this.zipComment = comment;
+ // The platform "generating" the zip file.
+ this.zipPlatform = platform;
+ // the function to encode file names and comments.
+ this.encodeFileName = encodeFileName;
+ // Should we stream the content of the files ?
+ this.streamFiles = streamFiles;
+ // If `streamFiles` is false, we will need to accumulate the content of the
+ // files to calculate sizes / crc32 (and write them *before* the content).
+ // This boolean indicates if we are accumulating chunks (it will change a lot
+ // during the lifetime of this worker).
+ this.accumulate = false;
+ // The buffer receiving chunks when accumulating content.
+ this.contentBuffer = [];
+ // The list of generated directory records.
+ this.dirRecords = [];
+ // The offset (in bytes) from the beginning of the zip file for the current source.
+ this.currentSourceOffset = 0;
+ // The total number of entries in this zip file.
+ this.entriesCount = 0;
+ // the name of the file currently being added, null when handling the end of the zip file.
+ // Used for the emited metadata.
+ this.currentFile = null;
+
+
+
+ this._sources = [];
+}
+utils.inherits(ZipFileWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.push
+ */
+ZipFileWorker.prototype.push = function (chunk) {
+
+ var currentFilePercent = chunk.meta.percent || 0;
+ var entriesCount = this.entriesCount;
+ var remainingFiles = this._sources.length;
+
+ if(this.accumulate) {
+ this.contentBuffer.push(chunk);
+ } else {
+ this.bytesWritten += chunk.data.length;
+
+ GenericWorker.prototype.push.call(this, {
+ data : chunk.data,
+ meta : {
+ currentFile : this.currentFile,
+ percent : entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100
+ }
+ });
+ }
+};
+
+/**
+ * The worker started a new source (an other worker).
+ * @param {Object} streamInfo the streamInfo object from the new source.
+ */
+ZipFileWorker.prototype.openedSource = function (streamInfo) {
+ this.currentSourceOffset = this.bytesWritten;
+ this.currentFile = streamInfo['file'].name;
+
+ var streamedContent = this.streamFiles && !streamInfo['file'].dir;
+
+ // don't stream folders (because they don't have any content)
+ if(streamedContent) {
+ var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);
+ this.push({
+ data : record.fileRecord,
+ meta : {percent:0}
+ });
+ } else {
+ // we need to wait for the whole file before pushing anything
+ this.accumulate = true;
+ }
+};
+
+/**
+ * The worker finished a source (an other worker).
+ * @param {Object} streamInfo the streamInfo object from the finished source.
+ */
+ZipFileWorker.prototype.closedSource = function (streamInfo) {
+ this.accumulate = false;
+ var streamedContent = this.streamFiles && !streamInfo['file'].dir;
+ var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);
+
+ this.dirRecords.push(record.dirRecord);
+ if(streamedContent) {
+ // after the streamed file, we put data descriptors
+ this.push({
+ data : generateDataDescriptors(streamInfo),
+ meta : {percent:100}
+ });
+ } else {
+ // the content wasn't streamed, we need to push everything now
+ // first the file record, then the content
+ this.push({
+ data : record.fileRecord,
+ meta : {percent:0}
+ });
+ while(this.contentBuffer.length) {
+ this.push(this.contentBuffer.shift());
+ }
+ }
+ this.currentFile = null;
+};
+
+/**
+ * @see GenericWorker.flush
+ */
+ZipFileWorker.prototype.flush = function () {
+
+ var localDirLength = this.bytesWritten;
+ for(var i = 0; i < this.dirRecords.length; i++) {
+ this.push({
+ data : this.dirRecords[i],
+ meta : {percent:100}
+ });
+ }
+ var centralDirLength = this.bytesWritten - localDirLength;
+
+ var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName);
+
+ this.push({
+ data : dirEnd,
+ meta : {percent:100}
+ });
+};
+
+/**
+ * Prepare the next source to be read.
+ */
+ZipFileWorker.prototype.prepareNextSource = function () {
+ this.previous = this._sources.shift();
+ this.openedSource(this.previous.streamInfo);
+ if (this.isPaused) {
+ this.previous.pause();
+ } else {
+ this.previous.resume();
+ }
+};
+
+/**
+ * @see GenericWorker.registerPrevious
+ */
+ZipFileWorker.prototype.registerPrevious = function (previous) {
+ this._sources.push(previous);
+ var self = this;
+
+ previous.on('data', function (chunk) {
+ self.processChunk(chunk);
+ });
+ previous.on('end', function () {
+ self.closedSource(self.previous.streamInfo);
+ if(self._sources.length) {
+ self.prepareNextSource();
+ } else {
+ self.end();
+ }
+ });
+ previous.on('error', function (e) {
+ self.error(e);
+ });
+ return this;
+};
+
+/**
+ * @see GenericWorker.resume
+ */
+ZipFileWorker.prototype.resume = function () {
+ if(!GenericWorker.prototype.resume.call(this)) {
+ return false;
+ }
+
+ if (!this.previous && this._sources.length) {
+ this.prepareNextSource();
+ return true;
+ }
+ if (!this.previous && !this._sources.length && !this.generatedError) {
+ this.end();
+ return true;
+ }
+};
+
+/**
+ * @see GenericWorker.error
+ */
+ZipFileWorker.prototype.error = function (e) {
+ var sources = this._sources;
+ if(!GenericWorker.prototype.error.call(this, e)) {
+ return false;
+ }
+ for(var i = 0; i < sources.length; i++) {
+ try {
+ sources[i].error(e);
+ } catch(e) {
+ // the `error` exploded, nothing to do
+ }
+ }
+ return true;
+};
+
+/**
+ * @see GenericWorker.lock
+ */
+ZipFileWorker.prototype.lock = function () {
+ GenericWorker.prototype.lock.call(this);
+ var sources = this._sources;
+ for(var i = 0; i < sources.length; i++) {
+ sources[i].lock();
+ }
+};
+
+module.exports = ZipFileWorker;
+
+},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(require,module,exports){
+'use strict';
+
+var compressions = require('../compressions');
+var ZipFileWorker = require('./ZipFileWorker');
+
+/**
+ * Find the compression to use.
+ * @param {String} fileCompression the compression defined at the file level, if any.
+ * @param {String} zipCompression the compression defined at the load() level.
+ * @return {Object} the compression object to use.
+ */
+var getCompression = function (fileCompression, zipCompression) {
+
+ var compressionName = fileCompression || zipCompression;
+ var compression = compressions[compressionName];
+ if (!compression) {
+ throw new Error(compressionName + " is not a valid compression method !");
+ }
+ return compression;
+};
+
+/**
+ * Create a worker to generate a zip file.
+ * @param {JSZip} zip the JSZip instance at the right root level.
+ * @param {Object} options to generate the zip file.
+ * @param {String} comment the comment to use.
+ */
+exports.generateWorker = function (zip, options, comment) {
+
+ var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName);
+ var entriesCount = 0;
+ try {
+
+ zip.forEach(function (relativePath, file) {
+ entriesCount++;
+ var compression = getCompression(file.options.compression, options.compression);
+ var compressionOptions = file.options.compressionOptions || options.compressionOptions || {};
+ var dir = file.dir, date = file.date;
+
+ file._compressWorker(compression, compressionOptions)
+ .withStreamInfo("file", {
+ name : relativePath,
+ dir : dir,
+ date : date,
+ comment : file.comment || "",
+ unixPermissions : file.unixPermissions,
+ dosPermissions : file.dosPermissions
+ })
+ .pipe(zipFileWorker);
+ });
+ zipFileWorker.entriesCount = entriesCount;
+ } catch (e) {
+ zipFileWorker.error(e);
+ }
+
+ return zipFileWorker;
+};
+
+},{"../compressions":3,"./ZipFileWorker":8}],10:[function(require,module,exports){
+'use strict';
+
+/**
+ * Representation a of zip file in js
+ * @constructor
+ */
+function JSZip() {
+ // if this constructor is used without `new`, it adds `new` before itself:
+ if(!(this instanceof JSZip)) {
+ return new JSZip();
+ }
+
+ if(arguments.length) {
+ throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
+ }
+
+ // object containing the files :
+ // {
+ // "folder/" : {...},
+ // "folder/data.txt" : {...}
+ // }
+ this.files = {};
+
+ this.comment = null;
+
+ // Where we are in the hierarchy
+ this.root = "";
+ this.clone = function() {
+ var newObj = new JSZip();
+ for (var i in this) {
+ if (typeof this[i] !== "function") {
+ newObj[i] = this[i];
+ }
+ }
+ return newObj;
+ };
+}
+JSZip.prototype = require('./object');
+JSZip.prototype.loadAsync = require('./load');
+JSZip.support = require('./support');
+JSZip.defaults = require('./defaults');
+
+// TODO find a better way to handle this version,
+// a require('package.json').version doesn't work with webpack, see #327
+JSZip.version = "3.2.0";
+
+JSZip.loadAsync = function (content, options) {
+ return new JSZip().loadAsync(content, options);
+};
+
+JSZip.external = require("./external");
+module.exports = JSZip;
+
+},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(require,module,exports){
+'use strict';
+var utils = require('./utils');
+var external = require("./external");
+var utf8 = require('./utf8');
+var utils = require('./utils');
+var ZipEntries = require('./zipEntries');
+var Crc32Probe = require('./stream/Crc32Probe');
+var nodejsUtils = require("./nodejsUtils");
+
+/**
+ * Check the CRC32 of an entry.
+ * @param {ZipEntry} zipEntry the zip entry to check.
+ * @return {Promise} the result.
+ */
+function checkEntryCRC32(zipEntry) {
+ return new external.Promise(function (resolve, reject) {
+ var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe());
+ worker.on("error", function (e) {
+ reject(e);
+ })
+ .on("end", function () {
+ if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) {
+ reject(new Error("Corrupted zip : CRC32 mismatch"));
+ } else {
+ resolve();
+ }
+ })
+ .resume();
+ });
+}
+
+module.exports = function(data, options) {
+ var zip = this;
+ options = utils.extend(options || {}, {
+ base64: false,
+ checkCRC32: false,
+ optimizedBinaryString: false,
+ createFolders: false,
+ decodeFileName: utf8.utf8decode
+ });
+
+ if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
+ return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file."));
+ }
+
+ return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64)
+ .then(function(data) {
+ var zipEntries = new ZipEntries(options);
+ zipEntries.load(data);
+ return zipEntries;
+ }).then(function checkCRC32(zipEntries) {
+ var promises = [external.Promise.resolve(zipEntries)];
+ var files = zipEntries.files;
+ if (options.checkCRC32) {
+ for (var i = 0; i < files.length; i++) {
+ promises.push(checkEntryCRC32(files[i]));
+ }
+ }
+ return external.Promise.all(promises);
+ }).then(function addFiles(results) {
+ var zipEntries = results.shift();
+ var files = zipEntries.files;
+ for (var i = 0; i < files.length; i++) {
+ var input = files[i];
+ zip.file(input.fileNameStr, input.decompressed, {
+ binary: true,
+ optimizedBinaryString: true,
+ date: input.date,
+ dir: input.dir,
+ comment : input.fileCommentStr.length ? input.fileCommentStr : null,
+ unixPermissions : input.unixPermissions,
+ dosPermissions : input.dosPermissions,
+ createFolders: options.createFolders
+ });
+ }
+ if (zipEntries.zipComment.length) {
+ zip.comment = zipEntries.zipComment;
+ }
+
+ return zip;
+ });
+};
+
+},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(require,module,exports){
+"use strict";
+
+var utils = require('../utils');
+var GenericWorker = require('../stream/GenericWorker');
+
+/**
+ * A worker that use a nodejs stream as source.
+ * @constructor
+ * @param {String} filename the name of the file entry for this stream.
+ * @param {Readable} stream the nodejs stream.
+ */
+function NodejsStreamInputAdapter(filename, stream) {
+ GenericWorker.call(this, "Nodejs stream input adapter for " + filename);
+ this._upstreamEnded = false;
+ this._bindStream(stream);
+}
+
+utils.inherits(NodejsStreamInputAdapter, GenericWorker);
+
+/**
+ * Prepare the stream and bind the callbacks on it.
+ * Do this ASAP on node 0.10 ! A lazy binding doesn't always work.
+ * @param {Stream} stream the nodejs stream to use.
+ */
+NodejsStreamInputAdapter.prototype._bindStream = function (stream) {
+ var self = this;
+ this._stream = stream;
+ stream.pause();
+ stream
+ .on("data", function (chunk) {
+ self.push({
+ data: chunk,
+ meta : {
+ percent : 0
+ }
+ });
+ })
+ .on("error", function (e) {
+ if(self.isPaused) {
+ this.generatedError = e;
+ } else {
+ self.error(e);
+ }
+ })
+ .on("end", function () {
+ if(self.isPaused) {
+ self._upstreamEnded = true;
+ } else {
+ self.end();
+ }
+ });
+};
+NodejsStreamInputAdapter.prototype.pause = function () {
+ if(!GenericWorker.prototype.pause.call(this)) {
+ return false;
+ }
+ this._stream.pause();
+ return true;
+};
+NodejsStreamInputAdapter.prototype.resume = function () {
+ if(!GenericWorker.prototype.resume.call(this)) {
+ return false;
+ }
+
+ if(this._upstreamEnded) {
+ this.end();
+ } else {
+ this._stream.resume();
+ }
+
+ return true;
+};
+
+module.exports = NodejsStreamInputAdapter;
+
+},{"../stream/GenericWorker":28,"../utils":32}],13:[function(require,module,exports){
+'use strict';
+
+var Readable = require('readable-stream').Readable;
+
+var utils = require('../utils');
+utils.inherits(NodejsStreamOutputAdapter, Readable);
+
+/**
+* A nodejs stream using a worker as source.
+* @see the SourceWrapper in http://nodejs.org/api/stream.html
+* @constructor
+* @param {StreamHelper} helper the helper wrapping the worker
+* @param {Object} options the nodejs stream options
+* @param {Function} updateCb the update callback.
+*/
+function NodejsStreamOutputAdapter(helper, options, updateCb) {
+ Readable.call(this, options);
+ this._helper = helper;
+
+ var self = this;
+ helper.on("data", function (data, meta) {
+ if (!self.push(data)) {
+ self._helper.pause();
+ }
+ if(updateCb) {
+ updateCb(meta);
+ }
+ })
+ .on("error", function(e) {
+ self.emit('error', e);
+ })
+ .on("end", function () {
+ self.push(null);
+ });
+}
+
+
+NodejsStreamOutputAdapter.prototype._read = function() {
+ this._helper.resume();
+};
+
+module.exports = NodejsStreamOutputAdapter;
+
+},{"../utils":32,"readable-stream":16}],14:[function(require,module,exports){
+'use strict';
+
+module.exports = {
+ /**
+ * True if this is running in Nodejs, will be undefined in a browser.
+ * In a browser, browserify won't include this file and the whole module
+ * will be resolved an empty object.
+ */
+ isNode : typeof Buffer !== "undefined",
+ /**
+ * Create a new nodejs Buffer from an existing content.
+ * @param {Object} data the data to pass to the constructor.
+ * @param {String} encoding the encoding to use.
+ * @return {Buffer} a new Buffer.
+ */
+ newBufferFrom: function(data, encoding) {
+ if (Buffer.from && Buffer.from !== Uint8Array.from) {
+ return Buffer.from(data, encoding);
+ } else {
+ if (typeof data === "number") {
+ // Safeguard for old Node.js versions. On newer versions,
+ // Buffer.from(number) / Buffer(number, encoding) already throw.
+ throw new Error("The \"data\" argument must not be a number");
+ }
+ return new Buffer(data, encoding);
+ }
+ },
+ /**
+ * Create a new nodejs Buffer with the specified size.
+ * @param {Integer} size the size of the buffer.
+ * @return {Buffer} a new Buffer.
+ */
+ allocBuffer: function (size) {
+ if (Buffer.alloc) {
+ return Buffer.alloc(size);
+ } else {
+ var buf = new Buffer(size);
+ buf.fill(0);
+ return buf;
+ }
+ },
+ /**
+ * Find out if an object is a Buffer.
+ * @param {Object} b the object to test.
+ * @return {Boolean} true if the object is a Buffer, false otherwise.
+ */
+ isBuffer : function(b){
+ return Buffer.isBuffer(b);
+ },
+
+ isStream : function (obj) {
+ return obj &&
+ typeof obj.on === "function" &&
+ typeof obj.pause === "function" &&
+ typeof obj.resume === "function";
+ }
+};
+
+},{}],15:[function(require,module,exports){
+'use strict';
+var utf8 = require('./utf8');
+var utils = require('./utils');
+var GenericWorker = require('./stream/GenericWorker');
+var StreamHelper = require('./stream/StreamHelper');
+var defaults = require('./defaults');
+var CompressedObject = require('./compressedObject');
+var ZipObject = require('./zipObject');
+var generate = require("./generate");
+var nodejsUtils = require("./nodejsUtils");
+var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter");
+
+
+/**
+ * Add a file in the current folder.
+ * @private
+ * @param {string} name the name of the file
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
+ * @param {Object} originalOptions the options of the file
+ * @return {Object} the new file.
+ */
+var fileAdd = function(name, data, originalOptions) {
+ // be sure sub folders exist
+ var dataType = utils.getTypeOf(data),
+ parent;
+
+
+ /*
+ * Correct options.
+ */
+
+ var o = utils.extend(originalOptions || {}, defaults);
+ o.date = o.date || new Date();
+ if (o.compression !== null) {
+ o.compression = o.compression.toUpperCase();
+ }
+
+ if (typeof o.unixPermissions === "string") {
+ o.unixPermissions = parseInt(o.unixPermissions, 8);
+ }
+
+ // UNX_IFDIR 0040000 see zipinfo.c
+ if (o.unixPermissions && (o.unixPermissions & 0x4000)) {
+ o.dir = true;
+ }
+ // Bit 4 Directory
+ if (o.dosPermissions && (o.dosPermissions & 0x0010)) {
+ o.dir = true;
+ }
+
+ if (o.dir) {
+ name = forceTrailingSlash(name);
+ }
+ if (o.createFolders && (parent = parentFolder(name))) {
+ folderAdd.call(this, parent, true);
+ }
+
+ var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false;
+ if (!originalOptions || typeof originalOptions.binary === "undefined") {
+ o.binary = !isUnicodeString;
+ }
+
+
+ var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0;
+
+ if (isCompressedEmpty || o.dir || !data || data.length === 0) {
+ o.base64 = false;
+ o.binary = true;
+ data = "";
+ o.compression = "STORE";
+ dataType = "string";
+ }
+
+ /*
+ * Convert content to fit.
+ */
+
+ var zipObjectContent = null;
+ if (data instanceof CompressedObject || data instanceof GenericWorker) {
+ zipObjectContent = data;
+ } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
+ zipObjectContent = new NodejsStreamInputAdapter(name, data);
+ } else {
+ zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64);
+ }
+
+ var object = new ZipObject(name, zipObjectContent, o);
+ this.files[name] = object;
+ /*
+ TODO: we can't throw an exception because we have async promises
+ (we can have a promise of a Date() for example) but returning a
+ promise is useless because file(name, data) returns the JSZip
+ object for chaining. Should we break that to allow the user
+ to catch the error ?
+
+ return external.Promise.resolve(zipObjectContent)
+ .then(function () {
+ return object;
+ });
+ */
+};
+
+/**
+ * Find the parent folder of the path.
+ * @private
+ * @param {string} path the path to use
+ * @return {string} the parent folder, or ""
+ */
+var parentFolder = function (path) {
+ if (path.slice(-1) === '/') {
+ path = path.substring(0, path.length - 1);
+ }
+ var lastSlash = path.lastIndexOf('/');
+ return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
+};
+
+/**
+ * Returns the path with a slash at the end.
+ * @private
+ * @param {String} path the path to check.
+ * @return {String} the path with a trailing slash.
+ */
+var forceTrailingSlash = function(path) {
+ // Check the name ends with a /
+ if (path.slice(-1) !== "/") {
+ path += "/"; // IE doesn't like substr(-1)
+ }
+ return path;
+};
+
+/**
+ * Add a (sub) folder in the current folder.
+ * @private
+ * @param {string} name the folder's name
+ * @param {boolean=} [createFolders] If true, automatically create sub
+ * folders. Defaults to false.
+ * @return {Object} the new folder.
+ */
+var folderAdd = function(name, createFolders) {
+ createFolders = (typeof createFolders !== 'undefined') ? createFolders : defaults.createFolders;
+
+ name = forceTrailingSlash(name);
+
+ // Does this folder already exist?
+ if (!this.files[name]) {
+ fileAdd.call(this, name, null, {
+ dir: true,
+ createFolders: createFolders
+ });
+ }
+ return this.files[name];
+};
+
+/**
+* Cross-window, cross-Node-context regular expression detection
+* @param {Object} object Anything
+* @return {Boolean} true if the object is a regular expression,
+* false otherwise
+*/
+function isRegExp(object) {
+ return Object.prototype.toString.call(object) === "[object RegExp]";
+}
+
+// return the actual prototype of JSZip
+var out = {
+ /**
+ * @see loadAsync
+ */
+ load: function() {
+ throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
+ },
+
+
+ /**
+ * Call a callback function for each entry at this folder level.
+ * @param {Function} cb the callback function:
+ * function (relativePath, file) {...}
+ * It takes 2 arguments : the relative path and the file.
+ */
+ forEach: function(cb) {
+ var filename, relativePath, file;
+ for (filename in this.files) {
+ if (!this.files.hasOwnProperty(filename)) {
+ continue;
+ }
+ file = this.files[filename];
+ relativePath = filename.slice(this.root.length, filename.length);
+ if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root
+ cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn...
+ }
+ }
+ },
+
+ /**
+ * Filter nested files/folders with the specified function.
+ * @param {Function} search the predicate to use :
+ * function (relativePath, file) {...}
+ * It takes 2 arguments : the relative path and the file.
+ * @return {Array} An array of matching elements.
+ */
+ filter: function(search) {
+ var result = [];
+ this.forEach(function (relativePath, entry) {
+ if (search(relativePath, entry)) { // the file matches the function
+ result.push(entry);
+ }
+
+ });
+ return result;
+ },
+
+ /**
+ * Add a file to the zip file, or search a file.
+ * @param {string|RegExp} name The name of the file to add (if data is defined),
+ * the name of the file to find (if no data) or a regex to match files.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded
+ * @param {Object} o File options
+ * @return {JSZip|Object|Array} this JSZip object (when adding a file),
+ * a file (when searching by string) or an array of files (when searching by regex).
+ */
+ file: function(name, data, o) {
+ if (arguments.length === 1) {
+ if (isRegExp(name)) {
+ var regexp = name;
+ return this.filter(function(relativePath, file) {
+ return !file.dir && regexp.test(relativePath);
+ });
+ }
+ else { // text
+ var obj = this.files[this.root + name];
+ if (obj && !obj.dir) {
+ return obj;
+ } else {
+ return null;
+ }
+ }
+ }
+ else { // more than one argument : we have data !
+ name = this.root + name;
+ fileAdd.call(this, name, data, o);
+ }
+ return this;
+ },
+
+ /**
+ * Add a directory to the zip file, or search.
+ * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders.
+ * @return {JSZip} an object with the new directory as the root, or an array containing matching folders.
+ */
+ folder: function(arg) {
+ if (!arg) {
+ return this;
+ }
+
+ if (isRegExp(arg)) {
+ return this.filter(function(relativePath, file) {
+ return file.dir && arg.test(relativePath);
+ });
+ }
+
+ // else, name is a new folder
+ var name = this.root + arg;
+ var newFolder = folderAdd.call(this, name);
+
+ // Allow chaining by returning a new object with this folder as the root
+ var ret = this.clone();
+ ret.root = newFolder.name;
+ return ret;
+ },
+
+ /**
+ * Delete a file, or a directory and all sub-files, from the zip
+ * @param {string} name the name of the file to delete
+ * @return {JSZip} this JSZip object
+ */
+ remove: function(name) {
+ name = this.root + name;
+ var file = this.files[name];
+ if (!file) {
+ // Look for any folders
+ if (name.slice(-1) !== "/") {
+ name += "/";
+ }
+ file = this.files[name];
+ }
+
+ if (file && !file.dir) {
+ // file
+ delete this.files[name];
+ } else {
+ // maybe a folder, delete recursively
+ var kids = this.filter(function(relativePath, file) {
+ return file.name.slice(0, name.length) === name;
+ });
+ for (var i = 0; i < kids.length; i++) {
+ delete this.files[kids[i].name];
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Generate the complete zip file
+ * @param {Object} options the options to generate the zip file :
+ * - compression, "STORE" by default.
+ * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
+ * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file
+ */
+ generate: function(options) {
+ throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
+ },
+
+ /**
+ * Generate the complete zip file as an internal stream.
+ * @param {Object} options the options to generate the zip file :
+ * - compression, "STORE" by default.
+ * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
+ * @return {StreamHelper} the streamed zip file.
+ */
+ generateInternalStream: function(options) {
+ var worker, opts = {};
+ try {
+ opts = utils.extend(options || {}, {
+ streamFiles: false,
+ compression: "STORE",
+ compressionOptions : null,
+ type: "",
+ platform: "DOS",
+ comment: null,
+ mimeType: 'application/zip',
+ encodeFileName: utf8.utf8encode
+ });
+
+ opts.type = opts.type.toLowerCase();
+ opts.compression = opts.compression.toUpperCase();
+
+ // "binarystring" is prefered but the internals use "string".
+ if(opts.type === "binarystring") {
+ opts.type = "string";
+ }
+
+ if (!opts.type) {
+ throw new Error("No output type specified.");
+ }
+
+ utils.checkSupport(opts.type);
+
+ // accept nodejs `process.platform`
+ if(
+ opts.platform === 'darwin' ||
+ opts.platform === 'freebsd' ||
+ opts.platform === 'linux' ||
+ opts.platform === 'sunos'
+ ) {
+ opts.platform = "UNIX";
+ }
+ if (opts.platform === 'win32') {
+ opts.platform = "DOS";
+ }
+
+ var comment = opts.comment || this.comment || "";
+ worker = generate.generateWorker(this, opts, comment);
+ } catch (e) {
+ worker = new GenericWorker("error");
+ worker.error(e);
+ }
+ return new StreamHelper(worker, opts.type || "string", opts.mimeType);
+ },
+ /**
+ * Generate the complete zip file asynchronously.
+ * @see generateInternalStream
+ */
+ generateAsync: function(options, onUpdate) {
+ return this.generateInternalStream(options).accumulate(onUpdate);
+ },
+ /**
+ * Generate the complete zip file asynchronously.
+ * @see generateInternalStream
+ */
+ generateNodeStream: function(options, onUpdate) {
+ options = options || {};
+ if (!options.type) {
+ options.type = "nodebuffer";
+ }
+ return this.generateInternalStream(options).toNodejsStream(onUpdate);
+ }
+};
+module.exports = out;
+
+},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(require,module,exports){
+/*
+ * This file is used by module bundlers (browserify/webpack/etc) when
+ * including a stream implementation. We use "readable-stream" to get a
+ * consistent behavior between nodejs versions but bundlers often have a shim
+ * for "stream". Using this shim greatly improve the compatibility and greatly
+ * reduce the final size of the bundle (only one stream implementation, not
+ * two).
+ */
+module.exports = require("stream");
+
+},{"stream":undefined}],17:[function(require,module,exports){
+'use strict';
+var DataReader = require('./DataReader');
+var utils = require('../utils');
+
+function ArrayReader(data) {
+ DataReader.call(this, data);
+ for(var i = 0; i < this.data.length; i++) {
+ data[i] = data[i] & 0xFF;
+ }
+}
+utils.inherits(ArrayReader, DataReader);
+/**
+ * @see DataReader.byteAt
+ */
+ArrayReader.prototype.byteAt = function(i) {
+ return this.data[this.zero + i];
+};
+/**
+ * @see DataReader.lastIndexOfSignature
+ */
+ArrayReader.prototype.lastIndexOfSignature = function(sig) {
+ var sig0 = sig.charCodeAt(0),
+ sig1 = sig.charCodeAt(1),
+ sig2 = sig.charCodeAt(2),
+ sig3 = sig.charCodeAt(3);
+ for (var i = this.length - 4; i >= 0; --i) {
+ if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) {
+ return i - this.zero;
+ }
+ }
+
+ return -1;
+};
+/**
+ * @see DataReader.readAndCheckSignature
+ */
+ArrayReader.prototype.readAndCheckSignature = function (sig) {
+ var sig0 = sig.charCodeAt(0),
+ sig1 = sig.charCodeAt(1),
+ sig2 = sig.charCodeAt(2),
+ sig3 = sig.charCodeAt(3),
+ data = this.readData(4);
+ return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3];
+};
+/**
+ * @see DataReader.readData
+ */
+ArrayReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ if(size === 0) {
+ return [];
+ }
+ var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = ArrayReader;
+
+},{"../utils":32,"./DataReader":18}],18:[function(require,module,exports){
+'use strict';
+var utils = require('../utils');
+
+function DataReader(data) {
+ this.data = data; // type : see implementation
+ this.length = data.length;
+ this.index = 0;
+ this.zero = 0;
+}
+DataReader.prototype = {
+ /**
+ * Check that the offset will not go too far.
+ * @param {string} offset the additional offset to check.
+ * @throws {Error} an Error if the offset is out of bounds.
+ */
+ checkOffset: function(offset) {
+ this.checkIndex(this.index + offset);
+ },
+ /**
+ * Check that the specified index will not be too far.
+ * @param {string} newIndex the index to check.
+ * @throws {Error} an Error if the index is out of bounds.
+ */
+ checkIndex: function(newIndex) {
+ if (this.length < this.zero + newIndex || newIndex < 0) {
+ throw new Error("End of data reached (data length = " + this.length + ", asked index = " + (newIndex) + "). Corrupted zip ?");
+ }
+ },
+ /**
+ * Change the index.
+ * @param {number} newIndex The new index.
+ * @throws {Error} if the new index is out of the data.
+ */
+ setIndex: function(newIndex) {
+ this.checkIndex(newIndex);
+ this.index = newIndex;
+ },
+ /**
+ * Skip the next n bytes.
+ * @param {number} n the number of bytes to skip.
+ * @throws {Error} if the new index is out of the data.
+ */
+ skip: function(n) {
+ this.setIndex(this.index + n);
+ },
+ /**
+ * Get the byte at the specified index.
+ * @param {number} i the index to use.
+ * @return {number} a byte.
+ */
+ byteAt: function(i) {
+ // see implementations
+ },
+ /**
+ * Get the next number with a given byte size.
+ * @param {number} size the number of bytes to read.
+ * @return {number} the corresponding number.
+ */
+ readInt: function(size) {
+ var result = 0,
+ i;
+ this.checkOffset(size);
+ for (i = this.index + size - 1; i >= this.index; i--) {
+ result = (result << 8) + this.byteAt(i);
+ }
+ this.index += size;
+ return result;
+ },
+ /**
+ * Get the next string with a given byte size.
+ * @param {number} size the number of bytes to read.
+ * @return {string} the corresponding string.
+ */
+ readString: function(size) {
+ return utils.transformTo("string", this.readData(size));
+ },
+ /**
+ * Get raw data without conversion, <size> bytes.
+ * @param {number} size the number of bytes to read.
+ * @return {Object} the raw data, implementation specific.
+ */
+ readData: function(size) {
+ // see implementations
+ },
+ /**
+ * Find the last occurence of a zip signature (4 bytes).
+ * @param {string} sig the signature to find.
+ * @return {number} the index of the last occurence, -1 if not found.
+ */
+ lastIndexOfSignature: function(sig) {
+ // see implementations
+ },
+ /**
+ * Read the signature (4 bytes) at the current position and compare it with sig.
+ * @param {string} sig the expected signature
+ * @return {boolean} true if the signature matches, false otherwise.
+ */
+ readAndCheckSignature: function(sig) {
+ // see implementations
+ },
+ /**
+ * Get the next date.
+ * @return {Date} the date.
+ */
+ readDate: function() {
+ var dostime = this.readInt(4);
+ return new Date(Date.UTC(
+ ((dostime >> 25) & 0x7f) + 1980, // year
+ ((dostime >> 21) & 0x0f) - 1, // month
+ (dostime >> 16) & 0x1f, // day
+ (dostime >> 11) & 0x1f, // hour
+ (dostime >> 5) & 0x3f, // minute
+ (dostime & 0x1f) << 1)); // second
+ }
+};
+module.exports = DataReader;
+
+},{"../utils":32}],19:[function(require,module,exports){
+'use strict';
+var Uint8ArrayReader = require('./Uint8ArrayReader');
+var utils = require('../utils');
+
+function NodeBufferReader(data) {
+ Uint8ArrayReader.call(this, data);
+}
+utils.inherits(NodeBufferReader, Uint8ArrayReader);
+
+/**
+ * @see DataReader.readData
+ */
+NodeBufferReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = NodeBufferReader;
+
+},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(require,module,exports){
+'use strict';
+var DataReader = require('./DataReader');
+var utils = require('../utils');
+
+function StringReader(data) {
+ DataReader.call(this, data);
+}
+utils.inherits(StringReader, DataReader);
+/**
+ * @see DataReader.byteAt
+ */
+StringReader.prototype.byteAt = function(i) {
+ return this.data.charCodeAt(this.zero + i);
+};
+/**
+ * @see DataReader.lastIndexOfSignature
+ */
+StringReader.prototype.lastIndexOfSignature = function(sig) {
+ return this.data.lastIndexOf(sig) - this.zero;
+};
+/**
+ * @see DataReader.readAndCheckSignature
+ */
+StringReader.prototype.readAndCheckSignature = function (sig) {
+ var data = this.readData(4);
+ return sig === data;
+};
+/**
+ * @see DataReader.readData
+ */
+StringReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ // this will work because the constructor applied the "& 0xff" mask.
+ var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = StringReader;
+
+},{"../utils":32,"./DataReader":18}],21:[function(require,module,exports){
+'use strict';
+var ArrayReader = require('./ArrayReader');
+var utils = require('../utils');
+
+function Uint8ArrayReader(data) {
+ ArrayReader.call(this, data);
+}
+utils.inherits(Uint8ArrayReader, ArrayReader);
+/**
+ * @see DataReader.readData
+ */
+Uint8ArrayReader.prototype.readData = function(size) {
+ this.checkOffset(size);
+ if(size === 0) {
+ // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of [].
+ return new Uint8Array(0);
+ }
+ var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size);
+ this.index += size;
+ return result;
+};
+module.exports = Uint8ArrayReader;
+
+},{"../utils":32,"./ArrayReader":17}],22:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var support = require('../support');
+var ArrayReader = require('./ArrayReader');
+var StringReader = require('./StringReader');
+var NodeBufferReader = require('./NodeBufferReader');
+var Uint8ArrayReader = require('./Uint8ArrayReader');
+
+/**
+ * Create a reader adapted to the data.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read.
+ * @return {DataReader} the data reader.
+ */
+module.exports = function (data) {
+ var type = utils.getTypeOf(data);
+ utils.checkSupport(type);
+ if (type === "string" && !support.uint8array) {
+ return new StringReader(data);
+ }
+ if (type === "nodebuffer") {
+ return new NodeBufferReader(data);
+ }
+ if (support.uint8array) {
+ return new Uint8ArrayReader(utils.transformTo("uint8array", data));
+ }
+ return new ArrayReader(utils.transformTo("array", data));
+};
+
+},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(require,module,exports){
+'use strict';
+exports.LOCAL_FILE_HEADER = "PK\x03\x04";
+exports.CENTRAL_FILE_HEADER = "PK\x01\x02";
+exports.CENTRAL_DIRECTORY_END = "PK\x05\x06";
+exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07";
+exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06";
+exports.DATA_DESCRIPTOR = "PK\x07\x08";
+
+},{}],24:[function(require,module,exports){
+'use strict';
+
+var GenericWorker = require('./GenericWorker');
+var utils = require('../utils');
+
+/**
+ * A worker which convert chunks to a specified type.
+ * @constructor
+ * @param {String} destType the destination type.
+ */
+function ConvertWorker(destType) {
+ GenericWorker.call(this, "ConvertWorker to " + destType);
+ this.destType = destType;
+}
+utils.inherits(ConvertWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+ConvertWorker.prototype.processChunk = function (chunk) {
+ this.push({
+ data : utils.transformTo(this.destType, chunk.data),
+ meta : chunk.meta
+ });
+};
+module.exports = ConvertWorker;
+
+},{"../utils":32,"./GenericWorker":28}],25:[function(require,module,exports){
+'use strict';
+
+var GenericWorker = require('./GenericWorker');
+var crc32 = require('../crc32');
+var utils = require('../utils');
+
+/**
+ * A worker which calculate the crc32 of the data flowing through.
+ * @constructor
+ */
+function Crc32Probe() {
+ GenericWorker.call(this, "Crc32Probe");
+ this.withStreamInfo("crc32", 0);
+}
+utils.inherits(Crc32Probe, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+Crc32Probe.prototype.processChunk = function (chunk) {
+ this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0);
+ this.push(chunk);
+};
+module.exports = Crc32Probe;
+
+},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var GenericWorker = require('./GenericWorker');
+
+/**
+ * A worker which calculate the total length of the data flowing through.
+ * @constructor
+ * @param {String} propName the name used to expose the length
+ */
+function DataLengthProbe(propName) {
+ GenericWorker.call(this, "DataLengthProbe for " + propName);
+ this.propName = propName;
+ this.withStreamInfo(propName, 0);
+}
+utils.inherits(DataLengthProbe, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+DataLengthProbe.prototype.processChunk = function (chunk) {
+ if(chunk) {
+ var length = this.streamInfo[this.propName] || 0;
+ this.streamInfo[this.propName] = length + chunk.data.length;
+ }
+ GenericWorker.prototype.processChunk.call(this, chunk);
+};
+module.exports = DataLengthProbe;
+
+
+},{"../utils":32,"./GenericWorker":28}],27:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var GenericWorker = require('./GenericWorker');
+
+// the size of the generated chunks
+// TODO expose this as a public variable
+var DEFAULT_BLOCK_SIZE = 16 * 1024;
+
+/**
+ * A worker that reads a content and emits chunks.
+ * @constructor
+ * @param {Promise} dataP the promise of the data to split
+ */
+function DataWorker(dataP) {
+ GenericWorker.call(this, "DataWorker");
+ var self = this;
+ this.dataIsReady = false;
+ this.index = 0;
+ this.max = 0;
+ this.data = null;
+ this.type = "";
+
+ this._tickScheduled = false;
+
+ dataP.then(function (data) {
+ self.dataIsReady = true;
+ self.data = data;
+ self.max = data && data.length || 0;
+ self.type = utils.getTypeOf(data);
+ if(!self.isPaused) {
+ self._tickAndRepeat();
+ }
+ }, function (e) {
+ self.error(e);
+ });
+}
+
+utils.inherits(DataWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.cleanUp
+ */
+DataWorker.prototype.cleanUp = function () {
+ GenericWorker.prototype.cleanUp.call(this);
+ this.data = null;
+};
+
+/**
+ * @see GenericWorker.resume
+ */
+DataWorker.prototype.resume = function () {
+ if(!GenericWorker.prototype.resume.call(this)) {
+ return false;
+ }
+
+ if (!this._tickScheduled && this.dataIsReady) {
+ this._tickScheduled = true;
+ utils.delay(this._tickAndRepeat, [], this);
+ }
+ return true;
+};
+
+/**
+ * Trigger a tick a schedule an other call to this function.
+ */
+DataWorker.prototype._tickAndRepeat = function() {
+ this._tickScheduled = false;
+ if(this.isPaused || this.isFinished) {
+ return;
+ }
+ this._tick();
+ if(!this.isFinished) {
+ utils.delay(this._tickAndRepeat, [], this);
+ this._tickScheduled = true;
+ }
+};
+
+/**
+ * Read and push a chunk.
+ */
+DataWorker.prototype._tick = function() {
+
+ if(this.isPaused || this.isFinished) {
+ return false;
+ }
+
+ var size = DEFAULT_BLOCK_SIZE;
+ var data = null, nextIndex = Math.min(this.max, this.index + size);
+ if (this.index >= this.max) {
+ // EOF
+ return this.end();
+ } else {
+ switch(this.type) {
+ case "string":
+ data = this.data.substring(this.index, nextIndex);
+ break;
+ case "uint8array":
+ data = this.data.subarray(this.index, nextIndex);
+ break;
+ case "array":
+ case "nodebuffer":
+ data = this.data.slice(this.index, nextIndex);
+ break;
+ }
+ this.index = nextIndex;
+ return this.push({
+ data : data,
+ meta : {
+ percent : this.max ? this.index / this.max * 100 : 0
+ }
+ });
+ }
+};
+
+module.exports = DataWorker;
+
+},{"../utils":32,"./GenericWorker":28}],28:[function(require,module,exports){
+'use strict';
+
+/**
+ * A worker that does nothing but passing chunks to the next one. This is like
+ * a nodejs stream but with some differences. On the good side :
+ * - it works on IE 6-9 without any issue / polyfill
+ * - it weights less than the full dependencies bundled with browserify
+ * - it forwards errors (no need to declare an error handler EVERYWHERE)
+ *
+ * A chunk is an object with 2 attributes : `meta` and `data`. The former is an
+ * object containing anything (`percent` for example), see each worker for more
+ * details. The latter is the real data (String, Uint8Array, etc).
+ *
+ * @constructor
+ * @param {String} name the name of the stream (mainly used for debugging purposes)
+ */
+function GenericWorker(name) {
+ // the name of the worker
+ this.name = name || "default";
+ // an object containing metadata about the workers chain
+ this.streamInfo = {};
+ // an error which happened when the worker was paused
+ this.generatedError = null;
+ // an object containing metadata to be merged by this worker into the general metadata
+ this.extraStreamInfo = {};
+ // true if the stream is paused (and should not do anything), false otherwise
+ this.isPaused = true;
+ // true if the stream is finished (and should not do anything), false otherwise
+ this.isFinished = false;
+ // true if the stream is locked to prevent further structure updates (pipe), false otherwise
+ this.isLocked = false;
+ // the event listeners
+ this._listeners = {
+ 'data':[],
+ 'end':[],
+ 'error':[]
+ };
+ // the previous worker, if any
+ this.previous = null;
+}
+
+GenericWorker.prototype = {
+ /**
+ * Push a chunk to the next workers.
+ * @param {Object} chunk the chunk to push
+ */
+ push : function (chunk) {
+ this.emit("data", chunk);
+ },
+ /**
+ * End the stream.
+ * @return {Boolean} true if this call ended the worker, false otherwise.
+ */
+ end : function () {
+ if (this.isFinished) {
+ return false;
+ }
+
+ this.flush();
+ try {
+ this.emit("end");
+ this.cleanUp();
+ this.isFinished = true;
+ } catch (e) {
+ this.emit("error", e);
+ }
+ return true;
+ },
+ /**
+ * End the stream with an error.
+ * @param {Error} e the error which caused the premature end.
+ * @return {Boolean} true if this call ended the worker with an error, false otherwise.
+ */
+ error : function (e) {
+ if (this.isFinished) {
+ return false;
+ }
+
+ if(this.isPaused) {
+ this.generatedError = e;
+ } else {
+ this.isFinished = true;
+
+ this.emit("error", e);
+
+ // in the workers chain exploded in the middle of the chain,
+ // the error event will go downward but we also need to notify
+ // workers upward that there has been an error.
+ if(this.previous) {
+ this.previous.error(e);
+ }
+
+ this.cleanUp();
+ }
+ return true;
+ },
+ /**
+ * Add a callback on an event.
+ * @param {String} name the name of the event (data, end, error)
+ * @param {Function} listener the function to call when the event is triggered
+ * @return {GenericWorker} the current object for chainability
+ */
+ on : function (name, listener) {
+ this._listeners[name].push(listener);
+ return this;
+ },
+ /**
+ * Clean any references when a worker is ending.
+ */
+ cleanUp : function () {
+ this.streamInfo = this.generatedError = this.extraStreamInfo = null;
+ this._listeners = [];
+ },
+ /**
+ * Trigger an event. This will call registered callback with the provided arg.
+ * @param {String} name the name of the event (data, end, error)
+ * @param {Object} arg the argument to call the callback with.
+ */
+ emit : function (name, arg) {
+ if (this._listeners[name]) {
+ for(var i = 0; i < this._listeners[name].length; i++) {
+ this._listeners[name][i].call(this, arg);
+ }
+ }
+ },
+ /**
+ * Chain a worker with an other.
+ * @param {Worker} next the worker receiving events from the current one.
+ * @return {worker} the next worker for chainability
+ */
+ pipe : function (next) {
+ return next.registerPrevious(this);
+ },
+ /**
+ * Same as `pipe` in the other direction.
+ * Using an API with `pipe(next)` is very easy.
+ * Implementing the API with the point of view of the next one registering
+ * a source is easier, see the ZipFileWorker.
+ * @param {Worker} previous the previous worker, sending events to this one
+ * @return {Worker} the current worker for chainability
+ */
+ registerPrevious : function (previous) {
+ if (this.isLocked) {
+ throw new Error("The stream '" + this + "' has already been used.");
+ }
+
+ // sharing the streamInfo...
+ this.streamInfo = previous.streamInfo;
+ // ... and adding our own bits
+ this.mergeStreamInfo();
+ this.previous = previous;
+ var self = this;
+ previous.on('data', function (chunk) {
+ self.processChunk(chunk);
+ });
+ previous.on('end', function () {
+ self.end();
+ });
+ previous.on('error', function (e) {
+ self.error(e);
+ });
+ return this;
+ },
+ /**
+ * Pause the stream so it doesn't send events anymore.
+ * @return {Boolean} true if this call paused the worker, false otherwise.
+ */
+ pause : function () {
+ if(this.isPaused || this.isFinished) {
+ return false;
+ }
+ this.isPaused = true;
+
+ if(this.previous) {
+ this.previous.pause();
+ }
+ return true;
+ },
+ /**
+ * Resume a paused stream.
+ * @return {Boolean} true if this call resumed the worker, false otherwise.
+ */
+ resume : function () {
+ if(!this.isPaused || this.isFinished) {
+ return false;
+ }
+ this.isPaused = false;
+
+ // if true, the worker tried to resume but failed
+ var withError = false;
+ if(this.generatedError) {
+ this.error(this.generatedError);
+ withError = true;
+ }
+ if(this.previous) {
+ this.previous.resume();
+ }
+
+ return !withError;
+ },
+ /**
+ * Flush any remaining bytes as the stream is ending.
+ */
+ flush : function () {},
+ /**
+ * Process a chunk. This is usually the method overridden.
+ * @param {Object} chunk the chunk to process.
+ */
+ processChunk : function(chunk) {
+ this.push(chunk);
+ },
+ /**
+ * Add a key/value to be added in the workers chain streamInfo once activated.
+ * @param {String} key the key to use
+ * @param {Object} value the associated value
+ * @return {Worker} the current worker for chainability
+ */
+ withStreamInfo : function (key, value) {
+ this.extraStreamInfo[key] = value;
+ this.mergeStreamInfo();
+ return this;
+ },
+ /**
+ * Merge this worker's streamInfo into the chain's streamInfo.
+ */
+ mergeStreamInfo : function () {
+ for(var key in this.extraStreamInfo) {
+ if (!this.extraStreamInfo.hasOwnProperty(key)) {
+ continue;
+ }
+ this.streamInfo[key] = this.extraStreamInfo[key];
+ }
+ },
+
+ /**
+ * Lock the stream to prevent further updates on the workers chain.
+ * After calling this method, all calls to pipe will fail.
+ */
+ lock: function () {
+ if (this.isLocked) {
+ throw new Error("The stream '" + this + "' has already been used.");
+ }
+ this.isLocked = true;
+ if (this.previous) {
+ this.previous.lock();
+ }
+ },
+
+ /**
+ *
+ * Pretty print the workers chain.
+ */
+ toString : function () {
+ var me = "Worker " + this.name;
+ if (this.previous) {
+ return this.previous + " -> " + me;
+ } else {
+ return me;
+ }
+ }
+};
+
+module.exports = GenericWorker;
+
+},{}],29:[function(require,module,exports){
+'use strict';
+
+var utils = require('../utils');
+var ConvertWorker = require('./ConvertWorker');
+var GenericWorker = require('./GenericWorker');
+var base64 = require('../base64');
+var support = require("../support");
+var external = require("../external");
+
+var NodejsStreamOutputAdapter = null;
+if (support.nodestream) {
+ try {
+ NodejsStreamOutputAdapter = require('../nodejs/NodejsStreamOutputAdapter');
+ } catch(e) {}
+}
+
+/**
+ * Apply the final transformation of the data. If the user wants a Blob for
+ * example, it's easier to work with an U8intArray and finally do the
+ * ArrayBuffer/Blob conversion.
+ * @param {String} type the name of the final type
+ * @param {String|Uint8Array|Buffer} content the content to transform
+ * @param {String} mimeType the mime type of the content, if applicable.
+ * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format.
+ */
+function transformZipOutput(type, content, mimeType) {
+ switch(type) {
+ case "blob" :
+ return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType);
+ case "base64" :
+ return base64.encode(content);
+ default :
+ return utils.transformTo(type, content);
+ }
+}
+
+/**
+ * Concatenate an array of data of the given type.
+ * @param {String} type the type of the data in the given array.
+ * @param {Array} dataArray the array containing the data chunks to concatenate
+ * @return {String|Uint8Array|Buffer} the concatenated data
+ * @throws Error if the asked type is unsupported
+ */
+function concat (type, dataArray) {
+ var i, index = 0, res = null, totalLength = 0;
+ for(i = 0; i < dataArray.length; i++) {
+ totalLength += dataArray[i].length;
+ }
+ switch(type) {
+ case "string":
+ return dataArray.join("");
+ case "array":
+ return Array.prototype.concat.apply([], dataArray);
+ case "uint8array":
+ res = new Uint8Array(totalLength);
+ for(i = 0; i < dataArray.length; i++) {
+ res.set(dataArray[i], index);
+ index += dataArray[i].length;
+ }
+ return res;
+ case "nodebuffer":
+ return Buffer.concat(dataArray);
+ default:
+ throw new Error("concat : unsupported type '" + type + "'");
+ }
+}
+
+/**
+ * Listen a StreamHelper, accumulate its content and concatenate it into a
+ * complete block.
+ * @param {StreamHelper} helper the helper to use.
+ * @param {Function} updateCallback a callback called on each update. Called
+ * with one arg :
+ * - the metadata linked to the update received.
+ * @return Promise the promise for the accumulation.
+ */
+function accumulate(helper, updateCallback) {
+ return new external.Promise(function (resolve, reject){
+ var dataArray = [];
+ var chunkType = helper._internalType,
+ resultType = helper._outputType,
+ mimeType = helper._mimeType;
+ helper
+ .on('data', function (data, meta) {
+ dataArray.push(data);
+ if(updateCallback) {
+ updateCallback(meta);
+ }
+ })
+ .on('error', function(err) {
+ dataArray = [];
+ reject(err);
+ })
+ .on('end', function (){
+ try {
+ var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType);
+ resolve(result);
+ } catch (e) {
+ reject(e);
+ }
+ dataArray = [];
+ })
+ .resume();
+ });
+}
+
+/**
+ * An helper to easily use workers outside of JSZip.
+ * @constructor
+ * @param {Worker} worker the worker to wrap
+ * @param {String} outputType the type of data expected by the use
+ * @param {String} mimeType the mime type of the content, if applicable.
+ */
+function StreamHelper(worker, outputType, mimeType) {
+ var internalType = outputType;
+ switch(outputType) {
+ case "blob":
+ case "arraybuffer":
+ internalType = "uint8array";
+ break;
+ case "base64":
+ internalType = "string";
+ break;
+ }
+
+ try {
+ // the type used internally
+ this._internalType = internalType;
+ // the type used to output results
+ this._outputType = outputType;
+ // the mime type
+ this._mimeType = mimeType;
+ utils.checkSupport(internalType);
+ this._worker = worker.pipe(new ConvertWorker(internalType));
+ // the last workers can be rewired without issues but we need to
+ // prevent any updates on previous workers.
+ worker.lock();
+ } catch(e) {
+ this._worker = new GenericWorker("error");
+ this._worker.error(e);
+ }
+}
+
+StreamHelper.prototype = {
+ /**
+ * Listen a StreamHelper, accumulate its content and concatenate it into a
+ * complete block.
+ * @param {Function} updateCb the update callback.
+ * @return Promise the promise for the accumulation.
+ */
+ accumulate : function (updateCb) {
+ return accumulate(this, updateCb);
+ },
+ /**
+ * Add a listener on an event triggered on a stream.
+ * @param {String} evt the name of the event
+ * @param {Function} fn the listener
+ * @return {StreamHelper} the current helper.
+ */
+ on : function (evt, fn) {
+ var self = this;
+
+ if(evt === "data") {
+ this._worker.on(evt, function (chunk) {
+ fn.call(self, chunk.data, chunk.meta);
+ });
+ } else {
+ this._worker.on(evt, function () {
+ utils.delay(fn, arguments, self);
+ });
+ }
+ return this;
+ },
+ /**
+ * Resume the flow of chunks.
+ * @return {StreamHelper} the current helper.
+ */
+ resume : function () {
+ utils.delay(this._worker.resume, [], this._worker);
+ return this;
+ },
+ /**
+ * Pause the flow of chunks.
+ * @return {StreamHelper} the current helper.
+ */
+ pause : function () {
+ this._worker.pause();
+ return this;
+ },
+ /**
+ * Return a nodejs stream for this helper.
+ * @param {Function} updateCb the update callback.
+ * @return {NodejsStreamOutputAdapter} the nodejs stream.
+ */
+ toNodejsStream : function (updateCb) {
+ utils.checkSupport("nodestream");
+ if (this._outputType !== "nodebuffer") {
+ // an object stream containing blob/arraybuffer/uint8array/string
+ // is strange and I don't know if it would be useful.
+ // I you find this comment and have a good usecase, please open a
+ // bug report !
+ throw new Error(this._outputType + " is not supported by this method");
+ }
+
+ return new NodejsStreamOutputAdapter(this, {
+ objectMode : this._outputType !== "nodebuffer"
+ }, updateCb);
+ }
+};
+
+
+module.exports = StreamHelper;
+
+},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(require,module,exports){
+'use strict';
+
+exports.base64 = true;
+exports.array = true;
+exports.string = true;
+exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
+exports.nodebuffer = typeof Buffer !== "undefined";
+// contains true if JSZip can read/generate Uint8Array, false otherwise.
+exports.uint8array = typeof Uint8Array !== "undefined";
+
+if (typeof ArrayBuffer === "undefined") {
+ exports.blob = false;
+}
+else {
+ var buffer = new ArrayBuffer(0);
+ try {
+ exports.blob = new Blob([buffer], {
+ type: "application/zip"
+ }).size === 0;
+ }
+ catch (e) {
+ try {
+ var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
+ var builder = new Builder();
+ builder.append(buffer);
+ exports.blob = builder.getBlob('application/zip').size === 0;
+ }
+ catch (e) {
+ exports.blob = false;
+ }
+ }
+}
+
+try {
+ exports.nodestream = !!require('readable-stream').Readable;
+} catch(e) {
+ exports.nodestream = false;
+}
+
+},{"readable-stream":16}],31:[function(require,module,exports){
+'use strict';
+
+var utils = require('./utils');
+var support = require('./support');
+var nodejsUtils = require('./nodejsUtils');
+var GenericWorker = require('./stream/GenericWorker');
+
+/**
+ * The following functions come from pako, from pako/lib/utils/strings
+ * released under the MIT license, see pako https://github.com/nodeca/pako/
+ */
+
+// Table with utf8 lengths (calculated by first byte of sequence)
+// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
+// because max possible codepoint is 0x10ffff
+var _utf8len = new Array(256);
+for (var i=0; i<256; i++) {
+ _utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1);
+}
+_utf8len[254]=_utf8len[254]=1; // Invalid sequence start
+
+// convert string to array (typed, when possible)
+var string2buf = function (str) {
+ var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;
+
+ // count binary size
+ for (m_pos = 0; m_pos < str_len; m_pos++) {
+ c = str.charCodeAt(m_pos);
+ if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
+ c2 = str.charCodeAt(m_pos+1);
+ if ((c2 & 0xfc00) === 0xdc00) {
+ c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
+ m_pos++;
+ }
+ }
+ buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
+ }
+
+ // allocate buffer
+ if (support.uint8array) {
+ buf = new Uint8Array(buf_len);
+ } else {
+ buf = new Array(buf_len);
+ }
+
+ // convert
+ for (i=0, m_pos = 0; i < buf_len; m_pos++) {
+ c = str.charCodeAt(m_pos);
+ if ((c & 0xfc00) === 0xd800 && (m_pos+1 < str_len)) {
+ c2 = str.charCodeAt(m_pos+1);
+ if ((c2 & 0xfc00) === 0xdc00) {
+ c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
+ m_pos++;
+ }
+ }
+ if (c < 0x80) {
+ /* one byte */
+ buf[i++] = c;
+ } else if (c < 0x800) {
+ /* two bytes */
+ buf[i++] = 0xC0 | (c >>> 6);
+ buf[i++] = 0x80 | (c & 0x3f);
+ } else if (c < 0x10000) {
+ /* three bytes */
+ buf[i++] = 0xE0 | (c >>> 12);
+ buf[i++] = 0x80 | (c >>> 6 & 0x3f);
+ buf[i++] = 0x80 | (c & 0x3f);
+ } else {
+ /* four bytes */
+ buf[i++] = 0xf0 | (c >>> 18);
+ buf[i++] = 0x80 | (c >>> 12 & 0x3f);
+ buf[i++] = 0x80 | (c >>> 6 & 0x3f);
+ buf[i++] = 0x80 | (c & 0x3f);
+ }
+ }
+
+ return buf;
+};
+
+// Calculate max possible position in utf8 buffer,
+// that will not break sequence. If that's not possible
+// - (very small limits) return max size as is.
+//
+// buf[] - utf8 bytes array
+// max - length limit (mandatory);
+var utf8border = function(buf, max) {
+ var pos;
+
+ max = max || buf.length;
+ if (max > buf.length) { max = buf.length; }
+
+ // go back from last position, until start of sequence found
+ pos = max-1;
+ while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }
+
+ // Fuckup - very small and broken sequence,
+ // return max, because we should return something anyway.
+ if (pos < 0) { return max; }
+
+ // If we came to start of buffer - that means vuffer is too small,
+ // return max too.
+ if (pos === 0) { return max; }
+
+ return (pos + _utf8len[buf[pos]] > max) ? pos : max;
+};
+
+// convert array to string
+var buf2string = function (buf) {
+ var str, i, out, c, c_len;
+ var len = buf.length;
+
+ // Reserve max possible length (2 words per char)
+ // NB: by unknown reasons, Array is significantly faster for
+ // String.fromCharCode.apply than Uint16Array.
+ var utf16buf = new Array(len*2);
+
+ for (out=0, i=0; i<len;) {
+ c = buf[i++];
+ // quick process ascii
+ if (c < 0x80) { utf16buf[out++] = c; continue; }
+
+ c_len = _utf8len[c];
+ // skip 5 & 6 byte codes
+ if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len-1; continue; }
+
+ // apply mask on first byte
+ c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
+ // join the rest
+ while (c_len > 1 && i < len) {
+ c = (c << 6) | (buf[i++] & 0x3f);
+ c_len--;
+ }
+
+ // terminated by end of string?
+ if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }
+
+ if (c < 0x10000) {
+ utf16buf[out++] = c;
+ } else {
+ c -= 0x10000;
+ utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
+ utf16buf[out++] = 0xdc00 | (c & 0x3ff);
+ }
+ }
+
+ // shrinkBuf(utf16buf, out)
+ if (utf16buf.length !== out) {
+ if(utf16buf.subarray) {
+ utf16buf = utf16buf.subarray(0, out);
+ } else {
+ utf16buf.length = out;
+ }
+ }
+
+ // return String.fromCharCode.apply(null, utf16buf);
+ return utils.applyFromCharCode(utf16buf);
+};
+
+
+// That's all for the pako functions.
+
+
+/**
+ * Transform a javascript string into an array (typed if possible) of bytes,
+ * UTF-8 encoded.
+ * @param {String} str the string to encode
+ * @return {Array|Uint8Array|Buffer} the UTF-8 encoded string.
+ */
+exports.utf8encode = function utf8encode(str) {
+ if (support.nodebuffer) {
+ return nodejsUtils.newBufferFrom(str, "utf-8");
+ }
+
+ return string2buf(str);
+};
+
+
+/**
+ * Transform a bytes array (or a representation) representing an UTF-8 encoded
+ * string into a javascript string.
+ * @param {Array|Uint8Array|Buffer} buf the data de decode
+ * @return {String} the decoded string.
+ */
+exports.utf8decode = function utf8decode(buf) {
+ if (support.nodebuffer) {
+ return utils.transformTo("nodebuffer", buf).toString("utf-8");
+ }
+
+ buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf);
+
+ return buf2string(buf);
+};
+
+/**
+ * A worker to decode utf8 encoded binary chunks into string chunks.
+ * @constructor
+ */
+function Utf8DecodeWorker() {
+ GenericWorker.call(this, "utf-8 decode");
+ // the last bytes if a chunk didn't end with a complete codepoint.
+ this.leftOver = null;
+}
+utils.inherits(Utf8DecodeWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+Utf8DecodeWorker.prototype.processChunk = function (chunk) {
+
+ var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data);
+
+ // 1st step, re-use what's left of the previous chunk
+ if (this.leftOver && this.leftOver.length) {
+ if(support.uint8array) {
+ var previousData = data;
+ data = new Uint8Array(previousData.length + this.leftOver.length);
+ data.set(this.leftOver, 0);
+ data.set(previousData, this.leftOver.length);
+ } else {
+ data = this.leftOver.concat(data);
+ }
+ this.leftOver = null;
+ }
+
+ var nextBoundary = utf8border(data);
+ var usableData = data;
+ if (nextBoundary !== data.length) {
+ if (support.uint8array) {
+ usableData = data.subarray(0, nextBoundary);
+ this.leftOver = data.subarray(nextBoundary, data.length);
+ } else {
+ usableData = data.slice(0, nextBoundary);
+ this.leftOver = data.slice(nextBoundary, data.length);
+ }
+ }
+
+ this.push({
+ data : exports.utf8decode(usableData),
+ meta : chunk.meta
+ });
+};
+
+/**
+ * @see GenericWorker.flush
+ */
+Utf8DecodeWorker.prototype.flush = function () {
+ if(this.leftOver && this.leftOver.length) {
+ this.push({
+ data : exports.utf8decode(this.leftOver),
+ meta : {}
+ });
+ this.leftOver = null;
+ }
+};
+exports.Utf8DecodeWorker = Utf8DecodeWorker;
+
+/**
+ * A worker to endcode string chunks into utf8 encoded binary chunks.
+ * @constructor
+ */
+function Utf8EncodeWorker() {
+ GenericWorker.call(this, "utf-8 encode");
+}
+utils.inherits(Utf8EncodeWorker, GenericWorker);
+
+/**
+ * @see GenericWorker.processChunk
+ */
+Utf8EncodeWorker.prototype.processChunk = function (chunk) {
+ this.push({
+ data : exports.utf8encode(chunk.data),
+ meta : chunk.meta
+ });
+};
+exports.Utf8EncodeWorker = Utf8EncodeWorker;
+
+},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(require,module,exports){
+'use strict';
+
+var support = require('./support');
+var base64 = require('./base64');
+var nodejsUtils = require('./nodejsUtils');
+var setImmediate = require('set-immediate-shim');
+var external = require("./external");
+
+
+/**
+ * Convert a string that pass as a "binary string": it should represent a byte
+ * array but may have > 255 char codes. Be sure to take only the first byte
+ * and returns the byte array.
+ * @param {String} str the string to transform.
+ * @return {Array|Uint8Array} the string in a binary format.
+ */
+function string2binary(str) {
+ var result = null;
+ if (support.uint8array) {
+ result = new Uint8Array(str.length);
+ } else {
+ result = new Array(str.length);
+ }
+ return stringToArrayLike(str, result);
+}
+
+/**
+ * Create a new blob with the given content and the given type.
+ * @param {String|ArrayBuffer} part the content to put in the blob. DO NOT use
+ * an Uint8Array because the stock browser of android 4 won't accept it (it
+ * will be silently converted to a string, "[object Uint8Array]").
+ *
+ * Use only ONE part to build the blob to avoid a memory leak in IE11 / Edge:
+ * when a large amount of Array is used to create the Blob, the amount of
+ * memory consumed is nearly 100 times the original data amount.
+ *
+ * @param {String} type the mime type of the blob.
+ * @return {Blob} the created blob.
+ */
+exports.newBlob = function(part, type) {
+ exports.checkSupport("blob");
+
+ try {
+ // Blob constructor
+ return new Blob([part], {
+ type: type
+ });
+ }
+ catch (e) {
+
+ try {
+ // deprecated, browser only, old way
+ var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
+ var builder = new Builder();
+ builder.append(part);
+ return builder.getBlob(type);
+ }
+ catch (e) {
+
+ // well, fuck ?!
+ throw new Error("Bug : can't construct the Blob.");
+ }
+ }
+
+
+};
+/**
+ * The identity function.
+ * @param {Object} input the input.
+ * @return {Object} the same input.
+ */
+function identity(input) {
+ return input;
+}
+
+/**
+ * Fill in an array with a string.
+ * @param {String} str the string to use.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated).
+ * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array.
+ */
+function stringToArrayLike(str, array) {
+ for (var i = 0; i < str.length; ++i) {
+ array[i] = str.charCodeAt(i) & 0xFF;
+ }
+ return array;
+}
+
+/**
+ * An helper for the function arrayLikeToString.
+ * This contains static informations and functions that
+ * can be optimized by the browser JIT compiler.
+ */
+var arrayToStringHelper = {
+ /**
+ * Transform an array of int into a string, chunk by chunk.
+ * See the performances notes on arrayLikeToString.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
+ * @param {String} type the type of the array.
+ * @param {Integer} chunk the chunk size.
+ * @return {String} the resulting string.
+ * @throws Error if the chunk is too big for the stack.
+ */
+ stringifyByChunk: function(array, type, chunk) {
+ var result = [], k = 0, len = array.length;
+ // shortcut
+ if (len <= chunk) {
+ return String.fromCharCode.apply(null, array);
+ }
+ while (k < len) {
+ if (type === "array" || type === "nodebuffer") {
+ result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len))));
+ }
+ else {
+ result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len))));
+ }
+ k += chunk;
+ }
+ return result.join("");
+ },
+ /**
+ * Call String.fromCharCode on every item in the array.
+ * This is the naive implementation, which generate A LOT of intermediate string.
+ * This should be used when everything else fail.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
+ * @return {String} the result.
+ */
+ stringifyByChar: function(array){
+ var resultStr = "";
+ for(var i = 0; i < array.length; i++) {
+ resultStr += String.fromCharCode(array[i]);
+ }
+ return resultStr;
+ },
+ applyCanBeUsed : {
+ /**
+ * true if the browser accepts to use String.fromCharCode on Uint8Array
+ */
+ uint8array : (function () {
+ try {
+ return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1;
+ } catch (e) {
+ return false;
+ }
+ })(),
+ /**
+ * true if the browser accepts to use String.fromCharCode on nodejs Buffer.
+ */
+ nodebuffer : (function () {
+ try {
+ return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.allocBuffer(1)).length === 1;
+ } catch (e) {
+ return false;
+ }
+ })()
+ }
+};
+
+/**
+ * Transform an array-like object to a string.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
+ * @return {String} the result.
+ */
+function arrayLikeToString(array) {
+ // Performances notes :
+ // --------------------
+ // String.fromCharCode.apply(null, array) is the fastest, see
+ // see http://jsperf.com/converting-a-uint8array-to-a-string/2
+ // but the stack is limited (and we can get huge arrays !).
+ //
+ // result += String.fromCharCode(array[i]); generate too many strings !
+ //
+ // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2
+ // TODO : we now have workers that split the work. Do we still need that ?
+ var chunk = 65536,
+ type = exports.getTypeOf(array),
+ canUseApply = true;
+ if (type === "uint8array") {
+ canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array;
+ } else if (type === "nodebuffer") {
+ canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer;
+ }
+
+ if (canUseApply) {
+ while (chunk > 1) {
+ try {
+ return arrayToStringHelper.stringifyByChunk(array, type, chunk);
+ } catch (e) {
+ chunk = Math.floor(chunk / 2);
+ }
+ }
+ }
+
+ // no apply or chunk error : slow and painful algorithm
+ // default browser on android 4.*
+ return arrayToStringHelper.stringifyByChar(array);
+}
+
+exports.applyFromCharCode = arrayLikeToString;
+
+
+/**
+ * Copy the data from an array-like to an other array-like.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array.
+ * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated.
+ * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array.
+ */
+function arrayLikeToArrayLike(arrayFrom, arrayTo) {
+ for (var i = 0; i < arrayFrom.length; i++) {
+ arrayTo[i] = arrayFrom[i];
+ }
+ return arrayTo;
+}
+
+// a matrix containing functions to transform everything into everything.
+var transform = {};
+
+// string to ?
+transform["string"] = {
+ "string": identity,
+ "array": function(input) {
+ return stringToArrayLike(input, new Array(input.length));
+ },
+ "arraybuffer": function(input) {
+ return transform["string"]["uint8array"](input).buffer;
+ },
+ "uint8array": function(input) {
+ return stringToArrayLike(input, new Uint8Array(input.length));
+ },
+ "nodebuffer": function(input) {
+ return stringToArrayLike(input, nodejsUtils.allocBuffer(input.length));
+ }
+};
+
+// array to ?
+transform["array"] = {
+ "string": arrayLikeToString,
+ "array": identity,
+ "arraybuffer": function(input) {
+ return (new Uint8Array(input)).buffer;
+ },
+ "uint8array": function(input) {
+ return new Uint8Array(input);
+ },
+ "nodebuffer": function(input) {
+ return nodejsUtils.newBufferFrom(input);
+ }
+};
+
+// arraybuffer to ?
+transform["arraybuffer"] = {
+ "string": function(input) {
+ return arrayLikeToString(new Uint8Array(input));
+ },
+ "array": function(input) {
+ return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength));
+ },
+ "arraybuffer": identity,
+ "uint8array": function(input) {
+ return new Uint8Array(input);
+ },
+ "nodebuffer": function(input) {
+ return nodejsUtils.newBufferFrom(new Uint8Array(input));
+ }
+};
+
+// uint8array to ?
+transform["uint8array"] = {
+ "string": arrayLikeToString,
+ "array": function(input) {
+ return arrayLikeToArrayLike(input, new Array(input.length));
+ },
+ "arraybuffer": function(input) {
+ return input.buffer;
+ },
+ "uint8array": identity,
+ "nodebuffer": function(input) {
+ return nodejsUtils.newBufferFrom(input);
+ }
+};
+
+// nodebuffer to ?
+transform["nodebuffer"] = {
+ "string": arrayLikeToString,
+ "array": function(input) {
+ return arrayLikeToArrayLike(input, new Array(input.length));
+ },
+ "arraybuffer": function(input) {
+ return transform["nodebuffer"]["uint8array"](input).buffer;
+ },
+ "uint8array": function(input) {
+ return arrayLikeToArrayLike(input, new Uint8Array(input.length));
+ },
+ "nodebuffer": identity
+};
+
+/**
+ * Transform an input into any type.
+ * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer.
+ * If no output type is specified, the unmodified input will be returned.
+ * @param {String} outputType the output type.
+ * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert.
+ * @throws {Error} an Error if the browser doesn't support the requested output type.
+ */
+exports.transformTo = function(outputType, input) {
+ if (!input) {
+ // undefined, null, etc
+ // an empty string won't harm.
+ input = "";
+ }
+ if (!outputType) {
+ return input;
+ }
+ exports.checkSupport(outputType);
+ var inputType = exports.getTypeOf(input);
+ var result = transform[inputType][outputType](input);
+ return result;
+};
+
+/**
+ * Return the type of the input.
+ * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
+ * @param {Object} input the input to identify.
+ * @return {String} the (lowercase) type of the input.
+ */
+exports.getTypeOf = function(input) {
+ if (typeof input === "string") {
+ return "string";
+ }
+ if (Object.prototype.toString.call(input) === "[object Array]") {
+ return "array";
+ }
+ if (support.nodebuffer && nodejsUtils.isBuffer(input)) {
+ return "nodebuffer";
+ }
+ if (support.uint8array && input instanceof Uint8Array) {
+ return "uint8array";
+ }
+ if (support.arraybuffer && input instanceof ArrayBuffer) {
+ return "arraybuffer";
+ }
+};
+
+/**
+ * Throw an exception if the type is not supported.
+ * @param {String} type the type to check.
+ * @throws {Error} an Error if the browser doesn't support the requested type.
+ */
+exports.checkSupport = function(type) {
+ var supported = support[type.toLowerCase()];
+ if (!supported) {
+ throw new Error(type + " is not supported by this platform");
+ }
+};
+
+exports.MAX_VALUE_16BITS = 65535;
+exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1
+
+/**
+ * Prettify a string read as binary.
+ * @param {string} str the string to prettify.
+ * @return {string} a pretty string.
+ */
+exports.pretty = function(str) {
+ var res = '',
+ code, i;
+ for (i = 0; i < (str || "").length; i++) {
+ code = str.charCodeAt(i);
+ res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase();
+ }
+ return res;
+};
+
+/**
+ * Defer the call of a function.
+ * @param {Function} callback the function to call asynchronously.
+ * @param {Array} args the arguments to give to the callback.
+ */
+exports.delay = function(callback, args, self) {
+ setImmediate(function () {
+ callback.apply(self || null, args || []);
+ });
+};
+
+/**
+ * Extends a prototype with an other, without calling a constructor with
+ * side effects. Inspired by nodejs' `utils.inherits`
+ * @param {Function} ctor the constructor to augment
+ * @param {Function} superCtor the parent constructor to use
+ */
+exports.inherits = function (ctor, superCtor) {
+ var Obj = function() {};
+ Obj.prototype = superCtor.prototype;
+ ctor.prototype = new Obj();
+};
+
+/**
+ * Merge the objects passed as parameters into a new one.
+ * @private
+ * @param {...Object} var_args All objects to merge.
+ * @return {Object} a new object with the data of the others.
+ */
+exports.extend = function() {
+ var result = {}, i, attr;
+ for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
+ for (attr in arguments[i]) {
+ if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
+ result[attr] = arguments[i][attr];
+ }
+ }
+ }
+ return result;
+};
+
+/**
+ * Transform arbitrary content into a Promise.
+ * @param {String} name a name for the content being processed.
+ * @param {Object} inputData the content to process.
+ * @param {Boolean} isBinary true if the content is not an unicode string
+ * @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character.
+ * @param {Boolean} isBase64 true if the string content is encoded with base64.
+ * @return {Promise} a promise in a format usable by JSZip.
+ */
+exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinaryString, isBase64) {
+
+ // if inputData is already a promise, this flatten it.
+ var promise = external.Promise.resolve(inputData).then(function(data) {
+
+
+ var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1);
+
+ if (isBlob && typeof FileReader !== "undefined") {
+ return new external.Promise(function (resolve, reject) {
+ var reader = new FileReader();
+
+ reader.onload = function(e) {
+ resolve(e.target.result);
+ };
+ reader.onerror = function(e) {
+ reject(e.target.error);
+ };
+ reader.readAsArrayBuffer(data);
+ });
+ } else {
+ return data;
+ }
+ });
+
+ return promise.then(function(data) {
+ var dataType = exports.getTypeOf(data);
+
+ if (!dataType) {
+ return external.Promise.reject(
+ new Error("Can't read the data of '" + name + "'. Is it " +
+ "in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?")
+ );
+ }
+ // special case : it's way easier to work with Uint8Array than with ArrayBuffer
+ if (dataType === "arraybuffer") {
+ data = exports.transformTo("uint8array", data);
+ } else if (dataType === "string") {
+ if (isBase64) {
+ data = base64.decode(data);
+ }
+ else if (isBinary) {
+ // optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask
+ if (isOptimizedBinaryString !== true) {
+ // this is a string, not in a base64 format.
+ // Be sure that this is a correct "binary string"
+ data = string2binary(data);
+ }
+ }
+ }
+ return data;
+ });
+};
+
+},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,"set-immediate-shim":54}],33:[function(require,module,exports){
+'use strict';
+var readerFor = require('./reader/readerFor');
+var utils = require('./utils');
+var sig = require('./signature');
+var ZipEntry = require('./zipEntry');
+var utf8 = require('./utf8');
+var support = require('./support');
+// class ZipEntries {{{
+/**
+ * All the entries in the zip file.
+ * @constructor
+ * @param {Object} loadOptions Options for loading the stream.
+ */
+function ZipEntries(loadOptions) {
+ this.files = [];
+ this.loadOptions = loadOptions;
+}
+ZipEntries.prototype = {
+ /**
+ * Check that the reader is on the specified signature.
+ * @param {string} expectedSignature the expected signature.
+ * @throws {Error} if it is an other signature.
+ */
+ checkSignature: function(expectedSignature) {
+ if (!this.reader.readAndCheckSignature(expectedSignature)) {
+ this.reader.index -= 4;
+ var signature = this.reader.readString(4);
+ throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")");
+ }
+ },
+ /**
+ * Check if the given signature is at the given index.
+ * @param {number} askedIndex the index to check.
+ * @param {string} expectedSignature the signature to expect.
+ * @return {boolean} true if the signature is here, false otherwise.
+ */
+ isSignature: function(askedIndex, expectedSignature) {
+ var currentIndex = this.reader.index;
+ this.reader.setIndex(askedIndex);
+ var signature = this.reader.readString(4);
+ var result = signature === expectedSignature;
+ this.reader.setIndex(currentIndex);
+ return result;
+ },
+ /**
+ * Read the end of the central directory.
+ */
+ readBlockEndOfCentral: function() {
+ this.diskNumber = this.reader.readInt(2);
+ this.diskWithCentralDirStart = this.reader.readInt(2);
+ this.centralDirRecordsOnThisDisk = this.reader.readInt(2);
+ this.centralDirRecords = this.reader.readInt(2);
+ this.centralDirSize = this.reader.readInt(4);
+ this.centralDirOffset = this.reader.readInt(4);
+
+ this.zipCommentLength = this.reader.readInt(2);
+ // warning : the encoding depends of the system locale
+ // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded.
+ // On a windows machine, this field is encoded with the localized windows code page.
+ var zipComment = this.reader.readData(this.zipCommentLength);
+ var decodeParamType = support.uint8array ? "uint8array" : "array";
+ // To get consistent behavior with the generation part, we will assume that
+ // this is utf8 encoded unless specified otherwise.
+ var decodeContent = utils.transformTo(decodeParamType, zipComment);
+ this.zipComment = this.loadOptions.decodeFileName(decodeContent);
+ },
+ /**
+ * Read the end of the Zip 64 central directory.
+ * Not merged with the method readEndOfCentral :
+ * The end of central can coexist with its Zip64 brother,
+ * I don't want to read the wrong number of bytes !
+ */
+ readBlockZip64EndOfCentral: function() {
+ this.zip64EndOfCentralSize = this.reader.readInt(8);
+ this.reader.skip(4);
+ // this.versionMadeBy = this.reader.readString(2);
+ // this.versionNeeded = this.reader.readInt(2);
+ this.diskNumber = this.reader.readInt(4);
+ this.diskWithCentralDirStart = this.reader.readInt(4);
+ this.centralDirRecordsOnThisDisk = this.reader.readInt(8);
+ this.centralDirRecords = this.reader.readInt(8);
+ this.centralDirSize = this.reader.readInt(8);
+ this.centralDirOffset = this.reader.readInt(8);
+
+ this.zip64ExtensibleData = {};
+ var extraDataSize = this.zip64EndOfCentralSize - 44,
+ index = 0,
+ extraFieldId,
+ extraFieldLength,
+ extraFieldValue;
+ while (index < extraDataSize) {
+ extraFieldId = this.reader.readInt(2);
+ extraFieldLength = this.reader.readInt(4);
+ extraFieldValue = this.reader.readData(extraFieldLength);
+ this.zip64ExtensibleData[extraFieldId] = {
+ id: extraFieldId,
+ length: extraFieldLength,
+ value: extraFieldValue
+ };
+ }
+ },
+ /**
+ * Read the end of the Zip 64 central directory locator.
+ */
+ readBlockZip64EndOfCentralLocator: function() {
+ this.diskWithZip64CentralDirStart = this.reader.readInt(4);
+ this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8);
+ this.disksCount = this.reader.readInt(4);
+ if (this.disksCount > 1) {
+ throw new Error("Multi-volumes zip are not supported");
+ }
+ },
+ /**
+ * Read the local files, based on the offset read in the central part.
+ */
+ readLocalFiles: function() {
+ var i, file;
+ for (i = 0; i < this.files.length; i++) {
+ file = this.files[i];
+ this.reader.setIndex(file.localHeaderOffset);
+ this.checkSignature(sig.LOCAL_FILE_HEADER);
+ file.readLocalPart(this.reader);
+ file.handleUTF8();
+ file.processAttributes();
+ }
+ },
+ /**
+ * Read the central directory.
+ */
+ readCentralDir: function() {
+ var file;
+
+ this.reader.setIndex(this.centralDirOffset);
+ while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) {
+ file = new ZipEntry({
+ zip64: this.zip64
+ }, this.loadOptions);
+ file.readCentralPart(this.reader);
+ this.files.push(file);
+ }
+
+ if (this.centralDirRecords !== this.files.length) {
+ if (this.centralDirRecords !== 0 && this.files.length === 0) {
+ // We expected some records but couldn't find ANY.
+ // This is really suspicious, as if something went wrong.
+ throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length);
+ } else {
+ // We found some records but not all.
+ // Something is wrong but we got something for the user: no error here.
+ // console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length);
+ }
+ }
+ },
+ /**
+ * Read the end of central directory.
+ */
+ readEndOfCentral: function() {
+ var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END);
+ if (offset < 0) {
+ // Check if the content is a truncated zip or complete garbage.
+ // A "LOCAL_FILE_HEADER" is not required at the beginning (auto
+ // extractible zip for example) but it can give a good hint.
+ // If an ajax request was used without responseType, we will also
+ // get unreadable data.
+ var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER);
+
+ if (isGarbage) {
+ throw new Error("Can't find end of central directory : is this a zip file ? " +
+ "If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html");
+ } else {
+ throw new Error("Corrupted zip: can't find end of central directory");
+ }
+
+ }
+ this.reader.setIndex(offset);
+ var endOfCentralDirOffset = offset;
+ this.checkSignature(sig.CENTRAL_DIRECTORY_END);
+ this.readBlockEndOfCentral();
+
+
+ /* extract from the zip spec :
+ 4) If one of the fields in the end of central directory
+ record is too small to hold required data, the field
+ should be set to -1 (0xFFFF or 0xFFFFFFFF) and the
+ ZIP64 format record should be created.
+ 5) The end of central directory record and the
+ Zip64 end of central directory locator record must
+ reside on the same disk when splitting or spanning
+ an archive.
+ */
+ if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) {
+ this.zip64 = true;
+
+ /*
+ Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from
+ the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents
+ all numbers as 64-bit double precision IEEE 754 floating point numbers.
+ So, we have 53bits for integers and bitwise operations treat everything as 32bits.
+ see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
+ and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5
+ */
+
+ // should look for a zip64 EOCD locator
+ offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
+ if (offset < 0) {
+ throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator");
+ }
+ this.reader.setIndex(offset);
+ this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
+ this.readBlockZip64EndOfCentralLocator();
+
+ // now the zip64 EOCD record
+ if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) {
+ // console.warn("ZIP64 end of central directory not where expected.");
+ this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
+ if (this.relativeOffsetEndOfZip64CentralDir < 0) {
+ throw new Error("Corrupted zip: can't find the ZIP64 end of central directory");
+ }
+ }
+ this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir);
+ this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
+ this.readBlockZip64EndOfCentral();
+ }
+
+ var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize;
+ if (this.zip64) {
+ expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator
+ expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize;
+ }
+
+ var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset;
+
+ if (extraBytes > 0) {
+ // console.warn(extraBytes, "extra bytes at beginning or within zipfile");
+ if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) {
+ // The offsets seem wrong, but we have something at the specified offset.
+ // So… we keep it.
+ } else {
+ // the offset is wrong, update the "zero" of the reader
+ // this happens if data has been prepended (crx files for example)
+ this.reader.zero = extraBytes;
+ }
+ } else if (extraBytes < 0) {
+ throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes.");
+ }
+ },
+ prepareReader: function(data) {
+ this.reader = readerFor(data);
+ },
+ /**
+ * Read a zip file and create ZipEntries.
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file.
+ */
+ load: function(data) {
+ this.prepareReader(data);
+ this.readEndOfCentral();
+ this.readCentralDir();
+ this.readLocalFiles();
+ }
+};
+// }}} end of ZipEntries
+module.exports = ZipEntries;
+
+},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utf8":31,"./utils":32,"./zipEntry":34}],34:[function(require,module,exports){
+'use strict';
+var readerFor = require('./reader/readerFor');
+var utils = require('./utils');
+var CompressedObject = require('./compressedObject');
+var crc32fn = require('./crc32');
+var utf8 = require('./utf8');
+var compressions = require('./compressions');
+var support = require('./support');
+
+var MADE_BY_DOS = 0x00;
+var MADE_BY_UNIX = 0x03;
+
+/**
+ * Find a compression registered in JSZip.
+ * @param {string} compressionMethod the method magic to find.
+ * @return {Object|null} the JSZip compression object, null if none found.
+ */
+var findCompression = function(compressionMethod) {
+ for (var method in compressions) {
+ if (!compressions.hasOwnProperty(method)) {
+ continue;
+ }
+ if (compressions[method].magic === compressionMethod) {
+ return compressions[method];
+ }
+ }
+ return null;
+};
+
+// class ZipEntry {{{
+/**
+ * An entry in the zip file.
+ * @constructor
+ * @param {Object} options Options of the current file.
+ * @param {Object} loadOptions Options for loading the stream.
+ */
+function ZipEntry(options, loadOptions) {
+ this.options = options;
+ this.loadOptions = loadOptions;
+}
+ZipEntry.prototype = {
+ /**
+ * say if the file is encrypted.
+ * @return {boolean} true if the file is encrypted, false otherwise.
+ */
+ isEncrypted: function() {
+ // bit 1 is set
+ return (this.bitFlag & 0x0001) === 0x0001;
+ },
+ /**
+ * say if the file has utf-8 filename/comment.
+ * @return {boolean} true if the filename/comment is in utf-8, false otherwise.
+ */
+ useUTF8: function() {
+ // bit 11 is set
+ return (this.bitFlag & 0x0800) === 0x0800;
+ },
+ /**
+ * Read the local part of a zip file and add the info in this object.
+ * @param {DataReader} reader the reader to use.
+ */
+ readLocalPart: function(reader) {
+ var compression, localExtraFieldsLength;
+
+ // we already know everything from the central dir !
+ // If the central dir data are false, we are doomed.
+ // On the bright side, the local part is scary : zip64, data descriptors, both, etc.
+ // The less data we get here, the more reliable this should be.
+ // Let's skip the whole header and dash to the data !
+ reader.skip(22);
+ // in some zip created on windows, the filename stored in the central dir contains \ instead of /.
+ // Strangely, the filename here is OK.
+ // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes
+ // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators...
+ // Search "unzip mismatching "local" filename continuing with "central" filename version" on
+ // the internet.
+ //
+ // I think I see the logic here : the central directory is used to display
+ // content and the local directory is used to extract the files. Mixing / and \
+ // may be used to display \ to windows users and use / when extracting the files.
+ // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394
+ this.fileNameLength = reader.readInt(2);
+ localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir
+ // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding.
+ this.fileName = reader.readData(this.fileNameLength);
+ reader.skip(localExtraFieldsLength);
+
+ if (this.compressedSize === -1 || this.uncompressedSize === -1) {
+ throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)");
+ }
+
+ compression = findCompression(this.compressionMethod);
+ if (compression === null) { // no compression found
+ throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")");
+ }
+ this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize));
+ },
+
+ /**
+ * Read the central part of a zip file and add the info in this object.
+ * @param {DataReader} reader the reader to use.
+ */
+ readCentralPart: function(reader) {
+ this.versionMadeBy = reader.readInt(2);
+ reader.skip(2);
+ // this.versionNeeded = reader.readInt(2);
+ this.bitFlag = reader.readInt(2);
+ this.compressionMethod = reader.readString(2);
+ this.date = reader.readDate();
+ this.crc32 = reader.readInt(4);
+ this.compressedSize = reader.readInt(4);
+ this.uncompressedSize = reader.readInt(4);
+ var fileNameLength = reader.readInt(2);
+ this.extraFieldsLength = reader.readInt(2);
+ this.fileCommentLength = reader.readInt(2);
+ this.diskNumberStart = reader.readInt(2);
+ this.internalFileAttributes = reader.readInt(2);
+ this.externalFileAttributes = reader.readInt(4);
+ this.localHeaderOffset = reader.readInt(4);
+
+ if (this.isEncrypted()) {
+ throw new Error("Encrypted zip are not supported");
+ }
+
+ // will be read in the local part, see the comments there
+ reader.skip(fileNameLength);
+ this.readExtraFields(reader);
+ this.parseZIP64ExtraField(reader);
+ this.fileComment = reader.readData(this.fileCommentLength);
+ },
+
+ /**
+ * Parse the external file attributes and get the unix/dos permissions.
+ */
+ processAttributes: function () {
+ this.unixPermissions = null;
+ this.dosPermissions = null;
+ var madeBy = this.versionMadeBy >> 8;
+
+ // Check if we have the DOS directory flag set.
+ // We look for it in the DOS and UNIX permissions
+ // but some unknown platform could set it as a compatibility flag.
+ this.dir = this.externalFileAttributes & 0x0010 ? true : false;
+
+ if(madeBy === MADE_BY_DOS) {
+ // first 6 bits (0 to 5)
+ this.dosPermissions = this.externalFileAttributes & 0x3F;
+ }
+
+ if(madeBy === MADE_BY_UNIX) {
+ this.unixPermissions = (this.externalFileAttributes >> 16) & 0xFFFF;
+ // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8);
+ }
+
+ // fail safe : if the name ends with a / it probably means a folder
+ if (!this.dir && this.fileNameStr.slice(-1) === '/') {
+ this.dir = true;
+ }
+ },
+
+ /**
+ * Parse the ZIP64 extra field and merge the info in the current ZipEntry.
+ * @param {DataReader} reader the reader to use.
+ */
+ parseZIP64ExtraField: function(reader) {
+
+ if (!this.extraFields[0x0001]) {
+ return;
+ }
+
+ // should be something, preparing the extra reader
+ var extraReader = readerFor(this.extraFields[0x0001].value);
+
+ // I really hope that these 64bits integer can fit in 32 bits integer, because js
+ // won't let us have more.
+ if (this.uncompressedSize === utils.MAX_VALUE_32BITS) {
+ this.uncompressedSize = extraReader.readInt(8);
+ }
+ if (this.compressedSize === utils.MAX_VALUE_32BITS) {
+ this.compressedSize = extraReader.readInt(8);
+ }
+ if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) {
+ this.localHeaderOffset = extraReader.readInt(8);
+ }
+ if (this.diskNumberStart === utils.MAX_VALUE_32BITS) {
+ this.diskNumberStart = extraReader.readInt(4);
+ }
+ },
+ /**
+ * Read the central part of a zip file and add the info in this object.
+ * @param {DataReader} reader the reader to use.
+ */
+ readExtraFields: function(reader) {
+ var end = reader.index + this.extraFieldsLength,
+ extraFieldId,
+ extraFieldLength,
+ extraFieldValue;
+
+ if (!this.extraFields) {
+ this.extraFields = {};
+ }
+
+ while (reader.index < end) {
+ extraFieldId = reader.readInt(2);
+ extraFieldLength = reader.readInt(2);
+ extraFieldValue = reader.readData(extraFieldLength);
+
+ this.extraFields[extraFieldId] = {
+ id: extraFieldId,
+ length: extraFieldLength,
+ value: extraFieldValue
+ };
+ }
+ },
+ /**
+ * Apply an UTF8 transformation if needed.
+ */
+ handleUTF8: function() {
+ var decodeParamType = support.uint8array ? "uint8array" : "array";
+ if (this.useUTF8()) {
+ this.fileNameStr = utf8.utf8decode(this.fileName);
+ this.fileCommentStr = utf8.utf8decode(this.fileComment);
+ } else {
+ var upath = this.findExtraFieldUnicodePath();
+ if (upath !== null) {
+ this.fileNameStr = upath;
+ } else {
+ // ASCII text or unsupported code page
+ var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName);
+ this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray);
+ }
+
+ var ucomment = this.findExtraFieldUnicodeComment();
+ if (ucomment !== null) {
+ this.fileCommentStr = ucomment;
+ } else {
+ // ASCII text or unsupported code page
+ var commentByteArray = utils.transformTo(decodeParamType, this.fileComment);
+ this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray);
+ }
+ }
+ },
+
+ /**
+ * Find the unicode path declared in the extra field, if any.
+ * @return {String} the unicode path, null otherwise.
+ */
+ findExtraFieldUnicodePath: function() {
+ var upathField = this.extraFields[0x7075];
+ if (upathField) {
+ var extraReader = readerFor(upathField.value);
+
+ // wrong version
+ if (extraReader.readInt(1) !== 1) {
+ return null;
+ }
+
+ // the crc of the filename changed, this field is out of date.
+ if (crc32fn(this.fileName) !== extraReader.readInt(4)) {
+ return null;
+ }
+
+ return utf8.utf8decode(extraReader.readData(upathField.length - 5));
+ }
+ return null;
+ },
+
+ /**
+ * Find the unicode comment declared in the extra field, if any.
+ * @return {String} the unicode comment, null otherwise.
+ */
+ findExtraFieldUnicodeComment: function() {
+ var ucommentField = this.extraFields[0x6375];
+ if (ucommentField) {
+ var extraReader = readerFor(ucommentField.value);
+
+ // wrong version
+ if (extraReader.readInt(1) !== 1) {
+ return null;
+ }
+
+ // the crc of the comment changed, this field is out of date.
+ if (crc32fn(this.fileComment) !== extraReader.readInt(4)) {
+ return null;
+ }
+
+ return utf8.utf8decode(extraReader.readData(ucommentField.length - 5));
+ }
+ return null;
+ }
+};
+module.exports = ZipEntry;
+
+},{"./compressedObject":2,"./compressions":3,"./crc32":4,"./reader/readerFor":22,"./support":30,"./utf8":31,"./utils":32}],35:[function(require,module,exports){
+'use strict';
+
+var StreamHelper = require('./stream/StreamHelper');
+var DataWorker = require('./stream/DataWorker');
+var utf8 = require('./utf8');
+var CompressedObject = require('./compressedObject');
+var GenericWorker = require('./stream/GenericWorker');
+
+/**
+ * A simple object representing a file in the zip file.
+ * @constructor
+ * @param {string} name the name of the file
+ * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
+ * @param {Object} options the options of the file
+ */
+var ZipObject = function(name, data, options) {
+ this.name = name;
+ this.dir = options.dir;
+ this.date = options.date;
+ this.comment = options.comment;
+ this.unixPermissions = options.unixPermissions;
+ this.dosPermissions = options.dosPermissions;
+
+ this._data = data;
+ this._dataBinary = options.binary;
+ // keep only the compression
+ this.options = {
+ compression : options.compression,
+ compressionOptions : options.compressionOptions
+ };
+};
+
+ZipObject.prototype = {
+ /**
+ * Create an internal stream for the content of this object.
+ * @param {String} type the type of each chunk.
+ * @return StreamHelper the stream.
+ */
+ internalStream: function (type) {
+ var result = null, outputType = "string";
+ try {
+ if (!type) {
+ throw new Error("No output type specified.");
+ }
+ outputType = type.toLowerCase();
+ var askUnicodeString = outputType === "string" || outputType === "text";
+ if (outputType === "binarystring" || outputType === "text") {
+ outputType = "string";
+ }
+ result = this._decompressWorker();
+
+ var isUnicodeString = !this._dataBinary;
+
+ if (isUnicodeString && !askUnicodeString) {
+ result = result.pipe(new utf8.Utf8EncodeWorker());
+ }
+ if (!isUnicodeString && askUnicodeString) {
+ result = result.pipe(new utf8.Utf8DecodeWorker());
+ }
+ } catch (e) {
+ result = new GenericWorker("error");
+ result.error(e);
+ }
+
+ return new StreamHelper(result, outputType, "");
+ },
+
+ /**
+ * Prepare the content in the asked type.
+ * @param {String} type the type of the result.
+ * @param {Function} onUpdate a function to call on each internal update.
+ * @return Promise the promise of the result.
+ */
+ async: function (type, onUpdate) {
+ return this.internalStream(type).accumulate(onUpdate);
+ },
+
+ /**
+ * Prepare the content as a nodejs stream.
+ * @param {String} type the type of each chunk.
+ * @param {Function} onUpdate a function to call on each internal update.
+ * @return Stream the stream.
+ */
+ nodeStream: function (type, onUpdate) {
+ return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate);
+ },
+
+ /**
+ * Return a worker for the compressed content.
+ * @private
+ * @param {Object} compression the compression object to use.
+ * @param {Object} compressionOptions the options to use when compressing.
+ * @return Worker the worker.
+ */
+ _compressWorker: function (compression, compressionOptions) {
+ if (
+ this._data instanceof CompressedObject &&
+ this._data.compression.magic === compression.magic
+ ) {
+ return this._data.getCompressedWorker();
+ } else {
+ var result = this._decompressWorker();
+ if(!this._dataBinary) {
+ result = result.pipe(new utf8.Utf8EncodeWorker());
+ }
+ return CompressedObject.createWorkerFrom(result, compression, compressionOptions);
+ }
+ },
+ /**
+ * Return a worker for the decompressed content.
+ * @private
+ * @return Worker the worker.
+ */
+ _decompressWorker : function () {
+ if (this._data instanceof CompressedObject) {
+ return this._data.getContentWorker();
+ } else if (this._data instanceof GenericWorker) {
+ return this._data;
+ } else {
+ return new DataWorker(this._data);
+ }
+ }
+};
+
+var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"];
+var removedFn = function () {
+ throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
+};
+
+for(var i = 0; i < removedMethods.length; i++) {
+ ZipObject.prototype[removedMethods[i]] = removedFn;
+}
+module.exports = ZipObject;
+
+},{"./compressedObject":2,"./stream/DataWorker":27,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31}],36:[function(require,module,exports){
+(function (global){
+'use strict';
+var Mutation = global.MutationObserver || global.WebKitMutationObserver;
+
+var scheduleDrain;
+
+{
+ if (Mutation) {
+ var called = 0;
+ var observer = new Mutation(nextTick);
+ var element = global.document.createTextNode('');
+ observer.observe(element, {
+ characterData: true
+ });
+ scheduleDrain = function () {
+ element.data = (called = ++called % 2);
+ };
+ } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
+ var channel = new global.MessageChannel();
+ channel.port1.onmessage = nextTick;
+ scheduleDrain = function () {
+ channel.port2.postMessage(0);
+ };
+ } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
+ scheduleDrain = function () {
+
+ // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
+ // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
+ var scriptEl = global.document.createElement('script');
+ scriptEl.onreadystatechange = function () {
+ nextTick();
+
+ scriptEl.onreadystatechange = null;
+ scriptEl.parentNode.removeChild(scriptEl);
+ scriptEl = null;
+ };
+ global.document.documentElement.appendChild(scriptEl);
+ };
+ } else {
+ scheduleDrain = function () {
+ setTimeout(nextTick, 0);
+ };
+ }
+}
+
+var draining;
+var queue = [];
+//named nextTick for less confusing stack traces
+function nextTick() {
+ draining = true;
+ var i, oldQueue;
+ var len = queue.length;
+ while (len) {
+ oldQueue = queue;
+ queue = [];
+ i = -1;
+ while (++i < len) {
+ oldQueue[i]();
+ }
+ len = queue.length;
+ }
+ draining = false;
+}
+
+module.exports = immediate;
+function immediate(task) {
+ if (queue.push(task) === 1 && !draining) {
+ scheduleDrain();
+ }
+}
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],37:[function(require,module,exports){
+'use strict';
+var immediate = require('immediate');
+
+/* istanbul ignore next */
+function INTERNAL() {}
+
+var handlers = {};
+
+var REJECTED = ['REJECTED'];
+var FULFILLED = ['FULFILLED'];
+var PENDING = ['PENDING'];
+
+module.exports = Promise;
+
+function Promise(resolver) {
+ if (typeof resolver !== 'function') {
+ throw new TypeError('resolver must be a function');
+ }
+ this.state = PENDING;
+ this.queue = [];
+ this.outcome = void 0;
+ if (resolver !== INTERNAL) {
+ safelyResolveThenable(this, resolver);
+ }
+}
+
+Promise.prototype["finally"] = function (callback) {
+ if (typeof callback !== 'function') {
+ return this;
+ }
+ var p = this.constructor;
+ return this.then(resolve, reject);
+
+ function resolve(value) {
+ function yes () {
+ return value;
+ }
+ return p.resolve(callback()).then(yes);
+ }
+ function reject(reason) {
+ function no () {
+ throw reason;
+ }
+ return p.resolve(callback()).then(no);
+ }
+};
+Promise.prototype["catch"] = function (onRejected) {
+ return this.then(null, onRejected);
+};
+Promise.prototype.then = function (onFulfilled, onRejected) {
+ if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
+ typeof onRejected !== 'function' && this.state === REJECTED) {
+ return this;
+ }
+ var promise = new this.constructor(INTERNAL);
+ if (this.state !== PENDING) {
+ var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
+ unwrap(promise, resolver, this.outcome);
+ } else {
+ this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
+ }
+
+ return promise;
+};
+function QueueItem(promise, onFulfilled, onRejected) {
+ this.promise = promise;
+ if (typeof onFulfilled === 'function') {
+ this.onFulfilled = onFulfilled;
+ this.callFulfilled = this.otherCallFulfilled;
+ }
+ if (typeof onRejected === 'function') {
+ this.onRejected = onRejected;
+ this.callRejected = this.otherCallRejected;
+ }
+}
+QueueItem.prototype.callFulfilled = function (value) {
+ handlers.resolve(this.promise, value);
+};
+QueueItem.prototype.otherCallFulfilled = function (value) {
+ unwrap(this.promise, this.onFulfilled, value);
+};
+QueueItem.prototype.callRejected = function (value) {
+ handlers.reject(this.promise, value);
+};
+QueueItem.prototype.otherCallRejected = function (value) {
+ unwrap(this.promise, this.onRejected, value);
+};
+
+function unwrap(promise, func, value) {
+ immediate(function () {
+ var returnValue;
+ try {
+ returnValue = func(value);
+ } catch (e) {
+ return handlers.reject(promise, e);
+ }
+ if (returnValue === promise) {
+ handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
+ } else {
+ handlers.resolve(promise, returnValue);
+ }
+ });
+}
+
+handlers.resolve = function (self, value) {
+ var result = tryCatch(getThen, value);
+ if (result.status === 'error') {
+ return handlers.reject(self, result.value);
+ }
+ var thenable = result.value;
+
+ if (thenable) {
+ safelyResolveThenable(self, thenable);
+ } else {
+ self.state = FULFILLED;
+ self.outcome = value;
+ var i = -1;
+ var len = self.queue.length;
+ while (++i < len) {
+ self.queue[i].callFulfilled(value);
+ }
+ }
+ return self;
+};
+handlers.reject = function (self, error) {
+ self.state = REJECTED;
+ self.outcome = error;
+ var i = -1;
+ var len = self.queue.length;
+ while (++i < len) {
+ self.queue[i].callRejected(error);
+ }
+ return self;
+};
+
+function getThen(obj) {
+ // Make sure we only access the accessor once as required by the spec
+ var then = obj && obj.then;
+ if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
+ return function appyThen() {
+ then.apply(obj, arguments);
+ };
+ }
+}
+
+function safelyResolveThenable(self, thenable) {
+ // Either fulfill, reject or reject with error
+ var called = false;
+ function onError(value) {
+ if (called) {
+ return;
+ }
+ called = true;
+ handlers.reject(self, value);
+ }
+
+ function onSuccess(value) {
+ if (called) {
+ return;
+ }
+ called = true;
+ handlers.resolve(self, value);
+ }
+
+ function tryToUnwrap() {
+ thenable(onSuccess, onError);
+ }
+
+ var result = tryCatch(tryToUnwrap);
+ if (result.status === 'error') {
+ onError(result.value);
+ }
+}
+
+function tryCatch(func, value) {
+ var out = {};
+ try {
+ out.value = func(value);
+ out.status = 'success';
+ } catch (e) {
+ out.status = 'error';
+ out.value = e;
+ }
+ return out;
+}
+
+Promise.resolve = resolve;
+function resolve(value) {
+ if (value instanceof this) {
+ return value;
+ }
+ return handlers.resolve(new this(INTERNAL), value);
+}
+
+Promise.reject = reject;
+function reject(reason) {
+ var promise = new this(INTERNAL);
+ return handlers.reject(promise, reason);
+}
+
+Promise.all = all;
+function all(iterable) {
+ var self = this;
+ if (Object.prototype.toString.call(iterable) !== '[object Array]') {
+ return this.reject(new TypeError('must be an array'));
+ }
+
+ var len = iterable.length;
+ var called = false;
+ if (!len) {
+ return this.resolve([]);
+ }
+
+ var values = new Array(len);
+ var resolved = 0;
+ var i = -1;
+ var promise = new this(INTERNAL);
+
+ while (++i < len) {
+ allResolver(iterable[i], i);
+ }
+ return promise;
+ function allResolver(value, i) {
+ self.resolve(value).then(resolveFromAll, function (error) {
+ if (!called) {
+ called = true;
+ handlers.reject(promise, error);
+ }
+ });
+ function resolveFromAll(outValue) {
+ values[i] = outValue;
+ if (++resolved === len && !called) {
+ called = true;
+ handlers.resolve(promise, values);
+ }
+ }
+ }
+}
+
+Promise.race = race;
+function race(iterable) {
+ var self = this;
+ if (Object.prototype.toString.call(iterable) !== '[object Array]') {
+ return this.reject(new TypeError('must be an array'));
+ }
+
+ var len = iterable.length;
+ var called = false;
+ if (!len) {
+ return this.resolve([]);
+ }
+
+ var i = -1;
+ var promise = new this(INTERNAL);
+
+ while (++i < len) {
+ resolver(iterable[i]);
+ }
+ return promise;
+ function resolver(value) {
+ self.resolve(value).then(function (response) {
+ if (!called) {
+ called = true;
+ handlers.resolve(promise, response);
+ }
+ }, function (error) {
+ if (!called) {
+ called = true;
+ handlers.reject(promise, error);
+ }
+ });
+ }
+}
+
+},{"immediate":36}],38:[function(require,module,exports){
+// Top level file is just a mixin of submodules & constants
+'use strict';
+
+var assign = require('./lib/utils/common').assign;
+
+var deflate = require('./lib/deflate');
+var inflate = require('./lib/inflate');
+var constants = require('./lib/zlib/constants');
+
+var pako = {};
+
+assign(pako, deflate, inflate, constants);
+
+module.exports = pako;
+
+},{"./lib/deflate":39,"./lib/inflate":40,"./lib/utils/common":41,"./lib/zlib/constants":44}],39:[function(require,module,exports){
+'use strict';
+
+
+var zlib_deflate = require('./zlib/deflate');
+var utils = require('./utils/common');
+var strings = require('./utils/strings');
+var msg = require('./zlib/messages');
+var ZStream = require('./zlib/zstream');
+
+var toString = Object.prototype.toString;
+
+/* Public constants ==========================================================*/
+/* ===========================================================================*/
+
+var Z_NO_FLUSH = 0;
+var Z_FINISH = 4;
+
+var Z_OK = 0;
+var Z_STREAM_END = 1;
+var Z_SYNC_FLUSH = 2;
+
+var Z_DEFAULT_COMPRESSION = -1;
+
+var Z_DEFAULT_STRATEGY = 0;
+
+var Z_DEFLATED = 8;
+
+/* ===========================================================================*/
+
+
+/**
+ * class Deflate
+ *
+ * Generic JS-style wrapper for zlib calls. If you don't need
+ * streaming behaviour - use more simple functions: [[deflate]],
+ * [[deflateRaw]] and [[gzip]].
+ **/
+
+/* internal
+ * Deflate.chunks -> Array
+ *
+ * Chunks of output data, if [[Deflate#onData]] not overriden.
+ **/
+
+/**
+ * Deflate.result -> Uint8Array|Array
+ *
+ * Compressed result, generated by default [[Deflate#onData]]
+ * and [[Deflate#onEnd]] handlers. Filled after you push last chunk
+ * (call [[Deflate#push]] with `Z_FINISH` / `true` param) or if you
+ * push a chunk with explicit flush (call [[Deflate#push]] with
+ * `Z_SYNC_FLUSH` param).
+ **/
+
+/**
+ * Deflate.err -> Number
+ *
+ * Error code after deflate finished. 0 (Z_OK) on success.
+ * You will not need it in real life, because deflate errors
+ * are possible only on wrong options or bad `onData` / `onEnd`
+ * custom handlers.
+ **/
+
+/**
+ * Deflate.msg -> String
+ *
+ * Error message, if [[Deflate.err]] != 0
+ **/
+
+
+/**
+ * new Deflate(options)
+ * - options (Object): zlib deflate options.
+ *
+ * Creates new deflator instance with specified params. Throws exception
+ * on bad params. Supported options:
+ *
+ * - `level`
+ * - `windowBits`
+ * - `memLevel`
+ * - `strategy`
+ * - `dictionary`
+ *
+ * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
+ * for more information on these.
+ *
+ * Additional options, for internal needs:
+ *
+ * - `chunkSize` - size of generated data chunks (16K by default)
+ * - `raw` (Boolean) - do raw deflate
+ * - `gzip` (Boolean) - create gzip wrapper
+ * - `to` (String) - if equal to 'string', then result will be "binary string"
+ * (each char code [0..255])
+ * - `header` (Object) - custom header for gzip
+ * - `text` (Boolean) - true if compressed data believed to be text
+ * - `time` (Number) - modification time, unix timestamp
+ * - `os` (Number) - operation system code
+ * - `extra` (Array) - array of bytes with extra data (max 65536)
+ * - `name` (String) - file name (binary string)
+ * - `comment` (String) - comment (binary string)
+ * - `hcrc` (Boolean) - true if header crc should be added
+ *
+ * ##### Example:
+ *
+ * ```javascript
+ * var pako = require('pako')
+ * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
+ * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
+ *
+ * var deflate = new pako.Deflate({ level: 3});
+ *
+ * deflate.push(chunk1, false);
+ * deflate.push(chunk2, true); // true -> last chunk
+ *
+ * if (deflate.err) { throw new Error(deflate.err); }
+ *
+ * console.log(deflate.result);
+ * ```
+ **/
+function Deflate(options) {
+ if (!(this instanceof Deflate)) return new Deflate(options);
+
+ this.options = utils.assign({
+ level: Z_DEFAULT_COMPRESSION,
+ method: Z_DEFLATED,
+ chunkSize: 16384,
+ windowBits: 15,
+ memLevel: 8,
+ strategy: Z_DEFAULT_STRATEGY,
+ to: ''
+ }, options || {});
+
+ var opt = this.options;
+
+ if (opt.raw && (opt.windowBits > 0)) {
+ opt.windowBits = -opt.windowBits;
+ }
+
+ else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
+ opt.windowBits += 16;
+ }
+
+ this.err = 0; // error code, if happens (0 = Z_OK)
+ this.msg = ''; // error message
+ this.ended = false; // used to avoid multiple onEnd() calls
+ this.chunks = []; // chunks of compressed data
+
+ this.strm = new ZStream();
+ this.strm.avail_out = 0;
+
+ var status = zlib_deflate.deflateInit2(
+ this.strm,
+ opt.level,
+ opt.method,
+ opt.windowBits,
+ opt.memLevel,
+ opt.strategy
+ );
+
+ if (status !== Z_OK) {
+ throw new Error(msg[status]);
+ }
+
+ if (opt.header) {
+ zlib_deflate.deflateSetHeader(this.strm, opt.header);
+ }
+
+ if (opt.dictionary) {
+ var dict;
+ // Convert data if needed
+ if (typeof opt.dictionary === 'string') {
+ // If we need to compress text, change encoding to utf8.
+ dict = strings.string2buf(opt.dictionary);
+ } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
+ dict = new Uint8Array(opt.dictionary);
+ } else {
+ dict = opt.dictionary;
+ }
+
+ status = zlib_deflate.deflateSetDictionary(this.strm, dict);
+
+ if (status !== Z_OK) {
+ throw new Error(msg[status]);
+ }
+
+ this._dict_set = true;
+ }
+}
+
+/**
+ * Deflate#push(data[, mode]) -> Boolean
+ * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be
+ * converted to utf8 byte sequence.
+ * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
+ * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
+ *
+ * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
+ * new compressed chunks. Returns `true` on success. The last data block must have
+ * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
+ * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you
+ * can use mode Z_SYNC_FLUSH, keeping the compression context.
+ *
+ * On fail call [[Deflate#onEnd]] with error code and return false.
+ *
+ * We strongly recommend to use `Uint8Array` on input for best speed (output
+ * array format is detected automatically). Also, don't skip last param and always
+ * use the same type in your code (boolean or number). That will improve JS speed.
+ *
+ * For regular `Array`-s make sure all elements are [0..255].
+ *
+ * ##### Example
+ *
+ * ```javascript
+ * push(chunk, false); // push one of data chunks
+ * ...
+ * push(chunk, true); // push last chunk
+ * ```
+ **/
+Deflate.prototype.push = function (data, mode) {
+ var strm = this.strm;
+ var chunkSize = this.options.chunkSize;
+ var status, _mode;
+
+ if (this.ended) { return false; }
+
+ _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);
+
+ // Convert data if needed
+ if (typeof data === 'string') {
+ // If we need to compress text, change encoding to utf8.
+ strm.input = strings.string2buf(data);
+ } else if (toString.call(data) === '[object ArrayBuffer]') {
+ strm.input = new Uint8Array(data);
+ } else {
+ strm.input = data;
+ }
+
+ strm.next_in = 0;
+ strm.avail_in = strm.input.length;
+
+ do {
+ if (strm.avail_out === 0) {
+ strm.output = new utils.Buf8(chunkSize);
+ strm.next_out = 0;
+ strm.avail_out = chunkSize;
+ }
+ status = zlib_deflate.deflate(strm, _mode); /* no bad return value */
+
+ if (status !== Z_STREAM_END && status !== Z_OK) {
+ this.onEnd(status);
+ this.ended = true;
+ return false;
+ }
+ if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) {
+ if (this.options.to === 'string') {
+ this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));
+ } else {
+ this.onData(utils.shrinkBuf(strm.output, strm.next_out));
+ }
+ }
+ } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);
+
+ // Finalize on the last chunk.
+ if (_mode === Z_FINISH) {
+ status = zlib_deflate.deflateEnd(this.strm);
+ this.onEnd(status);
+ this.ended = true;
+ return status === Z_OK;
+ }
+
+ // callback interim results if Z_SYNC_FLUSH.
+ if (_mode === Z_SYNC_FLUSH) {
+ this.onEnd(Z_OK);
+ strm.avail_out = 0;
+ return true;
+ }
+
+ return true;
+};
+
+
+/**
+ * Deflate#onData(chunk) -> Void
+ * - chunk (Uint8Array|Array|String): ouput data. Type of array depends
+ * on js engine support. When string output requested, each chunk
+ * will be string.
+ *
+ * By default, stores data blocks in `chunks[]` property and glue
+ * those in `onEnd`. Override this handler, if you need another behaviour.
+ **/
+Deflate.prototype.onData = function (chunk) {
+ this.chunks.push(chunk);
+};
+
+
+/**
+ * Deflate#onEnd(status) -> Void
+ * - status (Number): deflate status. 0 (Z_OK) on success,
+ * other if not.
+ *
+ * Called once after you tell deflate that the input stream is
+ * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
+ * or if an error happened. By default - join collected chunks,
+ * free memory and fill `results` / `err` properties.
+ **/
+Deflate.prototype.onEnd = function (status) {
+ // On success - join
+ if (status === Z_OK) {
+ if (this.options.to === 'string') {
+ this.result = this.chunks.join('');
+ } else {
+ this.result = utils.flattenChunks(this.chunks);
+ }
+ }
+ this.chunks = [];
+ this.err = status;
+ this.msg = this.strm.msg;
+};
+
+
+/**
+ * deflate(data[, options]) -> Uint8Array|Array|String
+ * - data (Uint8Array|Array|String): input data to compress.
+ * - options (Object): zlib deflate options.
+ *
+ * Compress `data` with deflate algorithm and `options`.
+ *
+ * Supported options are:
+ *
+ * - level
+ * - windowBits
+ * - memLevel
+ * - strategy
+ * - dictionary
+ *
+ * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
+ * for more information on these.
+ *
+ * Sugar (options):
+ *
+ * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
+ * negative windowBits implicitly.
+ * - `to` (String) - if equal to 'string', then result will be "binary string"
+ * (each char code [0..255])
+ *
+ * ##### Example:
+ *
+ * ```javascript
+ * var pako = require('pako')
+ * , data = Uint8Array([1,2,3,4,5,6,7,8,9]);
+ *
+ * console.log(pako.deflate(data));
+ * ```
+ **/
+function deflate(input, options) {
+ var deflator = new Deflate(options);
+
+ deflator.push(input, true);
+
+ // That will never happens, if you don't cheat with options :)
+ if (deflator.err) { throw deflator.msg || msg[deflator.err]; }
+
+ return deflator.result;
+}
+
+
+/**
+ * deflateRaw(data[, options]) -> Uint8Array|Array|String
+ * - data (Uint8Array|Array|String): input data to compress.
+ * - options (Object): zlib deflate options.
+ *
+ * The same as [[deflate]], but creates raw data, without wrapper
+ * (header and adler32 crc).
+ **/
+function deflateRaw(input, options) {
+ options = options || {};
+ options.raw = true;
+ return deflate(input, options);
+}
+
+
+/**
+ * gzip(data[, options]) -> Uint8Array|Array|String
+ * - data (Uint8Array|Array|String): input data to compress.
+ * - options (Object): zlib deflate options.
+ *
+ * The same as [[deflate]], but create gzip wrapper instead of
+ * deflate one.
+ **/
+function gzip(input, options) {
+ options = options || {};
+ options.gzip = true;
+ return deflate(input, options);
+}
+
+
+exports.Deflate = Deflate;
+exports.deflate = deflate;
+exports.deflateRaw = deflateRaw;
+exports.gzip = gzip;
+
+},{"./utils/common":41,"./utils/strings":42,"./zlib/deflate":46,"./zlib/messages":51,"./zlib/zstream":53}],40:[function(require,module,exports){
+'use strict';
+
+
+var zlib_inflate = require('./zlib/inflate');
+var utils = require('./utils/common');
+var strings = require('./utils/strings');
+var c = require('./zlib/constants');
+var msg = require('./zlib/messages');
+var ZStream = require('./zlib/zstream');
+var GZheader = require('./zlib/gzheader');
+
+var toString = Object.prototype.toString;
+
+/**
+ * class Inflate
+ *
+ * Generic JS-style wrapper for zlib calls. If you don't need
+ * streaming behaviour - use more simple functions: [[inflate]]
+ * and [[inflateRaw]].
+ **/
+
+/* internal
+ * inflate.chunks -> Array
+ *
+ * Chunks of output data, if [[Inflate#onData]] not overriden.
+ **/
+
+/**
+ * Inflate.result -> Uint8Array|Array|String
+ *
+ * Uncompressed result, generated by default [[Inflate#onData]]
+ * and [[Inflate#onEnd]] handlers. Filled after you push last chunk
+ * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you
+ * push a chunk with explicit flush (call [[Inflate#push]] with
+ * `Z_SYNC_FLUSH` param).
+ **/
+
+/**
+ * Inflate.err -> Number
+ *
+ * Error code after inflate finished. 0 (Z_OK) on success.
+ * Should be checked if broken data possible.
+ **/
+
+/**
+ * Inflate.msg -> String
+ *
+ * Error message, if [[Inflate.err]] != 0
+ **/
+
+
+/**
+ * new Inflate(options)
+ * - options (Object): zlib inflate options.
+ *
+ * Creates new inflator instance with specified params. Throws exception
+ * on bad params. Supported options:
+ *
+ * - `windowBits`
+ * - `dictionary`
+ *
+ * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
+ * for more information on these.
+ *
+ * Additional options, for internal needs:
+ *
+ * - `chunkSize` - size of generated data chunks (16K by default)
+ * - `raw` (Boolean) - do raw inflate
+ * - `to` (String) - if equal to 'string', then result will be converted
+ * from utf8 to utf16 (javascript) string. When string output requested,
+ * chunk length can differ from `chunkSize`, depending on content.
+ *
+ * By default, when no options set, autodetect deflate/gzip data format via
+ * wrapper header.
+ *
+ * ##### Example:
+ *
+ * ```javascript
+ * var pako = require('pako')
+ * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
+ * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
+ *
+ * var inflate = new pako.Inflate({ level: 3});
+ *
+ * inflate.push(chunk1, false);
+ * inflate.push(chunk2, true); // true -> last chunk
+ *
+ * if (inflate.err) { throw new Error(inflate.err); }
+ *
+ * console.log(inflate.result);
+ * ```
+ **/
+function Inflate(options) {
+ if (!(this instanceof Inflate)) return new Inflate(options);
+
+ this.options = utils.assign({
+ chunkSize: 16384,
+ windowBits: 0,
+ to: ''
+ }, options || {});
+
+ var opt = this.options;
+
+ // Force window size for `raw` data, if not set directly,
+ // because we have no header for autodetect.
+ if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
+ opt.windowBits = -opt.windowBits;
+ if (opt.windowBits === 0) { opt.windowBits = -15; }
+ }
+
+ // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
+ if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&
+ !(options && options.windowBits)) {
+ opt.windowBits += 32;
+ }
+
+ // Gzip header has no info about windows size, we can do autodetect only
+ // for deflate. So, if window size not set, force it to max when gzip possible
+ if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
+ // bit 3 (16) -> gzipped data
+ // bit 4 (32) -> autodetect gzip/deflate
+ if ((opt.windowBits & 15) === 0) {
+ opt.windowBits |= 15;
+ }
+ }
+
+ this.err = 0; // error code, if happens (0 = Z_OK)
+ this.msg = ''; // error message
+ this.ended = false; // used to avoid multiple onEnd() calls
+ this.chunks = []; // chunks of compressed data
+
+ this.strm = new ZStream();
+ this.strm.avail_out = 0;
+
+ var status = zlib_inflate.inflateInit2(
+ this.strm,
+ opt.windowBits
+ );
+
+ if (status !== c.Z_OK) {
+ throw new Error(msg[status]);
+ }
+
+ this.header = new GZheader();
+
+ zlib_inflate.inflateGetHeader(this.strm, this.header);
+}
+
+/**
+ * Inflate#push(data[, mode]) -> Boolean
+ * - data (Uint8Array|Array|ArrayBuffer|String): input data
+ * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
+ * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
+ *
+ * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
+ * new output chunks. Returns `true` on success. The last data block must have
+ * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
+ * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you
+ * can use mode Z_SYNC_FLUSH, keeping the decompression context.
+ *
+ * On fail call [[Inflate#onEnd]] with error code and return false.
+ *
+ * We strongly recommend to use `Uint8Array` on input for best speed (output
+ * format is detected automatically). Also, don't skip last param and always
+ * use the same type in your code (boolean or number). That will improve JS speed.
+ *
+ * For regular `Array`-s make sure all elements are [0..255].
+ *
+ * ##### Example
+ *
+ * ```javascript
+ * push(chunk, false); // push one of data chunks
+ * ...
+ * push(chunk, true); // push last chunk
+ * ```
+ **/
+Inflate.prototype.push = function (data, mode) {
+ var strm = this.strm;
+ var chunkSize = this.options.chunkSize;
+ var dictionary = this.options.dictionary;
+ var status, _mode;
+ var next_out_utf8, tail, utf8str;
+ var dict;
+
+ // Flag to properly process Z_BUF_ERROR on testing inflate call
+ // when we check that all output data was flushed.
+ var allowBufError = false;
+
+ if (this.ended) { return false; }
+ _mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);
+
+ // Convert data if needed
+ if (typeof data === 'string') {
+ // Only binary strings can be decompressed on practice
+ strm.input = strings.binstring2buf(data);
+ } else if (toString.call(data) === '[object ArrayBuffer]') {
+ strm.input = new Uint8Array(data);
+ } else {
+ strm.input = data;
+ }
+
+ strm.next_in = 0;
+ strm.avail_in = strm.input.length;
+
+ do {
+ if (strm.avail_out === 0) {
+ strm.output = new utils.Buf8(chunkSize);
+ strm.next_out = 0;
+ strm.avail_out = chunkSize;
+ }
+
+ status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH); /* no bad return value */
+
+ if (status === c.Z_NEED_DICT && dictionary) {
+ // Convert data if needed
+ if (typeof dictionary === 'string') {
+ dict = strings.string2buf(dictionary);
+ } else if (toString.call(dictionary) === '[object ArrayBuffer]') {
+ dict = new Uint8Array(dictionary);
+ } else {
+ dict = dictionary;
+ }
+
+ status = zlib_inflate.inflateSetDictionary(this.strm, dict);
+
+ }
+
+ if (status === c.Z_BUF_ERROR && allowBufError === true) {
+ status = c.Z_OK;
+ allowBufError = false;
+ }
+
+ if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
+ this.onEnd(status);
+ this.ended = true;
+ return false;
+ }
+
+ if (strm.next_out) {
+ if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) {
+
+ if (this.options.to === 'string') {
+
+ next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
+
+ tail = strm.next_out - next_out_utf8;
+ utf8str = strings.buf2string(strm.output, next_out_utf8);
+
+ // move tail
+ strm.next_out = tail;
+ strm.avail_out = chunkSize - tail;
+ if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); }
+
+ this.onData(utf8str);
+
+ } else {
+ this.onData(utils.shrinkBuf(strm.output, strm.next_out));
+ }
+ }
+ }
+
+ // When no more input data, we should check that internal inflate buffers
+ // are flushed. The only way to do it when avail_out = 0 - run one more
+ // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR.
+ // Here we set flag to process this error properly.
+ //
+ // NOTE. Deflate does not return error in this case and does not needs such
+ // logic.
+ if (strm.avail_in === 0 && strm.avail_out === 0) {
+ allowBufError = true;
+ }
+
+ } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END);
+
+ if (status === c.Z_STREAM_END) {
+ _mode = c.Z_FINISH;
+ }
+
+ // Finalize on the last chunk.
+ if (_mode === c.Z_FINISH) {
+ status = zlib_inflate.inflateEnd(this.strm);
+ this.onEnd(status);
+ this.ended = true;
+ return status === c.Z_OK;
+ }
+
+ // callback interim results if Z_SYNC_FLUSH.
+ if (_mode === c.Z_SYNC_FLUSH) {
+ this.onEnd(c.Z_OK);
+ strm.avail_out = 0;
+ return true;
+ }
+
+ return true;
+};
+
+
+/**
+ * Inflate#onData(chunk) -> Void
+ * - chunk (Uint8Array|Array|String): ouput data. Type of array depends
+ * on js engine support. When string output requested, each chunk
+ * will be string.
+ *
+ * By default, stores data blocks in `chunks[]` property and glue
+ * those in `onEnd`. Override this handler, if you need another behaviour.
+ **/
+Inflate.prototype.onData = function (chunk) {
+ this.chunks.push(chunk);
+};
+
+
+/**
+ * Inflate#onEnd(status) -> Void
+ * - status (Number): inflate status. 0 (Z_OK) on success,
+ * other if not.
+ *
+ * Called either after you tell inflate that the input stream is
+ * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
+ * or if an error happened. By default - join collected chunks,
+ * free memory and fill `results` / `err` properties.
+ **/
+Inflate.prototype.onEnd = function (status) {
+ // On success - join
+ if (status === c.Z_OK) {
+ if (this.options.to === 'string') {
+ // Glue & convert here, until we teach pako to send
+ // utf8 alligned strings to onData
+ this.result = this.chunks.join('');
+ } else {
+ this.result = utils.flattenChunks(this.chunks);
+ }
+ }
+ this.chunks = [];
+ this.err = status;
+ this.msg = this.strm.msg;
+};
+
+
+/**
+ * inflate(data[, options]) -> Uint8Array|Array|String
+ * - data (Uint8Array|Array|String): input data to decompress.
+ * - options (Object): zlib inflate options.
+ *
+ * Decompress `data` with inflate/ungzip and `options`. Autodetect
+ * format via wrapper header by default. That's why we don't provide
+ * separate `ungzip` method.
+ *
+ * Supported options are:
+ *
+ * - windowBits
+ *
+ * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
+ * for more information.
+ *
+ * Sugar (options):
+ *
+ * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
+ * negative windowBits implicitly.
+ * - `to` (String) - if equal to 'string', then result will be converted
+ * from utf8 to utf16 (javascript) string. When string output requested,
+ * chunk length can differ from `chunkSize`, depending on content.
+ *
+ *
+ * ##### Example:
+ *
+ * ```javascript
+ * var pako = require('pako')
+ * , input = pako.deflate([1,2,3,4,5,6,7,8,9])
+ * , output;
+ *
+ * try {
+ * output = pako.inflate(input);
+ * } catch (err)
+ * console.log(err);
+ * }
+ * ```
+ **/
+function inflate(input, options) {
+ var inflator = new Inflate(options);
+
+ inflator.push(input, true);
+
+ // That will never happens, if you don't cheat with options :)
+ if (inflator.err) { throw inflator.msg || msg[inflator.err]; }
+
+ return inflator.result;
+}
+
+
+/**
+ * inflateRaw(data[, options]) -> Uint8Array|Array|String
+ * - data (Uint8Array|Array|String): input data to decompress.
+ * - options (Object): zlib inflate options.
+ *
+ * The same as [[inflate]], but creates raw data, without wrapper
+ * (header and adler32 crc).
+ **/
+function inflateRaw(input, options) {
+ options = options || {};
+ options.raw = true;
+ return inflate(input, options);
+}
+
+
+/**
+ * ungzip(data[, options]) -> Uint8Array|Array|String
+ * - data (Uint8Array|Array|String): input data to decompress.
+ * - options (Object): zlib inflate options.
+ *
+ * Just shortcut to [[inflate]], because it autodetects format
+ * by header.content. Done for convenience.
+ **/
+
+
+exports.Inflate = Inflate;
+exports.inflate = inflate;
+exports.inflateRaw = inflateRaw;
+exports.ungzip = inflate;
+
+},{"./utils/common":41,"./utils/strings":42,"./zlib/constants":44,"./zlib/gzheader":47,"./zlib/inflate":49,"./zlib/messages":51,"./zlib/zstream":53}],41:[function(require,module,exports){
+'use strict';
+
+
+var TYPED_OK = (typeof Uint8Array !== 'undefined') &&
+ (typeof Uint16Array !== 'undefined') &&
+ (typeof Int32Array !== 'undefined');
+
+
+exports.assign = function (obj /*from1, from2, from3, ...*/) {
+ var sources = Array.prototype.slice.call(arguments, 1);
+ while (sources.length) {
+ var source = sources.shift();
+ if (!source) { continue; }
+
+ if (typeof source !== 'object') {
+ throw new TypeError(source + 'must be non-object');
+ }
+
+ for (var p in source) {
+ if (source.hasOwnProperty(p)) {
+ obj[p] = source[p];
+ }
+ }
+ }
+
+ return obj;
+};
+
+
+// reduce buffer size, avoiding mem copy
+exports.shrinkBuf = function (buf, size) {
+ if (buf.length === size) { return buf; }
+ if (buf.subarray) { return buf.subarray(0, size); }
+ buf.length = size;
+ return buf;
+};
+
+
+var fnTyped = {
+ arraySet: function (dest, src, src_offs, len, dest_offs) {
+ if (src.subarray && dest.subarray) {
+ dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
+ return;
+ }
+ // Fallback to ordinary array
+ for (var i = 0; i < len; i++) {
+ dest[dest_offs + i] = src[src_offs + i];
+ }
+ },
+ // Join array of chunks to single array.
+ flattenChunks: function (chunks) {
+ var i, l, len, pos, chunk, result;
+
+ // calculate data length
+ len = 0;
+ for (i = 0, l = chunks.length; i < l; i++) {
+ len += chunks[i].length;
+ }
+
+ // join chunks
+ result = new Uint8Array(len);
+ pos = 0;
+ for (i = 0, l = chunks.length; i < l; i++) {
+ chunk = chunks[i];
+ result.set(chunk, pos);
+ pos += chunk.length;
+ }
+
+ return result;
+ }
+};
+
+var fnUntyped = {
+ arraySet: function (dest, src, src_offs, len, dest_offs) {
+ for (var i = 0; i < len; i++) {
+ dest[dest_offs + i] = src[src_offs + i];
+ }
+ },
+ // Join array of chunks to single array.
+ flattenChunks: function (chunks) {
+ return [].concat.apply([], chunks);
+ }
+};
+
+
+// Enable/Disable typed arrays use, for testing
+//
+exports.setTyped = function (on) {
+ if (on) {
+ exports.Buf8 = Uint8Array;
+ exports.Buf16 = Uint16Array;
+ exports.Buf32 = Int32Array;
+ exports.assign(exports, fnTyped);
+ } else {
+ exports.Buf8 = Array;
+ exports.Buf16 = Array;
+ exports.Buf32 = Array;
+ exports.assign(exports, fnUntyped);
+ }
+};
+
+exports.setTyped(TYPED_OK);
+
+},{}],42:[function(require,module,exports){
+// String encode/decode helpers
+'use strict';
+
+
+var utils = require('./common');
+
+
+// Quick check if we can use fast array to bin string conversion
+//
+// - apply(Array) can fail on Android 2.2
+// - apply(Uint8Array) can fail on iOS 5.1 Safary
+//
+var STR_APPLY_OK = true;
+var STR_APPLY_UIA_OK = true;
+
+try { String.fromCharCode.apply(null, [ 0 ]); } catch (__) { STR_APPLY_OK = false; }
+try { String.fromCharCode.apply(null, new Uint8Array(1)); } catch (__) { STR_APPLY_UIA_OK = false; }
+
+
+// Table with utf8 lengths (calculated by first byte of sequence)
+// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
+// because max possible codepoint is 0x10ffff
+var _utf8len = new utils.Buf8(256);
+for (var q = 0; q < 256; q++) {
+ _utf8len[q] = (q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1);
+}
+_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start
+
+
+// convert string to array (typed, when possible)
+exports.string2buf = function (str) {
+ var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;
+
+ // count binary size
+ for (m_pos = 0; m_pos < str_len; m_pos++) {
+ c = str.charCodeAt(m_pos);
+ if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
+ c2 = str.charCodeAt(m_pos + 1);
+ if ((c2 & 0xfc00) === 0xdc00) {
+ c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
+ m_pos++;
+ }
+ }
+ buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
+ }
+
+ // allocate buffer
+ buf = new utils.Buf8(buf_len);
+
+ // convert
+ for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
+ c = str.charCodeAt(m_pos);
+ if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
+ c2 = str.charCodeAt(m_pos + 1);
+ if ((c2 & 0xfc00) === 0xdc00) {
+ c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
+ m_pos++;
+ }
+ }
+ if (c < 0x80) {
+ /* one byte */
+ buf[i++] = c;
+ } else if (c < 0x800) {
+ /* two bytes */
+ buf[i++] = 0xC0 | (c >>> 6);
+ buf[i++] = 0x80 | (c & 0x3f);
+ } else if (c < 0x10000) {
+ /* three bytes */
+ buf[i++] = 0xE0 | (c >>> 12);
+ buf[i++] = 0x80 | (c >>> 6 & 0x3f);
+ buf[i++] = 0x80 | (c & 0x3f);
+ } else {
+ /* four bytes */
+ buf[i++] = 0xf0 | (c >>> 18);
+ buf[i++] = 0x80 | (c >>> 12 & 0x3f);
+ buf[i++] = 0x80 | (c >>> 6 & 0x3f);
+ buf[i++] = 0x80 | (c & 0x3f);
+ }
+ }
+
+ return buf;
+};
+
+// Helper (used in 2 places)
+function buf2binstring(buf, len) {
+ // use fallback for big arrays to avoid stack overflow
+ if (len < 65537) {
+ if ((buf.subarray && STR_APPLY_UIA_OK) || (!buf.subarray && STR_APPLY_OK)) {
+ return String.fromCharCode.apply(null, utils.shrinkBuf(buf, len));
+ }
+ }
+
+ var result = '';
+ for (var i = 0; i < len; i++) {
+ result += String.fromCharCode(buf[i]);
+ }
+ return result;
+}
+
+
+// Convert byte array to binary string
+exports.buf2binstring = function (buf) {
+ return buf2binstring(buf, buf.length);
+};
+
+
+// Convert binary string (typed, when possible)
+exports.binstring2buf = function (str) {
+ var buf = new utils.Buf8(str.length);
+ for (var i = 0, len = buf.length; i < len; i++) {
+ buf[i] = str.charCodeAt(i);
+ }
+ return buf;
+};
+
+
+// convert array to string
+exports.buf2string = function (buf, max) {
+ var i, out, c, c_len;
+ var len = max || buf.length;
+
+ // Reserve max possible length (2 words per char)
+ // NB: by unknown reasons, Array is significantly faster for
+ // String.fromCharCode.apply than Uint16Array.
+ var utf16buf = new Array(len * 2);
+
+ for (out = 0, i = 0; i < len;) {
+ c = buf[i++];
+ // quick process ascii
+ if (c < 0x80) { utf16buf[out++] = c; continue; }
+
+ c_len = _utf8len[c];
+ // skip 5 & 6 byte codes
+ if (c_len > 4) { utf16buf[out++] = 0xfffd; i += c_len - 1; continue; }
+
+ // apply mask on first byte
+ c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
+ // join the rest
+ while (c_len > 1 && i < len) {
+ c = (c << 6) | (buf[i++] & 0x3f);
+ c_len--;
+ }
+
+ // terminated by end of string?
+ if (c_len > 1) { utf16buf[out++] = 0xfffd; continue; }
+
+ if (c < 0x10000) {
+ utf16buf[out++] = c;
+ } else {
+ c -= 0x10000;
+ utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
+ utf16buf[out++] = 0xdc00 | (c & 0x3ff);
+ }
+ }
+
+ return buf2binstring(utf16buf, out);
+};
+
+
+// Calculate max possible position in utf8 buffer,
+// that will not break sequence. If that's not possible
+// - (very small limits) return max size as is.
+//
+// buf[] - utf8 bytes array
+// max - length limit (mandatory);
+exports.utf8border = function (buf, max) {
+ var pos;
+
+ max = max || buf.length;
+ if (max > buf.length) { max = buf.length; }
+
+ // go back from last position, until start of sequence found
+ pos = max - 1;
+ while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; }
+
+ // Fuckup - very small and broken sequence,
+ // return max, because we should return something anyway.
+ if (pos < 0) { return max; }
+
+ // If we came to start of buffer - that means vuffer is too small,
+ // return max too.
+ if (pos === 0) { return max; }
+
+ return (pos + _utf8len[buf[pos]] > max) ? pos : max;
+};
+
+},{"./common":41}],43:[function(require,module,exports){
+'use strict';
+
+// Note: adler32 takes 12% for level 0 and 2% for level 6.
+// It doesn't worth to make additional optimizationa as in original.
+// Small size is preferable.
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+function adler32(adler, buf, len, pos) {
+ var s1 = (adler & 0xffff) |0,
+ s2 = ((adler >>> 16) & 0xffff) |0,
+ n = 0;
+
+ while (len !== 0) {
+ // Set limit ~ twice less than 5552, to keep
+ // s2 in 31-bits, because we force signed ints.
+ // in other case %= will fail.
+ n = len > 2000 ? 2000 : len;
+ len -= n;
+
+ do {
+ s1 = (s1 + buf[pos++]) |0;
+ s2 = (s2 + s1) |0;
+ } while (--n);
+
+ s1 %= 65521;
+ s2 %= 65521;
+ }
+
+ return (s1 | (s2 << 16)) |0;
+}
+
+
+module.exports = adler32;
+
+},{}],44:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+module.exports = {
+
+ /* Allowed flush values; see deflate() and inflate() below for details */
+ Z_NO_FLUSH: 0,
+ Z_PARTIAL_FLUSH: 1,
+ Z_SYNC_FLUSH: 2,
+ Z_FULL_FLUSH: 3,
+ Z_FINISH: 4,
+ Z_BLOCK: 5,
+ Z_TREES: 6,
+
+ /* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+ Z_OK: 0,
+ Z_STREAM_END: 1,
+ Z_NEED_DICT: 2,
+ Z_ERRNO: -1,
+ Z_STREAM_ERROR: -2,
+ Z_DATA_ERROR: -3,
+ //Z_MEM_ERROR: -4,
+ Z_BUF_ERROR: -5,
+ //Z_VERSION_ERROR: -6,
+
+ /* compression levels */
+ Z_NO_COMPRESSION: 0,
+ Z_BEST_SPEED: 1,
+ Z_BEST_COMPRESSION: 9,
+ Z_DEFAULT_COMPRESSION: -1,
+
+
+ Z_FILTERED: 1,
+ Z_HUFFMAN_ONLY: 2,
+ Z_RLE: 3,
+ Z_FIXED: 4,
+ Z_DEFAULT_STRATEGY: 0,
+
+ /* Possible values of the data_type field (though see inflate()) */
+ Z_BINARY: 0,
+ Z_TEXT: 1,
+ //Z_ASCII: 1, // = Z_TEXT (deprecated)
+ Z_UNKNOWN: 2,
+
+ /* The deflate compression method */
+ Z_DEFLATED: 8
+ //Z_NULL: null // Use -1 or null inline, depending on var type
+};
+
+},{}],45:[function(require,module,exports){
+'use strict';
+
+// Note: we can't get significant speed boost here.
+// So write code to minimize size - no pregenerated tables
+// and array tools dependencies.
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+// Use ordinary array, since untyped makes no boost here
+function makeTable() {
+ var c, table = [];
+
+ for (var n = 0; n < 256; n++) {
+ c = n;
+ for (var k = 0; k < 8; k++) {
+ c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
+ }
+ table[n] = c;
+ }
+
+ return table;
+}
+
+// Create table on load. Just 255 signed longs. Not a problem.
+var crcTable = makeTable();
+
+
+function crc32(crc, buf, len, pos) {
+ var t = crcTable,
+ end = pos + len;
+
+ crc ^= -1;
+
+ for (var i = pos; i < end; i++) {
+ crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
+ }
+
+ return (crc ^ (-1)); // >>> 0;
+}
+
+
+module.exports = crc32;
+
+},{}],46:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+var utils = require('../utils/common');
+var trees = require('./trees');
+var adler32 = require('./adler32');
+var crc32 = require('./crc32');
+var msg = require('./messages');
+
+/* Public constants ==========================================================*/
+/* ===========================================================================*/
+
+
+/* Allowed flush values; see deflate() and inflate() below for details */
+var Z_NO_FLUSH = 0;
+var Z_PARTIAL_FLUSH = 1;
+//var Z_SYNC_FLUSH = 2;
+var Z_FULL_FLUSH = 3;
+var Z_FINISH = 4;
+var Z_BLOCK = 5;
+//var Z_TREES = 6;
+
+
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+var Z_OK = 0;
+var Z_STREAM_END = 1;
+//var Z_NEED_DICT = 2;
+//var Z_ERRNO = -1;
+var Z_STREAM_ERROR = -2;
+var Z_DATA_ERROR = -3;
+//var Z_MEM_ERROR = -4;
+var Z_BUF_ERROR = -5;
+//var Z_VERSION_ERROR = -6;
+
+
+/* compression levels */
+//var Z_NO_COMPRESSION = 0;
+//var Z_BEST_SPEED = 1;
+//var Z_BEST_COMPRESSION = 9;
+var Z_DEFAULT_COMPRESSION = -1;
+
+
+var Z_FILTERED = 1;
+var Z_HUFFMAN_ONLY = 2;
+var Z_RLE = 3;
+var Z_FIXED = 4;
+var Z_DEFAULT_STRATEGY = 0;
+
+/* Possible values of the data_type field (though see inflate()) */
+//var Z_BINARY = 0;
+//var Z_TEXT = 1;
+//var Z_ASCII = 1; // = Z_TEXT
+var Z_UNKNOWN = 2;
+
+
+/* The deflate compression method */
+var Z_DEFLATED = 8;
+
+/*============================================================================*/
+
+
+var MAX_MEM_LEVEL = 9;
+/* Maximum value for memLevel in deflateInit2 */
+var MAX_WBITS = 15;
+/* 32K LZ77 window */
+var DEF_MEM_LEVEL = 8;
+
+
+var LENGTH_CODES = 29;
+/* number of length codes, not counting the special END_BLOCK code */
+var LITERALS = 256;
+/* number of literal bytes 0..255 */
+var L_CODES = LITERALS + 1 + LENGTH_CODES;
+/* number of Literal or Length codes, including the END_BLOCK code */
+var D_CODES = 30;
+/* number of distance codes */
+var BL_CODES = 19;
+/* number of codes used to transfer the bit lengths */
+var HEAP_SIZE = 2 * L_CODES + 1;
+/* maximum heap size */
+var MAX_BITS = 15;
+/* All codes must not exceed MAX_BITS bits */
+
+var MIN_MATCH = 3;
+var MAX_MATCH = 258;
+var MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);
+
+var PRESET_DICT = 0x20;
+
+var INIT_STATE = 42;
+var EXTRA_STATE = 69;
+var NAME_STATE = 73;
+var COMMENT_STATE = 91;
+var HCRC_STATE = 103;
+var BUSY_STATE = 113;
+var FINISH_STATE = 666;
+
+var BS_NEED_MORE = 1; /* block not completed, need more input or more output */
+var BS_BLOCK_DONE = 2; /* block flush performed */
+var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
+var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */
+
+var OS_CODE = 0x03; // Unix :) . Don't detect, use this default.
+
+function err(strm, errorCode) {
+ strm.msg = msg[errorCode];
+ return errorCode;
+}
+
+function rank(f) {
+ return ((f) << 1) - ((f) > 4 ? 9 : 0);
+}
+
+function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }
+
+
+/* =========================================================================
+ * Flush as much pending output as possible. All deflate() output goes
+ * through this function so some applications may wish to modify it
+ * to avoid allocating a large strm->output buffer and copying into it.
+ * (See also read_buf()).
+ */
+function flush_pending(strm) {
+ var s = strm.state;
+
+ //_tr_flush_bits(s);
+ var len = s.pending;
+ if (len > strm.avail_out) {
+ len = strm.avail_out;
+ }
+ if (len === 0) { return; }
+
+ utils.arraySet(strm.output, s.pending_buf, s.pending_out, len, strm.next_out);
+ strm.next_out += len;
+ s.pending_out += len;
+ strm.total_out += len;
+ strm.avail_out -= len;
+ s.pending -= len;
+ if (s.pending === 0) {
+ s.pending_out = 0;
+ }
+}
+
+
+function flush_block_only(s, last) {
+ trees._tr_flush_block(s, (s.block_start >= 0 ? s.block_start : -1), s.strstart - s.block_start, last);
+ s.block_start = s.strstart;
+ flush_pending(s.strm);
+}
+
+
+function put_byte(s, b) {
+ s.pending_buf[s.pending++] = b;
+}
+
+
+/* =========================================================================
+ * Put a short in the pending buffer. The 16-bit value is put in MSB order.
+ * IN assertion: the stream state is correct and there is enough room in
+ * pending_buf.
+ */
+function putShortMSB(s, b) {
+// put_byte(s, (Byte)(b >> 8));
+// put_byte(s, (Byte)(b & 0xff));
+ s.pending_buf[s.pending++] = (b >>> 8) & 0xff;
+ s.pending_buf[s.pending++] = b & 0xff;
+}
+
+
+/* ===========================================================================
+ * Read a new buffer from the current input stream, update the adler32
+ * and total number of bytes read. All deflate() input goes through
+ * this function so some applications may wish to modify it to avoid
+ * allocating a large strm->input buffer and copying from it.
+ * (See also flush_pending()).
+ */
+function read_buf(strm, buf, start, size) {
+ var len = strm.avail_in;
+
+ if (len > size) { len = size; }
+ if (len === 0) { return 0; }
+
+ strm.avail_in -= len;
+
+ // zmemcpy(buf, strm->next_in, len);
+ utils.arraySet(buf, strm.input, strm.next_in, len, start);
+ if (strm.state.wrap === 1) {
+ strm.adler = adler32(strm.adler, buf, len, start);
+ }
+
+ else if (strm.state.wrap === 2) {
+ strm.adler = crc32(strm.adler, buf, len, start);
+ }
+
+ strm.next_in += len;
+ strm.total_in += len;
+
+ return len;
+}
+
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
+ * OUT assertion: the match length is not greater than s->lookahead.
+ */
+function longest_match(s, cur_match) {
+ var chain_length = s.max_chain_length; /* max hash chain length */
+ var scan = s.strstart; /* current string */
+ var match; /* matched string */
+ var len; /* length of current match */
+ var best_len = s.prev_length; /* best match length so far */
+ var nice_match = s.nice_match; /* stop if match long enough */
+ var limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) ?
+ s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0/*NIL*/;
+
+ var _win = s.window; // shortcut
+
+ var wmask = s.w_mask;
+ var prev = s.prev;
+
+ /* Stop when cur_match becomes <= limit. To simplify the code,
+ * we prevent matches with the string of window index 0.
+ */
+
+ var strend = s.strstart + MAX_MATCH;
+ var scan_end1 = _win[scan + best_len - 1];
+ var scan_end = _win[scan + best_len];
+
+ /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
+ * It is easy to get rid of this optimization if necessary.
+ */
+ // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");
+
+ /* Do not waste too much time if we already have a good match: */
+ if (s.prev_length >= s.good_match) {
+ chain_length >>= 2;
+ }
+ /* Do not look for matches beyond the end of the input. This is necessary
+ * to make deflate deterministic.
+ */
+ if (nice_match > s.lookahead) { nice_match = s.lookahead; }
+
+ // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");
+
+ do {
+ // Assert(cur_match < s->strstart, "no future");
+ match = cur_match;
+
+ /* Skip to next match if the match length cannot increase
+ * or if the match length is less than 2. Note that the checks below
+ * for insufficient lookahead only occur occasionally for performance
+ * reasons. Therefore uninitialized memory will be accessed, and
+ * conditional jumps will be made that depend on those values.
+ * However the length of the match is limited to the lookahead, so
+ * the output of deflate is not affected by the uninitialized values.
+ */
+
+ if (_win[match + best_len] !== scan_end ||
+ _win[match + best_len - 1] !== scan_end1 ||
+ _win[match] !== _win[scan] ||
+ _win[++match] !== _win[scan + 1]) {
+ continue;
+ }
+
+ /* The check at best_len-1 can be removed because it will be made
+ * again later. (This heuristic is not always a win.)
+ * It is not necessary to compare scan[2] and match[2] since they
+ * are always equal when the other bytes match, given that
+ * the hash keys are equal and that HASH_BITS >= 8.
+ */
+ scan += 2;
+ match++;
+ // Assert(*scan == *match, "match[2]?");
+
+ /* We check for insufficient lookahead only every 8th comparison;
+ * the 256th check will be made at strstart+258.
+ */
+ do {
+ /*jshint noempty:false*/
+ } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
+ _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
+ _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
+ _win[++scan] === _win[++match] && _win[++scan] === _win[++match] &&
+ scan < strend);
+
+ // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");
+
+ len = MAX_MATCH - (strend - scan);
+ scan = strend - MAX_MATCH;
+
+ if (len > best_len) {
+ s.match_start = cur_match;
+ best_len = len;
+ if (len >= nice_match) {
+ break;
+ }
+ scan_end1 = _win[scan + best_len - 1];
+ scan_end = _win[scan + best_len];
+ }
+ } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);
+
+ if (best_len <= s.lookahead) {
+ return best_len;
+ }
+ return s.lookahead;
+}
+
+
+/* ===========================================================================
+ * Fill the window when the lookahead becomes insufficient.
+ * Updates strstart and lookahead.
+ *
+ * IN assertion: lookahead < MIN_LOOKAHEAD
+ * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
+ * At least one byte has been read, or avail_in == 0; reads are
+ * performed for at least two bytes (required for the zip translate_eol
+ * option -- not supported here).
+ */
+function fill_window(s) {
+ var _w_size = s.w_size;
+ var p, n, m, more, str;
+
+ //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");
+
+ do {
+ more = s.window_size - s.lookahead - s.strstart;
+
+ // JS ints have 32 bit, block below not needed
+ /* Deal with !@#$% 64K limit: */
+ //if (sizeof(int) <= 2) {
+ // if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
+ // more = wsize;
+ //
+ // } else if (more == (unsigned)(-1)) {
+ // /* Very unlikely, but possible on 16 bit machine if
+ // * strstart == 0 && lookahead == 1 (input done a byte at time)
+ // */
+ // more--;
+ // }
+ //}
+
+
+ /* If the window is almost full and there is insufficient lookahead,
+ * move the upper half to the lower one to make room in the upper half.
+ */
+ if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {
+
+ utils.arraySet(s.window, s.window, _w_size, _w_size, 0);
+ s.match_start -= _w_size;
+ s.strstart -= _w_size;
+ /* we now have strstart >= MAX_DIST */
+ s.block_start -= _w_size;
+
+ /* Slide the hash table (could be avoided with 32 bit values
+ at the expense of memory usage). We slide even when level == 0
+ to keep the hash table consistent if we switch back to level > 0
+ later. (Using level 0 permanently is not an optimal usage of
+ zlib, so we don't care about this pathological case.)
+ */
+
+ n = s.hash_size;
+ p = n;
+ do {
+ m = s.head[--p];
+ s.head[p] = (m >= _w_size ? m - _w_size : 0);
+ } while (--n);
+
+ n = _w_size;
+ p = n;
+ do {
+ m = s.prev[--p];
+ s.prev[p] = (m >= _w_size ? m - _w_size : 0);
+ /* If n is not on any hash chain, prev[n] is garbage but
+ * its value will never be used.
+ */
+ } while (--n);
+
+ more += _w_size;
+ }
+ if (s.strm.avail_in === 0) {
+ break;
+ }
+
+ /* If there was no sliding:
+ * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
+ * more == window_size - lookahead - strstart
+ * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
+ * => more >= window_size - 2*WSIZE + 2
+ * In the BIG_MEM or MMAP case (not yet supported),
+ * window_size == input_size + MIN_LOOKAHEAD &&
+ * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
+ * Otherwise, window_size == 2*WSIZE so more >= 2.
+ * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
+ */
+ //Assert(more >= 2, "more < 2");
+ n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
+ s.lookahead += n;
+
+ /* Initialize the hash value now that we have some input: */
+ if (s.lookahead + s.insert >= MIN_MATCH) {
+ str = s.strstart - s.insert;
+ s.ins_h = s.window[str];
+
+ /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + 1]) & s.hash_mask;
+//#if MIN_MATCH != 3
+// Call update_hash() MIN_MATCH-3 more times
+//#endif
+ while (s.insert) {
+ /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;
+
+ s.prev[str & s.w_mask] = s.head[s.ins_h];
+ s.head[s.ins_h] = str;
+ str++;
+ s.insert--;
+ if (s.lookahead + s.insert < MIN_MATCH) {
+ break;
+ }
+ }
+ }
+ /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
+ * but this is not important since only literal bytes will be emitted.
+ */
+
+ } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);
+
+ /* If the WIN_INIT bytes after the end of the current data have never been
+ * written, then zero those bytes in order to avoid memory check reports of
+ * the use of uninitialized (or uninitialised as Julian writes) bytes by
+ * the longest match routines. Update the high water mark for the next
+ * time through here. WIN_INIT is set to MAX_MATCH since the longest match
+ * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
+ */
+// if (s.high_water < s.window_size) {
+// var curr = s.strstart + s.lookahead;
+// var init = 0;
+//
+// if (s.high_water < curr) {
+// /* Previous high water mark below current data -- zero WIN_INIT
+// * bytes or up to end of window, whichever is less.
+// */
+// init = s.window_size - curr;
+// if (init > WIN_INIT)
+// init = WIN_INIT;
+// zmemzero(s->window + curr, (unsigned)init);
+// s->high_water = curr + init;
+// }
+// else if (s->high_water < (ulg)curr + WIN_INIT) {
+// /* High water mark at or above current data, but below current data
+// * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
+// * to end of window, whichever is less.
+// */
+// init = (ulg)curr + WIN_INIT - s->high_water;
+// if (init > s->window_size - s->high_water)
+// init = s->window_size - s->high_water;
+// zmemzero(s->window + s->high_water, (unsigned)init);
+// s->high_water += init;
+// }
+// }
+//
+// Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
+// "not enough room for search");
+}
+
+/* ===========================================================================
+ * Copy without compression as much as possible from the input stream, return
+ * the current block state.
+ * This function does not insert new strings in the dictionary since
+ * uncompressible data is probably not useful. This function is used
+ * only for the level=0 compression option.
+ * NOTE: this function should be optimized to avoid extra copying from
+ * window to pending_buf.
+ */
+function deflate_stored(s, flush) {
+ /* Stored blocks are limited to 0xffff bytes, pending_buf is limited
+ * to pending_buf_size, and each stored block has a 5 byte header:
+ */
+ var max_block_size = 0xffff;
+
+ if (max_block_size > s.pending_buf_size - 5) {
+ max_block_size = s.pending_buf_size - 5;
+ }
+
+ /* Copy as much as possible from input to output: */
+ for (;;) {
+ /* Fill the window as much as possible: */
+ if (s.lookahead <= 1) {
+
+ //Assert(s->strstart < s->w_size+MAX_DIST(s) ||
+ // s->block_start >= (long)s->w_size, "slide too late");
+// if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) ||
+// s.block_start >= s.w_size)) {
+// throw new Error("slide too late");
+// }
+
+ fill_window(s);
+ if (s.lookahead === 0 && flush === Z_NO_FLUSH) {
+ return BS_NEED_MORE;
+ }
+
+ if (s.lookahead === 0) {
+ break;
+ }
+ /* flush the current block */
+ }
+ //Assert(s->block_start >= 0L, "block gone");
+// if (s.block_start < 0) throw new Error("block gone");
+
+ s.strstart += s.lookahead;
+ s.lookahead = 0;
+
+ /* Emit a stored block if pending_buf will be full: */
+ var max_start = s.block_start + max_block_size;
+
+ if (s.strstart === 0 || s.strstart >= max_start) {
+ /* strstart == 0 is possible when wraparound on 16-bit machine */
+ s.lookahead = s.strstart - max_start;
+ s.strstart = max_start;
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+
+
+ }
+ /* Flush if we may have to slide, otherwise block_start may become
+ * negative and the data will be gone:
+ */
+ if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+ }
+
+ s.insert = 0;
+
+ if (flush === Z_FINISH) {
+ /*** FLUSH_BLOCK(s, 1); ***/
+ flush_block_only(s, true);
+ if (s.strm.avail_out === 0) {
+ return BS_FINISH_STARTED;
+ }
+ /***/
+ return BS_FINISH_DONE;
+ }
+
+ if (s.strstart > s.block_start) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+
+ return BS_NEED_MORE;
+}
+
+/* ===========================================================================
+ * Compress as much as possible from the input stream, return the current
+ * block state.
+ * This function does not perform lazy evaluation of matches and inserts
+ * new strings in the dictionary only for unmatched strings or for short
+ * matches. It is used only for the fast compression options.
+ */
+function deflate_fast(s, flush) {
+ var hash_head; /* head of the hash chain */
+ var bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s.lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
+ return BS_NEED_MORE;
+ }
+ if (s.lookahead === 0) {
+ break; /* flush the current block */
+ }
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = 0/*NIL*/;
+ if (s.lookahead >= MIN_MATCH) {
+ /*** INSERT_STRING(s, s.strstart, hash_head); ***/
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
+ hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
+ s.head[s.ins_h] = s.strstart;
+ /***/
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ * At this point we have always match_length < MIN_MATCH
+ */
+ if (hash_head !== 0/*NIL*/ && ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD))) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ s.match_length = longest_match(s, hash_head);
+ /* longest_match() sets match_start */
+ }
+ if (s.match_length >= MIN_MATCH) {
+ // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only
+
+ /*** _tr_tally_dist(s, s.strstart - s.match_start,
+ s.match_length - MIN_MATCH, bflush); ***/
+ bflush = trees._tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);
+
+ s.lookahead -= s.match_length;
+
+ /* Insert new strings in the hash table only if the match length
+ * is not too large. This saves time but degrades compression.
+ */
+ if (s.match_length <= s.max_lazy_match/*max_insert_length*/ && s.lookahead >= MIN_MATCH) {
+ s.match_length--; /* string at strstart already in table */
+ do {
+ s.strstart++;
+ /*** INSERT_STRING(s, s.strstart, hash_head); ***/
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
+ hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
+ s.head[s.ins_h] = s.strstart;
+ /***/
+ /* strstart never exceeds WSIZE-MAX_MATCH, so there are
+ * always MIN_MATCH bytes ahead.
+ */
+ } while (--s.match_length !== 0);
+ s.strstart++;
+ } else
+ {
+ s.strstart += s.match_length;
+ s.match_length = 0;
+ s.ins_h = s.window[s.strstart];
+ /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + 1]) & s.hash_mask;
+
+//#if MIN_MATCH != 3
+// Call UPDATE_HASH() MIN_MATCH-3 more times
+//#endif
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
+ * matter since it will be recomputed at next deflate call.
+ */
+ }
+ } else {
+ /* No match, output a literal byte */
+ //Tracevv((stderr,"%c", s.window[s.strstart]));
+ /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
+ bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
+
+ s.lookahead--;
+ s.strstart++;
+ }
+ if (bflush) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+ }
+ s.insert = ((s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1);
+ if (flush === Z_FINISH) {
+ /*** FLUSH_BLOCK(s, 1); ***/
+ flush_block_only(s, true);
+ if (s.strm.avail_out === 0) {
+ return BS_FINISH_STARTED;
+ }
+ /***/
+ return BS_FINISH_DONE;
+ }
+ if (s.last_lit) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+ return BS_BLOCK_DONE;
+}
+
+/* ===========================================================================
+ * Same as above, but achieves better compression. We use a lazy
+ * evaluation for matches: a match is finally adopted only if there is
+ * no better match at the next window position.
+ */
+function deflate_slow(s, flush) {
+ var hash_head; /* head of hash chain */
+ var bflush; /* set if current block must be flushed */
+
+ var max_insert;
+
+ /* Process the input block. */
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the next match, plus MIN_MATCH bytes to insert the
+ * string following the next match.
+ */
+ if (s.lookahead < MIN_LOOKAHEAD) {
+ fill_window(s);
+ if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH) {
+ return BS_NEED_MORE;
+ }
+ if (s.lookahead === 0) { break; } /* flush the current block */
+ }
+
+ /* Insert the string window[strstart .. strstart+2] in the
+ * dictionary, and set hash_head to the head of the hash chain:
+ */
+ hash_head = 0/*NIL*/;
+ if (s.lookahead >= MIN_MATCH) {
+ /*** INSERT_STRING(s, s.strstart, hash_head); ***/
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
+ hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
+ s.head[s.ins_h] = s.strstart;
+ /***/
+ }
+
+ /* Find the longest match, discarding those <= prev_length.
+ */
+ s.prev_length = s.match_length;
+ s.prev_match = s.match_start;
+ s.match_length = MIN_MATCH - 1;
+
+ if (hash_head !== 0/*NIL*/ && s.prev_length < s.max_lazy_match &&
+ s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD)/*MAX_DIST(s)*/) {
+ /* To simplify the code, we prevent matches with the string
+ * of window index 0 (in particular we have to avoid a match
+ * of the string with itself at the start of the input file).
+ */
+ s.match_length = longest_match(s, hash_head);
+ /* longest_match() sets match_start */
+
+ if (s.match_length <= 5 &&
+ (s.strategy === Z_FILTERED || (s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096/*TOO_FAR*/))) {
+
+ /* If prev_match is also MIN_MATCH, match_start is garbage
+ * but we will ignore the current match anyway.
+ */
+ s.match_length = MIN_MATCH - 1;
+ }
+ }
+ /* If there was a match at the previous step and the current
+ * match is not better, output the previous match:
+ */
+ if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
+ max_insert = s.strstart + s.lookahead - MIN_MATCH;
+ /* Do not insert strings in hash table beyond this. */
+
+ //check_match(s, s.strstart-1, s.prev_match, s.prev_length);
+
+ /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
+ s.prev_length - MIN_MATCH, bflush);***/
+ bflush = trees._tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
+ /* Insert in hash table all strings up to the end of the match.
+ * strstart-1 and strstart are already inserted. If there is not
+ * enough lookahead, the last two strings are not inserted in
+ * the hash table.
+ */
+ s.lookahead -= s.prev_length - 1;
+ s.prev_length -= 2;
+ do {
+ if (++s.strstart <= max_insert) {
+ /*** INSERT_STRING(s, s.strstart, hash_head); ***/
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[s.strstart + MIN_MATCH - 1]) & s.hash_mask;
+ hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
+ s.head[s.ins_h] = s.strstart;
+ /***/
+ }
+ } while (--s.prev_length !== 0);
+ s.match_available = 0;
+ s.match_length = MIN_MATCH - 1;
+ s.strstart++;
+
+ if (bflush) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+
+ } else if (s.match_available) {
+ /* If there was no match at the previous position, output a
+ * single literal. If there was a match but the current match
+ * is longer, truncate the previous match to a single literal.
+ */
+ //Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
+ bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);
+
+ if (bflush) {
+ /*** FLUSH_BLOCK_ONLY(s, 0) ***/
+ flush_block_only(s, false);
+ /***/
+ }
+ s.strstart++;
+ s.lookahead--;
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ } else {
+ /* There is no previous match to compare with, wait for
+ * the next step to decide.
+ */
+ s.match_available = 1;
+ s.strstart++;
+ s.lookahead--;
+ }
+ }
+ //Assert (flush != Z_NO_FLUSH, "no flush?");
+ if (s.match_available) {
+ //Tracevv((stderr,"%c", s->window[s->strstart-1]));
+ /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
+ bflush = trees._tr_tally(s, 0, s.window[s.strstart - 1]);
+
+ s.match_available = 0;
+ }
+ s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
+ if (flush === Z_FINISH) {
+ /*** FLUSH_BLOCK(s, 1); ***/
+ flush_block_only(s, true);
+ if (s.strm.avail_out === 0) {
+ return BS_FINISH_STARTED;
+ }
+ /***/
+ return BS_FINISH_DONE;
+ }
+ if (s.last_lit) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+
+ return BS_BLOCK_DONE;
+}
+
+
+/* ===========================================================================
+ * For Z_RLE, simply look for runs of bytes, generate matches only of distance
+ * one. Do not maintain a hash table. (It will be regenerated if this run of
+ * deflate switches away from Z_RLE.)
+ */
+function deflate_rle(s, flush) {
+ var bflush; /* set if current block must be flushed */
+ var prev; /* byte at distance one to match */
+ var scan, strend; /* scan goes up to strend for length of run */
+
+ var _win = s.window;
+
+ for (;;) {
+ /* Make sure that we always have enough lookahead, except
+ * at the end of the input file. We need MAX_MATCH bytes
+ * for the longest run, plus one for the unrolled loop.
+ */
+ if (s.lookahead <= MAX_MATCH) {
+ fill_window(s);
+ if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH) {
+ return BS_NEED_MORE;
+ }
+ if (s.lookahead === 0) { break; } /* flush the current block */
+ }
+
+ /* See how many times the previous byte repeats */
+ s.match_length = 0;
+ if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
+ scan = s.strstart - 1;
+ prev = _win[scan];
+ if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
+ strend = s.strstart + MAX_MATCH;
+ do {
+ /*jshint noempty:false*/
+ } while (prev === _win[++scan] && prev === _win[++scan] &&
+ prev === _win[++scan] && prev === _win[++scan] &&
+ prev === _win[++scan] && prev === _win[++scan] &&
+ prev === _win[++scan] && prev === _win[++scan] &&
+ scan < strend);
+ s.match_length = MAX_MATCH - (strend - scan);
+ if (s.match_length > s.lookahead) {
+ s.match_length = s.lookahead;
+ }
+ }
+ //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
+ }
+
+ /* Emit match if have run of MIN_MATCH or longer, else emit literal */
+ if (s.match_length >= MIN_MATCH) {
+ //check_match(s, s.strstart, s.strstart - 1, s.match_length);
+
+ /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
+ bflush = trees._tr_tally(s, 1, s.match_length - MIN_MATCH);
+
+ s.lookahead -= s.match_length;
+ s.strstart += s.match_length;
+ s.match_length = 0;
+ } else {
+ /* No match, output a literal byte */
+ //Tracevv((stderr,"%c", s->window[s->strstart]));
+ /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
+ bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
+
+ s.lookahead--;
+ s.strstart++;
+ }
+ if (bflush) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+ }
+ s.insert = 0;
+ if (flush === Z_FINISH) {
+ /*** FLUSH_BLOCK(s, 1); ***/
+ flush_block_only(s, true);
+ if (s.strm.avail_out === 0) {
+ return BS_FINISH_STARTED;
+ }
+ /***/
+ return BS_FINISH_DONE;
+ }
+ if (s.last_lit) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+ return BS_BLOCK_DONE;
+}
+
+/* ===========================================================================
+ * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table.
+ * (It will be regenerated if this run of deflate switches away from Huffman.)
+ */
+function deflate_huff(s, flush) {
+ var bflush; /* set if current block must be flushed */
+
+ for (;;) {
+ /* Make sure that we have a literal to write. */
+ if (s.lookahead === 0) {
+ fill_window(s);
+ if (s.lookahead === 0) {
+ if (flush === Z_NO_FLUSH) {
+ return BS_NEED_MORE;
+ }
+ break; /* flush the current block */
+ }
+ }
+
+ /* Output a literal byte */
+ s.match_length = 0;
+ //Tracevv((stderr,"%c", s->window[s->strstart]));
+ /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
+ bflush = trees._tr_tally(s, 0, s.window[s.strstart]);
+ s.lookahead--;
+ s.strstart++;
+ if (bflush) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+ }
+ s.insert = 0;
+ if (flush === Z_FINISH) {
+ /*** FLUSH_BLOCK(s, 1); ***/
+ flush_block_only(s, true);
+ if (s.strm.avail_out === 0) {
+ return BS_FINISH_STARTED;
+ }
+ /***/
+ return BS_FINISH_DONE;
+ }
+ if (s.last_lit) {
+ /*** FLUSH_BLOCK(s, 0); ***/
+ flush_block_only(s, false);
+ if (s.strm.avail_out === 0) {
+ return BS_NEED_MORE;
+ }
+ /***/
+ }
+ return BS_BLOCK_DONE;
+}
+
+/* Values for max_lazy_match, good_match and max_chain_length, depending on
+ * the desired pack level (0..9). The values given below have been tuned to
+ * exclude worst case performance for pathological files. Better values may be
+ * found for specific files.
+ */
+function Config(good_length, max_lazy, nice_length, max_chain, func) {
+ this.good_length = good_length;
+ this.max_lazy = max_lazy;
+ this.nice_length = nice_length;
+ this.max_chain = max_chain;
+ this.func = func;
+}
+
+var configuration_table;
+
+configuration_table = [
+ /* good lazy nice chain */
+ new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */
+ new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */
+ new Config(4, 5, 16, 8, deflate_fast), /* 2 */
+ new Config(4, 6, 32, 32, deflate_fast), /* 3 */
+
+ new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */
+ new Config(8, 16, 32, 32, deflate_slow), /* 5 */
+ new Config(8, 16, 128, 128, deflate_slow), /* 6 */
+ new Config(8, 32, 128, 256, deflate_slow), /* 7 */
+ new Config(32, 128, 258, 1024, deflate_slow), /* 8 */
+ new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */
+];
+
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new zlib stream
+ */
+function lm_init(s) {
+ s.window_size = 2 * s.w_size;
+
+ /*** CLEAR_HASH(s); ***/
+ zero(s.head); // Fill with NIL (= 0);
+
+ /* Set the default configuration parameters:
+ */
+ s.max_lazy_match = configuration_table[s.level].max_lazy;
+ s.good_match = configuration_table[s.level].good_length;
+ s.nice_match = configuration_table[s.level].nice_length;
+ s.max_chain_length = configuration_table[s.level].max_chain;
+
+ s.strstart = 0;
+ s.block_start = 0;
+ s.lookahead = 0;
+ s.insert = 0;
+ s.match_length = s.prev_length = MIN_MATCH - 1;
+ s.match_available = 0;
+ s.ins_h = 0;
+}
+
+
+function DeflateState() {
+ this.strm = null; /* pointer back to this zlib stream */
+ this.status = 0; /* as the name implies */
+ this.pending_buf = null; /* output still pending */
+ this.pending_buf_size = 0; /* size of pending_buf */
+ this.pending_out = 0; /* next pending byte to output to the stream */
+ this.pending = 0; /* nb of bytes in the pending buffer */
+ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
+ this.gzhead = null; /* gzip header information to write */
+ this.gzindex = 0; /* where in extra, name, or comment */
+ this.method = Z_DEFLATED; /* can only be DEFLATED */
+ this.last_flush = -1; /* value of flush param for previous deflate call */
+
+ this.w_size = 0; /* LZ77 window size (32K by default) */
+ this.w_bits = 0; /* log2(w_size) (8..16) */
+ this.w_mask = 0; /* w_size - 1 */
+
+ this.window = null;
+ /* Sliding window. Input bytes are read into the second half of the window,
+ * and move to the first half later to keep a dictionary of at least wSize
+ * bytes. With this organization, matches are limited to a distance of
+ * wSize-MAX_MATCH bytes, but this ensures that IO is always
+ * performed with a length multiple of the block size.
+ */
+
+ this.window_size = 0;
+ /* Actual size of window: 2*wSize, except when the user input buffer
+ * is directly used as sliding window.
+ */
+
+ this.prev = null;
+ /* Link to older string with same hash index. To limit the size of this
+ * array to 64K, this link is maintained only for the last 32K strings.
+ * An index in this array is thus a window index modulo 32K.
+ */
+
+ this.head = null; /* Heads of the hash chains or NIL. */
+
+ this.ins_h = 0; /* hash index of string to be inserted */
+ this.hash_size = 0; /* number of elements in hash table */
+ this.hash_bits = 0; /* log2(hash_size) */
+ this.hash_mask = 0; /* hash_size-1 */
+
+ this.hash_shift = 0;
+ /* Number of bits by which ins_h must be shifted at each input
+ * step. It must be such that after MIN_MATCH steps, the oldest
+ * byte no longer takes part in the hash key, that is:
+ * hash_shift * MIN_MATCH >= hash_bits
+ */
+
+ this.block_start = 0;
+ /* Window position at the beginning of the current output block. Gets
+ * negative when the window is moved backwards.
+ */
+
+ this.match_length = 0; /* length of best match */
+ this.prev_match = 0; /* previous match */
+ this.match_available = 0; /* set if previous match exists */
+ this.strstart = 0; /* start of string to insert */
+ this.match_start = 0; /* start of matching string */
+ this.lookahead = 0; /* number of valid bytes ahead in window */
+
+ this.prev_length = 0;
+ /* Length of the best match at previous step. Matches not greater than this
+ * are discarded. This is used in the lazy match evaluation.
+ */
+
+ this.max_chain_length = 0;
+ /* To speed up deflation, hash chains are never searched beyond this
+ * length. A higher limit improves compression ratio but degrades the
+ * speed.
+ */
+
+ this.max_lazy_match = 0;
+ /* Attempt to find a better match only when the current match is strictly
+ * smaller than this value. This mechanism is used only for compression
+ * levels >= 4.
+ */
+ // That's alias to max_lazy_match, don't use directly
+ //this.max_insert_length = 0;
+ /* Insert new strings in the hash table only if the match length is not
+ * greater than this length. This saves time but degrades compression.
+ * max_insert_length is used only for compression levels <= 3.
+ */
+
+ this.level = 0; /* compression level (1..9) */
+ this.strategy = 0; /* favor or force Huffman coding*/
+
+ this.good_match = 0;
+ /* Use a faster search when the previous match is longer than this */
+
+ this.nice_match = 0; /* Stop searching when current match exceeds this */
+
+ /* used by trees.c: */
+
+ /* Didn't use ct_data typedef below to suppress compiler warning */
+
+ // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */
+ // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
+ // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */
+
+ // Use flat array of DOUBLE size, with interleaved fata,
+ // because JS does not support effective
+ this.dyn_ltree = new utils.Buf16(HEAP_SIZE * 2);
+ this.dyn_dtree = new utils.Buf16((2 * D_CODES + 1) * 2);
+ this.bl_tree = new utils.Buf16((2 * BL_CODES + 1) * 2);
+ zero(this.dyn_ltree);
+ zero(this.dyn_dtree);
+ zero(this.bl_tree);
+
+ this.l_desc = null; /* desc. for literal tree */
+ this.d_desc = null; /* desc. for distance tree */
+ this.bl_desc = null; /* desc. for bit length tree */
+
+ //ush bl_count[MAX_BITS+1];
+ this.bl_count = new utils.Buf16(MAX_BITS + 1);
+ /* number of codes at each bit length for an optimal tree */
+
+ //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */
+ this.heap = new utils.Buf16(2 * L_CODES + 1); /* heap used to build the Huffman trees */
+ zero(this.heap);
+
+ this.heap_len = 0; /* number of elements in the heap */
+ this.heap_max = 0; /* element of largest frequency */
+ /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
+ * The same heap array is used to build all trees.
+ */
+
+ this.depth = new utils.Buf16(2 * L_CODES + 1); //uch depth[2*L_CODES+1];
+ zero(this.depth);
+ /* Depth of each subtree used as tie breaker for trees of equal frequency
+ */
+
+ this.l_buf = 0; /* buffer index for literals or lengths */
+
+ this.lit_bufsize = 0;
+ /* Size of match buffer for literals/lengths. There are 4 reasons for
+ * limiting lit_bufsize to 64K:
+ * - frequencies can be kept in 16 bit counters
+ * - if compression is not successful for the first block, all input
+ * data is still in the window so we can still emit a stored block even
+ * when input comes from standard input. (This can also be done for
+ * all blocks if lit_bufsize is not greater than 32K.)
+ * - if compression is not successful for a file smaller than 64K, we can
+ * even emit a stored file instead of a stored block (saving 5 bytes).
+ * This is applicable only for zip (not gzip or zlib).
+ * - creating new Huffman trees less frequently may not provide fast
+ * adaptation to changes in the input data statistics. (Take for
+ * example a binary file with poorly compressible code followed by
+ * a highly compressible string table.) Smaller buffer sizes give
+ * fast adaptation but have of course the overhead of transmitting
+ * trees more frequently.
+ * - I can't count above 4
+ */
+
+ this.last_lit = 0; /* running index in l_buf */
+
+ this.d_buf = 0;
+ /* Buffer index for distances. To simplify the code, d_buf and l_buf have
+ * the same number of elements. To use different lengths, an extra flag
+ * array would be necessary.
+ */
+
+ this.opt_len = 0; /* bit length of current block with optimal trees */
+ this.static_len = 0; /* bit length of current block with static trees */
+ this.matches = 0; /* number of string matches in current block */
+ this.insert = 0; /* bytes at end of window left to insert */
+
+
+ this.bi_buf = 0;
+ /* Output buffer. bits are inserted starting at the bottom (least
+ * significant bits).
+ */
+ this.bi_valid = 0;
+ /* Number of valid bits in bi_buf. All bits above the last valid bit
+ * are always zero.
+ */
+
+ // Used for window memory init. We safely ignore it for JS. That makes
+ // sense only for pointers and memory check tools.
+ //this.high_water = 0;
+ /* High water mark offset in window for initialized bytes -- bytes above
+ * this are set to zero in order to avoid memory check warnings when
+ * longest match routines access bytes past the input. This is then
+ * updated to the new high water mark.
+ */
+}
+
+
+function deflateResetKeep(strm) {
+ var s;
+
+ if (!strm || !strm.state) {
+ return err(strm, Z_STREAM_ERROR);
+ }
+
+ strm.total_in = strm.total_out = 0;
+ strm.data_type = Z_UNKNOWN;
+
+ s = strm.state;
+ s.pending = 0;
+ s.pending_out = 0;
+
+ if (s.wrap < 0) {
+ s.wrap = -s.wrap;
+ /* was made negative by deflate(..., Z_FINISH); */
+ }
+ s.status = (s.wrap ? INIT_STATE : BUSY_STATE);
+ strm.adler = (s.wrap === 2) ?
+ 0 // crc32(0, Z_NULL, 0)
+ :
+ 1; // adler32(0, Z_NULL, 0)
+ s.last_flush = Z_NO_FLUSH;
+ trees._tr_init(s);
+ return Z_OK;
+}
+
+
+function deflateReset(strm) {
+ var ret = deflateResetKeep(strm);
+ if (ret === Z_OK) {
+ lm_init(strm.state);
+ }
+ return ret;
+}
+
+
+function deflateSetHeader(strm, head) {
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ if (strm.state.wrap !== 2) { return Z_STREAM_ERROR; }
+ strm.state.gzhead = head;
+ return Z_OK;
+}
+
+
+function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
+ if (!strm) { // === Z_NULL
+ return Z_STREAM_ERROR;
+ }
+ var wrap = 1;
+
+ if (level === Z_DEFAULT_COMPRESSION) {
+ level = 6;
+ }
+
+ if (windowBits < 0) { /* suppress zlib wrapper */
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+
+ else if (windowBits > 15) {
+ wrap = 2; /* write gzip wrapper instead */
+ windowBits -= 16;
+ }
+
+
+ if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED ||
+ windowBits < 8 || windowBits > 15 || level < 0 || level > 9 ||
+ strategy < 0 || strategy > Z_FIXED) {
+ return err(strm, Z_STREAM_ERROR);
+ }
+
+
+ if (windowBits === 8) {
+ windowBits = 9;
+ }
+ /* until 256-byte window bug fixed */
+
+ var s = new DeflateState();
+
+ strm.state = s;
+ s.strm = strm;
+
+ s.wrap = wrap;
+ s.gzhead = null;
+ s.w_bits = windowBits;
+ s.w_size = 1 << s.w_bits;
+ s.w_mask = s.w_size - 1;
+
+ s.hash_bits = memLevel + 7;
+ s.hash_size = 1 << s.hash_bits;
+ s.hash_mask = s.hash_size - 1;
+ s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);
+
+ s.window = new utils.Buf8(s.w_size * 2);
+ s.head = new utils.Buf16(s.hash_size);
+ s.prev = new utils.Buf16(s.w_size);
+
+ // Don't need mem init magic for JS.
+ //s.high_water = 0; /* nothing written to s->window yet */
+
+ s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
+
+ s.pending_buf_size = s.lit_bufsize * 4;
+
+ //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2);
+ //s->pending_buf = (uchf *) overlay;
+ s.pending_buf = new utils.Buf8(s.pending_buf_size);
+
+ // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)
+ //s->d_buf = overlay + s->lit_bufsize/sizeof(ush);
+ s.d_buf = 1 * s.lit_bufsize;
+
+ //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize;
+ s.l_buf = (1 + 2) * s.lit_bufsize;
+
+ s.level = level;
+ s.strategy = strategy;
+ s.method = method;
+
+ return deflateReset(strm);
+}
+
+function deflateInit(strm, level) {
+ return deflateInit2(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+}
+
+
+function deflate(strm, flush) {
+ var old_flush, s;
+ var beg, val; // for gzip header write only
+
+ if (!strm || !strm.state ||
+ flush > Z_BLOCK || flush < 0) {
+ return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;
+ }
+
+ s = strm.state;
+
+ if (!strm.output ||
+ (!strm.input && strm.avail_in !== 0) ||
+ (s.status === FINISH_STATE && flush !== Z_FINISH)) {
+ return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);
+ }
+
+ s.strm = strm; /* just in case */
+ old_flush = s.last_flush;
+ s.last_flush = flush;
+
+ /* Write the header */
+ if (s.status === INIT_STATE) {
+
+ if (s.wrap === 2) { // GZIP header
+ strm.adler = 0; //crc32(0L, Z_NULL, 0);
+ put_byte(s, 31);
+ put_byte(s, 139);
+ put_byte(s, 8);
+ if (!s.gzhead) { // s->gzhead == Z_NULL
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, 0);
+ put_byte(s, s.level === 9 ? 2 :
+ (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
+ 4 : 0));
+ put_byte(s, OS_CODE);
+ s.status = BUSY_STATE;
+ }
+ else {
+ put_byte(s, (s.gzhead.text ? 1 : 0) +
+ (s.gzhead.hcrc ? 2 : 0) +
+ (!s.gzhead.extra ? 0 : 4) +
+ (!s.gzhead.name ? 0 : 8) +
+ (!s.gzhead.comment ? 0 : 16)
+ );
+ put_byte(s, s.gzhead.time & 0xff);
+ put_byte(s, (s.gzhead.time >> 8) & 0xff);
+ put_byte(s, (s.gzhead.time >> 16) & 0xff);
+ put_byte(s, (s.gzhead.time >> 24) & 0xff);
+ put_byte(s, s.level === 9 ? 2 :
+ (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ?
+ 4 : 0));
+ put_byte(s, s.gzhead.os & 0xff);
+ if (s.gzhead.extra && s.gzhead.extra.length) {
+ put_byte(s, s.gzhead.extra.length & 0xff);
+ put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
+ }
+ if (s.gzhead.hcrc) {
+ strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
+ }
+ s.gzindex = 0;
+ s.status = EXTRA_STATE;
+ }
+ }
+ else // DEFLATE header
+ {
+ var header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
+ var level_flags = -1;
+
+ if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
+ level_flags = 0;
+ } else if (s.level < 6) {
+ level_flags = 1;
+ } else if (s.level === 6) {
+ level_flags = 2;
+ } else {
+ level_flags = 3;
+ }
+ header |= (level_flags << 6);
+ if (s.strstart !== 0) { header |= PRESET_DICT; }
+ header += 31 - (header % 31);
+
+ s.status = BUSY_STATE;
+ putShortMSB(s, header);
+
+ /* Save the adler32 of the preset dictionary: */
+ if (s.strstart !== 0) {
+ putShortMSB(s, strm.adler >>> 16);
+ putShortMSB(s, strm.adler & 0xffff);
+ }
+ strm.adler = 1; // adler32(0L, Z_NULL, 0);
+ }
+ }
+
+//#ifdef GZIP
+ if (s.status === EXTRA_STATE) {
+ if (s.gzhead.extra/* != Z_NULL*/) {
+ beg = s.pending; /* start of bytes to update crc */
+
+ while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
+ if (s.pending === s.pending_buf_size) {
+ if (s.gzhead.hcrc && s.pending > beg) {
+ strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
+ }
+ flush_pending(strm);
+ beg = s.pending;
+ if (s.pending === s.pending_buf_size) {
+ break;
+ }
+ }
+ put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
+ s.gzindex++;
+ }
+ if (s.gzhead.hcrc && s.pending > beg) {
+ strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
+ }
+ if (s.gzindex === s.gzhead.extra.length) {
+ s.gzindex = 0;
+ s.status = NAME_STATE;
+ }
+ }
+ else {
+ s.status = NAME_STATE;
+ }
+ }
+ if (s.status === NAME_STATE) {
+ if (s.gzhead.name/* != Z_NULL*/) {
+ beg = s.pending; /* start of bytes to update crc */
+ //int val;
+
+ do {
+ if (s.pending === s.pending_buf_size) {
+ if (s.gzhead.hcrc && s.pending > beg) {
+ strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
+ }
+ flush_pending(strm);
+ beg = s.pending;
+ if (s.pending === s.pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ // JS specific: little magic to add zero terminator to end of string
+ if (s.gzindex < s.gzhead.name.length) {
+ val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
+ } else {
+ val = 0;
+ }
+ put_byte(s, val);
+ } while (val !== 0);
+
+ if (s.gzhead.hcrc && s.pending > beg) {
+ strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
+ }
+ if (val === 0) {
+ s.gzindex = 0;
+ s.status = COMMENT_STATE;
+ }
+ }
+ else {
+ s.status = COMMENT_STATE;
+ }
+ }
+ if (s.status === COMMENT_STATE) {
+ if (s.gzhead.comment/* != Z_NULL*/) {
+ beg = s.pending; /* start of bytes to update crc */
+ //int val;
+
+ do {
+ if (s.pending === s.pending_buf_size) {
+ if (s.gzhead.hcrc && s.pending > beg) {
+ strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
+ }
+ flush_pending(strm);
+ beg = s.pending;
+ if (s.pending === s.pending_buf_size) {
+ val = 1;
+ break;
+ }
+ }
+ // JS specific: little magic to add zero terminator to end of string
+ if (s.gzindex < s.gzhead.comment.length) {
+ val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
+ } else {
+ val = 0;
+ }
+ put_byte(s, val);
+ } while (val !== 0);
+
+ if (s.gzhead.hcrc && s.pending > beg) {
+ strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
+ }
+ if (val === 0) {
+ s.status = HCRC_STATE;
+ }
+ }
+ else {
+ s.status = HCRC_STATE;
+ }
+ }
+ if (s.status === HCRC_STATE) {
+ if (s.gzhead.hcrc) {
+ if (s.pending + 2 > s.pending_buf_size) {
+ flush_pending(strm);
+ }
+ if (s.pending + 2 <= s.pending_buf_size) {
+ put_byte(s, strm.adler & 0xff);
+ put_byte(s, (strm.adler >> 8) & 0xff);
+ strm.adler = 0; //crc32(0L, Z_NULL, 0);
+ s.status = BUSY_STATE;
+ }
+ }
+ else {
+ s.status = BUSY_STATE;
+ }
+ }
+//#endif
+
+ /* Flush as much pending output as possible */
+ if (s.pending !== 0) {
+ flush_pending(strm);
+ if (strm.avail_out === 0) {
+ /* Since avail_out is 0, deflate will be called again with
+ * more output space, but possibly with both pending and
+ * avail_in equal to zero. There won't be anything to do,
+ * but this is not an error situation so make sure we
+ * return OK instead of BUF_ERROR at next call of deflate:
+ */
+ s.last_flush = -1;
+ return Z_OK;
+ }
+
+ /* Make sure there is something to do and avoid duplicate consecutive
+ * flushes. For repeated and useless calls with Z_FINISH, we keep
+ * returning Z_STREAM_END instead of Z_BUF_ERROR.
+ */
+ } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) &&
+ flush !== Z_FINISH) {
+ return err(strm, Z_BUF_ERROR);
+ }
+
+ /* User must not provide more input after the first FINISH: */
+ if (s.status === FINISH_STATE && strm.avail_in !== 0) {
+ return err(strm, Z_BUF_ERROR);
+ }
+
+ /* Start a new block or continue the current one.
+ */
+ if (strm.avail_in !== 0 || s.lookahead !== 0 ||
+ (flush !== Z_NO_FLUSH && s.status !== FINISH_STATE)) {
+ var bstate = (s.strategy === Z_HUFFMAN_ONLY) ? deflate_huff(s, flush) :
+ (s.strategy === Z_RLE ? deflate_rle(s, flush) :
+ configuration_table[s.level].func(s, flush));
+
+ if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
+ s.status = FINISH_STATE;
+ }
+ if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
+ if (strm.avail_out === 0) {
+ s.last_flush = -1;
+ /* avoid BUF_ERROR next call, see above */
+ }
+ return Z_OK;
+ /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
+ * of deflate should use the same flush parameter to make sure
+ * that the flush is complete. So we don't have to output an
+ * empty block here, this will be done at next call. This also
+ * ensures that for a very small output buffer, we emit at most
+ * one empty block.
+ */
+ }
+ if (bstate === BS_BLOCK_DONE) {
+ if (flush === Z_PARTIAL_FLUSH) {
+ trees._tr_align(s);
+ }
+ else if (flush !== Z_BLOCK) { /* FULL_FLUSH or SYNC_FLUSH */
+
+ trees._tr_stored_block(s, 0, 0, false);
+ /* For a full flush, this empty block will be recognized
+ * as a special marker by inflate_sync().
+ */
+ if (flush === Z_FULL_FLUSH) {
+ /*** CLEAR_HASH(s); ***/ /* forget history */
+ zero(s.head); // Fill with NIL (= 0);
+
+ if (s.lookahead === 0) {
+ s.strstart = 0;
+ s.block_start = 0;
+ s.insert = 0;
+ }
+ }
+ }
+ flush_pending(strm);
+ if (strm.avail_out === 0) {
+ s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */
+ return Z_OK;
+ }
+ }
+ }
+ //Assert(strm->avail_out > 0, "bug2");
+ //if (strm.avail_out <= 0) { throw new Error("bug2");}
+
+ if (flush !== Z_FINISH) { return Z_OK; }
+ if (s.wrap <= 0) { return Z_STREAM_END; }
+
+ /* Write the trailer */
+ if (s.wrap === 2) {
+ put_byte(s, strm.adler & 0xff);
+ put_byte(s, (strm.adler >> 8) & 0xff);
+ put_byte(s, (strm.adler >> 16) & 0xff);
+ put_byte(s, (strm.adler >> 24) & 0xff);
+ put_byte(s, strm.total_in & 0xff);
+ put_byte(s, (strm.total_in >> 8) & 0xff);
+ put_byte(s, (strm.total_in >> 16) & 0xff);
+ put_byte(s, (strm.total_in >> 24) & 0xff);
+ }
+ else
+ {
+ putShortMSB(s, strm.adler >>> 16);
+ putShortMSB(s, strm.adler & 0xffff);
+ }
+
+ flush_pending(strm);
+ /* If avail_out is zero, the application will call deflate again
+ * to flush the rest.
+ */
+ if (s.wrap > 0) { s.wrap = -s.wrap; }
+ /* write the trailer only once! */
+ return s.pending !== 0 ? Z_OK : Z_STREAM_END;
+}
+
+function deflateEnd(strm) {
+ var status;
+
+ if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
+ return Z_STREAM_ERROR;
+ }
+
+ status = strm.state.status;
+ if (status !== INIT_STATE &&
+ status !== EXTRA_STATE &&
+ status !== NAME_STATE &&
+ status !== COMMENT_STATE &&
+ status !== HCRC_STATE &&
+ status !== BUSY_STATE &&
+ status !== FINISH_STATE
+ ) {
+ return err(strm, Z_STREAM_ERROR);
+ }
+
+ strm.state = null;
+
+ return status === BUSY_STATE ? err(strm, Z_DATA_ERROR) : Z_OK;
+}
+
+
+/* =========================================================================
+ * Initializes the compression dictionary from the given byte
+ * sequence without producing any compressed output.
+ */
+function deflateSetDictionary(strm, dictionary) {
+ var dictLength = dictionary.length;
+
+ var s;
+ var str, n;
+ var wrap;
+ var avail;
+ var next;
+ var input;
+ var tmpDict;
+
+ if (!strm/*== Z_NULL*/ || !strm.state/*== Z_NULL*/) {
+ return Z_STREAM_ERROR;
+ }
+
+ s = strm.state;
+ wrap = s.wrap;
+
+ if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* when using zlib wrappers, compute Adler-32 for provided dictionary */
+ if (wrap === 1) {
+ /* adler32(strm->adler, dictionary, dictLength); */
+ strm.adler = adler32(strm.adler, dictionary, dictLength, 0);
+ }
+
+ s.wrap = 0; /* avoid computing Adler-32 in read_buf */
+
+ /* if dictionary would fill window, just replace the history */
+ if (dictLength >= s.w_size) {
+ if (wrap === 0) { /* already empty otherwise */
+ /*** CLEAR_HASH(s); ***/
+ zero(s.head); // Fill with NIL (= 0);
+ s.strstart = 0;
+ s.block_start = 0;
+ s.insert = 0;
+ }
+ /* use the tail */
+ // dictionary = dictionary.slice(dictLength - s.w_size);
+ tmpDict = new utils.Buf8(s.w_size);
+ utils.arraySet(tmpDict, dictionary, dictLength - s.w_size, s.w_size, 0);
+ dictionary = tmpDict;
+ dictLength = s.w_size;
+ }
+ /* insert dictionary into window and hash */
+ avail = strm.avail_in;
+ next = strm.next_in;
+ input = strm.input;
+ strm.avail_in = dictLength;
+ strm.next_in = 0;
+ strm.input = dictionary;
+ fill_window(s);
+ while (s.lookahead >= MIN_MATCH) {
+ str = s.strstart;
+ n = s.lookahead - (MIN_MATCH - 1);
+ do {
+ /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
+ s.ins_h = ((s.ins_h << s.hash_shift) ^ s.window[str + MIN_MATCH - 1]) & s.hash_mask;
+
+ s.prev[str & s.w_mask] = s.head[s.ins_h];
+
+ s.head[s.ins_h] = str;
+ str++;
+ } while (--n);
+ s.strstart = str;
+ s.lookahead = MIN_MATCH - 1;
+ fill_window(s);
+ }
+ s.strstart += s.lookahead;
+ s.block_start = s.strstart;
+ s.insert = s.lookahead;
+ s.lookahead = 0;
+ s.match_length = s.prev_length = MIN_MATCH - 1;
+ s.match_available = 0;
+ strm.next_in = next;
+ strm.input = input;
+ strm.avail_in = avail;
+ s.wrap = wrap;
+ return Z_OK;
+}
+
+
+exports.deflateInit = deflateInit;
+exports.deflateInit2 = deflateInit2;
+exports.deflateReset = deflateReset;
+exports.deflateResetKeep = deflateResetKeep;
+exports.deflateSetHeader = deflateSetHeader;
+exports.deflate = deflate;
+exports.deflateEnd = deflateEnd;
+exports.deflateSetDictionary = deflateSetDictionary;
+exports.deflateInfo = 'pako deflate (from Nodeca project)';
+
+/* Not implemented
+exports.deflateBound = deflateBound;
+exports.deflateCopy = deflateCopy;
+exports.deflateParams = deflateParams;
+exports.deflatePending = deflatePending;
+exports.deflatePrime = deflatePrime;
+exports.deflateTune = deflateTune;
+*/
+
+},{"../utils/common":41,"./adler32":43,"./crc32":45,"./messages":51,"./trees":52}],47:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+function GZheader() {
+ /* true if compressed data believed to be text */
+ this.text = 0;
+ /* modification time */
+ this.time = 0;
+ /* extra flags (not used when writing a gzip file) */
+ this.xflags = 0;
+ /* operating system */
+ this.os = 0;
+ /* pointer to extra field or Z_NULL if none */
+ this.extra = null;
+ /* extra field length (valid if extra != Z_NULL) */
+ this.extra_len = 0; // Actually, we don't need it in JS,
+ // but leave for few code modifications
+
+ //
+ // Setup limits is not necessary because in js we should not preallocate memory
+ // for inflate use constant limit in 65536 bytes
+ //
+
+ /* space at extra (only when reading header) */
+ // this.extra_max = 0;
+ /* pointer to zero-terminated file name or Z_NULL */
+ this.name = '';
+ /* space at name (only when reading header) */
+ // this.name_max = 0;
+ /* pointer to zero-terminated comment or Z_NULL */
+ this.comment = '';
+ /* space at comment (only when reading header) */
+ // this.comm_max = 0;
+ /* true if there was or will be a header crc */
+ this.hcrc = 0;
+ /* true when done reading gzip header (not used when writing a gzip file) */
+ this.done = false;
+}
+
+module.exports = GZheader;
+
+},{}],48:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+// See state defs from inflate.js
+var BAD = 30; /* got a data error -- remain here until reset */
+var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
+
+/*
+ Decode literal, length, and distance codes and write out the resulting
+ literal and match bytes until either not enough input or output is
+ available, an end-of-block is encountered, or a data error is encountered.
+ When large enough input and output buffers are supplied to inflate(), for
+ example, a 16K input buffer and a 64K output buffer, more than 95% of the
+ inflate execution time is spent in this routine.
+
+ Entry assumptions:
+
+ state.mode === LEN
+ strm.avail_in >= 6
+ strm.avail_out >= 258
+ start >= strm.avail_out
+ state.bits < 8
+
+ On return, state.mode is one of:
+
+ LEN -- ran out of enough output space or enough available input
+ TYPE -- reached end of block code, inflate() to interpret next block
+ BAD -- error in block data
+
+ Notes:
+
+ - The maximum input bits used by a length/distance pair is 15 bits for the
+ length code, 5 bits for the length extra, 15 bits for the distance code,
+ and 13 bits for the distance extra. This totals 48 bits, or six bytes.
+ Therefore if strm.avail_in >= 6, then there is enough input to avoid
+ checking for available input while decoding.
+
+ - The maximum bytes that a single length/distance pair can output is 258
+ bytes, which is the maximum length that can be coded. inflate_fast()
+ requires strm.avail_out >= 258 for each loop to avoid checking for
+ output space.
+ */
+module.exports = function inflate_fast(strm, start) {
+ var state;
+ var _in; /* local strm.input */
+ var last; /* have enough input while in < last */
+ var _out; /* local strm.output */
+ var beg; /* inflate()'s initial strm.output */
+ var end; /* while out < end, enough space available */
+//#ifdef INFLATE_STRICT
+ var dmax; /* maximum distance from zlib header */
+//#endif
+ var wsize; /* window size or zero if not using window */
+ var whave; /* valid bytes in the window */
+ var wnext; /* window write index */
+ // Use `s_window` instead `window`, avoid conflict with instrumentation tools
+ var s_window; /* allocated sliding window, if wsize != 0 */
+ var hold; /* local strm.hold */
+ var bits; /* local strm.bits */
+ var lcode; /* local strm.lencode */
+ var dcode; /* local strm.distcode */
+ var lmask; /* mask for first level of length codes */
+ var dmask; /* mask for first level of distance codes */
+ var here; /* retrieved table entry */
+ var op; /* code bits, operation, extra bits, or */
+ /* window position, window bytes to copy */
+ var len; /* match length, unused bytes */
+ var dist; /* match distance */
+ var from; /* where to copy match from */
+ var from_source;
+
+
+ var input, output; // JS specific, because we have no pointers
+
+ /* copy state to local variables */
+ state = strm.state;
+ //here = state.here;
+ _in = strm.next_in;
+ input = strm.input;
+ last = _in + (strm.avail_in - 5);
+ _out = strm.next_out;
+ output = strm.output;
+ beg = _out - (start - strm.avail_out);
+ end = _out + (strm.avail_out - 257);
+//#ifdef INFLATE_STRICT
+ dmax = state.dmax;
+//#endif
+ wsize = state.wsize;
+ whave = state.whave;
+ wnext = state.wnext;
+ s_window = state.window;
+ hold = state.hold;
+ bits = state.bits;
+ lcode = state.lencode;
+ dcode = state.distcode;
+ lmask = (1 << state.lenbits) - 1;
+ dmask = (1 << state.distbits) - 1;
+
+
+ /* decode literals and length/distances until end-of-block or not enough
+ input data or output space */
+
+ top:
+ do {
+ if (bits < 15) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+
+ here = lcode[hold & lmask];
+
+ dolen:
+ for (;;) { // Goto emulation
+ op = here >>> 24/*here.bits*/;
+ hold >>>= op;
+ bits -= op;
+ op = (here >>> 16) & 0xff/*here.op*/;
+ if (op === 0) { /* literal */
+ //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ // "inflate: literal '%c'\n" :
+ // "inflate: literal 0x%02x\n", here.val));
+ output[_out++] = here & 0xffff/*here.val*/;
+ }
+ else if (op & 16) { /* length base */
+ len = here & 0xffff/*here.val*/;
+ op &= 15; /* number of extra bits */
+ if (op) {
+ if (bits < op) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+ len += hold & ((1 << op) - 1);
+ hold >>>= op;
+ bits -= op;
+ }
+ //Tracevv((stderr, "inflate: length %u\n", len));
+ if (bits < 15) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+ here = dcode[hold & dmask];
+
+ dodist:
+ for (;;) { // goto emulation
+ op = here >>> 24/*here.bits*/;
+ hold >>>= op;
+ bits -= op;
+ op = (here >>> 16) & 0xff/*here.op*/;
+
+ if (op & 16) { /* distance base */
+ dist = here & 0xffff/*here.val*/;
+ op &= 15; /* number of extra bits */
+ if (bits < op) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ if (bits < op) {
+ hold += input[_in++] << bits;
+ bits += 8;
+ }
+ }
+ dist += hold & ((1 << op) - 1);
+//#ifdef INFLATE_STRICT
+ if (dist > dmax) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break top;
+ }
+//#endif
+ hold >>>= op;
+ bits -= op;
+ //Tracevv((stderr, "inflate: distance %u\n", dist));
+ op = _out - beg; /* max distance in output */
+ if (dist > op) { /* see if copy from window */
+ op = dist - op; /* distance back in window */
+ if (op > whave) {
+ if (state.sane) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break top;
+ }
+
+// (!) This block is disabled in zlib defailts,
+// don't enable it for binary compatibility
+//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+// if (len <= op - whave) {
+// do {
+// output[_out++] = 0;
+// } while (--len);
+// continue top;
+// }
+// len -= op - whave;
+// do {
+// output[_out++] = 0;
+// } while (--op > whave);
+// if (op === 0) {
+// from = _out - dist;
+// do {
+// output[_out++] = output[from++];
+// } while (--len);
+// continue top;
+// }
+//#endif
+ }
+ from = 0; // window index
+ from_source = s_window;
+ if (wnext === 0) { /* very common case */
+ from += wsize - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ output[_out++] = s_window[from++];
+ } while (--op);
+ from = _out - dist; /* rest from output */
+ from_source = output;
+ }
+ }
+ else if (wnext < op) { /* wrap around window */
+ from += wsize + wnext - op;
+ op -= wnext;
+ if (op < len) { /* some from end of window */
+ len -= op;
+ do {
+ output[_out++] = s_window[from++];
+ } while (--op);
+ from = 0;
+ if (wnext < len) { /* some from start of window */
+ op = wnext;
+ len -= op;
+ do {
+ output[_out++] = s_window[from++];
+ } while (--op);
+ from = _out - dist; /* rest from output */
+ from_source = output;
+ }
+ }
+ }
+ else { /* contiguous in window */
+ from += wnext - op;
+ if (op < len) { /* some from window */
+ len -= op;
+ do {
+ output[_out++] = s_window[from++];
+ } while (--op);
+ from = _out - dist; /* rest from output */
+ from_source = output;
+ }
+ }
+ while (len > 2) {
+ output[_out++] = from_source[from++];
+ output[_out++] = from_source[from++];
+ output[_out++] = from_source[from++];
+ len -= 3;
+ }
+ if (len) {
+ output[_out++] = from_source[from++];
+ if (len > 1) {
+ output[_out++] = from_source[from++];
+ }
+ }
+ }
+ else {
+ from = _out - dist; /* copy direct from output */
+ do { /* minimum length is three */
+ output[_out++] = output[from++];
+ output[_out++] = output[from++];
+ output[_out++] = output[from++];
+ len -= 3;
+ } while (len > 2);
+ if (len) {
+ output[_out++] = output[from++];
+ if (len > 1) {
+ output[_out++] = output[from++];
+ }
+ }
+ }
+ }
+ else if ((op & 64) === 0) { /* 2nd level distance code */
+ here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
+ continue dodist;
+ }
+ else {
+ strm.msg = 'invalid distance code';
+ state.mode = BAD;
+ break top;
+ }
+
+ break; // need to emulate goto via "continue"
+ }
+ }
+ else if ((op & 64) === 0) { /* 2nd level length code */
+ here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
+ continue dolen;
+ }
+ else if (op & 32) { /* end-of-block */
+ //Tracevv((stderr, "inflate: end of block\n"));
+ state.mode = TYPE;
+ break top;
+ }
+ else {
+ strm.msg = 'invalid literal/length code';
+ state.mode = BAD;
+ break top;
+ }
+
+ break; // need to emulate goto via "continue"
+ }
+ } while (_in < last && _out < end);
+
+ /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
+ len = bits >> 3;
+ _in -= len;
+ bits -= len << 3;
+ hold &= (1 << bits) - 1;
+
+ /* update state and return */
+ strm.next_in = _in;
+ strm.next_out = _out;
+ strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
+ strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
+ state.hold = hold;
+ state.bits = bits;
+ return;
+};
+
+},{}],49:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+var utils = require('../utils/common');
+var adler32 = require('./adler32');
+var crc32 = require('./crc32');
+var inflate_fast = require('./inffast');
+var inflate_table = require('./inftrees');
+
+var CODES = 0;
+var LENS = 1;
+var DISTS = 2;
+
+/* Public constants ==========================================================*/
+/* ===========================================================================*/
+
+
+/* Allowed flush values; see deflate() and inflate() below for details */
+//var Z_NO_FLUSH = 0;
+//var Z_PARTIAL_FLUSH = 1;
+//var Z_SYNC_FLUSH = 2;
+//var Z_FULL_FLUSH = 3;
+var Z_FINISH = 4;
+var Z_BLOCK = 5;
+var Z_TREES = 6;
+
+
+/* Return codes for the compression/decompression functions. Negative values
+ * are errors, positive values are used for special but normal events.
+ */
+var Z_OK = 0;
+var Z_STREAM_END = 1;
+var Z_NEED_DICT = 2;
+//var Z_ERRNO = -1;
+var Z_STREAM_ERROR = -2;
+var Z_DATA_ERROR = -3;
+var Z_MEM_ERROR = -4;
+var Z_BUF_ERROR = -5;
+//var Z_VERSION_ERROR = -6;
+
+/* The deflate compression method */
+var Z_DEFLATED = 8;
+
+
+/* STATES ====================================================================*/
+/* ===========================================================================*/
+
+
+var HEAD = 1; /* i: waiting for magic header */
+var FLAGS = 2; /* i: waiting for method and flags (gzip) */
+var TIME = 3; /* i: waiting for modification time (gzip) */
+var OS = 4; /* i: waiting for extra flags and operating system (gzip) */
+var EXLEN = 5; /* i: waiting for extra length (gzip) */
+var EXTRA = 6; /* i: waiting for extra bytes (gzip) */
+var NAME = 7; /* i: waiting for end of file name (gzip) */
+var COMMENT = 8; /* i: waiting for end of comment (gzip) */
+var HCRC = 9; /* i: waiting for header crc (gzip) */
+var DICTID = 10; /* i: waiting for dictionary check value */
+var DICT = 11; /* waiting for inflateSetDictionary() call */
+var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
+var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */
+var STORED = 14; /* i: waiting for stored size (length and complement) */
+var COPY_ = 15; /* i/o: same as COPY below, but only first time in */
+var COPY = 16; /* i/o: waiting for input or output to copy stored block */
+var TABLE = 17; /* i: waiting for dynamic block table lengths */
+var LENLENS = 18; /* i: waiting for code length code lengths */
+var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */
+var LEN_ = 20; /* i: same as LEN below, but only first time in */
+var LEN = 21; /* i: waiting for length/lit/eob code */
+var LENEXT = 22; /* i: waiting for length extra bits */
+var DIST = 23; /* i: waiting for distance code */
+var DISTEXT = 24; /* i: waiting for distance extra bits */
+var MATCH = 25; /* o: waiting for output space to copy string */
+var LIT = 26; /* o: waiting for output space to write literal */
+var CHECK = 27; /* i: waiting for 32-bit check value */
+var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */
+var DONE = 29; /* finished check, done -- remain here until reset */
+var BAD = 30; /* got a data error -- remain here until reset */
+var MEM = 31; /* got an inflate() memory error -- remain here until reset */
+var SYNC = 32; /* looking for synchronization bytes to restart inflate() */
+
+/* ===========================================================================*/
+
+
+
+var ENOUGH_LENS = 852;
+var ENOUGH_DISTS = 592;
+//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
+
+var MAX_WBITS = 15;
+/* 32K LZ77 window */
+var DEF_WBITS = MAX_WBITS;
+
+
+function zswap32(q) {
+ return (((q >>> 24) & 0xff) +
+ ((q >>> 8) & 0xff00) +
+ ((q & 0xff00) << 8) +
+ ((q & 0xff) << 24));
+}
+
+
+function InflateState() {
+ this.mode = 0; /* current inflate mode */
+ this.last = false; /* true if processing last block */
+ this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
+ this.havedict = false; /* true if dictionary provided */
+ this.flags = 0; /* gzip header method and flags (0 if zlib) */
+ this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */
+ this.check = 0; /* protected copy of check value */
+ this.total = 0; /* protected copy of output count */
+ // TODO: may be {}
+ this.head = null; /* where to save gzip header information */
+
+ /* sliding window */
+ this.wbits = 0; /* log base 2 of requested window size */
+ this.wsize = 0; /* window size or zero if not using window */
+ this.whave = 0; /* valid bytes in the window */
+ this.wnext = 0; /* window write index */
+ this.window = null; /* allocated sliding window, if needed */
+
+ /* bit accumulator */
+ this.hold = 0; /* input bit accumulator */
+ this.bits = 0; /* number of bits in "in" */
+
+ /* for string and stored block copying */
+ this.length = 0; /* literal or length of data to copy */
+ this.offset = 0; /* distance back to copy string from */
+
+ /* for table and code decoding */
+ this.extra = 0; /* extra bits needed */
+
+ /* fixed and dynamic code tables */
+ this.lencode = null; /* starting table for length/literal codes */
+ this.distcode = null; /* starting table for distance codes */
+ this.lenbits = 0; /* index bits for lencode */
+ this.distbits = 0; /* index bits for distcode */
+
+ /* dynamic table building */
+ this.ncode = 0; /* number of code length code lengths */
+ this.nlen = 0; /* number of length code lengths */
+ this.ndist = 0; /* number of distance code lengths */
+ this.have = 0; /* number of code lengths in lens[] */
+ this.next = null; /* next available space in codes[] */
+
+ this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
+ this.work = new utils.Buf16(288); /* work area for code table building */
+
+ /*
+ because we don't have pointers in js, we use lencode and distcode directly
+ as buffers so we don't need codes
+ */
+ //this.codes = new utils.Buf32(ENOUGH); /* space for code tables */
+ this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */
+ this.distdyn = null; /* dynamic table for distance codes (JS specific) */
+ this.sane = 0; /* if false, allow invalid distance too far */
+ this.back = 0; /* bits back of last unprocessed length/lit */
+ this.was = 0; /* initial length of match */
+}
+
+function inflateResetKeep(strm) {
+ var state;
+
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+ strm.total_in = strm.total_out = state.total = 0;
+ strm.msg = ''; /*Z_NULL*/
+ if (state.wrap) { /* to support ill-conceived Java test suite */
+ strm.adler = state.wrap & 1;
+ }
+ state.mode = HEAD;
+ state.last = 0;
+ state.havedict = 0;
+ state.dmax = 32768;
+ state.head = null/*Z_NULL*/;
+ state.hold = 0;
+ state.bits = 0;
+ //state.lencode = state.distcode = state.next = state.codes;
+ state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
+ state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);
+
+ state.sane = 1;
+ state.back = -1;
+ //Tracev((stderr, "inflate: reset\n"));
+ return Z_OK;
+}
+
+function inflateReset(strm) {
+ var state;
+
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+ state.wsize = 0;
+ state.whave = 0;
+ state.wnext = 0;
+ return inflateResetKeep(strm);
+
+}
+
+function inflateReset2(strm, windowBits) {
+ var wrap;
+ var state;
+
+ /* get the state */
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+
+ /* extract wrap request from windowBits parameter */
+ if (windowBits < 0) {
+ wrap = 0;
+ windowBits = -windowBits;
+ }
+ else {
+ wrap = (windowBits >> 4) + 1;
+ if (windowBits < 48) {
+ windowBits &= 15;
+ }
+ }
+
+ /* set number of window bits, free window if different */
+ if (windowBits && (windowBits < 8 || windowBits > 15)) {
+ return Z_STREAM_ERROR;
+ }
+ if (state.window !== null && state.wbits !== windowBits) {
+ state.window = null;
+ }
+
+ /* update state and reset the rest of it */
+ state.wrap = wrap;
+ state.wbits = windowBits;
+ return inflateReset(strm);
+}
+
+function inflateInit2(strm, windowBits) {
+ var ret;
+ var state;
+
+ if (!strm) { return Z_STREAM_ERROR; }
+ //strm.msg = Z_NULL; /* in case we return an error */
+
+ state = new InflateState();
+
+ //if (state === Z_NULL) return Z_MEM_ERROR;
+ //Tracev((stderr, "inflate: allocated\n"));
+ strm.state = state;
+ state.window = null/*Z_NULL*/;
+ ret = inflateReset2(strm, windowBits);
+ if (ret !== Z_OK) {
+ strm.state = null/*Z_NULL*/;
+ }
+ return ret;
+}
+
+function inflateInit(strm) {
+ return inflateInit2(strm, DEF_WBITS);
+}
+
+
+/*
+ Return state with length and distance decoding tables and index sizes set to
+ fixed code decoding. Normally this returns fixed tables from inffixed.h.
+ If BUILDFIXED is defined, then instead this routine builds the tables the
+ first time it's called, and returns those tables the first time and
+ thereafter. This reduces the size of the code by about 2K bytes, in
+ exchange for a little execution time. However, BUILDFIXED should not be
+ used for threaded applications, since the rewriting of the tables and virgin
+ may not be thread-safe.
+ */
+var virgin = true;
+
+var lenfix, distfix; // We have no pointers in JS, so keep tables separate
+
+function fixedtables(state) {
+ /* build fixed huffman tables if first call (may not be thread safe) */
+ if (virgin) {
+ var sym;
+
+ lenfix = new utils.Buf32(512);
+ distfix = new utils.Buf32(32);
+
+ /* literal/length table */
+ sym = 0;
+ while (sym < 144) { state.lens[sym++] = 8; }
+ while (sym < 256) { state.lens[sym++] = 9; }
+ while (sym < 280) { state.lens[sym++] = 7; }
+ while (sym < 288) { state.lens[sym++] = 8; }
+
+ inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 });
+
+ /* distance table */
+ sym = 0;
+ while (sym < 32) { state.lens[sym++] = 5; }
+
+ inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 });
+
+ /* do this just once */
+ virgin = false;
+ }
+
+ state.lencode = lenfix;
+ state.lenbits = 9;
+ state.distcode = distfix;
+ state.distbits = 5;
+}
+
+
+/*
+ Update the window with the last wsize (normally 32K) bytes written before
+ returning. If window does not exist yet, create it. This is only called
+ when a window is already in use, or when output has been written during this
+ inflate call, but the end of the deflate stream has not been reached yet.
+ It is also called to create a window for dictionary data when a dictionary
+ is loaded.
+
+ Providing output buffers larger than 32K to inflate() should provide a speed
+ advantage, since only the last 32K of output is copied to the sliding window
+ upon return from inflate(), and since all distances after the first 32K of
+ output will fall in the output data, making match copies simpler and faster.
+ The advantage may be dependent on the size of the processor's data caches.
+ */
+function updatewindow(strm, src, end, copy) {
+ var dist;
+ var state = strm.state;
+
+ /* if it hasn't been done already, allocate space for the window */
+ if (state.window === null) {
+ state.wsize = 1 << state.wbits;
+ state.wnext = 0;
+ state.whave = 0;
+
+ state.window = new utils.Buf8(state.wsize);
+ }
+
+ /* copy state->wsize or less output bytes into the circular window */
+ if (copy >= state.wsize) {
+ utils.arraySet(state.window, src, end - state.wsize, state.wsize, 0);
+ state.wnext = 0;
+ state.whave = state.wsize;
+ }
+ else {
+ dist = state.wsize - state.wnext;
+ if (dist > copy) {
+ dist = copy;
+ }
+ //zmemcpy(state->window + state->wnext, end - copy, dist);
+ utils.arraySet(state.window, src, end - copy, dist, state.wnext);
+ copy -= dist;
+ if (copy) {
+ //zmemcpy(state->window, end - copy, copy);
+ utils.arraySet(state.window, src, end - copy, copy, 0);
+ state.wnext = copy;
+ state.whave = state.wsize;
+ }
+ else {
+ state.wnext += dist;
+ if (state.wnext === state.wsize) { state.wnext = 0; }
+ if (state.whave < state.wsize) { state.whave += dist; }
+ }
+ }
+ return 0;
+}
+
+function inflate(strm, flush) {
+ var state;
+ var input, output; // input/output buffers
+ var next; /* next input INDEX */
+ var put; /* next output INDEX */
+ var have, left; /* available input and output */
+ var hold; /* bit buffer */
+ var bits; /* bits in bit buffer */
+ var _in, _out; /* save starting available input and output */
+ var copy; /* number of stored or match bytes to copy */
+ var from; /* where to copy match bytes from */
+ var from_source;
+ var here = 0; /* current decoding table entry */
+ var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
+ //var last; /* parent table entry */
+ var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
+ var len; /* length to copy for repeats, bits to drop */
+ var ret; /* return code */
+ var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */
+ var opts;
+
+ var n; // temporary var for NEED_BITS
+
+ var order = /* permutation of code lengths */
+ [ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 ];
+
+
+ if (!strm || !strm.state || !strm.output ||
+ (!strm.input && strm.avail_in !== 0)) {
+ return Z_STREAM_ERROR;
+ }
+
+ state = strm.state;
+ if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */
+
+
+ //--- LOAD() ---
+ put = strm.next_out;
+ output = strm.output;
+ left = strm.avail_out;
+ next = strm.next_in;
+ input = strm.input;
+ have = strm.avail_in;
+ hold = state.hold;
+ bits = state.bits;
+ //---
+
+ _in = have;
+ _out = left;
+ ret = Z_OK;
+
+ inf_leave: // goto emulation
+ for (;;) {
+ switch (state.mode) {
+ case HEAD:
+ if (state.wrap === 0) {
+ state.mode = TYPEDO;
+ break;
+ }
+ //=== NEEDBITS(16);
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */
+ state.check = 0/*crc32(0L, Z_NULL, 0)*/;
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = FLAGS;
+ break;
+ }
+ state.flags = 0; /* expect zlib header */
+ if (state.head) {
+ state.head.done = false;
+ }
+ if (!(state.wrap & 1) || /* check if zlib header allowed */
+ (((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
+ strm.msg = 'incorrect header check';
+ state.mode = BAD;
+ break;
+ }
+ if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
+ strm.msg = 'unknown compression method';
+ state.mode = BAD;
+ break;
+ }
+ //--- DROPBITS(4) ---//
+ hold >>>= 4;
+ bits -= 4;
+ //---//
+ len = (hold & 0x0f)/*BITS(4)*/ + 8;
+ if (state.wbits === 0) {
+ state.wbits = len;
+ }
+ else if (len > state.wbits) {
+ strm.msg = 'invalid window size';
+ state.mode = BAD;
+ break;
+ }
+ state.dmax = 1 << len;
+ //Tracev((stderr, "inflate: zlib header ok\n"));
+ strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
+ state.mode = hold & 0x200 ? DICTID : TYPE;
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ break;
+ case FLAGS:
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.flags = hold;
+ if ((state.flags & 0xff) !== Z_DEFLATED) {
+ strm.msg = 'unknown compression method';
+ state.mode = BAD;
+ break;
+ }
+ if (state.flags & 0xe000) {
+ strm.msg = 'unknown header flags set';
+ state.mode = BAD;
+ break;
+ }
+ if (state.head) {
+ state.head.text = ((hold >> 8) & 1);
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = TIME;
+ /* falls through */
+ case TIME:
+ //=== NEEDBITS(32); */
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (state.head) {
+ state.head.time = hold;
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC4(state.check, hold)
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ hbuf[2] = (hold >>> 16) & 0xff;
+ hbuf[3] = (hold >>> 24) & 0xff;
+ state.check = crc32(state.check, hbuf, 4, 0);
+ //===
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = OS;
+ /* falls through */
+ case OS:
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (state.head) {
+ state.head.xflags = (hold & 0xff);
+ state.head.os = (hold >> 8);
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = EXLEN;
+ /* falls through */
+ case EXLEN:
+ if (state.flags & 0x0400) {
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.length = hold;
+ if (state.head) {
+ state.head.extra_len = hold;
+ }
+ if (state.flags & 0x0200) {
+ //=== CRC2(state.check, hold);
+ hbuf[0] = hold & 0xff;
+ hbuf[1] = (hold >>> 8) & 0xff;
+ state.check = crc32(state.check, hbuf, 2, 0);
+ //===//
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ }
+ else if (state.head) {
+ state.head.extra = null/*Z_NULL*/;
+ }
+ state.mode = EXTRA;
+ /* falls through */
+ case EXTRA:
+ if (state.flags & 0x0400) {
+ copy = state.length;
+ if (copy > have) { copy = have; }
+ if (copy) {
+ if (state.head) {
+ len = state.head.extra_len - state.length;
+ if (!state.head.extra) {
+ // Use untyped array for more conveniend processing later
+ state.head.extra = new Array(state.head.extra_len);
+ }
+ utils.arraySet(
+ state.head.extra,
+ input,
+ next,
+ // extra field is limited to 65536 bytes
+ // - no need for additional size check
+ copy,
+ /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
+ len
+ );
+ //zmemcpy(state.head.extra + len, next,
+ // len + copy > state.head.extra_max ?
+ // state.head.extra_max - len : copy);
+ }
+ if (state.flags & 0x0200) {
+ state.check = crc32(state.check, input, copy, next);
+ }
+ have -= copy;
+ next += copy;
+ state.length -= copy;
+ }
+ if (state.length) { break inf_leave; }
+ }
+ state.length = 0;
+ state.mode = NAME;
+ /* falls through */
+ case NAME:
+ if (state.flags & 0x0800) {
+ if (have === 0) { break inf_leave; }
+ copy = 0;
+ do {
+ // TODO: 2 or 1 bytes?
+ len = input[next + copy++];
+ /* use constant limit because in js we should not preallocate memory */
+ if (state.head && len &&
+ (state.length < 65536 /*state.head.name_max*/)) {
+ state.head.name += String.fromCharCode(len);
+ }
+ } while (len && copy < have);
+
+ if (state.flags & 0x0200) {
+ state.check = crc32(state.check, input, copy, next);
+ }
+ have -= copy;
+ next += copy;
+ if (len) { break inf_leave; }
+ }
+ else if (state.head) {
+ state.head.name = null;
+ }
+ state.length = 0;
+ state.mode = COMMENT;
+ /* falls through */
+ case COMMENT:
+ if (state.flags & 0x1000) {
+ if (have === 0) { break inf_leave; }
+ copy = 0;
+ do {
+ len = input[next + copy++];
+ /* use constant limit because in js we should not preallocate memory */
+ if (state.head && len &&
+ (state.length < 65536 /*state.head.comm_max*/)) {
+ state.head.comment += String.fromCharCode(len);
+ }
+ } while (len && copy < have);
+ if (state.flags & 0x0200) {
+ state.check = crc32(state.check, input, copy, next);
+ }
+ have -= copy;
+ next += copy;
+ if (len) { break inf_leave; }
+ }
+ else if (state.head) {
+ state.head.comment = null;
+ }
+ state.mode = HCRC;
+ /* falls through */
+ case HCRC:
+ if (state.flags & 0x0200) {
+ //=== NEEDBITS(16); */
+ while (bits < 16) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (hold !== (state.check & 0xffff)) {
+ strm.msg = 'header crc mismatch';
+ state.mode = BAD;
+ break;
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ }
+ if (state.head) {
+ state.head.hcrc = ((state.flags >> 9) & 1);
+ state.head.done = true;
+ }
+ strm.adler = state.check = 0;
+ state.mode = TYPE;
+ break;
+ case DICTID:
+ //=== NEEDBITS(32); */
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ strm.adler = state.check = zswap32(hold);
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = DICT;
+ /* falls through */
+ case DICT:
+ if (state.havedict === 0) {
+ //--- RESTORE() ---
+ strm.next_out = put;
+ strm.avail_out = left;
+ strm.next_in = next;
+ strm.avail_in = have;
+ state.hold = hold;
+ state.bits = bits;
+ //---
+ return Z_NEED_DICT;
+ }
+ strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
+ state.mode = TYPE;
+ /* falls through */
+ case TYPE:
+ if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
+ /* falls through */
+ case TYPEDO:
+ if (state.last) {
+ //--- BYTEBITS() ---//
+ hold >>>= bits & 7;
+ bits -= bits & 7;
+ //---//
+ state.mode = CHECK;
+ break;
+ }
+ //=== NEEDBITS(3); */
+ while (bits < 3) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.last = (hold & 0x01)/*BITS(1)*/;
+ //--- DROPBITS(1) ---//
+ hold >>>= 1;
+ bits -= 1;
+ //---//
+
+ switch ((hold & 0x03)/*BITS(2)*/) {
+ case 0: /* stored block */
+ //Tracev((stderr, "inflate: stored block%s\n",
+ // state.last ? " (last)" : ""));
+ state.mode = STORED;
+ break;
+ case 1: /* fixed block */
+ fixedtables(state);
+ //Tracev((stderr, "inflate: fixed codes block%s\n",
+ // state.last ? " (last)" : ""));
+ state.mode = LEN_; /* decode codes */
+ if (flush === Z_TREES) {
+ //--- DROPBITS(2) ---//
+ hold >>>= 2;
+ bits -= 2;
+ //---//
+ break inf_leave;
+ }
+ break;
+ case 2: /* dynamic block */
+ //Tracev((stderr, "inflate: dynamic codes block%s\n",
+ // state.last ? " (last)" : ""));
+ state.mode = TABLE;
+ break;
+ case 3:
+ strm.msg = 'invalid block type';
+ state.mode = BAD;
+ }
+ //--- DROPBITS(2) ---//
+ hold >>>= 2;
+ bits -= 2;
+ //---//
+ break;
+ case STORED:
+ //--- BYTEBITS() ---// /* go to byte boundary */
+ hold >>>= bits & 7;
+ bits -= bits & 7;
+ //---//
+ //=== NEEDBITS(32); */
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
+ strm.msg = 'invalid stored block lengths';
+ state.mode = BAD;
+ break;
+ }
+ state.length = hold & 0xffff;
+ //Tracev((stderr, "inflate: stored length %u\n",
+ // state.length));
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ state.mode = COPY_;
+ if (flush === Z_TREES) { break inf_leave; }
+ /* falls through */
+ case COPY_:
+ state.mode = COPY;
+ /* falls through */
+ case COPY:
+ copy = state.length;
+ if (copy) {
+ if (copy > have) { copy = have; }
+ if (copy > left) { copy = left; }
+ if (copy === 0) { break inf_leave; }
+ //--- zmemcpy(put, next, copy); ---
+ utils.arraySet(output, input, next, copy, put);
+ //---//
+ have -= copy;
+ next += copy;
+ left -= copy;
+ put += copy;
+ state.length -= copy;
+ break;
+ }
+ //Tracev((stderr, "inflate: stored end\n"));
+ state.mode = TYPE;
+ break;
+ case TABLE:
+ //=== NEEDBITS(14); */
+ while (bits < 14) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
+ //--- DROPBITS(5) ---//
+ hold >>>= 5;
+ bits -= 5;
+ //---//
+ state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
+ //--- DROPBITS(5) ---//
+ hold >>>= 5;
+ bits -= 5;
+ //---//
+ state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
+ //--- DROPBITS(4) ---//
+ hold >>>= 4;
+ bits -= 4;
+ //---//
+//#ifndef PKZIP_BUG_WORKAROUND
+ if (state.nlen > 286 || state.ndist > 30) {
+ strm.msg = 'too many length or distance symbols';
+ state.mode = BAD;
+ break;
+ }
+//#endif
+ //Tracev((stderr, "inflate: table sizes ok\n"));
+ state.have = 0;
+ state.mode = LENLENS;
+ /* falls through */
+ case LENLENS:
+ while (state.have < state.ncode) {
+ //=== NEEDBITS(3);
+ while (bits < 3) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
+ //--- DROPBITS(3) ---//
+ hold >>>= 3;
+ bits -= 3;
+ //---//
+ }
+ while (state.have < 19) {
+ state.lens[order[state.have++]] = 0;
+ }
+ // We have separate tables & no pointers. 2 commented lines below not needed.
+ //state.next = state.codes;
+ //state.lencode = state.next;
+ // Switch to use dynamic table
+ state.lencode = state.lendyn;
+ state.lenbits = 7;
+
+ opts = { bits: state.lenbits };
+ ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
+ state.lenbits = opts.bits;
+
+ if (ret) {
+ strm.msg = 'invalid code lengths set';
+ state.mode = BAD;
+ break;
+ }
+ //Tracev((stderr, "inflate: code lengths ok\n"));
+ state.have = 0;
+ state.mode = CODELENS;
+ /* falls through */
+ case CODELENS:
+ while (state.have < state.nlen + state.ndist) {
+ for (;;) {
+ here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ if (here_val < 16) {
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ state.lens[state.have++] = here_val;
+ }
+ else {
+ if (here_val === 16) {
+ //=== NEEDBITS(here.bits + 2);
+ n = here_bits + 2;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ if (state.have === 0) {
+ strm.msg = 'invalid bit length repeat';
+ state.mode = BAD;
+ break;
+ }
+ len = state.lens[state.have - 1];
+ copy = 3 + (hold & 0x03);//BITS(2);
+ //--- DROPBITS(2) ---//
+ hold >>>= 2;
+ bits -= 2;
+ //---//
+ }
+ else if (here_val === 17) {
+ //=== NEEDBITS(here.bits + 3);
+ n = here_bits + 3;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ len = 0;
+ copy = 3 + (hold & 0x07);//BITS(3);
+ //--- DROPBITS(3) ---//
+ hold >>>= 3;
+ bits -= 3;
+ //---//
+ }
+ else {
+ //=== NEEDBITS(here.bits + 7);
+ n = here_bits + 7;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ len = 0;
+ copy = 11 + (hold & 0x7f);//BITS(7);
+ //--- DROPBITS(7) ---//
+ hold >>>= 7;
+ bits -= 7;
+ //---//
+ }
+ if (state.have + copy > state.nlen + state.ndist) {
+ strm.msg = 'invalid bit length repeat';
+ state.mode = BAD;
+ break;
+ }
+ while (copy--) {
+ state.lens[state.have++] = len;
+ }
+ }
+ }
+
+ /* handle error breaks in while */
+ if (state.mode === BAD) { break; }
+
+ /* check for end-of-block code (better have one) */
+ if (state.lens[256] === 0) {
+ strm.msg = 'invalid code -- missing end-of-block';
+ state.mode = BAD;
+ break;
+ }
+
+ /* build code tables -- note: do not change the lenbits or distbits
+ values here (9 and 6) without reading the comments in inftrees.h
+ concerning the ENOUGH constants, which depend on those values */
+ state.lenbits = 9;
+
+ opts = { bits: state.lenbits };
+ ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
+ // We have separate tables & no pointers. 2 commented lines below not needed.
+ // state.next_index = opts.table_index;
+ state.lenbits = opts.bits;
+ // state.lencode = state.next;
+
+ if (ret) {
+ strm.msg = 'invalid literal/lengths set';
+ state.mode = BAD;
+ break;
+ }
+
+ state.distbits = 6;
+ //state.distcode.copy(state.codes);
+ // Switch to use dynamic table
+ state.distcode = state.distdyn;
+ opts = { bits: state.distbits };
+ ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
+ // We have separate tables & no pointers. 2 commented lines below not needed.
+ // state.next_index = opts.table_index;
+ state.distbits = opts.bits;
+ // state.distcode = state.next;
+
+ if (ret) {
+ strm.msg = 'invalid distances set';
+ state.mode = BAD;
+ break;
+ }
+ //Tracev((stderr, 'inflate: codes ok\n'));
+ state.mode = LEN_;
+ if (flush === Z_TREES) { break inf_leave; }
+ /* falls through */
+ case LEN_:
+ state.mode = LEN;
+ /* falls through */
+ case LEN:
+ if (have >= 6 && left >= 258) {
+ //--- RESTORE() ---
+ strm.next_out = put;
+ strm.avail_out = left;
+ strm.next_in = next;
+ strm.avail_in = have;
+ state.hold = hold;
+ state.bits = bits;
+ //---
+ inflate_fast(strm, _out);
+ //--- LOAD() ---
+ put = strm.next_out;
+ output = strm.output;
+ left = strm.avail_out;
+ next = strm.next_in;
+ input = strm.input;
+ have = strm.avail_in;
+ hold = state.hold;
+ bits = state.bits;
+ //---
+
+ if (state.mode === TYPE) {
+ state.back = -1;
+ }
+ break;
+ }
+ state.back = 0;
+ for (;;) {
+ here = state.lencode[hold & ((1 << state.lenbits) - 1)]; /*BITS(state.lenbits)*/
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if (here_bits <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ if (here_op && (here_op & 0xf0) === 0) {
+ last_bits = here_bits;
+ last_op = here_op;
+ last_val = here_val;
+ for (;;) {
+ here = state.lencode[last_val +
+ ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((last_bits + here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ //--- DROPBITS(last.bits) ---//
+ hold >>>= last_bits;
+ bits -= last_bits;
+ //---//
+ state.back += last_bits;
+ }
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ state.back += here_bits;
+ state.length = here_val;
+ if (here_op === 0) {
+ //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ // "inflate: literal '%c'\n" :
+ // "inflate: literal 0x%02x\n", here.val));
+ state.mode = LIT;
+ break;
+ }
+ if (here_op & 32) {
+ //Tracevv((stderr, "inflate: end of block\n"));
+ state.back = -1;
+ state.mode = TYPE;
+ break;
+ }
+ if (here_op & 64) {
+ strm.msg = 'invalid literal/length code';
+ state.mode = BAD;
+ break;
+ }
+ state.extra = here_op & 15;
+ state.mode = LENEXT;
+ /* falls through */
+ case LENEXT:
+ if (state.extra) {
+ //=== NEEDBITS(state.extra);
+ n = state.extra;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.length += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
+ //--- DROPBITS(state.extra) ---//
+ hold >>>= state.extra;
+ bits -= state.extra;
+ //---//
+ state.back += state.extra;
+ }
+ //Tracevv((stderr, "inflate: length %u\n", state.length));
+ state.was = state.length;
+ state.mode = DIST;
+ /* falls through */
+ case DIST:
+ for (;;) {
+ here = state.distcode[hold & ((1 << state.distbits) - 1)];/*BITS(state.distbits)*/
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ if ((here_op & 0xf0) === 0) {
+ last_bits = here_bits;
+ last_op = here_op;
+ last_val = here_val;
+ for (;;) {
+ here = state.distcode[last_val +
+ ((hold & ((1 << (last_bits + last_op)) - 1))/*BITS(last.bits + last.op)*/ >> last_bits)];
+ here_bits = here >>> 24;
+ here_op = (here >>> 16) & 0xff;
+ here_val = here & 0xffff;
+
+ if ((last_bits + here_bits) <= bits) { break; }
+ //--- PULLBYTE() ---//
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ //---//
+ }
+ //--- DROPBITS(last.bits) ---//
+ hold >>>= last_bits;
+ bits -= last_bits;
+ //---//
+ state.back += last_bits;
+ }
+ //--- DROPBITS(here.bits) ---//
+ hold >>>= here_bits;
+ bits -= here_bits;
+ //---//
+ state.back += here_bits;
+ if (here_op & 64) {
+ strm.msg = 'invalid distance code';
+ state.mode = BAD;
+ break;
+ }
+ state.offset = here_val;
+ state.extra = (here_op) & 15;
+ state.mode = DISTEXT;
+ /* falls through */
+ case DISTEXT:
+ if (state.extra) {
+ //=== NEEDBITS(state.extra);
+ n = state.extra;
+ while (bits < n) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ state.offset += hold & ((1 << state.extra) - 1)/*BITS(state.extra)*/;
+ //--- DROPBITS(state.extra) ---//
+ hold >>>= state.extra;
+ bits -= state.extra;
+ //---//
+ state.back += state.extra;
+ }
+//#ifdef INFLATE_STRICT
+ if (state.offset > state.dmax) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break;
+ }
+//#endif
+ //Tracevv((stderr, "inflate: distance %u\n", state.offset));
+ state.mode = MATCH;
+ /* falls through */
+ case MATCH:
+ if (left === 0) { break inf_leave; }
+ copy = _out - left;
+ if (state.offset > copy) { /* copy from window */
+ copy = state.offset - copy;
+ if (copy > state.whave) {
+ if (state.sane) {
+ strm.msg = 'invalid distance too far back';
+ state.mode = BAD;
+ break;
+ }
+// (!) This block is disabled in zlib defailts,
+// don't enable it for binary compatibility
+//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
+// Trace((stderr, "inflate.c too far\n"));
+// copy -= state.whave;
+// if (copy > state.length) { copy = state.length; }
+// if (copy > left) { copy = left; }
+// left -= copy;
+// state.length -= copy;
+// do {
+// output[put++] = 0;
+// } while (--copy);
+// if (state.length === 0) { state.mode = LEN; }
+// break;
+//#endif
+ }
+ if (copy > state.wnext) {
+ copy -= state.wnext;
+ from = state.wsize - copy;
+ }
+ else {
+ from = state.wnext - copy;
+ }
+ if (copy > state.length) { copy = state.length; }
+ from_source = state.window;
+ }
+ else { /* copy from output */
+ from_source = output;
+ from = put - state.offset;
+ copy = state.length;
+ }
+ if (copy > left) { copy = left; }
+ left -= copy;
+ state.length -= copy;
+ do {
+ output[put++] = from_source[from++];
+ } while (--copy);
+ if (state.length === 0) { state.mode = LEN; }
+ break;
+ case LIT:
+ if (left === 0) { break inf_leave; }
+ output[put++] = state.length;
+ left--;
+ state.mode = LEN;
+ break;
+ case CHECK:
+ if (state.wrap) {
+ //=== NEEDBITS(32);
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ // Use '|' insdead of '+' to make sure that result is signed
+ hold |= input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ _out -= left;
+ strm.total_out += _out;
+ state.total += _out;
+ if (_out) {
+ strm.adler = state.check =
+ /*UPDATE(state.check, put - _out, _out);*/
+ (state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));
+
+ }
+ _out = left;
+ // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too
+ if ((state.flags ? hold : zswap32(hold)) !== state.check) {
+ strm.msg = 'incorrect data check';
+ state.mode = BAD;
+ break;
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ //Tracev((stderr, "inflate: check matches trailer\n"));
+ }
+ state.mode = LENGTH;
+ /* falls through */
+ case LENGTH:
+ if (state.wrap && state.flags) {
+ //=== NEEDBITS(32);
+ while (bits < 32) {
+ if (have === 0) { break inf_leave; }
+ have--;
+ hold += input[next++] << bits;
+ bits += 8;
+ }
+ //===//
+ if (hold !== (state.total & 0xffffffff)) {
+ strm.msg = 'incorrect length check';
+ state.mode = BAD;
+ break;
+ }
+ //=== INITBITS();
+ hold = 0;
+ bits = 0;
+ //===//
+ //Tracev((stderr, "inflate: length matches trailer\n"));
+ }
+ state.mode = DONE;
+ /* falls through */
+ case DONE:
+ ret = Z_STREAM_END;
+ break inf_leave;
+ case BAD:
+ ret = Z_DATA_ERROR;
+ break inf_leave;
+ case MEM:
+ return Z_MEM_ERROR;
+ case SYNC:
+ /* falls through */
+ default:
+ return Z_STREAM_ERROR;
+ }
+ }
+
+ // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"
+
+ /*
+ Return from inflate(), updating the total counts and the check value.
+ If there was no progress during the inflate() call, return a buffer
+ error. Call updatewindow() to create and/or update the window state.
+ Note: a memory error from inflate() is non-recoverable.
+ */
+
+ //--- RESTORE() ---
+ strm.next_out = put;
+ strm.avail_out = left;
+ strm.next_in = next;
+ strm.avail_in = have;
+ state.hold = hold;
+ state.bits = bits;
+ //---
+
+ if (state.wsize || (_out !== strm.avail_out && state.mode < BAD &&
+ (state.mode < CHECK || flush !== Z_FINISH))) {
+ if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) {
+ state.mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ }
+ _in -= strm.avail_in;
+ _out -= strm.avail_out;
+ strm.total_in += _in;
+ strm.total_out += _out;
+ state.total += _out;
+ if (state.wrap && _out) {
+ strm.adler = state.check = /*UPDATE(state.check, strm.next_out - _out, _out);*/
+ (state.flags ? crc32(state.check, output, _out, strm.next_out - _out) : adler32(state.check, output, _out, strm.next_out - _out));
+ }
+ strm.data_type = state.bits + (state.last ? 64 : 0) +
+ (state.mode === TYPE ? 128 : 0) +
+ (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
+ if (((_in === 0 && _out === 0) || flush === Z_FINISH) && ret === Z_OK) {
+ ret = Z_BUF_ERROR;
+ }
+ return ret;
+}
+
+function inflateEnd(strm) {
+
+ if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) {
+ return Z_STREAM_ERROR;
+ }
+
+ var state = strm.state;
+ if (state.window) {
+ state.window = null;
+ }
+ strm.state = null;
+ return Z_OK;
+}
+
+function inflateGetHeader(strm, head) {
+ var state;
+
+ /* check state */
+ if (!strm || !strm.state) { return Z_STREAM_ERROR; }
+ state = strm.state;
+ if ((state.wrap & 2) === 0) { return Z_STREAM_ERROR; }
+
+ /* save header structure */
+ state.head = head;
+ head.done = false;
+ return Z_OK;
+}
+
+function inflateSetDictionary(strm, dictionary) {
+ var dictLength = dictionary.length;
+
+ var state;
+ var dictid;
+ var ret;
+
+ /* check state */
+ if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { return Z_STREAM_ERROR; }
+ state = strm.state;
+
+ if (state.wrap !== 0 && state.mode !== DICT) {
+ return Z_STREAM_ERROR;
+ }
+
+ /* check for correct dictionary identifier */
+ if (state.mode === DICT) {
+ dictid = 1; /* adler32(0, null, 0)*/
+ /* dictid = adler32(dictid, dictionary, dictLength); */
+ dictid = adler32(dictid, dictionary, dictLength, 0);
+ if (dictid !== state.check) {
+ return Z_DATA_ERROR;
+ }
+ }
+ /* copy dictionary to window using updatewindow(), which will amend the
+ existing dictionary if appropriate */
+ ret = updatewindow(strm, dictionary, dictLength, dictLength);
+ if (ret) {
+ state.mode = MEM;
+ return Z_MEM_ERROR;
+ }
+ state.havedict = 1;
+ // Tracev((stderr, "inflate: dictionary set\n"));
+ return Z_OK;
+}
+
+exports.inflateReset = inflateReset;
+exports.inflateReset2 = inflateReset2;
+exports.inflateResetKeep = inflateResetKeep;
+exports.inflateInit = inflateInit;
+exports.inflateInit2 = inflateInit2;
+exports.inflate = inflate;
+exports.inflateEnd = inflateEnd;
+exports.inflateGetHeader = inflateGetHeader;
+exports.inflateSetDictionary = inflateSetDictionary;
+exports.inflateInfo = 'pako inflate (from Nodeca project)';
+
+/* Not implemented
+exports.inflateCopy = inflateCopy;
+exports.inflateGetDictionary = inflateGetDictionary;
+exports.inflateMark = inflateMark;
+exports.inflatePrime = inflatePrime;
+exports.inflateSync = inflateSync;
+exports.inflateSyncPoint = inflateSyncPoint;
+exports.inflateUndermine = inflateUndermine;
+*/
+
+},{"../utils/common":41,"./adler32":43,"./crc32":45,"./inffast":48,"./inftrees":50}],50:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+var utils = require('../utils/common');
+
+var MAXBITS = 15;
+var ENOUGH_LENS = 852;
+var ENOUGH_DISTS = 592;
+//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
+
+var CODES = 0;
+var LENS = 1;
+var DISTS = 2;
+
+var lbase = [ /* Length codes 257..285 base */
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
+];
+
+var lext = [ /* Length codes 257..285 extra */
+ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
+ 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78
+];
+
+var dbase = [ /* Distance codes 0..29 base */
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+ 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+ 8193, 12289, 16385, 24577, 0, 0
+];
+
+var dext = [ /* Distance codes 0..29 extra */
+ 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
+ 23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
+ 28, 28, 29, 29, 64, 64
+];
+
+module.exports = function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
+{
+ var bits = opts.bits;
+ //here = opts.here; /* table entry for duplication */
+
+ var len = 0; /* a code's length in bits */
+ var sym = 0; /* index of code symbols */
+ var min = 0, max = 0; /* minimum and maximum code lengths */
+ var root = 0; /* number of index bits for root table */
+ var curr = 0; /* number of index bits for current table */
+ var drop = 0; /* code bits to drop for sub-table */
+ var left = 0; /* number of prefix codes available */
+ var used = 0; /* code entries in table used */
+ var huff = 0; /* Huffman code */
+ var incr; /* for incrementing code, index */
+ var fill; /* index for replicating entries */
+ var low; /* low bits for current root entry */
+ var mask; /* mask for low root bits */
+ var next; /* next available space in table */
+ var base = null; /* base value table to use */
+ var base_index = 0;
+// var shoextra; /* extra bits table to use */
+ var end; /* use base and extra for symbol > end */
+ var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */
+ var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */
+ var extra = null;
+ var extra_index = 0;
+
+ var here_bits, here_op, here_val;
+
+ /*
+ Process a set of code lengths to create a canonical Huffman code. The
+ code lengths are lens[0..codes-1]. Each length corresponds to the
+ symbols 0..codes-1. The Huffman code is generated by first sorting the
+ symbols by length from short to long, and retaining the symbol order
+ for codes with equal lengths. Then the code starts with all zero bits
+ for the first code of the shortest length, and the codes are integer
+ increments for the same length, and zeros are appended as the length
+ increases. For the deflate format, these bits are stored backwards
+ from their more natural integer increment ordering, and so when the
+ decoding tables are built in the large loop below, the integer codes
+ are incremented backwards.
+
+ This routine assumes, but does not check, that all of the entries in
+ lens[] are in the range 0..MAXBITS. The caller must assure this.
+ 1..MAXBITS is interpreted as that code length. zero means that that
+ symbol does not occur in this code.
+
+ The codes are sorted by computing a count of codes for each length,
+ creating from that a table of starting indices for each length in the
+ sorted table, and then entering the symbols in order in the sorted
+ table. The sorted table is work[], with that space being provided by
+ the caller.
+
+ The length counts are used for other purposes as well, i.e. finding
+ the minimum and maximum length codes, determining if there are any
+ codes at all, checking for a valid set of lengths, and looking ahead
+ at length counts to determine sub-table sizes when building the
+ decoding tables.
+ */
+
+ /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
+ for (len = 0; len <= MAXBITS; len++) {
+ count[len] = 0;
+ }
+ for (sym = 0; sym < codes; sym++) {
+ count[lens[lens_index + sym]]++;
+ }
+
+ /* bound code lengths, force root to be within code lengths */
+ root = bits;
+ for (max = MAXBITS; max >= 1; max--) {
+ if (count[max] !== 0) { break; }
+ }
+ if (root > max) {
+ root = max;
+ }
+ if (max === 0) { /* no symbols to code at all */
+ //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */
+ //table.bits[opts.table_index] = 1; //here.bits = (var char)1;
+ //table.val[opts.table_index++] = 0; //here.val = (var short)0;
+ table[table_index++] = (1 << 24) | (64 << 16) | 0;
+
+
+ //table.op[opts.table_index] = 64;
+ //table.bits[opts.table_index] = 1;
+ //table.val[opts.table_index++] = 0;
+ table[table_index++] = (1 << 24) | (64 << 16) | 0;
+
+ opts.bits = 1;
+ return 0; /* no symbols, but wait for decoding to report error */
+ }
+ for (min = 1; min < max; min++) {
+ if (count[min] !== 0) { break; }
+ }
+ if (root < min) {
+ root = min;
+ }
+
+ /* check for an over-subscribed or incomplete set of lengths */
+ left = 1;
+ for (len = 1; len <= MAXBITS; len++) {
+ left <<= 1;
+ left -= count[len];
+ if (left < 0) {
+ return -1;
+ } /* over-subscribed */
+ }
+ if (left > 0 && (type === CODES || max !== 1)) {
+ return -1; /* incomplete set */
+ }
+
+ /* generate offsets into symbol table for each length for sorting */
+ offs[1] = 0;
+ for (len = 1; len < MAXBITS; len++) {
+ offs[len + 1] = offs[len] + count[len];
+ }
+
+ /* sort symbols by length, by symbol order within each length */
+ for (sym = 0; sym < codes; sym++) {
+ if (lens[lens_index + sym] !== 0) {
+ work[offs[lens[lens_index + sym]]++] = sym;
+ }
+ }
+
+ /*
+ Create and fill in decoding tables. In this loop, the table being
+ filled is at next and has curr index bits. The code being used is huff
+ with length len. That code is converted to an index by dropping drop
+ bits off of the bottom. For codes where len is less than drop + curr,
+ those top drop + curr - len bits are incremented through all values to
+ fill the table with replicated entries.
+
+ root is the number of index bits for the root table. When len exceeds
+ root, sub-tables are created pointed to by the root entry with an index
+ of the low root bits of huff. This is saved in low to check for when a
+ new sub-table should be started. drop is zero when the root table is
+ being filled, and drop is root when sub-tables are being filled.
+
+ When a new sub-table is needed, it is necessary to look ahead in the
+ code lengths to determine what size sub-table is needed. The length
+ counts are used for this, and so count[] is decremented as codes are
+ entered in the tables.
+
+ used keeps track of how many table entries have been allocated from the
+ provided *table space. It is checked for LENS and DIST tables against
+ the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
+ the initial root table size constants. See the comments in inftrees.h
+ for more information.
+
+ sym increments through all symbols, and the loop terminates when
+ all codes of length max, i.e. all codes, have been processed. This
+ routine permits incomplete codes, so another loop after this one fills
+ in the rest of the decoding tables with invalid code markers.
+ */
+
+ /* set up for code type */
+ // poor man optimization - use if-else instead of switch,
+ // to avoid deopts in old v8
+ if (type === CODES) {
+ base = extra = work; /* dummy value--not used */
+ end = 19;
+
+ } else if (type === LENS) {
+ base = lbase;
+ base_index -= 257;
+ extra = lext;
+ extra_index -= 257;
+ end = 256;
+
+ } else { /* DISTS */
+ base = dbase;
+ extra = dext;
+ end = -1;
+ }
+
+ /* initialize opts for loop */
+ huff = 0; /* starting code */
+ sym = 0; /* starting code symbol */
+ len = min; /* starting code length */
+ next = table_index; /* current table to fill in */
+ curr = root; /* current table index bits */
+ drop = 0; /* current bits to drop from code for index */
+ low = -1; /* trigger new sub-table when len > root */
+ used = 1 << root; /* use root table entries */
+ mask = used - 1; /* mask for comparing low */
+
+ /* check available table space */
+ if ((type === LENS && used > ENOUGH_LENS) ||
+ (type === DISTS && used > ENOUGH_DISTS)) {
+ return 1;
+ }
+
+ /* process all codes and make table entries */
+ for (;;) {
+ /* create table entry */
+ here_bits = len - drop;
+ if (work[sym] < end) {
+ here_op = 0;
+ here_val = work[sym];
+ }
+ else if (work[sym] > end) {
+ here_op = extra[extra_index + work[sym]];
+ here_val = base[base_index + work[sym]];
+ }
+ else {
+ here_op = 32 + 64; /* end of block */
+ here_val = 0;
+ }
+
+ /* replicate for those indices with low len bits equal to huff */
+ incr = 1 << (len - drop);
+ fill = 1 << curr;
+ min = fill; /* save offset to next table */
+ do {
+ fill -= incr;
+ table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
+ } while (fill !== 0);
+
+ /* backwards increment the len-bit code huff */
+ incr = 1 << (len - 1);
+ while (huff & incr) {
+ incr >>= 1;
+ }
+ if (incr !== 0) {
+ huff &= incr - 1;
+ huff += incr;
+ } else {
+ huff = 0;
+ }
+
+ /* go to next symbol, update count, len */
+ sym++;
+ if (--count[len] === 0) {
+ if (len === max) { break; }
+ len = lens[lens_index + work[sym]];
+ }
+
+ /* create new sub-table if needed */
+ if (len > root && (huff & mask) !== low) {
+ /* if first time, transition to sub-tables */
+ if (drop === 0) {
+ drop = root;
+ }
+
+ /* increment past last table */
+ next += min; /* here min is 1 << curr */
+
+ /* determine length of next table */
+ curr = len - drop;
+ left = 1 << curr;
+ while (curr + drop < max) {
+ left -= count[curr + drop];
+ if (left <= 0) { break; }
+ curr++;
+ left <<= 1;
+ }
+
+ /* check for enough space */
+ used += 1 << curr;
+ if ((type === LENS && used > ENOUGH_LENS) ||
+ (type === DISTS && used > ENOUGH_DISTS)) {
+ return 1;
+ }
+
+ /* point entry in root table to sub-table */
+ low = huff & mask;
+ /*table.op[low] = curr;
+ table.bits[low] = root;
+ table.val[low] = next - opts.table_index;*/
+ table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
+ }
+ }
+
+ /* fill in remaining table entry if code is incomplete (guaranteed to have
+ at most one remaining entry, since if the code is incomplete, the
+ maximum code length that was allowed to get this far is one bit) */
+ if (huff !== 0) {
+ //table.op[next + huff] = 64; /* invalid code marker */
+ //table.bits[next + huff] = len - drop;
+ //table.val[next + huff] = 0;
+ table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
+ }
+
+ /* set return parameters */
+ //opts.table_index += used;
+ opts.bits = root;
+ return 0;
+};
+
+},{"../utils/common":41}],51:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+module.exports = {
+ 2: 'need dictionary', /* Z_NEED_DICT 2 */
+ 1: 'stream end', /* Z_STREAM_END 1 */
+ 0: '', /* Z_OK 0 */
+ '-1': 'file error', /* Z_ERRNO (-1) */
+ '-2': 'stream error', /* Z_STREAM_ERROR (-2) */
+ '-3': 'data error', /* Z_DATA_ERROR (-3) */
+ '-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */
+ '-5': 'buffer error', /* Z_BUF_ERROR (-5) */
+ '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */
+};
+
+},{}],52:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+var utils = require('../utils/common');
+
+/* Public constants ==========================================================*/
+/* ===========================================================================*/
+
+
+//var Z_FILTERED = 1;
+//var Z_HUFFMAN_ONLY = 2;
+//var Z_RLE = 3;
+var Z_FIXED = 4;
+//var Z_DEFAULT_STRATEGY = 0;
+
+/* Possible values of the data_type field (though see inflate()) */
+var Z_BINARY = 0;
+var Z_TEXT = 1;
+//var Z_ASCII = 1; // = Z_TEXT
+var Z_UNKNOWN = 2;
+
+/*============================================================================*/
+
+
+function zero(buf) { var len = buf.length; while (--len >= 0) { buf[len] = 0; } }
+
+// From zutil.h
+
+var STORED_BLOCK = 0;
+var STATIC_TREES = 1;
+var DYN_TREES = 2;
+/* The three kinds of block type */
+
+var MIN_MATCH = 3;
+var MAX_MATCH = 258;
+/* The minimum and maximum match lengths */
+
+// From deflate.h
+/* ===========================================================================
+ * Internal compression state.
+ */
+
+var LENGTH_CODES = 29;
+/* number of length codes, not counting the special END_BLOCK code */
+
+var LITERALS = 256;
+/* number of literal bytes 0..255 */
+
+var L_CODES = LITERALS + 1 + LENGTH_CODES;
+/* number of Literal or Length codes, including the END_BLOCK code */
+
+var D_CODES = 30;
+/* number of distance codes */
+
+var BL_CODES = 19;
+/* number of codes used to transfer the bit lengths */
+
+var HEAP_SIZE = 2 * L_CODES + 1;
+/* maximum heap size */
+
+var MAX_BITS = 15;
+/* All codes must not exceed MAX_BITS bits */
+
+var Buf_size = 16;
+/* size of bit buffer in bi_buf */
+
+
+/* ===========================================================================
+ * Constants
+ */
+
+var MAX_BL_BITS = 7;
+/* Bit length codes must not exceed MAX_BL_BITS bits */
+
+var END_BLOCK = 256;
+/* end of block literal code */
+
+var REP_3_6 = 16;
+/* repeat previous bit length 3-6 times (2 bits of repeat count) */
+
+var REPZ_3_10 = 17;
+/* repeat a zero length 3-10 times (3 bits of repeat count) */
+
+var REPZ_11_138 = 18;
+/* repeat a zero length 11-138 times (7 bits of repeat count) */
+
+/* eslint-disable comma-spacing,array-bracket-spacing */
+var extra_lbits = /* extra bits for each length code */
+ [0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0];
+
+var extra_dbits = /* extra bits for each distance code */
+ [0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13];
+
+var extra_blbits = /* extra bits for each bit length code */
+ [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7];
+
+var bl_order =
+ [16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];
+/* eslint-enable comma-spacing,array-bracket-spacing */
+
+/* The lengths of the bit length codes are sent in order of decreasing
+ * probability, to avoid transmitting the lengths for unused bit length codes.
+ */
+
+/* ===========================================================================
+ * Local data. These are initialized only once.
+ */
+
+// We pre-fill arrays with 0 to avoid uninitialized gaps
+
+var DIST_CODE_LEN = 512; /* see definition of array dist_code below */
+
+// !!!! Use flat array insdead of structure, Freq = i*2, Len = i*2+1
+var static_ltree = new Array((L_CODES + 2) * 2);
+zero(static_ltree);
+/* The static literal tree. Since the bit lengths are imposed, there is no
+ * need for the L_CODES extra codes used during heap construction. However
+ * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
+ * below).
+ */
+
+var static_dtree = new Array(D_CODES * 2);
+zero(static_dtree);
+/* The static distance tree. (Actually a trivial tree since all codes use
+ * 5 bits.)
+ */
+
+var _dist_code = new Array(DIST_CODE_LEN);
+zero(_dist_code);
+/* Distance codes. The first 256 values correspond to the distances
+ * 3 .. 258, the last 256 values correspond to the top 8 bits of
+ * the 15 bit distances.
+ */
+
+var _length_code = new Array(MAX_MATCH - MIN_MATCH + 1);
+zero(_length_code);
+/* length code for each normalized match length (0 == MIN_MATCH) */
+
+var base_length = new Array(LENGTH_CODES);
+zero(base_length);
+/* First normalized length for each code (0 = MIN_MATCH) */
+
+var base_dist = new Array(D_CODES);
+zero(base_dist);
+/* First normalized distance for each code (0 = distance of 1) */
+
+
+function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {
+
+ this.static_tree = static_tree; /* static tree or NULL */
+ this.extra_bits = extra_bits; /* extra bits for each code or NULL */
+ this.extra_base = extra_base; /* base index for extra_bits */
+ this.elems = elems; /* max number of elements in the tree */
+ this.max_length = max_length; /* max bit length for the codes */
+
+ // show if `static_tree` has data or dummy - needed for monomorphic objects
+ this.has_stree = static_tree && static_tree.length;
+}
+
+
+var static_l_desc;
+var static_d_desc;
+var static_bl_desc;
+
+
+function TreeDesc(dyn_tree, stat_desc) {
+ this.dyn_tree = dyn_tree; /* the dynamic tree */
+ this.max_code = 0; /* largest code with non zero frequency */
+ this.stat_desc = stat_desc; /* the corresponding static tree */
+}
+
+
+
+function d_code(dist) {
+ return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
+}
+
+
+/* ===========================================================================
+ * Output a short LSB first on the stream.
+ * IN assertion: there is enough room in pendingBuf.
+ */
+function put_short(s, w) {
+// put_byte(s, (uch)((w) & 0xff));
+// put_byte(s, (uch)((ush)(w) >> 8));
+ s.pending_buf[s.pending++] = (w) & 0xff;
+ s.pending_buf[s.pending++] = (w >>> 8) & 0xff;
+}
+
+
+/* ===========================================================================
+ * Send a value on a given number of bits.
+ * IN assertion: length <= 16 and value fits in length bits.
+ */
+function send_bits(s, value, length) {
+ if (s.bi_valid > (Buf_size - length)) {
+ s.bi_buf |= (value << s.bi_valid) & 0xffff;
+ put_short(s, s.bi_buf);
+ s.bi_buf = value >> (Buf_size - s.bi_valid);
+ s.bi_valid += length - Buf_size;
+ } else {
+ s.bi_buf |= (value << s.bi_valid) & 0xffff;
+ s.bi_valid += length;
+ }
+}
+
+
+function send_code(s, c, tree) {
+ send_bits(s, tree[c * 2]/*.Code*/, tree[c * 2 + 1]/*.Len*/);
+}
+
+
+/* ===========================================================================
+ * Reverse the first len bits of a code, using straightforward code (a faster
+ * method would use a table)
+ * IN assertion: 1 <= len <= 15
+ */
+function bi_reverse(code, len) {
+ var res = 0;
+ do {
+ res |= code & 1;
+ code >>>= 1;
+ res <<= 1;
+ } while (--len > 0);
+ return res >>> 1;
+}
+
+
+/* ===========================================================================
+ * Flush the bit buffer, keeping at most 7 bits in it.
+ */
+function bi_flush(s) {
+ if (s.bi_valid === 16) {
+ put_short(s, s.bi_buf);
+ s.bi_buf = 0;
+ s.bi_valid = 0;
+
+ } else if (s.bi_valid >= 8) {
+ s.pending_buf[s.pending++] = s.bi_buf & 0xff;
+ s.bi_buf >>= 8;
+ s.bi_valid -= 8;
+ }
+}
+
+
+/* ===========================================================================
+ * Compute the optimal bit lengths for a tree and update the total bit length
+ * for the current block.
+ * IN assertion: the fields freq and dad are set, heap[heap_max] and
+ * above are the tree nodes sorted by increasing frequency.
+ * OUT assertions: the field len is set to the optimal bit length, the
+ * array bl_count contains the frequencies for each bit length.
+ * The length opt_len is updated; static_len is also updated if stree is
+ * not null.
+ */
+function gen_bitlen(s, desc)
+// deflate_state *s;
+// tree_desc *desc; /* the tree descriptor */
+{
+ var tree = desc.dyn_tree;
+ var max_code = desc.max_code;
+ var stree = desc.stat_desc.static_tree;
+ var has_stree = desc.stat_desc.has_stree;
+ var extra = desc.stat_desc.extra_bits;
+ var base = desc.stat_desc.extra_base;
+ var max_length = desc.stat_desc.max_length;
+ var h; /* heap index */
+ var n, m; /* iterate over the tree elements */
+ var bits; /* bit length */
+ var xbits; /* extra bits */
+ var f; /* frequency */
+ var overflow = 0; /* number of elements with bit length too large */
+
+ for (bits = 0; bits <= MAX_BITS; bits++) {
+ s.bl_count[bits] = 0;
+ }
+
+ /* In a first pass, compute the optimal bit lengths (which may
+ * overflow in the case of the bit length tree).
+ */
+ tree[s.heap[s.heap_max] * 2 + 1]/*.Len*/ = 0; /* root of the heap */
+
+ for (h = s.heap_max + 1; h < HEAP_SIZE; h++) {
+ n = s.heap[h];
+ bits = tree[tree[n * 2 + 1]/*.Dad*/ * 2 + 1]/*.Len*/ + 1;
+ if (bits > max_length) {
+ bits = max_length;
+ overflow++;
+ }
+ tree[n * 2 + 1]/*.Len*/ = bits;
+ /* We overwrite tree[n].Dad which is no longer needed */
+
+ if (n > max_code) { continue; } /* not a leaf node */
+
+ s.bl_count[bits]++;
+ xbits = 0;
+ if (n >= base) {
+ xbits = extra[n - base];
+ }
+ f = tree[n * 2]/*.Freq*/;
+ s.opt_len += f * (bits + xbits);
+ if (has_stree) {
+ s.static_len += f * (stree[n * 2 + 1]/*.Len*/ + xbits);
+ }
+ }
+ if (overflow === 0) { return; }
+
+ // Trace((stderr,"\nbit length overflow\n"));
+ /* This happens for example on obj2 and pic of the Calgary corpus */
+
+ /* Find the first bit length which could increase: */
+ do {
+ bits = max_length - 1;
+ while (s.bl_count[bits] === 0) { bits--; }
+ s.bl_count[bits]--; /* move one leaf down the tree */
+ s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
+ s.bl_count[max_length]--;
+ /* The brother of the overflow item also moves one step up,
+ * but this does not affect bl_count[max_length]
+ */
+ overflow -= 2;
+ } while (overflow > 0);
+
+ /* Now recompute all bit lengths, scanning in increasing frequency.
+ * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
+ * lengths instead of fixing only the wrong ones. This idea is taken
+ * from 'ar' written by Haruhiko Okumura.)
+ */
+ for (bits = max_length; bits !== 0; bits--) {
+ n = s.bl_count[bits];
+ while (n !== 0) {
+ m = s.heap[--h];
+ if (m > max_code) { continue; }
+ if (tree[m * 2 + 1]/*.Len*/ !== bits) {
+ // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
+ s.opt_len += (bits - tree[m * 2 + 1]/*.Len*/) * tree[m * 2]/*.Freq*/;
+ tree[m * 2 + 1]/*.Len*/ = bits;
+ }
+ n--;
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Generate the codes for a given tree and bit counts (which need not be
+ * optimal).
+ * IN assertion: the array bl_count contains the bit length statistics for
+ * the given tree and the field len is set for all tree elements.
+ * OUT assertion: the field code is set for all tree elements of non
+ * zero code length.
+ */
+function gen_codes(tree, max_code, bl_count)
+// ct_data *tree; /* the tree to decorate */
+// int max_code; /* largest code with non zero frequency */
+// ushf *bl_count; /* number of codes at each bit length */
+{
+ var next_code = new Array(MAX_BITS + 1); /* next code value for each bit length */
+ var code = 0; /* running code value */
+ var bits; /* bit index */
+ var n; /* code index */
+
+ /* The distribution counts are first used to generate the code values
+ * without bit reversal.
+ */
+ for (bits = 1; bits <= MAX_BITS; bits++) {
+ next_code[bits] = code = (code + bl_count[bits - 1]) << 1;
+ }
+ /* Check that the bit counts in bl_count are consistent. The last code
+ * must be all ones.
+ */
+ //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
+ // "inconsistent bit counts");
+ //Tracev((stderr,"\ngen_codes: max_code %d ", max_code));
+
+ for (n = 0; n <= max_code; n++) {
+ var len = tree[n * 2 + 1]/*.Len*/;
+ if (len === 0) { continue; }
+ /* Now reverse the bits */
+ tree[n * 2]/*.Code*/ = bi_reverse(next_code[len]++, len);
+
+ //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
+ // n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
+ }
+}
+
+
+/* ===========================================================================
+ * Initialize the various 'constant' tables.
+ */
+function tr_static_init() {
+ var n; /* iterates over tree elements */
+ var bits; /* bit counter */
+ var length; /* length value */
+ var code; /* code value */
+ var dist; /* distance index */
+ var bl_count = new Array(MAX_BITS + 1);
+ /* number of codes at each bit length for an optimal tree */
+
+ // do check in _tr_init()
+ //if (static_init_done) return;
+
+ /* For some embedded targets, global variables are not initialized: */
+/*#ifdef NO_INIT_GLOBAL_POINTERS
+ static_l_desc.static_tree = static_ltree;
+ static_l_desc.extra_bits = extra_lbits;
+ static_d_desc.static_tree = static_dtree;
+ static_d_desc.extra_bits = extra_dbits;
+ static_bl_desc.extra_bits = extra_blbits;
+#endif*/
+
+ /* Initialize the mapping length (0..255) -> length code (0..28) */
+ length = 0;
+ for (code = 0; code < LENGTH_CODES - 1; code++) {
+ base_length[code] = length;
+ for (n = 0; n < (1 << extra_lbits[code]); n++) {
+ _length_code[length++] = code;
+ }
+ }
+ //Assert (length == 256, "tr_static_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ _length_code[length - 1] = code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0; code < 16; code++) {
+ base_dist[code] = dist;
+ for (n = 0; n < (1 << extra_dbits[code]); n++) {
+ _dist_code[dist++] = code;
+ }
+ }
+ //Assert (dist == 256, "tr_static_init: dist != 256");
+ dist >>= 7; /* from now on, all distances are divided by 128 */
+ for (; code < D_CODES; code++) {
+ base_dist[code] = dist << 7;
+ for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) {
+ _dist_code[256 + dist++] = code;
+ }
+ }
+ //Assert (dist == 256, "tr_static_init: 256+dist != 512");
+
+ /* Construct the codes of the static literal tree */
+ for (bits = 0; bits <= MAX_BITS; bits++) {
+ bl_count[bits] = 0;
+ }
+
+ n = 0;
+ while (n <= 143) {
+ static_ltree[n * 2 + 1]/*.Len*/ = 8;
+ n++;
+ bl_count[8]++;
+ }
+ while (n <= 255) {
+ static_ltree[n * 2 + 1]/*.Len*/ = 9;
+ n++;
+ bl_count[9]++;
+ }
+ while (n <= 279) {
+ static_ltree[n * 2 + 1]/*.Len*/ = 7;
+ n++;
+ bl_count[7]++;
+ }
+ while (n <= 287) {
+ static_ltree[n * 2 + 1]/*.Len*/ = 8;
+ n++;
+ bl_count[8]++;
+ }
+ /* Codes 286 and 287 do not exist, but we must include them in the
+ * tree construction to get a canonical Huffman tree (longest code
+ * all ones)
+ */
+ gen_codes(static_ltree, L_CODES + 1, bl_count);
+
+ /* The static distance tree is trivial: */
+ for (n = 0; n < D_CODES; n++) {
+ static_dtree[n * 2 + 1]/*.Len*/ = 5;
+ static_dtree[n * 2]/*.Code*/ = bi_reverse(n, 5);
+ }
+
+ // Now data ready and we can init static trees
+ static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS + 1, L_CODES, MAX_BITS);
+ static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES, MAX_BITS);
+ static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES, MAX_BL_BITS);
+
+ //static_init_done = true;
+}
+
+
+/* ===========================================================================
+ * Initialize a new block.
+ */
+function init_block(s) {
+ var n; /* iterates over tree elements */
+
+ /* Initialize the trees. */
+ for (n = 0; n < L_CODES; n++) { s.dyn_ltree[n * 2]/*.Freq*/ = 0; }
+ for (n = 0; n < D_CODES; n++) { s.dyn_dtree[n * 2]/*.Freq*/ = 0; }
+ for (n = 0; n < BL_CODES; n++) { s.bl_tree[n * 2]/*.Freq*/ = 0; }
+
+ s.dyn_ltree[END_BLOCK * 2]/*.Freq*/ = 1;
+ s.opt_len = s.static_len = 0;
+ s.last_lit = s.matches = 0;
+}
+
+
+/* ===========================================================================
+ * Flush the bit buffer and align the output on a byte boundary
+ */
+function bi_windup(s)
+{
+ if (s.bi_valid > 8) {
+ put_short(s, s.bi_buf);
+ } else if (s.bi_valid > 0) {
+ //put_byte(s, (Byte)s->bi_buf);
+ s.pending_buf[s.pending++] = s.bi_buf;
+ }
+ s.bi_buf = 0;
+ s.bi_valid = 0;
+}
+
+/* ===========================================================================
+ * Copy a stored block, storing first the length and its
+ * one's complement if requested.
+ */
+function copy_block(s, buf, len, header)
+//DeflateState *s;
+//charf *buf; /* the input data */
+//unsigned len; /* its length */
+//int header; /* true if block header must be written */
+{
+ bi_windup(s); /* align on byte boundary */
+
+ if (header) {
+ put_short(s, len);
+ put_short(s, ~len);
+ }
+// while (len--) {
+// put_byte(s, *buf++);
+// }
+ utils.arraySet(s.pending_buf, s.window, buf, len, s.pending);
+ s.pending += len;
+}
+
+/* ===========================================================================
+ * Compares to subtrees, using the tree depth as tie breaker when
+ * the subtrees have equal frequency. This minimizes the worst case length.
+ */
+function smaller(tree, n, m, depth) {
+ var _n2 = n * 2;
+ var _m2 = m * 2;
+ return (tree[_n2]/*.Freq*/ < tree[_m2]/*.Freq*/ ||
+ (tree[_n2]/*.Freq*/ === tree[_m2]/*.Freq*/ && depth[n] <= depth[m]));
+}
+
+/* ===========================================================================
+ * Restore the heap property by moving down the tree starting at node k,
+ * exchanging a node with the smallest of its two sons if necessary, stopping
+ * when the heap property is re-established (each father smaller than its
+ * two sons).
+ */
+function pqdownheap(s, tree, k)
+// deflate_state *s;
+// ct_data *tree; /* the tree to restore */
+// int k; /* node to move down */
+{
+ var v = s.heap[k];
+ var j = k << 1; /* left son of k */
+ while (j <= s.heap_len) {
+ /* Set j to the smallest of the two sons: */
+ if (j < s.heap_len &&
+ smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
+ j++;
+ }
+ /* Exit if v is smaller than both sons */
+ if (smaller(tree, v, s.heap[j], s.depth)) { break; }
+
+ /* Exchange v with the smallest son */
+ s.heap[k] = s.heap[j];
+ k = j;
+
+ /* And continue down the tree, setting j to the left son of k */
+ j <<= 1;
+ }
+ s.heap[k] = v;
+}
+
+
+// inlined manually
+// var SMALLEST = 1;
+
+/* ===========================================================================
+ * Send the block data compressed using the given Huffman trees
+ */
+function compress_block(s, ltree, dtree)
+// deflate_state *s;
+// const ct_data *ltree; /* literal tree */
+// const ct_data *dtree; /* distance tree */
+{
+ var dist; /* distance of matched string */
+ var lc; /* match length or unmatched char (if dist == 0) */
+ var lx = 0; /* running index in l_buf */
+ var code; /* the code to send */
+ var extra; /* number of extra bits to send */
+
+ if (s.last_lit !== 0) {
+ do {
+ dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | (s.pending_buf[s.d_buf + lx * 2 + 1]);
+ lc = s.pending_buf[s.l_buf + lx];
+ lx++;
+
+ if (dist === 0) {
+ send_code(s, lc, ltree); /* send a literal byte */
+ //Tracecv(isgraph(lc), (stderr," '%c' ", lc));
+ } else {
+ /* Here, lc is the match length - MIN_MATCH */
+ code = _length_code[lc];
+ send_code(s, code + LITERALS + 1, ltree); /* send the length code */
+ extra = extra_lbits[code];
+ if (extra !== 0) {
+ lc -= base_length[code];
+ send_bits(s, lc, extra); /* send the extra length bits */
+ }
+ dist--; /* dist is now the match distance - 1 */
+ code = d_code(dist);
+ //Assert (code < D_CODES, "bad d_code");
+
+ send_code(s, code, dtree); /* send the distance code */
+ extra = extra_dbits[code];
+ if (extra !== 0) {
+ dist -= base_dist[code];
+ send_bits(s, dist, extra); /* send the extra distance bits */
+ }
+ } /* literal or match pair ? */
+
+ /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */
+ //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx,
+ // "pendingBuf overflow");
+
+ } while (lx < s.last_lit);
+ }
+
+ send_code(s, END_BLOCK, ltree);
+}
+
+
+/* ===========================================================================
+ * Construct one Huffman tree and assigns the code bit strings and lengths.
+ * Update the total bit length for the current block.
+ * IN assertion: the field freq is set for all tree elements.
+ * OUT assertions: the fields len and code are set to the optimal bit length
+ * and corresponding code. The length opt_len is updated; static_len is
+ * also updated if stree is not null. The field max_code is set.
+ */
+function build_tree(s, desc)
+// deflate_state *s;
+// tree_desc *desc; /* the tree descriptor */
+{
+ var tree = desc.dyn_tree;
+ var stree = desc.stat_desc.static_tree;
+ var has_stree = desc.stat_desc.has_stree;
+ var elems = desc.stat_desc.elems;
+ var n, m; /* iterate over heap elements */
+ var max_code = -1; /* largest code with non zero frequency */
+ var node; /* new node being created */
+
+ /* Construct the initial heap, with least frequent element in
+ * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
+ * heap[0] is not used.
+ */
+ s.heap_len = 0;
+ s.heap_max = HEAP_SIZE;
+
+ for (n = 0; n < elems; n++) {
+ if (tree[n * 2]/*.Freq*/ !== 0) {
+ s.heap[++s.heap_len] = max_code = n;
+ s.depth[n] = 0;
+
+ } else {
+ tree[n * 2 + 1]/*.Len*/ = 0;
+ }
+ }
+
+ /* The pkzip format requires that at least one distance code exists,
+ * and that at least one bit should be sent even if there is only one
+ * possible code. So to avoid special checks later on we force at least
+ * two codes of non zero frequency.
+ */
+ while (s.heap_len < 2) {
+ node = s.heap[++s.heap_len] = (max_code < 2 ? ++max_code : 0);
+ tree[node * 2]/*.Freq*/ = 1;
+ s.depth[node] = 0;
+ s.opt_len--;
+
+ if (has_stree) {
+ s.static_len -= stree[node * 2 + 1]/*.Len*/;
+ }
+ /* node is 0 or 1 so it does not have extra bits */
+ }
+ desc.max_code = max_code;
+
+ /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
+ * establish sub-heaps of increasing lengths:
+ */
+ for (n = (s.heap_len >> 1/*int /2*/); n >= 1; n--) { pqdownheap(s, tree, n); }
+
+ /* Construct the Huffman tree by repeatedly combining the least two
+ * frequent nodes.
+ */
+ node = elems; /* next internal node of the tree */
+ do {
+ //pqremove(s, tree, n); /* n = node of least frequency */
+ /*** pqremove ***/
+ n = s.heap[1/*SMALLEST*/];
+ s.heap[1/*SMALLEST*/] = s.heap[s.heap_len--];
+ pqdownheap(s, tree, 1/*SMALLEST*/);
+ /***/
+
+ m = s.heap[1/*SMALLEST*/]; /* m = node of next least frequency */
+
+ s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */
+ s.heap[--s.heap_max] = m;
+
+ /* Create a new node father of n and m */
+ tree[node * 2]/*.Freq*/ = tree[n * 2]/*.Freq*/ + tree[m * 2]/*.Freq*/;
+ s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
+ tree[n * 2 + 1]/*.Dad*/ = tree[m * 2 + 1]/*.Dad*/ = node;
+
+ /* and insert the new node in the heap */
+ s.heap[1/*SMALLEST*/] = node++;
+ pqdownheap(s, tree, 1/*SMALLEST*/);
+
+ } while (s.heap_len >= 2);
+
+ s.heap[--s.heap_max] = s.heap[1/*SMALLEST*/];
+
+ /* At this point, the fields freq and dad are set. We can now
+ * generate the bit lengths.
+ */
+ gen_bitlen(s, desc);
+
+ /* The field len is now set, we can generate the bit codes */
+ gen_codes(tree, max_code, s.bl_count);
+}
+
+
+/* ===========================================================================
+ * Scan a literal or distance tree to determine the frequencies of the codes
+ * in the bit length tree.
+ */
+function scan_tree(s, tree, max_code)
+// deflate_state *s;
+// ct_data *tree; /* the tree to be scanned */
+// int max_code; /* and its largest code of non zero frequency */
+{
+ var n; /* iterates over all tree elements */
+ var prevlen = -1; /* last emitted length */
+ var curlen; /* length of current code */
+
+ var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */
+
+ var count = 0; /* repeat count of the current code */
+ var max_count = 7; /* max repeat count */
+ var min_count = 4; /* min repeat count */
+
+ if (nextlen === 0) {
+ max_count = 138;
+ min_count = 3;
+ }
+ tree[(max_code + 1) * 2 + 1]/*.Len*/ = 0xffff; /* guard */
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen;
+ nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;
+
+ if (++count < max_count && curlen === nextlen) {
+ continue;
+
+ } else if (count < min_count) {
+ s.bl_tree[curlen * 2]/*.Freq*/ += count;
+
+ } else if (curlen !== 0) {
+
+ if (curlen !== prevlen) { s.bl_tree[curlen * 2]/*.Freq*/++; }
+ s.bl_tree[REP_3_6 * 2]/*.Freq*/++;
+
+ } else if (count <= 10) {
+ s.bl_tree[REPZ_3_10 * 2]/*.Freq*/++;
+
+ } else {
+ s.bl_tree[REPZ_11_138 * 2]/*.Freq*/++;
+ }
+
+ count = 0;
+ prevlen = curlen;
+
+ if (nextlen === 0) {
+ max_count = 138;
+ min_count = 3;
+
+ } else if (curlen === nextlen) {
+ max_count = 6;
+ min_count = 3;
+
+ } else {
+ max_count = 7;
+ min_count = 4;
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Send a literal or distance tree in compressed form, using the codes in
+ * bl_tree.
+ */
+function send_tree(s, tree, max_code)
+// deflate_state *s;
+// ct_data *tree; /* the tree to be scanned */
+// int max_code; /* and its largest code of non zero frequency */
+{
+ var n; /* iterates over all tree elements */
+ var prevlen = -1; /* last emitted length */
+ var curlen; /* length of current code */
+
+ var nextlen = tree[0 * 2 + 1]/*.Len*/; /* length of next code */
+
+ var count = 0; /* repeat count of the current code */
+ var max_count = 7; /* max repeat count */
+ var min_count = 4; /* min repeat count */
+
+ /* tree[max_code+1].Len = -1; */ /* guard already set */
+ if (nextlen === 0) {
+ max_count = 138;
+ min_count = 3;
+ }
+
+ for (n = 0; n <= max_code; n++) {
+ curlen = nextlen;
+ nextlen = tree[(n + 1) * 2 + 1]/*.Len*/;
+
+ if (++count < max_count && curlen === nextlen) {
+ continue;
+
+ } else if (count < min_count) {
+ do { send_code(s, curlen, s.bl_tree); } while (--count !== 0);
+
+ } else if (curlen !== 0) {
+ if (curlen !== prevlen) {
+ send_code(s, curlen, s.bl_tree);
+ count--;
+ }
+ //Assert(count >= 3 && count <= 6, " 3_6?");
+ send_code(s, REP_3_6, s.bl_tree);
+ send_bits(s, count - 3, 2);
+
+ } else if (count <= 10) {
+ send_code(s, REPZ_3_10, s.bl_tree);
+ send_bits(s, count - 3, 3);
+
+ } else {
+ send_code(s, REPZ_11_138, s.bl_tree);
+ send_bits(s, count - 11, 7);
+ }
+
+ count = 0;
+ prevlen = curlen;
+ if (nextlen === 0) {
+ max_count = 138;
+ min_count = 3;
+
+ } else if (curlen === nextlen) {
+ max_count = 6;
+ min_count = 3;
+
+ } else {
+ max_count = 7;
+ min_count = 4;
+ }
+ }
+}
+
+
+/* ===========================================================================
+ * Construct the Huffman tree for the bit lengths and return the index in
+ * bl_order of the last bit length code to send.
+ */
+function build_bl_tree(s) {
+ var max_blindex; /* index of last bit length code of non zero freq */
+
+ /* Determine the bit length frequencies for literal and distance trees */
+ scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
+ scan_tree(s, s.dyn_dtree, s.d_desc.max_code);
+
+ /* Build the bit length tree: */
+ build_tree(s, s.bl_desc);
+ /* opt_len now includes the length of the tree representations, except
+ * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
+ */
+
+ /* Determine the number of bit length codes to send. The pkzip format
+ * requires that at least 4 bit length codes be sent. (appnote.txt says
+ * 3 but the actual value used is 4.)
+ */
+ for (max_blindex = BL_CODES - 1; max_blindex >= 3; max_blindex--) {
+ if (s.bl_tree[bl_order[max_blindex] * 2 + 1]/*.Len*/ !== 0) {
+ break;
+ }
+ }
+ /* Update opt_len to include the bit length tree and counts */
+ s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
+ //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
+ // s->opt_len, s->static_len));
+
+ return max_blindex;
+}
+
+
+/* ===========================================================================
+ * Send the header for a block using dynamic Huffman trees: the counts, the
+ * lengths of the bit length codes, the literal tree and the distance tree.
+ * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
+ */
+function send_all_trees(s, lcodes, dcodes, blcodes)
+// deflate_state *s;
+// int lcodes, dcodes, blcodes; /* number of codes for each tree */
+{
+ var rank; /* index in bl_order */
+
+ //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
+ //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
+ // "too many codes");
+ //Tracev((stderr, "\nbl counts: "));
+ send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */
+ send_bits(s, dcodes - 1, 5);
+ send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */
+ for (rank = 0; rank < blcodes; rank++) {
+ //Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
+ send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1]/*.Len*/, 3);
+ }
+ //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));
+
+ send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */
+ //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));
+
+ send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */
+ //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
+}
+
+
+/* ===========================================================================
+ * Check if the data type is TEXT or BINARY, using the following algorithm:
+ * - TEXT if the two conditions below are satisfied:
+ * a) There are no non-portable control characters belonging to the
+ * "black list" (0..6, 14..25, 28..31).
+ * b) There is at least one printable character belonging to the
+ * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
+ * - BINARY otherwise.
+ * - The following partially-portable control characters form a
+ * "gray list" that is ignored in this detection algorithm:
+ * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
+ * IN assertion: the fields Freq of dyn_ltree are set.
+ */
+function detect_data_type(s) {
+ /* black_mask is the bit mask of black-listed bytes
+ * set bits 0..6, 14..25, and 28..31
+ * 0xf3ffc07f = binary 11110011111111111100000001111111
+ */
+ var black_mask = 0xf3ffc07f;
+ var n;
+
+ /* Check for non-textual ("black-listed") bytes. */
+ for (n = 0; n <= 31; n++, black_mask >>>= 1) {
+ if ((black_mask & 1) && (s.dyn_ltree[n * 2]/*.Freq*/ !== 0)) {
+ return Z_BINARY;
+ }
+ }
+
+ /* Check for textual ("white-listed") bytes. */
+ if (s.dyn_ltree[9 * 2]/*.Freq*/ !== 0 || s.dyn_ltree[10 * 2]/*.Freq*/ !== 0 ||
+ s.dyn_ltree[13 * 2]/*.Freq*/ !== 0) {
+ return Z_TEXT;
+ }
+ for (n = 32; n < LITERALS; n++) {
+ if (s.dyn_ltree[n * 2]/*.Freq*/ !== 0) {
+ return Z_TEXT;
+ }
+ }
+
+ /* There are no "black-listed" or "white-listed" bytes:
+ * this stream either is empty or has tolerated ("gray-listed") bytes only.
+ */
+ return Z_BINARY;
+}
+
+
+var static_init_done = false;
+
+/* ===========================================================================
+ * Initialize the tree data structures for a new zlib stream.
+ */
+function _tr_init(s)
+{
+
+ if (!static_init_done) {
+ tr_static_init();
+ static_init_done = true;
+ }
+
+ s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc);
+ s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc);
+ s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);
+
+ s.bi_buf = 0;
+ s.bi_valid = 0;
+
+ /* Initialize the first block of the first file: */
+ init_block(s);
+}
+
+
+/* ===========================================================================
+ * Send a stored block
+ */
+function _tr_stored_block(s, buf, stored_len, last)
+//DeflateState *s;
+//charf *buf; /* input block */
+//ulg stored_len; /* length of input block */
+//int last; /* one if this is the last block for a file */
+{
+ send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */
+ copy_block(s, buf, stored_len, true); /* with header */
+}
+
+
+/* ===========================================================================
+ * Send one empty static block to give enough lookahead for inflate.
+ * This takes 10 bits, of which 7 may remain in the bit buffer.
+ */
+function _tr_align(s) {
+ send_bits(s, STATIC_TREES << 1, 3);
+ send_code(s, END_BLOCK, static_ltree);
+ bi_flush(s);
+}
+
+
+/* ===========================================================================
+ * Determine the best encoding for the current block: dynamic trees, static
+ * trees or store, and output the encoded block to the zip file.
+ */
+function _tr_flush_block(s, buf, stored_len, last)
+//DeflateState *s;
+//charf *buf; /* input block, or NULL if too old */
+//ulg stored_len; /* length of input block */
+//int last; /* one if this is the last block for a file */
+{
+ var opt_lenb, static_lenb; /* opt_len and static_len in bytes */
+ var max_blindex = 0; /* index of last bit length code of non zero freq */
+
+ /* Build the Huffman trees unless a stored block is forced */
+ if (s.level > 0) {
+
+ /* Check if the file is binary or text */
+ if (s.strm.data_type === Z_UNKNOWN) {
+ s.strm.data_type = detect_data_type(s);
+ }
+
+ /* Construct the literal and distance trees */
+ build_tree(s, s.l_desc);
+ // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
+ // s->static_len));
+
+ build_tree(s, s.d_desc);
+ // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
+ // s->static_len));
+ /* At this point, opt_len and static_len are the total bit lengths of
+ * the compressed block data, excluding the tree representations.
+ */
+
+ /* Build the bit length tree for the above two trees, and get the index
+ * in bl_order of the last bit length code to send.
+ */
+ max_blindex = build_bl_tree(s);
+
+ /* Determine the best encoding. Compute the block lengths in bytes. */
+ opt_lenb = (s.opt_len + 3 + 7) >>> 3;
+ static_lenb = (s.static_len + 3 + 7) >>> 3;
+
+ // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
+ // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
+ // s->last_lit));
+
+ if (static_lenb <= opt_lenb) { opt_lenb = static_lenb; }
+
+ } else {
+ // Assert(buf != (char*)0, "lost buf");
+ opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
+ }
+
+ if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) {
+ /* 4: two words for the lengths */
+
+ /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
+ * Otherwise we can't have processed more than WSIZE input bytes since
+ * the last block flush, because compression would have been
+ * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
+ * transform a block into a stored block.
+ */
+ _tr_stored_block(s, buf, stored_len, last);
+
+ } else if (s.strategy === Z_FIXED || static_lenb === opt_lenb) {
+
+ send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
+ compress_block(s, static_ltree, static_dtree);
+
+ } else {
+ send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
+ send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
+ compress_block(s, s.dyn_ltree, s.dyn_dtree);
+ }
+ // Assert (s->compressed_len == s->bits_sent, "bad compressed size");
+ /* The above check is made mod 2^32, for files larger than 512 MB
+ * and uLong implemented on 32 bits.
+ */
+ init_block(s);
+
+ if (last) {
+ bi_windup(s);
+ }
+ // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
+ // s->compressed_len-7*last));
+}
+
+/* ===========================================================================
+ * Save the match info and tally the frequency counts. Return true if
+ * the current block must be flushed.
+ */
+function _tr_tally(s, dist, lc)
+// deflate_state *s;
+// unsigned dist; /* distance of matched string */
+// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */
+{
+ //var out_length, in_length, dcode;
+
+ s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff;
+ s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff;
+
+ s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff;
+ s.last_lit++;
+
+ if (dist === 0) {
+ /* lc is the unmatched char */
+ s.dyn_ltree[lc * 2]/*.Freq*/++;
+ } else {
+ s.matches++;
+ /* Here, lc is the match length - MIN_MATCH */
+ dist--; /* dist = match distance - 1 */
+ //Assert((ush)dist < (ush)MAX_DIST(s) &&
+ // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
+ // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match");
+
+ s.dyn_ltree[(_length_code[lc] + LITERALS + 1) * 2]/*.Freq*/++;
+ s.dyn_dtree[d_code(dist) * 2]/*.Freq*/++;
+ }
+
+// (!) This block is disabled in zlib defailts,
+// don't enable it for binary compatibility
+
+//#ifdef TRUNCATE_BLOCK
+// /* Try to guess if it is profitable to stop the current block here */
+// if ((s.last_lit & 0x1fff) === 0 && s.level > 2) {
+// /* Compute an upper bound for the compressed length */
+// out_length = s.last_lit*8;
+// in_length = s.strstart - s.block_start;
+//
+// for (dcode = 0; dcode < D_CODES; dcode++) {
+// out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]);
+// }
+// out_length >>>= 3;
+// //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ",
+// // s->last_lit, in_length, out_length,
+// // 100L - out_length*100L/in_length));
+// if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) {
+// return true;
+// }
+// }
+//#endif
+
+ return (s.last_lit === s.lit_bufsize - 1);
+ /* We avoid equality with lit_bufsize because of wraparound at 64K
+ * on 16 bit machines and because stored blocks are restricted to
+ * 64K-1 bytes.
+ */
+}
+
+exports._tr_init = _tr_init;
+exports._tr_stored_block = _tr_stored_block;
+exports._tr_flush_block = _tr_flush_block;
+exports._tr_tally = _tr_tally;
+exports._tr_align = _tr_align;
+
+},{"../utils/common":41}],53:[function(require,module,exports){
+'use strict';
+
+// (C) 1995-2013 Jean-loup Gailly and Mark Adler
+// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
+//
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+//
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+//
+// 1. The origin of this software must not be misrepresented; you must not
+// claim that you wrote the original software. If you use this software
+// in a product, an acknowledgment in the product documentation would be
+// appreciated but is not required.
+// 2. Altered source versions must be plainly marked as such, and must not be
+// misrepresented as being the original software.
+// 3. This notice may not be removed or altered from any source distribution.
+
+function ZStream() {
+ /* next input byte */
+ this.input = null; // JS specific, because we have no pointers
+ this.next_in = 0;
+ /* number of bytes available at input */
+ this.avail_in = 0;
+ /* total number of input bytes read so far */
+ this.total_in = 0;
+ /* next output byte should be put there */
+ this.output = null; // JS specific, because we have no pointers
+ this.next_out = 0;
+ /* remaining free space at output */
+ this.avail_out = 0;
+ /* total number of bytes output so far */
+ this.total_out = 0;
+ /* last error message, NULL if no error */
+ this.msg = ''/*Z_NULL*/;
+ /* not visible by applications */
+ this.state = null;
+ /* best guess about the data type: binary or text */
+ this.data_type = 2/*Z_UNKNOWN*/;
+ /* adler32 value of the uncompressed data */
+ this.adler = 0;
+}
+
+module.exports = ZStream;
+
+},{}],54:[function(require,module,exports){
+'use strict';
+module.exports = typeof setImmediate === 'function' ? setImmediate :
+ function setImmediate() {
+ var args = [].slice.apply(arguments);
+ args.splice(1, 0, 0);
+ setTimeout.apply(null, args);
+ };
+
+},{}]},{},[10])(10)
+}); \ No newline at end of file
diff --git a/devtools/client/shared/vendor/md5.js b/devtools/client/shared/vendor/md5.js
new file mode 100644
index 0000000000..63482c42f2
--- /dev/null
+++ b/devtools/client/shared/vendor/md5.js
@@ -0,0 +1,7 @@
+!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.MD5=n():t.MD5=n()}(this,function(){return function(t){function n(o){if(r[o])return r[o].exports;var e=r[o]={i:o,l:!1,exports:{}};return t[o].call(e.exports,e,e.exports,n),e.l=!0,e.exports}var r={};return n.m=t,n.c=r,n.i=function(t){return t},n.d=function(t,r,o){n.o(t,r)||Object.defineProperty(t,r,{configurable:!1,enumerable:!0,get:o})},n.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(r,"a",r),r},n.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},n.p="",n(n.s=4)}([function(t,n){var r={utf8:{stringToBytes:function(t){return r.bin.stringToBytes(unescape(encodeURIComponent(t)))},bytesToString:function(t){return decodeURIComponent(escape(r.bin.bytesToString(t)))}},bin:{stringToBytes:function(t){for(var n=[],r=0;r<t.length;r++)n.push(255&t.charCodeAt(r));return n},bytesToString:function(t){for(var n=[],r=0;r<t.length;r++)n.push(String.fromCharCode(t[r]));return n.join("")}}};t.exports=r},function(t,n,r){!function(){var n=r(2),o=r(0).utf8,e=r(3),u=r(0).bin,i=function(t,r){t.constructor==String?t=r&&"binary"===r.encoding?u.stringToBytes(t):o.stringToBytes(t):e(t)?t=Array.prototype.slice.call(t,0):Array.isArray(t)||t.constructor===Uint8Array||(t=t.toString());for(var f=n.bytesToWords(t),s=8*t.length,c=1732584193,a=-271733879,p=-1732584194,l=271733878,g=0;g<f.length;g++)f[g]=16711935&(f[g]<<8|f[g]>>>24)|4278255360&(f[g]<<24|f[g]>>>8);f[s>>>5]|=128<<s%32,f[14+(s+64>>>9<<4)]=s;for(var h=i._ff,y=i._gg,d=i._hh,v=i._ii,g=0;g<f.length;g+=16){var b=c,x=a,T=p,B=l;c=h(c,a,p,l,f[g+0],7,-680876936),l=h(l,c,a,p,f[g+1],12,-389564586),p=h(p,l,c,a,f[g+2],17,606105819),a=h(a,p,l,c,f[g+3],22,-1044525330),c=h(c,a,p,l,f[g+4],7,-176418897),l=h(l,c,a,p,f[g+5],12,1200080426),p=h(p,l,c,a,f[g+6],17,-1473231341),a=h(a,p,l,c,f[g+7],22,-45705983),c=h(c,a,p,l,f[g+8],7,1770035416),l=h(l,c,a,p,f[g+9],12,-1958414417),p=h(p,l,c,a,f[g+10],17,-42063),a=h(a,p,l,c,f[g+11],22,-1990404162),c=h(c,a,p,l,f[g+12],7,1804603682),l=h(l,c,a,p,f[g+13],12,-40341101),p=h(p,l,c,a,f[g+14],17,-1502002290),a=h(a,p,l,c,f[g+15],22,1236535329),c=y(c,a,p,l,f[g+1],5,-165796510),l=y(l,c,a,p,f[g+6],9,-1069501632),p=y(p,l,c,a,f[g+11],14,643717713),a=y(a,p,l,c,f[g+0],20,-373897302),c=y(c,a,p,l,f[g+5],5,-701558691),l=y(l,c,a,p,f[g+10],9,38016083),p=y(p,l,c,a,f[g+15],14,-660478335),a=y(a,p,l,c,f[g+4],20,-405537848),c=y(c,a,p,l,f[g+9],5,568446438),l=y(l,c,a,p,f[g+14],9,-1019803690),p=y(p,l,c,a,f[g+3],14,-187363961),a=y(a,p,l,c,f[g+8],20,1163531501),c=y(c,a,p,l,f[g+13],5,-1444681467),l=y(l,c,a,p,f[g+2],9,-51403784),p=y(p,l,c,a,f[g+7],14,1735328473),a=y(a,p,l,c,f[g+12],20,-1926607734),c=d(c,a,p,l,f[g+5],4,-378558),l=d(l,c,a,p,f[g+8],11,-2022574463),p=d(p,l,c,a,f[g+11],16,1839030562),a=d(a,p,l,c,f[g+14],23,-35309556),c=d(c,a,p,l,f[g+1],4,-1530992060),l=d(l,c,a,p,f[g+4],11,1272893353),p=d(p,l,c,a,f[g+7],16,-155497632),a=d(a,p,l,c,f[g+10],23,-1094730640),c=d(c,a,p,l,f[g+13],4,681279174),l=d(l,c,a,p,f[g+0],11,-358537222),p=d(p,l,c,a,f[g+3],16,-722521979),a=d(a,p,l,c,f[g+6],23,76029189),c=d(c,a,p,l,f[g+9],4,-640364487),l=d(l,c,a,p,f[g+12],11,-421815835),p=d(p,l,c,a,f[g+15],16,530742520),a=d(a,p,l,c,f[g+2],23,-995338651),c=v(c,a,p,l,f[g+0],6,-198630844),l=v(l,c,a,p,f[g+7],10,1126891415),p=v(p,l,c,a,f[g+14],15,-1416354905),a=v(a,p,l,c,f[g+5],21,-57434055),c=v(c,a,p,l,f[g+12],6,1700485571),l=v(l,c,a,p,f[g+3],10,-1894986606),p=v(p,l,c,a,f[g+10],15,-1051523),a=v(a,p,l,c,f[g+1],21,-2054922799),c=v(c,a,p,l,f[g+8],6,1873313359),l=v(l,c,a,p,f[g+15],10,-30611744),p=v(p,l,c,a,f[g+6],15,-1560198380),a=v(a,p,l,c,f[g+13],21,1309151649),c=v(c,a,p,l,f[g+4],6,-145523070),l=v(l,c,a,p,f[g+11],10,-1120210379),p=v(p,l,c,a,f[g+2],15,718787259),a=v(a,p,l,c,f[g+9],21,-343485551),c=c+b>>>0,a=a+x>>>0,p=p+T>>>0,l=l+B>>>0}return n.endian([c,a,p,l])};i._ff=function(t,n,r,o,e,u,i){var f=t+(n&r|~n&o)+(e>>>0)+i;return(f<<u|f>>>32-u)+n},i._gg=function(t,n,r,o,e,u,i){var f=t+(n&o|r&~o)+(e>>>0)+i;return(f<<u|f>>>32-u)+n},i._hh=function(t,n,r,o,e,u,i){var f=t+(n^r^o)+(e>>>0)+i;return(f<<u|f>>>32-u)+n},i._ii=function(t,n,r,o,e,u,i){var f=t+(r^(n|~o))+(e>>>0)+i;return(f<<u|f>>>32-u)+n},i._blocksize=16,i._digestsize=16,t.exports=function(t,r){if(void 0===t||null===t)throw new Error("Illegal argument "+t);var o=n.wordsToBytes(i(t,r));return r&&r.asBytes?o:r&&r.asString?u.bytesToString(o):n.bytesToHex(o)}}()},function(t,n){!function(){var n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r={rotl:function(t,n){return t<<n|t>>>32-n},rotr:function(t,n){return t<<32-n|t>>>n},endian:function(t){if(t.constructor==Number)return 16711935&r.rotl(t,8)|4278255360&r.rotl(t,24);for(var n=0;n<t.length;n++)t[n]=r.endian(t[n]);return t},randomBytes:function(t){for(var n=[];t>0;t--)n.push(Math.floor(256*Math.random()));return n},bytesToWords:function(t){for(var n=[],r=0,o=0;r<t.length;r++,o+=8)n[o>>>5]|=t[r]<<24-o%32;return n},wordsToBytes:function(t){for(var n=[],r=0;r<32*t.length;r+=8)n.push(t[r>>>5]>>>24-r%32&255);return n},bytesToHex:function(t){for(var n=[],r=0;r<t.length;r++)n.push((t[r]>>>4).toString(16)),n.push((15&t[r]).toString(16));return n.join("")},hexToBytes:function(t){for(var n=[],r=0;r<t.length;r+=2)n.push(parseInt(t.substr(r,2),16));return n},bytesToBase64:function(t){for(var r=[],o=0;o<t.length;o+=3)for(var e=t[o]<<16|t[o+1]<<8|t[o+2],u=0;u<4;u++)8*o+6*u<=8*t.length?r.push(n.charAt(e>>>6*(3-u)&63)):r.push("=");return r.join("")},base64ToBytes:function(t){t=t.replace(/[^A-Z0-9+\/]/gi,"");for(var r=[],o=0,e=0;o<t.length;e=++o%4)0!=e&&r.push((n.indexOf(t.charAt(o-1))&Math.pow(2,-2*e+8)-1)<<2*e|n.indexOf(t.charAt(o))>>>6-2*e);return r}};t.exports=r}()},function(t,n){function r(t){return!!t.constructor&&"function"==typeof t.constructor.isBuffer&&t.constructor.isBuffer(t)}function o(t){return"function"==typeof t.readFloatLE&&"function"==typeof t.slice&&r(t.slice(0,0))}/*!
+ * Determine if an object is a Buffer
+ *
+ * @author Feross Aboukhadijeh <https://feross.org>
+ * @license MIT
+ */
+t.exports=function(t){return null!=t&&(r(t)||o(t)||!!t._isBuffer)}},function(t,n,r){t.exports=r(1)}])}); \ No newline at end of file
diff --git a/devtools/client/shared/vendor/micromatch/LICENSE b/devtools/client/shared/vendor/micromatch/LICENSE
new file mode 100755
index 0000000000..9af4a67d20
--- /dev/null
+++ b/devtools/client/shared/vendor/micromatch/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-present, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/devtools/client/shared/vendor/micromatch/UPGRADE.md b/devtools/client/shared/vendor/micromatch/UPGRADE.md
new file mode 100644
index 0000000000..785a0cef56
--- /dev/null
+++ b/devtools/client/shared/vendor/micromatch/UPGRADE.md
@@ -0,0 +1,17 @@
+# STEPS TO UPGRADE `MICROMATCH`
+
+# Go to the `micromatch` vendor folder
+
+1. cd devtools/client/shared/vendor/micromatch/ folder
+
+# Install the node package (Remeber to bump the version (to the latest) in the package.json)
+
+2. npm install
+
+# Generate the bundle (creates a `micromatch.js` file)
+
+3. npm run webpack
+
+# Cleanup all the folders and files
+
+4. rm -rf node_modules
diff --git a/devtools/client/shared/vendor/micromatch/micromatch.js b/devtools/client/shared/vendor/micromatch/micromatch.js
new file mode 100644
index 0000000000..868bf14e65
--- /dev/null
+++ b/devtools/client/shared/vendor/micromatch/micromatch.js
@@ -0,0 +1,5424 @@
+exports["micromatch"] =
+/******/ (function(modules) { // webpackBootstrap
+/******/ // The module cache
+/******/ var installedModules = {};
+/******/
+/******/ // The require function
+/******/ function __webpack_require__(moduleId) {
+/******/
+/******/ // Check if module is in cache
+/******/ if(installedModules[moduleId]) {
+/******/ return installedModules[moduleId].exports;
+/******/ }
+/******/ // Create a new module (and put it into the cache)
+/******/ var module = installedModules[moduleId] = {
+/******/ i: moduleId,
+/******/ l: false,
+/******/ exports: {}
+/******/ };
+/******/
+/******/ // Execute the module function
+/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ // Flag the module as loaded
+/******/ module.l = true;
+/******/
+/******/ // Return the exports of the module
+/******/ return module.exports;
+/******/ }
+/******/
+/******/
+/******/ // expose the modules object (__webpack_modules__)
+/******/ __webpack_require__.m = modules;
+/******/
+/******/ // expose the module cache
+/******/ __webpack_require__.c = installedModules;
+/******/
+/******/ // define getter function for harmony exports
+/******/ __webpack_require__.d = function(exports, name, getter) {
+/******/ if(!__webpack_require__.o(exports, name)) {
+/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
+/******/ }
+/******/ };
+/******/
+/******/ // define __esModule on exports
+/******/ __webpack_require__.r = function(exports) {
+/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
+/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+/******/ }
+/******/ Object.defineProperty(exports, '__esModule', { value: true });
+/******/ };
+/******/
+/******/ // create a fake namespace object
+/******/ // mode & 1: value is a module id, require it
+/******/ // mode & 2: merge all properties of value into the ns
+/******/ // mode & 4: return value when already ns object
+/******/ // mode & 8|1: behave like require
+/******/ __webpack_require__.t = function(value, mode) {
+/******/ if(mode & 1) value = __webpack_require__(value);
+/******/ if(mode & 8) return value;
+/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
+/******/ var ns = Object.create(null);
+/******/ __webpack_require__.r(ns);
+/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
+/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
+/******/ return ns;
+/******/ };
+/******/
+/******/ // getDefaultExport function for compatibility with non-harmony modules
+/******/ __webpack_require__.n = function(module) {
+/******/ var getter = module && module.__esModule ?
+/******/ function getDefault() { return module['default']; } :
+/******/ function getModuleExports() { return module; };
+/******/ __webpack_require__.d(getter, 'a', getter);
+/******/ return getter;
+/******/ };
+/******/
+/******/ // Object.prototype.hasOwnProperty.call
+/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ // __webpack_public_path__
+/******/ __webpack_require__.p = "";
+/******/
+/******/
+/******/ // Load entry module and return exports
+/******/ return __webpack_require__(__webpack_require__.s = 8);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/* WEBPACK VAR INJECTION */(function(process) {
+
+const path = __webpack_require__(5);
+const win32 = process.platform === 'win32';
+const {
+ REGEX_BACKSLASH,
+ REGEX_REMOVE_BACKSLASH,
+ REGEX_SPECIAL_CHARS,
+ REGEX_SPECIAL_CHARS_GLOBAL
+} = __webpack_require__(1);
+
+exports.isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
+exports.hasRegexChars = str => REGEX_SPECIAL_CHARS.test(str);
+exports.isRegexChar = str => str.length === 1 && exports.hasRegexChars(str);
+exports.escapeRegex = str => str.replace(REGEX_SPECIAL_CHARS_GLOBAL, '\\$1');
+exports.toPosixSlashes = str => str.replace(REGEX_BACKSLASH, '/');
+
+exports.removeBackslashes = str => {
+ return str.replace(REGEX_REMOVE_BACKSLASH, match => {
+ return match === '\\' ? '' : match;
+ });
+};
+
+exports.supportsLookbehinds = () => {
+ const segs = process.version.slice(1).split('.').map(Number);
+ if (segs.length === 3 && segs[0] >= 9 || (segs[0] === 8 && segs[1] >= 10)) {
+ return true;
+ }
+ return false;
+};
+
+exports.isWindows = options => {
+ if (options && typeof options.windows === 'boolean') {
+ return options.windows;
+ }
+ return win32 === true || path.sep === '\\';
+};
+
+exports.escapeLast = (input, char, lastIdx) => {
+ const idx = input.lastIndexOf(char, lastIdx);
+ if (idx === -1) return input;
+ if (input[idx - 1] === '\\') return exports.escapeLast(input, char, idx - 1);
+ return `${input.slice(0, idx)}\\${input.slice(idx)}`;
+};
+
+exports.removePrefix = (input, state = {}) => {
+ let output = input;
+ if (output.startsWith('./')) {
+ output = output.slice(2);
+ state.prefix = './';
+ }
+ return output;
+};
+
+exports.wrapOutput = (input, state = {}, options = {}) => {
+ const prepend = options.contains ? '' : '^';
+ const append = options.contains ? '' : '$';
+
+ let output = `${prepend}(?:${input})${append}`;
+ if (state.negated === true) {
+ output = `(?:^(?!${output}).*$)`;
+ }
+ return output;
+};
+
+/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2)))
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const path = __webpack_require__(5);
+const WIN_SLASH = '\\\\/';
+const WIN_NO_SLASH = `[^${WIN_SLASH}]`;
+
+/**
+ * Posix glob regex
+ */
+
+const DOT_LITERAL = '\\.';
+const PLUS_LITERAL = '\\+';
+const QMARK_LITERAL = '\\?';
+const SLASH_LITERAL = '\\/';
+const ONE_CHAR = '(?=.)';
+const QMARK = '[^/]';
+const END_ANCHOR = `(?:${SLASH_LITERAL}|$)`;
+const START_ANCHOR = `(?:^|${SLASH_LITERAL})`;
+const DOTS_SLASH = `${DOT_LITERAL}{1,2}${END_ANCHOR}`;
+const NO_DOT = `(?!${DOT_LITERAL})`;
+const NO_DOTS = `(?!${START_ANCHOR}${DOTS_SLASH})`;
+const NO_DOT_SLASH = `(?!${DOT_LITERAL}{0,1}${END_ANCHOR})`;
+const NO_DOTS_SLASH = `(?!${DOTS_SLASH})`;
+const QMARK_NO_DOT = `[^.${SLASH_LITERAL}]`;
+const STAR = `${QMARK}*?`;
+
+const POSIX_CHARS = {
+ DOT_LITERAL,
+ PLUS_LITERAL,
+ QMARK_LITERAL,
+ SLASH_LITERAL,
+ ONE_CHAR,
+ QMARK,
+ END_ANCHOR,
+ DOTS_SLASH,
+ NO_DOT,
+ NO_DOTS,
+ NO_DOT_SLASH,
+ NO_DOTS_SLASH,
+ QMARK_NO_DOT,
+ STAR,
+ START_ANCHOR
+};
+
+/**
+ * Windows glob regex
+ */
+
+const WINDOWS_CHARS = {
+ ...POSIX_CHARS,
+
+ SLASH_LITERAL: `[${WIN_SLASH}]`,
+ QMARK: WIN_NO_SLASH,
+ STAR: `${WIN_NO_SLASH}*?`,
+ DOTS_SLASH: `${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$)`,
+ NO_DOT: `(?!${DOT_LITERAL})`,
+ NO_DOTS: `(?!(?:^|[${WIN_SLASH}])${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
+ NO_DOT_SLASH: `(?!${DOT_LITERAL}{0,1}(?:[${WIN_SLASH}]|$))`,
+ NO_DOTS_SLASH: `(?!${DOT_LITERAL}{1,2}(?:[${WIN_SLASH}]|$))`,
+ QMARK_NO_DOT: `[^.${WIN_SLASH}]`,
+ START_ANCHOR: `(?:^|[${WIN_SLASH}])`,
+ END_ANCHOR: `(?:[${WIN_SLASH}]|$)`
+};
+
+/**
+ * POSIX Bracket Regex
+ */
+
+const POSIX_REGEX_SOURCE = {
+ alnum: 'a-zA-Z0-9',
+ alpha: 'a-zA-Z',
+ ascii: '\\x00-\\x7F',
+ blank: ' \\t',
+ cntrl: '\\x00-\\x1F\\x7F',
+ digit: '0-9',
+ graph: '\\x21-\\x7E',
+ lower: 'a-z',
+ print: '\\x20-\\x7E ',
+ punct: '\\-!"#$%&\'()\\*+,./:;<=>?@[\\]^_`{|}~',
+ space: ' \\t\\r\\n\\v\\f',
+ upper: 'A-Z',
+ word: 'A-Za-z0-9_',
+ xdigit: 'A-Fa-f0-9'
+};
+
+module.exports = {
+ MAX_LENGTH: 1024 * 64,
+ POSIX_REGEX_SOURCE,
+
+ // regular expressions
+ REGEX_BACKSLASH: /\\(?![*+?^${}(|)[\]])/g,
+ REGEX_NON_SPECIAL_CHARS: /^[^@![\].,$*+?^{}()|\\/]+/,
+ REGEX_SPECIAL_CHARS: /[-*+?.^${}(|)[\]]/,
+ REGEX_SPECIAL_CHARS_BACKREF: /(\\?)((\W)(\3*))/g,
+ REGEX_SPECIAL_CHARS_GLOBAL: /([-*+?.^${}(|)[\]])/g,
+ REGEX_REMOVE_BACKSLASH: /(?:\[.*?[^\\]\]|\\(?=.))/g,
+
+ // Replace globs with equivalent patterns to reduce parsing time.
+ REPLACEMENTS: {
+ '***': '*',
+ '**/**': '**',
+ '**/**/**': '**'
+ },
+
+ // Digits
+ CHAR_0: 48, /* 0 */
+ CHAR_9: 57, /* 9 */
+
+ // Alphabet chars.
+ CHAR_UPPERCASE_A: 65, /* A */
+ CHAR_LOWERCASE_A: 97, /* a */
+ CHAR_UPPERCASE_Z: 90, /* Z */
+ CHAR_LOWERCASE_Z: 122, /* z */
+
+ CHAR_LEFT_PARENTHESES: 40, /* ( */
+ CHAR_RIGHT_PARENTHESES: 41, /* ) */
+
+ CHAR_ASTERISK: 42, /* * */
+
+ // Non-alphabetic chars.
+ CHAR_AMPERSAND: 38, /* & */
+ CHAR_AT: 64, /* @ */
+ CHAR_BACKWARD_SLASH: 92, /* \ */
+ CHAR_CARRIAGE_RETURN: 13, /* \r */
+ CHAR_CIRCUMFLEX_ACCENT: 94, /* ^ */
+ CHAR_COLON: 58, /* : */
+ CHAR_COMMA: 44, /* , */
+ CHAR_DOT: 46, /* . */
+ CHAR_DOUBLE_QUOTE: 34, /* " */
+ CHAR_EQUAL: 61, /* = */
+ CHAR_EXCLAMATION_MARK: 33, /* ! */
+ CHAR_FORM_FEED: 12, /* \f */
+ CHAR_FORWARD_SLASH: 47, /* / */
+ CHAR_GRAVE_ACCENT: 96, /* ` */
+ CHAR_HASH: 35, /* # */
+ CHAR_HYPHEN_MINUS: 45, /* - */
+ CHAR_LEFT_ANGLE_BRACKET: 60, /* < */
+ CHAR_LEFT_CURLY_BRACE: 123, /* { */
+ CHAR_LEFT_SQUARE_BRACKET: 91, /* [ */
+ CHAR_LINE_FEED: 10, /* \n */
+ CHAR_NO_BREAK_SPACE: 160, /* \u00A0 */
+ CHAR_PERCENT: 37, /* % */
+ CHAR_PLUS: 43, /* + */
+ CHAR_QUESTION_MARK: 63, /* ? */
+ CHAR_RIGHT_ANGLE_BRACKET: 62, /* > */
+ CHAR_RIGHT_CURLY_BRACE: 125, /* } */
+ CHAR_RIGHT_SQUARE_BRACKET: 93, /* ] */
+ CHAR_SEMICOLON: 59, /* ; */
+ CHAR_SINGLE_QUOTE: 39, /* ' */
+ CHAR_SPACE: 32, /* */
+ CHAR_TAB: 9, /* \t */
+ CHAR_UNDERSCORE: 95, /* _ */
+ CHAR_VERTICAL_LINE: 124, /* | */
+ CHAR_ZERO_WIDTH_NOBREAK_SPACE: 65279, /* \uFEFF */
+
+ SEP: path.sep,
+
+ /**
+ * Create EXTGLOB_CHARS
+ */
+
+ extglobChars(chars) {
+ return {
+ '!': { type: 'negate', open: '(?:(?!(?:', close: `))${chars.STAR})` },
+ '?': { type: 'qmark', open: '(?:', close: ')?' },
+ '+': { type: 'plus', open: '(?:', close: ')+' },
+ '*': { type: 'star', open: '(?:', close: ')*' },
+ '@': { type: 'at', open: '(?:', close: ')' }
+ };
+ },
+
+ /**
+ * Create GLOB_CHARS
+ */
+
+ globChars(win32) {
+ return win32 === true ? WINDOWS_CHARS : POSIX_CHARS;
+ }
+};
+
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports) {
+
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things. But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals. It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+ throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+ throw new Error('clearTimeout has not been defined');
+}
+(function () {
+ try {
+ if (typeof setTimeout === 'function') {
+ cachedSetTimeout = setTimeout;
+ } else {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ } catch (e) {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ try {
+ if (typeof clearTimeout === 'function') {
+ cachedClearTimeout = clearTimeout;
+ } else {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+ } catch (e) {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+} ())
+function runTimeout(fun) {
+ if (cachedSetTimeout === setTimeout) {
+ //normal enviroments in sane situations
+ return setTimeout(fun, 0);
+ }
+ // if setTimeout wasn't available but was latter defined
+ if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+ cachedSetTimeout = setTimeout;
+ return setTimeout(fun, 0);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedSetTimeout(fun, 0);
+ } catch(e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedSetTimeout.call(null, fun, 0);
+ } catch(e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+ return cachedSetTimeout.call(this, fun, 0);
+ }
+ }
+
+
+}
+function runClearTimeout(marker) {
+ if (cachedClearTimeout === clearTimeout) {
+ //normal enviroments in sane situations
+ return clearTimeout(marker);
+ }
+ // if clearTimeout wasn't available but was latter defined
+ if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+ cachedClearTimeout = clearTimeout;
+ return clearTimeout(marker);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedClearTimeout(marker);
+ } catch (e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedClearTimeout.call(null, marker);
+ } catch (e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+ // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+ return cachedClearTimeout.call(this, marker);
+ }
+ }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ if (!draining || !currentQueue) {
+ return;
+ }
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = runTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
+ }
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ runTimeout(drainQueue);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const utils = __webpack_require__(4);
+
+module.exports = (ast, options = {}) => {
+ let stringify = (node, parent = {}) => {
+ let invalidBlock = options.escapeInvalid && utils.isInvalidBrace(parent);
+ let invalidNode = node.invalid === true && options.escapeInvalid === true;
+ let output = '';
+
+ if (node.value) {
+ if ((invalidBlock || invalidNode) && utils.isOpenOrClose(node)) {
+ return '\\' + node.value;
+ }
+ return node.value;
+ }
+
+ if (node.value) {
+ return node.value;
+ }
+
+ if (node.nodes) {
+ for (let child of node.nodes) {
+ output += stringify(child);
+ }
+ }
+ return output;
+ };
+
+ return stringify(ast);
+};
+
+
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+exports.isInteger = num => {
+ if (typeof num === 'number') {
+ return Number.isInteger(num);
+ }
+ if (typeof num === 'string' && num.trim() !== '') {
+ return Number.isInteger(Number(num));
+ }
+ return false;
+};
+
+/**
+ * Find a node of the given type
+ */
+
+exports.find = (node, type) => node.nodes.find(node => node.type === type);
+
+/**
+ * Find a node of the given type
+ */
+
+exports.exceedsLimit = (min, max, step = 1, limit) => {
+ if (limit === false) return false;
+ if (!exports.isInteger(min) || !exports.isInteger(max)) return false;
+ return ((Number(max) - Number(min)) / Number(step)) >= limit;
+};
+
+/**
+ * Escape the given node with '\\' before node.value
+ */
+
+exports.escapeNode = (block, n = 0, type) => {
+ let node = block.nodes[n];
+ if (!node) return;
+
+ if ((type && node.type === type) || node.type === 'open' || node.type === 'close') {
+ if (node.escaped !== true) {
+ node.value = '\\' + node.value;
+ node.escaped = true;
+ }
+ }
+};
+
+/**
+ * Returns true if the given brace node should be enclosed in literal braces
+ */
+
+exports.encloseBrace = node => {
+ if (node.type !== 'brace') return false;
+ if ((node.commas >> 0 + node.ranges >> 0) === 0) {
+ node.invalid = true;
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Returns true if a brace node is invalid.
+ */
+
+exports.isInvalidBrace = block => {
+ if (block.type !== 'brace') return false;
+ if (block.invalid === true || block.dollar) return true;
+ if ((block.commas >> 0 + block.ranges >> 0) === 0) {
+ block.invalid = true;
+ return true;
+ }
+ if (block.open !== true || block.close !== true) {
+ block.invalid = true;
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Returns true if a node is an open or close node
+ */
+
+exports.isOpenOrClose = node => {
+ if (node.type === 'open' || node.type === 'close') {
+ return true;
+ }
+ return node.open === true || node.close === true;
+};
+
+/**
+ * Reduce an array of text nodes.
+ */
+
+exports.reduce = nodes => nodes.reduce((acc, node) => {
+ if (node.type === 'text') acc.push(node.value);
+ if (node.type === 'range') node.type = 'text';
+ return acc;
+}, []);
+
+/**
+ * Flatten an array
+ */
+
+exports.flatten = (...args) => {
+ const result = [];
+ const flat = arr => {
+ for (let i = 0; i < arr.length; i++) {
+ let ele = arr[i];
+ Array.isArray(ele) ? flat(ele, result) : ele !== void 0 && result.push(ele);
+ }
+ return result;
+ };
+ flat(args);
+ return result;
+};
+
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* WEBPACK VAR INJECTION */(function(process) {// .dirname, .basename, and .extname methods are extracted from Node.js v8.11.1,
+// backported and transplited with Babel, with backwards-compat fixes
+
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// resolves . and .. elements in a path array with directory names there
+// must be no slashes, empty elements, or device names (c:\) in the array
+// (so also no leading and trailing slashes - it does not distinguish
+// relative and absolute paths)
+function normalizeArray(parts, allowAboveRoot) {
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = parts.length - 1; i >= 0; i--) {
+ var last = parts[i];
+ if (last === '.') {
+ parts.splice(i, 1);
+ } else if (last === '..') {
+ parts.splice(i, 1);
+ up++;
+ } else if (up) {
+ parts.splice(i, 1);
+ up--;
+ }
+ }
+
+ // if the path is allowed to go above the root, restore leading ..s
+ if (allowAboveRoot) {
+ for (; up--; up) {
+ parts.unshift('..');
+ }
+ }
+
+ return parts;
+}
+
+// path.resolve([from ...], to)
+// posix version
+exports.resolve = function() {
+ var resolvedPath = '',
+ resolvedAbsolute = false;
+
+ for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
+ var path = (i >= 0) ? arguments[i] : process.cwd();
+
+ // Skip empty and invalid entries
+ if (typeof path !== 'string') {
+ throw new TypeError('Arguments to path.resolve must be strings');
+ } else if (!path) {
+ continue;
+ }
+
+ resolvedPath = path + '/' + resolvedPath;
+ resolvedAbsolute = path.charAt(0) === '/';
+ }
+
+ // At this point the path should be resolved to a full absolute path, but
+ // handle relative paths to be safe (might happen when process.cwd() fails)
+
+ // Normalize the path
+ resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
+ return !!p;
+ }), !resolvedAbsolute).join('/');
+
+ return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+};
+
+// path.normalize(path)
+// posix version
+exports.normalize = function(path) {
+ var isAbsolute = exports.isAbsolute(path),
+ trailingSlash = substr(path, -1) === '/';
+
+ // Normalize the path
+ path = normalizeArray(filter(path.split('/'), function(p) {
+ return !!p;
+ }), !isAbsolute).join('/');
+
+ if (!path && !isAbsolute) {
+ path = '.';
+ }
+ if (path && trailingSlash) {
+ path += '/';
+ }
+
+ return (isAbsolute ? '/' : '') + path;
+};
+
+// posix version
+exports.isAbsolute = function(path) {
+ return path.charAt(0) === '/';
+};
+
+// posix version
+exports.join = function() {
+ var paths = Array.prototype.slice.call(arguments, 0);
+ return exports.normalize(filter(paths, function(p, index) {
+ if (typeof p !== 'string') {
+ throw new TypeError('Arguments to path.join must be strings');
+ }
+ return p;
+ }).join('/'));
+};
+
+
+// path.relative(from, to)
+// posix version
+exports.relative = function(from, to) {
+ from = exports.resolve(from).substr(1);
+ to = exports.resolve(to).substr(1);
+
+ function trim(arr) {
+ var start = 0;
+ for (; start < arr.length; start++) {
+ if (arr[start] !== '') break;
+ }
+
+ var end = arr.length - 1;
+ for (; end >= 0; end--) {
+ if (arr[end] !== '') break;
+ }
+
+ if (start > end) return [];
+ return arr.slice(start, end - start + 1);
+ }
+
+ var fromParts = trim(from.split('/'));
+ var toParts = trim(to.split('/'));
+
+ var length = Math.min(fromParts.length, toParts.length);
+ var samePartsLength = length;
+ for (var i = 0; i < length; i++) {
+ if (fromParts[i] !== toParts[i]) {
+ samePartsLength = i;
+ break;
+ }
+ }
+
+ var outputParts = [];
+ for (var i = samePartsLength; i < fromParts.length; i++) {
+ outputParts.push('..');
+ }
+
+ outputParts = outputParts.concat(toParts.slice(samePartsLength));
+
+ return outputParts.join('/');
+};
+
+exports.sep = '/';
+exports.delimiter = ':';
+
+exports.dirname = function (path) {
+ if (typeof path !== 'string') path = path + '';
+ if (path.length === 0) return '.';
+ var code = path.charCodeAt(0);
+ var hasRoot = code === 47 /*/*/;
+ var end = -1;
+ var matchedSlash = true;
+ for (var i = path.length - 1; i >= 1; --i) {
+ code = path.charCodeAt(i);
+ if (code === 47 /*/*/) {
+ if (!matchedSlash) {
+ end = i;
+ break;
+ }
+ } else {
+ // We saw the first non-path separator
+ matchedSlash = false;
+ }
+ }
+
+ if (end === -1) return hasRoot ? '/' : '.';
+ if (hasRoot && end === 1) {
+ // return '//';
+ // Backwards-compat fix:
+ return '/';
+ }
+ return path.slice(0, end);
+};
+
+function basename(path) {
+ if (typeof path !== 'string') path = path + '';
+
+ var start = 0;
+ var end = -1;
+ var matchedSlash = true;
+ var i;
+
+ for (i = path.length - 1; i >= 0; --i) {
+ if (path.charCodeAt(i) === 47 /*/*/) {
+ // If we reached a path separator that was not part of a set of path
+ // separators at the end of the string, stop now
+ if (!matchedSlash) {
+ start = i + 1;
+ break;
+ }
+ } else if (end === -1) {
+ // We saw the first non-path separator, mark this as the end of our
+ // path component
+ matchedSlash = false;
+ end = i + 1;
+ }
+ }
+
+ if (end === -1) return '';
+ return path.slice(start, end);
+}
+
+// Uses a mixed approach for backwards-compatibility, as ext behavior changed
+// in new Node.js versions, so only basename() above is backported here
+exports.basename = function (path, ext) {
+ var f = basename(path);
+ if (ext && f.substr(-1 * ext.length) === ext) {
+ f = f.substr(0, f.length - ext.length);
+ }
+ return f;
+};
+
+exports.extname = function (path) {
+ if (typeof path !== 'string') path = path + '';
+ var startDot = -1;
+ var startPart = 0;
+ var end = -1;
+ var matchedSlash = true;
+ // Track the state of characters (if any) we see before our first dot and
+ // after any path separator we find
+ var preDotState = 0;
+ for (var i = path.length - 1; i >= 0; --i) {
+ var code = path.charCodeAt(i);
+ if (code === 47 /*/*/) {
+ // If we reached a path separator that was not part of a set of path
+ // separators at the end of the string, stop now
+ if (!matchedSlash) {
+ startPart = i + 1;
+ break;
+ }
+ continue;
+ }
+ if (end === -1) {
+ // We saw the first non-path separator, mark this as the end of our
+ // extension
+ matchedSlash = false;
+ end = i + 1;
+ }
+ if (code === 46 /*.*/) {
+ // If this is our first dot, mark it as the start of our extension
+ if (startDot === -1)
+ startDot = i;
+ else if (preDotState !== 1)
+ preDotState = 1;
+ } else if (startDot !== -1) {
+ // We saw a non-dot and non-path separator before our dot, so we should
+ // have a good chance at having a non-empty extension
+ preDotState = -1;
+ }
+ }
+
+ if (startDot === -1 || end === -1 ||
+ // We saw a non-dot character immediately before the dot
+ preDotState === 0 ||
+ // The (right-most) trimmed path component is exactly '..'
+ preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) {
+ return '';
+ }
+ return path.slice(startDot, end);
+};
+
+function filter (xs, f) {
+ if (xs.filter) return xs.filter(f);
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ if (f(xs[i], i, xs)) res.push(xs[i]);
+ }
+ return res;
+}
+
+// String.prototype.substr - negative index don't work in IE8
+var substr = 'ab'.substr(-1) === 'b'
+ ? function (str, start, len) { return str.substr(start, len) }
+ : function (str, start, len) {
+ if (start < 0) start = str.length + start;
+ return str.substr(start, len);
+ }
+;
+
+/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2)))
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* WEBPACK VAR INJECTION */(function(process) {// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors ||
+ function getOwnPropertyDescriptors(obj) {
+ var keys = Object.keys(obj);
+ var descriptors = {};
+ for (var i = 0; i < keys.length; i++) {
+ descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]);
+ }
+ return descriptors;
+ };
+
+var formatRegExp = /%[sdj%]/g;
+exports.format = function(f) {
+ if (!isString(f)) {
+ var objects = [];
+ for (var i = 0; i < arguments.length; i++) {
+ objects.push(inspect(arguments[i]));
+ }
+ return objects.join(' ');
+ }
+
+ var i = 1;
+ var args = arguments;
+ var len = args.length;
+ var str = String(f).replace(formatRegExp, function(x) {
+ if (x === '%%') return '%';
+ if (i >= len) return x;
+ switch (x) {
+ case '%s': return String(args[i++]);
+ case '%d': return Number(args[i++]);
+ case '%j':
+ try {
+ return JSON.stringify(args[i++]);
+ } catch (_) {
+ return '[Circular]';
+ }
+ default:
+ return x;
+ }
+ });
+ for (var x = args[i]; i < len; x = args[++i]) {
+ if (isNull(x) || !isObject(x)) {
+ str += ' ' + x;
+ } else {
+ str += ' ' + inspect(x);
+ }
+ }
+ return str;
+};
+
+
+// Mark that a method should not be used.
+// Returns a modified function which warns once by default.
+// If --no-deprecation is set, then it is a no-op.
+exports.deprecate = function(fn, msg) {
+ if (typeof process !== 'undefined' && process.noDeprecation === true) {
+ return fn;
+ }
+
+ // Allow for deprecating things in the process of starting up.
+ if (typeof process === 'undefined') {
+ return function() {
+ return exports.deprecate(fn, msg).apply(this, arguments);
+ };
+ }
+
+ var warned = false;
+ function deprecated() {
+ if (!warned) {
+ if (process.throwDeprecation) {
+ throw new Error(msg);
+ } else if (process.traceDeprecation) {
+ console.trace(msg);
+ } else {
+ console.error(msg);
+ }
+ warned = true;
+ }
+ return fn.apply(this, arguments);
+ }
+
+ return deprecated;
+};
+
+
+var debugs = {};
+var debugEnviron;
+exports.debuglog = function(set) {
+ if (isUndefined(debugEnviron))
+ debugEnviron = process.env.NODE_DEBUG || '';
+ set = set.toUpperCase();
+ if (!debugs[set]) {
+ if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
+ var pid = process.pid;
+ debugs[set] = function() {
+ var msg = exports.format.apply(exports, arguments);
+ console.error('%s %d: %s', set, pid, msg);
+ };
+ } else {
+ debugs[set] = function() {};
+ }
+ }
+ return debugs[set];
+};
+
+
+/**
+ * Echos the value of a value. Trys to print the value out
+ * in the best way possible given the different types.
+ *
+ * @param {Object} obj The object to print out.
+ * @param {Object} opts Optional options object that alters the output.
+ */
+/* legacy: obj, showHidden, depth, colors*/
+function inspect(obj, opts) {
+ // default options
+ var ctx = {
+ seen: [],
+ stylize: stylizeNoColor
+ };
+ // legacy...
+ if (arguments.length >= 3) ctx.depth = arguments[2];
+ if (arguments.length >= 4) ctx.colors = arguments[3];
+ if (isBoolean(opts)) {
+ // legacy...
+ ctx.showHidden = opts;
+ } else if (opts) {
+ // got an "options" object
+ exports._extend(ctx, opts);
+ }
+ // set default options
+ if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
+ if (isUndefined(ctx.depth)) ctx.depth = 2;
+ if (isUndefined(ctx.colors)) ctx.colors = false;
+ if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
+ if (ctx.colors) ctx.stylize = stylizeWithColor;
+ return formatValue(ctx, obj, ctx.depth);
+}
+exports.inspect = inspect;
+
+
+// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
+inspect.colors = {
+ 'bold' : [1, 22],
+ 'italic' : [3, 23],
+ 'underline' : [4, 24],
+ 'inverse' : [7, 27],
+ 'white' : [37, 39],
+ 'grey' : [90, 39],
+ 'black' : [30, 39],
+ 'blue' : [34, 39],
+ 'cyan' : [36, 39],
+ 'green' : [32, 39],
+ 'magenta' : [35, 39],
+ 'red' : [31, 39],
+ 'yellow' : [33, 39]
+};
+
+// Don't use 'blue' not visible on cmd.exe
+inspect.styles = {
+ 'special': 'cyan',
+ 'number': 'yellow',
+ 'boolean': 'yellow',
+ 'undefined': 'grey',
+ 'null': 'bold',
+ 'string': 'green',
+ 'date': 'magenta',
+ // "name": intentionally not styling
+ 'regexp': 'red'
+};
+
+
+function stylizeWithColor(str, styleType) {
+ var style = inspect.styles[styleType];
+
+ if (style) {
+ return '\u001b[' + inspect.colors[style][0] + 'm' + str +
+ '\u001b[' + inspect.colors[style][1] + 'm';
+ } else {
+ return str;
+ }
+}
+
+
+function stylizeNoColor(str, styleType) {
+ return str;
+}
+
+
+function arrayToHash(array) {
+ var hash = {};
+
+ array.forEach(function(val, idx) {
+ hash[val] = true;
+ });
+
+ return hash;
+}
+
+
+function formatValue(ctx, value, recurseTimes) {
+ // Provide a hook for user-specified inspect functions.
+ // Check that value is an object with an inspect function on it
+ if (ctx.customInspect &&
+ value &&
+ isFunction(value.inspect) &&
+ // Filter out the util module, it's inspect function is special
+ value.inspect !== exports.inspect &&
+ // Also filter out any prototype objects using the circular check.
+ !(value.constructor && value.constructor.prototype === value)) {
+ var ret = value.inspect(recurseTimes, ctx);
+ if (!isString(ret)) {
+ ret = formatValue(ctx, ret, recurseTimes);
+ }
+ return ret;
+ }
+
+ // Primitive types cannot have properties
+ var primitive = formatPrimitive(ctx, value);
+ if (primitive) {
+ return primitive;
+ }
+
+ // Look up the keys of the object.
+ var keys = Object.keys(value);
+ var visibleKeys = arrayToHash(keys);
+
+ if (ctx.showHidden) {
+ keys = Object.getOwnPropertyNames(value);
+ }
+
+ // IE doesn't make error fields non-enumerable
+ // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
+ if (isError(value)
+ && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
+ return formatError(value);
+ }
+
+ // Some type of object without properties can be shortcutted.
+ if (keys.length === 0) {
+ if (isFunction(value)) {
+ var name = value.name ? ': ' + value.name : '';
+ return ctx.stylize('[Function' + name + ']', 'special');
+ }
+ if (isRegExp(value)) {
+ return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+ }
+ if (isDate(value)) {
+ return ctx.stylize(Date.prototype.toString.call(value), 'date');
+ }
+ if (isError(value)) {
+ return formatError(value);
+ }
+ }
+
+ var base = '', array = false, braces = ['{', '}'];
+
+ // Make Array say that they are Array
+ if (isArray(value)) {
+ array = true;
+ braces = ['[', ']'];
+ }
+
+ // Make functions say that they are functions
+ if (isFunction(value)) {
+ var n = value.name ? ': ' + value.name : '';
+ base = ' [Function' + n + ']';
+ }
+
+ // Make RegExps say that they are RegExps
+ if (isRegExp(value)) {
+ base = ' ' + RegExp.prototype.toString.call(value);
+ }
+
+ // Make dates with properties first say the date
+ if (isDate(value)) {
+ base = ' ' + Date.prototype.toUTCString.call(value);
+ }
+
+ // Make error with message first say the error
+ if (isError(value)) {
+ base = ' ' + formatError(value);
+ }
+
+ if (keys.length === 0 && (!array || value.length == 0)) {
+ return braces[0] + base + braces[1];
+ }
+
+ if (recurseTimes < 0) {
+ if (isRegExp(value)) {
+ return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+ } else {
+ return ctx.stylize('[Object]', 'special');
+ }
+ }
+
+ ctx.seen.push(value);
+
+ var output;
+ if (array) {
+ output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
+ } else {
+ output = keys.map(function(key) {
+ return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
+ });
+ }
+
+ ctx.seen.pop();
+
+ return reduceToSingleString(output, base, braces);
+}
+
+
+function formatPrimitive(ctx, value) {
+ if (isUndefined(value))
+ return ctx.stylize('undefined', 'undefined');
+ if (isString(value)) {
+ var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
+ .replace(/'/g, "\\'")
+ .replace(/\\"/g, '"') + '\'';
+ return ctx.stylize(simple, 'string');
+ }
+ if (isNumber(value))
+ return ctx.stylize('' + value, 'number');
+ if (isBoolean(value))
+ return ctx.stylize('' + value, 'boolean');
+ // For some reason typeof null is "object", so special case here.
+ if (isNull(value))
+ return ctx.stylize('null', 'null');
+}
+
+
+function formatError(value) {
+ return '[' + Error.prototype.toString.call(value) + ']';
+}
+
+
+function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
+ var output = [];
+ for (var i = 0, l = value.length; i < l; ++i) {
+ if (hasOwnProperty(value, String(i))) {
+ output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+ String(i), true));
+ } else {
+ output.push('');
+ }
+ }
+ keys.forEach(function(key) {
+ if (!key.match(/^\d+$/)) {
+ output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+ key, true));
+ }
+ });
+ return output;
+}
+
+
+function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
+ var name, str, desc;
+ desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
+ if (desc.get) {
+ if (desc.set) {
+ str = ctx.stylize('[Getter/Setter]', 'special');
+ } else {
+ str = ctx.stylize('[Getter]', 'special');
+ }
+ } else {
+ if (desc.set) {
+ str = ctx.stylize('[Setter]', 'special');
+ }
+ }
+ if (!hasOwnProperty(visibleKeys, key)) {
+ name = '[' + key + ']';
+ }
+ if (!str) {
+ if (ctx.seen.indexOf(desc.value) < 0) {
+ if (isNull(recurseTimes)) {
+ str = formatValue(ctx, desc.value, null);
+ } else {
+ str = formatValue(ctx, desc.value, recurseTimes - 1);
+ }
+ if (str.indexOf('\n') > -1) {
+ if (array) {
+ str = str.split('\n').map(function(line) {
+ return ' ' + line;
+ }).join('\n').substr(2);
+ } else {
+ str = '\n' + str.split('\n').map(function(line) {
+ return ' ' + line;
+ }).join('\n');
+ }
+ }
+ } else {
+ str = ctx.stylize('[Circular]', 'special');
+ }
+ }
+ if (isUndefined(name)) {
+ if (array && key.match(/^\d+$/)) {
+ return str;
+ }
+ name = JSON.stringify('' + key);
+ if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
+ name = name.substr(1, name.length - 2);
+ name = ctx.stylize(name, 'name');
+ } else {
+ name = name.replace(/'/g, "\\'")
+ .replace(/\\"/g, '"')
+ .replace(/(^"|"$)/g, "'");
+ name = ctx.stylize(name, 'string');
+ }
+ }
+
+ return name + ': ' + str;
+}
+
+
+function reduceToSingleString(output, base, braces) {
+ var numLinesEst = 0;
+ var length = output.reduce(function(prev, cur) {
+ numLinesEst++;
+ if (cur.indexOf('\n') >= 0) numLinesEst++;
+ return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
+ }, 0);
+
+ if (length > 60) {
+ return braces[0] +
+ (base === '' ? '' : base + '\n ') +
+ ' ' +
+ output.join(',\n ') +
+ ' ' +
+ braces[1];
+ }
+
+ return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
+}
+
+
+// NOTE: These type checking functions intentionally don't use `instanceof`
+// because it is fragile and can be easily faked with `Object.create()`.
+function isArray(ar) {
+ return Array.isArray(ar);
+}
+exports.isArray = isArray;
+
+function isBoolean(arg) {
+ return typeof arg === 'boolean';
+}
+exports.isBoolean = isBoolean;
+
+function isNull(arg) {
+ return arg === null;
+}
+exports.isNull = isNull;
+
+function isNullOrUndefined(arg) {
+ return arg == null;
+}
+exports.isNullOrUndefined = isNullOrUndefined;
+
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+exports.isNumber = isNumber;
+
+function isString(arg) {
+ return typeof arg === 'string';
+}
+exports.isString = isString;
+
+function isSymbol(arg) {
+ return typeof arg === 'symbol';
+}
+exports.isSymbol = isSymbol;
+
+function isUndefined(arg) {
+ return arg === void 0;
+}
+exports.isUndefined = isUndefined;
+
+function isRegExp(re) {
+ return isObject(re) && objectToString(re) === '[object RegExp]';
+}
+exports.isRegExp = isRegExp;
+
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+exports.isObject = isObject;
+
+function isDate(d) {
+ return isObject(d) && objectToString(d) === '[object Date]';
+}
+exports.isDate = isDate;
+
+function isError(e) {
+ return isObject(e) &&
+ (objectToString(e) === '[object Error]' || e instanceof Error);
+}
+exports.isError = isError;
+
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+exports.isFunction = isFunction;
+
+function isPrimitive(arg) {
+ return arg === null ||
+ typeof arg === 'boolean' ||
+ typeof arg === 'number' ||
+ typeof arg === 'string' ||
+ typeof arg === 'symbol' || // ES6 symbol
+ typeof arg === 'undefined';
+}
+exports.isPrimitive = isPrimitive;
+
+exports.isBuffer = __webpack_require__(9);
+
+function objectToString(o) {
+ return Object.prototype.toString.call(o);
+}
+
+
+function pad(n) {
+ return n < 10 ? '0' + n.toString(10) : n.toString(10);
+}
+
+
+var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
+ 'Oct', 'Nov', 'Dec'];
+
+// 26 Feb 16:19:34
+function timestamp() {
+ var d = new Date();
+ var time = [pad(d.getHours()),
+ pad(d.getMinutes()),
+ pad(d.getSeconds())].join(':');
+ return [d.getDate(), months[d.getMonth()], time].join(' ');
+}
+
+
+// log is just a thin wrapper to console.log that prepends a timestamp
+exports.log = function() {
+ console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
+};
+
+
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * The Function.prototype.inherits from lang.js rewritten as a standalone
+ * function (not on Function.prototype). NOTE: If this file is to be loaded
+ * during bootstrapping this function needs to be rewritten using some native
+ * functions as prototype setup using normal JavaScript does not work as
+ * expected during bootstrapping (see mirror.js in r114903).
+ *
+ * @param {function} ctor Constructor function which needs to inherit the
+ * prototype.
+ * @param {function} superCtor Constructor function to inherit prototype from.
+ */
+exports.inherits = __webpack_require__(10);
+
+exports._extend = function(origin, add) {
+ // Don't do anything if add isn't an object
+ if (!add || !isObject(add)) return origin;
+
+ var keys = Object.keys(add);
+ var i = keys.length;
+ while (i--) {
+ origin[keys[i]] = add[keys[i]];
+ }
+ return origin;
+};
+
+function hasOwnProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+var kCustomPromisifiedSymbol = typeof Symbol !== 'undefined' ? Symbol('util.promisify.custom') : undefined;
+
+exports.promisify = function promisify(original) {
+ if (typeof original !== 'function')
+ throw new TypeError('The "original" argument must be of type Function');
+
+ if (kCustomPromisifiedSymbol && original[kCustomPromisifiedSymbol]) {
+ var fn = original[kCustomPromisifiedSymbol];
+ if (typeof fn !== 'function') {
+ throw new TypeError('The "util.promisify.custom" argument must be of type Function');
+ }
+ Object.defineProperty(fn, kCustomPromisifiedSymbol, {
+ value: fn, enumerable: false, writable: false, configurable: true
+ });
+ return fn;
+ }
+
+ function fn() {
+ var promiseResolve, promiseReject;
+ var promise = new Promise(function (resolve, reject) {
+ promiseResolve = resolve;
+ promiseReject = reject;
+ });
+
+ var args = [];
+ for (var i = 0; i < arguments.length; i++) {
+ args.push(arguments[i]);
+ }
+ args.push(function (err, value) {
+ if (err) {
+ promiseReject(err);
+ } else {
+ promiseResolve(value);
+ }
+ });
+
+ try {
+ original.apply(this, args);
+ } catch (err) {
+ promiseReject(err);
+ }
+
+ return promise;
+ }
+
+ Object.setPrototypeOf(fn, Object.getPrototypeOf(original));
+
+ if (kCustomPromisifiedSymbol) Object.defineProperty(fn, kCustomPromisifiedSymbol, {
+ value: fn, enumerable: false, writable: false, configurable: true
+ });
+ return Object.defineProperties(
+ fn,
+ getOwnPropertyDescriptors(original)
+ );
+}
+
+exports.promisify.custom = kCustomPromisifiedSymbol
+
+function callbackifyOnRejected(reason, cb) {
+ // `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
+ // Because `null` is a special error value in callbacks which means "no error
+ // occurred", we error-wrap so the callback consumer can distinguish between
+ // "the promise rejected with null" or "the promise fulfilled with undefined".
+ if (!reason) {
+ var newReason = new Error('Promise was rejected with a falsy value');
+ newReason.reason = reason;
+ reason = newReason;
+ }
+ return cb(reason);
+}
+
+function callbackify(original) {
+ if (typeof original !== 'function') {
+ throw new TypeError('The "original" argument must be of type Function');
+ }
+
+ // We DO NOT return the promise as it gives the user a false sense that
+ // the promise is actually somehow related to the callback's execution
+ // and that the callback throwing will reject the promise.
+ function callbackified() {
+ var args = [];
+ for (var i = 0; i < arguments.length; i++) {
+ args.push(arguments[i]);
+ }
+
+ var maybeCb = args.pop();
+ if (typeof maybeCb !== 'function') {
+ throw new TypeError('The last argument must be of type Function');
+ }
+ var self = this;
+ var cb = function() {
+ return maybeCb.apply(self, arguments);
+ };
+ // In true node style we process the callback on `nextTick` with all the
+ // implications (stack, `uncaughtException`, `async_hooks`)
+ original.apply(this, args)
+ .then(function(ret) { process.nextTick(cb, null, ret) },
+ function(rej) { process.nextTick(callbackifyOnRejected, rej, cb) });
+ }
+
+ Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
+ Object.defineProperties(callbackified,
+ getOwnPropertyDescriptors(original));
+ return callbackified;
+}
+exports.callbackify = callbackify;
+
+/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(2)))
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*!
+ * fill-range <https://github.com/jonschlinkert/fill-range>
+ *
+ * Copyright (c) 2014-present, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+
+
+const util = __webpack_require__(6);
+const toRegexRange = __webpack_require__(13);
+
+const isObject = val => val !== null && typeof val === 'object' && !Array.isArray(val);
+
+const transform = toNumber => {
+ return value => toNumber === true ? Number(value) : String(value);
+};
+
+const isValidValue = value => {
+ return typeof value === 'number' || (typeof value === 'string' && value !== '');
+};
+
+const isNumber = num => Number.isInteger(+num);
+
+const zeros = input => {
+ let value = `${input}`;
+ let index = -1;
+ if (value[0] === '-') value = value.slice(1);
+ if (value === '0') return false;
+ while (value[++index] === '0');
+ return index > 0;
+};
+
+const stringify = (start, end, options) => {
+ if (typeof start === 'string' || typeof end === 'string') {
+ return true;
+ }
+ return options.stringify === true;
+};
+
+const pad = (input, maxLength, toNumber) => {
+ if (maxLength > 0) {
+ let dash = input[0] === '-' ? '-' : '';
+ if (dash) input = input.slice(1);
+ input = (dash + input.padStart(dash ? maxLength - 1 : maxLength, '0'));
+ }
+ if (toNumber === false) {
+ return String(input);
+ }
+ return input;
+};
+
+const toMaxLen = (input, maxLength) => {
+ let negative = input[0] === '-' ? '-' : '';
+ if (negative) {
+ input = input.slice(1);
+ maxLength--;
+ }
+ while (input.length < maxLength) input = '0' + input;
+ return negative ? ('-' + input) : input;
+};
+
+const toSequence = (parts, options) => {
+ parts.negatives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
+ parts.positives.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
+
+ let prefix = options.capture ? '' : '?:';
+ let positives = '';
+ let negatives = '';
+ let result;
+
+ if (parts.positives.length) {
+ positives = parts.positives.join('|');
+ }
+
+ if (parts.negatives.length) {
+ negatives = `-(${prefix}${parts.negatives.join('|')})`;
+ }
+
+ if (positives && negatives) {
+ result = `${positives}|${negatives}`;
+ } else {
+ result = positives || negatives;
+ }
+
+ if (options.wrap) {
+ return `(${prefix}${result})`;
+ }
+
+ return result;
+};
+
+const toRange = (a, b, isNumbers, options) => {
+ if (isNumbers) {
+ return toRegexRange(a, b, { wrap: false, ...options });
+ }
+
+ let start = String.fromCharCode(a);
+ if (a === b) return start;
+
+ let stop = String.fromCharCode(b);
+ return `[${start}-${stop}]`;
+};
+
+const toRegex = (start, end, options) => {
+ if (Array.isArray(start)) {
+ let wrap = options.wrap === true;
+ let prefix = options.capture ? '' : '?:';
+ return wrap ? `(${prefix}${start.join('|')})` : start.join('|');
+ }
+ return toRegexRange(start, end, options);
+};
+
+const rangeError = (...args) => {
+ return new RangeError('Invalid range arguments: ' + util.inspect(...args));
+};
+
+const invalidRange = (start, end, options) => {
+ if (options.strictRanges === true) throw rangeError([start, end]);
+ return [];
+};
+
+const invalidStep = (step, options) => {
+ if (options.strictRanges === true) {
+ throw new TypeError(`Expected step "${step}" to be a number`);
+ }
+ return [];
+};
+
+const fillNumbers = (start, end, step = 1, options = {}) => {
+ let a = Number(start);
+ let b = Number(end);
+
+ if (!Number.isInteger(a) || !Number.isInteger(b)) {
+ if (options.strictRanges === true) throw rangeError([start, end]);
+ return [];
+ }
+
+ // fix negative zero
+ if (a === 0) a = 0;
+ if (b === 0) b = 0;
+
+ let descending = a > b;
+ let startString = String(start);
+ let endString = String(end);
+ let stepString = String(step);
+ step = Math.max(Math.abs(step), 1);
+
+ let padded = zeros(startString) || zeros(endString) || zeros(stepString);
+ let maxLen = padded ? Math.max(startString.length, endString.length, stepString.length) : 0;
+ let toNumber = padded === false && stringify(start, end, options) === false;
+ let format = options.transform || transform(toNumber);
+
+ if (options.toRegex && step === 1) {
+ return toRange(toMaxLen(start, maxLen), toMaxLen(end, maxLen), true, options);
+ }
+
+ let parts = { negatives: [], positives: [] };
+ let push = num => parts[num < 0 ? 'negatives' : 'positives'].push(Math.abs(num));
+ let range = [];
+ let index = 0;
+
+ while (descending ? a >= b : a <= b) {
+ if (options.toRegex === true && step > 1) {
+ push(a);
+ } else {
+ range.push(pad(format(a, index), maxLen, toNumber));
+ }
+ a = descending ? a - step : a + step;
+ index++;
+ }
+
+ if (options.toRegex === true) {
+ return step > 1
+ ? toSequence(parts, options)
+ : toRegex(range, null, { wrap: false, ...options });
+ }
+
+ return range;
+};
+
+const fillLetters = (start, end, step = 1, options = {}) => {
+ if ((!isNumber(start) && start.length > 1) || (!isNumber(end) && end.length > 1)) {
+ return invalidRange(start, end, options);
+ }
+
+
+ let format = options.transform || (val => String.fromCharCode(val));
+ let a = `${start}`.charCodeAt(0);
+ let b = `${end}`.charCodeAt(0);
+
+ let descending = a > b;
+ let min = Math.min(a, b);
+ let max = Math.max(a, b);
+
+ if (options.toRegex && step === 1) {
+ return toRange(min, max, false, options);
+ }
+
+ let range = [];
+ let index = 0;
+
+ while (descending ? a >= b : a <= b) {
+ range.push(format(a, index));
+ a = descending ? a - step : a + step;
+ index++;
+ }
+
+ if (options.toRegex === true) {
+ return toRegex(range, null, { wrap: false, options });
+ }
+
+ return range;
+};
+
+const fill = (start, end, step, options = {}) => {
+ if (end == null && isValidValue(start)) {
+ return [start];
+ }
+
+ if (!isValidValue(start) || !isValidValue(end)) {
+ return invalidRange(start, end, options);
+ }
+
+ if (typeof step === 'function') {
+ return fill(start, end, 1, { transform: step });
+ }
+
+ if (isObject(step)) {
+ return fill(start, end, 0, step);
+ }
+
+ let opts = { ...options };
+ if (opts.capture === true) opts.wrap = true;
+ step = step || opts.step || 1;
+
+ if (!isNumber(step)) {
+ if (step != null && !isObject(step)) return invalidStep(step, opts);
+ return fill(start, end, 1, step);
+ }
+
+ if (isNumber(start) && isNumber(end)) {
+ return fillNumbers(start, end, step, opts);
+ }
+
+ return fillLetters(start, end, Math.max(Math.abs(step), 1), opts);
+};
+
+module.exports = fill;
+
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const util = __webpack_require__(6);
+const braces = __webpack_require__(11);
+const picomatch = __webpack_require__(18);
+const utils = __webpack_require__(0);
+const isEmptyString = val => val === '' || val === './';
+
+/**
+ * Returns an array of strings that match one or more glob patterns.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm(list, patterns[, options]);
+ *
+ * console.log(mm(['a.js', 'a.txt'], ['*.js']));
+ * //=> [ 'a.js' ]
+ * ```
+ * @param {String|Array<string>} `list` List of strings to match.
+ * @param {String|Array<string>} `patterns` One or more glob patterns to use for matching.
+ * @param {Object} `options` See available [options](#options)
+ * @return {Array} Returns an array of matches
+ * @summary false
+ * @api public
+ */
+
+const micromatch = (list, patterns, options) => {
+ patterns = [].concat(patterns);
+ list = [].concat(list);
+
+ let omit = new Set();
+ let keep = new Set();
+ let items = new Set();
+ let negatives = 0;
+
+ let onResult = state => {
+ items.add(state.output);
+ if (options && options.onResult) {
+ options.onResult(state);
+ }
+ };
+
+ for (let i = 0; i < patterns.length; i++) {
+ let isMatch = picomatch(String(patterns[i]), { ...options, onResult }, true);
+ let negated = isMatch.state.negated || isMatch.state.negatedExtglob;
+ if (negated) negatives++;
+
+ for (let item of list) {
+ let matched = isMatch(item, true);
+
+ let match = negated ? !matched.isMatch : matched.isMatch;
+ if (!match) continue;
+
+ if (negated) {
+ omit.add(matched.output);
+ } else {
+ omit.delete(matched.output);
+ keep.add(matched.output);
+ }
+ }
+ }
+
+ let result = negatives === patterns.length ? [...items] : [...keep];
+ let matches = result.filter(item => !omit.has(item));
+
+ if (options && matches.length === 0) {
+ if (options.failglob === true) {
+ throw new Error(`No matches found for "${patterns.join(', ')}"`);
+ }
+
+ if (options.nonull === true || options.nullglob === true) {
+ return options.unescape ? patterns.map(p => p.replace(/\\/g, '')) : patterns;
+ }
+ }
+
+ return matches;
+};
+
+/**
+ * Backwards compatibility
+ */
+
+micromatch.match = micromatch;
+
+/**
+ * Returns a matcher function from the given glob `pattern` and `options`.
+ * The returned function takes a string to match as its only argument and returns
+ * true if the string is a match.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.matcher(pattern[, options]);
+ *
+ * const isMatch = mm.matcher('*.!(*a)');
+ * console.log(isMatch('a.a')); //=> false
+ * console.log(isMatch('a.b')); //=> true
+ * ```
+ * @param {String} `pattern` Glob pattern
+ * @param {Object} `options`
+ * @return {Function} Returns a matcher function.
+ * @api public
+ */
+
+micromatch.matcher = (pattern, options) => picomatch(pattern, options);
+
+/**
+ * Returns true if **any** of the given glob `patterns` match the specified `string`.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.isMatch(string, patterns[, options]);
+ *
+ * console.log(mm.isMatch('a.a', ['b.*', '*.a'])); //=> true
+ * console.log(mm.isMatch('a.a', 'b.*')); //=> false
+ * ```
+ * @param {String} `str` The string to test.
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
+ * @param {Object} `[options]` See available [options](#options).
+ * @return {Boolean} Returns true if any patterns match `str`
+ * @api public
+ */
+
+micromatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
+
+/**
+ * Backwards compatibility
+ */
+
+micromatch.any = micromatch.isMatch;
+
+/**
+ * Returns a list of strings that _**do not match any**_ of the given `patterns`.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.not(list, patterns[, options]);
+ *
+ * console.log(mm.not(['a.a', 'b.b', 'c.c'], '*.a'));
+ * //=> ['b.b', 'c.c']
+ * ```
+ * @param {Array} `list` Array of strings to match.
+ * @param {String|Array} `patterns` One or more glob pattern to use for matching.
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
+ * @return {Array} Returns an array of strings that **do not match** the given patterns.
+ * @api public
+ */
+
+micromatch.not = (list, patterns, options = {}) => {
+ patterns = [].concat(patterns).map(String);
+ let result = new Set();
+ let items = [];
+
+ let onResult = state => {
+ if (options.onResult) options.onResult(state);
+ items.push(state.output);
+ };
+
+ let matches = new Set(micromatch(list, patterns, { ...options, onResult }));
+
+ for (let item of items) {
+ if (!matches.has(item)) {
+ result.add(item);
+ }
+ }
+ return [...result];
+};
+
+/**
+ * Returns true if the given `string` contains the given pattern. Similar
+ * to [.isMatch](#isMatch) but the pattern can match any part of the string.
+ *
+ * ```js
+ * var mm = require('micromatch');
+ * // mm.contains(string, pattern[, options]);
+ *
+ * console.log(mm.contains('aa/bb/cc', '*b'));
+ * //=> true
+ * console.log(mm.contains('aa/bb/cc', '*d'));
+ * //=> false
+ * ```
+ * @param {String} `str` The string to match.
+ * @param {String|Array} `patterns` Glob pattern to use for matching.
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
+ * @return {Boolean} Returns true if any of the patterns matches any part of `str`.
+ * @api public
+ */
+
+micromatch.contains = (str, pattern, options) => {
+ if (typeof str !== 'string') {
+ throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
+ }
+
+ if (Array.isArray(pattern)) {
+ return pattern.some(p => micromatch.contains(str, p, options));
+ }
+
+ if (typeof pattern === 'string') {
+ if (isEmptyString(str) || isEmptyString(pattern)) {
+ return false;
+ }
+
+ if (str.includes(pattern) || (str.startsWith('./') && str.slice(2).includes(pattern))) {
+ return true;
+ }
+ }
+
+ return micromatch.isMatch(str, pattern, { ...options, contains: true });
+};
+
+/**
+ * Filter the keys of the given object with the given `glob` pattern
+ * and `options`. Does not attempt to match nested keys. If you need this feature,
+ * use [glob-object][] instead.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.matchKeys(object, patterns[, options]);
+ *
+ * const obj = { aa: 'a', ab: 'b', ac: 'c' };
+ * console.log(mm.matchKeys(obj, '*b'));
+ * //=> { ab: 'b' }
+ * ```
+ * @param {Object} `object` The object with keys to filter.
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
+ * @return {Object} Returns an object with only keys that match the given patterns.
+ * @api public
+ */
+
+micromatch.matchKeys = (obj, patterns, options) => {
+ if (!utils.isObject(obj)) {
+ throw new TypeError('Expected the first argument to be an object');
+ }
+ let keys = micromatch(Object.keys(obj), patterns, options);
+ let res = {};
+ for (let key of keys) res[key] = obj[key];
+ return res;
+};
+
+/**
+ * Returns true if some of the strings in the given `list` match any of the given glob `patterns`.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.some(list, patterns[, options]);
+ *
+ * console.log(mm.some(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
+ * // true
+ * console.log(mm.some(['foo.js'], ['*.js', '!foo.js']));
+ * // false
+ * ```
+ * @param {String|Array} `list` The string or array of strings to test. Returns as soon as the first match is found.
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
+ * @return {Boolean} Returns true if any `patterns` matches any of the strings in `list`
+ * @api public
+ */
+
+micromatch.some = (list, patterns, options) => {
+ let items = [].concat(list);
+
+ for (let pattern of [].concat(patterns)) {
+ let isMatch = picomatch(String(pattern), options);
+ if (items.some(item => isMatch(item))) {
+ return true;
+ }
+ }
+ return false;
+};
+
+/**
+ * Returns true if every string in the given `list` matches
+ * any of the given glob `patterns`.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.every(list, patterns[, options]);
+ *
+ * console.log(mm.every('foo.js', ['foo.js']));
+ * // true
+ * console.log(mm.every(['foo.js', 'bar.js'], ['*.js']));
+ * // true
+ * console.log(mm.every(['foo.js', 'bar.js'], ['*.js', '!foo.js']));
+ * // false
+ * console.log(mm.every(['foo.js'], ['*.js', '!foo.js']));
+ * // false
+ * ```
+ * @param {String|Array} `list` The string or array of strings to test.
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
+ * @return {Boolean} Returns true if all `patterns` matches all of the strings in `list`
+ * @api public
+ */
+
+micromatch.every = (list, patterns, options) => {
+ let items = [].concat(list);
+
+ for (let pattern of [].concat(patterns)) {
+ let isMatch = picomatch(String(pattern), options);
+ if (!items.every(item => isMatch(item))) {
+ return false;
+ }
+ }
+ return true;
+};
+
+/**
+ * Returns true if **all** of the given `patterns` match
+ * the specified string.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.all(string, patterns[, options]);
+ *
+ * console.log(mm.all('foo.js', ['foo.js']));
+ * // true
+ *
+ * console.log(mm.all('foo.js', ['*.js', '!foo.js']));
+ * // false
+ *
+ * console.log(mm.all('foo.js', ['*.js', 'foo.js']));
+ * // true
+ *
+ * console.log(mm.all('foo.js', ['*.js', 'f*', '*o*', '*o.js']));
+ * // true
+ * ```
+ * @param {String|Array} `str` The string to test.
+ * @param {String|Array} `patterns` One or more glob patterns to use for matching.
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
+ * @return {Boolean} Returns true if any patterns match `str`
+ * @api public
+ */
+
+micromatch.all = (str, patterns, options) => {
+ if (typeof str !== 'string') {
+ throw new TypeError(`Expected a string: "${util.inspect(str)}"`);
+ }
+
+ return [].concat(patterns).every(p => picomatch(p, options)(str));
+};
+
+/**
+ * Returns an array of matches captured by `pattern` in `string, or `null` if the pattern did not match.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.capture(pattern, string[, options]);
+ *
+ * console.log(mm.capture('test/*.js', 'test/foo.js'));
+ * //=> ['foo']
+ * console.log(mm.capture('test/*.js', 'foo/bar.css'));
+ * //=> null
+ * ```
+ * @param {String} `glob` Glob pattern to use for matching.
+ * @param {String} `input` String to match
+ * @param {Object} `options` See available [options](#options) for changing how matches are performed
+ * @return {Array|null} Returns an array of captures if the input matches the glob pattern, otherwise `null`.
+ * @api public
+ */
+
+micromatch.capture = (glob, input, options) => {
+ let posix = utils.isWindows(options);
+ let regex = picomatch.makeRe(String(glob), { ...options, capture: true });
+ let match = regex.exec(posix ? utils.toPosixSlashes(input) : input);
+
+ if (match) {
+ return match.slice(1).map(v => v === void 0 ? '' : v);
+ }
+};
+
+/**
+ * Create a regular expression from the given glob `pattern`.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * // mm.makeRe(pattern[, options]);
+ *
+ * console.log(mm.makeRe('*.js'));
+ * //=> /^(?:(\.[\\\/])?(?!\.)(?=.)[^\/]*?\.js)$/
+ * ```
+ * @param {String} `pattern` A glob pattern to convert to regex.
+ * @param {Object} `options`
+ * @return {RegExp} Returns a regex created from the given pattern.
+ * @api public
+ */
+
+micromatch.makeRe = (...args) => picomatch.makeRe(...args);
+
+/**
+ * Scan a glob pattern to separate the pattern into segments. Used
+ * by the [split](#split) method.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * const state = mm.scan(pattern[, options]);
+ * ```
+ * @param {String} `pattern`
+ * @param {Object} `options`
+ * @return {Object} Returns an object with
+ * @api public
+ */
+
+micromatch.scan = (...args) => picomatch.scan(...args);
+
+/**
+ * Parse a glob pattern to create the source string for a regular
+ * expression.
+ *
+ * ```js
+ * const mm = require('micromatch');
+ * const state = mm.parse(pattern[, options]);
+ * ```
+ * @param {String} `glob`
+ * @param {Object} `options`
+ * @return {Object} Returns an object with useful properties and output to be used as regex source string.
+ * @api public
+ */
+
+micromatch.parse = (patterns, options) => {
+ let res = [];
+ for (let pattern of [].concat(patterns || [])) {
+ for (let str of braces(String(pattern), options)) {
+ res.push(picomatch.parse(str, options));
+ }
+ }
+ return res;
+};
+
+/**
+ * Process the given brace `pattern`.
+ *
+ * ```js
+ * const { braces } = require('micromatch');
+ * console.log(braces('foo/{a,b,c}/bar'));
+ * //=> [ 'foo/(a|b|c)/bar' ]
+ *
+ * console.log(braces('foo/{a,b,c}/bar', { expand: true }));
+ * //=> [ 'foo/a/bar', 'foo/b/bar', 'foo/c/bar' ]
+ * ```
+ * @param {String} `pattern` String with brace pattern to process.
+ * @param {Object} `options` Any [options](#options) to change how expansion is performed. See the [braces][] library for all available options.
+ * @return {Array}
+ * @api public
+ */
+
+micromatch.braces = (pattern, options) => {
+ if (typeof pattern !== 'string') throw new TypeError('Expected a string');
+ if ((options && options.nobrace === true) || !/\{.*\}/.test(pattern)) {
+ return [pattern];
+ }
+ return braces(pattern, options);
+};
+
+/**
+ * Expand braces
+ */
+
+micromatch.braceExpand = (pattern, options) => {
+ if (typeof pattern !== 'string') throw new TypeError('Expected a string');
+ return micromatch.braces(pattern, { ...options, expand: true });
+};
+
+/**
+ * Expose micromatch
+ */
+
+module.exports = micromatch;
+
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports) {
+
+module.exports = function isBuffer(arg) {
+ return arg && typeof arg === 'object'
+ && typeof arg.copy === 'function'
+ && typeof arg.fill === 'function'
+ && typeof arg.readUInt8 === 'function';
+}
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports) {
+
+if (typeof Object.create === 'function') {
+ // implementation from standard node.js 'util' module
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ };
+} else {
+ // old school shim for old browsers
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
+}
+
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const stringify = __webpack_require__(3);
+const compile = __webpack_require__(12);
+const expand = __webpack_require__(15);
+const parse = __webpack_require__(16);
+
+/**
+ * Expand the given pattern or create a regex-compatible string.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces('{a,b,c}', { compile: true })); //=> ['(a|b|c)']
+ * console.log(braces('{a,b,c}')); //=> ['a', 'b', 'c']
+ * ```
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {String}
+ * @api public
+ */
+
+const braces = (input, options = {}) => {
+ let output = [];
+
+ if (Array.isArray(input)) {
+ for (let pattern of input) {
+ let result = braces.create(pattern, options);
+ if (Array.isArray(result)) {
+ output.push(...result);
+ } else {
+ output.push(result);
+ }
+ }
+ } else {
+ output = [].concat(braces.create(input, options));
+ }
+
+ if (options && options.expand === true && options.nodupes === true) {
+ output = [...new Set(output)];
+ }
+ return output;
+};
+
+/**
+ * Parse the given `str` with the given `options`.
+ *
+ * ```js
+ * // braces.parse(pattern, [, options]);
+ * const ast = braces.parse('a/{b,c}/d');
+ * console.log(ast);
+ * ```
+ * @param {String} pattern Brace pattern to parse
+ * @param {Object} options
+ * @return {Object} Returns an AST
+ * @api public
+ */
+
+braces.parse = (input, options = {}) => parse(input, options);
+
+/**
+ * Creates a braces string from an AST, or an AST node.
+ *
+ * ```js
+ * const braces = require('braces');
+ * let ast = braces.parse('foo/{a,b}/bar');
+ * console.log(stringify(ast.nodes[2])); //=> '{a,b}'
+ * ```
+ * @param {String} `input` Brace pattern or AST.
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.stringify = (input, options = {}) => {
+ if (typeof input === 'string') {
+ return stringify(braces.parse(input, options), options);
+ }
+ return stringify(input, options);
+};
+
+/**
+ * Compiles a brace pattern into a regex-compatible, optimized string.
+ * This method is called by the main [braces](#braces) function by default.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces.compile('a/{b,c}/d'));
+ * //=> ['a/(b|c)/d']
+ * ```
+ * @param {String} `input` Brace pattern or AST.
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.compile = (input, options = {}) => {
+ if (typeof input === 'string') {
+ input = braces.parse(input, options);
+ }
+ return compile(input, options);
+};
+
+/**
+ * Expands a brace pattern into an array. This method is called by the
+ * main [braces](#braces) function when `options.expand` is true. Before
+ * using this method it's recommended that you read the [performance notes](#performance))
+ * and advantages of using [.compile](#compile) instead.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces.expand('a/{b,c}/d'));
+ * //=> ['a/b/d', 'a/c/d'];
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.expand = (input, options = {}) => {
+ if (typeof input === 'string') {
+ input = braces.parse(input, options);
+ }
+
+ let result = expand(input, options);
+
+ // filter out empty strings if specified
+ if (options.noempty === true) {
+ result = result.filter(Boolean);
+ }
+
+ // filter out duplicates if specified
+ if (options.nodupes === true) {
+ result = [...new Set(result)];
+ }
+
+ return result;
+};
+
+/**
+ * Processes a brace pattern and returns either an expanded array
+ * (if `options.expand` is true), a highly optimized regex-compatible string.
+ * This method is called by the main [braces](#braces) function.
+ *
+ * ```js
+ * const braces = require('braces');
+ * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
+ * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.create = (input, options = {}) => {
+ if (input === '' || input.length < 3) {
+ return [input];
+ }
+
+ return options.expand !== true
+ ? braces.compile(input, options)
+ : braces.expand(input, options);
+};
+
+/**
+ * Expose "braces"
+ */
+
+module.exports = braces;
+
+
+/***/ }),
+/* 12 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const fill = __webpack_require__(7);
+const utils = __webpack_require__(4);
+
+const compile = (ast, options = {}) => {
+ let walk = (node, parent = {}) => {
+ let invalidBlock = utils.isInvalidBrace(parent);
+ let invalidNode = node.invalid === true && options.escapeInvalid === true;
+ let invalid = invalidBlock === true || invalidNode === true;
+ let prefix = options.escapeInvalid === true ? '\\' : '';
+ let output = '';
+
+ if (node.isOpen === true) {
+ return prefix + node.value;
+ }
+ if (node.isClose === true) {
+ return prefix + node.value;
+ }
+
+ if (node.type === 'open') {
+ return invalid ? (prefix + node.value) : '(';
+ }
+
+ if (node.type === 'close') {
+ return invalid ? (prefix + node.value) : ')';
+ }
+
+ if (node.type === 'comma') {
+ return node.prev.type === 'comma' ? '' : (invalid ? node.value : '|');
+ }
+
+ if (node.value) {
+ return node.value;
+ }
+
+ if (node.nodes && node.ranges > 0) {
+ let args = utils.reduce(node.nodes);
+ let range = fill(...args, { ...options, wrap: false, toRegex: true });
+
+ if (range.length !== 0) {
+ return args.length > 1 && range.length > 1 ? `(${range})` : range;
+ }
+ }
+
+ if (node.nodes) {
+ for (let child of node.nodes) {
+ output += walk(child, node);
+ }
+ }
+ return output;
+ };
+
+ return walk(ast);
+};
+
+module.exports = compile;
+
+
+/***/ }),
+/* 13 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*!
+ * to-regex-range <https://github.com/micromatch/to-regex-range>
+ *
+ * Copyright (c) 2015-present, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+
+
+const isNumber = __webpack_require__(14);
+
+const toRegexRange = (min, max, options) => {
+ if (isNumber(min) === false) {
+ throw new TypeError('toRegexRange: expected the first argument to be a number');
+ }
+
+ if (max === void 0 || min === max) {
+ return String(min);
+ }
+
+ if (isNumber(max) === false) {
+ throw new TypeError('toRegexRange: expected the second argument to be a number.');
+ }
+
+ let opts = { relaxZeros: true, ...options };
+ if (typeof opts.strictZeros === 'boolean') {
+ opts.relaxZeros = opts.strictZeros === false;
+ }
+
+ let relax = String(opts.relaxZeros);
+ let shorthand = String(opts.shorthand);
+ let capture = String(opts.capture);
+ let wrap = String(opts.wrap);
+ let cacheKey = min + ':' + max + '=' + relax + shorthand + capture + wrap;
+
+ if (toRegexRange.cache.hasOwnProperty(cacheKey)) {
+ return toRegexRange.cache[cacheKey].result;
+ }
+
+ let a = Math.min(min, max);
+ let b = Math.max(min, max);
+
+ if (Math.abs(a - b) === 1) {
+ let result = min + '|' + max;
+ if (opts.capture) {
+ return `(${result})`;
+ }
+ if (opts.wrap === false) {
+ return result;
+ }
+ return `(?:${result})`;
+ }
+
+ let isPadded = hasPadding(min) || hasPadding(max);
+ let state = { min, max, a, b };
+ let positives = [];
+ let negatives = [];
+
+ if (isPadded) {
+ state.isPadded = isPadded;
+ state.maxLen = String(state.max).length;
+ }
+
+ if (a < 0) {
+ let newMin = b < 0 ? Math.abs(b) : 1;
+ negatives = splitToPatterns(newMin, Math.abs(a), state, opts);
+ a = state.a = 0;
+ }
+
+ if (b >= 0) {
+ positives = splitToPatterns(a, b, state, opts);
+ }
+
+ state.negatives = negatives;
+ state.positives = positives;
+ state.result = collatePatterns(negatives, positives, opts);
+
+ if (opts.capture === true) {
+ state.result = `(${state.result})`;
+ } else if (opts.wrap !== false && (positives.length + negatives.length) > 1) {
+ state.result = `(?:${state.result})`;
+ }
+
+ toRegexRange.cache[cacheKey] = state;
+ return state.result;
+};
+
+function collatePatterns(neg, pos, options) {
+ let onlyNegative = filterPatterns(neg, pos, '-', false, options) || [];
+ let onlyPositive = filterPatterns(pos, neg, '', false, options) || [];
+ let intersected = filterPatterns(neg, pos, '-?', true, options) || [];
+ let subpatterns = onlyNegative.concat(intersected).concat(onlyPositive);
+ return subpatterns.join('|');
+}
+
+function splitToRanges(min, max) {
+ let nines = 1;
+ let zeros = 1;
+
+ let stop = countNines(min, nines);
+ let stops = new Set([max]);
+
+ while (min <= stop && stop <= max) {
+ stops.add(stop);
+ nines += 1;
+ stop = countNines(min, nines);
+ }
+
+ stop = countZeros(max + 1, zeros) - 1;
+
+ while (min < stop && stop <= max) {
+ stops.add(stop);
+ zeros += 1;
+ stop = countZeros(max + 1, zeros) - 1;
+ }
+
+ stops = [...stops];
+ stops.sort(compare);
+ return stops;
+}
+
+/**
+ * Convert a range to a regex pattern
+ * @param {Number} `start`
+ * @param {Number} `stop`
+ * @return {String}
+ */
+
+function rangeToPattern(start, stop, options) {
+ if (start === stop) {
+ return { pattern: start, count: [], digits: 0 };
+ }
+
+ let zipped = zip(start, stop);
+ let digits = zipped.length;
+ let pattern = '';
+ let count = 0;
+
+ for (let i = 0; i < digits; i++) {
+ let [startDigit, stopDigit] = zipped[i];
+
+ if (startDigit === stopDigit) {
+ pattern += startDigit;
+
+ } else if (startDigit !== '0' || stopDigit !== '9') {
+ pattern += toCharacterClass(startDigit, stopDigit, options);
+
+ } else {
+ count++;
+ }
+ }
+
+ if (count) {
+ pattern += options.shorthand === true ? '\\d' : '[0-9]';
+ }
+
+ return { pattern, count: [count], digits };
+}
+
+function splitToPatterns(min, max, tok, options) {
+ let ranges = splitToRanges(min, max);
+ let tokens = [];
+ let start = min;
+ let prev;
+
+ for (let i = 0; i < ranges.length; i++) {
+ let max = ranges[i];
+ let obj = rangeToPattern(String(start), String(max), options);
+ let zeros = '';
+
+ if (!tok.isPadded && prev && prev.pattern === obj.pattern) {
+ if (prev.count.length > 1) {
+ prev.count.pop();
+ }
+
+ prev.count.push(obj.count[0]);
+ prev.string = prev.pattern + toQuantifier(prev.count);
+ start = max + 1;
+ continue;
+ }
+
+ if (tok.isPadded) {
+ zeros = padZeros(max, tok, options);
+ }
+
+ obj.string = zeros + obj.pattern + toQuantifier(obj.count);
+ tokens.push(obj);
+ start = max + 1;
+ prev = obj;
+ }
+
+ return tokens;
+}
+
+function filterPatterns(arr, comparison, prefix, intersection, options) {
+ let result = [];
+
+ for (let ele of arr) {
+ let { string } = ele;
+
+ // only push if _both_ are negative...
+ if (!intersection && !contains(comparison, 'string', string)) {
+ result.push(prefix + string);
+ }
+
+ // or _both_ are positive
+ if (intersection && contains(comparison, 'string', string)) {
+ result.push(prefix + string);
+ }
+ }
+ return result;
+}
+
+/**
+ * Zip strings
+ */
+
+function zip(a, b) {
+ let arr = [];
+ for (let i = 0; i < a.length; i++) arr.push([a[i], b[i]]);
+ return arr;
+}
+
+function compare(a, b) {
+ return a > b ? 1 : b > a ? -1 : 0;
+}
+
+function contains(arr, key, val) {
+ return arr.some(ele => ele[key] === val);
+}
+
+function countNines(min, len) {
+ return Number(String(min).slice(0, -len) + '9'.repeat(len));
+}
+
+function countZeros(integer, zeros) {
+ return integer - (integer % Math.pow(10, zeros));
+}
+
+function toQuantifier(digits) {
+ let [start = 0, stop = ''] = digits;
+ if (stop || start > 1) {
+ return `{${start + (stop ? ',' + stop : '')}}`;
+ }
+ return '';
+}
+
+function toCharacterClass(a, b, options) {
+ return `[${a}${(b - a === 1) ? '' : '-'}${b}]`;
+}
+
+function hasPadding(str) {
+ return /^-?(0+)\d/.test(str);
+}
+
+function padZeros(value, tok, options) {
+ if (!tok.isPadded) {
+ return value;
+ }
+
+ let diff = Math.abs(tok.maxLen - String(value).length);
+ let relax = options.relaxZeros !== false;
+
+ switch (diff) {
+ case 0:
+ return '';
+ case 1:
+ return relax ? '0?' : '0';
+ case 2:
+ return relax ? '0{0,2}' : '00';
+ default: {
+ return relax ? `0{0,${diff}}` : `0{${diff}}`;
+ }
+ }
+}
+
+/**
+ * Cache
+ */
+
+toRegexRange.cache = {};
+toRegexRange.clearCache = () => (toRegexRange.cache = {});
+
+/**
+ * Expose `toRegexRange`
+ */
+
+module.exports = toRegexRange;
+
+
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*!
+ * is-number <https://github.com/jonschlinkert/is-number>
+ *
+ * Copyright (c) 2014-present, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+
+
+module.exports = function(num) {
+ if (typeof num === 'number') {
+ return num - num === 0;
+ }
+ if (typeof num === 'string' && num.trim() !== '') {
+ return Number.isFinite ? Number.isFinite(+num) : isFinite(+num);
+ }
+ return false;
+};
+
+
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const fill = __webpack_require__(7);
+const stringify = __webpack_require__(3);
+const utils = __webpack_require__(4);
+
+const append = (queue = '', stash = '', enclose = false) => {
+ let result = [];
+
+ queue = [].concat(queue);
+ stash = [].concat(stash);
+
+ if (!stash.length) return queue;
+ if (!queue.length) {
+ return enclose ? utils.flatten(stash).map(ele => `{${ele}}`) : stash;
+ }
+
+ for (let item of queue) {
+ if (Array.isArray(item)) {
+ for (let value of item) {
+ result.push(append(value, stash, enclose));
+ }
+ } else {
+ for (let ele of stash) {
+ if (enclose === true && typeof ele === 'string') ele = `{${ele}}`;
+ result.push(Array.isArray(ele) ? append(item, ele, enclose) : (item + ele));
+ }
+ }
+ }
+ return utils.flatten(result);
+};
+
+const expand = (ast, options = {}) => {
+ let rangeLimit = options.rangeLimit === void 0 ? 1000 : options.rangeLimit;
+
+ let walk = (node, parent = {}) => {
+ node.queue = [];
+
+ let p = parent;
+ let q = parent.queue;
+
+ while (p.type !== 'brace' && p.type !== 'root' && p.parent) {
+ p = p.parent;
+ q = p.queue;
+ }
+
+ if (node.invalid || node.dollar) {
+ q.push(append(q.pop(), stringify(node, options)));
+ return;
+ }
+
+ if (node.type === 'brace' && node.invalid !== true && node.nodes.length === 2) {
+ q.push(append(q.pop(), ['{}']));
+ return;
+ }
+
+ if (node.nodes && node.ranges > 0) {
+ let args = utils.reduce(node.nodes);
+
+ if (utils.exceedsLimit(...args, options.step, rangeLimit)) {
+ throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
+ }
+
+ let range = fill(...args, options);
+ if (range.length === 0) {
+ range = stringify(node, options);
+ }
+
+ q.push(append(q.pop(), range));
+ node.nodes = [];
+ return;
+ }
+
+ let enclose = utils.encloseBrace(node);
+ let queue = node.queue;
+ let block = node;
+
+ while (block.type !== 'brace' && block.type !== 'root' && block.parent) {
+ block = block.parent;
+ queue = block.queue;
+ }
+
+ for (let i = 0; i < node.nodes.length; i++) {
+ let child = node.nodes[i];
+
+ if (child.type === 'comma' && node.type === 'brace') {
+ if (i === 1) queue.push('');
+ queue.push('');
+ continue;
+ }
+
+ if (child.type === 'close') {
+ q.push(append(q.pop(), queue, enclose));
+ continue;
+ }
+
+ if (child.value && child.type !== 'open') {
+ queue.push(append(queue.pop(), child.value));
+ continue;
+ }
+
+ if (child.nodes) {
+ walk(child, node);
+ }
+ }
+
+ return queue;
+ };
+
+ return utils.flatten(walk(ast));
+};
+
+module.exports = expand;
+
+
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const stringify = __webpack_require__(3);
+
+/**
+ * Constants
+ */
+
+const {
+ MAX_LENGTH,
+ CHAR_BACKSLASH, /* \ */
+ CHAR_BACKTICK, /* ` */
+ CHAR_COMMA, /* , */
+ CHAR_DOT, /* . */
+ CHAR_LEFT_PARENTHESES, /* ( */
+ CHAR_RIGHT_PARENTHESES, /* ) */
+ CHAR_LEFT_CURLY_BRACE, /* { */
+ CHAR_RIGHT_CURLY_BRACE, /* } */
+ CHAR_LEFT_SQUARE_BRACKET, /* [ */
+ CHAR_RIGHT_SQUARE_BRACKET, /* ] */
+ CHAR_DOUBLE_QUOTE, /* " */
+ CHAR_SINGLE_QUOTE, /* ' */
+ CHAR_NO_BREAK_SPACE,
+ CHAR_ZERO_WIDTH_NOBREAK_SPACE
+} = __webpack_require__(17);
+
+/**
+ * parse
+ */
+
+const parse = (input, options = {}) => {
+ if (typeof input !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ let opts = options || {};
+ let max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
+ if (input.length > max) {
+ throw new SyntaxError(`Input length (${input.length}), exceeds max characters (${max})`);
+ }
+
+ let ast = { type: 'root', input, nodes: [] };
+ let stack = [ast];
+ let block = ast;
+ let prev = ast;
+ let brackets = 0;
+ let length = input.length;
+ let index = 0;
+ let depth = 0;
+ let value;
+ let memo = {};
+
+ /**
+ * Helpers
+ */
+
+ const advance = () => input[index++];
+ const push = node => {
+ if (node.type === 'text' && prev.type === 'dot') {
+ prev.type = 'text';
+ }
+
+ if (prev && prev.type === 'text' && node.type === 'text') {
+ prev.value += node.value;
+ return;
+ }
+
+ block.nodes.push(node);
+ node.parent = block;
+ node.prev = prev;
+ prev = node;
+ return node;
+ };
+
+ push({ type: 'bos' });
+
+ while (index < length) {
+ block = stack[stack.length - 1];
+ value = advance();
+
+ /**
+ * Invalid chars
+ */
+
+ if (value === CHAR_ZERO_WIDTH_NOBREAK_SPACE || value === CHAR_NO_BREAK_SPACE) {
+ continue;
+ }
+
+ /**
+ * Escaped chars
+ */
+
+ if (value === CHAR_BACKSLASH) {
+ push({ type: 'text', value: (options.keepEscaping ? value : '') + advance() });
+ continue;
+ }
+
+ /**
+ * Right square bracket (literal): ']'
+ */
+
+ if (value === CHAR_RIGHT_SQUARE_BRACKET) {
+ push({ type: 'text', value: '\\' + value });
+ continue;
+ }
+
+ /**
+ * Left square bracket: '['
+ */
+
+ if (value === CHAR_LEFT_SQUARE_BRACKET) {
+ brackets++;
+
+ let closed = true;
+ let next;
+
+ while (index < length && (next = advance())) {
+ value += next;
+
+ if (next === CHAR_LEFT_SQUARE_BRACKET) {
+ brackets++;
+ continue;
+ }
+
+ if (next === CHAR_BACKSLASH) {
+ value += advance();
+ continue;
+ }
+
+ if (next === CHAR_RIGHT_SQUARE_BRACKET) {
+ brackets--;
+
+ if (brackets === 0) {
+ break;
+ }
+ }
+ }
+
+ push({ type: 'text', value });
+ continue;
+ }
+
+ /**
+ * Parentheses
+ */
+
+ if (value === CHAR_LEFT_PARENTHESES) {
+ block = push({ type: 'paren', nodes: [] });
+ stack.push(block);
+ push({ type: 'text', value });
+ continue;
+ }
+
+ if (value === CHAR_RIGHT_PARENTHESES) {
+ if (block.type !== 'paren') {
+ push({ type: 'text', value });
+ continue;
+ }
+ block = stack.pop();
+ push({ type: 'text', value });
+ block = stack[stack.length - 1];
+ continue;
+ }
+
+ /**
+ * Quotes: '|"|`
+ */
+
+ if (value === CHAR_DOUBLE_QUOTE || value === CHAR_SINGLE_QUOTE || value === CHAR_BACKTICK) {
+ let open = value;
+ let next;
+
+ if (options.keepQuotes !== true) {
+ value = '';
+ }
+
+ while (index < length && (next = advance())) {
+ if (next === CHAR_BACKSLASH) {
+ value += next + advance();
+ continue;
+ }
+
+ if (next === open) {
+ if (options.keepQuotes === true) value += next;
+ break;
+ }
+
+ value += next;
+ }
+
+ push({ type: 'text', value });
+ continue;
+ }
+
+ /**
+ * Left curly brace: '{'
+ */
+
+ if (value === CHAR_LEFT_CURLY_BRACE) {
+ depth++;
+
+ let dollar = prev.value && prev.value.slice(-1) === '$' || block.dollar === true;
+ let brace = {
+ type: 'brace',
+ open: true,
+ close: false,
+ dollar,
+ depth,
+ commas: 0,
+ ranges: 0,
+ nodes: []
+ };
+
+ block = push(brace);
+ stack.push(block);
+ push({ type: 'open', value });
+ continue;
+ }
+
+ /**
+ * Right curly brace: '}'
+ */
+
+ if (value === CHAR_RIGHT_CURLY_BRACE) {
+ if (block.type !== 'brace') {
+ push({ type: 'text', value });
+ continue;
+ }
+
+ let type = 'close';
+ block = stack.pop();
+ block.close = true;
+
+ push({ type, value });
+ depth--;
+
+ block = stack[stack.length - 1];
+ continue;
+ }
+
+ /**
+ * Comma: ','
+ */
+
+ if (value === CHAR_COMMA && depth > 0) {
+ if (block.ranges > 0) {
+ block.ranges = 0;
+ let open = block.nodes.shift();
+ block.nodes = [open, { type: 'text', value: stringify(block) }];
+ }
+
+ push({ type: 'comma', value });
+ block.commas++;
+ continue;
+ }
+
+ /**
+ * Dot: '.'
+ */
+
+ if (value === CHAR_DOT && depth > 0 && block.commas === 0) {
+ let siblings = block.nodes;
+
+ if (depth === 0 || siblings.length === 0) {
+ push({ type: 'text', value });
+ continue;
+ }
+
+ if (prev.type === 'dot') {
+ block.range = [];
+ prev.value += value;
+ prev.type = 'range';
+
+ if (block.nodes.length !== 3 && block.nodes.length !== 5) {
+ block.invalid = true;
+ block.ranges = 0;
+ prev.type = 'text';
+ continue;
+ }
+
+ block.ranges++;
+ block.args = [];
+ continue;
+ }
+
+ if (prev.type === 'range') {
+ siblings.pop();
+
+ let before = siblings[siblings.length - 1];
+ before.value += prev.value + value;
+ prev = before;
+ block.ranges--;
+ continue;
+ }
+
+ push({ type: 'dot', value });
+ continue;
+ }
+
+ /**
+ * Text
+ */
+
+ push({ type: 'text', value });
+ }
+
+ // Mark imbalanced braces and brackets as invalid
+ do {
+ block = stack.pop();
+
+ if (block.type !== 'root') {
+ block.nodes.forEach(node => {
+ if (!node.nodes) {
+ if (node.type === 'open') node.isOpen = true;
+ if (node.type === 'close') node.isClose = true;
+ if (!node.nodes) node.type = 'text';
+ node.invalid = true;
+ }
+ });
+
+ // get the location of the block on parent.nodes (block's siblings)
+ let parent = stack[stack.length - 1];
+ let index = parent.nodes.indexOf(block);
+ // replace the (invalid) block with it's nodes
+ parent.nodes.splice(index, 1, ...block.nodes);
+ }
+ } while (stack.length > 0);
+
+ push({ type: 'eos' });
+ return ast;
+};
+
+module.exports = parse;
+
+
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+module.exports = {
+ MAX_LENGTH: 1024 * 64,
+
+ // Digits
+ CHAR_0: '0', /* 0 */
+ CHAR_9: '9', /* 9 */
+
+ // Alphabet chars.
+ CHAR_UPPERCASE_A: 'A', /* A */
+ CHAR_LOWERCASE_A: 'a', /* a */
+ CHAR_UPPERCASE_Z: 'Z', /* Z */
+ CHAR_LOWERCASE_Z: 'z', /* z */
+
+ CHAR_LEFT_PARENTHESES: '(', /* ( */
+ CHAR_RIGHT_PARENTHESES: ')', /* ) */
+
+ CHAR_ASTERISK: '*', /* * */
+
+ // Non-alphabetic chars.
+ CHAR_AMPERSAND: '&', /* & */
+ CHAR_AT: '@', /* @ */
+ CHAR_BACKSLASH: '\\', /* \ */
+ CHAR_BACKTICK: '`', /* ` */
+ CHAR_CARRIAGE_RETURN: '\r', /* \r */
+ CHAR_CIRCUMFLEX_ACCENT: '^', /* ^ */
+ CHAR_COLON: ':', /* : */
+ CHAR_COMMA: ',', /* , */
+ CHAR_DOLLAR: '$', /* . */
+ CHAR_DOT: '.', /* . */
+ CHAR_DOUBLE_QUOTE: '"', /* " */
+ CHAR_EQUAL: '=', /* = */
+ CHAR_EXCLAMATION_MARK: '!', /* ! */
+ CHAR_FORM_FEED: '\f', /* \f */
+ CHAR_FORWARD_SLASH: '/', /* / */
+ CHAR_HASH: '#', /* # */
+ CHAR_HYPHEN_MINUS: '-', /* - */
+ CHAR_LEFT_ANGLE_BRACKET: '<', /* < */
+ CHAR_LEFT_CURLY_BRACE: '{', /* { */
+ CHAR_LEFT_SQUARE_BRACKET: '[', /* [ */
+ CHAR_LINE_FEED: '\n', /* \n */
+ CHAR_NO_BREAK_SPACE: '\u00A0', /* \u00A0 */
+ CHAR_PERCENT: '%', /* % */
+ CHAR_PLUS: '+', /* + */
+ CHAR_QUESTION_MARK: '?', /* ? */
+ CHAR_RIGHT_ANGLE_BRACKET: '>', /* > */
+ CHAR_RIGHT_CURLY_BRACE: '}', /* } */
+ CHAR_RIGHT_SQUARE_BRACKET: ']', /* ] */
+ CHAR_SEMICOLON: ';', /* ; */
+ CHAR_SINGLE_QUOTE: '\'', /* ' */
+ CHAR_SPACE: ' ', /* */
+ CHAR_TAB: '\t', /* \t */
+ CHAR_UNDERSCORE: '_', /* _ */
+ CHAR_VERTICAL_LINE: '|', /* | */
+ CHAR_ZERO_WIDTH_NOBREAK_SPACE: '\uFEFF' /* \uFEFF */
+};
+
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+module.exports = __webpack_require__(19);
+
+
+/***/ }),
+/* 19 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const path = __webpack_require__(5);
+const scan = __webpack_require__(20);
+const parse = __webpack_require__(21);
+const utils = __webpack_require__(0);
+const constants = __webpack_require__(1);
+const isObject = val => val && typeof val === 'object' && !Array.isArray(val);
+
+/**
+ * Creates a matcher function from one or more glob patterns. The
+ * returned function takes a string to match as its first argument,
+ * and returns true if the string is a match. The returned matcher
+ * function also takes a boolean as the second argument that, when true,
+ * returns an object with additional information.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * // picomatch(glob[, options]);
+ *
+ * const isMatch = picomatch('*.!(*a)');
+ * console.log(isMatch('a.a')); //=> false
+ * console.log(isMatch('a.b')); //=> true
+ * ```
+ * @name picomatch
+ * @param {String|Array} `globs` One or more glob patterns.
+ * @param {Object=} `options`
+ * @return {Function=} Returns a matcher function.
+ * @api public
+ */
+
+const picomatch = (glob, options, returnState = false) => {
+ if (Array.isArray(glob)) {
+ const fns = glob.map(input => picomatch(input, options, returnState));
+ const arrayMatcher = str => {
+ for (const isMatch of fns) {
+ const state = isMatch(str);
+ if (state) return state;
+ }
+ return false;
+ };
+ return arrayMatcher;
+ }
+
+ const isState = isObject(glob) && glob.tokens && glob.input;
+
+ if (glob === '' || (typeof glob !== 'string' && !isState)) {
+ throw new TypeError('Expected pattern to be a non-empty string');
+ }
+
+ const opts = options || {};
+ const posix = utils.isWindows(options);
+ const regex = isState
+ ? picomatch.compileRe(glob, options)
+ : picomatch.makeRe(glob, options, false, true);
+
+ const state = regex.state;
+ delete regex.state;
+
+ let isIgnored = () => false;
+ if (opts.ignore) {
+ const ignoreOpts = { ...options, ignore: null, onMatch: null, onResult: null };
+ isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
+ }
+
+ const matcher = (input, returnObject = false) => {
+ const { isMatch, match, output } = picomatch.test(input, regex, options, { glob, posix });
+ const result = { glob, state, regex, posix, input, output, match, isMatch };
+
+ if (typeof opts.onResult === 'function') {
+ opts.onResult(result);
+ }
+
+ if (isMatch === false) {
+ result.isMatch = false;
+ return returnObject ? result : false;
+ }
+
+ if (isIgnored(input)) {
+ if (typeof opts.onIgnore === 'function') {
+ opts.onIgnore(result);
+ }
+ result.isMatch = false;
+ return returnObject ? result : false;
+ }
+
+ if (typeof opts.onMatch === 'function') {
+ opts.onMatch(result);
+ }
+ return returnObject ? result : true;
+ };
+
+ if (returnState) {
+ matcher.state = state;
+ }
+
+ return matcher;
+};
+
+/**
+ * Test `input` with the given `regex`. This is used by the main
+ * `picomatch()` function to test the input string.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * // picomatch.test(input, regex[, options]);
+ *
+ * console.log(picomatch.test('foo/bar', /^(?:([^/]*?)\/([^/]*?))$/));
+ * // { isMatch: true, match: [ 'foo/', 'foo', 'bar' ], output: 'foo/bar' }
+ * ```
+ * @param {String} `input` String to test.
+ * @param {RegExp} `regex`
+ * @return {Object} Returns an object with matching info.
+ * @api public
+ */
+
+picomatch.test = (input, regex, options, { glob, posix } = {}) => {
+ if (typeof input !== 'string') {
+ throw new TypeError('Expected input to be a string');
+ }
+
+ if (input === '') {
+ return { isMatch: false, output: '' };
+ }
+
+ const opts = options || {};
+ const format = opts.format || (posix ? utils.toPosixSlashes : null);
+ let match = input === glob;
+ let output = (match && format) ? format(input) : input;
+
+ if (match === false) {
+ output = format ? format(input) : input;
+ match = output === glob;
+ }
+
+ if (match === false || opts.capture === true) {
+ if (opts.matchBase === true || opts.basename === true) {
+ match = picomatch.matchBase(input, regex, options, posix);
+ } else {
+ match = regex.exec(output);
+ }
+ }
+
+ return { isMatch: Boolean(match), match, output };
+};
+
+/**
+ * Match the basename of a filepath.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * // picomatch.matchBase(input, glob[, options]);
+ * console.log(picomatch.matchBase('foo/bar.js', '*.js'); // true
+ * ```
+ * @param {String} `input` String to test.
+ * @param {RegExp|String} `glob` Glob pattern or regex created by [.makeRe](#makeRe).
+ * @return {Boolean}
+ * @api public
+ */
+
+picomatch.matchBase = (input, glob, options, posix = utils.isWindows(options)) => {
+ const regex = glob instanceof RegExp ? glob : picomatch.makeRe(glob, options);
+ return regex.test(path.basename(input));
+};
+
+/**
+ * Returns true if **any** of the given glob `patterns` match the specified `string`.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * // picomatch.isMatch(string, patterns[, options]);
+ *
+ * console.log(picomatch.isMatch('a.a', ['b.*', '*.a'])); //=> true
+ * console.log(picomatch.isMatch('a.a', 'b.*')); //=> false
+ * ```
+ * @param {String|Array} str The string to test.
+ * @param {String|Array} patterns One or more glob patterns to use for matching.
+ * @param {Object} [options] See available [options](#options).
+ * @return {Boolean} Returns true if any patterns match `str`
+ * @api public
+ */
+
+picomatch.isMatch = (str, patterns, options) => picomatch(patterns, options)(str);
+
+/**
+ * Parse a glob pattern to create the source string for a regular
+ * expression.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * const result = picomatch.parse(pattern[, options]);
+ * ```
+ * @param {String} `pattern`
+ * @param {Object} `options`
+ * @return {Object} Returns an object with useful properties and output to be used as a regex source string.
+ * @api public
+ */
+
+picomatch.parse = (pattern, options) => {
+ if (Array.isArray(pattern)) return pattern.map(p => picomatch.parse(p, options));
+ return parse(pattern, { ...options, fastpaths: false });
+};
+
+/**
+ * Scan a glob pattern to separate the pattern into segments.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * // picomatch.scan(input[, options]);
+ *
+ * const result = picomatch.scan('!./foo/*.js');
+ * console.log(result);
+ * { prefix: '!./',
+ * input: '!./foo/*.js',
+ * start: 3,
+ * base: 'foo',
+ * glob: '*.js',
+ * isBrace: false,
+ * isBracket: false,
+ * isGlob: true,
+ * isExtglob: false,
+ * isGlobstar: false,
+ * negated: true }
+ * ```
+ * @param {String} `input` Glob pattern to scan.
+ * @param {Object} `options`
+ * @return {Object} Returns an object with
+ * @api public
+ */
+
+picomatch.scan = (input, options) => scan(input, options);
+
+/**
+ * Compile a regular expression from the `state` object returned by the
+ * [parse()](#parse) method.
+ *
+ * @param {Object} `state`
+ * @param {Object} `options`
+ * @param {Boolean} `returnOutput` Intended for implementors, this argument allows you to return the raw output from the parser.
+ * @param {Boolean} `returnState` Adds the state to a `state` property on the returned regex. Useful for implementors and debugging.
+ * @return {RegExp}
+ * @api public
+ */
+
+picomatch.compileRe = (state, options, returnOutput = false, returnState = false) => {
+ if (returnOutput === true) {
+ return state.output;
+ }
+
+ const opts = options || {};
+ const prepend = opts.contains ? '' : '^';
+ const append = opts.contains ? '' : '$';
+
+ let source = `${prepend}(?:${state.output})${append}`;
+ if (state && state.negated === true) {
+ source = `^(?!${source}).*$`;
+ }
+
+ const regex = picomatch.toRegex(source, options);
+ if (returnState === true) {
+ regex.state = state;
+ }
+
+ return regex;
+};
+
+/**
+ * Create a regular expression from a parsed glob pattern.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * const state = picomatch.parse('*.js');
+ * // picomatch.compileRe(state[, options]);
+ *
+ * console.log(picomatch.compileRe(state));
+ * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
+ * ```
+ * @param {String} `state` The object returned from the `.parse` method.
+ * @param {Object} `options`
+ * @param {Boolean} `returnOutput` Implementors may use this argument to return the compiled output, instead of a regular expression. This is not exposed on the options to prevent end-users from mutating the result.
+ * @param {Boolean} `returnState` Implementors may use this argument to return the state from the parsed glob with the returned regular expression.
+ * @return {RegExp} Returns a regex created from the given pattern.
+ * @api public
+ */
+
+picomatch.makeRe = (input, options = {}, returnOutput = false, returnState = false) => {
+ if (!input || typeof input !== 'string') {
+ throw new TypeError('Expected a non-empty string');
+ }
+
+ let parsed = { negated: false, fastpaths: true };
+
+ if (options.fastpaths !== false && (input[0] === '.' || input[0] === '*')) {
+ parsed.output = parse.fastpaths(input, options);
+ }
+
+ if (!parsed.output) {
+ parsed = parse(input, options);
+ }
+
+ return picomatch.compileRe(parsed, options, returnOutput, returnState);
+};
+
+/**
+ * Create a regular expression from the given regex source string.
+ *
+ * ```js
+ * const picomatch = require('picomatch');
+ * // picomatch.toRegex(source[, options]);
+ *
+ * const { output } = picomatch.parse('*.js');
+ * console.log(picomatch.toRegex(output));
+ * //=> /^(?:(?!\.)(?=.)[^/]*?\.js)$/
+ * ```
+ * @param {String} `source` Regular expression source string.
+ * @param {Object} `options`
+ * @return {RegExp}
+ * @api public
+ */
+
+picomatch.toRegex = (source, options) => {
+ try {
+ const opts = options || {};
+ return new RegExp(source, opts.flags || (opts.nocase ? 'i' : ''));
+ } catch (err) {
+ if (options && options.debug === true) throw err;
+ return /$^/;
+ }
+};
+
+/**
+ * Picomatch constants.
+ * @return {Object}
+ */
+
+picomatch.constants = constants;
+
+/**
+ * Expose "picomatch"
+ */
+
+module.exports = picomatch;
+
+
+/***/ }),
+/* 20 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const utils = __webpack_require__(0);
+const {
+ CHAR_ASTERISK, /* * */
+ CHAR_AT, /* @ */
+ CHAR_BACKWARD_SLASH, /* \ */
+ CHAR_COMMA, /* , */
+ CHAR_DOT, /* . */
+ CHAR_EXCLAMATION_MARK, /* ! */
+ CHAR_FORWARD_SLASH, /* / */
+ CHAR_LEFT_CURLY_BRACE, /* { */
+ CHAR_LEFT_PARENTHESES, /* ( */
+ CHAR_LEFT_SQUARE_BRACKET, /* [ */
+ CHAR_PLUS, /* + */
+ CHAR_QUESTION_MARK, /* ? */
+ CHAR_RIGHT_CURLY_BRACE, /* } */
+ CHAR_RIGHT_PARENTHESES, /* ) */
+ CHAR_RIGHT_SQUARE_BRACKET /* ] */
+} = __webpack_require__(1);
+
+const isPathSeparator = code => {
+ return code === CHAR_FORWARD_SLASH || code === CHAR_BACKWARD_SLASH;
+};
+
+const depth = token => {
+ if (token.isPrefix !== true) {
+ token.depth = token.isGlobstar ? Infinity : 1;
+ }
+};
+
+/**
+ * Quickly scans a glob pattern and returns an object with a handful of
+ * useful properties, like `isGlob`, `path` (the leading non-glob, if it exists),
+ * `glob` (the actual pattern), `negated` (true if the path starts with `!` but not
+ * with `!(`) and `negatedExtglob` (true if the path starts with `!(`).
+ *
+ * ```js
+ * const pm = require('picomatch');
+ * console.log(pm.scan('foo/bar/*.js'));
+ * { isGlob: true, input: 'foo/bar/*.js', base: 'foo/bar', glob: '*.js' }
+ * ```
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {Object} Returns an object with tokens and regex source string.
+ * @api public
+ */
+
+const scan = (input, options) => {
+ const opts = options || {};
+
+ const length = input.length - 1;
+ const scanToEnd = opts.parts === true || opts.scanToEnd === true;
+ const slashes = [];
+ const tokens = [];
+ const parts = [];
+
+ let str = input;
+ let index = -1;
+ let start = 0;
+ let lastIndex = 0;
+ let isBrace = false;
+ let isBracket = false;
+ let isGlob = false;
+ let isExtglob = false;
+ let isGlobstar = false;
+ let braceEscaped = false;
+ let backslashes = false;
+ let negated = false;
+ let negatedExtglob = false;
+ let finished = false;
+ let braces = 0;
+ let prev;
+ let code;
+ let token = { value: '', depth: 0, isGlob: false };
+
+ const eos = () => index >= length;
+ const peek = () => str.charCodeAt(index + 1);
+ const advance = () => {
+ prev = code;
+ return str.charCodeAt(++index);
+ };
+
+ while (index < length) {
+ code = advance();
+ let next;
+
+ if (code === CHAR_BACKWARD_SLASH) {
+ backslashes = token.backslashes = true;
+ code = advance();
+
+ if (code === CHAR_LEFT_CURLY_BRACE) {
+ braceEscaped = true;
+ }
+ continue;
+ }
+
+ if (braceEscaped === true || code === CHAR_LEFT_CURLY_BRACE) {
+ braces++;
+
+ while (eos() !== true && (code = advance())) {
+ if (code === CHAR_BACKWARD_SLASH) {
+ backslashes = token.backslashes = true;
+ advance();
+ continue;
+ }
+
+ if (code === CHAR_LEFT_CURLY_BRACE) {
+ braces++;
+ continue;
+ }
+
+ if (braceEscaped !== true && code === CHAR_DOT && (code = advance()) === CHAR_DOT) {
+ isBrace = token.isBrace = true;
+ isGlob = token.isGlob = true;
+ finished = true;
+
+ if (scanToEnd === true) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (braceEscaped !== true && code === CHAR_COMMA) {
+ isBrace = token.isBrace = true;
+ isGlob = token.isGlob = true;
+ finished = true;
+
+ if (scanToEnd === true) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (code === CHAR_RIGHT_CURLY_BRACE) {
+ braces--;
+
+ if (braces === 0) {
+ braceEscaped = false;
+ isBrace = token.isBrace = true;
+ finished = true;
+ break;
+ }
+ }
+ }
+
+ if (scanToEnd === true) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (code === CHAR_FORWARD_SLASH) {
+ slashes.push(index);
+ tokens.push(token);
+ token = { value: '', depth: 0, isGlob: false };
+
+ if (finished === true) continue;
+ if (prev === CHAR_DOT && index === (start + 1)) {
+ start += 2;
+ continue;
+ }
+
+ lastIndex = index + 1;
+ continue;
+ }
+
+ if (opts.noext !== true) {
+ const isExtglobChar = code === CHAR_PLUS
+ || code === CHAR_AT
+ || code === CHAR_ASTERISK
+ || code === CHAR_QUESTION_MARK
+ || code === CHAR_EXCLAMATION_MARK;
+
+ if (isExtglobChar === true && peek() === CHAR_LEFT_PARENTHESES) {
+ isGlob = token.isGlob = true;
+ isExtglob = token.isExtglob = true;
+ finished = true;
+ if (code === CHAR_EXCLAMATION_MARK && index === start) {
+ negatedExtglob = true;
+ }
+
+ if (scanToEnd === true) {
+ while (eos() !== true && (code = advance())) {
+ if (code === CHAR_BACKWARD_SLASH) {
+ backslashes = token.backslashes = true;
+ code = advance();
+ continue;
+ }
+
+ if (code === CHAR_RIGHT_PARENTHESES) {
+ isGlob = token.isGlob = true;
+ finished = true;
+ break;
+ }
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+ if (code === CHAR_ASTERISK) {
+ if (prev === CHAR_ASTERISK) isGlobstar = token.isGlobstar = true;
+ isGlob = token.isGlob = true;
+ finished = true;
+
+ if (scanToEnd === true) {
+ continue;
+ }
+ break;
+ }
+
+ if (code === CHAR_QUESTION_MARK) {
+ isGlob = token.isGlob = true;
+ finished = true;
+
+ if (scanToEnd === true) {
+ continue;
+ }
+ break;
+ }
+
+ if (code === CHAR_LEFT_SQUARE_BRACKET) {
+ while (eos() !== true && (next = advance())) {
+ if (next === CHAR_BACKWARD_SLASH) {
+ backslashes = token.backslashes = true;
+ advance();
+ continue;
+ }
+
+ if (next === CHAR_RIGHT_SQUARE_BRACKET) {
+ isBracket = token.isBracket = true;
+ isGlob = token.isGlob = true;
+ finished = true;
+ break;
+ }
+ }
+
+ if (scanToEnd === true) {
+ continue;
+ }
+
+ break;
+ }
+
+ if (opts.nonegate !== true && code === CHAR_EXCLAMATION_MARK && index === start) {
+ negated = token.negated = true;
+ start++;
+ continue;
+ }
+
+ if (opts.noparen !== true && code === CHAR_LEFT_PARENTHESES) {
+ isGlob = token.isGlob = true;
+
+ if (scanToEnd === true) {
+ while (eos() !== true && (code = advance())) {
+ if (code === CHAR_LEFT_PARENTHESES) {
+ backslashes = token.backslashes = true;
+ code = advance();
+ continue;
+ }
+
+ if (code === CHAR_RIGHT_PARENTHESES) {
+ finished = true;
+ break;
+ }
+ }
+ continue;
+ }
+ break;
+ }
+
+ if (isGlob === true) {
+ finished = true;
+
+ if (scanToEnd === true) {
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ if (opts.noext === true) {
+ isExtglob = false;
+ isGlob = false;
+ }
+
+ let base = str;
+ let prefix = '';
+ let glob = '';
+
+ if (start > 0) {
+ prefix = str.slice(0, start);
+ str = str.slice(start);
+ lastIndex -= start;
+ }
+
+ if (base && isGlob === true && lastIndex > 0) {
+ base = str.slice(0, lastIndex);
+ glob = str.slice(lastIndex);
+ } else if (isGlob === true) {
+ base = '';
+ glob = str;
+ } else {
+ base = str;
+ }
+
+ if (base && base !== '' && base !== '/' && base !== str) {
+ if (isPathSeparator(base.charCodeAt(base.length - 1))) {
+ base = base.slice(0, -1);
+ }
+ }
+
+ if (opts.unescape === true) {
+ if (glob) glob = utils.removeBackslashes(glob);
+
+ if (base && backslashes === true) {
+ base = utils.removeBackslashes(base);
+ }
+ }
+
+ const state = {
+ prefix,
+ input,
+ start,
+ base,
+ glob,
+ isBrace,
+ isBracket,
+ isGlob,
+ isExtglob,
+ isGlobstar,
+ negated,
+ negatedExtglob
+ };
+
+ if (opts.tokens === true) {
+ state.maxDepth = 0;
+ if (!isPathSeparator(code)) {
+ tokens.push(token);
+ }
+ state.tokens = tokens;
+ }
+
+ if (opts.parts === true || opts.tokens === true) {
+ let prevIndex;
+
+ for (let idx = 0; idx < slashes.length; idx++) {
+ const n = prevIndex ? prevIndex + 1 : start;
+ const i = slashes[idx];
+ const value = input.slice(n, i);
+ if (opts.tokens) {
+ if (idx === 0 && start !== 0) {
+ tokens[idx].isPrefix = true;
+ tokens[idx].value = prefix;
+ } else {
+ tokens[idx].value = value;
+ }
+ depth(tokens[idx]);
+ state.maxDepth += tokens[idx].depth;
+ }
+ if (idx !== 0 || value !== '') {
+ parts.push(value);
+ }
+ prevIndex = i;
+ }
+
+ if (prevIndex && prevIndex + 1 < input.length) {
+ const value = input.slice(prevIndex + 1);
+ parts.push(value);
+
+ if (opts.tokens) {
+ tokens[tokens.length - 1].value = value;
+ depth(tokens[tokens.length - 1]);
+ state.maxDepth += tokens[tokens.length - 1].depth;
+ }
+ }
+
+ state.slashes = slashes;
+ state.parts = parts;
+ }
+
+ return state;
+};
+
+module.exports = scan;
+
+
+/***/ }),
+/* 21 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+const constants = __webpack_require__(1);
+const utils = __webpack_require__(0);
+
+/**
+ * Constants
+ */
+
+const {
+ MAX_LENGTH,
+ POSIX_REGEX_SOURCE,
+ REGEX_NON_SPECIAL_CHARS,
+ REGEX_SPECIAL_CHARS_BACKREF,
+ REPLACEMENTS
+} = constants;
+
+/**
+ * Helpers
+ */
+
+const expandRange = (args, options) => {
+ if (typeof options.expandRange === 'function') {
+ return options.expandRange(...args, options);
+ }
+
+ args.sort();
+ const value = `[${args.join('-')}]`;
+
+ try {
+ /* eslint-disable-next-line no-new */
+ new RegExp(value);
+ } catch (ex) {
+ return args.map(v => utils.escapeRegex(v)).join('..');
+ }
+
+ return value;
+};
+
+/**
+ * Create the message for a syntax error
+ */
+
+const syntaxError = (type, char) => {
+ return `Missing ${type}: "${char}" - use "\\\\${char}" to match literal characters`;
+};
+
+/**
+ * Parse the given input string.
+ * @param {String} input
+ * @param {Object} options
+ * @return {Object}
+ */
+
+const parse = (input, options) => {
+ if (typeof input !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ input = REPLACEMENTS[input] || input;
+
+ const opts = { ...options };
+ const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
+
+ let len = input.length;
+ if (len > max) {
+ throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
+ }
+
+ const bos = { type: 'bos', value: '', output: opts.prepend || '' };
+ const tokens = [bos];
+
+ const capture = opts.capture ? '' : '?:';
+ const win32 = utils.isWindows(options);
+
+ // create constants based on platform, for windows or posix
+ const PLATFORM_CHARS = constants.globChars(win32);
+ const EXTGLOB_CHARS = constants.extglobChars(PLATFORM_CHARS);
+
+ const {
+ DOT_LITERAL,
+ PLUS_LITERAL,
+ SLASH_LITERAL,
+ ONE_CHAR,
+ DOTS_SLASH,
+ NO_DOT,
+ NO_DOT_SLASH,
+ NO_DOTS_SLASH,
+ QMARK,
+ QMARK_NO_DOT,
+ STAR,
+ START_ANCHOR
+ } = PLATFORM_CHARS;
+
+ const globstar = opts => {
+ return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
+ };
+
+ const nodot = opts.dot ? '' : NO_DOT;
+ const qmarkNoDot = opts.dot ? QMARK : QMARK_NO_DOT;
+ let star = opts.bash === true ? globstar(opts) : STAR;
+
+ if (opts.capture) {
+ star = `(${star})`;
+ }
+
+ // minimatch options support
+ if (typeof opts.noext === 'boolean') {
+ opts.noextglob = opts.noext;
+ }
+
+ const state = {
+ input,
+ index: -1,
+ start: 0,
+ dot: opts.dot === true,
+ consumed: '',
+ output: '',
+ prefix: '',
+ backtrack: false,
+ negated: false,
+ brackets: 0,
+ braces: 0,
+ parens: 0,
+ quotes: 0,
+ globstar: false,
+ tokens
+ };
+
+ input = utils.removePrefix(input, state);
+ len = input.length;
+
+ const extglobs = [];
+ const braces = [];
+ const stack = [];
+ let prev = bos;
+ let value;
+
+ /**
+ * Tokenizing helpers
+ */
+
+ const eos = () => state.index === len - 1;
+ const peek = state.peek = (n = 1) => input[state.index + n];
+ const advance = state.advance = () => input[++state.index] || '';
+ const remaining = () => input.slice(state.index + 1);
+ const consume = (value = '', num = 0) => {
+ state.consumed += value;
+ state.index += num;
+ };
+
+ const append = token => {
+ state.output += token.output != null ? token.output : token.value;
+ consume(token.value);
+ };
+
+ const negate = () => {
+ let count = 1;
+
+ while (peek() === '!' && (peek(2) !== '(' || peek(3) === '?')) {
+ advance();
+ state.start++;
+ count++;
+ }
+
+ if (count % 2 === 0) {
+ return false;
+ }
+
+ state.negated = true;
+ state.start++;
+ return true;
+ };
+
+ const increment = type => {
+ state[type]++;
+ stack.push(type);
+ };
+
+ const decrement = type => {
+ state[type]--;
+ stack.pop();
+ };
+
+ /**
+ * Push tokens onto the tokens array. This helper speeds up
+ * tokenizing by 1) helping us avoid backtracking as much as possible,
+ * and 2) helping us avoid creating extra tokens when consecutive
+ * characters are plain text. This improves performance and simplifies
+ * lookbehinds.
+ */
+
+ const push = tok => {
+ if (prev.type === 'globstar') {
+ const isBrace = state.braces > 0 && (tok.type === 'comma' || tok.type === 'brace');
+ const isExtglob = tok.extglob === true || (extglobs.length && (tok.type === 'pipe' || tok.type === 'paren'));
+
+ if (tok.type !== 'slash' && tok.type !== 'paren' && !isBrace && !isExtglob) {
+ state.output = state.output.slice(0, -prev.output.length);
+ prev.type = 'star';
+ prev.value = '*';
+ prev.output = star;
+ state.output += prev.output;
+ }
+ }
+
+ if (extglobs.length && tok.type !== 'paren') {
+ extglobs[extglobs.length - 1].inner += tok.value;
+ }
+
+ if (tok.value || tok.output) append(tok);
+ if (prev && prev.type === 'text' && tok.type === 'text') {
+ prev.value += tok.value;
+ prev.output = (prev.output || '') + tok.value;
+ return;
+ }
+
+ tok.prev = prev;
+ tokens.push(tok);
+ prev = tok;
+ };
+
+ const extglobOpen = (type, value) => {
+ const token = { ...EXTGLOB_CHARS[value], conditions: 1, inner: '' };
+
+ token.prev = prev;
+ token.parens = state.parens;
+ token.output = state.output;
+ const output = (opts.capture ? '(' : '') + token.open;
+
+ increment('parens');
+ push({ type, value, output: state.output ? '' : ONE_CHAR });
+ push({ type: 'paren', extglob: true, value: advance(), output });
+ extglobs.push(token);
+ };
+
+ const extglobClose = token => {
+ let output = token.close + (opts.capture ? ')' : '');
+ let rest;
+
+ if (token.type === 'negate') {
+ let extglobStar = star;
+
+ if (token.inner && token.inner.length > 1 && token.inner.includes('/')) {
+ extglobStar = globstar(opts);
+ }
+
+ if (extglobStar !== star || eos() || /^\)+$/.test(remaining())) {
+ output = token.close = `)$))${extglobStar}`;
+ }
+
+ if (token.inner.includes('*') && (rest = remaining()) && /^\.[^\\/.]+$/.test(rest)) {
+ // Any non-magical string (`.ts`) or even nested expression (`.{ts,tsx}`) can follow after the closing parenthesis.
+ // In this case, we need to parse the string and use it in the output of the original pattern.
+ // Suitable patterns: `/!(*.d).ts`, `/!(*.d).{ts,tsx}`, `**/!(*-dbg).@(js)`.
+ //
+ // Disabling the `fastpaths` option due to a problem with parsing strings as `.ts` in the pattern like `**/!(*.d).ts`.
+ const expression = parse(rest, { ...options, fastpaths: false }).output;
+
+ output = token.close = `)${expression})${extglobStar})`;
+ }
+
+ if (token.prev.type === 'bos') {
+ state.negatedExtglob = true;
+ }
+ }
+
+ push({ type: 'paren', extglob: true, value, output });
+ decrement('parens');
+ };
+
+ /**
+ * Fast paths
+ */
+
+ if (opts.fastpaths !== false && !/(^[*!]|[/()[\]{}"])/.test(input)) {
+ let backslashes = false;
+
+ let output = input.replace(REGEX_SPECIAL_CHARS_BACKREF, (m, esc, chars, first, rest, index) => {
+ if (first === '\\') {
+ backslashes = true;
+ return m;
+ }
+
+ if (first === '?') {
+ if (esc) {
+ return esc + first + (rest ? QMARK.repeat(rest.length) : '');
+ }
+ if (index === 0) {
+ return qmarkNoDot + (rest ? QMARK.repeat(rest.length) : '');
+ }
+ return QMARK.repeat(chars.length);
+ }
+
+ if (first === '.') {
+ return DOT_LITERAL.repeat(chars.length);
+ }
+
+ if (first === '*') {
+ if (esc) {
+ return esc + first + (rest ? star : '');
+ }
+ return star;
+ }
+ return esc ? m : `\\${m}`;
+ });
+
+ if (backslashes === true) {
+ if (opts.unescape === true) {
+ output = output.replace(/\\/g, '');
+ } else {
+ output = output.replace(/\\+/g, m => {
+ return m.length % 2 === 0 ? '\\\\' : (m ? '\\' : '');
+ });
+ }
+ }
+
+ if (output === input && opts.contains === true) {
+ state.output = input;
+ return state;
+ }
+
+ state.output = utils.wrapOutput(output, state, options);
+ return state;
+ }
+
+ /**
+ * Tokenize input until we reach end-of-string
+ */
+
+ while (!eos()) {
+ value = advance();
+
+ if (value === '\u0000') {
+ continue;
+ }
+
+ /**
+ * Escaped characters
+ */
+
+ if (value === '\\') {
+ const next = peek();
+
+ if (next === '/' && opts.bash !== true) {
+ continue;
+ }
+
+ if (next === '.' || next === ';') {
+ continue;
+ }
+
+ if (!next) {
+ value += '\\';
+ push({ type: 'text', value });
+ continue;
+ }
+
+ // collapse slashes to reduce potential for exploits
+ const match = /^\\+/.exec(remaining());
+ let slashes = 0;
+
+ if (match && match[0].length > 2) {
+ slashes = match[0].length;
+ state.index += slashes;
+ if (slashes % 2 !== 0) {
+ value += '\\';
+ }
+ }
+
+ if (opts.unescape === true) {
+ value = advance();
+ } else {
+ value += advance();
+ }
+
+ if (state.brackets === 0) {
+ push({ type: 'text', value });
+ continue;
+ }
+ }
+
+ /**
+ * If we're inside a regex character class, continue
+ * until we reach the closing bracket.
+ */
+
+ if (state.brackets > 0 && (value !== ']' || prev.value === '[' || prev.value === '[^')) {
+ if (opts.posix !== false && value === ':') {
+ const inner = prev.value.slice(1);
+ if (inner.includes('[')) {
+ prev.posix = true;
+
+ if (inner.includes(':')) {
+ const idx = prev.value.lastIndexOf('[');
+ const pre = prev.value.slice(0, idx);
+ const rest = prev.value.slice(idx + 2);
+ const posix = POSIX_REGEX_SOURCE[rest];
+ if (posix) {
+ prev.value = pre + posix;
+ state.backtrack = true;
+ advance();
+
+ if (!bos.output && tokens.indexOf(prev) === 1) {
+ bos.output = ONE_CHAR;
+ }
+ continue;
+ }
+ }
+ }
+ }
+
+ if ((value === '[' && peek() !== ':') || (value === '-' && peek() === ']')) {
+ value = `\\${value}`;
+ }
+
+ if (value === ']' && (prev.value === '[' || prev.value === '[^')) {
+ value = `\\${value}`;
+ }
+
+ if (opts.posix === true && value === '!' && prev.value === '[') {
+ value = '^';
+ }
+
+ prev.value += value;
+ append({ value });
+ continue;
+ }
+
+ /**
+ * If we're inside a quoted string, continue
+ * until we reach the closing double quote.
+ */
+
+ if (state.quotes === 1 && value !== '"') {
+ value = utils.escapeRegex(value);
+ prev.value += value;
+ append({ value });
+ continue;
+ }
+
+ /**
+ * Double quotes
+ */
+
+ if (value === '"') {
+ state.quotes = state.quotes === 1 ? 0 : 1;
+ if (opts.keepQuotes === true) {
+ push({ type: 'text', value });
+ }
+ continue;
+ }
+
+ /**
+ * Parentheses
+ */
+
+ if (value === '(') {
+ increment('parens');
+ push({ type: 'paren', value });
+ continue;
+ }
+
+ if (value === ')') {
+ if (state.parens === 0 && opts.strictBrackets === true) {
+ throw new SyntaxError(syntaxError('opening', '('));
+ }
+
+ const extglob = extglobs[extglobs.length - 1];
+ if (extglob && state.parens === extglob.parens + 1) {
+ extglobClose(extglobs.pop());
+ continue;
+ }
+
+ push({ type: 'paren', value, output: state.parens ? ')' : '\\)' });
+ decrement('parens');
+ continue;
+ }
+
+ /**
+ * Square brackets
+ */
+
+ if (value === '[') {
+ if (opts.nobracket === true || !remaining().includes(']')) {
+ if (opts.nobracket !== true && opts.strictBrackets === true) {
+ throw new SyntaxError(syntaxError('closing', ']'));
+ }
+
+ value = `\\${value}`;
+ } else {
+ increment('brackets');
+ }
+
+ push({ type: 'bracket', value });
+ continue;
+ }
+
+ if (value === ']') {
+ if (opts.nobracket === true || (prev && prev.type === 'bracket' && prev.value.length === 1)) {
+ push({ type: 'text', value, output: `\\${value}` });
+ continue;
+ }
+
+ if (state.brackets === 0) {
+ if (opts.strictBrackets === true) {
+ throw new SyntaxError(syntaxError('opening', '['));
+ }
+
+ push({ type: 'text', value, output: `\\${value}` });
+ continue;
+ }
+
+ decrement('brackets');
+
+ const prevValue = prev.value.slice(1);
+ if (prev.posix !== true && prevValue[0] === '^' && !prevValue.includes('/')) {
+ value = `/${value}`;
+ }
+
+ prev.value += value;
+ append({ value });
+
+ // when literal brackets are explicitly disabled
+ // assume we should match with a regex character class
+ if (opts.literalBrackets === false || utils.hasRegexChars(prevValue)) {
+ continue;
+ }
+
+ const escaped = utils.escapeRegex(prev.value);
+ state.output = state.output.slice(0, -prev.value.length);
+
+ // when literal brackets are explicitly enabled
+ // assume we should escape the brackets to match literal characters
+ if (opts.literalBrackets === true) {
+ state.output += escaped;
+ prev.value = escaped;
+ continue;
+ }
+
+ // when the user specifies nothing, try to match both
+ prev.value = `(${capture}${escaped}|${prev.value})`;
+ state.output += prev.value;
+ continue;
+ }
+
+ /**
+ * Braces
+ */
+
+ if (value === '{' && opts.nobrace !== true) {
+ increment('braces');
+
+ const open = {
+ type: 'brace',
+ value,
+ output: '(',
+ outputIndex: state.output.length,
+ tokensIndex: state.tokens.length
+ };
+
+ braces.push(open);
+ push(open);
+ continue;
+ }
+
+ if (value === '}') {
+ const brace = braces[braces.length - 1];
+
+ if (opts.nobrace === true || !brace) {
+ push({ type: 'text', value, output: value });
+ continue;
+ }
+
+ let output = ')';
+
+ if (brace.dots === true) {
+ const arr = tokens.slice();
+ const range = [];
+
+ for (let i = arr.length - 1; i >= 0; i--) {
+ tokens.pop();
+ if (arr[i].type === 'brace') {
+ break;
+ }
+ if (arr[i].type !== 'dots') {
+ range.unshift(arr[i].value);
+ }
+ }
+
+ output = expandRange(range, opts);
+ state.backtrack = true;
+ }
+
+ if (brace.comma !== true && brace.dots !== true) {
+ const out = state.output.slice(0, brace.outputIndex);
+ const toks = state.tokens.slice(brace.tokensIndex);
+ brace.value = brace.output = '\\{';
+ value = output = '\\}';
+ state.output = out;
+ for (const t of toks) {
+ state.output += (t.output || t.value);
+ }
+ }
+
+ push({ type: 'brace', value, output });
+ decrement('braces');
+ braces.pop();
+ continue;
+ }
+
+ /**
+ * Pipes
+ */
+
+ if (value === '|') {
+ if (extglobs.length > 0) {
+ extglobs[extglobs.length - 1].conditions++;
+ }
+ push({ type: 'text', value });
+ continue;
+ }
+
+ /**
+ * Commas
+ */
+
+ if (value === ',') {
+ let output = value;
+
+ const brace = braces[braces.length - 1];
+ if (brace && stack[stack.length - 1] === 'braces') {
+ brace.comma = true;
+ output = '|';
+ }
+
+ push({ type: 'comma', value, output });
+ continue;
+ }
+
+ /**
+ * Slashes
+ */
+
+ if (value === '/') {
+ // if the beginning of the glob is "./", advance the start
+ // to the current index, and don't add the "./" characters
+ // to the state. This greatly simplifies lookbehinds when
+ // checking for BOS characters like "!" and "." (not "./")
+ if (prev.type === 'dot' && state.index === state.start + 1) {
+ state.start = state.index + 1;
+ state.consumed = '';
+ state.output = '';
+ tokens.pop();
+ prev = bos; // reset "prev" to the first token
+ continue;
+ }
+
+ push({ type: 'slash', value, output: SLASH_LITERAL });
+ continue;
+ }
+
+ /**
+ * Dots
+ */
+
+ if (value === '.') {
+ if (state.braces > 0 && prev.type === 'dot') {
+ if (prev.value === '.') prev.output = DOT_LITERAL;
+ const brace = braces[braces.length - 1];
+ prev.type = 'dots';
+ prev.output += value;
+ prev.value += value;
+ brace.dots = true;
+ continue;
+ }
+
+ if ((state.braces + state.parens) === 0 && prev.type !== 'bos' && prev.type !== 'slash') {
+ push({ type: 'text', value, output: DOT_LITERAL });
+ continue;
+ }
+
+ push({ type: 'dot', value, output: DOT_LITERAL });
+ continue;
+ }
+
+ /**
+ * Question marks
+ */
+
+ if (value === '?') {
+ const isGroup = prev && prev.value === '(';
+ if (!isGroup && opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
+ extglobOpen('qmark', value);
+ continue;
+ }
+
+ if (prev && prev.type === 'paren') {
+ const next = peek();
+ let output = value;
+
+ if (next === '<' && !utils.supportsLookbehinds()) {
+ throw new Error('Node.js v10 or higher is required for regex lookbehinds');
+ }
+
+ if ((prev.value === '(' && !/[!=<:]/.test(next)) || (next === '<' && !/<([!=]|\w+>)/.test(remaining()))) {
+ output = `\\${value}`;
+ }
+
+ push({ type: 'text', value, output });
+ continue;
+ }
+
+ if (opts.dot !== true && (prev.type === 'slash' || prev.type === 'bos')) {
+ push({ type: 'qmark', value, output: QMARK_NO_DOT });
+ continue;
+ }
+
+ push({ type: 'qmark', value, output: QMARK });
+ continue;
+ }
+
+ /**
+ * Exclamation
+ */
+
+ if (value === '!') {
+ if (opts.noextglob !== true && peek() === '(') {
+ if (peek(2) !== '?' || !/[!=<:]/.test(peek(3))) {
+ extglobOpen('negate', value);
+ continue;
+ }
+ }
+
+ if (opts.nonegate !== true && state.index === 0) {
+ negate();
+ continue;
+ }
+ }
+
+ /**
+ * Plus
+ */
+
+ if (value === '+') {
+ if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
+ extglobOpen('plus', value);
+ continue;
+ }
+
+ if ((prev && prev.value === '(') || opts.regex === false) {
+ push({ type: 'plus', value, output: PLUS_LITERAL });
+ continue;
+ }
+
+ if ((prev && (prev.type === 'bracket' || prev.type === 'paren' || prev.type === 'brace')) || state.parens > 0) {
+ push({ type: 'plus', value });
+ continue;
+ }
+
+ push({ type: 'plus', value: PLUS_LITERAL });
+ continue;
+ }
+
+ /**
+ * Plain text
+ */
+
+ if (value === '@') {
+ if (opts.noextglob !== true && peek() === '(' && peek(2) !== '?') {
+ push({ type: 'at', extglob: true, value, output: '' });
+ continue;
+ }
+
+ push({ type: 'text', value });
+ continue;
+ }
+
+ /**
+ * Plain text
+ */
+
+ if (value !== '*') {
+ if (value === '$' || value === '^') {
+ value = `\\${value}`;
+ }
+
+ const match = REGEX_NON_SPECIAL_CHARS.exec(remaining());
+ if (match) {
+ value += match[0];
+ state.index += match[0].length;
+ }
+
+ push({ type: 'text', value });
+ continue;
+ }
+
+ /**
+ * Stars
+ */
+
+ if (prev && (prev.type === 'globstar' || prev.star === true)) {
+ prev.type = 'star';
+ prev.star = true;
+ prev.value += value;
+ prev.output = star;
+ state.backtrack = true;
+ state.globstar = true;
+ consume(value);
+ continue;
+ }
+
+ let rest = remaining();
+ if (opts.noextglob !== true && /^\([^?]/.test(rest)) {
+ extglobOpen('star', value);
+ continue;
+ }
+
+ if (prev.type === 'star') {
+ if (opts.noglobstar === true) {
+ consume(value);
+ continue;
+ }
+
+ const prior = prev.prev;
+ const before = prior.prev;
+ const isStart = prior.type === 'slash' || prior.type === 'bos';
+ const afterStar = before && (before.type === 'star' || before.type === 'globstar');
+
+ if (opts.bash === true && (!isStart || (rest[0] && rest[0] !== '/'))) {
+ push({ type: 'star', value, output: '' });
+ continue;
+ }
+
+ const isBrace = state.braces > 0 && (prior.type === 'comma' || prior.type === 'brace');
+ const isExtglob = extglobs.length && (prior.type === 'pipe' || prior.type === 'paren');
+ if (!isStart && prior.type !== 'paren' && !isBrace && !isExtglob) {
+ push({ type: 'star', value, output: '' });
+ continue;
+ }
+
+ // strip consecutive `/**/`
+ while (rest.slice(0, 3) === '/**') {
+ const after = input[state.index + 4];
+ if (after && after !== '/') {
+ break;
+ }
+ rest = rest.slice(3);
+ consume('/**', 3);
+ }
+
+ if (prior.type === 'bos' && eos()) {
+ prev.type = 'globstar';
+ prev.value += value;
+ prev.output = globstar(opts);
+ state.output = prev.output;
+ state.globstar = true;
+ consume(value);
+ continue;
+ }
+
+ if (prior.type === 'slash' && prior.prev.type !== 'bos' && !afterStar && eos()) {
+ state.output = state.output.slice(0, -(prior.output + prev.output).length);
+ prior.output = `(?:${prior.output}`;
+
+ prev.type = 'globstar';
+ prev.output = globstar(opts) + (opts.strictSlashes ? ')' : '|$)');
+ prev.value += value;
+ state.globstar = true;
+ state.output += prior.output + prev.output;
+ consume(value);
+ continue;
+ }
+
+ if (prior.type === 'slash' && prior.prev.type !== 'bos' && rest[0] === '/') {
+ const end = rest[1] !== void 0 ? '|$' : '';
+
+ state.output = state.output.slice(0, -(prior.output + prev.output).length);
+ prior.output = `(?:${prior.output}`;
+
+ prev.type = 'globstar';
+ prev.output = `${globstar(opts)}${SLASH_LITERAL}|${SLASH_LITERAL}${end})`;
+ prev.value += value;
+
+ state.output += prior.output + prev.output;
+ state.globstar = true;
+
+ consume(value + advance());
+
+ push({ type: 'slash', value: '/', output: '' });
+ continue;
+ }
+
+ if (prior.type === 'bos' && rest[0] === '/') {
+ prev.type = 'globstar';
+ prev.value += value;
+ prev.output = `(?:^|${SLASH_LITERAL}|${globstar(opts)}${SLASH_LITERAL})`;
+ state.output = prev.output;
+ state.globstar = true;
+ consume(value + advance());
+ push({ type: 'slash', value: '/', output: '' });
+ continue;
+ }
+
+ // remove single star from output
+ state.output = state.output.slice(0, -prev.output.length);
+
+ // reset previous token to globstar
+ prev.type = 'globstar';
+ prev.output = globstar(opts);
+ prev.value += value;
+
+ // reset output with globstar
+ state.output += prev.output;
+ state.globstar = true;
+ consume(value);
+ continue;
+ }
+
+ const token = { type: 'star', value, output: star };
+
+ if (opts.bash === true) {
+ token.output = '.*?';
+ if (prev.type === 'bos' || prev.type === 'slash') {
+ token.output = nodot + token.output;
+ }
+ push(token);
+ continue;
+ }
+
+ if (prev && (prev.type === 'bracket' || prev.type === 'paren') && opts.regex === true) {
+ token.output = value;
+ push(token);
+ continue;
+ }
+
+ if (state.index === state.start || prev.type === 'slash' || prev.type === 'dot') {
+ if (prev.type === 'dot') {
+ state.output += NO_DOT_SLASH;
+ prev.output += NO_DOT_SLASH;
+
+ } else if (opts.dot === true) {
+ state.output += NO_DOTS_SLASH;
+ prev.output += NO_DOTS_SLASH;
+
+ } else {
+ state.output += nodot;
+ prev.output += nodot;
+ }
+
+ if (peek() !== '*') {
+ state.output += ONE_CHAR;
+ prev.output += ONE_CHAR;
+ }
+ }
+
+ push(token);
+ }
+
+ while (state.brackets > 0) {
+ if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ']'));
+ state.output = utils.escapeLast(state.output, '[');
+ decrement('brackets');
+ }
+
+ while (state.parens > 0) {
+ if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', ')'));
+ state.output = utils.escapeLast(state.output, '(');
+ decrement('parens');
+ }
+
+ while (state.braces > 0) {
+ if (opts.strictBrackets === true) throw new SyntaxError(syntaxError('closing', '}'));
+ state.output = utils.escapeLast(state.output, '{');
+ decrement('braces');
+ }
+
+ if (opts.strictSlashes !== true && (prev.type === 'star' || prev.type === 'bracket')) {
+ push({ type: 'maybe_slash', value: '', output: `${SLASH_LITERAL}?` });
+ }
+
+ // rebuild the output if we had to backtrack at any point
+ if (state.backtrack === true) {
+ state.output = '';
+
+ for (const token of state.tokens) {
+ state.output += token.output != null ? token.output : token.value;
+
+ if (token.suffix) {
+ state.output += token.suffix;
+ }
+ }
+ }
+
+ return state;
+};
+
+/**
+ * Fast paths for creating regular expressions for common glob patterns.
+ * This can significantly speed up processing and has very little downside
+ * impact when none of the fast paths match.
+ */
+
+parse.fastpaths = (input, options) => {
+ const opts = { ...options };
+ const max = typeof opts.maxLength === 'number' ? Math.min(MAX_LENGTH, opts.maxLength) : MAX_LENGTH;
+ const len = input.length;
+ if (len > max) {
+ throw new SyntaxError(`Input length: ${len}, exceeds maximum allowed length: ${max}`);
+ }
+
+ input = REPLACEMENTS[input] || input;
+ const win32 = utils.isWindows(options);
+
+ // create constants based on platform, for windows or posix
+ const {
+ DOT_LITERAL,
+ SLASH_LITERAL,
+ ONE_CHAR,
+ DOTS_SLASH,
+ NO_DOT,
+ NO_DOTS,
+ NO_DOTS_SLASH,
+ STAR,
+ START_ANCHOR
+ } = constants.globChars(win32);
+
+ const nodot = opts.dot ? NO_DOTS : NO_DOT;
+ const slashDot = opts.dot ? NO_DOTS_SLASH : NO_DOT;
+ const capture = opts.capture ? '' : '?:';
+ const state = { negated: false, prefix: '' };
+ let star = opts.bash === true ? '.*?' : STAR;
+
+ if (opts.capture) {
+ star = `(${star})`;
+ }
+
+ const globstar = opts => {
+ if (opts.noglobstar === true) return star;
+ return `(${capture}(?:(?!${START_ANCHOR}${opts.dot ? DOTS_SLASH : DOT_LITERAL}).)*?)`;
+ };
+
+ const create = str => {
+ switch (str) {
+ case '*':
+ return `${nodot}${ONE_CHAR}${star}`;
+
+ case '.*':
+ return `${DOT_LITERAL}${ONE_CHAR}${star}`;
+
+ case '*.*':
+ return `${nodot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
+
+ case '*/*':
+ return `${nodot}${star}${SLASH_LITERAL}${ONE_CHAR}${slashDot}${star}`;
+
+ case '**':
+ return nodot + globstar(opts);
+
+ case '**/*':
+ return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${ONE_CHAR}${star}`;
+
+ case '**/*.*':
+ return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${slashDot}${star}${DOT_LITERAL}${ONE_CHAR}${star}`;
+
+ case '**/.*':
+ return `(?:${nodot}${globstar(opts)}${SLASH_LITERAL})?${DOT_LITERAL}${ONE_CHAR}${star}`;
+
+ default: {
+ const match = /^(.*?)\.(\w+)$/.exec(str);
+ if (!match) return;
+
+ const source = create(match[1]);
+ if (!source) return;
+
+ return source + DOT_LITERAL + match[2];
+ }
+ }
+ };
+
+ const output = utils.removePrefix(input, state);
+ let source = create(output);
+
+ if (source && opts.strictSlashes !== true) {
+ source += `${SLASH_LITERAL}?`;
+ }
+
+ return source;
+};
+
+module.exports = parse;
+
+
+/***/ })
+/******/ ]); \ No newline at end of file
diff --git a/devtools/client/shared/vendor/micromatch/moz.build b/devtools/client/shared/vendor/micromatch/moz.build
new file mode 100644
index 0000000000..b0b5500e6f
--- /dev/null
+++ b/devtools/client/shared/vendor/micromatch/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "micromatch.js",
+)
diff --git a/devtools/client/shared/vendor/micromatch/package-lock.json b/devtools/client/shared/vendor/micromatch/package-lock.json
new file mode 100644
index 0000000000..3a54d107be
--- /dev/null
+++ b/devtools/client/shared/vendor/micromatch/package-lock.json
@@ -0,0 +1,4497 @@
+{
+ "name": "micromatch",
+ "version": "4.0.4",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "micromatch",
+ "version": "4.0.4",
+ "license": "MIT",
+ "dependencies": {
+ "micromatch": "^4.0.4"
+ },
+ "devDependencies": {
+ "webpack": "^4.46.0",
+ "webpack-cli": "^4.10.0"
+ }
+ },
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+ "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
+ "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+ "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+ "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-code-frame": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
+ "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/wast-printer": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-fsm": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
+ "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-module-context": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
+ "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+ "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+ "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+ "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
+ "dev": true,
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+ "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
+ "dev": true,
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+ "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
+ "dev": true
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+ "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/helper-wasm-section": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-opt": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "@webassemblyjs/wast-printer": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+ "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+ "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+ "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
+ "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/floating-point-hex-parser": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-code-frame": "1.9.0",
+ "@webassemblyjs/helper-fsm": "1.9.0",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+ "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webpack-cli/configtest": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
+ "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
+ "dev": true,
+ "peerDependencies": {
+ "webpack": "4.x.x || 5.x.x",
+ "webpack-cli": "4.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/info": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
+ "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
+ "dev": true,
+ "dependencies": {
+ "envinfo": "^7.7.3"
+ },
+ "peerDependencies": {
+ "webpack-cli": "4.x.x"
+ }
+ },
+ "node_modules/@webpack-cli/serve": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
+ "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
+ "dev": true,
+ "peerDependencies": {
+ "webpack-cli": "4.x.x"
+ },
+ "peerDependenciesMeta": {
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
+ },
+ "node_modules/acorn": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-errors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+ "dev": true,
+ "peerDependencies": {
+ "ajv": ">=5.0.0"
+ }
+ },
+ "node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/aproba": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "dev": true
+ },
+ "node_modules/arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/asn1.js/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/assert": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+ "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+ "dev": true,
+ "dependencies": {
+ "object-assign": "^4.1.1",
+ "util": "0.10.3"
+ }
+ },
+ "node_modules/assert/node_modules/inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA==",
+ "dev": true
+ },
+ "node_modules/assert/node_modules/util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "2.0.1"
+ }
+ },
+ "node_modules/assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/async-each": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.6.tgz",
+ "integrity": "sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "optional": true
+ },
+ "node_modules/atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true,
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "dependencies": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/base/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "node_modules/bn.js": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+ "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
+ "dev": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "dependencies": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/braces/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/braces/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
+ "dev": true
+ },
+ "node_modules/browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "dependencies": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "dependencies": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "node_modules/browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/browserify-rsa": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz",
+ "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^5.0.0",
+ "randombytes": "^2.0.1"
+ }
+ },
+ "node_modules/browserify-sign": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
+ "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^5.1.1",
+ "browserify-rsa": "^4.0.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.5.3",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.5",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ }
+ },
+ "node_modules/browserify-sign/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "dependencies": {
+ "pako": "~1.0.5"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+ "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+ "dev": true,
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true
+ },
+ "node_modules/buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
+ "dev": true
+ },
+ "node_modules/builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==",
+ "dev": true
+ },
+ "node_modules/cacache": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "dependencies": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "node_modules/cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "dependencies": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+ "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "optional": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chokidar/node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chokidar/node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/chokidar/node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+ "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/class-utils/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==",
+ "dev": true,
+ "dependencies": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz",
+ "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==",
+ "dev": true
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
+ "dev": true
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "engines": [
+ "node >= 0.8"
+ ],
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "node_modules/console-browserify": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+ "dev": true
+ },
+ "node_modules/constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==",
+ "dev": true
+ },
+ "node_modules/copy-concurrently": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+ "dev": true,
+ "dependencies": {
+ "aproba": "^1.1.1",
+ "fs-write-stream-atomic": "^1.0.8",
+ "iferr": "^0.1.5",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.0"
+ }
+ },
+ "node_modules/copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true
+ },
+ "node_modules/create-ecdh": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+ "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.5.3"
+ }
+ },
+ "node_modules/create-ecdh/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "dependencies": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "node_modules/create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "dependencies": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "dependencies": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/cyclist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+ "integrity": "sha512-NJGVKPS81XejHcLhaLJS7plab0fK3slPh11mESeeDq2W4ZI5kUKK/LRRdVDvjJseojbPB7ZwjnyOybg3Igea/A==",
+ "dev": true
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/decode-uri-component": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
+ "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/des.js": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+ "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ }
+ },
+ "node_modules/diffie-hellman/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4",
+ "npm": ">=1.2"
+ }
+ },
+ "node_modules/duplexify": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "node_modules/elliptic": {
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+ "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.11.9",
+ "brorand": "^1.1.0",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.1",
+ "inherits": "^2.0.4",
+ "minimalistic-assert": "^1.0.1",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/elliptic/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz",
+ "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.5.0",
+ "tapable": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/enhanced-resolve/node_modules/memory-fs": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+ "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+ "dev": true,
+ "dependencies": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4.3.0 <5.0.0 || >=5.10"
+ }
+ },
+ "node_modules/envinfo": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==",
+ "dev": true,
+ "bin": {
+ "envinfo": "dist/cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/errno": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+ "dev": true,
+ "dependencies": {
+ "prr": "~1.0.1"
+ },
+ "bin": {
+ "errno": "cli.js"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+ "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "dependencies": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
+ "dev": true,
+ "dependencies": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/expand-brackets/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==",
+ "dev": true,
+ "dependencies": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "dependencies": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/extglob/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "node_modules/fastest-levenshtein": {
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.9.1"
+ }
+ },
+ "node_modules/figgy-pudding": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
+ "dev": true
+ },
+ "node_modules/file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fill-range/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fill-range/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "dependencies": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/flush-write-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.3.6"
+ }
+ },
+ "node_modules/for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==",
+ "dev": true,
+ "dependencies": {
+ "map-cache": "^0.2.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/from2": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+ "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "node_modules/fs-write-stream-atomic": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+ "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "iferr": "^0.1.5",
+ "imurmurhash": "^0.1.4",
+ "readable-stream": "1 || 2"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "node_modules/get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
+ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==",
+ "dev": true
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/has-values/node_modules/kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/hash-base": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/hash-base/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "node_modules/hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+ "dev": true,
+ "dependencies": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "node_modules/https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==",
+ "dev": true
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/iferr": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+ "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==",
+ "dev": true
+ },
+ "node_modules/import-local": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
+ "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==",
+ "dev": true,
+ "dependencies": {
+ "pkg-dir": "^4.2.0",
+ "resolve-cwd": "^3.0.0"
+ },
+ "bin": {
+ "import-local-fixture": "fixtures/cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-local/node_modules/find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/import-local/node_modules/locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/import-local/node_modules/p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/import-local/node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/import-local/node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "dev": true
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/interpret": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz",
+ "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "node_modules/is-core-module": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+ "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+ "dev": true,
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "dependencies": {
+ "is-plain-object": "^2.0.4"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/loader-runner": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+ "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.3.0 <5.0.0 || >=5.10"
+ }
+ },
+ "node_modules/loader-utils": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz",
+ "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==",
+ "dev": true,
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "dependencies": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==",
+ "dev": true,
+ "dependencies": {
+ "object-visit": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==",
+ "dev": true,
+ "dependencies": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/micromatch/node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/micromatch/node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/micromatch/node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ },
+ "bin": {
+ "miller-rabin": "bin/miller-rabin"
+ }
+ },
+ "node_modules/miller-rabin/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "node_modules/minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
+ "dev": true
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "dev": true,
+ "dependencies": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "dependencies": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/move-concurrently": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+ "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==",
+ "dev": true,
+ "dependencies": {
+ "aproba": "^1.1.1",
+ "copy-concurrently": "^1.0.0",
+ "fs-write-stream-atomic": "^1.0.8",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.3"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true
+ },
+ "node_modules/nan": {
+ "version": "2.17.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz",
+ "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "node_modules/node-libs-browser": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+ "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
+ "dev": true,
+ "dependencies": {
+ "assert": "^1.1.1",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^4.3.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "^1.0.0",
+ "crypto-browserify": "^3.11.0",
+ "domain-browser": "^1.1.1",
+ "events": "^3.0.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "0.0.1",
+ "process": "^0.11.10",
+ "punycode": "^1.2.4",
+ "querystring-es3": "^0.2.0",
+ "readable-stream": "^2.3.3",
+ "stream-browserify": "^2.0.1",
+ "stream-http": "^2.7.2",
+ "string_decoder": "^1.0.0",
+ "timers-browserify": "^2.0.4",
+ "tty-browserify": "0.0.0",
+ "url": "^0.11.0",
+ "util": "^0.11.0",
+ "vm-browserify": "^1.0.1"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==",
+ "dev": true,
+ "dependencies": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-copy/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==",
+ "dev": true,
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==",
+ "dev": true
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "dev": true,
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "node_modules/parallel-transform": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+ "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+ "dev": true,
+ "dependencies": {
+ "cyclist": "^1.0.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.1.5"
+ }
+ },
+ "node_modules/parse-asn1": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
+ "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
+ "dev": true,
+ "dependencies": {
+ "asn1.js": "^5.2.0",
+ "browserify-aes": "^1.0.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node_modules/pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+ "dev": true
+ },
+ "node_modules/path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true
+ },
+ "node_modules/pbkdf2": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
+ "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
+ "dev": true,
+ "dependencies": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "dependencies": {
+ "find-up": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "node_modules/promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
+ "dev": true
+ },
+ "node_modules/prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
+ "dev": true
+ },
+ "node_modules/public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "dependencies": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "node_modules/public-encrypt/node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==",
+ "dev": true
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/pumpify": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "dev": true,
+ "dependencies": {
+ "duplexify": "^3.6.0",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ }
+ },
+ "node_modules/pumpify/node_modules/pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
+ "dev": true
+ },
+ "node_modules/querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
+ "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true
+ },
+ "node_modules/readable-stream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/rechoir": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
+ "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
+ "dev": true,
+ "dependencies": {
+ "resolve": "^1.9.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==",
+ "dev": true,
+ "optional": true
+ },
+ "node_modules/repeat-element": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz",
+ "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+ "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+ "dev": true,
+ "dependencies": {
+ "is-core-module": "^2.9.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-cwd": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz",
+ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==",
+ "dev": true,
+ "dependencies": {
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
+ "deprecated": "https://github.com/lydell/resolve-url#deprecated",
+ "dev": true
+ },
+ "node_modules/ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ }
+ },
+ "node_modules/ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "dependencies": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "node_modules/run-queue": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+ "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==",
+ "dev": true,
+ "dependencies": {
+ "aproba": "^1.1.1"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==",
+ "dev": true,
+ "dependencies": {
+ "ret": "~0.1.10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "node_modules/schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/set-value/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+ "dev": true
+ },
+ "node_modules/sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ },
+ "bin": {
+ "sha.js": "bin.js"
+ }
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "dependencies": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-node/node_modules/define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon-util/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
+ "dev": true,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/snapdragon/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-list-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+ "dev": true
+ },
+ "node_modules/source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated",
+ "dev": true,
+ "dependencies": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-url": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+ "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+ "deprecated": "See https://github.com/lydell/source-map-url#deprecated",
+ "dev": true
+ },
+ "node_modules/split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "dependencies": {
+ "extend-shallow": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ssri": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz",
+ "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==",
+ "dev": true,
+ "dependencies": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "node_modules/static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
+ "dev": true,
+ "dependencies": {
+ "is-descriptor": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "dependencies": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/static-extend/node_modules/kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stream-browserify": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "node_modules/stream-each": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+ "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+ "dev": true,
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "node_modules/stream-http": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "dev": true,
+ "dependencies": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "node_modules/stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+ "dev": true
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/terser": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.1.tgz",
+ "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==",
+ "dev": true,
+ "dependencies": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+ "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+ "dev": true,
+ "dependencies": {
+ "cacache": "^12.0.2",
+ "find-cache-dir": "^2.1.0",
+ "is-wsl": "^1.1.0",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^4.0.0",
+ "source-map": "^0.6.1",
+ "terser": "^4.1.2",
+ "webpack-sources": "^1.4.0",
+ "worker-farm": "^1.7.0"
+ },
+ "engines": {
+ "node": ">= 6.9.0"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/terser/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "node_modules/timers-browserify": {
+ "version": "2.0.12",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz",
+ "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==",
+ "dev": true,
+ "dependencies": {
+ "setimmediate": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==",
+ "dev": true
+ },
+ "node_modules/to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==",
+ "dev": true,
+ "dependencies": {
+ "kind-of": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-object-path/node_modules/kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
+ "dev": true,
+ "dependencies": {
+ "is-buffer": "^1.1.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "dependencies": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tty-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==",
+ "dev": true
+ },
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
+ "dev": true
+ },
+ "node_modules/union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "dependencies": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/union-value/node_modules/is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unique-filename": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "dev": true,
+ "dependencies": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "node_modules/unique-slug": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+ "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+ "dev": true,
+ "dependencies": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "node_modules/unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==",
+ "dev": true,
+ "dependencies": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+ "dev": true,
+ "dependencies": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+ "dev": true,
+ "dependencies": {
+ "isarray": "1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/unset-value/node_modules/has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=4",
+ "yarn": "*"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/uri-js/node_modules/punycode": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
+ "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==",
+ "deprecated": "Please see https://github.com/lydell/urix#deprecated",
+ "dev": true
+ },
+ "node_modules/url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ }
+ },
+ "node_modules/url/node_modules/punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
+ "dev": true
+ },
+ "node_modules/use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/util": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+ "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+ "dev": true,
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true
+ },
+ "node_modules/util/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
+ "dev": true
+ },
+ "node_modules/vm-browserify": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+ "dev": true
+ },
+ "node_modules/watchpack": {
+ "version": "1.7.5",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz",
+ "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==",
+ "dev": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "neo-async": "^2.5.0"
+ },
+ "optionalDependencies": {
+ "chokidar": "^3.4.1",
+ "watchpack-chokidar2": "^2.0.1"
+ }
+ },
+ "node_modules/watchpack-chokidar2": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz",
+ "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "chokidar": "^2.1.8"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "remove-trailing-separator": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ },
+ "optionalDependencies": {
+ "fsevents": "^1.2.7"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "dependencies": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ },
+ "engines": {
+ "node": ">= 4.0"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "is-extglob": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "binary-extensions": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/watchpack-chokidar2/node_modules/readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "optional": true,
+ "dependencies": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/webpack": {
+ "version": "4.46.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.46.0.tgz",
+ "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==",
+ "dev": true,
+ "dependencies": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/wasm-edit": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "acorn": "^6.4.1",
+ "ajv": "^6.10.2",
+ "ajv-keywords": "^3.4.1",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^4.5.0",
+ "eslint-scope": "^4.0.3",
+ "json-parse-better-errors": "^1.0.2",
+ "loader-runner": "^2.4.0",
+ "loader-utils": "^1.2.3",
+ "memory-fs": "^0.4.1",
+ "micromatch": "^3.1.10",
+ "mkdirp": "^0.5.3",
+ "neo-async": "^2.6.1",
+ "node-libs-browser": "^2.2.1",
+ "schema-utils": "^1.0.0",
+ "tapable": "^1.1.3",
+ "terser-webpack-plugin": "^1.4.3",
+ "watchpack": "^1.7.4",
+ "webpack-sources": "^1.4.1"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=6.11.5"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ },
+ "webpack-command": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli": {
+ "version": "4.10.0",
+ "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
+ "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
+ "dev": true,
+ "dependencies": {
+ "@discoveryjs/json-ext": "^0.5.0",
+ "@webpack-cli/configtest": "^1.2.0",
+ "@webpack-cli/info": "^1.5.0",
+ "@webpack-cli/serve": "^1.7.0",
+ "colorette": "^2.0.14",
+ "commander": "^7.0.0",
+ "cross-spawn": "^7.0.3",
+ "fastest-levenshtein": "^1.0.12",
+ "import-local": "^3.0.2",
+ "interpret": "^2.2.0",
+ "rechoir": "^0.7.0",
+ "webpack-merge": "^5.7.3"
+ },
+ "bin": {
+ "webpack-cli": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "4.x.x || 5.x.x"
+ },
+ "peerDependenciesMeta": {
+ "@webpack-cli/generators": {
+ "optional": true
+ },
+ "@webpack-cli/migrate": {
+ "optional": true
+ },
+ "webpack-bundle-analyzer": {
+ "optional": true
+ },
+ "webpack-dev-server": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-cli/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
+ "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==",
+ "dev": true,
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "dev": true,
+ "dependencies": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/webpack-sources/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/webpack/node_modules/micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "dependencies": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wildcard": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
+ "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==",
+ "dev": true
+ },
+ "node_modules/worker-farm": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+ "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+ "dev": true,
+ "dependencies": {
+ "errno": "~0.1.7"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "dev": true
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+}
diff --git a/devtools/client/shared/vendor/micromatch/package.json b/devtools/client/shared/vendor/micromatch/package.json
new file mode 100644
index 0000000000..038894e822
--- /dev/null
+++ b/devtools/client/shared/vendor/micromatch/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "micromatch",
+ "version": "4.0.4",
+ "description": "The mozilla-central bundle of the micromatch package",
+ "main": "",
+ "scripts": {
+ "webpack": "webpack"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/micromatch/micromatch.git"
+ },
+ "author": "",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/micromatch/micromatch/issues"
+ },
+ "homepage": "https://github.com/micromatch/micromatch#readme",
+ "dependencies": {
+ "micromatch": "^4.0.4"
+ },
+ "devDependencies": {
+ "webpack": "^4.46.0",
+ "webpack-cli": "^4.10.0"
+ }
+}
diff --git a/devtools/client/shared/vendor/micromatch/webpack.config.js b/devtools/client/shared/vendor/micromatch/webpack.config.js
new file mode 100644
index 0000000000..f7f9107c30
--- /dev/null
+++ b/devtools/client/shared/vendor/micromatch/webpack.config.js
@@ -0,0 +1,20 @@
+
+const path = require("path");
+
+module.exports = [{
+ mode: "production",
+ entry: `./node_modules/micromatch/index.js`,
+ optimization: {
+ minimize: false
+ },
+ output: {
+ path: __dirname,
+ filename: `micromatch.js`,
+ library: "micromatch",
+ libraryTarget: "commonjs"
+ },
+ node: {
+ util: true,
+ path: true
+ }
+}]; \ No newline at end of file
diff --git a/devtools/client/shared/vendor/moz.build b/devtools/client/shared/vendor/moz.build
new file mode 100644
index 0000000000..40ef0aa964
--- /dev/null
+++ b/devtools/client/shared/vendor/moz.build
@@ -0,0 +1,44 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "micromatch",
+ "source-map",
+]
+
+DevToolsModules(
+ 'fluent-react.js',
+ 'fuzzaldrin-plus.js',
+ 'immutable.js',
+ 'jszip.js',
+ 'md5.js',
+ 'react-dom-factories.js',
+ 'react-redux.js',
+ 'react-router-dom.js',
+ 'react-test-renderer-shallow.js',
+ 'react-test-renderer.js',
+ 'redux.js',
+ 'reselect.js',
+ 'WasmDis.js',
+ 'WasmParser.js',
+ 'whatwg-url.js',
+)
+
+# react dev versions are used if enable-debug-js-modules is set in .mozconfig.
+if CONFIG['DEBUG_JS_MODULES']:
+ RenamedDevToolsModules('react-dom-dev.js', 'react-dom.js')
+ RenamedDevToolsModules('react-dom-server-dev.js', 'react-dom-server.js')
+ RenamedDevToolsModules('react-dom-test-utils-dev.js', 'react-dom-test-utils.js')
+ RenamedDevToolsModules('react-prop-types-dev.js', 'react-prop-types.js')
+ RenamedDevToolsModules('react-dev.js', 'react.js')
+else:
+ DevToolsModules(
+ 'react-dom-server.js',
+ 'react-dom-test-utils.js',
+ 'react-dom.js',
+ 'react-prop-types.js',
+ 'react.js'
+ )
diff --git a/devtools/client/shared/vendor/react-dev.js b/devtools/client/shared/vendor/react-dev.js
new file mode 100644
index 0000000000..1bfc4d0555
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dev.js
@@ -0,0 +1,3155 @@
+/** @license React v16.8.6
+ * react.development.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.React = factory());
+}(this, (function () { 'use strict';
+
+// TODO: this is special because it gets imported during build.
+
+var ReactVersion = '16.8.6';
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+var FAUX_ITERATOR_SYMBOL = '@@iterator';
+
+function getIteratorFn(maybeIterable) {
+ if (maybeIterable === null || typeof maybeIterable !== 'object') {
+ return null;
+ }
+ var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
+ if (typeof maybeIterator === 'function') {
+ return maybeIterator;
+ }
+ return null;
+}
+
+/*
+object-assign
+(c) Sindre Sorhus
+@license MIT
+*/
+
+
+/* eslint-disable no-unused-vars */
+var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+}
+
+function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+}
+
+var objectAssign = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+};
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+var validateFormat = function () {};
+
+{
+ validateFormat = function (format) {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ };
+}
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ validateFormat(format);
+
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var lowPriorityWarning = function () {};
+
+{
+ var printWarning = function (format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.warn(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ lowPriorityWarning = function (condition, format) {
+ if (format === undefined) {
+ throw new Error('`lowPriorityWarning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var lowPriorityWarning$1 = lowPriorityWarning;
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warningWithoutStack = function () {};
+
+{
+ warningWithoutStack = function (condition, format) {
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ args[_key - 2] = arguments[_key];
+ }
+
+ if (format === undefined) {
+ throw new Error('`warningWithoutStack(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (args.length > 8) {
+ // Check before the condition to catch violations early.
+ throw new Error('warningWithoutStack() currently supports at most 8 arguments.');
+ }
+ if (condition) {
+ return;
+ }
+ if (typeof console !== 'undefined') {
+ var argsWithFormat = args.map(function (item) {
+ return '' + item;
+ });
+ argsWithFormat.unshift('Warning: ' + format);
+
+ // We intentionally don't use spread (or .apply) directly because it
+ // breaks IE9: https://github.com/facebook/react/issues/13610
+ Function.prototype.apply.call(console.error, console, argsWithFormat);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+var warningWithoutStack$1 = warningWithoutStack;
+
+var didWarnStateUpdateForUnmountedComponent = {};
+
+function warnNoop(publicInstance, callerName) {
+ {
+ var _constructor = publicInstance.constructor;
+ var componentName = _constructor && (_constructor.displayName || _constructor.name) || 'ReactClass';
+ var warningKey = componentName + '.' + callerName;
+ if (didWarnStateUpdateForUnmountedComponent[warningKey]) {
+ return;
+ }
+ warningWithoutStack$1(false, "Can't call %s on a component that is not yet mounted. " + 'This is a no-op, but it might indicate a bug in your application. ' + 'Instead, assign to `this.state` directly or define a `state = {};` ' + 'class property with the desired state in the %s component.', callerName, componentName);
+ didWarnStateUpdateForUnmountedComponent[warningKey] = true;
+ }
+}
+
+/**
+ * This is the abstract API for an update queue.
+ */
+var ReactNoopUpdateQueue = {
+ /**
+ * Checks whether or not this composite component is mounted.
+ * @param {ReactClass} publicInstance The instance we want to test.
+ * @return {boolean} True if mounted, false otherwise.
+ * @protected
+ * @final
+ */
+ isMounted: function (publicInstance) {
+ return false;
+ },
+
+ /**
+ * Forces an update. This should only be invoked when it is known with
+ * certainty that we are **not** in a DOM transaction.
+ *
+ * You may want to call this when you know that some deeper aspect of the
+ * component's state has changed but `setState` was not called.
+ *
+ * This will not invoke `shouldComponentUpdate`, but it will invoke
+ * `componentWillUpdate` and `componentDidUpdate`.
+ *
+ * @param {ReactClass} publicInstance The instance that should rerender.
+ * @param {?function} callback Called after component is updated.
+ * @param {?string} callerName name of the calling function in the public API.
+ * @internal
+ */
+ enqueueForceUpdate: function (publicInstance, callback, callerName) {
+ warnNoop(publicInstance, 'forceUpdate');
+ },
+
+ /**
+ * Replaces all of the state. Always use this or `setState` to mutate state.
+ * You should treat `this.state` as immutable.
+ *
+ * There is no guarantee that `this.state` will be immediately updated, so
+ * accessing `this.state` after calling this method may return the old value.
+ *
+ * @param {ReactClass} publicInstance The instance that should rerender.
+ * @param {object} completeState Next state.
+ * @param {?function} callback Called after component is updated.
+ * @param {?string} callerName name of the calling function in the public API.
+ * @internal
+ */
+ enqueueReplaceState: function (publicInstance, completeState, callback, callerName) {
+ warnNoop(publicInstance, 'replaceState');
+ },
+
+ /**
+ * Sets a subset of the state. This only exists because _pendingState is
+ * internal. This provides a merging strategy that is not available to deep
+ * properties which is confusing. TODO: Expose pendingState or don't use it
+ * during the merge.
+ *
+ * @param {ReactClass} publicInstance The instance that should rerender.
+ * @param {object} partialState Next partial state to be merged with state.
+ * @param {?function} callback Called after component is updated.
+ * @param {?string} Name of the calling function in the public API.
+ * @internal
+ */
+ enqueueSetState: function (publicInstance, partialState, callback, callerName) {
+ warnNoop(publicInstance, 'setState');
+ }
+};
+
+var emptyObject = {};
+{
+ Object.freeze(emptyObject);
+}
+
+/**
+ * Base class helpers for the updating state of a component.
+ */
+function Component(props, context, updater) {
+ this.props = props;
+ this.context = context;
+ // If a component has string refs, we will assign a different object later.
+ this.refs = emptyObject;
+ // We initialize the default updater but the real one gets injected by the
+ // renderer.
+ this.updater = updater || ReactNoopUpdateQueue;
+}
+
+Component.prototype.isReactComponent = {};
+
+/**
+ * Sets a subset of the state. Always use this to mutate
+ * state. You should treat `this.state` as immutable.
+ *
+ * There is no guarantee that `this.state` will be immediately updated, so
+ * accessing `this.state` after calling this method may return the old value.
+ *
+ * There is no guarantee that calls to `setState` will run synchronously,
+ * as they may eventually be batched together. You can provide an optional
+ * callback that will be executed when the call to setState is actually
+ * completed.
+ *
+ * When a function is provided to setState, it will be called at some point in
+ * the future (not synchronously). It will be called with the up to date
+ * component arguments (state, props, context). These values can be different
+ * from this.* because your function may be called after receiveProps but before
+ * shouldComponentUpdate, and this new state, props, and context will not yet be
+ * assigned to this.
+ *
+ * @param {object|function} partialState Next partial state or function to
+ * produce next partial state to be merged with current state.
+ * @param {?function} callback Called after state is updated.
+ * @final
+ * @protected
+ */
+Component.prototype.setState = function (partialState, callback) {
+ !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : void 0;
+ this.updater.enqueueSetState(this, partialState, callback, 'setState');
+};
+
+/**
+ * Forces an update. This should only be invoked when it is known with
+ * certainty that we are **not** in a DOM transaction.
+ *
+ * You may want to call this when you know that some deeper aspect of the
+ * component's state has changed but `setState` was not called.
+ *
+ * This will not invoke `shouldComponentUpdate`, but it will invoke
+ * `componentWillUpdate` and `componentDidUpdate`.
+ *
+ * @param {?function} callback Called after update is complete.
+ * @final
+ * @protected
+ */
+Component.prototype.forceUpdate = function (callback) {
+ this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
+};
+
+/**
+ * Deprecated APIs. These APIs used to exist on classic React classes but since
+ * we would like to deprecate them, we're not going to move them over to this
+ * modern base class. Instead, we define a getter that warns if it's accessed.
+ */
+{
+ var deprecatedAPIs = {
+ isMounted: ['isMounted', 'Instead, make sure to clean up subscriptions and pending requests in ' + 'componentWillUnmount to prevent memory leaks.'],
+ replaceState: ['replaceState', 'Refactor your code to use setState instead (see ' + 'https://github.com/facebook/react/issues/3236).']
+ };
+ var defineDeprecationWarning = function (methodName, info) {
+ Object.defineProperty(Component.prototype, methodName, {
+ get: function () {
+ lowPriorityWarning$1(false, '%s(...) is deprecated in plain JavaScript React classes. %s', info[0], info[1]);
+ return undefined;
+ }
+ });
+ };
+ for (var fnName in deprecatedAPIs) {
+ if (deprecatedAPIs.hasOwnProperty(fnName)) {
+ defineDeprecationWarning(fnName, deprecatedAPIs[fnName]);
+ }
+ }
+}
+
+function ComponentDummy() {}
+ComponentDummy.prototype = Component.prototype;
+
+/**
+ * Convenience component with default shallow equality check for sCU.
+ */
+function PureComponent(props, context, updater) {
+ this.props = props;
+ this.context = context;
+ // If a component has string refs, we will assign a different object later.
+ this.refs = emptyObject;
+ this.updater = updater || ReactNoopUpdateQueue;
+}
+
+var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
+pureComponentPrototype.constructor = PureComponent;
+// Avoid an extra prototype jump for these methods.
+objectAssign(pureComponentPrototype, Component.prototype);
+pureComponentPrototype.isPureReactComponent = true;
+
+// an immutable object with a single mutable value
+function createRef() {
+ var refObject = {
+ current: null
+ };
+ {
+ Object.seal(refObject);
+ }
+ return refObject;
+}
+
+var enableSchedulerDebugging = false;
+
+/* eslint-disable no-var */
+
+// TODO: Use symbols?
+var ImmediatePriority = 1;
+var UserBlockingPriority = 2;
+var NormalPriority = 3;
+var LowPriority = 4;
+var IdlePriority = 5;
+
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
+// Math.pow(2, 30) - 1
+// 0b111111111111111111111111111111
+var maxSigned31BitInt = 1073741823;
+
+// Times out immediately
+var IMMEDIATE_PRIORITY_TIMEOUT = -1;
+// Eventually times out
+var USER_BLOCKING_PRIORITY = 250;
+var NORMAL_PRIORITY_TIMEOUT = 5000;
+var LOW_PRIORITY_TIMEOUT = 10000;
+// Never times out
+var IDLE_PRIORITY = maxSigned31BitInt;
+
+// Callbacks are stored as a circular, doubly linked list.
+var firstCallbackNode = null;
+
+var currentDidTimeout = false;
+// Pausing the scheduler is useful for debugging.
+var isSchedulerPaused = false;
+
+var currentPriorityLevel = NormalPriority;
+var currentEventStartTime = -1;
+var currentExpirationTime = -1;
+
+// This is set when a callback is being executed, to prevent re-entrancy.
+var isExecutingCallback = false;
+
+var isHostCallbackScheduled = false;
+
+var hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function';
+
+function ensureHostCallbackIsScheduled() {
+ if (isExecutingCallback) {
+ // Don't schedule work yet; wait until the next time we yield.
+ return;
+ }
+ // Schedule the host callback using the earliest expiration in the list.
+ var expirationTime = firstCallbackNode.expirationTime;
+ if (!isHostCallbackScheduled) {
+ isHostCallbackScheduled = true;
+ } else {
+ // Cancel the existing host callback.
+ cancelHostCallback();
+ }
+ requestHostCallback(flushWork, expirationTime);
+}
+
+function flushFirstCallback() {
+ var flushedNode = firstCallbackNode;
+
+ // Remove the node from the list before calling the callback. That way the
+ // list is in a consistent state even if the callback throws.
+ var next = firstCallbackNode.next;
+ if (firstCallbackNode === next) {
+ // This is the last callback in the list.
+ firstCallbackNode = null;
+ next = null;
+ } else {
+ var lastCallbackNode = firstCallbackNode.previous;
+ firstCallbackNode = lastCallbackNode.next = next;
+ next.previous = lastCallbackNode;
+ }
+
+ flushedNode.next = flushedNode.previous = null;
+
+ // Now it's safe to call the callback.
+ var callback = flushedNode.callback;
+ var expirationTime = flushedNode.expirationTime;
+ var priorityLevel = flushedNode.priorityLevel;
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousExpirationTime = currentExpirationTime;
+ currentPriorityLevel = priorityLevel;
+ currentExpirationTime = expirationTime;
+ var continuationCallback;
+ try {
+ continuationCallback = callback();
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentExpirationTime = previousExpirationTime;
+ }
+
+ // A callback may return a continuation. The continuation should be scheduled
+ // with the same priority and expiration as the just-finished callback.
+ if (typeof continuationCallback === 'function') {
+ var continuationNode = {
+ callback: continuationCallback,
+ priorityLevel: priorityLevel,
+ expirationTime: expirationTime,
+ next: null,
+ previous: null
+ };
+
+ // Insert the new callback into the list, sorted by its expiration. This is
+ // almost the same as the code in `scheduleCallback`, except the callback
+ // is inserted into the list *before* callbacks of equal expiration instead
+ // of after.
+ if (firstCallbackNode === null) {
+ // This is the first callback in the list.
+ firstCallbackNode = continuationNode.next = continuationNode.previous = continuationNode;
+ } else {
+ var nextAfterContinuation = null;
+ var node = firstCallbackNode;
+ do {
+ if (node.expirationTime >= expirationTime) {
+ // This callback expires at or after the continuation. We will insert
+ // the continuation *before* this callback.
+ nextAfterContinuation = node;
+ break;
+ }
+ node = node.next;
+ } while (node !== firstCallbackNode);
+
+ if (nextAfterContinuation === null) {
+ // No equal or lower priority callback was found, which means the new
+ // callback is the lowest priority callback in the list.
+ nextAfterContinuation = firstCallbackNode;
+ } else if (nextAfterContinuation === firstCallbackNode) {
+ // The new callback is the highest priority callback in the list.
+ firstCallbackNode = continuationNode;
+ ensureHostCallbackIsScheduled();
+ }
+
+ var previous = nextAfterContinuation.previous;
+ previous.next = nextAfterContinuation.previous = continuationNode;
+ continuationNode.next = nextAfterContinuation;
+ continuationNode.previous = previous;
+ }
+ }
+}
+
+function flushImmediateWork() {
+ if (
+ // Confirm we've exited the outer most event handler
+ currentEventStartTime === -1 && firstCallbackNode !== null && firstCallbackNode.priorityLevel === ImmediatePriority) {
+ isExecutingCallback = true;
+ try {
+ do {
+ flushFirstCallback();
+ } while (
+ // Keep flushing until there are no more immediate callbacks
+ firstCallbackNode !== null && firstCallbackNode.priorityLevel === ImmediatePriority);
+ } finally {
+ isExecutingCallback = false;
+ if (firstCallbackNode !== null) {
+ // There's still work remaining. Request another callback.
+ ensureHostCallbackIsScheduled();
+ } else {
+ isHostCallbackScheduled = false;
+ }
+ }
+ }
+}
+
+function flushWork(didTimeout) {
+ // Exit right away if we're currently paused
+
+ if (enableSchedulerDebugging && isSchedulerPaused) {
+ return;
+ }
+
+ isExecutingCallback = true;
+ var previousDidTimeout = currentDidTimeout;
+ currentDidTimeout = didTimeout;
+ try {
+ if (didTimeout) {
+ // Flush all the expired callbacks without yielding.
+ while (firstCallbackNode !== null && !(enableSchedulerDebugging && isSchedulerPaused)) {
+ // TODO Wrap in feature flag
+ // Read the current time. Flush all the callbacks that expire at or
+ // earlier than that time. Then read the current time again and repeat.
+ // This optimizes for as few performance.now calls as possible.
+ var currentTime = getCurrentTime();
+ if (firstCallbackNode.expirationTime <= currentTime) {
+ do {
+ flushFirstCallback();
+ } while (firstCallbackNode !== null && firstCallbackNode.expirationTime <= currentTime && !(enableSchedulerDebugging && isSchedulerPaused));
+ continue;
+ }
+ break;
+ }
+ } else {
+ // Keep flushing callbacks until we run out of time in the frame.
+ if (firstCallbackNode !== null) {
+ do {
+ if (enableSchedulerDebugging && isSchedulerPaused) {
+ break;
+ }
+ flushFirstCallback();
+ } while (firstCallbackNode !== null && !shouldYieldToHost());
+ }
+ }
+ } finally {
+ isExecutingCallback = false;
+ currentDidTimeout = previousDidTimeout;
+ if (firstCallbackNode !== null) {
+ // There's still work remaining. Request another callback.
+ ensureHostCallbackIsScheduled();
+ } else {
+ isHostCallbackScheduled = false;
+ }
+ // Before exiting, flush all the immediate work that was scheduled.
+ flushImmediateWork();
+ }
+}
+
+function unstable_runWithPriority(priorityLevel, eventHandler) {
+ switch (priorityLevel) {
+ case ImmediatePriority:
+ case UserBlockingPriority:
+ case NormalPriority:
+ case LowPriority:
+ case IdlePriority:
+ break;
+ default:
+ priorityLevel = NormalPriority;
+ }
+
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousEventStartTime = currentEventStartTime;
+ currentPriorityLevel = priorityLevel;
+ currentEventStartTime = getCurrentTime();
+
+ try {
+ return eventHandler();
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentEventStartTime = previousEventStartTime;
+
+ // Before exiting, flush all the immediate work that was scheduled.
+ flushImmediateWork();
+ }
+}
+
+function unstable_next(eventHandler) {
+ var priorityLevel = void 0;
+ switch (currentPriorityLevel) {
+ case ImmediatePriority:
+ case UserBlockingPriority:
+ case NormalPriority:
+ // Shift down to normal priority
+ priorityLevel = NormalPriority;
+ break;
+ default:
+ // Anything lower than normal priority should remain at the current level.
+ priorityLevel = currentPriorityLevel;
+ break;
+ }
+
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousEventStartTime = currentEventStartTime;
+ currentPriorityLevel = priorityLevel;
+ currentEventStartTime = getCurrentTime();
+
+ try {
+ return eventHandler();
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentEventStartTime = previousEventStartTime;
+
+ // Before exiting, flush all the immediate work that was scheduled.
+ flushImmediateWork();
+ }
+}
+
+function unstable_wrapCallback(callback) {
+ var parentPriorityLevel = currentPriorityLevel;
+ return function () {
+ // This is a fork of runWithPriority, inlined for performance.
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousEventStartTime = currentEventStartTime;
+ currentPriorityLevel = parentPriorityLevel;
+ currentEventStartTime = getCurrentTime();
+
+ try {
+ return callback.apply(this, arguments);
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentEventStartTime = previousEventStartTime;
+ flushImmediateWork();
+ }
+ };
+}
+
+function unstable_scheduleCallback(callback, deprecated_options) {
+ var startTime = currentEventStartTime !== -1 ? currentEventStartTime : getCurrentTime();
+
+ var expirationTime;
+ if (typeof deprecated_options === 'object' && deprecated_options !== null && typeof deprecated_options.timeout === 'number') {
+ // FIXME: Remove this branch once we lift expiration times out of React.
+ expirationTime = startTime + deprecated_options.timeout;
+ } else {
+ switch (currentPriorityLevel) {
+ case ImmediatePriority:
+ expirationTime = startTime + IMMEDIATE_PRIORITY_TIMEOUT;
+ break;
+ case UserBlockingPriority:
+ expirationTime = startTime + USER_BLOCKING_PRIORITY;
+ break;
+ case IdlePriority:
+ expirationTime = startTime + IDLE_PRIORITY;
+ break;
+ case LowPriority:
+ expirationTime = startTime + LOW_PRIORITY_TIMEOUT;
+ break;
+ case NormalPriority:
+ default:
+ expirationTime = startTime + NORMAL_PRIORITY_TIMEOUT;
+ }
+ }
+
+ var newNode = {
+ callback: callback,
+ priorityLevel: currentPriorityLevel,
+ expirationTime: expirationTime,
+ next: null,
+ previous: null
+ };
+
+ // Insert the new callback into the list, ordered first by expiration, then
+ // by insertion. So the new callback is inserted any other callback with
+ // equal expiration.
+ if (firstCallbackNode === null) {
+ // This is the first callback in the list.
+ firstCallbackNode = newNode.next = newNode.previous = newNode;
+ ensureHostCallbackIsScheduled();
+ } else {
+ var next = null;
+ var node = firstCallbackNode;
+ do {
+ if (node.expirationTime > expirationTime) {
+ // The new callback expires before this one.
+ next = node;
+ break;
+ }
+ node = node.next;
+ } while (node !== firstCallbackNode);
+
+ if (next === null) {
+ // No callback with a later expiration was found, which means the new
+ // callback has the latest expiration in the list.
+ next = firstCallbackNode;
+ } else if (next === firstCallbackNode) {
+ // The new callback has the earliest expiration in the entire list.
+ firstCallbackNode = newNode;
+ ensureHostCallbackIsScheduled();
+ }
+
+ var previous = next.previous;
+ previous.next = next.previous = newNode;
+ newNode.next = next;
+ newNode.previous = previous;
+ }
+
+ return newNode;
+}
+
+function unstable_pauseExecution() {
+ isSchedulerPaused = true;
+}
+
+function unstable_continueExecution() {
+ isSchedulerPaused = false;
+ if (firstCallbackNode !== null) {
+ ensureHostCallbackIsScheduled();
+ }
+}
+
+function unstable_getFirstCallbackNode() {
+ return firstCallbackNode;
+}
+
+function unstable_cancelCallback(callbackNode) {
+ var next = callbackNode.next;
+ if (next === null) {
+ // Already cancelled.
+ return;
+ }
+
+ if (next === callbackNode) {
+ // This is the only scheduled callback. Clear the list.
+ firstCallbackNode = null;
+ } else {
+ // Remove the callback from its position in the list.
+ if (callbackNode === firstCallbackNode) {
+ firstCallbackNode = next;
+ }
+ var previous = callbackNode.previous;
+ previous.next = next;
+ next.previous = previous;
+ }
+
+ callbackNode.next = callbackNode.previous = null;
+}
+
+function unstable_getCurrentPriorityLevel() {
+ return currentPriorityLevel;
+}
+
+function unstable_shouldYield() {
+ return !currentDidTimeout && (firstCallbackNode !== null && firstCallbackNode.expirationTime < currentExpirationTime || shouldYieldToHost());
+}
+
+// The remaining code is essentially a polyfill for requestIdleCallback. It
+// works by scheduling a requestAnimationFrame, storing the time for the start
+// of the frame, then scheduling a postMessage which gets scheduled after paint.
+// Within the postMessage handler do as much work as possible until time + frame
+// rate. By separating the idle call into a separate event tick we ensure that
+// layout, paint and other browser work is counted against the available time.
+// The frame rate is dynamically adjusted.
+
+// We capture a local reference to any global, in case it gets polyfilled after
+// this module is initially evaluated. We want to be using a
+// consistent implementation.
+var localDate = Date;
+
+// This initialization code may run even on server environments if a component
+// just imports ReactDOM (e.g. for findDOMNode). Some environments might not
+// have setTimeout or clearTimeout. However, we always expect them to be defined
+// on the client. https://github.com/facebook/react/pull/13088
+var localSetTimeout = typeof setTimeout === 'function' ? setTimeout : undefined;
+var localClearTimeout = typeof clearTimeout === 'function' ? clearTimeout : undefined;
+
+// We don't expect either of these to necessarily be defined, but we will error
+// later if they are missing on the client.
+var localRequestAnimationFrame = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : undefined;
+var localCancelAnimationFrame = typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame : undefined;
+
+var getCurrentTime;
+
+// requestAnimationFrame does not run when the tab is in the background. If
+// we're backgrounded we prefer for that work to happen so that the page
+// continues to load in the background. So we also schedule a 'setTimeout' as
+// a fallback.
+// TODO: Need a better heuristic for backgrounded work.
+var ANIMATION_FRAME_TIMEOUT = 100;
+var rAFID;
+var rAFTimeoutID;
+var requestAnimationFrameWithTimeout = function (callback) {
+ // schedule rAF and also a setTimeout
+ rAFID = localRequestAnimationFrame(function (timestamp) {
+ // cancel the setTimeout
+ localClearTimeout(rAFTimeoutID);
+ callback(timestamp);
+ });
+ rAFTimeoutID = localSetTimeout(function () {
+ // cancel the requestAnimationFrame
+ localCancelAnimationFrame(rAFID);
+ callback(getCurrentTime());
+ }, ANIMATION_FRAME_TIMEOUT);
+};
+
+if (hasNativePerformanceNow) {
+ var Performance = performance;
+ getCurrentTime = function () {
+ return Performance.now();
+ };
+} else {
+ getCurrentTime = function () {
+ return localDate.now();
+ };
+}
+
+var requestHostCallback;
+var cancelHostCallback;
+var shouldYieldToHost;
+
+var globalValue = null;
+if (typeof window !== 'undefined') {
+ globalValue = window;
+} else if (typeof global !== 'undefined') {
+ globalValue = global;
+}
+
+if (globalValue && globalValue._schedMock) {
+ // Dynamic injection, only for testing purposes.
+ var globalImpl = globalValue._schedMock;
+ requestHostCallback = globalImpl[0];
+ cancelHostCallback = globalImpl[1];
+ shouldYieldToHost = globalImpl[2];
+ getCurrentTime = globalImpl[3];
+} else if (
+// If Scheduler runs in a non-DOM environment, it falls back to a naive
+// implementation using setTimeout.
+typeof window === 'undefined' ||
+// Check if MessageChannel is supported, too.
+typeof MessageChannel !== 'function') {
+ // If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,
+ // fallback to a naive implementation.
+ var _callback = null;
+ var _flushCallback = function (didTimeout) {
+ if (_callback !== null) {
+ try {
+ _callback(didTimeout);
+ } finally {
+ _callback = null;
+ }
+ }
+ };
+ requestHostCallback = function (cb, ms) {
+ if (_callback !== null) {
+ // Protect against re-entrancy.
+ setTimeout(requestHostCallback, 0, cb);
+ } else {
+ _callback = cb;
+ setTimeout(_flushCallback, 0, false);
+ }
+ };
+ cancelHostCallback = function () {
+ _callback = null;
+ };
+ shouldYieldToHost = function () {
+ return false;
+ };
+} else {
+ if (typeof console !== 'undefined') {
+ // TODO: Remove fb.me link
+ if (typeof localRequestAnimationFrame !== 'function') {
+ console.error("This browser doesn't support requestAnimationFrame. " + 'Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills');
+ }
+ if (typeof localCancelAnimationFrame !== 'function') {
+ console.error("This browser doesn't support cancelAnimationFrame. " + 'Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills');
+ }
+ }
+
+ var scheduledHostCallback = null;
+ var isMessageEventScheduled = false;
+ var timeoutTime = -1;
+
+ var isAnimationFrameScheduled = false;
+
+ var isFlushingHostCallback = false;
+
+ var frameDeadline = 0;
+ // We start out assuming that we run at 30fps but then the heuristic tracking
+ // will adjust this value to a faster fps if we get more frequent animation
+ // frames.
+ var previousFrameTime = 33;
+ var activeFrameTime = 33;
+
+ shouldYieldToHost = function () {
+ return frameDeadline <= getCurrentTime();
+ };
+
+ // We use the postMessage trick to defer idle work until after the repaint.
+ var channel = new MessageChannel();
+ var port = channel.port2;
+ channel.port1.onmessage = function (event) {
+ isMessageEventScheduled = false;
+
+ var prevScheduledCallback = scheduledHostCallback;
+ var prevTimeoutTime = timeoutTime;
+ scheduledHostCallback = null;
+ timeoutTime = -1;
+
+ var currentTime = getCurrentTime();
+
+ var didTimeout = false;
+ if (frameDeadline - currentTime <= 0) {
+ // There's no time left in this idle period. Check if the callback has
+ // a timeout and whether it's been exceeded.
+ if (prevTimeoutTime !== -1 && prevTimeoutTime <= currentTime) {
+ // Exceeded the timeout. Invoke the callback even though there's no
+ // time left.
+ didTimeout = true;
+ } else {
+ // No timeout.
+ if (!isAnimationFrameScheduled) {
+ // Schedule another animation callback so we retry later.
+ isAnimationFrameScheduled = true;
+ requestAnimationFrameWithTimeout(animationTick);
+ }
+ // Exit without invoking the callback.
+ scheduledHostCallback = prevScheduledCallback;
+ timeoutTime = prevTimeoutTime;
+ return;
+ }
+ }
+
+ if (prevScheduledCallback !== null) {
+ isFlushingHostCallback = true;
+ try {
+ prevScheduledCallback(didTimeout);
+ } finally {
+ isFlushingHostCallback = false;
+ }
+ }
+ };
+
+ var animationTick = function (rafTime) {
+ if (scheduledHostCallback !== null) {
+ // Eagerly schedule the next animation callback at the beginning of the
+ // frame. If the scheduler queue is not empty at the end of the frame, it
+ // will continue flushing inside that callback. If the queue *is* empty,
+ // then it will exit immediately. Posting the callback at the start of the
+ // frame ensures it's fired within the earliest possible frame. If we
+ // waited until the end of the frame to post the callback, we risk the
+ // browser skipping a frame and not firing the callback until the frame
+ // after that.
+ requestAnimationFrameWithTimeout(animationTick);
+ } else {
+ // No pending work. Exit.
+ isAnimationFrameScheduled = false;
+ return;
+ }
+
+ var nextFrameTime = rafTime - frameDeadline + activeFrameTime;
+ if (nextFrameTime < activeFrameTime && previousFrameTime < activeFrameTime) {
+ if (nextFrameTime < 8) {
+ // Defensive coding. We don't support higher frame rates than 120hz.
+ // If the calculated frame time gets lower than 8, it is probably a bug.
+ nextFrameTime = 8;
+ }
+ // If one frame goes long, then the next one can be short to catch up.
+ // If two frames are short in a row, then that's an indication that we
+ // actually have a higher frame rate than what we're currently optimizing.
+ // We adjust our heuristic dynamically accordingly. For example, if we're
+ // running on 120hz display or 90hz VR display.
+ // Take the max of the two in case one of them was an anomaly due to
+ // missed frame deadlines.
+ activeFrameTime = nextFrameTime < previousFrameTime ? previousFrameTime : nextFrameTime;
+ } else {
+ previousFrameTime = nextFrameTime;
+ }
+ frameDeadline = rafTime + activeFrameTime;
+ if (!isMessageEventScheduled) {
+ isMessageEventScheduled = true;
+ port.postMessage(undefined);
+ }
+ };
+
+ requestHostCallback = function (callback, absoluteTimeout) {
+ scheduledHostCallback = callback;
+ timeoutTime = absoluteTimeout;
+ if (isFlushingHostCallback || absoluteTimeout < 0) {
+ // Don't wait for the next frame. Continue working ASAP, in a new event.
+ port.postMessage(undefined);
+ } else if (!isAnimationFrameScheduled) {
+ // If rAF didn't already schedule one, we need to schedule a frame.
+ // TODO: If this rAF doesn't materialize because the browser throttles, we
+ // might want to still have setTimeout trigger rIC as a backup to ensure
+ // that we keep performing work.
+ isAnimationFrameScheduled = true;
+ requestAnimationFrameWithTimeout(animationTick);
+ }
+ };
+
+ cancelHostCallback = function () {
+ scheduledHostCallback = null;
+ isMessageEventScheduled = false;
+ timeoutTime = -1;
+ };
+}
+
+// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
+
+
+// In some cases, StrictMode should also double-render lifecycles.
+// This can be confusing for tests though,
+// And it can be bad for performance in production.
+// This feature flag can be used to control the behavior:
+
+
+// To preserve the "Pause on caught exceptions" behavior of the debugger, we
+// replay the begin phase of a failed component inside invokeGuardedCallback.
+
+
+// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
+
+
+// Gather advanced timing metrics for Profiler subtrees.
+
+
+// Trace which interactions trigger each commit.
+var enableSchedulerTracing = true;
+
+// Only used in www builds.
+ // TODO: true? Here it might just be false.
+
+// Only used in www builds.
+
+
+// Only used in www builds.
+
+
+// React Fire: prevent the value and checked attributes from syncing
+// with their related DOM properties
+
+
+// These APIs will no longer be "unstable" in the upcoming 16.7 release,
+// Control this behavior with a flag to support 16.6 minor releases in the meanwhile.
+var enableStableConcurrentModeAPIs = false;
+
+var DEFAULT_THREAD_ID = 0;
+
+// Counters used to generate unique IDs.
+var interactionIDCounter = 0;
+var threadIDCounter = 0;
+
+// Set of currently traced interactions.
+// Interactions "stack"–
+// Meaning that newly traced interactions are appended to the previously active set.
+// When an interaction goes out of scope, the previous set (if any) is restored.
+var interactionsRef = null;
+
+// Listener(s) to notify when interactions begin and end.
+var subscriberRef = null;
+
+if (enableSchedulerTracing) {
+ interactionsRef = {
+ current: new Set()
+ };
+ subscriberRef = {
+ current: null
+ };
+}
+
+function unstable_clear(callback) {
+ if (!enableSchedulerTracing) {
+ return callback();
+ }
+
+ var prevInteractions = interactionsRef.current;
+ interactionsRef.current = new Set();
+
+ try {
+ return callback();
+ } finally {
+ interactionsRef.current = prevInteractions;
+ }
+}
+
+function unstable_getCurrent() {
+ if (!enableSchedulerTracing) {
+ return null;
+ } else {
+ return interactionsRef.current;
+ }
+}
+
+function unstable_getThreadID() {
+ return ++threadIDCounter;
+}
+
+function unstable_trace(name, timestamp, callback) {
+ var threadID = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : DEFAULT_THREAD_ID;
+
+ if (!enableSchedulerTracing) {
+ return callback();
+ }
+
+ var interaction = {
+ __count: 1,
+ id: interactionIDCounter++,
+ name: name,
+ timestamp: timestamp
+ };
+
+ var prevInteractions = interactionsRef.current;
+
+ // Traced interactions should stack/accumulate.
+ // To do that, clone the current interactions.
+ // The previous set will be restored upon completion.
+ var interactions = new Set(prevInteractions);
+ interactions.add(interaction);
+ interactionsRef.current = interactions;
+
+ var subscriber = subscriberRef.current;
+ var returnValue = void 0;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onInteractionTraced(interaction);
+ }
+ } finally {
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkStarted(interactions, threadID);
+ }
+ } finally {
+ try {
+ returnValue = callback();
+ } finally {
+ interactionsRef.current = prevInteractions;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkStopped(interactions, threadID);
+ }
+ } finally {
+ interaction.__count--;
+
+ // If no async work was scheduled for this interaction,
+ // Notify subscribers that it's completed.
+ if (subscriber !== null && interaction.__count === 0) {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ }
+ }
+ }
+ }
+ }
+
+ return returnValue;
+}
+
+function unstable_wrap(callback) {
+ var threadID = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_THREAD_ID;
+
+ if (!enableSchedulerTracing) {
+ return callback;
+ }
+
+ var wrappedInteractions = interactionsRef.current;
+
+ var subscriber = subscriberRef.current;
+ if (subscriber !== null) {
+ subscriber.onWorkScheduled(wrappedInteractions, threadID);
+ }
+
+ // Update the pending async work count for the current interactions.
+ // Update after calling subscribers in case of error.
+ wrappedInteractions.forEach(function (interaction) {
+ interaction.__count++;
+ });
+
+ var hasRun = false;
+
+ function wrapped() {
+ var prevInteractions = interactionsRef.current;
+ interactionsRef.current = wrappedInteractions;
+
+ subscriber = subscriberRef.current;
+
+ try {
+ var returnValue = void 0;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkStarted(wrappedInteractions, threadID);
+ }
+ } finally {
+ try {
+ returnValue = callback.apply(undefined, arguments);
+ } finally {
+ interactionsRef.current = prevInteractions;
+
+ if (subscriber !== null) {
+ subscriber.onWorkStopped(wrappedInteractions, threadID);
+ }
+ }
+ }
+
+ return returnValue;
+ } finally {
+ if (!hasRun) {
+ // We only expect a wrapped function to be executed once,
+ // But in the event that it's executed more than once–
+ // Only decrement the outstanding interaction counts once.
+ hasRun = true;
+
+ // Update pending async counts for all wrapped interactions.
+ // If this was the last scheduled async work for any of them,
+ // Mark them as completed.
+ wrappedInteractions.forEach(function (interaction) {
+ interaction.__count--;
+
+ if (subscriber !== null && interaction.__count === 0) {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ }
+ });
+ }
+ }
+ }
+
+ wrapped.cancel = function cancel() {
+ subscriber = subscriberRef.current;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkCanceled(wrappedInteractions, threadID);
+ }
+ } finally {
+ // Update pending async counts for all wrapped interactions.
+ // If this was the last scheduled async work for any of them,
+ // Mark them as completed.
+ wrappedInteractions.forEach(function (interaction) {
+ interaction.__count--;
+
+ if (subscriber && interaction.__count === 0) {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ }
+ });
+ }
+ };
+
+ return wrapped;
+}
+
+var subscribers = null;
+if (enableSchedulerTracing) {
+ subscribers = new Set();
+}
+
+function unstable_subscribe(subscriber) {
+ if (enableSchedulerTracing) {
+ subscribers.add(subscriber);
+
+ if (subscribers.size === 1) {
+ subscriberRef.current = {
+ onInteractionScheduledWorkCompleted: onInteractionScheduledWorkCompleted,
+ onInteractionTraced: onInteractionTraced,
+ onWorkCanceled: onWorkCanceled,
+ onWorkScheduled: onWorkScheduled,
+ onWorkStarted: onWorkStarted,
+ onWorkStopped: onWorkStopped
+ };
+ }
+ }
+}
+
+function unstable_unsubscribe(subscriber) {
+ if (enableSchedulerTracing) {
+ subscribers.delete(subscriber);
+
+ if (subscribers.size === 0) {
+ subscriberRef.current = null;
+ }
+ }
+}
+
+function onInteractionTraced(interaction) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onInteractionTraced(interaction);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onInteractionScheduledWorkCompleted(interaction) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkScheduled(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkScheduled(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkStarted(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkStarted(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkStopped(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkStopped(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkCanceled(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkCanceled(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+/**
+ * Keeps track of the current dispatcher.
+ */
+var ReactCurrentDispatcher = {
+ /**
+ * @internal
+ * @type {ReactComponent}
+ */
+ current: null
+};
+
+/**
+ * Keeps track of the current owner.
+ *
+ * The current owner is the component who should own any components that are
+ * currently being constructed.
+ */
+var ReactCurrentOwner = {
+ /**
+ * @internal
+ * @type {ReactComponent}
+ */
+ current: null
+};
+
+var BEFORE_SLASH_RE = /^(.*)[\\\/]/;
+
+var describeComponentFrame = function (name, source, ownerName) {
+ var sourceInfo = '';
+ if (source) {
+ var path = source.fileName;
+ var fileName = path.replace(BEFORE_SLASH_RE, '');
+ {
+ // In DEV, include code for a common special case:
+ // prefer "folder/index.js" instead of just "index.js".
+ if (/^index\./.test(fileName)) {
+ var match = path.match(BEFORE_SLASH_RE);
+ if (match) {
+ var pathBeforeSlash = match[1];
+ if (pathBeforeSlash) {
+ var folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
+ fileName = folderName + '/' + fileName;
+ }
+ }
+ }
+ }
+ sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
+ } else if (ownerName) {
+ sourceInfo = ' (created by ' + ownerName + ')';
+ }
+ return '\n in ' + (name || 'Unknown') + sourceInfo;
+};
+
+var Resolved = 1;
+
+
+function refineResolvedLazyComponent(lazyComponent) {
+ return lazyComponent._status === Resolved ? lazyComponent._result : null;
+}
+
+function getWrappedName(outerType, innerType, wrapperName) {
+ var functionName = innerType.displayName || innerType.name || '';
+ return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
+}
+
+function getComponentName(type) {
+ if (type == null) {
+ // Host root, text node or just invalid type.
+ return null;
+ }
+ {
+ if (typeof type.tag === 'number') {
+ warningWithoutStack$1(false, 'Received an unexpected object in getComponentName(). ' + 'This is likely a bug in React. Please file an issue.');
+ }
+ }
+ if (typeof type === 'function') {
+ return type.displayName || type.name || null;
+ }
+ if (typeof type === 'string') {
+ return type;
+ }
+ switch (type) {
+ case REACT_CONCURRENT_MODE_TYPE:
+ return 'ConcurrentMode';
+ case REACT_FRAGMENT_TYPE:
+ return 'Fragment';
+ case REACT_PORTAL_TYPE:
+ return 'Portal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_SUSPENSE_TYPE:
+ return 'Suspense';
+ }
+ if (typeof type === 'object') {
+ switch (type.$$typeof) {
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_FORWARD_REF_TYPE:
+ return getWrappedName(type, type.render, 'ForwardRef');
+ case REACT_MEMO_TYPE:
+ return getComponentName(type.type);
+ case REACT_LAZY_TYPE:
+ {
+ var thenable = type;
+ var resolvedThenable = refineResolvedLazyComponent(thenable);
+ if (resolvedThenable) {
+ return getComponentName(resolvedThenable);
+ }
+ }
+ }
+ }
+ return null;
+}
+
+var ReactDebugCurrentFrame = {};
+
+var currentlyValidatingElement = null;
+
+function setCurrentlyValidatingElement(element) {
+ {
+ currentlyValidatingElement = element;
+ }
+}
+
+{
+ // Stack implementation injected by the current renderer.
+ ReactDebugCurrentFrame.getCurrentStack = null;
+
+ ReactDebugCurrentFrame.getStackAddendum = function () {
+ var stack = '';
+
+ // Add an extra top frame while an element is being validated
+ if (currentlyValidatingElement) {
+ var name = getComponentName(currentlyValidatingElement.type);
+ var owner = currentlyValidatingElement._owner;
+ stack += describeComponentFrame(name, currentlyValidatingElement._source, owner && getComponentName(owner.type));
+ }
+
+ // Delegate to the injected renderer-specific implementation
+ var impl = ReactDebugCurrentFrame.getCurrentStack;
+ if (impl) {
+ stack += impl() || '';
+ }
+
+ return stack;
+ };
+}
+
+var ReactSharedInternals = {
+ ReactCurrentDispatcher: ReactCurrentDispatcher,
+ ReactCurrentOwner: ReactCurrentOwner,
+ // Used by renderers to avoid bundling object-assign twice in UMD bundles:
+ assign: objectAssign
+};
+
+{
+ // Re-export the schedule API(s) for UMD bundles.
+ // This avoids introducing a dependency on a new UMD global in a minor update,
+ // Since that would be a breaking change (e.g. for all existing CodeSandboxes).
+ // This re-export is only required for UMD bundles;
+ // CJS bundles use the shared NPM package.
+ objectAssign(ReactSharedInternals, {
+ Scheduler: {
+ unstable_cancelCallback: unstable_cancelCallback,
+ unstable_shouldYield: unstable_shouldYield,
+ unstable_now: getCurrentTime,
+ unstable_scheduleCallback: unstable_scheduleCallback,
+ unstable_runWithPriority: unstable_runWithPriority,
+ unstable_next: unstable_next,
+ unstable_wrapCallback: unstable_wrapCallback,
+ unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
+ unstable_pauseExecution: unstable_pauseExecution,
+ unstable_continueExecution: unstable_continueExecution,
+ unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
+ unstable_IdlePriority: IdlePriority,
+ unstable_ImmediatePriority: ImmediatePriority,
+ unstable_LowPriority: LowPriority,
+ unstable_NormalPriority: NormalPriority,
+ unstable_UserBlockingPriority: UserBlockingPriority
+ },
+ SchedulerTracing: {
+ __interactionsRef: interactionsRef,
+ __subscriberRef: subscriberRef,
+ unstable_clear: unstable_clear,
+ unstable_getCurrent: unstable_getCurrent,
+ unstable_getThreadID: unstable_getThreadID,
+ unstable_subscribe: unstable_subscribe,
+ unstable_trace: unstable_trace,
+ unstable_unsubscribe: unstable_unsubscribe,
+ unstable_wrap: unstable_wrap
+ }
+ });
+}
+
+{
+ objectAssign(ReactSharedInternals, {
+ // These should not be included in production.
+ ReactDebugCurrentFrame: ReactDebugCurrentFrame,
+ // Shim for React DOM 16.0.0 which still destructured (but not used) this.
+ // TODO: remove in React 17.0.
+ ReactComponentTreeHook: {}
+ });
+}
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warning = warningWithoutStack$1;
+
+{
+ warning = function (condition, format) {
+ if (condition) {
+ return;
+ }
+ var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+ var stack = ReactDebugCurrentFrame.getStackAddendum();
+ // eslint-disable-next-line react-internal/warning-and-invariant-args
+
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ args[_key - 2] = arguments[_key];
+ }
+
+ warningWithoutStack$1.apply(undefined, [false, format + '%s'].concat(args, [stack]));
+ };
+}
+
+var warning$1 = warning;
+
+var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+
+var RESERVED_PROPS = {
+ key: true,
+ ref: true,
+ __self: true,
+ __source: true
+};
+
+var specialPropKeyWarningShown = void 0;
+var specialPropRefWarningShown = void 0;
+
+function hasValidRef(config) {
+ {
+ if (hasOwnProperty$1.call(config, 'ref')) {
+ var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
+ if (getter && getter.isReactWarning) {
+ return false;
+ }
+ }
+ }
+ return config.ref !== undefined;
+}
+
+function hasValidKey(config) {
+ {
+ if (hasOwnProperty$1.call(config, 'key')) {
+ var getter = Object.getOwnPropertyDescriptor(config, 'key').get;
+ if (getter && getter.isReactWarning) {
+ return false;
+ }
+ }
+ }
+ return config.key !== undefined;
+}
+
+function defineKeyPropWarningGetter(props, displayName) {
+ var warnAboutAccessingKey = function () {
+ if (!specialPropKeyWarningShown) {
+ specialPropKeyWarningShown = true;
+ warningWithoutStack$1(false, '%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName);
+ }
+ };
+ warnAboutAccessingKey.isReactWarning = true;
+ Object.defineProperty(props, 'key', {
+ get: warnAboutAccessingKey,
+ configurable: true
+ });
+}
+
+function defineRefPropWarningGetter(props, displayName) {
+ var warnAboutAccessingRef = function () {
+ if (!specialPropRefWarningShown) {
+ specialPropRefWarningShown = true;
+ warningWithoutStack$1(false, '%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName);
+ }
+ };
+ warnAboutAccessingRef.isReactWarning = true;
+ Object.defineProperty(props, 'ref', {
+ get: warnAboutAccessingRef,
+ configurable: true
+ });
+}
+
+/**
+ * Factory method to create a new React element. This no longer adheres to
+ * the class pattern, so do not use new to call it. Also, no instanceof check
+ * will work. Instead test $$typeof field against Symbol.for('react.element') to check
+ * if something is a React Element.
+ *
+ * @param {*} type
+ * @param {*} key
+ * @param {string|object} ref
+ * @param {*} self A *temporary* helper to detect places where `this` is
+ * different from the `owner` when React.createElement is called, so that we
+ * can warn. We want to get rid of owner and replace string `ref`s with arrow
+ * functions, and as long as `this` and owner are the same, there will be no
+ * change in behavior.
+ * @param {*} source An annotation object (added by a transpiler or otherwise)
+ * indicating filename, line number, and/or other information.
+ * @param {*} owner
+ * @param {*} props
+ * @internal
+ */
+var ReactElement = function (type, key, ref, self, source, owner, props) {
+ var element = {
+ // This tag allows us to uniquely identify this as a React Element
+ $$typeof: REACT_ELEMENT_TYPE,
+
+ // Built-in properties that belong on the element
+ type: type,
+ key: key,
+ ref: ref,
+ props: props,
+
+ // Record the component responsible for creating this element.
+ _owner: owner
+ };
+
+ {
+ // The validation flag is currently mutative. We put it on
+ // an external backing store so that we can freeze the whole object.
+ // This can be replaced with a WeakMap once they are implemented in
+ // commonly used development environments.
+ element._store = {};
+
+ // To make comparing ReactElements easier for testing purposes, we make
+ // the validation flag non-enumerable (where possible, which should
+ // include every environment we run tests in), so the test framework
+ // ignores it.
+ Object.defineProperty(element._store, 'validated', {
+ configurable: false,
+ enumerable: false,
+ writable: true,
+ value: false
+ });
+ // self and source are DEV only properties.
+ Object.defineProperty(element, '_self', {
+ configurable: false,
+ enumerable: false,
+ writable: false,
+ value: self
+ });
+ // Two elements created in two different places should be considered
+ // equal for testing purposes and therefore we hide it from enumeration.
+ Object.defineProperty(element, '_source', {
+ configurable: false,
+ enumerable: false,
+ writable: false,
+ value: source
+ });
+ if (Object.freeze) {
+ Object.freeze(element.props);
+ Object.freeze(element);
+ }
+ }
+
+ return element;
+};
+
+/**
+ * Create and return a new ReactElement of the given type.
+ * See https://reactjs.org/docs/react-api.html#createelement
+ */
+function createElement(type, config, children) {
+ var propName = void 0;
+
+ // Reserved names are extracted
+ var props = {};
+
+ var key = null;
+ var ref = null;
+ var self = null;
+ var source = null;
+
+ if (config != null) {
+ if (hasValidRef(config)) {
+ ref = config.ref;
+ }
+ if (hasValidKey(config)) {
+ key = '' + config.key;
+ }
+
+ self = config.__self === undefined ? null : config.__self;
+ source = config.__source === undefined ? null : config.__source;
+ // Remaining properties are added to a new props object
+ for (propName in config) {
+ if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
+ props[propName] = config[propName];
+ }
+ }
+ }
+
+ // Children can be more than one argument, and those are transferred onto
+ // the newly allocated props object.
+ var childrenLength = arguments.length - 2;
+ if (childrenLength === 1) {
+ props.children = children;
+ } else if (childrenLength > 1) {
+ var childArray = Array(childrenLength);
+ for (var i = 0; i < childrenLength; i++) {
+ childArray[i] = arguments[i + 2];
+ }
+ {
+ if (Object.freeze) {
+ Object.freeze(childArray);
+ }
+ }
+ props.children = childArray;
+ }
+
+ // Resolve default props
+ if (type && type.defaultProps) {
+ var defaultProps = type.defaultProps;
+ for (propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
+ }
+ }
+ {
+ if (key || ref) {
+ var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
+ if (key) {
+ defineKeyPropWarningGetter(props, displayName);
+ }
+ if (ref) {
+ defineRefPropWarningGetter(props, displayName);
+ }
+ }
+ }
+ return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
+}
+
+/**
+ * Return a function that produces ReactElements of a given type.
+ * See https://reactjs.org/docs/react-api.html#createfactory
+ */
+
+
+function cloneAndReplaceKey(oldElement, newKey) {
+ var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props);
+
+ return newElement;
+}
+
+/**
+ * Clone and return a new ReactElement using element as the starting point.
+ * See https://reactjs.org/docs/react-api.html#cloneelement
+ */
+function cloneElement(element, config, children) {
+ !!(element === null || element === undefined) ? invariant(false, 'React.cloneElement(...): The argument must be a React element, but you passed %s.', element) : void 0;
+
+ var propName = void 0;
+
+ // Original props are copied
+ var props = objectAssign({}, element.props);
+
+ // Reserved names are extracted
+ var key = element.key;
+ var ref = element.ref;
+ // Self is preserved since the owner is preserved.
+ var self = element._self;
+ // Source is preserved since cloneElement is unlikely to be targeted by a
+ // transpiler, and the original source is probably a better indicator of the
+ // true owner.
+ var source = element._source;
+
+ // Owner will be preserved, unless ref is overridden
+ var owner = element._owner;
+
+ if (config != null) {
+ if (hasValidRef(config)) {
+ // Silently steal the ref from the parent.
+ ref = config.ref;
+ owner = ReactCurrentOwner.current;
+ }
+ if (hasValidKey(config)) {
+ key = '' + config.key;
+ }
+
+ // Remaining properties override existing props
+ var defaultProps = void 0;
+ if (element.type && element.type.defaultProps) {
+ defaultProps = element.type.defaultProps;
+ }
+ for (propName in config) {
+ if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
+ if (config[propName] === undefined && defaultProps !== undefined) {
+ // Resolve default props
+ props[propName] = defaultProps[propName];
+ } else {
+ props[propName] = config[propName];
+ }
+ }
+ }
+ }
+
+ // Children can be more than one argument, and those are transferred onto
+ // the newly allocated props object.
+ var childrenLength = arguments.length - 2;
+ if (childrenLength === 1) {
+ props.children = children;
+ } else if (childrenLength > 1) {
+ var childArray = Array(childrenLength);
+ for (var i = 0; i < childrenLength; i++) {
+ childArray[i] = arguments[i + 2];
+ }
+ props.children = childArray;
+ }
+
+ return ReactElement(element.type, key, ref, self, source, owner, props);
+}
+
+/**
+ * Verifies the object is a ReactElement.
+ * See https://reactjs.org/docs/react-api.html#isvalidelement
+ * @param {?object} object
+ * @return {boolean} True if `object` is a ReactElement.
+ * @final
+ */
+function isValidElement(object) {
+ return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
+}
+
+var SEPARATOR = '.';
+var SUBSEPARATOR = ':';
+
+/**
+ * Escape and wrap key so it is safe to use as a reactid
+ *
+ * @param {string} key to be escaped.
+ * @return {string} the escaped key.
+ */
+function escape(key) {
+ var escapeRegex = /[=:]/g;
+ var escaperLookup = {
+ '=': '=0',
+ ':': '=2'
+ };
+ var escapedString = ('' + key).replace(escapeRegex, function (match) {
+ return escaperLookup[match];
+ });
+
+ return '$' + escapedString;
+}
+
+/**
+ * TODO: Test that a single child and an array with one item have the same key
+ * pattern.
+ */
+
+var didWarnAboutMaps = false;
+
+var userProvidedKeyEscapeRegex = /\/+/g;
+function escapeUserProvidedKey(text) {
+ return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
+}
+
+var POOL_SIZE = 10;
+var traverseContextPool = [];
+function getPooledTraverseContext(mapResult, keyPrefix, mapFunction, mapContext) {
+ if (traverseContextPool.length) {
+ var traverseContext = traverseContextPool.pop();
+ traverseContext.result = mapResult;
+ traverseContext.keyPrefix = keyPrefix;
+ traverseContext.func = mapFunction;
+ traverseContext.context = mapContext;
+ traverseContext.count = 0;
+ return traverseContext;
+ } else {
+ return {
+ result: mapResult,
+ keyPrefix: keyPrefix,
+ func: mapFunction,
+ context: mapContext,
+ count: 0
+ };
+ }
+}
+
+function releaseTraverseContext(traverseContext) {
+ traverseContext.result = null;
+ traverseContext.keyPrefix = null;
+ traverseContext.func = null;
+ traverseContext.context = null;
+ traverseContext.count = 0;
+ if (traverseContextPool.length < POOL_SIZE) {
+ traverseContextPool.push(traverseContext);
+ }
+}
+
+/**
+ * @param {?*} children Children tree container.
+ * @param {!string} nameSoFar Name of the key path so far.
+ * @param {!function} callback Callback to invoke with each child found.
+ * @param {?*} traverseContext Used to pass information throughout the traversal
+ * process.
+ * @return {!number} The number of children in this subtree.
+ */
+function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
+ var type = typeof children;
+
+ if (type === 'undefined' || type === 'boolean') {
+ // All of the above are perceived as null.
+ children = null;
+ }
+
+ var invokeCallback = false;
+
+ if (children === null) {
+ invokeCallback = true;
+ } else {
+ switch (type) {
+ case 'string':
+ case 'number':
+ invokeCallback = true;
+ break;
+ case 'object':
+ switch (children.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ case REACT_PORTAL_TYPE:
+ invokeCallback = true;
+ }
+ }
+ }
+
+ if (invokeCallback) {
+ callback(traverseContext, children,
+ // If it's the only child, treat the name as if it was wrapped in an array
+ // so that it's consistent if the number of children grows.
+ nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
+ return 1;
+ }
+
+ var child = void 0;
+ var nextName = void 0;
+ var subtreeCount = 0; // Count of children found in the current subtree.
+ var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
+
+ if (Array.isArray(children)) {
+ for (var i = 0; i < children.length; i++) {
+ child = children[i];
+ nextName = nextNamePrefix + getComponentKey(child, i);
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
+ }
+ } else {
+ var iteratorFn = getIteratorFn(children);
+ if (typeof iteratorFn === 'function') {
+ {
+ // Warn about using Maps as children
+ if (iteratorFn === children.entries) {
+ !didWarnAboutMaps ? warning$1(false, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.') : void 0;
+ didWarnAboutMaps = true;
+ }
+ }
+
+ var iterator = iteratorFn.call(children);
+ var step = void 0;
+ var ii = 0;
+ while (!(step = iterator.next()).done) {
+ child = step.value;
+ nextName = nextNamePrefix + getComponentKey(child, ii++);
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
+ }
+ } else if (type === 'object') {
+ var addendum = '';
+ {
+ addendum = ' If you meant to render a collection of children, use an array ' + 'instead.' + ReactDebugCurrentFrame.getStackAddendum();
+ }
+ var childrenString = '' + children;
+ invariant(false, 'Objects are not valid as a React child (found: %s).%s', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum);
+ }
+ }
+
+ return subtreeCount;
+}
+
+/**
+ * Traverses children that are typically specified as `props.children`, but
+ * might also be specified through attributes:
+ *
+ * - `traverseAllChildren(this.props.children, ...)`
+ * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
+ *
+ * The `traverseContext` is an optional argument that is passed through the
+ * entire traversal. It can be used to store accumulations or anything else that
+ * the callback might find relevant.
+ *
+ * @param {?*} children Children tree object.
+ * @param {!function} callback To invoke upon traversing each child.
+ * @param {?*} traverseContext Context for traversal.
+ * @return {!number} The number of children in this subtree.
+ */
+function traverseAllChildren(children, callback, traverseContext) {
+ if (children == null) {
+ return 0;
+ }
+
+ return traverseAllChildrenImpl(children, '', callback, traverseContext);
+}
+
+/**
+ * Generate a key string that identifies a component within a set.
+ *
+ * @param {*} component A component that could contain a manual key.
+ * @param {number} index Index that is used if a manual key is not provided.
+ * @return {string}
+ */
+function getComponentKey(component, index) {
+ // Do some typechecking here since we call this blindly. We want to ensure
+ // that we don't block potential future ES APIs.
+ if (typeof component === 'object' && component !== null && component.key != null) {
+ // Explicit key
+ return escape(component.key);
+ }
+ // Implicit key determined by the index in the set
+ return index.toString(36);
+}
+
+function forEachSingleChild(bookKeeping, child, name) {
+ var func = bookKeeping.func,
+ context = bookKeeping.context;
+
+ func.call(context, child, bookKeeping.count++);
+}
+
+/**
+ * Iterates through children that are typically specified as `props.children`.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrenforeach
+ *
+ * The provided forEachFunc(child, index) will be called for each
+ * leaf child.
+ *
+ * @param {?*} children Children tree container.
+ * @param {function(*, int)} forEachFunc
+ * @param {*} forEachContext Context for forEachContext.
+ */
+function forEachChildren(children, forEachFunc, forEachContext) {
+ if (children == null) {
+ return children;
+ }
+ var traverseContext = getPooledTraverseContext(null, null, forEachFunc, forEachContext);
+ traverseAllChildren(children, forEachSingleChild, traverseContext);
+ releaseTraverseContext(traverseContext);
+}
+
+function mapSingleChildIntoContext(bookKeeping, child, childKey) {
+ var result = bookKeeping.result,
+ keyPrefix = bookKeeping.keyPrefix,
+ func = bookKeeping.func,
+ context = bookKeeping.context;
+
+
+ var mappedChild = func.call(context, child, bookKeeping.count++);
+ if (Array.isArray(mappedChild)) {
+ mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, function (c) {
+ return c;
+ });
+ } else if (mappedChild != null) {
+ if (isValidElement(mappedChild)) {
+ mappedChild = cloneAndReplaceKey(mappedChild,
+ // Keep both the (mapped) and old keys if they differ, just as
+ // traverseAllChildren used to do for objects as children
+ keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey);
+ }
+ result.push(mappedChild);
+ }
+}
+
+function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
+ var escapedPrefix = '';
+ if (prefix != null) {
+ escapedPrefix = escapeUserProvidedKey(prefix) + '/';
+ }
+ var traverseContext = getPooledTraverseContext(array, escapedPrefix, func, context);
+ traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
+ releaseTraverseContext(traverseContext);
+}
+
+/**
+ * Maps children that are typically specified as `props.children`.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrenmap
+ *
+ * The provided mapFunction(child, key, index) will be called for each
+ * leaf child.
+ *
+ * @param {?*} children Children tree container.
+ * @param {function(*, int)} func The map function.
+ * @param {*} context Context for mapFunction.
+ * @return {object} Object containing the ordered map of results.
+ */
+function mapChildren(children, func, context) {
+ if (children == null) {
+ return children;
+ }
+ var result = [];
+ mapIntoWithKeyPrefixInternal(children, result, null, func, context);
+ return result;
+}
+
+/**
+ * Count the number of children that are typically specified as
+ * `props.children`.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrencount
+ *
+ * @param {?*} children Children tree container.
+ * @return {number} The number of children.
+ */
+function countChildren(children) {
+ return traverseAllChildren(children, function () {
+ return null;
+ }, null);
+}
+
+/**
+ * Flatten a children object (typically specified as `props.children`) and
+ * return an array with appropriately re-keyed children.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrentoarray
+ */
+function toArray(children) {
+ var result = [];
+ mapIntoWithKeyPrefixInternal(children, result, null, function (child) {
+ return child;
+ });
+ return result;
+}
+
+/**
+ * Returns the first child in a collection of children and verifies that there
+ * is only one child in the collection.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrenonly
+ *
+ * The current implementation of this function assumes that a single child gets
+ * passed without a wrapper, but the purpose of this helper function is to
+ * abstract away the particular structure of children.
+ *
+ * @param {?object} children Child collection structure.
+ * @return {ReactElement} The first and only `ReactElement` contained in the
+ * structure.
+ */
+function onlyChild(children) {
+ !isValidElement(children) ? invariant(false, 'React.Children.only expected to receive a single React element child.') : void 0;
+ return children;
+}
+
+function createContext(defaultValue, calculateChangedBits) {
+ if (calculateChangedBits === undefined) {
+ calculateChangedBits = null;
+ } else {
+ {
+ !(calculateChangedBits === null || typeof calculateChangedBits === 'function') ? warningWithoutStack$1(false, 'createContext: Expected the optional second argument to be a ' + 'function. Instead received: %s', calculateChangedBits) : void 0;
+ }
+ }
+
+ var context = {
+ $$typeof: REACT_CONTEXT_TYPE,
+ _calculateChangedBits: calculateChangedBits,
+ // As a workaround to support multiple concurrent renderers, we categorize
+ // some renderers as primary and others as secondary. We only expect
+ // there to be two concurrent renderers at most: React Native (primary) and
+ // Fabric (secondary); React DOM (primary) and React ART (secondary).
+ // Secondary renderers store their context values on separate fields.
+ _currentValue: defaultValue,
+ _currentValue2: defaultValue,
+ // Used to track how many concurrent renderers this context currently
+ // supports within in a single renderer. Such as parallel server rendering.
+ _threadCount: 0,
+ // These are circular
+ Provider: null,
+ Consumer: null
+ };
+
+ context.Provider = {
+ $$typeof: REACT_PROVIDER_TYPE,
+ _context: context
+ };
+
+ var hasWarnedAboutUsingNestedContextConsumers = false;
+ var hasWarnedAboutUsingConsumerProvider = false;
+
+ {
+ // A separate object, but proxies back to the original context object for
+ // backwards compatibility. It has a different $$typeof, so we can properly
+ // warn for the incorrect usage of Context as a Consumer.
+ var Consumer = {
+ $$typeof: REACT_CONTEXT_TYPE,
+ _context: context,
+ _calculateChangedBits: context._calculateChangedBits
+ };
+ // $FlowFixMe: Flow complains about not setting a value, which is intentional here
+ Object.defineProperties(Consumer, {
+ Provider: {
+ get: function () {
+ if (!hasWarnedAboutUsingConsumerProvider) {
+ hasWarnedAboutUsingConsumerProvider = true;
+ warning$1(false, 'Rendering <Context.Consumer.Provider> is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Provider> instead?');
+ }
+ return context.Provider;
+ },
+ set: function (_Provider) {
+ context.Provider = _Provider;
+ }
+ },
+ _currentValue: {
+ get: function () {
+ return context._currentValue;
+ },
+ set: function (_currentValue) {
+ context._currentValue = _currentValue;
+ }
+ },
+ _currentValue2: {
+ get: function () {
+ return context._currentValue2;
+ },
+ set: function (_currentValue2) {
+ context._currentValue2 = _currentValue2;
+ }
+ },
+ _threadCount: {
+ get: function () {
+ return context._threadCount;
+ },
+ set: function (_threadCount) {
+ context._threadCount = _threadCount;
+ }
+ },
+ Consumer: {
+ get: function () {
+ if (!hasWarnedAboutUsingNestedContextConsumers) {
+ hasWarnedAboutUsingNestedContextConsumers = true;
+ warning$1(false, 'Rendering <Context.Consumer.Consumer> is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?');
+ }
+ return context.Consumer;
+ }
+ }
+ });
+ // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
+ context.Consumer = Consumer;
+ }
+
+ {
+ context._currentRenderer = null;
+ context._currentRenderer2 = null;
+ }
+
+ return context;
+}
+
+function lazy(ctor) {
+ var lazyType = {
+ $$typeof: REACT_LAZY_TYPE,
+ _ctor: ctor,
+ // React uses these fields to store the result.
+ _status: -1,
+ _result: null
+ };
+
+ {
+ // In production, this would just set it on the object.
+ var defaultProps = void 0;
+ var propTypes = void 0;
+ Object.defineProperties(lazyType, {
+ defaultProps: {
+ configurable: true,
+ get: function () {
+ return defaultProps;
+ },
+ set: function (newDefaultProps) {
+ warning$1(false, 'React.lazy(...): It is not supported to assign `defaultProps` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.');
+ defaultProps = newDefaultProps;
+ // Match production behavior more closely:
+ Object.defineProperty(lazyType, 'defaultProps', {
+ enumerable: true
+ });
+ }
+ },
+ propTypes: {
+ configurable: true,
+ get: function () {
+ return propTypes;
+ },
+ set: function (newPropTypes) {
+ warning$1(false, 'React.lazy(...): It is not supported to assign `propTypes` to ' + 'a lazy component import. Either specify them where the component ' + 'is defined, or create a wrapping component around it.');
+ propTypes = newPropTypes;
+ // Match production behavior more closely:
+ Object.defineProperty(lazyType, 'propTypes', {
+ enumerable: true
+ });
+ }
+ }
+ });
+ }
+
+ return lazyType;
+}
+
+function forwardRef(render) {
+ {
+ if (render != null && render.$$typeof === REACT_MEMO_TYPE) {
+ warningWithoutStack$1(false, 'forwardRef requires a render function but received a `memo` ' + 'component. Instead of forwardRef(memo(...)), use ' + 'memo(forwardRef(...)).');
+ } else if (typeof render !== 'function') {
+ warningWithoutStack$1(false, 'forwardRef requires a render function but was given %s.', render === null ? 'null' : typeof render);
+ } else {
+ !(
+ // Do not warn for 0 arguments because it could be due to usage of the 'arguments' object
+ render.length === 0 || render.length === 2) ? warningWithoutStack$1(false, 'forwardRef render functions accept exactly two parameters: props and ref. %s', render.length === 1 ? 'Did you forget to use the ref parameter?' : 'Any additional parameter will be undefined.') : void 0;
+ }
+
+ if (render != null) {
+ !(render.defaultProps == null && render.propTypes == null) ? warningWithoutStack$1(false, 'forwardRef render functions do not support propTypes or defaultProps. ' + 'Did you accidentally pass a React component?') : void 0;
+ }
+ }
+
+ return {
+ $$typeof: REACT_FORWARD_REF_TYPE,
+ render: render
+ };
+}
+
+function isValidElementType(type) {
+ return typeof type === 'string' || typeof type === 'function' ||
+ // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
+ type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || typeof type === 'object' && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE);
+}
+
+function memo(type, compare) {
+ {
+ if (!isValidElementType(type)) {
+ warningWithoutStack$1(false, 'memo: The first argument must be a component. Instead ' + 'received: %s', type === null ? 'null' : typeof type);
+ }
+ }
+ return {
+ $$typeof: REACT_MEMO_TYPE,
+ type: type,
+ compare: compare === undefined ? null : compare
+ };
+}
+
+function resolveDispatcher() {
+ var dispatcher = ReactCurrentDispatcher.current;
+ !(dispatcher !== null) ? invariant(false, 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.') : void 0;
+ return dispatcher;
+}
+
+function useContext(Context, unstable_observedBits) {
+ var dispatcher = resolveDispatcher();
+ {
+ !(unstable_observedBits === undefined) ? warning$1(false, 'useContext() second argument is reserved for future ' + 'use in React. Passing it is not supported. ' + 'You passed: %s.%s', unstable_observedBits, typeof unstable_observedBits === 'number' && Array.isArray(arguments[2]) ? '\n\nDid you call array.map(useContext)? ' + 'Calling Hooks inside a loop is not supported. ' + 'Learn more at https://fb.me/rules-of-hooks' : '') : void 0;
+
+ // TODO: add a more generic warning for invalid values.
+ if (Context._context !== undefined) {
+ var realContext = Context._context;
+ // Don't deduplicate because this legitimately causes bugs
+ // and nobody should be using this in existing code.
+ if (realContext.Consumer === Context) {
+ warning$1(false, 'Calling useContext(Context.Consumer) is not supported, may cause bugs, and will be ' + 'removed in a future major release. Did you mean to call useContext(Context) instead?');
+ } else if (realContext.Provider === Context) {
+ warning$1(false, 'Calling useContext(Context.Provider) is not supported. ' + 'Did you mean to call useContext(Context) instead?');
+ }
+ }
+ }
+ return dispatcher.useContext(Context, unstable_observedBits);
+}
+
+function useState(initialState) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useState(initialState);
+}
+
+function useReducer(reducer, initialArg, init) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useReducer(reducer, initialArg, init);
+}
+
+function useRef(initialValue) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useRef(initialValue);
+}
+
+function useEffect(create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useEffect(create, inputs);
+}
+
+function useLayoutEffect(create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useLayoutEffect(create, inputs);
+}
+
+function useCallback(callback, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useCallback(callback, inputs);
+}
+
+function useMemo(create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useMemo(create, inputs);
+}
+
+function useImperativeHandle(ref, create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useImperativeHandle(ref, create, inputs);
+}
+
+function useDebugValue(value, formatterFn) {
+ {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useDebugValue(value, formatterFn);
+ }
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+
+var ReactPropTypesSecret$1 = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+
+var ReactPropTypesSecret_1 = ReactPropTypesSecret$1;
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+
+var printWarning$1 = function() {};
+
+{
+ var ReactPropTypesSecret = ReactPropTypesSecret_1;
+ var loggedTypeFailures = {};
+
+ printWarning$1 = function(text) {
+ var message = 'Warning: ' + text;
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+ {
+ for (var typeSpecName in typeSpecs) {
+ if (typeSpecs.hasOwnProperty(typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ if (typeof typeSpecs[typeSpecName] !== 'function') {
+ var err = Error(
+ (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' +
+ 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ }
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
+ } catch (ex) {
+ error = ex;
+ }
+ if (error && !(error instanceof Error)) {
+ printWarning$1(
+ (componentName || 'React class') + ': type specification of ' +
+ location + ' `' + typeSpecName + '` is invalid; the type checker ' +
+ 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' +
+ 'You may have forgotten to pass an argument to the type checker ' +
+ 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
+ 'shape all require an argument).'
+ );
+
+ }
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var stack = getStack ? getStack() : '';
+
+ printWarning$1(
+ 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '')
+ );
+ }
+ }
+ }
+ }
+}
+
+var checkPropTypes_1 = checkPropTypes;
+
+/**
+ * ReactElementValidator provides a wrapper around a element factory
+ * which validates the props passed to the element. This is intended to be
+ * used only in DEV and could be replaced by a static type checker for languages
+ * that support it.
+ */
+
+var propTypesMisspellWarningShown = void 0;
+
+{
+ propTypesMisspellWarningShown = false;
+}
+
+function getDeclarationErrorAddendum() {
+ if (ReactCurrentOwner.current) {
+ var name = getComponentName(ReactCurrentOwner.current.type);
+ if (name) {
+ return '\n\nCheck the render method of `' + name + '`.';
+ }
+ }
+ return '';
+}
+
+function getSourceInfoErrorAddendum(elementProps) {
+ if (elementProps !== null && elementProps !== undefined && elementProps.__source !== undefined) {
+ var source = elementProps.__source;
+ var fileName = source.fileName.replace(/^.*[\\\/]/, '');
+ var lineNumber = source.lineNumber;
+ return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
+ }
+ return '';
+}
+
+/**
+ * Warn if there's no key explicitly set on dynamic arrays of children or
+ * object keys are not valid. This allows us to keep track of children between
+ * updates.
+ */
+var ownerHasKeyUseWarning = {};
+
+function getCurrentComponentErrorInfo(parentType) {
+ var info = getDeclarationErrorAddendum();
+
+ if (!info) {
+ var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;
+ if (parentName) {
+ info = '\n\nCheck the top-level render call using <' + parentName + '>.';
+ }
+ }
+ return info;
+}
+
+/**
+ * Warn if the element doesn't have an explicit key assigned to it.
+ * This element is in an array. The array could grow and shrink or be
+ * reordered. All children that haven't already been validated are required to
+ * have a "key" property assigned to it. Error statuses are cached so a warning
+ * will only be shown once.
+ *
+ * @internal
+ * @param {ReactElement} element Element that requires a key.
+ * @param {*} parentType element's parent's type.
+ */
+function validateExplicitKey(element, parentType) {
+ if (!element._store || element._store.validated || element.key != null) {
+ return;
+ }
+ element._store.validated = true;
+
+ var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
+ if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
+ return;
+ }
+ ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
+
+ // Usually the current owner is the offender, but if it accepts children as a
+ // property, it may be the creator of the child that's responsible for
+ // assigning it a key.
+ var childOwner = '';
+ if (element && element._owner && element._owner !== ReactCurrentOwner.current) {
+ // Give the component that originally created this child.
+ childOwner = ' It was passed a child from ' + getComponentName(element._owner.type) + '.';
+ }
+
+ setCurrentlyValidatingElement(element);
+ {
+ warning$1(false, 'Each child in a list should have a unique "key" prop.' + '%s%s See https://fb.me/react-warning-keys for more information.', currentComponentErrorInfo, childOwner);
+ }
+ setCurrentlyValidatingElement(null);
+}
+
+/**
+ * Ensure that every element either is passed in a static location, in an
+ * array with an explicit keys property defined, or in an object literal
+ * with valid key property.
+ *
+ * @internal
+ * @param {ReactNode} node Statically passed child of any type.
+ * @param {*} parentType node's parent's type.
+ */
+function validateChildKeys(node, parentType) {
+ if (typeof node !== 'object') {
+ return;
+ }
+ if (Array.isArray(node)) {
+ for (var i = 0; i < node.length; i++) {
+ var child = node[i];
+ if (isValidElement(child)) {
+ validateExplicitKey(child, parentType);
+ }
+ }
+ } else if (isValidElement(node)) {
+ // This element was passed in a valid location.
+ if (node._store) {
+ node._store.validated = true;
+ }
+ } else if (node) {
+ var iteratorFn = getIteratorFn(node);
+ if (typeof iteratorFn === 'function') {
+ // Entry iterators used to provide implicit keys,
+ // but now we print a separate warning for them later.
+ if (iteratorFn !== node.entries) {
+ var iterator = iteratorFn.call(node);
+ var step = void 0;
+ while (!(step = iterator.next()).done) {
+ if (isValidElement(step.value)) {
+ validateExplicitKey(step.value, parentType);
+ }
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Given an element, validate that its props follow the propTypes definition,
+ * provided by the type.
+ *
+ * @param {ReactElement} element
+ */
+function validatePropTypes(element) {
+ var type = element.type;
+ if (type === null || type === undefined || typeof type === 'string') {
+ return;
+ }
+ var name = getComponentName(type);
+ var propTypes = void 0;
+ if (typeof type === 'function') {
+ propTypes = type.propTypes;
+ } else if (typeof type === 'object' && (type.$$typeof === REACT_FORWARD_REF_TYPE ||
+ // Note: Memo only checks outer props here.
+ // Inner props are checked in the reconciler.
+ type.$$typeof === REACT_MEMO_TYPE)) {
+ propTypes = type.propTypes;
+ } else {
+ return;
+ }
+ if (propTypes) {
+ setCurrentlyValidatingElement(element);
+ checkPropTypes_1(propTypes, element.props, 'prop', name, ReactDebugCurrentFrame.getStackAddendum);
+ setCurrentlyValidatingElement(null);
+ } else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
+ propTypesMisspellWarningShown = true;
+ warningWithoutStack$1(false, 'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', name || 'Unknown');
+ }
+ if (typeof type.getDefaultProps === 'function') {
+ !type.getDefaultProps.isReactClassApproved ? warningWithoutStack$1(false, 'getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.') : void 0;
+ }
+}
+
+/**
+ * Given a fragment, validate that it can only be provided with fragment props
+ * @param {ReactElement} fragment
+ */
+function validateFragmentProps(fragment) {
+ setCurrentlyValidatingElement(fragment);
+
+ var keys = Object.keys(fragment.props);
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ if (key !== 'children' && key !== 'key') {
+ warning$1(false, 'Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.', key);
+ break;
+ }
+ }
+
+ if (fragment.ref !== null) {
+ warning$1(false, 'Invalid attribute `ref` supplied to `React.Fragment`.');
+ }
+
+ setCurrentlyValidatingElement(null);
+}
+
+function createElementWithValidation(type, props, children) {
+ var validType = isValidElementType(type);
+
+ // We warn in this case but don't throw. We expect the element creation to
+ // succeed and there will likely be errors in render.
+ if (!validType) {
+ var info = '';
+ if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
+ info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports.";
+ }
+
+ var sourceInfo = getSourceInfoErrorAddendum(props);
+ if (sourceInfo) {
+ info += sourceInfo;
+ } else {
+ info += getDeclarationErrorAddendum();
+ }
+
+ var typeString = void 0;
+ if (type === null) {
+ typeString = 'null';
+ } else if (Array.isArray(type)) {
+ typeString = 'array';
+ } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
+ typeString = '<' + (getComponentName(type.type) || 'Unknown') + ' />';
+ info = ' Did you accidentally export a JSX literal instead of a component?';
+ } else {
+ typeString = typeof type;
+ }
+
+ warning$1(false, 'React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);
+ }
+
+ var element = createElement.apply(this, arguments);
+
+ // The result can be nullish if a mock or a custom function is used.
+ // TODO: Drop this when these are no longer allowed as the type argument.
+ if (element == null) {
+ return element;
+ }
+
+ // Skip key warning if the type isn't valid since our key validation logic
+ // doesn't expect a non-string/function type and can throw confusing errors.
+ // We don't want exception behavior to differ between dev and prod.
+ // (Rendering will throw with a helpful message and as soon as the type is
+ // fixed, the key warnings will appear.)
+ if (validType) {
+ for (var i = 2; i < arguments.length; i++) {
+ validateChildKeys(arguments[i], type);
+ }
+ }
+
+ if (type === REACT_FRAGMENT_TYPE) {
+ validateFragmentProps(element);
+ } else {
+ validatePropTypes(element);
+ }
+
+ return element;
+}
+
+function createFactoryWithValidation(type) {
+ var validatedFactory = createElementWithValidation.bind(null, type);
+ validatedFactory.type = type;
+ // Legacy hook: remove it
+ {
+ Object.defineProperty(validatedFactory, 'type', {
+ enumerable: false,
+ get: function () {
+ lowPriorityWarning$1(false, 'Factory.type is deprecated. Access the class directly ' + 'before passing it to createFactory.');
+ Object.defineProperty(this, 'type', {
+ value: type
+ });
+ return type;
+ }
+ });
+ }
+
+ return validatedFactory;
+}
+
+function cloneElementWithValidation(element, props, children) {
+ var newElement = cloneElement.apply(this, arguments);
+ for (var i = 2; i < arguments.length; i++) {
+ validateChildKeys(arguments[i], newElement.type);
+ }
+ validatePropTypes(newElement);
+ return newElement;
+}
+
+var React = {
+ Children: {
+ map: mapChildren,
+ forEach: forEachChildren,
+ count: countChildren,
+ toArray: toArray,
+ only: onlyChild
+ },
+
+ createRef: createRef,
+ Component: Component,
+ PureComponent: PureComponent,
+
+ createContext: createContext,
+ forwardRef: forwardRef,
+ lazy: lazy,
+ memo: memo,
+
+ useCallback: useCallback,
+ useContext: useContext,
+ useEffect: useEffect,
+ useImperativeHandle: useImperativeHandle,
+ useDebugValue: useDebugValue,
+ useLayoutEffect: useLayoutEffect,
+ useMemo: useMemo,
+ useReducer: useReducer,
+ useRef: useRef,
+ useState: useState,
+
+ Fragment: REACT_FRAGMENT_TYPE,
+ StrictMode: REACT_STRICT_MODE_TYPE,
+ Suspense: REACT_SUSPENSE_TYPE,
+
+ createElement: createElementWithValidation,
+ cloneElement: cloneElementWithValidation,
+ createFactory: createFactoryWithValidation,
+ isValidElement: isValidElement,
+
+ version: ReactVersion,
+
+ unstable_ConcurrentMode: REACT_CONCURRENT_MODE_TYPE,
+ unstable_Profiler: REACT_PROFILER_TYPE,
+
+ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals
+};
+
+// Note: some APIs are added with feature flags.
+// Make sure that stable builds for open source
+// don't modify the React object to avoid deopts.
+// Also let's not expose their names in stable builds.
+
+if (enableStableConcurrentModeAPIs) {
+ React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
+ React.Profiler = REACT_PROFILER_TYPE;
+ React.unstable_ConcurrentMode = undefined;
+ React.unstable_Profiler = undefined;
+}
+
+
+
+var React$2 = ({
+ default: React
+});
+
+var React$3 = ( React$2 && React ) || React$2;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var react = React$3.default || React$3;
+
+return react;
+
+})));
diff --git a/devtools/client/shared/vendor/react-dom-dev.js b/devtools/client/shared/vendor/react-dom-dev.js
new file mode 100644
index 0000000000..30b45e8c69
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom-dev.js
@@ -0,0 +1,21413 @@
+/** @license React v16.8.6
+ * react-dom.development.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('devtools/client/shared/vendor/react')) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react'], factory) :
+ (global.ReactDOM = factory(global.React));
+}(this, (function (React) { 'use strict';
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+var validateFormat = function () {};
+
+{
+ validateFormat = function (format) {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ };
+}
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ validateFormat(format);
+
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+
+!React ? invariant(false, 'ReactDOM was loaded before React. Make sure you load the React package before loading ReactDOM.') : void 0;
+
+var invokeGuardedCallbackImpl = function (name, func, context, a, b, c, d, e, f) {
+ var funcArgs = Array.prototype.slice.call(arguments, 3);
+ try {
+ func.apply(context, funcArgs);
+ } catch (error) {
+ this.onError(error);
+ }
+};
+
+{
+ // In DEV mode, we swap out invokeGuardedCallback for a special version
+ // that plays more nicely with the browser's DevTools. The idea is to preserve
+ // "Pause on exceptions" behavior. Because React wraps all user-provided
+ // functions in invokeGuardedCallback, and the production version of
+ // invokeGuardedCallback uses a try-catch, all user exceptions are treated
+ // like caught exceptions, and the DevTools won't pause unless the developer
+ // takes the extra step of enabling pause on caught exceptions. This is
+ // unintuitive, though, because even though React has caught the error, from
+ // the developer's perspective, the error is uncaught.
+ //
+ // To preserve the expected "Pause on exceptions" behavior, we don't use a
+ // try-catch in DEV. Instead, we synchronously dispatch a fake event to a fake
+ // DOM node, and call the user-provided callback from inside an event handler
+ // for that fake event. If the callback throws, the error is "captured" using
+ // a global event handler. But because the error happens in a different
+ // event loop context, it does not interrupt the normal program flow.
+ // Effectively, this gives us try-catch behavior without actually using
+ // try-catch. Neat!
+
+ // Check that the browser supports the APIs we need to implement our special
+ // DEV version of invokeGuardedCallback
+ if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') {
+ var fakeNode = document.createElementNS('http://www.w3.org/1999/xhtml', 'react');
+
+ var invokeGuardedCallbackDev = function (name, func, context, a, b, c, d, e, f) {
+ // If document doesn't exist we know for sure we will crash in this method
+ // when we call document.createEvent(). However this can cause confusing
+ // errors: https://github.com/facebookincubator/create-react-app/issues/3482
+ // So we preemptively throw with a better message instead.
+ !(typeof document !== 'undefined') ? invariant(false, 'The `document` global was defined when React was initialized, but is not defined anymore. This can happen in a test environment if a component schedules an update from an asynchronous callback, but the test has already finished running. To solve this, you can either unmount the component at the end of your test (and ensure that any asynchronous operations get canceled in `componentWillUnmount`), or you can change the test itself to be asynchronous.') : void 0;
+ var evt = document.createEvent('Event');
+
+ // Keeps track of whether the user-provided callback threw an error. We
+ // set this to true at the beginning, then set it to false right after
+ // calling the function. If the function errors, `didError` will never be
+ // set to false. This strategy works even if the browser is flaky and
+ // fails to call our global error handler, because it doesn't rely on
+ // the error event at all.
+ var didError = true;
+
+ // Keeps track of the value of window.event so that we can reset it
+ // during the callback to let user code access window.event in the
+ // browsers that support it.
+ var windowEvent = window.event;
+
+ // Keeps track of the descriptor of window.event to restore it after event
+ // dispatching: https://github.com/facebook/react/issues/13688
+ var windowEventDescriptor = Object.getOwnPropertyDescriptor(window, 'event');
+
+ // Create an event handler for our fake event. We will synchronously
+ // dispatch our fake event using `dispatchEvent`. Inside the handler, we
+ // call the user-provided callback.
+ var funcArgs = Array.prototype.slice.call(arguments, 3);
+ function callCallback() {
+ // We immediately remove the callback from event listeners so that
+ // nested `invokeGuardedCallback` calls do not clash. Otherwise, a
+ // nested call would trigger the fake event handlers of any call higher
+ // in the stack.
+ fakeNode.removeEventListener(evtType, callCallback, false);
+
+ // We check for window.hasOwnProperty('event') to prevent the
+ // window.event assignment in both IE <= 10 as they throw an error
+ // "Member not found" in strict mode, and in Firefox which does not
+ // support window.event.
+ if (typeof window.event !== 'undefined' && window.hasOwnProperty('event')) {
+ window.event = windowEvent;
+ }
+
+ func.apply(context, funcArgs);
+ didError = false;
+ }
+
+ // Create a global error event handler. We use this to capture the value
+ // that was thrown. It's possible that this error handler will fire more
+ // than once; for example, if non-React code also calls `dispatchEvent`
+ // and a handler for that event throws. We should be resilient to most of
+ // those cases. Even if our error event handler fires more than once, the
+ // last error event is always used. If the callback actually does error,
+ // we know that the last error event is the correct one, because it's not
+ // possible for anything else to have happened in between our callback
+ // erroring and the code that follows the `dispatchEvent` call below. If
+ // the callback doesn't error, but the error event was fired, we know to
+ // ignore it because `didError` will be false, as described above.
+ var error = void 0;
+ // Use this to track whether the error event is ever called.
+ var didSetError = false;
+ var isCrossOriginError = false;
+
+ function handleWindowError(event) {
+ error = event.error;
+ didSetError = true;
+ if (error === null && event.colno === 0 && event.lineno === 0) {
+ isCrossOriginError = true;
+ }
+ if (event.defaultPrevented) {
+ // Some other error handler has prevented default.
+ // Browsers silence the error report if this happens.
+ // We'll remember this to later decide whether to log it or not.
+ if (error != null && typeof error === 'object') {
+ try {
+ error._suppressLogging = true;
+ } catch (inner) {
+ // Ignore.
+ }
+ }
+ }
+ }
+
+ // Create a fake event type.
+ var evtType = 'react-' + (name ? name : 'invokeguardedcallback');
+
+ // Attach our event handlers
+ window.addEventListener('error', handleWindowError);
+ fakeNode.addEventListener(evtType, callCallback, false);
+
+ // Synchronously dispatch our fake event. If the user-provided function
+ // errors, it will trigger our global error handler.
+ evt.initEvent(evtType, false, false);
+ fakeNode.dispatchEvent(evt);
+
+ if (windowEventDescriptor) {
+ Object.defineProperty(window, 'event', windowEventDescriptor);
+ }
+
+ if (didError) {
+ if (!didSetError) {
+ // The callback errored, but the error event never fired.
+ error = new Error('An error was thrown inside one of your components, but React ' + "doesn't know what it was. This is likely due to browser " + 'flakiness. React does its best to preserve the "Pause on ' + 'exceptions" behavior of the DevTools, which requires some ' + "DEV-mode only tricks. It's possible that these don't work in " + 'your browser. Try triggering the error in production mode, ' + 'or switching to a modern browser. If you suspect that this is ' + 'actually an issue with React, please file an issue.');
+ } else if (isCrossOriginError) {
+ error = new Error("A cross-origin error was thrown. React doesn't have access to " + 'the actual error object in development. ' + 'See https://fb.me/react-crossorigin-error for more information.');
+ }
+ this.onError(error);
+ }
+
+ // Remove our event listeners
+ window.removeEventListener('error', handleWindowError);
+ };
+
+ invokeGuardedCallbackImpl = invokeGuardedCallbackDev;
+ }
+}
+
+var invokeGuardedCallbackImpl$1 = invokeGuardedCallbackImpl;
+
+// Used by Fiber to simulate a try-catch.
+var hasError = false;
+var caughtError = null;
+
+// Used by event system to capture/rethrow the first error.
+var hasRethrowError = false;
+var rethrowError = null;
+
+var reporter = {
+ onError: function (error) {
+ hasError = true;
+ caughtError = error;
+ }
+};
+
+/**
+ * Call a function while guarding against errors that happens within it.
+ * Returns an error if it throws, otherwise null.
+ *
+ * In production, this is implemented using a try-catch. The reason we don't
+ * use a try-catch directly is so that we can swap out a different
+ * implementation in DEV mode.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
+ hasError = false;
+ caughtError = null;
+ invokeGuardedCallbackImpl$1.apply(reporter, arguments);
+}
+
+/**
+ * Same as invokeGuardedCallback, but instead of returning an error, it stores
+ * it in a global so it can be rethrown by `rethrowCaughtError` later.
+ * TODO: See if caughtError and rethrowError can be unified.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+function invokeGuardedCallbackAndCatchFirstError(name, func, context, a, b, c, d, e, f) {
+ invokeGuardedCallback.apply(this, arguments);
+ if (hasError) {
+ var error = clearCaughtError();
+ if (!hasRethrowError) {
+ hasRethrowError = true;
+ rethrowError = error;
+ }
+ }
+}
+
+/**
+ * During execution of guarded functions we will capture the first error which
+ * we will rethrow to be handled by the top level error handler.
+ */
+function rethrowCaughtError() {
+ if (hasRethrowError) {
+ var error = rethrowError;
+ hasRethrowError = false;
+ rethrowError = null;
+ throw error;
+ }
+}
+
+function hasCaughtError() {
+ return hasError;
+}
+
+function clearCaughtError() {
+ if (hasError) {
+ var error = caughtError;
+ hasError = false;
+ caughtError = null;
+ return error;
+ } else {
+ invariant(false, 'clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue.');
+ }
+}
+
+/**
+ * Injectable ordering of event plugins.
+ */
+var eventPluginOrder = null;
+
+/**
+ * Injectable mapping from names to event plugin modules.
+ */
+var namesToPlugins = {};
+
+/**
+ * Recomputes the plugin list using the injected plugins and plugin ordering.
+ *
+ * @private
+ */
+function recomputePluginOrdering() {
+ if (!eventPluginOrder) {
+ // Wait until an `eventPluginOrder` is injected.
+ return;
+ }
+ for (var pluginName in namesToPlugins) {
+ var pluginModule = namesToPlugins[pluginName];
+ var pluginIndex = eventPluginOrder.indexOf(pluginName);
+ !(pluginIndex > -1) ? invariant(false, 'EventPluginRegistry: Cannot inject event plugins that do not exist in the plugin ordering, `%s`.', pluginName) : void 0;
+ if (plugins[pluginIndex]) {
+ continue;
+ }
+ !pluginModule.extractEvents ? invariant(false, 'EventPluginRegistry: Event plugins must implement an `extractEvents` method, but `%s` does not.', pluginName) : void 0;
+ plugins[pluginIndex] = pluginModule;
+ var publishedEvents = pluginModule.eventTypes;
+ for (var eventName in publishedEvents) {
+ !publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName) ? invariant(false, 'EventPluginRegistry: Failed to publish event `%s` for plugin `%s`.', eventName, pluginName) : void 0;
+ }
+ }
+}
+
+/**
+ * Publishes an event so that it can be dispatched by the supplied plugin.
+ *
+ * @param {object} dispatchConfig Dispatch configuration for the event.
+ * @param {object} PluginModule Plugin publishing the event.
+ * @return {boolean} True if the event was successfully published.
+ * @private
+ */
+function publishEventForPlugin(dispatchConfig, pluginModule, eventName) {
+ !!eventNameDispatchConfigs.hasOwnProperty(eventName) ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same event name, `%s`.', eventName) : void 0;
+ eventNameDispatchConfigs[eventName] = dispatchConfig;
+
+ var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
+ if (phasedRegistrationNames) {
+ for (var phaseName in phasedRegistrationNames) {
+ if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
+ var phasedRegistrationName = phasedRegistrationNames[phaseName];
+ publishRegistrationName(phasedRegistrationName, pluginModule, eventName);
+ }
+ }
+ return true;
+ } else if (dispatchConfig.registrationName) {
+ publishRegistrationName(dispatchConfig.registrationName, pluginModule, eventName);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Publishes a registration name that is used to identify dispatched events.
+ *
+ * @param {string} registrationName Registration name to add.
+ * @param {object} PluginModule Plugin publishing the event.
+ * @private
+ */
+function publishRegistrationName(registrationName, pluginModule, eventName) {
+ !!registrationNameModules[registrationName] ? invariant(false, 'EventPluginHub: More than one plugin attempted to publish the same registration name, `%s`.', registrationName) : void 0;
+ registrationNameModules[registrationName] = pluginModule;
+ registrationNameDependencies[registrationName] = pluginModule.eventTypes[eventName].dependencies;
+
+ {
+ var lowerCasedName = registrationName.toLowerCase();
+ possibleRegistrationNames[lowerCasedName] = registrationName;
+
+ if (registrationName === 'onDoubleClick') {
+ possibleRegistrationNames.ondblclick = registrationName;
+ }
+ }
+}
+
+/**
+ * Registers plugins so that they can extract and dispatch events.
+ *
+ * @see {EventPluginHub}
+ */
+
+/**
+ * Ordered list of injected plugins.
+ */
+var plugins = [];
+
+/**
+ * Mapping from event name to dispatch config
+ */
+var eventNameDispatchConfigs = {};
+
+/**
+ * Mapping from registration name to plugin module
+ */
+var registrationNameModules = {};
+
+/**
+ * Mapping from registration name to event name
+ */
+var registrationNameDependencies = {};
+
+/**
+ * Mapping from lowercase registration names to the properly cased version,
+ * used to warn in the case of missing event handlers. Available
+ * only in true.
+ * @type {Object}
+ */
+var possibleRegistrationNames = {};
+// Trust the developer to only use possibleRegistrationNames in true
+
+/**
+ * Injects an ordering of plugins (by plugin name). This allows the ordering
+ * to be decoupled from injection of the actual plugins so that ordering is
+ * always deterministic regardless of packaging, on-the-fly injection, etc.
+ *
+ * @param {array} InjectedEventPluginOrder
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginOrder}
+ */
+function injectEventPluginOrder(injectedEventPluginOrder) {
+ !!eventPluginOrder ? invariant(false, 'EventPluginRegistry: Cannot inject event plugin ordering more than once. You are likely trying to load more than one copy of React.') : void 0;
+ // Clone the ordering so it cannot be dynamically mutated.
+ eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
+ recomputePluginOrdering();
+}
+
+/**
+ * Injects plugins to be used by `EventPluginHub`. The plugin names must be
+ * in the ordering injected by `injectEventPluginOrder`.
+ *
+ * Plugins can be injected as part of page initialization or on-the-fly.
+ *
+ * @param {object} injectedNamesToPlugins Map from names to plugin modules.
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginsByName}
+ */
+function injectEventPluginsByName(injectedNamesToPlugins) {
+ var isOrderingDirty = false;
+ for (var pluginName in injectedNamesToPlugins) {
+ if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
+ continue;
+ }
+ var pluginModule = injectedNamesToPlugins[pluginName];
+ if (!namesToPlugins.hasOwnProperty(pluginName) || namesToPlugins[pluginName] !== pluginModule) {
+ !!namesToPlugins[pluginName] ? invariant(false, 'EventPluginRegistry: Cannot inject two different event plugins using the same name, `%s`.', pluginName) : void 0;
+ namesToPlugins[pluginName] = pluginModule;
+ isOrderingDirty = true;
+ }
+ }
+ if (isOrderingDirty) {
+ recomputePluginOrdering();
+ }
+}
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warningWithoutStack = function () {};
+
+{
+ warningWithoutStack = function (condition, format) {
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ args[_key - 2] = arguments[_key];
+ }
+
+ if (format === undefined) {
+ throw new Error('`warningWithoutStack(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (args.length > 8) {
+ // Check before the condition to catch violations early.
+ throw new Error('warningWithoutStack() currently supports at most 8 arguments.');
+ }
+ if (condition) {
+ return;
+ }
+ if (typeof console !== 'undefined') {
+ var argsWithFormat = args.map(function (item) {
+ return '' + item;
+ });
+ argsWithFormat.unshift('Warning: ' + format);
+
+ // We intentionally don't use spread (or .apply) directly because it
+ // breaks IE9: https://github.com/facebook/react/issues/13610
+ Function.prototype.apply.call(console.error, console, argsWithFormat);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+var warningWithoutStack$1 = warningWithoutStack;
+
+var getFiberCurrentPropsFromNode = null;
+var getInstanceFromNode = null;
+var getNodeFromInstance = null;
+
+function setComponentTree(getFiberCurrentPropsFromNodeImpl, getInstanceFromNodeImpl, getNodeFromInstanceImpl) {
+ getFiberCurrentPropsFromNode = getFiberCurrentPropsFromNodeImpl;
+ getInstanceFromNode = getInstanceFromNodeImpl;
+ getNodeFromInstance = getNodeFromInstanceImpl;
+ {
+ !(getNodeFromInstance && getInstanceFromNode) ? warningWithoutStack$1(false, 'EventPluginUtils.setComponentTree(...): Injected ' + 'module is missing getNodeFromInstance or getInstanceFromNode.') : void 0;
+ }
+}
+
+var validateEventDispatches = void 0;
+{
+ validateEventDispatches = function (event) {
+ var dispatchListeners = event._dispatchListeners;
+ var dispatchInstances = event._dispatchInstances;
+
+ var listenersIsArr = Array.isArray(dispatchListeners);
+ var listenersLen = listenersIsArr ? dispatchListeners.length : dispatchListeners ? 1 : 0;
+
+ var instancesIsArr = Array.isArray(dispatchInstances);
+ var instancesLen = instancesIsArr ? dispatchInstances.length : dispatchInstances ? 1 : 0;
+
+ !(instancesIsArr === listenersIsArr && instancesLen === listenersLen) ? warningWithoutStack$1(false, 'EventPluginUtils: Invalid `event`.') : void 0;
+ };
+}
+
+/**
+ * Dispatch the event to the listener.
+ * @param {SyntheticEvent} event SyntheticEvent to handle
+ * @param {function} listener Application-level callback
+ * @param {*} inst Internal component instance
+ */
+function executeDispatch(event, listener, inst) {
+ var type = event.type || 'unknown-event';
+ event.currentTarget = getNodeFromInstance(inst);
+ invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
+ event.currentTarget = null;
+}
+
+/**
+ * Standard/simple iteration through an event's collected dispatches.
+ */
+function executeDispatchesInOrder(event) {
+ var dispatchListeners = event._dispatchListeners;
+ var dispatchInstances = event._dispatchInstances;
+ {
+ validateEventDispatches(event);
+ }
+ if (Array.isArray(dispatchListeners)) {
+ for (var i = 0; i < dispatchListeners.length; i++) {
+ if (event.isPropagationStopped()) {
+ break;
+ }
+ // Listeners and Instances are two parallel arrays that are always in sync.
+ executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
+ }
+ } else if (dispatchListeners) {
+ executeDispatch(event, dispatchListeners, dispatchInstances);
+ }
+ event._dispatchListeners = null;
+ event._dispatchInstances = null;
+}
+
+/**
+ * @see executeDispatchesInOrderStopAtTrueImpl
+ */
+
+
+/**
+ * Execution of a "direct" dispatch - there must be at most one dispatch
+ * accumulated on the event or it is considered an error. It doesn't really make
+ * sense for an event with multiple dispatches (bubbled) to keep track of the
+ * return values at each dispatch execution, but it does tend to make sense when
+ * dealing with "direct" dispatches.
+ *
+ * @return {*} The return value of executing the single dispatch.
+ */
+
+
+/**
+ * @param {SyntheticEvent} event
+ * @return {boolean} True iff number of dispatches accumulated is greater than 0.
+ */
+
+/**
+ * Accumulates items that must not be null or undefined into the first one. This
+ * is used to conserve memory by avoiding array allocations, and thus sacrifices
+ * API cleanness. Since `current` can be null before being passed in and not
+ * null after this function, make sure to assign it back to `current`:
+ *
+ * `a = accumulateInto(a, b);`
+ *
+ * This API should be sparingly used. Try `accumulate` for something cleaner.
+ *
+ * @return {*|array<*>} An accumulation of items.
+ */
+
+function accumulateInto(current, next) {
+ !(next != null) ? invariant(false, 'accumulateInto(...): Accumulated items must not be null or undefined.') : void 0;
+
+ if (current == null) {
+ return next;
+ }
+
+ // Both are not empty. Warning: Never call x.concat(y) when you are not
+ // certain that x is an Array (x could be a string with concat method).
+ if (Array.isArray(current)) {
+ if (Array.isArray(next)) {
+ current.push.apply(current, next);
+ return current;
+ }
+ current.push(next);
+ return current;
+ }
+
+ if (Array.isArray(next)) {
+ // A bit too dangerous to mutate `next`.
+ return [current].concat(next);
+ }
+
+ return [current, next];
+}
+
+/**
+ * @param {array} arr an "accumulation" of items which is either an Array or
+ * a single item. Useful when paired with the `accumulate` module. This is a
+ * simple utility that allows us to reason about a collection of items, but
+ * handling the case when there is exactly one item (and we do not need to
+ * allocate an array).
+ * @param {function} cb Callback invoked with each element or a collection.
+ * @param {?} [scope] Scope used as `this` in a callback.
+ */
+function forEachAccumulated(arr, cb, scope) {
+ if (Array.isArray(arr)) {
+ arr.forEach(cb, scope);
+ } else if (arr) {
+ cb.call(scope, arr);
+ }
+}
+
+/**
+ * Internal queue of events that have accumulated their dispatches and are
+ * waiting to have their dispatches executed.
+ */
+var eventQueue = null;
+
+/**
+ * Dispatches an event and releases it back into the pool, unless persistent.
+ *
+ * @param {?object} event Synthetic event to be dispatched.
+ * @private
+ */
+var executeDispatchesAndRelease = function (event) {
+ if (event) {
+ executeDispatchesInOrder(event);
+
+ if (!event.isPersistent()) {
+ event.constructor.release(event);
+ }
+ }
+};
+var executeDispatchesAndReleaseTopLevel = function (e) {
+ return executeDispatchesAndRelease(e);
+};
+
+function isInteractive(tag) {
+ return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';
+}
+
+function shouldPreventMouseEvent(name, type, props) {
+ switch (name) {
+ case 'onClick':
+ case 'onClickCapture':
+ case 'onDoubleClick':
+ case 'onDoubleClickCapture':
+ case 'onMouseDown':
+ case 'onMouseDownCapture':
+ case 'onMouseMove':
+ case 'onMouseMoveCapture':
+ case 'onMouseUp':
+ case 'onMouseUpCapture':
+ return !!(props.disabled && isInteractive(type));
+ default:
+ return false;
+ }
+}
+
+/**
+ * This is a unified interface for event plugins to be installed and configured.
+ *
+ * Event plugins can implement the following properties:
+ *
+ * `extractEvents` {function(string, DOMEventTarget, string, object): *}
+ * Required. When a top-level event is fired, this method is expected to
+ * extract synthetic events that will in turn be queued and dispatched.
+ *
+ * `eventTypes` {object}
+ * Optional, plugins that fire events must publish a mapping of registration
+ * names that are used to register listeners. Values of this mapping must
+ * be objects that contain `registrationName` or `phasedRegistrationNames`.
+ *
+ * `executeDispatch` {function(object, function, string)}
+ * Optional, allows plugins to override how an event gets dispatched. By
+ * default, the listener is simply invoked.
+ *
+ * Each plugin that is injected into `EventsPluginHub` is immediately operable.
+ *
+ * @public
+ */
+
+/**
+ * Methods for injecting dependencies.
+ */
+var injection = {
+ /**
+ * @param {array} InjectedEventPluginOrder
+ * @public
+ */
+ injectEventPluginOrder: injectEventPluginOrder,
+
+ /**
+ * @param {object} injectedNamesToPlugins Map from names to plugin modules.
+ */
+ injectEventPluginsByName: injectEventPluginsByName
+};
+
+/**
+ * @param {object} inst The instance, which is the source of events.
+ * @param {string} registrationName Name of listener (e.g. `onClick`).
+ * @return {?function} The stored callback.
+ */
+function getListener(inst, registrationName) {
+ var listener = void 0;
+
+ // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
+ // live here; needs to be moved to a better place soon
+ var stateNode = inst.stateNode;
+ if (!stateNode) {
+ // Work in progress (ex: onload events in incremental mode).
+ return null;
+ }
+ var props = getFiberCurrentPropsFromNode(stateNode);
+ if (!props) {
+ // Work in progress.
+ return null;
+ }
+ listener = props[registrationName];
+ if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
+ return null;
+ }
+ !(!listener || typeof listener === 'function') ? invariant(false, 'Expected `%s` listener to be a function, instead got a value of `%s` type.', registrationName, typeof listener) : void 0;
+ return listener;
+}
+
+/**
+ * Allows registered plugins an opportunity to extract events from top-level
+ * native browser events.
+ *
+ * @return {*} An accumulation of synthetic events.
+ * @internal
+ */
+function extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var events = null;
+ for (var i = 0; i < plugins.length; i++) {
+ // Not every plugin in the ordering may be loaded at runtime.
+ var possiblePlugin = plugins[i];
+ if (possiblePlugin) {
+ var extractedEvents = possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+ if (extractedEvents) {
+ events = accumulateInto(events, extractedEvents);
+ }
+ }
+ }
+ return events;
+}
+
+function runEventsInBatch(events) {
+ if (events !== null) {
+ eventQueue = accumulateInto(eventQueue, events);
+ }
+
+ // Set `eventQueue` to null before processing it so that we can tell if more
+ // events get enqueued while processing.
+ var processingEventQueue = eventQueue;
+ eventQueue = null;
+
+ if (!processingEventQueue) {
+ return;
+ }
+
+ forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
+ !!eventQueue ? invariant(false, 'processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented.') : void 0;
+ // This would be a good time to rethrow if any of the event handlers threw.
+ rethrowCaughtError();
+}
+
+function runExtractedEventsInBatch(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var events = extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+ runEventsInBatch(events);
+}
+
+var FunctionComponent = 0;
+var ClassComponent = 1;
+var IndeterminateComponent = 2; // Before we know whether it is function or class
+var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
+var HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
+var HostComponent = 5;
+var HostText = 6;
+var Fragment = 7;
+var Mode = 8;
+var ContextConsumer = 9;
+var ContextProvider = 10;
+var ForwardRef = 11;
+var Profiler = 12;
+var SuspenseComponent = 13;
+var MemoComponent = 14;
+var SimpleMemoComponent = 15;
+var LazyComponent = 16;
+var IncompleteClassComponent = 17;
+var DehydratedSuspenseComponent = 18;
+
+var randomKey = Math.random().toString(36).slice(2);
+var internalInstanceKey = '__reactInternalInstance$' + randomKey;
+var internalEventHandlersKey = '__reactEventHandlers$' + randomKey;
+
+function precacheFiberNode(hostInst, node) {
+ node[internalInstanceKey] = hostInst;
+}
+
+/**
+ * Given a DOM node, return the closest ReactDOMComponent or
+ * ReactDOMTextComponent instance ancestor.
+ */
+function getClosestInstanceFromNode(node) {
+ if (node[internalInstanceKey]) {
+ return node[internalInstanceKey];
+ }
+
+ while (!node[internalInstanceKey]) {
+ if (node.parentNode) {
+ node = node.parentNode;
+ } else {
+ // Top of the tree. This node must not be part of a React tree (or is
+ // unmounted, potentially).
+ return null;
+ }
+ }
+
+ var inst = node[internalInstanceKey];
+ if (inst.tag === HostComponent || inst.tag === HostText) {
+ // In Fiber, this will always be the deepest root.
+ return inst;
+ }
+
+ return null;
+}
+
+/**
+ * Given a DOM node, return the ReactDOMComponent or ReactDOMTextComponent
+ * instance, or null if the node was not rendered by this React.
+ */
+function getInstanceFromNode$1(node) {
+ var inst = node[internalInstanceKey];
+ if (inst) {
+ if (inst.tag === HostComponent || inst.tag === HostText) {
+ return inst;
+ } else {
+ return null;
+ }
+ }
+ return null;
+}
+
+/**
+ * Given a ReactDOMComponent or ReactDOMTextComponent, return the corresponding
+ * DOM node.
+ */
+function getNodeFromInstance$1(inst) {
+ if (inst.tag === HostComponent || inst.tag === HostText) {
+ // In Fiber this, is just the state node right now. We assume it will be
+ // a host component or host text.
+ return inst.stateNode;
+ }
+
+ // Without this first invariant, passing a non-DOM-component triggers the next
+ // invariant for a missing parent, which is super confusing.
+ invariant(false, 'getNodeFromInstance: Invalid argument.');
+}
+
+function getFiberCurrentPropsFromNode$1(node) {
+ return node[internalEventHandlersKey] || null;
+}
+
+function updateFiberProps(node, props) {
+ node[internalEventHandlersKey] = props;
+}
+
+function getParent(inst) {
+ do {
+ inst = inst.return;
+ // TODO: If this is a HostRoot we might want to bail out.
+ // That is depending on if we want nested subtrees (layers) to bubble
+ // events to their parent. We could also go through parentNode on the
+ // host node but that wouldn't work for React Native and doesn't let us
+ // do the portal feature.
+ } while (inst && inst.tag !== HostComponent);
+ if (inst) {
+ return inst;
+ }
+ return null;
+}
+
+/**
+ * Return the lowest common ancestor of A and B, or null if they are in
+ * different trees.
+ */
+function getLowestCommonAncestor(instA, instB) {
+ var depthA = 0;
+ for (var tempA = instA; tempA; tempA = getParent(tempA)) {
+ depthA++;
+ }
+ var depthB = 0;
+ for (var tempB = instB; tempB; tempB = getParent(tempB)) {
+ depthB++;
+ }
+
+ // If A is deeper, crawl up.
+ while (depthA - depthB > 0) {
+ instA = getParent(instA);
+ depthA--;
+ }
+
+ // If B is deeper, crawl up.
+ while (depthB - depthA > 0) {
+ instB = getParent(instB);
+ depthB--;
+ }
+
+ // Walk in lockstep until we find a match.
+ var depth = depthA;
+ while (depth--) {
+ if (instA === instB || instA === instB.alternate) {
+ return instA;
+ }
+ instA = getParent(instA);
+ instB = getParent(instB);
+ }
+ return null;
+}
+
+/**
+ * Return if A is an ancestor of B.
+ */
+
+
+/**
+ * Return the parent instance of the passed-in instance.
+ */
+
+
+/**
+ * Simulates the traversal of a two-phase, capture/bubble event dispatch.
+ */
+function traverseTwoPhase(inst, fn, arg) {
+ var path = [];
+ while (inst) {
+ path.push(inst);
+ inst = getParent(inst);
+ }
+ var i = void 0;
+ for (i = path.length; i-- > 0;) {
+ fn(path[i], 'captured', arg);
+ }
+ for (i = 0; i < path.length; i++) {
+ fn(path[i], 'bubbled', arg);
+ }
+}
+
+/**
+ * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
+ * should would receive a `mouseEnter` or `mouseLeave` event.
+ *
+ * Does not invoke the callback on the nearest common ancestor because nothing
+ * "entered" or "left" that element.
+ */
+function traverseEnterLeave(from, to, fn, argFrom, argTo) {
+ var common = from && to ? getLowestCommonAncestor(from, to) : null;
+ var pathFrom = [];
+ while (true) {
+ if (!from) {
+ break;
+ }
+ if (from === common) {
+ break;
+ }
+ var alternate = from.alternate;
+ if (alternate !== null && alternate === common) {
+ break;
+ }
+ pathFrom.push(from);
+ from = getParent(from);
+ }
+ var pathTo = [];
+ while (true) {
+ if (!to) {
+ break;
+ }
+ if (to === common) {
+ break;
+ }
+ var _alternate = to.alternate;
+ if (_alternate !== null && _alternate === common) {
+ break;
+ }
+ pathTo.push(to);
+ to = getParent(to);
+ }
+ for (var i = 0; i < pathFrom.length; i++) {
+ fn(pathFrom[i], 'bubbled', argFrom);
+ }
+ for (var _i = pathTo.length; _i-- > 0;) {
+ fn(pathTo[_i], 'captured', argTo);
+ }
+}
+
+/**
+ * Some event types have a notion of different registration names for different
+ * "phases" of propagation. This finds listeners by a given phase.
+ */
+function listenerAtPhase(inst, event, propagationPhase) {
+ var registrationName = event.dispatchConfig.phasedRegistrationNames[propagationPhase];
+ return getListener(inst, registrationName);
+}
+
+/**
+ * A small set of propagation patterns, each of which will accept a small amount
+ * of information, and generate a set of "dispatch ready event objects" - which
+ * are sets of events that have already been annotated with a set of dispatched
+ * listener functions/ids. The API is designed this way to discourage these
+ * propagation strategies from actually executing the dispatches, since we
+ * always want to collect the entire set of dispatches before executing even a
+ * single one.
+ */
+
+/**
+ * Tags a `SyntheticEvent` with dispatched listeners. Creating this function
+ * here, allows us to not have to bind or create functions for each event.
+ * Mutating the event's members allows us to not have to create a wrapping
+ * "dispatch" object that pairs the event with the listener.
+ */
+function accumulateDirectionalDispatches(inst, phase, event) {
+ {
+ !inst ? warningWithoutStack$1(false, 'Dispatching inst must not be null') : void 0;
+ }
+ var listener = listenerAtPhase(inst, event, phase);
+ if (listener) {
+ event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
+ }
+}
+
+/**
+ * Collect dispatches (must be entirely collected before dispatching - see unit
+ * tests). Lazily allocate the array to conserve memory. We must loop through
+ * each event and perform the traversal for each one. We cannot perform a
+ * single traversal for the entire collection of events because each event may
+ * have a different target.
+ */
+function accumulateTwoPhaseDispatchesSingle(event) {
+ if (event && event.dispatchConfig.phasedRegistrationNames) {
+ traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
+ }
+}
+
+/**
+ * Accumulates without regard to direction, does not look for phased
+ * registration names. Same as `accumulateDirectDispatchesSingle` but without
+ * requiring that the `dispatchMarker` be the same as the dispatched ID.
+ */
+function accumulateDispatches(inst, ignoredDirection, event) {
+ if (inst && event && event.dispatchConfig.registrationName) {
+ var registrationName = event.dispatchConfig.registrationName;
+ var listener = getListener(inst, registrationName);
+ if (listener) {
+ event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
+ }
+ }
+}
+
+/**
+ * Accumulates dispatches on an `SyntheticEvent`, but only for the
+ * `dispatchMarker`.
+ * @param {SyntheticEvent} event
+ */
+function accumulateDirectDispatchesSingle(event) {
+ if (event && event.dispatchConfig.registrationName) {
+ accumulateDispatches(event._targetInst, null, event);
+ }
+}
+
+function accumulateTwoPhaseDispatches(events) {
+ forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
+}
+
+
+
+function accumulateEnterLeaveDispatches(leave, enter, from, to) {
+ traverseEnterLeave(from, to, accumulateDispatches, leave, enter);
+}
+
+function accumulateDirectDispatches(events) {
+ forEachAccumulated(events, accumulateDirectDispatchesSingle);
+}
+
+var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
+
+// Do not uses the below two methods directly!
+// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
+// (It is the only module that is allowed to access these methods.)
+
+function unsafeCastStringToDOMTopLevelType(topLevelType) {
+ return topLevelType;
+}
+
+function unsafeCastDOMTopLevelTypeToString(topLevelType) {
+ return topLevelType;
+}
+
+/**
+ * Generate a mapping of standard vendor prefixes using the defined style property and event name.
+ *
+ * @param {string} styleProp
+ * @param {string} eventName
+ * @returns {object}
+ */
+function makePrefixMap(styleProp, eventName) {
+ var prefixes = {};
+
+ prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
+ prefixes['Webkit' + styleProp] = 'webkit' + eventName;
+ prefixes['Moz' + styleProp] = 'moz' + eventName;
+
+ return prefixes;
+}
+
+/**
+ * A list of event names to a configurable list of vendor prefixes.
+ */
+var vendorPrefixes = {
+ animationend: makePrefixMap('Animation', 'AnimationEnd'),
+ animationiteration: makePrefixMap('Animation', 'AnimationIteration'),
+ animationstart: makePrefixMap('Animation', 'AnimationStart'),
+ transitionend: makePrefixMap('Transition', 'TransitionEnd')
+};
+
+/**
+ * Event names that have already been detected and prefixed (if applicable).
+ */
+var prefixedEventNames = {};
+
+/**
+ * Element to check for prefixes on.
+ */
+var style = {};
+
+/**
+ * Bootstrap if a DOM exists.
+ */
+if (canUseDOM) {
+ style = document.createElementNS('http://www.w3.org/1999/xhtml', 'div').style;
+
+ // On some platforms, in particular some releases of Android 4.x,
+ // the un-prefixed "animation" and "transition" properties are defined on the
+ // style object but the events that fire will still be prefixed, so we need
+ // to check if the un-prefixed events are usable, and if not remove them from the map.
+ if (!('AnimationEvent' in window)) {
+ delete vendorPrefixes.animationend.animation;
+ delete vendorPrefixes.animationiteration.animation;
+ delete vendorPrefixes.animationstart.animation;
+ }
+
+ // Same as above
+ if (!('TransitionEvent' in window)) {
+ delete vendorPrefixes.transitionend.transition;
+ }
+}
+
+/**
+ * Attempts to determine the correct vendor prefixed event name.
+ *
+ * @param {string} eventName
+ * @returns {string}
+ */
+function getVendorPrefixedEventName(eventName) {
+ if (prefixedEventNames[eventName]) {
+ return prefixedEventNames[eventName];
+ } else if (!vendorPrefixes[eventName]) {
+ return eventName;
+ }
+
+ var prefixMap = vendorPrefixes[eventName];
+
+ for (var styleProp in prefixMap) {
+ if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
+ return prefixedEventNames[eventName] = prefixMap[styleProp];
+ }
+ }
+
+ return eventName;
+}
+
+/**
+ * To identify top level events in ReactDOM, we use constants defined by this
+ * module. This is the only module that uses the unsafe* methods to express
+ * that the constants actually correspond to the browser event names. This lets
+ * us save some bundle size by avoiding a top level type -> event name map.
+ * The rest of ReactDOM code should import top level types from this file.
+ */
+var TOP_ABORT = unsafeCastStringToDOMTopLevelType('abort');
+var TOP_ANIMATION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationend'));
+var TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationiteration'));
+var TOP_ANIMATION_START = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationstart'));
+var TOP_BLUR = unsafeCastStringToDOMTopLevelType('blur');
+var TOP_CAN_PLAY = unsafeCastStringToDOMTopLevelType('canplay');
+var TOP_CAN_PLAY_THROUGH = unsafeCastStringToDOMTopLevelType('canplaythrough');
+var TOP_CANCEL = unsafeCastStringToDOMTopLevelType('cancel');
+var TOP_CHANGE = unsafeCastStringToDOMTopLevelType('change');
+var TOP_CLICK = unsafeCastStringToDOMTopLevelType('click');
+var TOP_CLOSE = unsafeCastStringToDOMTopLevelType('close');
+var TOP_COMPOSITION_END = unsafeCastStringToDOMTopLevelType('compositionend');
+var TOP_COMPOSITION_START = unsafeCastStringToDOMTopLevelType('compositionstart');
+var TOP_COMPOSITION_UPDATE = unsafeCastStringToDOMTopLevelType('compositionupdate');
+var TOP_CONTEXT_MENU = unsafeCastStringToDOMTopLevelType('contextmenu');
+var TOP_COPY = unsafeCastStringToDOMTopLevelType('copy');
+var TOP_CUT = unsafeCastStringToDOMTopLevelType('cut');
+var TOP_DOUBLE_CLICK = unsafeCastStringToDOMTopLevelType('dblclick');
+var TOP_AUX_CLICK = unsafeCastStringToDOMTopLevelType('auxclick');
+var TOP_DRAG = unsafeCastStringToDOMTopLevelType('drag');
+var TOP_DRAG_END = unsafeCastStringToDOMTopLevelType('dragend');
+var TOP_DRAG_ENTER = unsafeCastStringToDOMTopLevelType('dragenter');
+var TOP_DRAG_EXIT = unsafeCastStringToDOMTopLevelType('dragexit');
+var TOP_DRAG_LEAVE = unsafeCastStringToDOMTopLevelType('dragleave');
+var TOP_DRAG_OVER = unsafeCastStringToDOMTopLevelType('dragover');
+var TOP_DRAG_START = unsafeCastStringToDOMTopLevelType('dragstart');
+var TOP_DROP = unsafeCastStringToDOMTopLevelType('drop');
+var TOP_DURATION_CHANGE = unsafeCastStringToDOMTopLevelType('durationchange');
+var TOP_EMPTIED = unsafeCastStringToDOMTopLevelType('emptied');
+var TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
+var TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
+var TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
+var TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
+var TOP_GOT_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType('gotpointercapture');
+var TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
+var TOP_INVALID = unsafeCastStringToDOMTopLevelType('invalid');
+var TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
+var TOP_KEY_PRESS = unsafeCastStringToDOMTopLevelType('keypress');
+var TOP_KEY_UP = unsafeCastStringToDOMTopLevelType('keyup');
+var TOP_LOAD = unsafeCastStringToDOMTopLevelType('load');
+var TOP_LOAD_START = unsafeCastStringToDOMTopLevelType('loadstart');
+var TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
+var TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType('loadedmetadata');
+var TOP_LOST_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType('lostpointercapture');
+var TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
+var TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
+var TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
+var TOP_MOUSE_OVER = unsafeCastStringToDOMTopLevelType('mouseover');
+var TOP_MOUSE_UP = unsafeCastStringToDOMTopLevelType('mouseup');
+var TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
+var TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
+var TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
+var TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
+var TOP_POINTER_CANCEL = unsafeCastStringToDOMTopLevelType('pointercancel');
+var TOP_POINTER_DOWN = unsafeCastStringToDOMTopLevelType('pointerdown');
+
+
+var TOP_POINTER_MOVE = unsafeCastStringToDOMTopLevelType('pointermove');
+var TOP_POINTER_OUT = unsafeCastStringToDOMTopLevelType('pointerout');
+var TOP_POINTER_OVER = unsafeCastStringToDOMTopLevelType('pointerover');
+var TOP_POINTER_UP = unsafeCastStringToDOMTopLevelType('pointerup');
+var TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
+var TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
+var TOP_RESET = unsafeCastStringToDOMTopLevelType('reset');
+var TOP_SCROLL = unsafeCastStringToDOMTopLevelType('scroll');
+var TOP_SEEKED = unsafeCastStringToDOMTopLevelType('seeked');
+var TOP_SEEKING = unsafeCastStringToDOMTopLevelType('seeking');
+var TOP_SELECTION_CHANGE = unsafeCastStringToDOMTopLevelType('selectionchange');
+var TOP_STALLED = unsafeCastStringToDOMTopLevelType('stalled');
+var TOP_SUBMIT = unsafeCastStringToDOMTopLevelType('submit');
+var TOP_SUSPEND = unsafeCastStringToDOMTopLevelType('suspend');
+var TOP_TEXT_INPUT = unsafeCastStringToDOMTopLevelType('textInput');
+var TOP_TIME_UPDATE = unsafeCastStringToDOMTopLevelType('timeupdate');
+var TOP_TOGGLE = unsafeCastStringToDOMTopLevelType('toggle');
+var TOP_TOUCH_CANCEL = unsafeCastStringToDOMTopLevelType('touchcancel');
+var TOP_TOUCH_END = unsafeCastStringToDOMTopLevelType('touchend');
+var TOP_TOUCH_MOVE = unsafeCastStringToDOMTopLevelType('touchmove');
+var TOP_TOUCH_START = unsafeCastStringToDOMTopLevelType('touchstart');
+var TOP_TRANSITION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('transitionend'));
+var TOP_VOLUME_CHANGE = unsafeCastStringToDOMTopLevelType('volumechange');
+var TOP_WAITING = unsafeCastStringToDOMTopLevelType('waiting');
+var TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');
+
+// List of events that need to be individually attached to media elements.
+// Note that events in this list will *not* be listened to at the top level
+// unless they're explicitly whitelisted in `ReactBrowserEventEmitter.listenTo`.
+var mediaEventTypes = [TOP_ABORT, TOP_CAN_PLAY, TOP_CAN_PLAY_THROUGH, TOP_DURATION_CHANGE, TOP_EMPTIED, TOP_ENCRYPTED, TOP_ENDED, TOP_ERROR, TOP_LOADED_DATA, TOP_LOADED_METADATA, TOP_LOAD_START, TOP_PAUSE, TOP_PLAY, TOP_PLAYING, TOP_PROGRESS, TOP_RATE_CHANGE, TOP_SEEKED, TOP_SEEKING, TOP_STALLED, TOP_SUSPEND, TOP_TIME_UPDATE, TOP_VOLUME_CHANGE, TOP_WAITING];
+
+function getRawEventName(topLevelType) {
+ return unsafeCastDOMTopLevelTypeToString(topLevelType);
+}
+
+/**
+ * These variables store information about text content of a target node,
+ * allowing comparison of content before and after a given event.
+ *
+ * Identify the node where selection currently begins, then observe
+ * both its text content and its current position in the DOM. Since the
+ * browser may natively replace the target node during composition, we can
+ * use its position to find its replacement.
+ *
+ *
+ */
+
+var root = null;
+var startText = null;
+var fallbackText = null;
+
+function initialize(nativeEventTarget) {
+ root = nativeEventTarget;
+ startText = getText();
+ return true;
+}
+
+function reset() {
+ root = null;
+ startText = null;
+ fallbackText = null;
+}
+
+function getData() {
+ if (fallbackText) {
+ return fallbackText;
+ }
+
+ var start = void 0;
+ var startValue = startText;
+ var startLength = startValue.length;
+ var end = void 0;
+ var endValue = getText();
+ var endLength = endValue.length;
+
+ for (start = 0; start < startLength; start++) {
+ if (startValue[start] !== endValue[start]) {
+ break;
+ }
+ }
+
+ var minEnd = startLength - start;
+ for (end = 1; end <= minEnd; end++) {
+ if (startValue[startLength - end] !== endValue[endLength - end]) {
+ break;
+ }
+ }
+
+ var sliceTail = end > 1 ? 1 - end : undefined;
+ fallbackText = endValue.slice(start, sliceTail);
+ return fallbackText;
+}
+
+function getText() {
+ if ('value' in root) {
+ return root.value;
+ }
+ return root.textContent;
+}
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+/* eslint valid-typeof: 0 */
+
+var EVENT_POOL_SIZE = 10;
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var EventInterface = {
+ type: null,
+ target: null,
+ // currentTarget is set when dispatching; no use in copying it here
+ currentTarget: function () {
+ return null;
+ },
+ eventPhase: null,
+ bubbles: null,
+ cancelable: null,
+ timeStamp: function (event) {
+ return event.timeStamp || Date.now();
+ },
+ defaultPrevented: null,
+ isTrusted: null
+};
+
+function functionThatReturnsTrue() {
+ return true;
+}
+
+function functionThatReturnsFalse() {
+ return false;
+}
+
+/**
+ * Synthetic events are dispatched by event plugins, typically in response to a
+ * top-level event delegation handler.
+ *
+ * These systems should generally use pooling to reduce the frequency of garbage
+ * collection. The system should check `isPersistent` to determine whether the
+ * event should be released into the pool after being dispatched. Users that
+ * need a persisted event should invoke `persist`.
+ *
+ * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
+ * normalizing browser quirks. Subclasses do not necessarily have to implement a
+ * DOM interface; custom application-specific events can also subclass this.
+ *
+ * @param {object} dispatchConfig Configuration used to dispatch this event.
+ * @param {*} targetInst Marker identifying the event target.
+ * @param {object} nativeEvent Native browser event.
+ * @param {DOMEventTarget} nativeEventTarget Target node.
+ */
+function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
+ {
+ // these have a getter/setter for warnings
+ delete this.nativeEvent;
+ delete this.preventDefault;
+ delete this.stopPropagation;
+ delete this.isDefaultPrevented;
+ delete this.isPropagationStopped;
+ }
+
+ this.dispatchConfig = dispatchConfig;
+ this._targetInst = targetInst;
+ this.nativeEvent = nativeEvent;
+
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ if (!Interface.hasOwnProperty(propName)) {
+ continue;
+ }
+ {
+ delete this[propName]; // this has a getter/setter for warnings
+ }
+ var normalize = Interface[propName];
+ if (normalize) {
+ this[propName] = normalize(nativeEvent);
+ } else {
+ if (propName === 'target') {
+ this.target = nativeEventTarget;
+ } else {
+ this[propName] = nativeEvent[propName];
+ }
+ }
+ }
+
+ var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
+ if (defaultPrevented) {
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ } else {
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ }
+ this.isPropagationStopped = functionThatReturnsFalse;
+ return this;
+}
+
+_assign(SyntheticEvent.prototype, {
+ preventDefault: function () {
+ this.defaultPrevented = true;
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else if (typeof event.returnValue !== 'unknown') {
+ event.returnValue = false;
+ }
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ },
+
+ stopPropagation: function () {
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.stopPropagation) {
+ event.stopPropagation();
+ } else if (typeof event.cancelBubble !== 'unknown') {
+ // The ChangeEventPlugin registers a "propertychange" event for
+ // IE. This event does not support bubbling or cancelling, and
+ // any references to cancelBubble throw "Member not found". A
+ // typeof check of "unknown" circumvents this issue (and is also
+ // IE specific).
+ event.cancelBubble = true;
+ }
+
+ this.isPropagationStopped = functionThatReturnsTrue;
+ },
+
+ /**
+ * We release all dispatched `SyntheticEvent`s after each event loop, adding
+ * them back into the pool. This allows a way to hold onto a reference that
+ * won't be added back into the pool.
+ */
+ persist: function () {
+ this.isPersistent = functionThatReturnsTrue;
+ },
+
+ /**
+ * Checks if this event should be released back into the pool.
+ *
+ * @return {boolean} True if this should not be released, false otherwise.
+ */
+ isPersistent: functionThatReturnsFalse,
+
+ /**
+ * `PooledClass` looks for `destructor` on each instance it releases.
+ */
+ destructor: function () {
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ {
+ Object.defineProperty(this, propName, getPooledWarningPropertyDefinition(propName, Interface[propName]));
+ }
+ }
+ this.dispatchConfig = null;
+ this._targetInst = null;
+ this.nativeEvent = null;
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ this.isPropagationStopped = functionThatReturnsFalse;
+ this._dispatchListeners = null;
+ this._dispatchInstances = null;
+ {
+ Object.defineProperty(this, 'nativeEvent', getPooledWarningPropertyDefinition('nativeEvent', null));
+ Object.defineProperty(this, 'isDefaultPrevented', getPooledWarningPropertyDefinition('isDefaultPrevented', functionThatReturnsFalse));
+ Object.defineProperty(this, 'isPropagationStopped', getPooledWarningPropertyDefinition('isPropagationStopped', functionThatReturnsFalse));
+ Object.defineProperty(this, 'preventDefault', getPooledWarningPropertyDefinition('preventDefault', function () {}));
+ Object.defineProperty(this, 'stopPropagation', getPooledWarningPropertyDefinition('stopPropagation', function () {}));
+ }
+ }
+});
+
+SyntheticEvent.Interface = EventInterface;
+
+/**
+ * Helper to reduce boilerplate when creating subclasses.
+ */
+SyntheticEvent.extend = function (Interface) {
+ var Super = this;
+
+ var E = function () {};
+ E.prototype = Super.prototype;
+ var prototype = new E();
+
+ function Class() {
+ return Super.apply(this, arguments);
+ }
+ _assign(prototype, Class.prototype);
+ Class.prototype = prototype;
+ Class.prototype.constructor = Class;
+
+ Class.Interface = _assign({}, Super.Interface, Interface);
+ Class.extend = Super.extend;
+ addEventPoolingTo(Class);
+
+ return Class;
+};
+
+addEventPoolingTo(SyntheticEvent);
+
+/**
+ * Helper to nullify syntheticEvent instance properties when destructing
+ *
+ * @param {String} propName
+ * @param {?object} getVal
+ * @return {object} defineProperty object
+ */
+function getPooledWarningPropertyDefinition(propName, getVal) {
+ var isFunction = typeof getVal === 'function';
+ return {
+ configurable: true,
+ set: set,
+ get: get
+ };
+
+ function set(val) {
+ var action = isFunction ? 'setting the method' : 'setting the property';
+ warn(action, 'This is effectively a no-op');
+ return val;
+ }
+
+ function get() {
+ var action = isFunction ? 'accessing the method' : 'accessing the property';
+ var result = isFunction ? 'This is a no-op function' : 'This is set to null';
+ warn(action, result);
+ return getVal;
+ }
+
+ function warn(action, result) {
+ var warningCondition = false;
+ !warningCondition ? warningWithoutStack$1(false, "This synthetic event is reused for performance reasons. If you're seeing this, " + "you're %s `%s` on a released/nullified synthetic event. %s. " + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.', action, propName, result) : void 0;
+ }
+}
+
+function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
+ var EventConstructor = this;
+ if (EventConstructor.eventPool.length) {
+ var instance = EventConstructor.eventPool.pop();
+ EventConstructor.call(instance, dispatchConfig, targetInst, nativeEvent, nativeInst);
+ return instance;
+ }
+ return new EventConstructor(dispatchConfig, targetInst, nativeEvent, nativeInst);
+}
+
+function releasePooledEvent(event) {
+ var EventConstructor = this;
+ !(event instanceof EventConstructor) ? invariant(false, 'Trying to release an event instance into a pool of a different type.') : void 0;
+ event.destructor();
+ if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
+ EventConstructor.eventPool.push(event);
+ }
+}
+
+function addEventPoolingTo(EventConstructor) {
+ EventConstructor.eventPool = [];
+ EventConstructor.getPooled = getPooledEvent;
+ EventConstructor.release = releasePooledEvent;
+}
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
+ */
+var SyntheticCompositionEvent = SyntheticEvent.extend({
+ data: null
+});
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
+ * /#events-inputevents
+ */
+var SyntheticInputEvent = SyntheticEvent.extend({
+ data: null
+});
+
+var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
+var START_KEYCODE = 229;
+
+var canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
+
+var documentMode = null;
+if (canUseDOM && 'documentMode' in document) {
+ documentMode = document.documentMode;
+}
+
+// Webkit offers a very useful `textInput` event that can be used to
+// directly represent `beforeInput`. The IE `textinput` event is not as
+// useful, so we don't use it.
+var canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode;
+
+// In IE9+, we have access to composition events, but the data supplied
+// by the native compositionend event may be incorrect. Japanese ideographic
+// spaces, for instance (\u3000) are not recorded correctly.
+var useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11);
+
+var SPACEBAR_CODE = 32;
+var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
+
+// Events and their corresponding property names.
+var eventTypes = {
+ beforeInput: {
+ phasedRegistrationNames: {
+ bubbled: 'onBeforeInput',
+ captured: 'onBeforeInputCapture'
+ },
+ dependencies: [TOP_COMPOSITION_END, TOP_KEY_PRESS, TOP_TEXT_INPUT, TOP_PASTE]
+ },
+ compositionEnd: {
+ phasedRegistrationNames: {
+ bubbled: 'onCompositionEnd',
+ captured: 'onCompositionEndCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_COMPOSITION_END, TOP_KEY_DOWN, TOP_KEY_PRESS, TOP_KEY_UP, TOP_MOUSE_DOWN]
+ },
+ compositionStart: {
+ phasedRegistrationNames: {
+ bubbled: 'onCompositionStart',
+ captured: 'onCompositionStartCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_COMPOSITION_START, TOP_KEY_DOWN, TOP_KEY_PRESS, TOP_KEY_UP, TOP_MOUSE_DOWN]
+ },
+ compositionUpdate: {
+ phasedRegistrationNames: {
+ bubbled: 'onCompositionUpdate',
+ captured: 'onCompositionUpdateCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_COMPOSITION_UPDATE, TOP_KEY_DOWN, TOP_KEY_PRESS, TOP_KEY_UP, TOP_MOUSE_DOWN]
+ }
+};
+
+// Track whether we've ever handled a keypress on the space key.
+var hasSpaceKeypress = false;
+
+/**
+ * Return whether a native keypress event is assumed to be a command.
+ * This is required because Firefox fires `keypress` events for key commands
+ * (cut, copy, select-all, etc.) even though no character is inserted.
+ */
+function isKeypressCommand(nativeEvent) {
+ return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
+ // ctrlKey && altKey is equivalent to AltGr, and is not a command.
+ !(nativeEvent.ctrlKey && nativeEvent.altKey);
+}
+
+/**
+ * Translate native top level events into event types.
+ *
+ * @param {string} topLevelType
+ * @return {object}
+ */
+function getCompositionEventType(topLevelType) {
+ switch (topLevelType) {
+ case TOP_COMPOSITION_START:
+ return eventTypes.compositionStart;
+ case TOP_COMPOSITION_END:
+ return eventTypes.compositionEnd;
+ case TOP_COMPOSITION_UPDATE:
+ return eventTypes.compositionUpdate;
+ }
+}
+
+/**
+ * Does our fallback best-guess model think this event signifies that
+ * composition has begun?
+ *
+ * @param {string} topLevelType
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isFallbackCompositionStart(topLevelType, nativeEvent) {
+ return topLevelType === TOP_KEY_DOWN && nativeEvent.keyCode === START_KEYCODE;
+}
+
+/**
+ * Does our fallback mode think that this event is the end of composition?
+ *
+ * @param {string} topLevelType
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isFallbackCompositionEnd(topLevelType, nativeEvent) {
+ switch (topLevelType) {
+ case TOP_KEY_UP:
+ // Command keys insert or clear IME input.
+ return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
+ case TOP_KEY_DOWN:
+ // Expect IME keyCode on each keydown. If we get any other
+ // code we must have exited earlier.
+ return nativeEvent.keyCode !== START_KEYCODE;
+ case TOP_KEY_PRESS:
+ case TOP_MOUSE_DOWN:
+ case TOP_BLUR:
+ // Events are not possible without cancelling IME.
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Google Input Tools provides composition data via a CustomEvent,
+ * with the `data` property populated in the `detail` object. If this
+ * is available on the event object, use it. If not, this is a plain
+ * composition event and we have nothing special to extract.
+ *
+ * @param {object} nativeEvent
+ * @return {?string}
+ */
+function getDataFromCustomEvent(nativeEvent) {
+ var detail = nativeEvent.detail;
+ if (typeof detail === 'object' && 'data' in detail) {
+ return detail.data;
+ }
+ return null;
+}
+
+/**
+ * Check if a composition event was triggered by Korean IME.
+ * Our fallback mode does not work well with IE's Korean IME,
+ * so just use native composition events when Korean IME is used.
+ * Although CompositionEvent.locale property is deprecated,
+ * it is available in IE, where our fallback mode is enabled.
+ *
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isUsingKoreanIME(nativeEvent) {
+ return nativeEvent.locale === 'ko';
+}
+
+// Track the current IME composition status, if any.
+var isComposing = false;
+
+/**
+ * @return {?object} A SyntheticCompositionEvent.
+ */
+function extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var eventType = void 0;
+ var fallbackData = void 0;
+
+ if (canUseCompositionEvent) {
+ eventType = getCompositionEventType(topLevelType);
+ } else if (!isComposing) {
+ if (isFallbackCompositionStart(topLevelType, nativeEvent)) {
+ eventType = eventTypes.compositionStart;
+ }
+ } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) {
+ eventType = eventTypes.compositionEnd;
+ }
+
+ if (!eventType) {
+ return null;
+ }
+
+ if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
+ // The current composition is stored statically and must not be
+ // overwritten while composition continues.
+ if (!isComposing && eventType === eventTypes.compositionStart) {
+ isComposing = initialize(nativeEventTarget);
+ } else if (eventType === eventTypes.compositionEnd) {
+ if (isComposing) {
+ fallbackData = getData();
+ }
+ }
+ }
+
+ var event = SyntheticCompositionEvent.getPooled(eventType, targetInst, nativeEvent, nativeEventTarget);
+
+ if (fallbackData) {
+ // Inject data generated from fallback path into the synthetic event.
+ // This matches the property of native CompositionEventInterface.
+ event.data = fallbackData;
+ } else {
+ var customData = getDataFromCustomEvent(nativeEvent);
+ if (customData !== null) {
+ event.data = customData;
+ }
+ }
+
+ accumulateTwoPhaseDispatches(event);
+ return event;
+}
+
+/**
+ * @param {TopLevelType} topLevelType Number from `TopLevelType`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?string} The string corresponding to this `beforeInput` event.
+ */
+function getNativeBeforeInputChars(topLevelType, nativeEvent) {
+ switch (topLevelType) {
+ case TOP_COMPOSITION_END:
+ return getDataFromCustomEvent(nativeEvent);
+ case TOP_KEY_PRESS:
+ /**
+ * If native `textInput` events are available, our goal is to make
+ * use of them. However, there is a special case: the spacebar key.
+ * In Webkit, preventing default on a spacebar `textInput` event
+ * cancels character insertion, but it *also* causes the browser
+ * to fall back to its default spacebar behavior of scrolling the
+ * page.
+ *
+ * Tracking at:
+ * https://code.google.com/p/chromium/issues/detail?id=355103
+ *
+ * To avoid this issue, use the keypress event as if no `textInput`
+ * event is available.
+ */
+ var which = nativeEvent.which;
+ if (which !== SPACEBAR_CODE) {
+ return null;
+ }
+
+ hasSpaceKeypress = true;
+ return SPACEBAR_CHAR;
+
+ case TOP_TEXT_INPUT:
+ // Record the characters to be added to the DOM.
+ var chars = nativeEvent.data;
+
+ // If it's a spacebar character, assume that we have already handled
+ // it at the keypress level and bail immediately. Android Chrome
+ // doesn't give us keycodes, so we need to ignore it.
+ if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
+ return null;
+ }
+
+ return chars;
+
+ default:
+ // For other native event types, do nothing.
+ return null;
+ }
+}
+
+/**
+ * For browsers that do not provide the `textInput` event, extract the
+ * appropriate string to use for SyntheticInputEvent.
+ *
+ * @param {number} topLevelType Number from `TopLevelEventTypes`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?string} The fallback string for this `beforeInput` event.
+ */
+function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
+ // If we are currently composing (IME) and using a fallback to do so,
+ // try to extract the composed characters from the fallback object.
+ // If composition event is available, we extract a string only at
+ // compositionevent, otherwise extract it at fallback events.
+ if (isComposing) {
+ if (topLevelType === TOP_COMPOSITION_END || !canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent)) {
+ var chars = getData();
+ reset();
+ isComposing = false;
+ return chars;
+ }
+ return null;
+ }
+
+ switch (topLevelType) {
+ case TOP_PASTE:
+ // If a paste event occurs after a keypress, throw out the input
+ // chars. Paste events should not lead to BeforeInput events.
+ return null;
+ case TOP_KEY_PRESS:
+ /**
+ * As of v27, Firefox may fire keypress events even when no character
+ * will be inserted. A few possibilities:
+ *
+ * - `which` is `0`. Arrow keys, Esc key, etc.
+ *
+ * - `which` is the pressed key code, but no char is available.
+ * Ex: 'AltGr + d` in Polish. There is no modified character for
+ * this key combination and no character is inserted into the
+ * document, but FF fires the keypress for char code `100` anyway.
+ * No `input` event will occur.
+ *
+ * - `which` is the pressed key code, but a command combination is
+ * being used. Ex: `Cmd+C`. No character is inserted, and no
+ * `input` event will occur.
+ */
+ if (!isKeypressCommand(nativeEvent)) {
+ // IE fires the `keypress` event when a user types an emoji via
+ // Touch keyboard of Windows. In such a case, the `char` property
+ // holds an emoji character like `\uD83D\uDE0A`. Because its length
+ // is 2, the property `which` does not represent an emoji correctly.
+ // In such a case, we directly return the `char` property instead of
+ // using `which`.
+ if (nativeEvent.char && nativeEvent.char.length > 1) {
+ return nativeEvent.char;
+ } else if (nativeEvent.which) {
+ return String.fromCharCode(nativeEvent.which);
+ }
+ }
+ return null;
+ case TOP_COMPOSITION_END:
+ return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data;
+ default:
+ return null;
+ }
+}
+
+/**
+ * Extract a SyntheticInputEvent for `beforeInput`, based on either native
+ * `textInput` or fallback behavior.
+ *
+ * @return {?object} A SyntheticInputEvent.
+ */
+function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var chars = void 0;
+
+ if (canUseTextInputEvent) {
+ chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
+ } else {
+ chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
+ }
+
+ // If no characters are being inserted, no BeforeInput event should
+ // be fired.
+ if (!chars) {
+ return null;
+ }
+
+ var event = SyntheticInputEvent.getPooled(eventTypes.beforeInput, targetInst, nativeEvent, nativeEventTarget);
+
+ event.data = chars;
+ accumulateTwoPhaseDispatches(event);
+ return event;
+}
+
+/**
+ * Create an `onBeforeInput` event to match
+ * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
+ *
+ * This event plugin is based on the native `textInput` event
+ * available in Chrome, Safari, Opera, and IE. This event fires after
+ * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
+ *
+ * `beforeInput` is spec'd but not implemented in any browsers, and
+ * the `input` event does not provide any useful information about what has
+ * actually been added, contrary to the spec. Thus, `textInput` is the best
+ * available event to identify the characters that have actually been inserted
+ * into the target node.
+ *
+ * This plugin is also responsible for emitting `composition` events, thus
+ * allowing us to share composition fallback code for both `beforeInput` and
+ * `composition` event types.
+ */
+var BeforeInputEventPlugin = {
+ eventTypes: eventTypes,
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var composition = extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+
+ var beforeInput = extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+
+ if (composition === null) {
+ return beforeInput;
+ }
+
+ if (beforeInput === null) {
+ return composition;
+ }
+
+ return [composition, beforeInput];
+ }
+};
+
+// Use to restore controlled state after a change event has fired.
+
+var restoreImpl = null;
+var restoreTarget = null;
+var restoreQueue = null;
+
+function restoreStateOfTarget(target) {
+ // We perform this translation at the end of the event loop so that we
+ // always receive the correct fiber here
+ var internalInstance = getInstanceFromNode(target);
+ if (!internalInstance) {
+ // Unmounted
+ return;
+ }
+ !(typeof restoreImpl === 'function') ? invariant(false, 'setRestoreImplementation() needs to be called to handle a target for controlled events. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ var props = getFiberCurrentPropsFromNode(internalInstance.stateNode);
+ restoreImpl(internalInstance.stateNode, internalInstance.type, props);
+}
+
+function setRestoreImplementation(impl) {
+ restoreImpl = impl;
+}
+
+function enqueueStateRestore(target) {
+ if (restoreTarget) {
+ if (restoreQueue) {
+ restoreQueue.push(target);
+ } else {
+ restoreQueue = [target];
+ }
+ } else {
+ restoreTarget = target;
+ }
+}
+
+function needsStateRestore() {
+ return restoreTarget !== null || restoreQueue !== null;
+}
+
+function restoreStateIfNeeded() {
+ if (!restoreTarget) {
+ return;
+ }
+ var target = restoreTarget;
+ var queuedTargets = restoreQueue;
+ restoreTarget = null;
+ restoreQueue = null;
+
+ restoreStateOfTarget(target);
+ if (queuedTargets) {
+ for (var i = 0; i < queuedTargets.length; i++) {
+ restoreStateOfTarget(queuedTargets[i]);
+ }
+ }
+}
+
+// Used as a way to call batchedUpdates when we don't have a reference to
+// the renderer. Such as when we're dispatching events or if third party
+// libraries need to call batchedUpdates. Eventually, this API will go away when
+// everything is batched by default. We'll then have a similar API to opt-out of
+// scheduled work and instead do synchronous work.
+
+// Defaults
+var _batchedUpdatesImpl = function (fn, bookkeeping) {
+ return fn(bookkeeping);
+};
+var _interactiveUpdatesImpl = function (fn, a, b) {
+ return fn(a, b);
+};
+var _flushInteractiveUpdatesImpl = function () {};
+
+var isBatching = false;
+function batchedUpdates(fn, bookkeeping) {
+ if (isBatching) {
+ // If we are currently inside another batch, we need to wait until it
+ // fully completes before restoring state.
+ return fn(bookkeeping);
+ }
+ isBatching = true;
+ try {
+ return _batchedUpdatesImpl(fn, bookkeeping);
+ } finally {
+ // Here we wait until all updates have propagated, which is important
+ // when using controlled components within layers:
+ // https://github.com/facebook/react/issues/1698
+ // Then we restore state of any controlled component.
+ isBatching = false;
+ var controlledComponentsHavePendingUpdates = needsStateRestore();
+ if (controlledComponentsHavePendingUpdates) {
+ // If a controlled event was fired, we may need to restore the state of
+ // the DOM node back to the controlled value. This is necessary when React
+ // bails out of the update without touching the DOM.
+ _flushInteractiveUpdatesImpl();
+ restoreStateIfNeeded();
+ }
+ }
+}
+
+function interactiveUpdates(fn, a, b) {
+ return _interactiveUpdatesImpl(fn, a, b);
+}
+
+
+
+function setBatchingImplementation(batchedUpdatesImpl, interactiveUpdatesImpl, flushInteractiveUpdatesImpl) {
+ _batchedUpdatesImpl = batchedUpdatesImpl;
+ _interactiveUpdatesImpl = interactiveUpdatesImpl;
+ _flushInteractiveUpdatesImpl = flushInteractiveUpdatesImpl;
+}
+
+/**
+ * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
+ */
+var supportedInputTypes = {
+ color: true,
+ date: true,
+ datetime: true,
+ 'datetime-local': true,
+ email: true,
+ month: true,
+ number: true,
+ password: true,
+ range: true,
+ search: true,
+ tel: true,
+ text: true,
+ time: true,
+ url: true,
+ week: true
+};
+
+function isTextInputElement(elem) {
+ var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
+
+ if (nodeName === 'input') {
+ return !!supportedInputTypes[elem.type];
+ }
+
+ if (nodeName === 'textarea') {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * HTML nodeType values that represent the type of the node
+ */
+
+var ELEMENT_NODE = 1;
+var TEXT_NODE = 3;
+var COMMENT_NODE = 8;
+var DOCUMENT_NODE = 9;
+var DOCUMENT_FRAGMENT_NODE = 11;
+
+/**
+ * Gets the target node from a native browser event by accounting for
+ * inconsistencies in browser DOM APIs.
+ *
+ * @param {object} nativeEvent Native browser event.
+ * @return {DOMEventTarget} Target node.
+ */
+function getEventTarget(nativeEvent) {
+ // Fallback to nativeEvent.srcElement for IE9
+ // https://github.com/facebook/react/issues/12506
+ var target = nativeEvent.target || nativeEvent.srcElement || window;
+
+ // Normalize SVG <use> element events #4963
+ if (target.correspondingUseElement) {
+ target = target.correspondingUseElement;
+ }
+
+ // Safari may fire events on text nodes (Node.TEXT_NODE is 3).
+ // @see http://www.quirksmode.org/js/events_properties.html
+ return target.nodeType === TEXT_NODE ? target.parentNode : target;
+}
+
+/**
+ * Checks if an event is supported in the current execution environment.
+ *
+ * NOTE: This will not work correctly for non-generic events such as `change`,
+ * `reset`, `load`, `error`, and `select`.
+ *
+ * Borrows from Modernizr.
+ *
+ * @param {string} eventNameSuffix Event name, e.g. "click".
+ * @return {boolean} True if the event is supported.
+ * @internal
+ * @license Modernizr 3.0.0pre (Custom Build) | MIT
+ */
+function isEventSupported(eventNameSuffix) {
+ if (!canUseDOM) {
+ return false;
+ }
+
+ var eventName = 'on' + eventNameSuffix;
+ var isSupported = eventName in document;
+
+ if (!isSupported) {
+ var element = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ element.setAttribute(eventName, 'return;');
+ isSupported = typeof element[eventName] === 'function';
+ }
+
+ return isSupported;
+}
+
+function isCheckable(elem) {
+ var type = elem.type;
+ var nodeName = elem.nodeName;
+ return nodeName && nodeName.toLowerCase() === 'input' && (type === 'checkbox' || type === 'radio');
+}
+
+function getTracker(node) {
+ return node._valueTracker;
+}
+
+function detachTracker(node) {
+ node._valueTracker = null;
+}
+
+function getValueFromNode(node) {
+ var value = '';
+ if (!node) {
+ return value;
+ }
+
+ if (isCheckable(node)) {
+ value = node.checked ? 'true' : 'false';
+ } else {
+ value = node.value;
+ }
+
+ return value;
+}
+
+function trackValueOnNode(node) {
+ var valueField = isCheckable(node) ? 'checked' : 'value';
+ var descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField);
+
+ var currentValue = '' + node[valueField];
+
+ // if someone has already defined a value or Safari, then bail
+ // and don't track value will cause over reporting of changes,
+ // but it's better then a hard failure
+ // (needed for certain tests that spyOn input values and Safari)
+ if (node.hasOwnProperty(valueField) || typeof descriptor === 'undefined' || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') {
+ return;
+ }
+ var get = descriptor.get,
+ set = descriptor.set;
+
+ Object.defineProperty(node, valueField, {
+ configurable: true,
+ get: function () {
+ return get.call(this);
+ },
+ set: function (value) {
+ currentValue = '' + value;
+ set.call(this, value);
+ }
+ });
+ // We could've passed this the first time
+ // but it triggers a bug in IE11 and Edge 14/15.
+ // Calling defineProperty() again should be equivalent.
+ // https://github.com/facebook/react/issues/11768
+ Object.defineProperty(node, valueField, {
+ enumerable: descriptor.enumerable
+ });
+
+ var tracker = {
+ getValue: function () {
+ return currentValue;
+ },
+ setValue: function (value) {
+ currentValue = '' + value;
+ },
+ stopTracking: function () {
+ detachTracker(node);
+ delete node[valueField];
+ }
+ };
+ return tracker;
+}
+
+function track(node) {
+ if (getTracker(node)) {
+ return;
+ }
+
+ // TODO: Once it's just Fiber we can move this to node._wrapperState
+ node._valueTracker = trackValueOnNode(node);
+}
+
+function updateValueIfChanged(node) {
+ if (!node) {
+ return false;
+ }
+
+ var tracker = getTracker(node);
+ // if there is no tracker at this point it's unlikely
+ // that trying again will succeed
+ if (!tracker) {
+ return true;
+ }
+
+ var lastValue = tracker.getValue();
+ var nextValue = getValueFromNode(node);
+ if (nextValue !== lastValue) {
+ tracker.setValue(nextValue);
+ return true;
+ }
+ return false;
+}
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+var BEFORE_SLASH_RE = /^(.*)[\\\/]/;
+
+var describeComponentFrame = function (name, source, ownerName) {
+ var sourceInfo = '';
+ if (source) {
+ var path = source.fileName;
+ var fileName = path.replace(BEFORE_SLASH_RE, '');
+ {
+ // In DEV, include code for a common special case:
+ // prefer "folder/index.js" instead of just "index.js".
+ if (/^index\./.test(fileName)) {
+ var match = path.match(BEFORE_SLASH_RE);
+ if (match) {
+ var pathBeforeSlash = match[1];
+ if (pathBeforeSlash) {
+ var folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
+ fileName = folderName + '/' + fileName;
+ }
+ }
+ }
+ }
+ sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
+ } else if (ownerName) {
+ sourceInfo = ' (created by ' + ownerName + ')';
+ }
+ return '\n in ' + (name || 'Unknown') + sourceInfo;
+};
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+var FAUX_ITERATOR_SYMBOL = '@@iterator';
+
+function getIteratorFn(maybeIterable) {
+ if (maybeIterable === null || typeof maybeIterable !== 'object') {
+ return null;
+ }
+ var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
+ if (typeof maybeIterator === 'function') {
+ return maybeIterator;
+ }
+ return null;
+}
+
+var Pending = 0;
+var Resolved = 1;
+var Rejected = 2;
+
+function refineResolvedLazyComponent(lazyComponent) {
+ return lazyComponent._status === Resolved ? lazyComponent._result : null;
+}
+
+function getWrappedName(outerType, innerType, wrapperName) {
+ var functionName = innerType.displayName || innerType.name || '';
+ return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
+}
+
+function getComponentName(type) {
+ if (type == null) {
+ // Host root, text node or just invalid type.
+ return null;
+ }
+ {
+ if (typeof type.tag === 'number') {
+ warningWithoutStack$1(false, 'Received an unexpected object in getComponentName(). ' + 'This is likely a bug in React. Please file an issue.');
+ }
+ }
+ if (typeof type === 'function') {
+ return type.displayName || type.name || null;
+ }
+ if (typeof type === 'string') {
+ return type;
+ }
+ switch (type) {
+ case REACT_CONCURRENT_MODE_TYPE:
+ return 'ConcurrentMode';
+ case REACT_FRAGMENT_TYPE:
+ return 'Fragment';
+ case REACT_PORTAL_TYPE:
+ return 'Portal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_SUSPENSE_TYPE:
+ return 'Suspense';
+ }
+ if (typeof type === 'object') {
+ switch (type.$$typeof) {
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_FORWARD_REF_TYPE:
+ return getWrappedName(type, type.render, 'ForwardRef');
+ case REACT_MEMO_TYPE:
+ return getComponentName(type.type);
+ case REACT_LAZY_TYPE:
+ {
+ var thenable = type;
+ var resolvedThenable = refineResolvedLazyComponent(thenable);
+ if (resolvedThenable) {
+ return getComponentName(resolvedThenable);
+ }
+ }
+ }
+ }
+ return null;
+}
+
+var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+
+function describeFiber(fiber) {
+ switch (fiber.tag) {
+ case HostRoot:
+ case HostPortal:
+ case HostText:
+ case Fragment:
+ case ContextProvider:
+ case ContextConsumer:
+ return '';
+ default:
+ var owner = fiber._debugOwner;
+ var source = fiber._debugSource;
+ var name = getComponentName(fiber.type);
+ var ownerName = null;
+ if (owner) {
+ ownerName = getComponentName(owner.type);
+ }
+ return describeComponentFrame(name, source, ownerName);
+ }
+}
+
+function getStackByFiberInDevAndProd(workInProgress) {
+ var info = '';
+ var node = workInProgress;
+ do {
+ info += describeFiber(node);
+ node = node.return;
+ } while (node);
+ return info;
+}
+
+var current = null;
+var phase = null;
+
+function getCurrentFiberOwnerNameInDevOrNull() {
+ {
+ if (current === null) {
+ return null;
+ }
+ var owner = current._debugOwner;
+ if (owner !== null && typeof owner !== 'undefined') {
+ return getComponentName(owner.type);
+ }
+ }
+ return null;
+}
+
+function getCurrentFiberStackInDev() {
+ {
+ if (current === null) {
+ return '';
+ }
+ // Safe because if current fiber exists, we are reconciling,
+ // and it is guaranteed to be the work-in-progress version.
+ return getStackByFiberInDevAndProd(current);
+ }
+ return '';
+}
+
+function resetCurrentFiber() {
+ {
+ ReactDebugCurrentFrame.getCurrentStack = null;
+ current = null;
+ phase = null;
+ }
+}
+
+function setCurrentFiber(fiber) {
+ {
+ ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackInDev;
+ current = fiber;
+ phase = null;
+ }
+}
+
+function setCurrentPhase(lifeCyclePhase) {
+ {
+ phase = lifeCyclePhase;
+ }
+}
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warning = warningWithoutStack$1;
+
+{
+ warning = function (condition, format) {
+ if (condition) {
+ return;
+ }
+ var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+ var stack = ReactDebugCurrentFrame.getStackAddendum();
+ // eslint-disable-next-line react-internal/warning-and-invariant-args
+
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ args[_key - 2] = arguments[_key];
+ }
+
+ warningWithoutStack$1.apply(undefined, [false, format + '%s'].concat(args, [stack]));
+ };
+}
+
+var warning$1 = warning;
+
+// A reserved attribute.
+// It is handled by React separately and shouldn't be written to the DOM.
+var RESERVED = 0;
+
+// A simple string attribute.
+// Attributes that aren't in the whitelist are presumed to have this type.
+var STRING = 1;
+
+// A string attribute that accepts booleans in React. In HTML, these are called
+// "enumerated" attributes with "true" and "false" as possible values.
+// When true, it should be set to a "true" string.
+// When false, it should be set to a "false" string.
+var BOOLEANISH_STRING = 2;
+
+// A real boolean attribute.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+var BOOLEAN = 3;
+
+// An attribute that can be used as a flag as well as with a value.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+// For any other value, should be present with that value.
+var OVERLOADED_BOOLEAN = 4;
+
+// An attribute that must be numeric or parse as a numeric.
+// When falsy, it should be removed.
+var NUMERIC = 5;
+
+// An attribute that must be positive numeric or parse as a positive numeric.
+// When falsy, it should be removed.
+var POSITIVE_NUMERIC = 6;
+
+/* eslint-disable max-len */
+var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
+/* eslint-enable max-len */
+var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
+
+
+var ROOT_ATTRIBUTE_NAME = 'data-reactroot';
+var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var illegalAttributeNameCache = {};
+var validatedAttributeNameCache = {};
+
+function isAttributeNameSafe(attributeName) {
+ if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) {
+ return true;
+ }
+ if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) {
+ return false;
+ }
+ if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
+ validatedAttributeNameCache[attributeName] = true;
+ return true;
+ }
+ illegalAttributeNameCache[attributeName] = true;
+ {
+ warning$1(false, 'Invalid attribute name: `%s`', attributeName);
+ }
+ return false;
+}
+
+function shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null) {
+ return propertyInfo.type === RESERVED;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) {
+ return true;
+ }
+ return false;
+}
+
+function shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null && propertyInfo.type === RESERVED) {
+ return false;
+ }
+ switch (typeof value) {
+ case 'function':
+ // $FlowIssue symbol is perfectly valid here
+ case 'symbol':
+ // eslint-disable-line
+ return true;
+ case 'boolean':
+ {
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ return !propertyInfo.acceptsBooleans;
+ } else {
+ var prefix = name.toLowerCase().slice(0, 5);
+ return prefix !== 'data-' && prefix !== 'aria-';
+ }
+ }
+ default:
+ return false;
+ }
+}
+
+function shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag) {
+ if (value === null || typeof value === 'undefined') {
+ return true;
+ }
+ if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag)) {
+ return true;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ switch (propertyInfo.type) {
+ case BOOLEAN:
+ return !value;
+ case OVERLOADED_BOOLEAN:
+ return value === false;
+ case NUMERIC:
+ return isNaN(value);
+ case POSITIVE_NUMERIC:
+ return isNaN(value) || value < 1;
+ }
+ }
+ return false;
+}
+
+function getPropertyInfo(name) {
+ return properties.hasOwnProperty(name) ? properties[name] : null;
+}
+
+function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace) {
+ this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN;
+ this.attributeName = attributeName;
+ this.attributeNamespace = attributeNamespace;
+ this.mustUseProperty = mustUseProperty;
+ this.propertyName = name;
+ this.type = type;
+}
+
+// When adding attributes to this list, be sure to also add them to
+// the `possibleStandardNames` module to ensure casing and incorrect
+// name warnings.
+var properties = {};
+
+// These props are reserved by React. They shouldn't be written to the DOM.
+['children', 'dangerouslySetInnerHTML',
+// TODO: This prevents the assignment of defaultValue to regular
+// elements (not just inputs). Now that ReactDOMInput assigns to the
+// defaultValue property -- do we need this?
+'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, RESERVED, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// A few React string attributes have a different name.
+// This is a mapping from React prop names to the attribute names.
+[['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv']].forEach(function (_ref) {
+ var name = _ref[0],
+ attributeName = _ref[1];
+
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" HTML attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" SVG attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+// Since these are SVG attributes, their attribute names are case-sensitive.
+['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML boolean attributes.
+['allowFullScreen', 'async',
+// Note: there is a special case that prevents it from being written to the DOM
+// on the client side because the browsers are inconsistent. Instead we call focus().
+'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless',
+// Microdata
+'itemScope'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are the few React props that we set as DOM properties
+// rather than attributes. These are all booleans.
+['checked',
+// Note: `option.selected` is not updated if `select.multiple` is
+// disabled with `removeAttribute`. We have special logic for handling this.
+'multiple', 'muted', 'selected'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, true, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that are "overloaded booleans": they behave like
+// booleans, but can also accept a string value.
+['capture', 'download'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, OVERLOADED_BOOLEAN, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be positive numbers.
+['cols', 'rows', 'size', 'span'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, POSITIVE_NUMERIC, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be numbers.
+['rowSpan', 'start'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, NUMERIC, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+var CAMELIZE = /[\-\:]([a-z])/g;
+var capitalize = function (token) {
+ return token[1].toUpperCase();
+};
+
+// This is a list of all SVG attributes that need special casing, namespacing,
+// or boolean value assignment. Regular attributes that just accept strings
+// and have the same names are omitted, just like in the HTML whitelist.
+// Some of these attributes can be hard to find. This list was created by
+// scrapping the MDN documentation.
+['accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'xmlns:xlink', 'x-height'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, null);
+} // attributeNamespace
+);
+
+// String SVG attributes with the xlink namespace.
+['xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/1999/xlink');
+});
+
+// String SVG attributes with the xml namespace.
+['xml:base', 'xml:lang', 'xml:space'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/XML/1998/namespace');
+});
+
+// These attribute exists both in HTML and SVG.
+// The attribute name is case-sensitive in SVG so we can't just use
+// the React name like we do for attributes that exist only in HTML.
+['tabIndex', 'crossOrigin'].forEach(function (attributeName) {
+ properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty
+ attributeName.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+/**
+ * Get the value for a property on a node. Only used in DEV for SSR validation.
+ * The "expected" argument is used as a hint of what the expected value is.
+ * Some properties have multiple equivalent values.
+ */
+function getValueForProperty(node, name, expected, propertyInfo) {
+ {
+ if (propertyInfo.mustUseProperty) {
+ var propertyName = propertyInfo.propertyName;
+
+ return node[propertyName];
+ } else {
+ var attributeName = propertyInfo.attributeName;
+
+ var stringValue = null;
+
+ if (propertyInfo.type === OVERLOADED_BOOLEAN) {
+ if (node.hasAttribute(attributeName)) {
+ var value = node.getAttribute(attributeName);
+ if (value === '') {
+ return true;
+ }
+ if (shouldRemoveAttribute(name, expected, propertyInfo, false)) {
+ return value;
+ }
+ if (value === '' + expected) {
+ return expected;
+ }
+ return value;
+ }
+ } else if (node.hasAttribute(attributeName)) {
+ if (shouldRemoveAttribute(name, expected, propertyInfo, false)) {
+ // We had an attribute but shouldn't have had one, so read it
+ // for the error message.
+ return node.getAttribute(attributeName);
+ }
+ if (propertyInfo.type === BOOLEAN) {
+ // If this was a boolean, it doesn't matter what the value is
+ // the fact that we have it is the same as the expected.
+ return expected;
+ }
+ // Even if this property uses a namespace we use getAttribute
+ // because we assume its namespaced name is the same as our config.
+ // To use getAttributeNS we need the local name which we don't have
+ // in our config atm.
+ stringValue = node.getAttribute(attributeName);
+ }
+
+ if (shouldRemoveAttribute(name, expected, propertyInfo, false)) {
+ return stringValue === null ? expected : stringValue;
+ } else if (stringValue === '' + expected) {
+ return expected;
+ } else {
+ return stringValue;
+ }
+ }
+ }
+}
+
+/**
+ * Get the value for a attribute on a node. Only used in DEV for SSR validation.
+ * The third argument is used as a hint of what the expected value is. Some
+ * attributes have multiple equivalent values.
+ */
+function getValueForAttribute(node, name, expected) {
+ {
+ if (!isAttributeNameSafe(name)) {
+ return;
+ }
+ if (!node.hasAttribute(name)) {
+ return expected === undefined ? undefined : null;
+ }
+ var value = node.getAttribute(name);
+ if (value === '' + expected) {
+ return expected;
+ }
+ return value;
+ }
+}
+
+/**
+ * Sets the value for a property on a node.
+ *
+ * @param {DOMElement} node
+ * @param {string} name
+ * @param {*} value
+ */
+function setValueForProperty(node, name, value, isCustomComponentTag) {
+ var propertyInfo = getPropertyInfo(name);
+ if (shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag)) {
+ return;
+ }
+ if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) {
+ value = null;
+ }
+ // If the prop isn't in the special list, treat it as a simple attribute.
+ if (isCustomComponentTag || propertyInfo === null) {
+ if (isAttributeNameSafe(name)) {
+ var _attributeName = name;
+ if (value === null) {
+ node.removeAttribute(_attributeName);
+ } else {
+ node.setAttribute(_attributeName, '' + value);
+ }
+ }
+ return;
+ }
+ var mustUseProperty = propertyInfo.mustUseProperty;
+
+ if (mustUseProperty) {
+ var propertyName = propertyInfo.propertyName;
+
+ if (value === null) {
+ var type = propertyInfo.type;
+
+ node[propertyName] = type === BOOLEAN ? false : '';
+ } else {
+ // Contrary to `setAttribute`, object properties are properly
+ // `toString`ed by IE8/9.
+ node[propertyName] = value;
+ }
+ return;
+ }
+ // The rest are treated as attributes with special cases.
+ var attributeName = propertyInfo.attributeName,
+ attributeNamespace = propertyInfo.attributeNamespace;
+
+ if (value === null) {
+ node.removeAttribute(attributeName);
+ } else {
+ var _type = propertyInfo.type;
+
+ var attributeValue = void 0;
+ if (_type === BOOLEAN || _type === OVERLOADED_BOOLEAN && value === true) {
+ attributeValue = '';
+ } else {
+ // `setAttribute` with objects becomes only `[object]` in IE8/9,
+ // ('' + value) makes it output the correct toString()-value.
+ attributeValue = '' + value;
+ }
+ if (attributeNamespace) {
+ node.setAttributeNS(attributeNamespace, attributeName, attributeValue);
+ } else {
+ node.setAttribute(attributeName, attributeValue);
+ }
+ }
+}
+
+// Flow does not allow string concatenation of most non-string types. To work
+// around this limitation, we use an opaque type that can only be obtained by
+// passing the value through getToStringValue first.
+function toString(value) {
+ return '' + value;
+}
+
+function getToStringValue(value) {
+ switch (typeof value) {
+ case 'boolean':
+ case 'number':
+ case 'object':
+ case 'string':
+ case 'undefined':
+ return value;
+ default:
+ // function, symbol are assigned as empty strings
+ return '';
+ }
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+
+var ReactPropTypesSecret$1 = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+
+var ReactPropTypesSecret_1 = ReactPropTypesSecret$1;
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+
+var printWarning = function() {};
+
+{
+ var ReactPropTypesSecret = ReactPropTypesSecret_1;
+ var loggedTypeFailures = {};
+
+ printWarning = function(text) {
+ var message = 'Warning: ' + text;
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+ {
+ for (var typeSpecName in typeSpecs) {
+ if (typeSpecs.hasOwnProperty(typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ if (typeof typeSpecs[typeSpecName] !== 'function') {
+ var err = Error(
+ (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' +
+ 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ }
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
+ } catch (ex) {
+ error = ex;
+ }
+ if (error && !(error instanceof Error)) {
+ printWarning(
+ (componentName || 'React class') + ': type specification of ' +
+ location + ' `' + typeSpecName + '` is invalid; the type checker ' +
+ 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' +
+ 'You may have forgotten to pass an argument to the type checker ' +
+ 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
+ 'shape all require an argument).'
+ );
+
+ }
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var stack = getStack ? getStack() : '';
+
+ printWarning(
+ 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '')
+ );
+ }
+ }
+ }
+ }
+}
+
+var checkPropTypes_1 = checkPropTypes;
+
+var ReactDebugCurrentFrame$1 = null;
+
+var ReactControlledValuePropTypes = {
+ checkPropTypes: null
+};
+
+{
+ ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame;
+
+ var hasReadOnlyValue = {
+ button: true,
+ checkbox: true,
+ image: true,
+ hidden: true,
+ radio: true,
+ reset: true,
+ submit: true
+ };
+
+ var propTypes = {
+ value: function (props, propName, componentName) {
+ if (hasReadOnlyValue[props.type] || props.onChange || props.readOnly || props.disabled || props[propName] == null) {
+ return null;
+ }
+ return new Error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
+ },
+ checked: function (props, propName, componentName) {
+ if (props.onChange || props.readOnly || props.disabled || props[propName] == null) {
+ return null;
+ }
+ return new Error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
+ }
+ };
+
+ /**
+ * Provide a linked `value` attribute for controlled forms. You should not use
+ * this outside of the ReactDOM controlled form components.
+ */
+ ReactControlledValuePropTypes.checkPropTypes = function (tagName, props) {
+ checkPropTypes_1(propTypes, props, 'prop', tagName, ReactDebugCurrentFrame$1.getStackAddendum);
+ };
+}
+
+var enableUserTimingAPI = true;
+
+// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
+var debugRenderPhaseSideEffects = false;
+
+// In some cases, StrictMode should also double-render lifecycles.
+// This can be confusing for tests though,
+// And it can be bad for performance in production.
+// This feature flag can be used to control the behavior:
+var debugRenderPhaseSideEffectsForStrictMode = true;
+
+// To preserve the "Pause on caught exceptions" behavior of the debugger, we
+// replay the begin phase of a failed component inside invokeGuardedCallback.
+var replayFailedUnitOfWorkWithInvokeGuardedCallback = true;
+
+// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
+var warnAboutDeprecatedLifecycles = false;
+
+// Gather advanced timing metrics for Profiler subtrees.
+var enableProfilerTimer = true;
+
+// Trace which interactions trigger each commit.
+var enableSchedulerTracing = true;
+
+// Only used in www builds.
+var enableSuspenseServerRenderer = false; // TODO: true? Here it might just be false.
+
+// Only used in www builds.
+
+
+// Only used in www builds.
+
+
+// React Fire: prevent the value and checked attributes from syncing
+// with their related DOM properties
+var disableInputAttributeSyncing = false;
+
+// These APIs will no longer be "unstable" in the upcoming 16.7 release,
+// Control this behavior with a flag to support 16.6 minor releases in the meanwhile.
+var enableStableConcurrentModeAPIs = false;
+
+var warnAboutShorthandPropertyCollision = false;
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+var didWarnValueDefaultValue = false;
+var didWarnCheckedDefaultChecked = false;
+var didWarnControlledToUncontrolled = false;
+var didWarnUncontrolledToControlled = false;
+
+function isControlled(props) {
+ var usesChecked = props.type === 'checkbox' || props.type === 'radio';
+ return usesChecked ? props.checked != null : props.value != null;
+}
+
+/**
+ * Implements an <input> host component that allows setting these optional
+ * props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
+ *
+ * If `checked` or `value` are not supplied (or null/undefined), user actions
+ * that affect the checked state or value will trigger updates to the element.
+ *
+ * If they are supplied (and not null/undefined), the rendered element will not
+ * trigger updates to the element. Instead, the props must change in order for
+ * the rendered element to be updated.
+ *
+ * The rendered element will be initialized as unchecked (or `defaultChecked`)
+ * with an empty value (or `defaultValue`).
+ *
+ * See http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
+ */
+
+function getHostProps(element, props) {
+ var node = element;
+ var checked = props.checked;
+
+ var hostProps = _assign({}, props, {
+ defaultChecked: undefined,
+ defaultValue: undefined,
+ value: undefined,
+ checked: checked != null ? checked : node._wrapperState.initialChecked
+ });
+
+ return hostProps;
+}
+
+function initWrapperState(element, props) {
+ {
+ ReactControlledValuePropTypes.checkPropTypes('input', props);
+
+ if (props.checked !== undefined && props.defaultChecked !== undefined && !didWarnCheckedDefaultChecked) {
+ warning$1(false, '%s contains an input of type %s with both checked and defaultChecked props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the checked prop, or the defaultChecked prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', getCurrentFiberOwnerNameInDevOrNull() || 'A component', props.type);
+ didWarnCheckedDefaultChecked = true;
+ }
+ if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) {
+ warning$1(false, '%s contains an input of type %s with both value and defaultValue props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', getCurrentFiberOwnerNameInDevOrNull() || 'A component', props.type);
+ didWarnValueDefaultValue = true;
+ }
+ }
+
+ var node = element;
+ var defaultValue = props.defaultValue == null ? '' : props.defaultValue;
+
+ node._wrapperState = {
+ initialChecked: props.checked != null ? props.checked : props.defaultChecked,
+ initialValue: getToStringValue(props.value != null ? props.value : defaultValue),
+ controlled: isControlled(props)
+ };
+}
+
+function updateChecked(element, props) {
+ var node = element;
+ var checked = props.checked;
+ if (checked != null) {
+ setValueForProperty(node, 'checked', checked, false);
+ }
+}
+
+function updateWrapper(element, props) {
+ var node = element;
+ {
+ var _controlled = isControlled(props);
+
+ if (!node._wrapperState.controlled && _controlled && !didWarnUncontrolledToControlled) {
+ warning$1(false, 'A component is changing an uncontrolled input of type %s to be controlled. ' + 'Input elements should not switch from uncontrolled to controlled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components', props.type);
+ didWarnUncontrolledToControlled = true;
+ }
+ if (node._wrapperState.controlled && !_controlled && !didWarnControlledToUncontrolled) {
+ warning$1(false, 'A component is changing a controlled input of type %s to be uncontrolled. ' + 'Input elements should not switch from controlled to uncontrolled (or vice versa). ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://fb.me/react-controlled-components', props.type);
+ didWarnControlledToUncontrolled = true;
+ }
+ }
+
+ updateChecked(element, props);
+
+ var value = getToStringValue(props.value);
+ var type = props.type;
+
+ if (value != null) {
+ if (type === 'number') {
+ if (value === 0 && node.value === '' ||
+ // We explicitly want to coerce to number here if possible.
+ // eslint-disable-next-line
+ node.value != value) {
+ node.value = toString(value);
+ }
+ } else if (node.value !== toString(value)) {
+ node.value = toString(value);
+ }
+ } else if (type === 'submit' || type === 'reset') {
+ // Submit/reset inputs need the attribute removed completely to avoid
+ // blank-text buttons.
+ node.removeAttribute('value');
+ return;
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the value attribute, React only assigns a new value
+ // whenever the defaultValue React prop has changed. When not present,
+ // React does nothing
+ if (props.hasOwnProperty('defaultValue')) {
+ setDefaultValue(node, props.type, getToStringValue(props.defaultValue));
+ }
+ } else {
+ // When syncing the value attribute, the value comes from a cascade of
+ // properties:
+ // 1. The value React property
+ // 2. The defaultValue React property
+ // 3. Otherwise there should be no change
+ if (props.hasOwnProperty('value')) {
+ setDefaultValue(node, props.type, value);
+ } else if (props.hasOwnProperty('defaultValue')) {
+ setDefaultValue(node, props.type, getToStringValue(props.defaultValue));
+ }
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the checked attribute, the attribute is directly
+ // controllable from the defaultValue React property. It needs to be
+ // updated as new props come in.
+ if (props.defaultChecked == null) {
+ node.removeAttribute('checked');
+ } else {
+ node.defaultChecked = !!props.defaultChecked;
+ }
+ } else {
+ // When syncing the checked attribute, it only changes when it needs
+ // to be removed, such as transitioning from a checkbox into a text input
+ if (props.checked == null && props.defaultChecked != null) {
+ node.defaultChecked = !!props.defaultChecked;
+ }
+ }
+}
+
+function postMountWrapper(element, props, isHydrating) {
+ var node = element;
+
+ // Do not assign value if it is already set. This prevents user text input
+ // from being lost during SSR hydration.
+ if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) {
+ var type = props.type;
+ var isButton = type === 'submit' || type === 'reset';
+
+ // Avoid setting value attribute on submit/reset inputs as it overrides the
+ // default value provided by the browser. See: #12872
+ if (isButton && (props.value === undefined || props.value === null)) {
+ return;
+ }
+
+ var _initialValue = toString(node._wrapperState.initialValue);
+
+ // Do not assign value if it is already set. This prevents user text input
+ // from being lost during SSR hydration.
+ if (!isHydrating) {
+ if (disableInputAttributeSyncing) {
+ var value = getToStringValue(props.value);
+
+ // When not syncing the value attribute, the value property points
+ // directly to the React prop. Only assign it if it exists.
+ if (value != null) {
+ // Always assign on buttons so that it is possible to assign an
+ // empty string to clear button text.
+ //
+ // Otherwise, do not re-assign the value property if is empty. This
+ // potentially avoids a DOM write and prevents Firefox (~60.0.1) from
+ // prematurely marking required inputs as invalid. Equality is compared
+ // to the current value in case the browser provided value is not an
+ // empty string.
+ if (isButton || value !== node.value) {
+ node.value = toString(value);
+ }
+ }
+ } else {
+ // When syncing the value attribute, the value property should use
+ // the wrapperState._initialValue property. This uses:
+ //
+ // 1. The value React property when present
+ // 2. The defaultValue React property when present
+ // 3. An empty string
+ if (_initialValue !== node.value) {
+ node.value = _initialValue;
+ }
+ }
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the value attribute, assign the value attribute
+ // directly from the defaultValue React property (when present)
+ var defaultValue = getToStringValue(props.defaultValue);
+ if (defaultValue != null) {
+ node.defaultValue = toString(defaultValue);
+ }
+ } else {
+ // Otherwise, the value attribute is synchronized to the property,
+ // so we assign defaultValue to the same thing as the value property
+ // assignment step above.
+ node.defaultValue = _initialValue;
+ }
+ }
+
+ // Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
+ // this is needed to work around a chrome bug where setting defaultChecked
+ // will sometimes influence the value of checked (even after detachment).
+ // Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416
+ // We need to temporarily unset name to avoid disrupting radio button groups.
+ var name = node.name;
+ if (name !== '') {
+ node.name = '';
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the checked attribute, the checked property
+ // never gets assigned. It must be manually set. We don't want
+ // to do this when hydrating so that existing user input isn't
+ // modified
+ if (!isHydrating) {
+ updateChecked(element, props);
+ }
+
+ // Only assign the checked attribute if it is defined. This saves
+ // a DOM write when controlling the checked attribute isn't needed
+ // (text inputs, submit/reset)
+ if (props.hasOwnProperty('defaultChecked')) {
+ node.defaultChecked = !node.defaultChecked;
+ node.defaultChecked = !!props.defaultChecked;
+ }
+ } else {
+ // When syncing the checked attribute, both the checked property and
+ // attribute are assigned at the same time using defaultChecked. This uses:
+ //
+ // 1. The checked React property when present
+ // 2. The defaultChecked React property when present
+ // 3. Otherwise, false
+ node.defaultChecked = !node.defaultChecked;
+ node.defaultChecked = !!node._wrapperState.initialChecked;
+ }
+
+ if (name !== '') {
+ node.name = name;
+ }
+}
+
+function restoreControlledState(element, props) {
+ var node = element;
+ updateWrapper(node, props);
+ updateNamedCousins(node, props);
+}
+
+function updateNamedCousins(rootNode, props) {
+ var name = props.name;
+ if (props.type === 'radio' && name != null) {
+ var queryRoot = rootNode;
+
+ while (queryRoot.parentNode) {
+ queryRoot = queryRoot.parentNode;
+ }
+
+ // If `rootNode.form` was non-null, then we could try `form.elements`,
+ // but that sometimes behaves strangely in IE8. We could also try using
+ // `form.getElementsByName`, but that will only return direct children
+ // and won't include inputs that use the HTML5 `form=` attribute. Since
+ // the input might not even be in a form. It might not even be in the
+ // document. Let's just use the local `querySelectorAll` to ensure we don't
+ // miss anything.
+ var group = queryRoot.querySelectorAll('input[name=' + JSON.stringify('' + name) + '][type="radio"]');
+
+ for (var i = 0; i < group.length; i++) {
+ var otherNode = group[i];
+ if (otherNode === rootNode || otherNode.form !== rootNode.form) {
+ continue;
+ }
+ // This will throw if radio buttons rendered by different copies of React
+ // and the same name are rendered into the same form (same as #1939).
+ // That's probably okay; we don't support it just as we don't support
+ // mixing React radio buttons with non-React ones.
+ var otherProps = getFiberCurrentPropsFromNode$1(otherNode);
+ !otherProps ? invariant(false, 'ReactDOMInput: Mixing React and non-React radio inputs with the same `name` is not supported.') : void 0;
+
+ // We need update the tracked value on the named cousin since the value
+ // was changed but the input saw no event or value set
+ updateValueIfChanged(otherNode);
+
+ // If this is a controlled radio button group, forcing the input that
+ // was previously checked to update will cause it to be come re-checked
+ // as appropriate.
+ updateWrapper(otherNode, otherProps);
+ }
+ }
+}
+
+// In Chrome, assigning defaultValue to certain input types triggers input validation.
+// For number inputs, the display value loses trailing decimal points. For email inputs,
+// Chrome raises "The specified value <x> is not a valid email address".
+//
+// Here we check to see if the defaultValue has actually changed, avoiding these problems
+// when the user is inputting text
+//
+// https://github.com/facebook/react/issues/7253
+function setDefaultValue(node, type, value) {
+ if (
+ // Focused number inputs synchronize on blur. See ChangeEventPlugin.js
+ type !== 'number' || node.ownerDocument.activeElement !== node) {
+ if (value == null) {
+ node.defaultValue = toString(node._wrapperState.initialValue);
+ } else if (node.defaultValue !== toString(value)) {
+ node.defaultValue = toString(value);
+ }
+ }
+}
+
+var eventTypes$1 = {
+ change: {
+ phasedRegistrationNames: {
+ bubbled: 'onChange',
+ captured: 'onChangeCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_CHANGE, TOP_CLICK, TOP_FOCUS, TOP_INPUT, TOP_KEY_DOWN, TOP_KEY_UP, TOP_SELECTION_CHANGE]
+ }
+};
+
+function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
+ var event = SyntheticEvent.getPooled(eventTypes$1.change, inst, nativeEvent, target);
+ event.type = 'change';
+ // Flag this event loop as needing state restore.
+ enqueueStateRestore(target);
+ accumulateTwoPhaseDispatches(event);
+ return event;
+}
+/**
+ * For IE shims
+ */
+var activeElement = null;
+var activeElementInst = null;
+
+/**
+ * SECTION: handle `change` event
+ */
+function shouldUseChangeEvent(elem) {
+ var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
+ return nodeName === 'select' || nodeName === 'input' && elem.type === 'file';
+}
+
+function manualDispatchChangeEvent(nativeEvent) {
+ var event = createAndAccumulateChangeEvent(activeElementInst, nativeEvent, getEventTarget(nativeEvent));
+
+ // If change and propertychange bubbled, we'd just bind to it like all the
+ // other events and have it go through ReactBrowserEventEmitter. Since it
+ // doesn't, we manually listen for the events and so we have to enqueue and
+ // process the abstract event manually.
+ //
+ // Batching is necessary here in order to ensure that all event handlers run
+ // before the next rerender (including event handlers attached to ancestor
+ // elements instead of directly on the input). Without this, controlled
+ // components don't work properly in conjunction with event bubbling because
+ // the component is rerendered and the value reverted before all the event
+ // handlers can run. See https://github.com/facebook/react/issues/708.
+ batchedUpdates(runEventInBatch, event);
+}
+
+function runEventInBatch(event) {
+ runEventsInBatch(event);
+}
+
+function getInstIfValueChanged(targetInst) {
+ var targetNode = getNodeFromInstance$1(targetInst);
+ if (updateValueIfChanged(targetNode)) {
+ return targetInst;
+ }
+}
+
+function getTargetInstForChangeEvent(topLevelType, targetInst) {
+ if (topLevelType === TOP_CHANGE) {
+ return targetInst;
+ }
+}
+
+/**
+ * SECTION: handle `input` event
+ */
+var isInputEventSupported = false;
+if (canUseDOM) {
+ // IE9 claims to support the input event but fails to trigger it when
+ // deleting text, so we ignore its input events.
+ isInputEventSupported = isEventSupported('input') && (!document.documentMode || document.documentMode > 9);
+}
+
+/**
+ * (For IE <=9) Starts tracking propertychange events on the passed-in element
+ * and override the value property so that we can distinguish user events from
+ * value changes in JS.
+ */
+function startWatchingForValueChange(target, targetInst) {
+ activeElement = target;
+ activeElementInst = targetInst;
+ activeElement.attachEvent('onpropertychange', handlePropertyChange);
+}
+
+/**
+ * (For IE <=9) Removes the event listeners from the currently-tracked element,
+ * if any exists.
+ */
+function stopWatchingForValueChange() {
+ if (!activeElement) {
+ return;
+ }
+ activeElement.detachEvent('onpropertychange', handlePropertyChange);
+ activeElement = null;
+ activeElementInst = null;
+}
+
+/**
+ * (For IE <=9) Handles a propertychange event, sending a `change` event if
+ * the value of the active element has changed.
+ */
+function handlePropertyChange(nativeEvent) {
+ if (nativeEvent.propertyName !== 'value') {
+ return;
+ }
+ if (getInstIfValueChanged(activeElementInst)) {
+ manualDispatchChangeEvent(nativeEvent);
+ }
+}
+
+function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) {
+ if (topLevelType === TOP_FOCUS) {
+ // In IE9, propertychange fires for most input events but is buggy and
+ // doesn't fire when text is deleted, but conveniently, selectionchange
+ // appears to fire in all of the remaining cases so we catch those and
+ // forward the event if the value has changed
+ // In either case, we don't want to call the event handler if the value
+ // is changed from JS so we redefine a setter for `.value` that updates
+ // our activeElementValue variable, allowing us to ignore those changes
+ //
+ // stopWatching() should be a noop here but we call it just in case we
+ // missed a blur event somehow.
+ stopWatchingForValueChange();
+ startWatchingForValueChange(target, targetInst);
+ } else if (topLevelType === TOP_BLUR) {
+ stopWatchingForValueChange();
+ }
+}
+
+// For IE8 and IE9.
+function getTargetInstForInputEventPolyfill(topLevelType, targetInst) {
+ if (topLevelType === TOP_SELECTION_CHANGE || topLevelType === TOP_KEY_UP || topLevelType === TOP_KEY_DOWN) {
+ // On the selectionchange event, the target is just document which isn't
+ // helpful for us so just check activeElement instead.
+ //
+ // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
+ // propertychange on the first input event after setting `value` from a
+ // script and fires only keydown, keypress, keyup. Catching keyup usually
+ // gets it and catching keydown lets us fire an event for the first
+ // keystroke if user does a key repeat (it'll be a little delayed: right
+ // before the second keystroke). Other input methods (e.g., paste) seem to
+ // fire selectionchange normally.
+ return getInstIfValueChanged(activeElementInst);
+ }
+}
+
+/**
+ * SECTION: handle `click` event
+ */
+function shouldUseClickEvent(elem) {
+ // Use the `click` event to detect changes to checkbox and radio inputs.
+ // This approach works across all browsers, whereas `change` does not fire
+ // until `blur` in IE8.
+ var nodeName = elem.nodeName;
+ return nodeName && nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio');
+}
+
+function getTargetInstForClickEvent(topLevelType, targetInst) {
+ if (topLevelType === TOP_CLICK) {
+ return getInstIfValueChanged(targetInst);
+ }
+}
+
+function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) {
+ if (topLevelType === TOP_INPUT || topLevelType === TOP_CHANGE) {
+ return getInstIfValueChanged(targetInst);
+ }
+}
+
+function handleControlledInputBlur(node) {
+ var state = node._wrapperState;
+
+ if (!state || !state.controlled || node.type !== 'number') {
+ return;
+ }
+
+ if (!disableInputAttributeSyncing) {
+ // If controlled, assign the value attribute to the current value on blur
+ setDefaultValue(node, 'number', node.value);
+ }
+}
+
+/**
+ * This plugin creates an `onChange` event that normalizes change events
+ * across form elements. This event fires at a time when it's possible to
+ * change the element's value without seeing a flicker.
+ *
+ * Supported elements are:
+ * - input (see `isTextInputElement`)
+ * - textarea
+ * - select
+ */
+var ChangeEventPlugin = {
+ eventTypes: eventTypes$1,
+
+ _isInputEventSupported: isInputEventSupported,
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var targetNode = targetInst ? getNodeFromInstance$1(targetInst) : window;
+
+ var getTargetInstFunc = void 0,
+ handleEventFunc = void 0;
+ if (shouldUseChangeEvent(targetNode)) {
+ getTargetInstFunc = getTargetInstForChangeEvent;
+ } else if (isTextInputElement(targetNode)) {
+ if (isInputEventSupported) {
+ getTargetInstFunc = getTargetInstForInputOrChangeEvent;
+ } else {
+ getTargetInstFunc = getTargetInstForInputEventPolyfill;
+ handleEventFunc = handleEventsForInputEventPolyfill;
+ }
+ } else if (shouldUseClickEvent(targetNode)) {
+ getTargetInstFunc = getTargetInstForClickEvent;
+ }
+
+ if (getTargetInstFunc) {
+ var inst = getTargetInstFunc(topLevelType, targetInst);
+ if (inst) {
+ var event = createAndAccumulateChangeEvent(inst, nativeEvent, nativeEventTarget);
+ return event;
+ }
+ }
+
+ if (handleEventFunc) {
+ handleEventFunc(topLevelType, targetNode, targetInst);
+ }
+
+ // When blurring, set the value attribute for number inputs
+ if (topLevelType === TOP_BLUR) {
+ handleControlledInputBlur(targetNode);
+ }
+ }
+};
+
+/**
+ * Module that is injectable into `EventPluginHub`, that specifies a
+ * deterministic ordering of `EventPlugin`s. A convenient way to reason about
+ * plugins, without having to package every one of them. This is better than
+ * having plugins be ordered in the same order that they are injected because
+ * that ordering would be influenced by the packaging order.
+ * `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that
+ * preventing default on events is convenient in `SimpleEventPlugin` handlers.
+ */
+var DOMEventPluginOrder = ['ResponderEventPlugin', 'SimpleEventPlugin', 'EnterLeaveEventPlugin', 'ChangeEventPlugin', 'SelectEventPlugin', 'BeforeInputEventPlugin'];
+
+var SyntheticUIEvent = SyntheticEvent.extend({
+ view: null,
+ detail: null
+});
+
+var modifierKeyToProp = {
+ Alt: 'altKey',
+ Control: 'ctrlKey',
+ Meta: 'metaKey',
+ Shift: 'shiftKey'
+};
+
+// Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support
+// getModifierState. If getModifierState is not supported, we map it to a set of
+// modifier keys exposed by the event. In this case, Lock-keys are not supported.
+/**
+ * Translation from modifier key to the associated property in the event.
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
+ */
+
+function modifierStateGetter(keyArg) {
+ var syntheticEvent = this;
+ var nativeEvent = syntheticEvent.nativeEvent;
+ if (nativeEvent.getModifierState) {
+ return nativeEvent.getModifierState(keyArg);
+ }
+ var keyProp = modifierKeyToProp[keyArg];
+ return keyProp ? !!nativeEvent[keyProp] : false;
+}
+
+function getEventModifierState(nativeEvent) {
+ return modifierStateGetter;
+}
+
+var previousScreenX = 0;
+var previousScreenY = 0;
+// Use flags to signal movementX/Y has already been set
+var isMovementXSet = false;
+var isMovementYSet = false;
+
+/**
+ * @interface MouseEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticMouseEvent = SyntheticUIEvent.extend({
+ screenX: null,
+ screenY: null,
+ clientX: null,
+ clientY: null,
+ pageX: null,
+ pageY: null,
+ ctrlKey: null,
+ shiftKey: null,
+ altKey: null,
+ metaKey: null,
+ getModifierState: getEventModifierState,
+ button: null,
+ buttons: null,
+ relatedTarget: function (event) {
+ return event.relatedTarget || (event.fromElement === event.srcElement ? event.toElement : event.fromElement);
+ },
+ movementX: function (event) {
+ if ('movementX' in event) {
+ return event.movementX;
+ }
+
+ var screenX = previousScreenX;
+ previousScreenX = event.screenX;
+
+ if (!isMovementXSet) {
+ isMovementXSet = true;
+ return 0;
+ }
+
+ return event.type === 'mousemove' ? event.screenX - screenX : 0;
+ },
+ movementY: function (event) {
+ if ('movementY' in event) {
+ return event.movementY;
+ }
+
+ var screenY = previousScreenY;
+ previousScreenY = event.screenY;
+
+ if (!isMovementYSet) {
+ isMovementYSet = true;
+ return 0;
+ }
+
+ return event.type === 'mousemove' ? event.screenY - screenY : 0;
+ }
+});
+
+/**
+ * @interface PointerEvent
+ * @see http://www.w3.org/TR/pointerevents/
+ */
+var SyntheticPointerEvent = SyntheticMouseEvent.extend({
+ pointerId: null,
+ width: null,
+ height: null,
+ pressure: null,
+ tangentialPressure: null,
+ tiltX: null,
+ tiltY: null,
+ twist: null,
+ pointerType: null,
+ isPrimary: null
+});
+
+var eventTypes$2 = {
+ mouseEnter: {
+ registrationName: 'onMouseEnter',
+ dependencies: [TOP_MOUSE_OUT, TOP_MOUSE_OVER]
+ },
+ mouseLeave: {
+ registrationName: 'onMouseLeave',
+ dependencies: [TOP_MOUSE_OUT, TOP_MOUSE_OVER]
+ },
+ pointerEnter: {
+ registrationName: 'onPointerEnter',
+ dependencies: [TOP_POINTER_OUT, TOP_POINTER_OVER]
+ },
+ pointerLeave: {
+ registrationName: 'onPointerLeave',
+ dependencies: [TOP_POINTER_OUT, TOP_POINTER_OVER]
+ }
+};
+
+var EnterLeaveEventPlugin = {
+ eventTypes: eventTypes$2,
+
+ /**
+ * For almost every interaction we care about, there will be both a top-level
+ * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
+ * we do not extract duplicate events. However, moving the mouse into the
+ * browser from outside will not fire a `mouseout` event. In this case, we use
+ * the `mouseover` top-level event.
+ */
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var isOverEvent = topLevelType === TOP_MOUSE_OVER || topLevelType === TOP_POINTER_OVER;
+ var isOutEvent = topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_POINTER_OUT;
+
+ if (isOverEvent && (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
+ return null;
+ }
+
+ if (!isOutEvent && !isOverEvent) {
+ // Must not be a mouse or pointer in or out - ignoring.
+ return null;
+ }
+
+ var win = void 0;
+ if (nativeEventTarget.window === nativeEventTarget) {
+ // `nativeEventTarget` is probably a window object.
+ win = nativeEventTarget;
+ } else {
+ // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
+ var doc = nativeEventTarget.ownerDocument;
+ if (doc) {
+ win = doc.defaultView || doc.parentWindow;
+ } else {
+ win = window;
+ }
+ }
+
+ var from = void 0;
+ var to = void 0;
+ if (isOutEvent) {
+ from = targetInst;
+ var related = nativeEvent.relatedTarget || nativeEvent.toElement;
+ to = related ? getClosestInstanceFromNode(related) : null;
+ } else {
+ // Moving to a node from outside the window.
+ from = null;
+ to = targetInst;
+ }
+
+ if (from === to) {
+ // Nothing pertains to our managed components.
+ return null;
+ }
+
+ var eventInterface = void 0,
+ leaveEventType = void 0,
+ enterEventType = void 0,
+ eventTypePrefix = void 0;
+
+ if (topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_MOUSE_OVER) {
+ eventInterface = SyntheticMouseEvent;
+ leaveEventType = eventTypes$2.mouseLeave;
+ enterEventType = eventTypes$2.mouseEnter;
+ eventTypePrefix = 'mouse';
+ } else if (topLevelType === TOP_POINTER_OUT || topLevelType === TOP_POINTER_OVER) {
+ eventInterface = SyntheticPointerEvent;
+ leaveEventType = eventTypes$2.pointerLeave;
+ enterEventType = eventTypes$2.pointerEnter;
+ eventTypePrefix = 'pointer';
+ }
+
+ var fromNode = from == null ? win : getNodeFromInstance$1(from);
+ var toNode = to == null ? win : getNodeFromInstance$1(to);
+
+ var leave = eventInterface.getPooled(leaveEventType, from, nativeEvent, nativeEventTarget);
+ leave.type = eventTypePrefix + 'leave';
+ leave.target = fromNode;
+ leave.relatedTarget = toNode;
+
+ var enter = eventInterface.getPooled(enterEventType, to, nativeEvent, nativeEventTarget);
+ enter.type = eventTypePrefix + 'enter';
+ enter.target = toNode;
+ enter.relatedTarget = fromNode;
+
+ accumulateEnterLeaveDispatches(leave, enter, from, to);
+
+ return [leave, enter];
+ }
+};
+
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
+ ;
+}
+
+var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+
+/**
+ * Performs equality by iterating through keys on an object and returning false
+ * when any key has values which are not strictly equal between the arguments.
+ * Returns true when the values of all keys are strictly equal.
+ */
+function shallowEqual(objA, objB) {
+ if (is(objA, objB)) {
+ return true;
+ }
+
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
+ return false;
+ }
+
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
+
+ if (keysA.length !== keysB.length) {
+ return false;
+ }
+
+ // Test for A's keys different from B.
+ for (var i = 0; i < keysA.length; i++) {
+ if (!hasOwnProperty$1.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * `ReactInstanceMap` maintains a mapping from a public facing stateful
+ * instance (key) and the internal representation (value). This allows public
+ * methods to accept the user facing instance as an argument and map them back
+ * to internal methods.
+ *
+ * Note that this module is currently shared and assumed to be stateless.
+ * If this becomes an actual Map, that will break.
+ */
+
+/**
+ * This API should be called `delete` but we'd have to make sure to always
+ * transform these to strings for IE support. When this transform is fully
+ * supported we can rename it.
+ */
+
+
+function get(key) {
+ return key._reactInternalFiber;
+}
+
+function has(key) {
+ return key._reactInternalFiber !== undefined;
+}
+
+function set(key, value) {
+ key._reactInternalFiber = value;
+}
+
+// Don't change these two values. They're used by React Dev Tools.
+var NoEffect = /* */0;
+var PerformedWork = /* */1;
+
+// You can change the rest (and add more).
+var Placement = /* */2;
+var Update = /* */4;
+var PlacementAndUpdate = /* */6;
+var Deletion = /* */8;
+var ContentReset = /* */16;
+var Callback = /* */32;
+var DidCapture = /* */64;
+var Ref = /* */128;
+var Snapshot = /* */256;
+var Passive = /* */512;
+
+// Passive & Update & Callback & Ref & Snapshot
+var LifecycleEffectMask = /* */932;
+
+// Union of all host effects
+var HostEffectMask = /* */1023;
+
+var Incomplete = /* */1024;
+var ShouldCapture = /* */2048;
+
+var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;
+
+var MOUNTING = 1;
+var MOUNTED = 2;
+var UNMOUNTED = 3;
+
+function isFiberMountedImpl(fiber) {
+ var node = fiber;
+ if (!fiber.alternate) {
+ // If there is no alternate, this might be a new tree that isn't inserted
+ // yet. If it is, then it will have a pending insertion effect on it.
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ while (node.return) {
+ node = node.return;
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ }
+ } else {
+ while (node.return) {
+ node = node.return;
+ }
+ }
+ if (node.tag === HostRoot) {
+ // TODO: Check if this was a nested HostRoot when used with
+ // renderContainerIntoSubtree.
+ return MOUNTED;
+ }
+ // If we didn't hit the root, that means that we're in an disconnected tree
+ // that has been unmounted.
+ return UNMOUNTED;
+}
+
+function isFiberMounted(fiber) {
+ return isFiberMountedImpl(fiber) === MOUNTED;
+}
+
+function isMounted(component) {
+ {
+ var owner = ReactCurrentOwner$1.current;
+ if (owner !== null && owner.tag === ClassComponent) {
+ var ownerFiber = owner;
+ var instance = ownerFiber.stateNode;
+ !instance._warnedAboutRefsInRender ? warningWithoutStack$1(false, '%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', getComponentName(ownerFiber.type) || 'A component') : void 0;
+ instance._warnedAboutRefsInRender = true;
+ }
+ }
+
+ var fiber = get(component);
+ if (!fiber) {
+ return false;
+ }
+ return isFiberMountedImpl(fiber) === MOUNTED;
+}
+
+function assertIsMounted(fiber) {
+ !(isFiberMountedImpl(fiber) === MOUNTED) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0;
+}
+
+function findCurrentFiberUsingSlowPath(fiber) {
+ var alternate = fiber.alternate;
+ if (!alternate) {
+ // If there is no alternate, then we only need to check if it is mounted.
+ var state = isFiberMountedImpl(fiber);
+ !(state !== UNMOUNTED) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0;
+ if (state === MOUNTING) {
+ return null;
+ }
+ return fiber;
+ }
+ // If we have two possible branches, we'll walk backwards up to the root
+ // to see what path the root points to. On the way we may hit one of the
+ // special cases and we'll deal with them.
+ var a = fiber;
+ var b = alternate;
+ while (true) {
+ var parentA = a.return;
+ var parentB = parentA ? parentA.alternate : null;
+ if (!parentA || !parentB) {
+ // We're at the root.
+ break;
+ }
+
+ // If both copies of the parent fiber point to the same child, we can
+ // assume that the child is current. This happens when we bailout on low
+ // priority: the bailed out fiber's child reuses the current child.
+ if (parentA.child === parentB.child) {
+ var child = parentA.child;
+ while (child) {
+ if (child === a) {
+ // We've determined that A is the current branch.
+ assertIsMounted(parentA);
+ return fiber;
+ }
+ if (child === b) {
+ // We've determined that B is the current branch.
+ assertIsMounted(parentA);
+ return alternate;
+ }
+ child = child.sibling;
+ }
+ // We should never have an alternate for any mounting node. So the only
+ // way this could possibly happen is if this was unmounted, if at all.
+ invariant(false, 'Unable to find node on an unmounted component.');
+ }
+
+ if (a.return !== b.return) {
+ // The return pointer of A and the return pointer of B point to different
+ // fibers. We assume that return pointers never criss-cross, so A must
+ // belong to the child set of A.return, and B must belong to the child
+ // set of B.return.
+ a = parentA;
+ b = parentB;
+ } else {
+ // The return pointers point to the same fiber. We'll have to use the
+ // default, slow path: scan the child sets of each parent alternate to see
+ // which child belongs to which set.
+ //
+ // Search parent A's child set
+ var didFindChild = false;
+ var _child = parentA.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentA;
+ b = parentB;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentA;
+ a = parentB;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ if (!didFindChild) {
+ // Search parent B's child set
+ _child = parentB.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentB;
+ b = parentA;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentB;
+ a = parentA;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ !didFindChild ? invariant(false, 'Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue.') : void 0;
+ }
+ }
+
+ !(a.alternate === b) ? invariant(false, 'Return fibers should always be each others\' alternates. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ }
+ // If the root is not a host container, we're in a disconnected tree. I.e.
+ // unmounted.
+ !(a.tag === HostRoot) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0;
+ if (a.stateNode.current === a) {
+ // We've determined that A is the current branch.
+ return fiber;
+ }
+ // Otherwise B has to be current branch.
+ return alternate;
+}
+
+function findCurrentHostFiber(parent) {
+ var currentParent = findCurrentFiberUsingSlowPath(parent);
+ if (!currentParent) {
+ return null;
+ }
+
+ // Next we'll drill down this component to find the first HostComponent/Text.
+ var node = currentParent;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ return node;
+ } else if (node.child) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === currentParent) {
+ return null;
+ }
+ while (!node.sibling) {
+ if (!node.return || node.return === currentParent) {
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ // Flow needs the return null here, but ESLint complains about it.
+ // eslint-disable-next-line no-unreachable
+ return null;
+}
+
+function findCurrentHostFiberWithNoPortals(parent) {
+ var currentParent = findCurrentFiberUsingSlowPath(parent);
+ if (!currentParent) {
+ return null;
+ }
+
+ // Next we'll drill down this component to find the first HostComponent/Text.
+ var node = currentParent;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ return node;
+ } else if (node.child && node.tag !== HostPortal) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === currentParent) {
+ return null;
+ }
+ while (!node.sibling) {
+ if (!node.return || node.return === currentParent) {
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ // Flow needs the return null here, but ESLint complains about it.
+ // eslint-disable-next-line no-unreachable
+ return null;
+}
+
+function addEventBubbleListener(element, eventType, listener) {
+ element.addEventListener(eventType, listener, false);
+}
+
+function addEventCaptureListener(element, eventType, listener) {
+ element.addEventListener(eventType, listener, true);
+}
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
+ */
+var SyntheticAnimationEvent = SyntheticEvent.extend({
+ animationName: null,
+ elapsedTime: null,
+ pseudoElement: null
+});
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/clipboard-apis/
+ */
+var SyntheticClipboardEvent = SyntheticEvent.extend({
+ clipboardData: function (event) {
+ return 'clipboardData' in event ? event.clipboardData : window.clipboardData;
+ }
+});
+
+/**
+ * @interface FocusEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticFocusEvent = SyntheticUIEvent.extend({
+ relatedTarget: null
+});
+
+/**
+ * `charCode` represents the actual "character code" and is safe to use with
+ * `String.fromCharCode`. As such, only keys that correspond to printable
+ * characters produce a valid `charCode`, the only exception to this is Enter.
+ * The Tab-key is considered non-printable and does not have a `charCode`,
+ * presumably because it does not produce a tab-character in browsers.
+ *
+ * @param {object} nativeEvent Native browser event.
+ * @return {number} Normalized `charCode` property.
+ */
+function getEventCharCode(nativeEvent) {
+ var charCode = void 0;
+ var keyCode = nativeEvent.keyCode;
+
+ if ('charCode' in nativeEvent) {
+ charCode = nativeEvent.charCode;
+
+ // FF does not set `charCode` for the Enter-key, check against `keyCode`.
+ if (charCode === 0 && keyCode === 13) {
+ charCode = 13;
+ }
+ } else {
+ // IE8 does not implement `charCode`, but `keyCode` has the correct value.
+ charCode = keyCode;
+ }
+
+ // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
+ // report Enter as charCode 10 when ctrl is pressed.
+ if (charCode === 10) {
+ charCode = 13;
+ }
+
+ // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
+ // Must not discard the (non-)printable Enter-key.
+ if (charCode >= 32 || charCode === 13) {
+ return charCode;
+ }
+
+ return 0;
+}
+
+/**
+ * Normalization of deprecated HTML5 `key` values
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
+ */
+var normalizeKey = {
+ Esc: 'Escape',
+ Spacebar: ' ',
+ Left: 'ArrowLeft',
+ Up: 'ArrowUp',
+ Right: 'ArrowRight',
+ Down: 'ArrowDown',
+ Del: 'Delete',
+ Win: 'OS',
+ Menu: 'ContextMenu',
+ Apps: 'ContextMenu',
+ Scroll: 'ScrollLock',
+ MozPrintableKey: 'Unidentified'
+};
+
+/**
+ * Translation from legacy `keyCode` to HTML5 `key`
+ * Only special keys supported, all others depend on keyboard layout or browser
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
+ */
+var translateToKey = {
+ '8': 'Backspace',
+ '9': 'Tab',
+ '12': 'Clear',
+ '13': 'Enter',
+ '16': 'Shift',
+ '17': 'Control',
+ '18': 'Alt',
+ '19': 'Pause',
+ '20': 'CapsLock',
+ '27': 'Escape',
+ '32': ' ',
+ '33': 'PageUp',
+ '34': 'PageDown',
+ '35': 'End',
+ '36': 'Home',
+ '37': 'ArrowLeft',
+ '38': 'ArrowUp',
+ '39': 'ArrowRight',
+ '40': 'ArrowDown',
+ '45': 'Insert',
+ '46': 'Delete',
+ '112': 'F1',
+ '113': 'F2',
+ '114': 'F3',
+ '115': 'F4',
+ '116': 'F5',
+ '117': 'F6',
+ '118': 'F7',
+ '119': 'F8',
+ '120': 'F9',
+ '121': 'F10',
+ '122': 'F11',
+ '123': 'F12',
+ '144': 'NumLock',
+ '145': 'ScrollLock',
+ '224': 'Meta'
+};
+
+/**
+ * @param {object} nativeEvent Native browser event.
+ * @return {string} Normalized `key` property.
+ */
+function getEventKey(nativeEvent) {
+ if (nativeEvent.key) {
+ // Normalize inconsistent values reported by browsers due to
+ // implementations of a working draft specification.
+
+ // FireFox implements `key` but returns `MozPrintableKey` for all
+ // printable characters (normalized to `Unidentified`), ignore it.
+ var key = normalizeKey[nativeEvent.key] || nativeEvent.key;
+ if (key !== 'Unidentified') {
+ return key;
+ }
+ }
+
+ // Browser does not implement `key`, polyfill as much of it as we can.
+ if (nativeEvent.type === 'keypress') {
+ var charCode = getEventCharCode(nativeEvent);
+
+ // The enter-key is technically both printable and non-printable and can
+ // thus be captured by `keypress`, no other non-printable key should.
+ return charCode === 13 ? 'Enter' : String.fromCharCode(charCode);
+ }
+ if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') {
+ // While user keyboard layout determines the actual meaning of each
+ // `keyCode` value, almost all function keys have a universal value.
+ return translateToKey[nativeEvent.keyCode] || 'Unidentified';
+ }
+ return '';
+}
+
+/**
+ * @interface KeyboardEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticKeyboardEvent = SyntheticUIEvent.extend({
+ key: getEventKey,
+ location: null,
+ ctrlKey: null,
+ shiftKey: null,
+ altKey: null,
+ metaKey: null,
+ repeat: null,
+ locale: null,
+ getModifierState: getEventModifierState,
+ // Legacy Interface
+ charCode: function (event) {
+ // `charCode` is the result of a KeyPress event and represents the value of
+ // the actual printable character.
+
+ // KeyPress is deprecated, but its replacement is not yet final and not
+ // implemented in any major browser. Only KeyPress has charCode.
+ if (event.type === 'keypress') {
+ return getEventCharCode(event);
+ }
+ return 0;
+ },
+ keyCode: function (event) {
+ // `keyCode` is the result of a KeyDown/Up event and represents the value of
+ // physical keyboard key.
+
+ // The actual meaning of the value depends on the users' keyboard layout
+ // which cannot be detected. Assuming that it is a US keyboard layout
+ // provides a surprisingly accurate mapping for US and European users.
+ // Due to this, it is left to the user to implement at this time.
+ if (event.type === 'keydown' || event.type === 'keyup') {
+ return event.keyCode;
+ }
+ return 0;
+ },
+ which: function (event) {
+ // `which` is an alias for either `keyCode` or `charCode` depending on the
+ // type of the event.
+ if (event.type === 'keypress') {
+ return getEventCharCode(event);
+ }
+ if (event.type === 'keydown' || event.type === 'keyup') {
+ return event.keyCode;
+ }
+ return 0;
+ }
+});
+
+/**
+ * @interface DragEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticDragEvent = SyntheticMouseEvent.extend({
+ dataTransfer: null
+});
+
+/**
+ * @interface TouchEvent
+ * @see http://www.w3.org/TR/touch-events/
+ */
+var SyntheticTouchEvent = SyntheticUIEvent.extend({
+ touches: null,
+ targetTouches: null,
+ changedTouches: null,
+ altKey: null,
+ metaKey: null,
+ ctrlKey: null,
+ shiftKey: null,
+ getModifierState: getEventModifierState
+});
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
+ */
+var SyntheticTransitionEvent = SyntheticEvent.extend({
+ propertyName: null,
+ elapsedTime: null,
+ pseudoElement: null
+});
+
+/**
+ * @interface WheelEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticWheelEvent = SyntheticMouseEvent.extend({
+ deltaX: function (event) {
+ return 'deltaX' in event ? event.deltaX : // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
+ 'wheelDeltaX' in event ? -event.wheelDeltaX : 0;
+ },
+ deltaY: function (event) {
+ return 'deltaY' in event ? event.deltaY : // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
+ 'wheelDeltaY' in event ? -event.wheelDeltaY : // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
+ 'wheelDelta' in event ? -event.wheelDelta : 0;
+ },
+
+ deltaZ: null,
+
+ // Browsers without "deltaMode" is reporting in raw wheel delta where one
+ // notch on the scroll is always +/- 120, roughly equivalent to pixels.
+ // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
+ // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
+ deltaMode: null
+});
+
+/**
+ * Turns
+ * ['abort', ...]
+ * into
+ * eventTypes = {
+ * 'abort': {
+ * phasedRegistrationNames: {
+ * bubbled: 'onAbort',
+ * captured: 'onAbortCapture',
+ * },
+ * dependencies: [TOP_ABORT],
+ * },
+ * ...
+ * };
+ * topLevelEventsToDispatchConfig = new Map([
+ * [TOP_ABORT, { sameConfig }],
+ * ]);
+ */
+
+var interactiveEventTypeNames = [[TOP_BLUR, 'blur'], [TOP_CANCEL, 'cancel'], [TOP_CLICK, 'click'], [TOP_CLOSE, 'close'], [TOP_CONTEXT_MENU, 'contextMenu'], [TOP_COPY, 'copy'], [TOP_CUT, 'cut'], [TOP_AUX_CLICK, 'auxClick'], [TOP_DOUBLE_CLICK, 'doubleClick'], [TOP_DRAG_END, 'dragEnd'], [TOP_DRAG_START, 'dragStart'], [TOP_DROP, 'drop'], [TOP_FOCUS, 'focus'], [TOP_INPUT, 'input'], [TOP_INVALID, 'invalid'], [TOP_KEY_DOWN, 'keyDown'], [TOP_KEY_PRESS, 'keyPress'], [TOP_KEY_UP, 'keyUp'], [TOP_MOUSE_DOWN, 'mouseDown'], [TOP_MOUSE_UP, 'mouseUp'], [TOP_PASTE, 'paste'], [TOP_PAUSE, 'pause'], [TOP_PLAY, 'play'], [TOP_POINTER_CANCEL, 'pointerCancel'], [TOP_POINTER_DOWN, 'pointerDown'], [TOP_POINTER_UP, 'pointerUp'], [TOP_RATE_CHANGE, 'rateChange'], [TOP_RESET, 'reset'], [TOP_SEEKED, 'seeked'], [TOP_SUBMIT, 'submit'], [TOP_TOUCH_CANCEL, 'touchCancel'], [TOP_TOUCH_END, 'touchEnd'], [TOP_TOUCH_START, 'touchStart'], [TOP_VOLUME_CHANGE, 'volumeChange']];
+var nonInteractiveEventTypeNames = [[TOP_ABORT, 'abort'], [TOP_ANIMATION_END, 'animationEnd'], [TOP_ANIMATION_ITERATION, 'animationIteration'], [TOP_ANIMATION_START, 'animationStart'], [TOP_CAN_PLAY, 'canPlay'], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough'], [TOP_DRAG, 'drag'], [TOP_DRAG_ENTER, 'dragEnter'], [TOP_DRAG_EXIT, 'dragExit'], [TOP_DRAG_LEAVE, 'dragLeave'], [TOP_DRAG_OVER, 'dragOver'], [TOP_DURATION_CHANGE, 'durationChange'], [TOP_EMPTIED, 'emptied'], [TOP_ENCRYPTED, 'encrypted'], [TOP_ENDED, 'ended'], [TOP_ERROR, 'error'], [TOP_GOT_POINTER_CAPTURE, 'gotPointerCapture'], [TOP_LOAD, 'load'], [TOP_LOADED_DATA, 'loadedData'], [TOP_LOADED_METADATA, 'loadedMetadata'], [TOP_LOAD_START, 'loadStart'], [TOP_LOST_POINTER_CAPTURE, 'lostPointerCapture'], [TOP_MOUSE_MOVE, 'mouseMove'], [TOP_MOUSE_OUT, 'mouseOut'], [TOP_MOUSE_OVER, 'mouseOver'], [TOP_PLAYING, 'playing'], [TOP_POINTER_MOVE, 'pointerMove'], [TOP_POINTER_OUT, 'pointerOut'], [TOP_POINTER_OVER, 'pointerOver'], [TOP_PROGRESS, 'progress'], [TOP_SCROLL, 'scroll'], [TOP_SEEKING, 'seeking'], [TOP_STALLED, 'stalled'], [TOP_SUSPEND, 'suspend'], [TOP_TIME_UPDATE, 'timeUpdate'], [TOP_TOGGLE, 'toggle'], [TOP_TOUCH_MOVE, 'touchMove'], [TOP_TRANSITION_END, 'transitionEnd'], [TOP_WAITING, 'waiting'], [TOP_WHEEL, 'wheel']];
+
+var eventTypes$4 = {};
+var topLevelEventsToDispatchConfig = {};
+
+function addEventTypeNameToConfig(_ref, isInteractive) {
+ var topEvent = _ref[0],
+ event = _ref[1];
+
+ var capitalizedEvent = event[0].toUpperCase() + event.slice(1);
+ var onEvent = 'on' + capitalizedEvent;
+
+ var type = {
+ phasedRegistrationNames: {
+ bubbled: onEvent,
+ captured: onEvent + 'Capture'
+ },
+ dependencies: [topEvent],
+ isInteractive: isInteractive
+ };
+ eventTypes$4[event] = type;
+ topLevelEventsToDispatchConfig[topEvent] = type;
+}
+
+interactiveEventTypeNames.forEach(function (eventTuple) {
+ addEventTypeNameToConfig(eventTuple, true);
+});
+nonInteractiveEventTypeNames.forEach(function (eventTuple) {
+ addEventTypeNameToConfig(eventTuple, false);
+});
+
+// Only used in DEV for exhaustiveness validation.
+var knownHTMLTopLevelTypes = [TOP_ABORT, TOP_CANCEL, TOP_CAN_PLAY, TOP_CAN_PLAY_THROUGH, TOP_CLOSE, TOP_DURATION_CHANGE, TOP_EMPTIED, TOP_ENCRYPTED, TOP_ENDED, TOP_ERROR, TOP_INPUT, TOP_INVALID, TOP_LOAD, TOP_LOADED_DATA, TOP_LOADED_METADATA, TOP_LOAD_START, TOP_PAUSE, TOP_PLAY, TOP_PLAYING, TOP_PROGRESS, TOP_RATE_CHANGE, TOP_RESET, TOP_SEEKED, TOP_SEEKING, TOP_STALLED, TOP_SUBMIT, TOP_SUSPEND, TOP_TIME_UPDATE, TOP_TOGGLE, TOP_VOLUME_CHANGE, TOP_WAITING];
+
+var SimpleEventPlugin = {
+ eventTypes: eventTypes$4,
+
+ isInteractiveTopLevelEventType: function (topLevelType) {
+ var config = topLevelEventsToDispatchConfig[topLevelType];
+ return config !== undefined && config.isInteractive === true;
+ },
+
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
+ if (!dispatchConfig) {
+ return null;
+ }
+ var EventConstructor = void 0;
+ switch (topLevelType) {
+ case TOP_KEY_PRESS:
+ // Firefox creates a keypress event for function keys too. This removes
+ // the unwanted keypress events. Enter is however both printable and
+ // non-printable. One would expect Tab to be as well (but it isn't).
+ if (getEventCharCode(nativeEvent) === 0) {
+ return null;
+ }
+ /* falls through */
+ case TOP_KEY_DOWN:
+ case TOP_KEY_UP:
+ EventConstructor = SyntheticKeyboardEvent;
+ break;
+ case TOP_BLUR:
+ case TOP_FOCUS:
+ EventConstructor = SyntheticFocusEvent;
+ break;
+ case TOP_CLICK:
+ // Firefox creates a click event on right mouse clicks. This removes the
+ // unwanted click events.
+ if (nativeEvent.button === 2) {
+ return null;
+ }
+ /* falls through */
+ case TOP_AUX_CLICK:
+ case TOP_DOUBLE_CLICK:
+ case TOP_MOUSE_DOWN:
+ case TOP_MOUSE_MOVE:
+ case TOP_MOUSE_UP:
+ // TODO: Disabled elements should not respond to mouse events
+ /* falls through */
+ case TOP_MOUSE_OUT:
+ case TOP_MOUSE_OVER:
+ case TOP_CONTEXT_MENU:
+ EventConstructor = SyntheticMouseEvent;
+ break;
+ case TOP_DRAG:
+ case TOP_DRAG_END:
+ case TOP_DRAG_ENTER:
+ case TOP_DRAG_EXIT:
+ case TOP_DRAG_LEAVE:
+ case TOP_DRAG_OVER:
+ case TOP_DRAG_START:
+ case TOP_DROP:
+ EventConstructor = SyntheticDragEvent;
+ break;
+ case TOP_TOUCH_CANCEL:
+ case TOP_TOUCH_END:
+ case TOP_TOUCH_MOVE:
+ case TOP_TOUCH_START:
+ EventConstructor = SyntheticTouchEvent;
+ break;
+ case TOP_ANIMATION_END:
+ case TOP_ANIMATION_ITERATION:
+ case TOP_ANIMATION_START:
+ EventConstructor = SyntheticAnimationEvent;
+ break;
+ case TOP_TRANSITION_END:
+ EventConstructor = SyntheticTransitionEvent;
+ break;
+ case TOP_SCROLL:
+ EventConstructor = SyntheticUIEvent;
+ break;
+ case TOP_WHEEL:
+ EventConstructor = SyntheticWheelEvent;
+ break;
+ case TOP_COPY:
+ case TOP_CUT:
+ case TOP_PASTE:
+ EventConstructor = SyntheticClipboardEvent;
+ break;
+ case TOP_GOT_POINTER_CAPTURE:
+ case TOP_LOST_POINTER_CAPTURE:
+ case TOP_POINTER_CANCEL:
+ case TOP_POINTER_DOWN:
+ case TOP_POINTER_MOVE:
+ case TOP_POINTER_OUT:
+ case TOP_POINTER_OVER:
+ case TOP_POINTER_UP:
+ EventConstructor = SyntheticPointerEvent;
+ break;
+ default:
+ {
+ if (knownHTMLTopLevelTypes.indexOf(topLevelType) === -1) {
+ warningWithoutStack$1(false, 'SimpleEventPlugin: Unhandled event type, `%s`. This warning ' + 'is likely caused by a bug in React. Please file an issue.', topLevelType);
+ }
+ }
+ // HTML Events
+ // @see http://www.w3.org/TR/html5/index.html#events-0
+ EventConstructor = SyntheticEvent;
+ break;
+ }
+ var event = EventConstructor.getPooled(dispatchConfig, targetInst, nativeEvent, nativeEventTarget);
+ accumulateTwoPhaseDispatches(event);
+ return event;
+ }
+};
+
+var isInteractiveTopLevelEventType = SimpleEventPlugin.isInteractiveTopLevelEventType;
+
+
+var CALLBACK_BOOKKEEPING_POOL_SIZE = 10;
+var callbackBookkeepingPool = [];
+
+/**
+ * Find the deepest React component completely containing the root of the
+ * passed-in instance (for use when entire React trees are nested within each
+ * other). If React trees are not nested, returns null.
+ */
+function findRootContainerNode(inst) {
+ // TODO: It may be a good idea to cache this to prevent unnecessary DOM
+ // traversal, but caching is difficult to do correctly without using a
+ // mutation observer to listen for all DOM changes.
+ while (inst.return) {
+ inst = inst.return;
+ }
+ if (inst.tag !== HostRoot) {
+ // This can happen if we're in a detached tree.
+ return null;
+ }
+ return inst.stateNode.containerInfo;
+}
+
+// Used to store ancestor hierarchy in top level callback
+function getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst) {
+ if (callbackBookkeepingPool.length) {
+ var instance = callbackBookkeepingPool.pop();
+ instance.topLevelType = topLevelType;
+ instance.nativeEvent = nativeEvent;
+ instance.targetInst = targetInst;
+ return instance;
+ }
+ return {
+ topLevelType: topLevelType,
+ nativeEvent: nativeEvent,
+ targetInst: targetInst,
+ ancestors: []
+ };
+}
+
+function releaseTopLevelCallbackBookKeeping(instance) {
+ instance.topLevelType = null;
+ instance.nativeEvent = null;
+ instance.targetInst = null;
+ instance.ancestors.length = 0;
+ if (callbackBookkeepingPool.length < CALLBACK_BOOKKEEPING_POOL_SIZE) {
+ callbackBookkeepingPool.push(instance);
+ }
+}
+
+function handleTopLevel(bookKeeping) {
+ var targetInst = bookKeeping.targetInst;
+
+ // Loop through the hierarchy, in case there's any nested components.
+ // It's important that we build the array of ancestors before calling any
+ // event handlers, because event handlers can modify the DOM, leading to
+ // inconsistencies with ReactMount's node cache. See #1105.
+ var ancestor = targetInst;
+ do {
+ if (!ancestor) {
+ bookKeeping.ancestors.push(ancestor);
+ break;
+ }
+ var root = findRootContainerNode(ancestor);
+ if (!root) {
+ break;
+ }
+ bookKeeping.ancestors.push(ancestor);
+ ancestor = getClosestInstanceFromNode(root);
+ } while (ancestor);
+
+ for (var i = 0; i < bookKeeping.ancestors.length; i++) {
+ targetInst = bookKeeping.ancestors[i];
+ runExtractedEventsInBatch(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
+ }
+}
+
+// TODO: can we stop exporting these?
+var _enabled = true;
+
+function setEnabled(enabled) {
+ _enabled = !!enabled;
+}
+
+function isEnabled() {
+ return _enabled;
+}
+
+/**
+ * Traps top-level events by using event bubbling.
+ *
+ * @param {number} topLevelType Number from `TopLevelEventTypes`.
+ * @param {object} element Element on which to attach listener.
+ * @return {?object} An object with a remove function which will forcefully
+ * remove the listener.
+ * @internal
+ */
+function trapBubbledEvent(topLevelType, element) {
+ if (!element) {
+ return null;
+ }
+ var dispatch = isInteractiveTopLevelEventType(topLevelType) ? dispatchInteractiveEvent : dispatchEvent;
+
+ addEventBubbleListener(element, getRawEventName(topLevelType),
+ // Check if interactive and wrap in interactiveUpdates
+ dispatch.bind(null, topLevelType));
+}
+
+/**
+ * Traps a top-level event by using event capturing.
+ *
+ * @param {number} topLevelType Number from `TopLevelEventTypes`.
+ * @param {object} element Element on which to attach listener.
+ * @return {?object} An object with a remove function which will forcefully
+ * remove the listener.
+ * @internal
+ */
+function trapCapturedEvent(topLevelType, element) {
+ if (!element) {
+ return null;
+ }
+ var dispatch = isInteractiveTopLevelEventType(topLevelType) ? dispatchInteractiveEvent : dispatchEvent;
+
+ addEventCaptureListener(element, getRawEventName(topLevelType),
+ // Check if interactive and wrap in interactiveUpdates
+ dispatch.bind(null, topLevelType));
+}
+
+function dispatchInteractiveEvent(topLevelType, nativeEvent) {
+ interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
+}
+
+function dispatchEvent(topLevelType, nativeEvent) {
+ if (!_enabled) {
+ return;
+ }
+
+ var nativeEventTarget = getEventTarget(nativeEvent);
+ var targetInst = getClosestInstanceFromNode(nativeEventTarget);
+ if (targetInst !== null && typeof targetInst.tag === 'number' && !isFiberMounted(targetInst)) {
+ // If we get an event (ex: img onload) before committing that
+ // component's mount, ignore it for now (that is, treat it as if it was an
+ // event on a non-React tree). We might also consider queueing events and
+ // dispatching them after the mount.
+ targetInst = null;
+ }
+
+ var bookKeeping = getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst);
+
+ try {
+ // Event queue being processed in the same cycle allows
+ // `preventDefault`.
+ batchedUpdates(handleTopLevel, bookKeeping);
+ } finally {
+ releaseTopLevelCallbackBookKeeping(bookKeeping);
+ }
+}
+
+/**
+ * Summary of `ReactBrowserEventEmitter` event handling:
+ *
+ * - Top-level delegation is used to trap most native browser events. This
+ * may only occur in the main thread and is the responsibility of
+ * ReactDOMEventListener, which is injected and can therefore support
+ * pluggable event sources. This is the only work that occurs in the main
+ * thread.
+ *
+ * - We normalize and de-duplicate events to account for browser quirks. This
+ * may be done in the worker thread.
+ *
+ * - Forward these native events (with the associated top-level type used to
+ * trap it) to `EventPluginHub`, which in turn will ask plugins if they want
+ * to extract any synthetic events.
+ *
+ * - The `EventPluginHub` will then process each event by annotating them with
+ * "dispatches", a sequence of listeners and IDs that care about that event.
+ *
+ * - The `EventPluginHub` then dispatches the events.
+ *
+ * Overview of React and the event system:
+ *
+ * +------------+ .
+ * | DOM | .
+ * +------------+ .
+ * | .
+ * v .
+ * +------------+ .
+ * | ReactEvent | .
+ * | Listener | .
+ * +------------+ . +-----------+
+ * | . +--------+|SimpleEvent|
+ * | . | |Plugin |
+ * +-----|------+ . v +-----------+
+ * | | | . +--------------+ +------------+
+ * | +-----------.--->|EventPluginHub| | Event |
+ * | | . | | +-----------+ | Propagators|
+ * | ReactEvent | . | | |TapEvent | |------------|
+ * | Emitter | . | |<---+|Plugin | |other plugin|
+ * | | . | | +-----------+ | utilities |
+ * | +-----------.--->| | +------------+
+ * | | | . +--------------+
+ * +-----|------+ . ^ +-----------+
+ * | . | |Enter/Leave|
+ * + . +-------+|Plugin |
+ * +-------------+ . +-----------+
+ * | application | .
+ * |-------------| .
+ * | | .
+ * | | .
+ * +-------------+ .
+ * .
+ * React Core . General Purpose Event Plugin System
+ */
+
+var alreadyListeningTo = {};
+var reactTopListenersCounter = 0;
+
+/**
+ * To ensure no conflicts with other potential React instances on the page
+ */
+var topListenersIDKey = '_reactListenersID' + ('' + Math.random()).slice(2);
+
+function getListeningForDocument(mountAt) {
+ // In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
+ // directly.
+ if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
+ mountAt[topListenersIDKey] = reactTopListenersCounter++;
+ alreadyListeningTo[mountAt[topListenersIDKey]] = {};
+ }
+ return alreadyListeningTo[mountAt[topListenersIDKey]];
+}
+
+/**
+ * We listen for bubbled touch events on the document object.
+ *
+ * Firefox v8.01 (and possibly others) exhibited strange behavior when
+ * mounting `onmousemove` events at some node that was not the document
+ * element. The symptoms were that if your mouse is not moving over something
+ * contained within that mount point (for example on the background) the
+ * top-level listeners for `onmousemove` won't be called. However, if you
+ * register the `mousemove` on the document object, then it will of course
+ * catch all `mousemove`s. This along with iOS quirks, justifies restricting
+ * top-level listeners to the document object only, at least for these
+ * movement types of events and possibly all events.
+ *
+ * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
+ *
+ * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
+ * they bubble to document.
+ *
+ * @param {string} registrationName Name of listener (e.g. `onClick`).
+ * @param {object} mountAt Container where to mount the listener
+ */
+function listenTo(registrationName, mountAt) {
+ var isListening = getListeningForDocument(mountAt);
+ var dependencies = registrationNameDependencies[registrationName];
+
+ for (var i = 0; i < dependencies.length; i++) {
+ var dependency = dependencies[i];
+ if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
+ switch (dependency) {
+ case TOP_SCROLL:
+ trapCapturedEvent(TOP_SCROLL, mountAt);
+ break;
+ case TOP_FOCUS:
+ case TOP_BLUR:
+ trapCapturedEvent(TOP_FOCUS, mountAt);
+ trapCapturedEvent(TOP_BLUR, mountAt);
+ // We set the flag for a single dependency later in this function,
+ // but this ensures we mark both as attached rather than just one.
+ isListening[TOP_BLUR] = true;
+ isListening[TOP_FOCUS] = true;
+ break;
+ case TOP_CANCEL:
+ case TOP_CLOSE:
+ if (isEventSupported(getRawEventName(dependency))) {
+ trapCapturedEvent(dependency, mountAt);
+ }
+ break;
+ case TOP_INVALID:
+ case TOP_SUBMIT:
+ case TOP_RESET:
+ // We listen to them on the target DOM elements.
+ // Some of them bubble so we don't want them to fire twice.
+ break;
+ default:
+ // By default, listen on the top level to all non-media events.
+ // Media events don't bubble so adding the listener wouldn't do anything.
+ var isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;
+ if (!isMediaEvent) {
+ trapBubbledEvent(dependency, mountAt);
+ }
+ break;
+ }
+ isListening[dependency] = true;
+ }
+ }
+}
+
+function isListeningToAllDependencies(registrationName, mountAt) {
+ var isListening = getListeningForDocument(mountAt);
+ var dependencies = registrationNameDependencies[registrationName];
+ for (var i = 0; i < dependencies.length; i++) {
+ var dependency = dependencies[i];
+ if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function getActiveElement(doc) {
+ doc = doc || (typeof document !== 'undefined' ? document : undefined);
+ if (typeof doc === 'undefined') {
+ return null;
+ }
+ try {
+ return doc.activeElement || doc.body;
+ } catch (e) {
+ return doc.body;
+ }
+}
+
+/**
+ * Given any node return the first leaf node without children.
+ *
+ * @param {DOMElement|DOMTextNode} node
+ * @return {DOMElement|DOMTextNode}
+ */
+function getLeafNode(node) {
+ while (node && node.firstChild) {
+ node = node.firstChild;
+ }
+ return node;
+}
+
+/**
+ * Get the next sibling within a container. This will walk up the
+ * DOM if a node's siblings have been exhausted.
+ *
+ * @param {DOMElement|DOMTextNode} node
+ * @return {?DOMElement|DOMTextNode}
+ */
+function getSiblingNode(node) {
+ while (node) {
+ if (node.nextSibling) {
+ return node.nextSibling;
+ }
+ node = node.parentNode;
+ }
+}
+
+/**
+ * Get object describing the nodes which contain characters at offset.
+ *
+ * @param {DOMElement|DOMTextNode} root
+ * @param {number} offset
+ * @return {?object}
+ */
+function getNodeForCharacterOffset(root, offset) {
+ var node = getLeafNode(root);
+ var nodeStart = 0;
+ var nodeEnd = 0;
+
+ while (node) {
+ if (node.nodeType === TEXT_NODE) {
+ nodeEnd = nodeStart + node.textContent.length;
+
+ if (nodeStart <= offset && nodeEnd >= offset) {
+ return {
+ node: node,
+ offset: offset - nodeStart
+ };
+ }
+
+ nodeStart = nodeEnd;
+ }
+
+ node = getLeafNode(getSiblingNode(node));
+ }
+}
+
+/**
+ * @param {DOMElement} outerNode
+ * @return {?object}
+ */
+function getOffsets(outerNode) {
+ var ownerDocument = outerNode.ownerDocument;
+
+ var win = ownerDocument && ownerDocument.defaultView || window;
+ var selection = win.getSelection && win.getSelection();
+
+ if (!selection || selection.rangeCount === 0) {
+ return null;
+ }
+
+ var anchorNode = selection.anchorNode,
+ anchorOffset = selection.anchorOffset,
+ focusNode = selection.focusNode,
+ focusOffset = selection.focusOffset;
+
+ // In Firefox, anchorNode and focusNode can be "anonymous divs", e.g. the
+ // up/down buttons on an <input type="number">. Anonymous divs do not seem to
+ // expose properties, triggering a "Permission denied error" if any of its
+ // properties are accessed. The only seemingly possible way to avoid erroring
+ // is to access a property that typically works for non-anonymous divs and
+ // catch any error that may otherwise arise. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=208427
+
+ try {
+ /* eslint-disable no-unused-expressions */
+ anchorNode.nodeType;
+ focusNode.nodeType;
+ /* eslint-enable no-unused-expressions */
+ } catch (e) {
+ return null;
+ }
+
+ return getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset);
+}
+
+/**
+ * Returns {start, end} where `start` is the character/codepoint index of
+ * (anchorNode, anchorOffset) within the textContent of `outerNode`, and
+ * `end` is the index of (focusNode, focusOffset).
+ *
+ * Returns null if you pass in garbage input but we should probably just crash.
+ *
+ * Exported only for testing.
+ */
+function getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset) {
+ var length = 0;
+ var start = -1;
+ var end = -1;
+ var indexWithinAnchor = 0;
+ var indexWithinFocus = 0;
+ var node = outerNode;
+ var parentNode = null;
+
+ outer: while (true) {
+ var next = null;
+
+ while (true) {
+ if (node === anchorNode && (anchorOffset === 0 || node.nodeType === TEXT_NODE)) {
+ start = length + anchorOffset;
+ }
+ if (node === focusNode && (focusOffset === 0 || node.nodeType === TEXT_NODE)) {
+ end = length + focusOffset;
+ }
+
+ if (node.nodeType === TEXT_NODE) {
+ length += node.nodeValue.length;
+ }
+
+ if ((next = node.firstChild) === null) {
+ break;
+ }
+ // Moving from `node` to its first child `next`.
+ parentNode = node;
+ node = next;
+ }
+
+ while (true) {
+ if (node === outerNode) {
+ // If `outerNode` has children, this is always the second time visiting
+ // it. If it has no children, this is still the first loop, and the only
+ // valid selection is anchorNode and focusNode both equal to this node
+ // and both offsets 0, in which case we will have handled above.
+ break outer;
+ }
+ if (parentNode === anchorNode && ++indexWithinAnchor === anchorOffset) {
+ start = length;
+ }
+ if (parentNode === focusNode && ++indexWithinFocus === focusOffset) {
+ end = length;
+ }
+ if ((next = node.nextSibling) !== null) {
+ break;
+ }
+ node = parentNode;
+ parentNode = node.parentNode;
+ }
+
+ // Moving from `node` to its next sibling `next`.
+ node = next;
+ }
+
+ if (start === -1 || end === -1) {
+ // This should never happen. (Would happen if the anchor/focus nodes aren't
+ // actually inside the passed-in node.)
+ return null;
+ }
+
+ return {
+ start: start,
+ end: end
+ };
+}
+
+/**
+ * In modern non-IE browsers, we can support both forward and backward
+ * selections.
+ *
+ * Note: IE10+ supports the Selection object, but it does not support
+ * the `extend` method, which means that even in modern IE, it's not possible
+ * to programmatically create a backward selection. Thus, for all IE
+ * versions, we use the old IE API to create our selections.
+ *
+ * @param {DOMElement|DOMTextNode} node
+ * @param {object} offsets
+ */
+function setOffsets(node, offsets) {
+ var doc = node.ownerDocument || document;
+ var win = doc && doc.defaultView || window;
+
+ // Edge fails with "Object expected" in some scenarios.
+ // (For instance: TinyMCE editor used in a list component that supports pasting to add more,
+ // fails when pasting 100+ items)
+ if (!win.getSelection) {
+ return;
+ }
+
+ var selection = win.getSelection();
+ var length = node.textContent.length;
+ var start = Math.min(offsets.start, length);
+ var end = offsets.end === undefined ? start : Math.min(offsets.end, length);
+
+ // IE 11 uses modern selection, but doesn't support the extend method.
+ // Flip backward selections, so we can set with a single range.
+ if (!selection.extend && start > end) {
+ var temp = end;
+ end = start;
+ start = temp;
+ }
+
+ var startMarker = getNodeForCharacterOffset(node, start);
+ var endMarker = getNodeForCharacterOffset(node, end);
+
+ if (startMarker && endMarker) {
+ if (selection.rangeCount === 1 && selection.anchorNode === startMarker.node && selection.anchorOffset === startMarker.offset && selection.focusNode === endMarker.node && selection.focusOffset === endMarker.offset) {
+ return;
+ }
+ var range = doc.createRange();
+ range.setStart(startMarker.node, startMarker.offset);
+ selection.removeAllRanges();
+
+ if (start > end) {
+ selection.addRange(range);
+ selection.extend(endMarker.node, endMarker.offset);
+ } else {
+ range.setEnd(endMarker.node, endMarker.offset);
+ selection.addRange(range);
+ }
+ }
+}
+
+function isTextNode(node) {
+ return node && node.nodeType === TEXT_NODE;
+}
+
+function containsNode(outerNode, innerNode) {
+ if (!outerNode || !innerNode) {
+ return false;
+ } else if (outerNode === innerNode) {
+ return true;
+ } else if (isTextNode(outerNode)) {
+ return false;
+ } else if (isTextNode(innerNode)) {
+ return containsNode(outerNode, innerNode.parentNode);
+ } else if ('contains' in outerNode) {
+ return outerNode.contains(innerNode);
+ } else if (outerNode.compareDocumentPosition) {
+ return !!(outerNode.compareDocumentPosition(innerNode) & 16);
+ } else {
+ return false;
+ }
+}
+
+function isInDocument(node) {
+ return node && node.ownerDocument && containsNode(node.ownerDocument.documentElement, node);
+}
+
+function isSameOriginFrame(iframe) {
+ try {
+ // Accessing the contentDocument of a HTMLIframeElement can cause the browser
+ // to throw, e.g. if it has a cross-origin src attribute.
+ // Safari will show an error in the console when the access results in "Blocked a frame with origin". e.g:
+ // iframe.contentDocument.defaultView;
+ // A safety way is to access one of the cross origin properties: Window or Location
+ // Which might result in "SecurityError" DOM Exception and it is compatible to Safari.
+ // https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl
+
+ return typeof iframe.contentWindow.location.href === 'string';
+ } catch (err) {
+ return false;
+ }
+}
+
+function getActiveElementDeep() {
+ var win = window;
+ var element = getActiveElement();
+ while (element instanceof win.HTMLIFrameElement) {
+ if (isSameOriginFrame(element)) {
+ win = element.contentWindow;
+ } else {
+ return element;
+ }
+ element = getActiveElement(win.document);
+ }
+ return element;
+}
+
+/**
+ * @ReactInputSelection: React input selection module. Based on Selection.js,
+ * but modified to be suitable for react and has a couple of bug fixes (doesn't
+ * assume buttons have range selections allowed).
+ * Input selection module for React.
+ */
+
+/**
+ * @hasSelectionCapabilities: we get the element types that support selection
+ * from https://html.spec.whatwg.org/#do-not-apply, looking at `selectionStart`
+ * and `selectionEnd` rows.
+ */
+function hasSelectionCapabilities(elem) {
+ var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
+ return nodeName && (nodeName === 'input' && (elem.type === 'text' || elem.type === 'search' || elem.type === 'tel' || elem.type === 'url' || elem.type === 'password') || nodeName === 'textarea' || elem.contentEditable === 'true');
+}
+
+function getSelectionInformation() {
+ var focusedElem = getActiveElementDeep();
+ return {
+ focusedElem: focusedElem,
+ selectionRange: hasSelectionCapabilities(focusedElem) ? getSelection$1(focusedElem) : null
+ };
+}
+
+/**
+ * @restoreSelection: If any selection information was potentially lost,
+ * restore it. This is useful when performing operations that could remove dom
+ * nodes and place them back in, resulting in focus being lost.
+ */
+function restoreSelection(priorSelectionInformation) {
+ var curFocusedElem = getActiveElementDeep();
+ var priorFocusedElem = priorSelectionInformation.focusedElem;
+ var priorSelectionRange = priorSelectionInformation.selectionRange;
+ if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
+ if (priorSelectionRange !== null && hasSelectionCapabilities(priorFocusedElem)) {
+ setSelection(priorFocusedElem, priorSelectionRange);
+ }
+
+ // Focusing a node can change the scroll position, which is undesirable
+ var ancestors = [];
+ var ancestor = priorFocusedElem;
+ while (ancestor = ancestor.parentNode) {
+ if (ancestor.nodeType === ELEMENT_NODE) {
+ ancestors.push({
+ element: ancestor,
+ left: ancestor.scrollLeft,
+ top: ancestor.scrollTop
+ });
+ }
+ }
+
+ if (typeof priorFocusedElem.focus === 'function') {
+ priorFocusedElem.focus();
+ }
+
+ for (var i = 0; i < ancestors.length; i++) {
+ var info = ancestors[i];
+ info.element.scrollLeft = info.left;
+ info.element.scrollTop = info.top;
+ }
+ }
+}
+
+/**
+ * @getSelection: Gets the selection bounds of a focused textarea, input or
+ * contentEditable node.
+ * -@input: Look up selection bounds of this input
+ * -@return {start: selectionStart, end: selectionEnd}
+ */
+function getSelection$1(input) {
+ var selection = void 0;
+
+ if ('selectionStart' in input) {
+ // Modern browser with input or textarea.
+ selection = {
+ start: input.selectionStart,
+ end: input.selectionEnd
+ };
+ } else {
+ // Content editable or old IE textarea.
+ selection = getOffsets(input);
+ }
+
+ return selection || { start: 0, end: 0 };
+}
+
+/**
+ * @setSelection: Sets the selection bounds of a textarea or input and focuses
+ * the input.
+ * -@input Set selection bounds of this input or textarea
+ * -@offsets Object of same form that is returned from get*
+ */
+function setSelection(input, offsets) {
+ var start = offsets.start,
+ end = offsets.end;
+
+ if (end === undefined) {
+ end = start;
+ }
+
+ if ('selectionStart' in input) {
+ input.selectionStart = start;
+ input.selectionEnd = Math.min(end, input.value.length);
+ } else {
+ setOffsets(input, offsets);
+ }
+}
+
+var skipSelectionChangeEvent = canUseDOM && 'documentMode' in document && document.documentMode <= 11;
+
+var eventTypes$3 = {
+ select: {
+ phasedRegistrationNames: {
+ bubbled: 'onSelect',
+ captured: 'onSelectCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_CONTEXT_MENU, TOP_DRAG_END, TOP_FOCUS, TOP_KEY_DOWN, TOP_KEY_UP, TOP_MOUSE_DOWN, TOP_MOUSE_UP, TOP_SELECTION_CHANGE]
+ }
+};
+
+var activeElement$1 = null;
+var activeElementInst$1 = null;
+var lastSelection = null;
+var mouseDown = false;
+
+/**
+ * Get an object which is a unique representation of the current selection.
+ *
+ * The return value will not be consistent across nodes or browsers, but
+ * two identical selections on the same node will return identical objects.
+ *
+ * @param {DOMElement} node
+ * @return {object}
+ */
+function getSelection(node) {
+ if ('selectionStart' in node && hasSelectionCapabilities(node)) {
+ return {
+ start: node.selectionStart,
+ end: node.selectionEnd
+ };
+ } else {
+ var win = node.ownerDocument && node.ownerDocument.defaultView || window;
+ var selection = win.getSelection();
+ return {
+ anchorNode: selection.anchorNode,
+ anchorOffset: selection.anchorOffset,
+ focusNode: selection.focusNode,
+ focusOffset: selection.focusOffset
+ };
+ }
+}
+
+/**
+ * Get document associated with the event target.
+ *
+ * @param {object} nativeEventTarget
+ * @return {Document}
+ */
+function getEventTargetDocument(eventTarget) {
+ return eventTarget.window === eventTarget ? eventTarget.document : eventTarget.nodeType === DOCUMENT_NODE ? eventTarget : eventTarget.ownerDocument;
+}
+
+/**
+ * Poll selection to see whether it's changed.
+ *
+ * @param {object} nativeEvent
+ * @param {object} nativeEventTarget
+ * @return {?SyntheticEvent}
+ */
+function constructSelectEvent(nativeEvent, nativeEventTarget) {
+ // Ensure we have the right element, and that the user is not dragging a
+ // selection (this matches native `select` event behavior). In HTML5, select
+ // fires only on input and textarea thus if there's no focused element we
+ // won't dispatch.
+ var doc = getEventTargetDocument(nativeEventTarget);
+
+ if (mouseDown || activeElement$1 == null || activeElement$1 !== getActiveElement(doc)) {
+ return null;
+ }
+
+ // Only fire when selection has actually changed.
+ var currentSelection = getSelection(activeElement$1);
+ if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
+ lastSelection = currentSelection;
+
+ var syntheticEvent = SyntheticEvent.getPooled(eventTypes$3.select, activeElementInst$1, nativeEvent, nativeEventTarget);
+
+ syntheticEvent.type = 'select';
+ syntheticEvent.target = activeElement$1;
+
+ accumulateTwoPhaseDispatches(syntheticEvent);
+
+ return syntheticEvent;
+ }
+
+ return null;
+}
+
+/**
+ * This plugin creates an `onSelect` event that normalizes select events
+ * across form elements.
+ *
+ * Supported elements are:
+ * - input (see `isTextInputElement`)
+ * - textarea
+ * - contentEditable
+ *
+ * This differs from native browser implementations in the following ways:
+ * - Fires on contentEditable fields as well as inputs.
+ * - Fires for collapsed selection.
+ * - Fires after user input.
+ */
+var SelectEventPlugin = {
+ eventTypes: eventTypes$3,
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var doc = getEventTargetDocument(nativeEventTarget);
+ // Track whether all listeners exists for this plugin. If none exist, we do
+ // not extract events. See #3639.
+ if (!doc || !isListeningToAllDependencies('onSelect', doc)) {
+ return null;
+ }
+
+ var targetNode = targetInst ? getNodeFromInstance$1(targetInst) : window;
+
+ switch (topLevelType) {
+ // Track the input node that has focus.
+ case TOP_FOCUS:
+ if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') {
+ activeElement$1 = targetNode;
+ activeElementInst$1 = targetInst;
+ lastSelection = null;
+ }
+ break;
+ case TOP_BLUR:
+ activeElement$1 = null;
+ activeElementInst$1 = null;
+ lastSelection = null;
+ break;
+ // Don't fire the event while the user is dragging. This matches the
+ // semantics of the native select event.
+ case TOP_MOUSE_DOWN:
+ mouseDown = true;
+ break;
+ case TOP_CONTEXT_MENU:
+ case TOP_MOUSE_UP:
+ case TOP_DRAG_END:
+ mouseDown = false;
+ return constructSelectEvent(nativeEvent, nativeEventTarget);
+ // Chrome and IE fire non-standard event when selection is changed (and
+ // sometimes when it hasn't). IE's event fires out of order with respect
+ // to key and input events on deletion, so we discard it.
+ //
+ // Firefox doesn't support selectionchange, so check selection status
+ // after each key entry. The selection changes after keydown and before
+ // keyup, but we check on keydown as well in the case of holding down a
+ // key, when multiple keydown events are fired but only one keyup is.
+ // This is also our approach for IE handling, for the reason above.
+ case TOP_SELECTION_CHANGE:
+ if (skipSelectionChangeEvent) {
+ break;
+ }
+ // falls through
+ case TOP_KEY_DOWN:
+ case TOP_KEY_UP:
+ return constructSelectEvent(nativeEvent, nativeEventTarget);
+ }
+
+ return null;
+ }
+};
+
+/**
+ * Inject modules for resolving DOM hierarchy and plugin ordering.
+ */
+injection.injectEventPluginOrder(DOMEventPluginOrder);
+setComponentTree(getFiberCurrentPropsFromNode$1, getInstanceFromNode$1, getNodeFromInstance$1);
+
+/**
+ * Some important event plugins included by default (without having to require
+ * them).
+ */
+injection.injectEventPluginsByName({
+ SimpleEventPlugin: SimpleEventPlugin,
+ EnterLeaveEventPlugin: EnterLeaveEventPlugin,
+ ChangeEventPlugin: ChangeEventPlugin,
+ SelectEventPlugin: SelectEventPlugin,
+ BeforeInputEventPlugin: BeforeInputEventPlugin
+});
+
+var didWarnSelectedSetOnOption = false;
+var didWarnInvalidChild = false;
+
+function flattenChildren(children) {
+ var content = '';
+
+ // Flatten children. We'll warn if they are invalid
+ // during validateProps() which runs for hydration too.
+ // Note that this would throw on non-element objects.
+ // Elements are stringified (which is normally irrelevant
+ // but matters for <fbt>).
+ React.Children.forEach(children, function (child) {
+ if (child == null) {
+ return;
+ }
+ content += child;
+ // Note: we don't warn about invalid children here.
+ // Instead, this is done separately below so that
+ // it happens during the hydration codepath too.
+ });
+
+ return content;
+}
+
+/**
+ * Implements an <option> host component that warns when `selected` is set.
+ */
+
+function validateProps(element, props) {
+ {
+ // This mirrors the codepath above, but runs for hydration too.
+ // Warn about invalid children here so that client and hydration are consistent.
+ // TODO: this seems like it could cause a DEV-only throw for hydration
+ // if children contains a non-element object. We should try to avoid that.
+ if (typeof props.children === 'object' && props.children !== null) {
+ React.Children.forEach(props.children, function (child) {
+ if (child == null) {
+ return;
+ }
+ if (typeof child === 'string' || typeof child === 'number') {
+ return;
+ }
+ if (typeof child.type !== 'string') {
+ return;
+ }
+ if (!didWarnInvalidChild) {
+ didWarnInvalidChild = true;
+ warning$1(false, 'Only strings and numbers are supported as <option> children.');
+ }
+ });
+ }
+
+ // TODO: Remove support for `selected` in <option>.
+ if (props.selected != null && !didWarnSelectedSetOnOption) {
+ warning$1(false, 'Use the `defaultValue` or `value` props on <select> instead of ' + 'setting `selected` on <option>.');
+ didWarnSelectedSetOnOption = true;
+ }
+ }
+}
+
+function postMountWrapper$1(element, props) {
+ // value="" should make a value attribute (#6219)
+ if (props.value != null) {
+ element.setAttribute('value', toString(getToStringValue(props.value)));
+ }
+}
+
+function getHostProps$1(element, props) {
+ var hostProps = _assign({ children: undefined }, props);
+ var content = flattenChildren(props.children);
+
+ if (content) {
+ hostProps.children = content;
+ }
+
+ return hostProps;
+}
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+var didWarnValueDefaultValue$1 = void 0;
+
+{
+ didWarnValueDefaultValue$1 = false;
+}
+
+function getDeclarationErrorAddendum() {
+ var ownerName = getCurrentFiberOwnerNameInDevOrNull();
+ if (ownerName) {
+ return '\n\nCheck the render method of `' + ownerName + '`.';
+ }
+ return '';
+}
+
+var valuePropNames = ['value', 'defaultValue'];
+
+/**
+ * Validation function for `value` and `defaultValue`.
+ */
+function checkSelectPropTypes(props) {
+ ReactControlledValuePropTypes.checkPropTypes('select', props);
+
+ for (var i = 0; i < valuePropNames.length; i++) {
+ var propName = valuePropNames[i];
+ if (props[propName] == null) {
+ continue;
+ }
+ var isArray = Array.isArray(props[propName]);
+ if (props.multiple && !isArray) {
+ warning$1(false, 'The `%s` prop supplied to <select> must be an array if ' + '`multiple` is true.%s', propName, getDeclarationErrorAddendum());
+ } else if (!props.multiple && isArray) {
+ warning$1(false, 'The `%s` prop supplied to <select> must be a scalar ' + 'value if `multiple` is false.%s', propName, getDeclarationErrorAddendum());
+ }
+ }
+}
+
+function updateOptions(node, multiple, propValue, setDefaultSelected) {
+ var options = node.options;
+
+ if (multiple) {
+ var selectedValues = propValue;
+ var selectedValue = {};
+ for (var i = 0; i < selectedValues.length; i++) {
+ // Prefix to avoid chaos with special keys.
+ selectedValue['$' + selectedValues[i]] = true;
+ }
+ for (var _i = 0; _i < options.length; _i++) {
+ var selected = selectedValue.hasOwnProperty('$' + options[_i].value);
+ if (options[_i].selected !== selected) {
+ options[_i].selected = selected;
+ }
+ if (selected && setDefaultSelected) {
+ options[_i].defaultSelected = true;
+ }
+ }
+ } else {
+ // Do not set `select.value` as exact behavior isn't consistent across all
+ // browsers for all cases.
+ var _selectedValue = toString(getToStringValue(propValue));
+ var defaultSelected = null;
+ for (var _i2 = 0; _i2 < options.length; _i2++) {
+ if (options[_i2].value === _selectedValue) {
+ options[_i2].selected = true;
+ if (setDefaultSelected) {
+ options[_i2].defaultSelected = true;
+ }
+ return;
+ }
+ if (defaultSelected === null && !options[_i2].disabled) {
+ defaultSelected = options[_i2];
+ }
+ }
+ if (defaultSelected !== null) {
+ defaultSelected.selected = true;
+ }
+ }
+}
+
+/**
+ * Implements a <select> host component that allows optionally setting the
+ * props `value` and `defaultValue`. If `multiple` is false, the prop must be a
+ * stringable. If `multiple` is true, the prop must be an array of stringables.
+ *
+ * If `value` is not supplied (or null/undefined), user actions that change the
+ * selected option will trigger updates to the rendered options.
+ *
+ * If it is supplied (and not null/undefined), the rendered options will not
+ * update in response to user actions. Instead, the `value` prop must change in
+ * order for the rendered options to update.
+ *
+ * If `defaultValue` is provided, any options with the supplied values will be
+ * selected.
+ */
+
+function getHostProps$2(element, props) {
+ return _assign({}, props, {
+ value: undefined
+ });
+}
+
+function initWrapperState$1(element, props) {
+ var node = element;
+ {
+ checkSelectPropTypes(props);
+ }
+
+ node._wrapperState = {
+ wasMultiple: !!props.multiple
+ };
+
+ {
+ if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue$1) {
+ warning$1(false, 'Select elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled select ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components');
+ didWarnValueDefaultValue$1 = true;
+ }
+ }
+}
+
+function postMountWrapper$2(element, props) {
+ var node = element;
+ node.multiple = !!props.multiple;
+ var value = props.value;
+ if (value != null) {
+ updateOptions(node, !!props.multiple, value, false);
+ } else if (props.defaultValue != null) {
+ updateOptions(node, !!props.multiple, props.defaultValue, true);
+ }
+}
+
+function postUpdateWrapper(element, props) {
+ var node = element;
+ var wasMultiple = node._wrapperState.wasMultiple;
+ node._wrapperState.wasMultiple = !!props.multiple;
+
+ var value = props.value;
+ if (value != null) {
+ updateOptions(node, !!props.multiple, value, false);
+ } else if (wasMultiple !== !!props.multiple) {
+ // For simplicity, reapply `defaultValue` if `multiple` is toggled.
+ if (props.defaultValue != null) {
+ updateOptions(node, !!props.multiple, props.defaultValue, true);
+ } else {
+ // Revert the select back to its default unselected state.
+ updateOptions(node, !!props.multiple, props.multiple ? [] : '', false);
+ }
+ }
+}
+
+function restoreControlledState$2(element, props) {
+ var node = element;
+ var value = props.value;
+
+ if (value != null) {
+ updateOptions(node, !!props.multiple, value, false);
+ }
+}
+
+var didWarnValDefaultVal = false;
+
+/**
+ * Implements a <textarea> host component that allows setting `value`, and
+ * `defaultValue`. This differs from the traditional DOM API because value is
+ * usually set as PCDATA children.
+ *
+ * If `value` is not supplied (or null/undefined), user actions that affect the
+ * value will trigger updates to the element.
+ *
+ * If `value` is supplied (and not null/undefined), the rendered element will
+ * not trigger updates to the element. Instead, the `value` prop must change in
+ * order for the rendered element to be updated.
+ *
+ * The rendered element will be initialized with an empty value, the prop
+ * `defaultValue` if specified, or the children content (deprecated).
+ */
+
+function getHostProps$3(element, props) {
+ var node = element;
+ !(props.dangerouslySetInnerHTML == null) ? invariant(false, '`dangerouslySetInnerHTML` does not make sense on <textarea>.') : void 0;
+
+ // Always set children to the same thing. In IE9, the selection range will
+ // get reset if `textContent` is mutated. We could add a check in setTextContent
+ // to only set the value if/when the value differs from the node value (which would
+ // completely solve this IE9 bug), but Sebastian+Sophie seemed to like this
+ // solution. The value can be a boolean or object so that's why it's forced
+ // to be a string.
+ var hostProps = _assign({}, props, {
+ value: undefined,
+ defaultValue: undefined,
+ children: toString(node._wrapperState.initialValue)
+ });
+
+ return hostProps;
+}
+
+function initWrapperState$2(element, props) {
+ var node = element;
+ {
+ ReactControlledValuePropTypes.checkPropTypes('textarea', props);
+ if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValDefaultVal) {
+ warning$1(false, '%s contains a textarea with both value and defaultValue props. ' + 'Textarea elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled textarea ' + 'and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', getCurrentFiberOwnerNameInDevOrNull() || 'A component');
+ didWarnValDefaultVal = true;
+ }
+ }
+
+ var initialValue = props.value;
+
+ // Only bother fetching default value if we're going to use it
+ if (initialValue == null) {
+ var defaultValue = props.defaultValue;
+ // TODO (yungsters): Remove support for children content in <textarea>.
+ var children = props.children;
+ if (children != null) {
+ {
+ warning$1(false, 'Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.');
+ }
+ !(defaultValue == null) ? invariant(false, 'If you supply `defaultValue` on a <textarea>, do not pass children.') : void 0;
+ if (Array.isArray(children)) {
+ !(children.length <= 1) ? invariant(false, '<textarea> can only have at most one child.') : void 0;
+ children = children[0];
+ }
+
+ defaultValue = children;
+ }
+ if (defaultValue == null) {
+ defaultValue = '';
+ }
+ initialValue = defaultValue;
+ }
+
+ node._wrapperState = {
+ initialValue: getToStringValue(initialValue)
+ };
+}
+
+function updateWrapper$1(element, props) {
+ var node = element;
+ var value = getToStringValue(props.value);
+ var defaultValue = getToStringValue(props.defaultValue);
+ if (value != null) {
+ // Cast `value` to a string to ensure the value is set correctly. While
+ // browsers typically do this as necessary, jsdom doesn't.
+ var newValue = toString(value);
+ // To avoid side effects (such as losing text selection), only set value if changed
+ if (newValue !== node.value) {
+ node.value = newValue;
+ }
+ if (props.defaultValue == null && node.defaultValue !== newValue) {
+ node.defaultValue = newValue;
+ }
+ }
+ if (defaultValue != null) {
+ node.defaultValue = toString(defaultValue);
+ }
+}
+
+function postMountWrapper$3(element, props) {
+ var node = element;
+ // This is in postMount because we need access to the DOM node, which is not
+ // available until after the component has mounted.
+ var textContent = node.textContent;
+
+ // Only set node.value if textContent is equal to the expected
+ // initial value. In IE10/IE11 there is a bug where the placeholder attribute
+ // will populate textContent as well.
+ // https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
+ if (textContent === node._wrapperState.initialValue) {
+ node.value = textContent;
+ }
+}
+
+function restoreControlledState$3(element, props) {
+ // DOM component is still mounted; update
+ updateWrapper$1(element, props);
+}
+
+var HTML_NAMESPACE$1 = 'http://www.w3.org/1999/xhtml';
+var MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
+var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+
+var Namespaces = {
+ html: HTML_NAMESPACE$1,
+ mathml: MATH_NAMESPACE,
+ svg: SVG_NAMESPACE
+};
+
+// Assumes there is no parent namespace.
+function getIntrinsicNamespace(type) {
+ switch (type) {
+ case 'svg':
+ return SVG_NAMESPACE;
+ case 'math':
+ return MATH_NAMESPACE;
+ default:
+ return HTML_NAMESPACE$1;
+ }
+}
+
+function getChildNamespace(parentNamespace, type) {
+ if (parentNamespace == null || parentNamespace === HTML_NAMESPACE$1) {
+ // No (or default) parent namespace: potential entry point.
+ return getIntrinsicNamespace(type);
+ }
+ if (parentNamespace === SVG_NAMESPACE && type === 'foreignObject') {
+ // We're leaving SVG.
+ return HTML_NAMESPACE$1;
+ }
+ // By default, pass namespace below.
+ return parentNamespace;
+}
+
+/* globals MSApp */
+
+/**
+ * Create a function which has 'unsafe' privileges (required by windows8 apps)
+ */
+var createMicrosoftUnsafeLocalFunction = function (func) {
+ if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
+ return function (arg0, arg1, arg2, arg3) {
+ MSApp.execUnsafeLocalFunction(function () {
+ return func(arg0, arg1, arg2, arg3);
+ });
+ };
+ } else {
+ return func;
+ }
+};
+
+// SVG temp container for IE lacking innerHTML
+var reusableSVGContainer = void 0;
+
+/**
+ * Set the innerHTML property of a node
+ *
+ * @param {DOMElement} node
+ * @param {string} html
+ * @internal
+ */
+var setInnerHTML = createMicrosoftUnsafeLocalFunction(function (node, html) {
+ // IE does not have innerHTML for SVG nodes, so instead we inject the
+ // new markup in a temp node and then move the child nodes across into
+ // the target node
+
+ if (node.namespaceURI === Namespaces.svg && !('innerHTML' in node)) {
+ reusableSVGContainer = reusableSVGContainer || document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ reusableSVGContainer.innerHTML = '<svg>' + html + '</svg>';
+ var svgNode = reusableSVGContainer.firstChild;
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ while (svgNode.firstChild) {
+ node.appendChild(svgNode.firstChild);
+ }
+ } else {
+ node.innerHTML = html;
+ }
+});
+
+/**
+ * Set the textContent property of a node. For text updates, it's faster
+ * to set the `nodeValue` of the Text node directly instead of using
+ * `.textContent` which will remove the existing node and create a new one.
+ *
+ * @param {DOMElement} node
+ * @param {string} text
+ * @internal
+ */
+var setTextContent = function (node, text) {
+ if (text) {
+ var firstChild = node.firstChild;
+
+ if (firstChild && firstChild === node.lastChild && firstChild.nodeType === TEXT_NODE) {
+ firstChild.nodeValue = text;
+ return;
+ }
+ }
+ node.textContent = text;
+};
+
+// List derived from Gecko source code:
+// https://github.com/mozilla/gecko-dev/blob/4e638efc71/layout/style/test/property_database.js
+var shorthandToLonghand = {
+ animation: ['animationDelay', 'animationDirection', 'animationDuration', 'animationFillMode', 'animationIterationCount', 'animationName', 'animationPlayState', 'animationTimingFunction'],
+ background: ['backgroundAttachment', 'backgroundClip', 'backgroundColor', 'backgroundImage', 'backgroundOrigin', 'backgroundPositionX', 'backgroundPositionY', 'backgroundRepeat', 'backgroundSize'],
+ backgroundPosition: ['backgroundPositionX', 'backgroundPositionY'],
+ border: ['borderBottomColor', 'borderBottomStyle', 'borderBottomWidth', 'borderImageOutset', 'borderImageRepeat', 'borderImageSlice', 'borderImageSource', 'borderImageWidth', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth', 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderTopColor', 'borderTopStyle', 'borderTopWidth'],
+ borderBlockEnd: ['borderBlockEndColor', 'borderBlockEndStyle', 'borderBlockEndWidth'],
+ borderBlockStart: ['borderBlockStartColor', 'borderBlockStartStyle', 'borderBlockStartWidth'],
+ borderBottom: ['borderBottomColor', 'borderBottomStyle', 'borderBottomWidth'],
+ borderColor: ['borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor'],
+ borderImage: ['borderImageOutset', 'borderImageRepeat', 'borderImageSlice', 'borderImageSource', 'borderImageWidth'],
+ borderInlineEnd: ['borderInlineEndColor', 'borderInlineEndStyle', 'borderInlineEndWidth'],
+ borderInlineStart: ['borderInlineStartColor', 'borderInlineStartStyle', 'borderInlineStartWidth'],
+ borderLeft: ['borderLeftColor', 'borderLeftStyle', 'borderLeftWidth'],
+ borderRadius: ['borderBottomLeftRadius', 'borderBottomRightRadius', 'borderTopLeftRadius', 'borderTopRightRadius'],
+ borderRight: ['borderRightColor', 'borderRightStyle', 'borderRightWidth'],
+ borderStyle: ['borderBottomStyle', 'borderLeftStyle', 'borderRightStyle', 'borderTopStyle'],
+ borderTop: ['borderTopColor', 'borderTopStyle', 'borderTopWidth'],
+ borderWidth: ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth'],
+ columnRule: ['columnRuleColor', 'columnRuleStyle', 'columnRuleWidth'],
+ columns: ['columnCount', 'columnWidth'],
+ flex: ['flexBasis', 'flexGrow', 'flexShrink'],
+ flexFlow: ['flexDirection', 'flexWrap'],
+ font: ['fontFamily', 'fontFeatureSettings', 'fontKerning', 'fontLanguageOverride', 'fontSize', 'fontSizeAdjust', 'fontStretch', 'fontStyle', 'fontVariant', 'fontVariantAlternates', 'fontVariantCaps', 'fontVariantEastAsian', 'fontVariantLigatures', 'fontVariantNumeric', 'fontVariantPosition', 'fontWeight', 'lineHeight'],
+ fontVariant: ['fontVariantAlternates', 'fontVariantCaps', 'fontVariantEastAsian', 'fontVariantLigatures', 'fontVariantNumeric', 'fontVariantPosition'],
+ gap: ['columnGap', 'rowGap'],
+ grid: ['gridAutoColumns', 'gridAutoFlow', 'gridAutoRows', 'gridTemplateAreas', 'gridTemplateColumns', 'gridTemplateRows'],
+ gridArea: ['gridColumnEnd', 'gridColumnStart', 'gridRowEnd', 'gridRowStart'],
+ gridColumn: ['gridColumnEnd', 'gridColumnStart'],
+ gridColumnGap: ['columnGap'],
+ gridGap: ['columnGap', 'rowGap'],
+ gridRow: ['gridRowEnd', 'gridRowStart'],
+ gridRowGap: ['rowGap'],
+ gridTemplate: ['gridTemplateAreas', 'gridTemplateColumns', 'gridTemplateRows'],
+ listStyle: ['listStyleImage', 'listStylePosition', 'listStyleType'],
+ margin: ['marginBottom', 'marginLeft', 'marginRight', 'marginTop'],
+ marker: ['markerEnd', 'markerMid', 'markerStart'],
+ mask: ['maskClip', 'maskComposite', 'maskImage', 'maskMode', 'maskOrigin', 'maskPositionX', 'maskPositionY', 'maskRepeat', 'maskSize'],
+ maskPosition: ['maskPositionX', 'maskPositionY'],
+ outline: ['outlineColor', 'outlineStyle', 'outlineWidth'],
+ overflow: ['overflowX', 'overflowY'],
+ padding: ['paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop'],
+ placeContent: ['alignContent', 'justifyContent'],
+ placeItems: ['alignItems', 'justifyItems'],
+ placeSelf: ['alignSelf', 'justifySelf'],
+ textDecoration: ['textDecorationColor', 'textDecorationLine', 'textDecorationStyle'],
+ textEmphasis: ['textEmphasisColor', 'textEmphasisStyle'],
+ transition: ['transitionDelay', 'transitionDuration', 'transitionProperty', 'transitionTimingFunction'],
+ wordWrap: ['overflowWrap']
+};
+
+/**
+ * CSS properties which accept numbers but are not in units of "px".
+ */
+var isUnitlessNumber = {
+ animationIterationCount: true,
+ borderImageOutset: true,
+ borderImageSlice: true,
+ borderImageWidth: true,
+ boxFlex: true,
+ boxFlexGroup: true,
+ boxOrdinalGroup: true,
+ columnCount: true,
+ columns: true,
+ flex: true,
+ flexGrow: true,
+ flexPositive: true,
+ flexShrink: true,
+ flexNegative: true,
+ flexOrder: true,
+ gridArea: true,
+ gridRow: true,
+ gridRowEnd: true,
+ gridRowSpan: true,
+ gridRowStart: true,
+ gridColumn: true,
+ gridColumnEnd: true,
+ gridColumnSpan: true,
+ gridColumnStart: true,
+ fontWeight: true,
+ lineClamp: true,
+ lineHeight: true,
+ opacity: true,
+ order: true,
+ orphans: true,
+ tabSize: true,
+ widows: true,
+ zIndex: true,
+ zoom: true,
+
+ // SVG-related properties
+ fillOpacity: true,
+ floodOpacity: true,
+ stopOpacity: true,
+ strokeDasharray: true,
+ strokeDashoffset: true,
+ strokeMiterlimit: true,
+ strokeOpacity: true,
+ strokeWidth: true
+};
+
+/**
+ * @param {string} prefix vendor-specific prefix, eg: Webkit
+ * @param {string} key style name, eg: transitionDuration
+ * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
+ * WebkitTransitionDuration
+ */
+function prefixKey(prefix, key) {
+ return prefix + key.charAt(0).toUpperCase() + key.substring(1);
+}
+
+/**
+ * Support style names that may come passed in prefixed by adding permutations
+ * of vendor prefixes.
+ */
+var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
+
+// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
+// infinite loop, because it iterates over the newly added props too.
+Object.keys(isUnitlessNumber).forEach(function (prop) {
+ prefixes.forEach(function (prefix) {
+ isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
+ });
+});
+
+/**
+ * Convert a value into the proper css writable value. The style name `name`
+ * should be logical (no hyphens), as specified
+ * in `CSSProperty.isUnitlessNumber`.
+ *
+ * @param {string} name CSS property name such as `topMargin`.
+ * @param {*} value CSS property value such as `10px`.
+ * @return {string} Normalized style value with dimensions applied.
+ */
+function dangerousStyleValue(name, value, isCustomProperty) {
+ // Note that we've removed escapeTextForBrowser() calls here since the
+ // whole string will be escaped when the attribute is injected into
+ // the markup. If you provide unsafe user data here they can inject
+ // arbitrary CSS which may be problematic (I couldn't repro this):
+ // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
+ // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
+ // This is not an XSS hole but instead a potential CSS injection issue
+ // which has lead to a greater discussion about how we're going to
+ // trust URLs moving forward. See #2115901
+
+ var isEmpty = value == null || typeof value === 'boolean' || value === '';
+ if (isEmpty) {
+ return '';
+ }
+
+ if (!isCustomProperty && typeof value === 'number' && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])) {
+ return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers
+ }
+
+ return ('' + value).trim();
+}
+
+var uppercasePattern = /([A-Z])/g;
+var msPattern = /^ms-/;
+
+/**
+ * Hyphenates a camelcased CSS property name, for example:
+ *
+ * > hyphenateStyleName('backgroundColor')
+ * < "background-color"
+ * > hyphenateStyleName('MozTransition')
+ * < "-moz-transition"
+ * > hyphenateStyleName('msTransition')
+ * < "-ms-transition"
+ *
+ * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
+ * is converted to `-ms-`.
+ */
+function hyphenateStyleName(name) {
+ return name.replace(uppercasePattern, '-$1').toLowerCase().replace(msPattern, '-ms-');
+}
+
+var warnValidStyle = function () {};
+
+{
+ // 'msTransform' is correct, but the other prefixes should be capitalized
+ var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
+ var msPattern$1 = /^-ms-/;
+ var hyphenPattern = /-(.)/g;
+
+ // style values shouldn't contain a semicolon
+ var badStyleValueWithSemicolonPattern = /;\s*$/;
+
+ var warnedStyleNames = {};
+ var warnedStyleValues = {};
+ var warnedForNaNValue = false;
+ var warnedForInfinityValue = false;
+
+ var camelize = function (string) {
+ return string.replace(hyphenPattern, function (_, character) {
+ return character.toUpperCase();
+ });
+ };
+
+ var warnHyphenatedStyleName = function (name) {
+ if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
+ return;
+ }
+
+ warnedStyleNames[name] = true;
+ warning$1(false, 'Unsupported style property %s. Did you mean %s?', name,
+ // As Andi Smith suggests
+ // (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
+ // is converted to lowercase `ms`.
+ camelize(name.replace(msPattern$1, 'ms-')));
+ };
+
+ var warnBadVendoredStyleName = function (name) {
+ if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
+ return;
+ }
+
+ warnedStyleNames[name] = true;
+ warning$1(false, 'Unsupported vendor-prefixed style property %s. Did you mean %s?', name, name.charAt(0).toUpperCase() + name.slice(1));
+ };
+
+ var warnStyleValueWithSemicolon = function (name, value) {
+ if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
+ return;
+ }
+
+ warnedStyleValues[value] = true;
+ warning$1(false, "Style property values shouldn't contain a semicolon. " + 'Try "%s: %s" instead.', name, value.replace(badStyleValueWithSemicolonPattern, ''));
+ };
+
+ var warnStyleValueIsNaN = function (name, value) {
+ if (warnedForNaNValue) {
+ return;
+ }
+
+ warnedForNaNValue = true;
+ warning$1(false, '`NaN` is an invalid value for the `%s` css style property.', name);
+ };
+
+ var warnStyleValueIsInfinity = function (name, value) {
+ if (warnedForInfinityValue) {
+ return;
+ }
+
+ warnedForInfinityValue = true;
+ warning$1(false, '`Infinity` is an invalid value for the `%s` css style property.', name);
+ };
+
+ warnValidStyle = function (name, value) {
+ if (name.indexOf('-') > -1) {
+ warnHyphenatedStyleName(name);
+ } else if (badVendoredStyleNamePattern.test(name)) {
+ warnBadVendoredStyleName(name);
+ } else if (badStyleValueWithSemicolonPattern.test(value)) {
+ warnStyleValueWithSemicolon(name, value);
+ }
+
+ if (typeof value === 'number') {
+ if (isNaN(value)) {
+ warnStyleValueIsNaN(name, value);
+ } else if (!isFinite(value)) {
+ warnStyleValueIsInfinity(name, value);
+ }
+ }
+ };
+}
+
+var warnValidStyle$1 = warnValidStyle;
+
+/**
+ * Operations for dealing with CSS properties.
+ */
+
+/**
+ * This creates a string that is expected to be equivalent to the style
+ * attribute generated by server-side rendering. It by-passes warnings and
+ * security checks so it's not safe to use this value for anything other than
+ * comparison. It is only used in DEV for SSR validation.
+ */
+function createDangerousStringForStyles(styles) {
+ {
+ var serialized = '';
+ var delimiter = '';
+ for (var styleName in styles) {
+ if (!styles.hasOwnProperty(styleName)) {
+ continue;
+ }
+ var styleValue = styles[styleName];
+ if (styleValue != null) {
+ var isCustomProperty = styleName.indexOf('--') === 0;
+ serialized += delimiter + hyphenateStyleName(styleName) + ':';
+ serialized += dangerousStyleValue(styleName, styleValue, isCustomProperty);
+
+ delimiter = ';';
+ }
+ }
+ return serialized || null;
+ }
+}
+
+/**
+ * Sets the value for multiple styles on a node. If a value is specified as
+ * '' (empty string), the corresponding style property will be unset.
+ *
+ * @param {DOMElement} node
+ * @param {object} styles
+ */
+function setValueForStyles(node, styles) {
+ var style = node.style;
+ for (var styleName in styles) {
+ if (!styles.hasOwnProperty(styleName)) {
+ continue;
+ }
+ var isCustomProperty = styleName.indexOf('--') === 0;
+ {
+ if (!isCustomProperty) {
+ warnValidStyle$1(styleName, styles[styleName]);
+ }
+ }
+ var styleValue = dangerousStyleValue(styleName, styles[styleName], isCustomProperty);
+ if (styleName === 'float') {
+ styleName = 'cssFloat';
+ }
+ if (isCustomProperty) {
+ style.setProperty(styleName, styleValue);
+ } else {
+ style[styleName] = styleValue;
+ }
+ }
+}
+
+function isValueEmpty(value) {
+ return value == null || typeof value === 'boolean' || value === '';
+}
+
+/**
+ * Given {color: 'red', overflow: 'hidden'} returns {
+ * color: 'color',
+ * overflowX: 'overflow',
+ * overflowY: 'overflow',
+ * }. This can be read as "the overflowY property was set by the overflow
+ * shorthand". That is, the values are the property that each was derived from.
+ */
+function expandShorthandMap(styles) {
+ var expanded = {};
+ for (var key in styles) {
+ var longhands = shorthandToLonghand[key] || [key];
+ for (var i = 0; i < longhands.length; i++) {
+ expanded[longhands[i]] = key;
+ }
+ }
+ return expanded;
+}
+
+/**
+ * When mixing shorthand and longhand property names, we warn during updates if
+ * we expect an incorrect result to occur. In particular, we warn for:
+ *
+ * Updating a shorthand property (longhand gets overwritten):
+ * {font: 'foo', fontVariant: 'bar'} -> {font: 'baz', fontVariant: 'bar'}
+ * becomes .style.font = 'baz'
+ * Removing a shorthand property (longhand gets lost too):
+ * {font: 'foo', fontVariant: 'bar'} -> {fontVariant: 'bar'}
+ * becomes .style.font = ''
+ * Removing a longhand property (should revert to shorthand; doesn't):
+ * {font: 'foo', fontVariant: 'bar'} -> {font: 'foo'}
+ * becomes .style.fontVariant = ''
+ */
+function validateShorthandPropertyCollisionInDev(styleUpdates, nextStyles) {
+ if (!warnAboutShorthandPropertyCollision) {
+ return;
+ }
+
+ if (!nextStyles) {
+ return;
+ }
+
+ var expandedUpdates = expandShorthandMap(styleUpdates);
+ var expandedStyles = expandShorthandMap(nextStyles);
+ var warnedAbout = {};
+ for (var key in expandedUpdates) {
+ var originalKey = expandedUpdates[key];
+ var correctOriginalKey = expandedStyles[key];
+ if (correctOriginalKey && originalKey !== correctOriginalKey) {
+ var warningKey = originalKey + ',' + correctOriginalKey;
+ if (warnedAbout[warningKey]) {
+ continue;
+ }
+ warnedAbout[warningKey] = true;
+ warning$1(false, '%s a style property during rerender (%s) when a ' + 'conflicting property is set (%s) can lead to styling bugs. To ' + "avoid this, don't mix shorthand and non-shorthand properties " + 'for the same value; instead, replace the shorthand with ' + 'separate values.', isValueEmpty(styleUpdates[originalKey]) ? 'Removing' : 'Updating', originalKey, correctOriginalKey);
+ }
+ }
+}
+
+// For HTML, certain tags should omit their close tag. We keep a whitelist for
+// those special-case tags.
+
+var omittedCloseTags = {
+ area: true,
+ base: true,
+ br: true,
+ col: true,
+ embed: true,
+ hr: true,
+ img: true,
+ input: true,
+ keygen: true,
+ link: true,
+ meta: true,
+ param: true,
+ source: true,
+ track: true,
+ wbr: true
+ // NOTE: menuitem's close tag should be omitted, but that causes problems.
+};
+
+// For HTML, certain tags cannot have children. This has the same purpose as
+// `omittedCloseTags` except that `menuitem` should still have its closing tag.
+
+var voidElementTags = _assign({
+ menuitem: true
+}, omittedCloseTags);
+
+// TODO: We can remove this if we add invariantWithStack()
+// or add stack by default to invariants where possible.
+var HTML$1 = '__html';
+
+var ReactDebugCurrentFrame$2 = null;
+{
+ ReactDebugCurrentFrame$2 = ReactSharedInternals.ReactDebugCurrentFrame;
+}
+
+function assertValidProps(tag, props) {
+ if (!props) {
+ return;
+ }
+ // Note the use of `==` which checks for null or undefined.
+ if (voidElementTags[tag]) {
+ !(props.children == null && props.dangerouslySetInnerHTML == null) ? invariant(false, '%s is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.%s', tag, ReactDebugCurrentFrame$2.getStackAddendum()) : void 0;
+ }
+ if (props.dangerouslySetInnerHTML != null) {
+ !(props.children == null) ? invariant(false, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : void 0;
+ !(typeof props.dangerouslySetInnerHTML === 'object' && HTML$1 in props.dangerouslySetInnerHTML) ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information.') : void 0;
+ }
+ {
+ !(props.suppressContentEditableWarning || !props.contentEditable || props.children == null) ? warning$1(false, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : void 0;
+ }
+ !(props.style == null || typeof props.style === 'object') ? invariant(false, 'The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + \'em\'}} when using JSX.%s', ReactDebugCurrentFrame$2.getStackAddendum()) : void 0;
+}
+
+function isCustomComponent(tagName, props) {
+ if (tagName.indexOf('-') === -1) {
+ return typeof props.is === 'string';
+ }
+ switch (tagName) {
+ // These are reserved SVG and MathML elements.
+ // We don't mind this whitelist too much because we expect it to never grow.
+ // The alternative is to track the namespace in a few places which is convoluted.
+ // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts
+ case 'annotation-xml':
+ case 'color-profile':
+ case 'font-face':
+ case 'font-face-src':
+ case 'font-face-uri':
+ case 'font-face-format':
+ case 'font-face-name':
+ case 'missing-glyph':
+ return false;
+ default:
+ return true;
+ }
+}
+
+// When adding attributes to the HTML or SVG whitelist, be sure to
+// also add them to this module to ensure casing and incorrect name
+// warnings.
+var possibleStandardNames = {
+ // HTML
+ accept: 'accept',
+ acceptcharset: 'acceptCharset',
+ 'accept-charset': 'acceptCharset',
+ accesskey: 'accessKey',
+ action: 'action',
+ allowfullscreen: 'allowFullScreen',
+ alt: 'alt',
+ as: 'as',
+ async: 'async',
+ autocapitalize: 'autoCapitalize',
+ autocomplete: 'autoComplete',
+ autocorrect: 'autoCorrect',
+ autofocus: 'autoFocus',
+ autoplay: 'autoPlay',
+ autosave: 'autoSave',
+ capture: 'capture',
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing',
+ challenge: 'challenge',
+ charset: 'charSet',
+ checked: 'checked',
+ children: 'children',
+ cite: 'cite',
+ class: 'className',
+ classid: 'classID',
+ classname: 'className',
+ cols: 'cols',
+ colspan: 'colSpan',
+ content: 'content',
+ contenteditable: 'contentEditable',
+ contextmenu: 'contextMenu',
+ controls: 'controls',
+ controlslist: 'controlsList',
+ coords: 'coords',
+ crossorigin: 'crossOrigin',
+ dangerouslysetinnerhtml: 'dangerouslySetInnerHTML',
+ data: 'data',
+ datetime: 'dateTime',
+ default: 'default',
+ defaultchecked: 'defaultChecked',
+ defaultvalue: 'defaultValue',
+ defer: 'defer',
+ dir: 'dir',
+ disabled: 'disabled',
+ download: 'download',
+ draggable: 'draggable',
+ enctype: 'encType',
+ for: 'htmlFor',
+ form: 'form',
+ formmethod: 'formMethod',
+ formaction: 'formAction',
+ formenctype: 'formEncType',
+ formnovalidate: 'formNoValidate',
+ formtarget: 'formTarget',
+ frameborder: 'frameBorder',
+ headers: 'headers',
+ height: 'height',
+ hidden: 'hidden',
+ high: 'high',
+ href: 'href',
+ hreflang: 'hrefLang',
+ htmlfor: 'htmlFor',
+ httpequiv: 'httpEquiv',
+ 'http-equiv': 'httpEquiv',
+ icon: 'icon',
+ id: 'id',
+ innerhtml: 'innerHTML',
+ inputmode: 'inputMode',
+ integrity: 'integrity',
+ is: 'is',
+ itemid: 'itemID',
+ itemprop: 'itemProp',
+ itemref: 'itemRef',
+ itemscope: 'itemScope',
+ itemtype: 'itemType',
+ keyparams: 'keyParams',
+ keytype: 'keyType',
+ kind: 'kind',
+ label: 'label',
+ lang: 'lang',
+ list: 'list',
+ loop: 'loop',
+ low: 'low',
+ manifest: 'manifest',
+ marginwidth: 'marginWidth',
+ marginheight: 'marginHeight',
+ max: 'max',
+ maxlength: 'maxLength',
+ media: 'media',
+ mediagroup: 'mediaGroup',
+ method: 'method',
+ min: 'min',
+ minlength: 'minLength',
+ multiple: 'multiple',
+ muted: 'muted',
+ name: 'name',
+ nomodule: 'noModule',
+ nonce: 'nonce',
+ novalidate: 'noValidate',
+ open: 'open',
+ optimum: 'optimum',
+ pattern: 'pattern',
+ placeholder: 'placeholder',
+ playsinline: 'playsInline',
+ poster: 'poster',
+ preload: 'preload',
+ profile: 'profile',
+ radiogroup: 'radioGroup',
+ readonly: 'readOnly',
+ referrerpolicy: 'referrerPolicy',
+ rel: 'rel',
+ required: 'required',
+ reversed: 'reversed',
+ role: 'role',
+ rows: 'rows',
+ rowspan: 'rowSpan',
+ sandbox: 'sandbox',
+ scope: 'scope',
+ scoped: 'scoped',
+ scrolling: 'scrolling',
+ seamless: 'seamless',
+ selected: 'selected',
+ shape: 'shape',
+ size: 'size',
+ sizes: 'sizes',
+ span: 'span',
+ spellcheck: 'spellCheck',
+ src: 'src',
+ srcdoc: 'srcDoc',
+ srclang: 'srcLang',
+ srcset: 'srcSet',
+ start: 'start',
+ step: 'step',
+ style: 'style',
+ summary: 'summary',
+ tabindex: 'tabIndex',
+ target: 'target',
+ title: 'title',
+ type: 'type',
+ usemap: 'useMap',
+ value: 'value',
+ width: 'width',
+ wmode: 'wmode',
+ wrap: 'wrap',
+
+ // SVG
+ about: 'about',
+ accentheight: 'accentHeight',
+ 'accent-height': 'accentHeight',
+ accumulate: 'accumulate',
+ additive: 'additive',
+ alignmentbaseline: 'alignmentBaseline',
+ 'alignment-baseline': 'alignmentBaseline',
+ allowreorder: 'allowReorder',
+ alphabetic: 'alphabetic',
+ amplitude: 'amplitude',
+ arabicform: 'arabicForm',
+ 'arabic-form': 'arabicForm',
+ ascent: 'ascent',
+ attributename: 'attributeName',
+ attributetype: 'attributeType',
+ autoreverse: 'autoReverse',
+ azimuth: 'azimuth',
+ basefrequency: 'baseFrequency',
+ baselineshift: 'baselineShift',
+ 'baseline-shift': 'baselineShift',
+ baseprofile: 'baseProfile',
+ bbox: 'bbox',
+ begin: 'begin',
+ bias: 'bias',
+ by: 'by',
+ calcmode: 'calcMode',
+ capheight: 'capHeight',
+ 'cap-height': 'capHeight',
+ clip: 'clip',
+ clippath: 'clipPath',
+ 'clip-path': 'clipPath',
+ clippathunits: 'clipPathUnits',
+ cliprule: 'clipRule',
+ 'clip-rule': 'clipRule',
+ color: 'color',
+ colorinterpolation: 'colorInterpolation',
+ 'color-interpolation': 'colorInterpolation',
+ colorinterpolationfilters: 'colorInterpolationFilters',
+ 'color-interpolation-filters': 'colorInterpolationFilters',
+ colorprofile: 'colorProfile',
+ 'color-profile': 'colorProfile',
+ colorrendering: 'colorRendering',
+ 'color-rendering': 'colorRendering',
+ contentscripttype: 'contentScriptType',
+ contentstyletype: 'contentStyleType',
+ cursor: 'cursor',
+ cx: 'cx',
+ cy: 'cy',
+ d: 'd',
+ datatype: 'datatype',
+ decelerate: 'decelerate',
+ descent: 'descent',
+ diffuseconstant: 'diffuseConstant',
+ direction: 'direction',
+ display: 'display',
+ divisor: 'divisor',
+ dominantbaseline: 'dominantBaseline',
+ 'dominant-baseline': 'dominantBaseline',
+ dur: 'dur',
+ dx: 'dx',
+ dy: 'dy',
+ edgemode: 'edgeMode',
+ elevation: 'elevation',
+ enablebackground: 'enableBackground',
+ 'enable-background': 'enableBackground',
+ end: 'end',
+ exponent: 'exponent',
+ externalresourcesrequired: 'externalResourcesRequired',
+ fill: 'fill',
+ fillopacity: 'fillOpacity',
+ 'fill-opacity': 'fillOpacity',
+ fillrule: 'fillRule',
+ 'fill-rule': 'fillRule',
+ filter: 'filter',
+ filterres: 'filterRes',
+ filterunits: 'filterUnits',
+ floodopacity: 'floodOpacity',
+ 'flood-opacity': 'floodOpacity',
+ floodcolor: 'floodColor',
+ 'flood-color': 'floodColor',
+ focusable: 'focusable',
+ fontfamily: 'fontFamily',
+ 'font-family': 'fontFamily',
+ fontsize: 'fontSize',
+ 'font-size': 'fontSize',
+ fontsizeadjust: 'fontSizeAdjust',
+ 'font-size-adjust': 'fontSizeAdjust',
+ fontstretch: 'fontStretch',
+ 'font-stretch': 'fontStretch',
+ fontstyle: 'fontStyle',
+ 'font-style': 'fontStyle',
+ fontvariant: 'fontVariant',
+ 'font-variant': 'fontVariant',
+ fontweight: 'fontWeight',
+ 'font-weight': 'fontWeight',
+ format: 'format',
+ from: 'from',
+ fx: 'fx',
+ fy: 'fy',
+ g1: 'g1',
+ g2: 'g2',
+ glyphname: 'glyphName',
+ 'glyph-name': 'glyphName',
+ glyphorientationhorizontal: 'glyphOrientationHorizontal',
+ 'glyph-orientation-horizontal': 'glyphOrientationHorizontal',
+ glyphorientationvertical: 'glyphOrientationVertical',
+ 'glyph-orientation-vertical': 'glyphOrientationVertical',
+ glyphref: 'glyphRef',
+ gradienttransform: 'gradientTransform',
+ gradientunits: 'gradientUnits',
+ hanging: 'hanging',
+ horizadvx: 'horizAdvX',
+ 'horiz-adv-x': 'horizAdvX',
+ horizoriginx: 'horizOriginX',
+ 'horiz-origin-x': 'horizOriginX',
+ ideographic: 'ideographic',
+ imagerendering: 'imageRendering',
+ 'image-rendering': 'imageRendering',
+ in2: 'in2',
+ in: 'in',
+ inlist: 'inlist',
+ intercept: 'intercept',
+ k1: 'k1',
+ k2: 'k2',
+ k3: 'k3',
+ k4: 'k4',
+ k: 'k',
+ kernelmatrix: 'kernelMatrix',
+ kernelunitlength: 'kernelUnitLength',
+ kerning: 'kerning',
+ keypoints: 'keyPoints',
+ keysplines: 'keySplines',
+ keytimes: 'keyTimes',
+ lengthadjust: 'lengthAdjust',
+ letterspacing: 'letterSpacing',
+ 'letter-spacing': 'letterSpacing',
+ lightingcolor: 'lightingColor',
+ 'lighting-color': 'lightingColor',
+ limitingconeangle: 'limitingConeAngle',
+ local: 'local',
+ markerend: 'markerEnd',
+ 'marker-end': 'markerEnd',
+ markerheight: 'markerHeight',
+ markermid: 'markerMid',
+ 'marker-mid': 'markerMid',
+ markerstart: 'markerStart',
+ 'marker-start': 'markerStart',
+ markerunits: 'markerUnits',
+ markerwidth: 'markerWidth',
+ mask: 'mask',
+ maskcontentunits: 'maskContentUnits',
+ maskunits: 'maskUnits',
+ mathematical: 'mathematical',
+ mode: 'mode',
+ numoctaves: 'numOctaves',
+ offset: 'offset',
+ opacity: 'opacity',
+ operator: 'operator',
+ order: 'order',
+ orient: 'orient',
+ orientation: 'orientation',
+ origin: 'origin',
+ overflow: 'overflow',
+ overlineposition: 'overlinePosition',
+ 'overline-position': 'overlinePosition',
+ overlinethickness: 'overlineThickness',
+ 'overline-thickness': 'overlineThickness',
+ paintorder: 'paintOrder',
+ 'paint-order': 'paintOrder',
+ panose1: 'panose1',
+ 'panose-1': 'panose1',
+ pathlength: 'pathLength',
+ patterncontentunits: 'patternContentUnits',
+ patterntransform: 'patternTransform',
+ patternunits: 'patternUnits',
+ pointerevents: 'pointerEvents',
+ 'pointer-events': 'pointerEvents',
+ points: 'points',
+ pointsatx: 'pointsAtX',
+ pointsaty: 'pointsAtY',
+ pointsatz: 'pointsAtZ',
+ prefix: 'prefix',
+ preservealpha: 'preserveAlpha',
+ preserveaspectratio: 'preserveAspectRatio',
+ primitiveunits: 'primitiveUnits',
+ property: 'property',
+ r: 'r',
+ radius: 'radius',
+ refx: 'refX',
+ refy: 'refY',
+ renderingintent: 'renderingIntent',
+ 'rendering-intent': 'renderingIntent',
+ repeatcount: 'repeatCount',
+ repeatdur: 'repeatDur',
+ requiredextensions: 'requiredExtensions',
+ requiredfeatures: 'requiredFeatures',
+ resource: 'resource',
+ restart: 'restart',
+ result: 'result',
+ results: 'results',
+ rotate: 'rotate',
+ rx: 'rx',
+ ry: 'ry',
+ scale: 'scale',
+ security: 'security',
+ seed: 'seed',
+ shaperendering: 'shapeRendering',
+ 'shape-rendering': 'shapeRendering',
+ slope: 'slope',
+ spacing: 'spacing',
+ specularconstant: 'specularConstant',
+ specularexponent: 'specularExponent',
+ speed: 'speed',
+ spreadmethod: 'spreadMethod',
+ startoffset: 'startOffset',
+ stddeviation: 'stdDeviation',
+ stemh: 'stemh',
+ stemv: 'stemv',
+ stitchtiles: 'stitchTiles',
+ stopcolor: 'stopColor',
+ 'stop-color': 'stopColor',
+ stopopacity: 'stopOpacity',
+ 'stop-opacity': 'stopOpacity',
+ strikethroughposition: 'strikethroughPosition',
+ 'strikethrough-position': 'strikethroughPosition',
+ strikethroughthickness: 'strikethroughThickness',
+ 'strikethrough-thickness': 'strikethroughThickness',
+ string: 'string',
+ stroke: 'stroke',
+ strokedasharray: 'strokeDasharray',
+ 'stroke-dasharray': 'strokeDasharray',
+ strokedashoffset: 'strokeDashoffset',
+ 'stroke-dashoffset': 'strokeDashoffset',
+ strokelinecap: 'strokeLinecap',
+ 'stroke-linecap': 'strokeLinecap',
+ strokelinejoin: 'strokeLinejoin',
+ 'stroke-linejoin': 'strokeLinejoin',
+ strokemiterlimit: 'strokeMiterlimit',
+ 'stroke-miterlimit': 'strokeMiterlimit',
+ strokewidth: 'strokeWidth',
+ 'stroke-width': 'strokeWidth',
+ strokeopacity: 'strokeOpacity',
+ 'stroke-opacity': 'strokeOpacity',
+ suppresscontenteditablewarning: 'suppressContentEditableWarning',
+ suppresshydrationwarning: 'suppressHydrationWarning',
+ surfacescale: 'surfaceScale',
+ systemlanguage: 'systemLanguage',
+ tablevalues: 'tableValues',
+ targetx: 'targetX',
+ targety: 'targetY',
+ textanchor: 'textAnchor',
+ 'text-anchor': 'textAnchor',
+ textdecoration: 'textDecoration',
+ 'text-decoration': 'textDecoration',
+ textlength: 'textLength',
+ textrendering: 'textRendering',
+ 'text-rendering': 'textRendering',
+ to: 'to',
+ transform: 'transform',
+ typeof: 'typeof',
+ u1: 'u1',
+ u2: 'u2',
+ underlineposition: 'underlinePosition',
+ 'underline-position': 'underlinePosition',
+ underlinethickness: 'underlineThickness',
+ 'underline-thickness': 'underlineThickness',
+ unicode: 'unicode',
+ unicodebidi: 'unicodeBidi',
+ 'unicode-bidi': 'unicodeBidi',
+ unicoderange: 'unicodeRange',
+ 'unicode-range': 'unicodeRange',
+ unitsperem: 'unitsPerEm',
+ 'units-per-em': 'unitsPerEm',
+ unselectable: 'unselectable',
+ valphabetic: 'vAlphabetic',
+ 'v-alphabetic': 'vAlphabetic',
+ values: 'values',
+ vectoreffect: 'vectorEffect',
+ 'vector-effect': 'vectorEffect',
+ version: 'version',
+ vertadvy: 'vertAdvY',
+ 'vert-adv-y': 'vertAdvY',
+ vertoriginx: 'vertOriginX',
+ 'vert-origin-x': 'vertOriginX',
+ vertoriginy: 'vertOriginY',
+ 'vert-origin-y': 'vertOriginY',
+ vhanging: 'vHanging',
+ 'v-hanging': 'vHanging',
+ videographic: 'vIdeographic',
+ 'v-ideographic': 'vIdeographic',
+ viewbox: 'viewBox',
+ viewtarget: 'viewTarget',
+ visibility: 'visibility',
+ vmathematical: 'vMathematical',
+ 'v-mathematical': 'vMathematical',
+ vocab: 'vocab',
+ widths: 'widths',
+ wordspacing: 'wordSpacing',
+ 'word-spacing': 'wordSpacing',
+ writingmode: 'writingMode',
+ 'writing-mode': 'writingMode',
+ x1: 'x1',
+ x2: 'x2',
+ x: 'x',
+ xchannelselector: 'xChannelSelector',
+ xheight: 'xHeight',
+ 'x-height': 'xHeight',
+ xlinkactuate: 'xlinkActuate',
+ 'xlink:actuate': 'xlinkActuate',
+ xlinkarcrole: 'xlinkArcrole',
+ 'xlink:arcrole': 'xlinkArcrole',
+ xlinkhref: 'xlinkHref',
+ 'xlink:href': 'xlinkHref',
+ xlinkrole: 'xlinkRole',
+ 'xlink:role': 'xlinkRole',
+ xlinkshow: 'xlinkShow',
+ 'xlink:show': 'xlinkShow',
+ xlinktitle: 'xlinkTitle',
+ 'xlink:title': 'xlinkTitle',
+ xlinktype: 'xlinkType',
+ 'xlink:type': 'xlinkType',
+ xmlbase: 'xmlBase',
+ 'xml:base': 'xmlBase',
+ xmllang: 'xmlLang',
+ 'xml:lang': 'xmlLang',
+ xmlns: 'xmlns',
+ 'xml:space': 'xmlSpace',
+ xmlnsxlink: 'xmlnsXlink',
+ 'xmlns:xlink': 'xmlnsXlink',
+ xmlspace: 'xmlSpace',
+ y1: 'y1',
+ y2: 'y2',
+ y: 'y',
+ ychannelselector: 'yChannelSelector',
+ z: 'z',
+ zoomandpan: 'zoomAndPan'
+};
+
+var ariaProperties = {
+ 'aria-current': 0, // state
+ 'aria-details': 0,
+ 'aria-disabled': 0, // state
+ 'aria-hidden': 0, // state
+ 'aria-invalid': 0, // state
+ 'aria-keyshortcuts': 0,
+ 'aria-label': 0,
+ 'aria-roledescription': 0,
+ // Widget Attributes
+ 'aria-autocomplete': 0,
+ 'aria-checked': 0,
+ 'aria-expanded': 0,
+ 'aria-haspopup': 0,
+ 'aria-level': 0,
+ 'aria-modal': 0,
+ 'aria-multiline': 0,
+ 'aria-multiselectable': 0,
+ 'aria-orientation': 0,
+ 'aria-placeholder': 0,
+ 'aria-pressed': 0,
+ 'aria-readonly': 0,
+ 'aria-required': 0,
+ 'aria-selected': 0,
+ 'aria-sort': 0,
+ 'aria-valuemax': 0,
+ 'aria-valuemin': 0,
+ 'aria-valuenow': 0,
+ 'aria-valuetext': 0,
+ // Live Region Attributes
+ 'aria-atomic': 0,
+ 'aria-busy': 0,
+ 'aria-live': 0,
+ 'aria-relevant': 0,
+ // Drag-and-Drop Attributes
+ 'aria-dropeffect': 0,
+ 'aria-grabbed': 0,
+ // Relationship Attributes
+ 'aria-activedescendant': 0,
+ 'aria-colcount': 0,
+ 'aria-colindex': 0,
+ 'aria-colspan': 0,
+ 'aria-controls': 0,
+ 'aria-describedby': 0,
+ 'aria-errormessage': 0,
+ 'aria-flowto': 0,
+ 'aria-labelledby': 0,
+ 'aria-owns': 0,
+ 'aria-posinset': 0,
+ 'aria-rowcount': 0,
+ 'aria-rowindex': 0,
+ 'aria-rowspan': 0,
+ 'aria-setsize': 0
+};
+
+var warnedProperties = {};
+var rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
+var rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
+
+function validateProperty(tagName, name) {
+ if (hasOwnProperty$2.call(warnedProperties, name) && warnedProperties[name]) {
+ return true;
+ }
+
+ if (rARIACamel.test(name)) {
+ var ariaName = 'aria-' + name.slice(4).toLowerCase();
+ var correctName = ariaProperties.hasOwnProperty(ariaName) ? ariaName : null;
+
+ // If this is an aria-* attribute, but is not listed in the known DOM
+ // DOM properties, then it is an invalid aria-* attribute.
+ if (correctName == null) {
+ warning$1(false, 'Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.', name);
+ warnedProperties[name] = true;
+ return true;
+ }
+ // aria-* attributes should be lowercase; suggest the lowercase version.
+ if (name !== correctName) {
+ warning$1(false, 'Invalid ARIA attribute `%s`. Did you mean `%s`?', name, correctName);
+ warnedProperties[name] = true;
+ return true;
+ }
+ }
+
+ if (rARIA.test(name)) {
+ var lowerCasedName = name.toLowerCase();
+ var standardName = ariaProperties.hasOwnProperty(lowerCasedName) ? lowerCasedName : null;
+
+ // If this is an aria-* attribute, but is not listed in the known DOM
+ // DOM properties, then it is an invalid aria-* attribute.
+ if (standardName == null) {
+ warnedProperties[name] = true;
+ return false;
+ }
+ // aria-* attributes should be lowercase; suggest the lowercase version.
+ if (name !== standardName) {
+ warning$1(false, 'Unknown ARIA attribute `%s`. Did you mean `%s`?', name, standardName);
+ warnedProperties[name] = true;
+ return true;
+ }
+ }
+
+ return true;
+}
+
+function warnInvalidARIAProps(type, props) {
+ var invalidProps = [];
+
+ for (var key in props) {
+ var isValid = validateProperty(type, key);
+ if (!isValid) {
+ invalidProps.push(key);
+ }
+ }
+
+ var unknownPropString = invalidProps.map(function (prop) {
+ return '`' + prop + '`';
+ }).join(', ');
+
+ if (invalidProps.length === 1) {
+ warning$1(false, 'Invalid aria prop %s on <%s> tag. ' + 'For details, see https://fb.me/invalid-aria-prop', unknownPropString, type);
+ } else if (invalidProps.length > 1) {
+ warning$1(false, 'Invalid aria props %s on <%s> tag. ' + 'For details, see https://fb.me/invalid-aria-prop', unknownPropString, type);
+ }
+}
+
+function validateProperties(type, props) {
+ if (isCustomComponent(type, props)) {
+ return;
+ }
+ warnInvalidARIAProps(type, props);
+}
+
+var didWarnValueNull = false;
+
+function validateProperties$1(type, props) {
+ if (type !== 'input' && type !== 'textarea' && type !== 'select') {
+ return;
+ }
+
+ if (props != null && props.value === null && !didWarnValueNull) {
+ didWarnValueNull = true;
+ if (type === 'select' && props.multiple) {
+ warning$1(false, '`value` prop on `%s` should not be null. ' + 'Consider using an empty array when `multiple` is set to `true` ' + 'to clear the component or `undefined` for uncontrolled components.', type);
+ } else {
+ warning$1(false, '`value` prop on `%s` should not be null. ' + 'Consider using an empty string to clear the component or `undefined` ' + 'for uncontrolled components.', type);
+ }
+ }
+}
+
+var validateProperty$1 = function () {};
+
+{
+ var warnedProperties$1 = {};
+ var _hasOwnProperty = Object.prototype.hasOwnProperty;
+ var EVENT_NAME_REGEX = /^on./;
+ var INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
+ var rARIA$1 = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
+ var rARIACamel$1 = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+ validateProperty$1 = function (tagName, name, value, canUseEventSystem) {
+ if (_hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) {
+ return true;
+ }
+
+ var lowerCasedName = name.toLowerCase();
+ if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') {
+ warning$1(false, 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.');
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ // We can't rely on the event system being injected on the server.
+ if (canUseEventSystem) {
+ if (registrationNameModules.hasOwnProperty(name)) {
+ return true;
+ }
+ var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null;
+ if (registrationName != null) {
+ warning$1(false, 'Invalid event handler property `%s`. Did you mean `%s`?', name, registrationName);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+ if (EVENT_NAME_REGEX.test(name)) {
+ warning$1(false, 'Unknown event handler property `%s`. It will be ignored.', name);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+ } else if (EVENT_NAME_REGEX.test(name)) {
+ // If no event plugins have been injected, we are in a server environment.
+ // So we can't tell if the event name is correct for sure, but we can filter
+ // out known bad ones like `onclick`. We can't suggest a specific replacement though.
+ if (INVALID_EVENT_NAME_REGEX.test(name)) {
+ warning$1(false, 'Invalid event handler property `%s`. ' + 'React events use the camelCase naming convention, for example `onClick`.', name);
+ }
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ // Let the ARIA attribute hook validate ARIA attributes
+ if (rARIA$1.test(name) || rARIACamel$1.test(name)) {
+ return true;
+ }
+
+ if (lowerCasedName === 'innerhtml') {
+ warning$1(false, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.');
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (lowerCasedName === 'aria') {
+ warning$1(false, 'The `aria` attribute is reserved for future use in React. ' + 'Pass individual `aria-` attributes instead.');
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (lowerCasedName === 'is' && value !== null && value !== undefined && typeof value !== 'string') {
+ warning$1(false, 'Received a `%s` for a string attribute `is`. If this is expected, cast ' + 'the value to a string.', typeof value);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (typeof value === 'number' && isNaN(value)) {
+ warning$1(false, 'Received NaN for the `%s` attribute. If this is expected, cast ' + 'the value to a string.', name);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ var propertyInfo = getPropertyInfo(name);
+ var isReserved = propertyInfo !== null && propertyInfo.type === RESERVED;
+
+ // Known attributes should match the casing specified in the property config.
+ if (possibleStandardNames.hasOwnProperty(lowerCasedName)) {
+ var standardName = possibleStandardNames[lowerCasedName];
+ if (standardName !== name) {
+ warning$1(false, 'Invalid DOM property `%s`. Did you mean `%s`?', name, standardName);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+ } else if (!isReserved && name !== lowerCasedName) {
+ // Unknown attributes should have lowercase casing since that's how they
+ // will be cased anyway with server rendering.
+ warning$1(false, 'React does not recognize the `%s` prop on a DOM element. If you ' + 'intentionally want it to appear in the DOM as a custom ' + 'attribute, spell it as lowercase `%s` instead. ' + 'If you accidentally passed it from a parent component, remove ' + 'it from the DOM element.', name, lowerCasedName);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (typeof value === 'boolean' && shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) {
+ if (value) {
+ warning$1(false, 'Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.', value, name, name, value, name);
+ } else {
+ warning$1(false, 'Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', value, name, name, value, name, name, name);
+ }
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ // Now that we've validated casing, do not validate
+ // data types for reserved props
+ if (isReserved) {
+ return true;
+ }
+
+ // Warn when a known attribute is a bad type
+ if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) {
+ warnedProperties$1[name] = true;
+ return false;
+ }
+
+ // Warn when passing the strings 'false' or 'true' into a boolean prop
+ if ((value === 'false' || value === 'true') && propertyInfo !== null && propertyInfo.type === BOOLEAN) {
+ warning$1(false, 'Received the string `%s` for the boolean attribute `%s`. ' + '%s ' + 'Did you mean %s={%s}?', value, name, value === 'false' ? 'The browser will interpret it as a truthy value.' : 'Although this works, it will not work as expected if you pass the string "false".', name, value);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ return true;
+ };
+}
+
+var warnUnknownProperties = function (type, props, canUseEventSystem) {
+ var unknownProps = [];
+ for (var key in props) {
+ var isValid = validateProperty$1(type, key, props[key], canUseEventSystem);
+ if (!isValid) {
+ unknownProps.push(key);
+ }
+ }
+
+ var unknownPropString = unknownProps.map(function (prop) {
+ return '`' + prop + '`';
+ }).join(', ');
+ if (unknownProps.length === 1) {
+ warning$1(false, 'Invalid value for prop %s on <%s> tag. Either remove it from the element, ' + 'or pass a string or number value to keep it in the DOM. ' + 'For details, see https://fb.me/react-attribute-behavior', unknownPropString, type);
+ } else if (unknownProps.length > 1) {
+ warning$1(false, 'Invalid values for props %s on <%s> tag. Either remove them from the element, ' + 'or pass a string or number value to keep them in the DOM. ' + 'For details, see https://fb.me/react-attribute-behavior', unknownPropString, type);
+ }
+};
+
+function validateProperties$2(type, props, canUseEventSystem) {
+ if (isCustomComponent(type, props)) {
+ return;
+ }
+ warnUnknownProperties(type, props, canUseEventSystem);
+}
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+var didWarnInvalidHydration = false;
+var didWarnShadyDOM = false;
+
+var DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
+var SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
+var SUPPRESS_HYDRATION_WARNING$1 = 'suppressHydrationWarning';
+var AUTOFOCUS = 'autoFocus';
+var CHILDREN = 'children';
+var STYLE$1 = 'style';
+var HTML = '__html';
+
+var HTML_NAMESPACE = Namespaces.html;
+
+
+var warnedUnknownTags = void 0;
+var suppressHydrationWarning = void 0;
+
+var validatePropertiesInDevelopment = void 0;
+var warnForTextDifference = void 0;
+var warnForPropDifference = void 0;
+var warnForExtraAttributes = void 0;
+var warnForInvalidEventListener = void 0;
+var canDiffStyleForHydrationWarning = void 0;
+
+var normalizeMarkupForTextOrAttribute = void 0;
+var normalizeHTML = void 0;
+
+{
+ warnedUnknownTags = {
+ // Chrome is the only major browser not shipping <time>. But as of July
+ // 2017 it intends to ship it due to widespread usage. We intentionally
+ // *don't* warn for <time> even if it's unrecognized by Chrome because
+ // it soon will be, and many apps have been using it anyway.
+ time: true,
+ // There are working polyfills for <dialog>. Let people use it.
+ dialog: true,
+ // Electron ships a custom <webview> tag to display external web content in
+ // an isolated frame and process.
+ // This tag is not present in non Electron environments such as JSDom which
+ // is often used for testing purposes.
+ // @see https://electronjs.org/docs/api/webview-tag
+ webview: true
+ };
+
+ validatePropertiesInDevelopment = function (type, props) {
+ validateProperties(type, props);
+ validateProperties$1(type, props);
+ validateProperties$2(type, props, /* canUseEventSystem */true);
+ };
+
+ // IE 11 parses & normalizes the style attribute as opposed to other
+ // browsers. It adds spaces and sorts the properties in some
+ // non-alphabetical order. Handling that would require sorting CSS
+ // properties in the client & server versions or applying
+ // `expectedStyle` to a temporary DOM node to read its `style` attribute
+ // normalized. Since it only affects IE, we're skipping style warnings
+ // in that browser completely in favor of doing all that work.
+ // See https://github.com/facebook/react/issues/11807
+ canDiffStyleForHydrationWarning = canUseDOM && !document.documentMode;
+
+ // HTML parsing normalizes CR and CRLF to LF.
+ // It also can turn \u0000 into \uFFFD inside attributes.
+ // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream
+ // If we have a mismatch, it might be caused by that.
+ // We will still patch up in this case but not fire the warning.
+ var NORMALIZE_NEWLINES_REGEX = /\r\n?/g;
+ var NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g;
+
+ normalizeMarkupForTextOrAttribute = function (markup) {
+ var markupString = typeof markup === 'string' ? markup : '' + markup;
+ return markupString.replace(NORMALIZE_NEWLINES_REGEX, '\n').replace(NORMALIZE_NULL_AND_REPLACEMENT_REGEX, '');
+ };
+
+ warnForTextDifference = function (serverText, clientText) {
+ if (didWarnInvalidHydration) {
+ return;
+ }
+ var normalizedClientText = normalizeMarkupForTextOrAttribute(clientText);
+ var normalizedServerText = normalizeMarkupForTextOrAttribute(serverText);
+ if (normalizedServerText === normalizedClientText) {
+ return;
+ }
+ didWarnInvalidHydration = true;
+ warningWithoutStack$1(false, 'Text content did not match. Server: "%s" Client: "%s"', normalizedServerText, normalizedClientText);
+ };
+
+ warnForPropDifference = function (propName, serverValue, clientValue) {
+ if (didWarnInvalidHydration) {
+ return;
+ }
+ var normalizedClientValue = normalizeMarkupForTextOrAttribute(clientValue);
+ var normalizedServerValue = normalizeMarkupForTextOrAttribute(serverValue);
+ if (normalizedServerValue === normalizedClientValue) {
+ return;
+ }
+ didWarnInvalidHydration = true;
+ warningWithoutStack$1(false, 'Prop `%s` did not match. Server: %s Client: %s', propName, JSON.stringify(normalizedServerValue), JSON.stringify(normalizedClientValue));
+ };
+
+ warnForExtraAttributes = function (attributeNames) {
+ if (didWarnInvalidHydration) {
+ return;
+ }
+ didWarnInvalidHydration = true;
+ var names = [];
+ attributeNames.forEach(function (name) {
+ names.push(name);
+ });
+ warningWithoutStack$1(false, 'Extra attributes from the server: %s', names);
+ };
+
+ warnForInvalidEventListener = function (registrationName, listener) {
+ if (listener === false) {
+ warning$1(false, 'Expected `%s` listener to be a function, instead got `false`.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', registrationName, registrationName, registrationName);
+ } else {
+ warning$1(false, 'Expected `%s` listener to be a function, instead got a value of `%s` type.', registrationName, typeof listener);
+ }
+ };
+
+ // Parse the HTML and read it back to normalize the HTML string so that it
+ // can be used for comparison.
+ normalizeHTML = function (parent, html) {
+ // We could have created a separate document here to avoid
+ // re-initializing custom elements if they exist. But this breaks
+ // how <noscript> is being handled. So we use the same document.
+ // See the discussion in https://github.com/facebook/react/pull/11157.
+ var testElement = parent.namespaceURI === HTML_NAMESPACE ? parent.ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', parent.tagName) : parent.ownerDocument.createElementNS(parent.namespaceURI, parent.tagName);
+ testElement.innerHTML = html;
+ return testElement.innerHTML;
+ };
+}
+
+function ensureListeningTo(rootContainerElement, registrationName) {
+ var isDocumentOrFragment = rootContainerElement.nodeType === DOCUMENT_NODE || rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
+ var doc = isDocumentOrFragment ? rootContainerElement : rootContainerElement.ownerDocument;
+ listenTo(registrationName, doc);
+}
+
+function getOwnerDocumentFromRootContainer(rootContainerElement) {
+ return rootContainerElement.nodeType === DOCUMENT_NODE ? rootContainerElement : rootContainerElement.ownerDocument;
+}
+
+function noop() {}
+
+function trapClickOnNonInteractiveElement(node) {
+ // Mobile Safari does not fire properly bubble click events on
+ // non-interactive elements, which means delegated click listeners do not
+ // fire. The workaround for this bug involves attaching an empty click
+ // listener on the target node.
+ // http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
+ // Just set it using the onclick property so that we don't have to manage any
+ // bookkeeping for it. Not sure if we need to clear it when the listener is
+ // removed.
+ // TODO: Only do this for the relevant Safaris maybe?
+ node.onclick = noop;
+}
+
+function setInitialDOMProperties(tag, domElement, rootContainerElement, nextProps, isCustomComponentTag) {
+ for (var propKey in nextProps) {
+ if (!nextProps.hasOwnProperty(propKey)) {
+ continue;
+ }
+ var nextProp = nextProps[propKey];
+ if (propKey === STYLE$1) {
+ {
+ if (nextProp) {
+ // Freeze the next style object so that we can assume it won't be
+ // mutated. We have already warned for this in the past.
+ Object.freeze(nextProp);
+ }
+ }
+ // Relies on `updateStylesByID` not mutating `styleUpdates`.
+ setValueForStyles(domElement, nextProp);
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ var nextHtml = nextProp ? nextProp[HTML] : undefined;
+ if (nextHtml != null) {
+ setInnerHTML(domElement, nextHtml);
+ }
+ } else if (propKey === CHILDREN) {
+ if (typeof nextProp === 'string') {
+ // Avoid setting initial textContent when the text is empty. In IE11 setting
+ // textContent on a <textarea> will cause the placeholder to not
+ // show within the <textarea> until it has been focused and blurred again.
+ // https://github.com/facebook/react/issues/6731#issuecomment-254874553
+ var canSetTextContent = tag !== 'textarea' || nextProp !== '';
+ if (canSetTextContent) {
+ setTextContent(domElement, nextProp);
+ }
+ } else if (typeof nextProp === 'number') {
+ setTextContent(domElement, '' + nextProp);
+ }
+ } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING$1) {
+ // Noop
+ } else if (propKey === AUTOFOCUS) {
+ // We polyfill it separately on the client during commit.
+ // We could have excluded it in the property list instead of
+ // adding a special case here, but then it wouldn't be emitted
+ // on server rendering (but we *do* want to emit it in SSR).
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ if (nextProp != null) {
+ if (true && typeof nextProp !== 'function') {
+ warnForInvalidEventListener(propKey, nextProp);
+ }
+ ensureListeningTo(rootContainerElement, propKey);
+ }
+ } else if (nextProp != null) {
+ setValueForProperty(domElement, propKey, nextProp, isCustomComponentTag);
+ }
+ }
+}
+
+function updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag) {
+ // TODO: Handle wasCustomComponentTag
+ for (var i = 0; i < updatePayload.length; i += 2) {
+ var propKey = updatePayload[i];
+ var propValue = updatePayload[i + 1];
+ if (propKey === STYLE$1) {
+ setValueForStyles(domElement, propValue);
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ setInnerHTML(domElement, propValue);
+ } else if (propKey === CHILDREN) {
+ setTextContent(domElement, propValue);
+ } else {
+ setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
+ }
+ }
+}
+
+function createElement(type, props, rootContainerElement, parentNamespace) {
+ var isCustomComponentTag = void 0;
+
+ // We create tags in the namespace of their parent container, except HTML
+ // tags get no namespace.
+ var ownerDocument = getOwnerDocumentFromRootContainer(rootContainerElement);
+ var domElement = void 0;
+ var namespaceURI = parentNamespace;
+ if (namespaceURI === HTML_NAMESPACE) {
+ namespaceURI = getIntrinsicNamespace(type);
+ }
+ if (namespaceURI === HTML_NAMESPACE) {
+ {
+ isCustomComponentTag = isCustomComponent(type, props);
+ // Should this check be gated by parent namespace? Not sure we want to
+ // allow <SVG> or <mATH>.
+ !(isCustomComponentTag || type === type.toLowerCase()) ? warning$1(false, '<%s /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.', type) : void 0;
+ }
+
+ if (type === 'script') {
+ // Create the script via .innerHTML so its "parser-inserted" flag is
+ // set to true and it does not execute
+ var div = ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
+ // This is guaranteed to yield a script element.
+ var firstChild = div.firstChild;
+ domElement = div.removeChild(firstChild);
+ } else if (typeof props.is === 'string') {
+ // $FlowIssue `createElement` should be updated for Web Components
+ domElement = ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', type, { is: props.is });
+ } else {
+ // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.
+ // See discussion in https://github.com/facebook/react/pull/6896
+ // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240
+ domElement = ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', type);
+ // Normally attributes are assigned in `setInitialDOMProperties`, however the `multiple` and `size`
+ // attributes on `select`s needs to be added before `option`s are inserted.
+ // This prevents:
+ // - a bug where the `select` does not scroll to the correct option because singular
+ // `select` elements automatically pick the first item #13222
+ // - a bug where the `select` set the first item as selected despite the `size` attribute #14239
+ // See https://github.com/facebook/react/issues/13222
+ // and https://github.com/facebook/react/issues/14239
+ if (type === 'select') {
+ var node = domElement;
+ if (props.multiple) {
+ node.multiple = true;
+ } else if (props.size) {
+ // Setting a size greater than 1 causes a select to behave like `multiple=true`, where
+ // it is possible that no option is selected.
+ //
+ // This is only necessary when a select in "single selection mode".
+ node.size = props.size;
+ }
+ }
+ }
+ } else {
+ domElement = ownerDocument.createElementNS(namespaceURI, type);
+ }
+
+ {
+ if (namespaceURI === HTML_NAMESPACE) {
+ if (!isCustomComponentTag && Object.prototype.toString.call(domElement) === '[object HTMLUnknownElement]' && !Object.prototype.hasOwnProperty.call(warnedUnknownTags, type)) {
+ warnedUnknownTags[type] = true;
+ warning$1(false, 'The tag <%s> is unrecognized in this browser. ' + 'If you meant to render a React component, start its name with ' + 'an uppercase letter.', type);
+ }
+ }
+ }
+
+ return domElement;
+}
+
+function createTextNode(text, rootContainerElement) {
+ return getOwnerDocumentFromRootContainer(rootContainerElement).createTextNode(text);
+}
+
+function setInitialProperties(domElement, tag, rawProps, rootContainerElement) {
+ var isCustomComponentTag = isCustomComponent(tag, rawProps);
+ {
+ validatePropertiesInDevelopment(tag, rawProps);
+ if (isCustomComponentTag && !didWarnShadyDOM && domElement.shadyRoot) {
+ warning$1(false, '%s is using shady DOM. Using shady DOM with React can ' + 'cause things to break subtly.', getCurrentFiberOwnerNameInDevOrNull() || 'A component');
+ didWarnShadyDOM = true;
+ }
+ }
+
+ // TODO: Make sure that we check isMounted before firing any of these events.
+ var props = void 0;
+ switch (tag) {
+ case 'iframe':
+ case 'object':
+ trapBubbledEvent(TOP_LOAD, domElement);
+ props = rawProps;
+ break;
+ case 'video':
+ case 'audio':
+ // Create listener for each media event
+ for (var i = 0; i < mediaEventTypes.length; i++) {
+ trapBubbledEvent(mediaEventTypes[i], domElement);
+ }
+ props = rawProps;
+ break;
+ case 'source':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ props = rawProps;
+ break;
+ case 'img':
+ case 'image':
+ case 'link':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ trapBubbledEvent(TOP_LOAD, domElement);
+ props = rawProps;
+ break;
+ case 'form':
+ trapBubbledEvent(TOP_RESET, domElement);
+ trapBubbledEvent(TOP_SUBMIT, domElement);
+ props = rawProps;
+ break;
+ case 'details':
+ trapBubbledEvent(TOP_TOGGLE, domElement);
+ props = rawProps;
+ break;
+ case 'input':
+ initWrapperState(domElement, rawProps);
+ props = getHostProps(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'option':
+ validateProps(domElement, rawProps);
+ props = getHostProps$1(domElement, rawProps);
+ break;
+ case 'select':
+ initWrapperState$1(domElement, rawProps);
+ props = getHostProps$2(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'textarea':
+ initWrapperState$2(domElement, rawProps);
+ props = getHostProps$3(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ default:
+ props = rawProps;
+ }
+
+ assertValidProps(tag, props);
+
+ setInitialDOMProperties(tag, domElement, rootContainerElement, props, isCustomComponentTag);
+
+ switch (tag) {
+ case 'input':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper(domElement, rawProps, false);
+ break;
+ case 'textarea':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper$3(domElement, rawProps);
+ break;
+ case 'option':
+ postMountWrapper$1(domElement, rawProps);
+ break;
+ case 'select':
+ postMountWrapper$2(domElement, rawProps);
+ break;
+ default:
+ if (typeof props.onClick === 'function') {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(domElement);
+ }
+ break;
+ }
+}
+
+// Calculate the diff between the two objects.
+function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {
+ {
+ validatePropertiesInDevelopment(tag, nextRawProps);
+ }
+
+ var updatePayload = null;
+
+ var lastProps = void 0;
+ var nextProps = void 0;
+ switch (tag) {
+ case 'input':
+ lastProps = getHostProps(domElement, lastRawProps);
+ nextProps = getHostProps(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ case 'option':
+ lastProps = getHostProps$1(domElement, lastRawProps);
+ nextProps = getHostProps$1(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ case 'select':
+ lastProps = getHostProps$2(domElement, lastRawProps);
+ nextProps = getHostProps$2(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ case 'textarea':
+ lastProps = getHostProps$3(domElement, lastRawProps);
+ nextProps = getHostProps$3(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ default:
+ lastProps = lastRawProps;
+ nextProps = nextRawProps;
+ if (typeof lastProps.onClick !== 'function' && typeof nextProps.onClick === 'function') {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(domElement);
+ }
+ break;
+ }
+
+ assertValidProps(tag, nextProps);
+
+ var propKey = void 0;
+ var styleName = void 0;
+ var styleUpdates = null;
+ for (propKey in lastProps) {
+ if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {
+ continue;
+ }
+ if (propKey === STYLE$1) {
+ var lastStyle = lastProps[propKey];
+ for (styleName in lastStyle) {
+ if (lastStyle.hasOwnProperty(styleName)) {
+ if (!styleUpdates) {
+ styleUpdates = {};
+ }
+ styleUpdates[styleName] = '';
+ }
+ }
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML || propKey === CHILDREN) {
+ // Noop. This is handled by the clear text mechanism.
+ } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING$1) {
+ // Noop
+ } else if (propKey === AUTOFOCUS) {
+ // Noop. It doesn't work on updates anyway.
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ // This is a special case. If any listener updates we need to ensure
+ // that the "current" fiber pointer gets updated so we need a commit
+ // to update this element.
+ if (!updatePayload) {
+ updatePayload = [];
+ }
+ } else {
+ // For all other deleted properties we add it to the queue. We use
+ // the whitelist in the commit phase instead.
+ (updatePayload = updatePayload || []).push(propKey, null);
+ }
+ }
+ for (propKey in nextProps) {
+ var nextProp = nextProps[propKey];
+ var lastProp = lastProps != null ? lastProps[propKey] : undefined;
+ if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) {
+ continue;
+ }
+ if (propKey === STYLE$1) {
+ {
+ if (nextProp) {
+ // Freeze the next style object so that we can assume it won't be
+ // mutated. We have already warned for this in the past.
+ Object.freeze(nextProp);
+ }
+ }
+ if (lastProp) {
+ // Unset styles on `lastProp` but not on `nextProp`.
+ for (styleName in lastProp) {
+ if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {
+ if (!styleUpdates) {
+ styleUpdates = {};
+ }
+ styleUpdates[styleName] = '';
+ }
+ }
+ // Update styles that changed since `lastProp`.
+ for (styleName in nextProp) {
+ if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {
+ if (!styleUpdates) {
+ styleUpdates = {};
+ }
+ styleUpdates[styleName] = nextProp[styleName];
+ }
+ }
+ } else {
+ // Relies on `updateStylesByID` not mutating `styleUpdates`.
+ if (!styleUpdates) {
+ if (!updatePayload) {
+ updatePayload = [];
+ }
+ updatePayload.push(propKey, styleUpdates);
+ }
+ styleUpdates = nextProp;
+ }
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ var nextHtml = nextProp ? nextProp[HTML] : undefined;
+ var lastHtml = lastProp ? lastProp[HTML] : undefined;
+ if (nextHtml != null) {
+ if (lastHtml !== nextHtml) {
+ (updatePayload = updatePayload || []).push(propKey, '' + nextHtml);
+ }
+ } else {
+ // TODO: It might be too late to clear this if we have children
+ // inserted already.
+ }
+ } else if (propKey === CHILDREN) {
+ if (lastProp !== nextProp && (typeof nextProp === 'string' || typeof nextProp === 'number')) {
+ (updatePayload = updatePayload || []).push(propKey, '' + nextProp);
+ }
+ } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING$1) {
+ // Noop
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ if (nextProp != null) {
+ // We eagerly listen to this even though we haven't committed yet.
+ if (true && typeof nextProp !== 'function') {
+ warnForInvalidEventListener(propKey, nextProp);
+ }
+ ensureListeningTo(rootContainerElement, propKey);
+ }
+ if (!updatePayload && lastProp !== nextProp) {
+ // This is a special case. If any listener updates we need to ensure
+ // that the "current" props pointer gets updated so we need a commit
+ // to update this element.
+ updatePayload = [];
+ }
+ } else {
+ // For any other property we always add it to the queue and then we
+ // filter it out using the whitelist during the commit.
+ (updatePayload = updatePayload || []).push(propKey, nextProp);
+ }
+ }
+ if (styleUpdates) {
+ {
+ validateShorthandPropertyCollisionInDev(styleUpdates, nextProps[STYLE$1]);
+ }
+ (updatePayload = updatePayload || []).push(STYLE$1, styleUpdates);
+ }
+ return updatePayload;
+}
+
+// Apply the diff.
+function updateProperties(domElement, updatePayload, tag, lastRawProps, nextRawProps) {
+ // Update checked *before* name.
+ // In the middle of an update, it is possible to have multiple checked.
+ // When a checked radio tries to change name, browser makes another radio's checked false.
+ if (tag === 'input' && nextRawProps.type === 'radio' && nextRawProps.name != null) {
+ updateChecked(domElement, nextRawProps);
+ }
+
+ var wasCustomComponentTag = isCustomComponent(tag, lastRawProps);
+ var isCustomComponentTag = isCustomComponent(tag, nextRawProps);
+ // Apply the diff.
+ updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag);
+
+ // TODO: Ensure that an update gets scheduled if any of the special props
+ // changed.
+ switch (tag) {
+ case 'input':
+ // Update the wrapper around inputs *after* updating props. This has to
+ // happen after `updateDOMProperties`. Otherwise HTML5 input validations
+ // raise warnings and prevent the new value from being assigned.
+ updateWrapper(domElement, nextRawProps);
+ break;
+ case 'textarea':
+ updateWrapper$1(domElement, nextRawProps);
+ break;
+ case 'select':
+ // <select> value update needs to occur after <option> children
+ // reconciliation
+ postUpdateWrapper(domElement, nextRawProps);
+ break;
+ }
+}
+
+function getPossibleStandardName(propName) {
+ {
+ var lowerCasedName = propName.toLowerCase();
+ if (!possibleStandardNames.hasOwnProperty(lowerCasedName)) {
+ return null;
+ }
+ return possibleStandardNames[lowerCasedName] || null;
+ }
+ return null;
+}
+
+function diffHydratedProperties(domElement, tag, rawProps, parentNamespace, rootContainerElement) {
+ var isCustomComponentTag = void 0;
+ var extraAttributeNames = void 0;
+
+ {
+ suppressHydrationWarning = rawProps[SUPPRESS_HYDRATION_WARNING$1] === true;
+ isCustomComponentTag = isCustomComponent(tag, rawProps);
+ validatePropertiesInDevelopment(tag, rawProps);
+ if (isCustomComponentTag && !didWarnShadyDOM && domElement.shadyRoot) {
+ warning$1(false, '%s is using shady DOM. Using shady DOM with React can ' + 'cause things to break subtly.', getCurrentFiberOwnerNameInDevOrNull() || 'A component');
+ didWarnShadyDOM = true;
+ }
+ }
+
+ // TODO: Make sure that we check isMounted before firing any of these events.
+ switch (tag) {
+ case 'iframe':
+ case 'object':
+ trapBubbledEvent(TOP_LOAD, domElement);
+ break;
+ case 'video':
+ case 'audio':
+ // Create listener for each media event
+ for (var i = 0; i < mediaEventTypes.length; i++) {
+ trapBubbledEvent(mediaEventTypes[i], domElement);
+ }
+ break;
+ case 'source':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ break;
+ case 'img':
+ case 'image':
+ case 'link':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ trapBubbledEvent(TOP_LOAD, domElement);
+ break;
+ case 'form':
+ trapBubbledEvent(TOP_RESET, domElement);
+ trapBubbledEvent(TOP_SUBMIT, domElement);
+ break;
+ case 'details':
+ trapBubbledEvent(TOP_TOGGLE, domElement);
+ break;
+ case 'input':
+ initWrapperState(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'option':
+ validateProps(domElement, rawProps);
+ break;
+ case 'select':
+ initWrapperState$1(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'textarea':
+ initWrapperState$2(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ }
+
+ assertValidProps(tag, rawProps);
+
+ {
+ extraAttributeNames = new Set();
+ var attributes = domElement.attributes;
+ for (var _i = 0; _i < attributes.length; _i++) {
+ var name = attributes[_i].name.toLowerCase();
+ switch (name) {
+ // Built-in SSR attribute is whitelisted
+ case 'data-reactroot':
+ break;
+ // Controlled attributes are not validated
+ // TODO: Only ignore them on controlled tags.
+ case 'value':
+ break;
+ case 'checked':
+ break;
+ case 'selected':
+ break;
+ default:
+ // Intentionally use the original name.
+ // See discussion in https://github.com/facebook/react/pull/10676.
+ extraAttributeNames.add(attributes[_i].name);
+ }
+ }
+ }
+
+ var updatePayload = null;
+ for (var propKey in rawProps) {
+ if (!rawProps.hasOwnProperty(propKey)) {
+ continue;
+ }
+ var nextProp = rawProps[propKey];
+ if (propKey === CHILDREN) {
+ // For text content children we compare against textContent. This
+ // might match additional HTML that is hidden when we read it using
+ // textContent. E.g. "foo" will match "f<span>oo</span>" but that still
+ // satisfies our requirement. Our requirement is not to produce perfect
+ // HTML and attributes. Ideally we should preserve structure but it's
+ // ok not to if the visible content is still enough to indicate what
+ // even listeners these nodes might be wired up to.
+ // TODO: Warn if there is more than a single textNode as a child.
+ // TODO: Should we use domElement.firstChild.nodeValue to compare?
+ if (typeof nextProp === 'string') {
+ if (domElement.textContent !== nextProp) {
+ if (true && !suppressHydrationWarning) {
+ warnForTextDifference(domElement.textContent, nextProp);
+ }
+ updatePayload = [CHILDREN, nextProp];
+ }
+ } else if (typeof nextProp === 'number') {
+ if (domElement.textContent !== '' + nextProp) {
+ if (true && !suppressHydrationWarning) {
+ warnForTextDifference(domElement.textContent, nextProp);
+ }
+ updatePayload = [CHILDREN, '' + nextProp];
+ }
+ }
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ if (nextProp != null) {
+ if (true && typeof nextProp !== 'function') {
+ warnForInvalidEventListener(propKey, nextProp);
+ }
+ ensureListeningTo(rootContainerElement, propKey);
+ }
+ } else if (true &&
+ // Convince Flow we've calculated it (it's DEV-only in this method.)
+ typeof isCustomComponentTag === 'boolean') {
+ // Validate that the properties correspond to their expected values.
+ var serverValue = void 0;
+ var propertyInfo = getPropertyInfo(propKey);
+ if (suppressHydrationWarning) {
+ // Don't bother comparing. We're ignoring all these warnings.
+ } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING$1 ||
+ // Controlled attributes are not validated
+ // TODO: Only ignore them on controlled tags.
+ propKey === 'value' || propKey === 'checked' || propKey === 'selected') {
+ // Noop
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ var serverHTML = domElement.innerHTML;
+ var nextHtml = nextProp ? nextProp[HTML] : undefined;
+ var expectedHTML = normalizeHTML(domElement, nextHtml != null ? nextHtml : '');
+ if (expectedHTML !== serverHTML) {
+ warnForPropDifference(propKey, serverHTML, expectedHTML);
+ }
+ } else if (propKey === STYLE$1) {
+ // $FlowFixMe - Should be inferred as not undefined.
+ extraAttributeNames.delete(propKey);
+
+ if (canDiffStyleForHydrationWarning) {
+ var expectedStyle = createDangerousStringForStyles(nextProp);
+ serverValue = domElement.getAttribute('style');
+ if (expectedStyle !== serverValue) {
+ warnForPropDifference(propKey, serverValue, expectedStyle);
+ }
+ }
+ } else if (isCustomComponentTag) {
+ // $FlowFixMe - Should be inferred as not undefined.
+ extraAttributeNames.delete(propKey.toLowerCase());
+ serverValue = getValueForAttribute(domElement, propKey, nextProp);
+
+ if (nextProp !== serverValue) {
+ warnForPropDifference(propKey, serverValue, nextProp);
+ }
+ } else if (!shouldIgnoreAttribute(propKey, propertyInfo, isCustomComponentTag) && !shouldRemoveAttribute(propKey, nextProp, propertyInfo, isCustomComponentTag)) {
+ var isMismatchDueToBadCasing = false;
+ if (propertyInfo !== null) {
+ // $FlowFixMe - Should be inferred as not undefined.
+ extraAttributeNames.delete(propertyInfo.attributeName);
+ serverValue = getValueForProperty(domElement, propKey, nextProp, propertyInfo);
+ } else {
+ var ownNamespace = parentNamespace;
+ if (ownNamespace === HTML_NAMESPACE) {
+ ownNamespace = getIntrinsicNamespace(tag);
+ }
+ if (ownNamespace === HTML_NAMESPACE) {
+ // $FlowFixMe - Should be inferred as not undefined.
+ extraAttributeNames.delete(propKey.toLowerCase());
+ } else {
+ var standardName = getPossibleStandardName(propKey);
+ if (standardName !== null && standardName !== propKey) {
+ // If an SVG prop is supplied with bad casing, it will
+ // be successfully parsed from HTML, but will produce a mismatch
+ // (and would be incorrectly rendered on the client).
+ // However, we already warn about bad casing elsewhere.
+ // So we'll skip the misleading extra mismatch warning in this case.
+ isMismatchDueToBadCasing = true;
+ // $FlowFixMe - Should be inferred as not undefined.
+ extraAttributeNames.delete(standardName);
+ }
+ // $FlowFixMe - Should be inferred as not undefined.
+ extraAttributeNames.delete(propKey);
+ }
+ serverValue = getValueForAttribute(domElement, propKey, nextProp);
+ }
+
+ if (nextProp !== serverValue && !isMismatchDueToBadCasing) {
+ warnForPropDifference(propKey, serverValue, nextProp);
+ }
+ }
+ }
+ }
+
+ {
+ // $FlowFixMe - Should be inferred as not undefined.
+ if (extraAttributeNames.size > 0 && !suppressHydrationWarning) {
+ // $FlowFixMe - Should be inferred as not undefined.
+ warnForExtraAttributes(extraAttributeNames);
+ }
+ }
+
+ switch (tag) {
+ case 'input':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper(domElement, rawProps, true);
+ break;
+ case 'textarea':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper$3(domElement, rawProps);
+ break;
+ case 'select':
+ case 'option':
+ // For input and textarea we current always set the value property at
+ // post mount to force it to diverge from attributes. However, for
+ // option and select we don't quite do the same thing and select
+ // is not resilient to the DOM state changing so we don't do that here.
+ // TODO: Consider not doing this for input and textarea.
+ break;
+ default:
+ if (typeof rawProps.onClick === 'function') {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(domElement);
+ }
+ break;
+ }
+
+ return updatePayload;
+}
+
+function diffHydratedText(textNode, text) {
+ var isDifferent = textNode.nodeValue !== text;
+ return isDifferent;
+}
+
+function warnForUnmatchedText(textNode, text) {
+ {
+ warnForTextDifference(textNode.nodeValue, text);
+ }
+}
+
+function warnForDeletedHydratableElement(parentNode, child) {
+ {
+ if (didWarnInvalidHydration) {
+ return;
+ }
+ didWarnInvalidHydration = true;
+ warningWithoutStack$1(false, 'Did not expect server HTML to contain a <%s> in <%s>.', child.nodeName.toLowerCase(), parentNode.nodeName.toLowerCase());
+ }
+}
+
+function warnForDeletedHydratableText(parentNode, child) {
+ {
+ if (didWarnInvalidHydration) {
+ return;
+ }
+ didWarnInvalidHydration = true;
+ warningWithoutStack$1(false, 'Did not expect server HTML to contain the text node "%s" in <%s>.', child.nodeValue, parentNode.nodeName.toLowerCase());
+ }
+}
+
+function warnForInsertedHydratedElement(parentNode, tag, props) {
+ {
+ if (didWarnInvalidHydration) {
+ return;
+ }
+ didWarnInvalidHydration = true;
+ warningWithoutStack$1(false, 'Expected server HTML to contain a matching <%s> in <%s>.', tag, parentNode.nodeName.toLowerCase());
+ }
+}
+
+function warnForInsertedHydratedText(parentNode, text) {
+ {
+ if (text === '') {
+ // We expect to insert empty text nodes since they're not represented in
+ // the HTML.
+ // TODO: Remove this special case if we can just avoid inserting empty
+ // text nodes.
+ return;
+ }
+ if (didWarnInvalidHydration) {
+ return;
+ }
+ didWarnInvalidHydration = true;
+ warningWithoutStack$1(false, 'Expected server HTML to contain a matching text node for "%s" in <%s>.', text, parentNode.nodeName.toLowerCase());
+ }
+}
+
+function restoreControlledState$1(domElement, tag, props) {
+ switch (tag) {
+ case 'input':
+ restoreControlledState(domElement, props);
+ return;
+ case 'textarea':
+ restoreControlledState$3(domElement, props);
+ return;
+ case 'select':
+ restoreControlledState$2(domElement, props);
+ return;
+ }
+}
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+var validateDOMNesting = function () {};
+var updatedAncestorInfo = function () {};
+
+{
+ // This validation code was written based on the HTML5 parsing spec:
+ // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
+ //
+ // Note: this does not catch all invalid nesting, nor does it try to (as it's
+ // not clear what practical benefit doing so provides); instead, we warn only
+ // for cases where the parser will give a parse tree differing from what React
+ // intended. For example, <b><div></div></b> is invalid but we don't warn
+ // because it still parses correctly; we do warn for other cases like nested
+ // <p> tags where the beginning of the second element implicitly closes the
+ // first, causing a confusing mess.
+
+ // https://html.spec.whatwg.org/multipage/syntax.html#special
+ var specialTags = ['address', 'applet', 'area', 'article', 'aside', 'base', 'basefont', 'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe', 'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee', 'menu', 'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'p', 'param', 'plaintext', 'pre', 'script', 'section', 'select', 'source', 'style', 'summary', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr', 'xmp'];
+
+ // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
+ var inScopeTags = ['applet', 'caption', 'html', 'table', 'td', 'th', 'marquee', 'object', 'template',
+
+ // https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
+ // TODO: Distinguish by namespace here -- for <title>, including it here
+ // errs on the side of fewer warnings
+ 'foreignObject', 'desc', 'title'];
+
+ // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
+ var buttonScopeTags = inScopeTags.concat(['button']);
+
+ // https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
+ var impliedEndTags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt'];
+
+ var emptyAncestorInfo = {
+ current: null,
+
+ formTag: null,
+ aTagInScope: null,
+ buttonTagInScope: null,
+ nobrTagInScope: null,
+ pTagInButtonScope: null,
+
+ listItemTagAutoclosing: null,
+ dlItemTagAutoclosing: null
+ };
+
+ updatedAncestorInfo = function (oldInfo, tag) {
+ var ancestorInfo = _assign({}, oldInfo || emptyAncestorInfo);
+ var info = { tag: tag };
+
+ if (inScopeTags.indexOf(tag) !== -1) {
+ ancestorInfo.aTagInScope = null;
+ ancestorInfo.buttonTagInScope = null;
+ ancestorInfo.nobrTagInScope = null;
+ }
+ if (buttonScopeTags.indexOf(tag) !== -1) {
+ ancestorInfo.pTagInButtonScope = null;
+ }
+
+ // See rules for 'li', 'dd', 'dt' start tags in
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
+ if (specialTags.indexOf(tag) !== -1 && tag !== 'address' && tag !== 'div' && tag !== 'p') {
+ ancestorInfo.listItemTagAutoclosing = null;
+ ancestorInfo.dlItemTagAutoclosing = null;
+ }
+
+ ancestorInfo.current = info;
+
+ if (tag === 'form') {
+ ancestorInfo.formTag = info;
+ }
+ if (tag === 'a') {
+ ancestorInfo.aTagInScope = info;
+ }
+ if (tag === 'button') {
+ ancestorInfo.buttonTagInScope = info;
+ }
+ if (tag === 'nobr') {
+ ancestorInfo.nobrTagInScope = info;
+ }
+ if (tag === 'p') {
+ ancestorInfo.pTagInButtonScope = info;
+ }
+ if (tag === 'li') {
+ ancestorInfo.listItemTagAutoclosing = info;
+ }
+ if (tag === 'dd' || tag === 'dt') {
+ ancestorInfo.dlItemTagAutoclosing = info;
+ }
+
+ return ancestorInfo;
+ };
+
+ /**
+ * Returns whether
+ */
+ var isTagValidWithParent = function (tag, parentTag) {
+ // First, let's check if we're in an unusual parsing mode...
+ switch (parentTag) {
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
+ case 'select':
+ return tag === 'option' || tag === 'optgroup' || tag === '#text';
+ case 'optgroup':
+ return tag === 'option' || tag === '#text';
+ // Strictly speaking, seeing an <option> doesn't mean we're in a <select>
+ // but
+ case 'option':
+ return tag === '#text';
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
+ // No special behavior since these rules fall back to "in body" mode for
+ // all except special table nodes which cause bad parsing behavior anyway.
+
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr
+ case 'tr':
+ return tag === 'th' || tag === 'td' || tag === 'style' || tag === 'script' || tag === 'template';
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody
+ case 'tbody':
+ case 'thead':
+ case 'tfoot':
+ return tag === 'tr' || tag === 'style' || tag === 'script' || tag === 'template';
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup
+ case 'colgroup':
+ return tag === 'col' || tag === 'template';
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable
+ case 'table':
+ return tag === 'caption' || tag === 'colgroup' || tag === 'tbody' || tag === 'tfoot' || tag === 'thead' || tag === 'style' || tag === 'script' || tag === 'template';
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead
+ case 'head':
+ return tag === 'base' || tag === 'basefont' || tag === 'bgsound' || tag === 'link' || tag === 'meta' || tag === 'title' || tag === 'noscript' || tag === 'noframes' || tag === 'style' || tag === 'script' || tag === 'template';
+ // https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
+ case 'html':
+ return tag === 'head' || tag === 'body';
+ case '#document':
+ return tag === 'html';
+ }
+
+ // Probably in the "in body" parsing mode, so we outlaw only tag combos
+ // where the parsing rules cause implicit opens or closes to be added.
+ // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
+ switch (tag) {
+ case 'h1':
+ case 'h2':
+ case 'h3':
+ case 'h4':
+ case 'h5':
+ case 'h6':
+ return parentTag !== 'h1' && parentTag !== 'h2' && parentTag !== 'h3' && parentTag !== 'h4' && parentTag !== 'h5' && parentTag !== 'h6';
+
+ case 'rp':
+ case 'rt':
+ return impliedEndTags.indexOf(parentTag) === -1;
+
+ case 'body':
+ case 'caption':
+ case 'col':
+ case 'colgroup':
+ case 'frame':
+ case 'head':
+ case 'html':
+ case 'tbody':
+ case 'td':
+ case 'tfoot':
+ case 'th':
+ case 'thead':
+ case 'tr':
+ // These tags are only valid with a few parents that have special child
+ // parsing rules -- if we're down here, then none of those matched and
+ // so we allow it only if we don't know what the parent is, as all other
+ // cases are invalid.
+ return parentTag == null;
+ }
+
+ return true;
+ };
+
+ /**
+ * Returns whether
+ */
+ var findInvalidAncestorForTag = function (tag, ancestorInfo) {
+ switch (tag) {
+ case 'address':
+ case 'article':
+ case 'aside':
+ case 'blockquote':
+ case 'center':
+ case 'details':
+ case 'dialog':
+ case 'dir':
+ case 'div':
+ case 'dl':
+ case 'fieldset':
+ case 'figcaption':
+ case 'figure':
+ case 'footer':
+ case 'header':
+ case 'hgroup':
+ case 'main':
+ case 'menu':
+ case 'nav':
+ case 'ol':
+ case 'p':
+ case 'section':
+ case 'summary':
+ case 'ul':
+ case 'pre':
+ case 'listing':
+ case 'table':
+ case 'hr':
+ case 'xmp':
+ case 'h1':
+ case 'h2':
+ case 'h3':
+ case 'h4':
+ case 'h5':
+ case 'h6':
+ return ancestorInfo.pTagInButtonScope;
+
+ case 'form':
+ return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope;
+
+ case 'li':
+ return ancestorInfo.listItemTagAutoclosing;
+
+ case 'dd':
+ case 'dt':
+ return ancestorInfo.dlItemTagAutoclosing;
+
+ case 'button':
+ return ancestorInfo.buttonTagInScope;
+
+ case 'a':
+ // Spec says something about storing a list of markers, but it sounds
+ // equivalent to this check.
+ return ancestorInfo.aTagInScope;
+
+ case 'nobr':
+ return ancestorInfo.nobrTagInScope;
+ }
+
+ return null;
+ };
+
+ var didWarn = {};
+
+ validateDOMNesting = function (childTag, childText, ancestorInfo) {
+ ancestorInfo = ancestorInfo || emptyAncestorInfo;
+ var parentInfo = ancestorInfo.current;
+ var parentTag = parentInfo && parentInfo.tag;
+
+ if (childText != null) {
+ !(childTag == null) ? warningWithoutStack$1(false, 'validateDOMNesting: when childText is passed, childTag should be null') : void 0;
+ childTag = '#text';
+ }
+
+ var invalidParent = isTagValidWithParent(childTag, parentTag) ? null : parentInfo;
+ var invalidAncestor = invalidParent ? null : findInvalidAncestorForTag(childTag, ancestorInfo);
+ var invalidParentOrAncestor = invalidParent || invalidAncestor;
+ if (!invalidParentOrAncestor) {
+ return;
+ }
+
+ var ancestorTag = invalidParentOrAncestor.tag;
+ var addendum = getCurrentFiberStackInDev();
+
+ var warnKey = !!invalidParent + '|' + childTag + '|' + ancestorTag + '|' + addendum;
+ if (didWarn[warnKey]) {
+ return;
+ }
+ didWarn[warnKey] = true;
+
+ var tagDisplayName = childTag;
+ var whitespaceInfo = '';
+ if (childTag === '#text') {
+ if (/\S/.test(childText)) {
+ tagDisplayName = 'Text nodes';
+ } else {
+ tagDisplayName = 'Whitespace text nodes';
+ whitespaceInfo = " Make sure you don't have any extra whitespace between tags on " + 'each line of your source code.';
+ }
+ } else {
+ tagDisplayName = '<' + childTag + '>';
+ }
+
+ if (invalidParent) {
+ var info = '';
+ if (ancestorTag === 'table' && childTag === 'tr') {
+ info += ' Add a <tbody> to your code to match the DOM tree generated by ' + 'the browser.';
+ }
+ warningWithoutStack$1(false, 'validateDOMNesting(...): %s cannot appear as a child of <%s>.%s%s%s', tagDisplayName, ancestorTag, whitespaceInfo, info, addendum);
+ } else {
+ warningWithoutStack$1(false, 'validateDOMNesting(...): %s cannot appear as a descendant of ' + '<%s>.%s', tagDisplayName, ancestorTag, addendum);
+ }
+ };
+}
+
+var ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _ReactInternals$Sched = ReactInternals$1.Scheduler;
+var unstable_cancelCallback = _ReactInternals$Sched.unstable_cancelCallback;
+var unstable_now = _ReactInternals$Sched.unstable_now;
+var unstable_scheduleCallback = _ReactInternals$Sched.unstable_scheduleCallback;
+var unstable_shouldYield = _ReactInternals$Sched.unstable_shouldYield;
+var unstable_getFirstCallbackNode = _ReactInternals$Sched.unstable_getFirstCallbackNode;
+var unstable_runWithPriority = _ReactInternals$Sched.unstable_runWithPriority;
+var unstable_next = _ReactInternals$Sched.unstable_next;
+var unstable_continueExecution = _ReactInternals$Sched.unstable_continueExecution;
+var unstable_pauseExecution = _ReactInternals$Sched.unstable_pauseExecution;
+var unstable_getCurrentPriorityLevel = _ReactInternals$Sched.unstable_getCurrentPriorityLevel;
+var unstable_ImmediatePriority = _ReactInternals$Sched.unstable_ImmediatePriority;
+var unstable_UserBlockingPriority = _ReactInternals$Sched.unstable_UserBlockingPriority;
+var unstable_NormalPriority = _ReactInternals$Sched.unstable_NormalPriority;
+var unstable_LowPriority = _ReactInternals$Sched.unstable_LowPriority;
+var unstable_IdlePriority = _ReactInternals$Sched.unstable_IdlePriority;
+
+// Renderers that don't support persistence
+// can re-export everything from this module.
+
+function shim() {
+ invariant(false, 'The current renderer does not support persistence. This error is likely caused by a bug in React. Please file an issue.');
+}
+
+// Persistence (when unsupported)
+var supportsPersistence = false;
+var cloneInstance = shim;
+var createContainerChildSet = shim;
+var appendChildToContainerChildSet = shim;
+var finalizeContainerChildren = shim;
+var replaceContainerChildren = shim;
+var cloneHiddenInstance = shim;
+var cloneUnhiddenInstance = shim;
+var createHiddenTextInstance = shim;
+
+var SUPPRESS_HYDRATION_WARNING = void 0;
+{
+ SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
+}
+
+var SUSPENSE_START_DATA = '$';
+var SUSPENSE_END_DATA = '/$';
+
+var STYLE = 'style';
+
+var eventsEnabled = null;
+var selectionInformation = null;
+
+function shouldAutoFocusHostComponent(type, props) {
+ switch (type) {
+ case 'button':
+ case 'input':
+ case 'select':
+ case 'textarea':
+ return !!props.autoFocus;
+ }
+ return false;
+}
+
+function getRootHostContext(rootContainerInstance) {
+ var type = void 0;
+ var namespace = void 0;
+ var nodeType = rootContainerInstance.nodeType;
+ switch (nodeType) {
+ case DOCUMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ {
+ type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';
+ var root = rootContainerInstance.documentElement;
+ namespace = root ? root.namespaceURI : getChildNamespace(null, '');
+ break;
+ }
+ default:
+ {
+ var container = nodeType === COMMENT_NODE ? rootContainerInstance.parentNode : rootContainerInstance;
+ var ownNamespace = container.namespaceURI || null;
+ type = container.tagName;
+ namespace = getChildNamespace(ownNamespace, type);
+ break;
+ }
+ }
+ {
+ var validatedTag = type.toLowerCase();
+ var _ancestorInfo = updatedAncestorInfo(null, validatedTag);
+ return { namespace: namespace, ancestorInfo: _ancestorInfo };
+ }
+ return namespace;
+}
+
+function getChildHostContext(parentHostContext, type, rootContainerInstance) {
+ {
+ var parentHostContextDev = parentHostContext;
+ var _namespace = getChildNamespace(parentHostContextDev.namespace, type);
+ var _ancestorInfo2 = updatedAncestorInfo(parentHostContextDev.ancestorInfo, type);
+ return { namespace: _namespace, ancestorInfo: _ancestorInfo2 };
+ }
+ var parentNamespace = parentHostContext;
+ return getChildNamespace(parentNamespace, type);
+}
+
+function getPublicInstance(instance) {
+ return instance;
+}
+
+function prepareForCommit(containerInfo) {
+ eventsEnabled = isEnabled();
+ selectionInformation = getSelectionInformation();
+ setEnabled(false);
+}
+
+function resetAfterCommit(containerInfo) {
+ restoreSelection(selectionInformation);
+ selectionInformation = null;
+ setEnabled(eventsEnabled);
+ eventsEnabled = null;
+}
+
+function createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
+ var parentNamespace = void 0;
+ {
+ // TODO: take namespace into account when validating.
+ var hostContextDev = hostContext;
+ validateDOMNesting(type, null, hostContextDev.ancestorInfo);
+ if (typeof props.children === 'string' || typeof props.children === 'number') {
+ var string = '' + props.children;
+ var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type);
+ validateDOMNesting(null, string, ownAncestorInfo);
+ }
+ parentNamespace = hostContextDev.namespace;
+ }
+ var domElement = createElement(type, props, rootContainerInstance, parentNamespace);
+ precacheFiberNode(internalInstanceHandle, domElement);
+ updateFiberProps(domElement, props);
+ return domElement;
+}
+
+function appendInitialChild(parentInstance, child) {
+ parentInstance.appendChild(child);
+}
+
+function finalizeInitialChildren(domElement, type, props, rootContainerInstance, hostContext) {
+ setInitialProperties(domElement, type, props, rootContainerInstance);
+ return shouldAutoFocusHostComponent(type, props);
+}
+
+function prepareUpdate(domElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
+ {
+ var hostContextDev = hostContext;
+ if (typeof newProps.children !== typeof oldProps.children && (typeof newProps.children === 'string' || typeof newProps.children === 'number')) {
+ var string = '' + newProps.children;
+ var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type);
+ validateDOMNesting(null, string, ownAncestorInfo);
+ }
+ }
+ return diffProperties(domElement, type, oldProps, newProps, rootContainerInstance);
+}
+
+function shouldSetTextContent(type, props) {
+ return type === 'textarea' || type === 'option' || type === 'noscript' || typeof props.children === 'string' || typeof props.children === 'number' || typeof props.dangerouslySetInnerHTML === 'object' && props.dangerouslySetInnerHTML !== null && props.dangerouslySetInnerHTML.__html != null;
+}
+
+function shouldDeprioritizeSubtree(type, props) {
+ return !!props.hidden;
+}
+
+function createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) {
+ {
+ var hostContextDev = hostContext;
+ validateDOMNesting(null, text, hostContextDev.ancestorInfo);
+ }
+ var textNode = createTextNode(text, rootContainerInstance);
+ precacheFiberNode(internalInstanceHandle, textNode);
+ return textNode;
+}
+
+var isPrimaryRenderer = true;
+// This initialization code may run even on server environments
+// if a component just imports ReactDOM (e.g. for findDOMNode).
+// Some environments might not have setTimeout or clearTimeout.
+var scheduleTimeout = typeof setTimeout === 'function' ? setTimeout : undefined;
+var cancelTimeout = typeof clearTimeout === 'function' ? clearTimeout : undefined;
+var noTimeout = -1;
+var schedulePassiveEffects = unstable_scheduleCallback;
+var cancelPassiveEffects = unstable_cancelCallback;
+
+// -------------------
+// Mutation
+// -------------------
+
+var supportsMutation = true;
+
+function commitMount(domElement, type, newProps, internalInstanceHandle) {
+ // Despite the naming that might imply otherwise, this method only
+ // fires if there is an `Update` effect scheduled during mounting.
+ // This happens if `finalizeInitialChildren` returns `true` (which it
+ // does to implement the `autoFocus` attribute on the client). But
+ // there are also other cases when this might happen (such as patching
+ // up text content during hydration mismatch). So we'll check this again.
+ if (shouldAutoFocusHostComponent(type, newProps)) {
+ domElement.focus();
+ }
+}
+
+function commitUpdate(domElement, updatePayload, type, oldProps, newProps, internalInstanceHandle) {
+ // Update the props handle so that we know which props are the ones with
+ // with current event handlers.
+ updateFiberProps(domElement, newProps);
+ // Apply the diff to the DOM node.
+ updateProperties(domElement, updatePayload, type, oldProps, newProps);
+}
+
+function resetTextContent(domElement) {
+ setTextContent(domElement, '');
+}
+
+function commitTextUpdate(textInstance, oldText, newText) {
+ textInstance.nodeValue = newText;
+}
+
+function appendChild(parentInstance, child) {
+ parentInstance.appendChild(child);
+}
+
+function appendChildToContainer(container, child) {
+ var parentNode = void 0;
+ if (container.nodeType === COMMENT_NODE) {
+ parentNode = container.parentNode;
+ parentNode.insertBefore(child, container);
+ } else {
+ parentNode = container;
+ parentNode.appendChild(child);
+ }
+ // This container might be used for a portal.
+ // If something inside a portal is clicked, that click should bubble
+ // through the React tree. However, on Mobile Safari the click would
+ // never bubble through the *DOM* tree unless an ancestor with onclick
+ // event exists. So we wouldn't see it and dispatch it.
+ // This is why we ensure that non React root containers have inline onclick
+ // defined.
+ // https://github.com/facebook/react/issues/11918
+ var reactRootContainer = container._reactRootContainer;
+ if ((reactRootContainer === null || reactRootContainer === undefined) && parentNode.onclick === null) {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(parentNode);
+ }
+}
+
+function insertBefore(parentInstance, child, beforeChild) {
+ parentInstance.insertBefore(child, beforeChild);
+}
+
+function insertInContainerBefore(container, child, beforeChild) {
+ if (container.nodeType === COMMENT_NODE) {
+ container.parentNode.insertBefore(child, beforeChild);
+ } else {
+ container.insertBefore(child, beforeChild);
+ }
+}
+
+function removeChild(parentInstance, child) {
+ parentInstance.removeChild(child);
+}
+
+function removeChildFromContainer(container, child) {
+ if (container.nodeType === COMMENT_NODE) {
+ container.parentNode.removeChild(child);
+ } else {
+ container.removeChild(child);
+ }
+}
+
+function clearSuspenseBoundary(parentInstance, suspenseInstance) {
+ var node = suspenseInstance;
+ // Delete all nodes within this suspense boundary.
+ // There might be nested nodes so we need to keep track of how
+ // deep we are and only break out when we're back on top.
+ var depth = 0;
+ do {
+ var nextNode = node.nextSibling;
+ parentInstance.removeChild(node);
+ if (nextNode && nextNode.nodeType === COMMENT_NODE) {
+ var data = nextNode.data;
+ if (data === SUSPENSE_END_DATA) {
+ if (depth === 0) {
+ parentInstance.removeChild(nextNode);
+ return;
+ } else {
+ depth--;
+ }
+ } else if (data === SUSPENSE_START_DATA) {
+ depth++;
+ }
+ }
+ node = nextNode;
+ } while (node);
+ // TODO: Warn, we didn't find the end comment boundary.
+}
+
+function clearSuspenseBoundaryFromContainer(container, suspenseInstance) {
+ if (container.nodeType === COMMENT_NODE) {
+ clearSuspenseBoundary(container.parentNode, suspenseInstance);
+ } else if (container.nodeType === ELEMENT_NODE) {
+ clearSuspenseBoundary(container, suspenseInstance);
+ } else {
+ // Document nodes should never contain suspense boundaries.
+ }
+}
+
+function hideInstance(instance) {
+ // TODO: Does this work for all element types? What about MathML? Should we
+ // pass host context to this method?
+ instance = instance;
+ instance.style.display = 'none';
+}
+
+function hideTextInstance(textInstance) {
+ textInstance.nodeValue = '';
+}
+
+function unhideInstance(instance, props) {
+ instance = instance;
+ var styleProp = props[STYLE];
+ var display = styleProp !== undefined && styleProp !== null && styleProp.hasOwnProperty('display') ? styleProp.display : null;
+ instance.style.display = dangerousStyleValue('display', display);
+}
+
+function unhideTextInstance(textInstance, text) {
+ textInstance.nodeValue = text;
+}
+
+// -------------------
+// Hydration
+// -------------------
+
+var supportsHydration = true;
+
+function canHydrateInstance(instance, type, props) {
+ if (instance.nodeType !== ELEMENT_NODE || type.toLowerCase() !== instance.nodeName.toLowerCase()) {
+ return null;
+ }
+ // This has now been refined to an element node.
+ return instance;
+}
+
+function canHydrateTextInstance(instance, text) {
+ if (text === '' || instance.nodeType !== TEXT_NODE) {
+ // Empty strings are not parsed by HTML so there won't be a correct match here.
+ return null;
+ }
+ // This has now been refined to a text node.
+ return instance;
+}
+
+function canHydrateSuspenseInstance(instance) {
+ if (instance.nodeType !== COMMENT_NODE) {
+ // Empty strings are not parsed by HTML so there won't be a correct match here.
+ return null;
+ }
+ // This has now been refined to a suspense node.
+ return instance;
+}
+
+function getNextHydratableSibling(instance) {
+ var node = instance.nextSibling;
+ // Skip non-hydratable nodes.
+ while (node && node.nodeType !== ELEMENT_NODE && node.nodeType !== TEXT_NODE && (!enableSuspenseServerRenderer || node.nodeType !== COMMENT_NODE || node.data !== SUSPENSE_START_DATA)) {
+ node = node.nextSibling;
+ }
+ return node;
+}
+
+function getFirstHydratableChild(parentInstance) {
+ var next = parentInstance.firstChild;
+ // Skip non-hydratable nodes.
+ while (next && next.nodeType !== ELEMENT_NODE && next.nodeType !== TEXT_NODE && (!enableSuspenseServerRenderer || next.nodeType !== COMMENT_NODE || next.data !== SUSPENSE_START_DATA)) {
+ next = next.nextSibling;
+ }
+ return next;
+}
+
+function hydrateInstance(instance, type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
+ precacheFiberNode(internalInstanceHandle, instance);
+ // TODO: Possibly defer this until the commit phase where all the events
+ // get attached.
+ updateFiberProps(instance, props);
+ var parentNamespace = void 0;
+ {
+ var hostContextDev = hostContext;
+ parentNamespace = hostContextDev.namespace;
+ }
+ return diffHydratedProperties(instance, type, props, parentNamespace, rootContainerInstance);
+}
+
+function hydrateTextInstance(textInstance, text, internalInstanceHandle) {
+ precacheFiberNode(internalInstanceHandle, textInstance);
+ return diffHydratedText(textInstance, text);
+}
+
+function getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance) {
+ var node = suspenseInstance.nextSibling;
+ // Skip past all nodes within this suspense boundary.
+ // There might be nested nodes so we need to keep track of how
+ // deep we are and only break out when we're back on top.
+ var depth = 0;
+ while (node) {
+ if (node.nodeType === COMMENT_NODE) {
+ var data = node.data;
+ if (data === SUSPENSE_END_DATA) {
+ if (depth === 0) {
+ return getNextHydratableSibling(node);
+ } else {
+ depth--;
+ }
+ } else if (data === SUSPENSE_START_DATA) {
+ depth++;
+ }
+ }
+ node = node.nextSibling;
+ }
+ // TODO: Warn, we didn't find the end comment boundary.
+ return null;
+}
+
+function didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, text) {
+ {
+ warnForUnmatchedText(textInstance, text);
+ }
+}
+
+function didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, text) {
+ if (true && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForUnmatchedText(textInstance, text);
+ }
+}
+
+function didNotHydrateContainerInstance(parentContainer, instance) {
+ {
+ if (instance.nodeType === ELEMENT_NODE) {
+ warnForDeletedHydratableElement(parentContainer, instance);
+ } else if (instance.nodeType === COMMENT_NODE) {
+ // TODO: warnForDeletedHydratableSuspenseBoundary
+ } else {
+ warnForDeletedHydratableText(parentContainer, instance);
+ }
+ }
+}
+
+function didNotHydrateInstance(parentType, parentProps, parentInstance, instance) {
+ if (true && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ if (instance.nodeType === ELEMENT_NODE) {
+ warnForDeletedHydratableElement(parentInstance, instance);
+ } else if (instance.nodeType === COMMENT_NODE) {
+ // TODO: warnForDeletedHydratableSuspenseBoundary
+ } else {
+ warnForDeletedHydratableText(parentInstance, instance);
+ }
+ }
+}
+
+function didNotFindHydratableContainerInstance(parentContainer, type, props) {
+ {
+ warnForInsertedHydratedElement(parentContainer, type, props);
+ }
+}
+
+function didNotFindHydratableContainerTextInstance(parentContainer, text) {
+ {
+ warnForInsertedHydratedText(parentContainer, text);
+ }
+}
+
+
+
+function didNotFindHydratableInstance(parentType, parentProps, parentInstance, type, props) {
+ if (true && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForInsertedHydratedElement(parentInstance, type, props);
+ }
+}
+
+function didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, text) {
+ if (true && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ warnForInsertedHydratedText(parentInstance, text);
+ }
+}
+
+function didNotFindHydratableSuspenseInstance(parentType, parentProps, parentInstance) {
+ if (true && parentProps[SUPPRESS_HYDRATION_WARNING] !== true) {
+ // TODO: warnForInsertedHydratedSuspense(parentInstance);
+ }
+}
+
+// Prefix measurements so that it's possible to filter them.
+// Longer prefixes are hard to read in DevTools.
+var reactEmoji = '\u269B';
+var warningEmoji = '\u26D4';
+var supportsUserTiming = typeof performance !== 'undefined' && typeof performance.mark === 'function' && typeof performance.clearMarks === 'function' && typeof performance.measure === 'function' && typeof performance.clearMeasures === 'function';
+
+// Keep track of current fiber so that we know the path to unwind on pause.
+// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
+var currentFiber = null;
+// If we're in the middle of user code, which fiber and method is it?
+// Reusing `currentFiber` would be confusing for this because user code fiber
+// can change during commit phase too, but we don't need to unwind it (since
+// lifecycles in the commit phase don't resemble a tree).
+var currentPhase = null;
+var currentPhaseFiber = null;
+// Did lifecycle hook schedule an update? This is often a performance problem,
+// so we will keep track of it, and include it in the report.
+// Track commits caused by cascading updates.
+var isCommitting = false;
+var hasScheduledUpdateInCurrentCommit = false;
+var hasScheduledUpdateInCurrentPhase = false;
+var commitCountInCurrentWorkLoop = 0;
+var effectCountInCurrentCommit = 0;
+var isWaitingForCallback = false;
+// During commits, we only show a measurement once per method name
+// to avoid stretch the commit phase with measurement overhead.
+var labelsInCurrentCommit = new Set();
+
+var formatMarkName = function (markName) {
+ return reactEmoji + ' ' + markName;
+};
+
+var formatLabel = function (label, warning) {
+ var prefix = warning ? warningEmoji + ' ' : reactEmoji + ' ';
+ var suffix = warning ? ' Warning: ' + warning : '';
+ return '' + prefix + label + suffix;
+};
+
+var beginMark = function (markName) {
+ performance.mark(formatMarkName(markName));
+};
+
+var clearMark = function (markName) {
+ performance.clearMarks(formatMarkName(markName));
+};
+
+var endMark = function (label, markName, warning) {
+ var formattedMarkName = formatMarkName(markName);
+ var formattedLabel = formatLabel(label, warning);
+ try {
+ performance.measure(formattedLabel, formattedMarkName);
+ } catch (err) {}
+ // If previous mark was missing for some reason, this will throw.
+ // This could only happen if React crashed in an unexpected place earlier.
+ // Don't pile on with more errors.
+
+ // Clear marks immediately to avoid growing buffer.
+ performance.clearMarks(formattedMarkName);
+ performance.clearMeasures(formattedLabel);
+};
+
+var getFiberMarkName = function (label, debugID) {
+ return label + ' (#' + debugID + ')';
+};
+
+var getFiberLabel = function (componentName, isMounted, phase) {
+ if (phase === null) {
+ // These are composite component total time measurements.
+ return componentName + ' [' + (isMounted ? 'update' : 'mount') + ']';
+ } else {
+ // Composite component methods.
+ return componentName + '.' + phase;
+ }
+};
+
+var beginFiberMark = function (fiber, phase) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+
+ if (isCommitting && labelsInCurrentCommit.has(label)) {
+ // During the commit phase, we don't show duplicate labels because
+ // there is a fixed overhead for every measurement, and we don't
+ // want to stretch the commit phase beyond necessary.
+ return false;
+ }
+ labelsInCurrentCommit.add(label);
+
+ var markName = getFiberMarkName(label, debugID);
+ beginMark(markName);
+ return true;
+};
+
+var clearFiberMark = function (fiber, phase) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ clearMark(markName);
+};
+
+var endFiberMark = function (fiber, phase, warning) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ endMark(label, markName, warning);
+};
+
+var shouldIgnoreFiber = function (fiber) {
+ // Host components should be skipped in the timeline.
+ // We could check typeof fiber.type, but does this work with RN?
+ switch (fiber.tag) {
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ case Fragment:
+ case ContextProvider:
+ case ContextConsumer:
+ case Mode:
+ return true;
+ default:
+ return false;
+ }
+};
+
+var clearPendingPhaseMeasurement = function () {
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ clearFiberMark(currentPhaseFiber, currentPhase);
+ }
+ currentPhaseFiber = null;
+ currentPhase = null;
+ hasScheduledUpdateInCurrentPhase = false;
+};
+
+var pauseTimers = function () {
+ // Stops all currently active measurements so that they can be resumed
+ // if we continue in a later deferred loop from the same unit of work.
+ var fiber = currentFiber;
+ while (fiber) {
+ if (fiber._debugIsCurrentlyTiming) {
+ endFiberMark(fiber, null, null);
+ }
+ fiber = fiber.return;
+ }
+};
+
+var resumeTimersRecursively = function (fiber) {
+ if (fiber.return !== null) {
+ resumeTimersRecursively(fiber.return);
+ }
+ if (fiber._debugIsCurrentlyTiming) {
+ beginFiberMark(fiber, null);
+ }
+};
+
+var resumeTimers = function () {
+ // Resumes all measurements that were active during the last deferred loop.
+ if (currentFiber !== null) {
+ resumeTimersRecursively(currentFiber);
+ }
+};
+
+function recordEffect() {
+ if (enableUserTimingAPI) {
+ effectCountInCurrentCommit++;
+ }
+}
+
+function recordScheduleUpdate() {
+ if (enableUserTimingAPI) {
+ if (isCommitting) {
+ hasScheduledUpdateInCurrentCommit = true;
+ }
+ if (currentPhase !== null && currentPhase !== 'componentWillMount' && currentPhase !== 'componentWillReceiveProps') {
+ hasScheduledUpdateInCurrentPhase = true;
+ }
+ }
+}
+
+function startRequestCallbackTimer() {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming && !isWaitingForCallback) {
+ isWaitingForCallback = true;
+ beginMark('(Waiting for async callback...)');
+ }
+ }
+}
+
+function stopRequestCallbackTimer(didExpire, expirationTime) {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming) {
+ isWaitingForCallback = false;
+ var warning = didExpire ? 'React was blocked by main thread' : null;
+ endMark('(Waiting for async callback... will force flush in ' + expirationTime + ' ms)', '(Waiting for async callback...)', warning);
+ }
+ }
+}
+
+function startWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, this is the fiber to unwind from.
+ currentFiber = fiber;
+ if (!beginFiberMark(fiber, null)) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = true;
+ }
+}
+
+function cancelWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // Remember we shouldn't complete measurement for this fiber.
+ // Otherwise flamechart will be deep even for small updates.
+ fiber._debugIsCurrentlyTiming = false;
+ clearFiberMark(fiber, null);
+ }
+}
+
+function stopWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber.return;
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ endFiberMark(fiber, null, null);
+ }
+}
+
+function stopFailedWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber.return;
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ var warning = fiber.tag === SuspenseComponent || fiber.tag === DehydratedSuspenseComponent ? 'Rendering was suspended' : 'An error was thrown inside this error boundary';
+ endFiberMark(fiber, null, warning);
+ }
+}
+
+function startPhaseTimer(fiber, phase) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ clearPendingPhaseMeasurement();
+ if (!beginFiberMark(fiber, phase)) {
+ return;
+ }
+ currentPhaseFiber = fiber;
+ currentPhase = phase;
+ }
+}
+
+function stopPhaseTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ var warning = hasScheduledUpdateInCurrentPhase ? 'Scheduled a cascading update' : null;
+ endFiberMark(currentPhaseFiber, currentPhase, warning);
+ }
+ currentPhase = null;
+ currentPhaseFiber = null;
+ }
+}
+
+function startWorkLoopTimer(nextUnitOfWork) {
+ if (enableUserTimingAPI) {
+ currentFiber = nextUnitOfWork;
+ if (!supportsUserTiming) {
+ return;
+ }
+ commitCountInCurrentWorkLoop = 0;
+ // This is top level call.
+ // Any other measurements are performed within.
+ beginMark('(React Tree Reconciliation)');
+ // Resume any measurements that were in progress during the last loop.
+ resumeTimers();
+ }
+}
+
+function stopWorkLoopTimer(interruptedBy, didCompleteRoot) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var warning = null;
+ if (interruptedBy !== null) {
+ if (interruptedBy.tag === HostRoot) {
+ warning = 'A top-level update interrupted the previous render';
+ } else {
+ var componentName = getComponentName(interruptedBy.type) || 'Unknown';
+ warning = 'An update to ' + componentName + ' interrupted the previous render';
+ }
+ } else if (commitCountInCurrentWorkLoop > 1) {
+ warning = 'There were cascading updates';
+ }
+ commitCountInCurrentWorkLoop = 0;
+ var label = didCompleteRoot ? '(React Tree Reconciliation: Completed Root)' : '(React Tree Reconciliation: Yielded)';
+ // Pause any measurements until the next loop.
+ pauseTimers();
+ endMark(label, '(React Tree Reconciliation)', warning);
+ }
+}
+
+function startCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ isCommitting = true;
+ hasScheduledUpdateInCurrentCommit = false;
+ labelsInCurrentCommit.clear();
+ beginMark('(Committing Changes)');
+ }
+}
+
+function stopCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+
+ var warning = null;
+ if (hasScheduledUpdateInCurrentCommit) {
+ warning = 'Lifecycle hook scheduled a cascading update';
+ } else if (commitCountInCurrentWorkLoop > 0) {
+ warning = 'Caused by a cascading update in earlier commit';
+ }
+ hasScheduledUpdateInCurrentCommit = false;
+ commitCountInCurrentWorkLoop++;
+ isCommitting = false;
+ labelsInCurrentCommit.clear();
+
+ endMark('(Committing Changes)', '(Committing Changes)', warning);
+ }
+}
+
+function startCommitSnapshotEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Committing Snapshot Effects)');
+ }
+}
+
+function stopCommitSnapshotEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Committing Snapshot Effects: ' + count + ' Total)', '(Committing Snapshot Effects)', null);
+ }
+}
+
+function startCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Committing Host Effects)');
+ }
+}
+
+function stopCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Committing Host Effects: ' + count + ' Total)', '(Committing Host Effects)', null);
+ }
+}
+
+function startCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Calling Lifecycle Methods)');
+ }
+}
+
+function stopCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Calling Lifecycle Methods: ' + count + ' Total)', '(Calling Lifecycle Methods)', null);
+ }
+}
+
+var valueStack = [];
+
+var fiberStack = void 0;
+
+{
+ fiberStack = [];
+}
+
+var index = -1;
+
+function createCursor(defaultValue) {
+ return {
+ current: defaultValue
+ };
+}
+
+function pop(cursor, fiber) {
+ if (index < 0) {
+ {
+ warningWithoutStack$1(false, 'Unexpected pop.');
+ }
+ return;
+ }
+
+ {
+ if (fiber !== fiberStack[index]) {
+ warningWithoutStack$1(false, 'Unexpected Fiber popped.');
+ }
+ }
+
+ cursor.current = valueStack[index];
+
+ valueStack[index] = null;
+
+ {
+ fiberStack[index] = null;
+ }
+
+ index--;
+}
+
+function push(cursor, value, fiber) {
+ index++;
+
+ valueStack[index] = cursor.current;
+
+ {
+ fiberStack[index] = fiber;
+ }
+
+ cursor.current = value;
+}
+
+function checkThatStackIsEmpty() {
+ {
+ if (index !== -1) {
+ warningWithoutStack$1(false, 'Expected an empty stack. Something was not reset properly.');
+ }
+ }
+}
+
+function resetStackAfterFatalErrorInDev() {
+ {
+ index = -1;
+ valueStack.length = 0;
+ fiberStack.length = 0;
+ }
+}
+
+var warnedAboutMissingGetChildContext = void 0;
+
+{
+ warnedAboutMissingGetChildContext = {};
+}
+
+var emptyContextObject = {};
+{
+ Object.freeze(emptyContextObject);
+}
+
+// A cursor to the current merged context object on the stack.
+var contextStackCursor = createCursor(emptyContextObject);
+// A cursor to a boolean indicating whether the context has changed.
+var didPerformWorkStackCursor = createCursor(false);
+// Keep track of the previous context object that was on the stack.
+// We use this to get access to the parent context after we have already
+// pushed the next context provider, and now need to merge their contexts.
+var previousContext = emptyContextObject;
+
+function getUnmaskedContext(workInProgress, Component, didPushOwnContextIfProvider) {
+ if (didPushOwnContextIfProvider && isContextProvider(Component)) {
+ // If the fiber is a context provider itself, when we read its context
+ // we may have already pushed its own child context on the stack. A context
+ // provider should not "see" its own child context. Therefore we read the
+ // previous (parent) context instead for a context provider.
+ return previousContext;
+ }
+ return contextStackCursor.current;
+}
+
+function cacheContext(workInProgress, unmaskedContext, maskedContext) {
+ var instance = workInProgress.stateNode;
+ instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
+ instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
+}
+
+function getMaskedContext(workInProgress, unmaskedContext) {
+ var type = workInProgress.type;
+ var contextTypes = type.contextTypes;
+ if (!contextTypes) {
+ return emptyContextObject;
+ }
+
+ // Avoid recreating masked context unless unmasked context has changed.
+ // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
+ // This may trigger infinite loops if componentWillReceiveProps calls setState.
+ var instance = workInProgress.stateNode;
+ if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) {
+ return instance.__reactInternalMemoizedMaskedChildContext;
+ }
+
+ var context = {};
+ for (var key in contextTypes) {
+ context[key] = unmaskedContext[key];
+ }
+
+ {
+ var name = getComponentName(type) || 'Unknown';
+ checkPropTypes_1(contextTypes, context, 'context', name, getCurrentFiberStackInDev);
+ }
+
+ // Cache unmasked context so we can avoid recreating masked context unless necessary.
+ // Context is created before the class component is instantiated so check for instance.
+ if (instance) {
+ cacheContext(workInProgress, unmaskedContext, context);
+ }
+
+ return context;
+}
+
+function hasContextChanged() {
+ return didPerformWorkStackCursor.current;
+}
+
+function isContextProvider(type) {
+ var childContextTypes = type.childContextTypes;
+ return childContextTypes !== null && childContextTypes !== undefined;
+}
+
+function popContext(fiber) {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
+
+function popTopLevelContextObject(fiber) {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
+
+function pushTopLevelContextObject(fiber, context, didChange) {
+ !(contextStackCursor.current === emptyContextObject) ? invariant(false, 'Unexpected context found on stack. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+
+ push(contextStackCursor, context, fiber);
+ push(didPerformWorkStackCursor, didChange, fiber);
+}
+
+function processChildContext(fiber, type, parentContext) {
+ var instance = fiber.stateNode;
+ var childContextTypes = type.childContextTypes;
+
+ // TODO (bvaughn) Replace this behavior with an invariant() in the future.
+ // It has only been added in Fiber to match the (unintentional) behavior in Stack.
+ if (typeof instance.getChildContext !== 'function') {
+ {
+ var componentName = getComponentName(type) || 'Unknown';
+
+ if (!warnedAboutMissingGetChildContext[componentName]) {
+ warnedAboutMissingGetChildContext[componentName] = true;
+ warningWithoutStack$1(false, '%s.childContextTypes is specified but there is no getChildContext() method ' + 'on the instance. You can either define getChildContext() on %s or remove ' + 'childContextTypes from it.', componentName, componentName);
+ }
+ }
+ return parentContext;
+ }
+
+ var childContext = void 0;
+ {
+ setCurrentPhase('getChildContext');
+ }
+ startPhaseTimer(fiber, 'getChildContext');
+ childContext = instance.getChildContext();
+ stopPhaseTimer();
+ {
+ setCurrentPhase(null);
+ }
+ for (var contextKey in childContext) {
+ !(contextKey in childContextTypes) ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', getComponentName(type) || 'Unknown', contextKey) : void 0;
+ }
+ {
+ var name = getComponentName(type) || 'Unknown';
+ checkPropTypes_1(childContextTypes, childContext, 'child context', name,
+ // In practice, there is one case in which we won't get a stack. It's when
+ // somebody calls unstable_renderSubtreeIntoContainer() and we process
+ // context from the parent component instance. The stack will be missing
+ // because it's outside of the reconciliation, and so the pointer has not
+ // been set. This is rare and doesn't matter. We'll also remove that API.
+ getCurrentFiberStackInDev);
+ }
+
+ return _assign({}, parentContext, childContext);
+}
+
+function pushContextProvider(workInProgress) {
+ var instance = workInProgress.stateNode;
+ // We push the context as early as possible to ensure stack integrity.
+ // If the instance does not exist yet, we will push null at first,
+ // and replace it on the stack later when invalidating the context.
+ var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyContextObject;
+
+ // Remember the parent context so we can merge with it later.
+ // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
+ previousContext = contextStackCursor.current;
+ push(contextStackCursor, memoizedMergedChildContext, workInProgress);
+ push(didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress);
+
+ return true;
+}
+
+function invalidateContextProvider(workInProgress, type, didChange) {
+ var instance = workInProgress.stateNode;
+ !instance ? invariant(false, 'Expected to have an instance by this point. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+
+ if (didChange) {
+ // Merge parent and own context.
+ // Skip this if we're not updating due to sCU.
+ // This avoids unnecessarily recomputing memoized values.
+ var mergedContext = processChildContext(workInProgress, type, previousContext);
+ instance.__reactInternalMemoizedMergedChildContext = mergedContext;
+
+ // Replace the old (or empty) context with the new one.
+ // It is important to unwind the context in the reverse order.
+ pop(didPerformWorkStackCursor, workInProgress);
+ pop(contextStackCursor, workInProgress);
+ // Now push the new context and mark that it has changed.
+ push(contextStackCursor, mergedContext, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ } else {
+ pop(didPerformWorkStackCursor, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ }
+}
+
+function findCurrentUnmaskedContext(fiber) {
+ // Currently this is only used with renderSubtreeIntoContainer; not sure if it
+ // makes sense elsewhere
+ !(isFiberMounted(fiber) && fiber.tag === ClassComponent) ? invariant(false, 'Expected subtree parent to be a mounted class component. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+
+ var node = fiber;
+ do {
+ switch (node.tag) {
+ case HostRoot:
+ return node.stateNode.context;
+ case ClassComponent:
+ {
+ var Component = node.type;
+ if (isContextProvider(Component)) {
+ return node.stateNode.__reactInternalMemoizedMergedChildContext;
+ }
+ break;
+ }
+ }
+ node = node.return;
+ } while (node !== null);
+ invariant(false, 'Found unexpected detached subtree parent. This error is likely caused by a bug in React. Please file an issue.');
+}
+
+var onCommitFiberRoot = null;
+var onCommitFiberUnmount = null;
+var hasLoggedError = false;
+
+function catchErrors(fn) {
+ return function (arg) {
+ try {
+ return fn(arg);
+ } catch (err) {
+ if (true && !hasLoggedError) {
+ hasLoggedError = true;
+ warningWithoutStack$1(false, 'React DevTools encountered an error: %s', err);
+ }
+ }
+ };
+}
+
+var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined';
+
+function injectInternals(internals) {
+ if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
+ // No DevTools
+ return false;
+ }
+ var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
+ if (hook.isDisabled) {
+ // This isn't a real property on the hook, but it can be set to opt out
+ // of DevTools integration and associated warnings and logs.
+ // https://github.com/facebook/react/issues/3877
+ return true;
+ }
+ if (!hook.supportsFiber) {
+ {
+ warningWithoutStack$1(false, 'The installed version of React DevTools is too old and will not work ' + 'with the current version of React. Please update React DevTools. ' + 'https://fb.me/react-devtools');
+ }
+ // DevTools exists, even though it doesn't support Fiber.
+ return true;
+ }
+ try {
+ var rendererID = hook.inject(internals);
+ // We have successfully injected, so now it is safe to set up hooks.
+ onCommitFiberRoot = catchErrors(function (root) {
+ return hook.onCommitFiberRoot(rendererID, root);
+ });
+ onCommitFiberUnmount = catchErrors(function (fiber) {
+ return hook.onCommitFiberUnmount(rendererID, fiber);
+ });
+ } catch (err) {
+ // Catch all errors because it is unsafe to throw during initialization.
+ {
+ warningWithoutStack$1(false, 'React DevTools encountered an error: %s.', err);
+ }
+ }
+ // DevTools exists
+ return true;
+}
+
+function onCommitRoot(root) {
+ if (typeof onCommitFiberRoot === 'function') {
+ onCommitFiberRoot(root);
+ }
+}
+
+function onCommitUnmount(fiber) {
+ if (typeof onCommitFiberUnmount === 'function') {
+ onCommitFiberUnmount(fiber);
+ }
+}
+
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
+// Math.pow(2, 30) - 1
+// 0b111111111111111111111111111111
+var maxSigned31BitInt = 1073741823;
+
+var NoWork = 0;
+var Never = 1;
+var Sync = maxSigned31BitInt;
+
+var UNIT_SIZE = 10;
+var MAGIC_NUMBER_OFFSET = maxSigned31BitInt - 1;
+
+// 1 unit of expiration time represents 10ms.
+function msToExpirationTime(ms) {
+ // Always add an offset so that we don't clash with the magic number for NoWork.
+ return MAGIC_NUMBER_OFFSET - (ms / UNIT_SIZE | 0);
+}
+
+function expirationTimeToMs(expirationTime) {
+ return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
+}
+
+function ceiling(num, precision) {
+ return ((num / precision | 0) + 1) * precision;
+}
+
+function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) {
+ return MAGIC_NUMBER_OFFSET - ceiling(MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE);
+}
+
+var LOW_PRIORITY_EXPIRATION = 5000;
+var LOW_PRIORITY_BATCH_SIZE = 250;
+
+function computeAsyncExpiration(currentTime) {
+ return computeExpirationBucket(currentTime, LOW_PRIORITY_EXPIRATION, LOW_PRIORITY_BATCH_SIZE);
+}
+
+// We intentionally set a higher expiration time for interactive updates in
+// dev than in production.
+//
+// If the main thread is being blocked so long that you hit the expiration,
+// it's a problem that could be solved with better scheduling.
+//
+// People will be more likely to notice this and fix it with the long
+// expiration time in development.
+//
+// In production we opt for better UX at the risk of masking scheduling
+// problems, by expiring fast.
+var HIGH_PRIORITY_EXPIRATION = 500;
+var HIGH_PRIORITY_BATCH_SIZE = 100;
+
+function computeInteractiveExpiration(currentTime) {
+ return computeExpirationBucket(currentTime, HIGH_PRIORITY_EXPIRATION, HIGH_PRIORITY_BATCH_SIZE);
+}
+
+var NoContext = 0;
+var ConcurrentMode = 1;
+var StrictMode = 2;
+var ProfileMode = 4;
+
+var hasBadMapPolyfill = void 0;
+
+{
+ hasBadMapPolyfill = false;
+ try {
+ var nonExtensibleObject = Object.preventExtensions({});
+ var testMap = new Map([[nonExtensibleObject, null]]);
+ var testSet = new Set([nonExtensibleObject]);
+ // This is necessary for Rollup to not consider these unused.
+ // https://github.com/rollup/rollup/issues/1771
+ // TODO: we can remove these if Rollup fixes the bug.
+ testMap.set(0, 0);
+ testSet.add(0);
+ } catch (e) {
+ // TODO: Consider warning about bad polyfills
+ hasBadMapPolyfill = true;
+ }
+}
+
+// A Fiber is work on a Component that needs to be done or was done. There can
+// be more than one per component.
+
+
+var debugCounter = void 0;
+
+{
+ debugCounter = 1;
+}
+
+function FiberNode(tag, pendingProps, key, mode) {
+ // Instance
+ this.tag = tag;
+ this.key = key;
+ this.elementType = null;
+ this.type = null;
+ this.stateNode = null;
+
+ // Fiber
+ this.return = null;
+ this.child = null;
+ this.sibling = null;
+ this.index = 0;
+
+ this.ref = null;
+
+ this.pendingProps = pendingProps;
+ this.memoizedProps = null;
+ this.updateQueue = null;
+ this.memoizedState = null;
+ this.contextDependencies = null;
+
+ this.mode = mode;
+
+ // Effects
+ this.effectTag = NoEffect;
+ this.nextEffect = null;
+
+ this.firstEffect = null;
+ this.lastEffect = null;
+
+ this.expirationTime = NoWork;
+ this.childExpirationTime = NoWork;
+
+ this.alternate = null;
+
+ if (enableProfilerTimer) {
+ // Note: The following is done to avoid a v8 performance cliff.
+ //
+ // Initializing the fields below to smis and later updating them with
+ // double values will cause Fibers to end up having separate shapes.
+ // This behavior/bug has something to do with Object.preventExtension().
+ // Fortunately this only impacts DEV builds.
+ // Unfortunately it makes React unusably slow for some applications.
+ // To work around this, initialize the fields below with doubles.
+ //
+ // Learn more about this here:
+ // https://github.com/facebook/react/issues/14365
+ // https://bugs.chromium.org/p/v8/issues/detail?id=8538
+ this.actualDuration = Number.NaN;
+ this.actualStartTime = Number.NaN;
+ this.selfBaseDuration = Number.NaN;
+ this.treeBaseDuration = Number.NaN;
+
+ // It's okay to replace the initial doubles with smis after initialization.
+ // This won't trigger the performance cliff mentioned above,
+ // and it simplifies other profiler code (including DevTools).
+ this.actualDuration = 0;
+ this.actualStartTime = -1;
+ this.selfBaseDuration = 0;
+ this.treeBaseDuration = 0;
+ }
+
+ {
+ this._debugID = debugCounter++;
+ this._debugSource = null;
+ this._debugOwner = null;
+ this._debugIsCurrentlyTiming = false;
+ this._debugHookTypes = null;
+ if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
+ Object.preventExtensions(this);
+ }
+ }
+}
+
+// This is a constructor function, rather than a POJO constructor, still
+// please ensure we do the following:
+// 1) Nobody should add any instance methods on this. Instance methods can be
+// more difficult to predict when they get optimized and they are almost
+// never inlined properly in static compilers.
+// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
+// always know when it is a fiber.
+// 3) We might want to experiment with using numeric keys since they are easier
+// to optimize in a non-JIT environment.
+// 4) We can easily go from a constructor to a createFiber object literal if that
+// is faster.
+// 5) It should be easy to port this to a C struct and keep a C implementation
+// compatible.
+var createFiber = function (tag, pendingProps, key, mode) {
+ // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
+ return new FiberNode(tag, pendingProps, key, mode);
+};
+
+function shouldConstruct(Component) {
+ var prototype = Component.prototype;
+ return !!(prototype && prototype.isReactComponent);
+}
+
+function isSimpleFunctionComponent(type) {
+ return typeof type === 'function' && !shouldConstruct(type) && type.defaultProps === undefined;
+}
+
+function resolveLazyComponentTag(Component) {
+ if (typeof Component === 'function') {
+ return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
+ } else if (Component !== undefined && Component !== null) {
+ var $$typeof = Component.$$typeof;
+ if ($$typeof === REACT_FORWARD_REF_TYPE) {
+ return ForwardRef;
+ }
+ if ($$typeof === REACT_MEMO_TYPE) {
+ return MemoComponent;
+ }
+ }
+ return IndeterminateComponent;
+}
+
+// This is used to create an alternate fiber to do work on.
+function createWorkInProgress(current, pendingProps, expirationTime) {
+ var workInProgress = current.alternate;
+ if (workInProgress === null) {
+ // We use a double buffering pooling technique because we know that we'll
+ // only ever need at most two versions of a tree. We pool the "other" unused
+ // node that we're free to reuse. This is lazily created to avoid allocating
+ // extra objects for things that are never updated. It also allow us to
+ // reclaim the extra memory if needed.
+ workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
+ workInProgress.elementType = current.elementType;
+ workInProgress.type = current.type;
+ workInProgress.stateNode = current.stateNode;
+
+ {
+ // DEV-only fields
+ workInProgress._debugID = current._debugID;
+ workInProgress._debugSource = current._debugSource;
+ workInProgress._debugOwner = current._debugOwner;
+ workInProgress._debugHookTypes = current._debugHookTypes;
+ }
+
+ workInProgress.alternate = current;
+ current.alternate = workInProgress;
+ } else {
+ workInProgress.pendingProps = pendingProps;
+
+ // We already have an alternate.
+ // Reset the effect tag.
+ workInProgress.effectTag = NoEffect;
+
+ // The effect list is no longer valid.
+ workInProgress.nextEffect = null;
+ workInProgress.firstEffect = null;
+ workInProgress.lastEffect = null;
+
+ if (enableProfilerTimer) {
+ // We intentionally reset, rather than copy, actualDuration & actualStartTime.
+ // This prevents time from endlessly accumulating in new commits.
+ // This has the downside of resetting values for different priority renders,
+ // But works for yielding (the common case) and should support resuming.
+ workInProgress.actualDuration = 0;
+ workInProgress.actualStartTime = -1;
+ }
+ }
+
+ workInProgress.childExpirationTime = current.childExpirationTime;
+ workInProgress.expirationTime = current.expirationTime;
+
+ workInProgress.child = current.child;
+ workInProgress.memoizedProps = current.memoizedProps;
+ workInProgress.memoizedState = current.memoizedState;
+ workInProgress.updateQueue = current.updateQueue;
+ workInProgress.contextDependencies = current.contextDependencies;
+
+ // These will be overridden during the parent's reconciliation
+ workInProgress.sibling = current.sibling;
+ workInProgress.index = current.index;
+ workInProgress.ref = current.ref;
+
+ if (enableProfilerTimer) {
+ workInProgress.selfBaseDuration = current.selfBaseDuration;
+ workInProgress.treeBaseDuration = current.treeBaseDuration;
+ }
+
+ return workInProgress;
+}
+
+function createHostRootFiber(isConcurrent) {
+ var mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
+
+ if (enableProfilerTimer && isDevToolsPresent) {
+ // Always collect profile timings when DevTools are present.
+ // This enables DevTools to start capturing timing at any point–
+ // Without some nodes in the tree having empty base times.
+ mode |= ProfileMode;
+ }
+
+ return createFiber(HostRoot, null, null, mode);
+}
+
+function createFiberFromTypeAndProps(type, // React$ElementType
+key, pendingProps, owner, mode, expirationTime) {
+ var fiber = void 0;
+
+ var fiberTag = IndeterminateComponent;
+ // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
+ var resolvedType = type;
+ if (typeof type === 'function') {
+ if (shouldConstruct(type)) {
+ fiberTag = ClassComponent;
+ }
+ } else if (typeof type === 'string') {
+ fiberTag = HostComponent;
+ } else {
+ getTag: switch (type) {
+ case REACT_FRAGMENT_TYPE:
+ return createFiberFromFragment(pendingProps.children, mode, expirationTime, key);
+ case REACT_CONCURRENT_MODE_TYPE:
+ return createFiberFromMode(pendingProps, mode | ConcurrentMode | StrictMode, expirationTime, key);
+ case REACT_STRICT_MODE_TYPE:
+ return createFiberFromMode(pendingProps, mode | StrictMode, expirationTime, key);
+ case REACT_PROFILER_TYPE:
+ return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
+ case REACT_SUSPENSE_TYPE:
+ return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
+ default:
+ {
+ if (typeof type === 'object' && type !== null) {
+ switch (type.$$typeof) {
+ case REACT_PROVIDER_TYPE:
+ fiberTag = ContextProvider;
+ break getTag;
+ case REACT_CONTEXT_TYPE:
+ // This is a consumer
+ fiberTag = ContextConsumer;
+ break getTag;
+ case REACT_FORWARD_REF_TYPE:
+ fiberTag = ForwardRef;
+ break getTag;
+ case REACT_MEMO_TYPE:
+ fiberTag = MemoComponent;
+ break getTag;
+ case REACT_LAZY_TYPE:
+ fiberTag = LazyComponent;
+ resolvedType = null;
+ break getTag;
+ }
+ }
+ var info = '';
+ {
+ if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
+ info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and " + 'named imports.';
+ }
+ var ownerName = owner ? getComponentName(owner.type) : null;
+ if (ownerName) {
+ info += '\n\nCheck the render method of `' + ownerName + '`.';
+ }
+ }
+ invariant(false, 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s', type == null ? type : typeof type, info);
+ }
+ }
+ }
+
+ fiber = createFiber(fiberTag, pendingProps, key, mode);
+ fiber.elementType = type;
+ fiber.type = resolvedType;
+ fiber.expirationTime = expirationTime;
+
+ return fiber;
+}
+
+function createFiberFromElement(element, mode, expirationTime) {
+ var owner = null;
+ {
+ owner = element._owner;
+ }
+ var type = element.type;
+ var key = element.key;
+ var pendingProps = element.props;
+ var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, expirationTime);
+ {
+ fiber._debugSource = element._source;
+ fiber._debugOwner = element._owner;
+ }
+ return fiber;
+}
+
+function createFiberFromFragment(elements, mode, expirationTime, key) {
+ var fiber = createFiber(Fragment, elements, key, mode);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromProfiler(pendingProps, mode, expirationTime, key) {
+ {
+ if (typeof pendingProps.id !== 'string' || typeof pendingProps.onRender !== 'function') {
+ warningWithoutStack$1(false, 'Profiler must specify an "id" string and "onRender" function as props');
+ }
+ }
+
+ var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
+ // TODO: The Profiler fiber shouldn't have a type. It has a tag.
+ fiber.elementType = REACT_PROFILER_TYPE;
+ fiber.type = REACT_PROFILER_TYPE;
+ fiber.expirationTime = expirationTime;
+
+ return fiber;
+}
+
+function createFiberFromMode(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(Mode, pendingProps, key, mode);
+
+ // TODO: The Mode fiber shouldn't have a type. It has a tag.
+ var type = (mode & ConcurrentMode) === NoContext ? REACT_STRICT_MODE_TYPE : REACT_CONCURRENT_MODE_TYPE;
+ fiber.elementType = type;
+ fiber.type = type;
+
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromSuspense(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
+
+ // TODO: The SuspenseComponent fiber shouldn't have a type. It has a tag.
+ var type = REACT_SUSPENSE_TYPE;
+ fiber.elementType = type;
+ fiber.type = type;
+
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromText(content, mode, expirationTime) {
+ var fiber = createFiber(HostText, content, null, mode);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromHostInstanceForDeletion() {
+ var fiber = createFiber(HostComponent, null, null, NoContext);
+ // TODO: These should not need a type.
+ fiber.elementType = 'DELETED';
+ fiber.type = 'DELETED';
+ return fiber;
+}
+
+function createFiberFromPortal(portal, mode, expirationTime) {
+ var pendingProps = portal.children !== null ? portal.children : [];
+ var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
+ fiber.expirationTime = expirationTime;
+ fiber.stateNode = {
+ containerInfo: portal.containerInfo,
+ pendingChildren: null, // Used by persistent updates
+ implementation: portal.implementation
+ };
+ return fiber;
+}
+
+// Used for stashing WIP properties to replay failed work in DEV.
+function assignFiberPropertiesInDEV(target, source) {
+ if (target === null) {
+ // This Fiber's initial properties will always be overwritten.
+ // We only use a Fiber to ensure the same hidden class so DEV isn't slow.
+ target = createFiber(IndeterminateComponent, null, null, NoContext);
+ }
+
+ // This is intentionally written as a list of all properties.
+ // We tried to use Object.assign() instead but this is called in
+ // the hottest path, and Object.assign() was too slow:
+ // https://github.com/facebook/react/issues/12502
+ // This code is DEV-only so size is not a concern.
+
+ target.tag = source.tag;
+ target.key = source.key;
+ target.elementType = source.elementType;
+ target.type = source.type;
+ target.stateNode = source.stateNode;
+ target.return = source.return;
+ target.child = source.child;
+ target.sibling = source.sibling;
+ target.index = source.index;
+ target.ref = source.ref;
+ target.pendingProps = source.pendingProps;
+ target.memoizedProps = source.memoizedProps;
+ target.updateQueue = source.updateQueue;
+ target.memoizedState = source.memoizedState;
+ target.contextDependencies = source.contextDependencies;
+ target.mode = source.mode;
+ target.effectTag = source.effectTag;
+ target.nextEffect = source.nextEffect;
+ target.firstEffect = source.firstEffect;
+ target.lastEffect = source.lastEffect;
+ target.expirationTime = source.expirationTime;
+ target.childExpirationTime = source.childExpirationTime;
+ target.alternate = source.alternate;
+ if (enableProfilerTimer) {
+ target.actualDuration = source.actualDuration;
+ target.actualStartTime = source.actualStartTime;
+ target.selfBaseDuration = source.selfBaseDuration;
+ target.treeBaseDuration = source.treeBaseDuration;
+ }
+ target._debugID = source._debugID;
+ target._debugSource = source._debugSource;
+ target._debugOwner = source._debugOwner;
+ target._debugIsCurrentlyTiming = source._debugIsCurrentlyTiming;
+ target._debugHookTypes = source._debugHookTypes;
+ return target;
+}
+
+var ReactInternals$2 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _ReactInternals$Sched$1 = ReactInternals$2.SchedulerTracing;
+var __interactionsRef = _ReactInternals$Sched$1.__interactionsRef;
+var __subscriberRef = _ReactInternals$Sched$1.__subscriberRef;
+var unstable_clear = _ReactInternals$Sched$1.unstable_clear;
+var unstable_getCurrent = _ReactInternals$Sched$1.unstable_getCurrent;
+var unstable_getThreadID = _ReactInternals$Sched$1.unstable_getThreadID;
+var unstable_subscribe = _ReactInternals$Sched$1.unstable_subscribe;
+var unstable_trace = _ReactInternals$Sched$1.unstable_trace;
+var unstable_unsubscribe = _ReactInternals$Sched$1.unstable_unsubscribe;
+var unstable_wrap = _ReactInternals$Sched$1.unstable_wrap;
+
+// TODO: This should be lifted into the renderer.
+
+
+// The following attributes are only used by interaction tracing builds.
+// They enable interactions to be associated with their async work,
+// And expose interaction metadata to the React DevTools Profiler plugin.
+// Note that these attributes are only defined when the enableSchedulerTracing flag is enabled.
+
+
+// Exported FiberRoot type includes all properties,
+// To avoid requiring potentially error-prone :any casts throughout the project.
+// Profiling properties are only safe to access in profiling builds (when enableSchedulerTracing is true).
+// The types are defined separately within this file to ensure they stay in sync.
+// (We don't have to use an inline :any cast when enableSchedulerTracing is disabled.)
+
+
+function createFiberRoot(containerInfo, isConcurrent, hydrate) {
+ // Cyclic construction. This cheats the type system right now because
+ // stateNode is any.
+ var uninitializedFiber = createHostRootFiber(isConcurrent);
+
+ var root = void 0;
+ if (enableSchedulerTracing) {
+ root = {
+ current: uninitializedFiber,
+ containerInfo: containerInfo,
+ pendingChildren: null,
+
+ earliestPendingTime: NoWork,
+ latestPendingTime: NoWork,
+ earliestSuspendedTime: NoWork,
+ latestSuspendedTime: NoWork,
+ latestPingedTime: NoWork,
+
+ pingCache: null,
+
+ didError: false,
+
+ pendingCommitExpirationTime: NoWork,
+ finishedWork: null,
+ timeoutHandle: noTimeout,
+ context: null,
+ pendingContext: null,
+ hydrate: hydrate,
+ nextExpirationTimeToWorkOn: NoWork,
+ expirationTime: NoWork,
+ firstBatch: null,
+ nextScheduledRoot: null,
+
+ interactionThreadID: unstable_getThreadID(),
+ memoizedInteractions: new Set(),
+ pendingInteractionMap: new Map()
+ };
+ } else {
+ root = {
+ current: uninitializedFiber,
+ containerInfo: containerInfo,
+ pendingChildren: null,
+
+ pingCache: null,
+
+ earliestPendingTime: NoWork,
+ latestPendingTime: NoWork,
+ earliestSuspendedTime: NoWork,
+ latestSuspendedTime: NoWork,
+ latestPingedTime: NoWork,
+
+ didError: false,
+
+ pendingCommitExpirationTime: NoWork,
+ finishedWork: null,
+ timeoutHandle: noTimeout,
+ context: null,
+ pendingContext: null,
+ hydrate: hydrate,
+ nextExpirationTimeToWorkOn: NoWork,
+ expirationTime: NoWork,
+ firstBatch: null,
+ nextScheduledRoot: null
+ };
+ }
+
+ uninitializedFiber.stateNode = root;
+
+ // The reason for the way the Flow types are structured in this file,
+ // Is to avoid needing :any casts everywhere interaction tracing fields are used.
+ // Unfortunately that requires an :any cast for non-interaction tracing capable builds.
+ // $FlowFixMe Remove this :any cast and replace it with something better.
+ return root;
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var lowPriorityWarning = function () {};
+
+{
+ var printWarning$1 = function (format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.warn(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ lowPriorityWarning = function (condition, format) {
+ if (format === undefined) {
+ throw new Error('`lowPriorityWarning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning$1.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var lowPriorityWarning$1 = lowPriorityWarning;
+
+var ReactStrictModeWarnings = {
+ discardPendingWarnings: function () {},
+ flushPendingDeprecationWarnings: function () {},
+ flushPendingUnsafeLifecycleWarnings: function () {},
+ recordDeprecationWarnings: function (fiber, instance) {},
+ recordUnsafeLifecycleWarnings: function (fiber, instance) {},
+ recordLegacyContextWarning: function (fiber, instance) {},
+ flushLegacyContextWarning: function () {}
+};
+
+{
+ var LIFECYCLE_SUGGESTIONS = {
+ UNSAFE_componentWillMount: 'componentDidMount',
+ UNSAFE_componentWillReceiveProps: 'static getDerivedStateFromProps',
+ UNSAFE_componentWillUpdate: 'componentDidUpdate'
+ };
+
+ var pendingComponentWillMountWarnings = [];
+ var pendingComponentWillReceivePropsWarnings = [];
+ var pendingComponentWillUpdateWarnings = [];
+ var pendingUnsafeLifecycleWarnings = new Map();
+ var pendingLegacyContextWarning = new Map();
+
+ // Tracks components we have already warned about.
+ var didWarnAboutDeprecatedLifecycles = new Set();
+ var didWarnAboutUnsafeLifecycles = new Set();
+ var didWarnAboutLegacyContext = new Set();
+
+ var setToSortedString = function (set) {
+ var array = [];
+ set.forEach(function (value) {
+ array.push(value);
+ });
+ return array.sort().join(', ');
+ };
+
+ ReactStrictModeWarnings.discardPendingWarnings = function () {
+ pendingComponentWillMountWarnings = [];
+ pendingComponentWillReceivePropsWarnings = [];
+ pendingComponentWillUpdateWarnings = [];
+ pendingUnsafeLifecycleWarnings = new Map();
+ pendingLegacyContextWarning = new Map();
+ };
+
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function () {
+ pendingUnsafeLifecycleWarnings.forEach(function (lifecycleWarningsMap, strictRoot) {
+ var lifecyclesWarningMessages = [];
+
+ Object.keys(lifecycleWarningsMap).forEach(function (lifecycle) {
+ var lifecycleWarnings = lifecycleWarningsMap[lifecycle];
+ if (lifecycleWarnings.length > 0) {
+ var componentNames = new Set();
+ lifecycleWarnings.forEach(function (fiber) {
+ componentNames.add(getComponentName(fiber.type) || 'Component');
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+
+ var formatted = lifecycle.replace('UNSAFE_', '');
+ var suggestion = LIFECYCLE_SUGGESTIONS[lifecycle];
+ var sortedComponentNames = setToSortedString(componentNames);
+
+ lifecyclesWarningMessages.push(formatted + ': Please update the following components to use ' + (suggestion + ' instead: ' + sortedComponentNames));
+ }
+ });
+
+ if (lifecyclesWarningMessages.length > 0) {
+ var strictRootComponentStack = getStackByFiberInDevAndProd(strictRoot);
+
+ warningWithoutStack$1(false, 'Unsafe lifecycle methods were found within a strict-mode tree:%s' + '\n\n%s' + '\n\nLearn more about this warning here:' + '\nhttps://fb.me/react-strict-mode-warnings', strictRootComponentStack, lifecyclesWarningMessages.join('\n\n'));
+ }
+ });
+
+ pendingUnsafeLifecycleWarnings = new Map();
+ };
+
+ var findStrictRoot = function (fiber) {
+ var maybeStrictRoot = null;
+
+ var node = fiber;
+ while (node !== null) {
+ if (node.mode & StrictMode) {
+ maybeStrictRoot = node;
+ }
+ node = node.return;
+ }
+
+ return maybeStrictRoot;
+ };
+
+ ReactStrictModeWarnings.flushPendingDeprecationWarnings = function () {
+ if (pendingComponentWillMountWarnings.length > 0) {
+ var uniqueNames = new Set();
+ pendingComponentWillMountWarnings.forEach(function (fiber) {
+ uniqueNames.add(getComponentName(fiber.type) || 'Component');
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ var sortedNames = setToSortedString(uniqueNames);
+
+ lowPriorityWarning$1(false, 'componentWillMount is deprecated and will be removed in the next major version. ' + 'Use componentDidMount instead. As a temporary workaround, ' + 'you can rename to UNSAFE_componentWillMount.' + '\n\nPlease update the following components: %s' + '\n\nLearn more about this warning here:' + '\nhttps://fb.me/react-async-component-lifecycle-hooks', sortedNames);
+
+ pendingComponentWillMountWarnings = [];
+ }
+
+ if (pendingComponentWillReceivePropsWarnings.length > 0) {
+ var _uniqueNames = new Set();
+ pendingComponentWillReceivePropsWarnings.forEach(function (fiber) {
+ _uniqueNames.add(getComponentName(fiber.type) || 'Component');
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ var _sortedNames = setToSortedString(_uniqueNames);
+
+ lowPriorityWarning$1(false, 'componentWillReceiveProps is deprecated and will be removed in the next major version. ' + 'Use static getDerivedStateFromProps instead.' + '\n\nPlease update the following components: %s' + '\n\nLearn more about this warning here:' + '\nhttps://fb.me/react-async-component-lifecycle-hooks', _sortedNames);
+
+ pendingComponentWillReceivePropsWarnings = [];
+ }
+
+ if (pendingComponentWillUpdateWarnings.length > 0) {
+ var _uniqueNames2 = new Set();
+ pendingComponentWillUpdateWarnings.forEach(function (fiber) {
+ _uniqueNames2.add(getComponentName(fiber.type) || 'Component');
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ var _sortedNames2 = setToSortedString(_uniqueNames2);
+
+ lowPriorityWarning$1(false, 'componentWillUpdate is deprecated and will be removed in the next major version. ' + 'Use componentDidUpdate instead. As a temporary workaround, ' + 'you can rename to UNSAFE_componentWillUpdate.' + '\n\nPlease update the following components: %s' + '\n\nLearn more about this warning here:' + '\nhttps://fb.me/react-async-component-lifecycle-hooks', _sortedNames2);
+
+ pendingComponentWillUpdateWarnings = [];
+ }
+ };
+
+ ReactStrictModeWarnings.recordDeprecationWarnings = function (fiber, instance) {
+ // Dedup strategy: Warn once per component.
+ if (didWarnAboutDeprecatedLifecycles.has(fiber.type)) {
+ return;
+ }
+
+ // Don't warn about react-lifecycles-compat polyfilled components.
+ if (typeof instance.componentWillMount === 'function' && instance.componentWillMount.__suppressDeprecationWarning !== true) {
+ pendingComponentWillMountWarnings.push(fiber);
+ }
+ if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) {
+ pendingComponentWillReceivePropsWarnings.push(fiber);
+ }
+ if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) {
+ pendingComponentWillUpdateWarnings.push(fiber);
+ }
+ };
+
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function (fiber, instance) {
+ var strictRoot = findStrictRoot(fiber);
+ if (strictRoot === null) {
+ warningWithoutStack$1(false, 'Expected to find a StrictMode component in a strict mode tree. ' + 'This error is likely caused by a bug in React. Please file an issue.');
+ return;
+ }
+
+ // Dedup strategy: Warn once per component.
+ // This is difficult to track any other way since component names
+ // are often vague and are likely to collide between 3rd party libraries.
+ // An expand property is probably okay to use here since it's DEV-only,
+ // and will only be set in the event of serious warnings.
+ if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
+ return;
+ }
+
+ var warningsForRoot = void 0;
+ if (!pendingUnsafeLifecycleWarnings.has(strictRoot)) {
+ warningsForRoot = {
+ UNSAFE_componentWillMount: [],
+ UNSAFE_componentWillReceiveProps: [],
+ UNSAFE_componentWillUpdate: []
+ };
+
+ pendingUnsafeLifecycleWarnings.set(strictRoot, warningsForRoot);
+ } else {
+ warningsForRoot = pendingUnsafeLifecycleWarnings.get(strictRoot);
+ }
+
+ var unsafeLifecycles = [];
+ if (typeof instance.componentWillMount === 'function' && instance.componentWillMount.__suppressDeprecationWarning !== true || typeof instance.UNSAFE_componentWillMount === 'function') {
+ unsafeLifecycles.push('UNSAFE_componentWillMount');
+ }
+ if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true || typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
+ unsafeLifecycles.push('UNSAFE_componentWillReceiveProps');
+ }
+ if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true || typeof instance.UNSAFE_componentWillUpdate === 'function') {
+ unsafeLifecycles.push('UNSAFE_componentWillUpdate');
+ }
+
+ if (unsafeLifecycles.length > 0) {
+ unsafeLifecycles.forEach(function (lifecycle) {
+ warningsForRoot[lifecycle].push(fiber);
+ });
+ }
+ };
+
+ ReactStrictModeWarnings.recordLegacyContextWarning = function (fiber, instance) {
+ var strictRoot = findStrictRoot(fiber);
+ if (strictRoot === null) {
+ warningWithoutStack$1(false, 'Expected to find a StrictMode component in a strict mode tree. ' + 'This error is likely caused by a bug in React. Please file an issue.');
+ return;
+ }
+
+ // Dedup strategy: Warn once per component.
+ if (didWarnAboutLegacyContext.has(fiber.type)) {
+ return;
+ }
+
+ var warningsForRoot = pendingLegacyContextWarning.get(strictRoot);
+
+ if (fiber.type.contextTypes != null || fiber.type.childContextTypes != null || instance !== null && typeof instance.getChildContext === 'function') {
+ if (warningsForRoot === undefined) {
+ warningsForRoot = [];
+ pendingLegacyContextWarning.set(strictRoot, warningsForRoot);
+ }
+ warningsForRoot.push(fiber);
+ }
+ };
+
+ ReactStrictModeWarnings.flushLegacyContextWarning = function () {
+ pendingLegacyContextWarning.forEach(function (fiberArray, strictRoot) {
+ var uniqueNames = new Set();
+ fiberArray.forEach(function (fiber) {
+ uniqueNames.add(getComponentName(fiber.type) || 'Component');
+ didWarnAboutLegacyContext.add(fiber.type);
+ });
+
+ var sortedNames = setToSortedString(uniqueNames);
+ var strictRootComponentStack = getStackByFiberInDevAndProd(strictRoot);
+
+ warningWithoutStack$1(false, 'Legacy context API has been detected within a strict-mode tree: %s' + '\n\nPlease update the following components: %s' + '\n\nLearn more about this warning here:' + '\nhttps://fb.me/react-strict-mode-warnings', strictRootComponentStack, sortedNames);
+ });
+ };
+}
+
+// This lets us hook into Fiber to debug what it's doing.
+// See https://github.com/facebook/react/pull/8033.
+// This is not part of the public API, not even for React DevTools.
+// You may only inject a debugTool if you work on React Fiber itself.
+var ReactFiberInstrumentation = {
+ debugTool: null
+};
+
+var ReactFiberInstrumentation_1 = ReactFiberInstrumentation;
+
+// TODO: Offscreen updates should never suspend. However, a promise that
+// suspended inside an offscreen subtree should be able to ping at the priority
+// of the outer render.
+
+function markPendingPriorityLevel(root, expirationTime) {
+ // If there's a gap between completing a failed root and retrying it,
+ // additional updates may be scheduled. Clear `didError`, in case the update
+ // is sufficient to fix the error.
+ root.didError = false;
+
+ // Update the latest and earliest pending times
+ var earliestPendingTime = root.earliestPendingTime;
+ if (earliestPendingTime === NoWork) {
+ // No other pending updates.
+ root.earliestPendingTime = root.latestPendingTime = expirationTime;
+ } else {
+ if (earliestPendingTime < expirationTime) {
+ // This is the earliest pending update.
+ root.earliestPendingTime = expirationTime;
+ } else {
+ var latestPendingTime = root.latestPendingTime;
+ if (latestPendingTime > expirationTime) {
+ // This is the latest pending update
+ root.latestPendingTime = expirationTime;
+ }
+ }
+ }
+ findNextExpirationTimeToWorkOn(expirationTime, root);
+}
+
+function markCommittedPriorityLevels(root, earliestRemainingTime) {
+ root.didError = false;
+
+ if (earliestRemainingTime === NoWork) {
+ // Fast path. There's no remaining work. Clear everything.
+ root.earliestPendingTime = NoWork;
+ root.latestPendingTime = NoWork;
+ root.earliestSuspendedTime = NoWork;
+ root.latestSuspendedTime = NoWork;
+ root.latestPingedTime = NoWork;
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ if (earliestRemainingTime < root.latestPingedTime) {
+ root.latestPingedTime = NoWork;
+ }
+
+ // Let's see if the previous latest known pending level was just flushed.
+ var latestPendingTime = root.latestPendingTime;
+ if (latestPendingTime !== NoWork) {
+ if (latestPendingTime > earliestRemainingTime) {
+ // We've flushed all the known pending levels.
+ root.earliestPendingTime = root.latestPendingTime = NoWork;
+ } else {
+ var earliestPendingTime = root.earliestPendingTime;
+ if (earliestPendingTime > earliestRemainingTime) {
+ // We've flushed the earliest known pending level. Set this to the
+ // latest pending time.
+ root.earliestPendingTime = root.latestPendingTime;
+ }
+ }
+ }
+
+ // Now let's handle the earliest remaining level in the whole tree. We need to
+ // decide whether to treat it as a pending level or as suspended. Check
+ // it falls within the range of known suspended levels.
+
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ if (earliestSuspendedTime === NoWork) {
+ // There's no suspended work. Treat the earliest remaining level as a
+ // pending level.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ var latestSuspendedTime = root.latestSuspendedTime;
+ if (earliestRemainingTime < latestSuspendedTime) {
+ // The earliest remaining level is later than all the suspended work. That
+ // means we've flushed all the suspended work.
+ root.earliestSuspendedTime = NoWork;
+ root.latestSuspendedTime = NoWork;
+ root.latestPingedTime = NoWork;
+
+ // There's no suspended work. Treat the earliest remaining level as a
+ // pending level.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ if (earliestRemainingTime > earliestSuspendedTime) {
+ // The earliest remaining time is earlier than all the suspended work.
+ // Treat it as a pending update.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ // The earliest remaining time falls within the range of known suspended
+ // levels. We should treat this as suspended work.
+ findNextExpirationTimeToWorkOn(NoWork, root);
+}
+
+function hasLowerPriorityWork(root, erroredExpirationTime) {
+ var latestPendingTime = root.latestPendingTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ var latestPingedTime = root.latestPingedTime;
+ return latestPendingTime !== NoWork && latestPendingTime < erroredExpirationTime || latestSuspendedTime !== NoWork && latestSuspendedTime < erroredExpirationTime || latestPingedTime !== NoWork && latestPingedTime < erroredExpirationTime;
+}
+
+function isPriorityLevelSuspended(root, expirationTime) {
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ return earliestSuspendedTime !== NoWork && expirationTime <= earliestSuspendedTime && expirationTime >= latestSuspendedTime;
+}
+
+function markSuspendedPriorityLevel(root, suspendedTime) {
+ root.didError = false;
+ clearPing(root, suspendedTime);
+
+ // First, check the known pending levels and update them if needed.
+ var earliestPendingTime = root.earliestPendingTime;
+ var latestPendingTime = root.latestPendingTime;
+ if (earliestPendingTime === suspendedTime) {
+ if (latestPendingTime === suspendedTime) {
+ // Both known pending levels were suspended. Clear them.
+ root.earliestPendingTime = root.latestPendingTime = NoWork;
+ } else {
+ // The earliest pending level was suspended. Clear by setting it to the
+ // latest pending level.
+ root.earliestPendingTime = latestPendingTime;
+ }
+ } else if (latestPendingTime === suspendedTime) {
+ // The latest pending level was suspended. Clear by setting it to the
+ // latest pending level.
+ root.latestPendingTime = earliestPendingTime;
+ }
+
+ // Finally, update the known suspended levels.
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ if (earliestSuspendedTime === NoWork) {
+ // No other suspended levels.
+ root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
+ } else {
+ if (earliestSuspendedTime < suspendedTime) {
+ // This is the earliest suspended level.
+ root.earliestSuspendedTime = suspendedTime;
+ } else if (latestSuspendedTime > suspendedTime) {
+ // This is the latest suspended level
+ root.latestSuspendedTime = suspendedTime;
+ }
+ }
+
+ findNextExpirationTimeToWorkOn(suspendedTime, root);
+}
+
+function markPingedPriorityLevel(root, pingedTime) {
+ root.didError = false;
+
+ // TODO: When we add back resuming, we need to ensure the progressed work
+ // is thrown out and not reused during the restarted render. One way to
+ // invalidate the progressed work is to restart at expirationTime + 1.
+ var latestPingedTime = root.latestPingedTime;
+ if (latestPingedTime === NoWork || latestPingedTime > pingedTime) {
+ root.latestPingedTime = pingedTime;
+ }
+ findNextExpirationTimeToWorkOn(pingedTime, root);
+}
+
+function clearPing(root, completedTime) {
+ var latestPingedTime = root.latestPingedTime;
+ if (latestPingedTime >= completedTime) {
+ root.latestPingedTime = NoWork;
+ }
+}
+
+function findEarliestOutstandingPriorityLevel(root, renderExpirationTime) {
+ var earliestExpirationTime = renderExpirationTime;
+
+ var earliestPendingTime = root.earliestPendingTime;
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ if (earliestPendingTime > earliestExpirationTime) {
+ earliestExpirationTime = earliestPendingTime;
+ }
+ if (earliestSuspendedTime > earliestExpirationTime) {
+ earliestExpirationTime = earliestSuspendedTime;
+ }
+ return earliestExpirationTime;
+}
+
+function didExpireAtExpirationTime(root, currentTime) {
+ var expirationTime = root.expirationTime;
+ if (expirationTime !== NoWork && currentTime <= expirationTime) {
+ // The root has expired. Flush all work up to the current time.
+ root.nextExpirationTimeToWorkOn = currentTime;
+ }
+}
+
+function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ var earliestPendingTime = root.earliestPendingTime;
+ var latestPingedTime = root.latestPingedTime;
+
+ // Work on the earliest pending time. Failing that, work on the latest
+ // pinged time.
+ var nextExpirationTimeToWorkOn = earliestPendingTime !== NoWork ? earliestPendingTime : latestPingedTime;
+
+ // If there is no pending or pinged work, check if there's suspended work
+ // that's lower priority than what we just completed.
+ if (nextExpirationTimeToWorkOn === NoWork && (completedExpirationTime === NoWork || latestSuspendedTime < completedExpirationTime)) {
+ // The lowest priority suspended work is the work most likely to be
+ // committed next. Let's start rendering it again, so that if it times out,
+ // it's ready to commit.
+ nextExpirationTimeToWorkOn = latestSuspendedTime;
+ }
+
+ var expirationTime = nextExpirationTimeToWorkOn;
+ if (expirationTime !== NoWork && earliestSuspendedTime > expirationTime) {
+ // Expire using the earliest known expiration time.
+ expirationTime = earliestSuspendedTime;
+ }
+
+ root.nextExpirationTimeToWorkOn = nextExpirationTimeToWorkOn;
+ root.expirationTime = expirationTime;
+}
+
+function resolveDefaultProps(Component, baseProps) {
+ if (Component && Component.defaultProps) {
+ // Resolve default props. Taken from ReactElement
+ var props = _assign({}, baseProps);
+ var defaultProps = Component.defaultProps;
+ for (var propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
+ }
+ return props;
+ }
+ return baseProps;
+}
+
+function readLazyComponentType(lazyComponent) {
+ var status = lazyComponent._status;
+ var result = lazyComponent._result;
+ switch (status) {
+ case Resolved:
+ {
+ var Component = result;
+ return Component;
+ }
+ case Rejected:
+ {
+ var error = result;
+ throw error;
+ }
+ case Pending:
+ {
+ var thenable = result;
+ throw thenable;
+ }
+ default:
+ {
+ lazyComponent._status = Pending;
+ var ctor = lazyComponent._ctor;
+ var _thenable = ctor();
+ _thenable.then(function (moduleObject) {
+ if (lazyComponent._status === Pending) {
+ var defaultExport = moduleObject.default;
+ {
+ if (defaultExport === undefined) {
+ warning$1(false, 'lazy: Expected the result of a dynamic import() call. ' + 'Instead received: %s\n\nYour code should look like: \n ' + "const MyComponent = lazy(() => import('./MyComponent'))", moduleObject);
+ }
+ }
+ lazyComponent._status = Resolved;
+ lazyComponent._result = defaultExport;
+ }
+ }, function (error) {
+ if (lazyComponent._status === Pending) {
+ lazyComponent._status = Rejected;
+ lazyComponent._result = error;
+ }
+ });
+ // Handle synchronous thenables.
+ switch (lazyComponent._status) {
+ case Resolved:
+ return lazyComponent._result;
+ case Rejected:
+ throw lazyComponent._result;
+ }
+ lazyComponent._result = _thenable;
+ throw _thenable;
+ }
+ }
+}
+
+var fakeInternalInstance = {};
+var isArray$1 = Array.isArray;
+
+// React.Component uses a shared frozen object by default.
+// We'll use it to determine whether we need to initialize legacy refs.
+var emptyRefsObject = new React.Component().refs;
+
+var didWarnAboutStateAssignmentForComponent = void 0;
+var didWarnAboutUninitializedState = void 0;
+var didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = void 0;
+var didWarnAboutLegacyLifecyclesAndDerivedState = void 0;
+var didWarnAboutUndefinedDerivedState = void 0;
+var warnOnUndefinedDerivedState = void 0;
+var warnOnInvalidCallback$1 = void 0;
+var didWarnAboutDirectlyAssigningPropsToState = void 0;
+var didWarnAboutContextTypeAndContextTypes = void 0;
+var didWarnAboutInvalidateContextType = void 0;
+
+{
+ didWarnAboutStateAssignmentForComponent = new Set();
+ didWarnAboutUninitializedState = new Set();
+ didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set();
+ didWarnAboutLegacyLifecyclesAndDerivedState = new Set();
+ didWarnAboutDirectlyAssigningPropsToState = new Set();
+ didWarnAboutUndefinedDerivedState = new Set();
+ didWarnAboutContextTypeAndContextTypes = new Set();
+ didWarnAboutInvalidateContextType = new Set();
+
+ var didWarnOnInvalidCallback = new Set();
+
+ warnOnInvalidCallback$1 = function (callback, callerName) {
+ if (callback === null || typeof callback === 'function') {
+ return;
+ }
+ var key = callerName + '_' + callback;
+ if (!didWarnOnInvalidCallback.has(key)) {
+ didWarnOnInvalidCallback.add(key);
+ warningWithoutStack$1(false, '%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback);
+ }
+ };
+
+ warnOnUndefinedDerivedState = function (type, partialState) {
+ if (partialState === undefined) {
+ var componentName = getComponentName(type) || 'Component';
+ if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
+ didWarnAboutUndefinedDerivedState.add(componentName);
+ warningWithoutStack$1(false, '%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' + 'You have returned undefined.', componentName);
+ }
+ }
+ };
+
+ // This is so gross but it's at least non-critical and can be removed if
+ // it causes problems. This is meant to give a nicer error message for
+ // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component,
+ // ...)) which otherwise throws a "_processChildContext is not a function"
+ // exception.
+ Object.defineProperty(fakeInternalInstance, '_processChildContext', {
+ enumerable: false,
+ value: function () {
+ invariant(false, '_processChildContext is not available in React 16+. This likely means you have multiple copies of React and are attempting to nest a React 15 tree inside a React 16 tree using unstable_renderSubtreeIntoContainer, which isn\'t supported. Try to make sure you have only one copy of React (and ideally, switch to ReactDOM.createPortal).');
+ }
+ });
+ Object.freeze(fakeInternalInstance);
+}
+
+function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) {
+ var prevState = workInProgress.memoizedState;
+
+ {
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ // Invoke the function an extra time to help detect side-effects.
+ getDerivedStateFromProps(nextProps, prevState);
+ }
+ }
+
+ var partialState = getDerivedStateFromProps(nextProps, prevState);
+
+ {
+ warnOnUndefinedDerivedState(ctor, partialState);
+ }
+ // Merge the partial state and the previous state.
+ var memoizedState = partialState === null || partialState === undefined ? prevState : _assign({}, prevState, partialState);
+ workInProgress.memoizedState = memoizedState;
+
+ // Once the update queue is empty, persist the derived state onto the
+ // base state.
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null && workInProgress.expirationTime === NoWork) {
+ updateQueue.baseState = memoizedState;
+ }
+}
+
+var classComponentUpdater = {
+ isMounted: isMounted,
+ enqueueSetState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.payload = payload;
+ if (callback !== undefined && callback !== null) {
+ {
+ warnOnInvalidCallback$1(callback, 'setState');
+ }
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueReplaceState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.tag = ReplaceState;
+ update.payload = payload;
+
+ if (callback !== undefined && callback !== null) {
+ {
+ warnOnInvalidCallback$1(callback, 'replaceState');
+ }
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueForceUpdate: function (inst, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.tag = ForceUpdate;
+
+ if (callback !== undefined && callback !== null) {
+ {
+ warnOnInvalidCallback$1(callback, 'forceUpdate');
+ }
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ }
+};
+
+function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
+ var instance = workInProgress.stateNode;
+ if (typeof instance.shouldComponentUpdate === 'function') {
+ startPhaseTimer(workInProgress, 'shouldComponentUpdate');
+ var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
+ stopPhaseTimer();
+
+ {
+ !(shouldUpdate !== undefined) ? warningWithoutStack$1(false, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentName(ctor) || 'Component') : void 0;
+ }
+
+ return shouldUpdate;
+ }
+
+ if (ctor.prototype && ctor.prototype.isPureReactComponent) {
+ return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState);
+ }
+
+ return true;
+}
+
+function checkClassInstance(workInProgress, ctor, newProps) {
+ var instance = workInProgress.stateNode;
+ {
+ var name = getComponentName(ctor) || 'Component';
+ var renderPresent = instance.render;
+
+ if (!renderPresent) {
+ if (ctor.prototype && typeof ctor.prototype.render === 'function') {
+ warningWithoutStack$1(false, '%s(...): No `render` method found on the returned component ' + 'instance: did you accidentally return an object from the constructor?', name);
+ } else {
+ warningWithoutStack$1(false, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', name);
+ }
+ }
+
+ var noGetInitialStateOnES6 = !instance.getInitialState || instance.getInitialState.isReactClassApproved || instance.state;
+ !noGetInitialStateOnES6 ? warningWithoutStack$1(false, 'getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', name) : void 0;
+ var noGetDefaultPropsOnES6 = !instance.getDefaultProps || instance.getDefaultProps.isReactClassApproved;
+ !noGetDefaultPropsOnES6 ? warningWithoutStack$1(false, 'getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', name) : void 0;
+ var noInstancePropTypes = !instance.propTypes;
+ !noInstancePropTypes ? warningWithoutStack$1(false, 'propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', name) : void 0;
+ var noInstanceContextType = !instance.contextType;
+ !noInstanceContextType ? warningWithoutStack$1(false, 'contextType was defined as an instance property on %s. Use a static ' + 'property to define contextType instead.', name) : void 0;
+ var noInstanceContextTypes = !instance.contextTypes;
+ !noInstanceContextTypes ? warningWithoutStack$1(false, 'contextTypes was defined as an instance property on %s. Use a static ' + 'property to define contextTypes instead.', name) : void 0;
+
+ if (ctor.contextType && ctor.contextTypes && !didWarnAboutContextTypeAndContextTypes.has(ctor)) {
+ didWarnAboutContextTypeAndContextTypes.add(ctor);
+ warningWithoutStack$1(false, '%s declares both contextTypes and contextType static properties. ' + 'The legacy contextTypes property will be ignored.', name);
+ }
+
+ var noComponentShouldUpdate = typeof instance.componentShouldUpdate !== 'function';
+ !noComponentShouldUpdate ? warningWithoutStack$1(false, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', name) : void 0;
+ if (ctor.prototype && ctor.prototype.isPureReactComponent && typeof instance.shouldComponentUpdate !== 'undefined') {
+ warningWithoutStack$1(false, '%s has a method called shouldComponentUpdate(). ' + 'shouldComponentUpdate should not be used when extending React.PureComponent. ' + 'Please extend React.Component if shouldComponentUpdate is used.', getComponentName(ctor) || 'A pure component');
+ }
+ var noComponentDidUnmount = typeof instance.componentDidUnmount !== 'function';
+ !noComponentDidUnmount ? warningWithoutStack$1(false, '%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', name) : void 0;
+ var noComponentDidReceiveProps = typeof instance.componentDidReceiveProps !== 'function';
+ !noComponentDidReceiveProps ? warningWithoutStack$1(false, '%s has a method called ' + 'componentDidReceiveProps(). But there is no such lifecycle method. ' + 'If you meant to update the state in response to changing props, ' + 'use componentWillReceiveProps(). If you meant to fetch data or ' + 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().', name) : void 0;
+ var noComponentWillRecieveProps = typeof instance.componentWillRecieveProps !== 'function';
+ !noComponentWillRecieveProps ? warningWithoutStack$1(false, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name) : void 0;
+ var noUnsafeComponentWillRecieveProps = typeof instance.UNSAFE_componentWillRecieveProps !== 'function';
+ !noUnsafeComponentWillRecieveProps ? warningWithoutStack$1(false, '%s has a method called ' + 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?', name) : void 0;
+ var hasMutatedProps = instance.props !== newProps;
+ !(instance.props === undefined || !hasMutatedProps) ? warningWithoutStack$1(false, '%s(...): When calling super() in `%s`, make sure to pass ' + "up the same props that your component's constructor was passed.", name, name) : void 0;
+ var noInstanceDefaultProps = !instance.defaultProps;
+ !noInstanceDefaultProps ? warningWithoutStack$1(false, 'Setting defaultProps as an instance property on %s is not supported and will be ignored.' + ' Instead, define defaultProps as a static property on %s.', name, name) : void 0;
+
+ if (typeof instance.getSnapshotBeforeUpdate === 'function' && typeof instance.componentDidUpdate !== 'function' && !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)) {
+ didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
+ warningWithoutStack$1(false, '%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' + 'This component defines getSnapshotBeforeUpdate() only.', getComponentName(ctor));
+ }
+
+ var noInstanceGetDerivedStateFromProps = typeof instance.getDerivedStateFromProps !== 'function';
+ !noInstanceGetDerivedStateFromProps ? warningWithoutStack$1(false, '%s: getDerivedStateFromProps() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name) : void 0;
+ var noInstanceGetDerivedStateFromCatch = typeof instance.getDerivedStateFromError !== 'function';
+ !noInstanceGetDerivedStateFromCatch ? warningWithoutStack$1(false, '%s: getDerivedStateFromError() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name) : void 0;
+ var noStaticGetSnapshotBeforeUpdate = typeof ctor.getSnapshotBeforeUpdate !== 'function';
+ !noStaticGetSnapshotBeforeUpdate ? warningWithoutStack$1(false, '%s: getSnapshotBeforeUpdate() is defined as a static method ' + 'and will be ignored. Instead, declare it as an instance method.', name) : void 0;
+ var _state = instance.state;
+ if (_state && (typeof _state !== 'object' || isArray$1(_state))) {
+ warningWithoutStack$1(false, '%s.state: must be set to an object or null', name);
+ }
+ if (typeof instance.getChildContext === 'function') {
+ !(typeof ctor.childContextTypes === 'object') ? warningWithoutStack$1(false, '%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', name) : void 0;
+ }
+ }
+}
+
+function adoptClassInstance(workInProgress, instance) {
+ instance.updater = classComponentUpdater;
+ workInProgress.stateNode = instance;
+ // The instance needs access to the fiber so that it can schedule updates
+ set(instance, workInProgress);
+ {
+ instance._reactInternalInstance = fakeInternalInstance;
+ }
+}
+
+function constructClassInstance(workInProgress, ctor, props, renderExpirationTime) {
+ var isLegacyContextConsumer = false;
+ var unmaskedContext = emptyContextObject;
+ var context = null;
+ var contextType = ctor.contextType;
+
+ {
+ if ('contextType' in ctor) {
+ var isValid =
+ // Allow null for conditional declaration
+ contextType === null || contextType !== undefined && contextType.$$typeof === REACT_CONTEXT_TYPE && contextType._context === undefined; // Not a <Context.Consumer>
+
+ if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
+ didWarnAboutInvalidateContextType.add(ctor);
+
+ var addendum = '';
+ if (contextType === undefined) {
+ addendum = ' However, it is set to undefined. ' + 'This can be caused by a typo or by mixing up named and default imports. ' + 'This can also happen due to a circular dependency, so ' + 'try moving the createContext() call to a separate file.';
+ } else if (typeof contextType !== 'object') {
+ addendum = ' However, it is set to a ' + typeof contextType + '.';
+ } else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
+ addendum = ' Did you accidentally pass the Context.Provider instead?';
+ } else if (contextType._context !== undefined) {
+ // <Context.Consumer>
+ addendum = ' Did you accidentally pass the Context.Consumer instead?';
+ } else {
+ addendum = ' However, it is set to an object with keys {' + Object.keys(contextType).join(', ') + '}.';
+ }
+ warningWithoutStack$1(false, '%s defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext().%s', getComponentName(ctor) || 'Component', addendum);
+ }
+ }
+ }
+
+ if (typeof contextType === 'object' && contextType !== null) {
+ context = readContext(contextType);
+ } else {
+ unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ var contextTypes = ctor.contextTypes;
+ isLegacyContextConsumer = contextTypes !== null && contextTypes !== undefined;
+ context = isLegacyContextConsumer ? getMaskedContext(workInProgress, unmaskedContext) : emptyContextObject;
+ }
+
+ // Instantiate twice to help detect side-effects.
+ {
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ new ctor(props, context); // eslint-disable-line no-new
+ }
+ }
+
+ var instance = new ctor(props, context);
+ var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
+ adoptClassInstance(workInProgress, instance);
+
+ {
+ if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
+ var componentName = getComponentName(ctor) || 'Component';
+ if (!didWarnAboutUninitializedState.has(componentName)) {
+ didWarnAboutUninitializedState.add(componentName);
+ warningWithoutStack$1(false, '`%s` uses `getDerivedStateFromProps` but its initial state is ' + '%s. This is not recommended. Instead, define the initial state by ' + 'assigning an object to `this.state` in the constructor of `%s`. ' + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.', componentName, instance.state === null ? 'null' : 'undefined', componentName);
+ }
+ }
+
+ // If new component APIs are defined, "unsafe" lifecycles won't be called.
+ // Warn about these lifecycles if they are present.
+ // Don't warn about react-lifecycles-compat polyfilled methods though.
+ if (typeof ctor.getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function') {
+ var foundWillMountName = null;
+ var foundWillReceivePropsName = null;
+ var foundWillUpdateName = null;
+ if (typeof instance.componentWillMount === 'function' && instance.componentWillMount.__suppressDeprecationWarning !== true) {
+ foundWillMountName = 'componentWillMount';
+ } else if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ foundWillMountName = 'UNSAFE_componentWillMount';
+ }
+ if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) {
+ foundWillReceivePropsName = 'componentWillReceiveProps';
+ } else if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
+ foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
+ }
+ if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) {
+ foundWillUpdateName = 'componentWillUpdate';
+ } else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
+ foundWillUpdateName = 'UNSAFE_componentWillUpdate';
+ }
+ if (foundWillMountName !== null || foundWillReceivePropsName !== null || foundWillUpdateName !== null) {
+ var _componentName = getComponentName(ctor) || 'Component';
+ var newApiName = typeof ctor.getDerivedStateFromProps === 'function' ? 'getDerivedStateFromProps()' : 'getSnapshotBeforeUpdate()';
+ if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(_componentName)) {
+ didWarnAboutLegacyLifecyclesAndDerivedState.add(_componentName);
+ warningWithoutStack$1(false, 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' + 'The above lifecycles should be removed. Learn more about this warning here:\n' + 'https://fb.me/react-async-component-lifecycle-hooks', _componentName, newApiName, foundWillMountName !== null ? '\n ' + foundWillMountName : '', foundWillReceivePropsName !== null ? '\n ' + foundWillReceivePropsName : '', foundWillUpdateName !== null ? '\n ' + foundWillUpdateName : '');
+ }
+ }
+ }
+ }
+
+ // Cache unmasked context so we can avoid recreating masked context unless necessary.
+ // ReactFiberContext usually updates this cache but can't for newly-created instances.
+ if (isLegacyContextConsumer) {
+ cacheContext(workInProgress, unmaskedContext, context);
+ }
+
+ return instance;
+}
+
+function callComponentWillMount(workInProgress, instance) {
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ var oldState = instance.state;
+
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
+ }
+
+ stopPhaseTimer();
+
+ if (oldState !== instance.state) {
+ {
+ warningWithoutStack$1(false, '%s.componentWillMount(): Assigning directly to this.state is ' + "deprecated (except inside a component's " + 'constructor). Use setState instead.', getComponentName(workInProgress.type) || 'Component');
+ }
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
+
+function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) {
+ var oldState = instance.state;
+ startPhaseTimer(workInProgress, 'componentWillReceiveProps');
+ if (typeof instance.componentWillReceiveProps === 'function') {
+ instance.componentWillReceiveProps(newProps, nextContext);
+ }
+ if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
+ instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
+ }
+ stopPhaseTimer();
+
+ if (instance.state !== oldState) {
+ {
+ var componentName = getComponentName(workInProgress.type) || 'Component';
+ if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
+ didWarnAboutStateAssignmentForComponent.add(componentName);
+ warningWithoutStack$1(false, '%s.componentWillReceiveProps(): Assigning directly to ' + "this.state is deprecated (except inside a component's " + 'constructor). Use setState instead.', componentName);
+ }
+ }
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
+
+// Invokes the mount life-cycles on a previously never rendered instance.
+function mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
+ {
+ checkClassInstance(workInProgress, ctor, newProps);
+ }
+
+ var instance = workInProgress.stateNode;
+ instance.props = newProps;
+ instance.state = workInProgress.memoizedState;
+ instance.refs = emptyRefsObject;
+
+ var contextType = ctor.contextType;
+ if (typeof contextType === 'object' && contextType !== null) {
+ instance.context = readContext(contextType);
+ } else {
+ var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ instance.context = getMaskedContext(workInProgress, unmaskedContext);
+ }
+
+ {
+ if (instance.state === newProps) {
+ var componentName = getComponentName(ctor) || 'Component';
+ if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
+ didWarnAboutDirectlyAssigningPropsToState.add(componentName);
+ warningWithoutStack$1(false, '%s: It is not recommended to assign props directly to state ' + "because updates to props won't be reflected in state. " + 'In most cases, it is better to use props directly.', componentName);
+ }
+ }
+
+ if (workInProgress.mode & StrictMode) {
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(workInProgress, instance);
+
+ ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, instance);
+ }
+
+ if (warnAboutDeprecatedLifecycles) {
+ ReactStrictModeWarnings.recordDeprecationWarnings(workInProgress, instance);
+ }
+ }
+
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ instance.state = workInProgress.memoizedState;
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ instance.state = workInProgress.memoizedState;
+ }
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
+ callComponentWillMount(workInProgress, instance);
+ // If we had additional state updates during this life-cycle, let's
+ // process them now.
+ updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ instance.state = workInProgress.memoizedState;
+ }
+ }
+
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+}
+
+function resumeMountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+
+ var oldProps = workInProgress.memoizedProps;
+ instance.props = oldProps;
+
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = void 0;
+ if (typeof contextType === 'object' && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
+ if (oldProps !== newProps || oldContext !== nextContext) {
+ callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
+ }
+ }
+
+ resetHasForceUpdateBeforeProcessing();
+
+ var oldState = workInProgress.memoizedState;
+ var newState = instance.state = oldState;
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ newState = workInProgress.memoizedState;
+ }
+ if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ return false;
+ }
+
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ newState = workInProgress.memoizedState;
+ }
+
+ var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
+ }
+ stopPhaseTimer();
+ }
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ }
+
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+
+ return shouldUpdate;
+}
+
+// Invokes the update life-cycles and returns false if it shouldn't rerender.
+function updateClassInstance(current, workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+
+ var oldProps = workInProgress.memoizedProps;
+ instance.props = workInProgress.type === workInProgress.elementType ? oldProps : resolveDefaultProps(workInProgress.type, oldProps);
+
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = void 0;
+ if (typeof contextType === 'object' && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
+ if (oldProps !== newProps || oldContext !== nextContext) {
+ callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
+ }
+ }
+
+ resetHasForceUpdateBeforeProcessing();
+
+ var oldState = workInProgress.memoizedState;
+ var newState = instance.state = oldState;
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ newState = workInProgress.memoizedState;
+ }
+
+ if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Update;
+ }
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Snapshot;
+ }
+ }
+ return false;
+ }
+
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ newState = workInProgress.memoizedState;
+ }
+
+ var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function')) {
+ startPhaseTimer(workInProgress, 'componentWillUpdate');
+ if (typeof instance.componentWillUpdate === 'function') {
+ instance.componentWillUpdate(newProps, newState, nextContext);
+ }
+ if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
+ instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
+ }
+ stopPhaseTimer();
+ }
+ if (typeof instance.componentDidUpdate === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ workInProgress.effectTag |= Snapshot;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Update;
+ }
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Snapshot;
+ }
+ }
+
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized props/state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ }
+
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+
+ return shouldUpdate;
+}
+
+var didWarnAboutMaps = void 0;
+var didWarnAboutGenerators = void 0;
+var didWarnAboutStringRefInStrictMode = void 0;
+var ownerHasKeyUseWarning = void 0;
+var ownerHasFunctionTypeWarning = void 0;
+var warnForMissingKey = function (child) {};
+
+{
+ didWarnAboutMaps = false;
+ didWarnAboutGenerators = false;
+ didWarnAboutStringRefInStrictMode = {};
+
+ /**
+ * Warn if there's no key explicitly set on dynamic arrays of children or
+ * object keys are not valid. This allows us to keep track of children between
+ * updates.
+ */
+ ownerHasKeyUseWarning = {};
+ ownerHasFunctionTypeWarning = {};
+
+ warnForMissingKey = function (child) {
+ if (child === null || typeof child !== 'object') {
+ return;
+ }
+ if (!child._store || child._store.validated || child.key != null) {
+ return;
+ }
+ !(typeof child._store === 'object') ? invariant(false, 'React Component in warnForMissingKey should have a _store. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ child._store.validated = true;
+
+ var currentComponentErrorInfo = 'Each child in a list should have a unique ' + '"key" prop. See https://fb.me/react-warning-keys for ' + 'more information.' + getCurrentFiberStackInDev();
+ if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
+ return;
+ }
+ ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
+
+ warning$1(false, 'Each child in a list should have a unique ' + '"key" prop. See https://fb.me/react-warning-keys for ' + 'more information.');
+ };
+}
+
+var isArray = Array.isArray;
+
+function coerceRef(returnFiber, current$$1, element) {
+ var mixedRef = element.ref;
+ if (mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object') {
+ {
+ if (returnFiber.mode & StrictMode) {
+ var componentName = getComponentName(returnFiber.type) || 'Component';
+ if (!didWarnAboutStringRefInStrictMode[componentName]) {
+ warningWithoutStack$1(false, 'A string ref, "%s", has been found within a strict mode tree. ' + 'String refs are a source of potential bugs and should be avoided. ' + 'We recommend using createRef() instead.' + '\n%s' + '\n\nLearn more about using refs safely here:' + '\nhttps://fb.me/react-strict-mode-string-ref', mixedRef, getStackByFiberInDevAndProd(returnFiber));
+ didWarnAboutStringRefInStrictMode[componentName] = true;
+ }
+ }
+ }
+
+ if (element._owner) {
+ var owner = element._owner;
+ var inst = void 0;
+ if (owner) {
+ var ownerFiber = owner;
+ !(ownerFiber.tag === ClassComponent) ? invariant(false, 'Function components cannot have refs. Did you mean to use React.forwardRef()?') : void 0;
+ inst = ownerFiber.stateNode;
+ }
+ !inst ? invariant(false, 'Missing owner for string ref %s. This error is likely caused by a bug in React. Please file an issue.', mixedRef) : void 0;
+ var stringRef = '' + mixedRef;
+ // Check if previous string ref matches new string ref
+ if (current$$1 !== null && current$$1.ref !== null && typeof current$$1.ref === 'function' && current$$1.ref._stringRef === stringRef) {
+ return current$$1.ref;
+ }
+ var ref = function (value) {
+ var refs = inst.refs;
+ if (refs === emptyRefsObject) {
+ // This is a lazy pooled frozen object, so we need to initialize.
+ refs = inst.refs = {};
+ }
+ if (value === null) {
+ delete refs[stringRef];
+ } else {
+ refs[stringRef] = value;
+ }
+ };
+ ref._stringRef = stringRef;
+ return ref;
+ } else {
+ !(typeof mixedRef === 'string') ? invariant(false, 'Expected ref to be a function, a string, an object returned by React.createRef(), or null.') : void 0;
+ !element._owner ? invariant(false, 'Element ref was specified as a string (%s) but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a function component\n2. You may be adding a ref to a component that was not created inside a component\'s render method\n3. You have multiple copies of React loaded\nSee https://fb.me/react-refs-must-have-owner for more information.', mixedRef) : void 0;
+ }
+ }
+ return mixedRef;
+}
+
+function throwOnInvalidObjectType(returnFiber, newChild) {
+ if (returnFiber.type !== 'textarea') {
+ var addendum = '';
+ {
+ addendum = ' If you meant to render a collection of children, use an array ' + 'instead.' + getCurrentFiberStackInDev();
+ }
+ invariant(false, 'Objects are not valid as a React child (found: %s).%s', Object.prototype.toString.call(newChild) === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : newChild, addendum);
+ }
+}
+
+function warnOnFunctionType() {
+ var currentComponentErrorInfo = 'Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of <Component /> from render. ' + 'Or maybe you meant to call this function rather than return it.' + getCurrentFiberStackInDev();
+
+ if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) {
+ return;
+ }
+ ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true;
+
+ warning$1(false, 'Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of <Component /> from render. ' + 'Or maybe you meant to call this function rather than return it.');
+}
+
+// This wrapper function exists because I expect to clone the code in each path
+// to be able to optimize each path individually by branching early. This needs
+// a compiler or we can do it manually. Helpers that don't need this branching
+// live outside of this function.
+function ChildReconciler(shouldTrackSideEffects) {
+ function deleteChild(returnFiber, childToDelete) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return;
+ }
+ // Deletions are added in reversed order so we add it to the front.
+ // At this point, the return fiber's effect list is empty except for
+ // deletions, so we can just append the deletion to the list. The remaining
+ // effects aren't added until the complete phase. Once we implement
+ // resuming, this may not be true.
+ var last = returnFiber.lastEffect;
+ if (last !== null) {
+ last.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+ childToDelete.nextEffect = null;
+ childToDelete.effectTag = Deletion;
+ }
+
+ function deleteRemainingChildren(returnFiber, currentFirstChild) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return null;
+ }
+
+ // TODO: For the shouldClone case, this could be micro-optimized a bit by
+ // assuming that after the first child we've already added everything.
+ var childToDelete = currentFirstChild;
+ while (childToDelete !== null) {
+ deleteChild(returnFiber, childToDelete);
+ childToDelete = childToDelete.sibling;
+ }
+ return null;
+ }
+
+ function mapRemainingChildren(returnFiber, currentFirstChild) {
+ // Add the remaining children to a temporary map so that we can find them by
+ // keys quickly. Implicit (null) keys get added to this set with their index
+ var existingChildren = new Map();
+
+ var existingChild = currentFirstChild;
+ while (existingChild !== null) {
+ if (existingChild.key !== null) {
+ existingChildren.set(existingChild.key, existingChild);
+ } else {
+ existingChildren.set(existingChild.index, existingChild);
+ }
+ existingChild = existingChild.sibling;
+ }
+ return existingChildren;
+ }
+
+ function useFiber(fiber, pendingProps, expirationTime) {
+ // We currently set sibling to null and index to 0 here because it is easy
+ // to forget to do before returning it. E.g. for the single child case.
+ var clone = createWorkInProgress(fiber, pendingProps, expirationTime);
+ clone.index = 0;
+ clone.sibling = null;
+ return clone;
+ }
+
+ function placeChild(newFiber, lastPlacedIndex, newIndex) {
+ newFiber.index = newIndex;
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return lastPlacedIndex;
+ }
+ var current$$1 = newFiber.alternate;
+ if (current$$1 !== null) {
+ var oldIndex = current$$1.index;
+ if (oldIndex < lastPlacedIndex) {
+ // This is a move.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
+ } else {
+ // This item can stay in place.
+ return oldIndex;
+ }
+ } else {
+ // This is an insertion.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
+ }
+ }
+
+ function placeSingleChild(newFiber) {
+ // This is simpler for the single child case. We only need to do a
+ // placement for inserting new children.
+ if (shouldTrackSideEffects && newFiber.alternate === null) {
+ newFiber.effectTag = Placement;
+ }
+ return newFiber;
+ }
+
+ function updateTextNode(returnFiber, current$$1, textContent, expirationTime) {
+ if (current$$1 === null || current$$1.tag !== HostText) {
+ // Insert
+ var created = createFiberFromText(textContent, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, textContent, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function updateElement(returnFiber, current$$1, element, expirationTime) {
+ if (current$$1 !== null && current$$1.elementType === element.type) {
+ // Move based on index
+ var existing = useFiber(current$$1, element.props, expirationTime);
+ existing.ref = coerceRef(returnFiber, current$$1, element);
+ existing.return = returnFiber;
+ {
+ existing._debugSource = element._source;
+ existing._debugOwner = element._owner;
+ }
+ return existing;
+ } else {
+ // Insert
+ var created = createFiberFromElement(element, returnFiber.mode, expirationTime);
+ created.ref = coerceRef(returnFiber, current$$1, element);
+ created.return = returnFiber;
+ return created;
+ }
+ }
+
+ function updatePortal(returnFiber, current$$1, portal, expirationTime) {
+ if (current$$1 === null || current$$1.tag !== HostPortal || current$$1.stateNode.containerInfo !== portal.containerInfo || current$$1.stateNode.implementation !== portal.implementation) {
+ // Insert
+ var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, portal.children || [], expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function updateFragment(returnFiber, current$$1, fragment, expirationTime, key) {
+ if (current$$1 === null || current$$1.tag !== Fragment) {
+ // Insert
+ var created = createFiberFromFragment(fragment, returnFiber.mode, expirationTime, key);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, fragment, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function createChild(returnFiber, newChild, expirationTime) {
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ var created = createFiberFromText('' + newChild, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ var _created = createFiberFromElement(newChild, returnFiber.mode, expirationTime);
+ _created.ref = coerceRef(returnFiber, null, newChild);
+ _created.return = returnFiber;
+ return _created;
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ var _created2 = createFiberFromPortal(newChild, returnFiber.mode, expirationTime);
+ _created2.return = returnFiber;
+ return _created2;
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _created3 = createFiberFromFragment(newChild, returnFiber.mode, expirationTime, null);
+ _created3.return = returnFiber;
+ return _created3;
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ {
+ if (typeof newChild === 'function') {
+ warnOnFunctionType();
+ }
+ }
+
+ return null;
+ }
+
+ function updateSlot(returnFiber, oldFiber, newChild, expirationTime) {
+ // Update the fiber if the keys match, otherwise return null.
+
+ var key = oldFiber !== null ? oldFiber.key : null;
+
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ if (key !== null) {
+ return null;
+ }
+ return updateTextNode(returnFiber, oldFiber, '' + newChild, expirationTime);
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ if (newChild.key === key) {
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(returnFiber, oldFiber, newChild.props.children, expirationTime, key);
+ }
+ return updateElement(returnFiber, oldFiber, newChild, expirationTime);
+ } else {
+ return null;
+ }
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ if (newChild.key === key) {
+ return updatePortal(returnFiber, oldFiber, newChild, expirationTime);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ if (key !== null) {
+ return null;
+ }
+
+ return updateFragment(returnFiber, oldFiber, newChild, expirationTime, null);
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ {
+ if (typeof newChild === 'function') {
+ warnOnFunctionType();
+ }
+ }
+
+ return null;
+ }
+
+ function updateFromMap(existingChildren, returnFiber, newIdx, newChild, expirationTime) {
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys, so we neither have to check the old nor
+ // new node for the key. If both are text nodes, they match.
+ var matchedFiber = existingChildren.get(newIdx) || null;
+ return updateTextNode(returnFiber, matchedFiber, '' + newChild, expirationTime);
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(returnFiber, _matchedFiber, newChild.props.children, expirationTime, newChild.key);
+ }
+ return updateElement(returnFiber, _matchedFiber, newChild, expirationTime);
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
+ return updatePortal(returnFiber, _matchedFiber2, newChild, expirationTime);
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _matchedFiber3 = existingChildren.get(newIdx) || null;
+ return updateFragment(returnFiber, _matchedFiber3, newChild, expirationTime, null);
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ {
+ if (typeof newChild === 'function') {
+ warnOnFunctionType();
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Warns if there is a duplicate or missing key
+ */
+ function warnOnInvalidKey(child, knownKeys) {
+ {
+ if (typeof child !== 'object' || child === null) {
+ return knownKeys;
+ }
+ switch (child.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ case REACT_PORTAL_TYPE:
+ warnForMissingKey(child);
+ var key = child.key;
+ if (typeof key !== 'string') {
+ break;
+ }
+ if (knownKeys === null) {
+ knownKeys = new Set();
+ knownKeys.add(key);
+ break;
+ }
+ if (!knownKeys.has(key)) {
+ knownKeys.add(key);
+ break;
+ }
+ warning$1(false, 'Encountered two children with the same key, `%s`. ' + 'Keys should be unique so that components maintain their identity ' + 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.', key);
+ break;
+ default:
+ break;
+ }
+ }
+ return knownKeys;
+ }
+
+ function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) {
+ // This algorithm can't optimize by searching from both ends since we
+ // don't have backpointers on fibers. I'm trying to see how far we can get
+ // with that model. If it ends up not being worth the tradeoffs, we can
+ // add it later.
+
+ // Even with a two ended optimization, we'd want to optimize for the case
+ // where there are few changes and brute force the comparison instead of
+ // going for the Map. It'd like to explore hitting that path first in
+ // forward-only mode and only go for the Map once we notice that we need
+ // lots of look ahead. This doesn't handle reversal as well as two ended
+ // search but that's unusual. Besides, for the two ended optimization to
+ // work on Iterables, we'd need to copy the whole set.
+
+ // In this first iteration, we'll just live with hitting the bad case
+ // (adding everything to a Map) in for every insert/move.
+
+ // If you change this code, also update reconcileChildrenIterator() which
+ // uses the same algorithm.
+
+ {
+ // First, validate keys.
+ var knownKeys = null;
+ for (var i = 0; i < newChildren.length; i++) {
+ var child = newChildren[i];
+ knownKeys = warnOnInvalidKey(child, knownKeys);
+ }
+ }
+
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+ for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], expirationTime);
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (oldFiber === null) {
+ oldFiber = nextOldFiber;
+ }
+ break;
+ }
+ if (shouldTrackSideEffects) {
+ if (oldFiber && newFiber.alternate === null) {
+ // We matched the slot, but we didn't reuse the existing fiber, so we
+ // need to delete the existing child.
+ deleteChild(returnFiber, oldFiber);
+ }
+ }
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = newFiber;
+ } else {
+ // TODO: Defer siblings if we're not at the right index for this slot.
+ // I.e. if we had null values before, then we want to defer this
+ // for each null value. However, we also don't want to call updateSlot
+ // with the previous one.
+ previousNewFiber.sibling = newFiber;
+ }
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
+
+ if (newIdx === newChildren.length) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
+
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber = createChild(returnFiber, newChildren[newIdx], expirationTime);
+ if (!_newFiber) {
+ continue;
+ }
+ lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber;
+ } else {
+ previousNewFiber.sibling = _newFiber;
+ }
+ previousNewFiber = _newFiber;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime);
+ if (_newFiber2) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber2.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key);
+ }
+ }
+ lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber2;
+ } else {
+ previousNewFiber.sibling = _newFiber2;
+ }
+ previousNewFiber = _newFiber2;
+ }
+ }
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function (child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, expirationTime) {
+ // This is the same implementation as reconcileChildrenArray(),
+ // but using the iterator instead.
+
+ var iteratorFn = getIteratorFn(newChildrenIterable);
+ !(typeof iteratorFn === 'function') ? invariant(false, 'An object is not an iterable. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+
+ {
+ // We don't support rendering Generators because it's a mutation.
+ // See https://github.com/facebook/react/issues/12995
+ if (typeof Symbol === 'function' &&
+ // $FlowFixMe Flow doesn't know about toStringTag
+ newChildrenIterable[Symbol.toStringTag] === 'Generator') {
+ !didWarnAboutGenerators ? warning$1(false, 'Using Generators as children is unsupported and will likely yield ' + 'unexpected results because enumerating a generator mutates it. ' + 'You may convert it to an array with `Array.from()` or the ' + '`[...spread]` operator before rendering. Keep in mind ' + 'you might need to polyfill these features for older browsers.') : void 0;
+ didWarnAboutGenerators = true;
+ }
+
+ // Warn about using Maps as children
+ if (newChildrenIterable.entries === iteratorFn) {
+ !didWarnAboutMaps ? warning$1(false, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.') : void 0;
+ didWarnAboutMaps = true;
+ }
+
+ // First, validate keys.
+ // We'll get a different iterator later for the main pass.
+ var _newChildren = iteratorFn.call(newChildrenIterable);
+ if (_newChildren) {
+ var knownKeys = null;
+ var _step = _newChildren.next();
+ for (; !_step.done; _step = _newChildren.next()) {
+ var child = _step.value;
+ knownKeys = warnOnInvalidKey(child, knownKeys);
+ }
+ }
+ }
+
+ var newChildren = iteratorFn.call(newChildrenIterable);
+ !(newChildren != null) ? invariant(false, 'An iterable object provided no iterator.') : void 0;
+
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+
+ var step = newChildren.next();
+ for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(returnFiber, oldFiber, step.value, expirationTime);
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (!oldFiber) {
+ oldFiber = nextOldFiber;
+ }
+ break;
+ }
+ if (shouldTrackSideEffects) {
+ if (oldFiber && newFiber.alternate === null) {
+ // We matched the slot, but we didn't reuse the existing fiber, so we
+ // need to delete the existing child.
+ deleteChild(returnFiber, oldFiber);
+ }
+ }
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = newFiber;
+ } else {
+ // TODO: Defer siblings if we're not at the right index for this slot.
+ // I.e. if we had null values before, then we want to defer this
+ // for each null value. However, we also don't want to call updateSlot
+ // with the previous one.
+ previousNewFiber.sibling = newFiber;
+ }
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
+
+ if (step.done) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
+
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber3 = createChild(returnFiber, step.value, expirationTime);
+ if (_newFiber3 === null) {
+ continue;
+ }
+ lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber3;
+ } else {
+ previousNewFiber.sibling = _newFiber3;
+ }
+ previousNewFiber = _newFiber3;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, expirationTime);
+ if (_newFiber4 !== null) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber4.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(_newFiber4.key === null ? newIdx : _newFiber4.key);
+ }
+ }
+ lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber4;
+ } else {
+ previousNewFiber.sibling = _newFiber4;
+ }
+ previousNewFiber = _newFiber4;
+ }
+ }
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function (child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, expirationTime) {
+ // There's no need to check for keys on text nodes since we don't have a
+ // way to define them.
+ if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
+ // We already have an existing node so let's just update it and delete
+ // the rest.
+ deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
+ var existing = useFiber(currentFirstChild, textContent, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ // The existing first child is not a text node so we need to create one
+ // and delete the existing ones.
+ deleteRemainingChildren(returnFiber, currentFirstChild);
+ var created = createFiberFromText(textContent, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
+ var key = element.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime);
+ existing.ref = coerceRef(returnFiber, child, element);
+ existing.return = returnFiber;
+ {
+ existing._debugSource = element._source;
+ existing._debugOwner = element._owner;
+ }
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ if (element.type === REACT_FRAGMENT_TYPE) {
+ var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key);
+ created.return = returnFiber;
+ return created;
+ } else {
+ var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime);
+ _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
+ _created4.return = returnFiber;
+ return _created4;
+ }
+ }
+
+ function reconcileSinglePortal(returnFiber, currentFirstChild, portal, expirationTime) {
+ var key = portal.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, portal.children || [], expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ // This API will tag the children with the side-effect of the reconciliation
+ // itself. They will be added to the side-effect list as we pass through the
+ // children and the parent.
+ function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
+ // This function is not recursive.
+ // If the top level item is an array, we treat it as a set of children,
+ // not as a fragment. Nested arrays on the other hand will be treated as
+ // fragment nodes. Recursion happens at the normal flow.
+
+ // Handle top level unkeyed fragments as if they were arrays.
+ // This leads to an ambiguity between <>{[...]}</> and <>...</>.
+ // We treat the ambiguous cases above the same.
+ var isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null;
+ if (isUnkeyedTopLevelFragment) {
+ newChild = newChild.props.children;
+ }
+
+ // Handle object types
+ var isObject = typeof newChild === 'object' && newChild !== null;
+
+ if (isObject) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime));
+ case REACT_PORTAL_TYPE:
+ return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime));
+ }
+ }
+
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime));
+ }
+
+ if (isArray(newChild)) {
+ return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime);
+ }
+
+ if (getIteratorFn(newChild)) {
+ return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime);
+ }
+
+ if (isObject) {
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ {
+ if (typeof newChild === 'function') {
+ warnOnFunctionType();
+ }
+ }
+ if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
+ // If the new child is undefined, and the return fiber is a composite
+ // component, throw an error. If Fiber return types are disabled,
+ // we already threw above.
+ switch (returnFiber.tag) {
+ case ClassComponent:
+ {
+ {
+ var instance = returnFiber.stateNode;
+ if (instance.render._isMockFunction) {
+ // We allow auto-mocks to proceed as if they're returning null.
+ break;
+ }
+ }
+ }
+ // Intentionally fall through to the next case, which handles both
+ // functions and classes
+ // eslint-disable-next-lined no-fallthrough
+ case FunctionComponent:
+ {
+ var Component = returnFiber.type;
+ invariant(false, '%s(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.', Component.displayName || Component.name || 'Component');
+ }
+ }
+ }
+
+ // Remaining cases are all treated as empty.
+ return deleteRemainingChildren(returnFiber, currentFirstChild);
+ }
+
+ return reconcileChildFibers;
+}
+
+var reconcileChildFibers = ChildReconciler(true);
+var mountChildFibers = ChildReconciler(false);
+
+function cloneChildFibers(current$$1, workInProgress) {
+ !(current$$1 === null || workInProgress.child === current$$1.child) ? invariant(false, 'Resuming work not yet implemented.') : void 0;
+
+ if (workInProgress.child === null) {
+ return;
+ }
+
+ var currentChild = workInProgress.child;
+ var newChild = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
+ workInProgress.child = newChild;
+
+ newChild.return = workInProgress;
+ while (currentChild.sibling !== null) {
+ currentChild = currentChild.sibling;
+ newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
+ newChild.return = workInProgress;
+ }
+ newChild.sibling = null;
+}
+
+var NO_CONTEXT = {};
+
+var contextStackCursor$1 = createCursor(NO_CONTEXT);
+var contextFiberStackCursor = createCursor(NO_CONTEXT);
+var rootInstanceStackCursor = createCursor(NO_CONTEXT);
+
+function requiredContext(c) {
+ !(c !== NO_CONTEXT) ? invariant(false, 'Expected host context to exist. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ return c;
+}
+
+function getRootHostContainer() {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ return rootInstance;
+}
+
+function pushHostContainer(fiber, nextRootInstance) {
+ // Push current root instance onto the stack;
+ // This allows us to reset root when portals are popped.
+ push(rootInstanceStackCursor, nextRootInstance, fiber);
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+
+ // Finally, we need to push the host context to the stack.
+ // However, we can't just call getRootHostContext() and push it because
+ // we'd have a different number of entries on the stack depending on
+ // whether getRootHostContext() throws somewhere in renderer code or not.
+ // So we push an empty value first. This lets us safely unwind on errors.
+ push(contextStackCursor$1, NO_CONTEXT, fiber);
+ var nextRootContext = getRootHostContext(nextRootInstance);
+ // Now that we know this function doesn't throw, replace it.
+ pop(contextStackCursor$1, fiber);
+ push(contextStackCursor$1, nextRootContext, fiber);
+}
+
+function popHostContainer(fiber) {
+ pop(contextStackCursor$1, fiber);
+ pop(contextFiberStackCursor, fiber);
+ pop(rootInstanceStackCursor, fiber);
+}
+
+function getHostContext() {
+ var context = requiredContext(contextStackCursor$1.current);
+ return context;
+}
+
+function pushHostContext(fiber) {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ var context = requiredContext(contextStackCursor$1.current);
+ var nextContext = getChildHostContext(context, fiber.type, rootInstance);
+
+ // Don't push this Fiber's context unless it's unique.
+ if (context === nextContext) {
+ return;
+ }
+
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+ push(contextStackCursor$1, nextContext, fiber);
+}
+
+function popHostContext(fiber) {
+ // Do not pop unless this Fiber provided the current context.
+ // pushHostContext() only pushes Fibers that provide unique contexts.
+ if (contextFiberStackCursor.current !== fiber) {
+ return;
+ }
+
+ pop(contextStackCursor$1, fiber);
+ pop(contextFiberStackCursor, fiber);
+}
+
+var NoEffect$1 = /* */0;
+var UnmountSnapshot = /* */2;
+var UnmountMutation = /* */4;
+var MountMutation = /* */8;
+var UnmountLayout = /* */16;
+var MountLayout = /* */32;
+var MountPassive = /* */64;
+var UnmountPassive = /* */128;
+
+var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher;
+
+
+var didWarnAboutMismatchedHooksForComponent = void 0;
+{
+ didWarnAboutMismatchedHooksForComponent = new Set();
+}
+
+// These are set right before calling the component.
+var renderExpirationTime = NoWork;
+// The work-in-progress fiber. I've named it differently to distinguish it from
+// the work-in-progress hook.
+var currentlyRenderingFiber$1 = null;
+
+// Hooks are stored as a linked list on the fiber's memoizedState field. The
+// current hook list is the list that belongs to the current fiber. The
+// work-in-progress hook list is a new list that will be added to the
+// work-in-progress fiber.
+var currentHook = null;
+var nextCurrentHook = null;
+var firstWorkInProgressHook = null;
+var workInProgressHook = null;
+var nextWorkInProgressHook = null;
+
+var remainingExpirationTime = NoWork;
+var componentUpdateQueue = null;
+var sideEffectTag = 0;
+
+// Updates scheduled during render will trigger an immediate re-render at the
+// end of the current pass. We can't store these updates on the normal queue,
+// because if the work is aborted, they should be discarded. Because this is
+// a relatively rare case, we also don't want to add an additional field to
+// either the hook or queue object types. So we store them in a lazily create
+// map of queue -> render-phase updates, which are discarded once the component
+// completes without re-rendering.
+
+// Whether an update was scheduled during the currently executing render pass.
+var didScheduleRenderPhaseUpdate = false;
+// Lazily created map of render-phase updates
+var renderPhaseUpdates = null;
+// Counter to prevent infinite loops.
+var numberOfReRenders = 0;
+var RE_RENDER_LIMIT = 25;
+
+// In DEV, this is the name of the currently executing primitive hook
+var currentHookNameInDev = null;
+
+// In DEV, this list ensures that hooks are called in the same order between renders.
+// The list stores the order of hooks used during the initial render (mount).
+// Subsequent renders (updates) reference this list.
+var hookTypesDev = null;
+var hookTypesUpdateIndexDev = -1;
+
+function mountHookTypesDev() {
+ {
+ var hookName = currentHookNameInDev;
+
+ if (hookTypesDev === null) {
+ hookTypesDev = [hookName];
+ } else {
+ hookTypesDev.push(hookName);
+ }
+ }
+}
+
+function updateHookTypesDev() {
+ {
+ var hookName = currentHookNameInDev;
+
+ if (hookTypesDev !== null) {
+ hookTypesUpdateIndexDev++;
+ if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
+ warnOnHookMismatchInDev(hookName);
+ }
+ }
+ }
+}
+
+function warnOnHookMismatchInDev(currentHookName) {
+ {
+ var componentName = getComponentName(currentlyRenderingFiber$1.type);
+ if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
+ didWarnAboutMismatchedHooksForComponent.add(componentName);
+
+ if (hookTypesDev !== null) {
+ var table = '';
+
+ var secondColumnStart = 30;
+
+ for (var i = 0; i <= hookTypesUpdateIndexDev; i++) {
+ var oldHookName = hookTypesDev[i];
+ var newHookName = i === hookTypesUpdateIndexDev ? currentHookName : oldHookName;
+
+ var row = i + 1 + '. ' + oldHookName;
+
+ // Extra space so second column lines up
+ // lol @ IE not supporting String#repeat
+ while (row.length < secondColumnStart) {
+ row += ' ';
+ }
+
+ row += newHookName + '\n';
+
+ table += row;
+ }
+
+ warning$1(false, 'React has detected a change in the order of Hooks called by %s. ' + 'This will lead to bugs and errors if not fixed. ' + 'For more information, read the Rules of Hooks: https://fb.me/rules-of-hooks\n\n' + ' Previous render Next render\n' + ' ------------------------------------------------------\n' + '%s' + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n', componentName, table);
+ }
+ }
+ }
+}
+
+function throwInvalidHookError() {
+ invariant(false, 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.');
+}
+
+function areHookInputsEqual(nextDeps, prevDeps) {
+ if (prevDeps === null) {
+ {
+ warning$1(false, '%s received a final argument during this render, but not during ' + 'the previous render. Even though the final argument is optional, ' + 'its type cannot change between renders.', currentHookNameInDev);
+ }
+ return false;
+ }
+
+ {
+ // Don't bother comparing lengths in prod because these arrays should be
+ // passed inline.
+ if (nextDeps.length !== prevDeps.length) {
+ warning$1(false, 'The final argument passed to %s changed size between renders. The ' + 'order and size of this array must remain constant.\n\n' + 'Previous: %s\n' + 'Incoming: %s', currentHookNameInDev, '[' + nextDeps.join(', ') + ']', '[' + prevDeps.join(', ') + ']');
+ }
+ }
+ for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
+ if (is(nextDeps[i], prevDeps[i])) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+function renderWithHooks(current, workInProgress, Component, props, refOrContext, nextRenderExpirationTime) {
+ renderExpirationTime = nextRenderExpirationTime;
+ currentlyRenderingFiber$1 = workInProgress;
+ nextCurrentHook = current !== null ? current.memoizedState : null;
+
+ {
+ hookTypesDev = current !== null ? current._debugHookTypes : null;
+ hookTypesUpdateIndexDev = -1;
+ }
+
+ // The following should have already been reset
+ // currentHook = null;
+ // workInProgressHook = null;
+
+ // remainingExpirationTime = NoWork;
+ // componentUpdateQueue = null;
+
+ // didScheduleRenderPhaseUpdate = false;
+ // renderPhaseUpdates = null;
+ // numberOfReRenders = 0;
+ // sideEffectTag = 0;
+
+ // TODO Warn if no hooks are used at all during mount, then some are used during update.
+ // Currently we will identify the update render as a mount because nextCurrentHook === null.
+ // This is tricky because it's valid for certain types of components (e.g. React.lazy)
+
+ // Using nextCurrentHook to differentiate between mount/update only works if at least one stateful hook is used.
+ // Non-stateful hooks (e.g. context) don't get added to memoizedState,
+ // so nextCurrentHook would be null during updates and mounts.
+ {
+ if (nextCurrentHook !== null) {
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
+ } else if (hookTypesDev !== null) {
+ // This dispatcher handles an edge case where a component is updating,
+ // but no stateful hooks have been used.
+ // We want to match the production code behavior (which will use HooksDispatcherOnMount),
+ // but with the extra DEV validation to ensure hooks ordering hasn't changed.
+ // This dispatcher does that.
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
+ } else {
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
+ }
+ }
+
+ var children = Component(props, refOrContext);
+
+ if (didScheduleRenderPhaseUpdate) {
+ do {
+ didScheduleRenderPhaseUpdate = false;
+ numberOfReRenders += 1;
+
+ // Start over from the beginning of the list
+ nextCurrentHook = current !== null ? current.memoizedState : null;
+ nextWorkInProgressHook = firstWorkInProgressHook;
+
+ currentHook = null;
+ workInProgressHook = null;
+ componentUpdateQueue = null;
+
+ {
+ // Also validate hook order for cascading updates.
+ hookTypesUpdateIndexDev = -1;
+ }
+
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
+
+ children = Component(props, refOrContext);
+ } while (didScheduleRenderPhaseUpdate);
+
+ renderPhaseUpdates = null;
+ numberOfReRenders = 0;
+ }
+
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrancy.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
+
+ var renderedWork = currentlyRenderingFiber$1;
+
+ renderedWork.memoizedState = firstWorkInProgressHook;
+ renderedWork.expirationTime = remainingExpirationTime;
+ renderedWork.updateQueue = componentUpdateQueue;
+ renderedWork.effectTag |= sideEffectTag;
+
+ {
+ renderedWork._debugHookTypes = hookTypesDev;
+ }
+
+ // This check uses currentHook so that it works the same in DEV and prod bundles.
+ // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
+ var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
+
+ renderExpirationTime = NoWork;
+ currentlyRenderingFiber$1 = null;
+
+ currentHook = null;
+ nextCurrentHook = null;
+ firstWorkInProgressHook = null;
+ workInProgressHook = null;
+ nextWorkInProgressHook = null;
+
+ {
+ currentHookNameInDev = null;
+ hookTypesDev = null;
+ hookTypesUpdateIndexDev = -1;
+ }
+
+ remainingExpirationTime = NoWork;
+ componentUpdateQueue = null;
+ sideEffectTag = 0;
+
+ // These were reset above
+ // didScheduleRenderPhaseUpdate = false;
+ // renderPhaseUpdates = null;
+ // numberOfReRenders = 0;
+
+ !!didRenderTooFewHooks ? invariant(false, 'Rendered fewer hooks than expected. This may be caused by an accidental early return statement.') : void 0;
+
+ return children;
+}
+
+function bailoutHooks(current, workInProgress, expirationTime) {
+ workInProgress.updateQueue = current.updateQueue;
+ workInProgress.effectTag &= ~(Passive | Update);
+ if (current.expirationTime <= expirationTime) {
+ current.expirationTime = NoWork;
+ }
+}
+
+function resetHooks() {
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrancy.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
+
+ // This is used to reset the state of this module when a component throws.
+ // It's also called inside mountIndeterminateComponent if we determine the
+ // component is a module-style component.
+ renderExpirationTime = NoWork;
+ currentlyRenderingFiber$1 = null;
+
+ currentHook = null;
+ nextCurrentHook = null;
+ firstWorkInProgressHook = null;
+ workInProgressHook = null;
+ nextWorkInProgressHook = null;
+
+ {
+ hookTypesDev = null;
+ hookTypesUpdateIndexDev = -1;
+
+ currentHookNameInDev = null;
+ }
+
+ remainingExpirationTime = NoWork;
+ componentUpdateQueue = null;
+ sideEffectTag = 0;
+
+ didScheduleRenderPhaseUpdate = false;
+ renderPhaseUpdates = null;
+ numberOfReRenders = 0;
+}
+
+function mountWorkInProgressHook() {
+ var hook = {
+ memoizedState: null,
+
+ baseState: null,
+ queue: null,
+ baseUpdate: null,
+
+ next: null
+ };
+
+ if (workInProgressHook === null) {
+ // This is the first hook in the list
+ firstWorkInProgressHook = workInProgressHook = hook;
+ } else {
+ // Append to the end of the list
+ workInProgressHook = workInProgressHook.next = hook;
+ }
+ return workInProgressHook;
+}
+
+function updateWorkInProgressHook() {
+ // This function is used both for updates and for re-renders triggered by a
+ // render phase update. It assumes there is either a current hook we can
+ // clone, or a work-in-progress hook from a previous render pass that we can
+ // use as a base. When we reach the end of the base list, we must switch to
+ // the dispatcher used for mounts.
+ if (nextWorkInProgressHook !== null) {
+ // There's already a work-in-progress. Reuse it.
+ workInProgressHook = nextWorkInProgressHook;
+ nextWorkInProgressHook = workInProgressHook.next;
+
+ currentHook = nextCurrentHook;
+ nextCurrentHook = currentHook !== null ? currentHook.next : null;
+ } else {
+ // Clone from the current hook.
+ !(nextCurrentHook !== null) ? invariant(false, 'Rendered more hooks than during the previous render.') : void 0;
+ currentHook = nextCurrentHook;
+
+ var newHook = {
+ memoizedState: currentHook.memoizedState,
+
+ baseState: currentHook.baseState,
+ queue: currentHook.queue,
+ baseUpdate: currentHook.baseUpdate,
+
+ next: null
+ };
+
+ if (workInProgressHook === null) {
+ // This is the first hook in the list.
+ workInProgressHook = firstWorkInProgressHook = newHook;
+ } else {
+ // Append to the end of the list.
+ workInProgressHook = workInProgressHook.next = newHook;
+ }
+ nextCurrentHook = currentHook.next;
+ }
+ return workInProgressHook;
+}
+
+function createFunctionComponentUpdateQueue() {
+ return {
+ lastEffect: null
+ };
+}
+
+function basicStateReducer(state, action) {
+ return typeof action === 'function' ? action(state) : action;
+}
+
+function mountReducer(reducer, initialArg, init) {
+ var hook = mountWorkInProgressHook();
+ var initialState = void 0;
+ if (init !== undefined) {
+ initialState = init(initialArg);
+ } else {
+ initialState = initialArg;
+ }
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = hook.queue = {
+ last: null,
+ dispatch: null,
+ lastRenderedReducer: reducer,
+ lastRenderedState: initialState
+ };
+ var dispatch = queue.dispatch = dispatchAction.bind(null,
+ // Flow doesn't know this is non-null, but we do.
+ currentlyRenderingFiber$1, queue);
+ return [hook.memoizedState, dispatch];
+}
+
+function updateReducer(reducer, initialArg, init) {
+ var hook = updateWorkInProgressHook();
+ var queue = hook.queue;
+ !(queue !== null) ? invariant(false, 'Should have a queue. This is likely a bug in React. Please file an issue.') : void 0;
+
+ queue.lastRenderedReducer = reducer;
+
+ if (numberOfReRenders > 0) {
+ // This is a re-render. Apply the new render phase updates to the previous
+ var _dispatch = queue.dispatch;
+ if (renderPhaseUpdates !== null) {
+ // Render phase updates are stored in a map of queue -> linked list
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate !== undefined) {
+ renderPhaseUpdates.delete(queue);
+ var newState = hook.memoizedState;
+ var update = firstRenderPhaseUpdate;
+ do {
+ // Process this render phase update. We don't have to check the
+ // priority because it will always be the same as the current
+ // render's.
+ var _action = update.action;
+ newState = reducer(newState, _action);
+ update = update.next;
+ } while (update !== null);
+
+ // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
+ if (!is(newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
+ }
+
+ hook.memoizedState = newState;
+ // Don't persist the state accumlated from the render phase updates to
+ // the base state unless the queue is empty.
+ // TODO: Not sure if this is the desired semantics, but it's what we
+ // do for gDSFP. I can't remember why.
+ if (hook.baseUpdate === queue.last) {
+ hook.baseState = newState;
+ }
+
+ queue.lastRenderedState = newState;
+
+ return [newState, _dispatch];
+ }
+ }
+ return [hook.memoizedState, _dispatch];
+ }
+
+ // The last update in the entire queue
+ var last = queue.last;
+ // The last update that is part of the base state.
+ var baseUpdate = hook.baseUpdate;
+ var baseState = hook.baseState;
+
+ // Find the first unprocessed update.
+ var first = void 0;
+ if (baseUpdate !== null) {
+ if (last !== null) {
+ // For the first update, the queue is a circular linked list where
+ // `queue.last.next = queue.first`. Once the first update commits, and
+ // the `baseUpdate` is no longer empty, we can unravel the list.
+ last.next = null;
+ }
+ first = baseUpdate.next;
+ } else {
+ first = last !== null ? last.next : null;
+ }
+ if (first !== null) {
+ var _newState = baseState;
+ var newBaseState = null;
+ var newBaseUpdate = null;
+ var prevUpdate = baseUpdate;
+ var _update = first;
+ var didSkip = false;
+ do {
+ var updateExpirationTime = _update.expirationTime;
+ if (updateExpirationTime < renderExpirationTime) {
+ // Priority is insufficient. Skip this update. If this is the first
+ // skipped update, the previous update/state is the new base
+ // update/state.
+ if (!didSkip) {
+ didSkip = true;
+ newBaseUpdate = prevUpdate;
+ newBaseState = _newState;
+ }
+ // Update the remaining priority in the queue.
+ if (updateExpirationTime > remainingExpirationTime) {
+ remainingExpirationTime = updateExpirationTime;
+ }
+ } else {
+ // Process this update.
+ if (_update.eagerReducer === reducer) {
+ // If this update was processed eagerly, and its reducer matches the
+ // current reducer, we can use the eagerly computed state.
+ _newState = _update.eagerState;
+ } else {
+ var _action2 = _update.action;
+ _newState = reducer(_newState, _action2);
+ }
+ }
+ prevUpdate = _update;
+ _update = _update.next;
+ } while (_update !== null && _update !== first);
+
+ if (!didSkip) {
+ newBaseUpdate = prevUpdate;
+ newBaseState = _newState;
+ }
+
+ // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
+ if (!is(_newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
+ }
+
+ hook.memoizedState = _newState;
+ hook.baseUpdate = newBaseUpdate;
+ hook.baseState = newBaseState;
+
+ queue.lastRenderedState = _newState;
+ }
+
+ var dispatch = queue.dispatch;
+ return [hook.memoizedState, dispatch];
+}
+
+function mountState(initialState) {
+ var hook = mountWorkInProgressHook();
+ if (typeof initialState === 'function') {
+ initialState = initialState();
+ }
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = hook.queue = {
+ last: null,
+ dispatch: null,
+ lastRenderedReducer: basicStateReducer,
+ lastRenderedState: initialState
+ };
+ var dispatch = queue.dispatch = dispatchAction.bind(null,
+ // Flow doesn't know this is non-null, but we do.
+ currentlyRenderingFiber$1, queue);
+ return [hook.memoizedState, dispatch];
+}
+
+function updateState(initialState) {
+ return updateReducer(basicStateReducer, initialState);
+}
+
+function pushEffect(tag, create, destroy, deps) {
+ var effect = {
+ tag: tag,
+ create: create,
+ destroy: destroy,
+ deps: deps,
+ // Circular
+ next: null
+ };
+ if (componentUpdateQueue === null) {
+ componentUpdateQueue = createFunctionComponentUpdateQueue();
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var _lastEffect = componentUpdateQueue.lastEffect;
+ if (_lastEffect === null) {
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var firstEffect = _lastEffect.next;
+ _lastEffect.next = effect;
+ effect.next = firstEffect;
+ componentUpdateQueue.lastEffect = effect;
+ }
+ }
+ return effect;
+}
+
+function mountRef(initialValue) {
+ var hook = mountWorkInProgressHook();
+ var ref = { current: initialValue };
+ {
+ Object.seal(ref);
+ }
+ hook.memoizedState = ref;
+ return ref;
+}
+
+function updateRef(initialValue) {
+ var hook = updateWorkInProgressHook();
+ return hook.memoizedState;
+}
+
+function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ sideEffectTag |= fiberEffectTag;
+ hook.memoizedState = pushEffect(hookEffectTag, create, undefined, nextDeps);
+}
+
+function updateEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var destroy = undefined;
+
+ if (currentHook !== null) {
+ var prevEffect = currentHook.memoizedState;
+ destroy = prevEffect.destroy;
+ if (nextDeps !== null) {
+ var prevDeps = prevEffect.deps;
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ pushEffect(NoEffect$1, create, destroy, nextDeps);
+ return;
+ }
+ }
+ }
+
+ sideEffectTag |= fiberEffectTag;
+ hook.memoizedState = pushEffect(hookEffectTag, create, destroy, nextDeps);
+}
+
+function mountEffect(create, deps) {
+ return mountEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
+}
+
+function updateEffect(create, deps) {
+ return updateEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
+}
+
+function mountLayoutEffect(create, deps) {
+ return mountEffectImpl(Update, UnmountMutation | MountLayout, create, deps);
+}
+
+function updateLayoutEffect(create, deps) {
+ return updateEffectImpl(Update, UnmountMutation | MountLayout, create, deps);
+}
+
+function imperativeHandleEffect(create, ref) {
+ if (typeof ref === 'function') {
+ var refCallback = ref;
+ var _inst = create();
+ refCallback(_inst);
+ return function () {
+ refCallback(null);
+ };
+ } else if (ref !== null && ref !== undefined) {
+ var refObject = ref;
+ {
+ !refObject.hasOwnProperty('current') ? warning$1(false, 'Expected useImperativeHandle() first argument to either be a ' + 'ref callback or React.createRef() object. Instead received: %s.', 'an object with keys {' + Object.keys(refObject).join(', ') + '}') : void 0;
+ }
+ var _inst2 = create();
+ refObject.current = _inst2;
+ return function () {
+ refObject.current = null;
+ };
+ }
+}
+
+function mountImperativeHandle(ref, create, deps) {
+ {
+ !(typeof create === 'function') ? warning$1(false, 'Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null') : void 0;
+ }
+
+ // TODO: If deps are provided, should we skip comparing the ref itself?
+ var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+
+ return mountEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
+}
+
+function updateImperativeHandle(ref, create, deps) {
+ {
+ !(typeof create === 'function') ? warning$1(false, 'Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null') : void 0;
+ }
+
+ // TODO: If deps are provided, should we skip comparing the ref itself?
+ var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+
+ return updateEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
+}
+
+function mountDebugValue(value, formatterFn) {
+ // This hook is normally a no-op.
+ // The react-debug-hooks package injects its own implementation
+ // so that e.g. DevTools can display custom hook values.
+}
+
+var updateDebugValue = mountDebugValue;
+
+function mountCallback(callback, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
+
+function updateCallback(callback, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState;
+ if (prevState !== null) {
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
+
+function mountMemo(nextCreate, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+function updateMemo(nextCreate, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState;
+ if (prevState !== null) {
+ // Assume these are defined. If they're not, areHookInputsEqual will warn.
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+// in a test-like environment, we want to warn if dispatchAction()
+// is called outside of a batchedUpdates/TestUtils.act(...) call.
+var shouldWarnForUnbatchedSetState = false;
+
+{
+ // jest isn't a 'global', it's just exposed to tests via a wrapped function
+ // further, this isn't a test file, so flow doesn't recognize the symbol. So...
+ // $FlowExpectedError - because requirements don't give a damn about your type sigs.
+ if ('undefined' !== typeof jest) {
+ shouldWarnForUnbatchedSetState = true;
+ }
+}
+
+function dispatchAction(fiber, queue, action) {
+ !(numberOfReRenders < RE_RENDER_LIMIT) ? invariant(false, 'Too many re-renders. React limits the number of renders to prevent an infinite loop.') : void 0;
+
+ {
+ !(arguments.length <= 3) ? warning$1(false, "State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().') : void 0;
+ }
+
+ var alternate = fiber.alternate;
+ if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {
+ // This is a render phase update. Stash it in a lazily-created map of
+ // queue -> linked list of updates. After this render pass, we'll restart
+ // and apply the stashed updates on top of the work-in-progress hook.
+ didScheduleRenderPhaseUpdate = true;
+ var update = {
+ expirationTime: renderExpirationTime,
+ action: action,
+ eagerReducer: null,
+ eagerState: null,
+ next: null
+ };
+ if (renderPhaseUpdates === null) {
+ renderPhaseUpdates = new Map();
+ }
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate === undefined) {
+ renderPhaseUpdates.set(queue, update);
+ } else {
+ // Append the update to the end of the list.
+ var lastRenderPhaseUpdate = firstRenderPhaseUpdate;
+ while (lastRenderPhaseUpdate.next !== null) {
+ lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
+ }
+ lastRenderPhaseUpdate.next = update;
+ }
+ } else {
+ flushPassiveEffects();
+
+ var currentTime = requestCurrentTime();
+ var _expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var _update2 = {
+ expirationTime: _expirationTime,
+ action: action,
+ eagerReducer: null,
+ eagerState: null,
+ next: null
+ };
+
+ // Append the update to the end of the list.
+ var _last = queue.last;
+ if (_last === null) {
+ // This is the first update. Create a circular list.
+ _update2.next = _update2;
+ } else {
+ var first = _last.next;
+ if (first !== null) {
+ // Still circular.
+ _update2.next = first;
+ }
+ _last.next = _update2;
+ }
+ queue.last = _update2;
+
+ if (fiber.expirationTime === NoWork && (alternate === null || alternate.expirationTime === NoWork)) {
+ // The queue is currently empty, which means we can eagerly compute the
+ // next state before entering the render phase. If the new state is the
+ // same as the current state, we may be able to bail out entirely.
+ var _lastRenderedReducer = queue.lastRenderedReducer;
+ if (_lastRenderedReducer !== null) {
+ var prevDispatcher = void 0;
+ {
+ prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
+ }
+ try {
+ var currentState = queue.lastRenderedState;
+ var _eagerState = _lastRenderedReducer(currentState, action);
+ // Stash the eagerly computed state, and the reducer used to compute
+ // it, on the update object. If the reducer hasn't changed by the
+ // time we enter the render phase, then the eager state can be used
+ // without calling the reducer again.
+ _update2.eagerReducer = _lastRenderedReducer;
+ _update2.eagerState = _eagerState;
+ if (is(_eagerState, currentState)) {
+ // Fast path. We can bail out without scheduling React to re-render.
+ // It's still possible that we'll need to rebase this update later,
+ // if the component re-renders for a different reason and by that
+ // time the reducer has changed.
+ return;
+ }
+ } catch (error) {
+ // Suppress the error. It will throw again in the render phase.
+ } finally {
+ {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ }
+ }
+ }
+ {
+ if (shouldWarnForUnbatchedSetState === true) {
+ warnIfNotCurrentlyBatchingInDev(fiber);
+ }
+ }
+ scheduleWork(fiber, _expirationTime);
+ }
+}
+
+var ContextOnlyDispatcher = {
+ readContext: readContext,
+
+ useCallback: throwInvalidHookError,
+ useContext: throwInvalidHookError,
+ useEffect: throwInvalidHookError,
+ useImperativeHandle: throwInvalidHookError,
+ useLayoutEffect: throwInvalidHookError,
+ useMemo: throwInvalidHookError,
+ useReducer: throwInvalidHookError,
+ useRef: throwInvalidHookError,
+ useState: throwInvalidHookError,
+ useDebugValue: throwInvalidHookError
+};
+
+var HooksDispatcherOnMountInDEV = null;
+var HooksDispatcherOnMountWithHookTypesInDEV = null;
+var HooksDispatcherOnUpdateInDEV = null;
+var InvalidNestedHooksDispatcherOnMountInDEV = null;
+var InvalidNestedHooksDispatcherOnUpdateInDEV = null;
+
+{
+ var warnInvalidContextAccess = function () {
+ warning$1(false, 'Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
+ };
+
+ var warnInvalidHookAccess = function () {
+ warning$1(false, 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see ' + 'https://fb.me/rules-of-hooks');
+ };
+
+ HooksDispatcherOnMountInDEV = {
+ readContext: function (context, observedBits) {
+ return readContext(context, observedBits);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = 'useCallback';
+ mountHookTypesDev();
+ return mountCallback(callback, deps);
+ },
+ useContext: function (context, observedBits) {
+ currentHookNameInDev = 'useContext';
+ mountHookTypesDev();
+ return readContext(context, observedBits);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = 'useEffect';
+ mountHookTypesDev();
+ return mountEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = 'useImperativeHandle';
+ mountHookTypesDev();
+ return mountImperativeHandle(ref, create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = 'useLayoutEffect';
+ mountHookTypesDev();
+ return mountLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = 'useMemo';
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = 'useReducer';
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = 'useRef';
+ mountHookTypesDev();
+ return mountRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = 'useState';
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = 'useDebugValue';
+ mountHookTypesDev();
+ return mountDebugValue(value, formatterFn);
+ }
+ };
+
+ HooksDispatcherOnMountWithHookTypesInDEV = {
+ readContext: function (context, observedBits) {
+ return readContext(context, observedBits);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = 'useCallback';
+ updateHookTypesDev();
+ return mountCallback(callback, deps);
+ },
+ useContext: function (context, observedBits) {
+ currentHookNameInDev = 'useContext';
+ updateHookTypesDev();
+ return readContext(context, observedBits);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = 'useEffect';
+ updateHookTypesDev();
+ return mountEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = 'useImperativeHandle';
+ updateHookTypesDev();
+ return mountImperativeHandle(ref, create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = 'useLayoutEffect';
+ updateHookTypesDev();
+ return mountLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = 'useMemo';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = 'useReducer';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = 'useRef';
+ updateHookTypesDev();
+ return mountRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = 'useState';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = 'useDebugValue';
+ updateHookTypesDev();
+ return mountDebugValue(value, formatterFn);
+ }
+ };
+
+ HooksDispatcherOnUpdateInDEV = {
+ readContext: function (context, observedBits) {
+ return readContext(context, observedBits);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = 'useCallback';
+ updateHookTypesDev();
+ return updateCallback(callback, deps);
+ },
+ useContext: function (context, observedBits) {
+ currentHookNameInDev = 'useContext';
+ updateHookTypesDev();
+ return readContext(context, observedBits);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = 'useEffect';
+ updateHookTypesDev();
+ return updateEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = 'useImperativeHandle';
+ updateHookTypesDev();
+ return updateImperativeHandle(ref, create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = 'useLayoutEffect';
+ updateHookTypesDev();
+ return updateLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = 'useMemo';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
+ try {
+ return updateMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = 'useReducer';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
+ try {
+ return updateReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = 'useRef';
+ updateHookTypesDev();
+ return updateRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = 'useState';
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
+ try {
+ return updateState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = 'useDebugValue';
+ updateHookTypesDev();
+ return updateDebugValue(value, formatterFn);
+ }
+ };
+
+ InvalidNestedHooksDispatcherOnMountInDEV = {
+ readContext: function (context, observedBits) {
+ warnInvalidContextAccess();
+ return readContext(context, observedBits);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = 'useCallback';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountCallback(callback, deps);
+ },
+ useContext: function (context, observedBits) {
+ currentHookNameInDev = 'useContext';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return readContext(context, observedBits);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = 'useEffect';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = 'useImperativeHandle';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountImperativeHandle(ref, create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = 'useLayoutEffect';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = 'useMemo';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = 'useReducer';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = 'useRef';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = 'useState';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
+ try {
+ return mountState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = 'useDebugValue';
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountDebugValue(value, formatterFn);
+ }
+ };
+
+ InvalidNestedHooksDispatcherOnUpdateInDEV = {
+ readContext: function (context, observedBits) {
+ warnInvalidContextAccess();
+ return readContext(context, observedBits);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = 'useCallback';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateCallback(callback, deps);
+ },
+ useContext: function (context, observedBits) {
+ currentHookNameInDev = 'useContext';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return readContext(context, observedBits);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = 'useEffect';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = 'useImperativeHandle';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateImperativeHandle(ref, create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = 'useLayoutEffect';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = 'useMemo';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
+ try {
+ return updateMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = 'useReducer';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
+ try {
+ return updateReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = 'useRef';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = 'useState';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
+ try {
+ return updateState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = 'useDebugValue';
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateDebugValue(value, formatterFn);
+ }
+ };
+}
+
+var commitTime = 0;
+var profilerStartTime = -1;
+
+function getCommitTime() {
+ return commitTime;
+}
+
+function recordCommitTime() {
+ if (!enableProfilerTimer) {
+ return;
+ }
+ commitTime = unstable_now();
+}
+
+function startProfilerTimer(fiber) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+
+ profilerStartTime = unstable_now();
+
+ if (fiber.actualStartTime < 0) {
+ fiber.actualStartTime = unstable_now();
+ }
+}
+
+function stopProfilerTimerIfRunning(fiber) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+ profilerStartTime = -1;
+}
+
+function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+
+ if (profilerStartTime >= 0) {
+ var elapsedTime = unstable_now() - profilerStartTime;
+ fiber.actualDuration += elapsedTime;
+ if (overrideBaseTime) {
+ fiber.selfBaseDuration = elapsedTime;
+ }
+ profilerStartTime = -1;
+ }
+}
+
+// The deepest Fiber on the stack involved in a hydration context.
+// This may have been an insertion or a hydration.
+var hydrationParentFiber = null;
+var nextHydratableInstance = null;
+var isHydrating = false;
+
+function enterHydrationState(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+
+ var parentInstance = fiber.stateNode.containerInfo;
+ nextHydratableInstance = getFirstHydratableChild(parentInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ return true;
+}
+
+function reenterHydrationStateFromDehydratedSuspenseInstance(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+
+ var suspenseInstance = fiber.stateNode;
+ nextHydratableInstance = getNextHydratableSibling(suspenseInstance);
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return true;
+}
+
+function deleteHydratableInstance(returnFiber, instance) {
+ {
+ switch (returnFiber.tag) {
+ case HostRoot:
+ didNotHydrateContainerInstance(returnFiber.stateNode.containerInfo, instance);
+ break;
+ case HostComponent:
+ didNotHydrateInstance(returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance);
+ break;
+ }
+ }
+
+ var childToDelete = createFiberFromHostInstanceForDeletion();
+ childToDelete.stateNode = instance;
+ childToDelete.return = returnFiber;
+ childToDelete.effectTag = Deletion;
+
+ // This might seem like it belongs on progressedFirstDeletion. However,
+ // these children are not part of the reconciliation list of children.
+ // Even if we abort and rereconcile the children, that will try to hydrate
+ // again and the nodes are still in the host tree so these will be
+ // recreated.
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+}
+
+function insertNonHydratedInstance(returnFiber, fiber) {
+ fiber.effectTag |= Placement;
+ {
+ switch (returnFiber.tag) {
+ case HostRoot:
+ {
+ var parentContainer = returnFiber.stateNode.containerInfo;
+ switch (fiber.tag) {
+ case HostComponent:
+ var type = fiber.type;
+ var props = fiber.pendingProps;
+ didNotFindHydratableContainerInstance(parentContainer, type, props);
+ break;
+ case HostText:
+ var text = fiber.pendingProps;
+ didNotFindHydratableContainerTextInstance(parentContainer, text);
+ break;
+ case SuspenseComponent:
+
+ break;
+ }
+ break;
+ }
+ case HostComponent:
+ {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
+ switch (fiber.tag) {
+ case HostComponent:
+ var _type = fiber.type;
+ var _props = fiber.pendingProps;
+ didNotFindHydratableInstance(parentType, parentProps, parentInstance, _type, _props);
+ break;
+ case HostText:
+ var _text = fiber.pendingProps;
+ didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, _text);
+ break;
+ case SuspenseComponent:
+ didNotFindHydratableSuspenseInstance(parentType, parentProps, parentInstance);
+ break;
+ }
+ break;
+ }
+ default:
+ return;
+ }
+ }
+}
+
+function tryHydrate(fiber, nextInstance) {
+ switch (fiber.tag) {
+ case HostComponent:
+ {
+ var type = fiber.type;
+ var props = fiber.pendingProps;
+ var instance = canHydrateInstance(nextInstance, type, props);
+ if (instance !== null) {
+ fiber.stateNode = instance;
+ return true;
+ }
+ return false;
+ }
+ case HostText:
+ {
+ var text = fiber.pendingProps;
+ var textInstance = canHydrateTextInstance(nextInstance, text);
+ if (textInstance !== null) {
+ fiber.stateNode = textInstance;
+ return true;
+ }
+ return false;
+ }
+ case SuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
+ if (suspenseInstance !== null) {
+ // Downgrade the tag to a dehydrated component until we've hydrated it.
+ fiber.tag = DehydratedSuspenseComponent;
+ fiber.stateNode = suspenseInstance;
+ return true;
+ }
+ }
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+function tryToClaimNextHydratableInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
+ var nextInstance = nextHydratableInstance;
+ if (!nextInstance) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ var firstAttemptedInstance = nextInstance;
+ if (!tryHydrate(fiber, nextInstance)) {
+ // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
+ nextInstance = getNextHydratableSibling(firstAttemptedInstance);
+ if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ // We matched the next one, we'll now assume that the first one was
+ // superfluous and we'll delete it. Since we can't eagerly delete it
+ // we'll have to schedule a deletion. To do that, this node needs a dummy
+ // fiber associated with it.
+ deleteHydratableInstance(hydrationParentFiber, firstAttemptedInstance);
+ }
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = getFirstHydratableChild(nextInstance);
+}
+
+function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) {
+ if (!supportsHydration) {
+ invariant(false, 'Expected prepareToHydrateHostInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.');
+ }
+
+ var instance = fiber.stateNode;
+ var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber);
+ // TODO: Type this specific to this type of component.
+ fiber.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update.
+ if (updatePayload !== null) {
+ return true;
+ }
+ return false;
+}
+
+function prepareToHydrateHostTextInstance(fiber) {
+ if (!supportsHydration) {
+ invariant(false, 'Expected prepareToHydrateHostTextInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.');
+ }
+
+ var textInstance = fiber.stateNode;
+ var textContent = fiber.memoizedProps;
+ var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
+ {
+ if (shouldUpdate) {
+ // We assume that prepareToHydrateHostTextInstance is called in a context where the
+ // hydration parent is the parent host component of this host text.
+ var returnFiber = hydrationParentFiber;
+ if (returnFiber !== null) {
+ switch (returnFiber.tag) {
+ case HostRoot:
+ {
+ var parentContainer = returnFiber.stateNode.containerInfo;
+ didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, textContent);
+ break;
+ }
+ case HostComponent:
+ {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
+ didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, textContent);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return shouldUpdate;
+}
+
+function skipPastDehydratedSuspenseInstance(fiber) {
+ if (!supportsHydration) {
+ invariant(false, 'Expected skipPastDehydratedSuspenseInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.');
+ }
+ var suspenseInstance = fiber.stateNode;
+ !suspenseInstance ? invariant(false, 'Expected to have a hydrated suspense instance. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ nextHydratableInstance = getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
+}
+
+function popToNextHostParent(fiber) {
+ var parent = fiber.return;
+ while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot && parent.tag !== DehydratedSuspenseComponent) {
+ parent = parent.return;
+ }
+ hydrationParentFiber = parent;
+}
+
+function popHydrationState(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+ if (fiber !== hydrationParentFiber) {
+ // We're deeper than the current hydration context, inside an inserted
+ // tree.
+ return false;
+ }
+ if (!isHydrating) {
+ // If we're not currently hydrating but we're in a hydration context, then
+ // we were an insertion and now need to pop up reenter hydration of our
+ // siblings.
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return false;
+ }
+
+ var type = fiber.type;
+
+ // If we have any remaining hydratable nodes, we need to delete them now.
+ // We only do this deeper than head and body since they tend to have random
+ // other nodes in them. We also ignore components with pure text content in
+ // side of them.
+ // TODO: Better heuristic.
+ if (fiber.tag !== HostComponent || type !== 'head' && type !== 'body' && !shouldSetTextContent(type, fiber.memoizedProps)) {
+ var nextInstance = nextHydratableInstance;
+ while (nextInstance) {
+ deleteHydratableInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
+ }
+ }
+
+ popToNextHostParent(fiber);
+ nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null;
+ return true;
+}
+
+function resetHydrationState() {
+ if (!supportsHydration) {
+ return;
+ }
+
+ hydrationParentFiber = null;
+ nextHydratableInstance = null;
+ isHydrating = false;
+}
+
+var ReactCurrentOwner$3 = ReactSharedInternals.ReactCurrentOwner;
+
+var didReceiveUpdate = false;
+
+var didWarnAboutBadClass = void 0;
+var didWarnAboutContextTypeOnFunctionComponent = void 0;
+var didWarnAboutGetDerivedStateOnFunctionComponent = void 0;
+var didWarnAboutFunctionRefs = void 0;
+var didWarnAboutReassigningProps = void 0;
+
+{
+ didWarnAboutBadClass = {};
+ didWarnAboutContextTypeOnFunctionComponent = {};
+ didWarnAboutGetDerivedStateOnFunctionComponent = {};
+ didWarnAboutFunctionRefs = {};
+ didWarnAboutReassigningProps = false;
+}
+
+function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
+ if (current$$1 === null) {
+ // If this is a fresh new component that hasn't been rendered yet, we
+ // won't update its child set by applying minimal side-effects. Instead,
+ // we will add them all to the child before it gets rendered. That means
+ // we can optimize this reconciliation pass by not tracking side-effects.
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ // If the current child is the same as the work in progress, it means that
+ // we haven't yet started any work on these children. Therefore, we use
+ // the clone algorithm to create a copy of all the current children.
+
+ // If we had any progressed work already, that is invalid at this point so
+ // let's throw it out.
+ workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
+ }
+}
+
+function forceUnmountCurrentAndReconcile(current$$1, workInProgress, nextChildren, renderExpirationTime) {
+ // This function is fork of reconcileChildren. It's used in cases where we
+ // want to reconcile without matching against the existing set. This has the
+ // effect of all current children being unmounted; even if the type and key
+ // are the same, the old child is unmounted and a new child is created.
+ //
+ // To do this, we're going to go through the reconcile algorithm twice. In
+ // the first pass, we schedule a deletion for all the current children by
+ // passing null.
+ workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, null, renderExpirationTime);
+ // In the second pass, we mount the new children. The trick here is that we
+ // pass null in place of where we usually pass the current child set. This has
+ // the effect of remounting all children regardless of whether their their
+ // identity matches.
+ workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+}
+
+function updateForwardRef(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens after the first render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
+
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var innerPropTypes = Component.propTypes;
+ if (innerPropTypes) {
+ checkPropTypes_1(innerPropTypes, nextProps, // Resolved props
+ 'prop', getComponentName(Component), getCurrentFiberStackInDev);
+ }
+ }
+ }
+
+ var render = Component.render;
+ var ref = workInProgress.ref;
+
+ // The rest is a fork of updateFunctionComponent
+ var nextChildren = void 0;
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ {
+ ReactCurrentOwner$3.current = workInProgress;
+ setCurrentPhase('render');
+ nextChildren = renderWithHooks(current$$1, workInProgress, render, nextProps, ref, renderExpirationTime);
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ // Only double-render components with Hooks
+ if (workInProgress.memoizedState !== null) {
+ nextChildren = renderWithHooks(current$$1, workInProgress, render, nextProps, ref, renderExpirationTime);
+ }
+ }
+ setCurrentPhase(null);
+ }
+
+ if (current$$1 !== null && !didReceiveUpdate) {
+ bailoutHooks(current$$1, workInProgress, renderExpirationTime);
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
+ if (current$$1 === null) {
+ var type = Component.type;
+ if (isSimpleFunctionComponent(type) && Component.compare === null &&
+ // SimpleMemoComponent codepath doesn't resolve outer props either.
+ Component.defaultProps === undefined) {
+ // If this is a plain function component without default props,
+ // and with only the default shallow comparison, we upgrade it
+ // to a SimpleMemoComponent to allow fast path updates.
+ workInProgress.tag = SimpleMemoComponent;
+ workInProgress.type = type;
+ {
+ validateFunctionComponentInDev(workInProgress, type);
+ }
+ return updateSimpleMemoComponent(current$$1, workInProgress, type, nextProps, updateExpirationTime, renderExpirationTime);
+ }
+ {
+ var innerPropTypes = type.propTypes;
+ if (innerPropTypes) {
+ // Inner memo component props aren't currently validated in createElement.
+ // We could move it there, but we'd still need this for lazy code path.
+ checkPropTypes_1(innerPropTypes, nextProps, // Resolved props
+ 'prop', getComponentName(type), getCurrentFiberStackInDev);
+ }
+ }
+ var child = createFiberFromTypeAndProps(Component.type, null, nextProps, null, workInProgress.mode, renderExpirationTime);
+ child.ref = workInProgress.ref;
+ child.return = workInProgress;
+ workInProgress.child = child;
+ return child;
+ }
+ {
+ var _type = Component.type;
+ var _innerPropTypes = _type.propTypes;
+ if (_innerPropTypes) {
+ // Inner memo component props aren't currently validated in createElement.
+ // We could move it there, but we'd still need this for lazy code path.
+ checkPropTypes_1(_innerPropTypes, nextProps, // Resolved props
+ 'prop', getComponentName(_type), getCurrentFiberStackInDev);
+ }
+ }
+ var currentChild = current$$1.child; // This is always exactly one child
+ if (updateExpirationTime < renderExpirationTime) {
+ // This will be the props with resolved defaultProps,
+ // unlike current.memoizedProps which will be the unresolved ones.
+ var prevProps = currentChild.memoizedProps;
+ // Default to shallow comparison
+ var compare = Component.compare;
+ compare = compare !== null ? compare : shallowEqual;
+ if (compare(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ var newChild = createWorkInProgress(currentChild, nextProps, renderExpirationTime);
+ newChild.ref = workInProgress.ref;
+ newChild.return = workInProgress;
+ workInProgress.child = newChild;
+ return newChild;
+}
+
+function updateSimpleMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens when the inner render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
+
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var outerMemoType = workInProgress.elementType;
+ if (outerMemoType.$$typeof === REACT_LAZY_TYPE) {
+ // We warn when you define propTypes on lazy()
+ // so let's just skip over it to find memo() outer wrapper.
+ // Inner props for memo are validated later.
+ outerMemoType = refineResolvedLazyComponent(outerMemoType);
+ }
+ var outerPropTypes = outerMemoType && outerMemoType.propTypes;
+ if (outerPropTypes) {
+ checkPropTypes_1(outerPropTypes, nextProps, // Resolved (SimpleMemoComponent has no defaultProps)
+ 'prop', getComponentName(outerMemoType), getCurrentFiberStackInDev);
+ }
+ // Inner propTypes will be validated in the function component path.
+ }
+ }
+ if (current$$1 !== null) {
+ var prevProps = current$$1.memoizedProps;
+ if (shallowEqual(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
+ didReceiveUpdate = false;
+ if (updateExpirationTime < renderExpirationTime) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ }
+ }
+ return updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
+}
+
+function updateFragment(current$$1, workInProgress, renderExpirationTime) {
+ var nextChildren = workInProgress.pendingProps;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateMode(current$$1, workInProgress, renderExpirationTime) {
+ var nextChildren = workInProgress.pendingProps.children;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateProfiler(current$$1, workInProgress, renderExpirationTime) {
+ if (enableProfilerTimer) {
+ workInProgress.effectTag |= Update;
+ }
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function markRef(current$$1, workInProgress) {
+ var ref = workInProgress.ref;
+ if (current$$1 === null && ref !== null || current$$1 !== null && current$$1.ref !== ref) {
+ // Schedule a Ref effect
+ workInProgress.effectTag |= Ref;
+ }
+}
+
+function updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var innerPropTypes = Component.propTypes;
+ if (innerPropTypes) {
+ checkPropTypes_1(innerPropTypes, nextProps, // Resolved props
+ 'prop', getComponentName(Component), getCurrentFiberStackInDev);
+ }
+ }
+ }
+
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ var nextChildren = void 0;
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ {
+ ReactCurrentOwner$3.current = workInProgress;
+ setCurrentPhase('render');
+ nextChildren = renderWithHooks(current$$1, workInProgress, Component, nextProps, context, renderExpirationTime);
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ // Only double-render components with Hooks
+ if (workInProgress.memoizedState !== null) {
+ nextChildren = renderWithHooks(current$$1, workInProgress, Component, nextProps, context, renderExpirationTime);
+ }
+ }
+ setCurrentPhase(null);
+ }
+
+ if (current$$1 !== null && !didReceiveUpdate) {
+ bailoutHooks(current$$1, workInProgress, renderExpirationTime);
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var innerPropTypes = Component.propTypes;
+ if (innerPropTypes) {
+ checkPropTypes_1(innerPropTypes, nextProps, // Resolved props
+ 'prop', getComponentName(Component), getCurrentFiberStackInDev);
+ }
+ }
+ }
+
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = void 0;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ var instance = workInProgress.stateNode;
+ var shouldUpdate = void 0;
+ if (instance === null) {
+ if (current$$1 !== null) {
+ // An class component without an instance only mounts if it suspended
+ // inside a non- concurrent tree, in an inconsistent state. We want to
+ // tree it like a new mount, even though an empty version of it already
+ // committed. Disconnect the alternate pointers.
+ current$$1.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+ // In the initial pass we might need to construct the instance.
+ constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ shouldUpdate = true;
+ } else if (current$$1 === null) {
+ // In a resume, we'll already have an instance we can reuse.
+ shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ } else {
+ shouldUpdate = updateClassInstance(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
+ }
+ var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);
+ {
+ var inst = workInProgress.stateNode;
+ if (inst.props !== nextProps) {
+ !didWarnAboutReassigningProps ? warning$1(false, 'It looks like %s is reassigning its own `this.props` while rendering. ' + 'This is not supported and can lead to confusing bugs.', getComponentName(workInProgress.type) || 'a component') : void 0;
+ didWarnAboutReassigningProps = true;
+ }
+ }
+ return nextUnitOfWork;
+}
+
+function finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
+ // Refs should update even if shouldComponentUpdate returns false
+ markRef(current$$1, workInProgress);
+
+ var didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;
+
+ if (!shouldUpdate && !didCaptureError) {
+ // Context providers should defer to sCU for rendering
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, false);
+ }
+
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ var instance = workInProgress.stateNode;
+
+ // Rerender
+ ReactCurrentOwner$3.current = workInProgress;
+ var nextChildren = void 0;
+ if (didCaptureError && typeof Component.getDerivedStateFromError !== 'function') {
+ // If we captured an error, but getDerivedStateFrom catch is not defined,
+ // unmount all the children. componentDidCatch will schedule an update to
+ // re-render a fallback. This is temporary until we migrate everyone to
+ // the new API.
+ // TODO: Warn in a future release.
+ nextChildren = null;
+
+ if (enableProfilerTimer) {
+ stopProfilerTimerIfRunning(workInProgress);
+ }
+ } else {
+ {
+ setCurrentPhase('render');
+ nextChildren = instance.render();
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ instance.render();
+ }
+ setCurrentPhase(null);
+ }
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ if (current$$1 !== null && didCaptureError) {
+ // If we're recovering from an error, reconcile without reusing any of
+ // the existing children. Conceptually, the normal children and the children
+ // that are shown on error are two different sets, so we shouldn't reuse
+ // normal children even if their identities match.
+ forceUnmountCurrentAndReconcile(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ } else {
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ }
+
+ // Memoize state using the values we just used to render.
+ // TODO: Restructure so we never read values from the instance.
+ workInProgress.memoizedState = instance.state;
+
+ // The context might have changed so we need to recalculate it.
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, true);
+ }
+
+ return workInProgress.child;
+}
+
+function pushHostRootContext(workInProgress) {
+ var root = workInProgress.stateNode;
+ if (root.pendingContext) {
+ pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context);
+ } else if (root.context) {
+ // Should always be set
+ pushTopLevelContextObject(workInProgress, root.context, false);
+ }
+ pushHostContainer(workInProgress, root.containerInfo);
+}
+
+function updateHostRoot(current$$1, workInProgress, renderExpirationTime) {
+ pushHostRootContext(workInProgress);
+ var updateQueue = workInProgress.updateQueue;
+ !(updateQueue !== null) ? invariant(false, 'If the root does not have an updateQueue, we should have already bailed out. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ var nextProps = workInProgress.pendingProps;
+ var prevState = workInProgress.memoizedState;
+ var prevChildren = prevState !== null ? prevState.element : null;
+ processUpdateQueue(workInProgress, updateQueue, nextProps, null, renderExpirationTime);
+ var nextState = workInProgress.memoizedState;
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ var nextChildren = nextState.element;
+ if (nextChildren === prevChildren) {
+ // If the state is the same as before, that's a bailout because we had
+ // no work that expires at this time.
+ resetHydrationState();
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ var root = workInProgress.stateNode;
+ if ((current$$1 === null || current$$1.child === null) && root.hydrate && enterHydrationState(workInProgress)) {
+ // If we don't have any current children this might be the first pass.
+ // We always try to hydrate. If this isn't a hydration pass there won't
+ // be any children to hydrate which is effectively the same thing as
+ // not hydrating.
+
+ // This is a bit of a hack. We track the host root as a placement to
+ // know that we're currently in a mounting state. That way isMounted
+ // works as expected. We must reset this before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag |= Placement;
+
+ // Ensure that children mount into this root without tracking
+ // side-effects. This ensures that we don't store Placement effects on
+ // nodes that will be hydrated.
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ // Otherwise reset hydration state in case we aborted and resumed another
+ // root.
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ resetHydrationState();
+ }
+ return workInProgress.child;
+}
+
+function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
+ pushHostContext(workInProgress);
+
+ if (current$$1 === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+
+ var type = workInProgress.type;
+ var nextProps = workInProgress.pendingProps;
+ var prevProps = current$$1 !== null ? current$$1.memoizedProps : null;
+
+ var nextChildren = nextProps.children;
+ var isDirectTextChild = shouldSetTextContent(type, nextProps);
+
+ if (isDirectTextChild) {
+ // We special case a direct text child of a host node. This is a common
+ // case. We won't handle it as a reified child. We will instead handle
+ // this in the host environment that also have access to this prop. That
+ // avoids allocating another HostText fiber and traversing it.
+ nextChildren = null;
+ } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
+ // If we're switching from a direct text child to a normal child, or to
+ // empty, we need to schedule the text content to be reset.
+ workInProgress.effectTag |= ContentReset;
+ }
+
+ markRef(current$$1, workInProgress);
+
+ // Check the host config to see if the children are offscreen/hidden.
+ if (renderExpirationTime !== Never && workInProgress.mode & ConcurrentMode && shouldDeprioritizeSubtree(type, nextProps)) {
+ // Schedule this fiber to re-render at offscreen priority. Then bailout.
+ workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
+ return null;
+ }
+
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateHostText(current$$1, workInProgress) {
+ if (current$$1 === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+ // Nothing to do here. This is terminal. We'll do the completion step
+ // immediately after.
+ return null;
+}
+
+function mountLazyComponent(_current, workInProgress, elementType, updateExpirationTime, renderExpirationTime) {
+ if (_current !== null) {
+ // An lazy component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ var props = workInProgress.pendingProps;
+ // We can't start a User Timing measurement with correct label yet.
+ // Cancel and resume right after we know the tag.
+ cancelWorkTimer(workInProgress);
+ var Component = readLazyComponentType(elementType);
+ // Store the unwrapped component in the type.
+ workInProgress.type = Component;
+ var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
+ startWorkTimer(workInProgress);
+ var resolvedProps = resolveDefaultProps(Component, props);
+ var child = void 0;
+ switch (resolvedTag) {
+ case FunctionComponent:
+ {
+ {
+ validateFunctionComponentInDev(workInProgress, Component);
+ }
+ child = updateFunctionComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case ClassComponent:
+ {
+ child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case ForwardRef:
+ {
+ child = updateForwardRef(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case MemoComponent:
+ {
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ var outerPropTypes = Component.propTypes;
+ if (outerPropTypes) {
+ checkPropTypes_1(outerPropTypes, resolvedProps, // Resolved for outer only
+ 'prop', getComponentName(Component), getCurrentFiberStackInDev);
+ }
+ }
+ }
+ child = updateMemoComponent(null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
+ updateExpirationTime, renderExpirationTime);
+ break;
+ }
+ default:
+ {
+ var hint = '';
+ {
+ if (Component !== null && typeof Component === 'object' && Component.$$typeof === REACT_LAZY_TYPE) {
+ hint = ' Did you wrap a component in React.lazy() more than once?';
+ }
+ }
+ // This message intentionally doesn't mention ForwardRef or MemoComponent
+ // because the fact that it's a separate type of work is an
+ // implementation detail.
+ invariant(false, 'Element type is invalid. Received a promise that resolves to: %s. Lazy element type must resolve to a class or function.%s', Component, hint);
+ }
+ }
+ return child;
+}
+
+function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderExpirationTime) {
+ if (_current !== null) {
+ // An incomplete component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ // Promote the fiber to a class and try rendering again.
+ workInProgress.tag = ClassComponent;
+
+ // The rest of this function is a fork of `updateClassComponent`
+
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = void 0;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+
+ return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
+}
+
+function mountIndeterminateComponent(_current, workInProgress, Component, renderExpirationTime) {
+ if (_current !== null) {
+ // An indeterminate component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ var props = workInProgress.pendingProps;
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ var value = void 0;
+
+ {
+ if (Component.prototype && typeof Component.prototype.render === 'function') {
+ var componentName = getComponentName(Component) || 'Unknown';
+
+ if (!didWarnAboutBadClass[componentName]) {
+ warningWithoutStack$1(false, "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + 'This is likely to cause errors. Change %s to extend React.Component instead.', componentName, componentName);
+ didWarnAboutBadClass[componentName] = true;
+ }
+ }
+
+ if (workInProgress.mode & StrictMode) {
+ ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
+ }
+
+ ReactCurrentOwner$3.current = workInProgress;
+ value = renderWithHooks(null, workInProgress, Component, props, context, renderExpirationTime);
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+
+ if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
+ // Proceed under the assumption that this is a class instance
+ workInProgress.tag = ClassComponent;
+
+ // Throw out any hooks that were used.
+ resetHooks();
+
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = false;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+
+ workInProgress.memoizedState = value.state !== null && value.state !== undefined ? value.state : null;
+
+ var getDerivedStateFromProps = Component.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, Component, getDerivedStateFromProps, props);
+ }
+
+ adoptClassInstance(workInProgress, value);
+ mountClassInstance(workInProgress, Component, props, renderExpirationTime);
+ return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
+ } else {
+ // Proceed under the assumption that this is a function component
+ workInProgress.tag = FunctionComponent;
+ {
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ // Only double-render components with Hooks
+ if (workInProgress.memoizedState !== null) {
+ value = renderWithHooks(null, workInProgress, Component, props, context, renderExpirationTime);
+ }
+ }
+ }
+ reconcileChildren(null, workInProgress, value, renderExpirationTime);
+ {
+ validateFunctionComponentInDev(workInProgress, Component);
+ }
+ return workInProgress.child;
+ }
+}
+
+function validateFunctionComponentInDev(workInProgress, Component) {
+ if (Component) {
+ !!Component.childContextTypes ? warningWithoutStack$1(false, '%s(...): childContextTypes cannot be defined on a function component.', Component.displayName || Component.name || 'Component') : void 0;
+ }
+ if (workInProgress.ref !== null) {
+ var info = '';
+ var ownerName = getCurrentFiberOwnerNameInDevOrNull();
+ if (ownerName) {
+ info += '\n\nCheck the render method of `' + ownerName + '`.';
+ }
+
+ var warningKey = ownerName || workInProgress._debugID || '';
+ var debugSource = workInProgress._debugSource;
+ if (debugSource) {
+ warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
+ }
+ if (!didWarnAboutFunctionRefs[warningKey]) {
+ didWarnAboutFunctionRefs[warningKey] = true;
+ warning$1(false, 'Function components cannot be given refs. ' + 'Attempts to access this ref will fail. ' + 'Did you mean to use React.forwardRef()?%s', info);
+ }
+ }
+
+ if (typeof Component.getDerivedStateFromProps === 'function') {
+ var componentName = getComponentName(Component) || 'Unknown';
+
+ if (!didWarnAboutGetDerivedStateOnFunctionComponent[componentName]) {
+ warningWithoutStack$1(false, '%s: Function components do not support getDerivedStateFromProps.', componentName);
+ didWarnAboutGetDerivedStateOnFunctionComponent[componentName] = true;
+ }
+ }
+
+ if (typeof Component.contextType === 'object' && Component.contextType !== null) {
+ var _componentName = getComponentName(Component) || 'Unknown';
+
+ if (!didWarnAboutContextTypeOnFunctionComponent[_componentName]) {
+ warningWithoutStack$1(false, '%s: Function components do not support contextType.', _componentName);
+ didWarnAboutContextTypeOnFunctionComponent[_componentName] = true;
+ }
+ }
+}
+
+function updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
+ var mode = workInProgress.mode;
+ var nextProps = workInProgress.pendingProps;
+
+ // We should attempt to render the primary children unless this boundary
+ // already suspended during this render (`alreadyCaptured` is true).
+ var nextState = workInProgress.memoizedState;
+
+ var nextDidTimeout = void 0;
+ if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This is the first attempt.
+ nextState = null;
+ nextDidTimeout = false;
+ } else {
+ // Something in this boundary's subtree already suspended. Switch to
+ // rendering the fallback children.
+ nextState = {
+ timedOutAt: nextState !== null ? nextState.timedOutAt : NoWork
+ };
+ nextDidTimeout = true;
+ workInProgress.effectTag &= ~DidCapture;
+ }
+
+ // This next part is a bit confusing. If the children timeout, we switch to
+ // showing the fallback children in place of the "primary" children.
+ // However, we don't want to delete the primary children because then their
+ // state will be lost (both the React state and the host state, e.g.
+ // uncontrolled form inputs). Instead we keep them mounted and hide them.
+ // Both the fallback children AND the primary children are rendered at the
+ // same time. Once the primary children are un-suspended, we can delete
+ // the fallback children — don't need to preserve their state.
+ //
+ // The two sets of children are siblings in the host environment, but
+ // semantically, for purposes of reconciliation, they are two separate sets.
+ // So we store them using two fragment fibers.
+ //
+ // However, we want to avoid allocating extra fibers for every placeholder.
+ // They're only necessary when the children time out, because that's the
+ // only time when both sets are mounted.
+ //
+ // So, the extra fragment fibers are only used if the children time out.
+ // Otherwise, we render the primary children directly. This requires some
+ // custom reconciliation logic to preserve the state of the primary
+ // children. It's essentially a very basic form of re-parenting.
+
+ // `child` points to the child fiber. In the normal case, this is the first
+ // fiber of the primary children set. In the timed-out case, it's a
+ // a fragment fiber containing the primary children.
+ var child = void 0;
+ // `next` points to the next fiber React should render. In the normal case,
+ // it's the same as `child`: the first fiber of the primary children set.
+ // In the timed-out case, it's a fragment fiber containing the *fallback*
+ // children -- we skip over the primary children entirely.
+ var next = void 0;
+ if (current$$1 === null) {
+ if (enableSuspenseServerRenderer) {
+ // If we're currently hydrating, try to hydrate this boundary.
+ // But only if this has a fallback.
+ if (nextProps.fallback !== undefined) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ // This could've changed the tag if this was a dehydrated suspense component.
+ if (workInProgress.tag === DehydratedSuspenseComponent) {
+ return updateDehydratedSuspenseComponent(null, workInProgress, renderExpirationTime);
+ }
+ }
+ }
+
+ // This is the initial mount. This branch is pretty simple because there's
+ // no previous state that needs to be preserved.
+ if (nextDidTimeout) {
+ // Mount separate fragments for primary and fallback children.
+ var nextFallbackChildren = nextProps.fallback;
+ var primaryChildFragment = createFiberFromFragment(null, mode, NoWork, null);
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var progressedState = workInProgress.memoizedState;
+ var progressedPrimaryChild = progressedState !== null ? workInProgress.child.child : workInProgress.child;
+ primaryChildFragment.child = progressedPrimaryChild;
+ }
+
+ var fallbackChildFragment = createFiberFromFragment(nextFallbackChildren, mode, renderExpirationTime, null);
+ primaryChildFragment.sibling = fallbackChildFragment;
+ child = primaryChildFragment;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = fallbackChildFragment;
+ child.return = next.return = workInProgress;
+ } else {
+ // Mount the primary children without an intermediate fragment fiber.
+ var nextPrimaryChildren = nextProps.children;
+ child = next = mountChildFibers(workInProgress, null, nextPrimaryChildren, renderExpirationTime);
+ }
+ } else {
+ // This is an update. This branch is more complicated because we need to
+ // ensure the state of the primary children is preserved.
+ var prevState = current$$1.memoizedState;
+ var prevDidTimeout = prevState !== null;
+ if (prevDidTimeout) {
+ // The current tree already timed out. That means each child set is
+ var currentPrimaryChildFragment = current$$1.child;
+ var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
+ if (nextDidTimeout) {
+ // Still timed out. Reuse the current primary children by cloning
+ // its fragment. We're going to skip over these entirely.
+ var _nextFallbackChildren = nextProps.fallback;
+ var _primaryChildFragment = createWorkInProgress(currentPrimaryChildFragment, currentPrimaryChildFragment.pendingProps, NoWork);
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var _progressedState = workInProgress.memoizedState;
+ var _progressedPrimaryChild = _progressedState !== null ? workInProgress.child.child : workInProgress.child;
+ if (_progressedPrimaryChild !== currentPrimaryChildFragment.child) {
+ _primaryChildFragment.child = _progressedPrimaryChild;
+ }
+ }
+
+ // Because primaryChildFragment is a new fiber that we're inserting as the
+ // parent of a new tree, we need to set its treeBaseDuration.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // treeBaseDuration is the sum of all the child tree base durations.
+ var treeBaseDuration = 0;
+ var hiddenChild = _primaryChildFragment.child;
+ while (hiddenChild !== null) {
+ treeBaseDuration += hiddenChild.treeBaseDuration;
+ hiddenChild = hiddenChild.sibling;
+ }
+ _primaryChildFragment.treeBaseDuration = treeBaseDuration;
+ }
+
+ // Clone the fallback child fragment, too. These we'll continue
+ // working on.
+ var _fallbackChildFragment = _primaryChildFragment.sibling = createWorkInProgress(currentFallbackChildFragment, _nextFallbackChildren, currentFallbackChildFragment.expirationTime);
+ child = _primaryChildFragment;
+ _primaryChildFragment.childExpirationTime = NoWork;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = _fallbackChildFragment;
+ child.return = next.return = workInProgress;
+ } else {
+ // No longer suspended. Switch back to showing the primary children,
+ // and remove the intermediate fragment fiber.
+ var _nextPrimaryChildren = nextProps.children;
+ var currentPrimaryChild = currentPrimaryChildFragment.child;
+ var primaryChild = reconcileChildFibers(workInProgress, currentPrimaryChild, _nextPrimaryChildren, renderExpirationTime);
+
+ // If this render doesn't suspend, we need to delete the fallback
+ // children. Wait until the complete phase, after we've confirmed the
+ // fallback is no longer needed.
+ // TODO: Would it be better to store the fallback fragment on
+ // the stateNode?
+
+ // Continue rendering the children, like we normally do.
+ child = next = primaryChild;
+ }
+ } else {
+ // The current tree has not already timed out. That means the primary
+ // children are not wrapped in a fragment fiber.
+ var _currentPrimaryChild = current$$1.child;
+ if (nextDidTimeout) {
+ // Timed out. Wrap the children in a fragment fiber to keep them
+ // separate from the fallback children.
+ var _nextFallbackChildren2 = nextProps.fallback;
+ var _primaryChildFragment2 = createFiberFromFragment(
+ // It shouldn't matter what the pending props are because we aren't
+ // going to render this fragment.
+ null, mode, NoWork, null);
+ _primaryChildFragment2.child = _currentPrimaryChild;
+
+ // Even though we're creating a new fiber, there are no new children,
+ // because we're reusing an already mounted tree. So we don't need to
+ // schedule a placement.
+ // primaryChildFragment.effectTag |= Placement;
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var _progressedState2 = workInProgress.memoizedState;
+ var _progressedPrimaryChild2 = _progressedState2 !== null ? workInProgress.child.child : workInProgress.child;
+ _primaryChildFragment2.child = _progressedPrimaryChild2;
+ }
+
+ // Because primaryChildFragment is a new fiber that we're inserting as the
+ // parent of a new tree, we need to set its treeBaseDuration.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // treeBaseDuration is the sum of all the child tree base durations.
+ var _treeBaseDuration = 0;
+ var _hiddenChild = _primaryChildFragment2.child;
+ while (_hiddenChild !== null) {
+ _treeBaseDuration += _hiddenChild.treeBaseDuration;
+ _hiddenChild = _hiddenChild.sibling;
+ }
+ _primaryChildFragment2.treeBaseDuration = _treeBaseDuration;
+ }
+
+ // Create a fragment from the fallback children, too.
+ var _fallbackChildFragment2 = _primaryChildFragment2.sibling = createFiberFromFragment(_nextFallbackChildren2, mode, renderExpirationTime, null);
+ _fallbackChildFragment2.effectTag |= Placement;
+ child = _primaryChildFragment2;
+ _primaryChildFragment2.childExpirationTime = NoWork;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = _fallbackChildFragment2;
+ child.return = next.return = workInProgress;
+ } else {
+ // Still haven't timed out. Continue rendering the children, like we
+ // normally do.
+ var _nextPrimaryChildren2 = nextProps.children;
+ next = child = reconcileChildFibers(workInProgress, _currentPrimaryChild, _nextPrimaryChildren2, renderExpirationTime);
+ }
+ }
+ workInProgress.stateNode = current$$1.stateNode;
+ }
+
+ workInProgress.memoizedState = nextState;
+ workInProgress.child = child;
+ return next;
+}
+
+function updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
+ if (current$$1 === null) {
+ // During the first pass, we'll bail out and not drill into the children.
+ // Instead, we'll leave the content in place and try to hydrate it later.
+ workInProgress.expirationTime = Never;
+ return null;
+ }
+ // We use childExpirationTime to indicate that a child might depend on context, so if
+ // any context has changed, we need to treat is as if the input might have changed.
+ var hasContextChanged$$1 = current$$1.childExpirationTime >= renderExpirationTime;
+ if (didReceiveUpdate || hasContextChanged$$1) {
+ // This boundary has changed since the first render. This means that we are now unable to
+ // hydrate it. We might still be able to hydrate it using an earlier expiration time but
+ // during this render we can't. Instead, we're going to delete the whole subtree and
+ // instead inject a new real Suspense boundary to take its place, which may render content
+ // or fallback. The real Suspense boundary will suspend for a while so we have some time
+ // to ensure it can produce real content, but all state and pending events will be lost.
+
+ // Detach from the current dehydrated boundary.
+ current$$1.alternate = null;
+ workInProgress.alternate = null;
+
+ // Insert a deletion in the effect list.
+ var returnFiber = workInProgress.return;
+ !(returnFiber !== null) ? invariant(false, 'Suspense boundaries are never on the root. This is probably a bug in React.') : void 0;
+ var last = returnFiber.lastEffect;
+ if (last !== null) {
+ last.nextEffect = current$$1;
+ returnFiber.lastEffect = current$$1;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = current$$1;
+ }
+ current$$1.nextEffect = null;
+ current$$1.effectTag = Deletion;
+
+ // Upgrade this work in progress to a real Suspense component.
+ workInProgress.tag = SuspenseComponent;
+ workInProgress.stateNode = null;
+ workInProgress.memoizedState = null;
+ // This is now an insertion.
+ workInProgress.effectTag |= Placement;
+ // Retry as a real Suspense component.
+ return updateSuspenseComponent(null, workInProgress, renderExpirationTime);
+ }
+ if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This is the first attempt.
+ reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress);
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+ } else {
+ // Something suspended. Leave the existing children in place.
+ // TODO: In non-concurrent mode, should we commit the nodes we have hydrated so far?
+ workInProgress.child = null;
+ return null;
+ }
+}
+
+function updatePortalComponent(current$$1, workInProgress, renderExpirationTime) {
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ var nextChildren = workInProgress.pendingProps;
+ if (current$$1 === null) {
+ // Portals are special because we don't append the children during mount
+ // but at commit. Therefore we need to track insertions which the normal
+ // flow doesn't do during mount. This doesn't happen at the root because
+ // the root always starts with a "current" with a null child.
+ // TODO: Consider unifying this with how the root works.
+ workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ }
+ return workInProgress.child;
+}
+
+function updateContextProvider(current$$1, workInProgress, renderExpirationTime) {
+ var providerType = workInProgress.type;
+ var context = providerType._context;
+
+ var newProps = workInProgress.pendingProps;
+ var oldProps = workInProgress.memoizedProps;
+
+ var newValue = newProps.value;
+
+ {
+ var providerPropTypes = workInProgress.type.propTypes;
+
+ if (providerPropTypes) {
+ checkPropTypes_1(providerPropTypes, newProps, 'prop', 'Context.Provider', getCurrentFiberStackInDev);
+ }
+ }
+
+ pushProvider(workInProgress, newValue);
+
+ if (oldProps !== null) {
+ var oldValue = oldProps.value;
+ var changedBits = calculateChangedBits(context, newValue, oldValue);
+ if (changedBits === 0) {
+ // No change. Bailout early if children are the same.
+ if (oldProps.children === newProps.children && !hasContextChanged()) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ } else {
+ // The context value changed. Search for matching consumers and schedule
+ // them to update.
+ propagateContextChange(workInProgress, context, changedBits, renderExpirationTime);
+ }
+ }
+
+ var newChildren = newProps.children;
+ reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+var hasWarnedAboutUsingContextAsConsumer = false;
+
+function updateContextConsumer(current$$1, workInProgress, renderExpirationTime) {
+ var context = workInProgress.type;
+ // The logic below for Context differs depending on PROD or DEV mode. In
+ // DEV mode, we create a separate object for Context.Consumer that acts
+ // like a proxy to Context. This proxy object adds unnecessary code in PROD
+ // so we use the old behaviour (Context.Consumer references Context) to
+ // reduce size and overhead. The separate object references context via
+ // a property called "_context", which also gives us the ability to check
+ // in DEV mode if this property exists or not and warn if it does not.
+ {
+ if (context._context === undefined) {
+ // This may be because it's a Context (rather than a Consumer).
+ // Or it may be because it's older React where they're the same thing.
+ // We only want to warn if we're sure it's a new React.
+ if (context !== context.Consumer) {
+ if (!hasWarnedAboutUsingContextAsConsumer) {
+ hasWarnedAboutUsingContextAsConsumer = true;
+ warning$1(false, 'Rendering <Context> directly is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?');
+ }
+ }
+ } else {
+ context = context._context;
+ }
+ }
+ var newProps = workInProgress.pendingProps;
+ var render = newProps.children;
+
+ {
+ !(typeof render === 'function') ? warningWithoutStack$1(false, 'A context consumer was rendered with multiple children, or a child ' + "that isn't a function. A context consumer expects a single child " + 'that is a function. If you did pass a function, make sure there ' + 'is no trailing or leading whitespace around it.') : void 0;
+ }
+
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ var newValue = readContext(context, newProps.unstable_observedBits);
+ var newChildren = void 0;
+ {
+ ReactCurrentOwner$3.current = workInProgress;
+ setCurrentPhase('render');
+ newChildren = render(newValue);
+ setCurrentPhase(null);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function markWorkInProgressReceivedUpdate() {
+ didReceiveUpdate = true;
+}
+
+function bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime) {
+ cancelWorkTimer(workInProgress);
+
+ if (current$$1 !== null) {
+ // Reuse previous context list
+ workInProgress.contextDependencies = current$$1.contextDependencies;
+ }
+
+ if (enableProfilerTimer) {
+ // Don't update "base" render times for bailouts.
+ stopProfilerTimerIfRunning(workInProgress);
+ }
+
+ // Check if the children have any pending work.
+ var childExpirationTime = workInProgress.childExpirationTime;
+ if (childExpirationTime < renderExpirationTime) {
+ // The children don't have any work either. We can skip them.
+ // TODO: Once we add back resuming, we should check if the children are
+ // a work-in-progress set. If so, we need to transfer their effects.
+ return null;
+ } else {
+ // This fiber doesn't have work, but its subtree does. Clone the child
+ // fibers and continue.
+ cloneChildFibers(current$$1, workInProgress);
+ return workInProgress.child;
+ }
+}
+
+function beginWork(current$$1, workInProgress, renderExpirationTime) {
+ var updateExpirationTime = workInProgress.expirationTime;
+
+ if (current$$1 !== null) {
+ var oldProps = current$$1.memoizedProps;
+ var newProps = workInProgress.pendingProps;
+
+ if (oldProps !== newProps || hasContextChanged()) {
+ // If props or context changed, mark the fiber as having performed work.
+ // This may be unset if the props are determined to be equal later (memo).
+ didReceiveUpdate = true;
+ } else if (updateExpirationTime < renderExpirationTime) {
+ didReceiveUpdate = false;
+ // This fiber does not have any pending work. Bailout without entering
+ // the begin phase. There's still some bookkeeping we that needs to be done
+ // in this optimized path, mostly pushing stuff onto the stack.
+ switch (workInProgress.tag) {
+ case HostRoot:
+ pushHostRootContext(workInProgress);
+ resetHydrationState();
+ break;
+ case HostComponent:
+ pushHostContext(workInProgress);
+ break;
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ pushContextProvider(workInProgress);
+ }
+ break;
+ }
+ case HostPortal:
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ break;
+ case ContextProvider:
+ {
+ var newValue = workInProgress.memoizedProps.value;
+ pushProvider(workInProgress, newValue);
+ break;
+ }
+ case Profiler:
+ if (enableProfilerTimer) {
+ workInProgress.effectTag |= Update;
+ }
+ break;
+ case SuspenseComponent:
+ {
+ var state = workInProgress.memoizedState;
+ var didTimeout = state !== null;
+ if (didTimeout) {
+ // If this boundary is currently timed out, we need to decide
+ // whether to retry the primary children, or to skip over it and
+ // go straight to the fallback. Check the priority of the primary
+ var primaryChildFragment = workInProgress.child;
+ var primaryChildExpirationTime = primaryChildFragment.childExpirationTime;
+ if (primaryChildExpirationTime !== NoWork && primaryChildExpirationTime >= renderExpirationTime) {
+ // The primary children have pending work. Use the normal path
+ // to attempt to render the primary children again.
+ return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ } else {
+ // The primary children do not have pending work with sufficient
+ // priority. Bailout.
+ var child = bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ if (child !== null) {
+ // The fallback children have pending work. Skip over the
+ // primary children and work on the fallback.
+ return child.sibling;
+ } else {
+ return null;
+ }
+ }
+ }
+ break;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ // We know that this component will suspend again because if it has
+ // been unsuspended it has committed as a regular Suspense component.
+ // If it needs to be retried, it should have work scheduled on it.
+ workInProgress.effectTag |= DidCapture;
+ break;
+ }
+ }
+ }
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ } else {
+ didReceiveUpdate = false;
+ }
+
+ // Before entering the begin phase, clear the expiration time.
+ workInProgress.expirationTime = NoWork;
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ {
+ var elementType = workInProgress.elementType;
+ return mountIndeterminateComponent(current$$1, workInProgress, elementType, renderExpirationTime);
+ }
+ case LazyComponent:
+ {
+ var _elementType = workInProgress.elementType;
+ return mountLazyComponent(current$$1, workInProgress, _elementType, updateExpirationTime, renderExpirationTime);
+ }
+ case FunctionComponent:
+ {
+ var _Component = workInProgress.type;
+ var unresolvedProps = workInProgress.pendingProps;
+ var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps);
+ return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
+ }
+ case ClassComponent:
+ {
+ var _Component2 = workInProgress.type;
+ var _unresolvedProps = workInProgress.pendingProps;
+ var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
+ return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
+ }
+ case HostRoot:
+ return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
+ case HostComponent:
+ return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
+ case HostText:
+ return updateHostText(current$$1, workInProgress);
+ case SuspenseComponent:
+ return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ case HostPortal:
+ return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
+ case ForwardRef:
+ {
+ var type = workInProgress.type;
+ var _unresolvedProps2 = workInProgress.pendingProps;
+ var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
+ return updateForwardRef(current$$1, workInProgress, type, _resolvedProps2, renderExpirationTime);
+ }
+ case Fragment:
+ return updateFragment(current$$1, workInProgress, renderExpirationTime);
+ case Mode:
+ return updateMode(current$$1, workInProgress, renderExpirationTime);
+ case Profiler:
+ return updateProfiler(current$$1, workInProgress, renderExpirationTime);
+ case ContextProvider:
+ return updateContextProvider(current$$1, workInProgress, renderExpirationTime);
+ case ContextConsumer:
+ return updateContextConsumer(current$$1, workInProgress, renderExpirationTime);
+ case MemoComponent:
+ {
+ var _type2 = workInProgress.type;
+ var _unresolvedProps3 = workInProgress.pendingProps;
+ // Resolve outer props first, then resolve inner props.
+ var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ var outerPropTypes = _type2.propTypes;
+ if (outerPropTypes) {
+ checkPropTypes_1(outerPropTypes, _resolvedProps3, // Resolved for outer only
+ 'prop', getComponentName(_type2), getCurrentFiberStackInDev);
+ }
+ }
+ }
+ _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
+ return updateMemoComponent(current$$1, workInProgress, _type2, _resolvedProps3, updateExpirationTime, renderExpirationTime);
+ }
+ case SimpleMemoComponent:
+ {
+ return updateSimpleMemoComponent(current$$1, workInProgress, workInProgress.type, workInProgress.pendingProps, updateExpirationTime, renderExpirationTime);
+ }
+ case IncompleteClassComponent:
+ {
+ var _Component3 = workInProgress.type;
+ var _unresolvedProps4 = workInProgress.pendingProps;
+ var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4);
+ return mountIncompleteClassComponent(current$$1, workInProgress, _Component3, _resolvedProps4, renderExpirationTime);
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ return updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ }
+ break;
+ }
+ }
+ invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.');
+}
+
+var valueCursor = createCursor(null);
+
+var rendererSigil = void 0;
+{
+ // Use this to detect multiple renderers using the same context
+ rendererSigil = {};
+}
+
+var currentlyRenderingFiber = null;
+var lastContextDependency = null;
+var lastContextWithAllBitsObserved = null;
+
+var isDisallowedContextReadInDEV = false;
+
+function resetContextDependences() {
+ // This is called right before React yields execution, to ensure `readContext`
+ // cannot be called outside the render phase.
+ currentlyRenderingFiber = null;
+ lastContextDependency = null;
+ lastContextWithAllBitsObserved = null;
+ {
+ isDisallowedContextReadInDEV = false;
+ }
+}
+
+function enterDisallowedContextReadInDEV() {
+ {
+ isDisallowedContextReadInDEV = true;
+ }
+}
+
+function exitDisallowedContextReadInDEV() {
+ {
+ isDisallowedContextReadInDEV = false;
+ }
+}
+
+function pushProvider(providerFiber, nextValue) {
+ var context = providerFiber.type._context;
+
+ if (isPrimaryRenderer) {
+ push(valueCursor, context._currentValue, providerFiber);
+
+ context._currentValue = nextValue;
+ {
+ !(context._currentRenderer === undefined || context._currentRenderer === null || context._currentRenderer === rendererSigil) ? warningWithoutStack$1(false, 'Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.') : void 0;
+ context._currentRenderer = rendererSigil;
+ }
+ } else {
+ push(valueCursor, context._currentValue2, providerFiber);
+
+ context._currentValue2 = nextValue;
+ {
+ !(context._currentRenderer2 === undefined || context._currentRenderer2 === null || context._currentRenderer2 === rendererSigil) ? warningWithoutStack$1(false, 'Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.') : void 0;
+ context._currentRenderer2 = rendererSigil;
+ }
+ }
+}
+
+function popProvider(providerFiber) {
+ var currentValue = valueCursor.current;
+
+ pop(valueCursor, providerFiber);
+
+ var context = providerFiber.type._context;
+ if (isPrimaryRenderer) {
+ context._currentValue = currentValue;
+ } else {
+ context._currentValue2 = currentValue;
+ }
+}
+
+function calculateChangedBits(context, newValue, oldValue) {
+ if (is(oldValue, newValue)) {
+ // No change
+ return 0;
+ } else {
+ var changedBits = typeof context._calculateChangedBits === 'function' ? context._calculateChangedBits(oldValue, newValue) : maxSigned31BitInt;
+
+ {
+ !((changedBits & maxSigned31BitInt) === changedBits) ? warning$1(false, 'calculateChangedBits: Expected the return value to be a ' + '31-bit integer. Instead received: %s', changedBits) : void 0;
+ }
+ return changedBits | 0;
+ }
+}
+
+function scheduleWorkOnParentPath(parent, renderExpirationTime) {
+ // Update the child expiration time of all the ancestors, including
+ // the alternates.
+ var node = parent;
+ while (node !== null) {
+ var alternate = node.alternate;
+ if (node.childExpirationTime < renderExpirationTime) {
+ node.childExpirationTime = renderExpirationTime;
+ if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
+ alternate.childExpirationTime = renderExpirationTime;
+ }
+ } else if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
+ alternate.childExpirationTime = renderExpirationTime;
+ } else {
+ // Neither alternate was updated, which means the rest of the
+ // ancestor path already has sufficient priority.
+ break;
+ }
+ node = node.return;
+ }
+}
+
+function propagateContextChange(workInProgress, context, changedBits, renderExpirationTime) {
+ var fiber = workInProgress.child;
+ if (fiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ fiber.return = workInProgress;
+ }
+ while (fiber !== null) {
+ var nextFiber = void 0;
+
+ // Visit this fiber.
+ var list = fiber.contextDependencies;
+ if (list !== null) {
+ nextFiber = fiber.child;
+
+ var dependency = list.first;
+ while (dependency !== null) {
+ // Check if the context matches.
+ if (dependency.context === context && (dependency.observedBits & changedBits) !== 0) {
+ // Match! Schedule an update on this fiber.
+
+ if (fiber.tag === ClassComponent) {
+ // Schedule a force update on the work-in-progress.
+ var update = createUpdate(renderExpirationTime);
+ update.tag = ForceUpdate;
+ // TODO: Because we don't have a work-in-progress, this will add the
+ // update to the current fiber, too, which means it will persist even if
+ // this render is thrown away. Since it's a race condition, not sure it's
+ // worth fixing.
+ enqueueUpdate(fiber, update);
+ }
+
+ if (fiber.expirationTime < renderExpirationTime) {
+ fiber.expirationTime = renderExpirationTime;
+ }
+ var alternate = fiber.alternate;
+ if (alternate !== null && alternate.expirationTime < renderExpirationTime) {
+ alternate.expirationTime = renderExpirationTime;
+ }
+
+ scheduleWorkOnParentPath(fiber.return, renderExpirationTime);
+
+ // Mark the expiration time on the list, too.
+ if (list.expirationTime < renderExpirationTime) {
+ list.expirationTime = renderExpirationTime;
+ }
+
+ // Since we already found a match, we can stop traversing the
+ // dependency list.
+ break;
+ }
+ dependency = dependency.next;
+ }
+ } else if (fiber.tag === ContextProvider) {
+ // Don't scan deeper if this is a matching provider
+ nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
+ } else if (enableSuspenseServerRenderer && fiber.tag === DehydratedSuspenseComponent) {
+ // If a dehydrated suspense component is in this subtree, we don't know
+ // if it will have any context consumers in it. The best we can do is
+ // mark it as having updates on its children.
+ if (fiber.expirationTime < renderExpirationTime) {
+ fiber.expirationTime = renderExpirationTime;
+ }
+ var _alternate = fiber.alternate;
+ if (_alternate !== null && _alternate.expirationTime < renderExpirationTime) {
+ _alternate.expirationTime = renderExpirationTime;
+ }
+ // This is intentionally passing this fiber as the parent
+ // because we want to schedule this fiber as having work
+ // on its children. We'll use the childExpirationTime on
+ // this fiber to indicate that a context has changed.
+ scheduleWorkOnParentPath(fiber, renderExpirationTime);
+ nextFiber = fiber.sibling;
+ } else {
+ // Traverse down.
+ nextFiber = fiber.child;
+ }
+
+ if (nextFiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ nextFiber.return = fiber;
+ } else {
+ // No child. Traverse to next sibling.
+ nextFiber = fiber;
+ while (nextFiber !== null) {
+ if (nextFiber === workInProgress) {
+ // We're back to the root of this subtree. Exit.
+ nextFiber = null;
+ break;
+ }
+ var sibling = nextFiber.sibling;
+ if (sibling !== null) {
+ // Set the return pointer of the sibling to the work-in-progress fiber.
+ sibling.return = nextFiber.return;
+ nextFiber = sibling;
+ break;
+ }
+ // No more siblings. Traverse up.
+ nextFiber = nextFiber.return;
+ }
+ }
+ fiber = nextFiber;
+ }
+}
+
+function prepareToReadContext(workInProgress, renderExpirationTime) {
+ currentlyRenderingFiber = workInProgress;
+ lastContextDependency = null;
+ lastContextWithAllBitsObserved = null;
+
+ var currentDependencies = workInProgress.contextDependencies;
+ if (currentDependencies !== null && currentDependencies.expirationTime >= renderExpirationTime) {
+ // Context list has a pending update. Mark that this fiber performed work.
+ markWorkInProgressReceivedUpdate();
+ }
+
+ // Reset the work-in-progress list
+ workInProgress.contextDependencies = null;
+}
+
+function readContext(context, observedBits) {
+ {
+ // This warning would fire if you read context inside a Hook like useMemo.
+ // Unlike the class check below, it's not enforced in production for perf.
+ !!isDisallowedContextReadInDEV ? warning$1(false, 'Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().') : void 0;
+ }
+
+ if (lastContextWithAllBitsObserved === context) {
+ // Nothing to do. We already observe everything in this context.
+ } else if (observedBits === false || observedBits === 0) {
+ // Do not observe any updates.
+ } else {
+ var resolvedObservedBits = void 0; // Avoid deopting on observable arguments or heterogeneous types.
+ if (typeof observedBits !== 'number' || observedBits === maxSigned31BitInt) {
+ // Observe all updates.
+ lastContextWithAllBitsObserved = context;
+ resolvedObservedBits = maxSigned31BitInt;
+ } else {
+ resolvedObservedBits = observedBits;
+ }
+
+ var contextItem = {
+ context: context,
+ observedBits: resolvedObservedBits,
+ next: null
+ };
+
+ if (lastContextDependency === null) {
+ !(currentlyRenderingFiber !== null) ? invariant(false, 'Context can only be read while React is rendering. In classes, you can read it in the render method or getDerivedStateFromProps. In function components, you can read it directly in the function body, but not inside Hooks like useReducer() or useMemo().') : void 0;
+
+ // This is the first dependency for this component. Create a new list.
+ lastContextDependency = contextItem;
+ currentlyRenderingFiber.contextDependencies = {
+ first: contextItem,
+ expirationTime: NoWork
+ };
+ } else {
+ // Append a new context item.
+ lastContextDependency = lastContextDependency.next = contextItem;
+ }
+ }
+ return isPrimaryRenderer ? context._currentValue : context._currentValue2;
+}
+
+// UpdateQueue is a linked list of prioritized updates.
+//
+// Like fibers, update queues come in pairs: a current queue, which represents
+// the visible state of the screen, and a work-in-progress queue, which can be
+// mutated and processed asynchronously before it is committed — a form of
+// double buffering. If a work-in-progress render is discarded before finishing,
+// we create a new work-in-progress by cloning the current queue.
+//
+// Both queues share a persistent, singly-linked list structure. To schedule an
+// update, we append it to the end of both queues. Each queue maintains a
+// pointer to first update in the persistent list that hasn't been processed.
+// The work-in-progress pointer always has a position equal to or greater than
+// the current queue, since we always work on that one. The current queue's
+// pointer is only updated during the commit phase, when we swap in the
+// work-in-progress.
+//
+// For example:
+//
+// Current pointer: A - B - C - D - E - F
+// Work-in-progress pointer: D - E - F
+// ^
+// The work-in-progress queue has
+// processed more updates than current.
+//
+// The reason we append to both queues is because otherwise we might drop
+// updates without ever processing them. For example, if we only add updates to
+// the work-in-progress queue, some updates could be lost whenever a work-in
+// -progress render restarts by cloning from current. Similarly, if we only add
+// updates to the current queue, the updates will be lost whenever an already
+// in-progress queue commits and swaps with the current queue. However, by
+// adding to both queues, we guarantee that the update will be part of the next
+// work-in-progress. (And because the work-in-progress queue becomes the
+// current queue once it commits, there's no danger of applying the same
+// update twice.)
+//
+// Prioritization
+// --------------
+//
+// Updates are not sorted by priority, but by insertion; new updates are always
+// appended to the end of the list.
+//
+// The priority is still important, though. When processing the update queue
+// during the render phase, only the updates with sufficient priority are
+// included in the result. If we skip an update because it has insufficient
+// priority, it remains in the queue to be processed later, during a lower
+// priority render. Crucially, all updates subsequent to a skipped update also
+// remain in the queue *regardless of their priority*. That means high priority
+// updates are sometimes processed twice, at two separate priorities. We also
+// keep track of a base state, that represents the state before the first
+// update in the queue is applied.
+//
+// For example:
+//
+// Given a base state of '', and the following queue of updates
+//
+// A1 - B2 - C1 - D2
+//
+// where the number indicates the priority, and the update is applied to the
+// previous state by appending a letter, React will process these updates as
+// two separate renders, one per distinct priority level:
+//
+// First render, at priority 1:
+// Base state: ''
+// Updates: [A1, C1]
+// Result state: 'AC'
+//
+// Second render, at priority 2:
+// Base state: 'A' <- The base state does not include C1,
+// because B2 was skipped.
+// Updates: [B2, C1, D2] <- C1 was rebased on top of B2
+// Result state: 'ABCD'
+//
+// Because we process updates in insertion order, and rebase high priority
+// updates when preceding updates are skipped, the final result is deterministic
+// regardless of priority. Intermediate state may vary according to system
+// resources, but the final state is always the same.
+
+var UpdateState = 0;
+var ReplaceState = 1;
+var ForceUpdate = 2;
+var CaptureUpdate = 3;
+
+// Global state that is reset at the beginning of calling `processUpdateQueue`.
+// It should only be read right after calling `processUpdateQueue`, via
+// `checkHasForceUpdateAfterProcessing`.
+var hasForceUpdate = false;
+
+var didWarnUpdateInsideUpdate = void 0;
+var currentlyProcessingQueue = void 0;
+var resetCurrentlyProcessingQueue = void 0;
+{
+ didWarnUpdateInsideUpdate = false;
+ currentlyProcessingQueue = null;
+ resetCurrentlyProcessingQueue = function () {
+ currentlyProcessingQueue = null;
+ };
+}
+
+function createUpdateQueue(baseState) {
+ var queue = {
+ baseState: baseState,
+ firstUpdate: null,
+ lastUpdate: null,
+ firstCapturedUpdate: null,
+ lastCapturedUpdate: null,
+ firstEffect: null,
+ lastEffect: null,
+ firstCapturedEffect: null,
+ lastCapturedEffect: null
+ };
+ return queue;
+}
+
+function cloneUpdateQueue(currentQueue) {
+ var queue = {
+ baseState: currentQueue.baseState,
+ firstUpdate: currentQueue.firstUpdate,
+ lastUpdate: currentQueue.lastUpdate,
+
+ // TODO: With resuming, if we bail out and resuse the child tree, we should
+ // keep these effects.
+ firstCapturedUpdate: null,
+ lastCapturedUpdate: null,
+
+ firstEffect: null,
+ lastEffect: null,
+
+ firstCapturedEffect: null,
+ lastCapturedEffect: null
+ };
+ return queue;
+}
+
+function createUpdate(expirationTime) {
+ return {
+ expirationTime: expirationTime,
+
+ tag: UpdateState,
+ payload: null,
+ callback: null,
+
+ next: null,
+ nextEffect: null
+ };
+}
+
+function appendUpdateToQueue(queue, update) {
+ // Append the update to the end of the list.
+ if (queue.lastUpdate === null) {
+ // Queue is empty
+ queue.firstUpdate = queue.lastUpdate = update;
+ } else {
+ queue.lastUpdate.next = update;
+ queue.lastUpdate = update;
+ }
+}
+
+function enqueueUpdate(fiber, update) {
+ // Update queues are created lazily.
+ var alternate = fiber.alternate;
+ var queue1 = void 0;
+ var queue2 = void 0;
+ if (alternate === null) {
+ // There's only one fiber.
+ queue1 = fiber.updateQueue;
+ queue2 = null;
+ if (queue1 === null) {
+ queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
+ }
+ } else {
+ // There are two owners.
+ queue1 = fiber.updateQueue;
+ queue2 = alternate.updateQueue;
+ if (queue1 === null) {
+ if (queue2 === null) {
+ // Neither fiber has an update queue. Create new ones.
+ queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
+ queue2 = alternate.updateQueue = createUpdateQueue(alternate.memoizedState);
+ } else {
+ // Only one fiber has an update queue. Clone to create a new one.
+ queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
+ }
+ } else {
+ if (queue2 === null) {
+ // Only one fiber has an update queue. Clone to create a new one.
+ queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
+ } else {
+ // Both owners have an update queue.
+ }
+ }
+ }
+ if (queue2 === null || queue1 === queue2) {
+ // There's only a single queue.
+ appendUpdateToQueue(queue1, update);
+ } else {
+ // There are two queues. We need to append the update to both queues,
+ // while accounting for the persistent structure of the list — we don't
+ // want the same update to be added multiple times.
+ if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
+ // One of the queues is not empty. We must add the update to both queues.
+ appendUpdateToQueue(queue1, update);
+ appendUpdateToQueue(queue2, update);
+ } else {
+ // Both queues are non-empty. The last update is the same in both lists,
+ // because of structural sharing. So, only append to one of the lists.
+ appendUpdateToQueue(queue1, update);
+ // But we still need to update the `lastUpdate` pointer of queue2.
+ queue2.lastUpdate = update;
+ }
+ }
+
+ {
+ if (fiber.tag === ClassComponent && (currentlyProcessingQueue === queue1 || queue2 !== null && currentlyProcessingQueue === queue2) && !didWarnUpdateInsideUpdate) {
+ warningWithoutStack$1(false, 'An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.');
+ didWarnUpdateInsideUpdate = true;
+ }
+ }
+}
+
+function enqueueCapturedUpdate(workInProgress, update) {
+ // Captured updates go into a separate list, and only on the work-in-
+ // progress queue.
+ var workInProgressQueue = workInProgress.updateQueue;
+ if (workInProgressQueue === null) {
+ workInProgressQueue = workInProgress.updateQueue = createUpdateQueue(workInProgress.memoizedState);
+ } else {
+ // TODO: I put this here rather than createWorkInProgress so that we don't
+ // clone the queue unnecessarily. There's probably a better way to
+ // structure this.
+ workInProgressQueue = ensureWorkInProgressQueueIsAClone(workInProgress, workInProgressQueue);
+ }
+
+ // Append the update to the end of the list.
+ if (workInProgressQueue.lastCapturedUpdate === null) {
+ // This is the first render phase update
+ workInProgressQueue.firstCapturedUpdate = workInProgressQueue.lastCapturedUpdate = update;
+ } else {
+ workInProgressQueue.lastCapturedUpdate.next = update;
+ workInProgressQueue.lastCapturedUpdate = update;
+ }
+}
+
+function ensureWorkInProgressQueueIsAClone(workInProgress, queue) {
+ var current = workInProgress.alternate;
+ if (current !== null) {
+ // If the work-in-progress queue is equal to the current queue,
+ // we need to clone it first.
+ if (queue === current.updateQueue) {
+ queue = workInProgress.updateQueue = cloneUpdateQueue(queue);
+ }
+ }
+ return queue;
+}
+
+function getStateFromUpdate(workInProgress, queue, update, prevState, nextProps, instance) {
+ switch (update.tag) {
+ case ReplaceState:
+ {
+ var _payload = update.payload;
+ if (typeof _payload === 'function') {
+ // Updater function
+ {
+ enterDisallowedContextReadInDEV();
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ _payload.call(instance, prevState, nextProps);
+ }
+ }
+ var nextState = _payload.call(instance, prevState, nextProps);
+ {
+ exitDisallowedContextReadInDEV();
+ }
+ return nextState;
+ }
+ // State object
+ return _payload;
+ }
+ case CaptureUpdate:
+ {
+ workInProgress.effectTag = workInProgress.effectTag & ~ShouldCapture | DidCapture;
+ }
+ // Intentional fallthrough
+ case UpdateState:
+ {
+ var _payload2 = update.payload;
+ var partialState = void 0;
+ if (typeof _payload2 === 'function') {
+ // Updater function
+ {
+ enterDisallowedContextReadInDEV();
+ if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
+ _payload2.call(instance, prevState, nextProps);
+ }
+ }
+ partialState = _payload2.call(instance, prevState, nextProps);
+ {
+ exitDisallowedContextReadInDEV();
+ }
+ } else {
+ // Partial state object
+ partialState = _payload2;
+ }
+ if (partialState === null || partialState === undefined) {
+ // Null and undefined are treated as no-ops.
+ return prevState;
+ }
+ // Merge the partial state and the previous state.
+ return _assign({}, prevState, partialState);
+ }
+ case ForceUpdate:
+ {
+ hasForceUpdate = true;
+ return prevState;
+ }
+ }
+ return prevState;
+}
+
+function processUpdateQueue(workInProgress, queue, props, instance, renderExpirationTime) {
+ hasForceUpdate = false;
+
+ queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);
+
+ {
+ currentlyProcessingQueue = queue;
+ }
+
+ // These values may change as we process the queue.
+ var newBaseState = queue.baseState;
+ var newFirstUpdate = null;
+ var newExpirationTime = NoWork;
+
+ // Iterate through the list of updates to compute the result.
+ var update = queue.firstUpdate;
+ var resultState = newBaseState;
+ while (update !== null) {
+ var updateExpirationTime = update.expirationTime;
+ if (updateExpirationTime < renderExpirationTime) {
+ // This update does not have sufficient priority. Skip it.
+ if (newFirstUpdate === null) {
+ // This is the first skipped update. It will be the first update in
+ // the new list.
+ newFirstUpdate = update;
+ // Since this is the first update that was skipped, the current result
+ // is the new base state.
+ newBaseState = resultState;
+ }
+ // Since this update will remain in the list, update the remaining
+ // expiration time.
+ if (newExpirationTime < updateExpirationTime) {
+ newExpirationTime = updateExpirationTime;
+ }
+ } else {
+ // This update does have sufficient priority. Process it and compute
+ // a new result.
+ resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
+ var _callback = update.callback;
+ if (_callback !== null) {
+ workInProgress.effectTag |= Callback;
+ // Set this to null, in case it was mutated during an aborted render.
+ update.nextEffect = null;
+ if (queue.lastEffect === null) {
+ queue.firstEffect = queue.lastEffect = update;
+ } else {
+ queue.lastEffect.nextEffect = update;
+ queue.lastEffect = update;
+ }
+ }
+ }
+ // Continue to the next update.
+ update = update.next;
+ }
+
+ // Separately, iterate though the list of captured updates.
+ var newFirstCapturedUpdate = null;
+ update = queue.firstCapturedUpdate;
+ while (update !== null) {
+ var _updateExpirationTime = update.expirationTime;
+ if (_updateExpirationTime < renderExpirationTime) {
+ // This update does not have sufficient priority. Skip it.
+ if (newFirstCapturedUpdate === null) {
+ // This is the first skipped captured update. It will be the first
+ // update in the new list.
+ newFirstCapturedUpdate = update;
+ // If this is the first update that was skipped, the current result is
+ // the new base state.
+ if (newFirstUpdate === null) {
+ newBaseState = resultState;
+ }
+ }
+ // Since this update will remain in the list, update the remaining
+ // expiration time.
+ if (newExpirationTime < _updateExpirationTime) {
+ newExpirationTime = _updateExpirationTime;
+ }
+ } else {
+ // This update does have sufficient priority. Process it and compute
+ // a new result.
+ resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
+ var _callback2 = update.callback;
+ if (_callback2 !== null) {
+ workInProgress.effectTag |= Callback;
+ // Set this to null, in case it was mutated during an aborted render.
+ update.nextEffect = null;
+ if (queue.lastCapturedEffect === null) {
+ queue.firstCapturedEffect = queue.lastCapturedEffect = update;
+ } else {
+ queue.lastCapturedEffect.nextEffect = update;
+ queue.lastCapturedEffect = update;
+ }
+ }
+ }
+ update = update.next;
+ }
+
+ if (newFirstUpdate === null) {
+ queue.lastUpdate = null;
+ }
+ if (newFirstCapturedUpdate === null) {
+ queue.lastCapturedUpdate = null;
+ } else {
+ workInProgress.effectTag |= Callback;
+ }
+ if (newFirstUpdate === null && newFirstCapturedUpdate === null) {
+ // We processed every update, without skipping. That means the new base
+ // state is the same as the result state.
+ newBaseState = resultState;
+ }
+
+ queue.baseState = newBaseState;
+ queue.firstUpdate = newFirstUpdate;
+ queue.firstCapturedUpdate = newFirstCapturedUpdate;
+
+ // Set the remaining expiration time to be whatever is remaining in the queue.
+ // This should be fine because the only two other things that contribute to
+ // expiration time are props and context. We're already in the middle of the
+ // begin phase by the time we start processing the queue, so we've already
+ // dealt with the props. Context in components that specify
+ // shouldComponentUpdate is tricky; but we'll have to account for
+ // that regardless.
+ workInProgress.expirationTime = newExpirationTime;
+ workInProgress.memoizedState = resultState;
+
+ {
+ currentlyProcessingQueue = null;
+ }
+}
+
+function callCallback(callback, context) {
+ !(typeof callback === 'function') ? invariant(false, 'Invalid argument passed as callback. Expected a function. Instead received: %s', callback) : void 0;
+ callback.call(context);
+}
+
+function resetHasForceUpdateBeforeProcessing() {
+ hasForceUpdate = false;
+}
+
+function checkHasForceUpdateAfterProcessing() {
+ return hasForceUpdate;
+}
+
+function commitUpdateQueue(finishedWork, finishedQueue, instance, renderExpirationTime) {
+ // If the finished render included captured updates, and there are still
+ // lower priority updates left over, we need to keep the captured updates
+ // in the queue so that they are rebased and not dropped once we process the
+ // queue again at the lower priority.
+ if (finishedQueue.firstCapturedUpdate !== null) {
+ // Join the captured update list to the end of the normal list.
+ if (finishedQueue.lastUpdate !== null) {
+ finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;
+ finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;
+ }
+ // Clear the list of captured updates.
+ finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;
+ }
+
+ // Commit the effects
+ commitUpdateEffects(finishedQueue.firstEffect, instance);
+ finishedQueue.firstEffect = finishedQueue.lastEffect = null;
+
+ commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);
+ finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
+}
+
+function commitUpdateEffects(effect, instance) {
+ while (effect !== null) {
+ var _callback3 = effect.callback;
+ if (_callback3 !== null) {
+ effect.callback = null;
+ callCallback(_callback3, instance);
+ }
+ effect = effect.nextEffect;
+ }
+}
+
+function createCapturedValue(value, source) {
+ // If the value is an error, call this function immediately after it is thrown
+ // so the stack is accurate.
+ return {
+ value: value,
+ source: source,
+ stack: getStackByFiberInDevAndProd(source)
+ };
+}
+
+function markUpdate(workInProgress) {
+ // Tag the fiber with an update effect. This turns a Placement into
+ // a PlacementAndUpdate.
+ workInProgress.effectTag |= Update;
+}
+
+function markRef$1(workInProgress) {
+ workInProgress.effectTag |= Ref;
+}
+
+var appendAllChildren = void 0;
+var updateHostContainer = void 0;
+var updateHostComponent$1 = void 0;
+var updateHostText$1 = void 0;
+if (supportsMutation) {
+ // Mutation mode
+
+ appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ appendInitialChild(parent, node.stateNode);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+
+ updateHostContainer = function (workInProgress) {
+ // Noop
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ // If we have an alternate, that means this is an update and we need to
+ // schedule a side-effect to do the updates.
+ var oldProps = current.memoizedProps;
+ if (oldProps === newProps) {
+ // In mutation mode, this is sufficient for a bailout because
+ // we won't touch this node even if children changed.
+ return;
+ }
+
+ // If we get updated because one of our children updated, we don't
+ // have newProps so we'll have to reuse them.
+ // TODO: Split the update API as separate for the props vs. children.
+ // Even better would be if children weren't special cased at all tho.
+ var instance = workInProgress.stateNode;
+ var currentHostContext = getHostContext();
+ // TODO: Experiencing an error where oldProps is null. Suggests a host
+ // component is hitting the resume path. Figure out why. Possibly
+ // related to `hidden`.
+ var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
+ // TODO: Type this specific to this type of component.
+ workInProgress.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update. All the work is done in commitWork.
+ if (updatePayload) {
+ markUpdate(workInProgress);
+ }
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ // If the text differs, mark it as an update. All the work in done in commitWork.
+ if (oldText !== newText) {
+ markUpdate(workInProgress);
+ }
+ };
+} else if (supportsPersistence) {
+ // Persistent host tree mode
+
+ appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ // eslint-disable-next-line no-labels
+ branches: if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var props = node.memoizedProps;
+ var type = node.type;
+ if (isHidden) {
+ // This child is inside a timed out tree. Hide it.
+ instance = cloneHiddenInstance(instance, type, props, node);
+ } else {
+ // This child was previously inside a timed out tree. If it was not
+ // updated during this render, it may need to be unhidden. Clone
+ // again to be sure.
+ instance = cloneUnhiddenInstance(instance, type, props, node);
+ }
+ node.stateNode = instance;
+ }
+ appendInitialChild(parent, instance);
+ } else if (node.tag === HostText) {
+ var _instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var text = node.memoizedProps;
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ if (isHidden) {
+ _instance = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ } else {
+ _instance = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ }
+ node.stateNode = _instance;
+ }
+ appendInitialChild(parent, _instance);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.tag === SuspenseComponent) {
+ var current = node.alternate;
+ if (current !== null) {
+ var oldState = current.memoizedState;
+ var newState = node.memoizedState;
+ var oldIsHidden = oldState !== null;
+ var newIsHidden = newState !== null;
+ if (oldIsHidden !== newIsHidden) {
+ // The placeholder either just timed out or switched back to the normal
+ // children after having previously timed out. Toggle the visibility of
+ // the direct host children.
+ var primaryChildParent = newIsHidden ? node.child : node;
+ if (primaryChildParent !== null) {
+ appendAllChildren(parent, primaryChildParent, true, newIsHidden);
+ }
+ // eslint-disable-next-line no-labels
+ break branches;
+ }
+ }
+ if (node.child !== null) {
+ // Continue traversing like normal
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ // $FlowFixMe This is correct but Flow is confused by the labeled break.
+ node = node;
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+
+ // An unfortunate fork of appendAllChildren because we have two different parent types.
+ var appendAllChildrenToContainer = function (containerChildSet, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ // eslint-disable-next-line no-labels
+ branches: if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var props = node.memoizedProps;
+ var type = node.type;
+ if (isHidden) {
+ // This child is inside a timed out tree. Hide it.
+ instance = cloneHiddenInstance(instance, type, props, node);
+ } else {
+ // This child was previously inside a timed out tree. If it was not
+ // updated during this render, it may need to be unhidden. Clone
+ // again to be sure.
+ instance = cloneUnhiddenInstance(instance, type, props, node);
+ }
+ node.stateNode = instance;
+ }
+ appendChildToContainerChildSet(containerChildSet, instance);
+ } else if (node.tag === HostText) {
+ var _instance2 = node.stateNode;
+ if (needsVisibilityToggle) {
+ var text = node.memoizedProps;
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ if (isHidden) {
+ _instance2 = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ } else {
+ _instance2 = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ }
+ node.stateNode = _instance2;
+ }
+ appendChildToContainerChildSet(containerChildSet, _instance2);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.tag === SuspenseComponent) {
+ var current = node.alternate;
+ if (current !== null) {
+ var oldState = current.memoizedState;
+ var newState = node.memoizedState;
+ var oldIsHidden = oldState !== null;
+ var newIsHidden = newState !== null;
+ if (oldIsHidden !== newIsHidden) {
+ // The placeholder either just timed out or switched back to the normal
+ // children after having previously timed out. Toggle the visibility of
+ // the direct host children.
+ var primaryChildParent = newIsHidden ? node.child : node;
+ if (primaryChildParent !== null) {
+ appendAllChildrenToContainer(containerChildSet, primaryChildParent, true, newIsHidden);
+ }
+ // eslint-disable-next-line no-labels
+ break branches;
+ }
+ }
+ if (node.child !== null) {
+ // Continue traversing like normal
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ // $FlowFixMe This is correct but Flow is confused by the labeled break.
+ node = node;
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+ updateHostContainer = function (workInProgress) {
+ var portalOrRoot = workInProgress.stateNode;
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged) {
+ // No changes, just reuse the existing instance.
+ } else {
+ var container = portalOrRoot.containerInfo;
+ var newChildSet = createContainerChildSet(container);
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildrenToContainer(newChildSet, workInProgress, false, false);
+ portalOrRoot.pendingChildren = newChildSet;
+ // Schedule an update on the container to swap out the container.
+ markUpdate(workInProgress);
+ finalizeContainerChildren(container, newChildSet);
+ }
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ var currentInstance = current.stateNode;
+ var oldProps = current.memoizedProps;
+ // If there are no effects associated with this node, then none of our children had any updates.
+ // This guarantees that we can reuse all of them.
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged && oldProps === newProps) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ return;
+ }
+ var recyclableInstance = workInProgress.stateNode;
+ var currentHostContext = getHostContext();
+ var updatePayload = null;
+ if (oldProps !== newProps) {
+ updatePayload = prepareUpdate(recyclableInstance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
+ }
+ if (childrenUnchanged && updatePayload === null) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ return;
+ }
+ var newInstance = cloneInstance(currentInstance, updatePayload, type, oldProps, newProps, workInProgress, childrenUnchanged, recyclableInstance);
+ if (finalizeInitialChildren(newInstance, type, newProps, rootContainerInstance, currentHostContext)) {
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = newInstance;
+ if (childrenUnchanged) {
+ // If there are no other effects in this tree, we need to flag this node as having one.
+ // Even though we're not going to use it for anything.
+ // Otherwise parents won't know that there are new children to propagate upwards.
+ markUpdate(workInProgress);
+ } else {
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildren(newInstance, workInProgress, false, false);
+ }
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ if (oldText !== newText) {
+ // If the text content differs, we'll create a new text instance for it.
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ workInProgress.stateNode = createTextInstance(newText, rootContainerInstance, currentHostContext, workInProgress);
+ // We'll have to mark it as having an effect, even though we won't use the effect for anything.
+ // This lets the parents know that at least one of their children has changed.
+ markUpdate(workInProgress);
+ }
+ };
+} else {
+ // No host operations
+ updateHostContainer = function (workInProgress) {
+ // Noop
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ // Noop
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ // Noop
+ };
+}
+
+function completeWork(current, workInProgress, renderExpirationTime) {
+ var newProps = workInProgress.pendingProps;
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ break;
+ case LazyComponent:
+ break;
+ case SimpleMemoComponent:
+ case FunctionComponent:
+ break;
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
+ }
+ break;
+ }
+ case HostRoot:
+ {
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ var fiberRoot = workInProgress.stateNode;
+ if (fiberRoot.pendingContext) {
+ fiberRoot.context = fiberRoot.pendingContext;
+ fiberRoot.pendingContext = null;
+ }
+ if (current === null || current.child === null) {
+ // If we hydrated, pop so that we can delete any remaining children
+ // that weren't hydrated.
+ popHydrationState(workInProgress);
+ // This resets the hacky state to fix isMounted before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag &= ~Placement;
+ }
+ updateHostContainer(workInProgress);
+ break;
+ }
+ case HostComponent:
+ {
+ popHostContext(workInProgress);
+ var rootContainerInstance = getRootHostContainer();
+ var type = workInProgress.type;
+ if (current !== null && workInProgress.stateNode != null) {
+ updateHostComponent$1(current, workInProgress, type, newProps, rootContainerInstance);
+
+ if (current.ref !== workInProgress.ref) {
+ markRef$1(workInProgress);
+ }
+ } else {
+ if (!newProps) {
+ !(workInProgress.stateNode !== null) ? invariant(false, 'We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ // This can happen when we abort work.
+ break;
+ }
+
+ var currentHostContext = getHostContext();
+ // TODO: Move createInstance to beginWork and keep it on a context
+ // "stack" as the parent. Then append children as we go in beginWork
+ // or completeWork depending on we want to add then top->down or
+ // bottom->up. Top->down is faster in IE11.
+ var wasHydrated = popHydrationState(workInProgress);
+ if (wasHydrated) {
+ // TODO: Move this and createInstance step into the beginPhase
+ // to consolidate.
+ if (prepareToHydrateHostInstance(workInProgress, rootContainerInstance, currentHostContext)) {
+ // If changes to the hydrated node needs to be applied at the
+ // commit-phase we mark this as such.
+ markUpdate(workInProgress);
+ }
+ } else {
+ var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
+
+ appendAllChildren(instance, workInProgress, false, false);
+
+ // Certain renderers require commit-time effects for initial mount.
+ // (eg DOM renderer supports auto-focus for certain elements).
+ // Make sure such renderers get scheduled for later work.
+ if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance, currentHostContext)) {
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = instance;
+ }
+
+ if (workInProgress.ref !== null) {
+ // If there is a ref on a host node we need to schedule a callback
+ markRef$1(workInProgress);
+ }
+ }
+ break;
+ }
+ case HostText:
+ {
+ var newText = newProps;
+ if (current && workInProgress.stateNode != null) {
+ var oldText = current.memoizedProps;
+ // If we have an alternate, that means this is an update and we need
+ // to schedule a side-effect to do the updates.
+ updateHostText$1(current, workInProgress, oldText, newText);
+ } else {
+ if (typeof newText !== 'string') {
+ !(workInProgress.stateNode !== null) ? invariant(false, 'We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ // This can happen when we abort work.
+ }
+ var _rootContainerInstance = getRootHostContainer();
+ var _currentHostContext = getHostContext();
+ var _wasHydrated = popHydrationState(workInProgress);
+ if (_wasHydrated) {
+ if (prepareToHydrateHostTextInstance(workInProgress)) {
+ markUpdate(workInProgress);
+ }
+ } else {
+ workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext, workInProgress);
+ }
+ }
+ break;
+ }
+ case ForwardRef:
+ break;
+ case SuspenseComponent:
+ {
+ var nextState = workInProgress.memoizedState;
+ if ((workInProgress.effectTag & DidCapture) !== NoEffect) {
+ // Something suspended. Re-render with the fallback children.
+ workInProgress.expirationTime = renderExpirationTime;
+ // Do not reset the effect list.
+ return workInProgress;
+ }
+
+ var nextDidTimeout = nextState !== null;
+ var prevDidTimeout = current !== null && current.memoizedState !== null;
+
+ if (current !== null && !nextDidTimeout && prevDidTimeout) {
+ // We just switched from the fallback to the normal children. Delete
+ // the fallback.
+ // TODO: Would it be better to store the fallback fragment on
+ var currentFallbackChild = current.child.sibling;
+ if (currentFallbackChild !== null) {
+ // Deletions go at the beginning of the return fiber's effect list
+ var first = workInProgress.firstEffect;
+ if (first !== null) {
+ workInProgress.firstEffect = currentFallbackChild;
+ currentFallbackChild.nextEffect = first;
+ } else {
+ workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChild;
+ currentFallbackChild.nextEffect = null;
+ }
+ currentFallbackChild.effectTag = Deletion;
+ }
+ }
+
+ if (nextDidTimeout || prevDidTimeout) {
+ // If the children are hidden, or if they were previous hidden, schedule
+ // an effect to toggle their visibility. This is also used to attach a
+ // retry listener to the promise.
+ workInProgress.effectTag |= Update;
+ }
+ break;
+ }
+ case Fragment:
+ break;
+ case Mode:
+ break;
+ case Profiler:
+ break;
+ case HostPortal:
+ popHostContainer(workInProgress);
+ updateHostContainer(workInProgress);
+ break;
+ case ContextProvider:
+ // Pop provider fiber
+ popProvider(workInProgress);
+ break;
+ case ContextConsumer:
+ break;
+ case MemoComponent:
+ break;
+ case IncompleteClassComponent:
+ {
+ // Same as class component case. I put it down here so that the tags are
+ // sequential to ensure this switch is compiled to a jump table.
+ var _Component = workInProgress.type;
+ if (isContextProvider(_Component)) {
+ popContext(workInProgress);
+ }
+ break;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ if (current === null) {
+ var _wasHydrated2 = popHydrationState(workInProgress);
+ !_wasHydrated2 ? invariant(false, 'A dehydrated suspense component was completed without a hydrated node. This is probably a bug in React.') : void 0;
+ skipPastDehydratedSuspenseInstance(workInProgress);
+ } else if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This boundary did not suspend so it's now hydrated.
+ // To handle any future suspense cases, we're going to now upgrade it
+ // to a Suspense component. We detach it from the existing current fiber.
+ current.alternate = null;
+ workInProgress.alternate = null;
+ workInProgress.tag = SuspenseComponent;
+ workInProgress.memoizedState = null;
+ workInProgress.stateNode = null;
+ }
+ }
+ break;
+ }
+ default:
+ invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.');
+ }
+
+ return null;
+}
+
+function shouldCaptureSuspense(workInProgress) {
+ // In order to capture, the Suspense component must have a fallback prop.
+ if (workInProgress.memoizedProps.fallback === undefined) {
+ return false;
+ }
+ // If it was the primary children that just suspended, capture and render the
+ // fallback. Otherwise, don't capture and bubble to the next boundary.
+ var nextState = workInProgress.memoizedState;
+ return nextState === null;
+}
+
+// This module is forked in different environments.
+// By default, return `true` to log errors to the console.
+// Forks can return `false` if this isn't desirable.
+function showErrorDialog(capturedError) {
+ return true;
+}
+
+function logCapturedError(capturedError) {
+ var logError = showErrorDialog(capturedError);
+
+ // Allow injected showErrorDialog() to prevent default console.error logging.
+ // This enables renderers like ReactNative to better manage redbox behavior.
+ if (logError === false) {
+ return;
+ }
+
+ var error = capturedError.error;
+ {
+ var componentName = capturedError.componentName,
+ componentStack = capturedError.componentStack,
+ errorBoundaryName = capturedError.errorBoundaryName,
+ errorBoundaryFound = capturedError.errorBoundaryFound,
+ willRetry = capturedError.willRetry;
+
+ // Browsers support silencing uncaught errors by calling
+ // `preventDefault()` in window `error` handler.
+ // We record this information as an expando on the error.
+
+ if (error != null && error._suppressLogging) {
+ if (errorBoundaryFound && willRetry) {
+ // The error is recoverable and was silenced.
+ // Ignore it and don't print the stack addendum.
+ // This is handy for testing error boundaries without noise.
+ return;
+ }
+ // The error is fatal. Since the silencing might have
+ // been accidental, we'll surface it anyway.
+ // However, the browser would have silenced the original error
+ // so we'll print it first, and then print the stack addendum.
+ console.error(error);
+ // For a more detailed description of this block, see:
+ // https://github.com/facebook/react/pull/13384
+ }
+
+ var componentNameMessage = componentName ? 'The above error occurred in the <' + componentName + '> component:' : 'The above error occurred in one of your React components:';
+
+ var errorBoundaryMessage = void 0;
+ // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow.
+ if (errorBoundaryFound && errorBoundaryName) {
+ if (willRetry) {
+ errorBoundaryMessage = 'React will try to recreate this component tree from scratch ' + ('using the error boundary you provided, ' + errorBoundaryName + '.');
+ } else {
+ errorBoundaryMessage = 'This error was initially handled by the error boundary ' + errorBoundaryName + '.\n' + 'Recreating the tree from scratch failed so React will unmount the tree.';
+ }
+ } else {
+ errorBoundaryMessage = 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://fb.me/react-error-boundaries to learn more about error boundaries.';
+ }
+ var combinedMessage = '' + componentNameMessage + componentStack + '\n\n' + ('' + errorBoundaryMessage);
+
+ // In development, we provide our own message with just the component stack.
+ // We don't include the original error message and JS stack because the browser
+ // has already printed it. Even if the application swallows the error, it is still
+ // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
+ console.error(combinedMessage);
+ }
+}
+
+var didWarnAboutUndefinedSnapshotBeforeUpdate = null;
+{
+ didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
+}
+
+var PossiblyWeakSet$1 = typeof WeakSet === 'function' ? WeakSet : Set;
+
+function logError(boundary, errorInfo) {
+ var source = errorInfo.source;
+ var stack = errorInfo.stack;
+ if (stack === null && source !== null) {
+ stack = getStackByFiberInDevAndProd(source);
+ }
+
+ var capturedError = {
+ componentName: source !== null ? getComponentName(source.type) : null,
+ componentStack: stack !== null ? stack : '',
+ error: errorInfo.value,
+ errorBoundary: null,
+ errorBoundaryName: null,
+ errorBoundaryFound: false,
+ willRetry: false
+ };
+
+ if (boundary !== null && boundary.tag === ClassComponent) {
+ capturedError.errorBoundary = boundary.stateNode;
+ capturedError.errorBoundaryName = getComponentName(boundary.type);
+ capturedError.errorBoundaryFound = true;
+ capturedError.willRetry = true;
+ }
+
+ try {
+ logCapturedError(capturedError);
+ } catch (e) {
+ // This method must not throw, or React internal state will get messed up.
+ // If console.error is overridden, or logCapturedError() shows a dialog that throws,
+ // we want to report this error outside of the normal stack as a last resort.
+ // https://github.com/facebook/react/issues/13188
+ setTimeout(function () {
+ throw e;
+ });
+ }
+}
+
+var callComponentWillUnmountWithTimer = function (current$$1, instance) {
+ startPhaseTimer(current$$1, 'componentWillUnmount');
+ instance.props = current$$1.memoizedProps;
+ instance.state = current$$1.memoizedState;
+ instance.componentWillUnmount();
+ stopPhaseTimer();
+};
+
+// Capture errors so they don't interrupt unmounting.
+function safelyCallComponentWillUnmount(current$$1, instance) {
+ {
+ invokeGuardedCallback(null, callComponentWillUnmountWithTimer, null, current$$1, instance);
+ if (hasCaughtError()) {
+ var unmountError = clearCaughtError();
+ captureCommitPhaseError(current$$1, unmountError);
+ }
+ }
+}
+
+function safelyDetachRef(current$$1) {
+ var ref = current$$1.ref;
+ if (ref !== null) {
+ if (typeof ref === 'function') {
+ {
+ invokeGuardedCallback(null, ref, null, null);
+ if (hasCaughtError()) {
+ var refError = clearCaughtError();
+ captureCommitPhaseError(current$$1, refError);
+ }
+ }
+ } else {
+ ref.current = null;
+ }
+ }
+}
+
+function safelyCallDestroy(current$$1, destroy) {
+ {
+ invokeGuardedCallback(null, destroy, null);
+ if (hasCaughtError()) {
+ var error = clearCaughtError();
+ captureCommitPhaseError(current$$1, error);
+ }
+ }
+}
+
+function commitBeforeMutationLifeCycles(current$$1, finishedWork) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent:
+ {
+ commitHookEffectList(UnmountSnapshot, NoEffect$1, finishedWork);
+ return;
+ }
+ case ClassComponent:
+ {
+ if (finishedWork.effectTag & Snapshot) {
+ if (current$$1 !== null) {
+ var prevProps = current$$1.memoizedProps;
+ var prevState = current$$1.memoizedState;
+ startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
+ var instance = finishedWork.stateNode;
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ {
+ if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
+ !(instance.props === finishedWork.memoizedProps) ? warning$1(false, 'Expected %s props to match memoized props before ' + 'getSnapshotBeforeUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ !(instance.state === finishedWork.memoizedState) ? warning$1(false, 'Expected %s state to match memoized state before ' + 'getSnapshotBeforeUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ }
+ }
+ var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
+ {
+ var didWarnSet = didWarnAboutUndefinedSnapshotBeforeUpdate;
+ if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
+ didWarnSet.add(finishedWork.type);
+ warningWithoutStack$1(false, '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' + 'must be returned. You have returned undefined.', getComponentName(finishedWork.type));
+ }
+ }
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
+ stopPhaseTimer();
+ }
+ }
+ return;
+ }
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ case IncompleteClassComponent:
+ // Nothing to do for these component types
+ return;
+ default:
+ {
+ invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.');
+ }
+ }
+}
+
+function commitHookEffectList(unmountTag, mountTag, finishedWork) {
+ var updateQueue = finishedWork.updateQueue;
+ var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
+ do {
+ if ((effect.tag & unmountTag) !== NoEffect$1) {
+ // Unmount
+ var destroy = effect.destroy;
+ effect.destroy = undefined;
+ if (destroy !== undefined) {
+ destroy();
+ }
+ }
+ if ((effect.tag & mountTag) !== NoEffect$1) {
+ // Mount
+ var create = effect.create;
+ effect.destroy = create();
+
+ {
+ var _destroy = effect.destroy;
+ if (_destroy !== undefined && typeof _destroy !== 'function') {
+ var addendum = void 0;
+ if (_destroy === null) {
+ addendum = ' You returned null. If your effect does not require clean ' + 'up, return undefined (or nothing).';
+ } else if (typeof _destroy.then === 'function') {
+ addendum = '\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' + 'Instead, write the async function inside your effect ' + 'and call it immediately:\n\n' + 'useEffect(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + '}, [someId]); // Or [] if effect doesn\'t need props or state\n\n' + 'Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching';
+ } else {
+ addendum = ' You returned: ' + _destroy;
+ }
+ warningWithoutStack$1(false, 'An effect function must not return anything besides a function, ' + 'which is used for clean-up.%s%s', addendum, getStackByFiberInDevAndProd(finishedWork));
+ }
+ }
+ }
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+}
+
+function commitPassiveHookEffects(finishedWork) {
+ commitHookEffectList(UnmountPassive, NoEffect$1, finishedWork);
+ commitHookEffectList(NoEffect$1, MountPassive, finishedWork);
+}
+
+function commitLifeCycles(finishedRoot, current$$1, finishedWork, committedExpirationTime) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent:
+ {
+ commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
+ break;
+ }
+ case ClassComponent:
+ {
+ var instance = finishedWork.stateNode;
+ if (finishedWork.effectTag & Update) {
+ if (current$$1 === null) {
+ startPhaseTimer(finishedWork, 'componentDidMount');
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ {
+ if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
+ !(instance.props === finishedWork.memoizedProps) ? warning$1(false, 'Expected %s props to match memoized props before ' + 'componentDidMount. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ !(instance.state === finishedWork.memoizedState) ? warning$1(false, 'Expected %s state to match memoized state before ' + 'componentDidMount. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ }
+ }
+ instance.componentDidMount();
+ stopPhaseTimer();
+ } else {
+ var prevProps = finishedWork.elementType === finishedWork.type ? current$$1.memoizedProps : resolveDefaultProps(finishedWork.type, current$$1.memoizedProps);
+ var prevState = current$$1.memoizedState;
+ startPhaseTimer(finishedWork, 'componentDidUpdate');
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ {
+ if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
+ !(instance.props === finishedWork.memoizedProps) ? warning$1(false, 'Expected %s props to match memoized props before ' + 'componentDidUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ !(instance.state === finishedWork.memoizedState) ? warning$1(false, 'Expected %s state to match memoized state before ' + 'componentDidUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ }
+ }
+ instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
+ stopPhaseTimer();
+ }
+ }
+ var updateQueue = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ {
+ if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
+ !(instance.props === finishedWork.memoizedProps) ? warning$1(false, 'Expected %s props to match memoized props before ' + 'processing the update queue. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ !(instance.state === finishedWork.memoizedState) ? warning$1(false, 'Expected %s state to match memoized state before ' + 'processing the update queue. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentName(finishedWork.type) || 'instance') : void 0;
+ }
+ }
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ commitUpdateQueue(finishedWork, updateQueue, instance, committedExpirationTime);
+ }
+ return;
+ }
+ case HostRoot:
+ {
+ var _updateQueue = finishedWork.updateQueue;
+ if (_updateQueue !== null) {
+ var _instance = null;
+ if (finishedWork.child !== null) {
+ switch (finishedWork.child.tag) {
+ case HostComponent:
+ _instance = getPublicInstance(finishedWork.child.stateNode);
+ break;
+ case ClassComponent:
+ _instance = finishedWork.child.stateNode;
+ break;
+ }
+ }
+ commitUpdateQueue(finishedWork, _updateQueue, _instance, committedExpirationTime);
+ }
+ return;
+ }
+ case HostComponent:
+ {
+ var _instance2 = finishedWork.stateNode;
+
+ // Renderers may schedule work to be done after host components are mounted
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
+ // These effects should only be committed when components are first mounted,
+ // aka when there is no current/alternate.
+ if (current$$1 === null && finishedWork.effectTag & Update) {
+ var type = finishedWork.type;
+ var props = finishedWork.memoizedProps;
+ commitMount(_instance2, type, props, finishedWork);
+ }
+
+ return;
+ }
+ case HostText:
+ {
+ // We have no life-cycles associated with text.
+ return;
+ }
+ case HostPortal:
+ {
+ // We have no life-cycles associated with portals.
+ return;
+ }
+ case Profiler:
+ {
+ if (enableProfilerTimer) {
+ var onRender = finishedWork.memoizedProps.onRender;
+
+ if (enableSchedulerTracing) {
+ onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime(), finishedRoot.memoizedInteractions);
+ } else {
+ onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime());
+ }
+ }
+ return;
+ }
+ case SuspenseComponent:
+ break;
+ case IncompleteClassComponent:
+ break;
+ default:
+ {
+ invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.');
+ }
+ }
+}
+
+function hideOrUnhideAllChildren(finishedWork, isHidden) {
+ if (supportsMutation) {
+ // We only have the top Fiber that was inserted but we need to recurse down its
+ var node = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (isHidden) {
+ hideInstance(instance);
+ } else {
+ unhideInstance(node.stateNode, node.memoizedProps);
+ }
+ } else if (node.tag === HostText) {
+ var _instance3 = node.stateNode;
+ if (isHidden) {
+ hideTextInstance(_instance3);
+ } else {
+ unhideTextInstance(_instance3, node.memoizedProps);
+ }
+ } else if (node.tag === SuspenseComponent && node.memoizedState !== null) {
+ // Found a nested Suspense component that timed out. Skip over the
+ var fallbackChildFragment = node.child.sibling;
+ fallbackChildFragment.return = node;
+ node = fallbackChildFragment;
+ continue;
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ }
+}
+
+function commitAttachRef(finishedWork) {
+ var ref = finishedWork.ref;
+ if (ref !== null) {
+ var instance = finishedWork.stateNode;
+ var instanceToUse = void 0;
+ switch (finishedWork.tag) {
+ case HostComponent:
+ instanceToUse = getPublicInstance(instance);
+ break;
+ default:
+ instanceToUse = instance;
+ }
+ if (typeof ref === 'function') {
+ ref(instanceToUse);
+ } else {
+ {
+ if (!ref.hasOwnProperty('current')) {
+ warningWithoutStack$1(false, 'Unexpected ref object provided for %s. ' + 'Use either a ref-setter function or React.createRef().%s', getComponentName(finishedWork.type), getStackByFiberInDevAndProd(finishedWork));
+ }
+ }
+
+ ref.current = instanceToUse;
+ }
+ }
+}
+
+function commitDetachRef(current$$1) {
+ var currentRef = current$$1.ref;
+ if (currentRef !== null) {
+ if (typeof currentRef === 'function') {
+ currentRef(null);
+ } else {
+ currentRef.current = null;
+ }
+ }
+}
+
+// User-originating errors (lifecycles and refs) should not interrupt
+// deletion, so don't let them throw. Host-originating errors should
+// interrupt deletion, so it's okay
+function commitUnmount(current$$1) {
+ onCommitUnmount(current$$1);
+
+ switch (current$$1.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ var updateQueue = current$$1.updateQueue;
+ if (updateQueue !== null) {
+ var lastEffect = updateQueue.lastEffect;
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
+ do {
+ var destroy = effect.destroy;
+ if (destroy !== undefined) {
+ safelyCallDestroy(current$$1, destroy);
+ }
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+ }
+ break;
+ }
+ case ClassComponent:
+ {
+ safelyDetachRef(current$$1);
+ var instance = current$$1.stateNode;
+ if (typeof instance.componentWillUnmount === 'function') {
+ safelyCallComponentWillUnmount(current$$1, instance);
+ }
+ return;
+ }
+ case HostComponent:
+ {
+ safelyDetachRef(current$$1);
+ return;
+ }
+ case HostPortal:
+ {
+ // TODO: this is recursive.
+ // We are also not using this parent because
+ // the portal will get pushed immediately.
+ if (supportsMutation) {
+ unmountHostComponents(current$$1);
+ } else if (supportsPersistence) {
+ emptyPortalContainer(current$$1);
+ }
+ return;
+ }
+ }
+}
+
+function commitNestedUnmounts(root) {
+ // While we're inside a removed host node we don't want to call
+ // removeChild on the inner nodes because they're removed by the top
+ // call anyway. We also want to call componentWillUnmount on all
+ // composites before this host node is removed from the tree. Therefore
+ var node = root;
+ while (true) {
+ commitUnmount(node);
+ // Visit children because they may contain more composite or host nodes.
+ // Skip portals because commitUnmount() currently visits them recursively.
+ if (node.child !== null && (
+ // If we use mutation we drill down into portals using commitUnmount above.
+ // If we don't use mutation we drill down into portals here instead.
+ !supportsMutation || node.tag !== HostPortal)) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === root) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === root) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function detachFiber(current$$1) {
+ // Cut off the return pointers to disconnect it from the tree. Ideally, we
+ // should clear the child pointer of the parent alternate to let this
+ // get GC:ed but we don't know which for sure which parent is the current
+ // one so we'll settle for GC:ing the subtree of this child. This child
+ // itself will be GC:ed when the parent updates the next time.
+ current$$1.return = null;
+ current$$1.child = null;
+ current$$1.memoizedState = null;
+ current$$1.updateQueue = null;
+ var alternate = current$$1.alternate;
+ if (alternate !== null) {
+ alternate.return = null;
+ alternate.child = null;
+ alternate.memoizedState = null;
+ alternate.updateQueue = null;
+ }
+}
+
+function emptyPortalContainer(current$$1) {
+ if (!supportsPersistence) {
+ return;
+ }
+
+ var portal = current$$1.stateNode;
+ var containerInfo = portal.containerInfo;
+
+ var emptyChildSet = createContainerChildSet(containerInfo);
+ replaceContainerChildren(containerInfo, emptyChildSet);
+}
+
+function commitContainer(finishedWork) {
+ if (!supportsPersistence) {
+ return;
+ }
+
+ switch (finishedWork.tag) {
+ case ClassComponent:
+ {
+ return;
+ }
+ case HostComponent:
+ {
+ return;
+ }
+ case HostText:
+ {
+ return;
+ }
+ case HostRoot:
+ case HostPortal:
+ {
+ var portalOrRoot = finishedWork.stateNode;
+ var containerInfo = portalOrRoot.containerInfo,
+ _pendingChildren = portalOrRoot.pendingChildren;
+
+ replaceContainerChildren(containerInfo, _pendingChildren);
+ return;
+ }
+ default:
+ {
+ invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.');
+ }
+ }
+}
+
+function getHostParentFiber(fiber) {
+ var parent = fiber.return;
+ while (parent !== null) {
+ if (isHostParent(parent)) {
+ return parent;
+ }
+ parent = parent.return;
+ }
+ invariant(false, 'Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue.');
+}
+
+function isHostParent(fiber) {
+ return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal;
+}
+
+function getHostSibling(fiber) {
+ // We're going to search forward into the tree until we find a sibling host
+ // node. Unfortunately, if multiple insertions are done in a row we have to
+ // search past them. This leads to exponential search for the next sibling.
+ var node = fiber;
+ siblings: while (true) {
+ // If we didn't find anything, let's try the next sibling.
+ while (node.sibling === null) {
+ if (node.return === null || isHostParent(node.return)) {
+ // If we pop out of the root or hit the parent the fiber we are the
+ // last sibling.
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ while (node.tag !== HostComponent && node.tag !== HostText && node.tag !== DehydratedSuspenseComponent) {
+ // If it is not host node and, we might have a host node inside it.
+ // Try to search down until we find one.
+ if (node.effectTag & Placement) {
+ // If we don't have a child, try the siblings instead.
+ continue siblings;
+ }
+ // If we don't have a child, try the siblings instead.
+ // We also skip portals because they are not part of this host tree.
+ if (node.child === null || node.tag === HostPortal) {
+ continue siblings;
+ } else {
+ node.child.return = node;
+ node = node.child;
+ }
+ }
+ // Check if this host node is stable or about to be placed.
+ if (!(node.effectTag & Placement)) {
+ // Found it!
+ return node.stateNode;
+ }
+ }
+}
+
+function commitPlacement(finishedWork) {
+ if (!supportsMutation) {
+ return;
+ }
+
+ // Recursively insert all host nodes into the parent.
+ var parentFiber = getHostParentFiber(finishedWork);
+
+ // Note: these two variables *must* always be updated together.
+ var parent = void 0;
+ var isContainer = void 0;
+
+ switch (parentFiber.tag) {
+ case HostComponent:
+ parent = parentFiber.stateNode;
+ isContainer = false;
+ break;
+ case HostRoot:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ case HostPortal:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ default:
+ invariant(false, 'Invalid host parent fiber. This error is likely caused by a bug in React. Please file an issue.');
+ }
+ if (parentFiber.effectTag & ContentReset) {
+ // Reset the text content of the parent before doing any insertions
+ resetTextContent(parent);
+ // Clear ContentReset from the effect tag
+ parentFiber.effectTag &= ~ContentReset;
+ }
+
+ var before = getHostSibling(finishedWork);
+ // We only have the top Fiber that was inserted but we need to recurse down its
+ // children to find all the terminal nodes.
+ var node = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ if (before) {
+ if (isContainer) {
+ insertInContainerBefore(parent, node.stateNode, before);
+ } else {
+ insertBefore(parent, node.stateNode, before);
+ }
+ } else {
+ if (isContainer) {
+ appendChildToContainer(parent, node.stateNode);
+ } else {
+ appendChild(parent, node.stateNode);
+ }
+ }
+ } else if (node.tag === HostPortal) {
+ // If the insertion itself is a portal, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function unmountHostComponents(current$$1) {
+ // We only have the top Fiber that was deleted but we need to recurse down its
+ var node = current$$1;
+
+ // Each iteration, currentParent is populated with node's host parent if not
+ // currentParentIsValid.
+ var currentParentIsValid = false;
+
+ // Note: these two variables *must* always be updated together.
+ var currentParent = void 0;
+ var currentParentIsContainer = void 0;
+
+ while (true) {
+ if (!currentParentIsValid) {
+ var parent = node.return;
+ findParent: while (true) {
+ !(parent !== null) ? invariant(false, 'Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ switch (parent.tag) {
+ case HostComponent:
+ currentParent = parent.stateNode;
+ currentParentIsContainer = false;
+ break findParent;
+ case HostRoot:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ case HostPortal:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ }
+ parent = parent.return;
+ }
+ currentParentIsValid = true;
+ }
+
+ if (node.tag === HostComponent || node.tag === HostText) {
+ commitNestedUnmounts(node);
+ // After all the children have unmounted, it is now safe to remove the
+ // node from the tree.
+ if (currentParentIsContainer) {
+ removeChildFromContainer(currentParent, node.stateNode);
+ } else {
+ removeChild(currentParent, node.stateNode);
+ }
+ // Don't visit children because we already visited them.
+ } else if (enableSuspenseServerRenderer && node.tag === DehydratedSuspenseComponent) {
+ // Delete the dehydrated suspense boundary and all of its content.
+ if (currentParentIsContainer) {
+ clearSuspenseBoundaryFromContainer(currentParent, node.stateNode);
+ } else {
+ clearSuspenseBoundary(currentParent, node.stateNode);
+ }
+ } else if (node.tag === HostPortal) {
+ if (node.child !== null) {
+ // When we go into a portal, it becomes the parent to remove from.
+ // We will reassign it back when we pop the portal on the way up.
+ currentParent = node.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ // Visit children because portals might contain host components.
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else {
+ commitUnmount(node);
+ // Visit children because we may find more host components below.
+ if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ }
+ if (node === current$$1) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === current$$1) {
+ return;
+ }
+ node = node.return;
+ if (node.tag === HostPortal) {
+ // When we go out of the portal, we need to restore the parent.
+ // Since we don't keep a stack of them, we will search for it.
+ currentParentIsValid = false;
+ }
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function commitDeletion(current$$1) {
+ if (supportsMutation) {
+ // Recursively delete all host nodes from the parent.
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ unmountHostComponents(current$$1);
+ } else {
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ commitNestedUnmounts(current$$1);
+ }
+ detachFiber(current$$1);
+}
+
+function commitWork(current$$1, finishedWork) {
+ if (!supportsMutation) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ // Note: We currently never use MountMutation, but useLayout uses
+ // UnmountMutation.
+ commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
+ return;
+ }
+ }
+
+ commitContainer(finishedWork);
+ return;
+ }
+
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ // Note: We currently never use MountMutation, but useLayout uses
+ // UnmountMutation.
+ commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
+ return;
+ }
+ case ClassComponent:
+ {
+ return;
+ }
+ case HostComponent:
+ {
+ var instance = finishedWork.stateNode;
+ if (instance != null) {
+ // Commit the work prepared earlier.
+ var newProps = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldProps = current$$1 !== null ? current$$1.memoizedProps : newProps;
+ var type = finishedWork.type;
+ // TODO: Type the updateQueue to be specific to host components.
+ var updatePayload = finishedWork.updateQueue;
+ finishedWork.updateQueue = null;
+ if (updatePayload !== null) {
+ commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork);
+ }
+ }
+ return;
+ }
+ case HostText:
+ {
+ !(finishedWork.stateNode !== null) ? invariant(false, 'This should have a text node initialized. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ var textInstance = finishedWork.stateNode;
+ var newText = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldText = current$$1 !== null ? current$$1.memoizedProps : newText;
+ commitTextUpdate(textInstance, oldText, newText);
+ return;
+ }
+ case HostRoot:
+ {
+ return;
+ }
+ case Profiler:
+ {
+ return;
+ }
+ case SuspenseComponent:
+ {
+ var newState = finishedWork.memoizedState;
+
+ var newDidTimeout = void 0;
+ var primaryChildParent = finishedWork;
+ if (newState === null) {
+ newDidTimeout = false;
+ } else {
+ newDidTimeout = true;
+ primaryChildParent = finishedWork.child;
+ if (newState.timedOutAt === NoWork) {
+ // If the children had not already timed out, record the time.
+ // This is used to compute the elapsed time during subsequent
+ // attempts to render the children.
+ newState.timedOutAt = requestCurrentTime();
+ }
+ }
+
+ if (primaryChildParent !== null) {
+ hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
+ }
+
+ // If this boundary just timed out, then it will have a set of thenables.
+ // For each thenable, attach a listener so that when it resolves, React
+ // attempts to re-render the boundary in the primary (pre-timeout) state.
+ var thenables = finishedWork.updateQueue;
+ if (thenables !== null) {
+ finishedWork.updateQueue = null;
+ var retryCache = finishedWork.stateNode;
+ if (retryCache === null) {
+ retryCache = finishedWork.stateNode = new PossiblyWeakSet$1();
+ }
+ thenables.forEach(function (thenable) {
+ // Memoize using the boundary fiber to prevent redundant listeners.
+ var retry = retryTimedOutBoundary.bind(null, finishedWork, thenable);
+ if (enableSchedulerTracing) {
+ retry = unstable_wrap(retry);
+ }
+ if (!retryCache.has(thenable)) {
+ retryCache.add(thenable);
+ thenable.then(retry, retry);
+ }
+ });
+ }
+
+ return;
+ }
+ case IncompleteClassComponent:
+ {
+ return;
+ }
+ default:
+ {
+ invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.');
+ }
+ }
+}
+
+function commitResetTextContent(current$$1) {
+ if (!supportsMutation) {
+ return;
+ }
+ resetTextContent(current$$1.stateNode);
+}
+
+var PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
+var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
+
+function createRootErrorUpdate(fiber, errorInfo, expirationTime) {
+ var update = createUpdate(expirationTime);
+ // Unmount the root by rendering null.
+ update.tag = CaptureUpdate;
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ update.payload = { element: null };
+ var error = errorInfo.value;
+ update.callback = function () {
+ onUncaughtError(error);
+ logError(fiber, errorInfo);
+ };
+ return update;
+}
+
+function createClassErrorUpdate(fiber, errorInfo, expirationTime) {
+ var update = createUpdate(expirationTime);
+ update.tag = CaptureUpdate;
+ var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
+ if (typeof getDerivedStateFromError === 'function') {
+ var error = errorInfo.value;
+ update.payload = function () {
+ return getDerivedStateFromError(error);
+ };
+ }
+
+ var inst = fiber.stateNode;
+ if (inst !== null && typeof inst.componentDidCatch === 'function') {
+ update.callback = function callback() {
+ if (typeof getDerivedStateFromError !== 'function') {
+ // To preserve the preexisting retry behavior of error boundaries,
+ // we keep track of which ones already failed during this batch.
+ // This gets reset before we yield back to the browser.
+ // TODO: Warn in strict mode if getDerivedStateFromError is
+ // not defined.
+ markLegacyErrorBoundaryAsFailed(this);
+ }
+ var error = errorInfo.value;
+ var stack = errorInfo.stack;
+ logError(fiber, errorInfo);
+ this.componentDidCatch(error, {
+ componentStack: stack !== null ? stack : ''
+ });
+ {
+ if (typeof getDerivedStateFromError !== 'function') {
+ // If componentDidCatch is the only error boundary method defined,
+ // then it needs to call setState to recover from errors.
+ // If no state update is scheduled then the boundary will swallow the error.
+ !(fiber.expirationTime === Sync) ? warningWithoutStack$1(false, '%s: Error boundaries should implement getDerivedStateFromError(). ' + 'In that method, return a state update to display an error message or fallback UI.', getComponentName(fiber.type) || 'Unknown') : void 0;
+ }
+ }
+ };
+ }
+ return update;
+}
+
+function attachPingListener(root, renderExpirationTime, thenable) {
+ // Attach a listener to the promise to "ping" the root and retry. But
+ // only if one does not already exist for the current render expiration
+ // time (which acts like a "thread ID" here).
+ var pingCache = root.pingCache;
+ var threadIDs = void 0;
+ if (pingCache === null) {
+ pingCache = root.pingCache = new PossiblyWeakMap();
+ threadIDs = new Set();
+ pingCache.set(thenable, threadIDs);
+ } else {
+ threadIDs = pingCache.get(thenable);
+ if (threadIDs === undefined) {
+ threadIDs = new Set();
+ pingCache.set(thenable, threadIDs);
+ }
+ }
+ if (!threadIDs.has(renderExpirationTime)) {
+ // Memoize using the thread ID to prevent redundant listeners.
+ threadIDs.add(renderExpirationTime);
+ var ping = pingSuspendedRoot.bind(null, root, thenable, renderExpirationTime);
+ if (enableSchedulerTracing) {
+ ping = unstable_wrap(ping);
+ }
+ thenable.then(ping, ping);
+ }
+}
+
+function throwException(root, returnFiber, sourceFiber, value, renderExpirationTime) {
+ // The source fiber did not complete.
+ sourceFiber.effectTag |= Incomplete;
+ // Its effect list is no longer valid.
+ sourceFiber.firstEffect = sourceFiber.lastEffect = null;
+
+ if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
+ // This is a thenable.
+ var thenable = value;
+
+ // Find the earliest timeout threshold of all the placeholders in the
+ // ancestor path. We could avoid this traversal by storing the thresholds on
+ // the stack, but we choose not to because we only hit this path if we're
+ // IO-bound (i.e. if something suspends). Whereas the stack is used even in
+ // the non-IO- bound case.
+ var _workInProgress = returnFiber;
+ var earliestTimeoutMs = -1;
+ var startTimeMs = -1;
+ do {
+ if (_workInProgress.tag === SuspenseComponent) {
+ var current$$1 = _workInProgress.alternate;
+ if (current$$1 !== null) {
+ var currentState = current$$1.memoizedState;
+ if (currentState !== null) {
+ // Reached a boundary that already timed out. Do not search
+ // any further.
+ var timedOutAt = currentState.timedOutAt;
+ startTimeMs = expirationTimeToMs(timedOutAt);
+ // Do not search any further.
+ break;
+ }
+ }
+ var timeoutPropMs = _workInProgress.pendingProps.maxDuration;
+ if (typeof timeoutPropMs === 'number') {
+ if (timeoutPropMs <= 0) {
+ earliestTimeoutMs = 0;
+ } else if (earliestTimeoutMs === -1 || timeoutPropMs < earliestTimeoutMs) {
+ earliestTimeoutMs = timeoutPropMs;
+ }
+ }
+ }
+ // If there is a DehydratedSuspenseComponent we don't have to do anything because
+ // if something suspends inside it, we will simply leave that as dehydrated. It
+ // will never timeout.
+ _workInProgress = _workInProgress.return;
+ } while (_workInProgress !== null);
+
+ // Schedule the nearest Suspense to re-render the timed out view.
+ _workInProgress = returnFiber;
+ do {
+ if (_workInProgress.tag === SuspenseComponent && shouldCaptureSuspense(_workInProgress)) {
+ // Found the nearest boundary.
+
+ // Stash the promise on the boundary fiber. If the boundary times out, we'll
+ var thenables = _workInProgress.updateQueue;
+ if (thenables === null) {
+ var updateQueue = new Set();
+ updateQueue.add(thenable);
+ _workInProgress.updateQueue = updateQueue;
+ } else {
+ thenables.add(thenable);
+ }
+
+ // If the boundary is outside of concurrent mode, we should *not*
+ // suspend the commit. Pretend as if the suspended component rendered
+ // null and keep rendering. In the commit phase, we'll schedule a
+ // subsequent synchronous update to re-render the Suspense.
+ //
+ // Note: It doesn't matter whether the component that suspended was
+ // inside a concurrent mode tree. If the Suspense is outside of it, we
+ // should *not* suspend the commit.
+ if ((_workInProgress.mode & ConcurrentMode) === NoEffect) {
+ _workInProgress.effectTag |= DidCapture;
+
+ // We're going to commit this fiber even though it didn't complete.
+ // But we shouldn't call any lifecycle methods or callbacks. Remove
+ // all lifecycle effect tags.
+ sourceFiber.effectTag &= ~(LifecycleEffectMask | Incomplete);
+
+ if (sourceFiber.tag === ClassComponent) {
+ var currentSourceFiber = sourceFiber.alternate;
+ if (currentSourceFiber === null) {
+ // This is a new mount. Change the tag so it's not mistaken for a
+ // completed class component. For example, we should not call
+ // componentWillUnmount if it is deleted.
+ sourceFiber.tag = IncompleteClassComponent;
+ } else {
+ // When we try rendering again, we should not reuse the current fiber,
+ // since it's known to be in an inconsistent state. Use a force updte to
+ // prevent a bail out.
+ var update = createUpdate(Sync);
+ update.tag = ForceUpdate;
+ enqueueUpdate(sourceFiber, update);
+ }
+ }
+
+ // The source fiber did not complete. Mark it with Sync priority to
+ // indicate that it still has pending work.
+ sourceFiber.expirationTime = Sync;
+
+ // Exit without suspending.
+ return;
+ }
+
+ // Confirmed that the boundary is in a concurrent mode tree. Continue
+ // with the normal suspend path.
+
+ attachPingListener(root, renderExpirationTime, thenable);
+
+ var absoluteTimeoutMs = void 0;
+ if (earliestTimeoutMs === -1) {
+ // If no explicit threshold is given, default to an arbitrarily large
+ // value. The actual size doesn't matter because the threshold for the
+ // whole tree will be clamped to the expiration time.
+ absoluteTimeoutMs = maxSigned31BitInt;
+ } else {
+ if (startTimeMs === -1) {
+ // This suspend happened outside of any already timed-out
+ // placeholders. We don't know exactly when the update was
+ // scheduled, but we can infer an approximate start time from the
+ // expiration time. First, find the earliest uncommitted expiration
+ // time in the tree, including work that is suspended. Then subtract
+ // the offset used to compute an async update's expiration time.
+ // This will cause high priority (interactive) work to expire
+ // earlier than necessary, but we can account for this by adjusting
+ // for the Just Noticeable Difference.
+ var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, renderExpirationTime);
+ var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
+ startTimeMs = earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;
+ }
+ absoluteTimeoutMs = startTimeMs + earliestTimeoutMs;
+ }
+
+ // Mark the earliest timeout in the suspended fiber's ancestor path.
+ // After completing the root, we'll take the largest of all the
+ // suspended fiber's timeouts and use it to compute a timeout for the
+ // whole tree.
+ renderDidSuspend(root, absoluteTimeoutMs, renderExpirationTime);
+
+ _workInProgress.effectTag |= ShouldCapture;
+ _workInProgress.expirationTime = renderExpirationTime;
+ return;
+ } else if (enableSuspenseServerRenderer && _workInProgress.tag === DehydratedSuspenseComponent) {
+ attachPingListener(root, renderExpirationTime, thenable);
+
+ // Since we already have a current fiber, we can eagerly add a retry listener.
+ var retryCache = _workInProgress.memoizedState;
+ if (retryCache === null) {
+ retryCache = _workInProgress.memoizedState = new PossiblyWeakSet();
+ var _current = _workInProgress.alternate;
+ !_current ? invariant(false, 'A dehydrated suspense boundary must commit before trying to render. This is probably a bug in React.') : void 0;
+ _current.memoizedState = retryCache;
+ }
+ // Memoize using the boundary fiber to prevent redundant listeners.
+ if (!retryCache.has(thenable)) {
+ retryCache.add(thenable);
+ var retry = retryTimedOutBoundary.bind(null, _workInProgress, thenable);
+ if (enableSchedulerTracing) {
+ retry = unstable_wrap(retry);
+ }
+ thenable.then(retry, retry);
+ }
+ _workInProgress.effectTag |= ShouldCapture;
+ _workInProgress.expirationTime = renderExpirationTime;
+ return;
+ }
+ // This boundary already captured during this render. Continue to the next
+ // boundary.
+ _workInProgress = _workInProgress.return;
+ } while (_workInProgress !== null);
+ // No boundary was found. Fallthrough to error mode.
+ // TODO: Use invariant so the message is stripped in prod?
+ value = new Error((getComponentName(sourceFiber.type) || 'A React component') + ' suspended while rendering, but no fallback UI was specified.\n' + '\n' + 'Add a <Suspense fallback=...> component higher in the tree to ' + 'provide a loading indicator or placeholder to display.' + getStackByFiberInDevAndProd(sourceFiber));
+ }
+
+ // We didn't find a boundary that could handle this type of exception. Start
+ // over and traverse parent path again, this time treating the exception
+ // as an error.
+ renderDidError();
+ value = createCapturedValue(value, sourceFiber);
+ var workInProgress = returnFiber;
+ do {
+ switch (workInProgress.tag) {
+ case HostRoot:
+ {
+ var _errorInfo = value;
+ workInProgress.effectTag |= ShouldCapture;
+ workInProgress.expirationTime = renderExpirationTime;
+ var _update = createRootErrorUpdate(workInProgress, _errorInfo, renderExpirationTime);
+ enqueueCapturedUpdate(workInProgress, _update);
+ return;
+ }
+ case ClassComponent:
+ // Capture and retry
+ var errorInfo = value;
+ var ctor = workInProgress.type;
+ var instance = workInProgress.stateNode;
+ if ((workInProgress.effectTag & DidCapture) === NoEffect && (typeof ctor.getDerivedStateFromError === 'function' || instance !== null && typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance))) {
+ workInProgress.effectTag |= ShouldCapture;
+ workInProgress.expirationTime = renderExpirationTime;
+ // Schedule the error boundary to re-render using updated state
+ var _update2 = createClassErrorUpdate(workInProgress, errorInfo, renderExpirationTime);
+ enqueueCapturedUpdate(workInProgress, _update2);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ workInProgress = workInProgress.return;
+ } while (workInProgress !== null);
+}
+
+function unwindWork(workInProgress, renderExpirationTime) {
+ switch (workInProgress.tag) {
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
+ }
+ var effectTag = workInProgress.effectTag;
+ if (effectTag & ShouldCapture) {
+ workInProgress.effectTag = effectTag & ~ShouldCapture | DidCapture;
+ return workInProgress;
+ }
+ return null;
+ }
+ case HostRoot:
+ {
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ var _effectTag = workInProgress.effectTag;
+ !((_effectTag & DidCapture) === NoEffect) ? invariant(false, 'The root failed to unmount after an error. This is likely a bug in React. Please file an issue.') : void 0;
+ workInProgress.effectTag = _effectTag & ~ShouldCapture | DidCapture;
+ return workInProgress;
+ }
+ case HostComponent:
+ {
+ // TODO: popHydrationState
+ popHostContext(workInProgress);
+ return null;
+ }
+ case SuspenseComponent:
+ {
+ var _effectTag2 = workInProgress.effectTag;
+ if (_effectTag2 & ShouldCapture) {
+ workInProgress.effectTag = _effectTag2 & ~ShouldCapture | DidCapture;
+ // Captured a suspense effect. Re-render the boundary.
+ return workInProgress;
+ }
+ return null;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ // TODO: popHydrationState
+ var _effectTag3 = workInProgress.effectTag;
+ if (_effectTag3 & ShouldCapture) {
+ workInProgress.effectTag = _effectTag3 & ~ShouldCapture | DidCapture;
+ // Captured a suspense effect. Re-render the boundary.
+ return workInProgress;
+ }
+ }
+ return null;
+ }
+ case HostPortal:
+ popHostContainer(workInProgress);
+ return null;
+ case ContextProvider:
+ popProvider(workInProgress);
+ return null;
+ default:
+ return null;
+ }
+}
+
+function unwindInterruptedWork(interruptedWork) {
+ switch (interruptedWork.tag) {
+ case ClassComponent:
+ {
+ var childContextTypes = interruptedWork.type.childContextTypes;
+ if (childContextTypes !== null && childContextTypes !== undefined) {
+ popContext(interruptedWork);
+ }
+ break;
+ }
+ case HostRoot:
+ {
+ popHostContainer(interruptedWork);
+ popTopLevelContextObject(interruptedWork);
+ break;
+ }
+ case HostComponent:
+ {
+ popHostContext(interruptedWork);
+ break;
+ }
+ case HostPortal:
+ popHostContainer(interruptedWork);
+ break;
+ case ContextProvider:
+ popProvider(interruptedWork);
+ break;
+ default:
+ break;
+ }
+}
+
+var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner;
+
+
+var didWarnAboutStateTransition = void 0;
+var didWarnSetStateChildContext = void 0;
+var warnAboutUpdateOnUnmounted = void 0;
+var warnAboutInvalidUpdates = void 0;
+
+if (enableSchedulerTracing) {
+ // Provide explicit error message when production+profiling bundle of e.g. react-dom
+ // is used with production (non-profiling) bundle of scheduler/tracing
+ !(__interactionsRef != null && __interactionsRef.current != null) ? invariant(false, 'It is not supported to run the profiling version of a renderer (for example, `react-dom/profiling`) without also replacing the `scheduler/tracing` module with `scheduler/tracing-profiling`. Your bundler might have a setting for aliasing both modules. Learn more at http://fb.me/react-profiling') : void 0;
+}
+
+{
+ didWarnAboutStateTransition = false;
+ didWarnSetStateChildContext = false;
+ var didWarnStateUpdateForUnmountedComponent = {};
+
+ warnAboutUpdateOnUnmounted = function (fiber, isClass) {
+ // We show the whole stack but dedupe on the top component's name because
+ // the problematic code almost always lies inside that component.
+ var componentName = getComponentName(fiber.type) || 'ReactComponent';
+ if (didWarnStateUpdateForUnmountedComponent[componentName]) {
+ return;
+ }
+ warningWithoutStack$1(false, "Can't perform a React state update on an unmounted component. This " + 'is a no-op, but it indicates a memory leak in your application. To ' + 'fix, cancel all subscriptions and asynchronous tasks in %s.%s', isClass ? 'the componentWillUnmount method' : 'a useEffect cleanup function', getStackByFiberInDevAndProd(fiber));
+ didWarnStateUpdateForUnmountedComponent[componentName] = true;
+ };
+
+ warnAboutInvalidUpdates = function (instance) {
+ switch (phase) {
+ case 'getChildContext':
+ if (didWarnSetStateChildContext) {
+ return;
+ }
+ warningWithoutStack$1(false, 'setState(...): Cannot call setState() inside getChildContext()');
+ didWarnSetStateChildContext = true;
+ break;
+ case 'render':
+ if (didWarnAboutStateTransition) {
+ return;
+ }
+ warningWithoutStack$1(false, 'Cannot update during an existing state transition (such as within ' + '`render`). Render methods should be a pure function of props and state.');
+ didWarnAboutStateTransition = true;
+ break;
+ }
+ };
+}
+
+// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
+var lastUniqueAsyncExpiration = Sync - 1;
+
+var isWorking = false;
+
+// The next work in progress fiber that we're currently working on.
+var nextUnitOfWork = null;
+var nextRoot = null;
+// The time at which we're currently rendering work.
+var nextRenderExpirationTime = NoWork;
+var nextLatestAbsoluteTimeoutMs = -1;
+var nextRenderDidError = false;
+
+// The next fiber with an effect that we're currently committing.
+var nextEffect = null;
+
+var isCommitting$1 = false;
+var rootWithPendingPassiveEffects = null;
+var passiveEffectCallbackHandle = null;
+var passiveEffectCallback = null;
+
+var legacyErrorBoundariesThatAlreadyFailed = null;
+
+// Used for performance tracking.
+var interruptedBy = null;
+
+var stashedWorkInProgressProperties = void 0;
+var replayUnitOfWork = void 0;
+var mayReplayFailedUnitOfWork = void 0;
+var isReplayingFailedUnitOfWork = void 0;
+var originalReplayError = void 0;
+var rethrowOriginalError = void 0;
+if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
+ stashedWorkInProgressProperties = null;
+ mayReplayFailedUnitOfWork = true;
+ isReplayingFailedUnitOfWork = false;
+ originalReplayError = null;
+ replayUnitOfWork = function (failedUnitOfWork, thrownValue, isYieldy) {
+ if (thrownValue !== null && typeof thrownValue === 'object' && typeof thrownValue.then === 'function') {
+ // Don't replay promises. Treat everything else like an error.
+ // TODO: Need to figure out a different strategy if/when we add
+ // support for catching other types.
+ return;
+ }
+
+ // Restore the original state of the work-in-progress
+ if (stashedWorkInProgressProperties === null) {
+ // This should never happen. Don't throw because this code is DEV-only.
+ warningWithoutStack$1(false, 'Could not replay rendering after an error. This is likely a bug in React. ' + 'Please file an issue.');
+ return;
+ }
+ assignFiberPropertiesInDEV(failedUnitOfWork, stashedWorkInProgressProperties);
+
+ switch (failedUnitOfWork.tag) {
+ case HostRoot:
+ popHostContainer(failedUnitOfWork);
+ popTopLevelContextObject(failedUnitOfWork);
+ break;
+ case HostComponent:
+ popHostContext(failedUnitOfWork);
+ break;
+ case ClassComponent:
+ {
+ var Component = failedUnitOfWork.type;
+ if (isContextProvider(Component)) {
+ popContext(failedUnitOfWork);
+ }
+ break;
+ }
+ case HostPortal:
+ popHostContainer(failedUnitOfWork);
+ break;
+ case ContextProvider:
+ popProvider(failedUnitOfWork);
+ break;
+ }
+ // Replay the begin phase.
+ isReplayingFailedUnitOfWork = true;
+ originalReplayError = thrownValue;
+ invokeGuardedCallback(null, workLoop, null, isYieldy);
+ isReplayingFailedUnitOfWork = false;
+ originalReplayError = null;
+ if (hasCaughtError()) {
+ var replayError = clearCaughtError();
+ if (replayError != null && thrownValue != null) {
+ try {
+ // Reading the expando property is intentionally
+ // inside `try` because it might be a getter or Proxy.
+ if (replayError._suppressLogging) {
+ // Also suppress logging for the original error.
+ thrownValue._suppressLogging = true;
+ }
+ } catch (inner) {
+ // Ignore.
+ }
+ }
+ } else {
+ // If the begin phase did not fail the second time, set this pointer
+ // back to the original value.
+ nextUnitOfWork = failedUnitOfWork;
+ }
+ };
+ rethrowOriginalError = function () {
+ throw originalReplayError;
+ };
+}
+
+function resetStack() {
+ if (nextUnitOfWork !== null) {
+ var interruptedWork = nextUnitOfWork.return;
+ while (interruptedWork !== null) {
+ unwindInterruptedWork(interruptedWork);
+ interruptedWork = interruptedWork.return;
+ }
+ }
+
+ {
+ ReactStrictModeWarnings.discardPendingWarnings();
+ checkThatStackIsEmpty();
+ }
+
+ nextRoot = null;
+ nextRenderExpirationTime = NoWork;
+ nextLatestAbsoluteTimeoutMs = -1;
+ nextRenderDidError = false;
+ nextUnitOfWork = null;
+}
+
+function commitAllHostEffects() {
+ while (nextEffect !== null) {
+ {
+ setCurrentFiber(nextEffect);
+ }
+ recordEffect();
+
+ var effectTag = nextEffect.effectTag;
+
+ if (effectTag & ContentReset) {
+ commitResetTextContent(nextEffect);
+ }
+
+ if (effectTag & Ref) {
+ var current$$1 = nextEffect.alternate;
+ if (current$$1 !== null) {
+ commitDetachRef(current$$1);
+ }
+ }
+
+ // The following switch statement is only concerned about placement,
+ // updates, and deletions. To avoid needing to add a case for every
+ // possible bitmap value, we remove the secondary effects from the
+ // effect tag and switch on that value.
+ var primaryEffectTag = effectTag & (Placement | Update | Deletion);
+ switch (primaryEffectTag) {
+ case Placement:
+ {
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ // TODO: findDOMNode doesn't rely on this any more but isMounted
+ // does and isMounted is deprecated anyway so we should be able
+ // to kill this.
+ nextEffect.effectTag &= ~Placement;
+ break;
+ }
+ case PlacementAndUpdate:
+ {
+ // Placement
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ nextEffect.effectTag &= ~Placement;
+
+ // Update
+ var _current = nextEffect.alternate;
+ commitWork(_current, nextEffect);
+ break;
+ }
+ case Update:
+ {
+ var _current2 = nextEffect.alternate;
+ commitWork(_current2, nextEffect);
+ break;
+ }
+ case Deletion:
+ {
+ commitDeletion(nextEffect);
+ break;
+ }
+ }
+ nextEffect = nextEffect.nextEffect;
+ }
+
+ {
+ resetCurrentFiber();
+ }
+}
+
+function commitBeforeMutationLifecycles() {
+ while (nextEffect !== null) {
+ {
+ setCurrentFiber(nextEffect);
+ }
+
+ var effectTag = nextEffect.effectTag;
+ if (effectTag & Snapshot) {
+ recordEffect();
+ var current$$1 = nextEffect.alternate;
+ commitBeforeMutationLifeCycles(current$$1, nextEffect);
+ }
+
+ nextEffect = nextEffect.nextEffect;
+ }
+
+ {
+ resetCurrentFiber();
+ }
+}
+
+function commitAllLifeCycles(finishedRoot, committedExpirationTime) {
+ {
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
+ ReactStrictModeWarnings.flushLegacyContextWarning();
+
+ if (warnAboutDeprecatedLifecycles) {
+ ReactStrictModeWarnings.flushPendingDeprecationWarnings();
+ }
+ }
+ while (nextEffect !== null) {
+ {
+ setCurrentFiber(nextEffect);
+ }
+ var effectTag = nextEffect.effectTag;
+
+ if (effectTag & (Update | Callback)) {
+ recordEffect();
+ var current$$1 = nextEffect.alternate;
+ commitLifeCycles(finishedRoot, current$$1, nextEffect, committedExpirationTime);
+ }
+
+ if (effectTag & Ref) {
+ recordEffect();
+ commitAttachRef(nextEffect);
+ }
+
+ if (effectTag & Passive) {
+ rootWithPendingPassiveEffects = finishedRoot;
+ }
+
+ nextEffect = nextEffect.nextEffect;
+ }
+ {
+ resetCurrentFiber();
+ }
+}
+
+function commitPassiveEffects(root, firstEffect) {
+ rootWithPendingPassiveEffects = null;
+ passiveEffectCallbackHandle = null;
+ passiveEffectCallback = null;
+
+ // Set this to true to prevent re-entrancy
+ var previousIsRendering = isRendering;
+ isRendering = true;
+
+ var effect = firstEffect;
+ do {
+ {
+ setCurrentFiber(effect);
+ }
+
+ if (effect.effectTag & Passive) {
+ var didError = false;
+ var error = void 0;
+ {
+ invokeGuardedCallback(null, commitPassiveHookEffects, null, effect);
+ if (hasCaughtError()) {
+ didError = true;
+ error = clearCaughtError();
+ }
+ }
+ if (didError) {
+ captureCommitPhaseError(effect, error);
+ }
+ }
+ effect = effect.nextEffect;
+ } while (effect !== null);
+ {
+ resetCurrentFiber();
+ }
+
+ isRendering = previousIsRendering;
+
+ // Check if work was scheduled by one of the effects
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ // Flush any sync work that was scheduled by effects
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+}
+
+function isAlreadyFailedLegacyErrorBoundary(instance) {
+ return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);
+}
+
+function markLegacyErrorBoundaryAsFailed(instance) {
+ if (legacyErrorBoundariesThatAlreadyFailed === null) {
+ legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
+ } else {
+ legacyErrorBoundariesThatAlreadyFailed.add(instance);
+ }
+}
+
+function flushPassiveEffects() {
+ if (passiveEffectCallbackHandle !== null) {
+ cancelPassiveEffects(passiveEffectCallbackHandle);
+ }
+ if (passiveEffectCallback !== null) {
+ // We call the scheduled callback instead of commitPassiveEffects directly
+ // to ensure tracing works correctly.
+ passiveEffectCallback();
+ }
+}
+
+function commitRoot(root, finishedWork) {
+ isWorking = true;
+ isCommitting$1 = true;
+ startCommitTimer();
+
+ !(root.current !== finishedWork) ? invariant(false, 'Cannot commit the same tree as before. This is probably a bug related to the return field. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ var committedExpirationTime = root.pendingCommitExpirationTime;
+ !(committedExpirationTime !== NoWork) ? invariant(false, 'Cannot commit an incomplete root. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ root.pendingCommitExpirationTime = NoWork;
+
+ // Update the pending priority levels to account for the work that we are
+ // about to commit. This needs to happen before calling the lifecycles, since
+ // they may schedule additional updates.
+ var updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
+ var childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
+ var earliestRemainingTimeBeforeCommit = childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit ? childExpirationTimeBeforeCommit : updateExpirationTimeBeforeCommit;
+ markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
+
+ var prevInteractions = null;
+ if (enableSchedulerTracing) {
+ // Restore any pending interactions at this point,
+ // So that cascading work triggered during the render phase will be accounted for.
+ prevInteractions = __interactionsRef.current;
+ __interactionsRef.current = root.memoizedInteractions;
+ }
+
+ // Reset this to null before calling lifecycles
+ ReactCurrentOwner$2.current = null;
+
+ var firstEffect = void 0;
+ if (finishedWork.effectTag > PerformedWork) {
+ // A fiber's effect list consists only of its children, not itself. So if
+ // the root has an effect, we need to add it to the end of the list. The
+ // resulting list is the set that would belong to the root's parent, if
+ // it had one; that is, all the effects in the tree including the root.
+ if (finishedWork.lastEffect !== null) {
+ finishedWork.lastEffect.nextEffect = finishedWork;
+ firstEffect = finishedWork.firstEffect;
+ } else {
+ firstEffect = finishedWork;
+ }
+ } else {
+ // There is no effect on the root.
+ firstEffect = finishedWork.firstEffect;
+ }
+
+ prepareForCommit(root.containerInfo);
+
+ // Invoke instances of getSnapshotBeforeUpdate before mutation.
+ nextEffect = firstEffect;
+ startCommitSnapshotEffectsTimer();
+ while (nextEffect !== null) {
+ var didError = false;
+ var error = void 0;
+ {
+ invokeGuardedCallback(null, commitBeforeMutationLifecycles, null);
+ if (hasCaughtError()) {
+ didError = true;
+ error = clearCaughtError();
+ }
+ }
+ if (didError) {
+ !(nextEffect !== null) ? invariant(false, 'Should have next effect. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ captureCommitPhaseError(nextEffect, error);
+ // Clean-up
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+ stopCommitSnapshotEffectsTimer();
+
+ if (enableProfilerTimer) {
+ // Mark the current commit time to be shared by all Profilers in this batch.
+ // This enables them to be grouped later.
+ recordCommitTime();
+ }
+
+ // Commit all the side-effects within a tree. We'll do this in two passes.
+ // The first pass performs all the host insertions, updates, deletions and
+ // ref unmounts.
+ nextEffect = firstEffect;
+ startCommitHostEffectsTimer();
+ while (nextEffect !== null) {
+ var _didError = false;
+ var _error = void 0;
+ {
+ invokeGuardedCallback(null, commitAllHostEffects, null);
+ if (hasCaughtError()) {
+ _didError = true;
+ _error = clearCaughtError();
+ }
+ }
+ if (_didError) {
+ !(nextEffect !== null) ? invariant(false, 'Should have next effect. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ captureCommitPhaseError(nextEffect, _error);
+ // Clean-up
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+ stopCommitHostEffectsTimer();
+
+ resetAfterCommit(root.containerInfo);
+
+ // The work-in-progress tree is now the current tree. This must come after
+ // the first pass of the commit phase, so that the previous tree is still
+ // current during componentWillUnmount, but before the second pass, so that
+ // the finished work is current during componentDidMount/Update.
+ root.current = finishedWork;
+
+ // In the second pass we'll perform all life-cycles and ref callbacks.
+ // Life-cycles happen as a separate pass so that all placements, updates,
+ // and deletions in the entire tree have already been invoked.
+ // This pass also triggers any renderer-specific initial effects.
+ nextEffect = firstEffect;
+ startCommitLifeCyclesTimer();
+ while (nextEffect !== null) {
+ var _didError2 = false;
+ var _error2 = void 0;
+ {
+ invokeGuardedCallback(null, commitAllLifeCycles, null, root, committedExpirationTime);
+ if (hasCaughtError()) {
+ _didError2 = true;
+ _error2 = clearCaughtError();
+ }
+ }
+ if (_didError2) {
+ !(nextEffect !== null) ? invariant(false, 'Should have next effect. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ captureCommitPhaseError(nextEffect, _error2);
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+
+ if (firstEffect !== null && rootWithPendingPassiveEffects !== null) {
+ // This commit included a passive effect. These do not need to fire until
+ // after the next paint. Schedule an callback to fire them in an async
+ // event. To ensure serial execution, the callback will be flushed early if
+ // we enter rootWithPendingPassiveEffects commit phase before then.
+ var callback = commitPassiveEffects.bind(null, root, firstEffect);
+ if (enableSchedulerTracing) {
+ // TODO: Avoid this extra callback by mutating the tracing ref directly,
+ // like we do at the beginning of commitRoot. I've opted not to do that
+ // here because that code is still in flux.
+ callback = unstable_wrap(callback);
+ }
+ passiveEffectCallbackHandle = unstable_runWithPriority(unstable_NormalPriority, function () {
+ return schedulePassiveEffects(callback);
+ });
+ passiveEffectCallback = callback;
+ }
+
+ isCommitting$1 = false;
+ isWorking = false;
+ stopCommitLifeCyclesTimer();
+ stopCommitTimer();
+ onCommitRoot(finishedWork.stateNode);
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onCommitWork(finishedWork);
+ }
+
+ var updateExpirationTimeAfterCommit = finishedWork.expirationTime;
+ var childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
+ var earliestRemainingTimeAfterCommit = childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit ? childExpirationTimeAfterCommit : updateExpirationTimeAfterCommit;
+ if (earliestRemainingTimeAfterCommit === NoWork) {
+ // If there's no remaining work, we can clear the set of already failed
+ // error boundaries.
+ legacyErrorBoundariesThatAlreadyFailed = null;
+ }
+ onCommit(root, earliestRemainingTimeAfterCommit);
+
+ if (enableSchedulerTracing) {
+ __interactionsRef.current = prevInteractions;
+
+ var subscriber = void 0;
+
+ try {
+ subscriber = __subscriberRef.current;
+ if (subscriber !== null && root.memoizedInteractions.size > 0) {
+ var threadID = computeThreadID(committedExpirationTime, root.interactionThreadID);
+ subscriber.onWorkStopped(root.memoizedInteractions, threadID);
+ }
+ } catch (error) {
+ // It's not safe for commitRoot() to throw.
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ } finally {
+ // Clear completed interactions from the pending Map.
+ // Unless the render was suspended or cascading work was scheduled,
+ // In which case– leave pending interactions until the subsequent render.
+ var pendingInteractionMap = root.pendingInteractionMap;
+ pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
+ // Only decrement the pending interaction count if we're done.
+ // If there's still work at the current priority,
+ // That indicates that we are waiting for suspense data.
+ if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) {
+ pendingInteractionMap.delete(scheduledExpirationTime);
+
+ scheduledInteractions.forEach(function (interaction) {
+ interaction.__count--;
+
+ if (subscriber !== null && interaction.__count === 0) {
+ try {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ } catch (error) {
+ // It's not safe for commitRoot() to throw.
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+ }
+}
+
+function resetChildExpirationTime(workInProgress, renderTime) {
+ if (renderTime !== Never && workInProgress.childExpirationTime === Never) {
+ // The children of this component are hidden. Don't bubble their
+ // expiration times.
+ return;
+ }
+
+ var newChildExpirationTime = NoWork;
+
+ // Bubble up the earliest expiration time.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // We're in profiling mode.
+ // Let's use this same traversal to update the render durations.
+ var actualDuration = workInProgress.actualDuration;
+ var treeBaseDuration = workInProgress.selfBaseDuration;
+
+ // When a fiber is cloned, its actualDuration is reset to 0.
+ // This value will only be updated if work is done on the fiber (i.e. it doesn't bailout).
+ // When work is done, it should bubble to the parent's actualDuration.
+ // If the fiber has not been cloned though, (meaning no work was done),
+ // Then this value will reflect the amount of time spent working on a previous render.
+ // In that case it should not bubble.
+ // We determine whether it was cloned by comparing the child pointer.
+ var shouldBubbleActualDurations = workInProgress.alternate === null || workInProgress.child !== workInProgress.alternate.child;
+
+ var child = workInProgress.child;
+ while (child !== null) {
+ var childUpdateExpirationTime = child.expirationTime;
+ var childChildExpirationTime = child.childExpirationTime;
+ if (childUpdateExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = childUpdateExpirationTime;
+ }
+ if (childChildExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = childChildExpirationTime;
+ }
+ if (shouldBubbleActualDurations) {
+ actualDuration += child.actualDuration;
+ }
+ treeBaseDuration += child.treeBaseDuration;
+ child = child.sibling;
+ }
+ workInProgress.actualDuration = actualDuration;
+ workInProgress.treeBaseDuration = treeBaseDuration;
+ } else {
+ var _child = workInProgress.child;
+ while (_child !== null) {
+ var _childUpdateExpirationTime = _child.expirationTime;
+ var _childChildExpirationTime = _child.childExpirationTime;
+ if (_childUpdateExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = _childUpdateExpirationTime;
+ }
+ if (_childChildExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = _childChildExpirationTime;
+ }
+ _child = _child.sibling;
+ }
+ }
+
+ workInProgress.childExpirationTime = newChildExpirationTime;
+}
+
+function completeUnitOfWork(workInProgress) {
+ // Attempt to complete the current unit of work, then move to the
+ // next sibling. If there are no more siblings, return to the
+ // parent fiber.
+ while (true) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current$$1 = workInProgress.alternate;
+ {
+ setCurrentFiber(workInProgress);
+ }
+
+ var returnFiber = workInProgress.return;
+ var siblingFiber = workInProgress.sibling;
+
+ if ((workInProgress.effectTag & Incomplete) === NoEffect) {
+ if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
+ // Don't replay if it fails during completion phase.
+ mayReplayFailedUnitOfWork = false;
+ }
+ // This fiber completed.
+ // Remember we're completing this unit so we can find a boundary if it fails.
+ nextUnitOfWork = workInProgress;
+ if (enableProfilerTimer) {
+ if (workInProgress.mode & ProfileMode) {
+ startProfilerTimer(workInProgress);
+ }
+ nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
+ if (workInProgress.mode & ProfileMode) {
+ // Update render duration assuming we didn't error.
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
+ }
+ } else {
+ nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
+ }
+ if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
+ // We're out of completion phase so replaying is fine now.
+ mayReplayFailedUnitOfWork = true;
+ }
+ stopWorkTimer(workInProgress);
+ resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
+ {
+ resetCurrentFiber();
+ }
+
+ if (nextUnitOfWork !== null) {
+ // Completing this fiber spawned new work. Work on that next.
+ return nextUnitOfWork;
+ }
+
+ if (returnFiber !== null &&
+ // Do not append effects to parents if a sibling failed to complete
+ (returnFiber.effectTag & Incomplete) === NoEffect) {
+ // Append all the effects of the subtree and this fiber onto the effect
+ // list of the parent. The completion order of the children affects the
+ // side-effect order.
+ if (returnFiber.firstEffect === null) {
+ returnFiber.firstEffect = workInProgress.firstEffect;
+ }
+ if (workInProgress.lastEffect !== null) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
+ }
+ returnFiber.lastEffect = workInProgress.lastEffect;
+ }
+
+ // If this fiber had side-effects, we append it AFTER the children's
+ // side-effects. We can perform certain side-effects earlier if
+ // needed, by doing multiple passes over the effect list. We don't want
+ // to schedule our own side-effect on our own list because if end up
+ // reusing children we'll schedule this effect onto itself since we're
+ // at the end.
+ var effectTag = workInProgress.effectTag;
+ // Skip both NoWork and PerformedWork tags when creating the effect list.
+ // PerformedWork effect is read by React DevTools but shouldn't be committed.
+ if (effectTag > PerformedWork) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress;
+ } else {
+ returnFiber.firstEffect = workInProgress;
+ }
+ returnFiber.lastEffect = workInProgress;
+ }
+ }
+
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress);
+ }
+
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ return siblingFiber;
+ } else if (returnFiber !== null) {
+ // If there's no more work in this returnFiber. Complete the returnFiber.
+ workInProgress = returnFiber;
+ continue;
+ } else {
+ // We've reached the root.
+ return null;
+ }
+ } else {
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // Record the render duration for the fiber that errored.
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
+
+ // Include the time spent working on failed children before continuing.
+ var actualDuration = workInProgress.actualDuration;
+ var child = workInProgress.child;
+ while (child !== null) {
+ actualDuration += child.actualDuration;
+ child = child.sibling;
+ }
+ workInProgress.actualDuration = actualDuration;
+ }
+
+ // This fiber did not complete because something threw. Pop values off
+ // the stack without entering the complete phase. If this is a boundary,
+ // capture values if possible.
+ var next = unwindWork(workInProgress, nextRenderExpirationTime);
+ // Because this fiber did not complete, don't reset its expiration time.
+ if (workInProgress.effectTag & DidCapture) {
+ // Restarting an error boundary
+ stopFailedWorkTimer(workInProgress);
+ } else {
+ stopWorkTimer(workInProgress);
+ }
+
+ {
+ resetCurrentFiber();
+ }
+
+ if (next !== null) {
+ stopWorkTimer(workInProgress);
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress);
+ }
+
+ // If completing this work spawned new work, do that next. We'll come
+ // back here again.
+ // Since we're restarting, remove anything that is not a host effect
+ // from the effect tag.
+ next.effectTag &= HostEffectMask;
+ return next;
+ }
+
+ if (returnFiber !== null) {
+ // Mark the parent fiber as incomplete and clear its effect list.
+ returnFiber.firstEffect = returnFiber.lastEffect = null;
+ returnFiber.effectTag |= Incomplete;
+ }
+
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress);
+ }
+
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ return siblingFiber;
+ } else if (returnFiber !== null) {
+ // If there's no more work in this returnFiber. Complete the returnFiber.
+ workInProgress = returnFiber;
+ continue;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ // Without this explicit null return Flow complains of invalid return type
+ // TODO Remove the above while(true) loop
+ // eslint-disable-next-line no-unreachable
+ return null;
+}
+
+function performUnitOfWork(workInProgress) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current$$1 = workInProgress.alternate;
+
+ // See if beginning this work spawns more work.
+ startWorkTimer(workInProgress);
+ {
+ setCurrentFiber(workInProgress);
+ }
+
+ if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
+ stashedWorkInProgressProperties = assignFiberPropertiesInDEV(stashedWorkInProgressProperties, workInProgress);
+ }
+
+ var next = void 0;
+ if (enableProfilerTimer) {
+ if (workInProgress.mode & ProfileMode) {
+ startProfilerTimer(workInProgress);
+ }
+
+ next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
+ workInProgress.memoizedProps = workInProgress.pendingProps;
+
+ if (workInProgress.mode & ProfileMode) {
+ // Record the render duration assuming we didn't bailout (or error).
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
+ }
+ } else {
+ next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
+ workInProgress.memoizedProps = workInProgress.pendingProps;
+ }
+
+ {
+ resetCurrentFiber();
+ if (isReplayingFailedUnitOfWork) {
+ // Currently replaying a failed unit of work. This should be unreachable,
+ // because the render phase is meant to be idempotent, and it should
+ // have thrown again. Since it didn't, rethrow the original error, so
+ // React's internal stack is not misaligned.
+ rethrowOriginalError();
+ }
+ }
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress);
+ }
+
+ if (next === null) {
+ // If this doesn't spawn new work, complete the current work.
+ next = completeUnitOfWork(workInProgress);
+ }
+
+ ReactCurrentOwner$2.current = null;
+
+ return next;
+}
+
+function workLoop(isYieldy) {
+ if (!isYieldy) {
+ // Flush work without yielding
+ while (nextUnitOfWork !== null) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ }
+ } else {
+ // Flush asynchronous work until there's a higher priority event
+ while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ }
+ }
+}
+
+function renderRoot(root, isYieldy) {
+ !!isWorking ? invariant(false, 'renderRoot was called recursively. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+
+ flushPassiveEffects();
+
+ isWorking = true;
+ var previousDispatcher = ReactCurrentDispatcher.current;
+ ReactCurrentDispatcher.current = ContextOnlyDispatcher;
+
+ var expirationTime = root.nextExpirationTimeToWorkOn;
+
+ // Check if we're starting from a fresh stack, or if we're resuming from
+ // previously yielded work.
+ if (expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null) {
+ // Reset the stack and start working from the root.
+ resetStack();
+ nextRoot = root;
+ nextRenderExpirationTime = expirationTime;
+ nextUnitOfWork = createWorkInProgress(nextRoot.current, null, nextRenderExpirationTime);
+ root.pendingCommitExpirationTime = NoWork;
+
+ if (enableSchedulerTracing) {
+ // Determine which interactions this batch of work currently includes,
+ // So that we can accurately attribute time spent working on it,
+ var interactions = new Set();
+ root.pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
+ if (scheduledExpirationTime >= expirationTime) {
+ scheduledInteractions.forEach(function (interaction) {
+ return interactions.add(interaction);
+ });
+ }
+ });
+
+ // Store the current set of interactions on the FiberRoot for a few reasons:
+ // We can re-use it in hot functions like renderRoot() without having to recalculate it.
+ // We will also use it in commitWork() to pass to any Profiler onRender() hooks.
+ // This also provides DevTools with a way to access it when the onCommitRoot() hook is called.
+ root.memoizedInteractions = interactions;
+
+ if (interactions.size > 0) {
+ var subscriber = __subscriberRef.current;
+ if (subscriber !== null) {
+ var threadID = computeThreadID(expirationTime, root.interactionThreadID);
+ try {
+ subscriber.onWorkStarted(interactions, threadID);
+ } catch (error) {
+ // Work thrown by an interaction tracing subscriber should be rethrown,
+ // But only once it's safe (to avoid leaving the scheduler in an invalid state).
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ var prevInteractions = null;
+ if (enableSchedulerTracing) {
+ // We're about to start new traced work.
+ // Restore pending interactions so cascading work triggered during the render phase will be accounted for.
+ prevInteractions = __interactionsRef.current;
+ __interactionsRef.current = root.memoizedInteractions;
+ }
+
+ var didFatal = false;
+
+ startWorkLoopTimer(nextUnitOfWork);
+
+ do {
+ try {
+ workLoop(isYieldy);
+ } catch (thrownValue) {
+ resetContextDependences();
+ resetHooks();
+
+ // Reset in case completion throws.
+ // This is only used in DEV and when replaying is on.
+ var mayReplay = void 0;
+ if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
+ mayReplay = mayReplayFailedUnitOfWork;
+ mayReplayFailedUnitOfWork = true;
+ }
+
+ if (nextUnitOfWork === null) {
+ // This is a fatal error.
+ didFatal = true;
+ onUncaughtError(thrownValue);
+ } else {
+ if (enableProfilerTimer && nextUnitOfWork.mode & ProfileMode) {
+ // Record the time spent rendering before an error was thrown.
+ // This avoids inaccurate Profiler durations in the case of a suspended render.
+ stopProfilerTimerIfRunningAndRecordDelta(nextUnitOfWork, true);
+ }
+
+ {
+ // Reset global debug state
+ // We assume this is defined in DEV
+ resetCurrentlyProcessingQueue();
+ }
+
+ if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
+ if (mayReplay) {
+ var failedUnitOfWork = nextUnitOfWork;
+ replayUnitOfWork(failedUnitOfWork, thrownValue, isYieldy);
+ }
+ }
+
+ // TODO: we already know this isn't true in some cases.
+ // At least this shows a nicer error message until we figure out the cause.
+ // https://github.com/facebook/react/issues/12449#issuecomment-386727431
+ !(nextUnitOfWork !== null) ? invariant(false, 'Failed to replay rendering after an error. This is likely caused by a bug in React. Please file an issue with a reproducing case to help us find it.') : void 0;
+
+ var sourceFiber = nextUnitOfWork;
+ var returnFiber = sourceFiber.return;
+ if (returnFiber === null) {
+ // This is the root. The root could capture its own errors. However,
+ // we don't know if it errors before or after we pushed the host
+ // context. This information is needed to avoid a stack mismatch.
+ // Because we're not sure, treat this as a fatal error. We could track
+ // which phase it fails in, but doesn't seem worth it. At least
+ // for now.
+ didFatal = true;
+ onUncaughtError(thrownValue);
+ } else {
+ throwException(root, returnFiber, sourceFiber, thrownValue, nextRenderExpirationTime);
+ nextUnitOfWork = completeUnitOfWork(sourceFiber);
+ continue;
+ }
+ }
+ }
+ break;
+ } while (true);
+
+ if (enableSchedulerTracing) {
+ // Traced work is done for now; restore the previous interactions.
+ __interactionsRef.current = prevInteractions;
+ }
+
+ // We're done performing work. Time to clean up.
+ isWorking = false;
+ ReactCurrentDispatcher.current = previousDispatcher;
+ resetContextDependences();
+ resetHooks();
+
+ // Yield back to main thread.
+ if (didFatal) {
+ var _didCompleteRoot = false;
+ stopWorkLoopTimer(interruptedBy, _didCompleteRoot);
+ interruptedBy = null;
+ // There was a fatal error.
+ {
+ resetStackAfterFatalErrorInDev();
+ }
+ // `nextRoot` points to the in-progress root. A non-null value indicates
+ // that we're in the middle of an async render. Set it to null to indicate
+ // there's no more work to be done in the current batch.
+ nextRoot = null;
+ onFatal(root);
+ return;
+ }
+
+ if (nextUnitOfWork !== null) {
+ // There's still remaining async work in this tree, but we ran out of time
+ // in the current frame. Yield back to the renderer. Unless we're
+ // interrupted by a higher priority update, we'll continue later from where
+ // we left off.
+ var _didCompleteRoot2 = false;
+ stopWorkLoopTimer(interruptedBy, _didCompleteRoot2);
+ interruptedBy = null;
+ onYield(root);
+ return;
+ }
+
+ // We completed the whole tree.
+ var didCompleteRoot = true;
+ stopWorkLoopTimer(interruptedBy, didCompleteRoot);
+ var rootWorkInProgress = root.current.alternate;
+ !(rootWorkInProgress !== null) ? invariant(false, 'Finished root should have a work-in-progress. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+
+ // `nextRoot` points to the in-progress root. A non-null value indicates
+ // that we're in the middle of an async render. Set it to null to indicate
+ // there's no more work to be done in the current batch.
+ nextRoot = null;
+ interruptedBy = null;
+
+ if (nextRenderDidError) {
+ // There was an error
+ if (hasLowerPriorityWork(root, expirationTime)) {
+ // There's lower priority work. If so, it may have the effect of fixing
+ // the exception that was just thrown. Exit without committing. This is
+ // similar to a suspend, but without a timeout because we're not waiting
+ // for a promise to resolve. React will restart at the lower
+ // priority level.
+ markSuspendedPriorityLevel(root, expirationTime);
+ var suspendedExpirationTime = expirationTime;
+ var rootExpirationTime = root.expirationTime;
+ onSuspend(root, rootWorkInProgress, suspendedExpirationTime, rootExpirationTime, -1 // Indicates no timeout
+ );
+ return;
+ } else if (
+ // There's no lower priority work, but we're rendering asynchronously.
+ // Synchronously attempt to render the same level one more time. This is
+ // similar to a suspend, but without a timeout because we're not waiting
+ // for a promise to resolve.
+ !root.didError && isYieldy) {
+ root.didError = true;
+ var _suspendedExpirationTime = root.nextExpirationTimeToWorkOn = expirationTime;
+ var _rootExpirationTime = root.expirationTime = Sync;
+ onSuspend(root, rootWorkInProgress, _suspendedExpirationTime, _rootExpirationTime, -1 // Indicates no timeout
+ );
+ return;
+ }
+ }
+
+ if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {
+ // The tree was suspended.
+ var _suspendedExpirationTime2 = expirationTime;
+ markSuspendedPriorityLevel(root, _suspendedExpirationTime2);
+
+ // Find the earliest uncommitted expiration time in the tree, including
+ // work that is suspended. The timeout threshold cannot be longer than
+ // the overall expiration.
+ var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, expirationTime);
+ var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
+ if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {
+ nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;
+ }
+
+ // Subtract the current time from the absolute timeout to get the number
+ // of milliseconds until the timeout. In other words, convert an absolute
+ // timestamp to a relative time. This is the value that is passed
+ // to `setTimeout`.
+ var currentTimeMs = expirationTimeToMs(requestCurrentTime());
+ var msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;
+ msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;
+
+ // TODO: Account for the Just Noticeable Difference
+
+ var _rootExpirationTime2 = root.expirationTime;
+ onSuspend(root, rootWorkInProgress, _suspendedExpirationTime2, _rootExpirationTime2, msUntilTimeout);
+ return;
+ }
+
+ // Ready to commit.
+ onComplete(root, rootWorkInProgress, expirationTime);
+}
+
+function captureCommitPhaseError(sourceFiber, value) {
+ var expirationTime = Sync;
+ var fiber = sourceFiber.return;
+ while (fiber !== null) {
+ switch (fiber.tag) {
+ case ClassComponent:
+ var ctor = fiber.type;
+ var instance = fiber.stateNode;
+ if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {
+ var errorInfo = createCapturedValue(value, sourceFiber);
+ var update = createClassErrorUpdate(fiber, errorInfo, expirationTime);
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ return;
+ }
+ break;
+ case HostRoot:
+ {
+ var _errorInfo = createCapturedValue(value, sourceFiber);
+ var _update = createRootErrorUpdate(fiber, _errorInfo, expirationTime);
+ enqueueUpdate(fiber, _update);
+ scheduleWork(fiber, expirationTime);
+ return;
+ }
+ }
+ fiber = fiber.return;
+ }
+
+ if (sourceFiber.tag === HostRoot) {
+ // Error was thrown at the root. There is no parent, so the root
+ // itself should capture it.
+ var rootFiber = sourceFiber;
+ var _errorInfo2 = createCapturedValue(value, rootFiber);
+ var _update2 = createRootErrorUpdate(rootFiber, _errorInfo2, expirationTime);
+ enqueueUpdate(rootFiber, _update2);
+ scheduleWork(rootFiber, expirationTime);
+ }
+}
+
+function computeThreadID(expirationTime, interactionThreadID) {
+ // Interaction threads are unique per root and expiration time.
+ return expirationTime * 1000 + interactionThreadID;
+}
+
+// Creates a unique async expiration time.
+function computeUniqueAsyncExpiration() {
+ var currentTime = requestCurrentTime();
+ var result = computeAsyncExpiration(currentTime);
+ if (result >= lastUniqueAsyncExpiration) {
+ // Since we assume the current time monotonically increases, we only hit
+ // this branch when computeUniqueAsyncExpiration is fired multiple times
+ // within a 200ms window (or whatever the async bucket size is).
+ result = lastUniqueAsyncExpiration - 1;
+ }
+ lastUniqueAsyncExpiration = result;
+ return lastUniqueAsyncExpiration;
+}
+
+function computeExpirationForFiber(currentTime, fiber) {
+ var priorityLevel = unstable_getCurrentPriorityLevel();
+
+ var expirationTime = void 0;
+ if ((fiber.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, updates are always synchronous.
+ expirationTime = Sync;
+ } else if (isWorking && !isCommitting$1) {
+ // During render phase, updates expire during as the current render.
+ expirationTime = nextRenderExpirationTime;
+ } else {
+ switch (priorityLevel) {
+ case unstable_ImmediatePriority:
+ expirationTime = Sync;
+ break;
+ case unstable_UserBlockingPriority:
+ expirationTime = computeInteractiveExpiration(currentTime);
+ break;
+ case unstable_NormalPriority:
+ // This is a normal, concurrent update
+ expirationTime = computeAsyncExpiration(currentTime);
+ break;
+ case unstable_LowPriority:
+ case unstable_IdlePriority:
+ expirationTime = Never;
+ break;
+ default:
+ invariant(false, 'Unknown priority level. This error is likely caused by a bug in React. Please file an issue.');
+ }
+
+ // If we're in the middle of rendering a tree, do not update at the same
+ // expiration time that is already rendering.
+ if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
+ expirationTime -= 1;
+ }
+ }
+
+ // Keep track of the lowest pending interactive expiration time. This
+ // allows us to synchronously flush all interactive updates
+ // when needed.
+ // TODO: Move this to renderer?
+ if (priorityLevel === unstable_UserBlockingPriority && (lowestPriorityPendingInteractiveExpirationTime === NoWork || expirationTime < lowestPriorityPendingInteractiveExpirationTime)) {
+ lowestPriorityPendingInteractiveExpirationTime = expirationTime;
+ }
+
+ return expirationTime;
+}
+
+function renderDidSuspend(root, absoluteTimeoutMs, suspendedTime) {
+ // Schedule the timeout.
+ if (absoluteTimeoutMs >= 0 && nextLatestAbsoluteTimeoutMs < absoluteTimeoutMs) {
+ nextLatestAbsoluteTimeoutMs = absoluteTimeoutMs;
+ }
+}
+
+function renderDidError() {
+ nextRenderDidError = true;
+}
+
+function pingSuspendedRoot(root, thenable, pingTime) {
+ // A promise that previously suspended React from committing has resolved.
+ // If React is still suspended, try again at the previous level (pingTime).
+
+ var pingCache = root.pingCache;
+ if (pingCache !== null) {
+ // The thenable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ pingCache.delete(thenable);
+ }
+
+ if (nextRoot !== null && nextRenderExpirationTime === pingTime) {
+ // Received a ping at the same priority level at which we're currently
+ // rendering. Restart from the root.
+ nextRoot = null;
+ } else {
+ // Confirm that the root is still suspended at this level. Otherwise exit.
+ if (isPriorityLevelSuspended(root, pingTime)) {
+ // Ping at the original level
+ markPingedPriorityLevel(root, pingTime);
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ }
+ }
+}
+
+function retryTimedOutBoundary(boundaryFiber, thenable) {
+ // The boundary fiber (a Suspense component) previously timed out and was
+ // rendered in its fallback state. One of the promises that suspended it has
+ // resolved, which means at least part of the tree was likely unblocked. Try
+ var retryCache = void 0;
+ if (enableSuspenseServerRenderer) {
+ switch (boundaryFiber.tag) {
+ case SuspenseComponent:
+ retryCache = boundaryFiber.stateNode;
+ break;
+ case DehydratedSuspenseComponent:
+ retryCache = boundaryFiber.memoizedState;
+ break;
+ default:
+ invariant(false, 'Pinged unknown suspense boundary type. This is probably a bug in React.');
+ }
+ } else {
+ retryCache = boundaryFiber.stateNode;
+ }
+ if (retryCache !== null) {
+ // The thenable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ retryCache.delete(thenable);
+ }
+
+ var currentTime = requestCurrentTime();
+ var retryTime = computeExpirationForFiber(currentTime, boundaryFiber);
+ var root = scheduleWorkToRoot(boundaryFiber, retryTime);
+ if (root !== null) {
+ markPendingPriorityLevel(root, retryTime);
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ }
+}
+
+function scheduleWorkToRoot(fiber, expirationTime) {
+ recordScheduleUpdate();
+
+ {
+ if (fiber.tag === ClassComponent) {
+ var instance = fiber.stateNode;
+ warnAboutInvalidUpdates(instance);
+ }
+ }
+
+ // Update the source fiber's expiration time
+ if (fiber.expirationTime < expirationTime) {
+ fiber.expirationTime = expirationTime;
+ }
+ var alternate = fiber.alternate;
+ if (alternate !== null && alternate.expirationTime < expirationTime) {
+ alternate.expirationTime = expirationTime;
+ }
+ // Walk the parent path to the root and update the child expiration time.
+ var node = fiber.return;
+ var root = null;
+ if (node === null && fiber.tag === HostRoot) {
+ root = fiber.stateNode;
+ } else {
+ while (node !== null) {
+ alternate = node.alternate;
+ if (node.childExpirationTime < expirationTime) {
+ node.childExpirationTime = expirationTime;
+ if (alternate !== null && alternate.childExpirationTime < expirationTime) {
+ alternate.childExpirationTime = expirationTime;
+ }
+ } else if (alternate !== null && alternate.childExpirationTime < expirationTime) {
+ alternate.childExpirationTime = expirationTime;
+ }
+ if (node.return === null && node.tag === HostRoot) {
+ root = node.stateNode;
+ break;
+ }
+ node = node.return;
+ }
+ }
+
+ if (enableSchedulerTracing) {
+ if (root !== null) {
+ var interactions = __interactionsRef.current;
+ if (interactions.size > 0) {
+ var pendingInteractionMap = root.pendingInteractionMap;
+ var pendingInteractions = pendingInteractionMap.get(expirationTime);
+ if (pendingInteractions != null) {
+ interactions.forEach(function (interaction) {
+ if (!pendingInteractions.has(interaction)) {
+ // Update the pending async work count for previously unscheduled interaction.
+ interaction.__count++;
+ }
+
+ pendingInteractions.add(interaction);
+ });
+ } else {
+ pendingInteractionMap.set(expirationTime, new Set(interactions));
+
+ // Update the pending async work count for the current interactions.
+ interactions.forEach(function (interaction) {
+ interaction.__count++;
+ });
+ }
+
+ var subscriber = __subscriberRef.current;
+ if (subscriber !== null) {
+ var threadID = computeThreadID(expirationTime, root.interactionThreadID);
+ subscriber.onWorkScheduled(interactions, threadID);
+ }
+ }
+ }
+ }
+ return root;
+}
+
+function warnIfNotCurrentlyBatchingInDev(fiber) {
+ {
+ if (isRendering === false && isBatchingUpdates === false) {
+ warningWithoutStack$1(false, 'An update to %s inside a test was not wrapped in act(...).\n\n' + 'When testing, code that causes React state updates should be wrapped into act(...):\n\n' + 'act(() => {\n' + ' /* fire events that update state */\n' + '});\n' + '/* assert on the output */\n\n' + "This ensures that you're testing the behavior the user would see in the browser." + ' Learn more at https://fb.me/react-wrap-tests-with-act' + '%s', getComponentName(fiber.type), getStackByFiberInDevAndProd(fiber));
+ }
+ }
+}
+
+function scheduleWork(fiber, expirationTime) {
+ var root = scheduleWorkToRoot(fiber, expirationTime);
+ if (root === null) {
+ {
+ switch (fiber.tag) {
+ case ClassComponent:
+ warnAboutUpdateOnUnmounted(fiber, true);
+ break;
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ warnAboutUpdateOnUnmounted(fiber, false);
+ break;
+ }
+ }
+ return;
+ }
+
+ if (!isWorking && nextRenderExpirationTime !== NoWork && expirationTime > nextRenderExpirationTime) {
+ // This is an interruption. (Used for performance tracking.)
+ interruptedBy = fiber;
+ resetStack();
+ }
+ markPendingPriorityLevel(root, expirationTime);
+ if (
+ // If we're in the render phase, we don't need to schedule this root
+ // for an update, because we'll do it before we exit...
+ !isWorking || isCommitting$1 ||
+ // ...unless this is a different root than the one we're rendering.
+ nextRoot !== root) {
+ var rootExpirationTime = root.expirationTime;
+ requestWork(root, rootExpirationTime);
+ }
+ if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
+ // Reset this back to zero so subsequent updates don't throw.
+ nestedUpdateCount = 0;
+ invariant(false, 'Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.');
+ }
+}
+
+function syncUpdates(fn, a, b, c, d) {
+ return unstable_runWithPriority(unstable_ImmediatePriority, function () {
+ return fn(a, b, c, d);
+ });
+}
+
+// TODO: Everything below this is written as if it has been lifted to the
+// renderers. I'll do this in a follow-up.
+
+// Linked-list of roots
+var firstScheduledRoot = null;
+var lastScheduledRoot = null;
+
+var callbackExpirationTime = NoWork;
+var callbackID = void 0;
+var isRendering = false;
+var nextFlushedRoot = null;
+var nextFlushedExpirationTime = NoWork;
+var lowestPriorityPendingInteractiveExpirationTime = NoWork;
+var hasUnhandledError = false;
+var unhandledError = null;
+
+var isBatchingUpdates = false;
+var isUnbatchingUpdates = false;
+
+var completedBatches = null;
+
+var originalStartTimeMs = unstable_now();
+var currentRendererTime = msToExpirationTime(originalStartTimeMs);
+var currentSchedulerTime = currentRendererTime;
+
+// Use these to prevent an infinite loop of nested updates
+var NESTED_UPDATE_LIMIT = 50;
+var nestedUpdateCount = 0;
+var lastCommittedRootDuringThisBatch = null;
+
+function recomputeCurrentRendererTime() {
+ var currentTimeMs = unstable_now() - originalStartTimeMs;
+ currentRendererTime = msToExpirationTime(currentTimeMs);
+}
+
+function scheduleCallbackWithExpirationTime(root, expirationTime) {
+ if (callbackExpirationTime !== NoWork) {
+ // A callback is already scheduled. Check its expiration time (timeout).
+ if (expirationTime < callbackExpirationTime) {
+ // Existing callback has sufficient timeout. Exit.
+ return;
+ } else {
+ if (callbackID !== null) {
+ // Existing callback has insufficient timeout. Cancel and schedule a
+ // new one.
+ unstable_cancelCallback(callbackID);
+ }
+ }
+ // The request callback timer is already running. Don't start a new one.
+ } else {
+ startRequestCallbackTimer();
+ }
+
+ callbackExpirationTime = expirationTime;
+ var currentMs = unstable_now() - originalStartTimeMs;
+ var expirationTimeMs = expirationTimeToMs(expirationTime);
+ var timeout = expirationTimeMs - currentMs;
+ callbackID = unstable_scheduleCallback(performAsyncWork, { timeout: timeout });
+}
+
+// For every call to renderRoot, one of onFatal, onComplete, onSuspend, and
+// onYield is called upon exiting. We use these in lieu of returning a tuple.
+// I've also chosen not to inline them into renderRoot because these will
+// eventually be lifted into the renderer.
+function onFatal(root) {
+ root.finishedWork = null;
+}
+
+function onComplete(root, finishedWork, expirationTime) {
+ root.pendingCommitExpirationTime = expirationTime;
+ root.finishedWork = finishedWork;
+}
+
+function onSuspend(root, finishedWork, suspendedExpirationTime, rootExpirationTime, msUntilTimeout) {
+ root.expirationTime = rootExpirationTime;
+ if (msUntilTimeout === 0 && !shouldYieldToRenderer()) {
+ // Don't wait an additional tick. Commit the tree immediately.
+ root.pendingCommitExpirationTime = suspendedExpirationTime;
+ root.finishedWork = finishedWork;
+ } else if (msUntilTimeout > 0) {
+ // Wait `msUntilTimeout` milliseconds before committing.
+ root.timeoutHandle = scheduleTimeout(onTimeout.bind(null, root, finishedWork, suspendedExpirationTime), msUntilTimeout);
+ }
+}
+
+function onYield(root) {
+ root.finishedWork = null;
+}
+
+function onTimeout(root, finishedWork, suspendedExpirationTime) {
+ // The root timed out. Commit it.
+ root.pendingCommitExpirationTime = suspendedExpirationTime;
+ root.finishedWork = finishedWork;
+ // Read the current time before entering the commit phase. We can be
+ // certain this won't cause tearing related to batching of event updates
+ // because we're at the top of a timer event.
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ flushRoot(root, suspendedExpirationTime);
+}
+
+function onCommit(root, expirationTime) {
+ root.expirationTime = expirationTime;
+ root.finishedWork = null;
+}
+
+function requestCurrentTime() {
+ // requestCurrentTime is called by the scheduler to compute an expiration
+ // time.
+ //
+ // Expiration times are computed by adding to the current time (the start
+ // time). However, if two updates are scheduled within the same event, we
+ // should treat their start times as simultaneous, even if the actual clock
+ // time has advanced between the first and second call.
+
+ // In other words, because expiration times determine how updates are batched,
+ // we want all updates of like priority that occur within the same event to
+ // receive the same expiration time. Otherwise we get tearing.
+ //
+ // We keep track of two separate times: the current "renderer" time and the
+ // current "scheduler" time. The renderer time can be updated whenever; it
+ // only exists to minimize the calls performance.now.
+ //
+ // But the scheduler time can only be updated if there's no pending work, or
+ // if we know for certain that we're not in the middle of an event.
+
+ if (isRendering) {
+ // We're already rendering. Return the most recently read time.
+ return currentSchedulerTime;
+ }
+ // Check if there's pending work.
+ findHighestPriorityRoot();
+ if (nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never) {
+ // If there's no pending work, or if the pending work is offscreen, we can
+ // read the current time without risk of tearing.
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ return currentSchedulerTime;
+ }
+ // There's already pending work. We might be in the middle of a browser
+ // event. If we were to read the current time, it could cause multiple updates
+ // within the same event to receive different expiration times, leading to
+ // tearing. Return the last read time. During the next idle callback, the
+ // time will be updated.
+ return currentSchedulerTime;
+}
+
+// requestWork is called by the scheduler whenever a root receives an update.
+// It's up to the renderer to call renderRoot at some point in the future.
+function requestWork(root, expirationTime) {
+ addRootToSchedule(root, expirationTime);
+ if (isRendering) {
+ // Prevent reentrancy. Remaining work will be scheduled at the end of
+ // the currently rendering batch.
+ return;
+ }
+
+ if (isBatchingUpdates) {
+ // Flush work at the end of the batch.
+ if (isUnbatchingUpdates) {
+ // ...unless we're inside unbatchedUpdates, in which case we should
+ // flush it now.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = Sync;
+ performWorkOnRoot(root, Sync, false);
+ }
+ return;
+ }
+
+ // TODO: Get rid of Sync and use current time?
+ if (expirationTime === Sync) {
+ performSyncWork();
+ } else {
+ scheduleCallbackWithExpirationTime(root, expirationTime);
+ }
+}
+
+function addRootToSchedule(root, expirationTime) {
+ // Add the root to the schedule.
+ // Check if this root is already part of the schedule.
+ if (root.nextScheduledRoot === null) {
+ // This root is not already scheduled. Add it.
+ root.expirationTime = expirationTime;
+ if (lastScheduledRoot === null) {
+ firstScheduledRoot = lastScheduledRoot = root;
+ root.nextScheduledRoot = root;
+ } else {
+ lastScheduledRoot.nextScheduledRoot = root;
+ lastScheduledRoot = root;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ }
+ } else {
+ // This root is already scheduled, but its priority may have increased.
+ var remainingExpirationTime = root.expirationTime;
+ if (expirationTime > remainingExpirationTime) {
+ // Update the priority.
+ root.expirationTime = expirationTime;
+ }
+ }
+}
+
+function findHighestPriorityRoot() {
+ var highestPriorityWork = NoWork;
+ var highestPriorityRoot = null;
+ if (lastScheduledRoot !== null) {
+ var previousScheduledRoot = lastScheduledRoot;
+ var root = firstScheduledRoot;
+ while (root !== null) {
+ var remainingExpirationTime = root.expirationTime;
+ if (remainingExpirationTime === NoWork) {
+ // This root no longer has work. Remove it from the scheduler.
+
+ // TODO: This check is redudant, but Flow is confused by the branch
+ // below where we set lastScheduledRoot to null, even though we break
+ // from the loop right after.
+ !(previousScheduledRoot !== null && lastScheduledRoot !== null) ? invariant(false, 'Should have a previous and last root. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ if (root === root.nextScheduledRoot) {
+ // This is the only root in the list.
+ root.nextScheduledRoot = null;
+ firstScheduledRoot = lastScheduledRoot = null;
+ break;
+ } else if (root === firstScheduledRoot) {
+ // This is the first root in the list.
+ var next = root.nextScheduledRoot;
+ firstScheduledRoot = next;
+ lastScheduledRoot.nextScheduledRoot = next;
+ root.nextScheduledRoot = null;
+ } else if (root === lastScheduledRoot) {
+ // This is the last root in the list.
+ lastScheduledRoot = previousScheduledRoot;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ root.nextScheduledRoot = null;
+ break;
+ } else {
+ previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
+ root.nextScheduledRoot = null;
+ }
+ root = previousScheduledRoot.nextScheduledRoot;
+ } else {
+ if (remainingExpirationTime > highestPriorityWork) {
+ // Update the priority, if it's higher
+ highestPriorityWork = remainingExpirationTime;
+ highestPriorityRoot = root;
+ }
+ if (root === lastScheduledRoot) {
+ break;
+ }
+ if (highestPriorityWork === Sync) {
+ // Sync is highest priority by definition so
+ // we can stop searching.
+ break;
+ }
+ previousScheduledRoot = root;
+ root = root.nextScheduledRoot;
+ }
+ }
+ }
+
+ nextFlushedRoot = highestPriorityRoot;
+ nextFlushedExpirationTime = highestPriorityWork;
+}
+
+// TODO: This wrapper exists because many of the older tests (the ones that use
+// flushDeferredPri) rely on the number of times `shouldYield` is called. We
+// should get rid of it.
+var didYield = false;
+function shouldYieldToRenderer() {
+ if (didYield) {
+ return true;
+ }
+ if (unstable_shouldYield()) {
+ didYield = true;
+ return true;
+ }
+ return false;
+}
+
+function performAsyncWork() {
+ try {
+ if (!shouldYieldToRenderer()) {
+ // The callback timed out. That means at least one update has expired.
+ // Iterate through the root schedule. If they contain expired work, set
+ // the next render expiration time to the current time. This has the effect
+ // of flushing all expired work in a single batch, instead of flushing each
+ // level one at a time.
+ if (firstScheduledRoot !== null) {
+ recomputeCurrentRendererTime();
+ var root = firstScheduledRoot;
+ do {
+ didExpireAtExpirationTime(root, currentRendererTime);
+ // The root schedule is circular, so this is never null.
+ root = root.nextScheduledRoot;
+ } while (root !== firstScheduledRoot);
+ }
+ }
+ performWork(NoWork, true);
+ } finally {
+ didYield = false;
+ }
+}
+
+function performSyncWork() {
+ performWork(Sync, false);
+}
+
+function performWork(minExpirationTime, isYieldy) {
+ // Keep working on roots until there's no more work, or until there's a higher
+ // priority event.
+ findHighestPriorityRoot();
+
+ if (isYieldy) {
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+
+ if (enableUserTimingAPI) {
+ var didExpire = nextFlushedExpirationTime > currentRendererTime;
+ var timeout = expirationTimeToMs(nextFlushedExpirationTime);
+ stopRequestCallbackTimer(didExpire, timeout);
+ }
+
+ while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime && !(didYield && currentRendererTime > nextFlushedExpirationTime)) {
+ performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, currentRendererTime > nextFlushedExpirationTime);
+ findHighestPriorityRoot();
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ }
+ } else {
+ while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime) {
+ performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
+ findHighestPriorityRoot();
+ }
+ }
+
+ // We're done flushing work. Either we ran out of time in this callback,
+ // or there's no more work left with sufficient priority.
+
+ // If we're inside a callback, set this to false since we just completed it.
+ if (isYieldy) {
+ callbackExpirationTime = NoWork;
+ callbackID = null;
+ }
+ // If there's work left over, schedule a new callback.
+ if (nextFlushedExpirationTime !== NoWork) {
+ scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);
+ }
+
+ // Clean-up.
+ finishRendering();
+}
+
+function flushRoot(root, expirationTime) {
+ !!isRendering ? invariant(false, 'work.commit(): Cannot commit while already rendering. This likely means you attempted to commit from inside a lifecycle method.') : void 0;
+ // Perform work on root as if the given expiration time is the current time.
+ // This has the effect of synchronously flushing all work up to and
+ // including the given time.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = expirationTime;
+ performWorkOnRoot(root, expirationTime, false);
+ // Flush any sync work that was scheduled by lifecycles
+ performSyncWork();
+}
+
+function finishRendering() {
+ nestedUpdateCount = 0;
+ lastCommittedRootDuringThisBatch = null;
+
+ if (completedBatches !== null) {
+ var batches = completedBatches;
+ completedBatches = null;
+ for (var i = 0; i < batches.length; i++) {
+ var batch = batches[i];
+ try {
+ batch._onComplete();
+ } catch (error) {
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ }
+
+ if (hasUnhandledError) {
+ var error = unhandledError;
+ unhandledError = null;
+ hasUnhandledError = false;
+ throw error;
+ }
+}
+
+function performWorkOnRoot(root, expirationTime, isYieldy) {
+ !!isRendering ? invariant(false, 'performWorkOnRoot was called recursively. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+
+ isRendering = true;
+
+ // Check if this is async work or sync/expired work.
+ if (!isYieldy) {
+ // Flush work without yielding.
+ // TODO: Non-yieldy work does not necessarily imply expired work. A renderer
+ // may want to perform some work without yielding, but also without
+ // requiring the root to complete (by triggering placeholders).
+
+ var finishedWork = root.finishedWork;
+ if (finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ // If this root previously suspended, clear its existing timeout, since
+ // we're about to try rendering again.
+ var timeoutHandle = root.timeoutHandle;
+ if (timeoutHandle !== noTimeout) {
+ root.timeoutHandle = noTimeout;
+ // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
+ cancelTimeout(timeoutHandle);
+ }
+ renderRoot(root, isYieldy);
+ finishedWork = root.finishedWork;
+ if (finishedWork !== null) {
+ // We've completed the root. Commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ }
+ }
+ } else {
+ // Flush async work.
+ var _finishedWork = root.finishedWork;
+ if (_finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ // If this root previously suspended, clear its existing timeout, since
+ // we're about to try rendering again.
+ var _timeoutHandle = root.timeoutHandle;
+ if (_timeoutHandle !== noTimeout) {
+ root.timeoutHandle = noTimeout;
+ // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
+ cancelTimeout(_timeoutHandle);
+ }
+ renderRoot(root, isYieldy);
+ _finishedWork = root.finishedWork;
+ if (_finishedWork !== null) {
+ // We've completed the root. Check the if we should yield one more time
+ // before committing.
+ if (!shouldYieldToRenderer()) {
+ // Still time left. Commit the root.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ // There's no time left. Mark this root as complete. We'll come
+ // back and commit it later.
+ root.finishedWork = _finishedWork;
+ }
+ }
+ }
+ }
+
+ isRendering = false;
+}
+
+function completeRoot(root, finishedWork, expirationTime) {
+ // Check if there's a batch that matches this expiration time.
+ var firstBatch = root.firstBatch;
+ if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
+ if (completedBatches === null) {
+ completedBatches = [firstBatch];
+ } else {
+ completedBatches.push(firstBatch);
+ }
+ if (firstBatch._defer) {
+ // This root is blocked from committing by a batch. Unschedule it until
+ // we receive another update.
+ root.finishedWork = finishedWork;
+ root.expirationTime = NoWork;
+ return;
+ }
+ }
+
+ // Commit the root.
+ root.finishedWork = null;
+
+ // Check if this is a nested update (a sync update scheduled during the
+ // commit phase).
+ if (root === lastCommittedRootDuringThisBatch) {
+ // If the next root is the same as the previous root, this is a nested
+ // update. To prevent an infinite loop, increment the nested update count.
+ nestedUpdateCount++;
+ } else {
+ // Reset whenever we switch roots.
+ lastCommittedRootDuringThisBatch = root;
+ nestedUpdateCount = 0;
+ }
+ unstable_runWithPriority(unstable_ImmediatePriority, function () {
+ commitRoot(root, finishedWork);
+ });
+}
+
+function onUncaughtError(error) {
+ !(nextFlushedRoot !== null) ? invariant(false, 'Should be working on a root. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ // Unschedule this root so we don't work on it again until there's
+ // another update.
+ nextFlushedRoot.expirationTime = NoWork;
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+}
+
+// TODO: Batching should be implemented at the renderer level, not inside
+// the reconciler.
+function batchedUpdates$1(fn, a) {
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return fn(a);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+ }
+}
+
+// TODO: Batching should be implemented at the renderer level, not inside
+// the reconciler.
+function unbatchedUpdates(fn, a) {
+ if (isBatchingUpdates && !isUnbatchingUpdates) {
+ isUnbatchingUpdates = true;
+ try {
+ return fn(a);
+ } finally {
+ isUnbatchingUpdates = false;
+ }
+ }
+ return fn(a);
+}
+
+// TODO: Batching should be implemented at the renderer level, not within
+// the reconciler.
+function flushSync(fn, a) {
+ !!isRendering ? invariant(false, 'flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering.') : void 0;
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return syncUpdates(fn, a);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ performSyncWork();
+ }
+}
+
+function interactiveUpdates$1(fn, a, b) {
+ // If there are any pending interactive updates, synchronously flush them.
+ // This needs to happen before we read any handlers, because the effect of
+ // the previous event may influence which handlers are called during
+ // this event.
+ if (!isBatchingUpdates && !isRendering && lowestPriorityPendingInteractiveExpirationTime !== NoWork) {
+ // Synchronously flush pending interactive updates.
+ performWork(lowestPriorityPendingInteractiveExpirationTime, false);
+ lowestPriorityPendingInteractiveExpirationTime = NoWork;
+ }
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return unstable_runWithPriority(unstable_UserBlockingPriority, function () {
+ return fn(a, b);
+ });
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+ }
+}
+
+function flushInteractiveUpdates$1() {
+ if (!isRendering && lowestPriorityPendingInteractiveExpirationTime !== NoWork) {
+ // Synchronously flush pending interactive updates.
+ performWork(lowestPriorityPendingInteractiveExpirationTime, false);
+ lowestPriorityPendingInteractiveExpirationTime = NoWork;
+ }
+}
+
+function flushControlled(fn) {
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ syncUpdates(fn);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+ }
+}
+
+// 0 is PROD, 1 is DEV.
+// Might add PROFILE later.
+
+
+var didWarnAboutNestedUpdates = void 0;
+var didWarnAboutFindNodeInStrictMode = void 0;
+
+{
+ didWarnAboutNestedUpdates = false;
+ didWarnAboutFindNodeInStrictMode = {};
+}
+
+function getContextForSubtree(parentComponent) {
+ if (!parentComponent) {
+ return emptyContextObject;
+ }
+
+ var fiber = get(parentComponent);
+ var parentContext = findCurrentUnmaskedContext(fiber);
+
+ if (fiber.tag === ClassComponent) {
+ var Component = fiber.type;
+ if (isContextProvider(Component)) {
+ return processChildContext(fiber, Component, parentContext);
+ }
+ }
+
+ return parentContext;
+}
+
+function scheduleRootUpdate(current$$1, element, expirationTime, callback) {
+ {
+ if (phase === 'render' && current !== null && !didWarnAboutNestedUpdates) {
+ didWarnAboutNestedUpdates = true;
+ warningWithoutStack$1(false, 'Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', getComponentName(current.type) || 'Unknown');
+ }
+ }
+
+ var update = createUpdate(expirationTime);
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ update.payload = { element: element };
+
+ callback = callback === undefined ? null : callback;
+ if (callback !== null) {
+ !(typeof callback === 'function') ? warningWithoutStack$1(false, 'render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback) : void 0;
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(current$$1, update);
+ scheduleWork(current$$1, expirationTime);
+
+ return expirationTime;
+}
+
+function updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback) {
+ // TODO: If this is a nested container, this won't be the root.
+ var current$$1 = container.current;
+
+ {
+ if (ReactFiberInstrumentation_1.debugTool) {
+ if (current$$1.alternate === null) {
+ ReactFiberInstrumentation_1.debugTool.onMountContainer(container);
+ } else if (element === null) {
+ ReactFiberInstrumentation_1.debugTool.onUnmountContainer(container);
+ } else {
+ ReactFiberInstrumentation_1.debugTool.onUpdateContainer(container);
+ }
+ }
+ }
+
+ var context = getContextForSubtree(parentComponent);
+ if (container.context === null) {
+ container.context = context;
+ } else {
+ container.pendingContext = context;
+ }
+
+ return scheduleRootUpdate(current$$1, element, expirationTime, callback);
+}
+
+function findHostInstance(component) {
+ var fiber = get(component);
+ if (fiber === undefined) {
+ if (typeof component.render === 'function') {
+ invariant(false, 'Unable to find node on an unmounted component.');
+ } else {
+ invariant(false, 'Argument appears to not be a ReactComponent. Keys: %s', Object.keys(component));
+ }
+ }
+ var hostFiber = findCurrentHostFiber(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+}
+
+function findHostInstanceWithWarning(component, methodName) {
+ {
+ var fiber = get(component);
+ if (fiber === undefined) {
+ if (typeof component.render === 'function') {
+ invariant(false, 'Unable to find node on an unmounted component.');
+ } else {
+ invariant(false, 'Argument appears to not be a ReactComponent. Keys: %s', Object.keys(component));
+ }
+ }
+ var hostFiber = findCurrentHostFiber(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ if (hostFiber.mode & StrictMode) {
+ var componentName = getComponentName(fiber.type) || 'Component';
+ if (!didWarnAboutFindNodeInStrictMode[componentName]) {
+ didWarnAboutFindNodeInStrictMode[componentName] = true;
+ if (fiber.mode & StrictMode) {
+ warningWithoutStack$1(false, '%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which is inside StrictMode. ' + 'Instead, add a ref directly to the element you want to reference.' + '\n%s' + '\n\nLearn more about using refs safely here:' + '\nhttps://fb.me/react-strict-mode-find-node', methodName, methodName, componentName, getStackByFiberInDevAndProd(hostFiber));
+ } else {
+ warningWithoutStack$1(false, '%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which renders StrictMode children. ' + 'Instead, add a ref directly to the element you want to reference.' + '\n%s' + '\n\nLearn more about using refs safely here:' + '\nhttps://fb.me/react-strict-mode-find-node', methodName, methodName, componentName, getStackByFiberInDevAndProd(hostFiber));
+ }
+ }
+ }
+ return hostFiber.stateNode;
+ }
+ return findHostInstance(component);
+}
+
+function createContainer(containerInfo, isConcurrent, hydrate) {
+ return createFiberRoot(containerInfo, isConcurrent, hydrate);
+}
+
+function updateContainer(element, container, parentComponent, callback) {
+ var current$$1 = container.current;
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, current$$1);
+ return updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback);
+}
+
+function getPublicRootInstance(container) {
+ var containerFiber = container.current;
+ if (!containerFiber.child) {
+ return null;
+ }
+ switch (containerFiber.child.tag) {
+ case HostComponent:
+ return getPublicInstance(containerFiber.child.stateNode);
+ default:
+ return containerFiber.child.stateNode;
+ }
+}
+
+function findHostInstanceWithNoPortals(fiber) {
+ var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+}
+
+var overrideProps = null;
+
+{
+ var copyWithSetImpl = function (obj, path, idx, value) {
+ if (idx >= path.length) {
+ return value;
+ }
+ var key = path[idx];
+ var updated = Array.isArray(obj) ? obj.slice() : _assign({}, obj);
+ // $FlowFixMe number or string is fine here
+ updated[key] = copyWithSetImpl(obj[key], path, idx + 1, value);
+ return updated;
+ };
+
+ var copyWithSet = function (obj, path, value) {
+ return copyWithSetImpl(obj, path, 0, value);
+ };
+
+ // Support DevTools props for function components, forwardRef, memo, host components, etc.
+ overrideProps = function (fiber, path, value) {
+ flushPassiveEffects();
+ fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
+ if (fiber.alternate) {
+ fiber.alternate.pendingProps = fiber.pendingProps;
+ }
+ scheduleWork(fiber, Sync);
+ };
+}
+
+function injectIntoDevTools(devToolsConfig) {
+ var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
+ var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+
+
+ return injectInternals(_assign({}, devToolsConfig, {
+ overrideProps: overrideProps,
+ currentDispatcherRef: ReactCurrentDispatcher,
+ findHostInstanceByFiber: function (fiber) {
+ var hostFiber = findCurrentHostFiber(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+ },
+ findFiberByHostInstance: function (instance) {
+ if (!findFiberByHostInstance) {
+ // Might not be implemented by the renderer.
+ return null;
+ }
+ return findFiberByHostInstance(instance);
+ }
+ }));
+}
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+function createPortal$1(children, containerInfo,
+// TODO: figure out the API for cross-renderer implementation.
+implementation) {
+ var key = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
+
+ return {
+ // This tag allow us to uniquely identify this as a React Portal
+ $$typeof: REACT_PORTAL_TYPE,
+ key: key == null ? null : '' + key,
+ children: children,
+ containerInfo: containerInfo,
+ implementation: implementation
+ };
+}
+
+// TODO: this is special because it gets imported during build.
+
+var ReactVersion = '16.8.6';
+
+// TODO: This type is shared between the reconciler and ReactDOM, but will
+// eventually be lifted out to the renderer.
+
+var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
+
+var topLevelUpdateWarnings = void 0;
+var warnOnInvalidCallback = void 0;
+var didWarnAboutUnstableCreatePortal = false;
+
+{
+ if (typeof Map !== 'function' ||
+ // $FlowIssue Flow incorrectly thinks Map has no prototype
+ Map.prototype == null || typeof Map.prototype.forEach !== 'function' || typeof Set !== 'function' ||
+ // $FlowIssue Flow incorrectly thinks Set has no prototype
+ Set.prototype == null || typeof Set.prototype.clear !== 'function' || typeof Set.prototype.forEach !== 'function') {
+ warningWithoutStack$1(false, 'React depends on Map and Set built-in types. Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills');
+ }
+
+ topLevelUpdateWarnings = function (container) {
+ if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
+ var hostInstance = findHostInstanceWithNoPortals(container._reactRootContainer._internalRoot.current);
+ if (hostInstance) {
+ !(hostInstance.parentNode === container) ? warningWithoutStack$1(false, 'render(...): It looks like the React-rendered content of this ' + 'container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + 'ReactDOM.unmountComponentAtNode to empty a container.') : void 0;
+ }
+ }
+
+ var isRootRenderedBySomeReact = !!container._reactRootContainer;
+ var rootEl = getReactRootElementInContainer(container);
+ var hasNonRootReactChild = !!(rootEl && getInstanceFromNode$1(rootEl));
+
+ !(!hasNonRootReactChild || isRootRenderedBySomeReact) ? warningWithoutStack$1(false, 'render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.') : void 0;
+
+ !(container.nodeType !== ELEMENT_NODE || !container.tagName || container.tagName.toUpperCase() !== 'BODY') ? warningWithoutStack$1(false, 'render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.') : void 0;
+ };
+
+ warnOnInvalidCallback = function (callback, callerName) {
+ !(callback === null || typeof callback === 'function') ? warningWithoutStack$1(false, '%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback) : void 0;
+ };
+}
+
+setRestoreImplementation(restoreControlledState$1);
+
+function ReactBatch(root) {
+ var expirationTime = computeUniqueAsyncExpiration();
+ this._expirationTime = expirationTime;
+ this._root = root;
+ this._next = null;
+ this._callbacks = null;
+ this._didComplete = false;
+ this._hasChildren = false;
+ this._children = null;
+ this._defer = true;
+}
+ReactBatch.prototype.render = function (children) {
+ !this._defer ? invariant(false, 'batch.render: Cannot render a batch that already committed.') : void 0;
+ this._hasChildren = true;
+ this._children = children;
+ var internalRoot = this._root._internalRoot;
+ var expirationTime = this._expirationTime;
+ var work = new ReactWork();
+ updateContainerAtExpirationTime(children, internalRoot, null, expirationTime, work._onCommit);
+ return work;
+};
+ReactBatch.prototype.then = function (onComplete) {
+ if (this._didComplete) {
+ onComplete();
+ return;
+ }
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ callbacks = this._callbacks = [];
+ }
+ callbacks.push(onComplete);
+};
+ReactBatch.prototype.commit = function () {
+ var internalRoot = this._root._internalRoot;
+ var firstBatch = internalRoot.firstBatch;
+ !(this._defer && firstBatch !== null) ? invariant(false, 'batch.commit: Cannot commit a batch multiple times.') : void 0;
+
+ if (!this._hasChildren) {
+ // This batch is empty. Return.
+ this._next = null;
+ this._defer = false;
+ return;
+ }
+
+ var expirationTime = this._expirationTime;
+
+ // Ensure this is the first batch in the list.
+ if (firstBatch !== this) {
+ // This batch is not the earliest batch. We need to move it to the front.
+ // Update its expiration time to be the expiration time of the earliest
+ // batch, so that we can flush it without flushing the other batches.
+ if (this._hasChildren) {
+ expirationTime = this._expirationTime = firstBatch._expirationTime;
+ // Rendering this batch again ensures its children will be the final state
+ // when we flush (updates are processed in insertion order: last
+ // update wins).
+ // TODO: This forces a restart. Should we print a warning?
+ this.render(this._children);
+ }
+
+ // Remove the batch from the list.
+ var previous = null;
+ var batch = firstBatch;
+ while (batch !== this) {
+ previous = batch;
+ batch = batch._next;
+ }
+ !(previous !== null) ? invariant(false, 'batch.commit: Cannot commit a batch multiple times.') : void 0;
+ previous._next = batch._next;
+
+ // Add it to the front.
+ this._next = firstBatch;
+ firstBatch = internalRoot.firstBatch = this;
+ }
+
+ // Synchronously flush all the work up to this batch's expiration time.
+ this._defer = false;
+ flushRoot(internalRoot, expirationTime);
+
+ // Pop the batch from the list.
+ var next = this._next;
+ this._next = null;
+ firstBatch = internalRoot.firstBatch = next;
+
+ // Append the next earliest batch's children to the update queue.
+ if (firstBatch !== null && firstBatch._hasChildren) {
+ firstBatch.render(firstBatch._children);
+ }
+};
+ReactBatch.prototype._onComplete = function () {
+ if (this._didComplete) {
+ return;
+ }
+ this._didComplete = true;
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ return;
+ }
+ // TODO: Error handling.
+ for (var i = 0; i < callbacks.length; i++) {
+ var _callback = callbacks[i];
+ _callback();
+ }
+};
+
+function ReactWork() {
+ this._callbacks = null;
+ this._didCommit = false;
+ // TODO: Avoid need to bind by replacing callbacks in the update queue with
+ // list of Work objects.
+ this._onCommit = this._onCommit.bind(this);
+}
+ReactWork.prototype.then = function (onCommit) {
+ if (this._didCommit) {
+ onCommit();
+ return;
+ }
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ callbacks = this._callbacks = [];
+ }
+ callbacks.push(onCommit);
+};
+ReactWork.prototype._onCommit = function () {
+ if (this._didCommit) {
+ return;
+ }
+ this._didCommit = true;
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ return;
+ }
+ // TODO: Error handling.
+ for (var i = 0; i < callbacks.length; i++) {
+ var _callback2 = callbacks[i];
+ !(typeof _callback2 === 'function') ? invariant(false, 'Invalid argument passed as callback. Expected a function. Instead received: %s', _callback2) : void 0;
+ _callback2();
+ }
+};
+
+function ReactRoot(container, isConcurrent, hydrate) {
+ var root = createContainer(container, isConcurrent, hydrate);
+ this._internalRoot = root;
+}
+ReactRoot.prototype.render = function (children, callback) {
+ var root = this._internalRoot;
+ var work = new ReactWork();
+ callback = callback === undefined ? null : callback;
+ {
+ warnOnInvalidCallback(callback, 'render');
+ }
+ if (callback !== null) {
+ work.then(callback);
+ }
+ updateContainer(children, root, null, work._onCommit);
+ return work;
+};
+ReactRoot.prototype.unmount = function (callback) {
+ var root = this._internalRoot;
+ var work = new ReactWork();
+ callback = callback === undefined ? null : callback;
+ {
+ warnOnInvalidCallback(callback, 'render');
+ }
+ if (callback !== null) {
+ work.then(callback);
+ }
+ updateContainer(null, root, null, work._onCommit);
+ return work;
+};
+ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function (parentComponent, children, callback) {
+ var root = this._internalRoot;
+ var work = new ReactWork();
+ callback = callback === undefined ? null : callback;
+ {
+ warnOnInvalidCallback(callback, 'render');
+ }
+ if (callback !== null) {
+ work.then(callback);
+ }
+ updateContainer(children, root, parentComponent, work._onCommit);
+ return work;
+};
+ReactRoot.prototype.createBatch = function () {
+ var batch = new ReactBatch(this);
+ var expirationTime = batch._expirationTime;
+
+ var internalRoot = this._internalRoot;
+ var firstBatch = internalRoot.firstBatch;
+ if (firstBatch === null) {
+ internalRoot.firstBatch = batch;
+ batch._next = null;
+ } else {
+ // Insert sorted by expiration time then insertion order
+ var insertAfter = null;
+ var insertBefore = firstBatch;
+ while (insertBefore !== null && insertBefore._expirationTime >= expirationTime) {
+ insertAfter = insertBefore;
+ insertBefore = insertBefore._next;
+ }
+ batch._next = insertBefore;
+ if (insertAfter !== null) {
+ insertAfter._next = batch;
+ }
+ }
+
+ return batch;
+};
+
+/**
+ * True if the supplied DOM node is a valid node element.
+ *
+ * @param {?DOMElement} node The candidate DOM node.
+ * @return {boolean} True if the DOM is a valid DOM node.
+ * @internal
+ */
+function isValidContainer(node) {
+ return !!(node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || node.nodeType === COMMENT_NODE && node.nodeValue === ' react-mount-point-unstable '));
+}
+
+function getReactRootElementInContainer(container) {
+ if (!container) {
+ return null;
+ }
+
+ if (container.nodeType === DOCUMENT_NODE) {
+ return container.documentElement;
+ } else {
+ return container.firstChild;
+ }
+}
+
+function shouldHydrateDueToLegacyHeuristic(container) {
+ var rootElement = getReactRootElementInContainer(container);
+ return !!(rootElement && rootElement.nodeType === ELEMENT_NODE && rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME));
+}
+
+setBatchingImplementation(batchedUpdates$1, interactiveUpdates$1, flushInteractiveUpdates$1);
+
+var warnedAboutHydrateAPI = false;
+
+function legacyCreateRootFromDOMContainer(container, forceHydrate) {
+ var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
+ // First clear any existing content.
+ if (!shouldHydrate) {
+ var warned = false;
+ var rootSibling = void 0;
+ while (rootSibling = container.lastChild) {
+ {
+ if (!warned && rootSibling.nodeType === ELEMENT_NODE && rootSibling.hasAttribute(ROOT_ATTRIBUTE_NAME)) {
+ warned = true;
+ warningWithoutStack$1(false, 'render(): Target node has markup rendered by React, but there ' + 'are unrelated nodes as well. This is most commonly caused by ' + 'white-space inserted around server-rendered markup.');
+ }
+ }
+ container.removeChild(rootSibling);
+ }
+ }
+ {
+ if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
+ warnedAboutHydrateAPI = true;
+ lowPriorityWarning$1(false, 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + 'will stop working in React v17. Replace the ReactDOM.render() call ' + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.');
+ }
+ }
+ // Legacy roots are not async by default.
+ var isConcurrent = false;
+ return new ReactRoot(container, isConcurrent, shouldHydrate);
+}
+
+function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
+ {
+ topLevelUpdateWarnings(container);
+ }
+
+ // TODO: Without `any` type, Flow says "Property cannot be accessed on any
+ // member of intersection type." Whyyyyyy.
+ var root = container._reactRootContainer;
+ if (!root) {
+ // Initial mount
+ root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
+ if (typeof callback === 'function') {
+ var originalCallback = callback;
+ callback = function () {
+ var instance = getPublicRootInstance(root._internalRoot);
+ originalCallback.call(instance);
+ };
+ }
+ // Initial mount should not be batched.
+ unbatchedUpdates(function () {
+ if (parentComponent != null) {
+ root.legacy_renderSubtreeIntoContainer(parentComponent, children, callback);
+ } else {
+ root.render(children, callback);
+ }
+ });
+ } else {
+ if (typeof callback === 'function') {
+ var _originalCallback = callback;
+ callback = function () {
+ var instance = getPublicRootInstance(root._internalRoot);
+ _originalCallback.call(instance);
+ };
+ }
+ // Update
+ if (parentComponent != null) {
+ root.legacy_renderSubtreeIntoContainer(parentComponent, children, callback);
+ } else {
+ root.render(children, callback);
+ }
+ }
+ return getPublicRootInstance(root._internalRoot);
+}
+
+function createPortal$$1(children, container) {
+ var key = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
+
+ !isValidContainer(container) ? invariant(false, 'Target container is not a DOM element.') : void 0;
+ // TODO: pass ReactDOM portal implementation as third argument
+ return createPortal$1(children, container, null, key);
+}
+
+var ReactDOM = {
+ createPortal: createPortal$$1,
+
+ findDOMNode: function (componentOrElement) {
+ {
+ var owner = ReactCurrentOwner.current;
+ if (owner !== null && owner.stateNode !== null) {
+ var warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
+ !warnedAboutRefsInRender ? warningWithoutStack$1(false, '%s is accessing findDOMNode inside its render(). ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', getComponentName(owner.type) || 'A component') : void 0;
+ owner.stateNode._warnedAboutRefsInRender = true;
+ }
+ }
+ if (componentOrElement == null) {
+ return null;
+ }
+ if (componentOrElement.nodeType === ELEMENT_NODE) {
+ return componentOrElement;
+ }
+ {
+ return findHostInstanceWithWarning(componentOrElement, 'findDOMNode');
+ }
+ return findHostInstance(componentOrElement);
+ },
+ hydrate: function (element, container, callback) {
+ !isValidContainer(container) ? invariant(false, 'Target container is not a DOM element.') : void 0;
+ {
+ !!container._reactHasBeenPassedToCreateRootDEV ? warningWithoutStack$1(false, 'You are calling ReactDOM.hydrate() on a container that was previously ' + 'passed to ReactDOM.%s(). This is not supported. ' + 'Did you mean to call createRoot(container, {hydrate: true}).render(element)?', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot') : void 0;
+ }
+ // TODO: throw or warn if we couldn't hydrate?
+ return legacyRenderSubtreeIntoContainer(null, element, container, true, callback);
+ },
+ render: function (element, container, callback) {
+ !isValidContainer(container) ? invariant(false, 'Target container is not a DOM element.') : void 0;
+ {
+ !!container._reactHasBeenPassedToCreateRootDEV ? warningWithoutStack$1(false, 'You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOM.%s(). This is not supported. ' + 'Did you mean to call root.render(element)?', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot') : void 0;
+ }
+ return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
+ },
+ unstable_renderSubtreeIntoContainer: function (parentComponent, element, containerNode, callback) {
+ !isValidContainer(containerNode) ? invariant(false, 'Target container is not a DOM element.') : void 0;
+ !(parentComponent != null && has(parentComponent)) ? invariant(false, 'parentComponent must be a valid React Component') : void 0;
+ return legacyRenderSubtreeIntoContainer(parentComponent, element, containerNode, false, callback);
+ },
+ unmountComponentAtNode: function (container) {
+ !isValidContainer(container) ? invariant(false, 'unmountComponentAtNode(...): Target container is not a DOM element.') : void 0;
+
+ {
+ !!container._reactHasBeenPassedToCreateRootDEV ? warningWithoutStack$1(false, 'You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' + 'passed to ReactDOM.%s(). This is not supported. Did you mean to call root.unmount()?', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot') : void 0;
+ }
+
+ if (container._reactRootContainer) {
+ {
+ var rootEl = getReactRootElementInContainer(container);
+ var renderedByDifferentReact = rootEl && !getInstanceFromNode$1(rootEl);
+ !!renderedByDifferentReact ? warningWithoutStack$1(false, "unmountComponentAtNode(): The node you're attempting to unmount " + 'was rendered by another copy of React.') : void 0;
+ }
+
+ // Unmount should not be batched.
+ unbatchedUpdates(function () {
+ legacyRenderSubtreeIntoContainer(null, null, container, false, function () {
+ container._reactRootContainer = null;
+ });
+ });
+ // If you call unmountComponentAtNode twice in quick succession, you'll
+ // get `true` twice. That's probably fine?
+ return true;
+ } else {
+ {
+ var _rootEl = getReactRootElementInContainer(container);
+ var hasNonRootReactChild = !!(_rootEl && getInstanceFromNode$1(_rootEl));
+
+ // Check if the container itself is a React root node.
+ var isContainerReactRoot = container.nodeType === ELEMENT_NODE && isValidContainer(container.parentNode) && !!container.parentNode._reactRootContainer;
+
+ !!hasNonRootReactChild ? warningWithoutStack$1(false, "unmountComponentAtNode(): The node you're attempting to unmount " + 'was rendered by React and is not a top-level container. %s', isContainerReactRoot ? 'You may have accidentally passed in a React root node instead ' + 'of its container.' : 'Instead, have the parent component update its state and ' + 'rerender in order to remove this component.') : void 0;
+ }
+
+ return false;
+ }
+ },
+
+
+ // Temporary alias since we already shipped React 16 RC with it.
+ // TODO: remove in React 17.
+ unstable_createPortal: function () {
+ if (!didWarnAboutUnstableCreatePortal) {
+ didWarnAboutUnstableCreatePortal = true;
+ lowPriorityWarning$1(false, 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' + 'and will be removed in React 17+. Update your code to use ' + 'ReactDOM.createPortal() instead. It has the exact same API, ' + 'but without the "unstable_" prefix.');
+ }
+ return createPortal$$1.apply(undefined, arguments);
+ },
+
+
+ unstable_batchedUpdates: batchedUpdates$1,
+
+ unstable_interactiveUpdates: interactiveUpdates$1,
+
+ flushSync: flushSync,
+
+ unstable_createRoot: createRoot,
+ unstable_flushControlled: flushControlled,
+
+ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
+ // Keep in sync with ReactDOMUnstableNativeDependencies.js
+ // and ReactTestUtils.js. This is an array for better minification.
+ Events: [getInstanceFromNode$1, getNodeFromInstance$1, getFiberCurrentPropsFromNode$1, injection.injectEventPluginsByName, eventNameDispatchConfigs, accumulateTwoPhaseDispatches, accumulateDirectDispatches, enqueueStateRestore, restoreStateIfNeeded, dispatchEvent, runEventsInBatch]
+ }
+};
+
+function createRoot(container, options) {
+ var functionName = enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot';
+ !isValidContainer(container) ? invariant(false, '%s(...): Target container is not a DOM element.', functionName) : void 0;
+ {
+ !!container._reactRootContainer ? warningWithoutStack$1(false, 'You are calling ReactDOM.%s() on a container that was previously ' + 'passed to ReactDOM.render(). This is not supported.', enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot') : void 0;
+ container._reactHasBeenPassedToCreateRootDEV = true;
+ }
+ var hydrate = options != null && options.hydrate === true;
+ return new ReactRoot(container, true, hydrate);
+}
+
+if (enableStableConcurrentModeAPIs) {
+ ReactDOM.createRoot = createRoot;
+ ReactDOM.unstable_createRoot = undefined;
+}
+
+var foundDevTools = injectIntoDevTools({
+ findFiberByHostInstance: getClosestInstanceFromNode,
+ bundleType: 1,
+ version: ReactVersion,
+ rendererPackageName: 'react-dom'
+});
+
+{
+ if (!foundDevTools && canUseDOM && window.top === window.self) {
+ // If we're in Chrome or Firefox, provide a download link if not installed.
+ if (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1 || navigator.userAgent.indexOf('Firefox') > -1) {
+ var protocol = window.location.protocol;
+ // Don't warn in exotic cases like chrome-extension://.
+ if (/^(https?|file):$/.test(protocol)) {
+ console.info('%cDownload the React DevTools ' + 'for a better development experience: ' + 'https://fb.me/react-devtools' + (protocol === 'file:' ? '\nYou might need to use a local HTTP server (instead of file://): ' + 'https://fb.me/react-devtools-faq' : ''), 'font-weight:bold');
+ }
+ }
+ }
+}
+
+
+
+var ReactDOM$2 = ({
+ default: ReactDOM
+});
+
+var ReactDOM$3 = ( ReactDOM$2 && ReactDOM ) || ReactDOM$2;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var reactDom = ReactDOM$3.default || ReactDOM$3;
+
+return reactDom;
+
+})));
diff --git a/devtools/client/shared/vendor/react-dom-factories.js b/devtools/client/shared/vendor/react-dom-factories.js
new file mode 100644
index 0000000000..cb66f07c36
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom-factories.js
@@ -0,0 +1,195 @@
+'use strict';
+
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+(function(f) {
+ if (typeof exports === 'object' && typeof module !== 'undefined') {
+ module.exports = f(require('devtools/client/shared/vendor/react'));
+ /* global define */
+ } else if (typeof define === 'function' && define.amd) {
+ define(['devtools/client/shared/vendor/react'], f);
+ } else {
+ var g;
+ if (typeof window !== 'undefined') {
+ g = window;
+ } else if (typeof global !== 'undefined') {
+ g = global;
+ } else if (typeof self !== 'undefined') {
+ g = self;
+ } else {
+ g = this;
+ }
+
+ if (typeof g.React === 'undefined') {
+ throw Error('React module should be required before ReactDOMFactories');
+ }
+
+ g.ReactDOMFactories = f(g.React);
+ }
+})(function(React) {
+ /**
+ * Create a factory that creates HTML tag elements.
+ */
+ function createDOMFactory(type) {
+ var factory = React.createElement.bind(null, type);
+ // Expose the type on the factory and the prototype so that it can be
+ // easily accessed on elements. E.g. `<Foo />.type === Foo`.
+ // This should not be named `constructor` since this may not be the function
+ // that created the element, and it may not even be a constructor.
+ factory.type = type;
+ return factory;
+ };
+
+ /**
+ * Creates a mapping from supported HTML tags to `ReactDOMComponent` classes.
+ */
+ var ReactDOMFactories = {
+ a: createDOMFactory('a'),
+ abbr: createDOMFactory('abbr'),
+ address: createDOMFactory('address'),
+ area: createDOMFactory('area'),
+ article: createDOMFactory('article'),
+ aside: createDOMFactory('aside'),
+ audio: createDOMFactory('audio'),
+ b: createDOMFactory('b'),
+ base: createDOMFactory('base'),
+ bdi: createDOMFactory('bdi'),
+ bdo: createDOMFactory('bdo'),
+ big: createDOMFactory('big'),
+ blockquote: createDOMFactory('blockquote'),
+ body: createDOMFactory('body'),
+ br: createDOMFactory('br'),
+ button: createDOMFactory('button'),
+ canvas: createDOMFactory('canvas'),
+ caption: createDOMFactory('caption'),
+ cite: createDOMFactory('cite'),
+ code: createDOMFactory('code'),
+ col: createDOMFactory('col'),
+ colgroup: createDOMFactory('colgroup'),
+ data: createDOMFactory('data'),
+ datalist: createDOMFactory('datalist'),
+ dd: createDOMFactory('dd'),
+ del: createDOMFactory('del'),
+ details: createDOMFactory('details'),
+ dfn: createDOMFactory('dfn'),
+ dialog: createDOMFactory('dialog'),
+ div: createDOMFactory('div'),
+ dl: createDOMFactory('dl'),
+ dt: createDOMFactory('dt'),
+ em: createDOMFactory('em'),
+ embed: createDOMFactory('embed'),
+ fieldset: createDOMFactory('fieldset'),
+ figcaption: createDOMFactory('figcaption'),
+ figure: createDOMFactory('figure'),
+ footer: createDOMFactory('footer'),
+ form: createDOMFactory('form'),
+ h1: createDOMFactory('h1'),
+ h2: createDOMFactory('h2'),
+ h3: createDOMFactory('h3'),
+ h4: createDOMFactory('h4'),
+ h5: createDOMFactory('h5'),
+ h6: createDOMFactory('h6'),
+ head: createDOMFactory('head'),
+ header: createDOMFactory('header'),
+ hgroup: createDOMFactory('hgroup'),
+ hr: createDOMFactory('hr'),
+ html: createDOMFactory('html'),
+ i: createDOMFactory('i'),
+ iframe: createDOMFactory('iframe'),
+ img: createDOMFactory('img'),
+ input: createDOMFactory('input'),
+ ins: createDOMFactory('ins'),
+ kbd: createDOMFactory('kbd'),
+ keygen: createDOMFactory('keygen'),
+ label: createDOMFactory('label'),
+ legend: createDOMFactory('legend'),
+ li: createDOMFactory('li'),
+ link: createDOMFactory('link'),
+ main: createDOMFactory('main'),
+ map: createDOMFactory('map'),
+ mark: createDOMFactory('mark'),
+ menu: createDOMFactory('menu'),
+ menuitem: createDOMFactory('menuitem'),
+ meta: createDOMFactory('meta'),
+ meter: createDOMFactory('meter'),
+ nav: createDOMFactory('nav'),
+ noscript: createDOMFactory('noscript'),
+ object: createDOMFactory('object'),
+ ol: createDOMFactory('ol'),
+ optgroup: createDOMFactory('optgroup'),
+ option: createDOMFactory('option'),
+ output: createDOMFactory('output'),
+ p: createDOMFactory('p'),
+ param: createDOMFactory('param'),
+ picture: createDOMFactory('picture'),
+ pre: createDOMFactory('pre'),
+ progress: createDOMFactory('progress'),
+ q: createDOMFactory('q'),
+ rp: createDOMFactory('rp'),
+ rt: createDOMFactory('rt'),
+ ruby: createDOMFactory('ruby'),
+ s: createDOMFactory('s'),
+ samp: createDOMFactory('samp'),
+ script: createDOMFactory('script'),
+ section: createDOMFactory('section'),
+ select: createDOMFactory('select'),
+ small: createDOMFactory('small'),
+ source: createDOMFactory('source'),
+ span: createDOMFactory('span'),
+ strong: createDOMFactory('strong'),
+ style: createDOMFactory('style'),
+ sub: createDOMFactory('sub'),
+ summary: createDOMFactory('summary'),
+ sup: createDOMFactory('sup'),
+ table: createDOMFactory('table'),
+ tbody: createDOMFactory('tbody'),
+ td: createDOMFactory('td'),
+ textarea: createDOMFactory('textarea'),
+ tfoot: createDOMFactory('tfoot'),
+ th: createDOMFactory('th'),
+ thead: createDOMFactory('thead'),
+ time: createDOMFactory('time'),
+ title: createDOMFactory('title'),
+ tr: createDOMFactory('tr'),
+ track: createDOMFactory('track'),
+ u: createDOMFactory('u'),
+ ul: createDOMFactory('ul'),
+ var: createDOMFactory('var'),
+ video: createDOMFactory('video'),
+ wbr: createDOMFactory('wbr'),
+
+ // SVG
+ circle: createDOMFactory('circle'),
+ clipPath: createDOMFactory('clipPath'),
+ defs: createDOMFactory('defs'),
+ ellipse: createDOMFactory('ellipse'),
+ g: createDOMFactory('g'),
+ image: createDOMFactory('image'),
+ line: createDOMFactory('line'),
+ linearGradient: createDOMFactory('linearGradient'),
+ mask: createDOMFactory('mask'),
+ path: createDOMFactory('path'),
+ pattern: createDOMFactory('pattern'),
+ polygon: createDOMFactory('polygon'),
+ polyline: createDOMFactory('polyline'),
+ radialGradient: createDOMFactory('radialGradient'),
+ rect: createDOMFactory('rect'),
+ stop: createDOMFactory('stop'),
+ svg: createDOMFactory('svg'),
+ text: createDOMFactory('text'),
+ tspan: createDOMFactory('tspan'),
+ };
+
+ // due to wrapper and conditionals at the top, this will either become
+ // `module.exports ReactDOMFactories` if that is available,
+ // otherwise it will be defined via `define(['react'], ReactDOMFactories)`
+ // if that is available,
+ // otherwise it will be defined as global variable.
+ return ReactDOMFactories;
+});
+
diff --git a/devtools/client/shared/vendor/react-dom-server-dev.js b/devtools/client/shared/vendor/react-dom-server-dev.js
new file mode 100644
index 0000000000..0aaf146afa
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom-server-dev.js
@@ -0,0 +1,3801 @@
+/** @license React v16.8.6
+ * react-dom-server.browser.development.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require("resource://devtools/client/shared/vendor/react.js")) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react'], factory) :
+ (global.ReactDOMServer = factory(global.React));
+}(this, (function (React) { 'use strict';
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+var validateFormat = function () {};
+
+{
+ validateFormat = function (format) {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ };
+}
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ validateFormat(format);
+
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+
+// TODO: this is special because it gets imported during build.
+
+var ReactVersion = '16.8.6';
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warningWithoutStack = function () {};
+
+{
+ warningWithoutStack = function (condition, format) {
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ args[_key - 2] = arguments[_key];
+ }
+
+ if (format === undefined) {
+ throw new Error('`warningWithoutStack(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (args.length > 8) {
+ // Check before the condition to catch violations early.
+ throw new Error('warningWithoutStack() currently supports at most 8 arguments.');
+ }
+ if (condition) {
+ return;
+ }
+ if (typeof console !== 'undefined') {
+ var argsWithFormat = args.map(function (item) {
+ return '' + item;
+ });
+ argsWithFormat.unshift('Warning: ' + format);
+
+ // We intentionally don't use spread (or .apply) directly because it
+ // breaks IE9: https://github.com/facebook/react/issues/13610
+ Function.prototype.apply.call(console.error, console, argsWithFormat);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+var warningWithoutStack$1 = warningWithoutStack;
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+var Resolved = 1;
+
+
+function refineResolvedLazyComponent(lazyComponent) {
+ return lazyComponent._status === Resolved ? lazyComponent._result : null;
+}
+
+function getWrappedName(outerType, innerType, wrapperName) {
+ var functionName = innerType.displayName || innerType.name || '';
+ return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
+}
+
+function getComponentName(type) {
+ if (type == null) {
+ // Host root, text node or just invalid type.
+ return null;
+ }
+ {
+ if (typeof type.tag === 'number') {
+ warningWithoutStack$1(false, 'Received an unexpected object in getComponentName(). ' + 'This is likely a bug in React. Please file an issue.');
+ }
+ }
+ if (typeof type === 'function') {
+ return type.displayName || type.name || null;
+ }
+ if (typeof type === 'string') {
+ return type;
+ }
+ switch (type) {
+ case REACT_CONCURRENT_MODE_TYPE:
+ return 'ConcurrentMode';
+ case REACT_FRAGMENT_TYPE:
+ return 'Fragment';
+ case REACT_PORTAL_TYPE:
+ return 'Portal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_SUSPENSE_TYPE:
+ return 'Suspense';
+ }
+ if (typeof type === 'object') {
+ switch (type.$$typeof) {
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_FORWARD_REF_TYPE:
+ return getWrappedName(type, type.render, 'ForwardRef');
+ case REACT_MEMO_TYPE:
+ return getComponentName(type.type);
+ case REACT_LAZY_TYPE:
+ {
+ var thenable = type;
+ var resolvedThenable = refineResolvedLazyComponent(thenable);
+ if (resolvedThenable) {
+ return getComponentName(resolvedThenable);
+ }
+ }
+ }
+ }
+ return null;
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var lowPriorityWarning = function () {};
+
+{
+ var printWarning = function (format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.warn(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ lowPriorityWarning = function (condition, format) {
+ if (format === undefined) {
+ throw new Error('`lowPriorityWarning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var lowPriorityWarning$1 = lowPriorityWarning;
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warning = warningWithoutStack$1;
+
+{
+ warning = function (condition, format) {
+ if (condition) {
+ return;
+ }
+ var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+ var stack = ReactDebugCurrentFrame.getStackAddendum();
+ // eslint-disable-next-line react-internal/warning-and-invariant-args
+
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ args[_key - 2] = arguments[_key];
+ }
+
+ warningWithoutStack$1.apply(undefined, [false, format + '%s'].concat(args, [stack]));
+ };
+}
+
+var warning$1 = warning;
+
+var BEFORE_SLASH_RE = /^(.*)[\\\/]/;
+
+var describeComponentFrame = function (name, source, ownerName) {
+ var sourceInfo = '';
+ if (source) {
+ var path = source.fileName;
+ var fileName = path.replace(BEFORE_SLASH_RE, '');
+ {
+ // In DEV, include code for a common special case:
+ // prefer "folder/index.js" instead of just "index.js".
+ if (/^index\./.test(fileName)) {
+ var match = path.match(BEFORE_SLASH_RE);
+ if (match) {
+ var pathBeforeSlash = match[1];
+ if (pathBeforeSlash) {
+ var folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
+ fileName = folderName + '/' + fileName;
+ }
+ }
+ }
+ }
+ sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
+ } else if (ownerName) {
+ sourceInfo = ' (created by ' + ownerName + ')';
+ }
+ return '\n in ' + (name || 'Unknown') + sourceInfo;
+};
+
+// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
+
+
+// In some cases, StrictMode should also double-render lifecycles.
+// This can be confusing for tests though,
+// And it can be bad for performance in production.
+// This feature flag can be used to control the behavior:
+
+
+// To preserve the "Pause on caught exceptions" behavior of the debugger, we
+// replay the begin phase of a failed component inside invokeGuardedCallback.
+
+
+// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
+var warnAboutDeprecatedLifecycles = false;
+
+// Gather advanced timing metrics for Profiler subtrees.
+
+
+// Trace which interactions trigger each commit.
+
+
+// Only used in www builds.
+var enableSuspenseServerRenderer = false; // TODO: true? Here it might just be false.
+
+// Only used in www builds.
+
+
+// Only used in www builds.
+
+
+// React Fire: prevent the value and checked attributes from syncing
+// with their related DOM properties
+
+
+// These APIs will no longer be "unstable" in the upcoming 16.7 release,
+// Control this behavior with a flag to support 16.6 minor releases in the meanwhile.
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+
+var ReactPropTypesSecret$1 = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+
+var ReactPropTypesSecret_1 = ReactPropTypesSecret$1;
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+
+var printWarning$1 = function() {};
+
+{
+ var ReactPropTypesSecret = ReactPropTypesSecret_1;
+ var loggedTypeFailures = {};
+
+ printWarning$1 = function(text) {
+ var message = 'Warning: ' + text;
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+ {
+ for (var typeSpecName in typeSpecs) {
+ if (typeSpecs.hasOwnProperty(typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ if (typeof typeSpecs[typeSpecName] !== 'function') {
+ var err = Error(
+ (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' +
+ 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ }
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
+ } catch (ex) {
+ error = ex;
+ }
+ if (error && !(error instanceof Error)) {
+ printWarning$1(
+ (componentName || 'React class') + ': type specification of ' +
+ location + ' `' + typeSpecName + '` is invalid; the type checker ' +
+ 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' +
+ 'You may have forgotten to pass an argument to the type checker ' +
+ 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
+ 'shape all require an argument).'
+ );
+
+ }
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var stack = getStack ? getStack() : '';
+
+ printWarning$1(
+ 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '')
+ );
+ }
+ }
+ }
+ }
+}
+
+var checkPropTypes_1 = checkPropTypes;
+
+var ReactDebugCurrentFrame$1 = void 0;
+var didWarnAboutInvalidateContextType = void 0;
+{
+ ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame;
+ didWarnAboutInvalidateContextType = new Set();
+}
+
+var emptyObject = {};
+{
+ Object.freeze(emptyObject);
+}
+
+function maskContext(type, context) {
+ var contextTypes = type.contextTypes;
+ if (!contextTypes) {
+ return emptyObject;
+ }
+ var maskedContext = {};
+ for (var contextName in contextTypes) {
+ maskedContext[contextName] = context[contextName];
+ }
+ return maskedContext;
+}
+
+function checkContextTypes(typeSpecs, values, location) {
+ {
+ checkPropTypes_1(typeSpecs, values, location, 'Component', ReactDebugCurrentFrame$1.getCurrentStack);
+ }
+}
+
+function validateContextBounds(context, threadID) {
+ // If we don't have enough slots in this context to store this threadID,
+ // fill it in without leaving any holes to ensure that the VM optimizes
+ // this as non-holey index properties.
+ // (Note: If `react` package is < 16.6, _threadCount is undefined.)
+ for (var i = context._threadCount | 0; i <= threadID; i++) {
+ // We assume that this is the same as the defaultValue which might not be
+ // true if we're rendering inside a secondary renderer but they are
+ // secondary because these use cases are very rare.
+ context[i] = context._currentValue2;
+ context._threadCount = i + 1;
+ }
+}
+
+function processContext(type, context, threadID) {
+ var contextType = type.contextType;
+ {
+ if ('contextType' in type) {
+ var isValid =
+ // Allow null for conditional declaration
+ contextType === null || contextType !== undefined && contextType.$$typeof === REACT_CONTEXT_TYPE && contextType._context === undefined; // Not a <Context.Consumer>
+
+ if (!isValid && !didWarnAboutInvalidateContextType.has(type)) {
+ didWarnAboutInvalidateContextType.add(type);
+
+ var addendum = '';
+ if (contextType === undefined) {
+ addendum = ' However, it is set to undefined. ' + 'This can be caused by a typo or by mixing up named and default imports. ' + 'This can also happen due to a circular dependency, so ' + 'try moving the createContext() call to a separate file.';
+ } else if (typeof contextType !== 'object') {
+ addendum = ' However, it is set to a ' + typeof contextType + '.';
+ } else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
+ addendum = ' Did you accidentally pass the Context.Provider instead?';
+ } else if (contextType._context !== undefined) {
+ // <Context.Consumer>
+ addendum = ' Did you accidentally pass the Context.Consumer instead?';
+ } else {
+ addendum = ' However, it is set to an object with keys {' + Object.keys(contextType).join(', ') + '}.';
+ }
+ warningWithoutStack$1(false, '%s defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext().%s', getComponentName(type) || 'Component', addendum);
+ }
+ }
+ }
+ if (typeof contextType === 'object' && contextType !== null) {
+ validateContextBounds(contextType, threadID);
+ return contextType[threadID];
+ } else {
+ var maskedContext = maskContext(type, context);
+ {
+ if (type.contextTypes) {
+ checkContextTypes(type.contextTypes, maskedContext, 'context');
+ }
+ }
+ return maskedContext;
+ }
+}
+
+// Allocates a new index for each request. Tries to stay as compact as possible so that these
+// indices can be used to reference a tightly packaged array. As opposed to being used in a Map.
+// The first allocated index is 1.
+
+var nextAvailableThreadIDs = new Uint16Array(16);
+for (var i = 0; i < 15; i++) {
+ nextAvailableThreadIDs[i] = i + 1;
+}
+nextAvailableThreadIDs[15] = 0;
+
+function growThreadCountAndReturnNextAvailable() {
+ var oldArray = nextAvailableThreadIDs;
+ var oldSize = oldArray.length;
+ var newSize = oldSize * 2;
+ !(newSize <= 0x10000) ? invariant(false, 'Maximum number of concurrent React renderers exceeded. This can happen if you are not properly destroying the Readable provided by React. Ensure that you call .destroy() on it if you no longer want to read from it, and did not read to the end. If you use .pipe() this should be automatic.') : void 0;
+ var newArray = new Uint16Array(newSize);
+ newArray.set(oldArray);
+ nextAvailableThreadIDs = newArray;
+ nextAvailableThreadIDs[0] = oldSize + 1;
+ for (var _i = oldSize; _i < newSize - 1; _i++) {
+ nextAvailableThreadIDs[_i] = _i + 1;
+ }
+ nextAvailableThreadIDs[newSize - 1] = 0;
+ return oldSize;
+}
+
+function allocThreadID() {
+ var nextID = nextAvailableThreadIDs[0];
+ if (nextID === 0) {
+ return growThreadCountAndReturnNextAvailable();
+ }
+ nextAvailableThreadIDs[0] = nextAvailableThreadIDs[nextID];
+ return nextID;
+}
+
+function freeThreadID(id) {
+ nextAvailableThreadIDs[id] = nextAvailableThreadIDs[0];
+ nextAvailableThreadIDs[0] = id;
+}
+
+// A reserved attribute.
+// It is handled by React separately and shouldn't be written to the DOM.
+var RESERVED = 0;
+
+// A simple string attribute.
+// Attributes that aren't in the whitelist are presumed to have this type.
+var STRING = 1;
+
+// A string attribute that accepts booleans in React. In HTML, these are called
+// "enumerated" attributes with "true" and "false" as possible values.
+// When true, it should be set to a "true" string.
+// When false, it should be set to a "false" string.
+var BOOLEANISH_STRING = 2;
+
+// A real boolean attribute.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+var BOOLEAN = 3;
+
+// An attribute that can be used as a flag as well as with a value.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+// For any other value, should be present with that value.
+var OVERLOADED_BOOLEAN = 4;
+
+// An attribute that must be numeric or parse as a numeric.
+// When falsy, it should be removed.
+var NUMERIC = 5;
+
+// An attribute that must be positive numeric or parse as a positive numeric.
+// When falsy, it should be removed.
+var POSITIVE_NUMERIC = 6;
+
+/* eslint-disable max-len */
+var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
+/* eslint-enable max-len */
+var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
+
+
+var ROOT_ATTRIBUTE_NAME = 'data-reactroot';
+var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+var illegalAttributeNameCache = {};
+var validatedAttributeNameCache = {};
+
+function isAttributeNameSafe(attributeName) {
+ if (hasOwnProperty$1.call(validatedAttributeNameCache, attributeName)) {
+ return true;
+ }
+ if (hasOwnProperty$1.call(illegalAttributeNameCache, attributeName)) {
+ return false;
+ }
+ if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
+ validatedAttributeNameCache[attributeName] = true;
+ return true;
+ }
+ illegalAttributeNameCache[attributeName] = true;
+ {
+ warning$1(false, 'Invalid attribute name: `%s`', attributeName);
+ }
+ return false;
+}
+
+function shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null) {
+ return propertyInfo.type === RESERVED;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) {
+ return true;
+ }
+ return false;
+}
+
+function shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null && propertyInfo.type === RESERVED) {
+ return false;
+ }
+ switch (typeof value) {
+ case 'function':
+ // $FlowIssue symbol is perfectly valid here
+ case 'symbol':
+ // eslint-disable-line
+ return true;
+ case 'boolean':
+ {
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ return !propertyInfo.acceptsBooleans;
+ } else {
+ var prefix = name.toLowerCase().slice(0, 5);
+ return prefix !== 'data-' && prefix !== 'aria-';
+ }
+ }
+ default:
+ return false;
+ }
+}
+
+function shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag) {
+ if (value === null || typeof value === 'undefined') {
+ return true;
+ }
+ if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag)) {
+ return true;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ switch (propertyInfo.type) {
+ case BOOLEAN:
+ return !value;
+ case OVERLOADED_BOOLEAN:
+ return value === false;
+ case NUMERIC:
+ return isNaN(value);
+ case POSITIVE_NUMERIC:
+ return isNaN(value) || value < 1;
+ }
+ }
+ return false;
+}
+
+function getPropertyInfo(name) {
+ return properties.hasOwnProperty(name) ? properties[name] : null;
+}
+
+function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace) {
+ this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN;
+ this.attributeName = attributeName;
+ this.attributeNamespace = attributeNamespace;
+ this.mustUseProperty = mustUseProperty;
+ this.propertyName = name;
+ this.type = type;
+}
+
+// When adding attributes to this list, be sure to also add them to
+// the `possibleStandardNames` module to ensure casing and incorrect
+// name warnings.
+var properties = {};
+
+// These props are reserved by React. They shouldn't be written to the DOM.
+['children', 'dangerouslySetInnerHTML',
+// TODO: This prevents the assignment of defaultValue to regular
+// elements (not just inputs). Now that ReactDOMInput assigns to the
+// defaultValue property -- do we need this?
+'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, RESERVED, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// A few React string attributes have a different name.
+// This is a mapping from React prop names to the attribute names.
+[['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv']].forEach(function (_ref) {
+ var name = _ref[0],
+ attributeName = _ref[1];
+
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" HTML attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" SVG attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+// Since these are SVG attributes, their attribute names are case-sensitive.
+['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML boolean attributes.
+['allowFullScreen', 'async',
+// Note: there is a special case that prevents it from being written to the DOM
+// on the client side because the browsers are inconsistent. Instead we call focus().
+'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless',
+// Microdata
+'itemScope'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are the few React props that we set as DOM properties
+// rather than attributes. These are all booleans.
+['checked',
+// Note: `option.selected` is not updated if `select.multiple` is
+// disabled with `removeAttribute`. We have special logic for handling this.
+'multiple', 'muted', 'selected'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, true, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that are "overloaded booleans": they behave like
+// booleans, but can also accept a string value.
+['capture', 'download'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, OVERLOADED_BOOLEAN, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be positive numbers.
+['cols', 'rows', 'size', 'span'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, POSITIVE_NUMERIC, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be numbers.
+['rowSpan', 'start'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, NUMERIC, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+var CAMELIZE = /[\-\:]([a-z])/g;
+var capitalize = function (token) {
+ return token[1].toUpperCase();
+};
+
+// This is a list of all SVG attributes that need special casing, namespacing,
+// or boolean value assignment. Regular attributes that just accept strings
+// and have the same names are omitted, just like in the HTML whitelist.
+// Some of these attributes can be hard to find. This list was created by
+// scrapping the MDN documentation.
+['accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'xmlns:xlink', 'x-height'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, null);
+} // attributeNamespace
+);
+
+// String SVG attributes with the xlink namespace.
+['xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/1999/xlink');
+});
+
+// String SVG attributes with the xml namespace.
+['xml:base', 'xml:lang', 'xml:space'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/XML/1998/namespace');
+});
+
+// These attribute exists both in HTML and SVG.
+// The attribute name is case-sensitive in SVG so we can't just use
+// the React name like we do for attributes that exist only in HTML.
+['tabIndex', 'crossOrigin'].forEach(function (attributeName) {
+ properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty
+ attributeName.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// code copied and modified from escape-html
+/**
+ * Module variables.
+ * @private
+ */
+
+var matchHtmlRegExp = /["'&<>]/;
+
+/**
+ * Escapes special characters and HTML entities in a given html string.
+ *
+ * @param {string} string HTML string to escape for later insertion
+ * @return {string}
+ * @public
+ */
+
+function escapeHtml(string) {
+ var str = '' + string;
+ var match = matchHtmlRegExp.exec(str);
+
+ if (!match) {
+ return str;
+ }
+
+ var escape = void 0;
+ var html = '';
+ var index = void 0;
+ var lastIndex = 0;
+
+ for (index = match.index; index < str.length; index++) {
+ switch (str.charCodeAt(index)) {
+ case 34:
+ // "
+ escape = '&quot;';
+ break;
+ case 38:
+ // &
+ escape = '&amp;';
+ break;
+ case 39:
+ // '
+ escape = '&#x27;'; // modified from escape-html; used to be '&#39'
+ break;
+ case 60:
+ // <
+ escape = '&lt;';
+ break;
+ case 62:
+ // >
+ escape = '&gt;';
+ break;
+ default:
+ continue;
+ }
+
+ if (lastIndex !== index) {
+ html += str.substring(lastIndex, index);
+ }
+
+ lastIndex = index + 1;
+ html += escape;
+ }
+
+ return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
+}
+// end code copied and modified from escape-html
+
+/**
+ * Escapes text to prevent scripting attacks.
+ *
+ * @param {*} text Text value to escape.
+ * @return {string} An escaped string.
+ */
+function escapeTextForBrowser(text) {
+ if (typeof text === 'boolean' || typeof text === 'number') {
+ // this shortcircuit helps perf for types that we know will never have
+ // special characters, especially given that this function is used often
+ // for numeric dom ids.
+ return '' + text;
+ }
+ return escapeHtml(text);
+}
+
+/**
+ * Escapes attribute value to prevent scripting attacks.
+ *
+ * @param {*} value Value to escape.
+ * @return {string} An escaped string.
+ */
+function quoteAttributeValueForBrowser(value) {
+ return '"' + escapeTextForBrowser(value) + '"';
+}
+
+/**
+ * Operations for dealing with DOM properties.
+ */
+
+/**
+ * Creates markup for the ID property.
+ *
+ * @param {string} id Unescaped ID.
+ * @return {string} Markup string.
+ */
+
+
+function createMarkupForRoot() {
+ return ROOT_ATTRIBUTE_NAME + '=""';
+}
+
+/**
+ * Creates markup for a property.
+ *
+ * @param {string} name
+ * @param {*} value
+ * @return {?string} Markup string, or null if the property was invalid.
+ */
+function createMarkupForProperty(name, value) {
+ var propertyInfo = getPropertyInfo(name);
+ if (name !== 'style' && shouldIgnoreAttribute(name, propertyInfo, false)) {
+ return '';
+ }
+ if (shouldRemoveAttribute(name, value, propertyInfo, false)) {
+ return '';
+ }
+ if (propertyInfo !== null) {
+ var attributeName = propertyInfo.attributeName;
+ var type = propertyInfo.type;
+
+ if (type === BOOLEAN || type === OVERLOADED_BOOLEAN && value === true) {
+ return attributeName + '=""';
+ } else {
+ return attributeName + '=' + quoteAttributeValueForBrowser(value);
+ }
+ } else if (isAttributeNameSafe(name)) {
+ return name + '=' + quoteAttributeValueForBrowser(value);
+ }
+ return '';
+}
+
+/**
+ * Creates markup for a custom property.
+ *
+ * @param {string} name
+ * @param {*} value
+ * @return {string} Markup string, or empty string if the property was invalid.
+ */
+function createMarkupForCustomAttribute(name, value) {
+ if (!isAttributeNameSafe(name) || value == null) {
+ return '';
+ }
+ return name + '=' + quoteAttributeValueForBrowser(value);
+}
+
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
+ ;
+}
+
+var currentlyRenderingComponent = null;
+var firstWorkInProgressHook = null;
+var workInProgressHook = null;
+// Whether the work-in-progress hook is a re-rendered hook
+var isReRender = false;
+// Whether an update was scheduled during the currently executing render pass.
+var didScheduleRenderPhaseUpdate = false;
+// Lazily created map of render-phase updates
+var renderPhaseUpdates = null;
+// Counter to prevent infinite loops.
+var numberOfReRenders = 0;
+var RE_RENDER_LIMIT = 25;
+
+var isInHookUserCodeInDev = false;
+
+// In DEV, this is the name of the currently executing primitive hook
+var currentHookNameInDev = void 0;
+
+function resolveCurrentlyRenderingComponent() {
+ !(currentlyRenderingComponent !== null) ? invariant(false, 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:\n1. You might have mismatching versions of React and the renderer (such as React DOM)\n2. You might be breaking the Rules of Hooks\n3. You might have more than one copy of React in the same app\nSee https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.') : void 0;
+ {
+ !!isInHookUserCodeInDev ? warning$1(false, 'Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see ' + 'https://fb.me/rules-of-hooks') : void 0;
+ }
+ return currentlyRenderingComponent;
+}
+
+function areHookInputsEqual(nextDeps, prevDeps) {
+ if (prevDeps === null) {
+ {
+ warning$1(false, '%s received a final argument during this render, but not during ' + 'the previous render. Even though the final argument is optional, ' + 'its type cannot change between renders.', currentHookNameInDev);
+ }
+ return false;
+ }
+
+ {
+ // Don't bother comparing lengths in prod because these arrays should be
+ // passed inline.
+ if (nextDeps.length !== prevDeps.length) {
+ warning$1(false, 'The final argument passed to %s changed size between renders. The ' + 'order and size of this array must remain constant.\n\n' + 'Previous: %s\n' + 'Incoming: %s', currentHookNameInDev, '[' + nextDeps.join(', ') + ']', '[' + prevDeps.join(', ') + ']');
+ }
+ }
+ for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
+ if (is(nextDeps[i], prevDeps[i])) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+function createHook() {
+ if (numberOfReRenders > 0) {
+ invariant(false, 'Rendered more hooks than during the previous render');
+ }
+ return {
+ memoizedState: null,
+ queue: null,
+ next: null
+ };
+}
+
+function createWorkInProgressHook() {
+ if (workInProgressHook === null) {
+ // This is the first hook in the list
+ if (firstWorkInProgressHook === null) {
+ isReRender = false;
+ firstWorkInProgressHook = workInProgressHook = createHook();
+ } else {
+ // There's already a work-in-progress. Reuse it.
+ isReRender = true;
+ workInProgressHook = firstWorkInProgressHook;
+ }
+ } else {
+ if (workInProgressHook.next === null) {
+ isReRender = false;
+ // Append to the end of the list
+ workInProgressHook = workInProgressHook.next = createHook();
+ } else {
+ // There's already a work-in-progress. Reuse it.
+ isReRender = true;
+ workInProgressHook = workInProgressHook.next;
+ }
+ }
+ return workInProgressHook;
+}
+
+function prepareToUseHooks(componentIdentity) {
+ currentlyRenderingComponent = componentIdentity;
+ {
+ isInHookUserCodeInDev = false;
+ }
+
+ // The following should have already been reset
+ // didScheduleRenderPhaseUpdate = false;
+ // firstWorkInProgressHook = null;
+ // numberOfReRenders = 0;
+ // renderPhaseUpdates = null;
+ // workInProgressHook = null;
+}
+
+function finishHooks(Component, props, children, refOrContext) {
+ // This must be called after every function component to prevent hooks from
+ // being used in classes.
+
+ while (didScheduleRenderPhaseUpdate) {
+ // Updates were scheduled during the render phase. They are stored in
+ // the `renderPhaseUpdates` map. Call the component again, reusing the
+ // work-in-progress hooks and applying the additional updates on top. Keep
+ // restarting until no more updates are scheduled.
+ didScheduleRenderPhaseUpdate = false;
+ numberOfReRenders += 1;
+
+ // Start over from the beginning of the list
+ workInProgressHook = null;
+
+ children = Component(props, refOrContext);
+ }
+ currentlyRenderingComponent = null;
+ firstWorkInProgressHook = null;
+ numberOfReRenders = 0;
+ renderPhaseUpdates = null;
+ workInProgressHook = null;
+ {
+ isInHookUserCodeInDev = false;
+ }
+
+ // These were reset above
+ // currentlyRenderingComponent = null;
+ // didScheduleRenderPhaseUpdate = false;
+ // firstWorkInProgressHook = null;
+ // numberOfReRenders = 0;
+ // renderPhaseUpdates = null;
+ // workInProgressHook = null;
+
+ return children;
+}
+
+function readContext(context, observedBits) {
+ var threadID = currentThreadID;
+ validateContextBounds(context, threadID);
+ {
+ !!isInHookUserCodeInDev ? warning$1(false, 'Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().') : void 0;
+ }
+ return context[threadID];
+}
+
+function useContext(context, observedBits) {
+ {
+ currentHookNameInDev = 'useContext';
+ }
+ resolveCurrentlyRenderingComponent();
+ var threadID = currentThreadID;
+ validateContextBounds(context, threadID);
+ return context[threadID];
+}
+
+function basicStateReducer(state, action) {
+ return typeof action === 'function' ? action(state) : action;
+}
+
+function useState(initialState) {
+ {
+ currentHookNameInDev = 'useState';
+ }
+ return useReducer(basicStateReducer,
+ // useReducer has a special case to support lazy useState initializers
+ initialState);
+}
+
+function useReducer(reducer, initialArg, init) {
+ {
+ if (reducer !== basicStateReducer) {
+ currentHookNameInDev = 'useReducer';
+ }
+ }
+ currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
+ workInProgressHook = createWorkInProgressHook();
+ if (isReRender) {
+ // This is a re-render. Apply the new render phase updates to the previous
+ var _queue = workInProgressHook.queue;
+ var _dispatch = _queue.dispatch;
+ if (renderPhaseUpdates !== null) {
+ // Render phase updates are stored in a map of queue -> linked list
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(_queue);
+ if (firstRenderPhaseUpdate !== undefined) {
+ renderPhaseUpdates.delete(_queue);
+ var newState = workInProgressHook.memoizedState;
+ var update = firstRenderPhaseUpdate;
+ do {
+ // Process this render phase update. We don't have to check the
+ // priority because it will always be the same as the current
+ // render's.
+ var _action = update.action;
+ {
+ isInHookUserCodeInDev = true;
+ }
+ newState = reducer(newState, _action);
+ {
+ isInHookUserCodeInDev = false;
+ }
+ update = update.next;
+ } while (update !== null);
+
+ workInProgressHook.memoizedState = newState;
+
+ return [newState, _dispatch];
+ }
+ }
+ return [workInProgressHook.memoizedState, _dispatch];
+ } else {
+ {
+ isInHookUserCodeInDev = true;
+ }
+ var initialState = void 0;
+ if (reducer === basicStateReducer) {
+ // Special case for `useState`.
+ initialState = typeof initialArg === 'function' ? initialArg() : initialArg;
+ } else {
+ initialState = init !== undefined ? init(initialArg) : initialArg;
+ }
+ {
+ isInHookUserCodeInDev = false;
+ }
+ workInProgressHook.memoizedState = initialState;
+ var _queue2 = workInProgressHook.queue = {
+ last: null,
+ dispatch: null
+ };
+ var _dispatch2 = _queue2.dispatch = dispatchAction.bind(null, currentlyRenderingComponent, _queue2);
+ return [workInProgressHook.memoizedState, _dispatch2];
+ }
+}
+
+function useMemo(nextCreate, deps) {
+ currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
+ workInProgressHook = createWorkInProgressHook();
+
+ var nextDeps = deps === undefined ? null : deps;
+
+ if (workInProgressHook !== null) {
+ var prevState = workInProgressHook.memoizedState;
+ if (prevState !== null) {
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ }
+
+ {
+ isInHookUserCodeInDev = true;
+ }
+ var nextValue = nextCreate();
+ {
+ isInHookUserCodeInDev = false;
+ }
+ workInProgressHook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+function useRef(initialValue) {
+ currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
+ workInProgressHook = createWorkInProgressHook();
+ var previousRef = workInProgressHook.memoizedState;
+ if (previousRef === null) {
+ var ref = { current: initialValue };
+ {
+ Object.seal(ref);
+ }
+ workInProgressHook.memoizedState = ref;
+ return ref;
+ } else {
+ return previousRef;
+ }
+}
+
+function useLayoutEffect(create, inputs) {
+ {
+ currentHookNameInDev = 'useLayoutEffect';
+ }
+ warning$1(false, 'useLayoutEffect does nothing on the server, because its effect cannot ' + "be encoded into the server renderer's output format. This will lead " + 'to a mismatch between the initial, non-hydrated UI and the intended ' + 'UI. To avoid this, useLayoutEffect should only be used in ' + 'components that render exclusively on the client. ' + 'See https://fb.me/react-uselayouteffect-ssr for common fixes.');
+}
+
+function dispatchAction(componentIdentity, queue, action) {
+ !(numberOfReRenders < RE_RENDER_LIMIT) ? invariant(false, 'Too many re-renders. React limits the number of renders to prevent an infinite loop.') : void 0;
+
+ if (componentIdentity === currentlyRenderingComponent) {
+ // This is a render phase update. Stash it in a lazily-created map of
+ // queue -> linked list of updates. After this render pass, we'll restart
+ // and apply the stashed updates on top of the work-in-progress hook.
+ didScheduleRenderPhaseUpdate = true;
+ var update = {
+ action: action,
+ next: null
+ };
+ if (renderPhaseUpdates === null) {
+ renderPhaseUpdates = new Map();
+ }
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate === undefined) {
+ renderPhaseUpdates.set(queue, update);
+ } else {
+ // Append the update to the end of the list.
+ var lastRenderPhaseUpdate = firstRenderPhaseUpdate;
+ while (lastRenderPhaseUpdate.next !== null) {
+ lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
+ }
+ lastRenderPhaseUpdate.next = update;
+ }
+ } else {
+ // This means an update has happened after the function component has
+ // returned. On the server this is a no-op. In React Fiber, the update
+ // would be scheduled for a future render.
+ }
+}
+
+function useCallback(callback, deps) {
+ // Callbacks are passed as they are in the server environment.
+ return callback;
+}
+
+function noop() {}
+
+var currentThreadID = 0;
+
+function setCurrentThreadID(threadID) {
+ currentThreadID = threadID;
+}
+
+var Dispatcher = {
+ readContext: readContext,
+ useContext: useContext,
+ useMemo: useMemo,
+ useReducer: useReducer,
+ useRef: useRef,
+ useState: useState,
+ useLayoutEffect: useLayoutEffect,
+ useCallback: useCallback,
+ // useImperativeHandle is not run in the server environment
+ useImperativeHandle: noop,
+ // Effects are not run in the server environment.
+ useEffect: noop,
+ // Debugging effect
+ useDebugValue: noop
+};
+
+var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
+var MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
+var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+
+var Namespaces = {
+ html: HTML_NAMESPACE,
+ mathml: MATH_NAMESPACE,
+ svg: SVG_NAMESPACE
+};
+
+// Assumes there is no parent namespace.
+function getIntrinsicNamespace(type) {
+ switch (type) {
+ case 'svg':
+ return SVG_NAMESPACE;
+ case 'math':
+ return MATH_NAMESPACE;
+ default:
+ return HTML_NAMESPACE;
+ }
+}
+
+function getChildNamespace(parentNamespace, type) {
+ if (parentNamespace == null || parentNamespace === HTML_NAMESPACE) {
+ // No (or default) parent namespace: potential entry point.
+ return getIntrinsicNamespace(type);
+ }
+ if (parentNamespace === SVG_NAMESPACE && type === 'foreignObject') {
+ // We're leaving SVG.
+ return HTML_NAMESPACE;
+ }
+ // By default, pass namespace below.
+ return parentNamespace;
+}
+
+var ReactDebugCurrentFrame$2 = null;
+
+var ReactControlledValuePropTypes = {
+ checkPropTypes: null
+};
+
+{
+ ReactDebugCurrentFrame$2 = ReactSharedInternals.ReactDebugCurrentFrame;
+
+ var hasReadOnlyValue = {
+ button: true,
+ checkbox: true,
+ image: true,
+ hidden: true,
+ radio: true,
+ reset: true,
+ submit: true
+ };
+
+ var propTypes = {
+ value: function (props, propName, componentName) {
+ if (hasReadOnlyValue[props.type] || props.onChange || props.readOnly || props.disabled || props[propName] == null) {
+ return null;
+ }
+ return new Error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
+ },
+ checked: function (props, propName, componentName) {
+ if (props.onChange || props.readOnly || props.disabled || props[propName] == null) {
+ return null;
+ }
+ return new Error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
+ }
+ };
+
+ /**
+ * Provide a linked `value` attribute for controlled forms. You should not use
+ * this outside of the ReactDOM controlled form components.
+ */
+ ReactControlledValuePropTypes.checkPropTypes = function (tagName, props) {
+ checkPropTypes_1(propTypes, props, 'prop', tagName, ReactDebugCurrentFrame$2.getStackAddendum);
+ };
+}
+
+// For HTML, certain tags should omit their close tag. We keep a whitelist for
+// those special-case tags.
+
+var omittedCloseTags = {
+ area: true,
+ base: true,
+ br: true,
+ col: true,
+ embed: true,
+ hr: true,
+ img: true,
+ input: true,
+ keygen: true,
+ link: true,
+ meta: true,
+ param: true,
+ source: true,
+ track: true,
+ wbr: true
+ // NOTE: menuitem's close tag should be omitted, but that causes problems.
+};
+
+// For HTML, certain tags cannot have children. This has the same purpose as
+// `omittedCloseTags` except that `menuitem` should still have its closing tag.
+
+var voidElementTags = _assign({
+ menuitem: true
+}, omittedCloseTags);
+
+// TODO: We can remove this if we add invariantWithStack()
+// or add stack by default to invariants where possible.
+var HTML = '__html';
+
+var ReactDebugCurrentFrame$3 = null;
+{
+ ReactDebugCurrentFrame$3 = ReactSharedInternals.ReactDebugCurrentFrame;
+}
+
+function assertValidProps(tag, props) {
+ if (!props) {
+ return;
+ }
+ // Note the use of `==` which checks for null or undefined.
+ if (voidElementTags[tag]) {
+ !(props.children == null && props.dangerouslySetInnerHTML == null) ? invariant(false, '%s is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.%s', tag, ReactDebugCurrentFrame$3.getStackAddendum()) : void 0;
+ }
+ if (props.dangerouslySetInnerHTML != null) {
+ !(props.children == null) ? invariant(false, 'Can only set one of `children` or `props.dangerouslySetInnerHTML`.') : void 0;
+ !(typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML) ? invariant(false, '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. Please visit https://fb.me/react-invariant-dangerously-set-inner-html for more information.') : void 0;
+ }
+ {
+ !(props.suppressContentEditableWarning || !props.contentEditable || props.children == null) ? warning$1(false, 'A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.') : void 0;
+ }
+ !(props.style == null || typeof props.style === 'object') ? invariant(false, 'The `style` prop expects a mapping from style properties to values, not a string. For example, style={{marginRight: spacing + \'em\'}} when using JSX.%s', ReactDebugCurrentFrame$3.getStackAddendum()) : void 0;
+}
+
+/**
+ * CSS properties which accept numbers but are not in units of "px".
+ */
+var isUnitlessNumber = {
+ animationIterationCount: true,
+ borderImageOutset: true,
+ borderImageSlice: true,
+ borderImageWidth: true,
+ boxFlex: true,
+ boxFlexGroup: true,
+ boxOrdinalGroup: true,
+ columnCount: true,
+ columns: true,
+ flex: true,
+ flexGrow: true,
+ flexPositive: true,
+ flexShrink: true,
+ flexNegative: true,
+ flexOrder: true,
+ gridArea: true,
+ gridRow: true,
+ gridRowEnd: true,
+ gridRowSpan: true,
+ gridRowStart: true,
+ gridColumn: true,
+ gridColumnEnd: true,
+ gridColumnSpan: true,
+ gridColumnStart: true,
+ fontWeight: true,
+ lineClamp: true,
+ lineHeight: true,
+ opacity: true,
+ order: true,
+ orphans: true,
+ tabSize: true,
+ widows: true,
+ zIndex: true,
+ zoom: true,
+
+ // SVG-related properties
+ fillOpacity: true,
+ floodOpacity: true,
+ stopOpacity: true,
+ strokeDasharray: true,
+ strokeDashoffset: true,
+ strokeMiterlimit: true,
+ strokeOpacity: true,
+ strokeWidth: true
+};
+
+/**
+ * @param {string} prefix vendor-specific prefix, eg: Webkit
+ * @param {string} key style name, eg: transitionDuration
+ * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
+ * WebkitTransitionDuration
+ */
+function prefixKey(prefix, key) {
+ return prefix + key.charAt(0).toUpperCase() + key.substring(1);
+}
+
+/**
+ * Support style names that may come passed in prefixed by adding permutations
+ * of vendor prefixes.
+ */
+var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
+
+// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
+// infinite loop, because it iterates over the newly added props too.
+Object.keys(isUnitlessNumber).forEach(function (prop) {
+ prefixes.forEach(function (prefix) {
+ isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
+ });
+});
+
+/**
+ * Convert a value into the proper css writable value. The style name `name`
+ * should be logical (no hyphens), as specified
+ * in `CSSProperty.isUnitlessNumber`.
+ *
+ * @param {string} name CSS property name such as `topMargin`.
+ * @param {*} value CSS property value such as `10px`.
+ * @return {string} Normalized style value with dimensions applied.
+ */
+function dangerousStyleValue(name, value, isCustomProperty) {
+ // Note that we've removed escapeTextForBrowser() calls here since the
+ // whole string will be escaped when the attribute is injected into
+ // the markup. If you provide unsafe user data here they can inject
+ // arbitrary CSS which may be problematic (I couldn't repro this):
+ // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
+ // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
+ // This is not an XSS hole but instead a potential CSS injection issue
+ // which has lead to a greater discussion about how we're going to
+ // trust URLs moving forward. See #2115901
+
+ var isEmpty = value == null || typeof value === 'boolean' || value === '';
+ if (isEmpty) {
+ return '';
+ }
+
+ if (!isCustomProperty && typeof value === 'number' && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])) {
+ return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers
+ }
+
+ return ('' + value).trim();
+}
+
+var uppercasePattern = /([A-Z])/g;
+var msPattern = /^ms-/;
+
+/**
+ * Hyphenates a camelcased CSS property name, for example:
+ *
+ * > hyphenateStyleName('backgroundColor')
+ * < "background-color"
+ * > hyphenateStyleName('MozTransition')
+ * < "-moz-transition"
+ * > hyphenateStyleName('msTransition')
+ * < "-ms-transition"
+ *
+ * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
+ * is converted to `-ms-`.
+ */
+function hyphenateStyleName(name) {
+ return name.replace(uppercasePattern, '-$1').toLowerCase().replace(msPattern, '-ms-');
+}
+
+function isCustomComponent(tagName, props) {
+ if (tagName.indexOf('-') === -1) {
+ return typeof props.is === 'string';
+ }
+ switch (tagName) {
+ // These are reserved SVG and MathML elements.
+ // We don't mind this whitelist too much because we expect it to never grow.
+ // The alternative is to track the namespace in a few places which is convoluted.
+ // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts
+ case 'annotation-xml':
+ case 'color-profile':
+ case 'font-face':
+ case 'font-face-src':
+ case 'font-face-uri':
+ case 'font-face-format':
+ case 'font-face-name':
+ case 'missing-glyph':
+ return false;
+ default:
+ return true;
+ }
+}
+
+var warnValidStyle = function () {};
+
+{
+ // 'msTransform' is correct, but the other prefixes should be capitalized
+ var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
+ var msPattern$1 = /^-ms-/;
+ var hyphenPattern = /-(.)/g;
+
+ // style values shouldn't contain a semicolon
+ var badStyleValueWithSemicolonPattern = /;\s*$/;
+
+ var warnedStyleNames = {};
+ var warnedStyleValues = {};
+ var warnedForNaNValue = false;
+ var warnedForInfinityValue = false;
+
+ var camelize = function (string) {
+ return string.replace(hyphenPattern, function (_, character) {
+ return character.toUpperCase();
+ });
+ };
+
+ var warnHyphenatedStyleName = function (name) {
+ if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
+ return;
+ }
+
+ warnedStyleNames[name] = true;
+ warning$1(false, 'Unsupported style property %s. Did you mean %s?', name,
+ // As Andi Smith suggests
+ // (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
+ // is converted to lowercase `ms`.
+ camelize(name.replace(msPattern$1, 'ms-')));
+ };
+
+ var warnBadVendoredStyleName = function (name) {
+ if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
+ return;
+ }
+
+ warnedStyleNames[name] = true;
+ warning$1(false, 'Unsupported vendor-prefixed style property %s. Did you mean %s?', name, name.charAt(0).toUpperCase() + name.slice(1));
+ };
+
+ var warnStyleValueWithSemicolon = function (name, value) {
+ if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
+ return;
+ }
+
+ warnedStyleValues[value] = true;
+ warning$1(false, "Style property values shouldn't contain a semicolon. " + 'Try "%s: %s" instead.', name, value.replace(badStyleValueWithSemicolonPattern, ''));
+ };
+
+ var warnStyleValueIsNaN = function (name, value) {
+ if (warnedForNaNValue) {
+ return;
+ }
+
+ warnedForNaNValue = true;
+ warning$1(false, '`NaN` is an invalid value for the `%s` css style property.', name);
+ };
+
+ var warnStyleValueIsInfinity = function (name, value) {
+ if (warnedForInfinityValue) {
+ return;
+ }
+
+ warnedForInfinityValue = true;
+ warning$1(false, '`Infinity` is an invalid value for the `%s` css style property.', name);
+ };
+
+ warnValidStyle = function (name, value) {
+ if (name.indexOf('-') > -1) {
+ warnHyphenatedStyleName(name);
+ } else if (badVendoredStyleNamePattern.test(name)) {
+ warnBadVendoredStyleName(name);
+ } else if (badStyleValueWithSemicolonPattern.test(value)) {
+ warnStyleValueWithSemicolon(name, value);
+ }
+
+ if (typeof value === 'number') {
+ if (isNaN(value)) {
+ warnStyleValueIsNaN(name, value);
+ } else if (!isFinite(value)) {
+ warnStyleValueIsInfinity(name, value);
+ }
+ }
+ };
+}
+
+var warnValidStyle$1 = warnValidStyle;
+
+var ariaProperties = {
+ 'aria-current': 0, // state
+ 'aria-details': 0,
+ 'aria-disabled': 0, // state
+ 'aria-hidden': 0, // state
+ 'aria-invalid': 0, // state
+ 'aria-keyshortcuts': 0,
+ 'aria-label': 0,
+ 'aria-roledescription': 0,
+ // Widget Attributes
+ 'aria-autocomplete': 0,
+ 'aria-checked': 0,
+ 'aria-expanded': 0,
+ 'aria-haspopup': 0,
+ 'aria-level': 0,
+ 'aria-modal': 0,
+ 'aria-multiline': 0,
+ 'aria-multiselectable': 0,
+ 'aria-orientation': 0,
+ 'aria-placeholder': 0,
+ 'aria-pressed': 0,
+ 'aria-readonly': 0,
+ 'aria-required': 0,
+ 'aria-selected': 0,
+ 'aria-sort': 0,
+ 'aria-valuemax': 0,
+ 'aria-valuemin': 0,
+ 'aria-valuenow': 0,
+ 'aria-valuetext': 0,
+ // Live Region Attributes
+ 'aria-atomic': 0,
+ 'aria-busy': 0,
+ 'aria-live': 0,
+ 'aria-relevant': 0,
+ // Drag-and-Drop Attributes
+ 'aria-dropeffect': 0,
+ 'aria-grabbed': 0,
+ // Relationship Attributes
+ 'aria-activedescendant': 0,
+ 'aria-colcount': 0,
+ 'aria-colindex': 0,
+ 'aria-colspan': 0,
+ 'aria-controls': 0,
+ 'aria-describedby': 0,
+ 'aria-errormessage': 0,
+ 'aria-flowto': 0,
+ 'aria-labelledby': 0,
+ 'aria-owns': 0,
+ 'aria-posinset': 0,
+ 'aria-rowcount': 0,
+ 'aria-rowindex': 0,
+ 'aria-rowspan': 0,
+ 'aria-setsize': 0
+};
+
+var warnedProperties = {};
+var rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
+var rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+var hasOwnProperty$2 = Object.prototype.hasOwnProperty;
+
+function validateProperty(tagName, name) {
+ if (hasOwnProperty$2.call(warnedProperties, name) && warnedProperties[name]) {
+ return true;
+ }
+
+ if (rARIACamel.test(name)) {
+ var ariaName = 'aria-' + name.slice(4).toLowerCase();
+ var correctName = ariaProperties.hasOwnProperty(ariaName) ? ariaName : null;
+
+ // If this is an aria-* attribute, but is not listed in the known DOM
+ // DOM properties, then it is an invalid aria-* attribute.
+ if (correctName == null) {
+ warning$1(false, 'Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.', name);
+ warnedProperties[name] = true;
+ return true;
+ }
+ // aria-* attributes should be lowercase; suggest the lowercase version.
+ if (name !== correctName) {
+ warning$1(false, 'Invalid ARIA attribute `%s`. Did you mean `%s`?', name, correctName);
+ warnedProperties[name] = true;
+ return true;
+ }
+ }
+
+ if (rARIA.test(name)) {
+ var lowerCasedName = name.toLowerCase();
+ var standardName = ariaProperties.hasOwnProperty(lowerCasedName) ? lowerCasedName : null;
+
+ // If this is an aria-* attribute, but is not listed in the known DOM
+ // DOM properties, then it is an invalid aria-* attribute.
+ if (standardName == null) {
+ warnedProperties[name] = true;
+ return false;
+ }
+ // aria-* attributes should be lowercase; suggest the lowercase version.
+ if (name !== standardName) {
+ warning$1(false, 'Unknown ARIA attribute `%s`. Did you mean `%s`?', name, standardName);
+ warnedProperties[name] = true;
+ return true;
+ }
+ }
+
+ return true;
+}
+
+function warnInvalidARIAProps(type, props) {
+ var invalidProps = [];
+
+ for (var key in props) {
+ var isValid = validateProperty(type, key);
+ if (!isValid) {
+ invalidProps.push(key);
+ }
+ }
+
+ var unknownPropString = invalidProps.map(function (prop) {
+ return '`' + prop + '`';
+ }).join(', ');
+
+ if (invalidProps.length === 1) {
+ warning$1(false, 'Invalid aria prop %s on <%s> tag. ' + 'For details, see https://fb.me/invalid-aria-prop', unknownPropString, type);
+ } else if (invalidProps.length > 1) {
+ warning$1(false, 'Invalid aria props %s on <%s> tag. ' + 'For details, see https://fb.me/invalid-aria-prop', unknownPropString, type);
+ }
+}
+
+function validateProperties(type, props) {
+ if (isCustomComponent(type, props)) {
+ return;
+ }
+ warnInvalidARIAProps(type, props);
+}
+
+var didWarnValueNull = false;
+
+function validateProperties$1(type, props) {
+ if (type !== 'input' && type !== 'textarea' && type !== 'select') {
+ return;
+ }
+
+ if (props != null && props.value === null && !didWarnValueNull) {
+ didWarnValueNull = true;
+ if (type === 'select' && props.multiple) {
+ warning$1(false, '`value` prop on `%s` should not be null. ' + 'Consider using an empty array when `multiple` is set to `true` ' + 'to clear the component or `undefined` for uncontrolled components.', type);
+ } else {
+ warning$1(false, '`value` prop on `%s` should not be null. ' + 'Consider using an empty string to clear the component or `undefined` ' + 'for uncontrolled components.', type);
+ }
+ }
+}
+
+/**
+ * Registers plugins so that they can extract and dispatch events.
+ *
+ * @see {EventPluginHub}
+ */
+
+/**
+ * Ordered list of injected plugins.
+ */
+
+
+/**
+ * Mapping from event name to dispatch config
+ */
+
+
+/**
+ * Mapping from registration name to plugin module
+ */
+var registrationNameModules = {};
+
+/**
+ * Mapping from registration name to event name
+ */
+
+
+/**
+ * Mapping from lowercase registration names to the properly cased version,
+ * used to warn in the case of missing event handlers. Available
+ * only in true.
+ * @type {Object}
+ */
+var possibleRegistrationNames = {};
+// Trust the developer to only use possibleRegistrationNames in true
+
+/**
+ * Injects an ordering of plugins (by plugin name). This allows the ordering
+ * to be decoupled from injection of the actual plugins so that ordering is
+ * always deterministic regardless of packaging, on-the-fly injection, etc.
+ *
+ * @param {array} InjectedEventPluginOrder
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginOrder}
+ */
+
+
+/**
+ * Injects plugins to be used by `EventPluginHub`. The plugin names must be
+ * in the ordering injected by `injectEventPluginOrder`.
+ *
+ * Plugins can be injected as part of page initialization or on-the-fly.
+ *
+ * @param {object} injectedNamesToPlugins Map from names to plugin modules.
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginsByName}
+ */
+
+// When adding attributes to the HTML or SVG whitelist, be sure to
+// also add them to this module to ensure casing and incorrect name
+// warnings.
+var possibleStandardNames = {
+ // HTML
+ accept: 'accept',
+ acceptcharset: 'acceptCharset',
+ 'accept-charset': 'acceptCharset',
+ accesskey: 'accessKey',
+ action: 'action',
+ allowfullscreen: 'allowFullScreen',
+ alt: 'alt',
+ as: 'as',
+ async: 'async',
+ autocapitalize: 'autoCapitalize',
+ autocomplete: 'autoComplete',
+ autocorrect: 'autoCorrect',
+ autofocus: 'autoFocus',
+ autoplay: 'autoPlay',
+ autosave: 'autoSave',
+ capture: 'capture',
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing',
+ challenge: 'challenge',
+ charset: 'charSet',
+ checked: 'checked',
+ children: 'children',
+ cite: 'cite',
+ class: 'className',
+ classid: 'classID',
+ classname: 'className',
+ cols: 'cols',
+ colspan: 'colSpan',
+ content: 'content',
+ contenteditable: 'contentEditable',
+ contextmenu: 'contextMenu',
+ controls: 'controls',
+ controlslist: 'controlsList',
+ coords: 'coords',
+ crossorigin: 'crossOrigin',
+ dangerouslysetinnerhtml: 'dangerouslySetInnerHTML',
+ data: 'data',
+ datetime: 'dateTime',
+ default: 'default',
+ defaultchecked: 'defaultChecked',
+ defaultvalue: 'defaultValue',
+ defer: 'defer',
+ dir: 'dir',
+ disabled: 'disabled',
+ download: 'download',
+ draggable: 'draggable',
+ enctype: 'encType',
+ for: 'htmlFor',
+ form: 'form',
+ formmethod: 'formMethod',
+ formaction: 'formAction',
+ formenctype: 'formEncType',
+ formnovalidate: 'formNoValidate',
+ formtarget: 'formTarget',
+ frameborder: 'frameBorder',
+ headers: 'headers',
+ height: 'height',
+ hidden: 'hidden',
+ high: 'high',
+ href: 'href',
+ hreflang: 'hrefLang',
+ htmlfor: 'htmlFor',
+ httpequiv: 'httpEquiv',
+ 'http-equiv': 'httpEquiv',
+ icon: 'icon',
+ id: 'id',
+ innerhtml: 'innerHTML',
+ inputmode: 'inputMode',
+ integrity: 'integrity',
+ is: 'is',
+ itemid: 'itemID',
+ itemprop: 'itemProp',
+ itemref: 'itemRef',
+ itemscope: 'itemScope',
+ itemtype: 'itemType',
+ keyparams: 'keyParams',
+ keytype: 'keyType',
+ kind: 'kind',
+ label: 'label',
+ lang: 'lang',
+ list: 'list',
+ loop: 'loop',
+ low: 'low',
+ manifest: 'manifest',
+ marginwidth: 'marginWidth',
+ marginheight: 'marginHeight',
+ max: 'max',
+ maxlength: 'maxLength',
+ media: 'media',
+ mediagroup: 'mediaGroup',
+ method: 'method',
+ min: 'min',
+ minlength: 'minLength',
+ multiple: 'multiple',
+ muted: 'muted',
+ name: 'name',
+ nomodule: 'noModule',
+ nonce: 'nonce',
+ novalidate: 'noValidate',
+ open: 'open',
+ optimum: 'optimum',
+ pattern: 'pattern',
+ placeholder: 'placeholder',
+ playsinline: 'playsInline',
+ poster: 'poster',
+ preload: 'preload',
+ profile: 'profile',
+ radiogroup: 'radioGroup',
+ readonly: 'readOnly',
+ referrerpolicy: 'referrerPolicy',
+ rel: 'rel',
+ required: 'required',
+ reversed: 'reversed',
+ role: 'role',
+ rows: 'rows',
+ rowspan: 'rowSpan',
+ sandbox: 'sandbox',
+ scope: 'scope',
+ scoped: 'scoped',
+ scrolling: 'scrolling',
+ seamless: 'seamless',
+ selected: 'selected',
+ shape: 'shape',
+ size: 'size',
+ sizes: 'sizes',
+ span: 'span',
+ spellcheck: 'spellCheck',
+ src: 'src',
+ srcdoc: 'srcDoc',
+ srclang: 'srcLang',
+ srcset: 'srcSet',
+ start: 'start',
+ step: 'step',
+ style: 'style',
+ summary: 'summary',
+ tabindex: 'tabIndex',
+ target: 'target',
+ title: 'title',
+ type: 'type',
+ usemap: 'useMap',
+ value: 'value',
+ width: 'width',
+ wmode: 'wmode',
+ wrap: 'wrap',
+
+ // SVG
+ about: 'about',
+ accentheight: 'accentHeight',
+ 'accent-height': 'accentHeight',
+ accumulate: 'accumulate',
+ additive: 'additive',
+ alignmentbaseline: 'alignmentBaseline',
+ 'alignment-baseline': 'alignmentBaseline',
+ allowreorder: 'allowReorder',
+ alphabetic: 'alphabetic',
+ amplitude: 'amplitude',
+ arabicform: 'arabicForm',
+ 'arabic-form': 'arabicForm',
+ ascent: 'ascent',
+ attributename: 'attributeName',
+ attributetype: 'attributeType',
+ autoreverse: 'autoReverse',
+ azimuth: 'azimuth',
+ basefrequency: 'baseFrequency',
+ baselineshift: 'baselineShift',
+ 'baseline-shift': 'baselineShift',
+ baseprofile: 'baseProfile',
+ bbox: 'bbox',
+ begin: 'begin',
+ bias: 'bias',
+ by: 'by',
+ calcmode: 'calcMode',
+ capheight: 'capHeight',
+ 'cap-height': 'capHeight',
+ clip: 'clip',
+ clippath: 'clipPath',
+ 'clip-path': 'clipPath',
+ clippathunits: 'clipPathUnits',
+ cliprule: 'clipRule',
+ 'clip-rule': 'clipRule',
+ color: 'color',
+ colorinterpolation: 'colorInterpolation',
+ 'color-interpolation': 'colorInterpolation',
+ colorinterpolationfilters: 'colorInterpolationFilters',
+ 'color-interpolation-filters': 'colorInterpolationFilters',
+ colorprofile: 'colorProfile',
+ 'color-profile': 'colorProfile',
+ colorrendering: 'colorRendering',
+ 'color-rendering': 'colorRendering',
+ contentscripttype: 'contentScriptType',
+ contentstyletype: 'contentStyleType',
+ cursor: 'cursor',
+ cx: 'cx',
+ cy: 'cy',
+ d: 'd',
+ datatype: 'datatype',
+ decelerate: 'decelerate',
+ descent: 'descent',
+ diffuseconstant: 'diffuseConstant',
+ direction: 'direction',
+ display: 'display',
+ divisor: 'divisor',
+ dominantbaseline: 'dominantBaseline',
+ 'dominant-baseline': 'dominantBaseline',
+ dur: 'dur',
+ dx: 'dx',
+ dy: 'dy',
+ edgemode: 'edgeMode',
+ elevation: 'elevation',
+ enablebackground: 'enableBackground',
+ 'enable-background': 'enableBackground',
+ end: 'end',
+ exponent: 'exponent',
+ externalresourcesrequired: 'externalResourcesRequired',
+ fill: 'fill',
+ fillopacity: 'fillOpacity',
+ 'fill-opacity': 'fillOpacity',
+ fillrule: 'fillRule',
+ 'fill-rule': 'fillRule',
+ filter: 'filter',
+ filterres: 'filterRes',
+ filterunits: 'filterUnits',
+ floodopacity: 'floodOpacity',
+ 'flood-opacity': 'floodOpacity',
+ floodcolor: 'floodColor',
+ 'flood-color': 'floodColor',
+ focusable: 'focusable',
+ fontfamily: 'fontFamily',
+ 'font-family': 'fontFamily',
+ fontsize: 'fontSize',
+ 'font-size': 'fontSize',
+ fontsizeadjust: 'fontSizeAdjust',
+ 'font-size-adjust': 'fontSizeAdjust',
+ fontstretch: 'fontStretch',
+ 'font-stretch': 'fontStretch',
+ fontstyle: 'fontStyle',
+ 'font-style': 'fontStyle',
+ fontvariant: 'fontVariant',
+ 'font-variant': 'fontVariant',
+ fontweight: 'fontWeight',
+ 'font-weight': 'fontWeight',
+ format: 'format',
+ from: 'from',
+ fx: 'fx',
+ fy: 'fy',
+ g1: 'g1',
+ g2: 'g2',
+ glyphname: 'glyphName',
+ 'glyph-name': 'glyphName',
+ glyphorientationhorizontal: 'glyphOrientationHorizontal',
+ 'glyph-orientation-horizontal': 'glyphOrientationHorizontal',
+ glyphorientationvertical: 'glyphOrientationVertical',
+ 'glyph-orientation-vertical': 'glyphOrientationVertical',
+ glyphref: 'glyphRef',
+ gradienttransform: 'gradientTransform',
+ gradientunits: 'gradientUnits',
+ hanging: 'hanging',
+ horizadvx: 'horizAdvX',
+ 'horiz-adv-x': 'horizAdvX',
+ horizoriginx: 'horizOriginX',
+ 'horiz-origin-x': 'horizOriginX',
+ ideographic: 'ideographic',
+ imagerendering: 'imageRendering',
+ 'image-rendering': 'imageRendering',
+ in2: 'in2',
+ in: 'in',
+ inlist: 'inlist',
+ intercept: 'intercept',
+ k1: 'k1',
+ k2: 'k2',
+ k3: 'k3',
+ k4: 'k4',
+ k: 'k',
+ kernelmatrix: 'kernelMatrix',
+ kernelunitlength: 'kernelUnitLength',
+ kerning: 'kerning',
+ keypoints: 'keyPoints',
+ keysplines: 'keySplines',
+ keytimes: 'keyTimes',
+ lengthadjust: 'lengthAdjust',
+ letterspacing: 'letterSpacing',
+ 'letter-spacing': 'letterSpacing',
+ lightingcolor: 'lightingColor',
+ 'lighting-color': 'lightingColor',
+ limitingconeangle: 'limitingConeAngle',
+ local: 'local',
+ markerend: 'markerEnd',
+ 'marker-end': 'markerEnd',
+ markerheight: 'markerHeight',
+ markermid: 'markerMid',
+ 'marker-mid': 'markerMid',
+ markerstart: 'markerStart',
+ 'marker-start': 'markerStart',
+ markerunits: 'markerUnits',
+ markerwidth: 'markerWidth',
+ mask: 'mask',
+ maskcontentunits: 'maskContentUnits',
+ maskunits: 'maskUnits',
+ mathematical: 'mathematical',
+ mode: 'mode',
+ numoctaves: 'numOctaves',
+ offset: 'offset',
+ opacity: 'opacity',
+ operator: 'operator',
+ order: 'order',
+ orient: 'orient',
+ orientation: 'orientation',
+ origin: 'origin',
+ overflow: 'overflow',
+ overlineposition: 'overlinePosition',
+ 'overline-position': 'overlinePosition',
+ overlinethickness: 'overlineThickness',
+ 'overline-thickness': 'overlineThickness',
+ paintorder: 'paintOrder',
+ 'paint-order': 'paintOrder',
+ panose1: 'panose1',
+ 'panose-1': 'panose1',
+ pathlength: 'pathLength',
+ patterncontentunits: 'patternContentUnits',
+ patterntransform: 'patternTransform',
+ patternunits: 'patternUnits',
+ pointerevents: 'pointerEvents',
+ 'pointer-events': 'pointerEvents',
+ points: 'points',
+ pointsatx: 'pointsAtX',
+ pointsaty: 'pointsAtY',
+ pointsatz: 'pointsAtZ',
+ prefix: 'prefix',
+ preservealpha: 'preserveAlpha',
+ preserveaspectratio: 'preserveAspectRatio',
+ primitiveunits: 'primitiveUnits',
+ property: 'property',
+ r: 'r',
+ radius: 'radius',
+ refx: 'refX',
+ refy: 'refY',
+ renderingintent: 'renderingIntent',
+ 'rendering-intent': 'renderingIntent',
+ repeatcount: 'repeatCount',
+ repeatdur: 'repeatDur',
+ requiredextensions: 'requiredExtensions',
+ requiredfeatures: 'requiredFeatures',
+ resource: 'resource',
+ restart: 'restart',
+ result: 'result',
+ results: 'results',
+ rotate: 'rotate',
+ rx: 'rx',
+ ry: 'ry',
+ scale: 'scale',
+ security: 'security',
+ seed: 'seed',
+ shaperendering: 'shapeRendering',
+ 'shape-rendering': 'shapeRendering',
+ slope: 'slope',
+ spacing: 'spacing',
+ specularconstant: 'specularConstant',
+ specularexponent: 'specularExponent',
+ speed: 'speed',
+ spreadmethod: 'spreadMethod',
+ startoffset: 'startOffset',
+ stddeviation: 'stdDeviation',
+ stemh: 'stemh',
+ stemv: 'stemv',
+ stitchtiles: 'stitchTiles',
+ stopcolor: 'stopColor',
+ 'stop-color': 'stopColor',
+ stopopacity: 'stopOpacity',
+ 'stop-opacity': 'stopOpacity',
+ strikethroughposition: 'strikethroughPosition',
+ 'strikethrough-position': 'strikethroughPosition',
+ strikethroughthickness: 'strikethroughThickness',
+ 'strikethrough-thickness': 'strikethroughThickness',
+ string: 'string',
+ stroke: 'stroke',
+ strokedasharray: 'strokeDasharray',
+ 'stroke-dasharray': 'strokeDasharray',
+ strokedashoffset: 'strokeDashoffset',
+ 'stroke-dashoffset': 'strokeDashoffset',
+ strokelinecap: 'strokeLinecap',
+ 'stroke-linecap': 'strokeLinecap',
+ strokelinejoin: 'strokeLinejoin',
+ 'stroke-linejoin': 'strokeLinejoin',
+ strokemiterlimit: 'strokeMiterlimit',
+ 'stroke-miterlimit': 'strokeMiterlimit',
+ strokewidth: 'strokeWidth',
+ 'stroke-width': 'strokeWidth',
+ strokeopacity: 'strokeOpacity',
+ 'stroke-opacity': 'strokeOpacity',
+ suppresscontenteditablewarning: 'suppressContentEditableWarning',
+ suppresshydrationwarning: 'suppressHydrationWarning',
+ surfacescale: 'surfaceScale',
+ systemlanguage: 'systemLanguage',
+ tablevalues: 'tableValues',
+ targetx: 'targetX',
+ targety: 'targetY',
+ textanchor: 'textAnchor',
+ 'text-anchor': 'textAnchor',
+ textdecoration: 'textDecoration',
+ 'text-decoration': 'textDecoration',
+ textlength: 'textLength',
+ textrendering: 'textRendering',
+ 'text-rendering': 'textRendering',
+ to: 'to',
+ transform: 'transform',
+ typeof: 'typeof',
+ u1: 'u1',
+ u2: 'u2',
+ underlineposition: 'underlinePosition',
+ 'underline-position': 'underlinePosition',
+ underlinethickness: 'underlineThickness',
+ 'underline-thickness': 'underlineThickness',
+ unicode: 'unicode',
+ unicodebidi: 'unicodeBidi',
+ 'unicode-bidi': 'unicodeBidi',
+ unicoderange: 'unicodeRange',
+ 'unicode-range': 'unicodeRange',
+ unitsperem: 'unitsPerEm',
+ 'units-per-em': 'unitsPerEm',
+ unselectable: 'unselectable',
+ valphabetic: 'vAlphabetic',
+ 'v-alphabetic': 'vAlphabetic',
+ values: 'values',
+ vectoreffect: 'vectorEffect',
+ 'vector-effect': 'vectorEffect',
+ version: 'version',
+ vertadvy: 'vertAdvY',
+ 'vert-adv-y': 'vertAdvY',
+ vertoriginx: 'vertOriginX',
+ 'vert-origin-x': 'vertOriginX',
+ vertoriginy: 'vertOriginY',
+ 'vert-origin-y': 'vertOriginY',
+ vhanging: 'vHanging',
+ 'v-hanging': 'vHanging',
+ videographic: 'vIdeographic',
+ 'v-ideographic': 'vIdeographic',
+ viewbox: 'viewBox',
+ viewtarget: 'viewTarget',
+ visibility: 'visibility',
+ vmathematical: 'vMathematical',
+ 'v-mathematical': 'vMathematical',
+ vocab: 'vocab',
+ widths: 'widths',
+ wordspacing: 'wordSpacing',
+ 'word-spacing': 'wordSpacing',
+ writingmode: 'writingMode',
+ 'writing-mode': 'writingMode',
+ x1: 'x1',
+ x2: 'x2',
+ x: 'x',
+ xchannelselector: 'xChannelSelector',
+ xheight: 'xHeight',
+ 'x-height': 'xHeight',
+ xlinkactuate: 'xlinkActuate',
+ 'xlink:actuate': 'xlinkActuate',
+ xlinkarcrole: 'xlinkArcrole',
+ 'xlink:arcrole': 'xlinkArcrole',
+ xlinkhref: 'xlinkHref',
+ 'xlink:href': 'xlinkHref',
+ xlinkrole: 'xlinkRole',
+ 'xlink:role': 'xlinkRole',
+ xlinkshow: 'xlinkShow',
+ 'xlink:show': 'xlinkShow',
+ xlinktitle: 'xlinkTitle',
+ 'xlink:title': 'xlinkTitle',
+ xlinktype: 'xlinkType',
+ 'xlink:type': 'xlinkType',
+ xmlbase: 'xmlBase',
+ 'xml:base': 'xmlBase',
+ xmllang: 'xmlLang',
+ 'xml:lang': 'xmlLang',
+ xmlns: 'xmlns',
+ 'xml:space': 'xmlSpace',
+ xmlnsxlink: 'xmlnsXlink',
+ 'xmlns:xlink': 'xmlnsXlink',
+ xmlspace: 'xmlSpace',
+ y1: 'y1',
+ y2: 'y2',
+ y: 'y',
+ ychannelselector: 'yChannelSelector',
+ z: 'z',
+ zoomandpan: 'zoomAndPan'
+};
+
+var validateProperty$1 = function () {};
+
+{
+ var warnedProperties$1 = {};
+ var _hasOwnProperty = Object.prototype.hasOwnProperty;
+ var EVENT_NAME_REGEX = /^on./;
+ var INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
+ var rARIA$1 = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
+ var rARIACamel$1 = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+ validateProperty$1 = function (tagName, name, value, canUseEventSystem) {
+ if (_hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) {
+ return true;
+ }
+
+ var lowerCasedName = name.toLowerCase();
+ if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') {
+ warning$1(false, 'React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.');
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ // We can't rely on the event system being injected on the server.
+ if (canUseEventSystem) {
+ if (registrationNameModules.hasOwnProperty(name)) {
+ return true;
+ }
+ var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null;
+ if (registrationName != null) {
+ warning$1(false, 'Invalid event handler property `%s`. Did you mean `%s`?', name, registrationName);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+ if (EVENT_NAME_REGEX.test(name)) {
+ warning$1(false, 'Unknown event handler property `%s`. It will be ignored.', name);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+ } else if (EVENT_NAME_REGEX.test(name)) {
+ // If no event plugins have been injected, we are in a server environment.
+ // So we can't tell if the event name is correct for sure, but we can filter
+ // out known bad ones like `onclick`. We can't suggest a specific replacement though.
+ if (INVALID_EVENT_NAME_REGEX.test(name)) {
+ warning$1(false, 'Invalid event handler property `%s`. ' + 'React events use the camelCase naming convention, for example `onClick`.', name);
+ }
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ // Let the ARIA attribute hook validate ARIA attributes
+ if (rARIA$1.test(name) || rARIACamel$1.test(name)) {
+ return true;
+ }
+
+ if (lowerCasedName === 'innerhtml') {
+ warning$1(false, 'Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.');
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (lowerCasedName === 'aria') {
+ warning$1(false, 'The `aria` attribute is reserved for future use in React. ' + 'Pass individual `aria-` attributes instead.');
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (lowerCasedName === 'is' && value !== null && value !== undefined && typeof value !== 'string') {
+ warning$1(false, 'Received a `%s` for a string attribute `is`. If this is expected, cast ' + 'the value to a string.', typeof value);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (typeof value === 'number' && isNaN(value)) {
+ warning$1(false, 'Received NaN for the `%s` attribute. If this is expected, cast ' + 'the value to a string.', name);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ var propertyInfo = getPropertyInfo(name);
+ var isReserved = propertyInfo !== null && propertyInfo.type === RESERVED;
+
+ // Known attributes should match the casing specified in the property config.
+ if (possibleStandardNames.hasOwnProperty(lowerCasedName)) {
+ var standardName = possibleStandardNames[lowerCasedName];
+ if (standardName !== name) {
+ warning$1(false, 'Invalid DOM property `%s`. Did you mean `%s`?', name, standardName);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+ } else if (!isReserved && name !== lowerCasedName) {
+ // Unknown attributes should have lowercase casing since that's how they
+ // will be cased anyway with server rendering.
+ warning$1(false, 'React does not recognize the `%s` prop on a DOM element. If you ' + 'intentionally want it to appear in the DOM as a custom ' + 'attribute, spell it as lowercase `%s` instead. ' + 'If you accidentally passed it from a parent component, remove ' + 'it from the DOM element.', name, lowerCasedName);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ if (typeof value === 'boolean' && shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) {
+ if (value) {
+ warning$1(false, 'Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.', value, name, name, value, name);
+ } else {
+ warning$1(false, 'Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', value, name, name, value, name, name, name);
+ }
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ // Now that we've validated casing, do not validate
+ // data types for reserved props
+ if (isReserved) {
+ return true;
+ }
+
+ // Warn when a known attribute is a bad type
+ if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) {
+ warnedProperties$1[name] = true;
+ return false;
+ }
+
+ // Warn when passing the strings 'false' or 'true' into a boolean prop
+ if ((value === 'false' || value === 'true') && propertyInfo !== null && propertyInfo.type === BOOLEAN) {
+ warning$1(false, 'Received the string `%s` for the boolean attribute `%s`. ' + '%s ' + 'Did you mean %s={%s}?', value, name, value === 'false' ? 'The browser will interpret it as a truthy value.' : 'Although this works, it will not work as expected if you pass the string "false".', name, value);
+ warnedProperties$1[name] = true;
+ return true;
+ }
+
+ return true;
+ };
+}
+
+var warnUnknownProperties = function (type, props, canUseEventSystem) {
+ var unknownProps = [];
+ for (var key in props) {
+ var isValid = validateProperty$1(type, key, props[key], canUseEventSystem);
+ if (!isValid) {
+ unknownProps.push(key);
+ }
+ }
+
+ var unknownPropString = unknownProps.map(function (prop) {
+ return '`' + prop + '`';
+ }).join(', ');
+ if (unknownProps.length === 1) {
+ warning$1(false, 'Invalid value for prop %s on <%s> tag. Either remove it from the element, ' + 'or pass a string or number value to keep it in the DOM. ' + 'For details, see https://fb.me/react-attribute-behavior', unknownPropString, type);
+ } else if (unknownProps.length > 1) {
+ warning$1(false, 'Invalid values for props %s on <%s> tag. Either remove them from the element, ' + 'or pass a string or number value to keep them in the DOM. ' + 'For details, see https://fb.me/react-attribute-behavior', unknownPropString, type);
+ }
+};
+
+function validateProperties$2(type, props, canUseEventSystem) {
+ if (isCustomComponent(type, props)) {
+ return;
+ }
+ warnUnknownProperties(type, props, canUseEventSystem);
+}
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+// Based on reading the React.Children implementation. TODO: type this somewhere?
+
+var toArray = React.Children.toArray;
+
+// This is only used in DEV.
+// Each entry is `this.stack` from a currently executing renderer instance.
+// (There may be more than one because ReactDOMServer is reentrant).
+// Each stack is an array of frames which may contain nested stacks of elements.
+var currentDebugStacks = [];
+
+var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+var ReactDebugCurrentFrame = void 0;
+var prevGetCurrentStackImpl = null;
+var getCurrentServerStackImpl = function () {
+ return '';
+};
+var describeStackFrame = function (element) {
+ return '';
+};
+
+var validatePropertiesInDevelopment = function (type, props) {};
+var pushCurrentDebugStack = function (stack) {};
+var pushElementToDebugStack = function (element) {};
+var popCurrentDebugStack = function () {};
+var hasWarnedAboutUsingContextAsConsumer = false;
+
+{
+ ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+
+ validatePropertiesInDevelopment = function (type, props) {
+ validateProperties(type, props);
+ validateProperties$1(type, props);
+ validateProperties$2(type, props, /* canUseEventSystem */false);
+ };
+
+ describeStackFrame = function (element) {
+ var source = element._source;
+ var type = element.type;
+ var name = getComponentName(type);
+ var ownerName = null;
+ return describeComponentFrame(name, source, ownerName);
+ };
+
+ pushCurrentDebugStack = function (stack) {
+ currentDebugStacks.push(stack);
+
+ if (currentDebugStacks.length === 1) {
+ // We are entering a server renderer.
+ // Remember the previous (e.g. client) global stack implementation.
+ prevGetCurrentStackImpl = ReactDebugCurrentFrame.getCurrentStack;
+ ReactDebugCurrentFrame.getCurrentStack = getCurrentServerStackImpl;
+ }
+ };
+
+ pushElementToDebugStack = function (element) {
+ // For the innermost executing ReactDOMServer call,
+ var stack = currentDebugStacks[currentDebugStacks.length - 1];
+ // Take the innermost executing frame (e.g. <Foo>),
+ var frame = stack[stack.length - 1];
+ // and record that it has one more element associated with it.
+ frame.debugElementStack.push(element);
+ // We only need this because we tail-optimize single-element
+ // children and directly handle them in an inner loop instead of
+ // creating separate frames for them.
+ };
+
+ popCurrentDebugStack = function () {
+ currentDebugStacks.pop();
+
+ if (currentDebugStacks.length === 0) {
+ // We are exiting the server renderer.
+ // Restore the previous (e.g. client) global stack implementation.
+ ReactDebugCurrentFrame.getCurrentStack = prevGetCurrentStackImpl;
+ prevGetCurrentStackImpl = null;
+ }
+ };
+
+ getCurrentServerStackImpl = function () {
+ if (currentDebugStacks.length === 0) {
+ // Nothing is currently rendering.
+ return '';
+ }
+ // ReactDOMServer is reentrant so there may be multiple calls at the same time.
+ // Take the frames from the innermost call which is the last in the array.
+ var frames = currentDebugStacks[currentDebugStacks.length - 1];
+ var stack = '';
+ // Go through every frame in the stack from the innermost one.
+ for (var i = frames.length - 1; i >= 0; i--) {
+ var frame = frames[i];
+ // Every frame might have more than one debug element stack entry associated with it.
+ // This is because single-child nesting doesn't create materialized frames.
+ // Instead it would push them through `pushElementToDebugStack()`.
+ var _debugElementStack = frame.debugElementStack;
+ for (var ii = _debugElementStack.length - 1; ii >= 0; ii--) {
+ stack += describeStackFrame(_debugElementStack[ii]);
+ }
+ }
+ return stack;
+ };
+}
+
+var didWarnDefaultInputValue = false;
+var didWarnDefaultChecked = false;
+var didWarnDefaultSelectValue = false;
+var didWarnDefaultTextareaValue = false;
+var didWarnInvalidOptionChildren = false;
+var didWarnAboutNoopUpdateForComponent = {};
+var didWarnAboutBadClass = {};
+var didWarnAboutDeprecatedWillMount = {};
+var didWarnAboutUndefinedDerivedState = {};
+var didWarnAboutUninitializedState = {};
+var valuePropNames = ['value', 'defaultValue'];
+var newlineEatingTags = {
+ listing: true,
+ pre: true,
+ textarea: true
+};
+
+// We accept any tag to be rendered but since this gets injected into arbitrary
+// HTML, we want to make sure that it's a safe tag.
+// http://www.w3.org/TR/REC-xml/#NT-Name
+var VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset
+var validatedTagCache = {};
+function validateDangerousTag(tag) {
+ if (!validatedTagCache.hasOwnProperty(tag)) {
+ !VALID_TAG_REGEX.test(tag) ? invariant(false, 'Invalid tag: %s', tag) : void 0;
+ validatedTagCache[tag] = true;
+ }
+}
+
+var styleNameCache = {};
+var processStyleName = function (styleName) {
+ if (styleNameCache.hasOwnProperty(styleName)) {
+ return styleNameCache[styleName];
+ }
+ var result = hyphenateStyleName(styleName);
+ styleNameCache[styleName] = result;
+ return result;
+};
+
+function createMarkupForStyles(styles) {
+ var serialized = '';
+ var delimiter = '';
+ for (var styleName in styles) {
+ if (!styles.hasOwnProperty(styleName)) {
+ continue;
+ }
+ var isCustomProperty = styleName.indexOf('--') === 0;
+ var styleValue = styles[styleName];
+ {
+ if (!isCustomProperty) {
+ warnValidStyle$1(styleName, styleValue);
+ }
+ }
+ if (styleValue != null) {
+ serialized += delimiter + processStyleName(styleName) + ':';
+ serialized += dangerousStyleValue(styleName, styleValue, isCustomProperty);
+
+ delimiter = ';';
+ }
+ }
+ return serialized || null;
+}
+
+function warnNoop(publicInstance, callerName) {
+ {
+ var _constructor = publicInstance.constructor;
+ var componentName = _constructor && getComponentName(_constructor) || 'ReactClass';
+ var warningKey = componentName + '.' + callerName;
+ if (didWarnAboutNoopUpdateForComponent[warningKey]) {
+ return;
+ }
+
+ warningWithoutStack$1(false, '%s(...): Can only update a mounting component. ' + 'This usually means you called %s() outside componentWillMount() on the server. ' + 'This is a no-op.\n\nPlease check the code for the %s component.', callerName, callerName, componentName);
+ didWarnAboutNoopUpdateForComponent[warningKey] = true;
+ }
+}
+
+function shouldConstruct(Component) {
+ return Component.prototype && Component.prototype.isReactComponent;
+}
+
+function getNonChildrenInnerMarkup(props) {
+ var innerHTML = props.dangerouslySetInnerHTML;
+ if (innerHTML != null) {
+ if (innerHTML.__html != null) {
+ return innerHTML.__html;
+ }
+ } else {
+ var content = props.children;
+ if (typeof content === 'string' || typeof content === 'number') {
+ return escapeTextForBrowser(content);
+ }
+ }
+ return null;
+}
+
+function flattenTopLevelChildren(children) {
+ if (!React.isValidElement(children)) {
+ return toArray(children);
+ }
+ var element = children;
+ if (element.type !== REACT_FRAGMENT_TYPE) {
+ return [element];
+ }
+ var fragmentChildren = element.props.children;
+ if (!React.isValidElement(fragmentChildren)) {
+ return toArray(fragmentChildren);
+ }
+ var fragmentChildElement = fragmentChildren;
+ return [fragmentChildElement];
+}
+
+function flattenOptionChildren(children) {
+ if (children === undefined || children === null) {
+ return children;
+ }
+ var content = '';
+ // Flatten children and warn if they aren't strings or numbers;
+ // invalid types are ignored.
+ React.Children.forEach(children, function (child) {
+ if (child == null) {
+ return;
+ }
+ content += child;
+ {
+ if (!didWarnInvalidOptionChildren && typeof child !== 'string' && typeof child !== 'number') {
+ didWarnInvalidOptionChildren = true;
+ warning$1(false, 'Only strings and numbers are supported as <option> children.');
+ }
+ }
+ });
+ return content;
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var STYLE = 'style';
+var RESERVED_PROPS = {
+ children: null,
+ dangerouslySetInnerHTML: null,
+ suppressContentEditableWarning: null,
+ suppressHydrationWarning: null
+};
+
+function createOpenTagMarkup(tagVerbatim, tagLowercase, props, namespace, makeStaticMarkup, isRootElement) {
+ var ret = '<' + tagVerbatim;
+
+ for (var propKey in props) {
+ if (!hasOwnProperty.call(props, propKey)) {
+ continue;
+ }
+ var propValue = props[propKey];
+ if (propValue == null) {
+ continue;
+ }
+ if (propKey === STYLE) {
+ propValue = createMarkupForStyles(propValue);
+ }
+ var markup = null;
+ if (isCustomComponent(tagLowercase, props)) {
+ if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
+ markup = createMarkupForCustomAttribute(propKey, propValue);
+ }
+ } else {
+ markup = createMarkupForProperty(propKey, propValue);
+ }
+ if (markup) {
+ ret += ' ' + markup;
+ }
+ }
+
+ // For static pages, no need to put React ID and checksum. Saves lots of
+ // bytes.
+ if (makeStaticMarkup) {
+ return ret;
+ }
+
+ if (isRootElement) {
+ ret += ' ' + createMarkupForRoot();
+ }
+ return ret;
+}
+
+function validateRenderResult(child, type) {
+ if (child === undefined) {
+ invariant(false, '%s(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.', getComponentName(type) || 'Component');
+ }
+}
+
+function resolve(child, context, threadID) {
+ while (React.isValidElement(child)) {
+ // Safe because we just checked it's an element.
+ var element = child;
+ var Component = element.type;
+ {
+ pushElementToDebugStack(element);
+ }
+ if (typeof Component !== 'function') {
+ break;
+ }
+ processChild(element, Component);
+ }
+
+ // Extra closure so queue and replace can be captured properly
+ function processChild(element, Component) {
+ var publicContext = processContext(Component, context, threadID);
+
+ var queue = [];
+ var replace = false;
+ var updater = {
+ isMounted: function (publicInstance) {
+ return false;
+ },
+ enqueueForceUpdate: function (publicInstance) {
+ if (queue === null) {
+ warnNoop(publicInstance, 'forceUpdate');
+ return null;
+ }
+ },
+ enqueueReplaceState: function (publicInstance, completeState) {
+ replace = true;
+ queue = [completeState];
+ },
+ enqueueSetState: function (publicInstance, currentPartialState) {
+ if (queue === null) {
+ warnNoop(publicInstance, 'setState');
+ return null;
+ }
+ queue.push(currentPartialState);
+ }
+ };
+
+ var inst = void 0;
+ if (shouldConstruct(Component)) {
+ inst = new Component(element.props, publicContext, updater);
+
+ if (typeof Component.getDerivedStateFromProps === 'function') {
+ {
+ if (inst.state === null || inst.state === undefined) {
+ var componentName = getComponentName(Component) || 'Unknown';
+ if (!didWarnAboutUninitializedState[componentName]) {
+ warningWithoutStack$1(false, '`%s` uses `getDerivedStateFromProps` but its initial state is ' + '%s. This is not recommended. Instead, define the initial state by ' + 'assigning an object to `this.state` in the constructor of `%s`. ' + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.', componentName, inst.state === null ? 'null' : 'undefined', componentName);
+ didWarnAboutUninitializedState[componentName] = true;
+ }
+ }
+ }
+
+ var partialState = Component.getDerivedStateFromProps.call(null, element.props, inst.state);
+
+ {
+ if (partialState === undefined) {
+ var _componentName = getComponentName(Component) || 'Unknown';
+ if (!didWarnAboutUndefinedDerivedState[_componentName]) {
+ warningWithoutStack$1(false, '%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' + 'You have returned undefined.', _componentName);
+ didWarnAboutUndefinedDerivedState[_componentName] = true;
+ }
+ }
+ }
+
+ if (partialState != null) {
+ inst.state = _assign({}, inst.state, partialState);
+ }
+ }
+ } else {
+ {
+ if (Component.prototype && typeof Component.prototype.render === 'function') {
+ var _componentName2 = getComponentName(Component) || 'Unknown';
+
+ if (!didWarnAboutBadClass[_componentName2]) {
+ warningWithoutStack$1(false, "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + 'This is likely to cause errors. Change %s to extend React.Component instead.', _componentName2, _componentName2);
+ didWarnAboutBadClass[_componentName2] = true;
+ }
+ }
+ }
+ var componentIdentity = {};
+ prepareToUseHooks(componentIdentity);
+ inst = Component(element.props, publicContext, updater);
+ inst = finishHooks(Component, element.props, inst, publicContext);
+
+ if (inst == null || inst.render == null) {
+ child = inst;
+ validateRenderResult(child, Component);
+ return;
+ }
+ }
+
+ inst.props = element.props;
+ inst.context = publicContext;
+ inst.updater = updater;
+
+ var initialState = inst.state;
+ if (initialState === undefined) {
+ inst.state = initialState = null;
+ }
+ if (typeof inst.UNSAFE_componentWillMount === 'function' || typeof inst.componentWillMount === 'function') {
+ if (typeof inst.componentWillMount === 'function') {
+ {
+ if (warnAboutDeprecatedLifecycles && inst.componentWillMount.__suppressDeprecationWarning !== true) {
+ var _componentName3 = getComponentName(Component) || 'Unknown';
+
+ if (!didWarnAboutDeprecatedWillMount[_componentName3]) {
+ lowPriorityWarning$1(false, '%s: componentWillMount() is deprecated and will be ' + 'removed in the next major version. Read about the motivations ' + 'behind this change: ' + 'https://fb.me/react-async-component-lifecycle-hooks' + '\n\n' + 'As a temporary workaround, you can rename to ' + 'UNSAFE_componentWillMount instead.', _componentName3);
+ didWarnAboutDeprecatedWillMount[_componentName3] = true;
+ }
+ }
+ }
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
+ if (typeof Component.getDerivedStateFromProps !== 'function') {
+ inst.componentWillMount();
+ }
+ }
+ if (typeof inst.UNSAFE_componentWillMount === 'function' && typeof Component.getDerivedStateFromProps !== 'function') {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
+ inst.UNSAFE_componentWillMount();
+ }
+ if (queue.length) {
+ var oldQueue = queue;
+ var oldReplace = replace;
+ queue = null;
+ replace = false;
+
+ if (oldReplace && oldQueue.length === 1) {
+ inst.state = oldQueue[0];
+ } else {
+ var nextState = oldReplace ? oldQueue[0] : inst.state;
+ var dontMutate = true;
+ for (var i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
+ var partial = oldQueue[i];
+ var _partialState = typeof partial === 'function' ? partial.call(inst, nextState, element.props, publicContext) : partial;
+ if (_partialState != null) {
+ if (dontMutate) {
+ dontMutate = false;
+ nextState = _assign({}, nextState, _partialState);
+ } else {
+ _assign(nextState, _partialState);
+ }
+ }
+ }
+ inst.state = nextState;
+ }
+ } else {
+ queue = null;
+ }
+ }
+ child = inst.render();
+
+ {
+ if (child === undefined && inst.render._isMockFunction) {
+ // This is probably bad practice. Consider warning here and
+ // deprecating this convenience.
+ child = null;
+ }
+ }
+ validateRenderResult(child, Component);
+
+ var childContext = void 0;
+ if (typeof inst.getChildContext === 'function') {
+ var childContextTypes = Component.childContextTypes;
+ if (typeof childContextTypes === 'object') {
+ childContext = inst.getChildContext();
+ for (var contextKey in childContext) {
+ !(contextKey in childContextTypes) ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', getComponentName(Component) || 'Unknown', contextKey) : void 0;
+ }
+ } else {
+ warningWithoutStack$1(false, '%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', getComponentName(Component) || 'Unknown');
+ }
+ }
+ if (childContext) {
+ context = _assign({}, context, childContext);
+ }
+ }
+ return { child: child, context: context };
+}
+
+var ReactDOMServerRenderer = function () {
+ // DEV-only
+
+ // TODO: type this more strictly:
+ function ReactDOMServerRenderer(children, makeStaticMarkup) {
+ _classCallCheck(this, ReactDOMServerRenderer);
+
+ var flatChildren = flattenTopLevelChildren(children);
+
+ var topFrame = {
+ type: null,
+ // Assume all trees start in the HTML namespace (not totally true, but
+ // this is what we did historically)
+ domNamespace: Namespaces.html,
+ children: flatChildren,
+ childIndex: 0,
+ context: emptyObject,
+ footer: ''
+ };
+ {
+ topFrame.debugElementStack = [];
+ }
+ this.threadID = allocThreadID();
+ this.stack = [topFrame];
+ this.exhausted = false;
+ this.currentSelectValue = null;
+ this.previousWasTextNode = false;
+ this.makeStaticMarkup = makeStaticMarkup;
+ this.suspenseDepth = 0;
+
+ // Context (new API)
+ this.contextIndex = -1;
+ this.contextStack = [];
+ this.contextValueStack = [];
+ {
+ this.contextProviderStack = [];
+ }
+ }
+
+ ReactDOMServerRenderer.prototype.destroy = function destroy() {
+ if (!this.exhausted) {
+ this.exhausted = true;
+ this.clearProviders();
+ freeThreadID(this.threadID);
+ }
+ };
+
+ /**
+ * Note: We use just two stacks regardless of how many context providers you have.
+ * Providers are always popped in the reverse order to how they were pushed
+ * so we always know on the way down which provider you'll encounter next on the way up.
+ * On the way down, we push the current provider, and its context value *before*
+ * we mutated it, onto the stacks. Therefore, on the way up, we always know which
+ * provider needs to be "restored" to which value.
+ * https://github.com/facebook/react/pull/12985#issuecomment-396301248
+ */
+
+ ReactDOMServerRenderer.prototype.pushProvider = function pushProvider(provider) {
+ var index = ++this.contextIndex;
+ var context = provider.type._context;
+ var threadID = this.threadID;
+ validateContextBounds(context, threadID);
+ var previousValue = context[threadID];
+
+ // Remember which value to restore this context to on our way up.
+ this.contextStack[index] = context;
+ this.contextValueStack[index] = previousValue;
+ {
+ // Only used for push/pop mismatch warnings.
+ this.contextProviderStack[index] = provider;
+ }
+
+ // Mutate the current value.
+ context[threadID] = provider.props.value;
+ };
+
+ ReactDOMServerRenderer.prototype.popProvider = function popProvider(provider) {
+ var index = this.contextIndex;
+ {
+ !(index > -1 && provider === this.contextProviderStack[index]) ? warningWithoutStack$1(false, 'Unexpected pop.') : void 0;
+ }
+
+ var context = this.contextStack[index];
+ var previousValue = this.contextValueStack[index];
+
+ // "Hide" these null assignments from Flow by using `any`
+ // because conceptually they are deletions--as long as we
+ // promise to never access values beyond `this.contextIndex`.
+ this.contextStack[index] = null;
+ this.contextValueStack[index] = null;
+ {
+ this.contextProviderStack[index] = null;
+ }
+ this.contextIndex--;
+
+ // Restore to the previous value we stored as we were walking down.
+ // We've already verified that this context has been expanded to accommodate
+ // this thread id, so we don't need to do it again.
+ context[this.threadID] = previousValue;
+ };
+
+ ReactDOMServerRenderer.prototype.clearProviders = function clearProviders() {
+ // Restore any remaining providers on the stack to previous values
+ for (var index = this.contextIndex; index >= 0; index--) {
+ var _context = this.contextStack[index];
+ var previousValue = this.contextValueStack[index];
+ _context[this.threadID] = previousValue;
+ }
+ };
+
+ ReactDOMServerRenderer.prototype.read = function read(bytes) {
+ if (this.exhausted) {
+ return null;
+ }
+
+ var prevThreadID = currentThreadID;
+ setCurrentThreadID(this.threadID);
+ var prevDispatcher = ReactCurrentDispatcher.current;
+ ReactCurrentDispatcher.current = Dispatcher;
+ try {
+ // Markup generated within <Suspense> ends up buffered until we know
+ // nothing in that boundary suspended
+ var out = [''];
+ var suspended = false;
+ while (out[0].length < bytes) {
+ if (this.stack.length === 0) {
+ this.exhausted = true;
+ freeThreadID(this.threadID);
+ break;
+ }
+ var frame = this.stack[this.stack.length - 1];
+ if (suspended || frame.childIndex >= frame.children.length) {
+ var _footer = frame.footer;
+ if (_footer !== '') {
+ this.previousWasTextNode = false;
+ }
+ this.stack.pop();
+ if (frame.type === 'select') {
+ this.currentSelectValue = null;
+ } else if (frame.type != null && frame.type.type != null && frame.type.type.$$typeof === REACT_PROVIDER_TYPE) {
+ var provider = frame.type;
+ this.popProvider(provider);
+ } else if (frame.type === REACT_SUSPENSE_TYPE) {
+ this.suspenseDepth--;
+ var buffered = out.pop();
+
+ if (suspended) {
+ suspended = false;
+ // If rendering was suspended at this boundary, render the fallbackFrame
+ var _fallbackFrame = frame.fallbackFrame;
+ !_fallbackFrame ? invariant(false, 'suspense fallback not found, something is broken') : void 0;
+ this.stack.push(_fallbackFrame);
+ // Skip flushing output since we're switching to the fallback
+ continue;
+ } else {
+ out[this.suspenseDepth] += buffered;
+ }
+ }
+
+ // Flush output
+ out[this.suspenseDepth] += _footer;
+ continue;
+ }
+ var child = frame.children[frame.childIndex++];
+
+ var outBuffer = '';
+ {
+ pushCurrentDebugStack(this.stack);
+ // We're starting work on this frame, so reset its inner stack.
+ frame.debugElementStack.length = 0;
+ }
+ try {
+ outBuffer += this.render(child, frame.context, frame.domNamespace);
+ } catch (err) {
+ if (enableSuspenseServerRenderer && typeof err.then === 'function') {
+ suspended = true;
+ } else {
+ throw err;
+ }
+ } finally {
+ {
+ popCurrentDebugStack();
+ }
+ }
+ if (out.length <= this.suspenseDepth) {
+ out.push('');
+ }
+ out[this.suspenseDepth] += outBuffer;
+ }
+ return out[0];
+ } finally {
+ ReactCurrentDispatcher.current = prevDispatcher;
+ setCurrentThreadID(prevThreadID);
+ }
+ };
+
+ ReactDOMServerRenderer.prototype.render = function render(child, context, parentNamespace) {
+ if (typeof child === 'string' || typeof child === 'number') {
+ var text = '' + child;
+ if (text === '') {
+ return '';
+ }
+ if (this.makeStaticMarkup) {
+ return escapeTextForBrowser(text);
+ }
+ if (this.previousWasTextNode) {
+ return '<!-- -->' + escapeTextForBrowser(text);
+ }
+ this.previousWasTextNode = true;
+ return escapeTextForBrowser(text);
+ } else {
+ var nextChild = void 0;
+
+ var _resolve = resolve(child, context, this.threadID);
+
+ nextChild = _resolve.child;
+ context = _resolve.context;
+
+ if (nextChild === null || nextChild === false) {
+ return '';
+ } else if (!React.isValidElement(nextChild)) {
+ if (nextChild != null && nextChild.$$typeof != null) {
+ // Catch unexpected special types early.
+ var $$typeof = nextChild.$$typeof;
+ !($$typeof !== REACT_PORTAL_TYPE) ? invariant(false, 'Portals are not currently supported by the server renderer. Render them conditionally so that they only appear on the client render.') : void 0;
+ // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type.
+ invariant(false, 'Unknown element-like object type: %s. This is likely a bug in React. Please file an issue.', $$typeof.toString());
+ }
+ var nextChildren = toArray(nextChild);
+ var frame = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: nextChildren,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ {
+ frame.debugElementStack = [];
+ }
+ this.stack.push(frame);
+ return '';
+ }
+ // Safe because we just checked it's an element.
+ var nextElement = nextChild;
+ var elementType = nextElement.type;
+
+ if (typeof elementType === 'string') {
+ return this.renderDOM(nextElement, context, parentNamespace);
+ }
+
+ switch (elementType) {
+ case REACT_STRICT_MODE_TYPE:
+ case REACT_CONCURRENT_MODE_TYPE:
+ case REACT_PROFILER_TYPE:
+ case REACT_FRAGMENT_TYPE:
+ {
+ var _nextChildren = toArray(nextChild.props.children);
+ var _frame = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ {
+ _frame.debugElementStack = [];
+ }
+ this.stack.push(_frame);
+ return '';
+ }
+ case REACT_SUSPENSE_TYPE:
+ {
+ if (enableSuspenseServerRenderer) {
+ var fallback = nextChild.props.fallback;
+ if (fallback === undefined) {
+ // If there is no fallback, then this just behaves as a fragment.
+ var _nextChildren3 = toArray(nextChild.props.children);
+ var _frame3 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren3,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ {
+ _frame3.debugElementStack = [];
+ }
+ this.stack.push(_frame3);
+ return '';
+ }
+ var fallbackChildren = toArray(fallback);
+ var _nextChildren2 = toArray(nextChild.props.children);
+ var _fallbackFrame2 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: fallbackChildren,
+ childIndex: 0,
+ context: context,
+ footer: '',
+ out: ''
+ };
+ var _frame2 = {
+ fallbackFrame: _fallbackFrame2,
+ type: REACT_SUSPENSE_TYPE,
+ domNamespace: parentNamespace,
+ children: _nextChildren2,
+ childIndex: 0,
+ context: context,
+ footer: '<!--/$-->'
+ };
+ {
+ _frame2.debugElementStack = [];
+ _fallbackFrame2.debugElementStack = [];
+ }
+ this.stack.push(_frame2);
+ this.suspenseDepth++;
+ return '<!--$-->';
+ } else {
+ invariant(false, 'ReactDOMServer does not yet support Suspense.');
+ }
+ }
+ // eslint-disable-next-line-no-fallthrough
+ default:
+ break;
+ }
+ if (typeof elementType === 'object' && elementType !== null) {
+ switch (elementType.$$typeof) {
+ case REACT_FORWARD_REF_TYPE:
+ {
+ var element = nextChild;
+ var _nextChildren4 = void 0;
+ var componentIdentity = {};
+ prepareToUseHooks(componentIdentity);
+ _nextChildren4 = elementType.render(element.props, element.ref);
+ _nextChildren4 = finishHooks(elementType.render, element.props, _nextChildren4, element.ref);
+ _nextChildren4 = toArray(_nextChildren4);
+ var _frame4 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren4,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ {
+ _frame4.debugElementStack = [];
+ }
+ this.stack.push(_frame4);
+ return '';
+ }
+ case REACT_MEMO_TYPE:
+ {
+ var _element = nextChild;
+ var _nextChildren5 = [React.createElement(elementType.type, _assign({ ref: _element.ref }, _element.props))];
+ var _frame5 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren5,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ {
+ _frame5.debugElementStack = [];
+ }
+ this.stack.push(_frame5);
+ return '';
+ }
+ case REACT_PROVIDER_TYPE:
+ {
+ var provider = nextChild;
+ var nextProps = provider.props;
+ var _nextChildren6 = toArray(nextProps.children);
+ var _frame6 = {
+ type: provider,
+ domNamespace: parentNamespace,
+ children: _nextChildren6,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ {
+ _frame6.debugElementStack = [];
+ }
+
+ this.pushProvider(provider);
+
+ this.stack.push(_frame6);
+ return '';
+ }
+ case REACT_CONTEXT_TYPE:
+ {
+ var reactContext = nextChild.type;
+ // The logic below for Context differs depending on PROD or DEV mode. In
+ // DEV mode, we create a separate object for Context.Consumer that acts
+ // like a proxy to Context. This proxy object adds unnecessary code in PROD
+ // so we use the old behaviour (Context.Consumer references Context) to
+ // reduce size and overhead. The separate object references context via
+ // a property called "_context", which also gives us the ability to check
+ // in DEV mode if this property exists or not and warn if it does not.
+ {
+ if (reactContext._context === undefined) {
+ // This may be because it's a Context (rather than a Consumer).
+ // Or it may be because it's older React where they're the same thing.
+ // We only want to warn if we're sure it's a new React.
+ if (reactContext !== reactContext.Consumer) {
+ if (!hasWarnedAboutUsingContextAsConsumer) {
+ hasWarnedAboutUsingContextAsConsumer = true;
+ warning$1(false, 'Rendering <Context> directly is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?');
+ }
+ }
+ } else {
+ reactContext = reactContext._context;
+ }
+ }
+ var _nextProps = nextChild.props;
+ var threadID = this.threadID;
+ validateContextBounds(reactContext, threadID);
+ var nextValue = reactContext[threadID];
+
+ var _nextChildren7 = toArray(_nextProps.children(nextValue));
+ var _frame7 = {
+ type: nextChild,
+ domNamespace: parentNamespace,
+ children: _nextChildren7,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ {
+ _frame7.debugElementStack = [];
+ }
+ this.stack.push(_frame7);
+ return '';
+ }
+ case REACT_LAZY_TYPE:
+ invariant(false, 'ReactDOMServer does not yet support lazy-loaded components.');
+ }
+ }
+
+ var info = '';
+ {
+ var owner = nextElement._owner;
+ if (elementType === undefined || typeof elementType === 'object' && elementType !== null && Object.keys(elementType).length === 0) {
+ info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and " + 'named imports.';
+ }
+ var ownerName = owner ? getComponentName(owner) : null;
+ if (ownerName) {
+ info += '\n\nCheck the render method of `' + ownerName + '`.';
+ }
+ }
+ invariant(false, 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s', elementType == null ? elementType : typeof elementType, info);
+ }
+ };
+
+ ReactDOMServerRenderer.prototype.renderDOM = function renderDOM(element, context, parentNamespace) {
+ var tag = element.type.toLowerCase();
+
+ var namespace = parentNamespace;
+ if (parentNamespace === Namespaces.html) {
+ namespace = getIntrinsicNamespace(tag);
+ }
+
+ {
+ if (namespace === Namespaces.html) {
+ // Should this check be gated by parent namespace? Not sure we want to
+ // allow <SVG> or <mATH>.
+ !(tag === element.type) ? warning$1(false, '<%s /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.', element.type) : void 0;
+ }
+ }
+
+ validateDangerousTag(tag);
+
+ var props = element.props;
+ if (tag === 'input') {
+ {
+ ReactControlledValuePropTypes.checkPropTypes('input', props);
+
+ if (props.checked !== undefined && props.defaultChecked !== undefined && !didWarnDefaultChecked) {
+ warning$1(false, '%s contains an input of type %s with both checked and defaultChecked props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the checked prop, or the defaultChecked prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', 'A component', props.type);
+ didWarnDefaultChecked = true;
+ }
+ if (props.value !== undefined && props.defaultValue !== undefined && !didWarnDefaultInputValue) {
+ warning$1(false, '%s contains an input of type %s with both value and defaultValue props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components', 'A component', props.type);
+ didWarnDefaultInputValue = true;
+ }
+ }
+
+ props = _assign({
+ type: undefined
+ }, props, {
+ defaultChecked: undefined,
+ defaultValue: undefined,
+ value: props.value != null ? props.value : props.defaultValue,
+ checked: props.checked != null ? props.checked : props.defaultChecked
+ });
+ } else if (tag === 'textarea') {
+ {
+ ReactControlledValuePropTypes.checkPropTypes('textarea', props);
+ if (props.value !== undefined && props.defaultValue !== undefined && !didWarnDefaultTextareaValue) {
+ warning$1(false, 'Textarea elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled textarea ' + 'and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components');
+ didWarnDefaultTextareaValue = true;
+ }
+ }
+
+ var initialValue = props.value;
+ if (initialValue == null) {
+ var defaultValue = props.defaultValue;
+ // TODO (yungsters): Remove support for children content in <textarea>.
+ var textareaChildren = props.children;
+ if (textareaChildren != null) {
+ {
+ warning$1(false, 'Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.');
+ }
+ !(defaultValue == null) ? invariant(false, 'If you supply `defaultValue` on a <textarea>, do not pass children.') : void 0;
+ if (Array.isArray(textareaChildren)) {
+ !(textareaChildren.length <= 1) ? invariant(false, '<textarea> can only have at most one child.') : void 0;
+ textareaChildren = textareaChildren[0];
+ }
+
+ defaultValue = '' + textareaChildren;
+ }
+ if (defaultValue == null) {
+ defaultValue = '';
+ }
+ initialValue = defaultValue;
+ }
+
+ props = _assign({}, props, {
+ value: undefined,
+ children: '' + initialValue
+ });
+ } else if (tag === 'select') {
+ {
+ ReactControlledValuePropTypes.checkPropTypes('select', props);
+
+ for (var i = 0; i < valuePropNames.length; i++) {
+ var propName = valuePropNames[i];
+ if (props[propName] == null) {
+ continue;
+ }
+ var isArray = Array.isArray(props[propName]);
+ if (props.multiple && !isArray) {
+ warning$1(false, 'The `%s` prop supplied to <select> must be an array if ' + '`multiple` is true.', propName);
+ } else if (!props.multiple && isArray) {
+ warning$1(false, 'The `%s` prop supplied to <select> must be a scalar ' + 'value if `multiple` is false.', propName);
+ }
+ }
+
+ if (props.value !== undefined && props.defaultValue !== undefined && !didWarnDefaultSelectValue) {
+ warning$1(false, 'Select elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled select ' + 'element and remove one of these props. More info: ' + 'https://fb.me/react-controlled-components');
+ didWarnDefaultSelectValue = true;
+ }
+ }
+ this.currentSelectValue = props.value != null ? props.value : props.defaultValue;
+ props = _assign({}, props, {
+ value: undefined
+ });
+ } else if (tag === 'option') {
+ var selected = null;
+ var selectValue = this.currentSelectValue;
+ var optionChildren = flattenOptionChildren(props.children);
+ if (selectValue != null) {
+ var value = void 0;
+ if (props.value != null) {
+ value = props.value + '';
+ } else {
+ value = optionChildren;
+ }
+ selected = false;
+ if (Array.isArray(selectValue)) {
+ // multiple
+ for (var j = 0; j < selectValue.length; j++) {
+ if ('' + selectValue[j] === value) {
+ selected = true;
+ break;
+ }
+ }
+ } else {
+ selected = '' + selectValue === value;
+ }
+
+ props = _assign({
+ selected: undefined,
+ children: undefined
+ }, props, {
+ selected: selected,
+ children: optionChildren
+ });
+ }
+ }
+
+ {
+ validatePropertiesInDevelopment(tag, props);
+ }
+
+ assertValidProps(tag, props);
+
+ var out = createOpenTagMarkup(element.type, tag, props, namespace, this.makeStaticMarkup, this.stack.length === 1);
+ var footer = '';
+ if (omittedCloseTags.hasOwnProperty(tag)) {
+ out += '/>';
+ } else {
+ out += '>';
+ footer = '</' + element.type + '>';
+ }
+ var children = void 0;
+ var innerMarkup = getNonChildrenInnerMarkup(props);
+ if (innerMarkup != null) {
+ children = [];
+ if (newlineEatingTags[tag] && innerMarkup.charAt(0) === '\n') {
+ // text/html ignores the first character in these tags if it's a newline
+ // Prefer to break application/xml over text/html (for now) by adding
+ // a newline specifically to get eaten by the parser. (Alternately for
+ // textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first
+ // \r is normalized out by HTMLTextAreaElement#value.)
+ // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
+ // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
+ // See: <http://www.w3.org/TR/html5/syntax.html#newlines>
+ // See: Parsing of "textarea" "listing" and "pre" elements
+ // from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
+ out += '\n';
+ }
+ out += innerMarkup;
+ } else {
+ children = toArray(props.children);
+ }
+ var frame = {
+ domNamespace: getChildNamespace(parentNamespace, element.type),
+ type: tag,
+ children: children,
+ childIndex: 0,
+ context: context,
+ footer: footer
+ };
+ {
+ frame.debugElementStack = [];
+ }
+ this.stack.push(frame);
+ this.previousWasTextNode = false;
+ return out;
+ };
+
+ return ReactDOMServerRenderer;
+}();
+
+/**
+ * Render a ReactElement to its initial HTML. This should only be used on the
+ * server.
+ * See https://reactjs.org/docs/react-dom-server.html#rendertostring
+ */
+function renderToString(element) {
+ var renderer = new ReactDOMServerRenderer(element, false);
+ try {
+ var markup = renderer.read(Infinity);
+ return markup;
+ } finally {
+ renderer.destroy();
+ }
+}
+
+/**
+ * Similar to renderToString, except this doesn't create extra DOM attributes
+ * such as data-react-id that React uses internally.
+ * See https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup
+ */
+function renderToStaticMarkup(element) {
+ var renderer = new ReactDOMServerRenderer(element, true);
+ try {
+ var markup = renderer.read(Infinity);
+ return markup;
+ } finally {
+ renderer.destroy();
+ }
+}
+
+function renderToNodeStream() {
+ invariant(false, 'ReactDOMServer.renderToNodeStream(): The streaming API is not available in the browser. Use ReactDOMServer.renderToString() instead.');
+}
+
+function renderToStaticNodeStream() {
+ invariant(false, 'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.');
+}
+
+// Note: when changing this, also consider https://github.com/facebook/react/issues/11526
+var ReactDOMServerBrowser = {
+ renderToString: renderToString,
+ renderToStaticMarkup: renderToStaticMarkup,
+ renderToNodeStream: renderToNodeStream,
+ renderToStaticNodeStream: renderToStaticNodeStream,
+ version: ReactVersion
+};
+
+var ReactDOMServerBrowser$1 = ({
+ default: ReactDOMServerBrowser
+});
+
+var ReactDOMServer = ( ReactDOMServerBrowser$1 && ReactDOMServerBrowser ) || ReactDOMServerBrowser$1;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest
+var server_browser = ReactDOMServer.default || ReactDOMServer;
+
+return server_browser;
+
+})));
diff --git a/devtools/client/shared/vendor/react-dom-server.js b/devtools/client/shared/vendor/react-dom-server.js
new file mode 100644
index 0000000000..110ac83949
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom-server.js
@@ -0,0 +1,2188 @@
+/** @license React v16.8.6
+ * react-dom-server.browser.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require("resource://devtools/client/shared/vendor/react.js")) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react'], factory) :
+ (global.ReactDOMServer = factory(global.React));
+}(this, (function (React) { 'use strict';
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+/**
+ * WARNING: DO NOT manually require this module.
+ * This is a replacement for `invariant(...)` used by the error code system
+ * and will _only_ be required by the corresponding babel pass.
+ * It always throws.
+ */
+function reactProdInvariant(code) {
+ var argCount = arguments.length - 1;
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
+ for (var argIdx = 0; argIdx < argCount; argIdx++) {
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
+ }
+ // Rename it so that our build transform doesn't attempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
+}
+
+// TODO: this is special because it gets imported during build.
+
+var ReactVersion = '16.8.6';
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+var Resolved = 1;
+
+
+function refineResolvedLazyComponent(lazyComponent) {
+ return lazyComponent._status === Resolved ? lazyComponent._result : null;
+}
+
+function getWrappedName(outerType, innerType, wrapperName) {
+ var functionName = innerType.displayName || innerType.name || '';
+ return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
+}
+
+function getComponentName(type) {
+ if (type == null) {
+ // Host root, text node or just invalid type.
+ return null;
+ }
+ if (typeof type === 'function') {
+ return type.displayName || type.name || null;
+ }
+ if (typeof type === 'string') {
+ return type;
+ }
+ switch (type) {
+ case REACT_CONCURRENT_MODE_TYPE:
+ return 'ConcurrentMode';
+ case REACT_FRAGMENT_TYPE:
+ return 'Fragment';
+ case REACT_PORTAL_TYPE:
+ return 'Portal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_SUSPENSE_TYPE:
+ return 'Suspense';
+ }
+ if (typeof type === 'object') {
+ switch (type.$$typeof) {
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_FORWARD_REF_TYPE:
+ return getWrappedName(type, type.render, 'ForwardRef');
+ case REACT_MEMO_TYPE:
+ return getComponentName(type.type);
+ case REACT_LAZY_TYPE:
+ {
+ var thenable = type;
+ var resolvedThenable = refineResolvedLazyComponent(thenable);
+ if (resolvedThenable) {
+ return getComponentName(resolvedThenable);
+ }
+ }
+ }
+ }
+ return null;
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
+
+
+// In some cases, StrictMode should also double-render lifecycles.
+// This can be confusing for tests though,
+// And it can be bad for performance in production.
+// This feature flag can be used to control the behavior:
+
+
+// To preserve the "Pause on caught exceptions" behavior of the debugger, we
+// replay the begin phase of a failed component inside invokeGuardedCallback.
+
+
+// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
+
+
+// Gather advanced timing metrics for Profiler subtrees.
+
+
+// Trace which interactions trigger each commit.
+
+
+// Only used in www builds.
+var enableSuspenseServerRenderer = false; // TODO: false? Here it might just be false.
+
+// Only used in www builds.
+
+
+// Only used in www builds.
+
+
+// React Fire: prevent the value and checked attributes from syncing
+// with their related DOM properties
+
+
+// These APIs will no longer be "unstable" in the upcoming 16.7 release,
+// Control this behavior with a flag to support 16.6 minor releases in the meanwhile.
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+var emptyObject = {};
+function maskContext(type, context) {
+ var contextTypes = type.contextTypes;
+ if (!contextTypes) {
+ return emptyObject;
+ }
+ var maskedContext = {};
+ for (var contextName in contextTypes) {
+ maskedContext[contextName] = context[contextName];
+ }
+ return maskedContext;
+}
+
+function validateContextBounds(context, threadID) {
+ // If we don't have enough slots in this context to store this threadID,
+ // fill it in without leaving any holes to ensure that the VM optimizes
+ // this as non-holey index properties.
+ // (Note: If `react` package is < 16.6, _threadCount is undefined.)
+ for (var i = context._threadCount | 0; i <= threadID; i++) {
+ // We assume that this is the same as the defaultValue which might not be
+ // true if we're rendering inside a secondary renderer but they are
+ // secondary because these use cases are very rare.
+ context[i] = context._currentValue2;
+ context._threadCount = i + 1;
+ }
+}
+
+function processContext(type, context, threadID) {
+ var contextType = type.contextType;
+ if (typeof contextType === 'object' && contextType !== null) {
+ validateContextBounds(contextType, threadID);
+ return contextType[threadID];
+ } else {
+ var maskedContext = maskContext(type, context);
+ return maskedContext;
+ }
+}
+
+// Allocates a new index for each request. Tries to stay as compact as possible so that these
+// indices can be used to reference a tightly packaged array. As opposed to being used in a Map.
+// The first allocated index is 1.
+
+var nextAvailableThreadIDs = new Uint16Array(16);
+for (var i = 0; i < 15; i++) {
+ nextAvailableThreadIDs[i] = i + 1;
+}
+nextAvailableThreadIDs[15] = 0;
+
+function growThreadCountAndReturnNextAvailable() {
+ var oldArray = nextAvailableThreadIDs;
+ var oldSize = oldArray.length;
+ var newSize = oldSize * 2;
+ !(newSize <= 0x10000) ? reactProdInvariant('304') : void 0;
+ var newArray = new Uint16Array(newSize);
+ newArray.set(oldArray);
+ nextAvailableThreadIDs = newArray;
+ nextAvailableThreadIDs[0] = oldSize + 1;
+ for (var _i = oldSize; _i < newSize - 1; _i++) {
+ nextAvailableThreadIDs[_i] = _i + 1;
+ }
+ nextAvailableThreadIDs[newSize - 1] = 0;
+ return oldSize;
+}
+
+function allocThreadID() {
+ var nextID = nextAvailableThreadIDs[0];
+ if (nextID === 0) {
+ return growThreadCountAndReturnNextAvailable();
+ }
+ nextAvailableThreadIDs[0] = nextAvailableThreadIDs[nextID];
+ return nextID;
+}
+
+function freeThreadID(id) {
+ nextAvailableThreadIDs[id] = nextAvailableThreadIDs[0];
+ nextAvailableThreadIDs[0] = id;
+}
+
+// A reserved attribute.
+// It is handled by React separately and shouldn't be written to the DOM.
+var RESERVED = 0;
+
+// A simple string attribute.
+// Attributes that aren't in the whitelist are presumed to have this type.
+var STRING = 1;
+
+// A string attribute that accepts booleans in React. In HTML, these are called
+// "enumerated" attributes with "true" and "false" as possible values.
+// When true, it should be set to a "true" string.
+// When false, it should be set to a "false" string.
+var BOOLEANISH_STRING = 2;
+
+// A real boolean attribute.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+var BOOLEAN = 3;
+
+// An attribute that can be used as a flag as well as with a value.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+// For any other value, should be present with that value.
+var OVERLOADED_BOOLEAN = 4;
+
+// An attribute that must be numeric or parse as a numeric.
+// When falsy, it should be removed.
+var NUMERIC = 5;
+
+// An attribute that must be positive numeric or parse as a positive numeric.
+// When falsy, it should be removed.
+var POSITIVE_NUMERIC = 6;
+
+/* eslint-disable max-len */
+var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
+/* eslint-enable max-len */
+var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
+
+
+var ROOT_ATTRIBUTE_NAME = 'data-reactroot';
+var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+var illegalAttributeNameCache = {};
+var validatedAttributeNameCache = {};
+
+function isAttributeNameSafe(attributeName) {
+ if (hasOwnProperty$1.call(validatedAttributeNameCache, attributeName)) {
+ return true;
+ }
+ if (hasOwnProperty$1.call(illegalAttributeNameCache, attributeName)) {
+ return false;
+ }
+ if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
+ validatedAttributeNameCache[attributeName] = true;
+ return true;
+ }
+ illegalAttributeNameCache[attributeName] = true;
+ return false;
+}
+
+function shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null) {
+ return propertyInfo.type === RESERVED;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) {
+ return true;
+ }
+ return false;
+}
+
+function shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null && propertyInfo.type === RESERVED) {
+ return false;
+ }
+ switch (typeof value) {
+ case 'function':
+ // $FlowIssue symbol is perfectly valid here
+ case 'symbol':
+ // eslint-disable-line
+ return true;
+ case 'boolean':
+ {
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ return !propertyInfo.acceptsBooleans;
+ } else {
+ var prefix = name.toLowerCase().slice(0, 5);
+ return prefix !== 'data-' && prefix !== 'aria-';
+ }
+ }
+ default:
+ return false;
+ }
+}
+
+function shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag) {
+ if (value === null || typeof value === 'undefined') {
+ return true;
+ }
+ if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag)) {
+ return true;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ switch (propertyInfo.type) {
+ case BOOLEAN:
+ return !value;
+ case OVERLOADED_BOOLEAN:
+ return value === false;
+ case NUMERIC:
+ return isNaN(value);
+ case POSITIVE_NUMERIC:
+ return isNaN(value) || value < 1;
+ }
+ }
+ return false;
+}
+
+function getPropertyInfo(name) {
+ return properties.hasOwnProperty(name) ? properties[name] : null;
+}
+
+function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace) {
+ this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN;
+ this.attributeName = attributeName;
+ this.attributeNamespace = attributeNamespace;
+ this.mustUseProperty = mustUseProperty;
+ this.propertyName = name;
+ this.type = type;
+}
+
+// When adding attributes to this list, be sure to also add them to
+// the `possibleStandardNames` module to ensure casing and incorrect
+// name warnings.
+var properties = {};
+
+// These props are reserved by React. They shouldn't be written to the DOM.
+['children', 'dangerouslySetInnerHTML',
+// TODO: This prevents the assignment of defaultValue to regular
+// elements (not just inputs). Now that ReactDOMInput assigns to the
+// defaultValue property -- do we need this?
+'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, RESERVED, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// A few React string attributes have a different name.
+// This is a mapping from React prop names to the attribute names.
+[['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv']].forEach(function (_ref) {
+ var name = _ref[0],
+ attributeName = _ref[1];
+
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" HTML attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" SVG attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+// Since these are SVG attributes, their attribute names are case-sensitive.
+['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML boolean attributes.
+['allowFullScreen', 'async',
+// Note: there is a special case that prevents it from being written to the DOM
+// on the client side because the browsers are inconsistent. Instead we call focus().
+'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless',
+// Microdata
+'itemScope'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are the few React props that we set as DOM properties
+// rather than attributes. These are all booleans.
+['checked',
+// Note: `option.selected` is not updated if `select.multiple` is
+// disabled with `removeAttribute`. We have special logic for handling this.
+'multiple', 'muted', 'selected'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, true, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that are "overloaded booleans": they behave like
+// booleans, but can also accept a string value.
+['capture', 'download'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, OVERLOADED_BOOLEAN, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be positive numbers.
+['cols', 'rows', 'size', 'span'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, POSITIVE_NUMERIC, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be numbers.
+['rowSpan', 'start'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, NUMERIC, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+var CAMELIZE = /[\-\:]([a-z])/g;
+var capitalize = function (token) {
+ return token[1].toUpperCase();
+};
+
+// This is a list of all SVG attributes that need special casing, namespacing,
+// or boolean value assignment. Regular attributes that just accept strings
+// and have the same names are omitted, just like in the HTML whitelist.
+// Some of these attributes can be hard to find. This list was created by
+// scrapping the MDN documentation.
+['accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'xmlns:xlink', 'x-height'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, null);
+} // attributeNamespace
+);
+
+// String SVG attributes with the xlink namespace.
+['xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/1999/xlink');
+});
+
+// String SVG attributes with the xml namespace.
+['xml:base', 'xml:lang', 'xml:space'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/XML/1998/namespace');
+});
+
+// These attribute exists both in HTML and SVG.
+// The attribute name is case-sensitive in SVG so we can't just use
+// the React name like we do for attributes that exist only in HTML.
+['tabIndex', 'crossOrigin'].forEach(function (attributeName) {
+ properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty
+ attributeName.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// code copied and modified from escape-html
+/**
+ * Module variables.
+ * @private
+ */
+
+var matchHtmlRegExp = /["'&<>]/;
+
+/**
+ * Escapes special characters and HTML entities in a given html string.
+ *
+ * @param {string} string HTML string to escape for later insertion
+ * @return {string}
+ * @public
+ */
+
+function escapeHtml(string) {
+ var str = '' + string;
+ var match = matchHtmlRegExp.exec(str);
+
+ if (!match) {
+ return str;
+ }
+
+ var escape = void 0;
+ var html = '';
+ var index = void 0;
+ var lastIndex = 0;
+
+ for (index = match.index; index < str.length; index++) {
+ switch (str.charCodeAt(index)) {
+ case 34:
+ // "
+ escape = '&quot;';
+ break;
+ case 38:
+ // &
+ escape = '&amp;';
+ break;
+ case 39:
+ // '
+ escape = '&#x27;'; // modified from escape-html; used to be '&#39'
+ break;
+ case 60:
+ // <
+ escape = '&lt;';
+ break;
+ case 62:
+ // >
+ escape = '&gt;';
+ break;
+ default:
+ continue;
+ }
+
+ if (lastIndex !== index) {
+ html += str.substring(lastIndex, index);
+ }
+
+ lastIndex = index + 1;
+ html += escape;
+ }
+
+ return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
+}
+// end code copied and modified from escape-html
+
+/**
+ * Escapes text to prevent scripting attacks.
+ *
+ * @param {*} text Text value to escape.
+ * @return {string} An escaped string.
+ */
+function escapeTextForBrowser(text) {
+ if (typeof text === 'boolean' || typeof text === 'number') {
+ // this shortcircuit helps perf for types that we know will never have
+ // special characters, especially given that this function is used often
+ // for numeric dom ids.
+ return '' + text;
+ }
+ return escapeHtml(text);
+}
+
+/**
+ * Escapes attribute value to prevent scripting attacks.
+ *
+ * @param {*} value Value to escape.
+ * @return {string} An escaped string.
+ */
+function quoteAttributeValueForBrowser(value) {
+ return '"' + escapeTextForBrowser(value) + '"';
+}
+
+/**
+ * Operations for dealing with DOM properties.
+ */
+
+/**
+ * Creates markup for the ID property.
+ *
+ * @param {string} id Unescaped ID.
+ * @return {string} Markup string.
+ */
+
+
+function createMarkupForRoot() {
+ return ROOT_ATTRIBUTE_NAME + '=""';
+}
+
+/**
+ * Creates markup for a property.
+ *
+ * @param {string} name
+ * @param {*} value
+ * @return {?string} Markup string, or null if the property was invalid.
+ */
+function createMarkupForProperty(name, value) {
+ var propertyInfo = getPropertyInfo(name);
+ if (name !== 'style' && shouldIgnoreAttribute(name, propertyInfo, false)) {
+ return '';
+ }
+ if (shouldRemoveAttribute(name, value, propertyInfo, false)) {
+ return '';
+ }
+ if (propertyInfo !== null) {
+ var attributeName = propertyInfo.attributeName;
+ var type = propertyInfo.type;
+
+ if (type === BOOLEAN || type === OVERLOADED_BOOLEAN && value === true) {
+ return attributeName + '=""';
+ } else {
+ return attributeName + '=' + quoteAttributeValueForBrowser(value);
+ }
+ } else if (isAttributeNameSafe(name)) {
+ return name + '=' + quoteAttributeValueForBrowser(value);
+ }
+ return '';
+}
+
+/**
+ * Creates markup for a custom property.
+ *
+ * @param {string} name
+ * @param {*} value
+ * @return {string} Markup string, or empty string if the property was invalid.
+ */
+function createMarkupForCustomAttribute(name, value) {
+ if (!isAttributeNameSafe(name) || value == null) {
+ return '';
+ }
+ return name + '=' + quoteAttributeValueForBrowser(value);
+}
+
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
+ ;
+}
+
+var currentlyRenderingComponent = null;
+var firstWorkInProgressHook = null;
+var workInProgressHook = null;
+// Whether the work-in-progress hook is a re-rendered hook
+var isReRender = false;
+// Whether an update was scheduled during the currently executing render pass.
+var didScheduleRenderPhaseUpdate = false;
+// Lazily created map of render-phase updates
+var renderPhaseUpdates = null;
+// Counter to prevent infinite loops.
+var numberOfReRenders = 0;
+var RE_RENDER_LIMIT = 25;
+
+function resolveCurrentlyRenderingComponent() {
+ !(currentlyRenderingComponent !== null) ? reactProdInvariant('321') : void 0;
+ return currentlyRenderingComponent;
+}
+
+function areHookInputsEqual(nextDeps, prevDeps) {
+ if (prevDeps === null) {
+ return false;
+ }
+
+ for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
+ if (is(nextDeps[i], prevDeps[i])) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+function createHook() {
+ if (numberOfReRenders > 0) {
+ reactProdInvariant('312');
+ }
+ return {
+ memoizedState: null,
+ queue: null,
+ next: null
+ };
+}
+
+function createWorkInProgressHook() {
+ if (workInProgressHook === null) {
+ // This is the first hook in the list
+ if (firstWorkInProgressHook === null) {
+ isReRender = false;
+ firstWorkInProgressHook = workInProgressHook = createHook();
+ } else {
+ // There's already a work-in-progress. Reuse it.
+ isReRender = true;
+ workInProgressHook = firstWorkInProgressHook;
+ }
+ } else {
+ if (workInProgressHook.next === null) {
+ isReRender = false;
+ // Append to the end of the list
+ workInProgressHook = workInProgressHook.next = createHook();
+ } else {
+ // There's already a work-in-progress. Reuse it.
+ isReRender = true;
+ workInProgressHook = workInProgressHook.next;
+ }
+ }
+ return workInProgressHook;
+}
+
+function prepareToUseHooks(componentIdentity) {
+ currentlyRenderingComponent = componentIdentity;
+
+
+ // The following should have already been reset
+ // didScheduleRenderPhaseUpdate = false;
+ // firstWorkInProgressHook = null;
+ // numberOfReRenders = 0;
+ // renderPhaseUpdates = null;
+ // workInProgressHook = null;
+}
+
+function finishHooks(Component, props, children, refOrContext) {
+ // This must be called after every function component to prevent hooks from
+ // being used in classes.
+
+ while (didScheduleRenderPhaseUpdate) {
+ // Updates were scheduled during the render phase. They are stored in
+ // the `renderPhaseUpdates` map. Call the component again, reusing the
+ // work-in-progress hooks and applying the additional updates on top. Keep
+ // restarting until no more updates are scheduled.
+ didScheduleRenderPhaseUpdate = false;
+ numberOfReRenders += 1;
+
+ // Start over from the beginning of the list
+ workInProgressHook = null;
+
+ children = Component(props, refOrContext);
+ }
+ currentlyRenderingComponent = null;
+ firstWorkInProgressHook = null;
+ numberOfReRenders = 0;
+ renderPhaseUpdates = null;
+ workInProgressHook = null;
+ return children;
+}
+
+function readContext(context, observedBits) {
+ var threadID = currentThreadID;
+ validateContextBounds(context, threadID);
+ return context[threadID];
+}
+
+function useContext(context, observedBits) {
+ resolveCurrentlyRenderingComponent();
+ var threadID = currentThreadID;
+ validateContextBounds(context, threadID);
+ return context[threadID];
+}
+
+function basicStateReducer(state, action) {
+ return typeof action === 'function' ? action(state) : action;
+}
+
+function useState(initialState) {
+ return useReducer(basicStateReducer,
+ // useReducer has a special case to support lazy useState initializers
+ initialState);
+}
+
+function useReducer(reducer, initialArg, init) {
+ currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
+ workInProgressHook = createWorkInProgressHook();
+ if (isReRender) {
+ // This is a re-render. Apply the new render phase updates to the previous
+ var _queue = workInProgressHook.queue;
+ var _dispatch = _queue.dispatch;
+ if (renderPhaseUpdates !== null) {
+ // Render phase updates are stored in a map of queue -> linked list
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(_queue);
+ if (firstRenderPhaseUpdate !== undefined) {
+ renderPhaseUpdates.delete(_queue);
+ var newState = workInProgressHook.memoizedState;
+ var update = firstRenderPhaseUpdate;
+ do {
+ // Process this render phase update. We don't have to check the
+ // priority because it will always be the same as the current
+ // render's.
+ var _action = update.action;
+ newState = reducer(newState, _action);
+ update = update.next;
+ } while (update !== null);
+
+ workInProgressHook.memoizedState = newState;
+
+ return [newState, _dispatch];
+ }
+ }
+ return [workInProgressHook.memoizedState, _dispatch];
+ } else {
+ var initialState = void 0;
+ if (reducer === basicStateReducer) {
+ // Special case for `useState`.
+ initialState = typeof initialArg === 'function' ? initialArg() : initialArg;
+ } else {
+ initialState = init !== undefined ? init(initialArg) : initialArg;
+ }
+ workInProgressHook.memoizedState = initialState;
+ var _queue2 = workInProgressHook.queue = {
+ last: null,
+ dispatch: null
+ };
+ var _dispatch2 = _queue2.dispatch = dispatchAction.bind(null, currentlyRenderingComponent, _queue2);
+ return [workInProgressHook.memoizedState, _dispatch2];
+ }
+}
+
+function useMemo(nextCreate, deps) {
+ currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
+ workInProgressHook = createWorkInProgressHook();
+
+ var nextDeps = deps === undefined ? null : deps;
+
+ if (workInProgressHook !== null) {
+ var prevState = workInProgressHook.memoizedState;
+ if (prevState !== null) {
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ }
+
+ var nextValue = nextCreate();
+ workInProgressHook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+function useRef(initialValue) {
+ currentlyRenderingComponent = resolveCurrentlyRenderingComponent();
+ workInProgressHook = createWorkInProgressHook();
+ var previousRef = workInProgressHook.memoizedState;
+ if (previousRef === null) {
+ var ref = { current: initialValue };
+ workInProgressHook.memoizedState = ref;
+ return ref;
+ } else {
+ return previousRef;
+ }
+}
+
+function useLayoutEffect(create, inputs) {
+
+}
+
+function dispatchAction(componentIdentity, queue, action) {
+ !(numberOfReRenders < RE_RENDER_LIMIT) ? reactProdInvariant('301') : void 0;
+
+ if (componentIdentity === currentlyRenderingComponent) {
+ // This is a render phase update. Stash it in a lazily-created map of
+ // queue -> linked list of updates. After this render pass, we'll restart
+ // and apply the stashed updates on top of the work-in-progress hook.
+ didScheduleRenderPhaseUpdate = true;
+ var update = {
+ action: action,
+ next: null
+ };
+ if (renderPhaseUpdates === null) {
+ renderPhaseUpdates = new Map();
+ }
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate === undefined) {
+ renderPhaseUpdates.set(queue, update);
+ } else {
+ // Append the update to the end of the list.
+ var lastRenderPhaseUpdate = firstRenderPhaseUpdate;
+ while (lastRenderPhaseUpdate.next !== null) {
+ lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
+ }
+ lastRenderPhaseUpdate.next = update;
+ }
+ } else {
+ // This means an update has happened after the function component has
+ // returned. On the server this is a no-op. In React Fiber, the update
+ // would be scheduled for a future render.
+ }
+}
+
+function useCallback(callback, deps) {
+ // Callbacks are passed as they are in the server environment.
+ return callback;
+}
+
+function noop() {}
+
+var currentThreadID = 0;
+
+function setCurrentThreadID(threadID) {
+ currentThreadID = threadID;
+}
+
+var Dispatcher = {
+ readContext: readContext,
+ useContext: useContext,
+ useMemo: useMemo,
+ useReducer: useReducer,
+ useRef: useRef,
+ useState: useState,
+ useLayoutEffect: useLayoutEffect,
+ useCallback: useCallback,
+ // useImperativeHandle is not run in the server environment
+ useImperativeHandle: noop,
+ // Effects are not run in the server environment.
+ useEffect: noop,
+ // Debugging effect
+ useDebugValue: noop
+};
+
+var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
+var MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
+var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+
+var Namespaces = {
+ html: HTML_NAMESPACE,
+ mathml: MATH_NAMESPACE,
+ svg: SVG_NAMESPACE
+};
+
+// Assumes there is no parent namespace.
+function getIntrinsicNamespace(type) {
+ switch (type) {
+ case 'svg':
+ return SVG_NAMESPACE;
+ case 'math':
+ return MATH_NAMESPACE;
+ default:
+ return HTML_NAMESPACE;
+ }
+}
+
+function getChildNamespace(parentNamespace, type) {
+ if (parentNamespace == null || parentNamespace === HTML_NAMESPACE) {
+ // No (or default) parent namespace: potential entry point.
+ return getIntrinsicNamespace(type);
+ }
+ if (parentNamespace === SVG_NAMESPACE && type === 'foreignObject') {
+ // We're leaving SVG.
+ return HTML_NAMESPACE;
+ }
+ // By default, pass namespace below.
+ return parentNamespace;
+}
+
+// For HTML, certain tags should omit their close tag. We keep a whitelist for
+// those special-case tags.
+
+var omittedCloseTags = {
+ area: true,
+ base: true,
+ br: true,
+ col: true,
+ embed: true,
+ hr: true,
+ img: true,
+ input: true,
+ keygen: true,
+ link: true,
+ meta: true,
+ param: true,
+ source: true,
+ track: true,
+ wbr: true
+ // NOTE: menuitem's close tag should be omitted, but that causes problems.
+};
+
+// For HTML, certain tags cannot have children. This has the same purpose as
+// `omittedCloseTags` except that `menuitem` should still have its closing tag.
+
+var voidElementTags = _assign({
+ menuitem: true
+}, omittedCloseTags);
+
+// TODO: We can remove this if we add invariantWithStack()
+// or add stack by default to invariants where possible.
+var HTML = '__html';
+
+function assertValidProps(tag, props) {
+ if (!props) {
+ return;
+ }
+ // Note the use of `==` which checks for null or undefined.
+ if (voidElementTags[tag]) {
+ !(props.children == null && props.dangerouslySetInnerHTML == null) ? reactProdInvariant('137', tag, '') : void 0;
+ }
+ if (props.dangerouslySetInnerHTML != null) {
+ !(props.children == null) ? reactProdInvariant('60') : void 0;
+ !(typeof props.dangerouslySetInnerHTML === 'object' && HTML in props.dangerouslySetInnerHTML) ? reactProdInvariant('61') : void 0;
+ }
+ !(props.style == null || typeof props.style === 'object') ? reactProdInvariant('62', '') : void 0;
+}
+
+/**
+ * CSS properties which accept numbers but are not in units of "px".
+ */
+var isUnitlessNumber = {
+ animationIterationCount: true,
+ borderImageOutset: true,
+ borderImageSlice: true,
+ borderImageWidth: true,
+ boxFlex: true,
+ boxFlexGroup: true,
+ boxOrdinalGroup: true,
+ columnCount: true,
+ columns: true,
+ flex: true,
+ flexGrow: true,
+ flexPositive: true,
+ flexShrink: true,
+ flexNegative: true,
+ flexOrder: true,
+ gridArea: true,
+ gridRow: true,
+ gridRowEnd: true,
+ gridRowSpan: true,
+ gridRowStart: true,
+ gridColumn: true,
+ gridColumnEnd: true,
+ gridColumnSpan: true,
+ gridColumnStart: true,
+ fontWeight: true,
+ lineClamp: true,
+ lineHeight: true,
+ opacity: true,
+ order: true,
+ orphans: true,
+ tabSize: true,
+ widows: true,
+ zIndex: true,
+ zoom: true,
+
+ // SVG-related properties
+ fillOpacity: true,
+ floodOpacity: true,
+ stopOpacity: true,
+ strokeDasharray: true,
+ strokeDashoffset: true,
+ strokeMiterlimit: true,
+ strokeOpacity: true,
+ strokeWidth: true
+};
+
+/**
+ * @param {string} prefix vendor-specific prefix, eg: Webkit
+ * @param {string} key style name, eg: transitionDuration
+ * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
+ * WebkitTransitionDuration
+ */
+function prefixKey(prefix, key) {
+ return prefix + key.charAt(0).toUpperCase() + key.substring(1);
+}
+
+/**
+ * Support style names that may come passed in prefixed by adding permutations
+ * of vendor prefixes.
+ */
+var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
+
+// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
+// infinite loop, because it iterates over the newly added props too.
+Object.keys(isUnitlessNumber).forEach(function (prop) {
+ prefixes.forEach(function (prefix) {
+ isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
+ });
+});
+
+/**
+ * Convert a value into the proper css writable value. The style name `name`
+ * should be logical (no hyphens), as specified
+ * in `CSSProperty.isUnitlessNumber`.
+ *
+ * @param {string} name CSS property name such as `topMargin`.
+ * @param {*} value CSS property value such as `10px`.
+ * @return {string} Normalized style value with dimensions applied.
+ */
+function dangerousStyleValue(name, value, isCustomProperty) {
+ // Note that we've removed escapeTextForBrowser() calls here since the
+ // whole string will be escaped when the attribute is injected into
+ // the markup. If you provide unsafe user data here they can inject
+ // arbitrary CSS which may be problematic (I couldn't repro this):
+ // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
+ // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
+ // This is not an XSS hole but instead a potential CSS injection issue
+ // which has lead to a greater discussion about how we're going to
+ // trust URLs moving forward. See #2115901
+
+ var isEmpty = value == null || typeof value === 'boolean' || value === '';
+ if (isEmpty) {
+ return '';
+ }
+
+ if (!isCustomProperty && typeof value === 'number' && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])) {
+ return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers
+ }
+
+ return ('' + value).trim();
+}
+
+var uppercasePattern = /([A-Z])/g;
+var msPattern = /^ms-/;
+
+/**
+ * Hyphenates a camelcased CSS property name, for example:
+ *
+ * > hyphenateStyleName('backgroundColor')
+ * < "background-color"
+ * > hyphenateStyleName('MozTransition')
+ * < "-moz-transition"
+ * > hyphenateStyleName('msTransition')
+ * < "-ms-transition"
+ *
+ * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
+ * is converted to `-ms-`.
+ */
+function hyphenateStyleName(name) {
+ return name.replace(uppercasePattern, '-$1').toLowerCase().replace(msPattern, '-ms-');
+}
+
+function isCustomComponent(tagName, props) {
+ if (tagName.indexOf('-') === -1) {
+ return typeof props.is === 'string';
+ }
+ switch (tagName) {
+ // These are reserved SVG and MathML elements.
+ // We don't mind this whitelist too much because we expect it to never grow.
+ // The alternative is to track the namespace in a few places which is convoluted.
+ // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts
+ case 'annotation-xml':
+ case 'color-profile':
+ case 'font-face':
+ case 'font-face-src':
+ case 'font-face-uri':
+ case 'font-face-format':
+ case 'font-face-name':
+ case 'missing-glyph':
+ return false;
+ default:
+ return true;
+ }
+}
+
+/**
+ * Registers plugins so that they can extract and dispatch events.
+ *
+ * @see {EventPluginHub}
+ */
+
+/**
+ * Ordered list of injected plugins.
+ */
+
+
+/**
+ * Mapping from event name to dispatch config
+ */
+
+
+/**
+ * Mapping from registration name to plugin module
+ */
+
+
+/**
+ * Mapping from registration name to event name
+ */
+
+
+/**
+ * Mapping from lowercase registration names to the properly cased version,
+ * used to warn in the case of missing event handlers. Available
+ * only in false.
+ * @type {Object}
+ */
+
+// Trust the developer to only use possibleRegistrationNames in false
+
+/**
+ * Injects an ordering of plugins (by plugin name). This allows the ordering
+ * to be decoupled from injection of the actual plugins so that ordering is
+ * always deterministic regardless of packaging, on-the-fly injection, etc.
+ *
+ * @param {array} InjectedEventPluginOrder
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginOrder}
+ */
+
+
+/**
+ * Injects plugins to be used by `EventPluginHub`. The plugin names must be
+ * in the ordering injected by `injectEventPluginOrder`.
+ *
+ * Plugins can be injected as part of page initialization or on-the-fly.
+ *
+ * @param {object} injectedNamesToPlugins Map from names to plugin modules.
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginsByName}
+ */
+
+// When adding attributes to the HTML or SVG whitelist, be sure to
+// also add them to this module to ensure casing and incorrect name
+// warnings.
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+// Based on reading the React.Children implementation. TODO: type this somewhere?
+
+var toArray = React.Children.toArray;
+
+var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+var newlineEatingTags = {
+ listing: true,
+ pre: true,
+ textarea: true
+};
+
+// We accept any tag to be rendered but since this gets injected into arbitrary
+// HTML, we want to make sure that it's a safe tag.
+// http://www.w3.org/TR/REC-xml/#NT-Name
+var VALID_TAG_REGEX = /^[a-zA-Z][a-zA-Z:_\.\-\d]*$/; // Simplified subset
+var validatedTagCache = {};
+function validateDangerousTag(tag) {
+ if (!validatedTagCache.hasOwnProperty(tag)) {
+ !VALID_TAG_REGEX.test(tag) ? reactProdInvariant('65', tag) : void 0;
+ validatedTagCache[tag] = true;
+ }
+}
+
+var styleNameCache = {};
+var processStyleName = function (styleName) {
+ if (styleNameCache.hasOwnProperty(styleName)) {
+ return styleNameCache[styleName];
+ }
+ var result = hyphenateStyleName(styleName);
+ styleNameCache[styleName] = result;
+ return result;
+};
+
+function createMarkupForStyles(styles) {
+ var serialized = '';
+ var delimiter = '';
+ for (var styleName in styles) {
+ if (!styles.hasOwnProperty(styleName)) {
+ continue;
+ }
+ var isCustomProperty = styleName.indexOf('--') === 0;
+ var styleValue = styles[styleName];
+ if (styleValue != null) {
+ serialized += delimiter + processStyleName(styleName) + ':';
+ serialized += dangerousStyleValue(styleName, styleValue, isCustomProperty);
+
+ delimiter = ';';
+ }
+ }
+ return serialized || null;
+}
+
+function shouldConstruct(Component) {
+ return Component.prototype && Component.prototype.isReactComponent;
+}
+
+function getNonChildrenInnerMarkup(props) {
+ var innerHTML = props.dangerouslySetInnerHTML;
+ if (innerHTML != null) {
+ if (innerHTML.__html != null) {
+ return innerHTML.__html;
+ }
+ } else {
+ var content = props.children;
+ if (typeof content === 'string' || typeof content === 'number') {
+ return escapeTextForBrowser(content);
+ }
+ }
+ return null;
+}
+
+function flattenTopLevelChildren(children) {
+ if (!React.isValidElement(children)) {
+ return toArray(children);
+ }
+ var element = children;
+ if (element.type !== REACT_FRAGMENT_TYPE) {
+ return [element];
+ }
+ var fragmentChildren = element.props.children;
+ if (!React.isValidElement(fragmentChildren)) {
+ return toArray(fragmentChildren);
+ }
+ var fragmentChildElement = fragmentChildren;
+ return [fragmentChildElement];
+}
+
+function flattenOptionChildren(children) {
+ if (children === undefined || children === null) {
+ return children;
+ }
+ var content = '';
+ // Flatten children and warn if they aren't strings or numbers;
+ // invalid types are ignored.
+ React.Children.forEach(children, function (child) {
+ if (child == null) {
+ return;
+ }
+ content += child;
+
+ });
+ return content;
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var STYLE = 'style';
+var RESERVED_PROPS = {
+ children: null,
+ dangerouslySetInnerHTML: null,
+ suppressContentEditableWarning: null,
+ suppressHydrationWarning: null
+};
+
+function createOpenTagMarkup(tagVerbatim, tagLowercase, props, namespace, makeStaticMarkup, isRootElement) {
+ var ret = '<' + tagVerbatim;
+
+ for (var propKey in props) {
+ if (!hasOwnProperty.call(props, propKey)) {
+ continue;
+ }
+ var propValue = props[propKey];
+ if (propValue == null) {
+ continue;
+ }
+ if (propKey === STYLE) {
+ propValue = createMarkupForStyles(propValue);
+ }
+ var markup = null;
+ if (isCustomComponent(tagLowercase, props)) {
+ if (!RESERVED_PROPS.hasOwnProperty(propKey)) {
+ markup = createMarkupForCustomAttribute(propKey, propValue);
+ }
+ } else {
+ markup = createMarkupForProperty(propKey, propValue);
+ }
+ if (markup) {
+ ret += ' ' + markup;
+ }
+ }
+
+ // For static pages, no need to put React ID and checksum. Saves lots of
+ // bytes.
+ if (makeStaticMarkup) {
+ return ret;
+ }
+
+ if (isRootElement) {
+ ret += ' ' + createMarkupForRoot();
+ }
+ return ret;
+}
+
+function validateRenderResult(child, type) {
+ if (child === undefined) {
+ reactProdInvariant('152', getComponentName(type) || 'Component');
+ }
+}
+
+function resolve(child, context, threadID) {
+ while (React.isValidElement(child)) {
+ // Safe because we just checked it's an element.
+ var element = child;
+ var Component = element.type;
+ if (typeof Component !== 'function') {
+ break;
+ }
+ processChild(element, Component);
+ }
+
+ // Extra closure so queue and replace can be captured properly
+ function processChild(element, Component) {
+ var publicContext = processContext(Component, context, threadID);
+
+ var queue = [];
+ var replace = false;
+ var updater = {
+ isMounted: function (publicInstance) {
+ return false;
+ },
+ enqueueForceUpdate: function (publicInstance) {
+ if (queue === null) {
+ return null;
+ }
+ },
+ enqueueReplaceState: function (publicInstance, completeState) {
+ replace = true;
+ queue = [completeState];
+ },
+ enqueueSetState: function (publicInstance, currentPartialState) {
+ if (queue === null) {
+ return null;
+ }
+ queue.push(currentPartialState);
+ }
+ };
+
+ var inst = void 0;
+ if (shouldConstruct(Component)) {
+ inst = new Component(element.props, publicContext, updater);
+
+ if (typeof Component.getDerivedStateFromProps === 'function') {
+ var partialState = Component.getDerivedStateFromProps.call(null, element.props, inst.state);
+
+ if (partialState != null) {
+ inst.state = _assign({}, inst.state, partialState);
+ }
+ }
+ } else {
+ var componentIdentity = {};
+ prepareToUseHooks(componentIdentity);
+ inst = Component(element.props, publicContext, updater);
+ inst = finishHooks(Component, element.props, inst, publicContext);
+
+ if (inst == null || inst.render == null) {
+ child = inst;
+ validateRenderResult(child, Component);
+ return;
+ }
+ }
+
+ inst.props = element.props;
+ inst.context = publicContext;
+ inst.updater = updater;
+
+ var initialState = inst.state;
+ if (initialState === undefined) {
+ inst.state = initialState = null;
+ }
+ if (typeof inst.UNSAFE_componentWillMount === 'function' || typeof inst.componentWillMount === 'function') {
+ if (typeof inst.componentWillMount === 'function') {
+ if (typeof Component.getDerivedStateFromProps !== 'function') {
+ inst.componentWillMount();
+ }
+ }
+ if (typeof inst.UNSAFE_componentWillMount === 'function' && typeof Component.getDerivedStateFromProps !== 'function') {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
+ inst.UNSAFE_componentWillMount();
+ }
+ if (queue.length) {
+ var oldQueue = queue;
+ var oldReplace = replace;
+ queue = null;
+ replace = false;
+
+ if (oldReplace && oldQueue.length === 1) {
+ inst.state = oldQueue[0];
+ } else {
+ var nextState = oldReplace ? oldQueue[0] : inst.state;
+ var dontMutate = true;
+ for (var i = oldReplace ? 1 : 0; i < oldQueue.length; i++) {
+ var partial = oldQueue[i];
+ var _partialState = typeof partial === 'function' ? partial.call(inst, nextState, element.props, publicContext) : partial;
+ if (_partialState != null) {
+ if (dontMutate) {
+ dontMutate = false;
+ nextState = _assign({}, nextState, _partialState);
+ } else {
+ _assign(nextState, _partialState);
+ }
+ }
+ }
+ inst.state = nextState;
+ }
+ } else {
+ queue = null;
+ }
+ }
+ child = inst.render();
+
+ validateRenderResult(child, Component);
+
+ var childContext = void 0;
+ if (typeof inst.getChildContext === 'function') {
+ var childContextTypes = Component.childContextTypes;
+ if (typeof childContextTypes === 'object') {
+ childContext = inst.getChildContext();
+ for (var contextKey in childContext) {
+ !(contextKey in childContextTypes) ? reactProdInvariant('108', getComponentName(Component) || 'Unknown', contextKey) : void 0;
+ }
+ } else {
+
+ }
+ }
+ if (childContext) {
+ context = _assign({}, context, childContext);
+ }
+ }
+ return { child: child, context: context };
+}
+
+var ReactDOMServerRenderer = function () {
+ // DEV-only
+
+ // TODO: type this more strictly:
+ function ReactDOMServerRenderer(children, makeStaticMarkup) {
+ _classCallCheck(this, ReactDOMServerRenderer);
+
+ var flatChildren = flattenTopLevelChildren(children);
+
+ var topFrame = {
+ type: null,
+ // Assume all trees start in the HTML namespace (not totally true, but
+ // this is what we did historically)
+ domNamespace: Namespaces.html,
+ children: flatChildren,
+ childIndex: 0,
+ context: emptyObject,
+ footer: ''
+ };
+ this.threadID = allocThreadID();
+ this.stack = [topFrame];
+ this.exhausted = false;
+ this.currentSelectValue = null;
+ this.previousWasTextNode = false;
+ this.makeStaticMarkup = makeStaticMarkup;
+ this.suspenseDepth = 0;
+
+ // Context (new API)
+ this.contextIndex = -1;
+ this.contextStack = [];
+ this.contextValueStack = [];
+
+ }
+
+ ReactDOMServerRenderer.prototype.destroy = function destroy() {
+ if (!this.exhausted) {
+ this.exhausted = true;
+ this.clearProviders();
+ freeThreadID(this.threadID);
+ }
+ };
+
+ /**
+ * Note: We use just two stacks regardless of how many context providers you have.
+ * Providers are always popped in the reverse order to how they were pushed
+ * so we always know on the way down which provider you'll encounter next on the way up.
+ * On the way down, we push the current provider, and its context value *before*
+ * we mutated it, onto the stacks. Therefore, on the way up, we always know which
+ * provider needs to be "restored" to which value.
+ * https://github.com/facebook/react/pull/12985#issuecomment-396301248
+ */
+
+ ReactDOMServerRenderer.prototype.pushProvider = function pushProvider(provider) {
+ var index = ++this.contextIndex;
+ var context = provider.type._context;
+ var threadID = this.threadID;
+ validateContextBounds(context, threadID);
+ var previousValue = context[threadID];
+
+ // Remember which value to restore this context to on our way up.
+ this.contextStack[index] = context;
+ this.contextValueStack[index] = previousValue;
+ context[threadID] = provider.props.value;
+ };
+
+ ReactDOMServerRenderer.prototype.popProvider = function popProvider(provider) {
+ var index = this.contextIndex;
+ var context = this.contextStack[index];
+ var previousValue = this.contextValueStack[index];
+
+ // "Hide" these null assignments from Flow by using `any`
+ // because conceptually they are deletions--as long as we
+ // promise to never access values beyond `this.contextIndex`.
+ this.contextStack[index] = null;
+ this.contextValueStack[index] = null;
+ this.contextIndex--;
+
+ // Restore to the previous value we stored as we were walking down.
+ // We've already verified that this context has been expanded to accommodate
+ // this thread id, so we don't need to do it again.
+ context[this.threadID] = previousValue;
+ };
+
+ ReactDOMServerRenderer.prototype.clearProviders = function clearProviders() {
+ // Restore any remaining providers on the stack to previous values
+ for (var index = this.contextIndex; index >= 0; index--) {
+ var _context = this.contextStack[index];
+ var previousValue = this.contextValueStack[index];
+ _context[this.threadID] = previousValue;
+ }
+ };
+
+ ReactDOMServerRenderer.prototype.read = function read(bytes) {
+ if (this.exhausted) {
+ return null;
+ }
+
+ var prevThreadID = currentThreadID;
+ setCurrentThreadID(this.threadID);
+ var prevDispatcher = ReactCurrentDispatcher.current;
+ ReactCurrentDispatcher.current = Dispatcher;
+ try {
+ // Markup generated within <Suspense> ends up buffered until we know
+ // nothing in that boundary suspended
+ var out = [''];
+ var suspended = false;
+ while (out[0].length < bytes) {
+ if (this.stack.length === 0) {
+ this.exhausted = true;
+ freeThreadID(this.threadID);
+ break;
+ }
+ var frame = this.stack[this.stack.length - 1];
+ if (suspended || frame.childIndex >= frame.children.length) {
+ var _footer = frame.footer;
+ if (_footer !== '') {
+ this.previousWasTextNode = false;
+ }
+ this.stack.pop();
+ if (frame.type === 'select') {
+ this.currentSelectValue = null;
+ } else if (frame.type != null && frame.type.type != null && frame.type.type.$$typeof === REACT_PROVIDER_TYPE) {
+ var provider = frame.type;
+ this.popProvider(provider);
+ } else if (frame.type === REACT_SUSPENSE_TYPE) {
+ this.suspenseDepth--;
+ var buffered = out.pop();
+
+ if (suspended) {
+ suspended = false;
+ // If rendering was suspended at this boundary, render the fallbackFrame
+ var _fallbackFrame = frame.fallbackFrame;
+ !_fallbackFrame ? reactProdInvariant('303') : void 0;
+ this.stack.push(_fallbackFrame);
+ // Skip flushing output since we're switching to the fallback
+ continue;
+ } else {
+ out[this.suspenseDepth] += buffered;
+ }
+ }
+
+ // Flush output
+ out[this.suspenseDepth] += _footer;
+ continue;
+ }
+ var child = frame.children[frame.childIndex++];
+
+ var outBuffer = '';
+ try {
+ outBuffer += this.render(child, frame.context, frame.domNamespace);
+ } catch (err) {
+ if (enableSuspenseServerRenderer && typeof err.then === 'function') {
+ suspended = true;
+ } else {
+ throw err;
+ }
+ } finally {
+
+ }
+ if (out.length <= this.suspenseDepth) {
+ out.push('');
+ }
+ out[this.suspenseDepth] += outBuffer;
+ }
+ return out[0];
+ } finally {
+ ReactCurrentDispatcher.current = prevDispatcher;
+ setCurrentThreadID(prevThreadID);
+ }
+ };
+
+ ReactDOMServerRenderer.prototype.render = function render(child, context, parentNamespace) {
+ if (typeof child === 'string' || typeof child === 'number') {
+ var text = '' + child;
+ if (text === '') {
+ return '';
+ }
+ if (this.makeStaticMarkup) {
+ return escapeTextForBrowser(text);
+ }
+ if (this.previousWasTextNode) {
+ return '<!-- -->' + escapeTextForBrowser(text);
+ }
+ this.previousWasTextNode = true;
+ return escapeTextForBrowser(text);
+ } else {
+ var nextChild = void 0;
+
+ var _resolve = resolve(child, context, this.threadID);
+
+ nextChild = _resolve.child;
+ context = _resolve.context;
+
+ if (nextChild === null || nextChild === false) {
+ return '';
+ } else if (!React.isValidElement(nextChild)) {
+ if (nextChild != null && nextChild.$$typeof != null) {
+ // Catch unexpected special types early.
+ var $$typeof = nextChild.$$typeof;
+ !($$typeof !== REACT_PORTAL_TYPE) ? reactProdInvariant('257') : void 0;
+ // Catch-all to prevent an infinite loop if React.Children.toArray() supports some new type.
+ reactProdInvariant('258', $$typeof.toString());
+ }
+ var nextChildren = toArray(nextChild);
+ var frame = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: nextChildren,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ this.stack.push(frame);
+ return '';
+ }
+ // Safe because we just checked it's an element.
+ var nextElement = nextChild;
+ var elementType = nextElement.type;
+
+ if (typeof elementType === 'string') {
+ return this.renderDOM(nextElement, context, parentNamespace);
+ }
+
+ switch (elementType) {
+ case REACT_STRICT_MODE_TYPE:
+ case REACT_CONCURRENT_MODE_TYPE:
+ case REACT_PROFILER_TYPE:
+ case REACT_FRAGMENT_TYPE:
+ {
+ var _nextChildren = toArray(nextChild.props.children);
+ var _frame = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ this.stack.push(_frame);
+ return '';
+ }
+ case REACT_SUSPENSE_TYPE:
+ {
+ if (enableSuspenseServerRenderer) {
+ var fallback = nextChild.props.fallback;
+ if (fallback === undefined) {
+ // If there is no fallback, then this just behaves as a fragment.
+ var _nextChildren3 = toArray(nextChild.props.children);
+ var _frame3 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren3,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ this.stack.push(_frame3);
+ return '';
+ }
+ var fallbackChildren = toArray(fallback);
+ var _nextChildren2 = toArray(nextChild.props.children);
+ var _fallbackFrame2 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: fallbackChildren,
+ childIndex: 0,
+ context: context,
+ footer: '',
+ out: ''
+ };
+ var _frame2 = {
+ fallbackFrame: _fallbackFrame2,
+ type: REACT_SUSPENSE_TYPE,
+ domNamespace: parentNamespace,
+ children: _nextChildren2,
+ childIndex: 0,
+ context: context,
+ footer: '<!--/$-->'
+ };
+ this.stack.push(_frame2);
+ this.suspenseDepth++;
+ return '<!--$-->';
+ } else {
+ reactProdInvariant('294');
+ }
+ }
+ // eslint-disable-next-line-no-fallthrough
+ default:
+ break;
+ }
+ if (typeof elementType === 'object' && elementType !== null) {
+ switch (elementType.$$typeof) {
+ case REACT_FORWARD_REF_TYPE:
+ {
+ var element = nextChild;
+ var _nextChildren4 = void 0;
+ var componentIdentity = {};
+ prepareToUseHooks(componentIdentity);
+ _nextChildren4 = elementType.render(element.props, element.ref);
+ _nextChildren4 = finishHooks(elementType.render, element.props, _nextChildren4, element.ref);
+ _nextChildren4 = toArray(_nextChildren4);
+ var _frame4 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren4,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ this.stack.push(_frame4);
+ return '';
+ }
+ case REACT_MEMO_TYPE:
+ {
+ var _element = nextChild;
+ var _nextChildren5 = [React.createElement(elementType.type, _assign({ ref: _element.ref }, _element.props))];
+ var _frame5 = {
+ type: null,
+ domNamespace: parentNamespace,
+ children: _nextChildren5,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ this.stack.push(_frame5);
+ return '';
+ }
+ case REACT_PROVIDER_TYPE:
+ {
+ var provider = nextChild;
+ var nextProps = provider.props;
+ var _nextChildren6 = toArray(nextProps.children);
+ var _frame6 = {
+ type: provider,
+ domNamespace: parentNamespace,
+ children: _nextChildren6,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ this.pushProvider(provider);
+
+ this.stack.push(_frame6);
+ return '';
+ }
+ case REACT_CONTEXT_TYPE:
+ {
+ var reactContext = nextChild.type;
+ // The logic below for Context differs depending on PROD or DEV mode. In
+ // DEV mode, we create a separate object for Context.Consumer that acts
+ // like a proxy to Context. This proxy object adds unnecessary code in PROD
+ // so we use the old behaviour (Context.Consumer references Context) to
+ // reduce size and overhead. The separate object references context via
+ // a property called "_context", which also gives us the ability to check
+ // in DEV mode if this property exists or not and warn if it does not.
+ var _nextProps = nextChild.props;
+ var threadID = this.threadID;
+ validateContextBounds(reactContext, threadID);
+ var nextValue = reactContext[threadID];
+
+ var _nextChildren7 = toArray(_nextProps.children(nextValue));
+ var _frame7 = {
+ type: nextChild,
+ domNamespace: parentNamespace,
+ children: _nextChildren7,
+ childIndex: 0,
+ context: context,
+ footer: ''
+ };
+ this.stack.push(_frame7);
+ return '';
+ }
+ case REACT_LAZY_TYPE:
+ reactProdInvariant('295');
+ }
+ }
+
+ var info = '';
+ reactProdInvariant('130', elementType == null ? elementType : typeof elementType, info);
+ }
+ };
+
+ ReactDOMServerRenderer.prototype.renderDOM = function renderDOM(element, context, parentNamespace) {
+ var tag = element.type.toLowerCase();
+
+ var namespace = parentNamespace;
+ if (parentNamespace === Namespaces.html) {
+ namespace = getIntrinsicNamespace(tag);
+ }
+
+ validateDangerousTag(tag);
+
+ var props = element.props;
+ if (tag === 'input') {
+ props = _assign({
+ type: undefined
+ }, props, {
+ defaultChecked: undefined,
+ defaultValue: undefined,
+ value: props.value != null ? props.value : props.defaultValue,
+ checked: props.checked != null ? props.checked : props.defaultChecked
+ });
+ } else if (tag === 'textarea') {
+ var initialValue = props.value;
+ if (initialValue == null) {
+ var defaultValue = props.defaultValue;
+ // TODO (yungsters): Remove support for children content in <textarea>.
+ var textareaChildren = props.children;
+ if (textareaChildren != null) {
+ !(defaultValue == null) ? reactProdInvariant('92') : void 0;
+ if (Array.isArray(textareaChildren)) {
+ !(textareaChildren.length <= 1) ? reactProdInvariant('93') : void 0;
+ textareaChildren = textareaChildren[0];
+ }
+
+ defaultValue = '' + textareaChildren;
+ }
+ if (defaultValue == null) {
+ defaultValue = '';
+ }
+ initialValue = defaultValue;
+ }
+
+ props = _assign({}, props, {
+ value: undefined,
+ children: '' + initialValue
+ });
+ } else if (tag === 'select') {
+ this.currentSelectValue = props.value != null ? props.value : props.defaultValue;
+ props = _assign({}, props, {
+ value: undefined
+ });
+ } else if (tag === 'option') {
+ var selected = null;
+ var selectValue = this.currentSelectValue;
+ var optionChildren = flattenOptionChildren(props.children);
+ if (selectValue != null) {
+ var value = void 0;
+ if (props.value != null) {
+ value = props.value + '';
+ } else {
+ value = optionChildren;
+ }
+ selected = false;
+ if (Array.isArray(selectValue)) {
+ // multiple
+ for (var j = 0; j < selectValue.length; j++) {
+ if ('' + selectValue[j] === value) {
+ selected = true;
+ break;
+ }
+ }
+ } else {
+ selected = '' + selectValue === value;
+ }
+
+ props = _assign({
+ selected: undefined,
+ children: undefined
+ }, props, {
+ selected: selected,
+ children: optionChildren
+ });
+ }
+ }
+
+ assertValidProps(tag, props);
+
+ var out = createOpenTagMarkup(element.type, tag, props, namespace, this.makeStaticMarkup, this.stack.length === 1);
+ var footer = '';
+ if (omittedCloseTags.hasOwnProperty(tag)) {
+ out += '/>';
+ } else {
+ out += '>';
+ footer = '</' + element.type + '>';
+ }
+ var children = void 0;
+ var innerMarkup = getNonChildrenInnerMarkup(props);
+ if (innerMarkup != null) {
+ children = [];
+ if (newlineEatingTags[tag] && innerMarkup.charAt(0) === '\n') {
+ // text/html ignores the first character in these tags if it's a newline
+ // Prefer to break application/xml over text/html (for now) by adding
+ // a newline specifically to get eaten by the parser. (Alternately for
+ // textareas, replacing "^\n" with "\r\n" doesn't get eaten, and the first
+ // \r is normalized out by HTMLTextAreaElement#value.)
+ // See: <http://www.w3.org/TR/html-polyglot/#newlines-in-textarea-and-pre>
+ // See: <http://www.w3.org/TR/html5/syntax.html#element-restrictions>
+ // See: <http://www.w3.org/TR/html5/syntax.html#newlines>
+ // See: Parsing of "textarea" "listing" and "pre" elements
+ // from <http://www.w3.org/TR/html5/syntax.html#parsing-main-inbody>
+ out += '\n';
+ }
+ out += innerMarkup;
+ } else {
+ children = toArray(props.children);
+ }
+ var frame = {
+ domNamespace: getChildNamespace(parentNamespace, element.type),
+ type: tag,
+ children: children,
+ childIndex: 0,
+ context: context,
+ footer: footer
+ };
+ this.stack.push(frame);
+ this.previousWasTextNode = false;
+ return out;
+ };
+
+ return ReactDOMServerRenderer;
+}();
+
+/**
+ * Render a ReactElement to its initial HTML. This should only be used on the
+ * server.
+ * See https://reactjs.org/docs/react-dom-server.html#rendertostring
+ */
+function renderToString(element) {
+ var renderer = new ReactDOMServerRenderer(element, false);
+ try {
+ var markup = renderer.read(Infinity);
+ return markup;
+ } finally {
+ renderer.destroy();
+ }
+}
+
+/**
+ * Similar to renderToString, except this doesn't create extra DOM attributes
+ * such as data-react-id that React uses internally.
+ * See https://reactjs.org/docs/react-dom-server.html#rendertostaticmarkup
+ */
+function renderToStaticMarkup(element) {
+ var renderer = new ReactDOMServerRenderer(element, true);
+ try {
+ var markup = renderer.read(Infinity);
+ return markup;
+ } finally {
+ renderer.destroy();
+ }
+}
+
+function renderToNodeStream() {
+ reactProdInvariant('207');
+}
+
+function renderToStaticNodeStream() {
+ reactProdInvariant('208');
+}
+
+// Note: when changing this, also consider https://github.com/facebook/react/issues/11526
+var ReactDOMServerBrowser = {
+ renderToString: renderToString,
+ renderToStaticMarkup: renderToStaticMarkup,
+ renderToNodeStream: renderToNodeStream,
+ renderToStaticNodeStream: renderToStaticNodeStream,
+ version: ReactVersion
+};
+
+var ReactDOMServerBrowser$1 = ({
+ default: ReactDOMServerBrowser
+});
+
+var ReactDOMServer = ( ReactDOMServerBrowser$1 && ReactDOMServerBrowser ) || ReactDOMServerBrowser$1;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest
+var server_browser = ReactDOMServer.default || ReactDOMServer;
+
+return server_browser;
+
+})));
diff --git a/devtools/client/shared/vendor/react-dom-test-utils-dev.js b/devtools/client/shared/vendor/react-dom-test-utils-dev.js
new file mode 100644
index 0000000000..4acc2b2c0e
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom-test-utils-dev.js
@@ -0,0 +1,1302 @@
+/** @license React v16.8.6
+ * react-dom-test-utils.development.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require("resource://devtools/client/shared/vendor/react.js"), require("resource://devtools/client/shared/vendor/react-dom.js")) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react', 'devtools/client/shared/vendor/react-dom'], factory) :
+ (global.ReactTestUtils = factory(global.React,global.ReactDOM));
+}(this, (function (React,ReactDOM) { 'use strict';
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+var validateFormat = function () {};
+
+{
+ validateFormat = function (format) {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ };
+}
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ validateFormat(format);
+
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warningWithoutStack = function () {};
+
+{
+ warningWithoutStack = function (condition, format) {
+ for (var _len = arguments.length, args = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
+ args[_key - 2] = arguments[_key];
+ }
+
+ if (format === undefined) {
+ throw new Error('`warningWithoutStack(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (args.length > 8) {
+ // Check before the condition to catch violations early.
+ throw new Error('warningWithoutStack() currently supports at most 8 arguments.');
+ }
+ if (condition) {
+ return;
+ }
+ if (typeof console !== 'undefined') {
+ var argsWithFormat = args.map(function (item) {
+ return '' + item;
+ });
+ argsWithFormat.unshift('Warning: ' + format);
+
+ // We intentionally don't use spread (or .apply) directly because it
+ // breaks IE9: https://github.com/facebook/react/issues/13610
+ Function.prototype.apply.call(console.error, console, argsWithFormat);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+var warningWithoutStack$1 = warningWithoutStack;
+
+/**
+ * `ReactInstanceMap` maintains a mapping from a public facing stateful
+ * instance (key) and the internal representation (value). This allows public
+ * methods to accept the user facing instance as an argument and map them back
+ * to internal methods.
+ *
+ * Note that this module is currently shared and assumed to be stateless.
+ * If this becomes an actual Map, that will break.
+ */
+
+/**
+ * This API should be called `delete` but we'd have to make sure to always
+ * transform these to strings for IE support. When this transform is fully
+ * supported we can rename it.
+ */
+
+
+function get(key) {
+ return key._reactInternalFiber;
+}
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+
+var FunctionComponent = 0;
+var ClassComponent = 1;
+ // Before we know whether it is function or class
+var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
+ // A subtree. Could be an entry point to a different renderer.
+var HostComponent = 5;
+var HostText = 6;
+
+// Don't change these two values. They're used by React Dev Tools.
+var NoEffect = /* */0;
+
+
+// You can change the rest (and add more).
+var Placement = /* */2;
+
+
+
+
+
+
+
+
+
+
+// Passive & Update & Callback & Ref & Snapshot
+
+
+// Union of all host effects
+
+var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
+
+var MOUNTING = 1;
+var MOUNTED = 2;
+var UNMOUNTED = 3;
+
+function isFiberMountedImpl(fiber) {
+ var node = fiber;
+ if (!fiber.alternate) {
+ // If there is no alternate, this might be a new tree that isn't inserted
+ // yet. If it is, then it will have a pending insertion effect on it.
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ while (node.return) {
+ node = node.return;
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ }
+ } else {
+ while (node.return) {
+ node = node.return;
+ }
+ }
+ if (node.tag === HostRoot) {
+ // TODO: Check if this was a nested HostRoot when used with
+ // renderContainerIntoSubtree.
+ return MOUNTED;
+ }
+ // If we didn't hit the root, that means that we're in an disconnected tree
+ // that has been unmounted.
+ return UNMOUNTED;
+}
+
+
+
+
+
+function assertIsMounted(fiber) {
+ !(isFiberMountedImpl(fiber) === MOUNTED) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0;
+}
+
+function findCurrentFiberUsingSlowPath(fiber) {
+ var alternate = fiber.alternate;
+ if (!alternate) {
+ // If there is no alternate, then we only need to check if it is mounted.
+ var state = isFiberMountedImpl(fiber);
+ !(state !== UNMOUNTED) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0;
+ if (state === MOUNTING) {
+ return null;
+ }
+ return fiber;
+ }
+ // If we have two possible branches, we'll walk backwards up to the root
+ // to see what path the root points to. On the way we may hit one of the
+ // special cases and we'll deal with them.
+ var a = fiber;
+ var b = alternate;
+ while (true) {
+ var parentA = a.return;
+ var parentB = parentA ? parentA.alternate : null;
+ if (!parentA || !parentB) {
+ // We're at the root.
+ break;
+ }
+
+ // If both copies of the parent fiber point to the same child, we can
+ // assume that the child is current. This happens when we bailout on low
+ // priority: the bailed out fiber's child reuses the current child.
+ if (parentA.child === parentB.child) {
+ var child = parentA.child;
+ while (child) {
+ if (child === a) {
+ // We've determined that A is the current branch.
+ assertIsMounted(parentA);
+ return fiber;
+ }
+ if (child === b) {
+ // We've determined that B is the current branch.
+ assertIsMounted(parentA);
+ return alternate;
+ }
+ child = child.sibling;
+ }
+ // We should never have an alternate for any mounting node. So the only
+ // way this could possibly happen is if this was unmounted, if at all.
+ invariant(false, 'Unable to find node on an unmounted component.');
+ }
+
+ if (a.return !== b.return) {
+ // The return pointer of A and the return pointer of B point to different
+ // fibers. We assume that return pointers never criss-cross, so A must
+ // belong to the child set of A.return, and B must belong to the child
+ // set of B.return.
+ a = parentA;
+ b = parentB;
+ } else {
+ // The return pointers point to the same fiber. We'll have to use the
+ // default, slow path: scan the child sets of each parent alternate to see
+ // which child belongs to which set.
+ //
+ // Search parent A's child set
+ var didFindChild = false;
+ var _child = parentA.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentA;
+ b = parentB;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentA;
+ a = parentB;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ if (!didFindChild) {
+ // Search parent B's child set
+ _child = parentB.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentB;
+ b = parentA;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentB;
+ a = parentA;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ !didFindChild ? invariant(false, 'Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue.') : void 0;
+ }
+ }
+
+ !(a.alternate === b) ? invariant(false, 'Return fibers should always be each others\' alternates. This error is likely caused by a bug in React. Please file an issue.') : void 0;
+ }
+ // If the root is not a host container, we're in a disconnected tree. I.e.
+ // unmounted.
+ !(a.tag === HostRoot) ? invariant(false, 'Unable to find node on an unmounted component.') : void 0;
+ if (a.stateNode.current === a) {
+ // We've determined that A is the current branch.
+ return fiber;
+ }
+ // Otherwise B has to be current branch.
+ return alternate;
+}
+
+/* eslint valid-typeof: 0 */
+
+var EVENT_POOL_SIZE = 10;
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var EventInterface = {
+ type: null,
+ target: null,
+ // currentTarget is set when dispatching; no use in copying it here
+ currentTarget: function () {
+ return null;
+ },
+ eventPhase: null,
+ bubbles: null,
+ cancelable: null,
+ timeStamp: function (event) {
+ return event.timeStamp || Date.now();
+ },
+ defaultPrevented: null,
+ isTrusted: null
+};
+
+function functionThatReturnsTrue() {
+ return true;
+}
+
+function functionThatReturnsFalse() {
+ return false;
+}
+
+/**
+ * Synthetic events are dispatched by event plugins, typically in response to a
+ * top-level event delegation handler.
+ *
+ * These systems should generally use pooling to reduce the frequency of garbage
+ * collection. The system should check `isPersistent` to determine whether the
+ * event should be released into the pool after being dispatched. Users that
+ * need a persisted event should invoke `persist`.
+ *
+ * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
+ * normalizing browser quirks. Subclasses do not necessarily have to implement a
+ * DOM interface; custom application-specific events can also subclass this.
+ *
+ * @param {object} dispatchConfig Configuration used to dispatch this event.
+ * @param {*} targetInst Marker identifying the event target.
+ * @param {object} nativeEvent Native browser event.
+ * @param {DOMEventTarget} nativeEventTarget Target node.
+ */
+function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
+ {
+ // these have a getter/setter for warnings
+ delete this.nativeEvent;
+ delete this.preventDefault;
+ delete this.stopPropagation;
+ delete this.isDefaultPrevented;
+ delete this.isPropagationStopped;
+ }
+
+ this.dispatchConfig = dispatchConfig;
+ this._targetInst = targetInst;
+ this.nativeEvent = nativeEvent;
+
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ if (!Interface.hasOwnProperty(propName)) {
+ continue;
+ }
+ {
+ delete this[propName]; // this has a getter/setter for warnings
+ }
+ var normalize = Interface[propName];
+ if (normalize) {
+ this[propName] = normalize(nativeEvent);
+ } else {
+ if (propName === 'target') {
+ this.target = nativeEventTarget;
+ } else {
+ this[propName] = nativeEvent[propName];
+ }
+ }
+ }
+
+ var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
+ if (defaultPrevented) {
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ } else {
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ }
+ this.isPropagationStopped = functionThatReturnsFalse;
+ return this;
+}
+
+_assign(SyntheticEvent.prototype, {
+ preventDefault: function () {
+ this.defaultPrevented = true;
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else if (typeof event.returnValue !== 'unknown') {
+ event.returnValue = false;
+ }
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ },
+
+ stopPropagation: function () {
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.stopPropagation) {
+ event.stopPropagation();
+ } else if (typeof event.cancelBubble !== 'unknown') {
+ // The ChangeEventPlugin registers a "propertychange" event for
+ // IE. This event does not support bubbling or cancelling, and
+ // any references to cancelBubble throw "Member not found". A
+ // typeof check of "unknown" circumvents this issue (and is also
+ // IE specific).
+ event.cancelBubble = true;
+ }
+
+ this.isPropagationStopped = functionThatReturnsTrue;
+ },
+
+ /**
+ * We release all dispatched `SyntheticEvent`s after each event loop, adding
+ * them back into the pool. This allows a way to hold onto a reference that
+ * won't be added back into the pool.
+ */
+ persist: function () {
+ this.isPersistent = functionThatReturnsTrue;
+ },
+
+ /**
+ * Checks if this event should be released back into the pool.
+ *
+ * @return {boolean} True if this should not be released, false otherwise.
+ */
+ isPersistent: functionThatReturnsFalse,
+
+ /**
+ * `PooledClass` looks for `destructor` on each instance it releases.
+ */
+ destructor: function () {
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ {
+ Object.defineProperty(this, propName, getPooledWarningPropertyDefinition(propName, Interface[propName]));
+ }
+ }
+ this.dispatchConfig = null;
+ this._targetInst = null;
+ this.nativeEvent = null;
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ this.isPropagationStopped = functionThatReturnsFalse;
+ this._dispatchListeners = null;
+ this._dispatchInstances = null;
+ {
+ Object.defineProperty(this, 'nativeEvent', getPooledWarningPropertyDefinition('nativeEvent', null));
+ Object.defineProperty(this, 'isDefaultPrevented', getPooledWarningPropertyDefinition('isDefaultPrevented', functionThatReturnsFalse));
+ Object.defineProperty(this, 'isPropagationStopped', getPooledWarningPropertyDefinition('isPropagationStopped', functionThatReturnsFalse));
+ Object.defineProperty(this, 'preventDefault', getPooledWarningPropertyDefinition('preventDefault', function () {}));
+ Object.defineProperty(this, 'stopPropagation', getPooledWarningPropertyDefinition('stopPropagation', function () {}));
+ }
+ }
+});
+
+SyntheticEvent.Interface = EventInterface;
+
+/**
+ * Helper to reduce boilerplate when creating subclasses.
+ */
+SyntheticEvent.extend = function (Interface) {
+ var Super = this;
+
+ var E = function () {};
+ E.prototype = Super.prototype;
+ var prototype = new E();
+
+ function Class() {
+ return Super.apply(this, arguments);
+ }
+ _assign(prototype, Class.prototype);
+ Class.prototype = prototype;
+ Class.prototype.constructor = Class;
+
+ Class.Interface = _assign({}, Super.Interface, Interface);
+ Class.extend = Super.extend;
+ addEventPoolingTo(Class);
+
+ return Class;
+};
+
+addEventPoolingTo(SyntheticEvent);
+
+/**
+ * Helper to nullify syntheticEvent instance properties when destructing
+ *
+ * @param {String} propName
+ * @param {?object} getVal
+ * @return {object} defineProperty object
+ */
+function getPooledWarningPropertyDefinition(propName, getVal) {
+ var isFunction = typeof getVal === 'function';
+ return {
+ configurable: true,
+ set: set,
+ get: get
+ };
+
+ function set(val) {
+ var action = isFunction ? 'setting the method' : 'setting the property';
+ warn(action, 'This is effectively a no-op');
+ return val;
+ }
+
+ function get() {
+ var action = isFunction ? 'accessing the method' : 'accessing the property';
+ var result = isFunction ? 'This is a no-op function' : 'This is set to null';
+ warn(action, result);
+ return getVal;
+ }
+
+ function warn(action, result) {
+ var warningCondition = false;
+ !warningCondition ? warningWithoutStack$1(false, "This synthetic event is reused for performance reasons. If you're seeing this, " + "you're %s `%s` on a released/nullified synthetic event. %s. " + 'If you must keep the original synthetic event around, use event.persist(). ' + 'See https://fb.me/react-event-pooling for more information.', action, propName, result) : void 0;
+ }
+}
+
+function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
+ var EventConstructor = this;
+ if (EventConstructor.eventPool.length) {
+ var instance = EventConstructor.eventPool.pop();
+ EventConstructor.call(instance, dispatchConfig, targetInst, nativeEvent, nativeInst);
+ return instance;
+ }
+ return new EventConstructor(dispatchConfig, targetInst, nativeEvent, nativeInst);
+}
+
+function releasePooledEvent(event) {
+ var EventConstructor = this;
+ !(event instanceof EventConstructor) ? invariant(false, 'Trying to release an event instance into a pool of a different type.') : void 0;
+ event.destructor();
+ if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
+ EventConstructor.eventPool.push(event);
+ }
+}
+
+function addEventPoolingTo(EventConstructor) {
+ EventConstructor.eventPool = [];
+ EventConstructor.getPooled = getPooledEvent;
+ EventConstructor.release = releasePooledEvent;
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var lowPriorityWarning = function () {};
+
+{
+ var printWarning = function (format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.warn(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ lowPriorityWarning = function (condition, format) {
+ if (format === undefined) {
+ throw new Error('`lowPriorityWarning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var lowPriorityWarning$1 = lowPriorityWarning;
+
+/**
+ * HTML nodeType values that represent the type of the node
+ */
+
+var ELEMENT_NODE = 1;
+
+// Do not uses the below two methods directly!
+// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
+// (It is the only module that is allowed to access these methods.)
+
+function unsafeCastStringToDOMTopLevelType(topLevelType) {
+ return topLevelType;
+}
+
+var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
+
+/**
+ * Generate a mapping of standard vendor prefixes using the defined style property and event name.
+ *
+ * @param {string} styleProp
+ * @param {string} eventName
+ * @returns {object}
+ */
+function makePrefixMap(styleProp, eventName) {
+ var prefixes = {};
+
+ prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
+ prefixes['Webkit' + styleProp] = 'webkit' + eventName;
+ prefixes['Moz' + styleProp] = 'moz' + eventName;
+
+ return prefixes;
+}
+
+/**
+ * A list of event names to a configurable list of vendor prefixes.
+ */
+var vendorPrefixes = {
+ animationend: makePrefixMap('Animation', 'AnimationEnd'),
+ animationiteration: makePrefixMap('Animation', 'AnimationIteration'),
+ animationstart: makePrefixMap('Animation', 'AnimationStart'),
+ transitionend: makePrefixMap('Transition', 'TransitionEnd')
+};
+
+/**
+ * Event names that have already been detected and prefixed (if applicable).
+ */
+var prefixedEventNames = {};
+
+/**
+ * Element to check for prefixes on.
+ */
+var style = {};
+
+/**
+ * Bootstrap if a DOM exists.
+ */
+if (canUseDOM) {
+ style = document.createElementNS('http://www.w3.org/1999/xhtml', 'div').style;
+
+ // On some platforms, in particular some releases of Android 4.x,
+ // the un-prefixed "animation" and "transition" properties are defined on the
+ // style object but the events that fire will still be prefixed, so we need
+ // to check if the un-prefixed events are usable, and if not remove them from the map.
+ if (!('AnimationEvent' in window)) {
+ delete vendorPrefixes.animationend.animation;
+ delete vendorPrefixes.animationiteration.animation;
+ delete vendorPrefixes.animationstart.animation;
+ }
+
+ // Same as above
+ if (!('TransitionEvent' in window)) {
+ delete vendorPrefixes.transitionend.transition;
+ }
+}
+
+/**
+ * Attempts to determine the correct vendor prefixed event name.
+ *
+ * @param {string} eventName
+ * @returns {string}
+ */
+function getVendorPrefixedEventName(eventName) {
+ if (prefixedEventNames[eventName]) {
+ return prefixedEventNames[eventName];
+ } else if (!vendorPrefixes[eventName]) {
+ return eventName;
+ }
+
+ var prefixMap = vendorPrefixes[eventName];
+
+ for (var styleProp in prefixMap) {
+ if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
+ return prefixedEventNames[eventName] = prefixMap[styleProp];
+ }
+ }
+
+ return eventName;
+}
+
+/**
+ * To identify top level events in ReactDOM, we use constants defined by this
+ * module. This is the only module that uses the unsafe* methods to express
+ * that the constants actually correspond to the browser event names. This lets
+ * us save some bundle size by avoiding a top level type -> event name map.
+ * The rest of ReactDOM code should import top level types from this file.
+ */
+var TOP_ABORT = unsafeCastStringToDOMTopLevelType('abort');
+var TOP_ANIMATION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationend'));
+var TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationiteration'));
+var TOP_ANIMATION_START = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationstart'));
+var TOP_BLUR = unsafeCastStringToDOMTopLevelType('blur');
+var TOP_CAN_PLAY = unsafeCastStringToDOMTopLevelType('canplay');
+var TOP_CAN_PLAY_THROUGH = unsafeCastStringToDOMTopLevelType('canplaythrough');
+var TOP_CANCEL = unsafeCastStringToDOMTopLevelType('cancel');
+var TOP_CHANGE = unsafeCastStringToDOMTopLevelType('change');
+var TOP_CLICK = unsafeCastStringToDOMTopLevelType('click');
+var TOP_CLOSE = unsafeCastStringToDOMTopLevelType('close');
+var TOP_COMPOSITION_END = unsafeCastStringToDOMTopLevelType('compositionend');
+var TOP_COMPOSITION_START = unsafeCastStringToDOMTopLevelType('compositionstart');
+var TOP_COMPOSITION_UPDATE = unsafeCastStringToDOMTopLevelType('compositionupdate');
+var TOP_CONTEXT_MENU = unsafeCastStringToDOMTopLevelType('contextmenu');
+var TOP_COPY = unsafeCastStringToDOMTopLevelType('copy');
+var TOP_CUT = unsafeCastStringToDOMTopLevelType('cut');
+var TOP_DOUBLE_CLICK = unsafeCastStringToDOMTopLevelType('dblclick');
+
+var TOP_DRAG = unsafeCastStringToDOMTopLevelType('drag');
+var TOP_DRAG_END = unsafeCastStringToDOMTopLevelType('dragend');
+var TOP_DRAG_ENTER = unsafeCastStringToDOMTopLevelType('dragenter');
+var TOP_DRAG_EXIT = unsafeCastStringToDOMTopLevelType('dragexit');
+var TOP_DRAG_LEAVE = unsafeCastStringToDOMTopLevelType('dragleave');
+var TOP_DRAG_OVER = unsafeCastStringToDOMTopLevelType('dragover');
+var TOP_DRAG_START = unsafeCastStringToDOMTopLevelType('dragstart');
+var TOP_DROP = unsafeCastStringToDOMTopLevelType('drop');
+var TOP_DURATION_CHANGE = unsafeCastStringToDOMTopLevelType('durationchange');
+var TOP_EMPTIED = unsafeCastStringToDOMTopLevelType('emptied');
+var TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
+var TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
+var TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
+var TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
+
+var TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
+
+var TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
+var TOP_KEY_PRESS = unsafeCastStringToDOMTopLevelType('keypress');
+var TOP_KEY_UP = unsafeCastStringToDOMTopLevelType('keyup');
+var TOP_LOAD = unsafeCastStringToDOMTopLevelType('load');
+var TOP_LOAD_START = unsafeCastStringToDOMTopLevelType('loadstart');
+var TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
+var TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType('loadedmetadata');
+
+var TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
+var TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
+var TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
+var TOP_MOUSE_OVER = unsafeCastStringToDOMTopLevelType('mouseover');
+var TOP_MOUSE_UP = unsafeCastStringToDOMTopLevelType('mouseup');
+var TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
+var TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
+var TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
+var TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
+
+
+
+
+
+
+
+
+var TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
+var TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
+
+var TOP_SCROLL = unsafeCastStringToDOMTopLevelType('scroll');
+var TOP_SEEKED = unsafeCastStringToDOMTopLevelType('seeked');
+var TOP_SEEKING = unsafeCastStringToDOMTopLevelType('seeking');
+var TOP_SELECTION_CHANGE = unsafeCastStringToDOMTopLevelType('selectionchange');
+var TOP_STALLED = unsafeCastStringToDOMTopLevelType('stalled');
+
+var TOP_SUSPEND = unsafeCastStringToDOMTopLevelType('suspend');
+var TOP_TEXT_INPUT = unsafeCastStringToDOMTopLevelType('textInput');
+var TOP_TIME_UPDATE = unsafeCastStringToDOMTopLevelType('timeupdate');
+var TOP_TOGGLE = unsafeCastStringToDOMTopLevelType('toggle');
+var TOP_TOUCH_CANCEL = unsafeCastStringToDOMTopLevelType('touchcancel');
+var TOP_TOUCH_END = unsafeCastStringToDOMTopLevelType('touchend');
+var TOP_TOUCH_MOVE = unsafeCastStringToDOMTopLevelType('touchmove');
+var TOP_TOUCH_START = unsafeCastStringToDOMTopLevelType('touchstart');
+var TOP_TRANSITION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('transitionend'));
+var TOP_VOLUME_CHANGE = unsafeCastStringToDOMTopLevelType('volumechange');
+var TOP_WAITING = unsafeCastStringToDOMTopLevelType('waiting');
+var TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');
+
+// List of events that need to be individually attached to media elements.
+// Note that events in this list will *not* be listened to at the top level
+// unless they're explicitly whitelisted in `ReactBrowserEventEmitter.listenTo`.
+
+// for .act's return value
+var findDOMNode = ReactDOM.findDOMNode;
+// Keep in sync with ReactDOMUnstableNativeDependencies.js
+// and ReactDOM.js:
+
+var _ReactDOM$__SECRET_IN = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events;
+var getInstanceFromNode = _ReactDOM$__SECRET_IN[0];
+var getNodeFromInstance = _ReactDOM$__SECRET_IN[1];
+var getFiberCurrentPropsFromNode = _ReactDOM$__SECRET_IN[2];
+var injectEventPluginsByName = _ReactDOM$__SECRET_IN[3];
+var eventNameDispatchConfigs = _ReactDOM$__SECRET_IN[4];
+var accumulateTwoPhaseDispatches = _ReactDOM$__SECRET_IN[5];
+var accumulateDirectDispatches = _ReactDOM$__SECRET_IN[6];
+var enqueueStateRestore = _ReactDOM$__SECRET_IN[7];
+var restoreStateIfNeeded = _ReactDOM$__SECRET_IN[8];
+var dispatchEvent = _ReactDOM$__SECRET_IN[9];
+var runEventsInBatch = _ReactDOM$__SECRET_IN[10];
+
+
+function Event(suffix) {}
+
+var hasWarnedAboutDeprecatedMockComponent = false;
+
+/**
+ * @class ReactTestUtils
+ */
+
+/**
+ * Simulates a top level event being dispatched from a raw event that occurred
+ * on an `Element` node.
+ * @param {number} topLevelType A number from `TopLevelEventTypes`
+ * @param {!Element} node The dom to simulate an event occurring on.
+ * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
+ */
+function simulateNativeEventOnNode(topLevelType, node, fakeNativeEvent) {
+ fakeNativeEvent.target = node;
+ dispatchEvent(topLevelType, fakeNativeEvent);
+}
+
+/**
+ * Simulates a top level event being dispatched from a raw event that occurred
+ * on the `ReactDOMComponent` `comp`.
+ * @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
+ * @param {!ReactDOMComponent} comp
+ * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
+ */
+function simulateNativeEventOnDOMComponent(topLevelType, comp, fakeNativeEvent) {
+ simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
+}
+
+function findAllInRenderedFiberTreeInternal(fiber, test) {
+ if (!fiber) {
+ return [];
+ }
+ var currentParent = findCurrentFiberUsingSlowPath(fiber);
+ if (!currentParent) {
+ return [];
+ }
+ var node = currentParent;
+ var ret = [];
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText || node.tag === ClassComponent || node.tag === FunctionComponent) {
+ var publicInst = node.stateNode;
+ if (test(publicInst)) {
+ ret.push(publicInst);
+ }
+ }
+ if (node.child) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === currentParent) {
+ return ret;
+ }
+ while (!node.sibling) {
+ if (!node.return || node.return === currentParent) {
+ return ret;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function validateClassInstance(inst, methodName) {
+ if (!inst) {
+ // This is probably too relaxed but it's existing behavior.
+ return;
+ }
+ if (get(inst)) {
+ // This is a public instance indeed.
+ return;
+ }
+ var received = void 0;
+ var stringified = '' + inst;
+ if (Array.isArray(inst)) {
+ received = 'an array';
+ } else if (inst && inst.nodeType === ELEMENT_NODE && inst.tagName) {
+ received = 'a DOM node';
+ } else if (stringified === '[object Object]') {
+ received = 'object with keys {' + Object.keys(inst).join(', ') + '}';
+ } else {
+ received = stringified;
+ }
+ invariant(false, '%s(...): the first argument must be a React class instance. Instead received: %s.', methodName, received);
+}
+
+// a stub element, lazily initialized, used by act() when flushing effects
+var actContainerElement = null;
+
+/**
+ * Utilities for making it easy to test React components.
+ *
+ * See https://reactjs.org/docs/test-utils.html
+ *
+ * Todo: Support the entire DOM.scry query syntax. For now, these simple
+ * utilities will suffice for testing purposes.
+ * @lends ReactTestUtils
+ */
+var ReactTestUtils = {
+ renderIntoDocument: function (element) {
+ var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ // None of our tests actually require attaching the container to the
+ // DOM, and doing so creates a mess that we rely on test isolation to
+ // clean up, so we're going to stop honoring the name of this method
+ // (and probably rename it eventually) if no problems arise.
+ // document.documentElement.appendChild(div);
+ return ReactDOM.render(element, div);
+ },
+
+ isElement: function (element) {
+ return React.isValidElement(element);
+ },
+
+ isElementOfType: function (inst, convenienceConstructor) {
+ return React.isValidElement(inst) && inst.type === convenienceConstructor;
+ },
+
+ isDOMComponent: function (inst) {
+ return !!(inst && inst.nodeType === ELEMENT_NODE && inst.tagName);
+ },
+
+ isDOMComponentElement: function (inst) {
+ return !!(inst && React.isValidElement(inst) && !!inst.tagName);
+ },
+
+ isCompositeComponent: function (inst) {
+ if (ReactTestUtils.isDOMComponent(inst)) {
+ // Accessing inst.setState warns; just return false as that'll be what
+ // this returns when we have DOM nodes as refs directly
+ return false;
+ }
+ return inst != null && typeof inst.render === 'function' && typeof inst.setState === 'function';
+ },
+
+ isCompositeComponentWithType: function (inst, type) {
+ if (!ReactTestUtils.isCompositeComponent(inst)) {
+ return false;
+ }
+ var internalInstance = get(inst);
+ var constructor = internalInstance.type;
+ return constructor === type;
+ },
+
+ findAllInRenderedTree: function (inst, test) {
+ validateClassInstance(inst, 'findAllInRenderedTree');
+ if (!inst) {
+ return [];
+ }
+ var internalInstance = get(inst);
+ return findAllInRenderedFiberTreeInternal(internalInstance, test);
+ },
+
+ /**
+ * Finds all instance of components in the rendered tree that are DOM
+ * components with the class name matching `className`.
+ * @return {array} an array of all the matches.
+ */
+ scryRenderedDOMComponentsWithClass: function (root, classNames) {
+ validateClassInstance(root, 'scryRenderedDOMComponentsWithClass');
+ return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
+ if (ReactTestUtils.isDOMComponent(inst)) {
+ var className = inst.className;
+ if (typeof className !== 'string') {
+ // SVG, probably.
+ className = inst.getAttribute('class') || '';
+ }
+ var classList = className.split(/\s+/);
+
+ if (!Array.isArray(classNames)) {
+ !(classNames !== undefined) ? invariant(false, 'TestUtils.scryRenderedDOMComponentsWithClass expects a className as a second argument.') : void 0;
+ classNames = classNames.split(/\s+/);
+ }
+ return classNames.every(function (name) {
+ return classList.indexOf(name) !== -1;
+ });
+ }
+ return false;
+ });
+ },
+
+ /**
+ * Like scryRenderedDOMComponentsWithClass but expects there to be one result,
+ * and returns that one result, or throws exception if there is any other
+ * number of matches besides one.
+ * @return {!ReactDOMComponent} The one match.
+ */
+ findRenderedDOMComponentWithClass: function (root, className) {
+ validateClassInstance(root, 'findRenderedDOMComponentWithClass');
+ var all = ReactTestUtils.scryRenderedDOMComponentsWithClass(root, className);
+ if (all.length !== 1) {
+ throw new Error('Did not find exactly one match (found: ' + all.length + ') ' + 'for class:' + className);
+ }
+ return all[0];
+ },
+
+ /**
+ * Finds all instance of components in the rendered tree that are DOM
+ * components with the tag name matching `tagName`.
+ * @return {array} an array of all the matches.
+ */
+ scryRenderedDOMComponentsWithTag: function (root, tagName) {
+ validateClassInstance(root, 'scryRenderedDOMComponentsWithTag');
+ return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
+ return ReactTestUtils.isDOMComponent(inst) && inst.tagName.toUpperCase() === tagName.toUpperCase();
+ });
+ },
+
+ /**
+ * Like scryRenderedDOMComponentsWithTag but expects there to be one result,
+ * and returns that one result, or throws exception if there is any other
+ * number of matches besides one.
+ * @return {!ReactDOMComponent} The one match.
+ */
+ findRenderedDOMComponentWithTag: function (root, tagName) {
+ validateClassInstance(root, 'findRenderedDOMComponentWithTag');
+ var all = ReactTestUtils.scryRenderedDOMComponentsWithTag(root, tagName);
+ if (all.length !== 1) {
+ throw new Error('Did not find exactly one match (found: ' + all.length + ') ' + 'for tag:' + tagName);
+ }
+ return all[0];
+ },
+
+ /**
+ * Finds all instances of components with type equal to `componentType`.
+ * @return {array} an array of all the matches.
+ */
+ scryRenderedComponentsWithType: function (root, componentType) {
+ validateClassInstance(root, 'scryRenderedComponentsWithType');
+ return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
+ return ReactTestUtils.isCompositeComponentWithType(inst, componentType);
+ });
+ },
+
+ /**
+ * Same as `scryRenderedComponentsWithType` but expects there to be one result
+ * and returns that one result, or throws exception if there is any other
+ * number of matches besides one.
+ * @return {!ReactComponent} The one match.
+ */
+ findRenderedComponentWithType: function (root, componentType) {
+ validateClassInstance(root, 'findRenderedComponentWithType');
+ var all = ReactTestUtils.scryRenderedComponentsWithType(root, componentType);
+ if (all.length !== 1) {
+ throw new Error('Did not find exactly one match (found: ' + all.length + ') ' + 'for componentType:' + componentType);
+ }
+ return all[0];
+ },
+
+ /**
+ * Pass a mocked component module to this method to augment it with
+ * useful methods that allow it to be used as a dummy React component.
+ * Instead of rendering as usual, the component will become a simple
+ * <div> containing any provided children.
+ *
+ * @param {object} module the mock function object exported from a
+ * module that defines the component to be mocked
+ * @param {?string} mockTagName optional dummy root tag name to return
+ * from render method (overrides
+ * module.mockTagName if provided)
+ * @return {object} the ReactTestUtils object (for chaining)
+ */
+ mockComponent: function (module, mockTagName) {
+ if (!hasWarnedAboutDeprecatedMockComponent) {
+ hasWarnedAboutDeprecatedMockComponent = true;
+ lowPriorityWarning$1(false, 'ReactTestUtils.mockComponent() is deprecated. ' + 'Use shallow rendering or jest.mock() instead.\n\n' + 'See https://fb.me/test-utils-mock-component for more information.');
+ }
+
+ mockTagName = mockTagName || module.mockTagName || 'div';
+
+ module.prototype.render.mockImplementation(function () {
+ return React.createElement(mockTagName, null, this.props.children);
+ });
+
+ return this;
+ },
+
+ nativeTouchData: function (x, y) {
+ return {
+ touches: [{ pageX: x, pageY: y }]
+ };
+ },
+
+ Simulate: null,
+ SimulateNative: {},
+
+ act: function (callback) {
+ if (actContainerElement === null) {
+ // warn if we can't actually create the stub element
+ {
+ !(typeof document !== 'undefined' && document !== null && typeof document.createElement === 'function') ? warningWithoutStack$1(false, 'It looks like you called TestUtils.act(...) in a non-browser environment. ' + "If you're using TestRenderer for your tests, you should call " + 'TestRenderer.act(...) instead of TestUtils.act(...).') : void 0;
+ }
+ // then make it
+ actContainerElement = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ }
+
+ var result = ReactDOM.unstable_batchedUpdates(callback);
+ // note: keep these warning messages in sync with
+ // createReactNoop.js and ReactTestRenderer.js
+ {
+ if (result !== undefined) {
+ var addendum = void 0;
+ if (result !== null && typeof result.then === 'function') {
+ addendum = '\n\nIt looks like you wrote ReactTestUtils.act(async () => ...), ' + 'or returned a Promise from the callback passed to it. ' + 'Putting asynchronous logic inside ReactTestUtils.act(...) is not supported.\n';
+ } else {
+ addendum = ' You returned: ' + result;
+ }
+ warningWithoutStack$1(false, 'The callback passed to ReactTestUtils.act(...) function must not return anything.%s', addendum);
+ }
+ }
+ ReactDOM.render(React.createElement('div', null), actContainerElement);
+ // we want the user to not expect a return,
+ // but we want to warn if they use it like they can await on it.
+ return {
+ then: function () {
+ {
+ warningWithoutStack$1(false, 'Do not await the result of calling ReactTestUtils.act(...), it is not a Promise.');
+ }
+ }
+ };
+ }
+};
+
+/**
+ * Exports:
+ *
+ * - `ReactTestUtils.Simulate.click(Element)`
+ * - `ReactTestUtils.Simulate.mouseMove(Element)`
+ * - `ReactTestUtils.Simulate.change(Element)`
+ * - ... (All keys from event plugin `eventTypes` objects)
+ */
+function makeSimulator(eventType) {
+ return function (domNode, eventData) {
+ !!React.isValidElement(domNode) ? invariant(false, 'TestUtils.Simulate expected a DOM node as the first argument but received a React element. Pass the DOM node you wish to simulate the event on instead. Note that TestUtils.Simulate will not work if you are using shallow rendering.') : void 0;
+ !!ReactTestUtils.isCompositeComponent(domNode) ? invariant(false, 'TestUtils.Simulate expected a DOM node as the first argument but received a component instance. Pass the DOM node you wish to simulate the event on instead.') : void 0;
+
+ var dispatchConfig = eventNameDispatchConfigs[eventType];
+
+ var fakeNativeEvent = new Event();
+ fakeNativeEvent.target = domNode;
+ fakeNativeEvent.type = eventType.toLowerCase();
+
+ // We don't use SyntheticEvent.getPooled in order to not have to worry about
+ // properly destroying any properties assigned from `eventData` upon release
+ var targetInst = getInstanceFromNode(domNode);
+ var event = new SyntheticEvent(dispatchConfig, targetInst, fakeNativeEvent, domNode);
+
+ // Since we aren't using pooling, always persist the event. This will make
+ // sure it's marked and won't warn when setting additional properties.
+ event.persist();
+ _assign(event, eventData);
+
+ if (dispatchConfig.phasedRegistrationNames) {
+ accumulateTwoPhaseDispatches(event);
+ } else {
+ accumulateDirectDispatches(event);
+ }
+
+ ReactDOM.unstable_batchedUpdates(function () {
+ // Normally extractEvent enqueues a state restore, but we'll just always
+ // do that since we're by-passing it here.
+ enqueueStateRestore(domNode);
+ runEventsInBatch(event);
+ });
+ restoreStateIfNeeded();
+ };
+}
+
+function buildSimulators() {
+ ReactTestUtils.Simulate = {};
+
+ var eventType = void 0;
+ for (eventType in eventNameDispatchConfigs) {
+ /**
+ * @param {!Element|ReactDOMComponent} domComponentOrNode
+ * @param {?object} eventData Fake event data to use in SyntheticEvent.
+ */
+ ReactTestUtils.Simulate[eventType] = makeSimulator(eventType);
+ }
+}
+
+buildSimulators();
+
+/**
+ * Exports:
+ *
+ * - `ReactTestUtils.SimulateNative.click(Element/ReactDOMComponent)`
+ * - `ReactTestUtils.SimulateNative.mouseMove(Element/ReactDOMComponent)`
+ * - `ReactTestUtils.SimulateNative.mouseIn/ReactDOMComponent)`
+ * - `ReactTestUtils.SimulateNative.mouseOut(Element/ReactDOMComponent)`
+ * - ... (All keys from `BrowserEventConstants.topLevelTypes`)
+ *
+ * Note: Top level event types are a subset of the entire set of handler types
+ * (which include a broader set of "synthetic" events). For example, onDragDone
+ * is a synthetic event. Except when testing an event plugin or React's event
+ * handling code specifically, you probably want to use ReactTestUtils.Simulate
+ * to dispatch synthetic events.
+ */
+
+function makeNativeSimulator(eventType, topLevelType) {
+ return function (domComponentOrNode, nativeEventData) {
+ var fakeNativeEvent = new Event(eventType);
+ _assign(fakeNativeEvent, nativeEventData);
+ if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
+ simulateNativeEventOnDOMComponent(topLevelType, domComponentOrNode, fakeNativeEvent);
+ } else if (domComponentOrNode.tagName) {
+ // Will allow on actual dom nodes.
+ simulateNativeEventOnNode(topLevelType, domComponentOrNode, fakeNativeEvent);
+ }
+ };
+}
+
+[[TOP_ABORT, 'abort'], [TOP_ANIMATION_END, 'animationEnd'], [TOP_ANIMATION_ITERATION, 'animationIteration'], [TOP_ANIMATION_START, 'animationStart'], [TOP_BLUR, 'blur'], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough'], [TOP_CAN_PLAY, 'canPlay'], [TOP_CANCEL, 'cancel'], [TOP_CHANGE, 'change'], [TOP_CLICK, 'click'], [TOP_CLOSE, 'close'], [TOP_COMPOSITION_END, 'compositionEnd'], [TOP_COMPOSITION_START, 'compositionStart'], [TOP_COMPOSITION_UPDATE, 'compositionUpdate'], [TOP_CONTEXT_MENU, 'contextMenu'], [TOP_COPY, 'copy'], [TOP_CUT, 'cut'], [TOP_DOUBLE_CLICK, 'doubleClick'], [TOP_DRAG_END, 'dragEnd'], [TOP_DRAG_ENTER, 'dragEnter'], [TOP_DRAG_EXIT, 'dragExit'], [TOP_DRAG_LEAVE, 'dragLeave'], [TOP_DRAG_OVER, 'dragOver'], [TOP_DRAG_START, 'dragStart'], [TOP_DRAG, 'drag'], [TOP_DROP, 'drop'], [TOP_DURATION_CHANGE, 'durationChange'], [TOP_EMPTIED, 'emptied'], [TOP_ENCRYPTED, 'encrypted'], [TOP_ENDED, 'ended'], [TOP_ERROR, 'error'], [TOP_FOCUS, 'focus'], [TOP_INPUT, 'input'], [TOP_KEY_DOWN, 'keyDown'], [TOP_KEY_PRESS, 'keyPress'], [TOP_KEY_UP, 'keyUp'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD, 'load'], [TOP_LOADED_DATA, 'loadedData'], [TOP_LOADED_METADATA, 'loadedMetadata'], [TOP_MOUSE_DOWN, 'mouseDown'], [TOP_MOUSE_MOVE, 'mouseMove'], [TOP_MOUSE_OUT, 'mouseOut'], [TOP_MOUSE_OVER, 'mouseOver'], [TOP_MOUSE_UP, 'mouseUp'], [TOP_PASTE, 'paste'], [TOP_PAUSE, 'pause'], [TOP_PLAY, 'play'], [TOP_PLAYING, 'playing'], [TOP_PROGRESS, 'progress'], [TOP_RATE_CHANGE, 'rateChange'], [TOP_SCROLL, 'scroll'], [TOP_SEEKED, 'seeked'], [TOP_SEEKING, 'seeking'], [TOP_SELECTION_CHANGE, 'selectionChange'], [TOP_STALLED, 'stalled'], [TOP_SUSPEND, 'suspend'], [TOP_TEXT_INPUT, 'textInput'], [TOP_TIME_UPDATE, 'timeUpdate'], [TOP_TOGGLE, 'toggle'], [TOP_TOUCH_CANCEL, 'touchCancel'], [TOP_TOUCH_END, 'touchEnd'], [TOP_TOUCH_MOVE, 'touchMove'], [TOP_TOUCH_START, 'touchStart'], [TOP_TRANSITION_END, 'transitionEnd'], [TOP_VOLUME_CHANGE, 'volumeChange'], [TOP_WAITING, 'waiting'], [TOP_WHEEL, 'wheel']].forEach(function (_ref) {
+ var topLevelType = _ref[0],
+ eventType = _ref[1];
+
+ /**
+ * @param {!Element|ReactDOMComponent} domComponentOrNode
+ * @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
+ */
+ ReactTestUtils.SimulateNative[eventType] = makeNativeSimulator(eventType, topLevelType);
+});
+
+
+
+var ReactTestUtils$2 = ({
+ default: ReactTestUtils
+});
+
+var ReactTestUtils$3 = ( ReactTestUtils$2 && ReactTestUtils ) || ReactTestUtils$2;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var testUtils = ReactTestUtils$3.default || ReactTestUtils$3;
+
+return testUtils;
+
+})));
diff --git a/devtools/client/shared/vendor/react-dom-test-utils.js b/devtools/client/shared/vendor/react-dom-test-utils.js
new file mode 100644
index 0000000000..f9de38e1d7
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom-test-utils.js
@@ -0,0 +1,1150 @@
+/** @license React v16.8.6
+ * react-dom-test-utils.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require("resource://devtools/client/shared/vendor/react.js"), require("resource://devtools/client/shared/vendor/react-dom.js")) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react', 'devtools/client/shared/vendor/react-dom'], factory) :
+ (global.ReactTestUtils = factory(global.React,global.ReactDOM));
+}(this, (function (React,ReactDOM) { 'use strict';
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+/**
+ * WARNING: DO NOT manually require this module.
+ * This is a replacement for `invariant(...)` used by the error code system
+ * and will _only_ be required by the corresponding babel pass.
+ * It always throws.
+ */
+function reactProdInvariant(code) {
+ var argCount = arguments.length - 1;
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
+ for (var argIdx = 0; argIdx < argCount; argIdx++) {
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
+ }
+ // Rename it so that our build transform doesn't attempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
+}
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+/**
+ * `ReactInstanceMap` maintains a mapping from a public facing stateful
+ * instance (key) and the internal representation (value). This allows public
+ * methods to accept the user facing instance as an argument and map them back
+ * to internal methods.
+ *
+ * Note that this module is currently shared and assumed to be stateless.
+ * If this becomes an actual Map, that will break.
+ */
+
+/**
+ * This API should be called `delete` but we'd have to make sure to always
+ * transform these to strings for IE support. When this transform is fully
+ * supported we can rename it.
+ */
+
+
+function get(key) {
+ return key._reactInternalFiber;
+}
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+
+var FunctionComponent = 0;
+var ClassComponent = 1;
+ // Before we know whether it is function or class
+var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
+ // A subtree. Could be an entry point to a different renderer.
+var HostComponent = 5;
+var HostText = 6;
+
+// Don't change these two values. They're used by React Dev Tools.
+var NoEffect = /* */0;
+
+
+// You can change the rest (and add more).
+var Placement = /* */2;
+
+
+
+
+
+
+
+
+
+
+// Passive & Update & Callback & Ref & Snapshot
+
+
+// Union of all host effects
+
+var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
+
+var MOUNTING = 1;
+var MOUNTED = 2;
+var UNMOUNTED = 3;
+
+function isFiberMountedImpl(fiber) {
+ var node = fiber;
+ if (!fiber.alternate) {
+ // If there is no alternate, this might be a new tree that isn't inserted
+ // yet. If it is, then it will have a pending insertion effect on it.
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ while (node.return) {
+ node = node.return;
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ }
+ } else {
+ while (node.return) {
+ node = node.return;
+ }
+ }
+ if (node.tag === HostRoot) {
+ // TODO: Check if this was a nested HostRoot when used with
+ // renderContainerIntoSubtree.
+ return MOUNTED;
+ }
+ // If we didn't hit the root, that means that we're in an disconnected tree
+ // that has been unmounted.
+ return UNMOUNTED;
+}
+
+
+
+
+
+function assertIsMounted(fiber) {
+ !(isFiberMountedImpl(fiber) === MOUNTED) ? reactProdInvariant('188') : void 0;
+}
+
+function findCurrentFiberUsingSlowPath(fiber) {
+ var alternate = fiber.alternate;
+ if (!alternate) {
+ // If there is no alternate, then we only need to check if it is mounted.
+ var state = isFiberMountedImpl(fiber);
+ !(state !== UNMOUNTED) ? reactProdInvariant('188') : void 0;
+ if (state === MOUNTING) {
+ return null;
+ }
+ return fiber;
+ }
+ // If we have two possible branches, we'll walk backwards up to the root
+ // to see what path the root points to. On the way we may hit one of the
+ // special cases and we'll deal with them.
+ var a = fiber;
+ var b = alternate;
+ while (true) {
+ var parentA = a.return;
+ var parentB = parentA ? parentA.alternate : null;
+ if (!parentA || !parentB) {
+ // We're at the root.
+ break;
+ }
+
+ // If both copies of the parent fiber point to the same child, we can
+ // assume that the child is current. This happens when we bailout on low
+ // priority: the bailed out fiber's child reuses the current child.
+ if (parentA.child === parentB.child) {
+ var child = parentA.child;
+ while (child) {
+ if (child === a) {
+ // We've determined that A is the current branch.
+ assertIsMounted(parentA);
+ return fiber;
+ }
+ if (child === b) {
+ // We've determined that B is the current branch.
+ assertIsMounted(parentA);
+ return alternate;
+ }
+ child = child.sibling;
+ }
+ // We should never have an alternate for any mounting node. So the only
+ // way this could possibly happen is if this was unmounted, if at all.
+ reactProdInvariant('188');
+ }
+
+ if (a.return !== b.return) {
+ // The return pointer of A and the return pointer of B point to different
+ // fibers. We assume that return pointers never criss-cross, so A must
+ // belong to the child set of A.return, and B must belong to the child
+ // set of B.return.
+ a = parentA;
+ b = parentB;
+ } else {
+ // The return pointers point to the same fiber. We'll have to use the
+ // default, slow path: scan the child sets of each parent alternate to see
+ // which child belongs to which set.
+ //
+ // Search parent A's child set
+ var didFindChild = false;
+ var _child = parentA.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentA;
+ b = parentB;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentA;
+ a = parentB;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ if (!didFindChild) {
+ // Search parent B's child set
+ _child = parentB.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentB;
+ b = parentA;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentB;
+ a = parentA;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ !didFindChild ? reactProdInvariant('189') : void 0;
+ }
+ }
+
+ !(a.alternate === b) ? reactProdInvariant('190') : void 0;
+ }
+ // If the root is not a host container, we're in a disconnected tree. I.e.
+ // unmounted.
+ !(a.tag === HostRoot) ? reactProdInvariant('188') : void 0;
+ if (a.stateNode.current === a) {
+ // We've determined that A is the current branch.
+ return fiber;
+ }
+ // Otherwise B has to be current branch.
+ return alternate;
+}
+
+/* eslint valid-typeof: 0 */
+
+var EVENT_POOL_SIZE = 10;
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var EventInterface = {
+ type: null,
+ target: null,
+ // currentTarget is set when dispatching; no use in copying it here
+ currentTarget: function () {
+ return null;
+ },
+ eventPhase: null,
+ bubbles: null,
+ cancelable: null,
+ timeStamp: function (event) {
+ return event.timeStamp || Date.now();
+ },
+ defaultPrevented: null,
+ isTrusted: null
+};
+
+function functionThatReturnsTrue() {
+ return true;
+}
+
+function functionThatReturnsFalse() {
+ return false;
+}
+
+/**
+ * Synthetic events are dispatched by event plugins, typically in response to a
+ * top-level event delegation handler.
+ *
+ * These systems should generally use pooling to reduce the frequency of garbage
+ * collection. The system should check `isPersistent` to determine whether the
+ * event should be released into the pool after being dispatched. Users that
+ * need a persisted event should invoke `persist`.
+ *
+ * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
+ * normalizing browser quirks. Subclasses do not necessarily have to implement a
+ * DOM interface; custom application-specific events can also subclass this.
+ *
+ * @param {object} dispatchConfig Configuration used to dispatch this event.
+ * @param {*} targetInst Marker identifying the event target.
+ * @param {object} nativeEvent Native browser event.
+ * @param {DOMEventTarget} nativeEventTarget Target node.
+ */
+function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
+ this.dispatchConfig = dispatchConfig;
+ this._targetInst = targetInst;
+ this.nativeEvent = nativeEvent;
+
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ if (!Interface.hasOwnProperty(propName)) {
+ continue;
+ }
+ var normalize = Interface[propName];
+ if (normalize) {
+ this[propName] = normalize(nativeEvent);
+ } else {
+ if (propName === 'target') {
+ this.target = nativeEventTarget;
+ } else {
+ this[propName] = nativeEvent[propName];
+ }
+ }
+ }
+
+ var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
+ if (defaultPrevented) {
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ } else {
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ }
+ this.isPropagationStopped = functionThatReturnsFalse;
+ return this;
+}
+
+_assign(SyntheticEvent.prototype, {
+ preventDefault: function () {
+ this.defaultPrevented = true;
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else if (typeof event.returnValue !== 'unknown') {
+ event.returnValue = false;
+ }
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ },
+
+ stopPropagation: function () {
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.stopPropagation) {
+ event.stopPropagation();
+ } else if (typeof event.cancelBubble !== 'unknown') {
+ // The ChangeEventPlugin registers a "propertychange" event for
+ // IE. This event does not support bubbling or cancelling, and
+ // any references to cancelBubble throw "Member not found". A
+ // typeof check of "unknown" circumvents this issue (and is also
+ // IE specific).
+ event.cancelBubble = true;
+ }
+
+ this.isPropagationStopped = functionThatReturnsTrue;
+ },
+
+ /**
+ * We release all dispatched `SyntheticEvent`s after each event loop, adding
+ * them back into the pool. This allows a way to hold onto a reference that
+ * won't be added back into the pool.
+ */
+ persist: function () {
+ this.isPersistent = functionThatReturnsTrue;
+ },
+
+ /**
+ * Checks if this event should be released back into the pool.
+ *
+ * @return {boolean} True if this should not be released, false otherwise.
+ */
+ isPersistent: functionThatReturnsFalse,
+
+ /**
+ * `PooledClass` looks for `destructor` on each instance it releases.
+ */
+ destructor: function () {
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ {
+ this[propName] = null;
+ }
+ }
+ this.dispatchConfig = null;
+ this._targetInst = null;
+ this.nativeEvent = null;
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ this.isPropagationStopped = functionThatReturnsFalse;
+ this._dispatchListeners = null;
+ this._dispatchInstances = null;
+
+ }
+});
+
+SyntheticEvent.Interface = EventInterface;
+
+/**
+ * Helper to reduce boilerplate when creating subclasses.
+ */
+SyntheticEvent.extend = function (Interface) {
+ var Super = this;
+
+ var E = function () {};
+ E.prototype = Super.prototype;
+ var prototype = new E();
+
+ function Class() {
+ return Super.apply(this, arguments);
+ }
+ _assign(prototype, Class.prototype);
+ Class.prototype = prototype;
+ Class.prototype.constructor = Class;
+
+ Class.Interface = _assign({}, Super.Interface, Interface);
+ Class.extend = Super.extend;
+ addEventPoolingTo(Class);
+
+ return Class;
+};
+
+addEventPoolingTo(SyntheticEvent);
+
+function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
+ var EventConstructor = this;
+ if (EventConstructor.eventPool.length) {
+ var instance = EventConstructor.eventPool.pop();
+ EventConstructor.call(instance, dispatchConfig, targetInst, nativeEvent, nativeInst);
+ return instance;
+ }
+ return new EventConstructor(dispatchConfig, targetInst, nativeEvent, nativeInst);
+}
+
+function releasePooledEvent(event) {
+ var EventConstructor = this;
+ !(event instanceof EventConstructor) ? reactProdInvariant('279') : void 0;
+ event.destructor();
+ if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
+ EventConstructor.eventPool.push(event);
+ }
+}
+
+function addEventPoolingTo(EventConstructor) {
+ EventConstructor.eventPool = [];
+ EventConstructor.getPooled = getPooledEvent;
+ EventConstructor.release = releasePooledEvent;
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+/**
+ * HTML nodeType values that represent the type of the node
+ */
+
+var ELEMENT_NODE = 1;
+
+// Do not uses the below two methods directly!
+// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
+// (It is the only module that is allowed to access these methods.)
+
+function unsafeCastStringToDOMTopLevelType(topLevelType) {
+ return topLevelType;
+}
+
+var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
+
+/**
+ * Generate a mapping of standard vendor prefixes using the defined style property and event name.
+ *
+ * @param {string} styleProp
+ * @param {string} eventName
+ * @returns {object}
+ */
+function makePrefixMap(styleProp, eventName) {
+ var prefixes = {};
+
+ prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
+ prefixes['Webkit' + styleProp] = 'webkit' + eventName;
+ prefixes['Moz' + styleProp] = 'moz' + eventName;
+
+ return prefixes;
+}
+
+/**
+ * A list of event names to a configurable list of vendor prefixes.
+ */
+var vendorPrefixes = {
+ animationend: makePrefixMap('Animation', 'AnimationEnd'),
+ animationiteration: makePrefixMap('Animation', 'AnimationIteration'),
+ animationstart: makePrefixMap('Animation', 'AnimationStart'),
+ transitionend: makePrefixMap('Transition', 'TransitionEnd')
+};
+
+/**
+ * Event names that have already been detected and prefixed (if applicable).
+ */
+var prefixedEventNames = {};
+
+/**
+ * Element to check for prefixes on.
+ */
+var style = {};
+
+/**
+ * Bootstrap if a DOM exists.
+ */
+if (canUseDOM) {
+ style = document.createElementNS('http://www.w3.org/1999/xhtml', 'div').style;
+
+ // On some platforms, in particular some releases of Android 4.x,
+ // the un-prefixed "animation" and "transition" properties are defined on the
+ // style object but the events that fire will still be prefixed, so we need
+ // to check if the un-prefixed events are usable, and if not remove them from the map.
+ if (!('AnimationEvent' in window)) {
+ delete vendorPrefixes.animationend.animation;
+ delete vendorPrefixes.animationiteration.animation;
+ delete vendorPrefixes.animationstart.animation;
+ }
+
+ // Same as above
+ if (!('TransitionEvent' in window)) {
+ delete vendorPrefixes.transitionend.transition;
+ }
+}
+
+/**
+ * Attempts to determine the correct vendor prefixed event name.
+ *
+ * @param {string} eventName
+ * @returns {string}
+ */
+function getVendorPrefixedEventName(eventName) {
+ if (prefixedEventNames[eventName]) {
+ return prefixedEventNames[eventName];
+ } else if (!vendorPrefixes[eventName]) {
+ return eventName;
+ }
+
+ var prefixMap = vendorPrefixes[eventName];
+
+ for (var styleProp in prefixMap) {
+ if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
+ return prefixedEventNames[eventName] = prefixMap[styleProp];
+ }
+ }
+
+ return eventName;
+}
+
+/**
+ * To identify top level events in ReactDOM, we use constants defined by this
+ * module. This is the only module that uses the unsafe* methods to express
+ * that the constants actually correspond to the browser event names. This lets
+ * us save some bundle size by avoiding a top level type -> event name map.
+ * The rest of ReactDOM code should import top level types from this file.
+ */
+var TOP_ABORT = unsafeCastStringToDOMTopLevelType('abort');
+var TOP_ANIMATION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationend'));
+var TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationiteration'));
+var TOP_ANIMATION_START = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationstart'));
+var TOP_BLUR = unsafeCastStringToDOMTopLevelType('blur');
+var TOP_CAN_PLAY = unsafeCastStringToDOMTopLevelType('canplay');
+var TOP_CAN_PLAY_THROUGH = unsafeCastStringToDOMTopLevelType('canplaythrough');
+var TOP_CANCEL = unsafeCastStringToDOMTopLevelType('cancel');
+var TOP_CHANGE = unsafeCastStringToDOMTopLevelType('change');
+var TOP_CLICK = unsafeCastStringToDOMTopLevelType('click');
+var TOP_CLOSE = unsafeCastStringToDOMTopLevelType('close');
+var TOP_COMPOSITION_END = unsafeCastStringToDOMTopLevelType('compositionend');
+var TOP_COMPOSITION_START = unsafeCastStringToDOMTopLevelType('compositionstart');
+var TOP_COMPOSITION_UPDATE = unsafeCastStringToDOMTopLevelType('compositionupdate');
+var TOP_CONTEXT_MENU = unsafeCastStringToDOMTopLevelType('contextmenu');
+var TOP_COPY = unsafeCastStringToDOMTopLevelType('copy');
+var TOP_CUT = unsafeCastStringToDOMTopLevelType('cut');
+var TOP_DOUBLE_CLICK = unsafeCastStringToDOMTopLevelType('dblclick');
+
+var TOP_DRAG = unsafeCastStringToDOMTopLevelType('drag');
+var TOP_DRAG_END = unsafeCastStringToDOMTopLevelType('dragend');
+var TOP_DRAG_ENTER = unsafeCastStringToDOMTopLevelType('dragenter');
+var TOP_DRAG_EXIT = unsafeCastStringToDOMTopLevelType('dragexit');
+var TOP_DRAG_LEAVE = unsafeCastStringToDOMTopLevelType('dragleave');
+var TOP_DRAG_OVER = unsafeCastStringToDOMTopLevelType('dragover');
+var TOP_DRAG_START = unsafeCastStringToDOMTopLevelType('dragstart');
+var TOP_DROP = unsafeCastStringToDOMTopLevelType('drop');
+var TOP_DURATION_CHANGE = unsafeCastStringToDOMTopLevelType('durationchange');
+var TOP_EMPTIED = unsafeCastStringToDOMTopLevelType('emptied');
+var TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
+var TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
+var TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
+var TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
+
+var TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
+
+var TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
+var TOP_KEY_PRESS = unsafeCastStringToDOMTopLevelType('keypress');
+var TOP_KEY_UP = unsafeCastStringToDOMTopLevelType('keyup');
+var TOP_LOAD = unsafeCastStringToDOMTopLevelType('load');
+var TOP_LOAD_START = unsafeCastStringToDOMTopLevelType('loadstart');
+var TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
+var TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType('loadedmetadata');
+
+var TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
+var TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
+var TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
+var TOP_MOUSE_OVER = unsafeCastStringToDOMTopLevelType('mouseover');
+var TOP_MOUSE_UP = unsafeCastStringToDOMTopLevelType('mouseup');
+var TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
+var TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
+var TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
+var TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
+
+
+
+
+
+
+
+
+var TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
+var TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
+
+var TOP_SCROLL = unsafeCastStringToDOMTopLevelType('scroll');
+var TOP_SEEKED = unsafeCastStringToDOMTopLevelType('seeked');
+var TOP_SEEKING = unsafeCastStringToDOMTopLevelType('seeking');
+var TOP_SELECTION_CHANGE = unsafeCastStringToDOMTopLevelType('selectionchange');
+var TOP_STALLED = unsafeCastStringToDOMTopLevelType('stalled');
+
+var TOP_SUSPEND = unsafeCastStringToDOMTopLevelType('suspend');
+var TOP_TEXT_INPUT = unsafeCastStringToDOMTopLevelType('textInput');
+var TOP_TIME_UPDATE = unsafeCastStringToDOMTopLevelType('timeupdate');
+var TOP_TOGGLE = unsafeCastStringToDOMTopLevelType('toggle');
+var TOP_TOUCH_CANCEL = unsafeCastStringToDOMTopLevelType('touchcancel');
+var TOP_TOUCH_END = unsafeCastStringToDOMTopLevelType('touchend');
+var TOP_TOUCH_MOVE = unsafeCastStringToDOMTopLevelType('touchmove');
+var TOP_TOUCH_START = unsafeCastStringToDOMTopLevelType('touchstart');
+var TOP_TRANSITION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('transitionend'));
+var TOP_VOLUME_CHANGE = unsafeCastStringToDOMTopLevelType('volumechange');
+var TOP_WAITING = unsafeCastStringToDOMTopLevelType('waiting');
+var TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');
+
+// List of events that need to be individually attached to media elements.
+// Note that events in this list will *not* be listened to at the top level
+// unless they're explicitly whitelisted in `ReactBrowserEventEmitter.listenTo`.
+
+// for .act's return value
+var findDOMNode = ReactDOM.findDOMNode;
+// Keep in sync with ReactDOMUnstableNativeDependencies.js
+// and ReactDOM.js:
+
+var _ReactDOM$__SECRET_IN = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.Events;
+var getInstanceFromNode = _ReactDOM$__SECRET_IN[0];
+var getNodeFromInstance = _ReactDOM$__SECRET_IN[1];
+var getFiberCurrentPropsFromNode = _ReactDOM$__SECRET_IN[2];
+var injectEventPluginsByName = _ReactDOM$__SECRET_IN[3];
+var eventNameDispatchConfigs = _ReactDOM$__SECRET_IN[4];
+var accumulateTwoPhaseDispatches = _ReactDOM$__SECRET_IN[5];
+var accumulateDirectDispatches = _ReactDOM$__SECRET_IN[6];
+var enqueueStateRestore = _ReactDOM$__SECRET_IN[7];
+var restoreStateIfNeeded = _ReactDOM$__SECRET_IN[8];
+var dispatchEvent = _ReactDOM$__SECRET_IN[9];
+var runEventsInBatch = _ReactDOM$__SECRET_IN[10];
+
+
+function Event(suffix) {}
+
+/**
+ * @class ReactTestUtils
+ */
+
+/**
+ * Simulates a top level event being dispatched from a raw event that occurred
+ * on an `Element` node.
+ * @param {number} topLevelType A number from `TopLevelEventTypes`
+ * @param {!Element} node The dom to simulate an event occurring on.
+ * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
+ */
+function simulateNativeEventOnNode(topLevelType, node, fakeNativeEvent) {
+ fakeNativeEvent.target = node;
+ dispatchEvent(topLevelType, fakeNativeEvent);
+}
+
+/**
+ * Simulates a top level event being dispatched from a raw event that occurred
+ * on the `ReactDOMComponent` `comp`.
+ * @param {Object} topLevelType A type from `BrowserEventConstants.topLevelTypes`.
+ * @param {!ReactDOMComponent} comp
+ * @param {?Event} fakeNativeEvent Fake native event to use in SyntheticEvent.
+ */
+function simulateNativeEventOnDOMComponent(topLevelType, comp, fakeNativeEvent) {
+ simulateNativeEventOnNode(topLevelType, findDOMNode(comp), fakeNativeEvent);
+}
+
+function findAllInRenderedFiberTreeInternal(fiber, test) {
+ if (!fiber) {
+ return [];
+ }
+ var currentParent = findCurrentFiberUsingSlowPath(fiber);
+ if (!currentParent) {
+ return [];
+ }
+ var node = currentParent;
+ var ret = [];
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText || node.tag === ClassComponent || node.tag === FunctionComponent) {
+ var publicInst = node.stateNode;
+ if (test(publicInst)) {
+ ret.push(publicInst);
+ }
+ }
+ if (node.child) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === currentParent) {
+ return ret;
+ }
+ while (!node.sibling) {
+ if (!node.return || node.return === currentParent) {
+ return ret;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function validateClassInstance(inst, methodName) {
+ if (!inst) {
+ // This is probably too relaxed but it's existing behavior.
+ return;
+ }
+ if (get(inst)) {
+ // This is a public instance indeed.
+ return;
+ }
+ var received = void 0;
+ var stringified = '' + inst;
+ if (Array.isArray(inst)) {
+ received = 'an array';
+ } else if (inst && inst.nodeType === ELEMENT_NODE && inst.tagName) {
+ received = 'a DOM node';
+ } else if (stringified === '[object Object]') {
+ received = 'object with keys {' + Object.keys(inst).join(', ') + '}';
+ } else {
+ received = stringified;
+ }
+ reactProdInvariant('286', methodName, received);
+}
+
+// a stub element, lazily initialized, used by act() when flushing effects
+var actContainerElement = null;
+
+/**
+ * Utilities for making it easy to test React components.
+ *
+ * See https://reactjs.org/docs/test-utils.html
+ *
+ * Todo: Support the entire DOM.scry query syntax. For now, these simple
+ * utilities will suffice for testing purposes.
+ * @lends ReactTestUtils
+ */
+var ReactTestUtils = {
+ renderIntoDocument: function (element) {
+ var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ // None of our tests actually require attaching the container to the
+ // DOM, and doing so creates a mess that we rely on test isolation to
+ // clean up, so we're going to stop honoring the name of this method
+ // (and probably rename it eventually) if no problems arise.
+ // document.documentElement.appendChild(div);
+ return ReactDOM.render(element, div);
+ },
+
+ isElement: function (element) {
+ return React.isValidElement(element);
+ },
+
+ isElementOfType: function (inst, convenienceConstructor) {
+ return React.isValidElement(inst) && inst.type === convenienceConstructor;
+ },
+
+ isDOMComponent: function (inst) {
+ return !!(inst && inst.nodeType === ELEMENT_NODE && inst.tagName);
+ },
+
+ isDOMComponentElement: function (inst) {
+ return !!(inst && React.isValidElement(inst) && !!inst.tagName);
+ },
+
+ isCompositeComponent: function (inst) {
+ if (ReactTestUtils.isDOMComponent(inst)) {
+ // Accessing inst.setState warns; just return false as that'll be what
+ // this returns when we have DOM nodes as refs directly
+ return false;
+ }
+ return inst != null && typeof inst.render === 'function' && typeof inst.setState === 'function';
+ },
+
+ isCompositeComponentWithType: function (inst, type) {
+ if (!ReactTestUtils.isCompositeComponent(inst)) {
+ return false;
+ }
+ var internalInstance = get(inst);
+ var constructor = internalInstance.type;
+ return constructor === type;
+ },
+
+ findAllInRenderedTree: function (inst, test) {
+ validateClassInstance(inst, 'findAllInRenderedTree');
+ if (!inst) {
+ return [];
+ }
+ var internalInstance = get(inst);
+ return findAllInRenderedFiberTreeInternal(internalInstance, test);
+ },
+
+ /**
+ * Finds all instance of components in the rendered tree that are DOM
+ * components with the class name matching `className`.
+ * @return {array} an array of all the matches.
+ */
+ scryRenderedDOMComponentsWithClass: function (root, classNames) {
+ validateClassInstance(root, 'scryRenderedDOMComponentsWithClass');
+ return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
+ if (ReactTestUtils.isDOMComponent(inst)) {
+ var className = inst.className;
+ if (typeof className !== 'string') {
+ // SVG, probably.
+ className = inst.getAttribute('class') || '';
+ }
+ var classList = className.split(/\s+/);
+
+ if (!Array.isArray(classNames)) {
+ !(classNames !== undefined) ? reactProdInvariant('11') : void 0;
+ classNames = classNames.split(/\s+/);
+ }
+ return classNames.every(function (name) {
+ return classList.indexOf(name) !== -1;
+ });
+ }
+ return false;
+ });
+ },
+
+ /**
+ * Like scryRenderedDOMComponentsWithClass but expects there to be one result,
+ * and returns that one result, or throws exception if there is any other
+ * number of matches besides one.
+ * @return {!ReactDOMComponent} The one match.
+ */
+ findRenderedDOMComponentWithClass: function (root, className) {
+ validateClassInstance(root, 'findRenderedDOMComponentWithClass');
+ var all = ReactTestUtils.scryRenderedDOMComponentsWithClass(root, className);
+ if (all.length !== 1) {
+ throw new Error('Did not find exactly one match (found: ' + all.length + ') ' + 'for class:' + className);
+ }
+ return all[0];
+ },
+
+ /**
+ * Finds all instance of components in the rendered tree that are DOM
+ * components with the tag name matching `tagName`.
+ * @return {array} an array of all the matches.
+ */
+ scryRenderedDOMComponentsWithTag: function (root, tagName) {
+ validateClassInstance(root, 'scryRenderedDOMComponentsWithTag');
+ return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
+ return ReactTestUtils.isDOMComponent(inst) && inst.tagName.toUpperCase() === tagName.toUpperCase();
+ });
+ },
+
+ /**
+ * Like scryRenderedDOMComponentsWithTag but expects there to be one result,
+ * and returns that one result, or throws exception if there is any other
+ * number of matches besides one.
+ * @return {!ReactDOMComponent} The one match.
+ */
+ findRenderedDOMComponentWithTag: function (root, tagName) {
+ validateClassInstance(root, 'findRenderedDOMComponentWithTag');
+ var all = ReactTestUtils.scryRenderedDOMComponentsWithTag(root, tagName);
+ if (all.length !== 1) {
+ throw new Error('Did not find exactly one match (found: ' + all.length + ') ' + 'for tag:' + tagName);
+ }
+ return all[0];
+ },
+
+ /**
+ * Finds all instances of components with type equal to `componentType`.
+ * @return {array} an array of all the matches.
+ */
+ scryRenderedComponentsWithType: function (root, componentType) {
+ validateClassInstance(root, 'scryRenderedComponentsWithType');
+ return ReactTestUtils.findAllInRenderedTree(root, function (inst) {
+ return ReactTestUtils.isCompositeComponentWithType(inst, componentType);
+ });
+ },
+
+ /**
+ * Same as `scryRenderedComponentsWithType` but expects there to be one result
+ * and returns that one result, or throws exception if there is any other
+ * number of matches besides one.
+ * @return {!ReactComponent} The one match.
+ */
+ findRenderedComponentWithType: function (root, componentType) {
+ validateClassInstance(root, 'findRenderedComponentWithType');
+ var all = ReactTestUtils.scryRenderedComponentsWithType(root, componentType);
+ if (all.length !== 1) {
+ throw new Error('Did not find exactly one match (found: ' + all.length + ') ' + 'for componentType:' + componentType);
+ }
+ return all[0];
+ },
+
+ /**
+ * Pass a mocked component module to this method to augment it with
+ * useful methods that allow it to be used as a dummy React component.
+ * Instead of rendering as usual, the component will become a simple
+ * <div> containing any provided children.
+ *
+ * @param {object} module the mock function object exported from a
+ * module that defines the component to be mocked
+ * @param {?string} mockTagName optional dummy root tag name to return
+ * from render method (overrides
+ * module.mockTagName if provided)
+ * @return {object} the ReactTestUtils object (for chaining)
+ */
+ mockComponent: function (module, mockTagName) {
+ mockTagName = mockTagName || module.mockTagName || 'div';
+
+ module.prototype.render.mockImplementation(function () {
+ return React.createElement(mockTagName, null, this.props.children);
+ });
+
+ return this;
+ },
+
+ nativeTouchData: function (x, y) {
+ return {
+ touches: [{ pageX: x, pageY: y }]
+ };
+ },
+
+ Simulate: null,
+ SimulateNative: {},
+
+ act: function (callback) {
+ if (actContainerElement === null) {
+ // warn if we can't actually create the stub element
+ actContainerElement = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ }
+
+ var result = ReactDOM.unstable_batchedUpdates(callback);
+ // note: keep these warning messages in sync with
+ // createReactNoop.js and ReactTestRenderer.js
+ ReactDOM.render(React.createElement('div', null), actContainerElement);
+ // we want the user to not expect a return,
+ // but we want to warn if they use it like they can await on it.
+ return {
+ then: function () {
+
+ }
+ };
+ }
+};
+
+/**
+ * Exports:
+ *
+ * - `ReactTestUtils.Simulate.click(Element)`
+ * - `ReactTestUtils.Simulate.mouseMove(Element)`
+ * - `ReactTestUtils.Simulate.change(Element)`
+ * - ... (All keys from event plugin `eventTypes` objects)
+ */
+function makeSimulator(eventType) {
+ return function (domNode, eventData) {
+ !!React.isValidElement(domNode) ? reactProdInvariant('228') : void 0;
+ !!ReactTestUtils.isCompositeComponent(domNode) ? reactProdInvariant('229') : void 0;
+
+ var dispatchConfig = eventNameDispatchConfigs[eventType];
+
+ var fakeNativeEvent = new Event();
+ fakeNativeEvent.target = domNode;
+ fakeNativeEvent.type = eventType.toLowerCase();
+
+ // We don't use SyntheticEvent.getPooled in order to not have to worry about
+ // properly destroying any properties assigned from `eventData` upon release
+ var targetInst = getInstanceFromNode(domNode);
+ var event = new SyntheticEvent(dispatchConfig, targetInst, fakeNativeEvent, domNode);
+
+ // Since we aren't using pooling, always persist the event. This will make
+ // sure it's marked and won't warn when setting additional properties.
+ event.persist();
+ _assign(event, eventData);
+
+ if (dispatchConfig.phasedRegistrationNames) {
+ accumulateTwoPhaseDispatches(event);
+ } else {
+ accumulateDirectDispatches(event);
+ }
+
+ ReactDOM.unstable_batchedUpdates(function () {
+ // Normally extractEvent enqueues a state restore, but we'll just always
+ // do that since we're by-passing it here.
+ enqueueStateRestore(domNode);
+ runEventsInBatch(event);
+ });
+ restoreStateIfNeeded();
+ };
+}
+
+function buildSimulators() {
+ ReactTestUtils.Simulate = {};
+
+ var eventType = void 0;
+ for (eventType in eventNameDispatchConfigs) {
+ /**
+ * @param {!Element|ReactDOMComponent} domComponentOrNode
+ * @param {?object} eventData Fake event data to use in SyntheticEvent.
+ */
+ ReactTestUtils.Simulate[eventType] = makeSimulator(eventType);
+ }
+}
+
+buildSimulators();
+
+/**
+ * Exports:
+ *
+ * - `ReactTestUtils.SimulateNative.click(Element/ReactDOMComponent)`
+ * - `ReactTestUtils.SimulateNative.mouseMove(Element/ReactDOMComponent)`
+ * - `ReactTestUtils.SimulateNative.mouseIn/ReactDOMComponent)`
+ * - `ReactTestUtils.SimulateNative.mouseOut(Element/ReactDOMComponent)`
+ * - ... (All keys from `BrowserEventConstants.topLevelTypes`)
+ *
+ * Note: Top level event types are a subset of the entire set of handler types
+ * (which include a broader set of "synthetic" events). For example, onDragDone
+ * is a synthetic event. Except when testing an event plugin or React's event
+ * handling code specifically, you probably want to use ReactTestUtils.Simulate
+ * to dispatch synthetic events.
+ */
+
+function makeNativeSimulator(eventType, topLevelType) {
+ return function (domComponentOrNode, nativeEventData) {
+ var fakeNativeEvent = new Event(eventType);
+ _assign(fakeNativeEvent, nativeEventData);
+ if (ReactTestUtils.isDOMComponent(domComponentOrNode)) {
+ simulateNativeEventOnDOMComponent(topLevelType, domComponentOrNode, fakeNativeEvent);
+ } else if (domComponentOrNode.tagName) {
+ // Will allow on actual dom nodes.
+ simulateNativeEventOnNode(topLevelType, domComponentOrNode, fakeNativeEvent);
+ }
+ };
+}
+
+[[TOP_ABORT, 'abort'], [TOP_ANIMATION_END, 'animationEnd'], [TOP_ANIMATION_ITERATION, 'animationIteration'], [TOP_ANIMATION_START, 'animationStart'], [TOP_BLUR, 'blur'], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough'], [TOP_CAN_PLAY, 'canPlay'], [TOP_CANCEL, 'cancel'], [TOP_CHANGE, 'change'], [TOP_CLICK, 'click'], [TOP_CLOSE, 'close'], [TOP_COMPOSITION_END, 'compositionEnd'], [TOP_COMPOSITION_START, 'compositionStart'], [TOP_COMPOSITION_UPDATE, 'compositionUpdate'], [TOP_CONTEXT_MENU, 'contextMenu'], [TOP_COPY, 'copy'], [TOP_CUT, 'cut'], [TOP_DOUBLE_CLICK, 'doubleClick'], [TOP_DRAG_END, 'dragEnd'], [TOP_DRAG_ENTER, 'dragEnter'], [TOP_DRAG_EXIT, 'dragExit'], [TOP_DRAG_LEAVE, 'dragLeave'], [TOP_DRAG_OVER, 'dragOver'], [TOP_DRAG_START, 'dragStart'], [TOP_DRAG, 'drag'], [TOP_DROP, 'drop'], [TOP_DURATION_CHANGE, 'durationChange'], [TOP_EMPTIED, 'emptied'], [TOP_ENCRYPTED, 'encrypted'], [TOP_ENDED, 'ended'], [TOP_ERROR, 'error'], [TOP_FOCUS, 'focus'], [TOP_INPUT, 'input'], [TOP_KEY_DOWN, 'keyDown'], [TOP_KEY_PRESS, 'keyPress'], [TOP_KEY_UP, 'keyUp'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD_START, 'loadStart'], [TOP_LOAD, 'load'], [TOP_LOADED_DATA, 'loadedData'], [TOP_LOADED_METADATA, 'loadedMetadata'], [TOP_MOUSE_DOWN, 'mouseDown'], [TOP_MOUSE_MOVE, 'mouseMove'], [TOP_MOUSE_OUT, 'mouseOut'], [TOP_MOUSE_OVER, 'mouseOver'], [TOP_MOUSE_UP, 'mouseUp'], [TOP_PASTE, 'paste'], [TOP_PAUSE, 'pause'], [TOP_PLAY, 'play'], [TOP_PLAYING, 'playing'], [TOP_PROGRESS, 'progress'], [TOP_RATE_CHANGE, 'rateChange'], [TOP_SCROLL, 'scroll'], [TOP_SEEKED, 'seeked'], [TOP_SEEKING, 'seeking'], [TOP_SELECTION_CHANGE, 'selectionChange'], [TOP_STALLED, 'stalled'], [TOP_SUSPEND, 'suspend'], [TOP_TEXT_INPUT, 'textInput'], [TOP_TIME_UPDATE, 'timeUpdate'], [TOP_TOGGLE, 'toggle'], [TOP_TOUCH_CANCEL, 'touchCancel'], [TOP_TOUCH_END, 'touchEnd'], [TOP_TOUCH_MOVE, 'touchMove'], [TOP_TOUCH_START, 'touchStart'], [TOP_TRANSITION_END, 'transitionEnd'], [TOP_VOLUME_CHANGE, 'volumeChange'], [TOP_WAITING, 'waiting'], [TOP_WHEEL, 'wheel']].forEach(function (_ref) {
+ var topLevelType = _ref[0],
+ eventType = _ref[1];
+
+ /**
+ * @param {!Element|ReactDOMComponent} domComponentOrNode
+ * @param {?Event} nativeEventData Fake native event to use in SyntheticEvent.
+ */
+ ReactTestUtils.SimulateNative[eventType] = makeNativeSimulator(eventType, topLevelType);
+});
+
+
+
+var ReactTestUtils$2 = ({
+ default: ReactTestUtils
+});
+
+var ReactTestUtils$3 = ( ReactTestUtils$2 && ReactTestUtils ) || ReactTestUtils$2;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var testUtils = ReactTestUtils$3.default || ReactTestUtils$3;
+
+return testUtils;
+
+})));
diff --git a/devtools/client/shared/vendor/react-dom.js b/devtools/client/shared/vendor/react-dom.js
new file mode 100644
index 0000000000..ca46bb9d75
--- /dev/null
+++ b/devtools/client/shared/vendor/react-dom.js
@@ -0,0 +1,16370 @@
+/** @license React v16.6.1
+ * react-dom.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('devtools/client/shared/vendor/react')) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react'], factory) :
+ (global.ReactDOM = factory(global.React));
+}(this, (function (React) { 'use strict';
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+/**
+ * WARNING: DO NOT manually require this module.
+ * This is a replacement for `invariant(...)` used by the error code system
+ * and will _only_ be required by the corresponding babel pass.
+ * It always throws.
+ */
+function reactProdInvariant(code) {
+ var argCount = arguments.length - 1;
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
+ for (var argIdx = 0; argIdx < argCount; argIdx++) {
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
+ }
+ // Rename it so that our build transform doesn't attempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
+}
+
+!React ? reactProdInvariant('227') : void 0;
+
+var invokeGuardedCallbackImpl = function (name, func, context, a, b, c, d, e, f) {
+ var funcArgs = Array.prototype.slice.call(arguments, 3);
+ try {
+ func.apply(context, funcArgs);
+ } catch (error) {
+ this.onError(error);
+ }
+};
+
+// Used by Fiber to simulate a try-catch.
+var hasError = false;
+var caughtError = null;
+
+// Used by event system to capture/rethrow the first error.
+var hasRethrowError = false;
+var rethrowError = null;
+
+var reporter = {
+ onError: function (error) {
+ hasError = true;
+ caughtError = error;
+ }
+};
+
+/**
+ * Call a function while guarding against errors that happens within it.
+ * Returns an error if it throws, otherwise null.
+ *
+ * In production, this is implemented using a try-catch. The reason we don't
+ * use a try-catch directly is so that we can swap out a different
+ * implementation in DEV mode.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
+ hasError = false;
+ caughtError = null;
+ invokeGuardedCallbackImpl.apply(reporter, arguments);
+}
+
+/**
+ * Same as invokeGuardedCallback, but instead of returning an error, it stores
+ * it in a global so it can be rethrown by `rethrowCaughtError` later.
+ * TODO: See if caughtError and rethrowError can be unified.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+function invokeGuardedCallbackAndCatchFirstError(name, func, context, a, b, c, d, e, f) {
+ invokeGuardedCallback.apply(this, arguments);
+ if (hasError) {
+ var error = clearCaughtError();
+ if (!hasRethrowError) {
+ hasRethrowError = true;
+ rethrowError = error;
+ }
+ }
+}
+
+/**
+ * During execution of guarded functions we will capture the first error which
+ * we will rethrow to be handled by the top level error handler.
+ */
+function rethrowCaughtError() {
+ if (hasRethrowError) {
+ var error = rethrowError;
+ hasRethrowError = false;
+ rethrowError = null;
+ throw error;
+ }
+}
+
+
+
+function clearCaughtError() {
+ if (hasError) {
+ var error = caughtError;
+ hasError = false;
+ caughtError = null;
+ return error;
+ } else {
+ reactProdInvariant('198');
+ }
+}
+
+/**
+ * Injectable ordering of event plugins.
+ */
+var eventPluginOrder = null;
+
+/**
+ * Injectable mapping from names to event plugin modules.
+ */
+var namesToPlugins = {};
+
+/**
+ * Recomputes the plugin list using the injected plugins and plugin ordering.
+ *
+ * @private
+ */
+function recomputePluginOrdering() {
+ if (!eventPluginOrder) {
+ // Wait until an `eventPluginOrder` is injected.
+ return;
+ }
+ for (var pluginName in namesToPlugins) {
+ var pluginModule = namesToPlugins[pluginName];
+ var pluginIndex = eventPluginOrder.indexOf(pluginName);
+ !(pluginIndex > -1) ? reactProdInvariant('96', pluginName) : void 0;
+ if (plugins[pluginIndex]) {
+ continue;
+ }
+ !pluginModule.extractEvents ? reactProdInvariant('97', pluginName) : void 0;
+ plugins[pluginIndex] = pluginModule;
+ var publishedEvents = pluginModule.eventTypes;
+ for (var eventName in publishedEvents) {
+ !publishEventForPlugin(publishedEvents[eventName], pluginModule, eventName) ? reactProdInvariant('98', eventName, pluginName) : void 0;
+ }
+ }
+}
+
+/**
+ * Publishes an event so that it can be dispatched by the supplied plugin.
+ *
+ * @param {object} dispatchConfig Dispatch configuration for the event.
+ * @param {object} PluginModule Plugin publishing the event.
+ * @return {boolean} True if the event was successfully published.
+ * @private
+ */
+function publishEventForPlugin(dispatchConfig, pluginModule, eventName) {
+ !!eventNameDispatchConfigs.hasOwnProperty(eventName) ? reactProdInvariant('99', eventName) : void 0;
+ eventNameDispatchConfigs[eventName] = dispatchConfig;
+
+ var phasedRegistrationNames = dispatchConfig.phasedRegistrationNames;
+ if (phasedRegistrationNames) {
+ for (var phaseName in phasedRegistrationNames) {
+ if (phasedRegistrationNames.hasOwnProperty(phaseName)) {
+ var phasedRegistrationName = phasedRegistrationNames[phaseName];
+ publishRegistrationName(phasedRegistrationName, pluginModule, eventName);
+ }
+ }
+ return true;
+ } else if (dispatchConfig.registrationName) {
+ publishRegistrationName(dispatchConfig.registrationName, pluginModule, eventName);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Publishes a registration name that is used to identify dispatched events.
+ *
+ * @param {string} registrationName Registration name to add.
+ * @param {object} PluginModule Plugin publishing the event.
+ * @private
+ */
+function publishRegistrationName(registrationName, pluginModule, eventName) {
+ !!registrationNameModules[registrationName] ? reactProdInvariant('100', registrationName) : void 0;
+ registrationNameModules[registrationName] = pluginModule;
+ registrationNameDependencies[registrationName] = pluginModule.eventTypes[eventName].dependencies;
+
+
+}
+
+/**
+ * Registers plugins so that they can extract and dispatch events.
+ *
+ * @see {EventPluginHub}
+ */
+
+/**
+ * Ordered list of injected plugins.
+ */
+var plugins = [];
+
+/**
+ * Mapping from event name to dispatch config
+ */
+var eventNameDispatchConfigs = {};
+
+/**
+ * Mapping from registration name to plugin module
+ */
+var registrationNameModules = {};
+
+/**
+ * Mapping from registration name to event name
+ */
+var registrationNameDependencies = {};
+
+/**
+ * Mapping from lowercase registration names to the properly cased version,
+ * used to warn in the case of missing event handlers. Available
+ * only in false.
+ * @type {Object}
+ */
+
+// Trust the developer to only use possibleRegistrationNames in false
+
+/**
+ * Injects an ordering of plugins (by plugin name). This allows the ordering
+ * to be decoupled from injection of the actual plugins so that ordering is
+ * always deterministic regardless of packaging, on-the-fly injection, etc.
+ *
+ * @param {array} InjectedEventPluginOrder
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginOrder}
+ */
+function injectEventPluginOrder(injectedEventPluginOrder) {
+ !!eventPluginOrder ? reactProdInvariant('101') : void 0;
+ // Clone the ordering so it cannot be dynamically mutated.
+ eventPluginOrder = Array.prototype.slice.call(injectedEventPluginOrder);
+ recomputePluginOrdering();
+}
+
+/**
+ * Injects plugins to be used by `EventPluginHub`. The plugin names must be
+ * in the ordering injected by `injectEventPluginOrder`.
+ *
+ * Plugins can be injected as part of page initialization or on-the-fly.
+ *
+ * @param {object} injectedNamesToPlugins Map from names to plugin modules.
+ * @internal
+ * @see {EventPluginHub.injection.injectEventPluginsByName}
+ */
+function injectEventPluginsByName(injectedNamesToPlugins) {
+ var isOrderingDirty = false;
+ for (var pluginName in injectedNamesToPlugins) {
+ if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
+ continue;
+ }
+ var pluginModule = injectedNamesToPlugins[pluginName];
+ if (!namesToPlugins.hasOwnProperty(pluginName) || namesToPlugins[pluginName] !== pluginModule) {
+ !!namesToPlugins[pluginName] ? reactProdInvariant('102', pluginName) : void 0;
+ namesToPlugins[pluginName] = pluginModule;
+ isOrderingDirty = true;
+ }
+ }
+ if (isOrderingDirty) {
+ recomputePluginOrdering();
+ }
+}
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var getFiberCurrentPropsFromNode = null;
+var getInstanceFromNode = null;
+var getNodeFromInstance = null;
+
+function setComponentTree(getFiberCurrentPropsFromNodeImpl, getInstanceFromNodeImpl, getNodeFromInstanceImpl) {
+ getFiberCurrentPropsFromNode = getFiberCurrentPropsFromNodeImpl;
+ getInstanceFromNode = getInstanceFromNodeImpl;
+ getNodeFromInstance = getNodeFromInstanceImpl;
+
+}
+
+/**
+ * Dispatch the event to the listener.
+ * @param {SyntheticEvent} event SyntheticEvent to handle
+ * @param {function} listener Application-level callback
+ * @param {*} inst Internal component instance
+ */
+function executeDispatch(event, listener, inst) {
+ var type = event.type || 'unknown-event';
+ event.currentTarget = getNodeFromInstance(inst);
+ invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
+ event.currentTarget = null;
+}
+
+/**
+ * Standard/simple iteration through an event's collected dispatches.
+ */
+function executeDispatchesInOrder(event) {
+ var dispatchListeners = event._dispatchListeners;
+ var dispatchInstances = event._dispatchInstances;
+ if (Array.isArray(dispatchListeners)) {
+ for (var i = 0; i < dispatchListeners.length; i++) {
+ if (event.isPropagationStopped()) {
+ break;
+ }
+ // Listeners and Instances are two parallel arrays that are always in sync.
+ executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
+ }
+ } else if (dispatchListeners) {
+ executeDispatch(event, dispatchListeners, dispatchInstances);
+ }
+ event._dispatchListeners = null;
+ event._dispatchInstances = null;
+}
+
+/**
+ * @see executeDispatchesInOrderStopAtTrueImpl
+ */
+
+
+/**
+ * Execution of a "direct" dispatch - there must be at most one dispatch
+ * accumulated on the event or it is considered an error. It doesn't really make
+ * sense for an event with multiple dispatches (bubbled) to keep track of the
+ * return values at each dispatch execution, but it does tend to make sense when
+ * dealing with "direct" dispatches.
+ *
+ * @return {*} The return value of executing the single dispatch.
+ */
+
+
+/**
+ * @param {SyntheticEvent} event
+ * @return {boolean} True iff number of dispatches accumulated is greater than 0.
+ */
+
+/**
+ * Accumulates items that must not be null or undefined into the first one. This
+ * is used to conserve memory by avoiding array allocations, and thus sacrifices
+ * API cleanness. Since `current` can be null before being passed in and not
+ * null after this function, make sure to assign it back to `current`:
+ *
+ * `a = accumulateInto(a, b);`
+ *
+ * This API should be sparingly used. Try `accumulate` for something cleaner.
+ *
+ * @return {*|array<*>} An accumulation of items.
+ */
+
+function accumulateInto(current, next) {
+ !(next != null) ? reactProdInvariant('30') : void 0;
+
+ if (current == null) {
+ return next;
+ }
+
+ // Both are not empty. Warning: Never call x.concat(y) when you are not
+ // certain that x is an Array (x could be a string with concat method).
+ if (Array.isArray(current)) {
+ if (Array.isArray(next)) {
+ current.push.apply(current, next);
+ return current;
+ }
+ current.push(next);
+ return current;
+ }
+
+ if (Array.isArray(next)) {
+ // A bit too dangerous to mutate `next`.
+ return [current].concat(next);
+ }
+
+ return [current, next];
+}
+
+/**
+ * @param {array} arr an "accumulation" of items which is either an Array or
+ * a single item. Useful when paired with the `accumulate` module. This is a
+ * simple utility that allows us to reason about a collection of items, but
+ * handling the case when there is exactly one item (and we do not need to
+ * allocate an array).
+ * @param {function} cb Callback invoked with each element or a collection.
+ * @param {?} [scope] Scope used as `this` in a callback.
+ */
+function forEachAccumulated(arr, cb, scope) {
+ if (Array.isArray(arr)) {
+ arr.forEach(cb, scope);
+ } else if (arr) {
+ cb.call(scope, arr);
+ }
+}
+
+/**
+ * Internal queue of events that have accumulated their dispatches and are
+ * waiting to have their dispatches executed.
+ */
+var eventQueue = null;
+
+/**
+ * Dispatches an event and releases it back into the pool, unless persistent.
+ *
+ * @param {?object} event Synthetic event to be dispatched.
+ * @private
+ */
+var executeDispatchesAndRelease = function (event) {
+ if (event) {
+ executeDispatchesInOrder(event);
+
+ if (!event.isPersistent()) {
+ event.constructor.release(event);
+ }
+ }
+};
+var executeDispatchesAndReleaseTopLevel = function (e) {
+ return executeDispatchesAndRelease(e);
+};
+
+function isInteractive(tag) {
+ return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';
+}
+
+function shouldPreventMouseEvent(name, type, props) {
+ switch (name) {
+ case 'onClick':
+ case 'onClickCapture':
+ case 'onDoubleClick':
+ case 'onDoubleClickCapture':
+ case 'onMouseDown':
+ case 'onMouseDownCapture':
+ case 'onMouseMove':
+ case 'onMouseMoveCapture':
+ case 'onMouseUp':
+ case 'onMouseUpCapture':
+ return !!(props.disabled && isInteractive(type));
+ default:
+ return false;
+ }
+}
+
+/**
+ * This is a unified interface for event plugins to be installed and configured.
+ *
+ * Event plugins can implement the following properties:
+ *
+ * `extractEvents` {function(string, DOMEventTarget, string, object): *}
+ * Required. When a top-level event is fired, this method is expected to
+ * extract synthetic events that will in turn be queued and dispatched.
+ *
+ * `eventTypes` {object}
+ * Optional, plugins that fire events must publish a mapping of registration
+ * names that are used to register listeners. Values of this mapping must
+ * be objects that contain `registrationName` or `phasedRegistrationNames`.
+ *
+ * `executeDispatch` {function(object, function, string)}
+ * Optional, allows plugins to override how an event gets dispatched. By
+ * default, the listener is simply invoked.
+ *
+ * Each plugin that is injected into `EventsPluginHub` is immediately operable.
+ *
+ * @public
+ */
+
+/**
+ * Methods for injecting dependencies.
+ */
+var injection = {
+ /**
+ * @param {array} InjectedEventPluginOrder
+ * @public
+ */
+ injectEventPluginOrder: injectEventPluginOrder,
+
+ /**
+ * @param {object} injectedNamesToPlugins Map from names to plugin modules.
+ */
+ injectEventPluginsByName: injectEventPluginsByName
+};
+
+/**
+ * @param {object} inst The instance, which is the source of events.
+ * @param {string} registrationName Name of listener (e.g. `onClick`).
+ * @return {?function} The stored callback.
+ */
+function getListener(inst, registrationName) {
+ var listener = void 0;
+
+ // TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
+ // live here; needs to be moved to a better place soon
+ var stateNode = inst.stateNode;
+ if (!stateNode) {
+ // Work in progress (ex: onload events in incremental mode).
+ return null;
+ }
+ var props = getFiberCurrentPropsFromNode(stateNode);
+ if (!props) {
+ // Work in progress.
+ return null;
+ }
+ listener = props[registrationName];
+ if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
+ return null;
+ }
+ !(!listener || typeof listener === 'function') ? reactProdInvariant('231', registrationName, typeof listener) : void 0;
+ return listener;
+}
+
+/**
+ * Allows registered plugins an opportunity to extract events from top-level
+ * native browser events.
+ *
+ * @return {*} An accumulation of synthetic events.
+ * @internal
+ */
+function extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var events = null;
+ for (var i = 0; i < plugins.length; i++) {
+ // Not every plugin in the ordering may be loaded at runtime.
+ var possiblePlugin = plugins[i];
+ if (possiblePlugin) {
+ var extractedEvents = possiblePlugin.extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+ if (extractedEvents) {
+ events = accumulateInto(events, extractedEvents);
+ }
+ }
+ }
+ return events;
+}
+
+function runEventsInBatch(events) {
+ if (events !== null) {
+ eventQueue = accumulateInto(eventQueue, events);
+ }
+
+ // Set `eventQueue` to null before processing it so that we can tell if more
+ // events get enqueued while processing.
+ var processingEventQueue = eventQueue;
+ eventQueue = null;
+
+ if (!processingEventQueue) {
+ return;
+ }
+
+ forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
+ !!eventQueue ? reactProdInvariant('95') : void 0;
+ // This would be a good time to rethrow if any of the event handlers threw.
+ rethrowCaughtError();
+}
+
+function runExtractedEventsInBatch(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var events = extractEvents(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+ runEventsInBatch(events);
+}
+
+var FunctionComponent = 0;
+var ClassComponent = 1;
+var IndeterminateComponent = 2; // Before we know whether it is function or class
+var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
+var HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
+var HostComponent = 5;
+var HostText = 6;
+var Fragment = 7;
+var Mode = 8;
+var ContextConsumer = 9;
+var ContextProvider = 10;
+var ForwardRef = 11;
+var Profiler = 12;
+var SuspenseComponent = 13;
+var MemoComponent = 14;
+var SimpleMemoComponent = 15;
+var LazyComponent = 16;
+var IncompleteClassComponent = 17;
+var DehydratedSuspenseComponent = 18;
+
+var randomKey = Math.random().toString(36).slice(2);
+var internalInstanceKey = '__reactInternalInstance$' + randomKey;
+var internalEventHandlersKey = '__reactEventHandlers$' + randomKey;
+
+function precacheFiberNode(hostInst, node) {
+ node[internalInstanceKey] = hostInst;
+}
+
+/**
+ * Given a DOM node, return the closest ReactDOMComponent or
+ * ReactDOMTextComponent instance ancestor.
+ */
+function getClosestInstanceFromNode(node) {
+ if (node[internalInstanceKey]) {
+ return node[internalInstanceKey];
+ }
+
+ while (!node[internalInstanceKey]) {
+ if (node.parentNode) {
+ node = node.parentNode;
+ } else {
+ // Top of the tree. This node must not be part of a React tree (or is
+ // unmounted, potentially).
+ return null;
+ }
+ }
+
+ var inst = node[internalInstanceKey];
+ if (inst.tag === HostComponent || inst.tag === HostText) {
+ // In Fiber, this will always be the deepest root.
+ return inst;
+ }
+
+ return null;
+}
+
+/**
+ * Given a DOM node, return the ReactDOMComponent or ReactDOMTextComponent
+ * instance, or null if the node was not rendered by this React.
+ */
+function getInstanceFromNode$1(node) {
+ var inst = node[internalInstanceKey];
+ if (inst) {
+ if (inst.tag === HostComponent || inst.tag === HostText) {
+ return inst;
+ } else {
+ return null;
+ }
+ }
+ return null;
+}
+
+/**
+ * Given a ReactDOMComponent or ReactDOMTextComponent, return the corresponding
+ * DOM node.
+ */
+function getNodeFromInstance$1(inst) {
+ if (inst.tag === HostComponent || inst.tag === HostText) {
+ // In Fiber this, is just the state node right now. We assume it will be
+ // a host component or host text.
+ return inst.stateNode;
+ }
+
+ // Without this first invariant, passing a non-DOM-component triggers the next
+ // invariant for a missing parent, which is super confusing.
+ reactProdInvariant('33');
+}
+
+function getFiberCurrentPropsFromNode$1(node) {
+ return node[internalEventHandlersKey] || null;
+}
+
+function updateFiberProps(node, props) {
+ node[internalEventHandlersKey] = props;
+}
+
+function getParent(inst) {
+ do {
+ inst = inst.return;
+ // TODO: If this is a HostRoot we might want to bail out.
+ // That is depending on if we want nested subtrees (layers) to bubble
+ // events to their parent. We could also go through parentNode on the
+ // host node but that wouldn't work for React Native and doesn't let us
+ // do the portal feature.
+ } while (inst && inst.tag !== HostComponent);
+ if (inst) {
+ return inst;
+ }
+ return null;
+}
+
+/**
+ * Return the lowest common ancestor of A and B, or null if they are in
+ * different trees.
+ */
+function getLowestCommonAncestor(instA, instB) {
+ var depthA = 0;
+ for (var tempA = instA; tempA; tempA = getParent(tempA)) {
+ depthA++;
+ }
+ var depthB = 0;
+ for (var tempB = instB; tempB; tempB = getParent(tempB)) {
+ depthB++;
+ }
+
+ // If A is deeper, crawl up.
+ while (depthA - depthB > 0) {
+ instA = getParent(instA);
+ depthA--;
+ }
+
+ // If B is deeper, crawl up.
+ while (depthB - depthA > 0) {
+ instB = getParent(instB);
+ depthB--;
+ }
+
+ // Walk in lockstep until we find a match.
+ var depth = depthA;
+ while (depth--) {
+ if (instA === instB || instA === instB.alternate) {
+ return instA;
+ }
+ instA = getParent(instA);
+ instB = getParent(instB);
+ }
+ return null;
+}
+
+/**
+ * Return if A is an ancestor of B.
+ */
+
+
+/**
+ * Return the parent instance of the passed-in instance.
+ */
+
+
+/**
+ * Simulates the traversal of a two-phase, capture/bubble event dispatch.
+ */
+function traverseTwoPhase(inst, fn, arg) {
+ var path = [];
+ while (inst) {
+ path.push(inst);
+ inst = getParent(inst);
+ }
+ var i = void 0;
+ for (i = path.length; i-- > 0;) {
+ fn(path[i], 'captured', arg);
+ }
+ for (i = 0; i < path.length; i++) {
+ fn(path[i], 'bubbled', arg);
+ }
+}
+
+/**
+ * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
+ * should would receive a `mouseEnter` or `mouseLeave` event.
+ *
+ * Does not invoke the callback on the nearest common ancestor because nothing
+ * "entered" or "left" that element.
+ */
+function traverseEnterLeave(from, to, fn, argFrom, argTo) {
+ var common = from && to ? getLowestCommonAncestor(from, to) : null;
+ var pathFrom = [];
+ while (true) {
+ if (!from) {
+ break;
+ }
+ if (from === common) {
+ break;
+ }
+ var alternate = from.alternate;
+ if (alternate !== null && alternate === common) {
+ break;
+ }
+ pathFrom.push(from);
+ from = getParent(from);
+ }
+ var pathTo = [];
+ while (true) {
+ if (!to) {
+ break;
+ }
+ if (to === common) {
+ break;
+ }
+ var _alternate = to.alternate;
+ if (_alternate !== null && _alternate === common) {
+ break;
+ }
+ pathTo.push(to);
+ to = getParent(to);
+ }
+ for (var i = 0; i < pathFrom.length; i++) {
+ fn(pathFrom[i], 'bubbled', argFrom);
+ }
+ for (var _i = pathTo.length; _i-- > 0;) {
+ fn(pathTo[_i], 'captured', argTo);
+ }
+}
+
+/**
+ * Some event types have a notion of different registration names for different
+ * "phases" of propagation. This finds listeners by a given phase.
+ */
+function listenerAtPhase(inst, event, propagationPhase) {
+ var registrationName = event.dispatchConfig.phasedRegistrationNames[propagationPhase];
+ return getListener(inst, registrationName);
+}
+
+/**
+ * A small set of propagation patterns, each of which will accept a small amount
+ * of information, and generate a set of "dispatch ready event objects" - which
+ * are sets of events that have already been annotated with a set of dispatched
+ * listener functions/ids. The API is designed this way to discourage these
+ * propagation strategies from actually executing the dispatches, since we
+ * always want to collect the entire set of dispatches before executing even a
+ * single one.
+ */
+
+/**
+ * Tags a `SyntheticEvent` with dispatched listeners. Creating this function
+ * here, allows us to not have to bind or create functions for each event.
+ * Mutating the event's members allows us to not have to create a wrapping
+ * "dispatch" object that pairs the event with the listener.
+ */
+function accumulateDirectionalDispatches(inst, phase, event) {
+ var listener = listenerAtPhase(inst, event, phase);
+ if (listener) {
+ event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
+ }
+}
+
+/**
+ * Collect dispatches (must be entirely collected before dispatching - see unit
+ * tests). Lazily allocate the array to conserve memory. We must loop through
+ * each event and perform the traversal for each one. We cannot perform a
+ * single traversal for the entire collection of events because each event may
+ * have a different target.
+ */
+function accumulateTwoPhaseDispatchesSingle(event) {
+ if (event && event.dispatchConfig.phasedRegistrationNames) {
+ traverseTwoPhase(event._targetInst, accumulateDirectionalDispatches, event);
+ }
+}
+
+/**
+ * Accumulates without regard to direction, does not look for phased
+ * registration names. Same as `accumulateDirectDispatchesSingle` but without
+ * requiring that the `dispatchMarker` be the same as the dispatched ID.
+ */
+function accumulateDispatches(inst, ignoredDirection, event) {
+ if (inst && event && event.dispatchConfig.registrationName) {
+ var registrationName = event.dispatchConfig.registrationName;
+ var listener = getListener(inst, registrationName);
+ if (listener) {
+ event._dispatchListeners = accumulateInto(event._dispatchListeners, listener);
+ event._dispatchInstances = accumulateInto(event._dispatchInstances, inst);
+ }
+ }
+}
+
+/**
+ * Accumulates dispatches on an `SyntheticEvent`, but only for the
+ * `dispatchMarker`.
+ * @param {SyntheticEvent} event
+ */
+function accumulateDirectDispatchesSingle(event) {
+ if (event && event.dispatchConfig.registrationName) {
+ accumulateDispatches(event._targetInst, null, event);
+ }
+}
+
+function accumulateTwoPhaseDispatches(events) {
+ forEachAccumulated(events, accumulateTwoPhaseDispatchesSingle);
+}
+
+
+
+function accumulateEnterLeaveDispatches(leave, enter, from, to) {
+ traverseEnterLeave(from, to, accumulateDispatches, leave, enter);
+}
+
+function accumulateDirectDispatches(events) {
+ forEachAccumulated(events, accumulateDirectDispatchesSingle);
+}
+
+var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
+
+// Do not uses the below two methods directly!
+// Instead use constants exported from DOMTopLevelEventTypes in ReactDOM.
+// (It is the only module that is allowed to access these methods.)
+
+function unsafeCastStringToDOMTopLevelType(topLevelType) {
+ return topLevelType;
+}
+
+function unsafeCastDOMTopLevelTypeToString(topLevelType) {
+ return topLevelType;
+}
+
+/**
+ * Generate a mapping of standard vendor prefixes using the defined style property and event name.
+ *
+ * @param {string} styleProp
+ * @param {string} eventName
+ * @returns {object}
+ */
+function makePrefixMap(styleProp, eventName) {
+ var prefixes = {};
+
+ prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
+ prefixes['Webkit' + styleProp] = 'webkit' + eventName;
+ prefixes['Moz' + styleProp] = 'moz' + eventName;
+
+ return prefixes;
+}
+
+/**
+ * A list of event names to a configurable list of vendor prefixes.
+ */
+var vendorPrefixes = {
+ animationend: makePrefixMap('Animation', 'AnimationEnd'),
+ animationiteration: makePrefixMap('Animation', 'AnimationIteration'),
+ animationstart: makePrefixMap('Animation', 'AnimationStart'),
+ transitionend: makePrefixMap('Transition', 'TransitionEnd')
+};
+
+/**
+ * Event names that have already been detected and prefixed (if applicable).
+ */
+var prefixedEventNames = {};
+
+/**
+ * Element to check for prefixes on.
+ */
+var style = {};
+
+/**
+ * Bootstrap if a DOM exists.
+ */
+if (canUseDOM) {
+ style = document.createElementNS('http://www.w3.org/1999/xhtml', 'div').style;
+
+ // On some platforms, in particular some releases of Android 4.x,
+ // the un-prefixed "animation" and "transition" properties are defined on the
+ // style object but the events that fire will still be prefixed, so we need
+ // to check if the un-prefixed events are usable, and if not remove them from the map.
+ if (!('AnimationEvent' in window)) {
+ delete vendorPrefixes.animationend.animation;
+ delete vendorPrefixes.animationiteration.animation;
+ delete vendorPrefixes.animationstart.animation;
+ }
+
+ // Same as above
+ if (!('TransitionEvent' in window)) {
+ delete vendorPrefixes.transitionend.transition;
+ }
+}
+
+/**
+ * Attempts to determine the correct vendor prefixed event name.
+ *
+ * @param {string} eventName
+ * @returns {string}
+ */
+function getVendorPrefixedEventName(eventName) {
+ if (prefixedEventNames[eventName]) {
+ return prefixedEventNames[eventName];
+ } else if (!vendorPrefixes[eventName]) {
+ return eventName;
+ }
+
+ var prefixMap = vendorPrefixes[eventName];
+
+ for (var styleProp in prefixMap) {
+ if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
+ return prefixedEventNames[eventName] = prefixMap[styleProp];
+ }
+ }
+
+ return eventName;
+}
+
+/**
+ * To identify top level events in ReactDOM, we use constants defined by this
+ * module. This is the only module that uses the unsafe* methods to express
+ * that the constants actually correspond to the browser event names. This lets
+ * us save some bundle size by avoiding a top level type -> event name map.
+ * The rest of ReactDOM code should import top level types from this file.
+ */
+var TOP_ABORT = unsafeCastStringToDOMTopLevelType('abort');
+var TOP_ANIMATION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationend'));
+var TOP_ANIMATION_ITERATION = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationiteration'));
+var TOP_ANIMATION_START = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('animationstart'));
+var TOP_BLUR = unsafeCastStringToDOMTopLevelType('blur');
+var TOP_CAN_PLAY = unsafeCastStringToDOMTopLevelType('canplay');
+var TOP_CAN_PLAY_THROUGH = unsafeCastStringToDOMTopLevelType('canplaythrough');
+var TOP_CANCEL = unsafeCastStringToDOMTopLevelType('cancel');
+var TOP_CHANGE = unsafeCastStringToDOMTopLevelType('change');
+var TOP_CLICK = unsafeCastStringToDOMTopLevelType('click');
+var TOP_CLOSE = unsafeCastStringToDOMTopLevelType('close');
+var TOP_COMPOSITION_END = unsafeCastStringToDOMTopLevelType('compositionend');
+var TOP_COMPOSITION_START = unsafeCastStringToDOMTopLevelType('compositionstart');
+var TOP_COMPOSITION_UPDATE = unsafeCastStringToDOMTopLevelType('compositionupdate');
+var TOP_CONTEXT_MENU = unsafeCastStringToDOMTopLevelType('contextmenu');
+var TOP_COPY = unsafeCastStringToDOMTopLevelType('copy');
+var TOP_CUT = unsafeCastStringToDOMTopLevelType('cut');
+var TOP_DOUBLE_CLICK = unsafeCastStringToDOMTopLevelType('dblclick');
+var TOP_AUX_CLICK = unsafeCastStringToDOMTopLevelType('auxclick');
+var TOP_DRAG = unsafeCastStringToDOMTopLevelType('drag');
+var TOP_DRAG_END = unsafeCastStringToDOMTopLevelType('dragend');
+var TOP_DRAG_ENTER = unsafeCastStringToDOMTopLevelType('dragenter');
+var TOP_DRAG_EXIT = unsafeCastStringToDOMTopLevelType('dragexit');
+var TOP_DRAG_LEAVE = unsafeCastStringToDOMTopLevelType('dragleave');
+var TOP_DRAG_OVER = unsafeCastStringToDOMTopLevelType('dragover');
+var TOP_DRAG_START = unsafeCastStringToDOMTopLevelType('dragstart');
+var TOP_DROP = unsafeCastStringToDOMTopLevelType('drop');
+var TOP_DURATION_CHANGE = unsafeCastStringToDOMTopLevelType('durationchange');
+var TOP_EMPTIED = unsafeCastStringToDOMTopLevelType('emptied');
+var TOP_ENCRYPTED = unsafeCastStringToDOMTopLevelType('encrypted');
+var TOP_ENDED = unsafeCastStringToDOMTopLevelType('ended');
+var TOP_ERROR = unsafeCastStringToDOMTopLevelType('error');
+var TOP_FOCUS = unsafeCastStringToDOMTopLevelType('focus');
+var TOP_GOT_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType('gotpointercapture');
+var TOP_INPUT = unsafeCastStringToDOMTopLevelType('input');
+var TOP_INVALID = unsafeCastStringToDOMTopLevelType('invalid');
+var TOP_KEY_DOWN = unsafeCastStringToDOMTopLevelType('keydown');
+var TOP_KEY_PRESS = unsafeCastStringToDOMTopLevelType('keypress');
+var TOP_KEY_UP = unsafeCastStringToDOMTopLevelType('keyup');
+var TOP_LOAD = unsafeCastStringToDOMTopLevelType('load');
+var TOP_LOAD_START = unsafeCastStringToDOMTopLevelType('loadstart');
+var TOP_LOADED_DATA = unsafeCastStringToDOMTopLevelType('loadeddata');
+var TOP_LOADED_METADATA = unsafeCastStringToDOMTopLevelType('loadedmetadata');
+var TOP_LOST_POINTER_CAPTURE = unsafeCastStringToDOMTopLevelType('lostpointercapture');
+var TOP_MOUSE_DOWN = unsafeCastStringToDOMTopLevelType('mousedown');
+var TOP_MOUSE_MOVE = unsafeCastStringToDOMTopLevelType('mousemove');
+var TOP_MOUSE_OUT = unsafeCastStringToDOMTopLevelType('mouseout');
+var TOP_MOUSE_OVER = unsafeCastStringToDOMTopLevelType('mouseover');
+var TOP_MOUSE_UP = unsafeCastStringToDOMTopLevelType('mouseup');
+var TOP_PASTE = unsafeCastStringToDOMTopLevelType('paste');
+var TOP_PAUSE = unsafeCastStringToDOMTopLevelType('pause');
+var TOP_PLAY = unsafeCastStringToDOMTopLevelType('play');
+var TOP_PLAYING = unsafeCastStringToDOMTopLevelType('playing');
+var TOP_POINTER_CANCEL = unsafeCastStringToDOMTopLevelType('pointercancel');
+var TOP_POINTER_DOWN = unsafeCastStringToDOMTopLevelType('pointerdown');
+
+
+var TOP_POINTER_MOVE = unsafeCastStringToDOMTopLevelType('pointermove');
+var TOP_POINTER_OUT = unsafeCastStringToDOMTopLevelType('pointerout');
+var TOP_POINTER_OVER = unsafeCastStringToDOMTopLevelType('pointerover');
+var TOP_POINTER_UP = unsafeCastStringToDOMTopLevelType('pointerup');
+var TOP_PROGRESS = unsafeCastStringToDOMTopLevelType('progress');
+var TOP_RATE_CHANGE = unsafeCastStringToDOMTopLevelType('ratechange');
+var TOP_RESET = unsafeCastStringToDOMTopLevelType('reset');
+var TOP_SCROLL = unsafeCastStringToDOMTopLevelType('scroll');
+var TOP_SEEKED = unsafeCastStringToDOMTopLevelType('seeked');
+var TOP_SEEKING = unsafeCastStringToDOMTopLevelType('seeking');
+var TOP_SELECTION_CHANGE = unsafeCastStringToDOMTopLevelType('selectionchange');
+var TOP_STALLED = unsafeCastStringToDOMTopLevelType('stalled');
+var TOP_SUBMIT = unsafeCastStringToDOMTopLevelType('submit');
+var TOP_SUSPEND = unsafeCastStringToDOMTopLevelType('suspend');
+var TOP_TEXT_INPUT = unsafeCastStringToDOMTopLevelType('textInput');
+var TOP_TIME_UPDATE = unsafeCastStringToDOMTopLevelType('timeupdate');
+var TOP_TOGGLE = unsafeCastStringToDOMTopLevelType('toggle');
+var TOP_TOUCH_CANCEL = unsafeCastStringToDOMTopLevelType('touchcancel');
+var TOP_TOUCH_END = unsafeCastStringToDOMTopLevelType('touchend');
+var TOP_TOUCH_MOVE = unsafeCastStringToDOMTopLevelType('touchmove');
+var TOP_TOUCH_START = unsafeCastStringToDOMTopLevelType('touchstart');
+var TOP_TRANSITION_END = unsafeCastStringToDOMTopLevelType(getVendorPrefixedEventName('transitionend'));
+var TOP_VOLUME_CHANGE = unsafeCastStringToDOMTopLevelType('volumechange');
+var TOP_WAITING = unsafeCastStringToDOMTopLevelType('waiting');
+var TOP_WHEEL = unsafeCastStringToDOMTopLevelType('wheel');
+
+// List of events that need to be individually attached to media elements.
+// Note that events in this list will *not* be listened to at the top level
+// unless they're explicitly whitelisted in `ReactBrowserEventEmitter.listenTo`.
+var mediaEventTypes = [TOP_ABORT, TOP_CAN_PLAY, TOP_CAN_PLAY_THROUGH, TOP_DURATION_CHANGE, TOP_EMPTIED, TOP_ENCRYPTED, TOP_ENDED, TOP_ERROR, TOP_LOADED_DATA, TOP_LOADED_METADATA, TOP_LOAD_START, TOP_PAUSE, TOP_PLAY, TOP_PLAYING, TOP_PROGRESS, TOP_RATE_CHANGE, TOP_SEEKED, TOP_SEEKING, TOP_STALLED, TOP_SUSPEND, TOP_TIME_UPDATE, TOP_VOLUME_CHANGE, TOP_WAITING];
+
+function getRawEventName(topLevelType) {
+ return unsafeCastDOMTopLevelTypeToString(topLevelType);
+}
+
+/**
+ * These variables store information about text content of a target node,
+ * allowing comparison of content before and after a given event.
+ *
+ * Identify the node where selection currently begins, then observe
+ * both its text content and its current position in the DOM. Since the
+ * browser may natively replace the target node during composition, we can
+ * use its position to find its replacement.
+ *
+ *
+ */
+
+var root = null;
+var startText = null;
+var fallbackText = null;
+
+function initialize(nativeEventTarget) {
+ root = nativeEventTarget;
+ startText = getText();
+ return true;
+}
+
+function reset() {
+ root = null;
+ startText = null;
+ fallbackText = null;
+}
+
+function getData() {
+ if (fallbackText) {
+ return fallbackText;
+ }
+
+ var start = void 0;
+ var startValue = startText;
+ var startLength = startValue.length;
+ var end = void 0;
+ var endValue = getText();
+ var endLength = endValue.length;
+
+ for (start = 0; start < startLength; start++) {
+ if (startValue[start] !== endValue[start]) {
+ break;
+ }
+ }
+
+ var minEnd = startLength - start;
+ for (end = 1; end <= minEnd; end++) {
+ if (startValue[startLength - end] !== endValue[endLength - end]) {
+ break;
+ }
+ }
+
+ var sliceTail = end > 1 ? 1 - end : undefined;
+ fallbackText = endValue.slice(start, sliceTail);
+ return fallbackText;
+}
+
+function getText() {
+ if ('value' in root) {
+ return root.value;
+ }
+ return root.textContent;
+}
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+/* eslint valid-typeof: 0 */
+
+var EVENT_POOL_SIZE = 10;
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var EventInterface = {
+ type: null,
+ target: null,
+ // currentTarget is set when dispatching; no use in copying it here
+ currentTarget: function () {
+ return null;
+ },
+ eventPhase: null,
+ bubbles: null,
+ cancelable: null,
+ timeStamp: function (event) {
+ return event.timeStamp || Date.now();
+ },
+ defaultPrevented: null,
+ isTrusted: null
+};
+
+function functionThatReturnsTrue() {
+ return true;
+}
+
+function functionThatReturnsFalse() {
+ return false;
+}
+
+/**
+ * Synthetic events are dispatched by event plugins, typically in response to a
+ * top-level event delegation handler.
+ *
+ * These systems should generally use pooling to reduce the frequency of garbage
+ * collection. The system should check `isPersistent` to determine whether the
+ * event should be released into the pool after being dispatched. Users that
+ * need a persisted event should invoke `persist`.
+ *
+ * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
+ * normalizing browser quirks. Subclasses do not necessarily have to implement a
+ * DOM interface; custom application-specific events can also subclass this.
+ *
+ * @param {object} dispatchConfig Configuration used to dispatch this event.
+ * @param {*} targetInst Marker identifying the event target.
+ * @param {object} nativeEvent Native browser event.
+ * @param {DOMEventTarget} nativeEventTarget Target node.
+ */
+function SyntheticEvent(dispatchConfig, targetInst, nativeEvent, nativeEventTarget) {
+ this.dispatchConfig = dispatchConfig;
+ this._targetInst = targetInst;
+ this.nativeEvent = nativeEvent;
+
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ if (!Interface.hasOwnProperty(propName)) {
+ continue;
+ }
+ var normalize = Interface[propName];
+ if (normalize) {
+ this[propName] = normalize(nativeEvent);
+ } else {
+ if (propName === 'target') {
+ this.target = nativeEventTarget;
+ } else {
+ this[propName] = nativeEvent[propName];
+ }
+ }
+ }
+
+ var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
+ if (defaultPrevented) {
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ } else {
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ }
+ this.isPropagationStopped = functionThatReturnsFalse;
+ return this;
+}
+
+_assign(SyntheticEvent.prototype, {
+ preventDefault: function () {
+ this.defaultPrevented = true;
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else if (typeof event.returnValue !== 'unknown') {
+ event.returnValue = false;
+ }
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ },
+
+ stopPropagation: function () {
+ var event = this.nativeEvent;
+ if (!event) {
+ return;
+ }
+
+ if (event.stopPropagation) {
+ event.stopPropagation();
+ } else if (typeof event.cancelBubble !== 'unknown') {
+ // The ChangeEventPlugin registers a "propertychange" event for
+ // IE. This event does not support bubbling or cancelling, and
+ // any references to cancelBubble throw "Member not found". A
+ // typeof check of "unknown" circumvents this issue (and is also
+ // IE specific).
+ event.cancelBubble = true;
+ }
+
+ this.isPropagationStopped = functionThatReturnsTrue;
+ },
+
+ /**
+ * We release all dispatched `SyntheticEvent`s after each event loop, adding
+ * them back into the pool. This allows a way to hold onto a reference that
+ * won't be added back into the pool.
+ */
+ persist: function () {
+ this.isPersistent = functionThatReturnsTrue;
+ },
+
+ /**
+ * Checks if this event should be released back into the pool.
+ *
+ * @return {boolean} True if this should not be released, false otherwise.
+ */
+ isPersistent: functionThatReturnsFalse,
+
+ /**
+ * `PooledClass` looks for `destructor` on each instance it releases.
+ */
+ destructor: function () {
+ var Interface = this.constructor.Interface;
+ for (var propName in Interface) {
+ {
+ this[propName] = null;
+ }
+ }
+ this.dispatchConfig = null;
+ this._targetInst = null;
+ this.nativeEvent = null;
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ this.isPropagationStopped = functionThatReturnsFalse;
+ this._dispatchListeners = null;
+ this._dispatchInstances = null;
+
+ }
+});
+
+SyntheticEvent.Interface = EventInterface;
+
+/**
+ * Helper to reduce boilerplate when creating subclasses.
+ */
+SyntheticEvent.extend = function (Interface) {
+ var Super = this;
+
+ var E = function () {};
+ E.prototype = Super.prototype;
+ var prototype = new E();
+
+ function Class() {
+ return Super.apply(this, arguments);
+ }
+ _assign(prototype, Class.prototype);
+ Class.prototype = prototype;
+ Class.prototype.constructor = Class;
+
+ Class.Interface = _assign({}, Super.Interface, Interface);
+ Class.extend = Super.extend;
+ addEventPoolingTo(Class);
+
+ return Class;
+};
+
+addEventPoolingTo(SyntheticEvent);
+
+function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
+ var EventConstructor = this;
+ if (EventConstructor.eventPool.length) {
+ var instance = EventConstructor.eventPool.pop();
+ EventConstructor.call(instance, dispatchConfig, targetInst, nativeEvent, nativeInst);
+ return instance;
+ }
+ return new EventConstructor(dispatchConfig, targetInst, nativeEvent, nativeInst);
+}
+
+function releasePooledEvent(event) {
+ var EventConstructor = this;
+ !(event instanceof EventConstructor) ? reactProdInvariant('279') : void 0;
+ event.destructor();
+ if (EventConstructor.eventPool.length < EVENT_POOL_SIZE) {
+ EventConstructor.eventPool.push(event);
+ }
+}
+
+function addEventPoolingTo(EventConstructor) {
+ EventConstructor.eventPool = [];
+ EventConstructor.getPooled = getPooledEvent;
+ EventConstructor.release = releasePooledEvent;
+}
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
+ */
+var SyntheticCompositionEvent = SyntheticEvent.extend({
+ data: null
+});
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
+ * /#events-inputevents
+ */
+var SyntheticInputEvent = SyntheticEvent.extend({
+ data: null
+});
+
+var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
+var START_KEYCODE = 229;
+
+var canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
+
+var documentMode = null;
+if (canUseDOM && 'documentMode' in document) {
+ documentMode = document.documentMode;
+}
+
+// Webkit offers a very useful `textInput` event that can be used to
+// directly represent `beforeInput`. The IE `textinput` event is not as
+// useful, so we don't use it.
+var canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode;
+
+// In IE9+, we have access to composition events, but the data supplied
+// by the native compositionend event may be incorrect. Japanese ideographic
+// spaces, for instance (\u3000) are not recorded correctly.
+var useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11);
+
+var SPACEBAR_CODE = 32;
+var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
+
+// Events and their corresponding property names.
+var eventTypes = {
+ beforeInput: {
+ phasedRegistrationNames: {
+ bubbled: 'onBeforeInput',
+ captured: 'onBeforeInputCapture'
+ },
+ dependencies: [TOP_COMPOSITION_END, TOP_KEY_PRESS, TOP_TEXT_INPUT, TOP_PASTE]
+ },
+ compositionEnd: {
+ phasedRegistrationNames: {
+ bubbled: 'onCompositionEnd',
+ captured: 'onCompositionEndCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_COMPOSITION_END, TOP_KEY_DOWN, TOP_KEY_PRESS, TOP_KEY_UP, TOP_MOUSE_DOWN]
+ },
+ compositionStart: {
+ phasedRegistrationNames: {
+ bubbled: 'onCompositionStart',
+ captured: 'onCompositionStartCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_COMPOSITION_START, TOP_KEY_DOWN, TOP_KEY_PRESS, TOP_KEY_UP, TOP_MOUSE_DOWN]
+ },
+ compositionUpdate: {
+ phasedRegistrationNames: {
+ bubbled: 'onCompositionUpdate',
+ captured: 'onCompositionUpdateCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_COMPOSITION_UPDATE, TOP_KEY_DOWN, TOP_KEY_PRESS, TOP_KEY_UP, TOP_MOUSE_DOWN]
+ }
+};
+
+// Track whether we've ever handled a keypress on the space key.
+var hasSpaceKeypress = false;
+
+/**
+ * Return whether a native keypress event is assumed to be a command.
+ * This is required because Firefox fires `keypress` events for key commands
+ * (cut, copy, select-all, etc.) even though no character is inserted.
+ */
+function isKeypressCommand(nativeEvent) {
+ return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) &&
+ // ctrlKey && altKey is equivalent to AltGr, and is not a command.
+ !(nativeEvent.ctrlKey && nativeEvent.altKey);
+}
+
+/**
+ * Translate native top level events into event types.
+ *
+ * @param {string} topLevelType
+ * @return {object}
+ */
+function getCompositionEventType(topLevelType) {
+ switch (topLevelType) {
+ case TOP_COMPOSITION_START:
+ return eventTypes.compositionStart;
+ case TOP_COMPOSITION_END:
+ return eventTypes.compositionEnd;
+ case TOP_COMPOSITION_UPDATE:
+ return eventTypes.compositionUpdate;
+ }
+}
+
+/**
+ * Does our fallback best-guess model think this event signifies that
+ * composition has begun?
+ *
+ * @param {string} topLevelType
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isFallbackCompositionStart(topLevelType, nativeEvent) {
+ return topLevelType === TOP_KEY_DOWN && nativeEvent.keyCode === START_KEYCODE;
+}
+
+/**
+ * Does our fallback mode think that this event is the end of composition?
+ *
+ * @param {string} topLevelType
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isFallbackCompositionEnd(topLevelType, nativeEvent) {
+ switch (topLevelType) {
+ case TOP_KEY_UP:
+ // Command keys insert or clear IME input.
+ return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
+ case TOP_KEY_DOWN:
+ // Expect IME keyCode on each keydown. If we get any other
+ // code we must have exited earlier.
+ return nativeEvent.keyCode !== START_KEYCODE;
+ case TOP_KEY_PRESS:
+ case TOP_MOUSE_DOWN:
+ case TOP_BLUR:
+ // Events are not possible without cancelling IME.
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * Google Input Tools provides composition data via a CustomEvent,
+ * with the `data` property populated in the `detail` object. If this
+ * is available on the event object, use it. If not, this is a plain
+ * composition event and we have nothing special to extract.
+ *
+ * @param {object} nativeEvent
+ * @return {?string}
+ */
+function getDataFromCustomEvent(nativeEvent) {
+ var detail = nativeEvent.detail;
+ if (typeof detail === 'object' && 'data' in detail) {
+ return detail.data;
+ }
+ return null;
+}
+
+/**
+ * Check if a composition event was triggered by Korean IME.
+ * Our fallback mode does not work well with IE's Korean IME,
+ * so just use native composition events when Korean IME is used.
+ * Although CompositionEvent.locale property is deprecated,
+ * it is available in IE, where our fallback mode is enabled.
+ *
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
+function isUsingKoreanIME(nativeEvent) {
+ return nativeEvent.locale === 'ko';
+}
+
+// Track the current IME composition status, if any.
+var isComposing = false;
+
+/**
+ * @return {?object} A SyntheticCompositionEvent.
+ */
+function extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var eventType = void 0;
+ var fallbackData = void 0;
+
+ if (canUseCompositionEvent) {
+ eventType = getCompositionEventType(topLevelType);
+ } else if (!isComposing) {
+ if (isFallbackCompositionStart(topLevelType, nativeEvent)) {
+ eventType = eventTypes.compositionStart;
+ }
+ } else if (isFallbackCompositionEnd(topLevelType, nativeEvent)) {
+ eventType = eventTypes.compositionEnd;
+ }
+
+ if (!eventType) {
+ return null;
+ }
+
+ if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
+ // The current composition is stored statically and must not be
+ // overwritten while composition continues.
+ if (!isComposing && eventType === eventTypes.compositionStart) {
+ isComposing = initialize(nativeEventTarget);
+ } else if (eventType === eventTypes.compositionEnd) {
+ if (isComposing) {
+ fallbackData = getData();
+ }
+ }
+ }
+
+ var event = SyntheticCompositionEvent.getPooled(eventType, targetInst, nativeEvent, nativeEventTarget);
+
+ if (fallbackData) {
+ // Inject data generated from fallback path into the synthetic event.
+ // This matches the property of native CompositionEventInterface.
+ event.data = fallbackData;
+ } else {
+ var customData = getDataFromCustomEvent(nativeEvent);
+ if (customData !== null) {
+ event.data = customData;
+ }
+ }
+
+ accumulateTwoPhaseDispatches(event);
+ return event;
+}
+
+/**
+ * @param {TopLevelType} topLevelType Number from `TopLevelType`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?string} The string corresponding to this `beforeInput` event.
+ */
+function getNativeBeforeInputChars(topLevelType, nativeEvent) {
+ switch (topLevelType) {
+ case TOP_COMPOSITION_END:
+ return getDataFromCustomEvent(nativeEvent);
+ case TOP_KEY_PRESS:
+ /**
+ * If native `textInput` events are available, our goal is to make
+ * use of them. However, there is a special case: the spacebar key.
+ * In Webkit, preventing default on a spacebar `textInput` event
+ * cancels character insertion, but it *also* causes the browser
+ * to fall back to its default spacebar behavior of scrolling the
+ * page.
+ *
+ * Tracking at:
+ * https://code.google.com/p/chromium/issues/detail?id=355103
+ *
+ * To avoid this issue, use the keypress event as if no `textInput`
+ * event is available.
+ */
+ var which = nativeEvent.which;
+ if (which !== SPACEBAR_CODE) {
+ return null;
+ }
+
+ hasSpaceKeypress = true;
+ return SPACEBAR_CHAR;
+
+ case TOP_TEXT_INPUT:
+ // Record the characters to be added to the DOM.
+ var chars = nativeEvent.data;
+
+ // If it's a spacebar character, assume that we have already handled
+ // it at the keypress level and bail immediately. Android Chrome
+ // doesn't give us keycodes, so we need to ignore it.
+ if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
+ return null;
+ }
+
+ return chars;
+
+ default:
+ // For other native event types, do nothing.
+ return null;
+ }
+}
+
+/**
+ * For browsers that do not provide the `textInput` event, extract the
+ * appropriate string to use for SyntheticInputEvent.
+ *
+ * @param {number} topLevelType Number from `TopLevelEventTypes`.
+ * @param {object} nativeEvent Native browser event.
+ * @return {?string} The fallback string for this `beforeInput` event.
+ */
+function getFallbackBeforeInputChars(topLevelType, nativeEvent) {
+ // If we are currently composing (IME) and using a fallback to do so,
+ // try to extract the composed characters from the fallback object.
+ // If composition event is available, we extract a string only at
+ // compositionevent, otherwise extract it at fallback events.
+ if (isComposing) {
+ if (topLevelType === TOP_COMPOSITION_END || !canUseCompositionEvent && isFallbackCompositionEnd(topLevelType, nativeEvent)) {
+ var chars = getData();
+ reset();
+ isComposing = false;
+ return chars;
+ }
+ return null;
+ }
+
+ switch (topLevelType) {
+ case TOP_PASTE:
+ // If a paste event occurs after a keypress, throw out the input
+ // chars. Paste events should not lead to BeforeInput events.
+ return null;
+ case TOP_KEY_PRESS:
+ /**
+ * As of v27, Firefox may fire keypress events even when no character
+ * will be inserted. A few possibilities:
+ *
+ * - `which` is `0`. Arrow keys, Esc key, etc.
+ *
+ * - `which` is the pressed key code, but no char is available.
+ * Ex: 'AltGr + d` in Polish. There is no modified character for
+ * this key combination and no character is inserted into the
+ * document, but FF fires the keypress for char code `100` anyway.
+ * No `input` event will occur.
+ *
+ * - `which` is the pressed key code, but a command combination is
+ * being used. Ex: `Cmd+C`. No character is inserted, and no
+ * `input` event will occur.
+ */
+ if (!isKeypressCommand(nativeEvent)) {
+ // IE fires the `keypress` event when a user types an emoji via
+ // Touch keyboard of Windows. In such a case, the `char` property
+ // holds an emoji character like `\uD83D\uDE0A`. Because its length
+ // is 2, the property `which` does not represent an emoji correctly.
+ // In such a case, we directly return the `char` property instead of
+ // using `which`.
+ if (nativeEvent.char && nativeEvent.char.length > 1) {
+ return nativeEvent.char;
+ } else if (nativeEvent.which) {
+ return String.fromCharCode(nativeEvent.which);
+ }
+ }
+ return null;
+ case TOP_COMPOSITION_END:
+ return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data;
+ default:
+ return null;
+ }
+}
+
+/**
+ * Extract a SyntheticInputEvent for `beforeInput`, based on either native
+ * `textInput` or fallback behavior.
+ *
+ * @return {?object} A SyntheticInputEvent.
+ */
+function extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var chars = void 0;
+
+ if (canUseTextInputEvent) {
+ chars = getNativeBeforeInputChars(topLevelType, nativeEvent);
+ } else {
+ chars = getFallbackBeforeInputChars(topLevelType, nativeEvent);
+ }
+
+ // If no characters are being inserted, no BeforeInput event should
+ // be fired.
+ if (!chars) {
+ return null;
+ }
+
+ var event = SyntheticInputEvent.getPooled(eventTypes.beforeInput, targetInst, nativeEvent, nativeEventTarget);
+
+ event.data = chars;
+ accumulateTwoPhaseDispatches(event);
+ return event;
+}
+
+/**
+ * Create an `onBeforeInput` event to match
+ * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
+ *
+ * This event plugin is based on the native `textInput` event
+ * available in Chrome, Safari, Opera, and IE. This event fires after
+ * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
+ *
+ * `beforeInput` is spec'd but not implemented in any browsers, and
+ * the `input` event does not provide any useful information about what has
+ * actually been added, contrary to the spec. Thus, `textInput` is the best
+ * available event to identify the characters that have actually been inserted
+ * into the target node.
+ *
+ * This plugin is also responsible for emitting `composition` events, thus
+ * allowing us to share composition fallback code for both `beforeInput` and
+ * `composition` event types.
+ */
+var BeforeInputEventPlugin = {
+ eventTypes: eventTypes,
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var composition = extractCompositionEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+
+ var beforeInput = extractBeforeInputEvent(topLevelType, targetInst, nativeEvent, nativeEventTarget);
+
+ if (composition === null) {
+ return beforeInput;
+ }
+
+ if (beforeInput === null) {
+ return composition;
+ }
+
+ return [composition, beforeInput];
+ }
+};
+
+// Use to restore controlled state after a change event has fired.
+
+var restoreImpl = null;
+var restoreTarget = null;
+var restoreQueue = null;
+
+function restoreStateOfTarget(target) {
+ // We perform this translation at the end of the event loop so that we
+ // always receive the correct fiber here
+ var internalInstance = getInstanceFromNode(target);
+ if (!internalInstance) {
+ // Unmounted
+ return;
+ }
+ !(typeof restoreImpl === 'function') ? reactProdInvariant('280') : void 0;
+ var props = getFiberCurrentPropsFromNode(internalInstance.stateNode);
+ restoreImpl(internalInstance.stateNode, internalInstance.type, props);
+}
+
+function setRestoreImplementation(impl) {
+ restoreImpl = impl;
+}
+
+function enqueueStateRestore(target) {
+ if (restoreTarget) {
+ if (restoreQueue) {
+ restoreQueue.push(target);
+ } else {
+ restoreQueue = [target];
+ }
+ } else {
+ restoreTarget = target;
+ }
+}
+
+function needsStateRestore() {
+ return restoreTarget !== null || restoreQueue !== null;
+}
+
+function restoreStateIfNeeded() {
+ if (!restoreTarget) {
+ return;
+ }
+ var target = restoreTarget;
+ var queuedTargets = restoreQueue;
+ restoreTarget = null;
+ restoreQueue = null;
+
+ restoreStateOfTarget(target);
+ if (queuedTargets) {
+ for (var i = 0; i < queuedTargets.length; i++) {
+ restoreStateOfTarget(queuedTargets[i]);
+ }
+ }
+}
+
+// Used as a way to call batchedUpdates when we don't have a reference to
+// the renderer. Such as when we're dispatching events or if third party
+// libraries need to call batchedUpdates. Eventually, this API will go away when
+// everything is batched by default. We'll then have a similar API to opt-out of
+// scheduled work and instead do synchronous work.
+
+// Defaults
+var _batchedUpdatesImpl = function (fn, bookkeeping) {
+ return fn(bookkeeping);
+};
+var _interactiveUpdatesImpl = function (fn, a, b) {
+ return fn(a, b);
+};
+var _flushInteractiveUpdatesImpl = function () {};
+
+var isBatching = false;
+function batchedUpdates(fn, bookkeeping) {
+ if (isBatching) {
+ // If we are currently inside another batch, we need to wait until it
+ // fully completes before restoring state.
+ return fn(bookkeeping);
+ }
+ isBatching = true;
+ try {
+ return _batchedUpdatesImpl(fn, bookkeeping);
+ } finally {
+ // Here we wait until all updates have propagated, which is important
+ // when using controlled components within layers:
+ // https://github.com/facebook/react/issues/1698
+ // Then we restore state of any controlled component.
+ isBatching = false;
+ var controlledComponentsHavePendingUpdates = needsStateRestore();
+ if (controlledComponentsHavePendingUpdates) {
+ // If a controlled event was fired, we may need to restore the state of
+ // the DOM node back to the controlled value. This is necessary when React
+ // bails out of the update without touching the DOM.
+ _flushInteractiveUpdatesImpl();
+ restoreStateIfNeeded();
+ }
+ }
+}
+
+function interactiveUpdates(fn, a, b) {
+ return _interactiveUpdatesImpl(fn, a, b);
+}
+
+
+
+function setBatchingImplementation(batchedUpdatesImpl, interactiveUpdatesImpl, flushInteractiveUpdatesImpl) {
+ _batchedUpdatesImpl = batchedUpdatesImpl;
+ _interactiveUpdatesImpl = interactiveUpdatesImpl;
+ _flushInteractiveUpdatesImpl = flushInteractiveUpdatesImpl;
+}
+
+/**
+ * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
+ */
+var supportedInputTypes = {
+ color: true,
+ date: true,
+ datetime: true,
+ 'datetime-local': true,
+ email: true,
+ month: true,
+ number: true,
+ password: true,
+ range: true,
+ search: true,
+ tel: true,
+ text: true,
+ time: true,
+ url: true,
+ week: true
+};
+
+function isTextInputElement(elem) {
+ var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
+
+ if (nodeName === 'input') {
+ return !!supportedInputTypes[elem.type];
+ }
+
+ if (nodeName === 'textarea') {
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * HTML nodeType values that represent the type of the node
+ */
+
+var ELEMENT_NODE = 1;
+var TEXT_NODE = 3;
+var COMMENT_NODE = 8;
+var DOCUMENT_NODE = 9;
+var DOCUMENT_FRAGMENT_NODE = 11;
+
+/**
+ * Gets the target node from a native browser event by accounting for
+ * inconsistencies in browser DOM APIs.
+ *
+ * @param {object} nativeEvent Native browser event.
+ * @return {DOMEventTarget} Target node.
+ */
+function getEventTarget(nativeEvent) {
+ // Fallback to nativeEvent.srcElement for IE9
+ // https://github.com/facebook/react/issues/12506
+ var target = nativeEvent.target || nativeEvent.srcElement || window;
+
+ // Normalize SVG <use> element events #4963
+ if (target.correspondingUseElement) {
+ target = target.correspondingUseElement;
+ }
+
+ // Safari may fire events on text nodes (Node.TEXT_NODE is 3).
+ // @see http://www.quirksmode.org/js/events_properties.html
+ return target.nodeType === TEXT_NODE ? target.parentNode : target;
+}
+
+/**
+ * Checks if an event is supported in the current execution environment.
+ *
+ * NOTE: This will not work correctly for non-generic events such as `change`,
+ * `reset`, `load`, `error`, and `select`.
+ *
+ * Borrows from Modernizr.
+ *
+ * @param {string} eventNameSuffix Event name, e.g. "click".
+ * @return {boolean} True if the event is supported.
+ * @internal
+ * @license Modernizr 3.0.0pre (Custom Build) | MIT
+ */
+function isEventSupported(eventNameSuffix) {
+ if (!canUseDOM) {
+ return false;
+ }
+
+ var eventName = 'on' + eventNameSuffix;
+ var isSupported = eventName in document;
+
+ if (!isSupported) {
+ var element = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ element.setAttribute(eventName, 'return;');
+ isSupported = typeof element[eventName] === 'function';
+ }
+
+ return isSupported;
+}
+
+function isCheckable(elem) {
+ var type = elem.type;
+ var nodeName = elem.nodeName;
+ return nodeName && nodeName.toLowerCase() === 'input' && (type === 'checkbox' || type === 'radio');
+}
+
+function getTracker(node) {
+ return node._valueTracker;
+}
+
+function detachTracker(node) {
+ node._valueTracker = null;
+}
+
+function getValueFromNode(node) {
+ var value = '';
+ if (!node) {
+ return value;
+ }
+
+ if (isCheckable(node)) {
+ value = node.checked ? 'true' : 'false';
+ } else {
+ value = node.value;
+ }
+
+ return value;
+}
+
+function trackValueOnNode(node) {
+ var valueField = isCheckable(node) ? 'checked' : 'value';
+ var descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField);
+
+ var currentValue = '' + node[valueField];
+
+ // if someone has already defined a value or Safari, then bail
+ // and don't track value will cause over reporting of changes,
+ // but it's better then a hard failure
+ // (needed for certain tests that spyOn input values and Safari)
+ if (node.hasOwnProperty(valueField) || typeof descriptor === 'undefined' || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') {
+ return;
+ }
+ var get = descriptor.get,
+ set = descriptor.set;
+
+ Object.defineProperty(node, valueField, {
+ configurable: true,
+ get: function () {
+ return get.call(this);
+ },
+ set: function (value) {
+ currentValue = '' + value;
+ set.call(this, value);
+ }
+ });
+ // We could've passed this the first time
+ // but it triggers a bug in IE11 and Edge 14/15.
+ // Calling defineProperty() again should be equivalent.
+ // https://github.com/facebook/react/issues/11768
+ Object.defineProperty(node, valueField, {
+ enumerable: descriptor.enumerable
+ });
+
+ var tracker = {
+ getValue: function () {
+ return currentValue;
+ },
+ setValue: function (value) {
+ currentValue = '' + value;
+ },
+ stopTracking: function () {
+ detachTracker(node);
+ delete node[valueField];
+ }
+ };
+ return tracker;
+}
+
+function track(node) {
+ if (getTracker(node)) {
+ return;
+ }
+
+ // TODO: Once it's just Fiber we can move this to node._wrapperState
+ node._valueTracker = trackValueOnNode(node);
+}
+
+function updateValueIfChanged(node) {
+ if (!node) {
+ return false;
+ }
+
+ var tracker = getTracker(node);
+ // if there is no tracker at this point it's unlikely
+ // that trying again will succeed
+ if (!tracker) {
+ return true;
+ }
+
+ var lastValue = tracker.getValue();
+ var nextValue = getValueFromNode(node);
+ if (nextValue !== lastValue) {
+ tracker.setValue(nextValue);
+ return true;
+ }
+ return false;
+}
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+var BEFORE_SLASH_RE = /^(.*)[\\\/]/;
+
+var describeComponentFrame = function (name, source, ownerName) {
+ var sourceInfo = '';
+ if (source) {
+ var path = source.fileName;
+ var fileName = path.replace(BEFORE_SLASH_RE, '');
+ sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
+ } else if (ownerName) {
+ sourceInfo = ' (created by ' + ownerName + ')';
+ }
+ return '\n in ' + (name || 'Unknown') + sourceInfo;
+};
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+var FAUX_ITERATOR_SYMBOL = '@@iterator';
+
+function getIteratorFn(maybeIterable) {
+ if (maybeIterable === null || typeof maybeIterable !== 'object') {
+ return null;
+ }
+ var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
+ if (typeof maybeIterator === 'function') {
+ return maybeIterator;
+ }
+ return null;
+}
+
+var Pending = 0;
+var Resolved = 1;
+var Rejected = 2;
+
+function refineResolvedLazyComponent(lazyComponent) {
+ return lazyComponent._status === Resolved ? lazyComponent._result : null;
+}
+
+function getWrappedName(outerType, innerType, wrapperName) {
+ var functionName = innerType.displayName || innerType.name || '';
+ return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
+}
+
+function getComponentName(type) {
+ if (type == null) {
+ // Host root, text node or just invalid type.
+ return null;
+ }
+ if (typeof type === 'function') {
+ return type.displayName || type.name || null;
+ }
+ if (typeof type === 'string') {
+ return type;
+ }
+ switch (type) {
+ case REACT_CONCURRENT_MODE_TYPE:
+ return 'ConcurrentMode';
+ case REACT_FRAGMENT_TYPE:
+ return 'Fragment';
+ case REACT_PORTAL_TYPE:
+ return 'Portal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_SUSPENSE_TYPE:
+ return 'Suspense';
+ }
+ if (typeof type === 'object') {
+ switch (type.$$typeof) {
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_FORWARD_REF_TYPE:
+ return getWrappedName(type, type.render, 'ForwardRef');
+ case REACT_MEMO_TYPE:
+ return getComponentName(type.type);
+ case REACT_LAZY_TYPE:
+ {
+ var thenable = type;
+ var resolvedThenable = refineResolvedLazyComponent(thenable);
+ if (resolvedThenable) {
+ return getComponentName(resolvedThenable);
+ }
+ }
+ }
+ }
+ return null;
+}
+
+var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+
+function describeFiber(fiber) {
+ switch (fiber.tag) {
+ case HostRoot:
+ case HostPortal:
+ case HostText:
+ case Fragment:
+ case ContextProvider:
+ case ContextConsumer:
+ return '';
+ default:
+ var owner = fiber._debugOwner;
+ var source = fiber._debugSource;
+ var name = getComponentName(fiber.type);
+ var ownerName = null;
+ if (owner) {
+ ownerName = getComponentName(owner.type);
+ }
+ return describeComponentFrame(name, source, ownerName);
+ }
+}
+
+function getStackByFiberInDevAndProd(workInProgress) {
+ var info = '';
+ var node = workInProgress;
+ do {
+ info += describeFiber(node);
+ node = node.return;
+ } while (node);
+ return info;
+}
+
+// A reserved attribute.
+// It is handled by React separately and shouldn't be written to the DOM.
+var RESERVED = 0;
+
+// A simple string attribute.
+// Attributes that aren't in the whitelist are presumed to have this type.
+var STRING = 1;
+
+// A string attribute that accepts booleans in React. In HTML, these are called
+// "enumerated" attributes with "true" and "false" as possible values.
+// When true, it should be set to a "true" string.
+// When false, it should be set to a "false" string.
+var BOOLEANISH_STRING = 2;
+
+// A real boolean attribute.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+var BOOLEAN = 3;
+
+// An attribute that can be used as a flag as well as with a value.
+// When true, it should be present (set either to an empty string or its name).
+// When false, it should be omitted.
+// For any other value, should be present with that value.
+var OVERLOADED_BOOLEAN = 4;
+
+// An attribute that must be numeric or parse as a numeric.
+// When falsy, it should be removed.
+var NUMERIC = 5;
+
+// An attribute that must be positive numeric or parse as a positive numeric.
+// When falsy, it should be removed.
+var POSITIVE_NUMERIC = 6;
+
+/* eslint-disable max-len */
+var ATTRIBUTE_NAME_START_CHAR = ':A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
+/* eslint-enable max-len */
+var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + '\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
+
+
+var ROOT_ATTRIBUTE_NAME = 'data-reactroot';
+var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$');
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var illegalAttributeNameCache = {};
+var validatedAttributeNameCache = {};
+
+function isAttributeNameSafe(attributeName) {
+ if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) {
+ return true;
+ }
+ if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) {
+ return false;
+ }
+ if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
+ validatedAttributeNameCache[attributeName] = true;
+ return true;
+ }
+ illegalAttributeNameCache[attributeName] = true;
+ return false;
+}
+
+function shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null) {
+ return propertyInfo.type === RESERVED;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) {
+ return true;
+ }
+ return false;
+}
+
+function shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag) {
+ if (propertyInfo !== null && propertyInfo.type === RESERVED) {
+ return false;
+ }
+ switch (typeof value) {
+ case 'function':
+ // $FlowIssue symbol is perfectly valid here
+ case 'symbol':
+ // eslint-disable-line
+ return true;
+ case 'boolean':
+ {
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ return !propertyInfo.acceptsBooleans;
+ } else {
+ var prefix = name.toLowerCase().slice(0, 5);
+ return prefix !== 'data-' && prefix !== 'aria-';
+ }
+ }
+ default:
+ return false;
+ }
+}
+
+function shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag) {
+ if (value === null || typeof value === 'undefined') {
+ return true;
+ }
+ if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag)) {
+ return true;
+ }
+ if (isCustomComponentTag) {
+ return false;
+ }
+ if (propertyInfo !== null) {
+ switch (propertyInfo.type) {
+ case BOOLEAN:
+ return !value;
+ case OVERLOADED_BOOLEAN:
+ return value === false;
+ case NUMERIC:
+ return isNaN(value);
+ case POSITIVE_NUMERIC:
+ return isNaN(value) || value < 1;
+ }
+ }
+ return false;
+}
+
+function getPropertyInfo(name) {
+ return properties.hasOwnProperty(name) ? properties[name] : null;
+}
+
+function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace) {
+ this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN;
+ this.attributeName = attributeName;
+ this.attributeNamespace = attributeNamespace;
+ this.mustUseProperty = mustUseProperty;
+ this.propertyName = name;
+ this.type = type;
+}
+
+// When adding attributes to this list, be sure to also add them to
+// the `possibleStandardNames` module to ensure casing and incorrect
+// name warnings.
+var properties = {};
+
+// These props are reserved by React. They shouldn't be written to the DOM.
+['children', 'dangerouslySetInnerHTML',
+// TODO: This prevents the assignment of defaultValue to regular
+// elements (not just inputs). Now that ReactDOMInput assigns to the
+// defaultValue property -- do we need this?
+'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, RESERVED, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// A few React string attributes have a different name.
+// This is a mapping from React prop names to the attribute names.
+[['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv']].forEach(function (_ref) {
+ var name = _ref[0],
+ attributeName = _ref[1];
+
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" HTML attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are "enumerated" SVG attributes that accept "true" and "false".
+// In React, we let users pass `true` and `false` even though technically
+// these aren't boolean attributes (they are coerced to strings).
+// Since these are SVG attributes, their attribute names are case-sensitive.
+['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML boolean attributes.
+['allowFullScreen', 'async',
+// Note: there is a special case that prevents it from being written to the DOM
+// on the client side because the browsers are inconsistent. Instead we call focus().
+'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless',
+// Microdata
+'itemScope'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are the few React props that we set as DOM properties
+// rather than attributes. These are all booleans.
+['checked',
+// Note: `option.selected` is not updated if `select.multiple` is
+// disabled with `removeAttribute`. We have special logic for handling this.
+'multiple', 'muted', 'selected'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, BOOLEAN, true, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that are "overloaded booleans": they behave like
+// booleans, but can also accept a string value.
+['capture', 'download'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, OVERLOADED_BOOLEAN, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be positive numbers.
+['cols', 'rows', 'size', 'span'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, POSITIVE_NUMERIC, false, // mustUseProperty
+ name, // attributeName
+ null);
+} // attributeNamespace
+);
+
+// These are HTML attributes that must be numbers.
+['rowSpan', 'start'].forEach(function (name) {
+ properties[name] = new PropertyInfoRecord(name, NUMERIC, false, // mustUseProperty
+ name.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+var CAMELIZE = /[\-\:]([a-z])/g;
+var capitalize = function (token) {
+ return token[1].toUpperCase();
+};
+
+// This is a list of all SVG attributes that need special casing, namespacing,
+// or boolean value assignment. Regular attributes that just accept strings
+// and have the same names are omitted, just like in the HTML whitelist.
+// Some of these attributes can be hard to find. This list was created by
+// scrapping the MDN documentation.
+['accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'xmlns:xlink', 'x-height'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, null);
+} // attributeNamespace
+);
+
+// String SVG attributes with the xlink namespace.
+['xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/1999/xlink');
+});
+
+// String SVG attributes with the xml namespace.
+['xml:base', 'xml:lang', 'xml:space'].forEach(function (attributeName) {
+ var name = attributeName.replace(CAMELIZE, capitalize);
+ properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
+ attributeName, 'http://www.w3.org/XML/1998/namespace');
+});
+
+// These attribute exists both in HTML and SVG.
+// The attribute name is case-sensitive in SVG so we can't just use
+// the React name like we do for attributes that exist only in HTML.
+['tabIndex', 'crossOrigin'].forEach(function (attributeName) {
+ properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty
+ attributeName.toLowerCase(), // attributeName
+ null);
+} // attributeNamespace
+);
+
+/**
+ * Get the value for a property on a node. Only used in DEV for SSR validation.
+ * The "expected" argument is used as a hint of what the expected value is.
+ * Some properties have multiple equivalent values.
+ */
+
+
+/**
+ * Get the value for a attribute on a node. Only used in DEV for SSR validation.
+ * The third argument is used as a hint of what the expected value is. Some
+ * attributes have multiple equivalent values.
+ */
+
+
+/**
+ * Sets the value for a property on a node.
+ *
+ * @param {DOMElement} node
+ * @param {string} name
+ * @param {*} value
+ */
+function setValueForProperty(node, name, value, isCustomComponentTag) {
+ var propertyInfo = getPropertyInfo(name);
+ if (shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag)) {
+ return;
+ }
+ if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) {
+ value = null;
+ }
+ // If the prop isn't in the special list, treat it as a simple attribute.
+ if (isCustomComponentTag || propertyInfo === null) {
+ if (isAttributeNameSafe(name)) {
+ var _attributeName = name;
+ if (value === null) {
+ node.removeAttribute(_attributeName);
+ } else {
+ node.setAttribute(_attributeName, '' + value);
+ }
+ }
+ return;
+ }
+ var mustUseProperty = propertyInfo.mustUseProperty;
+
+ if (mustUseProperty) {
+ var propertyName = propertyInfo.propertyName;
+
+ if (value === null) {
+ var type = propertyInfo.type;
+
+ node[propertyName] = type === BOOLEAN ? false : '';
+ } else {
+ // Contrary to `setAttribute`, object properties are properly
+ // `toString`ed by IE8/9.
+ node[propertyName] = value;
+ }
+ return;
+ }
+ // The rest are treated as attributes with special cases.
+ var attributeName = propertyInfo.attributeName,
+ attributeNamespace = propertyInfo.attributeNamespace;
+
+ if (value === null) {
+ node.removeAttribute(attributeName);
+ } else {
+ var _type = propertyInfo.type;
+
+ var attributeValue = void 0;
+ if (_type === BOOLEAN || _type === OVERLOADED_BOOLEAN && value === true) {
+ attributeValue = '';
+ } else {
+ // `setAttribute` with objects becomes only `[object]` in IE8/9,
+ // ('' + value) makes it output the correct toString()-value.
+ attributeValue = '' + value;
+ }
+ if (attributeNamespace) {
+ node.setAttributeNS(attributeNamespace, attributeName, attributeValue);
+ } else {
+ node.setAttribute(attributeName, attributeValue);
+ }
+ }
+}
+
+// Flow does not allow string concatenation of most non-string types. To work
+// around this limitation, we use an opaque type that can only be obtained by
+// passing the value through getToStringValue first.
+function toString(value) {
+ return '' + value;
+}
+
+function getToStringValue(value) {
+ switch (typeof value) {
+ case 'boolean':
+ case 'number':
+ case 'object':
+ case 'string':
+ case 'undefined':
+ return value;
+ default:
+ // function, symbol are assigned as empty strings
+ return '';
+ }
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+var enableUserTimingAPI = false;
+
+// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
+
+
+// In some cases, StrictMode should also double-render lifecycles.
+// This can be confusing for tests though,
+// And it can be bad for performance in production.
+// This feature flag can be used to control the behavior:
+
+
+// To preserve the "Pause on caught exceptions" behavior of the debugger, we
+// replay the begin phase of a failed component inside invokeGuardedCallback.
+
+
+// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
+
+
+// Gather advanced timing metrics for Profiler subtrees.
+var enableProfilerTimer = false;
+
+// Trace which interactions trigger each commit.
+var enableSchedulerTracing = false;
+
+// Only used in www builds.
+var enableSuspenseServerRenderer = false; // TODO: false? Here it might just be false.
+
+// Only used in www builds.
+
+
+// Only used in www builds.
+
+
+// React Fire: prevent the value and checked attributes from syncing
+// with their related DOM properties
+var disableInputAttributeSyncing = false;
+
+// These APIs will no longer be "unstable" in the upcoming 16.7 release,
+// Control this behavior with a flag to support 16.6 minor releases in the meanwhile.
+var enableStableConcurrentModeAPIs = false;
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+function isControlled(props) {
+ var usesChecked = props.type === 'checkbox' || props.type === 'radio';
+ return usesChecked ? props.checked != null : props.value != null;
+}
+
+/**
+ * Implements an <input> host component that allows setting these optional
+ * props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
+ *
+ * If `checked` or `value` are not supplied (or null/undefined), user actions
+ * that affect the checked state or value will trigger updates to the element.
+ *
+ * If they are supplied (and not null/undefined), the rendered element will not
+ * trigger updates to the element. Instead, the props must change in order for
+ * the rendered element to be updated.
+ *
+ * The rendered element will be initialized as unchecked (or `defaultChecked`)
+ * with an empty value (or `defaultValue`).
+ *
+ * See http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
+ */
+
+function getHostProps(element, props) {
+ var node = element;
+ var checked = props.checked;
+
+ var hostProps = _assign({}, props, {
+ defaultChecked: undefined,
+ defaultValue: undefined,
+ value: undefined,
+ checked: checked != null ? checked : node._wrapperState.initialChecked
+ });
+
+ return hostProps;
+}
+
+function initWrapperState(element, props) {
+ var node = element;
+ var defaultValue = props.defaultValue == null ? '' : props.defaultValue;
+
+ node._wrapperState = {
+ initialChecked: props.checked != null ? props.checked : props.defaultChecked,
+ initialValue: getToStringValue(props.value != null ? props.value : defaultValue),
+ controlled: isControlled(props)
+ };
+}
+
+function updateChecked(element, props) {
+ var node = element;
+ var checked = props.checked;
+ if (checked != null) {
+ setValueForProperty(node, 'checked', checked, false);
+ }
+}
+
+function updateWrapper(element, props) {
+ var node = element;
+ updateChecked(element, props);
+
+ var value = getToStringValue(props.value);
+ var type = props.type;
+
+ if (value != null) {
+ if (type === 'number') {
+ if (value === 0 && node.value === '' ||
+ // We explicitly want to coerce to number here if possible.
+ // eslint-disable-next-line
+ node.value != value) {
+ node.value = toString(value);
+ }
+ } else if (node.value !== toString(value)) {
+ node.value = toString(value);
+ }
+ } else if (type === 'submit' || type === 'reset') {
+ // Submit/reset inputs need the attribute removed completely to avoid
+ // blank-text buttons.
+ node.removeAttribute('value');
+ return;
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the value attribute, React only assigns a new value
+ // whenever the defaultValue React prop has changed. When not present,
+ // React does nothing
+ if (props.hasOwnProperty('defaultValue')) {
+ setDefaultValue(node, props.type, getToStringValue(props.defaultValue));
+ }
+ } else {
+ // When syncing the value attribute, the value comes from a cascade of
+ // properties:
+ // 1. The value React property
+ // 2. The defaultValue React property
+ // 3. Otherwise there should be no change
+ if (props.hasOwnProperty('value')) {
+ setDefaultValue(node, props.type, value);
+ } else if (props.hasOwnProperty('defaultValue')) {
+ setDefaultValue(node, props.type, getToStringValue(props.defaultValue));
+ }
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the checked attribute, the attribute is directly
+ // controllable from the defaultValue React property. It needs to be
+ // updated as new props come in.
+ if (props.defaultChecked == null) {
+ node.removeAttribute('checked');
+ } else {
+ node.defaultChecked = !!props.defaultChecked;
+ }
+ } else {
+ // When syncing the checked attribute, it only changes when it needs
+ // to be removed, such as transitioning from a checkbox into a text input
+ if (props.checked == null && props.defaultChecked != null) {
+ node.defaultChecked = !!props.defaultChecked;
+ }
+ }
+}
+
+function postMountWrapper(element, props, isHydrating) {
+ var node = element;
+
+ // Do not assign value if it is already set. This prevents user text input
+ // from being lost during SSR hydration.
+ if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) {
+ var type = props.type;
+ var isButton = type === 'submit' || type === 'reset';
+
+ // Avoid setting value attribute on submit/reset inputs as it overrides the
+ // default value provided by the browser. See: #12872
+ if (isButton && (props.value === undefined || props.value === null)) {
+ return;
+ }
+
+ var _initialValue = toString(node._wrapperState.initialValue);
+
+ // Do not assign value if it is already set. This prevents user text input
+ // from being lost during SSR hydration.
+ if (!isHydrating) {
+ if (disableInputAttributeSyncing) {
+ var value = getToStringValue(props.value);
+
+ // When not syncing the value attribute, the value property points
+ // directly to the React prop. Only assign it if it exists.
+ if (value != null) {
+ // Always assign on buttons so that it is possible to assign an
+ // empty string to clear button text.
+ //
+ // Otherwise, do not re-assign the value property if is empty. This
+ // potentially avoids a DOM write and prevents Firefox (~60.0.1) from
+ // prematurely marking required inputs as invalid. Equality is compared
+ // to the current value in case the browser provided value is not an
+ // empty string.
+ if (isButton || value !== node.value) {
+ node.value = toString(value);
+ }
+ }
+ } else {
+ // When syncing the value attribute, the value property should use
+ // the wrapperState._initialValue property. This uses:
+ //
+ // 1. The value React property when present
+ // 2. The defaultValue React property when present
+ // 3. An empty string
+ if (_initialValue !== node.value) {
+ node.value = _initialValue;
+ }
+ }
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the value attribute, assign the value attribute
+ // directly from the defaultValue React property (when present)
+ var defaultValue = getToStringValue(props.defaultValue);
+ if (defaultValue != null) {
+ node.defaultValue = toString(defaultValue);
+ }
+ } else {
+ // Otherwise, the value attribute is synchronized to the property,
+ // so we assign defaultValue to the same thing as the value property
+ // assignment step above.
+ node.defaultValue = _initialValue;
+ }
+ }
+
+ // Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
+ // this is needed to work around a chrome bug where setting defaultChecked
+ // will sometimes influence the value of checked (even after detachment).
+ // Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416
+ // We need to temporarily unset name to avoid disrupting radio button groups.
+ var name = node.name;
+ if (name !== '') {
+ node.name = '';
+ }
+
+ if (disableInputAttributeSyncing) {
+ // When not syncing the checked attribute, the checked property
+ // never gets assigned. It must be manually set. We don't want
+ // to do this when hydrating so that existing user input isn't
+ // modified
+ if (!isHydrating) {
+ updateChecked(element, props);
+ }
+
+ // Only assign the checked attribute if it is defined. This saves
+ // a DOM write when controlling the checked attribute isn't needed
+ // (text inputs, submit/reset)
+ if (props.hasOwnProperty('defaultChecked')) {
+ node.defaultChecked = !node.defaultChecked;
+ node.defaultChecked = !!props.defaultChecked;
+ }
+ } else {
+ // When syncing the checked attribute, both the checked property and
+ // attribute are assigned at the same time using defaultChecked. This uses:
+ //
+ // 1. The checked React property when present
+ // 2. The defaultChecked React property when present
+ // 3. Otherwise, false
+ node.defaultChecked = !node.defaultChecked;
+ node.defaultChecked = !!node._wrapperState.initialChecked;
+ }
+
+ if (name !== '') {
+ node.name = name;
+ }
+}
+
+function restoreControlledState(element, props) {
+ var node = element;
+ updateWrapper(node, props);
+ updateNamedCousins(node, props);
+}
+
+function updateNamedCousins(rootNode, props) {
+ var name = props.name;
+ if (props.type === 'radio' && name != null) {
+ var queryRoot = rootNode;
+
+ while (queryRoot.parentNode) {
+ queryRoot = queryRoot.parentNode;
+ }
+
+ // If `rootNode.form` was non-null, then we could try `form.elements`,
+ // but that sometimes behaves strangely in IE8. We could also try using
+ // `form.getElementsByName`, but that will only return direct children
+ // and won't include inputs that use the HTML5 `form=` attribute. Since
+ // the input might not even be in a form. It might not even be in the
+ // document. Let's just use the local `querySelectorAll` to ensure we don't
+ // miss anything.
+ var group = queryRoot.querySelectorAll('input[name=' + JSON.stringify('' + name) + '][type="radio"]');
+
+ for (var i = 0; i < group.length; i++) {
+ var otherNode = group[i];
+ if (otherNode === rootNode || otherNode.form !== rootNode.form) {
+ continue;
+ }
+ // This will throw if radio buttons rendered by different copies of React
+ // and the same name are rendered into the same form (same as #1939).
+ // That's probably okay; we don't support it just as we don't support
+ // mixing React radio buttons with non-React ones.
+ var otherProps = getFiberCurrentPropsFromNode$1(otherNode);
+ !otherProps ? reactProdInvariant('90') : void 0;
+
+ // We need update the tracked value on the named cousin since the value
+ // was changed but the input saw no event or value set
+ updateValueIfChanged(otherNode);
+
+ // If this is a controlled radio button group, forcing the input that
+ // was previously checked to update will cause it to be come re-checked
+ // as appropriate.
+ updateWrapper(otherNode, otherProps);
+ }
+ }
+}
+
+// In Chrome, assigning defaultValue to certain input types triggers input validation.
+// For number inputs, the display value loses trailing decimal points. For email inputs,
+// Chrome raises "The specified value <x> is not a valid email address".
+//
+// Here we check to see if the defaultValue has actually changed, avoiding these problems
+// when the user is inputting text
+//
+// https://github.com/facebook/react/issues/7253
+function setDefaultValue(node, type, value) {
+ if (
+ // Focused number inputs synchronize on blur. See ChangeEventPlugin.js
+ type !== 'number' || node.ownerDocument.activeElement !== node) {
+ if (value == null) {
+ node.defaultValue = toString(node._wrapperState.initialValue);
+ } else if (node.defaultValue !== toString(value)) {
+ node.defaultValue = toString(value);
+ }
+ }
+}
+
+var eventTypes$1 = {
+ change: {
+ phasedRegistrationNames: {
+ bubbled: 'onChange',
+ captured: 'onChangeCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_CHANGE, TOP_CLICK, TOP_FOCUS, TOP_INPUT, TOP_KEY_DOWN, TOP_KEY_UP, TOP_SELECTION_CHANGE]
+ }
+};
+
+function createAndAccumulateChangeEvent(inst, nativeEvent, target) {
+ var event = SyntheticEvent.getPooled(eventTypes$1.change, inst, nativeEvent, target);
+ event.type = 'change';
+ // Flag this event loop as needing state restore.
+ enqueueStateRestore(target);
+ accumulateTwoPhaseDispatches(event);
+ return event;
+}
+/**
+ * For IE shims
+ */
+var activeElement = null;
+var activeElementInst = null;
+
+/**
+ * SECTION: handle `change` event
+ */
+function shouldUseChangeEvent(elem) {
+ var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
+ return nodeName === 'select' || nodeName === 'input' && elem.type === 'file';
+}
+
+function manualDispatchChangeEvent(nativeEvent) {
+ var event = createAndAccumulateChangeEvent(activeElementInst, nativeEvent, getEventTarget(nativeEvent));
+
+ // If change and propertychange bubbled, we'd just bind to it like all the
+ // other events and have it go through ReactBrowserEventEmitter. Since it
+ // doesn't, we manually listen for the events and so we have to enqueue and
+ // process the abstract event manually.
+ //
+ // Batching is necessary here in order to ensure that all event handlers run
+ // before the next rerender (including event handlers attached to ancestor
+ // elements instead of directly on the input). Without this, controlled
+ // components don't work properly in conjunction with event bubbling because
+ // the component is rerendered and the value reverted before all the event
+ // handlers can run. See https://github.com/facebook/react/issues/708.
+ batchedUpdates(runEventInBatch, event);
+}
+
+function runEventInBatch(event) {
+ runEventsInBatch(event);
+}
+
+function getInstIfValueChanged(targetInst) {
+ var targetNode = getNodeFromInstance$1(targetInst);
+ if (updateValueIfChanged(targetNode)) {
+ return targetInst;
+ }
+}
+
+function getTargetInstForChangeEvent(topLevelType, targetInst) {
+ if (topLevelType === TOP_CHANGE) {
+ return targetInst;
+ }
+}
+
+/**
+ * SECTION: handle `input` event
+ */
+var isInputEventSupported = false;
+if (canUseDOM) {
+ // IE9 claims to support the input event but fails to trigger it when
+ // deleting text, so we ignore its input events.
+ isInputEventSupported = isEventSupported('input') && (!document.documentMode || document.documentMode > 9);
+}
+
+/**
+ * (For IE <=9) Starts tracking propertychange events on the passed-in element
+ * and override the value property so that we can distinguish user events from
+ * value changes in JS.
+ */
+function startWatchingForValueChange(target, targetInst) {
+ activeElement = target;
+ activeElementInst = targetInst;
+ activeElement.attachEvent('onpropertychange', handlePropertyChange);
+}
+
+/**
+ * (For IE <=9) Removes the event listeners from the currently-tracked element,
+ * if any exists.
+ */
+function stopWatchingForValueChange() {
+ if (!activeElement) {
+ return;
+ }
+ activeElement.detachEvent('onpropertychange', handlePropertyChange);
+ activeElement = null;
+ activeElementInst = null;
+}
+
+/**
+ * (For IE <=9) Handles a propertychange event, sending a `change` event if
+ * the value of the active element has changed.
+ */
+function handlePropertyChange(nativeEvent) {
+ if (nativeEvent.propertyName !== 'value') {
+ return;
+ }
+ if (getInstIfValueChanged(activeElementInst)) {
+ manualDispatchChangeEvent(nativeEvent);
+ }
+}
+
+function handleEventsForInputEventPolyfill(topLevelType, target, targetInst) {
+ if (topLevelType === TOP_FOCUS) {
+ // In IE9, propertychange fires for most input events but is buggy and
+ // doesn't fire when text is deleted, but conveniently, selectionchange
+ // appears to fire in all of the remaining cases so we catch those and
+ // forward the event if the value has changed
+ // In either case, we don't want to call the event handler if the value
+ // is changed from JS so we redefine a setter for `.value` that updates
+ // our activeElementValue variable, allowing us to ignore those changes
+ //
+ // stopWatching() should be a noop here but we call it just in case we
+ // missed a blur event somehow.
+ stopWatchingForValueChange();
+ startWatchingForValueChange(target, targetInst);
+ } else if (topLevelType === TOP_BLUR) {
+ stopWatchingForValueChange();
+ }
+}
+
+// For IE8 and IE9.
+function getTargetInstForInputEventPolyfill(topLevelType, targetInst) {
+ if (topLevelType === TOP_SELECTION_CHANGE || topLevelType === TOP_KEY_UP || topLevelType === TOP_KEY_DOWN) {
+ // On the selectionchange event, the target is just document which isn't
+ // helpful for us so just check activeElement instead.
+ //
+ // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
+ // propertychange on the first input event after setting `value` from a
+ // script and fires only keydown, keypress, keyup. Catching keyup usually
+ // gets it and catching keydown lets us fire an event for the first
+ // keystroke if user does a key repeat (it'll be a little delayed: right
+ // before the second keystroke). Other input methods (e.g., paste) seem to
+ // fire selectionchange normally.
+ return getInstIfValueChanged(activeElementInst);
+ }
+}
+
+/**
+ * SECTION: handle `click` event
+ */
+function shouldUseClickEvent(elem) {
+ // Use the `click` event to detect changes to checkbox and radio inputs.
+ // This approach works across all browsers, whereas `change` does not fire
+ // until `blur` in IE8.
+ var nodeName = elem.nodeName;
+ return nodeName && nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio');
+}
+
+function getTargetInstForClickEvent(topLevelType, targetInst) {
+ if (topLevelType === TOP_CLICK) {
+ return getInstIfValueChanged(targetInst);
+ }
+}
+
+function getTargetInstForInputOrChangeEvent(topLevelType, targetInst) {
+ if (topLevelType === TOP_INPUT || topLevelType === TOP_CHANGE) {
+ return getInstIfValueChanged(targetInst);
+ }
+}
+
+function handleControlledInputBlur(node) {
+ var state = node._wrapperState;
+
+ if (!state || !state.controlled || node.type !== 'number') {
+ return;
+ }
+
+ if (!disableInputAttributeSyncing) {
+ // If controlled, assign the value attribute to the current value on blur
+ setDefaultValue(node, 'number', node.value);
+ }
+}
+
+/**
+ * This plugin creates an `onChange` event that normalizes change events
+ * across form elements. This event fires at a time when it's possible to
+ * change the element's value without seeing a flicker.
+ *
+ * Supported elements are:
+ * - input (see `isTextInputElement`)
+ * - textarea
+ * - select
+ */
+var ChangeEventPlugin = {
+ eventTypes: eventTypes$1,
+
+ _isInputEventSupported: isInputEventSupported,
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var targetNode = targetInst ? getNodeFromInstance$1(targetInst) : window;
+
+ var getTargetInstFunc = void 0,
+ handleEventFunc = void 0;
+ if (shouldUseChangeEvent(targetNode)) {
+ getTargetInstFunc = getTargetInstForChangeEvent;
+ } else if (isTextInputElement(targetNode)) {
+ if (isInputEventSupported) {
+ getTargetInstFunc = getTargetInstForInputOrChangeEvent;
+ } else {
+ getTargetInstFunc = getTargetInstForInputEventPolyfill;
+ handleEventFunc = handleEventsForInputEventPolyfill;
+ }
+ } else if (shouldUseClickEvent(targetNode)) {
+ getTargetInstFunc = getTargetInstForClickEvent;
+ }
+
+ if (getTargetInstFunc) {
+ var inst = getTargetInstFunc(topLevelType, targetInst);
+ if (inst) {
+ var event = createAndAccumulateChangeEvent(inst, nativeEvent, nativeEventTarget);
+ return event;
+ }
+ }
+
+ if (handleEventFunc) {
+ handleEventFunc(topLevelType, targetNode, targetInst);
+ }
+
+ // When blurring, set the value attribute for number inputs
+ if (topLevelType === TOP_BLUR) {
+ handleControlledInputBlur(targetNode);
+ }
+ }
+};
+
+/**
+ * Module that is injectable into `EventPluginHub`, that specifies a
+ * deterministic ordering of `EventPlugin`s. A convenient way to reason about
+ * plugins, without having to package every one of them. This is better than
+ * having plugins be ordered in the same order that they are injected because
+ * that ordering would be influenced by the packaging order.
+ * `ResponderEventPlugin` must occur before `SimpleEventPlugin` so that
+ * preventing default on events is convenient in `SimpleEventPlugin` handlers.
+ */
+var DOMEventPluginOrder = ['ResponderEventPlugin', 'SimpleEventPlugin', 'EnterLeaveEventPlugin', 'ChangeEventPlugin', 'SelectEventPlugin', 'BeforeInputEventPlugin'];
+
+var SyntheticUIEvent = SyntheticEvent.extend({
+ view: null,
+ detail: null
+});
+
+var modifierKeyToProp = {
+ Alt: 'altKey',
+ Control: 'ctrlKey',
+ Meta: 'metaKey',
+ Shift: 'shiftKey'
+};
+
+// Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support
+// getModifierState. If getModifierState is not supported, we map it to a set of
+// modifier keys exposed by the event. In this case, Lock-keys are not supported.
+/**
+ * Translation from modifier key to the associated property in the event.
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
+ */
+
+function modifierStateGetter(keyArg) {
+ var syntheticEvent = this;
+ var nativeEvent = syntheticEvent.nativeEvent;
+ if (nativeEvent.getModifierState) {
+ return nativeEvent.getModifierState(keyArg);
+ }
+ var keyProp = modifierKeyToProp[keyArg];
+ return keyProp ? !!nativeEvent[keyProp] : false;
+}
+
+function getEventModifierState(nativeEvent) {
+ return modifierStateGetter;
+}
+
+var previousScreenX = 0;
+var previousScreenY = 0;
+// Use flags to signal movementX/Y has already been set
+var isMovementXSet = false;
+var isMovementYSet = false;
+
+/**
+ * @interface MouseEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticMouseEvent = SyntheticUIEvent.extend({
+ screenX: null,
+ screenY: null,
+ clientX: null,
+ clientY: null,
+ pageX: null,
+ pageY: null,
+ ctrlKey: null,
+ shiftKey: null,
+ altKey: null,
+ metaKey: null,
+ getModifierState: getEventModifierState,
+ button: null,
+ buttons: null,
+ relatedTarget: function (event) {
+ return event.relatedTarget || (event.fromElement === event.srcElement ? event.toElement : event.fromElement);
+ },
+ movementX: function (event) {
+ if ('movementX' in event) {
+ return event.movementX;
+ }
+
+ var screenX = previousScreenX;
+ previousScreenX = event.screenX;
+
+ if (!isMovementXSet) {
+ isMovementXSet = true;
+ return 0;
+ }
+
+ return event.type === 'mousemove' ? event.screenX - screenX : 0;
+ },
+ movementY: function (event) {
+ if ('movementY' in event) {
+ return event.movementY;
+ }
+
+ var screenY = previousScreenY;
+ previousScreenY = event.screenY;
+
+ if (!isMovementYSet) {
+ isMovementYSet = true;
+ return 0;
+ }
+
+ return event.type === 'mousemove' ? event.screenY - screenY : 0;
+ }
+});
+
+/**
+ * @interface PointerEvent
+ * @see http://www.w3.org/TR/pointerevents/
+ */
+var SyntheticPointerEvent = SyntheticMouseEvent.extend({
+ pointerId: null,
+ width: null,
+ height: null,
+ pressure: null,
+ tangentialPressure: null,
+ tiltX: null,
+ tiltY: null,
+ twist: null,
+ pointerType: null,
+ isPrimary: null
+});
+
+var eventTypes$2 = {
+ mouseEnter: {
+ registrationName: 'onMouseEnter',
+ dependencies: [TOP_MOUSE_OUT, TOP_MOUSE_OVER]
+ },
+ mouseLeave: {
+ registrationName: 'onMouseLeave',
+ dependencies: [TOP_MOUSE_OUT, TOP_MOUSE_OVER]
+ },
+ pointerEnter: {
+ registrationName: 'onPointerEnter',
+ dependencies: [TOP_POINTER_OUT, TOP_POINTER_OVER]
+ },
+ pointerLeave: {
+ registrationName: 'onPointerLeave',
+ dependencies: [TOP_POINTER_OUT, TOP_POINTER_OVER]
+ }
+};
+
+var EnterLeaveEventPlugin = {
+ eventTypes: eventTypes$2,
+
+ /**
+ * For almost every interaction we care about, there will be both a top-level
+ * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
+ * we do not extract duplicate events. However, moving the mouse into the
+ * browser from outside will not fire a `mouseout` event. In this case, we use
+ * the `mouseover` top-level event.
+ */
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var isOverEvent = topLevelType === TOP_MOUSE_OVER || topLevelType === TOP_POINTER_OVER;
+ var isOutEvent = topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_POINTER_OUT;
+
+ if (isOverEvent && (nativeEvent.relatedTarget || nativeEvent.fromElement)) {
+ return null;
+ }
+
+ if (!isOutEvent && !isOverEvent) {
+ // Must not be a mouse or pointer in or out - ignoring.
+ return null;
+ }
+
+ var win = void 0;
+ if (nativeEventTarget.window === nativeEventTarget) {
+ // `nativeEventTarget` is probably a window object.
+ win = nativeEventTarget;
+ } else {
+ // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
+ var doc = nativeEventTarget.ownerDocument;
+ if (doc) {
+ win = doc.defaultView || doc.parentWindow;
+ } else {
+ win = window;
+ }
+ }
+
+ var from = void 0;
+ var to = void 0;
+ if (isOutEvent) {
+ from = targetInst;
+ var related = nativeEvent.relatedTarget || nativeEvent.toElement;
+ to = related ? getClosestInstanceFromNode(related) : null;
+ } else {
+ // Moving to a node from outside the window.
+ from = null;
+ to = targetInst;
+ }
+
+ if (from === to) {
+ // Nothing pertains to our managed components.
+ return null;
+ }
+
+ var eventInterface = void 0,
+ leaveEventType = void 0,
+ enterEventType = void 0,
+ eventTypePrefix = void 0;
+
+ if (topLevelType === TOP_MOUSE_OUT || topLevelType === TOP_MOUSE_OVER) {
+ eventInterface = SyntheticMouseEvent;
+ leaveEventType = eventTypes$2.mouseLeave;
+ enterEventType = eventTypes$2.mouseEnter;
+ eventTypePrefix = 'mouse';
+ } else if (topLevelType === TOP_POINTER_OUT || topLevelType === TOP_POINTER_OVER) {
+ eventInterface = SyntheticPointerEvent;
+ leaveEventType = eventTypes$2.pointerLeave;
+ enterEventType = eventTypes$2.pointerEnter;
+ eventTypePrefix = 'pointer';
+ }
+
+ var fromNode = from == null ? win : getNodeFromInstance$1(from);
+ var toNode = to == null ? win : getNodeFromInstance$1(to);
+
+ var leave = eventInterface.getPooled(leaveEventType, from, nativeEvent, nativeEventTarget);
+ leave.type = eventTypePrefix + 'leave';
+ leave.target = fromNode;
+ leave.relatedTarget = toNode;
+
+ var enter = eventInterface.getPooled(enterEventType, to, nativeEvent, nativeEventTarget);
+ enter.type = eventTypePrefix + 'enter';
+ enter.target = toNode;
+ enter.relatedTarget = fromNode;
+
+ accumulateEnterLeaveDispatches(leave, enter, from, to);
+
+ return [leave, enter];
+ }
+};
+
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
+ ;
+}
+
+var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+
+/**
+ * Performs equality by iterating through keys on an object and returning false
+ * when any key has values which are not strictly equal between the arguments.
+ * Returns true when the values of all keys are strictly equal.
+ */
+function shallowEqual(objA, objB) {
+ if (is(objA, objB)) {
+ return true;
+ }
+
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
+ return false;
+ }
+
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
+
+ if (keysA.length !== keysB.length) {
+ return false;
+ }
+
+ // Test for A's keys different from B.
+ for (var i = 0; i < keysA.length; i++) {
+ if (!hasOwnProperty$1.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * `ReactInstanceMap` maintains a mapping from a public facing stateful
+ * instance (key) and the internal representation (value). This allows public
+ * methods to accept the user facing instance as an argument and map them back
+ * to internal methods.
+ *
+ * Note that this module is currently shared and assumed to be stateless.
+ * If this becomes an actual Map, that will break.
+ */
+
+/**
+ * This API should be called `delete` but we'd have to make sure to always
+ * transform these to strings for IE support. When this transform is fully
+ * supported we can rename it.
+ */
+
+
+function get(key) {
+ return key._reactInternalFiber;
+}
+
+function has(key) {
+ return key._reactInternalFiber !== undefined;
+}
+
+function set(key, value) {
+ key._reactInternalFiber = value;
+}
+
+// Don't change these two values. They're used by React Dev Tools.
+var NoEffect = /* */0;
+var PerformedWork = /* */1;
+
+// You can change the rest (and add more).
+var Placement = /* */2;
+var Update = /* */4;
+var PlacementAndUpdate = /* */6;
+var Deletion = /* */8;
+var ContentReset = /* */16;
+var Callback = /* */32;
+var DidCapture = /* */64;
+var Ref = /* */128;
+var Snapshot = /* */256;
+var Passive = /* */512;
+
+// Passive & Update & Callback & Ref & Snapshot
+var LifecycleEffectMask = /* */932;
+
+// Union of all host effects
+var HostEffectMask = /* */1023;
+
+var Incomplete = /* */1024;
+var ShouldCapture = /* */2048;
+
+var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;
+
+var MOUNTING = 1;
+var MOUNTED = 2;
+var UNMOUNTED = 3;
+
+function isFiberMountedImpl(fiber) {
+ var node = fiber;
+ if (!fiber.alternate) {
+ // If there is no alternate, this might be a new tree that isn't inserted
+ // yet. If it is, then it will have a pending insertion effect on it.
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ while (node.return) {
+ node = node.return;
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ }
+ } else {
+ while (node.return) {
+ node = node.return;
+ }
+ }
+ if (node.tag === HostRoot) {
+ // TODO: Check if this was a nested HostRoot when used with
+ // renderContainerIntoSubtree.
+ return MOUNTED;
+ }
+ // If we didn't hit the root, that means that we're in an disconnected tree
+ // that has been unmounted.
+ return UNMOUNTED;
+}
+
+function isFiberMounted(fiber) {
+ return isFiberMountedImpl(fiber) === MOUNTED;
+}
+
+function isMounted(component) {
+ var fiber = get(component);
+ if (!fiber) {
+ return false;
+ }
+ return isFiberMountedImpl(fiber) === MOUNTED;
+}
+
+function assertIsMounted(fiber) {
+ !(isFiberMountedImpl(fiber) === MOUNTED) ? reactProdInvariant('188') : void 0;
+}
+
+function findCurrentFiberUsingSlowPath(fiber) {
+ var alternate = fiber.alternate;
+ if (!alternate) {
+ // If there is no alternate, then we only need to check if it is mounted.
+ var state = isFiberMountedImpl(fiber);
+ !(state !== UNMOUNTED) ? reactProdInvariant('188') : void 0;
+ if (state === MOUNTING) {
+ return null;
+ }
+ return fiber;
+ }
+ // If we have two possible branches, we'll walk backwards up to the root
+ // to see what path the root points to. On the way we may hit one of the
+ // special cases and we'll deal with them.
+ var a = fiber;
+ var b = alternate;
+ while (true) {
+ var parentA = a.return;
+ var parentB = parentA ? parentA.alternate : null;
+ if (!parentA || !parentB) {
+ // We're at the root.
+ break;
+ }
+
+ // If both copies of the parent fiber point to the same child, we can
+ // assume that the child is current. This happens when we bailout on low
+ // priority: the bailed out fiber's child reuses the current child.
+ if (parentA.child === parentB.child) {
+ var child = parentA.child;
+ while (child) {
+ if (child === a) {
+ // We've determined that A is the current branch.
+ assertIsMounted(parentA);
+ return fiber;
+ }
+ if (child === b) {
+ // We've determined that B is the current branch.
+ assertIsMounted(parentA);
+ return alternate;
+ }
+ child = child.sibling;
+ }
+ // We should never have an alternate for any mounting node. So the only
+ // way this could possibly happen is if this was unmounted, if at all.
+ reactProdInvariant('188');
+ }
+
+ if (a.return !== b.return) {
+ // The return pointer of A and the return pointer of B point to different
+ // fibers. We assume that return pointers never criss-cross, so A must
+ // belong to the child set of A.return, and B must belong to the child
+ // set of B.return.
+ a = parentA;
+ b = parentB;
+ } else {
+ // The return pointers point to the same fiber. We'll have to use the
+ // default, slow path: scan the child sets of each parent alternate to see
+ // which child belongs to which set.
+ //
+ // Search parent A's child set
+ var didFindChild = false;
+ var _child = parentA.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentA;
+ b = parentB;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentA;
+ a = parentB;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ if (!didFindChild) {
+ // Search parent B's child set
+ _child = parentB.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentB;
+ b = parentA;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentB;
+ a = parentA;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ !didFindChild ? reactProdInvariant('189') : void 0;
+ }
+ }
+
+ !(a.alternate === b) ? reactProdInvariant('190') : void 0;
+ }
+ // If the root is not a host container, we're in a disconnected tree. I.e.
+ // unmounted.
+ !(a.tag === HostRoot) ? reactProdInvariant('188') : void 0;
+ if (a.stateNode.current === a) {
+ // We've determined that A is the current branch.
+ return fiber;
+ }
+ // Otherwise B has to be current branch.
+ return alternate;
+}
+
+function findCurrentHostFiber(parent) {
+ var currentParent = findCurrentFiberUsingSlowPath(parent);
+ if (!currentParent) {
+ return null;
+ }
+
+ // Next we'll drill down this component to find the first HostComponent/Text.
+ var node = currentParent;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ return node;
+ } else if (node.child) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === currentParent) {
+ return null;
+ }
+ while (!node.sibling) {
+ if (!node.return || node.return === currentParent) {
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ // Flow needs the return null here, but ESLint complains about it.
+ // eslint-disable-next-line no-unreachable
+ return null;
+}
+
+function addEventBubbleListener(element, eventType, listener) {
+ element.addEventListener(eventType, listener, false);
+}
+
+function addEventCaptureListener(element, eventType, listener) {
+ element.addEventListener(eventType, listener, true);
+}
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
+ */
+var SyntheticAnimationEvent = SyntheticEvent.extend({
+ animationName: null,
+ elapsedTime: null,
+ pseudoElement: null
+});
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/clipboard-apis/
+ */
+var SyntheticClipboardEvent = SyntheticEvent.extend({
+ clipboardData: function (event) {
+ return 'clipboardData' in event ? event.clipboardData : window.clipboardData;
+ }
+});
+
+/**
+ * @interface FocusEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticFocusEvent = SyntheticUIEvent.extend({
+ relatedTarget: null
+});
+
+/**
+ * `charCode` represents the actual "character code" and is safe to use with
+ * `String.fromCharCode`. As such, only keys that correspond to printable
+ * characters produce a valid `charCode`, the only exception to this is Enter.
+ * The Tab-key is considered non-printable and does not have a `charCode`,
+ * presumably because it does not produce a tab-character in browsers.
+ *
+ * @param {object} nativeEvent Native browser event.
+ * @return {number} Normalized `charCode` property.
+ */
+function getEventCharCode(nativeEvent) {
+ var charCode = void 0;
+ var keyCode = nativeEvent.keyCode;
+
+ if ('charCode' in nativeEvent) {
+ charCode = nativeEvent.charCode;
+
+ // FF does not set `charCode` for the Enter-key, check against `keyCode`.
+ if (charCode === 0 && keyCode === 13) {
+ charCode = 13;
+ }
+ } else {
+ // IE8 does not implement `charCode`, but `keyCode` has the correct value.
+ charCode = keyCode;
+ }
+
+ // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
+ // report Enter as charCode 10 when ctrl is pressed.
+ if (charCode === 10) {
+ charCode = 13;
+ }
+
+ // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
+ // Must not discard the (non-)printable Enter-key.
+ if (charCode >= 32 || charCode === 13) {
+ return charCode;
+ }
+
+ return 0;
+}
+
+/**
+ * Normalization of deprecated HTML5 `key` values
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
+ */
+var normalizeKey = {
+ Esc: 'Escape',
+ Spacebar: ' ',
+ Left: 'ArrowLeft',
+ Up: 'ArrowUp',
+ Right: 'ArrowRight',
+ Down: 'ArrowDown',
+ Del: 'Delete',
+ Win: 'OS',
+ Menu: 'ContextMenu',
+ Apps: 'ContextMenu',
+ Scroll: 'ScrollLock',
+ MozPrintableKey: 'Unidentified'
+};
+
+/**
+ * Translation from legacy `keyCode` to HTML5 `key`
+ * Only special keys supported, all others depend on keyboard layout or browser
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
+ */
+var translateToKey = {
+ '8': 'Backspace',
+ '9': 'Tab',
+ '12': 'Clear',
+ '13': 'Enter',
+ '16': 'Shift',
+ '17': 'Control',
+ '18': 'Alt',
+ '19': 'Pause',
+ '20': 'CapsLock',
+ '27': 'Escape',
+ '32': ' ',
+ '33': 'PageUp',
+ '34': 'PageDown',
+ '35': 'End',
+ '36': 'Home',
+ '37': 'ArrowLeft',
+ '38': 'ArrowUp',
+ '39': 'ArrowRight',
+ '40': 'ArrowDown',
+ '45': 'Insert',
+ '46': 'Delete',
+ '112': 'F1',
+ '113': 'F2',
+ '114': 'F3',
+ '115': 'F4',
+ '116': 'F5',
+ '117': 'F6',
+ '118': 'F7',
+ '119': 'F8',
+ '120': 'F9',
+ '121': 'F10',
+ '122': 'F11',
+ '123': 'F12',
+ '144': 'NumLock',
+ '145': 'ScrollLock',
+ '224': 'Meta'
+};
+
+/**
+ * @param {object} nativeEvent Native browser event.
+ * @return {string} Normalized `key` property.
+ */
+function getEventKey(nativeEvent) {
+ if (nativeEvent.key) {
+ // Normalize inconsistent values reported by browsers due to
+ // implementations of a working draft specification.
+
+ // FireFox implements `key` but returns `MozPrintableKey` for all
+ // printable characters (normalized to `Unidentified`), ignore it.
+ var key = normalizeKey[nativeEvent.key] || nativeEvent.key;
+ if (key !== 'Unidentified') {
+ return key;
+ }
+ }
+
+ // Browser does not implement `key`, polyfill as much of it as we can.
+ if (nativeEvent.type === 'keypress') {
+ var charCode = getEventCharCode(nativeEvent);
+
+ // The enter-key is technically both printable and non-printable and can
+ // thus be captured by `keypress`, no other non-printable key should.
+ return charCode === 13 ? 'Enter' : String.fromCharCode(charCode);
+ }
+ if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') {
+ // While user keyboard layout determines the actual meaning of each
+ // `keyCode` value, almost all function keys have a universal value.
+ return translateToKey[nativeEvent.keyCode] || 'Unidentified';
+ }
+ return '';
+}
+
+/**
+ * @interface KeyboardEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticKeyboardEvent = SyntheticUIEvent.extend({
+ key: getEventKey,
+ location: null,
+ ctrlKey: null,
+ shiftKey: null,
+ altKey: null,
+ metaKey: null,
+ repeat: null,
+ locale: null,
+ getModifierState: getEventModifierState,
+ // Legacy Interface
+ charCode: function (event) {
+ // `charCode` is the result of a KeyPress event and represents the value of
+ // the actual printable character.
+
+ // KeyPress is deprecated, but its replacement is not yet final and not
+ // implemented in any major browser. Only KeyPress has charCode.
+ if (event.type === 'keypress') {
+ return getEventCharCode(event);
+ }
+ return 0;
+ },
+ keyCode: function (event) {
+ // `keyCode` is the result of a KeyDown/Up event and represents the value of
+ // physical keyboard key.
+
+ // The actual meaning of the value depends on the users' keyboard layout
+ // which cannot be detected. Assuming that it is a US keyboard layout
+ // provides a surprisingly accurate mapping for US and European users.
+ // Due to this, it is left to the user to implement at this time.
+ if (event.type === 'keydown' || event.type === 'keyup') {
+ return event.keyCode;
+ }
+ return 0;
+ },
+ which: function (event) {
+ // `which` is an alias for either `keyCode` or `charCode` depending on the
+ // type of the event.
+ if (event.type === 'keypress') {
+ return getEventCharCode(event);
+ }
+ if (event.type === 'keydown' || event.type === 'keyup') {
+ return event.keyCode;
+ }
+ return 0;
+ }
+});
+
+/**
+ * @interface DragEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticDragEvent = SyntheticMouseEvent.extend({
+ dataTransfer: null
+});
+
+/**
+ * @interface TouchEvent
+ * @see http://www.w3.org/TR/touch-events/
+ */
+var SyntheticTouchEvent = SyntheticUIEvent.extend({
+ touches: null,
+ targetTouches: null,
+ changedTouches: null,
+ altKey: null,
+ metaKey: null,
+ ctrlKey: null,
+ shiftKey: null,
+ getModifierState: getEventModifierState
+});
+
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
+ */
+var SyntheticTransitionEvent = SyntheticEvent.extend({
+ propertyName: null,
+ elapsedTime: null,
+ pseudoElement: null
+});
+
+/**
+ * @interface WheelEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+var SyntheticWheelEvent = SyntheticMouseEvent.extend({
+ deltaX: function (event) {
+ return 'deltaX' in event ? event.deltaX : // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
+ 'wheelDeltaX' in event ? -event.wheelDeltaX : 0;
+ },
+ deltaY: function (event) {
+ return 'deltaY' in event ? event.deltaY : // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
+ 'wheelDeltaY' in event ? -event.wheelDeltaY : // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
+ 'wheelDelta' in event ? -event.wheelDelta : 0;
+ },
+
+ deltaZ: null,
+
+ // Browsers without "deltaMode" is reporting in raw wheel delta where one
+ // notch on the scroll is always +/- 120, roughly equivalent to pixels.
+ // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
+ // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
+ deltaMode: null
+});
+
+/**
+ * Turns
+ * ['abort', ...]
+ * into
+ * eventTypes = {
+ * 'abort': {
+ * phasedRegistrationNames: {
+ * bubbled: 'onAbort',
+ * captured: 'onAbortCapture',
+ * },
+ * dependencies: [TOP_ABORT],
+ * },
+ * ...
+ * };
+ * topLevelEventsToDispatchConfig = new Map([
+ * [TOP_ABORT, { sameConfig }],
+ * ]);
+ */
+
+var interactiveEventTypeNames = [[TOP_BLUR, 'blur'], [TOP_CANCEL, 'cancel'], [TOP_CLICK, 'click'], [TOP_CLOSE, 'close'], [TOP_CONTEXT_MENU, 'contextMenu'], [TOP_COPY, 'copy'], [TOP_CUT, 'cut'], [TOP_AUX_CLICK, 'auxClick'], [TOP_DOUBLE_CLICK, 'doubleClick'], [TOP_DRAG_END, 'dragEnd'], [TOP_DRAG_START, 'dragStart'], [TOP_DROP, 'drop'], [TOP_FOCUS, 'focus'], [TOP_INPUT, 'input'], [TOP_INVALID, 'invalid'], [TOP_KEY_DOWN, 'keyDown'], [TOP_KEY_PRESS, 'keyPress'], [TOP_KEY_UP, 'keyUp'], [TOP_MOUSE_DOWN, 'mouseDown'], [TOP_MOUSE_UP, 'mouseUp'], [TOP_PASTE, 'paste'], [TOP_PAUSE, 'pause'], [TOP_PLAY, 'play'], [TOP_POINTER_CANCEL, 'pointerCancel'], [TOP_POINTER_DOWN, 'pointerDown'], [TOP_POINTER_UP, 'pointerUp'], [TOP_RATE_CHANGE, 'rateChange'], [TOP_RESET, 'reset'], [TOP_SEEKED, 'seeked'], [TOP_SUBMIT, 'submit'], [TOP_TOUCH_CANCEL, 'touchCancel'], [TOP_TOUCH_END, 'touchEnd'], [TOP_TOUCH_START, 'touchStart'], [TOP_VOLUME_CHANGE, 'volumeChange']];
+var nonInteractiveEventTypeNames = [[TOP_ABORT, 'abort'], [TOP_ANIMATION_END, 'animationEnd'], [TOP_ANIMATION_ITERATION, 'animationIteration'], [TOP_ANIMATION_START, 'animationStart'], [TOP_CAN_PLAY, 'canPlay'], [TOP_CAN_PLAY_THROUGH, 'canPlayThrough'], [TOP_DRAG, 'drag'], [TOP_DRAG_ENTER, 'dragEnter'], [TOP_DRAG_EXIT, 'dragExit'], [TOP_DRAG_LEAVE, 'dragLeave'], [TOP_DRAG_OVER, 'dragOver'], [TOP_DURATION_CHANGE, 'durationChange'], [TOP_EMPTIED, 'emptied'], [TOP_ENCRYPTED, 'encrypted'], [TOP_ENDED, 'ended'], [TOP_ERROR, 'error'], [TOP_GOT_POINTER_CAPTURE, 'gotPointerCapture'], [TOP_LOAD, 'load'], [TOP_LOADED_DATA, 'loadedData'], [TOP_LOADED_METADATA, 'loadedMetadata'], [TOP_LOAD_START, 'loadStart'], [TOP_LOST_POINTER_CAPTURE, 'lostPointerCapture'], [TOP_MOUSE_MOVE, 'mouseMove'], [TOP_MOUSE_OUT, 'mouseOut'], [TOP_MOUSE_OVER, 'mouseOver'], [TOP_PLAYING, 'playing'], [TOP_POINTER_MOVE, 'pointerMove'], [TOP_POINTER_OUT, 'pointerOut'], [TOP_POINTER_OVER, 'pointerOver'], [TOP_PROGRESS, 'progress'], [TOP_SCROLL, 'scroll'], [TOP_SEEKING, 'seeking'], [TOP_STALLED, 'stalled'], [TOP_SUSPEND, 'suspend'], [TOP_TIME_UPDATE, 'timeUpdate'], [TOP_TOGGLE, 'toggle'], [TOP_TOUCH_MOVE, 'touchMove'], [TOP_TRANSITION_END, 'transitionEnd'], [TOP_WAITING, 'waiting'], [TOP_WHEEL, 'wheel']];
+
+var eventTypes$4 = {};
+var topLevelEventsToDispatchConfig = {};
+
+function addEventTypeNameToConfig(_ref, isInteractive) {
+ var topEvent = _ref[0],
+ event = _ref[1];
+
+ var capitalizedEvent = event[0].toUpperCase() + event.slice(1);
+ var onEvent = 'on' + capitalizedEvent;
+
+ var type = {
+ phasedRegistrationNames: {
+ bubbled: onEvent,
+ captured: onEvent + 'Capture'
+ },
+ dependencies: [topEvent],
+ isInteractive: isInteractive
+ };
+ eventTypes$4[event] = type;
+ topLevelEventsToDispatchConfig[topEvent] = type;
+}
+
+interactiveEventTypeNames.forEach(function (eventTuple) {
+ addEventTypeNameToConfig(eventTuple, true);
+});
+nonInteractiveEventTypeNames.forEach(function (eventTuple) {
+ addEventTypeNameToConfig(eventTuple, false);
+});
+
+var SimpleEventPlugin = {
+ eventTypes: eventTypes$4,
+
+ isInteractiveTopLevelEventType: function (topLevelType) {
+ var config = topLevelEventsToDispatchConfig[topLevelType];
+ return config !== undefined && config.isInteractive === true;
+ },
+
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var dispatchConfig = topLevelEventsToDispatchConfig[topLevelType];
+ if (!dispatchConfig) {
+ return null;
+ }
+ var EventConstructor = void 0;
+ switch (topLevelType) {
+ case TOP_KEY_PRESS:
+ // Firefox creates a keypress event for function keys too. This removes
+ // the unwanted keypress events. Enter is however both printable and
+ // non-printable. One would expect Tab to be as well (but it isn't).
+ if (getEventCharCode(nativeEvent) === 0) {
+ return null;
+ }
+ /* falls through */
+ case TOP_KEY_DOWN:
+ case TOP_KEY_UP:
+ EventConstructor = SyntheticKeyboardEvent;
+ break;
+ case TOP_BLUR:
+ case TOP_FOCUS:
+ EventConstructor = SyntheticFocusEvent;
+ break;
+ case TOP_CLICK:
+ // Firefox creates a click event on right mouse clicks. This removes the
+ // unwanted click events.
+ if (nativeEvent.button === 2) {
+ return null;
+ }
+ /* falls through */
+ case TOP_AUX_CLICK:
+ case TOP_DOUBLE_CLICK:
+ case TOP_MOUSE_DOWN:
+ case TOP_MOUSE_MOVE:
+ case TOP_MOUSE_UP:
+ // TODO: Disabled elements should not respond to mouse events
+ /* falls through */
+ case TOP_MOUSE_OUT:
+ case TOP_MOUSE_OVER:
+ case TOP_CONTEXT_MENU:
+ EventConstructor = SyntheticMouseEvent;
+ break;
+ case TOP_DRAG:
+ case TOP_DRAG_END:
+ case TOP_DRAG_ENTER:
+ case TOP_DRAG_EXIT:
+ case TOP_DRAG_LEAVE:
+ case TOP_DRAG_OVER:
+ case TOP_DRAG_START:
+ case TOP_DROP:
+ EventConstructor = SyntheticDragEvent;
+ break;
+ case TOP_TOUCH_CANCEL:
+ case TOP_TOUCH_END:
+ case TOP_TOUCH_MOVE:
+ case TOP_TOUCH_START:
+ EventConstructor = SyntheticTouchEvent;
+ break;
+ case TOP_ANIMATION_END:
+ case TOP_ANIMATION_ITERATION:
+ case TOP_ANIMATION_START:
+ EventConstructor = SyntheticAnimationEvent;
+ break;
+ case TOP_TRANSITION_END:
+ EventConstructor = SyntheticTransitionEvent;
+ break;
+ case TOP_SCROLL:
+ EventConstructor = SyntheticUIEvent;
+ break;
+ case TOP_WHEEL:
+ EventConstructor = SyntheticWheelEvent;
+ break;
+ case TOP_COPY:
+ case TOP_CUT:
+ case TOP_PASTE:
+ EventConstructor = SyntheticClipboardEvent;
+ break;
+ case TOP_GOT_POINTER_CAPTURE:
+ case TOP_LOST_POINTER_CAPTURE:
+ case TOP_POINTER_CANCEL:
+ case TOP_POINTER_DOWN:
+ case TOP_POINTER_MOVE:
+ case TOP_POINTER_OUT:
+ case TOP_POINTER_OVER:
+ case TOP_POINTER_UP:
+ EventConstructor = SyntheticPointerEvent;
+ break;
+ default:
+
+ // HTML Events
+ // @see http://www.w3.org/TR/html5/index.html#events-0
+ EventConstructor = SyntheticEvent;
+ break;
+ }
+ var event = EventConstructor.getPooled(dispatchConfig, targetInst, nativeEvent, nativeEventTarget);
+ accumulateTwoPhaseDispatches(event);
+ return event;
+ }
+};
+
+var isInteractiveTopLevelEventType = SimpleEventPlugin.isInteractiveTopLevelEventType;
+
+
+var CALLBACK_BOOKKEEPING_POOL_SIZE = 10;
+var callbackBookkeepingPool = [];
+
+/**
+ * Find the deepest React component completely containing the root of the
+ * passed-in instance (for use when entire React trees are nested within each
+ * other). If React trees are not nested, returns null.
+ */
+function findRootContainerNode(inst) {
+ // TODO: It may be a good idea to cache this to prevent unnecessary DOM
+ // traversal, but caching is difficult to do correctly without using a
+ // mutation observer to listen for all DOM changes.
+ while (inst.return) {
+ inst = inst.return;
+ }
+ if (inst.tag !== HostRoot) {
+ // This can happen if we're in a detached tree.
+ return null;
+ }
+ return inst.stateNode.containerInfo;
+}
+
+// Used to store ancestor hierarchy in top level callback
+function getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst) {
+ if (callbackBookkeepingPool.length) {
+ var instance = callbackBookkeepingPool.pop();
+ instance.topLevelType = topLevelType;
+ instance.nativeEvent = nativeEvent;
+ instance.targetInst = targetInst;
+ return instance;
+ }
+ return {
+ topLevelType: topLevelType,
+ nativeEvent: nativeEvent,
+ targetInst: targetInst,
+ ancestors: []
+ };
+}
+
+function releaseTopLevelCallbackBookKeeping(instance) {
+ instance.topLevelType = null;
+ instance.nativeEvent = null;
+ instance.targetInst = null;
+ instance.ancestors.length = 0;
+ if (callbackBookkeepingPool.length < CALLBACK_BOOKKEEPING_POOL_SIZE) {
+ callbackBookkeepingPool.push(instance);
+ }
+}
+
+function handleTopLevel(bookKeeping) {
+ var targetInst = bookKeeping.targetInst;
+
+ // Loop through the hierarchy, in case there's any nested components.
+ // It's important that we build the array of ancestors before calling any
+ // event handlers, because event handlers can modify the DOM, leading to
+ // inconsistencies with ReactMount's node cache. See #1105.
+ var ancestor = targetInst;
+ do {
+ if (!ancestor) {
+ bookKeeping.ancestors.push(ancestor);
+ break;
+ }
+ var root = findRootContainerNode(ancestor);
+ if (!root) {
+ break;
+ }
+ bookKeeping.ancestors.push(ancestor);
+ ancestor = getClosestInstanceFromNode(root);
+ } while (ancestor);
+
+ for (var i = 0; i < bookKeeping.ancestors.length; i++) {
+ targetInst = bookKeeping.ancestors[i];
+ runExtractedEventsInBatch(bookKeeping.topLevelType, targetInst, bookKeeping.nativeEvent, getEventTarget(bookKeeping.nativeEvent));
+ }
+}
+
+// TODO: can we stop exporting these?
+var _enabled = true;
+
+function setEnabled(enabled) {
+ _enabled = !!enabled;
+}
+
+function isEnabled() {
+ return _enabled;
+}
+
+/**
+ * Traps top-level events by using event bubbling.
+ *
+ * @param {number} topLevelType Number from `TopLevelEventTypes`.
+ * @param {object} element Element on which to attach listener.
+ * @return {?object} An object with a remove function which will forcefully
+ * remove the listener.
+ * @internal
+ */
+function trapBubbledEvent(topLevelType, element) {
+ if (!element) {
+ return null;
+ }
+ var dispatch = isInteractiveTopLevelEventType(topLevelType) ? dispatchInteractiveEvent : dispatchEvent;
+
+ addEventBubbleListener(element, getRawEventName(topLevelType),
+ // Check if interactive and wrap in interactiveUpdates
+ dispatch.bind(null, topLevelType));
+}
+
+/**
+ * Traps a top-level event by using event capturing.
+ *
+ * @param {number} topLevelType Number from `TopLevelEventTypes`.
+ * @param {object} element Element on which to attach listener.
+ * @return {?object} An object with a remove function which will forcefully
+ * remove the listener.
+ * @internal
+ */
+function trapCapturedEvent(topLevelType, element) {
+ if (!element) {
+ return null;
+ }
+ var dispatch = isInteractiveTopLevelEventType(topLevelType) ? dispatchInteractiveEvent : dispatchEvent;
+
+ addEventCaptureListener(element, getRawEventName(topLevelType),
+ // Check if interactive and wrap in interactiveUpdates
+ dispatch.bind(null, topLevelType));
+}
+
+function dispatchInteractiveEvent(topLevelType, nativeEvent) {
+ interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
+}
+
+function dispatchEvent(topLevelType, nativeEvent) {
+ if (!_enabled) {
+ return;
+ }
+
+ var nativeEventTarget = getEventTarget(nativeEvent);
+ var targetInst = getClosestInstanceFromNode(nativeEventTarget);
+ if (targetInst !== null && typeof targetInst.tag === 'number' && !isFiberMounted(targetInst)) {
+ // If we get an event (ex: img onload) before committing that
+ // component's mount, ignore it for now (that is, treat it as if it was an
+ // event on a non-React tree). We might also consider queueing events and
+ // dispatching them after the mount.
+ targetInst = null;
+ }
+
+ var bookKeeping = getTopLevelCallbackBookKeeping(topLevelType, nativeEvent, targetInst);
+
+ try {
+ // Event queue being processed in the same cycle allows
+ // `preventDefault`.
+ batchedUpdates(handleTopLevel, bookKeeping);
+ } finally {
+ releaseTopLevelCallbackBookKeeping(bookKeeping);
+ }
+}
+
+/**
+ * Summary of `ReactBrowserEventEmitter` event handling:
+ *
+ * - Top-level delegation is used to trap most native browser events. This
+ * may only occur in the main thread and is the responsibility of
+ * ReactDOMEventListener, which is injected and can therefore support
+ * pluggable event sources. This is the only work that occurs in the main
+ * thread.
+ *
+ * - We normalize and de-duplicate events to account for browser quirks. This
+ * may be done in the worker thread.
+ *
+ * - Forward these native events (with the associated top-level type used to
+ * trap it) to `EventPluginHub`, which in turn will ask plugins if they want
+ * to extract any synthetic events.
+ *
+ * - The `EventPluginHub` will then process each event by annotating them with
+ * "dispatches", a sequence of listeners and IDs that care about that event.
+ *
+ * - The `EventPluginHub` then dispatches the events.
+ *
+ * Overview of React and the event system:
+ *
+ * +------------+ .
+ * | DOM | .
+ * +------------+ .
+ * | .
+ * v .
+ * +------------+ .
+ * | ReactEvent | .
+ * | Listener | .
+ * +------------+ . +-----------+
+ * | . +--------+|SimpleEvent|
+ * | . | |Plugin |
+ * +-----|------+ . v +-----------+
+ * | | | . +--------------+ +------------+
+ * | +-----------.--->|EventPluginHub| | Event |
+ * | | . | | +-----------+ | Propagators|
+ * | ReactEvent | . | | |TapEvent | |------------|
+ * | Emitter | . | |<---+|Plugin | |other plugin|
+ * | | . | | +-----------+ | utilities |
+ * | +-----------.--->| | +------------+
+ * | | | . +--------------+
+ * +-----|------+ . ^ +-----------+
+ * | . | |Enter/Leave|
+ * + . +-------+|Plugin |
+ * +-------------+ . +-----------+
+ * | application | .
+ * |-------------| .
+ * | | .
+ * | | .
+ * +-------------+ .
+ * .
+ * React Core . General Purpose Event Plugin System
+ */
+
+var alreadyListeningTo = {};
+var reactTopListenersCounter = 0;
+
+/**
+ * To ensure no conflicts with other potential React instances on the page
+ */
+var topListenersIDKey = '_reactListenersID' + ('' + Math.random()).slice(2);
+
+function getListeningForDocument(mountAt) {
+ // In IE8, `mountAt` is a host object and doesn't have `hasOwnProperty`
+ // directly.
+ if (!Object.prototype.hasOwnProperty.call(mountAt, topListenersIDKey)) {
+ mountAt[topListenersIDKey] = reactTopListenersCounter++;
+ alreadyListeningTo[mountAt[topListenersIDKey]] = {};
+ }
+ return alreadyListeningTo[mountAt[topListenersIDKey]];
+}
+
+/**
+ * We listen for bubbled touch events on the document object.
+ *
+ * Firefox v8.01 (and possibly others) exhibited strange behavior when
+ * mounting `onmousemove` events at some node that was not the document
+ * element. The symptoms were that if your mouse is not moving over something
+ * contained within that mount point (for example on the background) the
+ * top-level listeners for `onmousemove` won't be called. However, if you
+ * register the `mousemove` on the document object, then it will of course
+ * catch all `mousemove`s. This along with iOS quirks, justifies restricting
+ * top-level listeners to the document object only, at least for these
+ * movement types of events and possibly all events.
+ *
+ * @see http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
+ *
+ * Also, `keyup`/`keypress`/`keydown` do not bubble to the window on IE, but
+ * they bubble to document.
+ *
+ * @param {string} registrationName Name of listener (e.g. `onClick`).
+ * @param {object} mountAt Container where to mount the listener
+ */
+function listenTo(registrationName, mountAt) {
+ var isListening = getListeningForDocument(mountAt);
+ var dependencies = registrationNameDependencies[registrationName];
+
+ for (var i = 0; i < dependencies.length; i++) {
+ var dependency = dependencies[i];
+ if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
+ switch (dependency) {
+ case TOP_SCROLL:
+ trapCapturedEvent(TOP_SCROLL, mountAt);
+ break;
+ case TOP_FOCUS:
+ case TOP_BLUR:
+ trapCapturedEvent(TOP_FOCUS, mountAt);
+ trapCapturedEvent(TOP_BLUR, mountAt);
+ // We set the flag for a single dependency later in this function,
+ // but this ensures we mark both as attached rather than just one.
+ isListening[TOP_BLUR] = true;
+ isListening[TOP_FOCUS] = true;
+ break;
+ case TOP_CANCEL:
+ case TOP_CLOSE:
+ if (isEventSupported(getRawEventName(dependency))) {
+ trapCapturedEvent(dependency, mountAt);
+ }
+ break;
+ case TOP_INVALID:
+ case TOP_SUBMIT:
+ case TOP_RESET:
+ // We listen to them on the target DOM elements.
+ // Some of them bubble so we don't want them to fire twice.
+ break;
+ default:
+ // By default, listen on the top level to all non-media events.
+ // Media events don't bubble so adding the listener wouldn't do anything.
+ var isMediaEvent = mediaEventTypes.indexOf(dependency) !== -1;
+ if (!isMediaEvent) {
+ trapBubbledEvent(dependency, mountAt);
+ }
+ break;
+ }
+ isListening[dependency] = true;
+ }
+ }
+}
+
+function isListeningToAllDependencies(registrationName, mountAt) {
+ var isListening = getListeningForDocument(mountAt);
+ var dependencies = registrationNameDependencies[registrationName];
+ for (var i = 0; i < dependencies.length; i++) {
+ var dependency = dependencies[i];
+ if (!(isListening.hasOwnProperty(dependency) && isListening[dependency])) {
+ return false;
+ }
+ }
+ return true;
+}
+
+function getActiveElement(doc) {
+ doc = doc || (typeof document !== 'undefined' ? document : undefined);
+ if (typeof doc === 'undefined') {
+ return null;
+ }
+ try {
+ return doc.activeElement || doc.body;
+ } catch (e) {
+ return doc.body;
+ }
+}
+
+/**
+ * Given any node return the first leaf node without children.
+ *
+ * @param {DOMElement|DOMTextNode} node
+ * @return {DOMElement|DOMTextNode}
+ */
+function getLeafNode(node) {
+ while (node && node.firstChild) {
+ node = node.firstChild;
+ }
+ return node;
+}
+
+/**
+ * Get the next sibling within a container. This will walk up the
+ * DOM if a node's siblings have been exhausted.
+ *
+ * @param {DOMElement|DOMTextNode} node
+ * @return {?DOMElement|DOMTextNode}
+ */
+function getSiblingNode(node) {
+ while (node) {
+ if (node.nextSibling) {
+ return node.nextSibling;
+ }
+ node = node.parentNode;
+ }
+}
+
+/**
+ * Get object describing the nodes which contain characters at offset.
+ *
+ * @param {DOMElement|DOMTextNode} root
+ * @param {number} offset
+ * @return {?object}
+ */
+function getNodeForCharacterOffset(root, offset) {
+ var node = getLeafNode(root);
+ var nodeStart = 0;
+ var nodeEnd = 0;
+
+ while (node) {
+ if (node.nodeType === TEXT_NODE) {
+ nodeEnd = nodeStart + node.textContent.length;
+
+ if (nodeStart <= offset && nodeEnd >= offset) {
+ return {
+ node: node,
+ offset: offset - nodeStart
+ };
+ }
+
+ nodeStart = nodeEnd;
+ }
+
+ node = getLeafNode(getSiblingNode(node));
+ }
+}
+
+/**
+ * @param {DOMElement} outerNode
+ * @return {?object}
+ */
+function getOffsets(outerNode) {
+ var ownerDocument = outerNode.ownerDocument;
+
+ var win = ownerDocument && ownerDocument.defaultView || window;
+ var selection = win.getSelection && win.getSelection();
+
+ if (!selection || selection.rangeCount === 0) {
+ return null;
+ }
+
+ var anchorNode = selection.anchorNode,
+ anchorOffset = selection.anchorOffset,
+ focusNode = selection.focusNode,
+ focusOffset = selection.focusOffset;
+
+ // In Firefox, anchorNode and focusNode can be "anonymous divs", e.g. the
+ // up/down buttons on an <input type="number">. Anonymous divs do not seem to
+ // expose properties, triggering a "Permission denied error" if any of its
+ // properties are accessed. The only seemingly possible way to avoid erroring
+ // is to access a property that typically works for non-anonymous divs and
+ // catch any error that may otherwise arise. See
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=208427
+
+ try {
+ /* eslint-disable no-unused-expressions */
+ anchorNode.nodeType;
+ focusNode.nodeType;
+ /* eslint-enable no-unused-expressions */
+ } catch (e) {
+ return null;
+ }
+
+ return getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset);
+}
+
+/**
+ * Returns {start, end} where `start` is the character/codepoint index of
+ * (anchorNode, anchorOffset) within the textContent of `outerNode`, and
+ * `end` is the index of (focusNode, focusOffset).
+ *
+ * Returns null if you pass in garbage input but we should probably just crash.
+ *
+ * Exported only for testing.
+ */
+function getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset) {
+ var length = 0;
+ var start = -1;
+ var end = -1;
+ var indexWithinAnchor = 0;
+ var indexWithinFocus = 0;
+ var node = outerNode;
+ var parentNode = null;
+
+ outer: while (true) {
+ var next = null;
+
+ while (true) {
+ if (node === anchorNode && (anchorOffset === 0 || node.nodeType === TEXT_NODE)) {
+ start = length + anchorOffset;
+ }
+ if (node === focusNode && (focusOffset === 0 || node.nodeType === TEXT_NODE)) {
+ end = length + focusOffset;
+ }
+
+ if (node.nodeType === TEXT_NODE) {
+ length += node.nodeValue.length;
+ }
+
+ if ((next = node.firstChild) === null) {
+ break;
+ }
+ // Moving from `node` to its first child `next`.
+ parentNode = node;
+ node = next;
+ }
+
+ while (true) {
+ if (node === outerNode) {
+ // If `outerNode` has children, this is always the second time visiting
+ // it. If it has no children, this is still the first loop, and the only
+ // valid selection is anchorNode and focusNode both equal to this node
+ // and both offsets 0, in which case we will have handled above.
+ break outer;
+ }
+ if (parentNode === anchorNode && ++indexWithinAnchor === anchorOffset) {
+ start = length;
+ }
+ if (parentNode === focusNode && ++indexWithinFocus === focusOffset) {
+ end = length;
+ }
+ if ((next = node.nextSibling) !== null) {
+ break;
+ }
+ node = parentNode;
+ parentNode = node.parentNode;
+ }
+
+ // Moving from `node` to its next sibling `next`.
+ node = next;
+ }
+
+ if (start === -1 || end === -1) {
+ // This should never happen. (Would happen if the anchor/focus nodes aren't
+ // actually inside the passed-in node.)
+ return null;
+ }
+
+ return {
+ start: start,
+ end: end
+ };
+}
+
+/**
+ * In modern non-IE browsers, we can support both forward and backward
+ * selections.
+ *
+ * Note: IE10+ supports the Selection object, but it does not support
+ * the `extend` method, which means that even in modern IE, it's not possible
+ * to programmatically create a backward selection. Thus, for all IE
+ * versions, we use the old IE API to create our selections.
+ *
+ * @param {DOMElement|DOMTextNode} node
+ * @param {object} offsets
+ */
+function setOffsets(node, offsets) {
+ var doc = node.ownerDocument || document;
+ var win = doc && doc.defaultView || window;
+
+ // Edge fails with "Object expected" in some scenarios.
+ // (For instance: TinyMCE editor used in a list component that supports pasting to add more,
+ // fails when pasting 100+ items)
+ if (!win.getSelection) {
+ return;
+ }
+
+ var selection = win.getSelection();
+ var length = node.textContent.length;
+ var start = Math.min(offsets.start, length);
+ var end = offsets.end === undefined ? start : Math.min(offsets.end, length);
+
+ // IE 11 uses modern selection, but doesn't support the extend method.
+ // Flip backward selections, so we can set with a single range.
+ if (!selection.extend && start > end) {
+ var temp = end;
+ end = start;
+ start = temp;
+ }
+
+ var startMarker = getNodeForCharacterOffset(node, start);
+ var endMarker = getNodeForCharacterOffset(node, end);
+
+ if (startMarker && endMarker) {
+ if (selection.rangeCount === 1 && selection.anchorNode === startMarker.node && selection.anchorOffset === startMarker.offset && selection.focusNode === endMarker.node && selection.focusOffset === endMarker.offset) {
+ return;
+ }
+ var range = doc.createRange();
+ range.setStart(startMarker.node, startMarker.offset);
+ selection.removeAllRanges();
+
+ if (start > end) {
+ selection.addRange(range);
+ selection.extend(endMarker.node, endMarker.offset);
+ } else {
+ range.setEnd(endMarker.node, endMarker.offset);
+ selection.addRange(range);
+ }
+ }
+}
+
+function isTextNode(node) {
+ return node && node.nodeType === TEXT_NODE;
+}
+
+function containsNode(outerNode, innerNode) {
+ if (!outerNode || !innerNode) {
+ return false;
+ } else if (outerNode === innerNode) {
+ return true;
+ } else if (isTextNode(outerNode)) {
+ return false;
+ } else if (isTextNode(innerNode)) {
+ return containsNode(outerNode, innerNode.parentNode);
+ } else if ('contains' in outerNode) {
+ return outerNode.contains(innerNode);
+ } else if (outerNode.compareDocumentPosition) {
+ return !!(outerNode.compareDocumentPosition(innerNode) & 16);
+ } else {
+ return false;
+ }
+}
+
+function isInDocument(node) {
+ return node && node.ownerDocument && containsNode(node.ownerDocument.documentElement, node);
+}
+
+function isSameOriginFrame(iframe) {
+ try {
+ // Accessing the contentDocument of a HTMLIframeElement can cause the browser
+ // to throw, e.g. if it has a cross-origin src attribute.
+ // Safari will show an error in the console when the access results in "Blocked a frame with origin". e.g:
+ // iframe.contentDocument.defaultView;
+ // A safety way is to access one of the cross origin properties: Window or Location
+ // Which might result in "SecurityError" DOM Exception and it is compatible to Safari.
+ // https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl
+
+ return typeof iframe.contentWindow.location.href === 'string';
+ } catch (err) {
+ return false;
+ }
+}
+
+function getActiveElementDeep() {
+ var win = window;
+ var element = getActiveElement();
+ while (element instanceof win.HTMLIFrameElement) {
+ if (isSameOriginFrame(element)) {
+ win = element.contentWindow;
+ } else {
+ return element;
+ }
+ element = getActiveElement(win.document);
+ }
+ return element;
+}
+
+/**
+ * @ReactInputSelection: React input selection module. Based on Selection.js,
+ * but modified to be suitable for react and has a couple of bug fixes (doesn't
+ * assume buttons have range selections allowed).
+ * Input selection module for React.
+ */
+
+/**
+ * @hasSelectionCapabilities: we get the element types that support selection
+ * from https://html.spec.whatwg.org/#do-not-apply, looking at `selectionStart`
+ * and `selectionEnd` rows.
+ */
+function hasSelectionCapabilities(elem) {
+ var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
+ return nodeName && (nodeName === 'input' && (elem.type === 'text' || elem.type === 'search' || elem.type === 'tel' || elem.type === 'url' || elem.type === 'password') || nodeName === 'textarea' || elem.contentEditable === 'true');
+}
+
+function getSelectionInformation() {
+ var focusedElem = getActiveElementDeep();
+ return {
+ focusedElem: focusedElem,
+ selectionRange: hasSelectionCapabilities(focusedElem) ? getSelection$1(focusedElem) : null
+ };
+}
+
+/**
+ * @restoreSelection: If any selection information was potentially lost,
+ * restore it. This is useful when performing operations that could remove dom
+ * nodes and place them back in, resulting in focus being lost.
+ */
+function restoreSelection(priorSelectionInformation) {
+ var curFocusedElem = getActiveElementDeep();
+ var priorFocusedElem = priorSelectionInformation.focusedElem;
+ var priorSelectionRange = priorSelectionInformation.selectionRange;
+ if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
+ if (priorSelectionRange !== null && hasSelectionCapabilities(priorFocusedElem)) {
+ setSelection(priorFocusedElem, priorSelectionRange);
+ }
+
+ // Focusing a node can change the scroll position, which is undesirable
+ var ancestors = [];
+ var ancestor = priorFocusedElem;
+ while (ancestor = ancestor.parentNode) {
+ if (ancestor.nodeType === ELEMENT_NODE) {
+ ancestors.push({
+ element: ancestor,
+ left: ancestor.scrollLeft,
+ top: ancestor.scrollTop
+ });
+ }
+ }
+
+ if (typeof priorFocusedElem.focus === 'function') {
+ priorFocusedElem.focus();
+ }
+
+ for (var i = 0; i < ancestors.length; i++) {
+ var info = ancestors[i];
+ info.element.scrollLeft = info.left;
+ info.element.scrollTop = info.top;
+ }
+ }
+}
+
+/**
+ * @getSelection: Gets the selection bounds of a focused textarea, input or
+ * contentEditable node.
+ * -@input: Look up selection bounds of this input
+ * -@return {start: selectionStart, end: selectionEnd}
+ */
+function getSelection$1(input) {
+ var selection = void 0;
+
+ if ('selectionStart' in input) {
+ // Modern browser with input or textarea.
+ selection = {
+ start: input.selectionStart,
+ end: input.selectionEnd
+ };
+ } else {
+ // Content editable or old IE textarea.
+ selection = getOffsets(input);
+ }
+
+ return selection || { start: 0, end: 0 };
+}
+
+/**
+ * @setSelection: Sets the selection bounds of a textarea or input and focuses
+ * the input.
+ * -@input Set selection bounds of this input or textarea
+ * -@offsets Object of same form that is returned from get*
+ */
+function setSelection(input, offsets) {
+ var start = offsets.start,
+ end = offsets.end;
+
+ if (end === undefined) {
+ end = start;
+ }
+
+ if ('selectionStart' in input) {
+ input.selectionStart = start;
+ input.selectionEnd = Math.min(end, input.value.length);
+ } else {
+ setOffsets(input, offsets);
+ }
+}
+
+var skipSelectionChangeEvent = canUseDOM && 'documentMode' in document && document.documentMode <= 11;
+
+var eventTypes$3 = {
+ select: {
+ phasedRegistrationNames: {
+ bubbled: 'onSelect',
+ captured: 'onSelectCapture'
+ },
+ dependencies: [TOP_BLUR, TOP_CONTEXT_MENU, TOP_DRAG_END, TOP_FOCUS, TOP_KEY_DOWN, TOP_KEY_UP, TOP_MOUSE_DOWN, TOP_MOUSE_UP, TOP_SELECTION_CHANGE]
+ }
+};
+
+var activeElement$1 = null;
+var activeElementInst$1 = null;
+var lastSelection = null;
+var mouseDown = false;
+
+/**
+ * Get an object which is a unique representation of the current selection.
+ *
+ * The return value will not be consistent across nodes or browsers, but
+ * two identical selections on the same node will return identical objects.
+ *
+ * @param {DOMElement} node
+ * @return {object}
+ */
+function getSelection(node) {
+ if ('selectionStart' in node && hasSelectionCapabilities(node)) {
+ return {
+ start: node.selectionStart,
+ end: node.selectionEnd
+ };
+ } else {
+ var win = node.ownerDocument && node.ownerDocument.defaultView || window;
+ var selection = win.getSelection();
+ return {
+ anchorNode: selection.anchorNode,
+ anchorOffset: selection.anchorOffset,
+ focusNode: selection.focusNode,
+ focusOffset: selection.focusOffset
+ };
+ }
+}
+
+/**
+ * Get document associated with the event target.
+ *
+ * @param {object} nativeEventTarget
+ * @return {Document}
+ */
+function getEventTargetDocument(eventTarget) {
+ return eventTarget.window === eventTarget ? eventTarget.document : eventTarget.nodeType === DOCUMENT_NODE ? eventTarget : eventTarget.ownerDocument;
+}
+
+/**
+ * Poll selection to see whether it's changed.
+ *
+ * @param {object} nativeEvent
+ * @param {object} nativeEventTarget
+ * @return {?SyntheticEvent}
+ */
+function constructSelectEvent(nativeEvent, nativeEventTarget) {
+ // Ensure we have the right element, and that the user is not dragging a
+ // selection (this matches native `select` event behavior). In HTML5, select
+ // fires only on input and textarea thus if there's no focused element we
+ // won't dispatch.
+ var doc = getEventTargetDocument(nativeEventTarget);
+
+ if (mouseDown || activeElement$1 == null || activeElement$1 !== getActiveElement(doc)) {
+ return null;
+ }
+
+ // Only fire when selection has actually changed.
+ var currentSelection = getSelection(activeElement$1);
+ if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
+ lastSelection = currentSelection;
+
+ var syntheticEvent = SyntheticEvent.getPooled(eventTypes$3.select, activeElementInst$1, nativeEvent, nativeEventTarget);
+
+ syntheticEvent.type = 'select';
+ syntheticEvent.target = activeElement$1;
+
+ accumulateTwoPhaseDispatches(syntheticEvent);
+
+ return syntheticEvent;
+ }
+
+ return null;
+}
+
+/**
+ * This plugin creates an `onSelect` event that normalizes select events
+ * across form elements.
+ *
+ * Supported elements are:
+ * - input (see `isTextInputElement`)
+ * - textarea
+ * - contentEditable
+ *
+ * This differs from native browser implementations in the following ways:
+ * - Fires on contentEditable fields as well as inputs.
+ * - Fires for collapsed selection.
+ * - Fires after user input.
+ */
+var SelectEventPlugin = {
+ eventTypes: eventTypes$3,
+
+ extractEvents: function (topLevelType, targetInst, nativeEvent, nativeEventTarget) {
+ var doc = getEventTargetDocument(nativeEventTarget);
+ // Track whether all listeners exists for this plugin. If none exist, we do
+ // not extract events. See #3639.
+ if (!doc || !isListeningToAllDependencies('onSelect', doc)) {
+ return null;
+ }
+
+ var targetNode = targetInst ? getNodeFromInstance$1(targetInst) : window;
+
+ switch (topLevelType) {
+ // Track the input node that has focus.
+ case TOP_FOCUS:
+ if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') {
+ activeElement$1 = targetNode;
+ activeElementInst$1 = targetInst;
+ lastSelection = null;
+ }
+ break;
+ case TOP_BLUR:
+ activeElement$1 = null;
+ activeElementInst$1 = null;
+ lastSelection = null;
+ break;
+ // Don't fire the event while the user is dragging. This matches the
+ // semantics of the native select event.
+ case TOP_MOUSE_DOWN:
+ mouseDown = true;
+ break;
+ case TOP_CONTEXT_MENU:
+ case TOP_MOUSE_UP:
+ case TOP_DRAG_END:
+ mouseDown = false;
+ return constructSelectEvent(nativeEvent, nativeEventTarget);
+ // Chrome and IE fire non-standard event when selection is changed (and
+ // sometimes when it hasn't). IE's event fires out of order with respect
+ // to key and input events on deletion, so we discard it.
+ //
+ // Firefox doesn't support selectionchange, so check selection status
+ // after each key entry. The selection changes after keydown and before
+ // keyup, but we check on keydown as well in the case of holding down a
+ // key, when multiple keydown events are fired but only one keyup is.
+ // This is also our approach for IE handling, for the reason above.
+ case TOP_SELECTION_CHANGE:
+ if (skipSelectionChangeEvent) {
+ break;
+ }
+ // falls through
+ case TOP_KEY_DOWN:
+ case TOP_KEY_UP:
+ return constructSelectEvent(nativeEvent, nativeEventTarget);
+ }
+
+ return null;
+ }
+};
+
+/**
+ * Inject modules for resolving DOM hierarchy and plugin ordering.
+ */
+injection.injectEventPluginOrder(DOMEventPluginOrder);
+setComponentTree(getFiberCurrentPropsFromNode$1, getInstanceFromNode$1, getNodeFromInstance$1);
+
+/**
+ * Some important event plugins included by default (without having to require
+ * them).
+ */
+injection.injectEventPluginsByName({
+ SimpleEventPlugin: SimpleEventPlugin,
+ EnterLeaveEventPlugin: EnterLeaveEventPlugin,
+ ChangeEventPlugin: ChangeEventPlugin,
+ SelectEventPlugin: SelectEventPlugin,
+ BeforeInputEventPlugin: BeforeInputEventPlugin
+});
+
+function flattenChildren(children) {
+ var content = '';
+
+ // Flatten children. We'll warn if they are invalid
+ // during validateProps() which runs for hydration too.
+ // Note that this would throw on non-element objects.
+ // Elements are stringified (which is normally irrelevant
+ // but matters for <fbt>).
+ React.Children.forEach(children, function (child) {
+ if (child == null) {
+ return;
+ }
+ content += child;
+ // Note: we don't warn about invalid children here.
+ // Instead, this is done separately below so that
+ // it happens during the hydration codepath too.
+ });
+
+ return content;
+}
+
+/**
+ * Implements an <option> host component that warns when `selected` is set.
+ */
+
+
+
+function postMountWrapper$1(element, props) {
+ // value="" should make a value attribute (#6219)
+ if (props.value != null) {
+ element.setAttribute('value', toString(getToStringValue(props.value)));
+ }
+}
+
+function getHostProps$1(element, props) {
+ var hostProps = _assign({ children: undefined }, props);
+ var content = flattenChildren(props.children);
+
+ if (content) {
+ hostProps.children = content;
+ }
+
+ return hostProps;
+}
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+function updateOptions(node, multiple, propValue, setDefaultSelected) {
+ var options = node.options;
+
+ if (multiple) {
+ var selectedValues = propValue;
+ var selectedValue = {};
+ for (var i = 0; i < selectedValues.length; i++) {
+ // Prefix to avoid chaos with special keys.
+ selectedValue['$' + selectedValues[i]] = true;
+ }
+ for (var _i = 0; _i < options.length; _i++) {
+ var selected = selectedValue.hasOwnProperty('$' + options[_i].value);
+ if (options[_i].selected !== selected) {
+ options[_i].selected = selected;
+ }
+ if (selected && setDefaultSelected) {
+ options[_i].defaultSelected = true;
+ }
+ }
+ } else {
+ // Do not set `select.value` as exact behavior isn't consistent across all
+ // browsers for all cases.
+ var _selectedValue = toString(getToStringValue(propValue));
+ var defaultSelected = null;
+ for (var _i2 = 0; _i2 < options.length; _i2++) {
+ if (options[_i2].value === _selectedValue) {
+ options[_i2].selected = true;
+ if (setDefaultSelected) {
+ options[_i2].defaultSelected = true;
+ }
+ return;
+ }
+ if (defaultSelected === null && !options[_i2].disabled) {
+ defaultSelected = options[_i2];
+ }
+ }
+ if (defaultSelected !== null) {
+ defaultSelected.selected = true;
+ }
+ }
+}
+
+/**
+ * Implements a <select> host component that allows optionally setting the
+ * props `value` and `defaultValue`. If `multiple` is false, the prop must be a
+ * stringable. If `multiple` is true, the prop must be an array of stringables.
+ *
+ * If `value` is not supplied (or null/undefined), user actions that change the
+ * selected option will trigger updates to the rendered options.
+ *
+ * If it is supplied (and not null/undefined), the rendered options will not
+ * update in response to user actions. Instead, the `value` prop must change in
+ * order for the rendered options to update.
+ *
+ * If `defaultValue` is provided, any options with the supplied values will be
+ * selected.
+ */
+
+function getHostProps$2(element, props) {
+ return _assign({}, props, {
+ value: undefined
+ });
+}
+
+function initWrapperState$1(element, props) {
+ var node = element;
+ node._wrapperState = {
+ wasMultiple: !!props.multiple
+ };
+
+
+}
+
+function postMountWrapper$2(element, props) {
+ var node = element;
+ node.multiple = !!props.multiple;
+ var value = props.value;
+ if (value != null) {
+ updateOptions(node, !!props.multiple, value, false);
+ } else if (props.defaultValue != null) {
+ updateOptions(node, !!props.multiple, props.defaultValue, true);
+ }
+}
+
+function postUpdateWrapper(element, props) {
+ var node = element;
+ var wasMultiple = node._wrapperState.wasMultiple;
+ node._wrapperState.wasMultiple = !!props.multiple;
+
+ var value = props.value;
+ if (value != null) {
+ updateOptions(node, !!props.multiple, value, false);
+ } else if (wasMultiple !== !!props.multiple) {
+ // For simplicity, reapply `defaultValue` if `multiple` is toggled.
+ if (props.defaultValue != null) {
+ updateOptions(node, !!props.multiple, props.defaultValue, true);
+ } else {
+ // Revert the select back to its default unselected state.
+ updateOptions(node, !!props.multiple, props.multiple ? [] : '', false);
+ }
+ }
+}
+
+function restoreControlledState$2(element, props) {
+ var node = element;
+ var value = props.value;
+
+ if (value != null) {
+ updateOptions(node, !!props.multiple, value, false);
+ }
+}
+
+/**
+ * Implements a <textarea> host component that allows setting `value`, and
+ * `defaultValue`. This differs from the traditional DOM API because value is
+ * usually set as PCDATA children.
+ *
+ * If `value` is not supplied (or null/undefined), user actions that affect the
+ * value will trigger updates to the element.
+ *
+ * If `value` is supplied (and not null/undefined), the rendered element will
+ * not trigger updates to the element. Instead, the `value` prop must change in
+ * order for the rendered element to be updated.
+ *
+ * The rendered element will be initialized with an empty value, the prop
+ * `defaultValue` if specified, or the children content (deprecated).
+ */
+
+function getHostProps$3(element, props) {
+ var node = element;
+ !(props.dangerouslySetInnerHTML == null) ? reactProdInvariant('91') : void 0;
+
+ // Always set children to the same thing. In IE9, the selection range will
+ // get reset if `textContent` is mutated. We could add a check in setTextContent
+ // to only set the value if/when the value differs from the node value (which would
+ // completely solve this IE9 bug), but Sebastian+Sophie seemed to like this
+ // solution. The value can be a boolean or object so that's why it's forced
+ // to be a string.
+ var hostProps = _assign({}, props, {
+ value: undefined,
+ defaultValue: undefined,
+ children: toString(node._wrapperState.initialValue)
+ });
+
+ return hostProps;
+}
+
+function initWrapperState$2(element, props) {
+ var node = element;
+ var initialValue = props.value;
+
+ // Only bother fetching default value if we're going to use it
+ if (initialValue == null) {
+ var defaultValue = props.defaultValue;
+ // TODO (yungsters): Remove support for children content in <textarea>.
+ var children = props.children;
+ if (children != null) {
+ !(defaultValue == null) ? reactProdInvariant('92') : void 0;
+ if (Array.isArray(children)) {
+ !(children.length <= 1) ? reactProdInvariant('93') : void 0;
+ children = children[0];
+ }
+
+ defaultValue = children;
+ }
+ if (defaultValue == null) {
+ defaultValue = '';
+ }
+ initialValue = defaultValue;
+ }
+
+ node._wrapperState = {
+ initialValue: getToStringValue(initialValue)
+ };
+}
+
+function updateWrapper$1(element, props) {
+ var node = element;
+ var value = getToStringValue(props.value);
+ var defaultValue = getToStringValue(props.defaultValue);
+ if (value != null) {
+ // Cast `value` to a string to ensure the value is set correctly. While
+ // browsers typically do this as necessary, jsdom doesn't.
+ var newValue = toString(value);
+ // To avoid side effects (such as losing text selection), only set value if changed
+ if (newValue !== node.value) {
+ node.value = newValue;
+ }
+ if (props.defaultValue == null && node.defaultValue !== newValue) {
+ node.defaultValue = newValue;
+ }
+ }
+ if (defaultValue != null) {
+ node.defaultValue = toString(defaultValue);
+ }
+}
+
+function postMountWrapper$3(element, props) {
+ var node = element;
+ // This is in postMount because we need access to the DOM node, which is not
+ // available until after the component has mounted.
+ var textContent = node.textContent;
+
+ // Only set node.value if textContent is equal to the expected
+ // initial value. In IE10/IE11 there is a bug where the placeholder attribute
+ // will populate textContent as well.
+ // https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
+ if (textContent === node._wrapperState.initialValue) {
+ node.value = textContent;
+ }
+}
+
+function restoreControlledState$3(element, props) {
+ // DOM component is still mounted; update
+ updateWrapper$1(element, props);
+}
+
+var HTML_NAMESPACE$1 = 'http://www.w3.org/1999/xhtml';
+var MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
+var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
+
+var Namespaces = {
+ html: HTML_NAMESPACE$1,
+ mathml: MATH_NAMESPACE,
+ svg: SVG_NAMESPACE
+};
+
+// Assumes there is no parent namespace.
+function getIntrinsicNamespace(type) {
+ switch (type) {
+ case 'svg':
+ return SVG_NAMESPACE;
+ case 'math':
+ return MATH_NAMESPACE;
+ default:
+ return HTML_NAMESPACE$1;
+ }
+}
+
+function getChildNamespace(parentNamespace, type) {
+ if (parentNamespace == null || parentNamespace === HTML_NAMESPACE$1) {
+ // No (or default) parent namespace: potential entry point.
+ return getIntrinsicNamespace(type);
+ }
+ if (parentNamespace === SVG_NAMESPACE && type === 'foreignObject') {
+ // We're leaving SVG.
+ return HTML_NAMESPACE$1;
+ }
+ // By default, pass namespace below.
+ return parentNamespace;
+}
+
+/* globals MSApp */
+
+/**
+ * Create a function which has 'unsafe' privileges (required by windows8 apps)
+ */
+var createMicrosoftUnsafeLocalFunction = function (func) {
+ if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
+ return function (arg0, arg1, arg2, arg3) {
+ MSApp.execUnsafeLocalFunction(function () {
+ return func(arg0, arg1, arg2, arg3);
+ });
+ };
+ } else {
+ return func;
+ }
+};
+
+// SVG temp container for IE lacking innerHTML
+var reusableSVGContainer = void 0;
+
+/**
+ * Set the innerHTML property of a node
+ *
+ * @param {DOMElement} node
+ * @param {string} html
+ * @internal
+ */
+var setInnerHTML = createMicrosoftUnsafeLocalFunction(function (node, html) {
+ // IE does not have innerHTML for SVG nodes, so instead we inject the
+ // new markup in a temp node and then move the child nodes across into
+ // the target node
+
+ if (node.namespaceURI === Namespaces.svg && !('innerHTML' in node)) {
+ reusableSVGContainer = reusableSVGContainer || document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ reusableSVGContainer.innerHTML = '<svg>' + html + '</svg>';
+ var svgNode = reusableSVGContainer.firstChild;
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ while (svgNode.firstChild) {
+ node.appendChild(svgNode.firstChild);
+ }
+ } else {
+ node.innerHTML = html;
+ }
+});
+
+/**
+ * Set the textContent property of a node. For text updates, it's faster
+ * to set the `nodeValue` of the Text node directly instead of using
+ * `.textContent` which will remove the existing node and create a new one.
+ *
+ * @param {DOMElement} node
+ * @param {string} text
+ * @internal
+ */
+var setTextContent = function (node, text) {
+ if (text) {
+ var firstChild = node.firstChild;
+
+ if (firstChild && firstChild === node.lastChild && firstChild.nodeType === TEXT_NODE) {
+ firstChild.nodeValue = text;
+ return;
+ }
+ }
+ node.textContent = text;
+};
+
+// List derived from Gecko source code:
+// https://github.com/mozilla/gecko-dev/blob/4e638efc71/layout/style/test/property_database.js
+
+/**
+ * CSS properties which accept numbers but are not in units of "px".
+ */
+var isUnitlessNumber = {
+ animationIterationCount: true,
+ borderImageOutset: true,
+ borderImageSlice: true,
+ borderImageWidth: true,
+ boxFlex: true,
+ boxFlexGroup: true,
+ boxOrdinalGroup: true,
+ columnCount: true,
+ columns: true,
+ flex: true,
+ flexGrow: true,
+ flexPositive: true,
+ flexShrink: true,
+ flexNegative: true,
+ flexOrder: true,
+ gridArea: true,
+ gridRow: true,
+ gridRowEnd: true,
+ gridRowSpan: true,
+ gridRowStart: true,
+ gridColumn: true,
+ gridColumnEnd: true,
+ gridColumnSpan: true,
+ gridColumnStart: true,
+ fontWeight: true,
+ lineClamp: true,
+ lineHeight: true,
+ opacity: true,
+ order: true,
+ orphans: true,
+ tabSize: true,
+ widows: true,
+ zIndex: true,
+ zoom: true,
+
+ // SVG-related properties
+ fillOpacity: true,
+ floodOpacity: true,
+ stopOpacity: true,
+ strokeDasharray: true,
+ strokeDashoffset: true,
+ strokeMiterlimit: true,
+ strokeOpacity: true,
+ strokeWidth: true
+};
+
+/**
+ * @param {string} prefix vendor-specific prefix, eg: Webkit
+ * @param {string} key style name, eg: transitionDuration
+ * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
+ * WebkitTransitionDuration
+ */
+function prefixKey(prefix, key) {
+ return prefix + key.charAt(0).toUpperCase() + key.substring(1);
+}
+
+/**
+ * Support style names that may come passed in prefixed by adding permutations
+ * of vendor prefixes.
+ */
+var prefixes = ['Webkit', 'ms', 'Moz', 'O'];
+
+// Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
+// infinite loop, because it iterates over the newly added props too.
+Object.keys(isUnitlessNumber).forEach(function (prop) {
+ prefixes.forEach(function (prefix) {
+ isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
+ });
+});
+
+/**
+ * Convert a value into the proper css writable value. The style name `name`
+ * should be logical (no hyphens), as specified
+ * in `CSSProperty.isUnitlessNumber`.
+ *
+ * @param {string} name CSS property name such as `topMargin`.
+ * @param {*} value CSS property value such as `10px`.
+ * @return {string} Normalized style value with dimensions applied.
+ */
+function dangerousStyleValue(name, value, isCustomProperty) {
+ // Note that we've removed escapeTextForBrowser() calls here since the
+ // whole string will be escaped when the attribute is injected into
+ // the markup. If you provide unsafe user data here they can inject
+ // arbitrary CSS which may be problematic (I couldn't repro this):
+ // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
+ // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
+ // This is not an XSS hole but instead a potential CSS injection issue
+ // which has lead to a greater discussion about how we're going to
+ // trust URLs moving forward. See #2115901
+
+ var isEmpty = value == null || typeof value === 'boolean' || value === '';
+ if (isEmpty) {
+ return '';
+ }
+
+ if (!isCustomProperty && typeof value === 'number' && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])) {
+ return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers
+ }
+
+ return ('' + value).trim();
+}
+
+/**
+ * Operations for dealing with CSS properties.
+ */
+
+/**
+ * This creates a string that is expected to be equivalent to the style
+ * attribute generated by server-side rendering. It by-passes warnings and
+ * security checks so it's not safe to use this value for anything other than
+ * comparison. It is only used in DEV for SSR validation.
+ */
+
+
+/**
+ * Sets the value for multiple styles on a node. If a value is specified as
+ * '' (empty string), the corresponding style property will be unset.
+ *
+ * @param {DOMElement} node
+ * @param {object} styles
+ */
+function setValueForStyles(node, styles) {
+ var style = node.style;
+ for (var styleName in styles) {
+ if (!styles.hasOwnProperty(styleName)) {
+ continue;
+ }
+ var isCustomProperty = styleName.indexOf('--') === 0;
+ var styleValue = dangerousStyleValue(styleName, styles[styleName], isCustomProperty);
+ if (styleName === 'float') {
+ styleName = 'cssFloat';
+ }
+ if (isCustomProperty) {
+ style.setProperty(styleName, styleValue);
+ } else {
+ style[styleName] = styleValue;
+ }
+ }
+}
+
+/**
+ * When mixing shorthand and longhand property names, we warn during updates if
+ * we expect an incorrect result to occur. In particular, we warn for:
+ *
+ * Updating a shorthand property (longhand gets overwritten):
+ * {font: 'foo', fontVariant: 'bar'} -> {font: 'baz', fontVariant: 'bar'}
+ * becomes .style.font = 'baz'
+ * Removing a shorthand property (longhand gets lost too):
+ * {font: 'foo', fontVariant: 'bar'} -> {fontVariant: 'bar'}
+ * becomes .style.font = ''
+ * Removing a longhand property (should revert to shorthand; doesn't):
+ * {font: 'foo', fontVariant: 'bar'} -> {font: 'foo'}
+ * becomes .style.fontVariant = ''
+ */
+
+// For HTML, certain tags should omit their close tag. We keep a whitelist for
+// those special-case tags.
+
+var omittedCloseTags = {
+ area: true,
+ base: true,
+ br: true,
+ col: true,
+ embed: true,
+ hr: true,
+ img: true,
+ input: true,
+ keygen: true,
+ link: true,
+ meta: true,
+ param: true,
+ source: true,
+ track: true,
+ wbr: true
+ // NOTE: menuitem's close tag should be omitted, but that causes problems.
+};
+
+// For HTML, certain tags cannot have children. This has the same purpose as
+// `omittedCloseTags` except that `menuitem` should still have its closing tag.
+
+var voidElementTags = _assign({
+ menuitem: true
+}, omittedCloseTags);
+
+// TODO: We can remove this if we add invariantWithStack()
+// or add stack by default to invariants where possible.
+var HTML$1 = '__html';
+
+function assertValidProps(tag, props) {
+ if (!props) {
+ return;
+ }
+ // Note the use of `==` which checks for null or undefined.
+ if (voidElementTags[tag]) {
+ !(props.children == null && props.dangerouslySetInnerHTML == null) ? reactProdInvariant('137', tag, '') : void 0;
+ }
+ if (props.dangerouslySetInnerHTML != null) {
+ !(props.children == null) ? reactProdInvariant('60') : void 0;
+ !(typeof props.dangerouslySetInnerHTML === 'object' && HTML$1 in props.dangerouslySetInnerHTML) ? reactProdInvariant('61') : void 0;
+ }
+ !(props.style == null || typeof props.style === 'object') ? reactProdInvariant('62', '') : void 0;
+}
+
+function isCustomComponent(tagName, props) {
+ if (tagName.indexOf('-') === -1) {
+ return typeof props.is === 'string';
+ }
+ switch (tagName) {
+ // These are reserved SVG and MathML elements.
+ // We don't mind this whitelist too much because we expect it to never grow.
+ // The alternative is to track the namespace in a few places which is convoluted.
+ // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts
+ case 'annotation-xml':
+ case 'color-profile':
+ case 'font-face':
+ case 'font-face-src':
+ case 'font-face-uri':
+ case 'font-face-format':
+ case 'font-face-name':
+ case 'missing-glyph':
+ return false;
+ default:
+ return true;
+ }
+}
+
+// When adding attributes to the HTML or SVG whitelist, be sure to
+// also add them to this module to ensure casing and incorrect name
+// warnings.
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+var DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
+var SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
+var SUPPRESS_HYDRATION_WARNING$1 = 'suppressHydrationWarning';
+var AUTOFOCUS = 'autoFocus';
+var CHILDREN = 'children';
+var STYLE$1 = 'style';
+var HTML = '__html';
+
+var HTML_NAMESPACE = Namespaces.html;
+
+
+function ensureListeningTo(rootContainerElement, registrationName) {
+ var isDocumentOrFragment = rootContainerElement.nodeType === DOCUMENT_NODE || rootContainerElement.nodeType === DOCUMENT_FRAGMENT_NODE;
+ var doc = isDocumentOrFragment ? rootContainerElement : rootContainerElement.ownerDocument;
+ listenTo(registrationName, doc);
+}
+
+function getOwnerDocumentFromRootContainer(rootContainerElement) {
+ return rootContainerElement.nodeType === DOCUMENT_NODE ? rootContainerElement : rootContainerElement.ownerDocument;
+}
+
+function noop() {}
+
+function trapClickOnNonInteractiveElement(node) {
+ // Mobile Safari does not fire properly bubble click events on
+ // non-interactive elements, which means delegated click listeners do not
+ // fire. The workaround for this bug involves attaching an empty click
+ // listener on the target node.
+ // http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
+ // Just set it using the onclick property so that we don't have to manage any
+ // bookkeeping for it. Not sure if we need to clear it when the listener is
+ // removed.
+ // TODO: Only do this for the relevant Safaris maybe?
+ node.onclick = noop;
+}
+
+function setInitialDOMProperties(tag, domElement, rootContainerElement, nextProps, isCustomComponentTag) {
+ for (var propKey in nextProps) {
+ if (!nextProps.hasOwnProperty(propKey)) {
+ continue;
+ }
+ var nextProp = nextProps[propKey];
+ if (propKey === STYLE$1) {
+ setValueForStyles(domElement, nextProp);
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ var nextHtml = nextProp ? nextProp[HTML] : undefined;
+ if (nextHtml != null) {
+ setInnerHTML(domElement, nextHtml);
+ }
+ } else if (propKey === CHILDREN) {
+ if (typeof nextProp === 'string') {
+ // Avoid setting initial textContent when the text is empty. In IE11 setting
+ // textContent on a <textarea> will cause the placeholder to not
+ // show within the <textarea> until it has been focused and blurred again.
+ // https://github.com/facebook/react/issues/6731#issuecomment-254874553
+ var canSetTextContent = tag !== 'textarea' || nextProp !== '';
+ if (canSetTextContent) {
+ setTextContent(domElement, nextProp);
+ }
+ } else if (typeof nextProp === 'number') {
+ setTextContent(domElement, '' + nextProp);
+ }
+ } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING$1) {
+ // Noop
+ } else if (propKey === AUTOFOCUS) {
+ // We polyfill it separately on the client during commit.
+ // We could have excluded it in the property list instead of
+ // adding a special case here, but then it wouldn't be emitted
+ // on server rendering (but we *do* want to emit it in SSR).
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ if (nextProp != null) {
+ ensureListeningTo(rootContainerElement, propKey);
+ }
+ } else if (nextProp != null) {
+ setValueForProperty(domElement, propKey, nextProp, isCustomComponentTag);
+ }
+ }
+}
+
+function updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag) {
+ // TODO: Handle wasCustomComponentTag
+ for (var i = 0; i < updatePayload.length; i += 2) {
+ var propKey = updatePayload[i];
+ var propValue = updatePayload[i + 1];
+ if (propKey === STYLE$1) {
+ setValueForStyles(domElement, propValue);
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ setInnerHTML(domElement, propValue);
+ } else if (propKey === CHILDREN) {
+ setTextContent(domElement, propValue);
+ } else {
+ setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
+ }
+ }
+}
+
+function createElement(type, props, rootContainerElement, parentNamespace) {
+ var ownerDocument = getOwnerDocumentFromRootContainer(rootContainerElement);
+ var domElement = void 0;
+ var namespaceURI = parentNamespace;
+ if (namespaceURI === HTML_NAMESPACE) {
+ namespaceURI = getIntrinsicNamespace(type);
+ }
+ if (namespaceURI === HTML_NAMESPACE) {
+ if (type === 'script') {
+ // Create the script via .innerHTML so its "parser-inserted" flag is
+ // set to true and it does not execute
+ var div = ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
+ // This is guaranteed to yield a script element.
+ var firstChild = div.firstChild;
+ domElement = div.removeChild(firstChild);
+ } else if (typeof props.is === 'string') {
+ // $FlowIssue `createElement` should be updated for Web Components
+ domElement = ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', type, { is: props.is });
+ } else {
+ // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.
+ // See discussion in https://github.com/facebook/react/pull/6896
+ // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240
+ domElement = ownerDocument.createElementNS('http://www.w3.org/1999/xhtml', type);
+ // Normally attributes are assigned in `setInitialDOMProperties`, however the `multiple` and `size`
+ // attributes on `select`s needs to be added before `option`s are inserted.
+ // This prevents:
+ // - a bug where the `select` does not scroll to the correct option because singular
+ // `select` elements automatically pick the first item #13222
+ // - a bug where the `select` set the first item as selected despite the `size` attribute #14239
+ // See https://github.com/facebook/react/issues/13222
+ // and https://github.com/facebook/react/issues/14239
+ if (type === 'select') {
+ var node = domElement;
+ if (props.multiple) {
+ node.multiple = true;
+ } else if (props.size) {
+ // Setting a size greater than 1 causes a select to behave like `multiple=true`, where
+ // it is possible that no option is selected.
+ //
+ // This is only necessary when a select in "single selection mode".
+ node.size = props.size;
+ }
+ }
+ }
+ } else {
+ domElement = ownerDocument.createElementNS(namespaceURI, type);
+ }
+
+ return domElement;
+}
+
+function createTextNode(text, rootContainerElement) {
+ return getOwnerDocumentFromRootContainer(rootContainerElement).createTextNode(text);
+}
+
+function setInitialProperties(domElement, tag, rawProps, rootContainerElement) {
+ var isCustomComponentTag = isCustomComponent(tag, rawProps);
+ var props = void 0;
+ switch (tag) {
+ case 'iframe':
+ case 'object':
+ trapBubbledEvent(TOP_LOAD, domElement);
+ props = rawProps;
+ break;
+ case 'video':
+ case 'audio':
+ // Create listener for each media event
+ for (var i = 0; i < mediaEventTypes.length; i++) {
+ trapBubbledEvent(mediaEventTypes[i], domElement);
+ }
+ props = rawProps;
+ break;
+ case 'source':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ props = rawProps;
+ break;
+ case 'img':
+ case 'image':
+ case 'link':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ trapBubbledEvent(TOP_LOAD, domElement);
+ props = rawProps;
+ break;
+ case 'form':
+ trapBubbledEvent(TOP_RESET, domElement);
+ trapBubbledEvent(TOP_SUBMIT, domElement);
+ props = rawProps;
+ break;
+ case 'details':
+ trapBubbledEvent(TOP_TOGGLE, domElement);
+ props = rawProps;
+ break;
+ case 'input':
+ initWrapperState(domElement, rawProps);
+ props = getHostProps(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'option':
+
+ props = getHostProps$1(domElement, rawProps);
+ break;
+ case 'select':
+ initWrapperState$1(domElement, rawProps);
+ props = getHostProps$2(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'textarea':
+ initWrapperState$2(domElement, rawProps);
+ props = getHostProps$3(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ default:
+ props = rawProps;
+ }
+
+ assertValidProps(tag, props);
+
+ setInitialDOMProperties(tag, domElement, rootContainerElement, props, isCustomComponentTag);
+
+ switch (tag) {
+ case 'input':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper(domElement, rawProps, false);
+ break;
+ case 'textarea':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper$3(domElement, rawProps);
+ break;
+ case 'option':
+ postMountWrapper$1(domElement, rawProps);
+ break;
+ case 'select':
+ postMountWrapper$2(domElement, rawProps);
+ break;
+ default:
+ if (typeof props.onClick === 'function') {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(domElement);
+ }
+ break;
+ }
+}
+
+// Calculate the diff between the two objects.
+function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {
+ var updatePayload = null;
+
+ var lastProps = void 0;
+ var nextProps = void 0;
+ switch (tag) {
+ case 'input':
+ lastProps = getHostProps(domElement, lastRawProps);
+ nextProps = getHostProps(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ case 'option':
+ lastProps = getHostProps$1(domElement, lastRawProps);
+ nextProps = getHostProps$1(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ case 'select':
+ lastProps = getHostProps$2(domElement, lastRawProps);
+ nextProps = getHostProps$2(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ case 'textarea':
+ lastProps = getHostProps$3(domElement, lastRawProps);
+ nextProps = getHostProps$3(domElement, nextRawProps);
+ updatePayload = [];
+ break;
+ default:
+ lastProps = lastRawProps;
+ nextProps = nextRawProps;
+ if (typeof lastProps.onClick !== 'function' && typeof nextProps.onClick === 'function') {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(domElement);
+ }
+ break;
+ }
+
+ assertValidProps(tag, nextProps);
+
+ var propKey = void 0;
+ var styleName = void 0;
+ var styleUpdates = null;
+ for (propKey in lastProps) {
+ if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {
+ continue;
+ }
+ if (propKey === STYLE$1) {
+ var lastStyle = lastProps[propKey];
+ for (styleName in lastStyle) {
+ if (lastStyle.hasOwnProperty(styleName)) {
+ if (!styleUpdates) {
+ styleUpdates = {};
+ }
+ styleUpdates[styleName] = '';
+ }
+ }
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML || propKey === CHILDREN) {
+ // Noop. This is handled by the clear text mechanism.
+ } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING$1) {
+ // Noop
+ } else if (propKey === AUTOFOCUS) {
+ // Noop. It doesn't work on updates anyway.
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ // This is a special case. If any listener updates we need to ensure
+ // that the "current" fiber pointer gets updated so we need a commit
+ // to update this element.
+ if (!updatePayload) {
+ updatePayload = [];
+ }
+ } else {
+ // For all other deleted properties we add it to the queue. We use
+ // the whitelist in the commit phase instead.
+ (updatePayload = updatePayload || []).push(propKey, null);
+ }
+ }
+ for (propKey in nextProps) {
+ var nextProp = nextProps[propKey];
+ var lastProp = lastProps != null ? lastProps[propKey] : undefined;
+ if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) {
+ continue;
+ }
+ if (propKey === STYLE$1) {
+ if (lastProp) {
+ // Unset styles on `lastProp` but not on `nextProp`.
+ for (styleName in lastProp) {
+ if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {
+ if (!styleUpdates) {
+ styleUpdates = {};
+ }
+ styleUpdates[styleName] = '';
+ }
+ }
+ // Update styles that changed since `lastProp`.
+ for (styleName in nextProp) {
+ if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {
+ if (!styleUpdates) {
+ styleUpdates = {};
+ }
+ styleUpdates[styleName] = nextProp[styleName];
+ }
+ }
+ } else {
+ // Relies on `updateStylesByID` not mutating `styleUpdates`.
+ if (!styleUpdates) {
+ if (!updatePayload) {
+ updatePayload = [];
+ }
+ updatePayload.push(propKey, styleUpdates);
+ }
+ styleUpdates = nextProp;
+ }
+ } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
+ var nextHtml = nextProp ? nextProp[HTML] : undefined;
+ var lastHtml = lastProp ? lastProp[HTML] : undefined;
+ if (nextHtml != null) {
+ if (lastHtml !== nextHtml) {
+ (updatePayload = updatePayload || []).push(propKey, '' + nextHtml);
+ }
+ } else {
+ // TODO: It might be too late to clear this if we have children
+ // inserted already.
+ }
+ } else if (propKey === CHILDREN) {
+ if (lastProp !== nextProp && (typeof nextProp === 'string' || typeof nextProp === 'number')) {
+ (updatePayload = updatePayload || []).push(propKey, '' + nextProp);
+ }
+ } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING$1) {
+ // Noop
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ if (nextProp != null) {
+ // We eagerly listen to this even though we haven't committed yet.
+ ensureListeningTo(rootContainerElement, propKey);
+ }
+ if (!updatePayload && lastProp !== nextProp) {
+ // This is a special case. If any listener updates we need to ensure
+ // that the "current" props pointer gets updated so we need a commit
+ // to update this element.
+ updatePayload = [];
+ }
+ } else {
+ // For any other property we always add it to the queue and then we
+ // filter it out using the whitelist during the commit.
+ (updatePayload = updatePayload || []).push(propKey, nextProp);
+ }
+ }
+ if (styleUpdates) {
+ (updatePayload = updatePayload || []).push(STYLE$1, styleUpdates);
+ }
+ return updatePayload;
+}
+
+// Apply the diff.
+function updateProperties(domElement, updatePayload, tag, lastRawProps, nextRawProps) {
+ // Update checked *before* name.
+ // In the middle of an update, it is possible to have multiple checked.
+ // When a checked radio tries to change name, browser makes another radio's checked false.
+ if (tag === 'input' && nextRawProps.type === 'radio' && nextRawProps.name != null) {
+ updateChecked(domElement, nextRawProps);
+ }
+
+ var wasCustomComponentTag = isCustomComponent(tag, lastRawProps);
+ var isCustomComponentTag = isCustomComponent(tag, nextRawProps);
+ // Apply the diff.
+ updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag);
+
+ // TODO: Ensure that an update gets scheduled if any of the special props
+ // changed.
+ switch (tag) {
+ case 'input':
+ // Update the wrapper around inputs *after* updating props. This has to
+ // happen after `updateDOMProperties`. Otherwise HTML5 input validations
+ // raise warnings and prevent the new value from being assigned.
+ updateWrapper(domElement, nextRawProps);
+ break;
+ case 'textarea':
+ updateWrapper$1(domElement, nextRawProps);
+ break;
+ case 'select':
+ // <select> value update needs to occur after <option> children
+ // reconciliation
+ postUpdateWrapper(domElement, nextRawProps);
+ break;
+ }
+}
+
+function diffHydratedProperties(domElement, tag, rawProps, parentNamespace, rootContainerElement) {
+ var isCustomComponentTag = void 0;
+ switch (tag) {
+ case 'iframe':
+ case 'object':
+ trapBubbledEvent(TOP_LOAD, domElement);
+ break;
+ case 'video':
+ case 'audio':
+ // Create listener for each media event
+ for (var i = 0; i < mediaEventTypes.length; i++) {
+ trapBubbledEvent(mediaEventTypes[i], domElement);
+ }
+ break;
+ case 'source':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ break;
+ case 'img':
+ case 'image':
+ case 'link':
+ trapBubbledEvent(TOP_ERROR, domElement);
+ trapBubbledEvent(TOP_LOAD, domElement);
+ break;
+ case 'form':
+ trapBubbledEvent(TOP_RESET, domElement);
+ trapBubbledEvent(TOP_SUBMIT, domElement);
+ break;
+ case 'details':
+ trapBubbledEvent(TOP_TOGGLE, domElement);
+ break;
+ case 'input':
+ initWrapperState(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'option':
+
+ break;
+ case 'select':
+ initWrapperState$1(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ case 'textarea':
+ initWrapperState$2(domElement, rawProps);
+ trapBubbledEvent(TOP_INVALID, domElement);
+ // For controlled components we always need to ensure we're listening
+ // to onChange. Even if there is no listener.
+ ensureListeningTo(rootContainerElement, 'onChange');
+ break;
+ }
+
+ assertValidProps(tag, rawProps);
+
+ var updatePayload = null;
+ for (var propKey in rawProps) {
+ if (!rawProps.hasOwnProperty(propKey)) {
+ continue;
+ }
+ var nextProp = rawProps[propKey];
+ if (propKey === CHILDREN) {
+ // For text content children we compare against textContent. This
+ // might match additional HTML that is hidden when we read it using
+ // textContent. E.g. "foo" will match "f<span>oo</span>" but that still
+ // satisfies our requirement. Our requirement is not to produce perfect
+ // HTML and attributes. Ideally we should preserve structure but it's
+ // ok not to if the visible content is still enough to indicate what
+ // even listeners these nodes might be wired up to.
+ // TODO: Warn if there is more than a single textNode as a child.
+ // TODO: Should we use domElement.firstChild.nodeValue to compare?
+ if (typeof nextProp === 'string') {
+ if (domElement.textContent !== nextProp) {
+ updatePayload = [CHILDREN, nextProp];
+ }
+ } else if (typeof nextProp === 'number') {
+ if (domElement.textContent !== '' + nextProp) {
+ updatePayload = [CHILDREN, '' + nextProp];
+ }
+ }
+ } else if (registrationNameModules.hasOwnProperty(propKey)) {
+ if (nextProp != null) {
+ ensureListeningTo(rootContainerElement, propKey);
+ }
+ } else {}
+ }
+
+ switch (tag) {
+ case 'input':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper(domElement, rawProps, true);
+ break;
+ case 'textarea':
+ // TODO: Make sure we check if this is still unmounted or do any clean
+ // up necessary since we never stop tracking anymore.
+ track(domElement);
+ postMountWrapper$3(domElement, rawProps);
+ break;
+ case 'select':
+ case 'option':
+ // For input and textarea we current always set the value property at
+ // post mount to force it to diverge from attributes. However, for
+ // option and select we don't quite do the same thing and select
+ // is not resilient to the DOM state changing so we don't do that here.
+ // TODO: Consider not doing this for input and textarea.
+ break;
+ default:
+ if (typeof rawProps.onClick === 'function') {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(domElement);
+ }
+ break;
+ }
+
+ return updatePayload;
+}
+
+function diffHydratedText(textNode, text) {
+ var isDifferent = textNode.nodeValue !== text;
+ return isDifferent;
+}
+
+
+
+
+
+
+
+
+
+
+
+function restoreControlledState$1(domElement, tag, props) {
+ switch (tag) {
+ case 'input':
+ restoreControlledState(domElement, props);
+ return;
+ case 'textarea':
+ restoreControlledState$3(domElement, props);
+ return;
+ case 'select':
+ restoreControlledState$2(domElement, props);
+ return;
+ }
+}
+
+// TODO: direct imports like some-package/src/* are bad. Fix me.
+
+var ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _ReactInternals$Sched = ReactInternals$1.Scheduler;
+var unstable_cancelCallback = _ReactInternals$Sched.unstable_cancelCallback;
+var unstable_now = _ReactInternals$Sched.unstable_now;
+var unstable_scheduleCallback = _ReactInternals$Sched.unstable_scheduleCallback;
+var unstable_shouldYield = _ReactInternals$Sched.unstable_shouldYield;
+var unstable_getFirstCallbackNode = _ReactInternals$Sched.unstable_getFirstCallbackNode;
+var unstable_runWithPriority = _ReactInternals$Sched.unstable_runWithPriority;
+var unstable_next = _ReactInternals$Sched.unstable_next;
+var unstable_continueExecution = _ReactInternals$Sched.unstable_continueExecution;
+var unstable_pauseExecution = _ReactInternals$Sched.unstable_pauseExecution;
+var unstable_getCurrentPriorityLevel = _ReactInternals$Sched.unstable_getCurrentPriorityLevel;
+var unstable_ImmediatePriority = _ReactInternals$Sched.unstable_ImmediatePriority;
+var unstable_UserBlockingPriority = _ReactInternals$Sched.unstable_UserBlockingPriority;
+var unstable_NormalPriority = _ReactInternals$Sched.unstable_NormalPriority;
+var unstable_LowPriority = _ReactInternals$Sched.unstable_LowPriority;
+var unstable_IdlePriority = _ReactInternals$Sched.unstable_IdlePriority;
+
+// Renderers that don't support persistence
+// can re-export everything from this module.
+
+function shim() {
+ reactProdInvariant('270');
+}
+
+// Persistence (when unsupported)
+var supportsPersistence = false;
+var cloneInstance = shim;
+var createContainerChildSet = shim;
+var appendChildToContainerChildSet = shim;
+var finalizeContainerChildren = shim;
+var replaceContainerChildren = shim;
+var cloneHiddenInstance = shim;
+var cloneUnhiddenInstance = shim;
+var createHiddenTextInstance = shim;
+
+var SUSPENSE_START_DATA = '$';
+var SUSPENSE_END_DATA = '/$';
+
+var STYLE = 'style';
+
+var eventsEnabled = null;
+var selectionInformation = null;
+
+function shouldAutoFocusHostComponent(type, props) {
+ switch (type) {
+ case 'button':
+ case 'input':
+ case 'select':
+ case 'textarea':
+ return !!props.autoFocus;
+ }
+ return false;
+}
+
+function getRootHostContext(rootContainerInstance) {
+ var type = void 0;
+ var namespace = void 0;
+ var nodeType = rootContainerInstance.nodeType;
+ switch (nodeType) {
+ case DOCUMENT_NODE:
+ case DOCUMENT_FRAGMENT_NODE:
+ {
+ type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';
+ var root = rootContainerInstance.documentElement;
+ namespace = root ? root.namespaceURI : getChildNamespace(null, '');
+ break;
+ }
+ default:
+ {
+ var container = nodeType === COMMENT_NODE ? rootContainerInstance.parentNode : rootContainerInstance;
+ var ownNamespace = container.namespaceURI || null;
+ type = container.tagName;
+ namespace = getChildNamespace(ownNamespace, type);
+ break;
+ }
+ }
+ return namespace;
+}
+
+function getChildHostContext(parentHostContext, type, rootContainerInstance) {
+ var parentNamespace = parentHostContext;
+ return getChildNamespace(parentNamespace, type);
+}
+
+function getPublicInstance(instance) {
+ return instance;
+}
+
+function prepareForCommit(containerInfo) {
+ eventsEnabled = isEnabled();
+ selectionInformation = getSelectionInformation();
+ setEnabled(false);
+}
+
+function resetAfterCommit(containerInfo) {
+ restoreSelection(selectionInformation);
+ selectionInformation = null;
+ setEnabled(eventsEnabled);
+ eventsEnabled = null;
+}
+
+function createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
+ var parentNamespace = void 0;
+ {
+ parentNamespace = hostContext;
+ }
+ var domElement = createElement(type, props, rootContainerInstance, parentNamespace);
+ precacheFiberNode(internalInstanceHandle, domElement);
+ updateFiberProps(domElement, props);
+ return domElement;
+}
+
+function appendInitialChild(parentInstance, child) {
+ parentInstance.appendChild(child);
+}
+
+function finalizeInitialChildren(domElement, type, props, rootContainerInstance, hostContext) {
+ setInitialProperties(domElement, type, props, rootContainerInstance);
+ return shouldAutoFocusHostComponent(type, props);
+}
+
+function prepareUpdate(domElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
+ return diffProperties(domElement, type, oldProps, newProps, rootContainerInstance);
+}
+
+function shouldSetTextContent(type, props) {
+ return type === 'textarea' || type === 'option' || type === 'noscript' || typeof props.children === 'string' || typeof props.children === 'number' || typeof props.dangerouslySetInnerHTML === 'object' && props.dangerouslySetInnerHTML !== null && props.dangerouslySetInnerHTML.__html != null;
+}
+
+function shouldDeprioritizeSubtree(type, props) {
+ return !!props.hidden;
+}
+
+function createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) {
+ var textNode = createTextNode(text, rootContainerInstance);
+ precacheFiberNode(internalInstanceHandle, textNode);
+ return textNode;
+}
+
+var isPrimaryRenderer = true;
+// This initialization code may run even on server environments
+// if a component just imports ReactDOM (e.g. for findDOMNode).
+// Some environments might not have setTimeout or clearTimeout.
+var scheduleTimeout = typeof setTimeout === 'function' ? setTimeout : undefined;
+var cancelTimeout = typeof clearTimeout === 'function' ? clearTimeout : undefined;
+var noTimeout = -1;
+var schedulePassiveEffects = unstable_scheduleCallback;
+var cancelPassiveEffects = unstable_cancelCallback;
+
+// -------------------
+// Mutation
+// -------------------
+
+var supportsMutation = true;
+
+function commitMount(domElement, type, newProps, internalInstanceHandle) {
+ // Despite the naming that might imply otherwise, this method only
+ // fires if there is an `Update` effect scheduled during mounting.
+ // This happens if `finalizeInitialChildren` returns `true` (which it
+ // does to implement the `autoFocus` attribute on the client). But
+ // there are also other cases when this might happen (such as patching
+ // up text content during hydration mismatch). So we'll check this again.
+ if (shouldAutoFocusHostComponent(type, newProps)) {
+ domElement.focus();
+ }
+}
+
+function commitUpdate(domElement, updatePayload, type, oldProps, newProps, internalInstanceHandle) {
+ // Update the props handle so that we know which props are the ones with
+ // with current event handlers.
+ updateFiberProps(domElement, newProps);
+ // Apply the diff to the DOM node.
+ updateProperties(domElement, updatePayload, type, oldProps, newProps);
+}
+
+function resetTextContent(domElement) {
+ setTextContent(domElement, '');
+}
+
+function commitTextUpdate(textInstance, oldText, newText) {
+ textInstance.nodeValue = newText;
+}
+
+function appendChild(parentInstance, child) {
+ parentInstance.appendChild(child);
+}
+
+function appendChildToContainer(container, child) {
+ var parentNode = void 0;
+ if (container.nodeType === COMMENT_NODE) {
+ parentNode = container.parentNode;
+ parentNode.insertBefore(child, container);
+ } else {
+ parentNode = container;
+ parentNode.appendChild(child);
+ }
+ // This container might be used for a portal.
+ // If something inside a portal is clicked, that click should bubble
+ // through the React tree. However, on Mobile Safari the click would
+ // never bubble through the *DOM* tree unless an ancestor with onclick
+ // event exists. So we wouldn't see it and dispatch it.
+ // This is why we ensure that non React root containers have inline onclick
+ // defined.
+ // https://github.com/facebook/react/issues/11918
+ var reactRootContainer = container._reactRootContainer;
+ if ((reactRootContainer === null || reactRootContainer === undefined) && parentNode.onclick === null) {
+ // TODO: This cast may not be sound for SVG, MathML or custom elements.
+ trapClickOnNonInteractiveElement(parentNode);
+ }
+}
+
+function insertBefore(parentInstance, child, beforeChild) {
+ parentInstance.insertBefore(child, beforeChild);
+}
+
+function insertInContainerBefore(container, child, beforeChild) {
+ if (container.nodeType === COMMENT_NODE) {
+ container.parentNode.insertBefore(child, beforeChild);
+ } else {
+ container.insertBefore(child, beforeChild);
+ }
+}
+
+function removeChild(parentInstance, child) {
+ parentInstance.removeChild(child);
+}
+
+function removeChildFromContainer(container, child) {
+ if (container.nodeType === COMMENT_NODE) {
+ container.parentNode.removeChild(child);
+ } else {
+ container.removeChild(child);
+ }
+}
+
+function clearSuspenseBoundary(parentInstance, suspenseInstance) {
+ var node = suspenseInstance;
+ // Delete all nodes within this suspense boundary.
+ // There might be nested nodes so we need to keep track of how
+ // deep we are and only break out when we're back on top.
+ var depth = 0;
+ do {
+ var nextNode = node.nextSibling;
+ parentInstance.removeChild(node);
+ if (nextNode && nextNode.nodeType === COMMENT_NODE) {
+ var data = nextNode.data;
+ if (data === SUSPENSE_END_DATA) {
+ if (depth === 0) {
+ parentInstance.removeChild(nextNode);
+ return;
+ } else {
+ depth--;
+ }
+ } else if (data === SUSPENSE_START_DATA) {
+ depth++;
+ }
+ }
+ node = nextNode;
+ } while (node);
+ // TODO: Warn, we didn't find the end comment boundary.
+}
+
+function clearSuspenseBoundaryFromContainer(container, suspenseInstance) {
+ if (container.nodeType === COMMENT_NODE) {
+ clearSuspenseBoundary(container.parentNode, suspenseInstance);
+ } else if (container.nodeType === ELEMENT_NODE) {
+ clearSuspenseBoundary(container, suspenseInstance);
+ } else {
+ // Document nodes should never contain suspense boundaries.
+ }
+}
+
+function hideInstance(instance) {
+ // TODO: Does this work for all element types? What about MathML? Should we
+ // pass host context to this method?
+ instance = instance;
+ instance.style.display = 'none';
+}
+
+function hideTextInstance(textInstance) {
+ textInstance.nodeValue = '';
+}
+
+function unhideInstance(instance, props) {
+ instance = instance;
+ var styleProp = props[STYLE];
+ var display = styleProp !== undefined && styleProp !== null && styleProp.hasOwnProperty('display') ? styleProp.display : null;
+ instance.style.display = dangerousStyleValue('display', display);
+}
+
+function unhideTextInstance(textInstance, text) {
+ textInstance.nodeValue = text;
+}
+
+// -------------------
+// Hydration
+// -------------------
+
+var supportsHydration = true;
+
+function canHydrateInstance(instance, type, props) {
+ if (instance.nodeType !== ELEMENT_NODE || type.toLowerCase() !== instance.nodeName.toLowerCase()) {
+ return null;
+ }
+ // This has now been refined to an element node.
+ return instance;
+}
+
+function canHydrateTextInstance(instance, text) {
+ if (text === '' || instance.nodeType !== TEXT_NODE) {
+ // Empty strings are not parsed by HTML so there won't be a correct match here.
+ return null;
+ }
+ // This has now been refined to a text node.
+ return instance;
+}
+
+function canHydrateSuspenseInstance(instance) {
+ if (instance.nodeType !== COMMENT_NODE) {
+ // Empty strings are not parsed by HTML so there won't be a correct match here.
+ return null;
+ }
+ // This has now been refined to a suspense node.
+ return instance;
+}
+
+function getNextHydratableSibling(instance) {
+ var node = instance.nextSibling;
+ // Skip non-hydratable nodes.
+ while (node && node.nodeType !== ELEMENT_NODE && node.nodeType !== TEXT_NODE && (!enableSuspenseServerRenderer || node.nodeType !== COMMENT_NODE || node.data !== SUSPENSE_START_DATA)) {
+ node = node.nextSibling;
+ }
+ return node;
+}
+
+function getFirstHydratableChild(parentInstance) {
+ var next = parentInstance.firstChild;
+ // Skip non-hydratable nodes.
+ while (next && next.nodeType !== ELEMENT_NODE && next.nodeType !== TEXT_NODE && (!enableSuspenseServerRenderer || next.nodeType !== COMMENT_NODE || next.data !== SUSPENSE_START_DATA)) {
+ next = next.nextSibling;
+ }
+ return next;
+}
+
+function hydrateInstance(instance, type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
+ precacheFiberNode(internalInstanceHandle, instance);
+ // TODO: Possibly defer this until the commit phase where all the events
+ // get attached.
+ updateFiberProps(instance, props);
+ var parentNamespace = void 0;
+ {
+ parentNamespace = hostContext;
+ }
+ return diffHydratedProperties(instance, type, props, parentNamespace, rootContainerInstance);
+}
+
+function hydrateTextInstance(textInstance, text, internalInstanceHandle) {
+ precacheFiberNode(internalInstanceHandle, textInstance);
+ return diffHydratedText(textInstance, text);
+}
+
+function getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance) {
+ var node = suspenseInstance.nextSibling;
+ // Skip past all nodes within this suspense boundary.
+ // There might be nested nodes so we need to keep track of how
+ // deep we are and only break out when we're back on top.
+ var depth = 0;
+ while (node) {
+ if (node.nodeType === COMMENT_NODE) {
+ var data = node.data;
+ if (data === SUSPENSE_END_DATA) {
+ if (depth === 0) {
+ return getNextHydratableSibling(node);
+ } else {
+ depth--;
+ }
+ } else if (data === SUSPENSE_START_DATA) {
+ depth++;
+ }
+ }
+ node = node.nextSibling;
+ }
+ // TODO: Warn, we didn't find the end comment boundary.
+ return null;
+}
+
+// Prefix measurements so that it's possible to filter them.
+// Longer prefixes are hard to read in DevTools.
+var reactEmoji = '\u269B';
+var warningEmoji = '\u26D4';
+var supportsUserTiming = typeof performance !== 'undefined' && typeof performance.mark === 'function' && typeof performance.clearMarks === 'function' && typeof performance.measure === 'function' && typeof performance.clearMeasures === 'function';
+
+// Keep track of current fiber so that we know the path to unwind on pause.
+// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
+var currentFiber = null;
+// If we're in the middle of user code, which fiber and method is it?
+// Reusing `currentFiber` would be confusing for this because user code fiber
+// can change during commit phase too, but we don't need to unwind it (since
+// lifecycles in the commit phase don't resemble a tree).
+var currentPhase = null;
+var currentPhaseFiber = null;
+// Did lifecycle hook schedule an update? This is often a performance problem,
+// so we will keep track of it, and include it in the report.
+// Track commits caused by cascading updates.
+var isCommitting = false;
+var hasScheduledUpdateInCurrentCommit = false;
+var hasScheduledUpdateInCurrentPhase = false;
+var commitCountInCurrentWorkLoop = 0;
+var effectCountInCurrentCommit = 0;
+var isWaitingForCallback = false;
+// During commits, we only show a measurement once per method name
+// to avoid stretch the commit phase with measurement overhead.
+var labelsInCurrentCommit = new Set();
+
+var formatMarkName = function (markName) {
+ return reactEmoji + ' ' + markName;
+};
+
+var formatLabel = function (label, warning) {
+ var prefix = warning ? warningEmoji + ' ' : reactEmoji + ' ';
+ var suffix = warning ? ' Warning: ' + warning : '';
+ return '' + prefix + label + suffix;
+};
+
+var beginMark = function (markName) {
+ performance.mark(formatMarkName(markName));
+};
+
+var clearMark = function (markName) {
+ performance.clearMarks(formatMarkName(markName));
+};
+
+var endMark = function (label, markName, warning) {
+ var formattedMarkName = formatMarkName(markName);
+ var formattedLabel = formatLabel(label, warning);
+ try {
+ performance.measure(formattedLabel, formattedMarkName);
+ } catch (err) {}
+ // If previous mark was missing for some reason, this will throw.
+ // This could only happen if React crashed in an unexpected place earlier.
+ // Don't pile on with more errors.
+
+ // Clear marks immediately to avoid growing buffer.
+ performance.clearMarks(formattedMarkName);
+ performance.clearMeasures(formattedLabel);
+};
+
+var getFiberMarkName = function (label, debugID) {
+ return label + ' (#' + debugID + ')';
+};
+
+var getFiberLabel = function (componentName, isMounted, phase) {
+ if (phase === null) {
+ // These are composite component total time measurements.
+ return componentName + ' [' + (isMounted ? 'update' : 'mount') + ']';
+ } else {
+ // Composite component methods.
+ return componentName + '.' + phase;
+ }
+};
+
+var beginFiberMark = function (fiber, phase) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+
+ if (isCommitting && labelsInCurrentCommit.has(label)) {
+ // During the commit phase, we don't show duplicate labels because
+ // there is a fixed overhead for every measurement, and we don't
+ // want to stretch the commit phase beyond necessary.
+ return false;
+ }
+ labelsInCurrentCommit.add(label);
+
+ var markName = getFiberMarkName(label, debugID);
+ beginMark(markName);
+ return true;
+};
+
+var clearFiberMark = function (fiber, phase) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ clearMark(markName);
+};
+
+var endFiberMark = function (fiber, phase, warning) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ endMark(label, markName, warning);
+};
+
+var shouldIgnoreFiber = function (fiber) {
+ // Host components should be skipped in the timeline.
+ // We could check typeof fiber.type, but does this work with RN?
+ switch (fiber.tag) {
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ case Fragment:
+ case ContextProvider:
+ case ContextConsumer:
+ case Mode:
+ return true;
+ default:
+ return false;
+ }
+};
+
+var clearPendingPhaseMeasurement = function () {
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ clearFiberMark(currentPhaseFiber, currentPhase);
+ }
+ currentPhaseFiber = null;
+ currentPhase = null;
+ hasScheduledUpdateInCurrentPhase = false;
+};
+
+var pauseTimers = function () {
+ // Stops all currently active measurements so that they can be resumed
+ // if we continue in a later deferred loop from the same unit of work.
+ var fiber = currentFiber;
+ while (fiber) {
+ if (fiber._debugIsCurrentlyTiming) {
+ endFiberMark(fiber, null, null);
+ }
+ fiber = fiber.return;
+ }
+};
+
+var resumeTimersRecursively = function (fiber) {
+ if (fiber.return !== null) {
+ resumeTimersRecursively(fiber.return);
+ }
+ if (fiber._debugIsCurrentlyTiming) {
+ beginFiberMark(fiber, null);
+ }
+};
+
+var resumeTimers = function () {
+ // Resumes all measurements that were active during the last deferred loop.
+ if (currentFiber !== null) {
+ resumeTimersRecursively(currentFiber);
+ }
+};
+
+function recordEffect() {
+ if (enableUserTimingAPI) {
+ effectCountInCurrentCommit++;
+ }
+}
+
+function recordScheduleUpdate() {
+ if (enableUserTimingAPI) {
+ if (isCommitting) {
+ hasScheduledUpdateInCurrentCommit = true;
+ }
+ if (currentPhase !== null && currentPhase !== 'componentWillMount' && currentPhase !== 'componentWillReceiveProps') {
+ hasScheduledUpdateInCurrentPhase = true;
+ }
+ }
+}
+
+function startRequestCallbackTimer() {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming && !isWaitingForCallback) {
+ isWaitingForCallback = true;
+ beginMark('(Waiting for async callback...)');
+ }
+ }
+}
+
+function stopRequestCallbackTimer(didExpire, expirationTime) {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming) {
+ isWaitingForCallback = false;
+ var warning = didExpire ? 'React was blocked by main thread' : null;
+ endMark('(Waiting for async callback... will force flush in ' + expirationTime + ' ms)', '(Waiting for async callback...)', warning);
+ }
+ }
+}
+
+function startWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, this is the fiber to unwind from.
+ currentFiber = fiber;
+ if (!beginFiberMark(fiber, null)) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = true;
+ }
+}
+
+function cancelWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // Remember we shouldn't complete measurement for this fiber.
+ // Otherwise flamechart will be deep even for small updates.
+ fiber._debugIsCurrentlyTiming = false;
+ clearFiberMark(fiber, null);
+ }
+}
+
+function stopWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber.return;
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ endFiberMark(fiber, null, null);
+ }
+}
+
+function stopFailedWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber.return;
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ var warning = fiber.tag === SuspenseComponent || fiber.tag === DehydratedSuspenseComponent ? 'Rendering was suspended' : 'An error was thrown inside this error boundary';
+ endFiberMark(fiber, null, warning);
+ }
+}
+
+function startPhaseTimer(fiber, phase) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ clearPendingPhaseMeasurement();
+ if (!beginFiberMark(fiber, phase)) {
+ return;
+ }
+ currentPhaseFiber = fiber;
+ currentPhase = phase;
+ }
+}
+
+function stopPhaseTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ var warning = hasScheduledUpdateInCurrentPhase ? 'Scheduled a cascading update' : null;
+ endFiberMark(currentPhaseFiber, currentPhase, warning);
+ }
+ currentPhase = null;
+ currentPhaseFiber = null;
+ }
+}
+
+function startWorkLoopTimer(nextUnitOfWork) {
+ if (enableUserTimingAPI) {
+ currentFiber = nextUnitOfWork;
+ if (!supportsUserTiming) {
+ return;
+ }
+ commitCountInCurrentWorkLoop = 0;
+ // This is top level call.
+ // Any other measurements are performed within.
+ beginMark('(React Tree Reconciliation)');
+ // Resume any measurements that were in progress during the last loop.
+ resumeTimers();
+ }
+}
+
+function stopWorkLoopTimer(interruptedBy, didCompleteRoot) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var warning = null;
+ if (interruptedBy !== null) {
+ if (interruptedBy.tag === HostRoot) {
+ warning = 'A top-level update interrupted the previous render';
+ } else {
+ var componentName = getComponentName(interruptedBy.type) || 'Unknown';
+ warning = 'An update to ' + componentName + ' interrupted the previous render';
+ }
+ } else if (commitCountInCurrentWorkLoop > 1) {
+ warning = 'There were cascading updates';
+ }
+ commitCountInCurrentWorkLoop = 0;
+ var label = didCompleteRoot ? '(React Tree Reconciliation: Completed Root)' : '(React Tree Reconciliation: Yielded)';
+ // Pause any measurements until the next loop.
+ pauseTimers();
+ endMark(label, '(React Tree Reconciliation)', warning);
+ }
+}
+
+function startCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ isCommitting = true;
+ hasScheduledUpdateInCurrentCommit = false;
+ labelsInCurrentCommit.clear();
+ beginMark('(Committing Changes)');
+ }
+}
+
+function stopCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+
+ var warning = null;
+ if (hasScheduledUpdateInCurrentCommit) {
+ warning = 'Lifecycle hook scheduled a cascading update';
+ } else if (commitCountInCurrentWorkLoop > 0) {
+ warning = 'Caused by a cascading update in earlier commit';
+ }
+ hasScheduledUpdateInCurrentCommit = false;
+ commitCountInCurrentWorkLoop++;
+ isCommitting = false;
+ labelsInCurrentCommit.clear();
+
+ endMark('(Committing Changes)', '(Committing Changes)', warning);
+ }
+}
+
+function startCommitSnapshotEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Committing Snapshot Effects)');
+ }
+}
+
+function stopCommitSnapshotEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Committing Snapshot Effects: ' + count + ' Total)', '(Committing Snapshot Effects)', null);
+ }
+}
+
+function startCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Committing Host Effects)');
+ }
+}
+
+function stopCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Committing Host Effects: ' + count + ' Total)', '(Committing Host Effects)', null);
+ }
+}
+
+function startCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Calling Lifecycle Methods)');
+ }
+}
+
+function stopCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Calling Lifecycle Methods: ' + count + ' Total)', '(Calling Lifecycle Methods)', null);
+ }
+}
+
+var valueStack = [];
+
+var index = -1;
+
+function createCursor(defaultValue) {
+ return {
+ current: defaultValue
+ };
+}
+
+function pop(cursor, fiber) {
+ if (index < 0) {
+ return;
+ }
+
+ cursor.current = valueStack[index];
+
+ valueStack[index] = null;
+
+ index--;
+}
+
+function push(cursor, value, fiber) {
+ index++;
+
+ valueStack[index] = cursor.current;
+
+ cursor.current = value;
+}
+
+var emptyContextObject = {};
+// A cursor to the current merged context object on the stack.
+var contextStackCursor = createCursor(emptyContextObject);
+// A cursor to a boolean indicating whether the context has changed.
+var didPerformWorkStackCursor = createCursor(false);
+// Keep track of the previous context object that was on the stack.
+// We use this to get access to the parent context after we have already
+// pushed the next context provider, and now need to merge their contexts.
+var previousContext = emptyContextObject;
+
+function getUnmaskedContext(workInProgress, Component, didPushOwnContextIfProvider) {
+ if (didPushOwnContextIfProvider && isContextProvider(Component)) {
+ // If the fiber is a context provider itself, when we read its context
+ // we may have already pushed its own child context on the stack. A context
+ // provider should not "see" its own child context. Therefore we read the
+ // previous (parent) context instead for a context provider.
+ return previousContext;
+ }
+ return contextStackCursor.current;
+}
+
+function cacheContext(workInProgress, unmaskedContext, maskedContext) {
+ var instance = workInProgress.stateNode;
+ instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
+ instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
+}
+
+function getMaskedContext(workInProgress, unmaskedContext) {
+ var type = workInProgress.type;
+ var contextTypes = type.contextTypes;
+ if (!contextTypes) {
+ return emptyContextObject;
+ }
+
+ // Avoid recreating masked context unless unmasked context has changed.
+ // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
+ // This may trigger infinite loops if componentWillReceiveProps calls setState.
+ var instance = workInProgress.stateNode;
+ if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) {
+ return instance.__reactInternalMemoizedMaskedChildContext;
+ }
+
+ var context = {};
+ for (var key in contextTypes) {
+ context[key] = unmaskedContext[key];
+ }
+
+ if (instance) {
+ cacheContext(workInProgress, unmaskedContext, context);
+ }
+
+ return context;
+}
+
+function hasContextChanged() {
+ return didPerformWorkStackCursor.current;
+}
+
+function isContextProvider(type) {
+ var childContextTypes = type.childContextTypes;
+ return childContextTypes !== null && childContextTypes !== undefined;
+}
+
+function popContext(fiber) {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
+
+function popTopLevelContextObject(fiber) {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
+
+function pushTopLevelContextObject(fiber, context, didChange) {
+ !(contextStackCursor.current === emptyContextObject) ? reactProdInvariant('168') : void 0;
+
+ push(contextStackCursor, context, fiber);
+ push(didPerformWorkStackCursor, didChange, fiber);
+}
+
+function processChildContext(fiber, type, parentContext) {
+ var instance = fiber.stateNode;
+ var childContextTypes = type.childContextTypes;
+
+ // TODO (bvaughn) Replace this behavior with an invariant() in the future.
+ // It has only been added in Fiber to match the (unintentional) behavior in Stack.
+ if (typeof instance.getChildContext !== 'function') {
+ return parentContext;
+ }
+
+ var childContext = void 0;
+ startPhaseTimer(fiber, 'getChildContext');
+ childContext = instance.getChildContext();
+ stopPhaseTimer();
+ for (var contextKey in childContext) {
+ !(contextKey in childContextTypes) ? reactProdInvariant('108', getComponentName(type) || 'Unknown', contextKey) : void 0;
+ }
+ return _assign({}, parentContext, childContext);
+}
+
+function pushContextProvider(workInProgress) {
+ var instance = workInProgress.stateNode;
+ // We push the context as early as possible to ensure stack integrity.
+ // If the instance does not exist yet, we will push null at first,
+ // and replace it on the stack later when invalidating the context.
+ var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyContextObject;
+
+ // Remember the parent context so we can merge with it later.
+ // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
+ previousContext = contextStackCursor.current;
+ push(contextStackCursor, memoizedMergedChildContext, workInProgress);
+ push(didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress);
+
+ return true;
+}
+
+function invalidateContextProvider(workInProgress, type, didChange) {
+ var instance = workInProgress.stateNode;
+ !instance ? reactProdInvariant('169') : void 0;
+
+ if (didChange) {
+ // Merge parent and own context.
+ // Skip this if we're not updating due to sCU.
+ // This avoids unnecessarily recomputing memoized values.
+ var mergedContext = processChildContext(workInProgress, type, previousContext);
+ instance.__reactInternalMemoizedMergedChildContext = mergedContext;
+
+ // Replace the old (or empty) context with the new one.
+ // It is important to unwind the context in the reverse order.
+ pop(didPerformWorkStackCursor, workInProgress);
+ pop(contextStackCursor, workInProgress);
+ // Now push the new context and mark that it has changed.
+ push(contextStackCursor, mergedContext, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ } else {
+ pop(didPerformWorkStackCursor, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ }
+}
+
+function findCurrentUnmaskedContext(fiber) {
+ // Currently this is only used with renderSubtreeIntoContainer; not sure if it
+ // makes sense elsewhere
+ !(isFiberMounted(fiber) && fiber.tag === ClassComponent) ? reactProdInvariant('170') : void 0;
+
+ var node = fiber;
+ do {
+ switch (node.tag) {
+ case HostRoot:
+ return node.stateNode.context;
+ case ClassComponent:
+ {
+ var Component = node.type;
+ if (isContextProvider(Component)) {
+ return node.stateNode.__reactInternalMemoizedMergedChildContext;
+ }
+ break;
+ }
+ }
+ node = node.return;
+ } while (node !== null);
+ reactProdInvariant('171');
+}
+
+var onCommitFiberRoot = null;
+var onCommitFiberUnmount = null;
+function catchErrors(fn) {
+ return function (arg) {
+ try {
+ return fn(arg);
+ } catch (err) {
+
+ }
+ };
+}
+
+var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined';
+
+function injectInternals(internals) {
+ if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
+ // No DevTools
+ return false;
+ }
+ var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
+ if (hook.isDisabled) {
+ // This isn't a real property on the hook, but it can be set to opt out
+ // of DevTools integration and associated warnings and logs.
+ // https://github.com/facebook/react/issues/3877
+ return true;
+ }
+ if (!hook.supportsFiber) {
+ return true;
+ }
+ try {
+ var rendererID = hook.inject(internals);
+ // We have successfully injected, so now it is safe to set up hooks.
+ onCommitFiberRoot = catchErrors(function (root) {
+ return hook.onCommitFiberRoot(rendererID, root);
+ });
+ onCommitFiberUnmount = catchErrors(function (fiber) {
+ return hook.onCommitFiberUnmount(rendererID, fiber);
+ });
+ } catch (err) {
+ // Catch all errors because it is unsafe to throw during initialization.
+
+ }
+ // DevTools exists
+ return true;
+}
+
+function onCommitRoot(root) {
+ if (typeof onCommitFiberRoot === 'function') {
+ onCommitFiberRoot(root);
+ }
+}
+
+function onCommitUnmount(fiber) {
+ if (typeof onCommitFiberUnmount === 'function') {
+ onCommitFiberUnmount(fiber);
+ }
+}
+
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
+// Math.pow(2, 30) - 1
+// 0b111111111111111111111111111111
+var maxSigned31BitInt = 1073741823;
+
+var NoWork = 0;
+var Never = 1;
+var Sync = maxSigned31BitInt;
+
+var UNIT_SIZE = 10;
+var MAGIC_NUMBER_OFFSET = maxSigned31BitInt - 1;
+
+// 1 unit of expiration time represents 10ms.
+function msToExpirationTime(ms) {
+ // Always add an offset so that we don't clash with the magic number for NoWork.
+ return MAGIC_NUMBER_OFFSET - (ms / UNIT_SIZE | 0);
+}
+
+function expirationTimeToMs(expirationTime) {
+ return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
+}
+
+function ceiling(num, precision) {
+ return ((num / precision | 0) + 1) * precision;
+}
+
+function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) {
+ return MAGIC_NUMBER_OFFSET - ceiling(MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE);
+}
+
+var LOW_PRIORITY_EXPIRATION = 5000;
+var LOW_PRIORITY_BATCH_SIZE = 250;
+
+function computeAsyncExpiration(currentTime) {
+ return computeExpirationBucket(currentTime, LOW_PRIORITY_EXPIRATION, LOW_PRIORITY_BATCH_SIZE);
+}
+
+// We intentionally set a higher expiration time for interactive updates in
+// dev than in production.
+//
+// If the main thread is being blocked so long that you hit the expiration,
+// it's a problem that could be solved with better scheduling.
+//
+// People will be more likely to notice this and fix it with the long
+// expiration time in development.
+//
+// In production we opt for better UX at the risk of masking scheduling
+// problems, by expiring fast.
+var HIGH_PRIORITY_EXPIRATION = 150;
+var HIGH_PRIORITY_BATCH_SIZE = 100;
+
+function computeInteractiveExpiration(currentTime) {
+ return computeExpirationBucket(currentTime, HIGH_PRIORITY_EXPIRATION, HIGH_PRIORITY_BATCH_SIZE);
+}
+
+var NoContext = 0;
+var ConcurrentMode = 1;
+var StrictMode = 2;
+var ProfileMode = 4;
+
+function FiberNode(tag, pendingProps, key, mode) {
+ // Instance
+ this.tag = tag;
+ this.key = key;
+ this.elementType = null;
+ this.type = null;
+ this.stateNode = null;
+
+ // Fiber
+ this.return = null;
+ this.child = null;
+ this.sibling = null;
+ this.index = 0;
+
+ this.ref = null;
+
+ this.pendingProps = pendingProps;
+ this.memoizedProps = null;
+ this.updateQueue = null;
+ this.memoizedState = null;
+ this.contextDependencies = null;
+
+ this.mode = mode;
+
+ // Effects
+ this.effectTag = NoEffect;
+ this.nextEffect = null;
+
+ this.firstEffect = null;
+ this.lastEffect = null;
+
+ this.expirationTime = NoWork;
+ this.childExpirationTime = NoWork;
+
+ this.alternate = null;
+
+ if (enableProfilerTimer) {
+ // Note: The following is done to avoid a v8 performance cliff.
+ //
+ // Initializing the fields below to smis and later updating them with
+ // double values will cause Fibers to end up having separate shapes.
+ // This behavior/bug has something to do with Object.preventExtension().
+ // Fortunately this only impacts DEV builds.
+ // Unfortunately it makes React unusably slow for some applications.
+ // To work around this, initialize the fields below with doubles.
+ //
+ // Learn more about this here:
+ // https://github.com/facebook/react/issues/14365
+ // https://bugs.chromium.org/p/v8/issues/detail?id=8538
+ this.actualDuration = Number.NaN;
+ this.actualStartTime = Number.NaN;
+ this.selfBaseDuration = Number.NaN;
+ this.treeBaseDuration = Number.NaN;
+
+ // It's okay to replace the initial doubles with smis after initialization.
+ // This won't trigger the performance cliff mentioned above,
+ // and it simplifies other profiler code (including DevTools).
+ this.actualDuration = 0;
+ this.actualStartTime = -1;
+ this.selfBaseDuration = 0;
+ this.treeBaseDuration = 0;
+ }
+
+
+}
+
+// This is a constructor function, rather than a POJO constructor, still
+// please ensure we do the following:
+// 1) Nobody should add any instance methods on this. Instance methods can be
+// more difficult to predict when they get optimized and they are almost
+// never inlined properly in static compilers.
+// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
+// always know when it is a fiber.
+// 3) We might want to experiment with using numeric keys since they are easier
+// to optimize in a non-JIT environment.
+// 4) We can easily go from a constructor to a createFiber object literal if that
+// is faster.
+// 5) It should be easy to port this to a C struct and keep a C implementation
+// compatible.
+var createFiber = function (tag, pendingProps, key, mode) {
+ // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
+ return new FiberNode(tag, pendingProps, key, mode);
+};
+
+function shouldConstruct(Component) {
+ var prototype = Component.prototype;
+ return !!(prototype && prototype.isReactComponent);
+}
+
+function isSimpleFunctionComponent(type) {
+ return typeof type === 'function' && !shouldConstruct(type) && type.defaultProps === undefined;
+}
+
+function resolveLazyComponentTag(Component) {
+ if (typeof Component === 'function') {
+ return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
+ } else if (Component !== undefined && Component !== null) {
+ var $$typeof = Component.$$typeof;
+ if ($$typeof === REACT_FORWARD_REF_TYPE) {
+ return ForwardRef;
+ }
+ if ($$typeof === REACT_MEMO_TYPE) {
+ return MemoComponent;
+ }
+ }
+ return IndeterminateComponent;
+}
+
+// This is used to create an alternate fiber to do work on.
+function createWorkInProgress(current, pendingProps, expirationTime) {
+ var workInProgress = current.alternate;
+ if (workInProgress === null) {
+ // We use a double buffering pooling technique because we know that we'll
+ // only ever need at most two versions of a tree. We pool the "other" unused
+ // node that we're free to reuse. This is lazily created to avoid allocating
+ // extra objects for things that are never updated. It also allow us to
+ // reclaim the extra memory if needed.
+ workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
+ workInProgress.elementType = current.elementType;
+ workInProgress.type = current.type;
+ workInProgress.stateNode = current.stateNode;
+
+ workInProgress.alternate = current;
+ current.alternate = workInProgress;
+ } else {
+ workInProgress.pendingProps = pendingProps;
+
+ // We already have an alternate.
+ // Reset the effect tag.
+ workInProgress.effectTag = NoEffect;
+
+ // The effect list is no longer valid.
+ workInProgress.nextEffect = null;
+ workInProgress.firstEffect = null;
+ workInProgress.lastEffect = null;
+
+ if (enableProfilerTimer) {
+ // We intentionally reset, rather than copy, actualDuration & actualStartTime.
+ // This prevents time from endlessly accumulating in new commits.
+ // This has the downside of resetting values for different priority renders,
+ // But works for yielding (the common case) and should support resuming.
+ workInProgress.actualDuration = 0;
+ workInProgress.actualStartTime = -1;
+ }
+ }
+
+ workInProgress.childExpirationTime = current.childExpirationTime;
+ workInProgress.expirationTime = current.expirationTime;
+
+ workInProgress.child = current.child;
+ workInProgress.memoizedProps = current.memoizedProps;
+ workInProgress.memoizedState = current.memoizedState;
+ workInProgress.updateQueue = current.updateQueue;
+ workInProgress.contextDependencies = current.contextDependencies;
+
+ // These will be overridden during the parent's reconciliation
+ workInProgress.sibling = current.sibling;
+ workInProgress.index = current.index;
+ workInProgress.ref = current.ref;
+
+ if (enableProfilerTimer) {
+ workInProgress.selfBaseDuration = current.selfBaseDuration;
+ workInProgress.treeBaseDuration = current.treeBaseDuration;
+ }
+
+ return workInProgress;
+}
+
+function createHostRootFiber(isConcurrent) {
+ var mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
+
+ if (enableProfilerTimer && isDevToolsPresent) {
+ // Always collect profile timings when DevTools are present.
+ // This enables DevTools to start capturing timing at any point–
+ // Without some nodes in the tree having empty base times.
+ mode |= ProfileMode;
+ }
+
+ return createFiber(HostRoot, null, null, mode);
+}
+
+function createFiberFromTypeAndProps(type, // React$ElementType
+key, pendingProps, owner, mode, expirationTime) {
+ var fiber = void 0;
+
+ var fiberTag = IndeterminateComponent;
+ // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
+ var resolvedType = type;
+ if (typeof type === 'function') {
+ if (shouldConstruct(type)) {
+ fiberTag = ClassComponent;
+ }
+ } else if (typeof type === 'string') {
+ fiberTag = HostComponent;
+ } else {
+ getTag: switch (type) {
+ case REACT_FRAGMENT_TYPE:
+ return createFiberFromFragment(pendingProps.children, mode, expirationTime, key);
+ case REACT_CONCURRENT_MODE_TYPE:
+ return createFiberFromMode(pendingProps, mode | ConcurrentMode | StrictMode, expirationTime, key);
+ case REACT_STRICT_MODE_TYPE:
+ return createFiberFromMode(pendingProps, mode | StrictMode, expirationTime, key);
+ case REACT_PROFILER_TYPE:
+ return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
+ case REACT_SUSPENSE_TYPE:
+ return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
+ default:
+ {
+ if (typeof type === 'object' && type !== null) {
+ switch (type.$$typeof) {
+ case REACT_PROVIDER_TYPE:
+ fiberTag = ContextProvider;
+ break getTag;
+ case REACT_CONTEXT_TYPE:
+ // This is a consumer
+ fiberTag = ContextConsumer;
+ break getTag;
+ case REACT_FORWARD_REF_TYPE:
+ fiberTag = ForwardRef;
+ break getTag;
+ case REACT_MEMO_TYPE:
+ fiberTag = MemoComponent;
+ break getTag;
+ case REACT_LAZY_TYPE:
+ fiberTag = LazyComponent;
+ resolvedType = null;
+ break getTag;
+ }
+ }
+ var info = '';
+ reactProdInvariant('130', type == null ? type : typeof type, info);
+ }
+ }
+ }
+
+ fiber = createFiber(fiberTag, pendingProps, key, mode);
+ fiber.elementType = type;
+ fiber.type = resolvedType;
+ fiber.expirationTime = expirationTime;
+
+ return fiber;
+}
+
+function createFiberFromElement(element, mode, expirationTime) {
+ var owner = null;
+ var type = element.type;
+ var key = element.key;
+ var pendingProps = element.props;
+ var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, expirationTime);
+ return fiber;
+}
+
+function createFiberFromFragment(elements, mode, expirationTime, key) {
+ var fiber = createFiber(Fragment, elements, key, mode);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromProfiler(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
+ // TODO: The Profiler fiber shouldn't have a type. It has a tag.
+ fiber.elementType = REACT_PROFILER_TYPE;
+ fiber.type = REACT_PROFILER_TYPE;
+ fiber.expirationTime = expirationTime;
+
+ return fiber;
+}
+
+function createFiberFromMode(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(Mode, pendingProps, key, mode);
+
+ // TODO: The Mode fiber shouldn't have a type. It has a tag.
+ var type = (mode & ConcurrentMode) === NoContext ? REACT_STRICT_MODE_TYPE : REACT_CONCURRENT_MODE_TYPE;
+ fiber.elementType = type;
+ fiber.type = type;
+
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromSuspense(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
+
+ // TODO: The SuspenseComponent fiber shouldn't have a type. It has a tag.
+ var type = REACT_SUSPENSE_TYPE;
+ fiber.elementType = type;
+ fiber.type = type;
+
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromText(content, mode, expirationTime) {
+ var fiber = createFiber(HostText, content, null, mode);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromHostInstanceForDeletion() {
+ var fiber = createFiber(HostComponent, null, null, NoContext);
+ // TODO: These should not need a type.
+ fiber.elementType = 'DELETED';
+ fiber.type = 'DELETED';
+ return fiber;
+}
+
+function createFiberFromPortal(portal, mode, expirationTime) {
+ var pendingProps = portal.children !== null ? portal.children : [];
+ var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
+ fiber.expirationTime = expirationTime;
+ fiber.stateNode = {
+ containerInfo: portal.containerInfo,
+ pendingChildren: null, // Used by persistent updates
+ implementation: portal.implementation
+ };
+ return fiber;
+}
+
+// Used for stashing WIP properties to replay failed work in DEV.
+
+var ReactInternals$2 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _ReactInternals$Sched$1 = ReactInternals$2.SchedulerTracing;
+var __interactionsRef = _ReactInternals$Sched$1.__interactionsRef;
+var __subscriberRef = _ReactInternals$Sched$1.__subscriberRef;
+var unstable_clear = _ReactInternals$Sched$1.unstable_clear;
+var unstable_getCurrent = _ReactInternals$Sched$1.unstable_getCurrent;
+var unstable_getThreadID = _ReactInternals$Sched$1.unstable_getThreadID;
+var unstable_subscribe = _ReactInternals$Sched$1.unstable_subscribe;
+var unstable_trace = _ReactInternals$Sched$1.unstable_trace;
+var unstable_unsubscribe = _ReactInternals$Sched$1.unstable_unsubscribe;
+var unstable_wrap = _ReactInternals$Sched$1.unstable_wrap;
+
+// TODO: This should be lifted into the renderer.
+
+
+// The following attributes are only used by interaction tracing builds.
+// They enable interactions to be associated with their async work,
+// And expose interaction metadata to the React DevTools Profiler plugin.
+// Note that these attributes are only defined when the enableSchedulerTracing flag is enabled.
+
+
+// Exported FiberRoot type includes all properties,
+// To avoid requiring potentially error-prone :any casts throughout the project.
+// Profiling properties are only safe to access in profiling builds (when enableSchedulerTracing is true).
+// The types are defined separately within this file to ensure they stay in sync.
+// (We don't have to use an inline :any cast when enableSchedulerTracing is disabled.)
+
+
+function createFiberRoot(containerInfo, isConcurrent, hydrate) {
+ // Cyclic construction. This cheats the type system right now because
+ // stateNode is any.
+ var uninitializedFiber = createHostRootFiber(isConcurrent);
+
+ var root = void 0;
+ if (enableSchedulerTracing) {
+ root = {
+ current: uninitializedFiber,
+ containerInfo: containerInfo,
+ pendingChildren: null,
+
+ earliestPendingTime: NoWork,
+ latestPendingTime: NoWork,
+ earliestSuspendedTime: NoWork,
+ latestSuspendedTime: NoWork,
+ latestPingedTime: NoWork,
+
+ pingCache: null,
+
+ didError: false,
+
+ pendingCommitExpirationTime: NoWork,
+ finishedWork: null,
+ timeoutHandle: noTimeout,
+ context: null,
+ pendingContext: null,
+ hydrate: hydrate,
+ nextExpirationTimeToWorkOn: NoWork,
+ expirationTime: NoWork,
+ firstBatch: null,
+ nextScheduledRoot: null,
+
+ interactionThreadID: unstable_getThreadID(),
+ memoizedInteractions: new Set(),
+ pendingInteractionMap: new Map()
+ };
+ } else {
+ root = {
+ current: uninitializedFiber,
+ containerInfo: containerInfo,
+ pendingChildren: null,
+
+ pingCache: null,
+
+ earliestPendingTime: NoWork,
+ latestPendingTime: NoWork,
+ earliestSuspendedTime: NoWork,
+ latestSuspendedTime: NoWork,
+ latestPingedTime: NoWork,
+
+ didError: false,
+
+ pendingCommitExpirationTime: NoWork,
+ finishedWork: null,
+ timeoutHandle: noTimeout,
+ context: null,
+ pendingContext: null,
+ hydrate: hydrate,
+ nextExpirationTimeToWorkOn: NoWork,
+ expirationTime: NoWork,
+ firstBatch: null,
+ nextScheduledRoot: null
+ };
+ }
+
+ uninitializedFiber.stateNode = root;
+
+ // The reason for the way the Flow types are structured in this file,
+ // Is to avoid needing :any casts everywhere interaction tracing fields are used.
+ // Unfortunately that requires an :any cast for non-interaction tracing capable builds.
+ // $FlowFixMe Remove this :any cast and replace it with something better.
+ return root;
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+// This lets us hook into Fiber to debug what it's doing.
+// See https://github.com/facebook/react/pull/8033.
+// This is not part of the public API, not even for React DevTools.
+// You may only inject a debugTool if you work on React Fiber itself.
+
+// TODO: Offscreen updates should never suspend. However, a promise that
+// suspended inside an offscreen subtree should be able to ping at the priority
+// of the outer render.
+
+function markPendingPriorityLevel(root, expirationTime) {
+ // If there's a gap between completing a failed root and retrying it,
+ // additional updates may be scheduled. Clear `didError`, in case the update
+ // is sufficient to fix the error.
+ root.didError = false;
+
+ // Update the latest and earliest pending times
+ var earliestPendingTime = root.earliestPendingTime;
+ if (earliestPendingTime === NoWork) {
+ // No other pending updates.
+ root.earliestPendingTime = root.latestPendingTime = expirationTime;
+ } else {
+ if (earliestPendingTime < expirationTime) {
+ // This is the earliest pending update.
+ root.earliestPendingTime = expirationTime;
+ } else {
+ var latestPendingTime = root.latestPendingTime;
+ if (latestPendingTime > expirationTime) {
+ // This is the latest pending update
+ root.latestPendingTime = expirationTime;
+ }
+ }
+ }
+ findNextExpirationTimeToWorkOn(expirationTime, root);
+}
+
+function markCommittedPriorityLevels(root, earliestRemainingTime) {
+ root.didError = false;
+
+ if (earliestRemainingTime === NoWork) {
+ // Fast path. There's no remaining work. Clear everything.
+ root.earliestPendingTime = NoWork;
+ root.latestPendingTime = NoWork;
+ root.earliestSuspendedTime = NoWork;
+ root.latestSuspendedTime = NoWork;
+ root.latestPingedTime = NoWork;
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ if (earliestRemainingTime < root.latestPingedTime) {
+ root.latestPingedTime = NoWork;
+ }
+
+ // Let's see if the previous latest known pending level was just flushed.
+ var latestPendingTime = root.latestPendingTime;
+ if (latestPendingTime !== NoWork) {
+ if (latestPendingTime > earliestRemainingTime) {
+ // We've flushed all the known pending levels.
+ root.earliestPendingTime = root.latestPendingTime = NoWork;
+ } else {
+ var earliestPendingTime = root.earliestPendingTime;
+ if (earliestPendingTime > earliestRemainingTime) {
+ // We've flushed the earliest known pending level. Set this to the
+ // latest pending time.
+ root.earliestPendingTime = root.latestPendingTime;
+ }
+ }
+ }
+
+ // Now let's handle the earliest remaining level in the whole tree. We need to
+ // decide whether to treat it as a pending level or as suspended. Check
+ // it falls within the range of known suspended levels.
+
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ if (earliestSuspendedTime === NoWork) {
+ // There's no suspended work. Treat the earliest remaining level as a
+ // pending level.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ var latestSuspendedTime = root.latestSuspendedTime;
+ if (earliestRemainingTime < latestSuspendedTime) {
+ // The earliest remaining level is later than all the suspended work. That
+ // means we've flushed all the suspended work.
+ root.earliestSuspendedTime = NoWork;
+ root.latestSuspendedTime = NoWork;
+ root.latestPingedTime = NoWork;
+
+ // There's no suspended work. Treat the earliest remaining level as a
+ // pending level.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ if (earliestRemainingTime > earliestSuspendedTime) {
+ // The earliest remaining time is earlier than all the suspended work.
+ // Treat it as a pending update.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ // The earliest remaining time falls within the range of known suspended
+ // levels. We should treat this as suspended work.
+ findNextExpirationTimeToWorkOn(NoWork, root);
+}
+
+function hasLowerPriorityWork(root, erroredExpirationTime) {
+ var latestPendingTime = root.latestPendingTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ var latestPingedTime = root.latestPingedTime;
+ return latestPendingTime !== NoWork && latestPendingTime < erroredExpirationTime || latestSuspendedTime !== NoWork && latestSuspendedTime < erroredExpirationTime || latestPingedTime !== NoWork && latestPingedTime < erroredExpirationTime;
+}
+
+function isPriorityLevelSuspended(root, expirationTime) {
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ return earliestSuspendedTime !== NoWork && expirationTime <= earliestSuspendedTime && expirationTime >= latestSuspendedTime;
+}
+
+function markSuspendedPriorityLevel(root, suspendedTime) {
+ root.didError = false;
+ clearPing(root, suspendedTime);
+
+ // First, check the known pending levels and update them if needed.
+ var earliestPendingTime = root.earliestPendingTime;
+ var latestPendingTime = root.latestPendingTime;
+ if (earliestPendingTime === suspendedTime) {
+ if (latestPendingTime === suspendedTime) {
+ // Both known pending levels were suspended. Clear them.
+ root.earliestPendingTime = root.latestPendingTime = NoWork;
+ } else {
+ // The earliest pending level was suspended. Clear by setting it to the
+ // latest pending level.
+ root.earliestPendingTime = latestPendingTime;
+ }
+ } else if (latestPendingTime === suspendedTime) {
+ // The latest pending level was suspended. Clear by setting it to the
+ // latest pending level.
+ root.latestPendingTime = earliestPendingTime;
+ }
+
+ // Finally, update the known suspended levels.
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ if (earliestSuspendedTime === NoWork) {
+ // No other suspended levels.
+ root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
+ } else {
+ if (earliestSuspendedTime < suspendedTime) {
+ // This is the earliest suspended level.
+ root.earliestSuspendedTime = suspendedTime;
+ } else if (latestSuspendedTime > suspendedTime) {
+ // This is the latest suspended level
+ root.latestSuspendedTime = suspendedTime;
+ }
+ }
+
+ findNextExpirationTimeToWorkOn(suspendedTime, root);
+}
+
+function markPingedPriorityLevel(root, pingedTime) {
+ root.didError = false;
+
+ // TODO: When we add back resuming, we need to ensure the progressed work
+ // is thrown out and not reused during the restarted render. One way to
+ // invalidate the progressed work is to restart at expirationTime + 1.
+ var latestPingedTime = root.latestPingedTime;
+ if (latestPingedTime === NoWork || latestPingedTime > pingedTime) {
+ root.latestPingedTime = pingedTime;
+ }
+ findNextExpirationTimeToWorkOn(pingedTime, root);
+}
+
+function clearPing(root, completedTime) {
+ var latestPingedTime = root.latestPingedTime;
+ if (latestPingedTime >= completedTime) {
+ root.latestPingedTime = NoWork;
+ }
+}
+
+function findEarliestOutstandingPriorityLevel(root, renderExpirationTime) {
+ var earliestExpirationTime = renderExpirationTime;
+
+ var earliestPendingTime = root.earliestPendingTime;
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ if (earliestPendingTime > earliestExpirationTime) {
+ earliestExpirationTime = earliestPendingTime;
+ }
+ if (earliestSuspendedTime > earliestExpirationTime) {
+ earliestExpirationTime = earliestSuspendedTime;
+ }
+ return earliestExpirationTime;
+}
+
+function didExpireAtExpirationTime(root, currentTime) {
+ var expirationTime = root.expirationTime;
+ if (expirationTime !== NoWork && currentTime <= expirationTime) {
+ // The root has expired. Flush all work up to the current time.
+ root.nextExpirationTimeToWorkOn = currentTime;
+ }
+}
+
+function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ var earliestPendingTime = root.earliestPendingTime;
+ var latestPingedTime = root.latestPingedTime;
+
+ // Work on the earliest pending time. Failing that, work on the latest
+ // pinged time.
+ var nextExpirationTimeToWorkOn = earliestPendingTime !== NoWork ? earliestPendingTime : latestPingedTime;
+
+ // If there is no pending or pinged work, check if there's suspended work
+ // that's lower priority than what we just completed.
+ if (nextExpirationTimeToWorkOn === NoWork && (completedExpirationTime === NoWork || latestSuspendedTime < completedExpirationTime)) {
+ // The lowest priority suspended work is the work most likely to be
+ // committed next. Let's start rendering it again, so that if it times out,
+ // it's ready to commit.
+ nextExpirationTimeToWorkOn = latestSuspendedTime;
+ }
+
+ var expirationTime = nextExpirationTimeToWorkOn;
+ if (expirationTime !== NoWork && earliestSuspendedTime > expirationTime) {
+ // Expire using the earliest known expiration time.
+ expirationTime = earliestSuspendedTime;
+ }
+
+ root.nextExpirationTimeToWorkOn = nextExpirationTimeToWorkOn;
+ root.expirationTime = expirationTime;
+}
+
+function resolveDefaultProps(Component, baseProps) {
+ if (Component && Component.defaultProps) {
+ // Resolve default props. Taken from ReactElement
+ var props = _assign({}, baseProps);
+ var defaultProps = Component.defaultProps;
+ for (var propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
+ }
+ return props;
+ }
+ return baseProps;
+}
+
+function readLazyComponentType(lazyComponent) {
+ var status = lazyComponent._status;
+ var result = lazyComponent._result;
+ switch (status) {
+ case Resolved:
+ {
+ var Component = result;
+ return Component;
+ }
+ case Rejected:
+ {
+ var error = result;
+ throw error;
+ }
+ case Pending:
+ {
+ var thenable = result;
+ throw thenable;
+ }
+ default:
+ {
+ lazyComponent._status = Pending;
+ var ctor = lazyComponent._ctor;
+ var _thenable = ctor();
+ _thenable.then(function (moduleObject) {
+ if (lazyComponent._status === Pending) {
+ var defaultExport = moduleObject.default;
+ lazyComponent._status = Resolved;
+ lazyComponent._result = defaultExport;
+ }
+ }, function (error) {
+ if (lazyComponent._status === Pending) {
+ lazyComponent._status = Rejected;
+ lazyComponent._result = error;
+ }
+ });
+ // Handle synchronous thenables.
+ switch (lazyComponent._status) {
+ case Resolved:
+ return lazyComponent._result;
+ case Rejected:
+ throw lazyComponent._result;
+ }
+ lazyComponent._result = _thenable;
+ throw _thenable;
+ }
+ }
+}
+
+// React.Component uses a shared frozen object by default.
+// We'll use it to determine whether we need to initialize legacy refs.
+var emptyRefsObject = new React.Component().refs;
+
+function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) {
+ var prevState = workInProgress.memoizedState;
+
+ var partialState = getDerivedStateFromProps(nextProps, prevState);
+
+ var memoizedState = partialState === null || partialState === undefined ? prevState : _assign({}, prevState, partialState);
+ workInProgress.memoizedState = memoizedState;
+
+ // Once the update queue is empty, persist the derived state onto the
+ // base state.
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null && workInProgress.expirationTime === NoWork) {
+ updateQueue.baseState = memoizedState;
+ }
+}
+
+var classComponentUpdater = {
+ isMounted: isMounted,
+ enqueueSetState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.payload = payload;
+ if (callback !== undefined && callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueReplaceState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.tag = ReplaceState;
+ update.payload = payload;
+
+ if (callback !== undefined && callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueForceUpdate: function (inst, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.tag = ForceUpdate;
+
+ if (callback !== undefined && callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ }
+};
+
+function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
+ var instance = workInProgress.stateNode;
+ if (typeof instance.shouldComponentUpdate === 'function') {
+ startPhaseTimer(workInProgress, 'shouldComponentUpdate');
+ var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
+ stopPhaseTimer();
+
+ return shouldUpdate;
+ }
+
+ if (ctor.prototype && ctor.prototype.isPureReactComponent) {
+ return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState);
+ }
+
+ return true;
+}
+
+function adoptClassInstance(workInProgress, instance) {
+ instance.updater = classComponentUpdater;
+ workInProgress.stateNode = instance;
+ // The instance needs access to the fiber so that it can schedule updates
+ set(instance, workInProgress);
+
+}
+
+function constructClassInstance(workInProgress, ctor, props, renderExpirationTime) {
+ var isLegacyContextConsumer = false;
+ var unmaskedContext = emptyContextObject;
+ var context = null;
+ var contextType = ctor.contextType;
+
+ if (typeof contextType === 'object' && contextType !== null) {
+ context = readContext(contextType);
+ } else {
+ unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ var contextTypes = ctor.contextTypes;
+ isLegacyContextConsumer = contextTypes !== null && contextTypes !== undefined;
+ context = isLegacyContextConsumer ? getMaskedContext(workInProgress, unmaskedContext) : emptyContextObject;
+ }
+
+ // Instantiate twice to help detect side-effects.
+ var instance = new ctor(props, context);
+ var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
+ adoptClassInstance(workInProgress, instance);
+
+ if (isLegacyContextConsumer) {
+ cacheContext(workInProgress, unmaskedContext, context);
+ }
+
+ return instance;
+}
+
+function callComponentWillMount(workInProgress, instance) {
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ var oldState = instance.state;
+
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
+ }
+
+ stopPhaseTimer();
+
+ if (oldState !== instance.state) {
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
+
+function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) {
+ var oldState = instance.state;
+ startPhaseTimer(workInProgress, 'componentWillReceiveProps');
+ if (typeof instance.componentWillReceiveProps === 'function') {
+ instance.componentWillReceiveProps(newProps, nextContext);
+ }
+ if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
+ instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
+ }
+ stopPhaseTimer();
+
+ if (instance.state !== oldState) {
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
+
+// Invokes the mount life-cycles on a previously never rendered instance.
+function mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+ instance.props = newProps;
+ instance.state = workInProgress.memoizedState;
+ instance.refs = emptyRefsObject;
+
+ var contextType = ctor.contextType;
+ if (typeof contextType === 'object' && contextType !== null) {
+ instance.context = readContext(contextType);
+ } else {
+ var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ instance.context = getMaskedContext(workInProgress, unmaskedContext);
+ }
+
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ instance.state = workInProgress.memoizedState;
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ instance.state = workInProgress.memoizedState;
+ }
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
+ callComponentWillMount(workInProgress, instance);
+ // If we had additional state updates during this life-cycle, let's
+ // process them now.
+ updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ instance.state = workInProgress.memoizedState;
+ }
+ }
+
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+}
+
+function resumeMountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+
+ var oldProps = workInProgress.memoizedProps;
+ instance.props = oldProps;
+
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = void 0;
+ if (typeof contextType === 'object' && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
+ if (oldProps !== newProps || oldContext !== nextContext) {
+ callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
+ }
+ }
+
+ resetHasForceUpdateBeforeProcessing();
+
+ var oldState = workInProgress.memoizedState;
+ var newState = instance.state = oldState;
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ newState = workInProgress.memoizedState;
+ }
+ if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ return false;
+ }
+
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ newState = workInProgress.memoizedState;
+ }
+
+ var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
+ }
+ stopPhaseTimer();
+ }
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ }
+
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+
+ return shouldUpdate;
+}
+
+// Invokes the update life-cycles and returns false if it shouldn't rerender.
+function updateClassInstance(current, workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+
+ var oldProps = workInProgress.memoizedProps;
+ instance.props = workInProgress.type === workInProgress.elementType ? oldProps : resolveDefaultProps(workInProgress.type, oldProps);
+
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = void 0;
+ if (typeof contextType === 'object' && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
+ if (oldProps !== newProps || oldContext !== nextContext) {
+ callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
+ }
+ }
+
+ resetHasForceUpdateBeforeProcessing();
+
+ var oldState = workInProgress.memoizedState;
+ var newState = instance.state = oldState;
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ newState = workInProgress.memoizedState;
+ }
+
+ if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Update;
+ }
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Snapshot;
+ }
+ }
+ return false;
+ }
+
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ newState = workInProgress.memoizedState;
+ }
+
+ var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function')) {
+ startPhaseTimer(workInProgress, 'componentWillUpdate');
+ if (typeof instance.componentWillUpdate === 'function') {
+ instance.componentWillUpdate(newProps, newState, nextContext);
+ }
+ if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
+ instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
+ }
+ stopPhaseTimer();
+ }
+ if (typeof instance.componentDidUpdate === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ workInProgress.effectTag |= Snapshot;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Update;
+ }
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Snapshot;
+ }
+ }
+
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized props/state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ }
+
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+
+ return shouldUpdate;
+}
+
+var isArray = Array.isArray;
+
+function coerceRef(returnFiber, current$$1, element) {
+ var mixedRef = element.ref;
+ if (mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object') {
+ if (element._owner) {
+ var owner = element._owner;
+ var inst = void 0;
+ if (owner) {
+ var ownerFiber = owner;
+ !(ownerFiber.tag === ClassComponent) ? reactProdInvariant('309') : void 0;
+ inst = ownerFiber.stateNode;
+ }
+ !inst ? reactProdInvariant('147', mixedRef) : void 0;
+ var stringRef = '' + mixedRef;
+ // Check if previous string ref matches new string ref
+ if (current$$1 !== null && current$$1.ref !== null && typeof current$$1.ref === 'function' && current$$1.ref._stringRef === stringRef) {
+ return current$$1.ref;
+ }
+ var ref = function (value) {
+ var refs = inst.refs;
+ if (refs === emptyRefsObject) {
+ // This is a lazy pooled frozen object, so we need to initialize.
+ refs = inst.refs = {};
+ }
+ if (value === null) {
+ delete refs[stringRef];
+ } else {
+ refs[stringRef] = value;
+ }
+ };
+ ref._stringRef = stringRef;
+ return ref;
+ } else {
+ !(typeof mixedRef === 'string') ? reactProdInvariant('284') : void 0;
+ !element._owner ? reactProdInvariant('290', mixedRef) : void 0;
+ }
+ }
+ return mixedRef;
+}
+
+function throwOnInvalidObjectType(returnFiber, newChild) {
+ if (returnFiber.type !== 'textarea') {
+ var addendum = '';
+ reactProdInvariant('31', Object.prototype.toString.call(newChild) === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : newChild, addendum);
+ }
+}
+
+// This wrapper function exists because I expect to clone the code in each path
+// to be able to optimize each path individually by branching early. This needs
+// a compiler or we can do it manually. Helpers that don't need this branching
+// live outside of this function.
+function ChildReconciler(shouldTrackSideEffects) {
+ function deleteChild(returnFiber, childToDelete) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return;
+ }
+ // Deletions are added in reversed order so we add it to the front.
+ // At this point, the return fiber's effect list is empty except for
+ // deletions, so we can just append the deletion to the list. The remaining
+ // effects aren't added until the complete phase. Once we implement
+ // resuming, this may not be true.
+ var last = returnFiber.lastEffect;
+ if (last !== null) {
+ last.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+ childToDelete.nextEffect = null;
+ childToDelete.effectTag = Deletion;
+ }
+
+ function deleteRemainingChildren(returnFiber, currentFirstChild) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return null;
+ }
+
+ // TODO: For the shouldClone case, this could be micro-optimized a bit by
+ // assuming that after the first child we've already added everything.
+ var childToDelete = currentFirstChild;
+ while (childToDelete !== null) {
+ deleteChild(returnFiber, childToDelete);
+ childToDelete = childToDelete.sibling;
+ }
+ return null;
+ }
+
+ function mapRemainingChildren(returnFiber, currentFirstChild) {
+ // Add the remaining children to a temporary map so that we can find them by
+ // keys quickly. Implicit (null) keys get added to this set with their index
+ var existingChildren = new Map();
+
+ var existingChild = currentFirstChild;
+ while (existingChild !== null) {
+ if (existingChild.key !== null) {
+ existingChildren.set(existingChild.key, existingChild);
+ } else {
+ existingChildren.set(existingChild.index, existingChild);
+ }
+ existingChild = existingChild.sibling;
+ }
+ return existingChildren;
+ }
+
+ function useFiber(fiber, pendingProps, expirationTime) {
+ // We currently set sibling to null and index to 0 here because it is easy
+ // to forget to do before returning it. E.g. for the single child case.
+ var clone = createWorkInProgress(fiber, pendingProps, expirationTime);
+ clone.index = 0;
+ clone.sibling = null;
+ return clone;
+ }
+
+ function placeChild(newFiber, lastPlacedIndex, newIndex) {
+ newFiber.index = newIndex;
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return lastPlacedIndex;
+ }
+ var current$$1 = newFiber.alternate;
+ if (current$$1 !== null) {
+ var oldIndex = current$$1.index;
+ if (oldIndex < lastPlacedIndex) {
+ // This is a move.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
+ } else {
+ // This item can stay in place.
+ return oldIndex;
+ }
+ } else {
+ // This is an insertion.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
+ }
+ }
+
+ function placeSingleChild(newFiber) {
+ // This is simpler for the single child case. We only need to do a
+ // placement for inserting new children.
+ if (shouldTrackSideEffects && newFiber.alternate === null) {
+ newFiber.effectTag = Placement;
+ }
+ return newFiber;
+ }
+
+ function updateTextNode(returnFiber, current$$1, textContent, expirationTime) {
+ if (current$$1 === null || current$$1.tag !== HostText) {
+ // Insert
+ var created = createFiberFromText(textContent, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, textContent, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function updateElement(returnFiber, current$$1, element, expirationTime) {
+ if (current$$1 !== null && current$$1.elementType === element.type) {
+ // Move based on index
+ var existing = useFiber(current$$1, element.props, expirationTime);
+ existing.ref = coerceRef(returnFiber, current$$1, element);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ // Insert
+ var created = createFiberFromElement(element, returnFiber.mode, expirationTime);
+ created.ref = coerceRef(returnFiber, current$$1, element);
+ created.return = returnFiber;
+ return created;
+ }
+ }
+
+ function updatePortal(returnFiber, current$$1, portal, expirationTime) {
+ if (current$$1 === null || current$$1.tag !== HostPortal || current$$1.stateNode.containerInfo !== portal.containerInfo || current$$1.stateNode.implementation !== portal.implementation) {
+ // Insert
+ var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, portal.children || [], expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function updateFragment(returnFiber, current$$1, fragment, expirationTime, key) {
+ if (current$$1 === null || current$$1.tag !== Fragment) {
+ // Insert
+ var created = createFiberFromFragment(fragment, returnFiber.mode, expirationTime, key);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, fragment, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function createChild(returnFiber, newChild, expirationTime) {
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ var created = createFiberFromText('' + newChild, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ var _created = createFiberFromElement(newChild, returnFiber.mode, expirationTime);
+ _created.ref = coerceRef(returnFiber, null, newChild);
+ _created.return = returnFiber;
+ return _created;
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ var _created2 = createFiberFromPortal(newChild, returnFiber.mode, expirationTime);
+ _created2.return = returnFiber;
+ return _created2;
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _created3 = createFiberFromFragment(newChild, returnFiber.mode, expirationTime, null);
+ _created3.return = returnFiber;
+ return _created3;
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ return null;
+ }
+
+ function updateSlot(returnFiber, oldFiber, newChild, expirationTime) {
+ // Update the fiber if the keys match, otherwise return null.
+
+ var key = oldFiber !== null ? oldFiber.key : null;
+
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ if (key !== null) {
+ return null;
+ }
+ return updateTextNode(returnFiber, oldFiber, '' + newChild, expirationTime);
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ if (newChild.key === key) {
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(returnFiber, oldFiber, newChild.props.children, expirationTime, key);
+ }
+ return updateElement(returnFiber, oldFiber, newChild, expirationTime);
+ } else {
+ return null;
+ }
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ if (newChild.key === key) {
+ return updatePortal(returnFiber, oldFiber, newChild, expirationTime);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ if (key !== null) {
+ return null;
+ }
+
+ return updateFragment(returnFiber, oldFiber, newChild, expirationTime, null);
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ return null;
+ }
+
+ function updateFromMap(existingChildren, returnFiber, newIdx, newChild, expirationTime) {
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys, so we neither have to check the old nor
+ // new node for the key. If both are text nodes, they match.
+ var matchedFiber = existingChildren.get(newIdx) || null;
+ return updateTextNode(returnFiber, matchedFiber, '' + newChild, expirationTime);
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(returnFiber, _matchedFiber, newChild.props.children, expirationTime, newChild.key);
+ }
+ return updateElement(returnFiber, _matchedFiber, newChild, expirationTime);
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
+ return updatePortal(returnFiber, _matchedFiber2, newChild, expirationTime);
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _matchedFiber3 = existingChildren.get(newIdx) || null;
+ return updateFragment(returnFiber, _matchedFiber3, newChild, expirationTime, null);
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ return null;
+ }
+
+ /**
+ * Warns if there is a duplicate or missing key
+ */
+ function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) {
+ // This algorithm can't optimize by searching from both ends since we
+ // don't have backpointers on fibers. I'm trying to see how far we can get
+ // with that model. If it ends up not being worth the tradeoffs, we can
+ // add it later.
+
+ // Even with a two ended optimization, we'd want to optimize for the case
+ // where there are few changes and brute force the comparison instead of
+ // going for the Map. It'd like to explore hitting that path first in
+ // forward-only mode and only go for the Map once we notice that we need
+ // lots of look ahead. This doesn't handle reversal as well as two ended
+ // search but that's unusual. Besides, for the two ended optimization to
+ // work on Iterables, we'd need to copy the whole set.
+
+ // In this first iteration, we'll just live with hitting the bad case
+ // (adding everything to a Map) in for every insert/move.
+
+ // If you change this code, also update reconcileChildrenIterator() which
+ // uses the same algorithm.
+
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+ for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], expirationTime);
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (oldFiber === null) {
+ oldFiber = nextOldFiber;
+ }
+ break;
+ }
+ if (shouldTrackSideEffects) {
+ if (oldFiber && newFiber.alternate === null) {
+ // We matched the slot, but we didn't reuse the existing fiber, so we
+ // need to delete the existing child.
+ deleteChild(returnFiber, oldFiber);
+ }
+ }
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = newFiber;
+ } else {
+ // TODO: Defer siblings if we're not at the right index for this slot.
+ // I.e. if we had null values before, then we want to defer this
+ // for each null value. However, we also don't want to call updateSlot
+ // with the previous one.
+ previousNewFiber.sibling = newFiber;
+ }
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
+
+ if (newIdx === newChildren.length) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
+
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber = createChild(returnFiber, newChildren[newIdx], expirationTime);
+ if (!_newFiber) {
+ continue;
+ }
+ lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber;
+ } else {
+ previousNewFiber.sibling = _newFiber;
+ }
+ previousNewFiber = _newFiber;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime);
+ if (_newFiber2) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber2.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key);
+ }
+ }
+ lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber2;
+ } else {
+ previousNewFiber.sibling = _newFiber2;
+ }
+ previousNewFiber = _newFiber2;
+ }
+ }
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function (child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, expirationTime) {
+ // This is the same implementation as reconcileChildrenArray(),
+ // but using the iterator instead.
+
+ var iteratorFn = getIteratorFn(newChildrenIterable);
+ !(typeof iteratorFn === 'function') ? reactProdInvariant('150') : void 0;
+
+ var newChildren = iteratorFn.call(newChildrenIterable);
+ !(newChildren != null) ? reactProdInvariant('151') : void 0;
+
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+
+ var step = newChildren.next();
+ for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(returnFiber, oldFiber, step.value, expirationTime);
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (!oldFiber) {
+ oldFiber = nextOldFiber;
+ }
+ break;
+ }
+ if (shouldTrackSideEffects) {
+ if (oldFiber && newFiber.alternate === null) {
+ // We matched the slot, but we didn't reuse the existing fiber, so we
+ // need to delete the existing child.
+ deleteChild(returnFiber, oldFiber);
+ }
+ }
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = newFiber;
+ } else {
+ // TODO: Defer siblings if we're not at the right index for this slot.
+ // I.e. if we had null values before, then we want to defer this
+ // for each null value. However, we also don't want to call updateSlot
+ // with the previous one.
+ previousNewFiber.sibling = newFiber;
+ }
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
+
+ if (step.done) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
+
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber3 = createChild(returnFiber, step.value, expirationTime);
+ if (_newFiber3 === null) {
+ continue;
+ }
+ lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber3;
+ } else {
+ previousNewFiber.sibling = _newFiber3;
+ }
+ previousNewFiber = _newFiber3;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, expirationTime);
+ if (_newFiber4 !== null) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber4.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(_newFiber4.key === null ? newIdx : _newFiber4.key);
+ }
+ }
+ lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber4;
+ } else {
+ previousNewFiber.sibling = _newFiber4;
+ }
+ previousNewFiber = _newFiber4;
+ }
+ }
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function (child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, expirationTime) {
+ // There's no need to check for keys on text nodes since we don't have a
+ // way to define them.
+ if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
+ // We already have an existing node so let's just update it and delete
+ // the rest.
+ deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
+ var existing = useFiber(currentFirstChild, textContent, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ // The existing first child is not a text node so we need to create one
+ // and delete the existing ones.
+ deleteRemainingChildren(returnFiber, currentFirstChild);
+ var created = createFiberFromText(textContent, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
+ var key = element.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime);
+ existing.ref = coerceRef(returnFiber, child, element);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ if (element.type === REACT_FRAGMENT_TYPE) {
+ var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key);
+ created.return = returnFiber;
+ return created;
+ } else {
+ var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime);
+ _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
+ _created4.return = returnFiber;
+ return _created4;
+ }
+ }
+
+ function reconcileSinglePortal(returnFiber, currentFirstChild, portal, expirationTime) {
+ var key = portal.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, portal.children || [], expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ // This API will tag the children with the side-effect of the reconciliation
+ // itself. They will be added to the side-effect list as we pass through the
+ // children and the parent.
+ function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
+ // This function is not recursive.
+ // If the top level item is an array, we treat it as a set of children,
+ // not as a fragment. Nested arrays on the other hand will be treated as
+ // fragment nodes. Recursion happens at the normal flow.
+
+ // Handle top level unkeyed fragments as if they were arrays.
+ // This leads to an ambiguity between <>{[...]}</> and <>...</>.
+ // We treat the ambiguous cases above the same.
+ var isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null;
+ if (isUnkeyedTopLevelFragment) {
+ newChild = newChild.props.children;
+ }
+
+ // Handle object types
+ var isObject = typeof newChild === 'object' && newChild !== null;
+
+ if (isObject) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime));
+ case REACT_PORTAL_TYPE:
+ return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime));
+ }
+ }
+
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime));
+ }
+
+ if (isArray(newChild)) {
+ return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime);
+ }
+
+ if (getIteratorFn(newChild)) {
+ return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime);
+ }
+
+ if (isObject) {
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
+ // If the new child is undefined, and the return fiber is a composite
+ // component, throw an error. If Fiber return types are disabled,
+ // we already threw above.
+ switch (returnFiber.tag) {
+ case ClassComponent:
+ {
+
+ }
+ // Intentionally fall through to the next case, which handles both
+ // functions and classes
+ // eslint-disable-next-lined no-fallthrough
+ case FunctionComponent:
+ {
+ var Component = returnFiber.type;
+ reactProdInvariant('152', Component.displayName || Component.name || 'Component');
+ }
+ }
+ }
+
+ // Remaining cases are all treated as empty.
+ return deleteRemainingChildren(returnFiber, currentFirstChild);
+ }
+
+ return reconcileChildFibers;
+}
+
+var reconcileChildFibers = ChildReconciler(true);
+var mountChildFibers = ChildReconciler(false);
+
+function cloneChildFibers(current$$1, workInProgress) {
+ !(current$$1 === null || workInProgress.child === current$$1.child) ? reactProdInvariant('153') : void 0;
+
+ if (workInProgress.child === null) {
+ return;
+ }
+
+ var currentChild = workInProgress.child;
+ var newChild = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
+ workInProgress.child = newChild;
+
+ newChild.return = workInProgress;
+ while (currentChild.sibling !== null) {
+ currentChild = currentChild.sibling;
+ newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
+ newChild.return = workInProgress;
+ }
+ newChild.sibling = null;
+}
+
+var NO_CONTEXT = {};
+
+var contextStackCursor$1 = createCursor(NO_CONTEXT);
+var contextFiberStackCursor = createCursor(NO_CONTEXT);
+var rootInstanceStackCursor = createCursor(NO_CONTEXT);
+
+function requiredContext(c) {
+ !(c !== NO_CONTEXT) ? reactProdInvariant('174') : void 0;
+ return c;
+}
+
+function getRootHostContainer() {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ return rootInstance;
+}
+
+function pushHostContainer(fiber, nextRootInstance) {
+ // Push current root instance onto the stack;
+ // This allows us to reset root when portals are popped.
+ push(rootInstanceStackCursor, nextRootInstance, fiber);
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+
+ // Finally, we need to push the host context to the stack.
+ // However, we can't just call getRootHostContext() and push it because
+ // we'd have a different number of entries on the stack depending on
+ // whether getRootHostContext() throws somewhere in renderer code or not.
+ // So we push an empty value first. This lets us safely unwind on errors.
+ push(contextStackCursor$1, NO_CONTEXT, fiber);
+ var nextRootContext = getRootHostContext(nextRootInstance);
+ // Now that we know this function doesn't throw, replace it.
+ pop(contextStackCursor$1, fiber);
+ push(contextStackCursor$1, nextRootContext, fiber);
+}
+
+function popHostContainer(fiber) {
+ pop(contextStackCursor$1, fiber);
+ pop(contextFiberStackCursor, fiber);
+ pop(rootInstanceStackCursor, fiber);
+}
+
+function getHostContext() {
+ var context = requiredContext(contextStackCursor$1.current);
+ return context;
+}
+
+function pushHostContext(fiber) {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ var context = requiredContext(contextStackCursor$1.current);
+ var nextContext = getChildHostContext(context, fiber.type, rootInstance);
+
+ // Don't push this Fiber's context unless it's unique.
+ if (context === nextContext) {
+ return;
+ }
+
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+ push(contextStackCursor$1, nextContext, fiber);
+}
+
+function popHostContext(fiber) {
+ // Do not pop unless this Fiber provided the current context.
+ // pushHostContext() only pushes Fibers that provide unique contexts.
+ if (contextFiberStackCursor.current !== fiber) {
+ return;
+ }
+
+ pop(contextStackCursor$1, fiber);
+ pop(contextFiberStackCursor, fiber);
+}
+
+var NoEffect$1 = /* */0;
+var UnmountSnapshot = /* */2;
+var UnmountMutation = /* */4;
+var MountMutation = /* */8;
+var UnmountLayout = /* */16;
+var MountLayout = /* */32;
+var MountPassive = /* */64;
+var UnmountPassive = /* */128;
+
+var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher;
+
+
+// These are set right before calling the component.
+var renderExpirationTime = NoWork;
+// The work-in-progress fiber. I've named it differently to distinguish it from
+// the work-in-progress hook.
+var currentlyRenderingFiber$1 = null;
+
+// Hooks are stored as a linked list on the fiber's memoizedState field. The
+// current hook list is the list that belongs to the current fiber. The
+// work-in-progress hook list is a new list that will be added to the
+// work-in-progress fiber.
+var currentHook = null;
+var nextCurrentHook = null;
+var firstWorkInProgressHook = null;
+var workInProgressHook = null;
+var nextWorkInProgressHook = null;
+
+var remainingExpirationTime = NoWork;
+var componentUpdateQueue = null;
+var sideEffectTag = 0;
+
+// Updates scheduled during render will trigger an immediate re-render at the
+// end of the current pass. We can't store these updates on the normal queue,
+// because if the work is aborted, they should be discarded. Because this is
+// a relatively rare case, we also don't want to add an additional field to
+// either the hook or queue object types. So we store them in a lazily create
+// map of queue -> render-phase updates, which are discarded once the component
+// completes without re-rendering.
+
+// Whether an update was scheduled during the currently executing render pass.
+var didScheduleRenderPhaseUpdate = false;
+// Lazily created map of render-phase updates
+var renderPhaseUpdates = null;
+// Counter to prevent infinite loops.
+var numberOfReRenders = 0;
+var RE_RENDER_LIMIT = 25;
+
+function throwInvalidHookError() {
+ reactProdInvariant('321');
+}
+
+function areHookInputsEqual(nextDeps, prevDeps) {
+ if (prevDeps === null) {
+ return false;
+ }
+
+ for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
+ if (is(nextDeps[i], prevDeps[i])) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+function renderWithHooks(current, workInProgress, Component, props, refOrContext, nextRenderExpirationTime) {
+ renderExpirationTime = nextRenderExpirationTime;
+ currentlyRenderingFiber$1 = workInProgress;
+ nextCurrentHook = current !== null ? current.memoizedState : null;
+
+ {
+ ReactCurrentDispatcher$1.current = nextCurrentHook === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate;
+ }
+
+ var children = Component(props, refOrContext);
+
+ if (didScheduleRenderPhaseUpdate) {
+ do {
+ didScheduleRenderPhaseUpdate = false;
+ numberOfReRenders += 1;
+
+ // Start over from the beginning of the list
+ nextCurrentHook = current !== null ? current.memoizedState : null;
+ nextWorkInProgressHook = firstWorkInProgressHook;
+
+ currentHook = null;
+ workInProgressHook = null;
+ componentUpdateQueue = null;
+
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdate;
+
+ children = Component(props, refOrContext);
+ } while (didScheduleRenderPhaseUpdate);
+
+ renderPhaseUpdates = null;
+ numberOfReRenders = 0;
+ }
+
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrancy.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
+
+ var renderedWork = currentlyRenderingFiber$1;
+
+ renderedWork.memoizedState = firstWorkInProgressHook;
+ renderedWork.expirationTime = remainingExpirationTime;
+ renderedWork.updateQueue = componentUpdateQueue;
+ renderedWork.effectTag |= sideEffectTag;
+
+ var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
+
+ renderExpirationTime = NoWork;
+ currentlyRenderingFiber$1 = null;
+
+ currentHook = null;
+ nextCurrentHook = null;
+ firstWorkInProgressHook = null;
+ workInProgressHook = null;
+ nextWorkInProgressHook = null;
+
+ remainingExpirationTime = NoWork;
+ componentUpdateQueue = null;
+ sideEffectTag = 0;
+
+ // These were reset above
+ // didScheduleRenderPhaseUpdate = false;
+ // renderPhaseUpdates = null;
+ // numberOfReRenders = 0;
+
+ !!didRenderTooFewHooks ? reactProdInvariant('300') : void 0;
+
+ return children;
+}
+
+function bailoutHooks(current, workInProgress, expirationTime) {
+ workInProgress.updateQueue = current.updateQueue;
+ workInProgress.effectTag &= ~(Passive | Update);
+ if (current.expirationTime <= expirationTime) {
+ current.expirationTime = NoWork;
+ }
+}
+
+function resetHooks() {
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrancy.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
+
+ // This is used to reset the state of this module when a component throws.
+ // It's also called inside mountIndeterminateComponent if we determine the
+ // component is a module-style component.
+ renderExpirationTime = NoWork;
+ currentlyRenderingFiber$1 = null;
+
+ currentHook = null;
+ nextCurrentHook = null;
+ firstWorkInProgressHook = null;
+ workInProgressHook = null;
+ nextWorkInProgressHook = null;
+
+ remainingExpirationTime = NoWork;
+ componentUpdateQueue = null;
+ sideEffectTag = 0;
+
+ didScheduleRenderPhaseUpdate = false;
+ renderPhaseUpdates = null;
+ numberOfReRenders = 0;
+}
+
+function mountWorkInProgressHook() {
+ var hook = {
+ memoizedState: null,
+
+ baseState: null,
+ queue: null,
+ baseUpdate: null,
+
+ next: null
+ };
+
+ if (workInProgressHook === null) {
+ // This is the first hook in the list
+ firstWorkInProgressHook = workInProgressHook = hook;
+ } else {
+ // Append to the end of the list
+ workInProgressHook = workInProgressHook.next = hook;
+ }
+ return workInProgressHook;
+}
+
+function updateWorkInProgressHook() {
+ // This function is used both for updates and for re-renders triggered by a
+ // render phase update. It assumes there is either a current hook we can
+ // clone, or a work-in-progress hook from a previous render pass that we can
+ // use as a base. When we reach the end of the base list, we must switch to
+ // the dispatcher used for mounts.
+ if (nextWorkInProgressHook !== null) {
+ // There's already a work-in-progress. Reuse it.
+ workInProgressHook = nextWorkInProgressHook;
+ nextWorkInProgressHook = workInProgressHook.next;
+
+ currentHook = nextCurrentHook;
+ nextCurrentHook = currentHook !== null ? currentHook.next : null;
+ } else {
+ // Clone from the current hook.
+ !(nextCurrentHook !== null) ? reactProdInvariant('310') : void 0;
+ currentHook = nextCurrentHook;
+
+ var newHook = {
+ memoizedState: currentHook.memoizedState,
+
+ baseState: currentHook.baseState,
+ queue: currentHook.queue,
+ baseUpdate: currentHook.baseUpdate,
+
+ next: null
+ };
+
+ if (workInProgressHook === null) {
+ // This is the first hook in the list.
+ workInProgressHook = firstWorkInProgressHook = newHook;
+ } else {
+ // Append to the end of the list.
+ workInProgressHook = workInProgressHook.next = newHook;
+ }
+ nextCurrentHook = currentHook.next;
+ }
+ return workInProgressHook;
+}
+
+function createFunctionComponentUpdateQueue() {
+ return {
+ lastEffect: null
+ };
+}
+
+function basicStateReducer(state, action) {
+ return typeof action === 'function' ? action(state) : action;
+}
+
+function mountReducer(reducer, initialArg, init) {
+ var hook = mountWorkInProgressHook();
+ var initialState = void 0;
+ if (init !== undefined) {
+ initialState = init(initialArg);
+ } else {
+ initialState = initialArg;
+ }
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = hook.queue = {
+ last: null,
+ dispatch: null,
+ lastRenderedReducer: reducer,
+ lastRenderedState: initialState
+ };
+ var dispatch = queue.dispatch = dispatchAction.bind(null,
+ // Flow doesn't know this is non-null, but we do.
+ currentlyRenderingFiber$1, queue);
+ return [hook.memoizedState, dispatch];
+}
+
+function updateReducer(reducer, initialArg, init) {
+ var hook = updateWorkInProgressHook();
+ var queue = hook.queue;
+ !(queue !== null) ? reactProdInvariant('311') : void 0;
+
+ queue.lastRenderedReducer = reducer;
+
+ if (numberOfReRenders > 0) {
+ // This is a re-render. Apply the new render phase updates to the previous
+ var _dispatch = queue.dispatch;
+ if (renderPhaseUpdates !== null) {
+ // Render phase updates are stored in a map of queue -> linked list
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate !== undefined) {
+ renderPhaseUpdates.delete(queue);
+ var newState = hook.memoizedState;
+ var update = firstRenderPhaseUpdate;
+ do {
+ // Process this render phase update. We don't have to check the
+ // priority because it will always be the same as the current
+ // render's.
+ var _action = update.action;
+ newState = reducer(newState, _action);
+ update = update.next;
+ } while (update !== null);
+
+ // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
+ if (!is(newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
+ }
+
+ hook.memoizedState = newState;
+ // Don't persist the state accumlated from the render phase updates to
+ // the base state unless the queue is empty.
+ // TODO: Not sure if this is the desired semantics, but it's what we
+ // do for gDSFP. I can't remember why.
+ if (hook.baseUpdate === queue.last) {
+ hook.baseState = newState;
+ }
+
+ queue.lastRenderedState = newState;
+
+ return [newState, _dispatch];
+ }
+ }
+ return [hook.memoizedState, _dispatch];
+ }
+
+ // The last update in the entire queue
+ var last = queue.last;
+ // The last update that is part of the base state.
+ var baseUpdate = hook.baseUpdate;
+ var baseState = hook.baseState;
+
+ // Find the first unprocessed update.
+ var first = void 0;
+ if (baseUpdate !== null) {
+ if (last !== null) {
+ // For the first update, the queue is a circular linked list where
+ // `queue.last.next = queue.first`. Once the first update commits, and
+ // the `baseUpdate` is no longer empty, we can unravel the list.
+ last.next = null;
+ }
+ first = baseUpdate.next;
+ } else {
+ first = last !== null ? last.next : null;
+ }
+ if (first !== null) {
+ var _newState = baseState;
+ var newBaseState = null;
+ var newBaseUpdate = null;
+ var prevUpdate = baseUpdate;
+ var _update = first;
+ var didSkip = false;
+ do {
+ var updateExpirationTime = _update.expirationTime;
+ if (updateExpirationTime < renderExpirationTime) {
+ // Priority is insufficient. Skip this update. If this is the first
+ // skipped update, the previous update/state is the new base
+ // update/state.
+ if (!didSkip) {
+ didSkip = true;
+ newBaseUpdate = prevUpdate;
+ newBaseState = _newState;
+ }
+ // Update the remaining priority in the queue.
+ if (updateExpirationTime > remainingExpirationTime) {
+ remainingExpirationTime = updateExpirationTime;
+ }
+ } else {
+ // Process this update.
+ if (_update.eagerReducer === reducer) {
+ // If this update was processed eagerly, and its reducer matches the
+ // current reducer, we can use the eagerly computed state.
+ _newState = _update.eagerState;
+ } else {
+ var _action2 = _update.action;
+ _newState = reducer(_newState, _action2);
+ }
+ }
+ prevUpdate = _update;
+ _update = _update.next;
+ } while (_update !== null && _update !== first);
+
+ if (!didSkip) {
+ newBaseUpdate = prevUpdate;
+ newBaseState = _newState;
+ }
+
+ // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
+ if (!is(_newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
+ }
+
+ hook.memoizedState = _newState;
+ hook.baseUpdate = newBaseUpdate;
+ hook.baseState = newBaseState;
+
+ queue.lastRenderedState = _newState;
+ }
+
+ var dispatch = queue.dispatch;
+ return [hook.memoizedState, dispatch];
+}
+
+function mountState(initialState) {
+ var hook = mountWorkInProgressHook();
+ if (typeof initialState === 'function') {
+ initialState = initialState();
+ }
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = hook.queue = {
+ last: null,
+ dispatch: null,
+ lastRenderedReducer: basicStateReducer,
+ lastRenderedState: initialState
+ };
+ var dispatch = queue.dispatch = dispatchAction.bind(null,
+ // Flow doesn't know this is non-null, but we do.
+ currentlyRenderingFiber$1, queue);
+ return [hook.memoizedState, dispatch];
+}
+
+function updateState(initialState) {
+ return updateReducer(basicStateReducer, initialState);
+}
+
+function pushEffect(tag, create, destroy, deps) {
+ var effect = {
+ tag: tag,
+ create: create,
+ destroy: destroy,
+ deps: deps,
+ // Circular
+ next: null
+ };
+ if (componentUpdateQueue === null) {
+ componentUpdateQueue = createFunctionComponentUpdateQueue();
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var _lastEffect = componentUpdateQueue.lastEffect;
+ if (_lastEffect === null) {
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var firstEffect = _lastEffect.next;
+ _lastEffect.next = effect;
+ effect.next = firstEffect;
+ componentUpdateQueue.lastEffect = effect;
+ }
+ }
+ return effect;
+}
+
+function mountRef(initialValue) {
+ var hook = mountWorkInProgressHook();
+ var ref = { current: initialValue };
+ hook.memoizedState = ref;
+ return ref;
+}
+
+function updateRef(initialValue) {
+ var hook = updateWorkInProgressHook();
+ return hook.memoizedState;
+}
+
+function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ sideEffectTag |= fiberEffectTag;
+ hook.memoizedState = pushEffect(hookEffectTag, create, undefined, nextDeps);
+}
+
+function updateEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var destroy = undefined;
+
+ if (currentHook !== null) {
+ var prevEffect = currentHook.memoizedState;
+ destroy = prevEffect.destroy;
+ if (nextDeps !== null) {
+ var prevDeps = prevEffect.deps;
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ pushEffect(NoEffect$1, create, destroy, nextDeps);
+ return;
+ }
+ }
+ }
+
+ sideEffectTag |= fiberEffectTag;
+ hook.memoizedState = pushEffect(hookEffectTag, create, destroy, nextDeps);
+}
+
+function mountEffect(create, deps) {
+ return mountEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
+}
+
+function updateEffect(create, deps) {
+ return updateEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
+}
+
+function mountLayoutEffect(create, deps) {
+ return mountEffectImpl(Update, UnmountMutation | MountLayout, create, deps);
+}
+
+function updateLayoutEffect(create, deps) {
+ return updateEffectImpl(Update, UnmountMutation | MountLayout, create, deps);
+}
+
+function imperativeHandleEffect(create, ref) {
+ if (typeof ref === 'function') {
+ var refCallback = ref;
+ var _inst = create();
+ refCallback(_inst);
+ return function () {
+ refCallback(null);
+ };
+ } else if (ref !== null && ref !== undefined) {
+ var refObject = ref;
+ var _inst2 = create();
+ refObject.current = _inst2;
+ return function () {
+ refObject.current = null;
+ };
+ }
+}
+
+function mountImperativeHandle(ref, create, deps) {
+ var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+
+ return mountEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
+}
+
+function updateImperativeHandle(ref, create, deps) {
+ var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+
+ return updateEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
+}
+
+function mountDebugValue(value, formatterFn) {
+ // This hook is normally a no-op.
+ // The react-debug-hooks package injects its own implementation
+ // so that e.g. DevTools can display custom hook values.
+}
+
+var updateDebugValue = mountDebugValue;
+
+function mountCallback(callback, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
+
+function updateCallback(callback, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState;
+ if (prevState !== null) {
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
+
+function mountMemo(nextCreate, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+function updateMemo(nextCreate, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState;
+ if (prevState !== null) {
+ // Assume these are defined. If they're not, areHookInputsEqual will warn.
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+function dispatchAction(fiber, queue, action) {
+ !(numberOfReRenders < RE_RENDER_LIMIT) ? reactProdInvariant('301') : void 0;
+
+ var alternate = fiber.alternate;
+ if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {
+ // This is a render phase update. Stash it in a lazily-created map of
+ // queue -> linked list of updates. After this render pass, we'll restart
+ // and apply the stashed updates on top of the work-in-progress hook.
+ didScheduleRenderPhaseUpdate = true;
+ var update = {
+ expirationTime: renderExpirationTime,
+ action: action,
+ eagerReducer: null,
+ eagerState: null,
+ next: null
+ };
+ if (renderPhaseUpdates === null) {
+ renderPhaseUpdates = new Map();
+ }
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate === undefined) {
+ renderPhaseUpdates.set(queue, update);
+ } else {
+ // Append the update to the end of the list.
+ var lastRenderPhaseUpdate = firstRenderPhaseUpdate;
+ while (lastRenderPhaseUpdate.next !== null) {
+ lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
+ }
+ lastRenderPhaseUpdate.next = update;
+ }
+ } else {
+ flushPassiveEffects();
+
+ var currentTime = requestCurrentTime();
+ var _expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var _update2 = {
+ expirationTime: _expirationTime,
+ action: action,
+ eagerReducer: null,
+ eagerState: null,
+ next: null
+ };
+
+ // Append the update to the end of the list.
+ var _last = queue.last;
+ if (_last === null) {
+ // This is the first update. Create a circular list.
+ _update2.next = _update2;
+ } else {
+ var first = _last.next;
+ if (first !== null) {
+ // Still circular.
+ _update2.next = first;
+ }
+ _last.next = _update2;
+ }
+ queue.last = _update2;
+
+ if (fiber.expirationTime === NoWork && (alternate === null || alternate.expirationTime === NoWork)) {
+ // The queue is currently empty, which means we can eagerly compute the
+ // next state before entering the render phase. If the new state is the
+ // same as the current state, we may be able to bail out entirely.
+ var _lastRenderedReducer = queue.lastRenderedReducer;
+ if (_lastRenderedReducer !== null) {
+ try {
+ var currentState = queue.lastRenderedState;
+ var _eagerState = _lastRenderedReducer(currentState, action);
+ // Stash the eagerly computed state, and the reducer used to compute
+ // it, on the update object. If the reducer hasn't changed by the
+ // time we enter the render phase, then the eager state can be used
+ // without calling the reducer again.
+ _update2.eagerReducer = _lastRenderedReducer;
+ _update2.eagerState = _eagerState;
+ if (is(_eagerState, currentState)) {
+ // Fast path. We can bail out without scheduling React to re-render.
+ // It's still possible that we'll need to rebase this update later,
+ // if the component re-renders for a different reason and by that
+ // time the reducer has changed.
+ return;
+ }
+ } catch (error) {
+ // Suppress the error. It will throw again in the render phase.
+ } finally {
+
+ }
+ }
+ }
+ scheduleWork(fiber, _expirationTime);
+ }
+}
+
+var ContextOnlyDispatcher = {
+ readContext: readContext,
+
+ useCallback: throwInvalidHookError,
+ useContext: throwInvalidHookError,
+ useEffect: throwInvalidHookError,
+ useImperativeHandle: throwInvalidHookError,
+ useLayoutEffect: throwInvalidHookError,
+ useMemo: throwInvalidHookError,
+ useReducer: throwInvalidHookError,
+ useRef: throwInvalidHookError,
+ useState: throwInvalidHookError,
+ useDebugValue: throwInvalidHookError
+};
+
+var HooksDispatcherOnMount = {
+ readContext: readContext,
+
+ useCallback: mountCallback,
+ useContext: readContext,
+ useEffect: mountEffect,
+ useImperativeHandle: mountImperativeHandle,
+ useLayoutEffect: mountLayoutEffect,
+ useMemo: mountMemo,
+ useReducer: mountReducer,
+ useRef: mountRef,
+ useState: mountState,
+ useDebugValue: mountDebugValue
+};
+
+var HooksDispatcherOnUpdate = {
+ readContext: readContext,
+
+ useCallback: updateCallback,
+ useContext: readContext,
+ useEffect: updateEffect,
+ useImperativeHandle: updateImperativeHandle,
+ useLayoutEffect: updateLayoutEffect,
+ useMemo: updateMemo,
+ useReducer: updateReducer,
+ useRef: updateRef,
+ useState: updateState,
+ useDebugValue: updateDebugValue
+};
+
+var commitTime = 0;
+var profilerStartTime = -1;
+
+function getCommitTime() {
+ return commitTime;
+}
+
+function recordCommitTime() {
+ if (!enableProfilerTimer) {
+ return;
+ }
+ commitTime = unstable_now();
+}
+
+function startProfilerTimer(fiber) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+
+ profilerStartTime = unstable_now();
+
+ if (fiber.actualStartTime < 0) {
+ fiber.actualStartTime = unstable_now();
+ }
+}
+
+function stopProfilerTimerIfRunning(fiber) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+ profilerStartTime = -1;
+}
+
+function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+
+ if (profilerStartTime >= 0) {
+ var elapsedTime = unstable_now() - profilerStartTime;
+ fiber.actualDuration += elapsedTime;
+ if (overrideBaseTime) {
+ fiber.selfBaseDuration = elapsedTime;
+ }
+ profilerStartTime = -1;
+ }
+}
+
+// The deepest Fiber on the stack involved in a hydration context.
+// This may have been an insertion or a hydration.
+var hydrationParentFiber = null;
+var nextHydratableInstance = null;
+var isHydrating = false;
+
+function enterHydrationState(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+
+ var parentInstance = fiber.stateNode.containerInfo;
+ nextHydratableInstance = getFirstHydratableChild(parentInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ return true;
+}
+
+function reenterHydrationStateFromDehydratedSuspenseInstance(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+
+ var suspenseInstance = fiber.stateNode;
+ nextHydratableInstance = getNextHydratableSibling(suspenseInstance);
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return true;
+}
+
+function deleteHydratableInstance(returnFiber, instance) {
+ var childToDelete = createFiberFromHostInstanceForDeletion();
+ childToDelete.stateNode = instance;
+ childToDelete.return = returnFiber;
+ childToDelete.effectTag = Deletion;
+
+ // This might seem like it belongs on progressedFirstDeletion. However,
+ // these children are not part of the reconciliation list of children.
+ // Even if we abort and rereconcile the children, that will try to hydrate
+ // again and the nodes are still in the host tree so these will be
+ // recreated.
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+}
+
+function insertNonHydratedInstance(returnFiber, fiber) {
+ fiber.effectTag |= Placement;
+
+}
+
+function tryHydrate(fiber, nextInstance) {
+ switch (fiber.tag) {
+ case HostComponent:
+ {
+ var type = fiber.type;
+ var props = fiber.pendingProps;
+ var instance = canHydrateInstance(nextInstance, type, props);
+ if (instance !== null) {
+ fiber.stateNode = instance;
+ return true;
+ }
+ return false;
+ }
+ case HostText:
+ {
+ var text = fiber.pendingProps;
+ var textInstance = canHydrateTextInstance(nextInstance, text);
+ if (textInstance !== null) {
+ fiber.stateNode = textInstance;
+ return true;
+ }
+ return false;
+ }
+ case SuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
+ if (suspenseInstance !== null) {
+ // Downgrade the tag to a dehydrated component until we've hydrated it.
+ fiber.tag = DehydratedSuspenseComponent;
+ fiber.stateNode = suspenseInstance;
+ return true;
+ }
+ }
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+function tryToClaimNextHydratableInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
+ var nextInstance = nextHydratableInstance;
+ if (!nextInstance) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ var firstAttemptedInstance = nextInstance;
+ if (!tryHydrate(fiber, nextInstance)) {
+ // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
+ nextInstance = getNextHydratableSibling(firstAttemptedInstance);
+ if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ // We matched the next one, we'll now assume that the first one was
+ // superfluous and we'll delete it. Since we can't eagerly delete it
+ // we'll have to schedule a deletion. To do that, this node needs a dummy
+ // fiber associated with it.
+ deleteHydratableInstance(hydrationParentFiber, firstAttemptedInstance);
+ }
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = getFirstHydratableChild(nextInstance);
+}
+
+function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) {
+ if (!supportsHydration) {
+ reactProdInvariant('175');
+ }
+
+ var instance = fiber.stateNode;
+ var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber);
+ // TODO: Type this specific to this type of component.
+ fiber.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update.
+ if (updatePayload !== null) {
+ return true;
+ }
+ return false;
+}
+
+function prepareToHydrateHostTextInstance(fiber) {
+ if (!supportsHydration) {
+ reactProdInvariant('176');
+ }
+
+ var textInstance = fiber.stateNode;
+ var textContent = fiber.memoizedProps;
+ var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
+ return shouldUpdate;
+}
+
+function skipPastDehydratedSuspenseInstance(fiber) {
+ if (!supportsHydration) {
+ reactProdInvariant('316');
+ }
+ var suspenseInstance = fiber.stateNode;
+ !suspenseInstance ? reactProdInvariant('317') : void 0;
+ nextHydratableInstance = getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
+}
+
+function popToNextHostParent(fiber) {
+ var parent = fiber.return;
+ while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot && parent.tag !== DehydratedSuspenseComponent) {
+ parent = parent.return;
+ }
+ hydrationParentFiber = parent;
+}
+
+function popHydrationState(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+ if (fiber !== hydrationParentFiber) {
+ // We're deeper than the current hydration context, inside an inserted
+ // tree.
+ return false;
+ }
+ if (!isHydrating) {
+ // If we're not currently hydrating but we're in a hydration context, then
+ // we were an insertion and now need to pop up reenter hydration of our
+ // siblings.
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return false;
+ }
+
+ var type = fiber.type;
+
+ // If we have any remaining hydratable nodes, we need to delete them now.
+ // We only do this deeper than head and body since they tend to have random
+ // other nodes in them. We also ignore components with pure text content in
+ // side of them.
+ // TODO: Better heuristic.
+ if (fiber.tag !== HostComponent || type !== 'head' && type !== 'body' && !shouldSetTextContent(type, fiber.memoizedProps)) {
+ var nextInstance = nextHydratableInstance;
+ while (nextInstance) {
+ deleteHydratableInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
+ }
+ }
+
+ popToNextHostParent(fiber);
+ nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null;
+ return true;
+}
+
+function resetHydrationState() {
+ if (!supportsHydration) {
+ return;
+ }
+
+ hydrationParentFiber = null;
+ nextHydratableInstance = null;
+ isHydrating = false;
+}
+
+var ReactCurrentOwner$3 = ReactSharedInternals.ReactCurrentOwner;
+
+var didReceiveUpdate = false;
+
+
+
+function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
+ if (current$$1 === null) {
+ // If this is a fresh new component that hasn't been rendered yet, we
+ // won't update its child set by applying minimal side-effects. Instead,
+ // we will add them all to the child before it gets rendered. That means
+ // we can optimize this reconciliation pass by not tracking side-effects.
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ // If the current child is the same as the work in progress, it means that
+ // we haven't yet started any work on these children. Therefore, we use
+ // the clone algorithm to create a copy of all the current children.
+
+ // If we had any progressed work already, that is invalid at this point so
+ // let's throw it out.
+ workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
+ }
+}
+
+function forceUnmountCurrentAndReconcile(current$$1, workInProgress, nextChildren, renderExpirationTime) {
+ // This function is fork of reconcileChildren. It's used in cases where we
+ // want to reconcile without matching against the existing set. This has the
+ // effect of all current children being unmounted; even if the type and key
+ // are the same, the old child is unmounted and a new child is created.
+ //
+ // To do this, we're going to go through the reconcile algorithm twice. In
+ // the first pass, we schedule a deletion for all the current children by
+ // passing null.
+ workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, null, renderExpirationTime);
+ // In the second pass, we mount the new children. The trick here is that we
+ // pass null in place of where we usually pass the current child set. This has
+ // the effect of remounting all children regardless of whether their their
+ // identity matches.
+ workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+}
+
+function updateForwardRef(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens after the first render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
+
+ var render = Component.render;
+ var ref = workInProgress.ref;
+
+ // The rest is a fork of updateFunctionComponent
+ var nextChildren = void 0;
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ {
+ nextChildren = renderWithHooks(current$$1, workInProgress, render, nextProps, ref, renderExpirationTime);
+ }
+
+ if (current$$1 !== null && !didReceiveUpdate) {
+ bailoutHooks(current$$1, workInProgress, renderExpirationTime);
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
+ if (current$$1 === null) {
+ var type = Component.type;
+ if (isSimpleFunctionComponent(type) && Component.compare === null &&
+ // SimpleMemoComponent codepath doesn't resolve outer props either.
+ Component.defaultProps === undefined) {
+ // If this is a plain function component without default props,
+ // and with only the default shallow comparison, we upgrade it
+ // to a SimpleMemoComponent to allow fast path updates.
+ workInProgress.tag = SimpleMemoComponent;
+ workInProgress.type = type;
+ return updateSimpleMemoComponent(current$$1, workInProgress, type, nextProps, updateExpirationTime, renderExpirationTime);
+ }
+ var child = createFiberFromTypeAndProps(Component.type, null, nextProps, null, workInProgress.mode, renderExpirationTime);
+ child.ref = workInProgress.ref;
+ child.return = workInProgress;
+ workInProgress.child = child;
+ return child;
+ }
+ var currentChild = current$$1.child; // This is always exactly one child
+ if (updateExpirationTime < renderExpirationTime) {
+ // This will be the props with resolved defaultProps,
+ // unlike current.memoizedProps which will be the unresolved ones.
+ var prevProps = currentChild.memoizedProps;
+ // Default to shallow comparison
+ var compare = Component.compare;
+ compare = compare !== null ? compare : shallowEqual;
+ if (compare(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ var newChild = createWorkInProgress(currentChild, nextProps, renderExpirationTime);
+ newChild.ref = workInProgress.ref;
+ newChild.return = workInProgress;
+ workInProgress.child = newChild;
+ return newChild;
+}
+
+function updateSimpleMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens when the inner render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
+
+ if (current$$1 !== null) {
+ var prevProps = current$$1.memoizedProps;
+ if (shallowEqual(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
+ didReceiveUpdate = false;
+ if (updateExpirationTime < renderExpirationTime) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ }
+ }
+ return updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
+}
+
+function updateFragment(current$$1, workInProgress, renderExpirationTime) {
+ var nextChildren = workInProgress.pendingProps;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateMode(current$$1, workInProgress, renderExpirationTime) {
+ var nextChildren = workInProgress.pendingProps.children;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateProfiler(current$$1, workInProgress, renderExpirationTime) {
+ if (enableProfilerTimer) {
+ workInProgress.effectTag |= Update;
+ }
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function markRef(current$$1, workInProgress) {
+ var ref = workInProgress.ref;
+ if (current$$1 === null && ref !== null || current$$1 !== null && current$$1.ref !== ref) {
+ // Schedule a Ref effect
+ workInProgress.effectTag |= Ref;
+ }
+}
+
+function updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ var nextChildren = void 0;
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ {
+ nextChildren = renderWithHooks(current$$1, workInProgress, Component, nextProps, context, renderExpirationTime);
+ }
+
+ if (current$$1 !== null && !didReceiveUpdate) {
+ bailoutHooks(current$$1, workInProgress, renderExpirationTime);
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ var hasContext = void 0;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ var instance = workInProgress.stateNode;
+ var shouldUpdate = void 0;
+ if (instance === null) {
+ if (current$$1 !== null) {
+ // An class component without an instance only mounts if it suspended
+ // inside a non- concurrent tree, in an inconsistent state. We want to
+ // tree it like a new mount, even though an empty version of it already
+ // committed. Disconnect the alternate pointers.
+ current$$1.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+ // In the initial pass we might need to construct the instance.
+ constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ shouldUpdate = true;
+ } else if (current$$1 === null) {
+ // In a resume, we'll already have an instance we can reuse.
+ shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ } else {
+ shouldUpdate = updateClassInstance(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
+ }
+ var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);
+ return nextUnitOfWork;
+}
+
+function finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
+ // Refs should update even if shouldComponentUpdate returns false
+ markRef(current$$1, workInProgress);
+
+ var didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;
+
+ if (!shouldUpdate && !didCaptureError) {
+ // Context providers should defer to sCU for rendering
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, false);
+ }
+
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ var instance = workInProgress.stateNode;
+
+ // Rerender
+ ReactCurrentOwner$3.current = workInProgress;
+ var nextChildren = void 0;
+ if (didCaptureError && typeof Component.getDerivedStateFromError !== 'function') {
+ // If we captured an error, but getDerivedStateFrom catch is not defined,
+ // unmount all the children. componentDidCatch will schedule an update to
+ // re-render a fallback. This is temporary until we migrate everyone to
+ // the new API.
+ // TODO: Warn in a future release.
+ nextChildren = null;
+
+ if (enableProfilerTimer) {
+ stopProfilerTimerIfRunning(workInProgress);
+ }
+ } else {
+ {
+ nextChildren = instance.render();
+ }
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ if (current$$1 !== null && didCaptureError) {
+ // If we're recovering from an error, reconcile without reusing any of
+ // the existing children. Conceptually, the normal children and the children
+ // that are shown on error are two different sets, so we shouldn't reuse
+ // normal children even if their identities match.
+ forceUnmountCurrentAndReconcile(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ } else {
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ }
+
+ // Memoize state using the values we just used to render.
+ // TODO: Restructure so we never read values from the instance.
+ workInProgress.memoizedState = instance.state;
+
+ // The context might have changed so we need to recalculate it.
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, true);
+ }
+
+ return workInProgress.child;
+}
+
+function pushHostRootContext(workInProgress) {
+ var root = workInProgress.stateNode;
+ if (root.pendingContext) {
+ pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context);
+ } else if (root.context) {
+ // Should always be set
+ pushTopLevelContextObject(workInProgress, root.context, false);
+ }
+ pushHostContainer(workInProgress, root.containerInfo);
+}
+
+function updateHostRoot(current$$1, workInProgress, renderExpirationTime) {
+ pushHostRootContext(workInProgress);
+ var updateQueue = workInProgress.updateQueue;
+ !(updateQueue !== null) ? reactProdInvariant('282') : void 0;
+ var nextProps = workInProgress.pendingProps;
+ var prevState = workInProgress.memoizedState;
+ var prevChildren = prevState !== null ? prevState.element : null;
+ processUpdateQueue(workInProgress, updateQueue, nextProps, null, renderExpirationTime);
+ var nextState = workInProgress.memoizedState;
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ var nextChildren = nextState.element;
+ if (nextChildren === prevChildren) {
+ // If the state is the same as before, that's a bailout because we had
+ // no work that expires at this time.
+ resetHydrationState();
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ var root = workInProgress.stateNode;
+ if ((current$$1 === null || current$$1.child === null) && root.hydrate && enterHydrationState(workInProgress)) {
+ // If we don't have any current children this might be the first pass.
+ // We always try to hydrate. If this isn't a hydration pass there won't
+ // be any children to hydrate which is effectively the same thing as
+ // not hydrating.
+
+ // This is a bit of a hack. We track the host root as a placement to
+ // know that we're currently in a mounting state. That way isMounted
+ // works as expected. We must reset this before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag |= Placement;
+
+ // Ensure that children mount into this root without tracking
+ // side-effects. This ensures that we don't store Placement effects on
+ // nodes that will be hydrated.
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ // Otherwise reset hydration state in case we aborted and resumed another
+ // root.
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ resetHydrationState();
+ }
+ return workInProgress.child;
+}
+
+function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
+ pushHostContext(workInProgress);
+
+ if (current$$1 === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+
+ var type = workInProgress.type;
+ var nextProps = workInProgress.pendingProps;
+ var prevProps = current$$1 !== null ? current$$1.memoizedProps : null;
+
+ var nextChildren = nextProps.children;
+ var isDirectTextChild = shouldSetTextContent(type, nextProps);
+
+ if (isDirectTextChild) {
+ // We special case a direct text child of a host node. This is a common
+ // case. We won't handle it as a reified child. We will instead handle
+ // this in the host environment that also have access to this prop. That
+ // avoids allocating another HostText fiber and traversing it.
+ nextChildren = null;
+ } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
+ // If we're switching from a direct text child to a normal child, or to
+ // empty, we need to schedule the text content to be reset.
+ workInProgress.effectTag |= ContentReset;
+ }
+
+ markRef(current$$1, workInProgress);
+
+ // Check the host config to see if the children are offscreen/hidden.
+ if (renderExpirationTime !== Never && workInProgress.mode & ConcurrentMode && shouldDeprioritizeSubtree(type, nextProps)) {
+ // Schedule this fiber to re-render at offscreen priority. Then bailout.
+ workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
+ return null;
+ }
+
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateHostText(current$$1, workInProgress) {
+ if (current$$1 === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+ // Nothing to do here. This is terminal. We'll do the completion step
+ // immediately after.
+ return null;
+}
+
+function mountLazyComponent(_current, workInProgress, elementType, updateExpirationTime, renderExpirationTime) {
+ if (_current !== null) {
+ // An lazy component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ var props = workInProgress.pendingProps;
+ // We can't start a User Timing measurement with correct label yet.
+ // Cancel and resume right after we know the tag.
+ cancelWorkTimer(workInProgress);
+ var Component = readLazyComponentType(elementType);
+ // Store the unwrapped component in the type.
+ workInProgress.type = Component;
+ var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
+ startWorkTimer(workInProgress);
+ var resolvedProps = resolveDefaultProps(Component, props);
+ var child = void 0;
+ switch (resolvedTag) {
+ case FunctionComponent:
+ {
+ child = updateFunctionComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case ClassComponent:
+ {
+ child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case ForwardRef:
+ {
+ child = updateForwardRef(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case MemoComponent:
+ {
+ child = updateMemoComponent(null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
+ updateExpirationTime, renderExpirationTime);
+ break;
+ }
+ default:
+ {
+ var hint = '';
+ reactProdInvariant('306', Component, hint);
+ }
+ }
+ return child;
+}
+
+function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderExpirationTime) {
+ if (_current !== null) {
+ // An incomplete component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ // Promote the fiber to a class and try rendering again.
+ workInProgress.tag = ClassComponent;
+
+ // The rest of this function is a fork of `updateClassComponent`
+
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = void 0;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+
+ return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
+}
+
+function mountIndeterminateComponent(_current, workInProgress, Component, renderExpirationTime) {
+ if (_current !== null) {
+ // An indeterminate component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ var props = workInProgress.pendingProps;
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ var value = void 0;
+
+ {
+ value = renderWithHooks(null, workInProgress, Component, props, context, renderExpirationTime);
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+
+ if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
+ // Proceed under the assumption that this is a class instance
+ workInProgress.tag = ClassComponent;
+
+ // Throw out any hooks that were used.
+ resetHooks();
+
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = false;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+
+ workInProgress.memoizedState = value.state !== null && value.state !== undefined ? value.state : null;
+
+ var getDerivedStateFromProps = Component.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, Component, getDerivedStateFromProps, props);
+ }
+
+ adoptClassInstance(workInProgress, value);
+ mountClassInstance(workInProgress, Component, props, renderExpirationTime);
+ return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
+ } else {
+ // Proceed under the assumption that this is a function component
+ workInProgress.tag = FunctionComponent;
+ reconcileChildren(null, workInProgress, value, renderExpirationTime);
+ return workInProgress.child;
+ }
+}
+
+function updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
+ var mode = workInProgress.mode;
+ var nextProps = workInProgress.pendingProps;
+
+ // We should attempt to render the primary children unless this boundary
+ // already suspended during this render (`alreadyCaptured` is true).
+ var nextState = workInProgress.memoizedState;
+
+ var nextDidTimeout = void 0;
+ if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This is the first attempt.
+ nextState = null;
+ nextDidTimeout = false;
+ } else {
+ // Something in this boundary's subtree already suspended. Switch to
+ // rendering the fallback children.
+ nextState = {
+ timedOutAt: nextState !== null ? nextState.timedOutAt : NoWork
+ };
+ nextDidTimeout = true;
+ workInProgress.effectTag &= ~DidCapture;
+ }
+
+ // This next part is a bit confusing. If the children timeout, we switch to
+ // showing the fallback children in place of the "primary" children.
+ // However, we don't want to delete the primary children because then their
+ // state will be lost (both the React state and the host state, e.g.
+ // uncontrolled form inputs). Instead we keep them mounted and hide them.
+ // Both the fallback children AND the primary children are rendered at the
+ // same time. Once the primary children are un-suspended, we can delete
+ // the fallback children — don't need to preserve their state.
+ //
+ // The two sets of children are siblings in the host environment, but
+ // semantically, for purposes of reconciliation, they are two separate sets.
+ // So we store them using two fragment fibers.
+ //
+ // However, we want to avoid allocating extra fibers for every placeholder.
+ // They're only necessary when the children time out, because that's the
+ // only time when both sets are mounted.
+ //
+ // So, the extra fragment fibers are only used if the children time out.
+ // Otherwise, we render the primary children directly. This requires some
+ // custom reconciliation logic to preserve the state of the primary
+ // children. It's essentially a very basic form of re-parenting.
+
+ // `child` points to the child fiber. In the normal case, this is the first
+ // fiber of the primary children set. In the timed-out case, it's a
+ // a fragment fiber containing the primary children.
+ var child = void 0;
+ // `next` points to the next fiber React should render. In the normal case,
+ // it's the same as `child`: the first fiber of the primary children set.
+ // In the timed-out case, it's a fragment fiber containing the *fallback*
+ // children -- we skip over the primary children entirely.
+ var next = void 0;
+ if (current$$1 === null) {
+ if (enableSuspenseServerRenderer) {
+ // If we're currently hydrating, try to hydrate this boundary.
+ // But only if this has a fallback.
+ if (nextProps.fallback !== undefined) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ // This could've changed the tag if this was a dehydrated suspense component.
+ if (workInProgress.tag === DehydratedSuspenseComponent) {
+ return updateDehydratedSuspenseComponent(null, workInProgress, renderExpirationTime);
+ }
+ }
+ }
+
+ // This is the initial mount. This branch is pretty simple because there's
+ // no previous state that needs to be preserved.
+ if (nextDidTimeout) {
+ // Mount separate fragments for primary and fallback children.
+ var nextFallbackChildren = nextProps.fallback;
+ var primaryChildFragment = createFiberFromFragment(null, mode, NoWork, null);
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var progressedState = workInProgress.memoizedState;
+ var progressedPrimaryChild = progressedState !== null ? workInProgress.child.child : workInProgress.child;
+ primaryChildFragment.child = progressedPrimaryChild;
+ }
+
+ var fallbackChildFragment = createFiberFromFragment(nextFallbackChildren, mode, renderExpirationTime, null);
+ primaryChildFragment.sibling = fallbackChildFragment;
+ child = primaryChildFragment;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = fallbackChildFragment;
+ child.return = next.return = workInProgress;
+ } else {
+ // Mount the primary children without an intermediate fragment fiber.
+ var nextPrimaryChildren = nextProps.children;
+ child = next = mountChildFibers(workInProgress, null, nextPrimaryChildren, renderExpirationTime);
+ }
+ } else {
+ // This is an update. This branch is more complicated because we need to
+ // ensure the state of the primary children is preserved.
+ var prevState = current$$1.memoizedState;
+ var prevDidTimeout = prevState !== null;
+ if (prevDidTimeout) {
+ // The current tree already timed out. That means each child set is
+ var currentPrimaryChildFragment = current$$1.child;
+ var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
+ if (nextDidTimeout) {
+ // Still timed out. Reuse the current primary children by cloning
+ // its fragment. We're going to skip over these entirely.
+ var _nextFallbackChildren = nextProps.fallback;
+ var _primaryChildFragment = createWorkInProgress(currentPrimaryChildFragment, currentPrimaryChildFragment.pendingProps, NoWork);
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var _progressedState = workInProgress.memoizedState;
+ var _progressedPrimaryChild = _progressedState !== null ? workInProgress.child.child : workInProgress.child;
+ if (_progressedPrimaryChild !== currentPrimaryChildFragment.child) {
+ _primaryChildFragment.child = _progressedPrimaryChild;
+ }
+ }
+
+ // Because primaryChildFragment is a new fiber that we're inserting as the
+ // parent of a new tree, we need to set its treeBaseDuration.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // treeBaseDuration is the sum of all the child tree base durations.
+ var treeBaseDuration = 0;
+ var hiddenChild = _primaryChildFragment.child;
+ while (hiddenChild !== null) {
+ treeBaseDuration += hiddenChild.treeBaseDuration;
+ hiddenChild = hiddenChild.sibling;
+ }
+ _primaryChildFragment.treeBaseDuration = treeBaseDuration;
+ }
+
+ // Clone the fallback child fragment, too. These we'll continue
+ // working on.
+ var _fallbackChildFragment = _primaryChildFragment.sibling = createWorkInProgress(currentFallbackChildFragment, _nextFallbackChildren, currentFallbackChildFragment.expirationTime);
+ child = _primaryChildFragment;
+ _primaryChildFragment.childExpirationTime = NoWork;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = _fallbackChildFragment;
+ child.return = next.return = workInProgress;
+ } else {
+ // No longer suspended. Switch back to showing the primary children,
+ // and remove the intermediate fragment fiber.
+ var _nextPrimaryChildren = nextProps.children;
+ var currentPrimaryChild = currentPrimaryChildFragment.child;
+ var primaryChild = reconcileChildFibers(workInProgress, currentPrimaryChild, _nextPrimaryChildren, renderExpirationTime);
+
+ // If this render doesn't suspend, we need to delete the fallback
+ // children. Wait until the complete phase, after we've confirmed the
+ // fallback is no longer needed.
+ // TODO: Would it be better to store the fallback fragment on
+ // the stateNode?
+
+ // Continue rendering the children, like we normally do.
+ child = next = primaryChild;
+ }
+ } else {
+ // The current tree has not already timed out. That means the primary
+ // children are not wrapped in a fragment fiber.
+ var _currentPrimaryChild = current$$1.child;
+ if (nextDidTimeout) {
+ // Timed out. Wrap the children in a fragment fiber to keep them
+ // separate from the fallback children.
+ var _nextFallbackChildren2 = nextProps.fallback;
+ var _primaryChildFragment2 = createFiberFromFragment(
+ // It shouldn't matter what the pending props are because we aren't
+ // going to render this fragment.
+ null, mode, NoWork, null);
+ _primaryChildFragment2.child = _currentPrimaryChild;
+
+ // Even though we're creating a new fiber, there are no new children,
+ // because we're reusing an already mounted tree. So we don't need to
+ // schedule a placement.
+ // primaryChildFragment.effectTag |= Placement;
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var _progressedState2 = workInProgress.memoizedState;
+ var _progressedPrimaryChild2 = _progressedState2 !== null ? workInProgress.child.child : workInProgress.child;
+ _primaryChildFragment2.child = _progressedPrimaryChild2;
+ }
+
+ // Because primaryChildFragment is a new fiber that we're inserting as the
+ // parent of a new tree, we need to set its treeBaseDuration.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // treeBaseDuration is the sum of all the child tree base durations.
+ var _treeBaseDuration = 0;
+ var _hiddenChild = _primaryChildFragment2.child;
+ while (_hiddenChild !== null) {
+ _treeBaseDuration += _hiddenChild.treeBaseDuration;
+ _hiddenChild = _hiddenChild.sibling;
+ }
+ _primaryChildFragment2.treeBaseDuration = _treeBaseDuration;
+ }
+
+ // Create a fragment from the fallback children, too.
+ var _fallbackChildFragment2 = _primaryChildFragment2.sibling = createFiberFromFragment(_nextFallbackChildren2, mode, renderExpirationTime, null);
+ _fallbackChildFragment2.effectTag |= Placement;
+ child = _primaryChildFragment2;
+ _primaryChildFragment2.childExpirationTime = NoWork;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = _fallbackChildFragment2;
+ child.return = next.return = workInProgress;
+ } else {
+ // Still haven't timed out. Continue rendering the children, like we
+ // normally do.
+ var _nextPrimaryChildren2 = nextProps.children;
+ next = child = reconcileChildFibers(workInProgress, _currentPrimaryChild, _nextPrimaryChildren2, renderExpirationTime);
+ }
+ }
+ workInProgress.stateNode = current$$1.stateNode;
+ }
+
+ workInProgress.memoizedState = nextState;
+ workInProgress.child = child;
+ return next;
+}
+
+function updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
+ if (current$$1 === null) {
+ // During the first pass, we'll bail out and not drill into the children.
+ // Instead, we'll leave the content in place and try to hydrate it later.
+ workInProgress.expirationTime = Never;
+ return null;
+ }
+ // We use childExpirationTime to indicate that a child might depend on context, so if
+ // any context has changed, we need to treat is as if the input might have changed.
+ var hasContextChanged$$1 = current$$1.childExpirationTime >= renderExpirationTime;
+ if (didReceiveUpdate || hasContextChanged$$1) {
+ // This boundary has changed since the first render. This means that we are now unable to
+ // hydrate it. We might still be able to hydrate it using an earlier expiration time but
+ // during this render we can't. Instead, we're going to delete the whole subtree and
+ // instead inject a new real Suspense boundary to take its place, which may render content
+ // or fallback. The real Suspense boundary will suspend for a while so we have some time
+ // to ensure it can produce real content, but all state and pending events will be lost.
+
+ // Detach from the current dehydrated boundary.
+ current$$1.alternate = null;
+ workInProgress.alternate = null;
+
+ // Insert a deletion in the effect list.
+ var returnFiber = workInProgress.return;
+ !(returnFiber !== null) ? reactProdInvariant('315') : void 0;
+ var last = returnFiber.lastEffect;
+ if (last !== null) {
+ last.nextEffect = current$$1;
+ returnFiber.lastEffect = current$$1;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = current$$1;
+ }
+ current$$1.nextEffect = null;
+ current$$1.effectTag = Deletion;
+
+ // Upgrade this work in progress to a real Suspense component.
+ workInProgress.tag = SuspenseComponent;
+ workInProgress.stateNode = null;
+ workInProgress.memoizedState = null;
+ // This is now an insertion.
+ workInProgress.effectTag |= Placement;
+ // Retry as a real Suspense component.
+ return updateSuspenseComponent(null, workInProgress, renderExpirationTime);
+ }
+ if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This is the first attempt.
+ reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress);
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+ } else {
+ // Something suspended. Leave the existing children in place.
+ // TODO: In non-concurrent mode, should we commit the nodes we have hydrated so far?
+ workInProgress.child = null;
+ return null;
+ }
+}
+
+function updatePortalComponent(current$$1, workInProgress, renderExpirationTime) {
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ var nextChildren = workInProgress.pendingProps;
+ if (current$$1 === null) {
+ // Portals are special because we don't append the children during mount
+ // but at commit. Therefore we need to track insertions which the normal
+ // flow doesn't do during mount. This doesn't happen at the root because
+ // the root always starts with a "current" with a null child.
+ // TODO: Consider unifying this with how the root works.
+ workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ }
+ return workInProgress.child;
+}
+
+function updateContextProvider(current$$1, workInProgress, renderExpirationTime) {
+ var providerType = workInProgress.type;
+ var context = providerType._context;
+
+ var newProps = workInProgress.pendingProps;
+ var oldProps = workInProgress.memoizedProps;
+
+ var newValue = newProps.value;
+
+ pushProvider(workInProgress, newValue);
+
+ if (oldProps !== null) {
+ var oldValue = oldProps.value;
+ var changedBits = calculateChangedBits(context, newValue, oldValue);
+ if (changedBits === 0) {
+ // No change. Bailout early if children are the same.
+ if (oldProps.children === newProps.children && !hasContextChanged()) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ } else {
+ // The context value changed. Search for matching consumers and schedule
+ // them to update.
+ propagateContextChange(workInProgress, context, changedBits, renderExpirationTime);
+ }
+ }
+
+ var newChildren = newProps.children;
+ reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateContextConsumer(current$$1, workInProgress, renderExpirationTime) {
+ var context = workInProgress.type;
+ // The logic below for Context differs depending on PROD or DEV mode. In
+ // DEV mode, we create a separate object for Context.Consumer that acts
+ // like a proxy to Context. This proxy object adds unnecessary code in PROD
+ // so we use the old behaviour (Context.Consumer references Context) to
+ // reduce size and overhead. The separate object references context via
+ // a property called "_context", which also gives us the ability to check
+ // in DEV mode if this property exists or not and warn if it does not.
+ var newProps = workInProgress.pendingProps;
+ var render = newProps.children;
+
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ var newValue = readContext(context, newProps.unstable_observedBits);
+ var newChildren = void 0;
+ {
+ newChildren = render(newValue);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function markWorkInProgressReceivedUpdate() {
+ didReceiveUpdate = true;
+}
+
+function bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime) {
+ cancelWorkTimer(workInProgress);
+
+ if (current$$1 !== null) {
+ // Reuse previous context list
+ workInProgress.contextDependencies = current$$1.contextDependencies;
+ }
+
+ if (enableProfilerTimer) {
+ // Don't update "base" render times for bailouts.
+ stopProfilerTimerIfRunning(workInProgress);
+ }
+
+ // Check if the children have any pending work.
+ var childExpirationTime = workInProgress.childExpirationTime;
+ if (childExpirationTime < renderExpirationTime) {
+ // The children don't have any work either. We can skip them.
+ // TODO: Once we add back resuming, we should check if the children are
+ // a work-in-progress set. If so, we need to transfer their effects.
+ return null;
+ } else {
+ // This fiber doesn't have work, but its subtree does. Clone the child
+ // fibers and continue.
+ cloneChildFibers(current$$1, workInProgress);
+ return workInProgress.child;
+ }
+}
+
+function beginWork(current$$1, workInProgress, renderExpirationTime) {
+ var updateExpirationTime = workInProgress.expirationTime;
+
+ if (current$$1 !== null) {
+ var oldProps = current$$1.memoizedProps;
+ var newProps = workInProgress.pendingProps;
+
+ if (oldProps !== newProps || hasContextChanged()) {
+ // If props or context changed, mark the fiber as having performed work.
+ // This may be unset if the props are determined to be equal later (memo).
+ didReceiveUpdate = true;
+ } else if (updateExpirationTime < renderExpirationTime) {
+ didReceiveUpdate = false;
+ // This fiber does not have any pending work. Bailout without entering
+ // the begin phase. There's still some bookkeeping we that needs to be done
+ // in this optimized path, mostly pushing stuff onto the stack.
+ switch (workInProgress.tag) {
+ case HostRoot:
+ pushHostRootContext(workInProgress);
+ resetHydrationState();
+ break;
+ case HostComponent:
+ pushHostContext(workInProgress);
+ break;
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ pushContextProvider(workInProgress);
+ }
+ break;
+ }
+ case HostPortal:
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ break;
+ case ContextProvider:
+ {
+ var newValue = workInProgress.memoizedProps.value;
+ pushProvider(workInProgress, newValue);
+ break;
+ }
+ case Profiler:
+ if (enableProfilerTimer) {
+ workInProgress.effectTag |= Update;
+ }
+ break;
+ case SuspenseComponent:
+ {
+ var state = workInProgress.memoizedState;
+ var didTimeout = state !== null;
+ if (didTimeout) {
+ // If this boundary is currently timed out, we need to decide
+ // whether to retry the primary children, or to skip over it and
+ // go straight to the fallback. Check the priority of the primary
+ var primaryChildFragment = workInProgress.child;
+ var primaryChildExpirationTime = primaryChildFragment.childExpirationTime;
+ if (primaryChildExpirationTime !== NoWork && primaryChildExpirationTime >= renderExpirationTime) {
+ // The primary children have pending work. Use the normal path
+ // to attempt to render the primary children again.
+ return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ } else {
+ // The primary children do not have pending work with sufficient
+ // priority. Bailout.
+ var child = bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ if (child !== null) {
+ // The fallback children have pending work. Skip over the
+ // primary children and work on the fallback.
+ return child.sibling;
+ } else {
+ return null;
+ }
+ }
+ }
+ break;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ // We know that this component will suspend again because if it has
+ // been unsuspended it has committed as a regular Suspense component.
+ // If it needs to be retried, it should have work scheduled on it.
+ workInProgress.effectTag |= DidCapture;
+ break;
+ }
+ }
+ }
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ } else {
+ didReceiveUpdate = false;
+ }
+
+ // Before entering the begin phase, clear the expiration time.
+ workInProgress.expirationTime = NoWork;
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ {
+ var elementType = workInProgress.elementType;
+ return mountIndeterminateComponent(current$$1, workInProgress, elementType, renderExpirationTime);
+ }
+ case LazyComponent:
+ {
+ var _elementType = workInProgress.elementType;
+ return mountLazyComponent(current$$1, workInProgress, _elementType, updateExpirationTime, renderExpirationTime);
+ }
+ case FunctionComponent:
+ {
+ var _Component = workInProgress.type;
+ var unresolvedProps = workInProgress.pendingProps;
+ var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps);
+ return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
+ }
+ case ClassComponent:
+ {
+ var _Component2 = workInProgress.type;
+ var _unresolvedProps = workInProgress.pendingProps;
+ var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
+ return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
+ }
+ case HostRoot:
+ return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
+ case HostComponent:
+ return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
+ case HostText:
+ return updateHostText(current$$1, workInProgress);
+ case SuspenseComponent:
+ return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ case HostPortal:
+ return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
+ case ForwardRef:
+ {
+ var type = workInProgress.type;
+ var _unresolvedProps2 = workInProgress.pendingProps;
+ var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
+ return updateForwardRef(current$$1, workInProgress, type, _resolvedProps2, renderExpirationTime);
+ }
+ case Fragment:
+ return updateFragment(current$$1, workInProgress, renderExpirationTime);
+ case Mode:
+ return updateMode(current$$1, workInProgress, renderExpirationTime);
+ case Profiler:
+ return updateProfiler(current$$1, workInProgress, renderExpirationTime);
+ case ContextProvider:
+ return updateContextProvider(current$$1, workInProgress, renderExpirationTime);
+ case ContextConsumer:
+ return updateContextConsumer(current$$1, workInProgress, renderExpirationTime);
+ case MemoComponent:
+ {
+ var _type2 = workInProgress.type;
+ var _unresolvedProps3 = workInProgress.pendingProps;
+ // Resolve outer props first, then resolve inner props.
+ var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
+ _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
+ return updateMemoComponent(current$$1, workInProgress, _type2, _resolvedProps3, updateExpirationTime, renderExpirationTime);
+ }
+ case SimpleMemoComponent:
+ {
+ return updateSimpleMemoComponent(current$$1, workInProgress, workInProgress.type, workInProgress.pendingProps, updateExpirationTime, renderExpirationTime);
+ }
+ case IncompleteClassComponent:
+ {
+ var _Component3 = workInProgress.type;
+ var _unresolvedProps4 = workInProgress.pendingProps;
+ var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4);
+ return mountIncompleteClassComponent(current$$1, workInProgress, _Component3, _resolvedProps4, renderExpirationTime);
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ return updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ }
+ break;
+ }
+ }
+ reactProdInvariant('156');
+}
+
+var valueCursor = createCursor(null);
+
+var currentlyRenderingFiber = null;
+var lastContextDependency = null;
+var lastContextWithAllBitsObserved = null;
+
+function resetContextDependences() {
+ // This is called right before React yields execution, to ensure `readContext`
+ // cannot be called outside the render phase.
+ currentlyRenderingFiber = null;
+ lastContextDependency = null;
+ lastContextWithAllBitsObserved = null;
+
+}
+
+
+
+
+
+function pushProvider(providerFiber, nextValue) {
+ var context = providerFiber.type._context;
+
+ if (isPrimaryRenderer) {
+ push(valueCursor, context._currentValue, providerFiber);
+
+ context._currentValue = nextValue;
+
+ } else {
+ push(valueCursor, context._currentValue2, providerFiber);
+
+ context._currentValue2 = nextValue;
+
+ }
+}
+
+function popProvider(providerFiber) {
+ var currentValue = valueCursor.current;
+
+ pop(valueCursor, providerFiber);
+
+ var context = providerFiber.type._context;
+ if (isPrimaryRenderer) {
+ context._currentValue = currentValue;
+ } else {
+ context._currentValue2 = currentValue;
+ }
+}
+
+function calculateChangedBits(context, newValue, oldValue) {
+ if (is(oldValue, newValue)) {
+ // No change
+ return 0;
+ } else {
+ var changedBits = typeof context._calculateChangedBits === 'function' ? context._calculateChangedBits(oldValue, newValue) : maxSigned31BitInt;
+
+ return changedBits | 0;
+ }
+}
+
+function scheduleWorkOnParentPath(parent, renderExpirationTime) {
+ // Update the child expiration time of all the ancestors, including
+ // the alternates.
+ var node = parent;
+ while (node !== null) {
+ var alternate = node.alternate;
+ if (node.childExpirationTime < renderExpirationTime) {
+ node.childExpirationTime = renderExpirationTime;
+ if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
+ alternate.childExpirationTime = renderExpirationTime;
+ }
+ } else if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
+ alternate.childExpirationTime = renderExpirationTime;
+ } else {
+ // Neither alternate was updated, which means the rest of the
+ // ancestor path already has sufficient priority.
+ break;
+ }
+ node = node.return;
+ }
+}
+
+function propagateContextChange(workInProgress, context, changedBits, renderExpirationTime) {
+ var fiber = workInProgress.child;
+ if (fiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ fiber.return = workInProgress;
+ }
+ while (fiber !== null) {
+ var nextFiber = void 0;
+
+ // Visit this fiber.
+ var list = fiber.contextDependencies;
+ if (list !== null) {
+ nextFiber = fiber.child;
+
+ var dependency = list.first;
+ while (dependency !== null) {
+ // Check if the context matches.
+ if (dependency.context === context && (dependency.observedBits & changedBits) !== 0) {
+ // Match! Schedule an update on this fiber.
+
+ if (fiber.tag === ClassComponent) {
+ // Schedule a force update on the work-in-progress.
+ var update = createUpdate(renderExpirationTime);
+ update.tag = ForceUpdate;
+ // TODO: Because we don't have a work-in-progress, this will add the
+ // update to the current fiber, too, which means it will persist even if
+ // this render is thrown away. Since it's a race condition, not sure it's
+ // worth fixing.
+ enqueueUpdate(fiber, update);
+ }
+
+ if (fiber.expirationTime < renderExpirationTime) {
+ fiber.expirationTime = renderExpirationTime;
+ }
+ var alternate = fiber.alternate;
+ if (alternate !== null && alternate.expirationTime < renderExpirationTime) {
+ alternate.expirationTime = renderExpirationTime;
+ }
+
+ scheduleWorkOnParentPath(fiber.return, renderExpirationTime);
+
+ // Mark the expiration time on the list, too.
+ if (list.expirationTime < renderExpirationTime) {
+ list.expirationTime = renderExpirationTime;
+ }
+
+ // Since we already found a match, we can stop traversing the
+ // dependency list.
+ break;
+ }
+ dependency = dependency.next;
+ }
+ } else if (fiber.tag === ContextProvider) {
+ // Don't scan deeper if this is a matching provider
+ nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
+ } else if (enableSuspenseServerRenderer && fiber.tag === DehydratedSuspenseComponent) {
+ // If a dehydrated suspense component is in this subtree, we don't know
+ // if it will have any context consumers in it. The best we can do is
+ // mark it as having updates on its children.
+ if (fiber.expirationTime < renderExpirationTime) {
+ fiber.expirationTime = renderExpirationTime;
+ }
+ var _alternate = fiber.alternate;
+ if (_alternate !== null && _alternate.expirationTime < renderExpirationTime) {
+ _alternate.expirationTime = renderExpirationTime;
+ }
+ // This is intentionally passing this fiber as the parent
+ // because we want to schedule this fiber as having work
+ // on its children. We'll use the childExpirationTime on
+ // this fiber to indicate that a context has changed.
+ scheduleWorkOnParentPath(fiber, renderExpirationTime);
+ nextFiber = fiber.sibling;
+ } else {
+ // Traverse down.
+ nextFiber = fiber.child;
+ }
+
+ if (nextFiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ nextFiber.return = fiber;
+ } else {
+ // No child. Traverse to next sibling.
+ nextFiber = fiber;
+ while (nextFiber !== null) {
+ if (nextFiber === workInProgress) {
+ // We're back to the root of this subtree. Exit.
+ nextFiber = null;
+ break;
+ }
+ var sibling = nextFiber.sibling;
+ if (sibling !== null) {
+ // Set the return pointer of the sibling to the work-in-progress fiber.
+ sibling.return = nextFiber.return;
+ nextFiber = sibling;
+ break;
+ }
+ // No more siblings. Traverse up.
+ nextFiber = nextFiber.return;
+ }
+ }
+ fiber = nextFiber;
+ }
+}
+
+function prepareToReadContext(workInProgress, renderExpirationTime) {
+ currentlyRenderingFiber = workInProgress;
+ lastContextDependency = null;
+ lastContextWithAllBitsObserved = null;
+
+ var currentDependencies = workInProgress.contextDependencies;
+ if (currentDependencies !== null && currentDependencies.expirationTime >= renderExpirationTime) {
+ // Context list has a pending update. Mark that this fiber performed work.
+ markWorkInProgressReceivedUpdate();
+ }
+
+ // Reset the work-in-progress list
+ workInProgress.contextDependencies = null;
+}
+
+function readContext(context, observedBits) {
+ if (lastContextWithAllBitsObserved === context) {
+ // Nothing to do. We already observe everything in this context.
+ } else if (observedBits === false || observedBits === 0) {
+ // Do not observe any updates.
+ } else {
+ var resolvedObservedBits = void 0; // Avoid deopting on observable arguments or heterogeneous types.
+ if (typeof observedBits !== 'number' || observedBits === maxSigned31BitInt) {
+ // Observe all updates.
+ lastContextWithAllBitsObserved = context;
+ resolvedObservedBits = maxSigned31BitInt;
+ } else {
+ resolvedObservedBits = observedBits;
+ }
+
+ var contextItem = {
+ context: context,
+ observedBits: resolvedObservedBits,
+ next: null
+ };
+
+ if (lastContextDependency === null) {
+ !(currentlyRenderingFiber !== null) ? reactProdInvariant('308') : void 0;
+
+ // This is the first dependency for this component. Create a new list.
+ lastContextDependency = contextItem;
+ currentlyRenderingFiber.contextDependencies = {
+ first: contextItem,
+ expirationTime: NoWork
+ };
+ } else {
+ // Append a new context item.
+ lastContextDependency = lastContextDependency.next = contextItem;
+ }
+ }
+ return isPrimaryRenderer ? context._currentValue : context._currentValue2;
+}
+
+// UpdateQueue is a linked list of prioritized updates.
+//
+// Like fibers, update queues come in pairs: a current queue, which represents
+// the visible state of the screen, and a work-in-progress queue, which can be
+// mutated and processed asynchronously before it is committed — a form of
+// double buffering. If a work-in-progress render is discarded before finishing,
+// we create a new work-in-progress by cloning the current queue.
+//
+// Both queues share a persistent, singly-linked list structure. To schedule an
+// update, we append it to the end of both queues. Each queue maintains a
+// pointer to first update in the persistent list that hasn't been processed.
+// The work-in-progress pointer always has a position equal to or greater than
+// the current queue, since we always work on that one. The current queue's
+// pointer is only updated during the commit phase, when we swap in the
+// work-in-progress.
+//
+// For example:
+//
+// Current pointer: A - B - C - D - E - F
+// Work-in-progress pointer: D - E - F
+// ^
+// The work-in-progress queue has
+// processed more updates than current.
+//
+// The reason we append to both queues is because otherwise we might drop
+// updates without ever processing them. For example, if we only add updates to
+// the work-in-progress queue, some updates could be lost whenever a work-in
+// -progress render restarts by cloning from current. Similarly, if we only add
+// updates to the current queue, the updates will be lost whenever an already
+// in-progress queue commits and swaps with the current queue. However, by
+// adding to both queues, we guarantee that the update will be part of the next
+// work-in-progress. (And because the work-in-progress queue becomes the
+// current queue once it commits, there's no danger of applying the same
+// update twice.)
+//
+// Prioritization
+// --------------
+//
+// Updates are not sorted by priority, but by insertion; new updates are always
+// appended to the end of the list.
+//
+// The priority is still important, though. When processing the update queue
+// during the render phase, only the updates with sufficient priority are
+// included in the result. If we skip an update because it has insufficient
+// priority, it remains in the queue to be processed later, during a lower
+// priority render. Crucially, all updates subsequent to a skipped update also
+// remain in the queue *regardless of their priority*. That means high priority
+// updates are sometimes processed twice, at two separate priorities. We also
+// keep track of a base state, that represents the state before the first
+// update in the queue is applied.
+//
+// For example:
+//
+// Given a base state of '', and the following queue of updates
+//
+// A1 - B2 - C1 - D2
+//
+// where the number indicates the priority, and the update is applied to the
+// previous state by appending a letter, React will process these updates as
+// two separate renders, one per distinct priority level:
+//
+// First render, at priority 1:
+// Base state: ''
+// Updates: [A1, C1]
+// Result state: 'AC'
+//
+// Second render, at priority 2:
+// Base state: 'A' <- The base state does not include C1,
+// because B2 was skipped.
+// Updates: [B2, C1, D2] <- C1 was rebased on top of B2
+// Result state: 'ABCD'
+//
+// Because we process updates in insertion order, and rebase high priority
+// updates when preceding updates are skipped, the final result is deterministic
+// regardless of priority. Intermediate state may vary according to system
+// resources, but the final state is always the same.
+
+var UpdateState = 0;
+var ReplaceState = 1;
+var ForceUpdate = 2;
+var CaptureUpdate = 3;
+
+// Global state that is reset at the beginning of calling `processUpdateQueue`.
+// It should only be read right after calling `processUpdateQueue`, via
+// `checkHasForceUpdateAfterProcessing`.
+var hasForceUpdate = false;
+
+
+function createUpdateQueue(baseState) {
+ var queue = {
+ baseState: baseState,
+ firstUpdate: null,
+ lastUpdate: null,
+ firstCapturedUpdate: null,
+ lastCapturedUpdate: null,
+ firstEffect: null,
+ lastEffect: null,
+ firstCapturedEffect: null,
+ lastCapturedEffect: null
+ };
+ return queue;
+}
+
+function cloneUpdateQueue(currentQueue) {
+ var queue = {
+ baseState: currentQueue.baseState,
+ firstUpdate: currentQueue.firstUpdate,
+ lastUpdate: currentQueue.lastUpdate,
+
+ // TODO: With resuming, if we bail out and resuse the child tree, we should
+ // keep these effects.
+ firstCapturedUpdate: null,
+ lastCapturedUpdate: null,
+
+ firstEffect: null,
+ lastEffect: null,
+
+ firstCapturedEffect: null,
+ lastCapturedEffect: null
+ };
+ return queue;
+}
+
+function createUpdate(expirationTime) {
+ return {
+ expirationTime: expirationTime,
+
+ tag: UpdateState,
+ payload: null,
+ callback: null,
+
+ next: null,
+ nextEffect: null
+ };
+}
+
+function appendUpdateToQueue(queue, update) {
+ // Append the update to the end of the list.
+ if (queue.lastUpdate === null) {
+ // Queue is empty
+ queue.firstUpdate = queue.lastUpdate = update;
+ } else {
+ queue.lastUpdate.next = update;
+ queue.lastUpdate = update;
+ }
+}
+
+function enqueueUpdate(fiber, update) {
+ // Update queues are created lazily.
+ var alternate = fiber.alternate;
+ var queue1 = void 0;
+ var queue2 = void 0;
+ if (alternate === null) {
+ // There's only one fiber.
+ queue1 = fiber.updateQueue;
+ queue2 = null;
+ if (queue1 === null) {
+ queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
+ }
+ } else {
+ // There are two owners.
+ queue1 = fiber.updateQueue;
+ queue2 = alternate.updateQueue;
+ if (queue1 === null) {
+ if (queue2 === null) {
+ // Neither fiber has an update queue. Create new ones.
+ queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
+ queue2 = alternate.updateQueue = createUpdateQueue(alternate.memoizedState);
+ } else {
+ // Only one fiber has an update queue. Clone to create a new one.
+ queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
+ }
+ } else {
+ if (queue2 === null) {
+ // Only one fiber has an update queue. Clone to create a new one.
+ queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
+ } else {
+ // Both owners have an update queue.
+ }
+ }
+ }
+ if (queue2 === null || queue1 === queue2) {
+ // There's only a single queue.
+ appendUpdateToQueue(queue1, update);
+ } else {
+ // There are two queues. We need to append the update to both queues,
+ // while accounting for the persistent structure of the list — we don't
+ // want the same update to be added multiple times.
+ if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
+ // One of the queues is not empty. We must add the update to both queues.
+ appendUpdateToQueue(queue1, update);
+ appendUpdateToQueue(queue2, update);
+ } else {
+ // Both queues are non-empty. The last update is the same in both lists,
+ // because of structural sharing. So, only append to one of the lists.
+ appendUpdateToQueue(queue1, update);
+ // But we still need to update the `lastUpdate` pointer of queue2.
+ queue2.lastUpdate = update;
+ }
+ }
+
+
+}
+
+function enqueueCapturedUpdate(workInProgress, update) {
+ // Captured updates go into a separate list, and only on the work-in-
+ // progress queue.
+ var workInProgressQueue = workInProgress.updateQueue;
+ if (workInProgressQueue === null) {
+ workInProgressQueue = workInProgress.updateQueue = createUpdateQueue(workInProgress.memoizedState);
+ } else {
+ // TODO: I put this here rather than createWorkInProgress so that we don't
+ // clone the queue unnecessarily. There's probably a better way to
+ // structure this.
+ workInProgressQueue = ensureWorkInProgressQueueIsAClone(workInProgress, workInProgressQueue);
+ }
+
+ // Append the update to the end of the list.
+ if (workInProgressQueue.lastCapturedUpdate === null) {
+ // This is the first render phase update
+ workInProgressQueue.firstCapturedUpdate = workInProgressQueue.lastCapturedUpdate = update;
+ } else {
+ workInProgressQueue.lastCapturedUpdate.next = update;
+ workInProgressQueue.lastCapturedUpdate = update;
+ }
+}
+
+function ensureWorkInProgressQueueIsAClone(workInProgress, queue) {
+ var current = workInProgress.alternate;
+ if (current !== null) {
+ // If the work-in-progress queue is equal to the current queue,
+ // we need to clone it first.
+ if (queue === current.updateQueue) {
+ queue = workInProgress.updateQueue = cloneUpdateQueue(queue);
+ }
+ }
+ return queue;
+}
+
+function getStateFromUpdate(workInProgress, queue, update, prevState, nextProps, instance) {
+ switch (update.tag) {
+ case ReplaceState:
+ {
+ var _payload = update.payload;
+ if (typeof _payload === 'function') {
+ // Updater function
+ var nextState = _payload.call(instance, prevState, nextProps);
+ return nextState;
+ }
+ // State object
+ return _payload;
+ }
+ case CaptureUpdate:
+ {
+ workInProgress.effectTag = workInProgress.effectTag & ~ShouldCapture | DidCapture;
+ }
+ // Intentional fallthrough
+ case UpdateState:
+ {
+ var _payload2 = update.payload;
+ var partialState = void 0;
+ if (typeof _payload2 === 'function') {
+ // Updater function
+ partialState = _payload2.call(instance, prevState, nextProps);
+
+ } else {
+ // Partial state object
+ partialState = _payload2;
+ }
+ if (partialState === null || partialState === undefined) {
+ // Null and undefined are treated as no-ops.
+ return prevState;
+ }
+ // Merge the partial state and the previous state.
+ return _assign({}, prevState, partialState);
+ }
+ case ForceUpdate:
+ {
+ hasForceUpdate = true;
+ return prevState;
+ }
+ }
+ return prevState;
+}
+
+function processUpdateQueue(workInProgress, queue, props, instance, renderExpirationTime) {
+ hasForceUpdate = false;
+
+ queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);
+
+ var newBaseState = queue.baseState;
+ var newFirstUpdate = null;
+ var newExpirationTime = NoWork;
+
+ // Iterate through the list of updates to compute the result.
+ var update = queue.firstUpdate;
+ var resultState = newBaseState;
+ while (update !== null) {
+ var updateExpirationTime = update.expirationTime;
+ if (updateExpirationTime < renderExpirationTime) {
+ // This update does not have sufficient priority. Skip it.
+ if (newFirstUpdate === null) {
+ // This is the first skipped update. It will be the first update in
+ // the new list.
+ newFirstUpdate = update;
+ // Since this is the first update that was skipped, the current result
+ // is the new base state.
+ newBaseState = resultState;
+ }
+ // Since this update will remain in the list, update the remaining
+ // expiration time.
+ if (newExpirationTime < updateExpirationTime) {
+ newExpirationTime = updateExpirationTime;
+ }
+ } else {
+ // This update does have sufficient priority. Process it and compute
+ // a new result.
+ resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
+ var _callback = update.callback;
+ if (_callback !== null) {
+ workInProgress.effectTag |= Callback;
+ // Set this to null, in case it was mutated during an aborted render.
+ update.nextEffect = null;
+ if (queue.lastEffect === null) {
+ queue.firstEffect = queue.lastEffect = update;
+ } else {
+ queue.lastEffect.nextEffect = update;
+ queue.lastEffect = update;
+ }
+ }
+ }
+ // Continue to the next update.
+ update = update.next;
+ }
+
+ // Separately, iterate though the list of captured updates.
+ var newFirstCapturedUpdate = null;
+ update = queue.firstCapturedUpdate;
+ while (update !== null) {
+ var _updateExpirationTime = update.expirationTime;
+ if (_updateExpirationTime < renderExpirationTime) {
+ // This update does not have sufficient priority. Skip it.
+ if (newFirstCapturedUpdate === null) {
+ // This is the first skipped captured update. It will be the first
+ // update in the new list.
+ newFirstCapturedUpdate = update;
+ // If this is the first update that was skipped, the current result is
+ // the new base state.
+ if (newFirstUpdate === null) {
+ newBaseState = resultState;
+ }
+ }
+ // Since this update will remain in the list, update the remaining
+ // expiration time.
+ if (newExpirationTime < _updateExpirationTime) {
+ newExpirationTime = _updateExpirationTime;
+ }
+ } else {
+ // This update does have sufficient priority. Process it and compute
+ // a new result.
+ resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
+ var _callback2 = update.callback;
+ if (_callback2 !== null) {
+ workInProgress.effectTag |= Callback;
+ // Set this to null, in case it was mutated during an aborted render.
+ update.nextEffect = null;
+ if (queue.lastCapturedEffect === null) {
+ queue.firstCapturedEffect = queue.lastCapturedEffect = update;
+ } else {
+ queue.lastCapturedEffect.nextEffect = update;
+ queue.lastCapturedEffect = update;
+ }
+ }
+ }
+ update = update.next;
+ }
+
+ if (newFirstUpdate === null) {
+ queue.lastUpdate = null;
+ }
+ if (newFirstCapturedUpdate === null) {
+ queue.lastCapturedUpdate = null;
+ } else {
+ workInProgress.effectTag |= Callback;
+ }
+ if (newFirstUpdate === null && newFirstCapturedUpdate === null) {
+ // We processed every update, without skipping. That means the new base
+ // state is the same as the result state.
+ newBaseState = resultState;
+ }
+
+ queue.baseState = newBaseState;
+ queue.firstUpdate = newFirstUpdate;
+ queue.firstCapturedUpdate = newFirstCapturedUpdate;
+
+ // Set the remaining expiration time to be whatever is remaining in the queue.
+ // This should be fine because the only two other things that contribute to
+ // expiration time are props and context. We're already in the middle of the
+ // begin phase by the time we start processing the queue, so we've already
+ // dealt with the props. Context in components that specify
+ // shouldComponentUpdate is tricky; but we'll have to account for
+ // that regardless.
+ workInProgress.expirationTime = newExpirationTime;
+ workInProgress.memoizedState = resultState;
+
+
+}
+
+function callCallback(callback, context) {
+ !(typeof callback === 'function') ? reactProdInvariant('191', callback) : void 0;
+ callback.call(context);
+}
+
+function resetHasForceUpdateBeforeProcessing() {
+ hasForceUpdate = false;
+}
+
+function checkHasForceUpdateAfterProcessing() {
+ return hasForceUpdate;
+}
+
+function commitUpdateQueue(finishedWork, finishedQueue, instance, renderExpirationTime) {
+ // If the finished render included captured updates, and there are still
+ // lower priority updates left over, we need to keep the captured updates
+ // in the queue so that they are rebased and not dropped once we process the
+ // queue again at the lower priority.
+ if (finishedQueue.firstCapturedUpdate !== null) {
+ // Join the captured update list to the end of the normal list.
+ if (finishedQueue.lastUpdate !== null) {
+ finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;
+ finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;
+ }
+ // Clear the list of captured updates.
+ finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;
+ }
+
+ // Commit the effects
+ commitUpdateEffects(finishedQueue.firstEffect, instance);
+ finishedQueue.firstEffect = finishedQueue.lastEffect = null;
+
+ commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);
+ finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
+}
+
+function commitUpdateEffects(effect, instance) {
+ while (effect !== null) {
+ var _callback3 = effect.callback;
+ if (_callback3 !== null) {
+ effect.callback = null;
+ callCallback(_callback3, instance);
+ }
+ effect = effect.nextEffect;
+ }
+}
+
+function createCapturedValue(value, source) {
+ // If the value is an error, call this function immediately after it is thrown
+ // so the stack is accurate.
+ return {
+ value: value,
+ source: source,
+ stack: getStackByFiberInDevAndProd(source)
+ };
+}
+
+function markUpdate(workInProgress) {
+ // Tag the fiber with an update effect. This turns a Placement into
+ // a PlacementAndUpdate.
+ workInProgress.effectTag |= Update;
+}
+
+function markRef$1(workInProgress) {
+ workInProgress.effectTag |= Ref;
+}
+
+var appendAllChildren = void 0;
+var updateHostContainer = void 0;
+var updateHostComponent$1 = void 0;
+var updateHostText$1 = void 0;
+if (supportsMutation) {
+ // Mutation mode
+
+ appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ appendInitialChild(parent, node.stateNode);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+
+ updateHostContainer = function (workInProgress) {
+ // Noop
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ // If we have an alternate, that means this is an update and we need to
+ // schedule a side-effect to do the updates.
+ var oldProps = current.memoizedProps;
+ if (oldProps === newProps) {
+ // In mutation mode, this is sufficient for a bailout because
+ // we won't touch this node even if children changed.
+ return;
+ }
+
+ // If we get updated because one of our children updated, we don't
+ // have newProps so we'll have to reuse them.
+ // TODO: Split the update API as separate for the props vs. children.
+ // Even better would be if children weren't special cased at all tho.
+ var instance = workInProgress.stateNode;
+ var currentHostContext = getHostContext();
+ // TODO: Experiencing an error where oldProps is null. Suggests a host
+ // component is hitting the resume path. Figure out why. Possibly
+ // related to `hidden`.
+ var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
+ // TODO: Type this specific to this type of component.
+ workInProgress.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update. All the work is done in commitWork.
+ if (updatePayload) {
+ markUpdate(workInProgress);
+ }
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ // If the text differs, mark it as an update. All the work in done in commitWork.
+ if (oldText !== newText) {
+ markUpdate(workInProgress);
+ }
+ };
+} else if (supportsPersistence) {
+ // Persistent host tree mode
+
+ appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ // eslint-disable-next-line no-labels
+ branches: if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var props = node.memoizedProps;
+ var type = node.type;
+ if (isHidden) {
+ // This child is inside a timed out tree. Hide it.
+ instance = cloneHiddenInstance(instance, type, props, node);
+ } else {
+ // This child was previously inside a timed out tree. If it was not
+ // updated during this render, it may need to be unhidden. Clone
+ // again to be sure.
+ instance = cloneUnhiddenInstance(instance, type, props, node);
+ }
+ node.stateNode = instance;
+ }
+ appendInitialChild(parent, instance);
+ } else if (node.tag === HostText) {
+ var _instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var text = node.memoizedProps;
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ if (isHidden) {
+ _instance = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ } else {
+ _instance = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ }
+ node.stateNode = _instance;
+ }
+ appendInitialChild(parent, _instance);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.tag === SuspenseComponent) {
+ var current = node.alternate;
+ if (current !== null) {
+ var oldState = current.memoizedState;
+ var newState = node.memoizedState;
+ var oldIsHidden = oldState !== null;
+ var newIsHidden = newState !== null;
+ if (oldIsHidden !== newIsHidden) {
+ // The placeholder either just timed out or switched back to the normal
+ // children after having previously timed out. Toggle the visibility of
+ // the direct host children.
+ var primaryChildParent = newIsHidden ? node.child : node;
+ if (primaryChildParent !== null) {
+ appendAllChildren(parent, primaryChildParent, true, newIsHidden);
+ }
+ // eslint-disable-next-line no-labels
+ break branches;
+ }
+ }
+ if (node.child !== null) {
+ // Continue traversing like normal
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ // $FlowFixMe This is correct but Flow is confused by the labeled break.
+ node = node;
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+
+ // An unfortunate fork of appendAllChildren because we have two different parent types.
+ var appendAllChildrenToContainer = function (containerChildSet, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ // eslint-disable-next-line no-labels
+ branches: if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var props = node.memoizedProps;
+ var type = node.type;
+ if (isHidden) {
+ // This child is inside a timed out tree. Hide it.
+ instance = cloneHiddenInstance(instance, type, props, node);
+ } else {
+ // This child was previously inside a timed out tree. If it was not
+ // updated during this render, it may need to be unhidden. Clone
+ // again to be sure.
+ instance = cloneUnhiddenInstance(instance, type, props, node);
+ }
+ node.stateNode = instance;
+ }
+ appendChildToContainerChildSet(containerChildSet, instance);
+ } else if (node.tag === HostText) {
+ var _instance2 = node.stateNode;
+ if (needsVisibilityToggle) {
+ var text = node.memoizedProps;
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ if (isHidden) {
+ _instance2 = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ } else {
+ _instance2 = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ }
+ node.stateNode = _instance2;
+ }
+ appendChildToContainerChildSet(containerChildSet, _instance2);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.tag === SuspenseComponent) {
+ var current = node.alternate;
+ if (current !== null) {
+ var oldState = current.memoizedState;
+ var newState = node.memoizedState;
+ var oldIsHidden = oldState !== null;
+ var newIsHidden = newState !== null;
+ if (oldIsHidden !== newIsHidden) {
+ // The placeholder either just timed out or switched back to the normal
+ // children after having previously timed out. Toggle the visibility of
+ // the direct host children.
+ var primaryChildParent = newIsHidden ? node.child : node;
+ if (primaryChildParent !== null) {
+ appendAllChildrenToContainer(containerChildSet, primaryChildParent, true, newIsHidden);
+ }
+ // eslint-disable-next-line no-labels
+ break branches;
+ }
+ }
+ if (node.child !== null) {
+ // Continue traversing like normal
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ // $FlowFixMe This is correct but Flow is confused by the labeled break.
+ node = node;
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+ updateHostContainer = function (workInProgress) {
+ var portalOrRoot = workInProgress.stateNode;
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged) {
+ // No changes, just reuse the existing instance.
+ } else {
+ var container = portalOrRoot.containerInfo;
+ var newChildSet = createContainerChildSet(container);
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildrenToContainer(newChildSet, workInProgress, false, false);
+ portalOrRoot.pendingChildren = newChildSet;
+ // Schedule an update on the container to swap out the container.
+ markUpdate(workInProgress);
+ finalizeContainerChildren(container, newChildSet);
+ }
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ var currentInstance = current.stateNode;
+ var oldProps = current.memoizedProps;
+ // If there are no effects associated with this node, then none of our children had any updates.
+ // This guarantees that we can reuse all of them.
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged && oldProps === newProps) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ return;
+ }
+ var recyclableInstance = workInProgress.stateNode;
+ var currentHostContext = getHostContext();
+ var updatePayload = null;
+ if (oldProps !== newProps) {
+ updatePayload = prepareUpdate(recyclableInstance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
+ }
+ if (childrenUnchanged && updatePayload === null) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ return;
+ }
+ var newInstance = cloneInstance(currentInstance, updatePayload, type, oldProps, newProps, workInProgress, childrenUnchanged, recyclableInstance);
+ if (finalizeInitialChildren(newInstance, type, newProps, rootContainerInstance, currentHostContext)) {
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = newInstance;
+ if (childrenUnchanged) {
+ // If there are no other effects in this tree, we need to flag this node as having one.
+ // Even though we're not going to use it for anything.
+ // Otherwise parents won't know that there are new children to propagate upwards.
+ markUpdate(workInProgress);
+ } else {
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildren(newInstance, workInProgress, false, false);
+ }
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ if (oldText !== newText) {
+ // If the text content differs, we'll create a new text instance for it.
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ workInProgress.stateNode = createTextInstance(newText, rootContainerInstance, currentHostContext, workInProgress);
+ // We'll have to mark it as having an effect, even though we won't use the effect for anything.
+ // This lets the parents know that at least one of their children has changed.
+ markUpdate(workInProgress);
+ }
+ };
+} else {
+ // No host operations
+ updateHostContainer = function (workInProgress) {
+ // Noop
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ // Noop
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ // Noop
+ };
+}
+
+function completeWork(current, workInProgress, renderExpirationTime) {
+ var newProps = workInProgress.pendingProps;
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ break;
+ case LazyComponent:
+ break;
+ case SimpleMemoComponent:
+ case FunctionComponent:
+ break;
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
+ }
+ break;
+ }
+ case HostRoot:
+ {
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ var fiberRoot = workInProgress.stateNode;
+ if (fiberRoot.pendingContext) {
+ fiberRoot.context = fiberRoot.pendingContext;
+ fiberRoot.pendingContext = null;
+ }
+ if (current === null || current.child === null) {
+ // If we hydrated, pop so that we can delete any remaining children
+ // that weren't hydrated.
+ popHydrationState(workInProgress);
+ // This resets the hacky state to fix isMounted before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag &= ~Placement;
+ }
+ updateHostContainer(workInProgress);
+ break;
+ }
+ case HostComponent:
+ {
+ popHostContext(workInProgress);
+ var rootContainerInstance = getRootHostContainer();
+ var type = workInProgress.type;
+ if (current !== null && workInProgress.stateNode != null) {
+ updateHostComponent$1(current, workInProgress, type, newProps, rootContainerInstance);
+
+ if (current.ref !== workInProgress.ref) {
+ markRef$1(workInProgress);
+ }
+ } else {
+ if (!newProps) {
+ !(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
+ // This can happen when we abort work.
+ break;
+ }
+
+ var currentHostContext = getHostContext();
+ // TODO: Move createInstance to beginWork and keep it on a context
+ // "stack" as the parent. Then append children as we go in beginWork
+ // or completeWork depending on we want to add then top->down or
+ // bottom->up. Top->down is faster in IE11.
+ var wasHydrated = popHydrationState(workInProgress);
+ if (wasHydrated) {
+ // TODO: Move this and createInstance step into the beginPhase
+ // to consolidate.
+ if (prepareToHydrateHostInstance(workInProgress, rootContainerInstance, currentHostContext)) {
+ // If changes to the hydrated node needs to be applied at the
+ // commit-phase we mark this as such.
+ markUpdate(workInProgress);
+ }
+ } else {
+ var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
+
+ appendAllChildren(instance, workInProgress, false, false);
+
+ // Certain renderers require commit-time effects for initial mount.
+ // (eg DOM renderer supports auto-focus for certain elements).
+ // Make sure such renderers get scheduled for later work.
+ if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance, currentHostContext)) {
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = instance;
+ }
+
+ if (workInProgress.ref !== null) {
+ // If there is a ref on a host node we need to schedule a callback
+ markRef$1(workInProgress);
+ }
+ }
+ break;
+ }
+ case HostText:
+ {
+ var newText = newProps;
+ if (current && workInProgress.stateNode != null) {
+ var oldText = current.memoizedProps;
+ // If we have an alternate, that means this is an update and we need
+ // to schedule a side-effect to do the updates.
+ updateHostText$1(current, workInProgress, oldText, newText);
+ } else {
+ if (typeof newText !== 'string') {
+ !(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
+ // This can happen when we abort work.
+ }
+ var _rootContainerInstance = getRootHostContainer();
+ var _currentHostContext = getHostContext();
+ var _wasHydrated = popHydrationState(workInProgress);
+ if (_wasHydrated) {
+ if (prepareToHydrateHostTextInstance(workInProgress)) {
+ markUpdate(workInProgress);
+ }
+ } else {
+ workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext, workInProgress);
+ }
+ }
+ break;
+ }
+ case ForwardRef:
+ break;
+ case SuspenseComponent:
+ {
+ var nextState = workInProgress.memoizedState;
+ if ((workInProgress.effectTag & DidCapture) !== NoEffect) {
+ // Something suspended. Re-render with the fallback children.
+ workInProgress.expirationTime = renderExpirationTime;
+ // Do not reset the effect list.
+ return workInProgress;
+ }
+
+ var nextDidTimeout = nextState !== null;
+ var prevDidTimeout = current !== null && current.memoizedState !== null;
+
+ if (current !== null && !nextDidTimeout && prevDidTimeout) {
+ // We just switched from the fallback to the normal children. Delete
+ // the fallback.
+ // TODO: Would it be better to store the fallback fragment on
+ var currentFallbackChild = current.child.sibling;
+ if (currentFallbackChild !== null) {
+ // Deletions go at the beginning of the return fiber's effect list
+ var first = workInProgress.firstEffect;
+ if (first !== null) {
+ workInProgress.firstEffect = currentFallbackChild;
+ currentFallbackChild.nextEffect = first;
+ } else {
+ workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChild;
+ currentFallbackChild.nextEffect = null;
+ }
+ currentFallbackChild.effectTag = Deletion;
+ }
+ }
+
+ if (nextDidTimeout || prevDidTimeout) {
+ // If the children are hidden, or if they were previous hidden, schedule
+ // an effect to toggle their visibility. This is also used to attach a
+ // retry listener to the promise.
+ workInProgress.effectTag |= Update;
+ }
+ break;
+ }
+ case Fragment:
+ break;
+ case Mode:
+ break;
+ case Profiler:
+ break;
+ case HostPortal:
+ popHostContainer(workInProgress);
+ updateHostContainer(workInProgress);
+ break;
+ case ContextProvider:
+ // Pop provider fiber
+ popProvider(workInProgress);
+ break;
+ case ContextConsumer:
+ break;
+ case MemoComponent:
+ break;
+ case IncompleteClassComponent:
+ {
+ // Same as class component case. I put it down here so that the tags are
+ // sequential to ensure this switch is compiled to a jump table.
+ var _Component = workInProgress.type;
+ if (isContextProvider(_Component)) {
+ popContext(workInProgress);
+ }
+ break;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ if (current === null) {
+ var _wasHydrated2 = popHydrationState(workInProgress);
+ !_wasHydrated2 ? reactProdInvariant('318') : void 0;
+ skipPastDehydratedSuspenseInstance(workInProgress);
+ } else if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This boundary did not suspend so it's now hydrated.
+ // To handle any future suspense cases, we're going to now upgrade it
+ // to a Suspense component. We detach it from the existing current fiber.
+ current.alternate = null;
+ workInProgress.alternate = null;
+ workInProgress.tag = SuspenseComponent;
+ workInProgress.memoizedState = null;
+ workInProgress.stateNode = null;
+ }
+ }
+ break;
+ }
+ default:
+ reactProdInvariant('156');
+ }
+
+ return null;
+}
+
+function shouldCaptureSuspense(workInProgress) {
+ // In order to capture, the Suspense component must have a fallback prop.
+ if (workInProgress.memoizedProps.fallback === undefined) {
+ return false;
+ }
+ // If it was the primary children that just suspended, capture and render the
+ // fallback. Otherwise, don't capture and bubble to the next boundary.
+ var nextState = workInProgress.memoizedState;
+ return nextState === null;
+}
+
+// This module is forked in different environments.
+// By default, return `true` to log errors to the console.
+// Forks can return `false` if this isn't desirable.
+function showErrorDialog(capturedError) {
+ return true;
+}
+
+function logCapturedError(capturedError) {
+ var logError = showErrorDialog(capturedError);
+
+ // Allow injected showErrorDialog() to prevent default console.error logging.
+ // This enables renderers like ReactNative to better manage redbox behavior.
+ if (logError === false) {
+ return;
+ }
+
+ var error = capturedError.error;
+ {
+ // In production, we print the error directly.
+ // This will include the message, the JS stack, and anything the browser wants to show.
+ // We pass the error object instead of custom message so that the browser displays the error natively.
+ console.error(error);
+ }
+}
+
+var PossiblyWeakSet$1 = typeof WeakSet === 'function' ? WeakSet : Set;
+
+function logError(boundary, errorInfo) {
+ var source = errorInfo.source;
+ var stack = errorInfo.stack;
+ if (stack === null && source !== null) {
+ stack = getStackByFiberInDevAndProd(source);
+ }
+
+ var capturedError = {
+ componentName: source !== null ? getComponentName(source.type) : null,
+ componentStack: stack !== null ? stack : '',
+ error: errorInfo.value,
+ errorBoundary: null,
+ errorBoundaryName: null,
+ errorBoundaryFound: false,
+ willRetry: false
+ };
+
+ if (boundary !== null && boundary.tag === ClassComponent) {
+ capturedError.errorBoundary = boundary.stateNode;
+ capturedError.errorBoundaryName = getComponentName(boundary.type);
+ capturedError.errorBoundaryFound = true;
+ capturedError.willRetry = true;
+ }
+
+ try {
+ logCapturedError(capturedError);
+ } catch (e) {
+ // This method must not throw, or React internal state will get messed up.
+ // If console.error is overridden, or logCapturedError() shows a dialog that throws,
+ // we want to report this error outside of the normal stack as a last resort.
+ // https://github.com/facebook/react/issues/13188
+ setTimeout(function () {
+ throw e;
+ });
+ }
+}
+
+var callComponentWillUnmountWithTimer = function (current$$1, instance) {
+ startPhaseTimer(current$$1, 'componentWillUnmount');
+ instance.props = current$$1.memoizedProps;
+ instance.state = current$$1.memoizedState;
+ instance.componentWillUnmount();
+ stopPhaseTimer();
+};
+
+// Capture errors so they don't interrupt unmounting.
+function safelyCallComponentWillUnmount(current$$1, instance) {
+ {
+ try {
+ callComponentWillUnmountWithTimer(current$$1, instance);
+ } catch (unmountError) {
+ captureCommitPhaseError(current$$1, unmountError);
+ }
+ }
+}
+
+function safelyDetachRef(current$$1) {
+ var ref = current$$1.ref;
+ if (ref !== null) {
+ if (typeof ref === 'function') {
+ {
+ try {
+ ref(null);
+ } catch (refError) {
+ captureCommitPhaseError(current$$1, refError);
+ }
+ }
+ } else {
+ ref.current = null;
+ }
+ }
+}
+
+function safelyCallDestroy(current$$1, destroy) {
+ {
+ try {
+ destroy();
+ } catch (error) {
+ captureCommitPhaseError(current$$1, error);
+ }
+ }
+}
+
+function commitBeforeMutationLifeCycles(current$$1, finishedWork) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent:
+ {
+ commitHookEffectList(UnmountSnapshot, NoEffect$1, finishedWork);
+ return;
+ }
+ case ClassComponent:
+ {
+ if (finishedWork.effectTag & Snapshot) {
+ if (current$$1 !== null) {
+ var prevProps = current$$1.memoizedProps;
+ var prevState = current$$1.memoizedState;
+ startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
+ var instance = finishedWork.stateNode;
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
+ stopPhaseTimer();
+ }
+ }
+ return;
+ }
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ case IncompleteClassComponent:
+ // Nothing to do for these component types
+ return;
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function commitHookEffectList(unmountTag, mountTag, finishedWork) {
+ var updateQueue = finishedWork.updateQueue;
+ var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
+ do {
+ if ((effect.tag & unmountTag) !== NoEffect$1) {
+ // Unmount
+ var destroy = effect.destroy;
+ effect.destroy = undefined;
+ if (destroy !== undefined) {
+ destroy();
+ }
+ }
+ if ((effect.tag & mountTag) !== NoEffect$1) {
+ // Mount
+ var create = effect.create;
+ effect.destroy = create();
+
+
+ }
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+}
+
+function commitPassiveHookEffects(finishedWork) {
+ commitHookEffectList(UnmountPassive, NoEffect$1, finishedWork);
+ commitHookEffectList(NoEffect$1, MountPassive, finishedWork);
+}
+
+function commitLifeCycles(finishedRoot, current$$1, finishedWork, committedExpirationTime) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent:
+ {
+ commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
+ break;
+ }
+ case ClassComponent:
+ {
+ var instance = finishedWork.stateNode;
+ if (finishedWork.effectTag & Update) {
+ if (current$$1 === null) {
+ startPhaseTimer(finishedWork, 'componentDidMount');
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ instance.componentDidMount();
+ stopPhaseTimer();
+ } else {
+ var prevProps = finishedWork.elementType === finishedWork.type ? current$$1.memoizedProps : resolveDefaultProps(finishedWork.type, current$$1.memoizedProps);
+ var prevState = current$$1.memoizedState;
+ startPhaseTimer(finishedWork, 'componentDidUpdate');
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
+ stopPhaseTimer();
+ }
+ }
+ var updateQueue = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ commitUpdateQueue(finishedWork, updateQueue, instance, committedExpirationTime);
+ }
+ return;
+ }
+ case HostRoot:
+ {
+ var _updateQueue = finishedWork.updateQueue;
+ if (_updateQueue !== null) {
+ var _instance = null;
+ if (finishedWork.child !== null) {
+ switch (finishedWork.child.tag) {
+ case HostComponent:
+ _instance = getPublicInstance(finishedWork.child.stateNode);
+ break;
+ case ClassComponent:
+ _instance = finishedWork.child.stateNode;
+ break;
+ }
+ }
+ commitUpdateQueue(finishedWork, _updateQueue, _instance, committedExpirationTime);
+ }
+ return;
+ }
+ case HostComponent:
+ {
+ var _instance2 = finishedWork.stateNode;
+
+ // Renderers may schedule work to be done after host components are mounted
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
+ // These effects should only be committed when components are first mounted,
+ // aka when there is no current/alternate.
+ if (current$$1 === null && finishedWork.effectTag & Update) {
+ var type = finishedWork.type;
+ var props = finishedWork.memoizedProps;
+ commitMount(_instance2, type, props, finishedWork);
+ }
+
+ return;
+ }
+ case HostText:
+ {
+ // We have no life-cycles associated with text.
+ return;
+ }
+ case HostPortal:
+ {
+ // We have no life-cycles associated with portals.
+ return;
+ }
+ case Profiler:
+ {
+ if (enableProfilerTimer) {
+ var onRender = finishedWork.memoizedProps.onRender;
+
+ if (enableSchedulerTracing) {
+ onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime(), finishedRoot.memoizedInteractions);
+ } else {
+ onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime());
+ }
+ }
+ return;
+ }
+ case SuspenseComponent:
+ break;
+ case IncompleteClassComponent:
+ break;
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function hideOrUnhideAllChildren(finishedWork, isHidden) {
+ if (supportsMutation) {
+ // We only have the top Fiber that was inserted but we need to recurse down its
+ var node = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (isHidden) {
+ hideInstance(instance);
+ } else {
+ unhideInstance(node.stateNode, node.memoizedProps);
+ }
+ } else if (node.tag === HostText) {
+ var _instance3 = node.stateNode;
+ if (isHidden) {
+ hideTextInstance(_instance3);
+ } else {
+ unhideTextInstance(_instance3, node.memoizedProps);
+ }
+ } else if (node.tag === SuspenseComponent && node.memoizedState !== null) {
+ // Found a nested Suspense component that timed out. Skip over the
+ var fallbackChildFragment = node.child.sibling;
+ fallbackChildFragment.return = node;
+ node = fallbackChildFragment;
+ continue;
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ }
+}
+
+function commitAttachRef(finishedWork) {
+ var ref = finishedWork.ref;
+ if (ref !== null) {
+ var instance = finishedWork.stateNode;
+ var instanceToUse = void 0;
+ switch (finishedWork.tag) {
+ case HostComponent:
+ instanceToUse = getPublicInstance(instance);
+ break;
+ default:
+ instanceToUse = instance;
+ }
+ if (typeof ref === 'function') {
+ ref(instanceToUse);
+ } else {
+ ref.current = instanceToUse;
+ }
+ }
+}
+
+function commitDetachRef(current$$1) {
+ var currentRef = current$$1.ref;
+ if (currentRef !== null) {
+ if (typeof currentRef === 'function') {
+ currentRef(null);
+ } else {
+ currentRef.current = null;
+ }
+ }
+}
+
+// User-originating errors (lifecycles and refs) should not interrupt
+// deletion, so don't let them throw. Host-originating errors should
+// interrupt deletion, so it's okay
+function commitUnmount(current$$1) {
+ onCommitUnmount(current$$1);
+
+ switch (current$$1.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ var updateQueue = current$$1.updateQueue;
+ if (updateQueue !== null) {
+ var lastEffect = updateQueue.lastEffect;
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
+ do {
+ var destroy = effect.destroy;
+ if (destroy !== undefined) {
+ safelyCallDestroy(current$$1, destroy);
+ }
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+ }
+ break;
+ }
+ case ClassComponent:
+ {
+ safelyDetachRef(current$$1);
+ var instance = current$$1.stateNode;
+ if (typeof instance.componentWillUnmount === 'function') {
+ safelyCallComponentWillUnmount(current$$1, instance);
+ }
+ return;
+ }
+ case HostComponent:
+ {
+ safelyDetachRef(current$$1);
+ return;
+ }
+ case HostPortal:
+ {
+ // TODO: this is recursive.
+ // We are also not using this parent because
+ // the portal will get pushed immediately.
+ if (supportsMutation) {
+ unmountHostComponents(current$$1);
+ } else if (supportsPersistence) {
+ emptyPortalContainer(current$$1);
+ }
+ return;
+ }
+ }
+}
+
+function commitNestedUnmounts(root) {
+ // While we're inside a removed host node we don't want to call
+ // removeChild on the inner nodes because they're removed by the top
+ // call anyway. We also want to call componentWillUnmount on all
+ // composites before this host node is removed from the tree. Therefore
+ var node = root;
+ while (true) {
+ commitUnmount(node);
+ // Visit children because they may contain more composite or host nodes.
+ // Skip portals because commitUnmount() currently visits them recursively.
+ if (node.child !== null && (
+ // If we use mutation we drill down into portals using commitUnmount above.
+ // If we don't use mutation we drill down into portals here instead.
+ !supportsMutation || node.tag !== HostPortal)) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === root) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === root) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function detachFiber(current$$1) {
+ // Cut off the return pointers to disconnect it from the tree. Ideally, we
+ // should clear the child pointer of the parent alternate to let this
+ // get GC:ed but we don't know which for sure which parent is the current
+ // one so we'll settle for GC:ing the subtree of this child. This child
+ // itself will be GC:ed when the parent updates the next time.
+ current$$1.return = null;
+ current$$1.child = null;
+ current$$1.memoizedState = null;
+ current$$1.updateQueue = null;
+ var alternate = current$$1.alternate;
+ if (alternate !== null) {
+ alternate.return = null;
+ alternate.child = null;
+ alternate.memoizedState = null;
+ alternate.updateQueue = null;
+ }
+}
+
+function emptyPortalContainer(current$$1) {
+ if (!supportsPersistence) {
+ return;
+ }
+
+ var portal = current$$1.stateNode;
+ var containerInfo = portal.containerInfo;
+
+ var emptyChildSet = createContainerChildSet(containerInfo);
+ replaceContainerChildren(containerInfo, emptyChildSet);
+}
+
+function commitContainer(finishedWork) {
+ if (!supportsPersistence) {
+ return;
+ }
+
+ switch (finishedWork.tag) {
+ case ClassComponent:
+ {
+ return;
+ }
+ case HostComponent:
+ {
+ return;
+ }
+ case HostText:
+ {
+ return;
+ }
+ case HostRoot:
+ case HostPortal:
+ {
+ var portalOrRoot = finishedWork.stateNode;
+ var containerInfo = portalOrRoot.containerInfo,
+ _pendingChildren = portalOrRoot.pendingChildren;
+
+ replaceContainerChildren(containerInfo, _pendingChildren);
+ return;
+ }
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function getHostParentFiber(fiber) {
+ var parent = fiber.return;
+ while (parent !== null) {
+ if (isHostParent(parent)) {
+ return parent;
+ }
+ parent = parent.return;
+ }
+ reactProdInvariant('160');
+}
+
+function isHostParent(fiber) {
+ return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal;
+}
+
+function getHostSibling(fiber) {
+ // We're going to search forward into the tree until we find a sibling host
+ // node. Unfortunately, if multiple insertions are done in a row we have to
+ // search past them. This leads to exponential search for the next sibling.
+ var node = fiber;
+ siblings: while (true) {
+ // If we didn't find anything, let's try the next sibling.
+ while (node.sibling === null) {
+ if (node.return === null || isHostParent(node.return)) {
+ // If we pop out of the root or hit the parent the fiber we are the
+ // last sibling.
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ while (node.tag !== HostComponent && node.tag !== HostText && node.tag !== DehydratedSuspenseComponent) {
+ // If it is not host node and, we might have a host node inside it.
+ // Try to search down until we find one.
+ if (node.effectTag & Placement) {
+ // If we don't have a child, try the siblings instead.
+ continue siblings;
+ }
+ // If we don't have a child, try the siblings instead.
+ // We also skip portals because they are not part of this host tree.
+ if (node.child === null || node.tag === HostPortal) {
+ continue siblings;
+ } else {
+ node.child.return = node;
+ node = node.child;
+ }
+ }
+ // Check if this host node is stable or about to be placed.
+ if (!(node.effectTag & Placement)) {
+ // Found it!
+ return node.stateNode;
+ }
+ }
+}
+
+function commitPlacement(finishedWork) {
+ if (!supportsMutation) {
+ return;
+ }
+
+ // Recursively insert all host nodes into the parent.
+ var parentFiber = getHostParentFiber(finishedWork);
+
+ // Note: these two variables *must* always be updated together.
+ var parent = void 0;
+ var isContainer = void 0;
+
+ switch (parentFiber.tag) {
+ case HostComponent:
+ parent = parentFiber.stateNode;
+ isContainer = false;
+ break;
+ case HostRoot:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ case HostPortal:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ default:
+ reactProdInvariant('161');
+ }
+ if (parentFiber.effectTag & ContentReset) {
+ // Reset the text content of the parent before doing any insertions
+ resetTextContent(parent);
+ // Clear ContentReset from the effect tag
+ parentFiber.effectTag &= ~ContentReset;
+ }
+
+ var before = getHostSibling(finishedWork);
+ // We only have the top Fiber that was inserted but we need to recurse down its
+ // children to find all the terminal nodes.
+ var node = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ if (before) {
+ if (isContainer) {
+ insertInContainerBefore(parent, node.stateNode, before);
+ } else {
+ insertBefore(parent, node.stateNode, before);
+ }
+ } else {
+ if (isContainer) {
+ appendChildToContainer(parent, node.stateNode);
+ } else {
+ appendChild(parent, node.stateNode);
+ }
+ }
+ } else if (node.tag === HostPortal) {
+ // If the insertion itself is a portal, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function unmountHostComponents(current$$1) {
+ // We only have the top Fiber that was deleted but we need to recurse down its
+ var node = current$$1;
+
+ // Each iteration, currentParent is populated with node's host parent if not
+ // currentParentIsValid.
+ var currentParentIsValid = false;
+
+ // Note: these two variables *must* always be updated together.
+ var currentParent = void 0;
+ var currentParentIsContainer = void 0;
+
+ while (true) {
+ if (!currentParentIsValid) {
+ var parent = node.return;
+ findParent: while (true) {
+ !(parent !== null) ? reactProdInvariant('160') : void 0;
+ switch (parent.tag) {
+ case HostComponent:
+ currentParent = parent.stateNode;
+ currentParentIsContainer = false;
+ break findParent;
+ case HostRoot:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ case HostPortal:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ }
+ parent = parent.return;
+ }
+ currentParentIsValid = true;
+ }
+
+ if (node.tag === HostComponent || node.tag === HostText) {
+ commitNestedUnmounts(node);
+ // After all the children have unmounted, it is now safe to remove the
+ // node from the tree.
+ if (currentParentIsContainer) {
+ removeChildFromContainer(currentParent, node.stateNode);
+ } else {
+ removeChild(currentParent, node.stateNode);
+ }
+ // Don't visit children because we already visited them.
+ } else if (enableSuspenseServerRenderer && node.tag === DehydratedSuspenseComponent) {
+ // Delete the dehydrated suspense boundary and all of its content.
+ if (currentParentIsContainer) {
+ clearSuspenseBoundaryFromContainer(currentParent, node.stateNode);
+ } else {
+ clearSuspenseBoundary(currentParent, node.stateNode);
+ }
+ } else if (node.tag === HostPortal) {
+ if (node.child !== null) {
+ // When we go into a portal, it becomes the parent to remove from.
+ // We will reassign it back when we pop the portal on the way up.
+ currentParent = node.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ // Visit children because portals might contain host components.
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else {
+ commitUnmount(node);
+ // Visit children because we may find more host components below.
+ if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ }
+ if (node === current$$1) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === current$$1) {
+ return;
+ }
+ node = node.return;
+ if (node.tag === HostPortal) {
+ // When we go out of the portal, we need to restore the parent.
+ // Since we don't keep a stack of them, we will search for it.
+ currentParentIsValid = false;
+ }
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function commitDeletion(current$$1) {
+ if (supportsMutation) {
+ // Recursively delete all host nodes from the parent.
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ unmountHostComponents(current$$1);
+ } else {
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ commitNestedUnmounts(current$$1);
+ }
+ detachFiber(current$$1);
+}
+
+function commitWork(current$$1, finishedWork) {
+ if (!supportsMutation) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ // Note: We currently never use MountMutation, but useLayout uses
+ // UnmountMutation.
+ commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
+ return;
+ }
+ }
+
+ commitContainer(finishedWork);
+ return;
+ }
+
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ // Note: We currently never use MountMutation, but useLayout uses
+ // UnmountMutation.
+ commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
+ return;
+ }
+ case ClassComponent:
+ {
+ return;
+ }
+ case HostComponent:
+ {
+ var instance = finishedWork.stateNode;
+ if (instance != null) {
+ // Commit the work prepared earlier.
+ var newProps = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldProps = current$$1 !== null ? current$$1.memoizedProps : newProps;
+ var type = finishedWork.type;
+ // TODO: Type the updateQueue to be specific to host components.
+ var updatePayload = finishedWork.updateQueue;
+ finishedWork.updateQueue = null;
+ if (updatePayload !== null) {
+ commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork);
+ }
+ }
+ return;
+ }
+ case HostText:
+ {
+ !(finishedWork.stateNode !== null) ? reactProdInvariant('162') : void 0;
+ var textInstance = finishedWork.stateNode;
+ var newText = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldText = current$$1 !== null ? current$$1.memoizedProps : newText;
+ commitTextUpdate(textInstance, oldText, newText);
+ return;
+ }
+ case HostRoot:
+ {
+ return;
+ }
+ case Profiler:
+ {
+ return;
+ }
+ case SuspenseComponent:
+ {
+ var newState = finishedWork.memoizedState;
+
+ var newDidTimeout = void 0;
+ var primaryChildParent = finishedWork;
+ if (newState === null) {
+ newDidTimeout = false;
+ } else {
+ newDidTimeout = true;
+ primaryChildParent = finishedWork.child;
+ if (newState.timedOutAt === NoWork) {
+ // If the children had not already timed out, record the time.
+ // This is used to compute the elapsed time during subsequent
+ // attempts to render the children.
+ newState.timedOutAt = requestCurrentTime();
+ }
+ }
+
+ if (primaryChildParent !== null) {
+ hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
+ }
+
+ // If this boundary just timed out, then it will have a set of thenables.
+ // For each thenable, attach a listener so that when it resolves, React
+ // attempts to re-render the boundary in the primary (pre-timeout) state.
+ var thenables = finishedWork.updateQueue;
+ if (thenables !== null) {
+ finishedWork.updateQueue = null;
+ var retryCache = finishedWork.stateNode;
+ if (retryCache === null) {
+ retryCache = finishedWork.stateNode = new PossiblyWeakSet$1();
+ }
+ thenables.forEach(function (thenable) {
+ // Memoize using the boundary fiber to prevent redundant listeners.
+ var retry = retryTimedOutBoundary.bind(null, finishedWork, thenable);
+ if (enableSchedulerTracing) {
+ retry = unstable_wrap(retry);
+ }
+ if (!retryCache.has(thenable)) {
+ retryCache.add(thenable);
+ thenable.then(retry, retry);
+ }
+ });
+ }
+
+ return;
+ }
+ case IncompleteClassComponent:
+ {
+ return;
+ }
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function commitResetTextContent(current$$1) {
+ if (!supportsMutation) {
+ return;
+ }
+ resetTextContent(current$$1.stateNode);
+}
+
+var PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
+var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
+
+function createRootErrorUpdate(fiber, errorInfo, expirationTime) {
+ var update = createUpdate(expirationTime);
+ // Unmount the root by rendering null.
+ update.tag = CaptureUpdate;
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ update.payload = { element: null };
+ var error = errorInfo.value;
+ update.callback = function () {
+ onUncaughtError(error);
+ logError(fiber, errorInfo);
+ };
+ return update;
+}
+
+function createClassErrorUpdate(fiber, errorInfo, expirationTime) {
+ var update = createUpdate(expirationTime);
+ update.tag = CaptureUpdate;
+ var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
+ if (typeof getDerivedStateFromError === 'function') {
+ var error = errorInfo.value;
+ update.payload = function () {
+ return getDerivedStateFromError(error);
+ };
+ }
+
+ var inst = fiber.stateNode;
+ if (inst !== null && typeof inst.componentDidCatch === 'function') {
+ update.callback = function callback() {
+ if (typeof getDerivedStateFromError !== 'function') {
+ // To preserve the preexisting retry behavior of error boundaries,
+ // we keep track of which ones already failed during this batch.
+ // This gets reset before we yield back to the browser.
+ // TODO: Warn in strict mode if getDerivedStateFromError is
+ // not defined.
+ markLegacyErrorBoundaryAsFailed(this);
+ }
+ var error = errorInfo.value;
+ var stack = errorInfo.stack;
+ logError(fiber, errorInfo);
+ this.componentDidCatch(error, {
+ componentStack: stack !== null ? stack : ''
+ });
+
+ };
+ }
+ return update;
+}
+
+function attachPingListener(root, renderExpirationTime, thenable) {
+ // Attach a listener to the promise to "ping" the root and retry. But
+ // only if one does not already exist for the current render expiration
+ // time (which acts like a "thread ID" here).
+ var pingCache = root.pingCache;
+ var threadIDs = void 0;
+ if (pingCache === null) {
+ pingCache = root.pingCache = new PossiblyWeakMap();
+ threadIDs = new Set();
+ pingCache.set(thenable, threadIDs);
+ } else {
+ threadIDs = pingCache.get(thenable);
+ if (threadIDs === undefined) {
+ threadIDs = new Set();
+ pingCache.set(thenable, threadIDs);
+ }
+ }
+ if (!threadIDs.has(renderExpirationTime)) {
+ // Memoize using the thread ID to prevent redundant listeners.
+ threadIDs.add(renderExpirationTime);
+ var ping = pingSuspendedRoot.bind(null, root, thenable, renderExpirationTime);
+ if (enableSchedulerTracing) {
+ ping = unstable_wrap(ping);
+ }
+ thenable.then(ping, ping);
+ }
+}
+
+function throwException(root, returnFiber, sourceFiber, value, renderExpirationTime) {
+ // The source fiber did not complete.
+ sourceFiber.effectTag |= Incomplete;
+ // Its effect list is no longer valid.
+ sourceFiber.firstEffect = sourceFiber.lastEffect = null;
+
+ if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
+ // This is a thenable.
+ var thenable = value;
+
+ // Find the earliest timeout threshold of all the placeholders in the
+ // ancestor path. We could avoid this traversal by storing the thresholds on
+ // the stack, but we choose not to because we only hit this path if we're
+ // IO-bound (i.e. if something suspends). Whereas the stack is used even in
+ // the non-IO- bound case.
+ var _workInProgress = returnFiber;
+ var earliestTimeoutMs = -1;
+ var startTimeMs = -1;
+ do {
+ if (_workInProgress.tag === SuspenseComponent) {
+ var current$$1 = _workInProgress.alternate;
+ if (current$$1 !== null) {
+ var currentState = current$$1.memoizedState;
+ if (currentState !== null) {
+ // Reached a boundary that already timed out. Do not search
+ // any further.
+ var timedOutAt = currentState.timedOutAt;
+ startTimeMs = expirationTimeToMs(timedOutAt);
+ // Do not search any further.
+ break;
+ }
+ }
+ var timeoutPropMs = _workInProgress.pendingProps.maxDuration;
+ if (typeof timeoutPropMs === 'number') {
+ if (timeoutPropMs <= 0) {
+ earliestTimeoutMs = 0;
+ } else if (earliestTimeoutMs === -1 || timeoutPropMs < earliestTimeoutMs) {
+ earliestTimeoutMs = timeoutPropMs;
+ }
+ }
+ }
+ // If there is a DehydratedSuspenseComponent we don't have to do anything because
+ // if something suspends inside it, we will simply leave that as dehydrated. It
+ // will never timeout.
+ _workInProgress = _workInProgress.return;
+ } while (_workInProgress !== null);
+
+ // Schedule the nearest Suspense to re-render the timed out view.
+ _workInProgress = returnFiber;
+ do {
+ if (_workInProgress.tag === SuspenseComponent && shouldCaptureSuspense(_workInProgress)) {
+ // Found the nearest boundary.
+
+ // Stash the promise on the boundary fiber. If the boundary times out, we'll
+ var thenables = _workInProgress.updateQueue;
+ if (thenables === null) {
+ var updateQueue = new Set();
+ updateQueue.add(thenable);
+ _workInProgress.updateQueue = updateQueue;
+ } else {
+ thenables.add(thenable);
+ }
+
+ // If the boundary is outside of concurrent mode, we should *not*
+ // suspend the commit. Pretend as if the suspended component rendered
+ // null and keep rendering. In the commit phase, we'll schedule a
+ // subsequent synchronous update to re-render the Suspense.
+ //
+ // Note: It doesn't matter whether the component that suspended was
+ // inside a concurrent mode tree. If the Suspense is outside of it, we
+ // should *not* suspend the commit.
+ if ((_workInProgress.mode & ConcurrentMode) === NoEffect) {
+ _workInProgress.effectTag |= DidCapture;
+
+ // We're going to commit this fiber even though it didn't complete.
+ // But we shouldn't call any lifecycle methods or callbacks. Remove
+ // all lifecycle effect tags.
+ sourceFiber.effectTag &= ~(LifecycleEffectMask | Incomplete);
+
+ if (sourceFiber.tag === ClassComponent) {
+ var currentSourceFiber = sourceFiber.alternate;
+ if (currentSourceFiber === null) {
+ // This is a new mount. Change the tag so it's not mistaken for a
+ // completed class component. For example, we should not call
+ // componentWillUnmount if it is deleted.
+ sourceFiber.tag = IncompleteClassComponent;
+ } else {
+ // When we try rendering again, we should not reuse the current fiber,
+ // since it's known to be in an inconsistent state. Use a force updte to
+ // prevent a bail out.
+ var update = createUpdate(Sync);
+ update.tag = ForceUpdate;
+ enqueueUpdate(sourceFiber, update);
+ }
+ }
+
+ // The source fiber did not complete. Mark it with Sync priority to
+ // indicate that it still has pending work.
+ sourceFiber.expirationTime = Sync;
+
+ // Exit without suspending.
+ return;
+ }
+
+ // Confirmed that the boundary is in a concurrent mode tree. Continue
+ // with the normal suspend path.
+
+ attachPingListener(root, renderExpirationTime, thenable);
+
+ var absoluteTimeoutMs = void 0;
+ if (earliestTimeoutMs === -1) {
+ // If no explicit threshold is given, default to an arbitrarily large
+ // value. The actual size doesn't matter because the threshold for the
+ // whole tree will be clamped to the expiration time.
+ absoluteTimeoutMs = maxSigned31BitInt;
+ } else {
+ if (startTimeMs === -1) {
+ // This suspend happened outside of any already timed-out
+ // placeholders. We don't know exactly when the update was
+ // scheduled, but we can infer an approximate start time from the
+ // expiration time. First, find the earliest uncommitted expiration
+ // time in the tree, including work that is suspended. Then subtract
+ // the offset used to compute an async update's expiration time.
+ // This will cause high priority (interactive) work to expire
+ // earlier than necessary, but we can account for this by adjusting
+ // for the Just Noticeable Difference.
+ var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, renderExpirationTime);
+ var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
+ startTimeMs = earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;
+ }
+ absoluteTimeoutMs = startTimeMs + earliestTimeoutMs;
+ }
+
+ // Mark the earliest timeout in the suspended fiber's ancestor path.
+ // After completing the root, we'll take the largest of all the
+ // suspended fiber's timeouts and use it to compute a timeout for the
+ // whole tree.
+ renderDidSuspend(root, absoluteTimeoutMs, renderExpirationTime);
+
+ _workInProgress.effectTag |= ShouldCapture;
+ _workInProgress.expirationTime = renderExpirationTime;
+ return;
+ } else if (enableSuspenseServerRenderer && _workInProgress.tag === DehydratedSuspenseComponent) {
+ attachPingListener(root, renderExpirationTime, thenable);
+
+ // Since we already have a current fiber, we can eagerly add a retry listener.
+ var retryCache = _workInProgress.memoizedState;
+ if (retryCache === null) {
+ retryCache = _workInProgress.memoizedState = new PossiblyWeakSet();
+ var _current = _workInProgress.alternate;
+ !_current ? reactProdInvariant('319') : void 0;
+ _current.memoizedState = retryCache;
+ }
+ // Memoize using the boundary fiber to prevent redundant listeners.
+ if (!retryCache.has(thenable)) {
+ retryCache.add(thenable);
+ var retry = retryTimedOutBoundary.bind(null, _workInProgress, thenable);
+ if (enableSchedulerTracing) {
+ retry = unstable_wrap(retry);
+ }
+ thenable.then(retry, retry);
+ }
+ _workInProgress.effectTag |= ShouldCapture;
+ _workInProgress.expirationTime = renderExpirationTime;
+ return;
+ }
+ // This boundary already captured during this render. Continue to the next
+ // boundary.
+ _workInProgress = _workInProgress.return;
+ } while (_workInProgress !== null);
+ // No boundary was found. Fallthrough to error mode.
+ // TODO: Use invariant so the message is stripped in prod?
+ value = new Error((getComponentName(sourceFiber.type) || 'A React component') + ' suspended while rendering, but no fallback UI was specified.\n' + '\n' + 'Add a <Suspense fallback=...> component higher in the tree to ' + 'provide a loading indicator or placeholder to display.' + getStackByFiberInDevAndProd(sourceFiber));
+ }
+
+ // We didn't find a boundary that could handle this type of exception. Start
+ // over and traverse parent path again, this time treating the exception
+ // as an error.
+ renderDidError();
+ value = createCapturedValue(value, sourceFiber);
+ var workInProgress = returnFiber;
+ do {
+ switch (workInProgress.tag) {
+ case HostRoot:
+ {
+ var _errorInfo = value;
+ workInProgress.effectTag |= ShouldCapture;
+ workInProgress.expirationTime = renderExpirationTime;
+ var _update = createRootErrorUpdate(workInProgress, _errorInfo, renderExpirationTime);
+ enqueueCapturedUpdate(workInProgress, _update);
+ return;
+ }
+ case ClassComponent:
+ // Capture and retry
+ var errorInfo = value;
+ var ctor = workInProgress.type;
+ var instance = workInProgress.stateNode;
+ if ((workInProgress.effectTag & DidCapture) === NoEffect && (typeof ctor.getDerivedStateFromError === 'function' || instance !== null && typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance))) {
+ workInProgress.effectTag |= ShouldCapture;
+ workInProgress.expirationTime = renderExpirationTime;
+ // Schedule the error boundary to re-render using updated state
+ var _update2 = createClassErrorUpdate(workInProgress, errorInfo, renderExpirationTime);
+ enqueueCapturedUpdate(workInProgress, _update2);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ workInProgress = workInProgress.return;
+ } while (workInProgress !== null);
+}
+
+function unwindWork(workInProgress, renderExpirationTime) {
+ switch (workInProgress.tag) {
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
+ }
+ var effectTag = workInProgress.effectTag;
+ if (effectTag & ShouldCapture) {
+ workInProgress.effectTag = effectTag & ~ShouldCapture | DidCapture;
+ return workInProgress;
+ }
+ return null;
+ }
+ case HostRoot:
+ {
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ var _effectTag = workInProgress.effectTag;
+ !((_effectTag & DidCapture) === NoEffect) ? reactProdInvariant('285') : void 0;
+ workInProgress.effectTag = _effectTag & ~ShouldCapture | DidCapture;
+ return workInProgress;
+ }
+ case HostComponent:
+ {
+ // TODO: popHydrationState
+ popHostContext(workInProgress);
+ return null;
+ }
+ case SuspenseComponent:
+ {
+ var _effectTag2 = workInProgress.effectTag;
+ if (_effectTag2 & ShouldCapture) {
+ workInProgress.effectTag = _effectTag2 & ~ShouldCapture | DidCapture;
+ // Captured a suspense effect. Re-render the boundary.
+ return workInProgress;
+ }
+ return null;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ // TODO: popHydrationState
+ var _effectTag3 = workInProgress.effectTag;
+ if (_effectTag3 & ShouldCapture) {
+ workInProgress.effectTag = _effectTag3 & ~ShouldCapture | DidCapture;
+ // Captured a suspense effect. Re-render the boundary.
+ return workInProgress;
+ }
+ }
+ return null;
+ }
+ case HostPortal:
+ popHostContainer(workInProgress);
+ return null;
+ case ContextProvider:
+ popProvider(workInProgress);
+ return null;
+ default:
+ return null;
+ }
+}
+
+function unwindInterruptedWork(interruptedWork) {
+ switch (interruptedWork.tag) {
+ case ClassComponent:
+ {
+ var childContextTypes = interruptedWork.type.childContextTypes;
+ if (childContextTypes !== null && childContextTypes !== undefined) {
+ popContext(interruptedWork);
+ }
+ break;
+ }
+ case HostRoot:
+ {
+ popHostContainer(interruptedWork);
+ popTopLevelContextObject(interruptedWork);
+ break;
+ }
+ case HostComponent:
+ {
+ popHostContext(interruptedWork);
+ break;
+ }
+ case HostPortal:
+ popHostContainer(interruptedWork);
+ break;
+ case ContextProvider:
+ popProvider(interruptedWork);
+ break;
+ default:
+ break;
+ }
+}
+
+var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner;
+
+
+if (enableSchedulerTracing) {
+ // Provide explicit error message when production+profiling bundle of e.g. react-dom
+ // is used with production (non-profiling) bundle of scheduler/tracing
+ !(__interactionsRef != null && __interactionsRef.current != null) ? reactProdInvariant('302') : void 0;
+}
+
+// Used to ensure computeUniqueAsyncExpiration is monotonically decreasing.
+var lastUniqueAsyncExpiration = Sync - 1;
+
+var isWorking = false;
+
+// The next work in progress fiber that we're currently working on.
+var nextUnitOfWork = null;
+var nextRoot = null;
+// The time at which we're currently rendering work.
+var nextRenderExpirationTime = NoWork;
+var nextLatestAbsoluteTimeoutMs = -1;
+var nextRenderDidError = false;
+
+// The next fiber with an effect that we're currently committing.
+var nextEffect = null;
+
+var isCommitting$1 = false;
+var rootWithPendingPassiveEffects = null;
+var passiveEffectCallbackHandle = null;
+var passiveEffectCallback = null;
+
+var legacyErrorBoundariesThatAlreadyFailed = null;
+
+// Used for performance tracking.
+var interruptedBy = null;
+
+function resetStack() {
+ if (nextUnitOfWork !== null) {
+ var interruptedWork = nextUnitOfWork.return;
+ while (interruptedWork !== null) {
+ unwindInterruptedWork(interruptedWork);
+ interruptedWork = interruptedWork.return;
+ }
+ }
+
+ nextRoot = null;
+ nextRenderExpirationTime = NoWork;
+ nextLatestAbsoluteTimeoutMs = -1;
+ nextRenderDidError = false;
+ nextUnitOfWork = null;
+}
+
+function commitAllHostEffects() {
+ while (nextEffect !== null) {
+ recordEffect();
+
+ var effectTag = nextEffect.effectTag;
+
+ if (effectTag & ContentReset) {
+ commitResetTextContent(nextEffect);
+ }
+
+ if (effectTag & Ref) {
+ var current$$1 = nextEffect.alternate;
+ if (current$$1 !== null) {
+ commitDetachRef(current$$1);
+ }
+ }
+
+ // The following switch statement is only concerned about placement,
+ // updates, and deletions. To avoid needing to add a case for every
+ // possible bitmap value, we remove the secondary effects from the
+ // effect tag and switch on that value.
+ var primaryEffectTag = effectTag & (Placement | Update | Deletion);
+ switch (primaryEffectTag) {
+ case Placement:
+ {
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ // TODO: findDOMNode doesn't rely on this any more but isMounted
+ // does and isMounted is deprecated anyway so we should be able
+ // to kill this.
+ nextEffect.effectTag &= ~Placement;
+ break;
+ }
+ case PlacementAndUpdate:
+ {
+ // Placement
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ nextEffect.effectTag &= ~Placement;
+
+ // Update
+ var _current = nextEffect.alternate;
+ commitWork(_current, nextEffect);
+ break;
+ }
+ case Update:
+ {
+ var _current2 = nextEffect.alternate;
+ commitWork(_current2, nextEffect);
+ break;
+ }
+ case Deletion:
+ {
+ commitDeletion(nextEffect);
+ break;
+ }
+ }
+ nextEffect = nextEffect.nextEffect;
+ }
+
+
+}
+
+function commitBeforeMutationLifecycles() {
+ while (nextEffect !== null) {
+ var effectTag = nextEffect.effectTag;
+ if (effectTag & Snapshot) {
+ recordEffect();
+ var current$$1 = nextEffect.alternate;
+ commitBeforeMutationLifeCycles(current$$1, nextEffect);
+ }
+
+ nextEffect = nextEffect.nextEffect;
+ }
+
+
+}
+
+function commitAllLifeCycles(finishedRoot, committedExpirationTime) {
+ while (nextEffect !== null) {
+ var effectTag = nextEffect.effectTag;
+
+ if (effectTag & (Update | Callback)) {
+ recordEffect();
+ var current$$1 = nextEffect.alternate;
+ commitLifeCycles(finishedRoot, current$$1, nextEffect, committedExpirationTime);
+ }
+
+ if (effectTag & Ref) {
+ recordEffect();
+ commitAttachRef(nextEffect);
+ }
+
+ if (effectTag & Passive) {
+ rootWithPendingPassiveEffects = finishedRoot;
+ }
+
+ nextEffect = nextEffect.nextEffect;
+ }
+
+}
+
+function commitPassiveEffects(root, firstEffect) {
+ rootWithPendingPassiveEffects = null;
+ passiveEffectCallbackHandle = null;
+ passiveEffectCallback = null;
+
+ // Set this to true to prevent re-entrancy
+ var previousIsRendering = isRendering;
+ isRendering = true;
+
+ var effect = firstEffect;
+ do {
+ if (effect.effectTag & Passive) {
+ var didError = false;
+ var error = void 0;
+ {
+ try {
+ commitPassiveHookEffects(effect);
+ } catch (e) {
+ didError = true;
+ error = e;
+ }
+ }
+ if (didError) {
+ captureCommitPhaseError(effect, error);
+ }
+ }
+ effect = effect.nextEffect;
+ } while (effect !== null);
+ isRendering = previousIsRendering;
+
+ // Check if work was scheduled by one of the effects
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ // Flush any sync work that was scheduled by effects
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+}
+
+function isAlreadyFailedLegacyErrorBoundary(instance) {
+ return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);
+}
+
+function markLegacyErrorBoundaryAsFailed(instance) {
+ if (legacyErrorBoundariesThatAlreadyFailed === null) {
+ legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
+ } else {
+ legacyErrorBoundariesThatAlreadyFailed.add(instance);
+ }
+}
+
+function flushPassiveEffects() {
+ if (passiveEffectCallbackHandle !== null) {
+ cancelPassiveEffects(passiveEffectCallbackHandle);
+ }
+ if (passiveEffectCallback !== null) {
+ // We call the scheduled callback instead of commitPassiveEffects directly
+ // to ensure tracing works correctly.
+ passiveEffectCallback();
+ }
+}
+
+function commitRoot(root, finishedWork) {
+ isWorking = true;
+ isCommitting$1 = true;
+ startCommitTimer();
+
+ !(root.current !== finishedWork) ? reactProdInvariant('177') : void 0;
+ var committedExpirationTime = root.pendingCommitExpirationTime;
+ !(committedExpirationTime !== NoWork) ? reactProdInvariant('261') : void 0;
+ root.pendingCommitExpirationTime = NoWork;
+
+ // Update the pending priority levels to account for the work that we are
+ // about to commit. This needs to happen before calling the lifecycles, since
+ // they may schedule additional updates.
+ var updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
+ var childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
+ var earliestRemainingTimeBeforeCommit = childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit ? childExpirationTimeBeforeCommit : updateExpirationTimeBeforeCommit;
+ markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
+
+ var prevInteractions = null;
+ if (enableSchedulerTracing) {
+ // Restore any pending interactions at this point,
+ // So that cascading work triggered during the render phase will be accounted for.
+ prevInteractions = __interactionsRef.current;
+ __interactionsRef.current = root.memoizedInteractions;
+ }
+
+ // Reset this to null before calling lifecycles
+ ReactCurrentOwner$2.current = null;
+
+ var firstEffect = void 0;
+ if (finishedWork.effectTag > PerformedWork) {
+ // A fiber's effect list consists only of its children, not itself. So if
+ // the root has an effect, we need to add it to the end of the list. The
+ // resulting list is the set that would belong to the root's parent, if
+ // it had one; that is, all the effects in the tree including the root.
+ if (finishedWork.lastEffect !== null) {
+ finishedWork.lastEffect.nextEffect = finishedWork;
+ firstEffect = finishedWork.firstEffect;
+ } else {
+ firstEffect = finishedWork;
+ }
+ } else {
+ // There is no effect on the root.
+ firstEffect = finishedWork.firstEffect;
+ }
+
+ prepareForCommit(root.containerInfo);
+
+ // Invoke instances of getSnapshotBeforeUpdate before mutation.
+ nextEffect = firstEffect;
+ startCommitSnapshotEffectsTimer();
+ while (nextEffect !== null) {
+ var didError = false;
+ var error = void 0;
+ {
+ try {
+ commitBeforeMutationLifecycles();
+ } catch (e) {
+ didError = true;
+ error = e;
+ }
+ }
+ if (didError) {
+ !(nextEffect !== null) ? reactProdInvariant('178') : void 0;
+ captureCommitPhaseError(nextEffect, error);
+ // Clean-up
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+ stopCommitSnapshotEffectsTimer();
+
+ if (enableProfilerTimer) {
+ // Mark the current commit time to be shared by all Profilers in this batch.
+ // This enables them to be grouped later.
+ recordCommitTime();
+ }
+
+ // Commit all the side-effects within a tree. We'll do this in two passes.
+ // The first pass performs all the host insertions, updates, deletions and
+ // ref unmounts.
+ nextEffect = firstEffect;
+ startCommitHostEffectsTimer();
+ while (nextEffect !== null) {
+ var _didError = false;
+ var _error = void 0;
+ {
+ try {
+ commitAllHostEffects();
+ } catch (e) {
+ _didError = true;
+ _error = e;
+ }
+ }
+ if (_didError) {
+ !(nextEffect !== null) ? reactProdInvariant('178') : void 0;
+ captureCommitPhaseError(nextEffect, _error);
+ // Clean-up
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+ stopCommitHostEffectsTimer();
+
+ resetAfterCommit(root.containerInfo);
+
+ // The work-in-progress tree is now the current tree. This must come after
+ // the first pass of the commit phase, so that the previous tree is still
+ // current during componentWillUnmount, but before the second pass, so that
+ // the finished work is current during componentDidMount/Update.
+ root.current = finishedWork;
+
+ // In the second pass we'll perform all life-cycles and ref callbacks.
+ // Life-cycles happen as a separate pass so that all placements, updates,
+ // and deletions in the entire tree have already been invoked.
+ // This pass also triggers any renderer-specific initial effects.
+ nextEffect = firstEffect;
+ startCommitLifeCyclesTimer();
+ while (nextEffect !== null) {
+ var _didError2 = false;
+ var _error2 = void 0;
+ {
+ try {
+ commitAllLifeCycles(root, committedExpirationTime);
+ } catch (e) {
+ _didError2 = true;
+ _error2 = e;
+ }
+ }
+ if (_didError2) {
+ !(nextEffect !== null) ? reactProdInvariant('178') : void 0;
+ captureCommitPhaseError(nextEffect, _error2);
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+
+ if (firstEffect !== null && rootWithPendingPassiveEffects !== null) {
+ // This commit included a passive effect. These do not need to fire until
+ // after the next paint. Schedule an callback to fire them in an async
+ // event. To ensure serial execution, the callback will be flushed early if
+ // we enter rootWithPendingPassiveEffects commit phase before then.
+ var callback = commitPassiveEffects.bind(null, root, firstEffect);
+ if (enableSchedulerTracing) {
+ // TODO: Avoid this extra callback by mutating the tracing ref directly,
+ // like we do at the beginning of commitRoot. I've opted not to do that
+ // here because that code is still in flux.
+ callback = unstable_wrap(callback);
+ }
+ passiveEffectCallbackHandle = unstable_runWithPriority(unstable_NormalPriority, function () {
+ return schedulePassiveEffects(callback);
+ });
+ passiveEffectCallback = callback;
+ }
+
+ isCommitting$1 = false;
+ isWorking = false;
+ stopCommitLifeCyclesTimer();
+ stopCommitTimer();
+ onCommitRoot(finishedWork.stateNode);
+ var updateExpirationTimeAfterCommit = finishedWork.expirationTime;
+ var childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
+ var earliestRemainingTimeAfterCommit = childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit ? childExpirationTimeAfterCommit : updateExpirationTimeAfterCommit;
+ if (earliestRemainingTimeAfterCommit === NoWork) {
+ // If there's no remaining work, we can clear the set of already failed
+ // error boundaries.
+ legacyErrorBoundariesThatAlreadyFailed = null;
+ }
+ onCommit(root, earliestRemainingTimeAfterCommit);
+
+ if (enableSchedulerTracing) {
+ __interactionsRef.current = prevInteractions;
+
+ var subscriber = void 0;
+
+ try {
+ subscriber = __subscriberRef.current;
+ if (subscriber !== null && root.memoizedInteractions.size > 0) {
+ var threadID = computeThreadID(committedExpirationTime, root.interactionThreadID);
+ subscriber.onWorkStopped(root.memoizedInteractions, threadID);
+ }
+ } catch (error) {
+ // It's not safe for commitRoot() to throw.
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ } finally {
+ // Clear completed interactions from the pending Map.
+ // Unless the render was suspended or cascading work was scheduled,
+ // In which case– leave pending interactions until the subsequent render.
+ var pendingInteractionMap = root.pendingInteractionMap;
+ pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
+ // Only decrement the pending interaction count if we're done.
+ // If there's still work at the current priority,
+ // That indicates that we are waiting for suspense data.
+ if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) {
+ pendingInteractionMap.delete(scheduledExpirationTime);
+
+ scheduledInteractions.forEach(function (interaction) {
+ interaction.__count--;
+
+ if (subscriber !== null && interaction.__count === 0) {
+ try {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ } catch (error) {
+ // It's not safe for commitRoot() to throw.
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+ }
+}
+
+function resetChildExpirationTime(workInProgress, renderTime) {
+ if (renderTime !== Never && workInProgress.childExpirationTime === Never) {
+ // The children of this component are hidden. Don't bubble their
+ // expiration times.
+ return;
+ }
+
+ var newChildExpirationTime = NoWork;
+
+ // Bubble up the earliest expiration time.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // We're in profiling mode.
+ // Let's use this same traversal to update the render durations.
+ var actualDuration = workInProgress.actualDuration;
+ var treeBaseDuration = workInProgress.selfBaseDuration;
+
+ // When a fiber is cloned, its actualDuration is reset to 0.
+ // This value will only be updated if work is done on the fiber (i.e. it doesn't bailout).
+ // When work is done, it should bubble to the parent's actualDuration.
+ // If the fiber has not been cloned though, (meaning no work was done),
+ // Then this value will reflect the amount of time spent working on a previous render.
+ // In that case it should not bubble.
+ // We determine whether it was cloned by comparing the child pointer.
+ var shouldBubbleActualDurations = workInProgress.alternate === null || workInProgress.child !== workInProgress.alternate.child;
+
+ var child = workInProgress.child;
+ while (child !== null) {
+ var childUpdateExpirationTime = child.expirationTime;
+ var childChildExpirationTime = child.childExpirationTime;
+ if (childUpdateExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = childUpdateExpirationTime;
+ }
+ if (childChildExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = childChildExpirationTime;
+ }
+ if (shouldBubbleActualDurations) {
+ actualDuration += child.actualDuration;
+ }
+ treeBaseDuration += child.treeBaseDuration;
+ child = child.sibling;
+ }
+ workInProgress.actualDuration = actualDuration;
+ workInProgress.treeBaseDuration = treeBaseDuration;
+ } else {
+ var _child = workInProgress.child;
+ while (_child !== null) {
+ var _childUpdateExpirationTime = _child.expirationTime;
+ var _childChildExpirationTime = _child.childExpirationTime;
+ if (_childUpdateExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = _childUpdateExpirationTime;
+ }
+ if (_childChildExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = _childChildExpirationTime;
+ }
+ _child = _child.sibling;
+ }
+ }
+
+ workInProgress.childExpirationTime = newChildExpirationTime;
+}
+
+function completeUnitOfWork(workInProgress) {
+ // Attempt to complete the current unit of work, then move to the
+ // next sibling. If there are no more siblings, return to the
+ // parent fiber.
+ while (true) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current$$1 = workInProgress.alternate;
+ var returnFiber = workInProgress.return;
+ var siblingFiber = workInProgress.sibling;
+
+ if ((workInProgress.effectTag & Incomplete) === NoEffect) {
+ nextUnitOfWork = workInProgress;
+ if (enableProfilerTimer) {
+ if (workInProgress.mode & ProfileMode) {
+ startProfilerTimer(workInProgress);
+ }
+ nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
+ if (workInProgress.mode & ProfileMode) {
+ // Update render duration assuming we didn't error.
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
+ }
+ } else {
+ nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
+ }
+ stopWorkTimer(workInProgress);
+ resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
+ if (nextUnitOfWork !== null) {
+ // Completing this fiber spawned new work. Work on that next.
+ return nextUnitOfWork;
+ }
+
+ if (returnFiber !== null &&
+ // Do not append effects to parents if a sibling failed to complete
+ (returnFiber.effectTag & Incomplete) === NoEffect) {
+ // Append all the effects of the subtree and this fiber onto the effect
+ // list of the parent. The completion order of the children affects the
+ // side-effect order.
+ if (returnFiber.firstEffect === null) {
+ returnFiber.firstEffect = workInProgress.firstEffect;
+ }
+ if (workInProgress.lastEffect !== null) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
+ }
+ returnFiber.lastEffect = workInProgress.lastEffect;
+ }
+
+ // If this fiber had side-effects, we append it AFTER the children's
+ // side-effects. We can perform certain side-effects earlier if
+ // needed, by doing multiple passes over the effect list. We don't want
+ // to schedule our own side-effect on our own list because if end up
+ // reusing children we'll schedule this effect onto itself since we're
+ // at the end.
+ var effectTag = workInProgress.effectTag;
+ // Skip both NoWork and PerformedWork tags when creating the effect list.
+ // PerformedWork effect is read by React DevTools but shouldn't be committed.
+ if (effectTag > PerformedWork) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress;
+ } else {
+ returnFiber.firstEffect = workInProgress;
+ }
+ returnFiber.lastEffect = workInProgress;
+ }
+ }
+
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ return siblingFiber;
+ } else if (returnFiber !== null) {
+ // If there's no more work in this returnFiber. Complete the returnFiber.
+ workInProgress = returnFiber;
+ continue;
+ } else {
+ // We've reached the root.
+ return null;
+ }
+ } else {
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // Record the render duration for the fiber that errored.
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
+
+ // Include the time spent working on failed children before continuing.
+ var actualDuration = workInProgress.actualDuration;
+ var child = workInProgress.child;
+ while (child !== null) {
+ actualDuration += child.actualDuration;
+ child = child.sibling;
+ }
+ workInProgress.actualDuration = actualDuration;
+ }
+
+ // This fiber did not complete because something threw. Pop values off
+ // the stack without entering the complete phase. If this is a boundary,
+ // capture values if possible.
+ var next = unwindWork(workInProgress, nextRenderExpirationTime);
+ // Because this fiber did not complete, don't reset its expiration time.
+ if (workInProgress.effectTag & DidCapture) {
+ // Restarting an error boundary
+ stopFailedWorkTimer(workInProgress);
+ } else {
+ stopWorkTimer(workInProgress);
+ }
+
+ if (next !== null) {
+ stopWorkTimer(workInProgress);
+ next.effectTag &= HostEffectMask;
+ return next;
+ }
+
+ if (returnFiber !== null) {
+ // Mark the parent fiber as incomplete and clear its effect list.
+ returnFiber.firstEffect = returnFiber.lastEffect = null;
+ returnFiber.effectTag |= Incomplete;
+ }
+
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ return siblingFiber;
+ } else if (returnFiber !== null) {
+ // If there's no more work in this returnFiber. Complete the returnFiber.
+ workInProgress = returnFiber;
+ continue;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ // Without this explicit null return Flow complains of invalid return type
+ // TODO Remove the above while(true) loop
+ // eslint-disable-next-line no-unreachable
+ return null;
+}
+
+function performUnitOfWork(workInProgress) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current$$1 = workInProgress.alternate;
+
+ // See if beginning this work spawns more work.
+ startWorkTimer(workInProgress);
+ var next = void 0;
+ if (enableProfilerTimer) {
+ if (workInProgress.mode & ProfileMode) {
+ startProfilerTimer(workInProgress);
+ }
+
+ next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
+ workInProgress.memoizedProps = workInProgress.pendingProps;
+
+ if (workInProgress.mode & ProfileMode) {
+ // Record the render duration assuming we didn't bailout (or error).
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
+ }
+ } else {
+ next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
+ workInProgress.memoizedProps = workInProgress.pendingProps;
+ }
+
+ if (next === null) {
+ // If this doesn't spawn new work, complete the current work.
+ next = completeUnitOfWork(workInProgress);
+ }
+
+ ReactCurrentOwner$2.current = null;
+
+ return next;
+}
+
+function workLoop(isYieldy) {
+ if (!isYieldy) {
+ // Flush work without yielding
+ while (nextUnitOfWork !== null) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ }
+ } else {
+ // Flush asynchronous work until there's a higher priority event
+ while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ }
+ }
+}
+
+function renderRoot(root, isYieldy) {
+ !!isWorking ? reactProdInvariant('243') : void 0;
+
+ flushPassiveEffects();
+
+ isWorking = true;
+ var previousDispatcher = ReactCurrentDispatcher.current;
+ ReactCurrentDispatcher.current = ContextOnlyDispatcher;
+
+ var expirationTime = root.nextExpirationTimeToWorkOn;
+
+ // Check if we're starting from a fresh stack, or if we're resuming from
+ // previously yielded work.
+ if (expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null) {
+ // Reset the stack and start working from the root.
+ resetStack();
+ nextRoot = root;
+ nextRenderExpirationTime = expirationTime;
+ nextUnitOfWork = createWorkInProgress(nextRoot.current, null, nextRenderExpirationTime);
+ root.pendingCommitExpirationTime = NoWork;
+
+ if (enableSchedulerTracing) {
+ // Determine which interactions this batch of work currently includes,
+ // So that we can accurately attribute time spent working on it,
+ var interactions = new Set();
+ root.pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
+ if (scheduledExpirationTime >= expirationTime) {
+ scheduledInteractions.forEach(function (interaction) {
+ return interactions.add(interaction);
+ });
+ }
+ });
+
+ // Store the current set of interactions on the FiberRoot for a few reasons:
+ // We can re-use it in hot functions like renderRoot() without having to recalculate it.
+ // We will also use it in commitWork() to pass to any Profiler onRender() hooks.
+ // This also provides DevTools with a way to access it when the onCommitRoot() hook is called.
+ root.memoizedInteractions = interactions;
+
+ if (interactions.size > 0) {
+ var subscriber = __subscriberRef.current;
+ if (subscriber !== null) {
+ var threadID = computeThreadID(expirationTime, root.interactionThreadID);
+ try {
+ subscriber.onWorkStarted(interactions, threadID);
+ } catch (error) {
+ // Work thrown by an interaction tracing subscriber should be rethrown,
+ // But only once it's safe (to avoid leaving the scheduler in an invalid state).
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ var prevInteractions = null;
+ if (enableSchedulerTracing) {
+ // We're about to start new traced work.
+ // Restore pending interactions so cascading work triggered during the render phase will be accounted for.
+ prevInteractions = __interactionsRef.current;
+ __interactionsRef.current = root.memoizedInteractions;
+ }
+
+ var didFatal = false;
+
+ startWorkLoopTimer(nextUnitOfWork);
+
+ do {
+ try {
+ workLoop(isYieldy);
+ } catch (thrownValue) {
+ resetContextDependences();
+ resetHooks();
+
+ // Reset in case completion throws.
+ // This is only used in DEV and when replaying is on.
+ if (nextUnitOfWork === null) {
+ // This is a fatal error.
+ didFatal = true;
+ onUncaughtError(thrownValue);
+ } else {
+ if (enableProfilerTimer && nextUnitOfWork.mode & ProfileMode) {
+ // Record the time spent rendering before an error was thrown.
+ // This avoids inaccurate Profiler durations in the case of a suspended render.
+ stopProfilerTimerIfRunningAndRecordDelta(nextUnitOfWork, true);
+ }
+
+ !(nextUnitOfWork !== null) ? reactProdInvariant('271') : void 0;
+
+ var sourceFiber = nextUnitOfWork;
+ var returnFiber = sourceFiber.return;
+ if (returnFiber === null) {
+ // This is the root. The root could capture its own errors. However,
+ // we don't know if it errors before or after we pushed the host
+ // context. This information is needed to avoid a stack mismatch.
+ // Because we're not sure, treat this as a fatal error. We could track
+ // which phase it fails in, but doesn't seem worth it. At least
+ // for now.
+ didFatal = true;
+ onUncaughtError(thrownValue);
+ } else {
+ throwException(root, returnFiber, sourceFiber, thrownValue, nextRenderExpirationTime);
+ nextUnitOfWork = completeUnitOfWork(sourceFiber);
+ continue;
+ }
+ }
+ }
+ break;
+ } while (true);
+
+ if (enableSchedulerTracing) {
+ // Traced work is done for now; restore the previous interactions.
+ __interactionsRef.current = prevInteractions;
+ }
+
+ // We're done performing work. Time to clean up.
+ isWorking = false;
+ ReactCurrentDispatcher.current = previousDispatcher;
+ resetContextDependences();
+ resetHooks();
+
+ // Yield back to main thread.
+ if (didFatal) {
+ var _didCompleteRoot = false;
+ stopWorkLoopTimer(interruptedBy, _didCompleteRoot);
+ interruptedBy = null;
+ // There was a fatal error.
+ nextRoot = null;
+ onFatal(root);
+ return;
+ }
+
+ if (nextUnitOfWork !== null) {
+ // There's still remaining async work in this tree, but we ran out of time
+ // in the current frame. Yield back to the renderer. Unless we're
+ // interrupted by a higher priority update, we'll continue later from where
+ // we left off.
+ var _didCompleteRoot2 = false;
+ stopWorkLoopTimer(interruptedBy, _didCompleteRoot2);
+ interruptedBy = null;
+ onYield(root);
+ return;
+ }
+
+ // We completed the whole tree.
+ var didCompleteRoot = true;
+ stopWorkLoopTimer(interruptedBy, didCompleteRoot);
+ var rootWorkInProgress = root.current.alternate;
+ !(rootWorkInProgress !== null) ? reactProdInvariant('281') : void 0;
+
+ // `nextRoot` points to the in-progress root. A non-null value indicates
+ // that we're in the middle of an async render. Set it to null to indicate
+ // there's no more work to be done in the current batch.
+ nextRoot = null;
+ interruptedBy = null;
+
+ if (nextRenderDidError) {
+ // There was an error
+ if (hasLowerPriorityWork(root, expirationTime)) {
+ // There's lower priority work. If so, it may have the effect of fixing
+ // the exception that was just thrown. Exit without committing. This is
+ // similar to a suspend, but without a timeout because we're not waiting
+ // for a promise to resolve. React will restart at the lower
+ // priority level.
+ markSuspendedPriorityLevel(root, expirationTime);
+ var suspendedExpirationTime = expirationTime;
+ var rootExpirationTime = root.expirationTime;
+ onSuspend(root, rootWorkInProgress, suspendedExpirationTime, rootExpirationTime, -1 // Indicates no timeout
+ );
+ return;
+ } else if (
+ // There's no lower priority work, but we're rendering asynchronously.
+ // Synchronously attempt to render the same level one more time. This is
+ // similar to a suspend, but without a timeout because we're not waiting
+ // for a promise to resolve.
+ !root.didError && isYieldy) {
+ root.didError = true;
+ var _suspendedExpirationTime = root.nextExpirationTimeToWorkOn = expirationTime;
+ var _rootExpirationTime = root.expirationTime = Sync;
+ onSuspend(root, rootWorkInProgress, _suspendedExpirationTime, _rootExpirationTime, -1 // Indicates no timeout
+ );
+ return;
+ }
+ }
+
+ if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {
+ // The tree was suspended.
+ var _suspendedExpirationTime2 = expirationTime;
+ markSuspendedPriorityLevel(root, _suspendedExpirationTime2);
+
+ // Find the earliest uncommitted expiration time in the tree, including
+ // work that is suspended. The timeout threshold cannot be longer than
+ // the overall expiration.
+ var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, expirationTime);
+ var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
+ if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {
+ nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;
+ }
+
+ // Subtract the current time from the absolute timeout to get the number
+ // of milliseconds until the timeout. In other words, convert an absolute
+ // timestamp to a relative time. This is the value that is passed
+ // to `setTimeout`.
+ var currentTimeMs = expirationTimeToMs(requestCurrentTime());
+ var msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;
+ msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;
+
+ // TODO: Account for the Just Noticeable Difference
+
+ var _rootExpirationTime2 = root.expirationTime;
+ onSuspend(root, rootWorkInProgress, _suspendedExpirationTime2, _rootExpirationTime2, msUntilTimeout);
+ return;
+ }
+
+ // Ready to commit.
+ onComplete(root, rootWorkInProgress, expirationTime);
+}
+
+function captureCommitPhaseError(sourceFiber, value) {
+ var expirationTime = Sync;
+ var fiber = sourceFiber.return;
+ while (fiber !== null) {
+ switch (fiber.tag) {
+ case ClassComponent:
+ var ctor = fiber.type;
+ var instance = fiber.stateNode;
+ if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {
+ var errorInfo = createCapturedValue(value, sourceFiber);
+ var update = createClassErrorUpdate(fiber, errorInfo, expirationTime);
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ return;
+ }
+ break;
+ case HostRoot:
+ {
+ var _errorInfo = createCapturedValue(value, sourceFiber);
+ var _update = createRootErrorUpdate(fiber, _errorInfo, expirationTime);
+ enqueueUpdate(fiber, _update);
+ scheduleWork(fiber, expirationTime);
+ return;
+ }
+ }
+ fiber = fiber.return;
+ }
+
+ if (sourceFiber.tag === HostRoot) {
+ // Error was thrown at the root. There is no parent, so the root
+ // itself should capture it.
+ var rootFiber = sourceFiber;
+ var _errorInfo2 = createCapturedValue(value, rootFiber);
+ var _update2 = createRootErrorUpdate(rootFiber, _errorInfo2, expirationTime);
+ enqueueUpdate(rootFiber, _update2);
+ scheduleWork(rootFiber, expirationTime);
+ }
+}
+
+function computeThreadID(expirationTime, interactionThreadID) {
+ // Interaction threads are unique per root and expiration time.
+ return expirationTime * 1000 + interactionThreadID;
+}
+
+// Creates a unique async expiration time.
+function computeUniqueAsyncExpiration() {
+ var currentTime = requestCurrentTime();
+ var result = computeAsyncExpiration(currentTime);
+ if (result >= lastUniqueAsyncExpiration) {
+ // Since we assume the current time monotonically increases, we only hit
+ // this branch when computeUniqueAsyncExpiration is fired multiple times
+ // within a 200ms window (or whatever the async bucket size is).
+ result = lastUniqueAsyncExpiration - 1;
+ }
+ lastUniqueAsyncExpiration = result;
+ return lastUniqueAsyncExpiration;
+}
+
+function computeExpirationForFiber(currentTime, fiber) {
+ var priorityLevel = unstable_getCurrentPriorityLevel();
+
+ var expirationTime = void 0;
+ if ((fiber.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, updates are always synchronous.
+ expirationTime = Sync;
+ } else if (isWorking && !isCommitting$1) {
+ // During render phase, updates expire during as the current render.
+ expirationTime = nextRenderExpirationTime;
+ } else {
+ switch (priorityLevel) {
+ case unstable_ImmediatePriority:
+ expirationTime = Sync;
+ break;
+ case unstable_UserBlockingPriority:
+ expirationTime = computeInteractiveExpiration(currentTime);
+ break;
+ case unstable_NormalPriority:
+ // This is a normal, concurrent update
+ expirationTime = computeAsyncExpiration(currentTime);
+ break;
+ case unstable_LowPriority:
+ case unstable_IdlePriority:
+ expirationTime = Never;
+ break;
+ default:
+ reactProdInvariant('313');
+ }
+
+ // If we're in the middle of rendering a tree, do not update at the same
+ // expiration time that is already rendering.
+ if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
+ expirationTime -= 1;
+ }
+ }
+
+ // Keep track of the lowest pending interactive expiration time. This
+ // allows us to synchronously flush all interactive updates
+ // when needed.
+ // TODO: Move this to renderer?
+ if (priorityLevel === unstable_UserBlockingPriority && (lowestPriorityPendingInteractiveExpirationTime === NoWork || expirationTime < lowestPriorityPendingInteractiveExpirationTime)) {
+ lowestPriorityPendingInteractiveExpirationTime = expirationTime;
+ }
+
+ return expirationTime;
+}
+
+function renderDidSuspend(root, absoluteTimeoutMs, suspendedTime) {
+ // Schedule the timeout.
+ if (absoluteTimeoutMs >= 0 && nextLatestAbsoluteTimeoutMs < absoluteTimeoutMs) {
+ nextLatestAbsoluteTimeoutMs = absoluteTimeoutMs;
+ }
+}
+
+function renderDidError() {
+ nextRenderDidError = true;
+}
+
+function pingSuspendedRoot(root, thenable, pingTime) {
+ // A promise that previously suspended React from committing has resolved.
+ // If React is still suspended, try again at the previous level (pingTime).
+
+ var pingCache = root.pingCache;
+ if (pingCache !== null) {
+ // The thenable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ pingCache.delete(thenable);
+ }
+
+ if (nextRoot !== null && nextRenderExpirationTime === pingTime) {
+ // Received a ping at the same priority level at which we're currently
+ // rendering. Restart from the root.
+ nextRoot = null;
+ } else {
+ // Confirm that the root is still suspended at this level. Otherwise exit.
+ if (isPriorityLevelSuspended(root, pingTime)) {
+ // Ping at the original level
+ markPingedPriorityLevel(root, pingTime);
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ }
+ }
+}
+
+function retryTimedOutBoundary(boundaryFiber, thenable) {
+ // The boundary fiber (a Suspense component) previously timed out and was
+ // rendered in its fallback state. One of the promises that suspended it has
+ // resolved, which means at least part of the tree was likely unblocked. Try
+ var retryCache = void 0;
+ if (enableSuspenseServerRenderer) {
+ switch (boundaryFiber.tag) {
+ case SuspenseComponent:
+ retryCache = boundaryFiber.stateNode;
+ break;
+ case DehydratedSuspenseComponent:
+ retryCache = boundaryFiber.memoizedState;
+ break;
+ default:
+ reactProdInvariant('314');
+ }
+ } else {
+ retryCache = boundaryFiber.stateNode;
+ }
+ if (retryCache !== null) {
+ // The thenable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ retryCache.delete(thenable);
+ }
+
+ var currentTime = requestCurrentTime();
+ var retryTime = computeExpirationForFiber(currentTime, boundaryFiber);
+ var root = scheduleWorkToRoot(boundaryFiber, retryTime);
+ if (root !== null) {
+ markPendingPriorityLevel(root, retryTime);
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ }
+}
+
+function scheduleWorkToRoot(fiber, expirationTime) {
+ recordScheduleUpdate();
+
+ if (fiber.expirationTime < expirationTime) {
+ fiber.expirationTime = expirationTime;
+ }
+ var alternate = fiber.alternate;
+ if (alternate !== null && alternate.expirationTime < expirationTime) {
+ alternate.expirationTime = expirationTime;
+ }
+ // Walk the parent path to the root and update the child expiration time.
+ var node = fiber.return;
+ var root = null;
+ if (node === null && fiber.tag === HostRoot) {
+ root = fiber.stateNode;
+ } else {
+ while (node !== null) {
+ alternate = node.alternate;
+ if (node.childExpirationTime < expirationTime) {
+ node.childExpirationTime = expirationTime;
+ if (alternate !== null && alternate.childExpirationTime < expirationTime) {
+ alternate.childExpirationTime = expirationTime;
+ }
+ } else if (alternate !== null && alternate.childExpirationTime < expirationTime) {
+ alternate.childExpirationTime = expirationTime;
+ }
+ if (node.return === null && node.tag === HostRoot) {
+ root = node.stateNode;
+ break;
+ }
+ node = node.return;
+ }
+ }
+
+ if (enableSchedulerTracing) {
+ if (root !== null) {
+ var interactions = __interactionsRef.current;
+ if (interactions.size > 0) {
+ var pendingInteractionMap = root.pendingInteractionMap;
+ var pendingInteractions = pendingInteractionMap.get(expirationTime);
+ if (pendingInteractions != null) {
+ interactions.forEach(function (interaction) {
+ if (!pendingInteractions.has(interaction)) {
+ // Update the pending async work count for previously unscheduled interaction.
+ interaction.__count++;
+ }
+
+ pendingInteractions.add(interaction);
+ });
+ } else {
+ pendingInteractionMap.set(expirationTime, new Set(interactions));
+
+ // Update the pending async work count for the current interactions.
+ interactions.forEach(function (interaction) {
+ interaction.__count++;
+ });
+ }
+
+ var subscriber = __subscriberRef.current;
+ if (subscriber !== null) {
+ var threadID = computeThreadID(expirationTime, root.interactionThreadID);
+ subscriber.onWorkScheduled(interactions, threadID);
+ }
+ }
+ }
+ }
+ return root;
+}
+
+
+
+function scheduleWork(fiber, expirationTime) {
+ var root = scheduleWorkToRoot(fiber, expirationTime);
+ if (root === null) {
+ return;
+ }
+
+ if (!isWorking && nextRenderExpirationTime !== NoWork && expirationTime > nextRenderExpirationTime) {
+ // This is an interruption. (Used for performance tracking.)
+ interruptedBy = fiber;
+ resetStack();
+ }
+ markPendingPriorityLevel(root, expirationTime);
+ if (
+ // If we're in the render phase, we don't need to schedule this root
+ // for an update, because we'll do it before we exit...
+ !isWorking || isCommitting$1 ||
+ // ...unless this is a different root than the one we're rendering.
+ nextRoot !== root) {
+ var rootExpirationTime = root.expirationTime;
+ requestWork(root, rootExpirationTime);
+ }
+ if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
+ // Reset this back to zero so subsequent updates don't throw.
+ nestedUpdateCount = 0;
+ reactProdInvariant('185');
+ }
+}
+
+function syncUpdates(fn, a, b, c, d) {
+ return unstable_runWithPriority(unstable_ImmediatePriority, function () {
+ return fn(a, b, c, d);
+ });
+}
+
+// TODO: Everything below this is written as if it has been lifted to the
+// renderers. I'll do this in a follow-up.
+
+// Linked-list of roots
+var firstScheduledRoot = null;
+var lastScheduledRoot = null;
+
+var callbackExpirationTime = NoWork;
+var callbackID = void 0;
+var isRendering = false;
+var nextFlushedRoot = null;
+var nextFlushedExpirationTime = NoWork;
+var lowestPriorityPendingInteractiveExpirationTime = NoWork;
+var hasUnhandledError = false;
+var unhandledError = null;
+
+var isBatchingUpdates = false;
+var isUnbatchingUpdates = false;
+
+var completedBatches = null;
+
+var originalStartTimeMs = unstable_now();
+var currentRendererTime = msToExpirationTime(originalStartTimeMs);
+var currentSchedulerTime = currentRendererTime;
+
+// Use these to prevent an infinite loop of nested updates
+var NESTED_UPDATE_LIMIT = 50;
+var nestedUpdateCount = 0;
+var lastCommittedRootDuringThisBatch = null;
+
+function recomputeCurrentRendererTime() {
+ var currentTimeMs = unstable_now() - originalStartTimeMs;
+ currentRendererTime = msToExpirationTime(currentTimeMs);
+}
+
+function scheduleCallbackWithExpirationTime(root, expirationTime) {
+ if (callbackExpirationTime !== NoWork) {
+ // A callback is already scheduled. Check its expiration time (timeout).
+ if (expirationTime < callbackExpirationTime) {
+ // Existing callback has sufficient timeout. Exit.
+ return;
+ } else {
+ if (callbackID !== null) {
+ // Existing callback has insufficient timeout. Cancel and schedule a
+ // new one.
+ unstable_cancelCallback(callbackID);
+ }
+ }
+ // The request callback timer is already running. Don't start a new one.
+ } else {
+ startRequestCallbackTimer();
+ }
+
+ callbackExpirationTime = expirationTime;
+ var currentMs = unstable_now() - originalStartTimeMs;
+ var expirationTimeMs = expirationTimeToMs(expirationTime);
+ var timeout = expirationTimeMs - currentMs;
+ callbackID = unstable_scheduleCallback(performAsyncWork, { timeout: timeout });
+}
+
+// For every call to renderRoot, one of onFatal, onComplete, onSuspend, and
+// onYield is called upon exiting. We use these in lieu of returning a tuple.
+// I've also chosen not to inline them into renderRoot because these will
+// eventually be lifted into the renderer.
+function onFatal(root) {
+ root.finishedWork = null;
+}
+
+function onComplete(root, finishedWork, expirationTime) {
+ root.pendingCommitExpirationTime = expirationTime;
+ root.finishedWork = finishedWork;
+}
+
+function onSuspend(root, finishedWork, suspendedExpirationTime, rootExpirationTime, msUntilTimeout) {
+ root.expirationTime = rootExpirationTime;
+ if (msUntilTimeout === 0 && !shouldYieldToRenderer()) {
+ // Don't wait an additional tick. Commit the tree immediately.
+ root.pendingCommitExpirationTime = suspendedExpirationTime;
+ root.finishedWork = finishedWork;
+ } else if (msUntilTimeout > 0) {
+ // Wait `msUntilTimeout` milliseconds before committing.
+ root.timeoutHandle = scheduleTimeout(onTimeout.bind(null, root, finishedWork, suspendedExpirationTime), msUntilTimeout);
+ }
+}
+
+function onYield(root) {
+ root.finishedWork = null;
+}
+
+function onTimeout(root, finishedWork, suspendedExpirationTime) {
+ // The root timed out. Commit it.
+ root.pendingCommitExpirationTime = suspendedExpirationTime;
+ root.finishedWork = finishedWork;
+ // Read the current time before entering the commit phase. We can be
+ // certain this won't cause tearing related to batching of event updates
+ // because we're at the top of a timer event.
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ flushRoot(root, suspendedExpirationTime);
+}
+
+function onCommit(root, expirationTime) {
+ root.expirationTime = expirationTime;
+ root.finishedWork = null;
+}
+
+function requestCurrentTime() {
+ // requestCurrentTime is called by the scheduler to compute an expiration
+ // time.
+ //
+ // Expiration times are computed by adding to the current time (the start
+ // time). However, if two updates are scheduled within the same event, we
+ // should treat their start times as simultaneous, even if the actual clock
+ // time has advanced between the first and second call.
+
+ // In other words, because expiration times determine how updates are batched,
+ // we want all updates of like priority that occur within the same event to
+ // receive the same expiration time. Otherwise we get tearing.
+ //
+ // We keep track of two separate times: the current "renderer" time and the
+ // current "scheduler" time. The renderer time can be updated whenever; it
+ // only exists to minimize the calls performance.now.
+ //
+ // But the scheduler time can only be updated if there's no pending work, or
+ // if we know for certain that we're not in the middle of an event.
+
+ if (isRendering) {
+ // We're already rendering. Return the most recently read time.
+ return currentSchedulerTime;
+ }
+ // Check if there's pending work.
+ findHighestPriorityRoot();
+ if (nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never) {
+ // If there's no pending work, or if the pending work is offscreen, we can
+ // read the current time without risk of tearing.
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ return currentSchedulerTime;
+ }
+ // There's already pending work. We might be in the middle of a browser
+ // event. If we were to read the current time, it could cause multiple updates
+ // within the same event to receive different expiration times, leading to
+ // tearing. Return the last read time. During the next idle callback, the
+ // time will be updated.
+ return currentSchedulerTime;
+}
+
+// requestWork is called by the scheduler whenever a root receives an update.
+// It's up to the renderer to call renderRoot at some point in the future.
+function requestWork(root, expirationTime) {
+ addRootToSchedule(root, expirationTime);
+ if (isRendering) {
+ // Prevent reentrancy. Remaining work will be scheduled at the end of
+ // the currently rendering batch.
+ return;
+ }
+
+ if (isBatchingUpdates) {
+ // Flush work at the end of the batch.
+ if (isUnbatchingUpdates) {
+ // ...unless we're inside unbatchedUpdates, in which case we should
+ // flush it now.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = Sync;
+ performWorkOnRoot(root, Sync, false);
+ }
+ return;
+ }
+
+ // TODO: Get rid of Sync and use current time?
+ if (expirationTime === Sync) {
+ performSyncWork();
+ } else {
+ scheduleCallbackWithExpirationTime(root, expirationTime);
+ }
+}
+
+function addRootToSchedule(root, expirationTime) {
+ // Add the root to the schedule.
+ // Check if this root is already part of the schedule.
+ if (root.nextScheduledRoot === null) {
+ // This root is not already scheduled. Add it.
+ root.expirationTime = expirationTime;
+ if (lastScheduledRoot === null) {
+ firstScheduledRoot = lastScheduledRoot = root;
+ root.nextScheduledRoot = root;
+ } else {
+ lastScheduledRoot.nextScheduledRoot = root;
+ lastScheduledRoot = root;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ }
+ } else {
+ // This root is already scheduled, but its priority may have increased.
+ var remainingExpirationTime = root.expirationTime;
+ if (expirationTime > remainingExpirationTime) {
+ // Update the priority.
+ root.expirationTime = expirationTime;
+ }
+ }
+}
+
+function findHighestPriorityRoot() {
+ var highestPriorityWork = NoWork;
+ var highestPriorityRoot = null;
+ if (lastScheduledRoot !== null) {
+ var previousScheduledRoot = lastScheduledRoot;
+ var root = firstScheduledRoot;
+ while (root !== null) {
+ var remainingExpirationTime = root.expirationTime;
+ if (remainingExpirationTime === NoWork) {
+ // This root no longer has work. Remove it from the scheduler.
+
+ // TODO: This check is redudant, but Flow is confused by the branch
+ // below where we set lastScheduledRoot to null, even though we break
+ // from the loop right after.
+ !(previousScheduledRoot !== null && lastScheduledRoot !== null) ? reactProdInvariant('244') : void 0;
+ if (root === root.nextScheduledRoot) {
+ // This is the only root in the list.
+ root.nextScheduledRoot = null;
+ firstScheduledRoot = lastScheduledRoot = null;
+ break;
+ } else if (root === firstScheduledRoot) {
+ // This is the first root in the list.
+ var next = root.nextScheduledRoot;
+ firstScheduledRoot = next;
+ lastScheduledRoot.nextScheduledRoot = next;
+ root.nextScheduledRoot = null;
+ } else if (root === lastScheduledRoot) {
+ // This is the last root in the list.
+ lastScheduledRoot = previousScheduledRoot;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ root.nextScheduledRoot = null;
+ break;
+ } else {
+ previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
+ root.nextScheduledRoot = null;
+ }
+ root = previousScheduledRoot.nextScheduledRoot;
+ } else {
+ if (remainingExpirationTime > highestPriorityWork) {
+ // Update the priority, if it's higher
+ highestPriorityWork = remainingExpirationTime;
+ highestPriorityRoot = root;
+ }
+ if (root === lastScheduledRoot) {
+ break;
+ }
+ if (highestPriorityWork === Sync) {
+ // Sync is highest priority by definition so
+ // we can stop searching.
+ break;
+ }
+ previousScheduledRoot = root;
+ root = root.nextScheduledRoot;
+ }
+ }
+ }
+
+ nextFlushedRoot = highestPriorityRoot;
+ nextFlushedExpirationTime = highestPriorityWork;
+}
+
+// TODO: This wrapper exists because many of the older tests (the ones that use
+// flushDeferredPri) rely on the number of times `shouldYield` is called. We
+// should get rid of it.
+var didYield = false;
+function shouldYieldToRenderer() {
+ if (didYield) {
+ return true;
+ }
+ if (unstable_shouldYield()) {
+ didYield = true;
+ return true;
+ }
+ return false;
+}
+
+function performAsyncWork() {
+ try {
+ if (!shouldYieldToRenderer()) {
+ // The callback timed out. That means at least one update has expired.
+ // Iterate through the root schedule. If they contain expired work, set
+ // the next render expiration time to the current time. This has the effect
+ // of flushing all expired work in a single batch, instead of flushing each
+ // level one at a time.
+ if (firstScheduledRoot !== null) {
+ recomputeCurrentRendererTime();
+ var root = firstScheduledRoot;
+ do {
+ didExpireAtExpirationTime(root, currentRendererTime);
+ // The root schedule is circular, so this is never null.
+ root = root.nextScheduledRoot;
+ } while (root !== firstScheduledRoot);
+ }
+ }
+ performWork(NoWork, true);
+ } finally {
+ didYield = false;
+ }
+}
+
+function performSyncWork() {
+ performWork(Sync, false);
+}
+
+function performWork(minExpirationTime, isYieldy) {
+ // Keep working on roots until there's no more work, or until there's a higher
+ // priority event.
+ findHighestPriorityRoot();
+
+ if (isYieldy) {
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+
+ if (enableUserTimingAPI) {
+ var didExpire = nextFlushedExpirationTime > currentRendererTime;
+ var timeout = expirationTimeToMs(nextFlushedExpirationTime);
+ stopRequestCallbackTimer(didExpire, timeout);
+ }
+
+ while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime && !(didYield && currentRendererTime > nextFlushedExpirationTime)) {
+ performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, currentRendererTime > nextFlushedExpirationTime);
+ findHighestPriorityRoot();
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ }
+ } else {
+ while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime) {
+ performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
+ findHighestPriorityRoot();
+ }
+ }
+
+ // We're done flushing work. Either we ran out of time in this callback,
+ // or there's no more work left with sufficient priority.
+
+ // If we're inside a callback, set this to false since we just completed it.
+ if (isYieldy) {
+ callbackExpirationTime = NoWork;
+ callbackID = null;
+ }
+ // If there's work left over, schedule a new callback.
+ if (nextFlushedExpirationTime !== NoWork) {
+ scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);
+ }
+
+ // Clean-up.
+ finishRendering();
+}
+
+function flushRoot(root, expirationTime) {
+ !!isRendering ? reactProdInvariant('253') : void 0;
+ // Perform work on root as if the given expiration time is the current time.
+ // This has the effect of synchronously flushing all work up to and
+ // including the given time.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = expirationTime;
+ performWorkOnRoot(root, expirationTime, false);
+ // Flush any sync work that was scheduled by lifecycles
+ performSyncWork();
+}
+
+function finishRendering() {
+ nestedUpdateCount = 0;
+ lastCommittedRootDuringThisBatch = null;
+
+ if (completedBatches !== null) {
+ var batches = completedBatches;
+ completedBatches = null;
+ for (var i = 0; i < batches.length; i++) {
+ var batch = batches[i];
+ try {
+ batch._onComplete();
+ } catch (error) {
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ }
+
+ if (hasUnhandledError) {
+ var error = unhandledError;
+ unhandledError = null;
+ hasUnhandledError = false;
+ throw error;
+ }
+}
+
+function performWorkOnRoot(root, expirationTime, isYieldy) {
+ !!isRendering ? reactProdInvariant('245') : void 0;
+
+ isRendering = true;
+
+ // Check if this is async work or sync/expired work.
+ if (!isYieldy) {
+ // Flush work without yielding.
+ // TODO: Non-yieldy work does not necessarily imply expired work. A renderer
+ // may want to perform some work without yielding, but also without
+ // requiring the root to complete (by triggering placeholders).
+
+ var finishedWork = root.finishedWork;
+ if (finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ // If this root previously suspended, clear its existing timeout, since
+ // we're about to try rendering again.
+ var timeoutHandle = root.timeoutHandle;
+ if (timeoutHandle !== noTimeout) {
+ root.timeoutHandle = noTimeout;
+ // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
+ cancelTimeout(timeoutHandle);
+ }
+ renderRoot(root, isYieldy);
+ finishedWork = root.finishedWork;
+ if (finishedWork !== null) {
+ // We've completed the root. Commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ }
+ }
+ } else {
+ // Flush async work.
+ var _finishedWork = root.finishedWork;
+ if (_finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ // If this root previously suspended, clear its existing timeout, since
+ // we're about to try rendering again.
+ var _timeoutHandle = root.timeoutHandle;
+ if (_timeoutHandle !== noTimeout) {
+ root.timeoutHandle = noTimeout;
+ // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
+ cancelTimeout(_timeoutHandle);
+ }
+ renderRoot(root, isYieldy);
+ _finishedWork = root.finishedWork;
+ if (_finishedWork !== null) {
+ // We've completed the root. Check the if we should yield one more time
+ // before committing.
+ if (!shouldYieldToRenderer()) {
+ // Still time left. Commit the root.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ // There's no time left. Mark this root as complete. We'll come
+ // back and commit it later.
+ root.finishedWork = _finishedWork;
+ }
+ }
+ }
+ }
+
+ isRendering = false;
+}
+
+function completeRoot(root, finishedWork, expirationTime) {
+ // Check if there's a batch that matches this expiration time.
+ var firstBatch = root.firstBatch;
+ if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
+ if (completedBatches === null) {
+ completedBatches = [firstBatch];
+ } else {
+ completedBatches.push(firstBatch);
+ }
+ if (firstBatch._defer) {
+ // This root is blocked from committing by a batch. Unschedule it until
+ // we receive another update.
+ root.finishedWork = finishedWork;
+ root.expirationTime = NoWork;
+ return;
+ }
+ }
+
+ // Commit the root.
+ root.finishedWork = null;
+
+ // Check if this is a nested update (a sync update scheduled during the
+ // commit phase).
+ if (root === lastCommittedRootDuringThisBatch) {
+ // If the next root is the same as the previous root, this is a nested
+ // update. To prevent an infinite loop, increment the nested update count.
+ nestedUpdateCount++;
+ } else {
+ // Reset whenever we switch roots.
+ lastCommittedRootDuringThisBatch = root;
+ nestedUpdateCount = 0;
+ }
+ unstable_runWithPriority(unstable_ImmediatePriority, function () {
+ commitRoot(root, finishedWork);
+ });
+}
+
+function onUncaughtError(error) {
+ !(nextFlushedRoot !== null) ? reactProdInvariant('246') : void 0;
+ // Unschedule this root so we don't work on it again until there's
+ // another update.
+ nextFlushedRoot.expirationTime = NoWork;
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+}
+
+// TODO: Batching should be implemented at the renderer level, not inside
+// the reconciler.
+function batchedUpdates$1(fn, a) {
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return fn(a);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+ }
+}
+
+// TODO: Batching should be implemented at the renderer level, not inside
+// the reconciler.
+function unbatchedUpdates(fn, a) {
+ if (isBatchingUpdates && !isUnbatchingUpdates) {
+ isUnbatchingUpdates = true;
+ try {
+ return fn(a);
+ } finally {
+ isUnbatchingUpdates = false;
+ }
+ }
+ return fn(a);
+}
+
+// TODO: Batching should be implemented at the renderer level, not within
+// the reconciler.
+function flushSync(fn, a) {
+ !!isRendering ? reactProdInvariant('187') : void 0;
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return syncUpdates(fn, a);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ performSyncWork();
+ }
+}
+
+function interactiveUpdates$1(fn, a, b) {
+ // If there are any pending interactive updates, synchronously flush them.
+ // This needs to happen before we read any handlers, because the effect of
+ // the previous event may influence which handlers are called during
+ // this event.
+ if (!isBatchingUpdates && !isRendering && lowestPriorityPendingInteractiveExpirationTime !== NoWork) {
+ // Synchronously flush pending interactive updates.
+ performWork(lowestPriorityPendingInteractiveExpirationTime, false);
+ lowestPriorityPendingInteractiveExpirationTime = NoWork;
+ }
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return unstable_runWithPriority(unstable_UserBlockingPriority, function () {
+ return fn(a, b);
+ });
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+ }
+}
+
+function flushInteractiveUpdates$1() {
+ if (!isRendering && lowestPriorityPendingInteractiveExpirationTime !== NoWork) {
+ // Synchronously flush pending interactive updates.
+ performWork(lowestPriorityPendingInteractiveExpirationTime, false);
+ lowestPriorityPendingInteractiveExpirationTime = NoWork;
+ }
+}
+
+function flushControlled(fn) {
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ syncUpdates(fn);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+ }
+}
+
+function getContextForSubtree(parentComponent) {
+ if (!parentComponent) {
+ return emptyContextObject;
+ }
+
+ var fiber = get(parentComponent);
+ var parentContext = findCurrentUnmaskedContext(fiber);
+
+ if (fiber.tag === ClassComponent) {
+ var Component = fiber.type;
+ if (isContextProvider(Component)) {
+ return processChildContext(fiber, Component, parentContext);
+ }
+ }
+
+ return parentContext;
+}
+
+function scheduleRootUpdate(current$$1, element, expirationTime, callback) {
+ var update = createUpdate(expirationTime);
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ update.payload = { element: element };
+
+ callback = callback === undefined ? null : callback;
+ if (callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects();
+ enqueueUpdate(current$$1, update);
+ scheduleWork(current$$1, expirationTime);
+
+ return expirationTime;
+}
+
+function updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback) {
+ // TODO: If this is a nested container, this won't be the root.
+ var current$$1 = container.current;
+
+ var context = getContextForSubtree(parentComponent);
+ if (container.context === null) {
+ container.context = context;
+ } else {
+ container.pendingContext = context;
+ }
+
+ return scheduleRootUpdate(current$$1, element, expirationTime, callback);
+}
+
+function findHostInstance(component) {
+ var fiber = get(component);
+ if (fiber === undefined) {
+ if (typeof component.render === 'function') {
+ reactProdInvariant('188');
+ } else {
+ reactProdInvariant('268', Object.keys(component));
+ }
+ }
+ var hostFiber = findCurrentHostFiber(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+}
+
+function createContainer(containerInfo, isConcurrent, hydrate) {
+ return createFiberRoot(containerInfo, isConcurrent, hydrate);
+}
+
+function updateContainer(element, container, parentComponent, callback) {
+ var current$$1 = container.current;
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, current$$1);
+ return updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback);
+}
+
+function getPublicRootInstance(container) {
+ var containerFiber = container.current;
+ if (!containerFiber.child) {
+ return null;
+ }
+ switch (containerFiber.child.tag) {
+ case HostComponent:
+ return getPublicInstance(containerFiber.child.stateNode);
+ default:
+ return containerFiber.child.stateNode;
+ }
+}
+
+
+
+var overrideProps = null;
+
+function injectIntoDevTools(devToolsConfig) {
+ var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
+ var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+
+
+ return injectInternals(_assign({}, devToolsConfig, {
+ overrideProps: overrideProps,
+ currentDispatcherRef: ReactCurrentDispatcher,
+ findHostInstanceByFiber: function (fiber) {
+ var hostFiber = findCurrentHostFiber(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+ },
+ findFiberByHostInstance: function (instance) {
+ if (!findFiberByHostInstance) {
+ // Might not be implemented by the renderer.
+ return null;
+ }
+ return findFiberByHostInstance(instance);
+ }
+ }));
+}
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+function createPortal$1(children, containerInfo,
+// TODO: figure out the API for cross-renderer implementation.
+implementation) {
+ var key = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
+
+ return {
+ // This tag allow us to uniquely identify this as a React Portal
+ $$typeof: REACT_PORTAL_TYPE,
+ key: key == null ? null : '' + key,
+ children: children,
+ containerInfo: containerInfo,
+ implementation: implementation
+ };
+}
+
+// TODO: this is special because it gets imported during build.
+
+var ReactVersion = '16.8.6';
+
+// TODO: This type is shared between the reconciler and ReactDOM, but will
+// eventually be lifted out to the renderer.
+
+var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
+
+setRestoreImplementation(restoreControlledState$1);
+
+function ReactBatch(root) {
+ var expirationTime = computeUniqueAsyncExpiration();
+ this._expirationTime = expirationTime;
+ this._root = root;
+ this._next = null;
+ this._callbacks = null;
+ this._didComplete = false;
+ this._hasChildren = false;
+ this._children = null;
+ this._defer = true;
+}
+ReactBatch.prototype.render = function (children) {
+ !this._defer ? reactProdInvariant('250') : void 0;
+ this._hasChildren = true;
+ this._children = children;
+ var internalRoot = this._root._internalRoot;
+ var expirationTime = this._expirationTime;
+ var work = new ReactWork();
+ updateContainerAtExpirationTime(children, internalRoot, null, expirationTime, work._onCommit);
+ return work;
+};
+ReactBatch.prototype.then = function (onComplete) {
+ if (this._didComplete) {
+ onComplete();
+ return;
+ }
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ callbacks = this._callbacks = [];
+ }
+ callbacks.push(onComplete);
+};
+ReactBatch.prototype.commit = function () {
+ var internalRoot = this._root._internalRoot;
+ var firstBatch = internalRoot.firstBatch;
+ !(this._defer && firstBatch !== null) ? reactProdInvariant('251') : void 0;
+
+ if (!this._hasChildren) {
+ // This batch is empty. Return.
+ this._next = null;
+ this._defer = false;
+ return;
+ }
+
+ var expirationTime = this._expirationTime;
+
+ // Ensure this is the first batch in the list.
+ if (firstBatch !== this) {
+ // This batch is not the earliest batch. We need to move it to the front.
+ // Update its expiration time to be the expiration time of the earliest
+ // batch, so that we can flush it without flushing the other batches.
+ if (this._hasChildren) {
+ expirationTime = this._expirationTime = firstBatch._expirationTime;
+ // Rendering this batch again ensures its children will be the final state
+ // when we flush (updates are processed in insertion order: last
+ // update wins).
+ // TODO: This forces a restart. Should we print a warning?
+ this.render(this._children);
+ }
+
+ // Remove the batch from the list.
+ var previous = null;
+ var batch = firstBatch;
+ while (batch !== this) {
+ previous = batch;
+ batch = batch._next;
+ }
+ !(previous !== null) ? reactProdInvariant('251') : void 0;
+ previous._next = batch._next;
+
+ // Add it to the front.
+ this._next = firstBatch;
+ firstBatch = internalRoot.firstBatch = this;
+ }
+
+ // Synchronously flush all the work up to this batch's expiration time.
+ this._defer = false;
+ flushRoot(internalRoot, expirationTime);
+
+ // Pop the batch from the list.
+ var next = this._next;
+ this._next = null;
+ firstBatch = internalRoot.firstBatch = next;
+
+ // Append the next earliest batch's children to the update queue.
+ if (firstBatch !== null && firstBatch._hasChildren) {
+ firstBatch.render(firstBatch._children);
+ }
+};
+ReactBatch.prototype._onComplete = function () {
+ if (this._didComplete) {
+ return;
+ }
+ this._didComplete = true;
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ return;
+ }
+ // TODO: Error handling.
+ for (var i = 0; i < callbacks.length; i++) {
+ var _callback = callbacks[i];
+ _callback();
+ }
+};
+
+function ReactWork() {
+ this._callbacks = null;
+ this._didCommit = false;
+ // TODO: Avoid need to bind by replacing callbacks in the update queue with
+ // list of Work objects.
+ this._onCommit = this._onCommit.bind(this);
+}
+ReactWork.prototype.then = function (onCommit) {
+ if (this._didCommit) {
+ onCommit();
+ return;
+ }
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ callbacks = this._callbacks = [];
+ }
+ callbacks.push(onCommit);
+};
+ReactWork.prototype._onCommit = function () {
+ if (this._didCommit) {
+ return;
+ }
+ this._didCommit = true;
+ var callbacks = this._callbacks;
+ if (callbacks === null) {
+ return;
+ }
+ // TODO: Error handling.
+ for (var i = 0; i < callbacks.length; i++) {
+ var _callback2 = callbacks[i];
+ !(typeof _callback2 === 'function') ? reactProdInvariant('191', _callback2) : void 0;
+ _callback2();
+ }
+};
+
+function ReactRoot(container, isConcurrent, hydrate) {
+ var root = createContainer(container, isConcurrent, hydrate);
+ this._internalRoot = root;
+}
+ReactRoot.prototype.render = function (children, callback) {
+ var root = this._internalRoot;
+ var work = new ReactWork();
+ callback = callback === undefined ? null : callback;
+ if (callback !== null) {
+ work.then(callback);
+ }
+ updateContainer(children, root, null, work._onCommit);
+ return work;
+};
+ReactRoot.prototype.unmount = function (callback) {
+ var root = this._internalRoot;
+ var work = new ReactWork();
+ callback = callback === undefined ? null : callback;
+ if (callback !== null) {
+ work.then(callback);
+ }
+ updateContainer(null, root, null, work._onCommit);
+ return work;
+};
+ReactRoot.prototype.legacy_renderSubtreeIntoContainer = function (parentComponent, children, callback) {
+ var root = this._internalRoot;
+ var work = new ReactWork();
+ callback = callback === undefined ? null : callback;
+ if (callback !== null) {
+ work.then(callback);
+ }
+ updateContainer(children, root, parentComponent, work._onCommit);
+ return work;
+};
+ReactRoot.prototype.createBatch = function () {
+ var batch = new ReactBatch(this);
+ var expirationTime = batch._expirationTime;
+
+ var internalRoot = this._internalRoot;
+ var firstBatch = internalRoot.firstBatch;
+ if (firstBatch === null) {
+ internalRoot.firstBatch = batch;
+ batch._next = null;
+ } else {
+ // Insert sorted by expiration time then insertion order
+ var insertAfter = null;
+ var insertBefore = firstBatch;
+ while (insertBefore !== null && insertBefore._expirationTime >= expirationTime) {
+ insertAfter = insertBefore;
+ insertBefore = insertBefore._next;
+ }
+ batch._next = insertBefore;
+ if (insertAfter !== null) {
+ insertAfter._next = batch;
+ }
+ }
+
+ return batch;
+};
+
+/**
+ * True if the supplied DOM node is a valid node element.
+ *
+ * @param {?DOMElement} node The candidate DOM node.
+ * @return {boolean} True if the DOM is a valid DOM node.
+ * @internal
+ */
+function isValidContainer(node) {
+ return !!(node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || node.nodeType === COMMENT_NODE && node.nodeValue === ' react-mount-point-unstable '));
+}
+
+function getReactRootElementInContainer(container) {
+ if (!container) {
+ return null;
+ }
+
+ if (container.nodeType === DOCUMENT_NODE) {
+ return container.documentElement;
+ } else {
+ return container.firstChild;
+ }
+}
+
+function shouldHydrateDueToLegacyHeuristic(container) {
+ var rootElement = getReactRootElementInContainer(container);
+ return !!(rootElement && rootElement.nodeType === ELEMENT_NODE && rootElement.hasAttribute(ROOT_ATTRIBUTE_NAME));
+}
+
+setBatchingImplementation(batchedUpdates$1, interactiveUpdates$1, flushInteractiveUpdates$1);
+
+function legacyCreateRootFromDOMContainer(container, forceHydrate) {
+ var shouldHydrate = forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
+ // First clear any existing content.
+ if (!shouldHydrate) {
+ var rootSibling = void 0;
+ while (rootSibling = container.lastChild) {
+ container.removeChild(rootSibling);
+ }
+ }
+ var isConcurrent = false;
+ return new ReactRoot(container, isConcurrent, shouldHydrate);
+}
+
+function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
+ var root = container._reactRootContainer;
+ if (!root) {
+ // Initial mount
+ root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);
+ if (typeof callback === 'function') {
+ var originalCallback = callback;
+ callback = function () {
+ var instance = getPublicRootInstance(root._internalRoot);
+ originalCallback.call(instance);
+ };
+ }
+ // Initial mount should not be batched.
+ unbatchedUpdates(function () {
+ if (parentComponent != null) {
+ root.legacy_renderSubtreeIntoContainer(parentComponent, children, callback);
+ } else {
+ root.render(children, callback);
+ }
+ });
+ } else {
+ if (typeof callback === 'function') {
+ var _originalCallback = callback;
+ callback = function () {
+ var instance = getPublicRootInstance(root._internalRoot);
+ _originalCallback.call(instance);
+ };
+ }
+ // Update
+ if (parentComponent != null) {
+ root.legacy_renderSubtreeIntoContainer(parentComponent, children, callback);
+ } else {
+ root.render(children, callback);
+ }
+ }
+ return getPublicRootInstance(root._internalRoot);
+}
+
+function createPortal$$1(children, container) {
+ var key = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
+
+ !isValidContainer(container) ? reactProdInvariant('200') : void 0;
+ // TODO: pass ReactDOM portal implementation as third argument
+ return createPortal$1(children, container, null, key);
+}
+
+var ReactDOM = {
+ createPortal: createPortal$$1,
+
+ findDOMNode: function (componentOrElement) {
+ if (componentOrElement == null) {
+ return null;
+ }
+ if (componentOrElement.nodeType === ELEMENT_NODE) {
+ return componentOrElement;
+ }
+ return findHostInstance(componentOrElement);
+ },
+ hydrate: function (element, container, callback) {
+ !isValidContainer(container) ? reactProdInvariant('200') : void 0;
+ return legacyRenderSubtreeIntoContainer(null, element, container, true, callback);
+ },
+ render: function (element, container, callback) {
+ !isValidContainer(container) ? reactProdInvariant('200') : void 0;
+ return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
+ },
+ unstable_renderSubtreeIntoContainer: function (parentComponent, element, containerNode, callback) {
+ !isValidContainer(containerNode) ? reactProdInvariant('200') : void 0;
+ !(parentComponent != null && has(parentComponent)) ? reactProdInvariant('38') : void 0;
+ return legacyRenderSubtreeIntoContainer(parentComponent, element, containerNode, false, callback);
+ },
+ unmountComponentAtNode: function (container) {
+ !isValidContainer(container) ? reactProdInvariant('40') : void 0;
+
+ if (container._reactRootContainer) {
+ unbatchedUpdates(function () {
+ legacyRenderSubtreeIntoContainer(null, null, container, false, function () {
+ container._reactRootContainer = null;
+ });
+ });
+ // If you call unmountComponentAtNode twice in quick succession, you'll
+ // get `true` twice. That's probably fine?
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+
+ // Temporary alias since we already shipped React 16 RC with it.
+ // TODO: remove in React 17.
+ unstable_createPortal: function () {
+ return createPortal$$1.apply(undefined, arguments);
+ },
+
+
+ unstable_batchedUpdates: batchedUpdates$1,
+
+ unstable_interactiveUpdates: interactiveUpdates$1,
+
+ flushSync: flushSync,
+
+ unstable_createRoot: createRoot,
+ unstable_flushControlled: flushControlled,
+
+ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
+ // Keep in sync with ReactDOMUnstableNativeDependencies.js
+ // and ReactTestUtils.js. This is an array for better minification.
+ Events: [getInstanceFromNode$1, getNodeFromInstance$1, getFiberCurrentPropsFromNode$1, injection.injectEventPluginsByName, eventNameDispatchConfigs, accumulateTwoPhaseDispatches, accumulateDirectDispatches, enqueueStateRestore, restoreStateIfNeeded, dispatchEvent, runEventsInBatch]
+ }
+};
+
+function createRoot(container, options) {
+ var functionName = enableStableConcurrentModeAPIs ? 'createRoot' : 'unstable_createRoot';
+ !isValidContainer(container) ? reactProdInvariant('299', functionName) : void 0;
+ var hydrate = options != null && options.hydrate === true;
+ return new ReactRoot(container, true, hydrate);
+}
+
+if (enableStableConcurrentModeAPIs) {
+ ReactDOM.createRoot = createRoot;
+ ReactDOM.unstable_createRoot = undefined;
+}
+
+var foundDevTools = injectIntoDevTools({
+ findFiberByHostInstance: getClosestInstanceFromNode,
+ bundleType: 0,
+ version: ReactVersion,
+ rendererPackageName: 'react-dom'
+});
+
+
+
+var ReactDOM$2 = ({
+ default: ReactDOM
+});
+
+var ReactDOM$3 = ( ReactDOM$2 && ReactDOM ) || ReactDOM$2;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var reactDom = ReactDOM$3.default || ReactDOM$3;
+
+return reactDom;
+
+})));
diff --git a/devtools/client/shared/vendor/react-prop-types-dev.js b/devtools/client/shared/vendor/react-prop-types-dev.js
new file mode 100644
index 0000000000..a99deba9bb
--- /dev/null
+++ b/devtools/client/shared/vendor/react-prop-types-dev.js
@@ -0,0 +1,1363 @@
+ /**
+ * react-prop-types v15.7.2
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.PropTypes = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var printWarning = function() {};
+
+if ("development" !== 'production') {
+ var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
+ var loggedTypeFailures = {};
+ var has = require('./lib/has');
+
+ printWarning = function(text) {
+ var message = 'Warning: ' + text;
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+ if ("development" !== 'production') {
+ for (var typeSpecName in typeSpecs) {
+ if (has(typeSpecs, typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ if (typeof typeSpecs[typeSpecName] !== 'function') {
+ var err = Error(
+ (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' +
+ 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' +
+ 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ }
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
+ } catch (ex) {
+ error = ex;
+ }
+ if (error && !(error instanceof Error)) {
+ printWarning(
+ (componentName || 'React class') + ': type specification of ' +
+ location + ' `' + typeSpecName + '` is invalid; the type checker ' +
+ 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' +
+ 'You may have forgotten to pass an argument to the type checker ' +
+ 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
+ 'shape all require an argument).'
+ );
+ }
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var stack = getStack ? getStack() : '';
+
+ printWarning(
+ 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '')
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Resets warning cache when testing.
+ *
+ * @private
+ */
+checkPropTypes.resetWarningCache = function() {
+ if ("development" !== 'production') {
+ loggedTypeFailures = {};
+ }
+}
+
+module.exports = checkPropTypes;
+
+},{"./lib/ReactPropTypesSecret":5,"./lib/has":6}],2:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
+
+function emptyFunction() {}
+function emptyFunctionWithReset() {}
+emptyFunctionWithReset.resetWarningCache = emptyFunction;
+
+module.exports = function() {
+ function shim(props, propName, componentName, location, propFullName, secret) {
+ if (secret === ReactPropTypesSecret) {
+ // It is still safe when called from React.
+ return;
+ }
+ var err = new Error(
+ 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +
+ 'Use PropTypes.checkPropTypes() to call them. ' +
+ 'Read more at http://fb.me/use-check-prop-types'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ };
+ shim.isRequired = shim;
+ function getShim() {
+ return shim;
+ };
+ // Important!
+ // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`.
+ var ReactPropTypes = {
+ array: shim,
+ bool: shim,
+ func: shim,
+ number: shim,
+ object: shim,
+ string: shim,
+ symbol: shim,
+
+ any: shim,
+ arrayOf: getShim,
+ element: shim,
+ elementType: shim,
+ instanceOf: getShim,
+ node: shim,
+ objectOf: getShim,
+ oneOf: getShim,
+ oneOfType: getShim,
+ shape: getShim,
+ exact: getShim,
+
+ checkPropTypes: emptyFunctionWithReset,
+ resetWarningCache: emptyFunction
+ };
+
+ ReactPropTypes.PropTypes = ReactPropTypes;
+
+ return ReactPropTypes;
+};
+
+},{"./lib/ReactPropTypesSecret":5}],3:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var ReactIs = require('react-is');
+var assign = require('object-assign');
+
+var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
+var has = require('./lib/has');
+var checkPropTypes = require('./checkPropTypes');
+
+var printWarning = function() {};
+
+if ("development" !== 'production') {
+ printWarning = function(text) {
+ var message = 'Warning: ' + text;
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+function emptyFunctionThatReturnsNull() {
+ return null;
+}
+
+module.exports = function(isValidElement, throwOnDirectAccess) {
+ /* global Symbol */
+ var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+ var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
+
+ /**
+ * Returns the iterator method function contained on the iterable object.
+ *
+ * Be sure to invoke the function with the iterable as context:
+ *
+ * var iteratorFn = getIteratorFn(myIterable);
+ * if (iteratorFn) {
+ * var iterator = iteratorFn.call(myIterable);
+ * ...
+ * }
+ *
+ * @param {?object} maybeIterable
+ * @return {?function}
+ */
+ function getIteratorFn(maybeIterable) {
+ var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
+ if (typeof iteratorFn === 'function') {
+ return iteratorFn;
+ }
+ }
+
+ /**
+ * Collection of methods that allow declaration and validation of props that are
+ * supplied to React components. Example usage:
+ *
+ * var Props = require('ReactPropTypes');
+ * var MyArticle = React.createClass({
+ * propTypes: {
+ * // An optional string prop named "description".
+ * description: Props.string,
+ *
+ * // A required enum prop named "category".
+ * category: Props.oneOf(['News','Photos']).isRequired,
+ *
+ * // A prop named "dialog" that requires an instance of Dialog.
+ * dialog: Props.instanceOf(Dialog).isRequired
+ * },
+ * render: function() { ... }
+ * });
+ *
+ * A more formal specification of how these methods are used:
+ *
+ * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
+ * decl := ReactPropTypes.{type}(.isRequired)?
+ *
+ * Each and every declaration produces a function with the same signature. This
+ * allows the creation of custom validation functions. For example:
+ *
+ * var MyLink = React.createClass({
+ * propTypes: {
+ * // An optional string or URI prop named "href".
+ * href: function(props, propName, componentName) {
+ * var propValue = props[propName];
+ * if (propValue != null && typeof propValue !== 'string' &&
+ * !(propValue instanceof URI)) {
+ * return new Error(
+ * 'Expected a string or an URI for ' + propName + ' in ' +
+ * componentName
+ * );
+ * }
+ * }
+ * },
+ * render: function() {...}
+ * });
+ *
+ * @internal
+ */
+
+ var ANONYMOUS = '<<anonymous>>';
+
+ // Important!
+ // Keep this list in sync with production version in `./factoryWithThrowingShims.js`.
+ var ReactPropTypes = {
+ array: createPrimitiveTypeChecker('array'),
+ bool: createPrimitiveTypeChecker('boolean'),
+ func: createPrimitiveTypeChecker('function'),
+ number: createPrimitiveTypeChecker('number'),
+ object: createPrimitiveTypeChecker('object'),
+ string: createPrimitiveTypeChecker('string'),
+ symbol: createPrimitiveTypeChecker('symbol'),
+
+ any: createAnyTypeChecker(),
+ arrayOf: createArrayOfTypeChecker,
+ element: createElementTypeChecker(),
+ elementType: createElementTypeTypeChecker(),
+ instanceOf: createInstanceTypeChecker,
+ node: createNodeChecker(),
+ objectOf: createObjectOfTypeChecker,
+ oneOf: createEnumTypeChecker,
+ oneOfType: createUnionTypeChecker,
+ shape: createShapeTypeChecker,
+ exact: createStrictShapeTypeChecker,
+ };
+
+ /**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+ /*eslint-disable no-self-compare*/
+ function is(x, y) {
+ // SameValue algorithm
+ if (x === y) {
+ // Steps 1-5, 7-10
+ // Steps 6.b-6.e: +0 != -0
+ return x !== 0 || 1 / x === 1 / y;
+ } else {
+ // Step 6.a: NaN == NaN
+ return x !== x && y !== y;
+ }
+ }
+ /*eslint-enable no-self-compare*/
+
+ /**
+ * We use an Error-like object for backward compatibility as people may call
+ * PropTypes directly and inspect their output. However, we don't use real
+ * Errors anymore. We don't inspect their stack anyway, and creating them
+ * is prohibitively expensive if they are created too often, such as what
+ * happens in oneOfType() for any type before the one that matched.
+ */
+ function PropTypeError(message, data) {
+ this.message = message;
+ this.data = data && typeof data === 'object' ? data: {};
+ this.stack = '';
+ }
+ // Make `instanceof Error` still work for returned errors.
+ PropTypeError.prototype = Error.prototype;
+
+ function createChainableTypeChecker(validate) {
+ if ("development" !== 'production') {
+ var manualPropTypeCallCache = {};
+ var manualPropTypeWarningCount = 0;
+ }
+ function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
+ componentName = componentName || ANONYMOUS;
+ propFullName = propFullName || propName;
+
+ if (secret !== ReactPropTypesSecret) {
+ if (throwOnDirectAccess) {
+ // New behavior only for users of `prop-types` package
+ var err = new Error(
+ 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +
+ 'Use `PropTypes.checkPropTypes()` to call them. ' +
+ 'Read more at http://fb.me/use-check-prop-types'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ } else if ("development" !== 'production' && typeof console !== 'undefined') {
+ // Old behavior for people using React.PropTypes
+ var cacheKey = componentName + ':' + propName;
+ if (
+ !manualPropTypeCallCache[cacheKey] &&
+ // Avoid spamming the console because they are often not actionable except for lib authors
+ manualPropTypeWarningCount < 3
+ ) {
+ printWarning(
+ 'You are manually calling a React.PropTypes validation ' +
+ 'function for the `' + propFullName + '` prop on `' + componentName + '`. This is deprecated ' +
+ 'and will throw in the standalone `prop-types` package. ' +
+ 'You may be seeing this warning due to a third-party PropTypes ' +
+ 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.'
+ );
+ manualPropTypeCallCache[cacheKey] = true;
+ manualPropTypeWarningCount++;
+ }
+ }
+ }
+ if (props[propName] == null) {
+ if (isRequired) {
+ if (props[propName] === null) {
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));
+ }
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));
+ }
+ return null;
+ } else {
+ return validate(props, propName, componentName, location, propFullName);
+ }
+ }
+
+ var chainedCheckType = checkType.bind(null, false);
+ chainedCheckType.isRequired = checkType.bind(null, true);
+
+ return chainedCheckType;
+ }
+
+ function createPrimitiveTypeChecker(expectedType) {
+ function validate(props, propName, componentName, location, propFullName, secret) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== expectedType) {
+ // `propValue` being instance of, say, date/regexp, pass the 'object'
+ // check, but we can offer a more precise error message here rather than
+ // 'of type `object`'.
+ var preciseType = getPreciseType(propValue);
+
+ return new PropTypeError(
+ 'Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'),
+ {expectedType: expectedType}
+ );
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createAnyTypeChecker() {
+ return createChainableTypeChecker(emptyFunctionThatReturnsNull);
+ }
+
+ function createArrayOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.');
+ }
+ var propValue = props[propName];
+ if (!Array.isArray(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));
+ }
+ for (var i = 0; i < propValue.length; i++) {
+ var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createElementTypeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ if (!isValidElement(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createElementTypeTypeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ if (!ReactIs.isValidElementType(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement type.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createInstanceTypeChecker(expectedClass) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!(props[propName] instanceof expectedClass)) {
+ var expectedClassName = expectedClass.name || ANONYMOUS;
+ var actualClassName = getClassName(props[propName]);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createEnumTypeChecker(expectedValues) {
+ if (!Array.isArray(expectedValues)) {
+ if ("development" !== 'production') {
+ if (arguments.length > 1) {
+ printWarning(
+ 'Invalid arguments supplied to oneOf, expected an array, got ' + arguments.length + ' arguments. ' +
+ 'A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).'
+ );
+ } else {
+ printWarning('Invalid argument supplied to oneOf, expected an array.');
+ }
+ }
+ return emptyFunctionThatReturnsNull;
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ for (var i = 0; i < expectedValues.length; i++) {
+ if (is(propValue, expectedValues[i])) {
+ return null;
+ }
+ }
+
+ var valuesString = JSON.stringify(expectedValues, function replacer(key, value) {
+ var type = getPreciseType(value);
+ if (type === 'symbol') {
+ return String(value);
+ }
+ return value;
+ });
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + String(propValue) + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createObjectOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.');
+ }
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));
+ }
+ for (var key in propValue) {
+ if (has(propValue, key)) {
+ var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createUnionTypeChecker(arrayOfTypeCheckers) {
+ if (!Array.isArray(arrayOfTypeCheckers)) {
+ "development" !== 'production' ? printWarning('Invalid argument supplied to oneOfType, expected an instance of array.') : void 0;
+ return emptyFunctionThatReturnsNull;
+ }
+
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ if (typeof checker !== 'function') {
+ printWarning(
+ 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' +
+ 'received ' + getPostfixForTypeWarning(checker) + ' at index ' + i + '.'
+ );
+ return emptyFunctionThatReturnsNull;
+ }
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ var expectedTypes = [];
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ var checkerResult = checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret);
+ if (checkerResult == null) {
+ return null;
+ }
+ if (checkerResult.data.hasOwnProperty('expectedType')) {
+ expectedTypes.push(checkerResult.data.expectedType);
+ }
+ }
+ var expectedTypesMessage = (expectedTypes.length > 0) ? ', expected one of type [' + expectedTypes.join(', ') + ']': '';
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`' + expectedTypesMessage + '.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createNodeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!isNode(props[propName])) {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function invalidValidatorError(componentName, location, propFullName, key, type) {
+ return new PropTypeError(
+ (componentName || 'React class') + ': ' + location + ' type `' + propFullName + '.' + key + '` is invalid; ' +
+ 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.'
+ );
+ }
+
+ function createShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ for (var key in shapeTypes) {
+ var checker = shapeTypes[key];
+ if (typeof checker !== 'function') {
+ return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createStrictShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ // We need to check all keys in case some are required but missing from
+ // props.
+ var allKeys = assign({}, props[propName], shapeTypes);
+ for (var key in allKeys) {
+ var checker = shapeTypes[key];
+ if (has(shapeTypes, key) && typeof checker !== 'function') {
+ return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
+ }
+ if (!checker) {
+ return new PropTypeError(
+ 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' +
+ '\nBad object: ' + JSON.stringify(props[propName], null, ' ') +
+ '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ')
+ );
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+
+ return createChainableTypeChecker(validate);
+ }
+
+ function isNode(propValue) {
+ switch (typeof propValue) {
+ case 'number':
+ case 'string':
+ case 'undefined':
+ return true;
+ case 'boolean':
+ return !propValue;
+ case 'object':
+ if (Array.isArray(propValue)) {
+ return propValue.every(isNode);
+ }
+ if (propValue === null || isValidElement(propValue)) {
+ return true;
+ }
+
+ var iteratorFn = getIteratorFn(propValue);
+ if (iteratorFn) {
+ var iterator = iteratorFn.call(propValue);
+ var step;
+ if (iteratorFn !== propValue.entries) {
+ while (!(step = iterator.next()).done) {
+ if (!isNode(step.value)) {
+ return false;
+ }
+ }
+ } else {
+ // Iterator will provide entry [k,v] tuples rather than values.
+ while (!(step = iterator.next()).done) {
+ var entry = step.value;
+ if (entry) {
+ if (!isNode(entry[1])) {
+ return false;
+ }
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ function isSymbol(propType, propValue) {
+ // Native Symbol.
+ if (propType === 'symbol') {
+ return true;
+ }
+
+ // falsy value can't be a Symbol
+ if (!propValue) {
+ return false;
+ }
+
+ // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol'
+ if (propValue['@@toStringTag'] === 'Symbol') {
+ return true;
+ }
+
+ // Fallback for non-spec compliant Symbols which are polyfilled.
+ if (typeof Symbol === 'function' && propValue instanceof Symbol) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Equivalent of `typeof` but with special handling for array and regexp.
+ function getPropType(propValue) {
+ var propType = typeof propValue;
+ if (Array.isArray(propValue)) {
+ return 'array';
+ }
+ if (propValue instanceof RegExp) {
+ // Old webkits (at least until Android 4.0) return 'function' rather than
+ // 'object' for typeof a RegExp. We'll normalize this here so that /bla/
+ // passes PropTypes.object.
+ return 'object';
+ }
+ if (isSymbol(propType, propValue)) {
+ return 'symbol';
+ }
+ return propType;
+ }
+
+ // This handles more types than `getPropType`. Only used for error messages.
+ // See `createPrimitiveTypeChecker`.
+ function getPreciseType(propValue) {
+ if (typeof propValue === 'undefined' || propValue === null) {
+ return '' + propValue;
+ }
+ var propType = getPropType(propValue);
+ if (propType === 'object') {
+ if (propValue instanceof Date) {
+ return 'date';
+ } else if (propValue instanceof RegExp) {
+ return 'regexp';
+ }
+ }
+ return propType;
+ }
+
+ // Returns a string that is postfixed to a warning about an invalid type.
+ // For example, "undefined" or "of type array"
+ function getPostfixForTypeWarning(value) {
+ var type = getPreciseType(value);
+ switch (type) {
+ case 'array':
+ case 'object':
+ return 'an ' + type;
+ case 'boolean':
+ case 'date':
+ case 'regexp':
+ return 'a ' + type;
+ default:
+ return type;
+ }
+ }
+
+ // Returns class name of the object, if any.
+ function getClassName(propValue) {
+ if (!propValue.constructor || !propValue.constructor.name) {
+ return ANONYMOUS;
+ }
+ return propValue.constructor.name;
+ }
+
+ ReactPropTypes.checkPropTypes = checkPropTypes;
+ ReactPropTypes.resetWarningCache = checkPropTypes.resetWarningCache;
+ ReactPropTypes.PropTypes = ReactPropTypes;
+
+ return ReactPropTypes;
+};
+
+},{"./checkPropTypes":1,"./lib/ReactPropTypesSecret":5,"./lib/has":6,"object-assign":7,"react-is":10}],4:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+if ("development" !== 'production') {
+ var ReactIs = require('react-is');
+
+ // By explicitly using `prop-types` you are opting into new development behavior.
+ // http://fb.me/prop-types-in-prod
+ var throwOnDirectAccess = true;
+ module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess);
+} else {
+ // By explicitly using `prop-types` you are opting into new production behavior.
+ // http://fb.me/prop-types-in-prod
+ module.exports = require('./factoryWithThrowingShims')();
+}
+
+},{"./factoryWithThrowingShims":2,"./factoryWithTypeCheckers":3,"react-is":10}],5:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+
+module.exports = ReactPropTypesSecret;
+
+},{}],6:[function(require,module,exports){
+module.exports = Function.call.bind(Object.prototype.hasOwnProperty);
+
+},{}],7:[function(require,module,exports){
+/*
+object-assign
+(c) Sindre Sorhus
+@license MIT
+*/
+
+'use strict';
+/* eslint-disable no-unused-vars */
+var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+}
+
+function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+}
+
+module.exports = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+};
+
+},{}],8:[function(require,module,exports){
+(function (process){
+/** @license React v16.8.6
+ * react-is.development.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+
+
+if (process.env.NODE_ENV !== "production") {
+ (function() {
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf;
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+function isValidElementType(type) {
+ return typeof type === 'string' || typeof type === 'function' ||
+ // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
+ type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || typeof type === 'object' && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE);
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var lowPriorityWarning = function () {};
+
+{
+ var printWarning = function (format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.warn(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ lowPriorityWarning = function (condition, format) {
+ if (format === undefined) {
+ throw new Error('`lowPriorityWarning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var lowPriorityWarning$1 = lowPriorityWarning;
+
+function typeOf(object) {
+ if (typeof object === 'object' && object !== null) {
+ var $$typeof = object.$$typeof;
+ switch ($$typeof) {
+ case REACT_ELEMENT_TYPE:
+ var type = object.type;
+
+ switch (type) {
+ case REACT_ASYNC_MODE_TYPE:
+ case REACT_CONCURRENT_MODE_TYPE:
+ case REACT_FRAGMENT_TYPE:
+ case REACT_PROFILER_TYPE:
+ case REACT_STRICT_MODE_TYPE:
+ case REACT_SUSPENSE_TYPE:
+ return type;
+ default:
+ var $$typeofType = type && type.$$typeof;
+
+ switch ($$typeofType) {
+ case REACT_CONTEXT_TYPE:
+ case REACT_FORWARD_REF_TYPE:
+ case REACT_PROVIDER_TYPE:
+ return $$typeofType;
+ default:
+ return $$typeof;
+ }
+ }
+ case REACT_LAZY_TYPE:
+ case REACT_MEMO_TYPE:
+ case REACT_PORTAL_TYPE:
+ return $$typeof;
+ }
+ }
+
+ return undefined;
+}
+
+// AsyncMode is deprecated along with isAsyncMode
+var AsyncMode = REACT_ASYNC_MODE_TYPE;
+var ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
+var ContextConsumer = REACT_CONTEXT_TYPE;
+var ContextProvider = REACT_PROVIDER_TYPE;
+var Element = REACT_ELEMENT_TYPE;
+var ForwardRef = REACT_FORWARD_REF_TYPE;
+var Fragment = REACT_FRAGMENT_TYPE;
+var Lazy = REACT_LAZY_TYPE;
+var Memo = REACT_MEMO_TYPE;
+var Portal = REACT_PORTAL_TYPE;
+var Profiler = REACT_PROFILER_TYPE;
+var StrictMode = REACT_STRICT_MODE_TYPE;
+var Suspense = REACT_SUSPENSE_TYPE;
+
+var hasWarnedAboutDeprecatedIsAsyncMode = false;
+
+// AsyncMode should be deprecated
+function isAsyncMode(object) {
+ {
+ if (!hasWarnedAboutDeprecatedIsAsyncMode) {
+ hasWarnedAboutDeprecatedIsAsyncMode = true;
+ lowPriorityWarning$1(false, 'The ReactIs.isAsyncMode() alias has been deprecated, ' + 'and will be removed in React 17+. Update your code to use ' + 'ReactIs.isConcurrentMode() instead. It has the exact same API.');
+ }
+ }
+ return isConcurrentMode(object) || typeOf(object) === REACT_ASYNC_MODE_TYPE;
+}
+function isConcurrentMode(object) {
+ return typeOf(object) === REACT_CONCURRENT_MODE_TYPE;
+}
+function isContextConsumer(object) {
+ return typeOf(object) === REACT_CONTEXT_TYPE;
+}
+function isContextProvider(object) {
+ return typeOf(object) === REACT_PROVIDER_TYPE;
+}
+function isElement(object) {
+ return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
+}
+function isForwardRef(object) {
+ return typeOf(object) === REACT_FORWARD_REF_TYPE;
+}
+function isFragment(object) {
+ return typeOf(object) === REACT_FRAGMENT_TYPE;
+}
+function isLazy(object) {
+ return typeOf(object) === REACT_LAZY_TYPE;
+}
+function isMemo(object) {
+ return typeOf(object) === REACT_MEMO_TYPE;
+}
+function isPortal(object) {
+ return typeOf(object) === REACT_PORTAL_TYPE;
+}
+function isProfiler(object) {
+ return typeOf(object) === REACT_PROFILER_TYPE;
+}
+function isStrictMode(object) {
+ return typeOf(object) === REACT_STRICT_MODE_TYPE;
+}
+function isSuspense(object) {
+ return typeOf(object) === REACT_SUSPENSE_TYPE;
+}
+
+exports.typeOf = typeOf;
+exports.AsyncMode = AsyncMode;
+exports.ConcurrentMode = ConcurrentMode;
+exports.ContextConsumer = ContextConsumer;
+exports.ContextProvider = ContextProvider;
+exports.Element = Element;
+exports.ForwardRef = ForwardRef;
+exports.Fragment = Fragment;
+exports.Lazy = Lazy;
+exports.Memo = Memo;
+exports.Portal = Portal;
+exports.Profiler = Profiler;
+exports.StrictMode = StrictMode;
+exports.Suspense = Suspense;
+exports.isValidElementType = isValidElementType;
+exports.isAsyncMode = isAsyncMode;
+exports.isConcurrentMode = isConcurrentMode;
+exports.isContextConsumer = isContextConsumer;
+exports.isContextProvider = isContextProvider;
+exports.isElement = isElement;
+exports.isForwardRef = isForwardRef;
+exports.isFragment = isFragment;
+exports.isLazy = isLazy;
+exports.isMemo = isMemo;
+exports.isPortal = isPortal;
+exports.isProfiler = isProfiler;
+exports.isStrictMode = isStrictMode;
+exports.isSuspense = isSuspense;
+ })();
+}
+
+}).call(this,require('_process'))
+},{"_process":11}],9:[function(require,module,exports){
+/** @license React v16.8.6
+ * react-is.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';Object.defineProperty(exports,"__esModule",{value:!0});
+var b="function"===typeof Symbol&&Symbol.for,c=b?Symbol.for("react.element"):60103,d=b?Symbol.for("react.portal"):60106,e=b?Symbol.for("react.fragment"):60107,f=b?Symbol.for("react.strict_mode"):60108,g=b?Symbol.for("react.profiler"):60114,h=b?Symbol.for("react.provider"):60109,k=b?Symbol.for("react.context"):60110,l=b?Symbol.for("react.async_mode"):60111,m=b?Symbol.for("react.concurrent_mode"):60111,n=b?Symbol.for("react.forward_ref"):60112,p=b?Symbol.for("react.suspense"):60113,q=b?Symbol.for("react.memo"):
+60115,r=b?Symbol.for("react.lazy"):60116;function t(a){if("object"===typeof a&&null!==a){var u=a.$$typeof;switch(u){case c:switch(a=a.type,a){case l:case m:case e:case g:case f:case p:return a;default:switch(a=a&&a.$$typeof,a){case k:case n:case h:return a;default:return u}}case r:case q:case d:return u}}}function v(a){return t(a)===m}exports.typeOf=t;exports.AsyncMode=l;exports.ConcurrentMode=m;exports.ContextConsumer=k;exports.ContextProvider=h;exports.Element=c;exports.ForwardRef=n;
+exports.Fragment=e;exports.Lazy=r;exports.Memo=q;exports.Portal=d;exports.Profiler=g;exports.StrictMode=f;exports.Suspense=p;exports.isValidElementType=function(a){return"string"===typeof a||"function"===typeof a||a===e||a===m||a===g||a===f||a===p||"object"===typeof a&&null!==a&&(a.$$typeof===r||a.$$typeof===q||a.$$typeof===h||a.$$typeof===k||a.$$typeof===n)};exports.isAsyncMode=function(a){return v(a)||t(a)===l};exports.isConcurrentMode=v;exports.isContextConsumer=function(a){return t(a)===k};
+exports.isContextProvider=function(a){return t(a)===h};exports.isElement=function(a){return"object"===typeof a&&null!==a&&a.$$typeof===c};exports.isForwardRef=function(a){return t(a)===n};exports.isFragment=function(a){return t(a)===e};exports.isLazy=function(a){return t(a)===r};exports.isMemo=function(a){return t(a)===q};exports.isPortal=function(a){return t(a)===d};exports.isProfiler=function(a){return t(a)===g};exports.isStrictMode=function(a){return t(a)===f};
+exports.isSuspense=function(a){return t(a)===p};
+
+},{}],10:[function(require,module,exports){
+(function (process){
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-is.production.min.js');
+} else {
+ module.exports = require('./cjs/react-is.development.js');
+}
+
+}).call(this,require('_process'))
+},{"./cjs/react-is.development.js":8,"./cjs/react-is.production.min.js":9,"_process":11}],11:[function(require,module,exports){
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things. But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals. It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+ throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+ throw new Error('clearTimeout has not been defined');
+}
+(function () {
+ try {
+ if (typeof setTimeout === 'function') {
+ cachedSetTimeout = setTimeout;
+ } else {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ } catch (e) {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ try {
+ if (typeof clearTimeout === 'function') {
+ cachedClearTimeout = clearTimeout;
+ } else {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+ } catch (e) {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+} ())
+function runTimeout(fun) {
+ if (cachedSetTimeout === setTimeout) {
+ //normal enviroments in sane situations
+ return setTimeout(fun, 0);
+ }
+ // if setTimeout wasn't available but was latter defined
+ if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+ cachedSetTimeout = setTimeout;
+ return setTimeout(fun, 0);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedSetTimeout(fun, 0);
+ } catch(e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedSetTimeout.call(null, fun, 0);
+ } catch(e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+ return cachedSetTimeout.call(this, fun, 0);
+ }
+ }
+
+
+}
+function runClearTimeout(marker) {
+ if (cachedClearTimeout === clearTimeout) {
+ //normal enviroments in sane situations
+ return clearTimeout(marker);
+ }
+ // if clearTimeout wasn't available but was latter defined
+ if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+ cachedClearTimeout = clearTimeout;
+ return clearTimeout(marker);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedClearTimeout(marker);
+ } catch (e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedClearTimeout.call(null, marker);
+ } catch (e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+ // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+ return cachedClearTimeout.call(this, marker);
+ }
+ }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ if (!draining || !currentQueue) {
+ return;
+ }
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = runTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
+ }
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ runTimeout(drainQueue);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}]},{},[4])(4)
+});
diff --git a/devtools/client/shared/vendor/react-prop-types.js b/devtools/client/shared/vendor/react-prop-types.js
new file mode 100644
index 0000000000..fd59b14fb4
--- /dev/null
+++ b/devtools/client/shared/vendor/react-prop-types.js
@@ -0,0 +1,1363 @@
+ /**
+ * react-prop-types v15.7.2
+ */
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.PropTypes = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var printWarning = function() {};
+
+if ("production" !== 'production') {
+ var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
+ var loggedTypeFailures = {};
+ var has = require('./lib/has');
+
+ printWarning = function(text) {
+ var message = 'Warning: ' + text;
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+ if ("production" !== 'production') {
+ for (var typeSpecName in typeSpecs) {
+ if (has(typeSpecs, typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ if (typeof typeSpecs[typeSpecName] !== 'function') {
+ var err = Error(
+ (componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' +
+ 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' +
+ 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ }
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
+ } catch (ex) {
+ error = ex;
+ }
+ if (error && !(error instanceof Error)) {
+ printWarning(
+ (componentName || 'React class') + ': type specification of ' +
+ location + ' `' + typeSpecName + '` is invalid; the type checker ' +
+ 'function must return `null` or an `Error` but returned a ' + typeof error + '. ' +
+ 'You may have forgotten to pass an argument to the type checker ' +
+ 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
+ 'shape all require an argument).'
+ );
+ }
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var stack = getStack ? getStack() : '';
+
+ printWarning(
+ 'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '')
+ );
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Resets warning cache when testing.
+ *
+ * @private
+ */
+checkPropTypes.resetWarningCache = function() {
+ if ("production" !== 'production') {
+ loggedTypeFailures = {};
+ }
+}
+
+module.exports = checkPropTypes;
+
+},{"./lib/ReactPropTypesSecret":5,"./lib/has":6}],2:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
+
+function emptyFunction() {}
+function emptyFunctionWithReset() {}
+emptyFunctionWithReset.resetWarningCache = emptyFunction;
+
+module.exports = function() {
+ function shim(props, propName, componentName, location, propFullName, secret) {
+ if (secret === ReactPropTypesSecret) {
+ // It is still safe when called from React.
+ return;
+ }
+ var err = new Error(
+ 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +
+ 'Use PropTypes.checkPropTypes() to call them. ' +
+ 'Read more at http://fb.me/use-check-prop-types'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ };
+ shim.isRequired = shim;
+ function getShim() {
+ return shim;
+ };
+ // Important!
+ // Keep this list in sync with production version in `./factoryWithTypeCheckers.js`.
+ var ReactPropTypes = {
+ array: shim,
+ bool: shim,
+ func: shim,
+ number: shim,
+ object: shim,
+ string: shim,
+ symbol: shim,
+
+ any: shim,
+ arrayOf: getShim,
+ element: shim,
+ elementType: shim,
+ instanceOf: getShim,
+ node: shim,
+ objectOf: getShim,
+ oneOf: getShim,
+ oneOfType: getShim,
+ shape: getShim,
+ exact: getShim,
+
+ checkPropTypes: emptyFunctionWithReset,
+ resetWarningCache: emptyFunction
+ };
+
+ ReactPropTypes.PropTypes = ReactPropTypes;
+
+ return ReactPropTypes;
+};
+
+},{"./lib/ReactPropTypesSecret":5}],3:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var ReactIs = require('react-is');
+var assign = require('object-assign');
+
+var ReactPropTypesSecret = require('./lib/ReactPropTypesSecret');
+var has = require('./lib/has');
+var checkPropTypes = require('./checkPropTypes');
+
+var printWarning = function() {};
+
+if ("production" !== 'production') {
+ printWarning = function(text) {
+ var message = 'Warning: ' + text;
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+}
+
+function emptyFunctionThatReturnsNull() {
+ return null;
+}
+
+module.exports = function(isValidElement, throwOnDirectAccess) {
+ /* global Symbol */
+ var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+ var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
+
+ /**
+ * Returns the iterator method function contained on the iterable object.
+ *
+ * Be sure to invoke the function with the iterable as context:
+ *
+ * var iteratorFn = getIteratorFn(myIterable);
+ * if (iteratorFn) {
+ * var iterator = iteratorFn.call(myIterable);
+ * ...
+ * }
+ *
+ * @param {?object} maybeIterable
+ * @return {?function}
+ */
+ function getIteratorFn(maybeIterable) {
+ var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
+ if (typeof iteratorFn === 'function') {
+ return iteratorFn;
+ }
+ }
+
+ /**
+ * Collection of methods that allow declaration and validation of props that are
+ * supplied to React components. Example usage:
+ *
+ * var Props = require('ReactPropTypes');
+ * var MyArticle = React.createClass({
+ * propTypes: {
+ * // An optional string prop named "description".
+ * description: Props.string,
+ *
+ * // A required enum prop named "category".
+ * category: Props.oneOf(['News','Photos']).isRequired,
+ *
+ * // A prop named "dialog" that requires an instance of Dialog.
+ * dialog: Props.instanceOf(Dialog).isRequired
+ * },
+ * render: function() { ... }
+ * });
+ *
+ * A more formal specification of how these methods are used:
+ *
+ * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
+ * decl := ReactPropTypes.{type}(.isRequired)?
+ *
+ * Each and every declaration produces a function with the same signature. This
+ * allows the creation of custom validation functions. For example:
+ *
+ * var MyLink = React.createClass({
+ * propTypes: {
+ * // An optional string or URI prop named "href".
+ * href: function(props, propName, componentName) {
+ * var propValue = props[propName];
+ * if (propValue != null && typeof propValue !== 'string' &&
+ * !(propValue instanceof URI)) {
+ * return new Error(
+ * 'Expected a string or an URI for ' + propName + ' in ' +
+ * componentName
+ * );
+ * }
+ * }
+ * },
+ * render: function() {...}
+ * });
+ *
+ * @internal
+ */
+
+ var ANONYMOUS = '<<anonymous>>';
+
+ // Important!
+ // Keep this list in sync with production version in `./factoryWithThrowingShims.js`.
+ var ReactPropTypes = {
+ array: createPrimitiveTypeChecker('array'),
+ bool: createPrimitiveTypeChecker('boolean'),
+ func: createPrimitiveTypeChecker('function'),
+ number: createPrimitiveTypeChecker('number'),
+ object: createPrimitiveTypeChecker('object'),
+ string: createPrimitiveTypeChecker('string'),
+ symbol: createPrimitiveTypeChecker('symbol'),
+
+ any: createAnyTypeChecker(),
+ arrayOf: createArrayOfTypeChecker,
+ element: createElementTypeChecker(),
+ elementType: createElementTypeTypeChecker(),
+ instanceOf: createInstanceTypeChecker,
+ node: createNodeChecker(),
+ objectOf: createObjectOfTypeChecker,
+ oneOf: createEnumTypeChecker,
+ oneOfType: createUnionTypeChecker,
+ shape: createShapeTypeChecker,
+ exact: createStrictShapeTypeChecker,
+ };
+
+ /**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+ /*eslint-disable no-self-compare*/
+ function is(x, y) {
+ // SameValue algorithm
+ if (x === y) {
+ // Steps 1-5, 7-10
+ // Steps 6.b-6.e: +0 != -0
+ return x !== 0 || 1 / x === 1 / y;
+ } else {
+ // Step 6.a: NaN == NaN
+ return x !== x && y !== y;
+ }
+ }
+ /*eslint-enable no-self-compare*/
+
+ /**
+ * We use an Error-like object for backward compatibility as people may call
+ * PropTypes directly and inspect their output. However, we don't use real
+ * Errors anymore. We don't inspect their stack anyway, and creating them
+ * is prohibitively expensive if they are created too often, such as what
+ * happens in oneOfType() for any type before the one that matched.
+ */
+ function PropTypeError(message, data) {
+ this.message = message;
+ this.data = data && typeof data === 'object' ? data: {};
+ this.stack = '';
+ }
+ // Make `instanceof Error` still work for returned errors.
+ PropTypeError.prototype = Error.prototype;
+
+ function createChainableTypeChecker(validate) {
+ if ("production" !== 'production') {
+ var manualPropTypeCallCache = {};
+ var manualPropTypeWarningCount = 0;
+ }
+ function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
+ componentName = componentName || ANONYMOUS;
+ propFullName = propFullName || propName;
+
+ if (secret !== ReactPropTypesSecret) {
+ if (throwOnDirectAccess) {
+ // New behavior only for users of `prop-types` package
+ var err = new Error(
+ 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +
+ 'Use `PropTypes.checkPropTypes()` to call them. ' +
+ 'Read more at http://fb.me/use-check-prop-types'
+ );
+ err.name = 'Invariant Violation';
+ throw err;
+ } else if ("production" !== 'production' && typeof console !== 'undefined') {
+ // Old behavior for people using React.PropTypes
+ var cacheKey = componentName + ':' + propName;
+ if (
+ !manualPropTypeCallCache[cacheKey] &&
+ // Avoid spamming the console because they are often not actionable except for lib authors
+ manualPropTypeWarningCount < 3
+ ) {
+ printWarning(
+ 'You are manually calling a React.PropTypes validation ' +
+ 'function for the `' + propFullName + '` prop on `' + componentName + '`. This is deprecated ' +
+ 'and will throw in the standalone `prop-types` package. ' +
+ 'You may be seeing this warning due to a third-party PropTypes ' +
+ 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.'
+ );
+ manualPropTypeCallCache[cacheKey] = true;
+ manualPropTypeWarningCount++;
+ }
+ }
+ }
+ if (props[propName] == null) {
+ if (isRequired) {
+ if (props[propName] === null) {
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));
+ }
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));
+ }
+ return null;
+ } else {
+ return validate(props, propName, componentName, location, propFullName);
+ }
+ }
+
+ var chainedCheckType = checkType.bind(null, false);
+ chainedCheckType.isRequired = checkType.bind(null, true);
+
+ return chainedCheckType;
+ }
+
+ function createPrimitiveTypeChecker(expectedType) {
+ function validate(props, propName, componentName, location, propFullName, secret) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== expectedType) {
+ // `propValue` being instance of, say, date/regexp, pass the 'object'
+ // check, but we can offer a more precise error message here rather than
+ // 'of type `object`'.
+ var preciseType = getPreciseType(propValue);
+
+ return new PropTypeError(
+ 'Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'),
+ {expectedType: expectedType}
+ );
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createAnyTypeChecker() {
+ return createChainableTypeChecker(emptyFunctionThatReturnsNull);
+ }
+
+ function createArrayOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.');
+ }
+ var propValue = props[propName];
+ if (!Array.isArray(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));
+ }
+ for (var i = 0; i < propValue.length; i++) {
+ var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createElementTypeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ if (!isValidElement(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createElementTypeTypeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ if (!ReactIs.isValidElementType(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement type.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createInstanceTypeChecker(expectedClass) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!(props[propName] instanceof expectedClass)) {
+ var expectedClassName = expectedClass.name || ANONYMOUS;
+ var actualClassName = getClassName(props[propName]);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createEnumTypeChecker(expectedValues) {
+ if (!Array.isArray(expectedValues)) {
+ if ("production" !== 'production') {
+ if (arguments.length > 1) {
+ printWarning(
+ 'Invalid arguments supplied to oneOf, expected an array, got ' + arguments.length + ' arguments. ' +
+ 'A common mistake is to write oneOf(x, y, z) instead of oneOf([x, y, z]).'
+ );
+ } else {
+ printWarning('Invalid argument supplied to oneOf, expected an array.');
+ }
+ }
+ return emptyFunctionThatReturnsNull;
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ for (var i = 0; i < expectedValues.length; i++) {
+ if (is(propValue, expectedValues[i])) {
+ return null;
+ }
+ }
+
+ var valuesString = JSON.stringify(expectedValues, function replacer(key, value) {
+ var type = getPreciseType(value);
+ if (type === 'symbol') {
+ return String(value);
+ }
+ return value;
+ });
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + String(propValue) + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createObjectOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.');
+ }
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));
+ }
+ for (var key in propValue) {
+ if (has(propValue, key)) {
+ var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createUnionTypeChecker(arrayOfTypeCheckers) {
+ if (!Array.isArray(arrayOfTypeCheckers)) {
+ "production" !== 'production' ? printWarning('Invalid argument supplied to oneOfType, expected an instance of array.') : void 0;
+ return emptyFunctionThatReturnsNull;
+ }
+
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ if (typeof checker !== 'function') {
+ printWarning(
+ 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' +
+ 'received ' + getPostfixForTypeWarning(checker) + ' at index ' + i + '.'
+ );
+ return emptyFunctionThatReturnsNull;
+ }
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ var expectedTypes = [];
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ var checkerResult = checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret);
+ if (checkerResult == null) {
+ return null;
+ }
+ if (checkerResult.data.hasOwnProperty('expectedType')) {
+ expectedTypes.push(checkerResult.data.expectedType);
+ }
+ }
+ var expectedTypesMessage = (expectedTypes.length > 0) ? ', expected one of type [' + expectedTypes.join(', ') + ']': '';
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`' + expectedTypesMessage + '.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createNodeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!isNode(props[propName])) {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function invalidValidatorError(componentName, location, propFullName, key, type) {
+ return new PropTypeError(
+ (componentName || 'React class') + ': ' + location + ' type `' + propFullName + '.' + key + '` is invalid; ' +
+ 'it must be a function, usually from the `prop-types` package, but received `' + type + '`.'
+ );
+ }
+
+ function createShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ for (var key in shapeTypes) {
+ var checker = shapeTypes[key];
+ if (typeof checker !== 'function') {
+ return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createStrictShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ // We need to check all keys in case some are required but missing from
+ // props.
+ var allKeys = assign({}, props[propName], shapeTypes);
+ for (var key in allKeys) {
+ var checker = shapeTypes[key];
+ if (has(shapeTypes, key) && typeof checker !== 'function') {
+ return invalidValidatorError(componentName, location, propFullName, key, getPreciseType(checker));
+ }
+ if (!checker) {
+ return new PropTypeError(
+ 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' +
+ '\nBad object: ' + JSON.stringify(props[propName], null, ' ') +
+ '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ')
+ );
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+
+ return createChainableTypeChecker(validate);
+ }
+
+ function isNode(propValue) {
+ switch (typeof propValue) {
+ case 'number':
+ case 'string':
+ case 'undefined':
+ return true;
+ case 'boolean':
+ return !propValue;
+ case 'object':
+ if (Array.isArray(propValue)) {
+ return propValue.every(isNode);
+ }
+ if (propValue === null || isValidElement(propValue)) {
+ return true;
+ }
+
+ var iteratorFn = getIteratorFn(propValue);
+ if (iteratorFn) {
+ var iterator = iteratorFn.call(propValue);
+ var step;
+ if (iteratorFn !== propValue.entries) {
+ while (!(step = iterator.next()).done) {
+ if (!isNode(step.value)) {
+ return false;
+ }
+ }
+ } else {
+ // Iterator will provide entry [k,v] tuples rather than values.
+ while (!(step = iterator.next()).done) {
+ var entry = step.value;
+ if (entry) {
+ if (!isNode(entry[1])) {
+ return false;
+ }
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ function isSymbol(propType, propValue) {
+ // Native Symbol.
+ if (propType === 'symbol') {
+ return true;
+ }
+
+ // falsy value can't be a Symbol
+ if (!propValue) {
+ return false;
+ }
+
+ // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol'
+ if (propValue['@@toStringTag'] === 'Symbol') {
+ return true;
+ }
+
+ // Fallback for non-spec compliant Symbols which are polyfilled.
+ if (typeof Symbol === 'function' && propValue instanceof Symbol) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Equivalent of `typeof` but with special handling for array and regexp.
+ function getPropType(propValue) {
+ var propType = typeof propValue;
+ if (Array.isArray(propValue)) {
+ return 'array';
+ }
+ if (propValue instanceof RegExp) {
+ // Old webkits (at least until Android 4.0) return 'function' rather than
+ // 'object' for typeof a RegExp. We'll normalize this here so that /bla/
+ // passes PropTypes.object.
+ return 'object';
+ }
+ if (isSymbol(propType, propValue)) {
+ return 'symbol';
+ }
+ return propType;
+ }
+
+ // This handles more types than `getPropType`. Only used for error messages.
+ // See `createPrimitiveTypeChecker`.
+ function getPreciseType(propValue) {
+ if (typeof propValue === 'undefined' || propValue === null) {
+ return '' + propValue;
+ }
+ var propType = getPropType(propValue);
+ if (propType === 'object') {
+ if (propValue instanceof Date) {
+ return 'date';
+ } else if (propValue instanceof RegExp) {
+ return 'regexp';
+ }
+ }
+ return propType;
+ }
+
+ // Returns a string that is postfixed to a warning about an invalid type.
+ // For example, "undefined" or "of type array"
+ function getPostfixForTypeWarning(value) {
+ var type = getPreciseType(value);
+ switch (type) {
+ case 'array':
+ case 'object':
+ return 'an ' + type;
+ case 'boolean':
+ case 'date':
+ case 'regexp':
+ return 'a ' + type;
+ default:
+ return type;
+ }
+ }
+
+ // Returns class name of the object, if any.
+ function getClassName(propValue) {
+ if (!propValue.constructor || !propValue.constructor.name) {
+ return ANONYMOUS;
+ }
+ return propValue.constructor.name;
+ }
+
+ ReactPropTypes.checkPropTypes = checkPropTypes;
+ ReactPropTypes.resetWarningCache = checkPropTypes.resetWarningCache;
+ ReactPropTypes.PropTypes = ReactPropTypes;
+
+ return ReactPropTypes;
+};
+
+},{"./checkPropTypes":1,"./lib/ReactPropTypesSecret":5,"./lib/has":6,"object-assign":7,"react-is":10}],4:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+if ("production" !== 'production') {
+ var ReactIs = require('react-is');
+
+ // By explicitly using `prop-types` you are opting into new development behavior.
+ // http://fb.me/prop-types-in-prod
+ var throwOnDirectAccess = true;
+ module.exports = require('./factoryWithTypeCheckers')(ReactIs.isElement, throwOnDirectAccess);
+} else {
+ // By explicitly using `prop-types` you are opting into new production behavior.
+ // http://fb.me/prop-types-in-prod
+ module.exports = require('./factoryWithThrowingShims')();
+}
+
+},{"./factoryWithThrowingShims":2,"./factoryWithTypeCheckers":3,"react-is":10}],5:[function(require,module,exports){
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+
+module.exports = ReactPropTypesSecret;
+
+},{}],6:[function(require,module,exports){
+module.exports = Function.call.bind(Object.prototype.hasOwnProperty);
+
+},{}],7:[function(require,module,exports){
+/*
+object-assign
+(c) Sindre Sorhus
+@license MIT
+*/
+
+'use strict';
+/* eslint-disable no-unused-vars */
+var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+}
+
+function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+}
+
+module.exports = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+};
+
+},{}],8:[function(require,module,exports){
+(function (process){
+/** @license React v16.8.6
+ * react-is.development.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';
+
+
+
+if (process.env.NODE_ENV !== "production") {
+ (function() {
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf;
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+function isValidElementType(type) {
+ return typeof type === 'string' || typeof type === 'function' ||
+ // Note: its typeof might be other than 'symbol' or 'number' if it's a polyfill.
+ type === REACT_FRAGMENT_TYPE || type === REACT_CONCURRENT_MODE_TYPE || type === REACT_PROFILER_TYPE || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || typeof type === 'object' && type !== null && (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE);
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var lowPriorityWarning = function () {};
+
+{
+ var printWarning = function (format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.warn(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ lowPriorityWarning = function (condition, format) {
+ if (format === undefined) {
+ throw new Error('`lowPriorityWarning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var lowPriorityWarning$1 = lowPriorityWarning;
+
+function typeOf(object) {
+ if (typeof object === 'object' && object !== null) {
+ var $$typeof = object.$$typeof;
+ switch ($$typeof) {
+ case REACT_ELEMENT_TYPE:
+ var type = object.type;
+
+ switch (type) {
+ case REACT_ASYNC_MODE_TYPE:
+ case REACT_CONCURRENT_MODE_TYPE:
+ case REACT_FRAGMENT_TYPE:
+ case REACT_PROFILER_TYPE:
+ case REACT_STRICT_MODE_TYPE:
+ case REACT_SUSPENSE_TYPE:
+ return type;
+ default:
+ var $$typeofType = type && type.$$typeof;
+
+ switch ($$typeofType) {
+ case REACT_CONTEXT_TYPE:
+ case REACT_FORWARD_REF_TYPE:
+ case REACT_PROVIDER_TYPE:
+ return $$typeofType;
+ default:
+ return $$typeof;
+ }
+ }
+ case REACT_LAZY_TYPE:
+ case REACT_MEMO_TYPE:
+ case REACT_PORTAL_TYPE:
+ return $$typeof;
+ }
+ }
+
+ return undefined;
+}
+
+// AsyncMode is deprecated along with isAsyncMode
+var AsyncMode = REACT_ASYNC_MODE_TYPE;
+var ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
+var ContextConsumer = REACT_CONTEXT_TYPE;
+var ContextProvider = REACT_PROVIDER_TYPE;
+var Element = REACT_ELEMENT_TYPE;
+var ForwardRef = REACT_FORWARD_REF_TYPE;
+var Fragment = REACT_FRAGMENT_TYPE;
+var Lazy = REACT_LAZY_TYPE;
+var Memo = REACT_MEMO_TYPE;
+var Portal = REACT_PORTAL_TYPE;
+var Profiler = REACT_PROFILER_TYPE;
+var StrictMode = REACT_STRICT_MODE_TYPE;
+var Suspense = REACT_SUSPENSE_TYPE;
+
+var hasWarnedAboutDeprecatedIsAsyncMode = false;
+
+// AsyncMode should be deprecated
+function isAsyncMode(object) {
+ {
+ if (!hasWarnedAboutDeprecatedIsAsyncMode) {
+ hasWarnedAboutDeprecatedIsAsyncMode = true;
+ lowPriorityWarning$1(false, 'The ReactIs.isAsyncMode() alias has been deprecated, ' + 'and will be removed in React 17+. Update your code to use ' + 'ReactIs.isConcurrentMode() instead. It has the exact same API.');
+ }
+ }
+ return isConcurrentMode(object) || typeOf(object) === REACT_ASYNC_MODE_TYPE;
+}
+function isConcurrentMode(object) {
+ return typeOf(object) === REACT_CONCURRENT_MODE_TYPE;
+}
+function isContextConsumer(object) {
+ return typeOf(object) === REACT_CONTEXT_TYPE;
+}
+function isContextProvider(object) {
+ return typeOf(object) === REACT_PROVIDER_TYPE;
+}
+function isElement(object) {
+ return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
+}
+function isForwardRef(object) {
+ return typeOf(object) === REACT_FORWARD_REF_TYPE;
+}
+function isFragment(object) {
+ return typeOf(object) === REACT_FRAGMENT_TYPE;
+}
+function isLazy(object) {
+ return typeOf(object) === REACT_LAZY_TYPE;
+}
+function isMemo(object) {
+ return typeOf(object) === REACT_MEMO_TYPE;
+}
+function isPortal(object) {
+ return typeOf(object) === REACT_PORTAL_TYPE;
+}
+function isProfiler(object) {
+ return typeOf(object) === REACT_PROFILER_TYPE;
+}
+function isStrictMode(object) {
+ return typeOf(object) === REACT_STRICT_MODE_TYPE;
+}
+function isSuspense(object) {
+ return typeOf(object) === REACT_SUSPENSE_TYPE;
+}
+
+exports.typeOf = typeOf;
+exports.AsyncMode = AsyncMode;
+exports.ConcurrentMode = ConcurrentMode;
+exports.ContextConsumer = ContextConsumer;
+exports.ContextProvider = ContextProvider;
+exports.Element = Element;
+exports.ForwardRef = ForwardRef;
+exports.Fragment = Fragment;
+exports.Lazy = Lazy;
+exports.Memo = Memo;
+exports.Portal = Portal;
+exports.Profiler = Profiler;
+exports.StrictMode = StrictMode;
+exports.Suspense = Suspense;
+exports.isValidElementType = isValidElementType;
+exports.isAsyncMode = isAsyncMode;
+exports.isConcurrentMode = isConcurrentMode;
+exports.isContextConsumer = isContextConsumer;
+exports.isContextProvider = isContextProvider;
+exports.isElement = isElement;
+exports.isForwardRef = isForwardRef;
+exports.isFragment = isFragment;
+exports.isLazy = isLazy;
+exports.isMemo = isMemo;
+exports.isPortal = isPortal;
+exports.isProfiler = isProfiler;
+exports.isStrictMode = isStrictMode;
+exports.isSuspense = isSuspense;
+ })();
+}
+
+}).call(this,require('_process'))
+},{"_process":11}],9:[function(require,module,exports){
+/** @license React v16.8.6
+ * react-is.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+'use strict';Object.defineProperty(exports,"__esModule",{value:!0});
+var b="function"===typeof Symbol&&Symbol.for,c=b?Symbol.for("react.element"):60103,d=b?Symbol.for("react.portal"):60106,e=b?Symbol.for("react.fragment"):60107,f=b?Symbol.for("react.strict_mode"):60108,g=b?Symbol.for("react.profiler"):60114,h=b?Symbol.for("react.provider"):60109,k=b?Symbol.for("react.context"):60110,l=b?Symbol.for("react.async_mode"):60111,m=b?Symbol.for("react.concurrent_mode"):60111,n=b?Symbol.for("react.forward_ref"):60112,p=b?Symbol.for("react.suspense"):60113,q=b?Symbol.for("react.memo"):
+60115,r=b?Symbol.for("react.lazy"):60116;function t(a){if("object"===typeof a&&null!==a){var u=a.$$typeof;switch(u){case c:switch(a=a.type,a){case l:case m:case e:case g:case f:case p:return a;default:switch(a=a&&a.$$typeof,a){case k:case n:case h:return a;default:return u}}case r:case q:case d:return u}}}function v(a){return t(a)===m}exports.typeOf=t;exports.AsyncMode=l;exports.ConcurrentMode=m;exports.ContextConsumer=k;exports.ContextProvider=h;exports.Element=c;exports.ForwardRef=n;
+exports.Fragment=e;exports.Lazy=r;exports.Memo=q;exports.Portal=d;exports.Profiler=g;exports.StrictMode=f;exports.Suspense=p;exports.isValidElementType=function(a){return"string"===typeof a||"function"===typeof a||a===e||a===m||a===g||a===f||a===p||"object"===typeof a&&null!==a&&(a.$$typeof===r||a.$$typeof===q||a.$$typeof===h||a.$$typeof===k||a.$$typeof===n)};exports.isAsyncMode=function(a){return v(a)||t(a)===l};exports.isConcurrentMode=v;exports.isContextConsumer=function(a){return t(a)===k};
+exports.isContextProvider=function(a){return t(a)===h};exports.isElement=function(a){return"object"===typeof a&&null!==a&&a.$$typeof===c};exports.isForwardRef=function(a){return t(a)===n};exports.isFragment=function(a){return t(a)===e};exports.isLazy=function(a){return t(a)===r};exports.isMemo=function(a){return t(a)===q};exports.isPortal=function(a){return t(a)===d};exports.isProfiler=function(a){return t(a)===g};exports.isStrictMode=function(a){return t(a)===f};
+exports.isSuspense=function(a){return t(a)===p};
+
+},{}],10:[function(require,module,exports){
+(function (process){
+'use strict';
+
+if (process.env.NODE_ENV === 'production') {
+ module.exports = require('./cjs/react-is.production.min.js');
+} else {
+ module.exports = require('./cjs/react-is.development.js');
+}
+
+}).call(this,require('_process'))
+},{"./cjs/react-is.development.js":8,"./cjs/react-is.production.min.js":9,"_process":11}],11:[function(require,module,exports){
+// shim for using process in browser
+var process = module.exports = {};
+
+// cached from whatever global is present so that test runners that stub it
+// don't break things. But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals. It's inside a
+// function because try/catches deoptimize in certain engines.
+
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+ throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+ throw new Error('clearTimeout has not been defined');
+}
+(function () {
+ try {
+ if (typeof setTimeout === 'function') {
+ cachedSetTimeout = setTimeout;
+ } else {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ } catch (e) {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ try {
+ if (typeof clearTimeout === 'function') {
+ cachedClearTimeout = clearTimeout;
+ } else {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+ } catch (e) {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+} ())
+function runTimeout(fun) {
+ if (cachedSetTimeout === setTimeout) {
+ //normal enviroments in sane situations
+ return setTimeout(fun, 0);
+ }
+ // if setTimeout wasn't available but was latter defined
+ if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+ cachedSetTimeout = setTimeout;
+ return setTimeout(fun, 0);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedSetTimeout(fun, 0);
+ } catch(e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedSetTimeout.call(null, fun, 0);
+ } catch(e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+ return cachedSetTimeout.call(this, fun, 0);
+ }
+ }
+
+
+}
+function runClearTimeout(marker) {
+ if (cachedClearTimeout === clearTimeout) {
+ //normal enviroments in sane situations
+ return clearTimeout(marker);
+ }
+ // if clearTimeout wasn't available but was latter defined
+ if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+ cachedClearTimeout = clearTimeout;
+ return clearTimeout(marker);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedClearTimeout(marker);
+ } catch (e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedClearTimeout.call(null, marker);
+ } catch (e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+ // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+ return cachedClearTimeout.call(this, marker);
+ }
+ }
+
+
+
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
+
+function cleanUpNextTick() {
+ if (!draining || !currentQueue) {
+ return;
+ }
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
+
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = runTimeout(cleanUpNextTick);
+ draining = true;
+
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
+ }
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ runClearTimeout(timeout);
+}
+
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ runTimeout(drainQueue);
+ }
+};
+
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
+
+function noop() {}
+
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
+process.prependListener = noop;
+process.prependOnceListener = noop;
+
+process.listeners = function (name) { return [] }
+
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
+
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
+
+},{}]},{},[4])(4)
+});
diff --git a/devtools/client/shared/vendor/react-redux.js b/devtools/client/shared/vendor/react-redux.js
new file mode 100644
index 0000000000..17dc7894ea
--- /dev/null
+++ b/devtools/client/shared/vendor/react-redux.js
@@ -0,0 +1,2089 @@
+/**
+ * react-redux v5.0.7
+ */
+
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require("resource://devtools/client/shared/vendor/react.js"), require("resource://devtools/client/shared/vendor/redux.js")) :
+ typeof define === 'function' && define.amd ? define(['exports', 'devtools/client/shared/vendor/react', 'devtools/client/shared/vendor/redux'], factory) :
+ (factory((global.ReactRedux = {}),global.React,global.Redux));
+}(this, (function (exports,react,redux) { 'use strict';
+
+var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ *
+ */
+
+function makeEmptyFunction(arg) {
+ return function () {
+ return arg;
+ };
+}
+
+/**
+ * This function accepts and discards inputs; it has no side effects. This is
+ * primarily useful idiomatically for overridable function endpoints which
+ * always need to be callable, since JS lacks a null-call idiom ala Cocoa.
+ */
+var emptyFunction = function emptyFunction() {};
+
+emptyFunction.thatReturns = makeEmptyFunction;
+emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
+emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
+emptyFunction.thatReturnsNull = makeEmptyFunction(null);
+emptyFunction.thatReturnsThis = function () {
+ return this;
+};
+emptyFunction.thatReturnsArgument = function (arg) {
+ return arg;
+};
+
+var emptyFunction_1 = emptyFunction;
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+var validateFormat = function validateFormat(format) {};
+
+{
+ validateFormat = function validateFormat(format) {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ };
+}
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ validateFormat(format);
+
+ if (!condition) {
+ var error;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+var invariant_1 = invariant;
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var warning = emptyFunction_1;
+
+{
+ var printWarning = function printWarning(format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ warning = function warning(condition, format) {
+ if (format === undefined) {
+ throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+
+ if (format.indexOf('Failed Composite propType: ') === 0) {
+ return; // Ignore CompositeComponent proptype check.
+ }
+
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var warning_1 = warning;
+
+/*
+object-assign
+(c) Sindre Sorhus
+@license MIT
+*/
+/* eslint-disable no-unused-vars */
+var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+}
+
+function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+}
+
+var objectAssign = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+};
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+
+var ReactPropTypesSecret_1 = ReactPropTypesSecret;
+
+{
+ var invariant$1 = invariant_1;
+ var warning$1 = warning_1;
+ var ReactPropTypesSecret$1 = ReactPropTypesSecret_1;
+ var loggedTypeFailures = {};
+}
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+ {
+ for (var typeSpecName in typeSpecs) {
+ if (typeSpecs.hasOwnProperty(typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ invariant$1(typeof typeSpecs[typeSpecName] === 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'the `prop-types` package, but received `%s`.', componentName || 'React class', location, typeSpecName, typeof typeSpecs[typeSpecName]);
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret$1);
+ } catch (ex) {
+ error = ex;
+ }
+ warning$1(!error || error instanceof Error, '%s: type specification of %s `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error);
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var stack = getStack ? getStack() : '';
+
+ warning$1(false, 'Failed %s type: %s%s', location, error.message, stack != null ? stack : '');
+ }
+ }
+ }
+ }
+}
+
+var checkPropTypes_1 = checkPropTypes;
+
+var factoryWithTypeCheckers = function(isValidElement, throwOnDirectAccess) {
+ /* global Symbol */
+ var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+ var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
+
+ /**
+ * Returns the iterator method function contained on the iterable object.
+ *
+ * Be sure to invoke the function with the iterable as context:
+ *
+ * var iteratorFn = getIteratorFn(myIterable);
+ * if (iteratorFn) {
+ * var iterator = iteratorFn.call(myIterable);
+ * ...
+ * }
+ *
+ * @param {?object} maybeIterable
+ * @return {?function}
+ */
+ function getIteratorFn(maybeIterable) {
+ var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
+ if (typeof iteratorFn === 'function') {
+ return iteratorFn;
+ }
+ }
+
+ /**
+ * Collection of methods that allow declaration and validation of props that are
+ * supplied to React components. Example usage:
+ *
+ * var Props = require('ReactPropTypes');
+ * var MyArticle = React.createClass({
+ * propTypes: {
+ * // An optional string prop named "description".
+ * description: Props.string,
+ *
+ * // A required enum prop named "category".
+ * category: Props.oneOf(['News','Photos']).isRequired,
+ *
+ * // A prop named "dialog" that requires an instance of Dialog.
+ * dialog: Props.instanceOf(Dialog).isRequired
+ * },
+ * render: function() { ... }
+ * });
+ *
+ * A more formal specification of how these methods are used:
+ *
+ * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
+ * decl := ReactPropTypes.{type}(.isRequired)?
+ *
+ * Each and every declaration produces a function with the same signature. This
+ * allows the creation of custom validation functions. For example:
+ *
+ * var MyLink = React.createClass({
+ * propTypes: {
+ * // An optional string or URI prop named "href".
+ * href: function(props, propName, componentName) {
+ * var propValue = props[propName];
+ * if (propValue != null && typeof propValue !== 'string' &&
+ * !(propValue instanceof URI)) {
+ * return new Error(
+ * 'Expected a string or an URI for ' + propName + ' in ' +
+ * componentName
+ * );
+ * }
+ * }
+ * },
+ * render: function() {...}
+ * });
+ *
+ * @internal
+ */
+
+ var ANONYMOUS = '<<anonymous>>';
+
+ // Important!
+ // Keep this list in sync with production version in `./factoryWithThrowingShims.js`.
+ var ReactPropTypes = {
+ array: createPrimitiveTypeChecker('array'),
+ bool: createPrimitiveTypeChecker('boolean'),
+ func: createPrimitiveTypeChecker('function'),
+ number: createPrimitiveTypeChecker('number'),
+ object: createPrimitiveTypeChecker('object'),
+ string: createPrimitiveTypeChecker('string'),
+ symbol: createPrimitiveTypeChecker('symbol'),
+
+ any: createAnyTypeChecker(),
+ arrayOf: createArrayOfTypeChecker,
+ element: createElementTypeChecker(),
+ instanceOf: createInstanceTypeChecker,
+ node: createNodeChecker(),
+ objectOf: createObjectOfTypeChecker,
+ oneOf: createEnumTypeChecker,
+ oneOfType: createUnionTypeChecker,
+ shape: createShapeTypeChecker,
+ exact: createStrictShapeTypeChecker,
+ };
+
+ /**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+ /*eslint-disable no-self-compare*/
+ function is(x, y) {
+ // SameValue algorithm
+ if (x === y) {
+ // Steps 1-5, 7-10
+ // Steps 6.b-6.e: +0 != -0
+ return x !== 0 || 1 / x === 1 / y;
+ } else {
+ // Step 6.a: NaN == NaN
+ return x !== x && y !== y;
+ }
+ }
+ /*eslint-enable no-self-compare*/
+
+ /**
+ * We use an Error-like object for backward compatibility as people may call
+ * PropTypes directly and inspect their output. However, we don't use real
+ * Errors anymore. We don't inspect their stack anyway, and creating them
+ * is prohibitively expensive if they are created too often, such as what
+ * happens in oneOfType() for any type before the one that matched.
+ */
+ function PropTypeError(message) {
+ this.message = message;
+ this.stack = '';
+ }
+ // Make `instanceof Error` still work for returned errors.
+ PropTypeError.prototype = Error.prototype;
+
+ function createChainableTypeChecker(validate) {
+ {
+ var manualPropTypeCallCache = {};
+ var manualPropTypeWarningCount = 0;
+ }
+ function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
+ componentName = componentName || ANONYMOUS;
+ propFullName = propFullName || propName;
+
+ if (secret !== ReactPropTypesSecret_1) {
+ if (throwOnDirectAccess) {
+ // New behavior only for users of `prop-types` package
+ invariant_1(
+ false,
+ 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' +
+ 'Use `PropTypes.checkPropTypes()` to call them. ' +
+ 'Read more at http://fb.me/use-check-prop-types'
+ );
+ } else if ("development" !== 'production' && typeof console !== 'undefined') {
+ // Old behavior for people using React.PropTypes
+ var cacheKey = componentName + ':' + propName;
+ if (
+ !manualPropTypeCallCache[cacheKey] &&
+ // Avoid spamming the console because they are often not actionable except for lib authors
+ manualPropTypeWarningCount < 3
+ ) {
+ warning_1(
+ false,
+ 'You are manually calling a React.PropTypes validation ' +
+ 'function for the `%s` prop on `%s`. This is deprecated ' +
+ 'and will throw in the standalone `prop-types` package. ' +
+ 'You may be seeing this warning due to a third-party PropTypes ' +
+ 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.',
+ propFullName,
+ componentName
+ );
+ manualPropTypeCallCache[cacheKey] = true;
+ manualPropTypeWarningCount++;
+ }
+ }
+ }
+ if (props[propName] == null) {
+ if (isRequired) {
+ if (props[propName] === null) {
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));
+ }
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));
+ }
+ return null;
+ } else {
+ return validate(props, propName, componentName, location, propFullName);
+ }
+ }
+
+ var chainedCheckType = checkType.bind(null, false);
+ chainedCheckType.isRequired = checkType.bind(null, true);
+
+ return chainedCheckType;
+ }
+
+ function createPrimitiveTypeChecker(expectedType) {
+ function validate(props, propName, componentName, location, propFullName, secret) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== expectedType) {
+ // `propValue` being instance of, say, date/regexp, pass the 'object'
+ // check, but we can offer a more precise error message here rather than
+ // 'of type `object`'.
+ var preciseType = getPreciseType(propValue);
+
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createAnyTypeChecker() {
+ return createChainableTypeChecker(emptyFunction_1.thatReturnsNull);
+ }
+
+ function createArrayOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.');
+ }
+ var propValue = props[propName];
+ if (!Array.isArray(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));
+ }
+ for (var i = 0; i < propValue.length; i++) {
+ var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret_1);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createElementTypeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ if (!isValidElement(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createInstanceTypeChecker(expectedClass) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!(props[propName] instanceof expectedClass)) {
+ var expectedClassName = expectedClass.name || ANONYMOUS;
+ var actualClassName = getClassName(props[propName]);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createEnumTypeChecker(expectedValues) {
+ if (!Array.isArray(expectedValues)) {
+ warning_1(false, 'Invalid argument supplied to oneOf, expected an instance of array.');
+ return emptyFunction_1.thatReturnsNull;
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ for (var i = 0; i < expectedValues.length; i++) {
+ if (is(propValue, expectedValues[i])) {
+ return null;
+ }
+ }
+
+ var valuesString = JSON.stringify(expectedValues);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + propValue + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createObjectOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.');
+ }
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));
+ }
+ for (var key in propValue) {
+ if (propValue.hasOwnProperty(key)) {
+ var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret_1);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createUnionTypeChecker(arrayOfTypeCheckers) {
+ if (!Array.isArray(arrayOfTypeCheckers)) {
+ warning_1(false, 'Invalid argument supplied to oneOfType, expected an instance of array.');
+ return emptyFunction_1.thatReturnsNull;
+ }
+
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ if (typeof checker !== 'function') {
+ warning_1(
+ false,
+ 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' +
+ 'received %s at index %s.',
+ getPostfixForTypeWarning(checker),
+ i
+ );
+ return emptyFunction_1.thatReturnsNull;
+ }
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ if (checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret_1) == null) {
+ return null;
+ }
+ }
+
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createNodeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!isNode(props[propName])) {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ for (var key in shapeTypes) {
+ var checker = shapeTypes[key];
+ if (!checker) {
+ continue;
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret_1);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createStrictShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ // We need to check all keys in case some are required but missing from
+ // props.
+ var allKeys = objectAssign({}, props[propName], shapeTypes);
+ for (var key in allKeys) {
+ var checker = shapeTypes[key];
+ if (!checker) {
+ return new PropTypeError(
+ 'Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' +
+ '\nBad object: ' + JSON.stringify(props[propName], null, ' ') +
+ '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' ')
+ );
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret_1);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+
+ return createChainableTypeChecker(validate);
+ }
+
+ function isNode(propValue) {
+ switch (typeof propValue) {
+ case 'number':
+ case 'string':
+ case 'undefined':
+ return true;
+ case 'boolean':
+ return !propValue;
+ case 'object':
+ if (Array.isArray(propValue)) {
+ return propValue.every(isNode);
+ }
+ if (propValue === null || isValidElement(propValue)) {
+ return true;
+ }
+
+ var iteratorFn = getIteratorFn(propValue);
+ if (iteratorFn) {
+ var iterator = iteratorFn.call(propValue);
+ var step;
+ if (iteratorFn !== propValue.entries) {
+ while (!(step = iterator.next()).done) {
+ if (!isNode(step.value)) {
+ return false;
+ }
+ }
+ } else {
+ // Iterator will provide entry [k,v] tuples rather than values.
+ while (!(step = iterator.next()).done) {
+ var entry = step.value;
+ if (entry) {
+ if (!isNode(entry[1])) {
+ return false;
+ }
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ function isSymbol(propType, propValue) {
+ // Native Symbol.
+ if (propType === 'symbol') {
+ return true;
+ }
+
+ // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol'
+ if (propValue['@@toStringTag'] === 'Symbol') {
+ return true;
+ }
+
+ // Fallback for non-spec compliant Symbols which are polyfilled.
+ if (typeof Symbol === 'function' && propValue instanceof Symbol) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Equivalent of `typeof` but with special handling for array and regexp.
+ function getPropType(propValue) {
+ var propType = typeof propValue;
+ if (Array.isArray(propValue)) {
+ return 'array';
+ }
+ if (propValue instanceof RegExp) {
+ // Old webkits (at least until Android 4.0) return 'function' rather than
+ // 'object' for typeof a RegExp. We'll normalize this here so that /bla/
+ // passes PropTypes.object.
+ return 'object';
+ }
+ if (isSymbol(propType, propValue)) {
+ return 'symbol';
+ }
+ return propType;
+ }
+
+ // This handles more types than `getPropType`. Only used for error messages.
+ // See `createPrimitiveTypeChecker`.
+ function getPreciseType(propValue) {
+ if (typeof propValue === 'undefined' || propValue === null) {
+ return '' + propValue;
+ }
+ var propType = getPropType(propValue);
+ if (propType === 'object') {
+ if (propValue instanceof Date) {
+ return 'date';
+ } else if (propValue instanceof RegExp) {
+ return 'regexp';
+ }
+ }
+ return propType;
+ }
+
+ // Returns a string that is postfixed to a warning about an invalid type.
+ // For example, "undefined" or "of type array"
+ function getPostfixForTypeWarning(value) {
+ var type = getPreciseType(value);
+ switch (type) {
+ case 'array':
+ case 'object':
+ return 'an ' + type;
+ case 'boolean':
+ case 'date':
+ case 'regexp':
+ return 'a ' + type;
+ default:
+ return type;
+ }
+ }
+
+ // Returns class name of the object, if any.
+ function getClassName(propValue) {
+ if (!propValue.constructor || !propValue.constructor.name) {
+ return ANONYMOUS;
+ }
+ return propValue.constructor.name;
+ }
+
+ ReactPropTypes.checkPropTypes = checkPropTypes_1;
+ ReactPropTypes.PropTypes = ReactPropTypes;
+
+ return ReactPropTypes;
+};
+
+var propTypes = createCommonjsModule(function (module) {
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+{
+ var REACT_ELEMENT_TYPE = (typeof Symbol === 'function' &&
+ Symbol.for &&
+ Symbol.for('react.element')) ||
+ 0xeac7;
+
+ var isValidElement = function(object) {
+ return typeof object === 'object' &&
+ object !== null &&
+ object.$$typeof === REACT_ELEMENT_TYPE;
+ };
+
+ // By explicitly using `prop-types` you are opting into new development behavior.
+ // http://fb.me/prop-types-in-prod
+ var throwOnDirectAccess = true;
+ module.exports = factoryWithTypeCheckers(isValidElement, throwOnDirectAccess);
+}
+});
+
+var subscriptionShape = propTypes.shape({
+ trySubscribe: propTypes.func.isRequired,
+ tryUnsubscribe: propTypes.func.isRequired,
+ notifyNestedSubs: propTypes.func.isRequired,
+ isSubscribed: propTypes.func.isRequired
+});
+
+var storeShape = propTypes.shape({
+ subscribe: propTypes.func.isRequired,
+ dispatch: propTypes.func.isRequired,
+ getState: propTypes.func.isRequired
+});
+
+/**
+ * Prints a warning in the console if it exists.
+ *
+ * @param {String} message The warning message.
+ * @returns {void}
+ */
+function warning$2(message) {
+ /* eslint-disable no-console */
+ if (typeof console !== 'undefined' && typeof console.error === 'function') {
+ console.error(message);
+ }
+ /* eslint-enable no-console */
+ try {
+ // This error was thrown as a convenience so that if you enable
+ // "break on all exceptions" in your console,
+ // it would pause the execution at this line.
+ throw new Error(message);
+ /* eslint-disable no-empty */
+ } catch (e) {}
+ /* eslint-enable no-empty */
+}
+
+var classCallCheck = function (instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+};
+
+var _extends = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target;
+};
+
+var inherits = function (subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }
+
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+};
+
+var objectWithoutProperties = function (obj, keys) {
+ var target = {};
+
+ for (var i in obj) {
+ if (keys.indexOf(i) >= 0) continue;
+ if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
+ target[i] = obj[i];
+ }
+
+ return target;
+};
+
+var possibleConstructorReturn = function (self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }
+
+ return call && (typeof call === "object" || typeof call === "function") ? call : self;
+};
+
+var didWarnAboutReceivingStore = false;
+function warnAboutReceivingStore() {
+ if (didWarnAboutReceivingStore) {
+ return;
+ }
+ didWarnAboutReceivingStore = true;
+
+ warning$2('<Provider> does not support changing `store` on the fly. ' + 'It is most likely that you see this error because you updated to ' + 'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' + 'automatically. See https://github.com/reactjs/react-redux/releases/' + 'tag/v2.0.0 for the migration instructions.');
+}
+
+function createProvider() {
+ var _Provider$childContex;
+
+ var storeKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'store';
+ var subKey = arguments[1];
+
+ var subscriptionKey = subKey || storeKey + 'Subscription';
+
+ var Provider = function (_Component) {
+ inherits(Provider, _Component);
+
+ Provider.prototype.getChildContext = function getChildContext() {
+ var _ref;
+
+ return _ref = {}, _ref[storeKey] = this[storeKey], _ref[subscriptionKey] = null, _ref;
+ };
+
+ function Provider(props, context) {
+ classCallCheck(this, Provider);
+
+ var _this = possibleConstructorReturn(this, _Component.call(this, props, context));
+
+ _this[storeKey] = props.store;
+ return _this;
+ }
+
+ Provider.prototype.render = function render() {
+ return react.Children.only(this.props.children);
+ };
+
+ return Provider;
+ }(react.Component);
+
+ {
+ Provider.prototype.componentWillReceiveProps = function (nextProps) {
+ if (this[storeKey] !== nextProps.store) {
+ warnAboutReceivingStore();
+ }
+ };
+ }
+
+ Provider.propTypes = {
+ store: storeShape.isRequired,
+ children: propTypes.element.isRequired
+ };
+ Provider.childContextTypes = (_Provider$childContex = {}, _Provider$childContex[storeKey] = storeShape.isRequired, _Provider$childContex[subscriptionKey] = subscriptionShape, _Provider$childContex);
+
+ return Provider;
+}
+
+var Provider = createProvider();
+
+var hoistNonReactStatics = createCommonjsModule(function (module, exports) {
+/**
+ * Copyright 2015, Yahoo! Inc.
+ * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+(function (global, factory) {
+ module.exports = factory();
+}(commonjsGlobal, (function () {
+
+ var REACT_STATICS = {
+ childContextTypes: true,
+ contextTypes: true,
+ defaultProps: true,
+ displayName: true,
+ getDefaultProps: true,
+ getDerivedStateFromProps: true,
+ mixins: true,
+ propTypes: true,
+ type: true
+ };
+
+ var KNOWN_STATICS = {
+ name: true,
+ length: true,
+ prototype: true,
+ caller: true,
+ callee: true,
+ arguments: true,
+ arity: true
+ };
+
+ var defineProperty = Object.defineProperty;
+ var getOwnPropertyNames = Object.getOwnPropertyNames;
+ var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+ var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+ var getPrototypeOf = Object.getPrototypeOf;
+ var objectPrototype = getPrototypeOf && getPrototypeOf(Object);
+
+ return function hoistNonReactStatics(targetComponent, sourceComponent, blacklist) {
+ if (typeof sourceComponent !== 'string') { // don't hoist over string (html) components
+
+ if (objectPrototype) {
+ var inheritedComponent = getPrototypeOf(sourceComponent);
+ if (inheritedComponent && inheritedComponent !== objectPrototype) {
+ hoistNonReactStatics(targetComponent, inheritedComponent, blacklist);
+ }
+ }
+
+ var keys = getOwnPropertyNames(sourceComponent);
+
+ if (getOwnPropertySymbols) {
+ keys = keys.concat(getOwnPropertySymbols(sourceComponent));
+ }
+
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (!REACT_STATICS[key] && !KNOWN_STATICS[key] && (!blacklist || !blacklist[key])) {
+ var descriptor = getOwnPropertyDescriptor(sourceComponent, key);
+ try { // Avoid failures from read-only properties
+ defineProperty(targetComponent, key, descriptor);
+ } catch (e) {}
+ }
+ }
+
+ return targetComponent;
+ }
+
+ return targetComponent;
+ };
+})));
+});
+
+/**
+ * Copyright 2013-2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+var NODE_ENV = "development";
+
+var invariant$2 = function(condition, format, a, b, c, d, e, f) {
+ if (NODE_ENV !== 'production') {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ }
+
+ if (!condition) {
+ var error;
+ if (format === undefined) {
+ error = new Error(
+ 'Minified exception occurred; use the non-minified dev environment ' +
+ 'for the full error message and additional helpful warnings.'
+ );
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(
+ format.replace(/%s/g, function() { return args[argIndex++]; })
+ );
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+};
+
+var invariant_1$2 = invariant$2;
+
+// encapsulates the subscription logic for connecting a component to the redux store, as
+// well as nesting subscriptions of descendant components, so that we can ensure the
+// ancestor components re-render before descendants
+
+var CLEARED = null;
+var nullListeners = {
+ notify: function notify() {}
+};
+
+function createListenerCollection() {
+ // the current/next pattern is copied from redux's createStore code.
+ // TODO: refactor+expose that code to be reusable here?
+ var current = [];
+ var next = [];
+
+ return {
+ clear: function clear() {
+ next = CLEARED;
+ current = CLEARED;
+ },
+ notify: function notify() {
+ var listeners = current = next;
+ for (var i = 0; i < listeners.length; i++) {
+ listeners[i]();
+ }
+ },
+ get: function get$$1() {
+ return next;
+ },
+ subscribe: function subscribe(listener) {
+ var isSubscribed = true;
+ if (next === current) next = current.slice();
+ next.push(listener);
+
+ return function unsubscribe() {
+ if (!isSubscribed || current === CLEARED) return;
+ isSubscribed = false;
+
+ if (next === current) next = current.slice();
+ next.splice(next.indexOf(listener), 1);
+ };
+ }
+ };
+}
+
+var Subscription = function () {
+ function Subscription(store, parentSub, onStateChange) {
+ classCallCheck(this, Subscription);
+
+ this.store = store;
+ this.parentSub = parentSub;
+ this.onStateChange = onStateChange;
+ this.unsubscribe = null;
+ this.listeners = nullListeners;
+ }
+
+ Subscription.prototype.addNestedSub = function addNestedSub(listener) {
+ this.trySubscribe();
+ return this.listeners.subscribe(listener);
+ };
+
+ Subscription.prototype.notifyNestedSubs = function notifyNestedSubs() {
+ this.listeners.notify();
+ };
+
+ Subscription.prototype.isSubscribed = function isSubscribed() {
+ return Boolean(this.unsubscribe);
+ };
+
+ Subscription.prototype.trySubscribe = function trySubscribe() {
+ if (!this.unsubscribe) {
+ this.unsubscribe = this.parentSub ? this.parentSub.addNestedSub(this.onStateChange) : this.store.subscribe(this.onStateChange);
+
+ this.listeners = createListenerCollection();
+ }
+ };
+
+ Subscription.prototype.tryUnsubscribe = function tryUnsubscribe() {
+ if (this.unsubscribe) {
+ this.unsubscribe();
+ this.unsubscribe = null;
+ this.listeners.clear();
+ this.listeners = nullListeners;
+ }
+ };
+
+ return Subscription;
+}();
+
+var hotReloadingVersion = 0;
+var dummyState = {};
+function noop() {}
+function makeSelectorStateful(sourceSelector, store) {
+ // wrap the selector in an object that tracks its results between runs.
+ var selector = {
+ run: function runComponentSelector(props) {
+ try {
+ var nextProps = sourceSelector(store.getState(), props);
+ if (nextProps !== selector.props || selector.error) {
+ selector.shouldComponentUpdate = true;
+ selector.props = nextProps;
+ selector.error = null;
+ }
+ } catch (error) {
+ selector.shouldComponentUpdate = true;
+ selector.error = error;
+ }
+ }
+ };
+
+ return selector;
+}
+
+function connectAdvanced(
+/*
+ selectorFactory is a func that is responsible for returning the selector function used to
+ compute new props from state, props, and dispatch. For example:
+ export default connectAdvanced((dispatch, options) => (state, props) => ({
+ thing: state.things[props.thingId],
+ saveThing: fields => dispatch(actionCreators.saveThing(props.thingId, fields)),
+ }))(YourComponent)
+ Access to dispatch is provided to the factory so selectorFactories can bind actionCreators
+ outside of their selector as an optimization. Options passed to connectAdvanced are passed to
+ the selectorFactory, along with displayName and WrappedComponent, as the second argument.
+ Note that selectorFactory is responsible for all caching/memoization of inbound and outbound
+ props. Do not use connectAdvanced directly without memoizing results between calls to your
+ selector, otherwise the Connect component will re-render on every state or props change.
+*/
+selectorFactory) {
+ var _contextTypes, _childContextTypes;
+
+ var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+ _ref$getDisplayName = _ref.getDisplayName,
+ getDisplayName = _ref$getDisplayName === undefined ? function (name) {
+ return 'ConnectAdvanced(' + name + ')';
+ } : _ref$getDisplayName,
+ _ref$methodName = _ref.methodName,
+ methodName = _ref$methodName === undefined ? 'connectAdvanced' : _ref$methodName,
+ _ref$renderCountProp = _ref.renderCountProp,
+ renderCountProp = _ref$renderCountProp === undefined ? undefined : _ref$renderCountProp,
+ _ref$shouldHandleStat = _ref.shouldHandleStateChanges,
+ shouldHandleStateChanges = _ref$shouldHandleStat === undefined ? true : _ref$shouldHandleStat,
+ _ref$storeKey = _ref.storeKey,
+ storeKey = _ref$storeKey === undefined ? 'store' : _ref$storeKey,
+ _ref$withRef = _ref.withRef,
+ withRef = _ref$withRef === undefined ? false : _ref$withRef,
+ connectOptions = objectWithoutProperties(_ref, ['getDisplayName', 'methodName', 'renderCountProp', 'shouldHandleStateChanges', 'storeKey', 'withRef']);
+
+ var subscriptionKey = storeKey + 'Subscription';
+ var version = hotReloadingVersion++;
+
+ var contextTypes = (_contextTypes = {}, _contextTypes[storeKey] = storeShape, _contextTypes[subscriptionKey] = subscriptionShape, _contextTypes);
+ var childContextTypes = (_childContextTypes = {}, _childContextTypes[subscriptionKey] = subscriptionShape, _childContextTypes);
+
+ return function wrapWithConnect(WrappedComponent) {
+ invariant_1$2(typeof WrappedComponent == 'function', 'You must pass a component to the function returned by ' + (methodName + '. Instead received ' + JSON.stringify(WrappedComponent)));
+
+ var wrappedComponentName = WrappedComponent.displayName || WrappedComponent.name || 'Component';
+
+ var displayName = getDisplayName(wrappedComponentName);
+
+ var selectorFactoryOptions = _extends({}, connectOptions, {
+ getDisplayName: getDisplayName,
+ methodName: methodName,
+ renderCountProp: renderCountProp,
+ shouldHandleStateChanges: shouldHandleStateChanges,
+ storeKey: storeKey,
+ withRef: withRef,
+ displayName: displayName,
+ wrappedComponentName: wrappedComponentName,
+ WrappedComponent: WrappedComponent
+ });
+
+ var Connect = function (_Component) {
+ inherits(Connect, _Component);
+
+ function Connect(props, context) {
+ classCallCheck(this, Connect);
+
+ var _this = possibleConstructorReturn(this, _Component.call(this, props, context));
+
+ _this.version = version;
+ _this.state = {};
+ _this.renderCount = 0;
+ _this.store = props[storeKey] || context[storeKey];
+ _this.propsMode = Boolean(props[storeKey]);
+ _this.setWrappedInstance = _this.setWrappedInstance.bind(_this);
+
+ invariant_1$2(_this.store, 'Could not find "' + storeKey + '" in either the context or props of ' + ('"' + displayName + '". Either wrap the root component in a <Provider>, ') + ('or explicitly pass "' + storeKey + '" as a prop to "' + displayName + '".'));
+
+ _this.initSelector();
+ _this.initSubscription();
+ return _this;
+ }
+
+ Connect.prototype.getChildContext = function getChildContext() {
+ var _ref2;
+
+ // If this component received store from props, its subscription should be transparent
+ // to any descendants receiving store+subscription from context; it passes along
+ // subscription passed to it. Otherwise, it shadows the parent subscription, which allows
+ // Connect to control ordering of notifications to flow top-down.
+ var subscription = this.propsMode ? null : this.subscription;
+ return _ref2 = {}, _ref2[subscriptionKey] = subscription || this.context[subscriptionKey], _ref2;
+ };
+
+ Connect.prototype.componentDidMount = function componentDidMount() {
+ if (!shouldHandleStateChanges) return;
+
+ // componentWillMount fires during server side rendering, but componentDidMount and
+ // componentWillUnmount do not. Because of this, trySubscribe happens during ...didMount.
+ // Otherwise, unsubscription would never take place during SSR, causing a memory leak.
+ // To handle the case where a child component may have triggered a state change by
+ // dispatching an action in its componentWillMount, we have to re-run the select and maybe
+ // re-render.
+ this.subscription.trySubscribe();
+ this.selector.run(this.props);
+ if (this.selector.shouldComponentUpdate) this.forceUpdate();
+ };
+
+ Connect.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
+ this.selector.run(nextProps);
+ };
+
+ Connect.prototype.shouldComponentUpdate = function shouldComponentUpdate() {
+ return this.selector.shouldComponentUpdate;
+ };
+
+ Connect.prototype.componentWillUnmount = function componentWillUnmount() {
+ if (this.subscription) this.subscription.tryUnsubscribe();
+ this.subscription = null;
+ this.notifyNestedSubs = noop;
+ this.store = null;
+ this.selector.run = noop;
+ this.selector.shouldComponentUpdate = false;
+ };
+
+ Connect.prototype.getWrappedInstance = function getWrappedInstance() {
+ invariant_1$2(withRef, 'To access the wrapped instance, you need to specify ' + ('{ withRef: true } in the options argument of the ' + methodName + '() call.'));
+ return this.wrappedInstance;
+ };
+
+ Connect.prototype.setWrappedInstance = function setWrappedInstance(ref) {
+ this.wrappedInstance = ref;
+ };
+
+ Connect.prototype.initSelector = function initSelector() {
+ var sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions);
+ this.selector = makeSelectorStateful(sourceSelector, this.store);
+ this.selector.run(this.props);
+ };
+
+ Connect.prototype.initSubscription = function initSubscription() {
+ if (!shouldHandleStateChanges) return;
+
+ // parentSub's source should match where store came from: props vs. context. A component
+ // connected to the store via props shouldn't use subscription from context, or vice versa.
+ var parentSub = (this.propsMode ? this.props : this.context)[subscriptionKey];
+ this.subscription = new Subscription(this.store, parentSub, this.onStateChange.bind(this));
+
+ // `notifyNestedSubs` is duplicated to handle the case where the component is unmounted in
+ // the middle of the notification loop, where `this.subscription` will then be null. An
+ // extra null check every change can be avoided by copying the method onto `this` and then
+ // replacing it with a no-op on unmount. This can probably be avoided if Subscription's
+ // listeners logic is changed to not call listeners that have been unsubscribed in the
+ // middle of the notification loop.
+ this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription);
+ };
+
+ Connect.prototype.onStateChange = function onStateChange() {
+ this.selector.run(this.props);
+
+ if (!this.selector.shouldComponentUpdate) {
+ this.notifyNestedSubs();
+ } else {
+ this.componentDidUpdate = this.notifyNestedSubsOnComponentDidUpdate;
+ this.setState(dummyState);
+ }
+ };
+
+ Connect.prototype.notifyNestedSubsOnComponentDidUpdate = function notifyNestedSubsOnComponentDidUpdate() {
+ // `componentDidUpdate` is conditionally implemented when `onStateChange` determines it
+ // needs to notify nested subs. Once called, it unimplements itself until further state
+ // changes occur. Doing it this way vs having a permanent `componentDidUpdate` that does
+ // a boolean check every time avoids an extra method call most of the time, resulting
+ // in some perf boost.
+ this.componentDidUpdate = undefined;
+ this.notifyNestedSubs();
+ };
+
+ Connect.prototype.isSubscribed = function isSubscribed() {
+ return Boolean(this.subscription) && this.subscription.isSubscribed();
+ };
+
+ Connect.prototype.addExtraProps = function addExtraProps(props) {
+ if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props;
+ // make a shallow copy so that fields added don't leak to the original selector.
+ // this is especially important for 'ref' since that's a reference back to the component
+ // instance. a singleton memoized selector would then be holding a reference to the
+ // instance, preventing the instance from being garbage collected, and that would be bad
+ var withExtras = _extends({}, props);
+ if (withRef) withExtras.ref = this.setWrappedInstance;
+ if (renderCountProp) withExtras[renderCountProp] = this.renderCount++;
+ if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription;
+ return withExtras;
+ };
+
+ Connect.prototype.render = function render() {
+ var selector = this.selector;
+ selector.shouldComponentUpdate = false;
+
+ if (selector.error) {
+ throw selector.error;
+ } else {
+ return react.createElement(WrappedComponent, this.addExtraProps(selector.props));
+ }
+ };
+
+ return Connect;
+ }(react.Component);
+
+ Connect.WrappedComponent = WrappedComponent;
+ Connect.displayName = displayName;
+ Connect.childContextTypes = childContextTypes;
+ Connect.contextTypes = contextTypes;
+ Connect.propTypes = contextTypes;
+
+ {
+ Connect.prototype.componentWillUpdate = function componentWillUpdate() {
+ var _this2 = this;
+
+ // We are hot reloading!
+ if (this.version !== version) {
+ this.version = version;
+ this.initSelector();
+
+ // If any connected descendants don't hot reload (and resubscribe in the process), their
+ // listeners will be lost when we unsubscribe. Unfortunately, by copying over all
+ // listeners, this does mean that the old versions of connected descendants will still be
+ // notified of state changes; however, their onStateChange function is a no-op so this
+ // isn't a huge deal.
+ var oldListeners = [];
+
+ if (this.subscription) {
+ oldListeners = this.subscription.listeners.get();
+ this.subscription.tryUnsubscribe();
+ }
+ this.initSubscription();
+ if (shouldHandleStateChanges) {
+ this.subscription.trySubscribe();
+ oldListeners.forEach(function (listener) {
+ return _this2.subscription.listeners.subscribe(listener);
+ });
+ }
+ }
+ };
+ }
+
+ return hoistNonReactStatics(Connect, WrappedComponent);
+ };
+}
+
+var hasOwn = Object.prototype.hasOwnProperty;
+
+function is(x, y) {
+ if (x === y) {
+ return x !== 0 || y !== 0 || 1 / x === 1 / y;
+ } else {
+ return x !== x && y !== y;
+ }
+}
+
+function shallowEqual(objA, objB) {
+ if (is(objA, objB)) return true;
+
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
+ return false;
+ }
+
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
+
+ if (keysA.length !== keysB.length) return false;
+
+ for (var i = 0; i < keysA.length; i++) {
+ if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/** Detect free variable `global` from Node.js. */
+var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+
+/** Detect free variable `self`. */
+var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+/** Used as a reference to the global object. */
+var root = freeGlobal || freeSelf || globalThis;
+
+/** Built-in value references. */
+var Symbol$1 = root.Symbol;
+
+/** Used for built-in method references. */
+var objectProto = Object.prototype;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty$1 = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var nativeObjectToString = objectProto.toString;
+
+/** Built-in value references. */
+var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
+
+/**
+ * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the raw `toStringTag`.
+ */
+function getRawTag(value) {
+ var isOwn = hasOwnProperty$1.call(value, symToStringTag),
+ tag = value[symToStringTag];
+
+ try {
+ value[symToStringTag] = undefined;
+ var unmasked = true;
+ } catch (e) {}
+
+ var result = nativeObjectToString.call(value);
+ if (unmasked) {
+ if (isOwn) {
+ value[symToStringTag] = tag;
+ } else {
+ delete value[symToStringTag];
+ }
+ }
+ return result;
+}
+
+/** Used for built-in method references. */
+var objectProto$1 = Object.prototype;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var nativeObjectToString$1 = objectProto$1.toString;
+
+/**
+ * Converts `value` to a string using `Object.prototype.toString`.
+ *
+ * @private
+ * @param {*} value The value to convert.
+ * @returns {string} Returns the converted string.
+ */
+function objectToString(value) {
+ return nativeObjectToString$1.call(value);
+}
+
+/** `Object#toString` result references. */
+var nullTag = '[object Null]',
+ undefinedTag = '[object Undefined]';
+
+/** Built-in value references. */
+var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
+
+/**
+ * The base implementation of `getTag` without fallbacks for buggy environments.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+function baseGetTag(value) {
+ if (value == null) {
+ return value === undefined ? undefinedTag : nullTag;
+ }
+ return (symToStringTag$1 && symToStringTag$1 in Object(value))
+ ? getRawTag(value)
+ : objectToString(value);
+}
+
+/**
+ * Creates a unary function that invokes `func` with its argument transformed.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {Function} transform The argument transform.
+ * @returns {Function} Returns the new function.
+ */
+function overArg(func, transform) {
+ return function(arg) {
+ return func(transform(arg));
+ };
+}
+
+/** Built-in value references. */
+var getPrototype = overArg(Object.getPrototypeOf, Object);
+
+/**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+function isObjectLike(value) {
+ return value != null && typeof value == 'object';
+}
+
+/** `Object#toString` result references. */
+var objectTag = '[object Object]';
+
+/** Used for built-in method references. */
+var funcProto = Function.prototype,
+ objectProto$2 = Object.prototype;
+
+/** Used to resolve the decompiled source of functions. */
+var funcToString = funcProto.toString;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty$2 = objectProto$2.hasOwnProperty;
+
+/** Used to infer the `Object` constructor. */
+var objectCtorString = funcToString.call(Object);
+
+/**
+ * Checks if `value` is a plain object, that is, an object created by the
+ * `Object` constructor or one with a `[[Prototype]]` of `null`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.8.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * }
+ *
+ * _.isPlainObject(new Foo);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ *
+ * _.isPlainObject(Object.create(null));
+ * // => true
+ */
+function isPlainObject(value) {
+ if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
+ return false;
+ }
+ var proto = getPrototype(value);
+ if (proto === null) {
+ return true;
+ }
+ var Ctor = hasOwnProperty$2.call(proto, 'constructor') && proto.constructor;
+ return typeof Ctor == 'function' && Ctor instanceof Ctor &&
+ funcToString.call(Ctor) == objectCtorString;
+}
+
+function verifyPlainObject(value, displayName, methodName) {
+ if (!isPlainObject(value)) {
+ warning$2(methodName + '() in ' + displayName + ' must return a plain object. Instead received ' + value + '.');
+ }
+}
+
+function wrapMapToPropsConstant(getConstant) {
+ return function initConstantSelector(dispatch, options) {
+ var constant = getConstant(dispatch, options);
+
+ function constantSelector() {
+ return constant;
+ }
+ constantSelector.dependsOnOwnProps = false;
+ return constantSelector;
+ };
+}
+
+// dependsOnOwnProps is used by createMapToPropsProxy to determine whether to pass props as args
+// to the mapToProps function being wrapped. It is also used by makePurePropsSelector to determine
+// whether mapToProps needs to be invoked when props have changed.
+//
+// A length of one signals that mapToProps does not depend on props from the parent component.
+// A length of zero is assumed to mean mapToProps is getting args via arguments or ...args and
+// therefore not reporting its length accurately..
+function getDependsOnOwnProps(mapToProps) {
+ return mapToProps.dependsOnOwnProps !== null && mapToProps.dependsOnOwnProps !== undefined ? Boolean(mapToProps.dependsOnOwnProps) : mapToProps.length !== 1;
+}
+
+// Used by whenMapStateToPropsIsFunction and whenMapDispatchToPropsIsFunction,
+// this function wraps mapToProps in a proxy function which does several things:
+//
+// * Detects whether the mapToProps function being called depends on props, which
+// is used by selectorFactory to decide if it should reinvoke on props changes.
+//
+// * On first call, handles mapToProps if returns another function, and treats that
+// new function as the true mapToProps for subsequent calls.
+//
+// * On first call, verifies the first result is a plain object, in order to warn
+// the developer that their mapToProps function is not returning a valid result.
+//
+function wrapMapToPropsFunc(mapToProps, methodName) {
+ return function initProxySelector(dispatch, _ref) {
+ var displayName = _ref.displayName;
+
+ var proxy = function mapToPropsProxy(stateOrDispatch, ownProps) {
+ return proxy.dependsOnOwnProps ? proxy.mapToProps(stateOrDispatch, ownProps) : proxy.mapToProps(stateOrDispatch);
+ };
+
+ // allow detectFactoryAndVerify to get ownProps
+ proxy.dependsOnOwnProps = true;
+
+ proxy.mapToProps = function detectFactoryAndVerify(stateOrDispatch, ownProps) {
+ proxy.mapToProps = mapToProps;
+ proxy.dependsOnOwnProps = getDependsOnOwnProps(mapToProps);
+ var props = proxy(stateOrDispatch, ownProps);
+
+ if (typeof props === 'function') {
+ proxy.mapToProps = props;
+ proxy.dependsOnOwnProps = getDependsOnOwnProps(props);
+ props = proxy(stateOrDispatch, ownProps);
+ }
+
+ verifyPlainObject(props, displayName, methodName);
+
+ return props;
+ };
+
+ return proxy;
+ };
+}
+
+function whenMapDispatchToPropsIsFunction(mapDispatchToProps) {
+ return typeof mapDispatchToProps === 'function' ? wrapMapToPropsFunc(mapDispatchToProps, 'mapDispatchToProps') : undefined;
+}
+
+function whenMapDispatchToPropsIsMissing(mapDispatchToProps) {
+ return !mapDispatchToProps ? wrapMapToPropsConstant(function (dispatch) {
+ return { dispatch: dispatch };
+ }) : undefined;
+}
+
+function whenMapDispatchToPropsIsObject(mapDispatchToProps) {
+ return mapDispatchToProps && typeof mapDispatchToProps === 'object' ? wrapMapToPropsConstant(function (dispatch) {
+ return redux.bindActionCreators(mapDispatchToProps, dispatch);
+ }) : undefined;
+}
+
+var defaultMapDispatchToPropsFactories = [whenMapDispatchToPropsIsFunction, whenMapDispatchToPropsIsMissing, whenMapDispatchToPropsIsObject];
+
+function whenMapStateToPropsIsFunction(mapStateToProps) {
+ return typeof mapStateToProps === 'function' ? wrapMapToPropsFunc(mapStateToProps, 'mapStateToProps') : undefined;
+}
+
+function whenMapStateToPropsIsMissing(mapStateToProps) {
+ return !mapStateToProps ? wrapMapToPropsConstant(function () {
+ return {};
+ }) : undefined;
+}
+
+var defaultMapStateToPropsFactories = [whenMapStateToPropsIsFunction, whenMapStateToPropsIsMissing];
+
+function defaultMergeProps(stateProps, dispatchProps, ownProps) {
+ return _extends({}, ownProps, stateProps, dispatchProps);
+}
+
+function wrapMergePropsFunc(mergeProps) {
+ return function initMergePropsProxy(dispatch, _ref) {
+ var displayName = _ref.displayName,
+ pure = _ref.pure,
+ areMergedPropsEqual = _ref.areMergedPropsEqual;
+
+ var hasRunOnce = false;
+ var mergedProps = void 0;
+
+ return function mergePropsProxy(stateProps, dispatchProps, ownProps) {
+ var nextMergedProps = mergeProps(stateProps, dispatchProps, ownProps);
+
+ if (hasRunOnce) {
+ if (!pure || !areMergedPropsEqual(nextMergedProps, mergedProps)) mergedProps = nextMergedProps;
+ } else {
+ hasRunOnce = true;
+ mergedProps = nextMergedProps;
+
+ verifyPlainObject(mergedProps, displayName, 'mergeProps');
+ }
+
+ return mergedProps;
+ };
+ };
+}
+
+function whenMergePropsIsFunction(mergeProps) {
+ return typeof mergeProps === 'function' ? wrapMergePropsFunc(mergeProps) : undefined;
+}
+
+function whenMergePropsIsOmitted(mergeProps) {
+ return !mergeProps ? function () {
+ return defaultMergeProps;
+ } : undefined;
+}
+
+var defaultMergePropsFactories = [whenMergePropsIsFunction, whenMergePropsIsOmitted];
+
+function verify(selector, methodName, displayName) {
+ if (!selector) {
+ throw new Error('Unexpected value for ' + methodName + ' in ' + displayName + '.');
+ } else if (methodName === 'mapStateToProps' || methodName === 'mapDispatchToProps') {
+ if (!selector.hasOwnProperty('dependsOnOwnProps')) {
+ warning$2('The selector for ' + methodName + ' of ' + displayName + ' did not specify a value for dependsOnOwnProps.');
+ }
+ }
+}
+
+function verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, displayName) {
+ verify(mapStateToProps, 'mapStateToProps', displayName);
+ verify(mapDispatchToProps, 'mapDispatchToProps', displayName);
+ verify(mergeProps, 'mergeProps', displayName);
+}
+
+function impureFinalPropsSelectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch) {
+ return function impureFinalPropsSelector(state, ownProps) {
+ return mergeProps(mapStateToProps(state, ownProps), mapDispatchToProps(dispatch, ownProps), ownProps);
+ };
+}
+
+function pureFinalPropsSelectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch, _ref) {
+ var areStatesEqual = _ref.areStatesEqual,
+ areOwnPropsEqual = _ref.areOwnPropsEqual,
+ areStatePropsEqual = _ref.areStatePropsEqual;
+
+ var hasRunAtLeastOnce = false;
+ var state = void 0;
+ var ownProps = void 0;
+ var stateProps = void 0;
+ var dispatchProps = void 0;
+ var mergedProps = void 0;
+
+ function handleFirstCall(firstState, firstOwnProps) {
+ state = firstState;
+ ownProps = firstOwnProps;
+ stateProps = mapStateToProps(state, ownProps);
+ dispatchProps = mapDispatchToProps(dispatch, ownProps);
+ mergedProps = mergeProps(stateProps, dispatchProps, ownProps);
+ hasRunAtLeastOnce = true;
+ return mergedProps;
+ }
+
+ function handleNewPropsAndNewState() {
+ stateProps = mapStateToProps(state, ownProps);
+
+ if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps);
+
+ mergedProps = mergeProps(stateProps, dispatchProps, ownProps);
+ return mergedProps;
+ }
+
+ function handleNewProps() {
+ if (mapStateToProps.dependsOnOwnProps) stateProps = mapStateToProps(state, ownProps);
+
+ if (mapDispatchToProps.dependsOnOwnProps) dispatchProps = mapDispatchToProps(dispatch, ownProps);
+
+ mergedProps = mergeProps(stateProps, dispatchProps, ownProps);
+ return mergedProps;
+ }
+
+ function handleNewState() {
+ var nextStateProps = mapStateToProps(state, ownProps);
+ var statePropsChanged = !areStatePropsEqual(nextStateProps, stateProps);
+ stateProps = nextStateProps;
+
+ if (statePropsChanged) mergedProps = mergeProps(stateProps, dispatchProps, ownProps);
+
+ return mergedProps;
+ }
+
+ function handleSubsequentCalls(nextState, nextOwnProps) {
+ var propsChanged = !areOwnPropsEqual(nextOwnProps, ownProps);
+ var stateChanged = !areStatesEqual(nextState, state);
+ state = nextState;
+ ownProps = nextOwnProps;
+
+ if (propsChanged && stateChanged) return handleNewPropsAndNewState();
+ if (propsChanged) return handleNewProps();
+ if (stateChanged) return handleNewState();
+ return mergedProps;
+ }
+
+ return function pureFinalPropsSelector(nextState, nextOwnProps) {
+ return hasRunAtLeastOnce ? handleSubsequentCalls(nextState, nextOwnProps) : handleFirstCall(nextState, nextOwnProps);
+ };
+}
+
+// TODO: Add more comments
+
+// If pure is true, the selector returned by selectorFactory will memoize its results,
+// allowing connectAdvanced's shouldComponentUpdate to return false if final
+// props have not changed. If false, the selector will always return a new
+// object and shouldComponentUpdate will always return true.
+
+function finalPropsSelectorFactory(dispatch, _ref2) {
+ var initMapStateToProps = _ref2.initMapStateToProps,
+ initMapDispatchToProps = _ref2.initMapDispatchToProps,
+ initMergeProps = _ref2.initMergeProps,
+ options = objectWithoutProperties(_ref2, ['initMapStateToProps', 'initMapDispatchToProps', 'initMergeProps']);
+
+ var mapStateToProps = initMapStateToProps(dispatch, options);
+ var mapDispatchToProps = initMapDispatchToProps(dispatch, options);
+ var mergeProps = initMergeProps(dispatch, options);
+
+ {
+ verifySubselectors(mapStateToProps, mapDispatchToProps, mergeProps, options.displayName);
+ }
+
+ var selectorFactory = options.pure ? pureFinalPropsSelectorFactory : impureFinalPropsSelectorFactory;
+
+ return selectorFactory(mapStateToProps, mapDispatchToProps, mergeProps, dispatch, options);
+}
+
+/*
+ connect is a facade over connectAdvanced. It turns its args into a compatible
+ selectorFactory, which has the signature:
+
+ (dispatch, options) => (nextState, nextOwnProps) => nextFinalProps
+
+ connect passes its args to connectAdvanced as options, which will in turn pass them to
+ selectorFactory each time a Connect component instance is instantiated or hot reloaded.
+
+ selectorFactory returns a final props selector from its mapStateToProps,
+ mapStateToPropsFactories, mapDispatchToProps, mapDispatchToPropsFactories, mergeProps,
+ mergePropsFactories, and pure args.
+
+ The resulting final props selector is called by the Connect component instance whenever
+ it receives new props or store state.
+ */
+
+function match(arg, factories, name) {
+ for (var i = factories.length - 1; i >= 0; i--) {
+ var result = factories[i](arg);
+ if (result) return result;
+ }
+
+ return function (dispatch, options) {
+ throw new Error('Invalid value of type ' + typeof arg + ' for ' + name + ' argument when connecting component ' + options.wrappedComponentName + '.');
+ };
+}
+
+function strictEqual(a, b) {
+ return a === b;
+}
+
+// createConnect with default args builds the 'official' connect behavior. Calling it with
+// different options opens up some testing and extensibility scenarios
+function createConnect() {
+ var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
+ _ref$connectHOC = _ref.connectHOC,
+ connectHOC = _ref$connectHOC === undefined ? connectAdvanced : _ref$connectHOC,
+ _ref$mapStateToPropsF = _ref.mapStateToPropsFactories,
+ mapStateToPropsFactories = _ref$mapStateToPropsF === undefined ? defaultMapStateToPropsFactories : _ref$mapStateToPropsF,
+ _ref$mapDispatchToPro = _ref.mapDispatchToPropsFactories,
+ mapDispatchToPropsFactories = _ref$mapDispatchToPro === undefined ? defaultMapDispatchToPropsFactories : _ref$mapDispatchToPro,
+ _ref$mergePropsFactor = _ref.mergePropsFactories,
+ mergePropsFactories = _ref$mergePropsFactor === undefined ? defaultMergePropsFactories : _ref$mergePropsFactor,
+ _ref$selectorFactory = _ref.selectorFactory,
+ selectorFactory = _ref$selectorFactory === undefined ? finalPropsSelectorFactory : _ref$selectorFactory;
+
+ return function connect(mapStateToProps, mapDispatchToProps, mergeProps) {
+ var _ref2 = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {},
+ _ref2$pure = _ref2.pure,
+ pure = _ref2$pure === undefined ? true : _ref2$pure,
+ _ref2$areStatesEqual = _ref2.areStatesEqual,
+ areStatesEqual = _ref2$areStatesEqual === undefined ? strictEqual : _ref2$areStatesEqual,
+ _ref2$areOwnPropsEqua = _ref2.areOwnPropsEqual,
+ areOwnPropsEqual = _ref2$areOwnPropsEqua === undefined ? shallowEqual : _ref2$areOwnPropsEqua,
+ _ref2$areStatePropsEq = _ref2.areStatePropsEqual,
+ areStatePropsEqual = _ref2$areStatePropsEq === undefined ? shallowEqual : _ref2$areStatePropsEq,
+ _ref2$areMergedPropsE = _ref2.areMergedPropsEqual,
+ areMergedPropsEqual = _ref2$areMergedPropsE === undefined ? shallowEqual : _ref2$areMergedPropsE,
+ extraOptions = objectWithoutProperties(_ref2, ['pure', 'areStatesEqual', 'areOwnPropsEqual', 'areStatePropsEqual', 'areMergedPropsEqual']);
+
+ var initMapStateToProps = match(mapStateToProps, mapStateToPropsFactories, 'mapStateToProps');
+ var initMapDispatchToProps = match(mapDispatchToProps, mapDispatchToPropsFactories, 'mapDispatchToProps');
+ var initMergeProps = match(mergeProps, mergePropsFactories, 'mergeProps');
+
+ return connectHOC(selectorFactory, _extends({
+ // used in error messages
+ methodName: 'connect',
+
+ // used to compute Connect's displayName from the wrapped component's displayName.
+ getDisplayName: function getDisplayName(name) {
+ return 'Connect(' + name + ')';
+ },
+
+ // if mapStateToProps is falsy, the Connect component doesn't subscribe to store state changes
+ shouldHandleStateChanges: Boolean(mapStateToProps),
+
+ // passed through to selectorFactory
+ initMapStateToProps: initMapStateToProps,
+ initMapDispatchToProps: initMapDispatchToProps,
+ initMergeProps: initMergeProps,
+ pure: pure,
+ areStatesEqual: areStatesEqual,
+ areOwnPropsEqual: areOwnPropsEqual,
+ areStatePropsEqual: areStatePropsEqual,
+ areMergedPropsEqual: areMergedPropsEqual
+
+ }, extraOptions));
+ };
+}
+
+var connect = createConnect();
+
+exports.Provider = Provider;
+exports.createProvider = createProvider;
+exports.connectAdvanced = connectAdvanced;
+exports.connect = connect;
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/devtools/client/shared/vendor/react-router-dom.js b/devtools/client/shared/vendor/react-router-dom.js
new file mode 100644
index 0000000000..d5d10eb01e
--- /dev/null
+++ b/devtools/client/shared/vendor/react-router-dom.js
@@ -0,0 +1,3788 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require("resource://devtools/client/shared/vendor/react.js")) :
+ typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) :
+ (factory((global.ReactRouterDOM = {}),global.React));
+}(this, (function (exports,React) { 'use strict';
+
+ React = React && React.hasOwnProperty('default') ? React['default'] : React;
+
+ /**
+ * Copyright (c) 2014-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @providesModule warning
+ */
+
+ var warning = function () {};
+
+ {
+ var printWarning = function printWarning(format, args) {
+ var len = arguments.length;
+ args = new Array(len > 2 ? len - 2 : 0);
+ for (var key = 2; key < len; key++) {
+ args[key - 2] = arguments[key];
+ }
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ warning = function (condition, format, args) {
+ var len = arguments.length;
+ args = new Array(len > 2 ? len - 2 : 0);
+ for (var key = 2; key < len; key++) {
+ args[key - 2] = arguments[key];
+ }
+ if (format === undefined) {
+ throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+ if (!condition) {
+ printWarning.apply(null, [format].concat(args));
+ }
+ };
+ }
+
+ var warning_1 = warning;
+
+ var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+
+ function createCommonjsModule(fn, module) {
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
+ }
+
+ /**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ *
+ */
+
+ function makeEmptyFunction(arg) {
+ return function () {
+ return arg;
+ };
+ }
+
+ /**
+ * This function accepts and discards inputs; it has no side effects. This is
+ * primarily useful idiomatically for overridable function endpoints which
+ * always need to be callable, since JS lacks a null-call idiom ala Cocoa.
+ */
+ var emptyFunction = function emptyFunction() {};
+
+ emptyFunction.thatReturns = makeEmptyFunction;
+ emptyFunction.thatReturnsFalse = makeEmptyFunction(false);
+ emptyFunction.thatReturnsTrue = makeEmptyFunction(true);
+ emptyFunction.thatReturnsNull = makeEmptyFunction(null);
+ emptyFunction.thatReturnsThis = function () {
+ return this;
+ };
+ emptyFunction.thatReturnsArgument = function (arg) {
+ return arg;
+ };
+
+ var emptyFunction_1 = emptyFunction;
+
+ /**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ */
+
+ /**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+ var validateFormat = function validateFormat(format) {};
+
+ {
+ validateFormat = function validateFormat(format) {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ };
+ }
+
+ function invariant(condition, format, a, b, c, d, e, f) {
+ validateFormat(format);
+
+ if (!condition) {
+ var error;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+ }
+
+ var invariant_1 = invariant;
+
+ /**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+ var warning$1 = emptyFunction_1;
+
+ {
+ var printWarning$1 = function printWarning(format) {
+ for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message = 'Warning: ' + format.replace(/%s/g, function () {
+ return args[argIndex++];
+ });
+ if (typeof console !== 'undefined') {
+ console.error(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ warning$1 = function warning(condition, format) {
+ if (format === undefined) {
+ throw new Error('`warning(condition, format, ...args)` requires a warning ' + 'message argument');
+ }
+
+ if (format.indexOf('Failed Composite propType: ') === 0) {
+ return; // Ignore CompositeComponent proptype check.
+ }
+
+ if (!condition) {
+ for (var _len2 = arguments.length, args = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning$1.apply(undefined, [format].concat(args));
+ }
+ };
+ }
+
+ var warning_1$1 = warning$1;
+
+ /*
+ object-assign
+ (c) Sindre Sorhus
+ @license MIT
+ */
+ /* eslint-disable no-unused-vars */
+
+ var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+ var hasOwnProperty = Object.prototype.hasOwnProperty;
+ var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+ function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+ }
+
+ function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !== 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+ }
+
+ var objectAssign = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+ };
+
+ /**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+ var ReactPropTypesSecret = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
+
+ var ReactPropTypesSecret_1 = ReactPropTypesSecret;
+
+ {
+ var invariant$1 = invariant_1;
+ var warning$2 = warning_1$1;
+ var ReactPropTypesSecret$1 = ReactPropTypesSecret_1;
+ var loggedTypeFailures = {};
+ }
+
+ /**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+ function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+ {
+ for (var typeSpecName in typeSpecs) {
+ if (typeSpecs.hasOwnProperty(typeSpecName)) {
+ var error;
+ // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ invariant$1(typeof typeSpecs[typeSpecName] === 'function', '%s: %s type `%s` is invalid; it must be a function, usually from ' + 'the `prop-types` package, but received `%s`.', componentName || 'React class', location, typeSpecName, typeof typeSpecs[typeSpecName]);
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret$1);
+ } catch (ex) {
+ error = ex;
+ }
+ warning$2(!error || error instanceof Error, '%s: type specification of %s `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error);
+ if (error instanceof Error && !(error.message in loggedTypeFailures)) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error.message] = true;
+
+ var stack = getStack ? getStack() : '';
+
+ warning$2(false, 'Failed %s type: %s%s', location, error.message, stack != null ? stack : '');
+ }
+ }
+ }
+ }
+ }
+
+ var checkPropTypes_1 = checkPropTypes;
+
+ var factoryWithTypeCheckers = function (isValidElement, throwOnDirectAccess) {
+ /* global Symbol */
+ var ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+ var FAUX_ITERATOR_SYMBOL = '@@iterator'; // Before Symbol spec.
+
+ /**
+ * Returns the iterator method function contained on the iterable object.
+ *
+ * Be sure to invoke the function with the iterable as context:
+ *
+ * var iteratorFn = getIteratorFn(myIterable);
+ * if (iteratorFn) {
+ * var iterator = iteratorFn.call(myIterable);
+ * ...
+ * }
+ *
+ * @param {?object} maybeIterable
+ * @return {?function}
+ */
+ function getIteratorFn(maybeIterable) {
+ var iteratorFn = maybeIterable && (ITERATOR_SYMBOL && maybeIterable[ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]);
+ if (typeof iteratorFn === 'function') {
+ return iteratorFn;
+ }
+ }
+
+ /**
+ * Collection of methods that allow declaration and validation of props that are
+ * supplied to React components. Example usage:
+ *
+ * var Props = require('ReactPropTypes');
+ * var MyArticle = React.createClass({
+ * propTypes: {
+ * // An optional string prop named "description".
+ * description: Props.string,
+ *
+ * // A required enum prop named "category".
+ * category: Props.oneOf(['News','Photos']).isRequired,
+ *
+ * // A prop named "dialog" that requires an instance of Dialog.
+ * dialog: Props.instanceOf(Dialog).isRequired
+ * },
+ * render: function() { ... }
+ * });
+ *
+ * A more formal specification of how these methods are used:
+ *
+ * type := array|bool|func|object|number|string|oneOf([...])|instanceOf(...)
+ * decl := ReactPropTypes.{type}(.isRequired)?
+ *
+ * Each and every declaration produces a function with the same signature. This
+ * allows the creation of custom validation functions. For example:
+ *
+ * var MyLink = React.createClass({
+ * propTypes: {
+ * // An optional string or URI prop named "href".
+ * href: function(props, propName, componentName) {
+ * var propValue = props[propName];
+ * if (propValue != null && typeof propValue !== 'string' &&
+ * !(propValue instanceof URI)) {
+ * return new Error(
+ * 'Expected a string or an URI for ' + propName + ' in ' +
+ * componentName
+ * );
+ * }
+ * }
+ * },
+ * render: function() {...}
+ * });
+ *
+ * @internal
+ */
+
+ var ANONYMOUS = '<<anonymous>>';
+
+ // Important!
+ // Keep this list in sync with production version in `./factoryWithThrowingShims.js`.
+ var ReactPropTypes = {
+ array: createPrimitiveTypeChecker('array'),
+ bool: createPrimitiveTypeChecker('boolean'),
+ func: createPrimitiveTypeChecker('function'),
+ number: createPrimitiveTypeChecker('number'),
+ object: createPrimitiveTypeChecker('object'),
+ string: createPrimitiveTypeChecker('string'),
+ symbol: createPrimitiveTypeChecker('symbol'),
+
+ any: createAnyTypeChecker(),
+ arrayOf: createArrayOfTypeChecker,
+ element: createElementTypeChecker(),
+ instanceOf: createInstanceTypeChecker,
+ node: createNodeChecker(),
+ objectOf: createObjectOfTypeChecker,
+ oneOf: createEnumTypeChecker,
+ oneOfType: createUnionTypeChecker,
+ shape: createShapeTypeChecker,
+ exact: createStrictShapeTypeChecker
+ };
+
+ /**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+ /*eslint-disable no-self-compare*/
+ function is(x, y) {
+ // SameValue algorithm
+ if (x === y) {
+ // Steps 1-5, 7-10
+ // Steps 6.b-6.e: +0 != -0
+ return x !== 0 || 1 / x === 1 / y;
+ } else {
+ // Step 6.a: NaN == NaN
+ return x !== x && y !== y;
+ }
+ }
+ /*eslint-enable no-self-compare*/
+
+ /**
+ * We use an Error-like object for backward compatibility as people may call
+ * PropTypes directly and inspect their output. However, we don't use real
+ * Errors anymore. We don't inspect their stack anyway, and creating them
+ * is prohibitively expensive if they are created too often, such as what
+ * happens in oneOfType() for any type before the one that matched.
+ */
+ function PropTypeError(message) {
+ this.message = message;
+ this.stack = '';
+ }
+ // Make `instanceof Error` still work for returned errors.
+ PropTypeError.prototype = Error.prototype;
+
+ function createChainableTypeChecker(validate) {
+ {
+ var manualPropTypeCallCache = {};
+ var manualPropTypeWarningCount = 0;
+ }
+ function checkType(isRequired, props, propName, componentName, location, propFullName, secret) {
+ componentName = componentName || ANONYMOUS;
+ propFullName = propFullName || propName;
+
+ if (secret !== ReactPropTypesSecret_1) {
+ if (throwOnDirectAccess) {
+ // New behavior only for users of `prop-types` package
+ invariant_1(false, 'Calling PropTypes validators directly is not supported by the `prop-types` package. ' + 'Use `PropTypes.checkPropTypes()` to call them. ' + 'Read more at http://fb.me/use-check-prop-types');
+ } else if (typeof console !== 'undefined') {
+ // Old behavior for people using React.PropTypes
+ var cacheKey = componentName + ':' + propName;
+ if (!manualPropTypeCallCache[cacheKey] &&
+ // Avoid spamming the console because they are often not actionable except for lib authors
+ manualPropTypeWarningCount < 3) {
+ warning_1$1(false, 'You are manually calling a React.PropTypes validation ' + 'function for the `%s` prop on `%s`. This is deprecated ' + 'and will throw in the standalone `prop-types` package. ' + 'You may be seeing this warning due to a third-party PropTypes ' + 'library. See https://fb.me/react-warning-dont-call-proptypes ' + 'for details.', propFullName, componentName);
+ manualPropTypeCallCache[cacheKey] = true;
+ manualPropTypeWarningCount++;
+ }
+ }
+ }
+ if (props[propName] == null) {
+ if (isRequired) {
+ if (props[propName] === null) {
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required ' + ('in `' + componentName + '`, but its value is `null`.'));
+ }
+ return new PropTypeError('The ' + location + ' `' + propFullName + '` is marked as required in ' + ('`' + componentName + '`, but its value is `undefined`.'));
+ }
+ return null;
+ } else {
+ return validate(props, propName, componentName, location, propFullName);
+ }
+ }
+
+ var chainedCheckType = checkType.bind(null, false);
+ chainedCheckType.isRequired = checkType.bind(null, true);
+
+ return chainedCheckType;
+ }
+
+ function createPrimitiveTypeChecker(expectedType) {
+ function validate(props, propName, componentName, location, propFullName, secret) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== expectedType) {
+ // `propValue` being instance of, say, date/regexp, pass the 'object'
+ // check, but we can offer a more precise error message here rather than
+ // 'of type `object`'.
+ var preciseType = getPreciseType(propValue);
+
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + preciseType + '` supplied to `' + componentName + '`, expected ') + ('`' + expectedType + '`.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createAnyTypeChecker() {
+ return createChainableTypeChecker(emptyFunction_1.thatReturnsNull);
+ }
+
+ function createArrayOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside arrayOf.');
+ }
+ var propValue = props[propName];
+ if (!Array.isArray(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an array.'));
+ }
+ for (var i = 0; i < propValue.length; i++) {
+ var error = typeChecker(propValue, i, componentName, location, propFullName + '[' + i + ']', ReactPropTypesSecret_1);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createElementTypeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ if (!isValidElement(propValue)) {
+ var propType = getPropType(propValue);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected a single ReactElement.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createInstanceTypeChecker(expectedClass) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!(props[propName] instanceof expectedClass)) {
+ var expectedClassName = expectedClass.name || ANONYMOUS;
+ var actualClassName = getClassName(props[propName]);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + actualClassName + '` supplied to `' + componentName + '`, expected ') + ('instance of `' + expectedClassName + '`.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createEnumTypeChecker(expectedValues) {
+ if (!Array.isArray(expectedValues)) {
+ warning_1$1(false, 'Invalid argument supplied to oneOf, expected an instance of array.');
+ return emptyFunction_1.thatReturnsNull;
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ for (var i = 0; i < expectedValues.length; i++) {
+ if (is(propValue, expectedValues[i])) {
+ return null;
+ }
+ }
+
+ var valuesString = JSON.stringify(expectedValues);
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of value `' + propValue + '` ' + ('supplied to `' + componentName + '`, expected one of ' + valuesString + '.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createObjectOfTypeChecker(typeChecker) {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (typeof typeChecker !== 'function') {
+ return new PropTypeError('Property `' + propFullName + '` of component `' + componentName + '` has invalid PropType notation inside objectOf.');
+ }
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type ' + ('`' + propType + '` supplied to `' + componentName + '`, expected an object.'));
+ }
+ for (var key in propValue) {
+ if (propValue.hasOwnProperty(key)) {
+ var error = typeChecker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret_1);
+ if (error instanceof Error) {
+ return error;
+ }
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createUnionTypeChecker(arrayOfTypeCheckers) {
+ if (!Array.isArray(arrayOfTypeCheckers)) {
+ warning_1$1(false, 'Invalid argument supplied to oneOfType, expected an instance of array.');
+ return emptyFunction_1.thatReturnsNull;
+ }
+
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ if (typeof checker !== 'function') {
+ warning_1$1(false, 'Invalid argument supplied to oneOfType. Expected an array of check functions, but ' + 'received %s at index %s.', getPostfixForTypeWarning(checker), i);
+ return emptyFunction_1.thatReturnsNull;
+ }
+ }
+
+ function validate(props, propName, componentName, location, propFullName) {
+ for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
+ var checker = arrayOfTypeCheckers[i];
+ if (checker(props, propName, componentName, location, propFullName, ReactPropTypesSecret_1) == null) {
+ return null;
+ }
+ }
+
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`.'));
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createNodeChecker() {
+ function validate(props, propName, componentName, location, propFullName) {
+ if (!isNode(props[propName])) {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` supplied to ' + ('`' + componentName + '`, expected a ReactNode.'));
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ for (var key in shapeTypes) {
+ var checker = shapeTypes[key];
+ if (!checker) {
+ continue;
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret_1);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+ return createChainableTypeChecker(validate);
+ }
+
+ function createStrictShapeTypeChecker(shapeTypes) {
+ function validate(props, propName, componentName, location, propFullName) {
+ var propValue = props[propName];
+ var propType = getPropType(propValue);
+ if (propType !== 'object') {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` of type `' + propType + '` ' + ('supplied to `' + componentName + '`, expected `object`.'));
+ }
+ // We need to check all keys in case some are required but missing from
+ // props.
+ var allKeys = objectAssign({}, props[propName], shapeTypes);
+ for (var key in allKeys) {
+ var checker = shapeTypes[key];
+ if (!checker) {
+ return new PropTypeError('Invalid ' + location + ' `' + propFullName + '` key `' + key + '` supplied to `' + componentName + '`.' + '\nBad object: ' + JSON.stringify(props[propName], null, ' ') + '\nValid keys: ' + JSON.stringify(Object.keys(shapeTypes), null, ' '));
+ }
+ var error = checker(propValue, key, componentName, location, propFullName + '.' + key, ReactPropTypesSecret_1);
+ if (error) {
+ return error;
+ }
+ }
+ return null;
+ }
+
+ return createChainableTypeChecker(validate);
+ }
+
+ function isNode(propValue) {
+ switch (typeof propValue) {
+ case 'number':
+ case 'string':
+ case 'undefined':
+ return true;
+ case 'boolean':
+ return !propValue;
+ case 'object':
+ if (Array.isArray(propValue)) {
+ return propValue.every(isNode);
+ }
+ if (propValue === null || isValidElement(propValue)) {
+ return true;
+ }
+
+ var iteratorFn = getIteratorFn(propValue);
+ if (iteratorFn) {
+ var iterator = iteratorFn.call(propValue);
+ var step;
+ if (iteratorFn !== propValue.entries) {
+ while (!(step = iterator.next()).done) {
+ if (!isNode(step.value)) {
+ return false;
+ }
+ }
+ } else {
+ // Iterator will provide entry [k,v] tuples rather than values.
+ while (!(step = iterator.next()).done) {
+ var entry = step.value;
+ if (entry) {
+ if (!isNode(entry[1])) {
+ return false;
+ }
+ }
+ }
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ function isSymbol(propType, propValue) {
+ // Native Symbol.
+ if (propType === 'symbol') {
+ return true;
+ }
+
+ // 19.4.3.5 Symbol.prototype[@@toStringTag] === 'Symbol'
+ if (propValue['@@toStringTag'] === 'Symbol') {
+ return true;
+ }
+
+ // Fallback for non-spec compliant Symbols which are polyfilled.
+ if (typeof Symbol === 'function' && propValue instanceof Symbol) {
+ return true;
+ }
+
+ return false;
+ }
+
+ // Equivalent of `typeof` but with special handling for array and regexp.
+ function getPropType(propValue) {
+ var propType = typeof propValue;
+ if (Array.isArray(propValue)) {
+ return 'array';
+ }
+ if (propValue instanceof RegExp) {
+ // Old webkits (at least until Android 4.0) return 'function' rather than
+ // 'object' for typeof a RegExp. We'll normalize this here so that /bla/
+ // passes PropTypes.object.
+ return 'object';
+ }
+ if (isSymbol(propType, propValue)) {
+ return 'symbol';
+ }
+ return propType;
+ }
+
+ // This handles more types than `getPropType`. Only used for error messages.
+ // See `createPrimitiveTypeChecker`.
+ function getPreciseType(propValue) {
+ if (typeof propValue === 'undefined' || propValue === null) {
+ return '' + propValue;
+ }
+ var propType = getPropType(propValue);
+ if (propType === 'object') {
+ if (propValue instanceof Date) {
+ return 'date';
+ } else if (propValue instanceof RegExp) {
+ return 'regexp';
+ }
+ }
+ return propType;
+ }
+
+ // Returns a string that is postfixed to a warning about an invalid type.
+ // For example, "undefined" or "of type array"
+ function getPostfixForTypeWarning(value) {
+ var type = getPreciseType(value);
+ switch (type) {
+ case 'array':
+ case 'object':
+ return 'an ' + type;
+ case 'boolean':
+ case 'date':
+ case 'regexp':
+ return 'a ' + type;
+ default:
+ return type;
+ }
+ }
+
+ // Returns class name of the object, if any.
+ function getClassName(propValue) {
+ if (!propValue.constructor || !propValue.constructor.name) {
+ return ANONYMOUS;
+ }
+ return propValue.constructor.name;
+ }
+
+ ReactPropTypes.checkPropTypes = checkPropTypes_1;
+ ReactPropTypes.PropTypes = ReactPropTypes;
+
+ return ReactPropTypes;
+ };
+
+ var propTypes = createCommonjsModule(function (module) {
+ /**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+ {
+ var REACT_ELEMENT_TYPE = typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element') || 0xeac7;
+
+ var isValidElement = function (object) {
+ return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
+ };
+
+ // By explicitly using `prop-types` you are opting into new development behavior.
+ // http://fb.me/prop-types-in-prod
+ var throwOnDirectAccess = true;
+ module.exports = factoryWithTypeCheckers(isValidElement, throwOnDirectAccess);
+ }
+ });
+
+ /**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+ var invariant$2 = function (condition, format, a, b, c, d, e, f) {
+ {
+ if (format === undefined) {
+ throw new Error('invariant requires an error message argument');
+ }
+ }
+
+ if (!condition) {
+ var error;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+ };
+
+ var invariant_1$1 = invariant$2;
+
+ function isAbsolute(pathname) {
+ return pathname.charAt(0) === '/';
+ }
+
+ // About 1.5x faster than the two-arg version of Array#splice()
+ function spliceOne(list, index) {
+ for (var i = index, k = i + 1, n = list.length; k < n; i += 1, k += 1) {
+ list[i] = list[k];
+ }
+
+ list.pop();
+ }
+
+ // This implementation is based heavily on node's url.parse
+ function resolvePathname(to) {
+ var from = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
+
+ var toParts = to && to.split('/') || [];
+ var fromParts = from && from.split('/') || [];
+
+ var isToAbs = to && isAbsolute(to);
+ var isFromAbs = from && isAbsolute(from);
+ var mustEndAbs = isToAbs || isFromAbs;
+
+ if (to && isAbsolute(to)) {
+ // to is absolute
+ fromParts = toParts;
+ } else if (toParts.length) {
+ // to is relative, drop the filename
+ fromParts.pop();
+ fromParts = fromParts.concat(toParts);
+ }
+
+ if (!fromParts.length) return '/';
+
+ var hasTrailingSlash = void 0;
+ if (fromParts.length) {
+ var last = fromParts[fromParts.length - 1];
+ hasTrailingSlash = last === '.' || last === '..' || last === '';
+ } else {
+ hasTrailingSlash = false;
+ }
+
+ var up = 0;
+ for (var i = fromParts.length; i >= 0; i--) {
+ var part = fromParts[i];
+
+ if (part === '.') {
+ spliceOne(fromParts, i);
+ } else if (part === '..') {
+ spliceOne(fromParts, i);
+ up++;
+ } else if (up) {
+ spliceOne(fromParts, i);
+ up--;
+ }
+ }
+
+ if (!mustEndAbs) for (; up--; up) {
+ fromParts.unshift('..');
+ }if (mustEndAbs && fromParts[0] !== '' && (!fromParts[0] || !isAbsolute(fromParts[0]))) fromParts.unshift('');
+
+ var result = fromParts.join('/');
+
+ if (hasTrailingSlash && result.substr(-1) !== '/') result += '/';
+
+ return result;
+ }
+
+ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+ } : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+
+ function valueEqual(a, b) {
+ if (a === b) return true;
+
+ if (a == null || b == null) return false;
+
+ if (Array.isArray(a)) {
+ return Array.isArray(b) && a.length === b.length && a.every(function (item, index) {
+ return valueEqual(item, b[index]);
+ });
+ }
+
+ var aType = typeof a === 'undefined' ? 'undefined' : _typeof(a);
+ var bType = typeof b === 'undefined' ? 'undefined' : _typeof(b);
+
+ if (aType !== bType) return false;
+
+ if (aType === 'object') {
+ var aValue = a.valueOf();
+ var bValue = b.valueOf();
+
+ if (aValue !== a || bValue !== b) return valueEqual(aValue, bValue);
+
+ var aKeys = Object.keys(a);
+ var bKeys = Object.keys(b);
+
+ if (aKeys.length !== bKeys.length) return false;
+
+ return aKeys.every(function (key) {
+ return valueEqual(a[key], b[key]);
+ });
+ }
+
+ return false;
+ }
+
+ var addLeadingSlash = function addLeadingSlash(path) {
+ return path.charAt(0) === '/' ? path : '/' + path;
+ };
+
+ var stripLeadingSlash = function stripLeadingSlash(path) {
+ return path.charAt(0) === '/' ? path.substr(1) : path;
+ };
+
+ var hasBasename = function hasBasename(path, prefix) {
+ return new RegExp('^' + prefix + '(\\/|\\?|#|$)', 'i').test(path);
+ };
+
+ var stripBasename = function stripBasename(path, prefix) {
+ return hasBasename(path, prefix) ? path.substr(prefix.length) : path;
+ };
+
+ var stripTrailingSlash = function stripTrailingSlash(path) {
+ return path.charAt(path.length - 1) === '/' ? path.slice(0, -1) : path;
+ };
+
+ var parsePath = function parsePath(path) {
+ var pathname = path || '/';
+ var search = '';
+ var hash = '';
+
+ var hashIndex = pathname.indexOf('#');
+ if (hashIndex !== -1) {
+ hash = pathname.substr(hashIndex);
+ pathname = pathname.substr(0, hashIndex);
+ }
+
+ var searchIndex = pathname.indexOf('?');
+ if (searchIndex !== -1) {
+ search = pathname.substr(searchIndex);
+ pathname = pathname.substr(0, searchIndex);
+ }
+
+ return {
+ pathname: pathname,
+ search: search === '?' ? '' : search,
+ hash: hash === '#' ? '' : hash
+ };
+ };
+
+ var createPath = function createPath(location) {
+ var pathname = location.pathname,
+ search = location.search,
+ hash = location.hash;
+
+ var path = pathname || '/';
+
+ if (search && search !== '?') path += search.charAt(0) === '?' ? search : '?' + search;
+
+ if (hash && hash !== '#') path += hash.charAt(0) === '#' ? hash : '#' + hash;
+
+ return path;
+ };
+
+ var _extends = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ var createLocation = function createLocation(path, state, key, currentLocation) {
+ var location = void 0;
+ if (typeof path === 'string') {
+ // Two-arg form: push(path, state)
+ location = parsePath(path);
+ location.state = state;
+ } else {
+ // One-arg form: push(location)
+ location = _extends({}, path);
+
+ if (location.pathname === undefined) location.pathname = '';
+
+ if (location.search) {
+ if (location.search.charAt(0) !== '?') location.search = '?' + location.search;
+ } else {
+ location.search = '';
+ }
+
+ if (location.hash) {
+ if (location.hash.charAt(0) !== '#') location.hash = '#' + location.hash;
+ } else {
+ location.hash = '';
+ }
+
+ if (state !== undefined && location.state === undefined) location.state = state;
+ }
+
+ try {
+ location.pathname = decodeURI(location.pathname);
+ } catch (e) {
+ if (e instanceof URIError) {
+ throw new URIError('Pathname "' + location.pathname + '" could not be decoded. ' + 'This is likely caused by an invalid percent-encoding.');
+ } else {
+ throw e;
+ }
+ }
+
+ if (key) location.key = key;
+
+ if (currentLocation) {
+ // Resolve incomplete/relative pathname relative to current location.
+ if (!location.pathname) {
+ location.pathname = currentLocation.pathname;
+ } else if (location.pathname.charAt(0) !== '/') {
+ location.pathname = resolvePathname(location.pathname, currentLocation.pathname);
+ }
+ } else {
+ // When there is no prior location and pathname is empty, set it to /
+ if (!location.pathname) {
+ location.pathname = '/';
+ }
+ }
+
+ return location;
+ };
+
+ var locationsAreEqual = function locationsAreEqual(a, b) {
+ return a.pathname === b.pathname && a.search === b.search && a.hash === b.hash && a.key === b.key && valueEqual(a.state, b.state);
+ };
+
+ var createTransitionManager = function createTransitionManager() {
+ var prompt = null;
+
+ var setPrompt = function setPrompt(nextPrompt) {
+ warning_1(prompt == null, 'A history supports only one prompt at a time');
+
+ prompt = nextPrompt;
+
+ return function () {
+ if (prompt === nextPrompt) prompt = null;
+ };
+ };
+
+ var confirmTransitionTo = function confirmTransitionTo(location, action, getUserConfirmation, callback) {
+ // TODO: If another transition starts while we're still confirming
+ // the previous one, we may end up in a weird state. Figure out the
+ // best way to handle this.
+ if (prompt != null) {
+ var result = typeof prompt === 'function' ? prompt(location, action) : prompt;
+
+ if (typeof result === 'string') {
+ if (typeof getUserConfirmation === 'function') {
+ getUserConfirmation(result, callback);
+ } else {
+ warning_1(false, 'A history needs a getUserConfirmation function in order to use a prompt message');
+
+ callback(true);
+ }
+ } else {
+ // Return false from a transition hook to cancel the transition.
+ callback(result !== false);
+ }
+ } else {
+ callback(true);
+ }
+ };
+
+ var listeners = [];
+
+ var appendListener = function appendListener(fn) {
+ var isActive = true;
+
+ var listener = function listener() {
+ if (isActive) fn.apply(undefined, arguments);
+ };
+
+ listeners.push(listener);
+
+ return function () {
+ isActive = false;
+ listeners = listeners.filter(function (item) {
+ return item !== listener;
+ });
+ };
+ };
+
+ var notifyListeners = function notifyListeners() {
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ listeners.forEach(function (listener) {
+ return listener.apply(undefined, args);
+ });
+ };
+
+ return {
+ setPrompt: setPrompt,
+ confirmTransitionTo: confirmTransitionTo,
+ appendListener: appendListener,
+ notifyListeners: notifyListeners
+ };
+ };
+
+ var canUseDOM = !!(typeof window !== 'undefined' && window.document && window.document.createElement);
+
+ var addEventListener = function addEventListener(node, event, listener) {
+ return node.addEventListener ? node.addEventListener(event, listener, false) : node.attachEvent('on' + event, listener);
+ };
+
+ var removeEventListener = function removeEventListener(node, event, listener) {
+ return node.removeEventListener ? node.removeEventListener(event, listener, false) : node.detachEvent('on' + event, listener);
+ };
+
+ var getConfirmation = function getConfirmation(message, callback) {
+ return callback(window.confirm(message));
+ }; // eslint-disable-line no-alert
+
+ /**
+ * Returns true if the HTML5 history API is supported. Taken from Modernizr.
+ *
+ * https://github.com/Modernizr/Modernizr/blob/master/LICENSE
+ * https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
+ * changed to avoid false negatives for Windows Phones: https://github.com/reactjs/react-router/issues/586
+ */
+ var supportsHistory = function supportsHistory() {
+ var ua = window.navigator.userAgent;
+
+ if ((ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) && ua.indexOf('Mobile Safari') !== -1 && ua.indexOf('Chrome') === -1 && ua.indexOf('Windows Phone') === -1) return false;
+
+ return window.history && 'pushState' in window.history;
+ };
+
+ /**
+ * Returns true if browser fires popstate on hash change.
+ * IE10 and IE11 do not.
+ */
+ var supportsPopStateOnHashChange = function supportsPopStateOnHashChange() {
+ return window.navigator.userAgent.indexOf('Trident') === -1;
+ };
+
+ /**
+ * Returns false if using go(n) with hash history causes a full page reload.
+ */
+ var supportsGoWithoutReloadUsingHash = function supportsGoWithoutReloadUsingHash() {
+ return window.navigator.userAgent.indexOf('Firefox') === -1;
+ };
+
+ /**
+ * Returns true if a given popstate event is an extraneous WebKit event.
+ * Accounts for the fact that Chrome on iOS fires real popstate events
+ * containing undefined state when pressing the back button.
+ */
+ var isExtraneousPopstateEvent = function isExtraneousPopstateEvent(event) {
+ return event.state === undefined && navigator.userAgent.indexOf('CriOS') === -1;
+ };
+
+ var _typeof$1 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+ } : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+
+ var _extends$1 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ var PopStateEvent = 'popstate';
+ var HashChangeEvent = 'hashchange';
+
+ var getHistoryState = function getHistoryState() {
+ try {
+ return window.history.state || {};
+ } catch (e) {
+ // IE 11 sometimes throws when accessing window.history.state
+ // See https://github.com/ReactTraining/history/pull/289
+ return {};
+ }
+ };
+
+ /**
+ * Creates a history object that uses the HTML5 history API including
+ * pushState, replaceState, and the popstate event.
+ */
+ var createBrowserHistory = function createBrowserHistory() {
+ var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ invariant_1$1(canUseDOM, 'Browser history needs a DOM');
+
+ var globalHistory = window.history;
+ var canUseHistory = supportsHistory();
+ var needsHashChangeListener = !supportsPopStateOnHashChange();
+
+ var _props$forceRefresh = props.forceRefresh,
+ forceRefresh = _props$forceRefresh === undefined ? false : _props$forceRefresh,
+ _props$getUserConfirm = props.getUserConfirmation,
+ getUserConfirmation = _props$getUserConfirm === undefined ? getConfirmation : _props$getUserConfirm,
+ _props$keyLength = props.keyLength,
+ keyLength = _props$keyLength === undefined ? 6 : _props$keyLength;
+
+ var basename = props.basename ? stripTrailingSlash(addLeadingSlash(props.basename)) : '';
+
+ var getDOMLocation = function getDOMLocation(historyState) {
+ var _ref = historyState || {},
+ key = _ref.key,
+ state = _ref.state;
+
+ var _window$location = window.location,
+ pathname = _window$location.pathname,
+ search = _window$location.search,
+ hash = _window$location.hash;
+
+ var path = pathname + search + hash;
+
+ warning_1(!basename || hasBasename(path, basename), 'You are attempting to use a basename on a page whose URL path does not begin ' + 'with the basename. Expected path "' + path + '" to begin with "' + basename + '".');
+
+ if (basename) path = stripBasename(path, basename);
+
+ return createLocation(path, state, key);
+ };
+
+ var createKey = function createKey() {
+ return Math.random().toString(36).substr(2, keyLength);
+ };
+
+ var transitionManager = createTransitionManager();
+
+ var setState = function setState(nextState) {
+ _extends$1(history, nextState);
+
+ history.length = globalHistory.length;
+
+ transitionManager.notifyListeners(history.location, history.action);
+ };
+
+ var handlePopState = function handlePopState(event) {
+ // Ignore extraneous popstate events in WebKit.
+ if (isExtraneousPopstateEvent(event)) return;
+
+ handlePop(getDOMLocation(event.state));
+ };
+
+ var handleHashChange = function handleHashChange() {
+ handlePop(getDOMLocation(getHistoryState()));
+ };
+
+ var forceNextPop = false;
+
+ var handlePop = function handlePop(location) {
+ if (forceNextPop) {
+ forceNextPop = false;
+ setState();
+ } else {
+ var action = 'POP';
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (ok) {
+ setState({ action: action, location: location });
+ } else {
+ revertPop(location);
+ }
+ });
+ }
+ };
+
+ var revertPop = function revertPop(fromLocation) {
+ var toLocation = history.location;
+
+ // TODO: We could probably make this more reliable by
+ // keeping a list of keys we've seen in sessionStorage.
+ // Instead, we just default to 0 for keys we don't know.
+
+ var toIndex = allKeys.indexOf(toLocation.key);
+
+ if (toIndex === -1) toIndex = 0;
+
+ var fromIndex = allKeys.indexOf(fromLocation.key);
+
+ if (fromIndex === -1) fromIndex = 0;
+
+ var delta = toIndex - fromIndex;
+
+ if (delta) {
+ forceNextPop = true;
+ go(delta);
+ }
+ };
+
+ var initialLocation = getDOMLocation(getHistoryState());
+ var allKeys = [initialLocation.key];
+
+ // Public interface
+
+ var createHref = function createHref(location) {
+ return basename + createPath(location);
+ };
+
+ var push = function push(path, state) {
+ warning_1(!((typeof path === 'undefined' ? 'undefined' : _typeof$1(path)) === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to push when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
+
+ var action = 'PUSH';
+ var location = createLocation(path, state, createKey(), history.location);
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (!ok) return;
+
+ var href = createHref(location);
+ var key = location.key,
+ state = location.state;
+
+ if (canUseHistory) {
+ globalHistory.pushState({ key: key, state: state }, null, href);
+
+ if (forceRefresh) {
+ window.location.href = href;
+ } else {
+ var prevIndex = allKeys.indexOf(history.location.key);
+ var nextKeys = allKeys.slice(0, prevIndex === -1 ? 0 : prevIndex + 1);
+
+ nextKeys.push(location.key);
+ allKeys = nextKeys;
+
+ setState({ action: action, location: location });
+ }
+ } else {
+ warning_1(state === undefined, 'Browser history cannot push state in browsers that do not support HTML5 history');
+
+ window.location.href = href;
+ }
+ });
+ };
+
+ var replace = function replace(path, state) {
+ warning_1(!((typeof path === 'undefined' ? 'undefined' : _typeof$1(path)) === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to replace when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
+
+ var action = 'REPLACE';
+ var location = createLocation(path, state, createKey(), history.location);
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (!ok) return;
+
+ var href = createHref(location);
+ var key = location.key,
+ state = location.state;
+
+ if (canUseHistory) {
+ globalHistory.replaceState({ key: key, state: state }, null, href);
+
+ if (forceRefresh) {
+ window.location.replace(href);
+ } else {
+ var prevIndex = allKeys.indexOf(history.location.key);
+
+ if (prevIndex !== -1) allKeys[prevIndex] = location.key;
+
+ setState({ action: action, location: location });
+ }
+ } else {
+ warning_1(state === undefined, 'Browser history cannot replace state in browsers that do not support HTML5 history');
+
+ window.location.replace(href);
+ }
+ });
+ };
+
+ var go = function go(n) {
+ globalHistory.go(n);
+ };
+
+ var goBack = function goBack() {
+ return go(-1);
+ };
+
+ var goForward = function goForward() {
+ return go(1);
+ };
+
+ var listenerCount = 0;
+
+ var checkDOMListeners = function checkDOMListeners(delta) {
+ listenerCount += delta;
+
+ if (listenerCount === 1) {
+ addEventListener(window, PopStateEvent, handlePopState);
+
+ if (needsHashChangeListener) addEventListener(window, HashChangeEvent, handleHashChange);
+ } else if (listenerCount === 0) {
+ removeEventListener(window, PopStateEvent, handlePopState);
+
+ if (needsHashChangeListener) removeEventListener(window, HashChangeEvent, handleHashChange);
+ }
+ };
+
+ var isBlocked = false;
+
+ var block = function block() {
+ var prompt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+
+ var unblock = transitionManager.setPrompt(prompt);
+
+ if (!isBlocked) {
+ checkDOMListeners(1);
+ isBlocked = true;
+ }
+
+ return function () {
+ if (isBlocked) {
+ isBlocked = false;
+ checkDOMListeners(-1);
+ }
+
+ return unblock();
+ };
+ };
+
+ var listen = function listen(listener) {
+ var unlisten = transitionManager.appendListener(listener);
+ checkDOMListeners(1);
+
+ return function () {
+ checkDOMListeners(-1);
+ unlisten();
+ };
+ };
+
+ var history = {
+ length: globalHistory.length,
+ action: 'POP',
+ location: initialLocation,
+ createHref: createHref,
+ push: push,
+ replace: replace,
+ go: go,
+ goBack: goBack,
+ goForward: goForward,
+ block: block,
+ listen: listen
+ };
+
+ return history;
+ };
+
+ var _extends$2 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ var HashChangeEvent$1 = 'hashchange';
+
+ var HashPathCoders = {
+ hashbang: {
+ encodePath: function encodePath(path) {
+ return path.charAt(0) === '!' ? path : '!/' + stripLeadingSlash(path);
+ },
+ decodePath: function decodePath(path) {
+ return path.charAt(0) === '!' ? path.substr(1) : path;
+ }
+ },
+ noslash: {
+ encodePath: stripLeadingSlash,
+ decodePath: addLeadingSlash
+ },
+ slash: {
+ encodePath: addLeadingSlash,
+ decodePath: addLeadingSlash
+ }
+ };
+
+ var getHashPath = function getHashPath() {
+ // We can't use window.location.hash here because it's not
+ // consistent across browsers - Firefox will pre-decode it!
+ var href = window.location.href;
+ var hashIndex = href.indexOf('#');
+ return hashIndex === -1 ? '' : href.substring(hashIndex + 1);
+ };
+
+ var pushHashPath = function pushHashPath(path) {
+ return window.location.hash = path;
+ };
+
+ var replaceHashPath = function replaceHashPath(path) {
+ var hashIndex = window.location.href.indexOf('#');
+
+ window.location.replace(window.location.href.slice(0, hashIndex >= 0 ? hashIndex : 0) + '#' + path);
+ };
+
+ var createHashHistory = function createHashHistory() {
+ var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+
+ invariant_1$1(canUseDOM, 'Hash history needs a DOM');
+
+ var globalHistory = window.history;
+ var canGoWithoutReload = supportsGoWithoutReloadUsingHash();
+
+ var _props$getUserConfirm = props.getUserConfirmation,
+ getUserConfirmation = _props$getUserConfirm === undefined ? getConfirmation : _props$getUserConfirm,
+ _props$hashType = props.hashType,
+ hashType = _props$hashType === undefined ? 'slash' : _props$hashType;
+
+ var basename = props.basename ? stripTrailingSlash(addLeadingSlash(props.basename)) : '';
+
+ var _HashPathCoders$hashT = HashPathCoders[hashType],
+ encodePath = _HashPathCoders$hashT.encodePath,
+ decodePath = _HashPathCoders$hashT.decodePath;
+
+ var getDOMLocation = function getDOMLocation() {
+ var path = decodePath(getHashPath());
+
+ warning_1(!basename || hasBasename(path, basename), 'You are attempting to use a basename on a page whose URL path does not begin ' + 'with the basename. Expected path "' + path + '" to begin with "' + basename + '".');
+
+ if (basename) path = stripBasename(path, basename);
+
+ return createLocation(path);
+ };
+
+ var transitionManager = createTransitionManager();
+
+ var setState = function setState(nextState) {
+ _extends$2(history, nextState);
+
+ history.length = globalHistory.length;
+
+ transitionManager.notifyListeners(history.location, history.action);
+ };
+
+ var forceNextPop = false;
+ var ignorePath = null;
+
+ var handleHashChange = function handleHashChange() {
+ var path = getHashPath();
+ var encodedPath = encodePath(path);
+
+ if (path !== encodedPath) {
+ // Ensure we always have a properly-encoded hash.
+ replaceHashPath(encodedPath);
+ } else {
+ var location = getDOMLocation();
+ var prevLocation = history.location;
+
+ if (!forceNextPop && locationsAreEqual(prevLocation, location)) return; // A hashchange doesn't always == location change.
+
+ if (ignorePath === createPath(location)) return; // Ignore this change; we already setState in push/replace.
+
+ ignorePath = null;
+
+ handlePop(location);
+ }
+ };
+
+ var handlePop = function handlePop(location) {
+ if (forceNextPop) {
+ forceNextPop = false;
+ setState();
+ } else {
+ var action = 'POP';
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (ok) {
+ setState({ action: action, location: location });
+ } else {
+ revertPop(location);
+ }
+ });
+ }
+ };
+
+ var revertPop = function revertPop(fromLocation) {
+ var toLocation = history.location;
+
+ // TODO: We could probably make this more reliable by
+ // keeping a list of paths we've seen in sessionStorage.
+ // Instead, we just default to 0 for paths we don't know.
+
+ var toIndex = allPaths.lastIndexOf(createPath(toLocation));
+
+ if (toIndex === -1) toIndex = 0;
+
+ var fromIndex = allPaths.lastIndexOf(createPath(fromLocation));
+
+ if (fromIndex === -1) fromIndex = 0;
+
+ var delta = toIndex - fromIndex;
+
+ if (delta) {
+ forceNextPop = true;
+ go(delta);
+ }
+ };
+
+ // Ensure the hash is encoded properly before doing anything else.
+ var path = getHashPath();
+ var encodedPath = encodePath(path);
+
+ if (path !== encodedPath) replaceHashPath(encodedPath);
+
+ var initialLocation = getDOMLocation();
+ var allPaths = [createPath(initialLocation)];
+
+ // Public interface
+
+ var createHref = function createHref(location) {
+ return '#' + encodePath(basename + createPath(location));
+ };
+
+ var push = function push(path, state) {
+ warning_1(state === undefined, 'Hash history cannot push state; it is ignored');
+
+ var action = 'PUSH';
+ var location = createLocation(path, undefined, undefined, history.location);
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (!ok) return;
+
+ var path = createPath(location);
+ var encodedPath = encodePath(basename + path);
+ var hashChanged = getHashPath() !== encodedPath;
+
+ if (hashChanged) {
+ // We cannot tell if a hashchange was caused by a PUSH, so we'd
+ // rather setState here and ignore the hashchange. The caveat here
+ // is that other hash histories in the page will consider it a POP.
+ ignorePath = path;
+ pushHashPath(encodedPath);
+
+ var prevIndex = allPaths.lastIndexOf(createPath(history.location));
+ var nextPaths = allPaths.slice(0, prevIndex === -1 ? 0 : prevIndex + 1);
+
+ nextPaths.push(path);
+ allPaths = nextPaths;
+
+ setState({ action: action, location: location });
+ } else {
+ warning_1(false, 'Hash history cannot PUSH the same path; a new entry will not be added to the history stack');
+
+ setState();
+ }
+ });
+ };
+
+ var replace = function replace(path, state) {
+ warning_1(state === undefined, 'Hash history cannot replace state; it is ignored');
+
+ var action = 'REPLACE';
+ var location = createLocation(path, undefined, undefined, history.location);
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (!ok) return;
+
+ var path = createPath(location);
+ var encodedPath = encodePath(basename + path);
+ var hashChanged = getHashPath() !== encodedPath;
+
+ if (hashChanged) {
+ // We cannot tell if a hashchange was caused by a REPLACE, so we'd
+ // rather setState here and ignore the hashchange. The caveat here
+ // is that other hash histories in the page will consider it a POP.
+ ignorePath = path;
+ replaceHashPath(encodedPath);
+ }
+
+ var prevIndex = allPaths.indexOf(createPath(history.location));
+
+ if (prevIndex !== -1) allPaths[prevIndex] = path;
+
+ setState({ action: action, location: location });
+ });
+ };
+
+ var go = function go(n) {
+ warning_1(canGoWithoutReload, 'Hash history go(n) causes a full page reload in this browser');
+
+ globalHistory.go(n);
+ };
+
+ var goBack = function goBack() {
+ return go(-1);
+ };
+
+ var goForward = function goForward() {
+ return go(1);
+ };
+
+ var listenerCount = 0;
+
+ var checkDOMListeners = function checkDOMListeners(delta) {
+ listenerCount += delta;
+
+ if (listenerCount === 1) {
+ addEventListener(window, HashChangeEvent$1, handleHashChange);
+ } else if (listenerCount === 0) {
+ removeEventListener(window, HashChangeEvent$1, handleHashChange);
+ }
+ };
+
+ var isBlocked = false;
+
+ var block = function block() {
+ var prompt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+
+ var unblock = transitionManager.setPrompt(prompt);
+
+ if (!isBlocked) {
+ checkDOMListeners(1);
+ isBlocked = true;
+ }
+
+ return function () {
+ if (isBlocked) {
+ isBlocked = false;
+ checkDOMListeners(-1);
+ }
+
+ return unblock();
+ };
+ };
+
+ var listen = function listen(listener) {
+ var unlisten = transitionManager.appendListener(listener);
+ checkDOMListeners(1);
+
+ return function () {
+ checkDOMListeners(-1);
+ unlisten();
+ };
+ };
+
+ var history = {
+ length: globalHistory.length,
+ action: 'POP',
+ location: initialLocation,
+ createHref: createHref,
+ push: push,
+ replace: replace,
+ go: go,
+ goBack: goBack,
+ goForward: goForward,
+ block: block,
+ listen: listen
+ };
+
+ return history;
+ };
+
+ var _typeof$2 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+ } : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+
+ var _extends$3 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ var clamp = function clamp(n, lowerBound, upperBound) {
+ return Math.min(Math.max(n, lowerBound), upperBound);
+ };
+
+ /**
+ * Creates a history object that stores locations in memory.
+ */
+ var createMemoryHistory = function createMemoryHistory() {
+ var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
+ var getUserConfirmation = props.getUserConfirmation,
+ _props$initialEntries = props.initialEntries,
+ initialEntries = _props$initialEntries === undefined ? ['/'] : _props$initialEntries,
+ _props$initialIndex = props.initialIndex,
+ initialIndex = _props$initialIndex === undefined ? 0 : _props$initialIndex,
+ _props$keyLength = props.keyLength,
+ keyLength = _props$keyLength === undefined ? 6 : _props$keyLength;
+
+ var transitionManager = createTransitionManager();
+
+ var setState = function setState(nextState) {
+ _extends$3(history, nextState);
+
+ history.length = history.entries.length;
+
+ transitionManager.notifyListeners(history.location, history.action);
+ };
+
+ var createKey = function createKey() {
+ return Math.random().toString(36).substr(2, keyLength);
+ };
+
+ var index = clamp(initialIndex, 0, initialEntries.length - 1);
+ var entries = initialEntries.map(function (entry) {
+ return typeof entry === 'string' ? createLocation(entry, undefined, createKey()) : createLocation(entry, undefined, entry.key || createKey());
+ });
+
+ // Public interface
+
+ var createHref = createPath;
+
+ var push = function push(path, state) {
+ warning_1(!((typeof path === 'undefined' ? 'undefined' : _typeof$2(path)) === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to push when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
+
+ var action = 'PUSH';
+ var location = createLocation(path, state, createKey(), history.location);
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (!ok) return;
+
+ var prevIndex = history.index;
+ var nextIndex = prevIndex + 1;
+
+ var nextEntries = history.entries.slice(0);
+ if (nextEntries.length > nextIndex) {
+ nextEntries.splice(nextIndex, nextEntries.length - nextIndex, location);
+ } else {
+ nextEntries.push(location);
+ }
+
+ setState({
+ action: action,
+ location: location,
+ index: nextIndex,
+ entries: nextEntries
+ });
+ });
+ };
+
+ var replace = function replace(path, state) {
+ warning_1(!((typeof path === 'undefined' ? 'undefined' : _typeof$2(path)) === 'object' && path.state !== undefined && state !== undefined), 'You should avoid providing a 2nd state argument to replace when the 1st ' + 'argument is a location-like object that already has state; it is ignored');
+
+ var action = 'REPLACE';
+ var location = createLocation(path, state, createKey(), history.location);
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (!ok) return;
+
+ history.entries[history.index] = location;
+
+ setState({ action: action, location: location });
+ });
+ };
+
+ var go = function go(n) {
+ var nextIndex = clamp(history.index + n, 0, history.entries.length - 1);
+
+ var action = 'POP';
+ var location = history.entries[nextIndex];
+
+ transitionManager.confirmTransitionTo(location, action, getUserConfirmation, function (ok) {
+ if (ok) {
+ setState({
+ action: action,
+ location: location,
+ index: nextIndex
+ });
+ } else {
+ // Mimic the behavior of DOM histories by
+ // causing a render after a cancelled POP.
+ setState();
+ }
+ });
+ };
+
+ var goBack = function goBack() {
+ return go(-1);
+ };
+
+ var goForward = function goForward() {
+ return go(1);
+ };
+
+ var canGo = function canGo(n) {
+ var nextIndex = history.index + n;
+ return nextIndex >= 0 && nextIndex < history.entries.length;
+ };
+
+ var block = function block() {
+ var prompt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
+ return transitionManager.setPrompt(prompt);
+ };
+
+ var listen = function listen(listener) {
+ return transitionManager.appendListener(listener);
+ };
+
+ var history = {
+ length: entries.length,
+ action: 'POP',
+ location: entries[index],
+ index: index,
+ entries: entries,
+ createHref: createHref,
+ push: push,
+ replace: replace,
+ go: go,
+ goBack: goBack,
+ goForward: goForward,
+ canGo: canGo,
+ block: block,
+ listen: listen
+ };
+
+ return history;
+ };
+
+ var _extends$4 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ function _classCallCheck(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ }
+
+ function _possibleConstructorReturn(self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ }
+
+ function _inherits(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ }
+
+ /**
+ * The public API for putting history on context.
+ */
+
+ var Router = function (_React$Component) {
+ _inherits(Router, _React$Component);
+
+ function Router() {
+ var _temp, _this, _ret;
+
+ _classCallCheck(this, Router);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {
+ match: _this.computeMatch(_this.props.history.location.pathname)
+ }, _temp), _possibleConstructorReturn(_this, _ret);
+ }
+
+ Router.prototype.getChildContext = function getChildContext() {
+ return {
+ router: _extends$4({}, this.context.router, {
+ history: this.props.history,
+ route: {
+ location: this.props.history.location,
+ match: this.state.match
+ }
+ })
+ };
+ };
+
+ Router.prototype.computeMatch = function computeMatch(pathname) {
+ return {
+ path: "/",
+ url: "/",
+ params: {},
+ isExact: pathname === "/"
+ };
+ };
+
+ Router.prototype.componentWillMount = function componentWillMount() {
+ var _this2 = this;
+
+ var _props = this.props,
+ children = _props.children,
+ history = _props.history;
+
+ invariant_1$1(children == null || React.Children.count(children) === 1, "A <Router> may have only one child element");
+
+ // Do this here so we can setState when a <Redirect> changes the
+ // location in componentWillMount. This happens e.g. when doing
+ // server rendering using a <StaticRouter>.
+ this.unlisten = history.listen(function () {
+ _this2.setState({
+ match: _this2.computeMatch(history.location.pathname)
+ });
+ });
+ };
+
+ Router.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
+ warning_1(this.props.history === nextProps.history, "You cannot change <Router history>");
+ };
+
+ Router.prototype.componentWillUnmount = function componentWillUnmount() {
+ this.unlisten();
+ };
+
+ Router.prototype.render = function render() {
+ var children = this.props.children;
+
+ return children ? React.Children.only(children) : null;
+ };
+
+ return Router;
+ }(React.Component);
+
+ Router.propTypes = {
+ history: propTypes.object.isRequired,
+ children: propTypes.node
+ };
+ Router.contextTypes = {
+ router: propTypes.object
+ };
+ Router.childContextTypes = {
+ router: propTypes.object.isRequired
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ var _typeof$3 = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
+ return typeof obj;
+ } : function (obj) {
+ return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
+ };
+
+ var classCallCheck = function (instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ };
+
+ var _extends$5 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];
+
+ for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+
+ return target;
+ };
+
+ var inherits = function (subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }
+
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ };
+
+ var objectWithoutProperties = function (obj, keys) {
+ var target = {};
+
+ for (var i in obj) {
+ if (keys.indexOf(i) >= 0) continue;
+ if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
+ target[i] = obj[i];
+ }
+
+ return target;
+ };
+
+ var possibleConstructorReturn = function (self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }
+
+ return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ };
+
+ /**
+ * The public API for a <Router> that uses HTML5 history.
+ */
+
+ var BrowserRouter = function (_React$Component) {
+ inherits(BrowserRouter, _React$Component);
+
+ function BrowserRouter() {
+ var _temp, _this, _ret;
+
+ classCallCheck(this, BrowserRouter);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.history = createBrowserHistory(_this.props), _temp), possibleConstructorReturn(_this, _ret);
+ }
+
+ BrowserRouter.prototype.componentWillMount = function componentWillMount() {
+ warning_1(!this.props.history, "<BrowserRouter> ignores the history prop. To use a custom history, " + "use `import { Router }` instead of `import { BrowserRouter as Router }`.");
+ };
+
+ BrowserRouter.prototype.render = function render() {
+ return React.createElement(Router, { history: this.history, children: this.props.children });
+ };
+
+ return BrowserRouter;
+ }(React.Component);
+
+ BrowserRouter.propTypes = {
+ basename: propTypes.string,
+ forceRefresh: propTypes.bool,
+ getUserConfirmation: propTypes.func,
+ keyLength: propTypes.number,
+ children: propTypes.node
+ };
+
+ /**
+ * The public API for a <Router> that uses window.location.hash.
+ */
+
+ var HashRouter = function (_React$Component) {
+ inherits(HashRouter, _React$Component);
+
+ function HashRouter() {
+ var _temp, _this, _ret;
+
+ classCallCheck(this, HashRouter);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.history = createHashHistory(_this.props), _temp), possibleConstructorReturn(_this, _ret);
+ }
+
+ HashRouter.prototype.componentWillMount = function componentWillMount() {
+ warning_1(!this.props.history, "<HashRouter> ignores the history prop. To use a custom history, " + "use `import { Router }` instead of `import { HashRouter as Router }`.");
+ };
+
+ HashRouter.prototype.render = function render() {
+ return React.createElement(Router, { history: this.history, children: this.props.children });
+ };
+
+ return HashRouter;
+ }(React.Component);
+
+ HashRouter.propTypes = {
+ basename: propTypes.string,
+ getUserConfirmation: propTypes.func,
+ hashType: propTypes.oneOf(["hashbang", "noslash", "slash"]),
+ children: propTypes.node
+ };
+
+ var isModifiedEvent = function isModifiedEvent(event) {
+ return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
+ };
+
+ /**
+ * The public API for rendering a history-aware <a>.
+ */
+
+ var Link = function (_React$Component) {
+ inherits(Link, _React$Component);
+
+ function Link() {
+ var _temp, _this, _ret;
+
+ classCallCheck(this, Link);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = possibleConstructorReturn(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.handleClick = function (event) {
+ if (_this.props.onClick) _this.props.onClick(event);
+
+ if (!event.defaultPrevented && // onClick prevented default
+ event.button === 0 && // ignore everything but left clicks
+ !_this.props.target && // let browser handle "target=_blank" etc.
+ !isModifiedEvent(event) // ignore clicks with modifier keys
+ ) {
+ event.preventDefault();
+
+ var history = _this.context.router.history;
+ var _this$props = _this.props,
+ replace = _this$props.replace,
+ to = _this$props.to;
+
+
+ if (replace) {
+ history.replace(to);
+ } else {
+ history.push(to);
+ }
+ }
+ }, _temp), possibleConstructorReturn(_this, _ret);
+ }
+
+ Link.prototype.render = function render() {
+ var _props = this.props,
+ replace = _props.replace,
+ to = _props.to,
+ innerRef = _props.innerRef,
+ props = objectWithoutProperties(_props, ["replace", "to", "innerRef"]); // eslint-disable-line no-unused-vars
+
+ invariant_1$1(this.context.router, "You should not use <Link> outside a <Router>");
+
+ invariant_1$1(to !== undefined, 'You must specify the "to" property');
+
+ var history = this.context.router.history;
+
+ var location = typeof to === "string" ? createLocation(to, null, null, history.location) : to;
+
+ var href = history.createHref(location);
+ return React.createElement("a", _extends$5({}, props, { onClick: this.handleClick, href: href, ref: innerRef }));
+ };
+
+ return Link;
+ }(React.Component);
+
+ Link.propTypes = {
+ onClick: propTypes.func,
+ target: propTypes.string,
+ replace: propTypes.bool,
+ to: propTypes.oneOfType([propTypes.string, propTypes.object]).isRequired,
+ innerRef: propTypes.oneOfType([propTypes.string, propTypes.func])
+ };
+ Link.defaultProps = {
+ replace: false
+ };
+ Link.contextTypes = {
+ router: propTypes.shape({
+ history: propTypes.shape({
+ push: propTypes.func.isRequired,
+ replace: propTypes.func.isRequired,
+ createHref: propTypes.func.isRequired
+ }).isRequired
+ }).isRequired
+ };
+
+ function _classCallCheck$1(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ }
+
+ function _possibleConstructorReturn$1(self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ }
+
+ function _inherits$1(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ }
+
+ /**
+ * The public API for a <Router> that stores location in memory.
+ */
+
+ var MemoryRouter = function (_React$Component) {
+ _inherits$1(MemoryRouter, _React$Component);
+
+ function MemoryRouter() {
+ var _temp, _this, _ret;
+
+ _classCallCheck$1(this, MemoryRouter);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn$1(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.history = createMemoryHistory(_this.props), _temp), _possibleConstructorReturn$1(_this, _ret);
+ }
+
+ MemoryRouter.prototype.componentWillMount = function componentWillMount() {
+ warning_1(!this.props.history, "<MemoryRouter> ignores the history prop. To use a custom history, " + "use `import { Router }` instead of `import { MemoryRouter as Router }`.");
+ };
+
+ MemoryRouter.prototype.render = function render() {
+ return React.createElement(Router, { history: this.history, children: this.props.children });
+ };
+
+ return MemoryRouter;
+ }(React.Component);
+
+ MemoryRouter.propTypes = {
+ initialEntries: propTypes.array,
+ initialIndex: propTypes.number,
+ getUserConfirmation: propTypes.func,
+ keyLength: propTypes.number,
+ children: propTypes.node
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ var toString = {}.toString;
+
+ var isarray = Array.isArray || function (arr) {
+ return toString.call(arr) == '[object Array]';
+ };
+
+ /**
+ * Expose `pathToRegexp`.
+ */
+ var pathToRegexp_1 = pathToRegexp;
+ var parse_1 = parse;
+ var compile_1 = compile;
+ var tokensToFunction_1 = tokensToFunction;
+ var tokensToRegExp_1 = tokensToRegExp;
+
+ /**
+ * The main path matching regexp utility.
+ *
+ * @type {RegExp}
+ */
+ var PATH_REGEXP = new RegExp([
+ // Match escaped characters that would otherwise appear in future matches.
+ // This allows the user to escape special characters that won't transform.
+ '(\\\\.)',
+ // Match Express-style parameters and un-named parameters with a prefix
+ // and optional suffixes. Matches appear as:
+ //
+ // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
+ // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined]
+ // "/*" => ["/", undefined, undefined, undefined, undefined, "*"]
+ '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'].join('|'), 'g');
+
+ /**
+ * Parse a string for the raw tokens.
+ *
+ * @param {string} str
+ * @param {Object=} options
+ * @return {!Array}
+ */
+ function parse(str, options) {
+ var tokens = [];
+ var key = 0;
+ var index = 0;
+ var path = '';
+ var defaultDelimiter = options && options.delimiter || '/';
+ var res;
+
+ while ((res = PATH_REGEXP.exec(str)) != null) {
+ var m = res[0];
+ var escaped = res[1];
+ var offset = res.index;
+ path += str.slice(index, offset);
+ index = offset + m.length;
+
+ // Ignore already escaped sequences.
+ if (escaped) {
+ path += escaped[1];
+ continue;
+ }
+
+ var next = str[index];
+ var prefix = res[2];
+ var name = res[3];
+ var capture = res[4];
+ var group = res[5];
+ var modifier = res[6];
+ var asterisk = res[7];
+
+ // Push the current path onto the tokens.
+ if (path) {
+ tokens.push(path);
+ path = '';
+ }
+
+ var partial = prefix != null && next != null && next !== prefix;
+ var repeat = modifier === '+' || modifier === '*';
+ var optional = modifier === '?' || modifier === '*';
+ var delimiter = res[2] || defaultDelimiter;
+ var pattern = capture || group;
+
+ tokens.push({
+ name: name || key++,
+ prefix: prefix || '',
+ delimiter: delimiter,
+ optional: optional,
+ repeat: repeat,
+ partial: partial,
+ asterisk: !!asterisk,
+ pattern: pattern ? escapeGroup(pattern) : asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?'
+ });
+ }
+
+ // Match any characters still remaining.
+ if (index < str.length) {
+ path += str.substr(index);
+ }
+
+ // If the path exists, push it onto the end.
+ if (path) {
+ tokens.push(path);
+ }
+
+ return tokens;
+ }
+
+ /**
+ * Compile a string to a template function for the path.
+ *
+ * @param {string} str
+ * @param {Object=} options
+ * @return {!function(Object=, Object=)}
+ */
+ function compile(str, options) {
+ return tokensToFunction(parse(str, options));
+ }
+
+ /**
+ * Prettier encoding of URI path segments.
+ *
+ * @param {string}
+ * @return {string}
+ */
+ function encodeURIComponentPretty(str) {
+ return encodeURI(str).replace(/[\/?#]/g, function (c) {
+ return '%' + c.charCodeAt(0).toString(16).toUpperCase();
+ });
+ }
+
+ /**
+ * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
+ *
+ * @param {string}
+ * @return {string}
+ */
+ function encodeAsterisk(str) {
+ return encodeURI(str).replace(/[?#]/g, function (c) {
+ return '%' + c.charCodeAt(0).toString(16).toUpperCase();
+ });
+ }
+
+ /**
+ * Expose a method for transforming tokens into the path function.
+ */
+ function tokensToFunction(tokens) {
+ // Compile all the tokens into regexps.
+ var matches = new Array(tokens.length);
+
+ // Compile all the patterns before compilation.
+ for (var i = 0; i < tokens.length; i++) {
+ if (typeof tokens[i] === 'object') {
+ matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$');
+ }
+ }
+
+ return function (obj, opts) {
+ var path = '';
+ var data = obj || {};
+ var options = opts || {};
+ var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent;
+
+ for (var i = 0; i < tokens.length; i++) {
+ var token = tokens[i];
+
+ if (typeof token === 'string') {
+ path += token;
+
+ continue;
+ }
+
+ var value = data[token.name];
+ var segment;
+
+ if (value == null) {
+ if (token.optional) {
+ // Prepend partial segment prefixes.
+ if (token.partial) {
+ path += token.prefix;
+ }
+
+ continue;
+ } else {
+ throw new TypeError('Expected "' + token.name + '" to be defined');
+ }
+ }
+
+ if (isarray(value)) {
+ if (!token.repeat) {
+ throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`');
+ }
+
+ if (value.length === 0) {
+ if (token.optional) {
+ continue;
+ } else {
+ throw new TypeError('Expected "' + token.name + '" to not be empty');
+ }
+ }
+
+ for (var j = 0; j < value.length; j++) {
+ segment = encode(value[j]);
+
+ if (!matches[i].test(segment)) {
+ throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`');
+ }
+
+ path += (j === 0 ? token.prefix : token.delimiter) + segment;
+ }
+
+ continue;
+ }
+
+ segment = token.asterisk ? encodeAsterisk(value) : encode(value);
+
+ if (!matches[i].test(segment)) {
+ throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"');
+ }
+
+ path += token.prefix + segment;
+ }
+
+ return path;
+ };
+ }
+
+ /**
+ * Escape a regular expression string.
+ *
+ * @param {string} str
+ * @return {string}
+ */
+ function escapeString(str) {
+ return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1');
+ }
+
+ /**
+ * Escape the capturing group by escaping special characters and meaning.
+ *
+ * @param {string} group
+ * @return {string}
+ */
+ function escapeGroup(group) {
+ return group.replace(/([=!:$\/()])/g, '\\$1');
+ }
+
+ /**
+ * Attach the keys as a property of the regexp.
+ *
+ * @param {!RegExp} re
+ * @param {Array} keys
+ * @return {!RegExp}
+ */
+ function attachKeys(re, keys) {
+ re.keys = keys;
+ return re;
+ }
+
+ /**
+ * Get the flags for a regexp from the options.
+ *
+ * @param {Object} options
+ * @return {string}
+ */
+ function flags(options) {
+ return options.sensitive ? '' : 'i';
+ }
+
+ /**
+ * Pull out keys from a regexp.
+ *
+ * @param {!RegExp} path
+ * @param {!Array} keys
+ * @return {!RegExp}
+ */
+ function regexpToRegexp(path, keys) {
+ // Use a negative lookahead to match only capturing groups.
+ var groups = path.source.match(/\((?!\?)/g);
+
+ if (groups) {
+ for (var i = 0; i < groups.length; i++) {
+ keys.push({
+ name: i,
+ prefix: null,
+ delimiter: null,
+ optional: false,
+ repeat: false,
+ partial: false,
+ asterisk: false,
+ pattern: null
+ });
+ }
+ }
+
+ return attachKeys(path, keys);
+ }
+
+ /**
+ * Transform an array into a regexp.
+ *
+ * @param {!Array} path
+ * @param {Array} keys
+ * @param {!Object} options
+ * @return {!RegExp}
+ */
+ function arrayToRegexp(path, keys, options) {
+ var parts = [];
+
+ for (var i = 0; i < path.length; i++) {
+ parts.push(pathToRegexp(path[i], keys, options).source);
+ }
+
+ var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options));
+
+ return attachKeys(regexp, keys);
+ }
+
+ /**
+ * Create a path regexp from string input.
+ *
+ * @param {string} path
+ * @param {!Array} keys
+ * @param {!Object} options
+ * @return {!RegExp}
+ */
+ function stringToRegexp(path, keys, options) {
+ return tokensToRegExp(parse(path, options), keys, options);
+ }
+
+ /**
+ * Expose a function for taking tokens and returning a RegExp.
+ *
+ * @param {!Array} tokens
+ * @param {(Array|Object)=} keys
+ * @param {Object=} options
+ * @return {!RegExp}
+ */
+ function tokensToRegExp(tokens, keys, options) {
+ if (!isarray(keys)) {
+ options = /** @type {!Object} */keys || options;
+ keys = [];
+ }
+
+ options = options || {};
+
+ var strict = options.strict;
+ var end = options.end !== false;
+ var route = '';
+
+ // Iterate over the tokens and create our regexp string.
+ for (var i = 0; i < tokens.length; i++) {
+ var token = tokens[i];
+
+ if (typeof token === 'string') {
+ route += escapeString(token);
+ } else {
+ var prefix = escapeString(token.prefix);
+ var capture = '(?:' + token.pattern + ')';
+
+ keys.push(token);
+
+ if (token.repeat) {
+ capture += '(?:' + prefix + capture + ')*';
+ }
+
+ if (token.optional) {
+ if (!token.partial) {
+ capture = '(?:' + prefix + '(' + capture + '))?';
+ } else {
+ capture = prefix + '(' + capture + ')?';
+ }
+ } else {
+ capture = prefix + '(' + capture + ')';
+ }
+
+ route += capture;
+ }
+ }
+
+ var delimiter = escapeString(options.delimiter || '/');
+ var endsWithDelimiter = route.slice(-delimiter.length) === delimiter;
+
+ // In non-strict mode we allow a slash at the end of match. If the path to
+ // match already ends with a slash, we remove it for consistency. The slash
+ // is valid at the end of a path match, not in the middle. This is important
+ // in non-ending mode, where "/test/" shouldn't match "/test//route".
+ if (!strict) {
+ route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?';
+ }
+
+ if (end) {
+ route += '$';
+ } else {
+ // In non-ending mode, we need the capturing groups to match as much as
+ // possible by using a positive lookahead to the end or next path segment.
+ route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)';
+ }
+
+ return attachKeys(new RegExp('^' + route, flags(options)), keys);
+ }
+
+ /**
+ * Normalize the given path string, returning a regular expression.
+ *
+ * An empty array can be passed in for the keys, which will hold the
+ * placeholder key descriptions. For example, using `/user/:id`, `keys` will
+ * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
+ *
+ * @param {(string|RegExp|Array)} path
+ * @param {(Array|Object)=} keys
+ * @param {Object=} options
+ * @return {!RegExp}
+ */
+ function pathToRegexp(path, keys, options) {
+ if (!isarray(keys)) {
+ options = /** @type {!Object} */keys || options;
+ keys = [];
+ }
+
+ options = options || {};
+
+ if (path instanceof RegExp) {
+ return regexpToRegexp(path, /** @type {!Array} */keys);
+ }
+
+ if (isarray(path)) {
+ return arrayToRegexp( /** @type {!Array} */path, /** @type {!Array} */keys, options);
+ }
+
+ return stringToRegexp( /** @type {string} */path, /** @type {!Array} */keys, options);
+ }
+ pathToRegexp_1.parse = parse_1;
+ pathToRegexp_1.compile = compile_1;
+ pathToRegexp_1.tokensToFunction = tokensToFunction_1;
+ pathToRegexp_1.tokensToRegExp = tokensToRegExp_1;
+
+ var patternCache = {};
+ var cacheLimit = 10000;
+ var cacheCount = 0;
+
+ var compilePath = function compilePath(pattern, options) {
+ var cacheKey = "" + options.end + options.strict + options.sensitive;
+ var cache = patternCache[cacheKey] || (patternCache[cacheKey] = {});
+
+ if (cache[pattern]) return cache[pattern];
+
+ var keys = [];
+ var re = pathToRegexp_1(pattern, keys, options);
+ var compiledPattern = { re: re, keys: keys };
+
+ if (cacheCount < cacheLimit) {
+ cache[pattern] = compiledPattern;
+ cacheCount++;
+ }
+
+ return compiledPattern;
+ };
+
+ /**
+ * Public API for matching a URL pathname to a path pattern.
+ */
+ var matchPath = function matchPath(pathname) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+ var parent = arguments[2];
+
+ if (typeof options === "string") options = { path: options };
+
+ var _options = options,
+ path = _options.path,
+ _options$exact = _options.exact,
+ exact = _options$exact === undefined ? false : _options$exact,
+ _options$strict = _options.strict,
+ strict = _options$strict === undefined ? false : _options$strict,
+ _options$sensitive = _options.sensitive,
+ sensitive = _options$sensitive === undefined ? false : _options$sensitive;
+
+ if (path == null) return parent;
+
+ var _compilePath = compilePath(path, { end: exact, strict: strict, sensitive: sensitive }),
+ re = _compilePath.re,
+ keys = _compilePath.keys;
+
+ var match = re.exec(pathname);
+
+ if (!match) return null;
+
+ var url = match[0],
+ values = match.slice(1);
+
+ var isExact = pathname === url;
+
+ if (exact && !isExact) return null;
+
+ return {
+ path: path, // the path pattern used to match
+ url: path === "/" && url === "" ? "/" : url, // the matched portion of the URL
+ isExact: isExact, // whether or not we matched exactly
+ params: keys.reduce(function (memo, key, index) {
+ memo[key.name] = values[index];
+ return memo;
+ }, {})
+ };
+ };
+
+ var _extends$6 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ function _classCallCheck$2(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ }
+
+ function _possibleConstructorReturn$2(self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ }
+
+ function _inherits$2(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ }
+
+ var isEmptyChildren = function isEmptyChildren(children) {
+ return React.Children.count(children) === 0;
+ };
+
+ /**
+ * The public API for matching a single path and rendering.
+ */
+
+ var Route = function (_React$Component) {
+ _inherits$2(Route, _React$Component);
+
+ function Route() {
+ var _temp, _this, _ret;
+
+ _classCallCheck$2(this, Route);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn$2(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.state = {
+ match: _this.computeMatch(_this.props, _this.context.router)
+ }, _temp), _possibleConstructorReturn$2(_this, _ret);
+ }
+
+ Route.prototype.getChildContext = function getChildContext() {
+ return {
+ router: _extends$6({}, this.context.router, {
+ route: {
+ location: this.props.location || this.context.router.route.location,
+ match: this.state.match
+ }
+ })
+ };
+ };
+
+ Route.prototype.computeMatch = function computeMatch(_ref, router) {
+ var computedMatch = _ref.computedMatch,
+ location = _ref.location,
+ path = _ref.path,
+ strict = _ref.strict,
+ exact = _ref.exact,
+ sensitive = _ref.sensitive;
+
+ if (computedMatch) return computedMatch; // <Switch> already computed the match for us
+
+ invariant_1$1(router, "You should not use <Route> or withRouter() outside a <Router>");
+
+ var route = router.route;
+
+ var pathname = (location || route.location).pathname;
+
+ return matchPath(pathname, { path: path, strict: strict, exact: exact, sensitive: sensitive }, route.match);
+ };
+
+ Route.prototype.componentWillMount = function componentWillMount() {
+ warning_1(!(this.props.component && this.props.render), "You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored");
+
+ warning_1(!(this.props.component && this.props.children && !isEmptyChildren(this.props.children)), "You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored");
+
+ warning_1(!(this.props.render && this.props.children && !isEmptyChildren(this.props.children)), "You should not use <Route render> and <Route children> in the same route; <Route children> will be ignored");
+ };
+
+ Route.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps, nextContext) {
+ warning_1(!(nextProps.location && !this.props.location), '<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.');
+
+ warning_1(!(!nextProps.location && this.props.location), '<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.');
+
+ this.setState({
+ match: this.computeMatch(nextProps, nextContext.router)
+ });
+ };
+
+ Route.prototype.render = function render() {
+ var match = this.state.match;
+ var _props = this.props,
+ children = _props.children,
+ component = _props.component,
+ render = _props.render;
+ var _context$router = this.context.router,
+ history = _context$router.history,
+ route = _context$router.route,
+ staticContext = _context$router.staticContext;
+
+ var location = this.props.location || route.location;
+ var props = { match: match, location: location, history: history, staticContext: staticContext };
+
+ if (component) return match ? React.createElement(component, props) : null;
+
+ if (render) return match ? render(props) : null;
+
+ if (typeof children === "function") return children(props);
+
+ if (children && !isEmptyChildren(children)) return React.Children.only(children);
+
+ return null;
+ };
+
+ return Route;
+ }(React.Component);
+
+ Route.propTypes = {
+ computedMatch: propTypes.object, // private, from <Switch>
+ path: propTypes.string,
+ exact: propTypes.bool,
+ strict: propTypes.bool,
+ sensitive: propTypes.bool,
+ component: propTypes.func,
+ render: propTypes.func,
+ children: propTypes.oneOfType([propTypes.func, propTypes.node]),
+ location: propTypes.object
+ };
+ Route.contextTypes = {
+ router: propTypes.shape({
+ history: propTypes.object.isRequired,
+ route: propTypes.object.isRequired,
+ staticContext: propTypes.object
+ })
+ };
+ Route.childContextTypes = {
+ router: propTypes.object.isRequired
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ /**
+ * A <Link> wrapper that knows if it's "active" or not.
+ */
+ var NavLink = function NavLink(_ref) {
+ var to = _ref.to,
+ exact = _ref.exact,
+ strict = _ref.strict,
+ location = _ref.location,
+ activeClassName = _ref.activeClassName,
+ className = _ref.className,
+ activeStyle = _ref.activeStyle,
+ style = _ref.style,
+ getIsActive = _ref.isActive,
+ ariaCurrent = _ref["aria-current"],
+ rest = objectWithoutProperties(_ref, ["to", "exact", "strict", "location", "activeClassName", "className", "activeStyle", "style", "isActive", "aria-current"]);
+
+ var path = (typeof to === "undefined" ? "undefined" : _typeof$3(to)) === "object" ? to.pathname : to;
+
+ // Regex taken from: https://github.com/pillarjs/path-to-regexp/blob/master/index.js#L202
+ var escapedPath = path && path.replace(/([.+*?=^!:${}()[\]|/\\])/g, "\\$1");
+
+ return React.createElement(Route, {
+ path: escapedPath,
+ exact: exact,
+ strict: strict,
+ location: location,
+ children: function children(_ref2) {
+ var location = _ref2.location,
+ match = _ref2.match;
+
+ var isActive = !!(getIsActive ? getIsActive(match, location) : match);
+
+ return React.createElement(Link, _extends$5({
+ to: to,
+ className: isActive ? [className, activeClassName].filter(function (i) {
+ return i;
+ }).join(" ") : className,
+ style: isActive ? _extends$5({}, style, activeStyle) : style,
+ "aria-current": isActive && ariaCurrent || null
+ }, rest));
+ }
+ });
+ };
+
+ NavLink.propTypes = {
+ to: Link.propTypes.to,
+ exact: propTypes.bool,
+ strict: propTypes.bool,
+ location: propTypes.object,
+ activeClassName: propTypes.string,
+ className: propTypes.string,
+ activeStyle: propTypes.object,
+ style: propTypes.object,
+ isActive: propTypes.func,
+ "aria-current": propTypes.oneOf(["page", "step", "location", "date", "time", "true"])
+ };
+
+ NavLink.defaultProps = {
+ activeClassName: "active",
+ "aria-current": "page"
+ };
+
+ function _classCallCheck$3(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ }
+
+ function _possibleConstructorReturn$3(self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ }
+
+ function _inherits$3(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ }
+
+ /**
+ * The public API for prompting the user before navigating away
+ * from a screen with a component.
+ */
+
+ var Prompt = function (_React$Component) {
+ _inherits$3(Prompt, _React$Component);
+
+ function Prompt() {
+ _classCallCheck$3(this, Prompt);
+
+ return _possibleConstructorReturn$3(this, _React$Component.apply(this, arguments));
+ }
+
+ Prompt.prototype.enable = function enable(message) {
+ if (this.unblock) this.unblock();
+
+ this.unblock = this.context.router.history.block(message);
+ };
+
+ Prompt.prototype.disable = function disable() {
+ if (this.unblock) {
+ this.unblock();
+ this.unblock = null;
+ }
+ };
+
+ Prompt.prototype.componentWillMount = function componentWillMount() {
+ invariant_1$1(this.context.router, "You should not use <Prompt> outside a <Router>");
+
+ if (this.props.when) this.enable(this.props.message);
+ };
+
+ Prompt.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
+ if (nextProps.when) {
+ if (!this.props.when || this.props.message !== nextProps.message) this.enable(nextProps.message);
+ } else {
+ this.disable();
+ }
+ };
+
+ Prompt.prototype.componentWillUnmount = function componentWillUnmount() {
+ this.disable();
+ };
+
+ Prompt.prototype.render = function render() {
+ return null;
+ };
+
+ return Prompt;
+ }(React.Component);
+
+ Prompt.propTypes = {
+ when: propTypes.bool,
+ message: propTypes.oneOfType([propTypes.func, propTypes.string]).isRequired
+ };
+ Prompt.defaultProps = {
+ when: true
+ };
+ Prompt.contextTypes = {
+ router: propTypes.shape({
+ history: propTypes.shape({
+ block: propTypes.func.isRequired
+ }).isRequired
+ }).isRequired
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ var patternCache$1 = {};
+ var cacheLimit$1 = 10000;
+ var cacheCount$1 = 0;
+
+ var compileGenerator = function compileGenerator(pattern) {
+ var cacheKey = pattern;
+ var cache = patternCache$1[cacheKey] || (patternCache$1[cacheKey] = {});
+
+ if (cache[pattern]) return cache[pattern];
+
+ var compiledGenerator = pathToRegexp_1.compile(pattern);
+
+ if (cacheCount$1 < cacheLimit$1) {
+ cache[pattern] = compiledGenerator;
+ cacheCount$1++;
+ }
+
+ return compiledGenerator;
+ };
+
+ /**
+ * Public API for generating a URL pathname from a pattern and parameters.
+ */
+ var generatePath = function generatePath() {
+ var pattern = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "/";
+ var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+ if (pattern === "/") {
+ return pattern;
+ }
+ var generator = compileGenerator(pattern);
+ return generator(params, { pretty: true });
+ };
+
+ var _extends$7 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ function _classCallCheck$4(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ }
+
+ function _possibleConstructorReturn$4(self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ }
+
+ function _inherits$4(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ }
+
+ /**
+ * The public API for updating the location programmatically
+ * with a component.
+ */
+
+ var Redirect = function (_React$Component) {
+ _inherits$4(Redirect, _React$Component);
+
+ function Redirect() {
+ _classCallCheck$4(this, Redirect);
+
+ return _possibleConstructorReturn$4(this, _React$Component.apply(this, arguments));
+ }
+
+ Redirect.prototype.isStatic = function isStatic() {
+ return this.context.router && this.context.router.staticContext;
+ };
+
+ Redirect.prototype.componentWillMount = function componentWillMount() {
+ invariant_1$1(this.context.router, "You should not use <Redirect> outside a <Router>");
+
+ if (this.isStatic()) this.perform();
+ };
+
+ Redirect.prototype.componentDidMount = function componentDidMount() {
+ if (!this.isStatic()) this.perform();
+ };
+
+ Redirect.prototype.componentDidUpdate = function componentDidUpdate(prevProps) {
+ var prevTo = createLocation(prevProps.to);
+ var nextTo = createLocation(this.props.to);
+
+ if (locationsAreEqual(prevTo, nextTo)) {
+ warning_1(false, "You tried to redirect to the same route you're currently on: " + ("\"" + nextTo.pathname + nextTo.search + "\""));
+ return;
+ }
+
+ this.perform();
+ };
+
+ Redirect.prototype.computeTo = function computeTo(_ref) {
+ var computedMatch = _ref.computedMatch,
+ to = _ref.to;
+
+ if (computedMatch) {
+ if (typeof to === "string") {
+ return generatePath(to, computedMatch.params);
+ } else {
+ return _extends$7({}, to, {
+ pathname: generatePath(to.pathname, computedMatch.params)
+ });
+ }
+ }
+
+ return to;
+ };
+
+ Redirect.prototype.perform = function perform() {
+ var history = this.context.router.history;
+ var push = this.props.push;
+
+ var to = this.computeTo(this.props);
+
+ if (push) {
+ history.push(to);
+ } else {
+ history.replace(to);
+ }
+ };
+
+ Redirect.prototype.render = function render() {
+ return null;
+ };
+
+ return Redirect;
+ }(React.Component);
+
+ Redirect.propTypes = {
+ computedMatch: propTypes.object, // private, from <Switch>
+ push: propTypes.bool,
+ from: propTypes.string,
+ to: propTypes.oneOfType([propTypes.string, propTypes.object]).isRequired
+ };
+ Redirect.defaultProps = {
+ push: false
+ };
+ Redirect.contextTypes = {
+ router: propTypes.shape({
+ history: propTypes.shape({
+ push: propTypes.func.isRequired,
+ replace: propTypes.func.isRequired
+ }).isRequired,
+ staticContext: propTypes.object
+ }).isRequired
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ var _extends$8 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ function _objectWithoutProperties(obj, keys) {
+ var target = {};for (var i in obj) {
+ if (keys.indexOf(i) >= 0) continue;if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;target[i] = obj[i];
+ }return target;
+ }
+
+ function _classCallCheck$5(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ }
+
+ function _possibleConstructorReturn$5(self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ }
+
+ function _inherits$5(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ }
+
+ var addLeadingSlash$1 = function addLeadingSlash(path) {
+ return path.charAt(0) === "/" ? path : "/" + path;
+ };
+
+ var addBasename = function addBasename(basename, location) {
+ if (!basename) return location;
+
+ return _extends$8({}, location, {
+ pathname: addLeadingSlash$1(basename) + location.pathname
+ });
+ };
+
+ var stripBasename$1 = function stripBasename(basename, location) {
+ if (!basename) return location;
+
+ var base = addLeadingSlash$1(basename);
+
+ if (location.pathname.indexOf(base) !== 0) return location;
+
+ return _extends$8({}, location, {
+ pathname: location.pathname.substr(base.length)
+ });
+ };
+
+ var createURL = function createURL(location) {
+ return typeof location === "string" ? location : createPath(location);
+ };
+
+ var staticHandler = function staticHandler(methodName) {
+ return function () {
+ invariant_1$1(false, "You cannot %s with <StaticRouter>", methodName);
+ };
+ };
+
+ var noop = function noop() {};
+
+ /**
+ * The public top-level API for a "static" <Router>, so-called because it
+ * can't actually change the current location. Instead, it just records
+ * location changes in a context object. Useful mainly in testing and
+ * server-rendering scenarios.
+ */
+
+ var StaticRouter = function (_React$Component) {
+ _inherits$5(StaticRouter, _React$Component);
+
+ function StaticRouter() {
+ var _temp, _this, _ret;
+
+ _classCallCheck$5(this, StaticRouter);
+
+ for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
+ args[_key] = arguments[_key];
+ }
+
+ return _ret = (_temp = (_this = _possibleConstructorReturn$5(this, _React$Component.call.apply(_React$Component, [this].concat(args))), _this), _this.createHref = function (path) {
+ return addLeadingSlash$1(_this.props.basename + createURL(path));
+ }, _this.handlePush = function (location) {
+ var _this$props = _this.props,
+ basename = _this$props.basename,
+ context = _this$props.context;
+
+ context.action = "PUSH";
+ context.location = addBasename(basename, createLocation(location));
+ context.url = createURL(context.location);
+ }, _this.handleReplace = function (location) {
+ var _this$props2 = _this.props,
+ basename = _this$props2.basename,
+ context = _this$props2.context;
+
+ context.action = "REPLACE";
+ context.location = addBasename(basename, createLocation(location));
+ context.url = createURL(context.location);
+ }, _this.handleListen = function () {
+ return noop;
+ }, _this.handleBlock = function () {
+ return noop;
+ }, _temp), _possibleConstructorReturn$5(_this, _ret);
+ }
+
+ StaticRouter.prototype.getChildContext = function getChildContext() {
+ return {
+ router: {
+ staticContext: this.props.context
+ }
+ };
+ };
+
+ StaticRouter.prototype.componentWillMount = function componentWillMount() {
+ warning_1(!this.props.history, "<StaticRouter> ignores the history prop. To use a custom history, " + "use `import { Router }` instead of `import { StaticRouter as Router }`.");
+ };
+
+ StaticRouter.prototype.render = function render() {
+ var _props = this.props,
+ basename = _props.basename,
+ context = _props.context,
+ location = _props.location,
+ props = _objectWithoutProperties(_props, ["basename", "context", "location"]);
+
+ var history = {
+ createHref: this.createHref,
+ action: "POP",
+ location: stripBasename$1(basename, createLocation(location)),
+ push: this.handlePush,
+ replace: this.handleReplace,
+ go: staticHandler("go"),
+ goBack: staticHandler("goBack"),
+ goForward: staticHandler("goForward"),
+ listen: this.handleListen,
+ block: this.handleBlock
+ };
+
+ return React.createElement(Router, _extends$8({}, props, { history: history }));
+ };
+
+ return StaticRouter;
+ }(React.Component);
+
+ StaticRouter.propTypes = {
+ basename: propTypes.string,
+ context: propTypes.object.isRequired,
+ location: propTypes.oneOfType([propTypes.string, propTypes.object])
+ };
+ StaticRouter.defaultProps = {
+ basename: "",
+ location: "/"
+ };
+ StaticRouter.childContextTypes = {
+ router: propTypes.object.isRequired
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ function _classCallCheck$6(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
+ }
+ }
+
+ function _possibleConstructorReturn$6(self, call) {
+ if (!self) {
+ throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
+ }return call && (typeof call === "object" || typeof call === "function") ? call : self;
+ }
+
+ function _inherits$6(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
+ }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
+ }
+
+ /**
+ * The public API for rendering the first <Route> that matches.
+ */
+
+ var Switch = function (_React$Component) {
+ _inherits$6(Switch, _React$Component);
+
+ function Switch() {
+ _classCallCheck$6(this, Switch);
+
+ return _possibleConstructorReturn$6(this, _React$Component.apply(this, arguments));
+ }
+
+ Switch.prototype.componentWillMount = function componentWillMount() {
+ invariant_1$1(this.context.router, "You should not use <Switch> outside a <Router>");
+ };
+
+ Switch.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) {
+ warning_1(!(nextProps.location && !this.props.location), '<Switch> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.');
+
+ warning_1(!(!nextProps.location && this.props.location), '<Switch> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.');
+ };
+
+ Switch.prototype.render = function render() {
+ var route = this.context.router.route;
+ var children = this.props.children;
+
+ var location = this.props.location || route.location;
+
+ var match = void 0,
+ child = void 0;
+ React.Children.forEach(children, function (element) {
+ if (match == null && React.isValidElement(element)) {
+ var _element$props = element.props,
+ pathProp = _element$props.path,
+ exact = _element$props.exact,
+ strict = _element$props.strict,
+ sensitive = _element$props.sensitive,
+ from = _element$props.from;
+
+ var path = pathProp || from;
+
+ child = element;
+ match = matchPath(location.pathname, { path: path, exact: exact, strict: strict, sensitive: sensitive }, route.match);
+ }
+ });
+
+ return match ? React.cloneElement(child, { location: location, computedMatch: match }) : null;
+ };
+
+ return Switch;
+ }(React.Component);
+
+ Switch.contextTypes = {
+ router: propTypes.shape({
+ route: propTypes.object.isRequired
+ }).isRequired
+ };
+ Switch.propTypes = {
+ children: propTypes.node,
+ location: propTypes.object
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ // Written in this round about way for babel-transform-imports
+
+ // Written in this round about way for babel-transform-imports
+
+ var hoistNonReactStatics = createCommonjsModule(function (module, exports) {
+ /**
+ * Copyright 2015, Yahoo! Inc.
+ * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
+ */
+ (function (global, factory) {
+ module.exports = factory();
+ })(commonjsGlobal, function () {
+
+ var REACT_STATICS = {
+ childContextTypes: true,
+ contextTypes: true,
+ defaultProps: true,
+ displayName: true,
+ getDefaultProps: true,
+ getDerivedStateFromProps: true,
+ mixins: true,
+ propTypes: true,
+ type: true
+ };
+
+ var KNOWN_STATICS = {
+ name: true,
+ length: true,
+ prototype: true,
+ caller: true,
+ callee: true,
+ arguments: true,
+ arity: true
+ };
+
+ var defineProperty = Object.defineProperty;
+ var getOwnPropertyNames = Object.getOwnPropertyNames;
+ var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+ var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+ var getPrototypeOf = Object.getPrototypeOf;
+ var objectPrototype = getPrototypeOf && getPrototypeOf(Object);
+
+ return function hoistNonReactStatics(targetComponent, sourceComponent, blacklist) {
+ if (typeof sourceComponent !== 'string') {
+ // don't hoist over string (html) components
+
+ if (objectPrototype) {
+ var inheritedComponent = getPrototypeOf(sourceComponent);
+ if (inheritedComponent && inheritedComponent !== objectPrototype) {
+ hoistNonReactStatics(targetComponent, inheritedComponent, blacklist);
+ }
+ }
+
+ var keys = getOwnPropertyNames(sourceComponent);
+
+ if (getOwnPropertySymbols) {
+ keys = keys.concat(getOwnPropertySymbols(sourceComponent));
+ }
+
+ for (var i = 0; i < keys.length; ++i) {
+ var key = keys[i];
+ if (!REACT_STATICS[key] && !KNOWN_STATICS[key] && (!blacklist || !blacklist[key])) {
+ var descriptor = getOwnPropertyDescriptor(sourceComponent, key);
+ try {
+ // Avoid failures from read-only properties
+ defineProperty(targetComponent, key, descriptor);
+ } catch (e) {}
+ }
+ }
+
+ return targetComponent;
+ }
+
+ return targetComponent;
+ };
+ });
+ });
+
+ var _extends$9 = Object.assign || function (target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i];for (var key in source) {
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }return target;
+ };
+
+ function _objectWithoutProperties$1(obj, keys) {
+ var target = {};for (var i in obj) {
+ if (keys.indexOf(i) >= 0) continue;if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;target[i] = obj[i];
+ }return target;
+ }
+
+ /**
+ * A public higher-order component to access the imperative API
+ */
+ var withRouter = function withRouter(Component) {
+ var C = function C(props) {
+ var wrappedComponentRef = props.wrappedComponentRef,
+ remainingProps = _objectWithoutProperties$1(props, ["wrappedComponentRef"]);
+
+ return React.createElement(Route, {
+ children: function children(routeComponentProps) {
+ return React.createElement(Component, _extends$9({}, remainingProps, routeComponentProps, {
+ ref: wrappedComponentRef
+ }));
+ }
+ });
+ };
+
+ C.displayName = "withRouter(" + (Component.displayName || Component.name) + ")";
+ C.WrappedComponent = Component;
+ C.propTypes = {
+ wrappedComponentRef: propTypes.func
+ };
+
+ return hoistNonReactStatics(C, Component);
+ };
+
+ // Written in this round about way for babel-transform-imports
+
+ exports.BrowserRouter = BrowserRouter;
+ exports.HashRouter = HashRouter;
+ exports.Link = Link;
+ exports.MemoryRouter = MemoryRouter;
+ exports.NavLink = NavLink;
+ exports.Prompt = Prompt;
+ exports.Redirect = Redirect;
+ exports.Route = Route;
+ exports.Router = Router;
+ exports.StaticRouter = StaticRouter;
+ exports.Switch = Switch;
+ exports.generatePath = generatePath;
+ exports.matchPath = matchPath;
+ exports.withRouter = withRouter;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/devtools/client/shared/vendor/react-test-renderer-shallow.js b/devtools/client/shared/vendor/react-test-renderer-shallow.js
new file mode 100644
index 0000000000..38db5e125a
--- /dev/null
+++ b/devtools/client/shared/vendor/react-test-renderer-shallow.js
@@ -0,0 +1,955 @@
+/** @license React v16.8.6
+ * react-test-renderer-shallow.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require("resource://devtools/client/shared/vendor/react.js")) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react'], factory) :
+ (global.ReactShallowRenderer = factory(global.React));
+}(this, (function (React) { 'use strict';
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+/**
+ * WARNING: DO NOT manually require this module.
+ * This is a replacement for `invariant(...)` used by the error code system
+ * and will _only_ be required by the corresponding babel pass.
+ * It always throws.
+ */
+function reactProdInvariant(code) {
+ var argCount = arguments.length - 1;
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
+ for (var argIdx = 0; argIdx < argCount; argIdx++) {
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
+ }
+ // Rename it so that our build transform doesn't attempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
+}
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+var REACT_ASYNC_MODE_TYPE = hasSymbol ? Symbol.for('react.async_mode') : 0xeacf;
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+function typeOf(object) {
+ if (typeof object === 'object' && object !== null) {
+ var $$typeof = object.$$typeof;
+ switch ($$typeof) {
+ case REACT_ELEMENT_TYPE:
+ var type = object.type;
+
+ switch (type) {
+ case REACT_ASYNC_MODE_TYPE:
+ case REACT_CONCURRENT_MODE_TYPE:
+ case REACT_FRAGMENT_TYPE:
+ case REACT_PROFILER_TYPE:
+ case REACT_STRICT_MODE_TYPE:
+ case REACT_SUSPENSE_TYPE:
+ return type;
+ default:
+ var $$typeofType = type && type.$$typeof;
+
+ switch ($$typeofType) {
+ case REACT_CONTEXT_TYPE:
+ case REACT_FORWARD_REF_TYPE:
+ case REACT_PROVIDER_TYPE:
+ return $$typeofType;
+ default:
+ return $$typeof;
+ }
+ }
+ case REACT_LAZY_TYPE:
+ case REACT_MEMO_TYPE:
+ case REACT_PORTAL_TYPE:
+ return $$typeof;
+ }
+ }
+
+ return undefined;
+}
+
+// AsyncMode is deprecated along with isAsyncMode
+
+
+
+
+
+var ForwardRef = REACT_FORWARD_REF_TYPE;
+
+
+
+
+
+
+
+
+// AsyncMode should be deprecated
+
+
+
+
+
+function isForwardRef(object) {
+ return typeOf(object) === REACT_FORWARD_REF_TYPE;
+}
+
+
+function isMemo(object) {
+ return typeOf(object) === REACT_MEMO_TYPE;
+}
+
+var BEFORE_SLASH_RE = /^(.*)[\\\/]/;
+
+var describeComponentFrame = function (name, source, ownerName) {
+ var sourceInfo = '';
+ if (source) {
+ var path = source.fileName;
+ var fileName = path.replace(BEFORE_SLASH_RE, '');
+ sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
+ } else if (ownerName) {
+ sourceInfo = ' (created by ' + ownerName + ')';
+ }
+ return '\n in ' + (name || 'Unknown') + sourceInfo;
+};
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var Resolved = 1;
+
+
+function refineResolvedLazyComponent(lazyComponent) {
+ return lazyComponent._status === Resolved ? lazyComponent._result : null;
+}
+
+function getWrappedName(outerType, innerType, wrapperName) {
+ var functionName = innerType.displayName || innerType.name || '';
+ return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
+}
+
+function getComponentName(type) {
+ if (type == null) {
+ // Host root, text node or just invalid type.
+ return null;
+ }
+ if (typeof type === 'function') {
+ return type.displayName || type.name || null;
+ }
+ if (typeof type === 'string') {
+ return type;
+ }
+ switch (type) {
+ case REACT_CONCURRENT_MODE_TYPE:
+ return 'ConcurrentMode';
+ case REACT_FRAGMENT_TYPE:
+ return 'Fragment';
+ case REACT_PORTAL_TYPE:
+ return 'Portal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_SUSPENSE_TYPE:
+ return 'Suspense';
+ }
+ if (typeof type === 'object') {
+ switch (type.$$typeof) {
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_FORWARD_REF_TYPE:
+ return getWrappedName(type, type.render, 'ForwardRef');
+ case REACT_MEMO_TYPE:
+ return getComponentName(type.type);
+ case REACT_LAZY_TYPE:
+ {
+ var thenable = type;
+ var resolvedThenable = refineResolvedLazyComponent(thenable);
+ if (resolvedThenable) {
+ return getComponentName(resolvedThenable);
+ }
+ }
+ }
+ }
+ return null;
+}
+
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
+ ;
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+/**
+ * Performs equality by iterating through keys on an object and returning false
+ * when any key has values which are not strictly equal between the arguments.
+ * Returns true when the values of all keys are strictly equal.
+ */
+function shallowEqual(objA, objB) {
+ if (is(objA, objB)) {
+ return true;
+ }
+
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
+ return false;
+ }
+
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
+
+ if (keysA.length !== keysB.length) {
+ return false;
+ }
+
+ // Test for A's keys different from B.
+ for (var i = 0; i < keysA.length; i++) {
+ if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+
+
+/**
+ * Assert that the values match with the type specs.
+ * Error messages are memorized and will only be shown once.
+ *
+ * @param {object} typeSpecs Map of name to a ReactPropType
+ * @param {object} values Runtime values that need to be type-checked
+ * @param {string} location e.g. "prop", "context", "child context"
+ * @param {string} componentName Name of the component for error messages.
+ * @param {?Function} getStack Returns the component stack.
+ * @private
+ */
+function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
+
+}
+
+var checkPropTypes_1 = checkPropTypes;
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+
+
+var RE_RENDER_LIMIT = 25;
+
+var emptyObject = {};
+function areHookInputsEqual(nextDeps, prevDeps) {
+ if (prevDeps === null) {
+ return false;
+ }
+
+ // Don't bother comparing lengths in prod because these arrays should be
+ // passed inline.
+ if (nextDeps.length !== prevDeps.length) {
+
+ }
+ for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
+ if (is(nextDeps[i], prevDeps[i])) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+var Updater = function () {
+ function Updater(renderer) {
+ _classCallCheck(this, Updater);
+
+ this._renderer = renderer;
+ this._callbacks = [];
+ }
+
+ Updater.prototype._enqueueCallback = function _enqueueCallback(callback, publicInstance) {
+ if (typeof callback === 'function' && publicInstance) {
+ this._callbacks.push({
+ callback: callback,
+ publicInstance: publicInstance
+ });
+ }
+ };
+
+ Updater.prototype._invokeCallbacks = function _invokeCallbacks() {
+ var callbacks = this._callbacks;
+ this._callbacks = [];
+
+ callbacks.forEach(function (_ref) {
+ var callback = _ref.callback,
+ publicInstance = _ref.publicInstance;
+
+ callback.call(publicInstance);
+ });
+ };
+
+ Updater.prototype.isMounted = function isMounted(publicInstance) {
+ return !!this._renderer._element;
+ };
+
+ Updater.prototype.enqueueForceUpdate = function enqueueForceUpdate(publicInstance, callback, callerName) {
+ this._enqueueCallback(callback, publicInstance);
+ this._renderer._forcedUpdate = true;
+ this._renderer.render(this._renderer._element, this._renderer._context);
+ };
+
+ Updater.prototype.enqueueReplaceState = function enqueueReplaceState(publicInstance, completeState, callback, callerName) {
+ this._enqueueCallback(callback, publicInstance);
+ this._renderer._newState = completeState;
+ this._renderer.render(this._renderer._element, this._renderer._context);
+ };
+
+ Updater.prototype.enqueueSetState = function enqueueSetState(publicInstance, partialState, callback, callerName) {
+ this._enqueueCallback(callback, publicInstance);
+ var currentState = this._renderer._newState || publicInstance.state;
+
+ if (typeof partialState === 'function') {
+ partialState = partialState.call(publicInstance, currentState, publicInstance.props);
+ }
+
+ // Null and undefined are treated as no-ops.
+ if (partialState === null || partialState === undefined) {
+ return;
+ }
+
+ this._renderer._newState = _assign({}, currentState, partialState);
+
+ this._renderer.render(this._renderer._element, this._renderer._context);
+ };
+
+ return Updater;
+}();
+
+function createHook() {
+ return {
+ memoizedState: null,
+ queue: null,
+ next: null
+ };
+}
+
+function basicStateReducer(state, action) {
+ return typeof action === 'function' ? action(state) : action;
+}
+
+var ReactShallowRenderer = function () {
+ function ReactShallowRenderer() {
+ _classCallCheck(this, ReactShallowRenderer);
+
+ this._reset();
+ }
+
+ ReactShallowRenderer.prototype._reset = function _reset() {
+ this._context = null;
+ this._element = null;
+ this._instance = null;
+ this._newState = null;
+ this._rendered = null;
+ this._rendering = false;
+ this._forcedUpdate = false;
+ this._updater = new Updater(this);
+ this._dispatcher = this._createDispatcher();
+ this._workInProgressHook = null;
+ this._firstWorkInProgressHook = null;
+ this._isReRender = false;
+ this._didScheduleRenderPhaseUpdate = false;
+ this._renderPhaseUpdates = null;
+ this._numberOfReRenders = 0;
+ };
+
+ ReactShallowRenderer.prototype._validateCurrentlyRenderingComponent = function _validateCurrentlyRenderingComponent() {
+ !(this._rendering && !this._instance) ? reactProdInvariant('321') : void 0;
+ };
+
+ ReactShallowRenderer.prototype._createDispatcher = function _createDispatcher() {
+ var _this = this;
+
+ var useReducer = function (reducer, initialArg, init) {
+ _this._validateCurrentlyRenderingComponent();
+ _this._createWorkInProgressHook();
+ var workInProgressHook = _this._workInProgressHook;
+
+ if (_this._isReRender) {
+ // This is a re-render.
+ var _queue = workInProgressHook.queue;
+ var _dispatch = _queue.dispatch;
+ if (_this._numberOfReRenders > 0) {
+ // Apply the new render phase updates to the previous current hook.
+ if (_this._renderPhaseUpdates !== null) {
+ // Render phase updates are stored in a map of queue -> linked list
+ var firstRenderPhaseUpdate = _this._renderPhaseUpdates.get(_queue);
+ if (firstRenderPhaseUpdate !== undefined) {
+ _this._renderPhaseUpdates.delete(_queue);
+ var _newState = workInProgressHook.memoizedState;
+ var _update = firstRenderPhaseUpdate;
+ do {
+ var _action = _update.action;
+ _newState = reducer(_newState, _action);
+ _update = _update.next;
+ } while (_update !== null);
+ workInProgressHook.memoizedState = _newState;
+ return [_newState, _dispatch];
+ }
+ }
+ return [workInProgressHook.memoizedState, _dispatch];
+ }
+ // Process updates outside of render
+ var newState = workInProgressHook.memoizedState;
+ var update = _queue.first;
+ if (update !== null) {
+ do {
+ var _action2 = update.action;
+ newState = reducer(newState, _action2);
+ update = update.next;
+ } while (update !== null);
+ _queue.first = null;
+ workInProgressHook.memoizedState = newState;
+ }
+ return [newState, _dispatch];
+ } else {
+ var initialState = void 0;
+ if (reducer === basicStateReducer) {
+ // Special case for `useState`.
+ initialState = typeof initialArg === 'function' ? initialArg() : initialArg;
+ } else {
+ initialState = init !== undefined ? init(initialArg) : initialArg;
+ }
+ workInProgressHook.memoizedState = initialState;
+ var _queue2 = workInProgressHook.queue = {
+ first: null,
+ dispatch: null
+ };
+ var _dispatch2 = _queue2.dispatch = _this._dispatchAction.bind(_this, _queue2);
+ return [workInProgressHook.memoizedState, _dispatch2];
+ }
+ };
+
+ var useState = function (initialState) {
+ return useReducer(basicStateReducer,
+ // useReducer has a special case to support lazy useState initializers
+ initialState);
+ };
+
+ var useMemo = function (nextCreate, deps) {
+ _this._validateCurrentlyRenderingComponent();
+ _this._createWorkInProgressHook();
+
+ var nextDeps = deps !== undefined ? deps : null;
+
+ if (_this._workInProgressHook !== null && _this._workInProgressHook.memoizedState !== null) {
+ var prevState = _this._workInProgressHook.memoizedState;
+ var prevDeps = prevState[1];
+ if (nextDeps !== null) {
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+
+ var nextValue = nextCreate();
+ _this._workInProgressHook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+ };
+
+ var useRef = function (initialValue) {
+ _this._validateCurrentlyRenderingComponent();
+ _this._createWorkInProgressHook();
+ var previousRef = _this._workInProgressHook.memoizedState;
+ if (previousRef === null) {
+ var ref = { current: initialValue };
+ _this._workInProgressHook.memoizedState = ref;
+ return ref;
+ } else {
+ return previousRef;
+ }
+ };
+
+ var readContext = function (context, observedBits) {
+ return context._currentValue;
+ };
+
+ var noOp = function () {
+ _this._validateCurrentlyRenderingComponent();
+ };
+
+ var identity = function (fn) {
+ return fn;
+ };
+
+ return {
+ readContext: readContext,
+ useCallback: identity,
+ useContext: function (context) {
+ _this._validateCurrentlyRenderingComponent();
+ return readContext(context);
+ },
+ useDebugValue: noOp,
+ useEffect: noOp,
+ useImperativeHandle: noOp,
+ useLayoutEffect: noOp,
+ useMemo: useMemo,
+ useReducer: useReducer,
+ useRef: useRef,
+ useState: useState
+ };
+ };
+
+ ReactShallowRenderer.prototype._dispatchAction = function _dispatchAction(queue, action) {
+ !(this._numberOfReRenders < RE_RENDER_LIMIT) ? reactProdInvariant('301') : void 0;
+
+ if (this._rendering) {
+ // This is a render phase update. Stash it in a lazily-created map of
+ // queue -> linked list of updates. After this render pass, we'll restart
+ // and apply the stashed updates on top of the work-in-progress hook.
+ this._didScheduleRenderPhaseUpdate = true;
+ var update = {
+ action: action,
+ next: null
+ };
+ var renderPhaseUpdates = this._renderPhaseUpdates;
+ if (renderPhaseUpdates === null) {
+ this._renderPhaseUpdates = renderPhaseUpdates = new Map();
+ }
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate === undefined) {
+ renderPhaseUpdates.set(queue, update);
+ } else {
+ // Append the update to the end of the list.
+ var lastRenderPhaseUpdate = firstRenderPhaseUpdate;
+ while (lastRenderPhaseUpdate.next !== null) {
+ lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
+ }
+ lastRenderPhaseUpdate.next = update;
+ }
+ } else {
+ var _update2 = {
+ action: action,
+ next: null
+ };
+
+ // Append the update to the end of the list.
+ var last = queue.first;
+ if (last === null) {
+ queue.first = _update2;
+ } else {
+ while (last.next !== null) {
+ last = last.next;
+ }
+ last.next = _update2;
+ }
+
+ // Re-render now.
+ this.render(this._element, this._context);
+ }
+ };
+
+ ReactShallowRenderer.prototype._createWorkInProgressHook = function _createWorkInProgressHook() {
+ if (this._workInProgressHook === null) {
+ // This is the first hook in the list
+ if (this._firstWorkInProgressHook === null) {
+ this._isReRender = false;
+ this._firstWorkInProgressHook = this._workInProgressHook = createHook();
+ } else {
+ // There's already a work-in-progress. Reuse it.
+ this._isReRender = true;
+ this._workInProgressHook = this._firstWorkInProgressHook;
+ }
+ } else {
+ if (this._workInProgressHook.next === null) {
+ this._isReRender = false;
+ // Append to the end of the list
+ this._workInProgressHook = this._workInProgressHook.next = createHook();
+ } else {
+ // There's already a work-in-progress. Reuse it.
+ this._isReRender = true;
+ this._workInProgressHook = this._workInProgressHook.next;
+ }
+ }
+ return this._workInProgressHook;
+ };
+
+ ReactShallowRenderer.prototype._finishHooks = function _finishHooks(element, context) {
+ if (this._didScheduleRenderPhaseUpdate) {
+ // Updates were scheduled during the render phase. They are stored in
+ // the `renderPhaseUpdates` map. Call the component again, reusing the
+ // work-in-progress hooks and applying the additional updates on top. Keep
+ // restarting until no more updates are scheduled.
+ this._didScheduleRenderPhaseUpdate = false;
+ this._numberOfReRenders += 1;
+
+ // Start over from the beginning of the list
+ this._workInProgressHook = null;
+ this._rendering = false;
+ this.render(element, context);
+ } else {
+ this._workInProgressHook = null;
+ this._renderPhaseUpdates = null;
+ this._numberOfReRenders = 0;
+ }
+ };
+
+ ReactShallowRenderer.prototype.getMountedInstance = function getMountedInstance() {
+ return this._instance;
+ };
+
+ ReactShallowRenderer.prototype.getRenderOutput = function getRenderOutput() {
+ return this._rendered;
+ };
+
+ ReactShallowRenderer.prototype.render = function render(element) {
+ var context = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : emptyObject;
+
+ !React.isValidElement(element) ? reactProdInvariant('12', typeof element === 'function' ? ' Instead of passing a component class, make sure to instantiate ' + 'it by passing it to React.createElement.' : '') : void 0;
+ element = element;
+ // Show a special message for host elements since it's a common case.
+ !(typeof element.type !== 'string') ? reactProdInvariant('13', element.type) : void 0;
+ !(isForwardRef(element) || typeof element.type === 'function' || isMemo(element.type)) ? reactProdInvariant('249', Array.isArray(element.type) ? 'array' : element.type === null ? 'null' : typeof element.type) : void 0;
+
+ if (this._rendering) {
+ return;
+ }
+ if (this._element != null && this._element.type !== element.type) {
+ this._reset();
+ }
+
+ var elementType = isMemo(element.type) ? element.type.type : element.type;
+ var previousElement = this._element;
+
+ this._rendering = true;
+ this._element = element;
+ this._context = getMaskedContext(elementType.contextTypes, context);
+
+ // Inner memo component props aren't currently validated in createElement.
+ if (isMemo(element.type) && elementType.propTypes) {
+ currentlyValidatingElement = element;
+ checkPropTypes_1(elementType.propTypes, element.props, 'prop', getComponentName(elementType), getStackAddendum);
+ }
+
+ if (this._instance) {
+ this._updateClassComponent(elementType, element, this._context);
+ } else {
+ if (shouldConstruct(elementType)) {
+ this._instance = new elementType(element.props, this._context, this._updater);
+ if (typeof elementType.getDerivedStateFromProps === 'function') {
+ var partialState = elementType.getDerivedStateFromProps.call(null, element.props, this._instance.state);
+ if (partialState != null) {
+ this._instance.state = _assign({}, this._instance.state, partialState);
+ }
+ }
+
+ if (elementType.contextTypes) {
+ currentlyValidatingElement = element;
+ checkPropTypes_1(elementType.contextTypes, this._context, 'context', getName(elementType, this._instance), getStackAddendum);
+
+ currentlyValidatingElement = null;
+ }
+
+ this._mountClassComponent(elementType, element, this._context);
+ } else {
+ var shouldRender = true;
+ if (isMemo(element.type) && previousElement !== null) {
+ // This is a Memo component that is being re-rendered.
+ var compare = element.type.compare || shallowEqual;
+ if (compare(previousElement.props, element.props)) {
+ shouldRender = false;
+ }
+ }
+ if (shouldRender) {
+ var prevDispatcher = ReactCurrentDispatcher.current;
+ ReactCurrentDispatcher.current = this._dispatcher;
+ try {
+ // elementType could still be a ForwardRef if it was
+ // nested inside Memo.
+ if (elementType.$$typeof === ForwardRef) {
+ !(typeof elementType.render === 'function') ? reactProdInvariant('322', typeof elementType.render) : void 0;
+ this._rendered = elementType.render.call(undefined, element.props, element.ref);
+ } else {
+ this._rendered = elementType(element.props, this._context);
+ }
+ } finally {
+ ReactCurrentDispatcher.current = prevDispatcher;
+ }
+ this._finishHooks(element, context);
+ }
+ }
+ }
+
+ this._rendering = false;
+ this._updater._invokeCallbacks();
+
+ return this.getRenderOutput();
+ };
+
+ ReactShallowRenderer.prototype.unmount = function unmount() {
+ if (this._instance) {
+ if (typeof this._instance.componentWillUnmount === 'function') {
+ this._instance.componentWillUnmount();
+ }
+ }
+ this._reset();
+ };
+
+ ReactShallowRenderer.prototype._mountClassComponent = function _mountClassComponent(elementType, element, context) {
+ this._instance.context = context;
+ this._instance.props = element.props;
+ this._instance.state = this._instance.state || null;
+ this._instance.updater = this._updater;
+
+ if (typeof this._instance.UNSAFE_componentWillMount === 'function' || typeof this._instance.componentWillMount === 'function') {
+ var beforeState = this._newState;
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof elementType.getDerivedStateFromProps !== 'function' && typeof this._instance.getSnapshotBeforeUpdate !== 'function') {
+ if (typeof this._instance.componentWillMount === 'function') {
+ this._instance.componentWillMount();
+ }
+ if (typeof this._instance.UNSAFE_componentWillMount === 'function') {
+ this._instance.UNSAFE_componentWillMount();
+ }
+ }
+
+ // setState may have been called during cWM
+ if (beforeState !== this._newState) {
+ this._instance.state = this._newState || emptyObject;
+ }
+ }
+
+ this._rendered = this._instance.render();
+ // Intentionally do not call componentDidMount()
+ // because DOM refs are not available.
+ };
+
+ ReactShallowRenderer.prototype._updateClassComponent = function _updateClassComponent(elementType, element, context) {
+ var props = element.props;
+
+
+ var oldState = this._instance.state || emptyObject;
+ var oldProps = this._instance.props;
+
+ if (oldProps !== props) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof elementType.getDerivedStateFromProps !== 'function' && typeof this._instance.getSnapshotBeforeUpdate !== 'function') {
+ if (typeof this._instance.componentWillReceiveProps === 'function') {
+ this._instance.componentWillReceiveProps(props, context);
+ }
+ if (typeof this._instance.UNSAFE_componentWillReceiveProps === 'function') {
+ this._instance.UNSAFE_componentWillReceiveProps(props, context);
+ }
+ }
+ }
+
+ // Read state after cWRP in case it calls setState
+ var state = this._newState || oldState;
+ if (typeof elementType.getDerivedStateFromProps === 'function') {
+ var partialState = elementType.getDerivedStateFromProps.call(null, props, state);
+ if (partialState != null) {
+ state = _assign({}, state, partialState);
+ }
+ }
+
+ var shouldUpdate = true;
+ if (this._forcedUpdate) {
+ shouldUpdate = true;
+ this._forcedUpdate = false;
+ } else if (typeof this._instance.shouldComponentUpdate === 'function') {
+ shouldUpdate = !!this._instance.shouldComponentUpdate(props, state, context);
+ } else if (elementType.prototype && elementType.prototype.isPureReactComponent) {
+ shouldUpdate = !shallowEqual(oldProps, props) || !shallowEqual(oldState, state);
+ }
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof elementType.getDerivedStateFromProps !== 'function' && typeof this._instance.getSnapshotBeforeUpdate !== 'function') {
+ if (typeof this._instance.componentWillUpdate === 'function') {
+ this._instance.componentWillUpdate(props, state, context);
+ }
+ if (typeof this._instance.UNSAFE_componentWillUpdate === 'function') {
+ this._instance.UNSAFE_componentWillUpdate(props, state, context);
+ }
+ }
+ }
+
+ this._instance.context = context;
+ this._instance.props = props;
+ this._instance.state = state;
+ this._newState = null;
+
+ if (shouldUpdate) {
+ this._rendered = this._instance.render();
+ }
+ // Intentionally do not call componentDidUpdate()
+ // because DOM refs are not available.
+ };
+
+ return ReactShallowRenderer;
+}();
+
+ReactShallowRenderer.createRenderer = function () {
+ return new ReactShallowRenderer();
+};
+
+var currentlyValidatingElement = null;
+
+function getDisplayName(element) {
+ if (element == null) {
+ return '#empty';
+ } else if (typeof element === 'string' || typeof element === 'number') {
+ return '#text';
+ } else if (typeof element.type === 'string') {
+ return element.type;
+ } else {
+ var elementType = isMemo(element.type) ? element.type.type : element.type;
+ return elementType.displayName || elementType.name || 'Unknown';
+ }
+}
+
+function getStackAddendum() {
+ var stack = '';
+ if (currentlyValidatingElement) {
+ var name = getDisplayName(currentlyValidatingElement);
+ var owner = currentlyValidatingElement._owner;
+ stack += describeComponentFrame(name, currentlyValidatingElement._source, owner && getComponentName(owner.type));
+ }
+ return stack;
+}
+
+function getName(type, instance) {
+ var constructor = instance && instance.constructor;
+ return type.displayName || constructor && constructor.displayName || type.name || constructor && constructor.name || null;
+}
+
+function shouldConstruct(Component) {
+ return !!(Component.prototype && Component.prototype.isReactComponent);
+}
+
+function getMaskedContext(contextTypes, unmaskedContext) {
+ if (!contextTypes || !unmaskedContext) {
+ return emptyObject;
+ }
+ var context = {};
+ for (var key in contextTypes) {
+ context[key] = unmaskedContext[key];
+ }
+ return context;
+}
+
+
+
+var ReactShallowRenderer$2 = ({
+ default: ReactShallowRenderer
+});
+
+var ReactShallowRenderer$3 = ( ReactShallowRenderer$2 && ReactShallowRenderer ) || ReactShallowRenderer$2;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var shallow = ReactShallowRenderer$3.default || ReactShallowRenderer$3;
+
+return shallow;
+
+})));
diff --git a/devtools/client/shared/vendor/react-test-renderer.js b/devtools/client/shared/vendor/react-test-renderer.js
new file mode 100644
index 0000000000..60f98c8a4f
--- /dev/null
+++ b/devtools/client/shared/vendor/react-test-renderer.js
@@ -0,0 +1,10580 @@
+/** @license React v16.8.6
+ * react-test-renderer.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require("resource://devtools/client/shared/vendor/react.js")) :
+ typeof define === 'function' && define.amd ? define(['devtools/client/shared/vendor/react'], factory) :
+ (global.ReactTestRenderer = factory(global.React));
+}(this, (function (React) { 'use strict';
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+/**
+ * WARNING: DO NOT manually require this module.
+ * This is a replacement for `invariant(...)` used by the error code system
+ * and will _only_ be required by the corresponding babel pass.
+ * It always throws.
+ */
+function reactProdInvariant(code) {
+ var argCount = arguments.length - 1;
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
+ for (var argIdx = 0; argIdx < argCount; argIdx++) {
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
+ }
+ // Rename it so that our build transform doesn't attempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
+}
+
+var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _assign = ReactInternals.assign;
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+/**
+ * `ReactInstanceMap` maintains a mapping from a public facing stateful
+ * instance (key) and the internal representation (value). This allows public
+ * methods to accept the user facing instance as an argument and map them back
+ * to internal methods.
+ *
+ * Note that this module is currently shared and assumed to be stateless.
+ * If this becomes an actual Map, that will break.
+ */
+
+/**
+ * This API should be called `delete` but we'd have to make sure to always
+ * transform these to strings for IE support. When this transform is fully
+ * supported we can rename it.
+ */
+
+
+function get(key) {
+ return key._reactInternalFiber;
+}
+
+
+
+function set(key, value) {
+ key._reactInternalFiber = value;
+}
+
+var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+// Prevent newer renderers from RTE when used with older react package versions.
+// Current owner and dispatcher used to share the same ref,
+// but PR #14548 split them out to better support the react-debug-tools package.
+if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
+ ReactSharedInternals.ReactCurrentDispatcher = {
+ current: null
+ };
+}
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+var FAUX_ITERATOR_SYMBOL = '@@iterator';
+
+function getIteratorFn(maybeIterable) {
+ if (maybeIterable === null || typeof maybeIterable !== 'object') {
+ return null;
+ }
+ var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
+ if (typeof maybeIterator === 'function') {
+ return maybeIterator;
+ }
+ return null;
+}
+
+var Pending = 0;
+var Resolved = 1;
+var Rejected = 2;
+
+function refineResolvedLazyComponent(lazyComponent) {
+ return lazyComponent._status === Resolved ? lazyComponent._result : null;
+}
+
+function getWrappedName(outerType, innerType, wrapperName) {
+ var functionName = innerType.displayName || innerType.name || '';
+ return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
+}
+
+function getComponentName(type) {
+ if (type == null) {
+ // Host root, text node or just invalid type.
+ return null;
+ }
+ if (typeof type === 'function') {
+ return type.displayName || type.name || null;
+ }
+ if (typeof type === 'string') {
+ return type;
+ }
+ switch (type) {
+ case REACT_CONCURRENT_MODE_TYPE:
+ return 'ConcurrentMode';
+ case REACT_FRAGMENT_TYPE:
+ return 'Fragment';
+ case REACT_PORTAL_TYPE:
+ return 'Portal';
+ case REACT_PROFILER_TYPE:
+ return 'Profiler';
+ case REACT_STRICT_MODE_TYPE:
+ return 'StrictMode';
+ case REACT_SUSPENSE_TYPE:
+ return 'Suspense';
+ }
+ if (typeof type === 'object') {
+ switch (type.$$typeof) {
+ case REACT_CONTEXT_TYPE:
+ return 'Context.Consumer';
+ case REACT_PROVIDER_TYPE:
+ return 'Context.Provider';
+ case REACT_FORWARD_REF_TYPE:
+ return getWrappedName(type, type.render, 'ForwardRef');
+ case REACT_MEMO_TYPE:
+ return getComponentName(type.type);
+ case REACT_LAZY_TYPE:
+ {
+ var thenable = type;
+ var resolvedThenable = refineResolvedLazyComponent(thenable);
+ if (resolvedThenable) {
+ return getComponentName(resolvedThenable);
+ }
+ }
+ }
+ }
+ return null;
+}
+
+var FunctionComponent = 0;
+var ClassComponent = 1;
+var IndeterminateComponent = 2; // Before we know whether it is function or class
+var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
+var HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
+var HostComponent = 5;
+var HostText = 6;
+var Fragment = 7;
+var Mode = 8;
+var ContextConsumer = 9;
+var ContextProvider = 10;
+var ForwardRef = 11;
+var Profiler = 12;
+var SuspenseComponent = 13;
+var MemoComponent = 14;
+var SimpleMemoComponent = 15;
+var LazyComponent = 16;
+var IncompleteClassComponent = 17;
+var DehydratedSuspenseComponent = 18;
+
+// Don't change these two values. They're used by React Dev Tools.
+var NoEffect = /* */0;
+var PerformedWork = /* */1;
+
+// You can change the rest (and add more).
+var Placement = /* */2;
+var Update = /* */4;
+var PlacementAndUpdate = /* */6;
+var Deletion = /* */8;
+var ContentReset = /* */16;
+var Callback = /* */32;
+var DidCapture = /* */64;
+var Ref = /* */128;
+var Snapshot = /* */256;
+var Passive = /* */512;
+
+// Passive & Update & Callback & Ref & Snapshot
+var LifecycleEffectMask = /* */932;
+
+// Union of all host effects
+var HostEffectMask = /* */1023;
+
+var Incomplete = /* */1024;
+var ShouldCapture = /* */2048;
+
+var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
+
+var MOUNTING = 1;
+var MOUNTED = 2;
+var UNMOUNTED = 3;
+
+function isFiberMountedImpl(fiber) {
+ var node = fiber;
+ if (!fiber.alternate) {
+ // If there is no alternate, this might be a new tree that isn't inserted
+ // yet. If it is, then it will have a pending insertion effect on it.
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ while (node.return) {
+ node = node.return;
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
+ }
+ } else {
+ while (node.return) {
+ node = node.return;
+ }
+ }
+ if (node.tag === HostRoot) {
+ // TODO: Check if this was a nested HostRoot when used with
+ // renderContainerIntoSubtree.
+ return MOUNTED;
+ }
+ // If we didn't hit the root, that means that we're in an disconnected tree
+ // that has been unmounted.
+ return UNMOUNTED;
+}
+
+function isFiberMounted(fiber) {
+ return isFiberMountedImpl(fiber) === MOUNTED;
+}
+
+function isMounted(component) {
+ var fiber = get(component);
+ if (!fiber) {
+ return false;
+ }
+ return isFiberMountedImpl(fiber) === MOUNTED;
+}
+
+function assertIsMounted(fiber) {
+ !(isFiberMountedImpl(fiber) === MOUNTED) ? reactProdInvariant('188') : void 0;
+}
+
+function findCurrentFiberUsingSlowPath(fiber) {
+ var alternate = fiber.alternate;
+ if (!alternate) {
+ // If there is no alternate, then we only need to check if it is mounted.
+ var state = isFiberMountedImpl(fiber);
+ !(state !== UNMOUNTED) ? reactProdInvariant('188') : void 0;
+ if (state === MOUNTING) {
+ return null;
+ }
+ return fiber;
+ }
+ // If we have two possible branches, we'll walk backwards up to the root
+ // to see what path the root points to. On the way we may hit one of the
+ // special cases and we'll deal with them.
+ var a = fiber;
+ var b = alternate;
+ while (true) {
+ var parentA = a.return;
+ var parentB = parentA ? parentA.alternate : null;
+ if (!parentA || !parentB) {
+ // We're at the root.
+ break;
+ }
+
+ // If both copies of the parent fiber point to the same child, we can
+ // assume that the child is current. This happens when we bailout on low
+ // priority: the bailed out fiber's child reuses the current child.
+ if (parentA.child === parentB.child) {
+ var child = parentA.child;
+ while (child) {
+ if (child === a) {
+ // We've determined that A is the current branch.
+ assertIsMounted(parentA);
+ return fiber;
+ }
+ if (child === b) {
+ // We've determined that B is the current branch.
+ assertIsMounted(parentA);
+ return alternate;
+ }
+ child = child.sibling;
+ }
+ // We should never have an alternate for any mounting node. So the only
+ // way this could possibly happen is if this was unmounted, if at all.
+ reactProdInvariant('188');
+ }
+
+ if (a.return !== b.return) {
+ // The return pointer of A and the return pointer of B point to different
+ // fibers. We assume that return pointers never criss-cross, so A must
+ // belong to the child set of A.return, and B must belong to the child
+ // set of B.return.
+ a = parentA;
+ b = parentB;
+ } else {
+ // The return pointers point to the same fiber. We'll have to use the
+ // default, slow path: scan the child sets of each parent alternate to see
+ // which child belongs to which set.
+ //
+ // Search parent A's child set
+ var didFindChild = false;
+ var _child = parentA.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentA;
+ b = parentB;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentA;
+ a = parentB;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ if (!didFindChild) {
+ // Search parent B's child set
+ _child = parentB.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentB;
+ b = parentA;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentB;
+ a = parentA;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ !didFindChild ? reactProdInvariant('189') : void 0;
+ }
+ }
+
+ !(a.alternate === b) ? reactProdInvariant('190') : void 0;
+ }
+ // If the root is not a host container, we're in a disconnected tree. I.e.
+ // unmounted.
+ !(a.tag === HostRoot) ? reactProdInvariant('188') : void 0;
+ if (a.stateNode.current === a) {
+ // We've determined that A is the current branch.
+ return fiber;
+ }
+ // Otherwise B has to be current branch.
+ return alternate;
+}
+
+function findCurrentHostFiber(parent) {
+ var currentParent = findCurrentFiberUsingSlowPath(parent);
+ if (!currentParent) {
+ return null;
+ }
+
+ // Next we'll drill down this component to find the first HostComponent/Text.
+ var node = currentParent;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ return node;
+ } else if (node.child) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === currentParent) {
+ return null;
+ }
+ while (!node.sibling) {
+ if (!node.return || node.return === currentParent) {
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ // Flow needs the return null here, but ESLint complains about it.
+ // eslint-disable-next-line no-unreachable
+ return null;
+}
+
+// Current virtual time
+var nowImplementation = function () {
+ return 0;
+};
+var scheduledCallback = null;
+var yieldedValues = [];
+
+var didStop = false;
+var expectedNumberOfYields = -1;
+
+function scheduleDeferredCallback$1(callback, options) {
+ scheduledCallback = callback;
+ var fakeCallbackId = 0;
+ return fakeCallbackId;
+}
+
+function cancelDeferredCallback$1(timeoutID) {
+ scheduledCallback = null;
+}
+
+function setNowImplementation(implementation) {
+ nowImplementation = implementation;
+}
+
+function shouldYield$1() {
+ if (expectedNumberOfYields !== -1 && yieldedValues.length >= expectedNumberOfYields) {
+ // We yielded at least as many values as expected. Stop rendering.
+ didStop = true;
+ return true;
+ }
+ // Keep rendering.
+ return false;
+}
+
+function flushAll() {
+ yieldedValues = [];
+ while (scheduledCallback !== null) {
+ var cb = scheduledCallback;
+ scheduledCallback = null;
+ cb();
+ }
+ var values = yieldedValues;
+ yieldedValues = [];
+ return values;
+}
+
+function flushNumberOfYields(count) {
+ expectedNumberOfYields = count;
+ didStop = false;
+ yieldedValues = [];
+ try {
+ while (scheduledCallback !== null && !didStop) {
+ var cb = scheduledCallback;
+ scheduledCallback = null;
+ cb();
+ }
+ return yieldedValues;
+ } finally {
+ expectedNumberOfYields = -1;
+ didStop = false;
+ yieldedValues = [];
+ }
+}
+
+function yieldValue(value) {
+ yieldedValues.push(value);
+}
+
+function clearYields() {
+ var values = yieldedValues;
+ yieldedValues = [];
+ return values;
+}
+
+// Renderers that don't support persistence
+// can re-export everything from this module.
+
+function shim() {
+ reactProdInvariant('270');
+}
+
+// Persistence (when unsupported)
+var supportsPersistence = false;
+var cloneInstance = shim;
+var createContainerChildSet = shim;
+var appendChildToContainerChildSet = shim;
+var finalizeContainerChildren = shim;
+var replaceContainerChildren = shim;
+var cloneHiddenInstance = shim;
+var cloneUnhiddenInstance = shim;
+var createHiddenTextInstance = shim;
+
+// Renderers that don't support hydration
+// can re-export everything from this module.
+
+function shim$1() {
+ reactProdInvariant('305');
+}
+
+// Hydration (when unsupported)
+
+var supportsHydration = false;
+var canHydrateInstance = shim$1;
+var canHydrateTextInstance = shim$1;
+var canHydrateSuspenseInstance = shim$1;
+var getNextHydratableSibling = shim$1;
+var getFirstHydratableChild = shim$1;
+var hydrateInstance = shim$1;
+var hydrateTextInstance = shim$1;
+var getNextHydratableInstanceAfterSuspenseInstance = shim$1;
+var clearSuspenseBoundary = shim$1;
+var clearSuspenseBoundaryFromContainer = shim$1;
+
+var NO_CONTEXT = {};
+var UPDATE_SIGNAL = {};
+function getPublicInstance(inst) {
+ switch (inst.tag) {
+ case 'INSTANCE':
+ var _createNodeMock = inst.rootContainerInstance.createNodeMock;
+ return _createNodeMock({
+ type: inst.type,
+ props: inst.props
+ });
+ default:
+ return inst;
+ }
+}
+
+function appendChild(parentInstance, child) {
+ var index = parentInstance.children.indexOf(child);
+ if (index !== -1) {
+ parentInstance.children.splice(index, 1);
+ }
+ parentInstance.children.push(child);
+}
+
+function insertBefore(parentInstance, child, beforeChild) {
+ var index = parentInstance.children.indexOf(child);
+ if (index !== -1) {
+ parentInstance.children.splice(index, 1);
+ }
+ var beforeIndex = parentInstance.children.indexOf(beforeChild);
+ parentInstance.children.splice(beforeIndex, 0, child);
+}
+
+function removeChild(parentInstance, child) {
+ var index = parentInstance.children.indexOf(child);
+ parentInstance.children.splice(index, 1);
+}
+
+function getRootHostContext(rootContainerInstance) {
+ return NO_CONTEXT;
+}
+
+function getChildHostContext(parentHostContext, type, rootContainerInstance) {
+ return NO_CONTEXT;
+}
+
+function prepareForCommit(containerInfo) {
+ // noop
+}
+
+function resetAfterCommit(containerInfo) {
+ // noop
+}
+
+function createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
+ return {
+ type: type,
+ props: props,
+ isHidden: false,
+ children: [],
+ rootContainerInstance: rootContainerInstance,
+ tag: 'INSTANCE'
+ };
+}
+
+function appendInitialChild(parentInstance, child) {
+ var index = parentInstance.children.indexOf(child);
+ if (index !== -1) {
+ parentInstance.children.splice(index, 1);
+ }
+ parentInstance.children.push(child);
+}
+
+function finalizeInitialChildren(testElement, type, props, rootContainerInstance, hostContext) {
+ return false;
+}
+
+function prepareUpdate(testElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
+ return UPDATE_SIGNAL;
+}
+
+function shouldSetTextContent(type, props) {
+ return false;
+}
+
+function shouldDeprioritizeSubtree(type, props) {
+ return false;
+}
+
+function createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) {
+ return {
+ text: text,
+ isHidden: false,
+ tag: 'TEXT'
+ };
+}
+
+var isPrimaryRenderer = false;
+// This approach enables `now` to be mocked by tests,
+// Even after the reconciler has initialized and read host config values.
+var now = function () {
+ return nowImplementation();
+};
+var scheduleDeferredCallback$$1 = scheduleDeferredCallback$1;
+var cancelDeferredCallback$$1 = cancelDeferredCallback$1;
+var shouldYield$$1 = shouldYield$1;
+
+var scheduleTimeout = setTimeout;
+var cancelTimeout = clearTimeout;
+var noTimeout = -1;
+var schedulePassiveEffects = scheduleDeferredCallback$$1;
+var cancelPassiveEffects = cancelDeferredCallback$$1;
+
+// -------------------
+// Mutation
+// -------------------
+
+var supportsMutation = true;
+
+function commitUpdate(instance, updatePayload, type, oldProps, newProps, internalInstanceHandle) {
+ instance.type = type;
+ instance.props = newProps;
+}
+
+
+
+function commitTextUpdate(textInstance, oldText, newText) {
+ textInstance.text = newText;
+}
+
+function resetTextContent(testElement) {
+ // noop
+}
+
+var appendChildToContainer = appendChild;
+var insertInContainerBefore = insertBefore;
+var removeChildFromContainer = removeChild;
+
+function hideInstance(instance) {
+ instance.isHidden = true;
+}
+
+function hideTextInstance(textInstance) {
+ textInstance.isHidden = true;
+}
+
+function unhideInstance(instance, props) {
+ instance.isHidden = false;
+}
+
+function unhideTextInstance(textInstance, text) {
+ textInstance.isHidden = false;
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+var BEFORE_SLASH_RE = /^(.*)[\\\/]/;
+
+var describeComponentFrame = function (name, source, ownerName) {
+ var sourceInfo = '';
+ if (source) {
+ var path = source.fileName;
+ var fileName = path.replace(BEFORE_SLASH_RE, '');
+ sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
+ } else if (ownerName) {
+ sourceInfo = ' (created by ' + ownerName + ')';
+ }
+ return '\n in ' + (name || 'Unknown') + sourceInfo;
+};
+
+var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+
+function describeFiber(fiber) {
+ switch (fiber.tag) {
+ case HostRoot:
+ case HostPortal:
+ case HostText:
+ case Fragment:
+ case ContextProvider:
+ case ContextConsumer:
+ return '';
+ default:
+ var owner = fiber._debugOwner;
+ var source = fiber._debugSource;
+ var name = getComponentName(fiber.type);
+ var ownerName = null;
+ if (owner) {
+ ownerName = getComponentName(owner.type);
+ }
+ return describeComponentFrame(name, source, ownerName);
+ }
+}
+
+function getStackByFiberInDevAndProd(workInProgress) {
+ var info = '';
+ var node = workInProgress;
+ do {
+ info += describeFiber(node);
+ node = node.return;
+ } while (node);
+ return info;
+}
+
+var enableUserTimingAPI = false;
+
+
+var enableProfilerTimer = false;
+var enableSchedulerTracing = false;
+var enableSuspenseServerRenderer = false;
+
+
+
+
+
+// Only used in www builds.
+
+// Prefix measurements so that it's possible to filter them.
+// Longer prefixes are hard to read in DevTools.
+var reactEmoji = '\u269B';
+var warningEmoji = '\u26D4';
+var supportsUserTiming = typeof performance !== 'undefined' && typeof performance.mark === 'function' && typeof performance.clearMarks === 'function' && typeof performance.measure === 'function' && typeof performance.clearMeasures === 'function';
+
+// Keep track of current fiber so that we know the path to unwind on pause.
+// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
+var currentFiber = null;
+// If we're in the middle of user code, which fiber and method is it?
+// Reusing `currentFiber` would be confusing for this because user code fiber
+// can change during commit phase too, but we don't need to unwind it (since
+// lifecycles in the commit phase don't resemble a tree).
+var currentPhase = null;
+var currentPhaseFiber = null;
+// Did lifecycle hook schedule an update? This is often a performance problem,
+// so we will keep track of it, and include it in the report.
+// Track commits caused by cascading updates.
+var isCommitting = false;
+var hasScheduledUpdateInCurrentCommit = false;
+var hasScheduledUpdateInCurrentPhase = false;
+var commitCountInCurrentWorkLoop = 0;
+var effectCountInCurrentCommit = 0;
+var isWaitingForCallback = false;
+// During commits, we only show a measurement once per method name
+// to avoid stretch the commit phase with measurement overhead.
+var labelsInCurrentCommit = new Set();
+
+var formatMarkName = function (markName) {
+ return reactEmoji + ' ' + markName;
+};
+
+var formatLabel = function (label, warning) {
+ var prefix = warning ? warningEmoji + ' ' : reactEmoji + ' ';
+ var suffix = warning ? ' Warning: ' + warning : '';
+ return '' + prefix + label + suffix;
+};
+
+var beginMark = function (markName) {
+ performance.mark(formatMarkName(markName));
+};
+
+var clearMark = function (markName) {
+ performance.clearMarks(formatMarkName(markName));
+};
+
+var endMark = function (label, markName, warning) {
+ var formattedMarkName = formatMarkName(markName);
+ var formattedLabel = formatLabel(label, warning);
+ try {
+ performance.measure(formattedLabel, formattedMarkName);
+ } catch (err) {}
+ // If previous mark was missing for some reason, this will throw.
+ // This could only happen if React crashed in an unexpected place earlier.
+ // Don't pile on with more errors.
+
+ // Clear marks immediately to avoid growing buffer.
+ performance.clearMarks(formattedMarkName);
+ performance.clearMeasures(formattedLabel);
+};
+
+var getFiberMarkName = function (label, debugID) {
+ return label + ' (#' + debugID + ')';
+};
+
+var getFiberLabel = function (componentName, isMounted, phase) {
+ if (phase === null) {
+ // These are composite component total time measurements.
+ return componentName + ' [' + (isMounted ? 'update' : 'mount') + ']';
+ } else {
+ // Composite component methods.
+ return componentName + '.' + phase;
+ }
+};
+
+var beginFiberMark = function (fiber, phase) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+
+ if (isCommitting && labelsInCurrentCommit.has(label)) {
+ // During the commit phase, we don't show duplicate labels because
+ // there is a fixed overhead for every measurement, and we don't
+ // want to stretch the commit phase beyond necessary.
+ return false;
+ }
+ labelsInCurrentCommit.add(label);
+
+ var markName = getFiberMarkName(label, debugID);
+ beginMark(markName);
+ return true;
+};
+
+var clearFiberMark = function (fiber, phase) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ clearMark(markName);
+};
+
+var endFiberMark = function (fiber, phase, warning) {
+ var componentName = getComponentName(fiber.type) || 'Unknown';
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ endMark(label, markName, warning);
+};
+
+var shouldIgnoreFiber = function (fiber) {
+ // Host components should be skipped in the timeline.
+ // We could check typeof fiber.type, but does this work with RN?
+ switch (fiber.tag) {
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ case Fragment:
+ case ContextProvider:
+ case ContextConsumer:
+ case Mode:
+ return true;
+ default:
+ return false;
+ }
+};
+
+var clearPendingPhaseMeasurement = function () {
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ clearFiberMark(currentPhaseFiber, currentPhase);
+ }
+ currentPhaseFiber = null;
+ currentPhase = null;
+ hasScheduledUpdateInCurrentPhase = false;
+};
+
+var pauseTimers = function () {
+ // Stops all currently active measurements so that they can be resumed
+ // if we continue in a later deferred loop from the same unit of work.
+ var fiber = currentFiber;
+ while (fiber) {
+ if (fiber._debugIsCurrentlyTiming) {
+ endFiberMark(fiber, null, null);
+ }
+ fiber = fiber.return;
+ }
+};
+
+var resumeTimersRecursively = function (fiber) {
+ if (fiber.return !== null) {
+ resumeTimersRecursively(fiber.return);
+ }
+ if (fiber._debugIsCurrentlyTiming) {
+ beginFiberMark(fiber, null);
+ }
+};
+
+var resumeTimers = function () {
+ // Resumes all measurements that were active during the last deferred loop.
+ if (currentFiber !== null) {
+ resumeTimersRecursively(currentFiber);
+ }
+};
+
+function recordEffect() {
+ if (enableUserTimingAPI) {
+ effectCountInCurrentCommit++;
+ }
+}
+
+function recordScheduleUpdate() {
+ if (enableUserTimingAPI) {
+ if (isCommitting) {
+ hasScheduledUpdateInCurrentCommit = true;
+ }
+ if (currentPhase !== null && currentPhase !== 'componentWillMount' && currentPhase !== 'componentWillReceiveProps') {
+ hasScheduledUpdateInCurrentPhase = true;
+ }
+ }
+}
+
+function startRequestCallbackTimer() {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming && !isWaitingForCallback) {
+ isWaitingForCallback = true;
+ beginMark('(Waiting for async callback...)');
+ }
+ }
+}
+
+function stopRequestCallbackTimer(didExpire, expirationTime) {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming) {
+ isWaitingForCallback = false;
+ var warning = didExpire ? 'React was blocked by main thread' : null;
+ endMark('(Waiting for async callback... will force flush in ' + expirationTime + ' ms)', '(Waiting for async callback...)', warning);
+ }
+ }
+}
+
+function startWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, this is the fiber to unwind from.
+ currentFiber = fiber;
+ if (!beginFiberMark(fiber, null)) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = true;
+ }
+}
+
+function cancelWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // Remember we shouldn't complete measurement for this fiber.
+ // Otherwise flamechart will be deep even for small updates.
+ fiber._debugIsCurrentlyTiming = false;
+ clearFiberMark(fiber, null);
+ }
+}
+
+function stopWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber.return;
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ endFiberMark(fiber, null, null);
+ }
+}
+
+function stopFailedWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber.return;
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ var warning = fiber.tag === SuspenseComponent || fiber.tag === DehydratedSuspenseComponent ? 'Rendering was suspended' : 'An error was thrown inside this error boundary';
+ endFiberMark(fiber, null, warning);
+ }
+}
+
+function startPhaseTimer(fiber, phase) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ clearPendingPhaseMeasurement();
+ if (!beginFiberMark(fiber, phase)) {
+ return;
+ }
+ currentPhaseFiber = fiber;
+ currentPhase = phase;
+ }
+}
+
+function stopPhaseTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ var warning = hasScheduledUpdateInCurrentPhase ? 'Scheduled a cascading update' : null;
+ endFiberMark(currentPhaseFiber, currentPhase, warning);
+ }
+ currentPhase = null;
+ currentPhaseFiber = null;
+ }
+}
+
+function startWorkLoopTimer(nextUnitOfWork) {
+ if (enableUserTimingAPI) {
+ currentFiber = nextUnitOfWork;
+ if (!supportsUserTiming) {
+ return;
+ }
+ commitCountInCurrentWorkLoop = 0;
+ // This is top level call.
+ // Any other measurements are performed within.
+ beginMark('(React Tree Reconciliation)');
+ // Resume any measurements that were in progress during the last loop.
+ resumeTimers();
+ }
+}
+
+function stopWorkLoopTimer(interruptedBy, didCompleteRoot) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var warning = null;
+ if (interruptedBy !== null) {
+ if (interruptedBy.tag === HostRoot) {
+ warning = 'A top-level update interrupted the previous render';
+ } else {
+ var componentName = getComponentName(interruptedBy.type) || 'Unknown';
+ warning = 'An update to ' + componentName + ' interrupted the previous render';
+ }
+ } else if (commitCountInCurrentWorkLoop > 1) {
+ warning = 'There were cascading updates';
+ }
+ commitCountInCurrentWorkLoop = 0;
+ var label = didCompleteRoot ? '(React Tree Reconciliation: Completed Root)' : '(React Tree Reconciliation: Yielded)';
+ // Pause any measurements until the next loop.
+ pauseTimers();
+ endMark(label, '(React Tree Reconciliation)', warning);
+ }
+}
+
+function startCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ isCommitting = true;
+ hasScheduledUpdateInCurrentCommit = false;
+ labelsInCurrentCommit.clear();
+ beginMark('(Committing Changes)');
+ }
+}
+
+function stopCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+
+ var warning = null;
+ if (hasScheduledUpdateInCurrentCommit) {
+ warning = 'Lifecycle hook scheduled a cascading update';
+ } else if (commitCountInCurrentWorkLoop > 0) {
+ warning = 'Caused by a cascading update in earlier commit';
+ }
+ hasScheduledUpdateInCurrentCommit = false;
+ commitCountInCurrentWorkLoop++;
+ isCommitting = false;
+ labelsInCurrentCommit.clear();
+
+ endMark('(Committing Changes)', '(Committing Changes)', warning);
+ }
+}
+
+function startCommitSnapshotEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Committing Snapshot Effects)');
+ }
+}
+
+function stopCommitSnapshotEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Committing Snapshot Effects: ' + count + ' Total)', '(Committing Snapshot Effects)', null);
+ }
+}
+
+function startCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Committing Host Effects)');
+ }
+}
+
+function stopCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Committing Host Effects: ' + count + ' Total)', '(Committing Host Effects)', null);
+ }
+}
+
+function startCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark('(Calling Lifecycle Methods)');
+ }
+}
+
+function stopCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark('(Calling Lifecycle Methods: ' + count + ' Total)', '(Calling Lifecycle Methods)', null);
+ }
+}
+
+var valueStack = [];
+
+var index = -1;
+
+function createCursor(defaultValue) {
+ return {
+ current: defaultValue
+ };
+}
+
+function pop(cursor, fiber) {
+ if (index < 0) {
+ return;
+ }
+
+ cursor.current = valueStack[index];
+
+ valueStack[index] = null;
+
+ index--;
+}
+
+function push(cursor, value, fiber) {
+ index++;
+
+ valueStack[index] = cursor.current;
+
+ cursor.current = value;
+}
+
+var emptyContextObject = {};
+// A cursor to the current merged context object on the stack.
+var contextStackCursor = createCursor(emptyContextObject);
+// A cursor to a boolean indicating whether the context has changed.
+var didPerformWorkStackCursor = createCursor(false);
+// Keep track of the previous context object that was on the stack.
+// We use this to get access to the parent context after we have already
+// pushed the next context provider, and now need to merge their contexts.
+var previousContext = emptyContextObject;
+
+function getUnmaskedContext(workInProgress, Component, didPushOwnContextIfProvider) {
+ if (didPushOwnContextIfProvider && isContextProvider(Component)) {
+ // If the fiber is a context provider itself, when we read its context
+ // we may have already pushed its own child context on the stack. A context
+ // provider should not "see" its own child context. Therefore we read the
+ // previous (parent) context instead for a context provider.
+ return previousContext;
+ }
+ return contextStackCursor.current;
+}
+
+function cacheContext(workInProgress, unmaskedContext, maskedContext) {
+ var instance = workInProgress.stateNode;
+ instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
+ instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
+}
+
+function getMaskedContext(workInProgress, unmaskedContext) {
+ var type = workInProgress.type;
+ var contextTypes = type.contextTypes;
+ if (!contextTypes) {
+ return emptyContextObject;
+ }
+
+ // Avoid recreating masked context unless unmasked context has changed.
+ // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
+ // This may trigger infinite loops if componentWillReceiveProps calls setState.
+ var instance = workInProgress.stateNode;
+ if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) {
+ return instance.__reactInternalMemoizedMaskedChildContext;
+ }
+
+ var context = {};
+ for (var key in contextTypes) {
+ context[key] = unmaskedContext[key];
+ }
+
+ if (instance) {
+ cacheContext(workInProgress, unmaskedContext, context);
+ }
+
+ return context;
+}
+
+function hasContextChanged() {
+ return didPerformWorkStackCursor.current;
+}
+
+function isContextProvider(type) {
+ var childContextTypes = type.childContextTypes;
+ return childContextTypes !== null && childContextTypes !== undefined;
+}
+
+function popContext(fiber) {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
+
+function popTopLevelContextObject(fiber) {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
+
+function pushTopLevelContextObject(fiber, context, didChange) {
+ !(contextStackCursor.current === emptyContextObject) ? reactProdInvariant('168') : void 0;
+
+ push(contextStackCursor, context, fiber);
+ push(didPerformWorkStackCursor, didChange, fiber);
+}
+
+function processChildContext(fiber, type, parentContext) {
+ var instance = fiber.stateNode;
+ var childContextTypes = type.childContextTypes;
+
+ // TODO (bvaughn) Replace this behavior with an invariant() in the future.
+ // It has only been added in Fiber to match the (unintentional) behavior in Stack.
+ if (typeof instance.getChildContext !== 'function') {
+ return parentContext;
+ }
+
+ var childContext = void 0;
+ startPhaseTimer(fiber, 'getChildContext');
+ childContext = instance.getChildContext();
+ stopPhaseTimer();
+ for (var contextKey in childContext) {
+ !(contextKey in childContextTypes) ? reactProdInvariant('108', getComponentName(type) || 'Unknown', contextKey) : void 0;
+ }
+ return _assign({}, parentContext, childContext);
+}
+
+function pushContextProvider(workInProgress) {
+ var instance = workInProgress.stateNode;
+ // We push the context as early as possible to ensure stack integrity.
+ // If the instance does not exist yet, we will push null at first,
+ // and replace it on the stack later when invalidating the context.
+ var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyContextObject;
+
+ // Remember the parent context so we can merge with it later.
+ // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
+ previousContext = contextStackCursor.current;
+ push(contextStackCursor, memoizedMergedChildContext, workInProgress);
+ push(didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress);
+
+ return true;
+}
+
+function invalidateContextProvider(workInProgress, type, didChange) {
+ var instance = workInProgress.stateNode;
+ !instance ? reactProdInvariant('169') : void 0;
+
+ if (didChange) {
+ // Merge parent and own context.
+ // Skip this if we're not updating due to sCU.
+ // This avoids unnecessarily recomputing memoized values.
+ var mergedContext = processChildContext(workInProgress, type, previousContext);
+ instance.__reactInternalMemoizedMergedChildContext = mergedContext;
+
+ // Replace the old (or empty) context with the new one.
+ // It is important to unwind the context in the reverse order.
+ pop(didPerformWorkStackCursor, workInProgress);
+ pop(contextStackCursor, workInProgress);
+ // Now push the new context and mark that it has changed.
+ push(contextStackCursor, mergedContext, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ } else {
+ pop(didPerformWorkStackCursor, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ }
+}
+
+function findCurrentUnmaskedContext(fiber) {
+ // Currently this is only used with renderSubtreeIntoContainer; not sure if it
+ // makes sense elsewhere
+ !(isFiberMounted(fiber) && fiber.tag === ClassComponent) ? reactProdInvariant('170') : void 0;
+
+ var node = fiber;
+ do {
+ switch (node.tag) {
+ case HostRoot:
+ return node.stateNode.context;
+ case ClassComponent:
+ {
+ var Component = node.type;
+ if (isContextProvider(Component)) {
+ return node.stateNode.__reactInternalMemoizedMergedChildContext;
+ }
+ break;
+ }
+ }
+ node = node.return;
+ } while (node !== null);
+ reactProdInvariant('171');
+}
+
+var onCommitFiberRoot = null;
+var onCommitFiberUnmount = null;
+function catchErrors(fn) {
+ return function (arg) {
+ try {
+ return fn(arg);
+ } catch (err) {
+
+ }
+ };
+}
+
+var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined';
+
+function injectInternals(internals) {
+ if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
+ // No DevTools
+ return false;
+ }
+ var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
+ if (hook.isDisabled) {
+ // This isn't a real property on the hook, but it can be set to opt out
+ // of DevTools integration and associated warnings and logs.
+ // https://github.com/facebook/react/issues/3877
+ return true;
+ }
+ if (!hook.supportsFiber) {
+ return true;
+ }
+ try {
+ var rendererID = hook.inject(internals);
+ // We have successfully injected, so now it is safe to set up hooks.
+ onCommitFiberRoot = catchErrors(function (root) {
+ return hook.onCommitFiberRoot(rendererID, root);
+ });
+ onCommitFiberUnmount = catchErrors(function (fiber) {
+ return hook.onCommitFiberUnmount(rendererID, fiber);
+ });
+ } catch (err) {
+ // Catch all errors because it is unsafe to throw during initialization.
+
+ }
+ // DevTools exists
+ return true;
+}
+
+function onCommitRoot(root) {
+ if (typeof onCommitFiberRoot === 'function') {
+ onCommitFiberRoot(root);
+ }
+}
+
+function onCommitUnmount(fiber) {
+ if (typeof onCommitFiberUnmount === 'function') {
+ onCommitFiberUnmount(fiber);
+ }
+}
+
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
+// Math.pow(2, 30) - 1
+// 0b111111111111111111111111111111
+var maxSigned31BitInt = 1073741823;
+
+var NoWork = 0;
+var Never = 1;
+var Sync = maxSigned31BitInt;
+
+var UNIT_SIZE = 10;
+var MAGIC_NUMBER_OFFSET = maxSigned31BitInt - 1;
+
+// 1 unit of expiration time represents 10ms.
+function msToExpirationTime(ms) {
+ // Always add an offset so that we don't clash with the magic number for NoWork.
+ return MAGIC_NUMBER_OFFSET - (ms / UNIT_SIZE | 0);
+}
+
+function expirationTimeToMs(expirationTime) {
+ return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
+}
+
+function ceiling(num, precision) {
+ return ((num / precision | 0) + 1) * precision;
+}
+
+function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) {
+ return MAGIC_NUMBER_OFFSET - ceiling(MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE);
+}
+
+var LOW_PRIORITY_EXPIRATION = 5000;
+var LOW_PRIORITY_BATCH_SIZE = 250;
+
+function computeAsyncExpiration(currentTime) {
+ return computeExpirationBucket(currentTime, LOW_PRIORITY_EXPIRATION, LOW_PRIORITY_BATCH_SIZE);
+}
+
+// We intentionally set a higher expiration time for interactive updates in
+// dev than in production.
+//
+// If the main thread is being blocked so long that you hit the expiration,
+// it's a problem that could be solved with better scheduling.
+//
+// People will be more likely to notice this and fix it with the long
+// expiration time in development.
+//
+// In production we opt for better UX at the risk of masking scheduling
+// problems, by expiring fast.
+var HIGH_PRIORITY_EXPIRATION = 150;
+var HIGH_PRIORITY_BATCH_SIZE = 100;
+
+function computeInteractiveExpiration(currentTime) {
+ return computeExpirationBucket(currentTime, HIGH_PRIORITY_EXPIRATION, HIGH_PRIORITY_BATCH_SIZE);
+}
+
+var NoContext = 0;
+var ConcurrentMode = 1;
+var StrictMode = 2;
+var ProfileMode = 4;
+
+function FiberNode(tag, pendingProps, key, mode) {
+ // Instance
+ this.tag = tag;
+ this.key = key;
+ this.elementType = null;
+ this.type = null;
+ this.stateNode = null;
+
+ // Fiber
+ this.return = null;
+ this.child = null;
+ this.sibling = null;
+ this.index = 0;
+
+ this.ref = null;
+
+ this.pendingProps = pendingProps;
+ this.memoizedProps = null;
+ this.updateQueue = null;
+ this.memoizedState = null;
+ this.contextDependencies = null;
+
+ this.mode = mode;
+
+ // Effects
+ this.effectTag = NoEffect;
+ this.nextEffect = null;
+
+ this.firstEffect = null;
+ this.lastEffect = null;
+
+ this.expirationTime = NoWork;
+ this.childExpirationTime = NoWork;
+
+ this.alternate = null;
+
+ if (enableProfilerTimer) {
+ // Note: The following is done to avoid a v8 performance cliff.
+ //
+ // Initializing the fields below to smis and later updating them with
+ // double values will cause Fibers to end up having separate shapes.
+ // This behavior/bug has something to do with Object.preventExtension().
+ // Fortunately this only impacts DEV builds.
+ // Unfortunately it makes React unusably slow for some applications.
+ // To work around this, initialize the fields below with doubles.
+ //
+ // Learn more about this here:
+ // https://github.com/facebook/react/issues/14365
+ // https://bugs.chromium.org/p/v8/issues/detail?id=8538
+ this.actualDuration = Number.NaN;
+ this.actualStartTime = Number.NaN;
+ this.selfBaseDuration = Number.NaN;
+ this.treeBaseDuration = Number.NaN;
+
+ // It's okay to replace the initial doubles with smis after initialization.
+ // This won't trigger the performance cliff mentioned above,
+ // and it simplifies other profiler code (including DevTools).
+ this.actualDuration = 0;
+ this.actualStartTime = -1;
+ this.selfBaseDuration = 0;
+ this.treeBaseDuration = 0;
+ }
+
+
+}
+
+// This is a constructor function, rather than a POJO constructor, still
+// please ensure we do the following:
+// 1) Nobody should add any instance methods on this. Instance methods can be
+// more difficult to predict when they get optimized and they are almost
+// never inlined properly in static compilers.
+// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
+// always know when it is a fiber.
+// 3) We might want to experiment with using numeric keys since they are easier
+// to optimize in a non-JIT environment.
+// 4) We can easily go from a constructor to a createFiber object literal if that
+// is faster.
+// 5) It should be easy to port this to a C struct and keep a C implementation
+// compatible.
+var createFiber = function (tag, pendingProps, key, mode) {
+ // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
+ return new FiberNode(tag, pendingProps, key, mode);
+};
+
+function shouldConstruct(Component) {
+ var prototype = Component.prototype;
+ return !!(prototype && prototype.isReactComponent);
+}
+
+function isSimpleFunctionComponent(type) {
+ return typeof type === 'function' && !shouldConstruct(type) && type.defaultProps === undefined;
+}
+
+function resolveLazyComponentTag(Component) {
+ if (typeof Component === 'function') {
+ return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
+ } else if (Component !== undefined && Component !== null) {
+ var $$typeof = Component.$$typeof;
+ if ($$typeof === REACT_FORWARD_REF_TYPE) {
+ return ForwardRef;
+ }
+ if ($$typeof === REACT_MEMO_TYPE) {
+ return MemoComponent;
+ }
+ }
+ return IndeterminateComponent;
+}
+
+// This is used to create an alternate fiber to do work on.
+function createWorkInProgress(current, pendingProps, expirationTime) {
+ var workInProgress = current.alternate;
+ if (workInProgress === null) {
+ // We use a double buffering pooling technique because we know that we'll
+ // only ever need at most two versions of a tree. We pool the "other" unused
+ // node that we're free to reuse. This is lazily created to avoid allocating
+ // extra objects for things that are never updated. It also allow us to
+ // reclaim the extra memory if needed.
+ workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
+ workInProgress.elementType = current.elementType;
+ workInProgress.type = current.type;
+ workInProgress.stateNode = current.stateNode;
+
+ workInProgress.alternate = current;
+ current.alternate = workInProgress;
+ } else {
+ workInProgress.pendingProps = pendingProps;
+
+ // We already have an alternate.
+ // Reset the effect tag.
+ workInProgress.effectTag = NoEffect;
+
+ // The effect list is no longer valid.
+ workInProgress.nextEffect = null;
+ workInProgress.firstEffect = null;
+ workInProgress.lastEffect = null;
+
+ if (enableProfilerTimer) {
+ // We intentionally reset, rather than copy, actualDuration & actualStartTime.
+ // This prevents time from endlessly accumulating in new commits.
+ // This has the downside of resetting values for different priority renders,
+ // But works for yielding (the common case) and should support resuming.
+ workInProgress.actualDuration = 0;
+ workInProgress.actualStartTime = -1;
+ }
+ }
+
+ workInProgress.childExpirationTime = current.childExpirationTime;
+ workInProgress.expirationTime = current.expirationTime;
+
+ workInProgress.child = current.child;
+ workInProgress.memoizedProps = current.memoizedProps;
+ workInProgress.memoizedState = current.memoizedState;
+ workInProgress.updateQueue = current.updateQueue;
+ workInProgress.contextDependencies = current.contextDependencies;
+
+ // These will be overridden during the parent's reconciliation
+ workInProgress.sibling = current.sibling;
+ workInProgress.index = current.index;
+ workInProgress.ref = current.ref;
+
+ if (enableProfilerTimer) {
+ workInProgress.selfBaseDuration = current.selfBaseDuration;
+ workInProgress.treeBaseDuration = current.treeBaseDuration;
+ }
+
+ return workInProgress;
+}
+
+function createHostRootFiber(isConcurrent) {
+ var mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
+
+ if (enableProfilerTimer && isDevToolsPresent) {
+ // Always collect profile timings when DevTools are present.
+ // This enables DevTools to start capturing timing at any point–
+ // Without some nodes in the tree having empty base times.
+ mode |= ProfileMode;
+ }
+
+ return createFiber(HostRoot, null, null, mode);
+}
+
+function createFiberFromTypeAndProps(type, // React$ElementType
+key, pendingProps, owner, mode, expirationTime) {
+ var fiber = void 0;
+
+ var fiberTag = IndeterminateComponent;
+ // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
+ var resolvedType = type;
+ if (typeof type === 'function') {
+ if (shouldConstruct(type)) {
+ fiberTag = ClassComponent;
+ }
+ } else if (typeof type === 'string') {
+ fiberTag = HostComponent;
+ } else {
+ getTag: switch (type) {
+ case REACT_FRAGMENT_TYPE:
+ return createFiberFromFragment(pendingProps.children, mode, expirationTime, key);
+ case REACT_CONCURRENT_MODE_TYPE:
+ return createFiberFromMode(pendingProps, mode | ConcurrentMode | StrictMode, expirationTime, key);
+ case REACT_STRICT_MODE_TYPE:
+ return createFiberFromMode(pendingProps, mode | StrictMode, expirationTime, key);
+ case REACT_PROFILER_TYPE:
+ return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
+ case REACT_SUSPENSE_TYPE:
+ return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
+ default:
+ {
+ if (typeof type === 'object' && type !== null) {
+ switch (type.$$typeof) {
+ case REACT_PROVIDER_TYPE:
+ fiberTag = ContextProvider;
+ break getTag;
+ case REACT_CONTEXT_TYPE:
+ // This is a consumer
+ fiberTag = ContextConsumer;
+ break getTag;
+ case REACT_FORWARD_REF_TYPE:
+ fiberTag = ForwardRef;
+ break getTag;
+ case REACT_MEMO_TYPE:
+ fiberTag = MemoComponent;
+ break getTag;
+ case REACT_LAZY_TYPE:
+ fiberTag = LazyComponent;
+ resolvedType = null;
+ break getTag;
+ }
+ }
+ var info = '';
+ reactProdInvariant('130', type == null ? type : typeof type, info);
+ }
+ }
+ }
+
+ fiber = createFiber(fiberTag, pendingProps, key, mode);
+ fiber.elementType = type;
+ fiber.type = resolvedType;
+ fiber.expirationTime = expirationTime;
+
+ return fiber;
+}
+
+function createFiberFromElement(element, mode, expirationTime) {
+ var owner = null;
+ var type = element.type;
+ var key = element.key;
+ var pendingProps = element.props;
+ var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, expirationTime);
+ return fiber;
+}
+
+function createFiberFromFragment(elements, mode, expirationTime, key) {
+ var fiber = createFiber(Fragment, elements, key, mode);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromProfiler(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
+ // TODO: The Profiler fiber shouldn't have a type. It has a tag.
+ fiber.elementType = REACT_PROFILER_TYPE;
+ fiber.type = REACT_PROFILER_TYPE;
+ fiber.expirationTime = expirationTime;
+
+ return fiber;
+}
+
+function createFiberFromMode(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(Mode, pendingProps, key, mode);
+
+ // TODO: The Mode fiber shouldn't have a type. It has a tag.
+ var type = (mode & ConcurrentMode) === NoContext ? REACT_STRICT_MODE_TYPE : REACT_CONCURRENT_MODE_TYPE;
+ fiber.elementType = type;
+ fiber.type = type;
+
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromSuspense(pendingProps, mode, expirationTime, key) {
+ var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
+
+ // TODO: The SuspenseComponent fiber shouldn't have a type. It has a tag.
+ var type = REACT_SUSPENSE_TYPE;
+ fiber.elementType = type;
+ fiber.type = type;
+
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromText(content, mode, expirationTime) {
+ var fiber = createFiber(HostText, content, null, mode);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
+
+function createFiberFromHostInstanceForDeletion() {
+ var fiber = createFiber(HostComponent, null, null, NoContext);
+ // TODO: These should not need a type.
+ fiber.elementType = 'DELETED';
+ fiber.type = 'DELETED';
+ return fiber;
+}
+
+function createFiberFromPortal(portal, mode, expirationTime) {
+ var pendingProps = portal.children !== null ? portal.children : [];
+ var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
+ fiber.expirationTime = expirationTime;
+ fiber.stateNode = {
+ containerInfo: portal.containerInfo,
+ pendingChildren: null, // Used by persistent updates
+ implementation: portal.implementation
+ };
+ return fiber;
+}
+
+// Used for stashing WIP properties to replay failed work in DEV.
+
+var ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _ReactInternals$Sched = ReactInternals$1.SchedulerTracing;
+var __interactionsRef = _ReactInternals$Sched.__interactionsRef;
+var __subscriberRef = _ReactInternals$Sched.__subscriberRef;
+var unstable_clear = _ReactInternals$Sched.unstable_clear;
+var unstable_getCurrent = _ReactInternals$Sched.unstable_getCurrent;
+var unstable_getThreadID = _ReactInternals$Sched.unstable_getThreadID;
+var unstable_subscribe = _ReactInternals$Sched.unstable_subscribe;
+var unstable_trace = _ReactInternals$Sched.unstable_trace;
+var unstable_unsubscribe = _ReactInternals$Sched.unstable_unsubscribe;
+var unstable_wrap = _ReactInternals$Sched.unstable_wrap;
+
+// TODO: This should be lifted into the renderer.
+
+
+// The following attributes are only used by interaction tracing builds.
+// They enable interactions to be associated with their async work,
+// And expose interaction metadata to the React DevTools Profiler plugin.
+// Note that these attributes are only defined when the enableSchedulerTracing flag is enabled.
+
+
+// Exported FiberRoot type includes all properties,
+// To avoid requiring potentially error-prone :any casts throughout the project.
+// Profiling properties are only safe to access in profiling builds (when enableSchedulerTracing is true).
+// The types are defined separately within this file to ensure they stay in sync.
+// (We don't have to use an inline :any cast when enableSchedulerTracing is disabled.)
+
+
+function createFiberRoot(containerInfo, isConcurrent, hydrate) {
+ // Cyclic construction. This cheats the type system right now because
+ // stateNode is any.
+ var uninitializedFiber = createHostRootFiber(isConcurrent);
+
+ var root = void 0;
+ if (enableSchedulerTracing) {
+ root = {
+ current: uninitializedFiber,
+ containerInfo: containerInfo,
+ pendingChildren: null,
+
+ earliestPendingTime: NoWork,
+ latestPendingTime: NoWork,
+ earliestSuspendedTime: NoWork,
+ latestSuspendedTime: NoWork,
+ latestPingedTime: NoWork,
+
+ pingCache: null,
+
+ didError: false,
+
+ pendingCommitExpirationTime: NoWork,
+ finishedWork: null,
+ timeoutHandle: noTimeout,
+ context: null,
+ pendingContext: null,
+ hydrate: hydrate,
+ nextExpirationTimeToWorkOn: NoWork,
+ expirationTime: NoWork,
+ firstBatch: null,
+ nextScheduledRoot: null,
+
+ interactionThreadID: unstable_getThreadID(),
+ memoizedInteractions: new Set(),
+ pendingInteractionMap: new Map()
+ };
+ } else {
+ root = {
+ current: uninitializedFiber,
+ containerInfo: containerInfo,
+ pendingChildren: null,
+
+ pingCache: null,
+
+ earliestPendingTime: NoWork,
+ latestPendingTime: NoWork,
+ earliestSuspendedTime: NoWork,
+ latestSuspendedTime: NoWork,
+ latestPingedTime: NoWork,
+
+ didError: false,
+
+ pendingCommitExpirationTime: NoWork,
+ finishedWork: null,
+ timeoutHandle: noTimeout,
+ context: null,
+ pendingContext: null,
+ hydrate: hydrate,
+ nextExpirationTimeToWorkOn: NoWork,
+ expirationTime: NoWork,
+ firstBatch: null,
+ nextScheduledRoot: null
+ };
+ }
+
+ uninitializedFiber.stateNode = root;
+
+ // The reason for the way the Flow types are structured in this file,
+ // Is to avoid needing :any casts everywhere interaction tracing fields are used.
+ // Unfortunately that requires an :any cast for non-interaction tracing capable builds.
+ // $FlowFixMe Remove this :any cast and replace it with something better.
+ return root;
+}
+
+var ReactInternals$2 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
+
+var _ReactInternals$Sched$1 = ReactInternals$2.Scheduler;
+var unstable_cancelCallback = _ReactInternals$Sched$1.unstable_cancelCallback;
+var unstable_now = _ReactInternals$Sched$1.unstable_now;
+var unstable_scheduleCallback = _ReactInternals$Sched$1.unstable_scheduleCallback;
+var unstable_shouldYield = _ReactInternals$Sched$1.unstable_shouldYield;
+var unstable_getFirstCallbackNode = _ReactInternals$Sched$1.unstable_getFirstCallbackNode;
+var unstable_runWithPriority = _ReactInternals$Sched$1.unstable_runWithPriority;
+var unstable_next = _ReactInternals$Sched$1.unstable_next;
+var unstable_continueExecution = _ReactInternals$Sched$1.unstable_continueExecution;
+var unstable_pauseExecution = _ReactInternals$Sched$1.unstable_pauseExecution;
+var unstable_getCurrentPriorityLevel = _ReactInternals$Sched$1.unstable_getCurrentPriorityLevel;
+var unstable_ImmediatePriority = _ReactInternals$Sched$1.unstable_ImmediatePriority;
+var unstable_UserBlockingPriority = _ReactInternals$Sched$1.unstable_UserBlockingPriority;
+var unstable_NormalPriority = _ReactInternals$Sched$1.unstable_NormalPriority;
+var unstable_LowPriority = _ReactInternals$Sched$1.unstable_LowPriority;
+var unstable_IdlePriority = _ReactInternals$Sched$1.unstable_IdlePriority;
+
+/**
+ * Call a function while guarding against errors that happens within it.
+ * Returns an error if it throws, otherwise null.
+ *
+ * In production, this is implemented using a try-catch. The reason we don't
+ * use a try-catch directly is so that we can swap out a different
+ * implementation in DEV mode.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+
+
+/**
+ * Same as invokeGuardedCallback, but instead of returning an error, it stores
+ * it in a global so it can be rethrown by `rethrowCaughtError` later.
+ * TODO: See if caughtError and rethrowError can be unified.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+
+
+/**
+ * During execution of guarded functions we will capture the first error which
+ * we will rethrow to be handled by the top level error handler.
+ */
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+// This lets us hook into Fiber to debug what it's doing.
+// See https://github.com/facebook/react/pull/8033.
+// This is not part of the public API, not even for React DevTools.
+// You may only inject a debugTool if you work on React Fiber itself.
+
+// TODO: Offscreen updates should never suspend. However, a promise that
+// suspended inside an offscreen subtree should be able to ping at the priority
+// of the outer render.
+
+function markPendingPriorityLevel(root, expirationTime) {
+ // If there's a gap between completing a failed root and retrying it,
+ // additional updates may be scheduled. Clear `didError`, in case the update
+ // is sufficient to fix the error.
+ root.didError = false;
+
+ // Update the latest and earliest pending times
+ var earliestPendingTime = root.earliestPendingTime;
+ if (earliestPendingTime === NoWork) {
+ // No other pending updates.
+ root.earliestPendingTime = root.latestPendingTime = expirationTime;
+ } else {
+ if (earliestPendingTime < expirationTime) {
+ // This is the earliest pending update.
+ root.earliestPendingTime = expirationTime;
+ } else {
+ var latestPendingTime = root.latestPendingTime;
+ if (latestPendingTime > expirationTime) {
+ // This is the latest pending update
+ root.latestPendingTime = expirationTime;
+ }
+ }
+ }
+ findNextExpirationTimeToWorkOn(expirationTime, root);
+}
+
+function markCommittedPriorityLevels(root, earliestRemainingTime) {
+ root.didError = false;
+
+ if (earliestRemainingTime === NoWork) {
+ // Fast path. There's no remaining work. Clear everything.
+ root.earliestPendingTime = NoWork;
+ root.latestPendingTime = NoWork;
+ root.earliestSuspendedTime = NoWork;
+ root.latestSuspendedTime = NoWork;
+ root.latestPingedTime = NoWork;
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ if (earliestRemainingTime < root.latestPingedTime) {
+ root.latestPingedTime = NoWork;
+ }
+
+ // Let's see if the previous latest known pending level was just flushed.
+ var latestPendingTime = root.latestPendingTime;
+ if (latestPendingTime !== NoWork) {
+ if (latestPendingTime > earliestRemainingTime) {
+ // We've flushed all the known pending levels.
+ root.earliestPendingTime = root.latestPendingTime = NoWork;
+ } else {
+ var earliestPendingTime = root.earliestPendingTime;
+ if (earliestPendingTime > earliestRemainingTime) {
+ // We've flushed the earliest known pending level. Set this to the
+ // latest pending time.
+ root.earliestPendingTime = root.latestPendingTime;
+ }
+ }
+ }
+
+ // Now let's handle the earliest remaining level in the whole tree. We need to
+ // decide whether to treat it as a pending level or as suspended. Check
+ // it falls within the range of known suspended levels.
+
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ if (earliestSuspendedTime === NoWork) {
+ // There's no suspended work. Treat the earliest remaining level as a
+ // pending level.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ var latestSuspendedTime = root.latestSuspendedTime;
+ if (earliestRemainingTime < latestSuspendedTime) {
+ // The earliest remaining level is later than all the suspended work. That
+ // means we've flushed all the suspended work.
+ root.earliestSuspendedTime = NoWork;
+ root.latestSuspendedTime = NoWork;
+ root.latestPingedTime = NoWork;
+
+ // There's no suspended work. Treat the earliest remaining level as a
+ // pending level.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ if (earliestRemainingTime > earliestSuspendedTime) {
+ // The earliest remaining time is earlier than all the suspended work.
+ // Treat it as a pending update.
+ markPendingPriorityLevel(root, earliestRemainingTime);
+ findNextExpirationTimeToWorkOn(NoWork, root);
+ return;
+ }
+
+ // The earliest remaining time falls within the range of known suspended
+ // levels. We should treat this as suspended work.
+ findNextExpirationTimeToWorkOn(NoWork, root);
+}
+
+function hasLowerPriorityWork(root, erroredExpirationTime) {
+ var latestPendingTime = root.latestPendingTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ var latestPingedTime = root.latestPingedTime;
+ return latestPendingTime !== NoWork && latestPendingTime < erroredExpirationTime || latestSuspendedTime !== NoWork && latestSuspendedTime < erroredExpirationTime || latestPingedTime !== NoWork && latestPingedTime < erroredExpirationTime;
+}
+
+function isPriorityLevelSuspended(root, expirationTime) {
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ return earliestSuspendedTime !== NoWork && expirationTime <= earliestSuspendedTime && expirationTime >= latestSuspendedTime;
+}
+
+function markSuspendedPriorityLevel(root, suspendedTime) {
+ root.didError = false;
+ clearPing(root, suspendedTime);
+
+ // First, check the known pending levels and update them if needed.
+ var earliestPendingTime = root.earliestPendingTime;
+ var latestPendingTime = root.latestPendingTime;
+ if (earliestPendingTime === suspendedTime) {
+ if (latestPendingTime === suspendedTime) {
+ // Both known pending levels were suspended. Clear them.
+ root.earliestPendingTime = root.latestPendingTime = NoWork;
+ } else {
+ // The earliest pending level was suspended. Clear by setting it to the
+ // latest pending level.
+ root.earliestPendingTime = latestPendingTime;
+ }
+ } else if (latestPendingTime === suspendedTime) {
+ // The latest pending level was suspended. Clear by setting it to the
+ // latest pending level.
+ root.latestPendingTime = earliestPendingTime;
+ }
+
+ // Finally, update the known suspended levels.
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ if (earliestSuspendedTime === NoWork) {
+ // No other suspended levels.
+ root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
+ } else {
+ if (earliestSuspendedTime < suspendedTime) {
+ // This is the earliest suspended level.
+ root.earliestSuspendedTime = suspendedTime;
+ } else if (latestSuspendedTime > suspendedTime) {
+ // This is the latest suspended level
+ root.latestSuspendedTime = suspendedTime;
+ }
+ }
+
+ findNextExpirationTimeToWorkOn(suspendedTime, root);
+}
+
+function markPingedPriorityLevel(root, pingedTime) {
+ root.didError = false;
+
+ // TODO: When we add back resuming, we need to ensure the progressed work
+ // is thrown out and not reused during the restarted render. One way to
+ // invalidate the progressed work is to restart at expirationTime + 1.
+ var latestPingedTime = root.latestPingedTime;
+ if (latestPingedTime === NoWork || latestPingedTime > pingedTime) {
+ root.latestPingedTime = pingedTime;
+ }
+ findNextExpirationTimeToWorkOn(pingedTime, root);
+}
+
+function clearPing(root, completedTime) {
+ var latestPingedTime = root.latestPingedTime;
+ if (latestPingedTime >= completedTime) {
+ root.latestPingedTime = NoWork;
+ }
+}
+
+function findEarliestOutstandingPriorityLevel(root, renderExpirationTime) {
+ var earliestExpirationTime = renderExpirationTime;
+
+ var earliestPendingTime = root.earliestPendingTime;
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ if (earliestPendingTime > earliestExpirationTime) {
+ earliestExpirationTime = earliestPendingTime;
+ }
+ if (earliestSuspendedTime > earliestExpirationTime) {
+ earliestExpirationTime = earliestSuspendedTime;
+ }
+ return earliestExpirationTime;
+}
+
+function didExpireAtExpirationTime(root, currentTime) {
+ var expirationTime = root.expirationTime;
+ if (expirationTime !== NoWork && currentTime <= expirationTime) {
+ // The root has expired. Flush all work up to the current time.
+ root.nextExpirationTimeToWorkOn = currentTime;
+ }
+}
+
+function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
+ var earliestSuspendedTime = root.earliestSuspendedTime;
+ var latestSuspendedTime = root.latestSuspendedTime;
+ var earliestPendingTime = root.earliestPendingTime;
+ var latestPingedTime = root.latestPingedTime;
+
+ // Work on the earliest pending time. Failing that, work on the latest
+ // pinged time.
+ var nextExpirationTimeToWorkOn = earliestPendingTime !== NoWork ? earliestPendingTime : latestPingedTime;
+
+ // If there is no pending or pinged work, check if there's suspended work
+ // that's lower priority than what we just completed.
+ if (nextExpirationTimeToWorkOn === NoWork && (completedExpirationTime === NoWork || latestSuspendedTime < completedExpirationTime)) {
+ // The lowest priority suspended work is the work most likely to be
+ // committed next. Let's start rendering it again, so that if it times out,
+ // it's ready to commit.
+ nextExpirationTimeToWorkOn = latestSuspendedTime;
+ }
+
+ var expirationTime = nextExpirationTimeToWorkOn;
+ if (expirationTime !== NoWork && earliestSuspendedTime > expirationTime) {
+ // Expire using the earliest known expiration time.
+ expirationTime = earliestSuspendedTime;
+ }
+
+ root.nextExpirationTimeToWorkOn = nextExpirationTimeToWorkOn;
+ root.expirationTime = expirationTime;
+}
+
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
+ ;
+}
+
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+
+/**
+ * Performs equality by iterating through keys on an object and returning false
+ * when any key has values which are not strictly equal between the arguments.
+ * Returns true when the values of all keys are strictly equal.
+ */
+function shallowEqual(objA, objB) {
+ if (is(objA, objB)) {
+ return true;
+ }
+
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
+ return false;
+ }
+
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
+
+ if (keysA.length !== keysB.length) {
+ return false;
+ }
+
+ // Test for A's keys different from B.
+ for (var i = 0; i < keysA.length; i++) {
+ if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function resolveDefaultProps(Component, baseProps) {
+ if (Component && Component.defaultProps) {
+ // Resolve default props. Taken from ReactElement
+ var props = _assign({}, baseProps);
+ var defaultProps = Component.defaultProps;
+ for (var propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
+ }
+ return props;
+ }
+ return baseProps;
+}
+
+function readLazyComponentType(lazyComponent) {
+ var status = lazyComponent._status;
+ var result = lazyComponent._result;
+ switch (status) {
+ case Resolved:
+ {
+ var Component = result;
+ return Component;
+ }
+ case Rejected:
+ {
+ var error = result;
+ throw error;
+ }
+ case Pending:
+ {
+ var thenable = result;
+ throw thenable;
+ }
+ default:
+ {
+ lazyComponent._status = Pending;
+ var ctor = lazyComponent._ctor;
+ var _thenable = ctor();
+ _thenable.then(function (moduleObject) {
+ if (lazyComponent._status === Pending) {
+ var defaultExport = moduleObject.default;
+ lazyComponent._status = Resolved;
+ lazyComponent._result = defaultExport;
+ }
+ }, function (error) {
+ if (lazyComponent._status === Pending) {
+ lazyComponent._status = Rejected;
+ lazyComponent._result = error;
+ }
+ });
+ // Handle synchronous thenables.
+ switch (lazyComponent._status) {
+ case Resolved:
+ return lazyComponent._result;
+ case Rejected:
+ throw lazyComponent._result;
+ }
+ lazyComponent._result = _thenable;
+ throw _thenable;
+ }
+ }
+}
+
+// React.Component uses a shared frozen object by default.
+// We'll use it to determine whether we need to initialize legacy refs.
+var emptyRefsObject = new React.Component().refs;
+
+function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) {
+ var prevState = workInProgress.memoizedState;
+
+ var partialState = getDerivedStateFromProps(nextProps, prevState);
+
+ var memoizedState = partialState === null || partialState === undefined ? prevState : _assign({}, prevState, partialState);
+ workInProgress.memoizedState = memoizedState;
+
+ // Once the update queue is empty, persist the derived state onto the
+ // base state.
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null && workInProgress.expirationTime === NoWork) {
+ updateQueue.baseState = memoizedState;
+ }
+}
+
+var classComponentUpdater = {
+ isMounted: isMounted,
+ enqueueSetState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.payload = payload;
+ if (callback !== undefined && callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects$1();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueReplaceState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.tag = ReplaceState;
+ update.payload = payload;
+
+ if (callback !== undefined && callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects$1();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueForceUpdate: function (inst, callback) {
+ var fiber = get(inst);
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var update = createUpdate(expirationTime);
+ update.tag = ForceUpdate;
+
+ if (callback !== undefined && callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects$1();
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ }
+};
+
+function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
+ var instance = workInProgress.stateNode;
+ if (typeof instance.shouldComponentUpdate === 'function') {
+ startPhaseTimer(workInProgress, 'shouldComponentUpdate');
+ var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
+ stopPhaseTimer();
+
+ return shouldUpdate;
+ }
+
+ if (ctor.prototype && ctor.prototype.isPureReactComponent) {
+ return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState);
+ }
+
+ return true;
+}
+
+function adoptClassInstance(workInProgress, instance) {
+ instance.updater = classComponentUpdater;
+ workInProgress.stateNode = instance;
+ // The instance needs access to the fiber so that it can schedule updates
+ set(instance, workInProgress);
+
+}
+
+function constructClassInstance(workInProgress, ctor, props, renderExpirationTime) {
+ var isLegacyContextConsumer = false;
+ var unmaskedContext = emptyContextObject;
+ var context = null;
+ var contextType = ctor.contextType;
+
+ if (typeof contextType === 'object' && contextType !== null) {
+ context = readContext(contextType);
+ } else {
+ unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ var contextTypes = ctor.contextTypes;
+ isLegacyContextConsumer = contextTypes !== null && contextTypes !== undefined;
+ context = isLegacyContextConsumer ? getMaskedContext(workInProgress, unmaskedContext) : emptyContextObject;
+ }
+
+ // Instantiate twice to help detect side-effects.
+ var instance = new ctor(props, context);
+ var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
+ adoptClassInstance(workInProgress, instance);
+
+ if (isLegacyContextConsumer) {
+ cacheContext(workInProgress, unmaskedContext, context);
+ }
+
+ return instance;
+}
+
+function callComponentWillMount(workInProgress, instance) {
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ var oldState = instance.state;
+
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
+ }
+
+ stopPhaseTimer();
+
+ if (oldState !== instance.state) {
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
+
+function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) {
+ var oldState = instance.state;
+ startPhaseTimer(workInProgress, 'componentWillReceiveProps');
+ if (typeof instance.componentWillReceiveProps === 'function') {
+ instance.componentWillReceiveProps(newProps, nextContext);
+ }
+ if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
+ instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
+ }
+ stopPhaseTimer();
+
+ if (instance.state !== oldState) {
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
+
+// Invokes the mount life-cycles on a previously never rendered instance.
+function mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+ instance.props = newProps;
+ instance.state = workInProgress.memoizedState;
+ instance.refs = emptyRefsObject;
+
+ var contextType = ctor.contextType;
+ if (typeof contextType === 'object' && contextType !== null) {
+ instance.context = readContext(contextType);
+ } else {
+ var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ instance.context = getMaskedContext(workInProgress, unmaskedContext);
+ }
+
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ instance.state = workInProgress.memoizedState;
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ instance.state = workInProgress.memoizedState;
+ }
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
+ callComponentWillMount(workInProgress, instance);
+ // If we had additional state updates during this life-cycle, let's
+ // process them now.
+ updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ instance.state = workInProgress.memoizedState;
+ }
+ }
+
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+}
+
+function resumeMountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+
+ var oldProps = workInProgress.memoizedProps;
+ instance.props = oldProps;
+
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = void 0;
+ if (typeof contextType === 'object' && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
+ if (oldProps !== newProps || oldContext !== nextContext) {
+ callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
+ }
+ }
+
+ resetHasForceUpdateBeforeProcessing();
+
+ var oldState = workInProgress.memoizedState;
+ var newState = instance.state = oldState;
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ newState = workInProgress.memoizedState;
+ }
+ if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ return false;
+ }
+
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ newState = workInProgress.memoizedState;
+ }
+
+ var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
+ startPhaseTimer(workInProgress, 'componentWillMount');
+ if (typeof instance.componentWillMount === 'function') {
+ instance.componentWillMount();
+ }
+ if (typeof instance.UNSAFE_componentWillMount === 'function') {
+ instance.UNSAFE_componentWillMount();
+ }
+ stopPhaseTimer();
+ }
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ }
+
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+
+ return shouldUpdate;
+}
+
+// Invokes the update life-cycles and returns false if it shouldn't rerender.
+function updateClassInstance(current, workInProgress, ctor, newProps, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+
+ var oldProps = workInProgress.memoizedProps;
+ instance.props = workInProgress.type === workInProgress.elementType ? oldProps : resolveDefaultProps(workInProgress.type, oldProps);
+
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = void 0;
+ if (typeof contextType === 'object' && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function';
+
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
+ if (oldProps !== newProps || oldContext !== nextContext) {
+ callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
+ }
+ }
+
+ resetHasForceUpdateBeforeProcessing();
+
+ var oldState = workInProgress.memoizedState;
+ var newState = instance.state = oldState;
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
+ newState = workInProgress.memoizedState;
+ }
+
+ if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Update;
+ }
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Snapshot;
+ }
+ }
+ return false;
+ }
+
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
+ newState = workInProgress.memoizedState;
+ }
+
+ var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function')) {
+ startPhaseTimer(workInProgress, 'componentWillUpdate');
+ if (typeof instance.componentWillUpdate === 'function') {
+ instance.componentWillUpdate(newProps, newState, nextContext);
+ }
+ if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
+ instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
+ }
+ stopPhaseTimer();
+ }
+ if (typeof instance.componentDidUpdate === 'function') {
+ workInProgress.effectTag |= Update;
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ workInProgress.effectTag |= Snapshot;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Update;
+ }
+ }
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
+ if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
+ workInProgress.effectTag |= Snapshot;
+ }
+ }
+
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized props/state to indicate that this work can be reused.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ }
+
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+
+ return shouldUpdate;
+}
+
+var isArray = Array.isArray;
+
+function coerceRef(returnFiber, current$$1, element) {
+ var mixedRef = element.ref;
+ if (mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object') {
+ if (element._owner) {
+ var owner = element._owner;
+ var inst = void 0;
+ if (owner) {
+ var ownerFiber = owner;
+ !(ownerFiber.tag === ClassComponent) ? reactProdInvariant('309') : void 0;
+ inst = ownerFiber.stateNode;
+ }
+ !inst ? reactProdInvariant('147', mixedRef) : void 0;
+ var stringRef = '' + mixedRef;
+ // Check if previous string ref matches new string ref
+ if (current$$1 !== null && current$$1.ref !== null && typeof current$$1.ref === 'function' && current$$1.ref._stringRef === stringRef) {
+ return current$$1.ref;
+ }
+ var ref = function (value) {
+ var refs = inst.refs;
+ if (refs === emptyRefsObject) {
+ // This is a lazy pooled frozen object, so we need to initialize.
+ refs = inst.refs = {};
+ }
+ if (value === null) {
+ delete refs[stringRef];
+ } else {
+ refs[stringRef] = value;
+ }
+ };
+ ref._stringRef = stringRef;
+ return ref;
+ } else {
+ !(typeof mixedRef === 'string') ? reactProdInvariant('284') : void 0;
+ !element._owner ? reactProdInvariant('290', mixedRef) : void 0;
+ }
+ }
+ return mixedRef;
+}
+
+function throwOnInvalidObjectType(returnFiber, newChild) {
+ if (returnFiber.type !== 'textarea') {
+ var addendum = '';
+ reactProdInvariant('31', Object.prototype.toString.call(newChild) === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : newChild, addendum);
+ }
+}
+
+// This wrapper function exists because I expect to clone the code in each path
+// to be able to optimize each path individually by branching early. This needs
+// a compiler or we can do it manually. Helpers that don't need this branching
+// live outside of this function.
+function ChildReconciler(shouldTrackSideEffects) {
+ function deleteChild(returnFiber, childToDelete) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return;
+ }
+ // Deletions are added in reversed order so we add it to the front.
+ // At this point, the return fiber's effect list is empty except for
+ // deletions, so we can just append the deletion to the list. The remaining
+ // effects aren't added until the complete phase. Once we implement
+ // resuming, this may not be true.
+ var last = returnFiber.lastEffect;
+ if (last !== null) {
+ last.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+ childToDelete.nextEffect = null;
+ childToDelete.effectTag = Deletion;
+ }
+
+ function deleteRemainingChildren(returnFiber, currentFirstChild) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return null;
+ }
+
+ // TODO: For the shouldClone case, this could be micro-optimized a bit by
+ // assuming that after the first child we've already added everything.
+ var childToDelete = currentFirstChild;
+ while (childToDelete !== null) {
+ deleteChild(returnFiber, childToDelete);
+ childToDelete = childToDelete.sibling;
+ }
+ return null;
+ }
+
+ function mapRemainingChildren(returnFiber, currentFirstChild) {
+ // Add the remaining children to a temporary map so that we can find them by
+ // keys quickly. Implicit (null) keys get added to this set with their index
+ var existingChildren = new Map();
+
+ var existingChild = currentFirstChild;
+ while (existingChild !== null) {
+ if (existingChild.key !== null) {
+ existingChildren.set(existingChild.key, existingChild);
+ } else {
+ existingChildren.set(existingChild.index, existingChild);
+ }
+ existingChild = existingChild.sibling;
+ }
+ return existingChildren;
+ }
+
+ function useFiber(fiber, pendingProps, expirationTime) {
+ // We currently set sibling to null and index to 0 here because it is easy
+ // to forget to do before returning it. E.g. for the single child case.
+ var clone = createWorkInProgress(fiber, pendingProps, expirationTime);
+ clone.index = 0;
+ clone.sibling = null;
+ return clone;
+ }
+
+ function placeChild(newFiber, lastPlacedIndex, newIndex) {
+ newFiber.index = newIndex;
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return lastPlacedIndex;
+ }
+ var current$$1 = newFiber.alternate;
+ if (current$$1 !== null) {
+ var oldIndex = current$$1.index;
+ if (oldIndex < lastPlacedIndex) {
+ // This is a move.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
+ } else {
+ // This item can stay in place.
+ return oldIndex;
+ }
+ } else {
+ // This is an insertion.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
+ }
+ }
+
+ function placeSingleChild(newFiber) {
+ // This is simpler for the single child case. We only need to do a
+ // placement for inserting new children.
+ if (shouldTrackSideEffects && newFiber.alternate === null) {
+ newFiber.effectTag = Placement;
+ }
+ return newFiber;
+ }
+
+ function updateTextNode(returnFiber, current$$1, textContent, expirationTime) {
+ if (current$$1 === null || current$$1.tag !== HostText) {
+ // Insert
+ var created = createFiberFromText(textContent, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, textContent, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function updateElement(returnFiber, current$$1, element, expirationTime) {
+ if (current$$1 !== null && current$$1.elementType === element.type) {
+ // Move based on index
+ var existing = useFiber(current$$1, element.props, expirationTime);
+ existing.ref = coerceRef(returnFiber, current$$1, element);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ // Insert
+ var created = createFiberFromElement(element, returnFiber.mode, expirationTime);
+ created.ref = coerceRef(returnFiber, current$$1, element);
+ created.return = returnFiber;
+ return created;
+ }
+ }
+
+ function updatePortal(returnFiber, current$$1, portal, expirationTime) {
+ if (current$$1 === null || current$$1.tag !== HostPortal || current$$1.stateNode.containerInfo !== portal.containerInfo || current$$1.stateNode.implementation !== portal.implementation) {
+ // Insert
+ var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, portal.children || [], expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function updateFragment(returnFiber, current$$1, fragment, expirationTime, key) {
+ if (current$$1 === null || current$$1.tag !== Fragment) {
+ // Insert
+ var created = createFiberFromFragment(fragment, returnFiber.mode, expirationTime, key);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current$$1, fragment, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
+
+ function createChild(returnFiber, newChild, expirationTime) {
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ var created = createFiberFromText('' + newChild, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ var _created = createFiberFromElement(newChild, returnFiber.mode, expirationTime);
+ _created.ref = coerceRef(returnFiber, null, newChild);
+ _created.return = returnFiber;
+ return _created;
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ var _created2 = createFiberFromPortal(newChild, returnFiber.mode, expirationTime);
+ _created2.return = returnFiber;
+ return _created2;
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _created3 = createFiberFromFragment(newChild, returnFiber.mode, expirationTime, null);
+ _created3.return = returnFiber;
+ return _created3;
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ return null;
+ }
+
+ function updateSlot(returnFiber, oldFiber, newChild, expirationTime) {
+ // Update the fiber if the keys match, otherwise return null.
+
+ var key = oldFiber !== null ? oldFiber.key : null;
+
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ if (key !== null) {
+ return null;
+ }
+ return updateTextNode(returnFiber, oldFiber, '' + newChild, expirationTime);
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ if (newChild.key === key) {
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(returnFiber, oldFiber, newChild.props.children, expirationTime, key);
+ }
+ return updateElement(returnFiber, oldFiber, newChild, expirationTime);
+ } else {
+ return null;
+ }
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ if (newChild.key === key) {
+ return updatePortal(returnFiber, oldFiber, newChild, expirationTime);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ if (key !== null) {
+ return null;
+ }
+
+ return updateFragment(returnFiber, oldFiber, newChild, expirationTime, null);
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ return null;
+ }
+
+ function updateFromMap(existingChildren, returnFiber, newIdx, newChild, expirationTime) {
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ // Text nodes don't have keys, so we neither have to check the old nor
+ // new node for the key. If both are text nodes, they match.
+ var matchedFiber = existingChildren.get(newIdx) || null;
+ return updateTextNode(returnFiber, matchedFiber, '' + newChild, expirationTime);
+ }
+
+ if (typeof newChild === 'object' && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ {
+ var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(returnFiber, _matchedFiber, newChild.props.children, expirationTime, newChild.key);
+ }
+ return updateElement(returnFiber, _matchedFiber, newChild, expirationTime);
+ }
+ case REACT_PORTAL_TYPE:
+ {
+ var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
+ return updatePortal(returnFiber, _matchedFiber2, newChild, expirationTime);
+ }
+ }
+
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _matchedFiber3 = existingChildren.get(newIdx) || null;
+ return updateFragment(returnFiber, _matchedFiber3, newChild, expirationTime, null);
+ }
+
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ return null;
+ }
+
+ /**
+ * Warns if there is a duplicate or missing key
+ */
+ function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) {
+ // This algorithm can't optimize by searching from both ends since we
+ // don't have backpointers on fibers. I'm trying to see how far we can get
+ // with that model. If it ends up not being worth the tradeoffs, we can
+ // add it later.
+
+ // Even with a two ended optimization, we'd want to optimize for the case
+ // where there are few changes and brute force the comparison instead of
+ // going for the Map. It'd like to explore hitting that path first in
+ // forward-only mode and only go for the Map once we notice that we need
+ // lots of look ahead. This doesn't handle reversal as well as two ended
+ // search but that's unusual. Besides, for the two ended optimization to
+ // work on Iterables, we'd need to copy the whole set.
+
+ // In this first iteration, we'll just live with hitting the bad case
+ // (adding everything to a Map) in for every insert/move.
+
+ // If you change this code, also update reconcileChildrenIterator() which
+ // uses the same algorithm.
+
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+ for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], expirationTime);
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (oldFiber === null) {
+ oldFiber = nextOldFiber;
+ }
+ break;
+ }
+ if (shouldTrackSideEffects) {
+ if (oldFiber && newFiber.alternate === null) {
+ // We matched the slot, but we didn't reuse the existing fiber, so we
+ // need to delete the existing child.
+ deleteChild(returnFiber, oldFiber);
+ }
+ }
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = newFiber;
+ } else {
+ // TODO: Defer siblings if we're not at the right index for this slot.
+ // I.e. if we had null values before, then we want to defer this
+ // for each null value. However, we also don't want to call updateSlot
+ // with the previous one.
+ previousNewFiber.sibling = newFiber;
+ }
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
+
+ if (newIdx === newChildren.length) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
+
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber = createChild(returnFiber, newChildren[newIdx], expirationTime);
+ if (!_newFiber) {
+ continue;
+ }
+ lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber;
+ } else {
+ previousNewFiber.sibling = _newFiber;
+ }
+ previousNewFiber = _newFiber;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], expirationTime);
+ if (_newFiber2) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber2.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key);
+ }
+ }
+ lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber2;
+ } else {
+ previousNewFiber.sibling = _newFiber2;
+ }
+ previousNewFiber = _newFiber2;
+ }
+ }
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function (child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, expirationTime) {
+ // This is the same implementation as reconcileChildrenArray(),
+ // but using the iterator instead.
+
+ var iteratorFn = getIteratorFn(newChildrenIterable);
+ !(typeof iteratorFn === 'function') ? reactProdInvariant('150') : void 0;
+
+ var newChildren = iteratorFn.call(newChildrenIterable);
+ !(newChildren != null) ? reactProdInvariant('151') : void 0;
+
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+
+ var step = newChildren.next();
+ for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(returnFiber, oldFiber, step.value, expirationTime);
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (!oldFiber) {
+ oldFiber = nextOldFiber;
+ }
+ break;
+ }
+ if (shouldTrackSideEffects) {
+ if (oldFiber && newFiber.alternate === null) {
+ // We matched the slot, but we didn't reuse the existing fiber, so we
+ // need to delete the existing child.
+ deleteChild(returnFiber, oldFiber);
+ }
+ }
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = newFiber;
+ } else {
+ // TODO: Defer siblings if we're not at the right index for this slot.
+ // I.e. if we had null values before, then we want to defer this
+ // for each null value. However, we also don't want to call updateSlot
+ // with the previous one.
+ previousNewFiber.sibling = newFiber;
+ }
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
+
+ if (step.done) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
+
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber3 = createChild(returnFiber, step.value, expirationTime);
+ if (_newFiber3 === null) {
+ continue;
+ }
+ lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber3;
+ } else {
+ previousNewFiber.sibling = _newFiber3;
+ }
+ previousNewFiber = _newFiber3;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, expirationTime);
+ if (_newFiber4 !== null) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber4.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(_newFiber4.key === null ? newIdx : _newFiber4.key);
+ }
+ }
+ lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber4;
+ } else {
+ previousNewFiber.sibling = _newFiber4;
+ }
+ previousNewFiber = _newFiber4;
+ }
+ }
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function (child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, expirationTime) {
+ // There's no need to check for keys on text nodes since we don't have a
+ // way to define them.
+ if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
+ // We already have an existing node so let's just update it and delete
+ // the rest.
+ deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
+ var existing = useFiber(currentFirstChild, textContent, expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ }
+ // The existing first child is not a text node so we need to create one
+ // and delete the existing ones.
+ deleteRemainingChildren(returnFiber, currentFirstChild);
+ var created = createFiberFromText(textContent, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
+ var key = element.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime);
+ existing.ref = coerceRef(returnFiber, child, element);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ if (element.type === REACT_FRAGMENT_TYPE) {
+ var created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime, element.key);
+ created.return = returnFiber;
+ return created;
+ } else {
+ var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime);
+ _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
+ _created4.return = returnFiber;
+ return _created4;
+ }
+ }
+
+ function reconcileSinglePortal(returnFiber, currentFirstChild, portal, expirationTime) {
+ var key = portal.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, portal.children || [], expirationTime);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
+ created.return = returnFiber;
+ return created;
+ }
+
+ // This API will tag the children with the side-effect of the reconciliation
+ // itself. They will be added to the side-effect list as we pass through the
+ // children and the parent.
+ function reconcileChildFibers(returnFiber, currentFirstChild, newChild, expirationTime) {
+ // This function is not recursive.
+ // If the top level item is an array, we treat it as a set of children,
+ // not as a fragment. Nested arrays on the other hand will be treated as
+ // fragment nodes. Recursion happens at the normal flow.
+
+ // Handle top level unkeyed fragments as if they were arrays.
+ // This leads to an ambiguity between <>{[...]}</> and <>...</>.
+ // We treat the ambiguous cases above the same.
+ var isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null;
+ if (isUnkeyedTopLevelFragment) {
+ newChild = newChild.props.children;
+ }
+
+ // Handle object types
+ var isObject = typeof newChild === 'object' && newChild !== null;
+
+ if (isObject) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime));
+ case REACT_PORTAL_TYPE:
+ return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime));
+ }
+ }
+
+ if (typeof newChild === 'string' || typeof newChild === 'number') {
+ return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime));
+ }
+
+ if (isArray(newChild)) {
+ return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime);
+ }
+
+ if (getIteratorFn(newChild)) {
+ return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime);
+ }
+
+ if (isObject) {
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
+ // If the new child is undefined, and the return fiber is a composite
+ // component, throw an error. If Fiber return types are disabled,
+ // we already threw above.
+ switch (returnFiber.tag) {
+ case ClassComponent:
+ {
+
+ }
+ // Intentionally fall through to the next case, which handles both
+ // functions and classes
+ // eslint-disable-next-lined no-fallthrough
+ case FunctionComponent:
+ {
+ var Component = returnFiber.type;
+ reactProdInvariant('152', Component.displayName || Component.name || 'Component');
+ }
+ }
+ }
+
+ // Remaining cases are all treated as empty.
+ return deleteRemainingChildren(returnFiber, currentFirstChild);
+ }
+
+ return reconcileChildFibers;
+}
+
+var reconcileChildFibers = ChildReconciler(true);
+var mountChildFibers = ChildReconciler(false);
+
+function cloneChildFibers(current$$1, workInProgress) {
+ !(current$$1 === null || workInProgress.child === current$$1.child) ? reactProdInvariant('153') : void 0;
+
+ if (workInProgress.child === null) {
+ return;
+ }
+
+ var currentChild = workInProgress.child;
+ var newChild = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
+ workInProgress.child = newChild;
+
+ newChild.return = workInProgress;
+ while (currentChild.sibling !== null) {
+ currentChild = currentChild.sibling;
+ newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
+ newChild.return = workInProgress;
+ }
+ newChild.sibling = null;
+}
+
+var NO_CONTEXT$1 = {};
+
+var contextStackCursor$1 = createCursor(NO_CONTEXT$1);
+var contextFiberStackCursor = createCursor(NO_CONTEXT$1);
+var rootInstanceStackCursor = createCursor(NO_CONTEXT$1);
+
+function requiredContext(c) {
+ !(c !== NO_CONTEXT$1) ? reactProdInvariant('174') : void 0;
+ return c;
+}
+
+function getRootHostContainer() {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ return rootInstance;
+}
+
+function pushHostContainer(fiber, nextRootInstance) {
+ // Push current root instance onto the stack;
+ // This allows us to reset root when portals are popped.
+ push(rootInstanceStackCursor, nextRootInstance, fiber);
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+
+ // Finally, we need to push the host context to the stack.
+ // However, we can't just call getRootHostContext() and push it because
+ // we'd have a different number of entries on the stack depending on
+ // whether getRootHostContext() throws somewhere in renderer code or not.
+ // So we push an empty value first. This lets us safely unwind on errors.
+ push(contextStackCursor$1, NO_CONTEXT$1, fiber);
+ var nextRootContext = getRootHostContext(nextRootInstance);
+ // Now that we know this function doesn't throw, replace it.
+ pop(contextStackCursor$1, fiber);
+ push(contextStackCursor$1, nextRootContext, fiber);
+}
+
+function popHostContainer(fiber) {
+ pop(contextStackCursor$1, fiber);
+ pop(contextFiberStackCursor, fiber);
+ pop(rootInstanceStackCursor, fiber);
+}
+
+function getHostContext() {
+ var context = requiredContext(contextStackCursor$1.current);
+ return context;
+}
+
+function pushHostContext(fiber) {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ var context = requiredContext(contextStackCursor$1.current);
+ var nextContext = getChildHostContext(context, fiber.type, rootInstance);
+
+ // Don't push this Fiber's context unless it's unique.
+ if (context === nextContext) {
+ return;
+ }
+
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+ push(contextStackCursor$1, nextContext, fiber);
+}
+
+function popHostContext(fiber) {
+ // Do not pop unless this Fiber provided the current context.
+ // pushHostContext() only pushes Fibers that provide unique contexts.
+ if (contextFiberStackCursor.current !== fiber) {
+ return;
+ }
+
+ pop(contextStackCursor$1, fiber);
+ pop(contextFiberStackCursor, fiber);
+}
+
+var NoEffect$1 = /* */0;
+var UnmountSnapshot = /* */2;
+var UnmountMutation = /* */4;
+var MountMutation = /* */8;
+var UnmountLayout = /* */16;
+var MountLayout = /* */32;
+var MountPassive = /* */64;
+var UnmountPassive = /* */128;
+
+var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher;
+
+
+// These are set right before calling the component.
+var renderExpirationTime = NoWork;
+// The work-in-progress fiber. I've named it differently to distinguish it from
+// the work-in-progress hook.
+var currentlyRenderingFiber$1 = null;
+
+// Hooks are stored as a linked list on the fiber's memoizedState field. The
+// current hook list is the list that belongs to the current fiber. The
+// work-in-progress hook list is a new list that will be added to the
+// work-in-progress fiber.
+var currentHook = null;
+var nextCurrentHook = null;
+var firstWorkInProgressHook = null;
+var workInProgressHook = null;
+var nextWorkInProgressHook = null;
+
+var remainingExpirationTime = NoWork;
+var componentUpdateQueue = null;
+var sideEffectTag = 0;
+
+// Updates scheduled during render will trigger an immediate re-render at the
+// end of the current pass. We can't store these updates on the normal queue,
+// because if the work is aborted, they should be discarded. Because this is
+// a relatively rare case, we also don't want to add an additional field to
+// either the hook or queue object types. So we store them in a lazily create
+// map of queue -> render-phase updates, which are discarded once the component
+// completes without re-rendering.
+
+// Whether an update was scheduled during the currently executing render pass.
+var didScheduleRenderPhaseUpdate = false;
+// Lazily created map of render-phase updates
+var renderPhaseUpdates = null;
+// Counter to prevent infinite loops.
+var numberOfReRenders = 0;
+var RE_RENDER_LIMIT = 25;
+
+function throwInvalidHookError() {
+ reactProdInvariant('321');
+}
+
+function areHookInputsEqual(nextDeps, prevDeps) {
+ if (prevDeps === null) {
+ return false;
+ }
+
+ for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
+ if (is(nextDeps[i], prevDeps[i])) {
+ continue;
+ }
+ return false;
+ }
+ return true;
+}
+
+function renderWithHooks(current, workInProgress, Component, props, refOrContext, nextRenderExpirationTime) {
+ renderExpirationTime = nextRenderExpirationTime;
+ currentlyRenderingFiber$1 = workInProgress;
+ nextCurrentHook = current !== null ? current.memoizedState : null;
+
+ {
+ ReactCurrentDispatcher$1.current = nextCurrentHook === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate;
+ }
+
+ var children = Component(props, refOrContext);
+
+ if (didScheduleRenderPhaseUpdate) {
+ do {
+ didScheduleRenderPhaseUpdate = false;
+ numberOfReRenders += 1;
+
+ // Start over from the beginning of the list
+ nextCurrentHook = current !== null ? current.memoizedState : null;
+ nextWorkInProgressHook = firstWorkInProgressHook;
+
+ currentHook = null;
+ workInProgressHook = null;
+ componentUpdateQueue = null;
+
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdate;
+
+ children = Component(props, refOrContext);
+ } while (didScheduleRenderPhaseUpdate);
+
+ renderPhaseUpdates = null;
+ numberOfReRenders = 0;
+ }
+
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrancy.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
+
+ var renderedWork = currentlyRenderingFiber$1;
+
+ renderedWork.memoizedState = firstWorkInProgressHook;
+ renderedWork.expirationTime = remainingExpirationTime;
+ renderedWork.updateQueue = componentUpdateQueue;
+ renderedWork.effectTag |= sideEffectTag;
+
+ var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
+
+ renderExpirationTime = NoWork;
+ currentlyRenderingFiber$1 = null;
+
+ currentHook = null;
+ nextCurrentHook = null;
+ firstWorkInProgressHook = null;
+ workInProgressHook = null;
+ nextWorkInProgressHook = null;
+
+ remainingExpirationTime = NoWork;
+ componentUpdateQueue = null;
+ sideEffectTag = 0;
+
+ // These were reset above
+ // didScheduleRenderPhaseUpdate = false;
+ // renderPhaseUpdates = null;
+ // numberOfReRenders = 0;
+
+ !!didRenderTooFewHooks ? reactProdInvariant('300') : void 0;
+
+ return children;
+}
+
+function bailoutHooks(current, workInProgress, expirationTime) {
+ workInProgress.updateQueue = current.updateQueue;
+ workInProgress.effectTag &= ~(Passive | Update);
+ if (current.expirationTime <= expirationTime) {
+ current.expirationTime = NoWork;
+ }
+}
+
+function resetHooks() {
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrancy.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
+
+ // This is used to reset the state of this module when a component throws.
+ // It's also called inside mountIndeterminateComponent if we determine the
+ // component is a module-style component.
+ renderExpirationTime = NoWork;
+ currentlyRenderingFiber$1 = null;
+
+ currentHook = null;
+ nextCurrentHook = null;
+ firstWorkInProgressHook = null;
+ workInProgressHook = null;
+ nextWorkInProgressHook = null;
+
+ remainingExpirationTime = NoWork;
+ componentUpdateQueue = null;
+ sideEffectTag = 0;
+
+ didScheduleRenderPhaseUpdate = false;
+ renderPhaseUpdates = null;
+ numberOfReRenders = 0;
+}
+
+function mountWorkInProgressHook() {
+ var hook = {
+ memoizedState: null,
+
+ baseState: null,
+ queue: null,
+ baseUpdate: null,
+
+ next: null
+ };
+
+ if (workInProgressHook === null) {
+ // This is the first hook in the list
+ firstWorkInProgressHook = workInProgressHook = hook;
+ } else {
+ // Append to the end of the list
+ workInProgressHook = workInProgressHook.next = hook;
+ }
+ return workInProgressHook;
+}
+
+function updateWorkInProgressHook() {
+ // This function is used both for updates and for re-renders triggered by a
+ // render phase update. It assumes there is either a current hook we can
+ // clone, or a work-in-progress hook from a previous render pass that we can
+ // use as a base. When we reach the end of the base list, we must switch to
+ // the dispatcher used for mounts.
+ if (nextWorkInProgressHook !== null) {
+ // There's already a work-in-progress. Reuse it.
+ workInProgressHook = nextWorkInProgressHook;
+ nextWorkInProgressHook = workInProgressHook.next;
+
+ currentHook = nextCurrentHook;
+ nextCurrentHook = currentHook !== null ? currentHook.next : null;
+ } else {
+ // Clone from the current hook.
+ !(nextCurrentHook !== null) ? reactProdInvariant('310') : void 0;
+ currentHook = nextCurrentHook;
+
+ var newHook = {
+ memoizedState: currentHook.memoizedState,
+
+ baseState: currentHook.baseState,
+ queue: currentHook.queue,
+ baseUpdate: currentHook.baseUpdate,
+
+ next: null
+ };
+
+ if (workInProgressHook === null) {
+ // This is the first hook in the list.
+ workInProgressHook = firstWorkInProgressHook = newHook;
+ } else {
+ // Append to the end of the list.
+ workInProgressHook = workInProgressHook.next = newHook;
+ }
+ nextCurrentHook = currentHook.next;
+ }
+ return workInProgressHook;
+}
+
+function createFunctionComponentUpdateQueue() {
+ return {
+ lastEffect: null
+ };
+}
+
+function basicStateReducer(state, action) {
+ return typeof action === 'function' ? action(state) : action;
+}
+
+function mountReducer(reducer, initialArg, init) {
+ var hook = mountWorkInProgressHook();
+ var initialState = void 0;
+ if (init !== undefined) {
+ initialState = init(initialArg);
+ } else {
+ initialState = initialArg;
+ }
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = hook.queue = {
+ last: null,
+ dispatch: null,
+ lastRenderedReducer: reducer,
+ lastRenderedState: initialState
+ };
+ var dispatch = queue.dispatch = dispatchAction.bind(null,
+ // Flow doesn't know this is non-null, but we do.
+ currentlyRenderingFiber$1, queue);
+ return [hook.memoizedState, dispatch];
+}
+
+function updateReducer(reducer, initialArg, init) {
+ var hook = updateWorkInProgressHook();
+ var queue = hook.queue;
+ !(queue !== null) ? reactProdInvariant('311') : void 0;
+
+ queue.lastRenderedReducer = reducer;
+
+ if (numberOfReRenders > 0) {
+ // This is a re-render. Apply the new render phase updates to the previous
+ var _dispatch = queue.dispatch;
+ if (renderPhaseUpdates !== null) {
+ // Render phase updates are stored in a map of queue -> linked list
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate !== undefined) {
+ renderPhaseUpdates.delete(queue);
+ var newState = hook.memoizedState;
+ var update = firstRenderPhaseUpdate;
+ do {
+ // Process this render phase update. We don't have to check the
+ // priority because it will always be the same as the current
+ // render's.
+ var _action = update.action;
+ newState = reducer(newState, _action);
+ update = update.next;
+ } while (update !== null);
+
+ // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
+ if (!is(newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
+ }
+
+ hook.memoizedState = newState;
+ // Don't persist the state accumlated from the render phase updates to
+ // the base state unless the queue is empty.
+ // TODO: Not sure if this is the desired semantics, but it's what we
+ // do for gDSFP. I can't remember why.
+ if (hook.baseUpdate === queue.last) {
+ hook.baseState = newState;
+ }
+
+ queue.lastRenderedState = newState;
+
+ return [newState, _dispatch];
+ }
+ }
+ return [hook.memoizedState, _dispatch];
+ }
+
+ // The last update in the entire queue
+ var last = queue.last;
+ // The last update that is part of the base state.
+ var baseUpdate = hook.baseUpdate;
+ var baseState = hook.baseState;
+
+ // Find the first unprocessed update.
+ var first = void 0;
+ if (baseUpdate !== null) {
+ if (last !== null) {
+ // For the first update, the queue is a circular linked list where
+ // `queue.last.next = queue.first`. Once the first update commits, and
+ // the `baseUpdate` is no longer empty, we can unravel the list.
+ last.next = null;
+ }
+ first = baseUpdate.next;
+ } else {
+ first = last !== null ? last.next : null;
+ }
+ if (first !== null) {
+ var _newState = baseState;
+ var newBaseState = null;
+ var newBaseUpdate = null;
+ var prevUpdate = baseUpdate;
+ var _update = first;
+ var didSkip = false;
+ do {
+ var updateExpirationTime = _update.expirationTime;
+ if (updateExpirationTime < renderExpirationTime) {
+ // Priority is insufficient. Skip this update. If this is the first
+ // skipped update, the previous update/state is the new base
+ // update/state.
+ if (!didSkip) {
+ didSkip = true;
+ newBaseUpdate = prevUpdate;
+ newBaseState = _newState;
+ }
+ // Update the remaining priority in the queue.
+ if (updateExpirationTime > remainingExpirationTime) {
+ remainingExpirationTime = updateExpirationTime;
+ }
+ } else {
+ // Process this update.
+ if (_update.eagerReducer === reducer) {
+ // If this update was processed eagerly, and its reducer matches the
+ // current reducer, we can use the eagerly computed state.
+ _newState = _update.eagerState;
+ } else {
+ var _action2 = _update.action;
+ _newState = reducer(_newState, _action2);
+ }
+ }
+ prevUpdate = _update;
+ _update = _update.next;
+ } while (_update !== null && _update !== first);
+
+ if (!didSkip) {
+ newBaseUpdate = prevUpdate;
+ newBaseState = _newState;
+ }
+
+ // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
+ if (!is(_newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
+ }
+
+ hook.memoizedState = _newState;
+ hook.baseUpdate = newBaseUpdate;
+ hook.baseState = newBaseState;
+
+ queue.lastRenderedState = _newState;
+ }
+
+ var dispatch = queue.dispatch;
+ return [hook.memoizedState, dispatch];
+}
+
+function mountState(initialState) {
+ var hook = mountWorkInProgressHook();
+ if (typeof initialState === 'function') {
+ initialState = initialState();
+ }
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = hook.queue = {
+ last: null,
+ dispatch: null,
+ lastRenderedReducer: basicStateReducer,
+ lastRenderedState: initialState
+ };
+ var dispatch = queue.dispatch = dispatchAction.bind(null,
+ // Flow doesn't know this is non-null, but we do.
+ currentlyRenderingFiber$1, queue);
+ return [hook.memoizedState, dispatch];
+}
+
+function updateState(initialState) {
+ return updateReducer(basicStateReducer, initialState);
+}
+
+function pushEffect(tag, create, destroy, deps) {
+ var effect = {
+ tag: tag,
+ create: create,
+ destroy: destroy,
+ deps: deps,
+ // Circular
+ next: null
+ };
+ if (componentUpdateQueue === null) {
+ componentUpdateQueue = createFunctionComponentUpdateQueue();
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var _lastEffect = componentUpdateQueue.lastEffect;
+ if (_lastEffect === null) {
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var firstEffect = _lastEffect.next;
+ _lastEffect.next = effect;
+ effect.next = firstEffect;
+ componentUpdateQueue.lastEffect = effect;
+ }
+ }
+ return effect;
+}
+
+function mountRef(initialValue) {
+ var hook = mountWorkInProgressHook();
+ var ref = { current: initialValue };
+ hook.memoizedState = ref;
+ return ref;
+}
+
+function updateRef(initialValue) {
+ var hook = updateWorkInProgressHook();
+ return hook.memoizedState;
+}
+
+function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ sideEffectTag |= fiberEffectTag;
+ hook.memoizedState = pushEffect(hookEffectTag, create, undefined, nextDeps);
+}
+
+function updateEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var destroy = undefined;
+
+ if (currentHook !== null) {
+ var prevEffect = currentHook.memoizedState;
+ destroy = prevEffect.destroy;
+ if (nextDeps !== null) {
+ var prevDeps = prevEffect.deps;
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ pushEffect(NoEffect$1, create, destroy, nextDeps);
+ return;
+ }
+ }
+ }
+
+ sideEffectTag |= fiberEffectTag;
+ hook.memoizedState = pushEffect(hookEffectTag, create, destroy, nextDeps);
+}
+
+function mountEffect(create, deps) {
+ return mountEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
+}
+
+function updateEffect(create, deps) {
+ return updateEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
+}
+
+function mountLayoutEffect(create, deps) {
+ return mountEffectImpl(Update, UnmountMutation | MountLayout, create, deps);
+}
+
+function updateLayoutEffect(create, deps) {
+ return updateEffectImpl(Update, UnmountMutation | MountLayout, create, deps);
+}
+
+function imperativeHandleEffect(create, ref) {
+ if (typeof ref === 'function') {
+ var refCallback = ref;
+ var _inst = create();
+ refCallback(_inst);
+ return function () {
+ refCallback(null);
+ };
+ } else if (ref !== null && ref !== undefined) {
+ var refObject = ref;
+ var _inst2 = create();
+ refObject.current = _inst2;
+ return function () {
+ refObject.current = null;
+ };
+ }
+}
+
+function mountImperativeHandle(ref, create, deps) {
+ var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+
+ return mountEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
+}
+
+function updateImperativeHandle(ref, create, deps) {
+ var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+
+ return updateEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
+}
+
+function mountDebugValue(value, formatterFn) {
+ // This hook is normally a no-op.
+ // The react-debug-hooks package injects its own implementation
+ // so that e.g. DevTools can display custom hook values.
+}
+
+var updateDebugValue = mountDebugValue;
+
+function mountCallback(callback, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
+
+function updateCallback(callback, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState;
+ if (prevState !== null) {
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
+
+function mountMemo(nextCreate, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+function updateMemo(nextCreate, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState;
+ if (prevState !== null) {
+ // Assume these are defined. If they're not, areHookInputsEqual will warn.
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
+ }
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
+
+function dispatchAction(fiber, queue, action) {
+ !(numberOfReRenders < RE_RENDER_LIMIT) ? reactProdInvariant('301') : void 0;
+
+ var alternate = fiber.alternate;
+ if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {
+ // This is a render phase update. Stash it in a lazily-created map of
+ // queue -> linked list of updates. After this render pass, we'll restart
+ // and apply the stashed updates on top of the work-in-progress hook.
+ didScheduleRenderPhaseUpdate = true;
+ var update = {
+ expirationTime: renderExpirationTime,
+ action: action,
+ eagerReducer: null,
+ eagerState: null,
+ next: null
+ };
+ if (renderPhaseUpdates === null) {
+ renderPhaseUpdates = new Map();
+ }
+ var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
+ if (firstRenderPhaseUpdate === undefined) {
+ renderPhaseUpdates.set(queue, update);
+ } else {
+ // Append the update to the end of the list.
+ var lastRenderPhaseUpdate = firstRenderPhaseUpdate;
+ while (lastRenderPhaseUpdate.next !== null) {
+ lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
+ }
+ lastRenderPhaseUpdate.next = update;
+ }
+ } else {
+ flushPassiveEffects$1();
+
+ var currentTime = requestCurrentTime();
+ var _expirationTime = computeExpirationForFiber(currentTime, fiber);
+
+ var _update2 = {
+ expirationTime: _expirationTime,
+ action: action,
+ eagerReducer: null,
+ eagerState: null,
+ next: null
+ };
+
+ // Append the update to the end of the list.
+ var _last = queue.last;
+ if (_last === null) {
+ // This is the first update. Create a circular list.
+ _update2.next = _update2;
+ } else {
+ var first = _last.next;
+ if (first !== null) {
+ // Still circular.
+ _update2.next = first;
+ }
+ _last.next = _update2;
+ }
+ queue.last = _update2;
+
+ if (fiber.expirationTime === NoWork && (alternate === null || alternate.expirationTime === NoWork)) {
+ // The queue is currently empty, which means we can eagerly compute the
+ // next state before entering the render phase. If the new state is the
+ // same as the current state, we may be able to bail out entirely.
+ var _lastRenderedReducer = queue.lastRenderedReducer;
+ if (_lastRenderedReducer !== null) {
+ try {
+ var currentState = queue.lastRenderedState;
+ var _eagerState = _lastRenderedReducer(currentState, action);
+ // Stash the eagerly computed state, and the reducer used to compute
+ // it, on the update object. If the reducer hasn't changed by the
+ // time we enter the render phase, then the eager state can be used
+ // without calling the reducer again.
+ _update2.eagerReducer = _lastRenderedReducer;
+ _update2.eagerState = _eagerState;
+ if (is(_eagerState, currentState)) {
+ // Fast path. We can bail out without scheduling React to re-render.
+ // It's still possible that we'll need to rebase this update later,
+ // if the component re-renders for a different reason and by that
+ // time the reducer has changed.
+ return;
+ }
+ } catch (error) {
+ // Suppress the error. It will throw again in the render phase.
+ } finally {
+
+ }
+ }
+ }
+ scheduleWork(fiber, _expirationTime);
+ }
+}
+
+var ContextOnlyDispatcher = {
+ readContext: readContext,
+
+ useCallback: throwInvalidHookError,
+ useContext: throwInvalidHookError,
+ useEffect: throwInvalidHookError,
+ useImperativeHandle: throwInvalidHookError,
+ useLayoutEffect: throwInvalidHookError,
+ useMemo: throwInvalidHookError,
+ useReducer: throwInvalidHookError,
+ useRef: throwInvalidHookError,
+ useState: throwInvalidHookError,
+ useDebugValue: throwInvalidHookError
+};
+
+var HooksDispatcherOnMount = {
+ readContext: readContext,
+
+ useCallback: mountCallback,
+ useContext: readContext,
+ useEffect: mountEffect,
+ useImperativeHandle: mountImperativeHandle,
+ useLayoutEffect: mountLayoutEffect,
+ useMemo: mountMemo,
+ useReducer: mountReducer,
+ useRef: mountRef,
+ useState: mountState,
+ useDebugValue: mountDebugValue
+};
+
+var HooksDispatcherOnUpdate = {
+ readContext: readContext,
+
+ useCallback: updateCallback,
+ useContext: readContext,
+ useEffect: updateEffect,
+ useImperativeHandle: updateImperativeHandle,
+ useLayoutEffect: updateLayoutEffect,
+ useMemo: updateMemo,
+ useReducer: updateReducer,
+ useRef: updateRef,
+ useState: updateState,
+ useDebugValue: updateDebugValue
+};
+
+var commitTime = 0;
+var profilerStartTime = -1;
+
+function getCommitTime() {
+ return commitTime;
+}
+
+function recordCommitTime() {
+ if (!enableProfilerTimer) {
+ return;
+ }
+ commitTime = now();
+}
+
+function startProfilerTimer(fiber) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+
+ profilerStartTime = now();
+
+ if (fiber.actualStartTime < 0) {
+ fiber.actualStartTime = now();
+ }
+}
+
+function stopProfilerTimerIfRunning(fiber) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+ profilerStartTime = -1;
+}
+
+function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
+ if (!enableProfilerTimer) {
+ return;
+ }
+
+ if (profilerStartTime >= 0) {
+ var elapsedTime = now() - profilerStartTime;
+ fiber.actualDuration += elapsedTime;
+ if (overrideBaseTime) {
+ fiber.selfBaseDuration = elapsedTime;
+ }
+ profilerStartTime = -1;
+ }
+}
+
+// The deepest Fiber on the stack involved in a hydration context.
+// This may have been an insertion or a hydration.
+var hydrationParentFiber = null;
+var nextHydratableInstance = null;
+var isHydrating = false;
+
+function enterHydrationState(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+
+ var parentInstance = fiber.stateNode.containerInfo;
+ nextHydratableInstance = getFirstHydratableChild(parentInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ return true;
+}
+
+function reenterHydrationStateFromDehydratedSuspenseInstance(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+
+ var suspenseInstance = fiber.stateNode;
+ nextHydratableInstance = getNextHydratableSibling(suspenseInstance);
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return true;
+}
+
+function deleteHydratableInstance(returnFiber, instance) {
+ var childToDelete = createFiberFromHostInstanceForDeletion();
+ childToDelete.stateNode = instance;
+ childToDelete.return = returnFiber;
+ childToDelete.effectTag = Deletion;
+
+ // This might seem like it belongs on progressedFirstDeletion. However,
+ // these children are not part of the reconciliation list of children.
+ // Even if we abort and rereconcile the children, that will try to hydrate
+ // again and the nodes are still in the host tree so these will be
+ // recreated.
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+}
+
+function insertNonHydratedInstance(returnFiber, fiber) {
+ fiber.effectTag |= Placement;
+
+}
+
+function tryHydrate(fiber, nextInstance) {
+ switch (fiber.tag) {
+ case HostComponent:
+ {
+ var type = fiber.type;
+ var props = fiber.pendingProps;
+ var instance = canHydrateInstance(nextInstance, type, props);
+ if (instance !== null) {
+ fiber.stateNode = instance;
+ return true;
+ }
+ return false;
+ }
+ case HostText:
+ {
+ var text = fiber.pendingProps;
+ var textInstance = canHydrateTextInstance(nextInstance, text);
+ if (textInstance !== null) {
+ fiber.stateNode = textInstance;
+ return true;
+ }
+ return false;
+ }
+ case SuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
+ if (suspenseInstance !== null) {
+ // Downgrade the tag to a dehydrated component until we've hydrated it.
+ fiber.tag = DehydratedSuspenseComponent;
+ fiber.stateNode = suspenseInstance;
+ return true;
+ }
+ }
+ return false;
+ }
+ default:
+ return false;
+ }
+}
+
+function tryToClaimNextHydratableInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
+ var nextInstance = nextHydratableInstance;
+ if (!nextInstance) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ var firstAttemptedInstance = nextInstance;
+ if (!tryHydrate(fiber, nextInstance)) {
+ // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
+ nextInstance = getNextHydratableSibling(firstAttemptedInstance);
+ if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ // We matched the next one, we'll now assume that the first one was
+ // superfluous and we'll delete it. Since we can't eagerly delete it
+ // we'll have to schedule a deletion. To do that, this node needs a dummy
+ // fiber associated with it.
+ deleteHydratableInstance(hydrationParentFiber, firstAttemptedInstance);
+ }
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = getFirstHydratableChild(nextInstance);
+}
+
+function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) {
+ if (!supportsHydration) {
+ reactProdInvariant('175');
+ }
+
+ var instance = fiber.stateNode;
+ var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber);
+ // TODO: Type this specific to this type of component.
+ fiber.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update.
+ if (updatePayload !== null) {
+ return true;
+ }
+ return false;
+}
+
+function prepareToHydrateHostTextInstance(fiber) {
+ if (!supportsHydration) {
+ reactProdInvariant('176');
+ }
+
+ var textInstance = fiber.stateNode;
+ var textContent = fiber.memoizedProps;
+ var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
+ return shouldUpdate;
+}
+
+function skipPastDehydratedSuspenseInstance(fiber) {
+ if (!supportsHydration) {
+ reactProdInvariant('316');
+ }
+ var suspenseInstance = fiber.stateNode;
+ !suspenseInstance ? reactProdInvariant('317') : void 0;
+ nextHydratableInstance = getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
+}
+
+function popToNextHostParent(fiber) {
+ var parent = fiber.return;
+ while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot && parent.tag !== DehydratedSuspenseComponent) {
+ parent = parent.return;
+ }
+ hydrationParentFiber = parent;
+}
+
+function popHydrationState(fiber) {
+ if (!supportsHydration) {
+ return false;
+ }
+ if (fiber !== hydrationParentFiber) {
+ // We're deeper than the current hydration context, inside an inserted
+ // tree.
+ return false;
+ }
+ if (!isHydrating) {
+ // If we're not currently hydrating but we're in a hydration context, then
+ // we were an insertion and now need to pop up reenter hydration of our
+ // siblings.
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return false;
+ }
+
+ var type = fiber.type;
+
+ // If we have any remaining hydratable nodes, we need to delete them now.
+ // We only do this deeper than head and body since they tend to have random
+ // other nodes in them. We also ignore components with pure text content in
+ // side of them.
+ // TODO: Better heuristic.
+ if (fiber.tag !== HostComponent || type !== 'head' && type !== 'body' && !shouldSetTextContent(type, fiber.memoizedProps)) {
+ var nextInstance = nextHydratableInstance;
+ while (nextInstance) {
+ deleteHydratableInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
+ }
+ }
+
+ popToNextHostParent(fiber);
+ nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null;
+ return true;
+}
+
+function resetHydrationState() {
+ if (!supportsHydration) {
+ return;
+ }
+
+ hydrationParentFiber = null;
+ nextHydratableInstance = null;
+ isHydrating = false;
+}
+
+var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner;
+
+var didReceiveUpdate = false;
+
+
+
+function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
+ if (current$$1 === null) {
+ // If this is a fresh new component that hasn't been rendered yet, we
+ // won't update its child set by applying minimal side-effects. Instead,
+ // we will add them all to the child before it gets rendered. That means
+ // we can optimize this reconciliation pass by not tracking side-effects.
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ // If the current child is the same as the work in progress, it means that
+ // we haven't yet started any work on these children. Therefore, we use
+ // the clone algorithm to create a copy of all the current children.
+
+ // If we had any progressed work already, that is invalid at this point so
+ // let's throw it out.
+ workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, nextChildren, renderExpirationTime);
+ }
+}
+
+function forceUnmountCurrentAndReconcile(current$$1, workInProgress, nextChildren, renderExpirationTime) {
+ // This function is fork of reconcileChildren. It's used in cases where we
+ // want to reconcile without matching against the existing set. This has the
+ // effect of all current children being unmounted; even if the type and key
+ // are the same, the old child is unmounted and a new child is created.
+ //
+ // To do this, we're going to go through the reconcile algorithm twice. In
+ // the first pass, we schedule a deletion for all the current children by
+ // passing null.
+ workInProgress.child = reconcileChildFibers(workInProgress, current$$1.child, null, renderExpirationTime);
+ // In the second pass, we mount the new children. The trick here is that we
+ // pass null in place of where we usually pass the current child set. This has
+ // the effect of remounting all children regardless of whether their their
+ // identity matches.
+ workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+}
+
+function updateForwardRef(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens after the first render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
+
+ var render = Component.render;
+ var ref = workInProgress.ref;
+
+ // The rest is a fork of updateFunctionComponent
+ var nextChildren = void 0;
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ {
+ nextChildren = renderWithHooks(current$$1, workInProgress, render, nextProps, ref, renderExpirationTime);
+ }
+
+ if (current$$1 !== null && !didReceiveUpdate) {
+ bailoutHooks(current$$1, workInProgress, renderExpirationTime);
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
+ if (current$$1 === null) {
+ var type = Component.type;
+ if (isSimpleFunctionComponent(type) && Component.compare === null &&
+ // SimpleMemoComponent codepath doesn't resolve outer props either.
+ Component.defaultProps === undefined) {
+ // If this is a plain function component without default props,
+ // and with only the default shallow comparison, we upgrade it
+ // to a SimpleMemoComponent to allow fast path updates.
+ workInProgress.tag = SimpleMemoComponent;
+ workInProgress.type = type;
+ return updateSimpleMemoComponent(current$$1, workInProgress, type, nextProps, updateExpirationTime, renderExpirationTime);
+ }
+ var child = createFiberFromTypeAndProps(Component.type, null, nextProps, null, workInProgress.mode, renderExpirationTime);
+ child.ref = workInProgress.ref;
+ child.return = workInProgress;
+ workInProgress.child = child;
+ return child;
+ }
+ var currentChild = current$$1.child; // This is always exactly one child
+ if (updateExpirationTime < renderExpirationTime) {
+ // This will be the props with resolved defaultProps,
+ // unlike current.memoizedProps which will be the unresolved ones.
+ var prevProps = currentChild.memoizedProps;
+ // Default to shallow comparison
+ var compare = Component.compare;
+ compare = compare !== null ? compare : shallowEqual;
+ if (compare(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ var newChild = createWorkInProgress(currentChild, nextProps, renderExpirationTime);
+ newChild.ref = workInProgress.ref;
+ newChild.return = workInProgress;
+ workInProgress.child = newChild;
+ return newChild;
+}
+
+function updateSimpleMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens when the inner render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
+
+ if (current$$1 !== null) {
+ var prevProps = current$$1.memoizedProps;
+ if (shallowEqual(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
+ didReceiveUpdate = false;
+ if (updateExpirationTime < renderExpirationTime) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ }
+ }
+ return updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
+}
+
+function updateFragment(current$$1, workInProgress, renderExpirationTime) {
+ var nextChildren = workInProgress.pendingProps;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateMode(current$$1, workInProgress, renderExpirationTime) {
+ var nextChildren = workInProgress.pendingProps.children;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateProfiler(current$$1, workInProgress, renderExpirationTime) {
+ if (enableProfilerTimer) {
+ workInProgress.effectTag |= Update;
+ }
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function markRef(current$$1, workInProgress) {
+ var ref = workInProgress.ref;
+ if (current$$1 === null && ref !== null || current$$1 !== null && current$$1.ref !== ref) {
+ // Schedule a Ref effect
+ workInProgress.effectTag |= Ref;
+ }
+}
+
+function updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ var nextChildren = void 0;
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ {
+ nextChildren = renderWithHooks(current$$1, workInProgress, Component, nextProps, context, renderExpirationTime);
+ }
+
+ if (current$$1 !== null && !didReceiveUpdate) {
+ bailoutHooks(current$$1, workInProgress, renderExpirationTime);
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
+ var hasContext = void 0;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ var instance = workInProgress.stateNode;
+ var shouldUpdate = void 0;
+ if (instance === null) {
+ if (current$$1 !== null) {
+ // An class component without an instance only mounts if it suspended
+ // inside a non- concurrent tree, in an inconsistent state. We want to
+ // tree it like a new mount, even though an empty version of it already
+ // committed. Disconnect the alternate pointers.
+ current$$1.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+ // In the initial pass we might need to construct the instance.
+ constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ shouldUpdate = true;
+ } else if (current$$1 === null) {
+ // In a resume, we'll already have an instance we can reuse.
+ shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ } else {
+ shouldUpdate = updateClassInstance(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
+ }
+ var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);
+ return nextUnitOfWork;
+}
+
+function finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
+ // Refs should update even if shouldComponentUpdate returns false
+ markRef(current$$1, workInProgress);
+
+ var didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;
+
+ if (!shouldUpdate && !didCaptureError) {
+ // Context providers should defer to sCU for rendering
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, false);
+ }
+
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+
+ var instance = workInProgress.stateNode;
+
+ // Rerender
+ ReactCurrentOwner$2.current = workInProgress;
+ var nextChildren = void 0;
+ if (didCaptureError && typeof Component.getDerivedStateFromError !== 'function') {
+ // If we captured an error, but getDerivedStateFrom catch is not defined,
+ // unmount all the children. componentDidCatch will schedule an update to
+ // re-render a fallback. This is temporary until we migrate everyone to
+ // the new API.
+ // TODO: Warn in a future release.
+ nextChildren = null;
+
+ if (enableProfilerTimer) {
+ stopProfilerTimerIfRunning(workInProgress);
+ }
+ } else {
+ {
+ nextChildren = instance.render();
+ }
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ if (current$$1 !== null && didCaptureError) {
+ // If we're recovering from an error, reconcile without reusing any of
+ // the existing children. Conceptually, the normal children and the children
+ // that are shown on error are two different sets, so we shouldn't reuse
+ // normal children even if their identities match.
+ forceUnmountCurrentAndReconcile(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ } else {
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ }
+
+ // Memoize state using the values we just used to render.
+ // TODO: Restructure so we never read values from the instance.
+ workInProgress.memoizedState = instance.state;
+
+ // The context might have changed so we need to recalculate it.
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, true);
+ }
+
+ return workInProgress.child;
+}
+
+function pushHostRootContext(workInProgress) {
+ var root = workInProgress.stateNode;
+ if (root.pendingContext) {
+ pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context);
+ } else if (root.context) {
+ // Should always be set
+ pushTopLevelContextObject(workInProgress, root.context, false);
+ }
+ pushHostContainer(workInProgress, root.containerInfo);
+}
+
+function updateHostRoot(current$$1, workInProgress, renderExpirationTime) {
+ pushHostRootContext(workInProgress);
+ var updateQueue = workInProgress.updateQueue;
+ !(updateQueue !== null) ? reactProdInvariant('282') : void 0;
+ var nextProps = workInProgress.pendingProps;
+ var prevState = workInProgress.memoizedState;
+ var prevChildren = prevState !== null ? prevState.element : null;
+ processUpdateQueue(workInProgress, updateQueue, nextProps, null, renderExpirationTime);
+ var nextState = workInProgress.memoizedState;
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ var nextChildren = nextState.element;
+ if (nextChildren === prevChildren) {
+ // If the state is the same as before, that's a bailout because we had
+ // no work that expires at this time.
+ resetHydrationState();
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ var root = workInProgress.stateNode;
+ if ((current$$1 === null || current$$1.child === null) && root.hydrate && enterHydrationState(workInProgress)) {
+ // If we don't have any current children this might be the first pass.
+ // We always try to hydrate. If this isn't a hydration pass there won't
+ // be any children to hydrate which is effectively the same thing as
+ // not hydrating.
+
+ // This is a bit of a hack. We track the host root as a placement to
+ // know that we're currently in a mounting state. That way isMounted
+ // works as expected. We must reset this before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag |= Placement;
+
+ // Ensure that children mount into this root without tracking
+ // side-effects. This ensures that we don't store Placement effects on
+ // nodes that will be hydrated.
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ // Otherwise reset hydration state in case we aborted and resumed another
+ // root.
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ resetHydrationState();
+ }
+ return workInProgress.child;
+}
+
+function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
+ pushHostContext(workInProgress);
+
+ if (current$$1 === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+
+ var type = workInProgress.type;
+ var nextProps = workInProgress.pendingProps;
+ var prevProps = current$$1 !== null ? current$$1.memoizedProps : null;
+
+ var nextChildren = nextProps.children;
+ var isDirectTextChild = shouldSetTextContent(type, nextProps);
+
+ if (isDirectTextChild) {
+ // We special case a direct text child of a host node. This is a common
+ // case. We won't handle it as a reified child. We will instead handle
+ // this in the host environment that also have access to this prop. That
+ // avoids allocating another HostText fiber and traversing it.
+ nextChildren = null;
+ } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
+ // If we're switching from a direct text child to a normal child, or to
+ // empty, we need to schedule the text content to be reset.
+ workInProgress.effectTag |= ContentReset;
+ }
+
+ markRef(current$$1, workInProgress);
+
+ // Check the host config to see if the children are offscreen/hidden.
+ if (renderExpirationTime !== Never && workInProgress.mode & ConcurrentMode && shouldDeprioritizeSubtree(type, nextProps)) {
+ // Schedule this fiber to re-render at offscreen priority. Then bailout.
+ workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
+ return null;
+ }
+
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateHostText(current$$1, workInProgress) {
+ if (current$$1 === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+ // Nothing to do here. This is terminal. We'll do the completion step
+ // immediately after.
+ return null;
+}
+
+function mountLazyComponent(_current, workInProgress, elementType, updateExpirationTime, renderExpirationTime) {
+ if (_current !== null) {
+ // An lazy component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ var props = workInProgress.pendingProps;
+ // We can't start a User Timing measurement with correct label yet.
+ // Cancel and resume right after we know the tag.
+ cancelWorkTimer(workInProgress);
+ var Component = readLazyComponentType(elementType);
+ // Store the unwrapped component in the type.
+ workInProgress.type = Component;
+ var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
+ startWorkTimer(workInProgress);
+ var resolvedProps = resolveDefaultProps(Component, props);
+ var child = void 0;
+ switch (resolvedTag) {
+ case FunctionComponent:
+ {
+ child = updateFunctionComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case ClassComponent:
+ {
+ child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case ForwardRef:
+ {
+ child = updateForwardRef(null, workInProgress, Component, resolvedProps, renderExpirationTime);
+ break;
+ }
+ case MemoComponent:
+ {
+ child = updateMemoComponent(null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
+ updateExpirationTime, renderExpirationTime);
+ break;
+ }
+ default:
+ {
+ var hint = '';
+ reactProdInvariant('306', Component, hint);
+ }
+ }
+ return child;
+}
+
+function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderExpirationTime) {
+ if (_current !== null) {
+ // An incomplete component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ // Promote the fiber to a class and try rendering again.
+ workInProgress.tag = ClassComponent;
+
+ // The rest of this function is a fork of `updateClassComponent`
+
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = void 0;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+ mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
+
+ return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
+}
+
+function mountIndeterminateComponent(_current, workInProgress, Component, renderExpirationTime) {
+ if (_current !== null) {
+ // An indeterminate component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ _current.alternate = null;
+ workInProgress.alternate = null;
+ // Since this is conceptually a new fiber, schedule a Placement effect
+ workInProgress.effectTag |= Placement;
+ }
+
+ var props = workInProgress.pendingProps;
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ prepareToReadContext(workInProgress, renderExpirationTime);
+
+ var value = void 0;
+
+ {
+ value = renderWithHooks(null, workInProgress, Component, props, context, renderExpirationTime);
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+
+ if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
+ // Proceed under the assumption that this is a class instance
+ workInProgress.tag = ClassComponent;
+
+ // Throw out any hooks that were used.
+ resetHooks();
+
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = false;
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+
+ workInProgress.memoizedState = value.state !== null && value.state !== undefined ? value.state : null;
+
+ var getDerivedStateFromProps = Component.getDerivedStateFromProps;
+ if (typeof getDerivedStateFromProps === 'function') {
+ applyDerivedStateFromProps(workInProgress, Component, getDerivedStateFromProps, props);
+ }
+
+ adoptClassInstance(workInProgress, value);
+ mountClassInstance(workInProgress, Component, props, renderExpirationTime);
+ return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
+ } else {
+ // Proceed under the assumption that this is a function component
+ workInProgress.tag = FunctionComponent;
+ reconcileChildren(null, workInProgress, value, renderExpirationTime);
+ return workInProgress.child;
+ }
+}
+
+function updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
+ var mode = workInProgress.mode;
+ var nextProps = workInProgress.pendingProps;
+
+ // We should attempt to render the primary children unless this boundary
+ // already suspended during this render (`alreadyCaptured` is true).
+ var nextState = workInProgress.memoizedState;
+
+ var nextDidTimeout = void 0;
+ if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This is the first attempt.
+ nextState = null;
+ nextDidTimeout = false;
+ } else {
+ // Something in this boundary's subtree already suspended. Switch to
+ // rendering the fallback children.
+ nextState = {
+ timedOutAt: nextState !== null ? nextState.timedOutAt : NoWork
+ };
+ nextDidTimeout = true;
+ workInProgress.effectTag &= ~DidCapture;
+ }
+
+ // This next part is a bit confusing. If the children timeout, we switch to
+ // showing the fallback children in place of the "primary" children.
+ // However, we don't want to delete the primary children because then their
+ // state will be lost (both the React state and the host state, e.g.
+ // uncontrolled form inputs). Instead we keep them mounted and hide them.
+ // Both the fallback children AND the primary children are rendered at the
+ // same time. Once the primary children are un-suspended, we can delete
+ // the fallback children — don't need to preserve their state.
+ //
+ // The two sets of children are siblings in the host environment, but
+ // semantically, for purposes of reconciliation, they are two separate sets.
+ // So we store them using two fragment fibers.
+ //
+ // However, we want to avoid allocating extra fibers for every placeholder.
+ // They're only necessary when the children time out, because that's the
+ // only time when both sets are mounted.
+ //
+ // So, the extra fragment fibers are only used if the children time out.
+ // Otherwise, we render the primary children directly. This requires some
+ // custom reconciliation logic to preserve the state of the primary
+ // children. It's essentially a very basic form of re-parenting.
+
+ // `child` points to the child fiber. In the normal case, this is the first
+ // fiber of the primary children set. In the timed-out case, it's a
+ // a fragment fiber containing the primary children.
+ var child = void 0;
+ // `next` points to the next fiber React should render. In the normal case,
+ // it's the same as `child`: the first fiber of the primary children set.
+ // In the timed-out case, it's a fragment fiber containing the *fallback*
+ // children -- we skip over the primary children entirely.
+ var next = void 0;
+ if (current$$1 === null) {
+ if (enableSuspenseServerRenderer) {
+ // If we're currently hydrating, try to hydrate this boundary.
+ // But only if this has a fallback.
+ if (nextProps.fallback !== undefined) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ // This could've changed the tag if this was a dehydrated suspense component.
+ if (workInProgress.tag === DehydratedSuspenseComponent) {
+ return updateDehydratedSuspenseComponent(null, workInProgress, renderExpirationTime);
+ }
+ }
+ }
+
+ // This is the initial mount. This branch is pretty simple because there's
+ // no previous state that needs to be preserved.
+ if (nextDidTimeout) {
+ // Mount separate fragments for primary and fallback children.
+ var nextFallbackChildren = nextProps.fallback;
+ var primaryChildFragment = createFiberFromFragment(null, mode, NoWork, null);
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var progressedState = workInProgress.memoizedState;
+ var progressedPrimaryChild = progressedState !== null ? workInProgress.child.child : workInProgress.child;
+ primaryChildFragment.child = progressedPrimaryChild;
+ }
+
+ var fallbackChildFragment = createFiberFromFragment(nextFallbackChildren, mode, renderExpirationTime, null);
+ primaryChildFragment.sibling = fallbackChildFragment;
+ child = primaryChildFragment;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = fallbackChildFragment;
+ child.return = next.return = workInProgress;
+ } else {
+ // Mount the primary children without an intermediate fragment fiber.
+ var nextPrimaryChildren = nextProps.children;
+ child = next = mountChildFibers(workInProgress, null, nextPrimaryChildren, renderExpirationTime);
+ }
+ } else {
+ // This is an update. This branch is more complicated because we need to
+ // ensure the state of the primary children is preserved.
+ var prevState = current$$1.memoizedState;
+ var prevDidTimeout = prevState !== null;
+ if (prevDidTimeout) {
+ // The current tree already timed out. That means each child set is
+ var currentPrimaryChildFragment = current$$1.child;
+ var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
+ if (nextDidTimeout) {
+ // Still timed out. Reuse the current primary children by cloning
+ // its fragment. We're going to skip over these entirely.
+ var _nextFallbackChildren = nextProps.fallback;
+ var _primaryChildFragment = createWorkInProgress(currentPrimaryChildFragment, currentPrimaryChildFragment.pendingProps, NoWork);
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var _progressedState = workInProgress.memoizedState;
+ var _progressedPrimaryChild = _progressedState !== null ? workInProgress.child.child : workInProgress.child;
+ if (_progressedPrimaryChild !== currentPrimaryChildFragment.child) {
+ _primaryChildFragment.child = _progressedPrimaryChild;
+ }
+ }
+
+ // Because primaryChildFragment is a new fiber that we're inserting as the
+ // parent of a new tree, we need to set its treeBaseDuration.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // treeBaseDuration is the sum of all the child tree base durations.
+ var treeBaseDuration = 0;
+ var hiddenChild = _primaryChildFragment.child;
+ while (hiddenChild !== null) {
+ treeBaseDuration += hiddenChild.treeBaseDuration;
+ hiddenChild = hiddenChild.sibling;
+ }
+ _primaryChildFragment.treeBaseDuration = treeBaseDuration;
+ }
+
+ // Clone the fallback child fragment, too. These we'll continue
+ // working on.
+ var _fallbackChildFragment = _primaryChildFragment.sibling = createWorkInProgress(currentFallbackChildFragment, _nextFallbackChildren, currentFallbackChildFragment.expirationTime);
+ child = _primaryChildFragment;
+ _primaryChildFragment.childExpirationTime = NoWork;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = _fallbackChildFragment;
+ child.return = next.return = workInProgress;
+ } else {
+ // No longer suspended. Switch back to showing the primary children,
+ // and remove the intermediate fragment fiber.
+ var _nextPrimaryChildren = nextProps.children;
+ var currentPrimaryChild = currentPrimaryChildFragment.child;
+ var primaryChild = reconcileChildFibers(workInProgress, currentPrimaryChild, _nextPrimaryChildren, renderExpirationTime);
+
+ // If this render doesn't suspend, we need to delete the fallback
+ // children. Wait until the complete phase, after we've confirmed the
+ // fallback is no longer needed.
+ // TODO: Would it be better to store the fallback fragment on
+ // the stateNode?
+
+ // Continue rendering the children, like we normally do.
+ child = next = primaryChild;
+ }
+ } else {
+ // The current tree has not already timed out. That means the primary
+ // children are not wrapped in a fragment fiber.
+ var _currentPrimaryChild = current$$1.child;
+ if (nextDidTimeout) {
+ // Timed out. Wrap the children in a fragment fiber to keep them
+ // separate from the fallback children.
+ var _nextFallbackChildren2 = nextProps.fallback;
+ var _primaryChildFragment2 = createFiberFromFragment(
+ // It shouldn't matter what the pending props are because we aren't
+ // going to render this fragment.
+ null, mode, NoWork, null);
+ _primaryChildFragment2.child = _currentPrimaryChild;
+
+ // Even though we're creating a new fiber, there are no new children,
+ // because we're reusing an already mounted tree. So we don't need to
+ // schedule a placement.
+ // primaryChildFragment.effectTag |= Placement;
+
+ if ((workInProgress.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, we commit the effects from the
+ var _progressedState2 = workInProgress.memoizedState;
+ var _progressedPrimaryChild2 = _progressedState2 !== null ? workInProgress.child.child : workInProgress.child;
+ _primaryChildFragment2.child = _progressedPrimaryChild2;
+ }
+
+ // Because primaryChildFragment is a new fiber that we're inserting as the
+ // parent of a new tree, we need to set its treeBaseDuration.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // treeBaseDuration is the sum of all the child tree base durations.
+ var _treeBaseDuration = 0;
+ var _hiddenChild = _primaryChildFragment2.child;
+ while (_hiddenChild !== null) {
+ _treeBaseDuration += _hiddenChild.treeBaseDuration;
+ _hiddenChild = _hiddenChild.sibling;
+ }
+ _primaryChildFragment2.treeBaseDuration = _treeBaseDuration;
+ }
+
+ // Create a fragment from the fallback children, too.
+ var _fallbackChildFragment2 = _primaryChildFragment2.sibling = createFiberFromFragment(_nextFallbackChildren2, mode, renderExpirationTime, null);
+ _fallbackChildFragment2.effectTag |= Placement;
+ child = _primaryChildFragment2;
+ _primaryChildFragment2.childExpirationTime = NoWork;
+ // Skip the primary children, and continue working on the
+ // fallback children.
+ next = _fallbackChildFragment2;
+ child.return = next.return = workInProgress;
+ } else {
+ // Still haven't timed out. Continue rendering the children, like we
+ // normally do.
+ var _nextPrimaryChildren2 = nextProps.children;
+ next = child = reconcileChildFibers(workInProgress, _currentPrimaryChild, _nextPrimaryChildren2, renderExpirationTime);
+ }
+ }
+ workInProgress.stateNode = current$$1.stateNode;
+ }
+
+ workInProgress.memoizedState = nextState;
+ workInProgress.child = child;
+ return next;
+}
+
+function updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
+ if (current$$1 === null) {
+ // During the first pass, we'll bail out and not drill into the children.
+ // Instead, we'll leave the content in place and try to hydrate it later.
+ workInProgress.expirationTime = Never;
+ return null;
+ }
+ // We use childExpirationTime to indicate that a child might depend on context, so if
+ // any context has changed, we need to treat is as if the input might have changed.
+ var hasContextChanged$$1 = current$$1.childExpirationTime >= renderExpirationTime;
+ if (didReceiveUpdate || hasContextChanged$$1) {
+ // This boundary has changed since the first render. This means that we are now unable to
+ // hydrate it. We might still be able to hydrate it using an earlier expiration time but
+ // during this render we can't. Instead, we're going to delete the whole subtree and
+ // instead inject a new real Suspense boundary to take its place, which may render content
+ // or fallback. The real Suspense boundary will suspend for a while so we have some time
+ // to ensure it can produce real content, but all state and pending events will be lost.
+
+ // Detach from the current dehydrated boundary.
+ current$$1.alternate = null;
+ workInProgress.alternate = null;
+
+ // Insert a deletion in the effect list.
+ var returnFiber = workInProgress.return;
+ !(returnFiber !== null) ? reactProdInvariant('315') : void 0;
+ var last = returnFiber.lastEffect;
+ if (last !== null) {
+ last.nextEffect = current$$1;
+ returnFiber.lastEffect = current$$1;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = current$$1;
+ }
+ current$$1.nextEffect = null;
+ current$$1.effectTag = Deletion;
+
+ // Upgrade this work in progress to a real Suspense component.
+ workInProgress.tag = SuspenseComponent;
+ workInProgress.stateNode = null;
+ workInProgress.memoizedState = null;
+ // This is now an insertion.
+ workInProgress.effectTag |= Placement;
+ // Retry as a real Suspense component.
+ return updateSuspenseComponent(null, workInProgress, renderExpirationTime);
+ }
+ if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This is the first attempt.
+ reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress);
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ return workInProgress.child;
+ } else {
+ // Something suspended. Leave the existing children in place.
+ // TODO: In non-concurrent mode, should we commit the nodes we have hydrated so far?
+ workInProgress.child = null;
+ return null;
+ }
+}
+
+function updatePortalComponent(current$$1, workInProgress, renderExpirationTime) {
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ var nextChildren = workInProgress.pendingProps;
+ if (current$$1 === null) {
+ // Portals are special because we don't append the children during mount
+ // but at commit. Therefore we need to track insertions which the normal
+ // flow doesn't do during mount. This doesn't happen at the root because
+ // the root always starts with a "current" with a null child.
+ // TODO: Consider unifying this with how the root works.
+ workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
+ } else {
+ reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
+ }
+ return workInProgress.child;
+}
+
+function updateContextProvider(current$$1, workInProgress, renderExpirationTime) {
+ var providerType = workInProgress.type;
+ var context = providerType._context;
+
+ var newProps = workInProgress.pendingProps;
+ var oldProps = workInProgress.memoizedProps;
+
+ var newValue = newProps.value;
+
+ pushProvider(workInProgress, newValue);
+
+ if (oldProps !== null) {
+ var oldValue = oldProps.value;
+ var changedBits = calculateChangedBits(context, newValue, oldValue);
+ if (changedBits === 0) {
+ // No change. Bailout early if children are the same.
+ if (oldProps.children === newProps.children && !hasContextChanged()) {
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ } else {
+ // The context value changed. Search for matching consumers and schedule
+ // them to update.
+ propagateContextChange(workInProgress, context, changedBits, renderExpirationTime);
+ }
+ }
+
+ var newChildren = newProps.children;
+ reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function updateContextConsumer(current$$1, workInProgress, renderExpirationTime) {
+ var context = workInProgress.type;
+ // The logic below for Context differs depending on PROD or DEV mode. In
+ // DEV mode, we create a separate object for Context.Consumer that acts
+ // like a proxy to Context. This proxy object adds unnecessary code in PROD
+ // so we use the old behaviour (Context.Consumer references Context) to
+ // reduce size and overhead. The separate object references context via
+ // a property called "_context", which also gives us the ability to check
+ // in DEV mode if this property exists or not and warn if it does not.
+ var newProps = workInProgress.pendingProps;
+ var render = newProps.children;
+
+ prepareToReadContext(workInProgress, renderExpirationTime);
+ var newValue = readContext(context, newProps.unstable_observedBits);
+ var newChildren = void 0;
+ {
+ newChildren = render(newValue);
+ }
+
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
+ return workInProgress.child;
+}
+
+function markWorkInProgressReceivedUpdate() {
+ didReceiveUpdate = true;
+}
+
+function bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime) {
+ cancelWorkTimer(workInProgress);
+
+ if (current$$1 !== null) {
+ // Reuse previous context list
+ workInProgress.contextDependencies = current$$1.contextDependencies;
+ }
+
+ if (enableProfilerTimer) {
+ // Don't update "base" render times for bailouts.
+ stopProfilerTimerIfRunning(workInProgress);
+ }
+
+ // Check if the children have any pending work.
+ var childExpirationTime = workInProgress.childExpirationTime;
+ if (childExpirationTime < renderExpirationTime) {
+ // The children don't have any work either. We can skip them.
+ // TODO: Once we add back resuming, we should check if the children are
+ // a work-in-progress set. If so, we need to transfer their effects.
+ return null;
+ } else {
+ // This fiber doesn't have work, but its subtree does. Clone the child
+ // fibers and continue.
+ cloneChildFibers(current$$1, workInProgress);
+ return workInProgress.child;
+ }
+}
+
+function beginWork(current$$1, workInProgress, renderExpirationTime) {
+ var updateExpirationTime = workInProgress.expirationTime;
+
+ if (current$$1 !== null) {
+ var oldProps = current$$1.memoizedProps;
+ var newProps = workInProgress.pendingProps;
+
+ if (oldProps !== newProps || hasContextChanged()) {
+ // If props or context changed, mark the fiber as having performed work.
+ // This may be unset if the props are determined to be equal later (memo).
+ didReceiveUpdate = true;
+ } else if (updateExpirationTime < renderExpirationTime) {
+ didReceiveUpdate = false;
+ // This fiber does not have any pending work. Bailout without entering
+ // the begin phase. There's still some bookkeeping we that needs to be done
+ // in this optimized path, mostly pushing stuff onto the stack.
+ switch (workInProgress.tag) {
+ case HostRoot:
+ pushHostRootContext(workInProgress);
+ resetHydrationState();
+ break;
+ case HostComponent:
+ pushHostContext(workInProgress);
+ break;
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ pushContextProvider(workInProgress);
+ }
+ break;
+ }
+ case HostPortal:
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ break;
+ case ContextProvider:
+ {
+ var newValue = workInProgress.memoizedProps.value;
+ pushProvider(workInProgress, newValue);
+ break;
+ }
+ case Profiler:
+ if (enableProfilerTimer) {
+ workInProgress.effectTag |= Update;
+ }
+ break;
+ case SuspenseComponent:
+ {
+ var state = workInProgress.memoizedState;
+ var didTimeout = state !== null;
+ if (didTimeout) {
+ // If this boundary is currently timed out, we need to decide
+ // whether to retry the primary children, or to skip over it and
+ // go straight to the fallback. Check the priority of the primary
+ var primaryChildFragment = workInProgress.child;
+ var primaryChildExpirationTime = primaryChildFragment.childExpirationTime;
+ if (primaryChildExpirationTime !== NoWork && primaryChildExpirationTime >= renderExpirationTime) {
+ // The primary children have pending work. Use the normal path
+ // to attempt to render the primary children again.
+ return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ } else {
+ // The primary children do not have pending work with sufficient
+ // priority. Bailout.
+ var child = bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ if (child !== null) {
+ // The fallback children have pending work. Skip over the
+ // primary children and work on the fallback.
+ return child.sibling;
+ } else {
+ return null;
+ }
+ }
+ }
+ break;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ // We know that this component will suspend again because if it has
+ // been unsuspended it has committed as a regular Suspense component.
+ // If it needs to be retried, it should have work scheduled on it.
+ workInProgress.effectTag |= DidCapture;
+ break;
+ }
+ }
+ }
+ return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
+ }
+ } else {
+ didReceiveUpdate = false;
+ }
+
+ // Before entering the begin phase, clear the expiration time.
+ workInProgress.expirationTime = NoWork;
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ {
+ var elementType = workInProgress.elementType;
+ return mountIndeterminateComponent(current$$1, workInProgress, elementType, renderExpirationTime);
+ }
+ case LazyComponent:
+ {
+ var _elementType = workInProgress.elementType;
+ return mountLazyComponent(current$$1, workInProgress, _elementType, updateExpirationTime, renderExpirationTime);
+ }
+ case FunctionComponent:
+ {
+ var _Component = workInProgress.type;
+ var unresolvedProps = workInProgress.pendingProps;
+ var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps);
+ return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
+ }
+ case ClassComponent:
+ {
+ var _Component2 = workInProgress.type;
+ var _unresolvedProps = workInProgress.pendingProps;
+ var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
+ return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
+ }
+ case HostRoot:
+ return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
+ case HostComponent:
+ return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
+ case HostText:
+ return updateHostText(current$$1, workInProgress);
+ case SuspenseComponent:
+ return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ case HostPortal:
+ return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
+ case ForwardRef:
+ {
+ var type = workInProgress.type;
+ var _unresolvedProps2 = workInProgress.pendingProps;
+ var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
+ return updateForwardRef(current$$1, workInProgress, type, _resolvedProps2, renderExpirationTime);
+ }
+ case Fragment:
+ return updateFragment(current$$1, workInProgress, renderExpirationTime);
+ case Mode:
+ return updateMode(current$$1, workInProgress, renderExpirationTime);
+ case Profiler:
+ return updateProfiler(current$$1, workInProgress, renderExpirationTime);
+ case ContextProvider:
+ return updateContextProvider(current$$1, workInProgress, renderExpirationTime);
+ case ContextConsumer:
+ return updateContextConsumer(current$$1, workInProgress, renderExpirationTime);
+ case MemoComponent:
+ {
+ var _type2 = workInProgress.type;
+ var _unresolvedProps3 = workInProgress.pendingProps;
+ // Resolve outer props first, then resolve inner props.
+ var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
+ _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
+ return updateMemoComponent(current$$1, workInProgress, _type2, _resolvedProps3, updateExpirationTime, renderExpirationTime);
+ }
+ case SimpleMemoComponent:
+ {
+ return updateSimpleMemoComponent(current$$1, workInProgress, workInProgress.type, workInProgress.pendingProps, updateExpirationTime, renderExpirationTime);
+ }
+ case IncompleteClassComponent:
+ {
+ var _Component3 = workInProgress.type;
+ var _unresolvedProps4 = workInProgress.pendingProps;
+ var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4);
+ return mountIncompleteClassComponent(current$$1, workInProgress, _Component3, _resolvedProps4, renderExpirationTime);
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ return updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
+ }
+ break;
+ }
+ }
+ reactProdInvariant('156');
+}
+
+var valueCursor = createCursor(null);
+
+var currentlyRenderingFiber = null;
+var lastContextDependency = null;
+var lastContextWithAllBitsObserved = null;
+
+function resetContextDependences() {
+ // This is called right before React yields execution, to ensure `readContext`
+ // cannot be called outside the render phase.
+ currentlyRenderingFiber = null;
+ lastContextDependency = null;
+ lastContextWithAllBitsObserved = null;
+
+}
+
+
+
+
+
+function pushProvider(providerFiber, nextValue) {
+ var context = providerFiber.type._context;
+
+ if (isPrimaryRenderer) {
+ push(valueCursor, context._currentValue, providerFiber);
+
+ context._currentValue = nextValue;
+
+ } else {
+ push(valueCursor, context._currentValue2, providerFiber);
+
+ context._currentValue2 = nextValue;
+
+ }
+}
+
+function popProvider(providerFiber) {
+ var currentValue = valueCursor.current;
+
+ pop(valueCursor, providerFiber);
+
+ var context = providerFiber.type._context;
+ if (isPrimaryRenderer) {
+ context._currentValue = currentValue;
+ } else {
+ context._currentValue2 = currentValue;
+ }
+}
+
+function calculateChangedBits(context, newValue, oldValue) {
+ if (is(oldValue, newValue)) {
+ // No change
+ return 0;
+ } else {
+ var changedBits = typeof context._calculateChangedBits === 'function' ? context._calculateChangedBits(oldValue, newValue) : maxSigned31BitInt;
+
+ return changedBits | 0;
+ }
+}
+
+function scheduleWorkOnParentPath(parent, renderExpirationTime) {
+ // Update the child expiration time of all the ancestors, including
+ // the alternates.
+ var node = parent;
+ while (node !== null) {
+ var alternate = node.alternate;
+ if (node.childExpirationTime < renderExpirationTime) {
+ node.childExpirationTime = renderExpirationTime;
+ if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
+ alternate.childExpirationTime = renderExpirationTime;
+ }
+ } else if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
+ alternate.childExpirationTime = renderExpirationTime;
+ } else {
+ // Neither alternate was updated, which means the rest of the
+ // ancestor path already has sufficient priority.
+ break;
+ }
+ node = node.return;
+ }
+}
+
+function propagateContextChange(workInProgress, context, changedBits, renderExpirationTime) {
+ var fiber = workInProgress.child;
+ if (fiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ fiber.return = workInProgress;
+ }
+ while (fiber !== null) {
+ var nextFiber = void 0;
+
+ // Visit this fiber.
+ var list = fiber.contextDependencies;
+ if (list !== null) {
+ nextFiber = fiber.child;
+
+ var dependency = list.first;
+ while (dependency !== null) {
+ // Check if the context matches.
+ if (dependency.context === context && (dependency.observedBits & changedBits) !== 0) {
+ // Match! Schedule an update on this fiber.
+
+ if (fiber.tag === ClassComponent) {
+ // Schedule a force update on the work-in-progress.
+ var update = createUpdate(renderExpirationTime);
+ update.tag = ForceUpdate;
+ // TODO: Because we don't have a work-in-progress, this will add the
+ // update to the current fiber, too, which means it will persist even if
+ // this render is thrown away. Since it's a race condition, not sure it's
+ // worth fixing.
+ enqueueUpdate(fiber, update);
+ }
+
+ if (fiber.expirationTime < renderExpirationTime) {
+ fiber.expirationTime = renderExpirationTime;
+ }
+ var alternate = fiber.alternate;
+ if (alternate !== null && alternate.expirationTime < renderExpirationTime) {
+ alternate.expirationTime = renderExpirationTime;
+ }
+
+ scheduleWorkOnParentPath(fiber.return, renderExpirationTime);
+
+ // Mark the expiration time on the list, too.
+ if (list.expirationTime < renderExpirationTime) {
+ list.expirationTime = renderExpirationTime;
+ }
+
+ // Since we already found a match, we can stop traversing the
+ // dependency list.
+ break;
+ }
+ dependency = dependency.next;
+ }
+ } else if (fiber.tag === ContextProvider) {
+ // Don't scan deeper if this is a matching provider
+ nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
+ } else if (enableSuspenseServerRenderer && fiber.tag === DehydratedSuspenseComponent) {
+ // If a dehydrated suspense component is in this subtree, we don't know
+ // if it will have any context consumers in it. The best we can do is
+ // mark it as having updates on its children.
+ if (fiber.expirationTime < renderExpirationTime) {
+ fiber.expirationTime = renderExpirationTime;
+ }
+ var _alternate = fiber.alternate;
+ if (_alternate !== null && _alternate.expirationTime < renderExpirationTime) {
+ _alternate.expirationTime = renderExpirationTime;
+ }
+ // This is intentionally passing this fiber as the parent
+ // because we want to schedule this fiber as having work
+ // on its children. We'll use the childExpirationTime on
+ // this fiber to indicate that a context has changed.
+ scheduleWorkOnParentPath(fiber, renderExpirationTime);
+ nextFiber = fiber.sibling;
+ } else {
+ // Traverse down.
+ nextFiber = fiber.child;
+ }
+
+ if (nextFiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ nextFiber.return = fiber;
+ } else {
+ // No child. Traverse to next sibling.
+ nextFiber = fiber;
+ while (nextFiber !== null) {
+ if (nextFiber === workInProgress) {
+ // We're back to the root of this subtree. Exit.
+ nextFiber = null;
+ break;
+ }
+ var sibling = nextFiber.sibling;
+ if (sibling !== null) {
+ // Set the return pointer of the sibling to the work-in-progress fiber.
+ sibling.return = nextFiber.return;
+ nextFiber = sibling;
+ break;
+ }
+ // No more siblings. Traverse up.
+ nextFiber = nextFiber.return;
+ }
+ }
+ fiber = nextFiber;
+ }
+}
+
+function prepareToReadContext(workInProgress, renderExpirationTime) {
+ currentlyRenderingFiber = workInProgress;
+ lastContextDependency = null;
+ lastContextWithAllBitsObserved = null;
+
+ var currentDependencies = workInProgress.contextDependencies;
+ if (currentDependencies !== null && currentDependencies.expirationTime >= renderExpirationTime) {
+ // Context list has a pending update. Mark that this fiber performed work.
+ markWorkInProgressReceivedUpdate();
+ }
+
+ // Reset the work-in-progress list
+ workInProgress.contextDependencies = null;
+}
+
+function readContext(context, observedBits) {
+ if (lastContextWithAllBitsObserved === context) {
+ // Nothing to do. We already observe everything in this context.
+ } else if (observedBits === false || observedBits === 0) {
+ // Do not observe any updates.
+ } else {
+ var resolvedObservedBits = void 0; // Avoid deopting on observable arguments or heterogeneous types.
+ if (typeof observedBits !== 'number' || observedBits === maxSigned31BitInt) {
+ // Observe all updates.
+ lastContextWithAllBitsObserved = context;
+ resolvedObservedBits = maxSigned31BitInt;
+ } else {
+ resolvedObservedBits = observedBits;
+ }
+
+ var contextItem = {
+ context: context,
+ observedBits: resolvedObservedBits,
+ next: null
+ };
+
+ if (lastContextDependency === null) {
+ !(currentlyRenderingFiber !== null) ? reactProdInvariant('308') : void 0;
+
+ // This is the first dependency for this component. Create a new list.
+ lastContextDependency = contextItem;
+ currentlyRenderingFiber.contextDependencies = {
+ first: contextItem,
+ expirationTime: NoWork
+ };
+ } else {
+ // Append a new context item.
+ lastContextDependency = lastContextDependency.next = contextItem;
+ }
+ }
+ return isPrimaryRenderer ? context._currentValue : context._currentValue2;
+}
+
+// UpdateQueue is a linked list of prioritized updates.
+//
+// Like fibers, update queues come in pairs: a current queue, which represents
+// the visible state of the screen, and a work-in-progress queue, which can be
+// mutated and processed asynchronously before it is committed — a form of
+// double buffering. If a work-in-progress render is discarded before finishing,
+// we create a new work-in-progress by cloning the current queue.
+//
+// Both queues share a persistent, singly-linked list structure. To schedule an
+// update, we append it to the end of both queues. Each queue maintains a
+// pointer to first update in the persistent list that hasn't been processed.
+// The work-in-progress pointer always has a position equal to or greater than
+// the current queue, since we always work on that one. The current queue's
+// pointer is only updated during the commit phase, when we swap in the
+// work-in-progress.
+//
+// For example:
+//
+// Current pointer: A - B - C - D - E - F
+// Work-in-progress pointer: D - E - F
+// ^
+// The work-in-progress queue has
+// processed more updates than current.
+//
+// The reason we append to both queues is because otherwise we might drop
+// updates without ever processing them. For example, if we only add updates to
+// the work-in-progress queue, some updates could be lost whenever a work-in
+// -progress render restarts by cloning from current. Similarly, if we only add
+// updates to the current queue, the updates will be lost whenever an already
+// in-progress queue commits and swaps with the current queue. However, by
+// adding to both queues, we guarantee that the update will be part of the next
+// work-in-progress. (And because the work-in-progress queue becomes the
+// current queue once it commits, there's no danger of applying the same
+// update twice.)
+//
+// Prioritization
+// --------------
+//
+// Updates are not sorted by priority, but by insertion; new updates are always
+// appended to the end of the list.
+//
+// The priority is still important, though. When processing the update queue
+// during the render phase, only the updates with sufficient priority are
+// included in the result. If we skip an update because it has insufficient
+// priority, it remains in the queue to be processed later, during a lower
+// priority render. Crucially, all updates subsequent to a skipped update also
+// remain in the queue *regardless of their priority*. That means high priority
+// updates are sometimes processed twice, at two separate priorities. We also
+// keep track of a base state, that represents the state before the first
+// update in the queue is applied.
+//
+// For example:
+//
+// Given a base state of '', and the following queue of updates
+//
+// A1 - B2 - C1 - D2
+//
+// where the number indicates the priority, and the update is applied to the
+// previous state by appending a letter, React will process these updates as
+// two separate renders, one per distinct priority level:
+//
+// First render, at priority 1:
+// Base state: ''
+// Updates: [A1, C1]
+// Result state: 'AC'
+//
+// Second render, at priority 2:
+// Base state: 'A' <- The base state does not include C1,
+// because B2 was skipped.
+// Updates: [B2, C1, D2] <- C1 was rebased on top of B2
+// Result state: 'ABCD'
+//
+// Because we process updates in insertion order, and rebase high priority
+// updates when preceding updates are skipped, the final result is deterministic
+// regardless of priority. Intermediate state may vary according to system
+// resources, but the final state is always the same.
+
+var UpdateState = 0;
+var ReplaceState = 1;
+var ForceUpdate = 2;
+var CaptureUpdate = 3;
+
+// Global state that is reset at the beginning of calling `processUpdateQueue`.
+// It should only be read right after calling `processUpdateQueue`, via
+// `checkHasForceUpdateAfterProcessing`.
+var hasForceUpdate = false;
+
+
+function createUpdateQueue(baseState) {
+ var queue = {
+ baseState: baseState,
+ firstUpdate: null,
+ lastUpdate: null,
+ firstCapturedUpdate: null,
+ lastCapturedUpdate: null,
+ firstEffect: null,
+ lastEffect: null,
+ firstCapturedEffect: null,
+ lastCapturedEffect: null
+ };
+ return queue;
+}
+
+function cloneUpdateQueue(currentQueue) {
+ var queue = {
+ baseState: currentQueue.baseState,
+ firstUpdate: currentQueue.firstUpdate,
+ lastUpdate: currentQueue.lastUpdate,
+
+ // TODO: With resuming, if we bail out and resuse the child tree, we should
+ // keep these effects.
+ firstCapturedUpdate: null,
+ lastCapturedUpdate: null,
+
+ firstEffect: null,
+ lastEffect: null,
+
+ firstCapturedEffect: null,
+ lastCapturedEffect: null
+ };
+ return queue;
+}
+
+function createUpdate(expirationTime) {
+ return {
+ expirationTime: expirationTime,
+
+ tag: UpdateState,
+ payload: null,
+ callback: null,
+
+ next: null,
+ nextEffect: null
+ };
+}
+
+function appendUpdateToQueue(queue, update) {
+ // Append the update to the end of the list.
+ if (queue.lastUpdate === null) {
+ // Queue is empty
+ queue.firstUpdate = queue.lastUpdate = update;
+ } else {
+ queue.lastUpdate.next = update;
+ queue.lastUpdate = update;
+ }
+}
+
+function enqueueUpdate(fiber, update) {
+ // Update queues are created lazily.
+ var alternate = fiber.alternate;
+ var queue1 = void 0;
+ var queue2 = void 0;
+ if (alternate === null) {
+ // There's only one fiber.
+ queue1 = fiber.updateQueue;
+ queue2 = null;
+ if (queue1 === null) {
+ queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
+ }
+ } else {
+ // There are two owners.
+ queue1 = fiber.updateQueue;
+ queue2 = alternate.updateQueue;
+ if (queue1 === null) {
+ if (queue2 === null) {
+ // Neither fiber has an update queue. Create new ones.
+ queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
+ queue2 = alternate.updateQueue = createUpdateQueue(alternate.memoizedState);
+ } else {
+ // Only one fiber has an update queue. Clone to create a new one.
+ queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
+ }
+ } else {
+ if (queue2 === null) {
+ // Only one fiber has an update queue. Clone to create a new one.
+ queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
+ } else {
+ // Both owners have an update queue.
+ }
+ }
+ }
+ if (queue2 === null || queue1 === queue2) {
+ // There's only a single queue.
+ appendUpdateToQueue(queue1, update);
+ } else {
+ // There are two queues. We need to append the update to both queues,
+ // while accounting for the persistent structure of the list — we don't
+ // want the same update to be added multiple times.
+ if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
+ // One of the queues is not empty. We must add the update to both queues.
+ appendUpdateToQueue(queue1, update);
+ appendUpdateToQueue(queue2, update);
+ } else {
+ // Both queues are non-empty. The last update is the same in both lists,
+ // because of structural sharing. So, only append to one of the lists.
+ appendUpdateToQueue(queue1, update);
+ // But we still need to update the `lastUpdate` pointer of queue2.
+ queue2.lastUpdate = update;
+ }
+ }
+
+
+}
+
+function enqueueCapturedUpdate(workInProgress, update) {
+ // Captured updates go into a separate list, and only on the work-in-
+ // progress queue.
+ var workInProgressQueue = workInProgress.updateQueue;
+ if (workInProgressQueue === null) {
+ workInProgressQueue = workInProgress.updateQueue = createUpdateQueue(workInProgress.memoizedState);
+ } else {
+ // TODO: I put this here rather than createWorkInProgress so that we don't
+ // clone the queue unnecessarily. There's probably a better way to
+ // structure this.
+ workInProgressQueue = ensureWorkInProgressQueueIsAClone(workInProgress, workInProgressQueue);
+ }
+
+ // Append the update to the end of the list.
+ if (workInProgressQueue.lastCapturedUpdate === null) {
+ // This is the first render phase update
+ workInProgressQueue.firstCapturedUpdate = workInProgressQueue.lastCapturedUpdate = update;
+ } else {
+ workInProgressQueue.lastCapturedUpdate.next = update;
+ workInProgressQueue.lastCapturedUpdate = update;
+ }
+}
+
+function ensureWorkInProgressQueueIsAClone(workInProgress, queue) {
+ var current = workInProgress.alternate;
+ if (current !== null) {
+ // If the work-in-progress queue is equal to the current queue,
+ // we need to clone it first.
+ if (queue === current.updateQueue) {
+ queue = workInProgress.updateQueue = cloneUpdateQueue(queue);
+ }
+ }
+ return queue;
+}
+
+function getStateFromUpdate(workInProgress, queue, update, prevState, nextProps, instance) {
+ switch (update.tag) {
+ case ReplaceState:
+ {
+ var _payload = update.payload;
+ if (typeof _payload === 'function') {
+ // Updater function
+ var nextState = _payload.call(instance, prevState, nextProps);
+ return nextState;
+ }
+ // State object
+ return _payload;
+ }
+ case CaptureUpdate:
+ {
+ workInProgress.effectTag = workInProgress.effectTag & ~ShouldCapture | DidCapture;
+ }
+ // Intentional fallthrough
+ case UpdateState:
+ {
+ var _payload2 = update.payload;
+ var partialState = void 0;
+ if (typeof _payload2 === 'function') {
+ // Updater function
+ partialState = _payload2.call(instance, prevState, nextProps);
+
+ } else {
+ // Partial state object
+ partialState = _payload2;
+ }
+ if (partialState === null || partialState === undefined) {
+ // Null and undefined are treated as no-ops.
+ return prevState;
+ }
+ // Merge the partial state and the previous state.
+ return _assign({}, prevState, partialState);
+ }
+ case ForceUpdate:
+ {
+ hasForceUpdate = true;
+ return prevState;
+ }
+ }
+ return prevState;
+}
+
+function processUpdateQueue(workInProgress, queue, props, instance, renderExpirationTime) {
+ hasForceUpdate = false;
+
+ queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);
+
+ var newBaseState = queue.baseState;
+ var newFirstUpdate = null;
+ var newExpirationTime = NoWork;
+
+ // Iterate through the list of updates to compute the result.
+ var update = queue.firstUpdate;
+ var resultState = newBaseState;
+ while (update !== null) {
+ var updateExpirationTime = update.expirationTime;
+ if (updateExpirationTime < renderExpirationTime) {
+ // This update does not have sufficient priority. Skip it.
+ if (newFirstUpdate === null) {
+ // This is the first skipped update. It will be the first update in
+ // the new list.
+ newFirstUpdate = update;
+ // Since this is the first update that was skipped, the current result
+ // is the new base state.
+ newBaseState = resultState;
+ }
+ // Since this update will remain in the list, update the remaining
+ // expiration time.
+ if (newExpirationTime < updateExpirationTime) {
+ newExpirationTime = updateExpirationTime;
+ }
+ } else {
+ // This update does have sufficient priority. Process it and compute
+ // a new result.
+ resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
+ var _callback = update.callback;
+ if (_callback !== null) {
+ workInProgress.effectTag |= Callback;
+ // Set this to null, in case it was mutated during an aborted render.
+ update.nextEffect = null;
+ if (queue.lastEffect === null) {
+ queue.firstEffect = queue.lastEffect = update;
+ } else {
+ queue.lastEffect.nextEffect = update;
+ queue.lastEffect = update;
+ }
+ }
+ }
+ // Continue to the next update.
+ update = update.next;
+ }
+
+ // Separately, iterate though the list of captured updates.
+ var newFirstCapturedUpdate = null;
+ update = queue.firstCapturedUpdate;
+ while (update !== null) {
+ var _updateExpirationTime = update.expirationTime;
+ if (_updateExpirationTime < renderExpirationTime) {
+ // This update does not have sufficient priority. Skip it.
+ if (newFirstCapturedUpdate === null) {
+ // This is the first skipped captured update. It will be the first
+ // update in the new list.
+ newFirstCapturedUpdate = update;
+ // If this is the first update that was skipped, the current result is
+ // the new base state.
+ if (newFirstUpdate === null) {
+ newBaseState = resultState;
+ }
+ }
+ // Since this update will remain in the list, update the remaining
+ // expiration time.
+ if (newExpirationTime < _updateExpirationTime) {
+ newExpirationTime = _updateExpirationTime;
+ }
+ } else {
+ // This update does have sufficient priority. Process it and compute
+ // a new result.
+ resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
+ var _callback2 = update.callback;
+ if (_callback2 !== null) {
+ workInProgress.effectTag |= Callback;
+ // Set this to null, in case it was mutated during an aborted render.
+ update.nextEffect = null;
+ if (queue.lastCapturedEffect === null) {
+ queue.firstCapturedEffect = queue.lastCapturedEffect = update;
+ } else {
+ queue.lastCapturedEffect.nextEffect = update;
+ queue.lastCapturedEffect = update;
+ }
+ }
+ }
+ update = update.next;
+ }
+
+ if (newFirstUpdate === null) {
+ queue.lastUpdate = null;
+ }
+ if (newFirstCapturedUpdate === null) {
+ queue.lastCapturedUpdate = null;
+ } else {
+ workInProgress.effectTag |= Callback;
+ }
+ if (newFirstUpdate === null && newFirstCapturedUpdate === null) {
+ // We processed every update, without skipping. That means the new base
+ // state is the same as the result state.
+ newBaseState = resultState;
+ }
+
+ queue.baseState = newBaseState;
+ queue.firstUpdate = newFirstUpdate;
+ queue.firstCapturedUpdate = newFirstCapturedUpdate;
+
+ // Set the remaining expiration time to be whatever is remaining in the queue.
+ // This should be fine because the only two other things that contribute to
+ // expiration time are props and context. We're already in the middle of the
+ // begin phase by the time we start processing the queue, so we've already
+ // dealt with the props. Context in components that specify
+ // shouldComponentUpdate is tricky; but we'll have to account for
+ // that regardless.
+ workInProgress.expirationTime = newExpirationTime;
+ workInProgress.memoizedState = resultState;
+
+
+}
+
+function callCallback(callback, context) {
+ !(typeof callback === 'function') ? reactProdInvariant('191', callback) : void 0;
+ callback.call(context);
+}
+
+function resetHasForceUpdateBeforeProcessing() {
+ hasForceUpdate = false;
+}
+
+function checkHasForceUpdateAfterProcessing() {
+ return hasForceUpdate;
+}
+
+function commitUpdateQueue(finishedWork, finishedQueue, instance, renderExpirationTime) {
+ // If the finished render included captured updates, and there are still
+ // lower priority updates left over, we need to keep the captured updates
+ // in the queue so that they are rebased and not dropped once we process the
+ // queue again at the lower priority.
+ if (finishedQueue.firstCapturedUpdate !== null) {
+ // Join the captured update list to the end of the normal list.
+ if (finishedQueue.lastUpdate !== null) {
+ finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;
+ finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;
+ }
+ // Clear the list of captured updates.
+ finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;
+ }
+
+ // Commit the effects
+ commitUpdateEffects(finishedQueue.firstEffect, instance);
+ finishedQueue.firstEffect = finishedQueue.lastEffect = null;
+
+ commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);
+ finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
+}
+
+function commitUpdateEffects(effect, instance) {
+ while (effect !== null) {
+ var _callback3 = effect.callback;
+ if (_callback3 !== null) {
+ effect.callback = null;
+ callCallback(_callback3, instance);
+ }
+ effect = effect.nextEffect;
+ }
+}
+
+function createCapturedValue(value, source) {
+ // If the value is an error, call this function immediately after it is thrown
+ // so the stack is accurate.
+ return {
+ value: value,
+ source: source,
+ stack: getStackByFiberInDevAndProd(source)
+ };
+}
+
+function markUpdate(workInProgress) {
+ // Tag the fiber with an update effect. This turns a Placement into
+ // a PlacementAndUpdate.
+ workInProgress.effectTag |= Update;
+}
+
+function markRef$1(workInProgress) {
+ workInProgress.effectTag |= Ref;
+}
+
+var appendAllChildren = void 0;
+var updateHostContainer = void 0;
+var updateHostComponent$1 = void 0;
+var updateHostText$1 = void 0;
+if (supportsMutation) {
+ // Mutation mode
+
+ appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ appendInitialChild(parent, node.stateNode);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+
+ updateHostContainer = function (workInProgress) {
+ // Noop
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ // If we have an alternate, that means this is an update and we need to
+ // schedule a side-effect to do the updates.
+ var oldProps = current.memoizedProps;
+ if (oldProps === newProps) {
+ // In mutation mode, this is sufficient for a bailout because
+ // we won't touch this node even if children changed.
+ return;
+ }
+
+ // If we get updated because one of our children updated, we don't
+ // have newProps so we'll have to reuse them.
+ // TODO: Split the update API as separate for the props vs. children.
+ // Even better would be if children weren't special cased at all tho.
+ var instance = workInProgress.stateNode;
+ var currentHostContext = getHostContext();
+ // TODO: Experiencing an error where oldProps is null. Suggests a host
+ // component is hitting the resume path. Figure out why. Possibly
+ // related to `hidden`.
+ var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
+ // TODO: Type this specific to this type of component.
+ workInProgress.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update. All the work is done in commitWork.
+ if (updatePayload) {
+ markUpdate(workInProgress);
+ }
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ // If the text differs, mark it as an update. All the work in done in commitWork.
+ if (oldText !== newText) {
+ markUpdate(workInProgress);
+ }
+ };
+} else if (supportsPersistence) {
+ // Persistent host tree mode
+
+ appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ // eslint-disable-next-line no-labels
+ branches: if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var props = node.memoizedProps;
+ var type = node.type;
+ if (isHidden) {
+ // This child is inside a timed out tree. Hide it.
+ instance = cloneHiddenInstance(instance, type, props, node);
+ } else {
+ // This child was previously inside a timed out tree. If it was not
+ // updated during this render, it may need to be unhidden. Clone
+ // again to be sure.
+ instance = cloneUnhiddenInstance(instance, type, props, node);
+ }
+ node.stateNode = instance;
+ }
+ appendInitialChild(parent, instance);
+ } else if (node.tag === HostText) {
+ var _instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var text = node.memoizedProps;
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ if (isHidden) {
+ _instance = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ } else {
+ _instance = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ }
+ node.stateNode = _instance;
+ }
+ appendInitialChild(parent, _instance);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.tag === SuspenseComponent) {
+ var current = node.alternate;
+ if (current !== null) {
+ var oldState = current.memoizedState;
+ var newState = node.memoizedState;
+ var oldIsHidden = oldState !== null;
+ var newIsHidden = newState !== null;
+ if (oldIsHidden !== newIsHidden) {
+ // The placeholder either just timed out or switched back to the normal
+ // children after having previously timed out. Toggle the visibility of
+ // the direct host children.
+ var primaryChildParent = newIsHidden ? node.child : node;
+ if (primaryChildParent !== null) {
+ appendAllChildren(parent, primaryChildParent, true, newIsHidden);
+ }
+ // eslint-disable-next-line no-labels
+ break branches;
+ }
+ }
+ if (node.child !== null) {
+ // Continue traversing like normal
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ // $FlowFixMe This is correct but Flow is confused by the labeled break.
+ node = node;
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+
+ // An unfortunate fork of appendAllChildren because we have two different parent types.
+ var appendAllChildrenToContainer = function (containerChildSet, workInProgress, needsVisibilityToggle, isHidden) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ // eslint-disable-next-line no-labels
+ branches: if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (needsVisibilityToggle) {
+ var props = node.memoizedProps;
+ var type = node.type;
+ if (isHidden) {
+ // This child is inside a timed out tree. Hide it.
+ instance = cloneHiddenInstance(instance, type, props, node);
+ } else {
+ // This child was previously inside a timed out tree. If it was not
+ // updated during this render, it may need to be unhidden. Clone
+ // again to be sure.
+ instance = cloneUnhiddenInstance(instance, type, props, node);
+ }
+ node.stateNode = instance;
+ }
+ appendChildToContainerChildSet(containerChildSet, instance);
+ } else if (node.tag === HostText) {
+ var _instance2 = node.stateNode;
+ if (needsVisibilityToggle) {
+ var text = node.memoizedProps;
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ if (isHidden) {
+ _instance2 = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ } else {
+ _instance2 = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
+ }
+ node.stateNode = _instance2;
+ }
+ appendChildToContainerChildSet(containerChildSet, _instance2);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.tag === SuspenseComponent) {
+ var current = node.alternate;
+ if (current !== null) {
+ var oldState = current.memoizedState;
+ var newState = node.memoizedState;
+ var oldIsHidden = oldState !== null;
+ var newIsHidden = newState !== null;
+ if (oldIsHidden !== newIsHidden) {
+ // The placeholder either just timed out or switched back to the normal
+ // children after having previously timed out. Toggle the visibility of
+ // the direct host children.
+ var primaryChildParent = newIsHidden ? node.child : node;
+ if (primaryChildParent !== null) {
+ appendAllChildrenToContainer(containerChildSet, primaryChildParent, true, newIsHidden);
+ }
+ // eslint-disable-next-line no-labels
+ break branches;
+ }
+ }
+ if (node.child !== null) {
+ // Continue traversing like normal
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ // $FlowFixMe This is correct but Flow is confused by the labeled break.
+ node = node;
+ if (node === workInProgress) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ };
+ updateHostContainer = function (workInProgress) {
+ var portalOrRoot = workInProgress.stateNode;
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged) {
+ // No changes, just reuse the existing instance.
+ } else {
+ var container = portalOrRoot.containerInfo;
+ var newChildSet = createContainerChildSet(container);
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildrenToContainer(newChildSet, workInProgress, false, false);
+ portalOrRoot.pendingChildren = newChildSet;
+ // Schedule an update on the container to swap out the container.
+ markUpdate(workInProgress);
+ finalizeContainerChildren(container, newChildSet);
+ }
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ var currentInstance = current.stateNode;
+ var oldProps = current.memoizedProps;
+ // If there are no effects associated with this node, then none of our children had any updates.
+ // This guarantees that we can reuse all of them.
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged && oldProps === newProps) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ return;
+ }
+ var recyclableInstance = workInProgress.stateNode;
+ var currentHostContext = getHostContext();
+ var updatePayload = null;
+ if (oldProps !== newProps) {
+ updatePayload = prepareUpdate(recyclableInstance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
+ }
+ if (childrenUnchanged && updatePayload === null) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ return;
+ }
+ var newInstance = cloneInstance(currentInstance, updatePayload, type, oldProps, newProps, workInProgress, childrenUnchanged, recyclableInstance);
+ if (finalizeInitialChildren(newInstance, type, newProps, rootContainerInstance, currentHostContext)) {
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = newInstance;
+ if (childrenUnchanged) {
+ // If there are no other effects in this tree, we need to flag this node as having one.
+ // Even though we're not going to use it for anything.
+ // Otherwise parents won't know that there are new children to propagate upwards.
+ markUpdate(workInProgress);
+ } else {
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildren(newInstance, workInProgress, false, false);
+ }
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ if (oldText !== newText) {
+ // If the text content differs, we'll create a new text instance for it.
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ workInProgress.stateNode = createTextInstance(newText, rootContainerInstance, currentHostContext, workInProgress);
+ // We'll have to mark it as having an effect, even though we won't use the effect for anything.
+ // This lets the parents know that at least one of their children has changed.
+ markUpdate(workInProgress);
+ }
+ };
+} else {
+ // No host operations
+ updateHostContainer = function (workInProgress) {
+ // Noop
+ };
+ updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
+ // Noop
+ };
+ updateHostText$1 = function (current, workInProgress, oldText, newText) {
+ // Noop
+ };
+}
+
+function completeWork(current, workInProgress, renderExpirationTime) {
+ var newProps = workInProgress.pendingProps;
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ break;
+ case LazyComponent:
+ break;
+ case SimpleMemoComponent:
+ case FunctionComponent:
+ break;
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
+ }
+ break;
+ }
+ case HostRoot:
+ {
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ var fiberRoot = workInProgress.stateNode;
+ if (fiberRoot.pendingContext) {
+ fiberRoot.context = fiberRoot.pendingContext;
+ fiberRoot.pendingContext = null;
+ }
+ if (current === null || current.child === null) {
+ // If we hydrated, pop so that we can delete any remaining children
+ // that weren't hydrated.
+ popHydrationState(workInProgress);
+ // This resets the hacky state to fix isMounted before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag &= ~Placement;
+ }
+ updateHostContainer(workInProgress);
+ break;
+ }
+ case HostComponent:
+ {
+ popHostContext(workInProgress);
+ var rootContainerInstance = getRootHostContainer();
+ var type = workInProgress.type;
+ if (current !== null && workInProgress.stateNode != null) {
+ updateHostComponent$1(current, workInProgress, type, newProps, rootContainerInstance);
+
+ if (current.ref !== workInProgress.ref) {
+ markRef$1(workInProgress);
+ }
+ } else {
+ if (!newProps) {
+ !(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
+ // This can happen when we abort work.
+ break;
+ }
+
+ var currentHostContext = getHostContext();
+ // TODO: Move createInstance to beginWork and keep it on a context
+ // "stack" as the parent. Then append children as we go in beginWork
+ // or completeWork depending on we want to add then top->down or
+ // bottom->up. Top->down is faster in IE11.
+ var wasHydrated = popHydrationState(workInProgress);
+ if (wasHydrated) {
+ // TODO: Move this and createInstance step into the beginPhase
+ // to consolidate.
+ if (prepareToHydrateHostInstance(workInProgress, rootContainerInstance, currentHostContext)) {
+ // If changes to the hydrated node needs to be applied at the
+ // commit-phase we mark this as such.
+ markUpdate(workInProgress);
+ }
+ } else {
+ var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
+
+ appendAllChildren(instance, workInProgress, false, false);
+
+ // Certain renderers require commit-time effects for initial mount.
+ // (eg DOM renderer supports auto-focus for certain elements).
+ // Make sure such renderers get scheduled for later work.
+ if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance, currentHostContext)) {
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = instance;
+ }
+
+ if (workInProgress.ref !== null) {
+ // If there is a ref on a host node we need to schedule a callback
+ markRef$1(workInProgress);
+ }
+ }
+ break;
+ }
+ case HostText:
+ {
+ var newText = newProps;
+ if (current && workInProgress.stateNode != null) {
+ var oldText = current.memoizedProps;
+ // If we have an alternate, that means this is an update and we need
+ // to schedule a side-effect to do the updates.
+ updateHostText$1(current, workInProgress, oldText, newText);
+ } else {
+ if (typeof newText !== 'string') {
+ !(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
+ // This can happen when we abort work.
+ }
+ var _rootContainerInstance = getRootHostContainer();
+ var _currentHostContext = getHostContext();
+ var _wasHydrated = popHydrationState(workInProgress);
+ if (_wasHydrated) {
+ if (prepareToHydrateHostTextInstance(workInProgress)) {
+ markUpdate(workInProgress);
+ }
+ } else {
+ workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext, workInProgress);
+ }
+ }
+ break;
+ }
+ case ForwardRef:
+ break;
+ case SuspenseComponent:
+ {
+ var nextState = workInProgress.memoizedState;
+ if ((workInProgress.effectTag & DidCapture) !== NoEffect) {
+ // Something suspended. Re-render with the fallback children.
+ workInProgress.expirationTime = renderExpirationTime;
+ // Do not reset the effect list.
+ return workInProgress;
+ }
+
+ var nextDidTimeout = nextState !== null;
+ var prevDidTimeout = current !== null && current.memoizedState !== null;
+
+ if (current !== null && !nextDidTimeout && prevDidTimeout) {
+ // We just switched from the fallback to the normal children. Delete
+ // the fallback.
+ // TODO: Would it be better to store the fallback fragment on
+ var currentFallbackChild = current.child.sibling;
+ if (currentFallbackChild !== null) {
+ // Deletions go at the beginning of the return fiber's effect list
+ var first = workInProgress.firstEffect;
+ if (first !== null) {
+ workInProgress.firstEffect = currentFallbackChild;
+ currentFallbackChild.nextEffect = first;
+ } else {
+ workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChild;
+ currentFallbackChild.nextEffect = null;
+ }
+ currentFallbackChild.effectTag = Deletion;
+ }
+ }
+
+ if (nextDidTimeout || prevDidTimeout) {
+ // If the children are hidden, or if they were previous hidden, schedule
+ // an effect to toggle their visibility. This is also used to attach a
+ // retry listener to the promise.
+ workInProgress.effectTag |= Update;
+ }
+ break;
+ }
+ case Fragment:
+ break;
+ case Mode:
+ break;
+ case Profiler:
+ break;
+ case HostPortal:
+ popHostContainer(workInProgress);
+ updateHostContainer(workInProgress);
+ break;
+ case ContextProvider:
+ // Pop provider fiber
+ popProvider(workInProgress);
+ break;
+ case ContextConsumer:
+ break;
+ case MemoComponent:
+ break;
+ case IncompleteClassComponent:
+ {
+ // Same as class component case. I put it down here so that the tags are
+ // sequential to ensure this switch is compiled to a jump table.
+ var _Component = workInProgress.type;
+ if (isContextProvider(_Component)) {
+ popContext(workInProgress);
+ }
+ break;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ if (current === null) {
+ var _wasHydrated2 = popHydrationState(workInProgress);
+ !_wasHydrated2 ? reactProdInvariant('318') : void 0;
+ skipPastDehydratedSuspenseInstance(workInProgress);
+ } else if ((workInProgress.effectTag & DidCapture) === NoEffect) {
+ // This boundary did not suspend so it's now hydrated.
+ // To handle any future suspense cases, we're going to now upgrade it
+ // to a Suspense component. We detach it from the existing current fiber.
+ current.alternate = null;
+ workInProgress.alternate = null;
+ workInProgress.tag = SuspenseComponent;
+ workInProgress.memoizedState = null;
+ workInProgress.stateNode = null;
+ }
+ }
+ break;
+ }
+ default:
+ reactProdInvariant('156');
+ }
+
+ return null;
+}
+
+function shouldCaptureSuspense(workInProgress) {
+ // In order to capture, the Suspense component must have a fallback prop.
+ if (workInProgress.memoizedProps.fallback === undefined) {
+ return false;
+ }
+ // If it was the primary children that just suspended, capture and render the
+ // fallback. Otherwise, don't capture and bubble to the next boundary.
+ var nextState = workInProgress.memoizedState;
+ return nextState === null;
+}
+
+// This module is forked in different environments.
+// By default, return `true` to log errors to the console.
+// Forks can return `false` if this isn't desirable.
+function showErrorDialog(capturedError) {
+ return true;
+}
+
+function logCapturedError(capturedError) {
+ var logError = showErrorDialog(capturedError);
+
+ // Allow injected showErrorDialog() to prevent default console.error logging.
+ // This enables renderers like ReactNative to better manage redbox behavior.
+ if (logError === false) {
+ return;
+ }
+
+ var error = capturedError.error;
+ {
+ // In production, we print the error directly.
+ // This will include the message, the JS stack, and anything the browser wants to show.
+ // We pass the error object instead of custom message so that the browser displays the error natively.
+ console.error(error);
+ }
+}
+
+var PossiblyWeakSet$1 = typeof WeakSet === 'function' ? WeakSet : Set;
+
+function logError(boundary, errorInfo) {
+ var source = errorInfo.source;
+ var stack = errorInfo.stack;
+ if (stack === null && source !== null) {
+ stack = getStackByFiberInDevAndProd(source);
+ }
+
+ var capturedError = {
+ componentName: source !== null ? getComponentName(source.type) : null,
+ componentStack: stack !== null ? stack : '',
+ error: errorInfo.value,
+ errorBoundary: null,
+ errorBoundaryName: null,
+ errorBoundaryFound: false,
+ willRetry: false
+ };
+
+ if (boundary !== null && boundary.tag === ClassComponent) {
+ capturedError.errorBoundary = boundary.stateNode;
+ capturedError.errorBoundaryName = getComponentName(boundary.type);
+ capturedError.errorBoundaryFound = true;
+ capturedError.willRetry = true;
+ }
+
+ try {
+ logCapturedError(capturedError);
+ } catch (e) {
+ // This method must not throw, or React internal state will get messed up.
+ // If console.error is overridden, or logCapturedError() shows a dialog that throws,
+ // we want to report this error outside of the normal stack as a last resort.
+ // https://github.com/facebook/react/issues/13188
+ setTimeout(function () {
+ throw e;
+ });
+ }
+}
+
+var callComponentWillUnmountWithTimer = function (current$$1, instance) {
+ startPhaseTimer(current$$1, 'componentWillUnmount');
+ instance.props = current$$1.memoizedProps;
+ instance.state = current$$1.memoizedState;
+ instance.componentWillUnmount();
+ stopPhaseTimer();
+};
+
+// Capture errors so they don't interrupt unmounting.
+function safelyCallComponentWillUnmount(current$$1, instance) {
+ {
+ try {
+ callComponentWillUnmountWithTimer(current$$1, instance);
+ } catch (unmountError) {
+ captureCommitPhaseError(current$$1, unmountError);
+ }
+ }
+}
+
+function safelyDetachRef(current$$1) {
+ var ref = current$$1.ref;
+ if (ref !== null) {
+ if (typeof ref === 'function') {
+ {
+ try {
+ ref(null);
+ } catch (refError) {
+ captureCommitPhaseError(current$$1, refError);
+ }
+ }
+ } else {
+ ref.current = null;
+ }
+ }
+}
+
+function safelyCallDestroy(current$$1, destroy) {
+ {
+ try {
+ destroy();
+ } catch (error) {
+ captureCommitPhaseError(current$$1, error);
+ }
+ }
+}
+
+function commitBeforeMutationLifeCycles(current$$1, finishedWork) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent:
+ {
+ commitHookEffectList(UnmountSnapshot, NoEffect$1, finishedWork);
+ return;
+ }
+ case ClassComponent:
+ {
+ if (finishedWork.effectTag & Snapshot) {
+ if (current$$1 !== null) {
+ var prevProps = current$$1.memoizedProps;
+ var prevState = current$$1.memoizedState;
+ startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
+ var instance = finishedWork.stateNode;
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
+ stopPhaseTimer();
+ }
+ }
+ return;
+ }
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ case IncompleteClassComponent:
+ // Nothing to do for these component types
+ return;
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function commitHookEffectList(unmountTag, mountTag, finishedWork) {
+ var updateQueue = finishedWork.updateQueue;
+ var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
+ do {
+ if ((effect.tag & unmountTag) !== NoEffect$1) {
+ // Unmount
+ var destroy = effect.destroy;
+ effect.destroy = undefined;
+ if (destroy !== undefined) {
+ destroy();
+ }
+ }
+ if ((effect.tag & mountTag) !== NoEffect$1) {
+ // Mount
+ var create = effect.create;
+ effect.destroy = create();
+
+
+ }
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+}
+
+function commitPassiveHookEffects(finishedWork) {
+ commitHookEffectList(UnmountPassive, NoEffect$1, finishedWork);
+ commitHookEffectList(NoEffect$1, MountPassive, finishedWork);
+}
+
+function commitLifeCycles(finishedRoot, current$$1, finishedWork, committedExpirationTime) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent:
+ {
+ commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
+ break;
+ }
+ case ClassComponent:
+ {
+ var instance = finishedWork.stateNode;
+ if (finishedWork.effectTag & Update) {
+ if (current$$1 === null) {
+ startPhaseTimer(finishedWork, 'componentDidMount');
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ instance.componentDidMount();
+ stopPhaseTimer();
+ } else {
+ var prevProps = finishedWork.elementType === finishedWork.type ? current$$1.memoizedProps : resolveDefaultProps(finishedWork.type, current$$1.memoizedProps);
+ var prevState = current$$1.memoizedState;
+ startPhaseTimer(finishedWork, 'componentDidUpdate');
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
+ stopPhaseTimer();
+ }
+ }
+ var updateQueue = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ commitUpdateQueue(finishedWork, updateQueue, instance, committedExpirationTime);
+ }
+ return;
+ }
+ case HostRoot:
+ {
+ var _updateQueue = finishedWork.updateQueue;
+ if (_updateQueue !== null) {
+ var _instance = null;
+ if (finishedWork.child !== null) {
+ switch (finishedWork.child.tag) {
+ case HostComponent:
+ _instance = getPublicInstance(finishedWork.child.stateNode);
+ break;
+ case ClassComponent:
+ _instance = finishedWork.child.stateNode;
+ break;
+ }
+ }
+ commitUpdateQueue(finishedWork, _updateQueue, _instance, committedExpirationTime);
+ }
+ return;
+ }
+ case HostComponent:
+ {
+ var _instance2 = finishedWork.stateNode;
+
+ // Renderers may schedule work to be done after host components are mounted
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
+ // These effects should only be committed when components are first mounted,
+ // aka when there is no current/alternate.
+ if (current$$1 === null && finishedWork.effectTag & Update) {
+ var type = finishedWork.type;
+ var props = finishedWork.memoizedProps;
+
+ }
+
+ return;
+ }
+ case HostText:
+ {
+ // We have no life-cycles associated with text.
+ return;
+ }
+ case HostPortal:
+ {
+ // We have no life-cycles associated with portals.
+ return;
+ }
+ case Profiler:
+ {
+ if (enableProfilerTimer) {
+ var onRender = finishedWork.memoizedProps.onRender;
+
+ if (enableSchedulerTracing) {
+ onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime(), finishedRoot.memoizedInteractions);
+ } else {
+ onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime());
+ }
+ }
+ return;
+ }
+ case SuspenseComponent:
+ break;
+ case IncompleteClassComponent:
+ break;
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function hideOrUnhideAllChildren(finishedWork, isHidden) {
+ if (supportsMutation) {
+ // We only have the top Fiber that was inserted but we need to recurse down its
+ var node = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent) {
+ var instance = node.stateNode;
+ if (isHidden) {
+ hideInstance(instance);
+ } else {
+ unhideInstance(node.stateNode, node.memoizedProps);
+ }
+ } else if (node.tag === HostText) {
+ var _instance3 = node.stateNode;
+ if (isHidden) {
+ hideTextInstance(_instance3);
+ } else {
+ unhideTextInstance(_instance3, node.memoizedProps);
+ }
+ } else if (node.tag === SuspenseComponent && node.memoizedState !== null) {
+ // Found a nested Suspense component that timed out. Skip over the
+ var fallbackChildFragment = node.child.sibling;
+ fallbackChildFragment.return = node;
+ node = fallbackChildFragment;
+ continue;
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ }
+}
+
+function commitAttachRef(finishedWork) {
+ var ref = finishedWork.ref;
+ if (ref !== null) {
+ var instance = finishedWork.stateNode;
+ var instanceToUse = void 0;
+ switch (finishedWork.tag) {
+ case HostComponent:
+ instanceToUse = getPublicInstance(instance);
+ break;
+ default:
+ instanceToUse = instance;
+ }
+ if (typeof ref === 'function') {
+ ref(instanceToUse);
+ } else {
+ ref.current = instanceToUse;
+ }
+ }
+}
+
+function commitDetachRef(current$$1) {
+ var currentRef = current$$1.ref;
+ if (currentRef !== null) {
+ if (typeof currentRef === 'function') {
+ currentRef(null);
+ } else {
+ currentRef.current = null;
+ }
+ }
+}
+
+// User-originating errors (lifecycles and refs) should not interrupt
+// deletion, so don't let them throw. Host-originating errors should
+// interrupt deletion, so it's okay
+function commitUnmount(current$$1) {
+ onCommitUnmount(current$$1);
+
+ switch (current$$1.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ var updateQueue = current$$1.updateQueue;
+ if (updateQueue !== null) {
+ var lastEffect = updateQueue.lastEffect;
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
+ do {
+ var destroy = effect.destroy;
+ if (destroy !== undefined) {
+ safelyCallDestroy(current$$1, destroy);
+ }
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+ }
+ break;
+ }
+ case ClassComponent:
+ {
+ safelyDetachRef(current$$1);
+ var instance = current$$1.stateNode;
+ if (typeof instance.componentWillUnmount === 'function') {
+ safelyCallComponentWillUnmount(current$$1, instance);
+ }
+ return;
+ }
+ case HostComponent:
+ {
+ safelyDetachRef(current$$1);
+ return;
+ }
+ case HostPortal:
+ {
+ // TODO: this is recursive.
+ // We are also not using this parent because
+ // the portal will get pushed immediately.
+ if (supportsMutation) {
+ unmountHostComponents(current$$1);
+ } else if (supportsPersistence) {
+ emptyPortalContainer(current$$1);
+ }
+ return;
+ }
+ }
+}
+
+function commitNestedUnmounts(root) {
+ // While we're inside a removed host node we don't want to call
+ // removeChild on the inner nodes because they're removed by the top
+ // call anyway. We also want to call componentWillUnmount on all
+ // composites before this host node is removed from the tree. Therefore
+ var node = root;
+ while (true) {
+ commitUnmount(node);
+ // Visit children because they may contain more composite or host nodes.
+ // Skip portals because commitUnmount() currently visits them recursively.
+ if (node.child !== null && (
+ // If we use mutation we drill down into portals using commitUnmount above.
+ // If we don't use mutation we drill down into portals here instead.
+ !supportsMutation || node.tag !== HostPortal)) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === root) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === root) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function detachFiber(current$$1) {
+ // Cut off the return pointers to disconnect it from the tree. Ideally, we
+ // should clear the child pointer of the parent alternate to let this
+ // get GC:ed but we don't know which for sure which parent is the current
+ // one so we'll settle for GC:ing the subtree of this child. This child
+ // itself will be GC:ed when the parent updates the next time.
+ current$$1.return = null;
+ current$$1.child = null;
+ current$$1.memoizedState = null;
+ current$$1.updateQueue = null;
+ var alternate = current$$1.alternate;
+ if (alternate !== null) {
+ alternate.return = null;
+ alternate.child = null;
+ alternate.memoizedState = null;
+ alternate.updateQueue = null;
+ }
+}
+
+function emptyPortalContainer(current$$1) {
+ if (!supportsPersistence) {
+ return;
+ }
+
+ var portal = current$$1.stateNode;
+ var containerInfo = portal.containerInfo;
+
+ var emptyChildSet = createContainerChildSet(containerInfo);
+ replaceContainerChildren(containerInfo, emptyChildSet);
+}
+
+function commitContainer(finishedWork) {
+ if (!supportsPersistence) {
+ return;
+ }
+
+ switch (finishedWork.tag) {
+ case ClassComponent:
+ {
+ return;
+ }
+ case HostComponent:
+ {
+ return;
+ }
+ case HostText:
+ {
+ return;
+ }
+ case HostRoot:
+ case HostPortal:
+ {
+ var portalOrRoot = finishedWork.stateNode;
+ var containerInfo = portalOrRoot.containerInfo,
+ _pendingChildren = portalOrRoot.pendingChildren;
+
+ replaceContainerChildren(containerInfo, _pendingChildren);
+ return;
+ }
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function getHostParentFiber(fiber) {
+ var parent = fiber.return;
+ while (parent !== null) {
+ if (isHostParent(parent)) {
+ return parent;
+ }
+ parent = parent.return;
+ }
+ reactProdInvariant('160');
+}
+
+function isHostParent(fiber) {
+ return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal;
+}
+
+function getHostSibling(fiber) {
+ // We're going to search forward into the tree until we find a sibling host
+ // node. Unfortunately, if multiple insertions are done in a row we have to
+ // search past them. This leads to exponential search for the next sibling.
+ var node = fiber;
+ siblings: while (true) {
+ // If we didn't find anything, let's try the next sibling.
+ while (node.sibling === null) {
+ if (node.return === null || isHostParent(node.return)) {
+ // If we pop out of the root or hit the parent the fiber we are the
+ // last sibling.
+ return null;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ while (node.tag !== HostComponent && node.tag !== HostText && node.tag !== DehydratedSuspenseComponent) {
+ // If it is not host node and, we might have a host node inside it.
+ // Try to search down until we find one.
+ if (node.effectTag & Placement) {
+ // If we don't have a child, try the siblings instead.
+ continue siblings;
+ }
+ // If we don't have a child, try the siblings instead.
+ // We also skip portals because they are not part of this host tree.
+ if (node.child === null || node.tag === HostPortal) {
+ continue siblings;
+ } else {
+ node.child.return = node;
+ node = node.child;
+ }
+ }
+ // Check if this host node is stable or about to be placed.
+ if (!(node.effectTag & Placement)) {
+ // Found it!
+ return node.stateNode;
+ }
+ }
+}
+
+function commitPlacement(finishedWork) {
+ if (!supportsMutation) {
+ return;
+ }
+
+ // Recursively insert all host nodes into the parent.
+ var parentFiber = getHostParentFiber(finishedWork);
+
+ // Note: these two variables *must* always be updated together.
+ var parent = void 0;
+ var isContainer = void 0;
+
+ switch (parentFiber.tag) {
+ case HostComponent:
+ parent = parentFiber.stateNode;
+ isContainer = false;
+ break;
+ case HostRoot:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ case HostPortal:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ default:
+ reactProdInvariant('161');
+ }
+ if (parentFiber.effectTag & ContentReset) {
+ // Reset the text content of the parent before doing any insertions
+ parentFiber.effectTag &= ~ContentReset;
+ }
+
+ var before = getHostSibling(finishedWork);
+ // We only have the top Fiber that was inserted but we need to recurse down its
+ // children to find all the terminal nodes.
+ var node = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ if (before) {
+ if (isContainer) {
+ insertInContainerBefore(parent, node.stateNode, before);
+ } else {
+ insertBefore(parent, node.stateNode, before);
+ }
+ } else {
+ if (isContainer) {
+ appendChildToContainer(parent, node.stateNode);
+ } else {
+ appendChild(parent, node.stateNode);
+ }
+ }
+ } else if (node.tag === HostPortal) {
+ // If the insertion itself is a portal, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function unmountHostComponents(current$$1) {
+ // We only have the top Fiber that was deleted but we need to recurse down its
+ var node = current$$1;
+
+ // Each iteration, currentParent is populated with node's host parent if not
+ // currentParentIsValid.
+ var currentParentIsValid = false;
+
+ // Note: these two variables *must* always be updated together.
+ var currentParent = void 0;
+ var currentParentIsContainer = void 0;
+
+ while (true) {
+ if (!currentParentIsValid) {
+ var parent = node.return;
+ findParent: while (true) {
+ !(parent !== null) ? reactProdInvariant('160') : void 0;
+ switch (parent.tag) {
+ case HostComponent:
+ currentParent = parent.stateNode;
+ currentParentIsContainer = false;
+ break findParent;
+ case HostRoot:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ case HostPortal:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ }
+ parent = parent.return;
+ }
+ currentParentIsValid = true;
+ }
+
+ if (node.tag === HostComponent || node.tag === HostText) {
+ commitNestedUnmounts(node);
+ // After all the children have unmounted, it is now safe to remove the
+ // node from the tree.
+ if (currentParentIsContainer) {
+ removeChildFromContainer(currentParent, node.stateNode);
+ } else {
+ removeChild(currentParent, node.stateNode);
+ }
+ // Don't visit children because we already visited them.
+ } else if (enableSuspenseServerRenderer && node.tag === DehydratedSuspenseComponent) {
+ // Delete the dehydrated suspense boundary and all of its content.
+ if (currentParentIsContainer) {
+ clearSuspenseBoundaryFromContainer(currentParent, node.stateNode);
+ } else {
+ clearSuspenseBoundary(currentParent, node.stateNode);
+ }
+ } else if (node.tag === HostPortal) {
+ if (node.child !== null) {
+ // When we go into a portal, it becomes the parent to remove from.
+ // We will reassign it back when we pop the portal on the way up.
+ currentParent = node.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ // Visit children because portals might contain host components.
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ } else {
+ commitUnmount(node);
+ // Visit children because we may find more host components below.
+ if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ }
+ if (node === current$$1) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node.return === null || node.return === current$$1) {
+ return;
+ }
+ node = node.return;
+ if (node.tag === HostPortal) {
+ // When we go out of the portal, we need to restore the parent.
+ // Since we don't keep a stack of them, we will search for it.
+ currentParentIsValid = false;
+ }
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+}
+
+function commitDeletion(current$$1) {
+ if (supportsMutation) {
+ // Recursively delete all host nodes from the parent.
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ unmountHostComponents(current$$1);
+ } else {
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ commitNestedUnmounts(current$$1);
+ }
+ detachFiber(current$$1);
+}
+
+function commitWork(current$$1, finishedWork) {
+ if (!supportsMutation) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ // Note: We currently never use MountMutation, but useLayout uses
+ // UnmountMutation.
+ commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
+ return;
+ }
+ }
+
+ commitContainer(finishedWork);
+ return;
+ }
+
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent:
+ {
+ // Note: We currently never use MountMutation, but useLayout uses
+ // UnmountMutation.
+ commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
+ return;
+ }
+ case ClassComponent:
+ {
+ return;
+ }
+ case HostComponent:
+ {
+ var instance = finishedWork.stateNode;
+ if (instance != null) {
+ // Commit the work prepared earlier.
+ var newProps = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldProps = current$$1 !== null ? current$$1.memoizedProps : newProps;
+ var type = finishedWork.type;
+ // TODO: Type the updateQueue to be specific to host components.
+ var updatePayload = finishedWork.updateQueue;
+ finishedWork.updateQueue = null;
+ if (updatePayload !== null) {
+ commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork);
+ }
+ }
+ return;
+ }
+ case HostText:
+ {
+ !(finishedWork.stateNode !== null) ? reactProdInvariant('162') : void 0;
+ var textInstance = finishedWork.stateNode;
+ var newText = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldText = current$$1 !== null ? current$$1.memoizedProps : newText;
+ commitTextUpdate(textInstance, oldText, newText);
+ return;
+ }
+ case HostRoot:
+ {
+ return;
+ }
+ case Profiler:
+ {
+ return;
+ }
+ case SuspenseComponent:
+ {
+ var newState = finishedWork.memoizedState;
+
+ var newDidTimeout = void 0;
+ var primaryChildParent = finishedWork;
+ if (newState === null) {
+ newDidTimeout = false;
+ } else {
+ newDidTimeout = true;
+ primaryChildParent = finishedWork.child;
+ if (newState.timedOutAt === NoWork) {
+ // If the children had not already timed out, record the time.
+ // This is used to compute the elapsed time during subsequent
+ // attempts to render the children.
+ newState.timedOutAt = requestCurrentTime();
+ }
+ }
+
+ if (primaryChildParent !== null) {
+ hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
+ }
+
+ // If this boundary just timed out, then it will have a set of thenables.
+ // For each thenable, attach a listener so that when it resolves, React
+ // attempts to re-render the boundary in the primary (pre-timeout) state.
+ var thenables = finishedWork.updateQueue;
+ if (thenables !== null) {
+ finishedWork.updateQueue = null;
+ var retryCache = finishedWork.stateNode;
+ if (retryCache === null) {
+ retryCache = finishedWork.stateNode = new PossiblyWeakSet$1();
+ }
+ thenables.forEach(function (thenable) {
+ // Memoize using the boundary fiber to prevent redundant listeners.
+ var retry = retryTimedOutBoundary.bind(null, finishedWork, thenable);
+ if (enableSchedulerTracing) {
+ retry = unstable_wrap(retry);
+ }
+ if (!retryCache.has(thenable)) {
+ retryCache.add(thenable);
+ thenable.then(retry, retry);
+ }
+ });
+ }
+
+ return;
+ }
+ case IncompleteClassComponent:
+ {
+ return;
+ }
+ default:
+ {
+ reactProdInvariant('163');
+ }
+ }
+}
+
+function commitResetTextContent(current$$1) {
+ if (!supportsMutation) {
+ return;
+ }
+ resetTextContent(current$$1.stateNode);
+}
+
+var PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
+var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
+
+function createRootErrorUpdate(fiber, errorInfo, expirationTime) {
+ var update = createUpdate(expirationTime);
+ // Unmount the root by rendering null.
+ update.tag = CaptureUpdate;
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ update.payload = { element: null };
+ var error = errorInfo.value;
+ update.callback = function () {
+ onUncaughtError(error);
+ logError(fiber, errorInfo);
+ };
+ return update;
+}
+
+function createClassErrorUpdate(fiber, errorInfo, expirationTime) {
+ var update = createUpdate(expirationTime);
+ update.tag = CaptureUpdate;
+ var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
+ if (typeof getDerivedStateFromError === 'function') {
+ var error = errorInfo.value;
+ update.payload = function () {
+ return getDerivedStateFromError(error);
+ };
+ }
+
+ var inst = fiber.stateNode;
+ if (inst !== null && typeof inst.componentDidCatch === 'function') {
+ update.callback = function callback() {
+ if (typeof getDerivedStateFromError !== 'function') {
+ // To preserve the preexisting retry behavior of error boundaries,
+ // we keep track of which ones already failed during this batch.
+ // This gets reset before we yield back to the browser.
+ // TODO: Warn in strict mode if getDerivedStateFromError is
+ // not defined.
+ markLegacyErrorBoundaryAsFailed(this);
+ }
+ var error = errorInfo.value;
+ var stack = errorInfo.stack;
+ logError(fiber, errorInfo);
+ this.componentDidCatch(error, {
+ componentStack: stack !== null ? stack : ''
+ });
+
+ };
+ }
+ return update;
+}
+
+function attachPingListener(root, renderExpirationTime, thenable) {
+ // Attach a listener to the promise to "ping" the root and retry. But
+ // only if one does not already exist for the current render expiration
+ // time (which acts like a "thread ID" here).
+ var pingCache = root.pingCache;
+ var threadIDs = void 0;
+ if (pingCache === null) {
+ pingCache = root.pingCache = new PossiblyWeakMap();
+ threadIDs = new Set();
+ pingCache.set(thenable, threadIDs);
+ } else {
+ threadIDs = pingCache.get(thenable);
+ if (threadIDs === undefined) {
+ threadIDs = new Set();
+ pingCache.set(thenable, threadIDs);
+ }
+ }
+ if (!threadIDs.has(renderExpirationTime)) {
+ // Memoize using the thread ID to prevent redundant listeners.
+ threadIDs.add(renderExpirationTime);
+ var ping = pingSuspendedRoot.bind(null, root, thenable, renderExpirationTime);
+ if (enableSchedulerTracing) {
+ ping = unstable_wrap(ping);
+ }
+ thenable.then(ping, ping);
+ }
+}
+
+function throwException(root, returnFiber, sourceFiber, value, renderExpirationTime) {
+ // The source fiber did not complete.
+ sourceFiber.effectTag |= Incomplete;
+ // Its effect list is no longer valid.
+ sourceFiber.firstEffect = sourceFiber.lastEffect = null;
+
+ if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
+ // This is a thenable.
+ var thenable = value;
+
+ // Find the earliest timeout threshold of all the placeholders in the
+ // ancestor path. We could avoid this traversal by storing the thresholds on
+ // the stack, but we choose not to because we only hit this path if we're
+ // IO-bound (i.e. if something suspends). Whereas the stack is used even in
+ // the non-IO- bound case.
+ var _workInProgress = returnFiber;
+ var earliestTimeoutMs = -1;
+ var startTimeMs = -1;
+ do {
+ if (_workInProgress.tag === SuspenseComponent) {
+ var current$$1 = _workInProgress.alternate;
+ if (current$$1 !== null) {
+ var currentState = current$$1.memoizedState;
+ if (currentState !== null) {
+ // Reached a boundary that already timed out. Do not search
+ // any further.
+ var timedOutAt = currentState.timedOutAt;
+ startTimeMs = expirationTimeToMs(timedOutAt);
+ // Do not search any further.
+ break;
+ }
+ }
+ var timeoutPropMs = _workInProgress.pendingProps.maxDuration;
+ if (typeof timeoutPropMs === 'number') {
+ if (timeoutPropMs <= 0) {
+ earliestTimeoutMs = 0;
+ } else if (earliestTimeoutMs === -1 || timeoutPropMs < earliestTimeoutMs) {
+ earliestTimeoutMs = timeoutPropMs;
+ }
+ }
+ }
+ // If there is a DehydratedSuspenseComponent we don't have to do anything because
+ // if something suspends inside it, we will simply leave that as dehydrated. It
+ // will never timeout.
+ _workInProgress = _workInProgress.return;
+ } while (_workInProgress !== null);
+
+ // Schedule the nearest Suspense to re-render the timed out view.
+ _workInProgress = returnFiber;
+ do {
+ if (_workInProgress.tag === SuspenseComponent && shouldCaptureSuspense(_workInProgress)) {
+ // Found the nearest boundary.
+
+ // Stash the promise on the boundary fiber. If the boundary times out, we'll
+ var thenables = _workInProgress.updateQueue;
+ if (thenables === null) {
+ var updateQueue = new Set();
+ updateQueue.add(thenable);
+ _workInProgress.updateQueue = updateQueue;
+ } else {
+ thenables.add(thenable);
+ }
+
+ // If the boundary is outside of concurrent mode, we should *not*
+ // suspend the commit. Pretend as if the suspended component rendered
+ // null and keep rendering. In the commit phase, we'll schedule a
+ // subsequent synchronous update to re-render the Suspense.
+ //
+ // Note: It doesn't matter whether the component that suspended was
+ // inside a concurrent mode tree. If the Suspense is outside of it, we
+ // should *not* suspend the commit.
+ if ((_workInProgress.mode & ConcurrentMode) === NoEffect) {
+ _workInProgress.effectTag |= DidCapture;
+
+ // We're going to commit this fiber even though it didn't complete.
+ // But we shouldn't call any lifecycle methods or callbacks. Remove
+ // all lifecycle effect tags.
+ sourceFiber.effectTag &= ~(LifecycleEffectMask | Incomplete);
+
+ if (sourceFiber.tag === ClassComponent) {
+ var currentSourceFiber = sourceFiber.alternate;
+ if (currentSourceFiber === null) {
+ // This is a new mount. Change the tag so it's not mistaken for a
+ // completed class component. For example, we should not call
+ // componentWillUnmount if it is deleted.
+ sourceFiber.tag = IncompleteClassComponent;
+ } else {
+ // When we try rendering again, we should not reuse the current fiber,
+ // since it's known to be in an inconsistent state. Use a force updte to
+ // prevent a bail out.
+ var update = createUpdate(Sync);
+ update.tag = ForceUpdate;
+ enqueueUpdate(sourceFiber, update);
+ }
+ }
+
+ // The source fiber did not complete. Mark it with Sync priority to
+ // indicate that it still has pending work.
+ sourceFiber.expirationTime = Sync;
+
+ // Exit without suspending.
+ return;
+ }
+
+ // Confirmed that the boundary is in a concurrent mode tree. Continue
+ // with the normal suspend path.
+
+ attachPingListener(root, renderExpirationTime, thenable);
+
+ var absoluteTimeoutMs = void 0;
+ if (earliestTimeoutMs === -1) {
+ // If no explicit threshold is given, default to an arbitrarily large
+ // value. The actual size doesn't matter because the threshold for the
+ // whole tree will be clamped to the expiration time.
+ absoluteTimeoutMs = maxSigned31BitInt;
+ } else {
+ if (startTimeMs === -1) {
+ // This suspend happened outside of any already timed-out
+ // placeholders. We don't know exactly when the update was
+ // scheduled, but we can infer an approximate start time from the
+ // expiration time. First, find the earliest uncommitted expiration
+ // time in the tree, including work that is suspended. Then subtract
+ // the offset used to compute an async update's expiration time.
+ // This will cause high priority (interactive) work to expire
+ // earlier than necessary, but we can account for this by adjusting
+ // for the Just Noticeable Difference.
+ var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, renderExpirationTime);
+ var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
+ startTimeMs = earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;
+ }
+ absoluteTimeoutMs = startTimeMs + earliestTimeoutMs;
+ }
+
+ // Mark the earliest timeout in the suspended fiber's ancestor path.
+ // After completing the root, we'll take the largest of all the
+ // suspended fiber's timeouts and use it to compute a timeout for the
+ // whole tree.
+ renderDidSuspend(root, absoluteTimeoutMs, renderExpirationTime);
+
+ _workInProgress.effectTag |= ShouldCapture;
+ _workInProgress.expirationTime = renderExpirationTime;
+ return;
+ } else if (enableSuspenseServerRenderer && _workInProgress.tag === DehydratedSuspenseComponent) {
+ attachPingListener(root, renderExpirationTime, thenable);
+
+ // Since we already have a current fiber, we can eagerly add a retry listener.
+ var retryCache = _workInProgress.memoizedState;
+ if (retryCache === null) {
+ retryCache = _workInProgress.memoizedState = new PossiblyWeakSet();
+ var _current = _workInProgress.alternate;
+ !_current ? reactProdInvariant('319') : void 0;
+ _current.memoizedState = retryCache;
+ }
+ // Memoize using the boundary fiber to prevent redundant listeners.
+ if (!retryCache.has(thenable)) {
+ retryCache.add(thenable);
+ var retry = retryTimedOutBoundary.bind(null, _workInProgress, thenable);
+ if (enableSchedulerTracing) {
+ retry = unstable_wrap(retry);
+ }
+ thenable.then(retry, retry);
+ }
+ _workInProgress.effectTag |= ShouldCapture;
+ _workInProgress.expirationTime = renderExpirationTime;
+ return;
+ }
+ // This boundary already captured during this render. Continue to the next
+ // boundary.
+ _workInProgress = _workInProgress.return;
+ } while (_workInProgress !== null);
+ // No boundary was found. Fallthrough to error mode.
+ // TODO: Use invariant so the message is stripped in prod?
+ value = new Error((getComponentName(sourceFiber.type) || 'A React component') + ' suspended while rendering, but no fallback UI was specified.\n' + '\n' + 'Add a <Suspense fallback=...> component higher in the tree to ' + 'provide a loading indicator or placeholder to display.' + getStackByFiberInDevAndProd(sourceFiber));
+ }
+
+ // We didn't find a boundary that could handle this type of exception. Start
+ // over and traverse parent path again, this time treating the exception
+ // as an error.
+ renderDidError();
+ value = createCapturedValue(value, sourceFiber);
+ var workInProgress = returnFiber;
+ do {
+ switch (workInProgress.tag) {
+ case HostRoot:
+ {
+ var _errorInfo = value;
+ workInProgress.effectTag |= ShouldCapture;
+ workInProgress.expirationTime = renderExpirationTime;
+ var _update = createRootErrorUpdate(workInProgress, _errorInfo, renderExpirationTime);
+ enqueueCapturedUpdate(workInProgress, _update);
+ return;
+ }
+ case ClassComponent:
+ // Capture and retry
+ var errorInfo = value;
+ var ctor = workInProgress.type;
+ var instance = workInProgress.stateNode;
+ if ((workInProgress.effectTag & DidCapture) === NoEffect && (typeof ctor.getDerivedStateFromError === 'function' || instance !== null && typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance))) {
+ workInProgress.effectTag |= ShouldCapture;
+ workInProgress.expirationTime = renderExpirationTime;
+ // Schedule the error boundary to re-render using updated state
+ var _update2 = createClassErrorUpdate(workInProgress, errorInfo, renderExpirationTime);
+ enqueueCapturedUpdate(workInProgress, _update2);
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ workInProgress = workInProgress.return;
+ } while (workInProgress !== null);
+}
+
+function unwindWork(workInProgress, renderExpirationTime) {
+ switch (workInProgress.tag) {
+ case ClassComponent:
+ {
+ var Component = workInProgress.type;
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
+ }
+ var effectTag = workInProgress.effectTag;
+ if (effectTag & ShouldCapture) {
+ workInProgress.effectTag = effectTag & ~ShouldCapture | DidCapture;
+ return workInProgress;
+ }
+ return null;
+ }
+ case HostRoot:
+ {
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ var _effectTag = workInProgress.effectTag;
+ !((_effectTag & DidCapture) === NoEffect) ? reactProdInvariant('285') : void 0;
+ workInProgress.effectTag = _effectTag & ~ShouldCapture | DidCapture;
+ return workInProgress;
+ }
+ case HostComponent:
+ {
+ // TODO: popHydrationState
+ popHostContext(workInProgress);
+ return null;
+ }
+ case SuspenseComponent:
+ {
+ var _effectTag2 = workInProgress.effectTag;
+ if (_effectTag2 & ShouldCapture) {
+ workInProgress.effectTag = _effectTag2 & ~ShouldCapture | DidCapture;
+ // Captured a suspense effect. Re-render the boundary.
+ return workInProgress;
+ }
+ return null;
+ }
+ case DehydratedSuspenseComponent:
+ {
+ if (enableSuspenseServerRenderer) {
+ // TODO: popHydrationState
+ var _effectTag3 = workInProgress.effectTag;
+ if (_effectTag3 & ShouldCapture) {
+ workInProgress.effectTag = _effectTag3 & ~ShouldCapture | DidCapture;
+ // Captured a suspense effect. Re-render the boundary.
+ return workInProgress;
+ }
+ }
+ return null;
+ }
+ case HostPortal:
+ popHostContainer(workInProgress);
+ return null;
+ case ContextProvider:
+ popProvider(workInProgress);
+ return null;
+ default:
+ return null;
+ }
+}
+
+function unwindInterruptedWork(interruptedWork) {
+ switch (interruptedWork.tag) {
+ case ClassComponent:
+ {
+ var childContextTypes = interruptedWork.type.childContextTypes;
+ if (childContextTypes !== null && childContextTypes !== undefined) {
+ popContext(interruptedWork);
+ }
+ break;
+ }
+ case HostRoot:
+ {
+ popHostContainer(interruptedWork);
+ popTopLevelContextObject(interruptedWork);
+ break;
+ }
+ case HostComponent:
+ {
+ popHostContext(interruptedWork);
+ break;
+ }
+ case HostPortal:
+ popHostContainer(interruptedWork);
+ break;
+ case ContextProvider:
+ popProvider(interruptedWork);
+ break;
+ default:
+ break;
+ }
+}
+
+var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;
+
+
+if (enableSchedulerTracing) {
+ // Provide explicit error message when production+profiling bundle of e.g. react-dom
+ // is used with production (non-profiling) bundle of scheduler/tracing
+ !(__interactionsRef != null && __interactionsRef.current != null) ? reactProdInvariant('302') : void 0;
+}
+
+var isWorking = false;
+
+// The next work in progress fiber that we're currently working on.
+var nextUnitOfWork = null;
+var nextRoot = null;
+// The time at which we're currently rendering work.
+var nextRenderExpirationTime = NoWork;
+var nextLatestAbsoluteTimeoutMs = -1;
+var nextRenderDidError = false;
+
+// The next fiber with an effect that we're currently committing.
+var nextEffect = null;
+
+var isCommitting$1 = false;
+var rootWithPendingPassiveEffects = null;
+var passiveEffectCallbackHandle = null;
+var passiveEffectCallback = null;
+
+var legacyErrorBoundariesThatAlreadyFailed = null;
+
+// Used for performance tracking.
+var interruptedBy = null;
+
+function resetStack() {
+ if (nextUnitOfWork !== null) {
+ var interruptedWork = nextUnitOfWork.return;
+ while (interruptedWork !== null) {
+ unwindInterruptedWork(interruptedWork);
+ interruptedWork = interruptedWork.return;
+ }
+ }
+
+ nextRoot = null;
+ nextRenderExpirationTime = NoWork;
+ nextLatestAbsoluteTimeoutMs = -1;
+ nextRenderDidError = false;
+ nextUnitOfWork = null;
+}
+
+function commitAllHostEffects() {
+ while (nextEffect !== null) {
+ recordEffect();
+
+ var effectTag = nextEffect.effectTag;
+
+ if (effectTag & ContentReset) {
+ commitResetTextContent(nextEffect);
+ }
+
+ if (effectTag & Ref) {
+ var current$$1 = nextEffect.alternate;
+ if (current$$1 !== null) {
+ commitDetachRef(current$$1);
+ }
+ }
+
+ // The following switch statement is only concerned about placement,
+ // updates, and deletions. To avoid needing to add a case for every
+ // possible bitmap value, we remove the secondary effects from the
+ // effect tag and switch on that value.
+ var primaryEffectTag = effectTag & (Placement | Update | Deletion);
+ switch (primaryEffectTag) {
+ case Placement:
+ {
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ // TODO: findDOMNode doesn't rely on this any more but isMounted
+ // does and isMounted is deprecated anyway so we should be able
+ // to kill this.
+ nextEffect.effectTag &= ~Placement;
+ break;
+ }
+ case PlacementAndUpdate:
+ {
+ // Placement
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ nextEffect.effectTag &= ~Placement;
+
+ // Update
+ var _current = nextEffect.alternate;
+ commitWork(_current, nextEffect);
+ break;
+ }
+ case Update:
+ {
+ var _current2 = nextEffect.alternate;
+ commitWork(_current2, nextEffect);
+ break;
+ }
+ case Deletion:
+ {
+ commitDeletion(nextEffect);
+ break;
+ }
+ }
+ nextEffect = nextEffect.nextEffect;
+ }
+
+
+}
+
+function commitBeforeMutationLifecycles() {
+ while (nextEffect !== null) {
+ var effectTag = nextEffect.effectTag;
+ if (effectTag & Snapshot) {
+ recordEffect();
+ var current$$1 = nextEffect.alternate;
+ commitBeforeMutationLifeCycles(current$$1, nextEffect);
+ }
+
+ nextEffect = nextEffect.nextEffect;
+ }
+
+
+}
+
+function commitAllLifeCycles(finishedRoot, committedExpirationTime) {
+ while (nextEffect !== null) {
+ var effectTag = nextEffect.effectTag;
+
+ if (effectTag & (Update | Callback)) {
+ recordEffect();
+ var current$$1 = nextEffect.alternate;
+ commitLifeCycles(finishedRoot, current$$1, nextEffect, committedExpirationTime);
+ }
+
+ if (effectTag & Ref) {
+ recordEffect();
+ commitAttachRef(nextEffect);
+ }
+
+ if (effectTag & Passive) {
+ rootWithPendingPassiveEffects = finishedRoot;
+ }
+
+ nextEffect = nextEffect.nextEffect;
+ }
+
+}
+
+function commitPassiveEffects(root, firstEffect) {
+ rootWithPendingPassiveEffects = null;
+ passiveEffectCallbackHandle = null;
+ passiveEffectCallback = null;
+
+ // Set this to true to prevent re-entrancy
+ var previousIsRendering = isRendering;
+ isRendering = true;
+
+ var effect = firstEffect;
+ do {
+ if (effect.effectTag & Passive) {
+ var didError = false;
+ var error = void 0;
+ {
+ try {
+ commitPassiveHookEffects(effect);
+ } catch (e) {
+ didError = true;
+ error = e;
+ }
+ }
+ if (didError) {
+ captureCommitPhaseError(effect, error);
+ }
+ }
+ effect = effect.nextEffect;
+ } while (effect !== null);
+ isRendering = previousIsRendering;
+
+ // Check if work was scheduled by one of the effects
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ // Flush any sync work that was scheduled by effects
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+}
+
+function isAlreadyFailedLegacyErrorBoundary(instance) {
+ return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);
+}
+
+function markLegacyErrorBoundaryAsFailed(instance) {
+ if (legacyErrorBoundariesThatAlreadyFailed === null) {
+ legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
+ } else {
+ legacyErrorBoundariesThatAlreadyFailed.add(instance);
+ }
+}
+
+function flushPassiveEffects$1() {
+ if (passiveEffectCallbackHandle !== null) {
+ cancelPassiveEffects(passiveEffectCallbackHandle);
+ }
+ if (passiveEffectCallback !== null) {
+ // We call the scheduled callback instead of commitPassiveEffects directly
+ // to ensure tracing works correctly.
+ passiveEffectCallback();
+ }
+}
+
+function commitRoot(root, finishedWork) {
+ isWorking = true;
+ isCommitting$1 = true;
+ startCommitTimer();
+
+ !(root.current !== finishedWork) ? reactProdInvariant('177') : void 0;
+ var committedExpirationTime = root.pendingCommitExpirationTime;
+ !(committedExpirationTime !== NoWork) ? reactProdInvariant('261') : void 0;
+ root.pendingCommitExpirationTime = NoWork;
+
+ // Update the pending priority levels to account for the work that we are
+ // about to commit. This needs to happen before calling the lifecycles, since
+ // they may schedule additional updates.
+ var updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
+ var childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
+ var earliestRemainingTimeBeforeCommit = childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit ? childExpirationTimeBeforeCommit : updateExpirationTimeBeforeCommit;
+ markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
+
+ var prevInteractions = null;
+ if (enableSchedulerTracing) {
+ // Restore any pending interactions at this point,
+ // So that cascading work triggered during the render phase will be accounted for.
+ prevInteractions = __interactionsRef.current;
+ __interactionsRef.current = root.memoizedInteractions;
+ }
+
+ // Reset this to null before calling lifecycles
+ ReactCurrentOwner$1.current = null;
+
+ var firstEffect = void 0;
+ if (finishedWork.effectTag > PerformedWork) {
+ // A fiber's effect list consists only of its children, not itself. So if
+ // the root has an effect, we need to add it to the end of the list. The
+ // resulting list is the set that would belong to the root's parent, if
+ // it had one; that is, all the effects in the tree including the root.
+ if (finishedWork.lastEffect !== null) {
+ finishedWork.lastEffect.nextEffect = finishedWork;
+ firstEffect = finishedWork.firstEffect;
+ } else {
+ firstEffect = finishedWork;
+ }
+ } else {
+ // There is no effect on the root.
+ firstEffect = finishedWork.firstEffect;
+ }
+
+ prepareForCommit(root.containerInfo);
+
+ // Invoke instances of getSnapshotBeforeUpdate before mutation.
+ nextEffect = firstEffect;
+ startCommitSnapshotEffectsTimer();
+ while (nextEffect !== null) {
+ var didError = false;
+ var error = void 0;
+ {
+ try {
+ commitBeforeMutationLifecycles();
+ } catch (e) {
+ didError = true;
+ error = e;
+ }
+ }
+ if (didError) {
+ !(nextEffect !== null) ? reactProdInvariant('178') : void 0;
+ captureCommitPhaseError(nextEffect, error);
+ // Clean-up
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+ stopCommitSnapshotEffectsTimer();
+
+ if (enableProfilerTimer) {
+ // Mark the current commit time to be shared by all Profilers in this batch.
+ // This enables them to be grouped later.
+ recordCommitTime();
+ }
+
+ // Commit all the side-effects within a tree. We'll do this in two passes.
+ // The first pass performs all the host insertions, updates, deletions and
+ // ref unmounts.
+ nextEffect = firstEffect;
+ startCommitHostEffectsTimer();
+ while (nextEffect !== null) {
+ var _didError = false;
+ var _error = void 0;
+ {
+ try {
+ commitAllHostEffects();
+ } catch (e) {
+ _didError = true;
+ _error = e;
+ }
+ }
+ if (_didError) {
+ !(nextEffect !== null) ? reactProdInvariant('178') : void 0;
+ captureCommitPhaseError(nextEffect, _error);
+ // Clean-up
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+ stopCommitHostEffectsTimer();
+
+ resetAfterCommit(root.containerInfo);
+
+ // The work-in-progress tree is now the current tree. This must come after
+ // the first pass of the commit phase, so that the previous tree is still
+ // current during componentWillUnmount, but before the second pass, so that
+ // the finished work is current during componentDidMount/Update.
+ root.current = finishedWork;
+
+ // In the second pass we'll perform all life-cycles and ref callbacks.
+ // Life-cycles happen as a separate pass so that all placements, updates,
+ // and deletions in the entire tree have already been invoked.
+ // This pass also triggers any renderer-specific initial effects.
+ nextEffect = firstEffect;
+ startCommitLifeCyclesTimer();
+ while (nextEffect !== null) {
+ var _didError2 = false;
+ var _error2 = void 0;
+ {
+ try {
+ commitAllLifeCycles(root, committedExpirationTime);
+ } catch (e) {
+ _didError2 = true;
+ _error2 = e;
+ }
+ }
+ if (_didError2) {
+ !(nextEffect !== null) ? reactProdInvariant('178') : void 0;
+ captureCommitPhaseError(nextEffect, _error2);
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
+ }
+
+ if (firstEffect !== null && rootWithPendingPassiveEffects !== null) {
+ // This commit included a passive effect. These do not need to fire until
+ // after the next paint. Schedule an callback to fire them in an async
+ // event. To ensure serial execution, the callback will be flushed early if
+ // we enter rootWithPendingPassiveEffects commit phase before then.
+ var callback = commitPassiveEffects.bind(null, root, firstEffect);
+ if (enableSchedulerTracing) {
+ // TODO: Avoid this extra callback by mutating the tracing ref directly,
+ // like we do at the beginning of commitRoot. I've opted not to do that
+ // here because that code is still in flux.
+ callback = unstable_wrap(callback);
+ }
+ passiveEffectCallbackHandle = unstable_runWithPriority(unstable_NormalPriority, function () {
+ return schedulePassiveEffects(callback);
+ });
+ passiveEffectCallback = callback;
+ }
+
+ isCommitting$1 = false;
+ isWorking = false;
+ stopCommitLifeCyclesTimer();
+ stopCommitTimer();
+ onCommitRoot(finishedWork.stateNode);
+ var updateExpirationTimeAfterCommit = finishedWork.expirationTime;
+ var childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
+ var earliestRemainingTimeAfterCommit = childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit ? childExpirationTimeAfterCommit : updateExpirationTimeAfterCommit;
+ if (earliestRemainingTimeAfterCommit === NoWork) {
+ // If there's no remaining work, we can clear the set of already failed
+ // error boundaries.
+ legacyErrorBoundariesThatAlreadyFailed = null;
+ }
+ onCommit(root, earliestRemainingTimeAfterCommit);
+
+ if (enableSchedulerTracing) {
+ __interactionsRef.current = prevInteractions;
+
+ var subscriber = void 0;
+
+ try {
+ subscriber = __subscriberRef.current;
+ if (subscriber !== null && root.memoizedInteractions.size > 0) {
+ var threadID = computeThreadID(committedExpirationTime, root.interactionThreadID);
+ subscriber.onWorkStopped(root.memoizedInteractions, threadID);
+ }
+ } catch (error) {
+ // It's not safe for commitRoot() to throw.
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ } finally {
+ // Clear completed interactions from the pending Map.
+ // Unless the render was suspended or cascading work was scheduled,
+ // In which case– leave pending interactions until the subsequent render.
+ var pendingInteractionMap = root.pendingInteractionMap;
+ pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
+ // Only decrement the pending interaction count if we're done.
+ // If there's still work at the current priority,
+ // That indicates that we are waiting for suspense data.
+ if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) {
+ pendingInteractionMap.delete(scheduledExpirationTime);
+
+ scheduledInteractions.forEach(function (interaction) {
+ interaction.__count--;
+
+ if (subscriber !== null && interaction.__count === 0) {
+ try {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ } catch (error) {
+ // It's not safe for commitRoot() to throw.
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ });
+ }
+ });
+ }
+ }
+}
+
+function resetChildExpirationTime(workInProgress, renderTime) {
+ if (renderTime !== Never && workInProgress.childExpirationTime === Never) {
+ // The children of this component are hidden. Don't bubble their
+ // expiration times.
+ return;
+ }
+
+ var newChildExpirationTime = NoWork;
+
+ // Bubble up the earliest expiration time.
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // We're in profiling mode.
+ // Let's use this same traversal to update the render durations.
+ var actualDuration = workInProgress.actualDuration;
+ var treeBaseDuration = workInProgress.selfBaseDuration;
+
+ // When a fiber is cloned, its actualDuration is reset to 0.
+ // This value will only be updated if work is done on the fiber (i.e. it doesn't bailout).
+ // When work is done, it should bubble to the parent's actualDuration.
+ // If the fiber has not been cloned though, (meaning no work was done),
+ // Then this value will reflect the amount of time spent working on a previous render.
+ // In that case it should not bubble.
+ // We determine whether it was cloned by comparing the child pointer.
+ var shouldBubbleActualDurations = workInProgress.alternate === null || workInProgress.child !== workInProgress.alternate.child;
+
+ var child = workInProgress.child;
+ while (child !== null) {
+ var childUpdateExpirationTime = child.expirationTime;
+ var childChildExpirationTime = child.childExpirationTime;
+ if (childUpdateExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = childUpdateExpirationTime;
+ }
+ if (childChildExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = childChildExpirationTime;
+ }
+ if (shouldBubbleActualDurations) {
+ actualDuration += child.actualDuration;
+ }
+ treeBaseDuration += child.treeBaseDuration;
+ child = child.sibling;
+ }
+ workInProgress.actualDuration = actualDuration;
+ workInProgress.treeBaseDuration = treeBaseDuration;
+ } else {
+ var _child = workInProgress.child;
+ while (_child !== null) {
+ var _childUpdateExpirationTime = _child.expirationTime;
+ var _childChildExpirationTime = _child.childExpirationTime;
+ if (_childUpdateExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = _childUpdateExpirationTime;
+ }
+ if (_childChildExpirationTime > newChildExpirationTime) {
+ newChildExpirationTime = _childChildExpirationTime;
+ }
+ _child = _child.sibling;
+ }
+ }
+
+ workInProgress.childExpirationTime = newChildExpirationTime;
+}
+
+function completeUnitOfWork(workInProgress) {
+ // Attempt to complete the current unit of work, then move to the
+ // next sibling. If there are no more siblings, return to the
+ // parent fiber.
+ while (true) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current$$1 = workInProgress.alternate;
+ var returnFiber = workInProgress.return;
+ var siblingFiber = workInProgress.sibling;
+
+ if ((workInProgress.effectTag & Incomplete) === NoEffect) {
+ nextUnitOfWork = workInProgress;
+ if (enableProfilerTimer) {
+ if (workInProgress.mode & ProfileMode) {
+ startProfilerTimer(workInProgress);
+ }
+ nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
+ if (workInProgress.mode & ProfileMode) {
+ // Update render duration assuming we didn't error.
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
+ }
+ } else {
+ nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
+ }
+ stopWorkTimer(workInProgress);
+ resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
+ if (nextUnitOfWork !== null) {
+ // Completing this fiber spawned new work. Work on that next.
+ return nextUnitOfWork;
+ }
+
+ if (returnFiber !== null &&
+ // Do not append effects to parents if a sibling failed to complete
+ (returnFiber.effectTag & Incomplete) === NoEffect) {
+ // Append all the effects of the subtree and this fiber onto the effect
+ // list of the parent. The completion order of the children affects the
+ // side-effect order.
+ if (returnFiber.firstEffect === null) {
+ returnFiber.firstEffect = workInProgress.firstEffect;
+ }
+ if (workInProgress.lastEffect !== null) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
+ }
+ returnFiber.lastEffect = workInProgress.lastEffect;
+ }
+
+ // If this fiber had side-effects, we append it AFTER the children's
+ // side-effects. We can perform certain side-effects earlier if
+ // needed, by doing multiple passes over the effect list. We don't want
+ // to schedule our own side-effect on our own list because if end up
+ // reusing children we'll schedule this effect onto itself since we're
+ // at the end.
+ var effectTag = workInProgress.effectTag;
+ // Skip both NoWork and PerformedWork tags when creating the effect list.
+ // PerformedWork effect is read by React DevTools but shouldn't be committed.
+ if (effectTag > PerformedWork) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress;
+ } else {
+ returnFiber.firstEffect = workInProgress;
+ }
+ returnFiber.lastEffect = workInProgress;
+ }
+ }
+
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ return siblingFiber;
+ } else if (returnFiber !== null) {
+ // If there's no more work in this returnFiber. Complete the returnFiber.
+ workInProgress = returnFiber;
+ continue;
+ } else {
+ // We've reached the root.
+ return null;
+ }
+ } else {
+ if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
+ // Record the render duration for the fiber that errored.
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
+
+ // Include the time spent working on failed children before continuing.
+ var actualDuration = workInProgress.actualDuration;
+ var child = workInProgress.child;
+ while (child !== null) {
+ actualDuration += child.actualDuration;
+ child = child.sibling;
+ }
+ workInProgress.actualDuration = actualDuration;
+ }
+
+ // This fiber did not complete because something threw. Pop values off
+ // the stack without entering the complete phase. If this is a boundary,
+ // capture values if possible.
+ var next = unwindWork(workInProgress, nextRenderExpirationTime);
+ // Because this fiber did not complete, don't reset its expiration time.
+ if (workInProgress.effectTag & DidCapture) {
+ // Restarting an error boundary
+ stopFailedWorkTimer(workInProgress);
+ } else {
+ stopWorkTimer(workInProgress);
+ }
+
+ if (next !== null) {
+ stopWorkTimer(workInProgress);
+ next.effectTag &= HostEffectMask;
+ return next;
+ }
+
+ if (returnFiber !== null) {
+ // Mark the parent fiber as incomplete and clear its effect list.
+ returnFiber.firstEffect = returnFiber.lastEffect = null;
+ returnFiber.effectTag |= Incomplete;
+ }
+
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ return siblingFiber;
+ } else if (returnFiber !== null) {
+ // If there's no more work in this returnFiber. Complete the returnFiber.
+ workInProgress = returnFiber;
+ continue;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ // Without this explicit null return Flow complains of invalid return type
+ // TODO Remove the above while(true) loop
+ // eslint-disable-next-line no-unreachable
+ return null;
+}
+
+function performUnitOfWork(workInProgress) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current$$1 = workInProgress.alternate;
+
+ // See if beginning this work spawns more work.
+ startWorkTimer(workInProgress);
+ var next = void 0;
+ if (enableProfilerTimer) {
+ if (workInProgress.mode & ProfileMode) {
+ startProfilerTimer(workInProgress);
+ }
+
+ next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
+ workInProgress.memoizedProps = workInProgress.pendingProps;
+
+ if (workInProgress.mode & ProfileMode) {
+ // Record the render duration assuming we didn't bailout (or error).
+ stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
+ }
+ } else {
+ next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
+ workInProgress.memoizedProps = workInProgress.pendingProps;
+ }
+
+ if (next === null) {
+ // If this doesn't spawn new work, complete the current work.
+ next = completeUnitOfWork(workInProgress);
+ }
+
+ ReactCurrentOwner$1.current = null;
+
+ return next;
+}
+
+function workLoop(isYieldy) {
+ if (!isYieldy) {
+ // Flush work without yielding
+ while (nextUnitOfWork !== null) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ }
+ } else {
+ // Flush asynchronous work until there's a higher priority event
+ while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ }
+ }
+}
+
+function renderRoot(root, isYieldy) {
+ !!isWorking ? reactProdInvariant('243') : void 0;
+
+ flushPassiveEffects$1();
+
+ isWorking = true;
+ var previousDispatcher = ReactCurrentDispatcher.current;
+ ReactCurrentDispatcher.current = ContextOnlyDispatcher;
+
+ var expirationTime = root.nextExpirationTimeToWorkOn;
+
+ // Check if we're starting from a fresh stack, or if we're resuming from
+ // previously yielded work.
+ if (expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null) {
+ // Reset the stack and start working from the root.
+ resetStack();
+ nextRoot = root;
+ nextRenderExpirationTime = expirationTime;
+ nextUnitOfWork = createWorkInProgress(nextRoot.current, null, nextRenderExpirationTime);
+ root.pendingCommitExpirationTime = NoWork;
+
+ if (enableSchedulerTracing) {
+ // Determine which interactions this batch of work currently includes,
+ // So that we can accurately attribute time spent working on it,
+ var interactions = new Set();
+ root.pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
+ if (scheduledExpirationTime >= expirationTime) {
+ scheduledInteractions.forEach(function (interaction) {
+ return interactions.add(interaction);
+ });
+ }
+ });
+
+ // Store the current set of interactions on the FiberRoot for a few reasons:
+ // We can re-use it in hot functions like renderRoot() without having to recalculate it.
+ // We will also use it in commitWork() to pass to any Profiler onRender() hooks.
+ // This also provides DevTools with a way to access it when the onCommitRoot() hook is called.
+ root.memoizedInteractions = interactions;
+
+ if (interactions.size > 0) {
+ var subscriber = __subscriberRef.current;
+ if (subscriber !== null) {
+ var threadID = computeThreadID(expirationTime, root.interactionThreadID);
+ try {
+ subscriber.onWorkStarted(interactions, threadID);
+ } catch (error) {
+ // Work thrown by an interaction tracing subscriber should be rethrown,
+ // But only once it's safe (to avoid leaving the scheduler in an invalid state).
+ // Store the error for now and we'll re-throw in finishRendering().
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ var prevInteractions = null;
+ if (enableSchedulerTracing) {
+ // We're about to start new traced work.
+ // Restore pending interactions so cascading work triggered during the render phase will be accounted for.
+ prevInteractions = __interactionsRef.current;
+ __interactionsRef.current = root.memoizedInteractions;
+ }
+
+ var didFatal = false;
+
+ startWorkLoopTimer(nextUnitOfWork);
+
+ do {
+ try {
+ workLoop(isYieldy);
+ } catch (thrownValue) {
+ resetContextDependences();
+ resetHooks();
+
+ // Reset in case completion throws.
+ // This is only used in DEV and when replaying is on.
+ if (nextUnitOfWork === null) {
+ // This is a fatal error.
+ didFatal = true;
+ onUncaughtError(thrownValue);
+ } else {
+ if (enableProfilerTimer && nextUnitOfWork.mode & ProfileMode) {
+ // Record the time spent rendering before an error was thrown.
+ // This avoids inaccurate Profiler durations in the case of a suspended render.
+ stopProfilerTimerIfRunningAndRecordDelta(nextUnitOfWork, true);
+ }
+
+ !(nextUnitOfWork !== null) ? reactProdInvariant('271') : void 0;
+
+ var sourceFiber = nextUnitOfWork;
+ var returnFiber = sourceFiber.return;
+ if (returnFiber === null) {
+ // This is the root. The root could capture its own errors. However,
+ // we don't know if it errors before or after we pushed the host
+ // context. This information is needed to avoid a stack mismatch.
+ // Because we're not sure, treat this as a fatal error. We could track
+ // which phase it fails in, but doesn't seem worth it. At least
+ // for now.
+ didFatal = true;
+ onUncaughtError(thrownValue);
+ } else {
+ throwException(root, returnFiber, sourceFiber, thrownValue, nextRenderExpirationTime);
+ nextUnitOfWork = completeUnitOfWork(sourceFiber);
+ continue;
+ }
+ }
+ }
+ break;
+ } while (true);
+
+ if (enableSchedulerTracing) {
+ // Traced work is done for now; restore the previous interactions.
+ __interactionsRef.current = prevInteractions;
+ }
+
+ // We're done performing work. Time to clean up.
+ isWorking = false;
+ ReactCurrentDispatcher.current = previousDispatcher;
+ resetContextDependences();
+ resetHooks();
+
+ // Yield back to main thread.
+ if (didFatal) {
+ var _didCompleteRoot = false;
+ stopWorkLoopTimer(interruptedBy, _didCompleteRoot);
+ interruptedBy = null;
+ // There was a fatal error.
+ nextRoot = null;
+ onFatal(root);
+ return;
+ }
+
+ if (nextUnitOfWork !== null) {
+ // There's still remaining async work in this tree, but we ran out of time
+ // in the current frame. Yield back to the renderer. Unless we're
+ // interrupted by a higher priority update, we'll continue later from where
+ // we left off.
+ var _didCompleteRoot2 = false;
+ stopWorkLoopTimer(interruptedBy, _didCompleteRoot2);
+ interruptedBy = null;
+ onYield(root);
+ return;
+ }
+
+ // We completed the whole tree.
+ var didCompleteRoot = true;
+ stopWorkLoopTimer(interruptedBy, didCompleteRoot);
+ var rootWorkInProgress = root.current.alternate;
+ !(rootWorkInProgress !== null) ? reactProdInvariant('281') : void 0;
+
+ // `nextRoot` points to the in-progress root. A non-null value indicates
+ // that we're in the middle of an async render. Set it to null to indicate
+ // there's no more work to be done in the current batch.
+ nextRoot = null;
+ interruptedBy = null;
+
+ if (nextRenderDidError) {
+ // There was an error
+ if (hasLowerPriorityWork(root, expirationTime)) {
+ // There's lower priority work. If so, it may have the effect of fixing
+ // the exception that was just thrown. Exit without committing. This is
+ // similar to a suspend, but without a timeout because we're not waiting
+ // for a promise to resolve. React will restart at the lower
+ // priority level.
+ markSuspendedPriorityLevel(root, expirationTime);
+ var suspendedExpirationTime = expirationTime;
+ var rootExpirationTime = root.expirationTime;
+ onSuspend(root, rootWorkInProgress, suspendedExpirationTime, rootExpirationTime, -1 // Indicates no timeout
+ );
+ return;
+ } else if (
+ // There's no lower priority work, but we're rendering asynchronously.
+ // Synchronously attempt to render the same level one more time. This is
+ // similar to a suspend, but without a timeout because we're not waiting
+ // for a promise to resolve.
+ !root.didError && isYieldy) {
+ root.didError = true;
+ var _suspendedExpirationTime = root.nextExpirationTimeToWorkOn = expirationTime;
+ var _rootExpirationTime = root.expirationTime = Sync;
+ onSuspend(root, rootWorkInProgress, _suspendedExpirationTime, _rootExpirationTime, -1 // Indicates no timeout
+ );
+ return;
+ }
+ }
+
+ if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {
+ // The tree was suspended.
+ var _suspendedExpirationTime2 = expirationTime;
+ markSuspendedPriorityLevel(root, _suspendedExpirationTime2);
+
+ // Find the earliest uncommitted expiration time in the tree, including
+ // work that is suspended. The timeout threshold cannot be longer than
+ // the overall expiration.
+ var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, expirationTime);
+ var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
+ if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {
+ nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;
+ }
+
+ // Subtract the current time from the absolute timeout to get the number
+ // of milliseconds until the timeout. In other words, convert an absolute
+ // timestamp to a relative time. This is the value that is passed
+ // to `setTimeout`.
+ var currentTimeMs = expirationTimeToMs(requestCurrentTime());
+ var msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;
+ msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;
+
+ // TODO: Account for the Just Noticeable Difference
+
+ var _rootExpirationTime2 = root.expirationTime;
+ onSuspend(root, rootWorkInProgress, _suspendedExpirationTime2, _rootExpirationTime2, msUntilTimeout);
+ return;
+ }
+
+ // Ready to commit.
+ onComplete(root, rootWorkInProgress, expirationTime);
+}
+
+function captureCommitPhaseError(sourceFiber, value) {
+ var expirationTime = Sync;
+ var fiber = sourceFiber.return;
+ while (fiber !== null) {
+ switch (fiber.tag) {
+ case ClassComponent:
+ var ctor = fiber.type;
+ var instance = fiber.stateNode;
+ if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {
+ var errorInfo = createCapturedValue(value, sourceFiber);
+ var update = createClassErrorUpdate(fiber, errorInfo, expirationTime);
+ enqueueUpdate(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ return;
+ }
+ break;
+ case HostRoot:
+ {
+ var _errorInfo = createCapturedValue(value, sourceFiber);
+ var _update = createRootErrorUpdate(fiber, _errorInfo, expirationTime);
+ enqueueUpdate(fiber, _update);
+ scheduleWork(fiber, expirationTime);
+ return;
+ }
+ }
+ fiber = fiber.return;
+ }
+
+ if (sourceFiber.tag === HostRoot) {
+ // Error was thrown at the root. There is no parent, so the root
+ // itself should capture it.
+ var rootFiber = sourceFiber;
+ var _errorInfo2 = createCapturedValue(value, rootFiber);
+ var _update2 = createRootErrorUpdate(rootFiber, _errorInfo2, expirationTime);
+ enqueueUpdate(rootFiber, _update2);
+ scheduleWork(rootFiber, expirationTime);
+ }
+}
+
+function computeThreadID(expirationTime, interactionThreadID) {
+ // Interaction threads are unique per root and expiration time.
+ return expirationTime * 1000 + interactionThreadID;
+}
+
+function computeExpirationForFiber(currentTime, fiber) {
+ var priorityLevel = unstable_getCurrentPriorityLevel();
+
+ var expirationTime = void 0;
+ if ((fiber.mode & ConcurrentMode) === NoContext) {
+ // Outside of concurrent mode, updates are always synchronous.
+ expirationTime = Sync;
+ } else if (isWorking && !isCommitting$1) {
+ // During render phase, updates expire during as the current render.
+ expirationTime = nextRenderExpirationTime;
+ } else {
+ switch (priorityLevel) {
+ case unstable_ImmediatePriority:
+ expirationTime = Sync;
+ break;
+ case unstable_UserBlockingPriority:
+ expirationTime = computeInteractiveExpiration(currentTime);
+ break;
+ case unstable_NormalPriority:
+ // This is a normal, concurrent update
+ expirationTime = computeAsyncExpiration(currentTime);
+ break;
+ case unstable_LowPriority:
+ case unstable_IdlePriority:
+ expirationTime = Never;
+ break;
+ default:
+ reactProdInvariant('313');
+ }
+
+ // If we're in the middle of rendering a tree, do not update at the same
+ // expiration time that is already rendering.
+ if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
+ expirationTime -= 1;
+ }
+ }
+
+ // Keep track of the lowest pending interactive expiration time. This
+ // allows us to synchronously flush all interactive updates
+ // when needed.
+ // TODO: Move this to renderer?
+ return expirationTime;
+}
+
+function renderDidSuspend(root, absoluteTimeoutMs, suspendedTime) {
+ // Schedule the timeout.
+ if (absoluteTimeoutMs >= 0 && nextLatestAbsoluteTimeoutMs < absoluteTimeoutMs) {
+ nextLatestAbsoluteTimeoutMs = absoluteTimeoutMs;
+ }
+}
+
+function renderDidError() {
+ nextRenderDidError = true;
+}
+
+function pingSuspendedRoot(root, thenable, pingTime) {
+ // A promise that previously suspended React from committing has resolved.
+ // If React is still suspended, try again at the previous level (pingTime).
+
+ var pingCache = root.pingCache;
+ if (pingCache !== null) {
+ // The thenable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ pingCache.delete(thenable);
+ }
+
+ if (nextRoot !== null && nextRenderExpirationTime === pingTime) {
+ // Received a ping at the same priority level at which we're currently
+ // rendering. Restart from the root.
+ nextRoot = null;
+ } else {
+ // Confirm that the root is still suspended at this level. Otherwise exit.
+ if (isPriorityLevelSuspended(root, pingTime)) {
+ // Ping at the original level
+ markPingedPriorityLevel(root, pingTime);
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ }
+ }
+}
+
+function retryTimedOutBoundary(boundaryFiber, thenable) {
+ // The boundary fiber (a Suspense component) previously timed out and was
+ // rendered in its fallback state. One of the promises that suspended it has
+ // resolved, which means at least part of the tree was likely unblocked. Try
+ var retryCache = void 0;
+ if (enableSuspenseServerRenderer) {
+ switch (boundaryFiber.tag) {
+ case SuspenseComponent:
+ retryCache = boundaryFiber.stateNode;
+ break;
+ case DehydratedSuspenseComponent:
+ retryCache = boundaryFiber.memoizedState;
+ break;
+ default:
+ reactProdInvariant('314');
+ }
+ } else {
+ retryCache = boundaryFiber.stateNode;
+ }
+ if (retryCache !== null) {
+ // The thenable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ retryCache.delete(thenable);
+ }
+
+ var currentTime = requestCurrentTime();
+ var retryTime = computeExpirationForFiber(currentTime, boundaryFiber);
+ var root = scheduleWorkToRoot(boundaryFiber, retryTime);
+ if (root !== null) {
+ markPendingPriorityLevel(root, retryTime);
+ var rootExpirationTime = root.expirationTime;
+ if (rootExpirationTime !== NoWork) {
+ requestWork(root, rootExpirationTime);
+ }
+ }
+}
+
+function scheduleWorkToRoot(fiber, expirationTime) {
+ recordScheduleUpdate();
+
+ if (fiber.expirationTime < expirationTime) {
+ fiber.expirationTime = expirationTime;
+ }
+ var alternate = fiber.alternate;
+ if (alternate !== null && alternate.expirationTime < expirationTime) {
+ alternate.expirationTime = expirationTime;
+ }
+ // Walk the parent path to the root and update the child expiration time.
+ var node = fiber.return;
+ var root = null;
+ if (node === null && fiber.tag === HostRoot) {
+ root = fiber.stateNode;
+ } else {
+ while (node !== null) {
+ alternate = node.alternate;
+ if (node.childExpirationTime < expirationTime) {
+ node.childExpirationTime = expirationTime;
+ if (alternate !== null && alternate.childExpirationTime < expirationTime) {
+ alternate.childExpirationTime = expirationTime;
+ }
+ } else if (alternate !== null && alternate.childExpirationTime < expirationTime) {
+ alternate.childExpirationTime = expirationTime;
+ }
+ if (node.return === null && node.tag === HostRoot) {
+ root = node.stateNode;
+ break;
+ }
+ node = node.return;
+ }
+ }
+
+ if (enableSchedulerTracing) {
+ if (root !== null) {
+ var interactions = __interactionsRef.current;
+ if (interactions.size > 0) {
+ var pendingInteractionMap = root.pendingInteractionMap;
+ var pendingInteractions = pendingInteractionMap.get(expirationTime);
+ if (pendingInteractions != null) {
+ interactions.forEach(function (interaction) {
+ if (!pendingInteractions.has(interaction)) {
+ // Update the pending async work count for previously unscheduled interaction.
+ interaction.__count++;
+ }
+
+ pendingInteractions.add(interaction);
+ });
+ } else {
+ pendingInteractionMap.set(expirationTime, new Set(interactions));
+
+ // Update the pending async work count for the current interactions.
+ interactions.forEach(function (interaction) {
+ interaction.__count++;
+ });
+ }
+
+ var subscriber = __subscriberRef.current;
+ if (subscriber !== null) {
+ var threadID = computeThreadID(expirationTime, root.interactionThreadID);
+ subscriber.onWorkScheduled(interactions, threadID);
+ }
+ }
+ }
+ }
+ return root;
+}
+
+
+
+function scheduleWork(fiber, expirationTime) {
+ var root = scheduleWorkToRoot(fiber, expirationTime);
+ if (root === null) {
+ return;
+ }
+
+ if (!isWorking && nextRenderExpirationTime !== NoWork && expirationTime > nextRenderExpirationTime) {
+ // This is an interruption. (Used for performance tracking.)
+ interruptedBy = fiber;
+ resetStack();
+ }
+ markPendingPriorityLevel(root, expirationTime);
+ if (
+ // If we're in the render phase, we don't need to schedule this root
+ // for an update, because we'll do it before we exit...
+ !isWorking || isCommitting$1 ||
+ // ...unless this is a different root than the one we're rendering.
+ nextRoot !== root) {
+ var rootExpirationTime = root.expirationTime;
+ requestWork(root, rootExpirationTime);
+ }
+ if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
+ // Reset this back to zero so subsequent updates don't throw.
+ nestedUpdateCount = 0;
+ reactProdInvariant('185');
+ }
+}
+
+function syncUpdates(fn, a, b, c, d) {
+ return unstable_runWithPriority(unstable_ImmediatePriority, function () {
+ return fn(a, b, c, d);
+ });
+}
+
+// TODO: Everything below this is written as if it has been lifted to the
+// renderers. I'll do this in a follow-up.
+
+// Linked-list of roots
+var firstScheduledRoot = null;
+var lastScheduledRoot = null;
+
+var callbackExpirationTime = NoWork;
+var callbackID = void 0;
+var isRendering = false;
+var nextFlushedRoot = null;
+var nextFlushedExpirationTime = NoWork;
+var hasUnhandledError = false;
+var unhandledError = null;
+
+var isBatchingUpdates = false;
+var isUnbatchingUpdates = false;
+
+var completedBatches = null;
+
+var originalStartTimeMs = now();
+var currentRendererTime = msToExpirationTime(originalStartTimeMs);
+var currentSchedulerTime = currentRendererTime;
+
+// Use these to prevent an infinite loop of nested updates
+var NESTED_UPDATE_LIMIT = 50;
+var nestedUpdateCount = 0;
+var lastCommittedRootDuringThisBatch = null;
+
+function recomputeCurrentRendererTime() {
+ var currentTimeMs = now() - originalStartTimeMs;
+ currentRendererTime = msToExpirationTime(currentTimeMs);
+}
+
+function scheduleCallbackWithExpirationTime(root, expirationTime) {
+ if (callbackExpirationTime !== NoWork) {
+ // A callback is already scheduled. Check its expiration time (timeout).
+ if (expirationTime < callbackExpirationTime) {
+ // Existing callback has sufficient timeout. Exit.
+ return;
+ } else {
+ if (callbackID !== null) {
+ // Existing callback has insufficient timeout. Cancel and schedule a
+ // new one.
+ cancelDeferredCallback$$1(callbackID);
+ }
+ }
+ // The request callback timer is already running. Don't start a new one.
+ } else {
+ startRequestCallbackTimer();
+ }
+
+ callbackExpirationTime = expirationTime;
+ var currentMs = now() - originalStartTimeMs;
+ var expirationTimeMs = expirationTimeToMs(expirationTime);
+ var timeout = expirationTimeMs - currentMs;
+ callbackID = scheduleDeferredCallback$$1(performAsyncWork, { timeout: timeout });
+}
+
+// For every call to renderRoot, one of onFatal, onComplete, onSuspend, and
+// onYield is called upon exiting. We use these in lieu of returning a tuple.
+// I've also chosen not to inline them into renderRoot because these will
+// eventually be lifted into the renderer.
+function onFatal(root) {
+ root.finishedWork = null;
+}
+
+function onComplete(root, finishedWork, expirationTime) {
+ root.pendingCommitExpirationTime = expirationTime;
+ root.finishedWork = finishedWork;
+}
+
+function onSuspend(root, finishedWork, suspendedExpirationTime, rootExpirationTime, msUntilTimeout) {
+ root.expirationTime = rootExpirationTime;
+ if (msUntilTimeout === 0 && !shouldYieldToRenderer()) {
+ // Don't wait an additional tick. Commit the tree immediately.
+ root.pendingCommitExpirationTime = suspendedExpirationTime;
+ root.finishedWork = finishedWork;
+ } else if (msUntilTimeout > 0) {
+ // Wait `msUntilTimeout` milliseconds before committing.
+ root.timeoutHandle = scheduleTimeout(onTimeout.bind(null, root, finishedWork, suspendedExpirationTime), msUntilTimeout);
+ }
+}
+
+function onYield(root) {
+ root.finishedWork = null;
+}
+
+function onTimeout(root, finishedWork, suspendedExpirationTime) {
+ // The root timed out. Commit it.
+ root.pendingCommitExpirationTime = suspendedExpirationTime;
+ root.finishedWork = finishedWork;
+ // Read the current time before entering the commit phase. We can be
+ // certain this won't cause tearing related to batching of event updates
+ // because we're at the top of a timer event.
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ flushRoot(root, suspendedExpirationTime);
+}
+
+function onCommit(root, expirationTime) {
+ root.expirationTime = expirationTime;
+ root.finishedWork = null;
+}
+
+function requestCurrentTime() {
+ // requestCurrentTime is called by the scheduler to compute an expiration
+ // time.
+ //
+ // Expiration times are computed by adding to the current time (the start
+ // time). However, if two updates are scheduled within the same event, we
+ // should treat their start times as simultaneous, even if the actual clock
+ // time has advanced between the first and second call.
+
+ // In other words, because expiration times determine how updates are batched,
+ // we want all updates of like priority that occur within the same event to
+ // receive the same expiration time. Otherwise we get tearing.
+ //
+ // We keep track of two separate times: the current "renderer" time and the
+ // current "scheduler" time. The renderer time can be updated whenever; it
+ // only exists to minimize the calls performance.now.
+ //
+ // But the scheduler time can only be updated if there's no pending work, or
+ // if we know for certain that we're not in the middle of an event.
+
+ if (isRendering) {
+ // We're already rendering. Return the most recently read time.
+ return currentSchedulerTime;
+ }
+ // Check if there's pending work.
+ findHighestPriorityRoot();
+ if (nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never) {
+ // If there's no pending work, or if the pending work is offscreen, we can
+ // read the current time without risk of tearing.
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ return currentSchedulerTime;
+ }
+ // There's already pending work. We might be in the middle of a browser
+ // event. If we were to read the current time, it could cause multiple updates
+ // within the same event to receive different expiration times, leading to
+ // tearing. Return the last read time. During the next idle callback, the
+ // time will be updated.
+ return currentSchedulerTime;
+}
+
+// requestWork is called by the scheduler whenever a root receives an update.
+// It's up to the renderer to call renderRoot at some point in the future.
+function requestWork(root, expirationTime) {
+ addRootToSchedule(root, expirationTime);
+ if (isRendering) {
+ // Prevent reentrancy. Remaining work will be scheduled at the end of
+ // the currently rendering batch.
+ return;
+ }
+
+ if (isBatchingUpdates) {
+ // Flush work at the end of the batch.
+ if (isUnbatchingUpdates) {
+ // ...unless we're inside unbatchedUpdates, in which case we should
+ // flush it now.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = Sync;
+ performWorkOnRoot(root, Sync, false);
+ }
+ return;
+ }
+
+ // TODO: Get rid of Sync and use current time?
+ if (expirationTime === Sync) {
+ performSyncWork();
+ } else {
+ scheduleCallbackWithExpirationTime(root, expirationTime);
+ }
+}
+
+function addRootToSchedule(root, expirationTime) {
+ // Add the root to the schedule.
+ // Check if this root is already part of the schedule.
+ if (root.nextScheduledRoot === null) {
+ // This root is not already scheduled. Add it.
+ root.expirationTime = expirationTime;
+ if (lastScheduledRoot === null) {
+ firstScheduledRoot = lastScheduledRoot = root;
+ root.nextScheduledRoot = root;
+ } else {
+ lastScheduledRoot.nextScheduledRoot = root;
+ lastScheduledRoot = root;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ }
+ } else {
+ // This root is already scheduled, but its priority may have increased.
+ var remainingExpirationTime = root.expirationTime;
+ if (expirationTime > remainingExpirationTime) {
+ // Update the priority.
+ root.expirationTime = expirationTime;
+ }
+ }
+}
+
+function findHighestPriorityRoot() {
+ var highestPriorityWork = NoWork;
+ var highestPriorityRoot = null;
+ if (lastScheduledRoot !== null) {
+ var previousScheduledRoot = lastScheduledRoot;
+ var root = firstScheduledRoot;
+ while (root !== null) {
+ var remainingExpirationTime = root.expirationTime;
+ if (remainingExpirationTime === NoWork) {
+ // This root no longer has work. Remove it from the scheduler.
+
+ // TODO: This check is redudant, but Flow is confused by the branch
+ // below where we set lastScheduledRoot to null, even though we break
+ // from the loop right after.
+ !(previousScheduledRoot !== null && lastScheduledRoot !== null) ? reactProdInvariant('244') : void 0;
+ if (root === root.nextScheduledRoot) {
+ // This is the only root in the list.
+ root.nextScheduledRoot = null;
+ firstScheduledRoot = lastScheduledRoot = null;
+ break;
+ } else if (root === firstScheduledRoot) {
+ // This is the first root in the list.
+ var next = root.nextScheduledRoot;
+ firstScheduledRoot = next;
+ lastScheduledRoot.nextScheduledRoot = next;
+ root.nextScheduledRoot = null;
+ } else if (root === lastScheduledRoot) {
+ // This is the last root in the list.
+ lastScheduledRoot = previousScheduledRoot;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ root.nextScheduledRoot = null;
+ break;
+ } else {
+ previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
+ root.nextScheduledRoot = null;
+ }
+ root = previousScheduledRoot.nextScheduledRoot;
+ } else {
+ if (remainingExpirationTime > highestPriorityWork) {
+ // Update the priority, if it's higher
+ highestPriorityWork = remainingExpirationTime;
+ highestPriorityRoot = root;
+ }
+ if (root === lastScheduledRoot) {
+ break;
+ }
+ if (highestPriorityWork === Sync) {
+ // Sync is highest priority by definition so
+ // we can stop searching.
+ break;
+ }
+ previousScheduledRoot = root;
+ root = root.nextScheduledRoot;
+ }
+ }
+ }
+
+ nextFlushedRoot = highestPriorityRoot;
+ nextFlushedExpirationTime = highestPriorityWork;
+}
+
+// TODO: This wrapper exists because many of the older tests (the ones that use
+// flushDeferredPri) rely on the number of times `shouldYield` is called. We
+// should get rid of it.
+var didYield = false;
+function shouldYieldToRenderer() {
+ if (didYield) {
+ return true;
+ }
+ if (shouldYield$$1()) {
+ didYield = true;
+ return true;
+ }
+ return false;
+}
+
+function performAsyncWork() {
+ try {
+ if (!shouldYieldToRenderer()) {
+ // The callback timed out. That means at least one update has expired.
+ // Iterate through the root schedule. If they contain expired work, set
+ // the next render expiration time to the current time. This has the effect
+ // of flushing all expired work in a single batch, instead of flushing each
+ // level one at a time.
+ if (firstScheduledRoot !== null) {
+ recomputeCurrentRendererTime();
+ var root = firstScheduledRoot;
+ do {
+ didExpireAtExpirationTime(root, currentRendererTime);
+ // The root schedule is circular, so this is never null.
+ root = root.nextScheduledRoot;
+ } while (root !== firstScheduledRoot);
+ }
+ }
+ performWork(NoWork, true);
+ } finally {
+ didYield = false;
+ }
+}
+
+function performSyncWork() {
+ performWork(Sync, false);
+}
+
+function performWork(minExpirationTime, isYieldy) {
+ // Keep working on roots until there's no more work, or until there's a higher
+ // priority event.
+ findHighestPriorityRoot();
+
+ if (isYieldy) {
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+
+ if (enableUserTimingAPI) {
+ var didExpire = nextFlushedExpirationTime > currentRendererTime;
+ var timeout = expirationTimeToMs(nextFlushedExpirationTime);
+ stopRequestCallbackTimer(didExpire, timeout);
+ }
+
+ while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime && !(didYield && currentRendererTime > nextFlushedExpirationTime)) {
+ performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, currentRendererTime > nextFlushedExpirationTime);
+ findHighestPriorityRoot();
+ recomputeCurrentRendererTime();
+ currentSchedulerTime = currentRendererTime;
+ }
+ } else {
+ while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime) {
+ performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
+ findHighestPriorityRoot();
+ }
+ }
+
+ // We're done flushing work. Either we ran out of time in this callback,
+ // or there's no more work left with sufficient priority.
+
+ // If we're inside a callback, set this to false since we just completed it.
+ if (isYieldy) {
+ callbackExpirationTime = NoWork;
+ callbackID = null;
+ }
+ // If there's work left over, schedule a new callback.
+ if (nextFlushedExpirationTime !== NoWork) {
+ scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);
+ }
+
+ // Clean-up.
+ finishRendering();
+}
+
+function flushRoot(root, expirationTime) {
+ !!isRendering ? reactProdInvariant('253') : void 0;
+ // Perform work on root as if the given expiration time is the current time.
+ // This has the effect of synchronously flushing all work up to and
+ // including the given time.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = expirationTime;
+ performWorkOnRoot(root, expirationTime, false);
+ // Flush any sync work that was scheduled by lifecycles
+ performSyncWork();
+}
+
+function finishRendering() {
+ nestedUpdateCount = 0;
+ lastCommittedRootDuringThisBatch = null;
+
+ if (completedBatches !== null) {
+ var batches = completedBatches;
+ completedBatches = null;
+ for (var i = 0; i < batches.length; i++) {
+ var batch = batches[i];
+ try {
+ batch._onComplete();
+ } catch (error) {
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ }
+
+ if (hasUnhandledError) {
+ var error = unhandledError;
+ unhandledError = null;
+ hasUnhandledError = false;
+ throw error;
+ }
+}
+
+function performWorkOnRoot(root, expirationTime, isYieldy) {
+ !!isRendering ? reactProdInvariant('245') : void 0;
+
+ isRendering = true;
+
+ // Check if this is async work or sync/expired work.
+ if (!isYieldy) {
+ // Flush work without yielding.
+ // TODO: Non-yieldy work does not necessarily imply expired work. A renderer
+ // may want to perform some work without yielding, but also without
+ // requiring the root to complete (by triggering placeholders).
+
+ var finishedWork = root.finishedWork;
+ if (finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ // If this root previously suspended, clear its existing timeout, since
+ // we're about to try rendering again.
+ var timeoutHandle = root.timeoutHandle;
+ if (timeoutHandle !== noTimeout) {
+ root.timeoutHandle = noTimeout;
+ // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
+ cancelTimeout(timeoutHandle);
+ }
+ renderRoot(root, isYieldy);
+ finishedWork = root.finishedWork;
+ if (finishedWork !== null) {
+ // We've completed the root. Commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ }
+ }
+ } else {
+ // Flush async work.
+ var _finishedWork = root.finishedWork;
+ if (_finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ // If this root previously suspended, clear its existing timeout, since
+ // we're about to try rendering again.
+ var _timeoutHandle = root.timeoutHandle;
+ if (_timeoutHandle !== noTimeout) {
+ root.timeoutHandle = noTimeout;
+ // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
+ cancelTimeout(_timeoutHandle);
+ }
+ renderRoot(root, isYieldy);
+ _finishedWork = root.finishedWork;
+ if (_finishedWork !== null) {
+ // We've completed the root. Check the if we should yield one more time
+ // before committing.
+ if (!shouldYieldToRenderer()) {
+ // Still time left. Commit the root.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ // There's no time left. Mark this root as complete. We'll come
+ // back and commit it later.
+ root.finishedWork = _finishedWork;
+ }
+ }
+ }
+ }
+
+ isRendering = false;
+}
+
+function completeRoot(root, finishedWork, expirationTime) {
+ // Check if there's a batch that matches this expiration time.
+ var firstBatch = root.firstBatch;
+ if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
+ if (completedBatches === null) {
+ completedBatches = [firstBatch];
+ } else {
+ completedBatches.push(firstBatch);
+ }
+ if (firstBatch._defer) {
+ // This root is blocked from committing by a batch. Unschedule it until
+ // we receive another update.
+ root.finishedWork = finishedWork;
+ root.expirationTime = NoWork;
+ return;
+ }
+ }
+
+ // Commit the root.
+ root.finishedWork = null;
+
+ // Check if this is a nested update (a sync update scheduled during the
+ // commit phase).
+ if (root === lastCommittedRootDuringThisBatch) {
+ // If the next root is the same as the previous root, this is a nested
+ // update. To prevent an infinite loop, increment the nested update count.
+ nestedUpdateCount++;
+ } else {
+ // Reset whenever we switch roots.
+ lastCommittedRootDuringThisBatch = root;
+ nestedUpdateCount = 0;
+ }
+ unstable_runWithPriority(unstable_ImmediatePriority, function () {
+ commitRoot(root, finishedWork);
+ });
+}
+
+function onUncaughtError(error) {
+ !(nextFlushedRoot !== null) ? reactProdInvariant('246') : void 0;
+ // Unschedule this root so we don't work on it again until there's
+ // another update.
+ nextFlushedRoot.expirationTime = NoWork;
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+}
+
+// TODO: Batching should be implemented at the renderer level, not inside
+// the reconciler.
+function batchedUpdates(fn, a) {
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return fn(a);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performSyncWork();
+ }
+ }
+}
+
+// TODO: Batching should be implemented at the renderer level, not within
+// the reconciler.
+function flushSync(fn, a) {
+ !!isRendering ? reactProdInvariant('187') : void 0;
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return syncUpdates(fn, a);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ performSyncWork();
+ }
+}
+
+function getContextForSubtree(parentComponent) {
+ if (!parentComponent) {
+ return emptyContextObject;
+ }
+
+ var fiber = get(parentComponent);
+ var parentContext = findCurrentUnmaskedContext(fiber);
+
+ if (fiber.tag === ClassComponent) {
+ var Component = fiber.type;
+ if (isContextProvider(Component)) {
+ return processChildContext(fiber, Component, parentContext);
+ }
+ }
+
+ return parentContext;
+}
+
+function scheduleRootUpdate(current$$1, element, expirationTime, callback) {
+ var update = createUpdate(expirationTime);
+ // Caution: React DevTools currently depends on this property
+ // being called "element".
+ update.payload = { element: element };
+
+ callback = callback === undefined ? null : callback;
+ if (callback !== null) {
+ update.callback = callback;
+ }
+
+ flushPassiveEffects$1();
+ enqueueUpdate(current$$1, update);
+ scheduleWork(current$$1, expirationTime);
+
+ return expirationTime;
+}
+
+function updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback) {
+ // TODO: If this is a nested container, this won't be the root.
+ var current$$1 = container.current;
+
+ var context = getContextForSubtree(parentComponent);
+ if (container.context === null) {
+ container.context = context;
+ } else {
+ container.pendingContext = context;
+ }
+
+ return scheduleRootUpdate(current$$1, element, expirationTime, callback);
+}
+
+function createContainer(containerInfo, isConcurrent, hydrate) {
+ return createFiberRoot(containerInfo, isConcurrent, hydrate);
+}
+
+function updateContainer(element, container, parentComponent, callback) {
+ var current$$1 = container.current;
+ var currentTime = requestCurrentTime();
+ var expirationTime = computeExpirationForFiber(currentTime, current$$1);
+ return updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback);
+}
+
+function getPublicRootInstance(container) {
+ var containerFiber = container.current;
+ if (!containerFiber.child) {
+ return null;
+ }
+ switch (containerFiber.child.tag) {
+ case HostComponent:
+ return getPublicInstance(containerFiber.child.stateNode);
+ default:
+ return containerFiber.child.stateNode;
+ }
+}
+
+
+
+var overrideProps = null;
+
+function injectIntoDevTools(devToolsConfig) {
+ var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
+ var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+
+
+ return injectInternals(_assign({}, devToolsConfig, {
+ overrideProps: overrideProps,
+ currentDispatcherRef: ReactCurrentDispatcher,
+ findHostInstanceByFiber: function (fiber) {
+ var hostFiber = findCurrentHostFiber(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+ },
+ findFiberByHostInstance: function (instance) {
+ if (!findFiberByHostInstance) {
+ // Might not be implemented by the renderer.
+ return null;
+ }
+ return findFiberByHostInstance(instance);
+ }
+ }));
+}
+
+// This file intentionally does *not* have the Flow annotation.
+// Don't add it. See `./inline-typed.js` for an explanation.
+
+// TODO: this is special because it gets imported during build.
+
+var ReactVersion = '16.8.6';
+
+var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+
+function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+
+function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+
+// for .act's return value
+
+
+var defaultTestOptions = {
+ createNodeMock: function () {
+ return null;
+ }
+};
+
+function toJSON(inst) {
+ if (inst.isHidden) {
+ // Omit timed out children from output entirely. This seems like the least
+ // surprising behavior. We could perhaps add a separate API that includes
+ // them, if it turns out people need it.
+ return null;
+ }
+ switch (inst.tag) {
+ case 'TEXT':
+ return inst.text;
+ case 'INSTANCE':
+ {
+ /* eslint-disable no-unused-vars */
+ // We don't include the `children` prop in JSON.
+ // Instead, we will include the actual rendered children.
+ var _inst$props = inst.props,
+ _children = _inst$props.children,
+ _props = _objectWithoutProperties(_inst$props, ['children']);
+ /* eslint-enable */
+
+
+ var renderedChildren = null;
+ if (inst.children && inst.children.length) {
+ for (var i = 0; i < inst.children.length; i++) {
+ var renderedChild = toJSON(inst.children[i]);
+ if (renderedChild !== null) {
+ if (renderedChildren === null) {
+ renderedChildren = [renderedChild];
+ } else {
+ renderedChildren.push(renderedChild);
+ }
+ }
+ }
+ }
+ var json = {
+ type: inst.type,
+ props: _props,
+ children: renderedChildren
+ };
+ Object.defineProperty(json, '$$typeof', {
+ value: Symbol.for('react.test.json')
+ });
+ return json;
+ }
+ default:
+ throw new Error('Unexpected node type in toJSON: ' + inst.tag);
+ }
+}
+
+function childrenToTree(node) {
+ if (!node) {
+ return null;
+ }
+ var children = nodeAndSiblingsArray(node);
+ if (children.length === 0) {
+ return null;
+ } else if (children.length === 1) {
+ return toTree(children[0]);
+ }
+ return flatten(children.map(toTree));
+}
+
+function nodeAndSiblingsArray(nodeWithSibling) {
+ var array = [];
+ var node = nodeWithSibling;
+ while (node != null) {
+ array.push(node);
+ node = node.sibling;
+ }
+ return array;
+}
+
+function flatten(arr) {
+ var result = [];
+ var stack = [{ i: 0, array: arr }];
+ while (stack.length) {
+ var n = stack.pop();
+ while (n.i < n.array.length) {
+ var el = n.array[n.i];
+ n.i += 1;
+ if (Array.isArray(el)) {
+ stack.push(n);
+ stack.push({ i: 0, array: el });
+ break;
+ }
+ result.push(el);
+ }
+ }
+ return result;
+}
+
+function toTree(node) {
+ if (node == null) {
+ return null;
+ }
+ switch (node.tag) {
+ case HostRoot:
+ return childrenToTree(node.child);
+ case HostPortal:
+ return childrenToTree(node.child);
+ case ClassComponent:
+ return {
+ nodeType: 'component',
+ type: node.type,
+ props: _assign({}, node.memoizedProps),
+ instance: node.stateNode,
+ rendered: childrenToTree(node.child)
+ };
+ case FunctionComponent:
+ case SimpleMemoComponent:
+ return {
+ nodeType: 'component',
+ type: node.type,
+ props: _assign({}, node.memoizedProps),
+ instance: null,
+ rendered: childrenToTree(node.child)
+ };
+ case HostComponent:
+ {
+ return {
+ nodeType: 'host',
+ type: node.type,
+ props: _assign({}, node.memoizedProps),
+ instance: null, // TODO: use createNodeMock here somehow?
+ rendered: flatten(nodeAndSiblingsArray(node.child).map(toTree))
+ };
+ }
+ case HostText:
+ return node.stateNode.text;
+ case Fragment:
+ case ContextProvider:
+ case ContextConsumer:
+ case Mode:
+ case Profiler:
+ case ForwardRef:
+ case MemoComponent:
+ case IncompleteClassComponent:
+ return childrenToTree(node.child);
+ default:
+ reactProdInvariant('214', node.tag);
+ }
+}
+
+var validWrapperTypes = new Set([FunctionComponent, ClassComponent, HostComponent, ForwardRef, MemoComponent, SimpleMemoComponent,
+// Normally skipped, but used when there's more than one root child.
+HostRoot]);
+
+function getChildren(parent) {
+ var children = [];
+ var startingNode = parent;
+ var node = startingNode;
+ if (node.child === null) {
+ return children;
+ }
+ node.child.return = node;
+ node = node.child;
+ outer: while (true) {
+ var descend = false;
+ if (validWrapperTypes.has(node.tag)) {
+ children.push(wrapFiber(node));
+ } else if (node.tag === HostText) {
+ children.push('' + node.memoizedProps);
+ } else {
+ descend = true;
+ }
+ if (descend && node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
+ while (node.sibling === null) {
+ if (node.return === startingNode) {
+ break outer;
+ }
+ node = node.return;
+ }
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ return children;
+}
+
+var ReactTestInstance = function () {
+ ReactTestInstance.prototype._currentFiber = function _currentFiber() {
+ // Throws if this component has been unmounted.
+ var fiber = findCurrentFiberUsingSlowPath(this._fiber);
+ !(fiber !== null) ? reactProdInvariant('224') : void 0;
+ return fiber;
+ };
+
+ function ReactTestInstance(fiber) {
+ _classCallCheck(this, ReactTestInstance);
+
+ !validWrapperTypes.has(fiber.tag) ? reactProdInvariant('225', fiber.tag) : void 0;
+ this._fiber = fiber;
+ }
+
+ // Custom search functions
+ ReactTestInstance.prototype.find = function find(predicate) {
+ return expectOne(this.findAll(predicate, { deep: false }), 'matching custom predicate: ' + predicate.toString());
+ };
+
+ ReactTestInstance.prototype.findByType = function findByType(type) {
+ return expectOne(this.findAllByType(type, { deep: false }), 'with node type: "' + (type.displayName || type.name) + '"');
+ };
+
+ ReactTestInstance.prototype.findByProps = function findByProps(props) {
+ return expectOne(this.findAllByProps(props, { deep: false }), 'with props: ' + JSON.stringify(props));
+ };
+
+ ReactTestInstance.prototype.findAll = function findAll(predicate) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
+
+ return _findAll(this, predicate, options);
+ };
+
+ ReactTestInstance.prototype.findAllByType = function findAllByType(type) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
+
+ return _findAll(this, function (node) {
+ return node.type === type;
+ }, options);
+ };
+
+ ReactTestInstance.prototype.findAllByProps = function findAllByProps(props) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
+
+ return _findAll(this, function (node) {
+ return node.props && propsMatch(node.props, props);
+ }, options);
+ };
+
+ _createClass(ReactTestInstance, [{
+ key: 'instance',
+ get: function () {
+ if (this._fiber.tag === HostComponent) {
+ return getPublicInstance(this._fiber.stateNode);
+ } else {
+ return this._fiber.stateNode;
+ }
+ }
+ }, {
+ key: 'type',
+ get: function () {
+ return this._fiber.type;
+ }
+ }, {
+ key: 'props',
+ get: function () {
+ return this._currentFiber().memoizedProps;
+ }
+ }, {
+ key: 'parent',
+ get: function () {
+ var parent = this._fiber.return;
+ while (parent !== null) {
+ if (validWrapperTypes.has(parent.tag)) {
+ if (parent.tag === HostRoot) {
+ // Special case: we only "materialize" instances for roots
+ // if they have more than a single child. So we'll check that now.
+ if (getChildren(parent).length < 2) {
+ return null;
+ }
+ }
+ return wrapFiber(parent);
+ }
+ parent = parent.return;
+ }
+ return null;
+ }
+ }, {
+ key: 'children',
+ get: function () {
+ return getChildren(this._currentFiber());
+ }
+ }]);
+
+ return ReactTestInstance;
+}();
+
+function _findAll(root, predicate, options) {
+ var deep = options ? options.deep : true;
+ var results = [];
+
+ if (predicate(root)) {
+ results.push(root);
+ if (!deep) {
+ return results;
+ }
+ }
+
+ root.children.forEach(function (child) {
+ if (typeof child === 'string') {
+ return;
+ }
+ results.push.apply(results, _findAll(child, predicate, options));
+ });
+
+ return results;
+}
+
+function expectOne(all, message) {
+ if (all.length === 1) {
+ return all[0];
+ }
+
+ var prefix = all.length === 0 ? 'No instances found ' : 'Expected 1 but found ' + all.length + ' instances ';
+
+ throw new Error(prefix + message);
+}
+
+function propsMatch(props, filter) {
+ for (var key in filter) {
+ if (props[key] !== filter[key]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+var ReactTestRendererFiber = {
+ create: function (element, options) {
+ var createNodeMock = defaultTestOptions.createNodeMock;
+ var isConcurrent = false;
+ if (typeof options === 'object' && options !== null) {
+ if (typeof options.createNodeMock === 'function') {
+ createNodeMock = options.createNodeMock;
+ }
+ if (options.unstable_isConcurrent === true) {
+ isConcurrent = true;
+ }
+ }
+ var container = {
+ children: [],
+ createNodeMock: createNodeMock,
+ tag: 'CONTAINER'
+ };
+ var root = createContainer(container, isConcurrent, false);
+ !(root != null) ? reactProdInvariant('215') : void 0;
+ updateContainer(element, root, null, null);
+
+ var entry = {
+ root: undefined, // makes flow happy
+ // we define a 'getter' for 'root' below using 'Object.defineProperty'
+ toJSON: function () {
+ if (root == null || root.current == null || container == null) {
+ return null;
+ }
+ if (container.children.length === 0) {
+ return null;
+ }
+ if (container.children.length === 1) {
+ return toJSON(container.children[0]);
+ }
+ if (container.children.length === 2 && container.children[0].isHidden === true && container.children[1].isHidden === false) {
+ // Omit timed out children from output entirely, including the fact that we
+ // temporarily wrap fallback and timed out children in an array.
+ return toJSON(container.children[1]);
+ }
+ var renderedChildren = null;
+ if (container.children && container.children.length) {
+ for (var i = 0; i < container.children.length; i++) {
+ var renderedChild = toJSON(container.children[i]);
+ if (renderedChild !== null) {
+ if (renderedChildren === null) {
+ renderedChildren = [renderedChild];
+ } else {
+ renderedChildren.push(renderedChild);
+ }
+ }
+ }
+ }
+ return renderedChildren;
+ },
+ toTree: function () {
+ if (root == null || root.current == null) {
+ return null;
+ }
+ return toTree(root.current);
+ },
+ update: function (newElement) {
+ if (root == null || root.current == null) {
+ return;
+ }
+ updateContainer(newElement, root, null, null);
+ },
+ unmount: function () {
+ if (root == null || root.current == null) {
+ return;
+ }
+ updateContainer(null, root, null, null);
+ container = null;
+ root = null;
+ },
+ getInstance: function () {
+ if (root == null || root.current == null) {
+ return null;
+ }
+ return getPublicRootInstance(root);
+ },
+
+
+ unstable_flushAll: flushAll,
+ unstable_flushSync: function (fn) {
+ clearYields();
+ return flushSync(fn);
+ },
+
+ unstable_flushNumberOfYields: flushNumberOfYields,
+ unstable_clearYields: clearYields
+ };
+
+ Object.defineProperty(entry, 'root', {
+ configurable: true,
+ enumerable: true,
+ get: function () {
+ if (root === null) {
+ throw new Error("Can't access .root on unmounted test renderer");
+ }
+ var children = getChildren(root.current);
+ if (children.length === 0) {
+ throw new Error("Can't access .root on unmounted test renderer");
+ } else if (children.length === 1) {
+ // Normally, we skip the root and just give you the child.
+ return children[0];
+ } else {
+ // However, we give you the root if there's more than one root child.
+ // We could make this the behavior for all cases but it would be a breaking change.
+ return wrapFiber(root.current);
+ }
+ }
+ });
+
+ return entry;
+ },
+
+
+ unstable_yield: yieldValue,
+ unstable_clearYields: clearYields,
+
+ /* eslint-disable camelcase */
+ unstable_batchedUpdates: batchedUpdates,
+ /* eslint-enable camelcase */
+
+ unstable_setNowImplementation: setNowImplementation,
+
+ act: function (callback) {
+ // note: keep these warning messages in sync with
+ // createNoop.js and ReactTestUtils.js
+ var result = batchedUpdates(callback);
+ flushPassiveEffects();
+ // we want the user to not expect a return,
+ // but we want to warn if they use it like they can await on it.
+ return {
+ then: function () {
+
+ }
+ };
+ }
+};
+
+// root used to flush effects during .act() calls
+var actRoot = createContainer({
+ children: [],
+ createNodeMock: defaultTestOptions.createNodeMock,
+ tag: 'CONTAINER'
+}, true, false);
+
+function flushPassiveEffects() {
+ // Trick to flush passive effects without exposing an internal API:
+ // Create a throwaway root and schedule a dummy update on it.
+ updateContainer(null, actRoot, null, null);
+}
+
+var fiberToWrapper = new WeakMap();
+function wrapFiber(fiber) {
+ var wrapper = fiberToWrapper.get(fiber);
+ if (wrapper === undefined && fiber.alternate !== null) {
+ wrapper = fiberToWrapper.get(fiber.alternate);
+ }
+ if (wrapper === undefined) {
+ wrapper = new ReactTestInstance(fiber);
+ fiberToWrapper.set(fiber, wrapper);
+ }
+ return wrapper;
+}
+
+// Enable ReactTestRenderer to be used to test DevTools integration.
+injectIntoDevTools({
+ findFiberByHostInstance: function () {
+ throw new Error('TestRenderer does not support findFiberByHostInstance()');
+ },
+ bundleType: 0,
+ version: ReactVersion,
+ rendererPackageName: 'react-test-renderer'
+});
+
+
+
+var ReactTestRenderer = ({
+ default: ReactTestRendererFiber
+});
+
+var ReactTestRenderer$1 = ( ReactTestRenderer && ReactTestRendererFiber ) || ReactTestRenderer;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var reactTestRenderer = ReactTestRenderer$1.default || ReactTestRenderer$1;
+
+return reactTestRenderer;
+
+})));
diff --git a/devtools/client/shared/vendor/react.js b/devtools/client/shared/vendor/react.js
new file mode 100644
index 0000000000..423630e71a
--- /dev/null
+++ b/devtools/client/shared/vendor/react.js
@@ -0,0 +1,2240 @@
+/** @license React v16.8.6
+ * react.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+ typeof define === 'function' && define.amd ? define(factory) :
+ (global.React = factory());
+}(this, (function () { 'use strict';
+
+// TODO: this is special because it gets imported during build.
+
+var ReactVersion = '16.8.6';
+
+// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
+// nor polyfill, then a plain number is used for performance.
+var hasSymbol = typeof Symbol === 'function' && Symbol.for;
+
+var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
+var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
+var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
+var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
+
+var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
+var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
+var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
+var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
+var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
+
+var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
+var FAUX_ITERATOR_SYMBOL = '@@iterator';
+
+function getIteratorFn(maybeIterable) {
+ if (maybeIterable === null || typeof maybeIterable !== 'object') {
+ return null;
+ }
+ var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
+ if (typeof maybeIterator === 'function') {
+ return maybeIterator;
+ }
+ return null;
+}
+
+/*
+object-assign
+(c) Sindre Sorhus
+@license MIT
+*/
+
+
+/* eslint-disable no-unused-vars */
+var getOwnPropertySymbols = Object.getOwnPropertySymbols;
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var propIsEnumerable = Object.prototype.propertyIsEnumerable;
+
+function toObject(val) {
+ if (val === null || val === undefined) {
+ throw new TypeError('Object.assign cannot be called with null or undefined');
+ }
+
+ return Object(val);
+}
+
+function shouldUseNative() {
+ try {
+ if (!Object.assign) {
+ return false;
+ }
+
+ // Detect buggy property enumeration order in older V8 versions.
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=4118
+ var test1 = new String('abc'); // eslint-disable-line no-new-wrappers
+ test1[5] = 'de';
+ if (Object.getOwnPropertyNames(test1)[0] === '5') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test2 = {};
+ for (var i = 0; i < 10; i++) {
+ test2['_' + String.fromCharCode(i)] = i;
+ }
+ var order2 = Object.getOwnPropertyNames(test2).map(function (n) {
+ return test2[n];
+ });
+ if (order2.join('') !== '0123456789') {
+ return false;
+ }
+
+ // https://bugs.chromium.org/p/v8/issues/detail?id=3056
+ var test3 = {};
+ 'abcdefghijklmnopqrst'.split('').forEach(function (letter) {
+ test3[letter] = letter;
+ });
+ if (Object.keys(Object.assign({}, test3)).join('') !==
+ 'abcdefghijklmnopqrst') {
+ return false;
+ }
+
+ return true;
+ } catch (err) {
+ // We don't expect any of the above to throw, but better to be safe.
+ return false;
+ }
+}
+
+var objectAssign = shouldUseNative() ? Object.assign : function (target, source) {
+ var from;
+ var to = toObject(target);
+ var symbols;
+
+ for (var s = 1; s < arguments.length; s++) {
+ from = Object(arguments[s]);
+
+ for (var key in from) {
+ if (hasOwnProperty.call(from, key)) {
+ to[key] = from[key];
+ }
+ }
+
+ if (getOwnPropertySymbols) {
+ symbols = getOwnPropertySymbols(from);
+ for (var i = 0; i < symbols.length; i++) {
+ if (propIsEnumerable.call(from, symbols[i])) {
+ to[symbols[i]] = from[symbols[i]];
+ }
+ }
+ }
+ }
+
+ return to;
+};
+
+/**
+ * Use invariant() to assert state which your program assumes to be true.
+ *
+ * Provide sprintf-style format (only %s is supported) and arguments
+ * to provide information about what broke and what you were
+ * expecting.
+ *
+ * The invariant message will be stripped in production, but the invariant
+ * will remain to ensure logic does not differ in production.
+ */
+
+function invariant(condition, format, a, b, c, d, e, f) {
+ if (!condition) {
+ var error = void 0;
+ if (format === undefined) {
+ error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
+ } else {
+ var args = [a, b, c, d, e, f];
+ var argIndex = 0;
+ error = new Error(format.replace(/%s/g, function () {
+ return args[argIndex++];
+ }));
+ error.name = 'Invariant Violation';
+ }
+
+ error.framesToPop = 1; // we don't care about invariant's own frame
+ throw error;
+ }
+}
+
+// Relying on the `invariant()` implementation lets us
+// preserve the format and params in the www builds.
+/**
+ * WARNING: DO NOT manually require this module.
+ * This is a replacement for `invariant(...)` used by the error code system
+ * and will _only_ be required by the corresponding babel pass.
+ * It always throws.
+ */
+function reactProdInvariant(code) {
+ var argCount = arguments.length - 1;
+ var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
+ for (var argIdx = 0; argIdx < argCount; argIdx++) {
+ url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
+ }
+ // Rename it so that our build transform doesn't attempt
+ // to replace this invariant() call with reactProdInvariant().
+ var i = invariant;
+ i(false,
+ // The error code is intentionally part of the message (and
+ // not the format argument) so that we could deduplicate
+ // different errors in logs based on the code.
+ 'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
+}
+
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+/**
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+/**
+ * This is the abstract API for an update queue.
+ */
+var ReactNoopUpdateQueue = {
+ /**
+ * Checks whether or not this composite component is mounted.
+ * @param {ReactClass} publicInstance The instance we want to test.
+ * @return {boolean} True if mounted, false otherwise.
+ * @protected
+ * @final
+ */
+ isMounted: function (publicInstance) {
+ return false;
+ },
+
+ /**
+ * Forces an update. This should only be invoked when it is known with
+ * certainty that we are **not** in a DOM transaction.
+ *
+ * You may want to call this when you know that some deeper aspect of the
+ * component's state has changed but `setState` was not called.
+ *
+ * This will not invoke `shouldComponentUpdate`, but it will invoke
+ * `componentWillUpdate` and `componentDidUpdate`.
+ *
+ * @param {ReactClass} publicInstance The instance that should rerender.
+ * @param {?function} callback Called after component is updated.
+ * @param {?string} callerName name of the calling function in the public API.
+ * @internal
+ */
+ enqueueForceUpdate: function (publicInstance, callback, callerName) {
+
+ },
+
+ /**
+ * Replaces all of the state. Always use this or `setState` to mutate state.
+ * You should treat `this.state` as immutable.
+ *
+ * There is no guarantee that `this.state` will be immediately updated, so
+ * accessing `this.state` after calling this method may return the old value.
+ *
+ * @param {ReactClass} publicInstance The instance that should rerender.
+ * @param {object} completeState Next state.
+ * @param {?function} callback Called after component is updated.
+ * @param {?string} callerName name of the calling function in the public API.
+ * @internal
+ */
+ enqueueReplaceState: function (publicInstance, completeState, callback, callerName) {
+
+ },
+
+ /**
+ * Sets a subset of the state. This only exists because _pendingState is
+ * internal. This provides a merging strategy that is not available to deep
+ * properties which is confusing. TODO: Expose pendingState or don't use it
+ * during the merge.
+ *
+ * @param {ReactClass} publicInstance The instance that should rerender.
+ * @param {object} partialState Next partial state to be merged with state.
+ * @param {?function} callback Called after component is updated.
+ * @param {?string} Name of the calling function in the public API.
+ * @internal
+ */
+ enqueueSetState: function (publicInstance, partialState, callback, callerName) {
+
+ }
+};
+
+var emptyObject = {};
+/**
+ * Base class helpers for the updating state of a component.
+ */
+function Component(props, context, updater) {
+ this.props = props;
+ this.context = context;
+ // If a component has string refs, we will assign a different object later.
+ this.refs = emptyObject;
+ // We initialize the default updater but the real one gets injected by the
+ // renderer.
+ this.updater = updater || ReactNoopUpdateQueue;
+}
+
+Component.prototype.isReactComponent = {};
+
+/**
+ * Sets a subset of the state. Always use this to mutate
+ * state. You should treat `this.state` as immutable.
+ *
+ * There is no guarantee that `this.state` will be immediately updated, so
+ * accessing `this.state` after calling this method may return the old value.
+ *
+ * There is no guarantee that calls to `setState` will run synchronously,
+ * as they may eventually be batched together. You can provide an optional
+ * callback that will be executed when the call to setState is actually
+ * completed.
+ *
+ * When a function is provided to setState, it will be called at some point in
+ * the future (not synchronously). It will be called with the up to date
+ * component arguments (state, props, context). These values can be different
+ * from this.* because your function may be called after receiveProps but before
+ * shouldComponentUpdate, and this new state, props, and context will not yet be
+ * assigned to this.
+ *
+ * @param {object|function} partialState Next partial state or function to
+ * produce next partial state to be merged with current state.
+ * @param {?function} callback Called after state is updated.
+ * @final
+ * @protected
+ */
+Component.prototype.setState = function (partialState, callback) {
+ !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? reactProdInvariant('85') : void 0;
+ this.updater.enqueueSetState(this, partialState, callback, 'setState');
+};
+
+/**
+ * Forces an update. This should only be invoked when it is known with
+ * certainty that we are **not** in a DOM transaction.
+ *
+ * You may want to call this when you know that some deeper aspect of the
+ * component's state has changed but `setState` was not called.
+ *
+ * This will not invoke `shouldComponentUpdate`, but it will invoke
+ * `componentWillUpdate` and `componentDidUpdate`.
+ *
+ * @param {?function} callback Called after update is complete.
+ * @final
+ * @protected
+ */
+Component.prototype.forceUpdate = function (callback) {
+ this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
+};
+
+/**
+ * Deprecated APIs. These APIs used to exist on classic React classes but since
+ * we would like to deprecate them, we're not going to move them over to this
+ * modern base class. Instead, we define a getter that warns if it's accessed.
+ */
+function ComponentDummy() {}
+ComponentDummy.prototype = Component.prototype;
+
+/**
+ * Convenience component with default shallow equality check for sCU.
+ */
+function PureComponent(props, context, updater) {
+ this.props = props;
+ this.context = context;
+ // If a component has string refs, we will assign a different object later.
+ this.refs = emptyObject;
+ this.updater = updater || ReactNoopUpdateQueue;
+}
+
+var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();
+pureComponentPrototype.constructor = PureComponent;
+// Avoid an extra prototype jump for these methods.
+objectAssign(pureComponentPrototype, Component.prototype);
+pureComponentPrototype.isPureReactComponent = true;
+
+// an immutable object with a single mutable value
+function createRef() {
+ var refObject = {
+ current: null
+ };
+ return refObject;
+}
+
+var enableSchedulerDebugging = false;
+
+/* eslint-disable no-var */
+
+// TODO: Use symbols?
+var ImmediatePriority = 1;
+var UserBlockingPriority = 2;
+var NormalPriority = 3;
+var LowPriority = 4;
+var IdlePriority = 5;
+
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
+// Math.pow(2, 30) - 1
+// 0b111111111111111111111111111111
+var maxSigned31BitInt = 1073741823;
+
+// Times out immediately
+var IMMEDIATE_PRIORITY_TIMEOUT = -1;
+// Eventually times out
+var USER_BLOCKING_PRIORITY = 250;
+var NORMAL_PRIORITY_TIMEOUT = 5000;
+var LOW_PRIORITY_TIMEOUT = 10000;
+// Never times out
+var IDLE_PRIORITY = maxSigned31BitInt;
+
+// Callbacks are stored as a circular, doubly linked list.
+var firstCallbackNode = null;
+
+var currentDidTimeout = false;
+// Pausing the scheduler is useful for debugging.
+var isSchedulerPaused = false;
+
+var currentPriorityLevel = NormalPriority;
+var currentEventStartTime = -1;
+var currentExpirationTime = -1;
+
+// This is set when a callback is being executed, to prevent re-entrancy.
+var isExecutingCallback = false;
+
+var isHostCallbackScheduled = false;
+
+var hasNativePerformanceNow = typeof performance === 'object' && typeof performance.now === 'function';
+
+function ensureHostCallbackIsScheduled() {
+ if (isExecutingCallback) {
+ // Don't schedule work yet; wait until the next time we yield.
+ return;
+ }
+ // Schedule the host callback using the earliest expiration in the list.
+ var expirationTime = firstCallbackNode.expirationTime;
+ if (!isHostCallbackScheduled) {
+ isHostCallbackScheduled = true;
+ } else {
+ // Cancel the existing host callback.
+ cancelHostCallback();
+ }
+ requestHostCallback(flushWork, expirationTime);
+}
+
+function flushFirstCallback() {
+ var flushedNode = firstCallbackNode;
+
+ // Remove the node from the list before calling the callback. That way the
+ // list is in a consistent state even if the callback throws.
+ var next = firstCallbackNode.next;
+ if (firstCallbackNode === next) {
+ // This is the last callback in the list.
+ firstCallbackNode = null;
+ next = null;
+ } else {
+ var lastCallbackNode = firstCallbackNode.previous;
+ firstCallbackNode = lastCallbackNode.next = next;
+ next.previous = lastCallbackNode;
+ }
+
+ flushedNode.next = flushedNode.previous = null;
+
+ // Now it's safe to call the callback.
+ var callback = flushedNode.callback;
+ var expirationTime = flushedNode.expirationTime;
+ var priorityLevel = flushedNode.priorityLevel;
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousExpirationTime = currentExpirationTime;
+ currentPriorityLevel = priorityLevel;
+ currentExpirationTime = expirationTime;
+ var continuationCallback;
+ try {
+ continuationCallback = callback();
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentExpirationTime = previousExpirationTime;
+ }
+
+ // A callback may return a continuation. The continuation should be scheduled
+ // with the same priority and expiration as the just-finished callback.
+ if (typeof continuationCallback === 'function') {
+ var continuationNode = {
+ callback: continuationCallback,
+ priorityLevel: priorityLevel,
+ expirationTime: expirationTime,
+ next: null,
+ previous: null
+ };
+
+ // Insert the new callback into the list, sorted by its expiration. This is
+ // almost the same as the code in `scheduleCallback`, except the callback
+ // is inserted into the list *before* callbacks of equal expiration instead
+ // of after.
+ if (firstCallbackNode === null) {
+ // This is the first callback in the list.
+ firstCallbackNode = continuationNode.next = continuationNode.previous = continuationNode;
+ } else {
+ var nextAfterContinuation = null;
+ var node = firstCallbackNode;
+ do {
+ if (node.expirationTime >= expirationTime) {
+ // This callback expires at or after the continuation. We will insert
+ // the continuation *before* this callback.
+ nextAfterContinuation = node;
+ break;
+ }
+ node = node.next;
+ } while (node !== firstCallbackNode);
+
+ if (nextAfterContinuation === null) {
+ // No equal or lower priority callback was found, which means the new
+ // callback is the lowest priority callback in the list.
+ nextAfterContinuation = firstCallbackNode;
+ } else if (nextAfterContinuation === firstCallbackNode) {
+ // The new callback is the highest priority callback in the list.
+ firstCallbackNode = continuationNode;
+ ensureHostCallbackIsScheduled();
+ }
+
+ var previous = nextAfterContinuation.previous;
+ previous.next = nextAfterContinuation.previous = continuationNode;
+ continuationNode.next = nextAfterContinuation;
+ continuationNode.previous = previous;
+ }
+ }
+}
+
+function flushImmediateWork() {
+ if (
+ // Confirm we've exited the outer most event handler
+ currentEventStartTime === -1 && firstCallbackNode !== null && firstCallbackNode.priorityLevel === ImmediatePriority) {
+ isExecutingCallback = true;
+ try {
+ do {
+ flushFirstCallback();
+ } while (
+ // Keep flushing until there are no more immediate callbacks
+ firstCallbackNode !== null && firstCallbackNode.priorityLevel === ImmediatePriority);
+ } finally {
+ isExecutingCallback = false;
+ if (firstCallbackNode !== null) {
+ // There's still work remaining. Request another callback.
+ ensureHostCallbackIsScheduled();
+ } else {
+ isHostCallbackScheduled = false;
+ }
+ }
+ }
+}
+
+function flushWork(didTimeout) {
+ // Exit right away if we're currently paused
+
+ if (enableSchedulerDebugging && isSchedulerPaused) {
+ return;
+ }
+
+ isExecutingCallback = true;
+ var previousDidTimeout = currentDidTimeout;
+ currentDidTimeout = didTimeout;
+ try {
+ if (didTimeout) {
+ // Flush all the expired callbacks without yielding.
+ while (firstCallbackNode !== null && !(enableSchedulerDebugging && isSchedulerPaused)) {
+ // TODO Wrap in feature flag
+ // Read the current time. Flush all the callbacks that expire at or
+ // earlier than that time. Then read the current time again and repeat.
+ // This optimizes for as few performance.now calls as possible.
+ var currentTime = getCurrentTime();
+ if (firstCallbackNode.expirationTime <= currentTime) {
+ do {
+ flushFirstCallback();
+ } while (firstCallbackNode !== null && firstCallbackNode.expirationTime <= currentTime && !(enableSchedulerDebugging && isSchedulerPaused));
+ continue;
+ }
+ break;
+ }
+ } else {
+ // Keep flushing callbacks until we run out of time in the frame.
+ if (firstCallbackNode !== null) {
+ do {
+ if (enableSchedulerDebugging && isSchedulerPaused) {
+ break;
+ }
+ flushFirstCallback();
+ } while (firstCallbackNode !== null && !shouldYieldToHost());
+ }
+ }
+ } finally {
+ isExecutingCallback = false;
+ currentDidTimeout = previousDidTimeout;
+ if (firstCallbackNode !== null) {
+ // There's still work remaining. Request another callback.
+ ensureHostCallbackIsScheduled();
+ } else {
+ isHostCallbackScheduled = false;
+ }
+ // Before exiting, flush all the immediate work that was scheduled.
+ flushImmediateWork();
+ }
+}
+
+function unstable_runWithPriority(priorityLevel, eventHandler) {
+ switch (priorityLevel) {
+ case ImmediatePriority:
+ case UserBlockingPriority:
+ case NormalPriority:
+ case LowPriority:
+ case IdlePriority:
+ break;
+ default:
+ priorityLevel = NormalPriority;
+ }
+
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousEventStartTime = currentEventStartTime;
+ currentPriorityLevel = priorityLevel;
+ currentEventStartTime = getCurrentTime();
+
+ try {
+ return eventHandler();
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentEventStartTime = previousEventStartTime;
+
+ // Before exiting, flush all the immediate work that was scheduled.
+ flushImmediateWork();
+ }
+}
+
+function unstable_next(eventHandler) {
+ var priorityLevel = void 0;
+ switch (currentPriorityLevel) {
+ case ImmediatePriority:
+ case UserBlockingPriority:
+ case NormalPriority:
+ // Shift down to normal priority
+ priorityLevel = NormalPriority;
+ break;
+ default:
+ // Anything lower than normal priority should remain at the current level.
+ priorityLevel = currentPriorityLevel;
+ break;
+ }
+
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousEventStartTime = currentEventStartTime;
+ currentPriorityLevel = priorityLevel;
+ currentEventStartTime = getCurrentTime();
+
+ try {
+ return eventHandler();
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentEventStartTime = previousEventStartTime;
+
+ // Before exiting, flush all the immediate work that was scheduled.
+ flushImmediateWork();
+ }
+}
+
+function unstable_wrapCallback(callback) {
+ var parentPriorityLevel = currentPriorityLevel;
+ return function () {
+ // This is a fork of runWithPriority, inlined for performance.
+ var previousPriorityLevel = currentPriorityLevel;
+ var previousEventStartTime = currentEventStartTime;
+ currentPriorityLevel = parentPriorityLevel;
+ currentEventStartTime = getCurrentTime();
+
+ try {
+ return callback.apply(this, arguments);
+ } finally {
+ currentPriorityLevel = previousPriorityLevel;
+ currentEventStartTime = previousEventStartTime;
+ flushImmediateWork();
+ }
+ };
+}
+
+function unstable_scheduleCallback(callback, deprecated_options) {
+ var startTime = currentEventStartTime !== -1 ? currentEventStartTime : getCurrentTime();
+
+ var expirationTime;
+ if (typeof deprecated_options === 'object' && deprecated_options !== null && typeof deprecated_options.timeout === 'number') {
+ // FIXME: Remove this branch once we lift expiration times out of React.
+ expirationTime = startTime + deprecated_options.timeout;
+ } else {
+ switch (currentPriorityLevel) {
+ case ImmediatePriority:
+ expirationTime = startTime + IMMEDIATE_PRIORITY_TIMEOUT;
+ break;
+ case UserBlockingPriority:
+ expirationTime = startTime + USER_BLOCKING_PRIORITY;
+ break;
+ case IdlePriority:
+ expirationTime = startTime + IDLE_PRIORITY;
+ break;
+ case LowPriority:
+ expirationTime = startTime + LOW_PRIORITY_TIMEOUT;
+ break;
+ case NormalPriority:
+ default:
+ expirationTime = startTime + NORMAL_PRIORITY_TIMEOUT;
+ }
+ }
+
+ var newNode = {
+ callback: callback,
+ priorityLevel: currentPriorityLevel,
+ expirationTime: expirationTime,
+ next: null,
+ previous: null
+ };
+
+ // Insert the new callback into the list, ordered first by expiration, then
+ // by insertion. So the new callback is inserted any other callback with
+ // equal expiration.
+ if (firstCallbackNode === null) {
+ // This is the first callback in the list.
+ firstCallbackNode = newNode.next = newNode.previous = newNode;
+ ensureHostCallbackIsScheduled();
+ } else {
+ var next = null;
+ var node = firstCallbackNode;
+ do {
+ if (node.expirationTime > expirationTime) {
+ // The new callback expires before this one.
+ next = node;
+ break;
+ }
+ node = node.next;
+ } while (node !== firstCallbackNode);
+
+ if (next === null) {
+ // No callback with a later expiration was found, which means the new
+ // callback has the latest expiration in the list.
+ next = firstCallbackNode;
+ } else if (next === firstCallbackNode) {
+ // The new callback has the earliest expiration in the entire list.
+ firstCallbackNode = newNode;
+ ensureHostCallbackIsScheduled();
+ }
+
+ var previous = next.previous;
+ previous.next = next.previous = newNode;
+ newNode.next = next;
+ newNode.previous = previous;
+ }
+
+ return newNode;
+}
+
+function unstable_pauseExecution() {
+ isSchedulerPaused = true;
+}
+
+function unstable_continueExecution() {
+ isSchedulerPaused = false;
+ if (firstCallbackNode !== null) {
+ ensureHostCallbackIsScheduled();
+ }
+}
+
+function unstable_getFirstCallbackNode() {
+ return firstCallbackNode;
+}
+
+function unstable_cancelCallback(callbackNode) {
+ var next = callbackNode.next;
+ if (next === null) {
+ // Already cancelled.
+ return;
+ }
+
+ if (next === callbackNode) {
+ // This is the only scheduled callback. Clear the list.
+ firstCallbackNode = null;
+ } else {
+ // Remove the callback from its position in the list.
+ if (callbackNode === firstCallbackNode) {
+ firstCallbackNode = next;
+ }
+ var previous = callbackNode.previous;
+ previous.next = next;
+ next.previous = previous;
+ }
+
+ callbackNode.next = callbackNode.previous = null;
+}
+
+function unstable_getCurrentPriorityLevel() {
+ return currentPriorityLevel;
+}
+
+function unstable_shouldYield() {
+ return !currentDidTimeout && (firstCallbackNode !== null && firstCallbackNode.expirationTime < currentExpirationTime || shouldYieldToHost());
+}
+
+// The remaining code is essentially a polyfill for requestIdleCallback. It
+// works by scheduling a requestAnimationFrame, storing the time for the start
+// of the frame, then scheduling a postMessage which gets scheduled after paint.
+// Within the postMessage handler do as much work as possible until time + frame
+// rate. By separating the idle call into a separate event tick we ensure that
+// layout, paint and other browser work is counted against the available time.
+// The frame rate is dynamically adjusted.
+
+// We capture a local reference to any global, in case it gets polyfilled after
+// this module is initially evaluated. We want to be using a
+// consistent implementation.
+var localDate = Date;
+
+// This initialization code may run even on server environments if a component
+// just imports ReactDOM (e.g. for findDOMNode). Some environments might not
+// have setTimeout or clearTimeout. However, we always expect them to be defined
+// on the client. https://github.com/facebook/react/pull/13088
+var localSetTimeout = typeof setTimeout === 'function' ? setTimeout : undefined;
+var localClearTimeout = typeof clearTimeout === 'function' ? clearTimeout : undefined;
+
+// We don't expect either of these to necessarily be defined, but we will error
+// later if they are missing on the client.
+var localRequestAnimationFrame = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : undefined;
+var localCancelAnimationFrame = typeof cancelAnimationFrame === 'function' ? cancelAnimationFrame : undefined;
+
+var getCurrentTime;
+
+// requestAnimationFrame does not run when the tab is in the background. If
+// we're backgrounded we prefer for that work to happen so that the page
+// continues to load in the background. So we also schedule a 'setTimeout' as
+// a fallback.
+// TODO: Need a better heuristic for backgrounded work.
+var ANIMATION_FRAME_TIMEOUT = 100;
+var rAFID;
+var rAFTimeoutID;
+var requestAnimationFrameWithTimeout = function (callback) {
+ // schedule rAF and also a setTimeout
+ rAFID = localRequestAnimationFrame(function (timestamp) {
+ // cancel the setTimeout
+ localClearTimeout(rAFTimeoutID);
+ callback(timestamp);
+ });
+ rAFTimeoutID = localSetTimeout(function () {
+ // cancel the requestAnimationFrame
+ localCancelAnimationFrame(rAFID);
+ callback(getCurrentTime());
+ }, ANIMATION_FRAME_TIMEOUT);
+};
+
+if (hasNativePerformanceNow) {
+ var Performance = performance;
+ getCurrentTime = function () {
+ return Performance.now();
+ };
+} else {
+ getCurrentTime = function () {
+ return localDate.now();
+ };
+}
+
+var requestHostCallback;
+var cancelHostCallback;
+var shouldYieldToHost;
+
+var globalValue = null;
+if (typeof window !== 'undefined') {
+ globalValue = window;
+} else if (typeof global !== 'undefined') {
+ globalValue = global;
+}
+
+if (globalValue && globalValue._schedMock) {
+ // Dynamic injection, only for testing purposes.
+ var globalImpl = globalValue._schedMock;
+ requestHostCallback = globalImpl[0];
+ cancelHostCallback = globalImpl[1];
+ shouldYieldToHost = globalImpl[2];
+ getCurrentTime = globalImpl[3];
+} else if (
+// If Scheduler runs in a non-DOM environment, it falls back to a naive
+// implementation using setTimeout.
+typeof window === 'undefined' ||
+// Check if MessageChannel is supported, too.
+typeof MessageChannel !== 'function') {
+ // If this accidentally gets imported in a non-browser environment, e.g. JavaScriptCore,
+ // fallback to a naive implementation.
+ var _callback = null;
+ var _flushCallback = function (didTimeout) {
+ if (_callback !== null) {
+ try {
+ _callback(didTimeout);
+ } finally {
+ _callback = null;
+ }
+ }
+ };
+ requestHostCallback = function (cb, ms) {
+ if (_callback !== null) {
+ // Protect against re-entrancy.
+ setTimeout(requestHostCallback, 0, cb);
+ } else {
+ _callback = cb;
+ setTimeout(_flushCallback, 0, false);
+ }
+ };
+ cancelHostCallback = function () {
+ _callback = null;
+ };
+ shouldYieldToHost = function () {
+ return false;
+ };
+} else {
+ if (typeof console !== 'undefined') {
+ // TODO: Remove fb.me link
+ if (typeof localRequestAnimationFrame !== 'function') {
+ console.error("This browser doesn't support requestAnimationFrame. " + 'Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills');
+ }
+ if (typeof localCancelAnimationFrame !== 'function') {
+ console.error("This browser doesn't support cancelAnimationFrame. " + 'Make sure that you load a ' + 'polyfill in older browsers. https://fb.me/react-polyfills');
+ }
+ }
+
+ var scheduledHostCallback = null;
+ var isMessageEventScheduled = false;
+ var timeoutTime = -1;
+
+ var isAnimationFrameScheduled = false;
+
+ var isFlushingHostCallback = false;
+
+ var frameDeadline = 0;
+ // We start out assuming that we run at 30fps but then the heuristic tracking
+ // will adjust this value to a faster fps if we get more frequent animation
+ // frames.
+ var previousFrameTime = 33;
+ var activeFrameTime = 33;
+
+ shouldYieldToHost = function () {
+ return frameDeadline <= getCurrentTime();
+ };
+
+ // We use the postMessage trick to defer idle work until after the repaint.
+ var channel = new MessageChannel();
+ var port = channel.port2;
+ channel.port1.onmessage = function (event) {
+ isMessageEventScheduled = false;
+
+ var prevScheduledCallback = scheduledHostCallback;
+ var prevTimeoutTime = timeoutTime;
+ scheduledHostCallback = null;
+ timeoutTime = -1;
+
+ var currentTime = getCurrentTime();
+
+ var didTimeout = false;
+ if (frameDeadline - currentTime <= 0) {
+ // There's no time left in this idle period. Check if the callback has
+ // a timeout and whether it's been exceeded.
+ if (prevTimeoutTime !== -1 && prevTimeoutTime <= currentTime) {
+ // Exceeded the timeout. Invoke the callback even though there's no
+ // time left.
+ didTimeout = true;
+ } else {
+ // No timeout.
+ if (!isAnimationFrameScheduled) {
+ // Schedule another animation callback so we retry later.
+ isAnimationFrameScheduled = true;
+ requestAnimationFrameWithTimeout(animationTick);
+ }
+ // Exit without invoking the callback.
+ scheduledHostCallback = prevScheduledCallback;
+ timeoutTime = prevTimeoutTime;
+ return;
+ }
+ }
+
+ if (prevScheduledCallback !== null) {
+ isFlushingHostCallback = true;
+ try {
+ prevScheduledCallback(didTimeout);
+ } finally {
+ isFlushingHostCallback = false;
+ }
+ }
+ };
+
+ var animationTick = function (rafTime) {
+ if (scheduledHostCallback !== null) {
+ // Eagerly schedule the next animation callback at the beginning of the
+ // frame. If the scheduler queue is not empty at the end of the frame, it
+ // will continue flushing inside that callback. If the queue *is* empty,
+ // then it will exit immediately. Posting the callback at the start of the
+ // frame ensures it's fired within the earliest possible frame. If we
+ // waited until the end of the frame to post the callback, we risk the
+ // browser skipping a frame and not firing the callback until the frame
+ // after that.
+ requestAnimationFrameWithTimeout(animationTick);
+ } else {
+ // No pending work. Exit.
+ isAnimationFrameScheduled = false;
+ return;
+ }
+
+ var nextFrameTime = rafTime - frameDeadline + activeFrameTime;
+ if (nextFrameTime < activeFrameTime && previousFrameTime < activeFrameTime) {
+ if (nextFrameTime < 8) {
+ // Defensive coding. We don't support higher frame rates than 120hz.
+ // If the calculated frame time gets lower than 8, it is probably a bug.
+ nextFrameTime = 8;
+ }
+ // If one frame goes long, then the next one can be short to catch up.
+ // If two frames are short in a row, then that's an indication that we
+ // actually have a higher frame rate than what we're currently optimizing.
+ // We adjust our heuristic dynamically accordingly. For example, if we're
+ // running on 120hz display or 90hz VR display.
+ // Take the max of the two in case one of them was an anomaly due to
+ // missed frame deadlines.
+ activeFrameTime = nextFrameTime < previousFrameTime ? previousFrameTime : nextFrameTime;
+ } else {
+ previousFrameTime = nextFrameTime;
+ }
+ frameDeadline = rafTime + activeFrameTime;
+ if (!isMessageEventScheduled) {
+ isMessageEventScheduled = true;
+ port.postMessage(undefined);
+ }
+ };
+
+ requestHostCallback = function (callback, absoluteTimeout) {
+ scheduledHostCallback = callback;
+ timeoutTime = absoluteTimeout;
+ if (isFlushingHostCallback || absoluteTimeout < 0) {
+ // Don't wait for the next frame. Continue working ASAP, in a new event.
+ port.postMessage(undefined);
+ } else if (!isAnimationFrameScheduled) {
+ // If rAF didn't already schedule one, we need to schedule a frame.
+ // TODO: If this rAF doesn't materialize because the browser throttles, we
+ // might want to still have setTimeout trigger rIC as a backup to ensure
+ // that we keep performing work.
+ isAnimationFrameScheduled = true;
+ requestAnimationFrameWithTimeout(animationTick);
+ }
+ };
+
+ cancelHostCallback = function () {
+ scheduledHostCallback = null;
+ isMessageEventScheduled = false;
+ timeoutTime = -1;
+ };
+}
+
+// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
+
+
+// In some cases, StrictMode should also double-render lifecycles.
+// This can be confusing for tests though,
+// And it can be bad for performance in production.
+// This feature flag can be used to control the behavior:
+
+
+// To preserve the "Pause on caught exceptions" behavior of the debugger, we
+// replay the begin phase of a failed component inside invokeGuardedCallback.
+
+
+// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
+
+
+// Gather advanced timing metrics for Profiler subtrees.
+
+
+// Trace which interactions trigger each commit.
+var enableSchedulerTracing = false;
+
+// Only used in www builds.
+ // TODO: false? Here it might just be false.
+
+// Only used in www builds.
+
+
+// Only used in www builds.
+
+
+// React Fire: prevent the value and checked attributes from syncing
+// with their related DOM properties
+
+
+// These APIs will no longer be "unstable" in the upcoming 16.7 release,
+// Control this behavior with a flag to support 16.6 minor releases in the meanwhile.
+var enableStableConcurrentModeAPIs = false;
+
+var DEFAULT_THREAD_ID = 0;
+
+// Counters used to generate unique IDs.
+var interactionIDCounter = 0;
+var threadIDCounter = 0;
+
+// Set of currently traced interactions.
+// Interactions "stack"–
+// Meaning that newly traced interactions are appended to the previously active set.
+// When an interaction goes out of scope, the previous set (if any) is restored.
+var interactionsRef = null;
+
+// Listener(s) to notify when interactions begin and end.
+var subscriberRef = null;
+
+if (enableSchedulerTracing) {
+ interactionsRef = {
+ current: new Set()
+ };
+ subscriberRef = {
+ current: null
+ };
+}
+
+function unstable_clear(callback) {
+ if (!enableSchedulerTracing) {
+ return callback();
+ }
+
+ var prevInteractions = interactionsRef.current;
+ interactionsRef.current = new Set();
+
+ try {
+ return callback();
+ } finally {
+ interactionsRef.current = prevInteractions;
+ }
+}
+
+function unstable_getCurrent() {
+ if (!enableSchedulerTracing) {
+ return null;
+ } else {
+ return interactionsRef.current;
+ }
+}
+
+function unstable_getThreadID() {
+ return ++threadIDCounter;
+}
+
+function unstable_trace(name, timestamp, callback) {
+ var threadID = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : DEFAULT_THREAD_ID;
+
+ if (!enableSchedulerTracing) {
+ return callback();
+ }
+
+ var interaction = {
+ __count: 1,
+ id: interactionIDCounter++,
+ name: name,
+ timestamp: timestamp
+ };
+
+ var prevInteractions = interactionsRef.current;
+
+ // Traced interactions should stack/accumulate.
+ // To do that, clone the current interactions.
+ // The previous set will be restored upon completion.
+ var interactions = new Set(prevInteractions);
+ interactions.add(interaction);
+ interactionsRef.current = interactions;
+
+ var subscriber = subscriberRef.current;
+ var returnValue = void 0;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onInteractionTraced(interaction);
+ }
+ } finally {
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkStarted(interactions, threadID);
+ }
+ } finally {
+ try {
+ returnValue = callback();
+ } finally {
+ interactionsRef.current = prevInteractions;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkStopped(interactions, threadID);
+ }
+ } finally {
+ interaction.__count--;
+
+ // If no async work was scheduled for this interaction,
+ // Notify subscribers that it's completed.
+ if (subscriber !== null && interaction.__count === 0) {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ }
+ }
+ }
+ }
+ }
+
+ return returnValue;
+}
+
+function unstable_wrap(callback) {
+ var threadID = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_THREAD_ID;
+
+ if (!enableSchedulerTracing) {
+ return callback;
+ }
+
+ var wrappedInteractions = interactionsRef.current;
+
+ var subscriber = subscriberRef.current;
+ if (subscriber !== null) {
+ subscriber.onWorkScheduled(wrappedInteractions, threadID);
+ }
+
+ // Update the pending async work count for the current interactions.
+ // Update after calling subscribers in case of error.
+ wrappedInteractions.forEach(function (interaction) {
+ interaction.__count++;
+ });
+
+ var hasRun = false;
+
+ function wrapped() {
+ var prevInteractions = interactionsRef.current;
+ interactionsRef.current = wrappedInteractions;
+
+ subscriber = subscriberRef.current;
+
+ try {
+ var returnValue = void 0;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkStarted(wrappedInteractions, threadID);
+ }
+ } finally {
+ try {
+ returnValue = callback.apply(undefined, arguments);
+ } finally {
+ interactionsRef.current = prevInteractions;
+
+ if (subscriber !== null) {
+ subscriber.onWorkStopped(wrappedInteractions, threadID);
+ }
+ }
+ }
+
+ return returnValue;
+ } finally {
+ if (!hasRun) {
+ // We only expect a wrapped function to be executed once,
+ // But in the event that it's executed more than once–
+ // Only decrement the outstanding interaction counts once.
+ hasRun = true;
+
+ // Update pending async counts for all wrapped interactions.
+ // If this was the last scheduled async work for any of them,
+ // Mark them as completed.
+ wrappedInteractions.forEach(function (interaction) {
+ interaction.__count--;
+
+ if (subscriber !== null && interaction.__count === 0) {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ }
+ });
+ }
+ }
+ }
+
+ wrapped.cancel = function cancel() {
+ subscriber = subscriberRef.current;
+
+ try {
+ if (subscriber !== null) {
+ subscriber.onWorkCanceled(wrappedInteractions, threadID);
+ }
+ } finally {
+ // Update pending async counts for all wrapped interactions.
+ // If this was the last scheduled async work for any of them,
+ // Mark them as completed.
+ wrappedInteractions.forEach(function (interaction) {
+ interaction.__count--;
+
+ if (subscriber && interaction.__count === 0) {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ }
+ });
+ }
+ };
+
+ return wrapped;
+}
+
+var subscribers = null;
+if (enableSchedulerTracing) {
+ subscribers = new Set();
+}
+
+function unstable_subscribe(subscriber) {
+ if (enableSchedulerTracing) {
+ subscribers.add(subscriber);
+
+ if (subscribers.size === 1) {
+ subscriberRef.current = {
+ onInteractionScheduledWorkCompleted: onInteractionScheduledWorkCompleted,
+ onInteractionTraced: onInteractionTraced,
+ onWorkCanceled: onWorkCanceled,
+ onWorkScheduled: onWorkScheduled,
+ onWorkStarted: onWorkStarted,
+ onWorkStopped: onWorkStopped
+ };
+ }
+ }
+}
+
+function unstable_unsubscribe(subscriber) {
+ if (enableSchedulerTracing) {
+ subscribers.delete(subscriber);
+
+ if (subscribers.size === 0) {
+ subscriberRef.current = null;
+ }
+ }
+}
+
+function onInteractionTraced(interaction) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onInteractionTraced(interaction);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onInteractionScheduledWorkCompleted(interaction) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onInteractionScheduledWorkCompleted(interaction);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkScheduled(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkScheduled(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkStarted(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkStarted(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkStopped(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkStopped(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+function onWorkCanceled(interactions, threadID) {
+ var didCatchError = false;
+ var caughtError = null;
+
+ subscribers.forEach(function (subscriber) {
+ try {
+ subscriber.onWorkCanceled(interactions, threadID);
+ } catch (error) {
+ if (!didCatchError) {
+ didCatchError = true;
+ caughtError = error;
+ }
+ }
+ });
+
+ if (didCatchError) {
+ throw caughtError;
+ }
+}
+
+/**
+ * Keeps track of the current dispatcher.
+ */
+var ReactCurrentDispatcher = {
+ /**
+ * @internal
+ * @type {ReactComponent}
+ */
+ current: null
+};
+
+/**
+ * Keeps track of the current owner.
+ *
+ * The current owner is the component who should own any components that are
+ * currently being constructed.
+ */
+var ReactCurrentOwner = {
+ /**
+ * @internal
+ * @type {ReactComponent}
+ */
+ current: null
+};
+
+var ReactSharedInternals = {
+ ReactCurrentDispatcher: ReactCurrentDispatcher,
+ ReactCurrentOwner: ReactCurrentOwner,
+ // Used by renderers to avoid bundling object-assign twice in UMD bundles:
+ assign: objectAssign
+};
+
+{
+ // Re-export the schedule API(s) for UMD bundles.
+ // This avoids introducing a dependency on a new UMD global in a minor update,
+ // Since that would be a breaking change (e.g. for all existing CodeSandboxes).
+ // This re-export is only required for UMD bundles;
+ // CJS bundles use the shared NPM package.
+ objectAssign(ReactSharedInternals, {
+ Scheduler: {
+ unstable_cancelCallback: unstable_cancelCallback,
+ unstable_shouldYield: unstable_shouldYield,
+ unstable_now: getCurrentTime,
+ unstable_scheduleCallback: unstable_scheduleCallback,
+ unstable_runWithPriority: unstable_runWithPriority,
+ unstable_next: unstable_next,
+ unstable_wrapCallback: unstable_wrapCallback,
+ unstable_getFirstCallbackNode: unstable_getFirstCallbackNode,
+ unstable_pauseExecution: unstable_pauseExecution,
+ unstable_continueExecution: unstable_continueExecution,
+ unstable_getCurrentPriorityLevel: unstable_getCurrentPriorityLevel,
+ unstable_IdlePriority: IdlePriority,
+ unstable_ImmediatePriority: ImmediatePriority,
+ unstable_LowPriority: LowPriority,
+ unstable_NormalPriority: NormalPriority,
+ unstable_UserBlockingPriority: UserBlockingPriority
+ },
+ SchedulerTracing: {
+ __interactionsRef: interactionsRef,
+ __subscriberRef: subscriberRef,
+ unstable_clear: unstable_clear,
+ unstable_getCurrent: unstable_getCurrent,
+ unstable_getThreadID: unstable_getThreadID,
+ unstable_subscribe: unstable_subscribe,
+ unstable_trace: unstable_trace,
+ unstable_unsubscribe: unstable_unsubscribe,
+ unstable_wrap: unstable_wrap
+ }
+ });
+}
+
+var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
+
+var RESERVED_PROPS = {
+ key: true,
+ ref: true,
+ __self: true,
+ __source: true
+};
+
+function hasValidRef(config) {
+ return config.ref !== undefined;
+}
+
+function hasValidKey(config) {
+ return config.key !== undefined;
+}
+
+/**
+ * Factory method to create a new React element. This no longer adheres to
+ * the class pattern, so do not use new to call it. Also, no instanceof check
+ * will work. Instead test $$typeof field against Symbol.for('react.element') to check
+ * if something is a React Element.
+ *
+ * @param {*} type
+ * @param {*} key
+ * @param {string|object} ref
+ * @param {*} self A *temporary* helper to detect places where `this` is
+ * different from the `owner` when React.createElement is called, so that we
+ * can warn. We want to get rid of owner and replace string `ref`s with arrow
+ * functions, and as long as `this` and owner are the same, there will be no
+ * change in behavior.
+ * @param {*} source An annotation object (added by a transpiler or otherwise)
+ * indicating filename, line number, and/or other information.
+ * @param {*} owner
+ * @param {*} props
+ * @internal
+ */
+var ReactElement = function (type, key, ref, self, source, owner, props) {
+ var element = {
+ // This tag allows us to uniquely identify this as a React Element
+ $$typeof: REACT_ELEMENT_TYPE,
+
+ // Built-in properties that belong on the element
+ type: type,
+ key: key,
+ ref: ref,
+ props: props,
+
+ // Record the component responsible for creating this element.
+ _owner: owner
+ };
+
+ return element;
+};
+
+/**
+ * Create and return a new ReactElement of the given type.
+ * See https://reactjs.org/docs/react-api.html#createelement
+ */
+function createElement(type, config, children) {
+ var propName = void 0;
+
+ // Reserved names are extracted
+ var props = {};
+
+ var key = null;
+ var ref = null;
+ var self = null;
+ var source = null;
+
+ if (config != null) {
+ if (hasValidRef(config)) {
+ ref = config.ref;
+ }
+ if (hasValidKey(config)) {
+ key = '' + config.key;
+ }
+
+ self = config.__self === undefined ? null : config.__self;
+ source = config.__source === undefined ? null : config.__source;
+ // Remaining properties are added to a new props object
+ for (propName in config) {
+ if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
+ props[propName] = config[propName];
+ }
+ }
+ }
+
+ // Children can be more than one argument, and those are transferred onto
+ // the newly allocated props object.
+ var childrenLength = arguments.length - 2;
+ if (childrenLength === 1) {
+ props.children = children;
+ } else if (childrenLength > 1) {
+ var childArray = Array(childrenLength);
+ for (var i = 0; i < childrenLength; i++) {
+ childArray[i] = arguments[i + 2];
+ }
+ props.children = childArray;
+ }
+
+ // Resolve default props
+ if (type && type.defaultProps) {
+ var defaultProps = type.defaultProps;
+ for (propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
+ }
+ }
+ return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
+}
+
+/**
+ * Return a function that produces ReactElements of a given type.
+ * See https://reactjs.org/docs/react-api.html#createfactory
+ */
+function createFactory(type) {
+ var factory = createElement.bind(null, type);
+ // Expose the type on the factory and the prototype so that it can be
+ // easily accessed on elements. E.g. `<Foo />.type === Foo`.
+ // This should not be named `constructor` since this may not be the function
+ // that created the element, and it may not even be a constructor.
+ // Legacy hook: remove it
+ factory.type = type;
+ return factory;
+}
+
+function cloneAndReplaceKey(oldElement, newKey) {
+ var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props);
+
+ return newElement;
+}
+
+/**
+ * Clone and return a new ReactElement using element as the starting point.
+ * See https://reactjs.org/docs/react-api.html#cloneelement
+ */
+function cloneElement(element, config, children) {
+ !!(element === null || element === undefined) ? reactProdInvariant('267', element) : void 0;
+
+ var propName = void 0;
+
+ // Original props are copied
+ var props = objectAssign({}, element.props);
+
+ // Reserved names are extracted
+ var key = element.key;
+ var ref = element.ref;
+ // Self is preserved since the owner is preserved.
+ var self = element._self;
+ // Source is preserved since cloneElement is unlikely to be targeted by a
+ // transpiler, and the original source is probably a better indicator of the
+ // true owner.
+ var source = element._source;
+
+ // Owner will be preserved, unless ref is overridden
+ var owner = element._owner;
+
+ if (config != null) {
+ if (hasValidRef(config)) {
+ // Silently steal the ref from the parent.
+ ref = config.ref;
+ owner = ReactCurrentOwner.current;
+ }
+ if (hasValidKey(config)) {
+ key = '' + config.key;
+ }
+
+ // Remaining properties override existing props
+ var defaultProps = void 0;
+ if (element.type && element.type.defaultProps) {
+ defaultProps = element.type.defaultProps;
+ }
+ for (propName in config) {
+ if (hasOwnProperty$1.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
+ if (config[propName] === undefined && defaultProps !== undefined) {
+ // Resolve default props
+ props[propName] = defaultProps[propName];
+ } else {
+ props[propName] = config[propName];
+ }
+ }
+ }
+ }
+
+ // Children can be more than one argument, and those are transferred onto
+ // the newly allocated props object.
+ var childrenLength = arguments.length - 2;
+ if (childrenLength === 1) {
+ props.children = children;
+ } else if (childrenLength > 1) {
+ var childArray = Array(childrenLength);
+ for (var i = 0; i < childrenLength; i++) {
+ childArray[i] = arguments[i + 2];
+ }
+ props.children = childArray;
+ }
+
+ return ReactElement(element.type, key, ref, self, source, owner, props);
+}
+
+/**
+ * Verifies the object is a ReactElement.
+ * See https://reactjs.org/docs/react-api.html#isvalidelement
+ * @param {?object} object
+ * @return {boolean} True if `object` is a ReactElement.
+ * @final
+ */
+function isValidElement(object) {
+ return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
+}
+
+var SEPARATOR = '.';
+var SUBSEPARATOR = ':';
+
+/**
+ * Escape and wrap key so it is safe to use as a reactid
+ *
+ * @param {string} key to be escaped.
+ * @return {string} the escaped key.
+ */
+function escape(key) {
+ var escapeRegex = /[=:]/g;
+ var escaperLookup = {
+ '=': '=0',
+ ':': '=2'
+ };
+ var escapedString = ('' + key).replace(escapeRegex, function (match) {
+ return escaperLookup[match];
+ });
+
+ return '$' + escapedString;
+}
+
+var userProvidedKeyEscapeRegex = /\/+/g;
+function escapeUserProvidedKey(text) {
+ return ('' + text).replace(userProvidedKeyEscapeRegex, '$&/');
+}
+
+var POOL_SIZE = 10;
+var traverseContextPool = [];
+function getPooledTraverseContext(mapResult, keyPrefix, mapFunction, mapContext) {
+ if (traverseContextPool.length) {
+ var traverseContext = traverseContextPool.pop();
+ traverseContext.result = mapResult;
+ traverseContext.keyPrefix = keyPrefix;
+ traverseContext.func = mapFunction;
+ traverseContext.context = mapContext;
+ traverseContext.count = 0;
+ return traverseContext;
+ } else {
+ return {
+ result: mapResult,
+ keyPrefix: keyPrefix,
+ func: mapFunction,
+ context: mapContext,
+ count: 0
+ };
+ }
+}
+
+function releaseTraverseContext(traverseContext) {
+ traverseContext.result = null;
+ traverseContext.keyPrefix = null;
+ traverseContext.func = null;
+ traverseContext.context = null;
+ traverseContext.count = 0;
+ if (traverseContextPool.length < POOL_SIZE) {
+ traverseContextPool.push(traverseContext);
+ }
+}
+
+/**
+ * @param {?*} children Children tree container.
+ * @param {!string} nameSoFar Name of the key path so far.
+ * @param {!function} callback Callback to invoke with each child found.
+ * @param {?*} traverseContext Used to pass information throughout the traversal
+ * process.
+ * @return {!number} The number of children in this subtree.
+ */
+function traverseAllChildrenImpl(children, nameSoFar, callback, traverseContext) {
+ var type = typeof children;
+
+ if (type === 'undefined' || type === 'boolean') {
+ // All of the above are perceived as null.
+ children = null;
+ }
+
+ var invokeCallback = false;
+
+ if (children === null) {
+ invokeCallback = true;
+ } else {
+ switch (type) {
+ case 'string':
+ case 'number':
+ invokeCallback = true;
+ break;
+ case 'object':
+ switch (children.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ case REACT_PORTAL_TYPE:
+ invokeCallback = true;
+ }
+ }
+ }
+
+ if (invokeCallback) {
+ callback(traverseContext, children,
+ // If it's the only child, treat the name as if it was wrapped in an array
+ // so that it's consistent if the number of children grows.
+ nameSoFar === '' ? SEPARATOR + getComponentKey(children, 0) : nameSoFar);
+ return 1;
+ }
+
+ var child = void 0;
+ var nextName = void 0;
+ var subtreeCount = 0; // Count of children found in the current subtree.
+ var nextNamePrefix = nameSoFar === '' ? SEPARATOR : nameSoFar + SUBSEPARATOR;
+
+ if (Array.isArray(children)) {
+ for (var i = 0; i < children.length; i++) {
+ child = children[i];
+ nextName = nextNamePrefix + getComponentKey(child, i);
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
+ }
+ } else {
+ var iteratorFn = getIteratorFn(children);
+ if (typeof iteratorFn === 'function') {
+ var iterator = iteratorFn.call(children);
+ var step = void 0;
+ var ii = 0;
+ while (!(step = iterator.next()).done) {
+ child = step.value;
+ nextName = nextNamePrefix + getComponentKey(child, ii++);
+ subtreeCount += traverseAllChildrenImpl(child, nextName, callback, traverseContext);
+ }
+ } else if (type === 'object') {
+ var addendum = '';
+ var childrenString = '' + children;
+ reactProdInvariant('31', childrenString === '[object Object]' ? 'object with keys {' + Object.keys(children).join(', ') + '}' : childrenString, addendum);
+ }
+ }
+
+ return subtreeCount;
+}
+
+/**
+ * Traverses children that are typically specified as `props.children`, but
+ * might also be specified through attributes:
+ *
+ * - `traverseAllChildren(this.props.children, ...)`
+ * - `traverseAllChildren(this.props.leftPanelChildren, ...)`
+ *
+ * The `traverseContext` is an optional argument that is passed through the
+ * entire traversal. It can be used to store accumulations or anything else that
+ * the callback might find relevant.
+ *
+ * @param {?*} children Children tree object.
+ * @param {!function} callback To invoke upon traversing each child.
+ * @param {?*} traverseContext Context for traversal.
+ * @return {!number} The number of children in this subtree.
+ */
+function traverseAllChildren(children, callback, traverseContext) {
+ if (children == null) {
+ return 0;
+ }
+
+ return traverseAllChildrenImpl(children, '', callback, traverseContext);
+}
+
+/**
+ * Generate a key string that identifies a component within a set.
+ *
+ * @param {*} component A component that could contain a manual key.
+ * @param {number} index Index that is used if a manual key is not provided.
+ * @return {string}
+ */
+function getComponentKey(component, index) {
+ // Do some typechecking here since we call this blindly. We want to ensure
+ // that we don't block potential future ES APIs.
+ if (typeof component === 'object' && component !== null && component.key != null) {
+ // Explicit key
+ return escape(component.key);
+ }
+ // Implicit key determined by the index in the set
+ return index.toString(36);
+}
+
+function forEachSingleChild(bookKeeping, child, name) {
+ var func = bookKeeping.func,
+ context = bookKeeping.context;
+
+ func.call(context, child, bookKeeping.count++);
+}
+
+/**
+ * Iterates through children that are typically specified as `props.children`.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrenforeach
+ *
+ * The provided forEachFunc(child, index) will be called for each
+ * leaf child.
+ *
+ * @param {?*} children Children tree container.
+ * @param {function(*, int)} forEachFunc
+ * @param {*} forEachContext Context for forEachContext.
+ */
+function forEachChildren(children, forEachFunc, forEachContext) {
+ if (children == null) {
+ return children;
+ }
+ var traverseContext = getPooledTraverseContext(null, null, forEachFunc, forEachContext);
+ traverseAllChildren(children, forEachSingleChild, traverseContext);
+ releaseTraverseContext(traverseContext);
+}
+
+function mapSingleChildIntoContext(bookKeeping, child, childKey) {
+ var result = bookKeeping.result,
+ keyPrefix = bookKeeping.keyPrefix,
+ func = bookKeeping.func,
+ context = bookKeeping.context;
+
+
+ var mappedChild = func.call(context, child, bookKeeping.count++);
+ if (Array.isArray(mappedChild)) {
+ mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, function (c) {
+ return c;
+ });
+ } else if (mappedChild != null) {
+ if (isValidElement(mappedChild)) {
+ mappedChild = cloneAndReplaceKey(mappedChild,
+ // Keep both the (mapped) and old keys if they differ, just as
+ // traverseAllChildren used to do for objects as children
+ keyPrefix + (mappedChild.key && (!child || child.key !== mappedChild.key) ? escapeUserProvidedKey(mappedChild.key) + '/' : '') + childKey);
+ }
+ result.push(mappedChild);
+ }
+}
+
+function mapIntoWithKeyPrefixInternal(children, array, prefix, func, context) {
+ var escapedPrefix = '';
+ if (prefix != null) {
+ escapedPrefix = escapeUserProvidedKey(prefix) + '/';
+ }
+ var traverseContext = getPooledTraverseContext(array, escapedPrefix, func, context);
+ traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
+ releaseTraverseContext(traverseContext);
+}
+
+/**
+ * Maps children that are typically specified as `props.children`.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrenmap
+ *
+ * The provided mapFunction(child, key, index) will be called for each
+ * leaf child.
+ *
+ * @param {?*} children Children tree container.
+ * @param {function(*, int)} func The map function.
+ * @param {*} context Context for mapFunction.
+ * @return {object} Object containing the ordered map of results.
+ */
+function mapChildren(children, func, context) {
+ if (children == null) {
+ return children;
+ }
+ var result = [];
+ mapIntoWithKeyPrefixInternal(children, result, null, func, context);
+ return result;
+}
+
+/**
+ * Count the number of children that are typically specified as
+ * `props.children`.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrencount
+ *
+ * @param {?*} children Children tree container.
+ * @return {number} The number of children.
+ */
+function countChildren(children) {
+ return traverseAllChildren(children, function () {
+ return null;
+ }, null);
+}
+
+/**
+ * Flatten a children object (typically specified as `props.children`) and
+ * return an array with appropriately re-keyed children.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrentoarray
+ */
+function toArray(children) {
+ var result = [];
+ mapIntoWithKeyPrefixInternal(children, result, null, function (child) {
+ return child;
+ });
+ return result;
+}
+
+/**
+ * Returns the first child in a collection of children and verifies that there
+ * is only one child in the collection.
+ *
+ * See https://reactjs.org/docs/react-api.html#reactchildrenonly
+ *
+ * The current implementation of this function assumes that a single child gets
+ * passed without a wrapper, but the purpose of this helper function is to
+ * abstract away the particular structure of children.
+ *
+ * @param {?object} children Child collection structure.
+ * @return {ReactElement} The first and only `ReactElement` contained in the
+ * structure.
+ */
+function onlyChild(children) {
+ !isValidElement(children) ? reactProdInvariant('143') : void 0;
+ return children;
+}
+
+function createContext(defaultValue, calculateChangedBits) {
+ if (calculateChangedBits === undefined) {
+ calculateChangedBits = null;
+ } else {
+
+ }
+
+ var context = {
+ $$typeof: REACT_CONTEXT_TYPE,
+ _calculateChangedBits: calculateChangedBits,
+ // As a workaround to support multiple concurrent renderers, we categorize
+ // some renderers as primary and others as secondary. We only expect
+ // there to be two concurrent renderers at most: React Native (primary) and
+ // Fabric (secondary); React DOM (primary) and React ART (secondary).
+ // Secondary renderers store their context values on separate fields.
+ _currentValue: defaultValue,
+ _currentValue2: defaultValue,
+ // Used to track how many concurrent renderers this context currently
+ // supports within in a single renderer. Such as parallel server rendering.
+ _threadCount: 0,
+ // These are circular
+ Provider: null,
+ Consumer: null
+ };
+
+ context.Provider = {
+ $$typeof: REACT_PROVIDER_TYPE,
+ _context: context
+ };
+
+ {
+ context.Consumer = context;
+ }
+
+ return context;
+}
+
+function lazy(ctor) {
+ var lazyType = {
+ $$typeof: REACT_LAZY_TYPE,
+ _ctor: ctor,
+ // React uses these fields to store the result.
+ _status: -1,
+ _result: null
+ };
+
+ return lazyType;
+}
+
+function forwardRef(render) {
+ return {
+ $$typeof: REACT_FORWARD_REF_TYPE,
+ render: render
+ };
+}
+
+function memo(type, compare) {
+ return {
+ $$typeof: REACT_MEMO_TYPE,
+ type: type,
+ compare: compare === undefined ? null : compare
+ };
+}
+
+function resolveDispatcher() {
+ var dispatcher = ReactCurrentDispatcher.current;
+ !(dispatcher !== null) ? reactProdInvariant('321') : void 0;
+ return dispatcher;
+}
+
+function useContext(Context, unstable_observedBits) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useContext(Context, unstable_observedBits);
+}
+
+function useState(initialState) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useState(initialState);
+}
+
+function useReducer(reducer, initialArg, init) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useReducer(reducer, initialArg, init);
+}
+
+function useRef(initialValue) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useRef(initialValue);
+}
+
+function useEffect(create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useEffect(create, inputs);
+}
+
+function useLayoutEffect(create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useLayoutEffect(create, inputs);
+}
+
+function useCallback(callback, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useCallback(callback, inputs);
+}
+
+function useMemo(create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useMemo(create, inputs);
+}
+
+function useImperativeHandle(ref, create, inputs) {
+ var dispatcher = resolveDispatcher();
+ return dispatcher.useImperativeHandle(ref, create, inputs);
+}
+
+function useDebugValue(value, formatterFn) {
+
+}
+
+/**
+ * Copyright (c) 2013-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+/**
+ * ReactElementValidator provides a wrapper around a element factory
+ * which validates the props passed to the element. This is intended to be
+ * used only in DEV and could be replaced by a static type checker for languages
+ * that support it.
+ */
+
+var React = {
+ Children: {
+ map: mapChildren,
+ forEach: forEachChildren,
+ count: countChildren,
+ toArray: toArray,
+ only: onlyChild
+ },
+
+ createRef: createRef,
+ Component: Component,
+ PureComponent: PureComponent,
+
+ createContext: createContext,
+ forwardRef: forwardRef,
+ lazy: lazy,
+ memo: memo,
+
+ useCallback: useCallback,
+ useContext: useContext,
+ useEffect: useEffect,
+ useImperativeHandle: useImperativeHandle,
+ useDebugValue: useDebugValue,
+ useLayoutEffect: useLayoutEffect,
+ useMemo: useMemo,
+ useReducer: useReducer,
+ useRef: useRef,
+ useState: useState,
+
+ Fragment: REACT_FRAGMENT_TYPE,
+ StrictMode: REACT_STRICT_MODE_TYPE,
+ Suspense: REACT_SUSPENSE_TYPE,
+
+ createElement: createElement,
+ cloneElement: cloneElement,
+ createFactory: createFactory,
+ isValidElement: isValidElement,
+
+ version: ReactVersion,
+
+ unstable_ConcurrentMode: REACT_CONCURRENT_MODE_TYPE,
+ unstable_Profiler: REACT_PROFILER_TYPE,
+
+ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals
+};
+
+// Note: some APIs are added with feature flags.
+// Make sure that stable builds for open source
+// don't modify the React object to avoid deopts.
+// Also let's not expose their names in stable builds.
+
+if (enableStableConcurrentModeAPIs) {
+ React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
+ React.Profiler = REACT_PROFILER_TYPE;
+ React.unstable_ConcurrentMode = undefined;
+ React.unstable_Profiler = undefined;
+}
+
+
+
+var React$2 = ({
+ default: React
+});
+
+var React$3 = ( React$2 && React ) || React$2;
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var react = React$3.default || React$3;
+
+return react;
+
+})));
diff --git a/devtools/client/shared/vendor/redux.js b/devtools/client/shared/vendor/redux.js
new file mode 100644
index 0000000000..b5468b55bd
--- /dev/null
+++ b/devtools/client/shared/vendor/redux.js
@@ -0,0 +1,715 @@
+/**
+ * react-redux v4.0.5
+ */
+(function (global, factory) {
+typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+typeof define === 'function' && define.amd ? define(['exports'], factory) :
+(global = global || self, factory(global.Redux = {}));
+}(this, function (exports) { 'use strict';
+
+function symbolObservablePonyfill(root) {
+ var result;
+ var Symbol = root.Symbol;
+
+ if (typeof Symbol === 'function') {
+ if (Symbol.observable) {
+ result = Symbol.observable;
+ } else {
+ result = Symbol('observable');
+ Symbol.observable = result;
+ }
+ } else {
+ result = '@@observable';
+ }
+
+ return result;
+}
+
+/* global window */
+
+var root;
+
+if (typeof self !== 'undefined') {
+ root = self;
+} else if (typeof window !== 'undefined') {
+ root = window;
+} else if (typeof global !== 'undefined') {
+ root = global;
+} else if (typeof module !== 'undefined') {
+ root = module;
+} else {
+ root = globalThis;
+}
+
+var result = symbolObservablePonyfill(root);
+
+/**
+ * These are private action types reserved by Redux.
+ * For any unknown actions, you must return the current state.
+ * If the current state is undefined, you must return the initial state.
+ * Do not reference these action types directly in your code.
+ */
+var randomString = function randomString() {
+ return Math.random().toString(36).substring(7).split('').join('.');
+};
+
+var ActionTypes = {
+ INIT: "@@redux/INIT" + randomString(),
+ REPLACE: "@@redux/REPLACE" + randomString(),
+ PROBE_UNKNOWN_ACTION: function PROBE_UNKNOWN_ACTION() {
+ return "@@redux/PROBE_UNKNOWN_ACTION" + randomString();
+ }
+};
+
+/**
+ * @param {any} obj The object to inspect.
+ * @returns {boolean} True if the argument appears to be a plain object.
+ */
+function isPlainObject(obj) {
+ if (typeof obj !== 'object' || obj === null) return false;
+ var proto = obj;
+
+ while (Object.getPrototypeOf(proto) !== null) {
+ proto = Object.getPrototypeOf(proto);
+ }
+
+ return Object.getPrototypeOf(obj) === proto;
+}
+
+/**
+ * Creates a Redux store that holds the state tree.
+ * The only way to change the data in the store is to call `dispatch()` on it.
+ *
+ * There should only be a single store in your app. To specify how different
+ * parts of the state tree respond to actions, you may combine several reducers
+ * into a single reducer function by using `combineReducers`.
+ *
+ * @param {Function} reducer A function that returns the next state tree, given
+ * the current state tree and the action to handle.
+ *
+ * @param {any} [preloadedState] The initial state. You may optionally specify it
+ * to hydrate the state from the server in universal apps, or to restore a
+ * previously serialized user session.
+ * If you use `combineReducers` to produce the root reducer function, this must be
+ * an object with the same shape as `combineReducers` keys.
+ *
+ * @param {Function} [enhancer] The store enhancer. You may optionally specify it
+ * to enhance the store with third-party capabilities such as middleware,
+ * time travel, persistence, etc. The only store enhancer that ships with Redux
+ * is `applyMiddleware()`.
+ *
+ * @returns {Store} A Redux store that lets you read the state, dispatch actions
+ * and subscribe to changes.
+ */
+
+function createStore(reducer, preloadedState, enhancer) {
+ var _ref2;
+
+ if (typeof preloadedState === 'function' && typeof enhancer === 'function' || typeof enhancer === 'function' && typeof arguments[3] === 'function') {
+ throw new Error('It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function.');
+ }
+
+ if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
+ enhancer = preloadedState;
+ preloadedState = undefined;
+ }
+
+ if (typeof enhancer !== 'undefined') {
+ if (typeof enhancer !== 'function') {
+ throw new Error('Expected the enhancer to be a function.');
+ }
+
+ return enhancer(createStore)(reducer, preloadedState);
+ }
+
+ if (typeof reducer !== 'function') {
+ throw new Error('Expected the reducer to be a function.');
+ }
+
+ var currentReducer = reducer;
+ var currentState = preloadedState;
+ var currentListeners = [];
+ var nextListeners = currentListeners;
+ var isDispatching = false;
+ /**
+ * This makes a shallow copy of currentListeners so we can use
+ * nextListeners as a temporary list while dispatching.
+ *
+ * This prevents any bugs around consumers calling
+ * subscribe/unsubscribe in the middle of a dispatch.
+ */
+
+ function ensureCanMutateNextListeners() {
+ if (nextListeners === currentListeners) {
+ nextListeners = currentListeners.slice();
+ }
+ }
+ /**
+ * Reads the state tree managed by the store.
+ *
+ * @returns {any} The current state tree of your application.
+ */
+
+
+ function getState() {
+ if (isDispatching) {
+ throw new Error('You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.');
+ }
+
+ return currentState;
+ }
+ /**
+ * Adds a change listener. It will be called any time an action is dispatched,
+ * and some part of the state tree may potentially have changed. You may then
+ * call `getState()` to read the current state tree inside the callback.
+ *
+ * You may call `dispatch()` from a change listener, with the following
+ * caveats:
+ *
+ * 1. The subscriptions are snapshotted just before every `dispatch()` call.
+ * If you subscribe or unsubscribe while the listeners are being invoked, this
+ * will not have any effect on the `dispatch()` that is currently in progress.
+ * However, the next `dispatch()` call, whether nested or not, will use a more
+ * recent snapshot of the subscription list.
+ *
+ * 2. The listener should not expect to see all state changes, as the state
+ * might have been updated multiple times during a nested `dispatch()` before
+ * the listener is called. It is, however, guaranteed that all subscribers
+ * registered before the `dispatch()` started will be called with the latest
+ * state by the time it exits.
+ *
+ * @param {Function} listener A callback to be invoked on every dispatch.
+ * @returns {Function} A function to remove this change listener.
+ */
+
+
+ function subscribe(listener) {
+ if (typeof listener !== 'function') {
+ throw new Error('Expected the listener to be a function.');
+ }
+
+ if (isDispatching) {
+ throw new Error('You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.');
+ }
+
+ var isSubscribed = true;
+ ensureCanMutateNextListeners();
+ nextListeners.push(listener);
+ return function unsubscribe() {
+ if (!isSubscribed) {
+ return;
+ }
+
+ if (isDispatching) {
+ throw new Error('You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.');
+ }
+
+ isSubscribed = false;
+ ensureCanMutateNextListeners();
+ var index = nextListeners.indexOf(listener);
+ nextListeners.splice(index, 1);
+ currentListeners = null;
+ };
+ }
+ /**
+ * Dispatches an action. It is the only way to trigger a state change.
+ *
+ * The `reducer` function, used to create the store, will be called with the
+ * current state tree and the given `action`. Its return value will
+ * be considered the **next** state of the tree, and the change listeners
+ * will be notified.
+ *
+ * The base implementation only supports plain object actions. If you want to
+ * dispatch a Promise, an Observable, a thunk, or something else, you need to
+ * wrap your store creating function into the corresponding middleware. For
+ * example, see the documentation for the `redux-thunk` package. Even the
+ * middleware will eventually dispatch plain object actions using this method.
+ *
+ * @param {Object} action A plain object representing “what changed”. It is
+ * a good idea to keep actions serializable so you can record and replay user
+ * sessions, or use the time travelling `redux-devtools`. An action must have
+ * a `type` property which may not be `undefined`. It is a good idea to use
+ * string constants for action types.
+ *
+ * @returns {Object} For convenience, the same action object you dispatched.
+ *
+ * Note that, if you use a custom middleware, it may wrap `dispatch()` to
+ * return something else (for example, a Promise you can await).
+ */
+
+
+ function dispatch(action) {
+ if (!isPlainObject(action)) {
+ throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
+ }
+
+ if (typeof action.type === 'undefined') {
+ throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
+ }
+
+ if (isDispatching) {
+ throw new Error('Reducers may not dispatch actions.');
+ }
+
+ try {
+ isDispatching = true;
+ currentState = currentReducer(currentState, action);
+ } finally {
+ isDispatching = false;
+ }
+
+ var listeners = currentListeners = nextListeners;
+
+ for (var i = 0; i < listeners.length; i++) {
+ var listener = listeners[i];
+ listener();
+ }
+
+ return action;
+ }
+ /**
+ * Replaces the reducer currently used by the store to calculate the state.
+ *
+ * You might need this if your app implements code splitting and you want to
+ * load some of the reducers dynamically. You might also need this if you
+ * implement a hot reloading mechanism for Redux.
+ *
+ * @param {Function} nextReducer The reducer for the store to use instead.
+ * @returns {void}
+ */
+
+
+ function replaceReducer(nextReducer) {
+ if (typeof nextReducer !== 'function') {
+ throw new Error('Expected the nextReducer to be a function.');
+ }
+
+ currentReducer = nextReducer; // This action has a similiar effect to ActionTypes.INIT.
+ // Any reducers that existed in both the new and old rootReducer
+ // will receive the previous state. This effectively populates
+ // the new state tree with any relevant data from the old one.
+
+ dispatch({
+ type: ActionTypes.REPLACE
+ });
+ }
+ /**
+ * Interoperability point for observable/reactive libraries.
+ * @returns {observable} A minimal observable of state changes.
+ * For more information, see the observable proposal:
+ * https://github.com/tc39/proposal-observable
+ */
+
+
+ function observable() {
+ var _ref;
+
+ var outerSubscribe = subscribe;
+ return _ref = {
+ /**
+ * The minimal observable subscription method.
+ * @param {Object} observer Any object that can be used as an observer.
+ * The observer object should have a `next` method.
+ * @returns {subscription} An object with an `unsubscribe` method that can
+ * be used to unsubscribe the observable from the store, and prevent further
+ * emission of values from the observable.
+ */
+ subscribe: function subscribe(observer) {
+ if (typeof observer !== 'object' || observer === null) {
+ throw new TypeError('Expected the observer to be an object.');
+ }
+
+ function observeState() {
+ if (observer.next) {
+ observer.next(getState());
+ }
+ }
+
+ observeState();
+ var unsubscribe = outerSubscribe(observeState);
+ return {
+ unsubscribe: unsubscribe
+ };
+ }
+ }, _ref[result] = function () {
+ return this;
+ }, _ref;
+ } // When a store is created, an "INIT" action is dispatched so that every
+ // reducer returns their initial state. This effectively populates
+ // the initial state tree.
+
+
+ dispatch({
+ type: ActionTypes.INIT
+ });
+ return _ref2 = {
+ dispatch: dispatch,
+ subscribe: subscribe,
+ getState: getState,
+ replaceReducer: replaceReducer
+ }, _ref2[result] = observable, _ref2;
+}
+
+/**
+ * Prints a warning in the console if it exists.
+ *
+ * @param {String} message The warning message.
+ * @returns {void}
+ */
+function warning(message) {
+ /* eslint-disable no-console */
+ if (typeof console !== 'undefined' && typeof console.error === 'function') {
+ console.error(message);
+ }
+ /* eslint-enable no-console */
+
+
+ try {
+ // This error was thrown as a convenience so that if you enable
+ // "break on all exceptions" in your console,
+ // it would pause the execution at this line.
+ throw new Error(message);
+ } catch (e) {} // eslint-disable-line no-empty
+
+}
+
+function getUndefinedStateErrorMessage(key, action) {
+ var actionType = action && action.type;
+ var actionDescription = actionType && "action \"" + String(actionType) + "\"" || 'an action';
+ return "Given " + actionDescription + ", reducer \"" + key + "\" returned undefined. " + "To ignore an action, you must explicitly return the previous state. " + "If you want this reducer to hold no value, you can return null instead of undefined.";
+}
+
+function getUnexpectedStateShapeWarningMessage(inputState, reducers, action, unexpectedKeyCache) {
+ var reducerKeys = Object.keys(reducers);
+ var argumentName = action && action.type === ActionTypes.INIT ? 'preloadedState argument passed to createStore' : 'previous state received by the reducer';
+
+ if (reducerKeys.length === 0) {
+ return 'Store does not have a valid reducer. Make sure the argument passed ' + 'to combineReducers is an object whose values are reducers.';
+ }
+
+ if (!isPlainObject(inputState)) {
+ return "The " + argumentName + " has unexpected type of \"" + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + "\". Expected argument to be an object with the following " + ("keys: \"" + reducerKeys.join('", "') + "\"");
+ }
+
+ var unexpectedKeys = Object.keys(inputState).filter(function (key) {
+ return !reducers.hasOwnProperty(key) && !unexpectedKeyCache[key];
+ });
+ unexpectedKeys.forEach(function (key) {
+ unexpectedKeyCache[key] = true;
+ });
+ if (action && action.type === ActionTypes.REPLACE) return;
+
+ if (unexpectedKeys.length > 0) {
+ return "Unexpected " + (unexpectedKeys.length > 1 ? 'keys' : 'key') + " " + ("\"" + unexpectedKeys.join('", "') + "\" found in " + argumentName + ". ") + "Expected to find one of the known reducer keys instead: " + ("\"" + reducerKeys.join('", "') + "\". Unexpected keys will be ignored.");
+ }
+}
+
+function assertReducerShape(reducers) {
+ Object.keys(reducers).forEach(function (key) {
+ var reducer = reducers[key];
+ var initialState = reducer(undefined, {
+ type: ActionTypes.INIT
+ });
+
+ if (typeof initialState === 'undefined') {
+ throw new Error("Reducer \"" + key + "\" returned undefined during initialization. " + "If the state passed to the reducer is undefined, you must " + "explicitly return the initial state. The initial state may " + "not be undefined. If you don't want to set a value for this reducer, " + "you can use null instead of undefined.");
+ }
+
+ if (typeof reducer(undefined, {
+ type: ActionTypes.PROBE_UNKNOWN_ACTION()
+ }) === 'undefined') {
+ throw new Error("Reducer \"" + key + "\" returned undefined when probed with a random type. " + ("Don't try to handle " + ActionTypes.INIT + " or other actions in \"redux/*\" ") + "namespace. They are considered private. Instead, you must return the " + "current state for any unknown actions, unless it is undefined, " + "in which case you must return the initial state, regardless of the " + "action type. The initial state may not be undefined, but can be null.");
+ }
+ });
+}
+/**
+ * Turns an object whose values are different reducer functions, into a single
+ * reducer function. It will call every child reducer, and gather their results
+ * into a single state object, whose keys correspond to the keys of the passed
+ * reducer functions.
+ *
+ * @param {Object} reducers An object whose values correspond to different
+ * reducer functions that need to be combined into one. One handy way to obtain
+ * it is to use ES6 `import * as reducers` syntax. The reducers may never return
+ * undefined for any action. Instead, they should return their initial state
+ * if the state passed to them was undefined, and the current state for any
+ * unrecognized action.
+ *
+ * @returns {Function} A reducer function that invokes every reducer inside the
+ * passed object, and builds a state object with the same shape.
+ */
+
+
+function combineReducers(reducers) {
+ var reducerKeys = Object.keys(reducers);
+ var finalReducers = {};
+
+ for (var i = 0; i < reducerKeys.length; i++) {
+ var key = reducerKeys[i];
+
+ {
+ if (typeof reducers[key] === 'undefined') {
+ warning("No reducer provided for key \"" + key + "\"");
+ }
+ }
+
+ if (typeof reducers[key] === 'function') {
+ finalReducers[key] = reducers[key];
+ }
+ }
+
+ var finalReducerKeys = Object.keys(finalReducers); // This is used to make sure we don't warn about the same
+ // keys multiple times.
+
+ var unexpectedKeyCache;
+
+ {
+ unexpectedKeyCache = {};
+ }
+
+ var shapeAssertionError;
+
+ try {
+ assertReducerShape(finalReducers);
+ } catch (e) {
+ shapeAssertionError = e;
+ }
+
+ return function combination(state, action) {
+ if (state === void 0) {
+ state = {};
+ }
+
+ if (shapeAssertionError) {
+ throw shapeAssertionError;
+ }
+
+ {
+ var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
+
+ if (warningMessage) {
+ warning(warningMessage);
+ }
+ }
+
+ var hasChanged = false;
+ var nextState = {};
+
+ for (var _i = 0; _i < finalReducerKeys.length; _i++) {
+ var _key = finalReducerKeys[_i];
+ var reducer = finalReducers[_key];
+ var previousStateForKey = state[_key];
+ var nextStateForKey = reducer(previousStateForKey, action);
+
+ if (typeof nextStateForKey === 'undefined') {
+ var errorMessage = getUndefinedStateErrorMessage(_key, action);
+ throw new Error(errorMessage);
+ }
+
+ nextState[_key] = nextStateForKey;
+ hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
+ }
+
+ hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;
+ return hasChanged ? nextState : state;
+ };
+}
+
+function bindActionCreator(actionCreator, dispatch) {
+ return function () {
+ return dispatch(actionCreator.apply(this, arguments));
+ };
+}
+/**
+ * Turns an object whose values are action creators, into an object with the
+ * same keys, but with every function wrapped into a `dispatch` call so they
+ * may be invoked directly. This is just a convenience method, as you can call
+ * `store.dispatch(MyActionCreators.doSomething())` yourself just fine.
+ *
+ * For convenience, you can also pass an action creator as the first argument,
+ * and get a dispatch wrapped function in return.
+ *
+ * @param {Function|Object} actionCreators An object whose values are action
+ * creator functions. One handy way to obtain it is to use ES6 `import * as`
+ * syntax. You may also pass a single function.
+ *
+ * @param {Function} dispatch The `dispatch` function available on your Redux
+ * store.
+ *
+ * @returns {Function|Object} The object mimicking the original object, but with
+ * every action creator wrapped into the `dispatch` call. If you passed a
+ * function as `actionCreators`, the return value will also be a single
+ * function.
+ */
+
+
+function bindActionCreators(actionCreators, dispatch) {
+ if (typeof actionCreators === 'function') {
+ return bindActionCreator(actionCreators, dispatch);
+ }
+
+ if (typeof actionCreators !== 'object' || actionCreators === null) {
+ throw new Error("bindActionCreators expected an object or a function, instead received " + (actionCreators === null ? 'null' : typeof actionCreators) + ". " + "Did you write \"import ActionCreators from\" instead of \"import * as ActionCreators from\"?");
+ }
+
+ var boundActionCreators = {};
+
+ for (var key in actionCreators) {
+ var actionCreator = actionCreators[key];
+
+ if (typeof actionCreator === 'function') {
+ boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
+ }
+ }
+
+ return boundActionCreators;
+}
+
+function _defineProperty(obj, key, value) {
+ if (key in obj) {
+ Object.defineProperty(obj, key, {
+ value: value,
+ enumerable: true,
+ configurable: true,
+ writable: true
+ });
+ } else {
+ obj[key] = value;
+ }
+
+ return obj;
+}
+
+function ownKeys(object, enumerableOnly) {
+ var keys = Object.keys(object);
+
+ if (Object.getOwnPropertySymbols) {
+ keys.push.apply(keys, Object.getOwnPropertySymbols(object));
+ }
+
+ if (enumerableOnly) keys = keys.filter(function (sym) {
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
+ });
+ return keys;
+}
+
+function _objectSpread2(target) {
+ for (var i = 1; i < arguments.length; i++) {
+ var source = arguments[i] != null ? arguments[i] : {};
+
+ if (i % 2) {
+ ownKeys(source, true).forEach(function (key) {
+ _defineProperty(target, key, source[key]);
+ });
+ } else if (Object.getOwnPropertyDescriptors) {
+ Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
+ } else {
+ ownKeys(source).forEach(function (key) {
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
+ });
+ }
+ }
+
+ return target;
+}
+
+/**
+ * Composes single-argument functions from right to left. The rightmost
+ * function can take multiple arguments as it provides the signature for
+ * the resulting composite function.
+ *
+ * @param {...Function} funcs The functions to compose.
+ * @returns {Function} A function obtained by composing the argument functions
+ * from right to left. For example, compose(f, g, h) is identical to doing
+ * (...args) => f(g(h(...args))).
+ */
+function compose() {
+ for (var _len = arguments.length, funcs = new Array(_len), _key = 0; _key < _len; _key++) {
+ funcs[_key] = arguments[_key];
+ }
+
+ if (funcs.length === 0) {
+ return function (arg) {
+ return arg;
+ };
+ }
+
+ if (funcs.length === 1) {
+ return funcs[0];
+ }
+
+ return funcs.reduce(function (a, b) {
+ return function () {
+ return a(b.apply(void 0, arguments));
+ };
+ });
+}
+
+/**
+ * Creates a store enhancer that applies middleware to the dispatch method
+ * of the Redux store. This is handy for a variety of tasks, such as expressing
+ * asynchronous actions in a concise manner, or logging every action payload.
+ *
+ * See `redux-thunk` package as an example of the Redux middleware.
+ *
+ * Because middleware is potentially asynchronous, this should be the first
+ * store enhancer in the composition chain.
+ *
+ * Note that each middleware will be given the `dispatch` and `getState` functions
+ * as named arguments.
+ *
+ * @param {...Function} middlewares The middleware chain to be applied.
+ * @returns {Function} A store enhancer applying the middleware.
+ */
+
+function applyMiddleware() {
+ for (var _len = arguments.length, middlewares = new Array(_len), _key = 0; _key < _len; _key++) {
+ middlewares[_key] = arguments[_key];
+ }
+
+ return function (createStore) {
+ return function () {
+ var store = createStore.apply(void 0, arguments);
+
+ var _dispatch = function dispatch() {
+ throw new Error('Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.');
+ };
+
+ var middlewareAPI = {
+ getState: store.getState,
+ dispatch: function dispatch() {
+ return _dispatch.apply(void 0, arguments);
+ }
+ };
+ var chain = middlewares.map(function (middleware) {
+ return middleware(middlewareAPI);
+ });
+ _dispatch = compose.apply(void 0, chain)(store.dispatch);
+ return _objectSpread2({}, store, {
+ dispatch: _dispatch
+ });
+ };
+ };
+}
+
+/*
+ * This is a dummy function to check if the function name has been altered by minification.
+ * If the function has been minified and NODE_ENV !== 'production', warn the user.
+ */
+
+function isCrushed() {}
+
+if ( typeof isCrushed.name === 'string' && isCrushed.name !== 'isCrushed') {
+ warning('You are currently using minified code outside of NODE_ENV === "production". ' + 'This means that you are running a slower development build of Redux. ' + 'You can use loose-envify (https://github.com/zertosh/loose-envify) for browserify ' + 'or setting mode to production in webpack (https://webpack.js.org/concepts/mode/) ' + 'to ensure you have the correct code for your production build.');
+}
+
+exports.__DO_NOT_USE__ActionTypes = ActionTypes;
+exports.applyMiddleware = applyMiddleware;
+exports.bindActionCreators = bindActionCreators;
+exports.combineReducers = combineReducers;
+exports.compose = compose;
+exports.createStore = createStore;
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/devtools/client/shared/vendor/reselect.js b/devtools/client/shared/vendor/reselect.js
new file mode 100644
index 0000000000..9aa421edbc
--- /dev/null
+++ b/devtools/client/shared/vendor/reselect.js
@@ -0,0 +1,291 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Reselect = {}));
+})(this, (function (exports) { 'use strict';
+
+ // Cache implementation based on Erik Rasmussen's `lru-memoize`:
+ // https://github.com/erikras/lru-memoize
+ var NOT_FOUND = 'NOT_FOUND';
+
+ function createSingletonCache(equals) {
+ var entry;
+ return {
+ get: function get(key) {
+ if (entry && equals(entry.key, key)) {
+ return entry.value;
+ }
+
+ return NOT_FOUND;
+ },
+ put: function put(key, value) {
+ entry = {
+ key: key,
+ value: value
+ };
+ },
+ getEntries: function getEntries() {
+ return entry ? [entry] : [];
+ },
+ clear: function clear() {
+ entry = undefined;
+ }
+ };
+ }
+
+ function createLruCache(maxSize, equals) {
+ var entries = [];
+
+ function get(key) {
+ var cacheIndex = entries.findIndex(function (entry) {
+ return equals(key, entry.key);
+ }); // We found a cached entry
+
+ if (cacheIndex > -1) {
+ var entry = entries[cacheIndex]; // Cached entry not at top of cache, move it to the top
+
+ if (cacheIndex > 0) {
+ entries.splice(cacheIndex, 1);
+ entries.unshift(entry);
+ }
+
+ return entry.value;
+ } // No entry found in cache, return sentinel
+
+
+ return NOT_FOUND;
+ }
+
+ function put(key, value) {
+ if (get(key) === NOT_FOUND) {
+ // TODO Is unshift slow?
+ entries.unshift({
+ key: key,
+ value: value
+ });
+
+ if (entries.length > maxSize) {
+ entries.pop();
+ }
+ }
+ }
+
+ function getEntries() {
+ return entries;
+ }
+
+ function clear() {
+ entries = [];
+ }
+
+ return {
+ get: get,
+ put: put,
+ getEntries: getEntries,
+ clear: clear
+ };
+ }
+
+ var defaultEqualityCheck = function defaultEqualityCheck(a, b) {
+ return a === b;
+ };
+ function createCacheKeyComparator(equalityCheck) {
+ return function areArgumentsShallowlyEqual(prev, next) {
+ if (prev === null || next === null || prev.length !== next.length) {
+ return false;
+ } // Do this in a for loop (and not a `forEach` or an `every`) so we can determine equality as fast as possible.
+
+
+ var length = prev.length;
+
+ for (var i = 0; i < length; i++) {
+ if (!equalityCheck(prev[i], next[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+ }
+ // defaultMemoize now supports a configurable cache size with LRU behavior,
+ // and optional comparison of the result value with existing values
+ function defaultMemoize(func, equalityCheckOrOptions) {
+ var providedOptions = typeof equalityCheckOrOptions === 'object' ? equalityCheckOrOptions : {
+ equalityCheck: equalityCheckOrOptions
+ };
+ var _providedOptions$equa = providedOptions.equalityCheck,
+ equalityCheck = _providedOptions$equa === void 0 ? defaultEqualityCheck : _providedOptions$equa,
+ _providedOptions$maxS = providedOptions.maxSize,
+ maxSize = _providedOptions$maxS === void 0 ? 1 : _providedOptions$maxS,
+ resultEqualityCheck = providedOptions.resultEqualityCheck;
+ var comparator = createCacheKeyComparator(equalityCheck);
+ var cache = maxSize === 1 ? createSingletonCache(comparator) : createLruCache(maxSize, comparator); // we reference arguments instead of spreading them for performance reasons
+
+ function memoized() {
+ var value = cache.get(arguments);
+
+ if (value === NOT_FOUND) {
+ // @ts-ignore
+ value = func.apply(null, arguments);
+
+ if (resultEqualityCheck) {
+ var entries = cache.getEntries();
+ var matchingEntry = entries.find(function (entry) {
+ return resultEqualityCheck(entry.value, value);
+ });
+
+ if (matchingEntry) {
+ value = matchingEntry.value;
+ }
+ }
+
+ cache.put(arguments, value);
+ }
+
+ return value;
+ }
+
+ memoized.clearCache = function () {
+ return cache.clear();
+ };
+
+ return memoized;
+ }
+
+ function getDependencies(funcs) {
+ var dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs;
+
+ if (!dependencies.every(function (dep) {
+ return typeof dep === 'function';
+ })) {
+ var dependencyTypes = dependencies.map(function (dep) {
+ return typeof dep === 'function' ? "function " + (dep.name || 'unnamed') + "()" : typeof dep;
+ }).join(', ');
+ throw new Error("createSelector expects all input-selectors to be functions, but received the following types: [" + dependencyTypes + "]");
+ }
+
+ return dependencies;
+ }
+
+ function createSelectorCreator(memoize) {
+ for (var _len = arguments.length, memoizeOptionsFromArgs = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+ memoizeOptionsFromArgs[_key - 1] = arguments[_key];
+ }
+
+ var createSelector = function createSelector() {
+ for (var _len2 = arguments.length, funcs = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+ funcs[_key2] = arguments[_key2];
+ }
+
+ var _recomputations = 0;
+
+ var _lastResult; // Due to the intricacies of rest params, we can't do an optional arg after `...funcs`.
+ // So, start by declaring the default value here.
+ // (And yes, the words 'memoize' and 'options' appear too many times in this next sequence.)
+
+
+ var directlyPassedOptions = {
+ memoizeOptions: undefined
+ }; // Normally, the result func or "output selector" is the last arg
+
+ var resultFunc = funcs.pop(); // If the result func is actually an _object_, assume it's our options object
+
+ if (typeof resultFunc === 'object') {
+ directlyPassedOptions = resultFunc; // and pop the real result func off
+
+ resultFunc = funcs.pop();
+ }
+
+ if (typeof resultFunc !== 'function') {
+ throw new Error("createSelector expects an output function after the inputs, but received: [" + typeof resultFunc + "]");
+ } // Determine which set of options we're using. Prefer options passed directly,
+ // but fall back to options given to createSelectorCreator.
+
+
+ var _directlyPassedOption = directlyPassedOptions,
+ _directlyPassedOption2 = _directlyPassedOption.memoizeOptions,
+ memoizeOptions = _directlyPassedOption2 === void 0 ? memoizeOptionsFromArgs : _directlyPassedOption2; // Simplifying assumption: it's unlikely that the first options arg of the provided memoizer
+ // is an array. In most libs I've looked at, it's an equality function or options object.
+ // Based on that, if `memoizeOptions` _is_ an array, we assume it's a full
+ // user-provided array of options. Otherwise, it must be just the _first_ arg, and so
+ // we wrap it in an array so we can apply it.
+
+ var finalMemoizeOptions = Array.isArray(memoizeOptions) ? memoizeOptions : [memoizeOptions];
+ var dependencies = getDependencies(funcs);
+ var memoizedResultFunc = memoize.apply(void 0, [function () {
+ _recomputations++; // apply arguments instead of spreading for performance.
+
+ return resultFunc.apply(null, arguments);
+ }].concat(finalMemoizeOptions)); // If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
+
+ var selector = memoize(function () {
+ var params = [];
+ var length = dependencies.length;
+
+ for (var i = 0; i < length; i++) {
+ // apply arguments instead of spreading and mutate a local list of params for performance.
+ // @ts-ignore
+ params.push(dependencies[i].apply(null, arguments));
+ } // apply arguments instead of spreading for performance.
+
+
+ _lastResult = memoizedResultFunc.apply(null, params);
+ return _lastResult;
+ });
+ Object.assign(selector, {
+ resultFunc: resultFunc,
+ memoizedResultFunc: memoizedResultFunc,
+ dependencies: dependencies,
+ lastResult: function lastResult() {
+ return _lastResult;
+ },
+ recomputations: function recomputations() {
+ return _recomputations;
+ },
+ resetRecomputations: function resetRecomputations() {
+ return _recomputations = 0;
+ }
+ });
+ return selector;
+ }; // @ts-ignore
+
+
+ return createSelector;
+ }
+ var createSelector = /* #__PURE__ */createSelectorCreator(defaultMemoize);
+ // Manual definition of state and output arguments
+ var createStructuredSelector = function createStructuredSelector(selectors, selectorCreator) {
+ if (selectorCreator === void 0) {
+ selectorCreator = createSelector;
+ }
+
+ if (typeof selectors !== 'object') {
+ throw new Error('createStructuredSelector expects first argument to be an object ' + ("where each property is a selector, instead received a " + typeof selectors));
+ }
+
+ var objectKeys = Object.keys(selectors);
+ var resultSelector = selectorCreator( // @ts-ignore
+ objectKeys.map(function (key) {
+ return selectors[key];
+ }), function () {
+ for (var _len3 = arguments.length, values = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
+ values[_key3] = arguments[_key3];
+ }
+
+ return values.reduce(function (composition, value, index) {
+ composition[objectKeys[index]] = value;
+ return composition;
+ }, {});
+ });
+ return resultSelector;
+ };
+
+ exports.createSelector = createSelector;
+ exports.createSelectorCreator = createSelectorCreator;
+ exports.createStructuredSelector = createStructuredSelector;
+ exports.defaultEqualityCheck = defaultEqualityCheck;
+ exports.defaultMemoize = defaultMemoize;
+
+ Object.defineProperty(exports, '__esModule', { value: true });
+
+}));
diff --git a/devtools/client/shared/vendor/source-map/LICENSE b/devtools/client/shared/vendor/source-map/LICENSE
new file mode 100644
index 0000000000..ed1b7cf27e
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/LICENSE
@@ -0,0 +1,28 @@
+
+Copyright (c) 2009-2011, Mozilla Foundation and contributors
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+* Neither the names of the Mozilla Foundation nor the names of project
+ contributors may be used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/devtools/client/shared/vendor/source-map/lib/array-set.js b/devtools/client/shared/vendor/source-map/lib/array-set.js
new file mode 100644
index 0000000000..d2ed9cd135
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/array-set.js
@@ -0,0 +1,100 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+/**
+ * A data structure which is a combination of an array and a set. Adding a new
+ * member is O(1), testing for membership is O(1), and finding the index of an
+ * element is O(1). Removing elements from the set is not supported. Only
+ * strings are supported for membership.
+ */
+class ArraySet {
+ constructor() {
+ this._array = [];
+ this._set = new Map();
+ }
+
+ /**
+ * Static method for creating ArraySet instances from an existing array.
+ */
+ static fromArray(aArray, aAllowDuplicates) {
+ const set = new ArraySet();
+ for (let i = 0, len = aArray.length; i < len; i++) {
+ set.add(aArray[i], aAllowDuplicates);
+ }
+ return set;
+ }
+
+ /**
+ * Return how many unique items are in this ArraySet. If duplicates have been
+ * added, than those do not count towards the size.
+ *
+ * @returns Number
+ */
+ size() {
+ return this._set.size;
+ }
+
+ /**
+ * Add the given string to this set.
+ *
+ * @param String aStr
+ */
+ add(aStr, aAllowDuplicates) {
+ const isDuplicate = this.has(aStr);
+ const idx = this._array.length;
+ if (!isDuplicate || aAllowDuplicates) {
+ this._array.push(aStr);
+ }
+ if (!isDuplicate) {
+ this._set.set(aStr, idx);
+ }
+ }
+
+ /**
+ * Is the given string a member of this set?
+ *
+ * @param String aStr
+ */
+ has(aStr) {
+ return this._set.has(aStr);
+ }
+
+ /**
+ * What is the index of the given string in the array?
+ *
+ * @param String aStr
+ */
+ indexOf(aStr) {
+ const idx = this._set.get(aStr);
+ if (idx >= 0) {
+ return idx;
+ }
+ throw new Error('"' + aStr + '" is not in the set.');
+ }
+
+ /**
+ * What is the element at the given index?
+ *
+ * @param Number aIdx
+ */
+ at(aIdx) {
+ if (aIdx >= 0 && aIdx < this._array.length) {
+ return this._array[aIdx];
+ }
+ throw new Error("No element indexed by " + aIdx);
+ }
+
+ /**
+ * Returns the array representation of this set (which has the proper indices
+ * indicated by indexOf). Note that this is a copy of the internal array used
+ * for storing the members so that no one can mess with internal state.
+ */
+ toArray() {
+ return this._array.slice();
+ }
+}
+exports.ArraySet = ArraySet;
diff --git a/devtools/client/shared/vendor/source-map/lib/base64-vlq.js b/devtools/client/shared/vendor/source-map/lib/base64-vlq.js
new file mode 100644
index 0000000000..2daed1eb5a
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/base64-vlq.js
@@ -0,0 +1,94 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ *
+ * Based on the Base 64 VLQ implementation in Closure Compiler:
+ * https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
+ *
+ * Copyright 2011 The Closure Compiler Authors. All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+const base64 = require("./base64");
+
+// A single base 64 digit can contain 6 bits of data. For the base 64 variable
+// length quantities we use in the source map spec, the first bit is the sign,
+// the next four bits are the actual value, and the 6th bit is the
+// continuation bit. The continuation bit tells us whether there are more
+// digits in this value following this digit.
+//
+// Continuation
+// | Sign
+// | |
+// V V
+// 101011
+
+const VLQ_BASE_SHIFT = 5;
+
+// binary: 100000
+const VLQ_BASE = 1 << VLQ_BASE_SHIFT;
+
+// binary: 011111
+const VLQ_BASE_MASK = VLQ_BASE - 1;
+
+// binary: 100000
+const VLQ_CONTINUATION_BIT = VLQ_BASE;
+
+/**
+ * Converts from a two-complement value to a value where the sign bit is
+ * placed in the least significant bit. For example, as decimals:
+ * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+ * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+ */
+function toVLQSigned(aValue) {
+ return aValue < 0 ? (-aValue << 1) + 1 : (aValue << 1) + 0;
+}
+
+/**
+ * Returns the base 64 VLQ encoded value.
+ */
+exports.encode = function base64VLQ_encode(aValue) {
+ let encoded = "";
+ let digit;
+
+ let vlq = toVLQSigned(aValue);
+
+ do {
+ digit = vlq & VLQ_BASE_MASK;
+ vlq >>>= VLQ_BASE_SHIFT;
+ if (vlq > 0) {
+ // There are still more digits in this value, so we must make sure the
+ // continuation bit is marked.
+ digit |= VLQ_CONTINUATION_BIT;
+ }
+ encoded += base64.encode(digit);
+ } while (vlq > 0);
+
+ return encoded;
+};
diff --git a/devtools/client/shared/vendor/source-map/lib/base64.js b/devtools/client/shared/vendor/source-map/lib/base64.js
new file mode 100644
index 0000000000..939abc8e1e
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/base64.js
@@ -0,0 +1,19 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+const intToCharMap =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");
+
+/**
+ * Encode an integer in the range of 0 to 63 to a single base 64 digit.
+ */
+exports.encode = function (number) {
+ if (0 <= number && number < intToCharMap.length) {
+ return intToCharMap[number];
+ }
+ throw new TypeError("Must be between 0 and 63: " + number);
+};
diff --git a/devtools/client/shared/vendor/source-map/lib/binary-search.js b/devtools/client/shared/vendor/source-map/lib/binary-search.js
new file mode 100644
index 0000000000..db65ccd12e
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/binary-search.js
@@ -0,0 +1,113 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+exports.GREATEST_LOWER_BOUND = 1;
+exports.LEAST_UPPER_BOUND = 2;
+
+/**
+ * Recursive implementation of binary search.
+ *
+ * @param aLow Indices here and lower do not contain the needle.
+ * @param aHigh Indices here and higher do not contain the needle.
+ * @param aNeedle The element being searched for.
+ * @param aHaystack The non-empty array being searched.
+ * @param aCompare Function which takes two elements and returns -1, 0, or 1.
+ * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
+ * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
+ * closest element that is smaller than or greater than the one we are
+ * searching for, respectively, if the exact element cannot be found.
+ */
+function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) {
+ // This function terminates when one of the following is true:
+ //
+ // 1. We find the exact element we are looking for.
+ //
+ // 2. We did not find the exact element, but we can return the index of
+ // the next-closest element.
+ //
+ // 3. We did not find the exact element, and there is no next-closest
+ // element than the one we are searching for, so we return -1.
+ const mid = Math.floor((aHigh - aLow) / 2) + aLow;
+ const cmp = aCompare(aNeedle, aHaystack[mid], true);
+ if (cmp === 0) {
+ // Found the element we are looking for.
+ return mid;
+ } else if (cmp > 0) {
+ // Our needle is greater than aHaystack[mid].
+ if (aHigh - mid > 1) {
+ // The element is in the upper half.
+ return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias);
+ }
+
+ // The exact needle element was not found in this haystack. Determine if
+ // we are in termination case (3) or (2) and return the appropriate thing.
+ if (aBias === exports.LEAST_UPPER_BOUND) {
+ return aHigh < aHaystack.length ? aHigh : -1;
+ }
+ return mid;
+ }
+
+ // Our needle is less than aHaystack[mid].
+ if (mid - aLow > 1) {
+ // The element is in the lower half.
+ return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias);
+ }
+
+ // we are in termination case (3) or (2) and return the appropriate thing.
+ if (aBias == exports.LEAST_UPPER_BOUND) {
+ return mid;
+ }
+ return aLow < 0 ? -1 : aLow;
+}
+
+/**
+ * This is an implementation of binary search which will always try and return
+ * the index of the closest element if there is no exact hit. This is because
+ * mappings between original and generated line/col pairs are single points,
+ * and there is an implicit region between each of them, so a miss just means
+ * that you aren't on the very start of a region.
+ *
+ * @param aNeedle The element you are looking for.
+ * @param aHaystack The array that is being searched.
+ * @param aCompare A function which takes the needle and an element in the
+ * array and returns -1, 0, or 1 depending on whether the needle is less
+ * than, equal to, or greater than the element, respectively.
+ * @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
+ * 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
+ * closest element that is smaller than or greater than the one we are
+ * searching for, respectively, if the exact element cannot be found.
+ * Defaults to 'binarySearch.GREATEST_LOWER_BOUND'.
+ */
+exports.search = function search(aNeedle, aHaystack, aCompare, aBias) {
+ if (aHaystack.length === 0) {
+ return -1;
+ }
+
+ let index = recursiveSearch(
+ -1,
+ aHaystack.length,
+ aNeedle,
+ aHaystack,
+ aCompare,
+ aBias || exports.GREATEST_LOWER_BOUND
+ );
+ if (index < 0) {
+ return -1;
+ }
+
+ // We have found either the exact element, or the next-closest element to
+ // the one we are searching for. However, there may be more than one such
+ // element. Make sure we always return the smallest of these.
+ while (index - 1 >= 0) {
+ if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) {
+ break;
+ }
+ --index;
+ }
+
+ return index;
+};
diff --git a/devtools/client/shared/vendor/source-map/lib/mapping-list.js b/devtools/client/shared/vendor/source-map/lib/mapping-list.js
new file mode 100644
index 0000000000..ece3c2ccc1
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/mapping-list.js
@@ -0,0 +1,83 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2014 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+const util = require("./util");
+
+/**
+ * Determine whether mappingB is after mappingA with respect to generated
+ * position.
+ */
+function generatedPositionAfter(mappingA, mappingB) {
+ // Optimized for most common case
+ const lineA = mappingA.generatedLine;
+ const lineB = mappingB.generatedLine;
+ const columnA = mappingA.generatedColumn;
+ const columnB = mappingB.generatedColumn;
+ return (
+ lineB > lineA ||
+ (lineB == lineA && columnB >= columnA) ||
+ util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0
+ );
+}
+
+/**
+ * A data structure to provide a sorted view of accumulated mappings in a
+ * performance conscious manner. It trades a negligible overhead in general
+ * case for a large speedup in case of mappings being added in order.
+ */
+class MappingList {
+ constructor() {
+ this._array = [];
+ this._sorted = true;
+ // Serves as infimum
+ this._last = { generatedLine: -1, generatedColumn: 0 };
+ }
+
+ /**
+ * Iterate through internal items. This method takes the same arguments that
+ * `Array.prototype.forEach` takes.
+ *
+ * NOTE: The order of the mappings is NOT guaranteed.
+ */
+ unsortedForEach(aCallback, aThisArg) {
+ this._array.forEach(aCallback, aThisArg);
+ }
+
+ /**
+ * Add the given source mapping.
+ *
+ * @param Object aMapping
+ */
+ add(aMapping) {
+ if (generatedPositionAfter(this._last, aMapping)) {
+ this._last = aMapping;
+ this._array.push(aMapping);
+ } else {
+ this._sorted = false;
+ this._array.push(aMapping);
+ }
+ }
+
+ /**
+ * Returns the flat, sorted array of mappings. The mappings are sorted by
+ * generated position.
+ *
+ * WARNING: This method returns internal data without copying, for
+ * performance. The return value must NOT be mutated, and should be treated as
+ * an immutable borrow. If you want to take ownership, you must make your own
+ * copy.
+ */
+ toArray() {
+ if (!this._sorted) {
+ this._array.sort(util.compareByGeneratedPositionsInflated);
+ this._sorted = true;
+ }
+ return this._array;
+ }
+}
+
+exports.MappingList = MappingList;
diff --git a/devtools/client/shared/vendor/source-map/lib/mappings.wasm b/devtools/client/shared/vendor/source-map/lib/mappings.wasm
new file mode 100644
index 0000000000..cdcc2958c0
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/mappings.wasm
Binary files differ
diff --git a/devtools/client/shared/vendor/source-map/lib/moz.build b/devtools/client/shared/vendor/source-map/lib/moz.build
new file mode 100644
index 0000000000..ccd2457b17
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "array-set.js",
+ "base64-vlq.js",
+ "base64.js",
+ "binary-search.js",
+ "mapping-list.js",
+ "mappings.wasm",
+ "read-wasm.js",
+ "source-map-consumer.js",
+ "source-map-generator.js",
+ "source-node.js",
+ "url.js",
+ "util.js",
+ "wasm.js",
+)
diff --git a/devtools/client/shared/vendor/source-map/lib/read-wasm.js b/devtools/client/shared/vendor/source-map/lib/read-wasm.js
new file mode 100644
index 0000000000..75d7bd5415
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/read-wasm.js
@@ -0,0 +1,46 @@
+"use strict";
+
+const isNode =
+ typeof process !== "undefined" &&
+ process.versions != null &&
+ process.versions.node != null;
+
+let mappingsWasm = null;
+
+if (isNode) {
+ const fs = require("fs");
+ const path = require("path");
+
+ module.exports = function readWasm() {
+ return new Promise((resolve, reject) => {
+ const wasmPath = path.join(__dirname, "mappings.wasm");
+ fs.readFile(wasmPath, null, (error, data) => {
+ if (error) {
+ reject(error);
+ return;
+ }
+
+ resolve(data.buffer);
+ });
+ });
+ };
+} else {
+ module.exports = function readWasm() {
+ if (typeof mappingsWasm === "string") {
+ return fetch(mappingsWasm)
+ .then(response => response.arrayBuffer());
+ }
+ if (mappingsWasm instanceof ArrayBuffer) {
+ return Promise.resolve(mappingsWasm);
+ }
+
+ throw new Error("You must provide the string URL or ArrayBuffer contents " +
+ "of lib/mappings.wasm by calling " +
+ "SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) " +
+ "before using SourceMapConsumer");
+ };
+}
+
+module.exports.initialize = input => {
+ mappingsWasm = input;
+};
diff --git a/devtools/client/shared/vendor/source-map/lib/source-map-consumer.js b/devtools/client/shared/vendor/source-map/lib/source-map-consumer.js
new file mode 100644
index 0000000000..be1289d990
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/source-map-consumer.js
@@ -0,0 +1,1078 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+const util = require("./util");
+const binarySearch = require("./binary-search");
+const ArraySet = require("./array-set").ArraySet;
+const base64VLQ = require("./base64-vlq"); // eslint-disable-line no-unused-vars
+const readWasm = require("../lib/read-wasm");
+const wasm = require("./wasm");
+
+const INTERNAL = Symbol("smcInternal");
+
+class SourceMapConsumer {
+ constructor(aSourceMap, aSourceMapURL) {
+ // If the constructor was called by super(), just return Promise<this>.
+ // Yes, this is a hack to retain the pre-existing API of the base-class
+ // constructor also being an async factory function.
+ if (aSourceMap == INTERNAL) {
+ return Promise.resolve(this);
+ }
+
+ return _factory(aSourceMap, aSourceMapURL);
+ }
+
+ static initialize(opts) {
+ readWasm.initialize(opts["lib/mappings.wasm"]);
+ }
+
+ static fromSourceMap(aSourceMap, aSourceMapURL) {
+ return _factoryBSM(aSourceMap, aSourceMapURL);
+ }
+
+ /**
+ * Construct a new `SourceMapConsumer` from `rawSourceMap` and `sourceMapUrl`
+ * (see the `SourceMapConsumer` constructor for details. Then, invoke the `async
+ * function f(SourceMapConsumer) -> T` with the newly constructed consumer, wait
+ * for `f` to complete, call `destroy` on the consumer, and return `f`'s return
+ * value.
+ *
+ * You must not use the consumer after `f` completes!
+ *
+ * By using `with`, you do not have to remember to manually call `destroy` on
+ * the consumer, since it will be called automatically once `f` completes.
+ *
+ * ```js
+ * const xSquared = await SourceMapConsumer.with(
+ * myRawSourceMap,
+ * null,
+ * async function (consumer) {
+ * // Use `consumer` inside here and don't worry about remembering
+ * // to call `destroy`.
+ *
+ * const x = await whatever(consumer);
+ * return x * x;
+ * }
+ * );
+ *
+ * // You may not use that `consumer` anymore out here; it has
+ * // been destroyed. But you can use `xSquared`.
+ * console.log(xSquared);
+ * ```
+ */
+ static async with(rawSourceMap, sourceMapUrl, f) {
+ const consumer = await new SourceMapConsumer(rawSourceMap, sourceMapUrl);
+ try {
+ return await f(consumer);
+ } finally {
+ consumer.destroy();
+ }
+ }
+
+ /**
+ * Iterate over each mapping between an original source/line/column and a
+ * generated line/column in this source map.
+ *
+ * @param Function aCallback
+ * The function that is called with each mapping.
+ * @param Object aContext
+ * Optional. If specified, this object will be the value of `this` every
+ * time that `aCallback` is called.
+ * @param aOrder
+ * Either `SourceMapConsumer.GENERATED_ORDER` or
+ * `SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to
+ * iterate over the mappings sorted by the generated file's line/column
+ * order or the original's source/line/column order, respectively. Defaults to
+ * `SourceMapConsumer.GENERATED_ORDER`.
+ */
+ eachMapping(aCallback, aContext, aOrder) {
+ throw new Error("Subclasses must implement eachMapping");
+ }
+
+ /**
+ * Returns all generated line and column information for the original source,
+ * line, and column provided. If no column is provided, returns all mappings
+ * corresponding to a either the line we are searching for or the next
+ * closest line that has any mappings. Otherwise, returns all mappings
+ * corresponding to the given line and either the column we are searching for
+ * or the next closest column that has any offsets.
+ *
+ * The only argument is an object with the following properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source. The line number is 1-based.
+ * - column: Optional. the column number in the original source.
+ * The column number is 0-based.
+ *
+ * and an array of objects is returned, each with the following properties:
+ *
+ * - line: The line number in the generated source, or null. The
+ * line number is 1-based.
+ * - column: The column number in the generated source, or null.
+ * The column number is 0-based.
+ */
+ allGeneratedPositionsFor(aArgs) {
+ throw new Error("Subclasses must implement allGeneratedPositionsFor");
+ }
+
+ destroy() {
+ throw new Error("Subclasses must implement destroy");
+ }
+}
+
+/**
+ * The version of the source mapping spec that we are consuming.
+ */
+SourceMapConsumer.prototype._version = 3;
+SourceMapConsumer.GENERATED_ORDER = 1;
+SourceMapConsumer.ORIGINAL_ORDER = 2;
+
+SourceMapConsumer.GREATEST_LOWER_BOUND = 1;
+SourceMapConsumer.LEAST_UPPER_BOUND = 2;
+
+exports.SourceMapConsumer = SourceMapConsumer;
+
+/**
+ * A BasicSourceMapConsumer instance represents a parsed source map which we can
+ * query for information about the original file positions by giving it a file
+ * position in the generated source.
+ *
+ * The first parameter is the raw source map (either as a JSON string, or
+ * already parsed to an object). According to the spec, source maps have the
+ * following attributes:
+ *
+ * - version: Which version of the source map spec this map is following.
+ * - sources: An array of URLs to the original source files.
+ * - names: An array of identifiers which can be referenced by individual mappings.
+ * - sourceRoot: Optional. The URL root from which all sources are relative.
+ * - sourcesContent: Optional. An array of contents of the original source files.
+ * - mappings: A string of base64 VLQs which contain the actual mappings.
+ * - file: Optional. The generated file this source map is associated with.
+ *
+ * Here is an example source map, taken from the source map spec[0]:
+ *
+ * {
+ * version : 3,
+ * file: "out.js",
+ * sourceRoot : "",
+ * sources: ["foo.js", "bar.js"],
+ * names: ["src", "maps", "are", "fun"],
+ * mappings: "AA,AB;;ABCDE;"
+ * }
+ *
+ * The second parameter, if given, is a string whose value is the URL
+ * at which the source map was found. This URL is used to compute the
+ * sources array.
+ *
+ * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit?pli=1#
+ */
+class BasicSourceMapConsumer extends SourceMapConsumer {
+ constructor(aSourceMap, aSourceMapURL) {
+ return super(INTERNAL).then(that => {
+ let sourceMap = aSourceMap;
+ if (typeof aSourceMap === "string") {
+ sourceMap = util.parseSourceMapInput(aSourceMap);
+ }
+
+ const version = util.getArg(sourceMap, "version");
+ const sources = util.getArg(sourceMap, "sources").map(String);
+ // Sass 3.3 leaves out the 'names' array, so we deviate from the spec (which
+ // requires the array) to play nice here.
+ const names = util.getArg(sourceMap, "names", []);
+ const sourceRoot = util.getArg(sourceMap, "sourceRoot", null);
+ const sourcesContent = util.getArg(sourceMap, "sourcesContent", null);
+ const mappings = util.getArg(sourceMap, "mappings");
+ const file = util.getArg(sourceMap, "file", null);
+ const x_google_ignoreList = util.getArg(
+ sourceMap,
+ "x_google_ignoreList",
+ null
+ );
+
+ // Once again, Sass deviates from the spec and supplies the version as a
+ // string rather than a number, so we use loose equality checking here.
+ if (version != that._version) {
+ throw new Error("Unsupported version: " + version);
+ }
+
+ that._sourceLookupCache = new Map();
+
+ // Pass `true` below to allow duplicate names and sources. While source maps
+ // are intended to be compressed and deduplicated, the TypeScript compiler
+ // sometimes generates source maps with duplicates in them. See Github issue
+ // #72 and bugzil.la/889492.
+ that._names = ArraySet.fromArray(names.map(String), true);
+ that._sources = ArraySet.fromArray(sources, true);
+
+ that._absoluteSources = ArraySet.fromArray(
+ that._sources.toArray().map(function (s) {
+ return util.computeSourceURL(sourceRoot, s, aSourceMapURL);
+ }),
+ true
+ );
+
+ that.sourceRoot = sourceRoot;
+ that.sourcesContent = sourcesContent;
+ that._mappings = mappings;
+ that._sourceMapURL = aSourceMapURL;
+ that.file = file;
+ that.x_google_ignoreList = x_google_ignoreList;
+
+ that._computedColumnSpans = false;
+ that._mappingsPtr = 0;
+ that._wasm = null;
+
+ return wasm().then(w => {
+ that._wasm = w;
+ return that;
+ });
+ });
+ }
+
+ /**
+ * Utility function to find the index of a source. Returns -1 if not
+ * found.
+ */
+ _findSourceIndex(aSource) {
+ // In the most common usecases, we'll be constantly looking up the index for the same source
+ // files, so we cache the index lookup to avoid constantly recomputing the full URLs.
+ const cachedIndex = this._sourceLookupCache.get(aSource);
+ if (typeof cachedIndex === "number") {
+ return cachedIndex;
+ }
+
+ // Treat the source as map-relative overall by default.
+ const sourceAsMapRelative = util.computeSourceURL(
+ null,
+ aSource,
+ this._sourceMapURL
+ );
+ if (this._absoluteSources.has(sourceAsMapRelative)) {
+ const index = this._absoluteSources.indexOf(sourceAsMapRelative);
+ this._sourceLookupCache.set(aSource, index);
+ return index;
+ }
+
+ // Fall back to treating the source as sourceRoot-relative.
+ const sourceAsSourceRootRelative = util.computeSourceURL(
+ this.sourceRoot,
+ aSource,
+ this._sourceMapURL
+ );
+ if (this._absoluteSources.has(sourceAsSourceRootRelative)) {
+ const index = this._absoluteSources.indexOf(sourceAsSourceRootRelative);
+ this._sourceLookupCache.set(aSource, index);
+ return index;
+ }
+
+ // To avoid this cache growing forever, we do not cache lookup misses.
+ return -1;
+ }
+
+ /**
+ * Create a BasicSourceMapConsumer from a SourceMapGenerator.
+ *
+ * @param SourceMapGenerator aSourceMap
+ * The source map that will be consumed.
+ * @param String aSourceMapURL
+ * The URL at which the source map can be found (optional)
+ * @returns BasicSourceMapConsumer
+ */
+ static fromSourceMap(aSourceMap, aSourceMapURL) {
+ return new BasicSourceMapConsumer(aSourceMap.toString());
+ }
+
+ get sources() {
+ return this._absoluteSources.toArray();
+ }
+
+ _getMappingsPtr() {
+ if (this._mappingsPtr === 0) {
+ this._parseMappings();
+ }
+
+ return this._mappingsPtr;
+ }
+
+ /**
+ * Parse the mappings in a string in to a data structure which we can easily
+ * query (the ordered arrays in the `this.__generatedMappings` and
+ * `this.__originalMappings` properties).
+ */
+ _parseMappings() {
+ const aStr = this._mappings;
+ const size = aStr.length;
+
+ // Interpret signed result of allocate_mappings as unsigned, otherwise
+ // addresses higher than 2GB will be negative.
+ const mappingsBufPtr = this._wasm.exports.allocate_mappings(size) >>> 0;
+ const mappingsBuf = new Uint8Array(
+ this._wasm.exports.memory.buffer,
+ mappingsBufPtr,
+ size
+ );
+ for (let i = 0; i < size; i++) {
+ mappingsBuf[i] = aStr.charCodeAt(i);
+ }
+
+ const mappingsPtr = this._wasm.exports.parse_mappings(mappingsBufPtr);
+
+ if (!mappingsPtr) {
+ const error = this._wasm.exports.get_last_error();
+ let msg = `Error parsing mappings (code ${error}): `;
+
+ // XXX: keep these error codes in sync with `wasm-mappings`.
+ switch (error) {
+ case 1:
+ msg +=
+ "the mappings contained a negative line, column, source index, or name index";
+ break;
+ case 2:
+ msg += "the mappings contained a number larger than 2**32";
+ break;
+ case 3:
+ msg += "reached EOF while in the middle of parsing a VLQ";
+ break;
+ case 4:
+ msg += "invalid base 64 character while parsing a VLQ";
+ break;
+ default:
+ msg += "unknown error code";
+ break;
+ }
+
+ throw new Error(msg);
+ }
+
+ this._mappingsPtr = mappingsPtr;
+ }
+
+ eachMapping(aCallback, aContext, aOrder) {
+ const context = aContext || null;
+ const order = aOrder || SourceMapConsumer.GENERATED_ORDER;
+
+ this._wasm.withMappingCallback(
+ mapping => {
+ if (mapping.source !== null) {
+ mapping.source = this._absoluteSources.at(mapping.source);
+
+ if (mapping.name !== null) {
+ mapping.name = this._names.at(mapping.name);
+ }
+ }
+ if (this._computedColumnSpans && mapping.lastGeneratedColumn === null) {
+ mapping.lastGeneratedColumn = Infinity;
+ }
+
+ aCallback.call(context, mapping);
+ },
+ () => {
+ switch (order) {
+ case SourceMapConsumer.GENERATED_ORDER:
+ this._wasm.exports.by_generated_location(this._getMappingsPtr());
+ break;
+ case SourceMapConsumer.ORIGINAL_ORDER:
+ this._wasm.exports.by_original_location(this._getMappingsPtr());
+ break;
+ default:
+ throw new Error("Unknown order of iteration.");
+ }
+ }
+ );
+ }
+
+ allGeneratedPositionsFor(aArgs) {
+ let source = util.getArg(aArgs, "source");
+ const originalLine = util.getArg(aArgs, "line");
+ const originalColumn = aArgs.column || 0;
+
+ source = this._findSourceIndex(source);
+ if (source < 0) {
+ return [];
+ }
+
+ if (originalLine < 1) {
+ throw new Error("Line numbers must be >= 1");
+ }
+
+ if (originalColumn < 0) {
+ throw new Error("Column numbers must be >= 0");
+ }
+
+ const mappings = [];
+
+ this._wasm.withMappingCallback(
+ m => {
+ let lastColumn = m.lastGeneratedColumn;
+ if (this._computedColumnSpans && lastColumn === null) {
+ lastColumn = Infinity;
+ }
+ mappings.push({
+ line: m.generatedLine,
+ column: m.generatedColumn,
+ lastColumn,
+ });
+ },
+ () => {
+ this._wasm.exports.all_generated_locations_for(
+ this._getMappingsPtr(),
+ source,
+ originalLine - 1,
+ "column" in aArgs,
+ originalColumn
+ );
+ }
+ );
+
+ return mappings;
+ }
+
+ destroy() {
+ if (this._mappingsPtr !== 0) {
+ this._wasm.exports.free_mappings(this._mappingsPtr);
+ this._mappingsPtr = 0;
+ }
+ }
+
+ /**
+ * Compute the last column for each generated mapping. The last column is
+ * inclusive.
+ */
+ computeColumnSpans() {
+ if (this._computedColumnSpans) {
+ return;
+ }
+
+ this._wasm.exports.compute_column_spans(this._getMappingsPtr());
+ this._computedColumnSpans = true;
+ }
+
+ /**
+ * Returns the original source, line, and column information for the generated
+ * source's line and column positions provided. The only argument is an object
+ * with the following properties:
+ *
+ * - line: The line number in the generated source. The line number
+ * is 1-based.
+ * - column: The column number in the generated source. The column
+ * number is 0-based.
+ * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
+ * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
+ * closest element that is smaller than or greater than the one we are
+ * searching for, respectively, if the exact element cannot be found.
+ * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - source: The original source file, or null.
+ * - line: The line number in the original source, or null. The
+ * line number is 1-based.
+ * - column: The column number in the original source, or null. The
+ * column number is 0-based.
+ * - name: The original identifier, or null.
+ */
+ originalPositionFor(aArgs) {
+ const needle = {
+ generatedLine: util.getArg(aArgs, "line"),
+ generatedColumn: util.getArg(aArgs, "column"),
+ };
+
+ if (needle.generatedLine < 1) {
+ throw new Error("Line numbers must be >= 1");
+ }
+
+ if (needle.generatedColumn < 0) {
+ throw new Error("Column numbers must be >= 0");
+ }
+
+ let bias = util.getArg(
+ aArgs,
+ "bias",
+ SourceMapConsumer.GREATEST_LOWER_BOUND
+ );
+ if (bias == null) {
+ bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
+ }
+
+ let mapping;
+ this._wasm.withMappingCallback(
+ m => (mapping = m),
+ () => {
+ this._wasm.exports.original_location_for(
+ this._getMappingsPtr(),
+ needle.generatedLine - 1,
+ needle.generatedColumn,
+ bias
+ );
+ }
+ );
+
+ if (mapping) {
+ if (mapping.generatedLine === needle.generatedLine) {
+ let source = util.getArg(mapping, "source", null);
+ if (source !== null) {
+ source = this._absoluteSources.at(source);
+ }
+
+ let name = util.getArg(mapping, "name", null);
+ if (name !== null) {
+ name = this._names.at(name);
+ }
+
+ return {
+ source,
+ line: util.getArg(mapping, "originalLine", null),
+ column: util.getArg(mapping, "originalColumn", null),
+ name,
+ };
+ }
+ }
+
+ return {
+ source: null,
+ line: null,
+ column: null,
+ name: null,
+ };
+ }
+
+ /**
+ * Return true if we have the source content for every source in the source
+ * map, false otherwise.
+ */
+ hasContentsOfAllSources() {
+ if (!this.sourcesContent) {
+ return false;
+ }
+ return (
+ this.sourcesContent.length >= this._sources.size() &&
+ !this.sourcesContent.some(function (sc) {
+ return sc == null;
+ })
+ );
+ }
+
+ /**
+ * Returns the original source content. The only argument is the url of the
+ * original source file. Returns null if no original source content is
+ * available.
+ */
+ sourceContentFor(aSource, nullOnMissing) {
+ if (!this.sourcesContent) {
+ return null;
+ }
+
+ const index = this._findSourceIndex(aSource);
+ if (index >= 0) {
+ return this.sourcesContent[index];
+ }
+
+ // This function is used recursively from
+ // IndexedSourceMapConsumer.prototype.sourceContentFor. In that case, we
+ // don't want to throw if we can't find the source - we just want to
+ // return null, so we provide a flag to exit gracefully.
+ if (nullOnMissing) {
+ return null;
+ }
+
+ throw new Error('"' + aSource + '" is not in the SourceMap.');
+ }
+
+ /**
+ * Returns the generated line and column information for the original source,
+ * line, and column positions provided. The only argument is an object with
+ * the following properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source. The line number
+ * is 1-based.
+ * - column: The column number in the original source. The column
+ * number is 0-based.
+ * - bias: Either 'SourceMapConsumer.GREATEST_LOWER_BOUND' or
+ * 'SourceMapConsumer.LEAST_UPPER_BOUND'. Specifies whether to return the
+ * closest element that is smaller than or greater than the one we are
+ * searching for, respectively, if the exact element cannot be found.
+ * Defaults to 'SourceMapConsumer.GREATEST_LOWER_BOUND'.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - line: The line number in the generated source, or null. The
+ * line number is 1-based.
+ * - column: The column number in the generated source, or null.
+ * The column number is 0-based.
+ */
+ generatedPositionFor(aArgs) {
+ let source = util.getArg(aArgs, "source");
+ source = this._findSourceIndex(source);
+ if (source < 0) {
+ return {
+ line: null,
+ column: null,
+ lastColumn: null,
+ };
+ }
+
+ const needle = {
+ source,
+ originalLine: util.getArg(aArgs, "line"),
+ originalColumn: util.getArg(aArgs, "column"),
+ };
+
+ if (needle.originalLine < 1) {
+ throw new Error("Line numbers must be >= 1");
+ }
+
+ if (needle.originalColumn < 0) {
+ throw new Error("Column numbers must be >= 0");
+ }
+
+ let bias = util.getArg(
+ aArgs,
+ "bias",
+ SourceMapConsumer.GREATEST_LOWER_BOUND
+ );
+ if (bias == null) {
+ bias = SourceMapConsumer.GREATEST_LOWER_BOUND;
+ }
+
+ let mapping;
+ this._wasm.withMappingCallback(
+ m => (mapping = m),
+ () => {
+ this._wasm.exports.generated_location_for(
+ this._getMappingsPtr(),
+ needle.source,
+ needle.originalLine - 1,
+ needle.originalColumn,
+ bias
+ );
+ }
+ );
+
+ if (mapping) {
+ if (mapping.source === needle.source) {
+ let lastColumn = mapping.lastGeneratedColumn;
+ if (this._computedColumnSpans && lastColumn === null) {
+ lastColumn = Infinity;
+ }
+ return {
+ line: util.getArg(mapping, "generatedLine", null),
+ column: util.getArg(mapping, "generatedColumn", null),
+ lastColumn,
+ };
+ }
+ }
+
+ return {
+ line: null,
+ column: null,
+ lastColumn: null,
+ };
+ }
+}
+
+BasicSourceMapConsumer.prototype.consumer = SourceMapConsumer;
+exports.BasicSourceMapConsumer = BasicSourceMapConsumer;
+
+/**
+ * An IndexedSourceMapConsumer instance represents a parsed source map which
+ * we can query for information. It differs from BasicSourceMapConsumer in
+ * that it takes "indexed" source maps (i.e. ones with a "sections" field) as
+ * input.
+ *
+ * The first parameter is a raw source map (either as a JSON string, or already
+ * parsed to an object). According to the spec for indexed source maps, they
+ * have the following attributes:
+ *
+ * - version: Which version of the source map spec this map is following.
+ * - file: Optional. The generated file this source map is associated with.
+ * - sections: A list of section definitions.
+ *
+ * Each value under the "sections" field has two fields:
+ * - offset: The offset into the original specified at which this section
+ * begins to apply, defined as an object with a "line" and "column"
+ * field.
+ * - map: A source map definition. This source map could also be indexed,
+ * but doesn't have to be.
+ *
+ * Instead of the "map" field, it's also possible to have a "url" field
+ * specifying a URL to retrieve a source map from, but that's currently
+ * unsupported.
+ *
+ * Here's an example source map, taken from the source map spec[0], but
+ * modified to omit a section which uses the "url" field.
+ *
+ * {
+ * version : 3,
+ * file: "app.js",
+ * sections: [{
+ * offset: {line:100, column:10},
+ * map: {
+ * version : 3,
+ * file: "section.js",
+ * sources: ["foo.js", "bar.js"],
+ * names: ["src", "maps", "are", "fun"],
+ * mappings: "AAAA,E;;ABCDE;"
+ * }
+ * }],
+ * }
+ *
+ * The second parameter, if given, is a string whose value is the URL
+ * at which the source map was found. This URL is used to compute the
+ * sources array.
+ *
+ * [0]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#heading=h.535es3xeprgt
+ */
+class IndexedSourceMapConsumer extends SourceMapConsumer {
+ constructor(aSourceMap, aSourceMapURL) {
+ return super(INTERNAL).then(that => {
+ let sourceMap = aSourceMap;
+ if (typeof aSourceMap === "string") {
+ sourceMap = util.parseSourceMapInput(aSourceMap);
+ }
+
+ const version = util.getArg(sourceMap, "version");
+ const sections = util.getArg(sourceMap, "sections");
+
+ if (version != that._version) {
+ throw new Error("Unsupported version: " + version);
+ }
+
+ let lastOffset = {
+ line: -1,
+ column: 0,
+ };
+ return Promise.all(
+ sections.map(s => {
+ if (s.url) {
+ // The url field will require support for asynchronicity.
+ // See https://github.com/mozilla/source-map/issues/16
+ throw new Error(
+ "Support for url field in sections not implemented."
+ );
+ }
+ const offset = util.getArg(s, "offset");
+ const offsetLine = util.getArg(offset, "line");
+ const offsetColumn = util.getArg(offset, "column");
+
+ if (
+ offsetLine < lastOffset.line ||
+ (offsetLine === lastOffset.line && offsetColumn < lastOffset.column)
+ ) {
+ throw new Error(
+ "Section offsets must be ordered and non-overlapping."
+ );
+ }
+ lastOffset = offset;
+
+ const cons = new SourceMapConsumer(
+ util.getArg(s, "map"),
+ aSourceMapURL
+ );
+ return cons.then(consumer => {
+ return {
+ generatedOffset: {
+ // The offset fields are 0-based, but we use 1-based indices when
+ // encoding/decoding from VLQ.
+ generatedLine: offsetLine + 1,
+ generatedColumn: offsetColumn + 1,
+ },
+ consumer,
+ };
+ });
+ })
+ ).then(s => {
+ that._sections = s;
+ return that;
+ });
+ });
+ }
+
+ /**
+ * The list of original sources.
+ */
+ get sources() {
+ const sources = [];
+ for (let i = 0; i < this._sections.length; i++) {
+ for (let j = 0; j < this._sections[i].consumer.sources.length; j++) {
+ sources.push(this._sections[i].consumer.sources[j]);
+ }
+ }
+ return sources;
+ }
+
+ /**
+ * Returns the original source, line, and column information for the generated
+ * source's line and column positions provided. The only argument is an object
+ * with the following properties:
+ *
+ * - line: The line number in the generated source. The line number
+ * is 1-based.
+ * - column: The column number in the generated source. The column
+ * number is 0-based.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - source: The original source file, or null.
+ * - line: The line number in the original source, or null. The
+ * line number is 1-based.
+ * - column: The column number in the original source, or null. The
+ * column number is 0-based.
+ * - name: The original identifier, or null.
+ */
+ originalPositionFor(aArgs) {
+ const needle = {
+ generatedLine: util.getArg(aArgs, "line"),
+ generatedColumn: util.getArg(aArgs, "column"),
+ };
+
+ // Find the section containing the generated position we're trying to map
+ // to an original position.
+ const sectionIndex = binarySearch.search(
+ needle,
+ this._sections,
+ function (aNeedle, section) {
+ const cmp =
+ aNeedle.generatedLine - section.generatedOffset.generatedLine;
+ if (cmp) {
+ return cmp;
+ }
+
+ return (
+ aNeedle.generatedColumn - section.generatedOffset.generatedColumn
+ );
+ }
+ );
+ const section = this._sections[sectionIndex];
+
+ if (!section) {
+ return {
+ source: null,
+ line: null,
+ column: null,
+ name: null,
+ };
+ }
+
+ return section.consumer.originalPositionFor({
+ line: needle.generatedLine - (section.generatedOffset.generatedLine - 1),
+ column:
+ needle.generatedColumn -
+ (section.generatedOffset.generatedLine === needle.generatedLine
+ ? section.generatedOffset.generatedColumn - 1
+ : 0),
+ bias: aArgs.bias,
+ });
+ }
+
+ /**
+ * Return true if we have the source content for every source in the source
+ * map, false otherwise.
+ */
+ hasContentsOfAllSources() {
+ return this._sections.every(function (s) {
+ return s.consumer.hasContentsOfAllSources();
+ });
+ }
+
+ /**
+ * Returns the original source content. The only argument is the url of the
+ * original source file. Returns null if no original source content is
+ * available.
+ */
+ sourceContentFor(aSource, nullOnMissing) {
+ for (let i = 0; i < this._sections.length; i++) {
+ const section = this._sections[i];
+
+ const content = section.consumer.sourceContentFor(aSource, true);
+ if (content) {
+ return content;
+ }
+ }
+ if (nullOnMissing) {
+ return null;
+ }
+ throw new Error('"' + aSource + '" is not in the SourceMap.');
+ }
+
+ _findSectionIndex(source) {
+ for (let i = 0; i < this._sections.length; i++) {
+ const { consumer } = this._sections[i];
+ if (consumer._findSourceIndex(source) !== -1) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the generated line and column information for the original source,
+ * line, and column positions provided. The only argument is an object with
+ * the following properties:
+ *
+ * - source: The filename of the original source.
+ * - line: The line number in the original source. The line number
+ * is 1-based.
+ * - column: The column number in the original source. The column
+ * number is 0-based.
+ *
+ * and an object is returned with the following properties:
+ *
+ * - line: The line number in the generated source, or null. The
+ * line number is 1-based.
+ * - column: The column number in the generated source, or null.
+ * The column number is 0-based.
+ */
+ generatedPositionFor(aArgs) {
+ const index = this._findSectionIndex(util.getArg(aArgs, "source"));
+ const section = index >= 0 ? this._sections[index] : null;
+ const nextSection =
+ index >= 0 && index + 1 < this._sections.length
+ ? this._sections[index + 1]
+ : null;
+
+ const generatedPosition =
+ section && section.consumer.generatedPositionFor(aArgs);
+ if (generatedPosition && generatedPosition.line !== null) {
+ const lineShift = section.generatedOffset.generatedLine - 1;
+ const columnShift = section.generatedOffset.generatedColumn - 1;
+
+ if (generatedPosition.line === 1) {
+ generatedPosition.column += columnShift;
+ if (typeof generatedPosition.lastColumn === "number") {
+ generatedPosition.lastColumn += columnShift;
+ }
+ }
+
+ if (
+ generatedPosition.lastColumn === Infinity &&
+ nextSection &&
+ generatedPosition.line === nextSection.generatedOffset.generatedLine
+ ) {
+ generatedPosition.lastColumn =
+ nextSection.generatedOffset.generatedColumn - 2;
+ }
+ generatedPosition.line += lineShift;
+
+ return generatedPosition;
+ }
+
+ return {
+ line: null,
+ column: null,
+ lastColumn: null,
+ };
+ }
+
+ allGeneratedPositionsFor(aArgs) {
+ const index = this._findSectionIndex(util.getArg(aArgs, "source"));
+ const section = index >= 0 ? this._sections[index] : null;
+ const nextSection =
+ index >= 0 && index + 1 < this._sections.length
+ ? this._sections[index + 1]
+ : null;
+
+ if (!section) return [];
+
+ return section.consumer
+ .allGeneratedPositionsFor(aArgs)
+ .map(generatedPosition => {
+ const lineShift = section.generatedOffset.generatedLine - 1;
+ const columnShift = section.generatedOffset.generatedColumn - 1;
+
+ if (generatedPosition.line === 1) {
+ generatedPosition.column += columnShift;
+ if (typeof generatedPosition.lastColumn === "number") {
+ generatedPosition.lastColumn += columnShift;
+ }
+ }
+
+ if (
+ generatedPosition.lastColumn === Infinity &&
+ nextSection &&
+ generatedPosition.line === nextSection.generatedOffset.generatedLine
+ ) {
+ generatedPosition.lastColumn =
+ nextSection.generatedOffset.generatedColumn - 2;
+ }
+ generatedPosition.line += lineShift;
+
+ return generatedPosition;
+ });
+ }
+
+ eachMapping(aCallback, aContext, aOrder) {
+ this._sections.forEach((section, index) => {
+ const nextSection =
+ index + 1 < this._sections.length ? this._sections[index + 1] : null;
+ const { generatedOffset } = section;
+
+ const lineShift = generatedOffset.generatedLine - 1;
+ const columnShift = generatedOffset.generatedColumn - 1;
+
+ section.consumer.eachMapping(
+ function (mapping) {
+ if (mapping.generatedLine === 1) {
+ mapping.generatedColumn += columnShift;
+
+ if (typeof mapping.lastGeneratedColumn === "number") {
+ mapping.lastGeneratedColumn += columnShift;
+ }
+ }
+
+ if (
+ mapping.lastGeneratedColumn === Infinity &&
+ nextSection &&
+ mapping.generatedLine === nextSection.generatedOffset.generatedLine
+ ) {
+ mapping.lastGeneratedColumn =
+ nextSection.generatedOffset.generatedColumn - 2;
+ }
+ mapping.generatedLine += lineShift;
+
+ aCallback.call(this, mapping);
+ },
+ aContext,
+ aOrder
+ );
+ });
+ }
+
+ computeColumnSpans() {
+ for (let i = 0; i < this._sections.length; i++) {
+ this._sections[i].consumer.computeColumnSpans();
+ }
+ }
+
+ destroy() {
+ for (let i = 0; i < this._sections.length; i++) {
+ this._sections[i].consumer.destroy();
+ }
+ }
+}
+exports.IndexedSourceMapConsumer = IndexedSourceMapConsumer;
+
+/*
+ * Cheat to get around inter-twingled classes. `factory()` can be at the end
+ * where it has access to non-hoisted classes, but it gets hoisted itself.
+ */
+function _factory(aSourceMap, aSourceMapURL) {
+ let sourceMap = aSourceMap;
+ if (typeof aSourceMap === "string") {
+ sourceMap = util.parseSourceMapInput(aSourceMap);
+ }
+
+ const consumer =
+ sourceMap.sections != null
+ ? new IndexedSourceMapConsumer(sourceMap, aSourceMapURL)
+ : new BasicSourceMapConsumer(sourceMap, aSourceMapURL);
+ return Promise.resolve(consumer);
+}
+
+function _factoryBSM(aSourceMap, aSourceMapURL) {
+ return BasicSourceMapConsumer.fromSourceMap(aSourceMap, aSourceMapURL);
+}
diff --git a/devtools/client/shared/vendor/source-map/lib/source-map-generator.js b/devtools/client/shared/vendor/source-map/lib/source-map-generator.js
new file mode 100644
index 0000000000..847017fac6
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/source-map-generator.js
@@ -0,0 +1,439 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+const base64VLQ = require("./base64-vlq");
+const util = require("./util");
+const ArraySet = require("./array-set").ArraySet;
+const MappingList = require("./mapping-list").MappingList;
+
+/**
+ * An instance of the SourceMapGenerator represents a source map which is
+ * being built incrementally. You may pass an object with the following
+ * properties:
+ *
+ * - file: The filename of the generated source.
+ * - sourceRoot: A root for all relative URLs in this source map.
+ */
+class SourceMapGenerator {
+ constructor(aArgs) {
+ if (!aArgs) {
+ aArgs = {};
+ }
+ this._file = util.getArg(aArgs, "file", null);
+ this._sourceRoot = util.getArg(aArgs, "sourceRoot", null);
+ this._skipValidation = util.getArg(aArgs, "skipValidation", false);
+ this._sources = new ArraySet();
+ this._names = new ArraySet();
+ this._mappings = new MappingList();
+ this._sourcesContents = null;
+ }
+
+ /**
+ * Creates a new SourceMapGenerator based on a SourceMapConsumer
+ *
+ * @param aSourceMapConsumer The SourceMap.
+ */
+ static fromSourceMap(aSourceMapConsumer) {
+ const sourceRoot = aSourceMapConsumer.sourceRoot;
+ const generator = new SourceMapGenerator({
+ file: aSourceMapConsumer.file,
+ sourceRoot,
+ });
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ const newMapping = {
+ generated: {
+ line: mapping.generatedLine,
+ column: mapping.generatedColumn,
+ },
+ };
+
+ if (mapping.source != null) {
+ newMapping.source = mapping.source;
+ if (sourceRoot != null) {
+ newMapping.source = util.relative(sourceRoot, newMapping.source);
+ }
+
+ newMapping.original = {
+ line: mapping.originalLine,
+ column: mapping.originalColumn,
+ };
+
+ if (mapping.name != null) {
+ newMapping.name = mapping.name;
+ }
+ }
+
+ generator.addMapping(newMapping);
+ });
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ let sourceRelative = sourceFile;
+ if (sourceRoot != null) {
+ sourceRelative = util.relative(sourceRoot, sourceFile);
+ }
+
+ if (!generator._sources.has(sourceRelative)) {
+ generator._sources.add(sourceRelative);
+ }
+
+ const content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content != null) {
+ generator.setSourceContent(sourceFile, content);
+ }
+ });
+ return generator;
+ }
+
+ /**
+ * Add a single mapping from original source line and column to the generated
+ * source's line and column for this source map being created. The mapping
+ * object should have the following properties:
+ *
+ * - generated: An object with the generated line and column positions.
+ * - original: An object with the original line and column positions.
+ * - source: The original source file (relative to the sourceRoot).
+ * - name: An optional original token name for this mapping.
+ */
+ addMapping(aArgs) {
+ const generated = util.getArg(aArgs, "generated");
+ const original = util.getArg(aArgs, "original", null);
+ let source = util.getArg(aArgs, "source", null);
+ let name = util.getArg(aArgs, "name", null);
+
+ if (!this._skipValidation) {
+ this._validateMapping(generated, original, source, name);
+ }
+
+ if (source != null) {
+ source = String(source);
+ if (!this._sources.has(source)) {
+ this._sources.add(source);
+ }
+ }
+
+ if (name != null) {
+ name = String(name);
+ if (!this._names.has(name)) {
+ this._names.add(name);
+ }
+ }
+
+ this._mappings.add({
+ generatedLine: generated.line,
+ generatedColumn: generated.column,
+ originalLine: original && original.line,
+ originalColumn: original && original.column,
+ source,
+ name,
+ });
+ }
+
+ /**
+ * Set the source content for a source file.
+ */
+ setSourceContent(aSourceFile, aSourceContent) {
+ let source = aSourceFile;
+ if (this._sourceRoot != null) {
+ source = util.relative(this._sourceRoot, source);
+ }
+
+ if (aSourceContent != null) {
+ // Add the source content to the _sourcesContents map.
+ // Create a new _sourcesContents map if the property is null.
+ if (!this._sourcesContents) {
+ this._sourcesContents = Object.create(null);
+ }
+ this._sourcesContents[util.toSetString(source)] = aSourceContent;
+ } else if (this._sourcesContents) {
+ // Remove the source file from the _sourcesContents map.
+ // If the _sourcesContents map is empty, set the property to null.
+ delete this._sourcesContents[util.toSetString(source)];
+ if (Object.keys(this._sourcesContents).length === 0) {
+ this._sourcesContents = null;
+ }
+ }
+ }
+
+ /**
+ * Applies the mappings of a sub-source-map for a specific source file to the
+ * source map being generated. Each mapping to the supplied source file is
+ * rewritten using the supplied source map. Note: The resolution for the
+ * resulting mappings is the minimium of this map and the supplied map.
+ *
+ * @param aSourceMapConsumer The source map to be applied.
+ * @param aSourceFile Optional. The filename of the source file.
+ * If omitted, SourceMapConsumer's file property will be used.
+ * @param aSourceMapPath Optional. The dirname of the path to the source map
+ * to be applied. If relative, it is relative to the SourceMapConsumer.
+ * This parameter is needed when the two source maps aren't in the same
+ * directory, and the source map to be applied contains relative source
+ * paths. If so, those relative source paths need to be rewritten
+ * relative to the SourceMapGenerator.
+ */
+ applySourceMap(aSourceMapConsumer, aSourceFile, aSourceMapPath) {
+ let sourceFile = aSourceFile;
+ // If aSourceFile is omitted, we will use the file property of the SourceMap
+ if (aSourceFile == null) {
+ if (aSourceMapConsumer.file == null) {
+ throw new Error(
+ "SourceMapGenerator.prototype.applySourceMap requires either an explicit source file, " +
+ 'or the source map\'s "file" property. Both were omitted.'
+ );
+ }
+ sourceFile = aSourceMapConsumer.file;
+ }
+ const sourceRoot = this._sourceRoot;
+ // Make "sourceFile" relative if an absolute Url is passed.
+ if (sourceRoot != null) {
+ sourceFile = util.relative(sourceRoot, sourceFile);
+ }
+ // Applying the SourceMap can add and remove items from the sources and
+ // the names array.
+ const newSources =
+ this._mappings.toArray().length > 0 ? new ArraySet() : this._sources;
+ const newNames = new ArraySet();
+
+ // Find mappings for the "sourceFile"
+ this._mappings.unsortedForEach(function (mapping) {
+ if (mapping.source === sourceFile && mapping.originalLine != null) {
+ // Check if it can be mapped by the source map, then update the mapping.
+ const original = aSourceMapConsumer.originalPositionFor({
+ line: mapping.originalLine,
+ column: mapping.originalColumn,
+ });
+ if (original.source != null) {
+ // Copy mapping
+ mapping.source = original.source;
+ if (aSourceMapPath != null) {
+ mapping.source = util.join(aSourceMapPath, mapping.source);
+ }
+ if (sourceRoot != null) {
+ mapping.source = util.relative(sourceRoot, mapping.source);
+ }
+ mapping.originalLine = original.line;
+ mapping.originalColumn = original.column;
+ if (original.name != null) {
+ mapping.name = original.name;
+ }
+ }
+ }
+
+ const source = mapping.source;
+ if (source != null && !newSources.has(source)) {
+ newSources.add(source);
+ }
+
+ const name = mapping.name;
+ if (name != null && !newNames.has(name)) {
+ newNames.add(name);
+ }
+ }, this);
+ this._sources = newSources;
+ this._names = newNames;
+
+ // Copy sourcesContents of applied map.
+ aSourceMapConsumer.sources.forEach(function (srcFile) {
+ const content = aSourceMapConsumer.sourceContentFor(srcFile);
+ if (content != null) {
+ if (aSourceMapPath != null) {
+ srcFile = util.join(aSourceMapPath, srcFile);
+ }
+ if (sourceRoot != null) {
+ srcFile = util.relative(sourceRoot, srcFile);
+ }
+ this.setSourceContent(srcFile, content);
+ }
+ }, this);
+ }
+
+ /**
+ * A mapping can have one of the three levels of data:
+ *
+ * 1. Just the generated position.
+ * 2. The Generated position, original position, and original source.
+ * 3. Generated and original position, original source, as well as a name
+ * token.
+ *
+ * To maintain consistency, we validate that any new mapping being added falls
+ * in to one of these categories.
+ */
+ _validateMapping(aGenerated, aOriginal, aSource, aName) {
+ // When aOriginal is truthy but has empty values for .line and .column,
+ // it is most likely a programmer error. In this case we throw a very
+ // specific error message to try to guide them the right way.
+ // For example: https://github.com/Polymer/polymer-bundler/pull/519
+ if (
+ aOriginal &&
+ typeof aOriginal.line !== "number" &&
+ typeof aOriginal.column !== "number"
+ ) {
+ throw new Error(
+ "original.line and original.column are not numbers -- you probably meant to omit " +
+ "the original mapping entirely and only map the generated position. If so, pass " +
+ "null for the original mapping instead of an object with empty or null values."
+ );
+ }
+
+ if (
+ aGenerated &&
+ "line" in aGenerated &&
+ "column" in aGenerated &&
+ aGenerated.line > 0 &&
+ aGenerated.column >= 0 &&
+ !aOriginal &&
+ !aSource &&
+ !aName
+ ) {
+ // Case 1.
+ } else if (
+ aGenerated &&
+ "line" in aGenerated &&
+ "column" in aGenerated &&
+ aOriginal &&
+ "line" in aOriginal &&
+ "column" in aOriginal &&
+ aGenerated.line > 0 &&
+ aGenerated.column >= 0 &&
+ aOriginal.line > 0 &&
+ aOriginal.column >= 0 &&
+ aSource
+ ) {
+ // Cases 2 and 3.
+ } else {
+ throw new Error(
+ "Invalid mapping: " +
+ JSON.stringify({
+ generated: aGenerated,
+ source: aSource,
+ original: aOriginal,
+ name: aName,
+ })
+ );
+ }
+ }
+
+ /**
+ * Serialize the accumulated mappings in to the stream of base 64 VLQs
+ * specified by the source map format.
+ */
+ _serializeMappings() {
+ let previousGeneratedColumn = 0;
+ let previousGeneratedLine = 1;
+ let previousOriginalColumn = 0;
+ let previousOriginalLine = 0;
+ let previousName = 0;
+ let previousSource = 0;
+ let result = "";
+ let next;
+ let mapping;
+ let nameIdx;
+ let sourceIdx;
+
+ const mappings = this._mappings.toArray();
+ for (let i = 0, len = mappings.length; i < len; i++) {
+ mapping = mappings[i];
+ next = "";
+
+ if (mapping.generatedLine !== previousGeneratedLine) {
+ previousGeneratedColumn = 0;
+ while (mapping.generatedLine !== previousGeneratedLine) {
+ next += ";";
+ previousGeneratedLine++;
+ }
+ } else if (i > 0) {
+ if (
+ !util.compareByGeneratedPositionsInflated(mapping, mappings[i - 1])
+ ) {
+ continue;
+ }
+ next += ",";
+ }
+
+ next += base64VLQ.encode(
+ mapping.generatedColumn - previousGeneratedColumn
+ );
+ previousGeneratedColumn = mapping.generatedColumn;
+
+ if (mapping.source != null) {
+ sourceIdx = this._sources.indexOf(mapping.source);
+ next += base64VLQ.encode(sourceIdx - previousSource);
+ previousSource = sourceIdx;
+
+ // lines are stored 0-based in SourceMap spec version 3
+ next += base64VLQ.encode(
+ mapping.originalLine - 1 - previousOriginalLine
+ );
+ previousOriginalLine = mapping.originalLine - 1;
+
+ next += base64VLQ.encode(
+ mapping.originalColumn - previousOriginalColumn
+ );
+ previousOriginalColumn = mapping.originalColumn;
+
+ if (mapping.name != null) {
+ nameIdx = this._names.indexOf(mapping.name);
+ next += base64VLQ.encode(nameIdx - previousName);
+ previousName = nameIdx;
+ }
+ }
+
+ result += next;
+ }
+
+ return result;
+ }
+
+ _generateSourcesContent(aSources, aSourceRoot) {
+ return aSources.map(function (source) {
+ if (!this._sourcesContents) {
+ return null;
+ }
+ if (aSourceRoot != null) {
+ source = util.relative(aSourceRoot, source);
+ }
+ const key = util.toSetString(source);
+ return Object.prototype.hasOwnProperty.call(this._sourcesContents, key)
+ ? this._sourcesContents[key]
+ : null;
+ }, this);
+ }
+
+ /**
+ * Externalize the source map.
+ */
+ toJSON() {
+ const map = {
+ version: this._version,
+ sources: this._sources.toArray(),
+ names: this._names.toArray(),
+ mappings: this._serializeMappings(),
+ };
+ if (this._file != null) {
+ map.file = this._file;
+ }
+ if (this._sourceRoot != null) {
+ map.sourceRoot = this._sourceRoot;
+ }
+ if (this._sourcesContents) {
+ map.sourcesContent = this._generateSourcesContent(
+ map.sources,
+ map.sourceRoot
+ );
+ }
+
+ return map;
+ }
+
+ /**
+ * Render the source map being generated to a string.
+ */
+ toString() {
+ return JSON.stringify(this.toJSON());
+ }
+}
+
+SourceMapGenerator.prototype._version = 3;
+exports.SourceMapGenerator = SourceMapGenerator;
diff --git a/devtools/client/shared/vendor/source-map/lib/source-node.js b/devtools/client/shared/vendor/source-map/lib/source-node.js
new file mode 100644
index 0000000000..ecee1ae620
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/source-node.js
@@ -0,0 +1,430 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+const SourceMapGenerator = require("./source-map-generator").SourceMapGenerator;
+const util = require("./util");
+
+// Matches a Windows-style `\r\n` newline or a `\n` newline used by all other
+// operating systems these days (capturing the result).
+const REGEX_NEWLINE = /(\r?\n)/;
+
+// Newline character code for charCodeAt() comparisons
+const NEWLINE_CODE = 10;
+
+// Private symbol for identifying `SourceNode`s when multiple versions of
+// the source-map library are loaded. This MUST NOT CHANGE across
+// versions!
+const isSourceNode = "$$$isSourceNode$$$";
+
+/**
+ * SourceNodes provide a way to abstract over interpolating/concatenating
+ * snippets of generated JavaScript source code while maintaining the line and
+ * column information associated with the original source code.
+ *
+ * @param aLine The original line number.
+ * @param aColumn The original column number.
+ * @param aSource The original source's filename.
+ * @param aChunks Optional. An array of strings which are snippets of
+ * generated JS, or other SourceNodes.
+ * @param aName The original identifier.
+ */
+class SourceNode {
+ constructor(aLine, aColumn, aSource, aChunks, aName) {
+ this.children = [];
+ this.sourceContents = {};
+ this.line = aLine == null ? null : aLine;
+ this.column = aColumn == null ? null : aColumn;
+ this.source = aSource == null ? null : aSource;
+ this.name = aName == null ? null : aName;
+ this[isSourceNode] = true;
+ if (aChunks != null) this.add(aChunks);
+ }
+
+ /**
+ * Creates a SourceNode from generated code and a SourceMapConsumer.
+ *
+ * @param aGeneratedCode The generated code
+ * @param aSourceMapConsumer The SourceMap for the generated code
+ * @param aRelativePath Optional. The path that relative sources in the
+ * SourceMapConsumer should be relative to.
+ */
+ static fromStringWithSourceMap(
+ aGeneratedCode,
+ aSourceMapConsumer,
+ aRelativePath
+ ) {
+ // The SourceNode we want to fill with the generated code
+ // and the SourceMap
+ const node = new SourceNode();
+
+ // All even indices of this array are one line of the generated code,
+ // while all odd indices are the newlines between two adjacent lines
+ // (since `REGEX_NEWLINE` captures its match).
+ // Processed fragments are accessed by calling `shiftNextLine`.
+ const remainingLines = aGeneratedCode.split(REGEX_NEWLINE);
+ let remainingLinesIndex = 0;
+ const shiftNextLine = function () {
+ const lineContents = getNextLine();
+ // The last line of a file might not have a newline.
+ const newLine = getNextLine() || "";
+ return lineContents + newLine;
+
+ function getNextLine() {
+ return remainingLinesIndex < remainingLines.length
+ ? remainingLines[remainingLinesIndex++]
+ : undefined;
+ }
+ };
+
+ // We need to remember the position of "remainingLines"
+ let lastGeneratedLine = 1,
+ lastGeneratedColumn = 0;
+
+ // The generate SourceNodes we need a code range.
+ // To extract it current and last mapping is used.
+ // Here we store the last mapping.
+ let lastMapping = null;
+ let nextLine;
+
+ aSourceMapConsumer.eachMapping(function (mapping) {
+ if (lastMapping !== null) {
+ // We add the code from "lastMapping" to "mapping":
+ // First check if there is a new line in between.
+ if (lastGeneratedLine < mapping.generatedLine) {
+ // Associate first line with "lastMapping"
+ addMappingWithCode(lastMapping, shiftNextLine());
+ lastGeneratedLine++;
+ lastGeneratedColumn = 0;
+ // The remaining code is added without mapping
+ } else {
+ // There is no new line in between.
+ // Associate the code between "lastGeneratedColumn" and
+ // "mapping.generatedColumn" with "lastMapping"
+ nextLine = remainingLines[remainingLinesIndex] || "";
+ const code = nextLine.substr(
+ 0,
+ mapping.generatedColumn - lastGeneratedColumn
+ );
+ remainingLines[remainingLinesIndex] = nextLine.substr(
+ mapping.generatedColumn - lastGeneratedColumn
+ );
+ lastGeneratedColumn = mapping.generatedColumn;
+ addMappingWithCode(lastMapping, code);
+ // No more remaining code, continue
+ lastMapping = mapping;
+ return;
+ }
+ }
+ // We add the generated code until the first mapping
+ // to the SourceNode without any mapping.
+ // Each line is added as separate string.
+ while (lastGeneratedLine < mapping.generatedLine) {
+ node.add(shiftNextLine());
+ lastGeneratedLine++;
+ }
+ if (lastGeneratedColumn < mapping.generatedColumn) {
+ nextLine = remainingLines[remainingLinesIndex] || "";
+ node.add(nextLine.substr(0, mapping.generatedColumn));
+ remainingLines[remainingLinesIndex] = nextLine.substr(
+ mapping.generatedColumn
+ );
+ lastGeneratedColumn = mapping.generatedColumn;
+ }
+ lastMapping = mapping;
+ }, this);
+ // We have processed all mappings.
+ if (remainingLinesIndex < remainingLines.length) {
+ if (lastMapping) {
+ // Associate the remaining code in the current line with "lastMapping"
+ addMappingWithCode(lastMapping, shiftNextLine());
+ }
+ // and add the remaining lines without any mapping
+ node.add(remainingLines.splice(remainingLinesIndex).join(""));
+ }
+
+ // Copy sourcesContent into SourceNode
+ aSourceMapConsumer.sources.forEach(function (sourceFile) {
+ const content = aSourceMapConsumer.sourceContentFor(sourceFile);
+ if (content != null) {
+ if (aRelativePath != null) {
+ sourceFile = util.join(aRelativePath, sourceFile);
+ }
+ node.setSourceContent(sourceFile, content);
+ }
+ });
+
+ return node;
+
+ function addMappingWithCode(mapping, code) {
+ if (mapping === null || mapping.source === undefined) {
+ node.add(code);
+ } else {
+ const source = aRelativePath
+ ? util.join(aRelativePath, mapping.source)
+ : mapping.source;
+ node.add(
+ new SourceNode(
+ mapping.originalLine,
+ mapping.originalColumn,
+ source,
+ code,
+ mapping.name
+ )
+ );
+ }
+ }
+ }
+
+ /**
+ * Add a chunk of generated JS to this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ add(aChunk) {
+ if (Array.isArray(aChunk)) {
+ aChunk.forEach(function (chunk) {
+ this.add(chunk);
+ }, this);
+ } else if (aChunk[isSourceNode] || typeof aChunk === "string") {
+ if (aChunk) {
+ this.children.push(aChunk);
+ }
+ } else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " +
+ aChunk
+ );
+ }
+ return this;
+ }
+
+ /**
+ * Add a chunk of generated JS to the beginning of this source node.
+ *
+ * @param aChunk A string snippet of generated JS code, another instance of
+ * SourceNode, or an array where each member is one of those things.
+ */
+ prepend(aChunk) {
+ if (Array.isArray(aChunk)) {
+ for (let i = aChunk.length - 1; i >= 0; i--) {
+ this.prepend(aChunk[i]);
+ }
+ } else if (aChunk[isSourceNode] || typeof aChunk === "string") {
+ this.children.unshift(aChunk);
+ } else {
+ throw new TypeError(
+ "Expected a SourceNode, string, or an array of SourceNodes and strings. Got " +
+ aChunk
+ );
+ }
+ return this;
+ }
+
+ /**
+ * Walk over the tree of JS snippets in this node and its children. The
+ * walking function is called once for each snippet of JS and is passed that
+ * snippet and the its original associated source's line/column location.
+ *
+ * @param aFn The traversal function.
+ */
+ walk(aFn) {
+ let chunk;
+ for (let i = 0, len = this.children.length; i < len; i++) {
+ chunk = this.children[i];
+ if (chunk[isSourceNode]) {
+ chunk.walk(aFn);
+ } else if (chunk !== "") {
+ aFn(chunk, {
+ source: this.source,
+ line: this.line,
+ column: this.column,
+ name: this.name,
+ });
+ }
+ }
+ }
+
+ /**
+ * Like `String.prototype.join` except for SourceNodes. Inserts `aStr` between
+ * each of `this.children`.
+ *
+ * @param aSep The separator.
+ */
+ join(aSep) {
+ let newChildren;
+ let i;
+ const len = this.children.length;
+ if (len > 0) {
+ newChildren = [];
+ for (i = 0; i < len - 1; i++) {
+ newChildren.push(this.children[i]);
+ newChildren.push(aSep);
+ }
+ newChildren.push(this.children[i]);
+ this.children = newChildren;
+ }
+ return this;
+ }
+
+ /**
+ * Call String.prototype.replace on the very right-most source snippet. Useful
+ * for trimming whitespace from the end of a source node, etc.
+ *
+ * @param aPattern The pattern to replace.
+ * @param aReplacement The thing to replace the pattern with.
+ */
+ replaceRight(aPattern, aReplacement) {
+ const lastChild = this.children[this.children.length - 1];
+ if (lastChild[isSourceNode]) {
+ lastChild.replaceRight(aPattern, aReplacement);
+ } else if (typeof lastChild === "string") {
+ this.children[this.children.length - 1] = lastChild.replace(
+ aPattern,
+ aReplacement
+ );
+ } else {
+ this.children.push("".replace(aPattern, aReplacement));
+ }
+ return this;
+ }
+
+ /**
+ * Set the source content for a source file. This will be added to the SourceMapGenerator
+ * in the sourcesContent field.
+ *
+ * @param aSourceFile The filename of the source file
+ * @param aSourceContent The content of the source file
+ */
+ setSourceContent(aSourceFile, aSourceContent) {
+ this.sourceContents[util.toSetString(aSourceFile)] = aSourceContent;
+ }
+
+ /**
+ * Walk over the tree of SourceNodes. The walking function is called for each
+ * source file content and is passed the filename and source content.
+ *
+ * @param aFn The traversal function.
+ */
+ walkSourceContents(aFn) {
+ for (let i = 0, len = this.children.length; i < len; i++) {
+ if (this.children[i][isSourceNode]) {
+ this.children[i].walkSourceContents(aFn);
+ }
+ }
+
+ const sources = Object.keys(this.sourceContents);
+ for (let i = 0, len = sources.length; i < len; i++) {
+ aFn(util.fromSetString(sources[i]), this.sourceContents[sources[i]]);
+ }
+ }
+
+ /**
+ * Return the string representation of this source node. Walks over the tree
+ * and concatenates all the various snippets together to one string.
+ */
+ toString() {
+ let str = "";
+ this.walk(function (chunk) {
+ str += chunk;
+ });
+ return str;
+ }
+
+ /**
+ * Returns the string representation of this source node along with a source
+ * map.
+ */
+ toStringWithSourceMap(aArgs) {
+ const generated = {
+ code: "",
+ line: 1,
+ column: 0,
+ };
+ const map = new SourceMapGenerator(aArgs);
+ let sourceMappingActive = false;
+ let lastOriginalSource = null;
+ let lastOriginalLine = null;
+ let lastOriginalColumn = null;
+ let lastOriginalName = null;
+ this.walk(function (chunk, original) {
+ generated.code += chunk;
+ if (
+ original.source !== null &&
+ original.line !== null &&
+ original.column !== null
+ ) {
+ if (
+ lastOriginalSource !== original.source ||
+ lastOriginalLine !== original.line ||
+ lastOriginalColumn !== original.column ||
+ lastOriginalName !== original.name
+ ) {
+ map.addMapping({
+ source: original.source,
+ original: {
+ line: original.line,
+ column: original.column,
+ },
+ generated: {
+ line: generated.line,
+ column: generated.column,
+ },
+ name: original.name,
+ });
+ }
+ lastOriginalSource = original.source;
+ lastOriginalLine = original.line;
+ lastOriginalColumn = original.column;
+ lastOriginalName = original.name;
+ sourceMappingActive = true;
+ } else if (sourceMappingActive) {
+ map.addMapping({
+ generated: {
+ line: generated.line,
+ column: generated.column,
+ },
+ });
+ lastOriginalSource = null;
+ sourceMappingActive = false;
+ }
+ for (let idx = 0, length = chunk.length; idx < length; idx++) {
+ if (chunk.charCodeAt(idx) === NEWLINE_CODE) {
+ generated.line++;
+ generated.column = 0;
+ // Mappings end at eol
+ if (idx + 1 === length) {
+ lastOriginalSource = null;
+ sourceMappingActive = false;
+ } else if (sourceMappingActive) {
+ map.addMapping({
+ source: original.source,
+ original: {
+ line: original.line,
+ column: original.column,
+ },
+ generated: {
+ line: generated.line,
+ column: generated.column,
+ },
+ name: original.name,
+ });
+ }
+ } else {
+ generated.column++;
+ }
+ }
+ });
+ this.walkSourceContents(function (sourceFile, sourceContent) {
+ map.setSourceContent(sourceFile, sourceContent);
+ });
+
+ return { code: generated.code, map };
+ }
+}
+
+exports.SourceNode = SourceNode;
diff --git a/devtools/client/shared/vendor/source-map/lib/url.js b/devtools/client/shared/vendor/source-map/lib/url.js
new file mode 100644
index 0000000000..b0f4434fe5
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/url.js
@@ -0,0 +1,21 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+"use strict";
+
+/**
+ * Browser 'URL' implementations have been found to handle non-standard URL
+ * schemes poorly, and schemes like
+ *
+ * webpack:///src/folder/file.js
+ *
+ * are very common in source maps. For the time being we use a JS
+ * implementation in these contexts instead. See
+ *
+ * * https://bugzilla.mozilla.org/show_bug.cgi?id=1374505
+ * * https://bugs.chromium.org/p/chromium/issues/detail?id=734880
+ */
+module.exports = require("../../whatwg-url.js").URL;
diff --git a/devtools/client/shared/vendor/source-map/lib/util.js b/devtools/client/shared/vendor/source-map/lib/util.js
new file mode 100644
index 0000000000..20fe8f6859
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/util.js
@@ -0,0 +1,444 @@
+/* -*- Mode: js; js-indent-level: 2; -*- */
+/*
+ * Copyright 2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+
+const URL = require("./url");
+
+/**
+ * This is a helper function for getting values from parameter/options
+ * objects.
+ *
+ * @param args The object we are extracting values from
+ * @param name The name of the property we are getting.
+ * @param defaultValue An optional value to return if the property is missing
+ * from the object. If this is not specified and the property is missing, an
+ * error will be thrown.
+ */
+function getArg(aArgs, aName, aDefaultValue) {
+ if (aName in aArgs) {
+ return aArgs[aName];
+ } else if (arguments.length === 3) {
+ return aDefaultValue;
+ }
+ throw new Error('"' + aName + '" is a required argument.');
+}
+exports.getArg = getArg;
+
+const supportsNullProto = (function () {
+ const obj = Object.create(null);
+ return !("__proto__" in obj);
+})();
+
+function identity(s) {
+ return s;
+}
+
+/**
+ * Because behavior goes wacky when you set `__proto__` on objects, we
+ * have to prefix all the strings in our set with an arbitrary character.
+ *
+ * See https://github.com/mozilla/source-map/pull/31 and
+ * https://github.com/mozilla/source-map/issues/30
+ *
+ * @param String aStr
+ */
+function toSetString(aStr) {
+ if (isProtoString(aStr)) {
+ return "$" + aStr;
+ }
+
+ return aStr;
+}
+exports.toSetString = supportsNullProto ? identity : toSetString;
+
+function fromSetString(aStr) {
+ if (isProtoString(aStr)) {
+ return aStr.slice(1);
+ }
+
+ return aStr;
+}
+exports.fromSetString = supportsNullProto ? identity : fromSetString;
+
+function isProtoString(s) {
+ if (!s) {
+ return false;
+ }
+
+ const length = s.length;
+
+ if (length < 9 /* "__proto__".length */) {
+ return false;
+ }
+
+ /* eslint-disable no-multi-spaces */
+ if (
+ s.charCodeAt(length - 1) !== 95 /* '_' */ ||
+ s.charCodeAt(length - 2) !== 95 /* '_' */ ||
+ s.charCodeAt(length - 3) !== 111 /* 'o' */ ||
+ s.charCodeAt(length - 4) !== 116 /* 't' */ ||
+ s.charCodeAt(length - 5) !== 111 /* 'o' */ ||
+ s.charCodeAt(length - 6) !== 114 /* 'r' */ ||
+ s.charCodeAt(length - 7) !== 112 /* 'p' */ ||
+ s.charCodeAt(length - 8) !== 95 /* '_' */ ||
+ s.charCodeAt(length - 9) !== 95 /* '_' */
+ ) {
+ return false;
+ }
+ /* eslint-enable no-multi-spaces */
+
+ for (let i = length - 10; i >= 0; i--) {
+ if (s.charCodeAt(i) !== 36 /* '$' */) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function strcmp(aStr1, aStr2) {
+ if (aStr1 === aStr2) {
+ return 0;
+ }
+
+ if (aStr1 === null) {
+ return 1; // aStr2 !== null
+ }
+
+ if (aStr2 === null) {
+ return -1; // aStr1 !== null
+ }
+
+ if (aStr1 > aStr2) {
+ return 1;
+ }
+
+ return -1;
+}
+
+/**
+ * Comparator between two mappings with inflated source and name strings where
+ * the generated positions are compared.
+ */
+function compareByGeneratedPositionsInflated(mappingA, mappingB) {
+ let cmp = mappingA.generatedLine - mappingB.generatedLine;
+ if (cmp !== 0) {
+ return cmp;
+ }
+
+ cmp = mappingA.generatedColumn - mappingB.generatedColumn;
+ if (cmp !== 0) {
+ return cmp;
+ }
+
+ cmp = strcmp(mappingA.source, mappingB.source);
+ if (cmp !== 0) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalLine - mappingB.originalLine;
+ if (cmp !== 0) {
+ return cmp;
+ }
+
+ cmp = mappingA.originalColumn - mappingB.originalColumn;
+ if (cmp !== 0) {
+ return cmp;
+ }
+
+ return strcmp(mappingA.name, mappingB.name);
+}
+exports.compareByGeneratedPositionsInflated =
+ compareByGeneratedPositionsInflated;
+
+/**
+ * Strip any JSON XSSI avoidance prefix from the string (as documented
+ * in the source maps specification), and then parse the string as
+ * JSON.
+ */
+function parseSourceMapInput(str) {
+ return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, ""));
+}
+exports.parseSourceMapInput = parseSourceMapInput;
+
+// We use 'http' as the base here because we want URLs processed relative
+// to the safe base to be treated as "special" URLs during parsing using
+// the WHATWG URL parsing. This ensures that backslash normalization
+// applies to the path and such.
+const PROTOCOL = "http:";
+const PROTOCOL_AND_HOST = `${PROTOCOL}//host`;
+
+/**
+ * Make it easy to create small utilities that tweak a URL's path.
+ */
+function createSafeHandler(cb) {
+ return input => {
+ const type = getURLType(input);
+ const base = buildSafeBase(input);
+ const url = new URL(input, base);
+
+ cb(url);
+
+ const result = url.toString();
+
+ if (type === "absolute") {
+ return result;
+ } else if (type === "scheme-relative") {
+ return result.slice(PROTOCOL.length);
+ } else if (type === "path-absolute") {
+ return result.slice(PROTOCOL_AND_HOST.length);
+ }
+
+ // This assumes that the callback will only change
+ // the path, search and hash values.
+ return computeRelativeURL(base, result);
+ };
+}
+
+function withBase(url, base) {
+ return new URL(url, base).toString();
+}
+
+function buildUniqueSegment(prefix, str) {
+ let id = 0;
+ do {
+ const ident = prefix + id++;
+ if (str.indexOf(ident) === -1) return ident;
+ } while (true);
+}
+
+function buildSafeBase(str) {
+ const maxDotParts = str.split("..").length - 1;
+
+ // If we used a segment that also existed in `str`, then we would be unable
+ // to compute relative paths. For example, if `segment` were just "a":
+ //
+ // const url = "../../a/"
+ // const base = buildSafeBase(url); // http://host/a/a/
+ // const joined = "http://host/a/";
+ // const result = relative(base, joined);
+ //
+ // Expected: "../../a/";
+ // Actual: "a/"
+ //
+ const segment = buildUniqueSegment("p", str);
+
+ let base = `${PROTOCOL_AND_HOST}/`;
+ for (let i = 0; i < maxDotParts; i++) {
+ base += `${segment}/`;
+ }
+ return base;
+}
+
+const ABSOLUTE_SCHEME = /^[A-Za-z0-9\+\-\.]+:/;
+function getURLType(url) {
+ if (url[0] === "/") {
+ if (url[1] === "/") return "scheme-relative";
+ return "path-absolute";
+ }
+
+ return ABSOLUTE_SCHEME.test(url) ? "absolute" : "path-relative";
+}
+
+/**
+ * Given two URLs that are assumed to be on the same
+ * protocol/host/user/password build a relative URL from the
+ * path, params, and hash values.
+ *
+ * @param rootURL The root URL that the target will be relative to.
+ * @param targetURL The target that the relative URL points to.
+ * @return A rootURL-relative, normalized URL value.
+ */
+function computeRelativeURL(rootURL, targetURL) {
+ if (typeof rootURL === "string") rootURL = new URL(rootURL);
+ if (typeof targetURL === "string") targetURL = new URL(targetURL);
+
+ const targetParts = targetURL.pathname.split("/");
+ const rootParts = rootURL.pathname.split("/");
+
+ // If we've got a URL path ending with a "/", we remove it since we'd
+ // otherwise be relative to the wrong location.
+ if (rootParts.length > 0 && !rootParts[rootParts.length - 1]) {
+ rootParts.pop();
+ }
+
+ while (
+ targetParts.length > 0 &&
+ rootParts.length > 0 &&
+ targetParts[0] === rootParts[0]
+ ) {
+ targetParts.shift();
+ rootParts.shift();
+ }
+
+ const relativePath = rootParts
+ .map(() => "..")
+ .concat(targetParts)
+ .join("/");
+
+ return relativePath + targetURL.search + targetURL.hash;
+}
+
+/**
+ * Given a URL, ensure that it is treated as a directory URL.
+ *
+ * @param url
+ * @return A normalized URL value.
+ */
+const ensureDirectory = createSafeHandler(url => {
+ url.pathname = url.pathname.replace(/\/?$/, "/");
+});
+
+/**
+ * Given a URL, strip off any filename if one is present.
+ *
+ * @param url
+ * @return A normalized URL value.
+ */
+const trimFilename = createSafeHandler(url => {
+ url.href = new URL(".", url.toString()).toString();
+});
+
+/**
+ * Normalize a given URL.
+ * * Convert backslashes.
+ * * Remove any ".." and "." segments.
+ *
+ * @param url
+ * @return A normalized URL value.
+ */
+const normalize = createSafeHandler(url => {});
+exports.normalize = normalize;
+
+/**
+ * Joins two paths/URLs.
+ *
+ * All returned URLs will be normalized.
+ *
+ * @param aRoot The root path or URL. Assumed to reference a directory.
+ * @param aPath The path or URL to be joined with the root.
+ * @return A joined and normalized URL value.
+ */
+function join(aRoot, aPath) {
+ const pathType = getURLType(aPath);
+ const rootType = getURLType(aRoot);
+
+ aRoot = ensureDirectory(aRoot);
+
+ if (pathType === "absolute") {
+ return withBase(aPath, undefined);
+ }
+ if (rootType === "absolute") {
+ return withBase(aPath, aRoot);
+ }
+
+ if (pathType === "scheme-relative") {
+ return normalize(aPath);
+ }
+ if (rootType === "scheme-relative") {
+ return withBase(aPath, withBase(aRoot, PROTOCOL_AND_HOST)).slice(
+ PROTOCOL.length
+ );
+ }
+
+ if (pathType === "path-absolute") {
+ return normalize(aPath);
+ }
+ if (rootType === "path-absolute") {
+ return withBase(aPath, withBase(aRoot, PROTOCOL_AND_HOST)).slice(
+ PROTOCOL_AND_HOST.length
+ );
+ }
+
+ const base = buildSafeBase(aPath + aRoot);
+ const newPath = withBase(aPath, withBase(aRoot, base));
+ return computeRelativeURL(base, newPath);
+}
+exports.join = join;
+
+/**
+ * Make a path relative to a URL or another path. If returning a
+ * relative URL is not possible, the original target will be returned.
+ * All returned URLs will be normalized.
+ *
+ * @param aRoot The root path or URL.
+ * @param aPath The path or URL to be made relative to aRoot.
+ * @return A rootURL-relative (if possible), normalized URL value.
+ */
+function relative(rootURL, targetURL) {
+ const result = relativeIfPossible(rootURL, targetURL);
+
+ return typeof result === "string" ? result : normalize(targetURL);
+}
+exports.relative = relative;
+
+function relativeIfPossible(rootURL, targetURL) {
+ const urlType = getURLType(rootURL);
+ if (urlType !== getURLType(targetURL)) {
+ return null;
+ }
+
+ const base = buildSafeBase(rootURL + targetURL);
+ const root = new URL(rootURL, base);
+ const target = new URL(targetURL, base);
+
+ try {
+ new URL("", target.toString());
+ } catch (err) {
+ // Bail if the URL doesn't support things being relative to it,
+ // For example, data: and blob: URLs.
+ return null;
+ }
+
+ if (
+ target.protocol !== root.protocol ||
+ target.user !== root.user ||
+ target.password !== root.password ||
+ target.hostname !== root.hostname ||
+ target.port !== root.port
+ ) {
+ return null;
+ }
+
+ return computeRelativeURL(root, target);
+}
+
+/**
+ * Compute the URL of a source given the the source root, the source's
+ * URL, and the source map's URL.
+ */
+function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) {
+ // The source map spec states that "sourceRoot" and "sources" entries are to be appended. While
+ // that is a little vague, implementations have generally interpreted that as joining the
+ // URLs with a `/` between then, assuming the "sourceRoot" doesn't already end with one.
+ // For example,
+ //
+ // sourceRoot: "some-dir",
+ // sources: ["/some-path.js"]
+ //
+ // and
+ //
+ // sourceRoot: "some-dir/",
+ // sources: ["/some-path.js"]
+ //
+ // must behave as "some-dir/some-path.js".
+ //
+ // With this library's the transition to a more URL-focused implementation, that behavior is
+ // preserved here. To acheive that, we trim the "/" from absolute-path when a sourceRoot value
+ // is present in order to make the sources entries behave as if they are relative to the
+ // "sourceRoot", as they would have if the two strings were simply concated.
+ if (sourceRoot && getURLType(sourceURL) === "path-absolute") {
+ sourceURL = sourceURL.replace(/^\//, "");
+ }
+
+ let url = normalize(sourceURL || "");
+
+ // Parsing URLs can be expensive, so we only perform these joins when needed.
+ if (sourceRoot) url = join(sourceRoot, url);
+ if (sourceMapURL) url = join(trimFilename(sourceMapURL), url);
+ return url;
+}
+exports.computeSourceURL = computeSourceURL;
diff --git a/devtools/client/shared/vendor/source-map/lib/wasm.js b/devtools/client/shared/vendor/source-map/lib/wasm.js
new file mode 100644
index 0000000000..3091d9ee26
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/lib/wasm.js
@@ -0,0 +1,138 @@
+const readWasm = require("../lib/read-wasm");
+
+/**
+ * Provide the JIT with a nice shape / hidden class.
+ */
+function Mapping() {
+ this.generatedLine = 0;
+ this.generatedColumn = 0;
+ this.lastGeneratedColumn = null;
+ this.source = null;
+ this.originalLine = null;
+ this.originalColumn = null;
+ this.name = null;
+}
+
+let cachedWasm = null;
+
+module.exports = function wasm() {
+ if (cachedWasm) {
+ return cachedWasm;
+ }
+
+ const callbackStack = [];
+
+ cachedWasm = readWasm()
+ .then(buffer => {
+ return WebAssembly.instantiate(buffer, {
+ env: {
+ mapping_callback(
+ generatedLine,
+ generatedColumn,
+
+ hasLastGeneratedColumn,
+ lastGeneratedColumn,
+
+ hasOriginal,
+ source,
+ originalLine,
+ originalColumn,
+
+ hasName,
+ name
+ ) {
+ const mapping = new Mapping();
+ // JS uses 1-based line numbers, wasm uses 0-based.
+ mapping.generatedLine = generatedLine + 1;
+ mapping.generatedColumn = generatedColumn;
+
+ if (hasLastGeneratedColumn) {
+ // JS uses inclusive last generated column, wasm uses exclusive.
+ mapping.lastGeneratedColumn = lastGeneratedColumn - 1;
+ }
+
+ if (hasOriginal) {
+ mapping.source = source;
+ // JS uses 1-based line numbers, wasm uses 0-based.
+ mapping.originalLine = originalLine + 1;
+ mapping.originalColumn = originalColumn;
+
+ if (hasName) {
+ mapping.name = name;
+ }
+ }
+
+ callbackStack[callbackStack.length - 1](mapping);
+ },
+
+ start_all_generated_locations_for() {
+ console.time("all_generated_locations_for");
+ },
+ end_all_generated_locations_for() {
+ console.timeEnd("all_generated_locations_for");
+ },
+
+ start_compute_column_spans() {
+ console.time("compute_column_spans");
+ },
+ end_compute_column_spans() {
+ console.timeEnd("compute_column_spans");
+ },
+
+ start_generated_location_for() {
+ console.time("generated_location_for");
+ },
+ end_generated_location_for() {
+ console.timeEnd("generated_location_for");
+ },
+
+ start_original_location_for() {
+ console.time("original_location_for");
+ },
+ end_original_location_for() {
+ console.timeEnd("original_location_for");
+ },
+
+ start_parse_mappings() {
+ console.time("parse_mappings");
+ },
+ end_parse_mappings() {
+ console.timeEnd("parse_mappings");
+ },
+
+ start_sort_by_generated_location() {
+ console.time("sort_by_generated_location");
+ },
+ end_sort_by_generated_location() {
+ console.timeEnd("sort_by_generated_location");
+ },
+
+ start_sort_by_original_location() {
+ console.time("sort_by_original_location");
+ },
+ end_sort_by_original_location() {
+ console.timeEnd("sort_by_original_location");
+ },
+ },
+ });
+ })
+ .then(Wasm => {
+ return {
+ exports: Wasm.instance.exports,
+ withMappingCallback: (mappingCallback, f) => {
+ callbackStack.push(mappingCallback);
+ try {
+ f();
+ } finally {
+ callbackStack.pop();
+ }
+ },
+ };
+ })
+ .then(null, e => {
+ cachedWasm = null;
+ throw e;
+ });
+
+ return cachedWasm;
+};
diff --git a/devtools/client/shared/vendor/source-map/moz.build b/devtools/client/shared/vendor/source-map/moz.build
new file mode 100644
index 0000000000..75f8cd7c1c
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "lib",
+]
+
+DevToolsModules(
+ "source-map.js",
+)
diff --git a/devtools/client/shared/vendor/source-map/moz.yaml b/devtools/client/shared/vendor/source-map/moz.yaml
new file mode 100644
index 0000000000..6e3629c156
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/moz.yaml
@@ -0,0 +1,47 @@
+schema: 1
+
+bugzilla:
+ product: DevTools
+ component: "General"
+
+origin:
+ name: source-map
+ description: library to generate and consume the source map
+
+ url: https://github.com/mozilla/source-map
+
+ release: 6cc15e15ffa9e042c7add5e16ce128d1e0b08115 (2023-04-04T12:55:01Z).
+ revision: 6cc15e15ffa9e042c7add5e16ce128d1e0b08115
+
+ license: BSD-3-Clause
+ license-file: LICENSE
+
+vendoring:
+ url: https://github.com/mozilla/source-map
+ source-hosting: github
+ tracking: commit
+
+ exclude:
+ - "**"
+
+ keep:
+ - LICENSE
+
+ include:
+ - source-map.js
+ - "lib/*.js"
+ - "lib/*.wasm"
+
+ update-actions:
+ - action: move-file
+ from: '{vendor_dir}/lib/read-wasm-browser.js'
+ to: '{vendor_dir}/lib/read-wasm.js'
+ - action: move-file
+ from: '{vendor_dir}/lib/url-browser.js'
+ to: '{vendor_dir}/lib/url.js'
+
+ patches:
+ # Force using the local vendored whatwg-url module
+ - relative_url.patch
+ # Tweak read-wasm in order to support running in both Jest/Node and Firefox
+ - read_wasm.patch
diff --git a/devtools/client/shared/vendor/source-map/read_wasm.patch b/devtools/client/shared/vendor/source-map/read_wasm.patch
new file mode 100644
index 0000000000..827fc3ec72
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/read_wasm.patch
@@ -0,0 +1,64 @@
+diff --git a/lib/read-wasm.js b/lib/read-wasm.js
+index a4fb29c295516..75d7bd54153ad 100644
+--- a/lib/read-wasm.js
++++ b/lib/read-wasm.js
+@@ -1,22 +1,45 @@
+ "use strict";
+
++const isNode =
++ typeof process !== "undefined" &&
++ process.versions != null &&
++ process.versions.node != null;
++
+ let mappingsWasm = null;
+
+-module.exports = function readWasm() {
+- if (typeof mappingsWasm === "string") {
+- return fetch(mappingsWasm).then(response => response.arrayBuffer());
+- }
+- if (mappingsWasm instanceof ArrayBuffer) {
+- return Promise.resolve(mappingsWasm);
+- }
++if (isNode) {
++ const fs = require("fs");
++ const path = require("path");
+
+- throw new Error(
+- "You must provide the string URL or ArrayBuffer contents " +
+- "of lib/mappings.wasm by calling " +
+- "SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) " +
+- "before using SourceMapConsumer"
+- );
+-};
++ module.exports = function readWasm() {
++ return new Promise((resolve, reject) => {
++ const wasmPath = path.join(__dirname, "mappings.wasm");
++ fs.readFile(wasmPath, null, (error, data) => {
++ if (error) {
++ reject(error);
++ return;
++ }
++
++ resolve(data.buffer);
++ });
++ });
++ };
++} else {
++ module.exports = function readWasm() {
++ if (typeof mappingsWasm === "string") {
++ return fetch(mappingsWasm)
++ .then(response => response.arrayBuffer());
++ }
++ if (mappingsWasm instanceof ArrayBuffer) {
++ return Promise.resolve(mappingsWasm);
++ }
++
++ throw new Error("You must provide the string URL or ArrayBuffer contents " +
++ "of lib/mappings.wasm by calling " +
++ "SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) " +
++ "before using SourceMapConsumer");
++ };
++}
+
+ module.exports.initialize = input => {
+ mappingsWasm = input;
diff --git a/devtools/client/shared/vendor/source-map/relative_url.patch b/devtools/client/shared/vendor/source-map/relative_url.patch
new file mode 100644
index 0000000000..6f5952f6ef
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/relative_url.patch
@@ -0,0 +1,10 @@
+diff --git a/lib/url.js b/lib/url.js
+index c9da02a8ec225..b0f4434fe5b41 100644
+--- a/lib/url.js
++++ b/lib/url.js
+@@ -18,4 +18,4 @@
+ * * https://bugzilla.mozilla.org/show_bug.cgi?id=1374505
+ * * https://bugs.chromium.org/p/chromium/issues/detail?id=734880
+ */
+-module.exports = require("whatwg-url").URL;
++module.exports = require("../../whatwg-url.js").URL;
diff --git a/devtools/client/shared/vendor/source-map/source-map.js b/devtools/client/shared/vendor/source-map/source-map.js
new file mode 100644
index 0000000000..41531fbc4c
--- /dev/null
+++ b/devtools/client/shared/vendor/source-map/source-map.js
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2009-2011 Mozilla Foundation and contributors
+ * Licensed under the New BSD license. See LICENSE.txt or:
+ * http://opensource.org/licenses/BSD-3-Clause
+ */
+exports.SourceMapGenerator =
+ require("./lib/source-map-generator").SourceMapGenerator;
+exports.SourceMapConsumer =
+ require("./lib/source-map-consumer").SourceMapConsumer;
+exports.SourceNode = require("./lib/source-node").SourceNode;
diff --git a/devtools/client/shared/vendor/whatwg-url.js b/devtools/client/shared/vendor/whatwg-url.js
new file mode 100644
index 0000000000..0dcc377add
--- /dev/null
+++ b/devtools/client/shared/vendor/whatwg-url.js
@@ -0,0 +1,8588 @@
+(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.whatwgURL = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
+"use strict";
+const usm = require("./url-state-machine");
+const urlencoded = require("./urlencoded");
+const URLSearchParams = require("./URLSearchParams");
+
+exports.implementation = class URLImpl {
+ constructor(constructorArgs) {
+ const url = constructorArgs[0];
+ const base = constructorArgs[1];
+
+ let parsedBase = null;
+ if (base !== undefined) {
+ parsedBase = usm.basicURLParse(base);
+ if (parsedBase === null) {
+ throw new TypeError(`Invalid base URL: ${base}`);
+ }
+ }
+
+ const parsedURL = usm.basicURLParse(url, { baseURL: parsedBase });
+ if (parsedURL === null) {
+ throw new TypeError(`Invalid URL: ${url}`);
+ }
+
+ const query = parsedURL.query !== null ? parsedURL.query : "";
+
+ this._url = parsedURL;
+
+ // We cannot invoke the "new URLSearchParams object" algorithm without going through the constructor, which strips
+ // question mark by default. Therefore the doNotStripQMark hack is used.
+ this._query = URLSearchParams.createImpl([query], { doNotStripQMark: true });
+ this._query._url = this;
+ }
+
+ get href() {
+ return usm.serializeURL(this._url);
+ }
+
+ set href(v) {
+ const parsedURL = usm.basicURLParse(v);
+ if (parsedURL === null) {
+ throw new TypeError(`Invalid URL: ${v}`);
+ }
+
+ this._url = parsedURL;
+
+ this._query._list.splice(0);
+ const { query } = parsedURL;
+ if (query !== null) {
+ this._query._list = urlencoded.parseUrlencoded(query);
+ }
+ }
+
+ get origin() {
+ return usm.serializeURLOrigin(this._url);
+ }
+
+ get protocol() {
+ return this._url.scheme + ":";
+ }
+
+ set protocol(v) {
+ usm.basicURLParse(v + ":", { url: this._url, stateOverride: "scheme start" });
+ }
+
+ get username() {
+ return this._url.username;
+ }
+
+ set username(v) {
+ if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
+ return;
+ }
+
+ usm.setTheUsername(this._url, v);
+ }
+
+ get password() {
+ return this._url.password;
+ }
+
+ set password(v) {
+ if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
+ return;
+ }
+
+ usm.setThePassword(this._url, v);
+ }
+
+ get host() {
+ const url = this._url;
+
+ if (url.host === null) {
+ return "";
+ }
+
+ if (url.port === null) {
+ return usm.serializeHost(url.host);
+ }
+
+ return usm.serializeHost(url.host) + ":" + usm.serializeInteger(url.port);
+ }
+
+ set host(v) {
+ if (this._url.cannotBeABaseURL) {
+ return;
+ }
+
+ usm.basicURLParse(v, { url: this._url, stateOverride: "host" });
+ }
+
+ get hostname() {
+ if (this._url.host === null) {
+ return "";
+ }
+
+ return usm.serializeHost(this._url.host);
+ }
+
+ set hostname(v) {
+ if (this._url.cannotBeABaseURL) {
+ return;
+ }
+
+ usm.basicURLParse(v, { url: this._url, stateOverride: "hostname" });
+ }
+
+ get port() {
+ if (this._url.port === null) {
+ return "";
+ }
+
+ return usm.serializeInteger(this._url.port);
+ }
+
+ set port(v) {
+ if (usm.cannotHaveAUsernamePasswordPort(this._url)) {
+ return;
+ }
+
+ if (v === "") {
+ this._url.port = null;
+ } else {
+ usm.basicURLParse(v, { url: this._url, stateOverride: "port" });
+ }
+ }
+
+ get pathname() {
+ if (this._url.cannotBeABaseURL) {
+ return this._url.path[0];
+ }
+
+ if (this._url.path.length === 0) {
+ return "";
+ }
+
+ return "/" + this._url.path.join("/");
+ }
+
+ set pathname(v) {
+ if (this._url.cannotBeABaseURL) {
+ return;
+ }
+
+ this._url.path = [];
+ usm.basicURLParse(v, { url: this._url, stateOverride: "path start" });
+ }
+
+ get search() {
+ if (this._url.query === null || this._url.query === "") {
+ return "";
+ }
+
+ return "?" + this._url.query;
+ }
+
+ set search(v) {
+ const url = this._url;
+
+ if (v === "") {
+ url.query = null;
+ this._query._list = [];
+ return;
+ }
+
+ const input = v[0] === "?" ? v.substring(1) : v;
+ url.query = "";
+ usm.basicURLParse(input, { url, stateOverride: "query" });
+ this._query._list = urlencoded.parseUrlencoded(input);
+ }
+
+ get searchParams() {
+ return this._query;
+ }
+
+ get hash() {
+ if (this._url.fragment === null || this._url.fragment === "") {
+ return "";
+ }
+
+ return "#" + this._url.fragment;
+ }
+
+ set hash(v) {
+ if (v === "") {
+ this._url.fragment = null;
+ return;
+ }
+
+ const input = v[0] === "#" ? v.substring(1) : v;
+ this._url.fragment = "";
+ usm.basicURLParse(input, { url: this._url, stateOverride: "fragment" });
+ }
+
+ toJSON() {
+ return this.href;
+ }
+};
+
+},{"./URLSearchParams":4,"./url-state-machine":7,"./urlencoded":8}],2:[function(require,module,exports){
+"use strict";
+
+const conversions = require("webidl-conversions");
+const utils = require("./utils.js");
+
+const impl = utils.implSymbol;
+
+class URL {
+ constructor(url) {
+ if (arguments.length < 1) {
+ throw new TypeError("Failed to construct 'URL': 1 argument required, but only " + arguments.length + " present.");
+ }
+ const args = [];
+ {
+ let curArg = arguments[0];
+ curArg = conversions["USVString"](curArg, { context: "Failed to construct 'URL': parameter 1" });
+ args.push(curArg);
+ }
+ {
+ let curArg = arguments[1];
+ if (curArg !== undefined) {
+ curArg = conversions["USVString"](curArg, { context: "Failed to construct 'URL': parameter 2" });
+ }
+ args.push(curArg);
+ }
+ return iface.setup(Object.create(new.target.prototype), args);
+ }
+
+ toJSON() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl].toJSON();
+ }
+
+ get href() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["href"];
+ }
+
+ set href(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'href' property on 'URL': The provided value" });
+
+ this[impl]["href"] = V;
+ }
+
+ toString() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+ return this[impl]["href"];
+ }
+
+ get origin() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["origin"];
+ }
+
+ get protocol() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["protocol"];
+ }
+
+ set protocol(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'protocol' property on 'URL': The provided value" });
+
+ this[impl]["protocol"] = V;
+ }
+
+ get username() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["username"];
+ }
+
+ set username(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'username' property on 'URL': The provided value" });
+
+ this[impl]["username"] = V;
+ }
+
+ get password() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["password"];
+ }
+
+ set password(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'password' property on 'URL': The provided value" });
+
+ this[impl]["password"] = V;
+ }
+
+ get host() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["host"];
+ }
+
+ set host(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'host' property on 'URL': The provided value" });
+
+ this[impl]["host"] = V;
+ }
+
+ get hostname() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["hostname"];
+ }
+
+ set hostname(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'hostname' property on 'URL': The provided value" });
+
+ this[impl]["hostname"] = V;
+ }
+
+ get port() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["port"];
+ }
+
+ set port(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'port' property on 'URL': The provided value" });
+
+ this[impl]["port"] = V;
+ }
+
+ get pathname() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["pathname"];
+ }
+
+ set pathname(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'pathname' property on 'URL': The provided value" });
+
+ this[impl]["pathname"] = V;
+ }
+
+ get search() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["search"];
+ }
+
+ set search(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'search' property on 'URL': The provided value" });
+
+ this[impl]["search"] = V;
+ }
+
+ get searchParams() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return utils.getSameObject(this, "searchParams", () => {
+ return utils.tryWrapperForImpl(this[impl]["searchParams"]);
+ });
+ }
+
+ get hash() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl]["hash"];
+ }
+
+ set hash(V) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ V = conversions["USVString"](V, { context: "Failed to set the 'hash' property on 'URL': The provided value" });
+
+ this[impl]["hash"] = V;
+ }
+}
+Object.defineProperties(URL.prototype, {
+ toJSON: { enumerable: true },
+ href: { enumerable: true },
+ toString: { enumerable: true },
+ origin: { enumerable: true },
+ protocol: { enumerable: true },
+ username: { enumerable: true },
+ password: { enumerable: true },
+ host: { enumerable: true },
+ hostname: { enumerable: true },
+ port: { enumerable: true },
+ pathname: { enumerable: true },
+ search: { enumerable: true },
+ searchParams: { enumerable: true },
+ hash: { enumerable: true },
+ [Symbol.toStringTag]: { value: "URL", configurable: true }
+});
+const iface = {
+ // When an interface-module that implements this interface as a mixin is loaded, it will append its own `.is()`
+ // method into this array. It allows objects that directly implements *those* interfaces to be recognized as
+ // implementing this mixin interface.
+ _mixedIntoPredicates: [],
+ is(obj) {
+ if (obj) {
+ if (utils.hasOwn(obj, impl) && obj[impl] instanceof Impl.implementation) {
+ return true;
+ }
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
+ if (isMixedInto(obj)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ isImpl(obj) {
+ if (obj) {
+ if (obj instanceof Impl.implementation) {
+ return true;
+ }
+
+ const wrapper = utils.wrapperForImpl(obj);
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
+ if (isMixedInto(wrapper)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ convert(obj, { context = "The provided value" } = {}) {
+ if (module.exports.is(obj)) {
+ return utils.implForWrapper(obj);
+ }
+ throw new TypeError(`${context} is not of type 'URL'.`);
+ },
+
+ create(constructorArgs, privateData) {
+ let obj = Object.create(URL.prototype);
+ obj = this.setup(obj, constructorArgs, privateData);
+ return obj;
+ },
+ createImpl(constructorArgs, privateData) {
+ let obj = Object.create(URL.prototype);
+ obj = this.setup(obj, constructorArgs, privateData);
+ return utils.implForWrapper(obj);
+ },
+ _internalSetup(obj) {},
+ setup(obj, constructorArgs, privateData) {
+ if (!privateData) privateData = {};
+
+ privateData.wrapper = obj;
+
+ this._internalSetup(obj);
+ Object.defineProperty(obj, impl, {
+ value: new Impl.implementation(constructorArgs, privateData),
+ configurable: true
+ });
+
+ obj[impl][utils.wrapperSymbol] = obj;
+ if (Impl.init) {
+ Impl.init(obj[impl], privateData);
+ }
+ return obj;
+ },
+ interface: URL,
+ expose: {
+ Window: { URL },
+ Worker: { URL }
+ }
+}; // iface
+module.exports = iface;
+
+const Impl = require("./URL-impl.js");
+
+},{"./URL-impl.js":1,"./utils.js":9,"webidl-conversions":18}],3:[function(require,module,exports){
+"use strict";
+const stableSortBy = require("lodash.sortby");
+const urlencoded = require("./urlencoded");
+
+exports.implementation = class URLSearchParamsImpl {
+ constructor(constructorArgs, { doNotStripQMark = false }) {
+ let init = constructorArgs[0];
+ this._list = [];
+ this._url = null;
+
+ if (!doNotStripQMark && typeof init === "string" && init[0] === "?") {
+ init = init.slice(1);
+ }
+
+ if (Array.isArray(init)) {
+ for (const pair of init) {
+ if (pair.length !== 2) {
+ throw new TypeError("Failed to construct 'URLSearchParams': parameter 1 sequence's element does not " +
+ "contain exactly two elements.");
+ }
+ this._list.push([pair[0], pair[1]]);
+ }
+ } else if (typeof init === "object" && Object.getPrototypeOf(init) === null) {
+ for (const name of Object.keys(init)) {
+ const value = init[name];
+ this._list.push([name, value]);
+ }
+ } else {
+ this._list = urlencoded.parseUrlencoded(init);
+ }
+ }
+
+ _updateSteps() {
+ if (this._url !== null) {
+ let query = urlencoded.serializeUrlencoded(this._list);
+ if (query === "") {
+ query = null;
+ }
+ this._url._url.query = query;
+ }
+ }
+
+ append(name, value) {
+ this._list.push([name, value]);
+ this._updateSteps();
+ }
+
+ delete(name) {
+ let i = 0;
+ while (i < this._list.length) {
+ if (this._list[i][0] === name) {
+ this._list.splice(i, 1);
+ } else {
+ i++;
+ }
+ }
+ this._updateSteps();
+ }
+
+ get(name) {
+ for (const tuple of this._list) {
+ if (tuple[0] === name) {
+ return tuple[1];
+ }
+ }
+ return null;
+ }
+
+ getAll(name) {
+ const output = [];
+ for (const tuple of this._list) {
+ if (tuple[0] === name) {
+ output.push(tuple[1]);
+ }
+ }
+ return output;
+ }
+
+ has(name) {
+ for (const tuple of this._list) {
+ if (tuple[0] === name) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ set(name, value) {
+ let found = false;
+ let i = 0;
+ while (i < this._list.length) {
+ if (this._list[i][0] === name) {
+ if (found) {
+ this._list.splice(i, 1);
+ } else {
+ found = true;
+ this._list[i][1] = value;
+ i++;
+ }
+ } else {
+ i++;
+ }
+ }
+ if (!found) {
+ this._list.push([name, value]);
+ }
+ this._updateSteps();
+ }
+
+ sort() {
+ this._list = stableSortBy(this._list, [0]);
+ this._updateSteps();
+ }
+
+ [Symbol.iterator]() {
+ return this._list[Symbol.iterator]();
+ }
+
+ toString() {
+ return urlencoded.serializeUrlencoded(this._list);
+ }
+};
+
+},{"./urlencoded":8,"lodash.sortby":14}],4:[function(require,module,exports){
+"use strict";
+
+const conversions = require("webidl-conversions");
+const utils = require("./utils.js");
+
+const impl = utils.implSymbol;
+
+const IteratorPrototype = Object.create(utils.IteratorPrototype, {
+ next: {
+ value: function next() {
+ const internal = this[utils.iterInternalSymbol];
+ const { target, kind, index } = internal;
+ const values = Array.from(target[impl]);
+ const len = values.length;
+ if (index >= len) {
+ return { value: undefined, done: true };
+ }
+
+ const pair = values[index];
+ internal.index = index + 1;
+ const [key, value] = pair.map(utils.tryWrapperForImpl);
+
+ let result;
+ switch (kind) {
+ case "key":
+ result = key;
+ break;
+ case "value":
+ result = value;
+ break;
+ case "key+value":
+ result = [key, value];
+ break;
+ }
+ return { value: result, done: false };
+ },
+ writable: true,
+ enumerable: true,
+ configurable: true
+ },
+ [Symbol.toStringTag]: {
+ value: "URLSearchParams Iterator",
+ configurable: true
+ }
+});
+class URLSearchParams {
+ constructor() {
+ const args = [];
+ {
+ let curArg = arguments[0];
+ if (curArg !== undefined) {
+ if (utils.isObject(curArg)) {
+ if (curArg[Symbol.iterator] !== undefined) {
+ if (!utils.isObject(curArg)) {
+ throw new TypeError(
+ "Failed to construct 'URLSearchParams': parameter 1" + " sequence" + " is not an iterable object."
+ );
+ } else {
+ const V = [];
+ const tmp = curArg;
+ for (let nextItem of tmp) {
+ if (!utils.isObject(nextItem)) {
+ throw new TypeError(
+ "Failed to construct 'URLSearchParams': parameter 1" +
+ " sequence" +
+ "'s element" +
+ " is not an iterable object."
+ );
+ } else {
+ const V = [];
+ const tmp = nextItem;
+ for (let nextItem of tmp) {
+ nextItem = conversions["USVString"](nextItem, {
+ context:
+ "Failed to construct 'URLSearchParams': parameter 1" + " sequence" + "'s element" + "'s element"
+ });
+
+ V.push(nextItem);
+ }
+ nextItem = V;
+ }
+
+ V.push(nextItem);
+ }
+ curArg = V;
+ }
+ } else {
+ if (!utils.isObject(curArg)) {
+ throw new TypeError(
+ "Failed to construct 'URLSearchParams': parameter 1" + " record" + " is not an object."
+ );
+ } else {
+ const result = Object.create(null);
+ for (const key of Reflect.ownKeys(curArg)) {
+ const desc = Object.getOwnPropertyDescriptor(curArg, key);
+ if (desc && desc.enumerable) {
+ let typedKey = key;
+
+ typedKey = conversions["USVString"](typedKey, {
+ context: "Failed to construct 'URLSearchParams': parameter 1" + " record" + "'s key"
+ });
+
+ let typedValue = curArg[key];
+
+ typedValue = conversions["USVString"](typedValue, {
+ context: "Failed to construct 'URLSearchParams': parameter 1" + " record" + "'s value"
+ });
+
+ result[typedKey] = typedValue;
+ }
+ }
+ curArg = result;
+ }
+ }
+ } else {
+ curArg = conversions["USVString"](curArg, { context: "Failed to construct 'URLSearchParams': parameter 1" });
+ }
+ } else {
+ curArg = "";
+ }
+ args.push(curArg);
+ }
+ return iface.setup(Object.create(new.target.prototype), args);
+ }
+
+ append(name, value) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ if (arguments.length < 2) {
+ throw new TypeError(
+ "Failed to execute 'append' on 'URLSearchParams': 2 arguments required, but only " +
+ arguments.length +
+ " present."
+ );
+ }
+ const args = [];
+ {
+ let curArg = arguments[0];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'append' on 'URLSearchParams': parameter 1"
+ });
+ args.push(curArg);
+ }
+ {
+ let curArg = arguments[1];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'append' on 'URLSearchParams': parameter 2"
+ });
+ args.push(curArg);
+ }
+ return this[impl].append(...args);
+ }
+
+ delete(name) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ if (arguments.length < 1) {
+ throw new TypeError(
+ "Failed to execute 'delete' on 'URLSearchParams': 1 argument required, but only " +
+ arguments.length +
+ " present."
+ );
+ }
+ const args = [];
+ {
+ let curArg = arguments[0];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'delete' on 'URLSearchParams': parameter 1"
+ });
+ args.push(curArg);
+ }
+ return this[impl].delete(...args);
+ }
+
+ get(name) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ if (arguments.length < 1) {
+ throw new TypeError(
+ "Failed to execute 'get' on 'URLSearchParams': 1 argument required, but only " + arguments.length + " present."
+ );
+ }
+ const args = [];
+ {
+ let curArg = arguments[0];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'get' on 'URLSearchParams': parameter 1"
+ });
+ args.push(curArg);
+ }
+ return this[impl].get(...args);
+ }
+
+ getAll(name) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ if (arguments.length < 1) {
+ throw new TypeError(
+ "Failed to execute 'getAll' on 'URLSearchParams': 1 argument required, but only " +
+ arguments.length +
+ " present."
+ );
+ }
+ const args = [];
+ {
+ let curArg = arguments[0];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'getAll' on 'URLSearchParams': parameter 1"
+ });
+ args.push(curArg);
+ }
+ return utils.tryWrapperForImpl(this[impl].getAll(...args));
+ }
+
+ has(name) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ if (arguments.length < 1) {
+ throw new TypeError(
+ "Failed to execute 'has' on 'URLSearchParams': 1 argument required, but only " + arguments.length + " present."
+ );
+ }
+ const args = [];
+ {
+ let curArg = arguments[0];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'has' on 'URLSearchParams': parameter 1"
+ });
+ args.push(curArg);
+ }
+ return this[impl].has(...args);
+ }
+
+ set(name, value) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ if (arguments.length < 2) {
+ throw new TypeError(
+ "Failed to execute 'set' on 'URLSearchParams': 2 arguments required, but only " + arguments.length + " present."
+ );
+ }
+ const args = [];
+ {
+ let curArg = arguments[0];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'set' on 'URLSearchParams': parameter 1"
+ });
+ args.push(curArg);
+ }
+ {
+ let curArg = arguments[1];
+ curArg = conversions["USVString"](curArg, {
+ context: "Failed to execute 'set' on 'URLSearchParams': parameter 2"
+ });
+ args.push(curArg);
+ }
+ return this[impl].set(...args);
+ }
+
+ sort() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl].sort();
+ }
+
+ toString() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+
+ return this[impl].toString();
+ }
+
+ keys() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+ return module.exports.createDefaultIterator(this, "key");
+ }
+
+ values() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+ return module.exports.createDefaultIterator(this, "value");
+ }
+
+ entries() {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+ return module.exports.createDefaultIterator(this, "key+value");
+ }
+
+ forEach(callback) {
+ if (!this || !module.exports.is(this)) {
+ throw new TypeError("Illegal invocation");
+ }
+ if (arguments.length < 1) {
+ throw new TypeError("Failed to execute 'forEach' on 'iterable': 1 argument required, " + "but only 0 present.");
+ }
+ if (typeof callback !== "function") {
+ throw new TypeError(
+ "Failed to execute 'forEach' on 'iterable': The callback provided " + "as parameter 1 is not a function."
+ );
+ }
+ const thisArg = arguments[1];
+ let pairs = Array.from(this[impl]);
+ let i = 0;
+ while (i < pairs.length) {
+ const [key, value] = pairs[i].map(utils.tryWrapperForImpl);
+ callback.call(thisArg, value, key, this);
+ pairs = Array.from(this[impl]);
+ i++;
+ }
+ }
+}
+Object.defineProperties(URLSearchParams.prototype, {
+ append: { enumerable: true },
+ delete: { enumerable: true },
+ get: { enumerable: true },
+ getAll: { enumerable: true },
+ has: { enumerable: true },
+ set: { enumerable: true },
+ sort: { enumerable: true },
+ toString: { enumerable: true },
+ keys: { enumerable: true },
+ values: { enumerable: true },
+ entries: { enumerable: true },
+ forEach: { enumerable: true },
+ [Symbol.toStringTag]: { value: "URLSearchParams", configurable: true },
+ [Symbol.iterator]: { value: URLSearchParams.prototype.entries, configurable: true, writable: true }
+});
+const iface = {
+ // When an interface-module that implements this interface as a mixin is loaded, it will append its own `.is()`
+ // method into this array. It allows objects that directly implements *those* interfaces to be recognized as
+ // implementing this mixin interface.
+ _mixedIntoPredicates: [],
+ is(obj) {
+ if (obj) {
+ if (utils.hasOwn(obj, impl) && obj[impl] instanceof Impl.implementation) {
+ return true;
+ }
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
+ if (isMixedInto(obj)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ isImpl(obj) {
+ if (obj) {
+ if (obj instanceof Impl.implementation) {
+ return true;
+ }
+
+ const wrapper = utils.wrapperForImpl(obj);
+ for (const isMixedInto of module.exports._mixedIntoPredicates) {
+ if (isMixedInto(wrapper)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ convert(obj, { context = "The provided value" } = {}) {
+ if (module.exports.is(obj)) {
+ return utils.implForWrapper(obj);
+ }
+ throw new TypeError(`${context} is not of type 'URLSearchParams'.`);
+ },
+
+ createDefaultIterator(target, kind) {
+ const iterator = Object.create(IteratorPrototype);
+ Object.defineProperty(iterator, utils.iterInternalSymbol, {
+ value: { target, kind, index: 0 },
+ configurable: true
+ });
+ return iterator;
+ },
+
+ create(constructorArgs, privateData) {
+ let obj = Object.create(URLSearchParams.prototype);
+ obj = this.setup(obj, constructorArgs, privateData);
+ return obj;
+ },
+ createImpl(constructorArgs, privateData) {
+ let obj = Object.create(URLSearchParams.prototype);
+ obj = this.setup(obj, constructorArgs, privateData);
+ return utils.implForWrapper(obj);
+ },
+ _internalSetup(obj) {},
+ setup(obj, constructorArgs, privateData) {
+ if (!privateData) privateData = {};
+
+ privateData.wrapper = obj;
+
+ this._internalSetup(obj);
+ Object.defineProperty(obj, impl, {
+ value: new Impl.implementation(constructorArgs, privateData),
+ configurable: true
+ });
+
+ obj[impl][utils.wrapperSymbol] = obj;
+ if (Impl.init) {
+ Impl.init(obj[impl], privateData);
+ }
+ return obj;
+ },
+ interface: URLSearchParams,
+ expose: {
+ Window: { URLSearchParams },
+ Worker: { URLSearchParams }
+ }
+}; // iface
+module.exports = iface;
+
+const Impl = require("./URLSearchParams-impl.js");
+
+},{"./URLSearchParams-impl.js":3,"./utils.js":9,"webidl-conversions":18}],5:[function(require,module,exports){
+"use strict";
+
+function isASCIIDigit(c) {
+ return c >= 0x30 && c <= 0x39;
+}
+
+function isASCIIAlpha(c) {
+ return (c >= 0x41 && c <= 0x5A) || (c >= 0x61 && c <= 0x7A);
+}
+
+function isASCIIAlphanumeric(c) {
+ return isASCIIAlpha(c) || isASCIIDigit(c);
+}
+
+function isASCIIHex(c) {
+ return isASCIIDigit(c) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66);
+}
+
+module.exports = {
+ isASCIIDigit,
+ isASCIIAlpha,
+ isASCIIAlphanumeric,
+ isASCIIHex
+};
+
+},{}],6:[function(require,module,exports){
+"use strict";
+
+exports.URL = require("./URL").interface;
+exports.URLSearchParams = require("./URLSearchParams").interface;
+
+exports.parseURL = require("./url-state-machine").parseURL;
+exports.basicURLParse = require("./url-state-machine").basicURLParse;
+exports.serializeURL = require("./url-state-machine").serializeURL;
+exports.serializeHost = require("./url-state-machine").serializeHost;
+exports.serializeInteger = require("./url-state-machine").serializeInteger;
+exports.serializeURLOrigin = require("./url-state-machine").serializeURLOrigin;
+exports.setTheUsername = require("./url-state-machine").setTheUsername;
+exports.setThePassword = require("./url-state-machine").setThePassword;
+exports.cannotHaveAUsernamePasswordPort = require("./url-state-machine").cannotHaveAUsernamePasswordPort;
+
+exports.percentDecode = require("./urlencoded").percentDecode;
+
+},{"./URL":2,"./URLSearchParams":4,"./url-state-machine":7,"./urlencoded":8}],7:[function(require,module,exports){
+(function (Buffer){(function (){
+"use strict";
+const punycode = require("punycode");
+const tr46 = require("tr46");
+
+const infra = require("./infra");
+const { percentEncode, percentDecode } = require("./urlencoded");
+
+const specialSchemes = {
+ ftp: 21,
+ file: null,
+ gopher: 70,
+ http: 80,
+ https: 443,
+ ws: 80,
+ wss: 443
+};
+
+const failure = Symbol("failure");
+
+function countSymbols(str) {
+ return punycode.ucs2.decode(str).length;
+}
+
+function at(input, idx) {
+ const c = input[idx];
+ return isNaN(c) ? undefined : String.fromCodePoint(c);
+}
+
+function isSingleDot(buffer) {
+ return buffer === "." || buffer.toLowerCase() === "%2e";
+}
+
+function isDoubleDot(buffer) {
+ buffer = buffer.toLowerCase();
+ return buffer === ".." || buffer === "%2e." || buffer === ".%2e" || buffer === "%2e%2e";
+}
+
+function isWindowsDriveLetterCodePoints(cp1, cp2) {
+ return infra.isASCIIAlpha(cp1) && (cp2 === 58 || cp2 === 124);
+}
+
+function isWindowsDriveLetterString(string) {
+ return string.length === 2 && infra.isASCIIAlpha(string.codePointAt(0)) && (string[1] === ":" || string[1] === "|");
+}
+
+function isNormalizedWindowsDriveLetterString(string) {
+ return string.length === 2 && infra.isASCIIAlpha(string.codePointAt(0)) && string[1] === ":";
+}
+
+function containsForbiddenHostCodePoint(string) {
+ return string.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|%|\/|:|\?|@|\[|\\|\]/) !== -1;
+}
+
+function containsForbiddenHostCodePointExcludingPercent(string) {
+ return string.search(/\u0000|\u0009|\u000A|\u000D|\u0020|#|\/|:|\?|@|\[|\\|\]/) !== -1;
+}
+
+function isSpecialScheme(scheme) {
+ return specialSchemes[scheme] !== undefined;
+}
+
+function isSpecial(url) {
+ return isSpecialScheme(url.scheme);
+}
+
+function isNotSpecial(url) {
+ return !isSpecialScheme(url.scheme);
+}
+
+function defaultPort(scheme) {
+ return specialSchemes[scheme];
+}
+
+function utf8PercentEncode(c) {
+ const buf = Buffer.from(c);
+
+ let str = "";
+
+ for (let i = 0; i < buf.length; ++i) {
+ str += percentEncode(buf[i]);
+ }
+
+ return str;
+}
+
+function isC0ControlPercentEncode(c) {
+ return c <= 0x1F || c > 0x7E;
+}
+
+const extraUserinfoPercentEncodeSet =
+ new Set([47, 58, 59, 61, 64, 91, 92, 93, 94, 124]);
+function isUserinfoPercentEncode(c) {
+ return isPathPercentEncode(c) || extraUserinfoPercentEncodeSet.has(c);
+}
+
+const extraFragmentPercentEncodeSet = new Set([32, 34, 60, 62, 96]);
+function isFragmentPercentEncode(c) {
+ return isC0ControlPercentEncode(c) || extraFragmentPercentEncodeSet.has(c);
+}
+
+const extraPathPercentEncodeSet = new Set([35, 63, 123, 125]);
+function isPathPercentEncode(c) {
+ return isFragmentPercentEncode(c) || extraPathPercentEncodeSet.has(c);
+}
+
+function percentEncodeChar(c, encodeSetPredicate) {
+ const cStr = String.fromCodePoint(c);
+
+ if (encodeSetPredicate(c)) {
+ return utf8PercentEncode(cStr);
+ }
+
+ return cStr;
+}
+
+function parseIPv4Number(input) {
+ let R = 10;
+
+ if (input.length >= 2 && input.charAt(0) === "0" && input.charAt(1).toLowerCase() === "x") {
+ input = input.substring(2);
+ R = 16;
+ } else if (input.length >= 2 && input.charAt(0) === "0") {
+ input = input.substring(1);
+ R = 8;
+ }
+
+ if (input === "") {
+ return 0;
+ }
+
+ let regex = /[^0-7]/;
+ if (R === 10) {
+ regex = /[^0-9]/;
+ }
+ if (R === 16) {
+ regex = /[^0-9A-Fa-f]/;
+ }
+
+ if (regex.test(input)) {
+ return failure;
+ }
+
+ return parseInt(input, R);
+}
+
+function parseIPv4(input) {
+ const parts = input.split(".");
+ if (parts[parts.length - 1] === "") {
+ if (parts.length > 1) {
+ parts.pop();
+ }
+ }
+
+ if (parts.length > 4) {
+ return input;
+ }
+
+ const numbers = [];
+ for (const part of parts) {
+ if (part === "") {
+ return input;
+ }
+ const n = parseIPv4Number(part);
+ if (n === failure) {
+ return input;
+ }
+
+ numbers.push(n);
+ }
+
+ for (let i = 0; i < numbers.length - 1; ++i) {
+ if (numbers[i] > 255) {
+ return failure;
+ }
+ }
+ if (numbers[numbers.length - 1] >= Math.pow(256, 5 - numbers.length)) {
+ return failure;
+ }
+
+ let ipv4 = numbers.pop();
+ let counter = 0;
+
+ for (const n of numbers) {
+ ipv4 += n * Math.pow(256, 3 - counter);
+ ++counter;
+ }
+
+ return ipv4;
+}
+
+function serializeIPv4(address) {
+ let output = "";
+ let n = address;
+
+ for (let i = 1; i <= 4; ++i) {
+ output = String(n % 256) + output;
+ if (i !== 4) {
+ output = "." + output;
+ }
+ n = Math.floor(n / 256);
+ }
+
+ return output;
+}
+
+function parseIPv6(input) {
+ const address = [0, 0, 0, 0, 0, 0, 0, 0];
+ let pieceIndex = 0;
+ let compress = null;
+ let pointer = 0;
+
+ input = punycode.ucs2.decode(input);
+
+ if (input[pointer] === 58) {
+ if (input[pointer + 1] !== 58) {
+ return failure;
+ }
+
+ pointer += 2;
+ ++pieceIndex;
+ compress = pieceIndex;
+ }
+
+ while (pointer < input.length) {
+ if (pieceIndex === 8) {
+ return failure;
+ }
+
+ if (input[pointer] === 58) {
+ if (compress !== null) {
+ return failure;
+ }
+ ++pointer;
+ ++pieceIndex;
+ compress = pieceIndex;
+ continue;
+ }
+
+ let value = 0;
+ let length = 0;
+
+ while (length < 4 && infra.isASCIIHex(input[pointer])) {
+ value = value * 0x10 + parseInt(at(input, pointer), 16);
+ ++pointer;
+ ++length;
+ }
+
+ if (input[pointer] === 46) {
+ if (length === 0) {
+ return failure;
+ }
+
+ pointer -= length;
+
+ if (pieceIndex > 6) {
+ return failure;
+ }
+
+ let numbersSeen = 0;
+
+ while (input[pointer] !== undefined) {
+ let ipv4Piece = null;
+
+ if (numbersSeen > 0) {
+ if (input[pointer] === 46 && numbersSeen < 4) {
+ ++pointer;
+ } else {
+ return failure;
+ }
+ }
+
+ if (!infra.isASCIIDigit(input[pointer])) {
+ return failure;
+ }
+
+ while (infra.isASCIIDigit(input[pointer])) {
+ const number = parseInt(at(input, pointer));
+ if (ipv4Piece === null) {
+ ipv4Piece = number;
+ } else if (ipv4Piece === 0) {
+ return failure;
+ } else {
+ ipv4Piece = ipv4Piece * 10 + number;
+ }
+ if (ipv4Piece > 255) {
+ return failure;
+ }
+ ++pointer;
+ }
+
+ address[pieceIndex] = address[pieceIndex] * 0x100 + ipv4Piece;
+
+ ++numbersSeen;
+
+ if (numbersSeen === 2 || numbersSeen === 4) {
+ ++pieceIndex;
+ }
+ }
+
+ if (numbersSeen !== 4) {
+ return failure;
+ }
+
+ break;
+ } else if (input[pointer] === 58) {
+ ++pointer;
+ if (input[pointer] === undefined) {
+ return failure;
+ }
+ } else if (input[pointer] !== undefined) {
+ return failure;
+ }
+
+ address[pieceIndex] = value;
+ ++pieceIndex;
+ }
+
+ if (compress !== null) {
+ let swaps = pieceIndex - compress;
+ pieceIndex = 7;
+ while (pieceIndex !== 0 && swaps > 0) {
+ const temp = address[compress + swaps - 1];
+ address[compress + swaps - 1] = address[pieceIndex];
+ address[pieceIndex] = temp;
+ --pieceIndex;
+ --swaps;
+ }
+ } else if (compress === null && pieceIndex !== 8) {
+ return failure;
+ }
+
+ return address;
+}
+
+function serializeIPv6(address) {
+ let output = "";
+ const seqResult = findLongestZeroSequence(address);
+ const compress = seqResult.idx;
+ let ignore0 = false;
+
+ for (let pieceIndex = 0; pieceIndex <= 7; ++pieceIndex) {
+ if (ignore0 && address[pieceIndex] === 0) {
+ continue;
+ } else if (ignore0) {
+ ignore0 = false;
+ }
+
+ if (compress === pieceIndex) {
+ const separator = pieceIndex === 0 ? "::" : ":";
+ output += separator;
+ ignore0 = true;
+ continue;
+ }
+
+ output += address[pieceIndex].toString(16);
+
+ if (pieceIndex !== 7) {
+ output += ":";
+ }
+ }
+
+ return output;
+}
+
+function parseHost(input, isNotSpecialArg = false) {
+ if (input[0] === "[") {
+ if (input[input.length - 1] !== "]") {
+ return failure;
+ }
+
+ return parseIPv6(input.substring(1, input.length - 1));
+ }
+
+ if (isNotSpecialArg) {
+ return parseOpaqueHost(input);
+ }
+
+ const domain = percentDecode(Buffer.from(input)).toString();
+ const asciiDomain = domainToASCII(domain);
+ if (asciiDomain === failure) {
+ return failure;
+ }
+
+ if (containsForbiddenHostCodePoint(asciiDomain)) {
+ return failure;
+ }
+
+ const ipv4Host = parseIPv4(asciiDomain);
+ if (typeof ipv4Host === "number" || ipv4Host === failure) {
+ return ipv4Host;
+ }
+
+ return asciiDomain;
+}
+
+function parseOpaqueHost(input) {
+ if (containsForbiddenHostCodePointExcludingPercent(input)) {
+ return failure;
+ }
+
+ let output = "";
+ const decoded = punycode.ucs2.decode(input);
+ for (let i = 0; i < decoded.length; ++i) {
+ output += percentEncodeChar(decoded[i], isC0ControlPercentEncode);
+ }
+ return output;
+}
+
+function findLongestZeroSequence(arr) {
+ let maxIdx = null;
+ let maxLen = 1; // only find elements > 1
+ let currStart = null;
+ let currLen = 0;
+
+ for (let i = 0; i < arr.length; ++i) {
+ if (arr[i] !== 0) {
+ if (currLen > maxLen) {
+ maxIdx = currStart;
+ maxLen = currLen;
+ }
+
+ currStart = null;
+ currLen = 0;
+ } else {
+ if (currStart === null) {
+ currStart = i;
+ }
+ ++currLen;
+ }
+ }
+
+ // if trailing zeros
+ if (currLen > maxLen) {
+ maxIdx = currStart;
+ maxLen = currLen;
+ }
+
+ return {
+ idx: maxIdx,
+ len: maxLen
+ };
+}
+
+function serializeHost(host) {
+ if (typeof host === "number") {
+ return serializeIPv4(host);
+ }
+
+ // IPv6 serializer
+ if (host instanceof Array) {
+ return "[" + serializeIPv6(host) + "]";
+ }
+
+ return host;
+}
+
+function domainToASCII(domain, beStrict = false) {
+ const result = tr46.toASCII(domain, {
+ checkBidi: true,
+ checkHyphens: false,
+ checkJoiners: true,
+ useSTD3ASCIIRules: beStrict,
+ verifyDNSLength: beStrict
+ });
+ if (result === null) {
+ return failure;
+ }
+ return result;
+}
+
+function trimControlChars(url) {
+ return url.replace(/^[\u0000-\u001F\u0020]+|[\u0000-\u001F\u0020]+$/g, "");
+}
+
+function trimTabAndNewline(url) {
+ return url.replace(/\u0009|\u000A|\u000D/g, "");
+}
+
+function shortenPath(url) {
+ const { path } = url;
+ if (path.length === 0) {
+ return;
+ }
+ if (url.scheme === "file" && path.length === 1 && isNormalizedWindowsDriveLetter(path[0])) {
+ return;
+ }
+
+ path.pop();
+}
+
+function includesCredentials(url) {
+ return url.username !== "" || url.password !== "";
+}
+
+function cannotHaveAUsernamePasswordPort(url) {
+ return url.host === null || url.host === "" || url.cannotBeABaseURL || url.scheme === "file";
+}
+
+function isNormalizedWindowsDriveLetter(string) {
+ return /^[A-Za-z]:$/.test(string);
+}
+
+function URLStateMachine(input, base, encodingOverride, url, stateOverride) {
+ this.pointer = 0;
+ this.input = input;
+ this.base = base || null;
+ this.encodingOverride = encodingOverride || "utf-8";
+ this.stateOverride = stateOverride;
+ this.url = url;
+ this.failure = false;
+ this.parseError = false;
+
+ if (!this.url) {
+ this.url = {
+ scheme: "",
+ username: "",
+ password: "",
+ host: null,
+ port: null,
+ path: [],
+ query: null,
+ fragment: null,
+
+ cannotBeABaseURL: false
+ };
+
+ const res = trimControlChars(this.input);
+ if (res !== this.input) {
+ this.parseError = true;
+ }
+ this.input = res;
+ }
+
+ const res = trimTabAndNewline(this.input);
+ if (res !== this.input) {
+ this.parseError = true;
+ }
+ this.input = res;
+
+ this.state = stateOverride || "scheme start";
+
+ this.buffer = "";
+ this.atFlag = false;
+ this.arrFlag = false;
+ this.passwordTokenSeenFlag = false;
+
+ this.input = punycode.ucs2.decode(this.input);
+
+ for (; this.pointer <= this.input.length; ++this.pointer) {
+ const c = this.input[this.pointer];
+ const cStr = isNaN(c) ? undefined : String.fromCodePoint(c);
+
+ // exec state machine
+ const ret = this["parse " + this.state](c, cStr);
+ if (!ret) {
+ break; // terminate algorithm
+ } else if (ret === failure) {
+ this.failure = true;
+ break;
+ }
+ }
+}
+
+URLStateMachine.prototype["parse scheme start"] = function parseSchemeStart(c, cStr) {
+ if (infra.isASCIIAlpha(c)) {
+ this.buffer += cStr.toLowerCase();
+ this.state = "scheme";
+ } else if (!this.stateOverride) {
+ this.state = "no scheme";
+ --this.pointer;
+ } else {
+ this.parseError = true;
+ return failure;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse scheme"] = function parseScheme(c, cStr) {
+ if (infra.isASCIIAlphanumeric(c) || c === 43 || c === 45 || c === 46) {
+ this.buffer += cStr.toLowerCase();
+ } else if (c === 58) {
+ if (this.stateOverride) {
+ if (isSpecial(this.url) && !isSpecialScheme(this.buffer)) {
+ return false;
+ }
+
+ if (!isSpecial(this.url) && isSpecialScheme(this.buffer)) {
+ return false;
+ }
+
+ if ((includesCredentials(this.url) || this.url.port !== null) && this.buffer === "file") {
+ return false;
+ }
+
+ if (this.url.scheme === "file" && (this.url.host === "" || this.url.host === null)) {
+ return false;
+ }
+ }
+ this.url.scheme = this.buffer;
+ if (this.stateOverride) {
+ if (this.url.port === defaultPort(this.url.scheme)) {
+ this.url.port = null;
+ }
+ return false;
+ }
+ this.buffer = "";
+ if (this.url.scheme === "file") {
+ if (this.input[this.pointer + 1] !== 47 || this.input[this.pointer + 2] !== 47) {
+ this.parseError = true;
+ }
+ this.state = "file";
+ } else if (isSpecial(this.url) && this.base !== null && this.base.scheme === this.url.scheme) {
+ this.state = "special relative or authority";
+ } else if (isSpecial(this.url)) {
+ this.state = "special authority slashes";
+ } else if (this.input[this.pointer + 1] === 47) {
+ this.state = "path or authority";
+ ++this.pointer;
+ } else {
+ this.url.cannotBeABaseURL = true;
+ this.url.path.push("");
+ this.state = "cannot-be-a-base-URL path";
+ }
+ } else if (!this.stateOverride) {
+ this.buffer = "";
+ this.state = "no scheme";
+ this.pointer = -1;
+ } else {
+ this.parseError = true;
+ return failure;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse no scheme"] = function parseNoScheme(c) {
+ if (this.base === null || (this.base.cannotBeABaseURL && c !== 35)) {
+ return failure;
+ } else if (this.base.cannotBeABaseURL && c === 35) {
+ this.url.scheme = this.base.scheme;
+ this.url.path = this.base.path.slice();
+ this.url.query = this.base.query;
+ this.url.fragment = "";
+ this.url.cannotBeABaseURL = true;
+ this.state = "fragment";
+ } else if (this.base.scheme === "file") {
+ this.state = "file";
+ --this.pointer;
+ } else {
+ this.state = "relative";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse special relative or authority"] = function parseSpecialRelativeOrAuthority(c) {
+ if (c === 47 && this.input[this.pointer + 1] === 47) {
+ this.state = "special authority ignore slashes";
+ ++this.pointer;
+ } else {
+ this.parseError = true;
+ this.state = "relative";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse path or authority"] = function parsePathOrAuthority(c) {
+ if (c === 47) {
+ this.state = "authority";
+ } else {
+ this.state = "path";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse relative"] = function parseRelative(c) {
+ this.url.scheme = this.base.scheme;
+ if (isNaN(c)) {
+ this.url.username = this.base.username;
+ this.url.password = this.base.password;
+ this.url.host = this.base.host;
+ this.url.port = this.base.port;
+ this.url.path = this.base.path.slice();
+ this.url.query = this.base.query;
+ } else if (c === 47) {
+ this.state = "relative slash";
+ } else if (c === 63) {
+ this.url.username = this.base.username;
+ this.url.password = this.base.password;
+ this.url.host = this.base.host;
+ this.url.port = this.base.port;
+ this.url.path = this.base.path.slice();
+ this.url.query = "";
+ this.state = "query";
+ } else if (c === 35) {
+ this.url.username = this.base.username;
+ this.url.password = this.base.password;
+ this.url.host = this.base.host;
+ this.url.port = this.base.port;
+ this.url.path = this.base.path.slice();
+ this.url.query = this.base.query;
+ this.url.fragment = "";
+ this.state = "fragment";
+ } else if (isSpecial(this.url) && c === 92) {
+ this.parseError = true;
+ this.state = "relative slash";
+ } else {
+ this.url.username = this.base.username;
+ this.url.password = this.base.password;
+ this.url.host = this.base.host;
+ this.url.port = this.base.port;
+ this.url.path = this.base.path.slice(0, this.base.path.length - 1);
+
+ this.state = "path";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse relative slash"] = function parseRelativeSlash(c) {
+ if (isSpecial(this.url) && (c === 47 || c === 92)) {
+ if (c === 92) {
+ this.parseError = true;
+ }
+ this.state = "special authority ignore slashes";
+ } else if (c === 47) {
+ this.state = "authority";
+ } else {
+ this.url.username = this.base.username;
+ this.url.password = this.base.password;
+ this.url.host = this.base.host;
+ this.url.port = this.base.port;
+ this.state = "path";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse special authority slashes"] = function parseSpecialAuthoritySlashes(c) {
+ if (c === 47 && this.input[this.pointer + 1] === 47) {
+ this.state = "special authority ignore slashes";
+ ++this.pointer;
+ } else {
+ this.parseError = true;
+ this.state = "special authority ignore slashes";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse special authority ignore slashes"] = function parseSpecialAuthorityIgnoreSlashes(c) {
+ if (c !== 47 && c !== 92) {
+ this.state = "authority";
+ --this.pointer;
+ } else {
+ this.parseError = true;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse authority"] = function parseAuthority(c, cStr) {
+ if (c === 64) {
+ this.parseError = true;
+ if (this.atFlag) {
+ this.buffer = "%40" + this.buffer;
+ }
+ this.atFlag = true;
+
+ // careful, this is based on buffer and has its own pointer (this.pointer != pointer) and inner chars
+ const len = countSymbols(this.buffer);
+ for (let pointer = 0; pointer < len; ++pointer) {
+ const codePoint = this.buffer.codePointAt(pointer);
+
+ if (codePoint === 58 && !this.passwordTokenSeenFlag) {
+ this.passwordTokenSeenFlag = true;
+ continue;
+ }
+ const encodedCodePoints = percentEncodeChar(codePoint, isUserinfoPercentEncode);
+ if (this.passwordTokenSeenFlag) {
+ this.url.password += encodedCodePoints;
+ } else {
+ this.url.username += encodedCodePoints;
+ }
+ }
+ this.buffer = "";
+ } else if (isNaN(c) || c === 47 || c === 63 || c === 35 ||
+ (isSpecial(this.url) && c === 92)) {
+ if (this.atFlag && this.buffer === "") {
+ this.parseError = true;
+ return failure;
+ }
+ this.pointer -= countSymbols(this.buffer) + 1;
+ this.buffer = "";
+ this.state = "host";
+ } else {
+ this.buffer += cStr;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse hostname"] =
+URLStateMachine.prototype["parse host"] = function parseHostName(c, cStr) {
+ if (this.stateOverride && this.url.scheme === "file") {
+ --this.pointer;
+ this.state = "file host";
+ } else if (c === 58 && !this.arrFlag) {
+ if (this.buffer === "") {
+ this.parseError = true;
+ return failure;
+ }
+
+ const host = parseHost(this.buffer, isNotSpecial(this.url));
+ if (host === failure) {
+ return failure;
+ }
+
+ this.url.host = host;
+ this.buffer = "";
+ this.state = "port";
+ if (this.stateOverride === "hostname") {
+ return false;
+ }
+ } else if (isNaN(c) || c === 47 || c === 63 || c === 35 ||
+ (isSpecial(this.url) && c === 92)) {
+ --this.pointer;
+ if (isSpecial(this.url) && this.buffer === "") {
+ this.parseError = true;
+ return failure;
+ } else if (this.stateOverride && this.buffer === "" &&
+ (includesCredentials(this.url) || this.url.port !== null)) {
+ this.parseError = true;
+ return false;
+ }
+
+ const host = parseHost(this.buffer, isNotSpecial(this.url));
+ if (host === failure) {
+ return failure;
+ }
+
+ this.url.host = host;
+ this.buffer = "";
+ this.state = "path start";
+ if (this.stateOverride) {
+ return false;
+ }
+ } else {
+ if (c === 91) {
+ this.arrFlag = true;
+ } else if (c === 93) {
+ this.arrFlag = false;
+ }
+ this.buffer += cStr;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse port"] = function parsePort(c, cStr) {
+ if (infra.isASCIIDigit(c)) {
+ this.buffer += cStr;
+ } else if (isNaN(c) || c === 47 || c === 63 || c === 35 ||
+ (isSpecial(this.url) && c === 92) ||
+ this.stateOverride) {
+ if (this.buffer !== "") {
+ const port = parseInt(this.buffer);
+ if (port > Math.pow(2, 16) - 1) {
+ this.parseError = true;
+ return failure;
+ }
+ this.url.port = port === defaultPort(this.url.scheme) ? null : port;
+ this.buffer = "";
+ }
+ if (this.stateOverride) {
+ return false;
+ }
+ this.state = "path start";
+ --this.pointer;
+ } else {
+ this.parseError = true;
+ return failure;
+ }
+
+ return true;
+};
+
+const fileOtherwiseCodePoints = new Set([47, 92, 63, 35]);
+
+function startsWithWindowsDriveLetter(input, pointer) {
+ const length = input.length - pointer;
+ return length >= 2 &&
+ isWindowsDriveLetterCodePoints(input[pointer], input[pointer + 1]) &&
+ (length === 2 || fileOtherwiseCodePoints.has(input[pointer + 2]));
+}
+
+URLStateMachine.prototype["parse file"] = function parseFile(c) {
+ this.url.scheme = "file";
+
+ if (c === 47 || c === 92) {
+ if (c === 92) {
+ this.parseError = true;
+ }
+ this.state = "file slash";
+ } else if (this.base !== null && this.base.scheme === "file") {
+ if (isNaN(c)) {
+ this.url.host = this.base.host;
+ this.url.path = this.base.path.slice();
+ this.url.query = this.base.query;
+ } else if (c === 63) {
+ this.url.host = this.base.host;
+ this.url.path = this.base.path.slice();
+ this.url.query = "";
+ this.state = "query";
+ } else if (c === 35) {
+ this.url.host = this.base.host;
+ this.url.path = this.base.path.slice();
+ this.url.query = this.base.query;
+ this.url.fragment = "";
+ this.state = "fragment";
+ } else {
+ if (!startsWithWindowsDriveLetter(this.input, this.pointer)) {
+ this.url.host = this.base.host;
+ this.url.path = this.base.path.slice();
+ shortenPath(this.url);
+ } else {
+ this.parseError = true;
+ }
+
+ this.state = "path";
+ --this.pointer;
+ }
+ } else {
+ this.state = "path";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse file slash"] = function parseFileSlash(c) {
+ if (c === 47 || c === 92) {
+ if (c === 92) {
+ this.parseError = true;
+ }
+ this.state = "file host";
+ } else {
+ if (this.base !== null && this.base.scheme === "file" &&
+ !startsWithWindowsDriveLetter(this.input, this.pointer)) {
+ if (isNormalizedWindowsDriveLetterString(this.base.path[0])) {
+ this.url.path.push(this.base.path[0]);
+ } else {
+ this.url.host = this.base.host;
+ }
+ }
+ this.state = "path";
+ --this.pointer;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse file host"] = function parseFileHost(c, cStr) {
+ if (isNaN(c) || c === 47 || c === 92 || c === 63 || c === 35) {
+ --this.pointer;
+ if (!this.stateOverride && isWindowsDriveLetterString(this.buffer)) {
+ this.parseError = true;
+ this.state = "path";
+ } else if (this.buffer === "") {
+ this.url.host = "";
+ if (this.stateOverride) {
+ return false;
+ }
+ this.state = "path start";
+ } else {
+ let host = parseHost(this.buffer, isNotSpecial(this.url));
+ if (host === failure) {
+ return failure;
+ }
+ if (host === "localhost") {
+ host = "";
+ }
+ this.url.host = host;
+
+ if (this.stateOverride) {
+ return false;
+ }
+
+ this.buffer = "";
+ this.state = "path start";
+ }
+ } else {
+ this.buffer += cStr;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse path start"] = function parsePathStart(c) {
+ if (isSpecial(this.url)) {
+ if (c === 92) {
+ this.parseError = true;
+ }
+ this.state = "path";
+
+ if (c !== 47 && c !== 92) {
+ --this.pointer;
+ }
+ } else if (!this.stateOverride && c === 63) {
+ this.url.query = "";
+ this.state = "query";
+ } else if (!this.stateOverride && c === 35) {
+ this.url.fragment = "";
+ this.state = "fragment";
+ } else if (c !== undefined) {
+ this.state = "path";
+ if (c !== 47) {
+ --this.pointer;
+ }
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse path"] = function parsePath(c) {
+ if (isNaN(c) || c === 47 || (isSpecial(this.url) && c === 92) ||
+ (!this.stateOverride && (c === 63 || c === 35))) {
+ if (isSpecial(this.url) && c === 92) {
+ this.parseError = true;
+ }
+
+ if (isDoubleDot(this.buffer)) {
+ shortenPath(this.url);
+ if (c !== 47 && !(isSpecial(this.url) && c === 92)) {
+ this.url.path.push("");
+ }
+ } else if (isSingleDot(this.buffer) && c !== 47 &&
+ !(isSpecial(this.url) && c === 92)) {
+ this.url.path.push("");
+ } else if (!isSingleDot(this.buffer)) {
+ if (this.url.scheme === "file" && this.url.path.length === 0 && isWindowsDriveLetterString(this.buffer)) {
+ if (this.url.host !== "" && this.url.host !== null) {
+ this.parseError = true;
+ this.url.host = "";
+ }
+ this.buffer = this.buffer[0] + ":";
+ }
+ this.url.path.push(this.buffer);
+ }
+ this.buffer = "";
+ if (this.url.scheme === "file" && (c === undefined || c === 63 || c === 35)) {
+ while (this.url.path.length > 1 && this.url.path[0] === "") {
+ this.parseError = true;
+ this.url.path.shift();
+ }
+ }
+ if (c === 63) {
+ this.url.query = "";
+ this.state = "query";
+ }
+ if (c === 35) {
+ this.url.fragment = "";
+ this.state = "fragment";
+ }
+ } else {
+ // TODO: If c is not a URL code point and not "%", parse error.
+
+ if (c === 37 &&
+ (!infra.isASCIIHex(this.input[this.pointer + 1]) ||
+ !infra.isASCIIHex(this.input[this.pointer + 2]))) {
+ this.parseError = true;
+ }
+
+ this.buffer += percentEncodeChar(c, isPathPercentEncode);
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse cannot-be-a-base-URL path"] = function parseCannotBeABaseURLPath(c) {
+ if (c === 63) {
+ this.url.query = "";
+ this.state = "query";
+ } else if (c === 35) {
+ this.url.fragment = "";
+ this.state = "fragment";
+ } else {
+ // TODO: Add: not a URL code point
+ if (!isNaN(c) && c !== 37) {
+ this.parseError = true;
+ }
+
+ if (c === 37 &&
+ (!infra.isASCIIHex(this.input[this.pointer + 1]) ||
+ !infra.isASCIIHex(this.input[this.pointer + 2]))) {
+ this.parseError = true;
+ }
+
+ if (!isNaN(c)) {
+ this.url.path[0] = this.url.path[0] + percentEncodeChar(c, isC0ControlPercentEncode);
+ }
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse query"] = function parseQuery(c, cStr) {
+ if (isNaN(c) || (!this.stateOverride && c === 35)) {
+ if (!isSpecial(this.url) || this.url.scheme === "ws" || this.url.scheme === "wss") {
+ this.encodingOverride = "utf-8";
+ }
+
+ const buffer = Buffer.from(this.buffer); // TODO: Use encoding override instead
+ for (let i = 0; i < buffer.length; ++i) {
+ if (buffer[i] < 0x21 ||
+ buffer[i] > 0x7E ||
+ buffer[i] === 0x22 || buffer[i] === 0x23 || buffer[i] === 0x3C || buffer[i] === 0x3E ||
+ (buffer[i] === 0x27 && isSpecial(this.url))) {
+ this.url.query += percentEncode(buffer[i]);
+ } else {
+ this.url.query += String.fromCodePoint(buffer[i]);
+ }
+ }
+
+ this.buffer = "";
+ if (c === 35) {
+ this.url.fragment = "";
+ this.state = "fragment";
+ }
+ } else {
+ // TODO: If c is not a URL code point and not "%", parse error.
+ if (c === 37 &&
+ (!infra.isASCIIHex(this.input[this.pointer + 1]) ||
+ !infra.isASCIIHex(this.input[this.pointer + 2]))) {
+ this.parseError = true;
+ }
+
+ this.buffer += cStr;
+ }
+
+ return true;
+};
+
+URLStateMachine.prototype["parse fragment"] = function parseFragment(c) {
+ if (isNaN(c)) { // do nothing
+ } else if (c === 0x0) {
+ this.parseError = true;
+ } else {
+ // TODO: If c is not a URL code point and not "%", parse error.
+ if (c === 37 &&
+ (!infra.isASCIIHex(this.input[this.pointer + 1]) ||
+ !infra.isASCIIHex(this.input[this.pointer + 2]))) {
+ this.parseError = true;
+ }
+
+ this.url.fragment += percentEncodeChar(c, isFragmentPercentEncode);
+ }
+
+ return true;
+};
+
+function serializeURL(url, excludeFragment) {
+ let output = url.scheme + ":";
+ if (url.host !== null) {
+ output += "//";
+
+ if (url.username !== "" || url.password !== "") {
+ output += url.username;
+ if (url.password !== "") {
+ output += ":" + url.password;
+ }
+ output += "@";
+ }
+
+ output += serializeHost(url.host);
+
+ if (url.port !== null) {
+ output += ":" + url.port;
+ }
+ } else if (url.host === null && url.scheme === "file") {
+ output += "//";
+ }
+
+ if (url.cannotBeABaseURL) {
+ output += url.path[0];
+ } else {
+ for (const string of url.path) {
+ output += "/" + string;
+ }
+ }
+
+ if (url.query !== null) {
+ output += "?" + url.query;
+ }
+
+ if (!excludeFragment && url.fragment !== null) {
+ output += "#" + url.fragment;
+ }
+
+ return output;
+}
+
+function serializeOrigin(tuple) {
+ let result = tuple.scheme + "://";
+ result += serializeHost(tuple.host);
+
+ if (tuple.port !== null) {
+ result += ":" + tuple.port;
+ }
+
+ return result;
+}
+
+module.exports.serializeURL = serializeURL;
+
+module.exports.serializeURLOrigin = function (url) {
+ // https://url.spec.whatwg.org/#concept-url-origin
+ switch (url.scheme) {
+ case "blob":
+ try {
+ return module.exports.serializeURLOrigin(module.exports.parseURL(url.path[0]));
+ } catch (e) {
+ // serializing an opaque origin returns "null"
+ return "null";
+ }
+ case "ftp":
+ case "gopher":
+ case "http":
+ case "https":
+ case "ws":
+ case "wss":
+ return serializeOrigin({
+ scheme: url.scheme,
+ host: url.host,
+ port: url.port
+ });
+ case "file":
+ // The spec says:
+ // > Unfortunate as it is, this is left as an exercise to the reader. When in doubt, return a new opaque origin.
+ // Browsers tested so far:
+ // - Chrome says "file://", but treats file: URLs as cross-origin for most (all?) purposes; see e.g.
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=37586
+ // - Firefox says "null", but treats file: URLs as same-origin sometimes based on directory stuff; see
+ // https://developer.mozilla.org/en-US/docs/Archive/Misc_top_level/Same-origin_policy_for_file:_URIs
+ return "null";
+ default:
+ // serializing an opaque origin returns "null"
+ return "null";
+ }
+};
+
+module.exports.basicURLParse = function (input, options) {
+ if (options === undefined) {
+ options = {};
+ }
+
+ const usm = new URLStateMachine(input, options.baseURL, options.encodingOverride, options.url, options.stateOverride);
+ if (usm.failure) {
+ return null;
+ }
+
+ return usm.url;
+};
+
+module.exports.setTheUsername = function (url, username) {
+ url.username = "";
+ const decoded = punycode.ucs2.decode(username);
+ for (let i = 0; i < decoded.length; ++i) {
+ url.username += percentEncodeChar(decoded[i], isUserinfoPercentEncode);
+ }
+};
+
+module.exports.setThePassword = function (url, password) {
+ url.password = "";
+ const decoded = punycode.ucs2.decode(password);
+ for (let i = 0; i < decoded.length; ++i) {
+ url.password += percentEncodeChar(decoded[i], isUserinfoPercentEncode);
+ }
+};
+
+module.exports.serializeHost = serializeHost;
+
+module.exports.cannotHaveAUsernamePasswordPort = cannotHaveAUsernamePasswordPort;
+
+module.exports.serializeInteger = function (integer) {
+ return String(integer);
+};
+
+module.exports.parseURL = function (input, options) {
+ if (options === undefined) {
+ options = {};
+ }
+
+ // We don't handle blobs, so this just delegates:
+ return module.exports.basicURLParse(input, { baseURL: options.baseURL, encodingOverride: options.encodingOverride });
+};
+
+}).call(this)}).call(this,require("buffer").Buffer)
+},{"./infra":5,"./urlencoded":8,"buffer":12,"punycode":11,"tr46":15}],8:[function(require,module,exports){
+(function (Buffer){(function (){
+"use strict";
+const { isASCIIHex } = require("./infra");
+
+function strictlySplitByteSequence(buf, cp) {
+ const list = [];
+ let last = 0;
+ let i = buf.indexOf(cp);
+ while (i >= 0) {
+ list.push(buf.slice(last, i));
+ last = i + 1;
+ i = buf.indexOf(cp, last);
+ }
+ if (last !== buf.length) {
+ list.push(buf.slice(last));
+ }
+ return list;
+}
+
+function replaceByteInByteSequence(buf, from, to) {
+ let i = buf.indexOf(from);
+ while (i >= 0) {
+ buf[i] = to;
+ i = buf.indexOf(from, i + 1);
+ }
+ return buf;
+}
+
+function percentEncode(c) {
+ let hex = c.toString(16).toUpperCase();
+ if (hex.length === 1) {
+ hex = "0" + hex;
+ }
+
+ return "%" + hex;
+}
+
+function percentDecode(input) {
+ const output = Buffer.alloc(input.byteLength);
+ let ptr = 0;
+ for (let i = 0; i < input.length; ++i) {
+ if (input[i] !== 37 || !isASCIIHex(input[i + 1]) || !isASCIIHex(input[i + 2])) {
+ output[ptr++] = input[i];
+ } else {
+ output[ptr++] = parseInt(input.slice(i + 1, i + 3).toString(), 16);
+ i += 2;
+ }
+ }
+ return output.slice(0, ptr);
+}
+
+function parseUrlencoded(input) {
+ const sequences = strictlySplitByteSequence(input, 38);
+ const output = [];
+ for (const bytes of sequences) {
+ if (bytes.length === 0) {
+ continue;
+ }
+
+ let name;
+ let value;
+ const indexOfEqual = bytes.indexOf(61);
+
+ if (indexOfEqual >= 0) {
+ name = bytes.slice(0, indexOfEqual);
+ value = bytes.slice(indexOfEqual + 1);
+ } else {
+ name = bytes;
+ value = Buffer.alloc(0);
+ }
+
+ name = replaceByteInByteSequence(Buffer.from(name), 43, 32);
+ value = replaceByteInByteSequence(Buffer.from(value), 43, 32);
+
+ output.push([percentDecode(name).toString(), percentDecode(value).toString()]);
+ }
+ return output;
+}
+
+function serializeUrlencodedByte(input) {
+ let output = "";
+ for (const byte of input) {
+ if (byte === 32) {
+ output += "+";
+ } else if (byte === 42 ||
+ byte === 45 ||
+ byte === 46 ||
+ (byte >= 48 && byte <= 57) ||
+ (byte >= 65 && byte <= 90) ||
+ byte === 95 ||
+ (byte >= 97 && byte <= 122)) {
+ output += String.fromCodePoint(byte);
+ } else {
+ output += percentEncode(byte);
+ }
+ }
+ return output;
+}
+
+function serializeUrlencoded(tuples, encodingOverride = undefined) {
+ let encoding = "utf-8";
+ if (encodingOverride !== undefined) {
+ encoding = encodingOverride;
+ }
+
+ let output = "";
+ for (const [i, tuple] of tuples.entries()) {
+ // TODO: handle encoding override
+ const name = serializeUrlencodedByte(Buffer.from(tuple[0]));
+ let value = tuple[1];
+ if (tuple.length > 2 && tuple[2] !== undefined) {
+ if (tuple[2] === "hidden" && name === "_charset_") {
+ value = encoding;
+ } else if (tuple[2] === "file") {
+ // value is a File object
+ value = value.name;
+ }
+ }
+ value = serializeUrlencodedByte(Buffer.from(value));
+ if (i !== 0) {
+ output += "&";
+ }
+ output += `${name}=${value}`;
+ }
+ return output;
+}
+
+module.exports = {
+ percentEncode,
+ percentDecode,
+
+ // application/x-www-form-urlencoded string parser
+ parseUrlencoded(input) {
+ return parseUrlencoded(Buffer.from(input));
+ },
+
+ // application/x-www-form-urlencoded serializer
+ serializeUrlencoded
+};
+
+}).call(this)}).call(this,require("buffer").Buffer)
+},{"./infra":5,"buffer":12}],9:[function(require,module,exports){
+"use strict";
+
+// Returns "Type(value) is Object" in ES terminology.
+function isObject(value) {
+ return typeof value === "object" && value !== null || typeof value === "function";
+}
+
+function hasOwn(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+const getOwnPropertyDescriptors = typeof Object.getOwnPropertyDescriptors === "function" ?
+ Object.getOwnPropertyDescriptors :
+ // Polyfill exists until we require Node.js v8.x
+ // https://tc39.github.io/ecma262/#sec-object.getownpropertydescriptors
+ obj => {
+ if (obj === undefined || obj === null) {
+ throw new TypeError("Cannot convert undefined or null to object");
+ }
+ obj = Object(obj);
+ const ownKeys = Reflect.ownKeys(obj);
+ const descriptors = {};
+ for (const key of ownKeys) {
+ const descriptor = Reflect.getOwnPropertyDescriptor(obj, key);
+ if (descriptor !== undefined) {
+ Reflect.defineProperty(descriptors, key, {
+ value: descriptor,
+ writable: true,
+ enumerable: true,
+ configurable: true
+ });
+ }
+ }
+ return descriptors;
+ };
+
+const wrapperSymbol = Symbol("wrapper");
+const implSymbol = Symbol("impl");
+const sameObjectCaches = Symbol("SameObject caches");
+
+function getSameObject(wrapper, prop, creator) {
+ if (!wrapper[sameObjectCaches]) {
+ wrapper[sameObjectCaches] = Object.create(null);
+ }
+
+ if (prop in wrapper[sameObjectCaches]) {
+ return wrapper[sameObjectCaches][prop];
+ }
+
+ wrapper[sameObjectCaches][prop] = creator();
+ return wrapper[sameObjectCaches][prop];
+}
+
+function wrapperForImpl(impl) {
+ return impl ? impl[wrapperSymbol] : null;
+}
+
+function implForWrapper(wrapper) {
+ return wrapper ? wrapper[implSymbol] : null;
+}
+
+function tryWrapperForImpl(impl) {
+ const wrapper = wrapperForImpl(impl);
+ return wrapper ? wrapper : impl;
+}
+
+function tryImplForWrapper(wrapper) {
+ const impl = implForWrapper(wrapper);
+ return impl ? impl : wrapper;
+}
+
+const iterInternalSymbol = Symbol("internal");
+const IteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
+
+function isArrayIndexPropName(P) {
+ if (typeof P !== "string") {
+ return false;
+ }
+ const i = P >>> 0;
+ if (i === Math.pow(2, 32) - 1) {
+ return false;
+ }
+ const s = `${i}`;
+ if (P !== s) {
+ return false;
+ }
+ return true;
+}
+
+const supportsPropertyIndex = Symbol("supports property index");
+const supportedPropertyIndices = Symbol("supported property indices");
+const supportsPropertyName = Symbol("supports property name");
+const supportedPropertyNames = Symbol("supported property names");
+const indexedGet = Symbol("indexed property get");
+const indexedSetNew = Symbol("indexed property set new");
+const indexedSetExisting = Symbol("indexed property set existing");
+const namedGet = Symbol("named property get");
+const namedSetNew = Symbol("named property set new");
+const namedSetExisting = Symbol("named property set existing");
+const namedDelete = Symbol("named property delete");
+
+module.exports = exports = {
+ isObject,
+ hasOwn,
+ getOwnPropertyDescriptors,
+ wrapperSymbol,
+ implSymbol,
+ getSameObject,
+ wrapperForImpl,
+ implForWrapper,
+ tryWrapperForImpl,
+ tryImplForWrapper,
+ iterInternalSymbol,
+ IteratorPrototype,
+ isArrayIndexPropName,
+ supportsPropertyIndex,
+ supportedPropertyIndices,
+ supportsPropertyName,
+ supportedPropertyNames,
+ indexedGet,
+ indexedSetNew,
+ indexedSetExisting,
+ namedGet,
+ namedSetNew,
+ namedSetExisting,
+ namedDelete
+};
+
+},{}],10:[function(require,module,exports){
+'use strict'
+
+exports.byteLength = byteLength
+exports.toByteArray = toByteArray
+exports.fromByteArray = fromByteArray
+
+var lookup = []
+var revLookup = []
+var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
+
+var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+for (var i = 0, len = code.length; i < len; ++i) {
+ lookup[i] = code[i]
+ revLookup[code.charCodeAt(i)] = i
+}
+
+// Support decoding URL-safe base64 strings, as Node.js does.
+// See: https://en.wikipedia.org/wiki/Base64#URL_applications
+revLookup['-'.charCodeAt(0)] = 62
+revLookup['_'.charCodeAt(0)] = 63
+
+function getLens (b64) {
+ var len = b64.length
+
+ if (len % 4 > 0) {
+ throw new Error('Invalid string. Length must be a multiple of 4')
+ }
+
+ // Trim off extra bytes after placeholder bytes are found
+ // See: https://github.com/beatgammit/base64-js/issues/42
+ var validLen = b64.indexOf('=')
+ if (validLen === -1) validLen = len
+
+ var placeHoldersLen = validLen === len
+ ? 0
+ : 4 - (validLen % 4)
+
+ return [validLen, placeHoldersLen]
+}
+
+// base64 is 4/3 + up to two characters of the original data
+function byteLength (b64) {
+ var lens = getLens(b64)
+ var validLen = lens[0]
+ var placeHoldersLen = lens[1]
+ return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
+}
+
+function _byteLength (b64, validLen, placeHoldersLen) {
+ return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
+}
+
+function toByteArray (b64) {
+ var tmp
+ var lens = getLens(b64)
+ var validLen = lens[0]
+ var placeHoldersLen = lens[1]
+
+ var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
+
+ var curByte = 0
+
+ // if there are placeholders, only get up to the last complete 4 chars
+ var len = placeHoldersLen > 0
+ ? validLen - 4
+ : validLen
+
+ var i
+ for (i = 0; i < len; i += 4) {
+ tmp =
+ (revLookup[b64.charCodeAt(i)] << 18) |
+ (revLookup[b64.charCodeAt(i + 1)] << 12) |
+ (revLookup[b64.charCodeAt(i + 2)] << 6) |
+ revLookup[b64.charCodeAt(i + 3)]
+ arr[curByte++] = (tmp >> 16) & 0xFF
+ arr[curByte++] = (tmp >> 8) & 0xFF
+ arr[curByte++] = tmp & 0xFF
+ }
+
+ if (placeHoldersLen === 2) {
+ tmp =
+ (revLookup[b64.charCodeAt(i)] << 2) |
+ (revLookup[b64.charCodeAt(i + 1)] >> 4)
+ arr[curByte++] = tmp & 0xFF
+ }
+
+ if (placeHoldersLen === 1) {
+ tmp =
+ (revLookup[b64.charCodeAt(i)] << 10) |
+ (revLookup[b64.charCodeAt(i + 1)] << 4) |
+ (revLookup[b64.charCodeAt(i + 2)] >> 2)
+ arr[curByte++] = (tmp >> 8) & 0xFF
+ arr[curByte++] = tmp & 0xFF
+ }
+
+ return arr
+}
+
+function tripletToBase64 (num) {
+ return lookup[num >> 18 & 0x3F] +
+ lookup[num >> 12 & 0x3F] +
+ lookup[num >> 6 & 0x3F] +
+ lookup[num & 0x3F]
+}
+
+function encodeChunk (uint8, start, end) {
+ var tmp
+ var output = []
+ for (var i = start; i < end; i += 3) {
+ tmp =
+ ((uint8[i] << 16) & 0xFF0000) +
+ ((uint8[i + 1] << 8) & 0xFF00) +
+ (uint8[i + 2] & 0xFF)
+ output.push(tripletToBase64(tmp))
+ }
+ return output.join('')
+}
+
+function fromByteArray (uint8) {
+ var tmp
+ var len = uint8.length
+ var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
+ var parts = []
+ var maxChunkLength = 16383 // must be multiple of 3
+
+ // go through the array every three bytes, we'll deal with trailing stuff later
+ for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
+ parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
+ }
+
+ // pad the end with zeros, but make sure to not forget the extra bytes
+ if (extraBytes === 1) {
+ tmp = uint8[len - 1]
+ parts.push(
+ lookup[tmp >> 2] +
+ lookup[(tmp << 4) & 0x3F] +
+ '=='
+ )
+ } else if (extraBytes === 2) {
+ tmp = (uint8[len - 2] << 8) + uint8[len - 1]
+ parts.push(
+ lookup[tmp >> 10] +
+ lookup[(tmp >> 4) & 0x3F] +
+ lookup[(tmp << 2) & 0x3F] +
+ '='
+ )
+ }
+
+ return parts.join('')
+}
+
+},{}],11:[function(require,module,exports){
+(function (global){(function (){
+/*! https://mths.be/punycode v1.4.1 by @mathias */
+;(function(root) {
+
+ /** Detect free variables */
+ var freeExports = typeof exports == 'object' && exports &&
+ !exports.nodeType && exports;
+ var freeModule = typeof module == 'object' && module &&
+ !module.nodeType && module;
+ var freeGlobal = typeof global == 'object' && global;
+ if (
+ freeGlobal.global === freeGlobal ||
+ freeGlobal.window === freeGlobal ||
+ freeGlobal.self === freeGlobal
+ ) {
+ root = freeGlobal;
+ }
+
+ /**
+ * The `punycode` object.
+ * @name punycode
+ * @type Object
+ */
+ var punycode,
+
+ /** Highest positive signed 32-bit float value */
+ maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
+
+ /** Bootstring parameters */
+ base = 36,
+ tMin = 1,
+ tMax = 26,
+ skew = 38,
+ damp = 700,
+ initialBias = 72,
+ initialN = 128, // 0x80
+ delimiter = '-', // '\x2D'
+
+ /** Regular expressions */
+ regexPunycode = /^xn--/,
+ regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
+ regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
+
+ /** Error messages */
+ errors = {
+ 'overflow': 'Overflow: input needs wider integers to process',
+ 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
+ 'invalid-input': 'Invalid input'
+ },
+
+ /** Convenience shortcuts */
+ baseMinusTMin = base - tMin,
+ floor = Math.floor,
+ stringFromCharCode = String.fromCharCode,
+
+ /** Temporary variable */
+ key;
+
+ /*--------------------------------------------------------------------------*/
+
+ /**
+ * A generic error utility function.
+ * @private
+ * @param {String} type The error type.
+ * @returns {Error} Throws a `RangeError` with the applicable error message.
+ */
+ function error(type) {
+ throw new RangeError(errors[type]);
+ }
+
+ /**
+ * A generic `Array#map` utility function.
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function that gets called for every array
+ * item.
+ * @returns {Array} A new array of values returned by the callback function.
+ */
+ function map(array, fn) {
+ var length = array.length;
+ var result = [];
+ while (length--) {
+ result[length] = fn(array[length]);
+ }
+ return result;
+ }
+
+ /**
+ * A simple `Array#map`-like wrapper to work with domain name strings or email
+ * addresses.
+ * @private
+ * @param {String} domain The domain name or email address.
+ * @param {Function} callback The function that gets called for every
+ * character.
+ * @returns {Array} A new string of characters returned by the callback
+ * function.
+ */
+ function mapDomain(string, fn) {
+ var parts = string.split('@');
+ var result = '';
+ if (parts.length > 1) {
+ // In email addresses, only the domain name should be punycoded. Leave
+ // the local part (i.e. everything up to `@`) intact.
+ result = parts[0] + '@';
+ string = parts[1];
+ }
+ // Avoid `split(regex)` for IE8 compatibility. See #17.
+ string = string.replace(regexSeparators, '\x2E');
+ var labels = string.split('.');
+ var encoded = map(labels, fn).join('.');
+ return result + encoded;
+ }
+
+ /**
+ * Creates an array containing the numeric code points of each Unicode
+ * character in the string. While JavaScript uses UCS-2 internally,
+ * this function will convert a pair of surrogate halves (each of which
+ * UCS-2 exposes as separate characters) into a single code point,
+ * matching UTF-16.
+ * @see `punycode.ucs2.encode`
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode.ucs2
+ * @name decode
+ * @param {String} string The Unicode input string (UCS-2).
+ * @returns {Array} The new array of code points.
+ */
+ function ucs2decode(string) {
+ var output = [],
+ counter = 0,
+ length = string.length,
+ value,
+ extra;
+ while (counter < length) {
+ value = string.charCodeAt(counter++);
+ if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
+ // high surrogate, and there is a next character
+ extra = string.charCodeAt(counter++);
+ if ((extra & 0xFC00) == 0xDC00) { // low surrogate
+ output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
+ } else {
+ // unmatched surrogate; only append this code unit, in case the next
+ // code unit is the high surrogate of a surrogate pair
+ output.push(value);
+ counter--;
+ }
+ } else {
+ output.push(value);
+ }
+ }
+ return output;
+ }
+
+ /**
+ * Creates a string based on an array of numeric code points.
+ * @see `punycode.ucs2.decode`
+ * @memberOf punycode.ucs2
+ * @name encode
+ * @param {Array} codePoints The array of numeric code points.
+ * @returns {String} The new Unicode string (UCS-2).
+ */
+ function ucs2encode(array) {
+ return map(array, function(value) {
+ var output = '';
+ if (value > 0xFFFF) {
+ value -= 0x10000;
+ output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
+ value = 0xDC00 | value & 0x3FF;
+ }
+ output += stringFromCharCode(value);
+ return output;
+ }).join('');
+ }
+
+ /**
+ * Converts a basic code point into a digit/integer.
+ * @see `digitToBasic()`
+ * @private
+ * @param {Number} codePoint The basic numeric code point value.
+ * @returns {Number} The numeric value of a basic code point (for use in
+ * representing integers) in the range `0` to `base - 1`, or `base` if
+ * the code point does not represent a value.
+ */
+ function basicToDigit(codePoint) {
+ if (codePoint - 48 < 10) {
+ return codePoint - 22;
+ }
+ if (codePoint - 65 < 26) {
+ return codePoint - 65;
+ }
+ if (codePoint - 97 < 26) {
+ return codePoint - 97;
+ }
+ return base;
+ }
+
+ /**
+ * Converts a digit/integer into a basic code point.
+ * @see `basicToDigit()`
+ * @private
+ * @param {Number} digit The numeric value of a basic code point.
+ * @returns {Number} The basic code point whose value (when used for
+ * representing integers) is `digit`, which needs to be in the range
+ * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
+ * used; else, the lowercase form is used. The behavior is undefined
+ * if `flag` is non-zero and `digit` has no uppercase form.
+ */
+ function digitToBasic(digit, flag) {
+ // 0..25 map to ASCII a..z or A..Z
+ // 26..35 map to ASCII 0..9
+ return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
+ }
+
+ /**
+ * Bias adaptation function as per section 3.4 of RFC 3492.
+ * https://tools.ietf.org/html/rfc3492#section-3.4
+ * @private
+ */
+ function adapt(delta, numPoints, firstTime) {
+ var k = 0;
+ delta = firstTime ? floor(delta / damp) : delta >> 1;
+ delta += floor(delta / numPoints);
+ for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
+ delta = floor(delta / baseMinusTMin);
+ }
+ return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
+ }
+
+ /**
+ * Converts a Punycode string of ASCII-only symbols to a string of Unicode
+ * symbols.
+ * @memberOf punycode
+ * @param {String} input The Punycode string of ASCII-only symbols.
+ * @returns {String} The resulting string of Unicode symbols.
+ */
+ function decode(input) {
+ // Don't use UCS-2
+ var output = [],
+ inputLength = input.length,
+ out,
+ i = 0,
+ n = initialN,
+ bias = initialBias,
+ basic,
+ j,
+ index,
+ oldi,
+ w,
+ k,
+ digit,
+ t,
+ /** Cached calculation results */
+ baseMinusT;
+
+ // Handle the basic code points: let `basic` be the number of input code
+ // points before the last delimiter, or `0` if there is none, then copy
+ // the first basic code points to the output.
+
+ basic = input.lastIndexOf(delimiter);
+ if (basic < 0) {
+ basic = 0;
+ }
+
+ for (j = 0; j < basic; ++j) {
+ // if it's not a basic code point
+ if (input.charCodeAt(j) >= 0x80) {
+ error('not-basic');
+ }
+ output.push(input.charCodeAt(j));
+ }
+
+ // Main decoding loop: start just after the last delimiter if any basic code
+ // points were copied; start at the beginning otherwise.
+
+ for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
+
+ // `index` is the index of the next character to be consumed.
+ // Decode a generalized variable-length integer into `delta`,
+ // which gets added to `i`. The overflow checking is easier
+ // if we increase `i` as we go, then subtract off its starting
+ // value at the end to obtain `delta`.
+ for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
+
+ if (index >= inputLength) {
+ error('invalid-input');
+ }
+
+ digit = basicToDigit(input.charCodeAt(index++));
+
+ if (digit >= base || digit > floor((maxInt - i) / w)) {
+ error('overflow');
+ }
+
+ i += digit * w;
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+
+ if (digit < t) {
+ break;
+ }
+
+ baseMinusT = base - t;
+ if (w > floor(maxInt / baseMinusT)) {
+ error('overflow');
+ }
+
+ w *= baseMinusT;
+
+ }
+
+ out = output.length + 1;
+ bias = adapt(i - oldi, out, oldi == 0);
+
+ // `i` was supposed to wrap around from `out` to `0`,
+ // incrementing `n` each time, so we'll fix that now:
+ if (floor(i / out) > maxInt - n) {
+ error('overflow');
+ }
+
+ n += floor(i / out);
+ i %= out;
+
+ // Insert `n` at position `i` of the output
+ output.splice(i++, 0, n);
+
+ }
+
+ return ucs2encode(output);
+ }
+
+ /**
+ * Converts a string of Unicode symbols (e.g. a domain name label) to a
+ * Punycode string of ASCII-only symbols.
+ * @memberOf punycode
+ * @param {String} input The string of Unicode symbols.
+ * @returns {String} The resulting Punycode string of ASCII-only symbols.
+ */
+ function encode(input) {
+ var n,
+ delta,
+ handledCPCount,
+ basicLength,
+ bias,
+ j,
+ m,
+ q,
+ k,
+ t,
+ currentValue,
+ output = [],
+ /** `inputLength` will hold the number of code points in `input`. */
+ inputLength,
+ /** Cached calculation results */
+ handledCPCountPlusOne,
+ baseMinusT,
+ qMinusT;
+
+ // Convert the input in UCS-2 to Unicode
+ input = ucs2decode(input);
+
+ // Cache the length
+ inputLength = input.length;
+
+ // Initialize the state
+ n = initialN;
+ delta = 0;
+ bias = initialBias;
+
+ // Handle the basic code points
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue < 0x80) {
+ output.push(stringFromCharCode(currentValue));
+ }
+ }
+
+ handledCPCount = basicLength = output.length;
+
+ // `handledCPCount` is the number of code points that have been handled;
+ // `basicLength` is the number of basic code points.
+
+ // Finish the basic string - if it is not empty - with a delimiter
+ if (basicLength) {
+ output.push(delimiter);
+ }
+
+ // Main encoding loop:
+ while (handledCPCount < inputLength) {
+
+ // All non-basic code points < n have been handled already. Find the next
+ // larger one:
+ for (m = maxInt, j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+ if (currentValue >= n && currentValue < m) {
+ m = currentValue;
+ }
+ }
+
+ // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
+ // but guard against overflow
+ handledCPCountPlusOne = handledCPCount + 1;
+ if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
+ error('overflow');
+ }
+
+ delta += (m - n) * handledCPCountPlusOne;
+ n = m;
+
+ for (j = 0; j < inputLength; ++j) {
+ currentValue = input[j];
+
+ if (currentValue < n && ++delta > maxInt) {
+ error('overflow');
+ }
+
+ if (currentValue == n) {
+ // Represent delta as a generalized variable-length integer
+ for (q = delta, k = base; /* no condition */; k += base) {
+ t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
+ if (q < t) {
+ break;
+ }
+ qMinusT = q - t;
+ baseMinusT = base - t;
+ output.push(
+ stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
+ );
+ q = floor(qMinusT / baseMinusT);
+ }
+
+ output.push(stringFromCharCode(digitToBasic(q, 0)));
+ bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
+ delta = 0;
+ ++handledCPCount;
+ }
+ }
+
+ ++delta;
+ ++n;
+
+ }
+ return output.join('');
+ }
+
+ /**
+ * Converts a Punycode string representing a domain name or an email address
+ * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
+ * it doesn't matter if you call it on a string that has already been
+ * converted to Unicode.
+ * @memberOf punycode
+ * @param {String} input The Punycoded domain name or email address to
+ * convert to Unicode.
+ * @returns {String} The Unicode representation of the given Punycode
+ * string.
+ */
+ function toUnicode(input) {
+ return mapDomain(input, function(string) {
+ return regexPunycode.test(string)
+ ? decode(string.slice(4).toLowerCase())
+ : string;
+ });
+ }
+
+ /**
+ * Converts a Unicode string representing a domain name or an email address to
+ * Punycode. Only the non-ASCII parts of the domain name will be converted,
+ * i.e. it doesn't matter if you call it with a domain that's already in
+ * ASCII.
+ * @memberOf punycode
+ * @param {String} input The domain name or email address to convert, as a
+ * Unicode string.
+ * @returns {String} The Punycode representation of the given domain name or
+ * email address.
+ */
+ function toASCII(input) {
+ return mapDomain(input, function(string) {
+ return regexNonASCII.test(string)
+ ? 'xn--' + encode(string)
+ : string;
+ });
+ }
+
+ /*--------------------------------------------------------------------------*/
+
+ /** Define the public API */
+ punycode = {
+ /**
+ * A string representing the current Punycode.js version number.
+ * @memberOf punycode
+ * @type String
+ */
+ 'version': '1.4.1',
+ /**
+ * An object of methods to convert from JavaScript's internal character
+ * representation (UCS-2) to Unicode code points, and back.
+ * @see <https://mathiasbynens.be/notes/javascript-encoding>
+ * @memberOf punycode
+ * @type Object
+ */
+ 'ucs2': {
+ 'decode': ucs2decode,
+ 'encode': ucs2encode
+ },
+ 'decode': decode,
+ 'encode': encode,
+ 'toASCII': toASCII,
+ 'toUnicode': toUnicode
+ };
+
+ /** Expose `punycode` */
+ // Some AMD build optimizers, like r.js, check for specific condition patterns
+ // like the following:
+ if (
+ typeof define == 'function' &&
+ typeof define.amd == 'object' &&
+ define.amd
+ ) {
+ define('punycode', function() {
+ return punycode;
+ });
+ } else if (freeExports && freeModule) {
+ if (module.exports == freeExports) {
+ // in Node.js, io.js, or RingoJS v0.8.0+
+ freeModule.exports = punycode;
+ } else {
+ // in Narwhal or RingoJS v0.7.0-
+ for (key in punycode) {
+ punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
+ }
+ }
+ } else {
+ // in Rhino or a web browser
+ root.punycode = punycode;
+ }
+
+}(this));
+
+}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],12:[function(require,module,exports){
+(function (Buffer){(function (){
+/*!
+ * The buffer module from node.js, for the browser.
+ *
+ * @author Feross Aboukhadijeh <https://feross.org>
+ * @license MIT
+ */
+/* eslint-disable no-proto */
+
+'use strict'
+
+var base64 = require('base64-js')
+var ieee754 = require('ieee754')
+
+exports.Buffer = Buffer
+exports.SlowBuffer = SlowBuffer
+exports.INSPECT_MAX_BYTES = 50
+
+var K_MAX_LENGTH = 0x7fffffff
+exports.kMaxLength = K_MAX_LENGTH
+
+/**
+ * If `Buffer.TYPED_ARRAY_SUPPORT`:
+ * === true Use Uint8Array implementation (fastest)
+ * === false Print warning and recommend using `buffer` v4.x which has an Object
+ * implementation (most compatible, even IE6)
+ *
+ * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
+ * Opera 11.6+, iOS 4.2+.
+ *
+ * We report that the browser does not support typed arrays if the are not subclassable
+ * using __proto__. Firefox 4-29 lacks support for adding new properties to `Uint8Array`
+ * (See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438). IE 10 lacks support
+ * for __proto__ and has a buggy typed array implementation.
+ */
+Buffer.TYPED_ARRAY_SUPPORT = typedArraySupport()
+
+if (!Buffer.TYPED_ARRAY_SUPPORT && typeof console !== 'undefined' &&
+ typeof console.error === 'function') {
+ console.error(
+ 'This browser lacks typed array (Uint8Array) support which is required by ' +
+ '`buffer` v5.x. Use `buffer` v4.x if you require old browser support.'
+ )
+}
+
+function typedArraySupport () {
+ // Can typed array instances can be augmented?
+ try {
+ var arr = new Uint8Array(1)
+ arr.__proto__ = { __proto__: Uint8Array.prototype, foo: function () { return 42 } }
+ return arr.foo() === 42
+ } catch (e) {
+ return false
+ }
+}
+
+Object.defineProperty(Buffer.prototype, 'parent', {
+ enumerable: true,
+ get: function () {
+ if (!Buffer.isBuffer(this)) return undefined
+ return this.buffer
+ }
+})
+
+Object.defineProperty(Buffer.prototype, 'offset', {
+ enumerable: true,
+ get: function () {
+ if (!Buffer.isBuffer(this)) return undefined
+ return this.byteOffset
+ }
+})
+
+function createBuffer (length) {
+ if (length > K_MAX_LENGTH) {
+ throw new RangeError('The value "' + length + '" is invalid for option "size"')
+ }
+ // Return an augmented `Uint8Array` instance
+ var buf = new Uint8Array(length)
+ buf.__proto__ = Buffer.prototype
+ return buf
+}
+
+/**
+ * The Buffer constructor returns instances of `Uint8Array` that have their
+ * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
+ * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
+ * and the `Uint8Array` methods. Square bracket notation works as expected -- it
+ * returns a single octet.
+ *
+ * The `Uint8Array` prototype remains unmodified.
+ */
+
+function Buffer (arg, encodingOrOffset, length) {
+ // Common case.
+ if (typeof arg === 'number') {
+ if (typeof encodingOrOffset === 'string') {
+ throw new TypeError(
+ 'The "string" argument must be of type string. Received type number'
+ )
+ }
+ return allocUnsafe(arg)
+ }
+ return from(arg, encodingOrOffset, length)
+}
+
+// Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
+if (typeof Symbol !== 'undefined' && Symbol.species != null &&
+ Buffer[Symbol.species] === Buffer) {
+ Object.defineProperty(Buffer, Symbol.species, {
+ value: null,
+ configurable: true,
+ enumerable: false,
+ writable: false
+ })
+}
+
+Buffer.poolSize = 8192 // not used by this implementation
+
+function from (value, encodingOrOffset, length) {
+ if (typeof value === 'string') {
+ return fromString(value, encodingOrOffset)
+ }
+
+ if (ArrayBuffer.isView(value)) {
+ return fromArrayLike(value)
+ }
+
+ if (value == null) {
+ throw TypeError(
+ 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
+ 'or Array-like Object. Received type ' + (typeof value)
+ )
+ }
+
+ if (isInstance(value, ArrayBuffer) ||
+ (value && isInstance(value.buffer, ArrayBuffer))) {
+ return fromArrayBuffer(value, encodingOrOffset, length)
+ }
+
+ if (typeof value === 'number') {
+ throw new TypeError(
+ 'The "value" argument must not be of type number. Received type number'
+ )
+ }
+
+ var valueOf = value.valueOf && value.valueOf()
+ if (valueOf != null && valueOf !== value) {
+ return Buffer.from(valueOf, encodingOrOffset, length)
+ }
+
+ var b = fromObject(value)
+ if (b) return b
+
+ if (typeof Symbol !== 'undefined' && Symbol.toPrimitive != null &&
+ typeof value[Symbol.toPrimitive] === 'function') {
+ return Buffer.from(
+ value[Symbol.toPrimitive]('string'), encodingOrOffset, length
+ )
+ }
+
+ throw new TypeError(
+ 'The first argument must be one of type string, Buffer, ArrayBuffer, Array, ' +
+ 'or Array-like Object. Received type ' + (typeof value)
+ )
+}
+
+/**
+ * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
+ * if value is a number.
+ * Buffer.from(str[, encoding])
+ * Buffer.from(array)
+ * Buffer.from(buffer)
+ * Buffer.from(arrayBuffer[, byteOffset[, length]])
+ **/
+Buffer.from = function (value, encodingOrOffset, length) {
+ return from(value, encodingOrOffset, length)
+}
+
+// Note: Change prototype *after* Buffer.from is defined to workaround Chrome bug:
+// https://github.com/feross/buffer/pull/148
+Buffer.prototype.__proto__ = Uint8Array.prototype
+Buffer.__proto__ = Uint8Array
+
+function assertSize (size) {
+ if (typeof size !== 'number') {
+ throw new TypeError('"size" argument must be of type number')
+ } else if (size < 0) {
+ throw new RangeError('The value "' + size + '" is invalid for option "size"')
+ }
+}
+
+function alloc (size, fill, encoding) {
+ assertSize(size)
+ if (size <= 0) {
+ return createBuffer(size)
+ }
+ if (fill !== undefined) {
+ // Only pay attention to encoding if it's a string. This
+ // prevents accidentally sending in a number that would
+ // be interpretted as a start offset.
+ return typeof encoding === 'string'
+ ? createBuffer(size).fill(fill, encoding)
+ : createBuffer(size).fill(fill)
+ }
+ return createBuffer(size)
+}
+
+/**
+ * Creates a new filled Buffer instance.
+ * alloc(size[, fill[, encoding]])
+ **/
+Buffer.alloc = function (size, fill, encoding) {
+ return alloc(size, fill, encoding)
+}
+
+function allocUnsafe (size) {
+ assertSize(size)
+ return createBuffer(size < 0 ? 0 : checked(size) | 0)
+}
+
+/**
+ * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
+ * */
+Buffer.allocUnsafe = function (size) {
+ return allocUnsafe(size)
+}
+/**
+ * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
+ */
+Buffer.allocUnsafeSlow = function (size) {
+ return allocUnsafe(size)
+}
+
+function fromString (string, encoding) {
+ if (typeof encoding !== 'string' || encoding === '') {
+ encoding = 'utf8'
+ }
+
+ if (!Buffer.isEncoding(encoding)) {
+ throw new TypeError('Unknown encoding: ' + encoding)
+ }
+
+ var length = byteLength(string, encoding) | 0
+ var buf = createBuffer(length)
+
+ var actual = buf.write(string, encoding)
+
+ if (actual !== length) {
+ // Writing a hex string, for example, that contains invalid characters will
+ // cause everything after the first invalid character to be ignored. (e.g.
+ // 'abxxcd' will be treated as 'ab')
+ buf = buf.slice(0, actual)
+ }
+
+ return buf
+}
+
+function fromArrayLike (array) {
+ var length = array.length < 0 ? 0 : checked(array.length) | 0
+ var buf = createBuffer(length)
+ for (var i = 0; i < length; i += 1) {
+ buf[i] = array[i] & 255
+ }
+ return buf
+}
+
+function fromArrayBuffer (array, byteOffset, length) {
+ if (byteOffset < 0 || array.byteLength < byteOffset) {
+ throw new RangeError('"offset" is outside of buffer bounds')
+ }
+
+ if (array.byteLength < byteOffset + (length || 0)) {
+ throw new RangeError('"length" is outside of buffer bounds')
+ }
+
+ var buf
+ if (byteOffset === undefined && length === undefined) {
+ buf = new Uint8Array(array)
+ } else if (length === undefined) {
+ buf = new Uint8Array(array, byteOffset)
+ } else {
+ buf = new Uint8Array(array, byteOffset, length)
+ }
+
+ // Return an augmented `Uint8Array` instance
+ buf.__proto__ = Buffer.prototype
+ return buf
+}
+
+function fromObject (obj) {
+ if (Buffer.isBuffer(obj)) {
+ var len = checked(obj.length) | 0
+ var buf = createBuffer(len)
+
+ if (buf.length === 0) {
+ return buf
+ }
+
+ obj.copy(buf, 0, 0, len)
+ return buf
+ }
+
+ if (obj.length !== undefined) {
+ if (typeof obj.length !== 'number' || numberIsNaN(obj.length)) {
+ return createBuffer(0)
+ }
+ return fromArrayLike(obj)
+ }
+
+ if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
+ return fromArrayLike(obj.data)
+ }
+}
+
+function checked (length) {
+ // Note: cannot use `length < K_MAX_LENGTH` here because that fails when
+ // length is NaN (which is otherwise coerced to zero.)
+ if (length >= K_MAX_LENGTH) {
+ throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
+ 'size: 0x' + K_MAX_LENGTH.toString(16) + ' bytes')
+ }
+ return length | 0
+}
+
+function SlowBuffer (length) {
+ if (+length != length) { // eslint-disable-line eqeqeq
+ length = 0
+ }
+ return Buffer.alloc(+length)
+}
+
+Buffer.isBuffer = function isBuffer (b) {
+ return b != null && b._isBuffer === true &&
+ b !== Buffer.prototype // so Buffer.isBuffer(Buffer.prototype) will be false
+}
+
+Buffer.compare = function compare (a, b) {
+ if (isInstance(a, Uint8Array)) a = Buffer.from(a, a.offset, a.byteLength)
+ if (isInstance(b, Uint8Array)) b = Buffer.from(b, b.offset, b.byteLength)
+ if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
+ throw new TypeError(
+ 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array'
+ )
+ }
+
+ if (a === b) return 0
+
+ var x = a.length
+ var y = b.length
+
+ for (var i = 0, len = Math.min(x, y); i < len; ++i) {
+ if (a[i] !== b[i]) {
+ x = a[i]
+ y = b[i]
+ break
+ }
+ }
+
+ if (x < y) return -1
+ if (y < x) return 1
+ return 0
+}
+
+Buffer.isEncoding = function isEncoding (encoding) {
+ switch (String(encoding).toLowerCase()) {
+ case 'hex':
+ case 'utf8':
+ case 'utf-8':
+ case 'ascii':
+ case 'latin1':
+ case 'binary':
+ case 'base64':
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return true
+ default:
+ return false
+ }
+}
+
+Buffer.concat = function concat (list, length) {
+ if (!Array.isArray(list)) {
+ throw new TypeError('"list" argument must be an Array of Buffers')
+ }
+
+ if (list.length === 0) {
+ return Buffer.alloc(0)
+ }
+
+ var i
+ if (length === undefined) {
+ length = 0
+ for (i = 0; i < list.length; ++i) {
+ length += list[i].length
+ }
+ }
+
+ var buffer = Buffer.allocUnsafe(length)
+ var pos = 0
+ for (i = 0; i < list.length; ++i) {
+ var buf = list[i]
+ if (isInstance(buf, Uint8Array)) {
+ buf = Buffer.from(buf)
+ }
+ if (!Buffer.isBuffer(buf)) {
+ throw new TypeError('"list" argument must be an Array of Buffers')
+ }
+ buf.copy(buffer, pos)
+ pos += buf.length
+ }
+ return buffer
+}
+
+function byteLength (string, encoding) {
+ if (Buffer.isBuffer(string)) {
+ return string.length
+ }
+ if (ArrayBuffer.isView(string) || isInstance(string, ArrayBuffer)) {
+ return string.byteLength
+ }
+ if (typeof string !== 'string') {
+ throw new TypeError(
+ 'The "string" argument must be one of type string, Buffer, or ArrayBuffer. ' +
+ 'Received type ' + typeof string
+ )
+ }
+
+ var len = string.length
+ var mustMatch = (arguments.length > 2 && arguments[2] === true)
+ if (!mustMatch && len === 0) return 0
+
+ // Use a for loop to avoid recursion
+ var loweredCase = false
+ for (;;) {
+ switch (encoding) {
+ case 'ascii':
+ case 'latin1':
+ case 'binary':
+ return len
+ case 'utf8':
+ case 'utf-8':
+ return utf8ToBytes(string).length
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return len * 2
+ case 'hex':
+ return len >>> 1
+ case 'base64':
+ return base64ToBytes(string).length
+ default:
+ if (loweredCase) {
+ return mustMatch ? -1 : utf8ToBytes(string).length // assume utf8
+ }
+ encoding = ('' + encoding).toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+Buffer.byteLength = byteLength
+
+function slowToString (encoding, start, end) {
+ var loweredCase = false
+
+ // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
+ // property of a typed array.
+
+ // This behaves neither like String nor Uint8Array in that we set start/end
+ // to their upper/lower bounds if the value passed is out of range.
+ // undefined is handled specially as per ECMA-262 6th Edition,
+ // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
+ if (start === undefined || start < 0) {
+ start = 0
+ }
+ // Return early if start > this.length. Done here to prevent potential uint32
+ // coercion fail below.
+ if (start > this.length) {
+ return ''
+ }
+
+ if (end === undefined || end > this.length) {
+ end = this.length
+ }
+
+ if (end <= 0) {
+ return ''
+ }
+
+ // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
+ end >>>= 0
+ start >>>= 0
+
+ if (end <= start) {
+ return ''
+ }
+
+ if (!encoding) encoding = 'utf8'
+
+ while (true) {
+ switch (encoding) {
+ case 'hex':
+ return hexSlice(this, start, end)
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8Slice(this, start, end)
+
+ case 'ascii':
+ return asciiSlice(this, start, end)
+
+ case 'latin1':
+ case 'binary':
+ return latin1Slice(this, start, end)
+
+ case 'base64':
+ return base64Slice(this, start, end)
+
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return utf16leSlice(this, start, end)
+
+ default:
+ if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
+ encoding = (encoding + '').toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+
+// This property is used by `Buffer.isBuffer` (and the `is-buffer` npm package)
+// to detect a Buffer instance. It's not possible to use `instanceof Buffer`
+// reliably in a browserify context because there could be multiple different
+// copies of the 'buffer' package in use. This method works even for Buffer
+// instances that were created from another copy of the `buffer` package.
+// See: https://github.com/feross/buffer/issues/154
+Buffer.prototype._isBuffer = true
+
+function swap (b, n, m) {
+ var i = b[n]
+ b[n] = b[m]
+ b[m] = i
+}
+
+Buffer.prototype.swap16 = function swap16 () {
+ var len = this.length
+ if (len % 2 !== 0) {
+ throw new RangeError('Buffer size must be a multiple of 16-bits')
+ }
+ for (var i = 0; i < len; i += 2) {
+ swap(this, i, i + 1)
+ }
+ return this
+}
+
+Buffer.prototype.swap32 = function swap32 () {
+ var len = this.length
+ if (len % 4 !== 0) {
+ throw new RangeError('Buffer size must be a multiple of 32-bits')
+ }
+ for (var i = 0; i < len; i += 4) {
+ swap(this, i, i + 3)
+ swap(this, i + 1, i + 2)
+ }
+ return this
+}
+
+Buffer.prototype.swap64 = function swap64 () {
+ var len = this.length
+ if (len % 8 !== 0) {
+ throw new RangeError('Buffer size must be a multiple of 64-bits')
+ }
+ for (var i = 0; i < len; i += 8) {
+ swap(this, i, i + 7)
+ swap(this, i + 1, i + 6)
+ swap(this, i + 2, i + 5)
+ swap(this, i + 3, i + 4)
+ }
+ return this
+}
+
+Buffer.prototype.toString = function toString () {
+ var length = this.length
+ if (length === 0) return ''
+ if (arguments.length === 0) return utf8Slice(this, 0, length)
+ return slowToString.apply(this, arguments)
+}
+
+Buffer.prototype.toLocaleString = Buffer.prototype.toString
+
+Buffer.prototype.equals = function equals (b) {
+ if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
+ if (this === b) return true
+ return Buffer.compare(this, b) === 0
+}
+
+Buffer.prototype.inspect = function inspect () {
+ var str = ''
+ var max = exports.INSPECT_MAX_BYTES
+ str = this.toString('hex', 0, max).replace(/(.{2})/g, '$1 ').trim()
+ if (this.length > max) str += ' ... '
+ return '<Buffer ' + str + '>'
+}
+
+Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
+ if (isInstance(target, Uint8Array)) {
+ target = Buffer.from(target, target.offset, target.byteLength)
+ }
+ if (!Buffer.isBuffer(target)) {
+ throw new TypeError(
+ 'The "target" argument must be one of type Buffer or Uint8Array. ' +
+ 'Received type ' + (typeof target)
+ )
+ }
+
+ if (start === undefined) {
+ start = 0
+ }
+ if (end === undefined) {
+ end = target ? target.length : 0
+ }
+ if (thisStart === undefined) {
+ thisStart = 0
+ }
+ if (thisEnd === undefined) {
+ thisEnd = this.length
+ }
+
+ if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
+ throw new RangeError('out of range index')
+ }
+
+ if (thisStart >= thisEnd && start >= end) {
+ return 0
+ }
+ if (thisStart >= thisEnd) {
+ return -1
+ }
+ if (start >= end) {
+ return 1
+ }
+
+ start >>>= 0
+ end >>>= 0
+ thisStart >>>= 0
+ thisEnd >>>= 0
+
+ if (this === target) return 0
+
+ var x = thisEnd - thisStart
+ var y = end - start
+ var len = Math.min(x, y)
+
+ var thisCopy = this.slice(thisStart, thisEnd)
+ var targetCopy = target.slice(start, end)
+
+ for (var i = 0; i < len; ++i) {
+ if (thisCopy[i] !== targetCopy[i]) {
+ x = thisCopy[i]
+ y = targetCopy[i]
+ break
+ }
+ }
+
+ if (x < y) return -1
+ if (y < x) return 1
+ return 0
+}
+
+// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
+// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
+//
+// Arguments:
+// - buffer - a Buffer to search
+// - val - a string, Buffer, or number
+// - byteOffset - an index into `buffer`; will be clamped to an int32
+// - encoding - an optional encoding, relevant is val is a string
+// - dir - true for indexOf, false for lastIndexOf
+function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
+ // Empty buffer means no match
+ if (buffer.length === 0) return -1
+
+ // Normalize byteOffset
+ if (typeof byteOffset === 'string') {
+ encoding = byteOffset
+ byteOffset = 0
+ } else if (byteOffset > 0x7fffffff) {
+ byteOffset = 0x7fffffff
+ } else if (byteOffset < -0x80000000) {
+ byteOffset = -0x80000000
+ }
+ byteOffset = +byteOffset // Coerce to Number.
+ if (numberIsNaN(byteOffset)) {
+ // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
+ byteOffset = dir ? 0 : (buffer.length - 1)
+ }
+
+ // Normalize byteOffset: negative offsets start from the end of the buffer
+ if (byteOffset < 0) byteOffset = buffer.length + byteOffset
+ if (byteOffset >= buffer.length) {
+ if (dir) return -1
+ else byteOffset = buffer.length - 1
+ } else if (byteOffset < 0) {
+ if (dir) byteOffset = 0
+ else return -1
+ }
+
+ // Normalize val
+ if (typeof val === 'string') {
+ val = Buffer.from(val, encoding)
+ }
+
+ // Finally, search either indexOf (if dir is true) or lastIndexOf
+ if (Buffer.isBuffer(val)) {
+ // Special case: looking for empty string/buffer always fails
+ if (val.length === 0) {
+ return -1
+ }
+ return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
+ } else if (typeof val === 'number') {
+ val = val & 0xFF // Search for a byte value [0-255]
+ if (typeof Uint8Array.prototype.indexOf === 'function') {
+ if (dir) {
+ return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
+ } else {
+ return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
+ }
+ }
+ return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
+ }
+
+ throw new TypeError('val must be string, number or Buffer')
+}
+
+function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
+ var indexSize = 1
+ var arrLength = arr.length
+ var valLength = val.length
+
+ if (encoding !== undefined) {
+ encoding = String(encoding).toLowerCase()
+ if (encoding === 'ucs2' || encoding === 'ucs-2' ||
+ encoding === 'utf16le' || encoding === 'utf-16le') {
+ if (arr.length < 2 || val.length < 2) {
+ return -1
+ }
+ indexSize = 2
+ arrLength /= 2
+ valLength /= 2
+ byteOffset /= 2
+ }
+ }
+
+ function read (buf, i) {
+ if (indexSize === 1) {
+ return buf[i]
+ } else {
+ return buf.readUInt16BE(i * indexSize)
+ }
+ }
+
+ var i
+ if (dir) {
+ var foundIndex = -1
+ for (i = byteOffset; i < arrLength; i++) {
+ if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
+ if (foundIndex === -1) foundIndex = i
+ if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
+ } else {
+ if (foundIndex !== -1) i -= i - foundIndex
+ foundIndex = -1
+ }
+ }
+ } else {
+ if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
+ for (i = byteOffset; i >= 0; i--) {
+ var found = true
+ for (var j = 0; j < valLength; j++) {
+ if (read(arr, i + j) !== read(val, j)) {
+ found = false
+ break
+ }
+ }
+ if (found) return i
+ }
+ }
+
+ return -1
+}
+
+Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
+ return this.indexOf(val, byteOffset, encoding) !== -1
+}
+
+Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
+ return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
+}
+
+Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
+ return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
+}
+
+function hexWrite (buf, string, offset, length) {
+ offset = Number(offset) || 0
+ var remaining = buf.length - offset
+ if (!length) {
+ length = remaining
+ } else {
+ length = Number(length)
+ if (length > remaining) {
+ length = remaining
+ }
+ }
+
+ var strLen = string.length
+
+ if (length > strLen / 2) {
+ length = strLen / 2
+ }
+ for (var i = 0; i < length; ++i) {
+ var parsed = parseInt(string.substr(i * 2, 2), 16)
+ if (numberIsNaN(parsed)) return i
+ buf[offset + i] = parsed
+ }
+ return i
+}
+
+function utf8Write (buf, string, offset, length) {
+ return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
+}
+
+function asciiWrite (buf, string, offset, length) {
+ return blitBuffer(asciiToBytes(string), buf, offset, length)
+}
+
+function latin1Write (buf, string, offset, length) {
+ return asciiWrite(buf, string, offset, length)
+}
+
+function base64Write (buf, string, offset, length) {
+ return blitBuffer(base64ToBytes(string), buf, offset, length)
+}
+
+function ucs2Write (buf, string, offset, length) {
+ return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
+}
+
+Buffer.prototype.write = function write (string, offset, length, encoding) {
+ // Buffer#write(string)
+ if (offset === undefined) {
+ encoding = 'utf8'
+ length = this.length
+ offset = 0
+ // Buffer#write(string, encoding)
+ } else if (length === undefined && typeof offset === 'string') {
+ encoding = offset
+ length = this.length
+ offset = 0
+ // Buffer#write(string, offset[, length][, encoding])
+ } else if (isFinite(offset)) {
+ offset = offset >>> 0
+ if (isFinite(length)) {
+ length = length >>> 0
+ if (encoding === undefined) encoding = 'utf8'
+ } else {
+ encoding = length
+ length = undefined
+ }
+ } else {
+ throw new Error(
+ 'Buffer.write(string, encoding, offset[, length]) is no longer supported'
+ )
+ }
+
+ var remaining = this.length - offset
+ if (length === undefined || length > remaining) length = remaining
+
+ if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
+ throw new RangeError('Attempt to write outside buffer bounds')
+ }
+
+ if (!encoding) encoding = 'utf8'
+
+ var loweredCase = false
+ for (;;) {
+ switch (encoding) {
+ case 'hex':
+ return hexWrite(this, string, offset, length)
+
+ case 'utf8':
+ case 'utf-8':
+ return utf8Write(this, string, offset, length)
+
+ case 'ascii':
+ return asciiWrite(this, string, offset, length)
+
+ case 'latin1':
+ case 'binary':
+ return latin1Write(this, string, offset, length)
+
+ case 'base64':
+ // Warning: maxLength not taken into account in base64Write
+ return base64Write(this, string, offset, length)
+
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16le':
+ case 'utf-16le':
+ return ucs2Write(this, string, offset, length)
+
+ default:
+ if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
+ encoding = ('' + encoding).toLowerCase()
+ loweredCase = true
+ }
+ }
+}
+
+Buffer.prototype.toJSON = function toJSON () {
+ return {
+ type: 'Buffer',
+ data: Array.prototype.slice.call(this._arr || this, 0)
+ }
+}
+
+function base64Slice (buf, start, end) {
+ if (start === 0 && end === buf.length) {
+ return base64.fromByteArray(buf)
+ } else {
+ return base64.fromByteArray(buf.slice(start, end))
+ }
+}
+
+function utf8Slice (buf, start, end) {
+ end = Math.min(buf.length, end)
+ var res = []
+
+ var i = start
+ while (i < end) {
+ var firstByte = buf[i]
+ var codePoint = null
+ var bytesPerSequence = (firstByte > 0xEF) ? 4
+ : (firstByte > 0xDF) ? 3
+ : (firstByte > 0xBF) ? 2
+ : 1
+
+ if (i + bytesPerSequence <= end) {
+ var secondByte, thirdByte, fourthByte, tempCodePoint
+
+ switch (bytesPerSequence) {
+ case 1:
+ if (firstByte < 0x80) {
+ codePoint = firstByte
+ }
+ break
+ case 2:
+ secondByte = buf[i + 1]
+ if ((secondByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
+ if (tempCodePoint > 0x7F) {
+ codePoint = tempCodePoint
+ }
+ }
+ break
+ case 3:
+ secondByte = buf[i + 1]
+ thirdByte = buf[i + 2]
+ if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
+ if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
+ codePoint = tempCodePoint
+ }
+ }
+ break
+ case 4:
+ secondByte = buf[i + 1]
+ thirdByte = buf[i + 2]
+ fourthByte = buf[i + 3]
+ if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
+ tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
+ if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
+ codePoint = tempCodePoint
+ }
+ }
+ }
+ }
+
+ if (codePoint === null) {
+ // we did not generate a valid codePoint so insert a
+ // replacement char (U+FFFD) and advance only 1 byte
+ codePoint = 0xFFFD
+ bytesPerSequence = 1
+ } else if (codePoint > 0xFFFF) {
+ // encode to utf16 (surrogate pair dance)
+ codePoint -= 0x10000
+ res.push(codePoint >>> 10 & 0x3FF | 0xD800)
+ codePoint = 0xDC00 | codePoint & 0x3FF
+ }
+
+ res.push(codePoint)
+ i += bytesPerSequence
+ }
+
+ return decodeCodePointsArray(res)
+}
+
+// Based on http://stackoverflow.com/a/22747272/680742, the browser with
+// the lowest limit is Chrome, with 0x10000 args.
+// We go 1 magnitude less, for safety
+var MAX_ARGUMENTS_LENGTH = 0x1000
+
+function decodeCodePointsArray (codePoints) {
+ var len = codePoints.length
+ if (len <= MAX_ARGUMENTS_LENGTH) {
+ return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
+ }
+
+ // Decode in chunks to avoid "call stack size exceeded".
+ var res = ''
+ var i = 0
+ while (i < len) {
+ res += String.fromCharCode.apply(
+ String,
+ codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
+ )
+ }
+ return res
+}
+
+function asciiSlice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; ++i) {
+ ret += String.fromCharCode(buf[i] & 0x7F)
+ }
+ return ret
+}
+
+function latin1Slice (buf, start, end) {
+ var ret = ''
+ end = Math.min(buf.length, end)
+
+ for (var i = start; i < end; ++i) {
+ ret += String.fromCharCode(buf[i])
+ }
+ return ret
+}
+
+function hexSlice (buf, start, end) {
+ var len = buf.length
+
+ if (!start || start < 0) start = 0
+ if (!end || end < 0 || end > len) end = len
+
+ var out = ''
+ for (var i = start; i < end; ++i) {
+ out += toHex(buf[i])
+ }
+ return out
+}
+
+function utf16leSlice (buf, start, end) {
+ var bytes = buf.slice(start, end)
+ var res = ''
+ for (var i = 0; i < bytes.length; i += 2) {
+ res += String.fromCharCode(bytes[i] + (bytes[i + 1] * 256))
+ }
+ return res
+}
+
+Buffer.prototype.slice = function slice (start, end) {
+ var len = this.length
+ start = ~~start
+ end = end === undefined ? len : ~~end
+
+ if (start < 0) {
+ start += len
+ if (start < 0) start = 0
+ } else if (start > len) {
+ start = len
+ }
+
+ if (end < 0) {
+ end += len
+ if (end < 0) end = 0
+ } else if (end > len) {
+ end = len
+ }
+
+ if (end < start) end = start
+
+ var newBuf = this.subarray(start, end)
+ // Return an augmented `Uint8Array` instance
+ newBuf.__proto__ = Buffer.prototype
+ return newBuf
+}
+
+/*
+ * Need to make sure that buffer isn't trying to write out of bounds.
+ */
+function checkOffset (offset, ext, length) {
+ if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
+ if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
+}
+
+Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) {
+ checkOffset(offset, byteLength, this.length)
+ }
+
+ var val = this[offset + --byteLength]
+ var mul = 1
+ while (byteLength > 0 && (mul *= 0x100)) {
+ val += this[offset + --byteLength] * mul
+ }
+
+ return val
+}
+
+Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ return this[offset]
+}
+
+Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return this[offset] | (this[offset + 1] << 8)
+}
+
+Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ return (this[offset] << 8) | this[offset + 1]
+}
+
+Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return ((this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16)) +
+ (this[offset + 3] * 0x1000000)
+}
+
+Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] * 0x1000000) +
+ ((this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ this[offset + 3])
+}
+
+Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var val = this[offset]
+ var mul = 1
+ var i = 0
+ while (++i < byteLength && (mul *= 0x100)) {
+ val += this[offset + i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) checkOffset(offset, byteLength, this.length)
+
+ var i = byteLength
+ var mul = 1
+ var val = this[offset + --i]
+ while (i > 0 && (mul *= 0x100)) {
+ val += this[offset + --i] * mul
+ }
+ mul *= 0x80
+
+ if (val >= mul) val -= Math.pow(2, 8 * byteLength)
+
+ return val
+}
+
+Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 1, this.length)
+ if (!(this[offset] & 0x80)) return (this[offset])
+ return ((0xff - this[offset] + 1) * -1)
+}
+
+Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset] | (this[offset + 1] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 2, this.length)
+ var val = this[offset + 1] | (this[offset] << 8)
+ return (val & 0x8000) ? val | 0xFFFF0000 : val
+}
+
+Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset]) |
+ (this[offset + 1] << 8) |
+ (this[offset + 2] << 16) |
+ (this[offset + 3] << 24)
+}
+
+Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+
+ return (this[offset] << 24) |
+ (this[offset + 1] << 16) |
+ (this[offset + 2] << 8) |
+ (this[offset + 3])
+}
+
+Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, true, 23, 4)
+}
+
+Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 4, this.length)
+ return ieee754.read(this, offset, false, 23, 4)
+}
+
+Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, true, 52, 8)
+}
+
+Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
+ offset = offset >>> 0
+ if (!noAssert) checkOffset(offset, 8, this.length)
+ return ieee754.read(this, offset, false, 52, 8)
+}
+
+function checkInt (buf, value, offset, ext, max, min) {
+ if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
+ if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
+ if (offset + ext > buf.length) throw new RangeError('Index out of range')
+}
+
+Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) {
+ var maxBytes = Math.pow(2, 8 * byteLength) - 1
+ checkInt(this, value, offset, byteLength, maxBytes, 0)
+ }
+
+ var mul = 1
+ var i = 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ byteLength = byteLength >>> 0
+ if (!noAssert) {
+ var maxBytes = Math.pow(2, 8 * byteLength) - 1
+ checkInt(this, value, offset, byteLength, maxBytes, 0)
+ }
+
+ var i = byteLength - 1
+ var mul = 1
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ this[offset + i] = (value / mul) & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
+ this[offset] = (value & 0xff)
+ return offset + 1
+}
+
+Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ return offset + 2
+}
+
+Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
+ this[offset] = (value >>> 8)
+ this[offset + 1] = (value & 0xff)
+ return offset + 2
+}
+
+Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ this[offset + 3] = (value >>> 24)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 1] = (value >>> 8)
+ this[offset] = (value & 0xff)
+ return offset + 4
+}
+
+Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = (value & 0xff)
+ return offset + 4
+}
+
+Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ var limit = Math.pow(2, (8 * byteLength) - 1)
+
+ checkInt(this, value, offset, byteLength, limit - 1, -limit)
+ }
+
+ var i = 0
+ var mul = 1
+ var sub = 0
+ this[offset] = value & 0xFF
+ while (++i < byteLength && (mul *= 0x100)) {
+ if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
+ sub = 1
+ }
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ var limit = Math.pow(2, (8 * byteLength) - 1)
+
+ checkInt(this, value, offset, byteLength, limit - 1, -limit)
+ }
+
+ var i = byteLength - 1
+ var mul = 1
+ var sub = 0
+ this[offset + i] = value & 0xFF
+ while (--i >= 0 && (mul *= 0x100)) {
+ if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
+ sub = 1
+ }
+ this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
+ }
+
+ return offset + byteLength
+}
+
+Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
+ if (value < 0) value = 0xff + value + 1
+ this[offset] = (value & 0xff)
+ return offset + 1
+}
+
+Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ return offset + 2
+}
+
+Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
+ this[offset] = (value >>> 8)
+ this[offset + 1] = (value & 0xff)
+ return offset + 2
+}
+
+Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ this[offset] = (value & 0xff)
+ this[offset + 1] = (value >>> 8)
+ this[offset + 2] = (value >>> 16)
+ this[offset + 3] = (value >>> 24)
+ return offset + 4
+}
+
+Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
+ if (value < 0) value = 0xffffffff + value + 1
+ this[offset] = (value >>> 24)
+ this[offset + 1] = (value >>> 16)
+ this[offset + 2] = (value >>> 8)
+ this[offset + 3] = (value & 0xff)
+ return offset + 4
+}
+
+function checkIEEE754 (buf, value, offset, ext, max, min) {
+ if (offset + ext > buf.length) throw new RangeError('Index out of range')
+ if (offset < 0) throw new RangeError('Index out of range')
+}
+
+function writeFloat (buf, value, offset, littleEndian, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 23, 4)
+ return offset + 4
+}
+
+Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
+ return writeFloat(this, value, offset, false, noAssert)
+}
+
+function writeDouble (buf, value, offset, littleEndian, noAssert) {
+ value = +value
+ offset = offset >>> 0
+ if (!noAssert) {
+ checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
+ }
+ ieee754.write(buf, value, offset, littleEndian, 52, 8)
+ return offset + 8
+}
+
+Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, true, noAssert)
+}
+
+Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
+ return writeDouble(this, value, offset, false, noAssert)
+}
+
+// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
+Buffer.prototype.copy = function copy (target, targetStart, start, end) {
+ if (!Buffer.isBuffer(target)) throw new TypeError('argument should be a Buffer')
+ if (!start) start = 0
+ if (!end && end !== 0) end = this.length
+ if (targetStart >= target.length) targetStart = target.length
+ if (!targetStart) targetStart = 0
+ if (end > 0 && end < start) end = start
+
+ // Copy 0 bytes; we're done
+ if (end === start) return 0
+ if (target.length === 0 || this.length === 0) return 0
+
+ // Fatal error conditions
+ if (targetStart < 0) {
+ throw new RangeError('targetStart out of bounds')
+ }
+ if (start < 0 || start >= this.length) throw new RangeError('Index out of range')
+ if (end < 0) throw new RangeError('sourceEnd out of bounds')
+
+ // Are we oob?
+ if (end > this.length) end = this.length
+ if (target.length - targetStart < end - start) {
+ end = target.length - targetStart + start
+ }
+
+ var len = end - start
+
+ if (this === target && typeof Uint8Array.prototype.copyWithin === 'function') {
+ // Use built-in when available, missing from IE11
+ this.copyWithin(targetStart, start, end)
+ } else if (this === target && start < targetStart && targetStart < end) {
+ // descending copy from end
+ for (var i = len - 1; i >= 0; --i) {
+ target[i + targetStart] = this[i + start]
+ }
+ } else {
+ Uint8Array.prototype.set.call(
+ target,
+ this.subarray(start, end),
+ targetStart
+ )
+ }
+
+ return len
+}
+
+// Usage:
+// buffer.fill(number[, offset[, end]])
+// buffer.fill(buffer[, offset[, end]])
+// buffer.fill(string[, offset[, end]][, encoding])
+Buffer.prototype.fill = function fill (val, start, end, encoding) {
+ // Handle string cases:
+ if (typeof val === 'string') {
+ if (typeof start === 'string') {
+ encoding = start
+ start = 0
+ end = this.length
+ } else if (typeof end === 'string') {
+ encoding = end
+ end = this.length
+ }
+ if (encoding !== undefined && typeof encoding !== 'string') {
+ throw new TypeError('encoding must be a string')
+ }
+ if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
+ throw new TypeError('Unknown encoding: ' + encoding)
+ }
+ if (val.length === 1) {
+ var code = val.charCodeAt(0)
+ if ((encoding === 'utf8' && code < 128) ||
+ encoding === 'latin1') {
+ // Fast path: If `val` fits into a single byte, use that numeric value.
+ val = code
+ }
+ }
+ } else if (typeof val === 'number') {
+ val = val & 255
+ }
+
+ // Invalid ranges are not set to a default, so can range check early.
+ if (start < 0 || this.length < start || this.length < end) {
+ throw new RangeError('Out of range index')
+ }
+
+ if (end <= start) {
+ return this
+ }
+
+ start = start >>> 0
+ end = end === undefined ? this.length : end >>> 0
+
+ if (!val) val = 0
+
+ var i
+ if (typeof val === 'number') {
+ for (i = start; i < end; ++i) {
+ this[i] = val
+ }
+ } else {
+ var bytes = Buffer.isBuffer(val)
+ ? val
+ : Buffer.from(val, encoding)
+ var len = bytes.length
+ if (len === 0) {
+ throw new TypeError('The value "' + val +
+ '" is invalid for argument "value"')
+ }
+ for (i = 0; i < end - start; ++i) {
+ this[i + start] = bytes[i % len]
+ }
+ }
+
+ return this
+}
+
+// HELPER FUNCTIONS
+// ================
+
+var INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g
+
+function base64clean (str) {
+ // Node takes equal signs as end of the Base64 encoding
+ str = str.split('=')[0]
+ // Node strips out invalid characters like \n and \t from the string, base64-js does not
+ str = str.trim().replace(INVALID_BASE64_RE, '')
+ // Node converts strings with length < 2 to ''
+ if (str.length < 2) return ''
+ // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
+ while (str.length % 4 !== 0) {
+ str = str + '='
+ }
+ return str
+}
+
+function toHex (n) {
+ if (n < 16) return '0' + n.toString(16)
+ return n.toString(16)
+}
+
+function utf8ToBytes (string, units) {
+ units = units || Infinity
+ var codePoint
+ var length = string.length
+ var leadSurrogate = null
+ var bytes = []
+
+ for (var i = 0; i < length; ++i) {
+ codePoint = string.charCodeAt(i)
+
+ // is surrogate component
+ if (codePoint > 0xD7FF && codePoint < 0xE000) {
+ // last char was a lead
+ if (!leadSurrogate) {
+ // no lead yet
+ if (codePoint > 0xDBFF) {
+ // unexpected trail
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ } else if (i + 1 === length) {
+ // unpaired lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ continue
+ }
+
+ // valid lead
+ leadSurrogate = codePoint
+
+ continue
+ }
+
+ // 2 leads in a row
+ if (codePoint < 0xDC00) {
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ leadSurrogate = codePoint
+ continue
+ }
+
+ // valid surrogate pair
+ codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
+ } else if (leadSurrogate) {
+ // valid bmp char, but last char was a lead
+ if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
+ }
+
+ leadSurrogate = null
+
+ // encode utf8
+ if (codePoint < 0x80) {
+ if ((units -= 1) < 0) break
+ bytes.push(codePoint)
+ } else if (codePoint < 0x800) {
+ if ((units -= 2) < 0) break
+ bytes.push(
+ codePoint >> 0x6 | 0xC0,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x10000) {
+ if ((units -= 3) < 0) break
+ bytes.push(
+ codePoint >> 0xC | 0xE0,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else if (codePoint < 0x110000) {
+ if ((units -= 4) < 0) break
+ bytes.push(
+ codePoint >> 0x12 | 0xF0,
+ codePoint >> 0xC & 0x3F | 0x80,
+ codePoint >> 0x6 & 0x3F | 0x80,
+ codePoint & 0x3F | 0x80
+ )
+ } else {
+ throw new Error('Invalid code point')
+ }
+ }
+
+ return bytes
+}
+
+function asciiToBytes (str) {
+ var byteArray = []
+ for (var i = 0; i < str.length; ++i) {
+ // Node's code seems to be doing this and not & 0x7F..
+ byteArray.push(str.charCodeAt(i) & 0xFF)
+ }
+ return byteArray
+}
+
+function utf16leToBytes (str, units) {
+ var c, hi, lo
+ var byteArray = []
+ for (var i = 0; i < str.length; ++i) {
+ if ((units -= 2) < 0) break
+
+ c = str.charCodeAt(i)
+ hi = c >> 8
+ lo = c % 256
+ byteArray.push(lo)
+ byteArray.push(hi)
+ }
+
+ return byteArray
+}
+
+function base64ToBytes (str) {
+ return base64.toByteArray(base64clean(str))
+}
+
+function blitBuffer (src, dst, offset, length) {
+ for (var i = 0; i < length; ++i) {
+ if ((i + offset >= dst.length) || (i >= src.length)) break
+ dst[i + offset] = src[i]
+ }
+ return i
+}
+
+// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass
+// the `instanceof` check but they should be treated as of that type.
+// See: https://github.com/feross/buffer/issues/166
+function isInstance (obj, type) {
+ return obj instanceof type ||
+ (obj != null && obj.constructor != null && obj.constructor.name != null &&
+ obj.constructor.name === type.name)
+}
+function numberIsNaN (obj) {
+ // For IE11 support
+ return obj !== obj // eslint-disable-line no-self-compare
+}
+
+}).call(this)}).call(this,require("buffer").Buffer)
+},{"base64-js":10,"buffer":12,"ieee754":13}],13:[function(require,module,exports){
+/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
+exports.read = function (buffer, offset, isLE, mLen, nBytes) {
+ var e, m
+ var eLen = (nBytes * 8) - mLen - 1
+ var eMax = (1 << eLen) - 1
+ var eBias = eMax >> 1
+ var nBits = -7
+ var i = isLE ? (nBytes - 1) : 0
+ var d = isLE ? -1 : 1
+ var s = buffer[offset + i]
+
+ i += d
+
+ e = s & ((1 << (-nBits)) - 1)
+ s >>= (-nBits)
+ nBits += eLen
+ for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
+
+ m = e & ((1 << (-nBits)) - 1)
+ e >>= (-nBits)
+ nBits += mLen
+ for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
+
+ if (e === 0) {
+ e = 1 - eBias
+ } else if (e === eMax) {
+ return m ? NaN : ((s ? -1 : 1) * Infinity)
+ } else {
+ m = m + Math.pow(2, mLen)
+ e = e - eBias
+ }
+ return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
+}
+
+exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
+ var e, m, c
+ var eLen = (nBytes * 8) - mLen - 1
+ var eMax = (1 << eLen) - 1
+ var eBias = eMax >> 1
+ var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
+ var i = isLE ? 0 : (nBytes - 1)
+ var d = isLE ? 1 : -1
+ var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
+
+ value = Math.abs(value)
+
+ if (isNaN(value) || value === Infinity) {
+ m = isNaN(value) ? 1 : 0
+ e = eMax
+ } else {
+ e = Math.floor(Math.log(value) / Math.LN2)
+ if (value * (c = Math.pow(2, -e)) < 1) {
+ e--
+ c *= 2
+ }
+ if (e + eBias >= 1) {
+ value += rt / c
+ } else {
+ value += rt * Math.pow(2, 1 - eBias)
+ }
+ if (value * c >= 2) {
+ e++
+ c /= 2
+ }
+
+ if (e + eBias >= eMax) {
+ m = 0
+ e = eMax
+ } else if (e + eBias >= 1) {
+ m = ((value * c) - 1) * Math.pow(2, mLen)
+ e = e + eBias
+ } else {
+ m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
+ e = 0
+ }
+ }
+
+ for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
+
+ e = (e << mLen) | m
+ eLen += mLen
+ for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
+
+ buffer[offset + i - d] |= s * 128
+}
+
+},{}],14:[function(require,module,exports){
+(function (global){(function (){
+/**
+ * lodash (Custom Build) <https://lodash.com/>
+ * Build: `lodash modularize exports="npm" -o ./`
+ * Copyright jQuery Foundation and other contributors <https://jquery.org/>
+ * Released under MIT license <https://lodash.com/license>
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */
+
+/** Used as the size to enable large array optimizations. */
+var LARGE_ARRAY_SIZE = 200;
+
+/** Used as the `TypeError` message for "Functions" methods. */
+var FUNC_ERROR_TEXT = 'Expected a function';
+
+/** Used to stand-in for `undefined` hash values. */
+var HASH_UNDEFINED = '__lodash_hash_undefined__';
+
+/** Used to compose bitmasks for comparison styles. */
+var UNORDERED_COMPARE_FLAG = 1,
+ PARTIAL_COMPARE_FLAG = 2;
+
+/** Used as references for various `Number` constants. */
+var INFINITY = 1 / 0,
+ MAX_SAFE_INTEGER = 9007199254740991;
+
+/** `Object#toString` result references. */
+var argsTag = '[object Arguments]',
+ arrayTag = '[object Array]',
+ boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ errorTag = '[object Error]',
+ funcTag = '[object Function]',
+ genTag = '[object GeneratorFunction]',
+ mapTag = '[object Map]',
+ numberTag = '[object Number]',
+ objectTag = '[object Object]',
+ promiseTag = '[object Promise]',
+ regexpTag = '[object RegExp]',
+ setTag = '[object Set]',
+ stringTag = '[object String]',
+ symbolTag = '[object Symbol]',
+ weakMapTag = '[object WeakMap]';
+
+var arrayBufferTag = '[object ArrayBuffer]',
+ dataViewTag = '[object DataView]',
+ float32Tag = '[object Float32Array]',
+ float64Tag = '[object Float64Array]',
+ int8Tag = '[object Int8Array]',
+ int16Tag = '[object Int16Array]',
+ int32Tag = '[object Int32Array]',
+ uint8Tag = '[object Uint8Array]',
+ uint8ClampedTag = '[object Uint8ClampedArray]',
+ uint16Tag = '[object Uint16Array]',
+ uint32Tag = '[object Uint32Array]';
+
+/** Used to match property names within property paths. */
+var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
+ reIsPlainProp = /^\w*$/,
+ reLeadingDot = /^\./,
+ rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
+
+/**
+ * Used to match `RegExp`
+ * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+ */
+var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
+
+/** Used to match backslashes in property paths. */
+var reEscapeChar = /\\(\\)?/g;
+
+/** Used to detect host constructors (Safari). */
+var reIsHostCtor = /^\[object .+?Constructor\]$/;
+
+/** Used to detect unsigned integer values. */
+var reIsUint = /^(?:0|[1-9]\d*)$/;
+
+/** Used to identify `toStringTag` values of typed arrays. */
+var typedArrayTags = {};
+typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+typedArrayTags[uint32Tag] = true;
+typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
+typedArrayTags[errorTag] = typedArrayTags[funcTag] =
+typedArrayTags[mapTag] = typedArrayTags[numberTag] =
+typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
+typedArrayTags[setTag] = typedArrayTags[stringTag] =
+typedArrayTags[weakMapTag] = false;
+
+/** Detect free variable `global` from Node.js. */
+var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+
+/** Detect free variable `self`. */
+var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+/** Used as a reference to the global object. */
+var root = freeGlobal || freeSelf || Function('return this')();
+
+/** Detect free variable `exports`. */
+var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+
+/** Detect free variable `module`. */
+var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+
+/** Detect the popular CommonJS extension `module.exports`. */
+var moduleExports = freeModule && freeModule.exports === freeExports;
+
+/** Detect free variable `process` from Node.js. */
+var freeProcess = moduleExports && freeGlobal.process;
+
+/** Used to access faster Node.js helpers. */
+var nodeUtil = (function() {
+ try {
+ return freeProcess && freeProcess.binding('util');
+ } catch (e) {}
+}());
+
+/* Node.js helper references. */
+var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
+
+/**
+ * A faster alternative to `Function#apply`, this function invokes `func`
+ * with the `this` binding of `thisArg` and the arguments of `args`.
+ *
+ * @private
+ * @param {Function} func The function to invoke.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} args The arguments to invoke `func` with.
+ * @returns {*} Returns the result of `func`.
+ */
+function apply(func, thisArg, args) {
+ switch (args.length) {
+ case 0: return func.call(thisArg);
+ case 1: return func.call(thisArg, args[0]);
+ case 2: return func.call(thisArg, args[0], args[1]);
+ case 3: return func.call(thisArg, args[0], args[1], args[2]);
+ }
+ return func.apply(thisArg, args);
+}
+
+/**
+ * A specialized version of `_.map` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+function arrayMap(array, iteratee) {
+ var index = -1,
+ length = array ? array.length : 0,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iteratee(array[index], index, array);
+ }
+ return result;
+}
+
+/**
+ * Appends the elements of `values` to `array`.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to append.
+ * @returns {Array} Returns `array`.
+ */
+function arrayPush(array, values) {
+ var index = -1,
+ length = values.length,
+ offset = array.length;
+
+ while (++index < length) {
+ array[offset + index] = values[index];
+ }
+ return array;
+}
+
+/**
+ * A specialized version of `_.some` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ */
+function arraySome(array, predicate) {
+ var index = -1,
+ length = array ? array.length : 0;
+
+ while (++index < length) {
+ if (predicate(array[index], index, array)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * The base implementation of `_.property` without support for deep paths.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @returns {Function} Returns the new accessor function.
+ */
+function baseProperty(key) {
+ return function(object) {
+ return object == null ? undefined : object[key];
+ };
+}
+
+/**
+ * The base implementation of `_.sortBy` which uses `comparer` to define the
+ * sort order of `array` and replaces criteria objects with their corresponding
+ * values.
+ *
+ * @private
+ * @param {Array} array The array to sort.
+ * @param {Function} comparer The function to define sort order.
+ * @returns {Array} Returns `array`.
+ */
+function baseSortBy(array, comparer) {
+ var length = array.length;
+
+ array.sort(comparer);
+ while (length--) {
+ array[length] = array[length].value;
+ }
+ return array;
+}
+
+/**
+ * The base implementation of `_.times` without support for iteratee shorthands
+ * or max array length checks.
+ *
+ * @private
+ * @param {number} n The number of times to invoke `iteratee`.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the array of results.
+ */
+function baseTimes(n, iteratee) {
+ var index = -1,
+ result = Array(n);
+
+ while (++index < n) {
+ result[index] = iteratee(index);
+ }
+ return result;
+}
+
+/**
+ * The base implementation of `_.unary` without support for storing metadata.
+ *
+ * @private
+ * @param {Function} func The function to cap arguments for.
+ * @returns {Function} Returns the new capped function.
+ */
+function baseUnary(func) {
+ return function(value) {
+ return func(value);
+ };
+}
+
+/**
+ * Gets the value at `key` of `object`.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {string} key The key of the property to get.
+ * @returns {*} Returns the property value.
+ */
+function getValue(object, key) {
+ return object == null ? undefined : object[key];
+}
+
+/**
+ * Checks if `value` is a host object in IE < 9.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
+ */
+function isHostObject(value) {
+ // Many host objects are `Object` objects that can coerce to strings
+ // despite having improperly defined `toString` methods.
+ var result = false;
+ if (value != null && typeof value.toString != 'function') {
+ try {
+ result = !!(value + '');
+ } catch (e) {}
+ }
+ return result;
+}
+
+/**
+ * Converts `map` to its key-value pairs.
+ *
+ * @private
+ * @param {Object} map The map to convert.
+ * @returns {Array} Returns the key-value pairs.
+ */
+function mapToArray(map) {
+ var index = -1,
+ result = Array(map.size);
+
+ map.forEach(function(value, key) {
+ result[++index] = [key, value];
+ });
+ return result;
+}
+
+/**
+ * Creates a unary function that invokes `func` with its argument transformed.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {Function} transform The argument transform.
+ * @returns {Function} Returns the new function.
+ */
+function overArg(func, transform) {
+ return function(arg) {
+ return func(transform(arg));
+ };
+}
+
+/**
+ * Converts `set` to an array of its values.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the values.
+ */
+function setToArray(set) {
+ var index = -1,
+ result = Array(set.size);
+
+ set.forEach(function(value) {
+ result[++index] = value;
+ });
+ return result;
+}
+
+/** Used for built-in method references. */
+var arrayProto = Array.prototype,
+ funcProto = Function.prototype,
+ objectProto = Object.prototype;
+
+/** Used to detect overreaching core-js shims. */
+var coreJsData = root['__core-js_shared__'];
+
+/** Used to detect methods masquerading as native. */
+var maskSrcKey = (function() {
+ var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
+ return uid ? ('Symbol(src)_1.' + uid) : '';
+}());
+
+/** Used to resolve the decompiled source of functions. */
+var funcToString = funcProto.toString;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var objectToString = objectProto.toString;
+
+/** Used to detect if a method is native. */
+var reIsNative = RegExp('^' +
+ funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
+ .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+);
+
+/** Built-in value references. */
+var Symbol = root.Symbol,
+ Uint8Array = root.Uint8Array,
+ propertyIsEnumerable = objectProto.propertyIsEnumerable,
+ splice = arrayProto.splice,
+ spreadableSymbol = Symbol ? Symbol.isConcatSpreadable : undefined;
+
+/* Built-in method references for those with the same name as other `lodash` methods. */
+var nativeKeys = overArg(Object.keys, Object),
+ nativeMax = Math.max;
+
+/* Built-in method references that are verified to be native. */
+var DataView = getNative(root, 'DataView'),
+ Map = getNative(root, 'Map'),
+ Promise = getNative(root, 'Promise'),
+ Set = getNative(root, 'Set'),
+ WeakMap = getNative(root, 'WeakMap'),
+ nativeCreate = getNative(Object, 'create');
+
+/** Used to detect maps, sets, and weakmaps. */
+var dataViewCtorString = toSource(DataView),
+ mapCtorString = toSource(Map),
+ promiseCtorString = toSource(Promise),
+ setCtorString = toSource(Set),
+ weakMapCtorString = toSource(WeakMap);
+
+/** Used to convert symbols to primitives and strings. */
+var symbolProto = Symbol ? Symbol.prototype : undefined,
+ symbolValueOf = symbolProto ? symbolProto.valueOf : undefined,
+ symbolToString = symbolProto ? symbolProto.toString : undefined;
+
+/**
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function Hash(entries) {
+ var index = -1,
+ length = entries ? entries.length : 0;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+}
+
+/**
+ * Removes all key-value entries from the hash.
+ *
+ * @private
+ * @name clear
+ * @memberOf Hash
+ */
+function hashClear() {
+ this.__data__ = nativeCreate ? nativeCreate(null) : {};
+}
+
+/**
+ * Removes `key` and its value from the hash.
+ *
+ * @private
+ * @name delete
+ * @memberOf Hash
+ * @param {Object} hash The hash to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function hashDelete(key) {
+ return this.has(key) && delete this.__data__[key];
+}
+
+/**
+ * Gets the hash value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Hash
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function hashGet(key) {
+ var data = this.__data__;
+ if (nativeCreate) {
+ var result = data[key];
+ return result === HASH_UNDEFINED ? undefined : result;
+ }
+ return hasOwnProperty.call(data, key) ? data[key] : undefined;
+}
+
+/**
+ * Checks if a hash value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Hash
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function hashHas(key) {
+ var data = this.__data__;
+ return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
+}
+
+/**
+ * Sets the hash `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Hash
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the hash instance.
+ */
+function hashSet(key, value) {
+ var data = this.__data__;
+ data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
+ return this;
+}
+
+// Add methods to `Hash`.
+Hash.prototype.clear = hashClear;
+Hash.prototype['delete'] = hashDelete;
+Hash.prototype.get = hashGet;
+Hash.prototype.has = hashHas;
+Hash.prototype.set = hashSet;
+
+/**
+ * Creates an list cache object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function ListCache(entries) {
+ var index = -1,
+ length = entries ? entries.length : 0;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+}
+
+/**
+ * Removes all key-value entries from the list cache.
+ *
+ * @private
+ * @name clear
+ * @memberOf ListCache
+ */
+function listCacheClear() {
+ this.__data__ = [];
+}
+
+/**
+ * Removes `key` and its value from the list cache.
+ *
+ * @private
+ * @name delete
+ * @memberOf ListCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function listCacheDelete(key) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ if (index < 0) {
+ return false;
+ }
+ var lastIndex = data.length - 1;
+ if (index == lastIndex) {
+ data.pop();
+ } else {
+ splice.call(data, index, 1);
+ }
+ return true;
+}
+
+/**
+ * Gets the list cache value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf ListCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function listCacheGet(key) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ return index < 0 ? undefined : data[index][1];
+}
+
+/**
+ * Checks if a list cache value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf ListCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function listCacheHas(key) {
+ return assocIndexOf(this.__data__, key) > -1;
+}
+
+/**
+ * Sets the list cache `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf ListCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the list cache instance.
+ */
+function listCacheSet(key, value) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ if (index < 0) {
+ data.push([key, value]);
+ } else {
+ data[index][1] = value;
+ }
+ return this;
+}
+
+// Add methods to `ListCache`.
+ListCache.prototype.clear = listCacheClear;
+ListCache.prototype['delete'] = listCacheDelete;
+ListCache.prototype.get = listCacheGet;
+ListCache.prototype.has = listCacheHas;
+ListCache.prototype.set = listCacheSet;
+
+/**
+ * Creates a map cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function MapCache(entries) {
+ var index = -1,
+ length = entries ? entries.length : 0;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+}
+
+/**
+ * Removes all key-value entries from the map.
+ *
+ * @private
+ * @name clear
+ * @memberOf MapCache
+ */
+function mapCacheClear() {
+ this.__data__ = {
+ 'hash': new Hash,
+ 'map': new (Map || ListCache),
+ 'string': new Hash
+ };
+}
+
+/**
+ * Removes `key` and its value from the map.
+ *
+ * @private
+ * @name delete
+ * @memberOf MapCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function mapCacheDelete(key) {
+ return getMapData(this, key)['delete'](key);
+}
+
+/**
+ * Gets the map value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf MapCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function mapCacheGet(key) {
+ return getMapData(this, key).get(key);
+}
+
+/**
+ * Checks if a map value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf MapCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function mapCacheHas(key) {
+ return getMapData(this, key).has(key);
+}
+
+/**
+ * Sets the map `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf MapCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the map cache instance.
+ */
+function mapCacheSet(key, value) {
+ getMapData(this, key).set(key, value);
+ return this;
+}
+
+// Add methods to `MapCache`.
+MapCache.prototype.clear = mapCacheClear;
+MapCache.prototype['delete'] = mapCacheDelete;
+MapCache.prototype.get = mapCacheGet;
+MapCache.prototype.has = mapCacheHas;
+MapCache.prototype.set = mapCacheSet;
+
+/**
+ *
+ * Creates an array cache object to store unique values.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [values] The values to cache.
+ */
+function SetCache(values) {
+ var index = -1,
+ length = values ? values.length : 0;
+
+ this.__data__ = new MapCache;
+ while (++index < length) {
+ this.add(values[index]);
+ }
+}
+
+/**
+ * Adds `value` to the array cache.
+ *
+ * @private
+ * @name add
+ * @memberOf SetCache
+ * @alias push
+ * @param {*} value The value to cache.
+ * @returns {Object} Returns the cache instance.
+ */
+function setCacheAdd(value) {
+ this.__data__.set(value, HASH_UNDEFINED);
+ return this;
+}
+
+/**
+ * Checks if `value` is in the array cache.
+ *
+ * @private
+ * @name has
+ * @memberOf SetCache
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `true` if `value` is found, else `false`.
+ */
+function setCacheHas(value) {
+ return this.__data__.has(value);
+}
+
+// Add methods to `SetCache`.
+SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
+SetCache.prototype.has = setCacheHas;
+
+/**
+ * Creates a stack cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function Stack(entries) {
+ this.__data__ = new ListCache(entries);
+}
+
+/**
+ * Removes all key-value entries from the stack.
+ *
+ * @private
+ * @name clear
+ * @memberOf Stack
+ */
+function stackClear() {
+ this.__data__ = new ListCache;
+}
+
+/**
+ * Removes `key` and its value from the stack.
+ *
+ * @private
+ * @name delete
+ * @memberOf Stack
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function stackDelete(key) {
+ return this.__data__['delete'](key);
+}
+
+/**
+ * Gets the stack value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Stack
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function stackGet(key) {
+ return this.__data__.get(key);
+}
+
+/**
+ * Checks if a stack value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Stack
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function stackHas(key) {
+ return this.__data__.has(key);
+}
+
+/**
+ * Sets the stack `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Stack
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the stack cache instance.
+ */
+function stackSet(key, value) {
+ var cache = this.__data__;
+ if (cache instanceof ListCache) {
+ var pairs = cache.__data__;
+ if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
+ pairs.push([key, value]);
+ return this;
+ }
+ cache = this.__data__ = new MapCache(pairs);
+ }
+ cache.set(key, value);
+ return this;
+}
+
+// Add methods to `Stack`.
+Stack.prototype.clear = stackClear;
+Stack.prototype['delete'] = stackDelete;
+Stack.prototype.get = stackGet;
+Stack.prototype.has = stackHas;
+Stack.prototype.set = stackSet;
+
+/**
+ * Creates an array of the enumerable property names of the array-like `value`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @param {boolean} inherited Specify returning inherited property names.
+ * @returns {Array} Returns the array of property names.
+ */
+function arrayLikeKeys(value, inherited) {
+ // Safari 8.1 makes `arguments.callee` enumerable in strict mode.
+ // Safari 9 makes `arguments.length` enumerable in strict mode.
+ var result = (isArray(value) || isArguments(value))
+ ? baseTimes(value.length, String)
+ : [];
+
+ var length = result.length,
+ skipIndexes = !!length;
+
+ for (var key in value) {
+ if ((inherited || hasOwnProperty.call(value, key)) &&
+ !(skipIndexes && (key == 'length' || isIndex(key, length)))) {
+ result.push(key);
+ }
+ }
+ return result;
+}
+
+/**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function assocIndexOf(array, key) {
+ var length = array.length;
+ while (length--) {
+ if (eq(array[length][0], key)) {
+ return length;
+ }
+ }
+ return -1;
+}
+
+/**
+ * The base implementation of `_.forEach` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ */
+var baseEach = createBaseEach(baseForOwn);
+
+/**
+ * The base implementation of `_.flatten` with support for restricting flattening.
+ *
+ * @private
+ * @param {Array} array The array to flatten.
+ * @param {number} depth The maximum recursion depth.
+ * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
+ * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
+ * @param {Array} [result=[]] The initial result value.
+ * @returns {Array} Returns the new flattened array.
+ */
+function baseFlatten(array, depth, predicate, isStrict, result) {
+ var index = -1,
+ length = array.length;
+
+ predicate || (predicate = isFlattenable);
+ result || (result = []);
+
+ while (++index < length) {
+ var value = array[index];
+ if (depth > 0 && predicate(value)) {
+ if (depth > 1) {
+ // Recursively flatten arrays (susceptible to call stack limits).
+ baseFlatten(value, depth - 1, predicate, isStrict, result);
+ } else {
+ arrayPush(result, value);
+ }
+ } else if (!isStrict) {
+ result[result.length] = value;
+ }
+ }
+ return result;
+}
+
+/**
+ * The base implementation of `baseForOwn` which iterates over `object`
+ * properties returned by `keysFunc` and invokes `iteratee` for each property.
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
+ */
+var baseFor = createBaseFor();
+
+/**
+ * The base implementation of `_.forOwn` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+function baseForOwn(object, iteratee) {
+ return object && baseFor(object, iteratee, keys);
+}
+
+/**
+ * The base implementation of `_.get` without support for default values.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to get.
+ * @returns {*} Returns the resolved value.
+ */
+function baseGet(object, path) {
+ path = isKey(path, object) ? [path] : castPath(path);
+
+ var index = 0,
+ length = path.length;
+
+ while (object != null && index < length) {
+ object = object[toKey(path[index++])];
+ }
+ return (index && index == length) ? object : undefined;
+}
+
+/**
+ * The base implementation of `getTag`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+function baseGetTag(value) {
+ return objectToString.call(value);
+}
+
+/**
+ * The base implementation of `_.hasIn` without support for deep paths.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {Array|string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
+ */
+function baseHasIn(object, key) {
+ return object != null && key in Object(object);
+}
+
+/**
+ * The base implementation of `_.isEqual` which supports partial comparisons
+ * and tracks traversed objects.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @param {boolean} [bitmask] The bitmask of comparison flags.
+ * The bitmask may be composed of the following flags:
+ * 1 - Unordered comparison
+ * 2 - Partial comparison
+ * @param {Object} [stack] Tracks traversed `value` and `other` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+function baseIsEqual(value, other, customizer, bitmask, stack) {
+ if (value === other) {
+ return true;
+ }
+ if (value == null || other == null || (!isObject(value) && !isObjectLike(other))) {
+ return value !== value && other !== other;
+ }
+ return baseIsEqualDeep(value, other, baseIsEqual, customizer, bitmask, stack);
+}
+
+/**
+ * A specialized version of `baseIsEqual` for arrays and objects which performs
+ * deep comparisons and tracks traversed objects enabling objects with circular
+ * references to be compared.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @param {number} [bitmask] The bitmask of comparison flags. See `baseIsEqual`
+ * for more details.
+ * @param {Object} [stack] Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+function baseIsEqualDeep(object, other, equalFunc, customizer, bitmask, stack) {
+ var objIsArr = isArray(object),
+ othIsArr = isArray(other),
+ objTag = arrayTag,
+ othTag = arrayTag;
+
+ if (!objIsArr) {
+ objTag = getTag(object);
+ objTag = objTag == argsTag ? objectTag : objTag;
+ }
+ if (!othIsArr) {
+ othTag = getTag(other);
+ othTag = othTag == argsTag ? objectTag : othTag;
+ }
+ var objIsObj = objTag == objectTag && !isHostObject(object),
+ othIsObj = othTag == objectTag && !isHostObject(other),
+ isSameTag = objTag == othTag;
+
+ if (isSameTag && !objIsObj) {
+ stack || (stack = new Stack);
+ return (objIsArr || isTypedArray(object))
+ ? equalArrays(object, other, equalFunc, customizer, bitmask, stack)
+ : equalByTag(object, other, objTag, equalFunc, customizer, bitmask, stack);
+ }
+ if (!(bitmask & PARTIAL_COMPARE_FLAG)) {
+ var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+ othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+ if (objIsWrapped || othIsWrapped) {
+ var objUnwrapped = objIsWrapped ? object.value() : object,
+ othUnwrapped = othIsWrapped ? other.value() : other;
+
+ stack || (stack = new Stack);
+ return equalFunc(objUnwrapped, othUnwrapped, customizer, bitmask, stack);
+ }
+ }
+ if (!isSameTag) {
+ return false;
+ }
+ stack || (stack = new Stack);
+ return equalObjects(object, other, equalFunc, customizer, bitmask, stack);
+}
+
+/**
+ * The base implementation of `_.isMatch` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @param {Array} matchData The property names, values, and compare flags to match.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ */
+function baseIsMatch(object, source, matchData, customizer) {
+ var index = matchData.length,
+ length = index,
+ noCustomizer = !customizer;
+
+ if (object == null) {
+ return !length;
+ }
+ object = Object(object);
+ while (index--) {
+ var data = matchData[index];
+ if ((noCustomizer && data[2])
+ ? data[1] !== object[data[0]]
+ : !(data[0] in object)
+ ) {
+ return false;
+ }
+ }
+ while (++index < length) {
+ data = matchData[index];
+ var key = data[0],
+ objValue = object[key],
+ srcValue = data[1];
+
+ if (noCustomizer && data[2]) {
+ if (objValue === undefined && !(key in object)) {
+ return false;
+ }
+ } else {
+ var stack = new Stack;
+ if (customizer) {
+ var result = customizer(objValue, srcValue, key, object, source, stack);
+ }
+ if (!(result === undefined
+ ? baseIsEqual(srcValue, objValue, customizer, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG, stack)
+ : result
+ )) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * The base implementation of `_.isNative` without bad shim checks.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a native function,
+ * else `false`.
+ */
+function baseIsNative(value) {
+ if (!isObject(value) || isMasked(value)) {
+ return false;
+ }
+ var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
+ return pattern.test(toSource(value));
+}
+
+/**
+ * The base implementation of `_.isTypedArray` without Node.js optimizations.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+ */
+function baseIsTypedArray(value) {
+ return isObjectLike(value) &&
+ isLength(value.length) && !!typedArrayTags[objectToString.call(value)];
+}
+
+/**
+ * The base implementation of `_.iteratee`.
+ *
+ * @private
+ * @param {*} [value=_.identity] The value to convert to an iteratee.
+ * @returns {Function} Returns the iteratee.
+ */
+function baseIteratee(value) {
+ // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
+ // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
+ if (typeof value == 'function') {
+ return value;
+ }
+ if (value == null) {
+ return identity;
+ }
+ if (typeof value == 'object') {
+ return isArray(value)
+ ? baseMatchesProperty(value[0], value[1])
+ : baseMatches(value);
+ }
+ return property(value);
+}
+
+/**
+ * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ */
+function baseKeys(object) {
+ if (!isPrototype(object)) {
+ return nativeKeys(object);
+ }
+ var result = [];
+ for (var key in Object(object)) {
+ if (hasOwnProperty.call(object, key) && key != 'constructor') {
+ result.push(key);
+ }
+ }
+ return result;
+}
+
+/**
+ * The base implementation of `_.map` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+function baseMap(collection, iteratee) {
+ var index = -1,
+ result = isArrayLike(collection) ? Array(collection.length) : [];
+
+ baseEach(collection, function(value, key, collection) {
+ result[++index] = iteratee(value, key, collection);
+ });
+ return result;
+}
+
+/**
+ * The base implementation of `_.matches` which doesn't clone `source`.
+ *
+ * @private
+ * @param {Object} source The object of property values to match.
+ * @returns {Function} Returns the new spec function.
+ */
+function baseMatches(source) {
+ var matchData = getMatchData(source);
+ if (matchData.length == 1 && matchData[0][2]) {
+ return matchesStrictComparable(matchData[0][0], matchData[0][1]);
+ }
+ return function(object) {
+ return object === source || baseIsMatch(object, source, matchData);
+ };
+}
+
+/**
+ * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
+ *
+ * @private
+ * @param {string} path The path of the property to get.
+ * @param {*} srcValue The value to match.
+ * @returns {Function} Returns the new spec function.
+ */
+function baseMatchesProperty(path, srcValue) {
+ if (isKey(path) && isStrictComparable(srcValue)) {
+ return matchesStrictComparable(toKey(path), srcValue);
+ }
+ return function(object) {
+ var objValue = get(object, path);
+ return (objValue === undefined && objValue === srcValue)
+ ? hasIn(object, path)
+ : baseIsEqual(srcValue, objValue, undefined, UNORDERED_COMPARE_FLAG | PARTIAL_COMPARE_FLAG);
+ };
+}
+
+/**
+ * The base implementation of `_.orderBy` without param guards.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
+ * @param {string[]} orders The sort orders of `iteratees`.
+ * @returns {Array} Returns the new sorted array.
+ */
+function baseOrderBy(collection, iteratees, orders) {
+ var index = -1;
+ iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(baseIteratee));
+
+ var result = baseMap(collection, function(value, key, collection) {
+ var criteria = arrayMap(iteratees, function(iteratee) {
+ return iteratee(value);
+ });
+ return { 'criteria': criteria, 'index': ++index, 'value': value };
+ });
+
+ return baseSortBy(result, function(object, other) {
+ return compareMultiple(object, other, orders);
+ });
+}
+
+/**
+ * A specialized version of `baseProperty` which supports deep paths.
+ *
+ * @private
+ * @param {Array|string} path The path of the property to get.
+ * @returns {Function} Returns the new accessor function.
+ */
+function basePropertyDeep(path) {
+ return function(object) {
+ return baseGet(object, path);
+ };
+}
+
+/**
+ * The base implementation of `_.rest` which doesn't validate or coerce arguments.
+ *
+ * @private
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
+ */
+function baseRest(func, start) {
+ start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
+ return function() {
+ var args = arguments,
+ index = -1,
+ length = nativeMax(args.length - start, 0),
+ array = Array(length);
+
+ while (++index < length) {
+ array[index] = args[start + index];
+ }
+ index = -1;
+ var otherArgs = Array(start + 1);
+ while (++index < start) {
+ otherArgs[index] = args[index];
+ }
+ otherArgs[start] = array;
+ return apply(func, this, otherArgs);
+ };
+}
+
+/**
+ * The base implementation of `_.toString` which doesn't convert nullish
+ * values to empty strings.
+ *
+ * @private
+ * @param {*} value The value to process.
+ * @returns {string} Returns the string.
+ */
+function baseToString(value) {
+ // Exit early for strings to avoid a performance hit in some environments.
+ if (typeof value == 'string') {
+ return value;
+ }
+ if (isSymbol(value)) {
+ return symbolToString ? symbolToString.call(value) : '';
+ }
+ var result = (value + '');
+ return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+}
+
+/**
+ * Casts `value` to a path array if it's not one.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @returns {Array} Returns the cast property path array.
+ */
+function castPath(value) {
+ return isArray(value) ? value : stringToPath(value);
+}
+
+/**
+ * Compares values to sort them in ascending order.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {number} Returns the sort order indicator for `value`.
+ */
+function compareAscending(value, other) {
+ if (value !== other) {
+ var valIsDefined = value !== undefined,
+ valIsNull = value === null,
+ valIsReflexive = value === value,
+ valIsSymbol = isSymbol(value);
+
+ var othIsDefined = other !== undefined,
+ othIsNull = other === null,
+ othIsReflexive = other === other,
+ othIsSymbol = isSymbol(other);
+
+ if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
+ (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||
+ (valIsNull && othIsDefined && othIsReflexive) ||
+ (!valIsDefined && othIsReflexive) ||
+ !valIsReflexive) {
+ return 1;
+ }
+ if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
+ (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||
+ (othIsNull && valIsDefined && valIsReflexive) ||
+ (!othIsDefined && valIsReflexive) ||
+ !othIsReflexive) {
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Used by `_.orderBy` to compare multiple properties of a value to another
+ * and stable sort them.
+ *
+ * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
+ * specify an order of "desc" for descending or "asc" for ascending sort order
+ * of corresponding values.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {boolean[]|string[]} orders The order to sort by for each property.
+ * @returns {number} Returns the sort order indicator for `object`.
+ */
+function compareMultiple(object, other, orders) {
+ var index = -1,
+ objCriteria = object.criteria,
+ othCriteria = other.criteria,
+ length = objCriteria.length,
+ ordersLength = orders.length;
+
+ while (++index < length) {
+ var result = compareAscending(objCriteria[index], othCriteria[index]);
+ if (result) {
+ if (index >= ordersLength) {
+ return result;
+ }
+ var order = orders[index];
+ return result * (order == 'desc' ? -1 : 1);
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to provide the same value for
+ // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
+ // for more details.
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
+ return object.index - other.index;
+}
+
+/**
+ * Creates a `baseEach` or `baseEachRight` function.
+ *
+ * @private
+ * @param {Function} eachFunc The function to iterate over a collection.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
+ */
+function createBaseEach(eachFunc, fromRight) {
+ return function(collection, iteratee) {
+ if (collection == null) {
+ return collection;
+ }
+ if (!isArrayLike(collection)) {
+ return eachFunc(collection, iteratee);
+ }
+ var length = collection.length,
+ index = fromRight ? length : -1,
+ iterable = Object(collection);
+
+ while ((fromRight ? index-- : ++index < length)) {
+ if (iteratee(iterable[index], index, iterable) === false) {
+ break;
+ }
+ }
+ return collection;
+ };
+}
+
+/**
+ * Creates a base function for methods like `_.forIn` and `_.forOwn`.
+ *
+ * @private
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
+ */
+function createBaseFor(fromRight) {
+ return function(object, iteratee, keysFunc) {
+ var index = -1,
+ iterable = Object(object),
+ props = keysFunc(object),
+ length = props.length;
+
+ while (length--) {
+ var key = props[fromRight ? length : ++index];
+ if (iteratee(iterable[key], key, iterable) === false) {
+ break;
+ }
+ }
+ return object;
+ };
+}
+
+/**
+ * A specialized version of `baseIsEqualDeep` for arrays with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Array} array The array to compare.
+ * @param {Array} other The other array to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
+ * for more details.
+ * @param {Object} stack Tracks traversed `array` and `other` objects.
+ * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+ */
+function equalArrays(array, other, equalFunc, customizer, bitmask, stack) {
+ var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
+ arrLength = array.length,
+ othLength = other.length;
+
+ if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
+ return false;
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(array);
+ if (stacked && stack.get(other)) {
+ return stacked == other;
+ }
+ var index = -1,
+ result = true,
+ seen = (bitmask & UNORDERED_COMPARE_FLAG) ? new SetCache : undefined;
+
+ stack.set(array, other);
+ stack.set(other, array);
+
+ // Ignore non-index properties.
+ while (++index < arrLength) {
+ var arrValue = array[index],
+ othValue = other[index];
+
+ if (customizer) {
+ var compared = isPartial
+ ? customizer(othValue, arrValue, index, other, array, stack)
+ : customizer(arrValue, othValue, index, array, other, stack);
+ }
+ if (compared !== undefined) {
+ if (compared) {
+ continue;
+ }
+ result = false;
+ break;
+ }
+ // Recursively compare arrays (susceptible to call stack limits).
+ if (seen) {
+ if (!arraySome(other, function(othValue, othIndex) {
+ if (!seen.has(othIndex) &&
+ (arrValue === othValue || equalFunc(arrValue, othValue, customizer, bitmask, stack))) {
+ return seen.add(othIndex);
+ }
+ })) {
+ result = false;
+ break;
+ }
+ } else if (!(
+ arrValue === othValue ||
+ equalFunc(arrValue, othValue, customizer, bitmask, stack)
+ )) {
+ result = false;
+ break;
+ }
+ }
+ stack['delete'](array);
+ stack['delete'](other);
+ return result;
+}
+
+/**
+ * A specialized version of `baseIsEqualDeep` for comparing objects of
+ * the same `toStringTag`.
+ *
+ * **Note:** This function only supports comparing values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {string} tag The `toStringTag` of the objects to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
+ * for more details.
+ * @param {Object} stack Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+function equalByTag(object, other, tag, equalFunc, customizer, bitmask, stack) {
+ switch (tag) {
+ case dataViewTag:
+ if ((object.byteLength != other.byteLength) ||
+ (object.byteOffset != other.byteOffset)) {
+ return false;
+ }
+ object = object.buffer;
+ other = other.buffer;
+
+ case arrayBufferTag:
+ if ((object.byteLength != other.byteLength) ||
+ !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
+ return false;
+ }
+ return true;
+
+ case boolTag:
+ case dateTag:
+ case numberTag:
+ // Coerce booleans to `1` or `0` and dates to milliseconds.
+ // Invalid dates are coerced to `NaN`.
+ return eq(+object, +other);
+
+ case errorTag:
+ return object.name == other.name && object.message == other.message;
+
+ case regexpTag:
+ case stringTag:
+ // Coerce regexes to strings and treat strings, primitives and objects,
+ // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
+ // for more details.
+ return object == (other + '');
+
+ case mapTag:
+ var convert = mapToArray;
+
+ case setTag:
+ var isPartial = bitmask & PARTIAL_COMPARE_FLAG;
+ convert || (convert = setToArray);
+
+ if (object.size != other.size && !isPartial) {
+ return false;
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(object);
+ if (stacked) {
+ return stacked == other;
+ }
+ bitmask |= UNORDERED_COMPARE_FLAG;
+
+ // Recursively compare objects (susceptible to call stack limits).
+ stack.set(object, other);
+ var result = equalArrays(convert(object), convert(other), equalFunc, customizer, bitmask, stack);
+ stack['delete'](object);
+ return result;
+
+ case symbolTag:
+ if (symbolValueOf) {
+ return symbolValueOf.call(object) == symbolValueOf.call(other);
+ }
+ }
+ return false;
+}
+
+/**
+ * A specialized version of `baseIsEqualDeep` for objects with support for
+ * partial deep comparisons.
+ *
+ * @private
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {number} bitmask The bitmask of comparison flags. See `baseIsEqual`
+ * for more details.
+ * @param {Object} stack Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ */
+function equalObjects(object, other, equalFunc, customizer, bitmask, stack) {
+ var isPartial = bitmask & PARTIAL_COMPARE_FLAG,
+ objProps = keys(object),
+ objLength = objProps.length,
+ othProps = keys(other),
+ othLength = othProps.length;
+
+ if (objLength != othLength && !isPartial) {
+ return false;
+ }
+ var index = objLength;
+ while (index--) {
+ var key = objProps[index];
+ if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
+ return false;
+ }
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(object);
+ if (stacked && stack.get(other)) {
+ return stacked == other;
+ }
+ var result = true;
+ stack.set(object, other);
+ stack.set(other, object);
+
+ var skipCtor = isPartial;
+ while (++index < objLength) {
+ key = objProps[index];
+ var objValue = object[key],
+ othValue = other[key];
+
+ if (customizer) {
+ var compared = isPartial
+ ? customizer(othValue, objValue, key, other, object, stack)
+ : customizer(objValue, othValue, key, object, other, stack);
+ }
+ // Recursively compare objects (susceptible to call stack limits).
+ if (!(compared === undefined
+ ? (objValue === othValue || equalFunc(objValue, othValue, customizer, bitmask, stack))
+ : compared
+ )) {
+ result = false;
+ break;
+ }
+ skipCtor || (skipCtor = key == 'constructor');
+ }
+ if (result && !skipCtor) {
+ var objCtor = object.constructor,
+ othCtor = other.constructor;
+
+ // Non `Object` object instances with different constructors are not equal.
+ if (objCtor != othCtor &&
+ ('constructor' in object && 'constructor' in other) &&
+ !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+ typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+ result = false;
+ }
+ }
+ stack['delete'](object);
+ stack['delete'](other);
+ return result;
+}
+
+/**
+ * Gets the data for `map`.
+ *
+ * @private
+ * @param {Object} map The map to query.
+ * @param {string} key The reference key.
+ * @returns {*} Returns the map data.
+ */
+function getMapData(map, key) {
+ var data = map.__data__;
+ return isKeyable(key)
+ ? data[typeof key == 'string' ? 'string' : 'hash']
+ : data.map;
+}
+
+/**
+ * Gets the property names, values, and compare flags of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the match data of `object`.
+ */
+function getMatchData(object) {
+ var result = keys(object),
+ length = result.length;
+
+ while (length--) {
+ var key = result[length],
+ value = object[key];
+
+ result[length] = [key, value, isStrictComparable(value)];
+ }
+ return result;
+}
+
+/**
+ * Gets the native function at `key` of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {string} key The key of the method to get.
+ * @returns {*} Returns the function if it's native, else `undefined`.
+ */
+function getNative(object, key) {
+ var value = getValue(object, key);
+ return baseIsNative(value) ? value : undefined;
+}
+
+/**
+ * Gets the `toStringTag` of `value`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+var getTag = baseGetTag;
+
+// Fallback for data views, maps, sets, and weak maps in IE 11,
+// for data views in Edge < 14, and promises in Node.js.
+if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
+ (Map && getTag(new Map) != mapTag) ||
+ (Promise && getTag(Promise.resolve()) != promiseTag) ||
+ (Set && getTag(new Set) != setTag) ||
+ (WeakMap && getTag(new WeakMap) != weakMapTag)) {
+ getTag = function(value) {
+ var result = objectToString.call(value),
+ Ctor = result == objectTag ? value.constructor : undefined,
+ ctorString = Ctor ? toSource(Ctor) : undefined;
+
+ if (ctorString) {
+ switch (ctorString) {
+ case dataViewCtorString: return dataViewTag;
+ case mapCtorString: return mapTag;
+ case promiseCtorString: return promiseTag;
+ case setCtorString: return setTag;
+ case weakMapCtorString: return weakMapTag;
+ }
+ }
+ return result;
+ };
+}
+
+/**
+ * Checks if `path` exists on `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @param {Function} hasFunc The function to check properties.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ */
+function hasPath(object, path, hasFunc) {
+ path = isKey(path, object) ? [path] : castPath(path);
+
+ var result,
+ index = -1,
+ length = path.length;
+
+ while (++index < length) {
+ var key = toKey(path[index]);
+ if (!(result = object != null && hasFunc(object, key))) {
+ break;
+ }
+ object = object[key];
+ }
+ if (result) {
+ return result;
+ }
+ var length = object ? object.length : 0;
+ return !!length && isLength(length) && isIndex(key, length) &&
+ (isArray(object) || isArguments(object));
+}
+
+/**
+ * Checks if `value` is a flattenable `arguments` object or array.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
+ */
+function isFlattenable(value) {
+ return isArray(value) || isArguments(value) ||
+ !!(spreadableSymbol && value && value[spreadableSymbol]);
+}
+
+/**
+ * Checks if `value` is a valid array-like index.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+ * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+ */
+function isIndex(value, length) {
+ length = length == null ? MAX_SAFE_INTEGER : length;
+ return !!length &&
+ (typeof value == 'number' || reIsUint.test(value)) &&
+ (value > -1 && value % 1 == 0 && value < length);
+}
+
+/**
+ * Checks if the given arguments are from an iteratee call.
+ *
+ * @private
+ * @param {*} value The potential iteratee value argument.
+ * @param {*} index The potential iteratee index or key argument.
+ * @param {*} object The potential iteratee object argument.
+ * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
+ * else `false`.
+ */
+function isIterateeCall(value, index, object) {
+ if (!isObject(object)) {
+ return false;
+ }
+ var type = typeof index;
+ if (type == 'number'
+ ? (isArrayLike(object) && isIndex(index, object.length))
+ : (type == 'string' && index in object)
+ ) {
+ return eq(object[index], value);
+ }
+ return false;
+}
+
+/**
+ * Checks if `value` is a property name and not a property path.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {Object} [object] The object to query keys on.
+ * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
+ */
+function isKey(value, object) {
+ if (isArray(value)) {
+ return false;
+ }
+ var type = typeof value;
+ if (type == 'number' || type == 'symbol' || type == 'boolean' ||
+ value == null || isSymbol(value)) {
+ return true;
+ }
+ return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
+ (object != null && value in Object(object));
+}
+
+/**
+ * Checks if `value` is suitable for use as unique object key.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+ */
+function isKeyable(value) {
+ var type = typeof value;
+ return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
+ ? (value !== '__proto__')
+ : (value === null);
+}
+
+/**
+ * Checks if `func` has its source masked.
+ *
+ * @private
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` is masked, else `false`.
+ */
+function isMasked(func) {
+ return !!maskSrcKey && (maskSrcKey in func);
+}
+
+/**
+ * Checks if `value` is likely a prototype object.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
+ */
+function isPrototype(value) {
+ var Ctor = value && value.constructor,
+ proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
+
+ return value === proto;
+}
+
+/**
+ * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` if suitable for strict
+ * equality comparisons, else `false`.
+ */
+function isStrictComparable(value) {
+ return value === value && !isObject(value);
+}
+
+/**
+ * A specialized version of `matchesProperty` for source values suitable
+ * for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @param {*} srcValue The value to match.
+ * @returns {Function} Returns the new spec function.
+ */
+function matchesStrictComparable(key, srcValue) {
+ return function(object) {
+ if (object == null) {
+ return false;
+ }
+ return object[key] === srcValue &&
+ (srcValue !== undefined || (key in Object(object)));
+ };
+}
+
+/**
+ * Converts `string` to a property path array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the property path array.
+ */
+var stringToPath = memoize(function(string) {
+ string = toString(string);
+
+ var result = [];
+ if (reLeadingDot.test(string)) {
+ result.push('');
+ }
+ string.replace(rePropName, function(match, number, quote, string) {
+ result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
+ });
+ return result;
+});
+
+/**
+ * Converts `value` to a string key if it's not a string or symbol.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @returns {string|symbol} Returns the key.
+ */
+function toKey(value) {
+ if (typeof value == 'string' || isSymbol(value)) {
+ return value;
+ }
+ var result = (value + '');
+ return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+}
+
+/**
+ * Converts `func` to its source code.
+ *
+ * @private
+ * @param {Function} func The function to process.
+ * @returns {string} Returns the source code.
+ */
+function toSource(func) {
+ if (func != null) {
+ try {
+ return funcToString.call(func);
+ } catch (e) {}
+ try {
+ return (func + '');
+ } catch (e) {}
+ }
+ return '';
+}
+
+/**
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection thru each iteratee. This method
+ * performs a stable sort, that is, it preserves the original sort order of
+ * equal elements. The iteratees are invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {...(Function|Function[])} [iteratees=[_.identity]]
+ * The iteratees to sort by.
+ * @returns {Array} Returns the new sorted array.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'barney', 'age': 34 }
+ * ];
+ *
+ * _.sortBy(users, function(o) { return o.user; });
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+ *
+ * _.sortBy(users, ['user', 'age']);
+ * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
+ *
+ * _.sortBy(users, 'user', function(o) {
+ * return Math.floor(o.age / 10);
+ * });
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+ */
+var sortBy = baseRest(function(collection, iteratees) {
+ if (collection == null) {
+ return [];
+ }
+ var length = iteratees.length;
+ if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
+ iteratees = [];
+ } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
+ iteratees = [iteratees[0]];
+ }
+ return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
+});
+
+/**
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided, it determines the cache key for storing the result based on the
+ * arguments provided to the memoized function. By default, the first argument
+ * provided to the memoized function is used as the map cache key. The `func`
+ * is invoked with the `this` binding of the memoized function.
+ *
+ * **Note:** The cache is exposed as the `cache` property on the memoized
+ * function. Its creation may be customized by replacing the `_.memoize.Cache`
+ * constructor with one whose instances implement the
+ * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
+ * method interface of `delete`, `get`, `has`, and `set`.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] The function to resolve the cache key.
+ * @returns {Function} Returns the new memoized function.
+ * @example
+ *
+ * var object = { 'a': 1, 'b': 2 };
+ * var other = { 'c': 3, 'd': 4 };
+ *
+ * var values = _.memoize(_.values);
+ * values(object);
+ * // => [1, 2]
+ *
+ * values(other);
+ * // => [3, 4]
+ *
+ * object.a = 2;
+ * values(object);
+ * // => [1, 2]
+ *
+ * // Modify the result cache.
+ * values.cache.set(object, ['a', 'b']);
+ * values(object);
+ * // => ['a', 'b']
+ *
+ * // Replace `_.memoize.Cache`.
+ * _.memoize.Cache = WeakMap;
+ */
+function memoize(func, resolver) {
+ if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var memoized = function() {
+ var args = arguments,
+ key = resolver ? resolver.apply(this, args) : args[0],
+ cache = memoized.cache;
+
+ if (cache.has(key)) {
+ return cache.get(key);
+ }
+ var result = func.apply(this, args);
+ memoized.cache = cache.set(key, result);
+ return result;
+ };
+ memoized.cache = new (memoize.Cache || MapCache);
+ return memoized;
+}
+
+// Assign cache to `_.memoize`.
+memoize.Cache = MapCache;
+
+/**
+ * Performs a
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * comparison between two values to determine if they are equivalent.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'a': 1 };
+ * var other = { 'a': 1 };
+ *
+ * _.eq(object, object);
+ * // => true
+ *
+ * _.eq(object, other);
+ * // => false
+ *
+ * _.eq('a', 'a');
+ * // => true
+ *
+ * _.eq('a', Object('a'));
+ * // => false
+ *
+ * _.eq(NaN, NaN);
+ * // => true
+ */
+function eq(value, other) {
+ return value === other || (value !== value && other !== other);
+}
+
+/**
+ * Checks if `value` is likely an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+ * else `false`.
+ * @example
+ *
+ * _.isArguments(function() { return arguments; }());
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+function isArguments(value) {
+ // Safari 8.1 makes `arguments.callee` enumerable in strict mode.
+ return isArrayLikeObject(value) && hasOwnProperty.call(value, 'callee') &&
+ (!propertyIsEnumerable.call(value, 'callee') || objectToString.call(value) == argsTag);
+}
+
+/**
+ * Checks if `value` is classified as an `Array` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array, else `false`.
+ * @example
+ *
+ * _.isArray([1, 2, 3]);
+ * // => true
+ *
+ * _.isArray(document.body.children);
+ * // => false
+ *
+ * _.isArray('abc');
+ * // => false
+ *
+ * _.isArray(_.noop);
+ * // => false
+ */
+var isArray = Array.isArray;
+
+/**
+ * Checks if `value` is array-like. A value is considered array-like if it's
+ * not a function and has a `value.length` that's an integer greater than or
+ * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+ * @example
+ *
+ * _.isArrayLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isArrayLike(document.body.children);
+ * // => true
+ *
+ * _.isArrayLike('abc');
+ * // => true
+ *
+ * _.isArrayLike(_.noop);
+ * // => false
+ */
+function isArrayLike(value) {
+ return value != null && isLength(value.length) && !isFunction(value);
+}
+
+/**
+ * This method is like `_.isArrayLike` except that it also checks if `value`
+ * is an object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array-like object,
+ * else `false`.
+ * @example
+ *
+ * _.isArrayLikeObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isArrayLikeObject(document.body.children);
+ * // => true
+ *
+ * _.isArrayLikeObject('abc');
+ * // => false
+ *
+ * _.isArrayLikeObject(_.noop);
+ * // => false
+ */
+function isArrayLikeObject(value) {
+ return isObjectLike(value) && isArrayLike(value);
+}
+
+/**
+ * Checks if `value` is classified as a `Function` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ *
+ * _.isFunction(/abc/);
+ * // => false
+ */
+function isFunction(value) {
+ // The use of `Object#toString` avoids issues with the `typeof` operator
+ // in Safari 8-9 which returns 'object' for typed array and other constructors.
+ var tag = isObject(value) ? objectToString.call(value) : '';
+ return tag == funcTag || tag == genTag;
+}
+
+/**
+ * Checks if `value` is a valid array-like length.
+ *
+ * **Note:** This method is loosely based on
+ * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+ * @example
+ *
+ * _.isLength(3);
+ * // => true
+ *
+ * _.isLength(Number.MIN_VALUE);
+ * // => false
+ *
+ * _.isLength(Infinity);
+ * // => false
+ *
+ * _.isLength('3');
+ * // => false
+ */
+function isLength(value) {
+ return typeof value == 'number' &&
+ value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+}
+
+/**
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+function isObject(value) {
+ var type = typeof value;
+ return !!value && (type == 'object' || type == 'function');
+}
+
+/**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @example
+ *
+ * _.isObjectLike({});
+ * // => true
+ *
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
+ *
+ * _.isObjectLike(_.noop);
+ * // => false
+ *
+ * _.isObjectLike(null);
+ * // => false
+ */
+function isObjectLike(value) {
+ return !!value && typeof value == 'object';
+}
+
+/**
+ * Checks if `value` is classified as a `Symbol` primitive or object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @example
+ *
+ * _.isSymbol(Symbol.iterator);
+ * // => true
+ *
+ * _.isSymbol('abc');
+ * // => false
+ */
+function isSymbol(value) {
+ return typeof value == 'symbol' ||
+ (isObjectLike(value) && objectToString.call(value) == symbolTag);
+}
+
+/**
+ * Checks if `value` is classified as a typed array.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+ * @example
+ *
+ * _.isTypedArray(new Uint8Array);
+ * // => true
+ *
+ * _.isTypedArray([]);
+ * // => false
+ */
+var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
+
+/**
+ * Converts `value` to a string. An empty string is returned for `null`
+ * and `undefined` values. The sign of `-0` is preserved.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to process.
+ * @returns {string} Returns the string.
+ * @example
+ *
+ * _.toString(null);
+ * // => ''
+ *
+ * _.toString(-0);
+ * // => '-0'
+ *
+ * _.toString([1, 2, 3]);
+ * // => '1,2,3'
+ */
+function toString(value) {
+ return value == null ? '' : baseToString(value);
+}
+
+/**
+ * Gets the value at `path` of `object`. If the resolved value is
+ * `undefined`, the `defaultValue` is returned in its place.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.7.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to get.
+ * @param {*} [defaultValue] The value returned for `undefined` resolved values.
+ * @returns {*} Returns the resolved value.
+ * @example
+ *
+ * var object = { 'a': [{ 'b': { 'c': 3 } }] };
+ *
+ * _.get(object, 'a[0].b.c');
+ * // => 3
+ *
+ * _.get(object, ['a', '0', 'b', 'c']);
+ * // => 3
+ *
+ * _.get(object, 'a.b.c', 'default');
+ * // => 'default'
+ */
+function get(object, path, defaultValue) {
+ var result = object == null ? undefined : baseGet(object, path);
+ return result === undefined ? defaultValue : result;
+}
+
+/**
+ * Checks if `path` is a direct or inherited property of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ * @example
+ *
+ * var object = _.create({ 'a': _.create({ 'b': 2 }) });
+ *
+ * _.hasIn(object, 'a');
+ * // => true
+ *
+ * _.hasIn(object, 'a.b');
+ * // => true
+ *
+ * _.hasIn(object, ['a', 'b']);
+ * // => true
+ *
+ * _.hasIn(object, 'b');
+ * // => false
+ */
+function hasIn(object, path) {
+ return object != null && hasPath(object, path, baseHasIn);
+}
+
+/**
+ * Creates an array of the own enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects. See the
+ * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
+ * for more details.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.keys(new Foo);
+ * // => ['a', 'b'] (iteration order is not guaranteed)
+ *
+ * _.keys('hi');
+ * // => ['0', '1']
+ */
+function keys(object) {
+ return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
+}
+
+/**
+ * This method returns the first argument it receives.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Util
+ * @param {*} value Any value.
+ * @returns {*} Returns `value`.
+ * @example
+ *
+ * var object = { 'a': 1 };
+ *
+ * console.log(_.identity(object) === object);
+ * // => true
+ */
+function identity(value) {
+ return value;
+}
+
+/**
+ * Creates a function that returns the value at `path` of a given object.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.4.0
+ * @category Util
+ * @param {Array|string} path The path of the property to get.
+ * @returns {Function} Returns the new accessor function.
+ * @example
+ *
+ * var objects = [
+ * { 'a': { 'b': 2 } },
+ * { 'a': { 'b': 1 } }
+ * ];
+ *
+ * _.map(objects, _.property('a.b'));
+ * // => [2, 1]
+ *
+ * _.map(_.sortBy(objects, _.property(['a', 'b'])), 'a.b');
+ * // => [1, 2]
+ */
+function property(path) {
+ return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path);
+}
+
+module.exports = sortBy;
+
+}).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}],15:[function(require,module,exports){
+"use strict";
+
+const punycode = require("punycode");
+const regexes = require("./lib/regexes.js");
+const mappingTable = require("./lib/mappingTable.json");
+
+function containsNonASCII(str) {
+ return /[^\x00-\x7F]/.test(str);
+}
+
+function findStatus(val, { useSTD3ASCIIRules }) {
+ let start = 0;
+ let end = mappingTable.length - 1;
+
+ while (start <= end) {
+ const mid = Math.floor((start + end) / 2);
+
+ const target = mappingTable[mid];
+ if (target[0][0] <= val && target[0][1] >= val) {
+ if (target[1].startsWith("disallowed_STD3_")) {
+ const newStatus = useSTD3ASCIIRules ? "disallowed" : target[1].slice(16);
+ return [newStatus, ...target.slice(2)];
+ }
+ return target.slice(1);
+ } else if (target[0][0] > val) {
+ end = mid - 1;
+ } else {
+ start = mid + 1;
+ }
+ }
+
+ return null;
+}
+
+function mapChars(domainName, { useSTD3ASCIIRules, processingOption }) {
+ let hasError = false;
+ let processed = "";
+
+ for (const ch of domainName) {
+ const [status, mapping] = findStatus(ch.codePointAt(0), { useSTD3ASCIIRules });
+
+ switch (status) {
+ case "disallowed":
+ hasError = true;
+ processed += ch;
+ break;
+ case "ignored":
+ break;
+ case "mapped":
+ processed += mapping;
+ break;
+ case "deviation":
+ if (processingOption === "transitional") {
+ processed += mapping;
+ } else {
+ processed += ch;
+ }
+ break;
+ case "valid":
+ processed += ch;
+ break;
+ }
+ }
+
+ return {
+ string: processed,
+ error: hasError
+ };
+}
+
+function validateLabel(label, { checkHyphens, checkBidi, checkJoiners, processingOption, useSTD3ASCIIRules }) {
+ if (label.normalize("NFC") !== label) {
+ return false;
+ }
+
+ const codePoints = Array.from(label);
+
+ if (checkHyphens) {
+ if ((codePoints[2] === "-" && codePoints[3] === "-") ||
+ (label.startsWith("-") || label.endsWith("-"))) {
+ return false;
+ }
+ }
+
+ if (label.includes(".") ||
+ (codePoints.length > 0 && regexes.combiningMarks.test(codePoints[0]))) {
+ return false;
+ }
+
+ for (const ch of codePoints) {
+ const [status] = findStatus(ch.codePointAt(0), { useSTD3ASCIIRules });
+ if ((processingOption === "transitional" && status !== "valid") ||
+ (processingOption === "nontransitional" &&
+ status !== "valid" && status !== "deviation")) {
+ return false;
+ }
+ }
+
+ // https://tools.ietf.org/html/rfc5892#appendix-A
+ if (checkJoiners) {
+ let last = 0;
+ for (const [i, ch] of codePoints.entries()) {
+ if (ch === "\u200C" || ch === "\u200D") {
+ if (i > 0) {
+ if (regexes.combiningClassVirama.test(codePoints[i - 1])) {
+ continue;
+ }
+ if (ch === "\u200C") {
+ // TODO: make this more efficient
+ const next = codePoints.indexOf("\u200C", i + 1);
+ const test = next < 0 ? codePoints.slice(last) : codePoints.slice(last, next);
+ if (regexes.validZWNJ.test(test.join(""))) {
+ last = i + 1;
+ continue;
+ }
+ }
+ }
+ return false;
+ }
+ }
+ }
+
+ // https://tools.ietf.org/html/rfc5893#section-2
+ if (checkBidi) {
+ let rtl;
+
+ // 1
+ if (regexes.bidiS1LTR.test(codePoints[0])) {
+ rtl = false;
+ } else if (regexes.bidiS1RTL.test(codePoints[0])) {
+ rtl = true;
+ } else {
+ return false;
+ }
+
+ if (rtl) {
+ // 2-4
+ if (!regexes.bidiS2.test(label) ||
+ !regexes.bidiS3.test(label) ||
+ (regexes.bidiS4EN.test(label) && regexes.bidiS4AN.test(label))) {
+ return false;
+ }
+ } else if (!regexes.bidiS5.test(label) ||
+ !regexes.bidiS6.test(label)) { // 5-6
+ return false;
+ }
+ }
+
+ return true;
+}
+
+function isBidiDomain(labels) {
+ const domain = labels.map(label => {
+ if (label.startsWith("xn--")) {
+ try {
+ return punycode.decode(label.substring(4));
+ } catch (err) {
+ return "";
+ }
+ }
+ return label;
+ }).join(".");
+ return regexes.bidiDomain.test(domain);
+}
+
+function processing(domainName, options) {
+ const { processingOption } = options;
+
+ // 1. Map.
+ let { string, error } = mapChars(domainName, options);
+
+ // 2. Normalize.
+ string = string.normalize("NFC");
+
+ // 3. Break.
+ const labels = string.split(".");
+ const isBidi = isBidiDomain(labels);
+
+ // 4. Convert/Validate.
+ for (const [i, origLabel] of labels.entries()) {
+ let label = origLabel;
+ let curProcessing = processingOption;
+ if (label.startsWith("xn--")) {
+ try {
+ label = punycode.decode(label.substring(4));
+ labels[i] = label;
+ } catch (err) {
+ error = true;
+ continue;
+ }
+ curProcessing = "nontransitional";
+ }
+
+ // No need to validate if we already know there is an error.
+ if (error) {
+ continue;
+ }
+ const validation = validateLabel(label, Object.assign({}, options, {
+ processingOption: curProcessing,
+ checkBidi: options.checkBidi && isBidi
+ }));
+ if (!validation) {
+ error = true;
+ }
+ }
+
+ return {
+ string: labels.join("."),
+ error
+ };
+}
+
+function toASCII(domainName, {
+ checkHyphens = false,
+ checkBidi = false,
+ checkJoiners = false,
+ useSTD3ASCIIRules = false,
+ processingOption = "nontransitional",
+ verifyDNSLength = false
+} = {}) {
+ if (processingOption !== "transitional" && processingOption !== "nontransitional") {
+ throw new RangeError("processingOption must be either transitional or nontransitional");
+ }
+
+ const result = processing(domainName, {
+ processingOption,
+ checkHyphens,
+ checkBidi,
+ checkJoiners,
+ useSTD3ASCIIRules
+ });
+ let labels = result.string.split(".");
+ labels = labels.map(l => {
+ if (containsNonASCII(l)) {
+ try {
+ return "xn--" + punycode.encode(l);
+ } catch (e) {
+ result.error = true;
+ }
+ }
+ return l;
+ });
+
+ if (verifyDNSLength) {
+ const total = labels.join(".").length;
+ if (total > 253 || total === 0) {
+ result.error = true;
+ }
+
+ for (let i = 0; i < labels.length; ++i) {
+ if (labels[i].length > 63 || labels[i].length === 0) {
+ result.error = true;
+ break;
+ }
+ }
+ }
+
+ if (result.error) {
+ return null;
+ }
+ return labels.join(".");
+}
+
+function toUnicode(domainName, {
+ checkHyphens = false,
+ checkBidi = false,
+ checkJoiners = false,
+ useSTD3ASCIIRules = false
+} = {}) {
+ const result = processing(domainName, {
+ processingOption: "nontransitional",
+ checkHyphens,
+ checkBidi,
+ checkJoiners,
+ useSTD3ASCIIRules
+ });
+
+ return {
+ domain: result.string,
+ error: result.error
+ };
+}
+
+module.exports = {
+ toASCII,
+ toUnicode
+};
+
+},{"./lib/mappingTable.json":16,"./lib/regexes.js":17,"punycode":11}],16:[function(require,module,exports){
+module.exports=[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"],[[47,47],"disallowed_STD3_valid"],[[48,57],"valid"],[[58,64],"disallowed_STD3_valid"],[[65,65],"mapped","a"],[[66,66],"mapped","b"],[[67,67],"mapped","c"],[[68,68],"mapped","d"],[[69,69],"mapped","e"],[[70,70],"mapped","f"],[[71,71],"mapped","g"],[[72,72],"mapped","h"],[[73,73],"mapped","i"],[[74,74],"mapped","j"],[[75,75],"mapped","k"],[[76,76],"mapped","l"],[[77,77],"mapped","m"],[[78,78],"mapped","n"],[[79,79],"mapped","o"],[[80,80],"mapped","p"],[[81,81],"mapped","q"],[[82,82],"mapped","r"],[[83,83],"mapped","s"],[[84,84],"mapped","t"],[[85,85],"mapped","u"],[[86,86],"mapped","v"],[[87,87],"mapped","w"],[[88,88],"mapped","x"],[[89,89],"mapped","y"],[[90,90],"mapped","z"],[[91,96],"disallowed_STD3_valid"],[[97,122],"valid"],[[123,127],"disallowed_STD3_valid"],[[128,159],"disallowed"],[[160,160],"disallowed_STD3_mapped"," "],[[161,167],"valid","","NV8"],[[168,168],"disallowed_STD3_mapped"," ̈"],[[169,169],"valid","","NV8"],[[170,170],"mapped","a"],[[171,172],"valid","","NV8"],[[173,173],"ignored"],[[174,174],"valid","","NV8"],[[175,175],"disallowed_STD3_mapped"," ̄"],[[176,177],"valid","","NV8"],[[178,178],"mapped","2"],[[179,179],"mapped","3"],[[180,180],"disallowed_STD3_mapped"," ́"],[[181,181],"mapped","μ"],[[182,182],"valid","","NV8"],[[183,183],"valid"],[[184,184],"disallowed_STD3_mapped"," ̧"],[[185,185],"mapped","1"],[[186,186],"mapped","o"],[[187,187],"valid","","NV8"],[[188,188],"mapped","1⁄4"],[[189,189],"mapped","1⁄2"],[[190,190],"mapped","3⁄4"],[[191,191],"valid","","NV8"],[[192,192],"mapped","à"],[[193,193],"mapped","á"],[[194,194],"mapped","â"],[[195,195],"mapped","ã"],[[196,196],"mapped","ä"],[[197,197],"mapped","å"],[[198,198],"mapped","æ"],[[199,199],"mapped","ç"],[[200,200],"mapped","è"],[[201,201],"mapped","é"],[[202,202],"mapped","ê"],[[203,203],"mapped","ë"],[[204,204],"mapped","ì"],[[205,205],"mapped","í"],[[206,206],"mapped","î"],[[207,207],"mapped","ï"],[[208,208],"mapped","ð"],[[209,209],"mapped","ñ"],[[210,210],"mapped","ò"],[[211,211],"mapped","ó"],[[212,212],"mapped","ô"],[[213,213],"mapped","õ"],[[214,214],"mapped","ö"],[[215,215],"valid","","NV8"],[[216,216],"mapped","ø"],[[217,217],"mapped","ù"],[[218,218],"mapped","ú"],[[219,219],"mapped","û"],[[220,220],"mapped","ü"],[[221,221],"mapped","ý"],[[222,222],"mapped","þ"],[[223,223],"deviation","ss"],[[224,246],"valid"],[[247,247],"valid","","NV8"],[[248,255],"valid"],[[256,256],"mapped","ā"],[[257,257],"valid"],[[258,258],"mapped","ă"],[[259,259],"valid"],[[260,260],"mapped","ą"],[[261,261],"valid"],[[262,262],"mapped","ć"],[[263,263],"valid"],[[264,264],"mapped","ĉ"],[[265,265],"valid"],[[266,266],"mapped","ċ"],[[267,267],"valid"],[[268,268],"mapped","č"],[[269,269],"valid"],[[270,270],"mapped","ď"],[[271,271],"valid"],[[272,272],"mapped","đ"],[[273,273],"valid"],[[274,274],"mapped","ē"],[[275,275],"valid"],[[276,276],"mapped","ĕ"],[[277,277],"valid"],[[278,278],"mapped","ė"],[[279,279],"valid"],[[280,280],"mapped","ę"],[[281,281],"valid"],[[282,282],"mapped","ě"],[[283,283],"valid"],[[284,284],"mapped","ĝ"],[[285,285],"valid"],[[286,286],"mapped","ğ"],[[287,287],"valid"],[[288,288],"mapped","ġ"],[[289,289],"valid"],[[290,290],"mapped","ģ"],[[291,291],"valid"],[[292,292],"mapped","ĥ"],[[293,293],"valid"],[[294,294],"mapped","ħ"],[[295,295],"valid"],[[296,296],"mapped","ĩ"],[[297,297],"valid"],[[298,298],"mapped","ī"],[[299,299],"valid"],[[300,300],"mapped","ĭ"],[[301,301],"valid"],[[302,302],"mapped","į"],[[303,303],"valid"],[[304,304],"mapped","i̇"],[[305,305],"valid"],[[306,307],"mapped","ij"],[[308,308],"mapped","ĵ"],[[309,309],"valid"],[[310,310],"mapped","ķ"],[[311,312],"valid"],[[313,313],"mapped","ĺ"],[[314,314],"valid"],[[315,315],"mapped","ļ"],[[316,316],"valid"],[[317,317],"mapped","ľ"],[[318,318],"valid"],[[319,320],"mapped","l·"],[[321,321],"mapped","ł"],[[322,322],"valid"],[[323,323],"mapped","ń"],[[324,324],"valid"],[[325,325],"mapped","ņ"],[[326,326],"valid"],[[327,327],"mapped","ň"],[[328,328],"valid"],[[329,329],"mapped","ʼn"],[[330,330],"mapped","ŋ"],[[331,331],"valid"],[[332,332],"mapped","ō"],[[333,333],"valid"],[[334,334],"mapped","ŏ"],[[335,335],"valid"],[[336,336],"mapped","ő"],[[337,337],"valid"],[[338,338],"mapped","œ"],[[339,339],"valid"],[[340,340],"mapped","ŕ"],[[341,341],"valid"],[[342,342],"mapped","ŗ"],[[343,343],"valid"],[[344,344],"mapped","ř"],[[345,345],"valid"],[[346,346],"mapped","ś"],[[347,347],"valid"],[[348,348],"mapped","ŝ"],[[349,349],"valid"],[[350,350],"mapped","ş"],[[351,351],"valid"],[[352,352],"mapped","š"],[[353,353],"valid"],[[354,354],"mapped","ţ"],[[355,355],"valid"],[[356,356],"mapped","ť"],[[357,357],"valid"],[[358,358],"mapped","ŧ"],[[359,359],"valid"],[[360,360],"mapped","ũ"],[[361,361],"valid"],[[362,362],"mapped","ū"],[[363,363],"valid"],[[364,364],"mapped","ŭ"],[[365,365],"valid"],[[366,366],"mapped","ů"],[[367,367],"valid"],[[368,368],"mapped","ű"],[[369,369],"valid"],[[370,370],"mapped","ų"],[[371,371],"valid"],[[372,372],"mapped","ŵ"],[[373,373],"valid"],[[374,374],"mapped","ŷ"],[[375,375],"valid"],[[376,376],"mapped","ÿ"],[[377,377],"mapped","ź"],[[378,378],"valid"],[[379,379],"mapped","ż"],[[380,380],"valid"],[[381,381],"mapped","ž"],[[382,382],"valid"],[[383,383],"mapped","s"],[[384,384],"valid"],[[385,385],"mapped","ɓ"],[[386,386],"mapped","ƃ"],[[387,387],"valid"],[[388,388],"mapped","ƅ"],[[389,389],"valid"],[[390,390],"mapped","ɔ"],[[391,391],"mapped","ƈ"],[[392,392],"valid"],[[393,393],"mapped","ɖ"],[[394,394],"mapped","ɗ"],[[395,395],"mapped","ƌ"],[[396,397],"valid"],[[398,398],"mapped","ǝ"],[[399,399],"mapped","ə"],[[400,400],"mapped","ɛ"],[[401,401],"mapped","ƒ"],[[402,402],"valid"],[[403,403],"mapped","ɠ"],[[404,404],"mapped","ɣ"],[[405,405],"valid"],[[406,406],"mapped","ɩ"],[[407,407],"mapped","ɨ"],[[408,408],"mapped","ƙ"],[[409,411],"valid"],[[412,412],"mapped","ɯ"],[[413,413],"mapped","ɲ"],[[414,414],"valid"],[[415,415],"mapped","ɵ"],[[416,416],"mapped","ơ"],[[417,417],"valid"],[[418,418],"mapped","ƣ"],[[419,419],"valid"],[[420,420],"mapped","ƥ"],[[421,421],"valid"],[[422,422],"mapped","ʀ"],[[423,423],"mapped","ƨ"],[[424,424],"valid"],[[425,425],"mapped","ʃ"],[[426,427],"valid"],[[428,428],"mapped","ƭ"],[[429,429],"valid"],[[430,430],"mapped","ʈ"],[[431,431],"mapped","ư"],[[432,432],"valid"],[[433,433],"mapped","ʊ"],[[434,434],"mapped","ʋ"],[[435,435],"mapped","ƴ"],[[436,436],"valid"],[[437,437],"mapped","ƶ"],[[438,438],"valid"],[[439,439],"mapped","ʒ"],[[440,440],"mapped","ƹ"],[[441,443],"valid"],[[444,444],"mapped","ƽ"],[[445,451],"valid"],[[452,454],"mapped","dž"],[[455,457],"mapped","lj"],[[458,460],"mapped","nj"],[[461,461],"mapped","ǎ"],[[462,462],"valid"],[[463,463],"mapped","ǐ"],[[464,464],"valid"],[[465,465],"mapped","ǒ"],[[466,466],"valid"],[[467,467],"mapped","ǔ"],[[468,468],"valid"],[[469,469],"mapped","ǖ"],[[470,470],"valid"],[[471,471],"mapped","ǘ"],[[472,472],"valid"],[[473,473],"mapped","ǚ"],[[474,474],"valid"],[[475,475],"mapped","ǜ"],[[476,477],"valid"],[[478,478],"mapped","ǟ"],[[479,479],"valid"],[[480,480],"mapped","ǡ"],[[481,481],"valid"],[[482,482],"mapped","ǣ"],[[483,483],"valid"],[[484,484],"mapped","ǥ"],[[485,485],"valid"],[[486,486],"mapped","ǧ"],[[487,487],"valid"],[[488,488],"mapped","ǩ"],[[489,489],"valid"],[[490,490],"mapped","ǫ"],[[491,491],"valid"],[[492,492],"mapped","ǭ"],[[493,493],"valid"],[[494,494],"mapped","ǯ"],[[495,496],"valid"],[[497,499],"mapped","dz"],[[500,500],"mapped","ǵ"],[[501,501],"valid"],[[502,502],"mapped","ƕ"],[[503,503],"mapped","ƿ"],[[504,504],"mapped","ǹ"],[[505,505],"valid"],[[506,506],"mapped","ǻ"],[[507,507],"valid"],[[508,508],"mapped","ǽ"],[[509,509],"valid"],[[510,510],"mapped","ǿ"],[[511,511],"valid"],[[512,512],"mapped","ȁ"],[[513,513],"valid"],[[514,514],"mapped","ȃ"],[[515,515],"valid"],[[516,516],"mapped","ȅ"],[[517,517],"valid"],[[518,518],"mapped","ȇ"],[[519,519],"valid"],[[520,520],"mapped","ȉ"],[[521,521],"valid"],[[522,522],"mapped","ȋ"],[[523,523],"valid"],[[524,524],"mapped","ȍ"],[[525,525],"valid"],[[526,526],"mapped","ȏ"],[[527,527],"valid"],[[528,528],"mapped","ȑ"],[[529,529],"valid"],[[530,530],"mapped","ȓ"],[[531,531],"valid"],[[532,532],"mapped","ȕ"],[[533,533],"valid"],[[534,534],"mapped","ȗ"],[[535,535],"valid"],[[536,536],"mapped","ș"],[[537,537],"valid"],[[538,538],"mapped","ț"],[[539,539],"valid"],[[540,540],"mapped","ȝ"],[[541,541],"valid"],[[542,542],"mapped","ȟ"],[[543,543],"valid"],[[544,544],"mapped","ƞ"],[[545,545],"valid"],[[546,546],"mapped","ȣ"],[[547,547],"valid"],[[548,548],"mapped","ȥ"],[[549,549],"valid"],[[550,550],"mapped","ȧ"],[[551,551],"valid"],[[552,552],"mapped","ȩ"],[[553,553],"valid"],[[554,554],"mapped","ȫ"],[[555,555],"valid"],[[556,556],"mapped","ȭ"],[[557,557],"valid"],[[558,558],"mapped","ȯ"],[[559,559],"valid"],[[560,560],"mapped","ȱ"],[[561,561],"valid"],[[562,562],"mapped","ȳ"],[[563,563],"valid"],[[564,566],"valid"],[[567,569],"valid"],[[570,570],"mapped","ⱥ"],[[571,571],"mapped","ȼ"],[[572,572],"valid"],[[573,573],"mapped","ƚ"],[[574,574],"mapped","ⱦ"],[[575,576],"valid"],[[577,577],"mapped","ɂ"],[[578,578],"valid"],[[579,579],"mapped","ƀ"],[[580,580],"mapped","ʉ"],[[581,581],"mapped","ʌ"],[[582,582],"mapped","ɇ"],[[583,583],"valid"],[[584,584],"mapped","ɉ"],[[585,585],"valid"],[[586,586],"mapped","ɋ"],[[587,587],"valid"],[[588,588],"mapped","ɍ"],[[589,589],"valid"],[[590,590],"mapped","ɏ"],[[591,591],"valid"],[[592,680],"valid"],[[681,685],"valid"],[[686,687],"valid"],[[688,688],"mapped","h"],[[689,689],"mapped","ɦ"],[[690,690],"mapped","j"],[[691,691],"mapped","r"],[[692,692],"mapped","ɹ"],[[693,693],"mapped","ɻ"],[[694,694],"mapped","ʁ"],[[695,695],"mapped","w"],[[696,696],"mapped","y"],[[697,705],"valid"],[[706,709],"valid","","NV8"],[[710,721],"valid"],[[722,727],"valid","","NV8"],[[728,728],"disallowed_STD3_mapped"," ̆"],[[729,729],"disallowed_STD3_mapped"," ̇"],[[730,730],"disallowed_STD3_mapped"," ̊"],[[731,731],"disallowed_STD3_mapped"," ̨"],[[732,732],"disallowed_STD3_mapped"," ̃"],[[733,733],"disallowed_STD3_mapped"," ̋"],[[734,734],"valid","","NV8"],[[735,735],"valid","","NV8"],[[736,736],"mapped","ɣ"],[[737,737],"mapped","l"],[[738,738],"mapped","s"],[[739,739],"mapped","x"],[[740,740],"mapped","ʕ"],[[741,745],"valid","","NV8"],[[746,747],"valid","","NV8"],[[748,748],"valid"],[[749,749],"valid","","NV8"],[[750,750],"valid"],[[751,767],"valid","","NV8"],[[768,831],"valid"],[[832,832],"mapped","̀"],[[833,833],"mapped","́"],[[834,834],"valid"],[[835,835],"mapped","̓"],[[836,836],"mapped","̈́"],[[837,837],"mapped","ι"],[[838,846],"valid"],[[847,847],"ignored"],[[848,855],"valid"],[[856,860],"valid"],[[861,863],"valid"],[[864,865],"valid"],[[866,866],"valid"],[[867,879],"valid"],[[880,880],"mapped","ͱ"],[[881,881],"valid"],[[882,882],"mapped","ͳ"],[[883,883],"valid"],[[884,884],"mapped","ʹ"],[[885,885],"valid"],[[886,886],"mapped","ͷ"],[[887,887],"valid"],[[888,889],"disallowed"],[[890,890],"disallowed_STD3_mapped"," ι"],[[891,893],"valid"],[[894,894],"disallowed_STD3_mapped",";"],[[895,895],"mapped","ϳ"],[[896,899],"disallowed"],[[900,900],"disallowed_STD3_mapped"," ́"],[[901,901],"disallowed_STD3_mapped"," ̈́"],[[902,902],"mapped","ά"],[[903,903],"mapped","·"],[[904,904],"mapped","έ"],[[905,905],"mapped","ή"],[[906,906],"mapped","ί"],[[907,907],"disallowed"],[[908,908],"mapped","ό"],[[909,909],"disallowed"],[[910,910],"mapped","ύ"],[[911,911],"mapped","ώ"],[[912,912],"valid"],[[913,913],"mapped","α"],[[914,914],"mapped","β"],[[915,915],"mapped","γ"],[[916,916],"mapped","δ"],[[917,917],"mapped","ε"],[[918,918],"mapped","ζ"],[[919,919],"mapped","η"],[[920,920],"mapped","θ"],[[921,921],"mapped","ι"],[[922,922],"mapped","κ"],[[923,923],"mapped","λ"],[[924,924],"mapped","μ"],[[925,925],"mapped","ν"],[[926,926],"mapped","ξ"],[[927,927],"mapped","ο"],[[928,928],"mapped","π"],[[929,929],"mapped","ρ"],[[930,930],"disallowed"],[[931,931],"mapped","σ"],[[932,932],"mapped","τ"],[[933,933],"mapped","υ"],[[934,934],"mapped","φ"],[[935,935],"mapped","χ"],[[936,936],"mapped","ψ"],[[937,937],"mapped","ω"],[[938,938],"mapped","ϊ"],[[939,939],"mapped","ϋ"],[[940,961],"valid"],[[962,962],"deviation","σ"],[[963,974],"valid"],[[975,975],"mapped","ϗ"],[[976,976],"mapped","β"],[[977,977],"mapped","θ"],[[978,978],"mapped","υ"],[[979,979],"mapped","ύ"],[[980,980],"mapped","ϋ"],[[981,981],"mapped","φ"],[[982,982],"mapped","π"],[[983,983],"valid"],[[984,984],"mapped","ϙ"],[[985,985],"valid"],[[986,986],"mapped","ϛ"],[[987,987],"valid"],[[988,988],"mapped","ϝ"],[[989,989],"valid"],[[990,990],"mapped","ϟ"],[[991,991],"valid"],[[992,992],"mapped","ϡ"],[[993,993],"valid"],[[994,994],"mapped","ϣ"],[[995,995],"valid"],[[996,996],"mapped","ϥ"],[[997,997],"valid"],[[998,998],"mapped","ϧ"],[[999,999],"valid"],[[1000,1000],"mapped","ϩ"],[[1001,1001],"valid"],[[1002,1002],"mapped","ϫ"],[[1003,1003],"valid"],[[1004,1004],"mapped","ϭ"],[[1005,1005],"valid"],[[1006,1006],"mapped","ϯ"],[[1007,1007],"valid"],[[1008,1008],"mapped","κ"],[[1009,1009],"mapped","ρ"],[[1010,1010],"mapped","σ"],[[1011,1011],"valid"],[[1012,1012],"mapped","θ"],[[1013,1013],"mapped","ε"],[[1014,1014],"valid","","NV8"],[[1015,1015],"mapped","ϸ"],[[1016,1016],"valid"],[[1017,1017],"mapped","σ"],[[1018,1018],"mapped","ϻ"],[[1019,1019],"valid"],[[1020,1020],"valid"],[[1021,1021],"mapped","ͻ"],[[1022,1022],"mapped","ͼ"],[[1023,1023],"mapped","ͽ"],[[1024,1024],"mapped","ѐ"],[[1025,1025],"mapped","ё"],[[1026,1026],"mapped","ђ"],[[1027,1027],"mapped","ѓ"],[[1028,1028],"mapped","є"],[[1029,1029],"mapped","ѕ"],[[1030,1030],"mapped","і"],[[1031,1031],"mapped","ї"],[[1032,1032],"mapped","ј"],[[1033,1033],"mapped","љ"],[[1034,1034],"mapped","њ"],[[1035,1035],"mapped","ћ"],[[1036,1036],"mapped","ќ"],[[1037,1037],"mapped","ѝ"],[[1038,1038],"mapped","ў"],[[1039,1039],"mapped","џ"],[[1040,1040],"mapped","а"],[[1041,1041],"mapped","б"],[[1042,1042],"mapped","в"],[[1043,1043],"mapped","г"],[[1044,1044],"mapped","д"],[[1045,1045],"mapped","е"],[[1046,1046],"mapped","ж"],[[1047,1047],"mapped","з"],[[1048,1048],"mapped","и"],[[1049,1049],"mapped","й"],[[1050,1050],"mapped","к"],[[1051,1051],"mapped","л"],[[1052,1052],"mapped","м"],[[1053,1053],"mapped","н"],[[1054,1054],"mapped","о"],[[1055,1055],"mapped","п"],[[1056,1056],"mapped","р"],[[1057,1057],"mapped","с"],[[1058,1058],"mapped","т"],[[1059,1059],"mapped","у"],[[1060,1060],"mapped","ф"],[[1061,1061],"mapped","х"],[[1062,1062],"mapped","ц"],[[1063,1063],"mapped","ч"],[[1064,1064],"mapped","ш"],[[1065,1065],"mapped","щ"],[[1066,1066],"mapped","ъ"],[[1067,1067],"mapped","ы"],[[1068,1068],"mapped","ь"],[[1069,1069],"mapped","э"],[[1070,1070],"mapped","ю"],[[1071,1071],"mapped","я"],[[1072,1103],"valid"],[[1104,1104],"valid"],[[1105,1116],"valid"],[[1117,1117],"valid"],[[1118,1119],"valid"],[[1120,1120],"mapped","ѡ"],[[1121,1121],"valid"],[[1122,1122],"mapped","ѣ"],[[1123,1123],"valid"],[[1124,1124],"mapped","ѥ"],[[1125,1125],"valid"],[[1126,1126],"mapped","ѧ"],[[1127,1127],"valid"],[[1128,1128],"mapped","ѩ"],[[1129,1129],"valid"],[[1130,1130],"mapped","ѫ"],[[1131,1131],"valid"],[[1132,1132],"mapped","ѭ"],[[1133,1133],"valid"],[[1134,1134],"mapped","ѯ"],[[1135,1135],"valid"],[[1136,1136],"mapped","ѱ"],[[1137,1137],"valid"],[[1138,1138],"mapped","ѳ"],[[1139,1139],"valid"],[[1140,1140],"mapped","ѵ"],[[1141,1141],"valid"],[[1142,1142],"mapped","ѷ"],[[1143,1143],"valid"],[[1144,1144],"mapped","ѹ"],[[1145,1145],"valid"],[[1146,1146],"mapped","ѻ"],[[1147,1147],"valid"],[[1148,1148],"mapped","ѽ"],[[1149,1149],"valid"],[[1150,1150],"mapped","ѿ"],[[1151,1151],"valid"],[[1152,1152],"mapped","ҁ"],[[1153,1153],"valid"],[[1154,1154],"valid","","NV8"],[[1155,1158],"valid"],[[1159,1159],"valid"],[[1160,1161],"valid","","NV8"],[[1162,1162],"mapped","ҋ"],[[1163,1163],"valid"],[[1164,1164],"mapped","ҍ"],[[1165,1165],"valid"],[[1166,1166],"mapped","ҏ"],[[1167,1167],"valid"],[[1168,1168],"mapped","ґ"],[[1169,1169],"valid"],[[1170,1170],"mapped","ғ"],[[1171,1171],"valid"],[[1172,1172],"mapped","ҕ"],[[1173,1173],"valid"],[[1174,1174],"mapped","җ"],[[1175,1175],"valid"],[[1176,1176],"mapped","ҙ"],[[1177,1177],"valid"],[[1178,1178],"mapped","қ"],[[1179,1179],"valid"],[[1180,1180],"mapped","ҝ"],[[1181,1181],"valid"],[[1182,1182],"mapped","ҟ"],[[1183,1183],"valid"],[[1184,1184],"mapped","ҡ"],[[1185,1185],"valid"],[[1186,1186],"mapped","ң"],[[1187,1187],"valid"],[[1188,1188],"mapped","ҥ"],[[1189,1189],"valid"],[[1190,1190],"mapped","ҧ"],[[1191,1191],"valid"],[[1192,1192],"mapped","ҩ"],[[1193,1193],"valid"],[[1194,1194],"mapped","ҫ"],[[1195,1195],"valid"],[[1196,1196],"mapped","ҭ"],[[1197,1197],"valid"],[[1198,1198],"mapped","ү"],[[1199,1199],"valid"],[[1200,1200],"mapped","ұ"],[[1201,1201],"valid"],[[1202,1202],"mapped","ҳ"],[[1203,1203],"valid"],[[1204,1204],"mapped","ҵ"],[[1205,1205],"valid"],[[1206,1206],"mapped","ҷ"],[[1207,1207],"valid"],[[1208,1208],"mapped","ҹ"],[[1209,1209],"valid"],[[1210,1210],"mapped","һ"],[[1211,1211],"valid"],[[1212,1212],"mapped","ҽ"],[[1213,1213],"valid"],[[1214,1214],"mapped","ҿ"],[[1215,1215],"valid"],[[1216,1216],"disallowed"],[[1217,1217],"mapped","ӂ"],[[1218,1218],"valid"],[[1219,1219],"mapped","ӄ"],[[1220,1220],"valid"],[[1221,1221],"mapped","ӆ"],[[1222,1222],"valid"],[[1223,1223],"mapped","ӈ"],[[1224,1224],"valid"],[[1225,1225],"mapped","ӊ"],[[1226,1226],"valid"],[[1227,1227],"mapped","ӌ"],[[1228,1228],"valid"],[[1229,1229],"mapped","ӎ"],[[1230,1230],"valid"],[[1231,1231],"valid"],[[1232,1232],"mapped","ӑ"],[[1233,1233],"valid"],[[1234,1234],"mapped","ӓ"],[[1235,1235],"valid"],[[1236,1236],"mapped","ӕ"],[[1237,1237],"valid"],[[1238,1238],"mapped","ӗ"],[[1239,1239],"valid"],[[1240,1240],"mapped","ә"],[[1241,1241],"valid"],[[1242,1242],"mapped","ӛ"],[[1243,1243],"valid"],[[1244,1244],"mapped","ӝ"],[[1245,1245],"valid"],[[1246,1246],"mapped","ӟ"],[[1247,1247],"valid"],[[1248,1248],"mapped","ӡ"],[[1249,1249],"valid"],[[1250,1250],"mapped","ӣ"],[[1251,1251],"valid"],[[1252,1252],"mapped","ӥ"],[[1253,1253],"valid"],[[1254,1254],"mapped","ӧ"],[[1255,1255],"valid"],[[1256,1256],"mapped","ө"],[[1257,1257],"valid"],[[1258,1258],"mapped","ӫ"],[[1259,1259],"valid"],[[1260,1260],"mapped","ӭ"],[[1261,1261],"valid"],[[1262,1262],"mapped","ӯ"],[[1263,1263],"valid"],[[1264,1264],"mapped","ӱ"],[[1265,1265],"valid"],[[1266,1266],"mapped","ӳ"],[[1267,1267],"valid"],[[1268,1268],"mapped","ӵ"],[[1269,1269],"valid"],[[1270,1270],"mapped","ӷ"],[[1271,1271],"valid"],[[1272,1272],"mapped","ӹ"],[[1273,1273],"valid"],[[1274,1274],"mapped","ӻ"],[[1275,1275],"valid"],[[1276,1276],"mapped","ӽ"],[[1277,1277],"valid"],[[1278,1278],"mapped","ӿ"],[[1279,1279],"valid"],[[1280,1280],"mapped","ԁ"],[[1281,1281],"valid"],[[1282,1282],"mapped","ԃ"],[[1283,1283],"valid"],[[1284,1284],"mapped","ԅ"],[[1285,1285],"valid"],[[1286,1286],"mapped","ԇ"],[[1287,1287],"valid"],[[1288,1288],"mapped","ԉ"],[[1289,1289],"valid"],[[1290,1290],"mapped","ԋ"],[[1291,1291],"valid"],[[1292,1292],"mapped","ԍ"],[[1293,1293],"valid"],[[1294,1294],"mapped","ԏ"],[[1295,1295],"valid"],[[1296,1296],"mapped","ԑ"],[[1297,1297],"valid"],[[1298,1298],"mapped","ԓ"],[[1299,1299],"valid"],[[1300,1300],"mapped","ԕ"],[[1301,1301],"valid"],[[1302,1302],"mapped","ԗ"],[[1303,1303],"valid"],[[1304,1304],"mapped","ԙ"],[[1305,1305],"valid"],[[1306,1306],"mapped","ԛ"],[[1307,1307],"valid"],[[1308,1308],"mapped","ԝ"],[[1309,1309],"valid"],[[1310,1310],"mapped","ԟ"],[[1311,1311],"valid"],[[1312,1312],"mapped","ԡ"],[[1313,1313],"valid"],[[1314,1314],"mapped","ԣ"],[[1315,1315],"valid"],[[1316,1316],"mapped","ԥ"],[[1317,1317],"valid"],[[1318,1318],"mapped","ԧ"],[[1319,1319],"valid"],[[1320,1320],"mapped","ԩ"],[[1321,1321],"valid"],[[1322,1322],"mapped","ԫ"],[[1323,1323],"valid"],[[1324,1324],"mapped","ԭ"],[[1325,1325],"valid"],[[1326,1326],"mapped","ԯ"],[[1327,1327],"valid"],[[1328,1328],"disallowed"],[[1329,1329],"mapped","ա"],[[1330,1330],"mapped","բ"],[[1331,1331],"mapped","գ"],[[1332,1332],"mapped","դ"],[[1333,1333],"mapped","ե"],[[1334,1334],"mapped","զ"],[[1335,1335],"mapped","է"],[[1336,1336],"mapped","ը"],[[1337,1337],"mapped","թ"],[[1338,1338],"mapped","ժ"],[[1339,1339],"mapped","ի"],[[1340,1340],"mapped","լ"],[[1341,1341],"mapped","խ"],[[1342,1342],"mapped","ծ"],[[1343,1343],"mapped","կ"],[[1344,1344],"mapped","հ"],[[1345,1345],"mapped","ձ"],[[1346,1346],"mapped","ղ"],[[1347,1347],"mapped","ճ"],[[1348,1348],"mapped","մ"],[[1349,1349],"mapped","յ"],[[1350,1350],"mapped","ն"],[[1351,1351],"mapped","շ"],[[1352,1352],"mapped","ո"],[[1353,1353],"mapped","չ"],[[1354,1354],"mapped","պ"],[[1355,1355],"mapped","ջ"],[[1356,1356],"mapped","ռ"],[[1357,1357],"mapped","ս"],[[1358,1358],"mapped","վ"],[[1359,1359],"mapped","տ"],[[1360,1360],"mapped","ր"],[[1361,1361],"mapped","ց"],[[1362,1362],"mapped","ւ"],[[1363,1363],"mapped","փ"],[[1364,1364],"mapped","ք"],[[1365,1365],"mapped","օ"],[[1366,1366],"mapped","ֆ"],[[1367,1368],"disallowed"],[[1369,1369],"valid"],[[1370,1375],"valid","","NV8"],[[1376,1376],"disallowed"],[[1377,1414],"valid"],[[1415,1415],"mapped","եւ"],[[1416,1416],"disallowed"],[[1417,1417],"valid","","NV8"],[[1418,1418],"valid","","NV8"],[[1419,1420],"disallowed"],[[1421,1422],"valid","","NV8"],[[1423,1423],"valid","","NV8"],[[1424,1424],"disallowed"],[[1425,1441],"valid"],[[1442,1442],"valid"],[[1443,1455],"valid"],[[1456,1465],"valid"],[[1466,1466],"valid"],[[1467,1469],"valid"],[[1470,1470],"valid","","NV8"],[[1471,1471],"valid"],[[1472,1472],"valid","","NV8"],[[1473,1474],"valid"],[[1475,1475],"valid","","NV8"],[[1476,1476],"valid"],[[1477,1477],"valid"],[[1478,1478],"valid","","NV8"],[[1479,1479],"valid"],[[1480,1487],"disallowed"],[[1488,1514],"valid"],[[1515,1519],"disallowed"],[[1520,1524],"valid"],[[1525,1535],"disallowed"],[[1536,1539],"disallowed"],[[1540,1540],"disallowed"],[[1541,1541],"disallowed"],[[1542,1546],"valid","","NV8"],[[1547,1547],"valid","","NV8"],[[1548,1548],"valid","","NV8"],[[1549,1551],"valid","","NV8"],[[1552,1557],"valid"],[[1558,1562],"valid"],[[1563,1563],"valid","","NV8"],[[1564,1564],"disallowed"],[[1565,1565],"disallowed"],[[1566,1566],"valid","","NV8"],[[1567,1567],"valid","","NV8"],[[1568,1568],"valid"],[[1569,1594],"valid"],[[1595,1599],"valid"],[[1600,1600],"valid","","NV8"],[[1601,1618],"valid"],[[1619,1621],"valid"],[[1622,1624],"valid"],[[1625,1630],"valid"],[[1631,1631],"valid"],[[1632,1641],"valid"],[[1642,1645],"valid","","NV8"],[[1646,1647],"valid"],[[1648,1652],"valid"],[[1653,1653],"mapped","اٴ"],[[1654,1654],"mapped","وٴ"],[[1655,1655],"mapped","ۇٴ"],[[1656,1656],"mapped","يٴ"],[[1657,1719],"valid"],[[1720,1721],"valid"],[[1722,1726],"valid"],[[1727,1727],"valid"],[[1728,1742],"valid"],[[1743,1743],"valid"],[[1744,1747],"valid"],[[1748,1748],"valid","","NV8"],[[1749,1756],"valid"],[[1757,1757],"disallowed"],[[1758,1758],"valid","","NV8"],[[1759,1768],"valid"],[[1769,1769],"valid","","NV8"],[[1770,1773],"valid"],[[1774,1775],"valid"],[[1776,1785],"valid"],[[1786,1790],"valid"],[[1791,1791],"valid"],[[1792,1805],"valid","","NV8"],[[1806,1806],"disallowed"],[[1807,1807],"disallowed"],[[1808,1836],"valid"],[[1837,1839],"valid"],[[1840,1866],"valid"],[[1867,1868],"disallowed"],[[1869,1871],"valid"],[[1872,1901],"valid"],[[1902,1919],"valid"],[[1920,1968],"valid"],[[1969,1969],"valid"],[[1970,1983],"disallowed"],[[1984,2037],"valid"],[[2038,2042],"valid","","NV8"],[[2043,2047],"disallowed"],[[2048,2093],"valid"],[[2094,2095],"disallowed"],[[2096,2110],"valid","","NV8"],[[2111,2111],"disallowed"],[[2112,2139],"valid"],[[2140,2141],"disallowed"],[[2142,2142],"valid","","NV8"],[[2143,2143],"disallowed"],[[2144,2154],"valid"],[[2155,2207],"disallowed"],[[2208,2208],"valid"],[[2209,2209],"valid"],[[2210,2220],"valid"],[[2221,2226],"valid"],[[2227,2228],"valid"],[[2229,2229],"disallowed"],[[2230,2237],"valid"],[[2238,2259],"disallowed"],[[2260,2273],"valid"],[[2274,2274],"disallowed"],[[2275,2275],"valid"],[[2276,2302],"valid"],[[2303,2303],"valid"],[[2304,2304],"valid"],[[2305,2307],"valid"],[[2308,2308],"valid"],[[2309,2361],"valid"],[[2362,2363],"valid"],[[2364,2381],"valid"],[[2382,2382],"valid"],[[2383,2383],"valid"],[[2384,2388],"valid"],[[2389,2389],"valid"],[[2390,2391],"valid"],[[2392,2392],"mapped","क़"],[[2393,2393],"mapped","ख़"],[[2394,2394],"mapped","ग़"],[[2395,2395],"mapped","ज़"],[[2396,2396],"mapped","ड़"],[[2397,2397],"mapped","ढ़"],[[2398,2398],"mapped","फ़"],[[2399,2399],"mapped","य़"],[[2400,2403],"valid"],[[2404,2405],"valid","","NV8"],[[2406,2415],"valid"],[[2416,2416],"valid","","NV8"],[[2417,2418],"valid"],[[2419,2423],"valid"],[[2424,2424],"valid"],[[2425,2426],"valid"],[[2427,2428],"valid"],[[2429,2429],"valid"],[[2430,2431],"valid"],[[2432,2432],"valid"],[[2433,2435],"valid"],[[2436,2436],"disallowed"],[[2437,2444],"valid"],[[2445,2446],"disallowed"],[[2447,2448],"valid"],[[2449,2450],"disallowed"],[[2451,2472],"valid"],[[2473,2473],"disallowed"],[[2474,2480],"valid"],[[2481,2481],"disallowed"],[[2482,2482],"valid"],[[2483,2485],"disallowed"],[[2486,2489],"valid"],[[2490,2491],"disallowed"],[[2492,2492],"valid"],[[2493,2493],"valid"],[[2494,2500],"valid"],[[2501,2502],"disallowed"],[[2503,2504],"valid"],[[2505,2506],"disallowed"],[[2507,2509],"valid"],[[2510,2510],"valid"],[[2511,2518],"disallowed"],[[2519,2519],"valid"],[[2520,2523],"disallowed"],[[2524,2524],"mapped","ড়"],[[2525,2525],"mapped","ঢ়"],[[2526,2526],"disallowed"],[[2527,2527],"mapped","য়"],[[2528,2531],"valid"],[[2532,2533],"disallowed"],[[2534,2545],"valid"],[[2546,2554],"valid","","NV8"],[[2555,2555],"valid","","NV8"],[[2556,2556],"valid"],[[2557,2557],"valid","","NV8"],[[2558,2560],"disallowed"],[[2561,2561],"valid"],[[2562,2562],"valid"],[[2563,2563],"valid"],[[2564,2564],"disallowed"],[[2565,2570],"valid"],[[2571,2574],"disallowed"],[[2575,2576],"valid"],[[2577,2578],"disallowed"],[[2579,2600],"valid"],[[2601,2601],"disallowed"],[[2602,2608],"valid"],[[2609,2609],"disallowed"],[[2610,2610],"valid"],[[2611,2611],"mapped","ਲ਼"],[[2612,2612],"disallowed"],[[2613,2613],"valid"],[[2614,2614],"mapped","ਸ਼"],[[2615,2615],"disallowed"],[[2616,2617],"valid"],[[2618,2619],"disallowed"],[[2620,2620],"valid"],[[2621,2621],"disallowed"],[[2622,2626],"valid"],[[2627,2630],"disallowed"],[[2631,2632],"valid"],[[2633,2634],"disallowed"],[[2635,2637],"valid"],[[2638,2640],"disallowed"],[[2641,2641],"valid"],[[2642,2648],"disallowed"],[[2649,2649],"mapped","ਖ਼"],[[2650,2650],"mapped","ਗ਼"],[[2651,2651],"mapped","ਜ਼"],[[2652,2652],"valid"],[[2653,2653],"disallowed"],[[2654,2654],"mapped","ਫ਼"],[[2655,2661],"disallowed"],[[2662,2676],"valid"],[[2677,2677],"valid"],[[2678,2688],"disallowed"],[[2689,2691],"valid"],[[2692,2692],"disallowed"],[[2693,2699],"valid"],[[2700,2700],"valid"],[[2701,2701],"valid"],[[2702,2702],"disallowed"],[[2703,2705],"valid"],[[2706,2706],"disallowed"],[[2707,2728],"valid"],[[2729,2729],"disallowed"],[[2730,2736],"valid"],[[2737,2737],"disallowed"],[[2738,2739],"valid"],[[2740,2740],"disallowed"],[[2741,2745],"valid"],[[2746,2747],"disallowed"],[[2748,2757],"valid"],[[2758,2758],"disallowed"],[[2759,2761],"valid"],[[2762,2762],"disallowed"],[[2763,2765],"valid"],[[2766,2767],"disallowed"],[[2768,2768],"valid"],[[2769,2783],"disallowed"],[[2784,2784],"valid"],[[2785,2787],"valid"],[[2788,2789],"disallowed"],[[2790,2799],"valid"],[[2800,2800],"valid","","NV8"],[[2801,2801],"valid","","NV8"],[[2802,2808],"disallowed"],[[2809,2809],"valid"],[[2810,2815],"valid"],[[2816,2816],"disallowed"],[[2817,2819],"valid"],[[2820,2820],"disallowed"],[[2821,2828],"valid"],[[2829,2830],"disallowed"],[[2831,2832],"valid"],[[2833,2834],"disallowed"],[[2835,2856],"valid"],[[2857,2857],"disallowed"],[[2858,2864],"valid"],[[2865,2865],"disallowed"],[[2866,2867],"valid"],[[2868,2868],"disallowed"],[[2869,2869],"valid"],[[2870,2873],"valid"],[[2874,2875],"disallowed"],[[2876,2883],"valid"],[[2884,2884],"valid"],[[2885,2886],"disallowed"],[[2887,2888],"valid"],[[2889,2890],"disallowed"],[[2891,2893],"valid"],[[2894,2901],"disallowed"],[[2902,2903],"valid"],[[2904,2907],"disallowed"],[[2908,2908],"mapped","ଡ଼"],[[2909,2909],"mapped","ଢ଼"],[[2910,2910],"disallowed"],[[2911,2913],"valid"],[[2914,2915],"valid"],[[2916,2917],"disallowed"],[[2918,2927],"valid"],[[2928,2928],"valid","","NV8"],[[2929,2929],"valid"],[[2930,2935],"valid","","NV8"],[[2936,2945],"disallowed"],[[2946,2947],"valid"],[[2948,2948],"disallowed"],[[2949,2954],"valid"],[[2955,2957],"disallowed"],[[2958,2960],"valid"],[[2961,2961],"disallowed"],[[2962,2965],"valid"],[[2966,2968],"disallowed"],[[2969,2970],"valid"],[[2971,2971],"disallowed"],[[2972,2972],"valid"],[[2973,2973],"disallowed"],[[2974,2975],"valid"],[[2976,2978],"disallowed"],[[2979,2980],"valid"],[[2981,2983],"disallowed"],[[2984,2986],"valid"],[[2987,2989],"disallowed"],[[2990,2997],"valid"],[[2998,2998],"valid"],[[2999,3001],"valid"],[[3002,3005],"disallowed"],[[3006,3010],"valid"],[[3011,3013],"disallowed"],[[3014,3016],"valid"],[[3017,3017],"disallowed"],[[3018,3021],"valid"],[[3022,3023],"disallowed"],[[3024,3024],"valid"],[[3025,3030],"disallowed"],[[3031,3031],"valid"],[[3032,3045],"disallowed"],[[3046,3046],"valid"],[[3047,3055],"valid"],[[3056,3058],"valid","","NV8"],[[3059,3066],"valid","","NV8"],[[3067,3071],"disallowed"],[[3072,3072],"valid"],[[3073,3075],"valid"],[[3076,3076],"disallowed"],[[3077,3084],"valid"],[[3085,3085],"disallowed"],[[3086,3088],"valid"],[[3089,3089],"disallowed"],[[3090,3112],"valid"],[[3113,3113],"disallowed"],[[3114,3123],"valid"],[[3124,3124],"valid"],[[3125,3129],"valid"],[[3130,3132],"disallowed"],[[3133,3133],"valid"],[[3134,3140],"valid"],[[3141,3141],"disallowed"],[[3142,3144],"valid"],[[3145,3145],"disallowed"],[[3146,3149],"valid"],[[3150,3156],"disallowed"],[[3157,3158],"valid"],[[3159,3159],"disallowed"],[[3160,3161],"valid"],[[3162,3162],"valid"],[[3163,3167],"disallowed"],[[3168,3169],"valid"],[[3170,3171],"valid"],[[3172,3173],"disallowed"],[[3174,3183],"valid"],[[3184,3191],"disallowed"],[[3192,3199],"valid","","NV8"],[[3200,3200],"valid"],[[3201,3201],"valid"],[[3202,3203],"valid"],[[3204,3204],"disallowed"],[[3205,3212],"valid"],[[3213,3213],"disallowed"],[[3214,3216],"valid"],[[3217,3217],"disallowed"],[[3218,3240],"valid"],[[3241,3241],"disallowed"],[[3242,3251],"valid"],[[3252,3252],"disallowed"],[[3253,3257],"valid"],[[3258,3259],"disallowed"],[[3260,3261],"valid"],[[3262,3268],"valid"],[[3269,3269],"disallowed"],[[3270,3272],"valid"],[[3273,3273],"disallowed"],[[3274,3277],"valid"],[[3278,3284],"disallowed"],[[3285,3286],"valid"],[[3287,3293],"disallowed"],[[3294,3294],"valid"],[[3295,3295],"disallowed"],[[3296,3297],"valid"],[[3298,3299],"valid"],[[3300,3301],"disallowed"],[[3302,3311],"valid"],[[3312,3312],"disallowed"],[[3313,3314],"valid"],[[3315,3327],"disallowed"],[[3328,3328],"valid"],[[3329,3329],"valid"],[[3330,3331],"valid"],[[3332,3332],"disallowed"],[[3333,3340],"valid"],[[3341,3341],"disallowed"],[[3342,3344],"valid"],[[3345,3345],"disallowed"],[[3346,3368],"valid"],[[3369,3369],"valid"],[[3370,3385],"valid"],[[3386,3386],"valid"],[[3387,3388],"valid"],[[3389,3389],"valid"],[[3390,3395],"valid"],[[3396,3396],"valid"],[[3397,3397],"disallowed"],[[3398,3400],"valid"],[[3401,3401],"disallowed"],[[3402,3405],"valid"],[[3406,3406],"valid"],[[3407,3407],"valid","","NV8"],[[3408,3411],"disallowed"],[[3412,3414],"valid"],[[3415,3415],"valid"],[[3416,3422],"valid","","NV8"],[[3423,3423],"valid"],[[3424,3425],"valid"],[[3426,3427],"valid"],[[3428,3429],"disallowed"],[[3430,3439],"valid"],[[3440,3445],"valid","","NV8"],[[3446,3448],"valid","","NV8"],[[3449,3449],"valid","","NV8"],[[3450,3455],"valid"],[[3456,3457],"disallowed"],[[3458,3459],"valid"],[[3460,3460],"disallowed"],[[3461,3478],"valid"],[[3479,3481],"disallowed"],[[3482,3505],"valid"],[[3506,3506],"disallowed"],[[3507,3515],"valid"],[[3516,3516],"disallowed"],[[3517,3517],"valid"],[[3518,3519],"disallowed"],[[3520,3526],"valid"],[[3527,3529],"disallowed"],[[3530,3530],"valid"],[[3531,3534],"disallowed"],[[3535,3540],"valid"],[[3541,3541],"disallowed"],[[3542,3542],"valid"],[[3543,3543],"disallowed"],[[3544,3551],"valid"],[[3552,3557],"disallowed"],[[3558,3567],"valid"],[[3568,3569],"disallowed"],[[3570,3571],"valid"],[[3572,3572],"valid","","NV8"],[[3573,3584],"disallowed"],[[3585,3634],"valid"],[[3635,3635],"mapped","ํา"],[[3636,3642],"valid"],[[3643,3646],"disallowed"],[[3647,3647],"valid","","NV8"],[[3648,3662],"valid"],[[3663,3663],"valid","","NV8"],[[3664,3673],"valid"],[[3674,3675],"valid","","NV8"],[[3676,3712],"disallowed"],[[3713,3714],"valid"],[[3715,3715],"disallowed"],[[3716,3716],"valid"],[[3717,3718],"disallowed"],[[3719,3720],"valid"],[[3721,3721],"disallowed"],[[3722,3722],"valid"],[[3723,3724],"disallowed"],[[3725,3725],"valid"],[[3726,3731],"disallowed"],[[3732,3735],"valid"],[[3736,3736],"disallowed"],[[3737,3743],"valid"],[[3744,3744],"disallowed"],[[3745,3747],"valid"],[[3748,3748],"disallowed"],[[3749,3749],"valid"],[[3750,3750],"disallowed"],[[3751,3751],"valid"],[[3752,3753],"disallowed"],[[3754,3755],"valid"],[[3756,3756],"disallowed"],[[3757,3762],"valid"],[[3763,3763],"mapped","ໍາ"],[[3764,3769],"valid"],[[3770,3770],"disallowed"],[[3771,3773],"valid"],[[3774,3775],"disallowed"],[[3776,3780],"valid"],[[3781,3781],"disallowed"],[[3782,3782],"valid"],[[3783,3783],"disallowed"],[[3784,3789],"valid"],[[3790,3791],"disallowed"],[[3792,3801],"valid"],[[3802,3803],"disallowed"],[[3804,3804],"mapped","ຫນ"],[[3805,3805],"mapped","ຫມ"],[[3806,3807],"valid"],[[3808,3839],"disallowed"],[[3840,3840],"valid"],[[3841,3850],"valid","","NV8"],[[3851,3851],"valid"],[[3852,3852],"mapped","་"],[[3853,3863],"valid","","NV8"],[[3864,3865],"valid"],[[3866,3871],"valid","","NV8"],[[3872,3881],"valid"],[[3882,3892],"valid","","NV8"],[[3893,3893],"valid"],[[3894,3894],"valid","","NV8"],[[3895,3895],"valid"],[[3896,3896],"valid","","NV8"],[[3897,3897],"valid"],[[3898,3901],"valid","","NV8"],[[3902,3906],"valid"],[[3907,3907],"mapped","གྷ"],[[3908,3911],"valid"],[[3912,3912],"disallowed"],[[3913,3916],"valid"],[[3917,3917],"mapped","ཌྷ"],[[3918,3921],"valid"],[[3922,3922],"mapped","དྷ"],[[3923,3926],"valid"],[[3927,3927],"mapped","བྷ"],[[3928,3931],"valid"],[[3932,3932],"mapped","ཛྷ"],[[3933,3944],"valid"],[[3945,3945],"mapped","ཀྵ"],[[3946,3946],"valid"],[[3947,3948],"valid"],[[3949,3952],"disallowed"],[[3953,3954],"valid"],[[3955,3955],"mapped","ཱི"],[[3956,3956],"valid"],[[3957,3957],"mapped","ཱུ"],[[3958,3958],"mapped","ྲྀ"],[[3959,3959],"mapped","ྲཱྀ"],[[3960,3960],"mapped","ླྀ"],[[3961,3961],"mapped","ླཱྀ"],[[3962,3968],"valid"],[[3969,3969],"mapped","ཱྀ"],[[3970,3972],"valid"],[[3973,3973],"valid","","NV8"],[[3974,3979],"valid"],[[3980,3983],"valid"],[[3984,3986],"valid"],[[3987,3987],"mapped","ྒྷ"],[[3988,3989],"valid"],[[3990,3990],"valid"],[[3991,3991],"valid"],[[3992,3992],"disallowed"],[[3993,3996],"valid"],[[3997,3997],"mapped","ྜྷ"],[[3998,4001],"valid"],[[4002,4002],"mapped","ྡྷ"],[[4003,4006],"valid"],[[4007,4007],"mapped","ྦྷ"],[[4008,4011],"valid"],[[4012,4012],"mapped","ྫྷ"],[[4013,4013],"valid"],[[4014,4016],"valid"],[[4017,4023],"valid"],[[4024,4024],"valid"],[[4025,4025],"mapped","ྐྵ"],[[4026,4028],"valid"],[[4029,4029],"disallowed"],[[4030,4037],"valid","","NV8"],[[4038,4038],"valid"],[[4039,4044],"valid","","NV8"],[[4045,4045],"disallowed"],[[4046,4046],"valid","","NV8"],[[4047,4047],"valid","","NV8"],[[4048,4049],"valid","","NV8"],[[4050,4052],"valid","","NV8"],[[4053,4056],"valid","","NV8"],[[4057,4058],"valid","","NV8"],[[4059,4095],"disallowed"],[[4096,4129],"valid"],[[4130,4130],"valid"],[[4131,4135],"valid"],[[4136,4136],"valid"],[[4137,4138],"valid"],[[4139,4139],"valid"],[[4140,4146],"valid"],[[4147,4149],"valid"],[[4150,4153],"valid"],[[4154,4159],"valid"],[[4160,4169],"valid"],[[4170,4175],"valid","","NV8"],[[4176,4185],"valid"],[[4186,4249],"valid"],[[4250,4253],"valid"],[[4254,4255],"valid","","NV8"],[[4256,4293],"disallowed"],[[4294,4294],"disallowed"],[[4295,4295],"mapped","ⴧ"],[[4296,4300],"disallowed"],[[4301,4301],"mapped","ⴭ"],[[4302,4303],"disallowed"],[[4304,4342],"valid"],[[4343,4344],"valid"],[[4345,4346],"valid"],[[4347,4347],"valid","","NV8"],[[4348,4348],"mapped","ნ"],[[4349,4351],"valid"],[[4352,4441],"valid","","NV8"],[[4442,4446],"valid","","NV8"],[[4447,4448],"disallowed"],[[4449,4514],"valid","","NV8"],[[4515,4519],"valid","","NV8"],[[4520,4601],"valid","","NV8"],[[4602,4607],"valid","","NV8"],[[4608,4614],"valid"],[[4615,4615],"valid"],[[4616,4678],"valid"],[[4679,4679],"valid"],[[4680,4680],"valid"],[[4681,4681],"disallowed"],[[4682,4685],"valid"],[[4686,4687],"disallowed"],[[4688,4694],"valid"],[[4695,4695],"disallowed"],[[4696,4696],"valid"],[[4697,4697],"disallowed"],[[4698,4701],"valid"],[[4702,4703],"disallowed"],[[4704,4742],"valid"],[[4743,4743],"valid"],[[4744,4744],"valid"],[[4745,4745],"disallowed"],[[4746,4749],"valid"],[[4750,4751],"disallowed"],[[4752,4782],"valid"],[[4783,4783],"valid"],[[4784,4784],"valid"],[[4785,4785],"disallowed"],[[4786,4789],"valid"],[[4790,4791],"disallowed"],[[4792,4798],"valid"],[[4799,4799],"disallowed"],[[4800,4800],"valid"],[[4801,4801],"disallowed"],[[4802,4805],"valid"],[[4806,4807],"disallowed"],[[4808,4814],"valid"],[[4815,4815],"valid"],[[4816,4822],"valid"],[[4823,4823],"disallowed"],[[4824,4846],"valid"],[[4847,4847],"valid"],[[4848,4878],"valid"],[[4879,4879],"valid"],[[4880,4880],"valid"],[[4881,4881],"disallowed"],[[4882,4885],"valid"],[[4886,4887],"disallowed"],[[4888,4894],"valid"],[[4895,4895],"valid"],[[4896,4934],"valid"],[[4935,4935],"valid"],[[4936,4954],"valid"],[[4955,4956],"disallowed"],[[4957,4958],"valid"],[[4959,4959],"valid"],[[4960,4960],"valid","","NV8"],[[4961,4988],"valid","","NV8"],[[4989,4991],"disallowed"],[[4992,5007],"valid"],[[5008,5017],"valid","","NV8"],[[5018,5023],"disallowed"],[[5024,5108],"valid"],[[5109,5109],"valid"],[[5110,5111],"disallowed"],[[5112,5112],"mapped","Ᏸ"],[[5113,5113],"mapped","Ᏹ"],[[5114,5114],"mapped","Ᏺ"],[[5115,5115],"mapped","Ᏻ"],[[5116,5116],"mapped","Ᏼ"],[[5117,5117],"mapped","Ᏽ"],[[5118,5119],"disallowed"],[[5120,5120],"valid","","NV8"],[[5121,5740],"valid"],[[5741,5742],"valid","","NV8"],[[5743,5750],"valid"],[[5751,5759],"valid"],[[5760,5760],"disallowed"],[[5761,5786],"valid"],[[5787,5788],"valid","","NV8"],[[5789,5791],"disallowed"],[[5792,5866],"valid"],[[5867,5872],"valid","","NV8"],[[5873,5880],"valid"],[[5881,5887],"disallowed"],[[5888,5900],"valid"],[[5901,5901],"disallowed"],[[5902,5908],"valid"],[[5909,5919],"disallowed"],[[5920,5940],"valid"],[[5941,5942],"valid","","NV8"],[[5943,5951],"disallowed"],[[5952,5971],"valid"],[[5972,5983],"disallowed"],[[5984,5996],"valid"],[[5997,5997],"disallowed"],[[5998,6000],"valid"],[[6001,6001],"disallowed"],[[6002,6003],"valid"],[[6004,6015],"disallowed"],[[6016,6067],"valid"],[[6068,6069],"disallowed"],[[6070,6099],"valid"],[[6100,6102],"valid","","NV8"],[[6103,6103],"valid"],[[6104,6107],"valid","","NV8"],[[6108,6108],"valid"],[[6109,6109],"valid"],[[6110,6111],"disallowed"],[[6112,6121],"valid"],[[6122,6127],"disallowed"],[[6128,6137],"valid","","NV8"],[[6138,6143],"disallowed"],[[6144,6149],"valid","","NV8"],[[6150,6150],"disallowed"],[[6151,6154],"valid","","NV8"],[[6155,6157],"ignored"],[[6158,6158],"disallowed"],[[6159,6159],"disallowed"],[[6160,6169],"valid"],[[6170,6175],"disallowed"],[[6176,6263],"valid"],[[6264,6271],"disallowed"],[[6272,6313],"valid"],[[6314,6314],"valid"],[[6315,6319],"disallowed"],[[6320,6389],"valid"],[[6390,6399],"disallowed"],[[6400,6428],"valid"],[[6429,6430],"valid"],[[6431,6431],"disallowed"],[[6432,6443],"valid"],[[6444,6447],"disallowed"],[[6448,6459],"valid"],[[6460,6463],"disallowed"],[[6464,6464],"valid","","NV8"],[[6465,6467],"disallowed"],[[6468,6469],"valid","","NV8"],[[6470,6509],"valid"],[[6510,6511],"disallowed"],[[6512,6516],"valid"],[[6517,6527],"disallowed"],[[6528,6569],"valid"],[[6570,6571],"valid"],[[6572,6575],"disallowed"],[[6576,6601],"valid"],[[6602,6607],"disallowed"],[[6608,6617],"valid"],[[6618,6618],"valid","","XV8"],[[6619,6621],"disallowed"],[[6622,6623],"valid","","NV8"],[[6624,6655],"valid","","NV8"],[[6656,6683],"valid"],[[6684,6685],"disallowed"],[[6686,6687],"valid","","NV8"],[[6688,6750],"valid"],[[6751,6751],"disallowed"],[[6752,6780],"valid"],[[6781,6782],"disallowed"],[[6783,6793],"valid"],[[6794,6799],"disallowed"],[[6800,6809],"valid"],[[6810,6815],"disallowed"],[[6816,6822],"valid","","NV8"],[[6823,6823],"valid"],[[6824,6829],"valid","","NV8"],[[6830,6831],"disallowed"],[[6832,6845],"valid"],[[6846,6846],"valid","","NV8"],[[6847,6911],"disallowed"],[[6912,6987],"valid"],[[6988,6991],"disallowed"],[[6992,7001],"valid"],[[7002,7018],"valid","","NV8"],[[7019,7027],"valid"],[[7028,7036],"valid","","NV8"],[[7037,7039],"disallowed"],[[7040,7082],"valid"],[[7083,7085],"valid"],[[7086,7097],"valid"],[[7098,7103],"valid"],[[7104,7155],"valid"],[[7156,7163],"disallowed"],[[7164,7167],"valid","","NV8"],[[7168,7223],"valid"],[[7224,7226],"disallowed"],[[7227,7231],"valid","","NV8"],[[7232,7241],"valid"],[[7242,7244],"disallowed"],[[7245,7293],"valid"],[[7294,7295],"valid","","NV8"],[[7296,7296],"mapped","в"],[[7297,7297],"mapped","д"],[[7298,7298],"mapped","о"],[[7299,7299],"mapped","с"],[[7300,7301],"mapped","т"],[[7302,7302],"mapped","ъ"],[[7303,7303],"mapped","ѣ"],[[7304,7304],"mapped","ꙋ"],[[7305,7359],"disallowed"],[[7360,7367],"valid","","NV8"],[[7368,7375],"disallowed"],[[7376,7378],"valid"],[[7379,7379],"valid","","NV8"],[[7380,7410],"valid"],[[7411,7414],"valid"],[[7415,7415],"valid"],[[7416,7417],"valid"],[[7418,7423],"disallowed"],[[7424,7467],"valid"],[[7468,7468],"mapped","a"],[[7469,7469],"mapped","æ"],[[7470,7470],"mapped","b"],[[7471,7471],"valid"],[[7472,7472],"mapped","d"],[[7473,7473],"mapped","e"],[[7474,7474],"mapped","ǝ"],[[7475,7475],"mapped","g"],[[7476,7476],"mapped","h"],[[7477,7477],"mapped","i"],[[7478,7478],"mapped","j"],[[7479,7479],"mapped","k"],[[7480,7480],"mapped","l"],[[7481,7481],"mapped","m"],[[7482,7482],"mapped","n"],[[7483,7483],"valid"],[[7484,7484],"mapped","o"],[[7485,7485],"mapped","ȣ"],[[7486,7486],"mapped","p"],[[7487,7487],"mapped","r"],[[7488,7488],"mapped","t"],[[7489,7489],"mapped","u"],[[7490,7490],"mapped","w"],[[7491,7491],"mapped","a"],[[7492,7492],"mapped","ɐ"],[[7493,7493],"mapped","ɑ"],[[7494,7494],"mapped","ᴂ"],[[7495,7495],"mapped","b"],[[7496,7496],"mapped","d"],[[7497,7497],"mapped","e"],[[7498,7498],"mapped","ə"],[[7499,7499],"mapped","ɛ"],[[7500,7500],"mapped","ɜ"],[[7501,7501],"mapped","g"],[[7502,7502],"valid"],[[7503,7503],"mapped","k"],[[7504,7504],"mapped","m"],[[7505,7505],"mapped","ŋ"],[[7506,7506],"mapped","o"],[[7507,7507],"mapped","ɔ"],[[7508,7508],"mapped","ᴖ"],[[7509,7509],"mapped","ᴗ"],[[7510,7510],"mapped","p"],[[7511,7511],"mapped","t"],[[7512,7512],"mapped","u"],[[7513,7513],"mapped","ᴝ"],[[7514,7514],"mapped","ɯ"],[[7515,7515],"mapped","v"],[[7516,7516],"mapped","ᴥ"],[[7517,7517],"mapped","β"],[[7518,7518],"mapped","γ"],[[7519,7519],"mapped","δ"],[[7520,7520],"mapped","φ"],[[7521,7521],"mapped","χ"],[[7522,7522],"mapped","i"],[[7523,7523],"mapped","r"],[[7524,7524],"mapped","u"],[[7525,7525],"mapped","v"],[[7526,7526],"mapped","β"],[[7527,7527],"mapped","γ"],[[7528,7528],"mapped","ρ"],[[7529,7529],"mapped","φ"],[[7530,7530],"mapped","χ"],[[7531,7531],"valid"],[[7532,7543],"valid"],[[7544,7544],"mapped","н"],[[7545,7578],"valid"],[[7579,7579],"mapped","ɒ"],[[7580,7580],"mapped","c"],[[7581,7581],"mapped","ɕ"],[[7582,7582],"mapped","ð"],[[7583,7583],"mapped","ɜ"],[[7584,7584],"mapped","f"],[[7585,7585],"mapped","ɟ"],[[7586,7586],"mapped","ɡ"],[[7587,7587],"mapped","ɥ"],[[7588,7588],"mapped","ɨ"],[[7589,7589],"mapped","ɩ"],[[7590,7590],"mapped","ɪ"],[[7591,7591],"mapped","ᵻ"],[[7592,7592],"mapped","ʝ"],[[7593,7593],"mapped","ɭ"],[[7594,7594],"mapped","ᶅ"],[[7595,7595],"mapped","ʟ"],[[7596,7596],"mapped","ɱ"],[[7597,7597],"mapped","ɰ"],[[7598,7598],"mapped","ɲ"],[[7599,7599],"mapped","ɳ"],[[7600,7600],"mapped","ɴ"],[[7601,7601],"mapped","ɵ"],[[7602,7602],"mapped","ɸ"],[[7603,7603],"mapped","ʂ"],[[7604,7604],"mapped","ʃ"],[[7605,7605],"mapped","ƫ"],[[7606,7606],"mapped","ʉ"],[[7607,7607],"mapped","ʊ"],[[7608,7608],"mapped","ᴜ"],[[7609,7609],"mapped","ʋ"],[[7610,7610],"mapped","ʌ"],[[7611,7611],"mapped","z"],[[7612,7612],"mapped","ʐ"],[[7613,7613],"mapped","ʑ"],[[7614,7614],"mapped","ʒ"],[[7615,7615],"mapped","θ"],[[7616,7619],"valid"],[[7620,7626],"valid"],[[7627,7654],"valid"],[[7655,7669],"valid"],[[7670,7673],"valid"],[[7674,7674],"disallowed"],[[7675,7675],"valid"],[[7676,7676],"valid"],[[7677,7677],"valid"],[[7678,7679],"valid"],[[7680,7680],"mapped","ḁ"],[[7681,7681],"valid"],[[7682,7682],"mapped","ḃ"],[[7683,7683],"valid"],[[7684,7684],"mapped","ḅ"],[[7685,7685],"valid"],[[7686,7686],"mapped","ḇ"],[[7687,7687],"valid"],[[7688,7688],"mapped","ḉ"],[[7689,7689],"valid"],[[7690,7690],"mapped","ḋ"],[[7691,7691],"valid"],[[7692,7692],"mapped","ḍ"],[[7693,7693],"valid"],[[7694,7694],"mapped","ḏ"],[[7695,7695],"valid"],[[7696,7696],"mapped","ḑ"],[[7697,7697],"valid"],[[7698,7698],"mapped","ḓ"],[[7699,7699],"valid"],[[7700,7700],"mapped","ḕ"],[[7701,7701],"valid"],[[7702,7702],"mapped","ḗ"],[[7703,7703],"valid"],[[7704,7704],"mapped","ḙ"],[[7705,7705],"valid"],[[7706,7706],"mapped","ḛ"],[[7707,7707],"valid"],[[7708,7708],"mapped","ḝ"],[[7709,7709],"valid"],[[7710,7710],"mapped","ḟ"],[[7711,7711],"valid"],[[7712,7712],"mapped","ḡ"],[[7713,7713],"valid"],[[7714,7714],"mapped","ḣ"],[[7715,7715],"valid"],[[7716,7716],"mapped","ḥ"],[[7717,7717],"valid"],[[7718,7718],"mapped","ḧ"],[[7719,7719],"valid"],[[7720,7720],"mapped","ḩ"],[[7721,7721],"valid"],[[7722,7722],"mapped","ḫ"],[[7723,7723],"valid"],[[7724,7724],"mapped","ḭ"],[[7725,7725],"valid"],[[7726,7726],"mapped","ḯ"],[[7727,7727],"valid"],[[7728,7728],"mapped","ḱ"],[[7729,7729],"valid"],[[7730,7730],"mapped","ḳ"],[[7731,7731],"valid"],[[7732,7732],"mapped","ḵ"],[[7733,7733],"valid"],[[7734,7734],"mapped","ḷ"],[[7735,7735],"valid"],[[7736,7736],"mapped","ḹ"],[[7737,7737],"valid"],[[7738,7738],"mapped","ḻ"],[[7739,7739],"valid"],[[7740,7740],"mapped","ḽ"],[[7741,7741],"valid"],[[7742,7742],"mapped","ḿ"],[[7743,7743],"valid"],[[7744,7744],"mapped","ṁ"],[[7745,7745],"valid"],[[7746,7746],"mapped","ṃ"],[[7747,7747],"valid"],[[7748,7748],"mapped","ṅ"],[[7749,7749],"valid"],[[7750,7750],"mapped","ṇ"],[[7751,7751],"valid"],[[7752,7752],"mapped","ṉ"],[[7753,7753],"valid"],[[7754,7754],"mapped","ṋ"],[[7755,7755],"valid"],[[7756,7756],"mapped","ṍ"],[[7757,7757],"valid"],[[7758,7758],"mapped","ṏ"],[[7759,7759],"valid"],[[7760,7760],"mapped","ṑ"],[[7761,7761],"valid"],[[7762,7762],"mapped","ṓ"],[[7763,7763],"valid"],[[7764,7764],"mapped","ṕ"],[[7765,7765],"valid"],[[7766,7766],"mapped","ṗ"],[[7767,7767],"valid"],[[7768,7768],"mapped","ṙ"],[[7769,7769],"valid"],[[7770,7770],"mapped","ṛ"],[[7771,7771],"valid"],[[7772,7772],"mapped","ṝ"],[[7773,7773],"valid"],[[7774,7774],"mapped","ṟ"],[[7775,7775],"valid"],[[7776,7776],"mapped","ṡ"],[[7777,7777],"valid"],[[7778,7778],"mapped","ṣ"],[[7779,7779],"valid"],[[7780,7780],"mapped","ṥ"],[[7781,7781],"valid"],[[7782,7782],"mapped","ṧ"],[[7783,7783],"valid"],[[7784,7784],"mapped","ṩ"],[[7785,7785],"valid"],[[7786,7786],"mapped","ṫ"],[[7787,7787],"valid"],[[7788,7788],"mapped","ṭ"],[[7789,7789],"valid"],[[7790,7790],"mapped","ṯ"],[[7791,7791],"valid"],[[7792,7792],"mapped","ṱ"],[[7793,7793],"valid"],[[7794,7794],"mapped","ṳ"],[[7795,7795],"valid"],[[7796,7796],"mapped","ṵ"],[[7797,7797],"valid"],[[7798,7798],"mapped","ṷ"],[[7799,7799],"valid"],[[7800,7800],"mapped","ṹ"],[[7801,7801],"valid"],[[7802,7802],"mapped","ṻ"],[[7803,7803],"valid"],[[7804,7804],"mapped","ṽ"],[[7805,7805],"valid"],[[7806,7806],"mapped","ṿ"],[[7807,7807],"valid"],[[7808,7808],"mapped","ẁ"],[[7809,7809],"valid"],[[7810,7810],"mapped","ẃ"],[[7811,7811],"valid"],[[7812,7812],"mapped","ẅ"],[[7813,7813],"valid"],[[7814,7814],"mapped","ẇ"],[[7815,7815],"valid"],[[7816,7816],"mapped","ẉ"],[[7817,7817],"valid"],[[7818,7818],"mapped","ẋ"],[[7819,7819],"valid"],[[7820,7820],"mapped","ẍ"],[[7821,7821],"valid"],[[7822,7822],"mapped","ẏ"],[[7823,7823],"valid"],[[7824,7824],"mapped","ẑ"],[[7825,7825],"valid"],[[7826,7826],"mapped","ẓ"],[[7827,7827],"valid"],[[7828,7828],"mapped","ẕ"],[[7829,7833],"valid"],[[7834,7834],"mapped","aʾ"],[[7835,7835],"mapped","ṡ"],[[7836,7837],"valid"],[[7838,7838],"mapped","ss"],[[7839,7839],"valid"],[[7840,7840],"mapped","ạ"],[[7841,7841],"valid"],[[7842,7842],"mapped","ả"],[[7843,7843],"valid"],[[7844,7844],"mapped","ấ"],[[7845,7845],"valid"],[[7846,7846],"mapped","ầ"],[[7847,7847],"valid"],[[7848,7848],"mapped","ẩ"],[[7849,7849],"valid"],[[7850,7850],"mapped","ẫ"],[[7851,7851],"valid"],[[7852,7852],"mapped","ậ"],[[7853,7853],"valid"],[[7854,7854],"mapped","ắ"],[[7855,7855],"valid"],[[7856,7856],"mapped","ằ"],[[7857,7857],"valid"],[[7858,7858],"mapped","ẳ"],[[7859,7859],"valid"],[[7860,7860],"mapped","ẵ"],[[7861,7861],"valid"],[[7862,7862],"mapped","ặ"],[[7863,7863],"valid"],[[7864,7864],"mapped","ẹ"],[[7865,7865],"valid"],[[7866,7866],"mapped","ẻ"],[[7867,7867],"valid"],[[7868,7868],"mapped","ẽ"],[[7869,7869],"valid"],[[7870,7870],"mapped","ế"],[[7871,7871],"valid"],[[7872,7872],"mapped","ề"],[[7873,7873],"valid"],[[7874,7874],"mapped","ể"],[[7875,7875],"valid"],[[7876,7876],"mapped","ễ"],[[7877,7877],"valid"],[[7878,7878],"mapped","ệ"],[[7879,7879],"valid"],[[7880,7880],"mapped","ỉ"],[[7881,7881],"valid"],[[7882,7882],"mapped","ị"],[[7883,7883],"valid"],[[7884,7884],"mapped","ọ"],[[7885,7885],"valid"],[[7886,7886],"mapped","ỏ"],[[7887,7887],"valid"],[[7888,7888],"mapped","ố"],[[7889,7889],"valid"],[[7890,7890],"mapped","ồ"],[[7891,7891],"valid"],[[7892,7892],"mapped","ổ"],[[7893,7893],"valid"],[[7894,7894],"mapped","ỗ"],[[7895,7895],"valid"],[[7896,7896],"mapped","ộ"],[[7897,7897],"valid"],[[7898,7898],"mapped","ớ"],[[7899,7899],"valid"],[[7900,7900],"mapped","ờ"],[[7901,7901],"valid"],[[7902,7902],"mapped","ở"],[[7903,7903],"valid"],[[7904,7904],"mapped","ỡ"],[[7905,7905],"valid"],[[7906,7906],"mapped","ợ"],[[7907,7907],"valid"],[[7908,7908],"mapped","ụ"],[[7909,7909],"valid"],[[7910,7910],"mapped","ủ"],[[7911,7911],"valid"],[[7912,7912],"mapped","ứ"],[[7913,7913],"valid"],[[7914,7914],"mapped","ừ"],[[7915,7915],"valid"],[[7916,7916],"mapped","ử"],[[7917,7917],"valid"],[[7918,7918],"mapped","ữ"],[[7919,7919],"valid"],[[7920,7920],"mapped","ự"],[[7921,7921],"valid"],[[7922,7922],"mapped","ỳ"],[[7923,7923],"valid"],[[7924,7924],"mapped","ỵ"],[[7925,7925],"valid"],[[7926,7926],"mapped","ỷ"],[[7927,7927],"valid"],[[7928,7928],"mapped","ỹ"],[[7929,7929],"valid"],[[7930,7930],"mapped","ỻ"],[[7931,7931],"valid"],[[7932,7932],"mapped","ỽ"],[[7933,7933],"valid"],[[7934,7934],"mapped","ỿ"],[[7935,7935],"valid"],[[7936,7943],"valid"],[[7944,7944],"mapped","ἀ"],[[7945,7945],"mapped","ἁ"],[[7946,7946],"mapped","ἂ"],[[7947,7947],"mapped","ἃ"],[[7948,7948],"mapped","ἄ"],[[7949,7949],"mapped","ἅ"],[[7950,7950],"mapped","ἆ"],[[7951,7951],"mapped","ἇ"],[[7952,7957],"valid"],[[7958,7959],"disallowed"],[[7960,7960],"mapped","ἐ"],[[7961,7961],"mapped","ἑ"],[[7962,7962],"mapped","ἒ"],[[7963,7963],"mapped","ἓ"],[[7964,7964],"mapped","ἔ"],[[7965,7965],"mapped","ἕ"],[[7966,7967],"disallowed"],[[7968,7975],"valid"],[[7976,7976],"mapped","ἠ"],[[7977,7977],"mapped","ἡ"],[[7978,7978],"mapped","ἢ"],[[7979,7979],"mapped","ἣ"],[[7980,7980],"mapped","ἤ"],[[7981,7981],"mapped","ἥ"],[[7982,7982],"mapped","ἦ"],[[7983,7983],"mapped","ἧ"],[[7984,7991],"valid"],[[7992,7992],"mapped","ἰ"],[[7993,7993],"mapped","ἱ"],[[7994,7994],"mapped","ἲ"],[[7995,7995],"mapped","ἳ"],[[7996,7996],"mapped","ἴ"],[[7997,7997],"mapped","ἵ"],[[7998,7998],"mapped","ἶ"],[[7999,7999],"mapped","ἷ"],[[8000,8005],"valid"],[[8006,8007],"disallowed"],[[8008,8008],"mapped","ὀ"],[[8009,8009],"mapped","ὁ"],[[8010,8010],"mapped","ὂ"],[[8011,8011],"mapped","ὃ"],[[8012,8012],"mapped","ὄ"],[[8013,8013],"mapped","ὅ"],[[8014,8015],"disallowed"],[[8016,8023],"valid"],[[8024,8024],"disallowed"],[[8025,8025],"mapped","ὑ"],[[8026,8026],"disallowed"],[[8027,8027],"mapped","ὓ"],[[8028,8028],"disallowed"],[[8029,8029],"mapped","ὕ"],[[8030,8030],"disallowed"],[[8031,8031],"mapped","ὗ"],[[8032,8039],"valid"],[[8040,8040],"mapped","ὠ"],[[8041,8041],"mapped","ὡ"],[[8042,8042],"mapped","ὢ"],[[8043,8043],"mapped","ὣ"],[[8044,8044],"mapped","ὤ"],[[8045,8045],"mapped","ὥ"],[[8046,8046],"mapped","ὦ"],[[8047,8047],"mapped","ὧ"],[[8048,8048],"valid"],[[8049,8049],"mapped","ά"],[[8050,8050],"valid"],[[8051,8051],"mapped","έ"],[[8052,8052],"valid"],[[8053,8053],"mapped","ή"],[[8054,8054],"valid"],[[8055,8055],"mapped","ί"],[[8056,8056],"valid"],[[8057,8057],"mapped","ό"],[[8058,8058],"valid"],[[8059,8059],"mapped","ύ"],[[8060,8060],"valid"],[[8061,8061],"mapped","ώ"],[[8062,8063],"disallowed"],[[8064,8064],"mapped","ἀι"],[[8065,8065],"mapped","ἁι"],[[8066,8066],"mapped","ἂι"],[[8067,8067],"mapped","ἃι"],[[8068,8068],"mapped","ἄι"],[[8069,8069],"mapped","ἅι"],[[8070,8070],"mapped","ἆι"],[[8071,8071],"mapped","ἇι"],[[8072,8072],"mapped","ἀι"],[[8073,8073],"mapped","ἁι"],[[8074,8074],"mapped","ἂι"],[[8075,8075],"mapped","ἃι"],[[8076,8076],"mapped","ἄι"],[[8077,8077],"mapped","ἅι"],[[8078,8078],"mapped","ἆι"],[[8079,8079],"mapped","ἇι"],[[8080,8080],"mapped","ἠι"],[[8081,8081],"mapped","ἡι"],[[8082,8082],"mapped","ἢι"],[[8083,8083],"mapped","ἣι"],[[8084,8084],"mapped","ἤι"],[[8085,8085],"mapped","ἥι"],[[8086,8086],"mapped","ἦι"],[[8087,8087],"mapped","ἧι"],[[8088,8088],"mapped","ἠι"],[[8089,8089],"mapped","ἡι"],[[8090,8090],"mapped","ἢι"],[[8091,8091],"mapped","ἣι"],[[8092,8092],"mapped","ἤι"],[[8093,8093],"mapped","ἥι"],[[8094,8094],"mapped","ἦι"],[[8095,8095],"mapped","ἧι"],[[8096,8096],"mapped","ὠι"],[[8097,8097],"mapped","ὡι"],[[8098,8098],"mapped","ὢι"],[[8099,8099],"mapped","ὣι"],[[8100,8100],"mapped","ὤι"],[[8101,8101],"mapped","ὥι"],[[8102,8102],"mapped","ὦι"],[[8103,8103],"mapped","ὧι"],[[8104,8104],"mapped","ὠι"],[[8105,8105],"mapped","ὡι"],[[8106,8106],"mapped","ὢι"],[[8107,8107],"mapped","ὣι"],[[8108,8108],"mapped","ὤι"],[[8109,8109],"mapped","ὥι"],[[8110,8110],"mapped","ὦι"],[[8111,8111],"mapped","ὧι"],[[8112,8113],"valid"],[[8114,8114],"mapped","ὰι"],[[8115,8115],"mapped","αι"],[[8116,8116],"mapped","άι"],[[8117,8117],"disallowed"],[[8118,8118],"valid"],[[8119,8119],"mapped","ᾶι"],[[8120,8120],"mapped","ᾰ"],[[8121,8121],"mapped","ᾱ"],[[8122,8122],"mapped","ὰ"],[[8123,8123],"mapped","ά"],[[8124,8124],"mapped","αι"],[[8125,8125],"disallowed_STD3_mapped"," ̓"],[[8126,8126],"mapped","ι"],[[8127,8127],"disallowed_STD3_mapped"," ̓"],[[8128,8128],"disallowed_STD3_mapped"," ͂"],[[8129,8129],"disallowed_STD3_mapped"," ̈͂"],[[8130,8130],"mapped","ὴι"],[[8131,8131],"mapped","ηι"],[[8132,8132],"mapped","ήι"],[[8133,8133],"disallowed"],[[8134,8134],"valid"],[[8135,8135],"mapped","ῆι"],[[8136,8136],"mapped","ὲ"],[[8137,8137],"mapped","έ"],[[8138,8138],"mapped","ὴ"],[[8139,8139],"mapped","ή"],[[8140,8140],"mapped","ηι"],[[8141,8141],"disallowed_STD3_mapped"," ̓̀"],[[8142,8142],"disallowed_STD3_mapped"," ̓́"],[[8143,8143],"disallowed_STD3_mapped"," ̓͂"],[[8144,8146],"valid"],[[8147,8147],"mapped","ΐ"],[[8148,8149],"disallowed"],[[8150,8151],"valid"],[[8152,8152],"mapped","ῐ"],[[8153,8153],"mapped","ῑ"],[[8154,8154],"mapped","ὶ"],[[8155,8155],"mapped","ί"],[[8156,8156],"disallowed"],[[8157,8157],"disallowed_STD3_mapped"," ̔̀"],[[8158,8158],"disallowed_STD3_mapped"," ̔́"],[[8159,8159],"disallowed_STD3_mapped"," ̔͂"],[[8160,8162],"valid"],[[8163,8163],"mapped","ΰ"],[[8164,8167],"valid"],[[8168,8168],"mapped","ῠ"],[[8169,8169],"mapped","ῡ"],[[8170,8170],"mapped","ὺ"],[[8171,8171],"mapped","ύ"],[[8172,8172],"mapped","ῥ"],[[8173,8173],"disallowed_STD3_mapped"," ̈̀"],[[8174,8174],"disallowed_STD3_mapped"," ̈́"],[[8175,8175],"disallowed_STD3_mapped","`"],[[8176,8177],"disallowed"],[[8178,8178],"mapped","ὼι"],[[8179,8179],"mapped","ωι"],[[8180,8180],"mapped","ώι"],[[8181,8181],"disallowed"],[[8182,8182],"valid"],[[8183,8183],"mapped","ῶι"],[[8184,8184],"mapped","ὸ"],[[8185,8185],"mapped","ό"],[[8186,8186],"mapped","ὼ"],[[8187,8187],"mapped","ώ"],[[8188,8188],"mapped","ωι"],[[8189,8189],"disallowed_STD3_mapped"," ́"],[[8190,8190],"disallowed_STD3_mapped"," ̔"],[[8191,8191],"disallowed"],[[8192,8202],"disallowed_STD3_mapped"," "],[[8203,8203],"ignored"],[[8204,8205],"deviation",""],[[8206,8207],"disallowed"],[[8208,8208],"valid","","NV8"],[[8209,8209],"mapped","‐"],[[8210,8214],"valid","","NV8"],[[8215,8215],"disallowed_STD3_mapped"," ̳"],[[8216,8227],"valid","","NV8"],[[8228,8230],"disallowed"],[[8231,8231],"valid","","NV8"],[[8232,8238],"disallowed"],[[8239,8239],"disallowed_STD3_mapped"," "],[[8240,8242],"valid","","NV8"],[[8243,8243],"mapped","′′"],[[8244,8244],"mapped","′′′"],[[8245,8245],"valid","","NV8"],[[8246,8246],"mapped","‵‵"],[[8247,8247],"mapped","‵‵‵"],[[8248,8251],"valid","","NV8"],[[8252,8252],"disallowed_STD3_mapped","!!"],[[8253,8253],"valid","","NV8"],[[8254,8254],"disallowed_STD3_mapped"," ̅"],[[8255,8262],"valid","","NV8"],[[8263,8263],"disallowed_STD3_mapped","??"],[[8264,8264],"disallowed_STD3_mapped","?!"],[[8265,8265],"disallowed_STD3_mapped","!?"],[[8266,8269],"valid","","NV8"],[[8270,8274],"valid","","NV8"],[[8275,8276],"valid","","NV8"],[[8277,8278],"valid","","NV8"],[[8279,8279],"mapped","′′′′"],[[8280,8286],"valid","","NV8"],[[8287,8287],"disallowed_STD3_mapped"," "],[[8288,8288],"ignored"],[[8289,8291],"disallowed"],[[8292,8292],"ignored"],[[8293,8293],"disallowed"],[[8294,8297],"disallowed"],[[8298,8303],"disallowed"],[[8304,8304],"mapped","0"],[[8305,8305],"mapped","i"],[[8306,8307],"disallowed"],[[8308,8308],"mapped","4"],[[8309,8309],"mapped","5"],[[8310,8310],"mapped","6"],[[8311,8311],"mapped","7"],[[8312,8312],"mapped","8"],[[8313,8313],"mapped","9"],[[8314,8314],"disallowed_STD3_mapped","+"],[[8315,8315],"mapped","−"],[[8316,8316],"disallowed_STD3_mapped","="],[[8317,8317],"disallowed_STD3_mapped","("],[[8318,8318],"disallowed_STD3_mapped",")"],[[8319,8319],"mapped","n"],[[8320,8320],"mapped","0"],[[8321,8321],"mapped","1"],[[8322,8322],"mapped","2"],[[8323,8323],"mapped","3"],[[8324,8324],"mapped","4"],[[8325,8325],"mapped","5"],[[8326,8326],"mapped","6"],[[8327,8327],"mapped","7"],[[8328,8328],"mapped","8"],[[8329,8329],"mapped","9"],[[8330,8330],"disallowed_STD3_mapped","+"],[[8331,8331],"mapped","−"],[[8332,8332],"disallowed_STD3_mapped","="],[[8333,8333],"disallowed_STD3_mapped","("],[[8334,8334],"disallowed_STD3_mapped",")"],[[8335,8335],"disallowed"],[[8336,8336],"mapped","a"],[[8337,8337],"mapped","e"],[[8338,8338],"mapped","o"],[[8339,8339],"mapped","x"],[[8340,8340],"mapped","ə"],[[8341,8341],"mapped","h"],[[8342,8342],"mapped","k"],[[8343,8343],"mapped","l"],[[8344,8344],"mapped","m"],[[8345,8345],"mapped","n"],[[8346,8346],"mapped","p"],[[8347,8347],"mapped","s"],[[8348,8348],"mapped","t"],[[8349,8351],"disallowed"],[[8352,8359],"valid","","NV8"],[[8360,8360],"mapped","rs"],[[8361,8362],"valid","","NV8"],[[8363,8363],"valid","","NV8"],[[8364,8364],"valid","","NV8"],[[8365,8367],"valid","","NV8"],[[8368,8369],"valid","","NV8"],[[8370,8373],"valid","","NV8"],[[8374,8376],"valid","","NV8"],[[8377,8377],"valid","","NV8"],[[8378,8378],"valid","","NV8"],[[8379,8381],"valid","","NV8"],[[8382,8382],"valid","","NV8"],[[8383,8383],"valid","","NV8"],[[8384,8399],"disallowed"],[[8400,8417],"valid","","NV8"],[[8418,8419],"valid","","NV8"],[[8420,8426],"valid","","NV8"],[[8427,8427],"valid","","NV8"],[[8428,8431],"valid","","NV8"],[[8432,8432],"valid","","NV8"],[[8433,8447],"disallowed"],[[8448,8448],"disallowed_STD3_mapped","a/c"],[[8449,8449],"disallowed_STD3_mapped","a/s"],[[8450,8450],"mapped","c"],[[8451,8451],"mapped","°c"],[[8452,8452],"valid","","NV8"],[[8453,8453],"disallowed_STD3_mapped","c/o"],[[8454,8454],"disallowed_STD3_mapped","c/u"],[[8455,8455],"mapped","ɛ"],[[8456,8456],"valid","","NV8"],[[8457,8457],"mapped","°f"],[[8458,8458],"mapped","g"],[[8459,8462],"mapped","h"],[[8463,8463],"mapped","ħ"],[[8464,8465],"mapped","i"],[[8466,8467],"mapped","l"],[[8468,8468],"valid","","NV8"],[[8469,8469],"mapped","n"],[[8470,8470],"mapped","no"],[[8471,8472],"valid","","NV8"],[[8473,8473],"mapped","p"],[[8474,8474],"mapped","q"],[[8475,8477],"mapped","r"],[[8478,8479],"valid","","NV8"],[[8480,8480],"mapped","sm"],[[8481,8481],"mapped","tel"],[[8482,8482],"mapped","tm"],[[8483,8483],"valid","","NV8"],[[8484,8484],"mapped","z"],[[8485,8485],"valid","","NV8"],[[8486,8486],"mapped","ω"],[[8487,8487],"valid","","NV8"],[[8488,8488],"mapped","z"],[[8489,8489],"valid","","NV8"],[[8490,8490],"mapped","k"],[[8491,8491],"mapped","å"],[[8492,8492],"mapped","b"],[[8493,8493],"mapped","c"],[[8494,8494],"valid","","NV8"],[[8495,8496],"mapped","e"],[[8497,8497],"mapped","f"],[[8498,8498],"disallowed"],[[8499,8499],"mapped","m"],[[8500,8500],"mapped","o"],[[8501,8501],"mapped","א"],[[8502,8502],"mapped","ב"],[[8503,8503],"mapped","ג"],[[8504,8504],"mapped","ד"],[[8505,8505],"mapped","i"],[[8506,8506],"valid","","NV8"],[[8507,8507],"mapped","fax"],[[8508,8508],"mapped","π"],[[8509,8510],"mapped","γ"],[[8511,8511],"mapped","π"],[[8512,8512],"mapped","∑"],[[8513,8516],"valid","","NV8"],[[8517,8518],"mapped","d"],[[8519,8519],"mapped","e"],[[8520,8520],"mapped","i"],[[8521,8521],"mapped","j"],[[8522,8523],"valid","","NV8"],[[8524,8524],"valid","","NV8"],[[8525,8525],"valid","","NV8"],[[8526,8526],"valid"],[[8527,8527],"valid","","NV8"],[[8528,8528],"mapped","1⁄7"],[[8529,8529],"mapped","1⁄9"],[[8530,8530],"mapped","1⁄10"],[[8531,8531],"mapped","1⁄3"],[[8532,8532],"mapped","2⁄3"],[[8533,8533],"mapped","1⁄5"],[[8534,8534],"mapped","2⁄5"],[[8535,8535],"mapped","3⁄5"],[[8536,8536],"mapped","4⁄5"],[[8537,8537],"mapped","1⁄6"],[[8538,8538],"mapped","5⁄6"],[[8539,8539],"mapped","1⁄8"],[[8540,8540],"mapped","3⁄8"],[[8541,8541],"mapped","5⁄8"],[[8542,8542],"mapped","7⁄8"],[[8543,8543],"mapped","1⁄"],[[8544,8544],"mapped","i"],[[8545,8545],"mapped","ii"],[[8546,8546],"mapped","iii"],[[8547,8547],"mapped","iv"],[[8548,8548],"mapped","v"],[[8549,8549],"mapped","vi"],[[8550,8550],"mapped","vii"],[[8551,8551],"mapped","viii"],[[8552,8552],"mapped","ix"],[[8553,8553],"mapped","x"],[[8554,8554],"mapped","xi"],[[8555,8555],"mapped","xii"],[[8556,8556],"mapped","l"],[[8557,8557],"mapped","c"],[[8558,8558],"mapped","d"],[[8559,8559],"mapped","m"],[[8560,8560],"mapped","i"],[[8561,8561],"mapped","ii"],[[8562,8562],"mapped","iii"],[[8563,8563],"mapped","iv"],[[8564,8564],"mapped","v"],[[8565,8565],"mapped","vi"],[[8566,8566],"mapped","vii"],[[8567,8567],"mapped","viii"],[[8568,8568],"mapped","ix"],[[8569,8569],"mapped","x"],[[8570,8570],"mapped","xi"],[[8571,8571],"mapped","xii"],[[8572,8572],"mapped","l"],[[8573,8573],"mapped","c"],[[8574,8574],"mapped","d"],[[8575,8575],"mapped","m"],[[8576,8578],"valid","","NV8"],[[8579,8579],"disallowed"],[[8580,8580],"valid"],[[8581,8584],"valid","","NV8"],[[8585,8585],"mapped","0⁄3"],[[8586,8587],"valid","","NV8"],[[8588,8591],"disallowed"],[[8592,8682],"valid","","NV8"],[[8683,8691],"valid","","NV8"],[[8692,8703],"valid","","NV8"],[[8704,8747],"valid","","NV8"],[[8748,8748],"mapped","∫∫"],[[8749,8749],"mapped","∫∫∫"],[[8750,8750],"valid","","NV8"],[[8751,8751],"mapped","∮∮"],[[8752,8752],"mapped","∮∮∮"],[[8753,8799],"valid","","NV8"],[[8800,8800],"disallowed_STD3_valid"],[[8801,8813],"valid","","NV8"],[[8814,8815],"disallowed_STD3_valid"],[[8816,8945],"valid","","NV8"],[[8946,8959],"valid","","NV8"],[[8960,8960],"valid","","NV8"],[[8961,8961],"valid","","NV8"],[[8962,9000],"valid","","NV8"],[[9001,9001],"mapped","〈"],[[9002,9002],"mapped","〉"],[[9003,9082],"valid","","NV8"],[[9083,9083],"valid","","NV8"],[[9084,9084],"valid","","NV8"],[[9085,9114],"valid","","NV8"],[[9115,9166],"valid","","NV8"],[[9167,9168],"valid","","NV8"],[[9169,9179],"valid","","NV8"],[[9180,9191],"valid","","NV8"],[[9192,9192],"valid","","NV8"],[[9193,9203],"valid","","NV8"],[[9204,9210],"valid","","NV8"],[[9211,9214],"valid","","NV8"],[[9215,9215],"valid","","NV8"],[[9216,9252],"valid","","NV8"],[[9253,9254],"valid","","NV8"],[[9255,9279],"disallowed"],[[9280,9290],"valid","","NV8"],[[9291,9311],"disallowed"],[[9312,9312],"mapped","1"],[[9313,9313],"mapped","2"],[[9314,9314],"mapped","3"],[[9315,9315],"mapped","4"],[[9316,9316],"mapped","5"],[[9317,9317],"mapped","6"],[[9318,9318],"mapped","7"],[[9319,9319],"mapped","8"],[[9320,9320],"mapped","9"],[[9321,9321],"mapped","10"],[[9322,9322],"mapped","11"],[[9323,9323],"mapped","12"],[[9324,9324],"mapped","13"],[[9325,9325],"mapped","14"],[[9326,9326],"mapped","15"],[[9327,9327],"mapped","16"],[[9328,9328],"mapped","17"],[[9329,9329],"mapped","18"],[[9330,9330],"mapped","19"],[[9331,9331],"mapped","20"],[[9332,9332],"disallowed_STD3_mapped","(1)"],[[9333,9333],"disallowed_STD3_mapped","(2)"],[[9334,9334],"disallowed_STD3_mapped","(3)"],[[9335,9335],"disallowed_STD3_mapped","(4)"],[[9336,9336],"disallowed_STD3_mapped","(5)"],[[9337,9337],"disallowed_STD3_mapped","(6)"],[[9338,9338],"disallowed_STD3_mapped","(7)"],[[9339,9339],"disallowed_STD3_mapped","(8)"],[[9340,9340],"disallowed_STD3_mapped","(9)"],[[9341,9341],"disallowed_STD3_mapped","(10)"],[[9342,9342],"disallowed_STD3_mapped","(11)"],[[9343,9343],"disallowed_STD3_mapped","(12)"],[[9344,9344],"disallowed_STD3_mapped","(13)"],[[9345,9345],"disallowed_STD3_mapped","(14)"],[[9346,9346],"disallowed_STD3_mapped","(15)"],[[9347,9347],"disallowed_STD3_mapped","(16)"],[[9348,9348],"disallowed_STD3_mapped","(17)"],[[9349,9349],"disallowed_STD3_mapped","(18)"],[[9350,9350],"disallowed_STD3_mapped","(19)"],[[9351,9351],"disallowed_STD3_mapped","(20)"],[[9352,9371],"disallowed"],[[9372,9372],"disallowed_STD3_mapped","(a)"],[[9373,9373],"disallowed_STD3_mapped","(b)"],[[9374,9374],"disallowed_STD3_mapped","(c)"],[[9375,9375],"disallowed_STD3_mapped","(d)"],[[9376,9376],"disallowed_STD3_mapped","(e)"],[[9377,9377],"disallowed_STD3_mapped","(f)"],[[9378,9378],"disallowed_STD3_mapped","(g)"],[[9379,9379],"disallowed_STD3_mapped","(h)"],[[9380,9380],"disallowed_STD3_mapped","(i)"],[[9381,9381],"disallowed_STD3_mapped","(j)"],[[9382,9382],"disallowed_STD3_mapped","(k)"],[[9383,9383],"disallowed_STD3_mapped","(l)"],[[9384,9384],"disallowed_STD3_mapped","(m)"],[[9385,9385],"disallowed_STD3_mapped","(n)"],[[9386,9386],"disallowed_STD3_mapped","(o)"],[[9387,9387],"disallowed_STD3_mapped","(p)"],[[9388,9388],"disallowed_STD3_mapped","(q)"],[[9389,9389],"disallowed_STD3_mapped","(r)"],[[9390,9390],"disallowed_STD3_mapped","(s)"],[[9391,9391],"disallowed_STD3_mapped","(t)"],[[9392,9392],"disallowed_STD3_mapped","(u)"],[[9393,9393],"disallowed_STD3_mapped","(v)"],[[9394,9394],"disallowed_STD3_mapped","(w)"],[[9395,9395],"disallowed_STD3_mapped","(x)"],[[9396,9396],"disallowed_STD3_mapped","(y)"],[[9397,9397],"disallowed_STD3_mapped","(z)"],[[9398,9398],"mapped","a"],[[9399,9399],"mapped","b"],[[9400,9400],"mapped","c"],[[9401,9401],"mapped","d"],[[9402,9402],"mapped","e"],[[9403,9403],"mapped","f"],[[9404,9404],"mapped","g"],[[9405,9405],"mapped","h"],[[9406,9406],"mapped","i"],[[9407,9407],"mapped","j"],[[9408,9408],"mapped","k"],[[9409,9409],"mapped","l"],[[9410,9410],"mapped","m"],[[9411,9411],"mapped","n"],[[9412,9412],"mapped","o"],[[9413,9413],"mapped","p"],[[9414,9414],"mapped","q"],[[9415,9415],"mapped","r"],[[9416,9416],"mapped","s"],[[9417,9417],"mapped","t"],[[9418,9418],"mapped","u"],[[9419,9419],"mapped","v"],[[9420,9420],"mapped","w"],[[9421,9421],"mapped","x"],[[9422,9422],"mapped","y"],[[9423,9423],"mapped","z"],[[9424,9424],"mapped","a"],[[9425,9425],"mapped","b"],[[9426,9426],"mapped","c"],[[9427,9427],"mapped","d"],[[9428,9428],"mapped","e"],[[9429,9429],"mapped","f"],[[9430,9430],"mapped","g"],[[9431,9431],"mapped","h"],[[9432,9432],"mapped","i"],[[9433,9433],"mapped","j"],[[9434,9434],"mapped","k"],[[9435,9435],"mapped","l"],[[9436,9436],"mapped","m"],[[9437,9437],"mapped","n"],[[9438,9438],"mapped","o"],[[9439,9439],"mapped","p"],[[9440,9440],"mapped","q"],[[9441,9441],"mapped","r"],[[9442,9442],"mapped","s"],[[9443,9443],"mapped","t"],[[9444,9444],"mapped","u"],[[9445,9445],"mapped","v"],[[9446,9446],"mapped","w"],[[9447,9447],"mapped","x"],[[9448,9448],"mapped","y"],[[9449,9449],"mapped","z"],[[9450,9450],"mapped","0"],[[9451,9470],"valid","","NV8"],[[9471,9471],"valid","","NV8"],[[9472,9621],"valid","","NV8"],[[9622,9631],"valid","","NV8"],[[9632,9711],"valid","","NV8"],[[9712,9719],"valid","","NV8"],[[9720,9727],"valid","","NV8"],[[9728,9747],"valid","","NV8"],[[9748,9749],"valid","","NV8"],[[9750,9751],"valid","","NV8"],[[9752,9752],"valid","","NV8"],[[9753,9753],"valid","","NV8"],[[9754,9839],"valid","","NV8"],[[9840,9841],"valid","","NV8"],[[9842,9853],"valid","","NV8"],[[9854,9855],"valid","","NV8"],[[9856,9865],"valid","","NV8"],[[9866,9873],"valid","","NV8"],[[9874,9884],"valid","","NV8"],[[9885,9885],"valid","","NV8"],[[9886,9887],"valid","","NV8"],[[9888,9889],"valid","","NV8"],[[9890,9905],"valid","","NV8"],[[9906,9906],"valid","","NV8"],[[9907,9916],"valid","","NV8"],[[9917,9919],"valid","","NV8"],[[9920,9923],"valid","","NV8"],[[9924,9933],"valid","","NV8"],[[9934,9934],"valid","","NV8"],[[9935,9953],"valid","","NV8"],[[9954,9954],"valid","","NV8"],[[9955,9955],"valid","","NV8"],[[9956,9959],"valid","","NV8"],[[9960,9983],"valid","","NV8"],[[9984,9984],"valid","","NV8"],[[9985,9988],"valid","","NV8"],[[9989,9989],"valid","","NV8"],[[9990,9993],"valid","","NV8"],[[9994,9995],"valid","","NV8"],[[9996,10023],"valid","","NV8"],[[10024,10024],"valid","","NV8"],[[10025,10059],"valid","","NV8"],[[10060,10060],"valid","","NV8"],[[10061,10061],"valid","","NV8"],[[10062,10062],"valid","","NV8"],[[10063,10066],"valid","","NV8"],[[10067,10069],"valid","","NV8"],[[10070,10070],"valid","","NV8"],[[10071,10071],"valid","","NV8"],[[10072,10078],"valid","","NV8"],[[10079,10080],"valid","","NV8"],[[10081,10087],"valid","","NV8"],[[10088,10101],"valid","","NV8"],[[10102,10132],"valid","","NV8"],[[10133,10135],"valid","","NV8"],[[10136,10159],"valid","","NV8"],[[10160,10160],"valid","","NV8"],[[10161,10174],"valid","","NV8"],[[10175,10175],"valid","","NV8"],[[10176,10182],"valid","","NV8"],[[10183,10186],"valid","","NV8"],[[10187,10187],"valid","","NV8"],[[10188,10188],"valid","","NV8"],[[10189,10189],"valid","","NV8"],[[10190,10191],"valid","","NV8"],[[10192,10219],"valid","","NV8"],[[10220,10223],"valid","","NV8"],[[10224,10239],"valid","","NV8"],[[10240,10495],"valid","","NV8"],[[10496,10763],"valid","","NV8"],[[10764,10764],"mapped","∫∫∫∫"],[[10765,10867],"valid","","NV8"],[[10868,10868],"disallowed_STD3_mapped","::="],[[10869,10869],"disallowed_STD3_mapped","=="],[[10870,10870],"disallowed_STD3_mapped","==="],[[10871,10971],"valid","","NV8"],[[10972,10972],"mapped","⫝̸"],[[10973,11007],"valid","","NV8"],[[11008,11021],"valid","","NV8"],[[11022,11027],"valid","","NV8"],[[11028,11034],"valid","","NV8"],[[11035,11039],"valid","","NV8"],[[11040,11043],"valid","","NV8"],[[11044,11084],"valid","","NV8"],[[11085,11087],"valid","","NV8"],[[11088,11092],"valid","","NV8"],[[11093,11097],"valid","","NV8"],[[11098,11123],"valid","","NV8"],[[11124,11125],"disallowed"],[[11126,11157],"valid","","NV8"],[[11158,11159],"disallowed"],[[11160,11193],"valid","","NV8"],[[11194,11196],"disallowed"],[[11197,11208],"valid","","NV8"],[[11209,11209],"disallowed"],[[11210,11217],"valid","","NV8"],[[11218,11218],"valid","","NV8"],[[11219,11243],"disallowed"],[[11244,11247],"valid","","NV8"],[[11248,11263],"disallowed"],[[11264,11264],"mapped","ⰰ"],[[11265,11265],"mapped","ⰱ"],[[11266,11266],"mapped","ⰲ"],[[11267,11267],"mapped","ⰳ"],[[11268,11268],"mapped","ⰴ"],[[11269,11269],"mapped","ⰵ"],[[11270,11270],"mapped","ⰶ"],[[11271,11271],"mapped","ⰷ"],[[11272,11272],"mapped","ⰸ"],[[11273,11273],"mapped","ⰹ"],[[11274,11274],"mapped","ⰺ"],[[11275,11275],"mapped","ⰻ"],[[11276,11276],"mapped","ⰼ"],[[11277,11277],"mapped","ⰽ"],[[11278,11278],"mapped","ⰾ"],[[11279,11279],"mapped","ⰿ"],[[11280,11280],"mapped","ⱀ"],[[11281,11281],"mapped","ⱁ"],[[11282,11282],"mapped","ⱂ"],[[11283,11283],"mapped","ⱃ"],[[11284,11284],"mapped","ⱄ"],[[11285,11285],"mapped","ⱅ"],[[11286,11286],"mapped","ⱆ"],[[11287,11287],"mapped","ⱇ"],[[11288,11288],"mapped","ⱈ"],[[11289,11289],"mapped","ⱉ"],[[11290,11290],"mapped","ⱊ"],[[11291,11291],"mapped","ⱋ"],[[11292,11292],"mapped","ⱌ"],[[11293,11293],"mapped","ⱍ"],[[11294,11294],"mapped","ⱎ"],[[11295,11295],"mapped","ⱏ"],[[11296,11296],"mapped","ⱐ"],[[11297,11297],"mapped","ⱑ"],[[11298,11298],"mapped","ⱒ"],[[11299,11299],"mapped","ⱓ"],[[11300,11300],"mapped","ⱔ"],[[11301,11301],"mapped","ⱕ"],[[11302,11302],"mapped","ⱖ"],[[11303,11303],"mapped","ⱗ"],[[11304,11304],"mapped","ⱘ"],[[11305,11305],"mapped","ⱙ"],[[11306,11306],"mapped","ⱚ"],[[11307,11307],"mapped","ⱛ"],[[11308,11308],"mapped","ⱜ"],[[11309,11309],"mapped","ⱝ"],[[11310,11310],"mapped","ⱞ"],[[11311,11311],"disallowed"],[[11312,11358],"valid"],[[11359,11359],"disallowed"],[[11360,11360],"mapped","ⱡ"],[[11361,11361],"valid"],[[11362,11362],"mapped","ɫ"],[[11363,11363],"mapped","ᵽ"],[[11364,11364],"mapped","ɽ"],[[11365,11366],"valid"],[[11367,11367],"mapped","ⱨ"],[[11368,11368],"valid"],[[11369,11369],"mapped","ⱪ"],[[11370,11370],"valid"],[[11371,11371],"mapped","ⱬ"],[[11372,11372],"valid"],[[11373,11373],"mapped","ɑ"],[[11374,11374],"mapped","ɱ"],[[11375,11375],"mapped","ɐ"],[[11376,11376],"mapped","ɒ"],[[11377,11377],"valid"],[[11378,11378],"mapped","ⱳ"],[[11379,11379],"valid"],[[11380,11380],"valid"],[[11381,11381],"mapped","ⱶ"],[[11382,11383],"valid"],[[11384,11387],"valid"],[[11388,11388],"mapped","j"],[[11389,11389],"mapped","v"],[[11390,11390],"mapped","ȿ"],[[11391,11391],"mapped","ɀ"],[[11392,11392],"mapped","ⲁ"],[[11393,11393],"valid"],[[11394,11394],"mapped","ⲃ"],[[11395,11395],"valid"],[[11396,11396],"mapped","ⲅ"],[[11397,11397],"valid"],[[11398,11398],"mapped","ⲇ"],[[11399,11399],"valid"],[[11400,11400],"mapped","ⲉ"],[[11401,11401],"valid"],[[11402,11402],"mapped","ⲋ"],[[11403,11403],"valid"],[[11404,11404],"mapped","ⲍ"],[[11405,11405],"valid"],[[11406,11406],"mapped","ⲏ"],[[11407,11407],"valid"],[[11408,11408],"mapped","ⲑ"],[[11409,11409],"valid"],[[11410,11410],"mapped","ⲓ"],[[11411,11411],"valid"],[[11412,11412],"mapped","ⲕ"],[[11413,11413],"valid"],[[11414,11414],"mapped","ⲗ"],[[11415,11415],"valid"],[[11416,11416],"mapped","ⲙ"],[[11417,11417],"valid"],[[11418,11418],"mapped","ⲛ"],[[11419,11419],"valid"],[[11420,11420],"mapped","ⲝ"],[[11421,11421],"valid"],[[11422,11422],"mapped","ⲟ"],[[11423,11423],"valid"],[[11424,11424],"mapped","ⲡ"],[[11425,11425],"valid"],[[11426,11426],"mapped","ⲣ"],[[11427,11427],"valid"],[[11428,11428],"mapped","ⲥ"],[[11429,11429],"valid"],[[11430,11430],"mapped","ⲧ"],[[11431,11431],"valid"],[[11432,11432],"mapped","ⲩ"],[[11433,11433],"valid"],[[11434,11434],"mapped","ⲫ"],[[11435,11435],"valid"],[[11436,11436],"mapped","ⲭ"],[[11437,11437],"valid"],[[11438,11438],"mapped","ⲯ"],[[11439,11439],"valid"],[[11440,11440],"mapped","ⲱ"],[[11441,11441],"valid"],[[11442,11442],"mapped","ⲳ"],[[11443,11443],"valid"],[[11444,11444],"mapped","ⲵ"],[[11445,11445],"valid"],[[11446,11446],"mapped","ⲷ"],[[11447,11447],"valid"],[[11448,11448],"mapped","ⲹ"],[[11449,11449],"valid"],[[11450,11450],"mapped","ⲻ"],[[11451,11451],"valid"],[[11452,11452],"mapped","ⲽ"],[[11453,11453],"valid"],[[11454,11454],"mapped","ⲿ"],[[11455,11455],"valid"],[[11456,11456],"mapped","ⳁ"],[[11457,11457],"valid"],[[11458,11458],"mapped","ⳃ"],[[11459,11459],"valid"],[[11460,11460],"mapped","ⳅ"],[[11461,11461],"valid"],[[11462,11462],"mapped","ⳇ"],[[11463,11463],"valid"],[[11464,11464],"mapped","ⳉ"],[[11465,11465],"valid"],[[11466,11466],"mapped","ⳋ"],[[11467,11467],"valid"],[[11468,11468],"mapped","ⳍ"],[[11469,11469],"valid"],[[11470,11470],"mapped","ⳏ"],[[11471,11471],"valid"],[[11472,11472],"mapped","ⳑ"],[[11473,11473],"valid"],[[11474,11474],"mapped","ⳓ"],[[11475,11475],"valid"],[[11476,11476],"mapped","ⳕ"],[[11477,11477],"valid"],[[11478,11478],"mapped","ⳗ"],[[11479,11479],"valid"],[[11480,11480],"mapped","ⳙ"],[[11481,11481],"valid"],[[11482,11482],"mapped","ⳛ"],[[11483,11483],"valid"],[[11484,11484],"mapped","ⳝ"],[[11485,11485],"valid"],[[11486,11486],"mapped","ⳟ"],[[11487,11487],"valid"],[[11488,11488],"mapped","ⳡ"],[[11489,11489],"valid"],[[11490,11490],"mapped","ⳣ"],[[11491,11492],"valid"],[[11493,11498],"valid","","NV8"],[[11499,11499],"mapped","ⳬ"],[[11500,11500],"valid"],[[11501,11501],"mapped","ⳮ"],[[11502,11505],"valid"],[[11506,11506],"mapped","ⳳ"],[[11507,11507],"valid"],[[11508,11512],"disallowed"],[[11513,11519],"valid","","NV8"],[[11520,11557],"valid"],[[11558,11558],"disallowed"],[[11559,11559],"valid"],[[11560,11564],"disallowed"],[[11565,11565],"valid"],[[11566,11567],"disallowed"],[[11568,11621],"valid"],[[11622,11623],"valid"],[[11624,11630],"disallowed"],[[11631,11631],"mapped","ⵡ"],[[11632,11632],"valid","","NV8"],[[11633,11646],"disallowed"],[[11647,11647],"valid"],[[11648,11670],"valid"],[[11671,11679],"disallowed"],[[11680,11686],"valid"],[[11687,11687],"disallowed"],[[11688,11694],"valid"],[[11695,11695],"disallowed"],[[11696,11702],"valid"],[[11703,11703],"disallowed"],[[11704,11710],"valid"],[[11711,11711],"disallowed"],[[11712,11718],"valid"],[[11719,11719],"disallowed"],[[11720,11726],"valid"],[[11727,11727],"disallowed"],[[11728,11734],"valid"],[[11735,11735],"disallowed"],[[11736,11742],"valid"],[[11743,11743],"disallowed"],[[11744,11775],"valid"],[[11776,11799],"valid","","NV8"],[[11800,11803],"valid","","NV8"],[[11804,11805],"valid","","NV8"],[[11806,11822],"valid","","NV8"],[[11823,11823],"valid"],[[11824,11824],"valid","","NV8"],[[11825,11825],"valid","","NV8"],[[11826,11835],"valid","","NV8"],[[11836,11842],"valid","","NV8"],[[11843,11844],"valid","","NV8"],[[11845,11849],"valid","","NV8"],[[11850,11903],"disallowed"],[[11904,11929],"valid","","NV8"],[[11930,11930],"disallowed"],[[11931,11934],"valid","","NV8"],[[11935,11935],"mapped","母"],[[11936,12018],"valid","","NV8"],[[12019,12019],"mapped","龟"],[[12020,12031],"disallowed"],[[12032,12032],"mapped","一"],[[12033,12033],"mapped","丨"],[[12034,12034],"mapped","丶"],[[12035,12035],"mapped","丿"],[[12036,12036],"mapped","乙"],[[12037,12037],"mapped","亅"],[[12038,12038],"mapped","二"],[[12039,12039],"mapped","亠"],[[12040,12040],"mapped","人"],[[12041,12041],"mapped","儿"],[[12042,12042],"mapped","入"],[[12043,12043],"mapped","八"],[[12044,12044],"mapped","冂"],[[12045,12045],"mapped","冖"],[[12046,12046],"mapped","冫"],[[12047,12047],"mapped","几"],[[12048,12048],"mapped","凵"],[[12049,12049],"mapped","刀"],[[12050,12050],"mapped","力"],[[12051,12051],"mapped","勹"],[[12052,12052],"mapped","匕"],[[12053,12053],"mapped","匚"],[[12054,12054],"mapped","匸"],[[12055,12055],"mapped","十"],[[12056,12056],"mapped","卜"],[[12057,12057],"mapped","卩"],[[12058,12058],"mapped","厂"],[[12059,12059],"mapped","厶"],[[12060,12060],"mapped","又"],[[12061,12061],"mapped","口"],[[12062,12062],"mapped","囗"],[[12063,12063],"mapped","土"],[[12064,12064],"mapped","士"],[[12065,12065],"mapped","夂"],[[12066,12066],"mapped","夊"],[[12067,12067],"mapped","夕"],[[12068,12068],"mapped","大"],[[12069,12069],"mapped","女"],[[12070,12070],"mapped","子"],[[12071,12071],"mapped","宀"],[[12072,12072],"mapped","寸"],[[12073,12073],"mapped","小"],[[12074,12074],"mapped","尢"],[[12075,12075],"mapped","尸"],[[12076,12076],"mapped","屮"],[[12077,12077],"mapped","山"],[[12078,12078],"mapped","巛"],[[12079,12079],"mapped","工"],[[12080,12080],"mapped","己"],[[12081,12081],"mapped","巾"],[[12082,12082],"mapped","干"],[[12083,12083],"mapped","幺"],[[12084,12084],"mapped","广"],[[12085,12085],"mapped","廴"],[[12086,12086],"mapped","廾"],[[12087,12087],"mapped","弋"],[[12088,12088],"mapped","弓"],[[12089,12089],"mapped","彐"],[[12090,12090],"mapped","彡"],[[12091,12091],"mapped","彳"],[[12092,12092],"mapped","心"],[[12093,12093],"mapped","戈"],[[12094,12094],"mapped","戶"],[[12095,12095],"mapped","手"],[[12096,12096],"mapped","支"],[[12097,12097],"mapped","攴"],[[12098,12098],"mapped","文"],[[12099,12099],"mapped","斗"],[[12100,12100],"mapped","斤"],[[12101,12101],"mapped","方"],[[12102,12102],"mapped","无"],[[12103,12103],"mapped","日"],[[12104,12104],"mapped","曰"],[[12105,12105],"mapped","月"],[[12106,12106],"mapped","木"],[[12107,12107],"mapped","欠"],[[12108,12108],"mapped","止"],[[12109,12109],"mapped","歹"],[[12110,12110],"mapped","殳"],[[12111,12111],"mapped","毋"],[[12112,12112],"mapped","比"],[[12113,12113],"mapped","毛"],[[12114,12114],"mapped","氏"],[[12115,12115],"mapped","气"],[[12116,12116],"mapped","水"],[[12117,12117],"mapped","火"],[[12118,12118],"mapped","爪"],[[12119,12119],"mapped","父"],[[12120,12120],"mapped","爻"],[[12121,12121],"mapped","爿"],[[12122,12122],"mapped","片"],[[12123,12123],"mapped","牙"],[[12124,12124],"mapped","牛"],[[12125,12125],"mapped","犬"],[[12126,12126],"mapped","玄"],[[12127,12127],"mapped","玉"],[[12128,12128],"mapped","瓜"],[[12129,12129],"mapped","瓦"],[[12130,12130],"mapped","甘"],[[12131,12131],"mapped","生"],[[12132,12132],"mapped","用"],[[12133,12133],"mapped","田"],[[12134,12134],"mapped","疋"],[[12135,12135],"mapped","疒"],[[12136,12136],"mapped","癶"],[[12137,12137],"mapped","白"],[[12138,12138],"mapped","皮"],[[12139,12139],"mapped","皿"],[[12140,12140],"mapped","目"],[[12141,12141],"mapped","矛"],[[12142,12142],"mapped","矢"],[[12143,12143],"mapped","石"],[[12144,12144],"mapped","示"],[[12145,12145],"mapped","禸"],[[12146,12146],"mapped","禾"],[[12147,12147],"mapped","穴"],[[12148,12148],"mapped","立"],[[12149,12149],"mapped","竹"],[[12150,12150],"mapped","米"],[[12151,12151],"mapped","糸"],[[12152,12152],"mapped","缶"],[[12153,12153],"mapped","网"],[[12154,12154],"mapped","羊"],[[12155,12155],"mapped","羽"],[[12156,12156],"mapped","老"],[[12157,12157],"mapped","而"],[[12158,12158],"mapped","耒"],[[12159,12159],"mapped","耳"],[[12160,12160],"mapped","聿"],[[12161,12161],"mapped","肉"],[[12162,12162],"mapped","臣"],[[12163,12163],"mapped","自"],[[12164,12164],"mapped","至"],[[12165,12165],"mapped","臼"],[[12166,12166],"mapped","舌"],[[12167,12167],"mapped","舛"],[[12168,12168],"mapped","舟"],[[12169,12169],"mapped","艮"],[[12170,12170],"mapped","色"],[[12171,12171],"mapped","艸"],[[12172,12172],"mapped","虍"],[[12173,12173],"mapped","虫"],[[12174,12174],"mapped","血"],[[12175,12175],"mapped","行"],[[12176,12176],"mapped","衣"],[[12177,12177],"mapped","襾"],[[12178,12178],"mapped","見"],[[12179,12179],"mapped","角"],[[12180,12180],"mapped","言"],[[12181,12181],"mapped","谷"],[[12182,12182],"mapped","豆"],[[12183,12183],"mapped","豕"],[[12184,12184],"mapped","豸"],[[12185,12185],"mapped","貝"],[[12186,12186],"mapped","赤"],[[12187,12187],"mapped","走"],[[12188,12188],"mapped","足"],[[12189,12189],"mapped","身"],[[12190,12190],"mapped","車"],[[12191,12191],"mapped","辛"],[[12192,12192],"mapped","辰"],[[12193,12193],"mapped","辵"],[[12194,12194],"mapped","邑"],[[12195,12195],"mapped","酉"],[[12196,12196],"mapped","釆"],[[12197,12197],"mapped","里"],[[12198,12198],"mapped","金"],[[12199,12199],"mapped","長"],[[12200,12200],"mapped","門"],[[12201,12201],"mapped","阜"],[[12202,12202],"mapped","隶"],[[12203,12203],"mapped","隹"],[[12204,12204],"mapped","雨"],[[12205,12205],"mapped","靑"],[[12206,12206],"mapped","非"],[[12207,12207],"mapped","面"],[[12208,12208],"mapped","革"],[[12209,12209],"mapped","韋"],[[12210,12210],"mapped","韭"],[[12211,12211],"mapped","音"],[[12212,12212],"mapped","頁"],[[12213,12213],"mapped","風"],[[12214,12214],"mapped","飛"],[[12215,12215],"mapped","食"],[[12216,12216],"mapped","首"],[[12217,12217],"mapped","香"],[[12218,12218],"mapped","馬"],[[12219,12219],"mapped","骨"],[[12220,12220],"mapped","高"],[[12221,12221],"mapped","髟"],[[12222,12222],"mapped","鬥"],[[12223,12223],"mapped","鬯"],[[12224,12224],"mapped","鬲"],[[12225,12225],"mapped","鬼"],[[12226,12226],"mapped","魚"],[[12227,12227],"mapped","鳥"],[[12228,12228],"mapped","鹵"],[[12229,12229],"mapped","鹿"],[[12230,12230],"mapped","麥"],[[12231,12231],"mapped","麻"],[[12232,12232],"mapped","黃"],[[12233,12233],"mapped","黍"],[[12234,12234],"mapped","黑"],[[12235,12235],"mapped","黹"],[[12236,12236],"mapped","黽"],[[12237,12237],"mapped","鼎"],[[12238,12238],"mapped","鼓"],[[12239,12239],"mapped","鼠"],[[12240,12240],"mapped","鼻"],[[12241,12241],"mapped","齊"],[[12242,12242],"mapped","齒"],[[12243,12243],"mapped","龍"],[[12244,12244],"mapped","龜"],[[12245,12245],"mapped","龠"],[[12246,12271],"disallowed"],[[12272,12283],"disallowed"],[[12284,12287],"disallowed"],[[12288,12288],"disallowed_STD3_mapped"," "],[[12289,12289],"valid","","NV8"],[[12290,12290],"mapped","."],[[12291,12292],"valid","","NV8"],[[12293,12295],"valid"],[[12296,12329],"valid","","NV8"],[[12330,12333],"valid"],[[12334,12341],"valid","","NV8"],[[12342,12342],"mapped","〒"],[[12343,12343],"valid","","NV8"],[[12344,12344],"mapped","十"],[[12345,12345],"mapped","卄"],[[12346,12346],"mapped","卅"],[[12347,12347],"valid","","NV8"],[[12348,12348],"valid"],[[12349,12349],"valid","","NV8"],[[12350,12350],"valid","","NV8"],[[12351,12351],"valid","","NV8"],[[12352,12352],"disallowed"],[[12353,12436],"valid"],[[12437,12438],"valid"],[[12439,12440],"disallowed"],[[12441,12442],"valid"],[[12443,12443],"disallowed_STD3_mapped"," ゙"],[[12444,12444],"disallowed_STD3_mapped"," ゚"],[[12445,12446],"valid"],[[12447,12447],"mapped","より"],[[12448,12448],"valid","","NV8"],[[12449,12542],"valid"],[[12543,12543],"mapped","コト"],[[12544,12548],"disallowed"],[[12549,12588],"valid"],[[12589,12589],"valid"],[[12590,12590],"valid"],[[12591,12592],"disallowed"],[[12593,12593],"mapped","ᄀ"],[[12594,12594],"mapped","ᄁ"],[[12595,12595],"mapped","ᆪ"],[[12596,12596],"mapped","ᄂ"],[[12597,12597],"mapped","ᆬ"],[[12598,12598],"mapped","ᆭ"],[[12599,12599],"mapped","ᄃ"],[[12600,12600],"mapped","ᄄ"],[[12601,12601],"mapped","ᄅ"],[[12602,12602],"mapped","ᆰ"],[[12603,12603],"mapped","ᆱ"],[[12604,12604],"mapped","ᆲ"],[[12605,12605],"mapped","ᆳ"],[[12606,12606],"mapped","ᆴ"],[[12607,12607],"mapped","ᆵ"],[[12608,12608],"mapped","ᄚ"],[[12609,12609],"mapped","ᄆ"],[[12610,12610],"mapped","ᄇ"],[[12611,12611],"mapped","ᄈ"],[[12612,12612],"mapped","ᄡ"],[[12613,12613],"mapped","ᄉ"],[[12614,12614],"mapped","ᄊ"],[[12615,12615],"mapped","ᄋ"],[[12616,12616],"mapped","ᄌ"],[[12617,12617],"mapped","ᄍ"],[[12618,12618],"mapped","ᄎ"],[[12619,12619],"mapped","ᄏ"],[[12620,12620],"mapped","ᄐ"],[[12621,12621],"mapped","ᄑ"],[[12622,12622],"mapped","ᄒ"],[[12623,12623],"mapped","ᅡ"],[[12624,12624],"mapped","ᅢ"],[[12625,12625],"mapped","ᅣ"],[[12626,12626],"mapped","ᅤ"],[[12627,12627],"mapped","ᅥ"],[[12628,12628],"mapped","ᅦ"],[[12629,12629],"mapped","ᅧ"],[[12630,12630],"mapped","ᅨ"],[[12631,12631],"mapped","ᅩ"],[[12632,12632],"mapped","ᅪ"],[[12633,12633],"mapped","ᅫ"],[[12634,12634],"mapped","ᅬ"],[[12635,12635],"mapped","ᅭ"],[[12636,12636],"mapped","ᅮ"],[[12637,12637],"mapped","ᅯ"],[[12638,12638],"mapped","ᅰ"],[[12639,12639],"mapped","ᅱ"],[[12640,12640],"mapped","ᅲ"],[[12641,12641],"mapped","ᅳ"],[[12642,12642],"mapped","ᅴ"],[[12643,12643],"mapped","ᅵ"],[[12644,12644],"disallowed"],[[12645,12645],"mapped","ᄔ"],[[12646,12646],"mapped","ᄕ"],[[12647,12647],"mapped","ᇇ"],[[12648,12648],"mapped","ᇈ"],[[12649,12649],"mapped","ᇌ"],[[12650,12650],"mapped","ᇎ"],[[12651,12651],"mapped","ᇓ"],[[12652,12652],"mapped","ᇗ"],[[12653,12653],"mapped","ᇙ"],[[12654,12654],"mapped","ᄜ"],[[12655,12655],"mapped","ᇝ"],[[12656,12656],"mapped","ᇟ"],[[12657,12657],"mapped","ᄝ"],[[12658,12658],"mapped","ᄞ"],[[12659,12659],"mapped","ᄠ"],[[12660,12660],"mapped","ᄢ"],[[12661,12661],"mapped","ᄣ"],[[12662,12662],"mapped","ᄧ"],[[12663,12663],"mapped","ᄩ"],[[12664,12664],"mapped","ᄫ"],[[12665,12665],"mapped","ᄬ"],[[12666,12666],"mapped","ᄭ"],[[12667,12667],"mapped","ᄮ"],[[12668,12668],"mapped","ᄯ"],[[12669,12669],"mapped","ᄲ"],[[12670,12670],"mapped","ᄶ"],[[12671,12671],"mapped","ᅀ"],[[12672,12672],"mapped","ᅇ"],[[12673,12673],"mapped","ᅌ"],[[12674,12674],"mapped","ᇱ"],[[12675,12675],"mapped","ᇲ"],[[12676,12676],"mapped","ᅗ"],[[12677,12677],"mapped","ᅘ"],[[12678,12678],"mapped","ᅙ"],[[12679,12679],"mapped","ᆄ"],[[12680,12680],"mapped","ᆅ"],[[12681,12681],"mapped","ᆈ"],[[12682,12682],"mapped","ᆑ"],[[12683,12683],"mapped","ᆒ"],[[12684,12684],"mapped","ᆔ"],[[12685,12685],"mapped","ᆞ"],[[12686,12686],"mapped","ᆡ"],[[12687,12687],"disallowed"],[[12688,12689],"valid","","NV8"],[[12690,12690],"mapped","一"],[[12691,12691],"mapped","二"],[[12692,12692],"mapped","三"],[[12693,12693],"mapped","四"],[[12694,12694],"mapped","上"],[[12695,12695],"mapped","中"],[[12696,12696],"mapped","下"],[[12697,12697],"mapped","甲"],[[12698,12698],"mapped","乙"],[[12699,12699],"mapped","丙"],[[12700,12700],"mapped","丁"],[[12701,12701],"mapped","天"],[[12702,12702],"mapped","地"],[[12703,12703],"mapped","人"],[[12704,12727],"valid"],[[12728,12730],"valid"],[[12731,12735],"disallowed"],[[12736,12751],"valid","","NV8"],[[12752,12771],"valid","","NV8"],[[12772,12783],"disallowed"],[[12784,12799],"valid"],[[12800,12800],"disallowed_STD3_mapped","(ᄀ)"],[[12801,12801],"disallowed_STD3_mapped","(ᄂ)"],[[12802,12802],"disallowed_STD3_mapped","(ᄃ)"],[[12803,12803],"disallowed_STD3_mapped","(ᄅ)"],[[12804,12804],"disallowed_STD3_mapped","(ᄆ)"],[[12805,12805],"disallowed_STD3_mapped","(ᄇ)"],[[12806,12806],"disallowed_STD3_mapped","(ᄉ)"],[[12807,12807],"disallowed_STD3_mapped","(ᄋ)"],[[12808,12808],"disallowed_STD3_mapped","(ᄌ)"],[[12809,12809],"disallowed_STD3_mapped","(ᄎ)"],[[12810,12810],"disallowed_STD3_mapped","(ᄏ)"],[[12811,12811],"disallowed_STD3_mapped","(ᄐ)"],[[12812,12812],"disallowed_STD3_mapped","(ᄑ)"],[[12813,12813],"disallowed_STD3_mapped","(ᄒ)"],[[12814,12814],"disallowed_STD3_mapped","(가)"],[[12815,12815],"disallowed_STD3_mapped","(나)"],[[12816,12816],"disallowed_STD3_mapped","(다)"],[[12817,12817],"disallowed_STD3_mapped","(라)"],[[12818,12818],"disallowed_STD3_mapped","(마)"],[[12819,12819],"disallowed_STD3_mapped","(바)"],[[12820,12820],"disallowed_STD3_mapped","(사)"],[[12821,12821],"disallowed_STD3_mapped","(아)"],[[12822,12822],"disallowed_STD3_mapped","(자)"],[[12823,12823],"disallowed_STD3_mapped","(차)"],[[12824,12824],"disallowed_STD3_mapped","(카)"],[[12825,12825],"disallowed_STD3_mapped","(타)"],[[12826,12826],"disallowed_STD3_mapped","(파)"],[[12827,12827],"disallowed_STD3_mapped","(하)"],[[12828,12828],"disallowed_STD3_mapped","(주)"],[[12829,12829],"disallowed_STD3_mapped","(오전)"],[[12830,12830],"disallowed_STD3_mapped","(오후)"],[[12831,12831],"disallowed"],[[12832,12832],"disallowed_STD3_mapped","(一)"],[[12833,12833],"disallowed_STD3_mapped","(二)"],[[12834,12834],"disallowed_STD3_mapped","(三)"],[[12835,12835],"disallowed_STD3_mapped","(四)"],[[12836,12836],"disallowed_STD3_mapped","(五)"],[[12837,12837],"disallowed_STD3_mapped","(六)"],[[12838,12838],"disallowed_STD3_mapped","(七)"],[[12839,12839],"disallowed_STD3_mapped","(八)"],[[12840,12840],"disallowed_STD3_mapped","(九)"],[[12841,12841],"disallowed_STD3_mapped","(十)"],[[12842,12842],"disallowed_STD3_mapped","(月)"],[[12843,12843],"disallowed_STD3_mapped","(火)"],[[12844,12844],"disallowed_STD3_mapped","(水)"],[[12845,12845],"disallowed_STD3_mapped","(木)"],[[12846,12846],"disallowed_STD3_mapped","(金)"],[[12847,12847],"disallowed_STD3_mapped","(土)"],[[12848,12848],"disallowed_STD3_mapped","(日)"],[[12849,12849],"disallowed_STD3_mapped","(株)"],[[12850,12850],"disallowed_STD3_mapped","(有)"],[[12851,12851],"disallowed_STD3_mapped","(社)"],[[12852,12852],"disallowed_STD3_mapped","(名)"],[[12853,12853],"disallowed_STD3_mapped","(特)"],[[12854,12854],"disallowed_STD3_mapped","(財)"],[[12855,12855],"disallowed_STD3_mapped","(祝)"],[[12856,12856],"disallowed_STD3_mapped","(労)"],[[12857,12857],"disallowed_STD3_mapped","(代)"],[[12858,12858],"disallowed_STD3_mapped","(呼)"],[[12859,12859],"disallowed_STD3_mapped","(学)"],[[12860,12860],"disallowed_STD3_mapped","(監)"],[[12861,12861],"disallowed_STD3_mapped","(企)"],[[12862,12862],"disallowed_STD3_mapped","(資)"],[[12863,12863],"disallowed_STD3_mapped","(協)"],[[12864,12864],"disallowed_STD3_mapped","(祭)"],[[12865,12865],"disallowed_STD3_mapped","(休)"],[[12866,12866],"disallowed_STD3_mapped","(自)"],[[12867,12867],"disallowed_STD3_mapped","(至)"],[[12868,12868],"mapped","問"],[[12869,12869],"mapped","幼"],[[12870,12870],"mapped","文"],[[12871,12871],"mapped","箏"],[[12872,12879],"valid","","NV8"],[[12880,12880],"mapped","pte"],[[12881,12881],"mapped","21"],[[12882,12882],"mapped","22"],[[12883,12883],"mapped","23"],[[12884,12884],"mapped","24"],[[12885,12885],"mapped","25"],[[12886,12886],"mapped","26"],[[12887,12887],"mapped","27"],[[12888,12888],"mapped","28"],[[12889,12889],"mapped","29"],[[12890,12890],"mapped","30"],[[12891,12891],"mapped","31"],[[12892,12892],"mapped","32"],[[12893,12893],"mapped","33"],[[12894,12894],"mapped","34"],[[12895,12895],"mapped","35"],[[12896,12896],"mapped","ᄀ"],[[12897,12897],"mapped","ᄂ"],[[12898,12898],"mapped","ᄃ"],[[12899,12899],"mapped","ᄅ"],[[12900,12900],"mapped","ᄆ"],[[12901,12901],"mapped","ᄇ"],[[12902,12902],"mapped","ᄉ"],[[12903,12903],"mapped","ᄋ"],[[12904,12904],"mapped","ᄌ"],[[12905,12905],"mapped","ᄎ"],[[12906,12906],"mapped","ᄏ"],[[12907,12907],"mapped","ᄐ"],[[12908,12908],"mapped","ᄑ"],[[12909,12909],"mapped","ᄒ"],[[12910,12910],"mapped","가"],[[12911,12911],"mapped","나"],[[12912,12912],"mapped","다"],[[12913,12913],"mapped","라"],[[12914,12914],"mapped","마"],[[12915,12915],"mapped","바"],[[12916,12916],"mapped","사"],[[12917,12917],"mapped","아"],[[12918,12918],"mapped","자"],[[12919,12919],"mapped","차"],[[12920,12920],"mapped","카"],[[12921,12921],"mapped","타"],[[12922,12922],"mapped","파"],[[12923,12923],"mapped","하"],[[12924,12924],"mapped","참고"],[[12925,12925],"mapped","주의"],[[12926,12926],"mapped","우"],[[12927,12927],"valid","","NV8"],[[12928,12928],"mapped","一"],[[12929,12929],"mapped","二"],[[12930,12930],"mapped","三"],[[12931,12931],"mapped","四"],[[12932,12932],"mapped","五"],[[12933,12933],"mapped","六"],[[12934,12934],"mapped","七"],[[12935,12935],"mapped","八"],[[12936,12936],"mapped","九"],[[12937,12937],"mapped","十"],[[12938,12938],"mapped","月"],[[12939,12939],"mapped","火"],[[12940,12940],"mapped","水"],[[12941,12941],"mapped","木"],[[12942,12942],"mapped","金"],[[12943,12943],"mapped","土"],[[12944,12944],"mapped","日"],[[12945,12945],"mapped","株"],[[12946,12946],"mapped","有"],[[12947,12947],"mapped","社"],[[12948,12948],"mapped","名"],[[12949,12949],"mapped","特"],[[12950,12950],"mapped","財"],[[12951,12951],"mapped","祝"],[[12952,12952],"mapped","労"],[[12953,12953],"mapped","秘"],[[12954,12954],"mapped","男"],[[12955,12955],"mapped","女"],[[12956,12956],"mapped","適"],[[12957,12957],"mapped","優"],[[12958,12958],"mapped","印"],[[12959,12959],"mapped","注"],[[12960,12960],"mapped","項"],[[12961,12961],"mapped","休"],[[12962,12962],"mapped","写"],[[12963,12963],"mapped","正"],[[12964,12964],"mapped","上"],[[12965,12965],"mapped","中"],[[12966,12966],"mapped","下"],[[12967,12967],"mapped","左"],[[12968,12968],"mapped","右"],[[12969,12969],"mapped","医"],[[12970,12970],"mapped","宗"],[[12971,12971],"mapped","学"],[[12972,12972],"mapped","監"],[[12973,12973],"mapped","企"],[[12974,12974],"mapped","資"],[[12975,12975],"mapped","協"],[[12976,12976],"mapped","夜"],[[12977,12977],"mapped","36"],[[12978,12978],"mapped","37"],[[12979,12979],"mapped","38"],[[12980,12980],"mapped","39"],[[12981,12981],"mapped","40"],[[12982,12982],"mapped","41"],[[12983,12983],"mapped","42"],[[12984,12984],"mapped","43"],[[12985,12985],"mapped","44"],[[12986,12986],"mapped","45"],[[12987,12987],"mapped","46"],[[12988,12988],"mapped","47"],[[12989,12989],"mapped","48"],[[12990,12990],"mapped","49"],[[12991,12991],"mapped","50"],[[12992,12992],"mapped","1月"],[[12993,12993],"mapped","2月"],[[12994,12994],"mapped","3月"],[[12995,12995],"mapped","4月"],[[12996,12996],"mapped","5月"],[[12997,12997],"mapped","6月"],[[12998,12998],"mapped","7月"],[[12999,12999],"mapped","8月"],[[13000,13000],"mapped","9月"],[[13001,13001],"mapped","10月"],[[13002,13002],"mapped","11月"],[[13003,13003],"mapped","12月"],[[13004,13004],"mapped","hg"],[[13005,13005],"mapped","erg"],[[13006,13006],"mapped","ev"],[[13007,13007],"mapped","ltd"],[[13008,13008],"mapped","ア"],[[13009,13009],"mapped","イ"],[[13010,13010],"mapped","ウ"],[[13011,13011],"mapped","エ"],[[13012,13012],"mapped","オ"],[[13013,13013],"mapped","カ"],[[13014,13014],"mapped","キ"],[[13015,13015],"mapped","ク"],[[13016,13016],"mapped","ケ"],[[13017,13017],"mapped","コ"],[[13018,13018],"mapped","サ"],[[13019,13019],"mapped","シ"],[[13020,13020],"mapped","ス"],[[13021,13021],"mapped","セ"],[[13022,13022],"mapped","ソ"],[[13023,13023],"mapped","タ"],[[13024,13024],"mapped","チ"],[[13025,13025],"mapped","ツ"],[[13026,13026],"mapped","テ"],[[13027,13027],"mapped","ト"],[[13028,13028],"mapped","ナ"],[[13029,13029],"mapped","ニ"],[[13030,13030],"mapped","ヌ"],[[13031,13031],"mapped","ネ"],[[13032,13032],"mapped","ノ"],[[13033,13033],"mapped","ハ"],[[13034,13034],"mapped","ヒ"],[[13035,13035],"mapped","フ"],[[13036,13036],"mapped","ヘ"],[[13037,13037],"mapped","ホ"],[[13038,13038],"mapped","マ"],[[13039,13039],"mapped","ミ"],[[13040,13040],"mapped","ム"],[[13041,13041],"mapped","メ"],[[13042,13042],"mapped","モ"],[[13043,13043],"mapped","ヤ"],[[13044,13044],"mapped","ユ"],[[13045,13045],"mapped","ヨ"],[[13046,13046],"mapped","ラ"],[[13047,13047],"mapped","リ"],[[13048,13048],"mapped","ル"],[[13049,13049],"mapped","レ"],[[13050,13050],"mapped","ロ"],[[13051,13051],"mapped","ワ"],[[13052,13052],"mapped","ヰ"],[[13053,13053],"mapped","ヱ"],[[13054,13054],"mapped","ヲ"],[[13055,13055],"disallowed"],[[13056,13056],"mapped","アパート"],[[13057,13057],"mapped","アルファ"],[[13058,13058],"mapped","アンペア"],[[13059,13059],"mapped","アール"],[[13060,13060],"mapped","イニング"],[[13061,13061],"mapped","インチ"],[[13062,13062],"mapped","ウォン"],[[13063,13063],"mapped","エスクード"],[[13064,13064],"mapped","エーカー"],[[13065,13065],"mapped","オンス"],[[13066,13066],"mapped","オーム"],[[13067,13067],"mapped","カイリ"],[[13068,13068],"mapped","カラット"],[[13069,13069],"mapped","カロリー"],[[13070,13070],"mapped","ガロン"],[[13071,13071],"mapped","ガンマ"],[[13072,13072],"mapped","ギガ"],[[13073,13073],"mapped","ギニー"],[[13074,13074],"mapped","キュリー"],[[13075,13075],"mapped","ギルダー"],[[13076,13076],"mapped","キロ"],[[13077,13077],"mapped","キログラム"],[[13078,13078],"mapped","キロメートル"],[[13079,13079],"mapped","キロワット"],[[13080,13080],"mapped","グラム"],[[13081,13081],"mapped","グラムトン"],[[13082,13082],"mapped","クルゼイロ"],[[13083,13083],"mapped","クローネ"],[[13084,13084],"mapped","ケース"],[[13085,13085],"mapped","コルナ"],[[13086,13086],"mapped","コーポ"],[[13087,13087],"mapped","サイクル"],[[13088,13088],"mapped","サンチーム"],[[13089,13089],"mapped","シリング"],[[13090,13090],"mapped","センチ"],[[13091,13091],"mapped","セント"],[[13092,13092],"mapped","ダース"],[[13093,13093],"mapped","デシ"],[[13094,13094],"mapped","ドル"],[[13095,13095],"mapped","トン"],[[13096,13096],"mapped","ナノ"],[[13097,13097],"mapped","ノット"],[[13098,13098],"mapped","ハイツ"],[[13099,13099],"mapped","パーセント"],[[13100,13100],"mapped","パーツ"],[[13101,13101],"mapped","バーレル"],[[13102,13102],"mapped","ピアストル"],[[13103,13103],"mapped","ピクル"],[[13104,13104],"mapped","ピコ"],[[13105,13105],"mapped","ビル"],[[13106,13106],"mapped","ファラッド"],[[13107,13107],"mapped","フィート"],[[13108,13108],"mapped","ブッシェル"],[[13109,13109],"mapped","フラン"],[[13110,13110],"mapped","ヘクタール"],[[13111,13111],"mapped","ペソ"],[[13112,13112],"mapped","ペニヒ"],[[13113,13113],"mapped","ヘルツ"],[[13114,13114],"mapped","ペンス"],[[13115,13115],"mapped","ページ"],[[13116,13116],"mapped","ベータ"],[[13117,13117],"mapped","ポイント"],[[13118,13118],"mapped","ボルト"],[[13119,13119],"mapped","ホン"],[[13120,13120],"mapped","ポンド"],[[13121,13121],"mapped","ホール"],[[13122,13122],"mapped","ホーン"],[[13123,13123],"mapped","マイクロ"],[[13124,13124],"mapped","マイル"],[[13125,13125],"mapped","マッハ"],[[13126,13126],"mapped","マルク"],[[13127,13127],"mapped","マンション"],[[13128,13128],"mapped","ミクロン"],[[13129,13129],"mapped","ミリ"],[[13130,13130],"mapped","ミリバール"],[[13131,13131],"mapped","メガ"],[[13132,13132],"mapped","メガトン"],[[13133,13133],"mapped","メートル"],[[13134,13134],"mapped","ヤード"],[[13135,13135],"mapped","ヤール"],[[13136,13136],"mapped","ユアン"],[[13137,13137],"mapped","リットル"],[[13138,13138],"mapped","リラ"],[[13139,13139],"mapped","ルピー"],[[13140,13140],"mapped","ルーブル"],[[13141,13141],"mapped","レム"],[[13142,13142],"mapped","レントゲン"],[[13143,13143],"mapped","ワット"],[[13144,13144],"mapped","0点"],[[13145,13145],"mapped","1点"],[[13146,13146],"mapped","2点"],[[13147,13147],"mapped","3点"],[[13148,13148],"mapped","4点"],[[13149,13149],"mapped","5点"],[[13150,13150],"mapped","6点"],[[13151,13151],"mapped","7点"],[[13152,13152],"mapped","8点"],[[13153,13153],"mapped","9点"],[[13154,13154],"mapped","10点"],[[13155,13155],"mapped","11点"],[[13156,13156],"mapped","12点"],[[13157,13157],"mapped","13点"],[[13158,13158],"mapped","14点"],[[13159,13159],"mapped","15点"],[[13160,13160],"mapped","16点"],[[13161,13161],"mapped","17点"],[[13162,13162],"mapped","18点"],[[13163,13163],"mapped","19点"],[[13164,13164],"mapped","20点"],[[13165,13165],"mapped","21点"],[[13166,13166],"mapped","22点"],[[13167,13167],"mapped","23点"],[[13168,13168],"mapped","24点"],[[13169,13169],"mapped","hpa"],[[13170,13170],"mapped","da"],[[13171,13171],"mapped","au"],[[13172,13172],"mapped","bar"],[[13173,13173],"mapped","ov"],[[13174,13174],"mapped","pc"],[[13175,13175],"mapped","dm"],[[13176,13176],"mapped","dm2"],[[13177,13177],"mapped","dm3"],[[13178,13178],"mapped","iu"],[[13179,13179],"mapped","平成"],[[13180,13180],"mapped","昭和"],[[13181,13181],"mapped","大正"],[[13182,13182],"mapped","明治"],[[13183,13183],"mapped","株式会社"],[[13184,13184],"mapped","pa"],[[13185,13185],"mapped","na"],[[13186,13186],"mapped","μa"],[[13187,13187],"mapped","ma"],[[13188,13188],"mapped","ka"],[[13189,13189],"mapped","kb"],[[13190,13190],"mapped","mb"],[[13191,13191],"mapped","gb"],[[13192,13192],"mapped","cal"],[[13193,13193],"mapped","kcal"],[[13194,13194],"mapped","pf"],[[13195,13195],"mapped","nf"],[[13196,13196],"mapped","μf"],[[13197,13197],"mapped","μg"],[[13198,13198],"mapped","mg"],[[13199,13199],"mapped","kg"],[[13200,13200],"mapped","hz"],[[13201,13201],"mapped","khz"],[[13202,13202],"mapped","mhz"],[[13203,13203],"mapped","ghz"],[[13204,13204],"mapped","thz"],[[13205,13205],"mapped","μl"],[[13206,13206],"mapped","ml"],[[13207,13207],"mapped","dl"],[[13208,13208],"mapped","kl"],[[13209,13209],"mapped","fm"],[[13210,13210],"mapped","nm"],[[13211,13211],"mapped","μm"],[[13212,13212],"mapped","mm"],[[13213,13213],"mapped","cm"],[[13214,13214],"mapped","km"],[[13215,13215],"mapped","mm2"],[[13216,13216],"mapped","cm2"],[[13217,13217],"mapped","m2"],[[13218,13218],"mapped","km2"],[[13219,13219],"mapped","mm3"],[[13220,13220],"mapped","cm3"],[[13221,13221],"mapped","m3"],[[13222,13222],"mapped","km3"],[[13223,13223],"mapped","m∕s"],[[13224,13224],"mapped","m∕s2"],[[13225,13225],"mapped","pa"],[[13226,13226],"mapped","kpa"],[[13227,13227],"mapped","mpa"],[[13228,13228],"mapped","gpa"],[[13229,13229],"mapped","rad"],[[13230,13230],"mapped","rad∕s"],[[13231,13231],"mapped","rad∕s2"],[[13232,13232],"mapped","ps"],[[13233,13233],"mapped","ns"],[[13234,13234],"mapped","μs"],[[13235,13235],"mapped","ms"],[[13236,13236],"mapped","pv"],[[13237,13237],"mapped","nv"],[[13238,13238],"mapped","μv"],[[13239,13239],"mapped","mv"],[[13240,13240],"mapped","kv"],[[13241,13241],"mapped","mv"],[[13242,13242],"mapped","pw"],[[13243,13243],"mapped","nw"],[[13244,13244],"mapped","μw"],[[13245,13245],"mapped","mw"],[[13246,13246],"mapped","kw"],[[13247,13247],"mapped","mw"],[[13248,13248],"mapped","kω"],[[13249,13249],"mapped","mω"],[[13250,13250],"disallowed"],[[13251,13251],"mapped","bq"],[[13252,13252],"mapped","cc"],[[13253,13253],"mapped","cd"],[[13254,13254],"mapped","c∕kg"],[[13255,13255],"disallowed"],[[13256,13256],"mapped","db"],[[13257,13257],"mapped","gy"],[[13258,13258],"mapped","ha"],[[13259,13259],"mapped","hp"],[[13260,13260],"mapped","in"],[[13261,13261],"mapped","kk"],[[13262,13262],"mapped","km"],[[13263,13263],"mapped","kt"],[[13264,13264],"mapped","lm"],[[13265,13265],"mapped","ln"],[[13266,13266],"mapped","log"],[[13267,13267],"mapped","lx"],[[13268,13268],"mapped","mb"],[[13269,13269],"mapped","mil"],[[13270,13270],"mapped","mol"],[[13271,13271],"mapped","ph"],[[13272,13272],"disallowed"],[[13273,13273],"mapped","ppm"],[[13274,13274],"mapped","pr"],[[13275,13275],"mapped","sr"],[[13276,13276],"mapped","sv"],[[13277,13277],"mapped","wb"],[[13278,13278],"mapped","v∕m"],[[13279,13279],"mapped","a∕m"],[[13280,13280],"mapped","1日"],[[13281,13281],"mapped","2日"],[[13282,13282],"mapped","3日"],[[13283,13283],"mapped","4日"],[[13284,13284],"mapped","5日"],[[13285,13285],"mapped","6日"],[[13286,13286],"mapped","7日"],[[13287,13287],"mapped","8日"],[[13288,13288],"mapped","9日"],[[13289,13289],"mapped","10日"],[[13290,13290],"mapped","11日"],[[13291,13291],"mapped","12日"],[[13292,13292],"mapped","13日"],[[13293,13293],"mapped","14日"],[[13294,13294],"mapped","15日"],[[13295,13295],"mapped","16日"],[[13296,13296],"mapped","17日"],[[13297,13297],"mapped","18日"],[[13298,13298],"mapped","19日"],[[13299,13299],"mapped","20日"],[[13300,13300],"mapped","21日"],[[13301,13301],"mapped","22日"],[[13302,13302],"mapped","23日"],[[13303,13303],"mapped","24日"],[[13304,13304],"mapped","25日"],[[13305,13305],"mapped","26日"],[[13306,13306],"mapped","27日"],[[13307,13307],"mapped","28日"],[[13308,13308],"mapped","29日"],[[13309,13309],"mapped","30日"],[[13310,13310],"mapped","31日"],[[13311,13311],"mapped","gal"],[[13312,19893],"valid"],[[19894,19903],"disallowed"],[[19904,19967],"valid","","NV8"],[[19968,40869],"valid"],[[40870,40891],"valid"],[[40892,40899],"valid"],[[40900,40907],"valid"],[[40908,40908],"valid"],[[40909,40917],"valid"],[[40918,40938],"valid"],[[40939,40959],"disallowed"],[[40960,42124],"valid"],[[42125,42127],"disallowed"],[[42128,42145],"valid","","NV8"],[[42146,42147],"valid","","NV8"],[[42148,42163],"valid","","NV8"],[[42164,42164],"valid","","NV8"],[[42165,42176],"valid","","NV8"],[[42177,42177],"valid","","NV8"],[[42178,42180],"valid","","NV8"],[[42181,42181],"valid","","NV8"],[[42182,42182],"valid","","NV8"],[[42183,42191],"disallowed"],[[42192,42237],"valid"],[[42238,42239],"valid","","NV8"],[[42240,42508],"valid"],[[42509,42511],"valid","","NV8"],[[42512,42539],"valid"],[[42540,42559],"disallowed"],[[42560,42560],"mapped","ꙁ"],[[42561,42561],"valid"],[[42562,42562],"mapped","ꙃ"],[[42563,42563],"valid"],[[42564,42564],"mapped","ꙅ"],[[42565,42565],"valid"],[[42566,42566],"mapped","ꙇ"],[[42567,42567],"valid"],[[42568,42568],"mapped","ꙉ"],[[42569,42569],"valid"],[[42570,42570],"mapped","ꙋ"],[[42571,42571],"valid"],[[42572,42572],"mapped","ꙍ"],[[42573,42573],"valid"],[[42574,42574],"mapped","ꙏ"],[[42575,42575],"valid"],[[42576,42576],"mapped","ꙑ"],[[42577,42577],"valid"],[[42578,42578],"mapped","ꙓ"],[[42579,42579],"valid"],[[42580,42580],"mapped","ꙕ"],[[42581,42581],"valid"],[[42582,42582],"mapped","ꙗ"],[[42583,42583],"valid"],[[42584,42584],"mapped","ꙙ"],[[42585,42585],"valid"],[[42586,42586],"mapped","ꙛ"],[[42587,42587],"valid"],[[42588,42588],"mapped","ꙝ"],[[42589,42589],"valid"],[[42590,42590],"mapped","ꙟ"],[[42591,42591],"valid"],[[42592,42592],"mapped","ꙡ"],[[42593,42593],"valid"],[[42594,42594],"mapped","ꙣ"],[[42595,42595],"valid"],[[42596,42596],"mapped","ꙥ"],[[42597,42597],"valid"],[[42598,42598],"mapped","ꙧ"],[[42599,42599],"valid"],[[42600,42600],"mapped","ꙩ"],[[42601,42601],"valid"],[[42602,42602],"mapped","ꙫ"],[[42603,42603],"valid"],[[42604,42604],"mapped","ꙭ"],[[42605,42607],"valid"],[[42608,42611],"valid","","NV8"],[[42612,42619],"valid"],[[42620,42621],"valid"],[[42622,42622],"valid","","NV8"],[[42623,42623],"valid"],[[42624,42624],"mapped","ꚁ"],[[42625,42625],"valid"],[[42626,42626],"mapped","ꚃ"],[[42627,42627],"valid"],[[42628,42628],"mapped","ꚅ"],[[42629,42629],"valid"],[[42630,42630],"mapped","ꚇ"],[[42631,42631],"valid"],[[42632,42632],"mapped","ꚉ"],[[42633,42633],"valid"],[[42634,42634],"mapped","ꚋ"],[[42635,42635],"valid"],[[42636,42636],"mapped","ꚍ"],[[42637,42637],"valid"],[[42638,42638],"mapped","ꚏ"],[[42639,42639],"valid"],[[42640,42640],"mapped","ꚑ"],[[42641,42641],"valid"],[[42642,42642],"mapped","ꚓ"],[[42643,42643],"valid"],[[42644,42644],"mapped","ꚕ"],[[42645,42645],"valid"],[[42646,42646],"mapped","ꚗ"],[[42647,42647],"valid"],[[42648,42648],"mapped","ꚙ"],[[42649,42649],"valid"],[[42650,42650],"mapped","ꚛ"],[[42651,42651],"valid"],[[42652,42652],"mapped","ъ"],[[42653,42653],"mapped","ь"],[[42654,42654],"valid"],[[42655,42655],"valid"],[[42656,42725],"valid"],[[42726,42735],"valid","","NV8"],[[42736,42737],"valid"],[[42738,42743],"valid","","NV8"],[[42744,42751],"disallowed"],[[42752,42774],"valid","","NV8"],[[42775,42778],"valid"],[[42779,42783],"valid"],[[42784,42785],"valid","","NV8"],[[42786,42786],"mapped","ꜣ"],[[42787,42787],"valid"],[[42788,42788],"mapped","ꜥ"],[[42789,42789],"valid"],[[42790,42790],"mapped","ꜧ"],[[42791,42791],"valid"],[[42792,42792],"mapped","ꜩ"],[[42793,42793],"valid"],[[42794,42794],"mapped","ꜫ"],[[42795,42795],"valid"],[[42796,42796],"mapped","ꜭ"],[[42797,42797],"valid"],[[42798,42798],"mapped","ꜯ"],[[42799,42801],"valid"],[[42802,42802],"mapped","ꜳ"],[[42803,42803],"valid"],[[42804,42804],"mapped","ꜵ"],[[42805,42805],"valid"],[[42806,42806],"mapped","ꜷ"],[[42807,42807],"valid"],[[42808,42808],"mapped","ꜹ"],[[42809,42809],"valid"],[[42810,42810],"mapped","ꜻ"],[[42811,42811],"valid"],[[42812,42812],"mapped","ꜽ"],[[42813,42813],"valid"],[[42814,42814],"mapped","ꜿ"],[[42815,42815],"valid"],[[42816,42816],"mapped","ꝁ"],[[42817,42817],"valid"],[[42818,42818],"mapped","ꝃ"],[[42819,42819],"valid"],[[42820,42820],"mapped","ꝅ"],[[42821,42821],"valid"],[[42822,42822],"mapped","ꝇ"],[[42823,42823],"valid"],[[42824,42824],"mapped","ꝉ"],[[42825,42825],"valid"],[[42826,42826],"mapped","ꝋ"],[[42827,42827],"valid"],[[42828,42828],"mapped","ꝍ"],[[42829,42829],"valid"],[[42830,42830],"mapped","ꝏ"],[[42831,42831],"valid"],[[42832,42832],"mapped","ꝑ"],[[42833,42833],"valid"],[[42834,42834],"mapped","ꝓ"],[[42835,42835],"valid"],[[42836,42836],"mapped","ꝕ"],[[42837,42837],"valid"],[[42838,42838],"mapped","ꝗ"],[[42839,42839],"valid"],[[42840,42840],"mapped","ꝙ"],[[42841,42841],"valid"],[[42842,42842],"mapped","ꝛ"],[[42843,42843],"valid"],[[42844,42844],"mapped","ꝝ"],[[42845,42845],"valid"],[[42846,42846],"mapped","ꝟ"],[[42847,42847],"valid"],[[42848,42848],"mapped","ꝡ"],[[42849,42849],"valid"],[[42850,42850],"mapped","ꝣ"],[[42851,42851],"valid"],[[42852,42852],"mapped","ꝥ"],[[42853,42853],"valid"],[[42854,42854],"mapped","ꝧ"],[[42855,42855],"valid"],[[42856,42856],"mapped","ꝩ"],[[42857,42857],"valid"],[[42858,42858],"mapped","ꝫ"],[[42859,42859],"valid"],[[42860,42860],"mapped","ꝭ"],[[42861,42861],"valid"],[[42862,42862],"mapped","ꝯ"],[[42863,42863],"valid"],[[42864,42864],"mapped","ꝯ"],[[42865,42872],"valid"],[[42873,42873],"mapped","ꝺ"],[[42874,42874],"valid"],[[42875,42875],"mapped","ꝼ"],[[42876,42876],"valid"],[[42877,42877],"mapped","ᵹ"],[[42878,42878],"mapped","ꝿ"],[[42879,42879],"valid"],[[42880,42880],"mapped","ꞁ"],[[42881,42881],"valid"],[[42882,42882],"mapped","ꞃ"],[[42883,42883],"valid"],[[42884,42884],"mapped","ꞅ"],[[42885,42885],"valid"],[[42886,42886],"mapped","ꞇ"],[[42887,42888],"valid"],[[42889,42890],"valid","","NV8"],[[42891,42891],"mapped","ꞌ"],[[42892,42892],"valid"],[[42893,42893],"mapped","ɥ"],[[42894,42894],"valid"],[[42895,42895],"valid"],[[42896,42896],"mapped","ꞑ"],[[42897,42897],"valid"],[[42898,42898],"mapped","ꞓ"],[[42899,42899],"valid"],[[42900,42901],"valid"],[[42902,42902],"mapped","ꞗ"],[[42903,42903],"valid"],[[42904,42904],"mapped","ꞙ"],[[42905,42905],"valid"],[[42906,42906],"mapped","ꞛ"],[[42907,42907],"valid"],[[42908,42908],"mapped","ꞝ"],[[42909,42909],"valid"],[[42910,42910],"mapped","ꞟ"],[[42911,42911],"valid"],[[42912,42912],"mapped","ꞡ"],[[42913,42913],"valid"],[[42914,42914],"mapped","ꞣ"],[[42915,42915],"valid"],[[42916,42916],"mapped","ꞥ"],[[42917,42917],"valid"],[[42918,42918],"mapped","ꞧ"],[[42919,42919],"valid"],[[42920,42920],"mapped","ꞩ"],[[42921,42921],"valid"],[[42922,42922],"mapped","ɦ"],[[42923,42923],"mapped","ɜ"],[[42924,42924],"mapped","ɡ"],[[42925,42925],"mapped","ɬ"],[[42926,42926],"mapped","ɪ"],[[42927,42927],"disallowed"],[[42928,42928],"mapped","ʞ"],[[42929,42929],"mapped","ʇ"],[[42930,42930],"mapped","ʝ"],[[42931,42931],"mapped","ꭓ"],[[42932,42932],"mapped","ꞵ"],[[42933,42933],"valid"],[[42934,42934],"mapped","ꞷ"],[[42935,42935],"valid"],[[42936,42998],"disallowed"],[[42999,42999],"valid"],[[43000,43000],"mapped","ħ"],[[43001,43001],"mapped","œ"],[[43002,43002],"valid"],[[43003,43007],"valid"],[[43008,43047],"valid"],[[43048,43051],"valid","","NV8"],[[43052,43055],"disallowed"],[[43056,43065],"valid","","NV8"],[[43066,43071],"disallowed"],[[43072,43123],"valid"],[[43124,43127],"valid","","NV8"],[[43128,43135],"disallowed"],[[43136,43204],"valid"],[[43205,43205],"valid"],[[43206,43213],"disallowed"],[[43214,43215],"valid","","NV8"],[[43216,43225],"valid"],[[43226,43231],"disallowed"],[[43232,43255],"valid"],[[43256,43258],"valid","","NV8"],[[43259,43259],"valid"],[[43260,43260],"valid","","NV8"],[[43261,43261],"valid"],[[43262,43263],"disallowed"],[[43264,43309],"valid"],[[43310,43311],"valid","","NV8"],[[43312,43347],"valid"],[[43348,43358],"disallowed"],[[43359,43359],"valid","","NV8"],[[43360,43388],"valid","","NV8"],[[43389,43391],"disallowed"],[[43392,43456],"valid"],[[43457,43469],"valid","","NV8"],[[43470,43470],"disallowed"],[[43471,43481],"valid"],[[43482,43485],"disallowed"],[[43486,43487],"valid","","NV8"],[[43488,43518],"valid"],[[43519,43519],"disallowed"],[[43520,43574],"valid"],[[43575,43583],"disallowed"],[[43584,43597],"valid"],[[43598,43599],"disallowed"],[[43600,43609],"valid"],[[43610,43611],"disallowed"],[[43612,43615],"valid","","NV8"],[[43616,43638],"valid"],[[43639,43641],"valid","","NV8"],[[43642,43643],"valid"],[[43644,43647],"valid"],[[43648,43714],"valid"],[[43715,43738],"disallowed"],[[43739,43741],"valid"],[[43742,43743],"valid","","NV8"],[[43744,43759],"valid"],[[43760,43761],"valid","","NV8"],[[43762,43766],"valid"],[[43767,43776],"disallowed"],[[43777,43782],"valid"],[[43783,43784],"disallowed"],[[43785,43790],"valid"],[[43791,43792],"disallowed"],[[43793,43798],"valid"],[[43799,43807],"disallowed"],[[43808,43814],"valid"],[[43815,43815],"disallowed"],[[43816,43822],"valid"],[[43823,43823],"disallowed"],[[43824,43866],"valid"],[[43867,43867],"valid","","NV8"],[[43868,43868],"mapped","ꜧ"],[[43869,43869],"mapped","ꬷ"],[[43870,43870],"mapped","ɫ"],[[43871,43871],"mapped","ꭒ"],[[43872,43875],"valid"],[[43876,43877],"valid"],[[43878,43887],"disallowed"],[[43888,43888],"mapped","Ꭰ"],[[43889,43889],"mapped","Ꭱ"],[[43890,43890],"mapped","Ꭲ"],[[43891,43891],"mapped","Ꭳ"],[[43892,43892],"mapped","Ꭴ"],[[43893,43893],"mapped","Ꭵ"],[[43894,43894],"mapped","Ꭶ"],[[43895,43895],"mapped","Ꭷ"],[[43896,43896],"mapped","Ꭸ"],[[43897,43897],"mapped","Ꭹ"],[[43898,43898],"mapped","Ꭺ"],[[43899,43899],"mapped","Ꭻ"],[[43900,43900],"mapped","Ꭼ"],[[43901,43901],"mapped","Ꭽ"],[[43902,43902],"mapped","Ꭾ"],[[43903,43903],"mapped","Ꭿ"],[[43904,43904],"mapped","Ꮀ"],[[43905,43905],"mapped","Ꮁ"],[[43906,43906],"mapped","Ꮂ"],[[43907,43907],"mapped","Ꮃ"],[[43908,43908],"mapped","Ꮄ"],[[43909,43909],"mapped","Ꮅ"],[[43910,43910],"mapped","Ꮆ"],[[43911,43911],"mapped","Ꮇ"],[[43912,43912],"mapped","Ꮈ"],[[43913,43913],"mapped","Ꮉ"],[[43914,43914],"mapped","Ꮊ"],[[43915,43915],"mapped","Ꮋ"],[[43916,43916],"mapped","Ꮌ"],[[43917,43917],"mapped","Ꮍ"],[[43918,43918],"mapped","Ꮎ"],[[43919,43919],"mapped","Ꮏ"],[[43920,43920],"mapped","Ꮐ"],[[43921,43921],"mapped","Ꮑ"],[[43922,43922],"mapped","Ꮒ"],[[43923,43923],"mapped","Ꮓ"],[[43924,43924],"mapped","Ꮔ"],[[43925,43925],"mapped","Ꮕ"],[[43926,43926],"mapped","Ꮖ"],[[43927,43927],"mapped","Ꮗ"],[[43928,43928],"mapped","Ꮘ"],[[43929,43929],"mapped","Ꮙ"],[[43930,43930],"mapped","Ꮚ"],[[43931,43931],"mapped","Ꮛ"],[[43932,43932],"mapped","Ꮜ"],[[43933,43933],"mapped","Ꮝ"],[[43934,43934],"mapped","Ꮞ"],[[43935,43935],"mapped","Ꮟ"],[[43936,43936],"mapped","Ꮠ"],[[43937,43937],"mapped","Ꮡ"],[[43938,43938],"mapped","Ꮢ"],[[43939,43939],"mapped","Ꮣ"],[[43940,43940],"mapped","Ꮤ"],[[43941,43941],"mapped","Ꮥ"],[[43942,43942],"mapped","Ꮦ"],[[43943,43943],"mapped","Ꮧ"],[[43944,43944],"mapped","Ꮨ"],[[43945,43945],"mapped","Ꮩ"],[[43946,43946],"mapped","Ꮪ"],[[43947,43947],"mapped","Ꮫ"],[[43948,43948],"mapped","Ꮬ"],[[43949,43949],"mapped","Ꮭ"],[[43950,43950],"mapped","Ꮮ"],[[43951,43951],"mapped","Ꮯ"],[[43952,43952],"mapped","Ꮰ"],[[43953,43953],"mapped","Ꮱ"],[[43954,43954],"mapped","Ꮲ"],[[43955,43955],"mapped","Ꮳ"],[[43956,43956],"mapped","Ꮴ"],[[43957,43957],"mapped","Ꮵ"],[[43958,43958],"mapped","Ꮶ"],[[43959,43959],"mapped","Ꮷ"],[[43960,43960],"mapped","Ꮸ"],[[43961,43961],"mapped","Ꮹ"],[[43962,43962],"mapped","Ꮺ"],[[43963,43963],"mapped","Ꮻ"],[[43964,43964],"mapped","Ꮼ"],[[43965,43965],"mapped","Ꮽ"],[[43966,43966],"mapped","Ꮾ"],[[43967,43967],"mapped","Ꮿ"],[[43968,44010],"valid"],[[44011,44011],"valid","","NV8"],[[44012,44013],"valid"],[[44014,44015],"disallowed"],[[44016,44025],"valid"],[[44026,44031],"disallowed"],[[44032,55203],"valid"],[[55204,55215],"disallowed"],[[55216,55238],"valid","","NV8"],[[55239,55242],"disallowed"],[[55243,55291],"valid","","NV8"],[[55292,55295],"disallowed"],[[55296,57343],"disallowed"],[[57344,63743],"disallowed"],[[63744,63744],"mapped","豈"],[[63745,63745],"mapped","更"],[[63746,63746],"mapped","車"],[[63747,63747],"mapped","賈"],[[63748,63748],"mapped","滑"],[[63749,63749],"mapped","串"],[[63750,63750],"mapped","句"],[[63751,63752],"mapped","龜"],[[63753,63753],"mapped","契"],[[63754,63754],"mapped","金"],[[63755,63755],"mapped","喇"],[[63756,63756],"mapped","奈"],[[63757,63757],"mapped","懶"],[[63758,63758],"mapped","癩"],[[63759,63759],"mapped","羅"],[[63760,63760],"mapped","蘿"],[[63761,63761],"mapped","螺"],[[63762,63762],"mapped","裸"],[[63763,63763],"mapped","邏"],[[63764,63764],"mapped","樂"],[[63765,63765],"mapped","洛"],[[63766,63766],"mapped","烙"],[[63767,63767],"mapped","珞"],[[63768,63768],"mapped","落"],[[63769,63769],"mapped","酪"],[[63770,63770],"mapped","駱"],[[63771,63771],"mapped","亂"],[[63772,63772],"mapped","卵"],[[63773,63773],"mapped","欄"],[[63774,63774],"mapped","爛"],[[63775,63775],"mapped","蘭"],[[63776,63776],"mapped","鸞"],[[63777,63777],"mapped","嵐"],[[63778,63778],"mapped","濫"],[[63779,63779],"mapped","藍"],[[63780,63780],"mapped","襤"],[[63781,63781],"mapped","拉"],[[63782,63782],"mapped","臘"],[[63783,63783],"mapped","蠟"],[[63784,63784],"mapped","廊"],[[63785,63785],"mapped","朗"],[[63786,63786],"mapped","浪"],[[63787,63787],"mapped","狼"],[[63788,63788],"mapped","郎"],[[63789,63789],"mapped","來"],[[63790,63790],"mapped","冷"],[[63791,63791],"mapped","勞"],[[63792,63792],"mapped","擄"],[[63793,63793],"mapped","櫓"],[[63794,63794],"mapped","爐"],[[63795,63795],"mapped","盧"],[[63796,63796],"mapped","老"],[[63797,63797],"mapped","蘆"],[[63798,63798],"mapped","虜"],[[63799,63799],"mapped","路"],[[63800,63800],"mapped","露"],[[63801,63801],"mapped","魯"],[[63802,63802],"mapped","鷺"],[[63803,63803],"mapped","碌"],[[63804,63804],"mapped","祿"],[[63805,63805],"mapped","綠"],[[63806,63806],"mapped","菉"],[[63807,63807],"mapped","錄"],[[63808,63808],"mapped","鹿"],[[63809,63809],"mapped","論"],[[63810,63810],"mapped","壟"],[[63811,63811],"mapped","弄"],[[63812,63812],"mapped","籠"],[[63813,63813],"mapped","聾"],[[63814,63814],"mapped","牢"],[[63815,63815],"mapped","磊"],[[63816,63816],"mapped","賂"],[[63817,63817],"mapped","雷"],[[63818,63818],"mapped","壘"],[[63819,63819],"mapped","屢"],[[63820,63820],"mapped","樓"],[[63821,63821],"mapped","淚"],[[63822,63822],"mapped","漏"],[[63823,63823],"mapped","累"],[[63824,63824],"mapped","縷"],[[63825,63825],"mapped","陋"],[[63826,63826],"mapped","勒"],[[63827,63827],"mapped","肋"],[[63828,63828],"mapped","凜"],[[63829,63829],"mapped","凌"],[[63830,63830],"mapped","稜"],[[63831,63831],"mapped","綾"],[[63832,63832],"mapped","菱"],[[63833,63833],"mapped","陵"],[[63834,63834],"mapped","讀"],[[63835,63835],"mapped","拏"],[[63836,63836],"mapped","樂"],[[63837,63837],"mapped","諾"],[[63838,63838],"mapped","丹"],[[63839,63839],"mapped","寧"],[[63840,63840],"mapped","怒"],[[63841,63841],"mapped","率"],[[63842,63842],"mapped","異"],[[63843,63843],"mapped","北"],[[63844,63844],"mapped","磻"],[[63845,63845],"mapped","便"],[[63846,63846],"mapped","復"],[[63847,63847],"mapped","不"],[[63848,63848],"mapped","泌"],[[63849,63849],"mapped","數"],[[63850,63850],"mapped","索"],[[63851,63851],"mapped","參"],[[63852,63852],"mapped","塞"],[[63853,63853],"mapped","省"],[[63854,63854],"mapped","葉"],[[63855,63855],"mapped","說"],[[63856,63856],"mapped","殺"],[[63857,63857],"mapped","辰"],[[63858,63858],"mapped","沈"],[[63859,63859],"mapped","拾"],[[63860,63860],"mapped","若"],[[63861,63861],"mapped","掠"],[[63862,63862],"mapped","略"],[[63863,63863],"mapped","亮"],[[63864,63864],"mapped","兩"],[[63865,63865],"mapped","凉"],[[63866,63866],"mapped","梁"],[[63867,63867],"mapped","糧"],[[63868,63868],"mapped","良"],[[63869,63869],"mapped","諒"],[[63870,63870],"mapped","量"],[[63871,63871],"mapped","勵"],[[63872,63872],"mapped","呂"],[[63873,63873],"mapped","女"],[[63874,63874],"mapped","廬"],[[63875,63875],"mapped","旅"],[[63876,63876],"mapped","濾"],[[63877,63877],"mapped","礪"],[[63878,63878],"mapped","閭"],[[63879,63879],"mapped","驪"],[[63880,63880],"mapped","麗"],[[63881,63881],"mapped","黎"],[[63882,63882],"mapped","力"],[[63883,63883],"mapped","曆"],[[63884,63884],"mapped","歷"],[[63885,63885],"mapped","轢"],[[63886,63886],"mapped","年"],[[63887,63887],"mapped","憐"],[[63888,63888],"mapped","戀"],[[63889,63889],"mapped","撚"],[[63890,63890],"mapped","漣"],[[63891,63891],"mapped","煉"],[[63892,63892],"mapped","璉"],[[63893,63893],"mapped","秊"],[[63894,63894],"mapped","練"],[[63895,63895],"mapped","聯"],[[63896,63896],"mapped","輦"],[[63897,63897],"mapped","蓮"],[[63898,63898],"mapped","連"],[[63899,63899],"mapped","鍊"],[[63900,63900],"mapped","列"],[[63901,63901],"mapped","劣"],[[63902,63902],"mapped","咽"],[[63903,63903],"mapped","烈"],[[63904,63904],"mapped","裂"],[[63905,63905],"mapped","說"],[[63906,63906],"mapped","廉"],[[63907,63907],"mapped","念"],[[63908,63908],"mapped","捻"],[[63909,63909],"mapped","殮"],[[63910,63910],"mapped","簾"],[[63911,63911],"mapped","獵"],[[63912,63912],"mapped","令"],[[63913,63913],"mapped","囹"],[[63914,63914],"mapped","寧"],[[63915,63915],"mapped","嶺"],[[63916,63916],"mapped","怜"],[[63917,63917],"mapped","玲"],[[63918,63918],"mapped","瑩"],[[63919,63919],"mapped","羚"],[[63920,63920],"mapped","聆"],[[63921,63921],"mapped","鈴"],[[63922,63922],"mapped","零"],[[63923,63923],"mapped","靈"],[[63924,63924],"mapped","領"],[[63925,63925],"mapped","例"],[[63926,63926],"mapped","禮"],[[63927,63927],"mapped","醴"],[[63928,63928],"mapped","隸"],[[63929,63929],"mapped","惡"],[[63930,63930],"mapped","了"],[[63931,63931],"mapped","僚"],[[63932,63932],"mapped","寮"],[[63933,63933],"mapped","尿"],[[63934,63934],"mapped","料"],[[63935,63935],"mapped","樂"],[[63936,63936],"mapped","燎"],[[63937,63937],"mapped","療"],[[63938,63938],"mapped","蓼"],[[63939,63939],"mapped","遼"],[[63940,63940],"mapped","龍"],[[63941,63941],"mapped","暈"],[[63942,63942],"mapped","阮"],[[63943,63943],"mapped","劉"],[[63944,63944],"mapped","杻"],[[63945,63945],"mapped","柳"],[[63946,63946],"mapped","流"],[[63947,63947],"mapped","溜"],[[63948,63948],"mapped","琉"],[[63949,63949],"mapped","留"],[[63950,63950],"mapped","硫"],[[63951,63951],"mapped","紐"],[[63952,63952],"mapped","類"],[[63953,63953],"mapped","六"],[[63954,63954],"mapped","戮"],[[63955,63955],"mapped","陸"],[[63956,63956],"mapped","倫"],[[63957,63957],"mapped","崙"],[[63958,63958],"mapped","淪"],[[63959,63959],"mapped","輪"],[[63960,63960],"mapped","律"],[[63961,63961],"mapped","慄"],[[63962,63962],"mapped","栗"],[[63963,63963],"mapped","率"],[[63964,63964],"mapped","隆"],[[63965,63965],"mapped","利"],[[63966,63966],"mapped","吏"],[[63967,63967],"mapped","履"],[[63968,63968],"mapped","易"],[[63969,63969],"mapped","李"],[[63970,63970],"mapped","梨"],[[63971,63971],"mapped","泥"],[[63972,63972],"mapped","理"],[[63973,63973],"mapped","痢"],[[63974,63974],"mapped","罹"],[[63975,63975],"mapped","裏"],[[63976,63976],"mapped","裡"],[[63977,63977],"mapped","里"],[[63978,63978],"mapped","離"],[[63979,63979],"mapped","匿"],[[63980,63980],"mapped","溺"],[[63981,63981],"mapped","吝"],[[63982,63982],"mapped","燐"],[[63983,63983],"mapped","璘"],[[63984,63984],"mapped","藺"],[[63985,63985],"mapped","隣"],[[63986,63986],"mapped","鱗"],[[63987,63987],"mapped","麟"],[[63988,63988],"mapped","林"],[[63989,63989],"mapped","淋"],[[63990,63990],"mapped","臨"],[[63991,63991],"mapped","立"],[[63992,63992],"mapped","笠"],[[63993,63993],"mapped","粒"],[[63994,63994],"mapped","狀"],[[63995,63995],"mapped","炙"],[[63996,63996],"mapped","識"],[[63997,63997],"mapped","什"],[[63998,63998],"mapped","茶"],[[63999,63999],"mapped","刺"],[[64000,64000],"mapped","切"],[[64001,64001],"mapped","度"],[[64002,64002],"mapped","拓"],[[64003,64003],"mapped","糖"],[[64004,64004],"mapped","宅"],[[64005,64005],"mapped","洞"],[[64006,64006],"mapped","暴"],[[64007,64007],"mapped","輻"],[[64008,64008],"mapped","行"],[[64009,64009],"mapped","降"],[[64010,64010],"mapped","見"],[[64011,64011],"mapped","廓"],[[64012,64012],"mapped","兀"],[[64013,64013],"mapped","嗀"],[[64014,64015],"valid"],[[64016,64016],"mapped","塚"],[[64017,64017],"valid"],[[64018,64018],"mapped","晴"],[[64019,64020],"valid"],[[64021,64021],"mapped","凞"],[[64022,64022],"mapped","猪"],[[64023,64023],"mapped","益"],[[64024,64024],"mapped","礼"],[[64025,64025],"mapped","神"],[[64026,64026],"mapped","祥"],[[64027,64027],"mapped","福"],[[64028,64028],"mapped","靖"],[[64029,64029],"mapped","精"],[[64030,64030],"mapped","羽"],[[64031,64031],"valid"],[[64032,64032],"mapped","蘒"],[[64033,64033],"valid"],[[64034,64034],"mapped","諸"],[[64035,64036],"valid"],[[64037,64037],"mapped","逸"],[[64038,64038],"mapped","都"],[[64039,64041],"valid"],[[64042,64042],"mapped","飯"],[[64043,64043],"mapped","飼"],[[64044,64044],"mapped","館"],[[64045,64045],"mapped","鶴"],[[64046,64046],"mapped","郞"],[[64047,64047],"mapped","隷"],[[64048,64048],"mapped","侮"],[[64049,64049],"mapped","僧"],[[64050,64050],"mapped","免"],[[64051,64051],"mapped","勉"],[[64052,64052],"mapped","勤"],[[64053,64053],"mapped","卑"],[[64054,64054],"mapped","喝"],[[64055,64055],"mapped","嘆"],[[64056,64056],"mapped","器"],[[64057,64057],"mapped","塀"],[[64058,64058],"mapped","墨"],[[64059,64059],"mapped","層"],[[64060,64060],"mapped","屮"],[[64061,64061],"mapped","悔"],[[64062,64062],"mapped","慨"],[[64063,64063],"mapped","憎"],[[64064,64064],"mapped","懲"],[[64065,64065],"mapped","敏"],[[64066,64066],"mapped","既"],[[64067,64067],"mapped","暑"],[[64068,64068],"mapped","梅"],[[64069,64069],"mapped","海"],[[64070,64070],"mapped","渚"],[[64071,64071],"mapped","漢"],[[64072,64072],"mapped","煮"],[[64073,64073],"mapped","爫"],[[64074,64074],"mapped","琢"],[[64075,64075],"mapped","碑"],[[64076,64076],"mapped","社"],[[64077,64077],"mapped","祉"],[[64078,64078],"mapped","祈"],[[64079,64079],"mapped","祐"],[[64080,64080],"mapped","祖"],[[64081,64081],"mapped","祝"],[[64082,64082],"mapped","禍"],[[64083,64083],"mapped","禎"],[[64084,64084],"mapped","穀"],[[64085,64085],"mapped","突"],[[64086,64086],"mapped","節"],[[64087,64087],"mapped","練"],[[64088,64088],"mapped","縉"],[[64089,64089],"mapped","繁"],[[64090,64090],"mapped","署"],[[64091,64091],"mapped","者"],[[64092,64092],"mapped","臭"],[[64093,64094],"mapped","艹"],[[64095,64095],"mapped","著"],[[64096,64096],"mapped","褐"],[[64097,64097],"mapped","視"],[[64098,64098],"mapped","謁"],[[64099,64099],"mapped","謹"],[[64100,64100],"mapped","賓"],[[64101,64101],"mapped","贈"],[[64102,64102],"mapped","辶"],[[64103,64103],"mapped","逸"],[[64104,64104],"mapped","難"],[[64105,64105],"mapped","響"],[[64106,64106],"mapped","頻"],[[64107,64107],"mapped","恵"],[[64108,64108],"mapped","𤋮"],[[64109,64109],"mapped","舘"],[[64110,64111],"disallowed"],[[64112,64112],"mapped","並"],[[64113,64113],"mapped","况"],[[64114,64114],"mapped","全"],[[64115,64115],"mapped","侀"],[[64116,64116],"mapped","充"],[[64117,64117],"mapped","冀"],[[64118,64118],"mapped","勇"],[[64119,64119],"mapped","勺"],[[64120,64120],"mapped","喝"],[[64121,64121],"mapped","啕"],[[64122,64122],"mapped","喙"],[[64123,64123],"mapped","嗢"],[[64124,64124],"mapped","塚"],[[64125,64125],"mapped","墳"],[[64126,64126],"mapped","奄"],[[64127,64127],"mapped","奔"],[[64128,64128],"mapped","婢"],[[64129,64129],"mapped","嬨"],[[64130,64130],"mapped","廒"],[[64131,64131],"mapped","廙"],[[64132,64132],"mapped","彩"],[[64133,64133],"mapped","徭"],[[64134,64134],"mapped","惘"],[[64135,64135],"mapped","慎"],[[64136,64136],"mapped","愈"],[[64137,64137],"mapped","憎"],[[64138,64138],"mapped","慠"],[[64139,64139],"mapped","懲"],[[64140,64140],"mapped","戴"],[[64141,64141],"mapped","揄"],[[64142,64142],"mapped","搜"],[[64143,64143],"mapped","摒"],[[64144,64144],"mapped","敖"],[[64145,64145],"mapped","晴"],[[64146,64146],"mapped","朗"],[[64147,64147],"mapped","望"],[[64148,64148],"mapped","杖"],[[64149,64149],"mapped","歹"],[[64150,64150],"mapped","殺"],[[64151,64151],"mapped","流"],[[64152,64152],"mapped","滛"],[[64153,64153],"mapped","滋"],[[64154,64154],"mapped","漢"],[[64155,64155],"mapped","瀞"],[[64156,64156],"mapped","煮"],[[64157,64157],"mapped","瞧"],[[64158,64158],"mapped","爵"],[[64159,64159],"mapped","犯"],[[64160,64160],"mapped","猪"],[[64161,64161],"mapped","瑱"],[[64162,64162],"mapped","甆"],[[64163,64163],"mapped","画"],[[64164,64164],"mapped","瘝"],[[64165,64165],"mapped","瘟"],[[64166,64166],"mapped","益"],[[64167,64167],"mapped","盛"],[[64168,64168],"mapped","直"],[[64169,64169],"mapped","睊"],[[64170,64170],"mapped","着"],[[64171,64171],"mapped","磌"],[[64172,64172],"mapped","窱"],[[64173,64173],"mapped","節"],[[64174,64174],"mapped","类"],[[64175,64175],"mapped","絛"],[[64176,64176],"mapped","練"],[[64177,64177],"mapped","缾"],[[64178,64178],"mapped","者"],[[64179,64179],"mapped","荒"],[[64180,64180],"mapped","華"],[[64181,64181],"mapped","蝹"],[[64182,64182],"mapped","襁"],[[64183,64183],"mapped","覆"],[[64184,64184],"mapped","視"],[[64185,64185],"mapped","調"],[[64186,64186],"mapped","諸"],[[64187,64187],"mapped","請"],[[64188,64188],"mapped","謁"],[[64189,64189],"mapped","諾"],[[64190,64190],"mapped","諭"],[[64191,64191],"mapped","謹"],[[64192,64192],"mapped","變"],[[64193,64193],"mapped","贈"],[[64194,64194],"mapped","輸"],[[64195,64195],"mapped","遲"],[[64196,64196],"mapped","醙"],[[64197,64197],"mapped","鉶"],[[64198,64198],"mapped","陼"],[[64199,64199],"mapped","難"],[[64200,64200],"mapped","靖"],[[64201,64201],"mapped","韛"],[[64202,64202],"mapped","響"],[[64203,64203],"mapped","頋"],[[64204,64204],"mapped","頻"],[[64205,64205],"mapped","鬒"],[[64206,64206],"mapped","龜"],[[64207,64207],"mapped","𢡊"],[[64208,64208],"mapped","𢡄"],[[64209,64209],"mapped","𣏕"],[[64210,64210],"mapped","㮝"],[[64211,64211],"mapped","䀘"],[[64212,64212],"mapped","䀹"],[[64213,64213],"mapped","𥉉"],[[64214,64214],"mapped","𥳐"],[[64215,64215],"mapped","𧻓"],[[64216,64216],"mapped","齃"],[[64217,64217],"mapped","龎"],[[64218,64255],"disallowed"],[[64256,64256],"mapped","ff"],[[64257,64257],"mapped","fi"],[[64258,64258],"mapped","fl"],[[64259,64259],"mapped","ffi"],[[64260,64260],"mapped","ffl"],[[64261,64262],"mapped","st"],[[64263,64274],"disallowed"],[[64275,64275],"mapped","մն"],[[64276,64276],"mapped","մե"],[[64277,64277],"mapped","մի"],[[64278,64278],"mapped","վն"],[[64279,64279],"mapped","մխ"],[[64280,64284],"disallowed"],[[64285,64285],"mapped","יִ"],[[64286,64286],"valid"],[[64287,64287],"mapped","ײַ"],[[64288,64288],"mapped","ע"],[[64289,64289],"mapped","א"],[[64290,64290],"mapped","ד"],[[64291,64291],"mapped","ה"],[[64292,64292],"mapped","כ"],[[64293,64293],"mapped","ל"],[[64294,64294],"mapped","ם"],[[64295,64295],"mapped","ר"],[[64296,64296],"mapped","ת"],[[64297,64297],"disallowed_STD3_mapped","+"],[[64298,64298],"mapped","שׁ"],[[64299,64299],"mapped","שׂ"],[[64300,64300],"mapped","שּׁ"],[[64301,64301],"mapped","שּׂ"],[[64302,64302],"mapped","אַ"],[[64303,64303],"mapped","אָ"],[[64304,64304],"mapped","אּ"],[[64305,64305],"mapped","בּ"],[[64306,64306],"mapped","גּ"],[[64307,64307],"mapped","דּ"],[[64308,64308],"mapped","הּ"],[[64309,64309],"mapped","וּ"],[[64310,64310],"mapped","זּ"],[[64311,64311],"disallowed"],[[64312,64312],"mapped","טּ"],[[64313,64313],"mapped","יּ"],[[64314,64314],"mapped","ךּ"],[[64315,64315],"mapped","כּ"],[[64316,64316],"mapped","לּ"],[[64317,64317],"disallowed"],[[64318,64318],"mapped","מּ"],[[64319,64319],"disallowed"],[[64320,64320],"mapped","נּ"],[[64321,64321],"mapped","סּ"],[[64322,64322],"disallowed"],[[64323,64323],"mapped","ףּ"],[[64324,64324],"mapped","פּ"],[[64325,64325],"disallowed"],[[64326,64326],"mapped","צּ"],[[64327,64327],"mapped","קּ"],[[64328,64328],"mapped","רּ"],[[64329,64329],"mapped","שּ"],[[64330,64330],"mapped","תּ"],[[64331,64331],"mapped","וֹ"],[[64332,64332],"mapped","בֿ"],[[64333,64333],"mapped","כֿ"],[[64334,64334],"mapped","פֿ"],[[64335,64335],"mapped","אל"],[[64336,64337],"mapped","ٱ"],[[64338,64341],"mapped","ٻ"],[[64342,64345],"mapped","پ"],[[64346,64349],"mapped","ڀ"],[[64350,64353],"mapped","ٺ"],[[64354,64357],"mapped","ٿ"],[[64358,64361],"mapped","ٹ"],[[64362,64365],"mapped","ڤ"],[[64366,64369],"mapped","ڦ"],[[64370,64373],"mapped","ڄ"],[[64374,64377],"mapped","ڃ"],[[64378,64381],"mapped","چ"],[[64382,64385],"mapped","ڇ"],[[64386,64387],"mapped","ڍ"],[[64388,64389],"mapped","ڌ"],[[64390,64391],"mapped","ڎ"],[[64392,64393],"mapped","ڈ"],[[64394,64395],"mapped","ژ"],[[64396,64397],"mapped","ڑ"],[[64398,64401],"mapped","ک"],[[64402,64405],"mapped","گ"],[[64406,64409],"mapped","ڳ"],[[64410,64413],"mapped","ڱ"],[[64414,64415],"mapped","ں"],[[64416,64419],"mapped","ڻ"],[[64420,64421],"mapped","ۀ"],[[64422,64425],"mapped","ہ"],[[64426,64429],"mapped","ھ"],[[64430,64431],"mapped","ے"],[[64432,64433],"mapped","ۓ"],[[64434,64449],"valid","","NV8"],[[64450,64466],"disallowed"],[[64467,64470],"mapped","ڭ"],[[64471,64472],"mapped","ۇ"],[[64473,64474],"mapped","ۆ"],[[64475,64476],"mapped","ۈ"],[[64477,64477],"mapped","ۇٴ"],[[64478,64479],"mapped","ۋ"],[[64480,64481],"mapped","ۅ"],[[64482,64483],"mapped","ۉ"],[[64484,64487],"mapped","ې"],[[64488,64489],"mapped","ى"],[[64490,64491],"mapped","ئا"],[[64492,64493],"mapped","ئە"],[[64494,64495],"mapped","ئو"],[[64496,64497],"mapped","ئۇ"],[[64498,64499],"mapped","ئۆ"],[[64500,64501],"mapped","ئۈ"],[[64502,64504],"mapped","ئې"],[[64505,64507],"mapped","ئى"],[[64508,64511],"mapped","ی"],[[64512,64512],"mapped","ئج"],[[64513,64513],"mapped","ئح"],[[64514,64514],"mapped","ئم"],[[64515,64515],"mapped","ئى"],[[64516,64516],"mapped","ئي"],[[64517,64517],"mapped","بج"],[[64518,64518],"mapped","بح"],[[64519,64519],"mapped","بخ"],[[64520,64520],"mapped","بم"],[[64521,64521],"mapped","بى"],[[64522,64522],"mapped","بي"],[[64523,64523],"mapped","تج"],[[64524,64524],"mapped","تح"],[[64525,64525],"mapped","تخ"],[[64526,64526],"mapped","تم"],[[64527,64527],"mapped","تى"],[[64528,64528],"mapped","تي"],[[64529,64529],"mapped","ثج"],[[64530,64530],"mapped","ثم"],[[64531,64531],"mapped","ثى"],[[64532,64532],"mapped","ثي"],[[64533,64533],"mapped","جح"],[[64534,64534],"mapped","جم"],[[64535,64535],"mapped","حج"],[[64536,64536],"mapped","حم"],[[64537,64537],"mapped","خج"],[[64538,64538],"mapped","خح"],[[64539,64539],"mapped","خم"],[[64540,64540],"mapped","سج"],[[64541,64541],"mapped","سح"],[[64542,64542],"mapped","سخ"],[[64543,64543],"mapped","سم"],[[64544,64544],"mapped","صح"],[[64545,64545],"mapped","صم"],[[64546,64546],"mapped","ضج"],[[64547,64547],"mapped","ضح"],[[64548,64548],"mapped","ضخ"],[[64549,64549],"mapped","ضم"],[[64550,64550],"mapped","طح"],[[64551,64551],"mapped","طم"],[[64552,64552],"mapped","ظم"],[[64553,64553],"mapped","عج"],[[64554,64554],"mapped","عم"],[[64555,64555],"mapped","غج"],[[64556,64556],"mapped","غم"],[[64557,64557],"mapped","فج"],[[64558,64558],"mapped","فح"],[[64559,64559],"mapped","فخ"],[[64560,64560],"mapped","فم"],[[64561,64561],"mapped","فى"],[[64562,64562],"mapped","في"],[[64563,64563],"mapped","قح"],[[64564,64564],"mapped","قم"],[[64565,64565],"mapped","قى"],[[64566,64566],"mapped","قي"],[[64567,64567],"mapped","كا"],[[64568,64568],"mapped","كج"],[[64569,64569],"mapped","كح"],[[64570,64570],"mapped","كخ"],[[64571,64571],"mapped","كل"],[[64572,64572],"mapped","كم"],[[64573,64573],"mapped","كى"],[[64574,64574],"mapped","كي"],[[64575,64575],"mapped","لج"],[[64576,64576],"mapped","لح"],[[64577,64577],"mapped","لخ"],[[64578,64578],"mapped","لم"],[[64579,64579],"mapped","لى"],[[64580,64580],"mapped","لي"],[[64581,64581],"mapped","مج"],[[64582,64582],"mapped","مح"],[[64583,64583],"mapped","مخ"],[[64584,64584],"mapped","مم"],[[64585,64585],"mapped","مى"],[[64586,64586],"mapped","مي"],[[64587,64587],"mapped","نج"],[[64588,64588],"mapped","نح"],[[64589,64589],"mapped","نخ"],[[64590,64590],"mapped","نم"],[[64591,64591],"mapped","نى"],[[64592,64592],"mapped","ني"],[[64593,64593],"mapped","هج"],[[64594,64594],"mapped","هم"],[[64595,64595],"mapped","هى"],[[64596,64596],"mapped","هي"],[[64597,64597],"mapped","يج"],[[64598,64598],"mapped","يح"],[[64599,64599],"mapped","يخ"],[[64600,64600],"mapped","يم"],[[64601,64601],"mapped","يى"],[[64602,64602],"mapped","يي"],[[64603,64603],"mapped","ذٰ"],[[64604,64604],"mapped","رٰ"],[[64605,64605],"mapped","ىٰ"],[[64606,64606],"disallowed_STD3_mapped"," ٌّ"],[[64607,64607],"disallowed_STD3_mapped"," ٍّ"],[[64608,64608],"disallowed_STD3_mapped"," َّ"],[[64609,64609],"disallowed_STD3_mapped"," ُّ"],[[64610,64610],"disallowed_STD3_mapped"," ِّ"],[[64611,64611],"disallowed_STD3_mapped"," ّٰ"],[[64612,64612],"mapped","ئر"],[[64613,64613],"mapped","ئز"],[[64614,64614],"mapped","ئم"],[[64615,64615],"mapped","ئن"],[[64616,64616],"mapped","ئى"],[[64617,64617],"mapped","ئي"],[[64618,64618],"mapped","بر"],[[64619,64619],"mapped","بز"],[[64620,64620],"mapped","بم"],[[64621,64621],"mapped","بن"],[[64622,64622],"mapped","بى"],[[64623,64623],"mapped","بي"],[[64624,64624],"mapped","تر"],[[64625,64625],"mapped","تز"],[[64626,64626],"mapped","تم"],[[64627,64627],"mapped","تن"],[[64628,64628],"mapped","تى"],[[64629,64629],"mapped","تي"],[[64630,64630],"mapped","ثر"],[[64631,64631],"mapped","ثز"],[[64632,64632],"mapped","ثم"],[[64633,64633],"mapped","ثن"],[[64634,64634],"mapped","ثى"],[[64635,64635],"mapped","ثي"],[[64636,64636],"mapped","فى"],[[64637,64637],"mapped","في"],[[64638,64638],"mapped","قى"],[[64639,64639],"mapped","قي"],[[64640,64640],"mapped","كا"],[[64641,64641],"mapped","كل"],[[64642,64642],"mapped","كم"],[[64643,64643],"mapped","كى"],[[64644,64644],"mapped","كي"],[[64645,64645],"mapped","لم"],[[64646,64646],"mapped","لى"],[[64647,64647],"mapped","لي"],[[64648,64648],"mapped","ما"],[[64649,64649],"mapped","مم"],[[64650,64650],"mapped","نر"],[[64651,64651],"mapped","نز"],[[64652,64652],"mapped","نم"],[[64653,64653],"mapped","نن"],[[64654,64654],"mapped","نى"],[[64655,64655],"mapped","ني"],[[64656,64656],"mapped","ىٰ"],[[64657,64657],"mapped","ير"],[[64658,64658],"mapped","يز"],[[64659,64659],"mapped","يم"],[[64660,64660],"mapped","ين"],[[64661,64661],"mapped","يى"],[[64662,64662],"mapped","يي"],[[64663,64663],"mapped","ئج"],[[64664,64664],"mapped","ئح"],[[64665,64665],"mapped","ئخ"],[[64666,64666],"mapped","ئم"],[[64667,64667],"mapped","ئه"],[[64668,64668],"mapped","بج"],[[64669,64669],"mapped","بح"],[[64670,64670],"mapped","بخ"],[[64671,64671],"mapped","بم"],[[64672,64672],"mapped","به"],[[64673,64673],"mapped","تج"],[[64674,64674],"mapped","تح"],[[64675,64675],"mapped","تخ"],[[64676,64676],"mapped","تم"],[[64677,64677],"mapped","ته"],[[64678,64678],"mapped","ثم"],[[64679,64679],"mapped","جح"],[[64680,64680],"mapped","جم"],[[64681,64681],"mapped","حج"],[[64682,64682],"mapped","حم"],[[64683,64683],"mapped","خج"],[[64684,64684],"mapped","خم"],[[64685,64685],"mapped","سج"],[[64686,64686],"mapped","سح"],[[64687,64687],"mapped","سخ"],[[64688,64688],"mapped","سم"],[[64689,64689],"mapped","صح"],[[64690,64690],"mapped","صخ"],[[64691,64691],"mapped","صم"],[[64692,64692],"mapped","ضج"],[[64693,64693],"mapped","ضح"],[[64694,64694],"mapped","ضخ"],[[64695,64695],"mapped","ضم"],[[64696,64696],"mapped","طح"],[[64697,64697],"mapped","ظم"],[[64698,64698],"mapped","عج"],[[64699,64699],"mapped","عم"],[[64700,64700],"mapped","غج"],[[64701,64701],"mapped","غم"],[[64702,64702],"mapped","فج"],[[64703,64703],"mapped","فح"],[[64704,64704],"mapped","فخ"],[[64705,64705],"mapped","فم"],[[64706,64706],"mapped","قح"],[[64707,64707],"mapped","قم"],[[64708,64708],"mapped","كج"],[[64709,64709],"mapped","كح"],[[64710,64710],"mapped","كخ"],[[64711,64711],"mapped","كل"],[[64712,64712],"mapped","كم"],[[64713,64713],"mapped","لج"],[[64714,64714],"mapped","لح"],[[64715,64715],"mapped","لخ"],[[64716,64716],"mapped","لم"],[[64717,64717],"mapped","له"],[[64718,64718],"mapped","مج"],[[64719,64719],"mapped","مح"],[[64720,64720],"mapped","مخ"],[[64721,64721],"mapped","مم"],[[64722,64722],"mapped","نج"],[[64723,64723],"mapped","نح"],[[64724,64724],"mapped","نخ"],[[64725,64725],"mapped","نم"],[[64726,64726],"mapped","نه"],[[64727,64727],"mapped","هج"],[[64728,64728],"mapped","هم"],[[64729,64729],"mapped","هٰ"],[[64730,64730],"mapped","يج"],[[64731,64731],"mapped","يح"],[[64732,64732],"mapped","يخ"],[[64733,64733],"mapped","يم"],[[64734,64734],"mapped","يه"],[[64735,64735],"mapped","ئم"],[[64736,64736],"mapped","ئه"],[[64737,64737],"mapped","بم"],[[64738,64738],"mapped","به"],[[64739,64739],"mapped","تم"],[[64740,64740],"mapped","ته"],[[64741,64741],"mapped","ثم"],[[64742,64742],"mapped","ثه"],[[64743,64743],"mapped","سم"],[[64744,64744],"mapped","سه"],[[64745,64745],"mapped","شم"],[[64746,64746],"mapped","شه"],[[64747,64747],"mapped","كل"],[[64748,64748],"mapped","كم"],[[64749,64749],"mapped","لم"],[[64750,64750],"mapped","نم"],[[64751,64751],"mapped","نه"],[[64752,64752],"mapped","يم"],[[64753,64753],"mapped","يه"],[[64754,64754],"mapped","ـَّ"],[[64755,64755],"mapped","ـُّ"],[[64756,64756],"mapped","ـِّ"],[[64757,64757],"mapped","طى"],[[64758,64758],"mapped","طي"],[[64759,64759],"mapped","عى"],[[64760,64760],"mapped","عي"],[[64761,64761],"mapped","غى"],[[64762,64762],"mapped","غي"],[[64763,64763],"mapped","سى"],[[64764,64764],"mapped","سي"],[[64765,64765],"mapped","شى"],[[64766,64766],"mapped","شي"],[[64767,64767],"mapped","حى"],[[64768,64768],"mapped","حي"],[[64769,64769],"mapped","جى"],[[64770,64770],"mapped","جي"],[[64771,64771],"mapped","خى"],[[64772,64772],"mapped","خي"],[[64773,64773],"mapped","صى"],[[64774,64774],"mapped","صي"],[[64775,64775],"mapped","ضى"],[[64776,64776],"mapped","ضي"],[[64777,64777],"mapped","شج"],[[64778,64778],"mapped","شح"],[[64779,64779],"mapped","شخ"],[[64780,64780],"mapped","شم"],[[64781,64781],"mapped","شر"],[[64782,64782],"mapped","سر"],[[64783,64783],"mapped","صر"],[[64784,64784],"mapped","ضر"],[[64785,64785],"mapped","طى"],[[64786,64786],"mapped","طي"],[[64787,64787],"mapped","عى"],[[64788,64788],"mapped","عي"],[[64789,64789],"mapped","غى"],[[64790,64790],"mapped","غي"],[[64791,64791],"mapped","سى"],[[64792,64792],"mapped","سي"],[[64793,64793],"mapped","شى"],[[64794,64794],"mapped","شي"],[[64795,64795],"mapped","حى"],[[64796,64796],"mapped","حي"],[[64797,64797],"mapped","جى"],[[64798,64798],"mapped","جي"],[[64799,64799],"mapped","خى"],[[64800,64800],"mapped","خي"],[[64801,64801],"mapped","صى"],[[64802,64802],"mapped","صي"],[[64803,64803],"mapped","ضى"],[[64804,64804],"mapped","ضي"],[[64805,64805],"mapped","شج"],[[64806,64806],"mapped","شح"],[[64807,64807],"mapped","شخ"],[[64808,64808],"mapped","شم"],[[64809,64809],"mapped","شر"],[[64810,64810],"mapped","سر"],[[64811,64811],"mapped","صر"],[[64812,64812],"mapped","ضر"],[[64813,64813],"mapped","شج"],[[64814,64814],"mapped","شح"],[[64815,64815],"mapped","شخ"],[[64816,64816],"mapped","شم"],[[64817,64817],"mapped","سه"],[[64818,64818],"mapped","شه"],[[64819,64819],"mapped","طم"],[[64820,64820],"mapped","سج"],[[64821,64821],"mapped","سح"],[[64822,64822],"mapped","سخ"],[[64823,64823],"mapped","شج"],[[64824,64824],"mapped","شح"],[[64825,64825],"mapped","شخ"],[[64826,64826],"mapped","طم"],[[64827,64827],"mapped","ظم"],[[64828,64829],"mapped","اً"],[[64830,64831],"valid","","NV8"],[[64832,64847],"disallowed"],[[64848,64848],"mapped","تجم"],[[64849,64850],"mapped","تحج"],[[64851,64851],"mapped","تحم"],[[64852,64852],"mapped","تخم"],[[64853,64853],"mapped","تمج"],[[64854,64854],"mapped","تمح"],[[64855,64855],"mapped","تمخ"],[[64856,64857],"mapped","جمح"],[[64858,64858],"mapped","حمي"],[[64859,64859],"mapped","حمى"],[[64860,64860],"mapped","سحج"],[[64861,64861],"mapped","سجح"],[[64862,64862],"mapped","سجى"],[[64863,64864],"mapped","سمح"],[[64865,64865],"mapped","سمج"],[[64866,64867],"mapped","سمم"],[[64868,64869],"mapped","صحح"],[[64870,64870],"mapped","صمم"],[[64871,64872],"mapped","شحم"],[[64873,64873],"mapped","شجي"],[[64874,64875],"mapped","شمخ"],[[64876,64877],"mapped","شمم"],[[64878,64878],"mapped","ضحى"],[[64879,64880],"mapped","ضخم"],[[64881,64882],"mapped","طمح"],[[64883,64883],"mapped","طمم"],[[64884,64884],"mapped","طمي"],[[64885,64885],"mapped","عجم"],[[64886,64887],"mapped","عمم"],[[64888,64888],"mapped","عمى"],[[64889,64889],"mapped","غمم"],[[64890,64890],"mapped","غمي"],[[64891,64891],"mapped","غمى"],[[64892,64893],"mapped","فخم"],[[64894,64894],"mapped","قمح"],[[64895,64895],"mapped","قمم"],[[64896,64896],"mapped","لحم"],[[64897,64897],"mapped","لحي"],[[64898,64898],"mapped","لحى"],[[64899,64900],"mapped","لجج"],[[64901,64902],"mapped","لخم"],[[64903,64904],"mapped","لمح"],[[64905,64905],"mapped","محج"],[[64906,64906],"mapped","محم"],[[64907,64907],"mapped","محي"],[[64908,64908],"mapped","مجح"],[[64909,64909],"mapped","مجم"],[[64910,64910],"mapped","مخج"],[[64911,64911],"mapped","مخم"],[[64912,64913],"disallowed"],[[64914,64914],"mapped","مجخ"],[[64915,64915],"mapped","همج"],[[64916,64916],"mapped","همم"],[[64917,64917],"mapped","نحم"],[[64918,64918],"mapped","نحى"],[[64919,64920],"mapped","نجم"],[[64921,64921],"mapped","نجى"],[[64922,64922],"mapped","نمي"],[[64923,64923],"mapped","نمى"],[[64924,64925],"mapped","يمم"],[[64926,64926],"mapped","بخي"],[[64927,64927],"mapped","تجي"],[[64928,64928],"mapped","تجى"],[[64929,64929],"mapped","تخي"],[[64930,64930],"mapped","تخى"],[[64931,64931],"mapped","تمي"],[[64932,64932],"mapped","تمى"],[[64933,64933],"mapped","جمي"],[[64934,64934],"mapped","جحى"],[[64935,64935],"mapped","جمى"],[[64936,64936],"mapped","سخى"],[[64937,64937],"mapped","صحي"],[[64938,64938],"mapped","شحي"],[[64939,64939],"mapped","ضحي"],[[64940,64940],"mapped","لجي"],[[64941,64941],"mapped","لمي"],[[64942,64942],"mapped","يحي"],[[64943,64943],"mapped","يجي"],[[64944,64944],"mapped","يمي"],[[64945,64945],"mapped","ممي"],[[64946,64946],"mapped","قمي"],[[64947,64947],"mapped","نحي"],[[64948,64948],"mapped","قمح"],[[64949,64949],"mapped","لحم"],[[64950,64950],"mapped","عمي"],[[64951,64951],"mapped","كمي"],[[64952,64952],"mapped","نجح"],[[64953,64953],"mapped","مخي"],[[64954,64954],"mapped","لجم"],[[64955,64955],"mapped","كمم"],[[64956,64956],"mapped","لجم"],[[64957,64957],"mapped","نجح"],[[64958,64958],"mapped","جحي"],[[64959,64959],"mapped","حجي"],[[64960,64960],"mapped","مجي"],[[64961,64961],"mapped","فمي"],[[64962,64962],"mapped","بحي"],[[64963,64963],"mapped","كمم"],[[64964,64964],"mapped","عجم"],[[64965,64965],"mapped","صمم"],[[64966,64966],"mapped","سخي"],[[64967,64967],"mapped","نجي"],[[64968,64975],"disallowed"],[[64976,65007],"disallowed"],[[65008,65008],"mapped","صلے"],[[65009,65009],"mapped","قلے"],[[65010,65010],"mapped","الله"],[[65011,65011],"mapped","اكبر"],[[65012,65012],"mapped","محمد"],[[65013,65013],"mapped","صلعم"],[[65014,65014],"mapped","رسول"],[[65015,65015],"mapped","عليه"],[[65016,65016],"mapped","وسلم"],[[65017,65017],"mapped","صلى"],[[65018,65018],"disallowed_STD3_mapped","صلى الله عليه وسلم"],[[65019,65019],"disallowed_STD3_mapped","جل جلاله"],[[65020,65020],"mapped","ریال"],[[65021,65021],"valid","","NV8"],[[65022,65023],"disallowed"],[[65024,65039],"ignored"],[[65040,65040],"disallowed_STD3_mapped",","],[[65041,65041],"mapped","、"],[[65042,65042],"disallowed"],[[65043,65043],"disallowed_STD3_mapped",":"],[[65044,65044],"disallowed_STD3_mapped",";"],[[65045,65045],"disallowed_STD3_mapped","!"],[[65046,65046],"disallowed_STD3_mapped","?"],[[65047,65047],"mapped","〖"],[[65048,65048],"mapped","〗"],[[65049,65049],"disallowed"],[[65050,65055],"disallowed"],[[65056,65059],"valid"],[[65060,65062],"valid"],[[65063,65069],"valid"],[[65070,65071],"valid"],[[65072,65072],"disallowed"],[[65073,65073],"mapped","—"],[[65074,65074],"mapped","–"],[[65075,65076],"disallowed_STD3_mapped","_"],[[65077,65077],"disallowed_STD3_mapped","("],[[65078,65078],"disallowed_STD3_mapped",")"],[[65079,65079],"disallowed_STD3_mapped","{"],[[65080,65080],"disallowed_STD3_mapped","}"],[[65081,65081],"mapped","〔"],[[65082,65082],"mapped","〕"],[[65083,65083],"mapped","【"],[[65084,65084],"mapped","】"],[[65085,65085],"mapped","《"],[[65086,65086],"mapped","》"],[[65087,65087],"mapped","〈"],[[65088,65088],"mapped","〉"],[[65089,65089],"mapped","「"],[[65090,65090],"mapped","」"],[[65091,65091],"mapped","『"],[[65092,65092],"mapped","』"],[[65093,65094],"valid","","NV8"],[[65095,65095],"disallowed_STD3_mapped","["],[[65096,65096],"disallowed_STD3_mapped","]"],[[65097,65100],"disallowed_STD3_mapped"," ̅"],[[65101,65103],"disallowed_STD3_mapped","_"],[[65104,65104],"disallowed_STD3_mapped",","],[[65105,65105],"mapped","、"],[[65106,65106],"disallowed"],[[65107,65107],"disallowed"],[[65108,65108],"disallowed_STD3_mapped",";"],[[65109,65109],"disallowed_STD3_mapped",":"],[[65110,65110],"disallowed_STD3_mapped","?"],[[65111,65111],"disallowed_STD3_mapped","!"],[[65112,65112],"mapped","—"],[[65113,65113],"disallowed_STD3_mapped","("],[[65114,65114],"disallowed_STD3_mapped",")"],[[65115,65115],"disallowed_STD3_mapped","{"],[[65116,65116],"disallowed_STD3_mapped","}"],[[65117,65117],"mapped","〔"],[[65118,65118],"mapped","〕"],[[65119,65119],"disallowed_STD3_mapped","#"],[[65120,65120],"disallowed_STD3_mapped","&"],[[65121,65121],"disallowed_STD3_mapped","*"],[[65122,65122],"disallowed_STD3_mapped","+"],[[65123,65123],"mapped","-"],[[65124,65124],"disallowed_STD3_mapped","<"],[[65125,65125],"disallowed_STD3_mapped",">"],[[65126,65126],"disallowed_STD3_mapped","="],[[65127,65127],"disallowed"],[[65128,65128],"disallowed_STD3_mapped","\\"],[[65129,65129],"disallowed_STD3_mapped","$"],[[65130,65130],"disallowed_STD3_mapped","%"],[[65131,65131],"disallowed_STD3_mapped","@"],[[65132,65135],"disallowed"],[[65136,65136],"disallowed_STD3_mapped"," ً"],[[65137,65137],"mapped","ـً"],[[65138,65138],"disallowed_STD3_mapped"," ٌ"],[[65139,65139],"valid"],[[65140,65140],"disallowed_STD3_mapped"," ٍ"],[[65141,65141],"disallowed"],[[65142,65142],"disallowed_STD3_mapped"," َ"],[[65143,65143],"mapped","ـَ"],[[65144,65144],"disallowed_STD3_mapped"," ُ"],[[65145,65145],"mapped","ـُ"],[[65146,65146],"disallowed_STD3_mapped"," ِ"],[[65147,65147],"mapped","ـِ"],[[65148,65148],"disallowed_STD3_mapped"," ّ"],[[65149,65149],"mapped","ـّ"],[[65150,65150],"disallowed_STD3_mapped"," ْ"],[[65151,65151],"mapped","ـْ"],[[65152,65152],"mapped","ء"],[[65153,65154],"mapped","آ"],[[65155,65156],"mapped","أ"],[[65157,65158],"mapped","ؤ"],[[65159,65160],"mapped","إ"],[[65161,65164],"mapped","ئ"],[[65165,65166],"mapped","ا"],[[65167,65170],"mapped","ب"],[[65171,65172],"mapped","ة"],[[65173,65176],"mapped","ت"],[[65177,65180],"mapped","ث"],[[65181,65184],"mapped","ج"],[[65185,65188],"mapped","ح"],[[65189,65192],"mapped","خ"],[[65193,65194],"mapped","د"],[[65195,65196],"mapped","ذ"],[[65197,65198],"mapped","ر"],[[65199,65200],"mapped","ز"],[[65201,65204],"mapped","س"],[[65205,65208],"mapped","ش"],[[65209,65212],"mapped","ص"],[[65213,65216],"mapped","ض"],[[65217,65220],"mapped","ط"],[[65221,65224],"mapped","ظ"],[[65225,65228],"mapped","ع"],[[65229,65232],"mapped","غ"],[[65233,65236],"mapped","ف"],[[65237,65240],"mapped","ق"],[[65241,65244],"mapped","ك"],[[65245,65248],"mapped","ل"],[[65249,65252],"mapped","م"],[[65253,65256],"mapped","ن"],[[65257,65260],"mapped","ه"],[[65261,65262],"mapped","و"],[[65263,65264],"mapped","ى"],[[65265,65268],"mapped","ي"],[[65269,65270],"mapped","لآ"],[[65271,65272],"mapped","لأ"],[[65273,65274],"mapped","لإ"],[[65275,65276],"mapped","لا"],[[65277,65278],"disallowed"],[[65279,65279],"ignored"],[[65280,65280],"disallowed"],[[65281,65281],"disallowed_STD3_mapped","!"],[[65282,65282],"disallowed_STD3_mapped","\""],[[65283,65283],"disallowed_STD3_mapped","#"],[[65284,65284],"disallowed_STD3_mapped","$"],[[65285,65285],"disallowed_STD3_mapped","%"],[[65286,65286],"disallowed_STD3_mapped","&"],[[65287,65287],"disallowed_STD3_mapped","'"],[[65288,65288],"disallowed_STD3_mapped","("],[[65289,65289],"disallowed_STD3_mapped",")"],[[65290,65290],"disallowed_STD3_mapped","*"],[[65291,65291],"disallowed_STD3_mapped","+"],[[65292,65292],"disallowed_STD3_mapped",","],[[65293,65293],"mapped","-"],[[65294,65294],"mapped","."],[[65295,65295],"disallowed_STD3_mapped","/"],[[65296,65296],"mapped","0"],[[65297,65297],"mapped","1"],[[65298,65298],"mapped","2"],[[65299,65299],"mapped","3"],[[65300,65300],"mapped","4"],[[65301,65301],"mapped","5"],[[65302,65302],"mapped","6"],[[65303,65303],"mapped","7"],[[65304,65304],"mapped","8"],[[65305,65305],"mapped","9"],[[65306,65306],"disallowed_STD3_mapped",":"],[[65307,65307],"disallowed_STD3_mapped",";"],[[65308,65308],"disallowed_STD3_mapped","<"],[[65309,65309],"disallowed_STD3_mapped","="],[[65310,65310],"disallowed_STD3_mapped",">"],[[65311,65311],"disallowed_STD3_mapped","?"],[[65312,65312],"disallowed_STD3_mapped","@"],[[65313,65313],"mapped","a"],[[65314,65314],"mapped","b"],[[65315,65315],"mapped","c"],[[65316,65316],"mapped","d"],[[65317,65317],"mapped","e"],[[65318,65318],"mapped","f"],[[65319,65319],"mapped","g"],[[65320,65320],"mapped","h"],[[65321,65321],"mapped","i"],[[65322,65322],"mapped","j"],[[65323,65323],"mapped","k"],[[65324,65324],"mapped","l"],[[65325,65325],"mapped","m"],[[65326,65326],"mapped","n"],[[65327,65327],"mapped","o"],[[65328,65328],"mapped","p"],[[65329,65329],"mapped","q"],[[65330,65330],"mapped","r"],[[65331,65331],"mapped","s"],[[65332,65332],"mapped","t"],[[65333,65333],"mapped","u"],[[65334,65334],"mapped","v"],[[65335,65335],"mapped","w"],[[65336,65336],"mapped","x"],[[65337,65337],"mapped","y"],[[65338,65338],"mapped","z"],[[65339,65339],"disallowed_STD3_mapped","["],[[65340,65340],"disallowed_STD3_mapped","\\"],[[65341,65341],"disallowed_STD3_mapped","]"],[[65342,65342],"disallowed_STD3_mapped","^"],[[65343,65343],"disallowed_STD3_mapped","_"],[[65344,65344],"disallowed_STD3_mapped","`"],[[65345,65345],"mapped","a"],[[65346,65346],"mapped","b"],[[65347,65347],"mapped","c"],[[65348,65348],"mapped","d"],[[65349,65349],"mapped","e"],[[65350,65350],"mapped","f"],[[65351,65351],"mapped","g"],[[65352,65352],"mapped","h"],[[65353,65353],"mapped","i"],[[65354,65354],"mapped","j"],[[65355,65355],"mapped","k"],[[65356,65356],"mapped","l"],[[65357,65357],"mapped","m"],[[65358,65358],"mapped","n"],[[65359,65359],"mapped","o"],[[65360,65360],"mapped","p"],[[65361,65361],"mapped","q"],[[65362,65362],"mapped","r"],[[65363,65363],"mapped","s"],[[65364,65364],"mapped","t"],[[65365,65365],"mapped","u"],[[65366,65366],"mapped","v"],[[65367,65367],"mapped","w"],[[65368,65368],"mapped","x"],[[65369,65369],"mapped","y"],[[65370,65370],"mapped","z"],[[65371,65371],"disallowed_STD3_mapped","{"],[[65372,65372],"disallowed_STD3_mapped","|"],[[65373,65373],"disallowed_STD3_mapped","}"],[[65374,65374],"disallowed_STD3_mapped","~"],[[65375,65375],"mapped","⦅"],[[65376,65376],"mapped","⦆"],[[65377,65377],"mapped","."],[[65378,65378],"mapped","「"],[[65379,65379],"mapped","」"],[[65380,65380],"mapped","、"],[[65381,65381],"mapped","・"],[[65382,65382],"mapped","ヲ"],[[65383,65383],"mapped","ァ"],[[65384,65384],"mapped","ィ"],[[65385,65385],"mapped","ゥ"],[[65386,65386],"mapped","ェ"],[[65387,65387],"mapped","ォ"],[[65388,65388],"mapped","ャ"],[[65389,65389],"mapped","ュ"],[[65390,65390],"mapped","ョ"],[[65391,65391],"mapped","ッ"],[[65392,65392],"mapped","ー"],[[65393,65393],"mapped","ア"],[[65394,65394],"mapped","イ"],[[65395,65395],"mapped","ウ"],[[65396,65396],"mapped","エ"],[[65397,65397],"mapped","オ"],[[65398,65398],"mapped","カ"],[[65399,65399],"mapped","キ"],[[65400,65400],"mapped","ク"],[[65401,65401],"mapped","ケ"],[[65402,65402],"mapped","コ"],[[65403,65403],"mapped","サ"],[[65404,65404],"mapped","シ"],[[65405,65405],"mapped","ス"],[[65406,65406],"mapped","セ"],[[65407,65407],"mapped","ソ"],[[65408,65408],"mapped","タ"],[[65409,65409],"mapped","チ"],[[65410,65410],"mapped","ツ"],[[65411,65411],"mapped","テ"],[[65412,65412],"mapped","ト"],[[65413,65413],"mapped","ナ"],[[65414,65414],"mapped","ニ"],[[65415,65415],"mapped","ヌ"],[[65416,65416],"mapped","ネ"],[[65417,65417],"mapped","ノ"],[[65418,65418],"mapped","ハ"],[[65419,65419],"mapped","ヒ"],[[65420,65420],"mapped","フ"],[[65421,65421],"mapped","ヘ"],[[65422,65422],"mapped","ホ"],[[65423,65423],"mapped","マ"],[[65424,65424],"mapped","ミ"],[[65425,65425],"mapped","ム"],[[65426,65426],"mapped","メ"],[[65427,65427],"mapped","モ"],[[65428,65428],"mapped","ヤ"],[[65429,65429],"mapped","ユ"],[[65430,65430],"mapped","ヨ"],[[65431,65431],"mapped","ラ"],[[65432,65432],"mapped","リ"],[[65433,65433],"mapped","ル"],[[65434,65434],"mapped","レ"],[[65435,65435],"mapped","ロ"],[[65436,65436],"mapped","ワ"],[[65437,65437],"mapped","ン"],[[65438,65438],"mapped","゙"],[[65439,65439],"mapped","゚"],[[65440,65440],"disallowed"],[[65441,65441],"mapped","ᄀ"],[[65442,65442],"mapped","ᄁ"],[[65443,65443],"mapped","ᆪ"],[[65444,65444],"mapped","ᄂ"],[[65445,65445],"mapped","ᆬ"],[[65446,65446],"mapped","ᆭ"],[[65447,65447],"mapped","ᄃ"],[[65448,65448],"mapped","ᄄ"],[[65449,65449],"mapped","ᄅ"],[[65450,65450],"mapped","ᆰ"],[[65451,65451],"mapped","ᆱ"],[[65452,65452],"mapped","ᆲ"],[[65453,65453],"mapped","ᆳ"],[[65454,65454],"mapped","ᆴ"],[[65455,65455],"mapped","ᆵ"],[[65456,65456],"mapped","ᄚ"],[[65457,65457],"mapped","ᄆ"],[[65458,65458],"mapped","ᄇ"],[[65459,65459],"mapped","ᄈ"],[[65460,65460],"mapped","ᄡ"],[[65461,65461],"mapped","ᄉ"],[[65462,65462],"mapped","ᄊ"],[[65463,65463],"mapped","ᄋ"],[[65464,65464],"mapped","ᄌ"],[[65465,65465],"mapped","ᄍ"],[[65466,65466],"mapped","ᄎ"],[[65467,65467],"mapped","ᄏ"],[[65468,65468],"mapped","ᄐ"],[[65469,65469],"mapped","ᄑ"],[[65470,65470],"mapped","ᄒ"],[[65471,65473],"disallowed"],[[65474,65474],"mapped","ᅡ"],[[65475,65475],"mapped","ᅢ"],[[65476,65476],"mapped","ᅣ"],[[65477,65477],"mapped","ᅤ"],[[65478,65478],"mapped","ᅥ"],[[65479,65479],"mapped","ᅦ"],[[65480,65481],"disallowed"],[[65482,65482],"mapped","ᅧ"],[[65483,65483],"mapped","ᅨ"],[[65484,65484],"mapped","ᅩ"],[[65485,65485],"mapped","ᅪ"],[[65486,65486],"mapped","ᅫ"],[[65487,65487],"mapped","ᅬ"],[[65488,65489],"disallowed"],[[65490,65490],"mapped","ᅭ"],[[65491,65491],"mapped","ᅮ"],[[65492,65492],"mapped","ᅯ"],[[65493,65493],"mapped","ᅰ"],[[65494,65494],"mapped","ᅱ"],[[65495,65495],"mapped","ᅲ"],[[65496,65497],"disallowed"],[[65498,65498],"mapped","ᅳ"],[[65499,65499],"mapped","ᅴ"],[[65500,65500],"mapped","ᅵ"],[[65501,65503],"disallowed"],[[65504,65504],"mapped","¢"],[[65505,65505],"mapped","£"],[[65506,65506],"mapped","¬"],[[65507,65507],"disallowed_STD3_mapped"," ̄"],[[65508,65508],"mapped","¦"],[[65509,65509],"mapped","¥"],[[65510,65510],"mapped","₩"],[[65511,65511],"disallowed"],[[65512,65512],"mapped","│"],[[65513,65513],"mapped","←"],[[65514,65514],"mapped","↑"],[[65515,65515],"mapped","→"],[[65516,65516],"mapped","↓"],[[65517,65517],"mapped","■"],[[65518,65518],"mapped","○"],[[65519,65528],"disallowed"],[[65529,65531],"disallowed"],[[65532,65532],"disallowed"],[[65533,65533],"disallowed"],[[65534,65535],"disallowed"],[[65536,65547],"valid"],[[65548,65548],"disallowed"],[[65549,65574],"valid"],[[65575,65575],"disallowed"],[[65576,65594],"valid"],[[65595,65595],"disallowed"],[[65596,65597],"valid"],[[65598,65598],"disallowed"],[[65599,65613],"valid"],[[65614,65615],"disallowed"],[[65616,65629],"valid"],[[65630,65663],"disallowed"],[[65664,65786],"valid"],[[65787,65791],"disallowed"],[[65792,65794],"valid","","NV8"],[[65795,65798],"disallowed"],[[65799,65843],"valid","","NV8"],[[65844,65846],"disallowed"],[[65847,65855],"valid","","NV8"],[[65856,65930],"valid","","NV8"],[[65931,65932],"valid","","NV8"],[[65933,65934],"valid","","NV8"],[[65935,65935],"disallowed"],[[65936,65947],"valid","","NV8"],[[65948,65951],"disallowed"],[[65952,65952],"valid","","NV8"],[[65953,65999],"disallowed"],[[66000,66044],"valid","","NV8"],[[66045,66045],"valid"],[[66046,66175],"disallowed"],[[66176,66204],"valid"],[[66205,66207],"disallowed"],[[66208,66256],"valid"],[[66257,66271],"disallowed"],[[66272,66272],"valid"],[[66273,66299],"valid","","NV8"],[[66300,66303],"disallowed"],[[66304,66334],"valid"],[[66335,66335],"valid"],[[66336,66339],"valid","","NV8"],[[66340,66348],"disallowed"],[[66349,66351],"valid"],[[66352,66368],"valid"],[[66369,66369],"valid","","NV8"],[[66370,66377],"valid"],[[66378,66378],"valid","","NV8"],[[66379,66383],"disallowed"],[[66384,66426],"valid"],[[66427,66431],"disallowed"],[[66432,66461],"valid"],[[66462,66462],"disallowed"],[[66463,66463],"valid","","NV8"],[[66464,66499],"valid"],[[66500,66503],"disallowed"],[[66504,66511],"valid"],[[66512,66517],"valid","","NV8"],[[66518,66559],"disallowed"],[[66560,66560],"mapped","𐐨"],[[66561,66561],"mapped","𐐩"],[[66562,66562],"mapped","𐐪"],[[66563,66563],"mapped","𐐫"],[[66564,66564],"mapped","𐐬"],[[66565,66565],"mapped","𐐭"],[[66566,66566],"mapped","𐐮"],[[66567,66567],"mapped","𐐯"],[[66568,66568],"mapped","𐐰"],[[66569,66569],"mapped","𐐱"],[[66570,66570],"mapped","𐐲"],[[66571,66571],"mapped","𐐳"],[[66572,66572],"mapped","𐐴"],[[66573,66573],"mapped","𐐵"],[[66574,66574],"mapped","𐐶"],[[66575,66575],"mapped","𐐷"],[[66576,66576],"mapped","𐐸"],[[66577,66577],"mapped","𐐹"],[[66578,66578],"mapped","𐐺"],[[66579,66579],"mapped","𐐻"],[[66580,66580],"mapped","𐐼"],[[66581,66581],"mapped","𐐽"],[[66582,66582],"mapped","𐐾"],[[66583,66583],"mapped","𐐿"],[[66584,66584],"mapped","𐑀"],[[66585,66585],"mapped","𐑁"],[[66586,66586],"mapped","𐑂"],[[66587,66587],"mapped","𐑃"],[[66588,66588],"mapped","𐑄"],[[66589,66589],"mapped","𐑅"],[[66590,66590],"mapped","𐑆"],[[66591,66591],"mapped","𐑇"],[[66592,66592],"mapped","𐑈"],[[66593,66593],"mapped","𐑉"],[[66594,66594],"mapped","𐑊"],[[66595,66595],"mapped","𐑋"],[[66596,66596],"mapped","𐑌"],[[66597,66597],"mapped","𐑍"],[[66598,66598],"mapped","𐑎"],[[66599,66599],"mapped","𐑏"],[[66600,66637],"valid"],[[66638,66717],"valid"],[[66718,66719],"disallowed"],[[66720,66729],"valid"],[[66730,66735],"disallowed"],[[66736,66736],"mapped","𐓘"],[[66737,66737],"mapped","𐓙"],[[66738,66738],"mapped","𐓚"],[[66739,66739],"mapped","𐓛"],[[66740,66740],"mapped","𐓜"],[[66741,66741],"mapped","𐓝"],[[66742,66742],"mapped","𐓞"],[[66743,66743],"mapped","𐓟"],[[66744,66744],"mapped","𐓠"],[[66745,66745],"mapped","𐓡"],[[66746,66746],"mapped","𐓢"],[[66747,66747],"mapped","𐓣"],[[66748,66748],"mapped","𐓤"],[[66749,66749],"mapped","𐓥"],[[66750,66750],"mapped","𐓦"],[[66751,66751],"mapped","𐓧"],[[66752,66752],"mapped","𐓨"],[[66753,66753],"mapped","𐓩"],[[66754,66754],"mapped","𐓪"],[[66755,66755],"mapped","𐓫"],[[66756,66756],"mapped","𐓬"],[[66757,66757],"mapped","𐓭"],[[66758,66758],"mapped","𐓮"],[[66759,66759],"mapped","𐓯"],[[66760,66760],"mapped","𐓰"],[[66761,66761],"mapped","𐓱"],[[66762,66762],"mapped","𐓲"],[[66763,66763],"mapped","𐓳"],[[66764,66764],"mapped","𐓴"],[[66765,66765],"mapped","𐓵"],[[66766,66766],"mapped","𐓶"],[[66767,66767],"mapped","𐓷"],[[66768,66768],"mapped","𐓸"],[[66769,66769],"mapped","𐓹"],[[66770,66770],"mapped","𐓺"],[[66771,66771],"mapped","𐓻"],[[66772,66775],"disallowed"],[[66776,66811],"valid"],[[66812,66815],"disallowed"],[[66816,66855],"valid"],[[66856,66863],"disallowed"],[[66864,66915],"valid"],[[66916,66926],"disallowed"],[[66927,66927],"valid","","NV8"],[[66928,67071],"disallowed"],[[67072,67382],"valid"],[[67383,67391],"disallowed"],[[67392,67413],"valid"],[[67414,67423],"disallowed"],[[67424,67431],"valid"],[[67432,67583],"disallowed"],[[67584,67589],"valid"],[[67590,67591],"disallowed"],[[67592,67592],"valid"],[[67593,67593],"disallowed"],[[67594,67637],"valid"],[[67638,67638],"disallowed"],[[67639,67640],"valid"],[[67641,67643],"disallowed"],[[67644,67644],"valid"],[[67645,67646],"disallowed"],[[67647,67647],"valid"],[[67648,67669],"valid"],[[67670,67670],"disallowed"],[[67671,67679],"valid","","NV8"],[[67680,67702],"valid"],[[67703,67711],"valid","","NV8"],[[67712,67742],"valid"],[[67743,67750],"disallowed"],[[67751,67759],"valid","","NV8"],[[67760,67807],"disallowed"],[[67808,67826],"valid"],[[67827,67827],"disallowed"],[[67828,67829],"valid"],[[67830,67834],"disallowed"],[[67835,67839],"valid","","NV8"],[[67840,67861],"valid"],[[67862,67865],"valid","","NV8"],[[67866,67867],"valid","","NV8"],[[67868,67870],"disallowed"],[[67871,67871],"valid","","NV8"],[[67872,67897],"valid"],[[67898,67902],"disallowed"],[[67903,67903],"valid","","NV8"],[[67904,67967],"disallowed"],[[67968,68023],"valid"],[[68024,68027],"disallowed"],[[68028,68029],"valid","","NV8"],[[68030,68031],"valid"],[[68032,68047],"valid","","NV8"],[[68048,68049],"disallowed"],[[68050,68095],"valid","","NV8"],[[68096,68099],"valid"],[[68100,68100],"disallowed"],[[68101,68102],"valid"],[[68103,68107],"disallowed"],[[68108,68115],"valid"],[[68116,68116],"disallowed"],[[68117,68119],"valid"],[[68120,68120],"disallowed"],[[68121,68147],"valid"],[[68148,68151],"disallowed"],[[68152,68154],"valid"],[[68155,68158],"disallowed"],[[68159,68159],"valid"],[[68160,68167],"valid","","NV8"],[[68168,68175],"disallowed"],[[68176,68184],"valid","","NV8"],[[68185,68191],"disallowed"],[[68192,68220],"valid"],[[68221,68223],"valid","","NV8"],[[68224,68252],"valid"],[[68253,68255],"valid","","NV8"],[[68256,68287],"disallowed"],[[68288,68295],"valid"],[[68296,68296],"valid","","NV8"],[[68297,68326],"valid"],[[68327,68330],"disallowed"],[[68331,68342],"valid","","NV8"],[[68343,68351],"disallowed"],[[68352,68405],"valid"],[[68406,68408],"disallowed"],[[68409,68415],"valid","","NV8"],[[68416,68437],"valid"],[[68438,68439],"disallowed"],[[68440,68447],"valid","","NV8"],[[68448,68466],"valid"],[[68467,68471],"disallowed"],[[68472,68479],"valid","","NV8"],[[68480,68497],"valid"],[[68498,68504],"disallowed"],[[68505,68508],"valid","","NV8"],[[68509,68520],"disallowed"],[[68521,68527],"valid","","NV8"],[[68528,68607],"disallowed"],[[68608,68680],"valid"],[[68681,68735],"disallowed"],[[68736,68736],"mapped","𐳀"],[[68737,68737],"mapped","𐳁"],[[68738,68738],"mapped","𐳂"],[[68739,68739],"mapped","𐳃"],[[68740,68740],"mapped","𐳄"],[[68741,68741],"mapped","𐳅"],[[68742,68742],"mapped","𐳆"],[[68743,68743],"mapped","𐳇"],[[68744,68744],"mapped","𐳈"],[[68745,68745],"mapped","𐳉"],[[68746,68746],"mapped","𐳊"],[[68747,68747],"mapped","𐳋"],[[68748,68748],"mapped","𐳌"],[[68749,68749],"mapped","𐳍"],[[68750,68750],"mapped","𐳎"],[[68751,68751],"mapped","𐳏"],[[68752,68752],"mapped","𐳐"],[[68753,68753],"mapped","𐳑"],[[68754,68754],"mapped","𐳒"],[[68755,68755],"mapped","𐳓"],[[68756,68756],"mapped","𐳔"],[[68757,68757],"mapped","𐳕"],[[68758,68758],"mapped","𐳖"],[[68759,68759],"mapped","𐳗"],[[68760,68760],"mapped","𐳘"],[[68761,68761],"mapped","𐳙"],[[68762,68762],"mapped","𐳚"],[[68763,68763],"mapped","𐳛"],[[68764,68764],"mapped","𐳜"],[[68765,68765],"mapped","𐳝"],[[68766,68766],"mapped","𐳞"],[[68767,68767],"mapped","𐳟"],[[68768,68768],"mapped","𐳠"],[[68769,68769],"mapped","𐳡"],[[68770,68770],"mapped","𐳢"],[[68771,68771],"mapped","𐳣"],[[68772,68772],"mapped","𐳤"],[[68773,68773],"mapped","𐳥"],[[68774,68774],"mapped","𐳦"],[[68775,68775],"mapped","𐳧"],[[68776,68776],"mapped","𐳨"],[[68777,68777],"mapped","𐳩"],[[68778,68778],"mapped","𐳪"],[[68779,68779],"mapped","𐳫"],[[68780,68780],"mapped","𐳬"],[[68781,68781],"mapped","𐳭"],[[68782,68782],"mapped","𐳮"],[[68783,68783],"mapped","𐳯"],[[68784,68784],"mapped","𐳰"],[[68785,68785],"mapped","𐳱"],[[68786,68786],"mapped","𐳲"],[[68787,68799],"disallowed"],[[68800,68850],"valid"],[[68851,68857],"disallowed"],[[68858,68863],"valid","","NV8"],[[68864,69215],"disallowed"],[[69216,69246],"valid","","NV8"],[[69247,69631],"disallowed"],[[69632,69702],"valid"],[[69703,69709],"valid","","NV8"],[[69710,69713],"disallowed"],[[69714,69733],"valid","","NV8"],[[69734,69743],"valid"],[[69744,69758],"disallowed"],[[69759,69759],"valid"],[[69760,69818],"valid"],[[69819,69820],"valid","","NV8"],[[69821,69821],"disallowed"],[[69822,69825],"valid","","NV8"],[[69826,69839],"disallowed"],[[69840,69864],"valid"],[[69865,69871],"disallowed"],[[69872,69881],"valid"],[[69882,69887],"disallowed"],[[69888,69940],"valid"],[[69941,69941],"disallowed"],[[69942,69951],"valid"],[[69952,69955],"valid","","NV8"],[[69956,69967],"disallowed"],[[69968,70003],"valid"],[[70004,70005],"valid","","NV8"],[[70006,70006],"valid"],[[70007,70015],"disallowed"],[[70016,70084],"valid"],[[70085,70088],"valid","","NV8"],[[70089,70089],"valid","","NV8"],[[70090,70092],"valid"],[[70093,70093],"valid","","NV8"],[[70094,70095],"disallowed"],[[70096,70105],"valid"],[[70106,70106],"valid"],[[70107,70107],"valid","","NV8"],[[70108,70108],"valid"],[[70109,70111],"valid","","NV8"],[[70112,70112],"disallowed"],[[70113,70132],"valid","","NV8"],[[70133,70143],"disallowed"],[[70144,70161],"valid"],[[70162,70162],"disallowed"],[[70163,70199],"valid"],[[70200,70205],"valid","","NV8"],[[70206,70206],"valid"],[[70207,70271],"disallowed"],[[70272,70278],"valid"],[[70279,70279],"disallowed"],[[70280,70280],"valid"],[[70281,70281],"disallowed"],[[70282,70285],"valid"],[[70286,70286],"disallowed"],[[70287,70301],"valid"],[[70302,70302],"disallowed"],[[70303,70312],"valid"],[[70313,70313],"valid","","NV8"],[[70314,70319],"disallowed"],[[70320,70378],"valid"],[[70379,70383],"disallowed"],[[70384,70393],"valid"],[[70394,70399],"disallowed"],[[70400,70400],"valid"],[[70401,70403],"valid"],[[70404,70404],"disallowed"],[[70405,70412],"valid"],[[70413,70414],"disallowed"],[[70415,70416],"valid"],[[70417,70418],"disallowed"],[[70419,70440],"valid"],[[70441,70441],"disallowed"],[[70442,70448],"valid"],[[70449,70449],"disallowed"],[[70450,70451],"valid"],[[70452,70452],"disallowed"],[[70453,70457],"valid"],[[70458,70459],"disallowed"],[[70460,70468],"valid"],[[70469,70470],"disallowed"],[[70471,70472],"valid"],[[70473,70474],"disallowed"],[[70475,70477],"valid"],[[70478,70479],"disallowed"],[[70480,70480],"valid"],[[70481,70486],"disallowed"],[[70487,70487],"valid"],[[70488,70492],"disallowed"],[[70493,70499],"valid"],[[70500,70501],"disallowed"],[[70502,70508],"valid"],[[70509,70511],"disallowed"],[[70512,70516],"valid"],[[70517,70655],"disallowed"],[[70656,70730],"valid"],[[70731,70735],"valid","","NV8"],[[70736,70745],"valid"],[[70746,70746],"disallowed"],[[70747,70747],"valid","","NV8"],[[70748,70748],"disallowed"],[[70749,70749],"valid","","NV8"],[[70750,70783],"disallowed"],[[70784,70853],"valid"],[[70854,70854],"valid","","NV8"],[[70855,70855],"valid"],[[70856,70863],"disallowed"],[[70864,70873],"valid"],[[70874,71039],"disallowed"],[[71040,71093],"valid"],[[71094,71095],"disallowed"],[[71096,71104],"valid"],[[71105,71113],"valid","","NV8"],[[71114,71127],"valid","","NV8"],[[71128,71133],"valid"],[[71134,71167],"disallowed"],[[71168,71232],"valid"],[[71233,71235],"valid","","NV8"],[[71236,71236],"valid"],[[71237,71247],"disallowed"],[[71248,71257],"valid"],[[71258,71263],"disallowed"],[[71264,71276],"valid","","NV8"],[[71277,71295],"disallowed"],[[71296,71351],"valid"],[[71352,71359],"disallowed"],[[71360,71369],"valid"],[[71370,71423],"disallowed"],[[71424,71449],"valid"],[[71450,71452],"disallowed"],[[71453,71467],"valid"],[[71468,71471],"disallowed"],[[71472,71481],"valid"],[[71482,71487],"valid","","NV8"],[[71488,71839],"disallowed"],[[71840,71840],"mapped","𑣀"],[[71841,71841],"mapped","𑣁"],[[71842,71842],"mapped","𑣂"],[[71843,71843],"mapped","𑣃"],[[71844,71844],"mapped","𑣄"],[[71845,71845],"mapped","𑣅"],[[71846,71846],"mapped","𑣆"],[[71847,71847],"mapped","𑣇"],[[71848,71848],"mapped","𑣈"],[[71849,71849],"mapped","𑣉"],[[71850,71850],"mapped","𑣊"],[[71851,71851],"mapped","𑣋"],[[71852,71852],"mapped","𑣌"],[[71853,71853],"mapped","𑣍"],[[71854,71854],"mapped","𑣎"],[[71855,71855],"mapped","𑣏"],[[71856,71856],"mapped","𑣐"],[[71857,71857],"mapped","𑣑"],[[71858,71858],"mapped","𑣒"],[[71859,71859],"mapped","𑣓"],[[71860,71860],"mapped","𑣔"],[[71861,71861],"mapped","𑣕"],[[71862,71862],"mapped","𑣖"],[[71863,71863],"mapped","𑣗"],[[71864,71864],"mapped","𑣘"],[[71865,71865],"mapped","𑣙"],[[71866,71866],"mapped","𑣚"],[[71867,71867],"mapped","𑣛"],[[71868,71868],"mapped","𑣜"],[[71869,71869],"mapped","𑣝"],[[71870,71870],"mapped","𑣞"],[[71871,71871],"mapped","𑣟"],[[71872,71913],"valid"],[[71914,71922],"valid","","NV8"],[[71923,71934],"disallowed"],[[71935,71935],"valid"],[[71936,72191],"disallowed"],[[72192,72254],"valid"],[[72255,72262],"valid","","NV8"],[[72263,72263],"valid"],[[72264,72271],"disallowed"],[[72272,72323],"valid"],[[72324,72325],"disallowed"],[[72326,72345],"valid"],[[72346,72348],"valid","","NV8"],[[72349,72349],"disallowed"],[[72350,72354],"valid","","NV8"],[[72355,72383],"disallowed"],[[72384,72440],"valid"],[[72441,72703],"disallowed"],[[72704,72712],"valid"],[[72713,72713],"disallowed"],[[72714,72758],"valid"],[[72759,72759],"disallowed"],[[72760,72768],"valid"],[[72769,72773],"valid","","NV8"],[[72774,72783],"disallowed"],[[72784,72793],"valid"],[[72794,72812],"valid","","NV8"],[[72813,72815],"disallowed"],[[72816,72817],"valid","","NV8"],[[72818,72847],"valid"],[[72848,72849],"disallowed"],[[72850,72871],"valid"],[[72872,72872],"disallowed"],[[72873,72886],"valid"],[[72887,72959],"disallowed"],[[72960,72966],"valid"],[[72967,72967],"disallowed"],[[72968,72969],"valid"],[[72970,72970],"disallowed"],[[72971,73014],"valid"],[[73015,73017],"disallowed"],[[73018,73018],"valid"],[[73019,73019],"disallowed"],[[73020,73021],"valid"],[[73022,73022],"disallowed"],[[73023,73031],"valid"],[[73032,73039],"disallowed"],[[73040,73049],"valid"],[[73050,73727],"disallowed"],[[73728,74606],"valid"],[[74607,74648],"valid"],[[74649,74649],"valid"],[[74650,74751],"disallowed"],[[74752,74850],"valid","","NV8"],[[74851,74862],"valid","","NV8"],[[74863,74863],"disallowed"],[[74864,74867],"valid","","NV8"],[[74868,74868],"valid","","NV8"],[[74869,74879],"disallowed"],[[74880,75075],"valid"],[[75076,77823],"disallowed"],[[77824,78894],"valid"],[[78895,82943],"disallowed"],[[82944,83526],"valid"],[[83527,92159],"disallowed"],[[92160,92728],"valid"],[[92729,92735],"disallowed"],[[92736,92766],"valid"],[[92767,92767],"disallowed"],[[92768,92777],"valid"],[[92778,92781],"disallowed"],[[92782,92783],"valid","","NV8"],[[92784,92879],"disallowed"],[[92880,92909],"valid"],[[92910,92911],"disallowed"],[[92912,92916],"valid"],[[92917,92917],"valid","","NV8"],[[92918,92927],"disallowed"],[[92928,92982],"valid"],[[92983,92991],"valid","","NV8"],[[92992,92995],"valid"],[[92996,92997],"valid","","NV8"],[[92998,93007],"disallowed"],[[93008,93017],"valid"],[[93018,93018],"disallowed"],[[93019,93025],"valid","","NV8"],[[93026,93026],"disallowed"],[[93027,93047],"valid"],[[93048,93052],"disallowed"],[[93053,93071],"valid"],[[93072,93951],"disallowed"],[[93952,94020],"valid"],[[94021,94031],"disallowed"],[[94032,94078],"valid"],[[94079,94094],"disallowed"],[[94095,94111],"valid"],[[94112,94175],"disallowed"],[[94176,94176],"valid"],[[94177,94177],"valid"],[[94178,94207],"disallowed"],[[94208,100332],"valid"],[[100333,100351],"disallowed"],[[100352,101106],"valid"],[[101107,110591],"disallowed"],[[110592,110593],"valid"],[[110594,110878],"valid"],[[110879,110959],"disallowed"],[[110960,111355],"valid"],[[111356,113663],"disallowed"],[[113664,113770],"valid"],[[113771,113775],"disallowed"],[[113776,113788],"valid"],[[113789,113791],"disallowed"],[[113792,113800],"valid"],[[113801,113807],"disallowed"],[[113808,113817],"valid"],[[113818,113819],"disallowed"],[[113820,113820],"valid","","NV8"],[[113821,113822],"valid"],[[113823,113823],"valid","","NV8"],[[113824,113827],"ignored"],[[113828,118783],"disallowed"],[[118784,119029],"valid","","NV8"],[[119030,119039],"disallowed"],[[119040,119078],"valid","","NV8"],[[119079,119080],"disallowed"],[[119081,119081],"valid","","NV8"],[[119082,119133],"valid","","NV8"],[[119134,119134],"mapped","𝅗𝅥"],[[119135,119135],"mapped","𝅘𝅥"],[[119136,119136],"mapped","𝅘𝅥𝅮"],[[119137,119137],"mapped","𝅘𝅥𝅯"],[[119138,119138],"mapped","𝅘𝅥𝅰"],[[119139,119139],"mapped","𝅘𝅥𝅱"],[[119140,119140],"mapped","𝅘𝅥𝅲"],[[119141,119154],"valid","","NV8"],[[119155,119162],"disallowed"],[[119163,119226],"valid","","NV8"],[[119227,119227],"mapped","𝆹𝅥"],[[119228,119228],"mapped","𝆺𝅥"],[[119229,119229],"mapped","𝆹𝅥𝅮"],[[119230,119230],"mapped","𝆺𝅥𝅮"],[[119231,119231],"mapped","𝆹𝅥𝅯"],[[119232,119232],"mapped","𝆺𝅥𝅯"],[[119233,119261],"valid","","NV8"],[[119262,119272],"valid","","NV8"],[[119273,119295],"disallowed"],[[119296,119365],"valid","","NV8"],[[119366,119551],"disallowed"],[[119552,119638],"valid","","NV8"],[[119639,119647],"disallowed"],[[119648,119665],"valid","","NV8"],[[119666,119807],"disallowed"],[[119808,119808],"mapped","a"],[[119809,119809],"mapped","b"],[[119810,119810],"mapped","c"],[[119811,119811],"mapped","d"],[[119812,119812],"mapped","e"],[[119813,119813],"mapped","f"],[[119814,119814],"mapped","g"],[[119815,119815],"mapped","h"],[[119816,119816],"mapped","i"],[[119817,119817],"mapped","j"],[[119818,119818],"mapped","k"],[[119819,119819],"mapped","l"],[[119820,119820],"mapped","m"],[[119821,119821],"mapped","n"],[[119822,119822],"mapped","o"],[[119823,119823],"mapped","p"],[[119824,119824],"mapped","q"],[[119825,119825],"mapped","r"],[[119826,119826],"mapped","s"],[[119827,119827],"mapped","t"],[[119828,119828],"mapped","u"],[[119829,119829],"mapped","v"],[[119830,119830],"mapped","w"],[[119831,119831],"mapped","x"],[[119832,119832],"mapped","y"],[[119833,119833],"mapped","z"],[[119834,119834],"mapped","a"],[[119835,119835],"mapped","b"],[[119836,119836],"mapped","c"],[[119837,119837],"mapped","d"],[[119838,119838],"mapped","e"],[[119839,119839],"mapped","f"],[[119840,119840],"mapped","g"],[[119841,119841],"mapped","h"],[[119842,119842],"mapped","i"],[[119843,119843],"mapped","j"],[[119844,119844],"mapped","k"],[[119845,119845],"mapped","l"],[[119846,119846],"mapped","m"],[[119847,119847],"mapped","n"],[[119848,119848],"mapped","o"],[[119849,119849],"mapped","p"],[[119850,119850],"mapped","q"],[[119851,119851],"mapped","r"],[[119852,119852],"mapped","s"],[[119853,119853],"mapped","t"],[[119854,119854],"mapped","u"],[[119855,119855],"mapped","v"],[[119856,119856],"mapped","w"],[[119857,119857],"mapped","x"],[[119858,119858],"mapped","y"],[[119859,119859],"mapped","z"],[[119860,119860],"mapped","a"],[[119861,119861],"mapped","b"],[[119862,119862],"mapped","c"],[[119863,119863],"mapped","d"],[[119864,119864],"mapped","e"],[[119865,119865],"mapped","f"],[[119866,119866],"mapped","g"],[[119867,119867],"mapped","h"],[[119868,119868],"mapped","i"],[[119869,119869],"mapped","j"],[[119870,119870],"mapped","k"],[[119871,119871],"mapped","l"],[[119872,119872],"mapped","m"],[[119873,119873],"mapped","n"],[[119874,119874],"mapped","o"],[[119875,119875],"mapped","p"],[[119876,119876],"mapped","q"],[[119877,119877],"mapped","r"],[[119878,119878],"mapped","s"],[[119879,119879],"mapped","t"],[[119880,119880],"mapped","u"],[[119881,119881],"mapped","v"],[[119882,119882],"mapped","w"],[[119883,119883],"mapped","x"],[[119884,119884],"mapped","y"],[[119885,119885],"mapped","z"],[[119886,119886],"mapped","a"],[[119887,119887],"mapped","b"],[[119888,119888],"mapped","c"],[[119889,119889],"mapped","d"],[[119890,119890],"mapped","e"],[[119891,119891],"mapped","f"],[[119892,119892],"mapped","g"],[[119893,119893],"disallowed"],[[119894,119894],"mapped","i"],[[119895,119895],"mapped","j"],[[119896,119896],"mapped","k"],[[119897,119897],"mapped","l"],[[119898,119898],"mapped","m"],[[119899,119899],"mapped","n"],[[119900,119900],"mapped","o"],[[119901,119901],"mapped","p"],[[119902,119902],"mapped","q"],[[119903,119903],"mapped","r"],[[119904,119904],"mapped","s"],[[119905,119905],"mapped","t"],[[119906,119906],"mapped","u"],[[119907,119907],"mapped","v"],[[119908,119908],"mapped","w"],[[119909,119909],"mapped","x"],[[119910,119910],"mapped","y"],[[119911,119911],"mapped","z"],[[119912,119912],"mapped","a"],[[119913,119913],"mapped","b"],[[119914,119914],"mapped","c"],[[119915,119915],"mapped","d"],[[119916,119916],"mapped","e"],[[119917,119917],"mapped","f"],[[119918,119918],"mapped","g"],[[119919,119919],"mapped","h"],[[119920,119920],"mapped","i"],[[119921,119921],"mapped","j"],[[119922,119922],"mapped","k"],[[119923,119923],"mapped","l"],[[119924,119924],"mapped","m"],[[119925,119925],"mapped","n"],[[119926,119926],"mapped","o"],[[119927,119927],"mapped","p"],[[119928,119928],"mapped","q"],[[119929,119929],"mapped","r"],[[119930,119930],"mapped","s"],[[119931,119931],"mapped","t"],[[119932,119932],"mapped","u"],[[119933,119933],"mapped","v"],[[119934,119934],"mapped","w"],[[119935,119935],"mapped","x"],[[119936,119936],"mapped","y"],[[119937,119937],"mapped","z"],[[119938,119938],"mapped","a"],[[119939,119939],"mapped","b"],[[119940,119940],"mapped","c"],[[119941,119941],"mapped","d"],[[119942,119942],"mapped","e"],[[119943,119943],"mapped","f"],[[119944,119944],"mapped","g"],[[119945,119945],"mapped","h"],[[119946,119946],"mapped","i"],[[119947,119947],"mapped","j"],[[119948,119948],"mapped","k"],[[119949,119949],"mapped","l"],[[119950,119950],"mapped","m"],[[119951,119951],"mapped","n"],[[119952,119952],"mapped","o"],[[119953,119953],"mapped","p"],[[119954,119954],"mapped","q"],[[119955,119955],"mapped","r"],[[119956,119956],"mapped","s"],[[119957,119957],"mapped","t"],[[119958,119958],"mapped","u"],[[119959,119959],"mapped","v"],[[119960,119960],"mapped","w"],[[119961,119961],"mapped","x"],[[119962,119962],"mapped","y"],[[119963,119963],"mapped","z"],[[119964,119964],"mapped","a"],[[119965,119965],"disallowed"],[[119966,119966],"mapped","c"],[[119967,119967],"mapped","d"],[[119968,119969],"disallowed"],[[119970,119970],"mapped","g"],[[119971,119972],"disallowed"],[[119973,119973],"mapped","j"],[[119974,119974],"mapped","k"],[[119975,119976],"disallowed"],[[119977,119977],"mapped","n"],[[119978,119978],"mapped","o"],[[119979,119979],"mapped","p"],[[119980,119980],"mapped","q"],[[119981,119981],"disallowed"],[[119982,119982],"mapped","s"],[[119983,119983],"mapped","t"],[[119984,119984],"mapped","u"],[[119985,119985],"mapped","v"],[[119986,119986],"mapped","w"],[[119987,119987],"mapped","x"],[[119988,119988],"mapped","y"],[[119989,119989],"mapped","z"],[[119990,119990],"mapped","a"],[[119991,119991],"mapped","b"],[[119992,119992],"mapped","c"],[[119993,119993],"mapped","d"],[[119994,119994],"disallowed"],[[119995,119995],"mapped","f"],[[119996,119996],"disallowed"],[[119997,119997],"mapped","h"],[[119998,119998],"mapped","i"],[[119999,119999],"mapped","j"],[[120000,120000],"mapped","k"],[[120001,120001],"mapped","l"],[[120002,120002],"mapped","m"],[[120003,120003],"mapped","n"],[[120004,120004],"disallowed"],[[120005,120005],"mapped","p"],[[120006,120006],"mapped","q"],[[120007,120007],"mapped","r"],[[120008,120008],"mapped","s"],[[120009,120009],"mapped","t"],[[120010,120010],"mapped","u"],[[120011,120011],"mapped","v"],[[120012,120012],"mapped","w"],[[120013,120013],"mapped","x"],[[120014,120014],"mapped","y"],[[120015,120015],"mapped","z"],[[120016,120016],"mapped","a"],[[120017,120017],"mapped","b"],[[120018,120018],"mapped","c"],[[120019,120019],"mapped","d"],[[120020,120020],"mapped","e"],[[120021,120021],"mapped","f"],[[120022,120022],"mapped","g"],[[120023,120023],"mapped","h"],[[120024,120024],"mapped","i"],[[120025,120025],"mapped","j"],[[120026,120026],"mapped","k"],[[120027,120027],"mapped","l"],[[120028,120028],"mapped","m"],[[120029,120029],"mapped","n"],[[120030,120030],"mapped","o"],[[120031,120031],"mapped","p"],[[120032,120032],"mapped","q"],[[120033,120033],"mapped","r"],[[120034,120034],"mapped","s"],[[120035,120035],"mapped","t"],[[120036,120036],"mapped","u"],[[120037,120037],"mapped","v"],[[120038,120038],"mapped","w"],[[120039,120039],"mapped","x"],[[120040,120040],"mapped","y"],[[120041,120041],"mapped","z"],[[120042,120042],"mapped","a"],[[120043,120043],"mapped","b"],[[120044,120044],"mapped","c"],[[120045,120045],"mapped","d"],[[120046,120046],"mapped","e"],[[120047,120047],"mapped","f"],[[120048,120048],"mapped","g"],[[120049,120049],"mapped","h"],[[120050,120050],"mapped","i"],[[120051,120051],"mapped","j"],[[120052,120052],"mapped","k"],[[120053,120053],"mapped","l"],[[120054,120054],"mapped","m"],[[120055,120055],"mapped","n"],[[120056,120056],"mapped","o"],[[120057,120057],"mapped","p"],[[120058,120058],"mapped","q"],[[120059,120059],"mapped","r"],[[120060,120060],"mapped","s"],[[120061,120061],"mapped","t"],[[120062,120062],"mapped","u"],[[120063,120063],"mapped","v"],[[120064,120064],"mapped","w"],[[120065,120065],"mapped","x"],[[120066,120066],"mapped","y"],[[120067,120067],"mapped","z"],[[120068,120068],"mapped","a"],[[120069,120069],"mapped","b"],[[120070,120070],"disallowed"],[[120071,120071],"mapped","d"],[[120072,120072],"mapped","e"],[[120073,120073],"mapped","f"],[[120074,120074],"mapped","g"],[[120075,120076],"disallowed"],[[120077,120077],"mapped","j"],[[120078,120078],"mapped","k"],[[120079,120079],"mapped","l"],[[120080,120080],"mapped","m"],[[120081,120081],"mapped","n"],[[120082,120082],"mapped","o"],[[120083,120083],"mapped","p"],[[120084,120084],"mapped","q"],[[120085,120085],"disallowed"],[[120086,120086],"mapped","s"],[[120087,120087],"mapped","t"],[[120088,120088],"mapped","u"],[[120089,120089],"mapped","v"],[[120090,120090],"mapped","w"],[[120091,120091],"mapped","x"],[[120092,120092],"mapped","y"],[[120093,120093],"disallowed"],[[120094,120094],"mapped","a"],[[120095,120095],"mapped","b"],[[120096,120096],"mapped","c"],[[120097,120097],"mapped","d"],[[120098,120098],"mapped","e"],[[120099,120099],"mapped","f"],[[120100,120100],"mapped","g"],[[120101,120101],"mapped","h"],[[120102,120102],"mapped","i"],[[120103,120103],"mapped","j"],[[120104,120104],"mapped","k"],[[120105,120105],"mapped","l"],[[120106,120106],"mapped","m"],[[120107,120107],"mapped","n"],[[120108,120108],"mapped","o"],[[120109,120109],"mapped","p"],[[120110,120110],"mapped","q"],[[120111,120111],"mapped","r"],[[120112,120112],"mapped","s"],[[120113,120113],"mapped","t"],[[120114,120114],"mapped","u"],[[120115,120115],"mapped","v"],[[120116,120116],"mapped","w"],[[120117,120117],"mapped","x"],[[120118,120118],"mapped","y"],[[120119,120119],"mapped","z"],[[120120,120120],"mapped","a"],[[120121,120121],"mapped","b"],[[120122,120122],"disallowed"],[[120123,120123],"mapped","d"],[[120124,120124],"mapped","e"],[[120125,120125],"mapped","f"],[[120126,120126],"mapped","g"],[[120127,120127],"disallowed"],[[120128,120128],"mapped","i"],[[120129,120129],"mapped","j"],[[120130,120130],"mapped","k"],[[120131,120131],"mapped","l"],[[120132,120132],"mapped","m"],[[120133,120133],"disallowed"],[[120134,120134],"mapped","o"],[[120135,120137],"disallowed"],[[120138,120138],"mapped","s"],[[120139,120139],"mapped","t"],[[120140,120140],"mapped","u"],[[120141,120141],"mapped","v"],[[120142,120142],"mapped","w"],[[120143,120143],"mapped","x"],[[120144,120144],"mapped","y"],[[120145,120145],"disallowed"],[[120146,120146],"mapped","a"],[[120147,120147],"mapped","b"],[[120148,120148],"mapped","c"],[[120149,120149],"mapped","d"],[[120150,120150],"mapped","e"],[[120151,120151],"mapped","f"],[[120152,120152],"mapped","g"],[[120153,120153],"mapped","h"],[[120154,120154],"mapped","i"],[[120155,120155],"mapped","j"],[[120156,120156],"mapped","k"],[[120157,120157],"mapped","l"],[[120158,120158],"mapped","m"],[[120159,120159],"mapped","n"],[[120160,120160],"mapped","o"],[[120161,120161],"mapped","p"],[[120162,120162],"mapped","q"],[[120163,120163],"mapped","r"],[[120164,120164],"mapped","s"],[[120165,120165],"mapped","t"],[[120166,120166],"mapped","u"],[[120167,120167],"mapped","v"],[[120168,120168],"mapped","w"],[[120169,120169],"mapped","x"],[[120170,120170],"mapped","y"],[[120171,120171],"mapped","z"],[[120172,120172],"mapped","a"],[[120173,120173],"mapped","b"],[[120174,120174],"mapped","c"],[[120175,120175],"mapped","d"],[[120176,120176],"mapped","e"],[[120177,120177],"mapped","f"],[[120178,120178],"mapped","g"],[[120179,120179],"mapped","h"],[[120180,120180],"mapped","i"],[[120181,120181],"mapped","j"],[[120182,120182],"mapped","k"],[[120183,120183],"mapped","l"],[[120184,120184],"mapped","m"],[[120185,120185],"mapped","n"],[[120186,120186],"mapped","o"],[[120187,120187],"mapped","p"],[[120188,120188],"mapped","q"],[[120189,120189],"mapped","r"],[[120190,120190],"mapped","s"],[[120191,120191],"mapped","t"],[[120192,120192],"mapped","u"],[[120193,120193],"mapped","v"],[[120194,120194],"mapped","w"],[[120195,120195],"mapped","x"],[[120196,120196],"mapped","y"],[[120197,120197],"mapped","z"],[[120198,120198],"mapped","a"],[[120199,120199],"mapped","b"],[[120200,120200],"mapped","c"],[[120201,120201],"mapped","d"],[[120202,120202],"mapped","e"],[[120203,120203],"mapped","f"],[[120204,120204],"mapped","g"],[[120205,120205],"mapped","h"],[[120206,120206],"mapped","i"],[[120207,120207],"mapped","j"],[[120208,120208],"mapped","k"],[[120209,120209],"mapped","l"],[[120210,120210],"mapped","m"],[[120211,120211],"mapped","n"],[[120212,120212],"mapped","o"],[[120213,120213],"mapped","p"],[[120214,120214],"mapped","q"],[[120215,120215],"mapped","r"],[[120216,120216],"mapped","s"],[[120217,120217],"mapped","t"],[[120218,120218],"mapped","u"],[[120219,120219],"mapped","v"],[[120220,120220],"mapped","w"],[[120221,120221],"mapped","x"],[[120222,120222],"mapped","y"],[[120223,120223],"mapped","z"],[[120224,120224],"mapped","a"],[[120225,120225],"mapped","b"],[[120226,120226],"mapped","c"],[[120227,120227],"mapped","d"],[[120228,120228],"mapped","e"],[[120229,120229],"mapped","f"],[[120230,120230],"mapped","g"],[[120231,120231],"mapped","h"],[[120232,120232],"mapped","i"],[[120233,120233],"mapped","j"],[[120234,120234],"mapped","k"],[[120235,120235],"mapped","l"],[[120236,120236],"mapped","m"],[[120237,120237],"mapped","n"],[[120238,120238],"mapped","o"],[[120239,120239],"mapped","p"],[[120240,120240],"mapped","q"],[[120241,120241],"mapped","r"],[[120242,120242],"mapped","s"],[[120243,120243],"mapped","t"],[[120244,120244],"mapped","u"],[[120245,120245],"mapped","v"],[[120246,120246],"mapped","w"],[[120247,120247],"mapped","x"],[[120248,120248],"mapped","y"],[[120249,120249],"mapped","z"],[[120250,120250],"mapped","a"],[[120251,120251],"mapped","b"],[[120252,120252],"mapped","c"],[[120253,120253],"mapped","d"],[[120254,120254],"mapped","e"],[[120255,120255],"mapped","f"],[[120256,120256],"mapped","g"],[[120257,120257],"mapped","h"],[[120258,120258],"mapped","i"],[[120259,120259],"mapped","j"],[[120260,120260],"mapped","k"],[[120261,120261],"mapped","l"],[[120262,120262],"mapped","m"],[[120263,120263],"mapped","n"],[[120264,120264],"mapped","o"],[[120265,120265],"mapped","p"],[[120266,120266],"mapped","q"],[[120267,120267],"mapped","r"],[[120268,120268],"mapped","s"],[[120269,120269],"mapped","t"],[[120270,120270],"mapped","u"],[[120271,120271],"mapped","v"],[[120272,120272],"mapped","w"],[[120273,120273],"mapped","x"],[[120274,120274],"mapped","y"],[[120275,120275],"mapped","z"],[[120276,120276],"mapped","a"],[[120277,120277],"mapped","b"],[[120278,120278],"mapped","c"],[[120279,120279],"mapped","d"],[[120280,120280],"mapped","e"],[[120281,120281],"mapped","f"],[[120282,120282],"mapped","g"],[[120283,120283],"mapped","h"],[[120284,120284],"mapped","i"],[[120285,120285],"mapped","j"],[[120286,120286],"mapped","k"],[[120287,120287],"mapped","l"],[[120288,120288],"mapped","m"],[[120289,120289],"mapped","n"],[[120290,120290],"mapped","o"],[[120291,120291],"mapped","p"],[[120292,120292],"mapped","q"],[[120293,120293],"mapped","r"],[[120294,120294],"mapped","s"],[[120295,120295],"mapped","t"],[[120296,120296],"mapped","u"],[[120297,120297],"mapped","v"],[[120298,120298],"mapped","w"],[[120299,120299],"mapped","x"],[[120300,120300],"mapped","y"],[[120301,120301],"mapped","z"],[[120302,120302],"mapped","a"],[[120303,120303],"mapped","b"],[[120304,120304],"mapped","c"],[[120305,120305],"mapped","d"],[[120306,120306],"mapped","e"],[[120307,120307],"mapped","f"],[[120308,120308],"mapped","g"],[[120309,120309],"mapped","h"],[[120310,120310],"mapped","i"],[[120311,120311],"mapped","j"],[[120312,120312],"mapped","k"],[[120313,120313],"mapped","l"],[[120314,120314],"mapped","m"],[[120315,120315],"mapped","n"],[[120316,120316],"mapped","o"],[[120317,120317],"mapped","p"],[[120318,120318],"mapped","q"],[[120319,120319],"mapped","r"],[[120320,120320],"mapped","s"],[[120321,120321],"mapped","t"],[[120322,120322],"mapped","u"],[[120323,120323],"mapped","v"],[[120324,120324],"mapped","w"],[[120325,120325],"mapped","x"],[[120326,120326],"mapped","y"],[[120327,120327],"mapped","z"],[[120328,120328],"mapped","a"],[[120329,120329],"mapped","b"],[[120330,120330],"mapped","c"],[[120331,120331],"mapped","d"],[[120332,120332],"mapped","e"],[[120333,120333],"mapped","f"],[[120334,120334],"mapped","g"],[[120335,120335],"mapped","h"],[[120336,120336],"mapped","i"],[[120337,120337],"mapped","j"],[[120338,120338],"mapped","k"],[[120339,120339],"mapped","l"],[[120340,120340],"mapped","m"],[[120341,120341],"mapped","n"],[[120342,120342],"mapped","o"],[[120343,120343],"mapped","p"],[[120344,120344],"mapped","q"],[[120345,120345],"mapped","r"],[[120346,120346],"mapped","s"],[[120347,120347],"mapped","t"],[[120348,120348],"mapped","u"],[[120349,120349],"mapped","v"],[[120350,120350],"mapped","w"],[[120351,120351],"mapped","x"],[[120352,120352],"mapped","y"],[[120353,120353],"mapped","z"],[[120354,120354],"mapped","a"],[[120355,120355],"mapped","b"],[[120356,120356],"mapped","c"],[[120357,120357],"mapped","d"],[[120358,120358],"mapped","e"],[[120359,120359],"mapped","f"],[[120360,120360],"mapped","g"],[[120361,120361],"mapped","h"],[[120362,120362],"mapped","i"],[[120363,120363],"mapped","j"],[[120364,120364],"mapped","k"],[[120365,120365],"mapped","l"],[[120366,120366],"mapped","m"],[[120367,120367],"mapped","n"],[[120368,120368],"mapped","o"],[[120369,120369],"mapped","p"],[[120370,120370],"mapped","q"],[[120371,120371],"mapped","r"],[[120372,120372],"mapped","s"],[[120373,120373],"mapped","t"],[[120374,120374],"mapped","u"],[[120375,120375],"mapped","v"],[[120376,120376],"mapped","w"],[[120377,120377],"mapped","x"],[[120378,120378],"mapped","y"],[[120379,120379],"mapped","z"],[[120380,120380],"mapped","a"],[[120381,120381],"mapped","b"],[[120382,120382],"mapped","c"],[[120383,120383],"mapped","d"],[[120384,120384],"mapped","e"],[[120385,120385],"mapped","f"],[[120386,120386],"mapped","g"],[[120387,120387],"mapped","h"],[[120388,120388],"mapped","i"],[[120389,120389],"mapped","j"],[[120390,120390],"mapped","k"],[[120391,120391],"mapped","l"],[[120392,120392],"mapped","m"],[[120393,120393],"mapped","n"],[[120394,120394],"mapped","o"],[[120395,120395],"mapped","p"],[[120396,120396],"mapped","q"],[[120397,120397],"mapped","r"],[[120398,120398],"mapped","s"],[[120399,120399],"mapped","t"],[[120400,120400],"mapped","u"],[[120401,120401],"mapped","v"],[[120402,120402],"mapped","w"],[[120403,120403],"mapped","x"],[[120404,120404],"mapped","y"],[[120405,120405],"mapped","z"],[[120406,120406],"mapped","a"],[[120407,120407],"mapped","b"],[[120408,120408],"mapped","c"],[[120409,120409],"mapped","d"],[[120410,120410],"mapped","e"],[[120411,120411],"mapped","f"],[[120412,120412],"mapped","g"],[[120413,120413],"mapped","h"],[[120414,120414],"mapped","i"],[[120415,120415],"mapped","j"],[[120416,120416],"mapped","k"],[[120417,120417],"mapped","l"],[[120418,120418],"mapped","m"],[[120419,120419],"mapped","n"],[[120420,120420],"mapped","o"],[[120421,120421],"mapped","p"],[[120422,120422],"mapped","q"],[[120423,120423],"mapped","r"],[[120424,120424],"mapped","s"],[[120425,120425],"mapped","t"],[[120426,120426],"mapped","u"],[[120427,120427],"mapped","v"],[[120428,120428],"mapped","w"],[[120429,120429],"mapped","x"],[[120430,120430],"mapped","y"],[[120431,120431],"mapped","z"],[[120432,120432],"mapped","a"],[[120433,120433],"mapped","b"],[[120434,120434],"mapped","c"],[[120435,120435],"mapped","d"],[[120436,120436],"mapped","e"],[[120437,120437],"mapped","f"],[[120438,120438],"mapped","g"],[[120439,120439],"mapped","h"],[[120440,120440],"mapped","i"],[[120441,120441],"mapped","j"],[[120442,120442],"mapped","k"],[[120443,120443],"mapped","l"],[[120444,120444],"mapped","m"],[[120445,120445],"mapped","n"],[[120446,120446],"mapped","o"],[[120447,120447],"mapped","p"],[[120448,120448],"mapped","q"],[[120449,120449],"mapped","r"],[[120450,120450],"mapped","s"],[[120451,120451],"mapped","t"],[[120452,120452],"mapped","u"],[[120453,120453],"mapped","v"],[[120454,120454],"mapped","w"],[[120455,120455],"mapped","x"],[[120456,120456],"mapped","y"],[[120457,120457],"mapped","z"],[[120458,120458],"mapped","a"],[[120459,120459],"mapped","b"],[[120460,120460],"mapped","c"],[[120461,120461],"mapped","d"],[[120462,120462],"mapped","e"],[[120463,120463],"mapped","f"],[[120464,120464],"mapped","g"],[[120465,120465],"mapped","h"],[[120466,120466],"mapped","i"],[[120467,120467],"mapped","j"],[[120468,120468],"mapped","k"],[[120469,120469],"mapped","l"],[[120470,120470],"mapped","m"],[[120471,120471],"mapped","n"],[[120472,120472],"mapped","o"],[[120473,120473],"mapped","p"],[[120474,120474],"mapped","q"],[[120475,120475],"mapped","r"],[[120476,120476],"mapped","s"],[[120477,120477],"mapped","t"],[[120478,120478],"mapped","u"],[[120479,120479],"mapped","v"],[[120480,120480],"mapped","w"],[[120481,120481],"mapped","x"],[[120482,120482],"mapped","y"],[[120483,120483],"mapped","z"],[[120484,120484],"mapped","ı"],[[120485,120485],"mapped","ȷ"],[[120486,120487],"disallowed"],[[120488,120488],"mapped","α"],[[120489,120489],"mapped","β"],[[120490,120490],"mapped","γ"],[[120491,120491],"mapped","δ"],[[120492,120492],"mapped","ε"],[[120493,120493],"mapped","ζ"],[[120494,120494],"mapped","η"],[[120495,120495],"mapped","θ"],[[120496,120496],"mapped","ι"],[[120497,120497],"mapped","κ"],[[120498,120498],"mapped","λ"],[[120499,120499],"mapped","μ"],[[120500,120500],"mapped","ν"],[[120501,120501],"mapped","ξ"],[[120502,120502],"mapped","ο"],[[120503,120503],"mapped","π"],[[120504,120504],"mapped","ρ"],[[120505,120505],"mapped","θ"],[[120506,120506],"mapped","σ"],[[120507,120507],"mapped","τ"],[[120508,120508],"mapped","υ"],[[120509,120509],"mapped","φ"],[[120510,120510],"mapped","χ"],[[120511,120511],"mapped","ψ"],[[120512,120512],"mapped","ω"],[[120513,120513],"mapped","∇"],[[120514,120514],"mapped","α"],[[120515,120515],"mapped","β"],[[120516,120516],"mapped","γ"],[[120517,120517],"mapped","δ"],[[120518,120518],"mapped","ε"],[[120519,120519],"mapped","ζ"],[[120520,120520],"mapped","η"],[[120521,120521],"mapped","θ"],[[120522,120522],"mapped","ι"],[[120523,120523],"mapped","κ"],[[120524,120524],"mapped","λ"],[[120525,120525],"mapped","μ"],[[120526,120526],"mapped","ν"],[[120527,120527],"mapped","ξ"],[[120528,120528],"mapped","ο"],[[120529,120529],"mapped","π"],[[120530,120530],"mapped","ρ"],[[120531,120532],"mapped","σ"],[[120533,120533],"mapped","τ"],[[120534,120534],"mapped","υ"],[[120535,120535],"mapped","φ"],[[120536,120536],"mapped","χ"],[[120537,120537],"mapped","ψ"],[[120538,120538],"mapped","ω"],[[120539,120539],"mapped","∂"],[[120540,120540],"mapped","ε"],[[120541,120541],"mapped","θ"],[[120542,120542],"mapped","κ"],[[120543,120543],"mapped","φ"],[[120544,120544],"mapped","ρ"],[[120545,120545],"mapped","π"],[[120546,120546],"mapped","α"],[[120547,120547],"mapped","β"],[[120548,120548],"mapped","γ"],[[120549,120549],"mapped","δ"],[[120550,120550],"mapped","ε"],[[120551,120551],"mapped","ζ"],[[120552,120552],"mapped","η"],[[120553,120553],"mapped","θ"],[[120554,120554],"mapped","ι"],[[120555,120555],"mapped","κ"],[[120556,120556],"mapped","λ"],[[120557,120557],"mapped","μ"],[[120558,120558],"mapped","ν"],[[120559,120559],"mapped","ξ"],[[120560,120560],"mapped","ο"],[[120561,120561],"mapped","π"],[[120562,120562],"mapped","ρ"],[[120563,120563],"mapped","θ"],[[120564,120564],"mapped","σ"],[[120565,120565],"mapped","τ"],[[120566,120566],"mapped","υ"],[[120567,120567],"mapped","φ"],[[120568,120568],"mapped","χ"],[[120569,120569],"mapped","ψ"],[[120570,120570],"mapped","ω"],[[120571,120571],"mapped","∇"],[[120572,120572],"mapped","α"],[[120573,120573],"mapped","β"],[[120574,120574],"mapped","γ"],[[120575,120575],"mapped","δ"],[[120576,120576],"mapped","ε"],[[120577,120577],"mapped","ζ"],[[120578,120578],"mapped","η"],[[120579,120579],"mapped","θ"],[[120580,120580],"mapped","ι"],[[120581,120581],"mapped","κ"],[[120582,120582],"mapped","λ"],[[120583,120583],"mapped","μ"],[[120584,120584],"mapped","ν"],[[120585,120585],"mapped","ξ"],[[120586,120586],"mapped","ο"],[[120587,120587],"mapped","π"],[[120588,120588],"mapped","ρ"],[[120589,120590],"mapped","σ"],[[120591,120591],"mapped","τ"],[[120592,120592],"mapped","υ"],[[120593,120593],"mapped","φ"],[[120594,120594],"mapped","χ"],[[120595,120595],"mapped","ψ"],[[120596,120596],"mapped","ω"],[[120597,120597],"mapped","∂"],[[120598,120598],"mapped","ε"],[[120599,120599],"mapped","θ"],[[120600,120600],"mapped","κ"],[[120601,120601],"mapped","φ"],[[120602,120602],"mapped","ρ"],[[120603,120603],"mapped","π"],[[120604,120604],"mapped","α"],[[120605,120605],"mapped","β"],[[120606,120606],"mapped","γ"],[[120607,120607],"mapped","δ"],[[120608,120608],"mapped","ε"],[[120609,120609],"mapped","ζ"],[[120610,120610],"mapped","η"],[[120611,120611],"mapped","θ"],[[120612,120612],"mapped","ι"],[[120613,120613],"mapped","κ"],[[120614,120614],"mapped","λ"],[[120615,120615],"mapped","μ"],[[120616,120616],"mapped","ν"],[[120617,120617],"mapped","ξ"],[[120618,120618],"mapped","ο"],[[120619,120619],"mapped","π"],[[120620,120620],"mapped","ρ"],[[120621,120621],"mapped","θ"],[[120622,120622],"mapped","σ"],[[120623,120623],"mapped","τ"],[[120624,120624],"mapped","υ"],[[120625,120625],"mapped","φ"],[[120626,120626],"mapped","χ"],[[120627,120627],"mapped","ψ"],[[120628,120628],"mapped","ω"],[[120629,120629],"mapped","∇"],[[120630,120630],"mapped","α"],[[120631,120631],"mapped","β"],[[120632,120632],"mapped","γ"],[[120633,120633],"mapped","δ"],[[120634,120634],"mapped","ε"],[[120635,120635],"mapped","ζ"],[[120636,120636],"mapped","η"],[[120637,120637],"mapped","θ"],[[120638,120638],"mapped","ι"],[[120639,120639],"mapped","κ"],[[120640,120640],"mapped","λ"],[[120641,120641],"mapped","μ"],[[120642,120642],"mapped","ν"],[[120643,120643],"mapped","ξ"],[[120644,120644],"mapped","ο"],[[120645,120645],"mapped","π"],[[120646,120646],"mapped","ρ"],[[120647,120648],"mapped","σ"],[[120649,120649],"mapped","τ"],[[120650,120650],"mapped","υ"],[[120651,120651],"mapped","φ"],[[120652,120652],"mapped","χ"],[[120653,120653],"mapped","ψ"],[[120654,120654],"mapped","ω"],[[120655,120655],"mapped","∂"],[[120656,120656],"mapped","ε"],[[120657,120657],"mapped","θ"],[[120658,120658],"mapped","κ"],[[120659,120659],"mapped","φ"],[[120660,120660],"mapped","ρ"],[[120661,120661],"mapped","π"],[[120662,120662],"mapped","α"],[[120663,120663],"mapped","β"],[[120664,120664],"mapped","γ"],[[120665,120665],"mapped","δ"],[[120666,120666],"mapped","ε"],[[120667,120667],"mapped","ζ"],[[120668,120668],"mapped","η"],[[120669,120669],"mapped","θ"],[[120670,120670],"mapped","ι"],[[120671,120671],"mapped","κ"],[[120672,120672],"mapped","λ"],[[120673,120673],"mapped","μ"],[[120674,120674],"mapped","ν"],[[120675,120675],"mapped","ξ"],[[120676,120676],"mapped","ο"],[[120677,120677],"mapped","π"],[[120678,120678],"mapped","ρ"],[[120679,120679],"mapped","θ"],[[120680,120680],"mapped","σ"],[[120681,120681],"mapped","τ"],[[120682,120682],"mapped","υ"],[[120683,120683],"mapped","φ"],[[120684,120684],"mapped","χ"],[[120685,120685],"mapped","ψ"],[[120686,120686],"mapped","ω"],[[120687,120687],"mapped","∇"],[[120688,120688],"mapped","α"],[[120689,120689],"mapped","β"],[[120690,120690],"mapped","γ"],[[120691,120691],"mapped","δ"],[[120692,120692],"mapped","ε"],[[120693,120693],"mapped","ζ"],[[120694,120694],"mapped","η"],[[120695,120695],"mapped","θ"],[[120696,120696],"mapped","ι"],[[120697,120697],"mapped","κ"],[[120698,120698],"mapped","λ"],[[120699,120699],"mapped","μ"],[[120700,120700],"mapped","ν"],[[120701,120701],"mapped","ξ"],[[120702,120702],"mapped","ο"],[[120703,120703],"mapped","π"],[[120704,120704],"mapped","ρ"],[[120705,120706],"mapped","σ"],[[120707,120707],"mapped","τ"],[[120708,120708],"mapped","υ"],[[120709,120709],"mapped","φ"],[[120710,120710],"mapped","χ"],[[120711,120711],"mapped","ψ"],[[120712,120712],"mapped","ω"],[[120713,120713],"mapped","∂"],[[120714,120714],"mapped","ε"],[[120715,120715],"mapped","θ"],[[120716,120716],"mapped","κ"],[[120717,120717],"mapped","φ"],[[120718,120718],"mapped","ρ"],[[120719,120719],"mapped","π"],[[120720,120720],"mapped","α"],[[120721,120721],"mapped","β"],[[120722,120722],"mapped","γ"],[[120723,120723],"mapped","δ"],[[120724,120724],"mapped","ε"],[[120725,120725],"mapped","ζ"],[[120726,120726],"mapped","η"],[[120727,120727],"mapped","θ"],[[120728,120728],"mapped","ι"],[[120729,120729],"mapped","κ"],[[120730,120730],"mapped","λ"],[[120731,120731],"mapped","μ"],[[120732,120732],"mapped","ν"],[[120733,120733],"mapped","ξ"],[[120734,120734],"mapped","ο"],[[120735,120735],"mapped","π"],[[120736,120736],"mapped","ρ"],[[120737,120737],"mapped","θ"],[[120738,120738],"mapped","σ"],[[120739,120739],"mapped","τ"],[[120740,120740],"mapped","υ"],[[120741,120741],"mapped","φ"],[[120742,120742],"mapped","χ"],[[120743,120743],"mapped","ψ"],[[120744,120744],"mapped","ω"],[[120745,120745],"mapped","∇"],[[120746,120746],"mapped","α"],[[120747,120747],"mapped","β"],[[120748,120748],"mapped","γ"],[[120749,120749],"mapped","δ"],[[120750,120750],"mapped","ε"],[[120751,120751],"mapped","ζ"],[[120752,120752],"mapped","η"],[[120753,120753],"mapped","θ"],[[120754,120754],"mapped","ι"],[[120755,120755],"mapped","κ"],[[120756,120756],"mapped","λ"],[[120757,120757],"mapped","μ"],[[120758,120758],"mapped","ν"],[[120759,120759],"mapped","ξ"],[[120760,120760],"mapped","ο"],[[120761,120761],"mapped","π"],[[120762,120762],"mapped","ρ"],[[120763,120764],"mapped","σ"],[[120765,120765],"mapped","τ"],[[120766,120766],"mapped","υ"],[[120767,120767],"mapped","φ"],[[120768,120768],"mapped","χ"],[[120769,120769],"mapped","ψ"],[[120770,120770],"mapped","ω"],[[120771,120771],"mapped","∂"],[[120772,120772],"mapped","ε"],[[120773,120773],"mapped","θ"],[[120774,120774],"mapped","κ"],[[120775,120775],"mapped","φ"],[[120776,120776],"mapped","ρ"],[[120777,120777],"mapped","π"],[[120778,120779],"mapped","ϝ"],[[120780,120781],"disallowed"],[[120782,120782],"mapped","0"],[[120783,120783],"mapped","1"],[[120784,120784],"mapped","2"],[[120785,120785],"mapped","3"],[[120786,120786],"mapped","4"],[[120787,120787],"mapped","5"],[[120788,120788],"mapped","6"],[[120789,120789],"mapped","7"],[[120790,120790],"mapped","8"],[[120791,120791],"mapped","9"],[[120792,120792],"mapped","0"],[[120793,120793],"mapped","1"],[[120794,120794],"mapped","2"],[[120795,120795],"mapped","3"],[[120796,120796],"mapped","4"],[[120797,120797],"mapped","5"],[[120798,120798],"mapped","6"],[[120799,120799],"mapped","7"],[[120800,120800],"mapped","8"],[[120801,120801],"mapped","9"],[[120802,120802],"mapped","0"],[[120803,120803],"mapped","1"],[[120804,120804],"mapped","2"],[[120805,120805],"mapped","3"],[[120806,120806],"mapped","4"],[[120807,120807],"mapped","5"],[[120808,120808],"mapped","6"],[[120809,120809],"mapped","7"],[[120810,120810],"mapped","8"],[[120811,120811],"mapped","9"],[[120812,120812],"mapped","0"],[[120813,120813],"mapped","1"],[[120814,120814],"mapped","2"],[[120815,120815],"mapped","3"],[[120816,120816],"mapped","4"],[[120817,120817],"mapped","5"],[[120818,120818],"mapped","6"],[[120819,120819],"mapped","7"],[[120820,120820],"mapped","8"],[[120821,120821],"mapped","9"],[[120822,120822],"mapped","0"],[[120823,120823],"mapped","1"],[[120824,120824],"mapped","2"],[[120825,120825],"mapped","3"],[[120826,120826],"mapped","4"],[[120827,120827],"mapped","5"],[[120828,120828],"mapped","6"],[[120829,120829],"mapped","7"],[[120830,120830],"mapped","8"],[[120831,120831],"mapped","9"],[[120832,121343],"valid","","NV8"],[[121344,121398],"valid"],[[121399,121402],"valid","","NV8"],[[121403,121452],"valid"],[[121453,121460],"valid","","NV8"],[[121461,121461],"valid"],[[121462,121475],"valid","","NV8"],[[121476,121476],"valid"],[[121477,121483],"valid","","NV8"],[[121484,121498],"disallowed"],[[121499,121503],"valid"],[[121504,121504],"disallowed"],[[121505,121519],"valid"],[[121520,122879],"disallowed"],[[122880,122886],"valid"],[[122887,122887],"disallowed"],[[122888,122904],"valid"],[[122905,122906],"disallowed"],[[122907,122913],"valid"],[[122914,122914],"disallowed"],[[122915,122916],"valid"],[[122917,122917],"disallowed"],[[122918,122922],"valid"],[[122923,124927],"disallowed"],[[124928,125124],"valid"],[[125125,125126],"disallowed"],[[125127,125135],"valid","","NV8"],[[125136,125142],"valid"],[[125143,125183],"disallowed"],[[125184,125184],"mapped","𞤢"],[[125185,125185],"mapped","𞤣"],[[125186,125186],"mapped","𞤤"],[[125187,125187],"mapped","𞤥"],[[125188,125188],"mapped","𞤦"],[[125189,125189],"mapped","𞤧"],[[125190,125190],"mapped","𞤨"],[[125191,125191],"mapped","𞤩"],[[125192,125192],"mapped","𞤪"],[[125193,125193],"mapped","𞤫"],[[125194,125194],"mapped","𞤬"],[[125195,125195],"mapped","𞤭"],[[125196,125196],"mapped","𞤮"],[[125197,125197],"mapped","𞤯"],[[125198,125198],"mapped","𞤰"],[[125199,125199],"mapped","𞤱"],[[125200,125200],"mapped","𞤲"],[[125201,125201],"mapped","𞤳"],[[125202,125202],"mapped","𞤴"],[[125203,125203],"mapped","𞤵"],[[125204,125204],"mapped","𞤶"],[[125205,125205],"mapped","𞤷"],[[125206,125206],"mapped","𞤸"],[[125207,125207],"mapped","𞤹"],[[125208,125208],"mapped","𞤺"],[[125209,125209],"mapped","𞤻"],[[125210,125210],"mapped","𞤼"],[[125211,125211],"mapped","𞤽"],[[125212,125212],"mapped","𞤾"],[[125213,125213],"mapped","𞤿"],[[125214,125214],"mapped","𞥀"],[[125215,125215],"mapped","𞥁"],[[125216,125216],"mapped","𞥂"],[[125217,125217],"mapped","𞥃"],[[125218,125258],"valid"],[[125259,125263],"disallowed"],[[125264,125273],"valid"],[[125274,125277],"disallowed"],[[125278,125279],"valid","","NV8"],[[125280,126463],"disallowed"],[[126464,126464],"mapped","ا"],[[126465,126465],"mapped","ب"],[[126466,126466],"mapped","ج"],[[126467,126467],"mapped","د"],[[126468,126468],"disallowed"],[[126469,126469],"mapped","و"],[[126470,126470],"mapped","ز"],[[126471,126471],"mapped","ح"],[[126472,126472],"mapped","ط"],[[126473,126473],"mapped","ي"],[[126474,126474],"mapped","ك"],[[126475,126475],"mapped","ل"],[[126476,126476],"mapped","م"],[[126477,126477],"mapped","ن"],[[126478,126478],"mapped","س"],[[126479,126479],"mapped","ع"],[[126480,126480],"mapped","ف"],[[126481,126481],"mapped","ص"],[[126482,126482],"mapped","ق"],[[126483,126483],"mapped","ر"],[[126484,126484],"mapped","ش"],[[126485,126485],"mapped","ت"],[[126486,126486],"mapped","ث"],[[126487,126487],"mapped","خ"],[[126488,126488],"mapped","ذ"],[[126489,126489],"mapped","ض"],[[126490,126490],"mapped","ظ"],[[126491,126491],"mapped","غ"],[[126492,126492],"mapped","ٮ"],[[126493,126493],"mapped","ں"],[[126494,126494],"mapped","ڡ"],[[126495,126495],"mapped","ٯ"],[[126496,126496],"disallowed"],[[126497,126497],"mapped","ب"],[[126498,126498],"mapped","ج"],[[126499,126499],"disallowed"],[[126500,126500],"mapped","ه"],[[126501,126502],"disallowed"],[[126503,126503],"mapped","ح"],[[126504,126504],"disallowed"],[[126505,126505],"mapped","ي"],[[126506,126506],"mapped","ك"],[[126507,126507],"mapped","ل"],[[126508,126508],"mapped","م"],[[126509,126509],"mapped","ن"],[[126510,126510],"mapped","س"],[[126511,126511],"mapped","ع"],[[126512,126512],"mapped","ف"],[[126513,126513],"mapped","ص"],[[126514,126514],"mapped","ق"],[[126515,126515],"disallowed"],[[126516,126516],"mapped","ش"],[[126517,126517],"mapped","ت"],[[126518,126518],"mapped","ث"],[[126519,126519],"mapped","خ"],[[126520,126520],"disallowed"],[[126521,126521],"mapped","ض"],[[126522,126522],"disallowed"],[[126523,126523],"mapped","غ"],[[126524,126529],"disallowed"],[[126530,126530],"mapped","ج"],[[126531,126534],"disallowed"],[[126535,126535],"mapped","ح"],[[126536,126536],"disallowed"],[[126537,126537],"mapped","ي"],[[126538,126538],"disallowed"],[[126539,126539],"mapped","ل"],[[126540,126540],"disallowed"],[[126541,126541],"mapped","ن"],[[126542,126542],"mapped","س"],[[126543,126543],"mapped","ع"],[[126544,126544],"disallowed"],[[126545,126545],"mapped","ص"],[[126546,126546],"mapped","ق"],[[126547,126547],"disallowed"],[[126548,126548],"mapped","ش"],[[126549,126550],"disallowed"],[[126551,126551],"mapped","خ"],[[126552,126552],"disallowed"],[[126553,126553],"mapped","ض"],[[126554,126554],"disallowed"],[[126555,126555],"mapped","غ"],[[126556,126556],"disallowed"],[[126557,126557],"mapped","ں"],[[126558,126558],"disallowed"],[[126559,126559],"mapped","ٯ"],[[126560,126560],"disallowed"],[[126561,126561],"mapped","ب"],[[126562,126562],"mapped","ج"],[[126563,126563],"disallowed"],[[126564,126564],"mapped","ه"],[[126565,126566],"disallowed"],[[126567,126567],"mapped","ح"],[[126568,126568],"mapped","ط"],[[126569,126569],"mapped","ي"],[[126570,126570],"mapped","ك"],[[126571,126571],"disallowed"],[[126572,126572],"mapped","م"],[[126573,126573],"mapped","ن"],[[126574,126574],"mapped","س"],[[126575,126575],"mapped","ع"],[[126576,126576],"mapped","ف"],[[126577,126577],"mapped","ص"],[[126578,126578],"mapped","ق"],[[126579,126579],"disallowed"],[[126580,126580],"mapped","ش"],[[126581,126581],"mapped","ت"],[[126582,126582],"mapped","ث"],[[126583,126583],"mapped","خ"],[[126584,126584],"disallowed"],[[126585,126585],"mapped","ض"],[[126586,126586],"mapped","ظ"],[[126587,126587],"mapped","غ"],[[126588,126588],"mapped","ٮ"],[[126589,126589],"disallowed"],[[126590,126590],"mapped","ڡ"],[[126591,126591],"disallowed"],[[126592,126592],"mapped","ا"],[[126593,126593],"mapped","ب"],[[126594,126594],"mapped","ج"],[[126595,126595],"mapped","د"],[[126596,126596],"mapped","ه"],[[126597,126597],"mapped","و"],[[126598,126598],"mapped","ز"],[[126599,126599],"mapped","ح"],[[126600,126600],"mapped","ط"],[[126601,126601],"mapped","ي"],[[126602,126602],"disallowed"],[[126603,126603],"mapped","ل"],[[126604,126604],"mapped","م"],[[126605,126605],"mapped","ن"],[[126606,126606],"mapped","س"],[[126607,126607],"mapped","ع"],[[126608,126608],"mapped","ف"],[[126609,126609],"mapped","ص"],[[126610,126610],"mapped","ق"],[[126611,126611],"mapped","ر"],[[126612,126612],"mapped","ش"],[[126613,126613],"mapped","ت"],[[126614,126614],"mapped","ث"],[[126615,126615],"mapped","خ"],[[126616,126616],"mapped","ذ"],[[126617,126617],"mapped","ض"],[[126618,126618],"mapped","ظ"],[[126619,126619],"mapped","غ"],[[126620,126624],"disallowed"],[[126625,126625],"mapped","ب"],[[126626,126626],"mapped","ج"],[[126627,126627],"mapped","د"],[[126628,126628],"disallowed"],[[126629,126629],"mapped","و"],[[126630,126630],"mapped","ز"],[[126631,126631],"mapped","ح"],[[126632,126632],"mapped","ط"],[[126633,126633],"mapped","ي"],[[126634,126634],"disallowed"],[[126635,126635],"mapped","ل"],[[126636,126636],"mapped","م"],[[126637,126637],"mapped","ن"],[[126638,126638],"mapped","س"],[[126639,126639],"mapped","ع"],[[126640,126640],"mapped","ف"],[[126641,126641],"mapped","ص"],[[126642,126642],"mapped","ق"],[[126643,126643],"mapped","ر"],[[126644,126644],"mapped","ش"],[[126645,126645],"mapped","ت"],[[126646,126646],"mapped","ث"],[[126647,126647],"mapped","خ"],[[126648,126648],"mapped","ذ"],[[126649,126649],"mapped","ض"],[[126650,126650],"mapped","ظ"],[[126651,126651],"mapped","غ"],[[126652,126703],"disallowed"],[[126704,126705],"valid","","NV8"],[[126706,126975],"disallowed"],[[126976,127019],"valid","","NV8"],[[127020,127023],"disallowed"],[[127024,127123],"valid","","NV8"],[[127124,127135],"disallowed"],[[127136,127150],"valid","","NV8"],[[127151,127152],"disallowed"],[[127153,127166],"valid","","NV8"],[[127167,127167],"valid","","NV8"],[[127168,127168],"disallowed"],[[127169,127183],"valid","","NV8"],[[127184,127184],"disallowed"],[[127185,127199],"valid","","NV8"],[[127200,127221],"valid","","NV8"],[[127222,127231],"disallowed"],[[127232,127232],"disallowed"],[[127233,127233],"disallowed_STD3_mapped","0,"],[[127234,127234],"disallowed_STD3_mapped","1,"],[[127235,127235],"disallowed_STD3_mapped","2,"],[[127236,127236],"disallowed_STD3_mapped","3,"],[[127237,127237],"disallowed_STD3_mapped","4,"],[[127238,127238],"disallowed_STD3_mapped","5,"],[[127239,127239],"disallowed_STD3_mapped","6,"],[[127240,127240],"disallowed_STD3_mapped","7,"],[[127241,127241],"disallowed_STD3_mapped","8,"],[[127242,127242],"disallowed_STD3_mapped","9,"],[[127243,127244],"valid","","NV8"],[[127245,127247],"disallowed"],[[127248,127248],"disallowed_STD3_mapped","(a)"],[[127249,127249],"disallowed_STD3_mapped","(b)"],[[127250,127250],"disallowed_STD3_mapped","(c)"],[[127251,127251],"disallowed_STD3_mapped","(d)"],[[127252,127252],"disallowed_STD3_mapped","(e)"],[[127253,127253],"disallowed_STD3_mapped","(f)"],[[127254,127254],"disallowed_STD3_mapped","(g)"],[[127255,127255],"disallowed_STD3_mapped","(h)"],[[127256,127256],"disallowed_STD3_mapped","(i)"],[[127257,127257],"disallowed_STD3_mapped","(j)"],[[127258,127258],"disallowed_STD3_mapped","(k)"],[[127259,127259],"disallowed_STD3_mapped","(l)"],[[127260,127260],"disallowed_STD3_mapped","(m)"],[[127261,127261],"disallowed_STD3_mapped","(n)"],[[127262,127262],"disallowed_STD3_mapped","(o)"],[[127263,127263],"disallowed_STD3_mapped","(p)"],[[127264,127264],"disallowed_STD3_mapped","(q)"],[[127265,127265],"disallowed_STD3_mapped","(r)"],[[127266,127266],"disallowed_STD3_mapped","(s)"],[[127267,127267],"disallowed_STD3_mapped","(t)"],[[127268,127268],"disallowed_STD3_mapped","(u)"],[[127269,127269],"disallowed_STD3_mapped","(v)"],[[127270,127270],"disallowed_STD3_mapped","(w)"],[[127271,127271],"disallowed_STD3_mapped","(x)"],[[127272,127272],"disallowed_STD3_mapped","(y)"],[[127273,127273],"disallowed_STD3_mapped","(z)"],[[127274,127274],"mapped","〔s〕"],[[127275,127275],"mapped","c"],[[127276,127276],"mapped","r"],[[127277,127277],"mapped","cd"],[[127278,127278],"mapped","wz"],[[127279,127279],"disallowed"],[[127280,127280],"mapped","a"],[[127281,127281],"mapped","b"],[[127282,127282],"mapped","c"],[[127283,127283],"mapped","d"],[[127284,127284],"mapped","e"],[[127285,127285],"mapped","f"],[[127286,127286],"mapped","g"],[[127287,127287],"mapped","h"],[[127288,127288],"mapped","i"],[[127289,127289],"mapped","j"],[[127290,127290],"mapped","k"],[[127291,127291],"mapped","l"],[[127292,127292],"mapped","m"],[[127293,127293],"mapped","n"],[[127294,127294],"mapped","o"],[[127295,127295],"mapped","p"],[[127296,127296],"mapped","q"],[[127297,127297],"mapped","r"],[[127298,127298],"mapped","s"],[[127299,127299],"mapped","t"],[[127300,127300],"mapped","u"],[[127301,127301],"mapped","v"],[[127302,127302],"mapped","w"],[[127303,127303],"mapped","x"],[[127304,127304],"mapped","y"],[[127305,127305],"mapped","z"],[[127306,127306],"mapped","hv"],[[127307,127307],"mapped","mv"],[[127308,127308],"mapped","sd"],[[127309,127309],"mapped","ss"],[[127310,127310],"mapped","ppv"],[[127311,127311],"mapped","wc"],[[127312,127318],"valid","","NV8"],[[127319,127319],"valid","","NV8"],[[127320,127326],"valid","","NV8"],[[127327,127327],"valid","","NV8"],[[127328,127337],"valid","","NV8"],[[127338,127338],"mapped","mc"],[[127339,127339],"mapped","md"],[[127340,127343],"disallowed"],[[127344,127352],"valid","","NV8"],[[127353,127353],"valid","","NV8"],[[127354,127354],"valid","","NV8"],[[127355,127356],"valid","","NV8"],[[127357,127358],"valid","","NV8"],[[127359,127359],"valid","","NV8"],[[127360,127369],"valid","","NV8"],[[127370,127373],"valid","","NV8"],[[127374,127375],"valid","","NV8"],[[127376,127376],"mapped","dj"],[[127377,127386],"valid","","NV8"],[[127387,127404],"valid","","NV8"],[[127405,127461],"disallowed"],[[127462,127487],"valid","","NV8"],[[127488,127488],"mapped","ほか"],[[127489,127489],"mapped","ココ"],[[127490,127490],"mapped","サ"],[[127491,127503],"disallowed"],[[127504,127504],"mapped","手"],[[127505,127505],"mapped","字"],[[127506,127506],"mapped","双"],[[127507,127507],"mapped","デ"],[[127508,127508],"mapped","二"],[[127509,127509],"mapped","多"],[[127510,127510],"mapped","解"],[[127511,127511],"mapped","天"],[[127512,127512],"mapped","交"],[[127513,127513],"mapped","映"],[[127514,127514],"mapped","無"],[[127515,127515],"mapped","料"],[[127516,127516],"mapped","前"],[[127517,127517],"mapped","後"],[[127518,127518],"mapped","再"],[[127519,127519],"mapped","新"],[[127520,127520],"mapped","初"],[[127521,127521],"mapped","終"],[[127522,127522],"mapped","生"],[[127523,127523],"mapped","販"],[[127524,127524],"mapped","声"],[[127525,127525],"mapped","吹"],[[127526,127526],"mapped","演"],[[127527,127527],"mapped","投"],[[127528,127528],"mapped","捕"],[[127529,127529],"mapped","一"],[[127530,127530],"mapped","三"],[[127531,127531],"mapped","遊"],[[127532,127532],"mapped","左"],[[127533,127533],"mapped","中"],[[127534,127534],"mapped","右"],[[127535,127535],"mapped","指"],[[127536,127536],"mapped","走"],[[127537,127537],"mapped","打"],[[127538,127538],"mapped","禁"],[[127539,127539],"mapped","空"],[[127540,127540],"mapped","合"],[[127541,127541],"mapped","満"],[[127542,127542],"mapped","有"],[[127543,127543],"mapped","月"],[[127544,127544],"mapped","申"],[[127545,127545],"mapped","割"],[[127546,127546],"mapped","営"],[[127547,127547],"mapped","配"],[[127548,127551],"disallowed"],[[127552,127552],"mapped","〔本〕"],[[127553,127553],"mapped","〔三〕"],[[127554,127554],"mapped","〔二〕"],[[127555,127555],"mapped","〔安〕"],[[127556,127556],"mapped","〔点〕"],[[127557,127557],"mapped","〔打〕"],[[127558,127558],"mapped","〔盗〕"],[[127559,127559],"mapped","〔勝〕"],[[127560,127560],"mapped","〔敗〕"],[[127561,127567],"disallowed"],[[127568,127568],"mapped","得"],[[127569,127569],"mapped","可"],[[127570,127583],"disallowed"],[[127584,127589],"valid","","NV8"],[[127590,127743],"disallowed"],[[127744,127776],"valid","","NV8"],[[127777,127788],"valid","","NV8"],[[127789,127791],"valid","","NV8"],[[127792,127797],"valid","","NV8"],[[127798,127798],"valid","","NV8"],[[127799,127868],"valid","","NV8"],[[127869,127869],"valid","","NV8"],[[127870,127871],"valid","","NV8"],[[127872,127891],"valid","","NV8"],[[127892,127903],"valid","","NV8"],[[127904,127940],"valid","","NV8"],[[127941,127941],"valid","","NV8"],[[127942,127946],"valid","","NV8"],[[127947,127950],"valid","","NV8"],[[127951,127955],"valid","","NV8"],[[127956,127967],"valid","","NV8"],[[127968,127984],"valid","","NV8"],[[127985,127991],"valid","","NV8"],[[127992,127999],"valid","","NV8"],[[128000,128062],"valid","","NV8"],[[128063,128063],"valid","","NV8"],[[128064,128064],"valid","","NV8"],[[128065,128065],"valid","","NV8"],[[128066,128247],"valid","","NV8"],[[128248,128248],"valid","","NV8"],[[128249,128252],"valid","","NV8"],[[128253,128254],"valid","","NV8"],[[128255,128255],"valid","","NV8"],[[128256,128317],"valid","","NV8"],[[128318,128319],"valid","","NV8"],[[128320,128323],"valid","","NV8"],[[128324,128330],"valid","","NV8"],[[128331,128335],"valid","","NV8"],[[128336,128359],"valid","","NV8"],[[128360,128377],"valid","","NV8"],[[128378,128378],"valid","","NV8"],[[128379,128419],"valid","","NV8"],[[128420,128420],"valid","","NV8"],[[128421,128506],"valid","","NV8"],[[128507,128511],"valid","","NV8"],[[128512,128512],"valid","","NV8"],[[128513,128528],"valid","","NV8"],[[128529,128529],"valid","","NV8"],[[128530,128532],"valid","","NV8"],[[128533,128533],"valid","","NV8"],[[128534,128534],"valid","","NV8"],[[128535,128535],"valid","","NV8"],[[128536,128536],"valid","","NV8"],[[128537,128537],"valid","","NV8"],[[128538,128538],"valid","","NV8"],[[128539,128539],"valid","","NV8"],[[128540,128542],"valid","","NV8"],[[128543,128543],"valid","","NV8"],[[128544,128549],"valid","","NV8"],[[128550,128551],"valid","","NV8"],[[128552,128555],"valid","","NV8"],[[128556,128556],"valid","","NV8"],[[128557,128557],"valid","","NV8"],[[128558,128559],"valid","","NV8"],[[128560,128563],"valid","","NV8"],[[128564,128564],"valid","","NV8"],[[128565,128576],"valid","","NV8"],[[128577,128578],"valid","","NV8"],[[128579,128580],"valid","","NV8"],[[128581,128591],"valid","","NV8"],[[128592,128639],"valid","","NV8"],[[128640,128709],"valid","","NV8"],[[128710,128719],"valid","","NV8"],[[128720,128720],"valid","","NV8"],[[128721,128722],"valid","","NV8"],[[128723,128724],"valid","","NV8"],[[128725,128735],"disallowed"],[[128736,128748],"valid","","NV8"],[[128749,128751],"disallowed"],[[128752,128755],"valid","","NV8"],[[128756,128758],"valid","","NV8"],[[128759,128760],"valid","","NV8"],[[128761,128767],"disallowed"],[[128768,128883],"valid","","NV8"],[[128884,128895],"disallowed"],[[128896,128980],"valid","","NV8"],[[128981,129023],"disallowed"],[[129024,129035],"valid","","NV8"],[[129036,129039],"disallowed"],[[129040,129095],"valid","","NV8"],[[129096,129103],"disallowed"],[[129104,129113],"valid","","NV8"],[[129114,129119],"disallowed"],[[129120,129159],"valid","","NV8"],[[129160,129167],"disallowed"],[[129168,129197],"valid","","NV8"],[[129198,129279],"disallowed"],[[129280,129291],"valid","","NV8"],[[129292,129295],"disallowed"],[[129296,129304],"valid","","NV8"],[[129305,129310],"valid","","NV8"],[[129311,129311],"valid","","NV8"],[[129312,129319],"valid","","NV8"],[[129320,129327],"valid","","NV8"],[[129328,129328],"valid","","NV8"],[[129329,129330],"valid","","NV8"],[[129331,129342],"valid","","NV8"],[[129343,129343],"disallowed"],[[129344,129355],"valid","","NV8"],[[129356,129356],"valid","","NV8"],[[129357,129359],"disallowed"],[[129360,129374],"valid","","NV8"],[[129375,129387],"valid","","NV8"],[[129388,129407],"disallowed"],[[129408,129412],"valid","","NV8"],[[129413,129425],"valid","","NV8"],[[129426,129431],"valid","","NV8"],[[129432,129471],"disallowed"],[[129472,129472],"valid","","NV8"],[[129473,129487],"disallowed"],[[129488,129510],"valid","","NV8"],[[129511,131069],"disallowed"],[[131070,131071],"disallowed"],[[131072,173782],"valid"],[[173783,173823],"disallowed"],[[173824,177972],"valid"],[[177973,177983],"disallowed"],[[177984,178205],"valid"],[[178206,178207],"disallowed"],[[178208,183969],"valid"],[[183970,183983],"disallowed"],[[183984,191456],"valid"],[[191457,194559],"disallowed"],[[194560,194560],"mapped","丽"],[[194561,194561],"mapped","丸"],[[194562,194562],"mapped","乁"],[[194563,194563],"mapped","𠄢"],[[194564,194564],"mapped","你"],[[194565,194565],"mapped","侮"],[[194566,194566],"mapped","侻"],[[194567,194567],"mapped","倂"],[[194568,194568],"mapped","偺"],[[194569,194569],"mapped","備"],[[194570,194570],"mapped","僧"],[[194571,194571],"mapped","像"],[[194572,194572],"mapped","㒞"],[[194573,194573],"mapped","𠘺"],[[194574,194574],"mapped","免"],[[194575,194575],"mapped","兔"],[[194576,194576],"mapped","兤"],[[194577,194577],"mapped","具"],[[194578,194578],"mapped","𠔜"],[[194579,194579],"mapped","㒹"],[[194580,194580],"mapped","內"],[[194581,194581],"mapped","再"],[[194582,194582],"mapped","𠕋"],[[194583,194583],"mapped","冗"],[[194584,194584],"mapped","冤"],[[194585,194585],"mapped","仌"],[[194586,194586],"mapped","冬"],[[194587,194587],"mapped","况"],[[194588,194588],"mapped","𩇟"],[[194589,194589],"mapped","凵"],[[194590,194590],"mapped","刃"],[[194591,194591],"mapped","㓟"],[[194592,194592],"mapped","刻"],[[194593,194593],"mapped","剆"],[[194594,194594],"mapped","割"],[[194595,194595],"mapped","剷"],[[194596,194596],"mapped","㔕"],[[194597,194597],"mapped","勇"],[[194598,194598],"mapped","勉"],[[194599,194599],"mapped","勤"],[[194600,194600],"mapped","勺"],[[194601,194601],"mapped","包"],[[194602,194602],"mapped","匆"],[[194603,194603],"mapped","北"],[[194604,194604],"mapped","卉"],[[194605,194605],"mapped","卑"],[[194606,194606],"mapped","博"],[[194607,194607],"mapped","即"],[[194608,194608],"mapped","卽"],[[194609,194611],"mapped","卿"],[[194612,194612],"mapped","𠨬"],[[194613,194613],"mapped","灰"],[[194614,194614],"mapped","及"],[[194615,194615],"mapped","叟"],[[194616,194616],"mapped","𠭣"],[[194617,194617],"mapped","叫"],[[194618,194618],"mapped","叱"],[[194619,194619],"mapped","吆"],[[194620,194620],"mapped","咞"],[[194621,194621],"mapped","吸"],[[194622,194622],"mapped","呈"],[[194623,194623],"mapped","周"],[[194624,194624],"mapped","咢"],[[194625,194625],"mapped","哶"],[[194626,194626],"mapped","唐"],[[194627,194627],"mapped","啓"],[[194628,194628],"mapped","啣"],[[194629,194630],"mapped","善"],[[194631,194631],"mapped","喙"],[[194632,194632],"mapped","喫"],[[194633,194633],"mapped","喳"],[[194634,194634],"mapped","嗂"],[[194635,194635],"mapped","圖"],[[194636,194636],"mapped","嘆"],[[194637,194637],"mapped","圗"],[[194638,194638],"mapped","噑"],[[194639,194639],"mapped","噴"],[[194640,194640],"mapped","切"],[[194641,194641],"mapped","壮"],[[194642,194642],"mapped","城"],[[194643,194643],"mapped","埴"],[[194644,194644],"mapped","堍"],[[194645,194645],"mapped","型"],[[194646,194646],"mapped","堲"],[[194647,194647],"mapped","報"],[[194648,194648],"mapped","墬"],[[194649,194649],"mapped","𡓤"],[[194650,194650],"mapped","売"],[[194651,194651],"mapped","壷"],[[194652,194652],"mapped","夆"],[[194653,194653],"mapped","多"],[[194654,194654],"mapped","夢"],[[194655,194655],"mapped","奢"],[[194656,194656],"mapped","𡚨"],[[194657,194657],"mapped","𡛪"],[[194658,194658],"mapped","姬"],[[194659,194659],"mapped","娛"],[[194660,194660],"mapped","娧"],[[194661,194661],"mapped","姘"],[[194662,194662],"mapped","婦"],[[194663,194663],"mapped","㛮"],[[194664,194664],"disallowed"],[[194665,194665],"mapped","嬈"],[[194666,194667],"mapped","嬾"],[[194668,194668],"mapped","𡧈"],[[194669,194669],"mapped","寃"],[[194670,194670],"mapped","寘"],[[194671,194671],"mapped","寧"],[[194672,194672],"mapped","寳"],[[194673,194673],"mapped","𡬘"],[[194674,194674],"mapped","寿"],[[194675,194675],"mapped","将"],[[194676,194676],"disallowed"],[[194677,194677],"mapped","尢"],[[194678,194678],"mapped","㞁"],[[194679,194679],"mapped","屠"],[[194680,194680],"mapped","屮"],[[194681,194681],"mapped","峀"],[[194682,194682],"mapped","岍"],[[194683,194683],"mapped","𡷤"],[[194684,194684],"mapped","嵃"],[[194685,194685],"mapped","𡷦"],[[194686,194686],"mapped","嵮"],[[194687,194687],"mapped","嵫"],[[194688,194688],"mapped","嵼"],[[194689,194689],"mapped","巡"],[[194690,194690],"mapped","巢"],[[194691,194691],"mapped","㠯"],[[194692,194692],"mapped","巽"],[[194693,194693],"mapped","帨"],[[194694,194694],"mapped","帽"],[[194695,194695],"mapped","幩"],[[194696,194696],"mapped","㡢"],[[194697,194697],"mapped","𢆃"],[[194698,194698],"mapped","㡼"],[[194699,194699],"mapped","庰"],[[194700,194700],"mapped","庳"],[[194701,194701],"mapped","庶"],[[194702,194702],"mapped","廊"],[[194703,194703],"mapped","𪎒"],[[194704,194704],"mapped","廾"],[[194705,194706],"mapped","𢌱"],[[194707,194707],"mapped","舁"],[[194708,194709],"mapped","弢"],[[194710,194710],"mapped","㣇"],[[194711,194711],"mapped","𣊸"],[[194712,194712],"mapped","𦇚"],[[194713,194713],"mapped","形"],[[194714,194714],"mapped","彫"],[[194715,194715],"mapped","㣣"],[[194716,194716],"mapped","徚"],[[194717,194717],"mapped","忍"],[[194718,194718],"mapped","志"],[[194719,194719],"mapped","忹"],[[194720,194720],"mapped","悁"],[[194721,194721],"mapped","㤺"],[[194722,194722],"mapped","㤜"],[[194723,194723],"mapped","悔"],[[194724,194724],"mapped","𢛔"],[[194725,194725],"mapped","惇"],[[194726,194726],"mapped","慈"],[[194727,194727],"mapped","慌"],[[194728,194728],"mapped","慎"],[[194729,194729],"mapped","慌"],[[194730,194730],"mapped","慺"],[[194731,194731],"mapped","憎"],[[194732,194732],"mapped","憲"],[[194733,194733],"mapped","憤"],[[194734,194734],"mapped","憯"],[[194735,194735],"mapped","懞"],[[194736,194736],"mapped","懲"],[[194737,194737],"mapped","懶"],[[194738,194738],"mapped","成"],[[194739,194739],"mapped","戛"],[[194740,194740],"mapped","扝"],[[194741,194741],"mapped","抱"],[[194742,194742],"mapped","拔"],[[194743,194743],"mapped","捐"],[[194744,194744],"mapped","𢬌"],[[194745,194745],"mapped","挽"],[[194746,194746],"mapped","拼"],[[194747,194747],"mapped","捨"],[[194748,194748],"mapped","掃"],[[194749,194749],"mapped","揤"],[[194750,194750],"mapped","𢯱"],[[194751,194751],"mapped","搢"],[[194752,194752],"mapped","揅"],[[194753,194753],"mapped","掩"],[[194754,194754],"mapped","㨮"],[[194755,194755],"mapped","摩"],[[194756,194756],"mapped","摾"],[[194757,194757],"mapped","撝"],[[194758,194758],"mapped","摷"],[[194759,194759],"mapped","㩬"],[[194760,194760],"mapped","敏"],[[194761,194761],"mapped","敬"],[[194762,194762],"mapped","𣀊"],[[194763,194763],"mapped","旣"],[[194764,194764],"mapped","書"],[[194765,194765],"mapped","晉"],[[194766,194766],"mapped","㬙"],[[194767,194767],"mapped","暑"],[[194768,194768],"mapped","㬈"],[[194769,194769],"mapped","㫤"],[[194770,194770],"mapped","冒"],[[194771,194771],"mapped","冕"],[[194772,194772],"mapped","最"],[[194773,194773],"mapped","暜"],[[194774,194774],"mapped","肭"],[[194775,194775],"mapped","䏙"],[[194776,194776],"mapped","朗"],[[194777,194777],"mapped","望"],[[194778,194778],"mapped","朡"],[[194779,194779],"mapped","杞"],[[194780,194780],"mapped","杓"],[[194781,194781],"mapped","𣏃"],[[194782,194782],"mapped","㭉"],[[194783,194783],"mapped","柺"],[[194784,194784],"mapped","枅"],[[194785,194785],"mapped","桒"],[[194786,194786],"mapped","梅"],[[194787,194787],"mapped","𣑭"],[[194788,194788],"mapped","梎"],[[194789,194789],"mapped","栟"],[[194790,194790],"mapped","椔"],[[194791,194791],"mapped","㮝"],[[194792,194792],"mapped","楂"],[[194793,194793],"mapped","榣"],[[194794,194794],"mapped","槪"],[[194795,194795],"mapped","檨"],[[194796,194796],"mapped","𣚣"],[[194797,194797],"mapped","櫛"],[[194798,194798],"mapped","㰘"],[[194799,194799],"mapped","次"],[[194800,194800],"mapped","𣢧"],[[194801,194801],"mapped","歔"],[[194802,194802],"mapped","㱎"],[[194803,194803],"mapped","歲"],[[194804,194804],"mapped","殟"],[[194805,194805],"mapped","殺"],[[194806,194806],"mapped","殻"],[[194807,194807],"mapped","𣪍"],[[194808,194808],"mapped","𡴋"],[[194809,194809],"mapped","𣫺"],[[194810,194810],"mapped","汎"],[[194811,194811],"mapped","𣲼"],[[194812,194812],"mapped","沿"],[[194813,194813],"mapped","泍"],[[194814,194814],"mapped","汧"],[[194815,194815],"mapped","洖"],[[194816,194816],"mapped","派"],[[194817,194817],"mapped","海"],[[194818,194818],"mapped","流"],[[194819,194819],"mapped","浩"],[[194820,194820],"mapped","浸"],[[194821,194821],"mapped","涅"],[[194822,194822],"mapped","𣴞"],[[194823,194823],"mapped","洴"],[[194824,194824],"mapped","港"],[[194825,194825],"mapped","湮"],[[194826,194826],"mapped","㴳"],[[194827,194827],"mapped","滋"],[[194828,194828],"mapped","滇"],[[194829,194829],"mapped","𣻑"],[[194830,194830],"mapped","淹"],[[194831,194831],"mapped","潮"],[[194832,194832],"mapped","𣽞"],[[194833,194833],"mapped","𣾎"],[[194834,194834],"mapped","濆"],[[194835,194835],"mapped","瀹"],[[194836,194836],"mapped","瀞"],[[194837,194837],"mapped","瀛"],[[194838,194838],"mapped","㶖"],[[194839,194839],"mapped","灊"],[[194840,194840],"mapped","災"],[[194841,194841],"mapped","灷"],[[194842,194842],"mapped","炭"],[[194843,194843],"mapped","𠔥"],[[194844,194844],"mapped","煅"],[[194845,194845],"mapped","𤉣"],[[194846,194846],"mapped","熜"],[[194847,194847],"disallowed"],[[194848,194848],"mapped","爨"],[[194849,194849],"mapped","爵"],[[194850,194850],"mapped","牐"],[[194851,194851],"mapped","𤘈"],[[194852,194852],"mapped","犀"],[[194853,194853],"mapped","犕"],[[194854,194854],"mapped","𤜵"],[[194855,194855],"mapped","𤠔"],[[194856,194856],"mapped","獺"],[[194857,194857],"mapped","王"],[[194858,194858],"mapped","㺬"],[[194859,194859],"mapped","玥"],[[194860,194861],"mapped","㺸"],[[194862,194862],"mapped","瑇"],[[194863,194863],"mapped","瑜"],[[194864,194864],"mapped","瑱"],[[194865,194865],"mapped","璅"],[[194866,194866],"mapped","瓊"],[[194867,194867],"mapped","㼛"],[[194868,194868],"mapped","甤"],[[194869,194869],"mapped","𤰶"],[[194870,194870],"mapped","甾"],[[194871,194871],"mapped","𤲒"],[[194872,194872],"mapped","異"],[[194873,194873],"mapped","𢆟"],[[194874,194874],"mapped","瘐"],[[194875,194875],"mapped","𤾡"],[[194876,194876],"mapped","𤾸"],[[194877,194877],"mapped","𥁄"],[[194878,194878],"mapped","㿼"],[[194879,194879],"mapped","䀈"],[[194880,194880],"mapped","直"],[[194881,194881],"mapped","𥃳"],[[194882,194882],"mapped","𥃲"],[[194883,194883],"mapped","𥄙"],[[194884,194884],"mapped","𥄳"],[[194885,194885],"mapped","眞"],[[194886,194887],"mapped","真"],[[194888,194888],"mapped","睊"],[[194889,194889],"mapped","䀹"],[[194890,194890],"mapped","瞋"],[[194891,194891],"mapped","䁆"],[[194892,194892],"mapped","䂖"],[[194893,194893],"mapped","𥐝"],[[194894,194894],"mapped","硎"],[[194895,194895],"mapped","碌"],[[194896,194896],"mapped","磌"],[[194897,194897],"mapped","䃣"],[[194898,194898],"mapped","𥘦"],[[194899,194899],"mapped","祖"],[[194900,194900],"mapped","𥚚"],[[194901,194901],"mapped","𥛅"],[[194902,194902],"mapped","福"],[[194903,194903],"mapped","秫"],[[194904,194904],"mapped","䄯"],[[194905,194905],"mapped","穀"],[[194906,194906],"mapped","穊"],[[194907,194907],"mapped","穏"],[[194908,194908],"mapped","𥥼"],[[194909,194910],"mapped","𥪧"],[[194911,194911],"disallowed"],[[194912,194912],"mapped","䈂"],[[194913,194913],"mapped","𥮫"],[[194914,194914],"mapped","篆"],[[194915,194915],"mapped","築"],[[194916,194916],"mapped","䈧"],[[194917,194917],"mapped","𥲀"],[[194918,194918],"mapped","糒"],[[194919,194919],"mapped","䊠"],[[194920,194920],"mapped","糨"],[[194921,194921],"mapped","糣"],[[194922,194922],"mapped","紀"],[[194923,194923],"mapped","𥾆"],[[194924,194924],"mapped","絣"],[[194925,194925],"mapped","䌁"],[[194926,194926],"mapped","緇"],[[194927,194927],"mapped","縂"],[[194928,194928],"mapped","繅"],[[194929,194929],"mapped","䌴"],[[194930,194930],"mapped","𦈨"],[[194931,194931],"mapped","𦉇"],[[194932,194932],"mapped","䍙"],[[194933,194933],"mapped","𦋙"],[[194934,194934],"mapped","罺"],[[194935,194935],"mapped","𦌾"],[[194936,194936],"mapped","羕"],[[194937,194937],"mapped","翺"],[[194938,194938],"mapped","者"],[[194939,194939],"mapped","𦓚"],[[194940,194940],"mapped","𦔣"],[[194941,194941],"mapped","聠"],[[194942,194942],"mapped","𦖨"],[[194943,194943],"mapped","聰"],[[194944,194944],"mapped","𣍟"],[[194945,194945],"mapped","䏕"],[[194946,194946],"mapped","育"],[[194947,194947],"mapped","脃"],[[194948,194948],"mapped","䐋"],[[194949,194949],"mapped","脾"],[[194950,194950],"mapped","媵"],[[194951,194951],"mapped","𦞧"],[[194952,194952],"mapped","𦞵"],[[194953,194953],"mapped","𣎓"],[[194954,194954],"mapped","𣎜"],[[194955,194955],"mapped","舁"],[[194956,194956],"mapped","舄"],[[194957,194957],"mapped","辞"],[[194958,194958],"mapped","䑫"],[[194959,194959],"mapped","芑"],[[194960,194960],"mapped","芋"],[[194961,194961],"mapped","芝"],[[194962,194962],"mapped","劳"],[[194963,194963],"mapped","花"],[[194964,194964],"mapped","芳"],[[194965,194965],"mapped","芽"],[[194966,194966],"mapped","苦"],[[194967,194967],"mapped","𦬼"],[[194968,194968],"mapped","若"],[[194969,194969],"mapped","茝"],[[194970,194970],"mapped","荣"],[[194971,194971],"mapped","莭"],[[194972,194972],"mapped","茣"],[[194973,194973],"mapped","莽"],[[194974,194974],"mapped","菧"],[[194975,194975],"mapped","著"],[[194976,194976],"mapped","荓"],[[194977,194977],"mapped","菊"],[[194978,194978],"mapped","菌"],[[194979,194979],"mapped","菜"],[[194980,194980],"mapped","𦰶"],[[194981,194981],"mapped","𦵫"],[[194982,194982],"mapped","𦳕"],[[194983,194983],"mapped","䔫"],[[194984,194984],"mapped","蓱"],[[194985,194985],"mapped","蓳"],[[194986,194986],"mapped","蔖"],[[194987,194987],"mapped","𧏊"],[[194988,194988],"mapped","蕤"],[[194989,194989],"mapped","𦼬"],[[194990,194990],"mapped","䕝"],[[194991,194991],"mapped","䕡"],[[194992,194992],"mapped","𦾱"],[[194993,194993],"mapped","𧃒"],[[194994,194994],"mapped","䕫"],[[194995,194995],"mapped","虐"],[[194996,194996],"mapped","虜"],[[194997,194997],"mapped","虧"],[[194998,194998],"mapped","虩"],[[194999,194999],"mapped","蚩"],[[195000,195000],"mapped","蚈"],[[195001,195001],"mapped","蜎"],[[195002,195002],"mapped","蛢"],[[195003,195003],"mapped","蝹"],[[195004,195004],"mapped","蜨"],[[195005,195005],"mapped","蝫"],[[195006,195006],"mapped","螆"],[[195007,195007],"disallowed"],[[195008,195008],"mapped","蟡"],[[195009,195009],"mapped","蠁"],[[195010,195010],"mapped","䗹"],[[195011,195011],"mapped","衠"],[[195012,195012],"mapped","衣"],[[195013,195013],"mapped","𧙧"],[[195014,195014],"mapped","裗"],[[195015,195015],"mapped","裞"],[[195016,195016],"mapped","䘵"],[[195017,195017],"mapped","裺"],[[195018,195018],"mapped","㒻"],[[195019,195019],"mapped","𧢮"],[[195020,195020],"mapped","𧥦"],[[195021,195021],"mapped","䚾"],[[195022,195022],"mapped","䛇"],[[195023,195023],"mapped","誠"],[[195024,195024],"mapped","諭"],[[195025,195025],"mapped","變"],[[195026,195026],"mapped","豕"],[[195027,195027],"mapped","𧲨"],[[195028,195028],"mapped","貫"],[[195029,195029],"mapped","賁"],[[195030,195030],"mapped","贛"],[[195031,195031],"mapped","起"],[[195032,195032],"mapped","𧼯"],[[195033,195033],"mapped","𠠄"],[[195034,195034],"mapped","跋"],[[195035,195035],"mapped","趼"],[[195036,195036],"mapped","跰"],[[195037,195037],"mapped","𠣞"],[[195038,195038],"mapped","軔"],[[195039,195039],"mapped","輸"],[[195040,195040],"mapped","𨗒"],[[195041,195041],"mapped","𨗭"],[[195042,195042],"mapped","邔"],[[195043,195043],"mapped","郱"],[[195044,195044],"mapped","鄑"],[[195045,195045],"mapped","𨜮"],[[195046,195046],"mapped","鄛"],[[195047,195047],"mapped","鈸"],[[195048,195048],"mapped","鋗"],[[195049,195049],"mapped","鋘"],[[195050,195050],"mapped","鉼"],[[195051,195051],"mapped","鏹"],[[195052,195052],"mapped","鐕"],[[195053,195053],"mapped","𨯺"],[[195054,195054],"mapped","開"],[[195055,195055],"mapped","䦕"],[[195056,195056],"mapped","閷"],[[195057,195057],"mapped","𨵷"],[[195058,195058],"mapped","䧦"],[[195059,195059],"mapped","雃"],[[195060,195060],"mapped","嶲"],[[195061,195061],"mapped","霣"],[[195062,195062],"mapped","𩅅"],[[195063,195063],"mapped","𩈚"],[[195064,195064],"mapped","䩮"],[[195065,195065],"mapped","䩶"],[[195066,195066],"mapped","韠"],[[195067,195067],"mapped","𩐊"],[[195068,195068],"mapped","䪲"],[[195069,195069],"mapped","𩒖"],[[195070,195071],"mapped","頋"],[[195072,195072],"mapped","頩"],[[195073,195073],"mapped","𩖶"],[[195074,195074],"mapped","飢"],[[195075,195075],"mapped","䬳"],[[195076,195076],"mapped","餩"],[[195077,195077],"mapped","馧"],[[195078,195078],"mapped","駂"],[[195079,195079],"mapped","駾"],[[195080,195080],"mapped","䯎"],[[195081,195081],"mapped","𩬰"],[[195082,195082],"mapped","鬒"],[[195083,195083],"mapped","鱀"],[[195084,195084],"mapped","鳽"],[[195085,195085],"mapped","䳎"],[[195086,195086],"mapped","䳭"],[[195087,195087],"mapped","鵧"],[[195088,195088],"mapped","𪃎"],[[195089,195089],"mapped","䳸"],[[195090,195090],"mapped","𪄅"],[[195091,195091],"mapped","𪈎"],[[195092,195092],"mapped","𪊑"],[[195093,195093],"mapped","麻"],[[195094,195094],"mapped","䵖"],[[195095,195095],"mapped","黹"],[[195096,195096],"mapped","黾"],[[195097,195097],"mapped","鼅"],[[195098,195098],"mapped","鼏"],[[195099,195099],"mapped","鼖"],[[195100,195100],"mapped","鼻"],[[195101,195101],"mapped","𪘀"],[[195102,196605],"disallowed"],[[196606,196607],"disallowed"],[[196608,262141],"disallowed"],[[262142,262143],"disallowed"],[[262144,327677],"disallowed"],[[327678,327679],"disallowed"],[[327680,393213],"disallowed"],[[393214,393215],"disallowed"],[[393216,458749],"disallowed"],[[458750,458751],"disallowed"],[[458752,524285],"disallowed"],[[524286,524287],"disallowed"],[[524288,589821],"disallowed"],[[589822,589823],"disallowed"],[[589824,655357],"disallowed"],[[655358,655359],"disallowed"],[[655360,720893],"disallowed"],[[720894,720895],"disallowed"],[[720896,786429],"disallowed"],[[786430,786431],"disallowed"],[[786432,851965],"disallowed"],[[851966,851967],"disallowed"],[[851968,917501],"disallowed"],[[917502,917503],"disallowed"],[[917504,917504],"disallowed"],[[917505,917505],"disallowed"],[[917506,917535],"disallowed"],[[917536,917631],"disallowed"],[[917632,917759],"disallowed"],[[917760,917999],"ignored"],[[918000,983037],"disallowed"],[[983038,983039],"disallowed"],[[983040,1048573],"disallowed"],[[1048574,1048575],"disallowed"],[[1048576,1114109],"disallowed"],[[1114110,1114111],"disallowed"]]
+},{}],17:[function(require,module,exports){
+"use strict";
+
+const combiningMarks = /[\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D00-\u0D03\u0D3B\u0D3C\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF7-\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11000}-\u{11002}\u{11038}-\u{11046}\u{1107F}-\u{11082}\u{110B0}-\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{11134}\u{11173}\u{11180}-\u{11182}\u{111B3}-\u{111C0}\u{111CA}-\u{111CC}\u{1122C}-\u{11237}\u{1123E}\u{112DF}-\u{112EA}\u{11300}-\u{11303}\u{1133C}\u{1133E}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11357}\u{11362}\u{11363}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11435}-\u{11446}\u{114B0}-\u{114C3}\u{115AF}-\u{115B5}\u{115B8}-\u{115C0}\u{115DC}\u{115DD}\u{11630}-\u{11640}\u{116AB}-\u{116B7}\u{1171D}-\u{1172B}\u{11A01}-\u{11A0A}\u{11A33}-\u{11A39}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A5B}\u{11A8A}-\u{11A99}\u{11C2F}-\u{11C36}\u{11C38}-\u{11C3F}\u{11C92}-\u{11CA7}\u{11CA9}-\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F51}-\u{16F7E}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1D165}-\u{1D169}\u{1D16D}-\u{1D172}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0100}-\u{E01EF}]/u;
+const combiningClassVirama = /[\u094D\u09CD\u0A4D\u0ACD\u0B4D\u0BCD\u0C4D\u0CCD\u0D3B\u0D3C\u0D4D\u0DCA\u0E3A\u0F84\u1039\u103A\u1714\u1734\u17D2\u1A60\u1B44\u1BAA\u1BAB\u1BF2\u1BF3\u2D7F\uA806\uA8C4\uA953\uA9C0\uAAF6\uABED\u{10A3F}\u{11046}\u{1107F}\u{110B9}\u{11133}\u{11134}\u{111C0}\u{11235}\u{112EA}\u{1134D}\u{11442}\u{114C2}\u{115BF}\u{1163F}\u{116B6}\u{1172B}\u{11A34}\u{11A47}\u{11A99}\u{11C3F}\u{11D44}\u{11D45}]/u;
+const validZWNJ = /[\u0620\u0626\u0628\u062A-\u062E\u0633-\u063F\u0641-\u0647\u0649\u064A\u066E\u066F\u0678-\u0687\u069A-\u06BF\u06C1\u06C2\u06CC\u06CE\u06D0\u06D1\u06FA-\u06FC\u06FF\u0712-\u0714\u071A-\u071D\u071F-\u0727\u0729\u072B\u072D\u072E\u074E-\u0758\u075C-\u076A\u076D-\u0770\u0772\u0775-\u0777\u077A-\u077F\u07CA-\u07EA\u0841-\u0845\u0848\u084A-\u0853\u0855\u0860\u0862-\u0865\u0868\u08A0-\u08A9\u08AF\u08B0\u08B3\u08B4\u08B6-\u08B8\u08BA-\u08BD\u1807\u1820-\u1877\u1887-\u18A8\u18AA\uA840-\uA872\u{10AC0}-\u{10AC4}\u{10ACD}\u{10AD3}-\u{10ADC}\u{10ADE}-\u{10AE0}\u{10AEB}-\u{10AEE}\u{10B80}\u{10B82}\u{10B86}-\u{10B88}\u{10B8A}\u{10B8B}\u{10B8D}\u{10B90}\u{10BAD}\u{10BAE}\u{1E900}-\u{1E943}][\xAD\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u070F\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CBF\u0CC6\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u200B\u200E\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFEFF\uFFF9-\uFFFB\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{110BD}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C3F}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1D167}-\u{1D169}\u{1D173}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]*\u200C[\xAD\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u061C\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u070F\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CBF\u0CC6\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u200B\u200E\u200F\u202A-\u202E\u2060-\u2064\u206A-\u206F\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\uFEFF\uFFF9-\uFFFB\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{110BD}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C3F}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1D167}-\u{1D169}\u{1D173}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]*[\u0620\u0622-\u063F\u0641-\u064A\u066E\u066F\u0671-\u0673\u0675-\u06D3\u06D5\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u077F\u07CA-\u07EA\u0840-\u0855\u0860\u0862-\u0865\u0867-\u086A\u08A0-\u08AC\u08AE-\u08B4\u08B6-\u08BD\u1807\u1820-\u1877\u1887-\u18A8\u18AA\uA840-\uA871\u{10AC0}-\u{10AC5}\u{10AC7}\u{10AC9}\u{10ACA}\u{10ACE}-\u{10AD6}\u{10AD8}-\u{10AE1}\u{10AE4}\u{10AEB}-\u{10AEF}\u{10B80}-\u{10B91}\u{10BA9}-\u{10BAE}\u{1E900}-\u{1E943}]/u;
+const bidiDomain = /[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05EA\u05F0-\u05F4\u0600-\u0605\u0608\u060B\u060D\u061B\u061C\u061E-\u064A\u0660-\u0669\u066B-\u066F\u0671-\u06D5\u06DD\u06E5\u06E6\u06EE\u06EF\u06FA-\u070D\u070F\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0830-\u083E\u0840-\u0858\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08E2\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFC\uFE70-\uFE74\uFE76-\uFEFC\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{10920}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A00}\u{10A10}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A40}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE4}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B40}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{10E60}-\u{10E7E}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8CF}\u{1E900}-\u{1E943}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}]/u;
+const bidiS1LTR = /[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02BB-\u02C1\u02D0\u02D1\u02E0-\u02E4\u02EE\u0370-\u0373\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0482\u048A-\u052F\u0531-\u0556\u0559-\u055F\u0561-\u0587\u0589\u0903-\u0939\u093B\u093D-\u0940\u0949-\u094C\u094E-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD-\u09C0\u09C7\u09C8\u09CB\u09CC\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09FA\u09FC\u09FD\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3E-\u0A40\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD-\u0AC0\u0AC9\u0ACB\u0ACC\u0AD0\u0AE0\u0AE1\u0AE6-\u0AF0\u0AF9\u0B02\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B3E\u0B40\u0B47\u0B48\u0B4B\u0B4C\u0B57\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE\u0BBF\u0BC1\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BD0\u0BD7\u0BE6-\u0BF2\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C41-\u0C44\u0C58-\u0C5A\u0C60\u0C61\u0C66-\u0C6F\u0C7F\u0C80\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD-\u0CC4\u0CC6-\u0CC8\u0CCA\u0CCB\u0CD5\u0CD6\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D4E\u0D4F\u0D54-\u0D61\u0D66-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCF-\u0DD1\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E4F-\u0E5B\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00-\u0F17\u0F1A-\u0F34\u0F36\u0F38\u0F3E-\u0F47\u0F49-\u0F6C\u0F7F\u0F85\u0F88-\u0F8C\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE-\u0FDA\u1000-\u102C\u1031\u1038\u103B\u103C\u103F-\u1057\u105A-\u105D\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108C\u108E-\u109C\u109E-\u10C5\u10C7\u10CD\u10D0-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1360-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u167F\u1681-\u169A\u16A0-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17B6\u17BE-\u17C5\u17C7\u17C8\u17D4-\u17DA\u17DC\u17E0-\u17E9\u1810-\u1819\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1923-\u1926\u1929-\u192B\u1930\u1931\u1933-\u1938\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A19\u1A1A\u1A1E-\u1A55\u1A57\u1A61\u1A63\u1A64\u1A6D-\u1A72\u1A80-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD\u1B04-\u1B33\u1B35\u1B3B\u1B3D-\u1B41\u1B43-\u1B4B\u1B50-\u1B6A\u1B74-\u1B7C\u1B82-\u1BA1\u1BA6\u1BA7\u1BAA\u1BAE-\u1BE5\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2\u1BF3\u1BFC-\u1C2B\u1C34\u1C35\u1C3B-\u1C49\u1C4D-\u1C88\u1CC0-\u1CC7\u1CD3\u1CE1\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5-\u1CF7\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200E\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u214F\u2160-\u2188\u2336-\u237A\u2395\u249C-\u24E9\u26AC\u2800-\u28FF\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D70\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u302E\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u3190-\u31BA\u31F0-\u321C\u3220-\u324F\u3260-\u327B\u327F-\u32B0\u32C0-\u32CB\u32D0-\u32FE\u3300-\u3376\u337B-\u33DD\u33E0-\u33FE\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA60C\uA610-\uA62B\uA640-\uA66E\uA680-\uA69D\uA6A0-\uA6EF\uA6F2-\uA6F7\uA722-\uA787\uA789-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA824\uA827\uA830-\uA837\uA840-\uA873\uA880-\uA8C3\uA8CE-\uA8D9\uA8F2-\uA8FD\uA900-\uA925\uA92E-\uA946\uA952\uA953\uA95F-\uA97C\uA983-\uA9B2\uA9B4\uA9B5\uA9BA\uA9BB\uA9BD-\uA9CD\uA9CF-\uA9D9\uA9DE-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA2F\uAA30\uAA33\uAA34\uAA40-\uAA42\uAA44-\uAA4B\uAA4D\uAA50-\uAA59\uAA5C-\uAA7B\uAA7D-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAAEB\uAAEE-\uAAF5\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB65\uAB70-\uABE4\uABE6\uABE7\uABE9-\uABEC\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uD800-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\u{10000}-\u{1000B}\u{1000D}-\u{10026}\u{10028}-\u{1003A}\u{1003C}\u{1003D}\u{1003F}-\u{1004D}\u{10050}-\u{1005D}\u{10080}-\u{100FA}\u{10100}\u{10102}\u{10107}-\u{10133}\u{10137}-\u{1013F}\u{1018D}\u{1018E}\u{101D0}-\u{101FC}\u{10280}-\u{1029C}\u{102A0}-\u{102D0}\u{10300}-\u{10323}\u{1032D}-\u{1034A}\u{10350}-\u{10375}\u{10380}-\u{1039D}\u{1039F}-\u{103C3}\u{103C8}-\u{103D5}\u{10400}-\u{1049D}\u{104A0}-\u{104A9}\u{104B0}-\u{104D3}\u{104D8}-\u{104FB}\u{10500}-\u{10527}\u{10530}-\u{10563}\u{1056F}\u{10600}-\u{10736}\u{10740}-\u{10755}\u{10760}-\u{10767}\u{11000}\u{11002}-\u{11037}\u{11047}-\u{1104D}\u{11066}-\u{1106F}\u{11082}-\u{110B2}\u{110B7}\u{110B8}\u{110BB}-\u{110C1}\u{110D0}-\u{110E8}\u{110F0}-\u{110F9}\u{11103}-\u{11126}\u{1112C}\u{11136}-\u{11143}\u{11150}-\u{11172}\u{11174}-\u{11176}\u{11182}-\u{111B5}\u{111BF}-\u{111C9}\u{111CD}\u{111D0}-\u{111DF}\u{111E1}-\u{111F4}\u{11200}-\u{11211}\u{11213}-\u{1122E}\u{11232}\u{11233}\u{11235}\u{11238}-\u{1123D}\u{11280}-\u{11286}\u{11288}\u{1128A}-\u{1128D}\u{1128F}-\u{1129D}\u{1129F}-\u{112A9}\u{112B0}-\u{112DE}\u{112E0}-\u{112E2}\u{112F0}-\u{112F9}\u{11302}\u{11303}\u{11305}-\u{1130C}\u{1130F}\u{11310}\u{11313}-\u{11328}\u{1132A}-\u{11330}\u{11332}\u{11333}\u{11335}-\u{11339}\u{1133D}-\u{1133F}\u{11341}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11350}\u{11357}\u{1135D}-\u{11363}\u{11400}-\u{11437}\u{11440}\u{11441}\u{11445}\u{11447}-\u{11459}\u{1145B}\u{1145D}\u{11480}-\u{114B2}\u{114B9}\u{114BB}-\u{114BE}\u{114C1}\u{114C4}-\u{114C7}\u{114D0}-\u{114D9}\u{11580}-\u{115B1}\u{115B8}-\u{115BB}\u{115BE}\u{115C1}-\u{115DB}\u{11600}-\u{11632}\u{1163B}\u{1163C}\u{1163E}\u{11641}-\u{11644}\u{11650}-\u{11659}\u{11680}-\u{116AA}\u{116AC}\u{116AE}\u{116AF}\u{116B6}\u{116C0}-\u{116C9}\u{11700}-\u{11719}\u{11720}\u{11721}\u{11726}\u{11730}-\u{1173F}\u{118A0}-\u{118F2}\u{118FF}\u{11A00}\u{11A07}\u{11A08}\u{11A0B}-\u{11A32}\u{11A39}\u{11A3A}\u{11A3F}-\u{11A46}\u{11A50}\u{11A57}\u{11A58}\u{11A5C}-\u{11A83}\u{11A86}-\u{11A89}\u{11A97}\u{11A9A}-\u{11A9C}\u{11A9E}-\u{11AA2}\u{11AC0}-\u{11AF8}\u{11C00}-\u{11C08}\u{11C0A}-\u{11C2F}\u{11C3E}-\u{11C45}\u{11C50}-\u{11C6C}\u{11C70}-\u{11C8F}\u{11CA9}\u{11CB1}\u{11CB4}\u{11D00}-\u{11D06}\u{11D08}\u{11D09}\u{11D0B}-\u{11D30}\u{11D46}\u{11D50}-\u{11D59}\u{12000}-\u{12399}\u{12400}-\u{1246E}\u{12470}-\u{12474}\u{12480}-\u{12543}\u{13000}-\u{1342E}\u{14400}-\u{14646}\u{16800}-\u{16A38}\u{16A40}-\u{16A5E}\u{16A60}-\u{16A69}\u{16A6E}\u{16A6F}\u{16AD0}-\u{16AED}\u{16AF5}\u{16B00}-\u{16B2F}\u{16B37}-\u{16B45}\u{16B50}-\u{16B59}\u{16B5B}-\u{16B61}\u{16B63}-\u{16B77}\u{16B7D}-\u{16B8F}\u{16F00}-\u{16F44}\u{16F50}-\u{16F7E}\u{16F93}-\u{16F9F}\u{16FE0}\u{16FE1}\u{17000}-\u{187EC}\u{18800}-\u{18AF2}\u{1B000}-\u{1B11E}\u{1B170}-\u{1B2FB}\u{1BC00}-\u{1BC6A}\u{1BC70}-\u{1BC7C}\u{1BC80}-\u{1BC88}\u{1BC90}-\u{1BC99}\u{1BC9C}\u{1BC9F}\u{1D000}-\u{1D0F5}\u{1D100}-\u{1D126}\u{1D129}-\u{1D166}\u{1D16A}-\u{1D172}\u{1D183}\u{1D184}\u{1D18C}-\u{1D1A9}\u{1D1AE}-\u{1D1E8}\u{1D360}-\u{1D371}\u{1D400}-\u{1D454}\u{1D456}-\u{1D49C}\u{1D49E}\u{1D49F}\u{1D4A2}\u{1D4A5}\u{1D4A6}\u{1D4A9}-\u{1D4AC}\u{1D4AE}-\u{1D4B9}\u{1D4BB}\u{1D4BD}-\u{1D4C3}\u{1D4C5}-\u{1D505}\u{1D507}-\u{1D50A}\u{1D50D}-\u{1D514}\u{1D516}-\u{1D51C}\u{1D51E}-\u{1D539}\u{1D53B}-\u{1D53E}\u{1D540}-\u{1D544}\u{1D546}\u{1D54A}-\u{1D550}\u{1D552}-\u{1D6A5}\u{1D6A8}-\u{1D6DA}\u{1D6DC}-\u{1D714}\u{1D716}-\u{1D74E}\u{1D750}-\u{1D788}\u{1D78A}-\u{1D7C2}\u{1D7C4}-\u{1D7CB}\u{1D800}-\u{1D9FF}\u{1DA37}-\u{1DA3A}\u{1DA6D}-\u{1DA74}\u{1DA76}-\u{1DA83}\u{1DA85}-\u{1DA8B}\u{1F110}-\u{1F12E}\u{1F130}-\u{1F169}\u{1F170}-\u{1F1AC}\u{1F1E6}-\u{1F202}\u{1F210}-\u{1F23B}\u{1F240}-\u{1F248}\u{1F250}\u{1F251}\u{20000}-\u{2A6D6}\u{2A700}-\u{2B734}\u{2B740}-\u{2B81D}\u{2B820}-\u{2CEA1}\u{2CEB0}-\u{2EBE0}\u{2F800}-\u{2FA1D}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]/u;
+const bidiS1RTL = /[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05EA\u05F0-\u05F4\u0608\u060B\u060D\u061B\u061C\u061E-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u070D\u070F\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0830-\u083E\u0840-\u0858\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFC\uFE70-\uFE74\uFE76-\uFEFC\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{10920}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A00}\u{10A10}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A40}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE4}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B40}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8CF}\u{1E900}-\u{1E943}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}]/u;
+const bidiS2 = /^[\0-\x08\x0E-\x1B!-@\[-`\{-\x84\x86-\xA9\xAB-\xB4\xB6-\xB9\xBB-\xBF\xD7\xF7\u02B9\u02BA\u02C2-\u02CF\u02D2-\u02DF\u02E5-\u02ED\u02EF-\u036F\u0374\u0375\u037E\u0384\u0385\u0387\u03F6\u0483-\u0489\u058A\u058D-\u058F\u0591-\u05C7\u05D0-\u05EA\u05F0-\u05F4\u0600-\u061C\u061E-\u070D\u070F-\u074A\u074D-\u07B1\u07C0-\u07FA\u0800-\u082D\u0830-\u083E\u0840-\u085B\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u09F2\u09F3\u09FB\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AF1\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0BF3-\u0BFA\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C78-\u0C7E\u0C81\u0CBC\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E3F\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39-\u0F3D\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1390-\u1399\u1400\u169B\u169C\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DB\u17DD\u17F0-\u17F9\u1800-\u180E\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1940\u1944\u1945\u19DE-\u19FF\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u200B-\u200D\u200F-\u2027\u202F-\u205E\u2060-\u2064\u206A-\u2070\u2074-\u207E\u2080-\u208E\u20A0-\u20BF\u20D0-\u20F0\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u2150-\u215F\u2189-\u218B\u2190-\u2335\u237B-\u2394\u2396-\u2426\u2440-\u244A\u2460-\u249B\u24EA-\u26AB\u26AD-\u27FF\u2900-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD2\u2BEC-\u2BEF\u2CE5-\u2CEA\u2CEF-\u2CF1\u2CF9-\u2CFF\u2D7F\u2DE0-\u2E49\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u3004\u3008-\u3020\u302A-\u302D\u3030\u3036\u3037\u303D-\u303F\u3099-\u309C\u30A0\u30FB\u31C0-\u31E3\u321D\u321E\u3250-\u325F\u327C-\u327E\u32B1-\u32BF\u32CC-\u32CF\u3377-\u337A\u33DE\u33DF\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA60D-\uA60F\uA66F-\uA67F\uA69E\uA69F\uA6F0\uA6F1\uA700-\uA721\uA788\uA802\uA806\uA80B\uA825\uA826\uA828-\uA82B\uA838\uA839\uA874-\uA877\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1D-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3F\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFD\uFE00-\uFE19\uFE20-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFE70-\uFE74\uFE76-\uFEFC\uFEFF\uFF01-\uFF20\uFF3B-\uFF40\uFF5B-\uFF65\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFF9-\uFFFD\u{10101}\u{10140}-\u{1018C}\u{10190}-\u{1019B}\u{101A0}\u{101FD}\u{102E0}-\u{102FB}\u{10376}-\u{1037A}\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{1091F}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A38}-\u{10A3A}\u{10A3F}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE6}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B39}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{10E60}-\u{10E7E}\u{11001}\u{11038}-\u{11046}\u{11052}-\u{11065}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{11660}-\u{1166C}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1BCA0}-\u{1BCA3}\u{1D167}-\u{1D169}\u{1D173}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D200}-\u{1D245}\u{1D300}-\u{1D356}\u{1D6DB}\u{1D715}\u{1D74F}\u{1D789}\u{1D7C3}\u{1D7CE}-\u{1D7FF}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8D6}\u{1E900}-\u{1E94A}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}\u{1EEF0}\u{1EEF1}\u{1F000}-\u{1F02B}\u{1F030}-\u{1F093}\u{1F0A0}-\u{1F0AE}\u{1F0B1}-\u{1F0BF}\u{1F0C1}-\u{1F0CF}\u{1F0D1}-\u{1F0F5}\u{1F100}-\u{1F10C}\u{1F16A}\u{1F16B}\u{1F260}-\u{1F265}\u{1F300}-\u{1F6D4}\u{1F6E0}-\u{1F6EC}\u{1F6F0}-\u{1F6F8}\u{1F700}-\u{1F773}\u{1F780}-\u{1F7D4}\u{1F800}-\u{1F80B}\u{1F810}-\u{1F847}\u{1F850}-\u{1F859}\u{1F860}-\u{1F887}\u{1F890}-\u{1F8AD}\u{1F900}-\u{1F90B}\u{1F910}-\u{1F93E}\u{1F940}-\u{1F94C}\u{1F950}-\u{1F96B}\u{1F980}-\u{1F997}\u{1F9C0}\u{1F9D0}-\u{1F9E6}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}]*$/u;
+const bidiS3 = /[0-9\xB2\xB3\xB9\u05BE\u05C0\u05C3\u05C6\u05D0-\u05EA\u05F0-\u05F4\u0600-\u0605\u0608\u060B\u060D\u061B\u061C\u061E-\u064A\u0660-\u0669\u066B-\u066F\u0671-\u06D5\u06DD\u06E5\u06E6\u06EE-\u070D\u070F\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07C0-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0830-\u083E\u0840-\u0858\u085E\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08E2\u200F\u2070\u2074-\u2079\u2080-\u2089\u2488-\u249B\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBC1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFC\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\u{102E1}-\u{102FB}\u{10800}-\u{10805}\u{10808}\u{1080A}-\u{10835}\u{10837}\u{10838}\u{1083C}\u{1083F}-\u{10855}\u{10857}-\u{1089E}\u{108A7}-\u{108AF}\u{108E0}-\u{108F2}\u{108F4}\u{108F5}\u{108FB}-\u{1091B}\u{10920}-\u{10939}\u{1093F}\u{10980}-\u{109B7}\u{109BC}-\u{109CF}\u{109D2}-\u{10A00}\u{10A10}-\u{10A13}\u{10A15}-\u{10A17}\u{10A19}-\u{10A33}\u{10A40}-\u{10A47}\u{10A50}-\u{10A58}\u{10A60}-\u{10A9F}\u{10AC0}-\u{10AE4}\u{10AEB}-\u{10AF6}\u{10B00}-\u{10B35}\u{10B40}-\u{10B55}\u{10B58}-\u{10B72}\u{10B78}-\u{10B91}\u{10B99}-\u{10B9C}\u{10BA9}-\u{10BAF}\u{10C00}-\u{10C48}\u{10C80}-\u{10CB2}\u{10CC0}-\u{10CF2}\u{10CFA}-\u{10CFF}\u{10E60}-\u{10E7E}\u{1D7CE}-\u{1D7FF}\u{1E800}-\u{1E8C4}\u{1E8C7}-\u{1E8CF}\u{1E900}-\u{1E943}\u{1E950}-\u{1E959}\u{1E95E}\u{1E95F}\u{1EE00}-\u{1EE03}\u{1EE05}-\u{1EE1F}\u{1EE21}\u{1EE22}\u{1EE24}\u{1EE27}\u{1EE29}-\u{1EE32}\u{1EE34}-\u{1EE37}\u{1EE39}\u{1EE3B}\u{1EE42}\u{1EE47}\u{1EE49}\u{1EE4B}\u{1EE4D}-\u{1EE4F}\u{1EE51}\u{1EE52}\u{1EE54}\u{1EE57}\u{1EE59}\u{1EE5B}\u{1EE5D}\u{1EE5F}\u{1EE61}\u{1EE62}\u{1EE64}\u{1EE67}-\u{1EE6A}\u{1EE6C}-\u{1EE72}\u{1EE74}-\u{1EE77}\u{1EE79}-\u{1EE7C}\u{1EE7E}\u{1EE80}-\u{1EE89}\u{1EE8B}-\u{1EE9B}\u{1EEA1}-\u{1EEA3}\u{1EEA5}-\u{1EEA9}\u{1EEAB}-\u{1EEBB}\u{1F100}-\u{1F10A}][\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1D167}-\u{1D169}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0100}-\u{E01EF}]*$/u;
+const bidiS4EN = /[0-9\xB2\xB3\xB9\u06F0-\u06F9\u2070\u2074-\u2079\u2080-\u2089\u2488-\u249B\uFF10-\uFF19\u{102E1}-\u{102FB}\u{1D7CE}-\u{1D7FF}\u{1F100}-\u{1F10A}]/u;
+const bidiS4AN = /[\u0600-\u0605\u0660-\u0669\u066B\u066C\u06DD\u08E2\u{10E60}-\u{10E7E}]/u;
+const bidiS5 = /^[\0-\x08\x0E-\x1B!-\x84\x86-\u0377\u037A-\u037F\u0384-\u038A\u038C\u038E-\u03A1\u03A3-\u052F\u0531-\u0556\u0559-\u055F\u0561-\u0587\u0589\u058A\u058D-\u058F\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0606\u0607\u0609\u060A\u060C\u060E-\u061A\u064B-\u065F\u066A\u0670\u06D6-\u06DC\u06DE-\u06E4\u06E7-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u07F6-\u07F9\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09FD\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AF1\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B77\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BFA\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C78-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4F\u0D54-\u0D63\u0D66-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4\u0E01-\u0E3A\u0E3F-\u0E5B\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00-\u0F47\u0F49-\u0F6C\u0F71-\u0F97\u0F99-\u0FBC\u0FBE-\u0FCC\u0FCE-\u0FDA\u1000-\u10C5\u10C7\u10CD\u10D0-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u137C\u1380-\u1399\u13A0-\u13F5\u13F8-\u13FD\u1400-\u167F\u1681-\u169C\u16A0-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1736\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17DD\u17E0-\u17E9\u17F0-\u17F9\u1800-\u180E\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1940\u1944-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u19DE-\u1A1B\u1A1E-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD\u1AB0-\u1ABE\u1B00-\u1B4B\u1B50-\u1B7C\u1B80-\u1BF3\u1BFC-\u1C37\u1C3B-\u1C49\u1C4D-\u1C88\u1CC0-\u1CC7\u1CD0-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FC4\u1FC6-\u1FD3\u1FD6-\u1FDB\u1FDD-\u1FEF\u1FF2-\u1FF4\u1FF6-\u1FFE\u200B-\u200E\u2010-\u2027\u202F-\u205E\u2060-\u2064\u206A-\u2071\u2074-\u208E\u2090-\u209C\u20A0-\u20BF\u20D0-\u20F0\u2100-\u218B\u2190-\u2426\u2440-\u244A\u2460-\u2B73\u2B76-\u2B95\u2B98-\u2BB9\u2BBD-\u2BC8\u2BCA-\u2BD2\u2BEC-\u2BEF\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CF3\u2CF9-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D70\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2E49\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3001-\u303F\u3041-\u3096\u3099-\u30FF\u3105-\u312E\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u32FE\u3300-\u4DB5\u4DC0-\u9FEA\uA000-\uA48C\uA490-\uA4C6\uA4D0-\uA62B\uA640-\uA6F7\uA700-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA82B\uA830-\uA839\uA840-\uA877\uA880-\uA8C5\uA8CE-\uA8D9\uA8E0-\uA8FD\uA900-\uA953\uA95F-\uA97C\uA980-\uA9CD\uA9CF-\uA9D9\uA9DE-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA5C-\uAAC2\uAADB-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB65\uAB70-\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uD800-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1E\uFB29\uFD3E\uFD3F\uFDFD\uFE00-\uFE19\uFE20-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFEFF\uFF01-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFF9-\uFFFD\u{10000}-\u{1000B}\u{1000D}-\u{10026}\u{10028}-\u{1003A}\u{1003C}\u{1003D}\u{1003F}-\u{1004D}\u{10050}-\u{1005D}\u{10080}-\u{100FA}\u{10100}-\u{10102}\u{10107}-\u{10133}\u{10137}-\u{1018E}\u{10190}-\u{1019B}\u{101A0}\u{101D0}-\u{101FD}\u{10280}-\u{1029C}\u{102A0}-\u{102D0}\u{102E0}-\u{102FB}\u{10300}-\u{10323}\u{1032D}-\u{1034A}\u{10350}-\u{1037A}\u{10380}-\u{1039D}\u{1039F}-\u{103C3}\u{103C8}-\u{103D5}\u{10400}-\u{1049D}\u{104A0}-\u{104A9}\u{104B0}-\u{104D3}\u{104D8}-\u{104FB}\u{10500}-\u{10527}\u{10530}-\u{10563}\u{1056F}\u{10600}-\u{10736}\u{10740}-\u{10755}\u{10760}-\u{10767}\u{1091F}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{10B39}-\u{10B3F}\u{11000}-\u{1104D}\u{11052}-\u{1106F}\u{1107F}-\u{110C1}\u{110D0}-\u{110E8}\u{110F0}-\u{110F9}\u{11100}-\u{11134}\u{11136}-\u{11143}\u{11150}-\u{11176}\u{11180}-\u{111CD}\u{111D0}-\u{111DF}\u{111E1}-\u{111F4}\u{11200}-\u{11211}\u{11213}-\u{1123E}\u{11280}-\u{11286}\u{11288}\u{1128A}-\u{1128D}\u{1128F}-\u{1129D}\u{1129F}-\u{112A9}\u{112B0}-\u{112EA}\u{112F0}-\u{112F9}\u{11300}-\u{11303}\u{11305}-\u{1130C}\u{1130F}\u{11310}\u{11313}-\u{11328}\u{1132A}-\u{11330}\u{11332}\u{11333}\u{11335}-\u{11339}\u{1133C}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11350}\u{11357}\u{1135D}-\u{11363}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11400}-\u{11459}\u{1145B}\u{1145D}\u{11480}-\u{114C7}\u{114D0}-\u{114D9}\u{11580}-\u{115B5}\u{115B8}-\u{115DD}\u{11600}-\u{11644}\u{11650}-\u{11659}\u{11660}-\u{1166C}\u{11680}-\u{116B7}\u{116C0}-\u{116C9}\u{11700}-\u{11719}\u{1171D}-\u{1172B}\u{11730}-\u{1173F}\u{118A0}-\u{118F2}\u{118FF}\u{11A00}-\u{11A47}\u{11A50}-\u{11A83}\u{11A86}-\u{11A9C}\u{11A9E}-\u{11AA2}\u{11AC0}-\u{11AF8}\u{11C00}-\u{11C08}\u{11C0A}-\u{11C36}\u{11C38}-\u{11C45}\u{11C50}-\u{11C6C}\u{11C70}-\u{11C8F}\u{11C92}-\u{11CA7}\u{11CA9}-\u{11CB6}\u{11D00}-\u{11D06}\u{11D08}\u{11D09}\u{11D0B}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D47}\u{11D50}-\u{11D59}\u{12000}-\u{12399}\u{12400}-\u{1246E}\u{12470}-\u{12474}\u{12480}-\u{12543}\u{13000}-\u{1342E}\u{14400}-\u{14646}\u{16800}-\u{16A38}\u{16A40}-\u{16A5E}\u{16A60}-\u{16A69}\u{16A6E}\u{16A6F}\u{16AD0}-\u{16AED}\u{16AF0}-\u{16AF5}\u{16B00}-\u{16B45}\u{16B50}-\u{16B59}\u{16B5B}-\u{16B61}\u{16B63}-\u{16B77}\u{16B7D}-\u{16B8F}\u{16F00}-\u{16F44}\u{16F50}-\u{16F7E}\u{16F8F}-\u{16F9F}\u{16FE0}\u{16FE1}\u{17000}-\u{187EC}\u{18800}-\u{18AF2}\u{1B000}-\u{1B11E}\u{1B170}-\u{1B2FB}\u{1BC00}-\u{1BC6A}\u{1BC70}-\u{1BC7C}\u{1BC80}-\u{1BC88}\u{1BC90}-\u{1BC99}\u{1BC9C}-\u{1BCA3}\u{1D000}-\u{1D0F5}\u{1D100}-\u{1D126}\u{1D129}-\u{1D1E8}\u{1D200}-\u{1D245}\u{1D300}-\u{1D356}\u{1D360}-\u{1D371}\u{1D400}-\u{1D454}\u{1D456}-\u{1D49C}\u{1D49E}\u{1D49F}\u{1D4A2}\u{1D4A5}\u{1D4A6}\u{1D4A9}-\u{1D4AC}\u{1D4AE}-\u{1D4B9}\u{1D4BB}\u{1D4BD}-\u{1D4C3}\u{1D4C5}-\u{1D505}\u{1D507}-\u{1D50A}\u{1D50D}-\u{1D514}\u{1D516}-\u{1D51C}\u{1D51E}-\u{1D539}\u{1D53B}-\u{1D53E}\u{1D540}-\u{1D544}\u{1D546}\u{1D54A}-\u{1D550}\u{1D552}-\u{1D6A5}\u{1D6A8}-\u{1D7CB}\u{1D7CE}-\u{1DA8B}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{1EEF0}\u{1EEF1}\u{1F000}-\u{1F02B}\u{1F030}-\u{1F093}\u{1F0A0}-\u{1F0AE}\u{1F0B1}-\u{1F0BF}\u{1F0C1}-\u{1F0CF}\u{1F0D1}-\u{1F0F5}\u{1F100}-\u{1F10C}\u{1F110}-\u{1F12E}\u{1F130}-\u{1F16B}\u{1F170}-\u{1F1AC}\u{1F1E6}-\u{1F202}\u{1F210}-\u{1F23B}\u{1F240}-\u{1F248}\u{1F250}\u{1F251}\u{1F260}-\u{1F265}\u{1F300}-\u{1F6D4}\u{1F6E0}-\u{1F6EC}\u{1F6F0}-\u{1F6F8}\u{1F700}-\u{1F773}\u{1F780}-\u{1F7D4}\u{1F800}-\u{1F80B}\u{1F810}-\u{1F847}\u{1F850}-\u{1F859}\u{1F860}-\u{1F887}\u{1F890}-\u{1F8AD}\u{1F900}-\u{1F90B}\u{1F910}-\u{1F93E}\u{1F940}-\u{1F94C}\u{1F950}-\u{1F96B}\u{1F980}-\u{1F997}\u{1F9C0}\u{1F9D0}-\u{1F9E6}\u{20000}-\u{2A6D6}\u{2A700}-\u{2B734}\u{2B740}-\u{2B81D}\u{2B820}-\u{2CEA1}\u{2CEB0}-\u{2EBE0}\u{2F800}-\u{2FA1D}\u{E0001}\u{E0020}-\u{E007F}\u{E0100}-\u{E01EF}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}]*$/u;
+const bidiS6 = /[0-9A-Za-z\xAA\xB2\xB3\xB5\xB9\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02B8\u02BB-\u02C1\u02D0\u02D1\u02E0-\u02E4\u02EE\u0370-\u0373\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0482\u048A-\u052F\u0531-\u0556\u0559-\u055F\u0561-\u0587\u0589\u06F0-\u06F9\u0903-\u0939\u093B\u093D-\u0940\u0949-\u094C\u094E-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD-\u09C0\u09C7\u09C8\u09CB\u09CC\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E1\u09E6-\u09F1\u09F4-\u09FA\u09FC\u09FD\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3E-\u0A40\u0A59-\u0A5C\u0A5E\u0A66-\u0A6F\u0A72-\u0A74\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD-\u0AC0\u0AC9\u0ACB\u0ACC\u0AD0\u0AE0\u0AE1\u0AE6-\u0AF0\u0AF9\u0B02\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B3E\u0B40\u0B47\u0B48\u0B4B\u0B4C\u0B57\u0B5C\u0B5D\u0B5F-\u0B61\u0B66-\u0B77\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE\u0BBF\u0BC1\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCC\u0BD0\u0BD7\u0BE6-\u0BF2\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C41-\u0C44\u0C58-\u0C5A\u0C60\u0C61\u0C66-\u0C6F\u0C7F\u0C80\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD-\u0CC4\u0CC6-\u0CC8\u0CCA\u0CCB\u0CD5\u0CD6\u0CDE\u0CE0\u0CE1\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D40\u0D46-\u0D48\u0D4A-\u0D4C\u0D4E\u0D4F\u0D54-\u0D61\u0D66-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCF-\u0DD1\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2-\u0DF4\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E4F-\u0E5B\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00-\u0F17\u0F1A-\u0F34\u0F36\u0F38\u0F3E-\u0F47\u0F49-\u0F6C\u0F7F\u0F85\u0F88-\u0F8C\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE-\u0FDA\u1000-\u102C\u1031\u1038\u103B\u103C\u103F-\u1057\u105A-\u105D\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108C\u108E-\u109C\u109E-\u10C5\u10C7\u10CD\u10D0-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1360-\u137C\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u167F\u1681-\u169A\u16A0-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17B6\u17BE-\u17C5\u17C7\u17C8\u17D4-\u17DA\u17DC\u17E0-\u17E9\u1810-\u1819\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1923-\u1926\u1929-\u192B\u1930\u1931\u1933-\u1938\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19DA\u1A00-\u1A16\u1A19\u1A1A\u1A1E-\u1A55\u1A57\u1A61\u1A63\u1A64\u1A6D-\u1A72\u1A80-\u1A89\u1A90-\u1A99\u1AA0-\u1AAD\u1B04-\u1B33\u1B35\u1B3B\u1B3D-\u1B41\u1B43-\u1B4B\u1B50-\u1B6A\u1B74-\u1B7C\u1B82-\u1BA1\u1BA6\u1BA7\u1BAA\u1BAE-\u1BE5\u1BE7\u1BEA-\u1BEC\u1BEE\u1BF2\u1BF3\u1BFC-\u1C2B\u1C34\u1C35\u1C3B-\u1C49\u1C4D-\u1C88\u1CC0-\u1CC7\u1CD3\u1CE1\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5-\u1CF7\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200E\u2070\u2071\u2074-\u2079\u207F-\u2089\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u214F\u2160-\u2188\u2336-\u237A\u2395\u2488-\u24E9\u26AC\u2800-\u28FF\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D70\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u3005-\u3007\u3021-\u3029\u302E\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u3190-\u31BA\u31F0-\u321C\u3220-\u324F\u3260-\u327B\u327F-\u32B0\u32C0-\u32CB\u32D0-\u32FE\u3300-\u3376\u337B-\u33DD\u33E0-\u33FE\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA60C\uA610-\uA62B\uA640-\uA66E\uA680-\uA69D\uA6A0-\uA6EF\uA6F2-\uA6F7\uA722-\uA787\uA789-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA824\uA827\uA830-\uA837\uA840-\uA873\uA880-\uA8C3\uA8CE-\uA8D9\uA8F2-\uA8FD\uA900-\uA925\uA92E-\uA946\uA952\uA953\uA95F-\uA97C\uA983-\uA9B2\uA9B4\uA9B5\uA9BA\uA9BB\uA9BD-\uA9CD\uA9CF-\uA9D9\uA9DE-\uA9E4\uA9E6-\uA9FE\uAA00-\uAA28\uAA2F\uAA30\uAA33\uAA34\uAA40-\uAA42\uAA44-\uAA4B\uAA4D\uAA50-\uAA59\uAA5C-\uAA7B\uAA7D-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAAEB\uAAEE-\uAAF5\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB65\uAB70-\uABE4\uABE6\uABE7\uABE9-\uABEC\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uD800-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\u{10000}-\u{1000B}\u{1000D}-\u{10026}\u{10028}-\u{1003A}\u{1003C}\u{1003D}\u{1003F}-\u{1004D}\u{10050}-\u{1005D}\u{10080}-\u{100FA}\u{10100}\u{10102}\u{10107}-\u{10133}\u{10137}-\u{1013F}\u{1018D}\u{1018E}\u{101D0}-\u{101FC}\u{10280}-\u{1029C}\u{102A0}-\u{102D0}\u{102E1}-\u{102FB}\u{10300}-\u{10323}\u{1032D}-\u{1034A}\u{10350}-\u{10375}\u{10380}-\u{1039D}\u{1039F}-\u{103C3}\u{103C8}-\u{103D5}\u{10400}-\u{1049D}\u{104A0}-\u{104A9}\u{104B0}-\u{104D3}\u{104D8}-\u{104FB}\u{10500}-\u{10527}\u{10530}-\u{10563}\u{1056F}\u{10600}-\u{10736}\u{10740}-\u{10755}\u{10760}-\u{10767}\u{11000}\u{11002}-\u{11037}\u{11047}-\u{1104D}\u{11066}-\u{1106F}\u{11082}-\u{110B2}\u{110B7}\u{110B8}\u{110BB}-\u{110C1}\u{110D0}-\u{110E8}\u{110F0}-\u{110F9}\u{11103}-\u{11126}\u{1112C}\u{11136}-\u{11143}\u{11150}-\u{11172}\u{11174}-\u{11176}\u{11182}-\u{111B5}\u{111BF}-\u{111C9}\u{111CD}\u{111D0}-\u{111DF}\u{111E1}-\u{111F4}\u{11200}-\u{11211}\u{11213}-\u{1122E}\u{11232}\u{11233}\u{11235}\u{11238}-\u{1123D}\u{11280}-\u{11286}\u{11288}\u{1128A}-\u{1128D}\u{1128F}-\u{1129D}\u{1129F}-\u{112A9}\u{112B0}-\u{112DE}\u{112E0}-\u{112E2}\u{112F0}-\u{112F9}\u{11302}\u{11303}\u{11305}-\u{1130C}\u{1130F}\u{11310}\u{11313}-\u{11328}\u{1132A}-\u{11330}\u{11332}\u{11333}\u{11335}-\u{11339}\u{1133D}-\u{1133F}\u{11341}-\u{11344}\u{11347}\u{11348}\u{1134B}-\u{1134D}\u{11350}\u{11357}\u{1135D}-\u{11363}\u{11400}-\u{11437}\u{11440}\u{11441}\u{11445}\u{11447}-\u{11459}\u{1145B}\u{1145D}\u{11480}-\u{114B2}\u{114B9}\u{114BB}-\u{114BE}\u{114C1}\u{114C4}-\u{114C7}\u{114D0}-\u{114D9}\u{11580}-\u{115B1}\u{115B8}-\u{115BB}\u{115BE}\u{115C1}-\u{115DB}\u{11600}-\u{11632}\u{1163B}\u{1163C}\u{1163E}\u{11641}-\u{11644}\u{11650}-\u{11659}\u{11680}-\u{116AA}\u{116AC}\u{116AE}\u{116AF}\u{116B6}\u{116C0}-\u{116C9}\u{11700}-\u{11719}\u{11720}\u{11721}\u{11726}\u{11730}-\u{1173F}\u{118A0}-\u{118F2}\u{118FF}\u{11A00}\u{11A07}\u{11A08}\u{11A0B}-\u{11A32}\u{11A39}\u{11A3A}\u{11A3F}-\u{11A46}\u{11A50}\u{11A57}\u{11A58}\u{11A5C}-\u{11A83}\u{11A86}-\u{11A89}\u{11A97}\u{11A9A}-\u{11A9C}\u{11A9E}-\u{11AA2}\u{11AC0}-\u{11AF8}\u{11C00}-\u{11C08}\u{11C0A}-\u{11C2F}\u{11C3E}-\u{11C45}\u{11C50}-\u{11C6C}\u{11C70}-\u{11C8F}\u{11CA9}\u{11CB1}\u{11CB4}\u{11D00}-\u{11D06}\u{11D08}\u{11D09}\u{11D0B}-\u{11D30}\u{11D46}\u{11D50}-\u{11D59}\u{12000}-\u{12399}\u{12400}-\u{1246E}\u{12470}-\u{12474}\u{12480}-\u{12543}\u{13000}-\u{1342E}\u{14400}-\u{14646}\u{16800}-\u{16A38}\u{16A40}-\u{16A5E}\u{16A60}-\u{16A69}\u{16A6E}\u{16A6F}\u{16AD0}-\u{16AED}\u{16AF5}\u{16B00}-\u{16B2F}\u{16B37}-\u{16B45}\u{16B50}-\u{16B59}\u{16B5B}-\u{16B61}\u{16B63}-\u{16B77}\u{16B7D}-\u{16B8F}\u{16F00}-\u{16F44}\u{16F50}-\u{16F7E}\u{16F93}-\u{16F9F}\u{16FE0}\u{16FE1}\u{17000}-\u{187EC}\u{18800}-\u{18AF2}\u{1B000}-\u{1B11E}\u{1B170}-\u{1B2FB}\u{1BC00}-\u{1BC6A}\u{1BC70}-\u{1BC7C}\u{1BC80}-\u{1BC88}\u{1BC90}-\u{1BC99}\u{1BC9C}\u{1BC9F}\u{1D000}-\u{1D0F5}\u{1D100}-\u{1D126}\u{1D129}-\u{1D166}\u{1D16A}-\u{1D172}\u{1D183}\u{1D184}\u{1D18C}-\u{1D1A9}\u{1D1AE}-\u{1D1E8}\u{1D360}-\u{1D371}\u{1D400}-\u{1D454}\u{1D456}-\u{1D49C}\u{1D49E}\u{1D49F}\u{1D4A2}\u{1D4A5}\u{1D4A6}\u{1D4A9}-\u{1D4AC}\u{1D4AE}-\u{1D4B9}\u{1D4BB}\u{1D4BD}-\u{1D4C3}\u{1D4C5}-\u{1D505}\u{1D507}-\u{1D50A}\u{1D50D}-\u{1D514}\u{1D516}-\u{1D51C}\u{1D51E}-\u{1D539}\u{1D53B}-\u{1D53E}\u{1D540}-\u{1D544}\u{1D546}\u{1D54A}-\u{1D550}\u{1D552}-\u{1D6A5}\u{1D6A8}-\u{1D6DA}\u{1D6DC}-\u{1D714}\u{1D716}-\u{1D74E}\u{1D750}-\u{1D788}\u{1D78A}-\u{1D7C2}\u{1D7C4}-\u{1D7CB}\u{1D7CE}-\u{1D9FF}\u{1DA37}-\u{1DA3A}\u{1DA6D}-\u{1DA74}\u{1DA76}-\u{1DA83}\u{1DA85}-\u{1DA8B}\u{1F100}-\u{1F10A}\u{1F110}-\u{1F12E}\u{1F130}-\u{1F169}\u{1F170}-\u{1F1AC}\u{1F1E6}-\u{1F202}\u{1F210}-\u{1F23B}\u{1F240}-\u{1F248}\u{1F250}\u{1F251}\u{20000}-\u{2A6D6}\u{2A700}-\u{2B734}\u{2B740}-\u{2B81D}\u{2B820}-\u{2CEA1}\u{2CEB0}-\u{2EBE0}\u{2F800}-\u{2FA1D}\u{F0000}-\u{FFFFD}\u{100000}-\u{10FFFD}][\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0902\u093A\u093C\u0941-\u0948\u094D\u0951-\u0957\u0962\u0963\u0981\u09BC\u09C1-\u09C4\u09CD\u09E2\u09E3\u0A01\u0A02\u0A3C\u0A41\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81\u0A82\u0ABC\u0AC1-\u0AC5\u0AC7\u0AC8\u0ACD\u0AE2\u0AE3\u0AFA-\u0AFF\u0B01\u0B3C\u0B3F\u0B41-\u0B44\u0B4D\u0B56\u0B62\u0B63\u0B82\u0BC0\u0BCD\u0C00\u0C3E-\u0C40\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81\u0CBC\u0CCC\u0CCD\u0CE2\u0CE3\u0D00\u0D01\u0D3B\u0D3C\u0D41-\u0D44\u0D4D\u0D62\u0D63\u0DCA\u0DD2-\u0DD4\u0DD6\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F71-\u0F7E\u0F80-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102D-\u1030\u1032-\u1037\u1039\u103A\u103D\u103E\u1058\u1059\u105E-\u1060\u1071-\u1074\u1082\u1085\u1086\u108D\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4\u17B5\u17B7-\u17BD\u17C6\u17C9-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193B\u1A17\u1A18\u1A1B\u1A56\u1A58-\u1A5E\u1A60\u1A62\u1A65-\u1A6C\u1A73-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B03\u1B34\u1B36-\u1B3A\u1B3C\u1B42\u1B6B-\u1B73\u1B80\u1B81\u1BA2-\u1BA5\u1BA8\u1BA9\u1BAB-\u1BAD\u1BE6\u1BE8\u1BE9\u1BED\u1BEF-\u1BF1\u1C2C-\u1C33\u1C36\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE0\u1CE2-\u1CE8\u1CED\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF9\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302D\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA825\uA826\uA8C4\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA951\uA980-\uA982\uA9B3\uA9B6-\uA9B9\uA9BC\uA9E5\uAA29-\uAA2E\uAA31\uAA32\uAA35\uAA36\uAA43\uAA4C\uAA7C\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEC\uAAED\uAAF6\uABE5\uABE8\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F\u{101FD}\u{102E0}\u{10376}-\u{1037A}\u{10A01}-\u{10A03}\u{10A05}\u{10A06}\u{10A0C}-\u{10A0F}\u{10A38}-\u{10A3A}\u{10A3F}\u{10AE5}\u{10AE6}\u{11001}\u{11038}-\u{11046}\u{1107F}-\u{11081}\u{110B3}-\u{110B6}\u{110B9}\u{110BA}\u{11100}-\u{11102}\u{11127}-\u{1112B}\u{1112D}-\u{11134}\u{11173}\u{11180}\u{11181}\u{111B6}-\u{111BE}\u{111CA}-\u{111CC}\u{1122F}-\u{11231}\u{11234}\u{11236}\u{11237}\u{1123E}\u{112DF}\u{112E3}-\u{112EA}\u{11300}\u{11301}\u{1133C}\u{11340}\u{11366}-\u{1136C}\u{11370}-\u{11374}\u{11438}-\u{1143F}\u{11442}-\u{11444}\u{11446}\u{114B3}-\u{114B8}\u{114BA}\u{114BF}\u{114C0}\u{114C2}\u{114C3}\u{115B2}-\u{115B5}\u{115BC}\u{115BD}\u{115BF}\u{115C0}\u{115DC}\u{115DD}\u{11633}-\u{1163A}\u{1163D}\u{1163F}\u{11640}\u{116AB}\u{116AD}\u{116B0}-\u{116B5}\u{116B7}\u{1171D}-\u{1171F}\u{11722}-\u{11725}\u{11727}-\u{1172B}\u{11A01}-\u{11A06}\u{11A09}\u{11A0A}\u{11A33}-\u{11A38}\u{11A3B}-\u{11A3E}\u{11A47}\u{11A51}-\u{11A56}\u{11A59}-\u{11A5B}\u{11A8A}-\u{11A96}\u{11A98}\u{11A99}\u{11C30}-\u{11C36}\u{11C38}-\u{11C3D}\u{11C92}-\u{11CA7}\u{11CAA}-\u{11CB0}\u{11CB2}\u{11CB3}\u{11CB5}\u{11CB6}\u{11D31}-\u{11D36}\u{11D3A}\u{11D3C}\u{11D3D}\u{11D3F}-\u{11D45}\u{11D47}\u{16AF0}-\u{16AF4}\u{16B30}-\u{16B36}\u{16F8F}-\u{16F92}\u{1BC9D}\u{1BC9E}\u{1D167}-\u{1D169}\u{1D17B}-\u{1D182}\u{1D185}-\u{1D18B}\u{1D1AA}-\u{1D1AD}\u{1D242}-\u{1D244}\u{1DA00}-\u{1DA36}\u{1DA3B}-\u{1DA6C}\u{1DA75}\u{1DA84}\u{1DA9B}-\u{1DA9F}\u{1DAA1}-\u{1DAAF}\u{1E000}-\u{1E006}\u{1E008}-\u{1E018}\u{1E01B}-\u{1E021}\u{1E023}\u{1E024}\u{1E026}-\u{1E02A}\u{1E8D0}-\u{1E8D6}\u{1E944}-\u{1E94A}\u{E0100}-\u{E01EF}]*$/u;
+
+module.exports = {
+ combiningMarks,
+ combiningClassVirama,
+ validZWNJ,
+ bidiDomain,
+ bidiS1LTR,
+ bidiS1RTL,
+ bidiS2,
+ bidiS3,
+ bidiS4EN,
+ bidiS4AN,
+ bidiS5,
+ bidiS6
+};
+
+},{}],18:[function(require,module,exports){
+"use strict";
+
+function _(message, opts) {
+ return `${opts && opts.context ? opts.context : "Value"} ${message}.`;
+}
+
+function type(V) {
+ if (V === null) {
+ return "Null";
+ }
+ switch (typeof V) {
+ case "undefined":
+ return "Undefined";
+ case "boolean":
+ return "Boolean";
+ case "number":
+ return "Number";
+ case "string":
+ return "String";
+ case "symbol":
+ return "Symbol";
+ case "object":
+ // Falls through
+ case "function":
+ // Falls through
+ default:
+ // Per ES spec, typeof returns an implemention-defined value that is not any of the existing ones for
+ // uncallable non-standard exotic objects. Yet Type() which the Web IDL spec depends on returns Object for
+ // such cases. So treat the default case as an object.
+ return "Object";
+ }
+}
+
+// Round x to the nearest integer, choosing the even integer if it lies halfway between two.
+function evenRound(x) {
+ // There are four cases for numbers with fractional part being .5:
+ //
+ // case | x | floor(x) | round(x) | expected | x <> 0 | x % 1 | x & 1 | example
+ // 1 | 2n + 0.5 | 2n | 2n + 1 | 2n | > | 0.5 | 0 | 0.5 -> 0
+ // 2 | 2n + 1.5 | 2n + 1 | 2n + 2 | 2n + 2 | > | 0.5 | 1 | 1.5 -> 2
+ // 3 | -2n - 0.5 | -2n - 1 | -2n | -2n | < | -0.5 | 0 | -0.5 -> 0
+ // 4 | -2n - 1.5 | -2n - 2 | -2n - 1 | -2n - 2 | < | -0.5 | 1 | -1.5 -> -2
+ // (where n is a non-negative integer)
+ //
+ // Branch here for cases 1 and 4
+ if ((x > 0 && (x % 1) === +0.5 && (x & 1) === 0) ||
+ (x < 0 && (x % 1) === -0.5 && (x & 1) === 1)) {
+ return censorNegativeZero(Math.floor(x));
+ }
+
+ return censorNegativeZero(Math.round(x));
+}
+
+function integerPart(n) {
+ return censorNegativeZero(Math.trunc(n));
+}
+
+function sign(x) {
+ return x < 0 ? -1 : 1;
+}
+
+function modulo(x, y) {
+ // https://tc39.github.io/ecma262/#eqn-modulo
+ // Note that http://stackoverflow.com/a/4467559/3191 does NOT work for large modulos
+ const signMightNotMatch = x % y;
+ if (sign(y) !== sign(signMightNotMatch)) {
+ return signMightNotMatch + y;
+ }
+ return signMightNotMatch;
+}
+
+function censorNegativeZero(x) {
+ return x === 0 ? 0 : x;
+}
+
+function createIntegerConversion(bitLength, typeOpts) {
+ const isSigned = !typeOpts.unsigned;
+
+ let lowerBound;
+ let upperBound;
+ if (bitLength === 64) {
+ upperBound = Math.pow(2, 53) - 1;
+ lowerBound = !isSigned ? 0 : -Math.pow(2, 53) + 1;
+ } else if (!isSigned) {
+ lowerBound = 0;
+ upperBound = Math.pow(2, bitLength) - 1;
+ } else {
+ lowerBound = -Math.pow(2, bitLength - 1);
+ upperBound = Math.pow(2, bitLength - 1) - 1;
+ }
+
+ const twoToTheBitLength = Math.pow(2, bitLength);
+ const twoToOneLessThanTheBitLength = Math.pow(2, bitLength - 1);
+
+ return (V, opts) => {
+ if (opts === undefined) {
+ opts = {};
+ }
+
+ let x = +V;
+ x = censorNegativeZero(x); // Spec discussion ongoing: https://github.com/heycam/webidl/issues/306
+
+ if (opts.enforceRange) {
+ if (!Number.isFinite(x)) {
+ throw new TypeError(_("is not a finite number", opts));
+ }
+
+ x = integerPart(x);
+
+ if (x < lowerBound || x > upperBound) {
+ throw new TypeError(_(
+ `is outside the accepted range of ${lowerBound} to ${upperBound}, inclusive`, opts));
+ }
+
+ return x;
+ }
+
+ if (!Number.isNaN(x) && opts.clamp) {
+ x = Math.min(Math.max(x, lowerBound), upperBound);
+ x = evenRound(x);
+ return x;
+ }
+
+ if (!Number.isFinite(x) || x === 0) {
+ return 0;
+ }
+ x = integerPart(x);
+
+ // Math.pow(2, 64) is not accurately representable in JavaScript, so try to avoid these per-spec operations if
+ // possible. Hopefully it's an optimization for the non-64-bitLength cases too.
+ if (x >= lowerBound && x <= upperBound) {
+ return x;
+ }
+
+ // These will not work great for bitLength of 64, but oh well. See the README for more details.
+ x = modulo(x, twoToTheBitLength);
+ if (isSigned && x >= twoToOneLessThanTheBitLength) {
+ return x - twoToTheBitLength;
+ }
+ return x;
+ };
+}
+
+exports.any = V => {
+ return V;
+};
+
+exports.void = function () {
+ return undefined;
+};
+
+exports.boolean = function (val) {
+ return !!val;
+};
+
+exports.byte = createIntegerConversion(8, { unsigned: false });
+exports.octet = createIntegerConversion(8, { unsigned: true });
+
+exports.short = createIntegerConversion(16, { unsigned: false });
+exports["unsigned short"] = createIntegerConversion(16, { unsigned: true });
+
+exports.long = createIntegerConversion(32, { unsigned: false });
+exports["unsigned long"] = createIntegerConversion(32, { unsigned: true });
+
+exports["long long"] = createIntegerConversion(64, { unsigned: false });
+exports["unsigned long long"] = createIntegerConversion(64, { unsigned: true });
+
+exports.double = (V, opts) => {
+ const x = +V;
+
+ if (!Number.isFinite(x)) {
+ throw new TypeError(_("is not a finite floating-point value", opts));
+ }
+
+ return x;
+};
+
+exports["unrestricted double"] = V => {
+ const x = +V;
+
+ return x;
+};
+
+exports.float = (V, opts) => {
+ const x = +V;
+
+ if (!Number.isFinite(x)) {
+ throw new TypeError(_("is not a finite floating-point value", opts));
+ }
+
+ if (Object.is(x, -0)) {
+ return x;
+ }
+
+ const y = Math.fround(x);
+
+ if (!Number.isFinite(y)) {
+ throw new TypeError(_("is outside the range of a single-precision floating-point value", opts));
+ }
+
+ return y;
+};
+
+exports["unrestricted float"] = V => {
+ const x = +V;
+
+ if (isNaN(x)) {
+ return x;
+ }
+
+ if (Object.is(x, -0)) {
+ return x;
+ }
+
+ return Math.fround(x);
+};
+
+exports.DOMString = function (V, opts) {
+ if (opts === undefined) {
+ opts = {};
+ }
+
+ if (opts.treatNullAsEmptyString && V === null) {
+ return "";
+ }
+
+ if (typeof V === "symbol") {
+ throw new TypeError(_("is a symbol, which cannot be converted to a string", opts));
+ }
+
+ return String(V);
+};
+
+exports.ByteString = (V, opts) => {
+ const x = exports.DOMString(V, opts);
+ let c;
+ for (let i = 0; (c = x.codePointAt(i)) !== undefined; ++i) {
+ if (c > 255) {
+ throw new TypeError(_("is not a valid ByteString", opts));
+ }
+ }
+
+ return x;
+};
+
+exports.USVString = (V, opts) => {
+ const S = exports.DOMString(V, opts);
+ const n = S.length;
+ const U = [];
+ for (let i = 0; i < n; ++i) {
+ const c = S.charCodeAt(i);
+ if (c < 0xD800 || c > 0xDFFF) {
+ U.push(String.fromCodePoint(c));
+ } else if (0xDC00 <= c && c <= 0xDFFF) {
+ U.push(String.fromCodePoint(0xFFFD));
+ } else if (i === n - 1) {
+ U.push(String.fromCodePoint(0xFFFD));
+ } else {
+ const d = S.charCodeAt(i + 1);
+ if (0xDC00 <= d && d <= 0xDFFF) {
+ const a = c & 0x3FF;
+ const b = d & 0x3FF;
+ U.push(String.fromCodePoint((2 << 15) + ((2 << 9) * a) + b));
+ ++i;
+ } else {
+ U.push(String.fromCodePoint(0xFFFD));
+ }
+ }
+ }
+
+ return U.join("");
+};
+
+exports.object = (V, opts) => {
+ if (type(V) !== "Object") {
+ throw new TypeError(_("is not an object", opts));
+ }
+
+ return V;
+};
+
+// Not exported, but used in Function and VoidFunction.
+
+// Neither Function nor VoidFunction is defined with [TreatNonObjectAsNull], so
+// handling for that is omitted.
+function convertCallbackFunction(V, opts) {
+ if (typeof V !== "function") {
+ throw new TypeError(_("is not a function", opts));
+ }
+ return V;
+}
+
+[
+ Error,
+ ArrayBuffer, // The IsDetachedBuffer abstract operation is not exposed in JS
+ DataView, Int8Array, Int16Array, Int32Array, Uint8Array,
+ Uint16Array, Uint32Array, Uint8ClampedArray, Float32Array, Float64Array
+].forEach(func => {
+ const name = func.name;
+ const article = /^[AEIOU]/.test(name) ? "an" : "a";
+ exports[name] = (V, opts) => {
+ if (!(V instanceof func)) {
+ throw new TypeError(_(`is not ${article} ${name} object`, opts));
+ }
+
+ return V;
+ };
+});
+
+// Common definitions
+
+exports.ArrayBufferView = (V, opts) => {
+ if (!ArrayBuffer.isView(V)) {
+ throw new TypeError(_("is not a view on an ArrayBuffer object", opts));
+ }
+
+ return V;
+};
+
+exports.BufferSource = (V, opts) => {
+ if (!(ArrayBuffer.isView(V) || V instanceof ArrayBuffer)) {
+ throw new TypeError(_("is not an ArrayBuffer object or a view on one", opts));
+ }
+
+ return V;
+};
+
+exports.DOMTimeStamp = exports["unsigned long long"];
+
+exports.Function = convertCallbackFunction;
+
+exports.VoidFunction = convertCallbackFunction;
+
+},{}]},{},[6])(6)
+});
diff --git a/devtools/client/shared/view-source.js b/devtools/client/shared/view-source.js
new file mode 100644
index 0000000000..d551a37aa8
--- /dev/null
+++ b/devtools/client/shared/view-source.js
@@ -0,0 +1,197 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Tries to open a Stylesheet file in the Style Editor. If the file is not
+ * found, it is opened in source view instead.
+ * Returns a promise resolving to a boolean indicating whether or not
+ * the source was able to be displayed in the StyleEditor, as the built-in
+ * Firefox View Source is the fallback.
+ *
+ * @param {Toolbox} toolbox
+ * @param {string|Object} stylesheetResourceOrGeneratedURL
+ * @param {number} generatedLine
+ * @param {number} generatedColumn
+ *
+ * @return {Promise<boolean>}
+ */
+exports.viewSourceInStyleEditor = async function (
+ toolbox,
+ stylesheetResourceOrGeneratedURL,
+ generatedLine,
+ generatedColumn
+) {
+ const originalPanelId = toolbox.currentToolId;
+
+ try {
+ const panel = await toolbox.selectTool("styleeditor", "view-source", {
+ // This will be only used in case the styleeditor wasn't loaded yet, to make the
+ // initialization faster in case we already have a stylesheet resource. We still
+ // need the rest of this function to handle subsequent calls and sourcemapped stylesheets.
+ stylesheetToSelect: {
+ stylesheet: stylesheetResourceOrGeneratedURL,
+ line: generatedLine,
+ column: generatedColumn,
+ },
+ });
+
+ let stylesheetResource;
+ if (typeof stylesheetResourceOrGeneratedURL === "string") {
+ stylesheetResource = panel.getStylesheetResourceForGeneratedURL(
+ stylesheetResourceOrGeneratedURL
+ );
+ } else {
+ stylesheetResource = stylesheetResourceOrGeneratedURL;
+ }
+
+ const originalLocation = stylesheetResource
+ ? await getOriginalLocation(
+ toolbox,
+ stylesheetResource.resourceId,
+ generatedLine,
+ generatedColumn
+ )
+ : null;
+
+ if (originalLocation) {
+ await panel.selectOriginalSheet(
+ originalLocation.sourceId,
+ originalLocation.line,
+ originalLocation.column
+ );
+ return true;
+ }
+
+ if (stylesheetResource) {
+ await panel.selectStyleSheet(
+ stylesheetResource,
+ generatedLine,
+ generatedColumn
+ );
+ return true;
+ }
+ } catch (e) {
+ console.error("Failed to view source in style editor", e);
+ }
+
+ // If we weren't able to select the stylesheet in the style editor, display it in a
+ // view-source tab
+ exports.viewSource(
+ toolbox,
+ typeof stylesheetResourceOrGeneratedURL === "string"
+ ? stylesheetResourceOrGeneratedURL
+ : stylesheetResourceOrGeneratedURL.href ||
+ stylesheetResourceOrGeneratedURL.nodeHref,
+ generatedLine
+ );
+
+ // As we might have moved to the styleeditor, switch back to the original panel
+ await toolbox.selectTool(originalPanelId);
+
+ return false;
+};
+
+/**
+ * Tries to open a JavaScript file in the Debugger. If the file is not found,
+ * it is opened in source view instead. Either the source URL or source actor ID
+ * can be specified. If both are specified, the source actor ID is used.
+ *
+ * Returns a promise resolving to a boolean indicating whether or not
+ * the source was able to be displayed in the Debugger, as the built-in Firefox
+ * View Source is the fallback.
+ *
+ * @param {Toolbox} toolbox
+ * @param {string} sourceURL
+ * @param {number} sourceLine
+ * @param {number} sourceColumn
+ * @param {string} sourceID
+ * @param {(string|object)} [reason=unknown]
+ *
+ * @return {Promise<boolean>}
+ */
+exports.viewSourceInDebugger = async function (
+ toolbox,
+ generatedURL,
+ generatedLine,
+ generatedColumn,
+ sourceActorId,
+ reason = "unknown"
+) {
+ // Load the debugger in the background
+ const dbg = await toolbox.loadTool("jsdebugger");
+
+ const openedSourceInDebugger = await dbg.openSourceInDebugger({
+ generatedURL,
+ generatedLine,
+ generatedColumn,
+ sourceActorId,
+ reason,
+ });
+
+ if (openedSourceInDebugger) {
+ return true;
+ }
+
+ // Fallback to built-in firefox view-source:
+ exports.viewSource(toolbox, generatedURL, generatedLine, generatedColumn);
+ return false;
+};
+
+async function getOriginalLocation(
+ toolbox,
+ generatedID,
+ generatedLine,
+ generatedColumn
+) {
+ // If there is no line number, then there's no chance that we'll get back
+ // a useful original location.
+ if (typeof generatedLine !== "number") {
+ return null;
+ }
+
+ let originalLocation = null;
+ try {
+ originalLocation = await toolbox.sourceMapLoader.getOriginalLocation({
+ sourceId: generatedID,
+ line: generatedLine,
+ column: generatedColumn,
+ });
+ if (originalLocation && originalLocation.sourceId === generatedID) {
+ originalLocation = null;
+ }
+ } catch (err) {
+ console.error(
+ "Failed to resolve sourcemapped location for the given source location",
+ { generatedID, generatedLine, generatedColumn },
+ err
+ );
+ }
+ return originalLocation;
+}
+
+/**
+ * Open a link in Firefox's View Source.
+ *
+ * @param {Toolbox} toolbox
+ * @param {string} sourceURL
+ * @param {number} sourceLine
+ * @param {number} sourceColumn
+ *
+ * @return {Promise}
+ */
+exports.viewSource = async function (
+ toolbox,
+ sourceURL,
+ sourceLine,
+ sourceColumn
+) {
+ const utils = toolbox.gViewSourceUtils;
+ utils.viewSource({
+ URL: sourceURL,
+ lineNumber: sourceLine || -1,
+ columnNumber: sourceColumn || -1,
+ });
+};
diff --git a/devtools/client/shared/webgl-utils.js b/devtools/client/shared/webgl-utils.js
new file mode 100644
index 0000000000..3d59627744
--- /dev/null
+++ b/devtools/client/shared/webgl-utils.js
@@ -0,0 +1,53 @@
+/* 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/. */
+"use strict";
+
+const WEBGL_CONTEXT_NAME = "experimental-webgl";
+
+function isWebGLForceEnabled() {
+ return Services.prefs.getBoolPref("webgl.force-enabled");
+}
+
+function isWebGLSupportedByGFX() {
+ let supported = false;
+
+ try {
+ const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ const angle = gfxInfo.FEATURE_WEBGL_ANGLE;
+ const opengl = gfxInfo.FEATURE_WEBGL_OPENGL;
+
+ // if either the Angle or OpenGL renderers are available, WebGL should work
+ supported =
+ gfxInfo.getFeatureStatus(angle) === gfxInfo.FEATURE_STATUS_OK ||
+ gfxInfo.getFeatureStatus(opengl) === gfxInfo.FEATURE_STATUS_OK;
+ } catch (e) {
+ return false;
+ }
+ return supported;
+}
+
+function create3DContext(canvas) {
+ // try to get a valid context from an existing canvas
+ let context = null;
+ try {
+ context = canvas.getContext(WEBGL_CONTEXT_NAME);
+ } catch (e) {
+ return null;
+ }
+ return context;
+}
+
+function createCanvas(doc) {
+ return doc.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+}
+
+function isWebGLSupported(doc) {
+ const supported =
+ !isWebGLForceEnabled() &&
+ isWebGLSupportedByGFX() &&
+ create3DContext(createCanvas(doc));
+
+ return supported;
+}
+exports.isWebGLSupported = isWebGLSupported;
diff --git a/devtools/client/shared/widgets/Chart.js b/devtools/client/shared/widgets/Chart.js
new file mode 100644
index 0000000000..574fadbadf
--- /dev/null
+++ b/devtools/client/shared/widgets/Chart.js
@@ -0,0 +1,532 @@
+/* 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/. */
+"use strict";
+
+const NET_STRINGS_URI = "devtools/client/locales/netmonitor.properties";
+const SVG_NS = "http://www.w3.org/2000/svg";
+const PI = Math.PI;
+const TAU = PI * 2;
+const EPSILON = 0.0000001;
+const NAMED_SLICE_MIN_ANGLE = TAU / 8;
+const NAMED_SLICE_TEXT_DISTANCE_RATIO = 1.9;
+const HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO = 20;
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(NET_STRINGS_URI);
+
+/**
+ * A factory for creating charts.
+ * Example usage: let myChart = Chart.Pie(document, { ... });
+ */
+var Chart = {
+ Pie: createPieChart,
+ Table: createTableChart,
+ PieTable: createPieTableChart,
+};
+
+/**
+ * A simple pie chart proxy for the underlying view.
+ * Each item in the `slices` property represents a [data, node] pair containing
+ * the data used to create the slice and the Node displaying it.
+ *
+ * @param Node node
+ * The node representing the view for this chart.
+ */
+function PieChart(node) {
+ this.node = node;
+ this.slices = new WeakMap();
+ EventEmitter.decorate(this);
+}
+
+/**
+ * A simple table chart proxy for the underlying view.
+ * Each item in the `rows` property represents a [data, node] pair containing
+ * the data used to create the row and the Node displaying it.
+ *
+ * @param Node node
+ * The node representing the view for this chart.
+ */
+function TableChart(node) {
+ this.node = node;
+ this.rows = new WeakMap();
+ EventEmitter.decorate(this);
+}
+
+/**
+ * A simple pie+table chart proxy for the underlying view.
+ *
+ * @param Node node
+ * The node representing the view for this chart.
+ * @param PieChart pie
+ * The pie chart proxy.
+ * @param TableChart table
+ * The table chart proxy.
+ */
+function PieTableChart(node, pie, table) {
+ this.node = node;
+ this.pie = pie;
+ this.table = table;
+ EventEmitter.decorate(this);
+}
+
+/**
+ * Creates the DOM for a pie+table chart.
+ *
+ * @param Document document
+ * The document responsible with creating the DOM.
+ * @param object
+ * An object containing all or some of the following properties:
+ * - title: a string displayed as the table chart's (description)/local
+ * - diameter: the diameter of the pie chart, in pixels
+ * - data: an array of items used to display each slice in the pie
+ * and each row in the table;
+ * @see `createPieChart` and `createTableChart` for details.
+ * - strings: @see `createTableChart` for details.
+ * - totals: @see `createTableChart` for details.
+ * - sorted: a flag specifying if the `data` should be sorted
+ * ascending by `size`.
+ * @return PieTableChart
+ * A pie+table chart proxy instance, which emits the following events:
+ * - "mouseover", when the mouse enters a slice or a row
+ * - "mouseout", when the mouse leaves a slice or a row
+ * - "click", when the mouse enters a slice or a row
+ */
+function createPieTableChart(
+ document,
+ { title, diameter, data, strings, totals, sorted, header }
+) {
+ if (data && sorted) {
+ data = data.slice().sort((a, b) => +(a.size < b.size));
+ }
+
+ const pie = Chart.Pie(document, {
+ width: diameter,
+ data,
+ });
+
+ const table = Chart.Table(document, {
+ title,
+ data,
+ strings,
+ totals,
+ header,
+ });
+
+ const container = document.createElement("div");
+ container.className = "pie-table-chart-container";
+ container.appendChild(pie.node);
+ container.appendChild(table.node);
+
+ const proxy = new PieTableChart(container, pie, table);
+
+ pie.on("click", item => {
+ proxy.emit("click", item);
+ });
+
+ table.on("click", item => {
+ proxy.emit("click", item);
+ });
+
+ pie.on("mouseover", item => {
+ proxy.emit("mouseover", item);
+ if (table.rows.has(item)) {
+ table.rows.get(item).setAttribute("focused", "");
+ }
+ });
+
+ pie.on("mouseout", item => {
+ proxy.emit("mouseout", item);
+ if (table.rows.has(item)) {
+ table.rows.get(item).removeAttribute("focused");
+ }
+ });
+
+ table.on("mouseover", item => {
+ proxy.emit("mouseover", item);
+ if (pie.slices.has(item)) {
+ pie.slices.get(item).setAttribute("focused", "");
+ }
+ });
+
+ table.on("mouseout", item => {
+ proxy.emit("mouseout", item);
+ if (pie.slices.has(item)) {
+ pie.slices.get(item).removeAttribute("focused");
+ }
+ });
+
+ return proxy;
+}
+
+/**
+ * Creates the DOM for a pie chart based on the specified properties.
+ *
+ * @param Document document
+ * The document responsible with creating the DOM.
+ * @param object
+ * An object containing all or some of the following properties:
+ * - data: an array of items used to display each slice; all the items
+ * should be objects containing a `size` and a `label` property.
+ * e.g: [{
+ * size: 1,
+ * label: "foo"
+ * }, {
+ * size: 2,
+ * label: "bar"
+ * }];
+ * - width: the width of the chart, in pixels
+ * - height: optional, the height of the chart, in pixels.
+ * - centerX: optional, the X-axis center of the chart, in pixels.
+ * - centerY: optional, the Y-axis center of the chart, in pixels.
+ * - radius: optional, the radius of the chart, in pixels.
+ * @return PieChart
+ * A pie chart proxy instance, which emits the following events:
+ * - "mouseover", when the mouse enters a slice
+ * - "mouseout", when the mouse leaves a slice
+ * - "click", when the mouse clicks a slice
+ */
+function createPieChart(
+ document,
+ { data, width, height, centerX, centerY, radius }
+) {
+ height = height || width;
+ centerX = centerX || width / 2;
+ centerY = centerY || height / 2;
+ radius = radius || (width + height) / 4;
+ let isPlaceholder = false;
+
+ // If there's no data available, display an empty placeholder.
+ if (!data) {
+ data = loadingPieChartData();
+ isPlaceholder = true;
+ }
+ if (!data.length) {
+ data = emptyPieChartData();
+ isPlaceholder = true;
+ }
+
+ const container = document.createElementNS(SVG_NS, "svg");
+ container.setAttribute(
+ "class",
+ "generic-chart-container pie-chart-container"
+ );
+
+ container.setAttribute("width", width);
+ container.setAttribute("height", height);
+ container.setAttribute("viewBox", "0 0 " + width + " " + height);
+ container.setAttribute("slices", data.length);
+ container.setAttribute("placeholder", isPlaceholder);
+ container.setAttribute("role", "group");
+ container.setAttribute("aria-label", L10N.getStr("pieChart.ariaLabel"));
+
+ const slicesGroup = document.createElementNS(SVG_NS, "g");
+ slicesGroup.setAttribute("role", "list");
+ container.append(slicesGroup);
+
+ const proxy = new PieChart(container);
+
+ const total = data.reduce((acc, e) => acc + e.size, 0);
+ const angles = data.map(e => (e.size / total) * (TAU - EPSILON));
+ const largest = data.reduce((a, b) => (a.size > b.size ? a : b));
+ const smallest = data.reduce((a, b) => (a.size < b.size ? a : b));
+
+ const textDistance = radius / NAMED_SLICE_TEXT_DISTANCE_RATIO;
+ const translateDistance = radius / HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO;
+ let startAngle = TAU;
+ let endAngle = 0;
+ let midAngle = 0;
+ radius -= translateDistance;
+
+ for (let i = data.length - 1; i >= 0; i--) {
+ const sliceInfo = data[i];
+ const sliceAngle = angles[i];
+
+ const sliceNode = document.createElementNS(SVG_NS, "g");
+ sliceNode.setAttribute("role", "listitem");
+ slicesGroup.append(sliceNode);
+
+ const interactiveNodeId = `${sliceInfo.label}-slice`;
+ const textNodeId = `${sliceInfo.label}-slice-label`;
+
+ // The only way to make this keyboard accessible is to have a link
+ const interactiveNode = document.createElementNS(SVG_NS, "a");
+ interactiveNode.setAttribute("id", interactiveNodeId);
+ interactiveNode.setAttribute("xlink:href", `#${interactiveNodeId}`);
+ interactiveNode.setAttribute("tabindex", `0`);
+ interactiveNode.setAttribute("role", `button`);
+ interactiveNode.classList.add("pie-chart-slice-container");
+ if (!isPlaceholder) {
+ interactiveNode.setAttribute(
+ "aria-label",
+ L10N.getFormatStr(
+ "pieChart.sliceAriaLabel",
+ sliceInfo.label,
+ new Intl.NumberFormat(undefined, {
+ style: "unit",
+ unit: "percent",
+ maximumFractionDigits: 2,
+ }).format((sliceInfo.size / total) * 100)
+ )
+ );
+ }
+
+ sliceNode.append(interactiveNode);
+
+ endAngle = startAngle - sliceAngle;
+ midAngle = (startAngle + endAngle) / 2;
+
+ const x1 = centerX + radius * Math.sin(startAngle);
+ const y1 = centerY - radius * Math.cos(startAngle);
+ const x2 = centerX + radius * Math.sin(endAngle);
+ const y2 = centerY - radius * Math.cos(endAngle);
+ const largeArcFlag = Math.abs(startAngle - endAngle) > PI ? 1 : 0;
+
+ const pathNode = document.createElementNS(SVG_NS, "path");
+ pathNode.classList.add("pie-chart-slice");
+ pathNode.setAttribute("data-statistic-name", sliceInfo.label);
+ pathNode.setAttribute(
+ "d",
+ " M " +
+ centerX +
+ "," +
+ centerY +
+ " L " +
+ x2 +
+ "," +
+ y2 +
+ " A " +
+ radius +
+ "," +
+ radius +
+ " 0 " +
+ largeArcFlag +
+ " 1 " +
+ x1 +
+ "," +
+ y1 +
+ " Z"
+ );
+
+ if (sliceInfo == largest) {
+ pathNode.setAttribute("largest", "");
+ }
+ if (sliceInfo == smallest) {
+ pathNode.setAttribute("smallest", "");
+ }
+
+ const hoverX = translateDistance * Math.sin(midAngle);
+ const hoverY = -translateDistance * Math.cos(midAngle);
+ const hoverTransform =
+ "transform: translate(" + hoverX + "px, " + hoverY + "px)";
+ pathNode.setAttribute("style", data.length > 1 ? hoverTransform : "");
+
+ proxy.slices.set(sliceInfo, pathNode);
+ delegate(
+ proxy,
+ ["click", "mouseover", "mouseout", "focus"],
+ interactiveNode,
+ sliceInfo
+ );
+ interactiveNode.appendChild(pathNode);
+
+ const textX = centerX + textDistance * Math.sin(midAngle);
+ const textY = centerY - textDistance * Math.cos(midAngle);
+
+ // Don't add the label if the slice isn't large enough so it doesn't look cramped.
+ if (sliceAngle >= NAMED_SLICE_MIN_ANGLE) {
+ const label = document.createElementNS(SVG_NS, "text");
+ label.appendChild(document.createTextNode(sliceInfo.label));
+ label.setAttribute("id", textNodeId);
+ // A label is already set on `interactiveNode`, so hide this from the accessibility tree
+ // to avoid duplicating text.
+ label.setAttribute("aria-hidden", "true");
+ label.setAttribute("class", "pie-chart-label");
+ label.setAttribute("style", data.length > 1 ? hoverTransform : "");
+ label.setAttribute("x", data.length > 1 ? textX : centerX);
+ label.setAttribute("y", data.length > 1 ? textY : centerY);
+ interactiveNode.append(label);
+ }
+
+ startAngle = endAngle;
+ }
+
+ return proxy;
+}
+
+/**
+ * Creates the DOM for a table chart based on the specified properties.
+ *
+ * @param Document document
+ * The document responsible with creating the DOM.
+ * @param object
+ * An object containing all or some of the following properties:
+ * - title: a string displayed as the chart's (description)/local
+ * - data: an array of items used to display each row; all the items
+ * should be objects representing columns, for which the
+ * properties' values will be displayed in each cell of a row.
+ * e.g: [{
+ * label1: 1,
+ * label2: 3,
+ * label3: "foo"
+ * }, {
+ * label1: 4,
+ * label2: 6,
+ * label3: "bar
+ * }];
+ * - strings: an object specifying for which rows in the `data` array
+ * their cell values should be stringified and localized
+ * based on a predicate function;
+ * e.g: {
+ * label1: value => l10n.getFormatStr("...", value)
+ * }
+ * - totals: an object specifying for which rows in the `data` array
+ * the sum of their cells is to be displayed in the chart;
+ * e.g: {
+ * label1: total => l10n.getFormatStr("...", total), // 5
+ * label2: total => l10n.getFormatStr("...", total), // 9
+ * }
+ * - header: an object specifying strings to use for table column
+ * headers
+ * e.g. {
+ * label1: l10n.getStr(...),
+ * label2: l10n.getStr(...),
+ * }
+ * @return TableChart
+ * A table chart proxy instance, which emits the following events:
+ * - "mouseover", when the mouse enters a row
+ * - "mouseout", when the mouse leaves a row
+ * - "click", when the mouse clicks a row
+ */
+function createTableChart(document, { title, data, strings, totals, header }) {
+ strings = strings || {};
+ totals = totals || {};
+ header = header || {};
+ let isPlaceholder = false;
+
+ // If there's no data available, display an empty placeholder.
+ if (!data) {
+ data = loadingTableChartData();
+ isPlaceholder = true;
+ }
+ if (!data.length) {
+ data = emptyTableChartData();
+ isPlaceholder = true;
+ }
+
+ const container = document.createElement("div");
+ container.className = "generic-chart-container table-chart-container";
+ container.setAttribute("placeholder", isPlaceholder);
+
+ const proxy = new TableChart(container);
+
+ const titleNode = document.createElement("span");
+ titleNode.className = "plain table-chart-title";
+ titleNode.textContent = title;
+ container.appendChild(titleNode);
+
+ const tableNode = document.createElement("table");
+ tableNode.className = "plain table-chart-grid";
+ container.appendChild(tableNode);
+
+ const headerNode = document.createElement("thead");
+ headerNode.className = "table-chart-row";
+
+ const bodyNode = document.createElement("tbody");
+
+ const headerBoxNode = document.createElement("tr");
+ headerBoxNode.className = "table-chart-row-box";
+ headerNode.appendChild(headerBoxNode);
+
+ for (const [key, value] of Object.entries(header)) {
+ const headerLabelNode = document.createElement("th");
+ headerLabelNode.className = "plain table-chart-row-label";
+ headerLabelNode.setAttribute("name", key);
+ headerLabelNode.textContent = value;
+ if (key == "count") {
+ headerLabelNode.classList.add("offscreen");
+ }
+ headerBoxNode.appendChild(headerLabelNode);
+ }
+
+ tableNode.append(headerNode, bodyNode);
+
+ for (const rowInfo of data) {
+ const rowNode = document.createElement("tr");
+ rowNode.className = "table-chart-row";
+ rowNode.setAttribute("data-statistic-name", rowInfo.label);
+
+ for (const [key, value] of Object.entries(rowInfo)) {
+ // Don't render the "cached" column. We only have it in here so it can be displayed
+ // in the `totals` section.
+ if (key == "cached") {
+ continue;
+ }
+ const index = data.indexOf(rowInfo);
+ const stringified = strings[key] ? strings[key](value, index) : value;
+ const labelNode = document.createElement("td");
+ labelNode.className = "plain table-chart-row-label";
+ labelNode.setAttribute("name", key);
+ labelNode.textContent = stringified;
+ rowNode.appendChild(labelNode);
+ }
+
+ proxy.rows.set(rowInfo, rowNode);
+ delegate(proxy, ["click", "mouseover", "mouseout"], rowNode, rowInfo);
+ bodyNode.appendChild(rowNode);
+ }
+
+ const totalsNode = document.createElement("div");
+ totalsNode.className = "table-chart-totals";
+
+ for (const [key, value] of Object.entries(totals)) {
+ const total = data.reduce((acc, e) => acc + e[key], 0);
+ const stringified = value ? value(total || 0) : total;
+ const labelNode = document.createElement("span");
+ labelNode.className = "plain table-chart-summary-label";
+ labelNode.setAttribute("name", key);
+ labelNode.textContent = stringified;
+ totalsNode.appendChild(labelNode);
+ }
+
+ container.appendChild(totalsNode);
+
+ return proxy;
+}
+
+function loadingPieChartData() {
+ return [{ size: 1, label: L10N.getStr("pieChart.loading") }];
+}
+
+function emptyPieChartData() {
+ return [{ size: 1, label: L10N.getStr("pieChart.unavailable") }];
+}
+
+function loadingTableChartData() {
+ return [{ size: "", label: L10N.getStr("tableChart.loading") }];
+}
+
+function emptyTableChartData() {
+ return [{ size: "", label: L10N.getStr("tableChart.unavailable") }];
+}
+
+/**
+ * Delegates DOM events emitted by a Node to an EventEmitter proxy.
+ *
+ * @param EventEmitter emitter
+ * The event emitter proxy instance.
+ * @param array events
+ * An array of events, e.g. ["mouseover", "mouseout"].
+ * @param Node node
+ * The element firing the DOM events.
+ * @param any args
+ * The arguments passed when emitting events through the proxy.
+ */
+function delegate(emitter, events, node, args) {
+ for (const event of events) {
+ node.addEventListener(event, emitter.emit.bind(emitter, event, args));
+ }
+}
+
+exports.Chart = Chart;
diff --git a/devtools/client/shared/widgets/CubicBezierPresets.js b/devtools/client/shared/widgets/CubicBezierPresets.js
new file mode 100644
index 0000000000..7422843d88
--- /dev/null
+++ b/devtools/client/shared/widgets/CubicBezierPresets.js
@@ -0,0 +1,64 @@
+/**
+ * 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/.
+ */
+
+// Set of preset definitions for use with CubicBezierWidget
+// Credit: http://easings.net
+
+"use strict";
+
+const PREDEFINED = {
+ ease: [0.25, 0.1, 0.25, 1],
+ linear: [0, 0, 1, 1],
+ "ease-in": [0.42, 0, 1, 1],
+ "ease-out": [0, 0, 0.58, 1],
+ "ease-in-out": [0.42, 0, 0.58, 1],
+};
+
+const PRESETS = {
+ "ease-in": {
+ "ease-in-linear": [0, 0, 1, 1],
+ "ease-in-ease-in": [0.42, 0, 1, 1],
+ "ease-in-sine": [0.47, 0, 0.74, 0.71],
+ "ease-in-quadratic": [0.55, 0.09, 0.68, 0.53],
+ "ease-in-cubic": [0.55, 0.06, 0.68, 0.19],
+ "ease-in-quartic": [0.9, 0.03, 0.69, 0.22],
+ "ease-in-quintic": [0.76, 0.05, 0.86, 0.06],
+ "ease-in-exponential": [0.95, 0.05, 0.8, 0.04],
+ "ease-in-circular": [0.6, 0.04, 0.98, 0.34],
+ "ease-in-backward": [0.6, -0.28, 0.74, 0.05],
+ },
+ "ease-out": {
+ "ease-out-linear": [0, 0, 1, 1],
+ "ease-out-ease-out": [0, 0, 0.58, 1],
+ "ease-out-sine": [0.39, 0.58, 0.57, 1],
+ "ease-out-quadratic": [0.25, 0.46, 0.45, 0.94],
+ "ease-out-cubic": [0.22, 0.61, 0.36, 1],
+ "ease-out-quartic": [0.17, 0.84, 0.44, 1],
+ "ease-out-quintic": [0.23, 1, 0.32, 1],
+ "ease-out-exponential": [0.19, 1, 0.22, 1],
+ "ease-out-circular": [0.08, 0.82, 0.17, 1],
+ "ease-out-backward": [0.18, 0.89, 0.32, 1.28],
+ },
+ "ease-in-out": {
+ "ease-in-out-linear": [0, 0, 1, 1],
+ "ease-in-out-ease": [0.25, 0.1, 0.25, 1],
+ "ease-in-out-ease-in-out": [0.42, 0, 0.58, 1],
+ "ease-in-out-sine": [0.45, 0.05, 0.55, 0.95],
+ "ease-in-out-quadratic": [0.46, 0.03, 0.52, 0.96],
+ "ease-in-out-cubic": [0.65, 0.05, 0.36, 1],
+ "ease-in-out-quartic": [0.77, 0, 0.18, 1],
+ "ease-in-out-quintic": [0.86, 0, 0.07, 1],
+ "ease-in-out-exponential": [1, 0, 0, 1],
+ "ease-in-out-circular": [0.79, 0.14, 0.15, 0.86],
+ "ease-in-out-backward": [0.68, -0.55, 0.27, 1.55],
+ },
+};
+
+const DEFAULT_PRESET_CATEGORY = Object.keys(PRESETS)[0];
+
+exports.PRESETS = PRESETS;
+exports.PREDEFINED = PREDEFINED;
+exports.DEFAULT_PRESET_CATEGORY = DEFAULT_PRESET_CATEGORY;
diff --git a/devtools/client/shared/widgets/CubicBezierWidget.js b/devtools/client/shared/widgets/CubicBezierWidget.js
new file mode 100644
index 0000000000..39407d4711
--- /dev/null
+++ b/devtools/client/shared/widgets/CubicBezierWidget.js
@@ -0,0 +1,986 @@
+/**
+ * Copyright (c) 2013 Lea Verou. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+// Based on www.cubic-bezier.com by Lea Verou
+// See https://github.com/LeaVerou/cubic-bezier
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const {
+ PREDEFINED,
+ PRESETS,
+ DEFAULT_PRESET_CATEGORY,
+} = require("resource://devtools/client/shared/widgets/CubicBezierPresets.js");
+const { getCSSLexer } = require("resource://devtools/shared/css/lexer.js");
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+/**
+ * CubicBezier data structure helper
+ * Accepts an array of coordinates and exposes a few useful getters
+ * @param {Array} coordinates i.e. [.42, 0, .58, 1]
+ */
+function CubicBezier(coordinates) {
+ if (!coordinates) {
+ throw new Error("No offsets were defined");
+ }
+
+ this.coordinates = coordinates.map(n => +n);
+
+ for (let i = 4; i--; ) {
+ const xy = this.coordinates[i];
+ if (isNaN(xy) || (!(i % 2) && (xy < 0 || xy > 1))) {
+ throw new Error(`Wrong coordinate at ${i}(${xy})`);
+ }
+ }
+
+ this.coordinates.toString = function () {
+ return (
+ this.map(n => {
+ return (Math.round(n * 100) / 100 + "").replace(/^0\./, ".");
+ }) + ""
+ );
+ };
+}
+
+exports.CubicBezier = CubicBezier;
+
+CubicBezier.prototype = {
+ get P1() {
+ return this.coordinates.slice(0, 2);
+ },
+
+ get P2() {
+ return this.coordinates.slice(2);
+ },
+
+ toString() {
+ // Check first if current coords are one of css predefined functions
+ const predefName = Object.keys(PREDEFINED).find(key =>
+ coordsAreEqual(PREDEFINED[key], this.coordinates)
+ );
+
+ return predefName || "cubic-bezier(" + this.coordinates + ")";
+ },
+};
+
+/**
+ * Bezier curve canvas plotting class
+ * @param {DOMNode} canvas
+ * @param {CubicBezier} bezier
+ * @param {Array} padding Amount of horizontal,vertical padding around the graph
+ */
+function BezierCanvas(canvas, bezier, padding) {
+ this.canvas = canvas;
+ this.bezier = bezier;
+ this.padding = getPadding(padding);
+
+ // Convert to a cartesian coordinate system with axes from 0 to 1
+ this.ctx = this.canvas.getContext("2d");
+ const p = this.padding;
+
+ this.ctx.scale(
+ canvas.width * (1 - p[1] - p[3]),
+ -canvas.height * (1 - p[0] - p[2])
+ );
+ this.ctx.translate(p[3] / (1 - p[1] - p[3]), -1 - p[0] / (1 - p[0] - p[2]));
+}
+
+exports.BezierCanvas = BezierCanvas;
+
+BezierCanvas.prototype = {
+ /**
+ * Get P1 and P2 current top/left offsets so they can be positioned
+ * @return {Array} Returns an array of 2 {top:String,left:String} objects
+ */
+ get offsets() {
+ const p = this.padding,
+ w = this.canvas.width,
+ h = this.canvas.height;
+
+ return [
+ {
+ left:
+ w * (this.bezier.coordinates[0] * (1 - p[3] - p[1]) - p[3]) + "px",
+ top:
+ h * (1 - this.bezier.coordinates[1] * (1 - p[0] - p[2]) - p[0]) +
+ "px",
+ },
+ {
+ left:
+ w * (this.bezier.coordinates[2] * (1 - p[3] - p[1]) - p[3]) + "px",
+ top:
+ h * (1 - this.bezier.coordinates[3] * (1 - p[0] - p[2]) - p[0]) +
+ "px",
+ },
+ ];
+ },
+
+ /**
+ * Convert an element's left/top offsets into coordinates
+ */
+ offsetsToCoordinates(element) {
+ const w = this.canvas.width,
+ h = this.canvas.height;
+
+ // Convert padding percentage to actual padding
+ const p = this.padding.map((a, i) => a * (i % 2 ? w : h));
+
+ return [
+ (parseFloat(element.style.left) - p[3]) / (w + p[1] + p[3]),
+ (h - parseFloat(element.style.top) - p[2]) / (h - p[0] - p[2]),
+ ];
+ },
+
+ /**
+ * Draw the cubic bezier curve for the current coordinates
+ */
+ plot(settings = {}) {
+ const xy = this.bezier.coordinates;
+
+ const defaultSettings = {
+ handleColor: "#666",
+ handleThickness: 0.008,
+ bezierColor: "#4C9ED9",
+ bezierThickness: 0.015,
+ drawHandles: true,
+ };
+
+ for (const setting in settings) {
+ defaultSettings[setting] = settings[setting];
+ }
+
+ // Clear the canvas –making sure to clear the
+ // whole area by resetting the transform first.
+ this.ctx.save();
+ this.ctx.setTransform(1, 0, 0, 1, 0, 0);
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ this.ctx.restore();
+
+ if (defaultSettings.drawHandles) {
+ // Draw control handles
+ this.ctx.beginPath();
+ this.ctx.fillStyle = defaultSettings.handleColor;
+ this.ctx.lineWidth = defaultSettings.handleThickness;
+ this.ctx.strokeStyle = defaultSettings.handleColor;
+
+ this.ctx.moveTo(0, 0);
+ this.ctx.lineTo(xy[0], xy[1]);
+ this.ctx.moveTo(1, 1);
+ this.ctx.lineTo(xy[2], xy[3]);
+
+ this.ctx.stroke();
+ this.ctx.closePath();
+
+ const circle = (ctx, cx, cy, r) => {
+ ctx.beginPath();
+ ctx.arc(cx, cy, r, 0, 2 * Math.PI, !1);
+ ctx.closePath();
+ };
+
+ circle(this.ctx, xy[0], xy[1], 1.5 * defaultSettings.handleThickness);
+ this.ctx.fill();
+ circle(this.ctx, xy[2], xy[3], 1.5 * defaultSettings.handleThickness);
+ this.ctx.fill();
+ }
+
+ // Draw bezier curve
+ this.ctx.beginPath();
+ this.ctx.lineWidth = defaultSettings.bezierThickness;
+ this.ctx.strokeStyle = defaultSettings.bezierColor;
+ this.ctx.moveTo(0, 0);
+ this.ctx.bezierCurveTo(xy[0], xy[1], xy[2], xy[3], 1, 1);
+ this.ctx.stroke();
+ this.ctx.closePath();
+ },
+};
+
+/**
+ * Cubic-bezier widget. Uses the BezierCanvas class to draw the curve and
+ * adds the control points and user interaction
+ * @param {DOMNode} parent The container where the graph should be created
+ * @param {Array} coordinates Coordinates of the curve to be drawn
+ *
+ * Emits "updated" events whenever the curve is changed. Along with the event is
+ * sent a CubicBezier object
+ */
+function CubicBezierWidget(
+ parent,
+ coordinates = PRESETS["ease-in"]["ease-in-sine"]
+) {
+ EventEmitter.decorate(this);
+
+ this.parent = parent;
+ const { curve, p1, p2 } = this._initMarkup();
+
+ this.curveBoundingBox = curve.getBoundingClientRect();
+ this.curve = curve;
+ this.p1 = p1;
+ this.p2 = p2;
+
+ // Create and plot the bezier curve
+ this.bezierCanvas = new BezierCanvas(
+ this.curve,
+ new CubicBezier(coordinates),
+ [0.3, 0]
+ );
+ this.bezierCanvas.plot();
+
+ // Place the control points
+ const offsets = this.bezierCanvas.offsets;
+ this.p1.style.left = offsets[0].left;
+ this.p1.style.top = offsets[0].top;
+ this.p2.style.left = offsets[1].left;
+ this.p2.style.top = offsets[1].top;
+
+ this._onPointMouseDown = this._onPointMouseDown.bind(this);
+ this._onPointKeyDown = this._onPointKeyDown.bind(this);
+ this._onCurveClick = this._onCurveClick.bind(this);
+ this._onNewCoordinates = this._onNewCoordinates.bind(this);
+ this.onPrefersReducedMotionChange =
+ this.onPrefersReducedMotionChange.bind(this);
+
+ // Add preset preview menu
+ this.presets = new CubicBezierPresetWidget(parent);
+
+ // Add the timing function previewer
+ // if prefers-reduced-motion is not set
+ this.reducedMotion = parent.ownerGlobal.matchMedia(
+ "(prefers-reduced-motion)"
+ );
+ if (!this.reducedMotion.matches) {
+ this.timingPreview = new TimingFunctionPreviewWidget(parent);
+ }
+
+ // add event listener to change prefers-reduced-motion
+ // of the timing function preview during runtime
+ this.reducedMotion.addEventListener(
+ "change",
+ this.onPrefersReducedMotionChange
+ );
+
+ this._initEvents();
+}
+
+exports.CubicBezierWidget = CubicBezierWidget;
+
+CubicBezierWidget.prototype = {
+ _initMarkup() {
+ const doc = this.parent.ownerDocument;
+
+ const wrap = doc.createElementNS(XHTML_NS, "div");
+ wrap.className = "display-wrap";
+
+ const plane = doc.createElementNS(XHTML_NS, "div");
+ plane.className = "coordinate-plane";
+
+ const p1 = doc.createElementNS(XHTML_NS, "button");
+ p1.className = "control-point";
+ plane.appendChild(p1);
+
+ const p2 = doc.createElementNS(XHTML_NS, "button");
+ p2.className = "control-point";
+ plane.appendChild(p2);
+
+ const curve = doc.createElementNS(XHTML_NS, "canvas");
+ curve.setAttribute("width", 150);
+ curve.setAttribute("height", 370);
+ curve.className = "curve";
+
+ plane.appendChild(curve);
+ wrap.appendChild(plane);
+
+ this.parent.appendChild(wrap);
+
+ return {
+ p1,
+ p2,
+ curve,
+ };
+ },
+
+ onPrefersReducedMotionChange(event) {
+ // if prefers-reduced-motion is enabled destroy timing function preview
+ // else create it if it does not exist
+ if (event.matches) {
+ if (this.timingPreview) {
+ this.timingPreview.destroy();
+ }
+ this.timingPreview = undefined;
+ } else if (!this.timingPreview) {
+ this.timingPreview = new TimingFunctionPreviewWidget(this.parent);
+ }
+ },
+
+ _removeMarkup() {
+ this.parent.querySelector(".display-wrap").remove();
+ },
+
+ _initEvents() {
+ this.p1.addEventListener("mousedown", this._onPointMouseDown);
+ this.p2.addEventListener("mousedown", this._onPointMouseDown);
+
+ this.p1.addEventListener("keydown", this._onPointKeyDown);
+ this.p2.addEventListener("keydown", this._onPointKeyDown);
+
+ this.curve.addEventListener("click", this._onCurveClick);
+
+ this.presets.on("new-coordinates", this._onNewCoordinates);
+ },
+
+ _removeEvents() {
+ this.p1.removeEventListener("mousedown", this._onPointMouseDown);
+ this.p2.removeEventListener("mousedown", this._onPointMouseDown);
+
+ this.p1.removeEventListener("keydown", this._onPointKeyDown);
+ this.p2.removeEventListener("keydown", this._onPointKeyDown);
+
+ this.curve.removeEventListener("click", this._onCurveClick);
+
+ this.presets.off("new-coordinates", this._onNewCoordinates);
+ },
+
+ _onPointMouseDown(event) {
+ // Updating the boundingbox in case it has changed
+ this.curveBoundingBox = this.curve.getBoundingClientRect();
+
+ const point = event.target;
+ const doc = point.ownerDocument;
+ const self = this;
+
+ doc.onmousemove = function drag(e) {
+ let x = e.pageX;
+ const y = e.pageY;
+ const left = self.curveBoundingBox.left;
+ const top = self.curveBoundingBox.top;
+
+ if (x === 0 && y == 0) {
+ return;
+ }
+
+ // Constrain x
+ x = Math.min(Math.max(left, x), left + self.curveBoundingBox.width);
+
+ point.style.left = x - left + "px";
+ point.style.top = y - top + "px";
+
+ self._updateFromPoints();
+ };
+
+ doc.onmouseup = function () {
+ point.focus();
+ doc.onmousemove = doc.onmouseup = null;
+ };
+ },
+
+ _onPointKeyDown(event) {
+ const point = event.target;
+ const code = event.keyCode;
+
+ if (code >= 37 && code <= 40) {
+ event.preventDefault();
+
+ // Arrow keys pressed
+ const left = parseInt(point.style.left, 10);
+ const top = parseInt(point.style.top, 10);
+ const offset = 3 * (event.shiftKey ? 10 : 1);
+
+ switch (code) {
+ case 37:
+ point.style.left = left - offset + "px";
+ break;
+ case 38:
+ point.style.top = top - offset + "px";
+ break;
+ case 39:
+ point.style.left = left + offset + "px";
+ break;
+ case 40:
+ point.style.top = top + offset + "px";
+ break;
+ }
+
+ this._updateFromPoints();
+ }
+ },
+
+ _onCurveClick(event) {
+ this.curveBoundingBox = this.curve.getBoundingClientRect();
+
+ const left = this.curveBoundingBox.left;
+ const top = this.curveBoundingBox.top;
+ const x = event.pageX - left;
+ const y = event.pageY - top;
+
+ // Find which point is closer
+ const distP1 = distance(
+ x,
+ y,
+ parseInt(this.p1.style.left, 10),
+ parseInt(this.p1.style.top, 10)
+ );
+ const distP2 = distance(
+ x,
+ y,
+ parseInt(this.p2.style.left, 10),
+ parseInt(this.p2.style.top, 10)
+ );
+
+ const point = distP1 < distP2 ? this.p1 : this.p2;
+ point.style.left = x + "px";
+ point.style.top = y + "px";
+
+ this._updateFromPoints();
+ },
+
+ _onNewCoordinates(coordinates) {
+ this.coordinates = coordinates;
+ },
+
+ /**
+ * Get the current point coordinates and redraw the curve to match
+ */
+ _updateFromPoints() {
+ // Get the new coordinates from the point's offsets
+ let coordinates = this.bezierCanvas.offsetsToCoordinates(this.p1);
+ coordinates = coordinates.concat(
+ this.bezierCanvas.offsetsToCoordinates(this.p2)
+ );
+
+ this.presets.refreshMenu(coordinates);
+ this._redraw(coordinates);
+ },
+
+ /**
+ * Redraw the curve
+ * @param {Array} coordinates The array of control point coordinates
+ */
+ _redraw(coordinates) {
+ // Provide a new CubicBezier to the canvas and plot the curve
+ this.bezierCanvas.bezier = new CubicBezier(coordinates);
+ this.bezierCanvas.plot();
+ this.emit("updated", this.bezierCanvas.bezier);
+
+ if (this.timingPreview) {
+ this.timingPreview.preview(this.bezierCanvas.bezier.toString());
+ }
+ },
+
+ /**
+ * Set new coordinates for the control points and redraw the curve
+ * @param {Array} coordinates
+ */
+ set coordinates(coordinates) {
+ this._redraw(coordinates);
+
+ // Move the points
+ const offsets = this.bezierCanvas.offsets;
+ this.p1.style.left = offsets[0].left;
+ this.p1.style.top = offsets[0].top;
+ this.p2.style.left = offsets[1].left;
+ this.p2.style.top = offsets[1].top;
+ },
+
+ /**
+ * Set new coordinates for the control point and redraw the curve
+ * @param {String} value A string value. E.g. "linear",
+ * "cubic-bezier(0,0,1,1)"
+ */
+ set cssCubicBezierValue(value) {
+ if (!value) {
+ return;
+ }
+
+ value = value.trim();
+
+ // Try with one of the predefined values
+ const coordinates = parseTimingFunction(value);
+
+ this.presets.refreshMenu(coordinates);
+ this.coordinates = coordinates;
+ },
+
+ destroy() {
+ this._removeEvents();
+ this._removeMarkup();
+
+ // remove prefers-reduced-motion event listener
+ this.reducedMotion.removeEventListener(
+ "change",
+ this.onPrefersReducedMotionChange
+ );
+ this.reducedMotion = null;
+
+ if (this.timingPreview) {
+ this.timingPreview.destroy();
+ this.timingPreview = null;
+ }
+ this.presets.destroy();
+
+ this.curve = this.p1 = this.p2 = null;
+ },
+};
+
+/**
+ * CubicBezierPreset widget.
+ * Builds a menu of presets from CubicBezierPresets
+ * @param {DOMNode} parent The container where the preset panel should be
+ * created
+ *
+ * Emits "new-coordinate" event along with the coordinates
+ * whenever a preset is selected.
+ */
+function CubicBezierPresetWidget(parent) {
+ this.parent = parent;
+
+ const { presetPane, presets, categories } = this._initMarkup();
+ this.presetPane = presetPane;
+ this.presets = presets;
+ this.categories = categories;
+
+ this._activeCategory = null;
+ this._activePresetList = null;
+ this._activePreset = null;
+
+ this._onCategoryClick = this._onCategoryClick.bind(this);
+ this._onPresetClick = this._onPresetClick.bind(this);
+
+ EventEmitter.decorate(this);
+ this._initEvents();
+}
+
+exports.CubicBezierPresetWidget = CubicBezierPresetWidget;
+
+CubicBezierPresetWidget.prototype = {
+ /*
+ * Constructs a list of all preset categories and a list
+ * of presets for each category.
+ *
+ * High level markup:
+ * div .preset-pane
+ * div .preset-categories
+ * div .category
+ * div .category
+ * ...
+ * div .preset-container
+ * div .presetList
+ * div .preset
+ * ...
+ * div .presetList
+ * div .preset
+ * ...
+ */
+ _initMarkup() {
+ const doc = this.parent.ownerDocument;
+
+ const presetPane = doc.createElementNS(XHTML_NS, "div");
+ presetPane.className = "preset-pane";
+
+ const categoryList = doc.createElementNS(XHTML_NS, "div");
+ categoryList.id = "preset-categories";
+
+ const presetContainer = doc.createElementNS(XHTML_NS, "div");
+ presetContainer.id = "preset-container";
+
+ Object.keys(PRESETS).forEach(categoryLabel => {
+ const category = this._createCategory(categoryLabel);
+ categoryList.appendChild(category);
+
+ const presetList = this._createPresetList(categoryLabel);
+ presetContainer.appendChild(presetList);
+ });
+
+ presetPane.appendChild(categoryList);
+ presetPane.appendChild(presetContainer);
+
+ this.parent.appendChild(presetPane);
+
+ const allCategories = presetPane.querySelectorAll(".category");
+ const allPresets = presetPane.querySelectorAll(".preset");
+
+ return {
+ presetPane,
+ presets: allPresets,
+ categories: allCategories,
+ };
+ },
+
+ _createCategory(categoryLabel) {
+ const doc = this.parent.ownerDocument;
+
+ const category = doc.createElementNS(XHTML_NS, "div");
+ category.id = categoryLabel;
+ category.classList.add("category");
+
+ const categoryDisplayLabel = this._normalizeCategoryLabel(categoryLabel);
+ category.textContent = categoryDisplayLabel;
+ category.setAttribute("title", categoryDisplayLabel);
+
+ return category;
+ },
+
+ _normalizeCategoryLabel(categoryLabel) {
+ return categoryLabel.replace("/-/g", " ");
+ },
+
+ _createPresetList(categoryLabel) {
+ const doc = this.parent.ownerDocument;
+
+ const presetList = doc.createElementNS(XHTML_NS, "div");
+ presetList.id = "preset-category-" + categoryLabel;
+ presetList.classList.add("preset-list");
+
+ Object.keys(PRESETS[categoryLabel]).forEach(presetLabel => {
+ const preset = this._createPreset(categoryLabel, presetLabel);
+ presetList.appendChild(preset);
+ });
+
+ return presetList;
+ },
+
+ _createPreset(categoryLabel, presetLabel) {
+ const doc = this.parent.ownerDocument;
+
+ const preset = doc.createElementNS(XHTML_NS, "div");
+ preset.classList.add("preset");
+ preset.id = presetLabel;
+ preset.coordinates = PRESETS[categoryLabel][presetLabel];
+ // Create preset preview
+ const curve = doc.createElementNS(XHTML_NS, "canvas");
+ const bezier = new CubicBezier(preset.coordinates);
+ curve.setAttribute("height", 50);
+ curve.setAttribute("width", 50);
+ preset.bezierCanvas = new BezierCanvas(curve, bezier, [0.15, 0]);
+ preset.bezierCanvas.plot({
+ drawHandles: false,
+ bezierThickness: 0.025,
+ });
+ preset.appendChild(curve);
+
+ // Create preset label
+ const presetLabelElem = doc.createElementNS(XHTML_NS, "p");
+ const presetDisplayLabel = this._normalizePresetLabel(
+ categoryLabel,
+ presetLabel
+ );
+ presetLabelElem.textContent = presetDisplayLabel;
+ preset.appendChild(presetLabelElem);
+ preset.setAttribute("title", presetDisplayLabel);
+
+ return preset;
+ },
+
+ _normalizePresetLabel(categoryLabel, presetLabel) {
+ return presetLabel.replace(categoryLabel + "-", "").replace("/-/g", " ");
+ },
+
+ _initEvents() {
+ for (const category of this.categories) {
+ category.addEventListener("click", this._onCategoryClick);
+ }
+
+ for (const preset of this.presets) {
+ preset.addEventListener("click", this._onPresetClick);
+ }
+ },
+
+ _removeEvents() {
+ for (const category of this.categories) {
+ category.removeEventListener("click", this._onCategoryClick);
+ }
+
+ for (const preset of this.presets) {
+ preset.removeEventListener("click", this._onPresetClick);
+ }
+ },
+
+ _onPresetClick(event) {
+ this.emit("new-coordinates", event.currentTarget.coordinates);
+ this.activePreset = event.currentTarget;
+ },
+
+ _onCategoryClick(event) {
+ this.activeCategory = event.target;
+ },
+
+ _setActivePresetList(presetListId) {
+ const presetList = this.presetPane.querySelector("#" + presetListId);
+ swapClassName("active-preset-list", this._activePresetList, presetList);
+ this._activePresetList = presetList;
+ },
+
+ set activeCategory(category) {
+ swapClassName("active-category", this._activeCategory, category);
+ this._activeCategory = category;
+ this._setActivePresetList("preset-category-" + category.id);
+ },
+
+ get activeCategory() {
+ return this._activeCategory;
+ },
+
+ set activePreset(preset) {
+ swapClassName("active-preset", this._activePreset, preset);
+ this._activePreset = preset;
+ },
+
+ get activePreset() {
+ return this._activePreset;
+ },
+
+ /**
+ * Called by CubicBezierWidget onload and when
+ * the curve is modified via the canvas.
+ * Attempts to match the new user setting with an
+ * existing preset.
+ * @param {Array} coordinates new coords [i, j, k, l]
+ */
+ refreshMenu(coordinates) {
+ // If we cannot find a matching preset, keep
+ // menu on last known preset category.
+ let category = this._activeCategory;
+
+ // If we cannot find a matching preset
+ // deselect any selected preset.
+ let preset = null;
+
+ // If a category has never been viewed before
+ // show the default category.
+ if (!category) {
+ category = this.parent.querySelector("#" + DEFAULT_PRESET_CATEGORY);
+ }
+
+ // If the new coordinates do match a preset,
+ // set its category and preset button as active.
+ Object.keys(PRESETS).forEach(categoryLabel => {
+ Object.keys(PRESETS[categoryLabel]).forEach(presetLabel => {
+ if (coordsAreEqual(PRESETS[categoryLabel][presetLabel], coordinates)) {
+ category = this.parent.querySelector("#" + categoryLabel);
+ preset = this.parent.querySelector("#" + presetLabel);
+ }
+ });
+ });
+
+ this.activeCategory = category;
+ this.activePreset = preset;
+ },
+
+ destroy() {
+ this._removeEvents();
+ this.parent.querySelector(".preset-pane").remove();
+ },
+};
+
+/**
+ * The TimingFunctionPreviewWidget animates a dot on a scale with a given
+ * timing-function
+ * @param {DOMNode} parent The container where this widget should go
+ */
+function TimingFunctionPreviewWidget(parent) {
+ this.previousValue = null;
+
+ this.parent = parent;
+ this._initMarkup();
+}
+
+TimingFunctionPreviewWidget.prototype = {
+ PREVIEW_DURATION: 1000,
+
+ _initMarkup() {
+ const doc = this.parent.ownerDocument;
+
+ const container = doc.createElementNS(XHTML_NS, "div");
+ container.className = "timing-function-preview";
+
+ this.dot = doc.createElementNS(XHTML_NS, "div");
+ this.dot.className = "dot";
+ container.appendChild(this.dot);
+
+ const scale = doc.createElementNS(XHTML_NS, "div");
+ scale.className = "scale";
+ container.appendChild(scale);
+
+ this.parent.appendChild(container);
+ },
+
+ destroy() {
+ this.dot.getAnimations().forEach(anim => anim.cancel());
+ this.parent.querySelector(".timing-function-preview").remove();
+ this.parent = this.dot = null;
+ },
+
+ /**
+ * Preview a new timing function. The current preview will only be stopped if
+ * the supplied function value is different from the previous one. If the
+ * supplied function is invalid, the preview will stop.
+ * @param {String} value
+ */
+ preview(value) {
+ // Don't restart the preview animation if the value is the same
+ if (value === this.previousValue) {
+ return;
+ }
+
+ if (parseTimingFunction(value)) {
+ this.restartAnimation(value);
+ }
+
+ this.previousValue = value;
+ },
+
+ /**
+ * Re-start the preview animation from the beginning.
+ * @param {String} timingFunction The value for the timing-function.
+ */
+ restartAnimation(timingFunction) {
+ // Cancel the previous animation if there was any.
+ this.dot.getAnimations().forEach(anim => anim.cancel());
+
+ // And start the new one.
+ // The animation consists of a few keyframes that move the dot to the right of the
+ // container, and then move it back to the left.
+ // It also contains some pause where the dot is semi transparent, before it moves to
+ // the right, and once again, before it comes back to the left.
+ // The timing function passed to this function is applied to the keyframes that
+ // actually move the dot. This way it can be previewed in both direction, instead of
+ // being spread over the whole animation.
+ this.dot.animate(
+ [
+ { left: "-7px", opacity: 0.5, offset: 0 },
+ { left: "-7px", opacity: 0.5, offset: 0.19 },
+ { left: "-7px", opacity: 1, offset: 0.2, easing: timingFunction },
+ { left: "143px", opacity: 1, offset: 0.5 },
+ { left: "143px", opacity: 0.5, offset: 0.51 },
+ { left: "143px", opacity: 0.5, offset: 0.7 },
+ { left: "143px", opacity: 1, offset: 0.71, easing: timingFunction },
+ { left: "-7px", opacity: 1, offset: 1 },
+ ],
+ {
+ duration: this.PREVIEW_DURATION * 2,
+ iterations: Infinity,
+ }
+ );
+ },
+};
+
+// Helpers
+
+function getPadding(padding) {
+ const p = typeof padding === "number" ? [padding] : padding;
+
+ if (p.length === 1) {
+ p[1] = p[0];
+ }
+
+ if (p.length === 2) {
+ p[2] = p[0];
+ }
+
+ if (p.length === 3) {
+ p[3] = p[1];
+ }
+
+ return p;
+}
+
+function distance(x1, y1, x2, y2) {
+ return Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
+}
+
+/**
+ * Parse a string to see whether it is a valid timing function.
+ * If it is, return the coordinates as an array.
+ * Otherwise, return undefined.
+ * @param {String} value
+ * @return {Array} of coordinates, or undefined
+ */
+function parseTimingFunction(value) {
+ if (value in PREDEFINED) {
+ return PREDEFINED[value];
+ }
+
+ const tokenStream = getCSSLexer(value);
+ const getNextToken = () => {
+ while (true) {
+ const token = tokenStream.nextToken();
+ if (
+ !token ||
+ (token.tokenType !== "whitespace" && token.tokenType !== "comment")
+ ) {
+ return token;
+ }
+ }
+ };
+
+ let token = getNextToken();
+ if (token.tokenType !== "function" || token.text !== "cubic-bezier") {
+ return undefined;
+ }
+
+ const result = [];
+ for (let i = 0; i < 4; ++i) {
+ token = getNextToken();
+ if (!token || token.tokenType !== "number") {
+ return undefined;
+ }
+ result.push(token.number);
+
+ token = getNextToken();
+ if (
+ !token ||
+ token.tokenType !== "symbol" ||
+ token.text !== (i == 3 ? ")" : ",")
+ ) {
+ return undefined;
+ }
+ }
+
+ return result;
+}
+
+exports.parseTimingFunction = parseTimingFunction;
+
+/**
+ * Removes a class from a node and adds it to another.
+ * @param {String} className the class to swap
+ * @param {DOMNode} from the node to remove the class from
+ * @param {DOMNode} to the node to add the class to
+ */
+function swapClassName(className, from, to) {
+ if (from !== null) {
+ from.classList.remove(className);
+ }
+
+ if (to !== null) {
+ to.classList.add(className);
+ }
+}
+
+/**
+ * Compares two arrays of coordinates [i, j, k, l]
+ * @param {Array} c1 first coordinate array to compare
+ * @param {Array} c2 second coordinate array to compare
+ * @return {Boolean}
+ */
+function coordsAreEqual(c1, c2) {
+ return c1.reduce((prev, curr, index) => prev && curr === c2[index], true);
+}
diff --git a/devtools/client/shared/widgets/FilterWidget.js b/devtools/client/shared/widgets/FilterWidget.js
new file mode 100644
index 0000000000..bb23bdfeca
--- /dev/null
+++ b/devtools/client/shared/widgets/FilterWidget.js
@@ -0,0 +1,1131 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * This is a CSS Filter Editor widget used
+ * for Rule View's filter swatches
+ */
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const STRINGS_URI = "devtools/client/locales/filterwidget.properties";
+const L10N = new LocalizationHelper(STRINGS_URI);
+
+const {
+ cssTokenizer,
+} = require("resource://devtools/shared/css/parsing-utils.js");
+
+const asyncStorage = require("resource://devtools/shared/async-storage.js");
+
+const DEFAULT_FILTER_TYPE = "length";
+const UNIT_MAPPING = {
+ percentage: "%",
+ length: "px",
+ angle: "deg",
+ string: "",
+};
+
+const FAST_VALUE_MULTIPLIER = 10;
+const SLOW_VALUE_MULTIPLIER = 0.1;
+const DEFAULT_VALUE_MULTIPLIER = 1;
+
+const LIST_PADDING = 7;
+const LIST_ITEM_HEIGHT = 32;
+
+const filterList = [
+ {
+ name: "blur",
+ range: [0, Infinity],
+ type: "length",
+ },
+ {
+ name: "brightness",
+ range: [0, Infinity],
+ type: "percentage",
+ },
+ {
+ name: "contrast",
+ range: [0, Infinity],
+ type: "percentage",
+ },
+ {
+ name: "drop-shadow",
+ placeholder: L10N.getStr("dropShadowPlaceholder"),
+ type: "string",
+ },
+ {
+ name: "grayscale",
+ range: [0, 100],
+ type: "percentage",
+ },
+ {
+ name: "hue-rotate",
+ range: [0, Infinity],
+ type: "angle",
+ },
+ {
+ name: "invert",
+ range: [0, 100],
+ type: "percentage",
+ },
+ {
+ name: "opacity",
+ range: [0, 100],
+ type: "percentage",
+ },
+ {
+ name: "saturate",
+ range: [0, Infinity],
+ type: "percentage",
+ },
+ {
+ name: "sepia",
+ range: [0, 100],
+ type: "percentage",
+ },
+ {
+ name: "url",
+ placeholder: "example.svg#c1",
+ type: "string",
+ },
+];
+
+// Valid values that shouldn't be parsed for filters.
+const SPECIAL_VALUES = new Set(["none", "unset", "initial", "inherit"]);
+
+/**
+ * A CSS Filter editor widget used to add/remove/modify
+ * filters.
+ *
+ * Normally, it takes a CSS filter value as input, parses it
+ * and creates the required elements / bindings.
+ *
+ * You can, however, use add/remove/update methods manually.
+ * See each method's comments for more details
+ *
+ * @param {Node} el
+ * The widget container.
+ * @param {String} value
+ * CSS filter value
+ */
+function CSSFilterEditorWidget(el, value = "") {
+ this.doc = el.ownerDocument;
+ this.win = this.doc.defaultView;
+ this.el = el;
+ this._cssIsValid = (name, val) => {
+ return this.win.CSS.supports(name, val);
+ };
+
+ this._addButtonClick = this._addButtonClick.bind(this);
+ this._removeButtonClick = this._removeButtonClick.bind(this);
+ this._mouseMove = this._mouseMove.bind(this);
+ this._mouseUp = this._mouseUp.bind(this);
+ this._mouseDown = this._mouseDown.bind(this);
+ this._keyDown = this._keyDown.bind(this);
+ this._input = this._input.bind(this);
+ this._presetClick = this._presetClick.bind(this);
+ this._savePreset = this._savePreset.bind(this);
+ this._togglePresets = this._togglePresets.bind(this);
+ this._resetFocus = this._resetFocus.bind(this);
+
+ // Passed to asyncStorage, requires binding
+ this.renderPresets = this.renderPresets.bind(this);
+
+ this._initMarkup();
+ this._buildFilterItemMarkup();
+ this._buildPresetItemMarkup();
+ this._addEventListeners();
+
+ EventEmitter.decorate(this);
+
+ this.filters = [];
+ this.setCssValue(value);
+ this.renderPresets();
+}
+
+exports.CSSFilterEditorWidget = CSSFilterEditorWidget;
+
+CSSFilterEditorWidget.prototype = {
+ _initMarkup() {
+ // The following structure is created:
+ // <div class="filters-list">
+ // <div id="filters"></div>
+ // <div class="footer">
+ // <select value="">
+ // <option value="">${filterListSelectPlaceholder}</option>
+ // </select>
+ // <button id="add-filter" class="add">${addNewFilterButton}</button>
+ // <button id="toggle-presets">${presetsToggleButton}</button>
+ // </div>
+ // </div>
+ // <div class="presets-list">
+ // <div id="presets"></div>
+ // <div class="footer">
+ // <input value="" class="devtools-textinput"
+ // placeholder="${newPresetPlaceholder}"></input>
+ // <button class="add">${savePresetButton}</button>
+ // </div>
+ // </div>
+ const content = this.doc.createDocumentFragment();
+
+ const filterListWrapper = this.doc.createElementNS(XHTML_NS, "div");
+ filterListWrapper.classList.add("filters-list");
+ content.appendChild(filterListWrapper);
+
+ this.filterList = this.doc.createElementNS(XHTML_NS, "div");
+ this.filterList.setAttribute("id", "filters");
+ filterListWrapper.appendChild(this.filterList);
+
+ const filterListFooter = this.doc.createElementNS(XHTML_NS, "div");
+ filterListFooter.classList.add("footer");
+ filterListWrapper.appendChild(filterListFooter);
+
+ this.filterSelect = this.doc.createElementNS(XHTML_NS, "select");
+ this.filterSelect.setAttribute("value", "");
+ filterListFooter.appendChild(this.filterSelect);
+
+ const filterListPlaceholder = this.doc.createElementNS(XHTML_NS, "option");
+ filterListPlaceholder.setAttribute("value", "");
+ filterListPlaceholder.textContent = L10N.getStr(
+ "filterListSelectPlaceholder"
+ );
+ this.filterSelect.appendChild(filterListPlaceholder);
+
+ const addFilter = this.doc.createElementNS(XHTML_NS, "button");
+ addFilter.setAttribute("id", "add-filter");
+ addFilter.classList.add("add");
+ addFilter.textContent = L10N.getStr("addNewFilterButton");
+ filterListFooter.appendChild(addFilter);
+
+ this.togglePresets = this.doc.createElementNS(XHTML_NS, "button");
+ this.togglePresets.setAttribute("id", "toggle-presets");
+ this.togglePresets.textContent = L10N.getStr("presetsToggleButton");
+ filterListFooter.appendChild(this.togglePresets);
+
+ const presetListWrapper = this.doc.createElementNS(XHTML_NS, "div");
+ presetListWrapper.classList.add("presets-list");
+ content.appendChild(presetListWrapper);
+
+ this.presetList = this.doc.createElementNS(XHTML_NS, "div");
+ this.presetList.setAttribute("id", "presets");
+ presetListWrapper.appendChild(this.presetList);
+
+ const presetListFooter = this.doc.createElementNS(XHTML_NS, "div");
+ presetListFooter.classList.add("footer");
+ presetListWrapper.appendChild(presetListFooter);
+
+ this.addPresetInput = this.doc.createElementNS(XHTML_NS, "input");
+ this.addPresetInput.setAttribute("value", "");
+ this.addPresetInput.classList.add("devtools-textinput");
+ this.addPresetInput.setAttribute(
+ "placeholder",
+ L10N.getStr("newPresetPlaceholder")
+ );
+ presetListFooter.appendChild(this.addPresetInput);
+
+ this.addPresetButton = this.doc.createElementNS(XHTML_NS, "button");
+ this.addPresetButton.classList.add("add");
+ this.addPresetButton.textContent = L10N.getStr("savePresetButton");
+ presetListFooter.appendChild(this.addPresetButton);
+
+ this.el.appendChild(content);
+
+ this._populateFilterSelect();
+ },
+
+ _destroyMarkup() {
+ this._filterItemMarkup.remove();
+ this.el.remove();
+ this.el = this.filterList = this._filterItemMarkup = null;
+ this.presetList = this.togglePresets = this.filterSelect = null;
+ this.addPresetButton = null;
+ },
+
+ destroy() {
+ this._removeEventListeners();
+ this._destroyMarkup();
+ },
+
+ /**
+ * Creates <option> elements for each filter definition
+ * in filterList
+ */
+ _populateFilterSelect() {
+ const select = this.filterSelect;
+ filterList.forEach(filter => {
+ const option = this.doc.createElementNS(XHTML_NS, "option");
+ option.textContent = option.value = filter.name;
+ select.appendChild(option);
+ });
+ },
+
+ /**
+ * Creates a template for filter elements which is cloned and used in render
+ */
+ _buildFilterItemMarkup() {
+ const base = this.doc.createElementNS(XHTML_NS, "div");
+ base.className = "filter";
+
+ const name = this.doc.createElementNS(XHTML_NS, "div");
+ name.className = "filter-name";
+
+ const value = this.doc.createElementNS(XHTML_NS, "div");
+ value.className = "filter-value";
+
+ const drag = this.doc.createElementNS(XHTML_NS, "i");
+ drag.title = L10N.getStr("dragHandleTooltipText");
+
+ const label = this.doc.createElementNS(XHTML_NS, "label");
+
+ name.appendChild(drag);
+ name.appendChild(label);
+
+ const unitPreview = this.doc.createElementNS(XHTML_NS, "span");
+ const input = this.doc.createElementNS(XHTML_NS, "input");
+ input.classList.add("devtools-textinput");
+
+ value.appendChild(input);
+ value.appendChild(unitPreview);
+
+ const removeButton = this.doc.createElementNS(XHTML_NS, "button");
+ removeButton.className = "remove-button";
+
+ base.appendChild(name);
+ base.appendChild(value);
+ base.appendChild(removeButton);
+
+ this._filterItemMarkup = base;
+ },
+
+ _buildPresetItemMarkup() {
+ const base = this.doc.createElementNS(XHTML_NS, "div");
+ base.classList.add("preset");
+
+ const name = this.doc.createElementNS(XHTML_NS, "label");
+ base.appendChild(name);
+
+ const value = this.doc.createElementNS(XHTML_NS, "span");
+ base.appendChild(value);
+
+ const removeButton = this.doc.createElementNS(XHTML_NS, "button");
+ removeButton.classList.add("remove-button");
+
+ base.appendChild(removeButton);
+
+ this._presetItemMarkup = base;
+ },
+
+ _addEventListeners() {
+ this.addButton = this.el.querySelector("#add-filter");
+ this.addButton.addEventListener("click", this._addButtonClick);
+ this.filterList.addEventListener("click", this._removeButtonClick);
+ this.filterList.addEventListener("mousedown", this._mouseDown);
+ this.filterList.addEventListener("keydown", this._keyDown);
+ this.el.addEventListener("mousedown", this._resetFocus);
+
+ this.presetList.addEventListener("click", this._presetClick);
+ this.togglePresets.addEventListener("click", this._togglePresets);
+ this.addPresetButton.addEventListener("click", this._savePreset);
+
+ // These events are event delegators for
+ // drag-drop re-ordering and label-dragging
+ this.win.addEventListener("mousemove", this._mouseMove);
+ this.win.addEventListener("mouseup", this._mouseUp);
+
+ // Used to workaround float-precision problems
+ this.filterList.addEventListener("input", this._input);
+ },
+
+ _removeEventListeners() {
+ this.addButton.removeEventListener("click", this._addButtonClick);
+ this.filterList.removeEventListener("click", this._removeButtonClick);
+ this.filterList.removeEventListener("mousedown", this._mouseDown);
+ this.filterList.removeEventListener("keydown", this._keyDown);
+ this.el.removeEventListener("mousedown", this._resetFocus);
+
+ this.presetList.removeEventListener("click", this._presetClick);
+ this.togglePresets.removeEventListener("click", this._togglePresets);
+ this.addPresetButton.removeEventListener("click", this._savePreset);
+
+ // These events are used for drag drop re-ordering
+ this.win.removeEventListener("mousemove", this._mouseMove);
+ this.win.removeEventListener("mouseup", this._mouseUp);
+
+ // Used to workaround float-precision problems
+ this.filterList.removeEventListener("input", this._input);
+ },
+
+ _getFilterElementIndex(el) {
+ return [...this.filterList.children].indexOf(el);
+ },
+
+ _keyDown(e) {
+ if (
+ e.target.tagName.toLowerCase() !== "input" ||
+ (e.keyCode !== 40 && e.keyCode !== 38)
+ ) {
+ return;
+ }
+ const input = e.target;
+
+ const direction = e.keyCode === 40 ? -1 : 1;
+
+ let multiplier = DEFAULT_VALUE_MULTIPLIER;
+ if (e.altKey) {
+ multiplier = SLOW_VALUE_MULTIPLIER;
+ } else if (e.shiftKey) {
+ multiplier = FAST_VALUE_MULTIPLIER;
+ }
+
+ const filterEl = e.target.closest(".filter");
+ const index = this._getFilterElementIndex(filterEl);
+ const filter = this.filters[index];
+
+ // Filters that have units are number-type filters. For them,
+ // the value can be incremented/decremented simply.
+ // For other types of filters (e.g. drop-shadow) we need to check
+ // if the keydown happened close to a number first.
+ if (filter.unit) {
+ const startValue = parseFloat(e.target.value);
+ let value = startValue + direction * multiplier;
+
+ const [min, max] = this._definition(filter.name).range;
+ if (value < min) {
+ value = min;
+ } else if (value > max) {
+ value = max;
+ }
+
+ input.value = fixFloat(value);
+
+ this.updateValueAt(index, value);
+ } else {
+ let selectionStart = input.selectionStart;
+ const num = getNeighbourNumber(input.value, selectionStart);
+ if (!num) {
+ return;
+ }
+
+ let { start, end, value } = num;
+
+ const split = input.value.split("");
+ let computed = fixFloat(value + direction * multiplier);
+ const dotIndex = computed.indexOf(".0");
+ if (dotIndex > -1) {
+ computed = computed.slice(0, -2);
+
+ selectionStart =
+ selectionStart > start + dotIndex ? start + dotIndex : selectionStart;
+ }
+ split.splice(start, end - start, computed);
+
+ value = split.join("");
+ input.value = value;
+ this.updateValueAt(index, value);
+ input.setSelectionRange(selectionStart, selectionStart);
+ }
+ e.preventDefault();
+ },
+
+ _input(e) {
+ const filterEl = e.target.closest(".filter");
+ const index = this._getFilterElementIndex(filterEl);
+ const filter = this.filters[index];
+ const def = this._definition(filter.name);
+
+ if (def.type !== "string") {
+ e.target.value = fixFloat(e.target.value);
+ }
+ this.updateValueAt(index, e.target.value);
+ },
+
+ _mouseDown(e) {
+ const filterEl = e.target.closest(".filter");
+
+ // re-ordering drag handle
+ if (e.target.tagName.toLowerCase() === "i") {
+ this.isReorderingFilter = true;
+ filterEl.startingY = e.pageY;
+ filterEl.classList.add("dragging");
+
+ this.el.classList.add("dragging");
+ // label-dragging
+ } else if (e.target.classList.contains("devtools-draglabel")) {
+ const label = e.target;
+ const input = filterEl.querySelector("input");
+ const index = this._getFilterElementIndex(filterEl);
+
+ this._dragging = {
+ index,
+ label,
+ input,
+ startX: e.pageX,
+ };
+
+ this.isDraggingLabel = true;
+ }
+ },
+
+ _addButtonClick() {
+ const select = this.filterSelect;
+ if (!select.value) {
+ return;
+ }
+
+ const key = select.value;
+ this.add(key, null);
+
+ this.render();
+ },
+
+ _removeButtonClick(e) {
+ const isRemoveButton = e.target.classList.contains("remove-button");
+ if (!isRemoveButton) {
+ return;
+ }
+
+ const filterEl = e.target.closest(".filter");
+ const index = this._getFilterElementIndex(filterEl);
+ this.removeAt(index);
+ },
+
+ _mouseMove(e) {
+ if (this.isReorderingFilter) {
+ this._dragFilterElement(e);
+ } else if (this.isDraggingLabel) {
+ this._dragLabel(e);
+ }
+ },
+
+ _dragFilterElement(e) {
+ const rect = this.filterList.getBoundingClientRect();
+ const top = e.pageY - LIST_PADDING;
+ const bottom = e.pageY + LIST_PADDING;
+ // don't allow dragging over top/bottom of list
+ if (top < rect.top || bottom > rect.bottom) {
+ return;
+ }
+
+ const filterEl = this.filterList.querySelector(".dragging");
+
+ const delta = e.pageY - filterEl.startingY;
+ filterEl.style.top = delta + "px";
+
+ // change is the number of _steps_ taken from initial position
+ // i.e. how many elements we have passed
+ let change = delta / LIST_ITEM_HEIGHT;
+ if (change > 0) {
+ change = Math.floor(change);
+ } else if (change < 0) {
+ change = Math.ceil(change);
+ }
+
+ const children = this.filterList.children;
+ const index = [...children].indexOf(filterEl);
+ const destination = index + change;
+
+ // If we're moving out, or there's no change at all, stop and return
+ if (destination >= children.length || destination < 0 || change === 0) {
+ return;
+ }
+
+ // Re-order filter objects
+ swapArrayIndices(this.filters, index, destination);
+
+ // Re-order the dragging element in markup
+ const target =
+ change > 0 ? children[destination + 1] : children[destination];
+ if (target) {
+ this.filterList.insertBefore(filterEl, target);
+ } else {
+ this.filterList.appendChild(filterEl);
+ }
+
+ filterEl.removeAttribute("style");
+
+ const currentPosition = change * LIST_ITEM_HEIGHT;
+ filterEl.startingY = e.pageY + currentPosition - delta;
+ },
+
+ _dragLabel(e) {
+ const dragging = this._dragging;
+
+ const input = dragging.input;
+
+ let multiplier = DEFAULT_VALUE_MULTIPLIER;
+
+ if (e.altKey) {
+ multiplier = SLOW_VALUE_MULTIPLIER;
+ } else if (e.shiftKey) {
+ multiplier = FAST_VALUE_MULTIPLIER;
+ }
+
+ dragging.lastX = e.pageX;
+ const delta = e.pageX - dragging.startX;
+ const startValue = parseFloat(input.value);
+ let value = startValue + delta * multiplier;
+
+ const filter = this.filters[dragging.index];
+ const [min, max] = this._definition(filter.name).range;
+ if (value < min) {
+ value = min;
+ } else if (value > max) {
+ value = max;
+ }
+
+ input.value = fixFloat(value);
+
+ dragging.startX = e.pageX;
+
+ this.updateValueAt(dragging.index, value);
+ },
+
+ _mouseUp() {
+ // Label-dragging is disabled on mouseup
+ this._dragging = null;
+ this.isDraggingLabel = false;
+
+ // Filter drag/drop needs more cleaning
+ if (!this.isReorderingFilter) {
+ return;
+ }
+ const filterEl = this.filterList.querySelector(".dragging");
+
+ this.isReorderingFilter = false;
+ filterEl.classList.remove("dragging");
+ this.el.classList.remove("dragging");
+ filterEl.removeAttribute("style");
+
+ this.emit("updated", this.getCssValue());
+ this.render();
+ },
+
+ _presetClick(e) {
+ const el = e.target;
+ const preset = el.closest(".preset");
+ if (!preset) {
+ return;
+ }
+
+ const id = +preset.dataset.id;
+
+ this.getPresets().then(presets => {
+ if (el.classList.contains("remove-button")) {
+ // If the click happened on the remove button.
+ presets.splice(id, 1);
+ this.setPresets(presets).then(this.renderPresets, console.error);
+ } else {
+ // Or if the click happened on a preset.
+ const p = presets[id];
+
+ this.setCssValue(p.value);
+ this.addPresetInput.value = p.name;
+ }
+ }, console.error);
+ },
+
+ _togglePresets() {
+ this.el.classList.toggle("show-presets");
+ this.emit("render");
+ },
+
+ _savePreset(e) {
+ e.preventDefault();
+
+ const name = this.addPresetInput.value;
+ const value = this.getCssValue();
+
+ if (!name || !value || SPECIAL_VALUES.has(value)) {
+ this.emit("preset-save-error");
+ return;
+ }
+
+ this.getPresets().then(presets => {
+ const index = presets.findIndex(preset => preset.name === name);
+
+ if (index > -1) {
+ presets[index].value = value;
+ } else {
+ presets.push({ name, value });
+ }
+
+ this.setPresets(presets).then(this.renderPresets, console.error);
+ }, console.error);
+ },
+
+ /**
+ * Workaround needed to reset the focus when using a HTML select inside a XUL panel.
+ * See Bug 1294366.
+ */
+ _resetFocus() {
+ this.filterSelect.ownerDocument.defaultView.focus();
+ },
+
+ /**
+ * Clears the list and renders filters, binding required events.
+ * There are some delegated events bound in _addEventListeners method
+ */
+ render() {
+ if (!this.filters.length) {
+ // eslint-disable-next-line no-unsanitized/property
+ this.filterList.innerHTML = `<p> ${L10N.getStr("emptyFilterList")} <br />
+ ${L10N.getStr("addUsingList")} </p>`;
+ this.emit("render");
+ return;
+ }
+
+ this.filterList.innerHTML = "";
+
+ const base = this._filterItemMarkup;
+
+ for (const filter of this.filters) {
+ const def = this._definition(filter.name);
+
+ const el = base.cloneNode(true);
+
+ const [name, value] = el.children;
+ const label = name.children[1];
+ const [input, unitPreview] = value.children;
+
+ let min, max;
+ if (def.range) {
+ [min, max] = def.range;
+ }
+
+ label.textContent = filter.name;
+ input.value = filter.value;
+
+ switch (def.type) {
+ case "percentage":
+ case "angle":
+ case "length":
+ input.type = "number";
+ input.min = min;
+ if (max !== Infinity) {
+ input.max = max;
+ }
+ input.step = "0.1";
+ break;
+ case "string":
+ input.type = "text";
+ input.placeholder = def.placeholder;
+ break;
+ }
+
+ // use photoshop-style label-dragging
+ // and show filters' unit next to their <input>
+ if (def.type !== "string") {
+ unitPreview.textContent = filter.unit;
+
+ label.classList.add("devtools-draglabel");
+ label.title = L10N.getStr("labelDragTooltipText");
+ } else {
+ // string-type filters have no unit
+ unitPreview.remove();
+ }
+
+ this.filterList.appendChild(el);
+ }
+
+ const lastInput = this.filterList.querySelector(
+ ".filter:last-of-type input"
+ );
+ if (lastInput) {
+ lastInput.focus();
+ if (lastInput.type === "text") {
+ // move cursor to end of input
+ const end = lastInput.value.length;
+ lastInput.setSelectionRange(end, end);
+ }
+ }
+
+ this.emit("render");
+ },
+
+ renderPresets() {
+ this.getPresets().then(presets => {
+ // getPresets is async and the widget may be destroyed in between.
+ if (!this.presetList) {
+ return;
+ }
+
+ if (!presets || !presets.length) {
+ // eslint-disable-next-line no-unsanitized/property
+ this.presetList.innerHTML = `<p>${L10N.getStr("emptyPresetList")}</p>`;
+ this.emit("render");
+ return;
+ }
+ const base = this._presetItemMarkup;
+
+ this.presetList.innerHTML = "";
+
+ for (const [index, preset] of presets.entries()) {
+ const el = base.cloneNode(true);
+
+ const [label, span] = el.children;
+
+ el.dataset.id = index;
+
+ label.textContent = preset.name;
+ span.textContent = preset.value;
+
+ this.presetList.appendChild(el);
+ }
+
+ this.emit("render");
+ });
+ },
+
+ /**
+ * returns definition of a filter as defined in filterList
+ *
+ * @param {String} name
+ * filter name (e.g. blur)
+ * @return {Object}
+ * filter's definition
+ */
+ _definition(name) {
+ name = name.toLowerCase();
+ return filterList.find(a => a.name === name);
+ },
+
+ /**
+ * Parses the CSS value specified, updating widget's filters
+ *
+ * @param {String} cssValue
+ * css value to be parsed
+ */
+ setCssValue(cssValue) {
+ if (!cssValue) {
+ throw new Error("Missing CSS filter value in setCssValue");
+ }
+
+ this.filters = [];
+
+ if (SPECIAL_VALUES.has(cssValue)) {
+ this._specialValue = cssValue;
+ this.emit("updated", this.getCssValue());
+ this.render();
+ return;
+ }
+
+ for (let { name, value, quote } of tokenizeFilterValue(cssValue)) {
+ // If the specified value is invalid, replace it with the
+ // default.
+ if (name !== "url") {
+ if (!this._cssIsValid("filter", name + "(" + value + ")")) {
+ value = null;
+ }
+ }
+
+ this.add(name, value, quote, true);
+ }
+
+ this.emit("updated", this.getCssValue());
+ this.render();
+ },
+
+ /**
+ * Creates a new [name] filter record with value
+ *
+ * @param {String} name
+ * filter name (e.g. blur)
+ * @param {String} value
+ * value of the filter (e.g. 30px, 20%)
+ * If this is |null|, then a default value may be supplied.
+ * @param {String} quote
+ * For a url filter, the quoting style. This can be a
+ * single quote, a double quote, or empty.
+ * @return {Number}
+ * The index of the new filter in the current list of filters
+ * @param {Boolean}
+ * By default, adding a new filter emits an "updated" event, but if
+ * you're calling add in a loop and wait to emit a single event after
+ * the loop yourself, set this parameter to true.
+ */
+ add(name, value, quote, noEvent) {
+ const def = this._definition(name);
+ if (!def) {
+ return false;
+ }
+
+ if (value === null) {
+ // UNIT_MAPPING[string] is an empty string (falsy), so
+ // using || doesn't work here
+ const unitLabel =
+ typeof UNIT_MAPPING[def.type] === "undefined"
+ ? UNIT_MAPPING[DEFAULT_FILTER_TYPE]
+ : UNIT_MAPPING[def.type];
+
+ // string-type filters have no default value but a placeholder instead
+ if (!unitLabel) {
+ value = "";
+ } else {
+ value = def.range[0] + unitLabel;
+ }
+
+ if (name === "url") {
+ // Default quote.
+ quote = '"';
+ }
+ }
+
+ let unit = def.type === "string" ? "" : (/[a-zA-Z%]+/.exec(value) || [])[0];
+
+ if (def.type !== "string") {
+ value = parseFloat(value);
+
+ // You can omit percentage values' and use a value between 0..1
+ if (def.type === "percentage" && !unit) {
+ value = value * 100;
+ unit = "%";
+ }
+
+ const [min, max] = def.range;
+ if (value < min) {
+ value = min;
+ } else if (value > max) {
+ value = max;
+ }
+ }
+
+ const index = this.filters.push({ value, unit, name, quote }) - 1;
+ if (!noEvent) {
+ this.emit("updated", this.getCssValue());
+ }
+
+ return index;
+ },
+
+ /**
+ * returns value + unit of the specified filter
+ *
+ * @param {Number} index
+ * filter index
+ * @return {String}
+ * css value of filter
+ */
+ getValueAt(index) {
+ const filter = this.filters[index];
+ if (!filter) {
+ return null;
+ }
+
+ // Just return the value+unit for non-url functions.
+ if (filter.name !== "url") {
+ return filter.value + filter.unit;
+ }
+
+ // url values need to be quoted and escaped.
+ if (filter.quote === "'") {
+ return "'" + filter.value.replace(/\'/g, "\\'") + "'";
+ } else if (filter.quote === '"') {
+ return '"' + filter.value.replace(/\"/g, '\\"') + '"';
+ }
+
+ // Unquoted. This approach might change the original input -- for
+ // example the original might be over-quoted. But, this is
+ // correct and probably good enough.
+ return filter.value.replace(/[\\ \t()"']/g, "\\$&");
+ },
+
+ removeAt(index) {
+ if (!this.filters[index]) {
+ return;
+ }
+
+ this.filters.splice(index, 1);
+ this.emit("updated", this.getCssValue());
+ this.render();
+ },
+
+ /**
+ * Generates CSS filter value for filters of the widget
+ *
+ * @return {String}
+ * css value of filters
+ */
+ getCssValue() {
+ return (
+ this.filters
+ .map((filter, i) => {
+ return `${filter.name}(${this.getValueAt(i)})`;
+ })
+ .join(" ") ||
+ this._specialValue ||
+ "none"
+ );
+ },
+
+ /**
+ * Updates specified filter's value
+ *
+ * @param {Number} index
+ * The index of the filter in the current list of filters
+ * @param {number/string} value
+ * value to set, string for string-typed filters
+ * number for the rest (unit automatically determined)
+ */
+ updateValueAt(index, value) {
+ const filter = this.filters[index];
+ if (!filter) {
+ return;
+ }
+
+ const def = this._definition(filter.name);
+
+ if (def.type !== "string") {
+ const [min, max] = def.range;
+ if (value < min) {
+ value = min;
+ } else if (value > max) {
+ value = max;
+ }
+ }
+
+ filter.value = filter.unit ? fixFloat(value, true) : value;
+
+ this.emit("updated", this.getCssValue());
+ },
+
+ getPresets() {
+ return asyncStorage.getItem("cssFilterPresets").then(presets => {
+ if (!presets) {
+ return [];
+ }
+
+ return presets;
+ }, console.error);
+ },
+
+ setPresets(presets) {
+ return asyncStorage
+ .setItem("cssFilterPresets", presets)
+ .catch(console.error);
+ },
+};
+
+// Fixes JavaScript's float precision
+function fixFloat(a, number) {
+ const fixed = parseFloat(a).toFixed(1);
+ return number ? parseFloat(fixed) : fixed;
+}
+
+/**
+ * Used to swap two filters' indexes
+ * after drag/drop re-ordering
+ *
+ * @param {Array} array
+ * the array to swap elements of
+ * @param {Number} a
+ * index of first element
+ * @param {Number} b
+ * index of second element
+ */
+function swapArrayIndices(array, a, b) {
+ array[a] = array.splice(b, 1, array[a])[0];
+}
+
+/**
+ * Tokenizes a CSS Filter value and returns an array of {name, value} pairs.
+ *
+ * @param {String} css CSS Filter value to be parsed
+ * @return {Array} An array of {name, value} pairs
+ */
+function tokenizeFilterValue(css) {
+ const filters = [];
+ let depth = 0;
+
+ if (SPECIAL_VALUES.has(css)) {
+ return filters;
+ }
+
+ let state = "initial";
+ let name;
+ let contents;
+ for (const token of cssTokenizer(css)) {
+ switch (state) {
+ case "initial":
+ if (token.tokenType === "function") {
+ name = token.text;
+ contents = "";
+ state = "function";
+ depth = 1;
+ } else if (token.tokenType === "url" || token.tokenType === "bad_url") {
+ // Extract the quoting style from the url.
+ const originalText = css.substring(
+ token.startOffset,
+ token.endOffset
+ );
+ const [, quote] = /^url\([ \t\r\n\f]*(["']?)/i.exec(originalText);
+
+ filters.push({ name: "url", value: token.text.trim(), quote });
+ // Leave state as "initial" because the URL token includes
+ // the trailing close paren.
+ }
+ break;
+
+ case "function":
+ if (token.tokenType === "symbol" && token.text === ")") {
+ --depth;
+ if (depth === 0) {
+ filters.push({ name, value: contents.trim() });
+ state = "initial";
+ break;
+ }
+ }
+ contents += css.substring(token.startOffset, token.endOffset);
+ if (
+ token.tokenType === "function" ||
+ (token.tokenType === "symbol" && token.text === "(")
+ ) {
+ ++depth;
+ }
+ break;
+ }
+ }
+
+ return filters;
+}
+
+/**
+ * Finds neighbour number characters of an index in a string
+ * the numbers may be floats (containing dots)
+ * It's assumed that the value given to this function is a valid number
+ *
+ * @param {String} string
+ * The string containing numbers
+ * @param {Number} index
+ * The index to look for neighbours for
+ * @return {Object}
+ * returns null if no number is found
+ * value: The number found
+ * start: The number's starting index
+ * end: The number's ending index
+ */
+function getNeighbourNumber(string, index) {
+ if (!/\d/.test(string)) {
+ return null;
+ }
+
+ let left = /-?[0-9.]*$/.exec(string.slice(0, index));
+ let right = /-?[0-9.]*/.exec(string.slice(index));
+
+ left = left ? left[0] : "";
+ right = right ? right[0] : "";
+
+ if (!right && !left) {
+ return null;
+ }
+
+ return {
+ value: fixFloat(left + right, true),
+ start: index - left.length,
+ end: index + right.length,
+ };
+}
diff --git a/devtools/client/shared/widgets/LinearEasingFunctionWidget.js b/devtools/client/shared/widgets/LinearEasingFunctionWidget.js
new file mode 100644
index 0000000000..e6d2e604df
--- /dev/null
+++ b/devtools/client/shared/widgets/LinearEasingFunctionWidget.js
@@ -0,0 +1,731 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * This is a chart-like editor for linear() easing function, used in the Rules View.
+ */
+
+const EventEmitter = require("devtools/shared/event-emitter");
+const { getCSSLexer } = require("devtools/shared/css/lexer");
+const { throttle } = require("devtools/shared/throttle");
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const SVG_NS = "http://www.w3.org/2000/svg";
+
+const numberFormatter = new Intl.NumberFormat("en", {
+ maximumFractionDigits: 3,
+});
+const percentFormatter = new Intl.NumberFormat("en", {
+ maximumFractionDigits: 2,
+ style: "percent",
+});
+
+/**
+ * Easing function widget. Draw the lines and control points in an svg.
+ *
+ * XXX: The spec allows input and output values in the [-Infinity,Infinity] range,
+ * but this will be hard to have proper visual representation to handle those cases, so we
+ * only handle points inside [0,0] [1,1] to represent most common use cases (even though
+ * the line will properly link points outside of this range)
+ *
+ *
+ * @emits "updated" events whenever the line is changed, with the updated property value.
+ */
+class LinearEasingFunctionWidget extends EventEmitter {
+ /**
+ * @param {DOMNode} parent The container where the widget should be created
+ */
+ constructor(parent) {
+ super();
+
+ this.parent = parent;
+ this.#initMarkup();
+
+ this.#svgEl.addEventListener("mousedown", this.#onMouseDown.bind(this), {
+ signal: this.#abortController.signal,
+ });
+ this.#svgEl.addEventListener("dblclick", this.#onDoubleClick.bind(this), {
+ signal: this.#abortController.signal,
+ });
+
+ // Add the timing function previewer
+ // if prefers-reduced-motion is not set
+ this.#reducedMotion = parent.ownerGlobal.matchMedia(
+ "(prefers-reduced-motion)"
+ );
+ if (!this.#reducedMotion.matches) {
+ this.#timingPreview = new TimingFunctionPreviewWidget(this.#wrapperEl);
+ }
+
+ // add event listener to change prefers-reduced-motion
+ // of the timing function preview during runtime
+ this.#reducedMotion.addEventListener(
+ "change",
+ event => {
+ // if prefers-reduced-motion is enabled destroy timing function preview
+ // else create it if it does not exist
+ if (event.matches) {
+ if (this.#timingPreview) {
+ this.#timingPreview.destroy();
+ }
+ this.#timingPreview = undefined;
+ } else if (!this.#timingPreview) {
+ this.#timingPreview = new TimingFunctionPreviewWidget(
+ this.#wrapperEl
+ );
+ }
+ },
+ { signal: this.#abortController.signal }
+ );
+ }
+
+ static CONTROL_POINTS_CLASSNAME = "control-point";
+
+ // Handles event listener that are enabled for the whole widget lifetime
+ #abortController = new AbortController();
+
+ // Array<Object>: Object has `input` (plotted on x axis) and `output` (plotted on y axis) properties
+ #functionPoints;
+
+ // MediaQueryList
+ #reducedMotion;
+
+ // TimingFunctionPreviewWidget
+ #timingPreview;
+
+ // current dragged element. null if there's no dragging happening
+ #draggedEl = null;
+
+ // handles event listeners added when user starts dragging an element
+ #dragAbortController;
+
+ // element references
+ #wrapperEl;
+ #svgEl;
+ #linearLineEl;
+ #controlPointGroupEl;
+
+ /**
+ * Creates the markup of the widget
+ */
+ #initMarkup() {
+ const doc = this.parent.ownerDocument;
+
+ const wrap = doc.createElementNS(XHTML_NS, "div");
+ wrap.className = "display-wrap";
+ this.#wrapperEl = wrap;
+
+ const svg = doc.createElementNS(SVG_NS, "svg");
+ svg.classList.add("chart");
+
+ // Add some "padding" to the viewBox so circles near the edges are not clipped.
+ const padding = 0.1;
+ const length = 1 + padding * 2;
+ // XXX: The spec allows input and output values in the [-Infinity,Infinity] range,
+ // but this will be hard to have proper visual representation for all cases, so we
+ // set the viewBox is basically starting at 0,0 and has a size of 1 (if we don't take the
+ // padding into account), to represent most common use cases.
+ svg.setAttribute(
+ "viewBox",
+ `${0 - padding} ${0 - padding} ${length} ${length}`
+ );
+
+ // Create a background grid
+ const chartGrid = doc.createElementNS(SVG_NS, "g");
+ chartGrid.setAttribute("stroke-width", "0.005");
+ chartGrid.classList.add("chart-grid");
+ for (let i = 0; i <= 10; i++) {
+ const value = i / 10;
+ const hLine = doc.createElementNS(SVG_NS, "line");
+ hLine.setAttribute("x1", 0);
+ hLine.setAttribute("y1", value);
+ hLine.setAttribute("x2", 1);
+ hLine.setAttribute("y2", value);
+ const vLine = doc.createElementNS(SVG_NS, "line");
+ vLine.setAttribute("x1", value);
+ vLine.setAttribute("y1", 0);
+ vLine.setAttribute("x2", value);
+ vLine.setAttribute("y2", 1);
+ chartGrid.append(hLine, vLine);
+ }
+
+ // Create the actual graph line
+ const linearLine = doc.createElementNS(SVG_NS, "polyline");
+ linearLine.classList.add("chart-linear");
+ linearLine.setAttribute("fill", "none");
+ linearLine.setAttribute("stroke", "context-stroke black");
+ linearLine.setAttribute("stroke-width", "0.01");
+
+ // And a group for all the control points
+ const controlPointGroup = doc.createElementNS(SVG_NS, "g");
+ controlPointGroup.classList.add("control-points-group");
+
+ this.#linearLineEl = linearLine;
+ this.#svgEl = svg;
+ this.#controlPointGroupEl = controlPointGroup;
+
+ svg.append(chartGrid, linearLine, controlPointGroup);
+ wrap.append(svg);
+ this.parent.append(wrap);
+ }
+
+ /**
+ * Remove widget markup, called on destroy
+ */
+ #removeMarkup() {
+ this.#wrapperEl.remove();
+ }
+
+ /**
+ * Handle mousedown event on the svg
+ *
+ * @param {MouseEvent} event
+ */
+ #onMouseDown(event) {
+ if (
+ !event.target.classList.contains(
+ LinearEasingFunctionWidget.CONTROL_POINTS_CLASSNAME
+ )
+ ) {
+ return;
+ }
+
+ this.#draggedEl = event.target;
+ this.#draggedEl.setPointerCapture(event.pointerId);
+
+ this.#dragAbortController = new AbortController();
+ this.#draggedEl.addEventListener(
+ "mousemove",
+ this.#onMouseMove.bind(this),
+ { signal: this.#dragAbortController.signal }
+ );
+ this.#draggedEl.addEventListener("mouseup", this.#onMouseUp.bind(this), {
+ signal: this.#dragAbortController.signal,
+ });
+ }
+
+ /**
+ * Handle mousemove event on a control point. Only active when there's a control point
+ * being dragged.
+ *
+ * @param {MouseEvent} event
+ */
+ #onMouseMove = throttle(event => {
+ if (!this.#draggedEl) {
+ return;
+ }
+
+ const { x, y } = this.#getPositionInSvgFromEvent(event);
+
+ // XXX: The spec allows input and output values in the [-Infinity,Infinity] range,
+ // but this will be hard to have proper visual representation for all cases, so we
+ // clamp x and y between 0 and 1 as it's more likely the range that will be used.
+ let cx = clamp(0, 1, x);
+ let cy = clamp(0, 1, y);
+
+ if (this.#draggedEl.previousSibling) {
+ // We don't allow moving the point before the previous point
+ cx = Math.max(
+ cx,
+ parseFloat(this.#draggedEl.previousSibling.getAttribute("cx"))
+ );
+ }
+ if (this.#draggedEl.nextSibling) {
+ // We don't allow moving the point after the next point
+ cx = Math.min(
+ cx,
+ parseFloat(this.#draggedEl.nextSibling.getAttribute("cx"))
+ );
+ }
+
+ // Enable "Snap to grid" when the user holds the shift key
+ if (event.shiftKey) {
+ cx = Math.round(cx * 10) / 10;
+ cy = Math.round(cy * 10) / 10;
+ }
+
+ this.#draggedEl.setAttribute("cx", cx);
+ this.#draggedEl.setAttribute("cy", cy);
+
+ this.#updateFunctionPointsFromControlPoints();
+ this.#redrawLineFromFunctionPoints();
+ this.emit("updated", this.getCssLinearValue());
+ }, 20);
+
+ /**
+ * Handle mouseup event on a control point. Only active when there's a control point
+ * being dragged.
+ *
+ * @param {MouseEvent} event
+ */
+ #onMouseUp(event) {
+ this.#draggedEl.releasePointerCapture(event.pointerId);
+ this.#draggedEl = null;
+ this.#dragAbortController.abort();
+ this.#dragAbortController = null;
+ }
+
+ /**
+ * Handle dblclick event on the svg.
+ * If the target is a control point, this will remove it, otherwise this will add
+ * a new control point at the clicked position.
+ *
+ * @param {MouseEvent} event
+ */
+ #onDoubleClick(event) {
+ const existingPoints = Array.from(
+ this.#controlPointGroupEl.querySelectorAll(
+ `.${LinearEasingFunctionWidget.CONTROL_POINTS_CLASSNAME}`
+ )
+ );
+
+ if (
+ event.target.classList.contains(
+ LinearEasingFunctionWidget.CONTROL_POINTS_CLASSNAME
+ )
+ ) {
+ // The function is only valid when it has at least 2 points, so don't allow to
+ // produce invalid value.
+ if (existingPoints.length <= 2) {
+ return;
+ }
+
+ event.target.remove();
+ this.#updateFunctionPointsFromControlPoints();
+ this.#redrawFromFunctionPoints();
+ } else {
+ let { x, y } = this.#getPositionInSvgFromEvent(event);
+
+ // Enable "Snap to grid" when the user holds the shift key
+ if (event.shiftKey) {
+ x = clamp(0, 1, Math.round(x * 10) / 10);
+ y = clamp(0, 1, Math.round(y * 10) / 10);
+ }
+
+ // Add a control point at specified x and y in svg coords
+ // We need to loop through existing control points to insert it at the correct index.
+ const nextSibling = existingPoints.find(
+ el => parseFloat(el.getAttribute("cx")) >= x
+ );
+
+ this.#controlPointGroupEl.insertBefore(
+ this.#createSvgControlPointEl(x, y),
+ nextSibling
+ );
+ this.#updateFunctionPointsFromControlPoints();
+ this.#redrawLineFromFunctionPoints();
+ }
+ }
+
+ /**
+ * Update this.#functionPoints based on the control points in the svg
+ */
+ #updateFunctionPointsFromControlPoints() {
+ // We ensure to order the control points based on their x position within the group,
+ // so here, we can iterate through them without any need to sort them.
+ this.#functionPoints = Array.from(
+ this.#controlPointGroupEl.querySelectorAll(
+ `.${LinearEasingFunctionWidget.CONTROL_POINTS_CLASSNAME}`
+ )
+ ).map(el => {
+ const input = parseFloat(el.getAttribute("cx"));
+ // Since svg coords start from the top-left corner, we need to translate cy
+ // to have the actual value we want for the function.
+ const output = 1 - parseFloat(el.getAttribute("cy"));
+
+ return {
+ input,
+ output,
+ };
+ });
+ }
+
+ /**
+ * Redraw the control points and the linear() line in the svg,
+ * based on the value of this.functionPoints.
+ */
+ #redrawFromFunctionPoints() {
+ // Remove previous control points
+ this.#controlPointGroupEl
+ .querySelectorAll(
+ `.${LinearEasingFunctionWidget.CONTROL_POINTS_CLASSNAME}`
+ )
+ .forEach(el => el.remove());
+
+ if (this.#functionPoints) {
+ // Add controls for each function points
+ this.#functionPoints.forEach(({ input, output }) => {
+ this.#controlPointGroupEl.append(
+ // Since svg coords start from the top-left corner, we need to translate output
+ // to properly place it on the graph.
+ this.#createSvgControlPointEl(input, 1 - output)
+ );
+ });
+ }
+
+ this.#redrawLineFromFunctionPoints();
+ }
+
+ /**
+ * Redraw linear() line in the svg based on the value of this.functionPoints.
+ */
+ #redrawLineFromFunctionPoints() {
+ // Set the line points
+ this.#linearLineEl.setAttribute(
+ "points",
+ (this.#functionPoints || [])
+ .map(
+ ({ input, output }) =>
+ // Since svg coords start from the top-left corner, we need to translate output
+ // to properly place it on the graph.
+ `${input},${1 - output}`
+ )
+ .join(" ")
+ );
+
+ const cssLinearValue = this.getCssLinearValue();
+ if (this.#timingPreview) {
+ this.#timingPreview.preview(cssLinearValue);
+ }
+
+ this.emit("updated", cssLinearValue);
+ }
+
+ /**
+ * Create a control points for the svg line.
+ *
+ * @param {Number} cx
+ * @param {Number} cy
+ * @returns {SVGCircleElement}
+ */
+ #createSvgControlPointEl(cx, cy) {
+ const controlEl = this.parent.ownerDocument.createElementNS(
+ SVG_NS,
+ "circle"
+ );
+ controlEl.classList.add("control-point");
+ controlEl.setAttribute("cx", cx);
+ controlEl.setAttribute("cy", cy);
+ controlEl.setAttribute("r", 0.025);
+ controlEl.setAttribute("fill", "context-fill");
+ controlEl.setAttribute("stroke-width", 0);
+ return controlEl;
+ }
+
+ /**
+ * Return the position in the SVG viewbox from mouse event.
+ *
+ * @param {MouseEvent} event
+ * @returns {Object} An object with x and y properties
+ */
+ #getPositionInSvgFromEvent(event) {
+ const position = this.#svgEl.createSVGPoint();
+ position.x = event.clientX;
+ position.y = event.clientY;
+
+ const matrix = this.#svgEl.getScreenCTM();
+ const inverseSvgMatrix = matrix.inverse();
+ const transformedPosition = position.matrixTransform(inverseSvgMatrix);
+
+ return { x: transformedPosition.x, y: transformedPosition.y };
+ }
+
+ /**
+ * Provide the value of the linear() function we want to visualize here.
+ * Called from the tooltip with the value of the function in the rule view.
+ *
+ * @param {String} linearFunctionValue: e.g. `linear(0, 0.5, 1)`.
+ */
+ setCssLinearValue(linearFunctionValue) {
+ if (!linearFunctionValue) {
+ return;
+ }
+
+ // Parse the string to extract all the points
+ const points = parseTimingFunction(linearFunctionValue);
+ this.#functionPoints = points;
+
+ // And draw the line and points
+ this.#redrawFromFunctionPoints();
+ }
+
+ /**
+ * Return the value of the linear() function based on the state of the graph.
+ * The resulting value is what we emit in the "updated" event.
+ *
+ * @return {String|null} e.g. `linear(0 0%, 0.5 50%, 1 100%)`.
+ */
+ getCssLinearValue() {
+ if (!this.#functionPoints) {
+ return null;
+ }
+
+ return `linear(${this.#functionPoints
+ .map(
+ ({ input, output }) =>
+ `${numberFormatter.format(output)} ${percentFormatter.format(input)}`
+ )
+ .join(", ")})`;
+ }
+
+ destroy() {
+ this.#abortController.abort();
+ this.#dragAbortController?.abort();
+ this.#removeMarkup();
+ this.#reducedMotion = null;
+
+ if (this.#timingPreview) {
+ this.#timingPreview.destroy();
+ this.#timingPreview = null;
+ }
+ }
+}
+
+exports.LinearEasingFunctionWidget = LinearEasingFunctionWidget;
+
+/**
+ * The TimingFunctionPreviewWidget animates a dot on a scale with a given
+ * timing-function
+ */
+class TimingFunctionPreviewWidget {
+ /**
+ * @param {DOMNode} parent The container where this widget should go
+ */
+ constructor(parent) {
+ this.#initMarkup(parent);
+ }
+
+ #PREVIEW_DURATION = 1000;
+ #dotEl;
+ #previousValue;
+
+ #initMarkup(parent) {
+ const doc = parent.ownerDocument;
+
+ const container = doc.createElementNS(XHTML_NS, "div");
+ container.className = "timing-function-preview";
+
+ this.#dotEl = doc.createElementNS(XHTML_NS, "div");
+ this.#dotEl.className = "dot";
+ container.appendChild(this.#dotEl);
+ parent.appendChild(container);
+ }
+
+ destroy() {
+ this.#dotEl.getAnimations().forEach(anim => anim.cancel());
+ this.#dotEl.parentElement.remove();
+ }
+
+ /**
+ * Preview a new timing function. The current preview will only be stopped if
+ * the supplied function value is different from the previous one. If the
+ * supplied function is invalid, the preview will stop.
+ * @param {Array} value
+ */
+ preview(timingFunction) {
+ if (this.#previousValue == timingFunction) {
+ return;
+ }
+ this.#restartAnimation(timingFunction);
+ this.#previousValue = timingFunction;
+ }
+
+ /**
+ * Re-start the preview animation from the beginning.
+ * @param {Array} points
+ */
+ #restartAnimation = throttle(timingFunction => {
+ // Cancel the previous animation if there was any.
+ this.#dotEl.getAnimations().forEach(anim => anim.cancel());
+
+ // And start the new one.
+ // The animation consists of a few keyframes that move the dot to the right of the
+ // container, and then move it back to the left.
+ // It also contains some pause where the dot is semi transparent, before it moves to
+ // the right, and once again, before it comes back to the left.
+ // The timing function passed to this function is applied to the keyframes that
+ // actually move the dot. This way it can be previewed in both direction, instead of
+ // being spread over the whole animation.
+ this.#dotEl.animate(
+ [
+ { translate: "0%", opacity: 0.5, offset: 0 },
+ { translate: "0%", opacity: 0.5, offset: 0.19 },
+ { translate: "0%", opacity: 1, offset: 0.2, easing: timingFunction },
+ { translate: "100%", opacity: 1, offset: 0.5 },
+ { translate: "100%", opacity: 0.5, offset: 0.51 },
+ { translate: "100%", opacity: 0.5, offset: 0.7 },
+ { translate: "100%", opacity: 1, offset: 0.71, easing: timingFunction },
+ { translate: "0%", opacity: 1, offset: 1 },
+ ],
+ {
+ duration: this.#PREVIEW_DURATION * 2,
+ iterations: Infinity,
+ }
+ );
+ }, 250);
+}
+
+/**
+ * Parse a linear() string to collect the different values.
+ * https://drafts.csswg.org/css-easing-2/#the-linear-easing-function
+ *
+ * @param {String} value
+ * @return {Array<Object>|undefined} returns undefined if value isn't a valid linear() value.
+ * the items of the array are objects with {Number} `input`
+ * and {Number} `output` properties.
+ */
+function parseTimingFunction(value) {
+ value = value.trim();
+ const tokenStream = getCSSLexer(value);
+ const getNextToken = () => {
+ while (true) {
+ const token = tokenStream.nextToken();
+ if (
+ !token ||
+ (token.tokenType !== "whitespace" && token.tokenType !== "comment")
+ ) {
+ return token;
+ }
+ }
+ };
+
+ let token = getNextToken();
+ if (!token || token.tokenType !== "function" || token.text !== "linear") {
+ return undefined;
+ }
+
+ // Let's follow the spec parsing algorithm https://drafts.csswg.org/css-easing-2/#linear-easing-function-parsing
+ const points = [];
+ let largestInput = -Infinity;
+
+ while ((token = getNextToken())) {
+ if (token.text === ")") {
+ break;
+ }
+
+ if (token.tokenType === "number") {
+ // [parsing step 4.1]
+ const point = { input: null, output: token.number };
+ // [parsing step 4.2]
+ points.push(point);
+
+ // get nextToken to see if there's a linear stop length
+ token = getNextToken();
+ // [parsing step 4.3]
+ if (token && token.tokenType === "percentage") {
+ // [parsing step 4.3.1]
+ point.input = Math.max(token.number, largestInput);
+ // [parsing step 4.3.2]
+ largestInput = point.input;
+
+ // get nextToken to see if there's a second linear stop length
+ token = getNextToken();
+
+ // [parsing step 4.3.3]
+ if (token && token.tokenType === "percentage") {
+ // [parsing step 4.3.3.1]
+ const extraPoint = { input: null, output: point.output };
+ // [parsing step 4.3.3.2]
+ points.push(extraPoint);
+
+ // [parsing step 4.3.3.3]
+ extraPoint.input = Math.max(token.number, largestInput);
+ // [parsing step 4.3.3.4]
+ largestInput = extraPoint.input;
+ }
+ } else if (points.length == 1) {
+ // [parsing step 4.4]
+ // [parsing step 4.4.1]
+ point.input = 0;
+ // [parsing step 4.4.2]
+ largestInput = 0;
+ }
+ }
+ }
+
+ if (points.length < 2) {
+ return undefined;
+ }
+
+ // [parsing step 4.5]
+ if (points.at(-1).input === null) {
+ points.at(-1).input = Math.max(largestInput, 1);
+ }
+
+ // [parsing step 5]
+
+ // We want to retrieve ranges ("runs" in the spec) of items with null inputs so we
+ // can compute their input using linear interpolation.
+ const nullInputPoints = [];
+ points.forEach((point, index, array) => {
+ if (point.input == null) {
+ // since the first point is guaranteed to have an non-null input, and given that
+ // we iterate through the points in regular order, we are guaranteed to find a previous
+ // non null point.
+ const previousNonNull = array.findLast(
+ (item, i) => i < index && item.input !== null
+ ).input;
+ // since the last point is guaranteed to have an non-null input, and given that
+ // we iterate through the points in regular order, we are guaranteed to find a next
+ // non null point.
+ const nextNonNull = array.find(
+ (item, i) => i > index && item.input !== null
+ ).input;
+
+ if (nullInputPoints.at(-1)?.indexes?.at(-1) == index - 1) {
+ nullInputPoints.at(-1).indexes.push(index);
+ } else {
+ nullInputPoints.push({
+ indexes: [index],
+ previousNonNull,
+ nextNonNull,
+ });
+ }
+ }
+ });
+
+ // For each range of consecutive null-input indexes
+ nullInputPoints.forEach(({ indexes, previousNonNull, nextNonNull }) => {
+ // For each null-input points, compute their input by linearly interpolating between
+ // the closest previous and next points that have a non-null input.
+ indexes.forEach((index, i) => {
+ points[index].input = lerp(
+ previousNonNull,
+ nextNonNull,
+ (i + 1) / (indexes.length + 1)
+ );
+ });
+ });
+
+ return points;
+}
+
+/**
+ * Linearly interpolate between 2 numbers.
+ *
+ * @param {Number} x
+ * @param {Number} y
+ * @param {Number} a
+ * A value of 0 returns x, and 1 returns y
+ * @return {Number}
+ */
+function lerp(x, y, a) {
+ return x * (1 - a) + y * a;
+}
+
+/**
+ * Clamp value in a range, meaning the result won't be smaller than min
+ * and no bigger than max.
+ *
+ * @param {Number} min
+ * @param {Number} max
+ * @param {Number} value
+ * @returns {Number}
+ */
+function clamp(min, max, value) {
+ return Math.max(min, Math.min(value, max));
+}
+
+exports.parseTimingFunction = parseTimingFunction;
diff --git a/devtools/client/shared/widgets/ShapesInContextEditor.js b/devtools/client/shared/widgets/ShapesInContextEditor.js
new file mode 100644
index 0000000000..3a7f6752d4
--- /dev/null
+++ b/devtools/client/shared/widgets/ShapesInContextEditor.js
@@ -0,0 +1,347 @@
+/* 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/. */
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const { debounce } = require("resource://devtools/shared/debounce.js");
+
+/**
+ * The ShapesInContextEditor:
+ * - communicates with the ShapesHighlighter actor from the server;
+ * - listens to events for shape change and hover point coming from the shape-highlighter;
+ * - writes shape value changes to the CSS declaration it was triggered from;
+ * - synchronises highlighting coordinate points on mouse over between the shapes
+ * highlighter and the shape value shown in the Rule view.
+ *
+ * It is instantiated once in HighlightersOverlay by calls to .getInContextEditor().
+ */
+class ShapesInContextEditor {
+ constructor(highlighter, inspector, state) {
+ EventEmitter.decorate(this);
+
+ this.inspector = inspector;
+ this.highlighter = highlighter;
+ // Refence to the NodeFront currently being highlighted.
+ this.highlighterTargetNode = null;
+ this.highligherEventHandlers = {};
+ this.highligherEventHandlers["shape-change"] = this.onShapeChange;
+ this.highligherEventHandlers["shape-hover-on"] = this.onShapeHover;
+ this.highligherEventHandlers["shape-hover-off"] = this.onShapeHover;
+ // Mode for shapes highlighter: shape-outside or clip-path. Used to discern
+ // when toggling the highlighter on the same node for different CSS properties.
+ this.mode = null;
+ // Reference to Rule view used to listen for changes
+ this.ruleView = this.inspector.getPanel("ruleview").view;
+ // Reference of |state| from HighlightersOverlay.
+ this.state = state;
+ // Reference to DOM node of the toggle icon for shapes highlighter.
+ this.swatch = null;
+
+ // Commit triggers expensive DOM changes in TextPropertyEditor.update()
+ // so we debounce it.
+ this.commit = debounce(this.commit, 200, this);
+ this.onHighlighterEvent = this.onHighlighterEvent.bind(this);
+ this.onNodeFrontChanged = this.onNodeFrontChanged.bind(this);
+ this.onShapeValueUpdated = this.onShapeValueUpdated.bind(this);
+ this.onRuleViewChanged = this.onRuleViewChanged.bind(this);
+
+ this.highlighter.on("highlighter-event", this.onHighlighterEvent);
+ this.ruleView.on("ruleview-changed", this.onRuleViewChanged);
+ }
+
+ /**
+ * Get the reference to the TextProperty where shape changes should be written.
+ *
+ * We can't rely on the TextProperty to be consistent while changing the value of an
+ * inline style because the fix for Bug 1467076 forces a full rebuild of TextProperties
+ * for the inline style's mock-CSS Rule in the Rule view.
+ *
+ * On |toggle()|, we store the target TextProperty index, property name and parent rule.
+ * Here, we use that index and property name to attempt to re-identify the correct
+ * TextProperty in the rule.
+ *
+ * @return {TextProperty|null}
+ */
+ get textProperty() {
+ if (!this.rule || !this.rule.textProps) {
+ return null;
+ }
+
+ const textProp = this.rule.textProps[this.textPropIndex];
+ return textProp && textProp.name === this.textPropName ? textProp : null;
+ }
+
+ /**
+ * Called when the element style changes from the Rule view.
+ * If the TextProperty we're acting on isn't enabled anymore or overridden,
+ * turn off the shapes highlighter.
+ */
+ async onRuleViewChanged() {
+ if (
+ this.textProperty &&
+ (!this.textProperty.enabled || this.textProperty.overridden)
+ ) {
+ await this.hide();
+ }
+ }
+
+ /**
+ * Toggle the shapes highlighter for the given element.
+ *
+ * @param {NodeFront} node
+ * The NodeFront of the element with a shape to highlight.
+ * @param {Object} options
+ * Object used for passing options to the shapes highlighter.
+ */
+ async toggle(node, options, prop) {
+ // Same target node, same mode -> hide and exit OR switch to toggle transform mode.
+ if (node == this.highlighterTargetNode && this.mode === options.mode) {
+ if (!options.transformMode) {
+ await this.hide();
+ return;
+ }
+
+ options.transformMode = !this.state.shapes.options.transformMode;
+ }
+
+ // Same target node, dfferent modes -> toggle between shape-outside, clip-path and offset-path.
+ // Hide highlighter for previous property, but continue and show for other property.
+ if (node == this.highlighterTargetNode && this.mode !== options.mode) {
+ await this.hide();
+ }
+
+ // Save the target TextProperty's parent rule, index and property name for later
+ // re-identification of the TextProperty. @see |get textProperty()|.
+ this.rule = prop.rule;
+ this.textPropIndex = this.rule.textProps.indexOf(prop);
+ this.textPropName = prop.name;
+
+ this.findSwatch();
+ await this.show(node, options);
+ }
+
+ /**
+ * Show the shapes highlighter for the given element.
+ *
+ * @param {NodeFront} node
+ * The NodeFront of the element with a shape to highlight.
+ * @param {Object} options
+ * Object used for passing options to the shapes highlighter.
+ */
+ async show(node, options) {
+ const isShown = await this.highlighter.show(node, options);
+ if (!isShown) {
+ return;
+ }
+
+ this.inspector.selection.on("detached-front", this.onNodeFrontChanged);
+ this.inspector.selection.on("new-node-front", this.onNodeFrontChanged);
+ this.ruleView.on("property-value-updated", this.onShapeValueUpdated);
+ this.highlighterTargetNode = node;
+ this.mode = options.mode;
+ this.emit("show", { node, options });
+ }
+
+ /**
+ * Hide the shapes highlighter.
+ */
+ async hide() {
+ try {
+ await this.highlighter.hide();
+ } catch (err) {
+ // silent error
+ }
+
+ // Stop if the panel has been destroyed during the call to hide.
+ if (this.destroyed) {
+ return;
+ }
+
+ if (this.swatch) {
+ this.swatch.classList.remove("active");
+ }
+ this.swatch = null;
+ this.rule = null;
+ this.textPropIndex = -1;
+ this.textPropName = null;
+
+ this.emit("hide", { node: this.highlighterTargetNode });
+ this.inspector.selection.off("detached-front", this.onNodeFrontChanged);
+ this.inspector.selection.off("new-node-front", this.onNodeFrontChanged);
+ this.ruleView.off("property-value-updated", this.onShapeValueUpdated);
+ this.highlighterTargetNode = null;
+ }
+
+ /**
+ * Identify the swatch (aka toggle icon) DOM node from the TextPropertyEditor of the
+ * TextProperty we're working with. Whenever the TextPropertyEditor is updated (i.e.
+ * when committing the shape value to the Rule view), it rebuilds its DOM and the old
+ * swatch reference becomes invalid. Call this method to identify the current swatch.
+ */
+ findSwatch() {
+ if (!this.textProperty) {
+ return;
+ }
+
+ const valueSpan = this.textProperty.editor.valueSpan;
+ this.swatch = valueSpan.querySelector(".ruleview-shapeswatch");
+ if (this.swatch) {
+ this.swatch.classList.add("active");
+ }
+ }
+
+ /**
+ * Handle events emitted by the highlighter.
+ * Find any callback assigned to the event type and call it with the given data object.
+ *
+ * @param {Object} data
+ * The data object sent in the event.
+ */
+ onHighlighterEvent(data) {
+ const handler = this.highligherEventHandlers[data.type];
+ if (!handler || typeof handler !== "function") {
+ return;
+ }
+ handler.call(this, data);
+ this.inspector.highlighters.emit("highlighter-event-handled");
+ }
+
+ /**
+ * Clean up when node selection changes because Rule view and TextPropertyEditor
+ * instances are not automatically destroyed when selection changes.
+ */
+ async onNodeFrontChanged() {
+ try {
+ await this.hide();
+ } catch (err) {
+ // Silent error.
+ }
+ }
+
+ /**
+ * Handler for "shape-change" event from the shapes highlighter.
+ *
+ * @param {Object} data
+ * Data associated with the "shape-change" event.
+ * Contains:
+ * - {String} value: the new shape value.
+ * - {String} type: the event type ("shape-change").
+ */
+ onShapeChange(data) {
+ this.preview(data.value);
+ this.commit(data.value);
+ }
+
+ /**
+ * Handler for "shape-hover-on" and "shape-hover-off" events from the shapes highlighter.
+ * Called when the mouse moves over or off of a coordinate point inside the shapes
+ * highlighter. Marks/unmarks the corresponding coordinate node in the shape value
+ * from the Rule view.
+ *
+ * @param {Object} data
+ * Data associated with the "shape-hover" event.
+ * Contains:
+ * - {String|null} point: coordinate to highlight or null if nothing to highlight
+ * - {String} type: the event type ("shape-hover-on" or "shape-hover-on").
+ */
+ onShapeHover(data) {
+ const shapeValueEl = this.swatch && this.swatch.nextSibling;
+ if (!shapeValueEl) {
+ return;
+ }
+
+ const pointSelector = ".ruleview-shape-point";
+ // First, unmark all highlighted coordinate nodes from Rule view
+ for (const node of shapeValueEl.querySelectorAll(
+ `${pointSelector}.active`
+ )) {
+ node.classList.remove("active");
+ }
+
+ // Exit if there's no coordinate to highlight.
+ if (typeof data.point !== "string") {
+ return;
+ }
+
+ const point = data.point.includes(",")
+ ? data.point.split(",")[0]
+ : data.point;
+
+ /**
+ * Build selector for coordinate nodes in shape value that must be highlighted.
+ * Coordinate values for inset() use class names instead of data attributes because
+ * a single node may represent multiple coordinates in shorthand notation.
+ * Example: inset(50px); The node wrapping 50px represents all four inset coordinates.
+ */
+ const INSET_POINT_TYPES = ["top", "right", "bottom", "left"];
+ const selector = INSET_POINT_TYPES.includes(point)
+ ? `${pointSelector}.${point}`
+ : `${pointSelector}[data-point='${point}']`;
+
+ for (const node of shapeValueEl.querySelectorAll(selector)) {
+ node.classList.add("active");
+ }
+ }
+
+ /**
+ * Handler for "property-value-updated" event triggered by the Rule view.
+ * Called after the shape value has been written to the element's style and the Rule
+ * view updated. Emits an event on HighlightersOverlay that is expected by
+ * tests in order to check if the shape value has been correctly applied.
+ */
+ async onShapeValueUpdated() {
+ if (this.textProperty) {
+ // When TextPropertyEditor updates, it replaces the previous swatch DOM node.
+ // Find and store the new one.
+ this.findSwatch();
+ this.inspector.highlighters.emit("shapes-highlighter-changes-applied");
+ } else {
+ await this.hide();
+ }
+ }
+
+ /**
+ * Preview a shape value on the element without committing the changes to the Rule view.
+ *
+ * @param {String} value
+ * The shape value to set the current property to
+ */
+ preview(value) {
+ if (!this.textProperty) {
+ return;
+ }
+ // Update the element's style to see live results.
+ this.textProperty.rule.previewPropertyValue(this.textProperty, value);
+ // Update the text of CSS value in the Rule view. This makes it inert.
+ // When commit() is called, the value is reparsed and its DOM structure rebuilt.
+ this.swatch.nextSibling.textContent = value;
+ }
+
+ /**
+ * Commit a shape value change which triggers an expensive operation that rebuilds
+ * part of the DOM of the TextPropertyEditor. Called in a debounced manner; see
+ * constructor.
+ *
+ * @param {String} value
+ * The shape value for the current property
+ */
+ commit(value) {
+ if (!this.textProperty) {
+ return;
+ }
+
+ this.textProperty.setValue(value);
+ }
+
+ destroy() {
+ this.highlighter.off("highlighter-event", this.onHighlighterEvent);
+ this.ruleView.off("ruleview-changed", this.onRuleViewChanged);
+ this.highligherEventHandlers = {};
+
+ this.destroyed = true;
+ }
+}
+
+module.exports = ShapesInContextEditor;
diff --git a/devtools/client/shared/widgets/Spectrum.js b/devtools/client/shared/widgets/Spectrum.js
new file mode 100644
index 0000000000..cdf5f2df6b
--- /dev/null
+++ b/devtools/client/shared/widgets/Spectrum.js
@@ -0,0 +1,783 @@
+/* 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/. */
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const {
+ MultiLocalizationHelper,
+} = require("resource://devtools/shared/l10n.js");
+
+loader.lazyRequireGetter(
+ this,
+ "colorUtils",
+ "resource://devtools/shared/css/color.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "labColors",
+ "resource://devtools/shared/css/color-db.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ ["getTextProperties", "getContrastRatioAgainstBackground"],
+ "resource://devtools/shared/accessibility.js",
+ true
+);
+
+const L10N = new MultiLocalizationHelper(
+ "devtools/client/locales/accessibility.properties",
+ "devtools/client/locales/inspector.properties"
+);
+const ARROW_KEYS = ["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft"];
+const [ArrowUp, ArrowRight, ArrowDown, ArrowLeft] = ARROW_KEYS;
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const SLIDER = {
+ hue: {
+ MIN: "0",
+ MAX: "128",
+ STEP: "1",
+ },
+ alpha: {
+ MIN: "0",
+ MAX: "1",
+ STEP: "0.01",
+ },
+};
+
+/**
+ * Spectrum creates a color picker widget in any container you give it.
+ *
+ * Simple usage example:
+ *
+ * const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
+ * let s = new Spectrum(containerElement, [255, 126, 255, 1]);
+ * s.on("changed", (rgba, color) => {
+ * console.log("rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ", " +
+ * rgba[3] + ")");
+ * });
+ * s.show();
+ * s.destroy();
+ *
+ * Note that the color picker is hidden by default and you need to call show to
+ * make it appear. This 2 stages initialization helps in cases you are creating
+ * the color picker in a parent element that hasn't been appended anywhere yet
+ * or that is hidden. Calling show() when the parent element is appended and
+ * visible will allow spectrum to correctly initialize its various parts.
+ *
+ * Fires the following events:
+ * - changed : When the user changes the current color
+ */
+class Spectrum {
+ constructor(parentEl, rgb) {
+ EventEmitter.decorate(this);
+
+ this.document = parentEl.ownerDocument;
+ this.element = parentEl.ownerDocument.createElementNS(XHTML_NS, "div");
+ this.parentEl = parentEl;
+
+ this.element.className = "spectrum-container";
+ // eslint-disable-next-line no-unsanitized/property
+ this.element.innerHTML = `
+ <section class="spectrum-color-picker">
+ <div class="spectrum-color spectrum-box"
+ tabindex="0"
+ role="slider"
+ title="${L10N.getStr("colorPickerTooltip.spectrumDraggerTitle")}"
+ aria-describedby="spectrum-dragger">
+ <div class="spectrum-sat">
+ <div class="spectrum-val">
+ <div class="spectrum-dragger" id="spectrum-dragger"></div>
+ </div>
+ </div>
+ </div>
+ </section>
+ <section class="spectrum-controls">
+ <div class="spectrum-color-preview"></div>
+ <div class="spectrum-slider-container">
+ <div class="spectrum-hue spectrum-box"></div>
+ <div class="spectrum-alpha spectrum-checker spectrum-box"></div>
+ </div>
+ </section>
+ <section class="spectrum-color-contrast accessibility-color-contrast">
+ <div class="contrast-ratio-header-and-single-ratio">
+ <span class="contrast-ratio-label" role="presentation"></span>
+ <span class="contrast-value-and-swatch contrast-ratio-single" role="presentation">
+ <span class="accessibility-contrast-value"></span>
+ </span>
+ </div>
+ <div class="contrast-ratio-range">
+ <span class="contrast-value-and-swatch contrast-ratio-min" role="presentation">
+ <span class="accessibility-contrast-value"></span>
+ </span>
+ <span class="accessibility-color-contrast-separator"></span>
+ <span class="contrast-value-and-swatch contrast-ratio-max" role="presentation">
+ <span class="accessibility-contrast-value"></span>
+ </span>
+ </div>
+ </section>
+ `;
+
+ this.onElementClick = this.onElementClick.bind(this);
+ this.element.addEventListener("click", this.onElementClick);
+
+ this.parentEl.appendChild(this.element);
+
+ // Color spectrum dragger.
+ this.dragger = this.element.querySelector(".spectrum-color");
+ this.dragHelper = this.element.querySelector(".spectrum-dragger");
+ draggable(this.dragger, this.dragHelper, this.onDraggerMove.bind(this));
+
+ // Here we define the components for the "controls" section of the color picker.
+ this.controls = this.element.querySelector(".spectrum-controls");
+ this.colorPreview = this.element.querySelector(".spectrum-color-preview");
+
+ // Create the eyedropper.
+ const eyedropper = this.document.createElementNS(XHTML_NS, "button");
+ eyedropper.id = "eyedropper-button";
+ eyedropper.className = "devtools-button";
+ eyedropper.style.pointerEvents = "auto";
+ eyedropper.setAttribute(
+ "aria-label",
+ L10N.getStr("colorPickerTooltip.eyedropperTitle")
+ );
+ this.controls.insertBefore(eyedropper, this.colorPreview);
+
+ // Hue slider and alpha slider
+ this.hueSlider = this.createSlider("hue", this.onHueSliderMove.bind(this));
+ this.hueSlider.setAttribute("aria-describedby", this.dragHelper.id);
+ this.alphaSlider = this.createSlider(
+ "alpha",
+ this.onAlphaSliderMove.bind(this)
+ );
+
+ // Color contrast
+ this.spectrumContrast = this.element.querySelector(
+ ".spectrum-color-contrast"
+ );
+ this.contrastLabel = this.element.querySelector(".contrast-ratio-label");
+ [this.contrastValue, this.contrastValueMin, this.contrastValueMax] =
+ this.element.querySelectorAll(".accessibility-contrast-value");
+
+ // Create the learn more info button
+ const learnMore = this.document.createElementNS(XHTML_NS, "button");
+ learnMore.id = "learn-more-button";
+ learnMore.className = "learn-more";
+ learnMore.title = L10N.getStr("accessibility.learnMore");
+ this.element
+ .querySelector(".contrast-ratio-header-and-single-ratio")
+ .appendChild(learnMore);
+
+ if (rgb) {
+ this.rgb = rgb;
+ this.updateUI();
+ }
+ }
+
+ set textProps(style) {
+ this._textProps = style
+ ? {
+ fontSize: style["font-size"].value,
+ fontWeight: style["font-weight"].value,
+ opacity: style.opacity.value,
+ }
+ : null;
+ }
+
+ set rgb(color) {
+ this.hsv = rgbToHsv(color[0], color[1], color[2], color[3]);
+ }
+
+ set backgroundColorData(colorData) {
+ this._backgroundColorData = colorData;
+ }
+
+ get backgroundColorData() {
+ return this._backgroundColorData;
+ }
+
+ get textProps() {
+ return this._textProps;
+ }
+
+ get rgb() {
+ const rgb = hsvToRgb(this.hsv[0], this.hsv[1], this.hsv[2], this.hsv[3]);
+ return [
+ Math.round(rgb[0]),
+ Math.round(rgb[1]),
+ Math.round(rgb[2]),
+ Math.round(rgb[3] * 100) / 100,
+ ];
+ }
+
+ /**
+ * Map current rgb to the closest color available in the database by
+ * calculating the delta-E between each available color and the current rgb
+ *
+ * @return {String}
+ * Color name or closest color name
+ */
+ get colorName() {
+ const labColorEntries = Object.entries(labColors);
+
+ const deltaEs = labColorEntries.map(color =>
+ colorUtils.calculateDeltaE(color[1], colorUtils.rgbToLab(this.rgb))
+ );
+
+ // Get the color name for the one that has the lowest delta-E
+ const minDeltaE = Math.min(...deltaEs);
+ const colorName = labColorEntries[deltaEs.indexOf(minDeltaE)][0];
+ return minDeltaE === 0
+ ? colorName
+ : L10N.getFormatStr("colorPickerTooltip.colorNameTitle", colorName);
+ }
+
+ get rgbNoSatVal() {
+ const rgb = hsvToRgb(this.hsv[0], 1, 1);
+ return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2]), rgb[3]];
+ }
+
+ get rgbCssString() {
+ const rgb = this.rgb;
+ return (
+ "rgba(" + rgb[0] + ", " + rgb[1] + ", " + rgb[2] + ", " + rgb[3] + ")"
+ );
+ }
+
+ show() {
+ this.dragWidth = this.dragger.offsetWidth;
+ this.dragHeight = this.dragger.offsetHeight;
+ this.dragHelperHeight = this.dragHelper.offsetHeight;
+
+ this.updateUI();
+ }
+
+ onElementClick(e) {
+ e.stopPropagation();
+ }
+
+ onHueSliderMove() {
+ this.hsv[0] = this.hueSlider.value / this.hueSlider.max;
+ this.updateUI();
+ this.onChange();
+ }
+
+ onDraggerMove(dragX, dragY) {
+ this.hsv[1] = dragX / this.dragWidth;
+ this.hsv[2] = (this.dragHeight - dragY) / this.dragHeight;
+ this.updateUI();
+ this.onChange();
+ }
+
+ onAlphaSliderMove() {
+ this.hsv[3] = this.alphaSlider.value / this.alphaSlider.max;
+ this.updateUI();
+ this.onChange();
+ }
+
+ onChange() {
+ this.emit("changed", this.rgb, this.rgbCssString);
+ }
+
+ /**
+ * Creates and initializes a slider element, attaches it to its parent container
+ * based on the slider type and returns it
+ *
+ * @param {String} sliderType
+ * The type of the slider (i.e. alpha or hue)
+ * @param {Function} onSliderMove
+ * The function to tie the slider to on input
+ * @return {DOMNode}
+ * Newly created slider
+ */
+ createSlider(sliderType, onSliderMove) {
+ const container = this.element.querySelector(`.spectrum-${sliderType}`);
+
+ const slider = this.document.createElementNS(XHTML_NS, "input");
+ slider.className = `spectrum-${sliderType}-input`;
+ slider.type = "range";
+ slider.min = SLIDER[sliderType].MIN;
+ slider.max = SLIDER[sliderType].MAX;
+ slider.step = SLIDER[sliderType].STEP;
+ slider.title = L10N.getStr(`colorPickerTooltip.${sliderType}SliderTitle`);
+ slider.addEventListener("input", onSliderMove);
+
+ container.appendChild(slider);
+ return slider;
+ }
+
+ /**
+ * Updates the contrast label with appropriate content (i.e. large text indicator
+ * if the contrast is calculated for large text, or a base label otherwise)
+ *
+ * @param {Boolean} isLargeText
+ * True if contrast is calculated for large text.
+ */
+ updateContrastLabel(isLargeText) {
+ if (!isLargeText) {
+ this.contrastLabel.textContent = L10N.getStr(
+ "accessibility.contrast.ratio.label"
+ );
+ return;
+ }
+
+ // Clear previously appended children before appending any new children
+ while (this.contrastLabel.firstChild) {
+ this.contrastLabel.firstChild.remove();
+ }
+
+ const largeTextStr = L10N.getStr("accessibility.contrast.large.text");
+ const contrastLabelStr = L10N.getFormatStr(
+ "colorPickerTooltip.contrast.large.title",
+ largeTextStr
+ );
+
+ // Build an array of children nodes for the contrast label element
+ const contents = contrastLabelStr
+ .split(new RegExp(largeTextStr), 2)
+ .map(content => this.document.createTextNode(content));
+ const largeTextIndicator = this.document.createElementNS(XHTML_NS, "span");
+ largeTextIndicator.className = "accessibility-color-contrast-large-text";
+ largeTextIndicator.textContent = largeTextStr;
+ largeTextIndicator.title = L10N.getStr(
+ "accessibility.contrast.large.title"
+ );
+ contents.splice(1, 0, largeTextIndicator);
+
+ // Append children to contrast label
+ for (const content of contents) {
+ this.contrastLabel.appendChild(content);
+ }
+ }
+
+ /**
+ * Updates a contrast value element with the given score, value and swatches.
+ *
+ * @param {DOMNode} el
+ * Contrast value element to update.
+ * @param {String} score
+ * Contrast ratio score.
+ * @param {Number} value
+ * Contrast ratio value.
+ * @param {Array} backgroundColor
+ * RGBA color array for the background color to show in the swatch.
+ */
+ updateContrastValueEl(el, score, value, backgroundColor) {
+ el.classList.toggle(score, true);
+ el.textContent = value.toFixed(2);
+ el.title = L10N.getFormatStr(
+ `accessibility.contrast.annotation.${score}`,
+ L10N.getFormatStr(
+ "colorPickerTooltip.contrastAgainstBgTitle",
+ `rgba(${backgroundColor})`
+ )
+ );
+ el.parentElement.style.setProperty(
+ "--accessibility-contrast-color",
+ this.rgbCssString
+ );
+ el.parentElement.style.setProperty(
+ "--accessibility-contrast-bg",
+ `rgba(${backgroundColor})`
+ );
+ }
+
+ updateAlphaSlider() {
+ // Set alpha slider background
+ const rgb = this.rgb;
+
+ const rgbNoAlpha = "rgb(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ")";
+ const rgbAlpha0 = "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + ", 0)";
+ const alphaGradient =
+ "linear-gradient(to right, " + rgbAlpha0 + ", " + rgbNoAlpha + ")";
+ this.alphaSlider.style.background = alphaGradient;
+ }
+
+ updateColorPreview() {
+ // Overlay the rgba color over a checkered image background.
+ this.colorPreview.style.setProperty("--overlay-color", this.rgbCssString);
+
+ // We should be able to distinguish the color preview on high luminance rgba values.
+ // Give the color preview a light grey border if the luminance of the current rgba
+ // tuple is great.
+ const colorLuminance = colorUtils.calculateLuminance(this.rgb);
+ this.colorPreview.classList.toggle("high-luminance", colorLuminance > 0.85);
+
+ // Set title on color preview for better UX
+ this.colorPreview.title = this.colorName;
+ }
+
+ updateDragger() {
+ // Set dragger background color
+ const flatColor =
+ "rgb(" +
+ this.rgbNoSatVal[0] +
+ ", " +
+ this.rgbNoSatVal[1] +
+ ", " +
+ this.rgbNoSatVal[2] +
+ ")";
+ this.dragger.style.backgroundColor = flatColor;
+
+ // Set dragger aria attributes
+ this.dragger.setAttribute("aria-valuetext", this.rgbCssString);
+ }
+
+ updateHueSlider() {
+ // Set hue slider aria attributes
+ this.hueSlider.setAttribute("aria-valuetext", this.rgbCssString);
+ }
+
+ updateHelperLocations() {
+ const h = this.hsv[0];
+ const s = this.hsv[1];
+ const v = this.hsv[2];
+
+ // Placing the color dragger
+ let dragX = s * this.dragWidth;
+ let dragY = this.dragHeight - v * this.dragHeight;
+ const helperDim = this.dragHelperHeight / 2;
+
+ dragX = Math.max(
+ -helperDim,
+ Math.min(this.dragWidth - helperDim, dragX - helperDim)
+ );
+ dragY = Math.max(
+ -helperDim,
+ Math.min(this.dragHeight - helperDim, dragY - helperDim)
+ );
+
+ this.dragHelper.style.top = dragY + "px";
+ this.dragHelper.style.left = dragX + "px";
+
+ // Placing the hue slider
+ this.hueSlider.value = h * this.hueSlider.max;
+
+ // Placing the alpha slider
+ this.alphaSlider.value = this.hsv[3] * this.alphaSlider.max;
+ }
+
+ /* Calculates the contrast ratio for the currently selected
+ * color against a single or range of background colors and displays contrast ratio section
+ * components depending on the contrast ratio calculated.
+ *
+ * Contrast ratio components include:
+ * - contrastLargeTextIndicator: Hidden by default, shown when text has large font
+ * size if there is no error in calculation.
+ * - contrastValue(s): Set to calculated value(s), score(s) and text color on
+ * background swatches. Set to error text
+ * if there is an error in calculation.
+ */
+ updateContrast() {
+ // Remove additional classes on spectrum contrast, leaving behind only base classes
+ this.spectrumContrast.classList.toggle("visible", false);
+ this.spectrumContrast.classList.toggle("range", false);
+ this.spectrumContrast.classList.toggle("error", false);
+ // Assign only base class to all contrastValues, removing any score class
+ this.contrastValue.className =
+ this.contrastValueMin.className =
+ this.contrastValueMax.className =
+ "accessibility-contrast-value";
+
+ if (!this.contrastEnabled) {
+ return;
+ }
+
+ const isRange = this.backgroundColorData.min !== undefined;
+ this.spectrumContrast.classList.toggle("visible", true);
+ this.spectrumContrast.classList.toggle("range", isRange);
+
+ const colorContrast = getContrastRatio(
+ {
+ ...this.textProps,
+ color: this.rgbCssString,
+ },
+ this.backgroundColorData
+ );
+
+ const {
+ value,
+ min,
+ max,
+ score,
+ scoreMin,
+ scoreMax,
+ backgroundColor,
+ backgroundColorMin,
+ backgroundColorMax,
+ isLargeText,
+ error,
+ } = colorContrast;
+
+ if (error) {
+ this.updateContrastLabel(false);
+ this.spectrumContrast.classList.toggle("error", true);
+
+ // If current background color is a range, show the error text in the contrast range
+ // span. Otherwise, show it in the single contrast span.
+ const contrastValEl = isRange
+ ? this.contrastValueMin
+ : this.contrastValue;
+ contrastValEl.textContent = L10N.getStr("accessibility.contrast.error");
+ contrastValEl.title = L10N.getStr(
+ "accessibility.contrast.annotation.transparent.error"
+ );
+
+ return;
+ }
+
+ this.updateContrastLabel(isLargeText);
+ if (!isRange) {
+ this.updateContrastValueEl(
+ this.contrastValue,
+ score,
+ value,
+ backgroundColor
+ );
+
+ return;
+ }
+
+ this.updateContrastValueEl(
+ this.contrastValueMin,
+ scoreMin,
+ min,
+ backgroundColorMin
+ );
+ this.updateContrastValueEl(
+ this.contrastValueMax,
+ scoreMax,
+ max,
+ backgroundColorMax
+ );
+ }
+
+ updateUI() {
+ this.updateHelperLocations();
+
+ this.updateColorPreview();
+ this.updateDragger();
+ this.updateHueSlider();
+ this.updateAlphaSlider();
+ this.updateContrast();
+ }
+
+ destroy() {
+ this.element.removeEventListener("click", this.onElementClick);
+ this.hueSlider.removeEventListener("input", this.onHueSliderMove);
+ this.alphaSlider.removeEventListener("input", this.onAlphaSliderMove);
+
+ this.parentEl.removeChild(this.element);
+
+ this.dragger = this.dragHelper = null;
+ this.alphaSlider = null;
+ this.hueSlider = null;
+ this.colorPreview = null;
+ this.element = null;
+ this.parentEl = null;
+ this.spectrumContrast = null;
+ this.contrastValue = this.contrastValueMin = this.contrastValueMax = null;
+ this.contrastLabel = null;
+ }
+}
+
+function hsvToRgb(h, s, v, a) {
+ let r, g, b;
+
+ const i = Math.floor(h * 6);
+ const f = h * 6 - i;
+ const p = v * (1 - s);
+ const q = v * (1 - f * s);
+ const t = v * (1 - (1 - f) * s);
+
+ switch (i % 6) {
+ case 0:
+ r = v;
+ g = t;
+ b = p;
+ break;
+ case 1:
+ r = q;
+ g = v;
+ b = p;
+ break;
+ case 2:
+ r = p;
+ g = v;
+ b = t;
+ break;
+ case 3:
+ r = p;
+ g = q;
+ b = v;
+ break;
+ case 4:
+ r = t;
+ g = p;
+ b = v;
+ break;
+ case 5:
+ r = v;
+ g = p;
+ b = q;
+ break;
+ }
+
+ return [r * 255, g * 255, b * 255, a];
+}
+
+function rgbToHsv(r, g, b, a) {
+ r = r / 255;
+ g = g / 255;
+ b = b / 255;
+
+ const max = Math.max(r, g, b);
+ const min = Math.min(r, g, b);
+
+ const v = max;
+ const d = max - min;
+ const s = max == 0 ? 0 : d / max;
+
+ let h;
+ if (max == min) {
+ // achromatic
+ h = 0;
+ } else {
+ switch (max) {
+ case r:
+ h = (g - b) / d + (g < b ? 6 : 0);
+ break;
+ case g:
+ h = (b - r) / d + 2;
+ break;
+ case b:
+ h = (r - g) / d + 4;
+ break;
+ }
+ h /= 6;
+ }
+ return [h, s, v, a];
+}
+
+function draggable(element, dragHelper, onmove) {
+ onmove = onmove || function () {};
+
+ const doc = element.ownerDocument;
+ let dragging = false;
+ let offset = {};
+ let maxHeight = 0;
+ let maxWidth = 0;
+
+ function setDraggerDimensionsAndOffset() {
+ maxHeight = element.offsetHeight;
+ maxWidth = element.offsetWidth;
+ offset = element.getBoundingClientRect();
+ }
+
+ function prevent(e) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+
+ function move(e) {
+ if (dragging) {
+ if (e.buttons === 0) {
+ // The button is no longer pressed but we did not get a mouseup event.
+ stop();
+ return;
+ }
+ const pageX = e.pageX;
+ const pageY = e.pageY;
+
+ const dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth));
+ const dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight));
+
+ onmove.apply(element, [dragX, dragY]);
+ }
+ }
+
+ function start(e) {
+ const rightClick = e.which === 3;
+
+ if (!rightClick && !dragging) {
+ dragging = true;
+ setDraggerDimensionsAndOffset();
+
+ move(e);
+
+ doc.addEventListener("selectstart", prevent);
+ doc.addEventListener("dragstart", prevent);
+ doc.addEventListener("mousemove", move);
+ doc.addEventListener("mouseup", stop);
+
+ prevent(e);
+ }
+ }
+
+ function stop() {
+ if (dragging) {
+ doc.removeEventListener("selectstart", prevent);
+ doc.removeEventListener("dragstart", prevent);
+ doc.removeEventListener("mousemove", move);
+ doc.removeEventListener("mouseup", stop);
+ }
+ dragging = false;
+ }
+
+ function onKeydown(e) {
+ const { key } = e;
+
+ if (!ARROW_KEYS.includes(key)) {
+ return;
+ }
+
+ setDraggerDimensionsAndOffset();
+ const { offsetHeight, offsetTop, offsetLeft } = dragHelper;
+ let dragX = offsetLeft + offsetHeight / 2;
+ let dragY = offsetTop + offsetHeight / 2;
+
+ if (key === ArrowLeft && dragX > 0) {
+ dragX -= 1;
+ } else if (key === ArrowRight && dragX < maxWidth) {
+ dragX += 1;
+ } else if (key === ArrowUp && dragY > 0) {
+ dragY -= 1;
+ } else if (key === ArrowDown && dragY < maxHeight) {
+ dragY += 1;
+ }
+
+ onmove.apply(element, [dragX, dragY]);
+ }
+
+ element.addEventListener("mousedown", start);
+ element.addEventListener("keydown", onKeydown);
+}
+
+/**
+ * Calculates the contrast ratio for a DOM node's computed style against
+ * a given background.
+ *
+ * @param {Object} computedStyle
+ * The computed style for which we want to calculate the contrast ratio.
+ * @param {Object} backgroundColor
+ * Object with one or more of the following properties: value, min, max
+ * @return {Object}
+ * An object that may contain one or more of the following fields: error,
+ * isLargeText, value, score for contrast.
+ */
+function getContrastRatio(computedStyle, backgroundColor) {
+ const props = getTextProperties(computedStyle);
+
+ if (!props) {
+ return {
+ error: true,
+ };
+ }
+
+ return getContrastRatioAgainstBackground(backgroundColor, props);
+}
+
+module.exports = Spectrum;
diff --git a/devtools/client/shared/widgets/TableWidget.js b/devtools/client/shared/widgets/TableWidget.js
new file mode 100644
index 0000000000..d37559b587
--- /dev/null
+++ b/devtools/client/shared/widgets/TableWidget.js
@@ -0,0 +1,2031 @@
+/* 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/. */
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+loader.lazyRequireGetter(
+ this,
+ ["clearNamedTimeout", "setNamedTimeout"],
+ "resource://devtools/client/shared/widgets/view-helpers.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "naturalSortCaseInsensitive",
+ "resource://devtools/shared/natural-sort.js",
+ true
+);
+loader.lazyGetter(this, "standardSessionString", () => {
+ const l10n = new Localization(["devtools/client/storage.ftl"], true);
+ return l10n.formatValueSync("storage-expires-session");
+});
+
+const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const AFTER_SCROLL_DELAY = 100;
+
+// Different types of events emitted by the Various components of the
+// TableWidget.
+const EVENTS = {
+ CELL_EDIT: "cell-edit",
+ COLUMN_SORTED: "column-sorted",
+ COLUMN_TOGGLED: "column-toggled",
+ FIELDS_EDITABLE: "fields-editable",
+ HEADER_CONTEXT_MENU: "header-context-menu",
+ ROW_EDIT: "row-edit",
+ ROW_CONTEXT_MENU: "row-context-menu",
+ ROW_REMOVED: "row-removed",
+ ROW_SELECTED: "row-selected",
+ ROW_UPDATED: "row-updated",
+ TABLE_CLEARED: "table-cleared",
+ TABLE_FILTERED: "table-filtered",
+ SCROLL_END: "scroll-end",
+};
+Object.defineProperty(this, "EVENTS", {
+ value: EVENTS,
+ enumerable: true,
+ writable: false,
+});
+
+/**
+ * A table widget with various features like resizble/toggleable columns,
+ * sorting, keyboard navigation etc.
+ *
+ * @param {Node} node
+ * The container element for the table widget.
+ * @param {object} options
+ * - initialColumns: map of key vs display name for initial columns of
+ * the table. See @setupColumns for more info.
+ * - uniqueId: the column which will be the unique identifier of each
+ * entry in the table. Default: name.
+ * - wrapTextInElements: Don't ever use 'value' attribute on labels.
+ * Default: false.
+ * - emptyText: Localization ID for the text to display when there are
+ * no entries in the table to display.
+ * - highlightUpdated: true to highlight the changed/added row.
+ * - removableColumns: Whether columns are removeable. If set to false,
+ * the context menu in the headers will not appear.
+ * - firstColumn: key of the first column that should appear.
+ * - cellContextMenuId: ID of a <menupopup> element to be set as a
+ * context menu of every cell.
+ */
+function TableWidget(node, options = {}) {
+ EventEmitter.decorate(this);
+
+ this.document = node.ownerDocument;
+ this.window = this.document.defaultView;
+ this._parent = node;
+
+ const {
+ initialColumns,
+ emptyText,
+ uniqueId,
+ highlightUpdated,
+ removableColumns,
+ firstColumn,
+ wrapTextInElements,
+ cellContextMenuId,
+ l10n,
+ } = options;
+ this.emptyText = emptyText || "";
+ this.uniqueId = uniqueId || "name";
+ this.wrapTextInElements = wrapTextInElements || false;
+ this.firstColumn = firstColumn || "";
+ this.highlightUpdated = highlightUpdated || false;
+ this.removableColumns = removableColumns !== false;
+ this.cellContextMenuId = cellContextMenuId;
+ this.l10n = l10n;
+
+ this.tbody = this.document.createXULElement("hbox");
+ this.tbody.className = "table-widget-body theme-body";
+ this.tbody.setAttribute("flex", "1");
+ this.tbody.setAttribute("tabindex", "0");
+ this._parent.appendChild(this.tbody);
+ this.afterScroll = this.afterScroll.bind(this);
+ this.tbody.addEventListener("scroll", this.onScroll.bind(this));
+
+ // Prepare placeholder
+ this.placeholder = this.document.createElement("div");
+ this.placeholder.className = "plain table-widget-empty-text";
+ this._parent.appendChild(this.placeholder);
+ this.setPlaceholder(this.emptyText);
+
+ this.items = new Map();
+ this.columns = new Map();
+
+ // Setup the column headers context menu to allow users to hide columns at
+ // will.
+ if (this.removableColumns) {
+ this.onPopupCommand = this.onPopupCommand.bind(this);
+ this.setupHeadersContextMenu();
+ }
+
+ if (initialColumns) {
+ this.setColumns(initialColumns, uniqueId);
+ }
+
+ this.bindSelectedRow = id => {
+ this.selectedRow = id;
+ };
+ this.on(EVENTS.ROW_SELECTED, this.bindSelectedRow);
+
+ this.onChange = this.onChange.bind(this);
+ this.onEditorDestroyed = this.onEditorDestroyed.bind(this);
+ this.onEditorTab = this.onEditorTab.bind(this);
+ this.onKeydown = this.onKeydown.bind(this);
+ this.onMousedown = this.onMousedown.bind(this);
+ this.onRowRemoved = this.onRowRemoved.bind(this);
+
+ this.document.addEventListener("keydown", this.onKeydown);
+ this.document.addEventListener("mousedown", this.onMousedown);
+}
+
+TableWidget.prototype = {
+ items: null,
+ editBookmark: null,
+ scrollIntoViewOnUpdate: null,
+
+ /**
+ * Return true if the table body has a scrollbar.
+ */
+ get hasScrollbar() {
+ return this.tbody.scrollHeight > this.tbody.clientHeight;
+ },
+
+ /**
+ * Getter for the headers context menu popup id.
+ */
+ get headersContextMenu() {
+ if (this.menupopup) {
+ return this.menupopup.id;
+ }
+ return null;
+ },
+
+ /**
+ * Select the row corresponding to the json object `id`
+ */
+ set selectedRow(id) {
+ for (const column of this.columns.values()) {
+ if (id || id === "") {
+ column.selectRow(id[this.uniqueId] || id);
+ } else {
+ column.selectedRow = null;
+ column.selectRow(null);
+ }
+ }
+ },
+
+ /**
+ * Is a row currently selected?
+ *
+ * @return {Boolean}
+ * true or false.
+ */
+ get hasSelectedRow() {
+ return (
+ this.columns.get(this.uniqueId) &&
+ this.columns.get(this.uniqueId).selectedRow
+ );
+ },
+
+ /**
+ * Returns the json object corresponding to the selected row.
+ */
+ get selectedRow() {
+ return this.items.get(this.columns.get(this.uniqueId).selectedRow);
+ },
+
+ /**
+ * Selects the row at index `index`.
+ */
+ set selectedIndex(index) {
+ for (const column of this.columns.values()) {
+ column.selectRowAt(index);
+ }
+ },
+
+ /**
+ * Returns the index of the selected row.
+ */
+ get selectedIndex() {
+ return this.columns.get(this.uniqueId).selectedIndex;
+ },
+
+ /**
+ * Returns the index of the selected row disregarding hidden rows.
+ */
+ get visibleSelectedIndex() {
+ const column = this.firstVisibleColumn;
+ const cells = column.visibleCellNodes;
+
+ for (let i = 0; i < cells.length; i++) {
+ if (cells[i].classList.contains("theme-selected")) {
+ return i;
+ }
+ }
+
+ return -1;
+ },
+
+ /**
+ * Returns the first visible column.
+ */
+ get firstVisibleColumn() {
+ for (const column of this.columns.values()) {
+ if (column._private) {
+ continue;
+ }
+
+ if (column.column.clientHeight > 0) {
+ return column;
+ }
+ }
+
+ return null;
+ },
+
+ /**
+ * returns all editable columns.
+ */
+ get editableColumns() {
+ const filter = columns => {
+ columns = [...columns].filter(col => {
+ if (col.clientWidth === 0) {
+ return false;
+ }
+
+ const cell = col.querySelector(".table-widget-cell");
+
+ for (const selector of this._editableFieldsEngine.selectors) {
+ if (cell.matches(selector)) {
+ return true;
+ }
+ }
+
+ return false;
+ });
+
+ return columns;
+ };
+
+ const columns = this._parent.querySelectorAll(".table-widget-column");
+ return filter(columns);
+ },
+
+ /**
+ * Emit all cell edit events.
+ */
+ onChange(data) {
+ const changedField = data.change.field;
+ const colName = changedField.parentNode.id;
+ const column = this.columns.get(colName);
+ const uniqueId = column.table.uniqueId;
+ const itemIndex = column.cellNodes.indexOf(changedField);
+ const items = {};
+
+ for (const [name, col] of this.columns) {
+ items[name] = col.cellNodes[itemIndex].value;
+ }
+
+ const change = {
+ host: this.host,
+ key: uniqueId,
+ field: colName,
+ oldValue: data.change.oldValue,
+ newValue: data.change.newValue,
+ items,
+ };
+
+ // A rows position in the table can change as the result of an edit. In
+ // order to ensure that the correct row is highlighted after an edit we
+ // save the uniqueId in editBookmark.
+ this.editBookmark =
+ colName === uniqueId ? change.newValue : items[uniqueId];
+ this.emit(EVENTS.CELL_EDIT, change);
+ },
+
+ onEditorDestroyed() {
+ this._editableFieldsEngine = null;
+ },
+
+ /**
+ * Called by the inplace editor when Tab / Shift-Tab is pressed in edit-mode.
+ * Because tables are live any row, column, cell or table can be added,
+ * deleted or moved by deleting and adding e.g. a row again.
+ *
+ * This presents various challenges when navigating via the keyboard so please
+ * keep this in mind whenever editing this method.
+ *
+ * @param {Event} event
+ * Keydown event
+ */
+ onEditorTab(event) {
+ const textbox = event.target;
+ const editor = this._editableFieldsEngine;
+
+ if (textbox.id !== editor.INPUT_ID) {
+ return;
+ }
+
+ const column = textbox.parentNode;
+
+ // Changing any value can change the position of the row depending on which
+ // column it is currently sorted on. In addition to this, the table cell may
+ // have been edited and had to be recreated when the user has pressed tab or
+ // shift+tab. Both of these situations require us to recover our target,
+ // select the appropriate row and move the textbox on to the next cell.
+ if (editor.changePending) {
+ // We need to apply a change, which can mean that the position of cells
+ // within the table can change. Because of this we need to wait for
+ // EVENTS.ROW_EDIT and then move the textbox.
+ this.once(EVENTS.ROW_EDIT, uniqueId => {
+ let columnObj;
+ const cols = this.editableColumns;
+ let rowIndex = this.visibleSelectedIndex;
+ const colIndex = cols.indexOf(column);
+ let newIndex;
+
+ // If the row has been deleted we should bail out.
+ if (!uniqueId) {
+ return;
+ }
+
+ // Find the column we need to move to.
+ if (event.shiftKey) {
+ // Navigate backwards on shift tab.
+ if (colIndex === 0) {
+ if (rowIndex === 0) {
+ return;
+ }
+ newIndex = cols.length - 1;
+ } else {
+ newIndex = colIndex - 1;
+ }
+ } else if (colIndex === cols.length - 1) {
+ const id = cols[0].id;
+ columnObj = this.columns.get(id);
+ const maxRowIndex = columnObj.visibleCellNodes.length - 1;
+ if (rowIndex === maxRowIndex) {
+ return;
+ }
+ newIndex = 0;
+ } else {
+ newIndex = colIndex + 1;
+ }
+
+ const newcol = cols[newIndex];
+ columnObj = this.columns.get(newcol.id);
+
+ // Select the correct row even if it has moved due to sorting.
+ const dataId = editor.currentTarget.getAttribute("data-id");
+ if (this.items.get(dataId)) {
+ this.emit(EVENTS.ROW_SELECTED, dataId);
+ } else {
+ this.emit(EVENTS.ROW_SELECTED, uniqueId);
+ }
+
+ // EVENTS.ROW_SELECTED may have changed the selected row so let's save
+ // the result in rowIndex.
+ rowIndex = this.visibleSelectedIndex;
+
+ // Edit the appropriate cell.
+ const cells = columnObj.visibleCellNodes;
+ const cell = cells[rowIndex];
+ editor.edit(cell);
+
+ // Remove flash-out class... it won't have been auto-removed because the
+ // cell was hidden for editing.
+ cell.classList.remove("flash-out");
+ });
+ }
+
+ // Begin cell edit. We always do this so that we can begin editing even in
+ // the case that the previous edit will cause the row to move.
+ const cell = this.getEditedCellOnTab(event, column);
+ editor.edit(cell);
+
+ // Prevent default input tabbing behaviour
+ event.preventDefault();
+ },
+
+ /**
+ * Get the cell that will be edited next on tab / shift tab and highlight the
+ * appropriate row. Edits etc. are not taken into account.
+ *
+ * This is used to tab from one field to another without editing and makes the
+ * editor much more responsive.
+ *
+ * @param {Event} event
+ * Keydown event
+ */
+ getEditedCellOnTab(event, column) {
+ let cell = null;
+ const cols = this.editableColumns;
+ const rowIndex = this.visibleSelectedIndex;
+ const colIndex = cols.indexOf(column);
+ const maxCol = cols.length - 1;
+ const maxRow = this.columns.get(column.id).visibleCellNodes.length - 1;
+
+ if (event.shiftKey) {
+ // Navigate backwards on shift tab.
+ if (colIndex === 0) {
+ if (rowIndex === 0) {
+ this._editableFieldsEngine.completeEdit();
+ return null;
+ }
+
+ column = cols[cols.length - 1];
+ const cells = this.columns.get(column.id).visibleCellNodes;
+ cell = cells[rowIndex - 1];
+
+ const rowId = cell.getAttribute("data-id");
+ this.emit(EVENTS.ROW_SELECTED, rowId);
+ } else {
+ column = cols[colIndex - 1];
+ const cells = this.columns.get(column.id).visibleCellNodes;
+ cell = cells[rowIndex];
+ }
+ } else if (colIndex === maxCol) {
+ // If in the rightmost column on the last row stop editing.
+ if (rowIndex === maxRow) {
+ this._editableFieldsEngine.completeEdit();
+ return null;
+ }
+
+ // If in the rightmost column of a row then move to the first column of
+ // the next row.
+ column = cols[0];
+ const cells = this.columns.get(column.id).visibleCellNodes;
+ cell = cells[rowIndex + 1];
+
+ const rowId = cell.getAttribute("data-id");
+ this.emit(EVENTS.ROW_SELECTED, rowId);
+ } else {
+ // Navigate forwards on tab.
+ column = cols[colIndex + 1];
+ const cells = this.columns.get(column.id).visibleCellNodes;
+ cell = cells[rowIndex];
+ }
+
+ return cell;
+ },
+
+ /**
+ * Reset the editable fields engine if the currently edited row is removed.
+ *
+ * @param {String} event
+ * The event name "event-removed."
+ * @param {Object} row
+ * The values from the removed row.
+ */
+ onRowRemoved(row) {
+ if (!this._editableFieldsEngine || !this._editableFieldsEngine.isEditing) {
+ return;
+ }
+
+ const removedKey = row[this.uniqueId];
+ const column = this.columns.get(this.uniqueId);
+
+ if (removedKey in column.items) {
+ return;
+ }
+
+ // The target is lost so we need to hide the remove the textbox from the DOM
+ // and reset the target nodes.
+ this.onEditorTargetLost();
+ },
+
+ /**
+ * Cancel an edit because the edit target has been lost.
+ */
+ onEditorTargetLost() {
+ const editor = this._editableFieldsEngine;
+
+ if (!editor || !editor.isEditing) {
+ return;
+ }
+
+ editor.cancelEdit();
+ },
+
+ /**
+ * Keydown event handler for the table. Used for keyboard navigation amongst
+ * rows.
+ */
+ onKeydown(event) {
+ // If we are in edit mode bail out.
+ if (this._editableFieldsEngine && this._editableFieldsEngine.isEditing) {
+ return;
+ }
+
+ // We need to get the first *visible* selected cell. Some columns are hidden
+ // e.g. because they contain a unique compound key for cookies that is never
+ // displayed in the UI. To do this we get all selected cells and filter out
+ // any that are hidden.
+ const selectedCells = [
+ ...this.tbody.querySelectorAll(".theme-selected"),
+ ].filter(cell => cell.clientWidth > 0);
+ // Select the first visible selected cell.
+ const selectedCell = selectedCells[0];
+ if (!selectedCell) {
+ return;
+ }
+
+ let colName;
+ let column;
+ let visibleCells;
+ let index;
+ let cell;
+
+ switch (event.keyCode) {
+ case KeyCodes.DOM_VK_UP:
+ event.preventDefault();
+
+ colName = selectedCell.parentNode.id;
+ column = this.columns.get(colName);
+ visibleCells = column.visibleCellNodes;
+ index = visibleCells.indexOf(selectedCell);
+
+ if (index > 0) {
+ index--;
+ } else {
+ index = visibleCells.length - 1;
+ }
+
+ cell = visibleCells[index];
+
+ this.emit(EVENTS.ROW_SELECTED, cell.getAttribute("data-id"));
+ break;
+ case KeyCodes.DOM_VK_DOWN:
+ event.preventDefault();
+
+ colName = selectedCell.parentNode.id;
+ column = this.columns.get(colName);
+ visibleCells = column.visibleCellNodes;
+ index = visibleCells.indexOf(selectedCell);
+
+ if (index === visibleCells.length - 1) {
+ index = 0;
+ } else {
+ index++;
+ }
+
+ cell = visibleCells[index];
+
+ this.emit(EVENTS.ROW_SELECTED, cell.getAttribute("data-id"));
+ break;
+ }
+ },
+
+ /**
+ * Close any editors if the area "outside the table" is clicked. In reality,
+ * the table covers the whole area but there are labels filling the top few
+ * rows. This method clears any inline editors if an area outside a textbox or
+ * label is clicked.
+ */
+ onMousedown({ target }) {
+ const localName = target.localName;
+
+ if (localName === "input" || !this._editableFieldsEngine) {
+ return;
+ }
+
+ // Force any editor fields to hide due to XUL focus quirks.
+ this._editableFieldsEngine.blur();
+ },
+
+ /**
+ * Make table fields editable.
+ *
+ * @param {String|Array} editableColumns
+ * An array or comma separated list of editable column names.
+ */
+ makeFieldsEditable(editableColumns) {
+ const selectors = [];
+
+ if (typeof editableColumns === "string") {
+ editableColumns = [editableColumns];
+ }
+
+ for (const id of editableColumns) {
+ selectors.push("#" + id + " .table-widget-cell");
+ }
+
+ for (const [name, column] of this.columns) {
+ if (!editableColumns.includes(name)) {
+ column.column.setAttribute("readonly", "");
+ }
+ }
+
+ if (this._editableFieldsEngine) {
+ this._editableFieldsEngine.selectors = selectors;
+ this._editableFieldsEngine.items = this.items;
+ } else {
+ this._editableFieldsEngine = new EditableFieldsEngine({
+ root: this.tbody,
+ onTab: this.onEditorTab,
+ onTriggerEvent: "dblclick",
+ selectors,
+ items: this.items,
+ });
+
+ this._editableFieldsEngine.on("change", this.onChange);
+ this._editableFieldsEngine.on("destroyed", this.onEditorDestroyed);
+
+ this.on(EVENTS.ROW_REMOVED, this.onRowRemoved);
+ this.on(EVENTS.TABLE_CLEARED, this._editableFieldsEngine.cancelEdit);
+
+ this.emit(EVENTS.FIELDS_EDITABLE, this._editableFieldsEngine);
+ }
+ },
+
+ destroy() {
+ this.off(EVENTS.ROW_SELECTED, this.bindSelectedRow);
+ this.off(EVENTS.ROW_REMOVED, this.onRowRemoved);
+
+ this.document.removeEventListener("keydown", this.onKeydown);
+ this.document.removeEventListener("mousedown", this.onMousedown);
+
+ if (this._editableFieldsEngine) {
+ this.off(EVENTS.TABLE_CLEARED, this._editableFieldsEngine.cancelEdit);
+ this._editableFieldsEngine.off("change", this.onChange);
+ this._editableFieldsEngine.off("destroyed", this.onEditorDestroyed);
+ this._editableFieldsEngine.destroy();
+ this._editableFieldsEngine = null;
+ }
+
+ if (this.menupopup) {
+ this.menupopup.removeEventListener("command", this.onPopupCommand);
+ this.menupopup.remove();
+ }
+ },
+
+ /**
+ * Sets the localization ID of the description to be shown when the table is empty.
+ *
+ * @param {String} l10nID
+ * The ID of the localization string.
+ * @param {String} learnMoreURL
+ * A URL referring to a website with further information related to
+ * the data shown in the table widget.
+ */
+ setPlaceholder(l10nID, learnMoreURL) {
+ if (learnMoreURL) {
+ let placeholderLink = this.placeholder.firstElementChild;
+ if (!placeholderLink) {
+ placeholderLink = this.document.createElement("a");
+ placeholderLink.setAttribute("target", "_blank");
+ placeholderLink.setAttribute("data-l10n-name", "learn-more-link");
+ this.placeholder.appendChild(placeholderLink);
+ }
+ placeholderLink.setAttribute("href", learnMoreURL);
+ } else {
+ // Remove link element if no learn more URL is given
+ this.placeholder.firstElementChild?.remove();
+ }
+
+ this.l10n.setAttributes(this.placeholder, l10nID);
+ },
+
+ /**
+ * Prepares the context menu for the headers of the table columns. This
+ * context menu allows users to toggle various columns, only with an exception
+ * of the unique columns and when only two columns are visible in the table.
+ */
+ setupHeadersContextMenu() {
+ let popupset = this.document.getElementsByTagName("popupset")[0];
+ if (!popupset) {
+ popupset = this.document.createXULElement("popupset");
+ this.document.documentElement.appendChild(popupset);
+ }
+
+ this.menupopup = this.document.createXULElement("menupopup");
+ this.menupopup.id = "table-widget-column-select";
+ this.menupopup.addEventListener("command", this.onPopupCommand);
+ popupset.appendChild(this.menupopup);
+ this.populateMenuPopup();
+ },
+
+ /**
+ * Populates the header context menu with the names of the columns along with
+ * displaying which columns are hidden or visible.
+ *
+ * @param {Array} privateColumns=[]
+ * An array of column names that should never appear in the table. This
+ * allows us to e.g. have an invisible compound primary key for a
+ * table's rows.
+ */
+ populateMenuPopup(privateColumns = []) {
+ if (!this.menupopup) {
+ return;
+ }
+
+ while (this.menupopup.firstChild) {
+ this.menupopup.firstChild.remove();
+ }
+
+ for (const column of this.columns.values()) {
+ if (privateColumns.includes(column.id)) {
+ continue;
+ }
+
+ const menuitem = this.document.createXULElement("menuitem");
+ menuitem.setAttribute("label", column.header.getAttribute("value"));
+ menuitem.setAttribute("data-id", column.id);
+ menuitem.setAttribute("type", "checkbox");
+ menuitem.setAttribute("checked", !column.hidden);
+ if (column.id == this.uniqueId) {
+ menuitem.setAttribute("disabled", "true");
+ }
+ this.menupopup.appendChild(menuitem);
+ }
+ const checked = this.menupopup.querySelectorAll("menuitem[checked]");
+ if (checked.length == 2) {
+ checked[checked.length - 1].setAttribute("disabled", "true");
+ }
+ },
+
+ /**
+ * Event handler for the `command` event on the column headers context menu
+ */
+ onPopupCommand(event) {
+ const item = event.originalTarget;
+ let checked = !!item.getAttribute("checked");
+ const id = item.getAttribute("data-id");
+ this.emit(EVENTS.HEADER_CONTEXT_MENU, id, checked);
+ checked = this.menupopup.querySelectorAll("menuitem[checked]");
+ const disabled = this.menupopup.querySelectorAll("menuitem[disabled]");
+ if (checked.length == 2) {
+ checked[checked.length - 1].setAttribute("disabled", "true");
+ } else if (disabled.length > 1) {
+ disabled[disabled.length - 1].removeAttribute("disabled");
+ }
+ },
+
+ /**
+ * Creates the columns in the table. Without calling this method, data cannot
+ * be inserted into the table unless `initialColumns` was supplied.
+ *
+ * @param {Object} columns
+ * A key value pair representing the columns of the table. Where the
+ * key represents the id of the column and the value is the displayed
+ * label in the header of the column.
+ * @param {String} sortOn
+ * The id of the column on which the table will be initially sorted on.
+ * @param {Array} hiddenColumns
+ * Ids of all the columns that are hidden by default.
+ * @param {Array} privateColumns=[]
+ * An array of column names that should never appear in the table. This
+ * allows us to e.g. have an invisible compound primary key for a
+ * table's rows.
+ */
+ setColumns(
+ columns,
+ sortOn = this.sortedOn,
+ hiddenColumns = [],
+ privateColumns = []
+ ) {
+ for (const column of this.columns.values()) {
+ column.destroy();
+ }
+
+ this.columns.clear();
+
+ if (!(sortOn in columns)) {
+ sortOn = null;
+ }
+
+ if (!(this.firstColumn in columns)) {
+ this.firstColumn = null;
+ }
+
+ if (this.firstColumn) {
+ this.columns.set(
+ this.firstColumn,
+ new Column(this, this.firstColumn, columns[this.firstColumn])
+ );
+ }
+
+ for (const id in columns) {
+ if (!sortOn) {
+ sortOn = id;
+ }
+
+ if (this.firstColumn && id == this.firstColumn) {
+ continue;
+ }
+
+ this.columns.set(id, new Column(this, id, columns[id]));
+ if (hiddenColumns.includes(id) || privateColumns.includes(id)) {
+ // Hide the column.
+ this.columns.get(id).toggleColumn();
+
+ if (privateColumns.includes(id)) {
+ this.columns.get(id).private = true;
+ }
+ }
+ }
+ this.sortedOn = sortOn;
+ this.sortBy(this.sortedOn);
+ this.populateMenuPopup(privateColumns);
+ },
+
+ /**
+ * Returns true if the passed string or the row json object corresponds to the
+ * selected item in the table.
+ */
+ isSelected(item) {
+ if (typeof item == "object") {
+ item = item[this.uniqueId];
+ }
+
+ return this.selectedRow && item == this.selectedRow[this.uniqueId];
+ },
+
+ /**
+ * Selects the row corresponding to the `id` json.
+ */
+ selectRow(id) {
+ this.selectedRow = id;
+ },
+
+ /**
+ * Selects the next row. Cycles over to the first row if last row is selected
+ */
+ selectNextRow() {
+ for (const column of this.columns.values()) {
+ column.selectNextRow();
+ }
+ },
+
+ /**
+ * Selects the previous row. Cycles over to the last row if first row is
+ * selected.
+ */
+ selectPreviousRow() {
+ for (const column of this.columns.values()) {
+ column.selectPreviousRow();
+ }
+ },
+
+ /**
+ * Clears any selected row.
+ */
+ clearSelection() {
+ this.selectedIndex = -1;
+ },
+
+ /**
+ * Adds a row into the table.
+ *
+ * @param {object} item
+ * The object from which the key-value pairs will be taken and added
+ * into the row. This object can have any arbitarary key value pairs,
+ * but only those will be used whose keys match to the ids of the
+ * columns.
+ * @param {boolean} suppressFlash
+ * true to not flash the row while inserting the row.
+ */
+ push(item, suppressFlash) {
+ if (!this.sortedOn || !this.columns) {
+ console.error("Can't insert item without defining columns first");
+ return;
+ }
+
+ if (this.items.has(item[this.uniqueId])) {
+ this.update(item);
+ return;
+ }
+
+ if (this.editBookmark && !this.items.has(this.editBookmark)) {
+ // Key has been updated... update bookmark.
+ this.editBookmark = item[this.uniqueId];
+ }
+
+ const index = this.columns.get(this.sortedOn).push(item);
+ for (const [key, column] of this.columns) {
+ if (key != this.sortedOn) {
+ column.insertAt(item, index);
+ }
+ column.updateZebra();
+ }
+ this.items.set(item[this.uniqueId], item);
+ this.tbody.removeAttribute("empty");
+
+ if (!suppressFlash) {
+ this.emit(EVENTS.ROW_UPDATED, item[this.uniqueId]);
+ }
+
+ this.emit(EVENTS.ROW_EDIT, item[this.uniqueId]);
+ },
+
+ /**
+ * Removes the row associated with the `item` object.
+ */
+ remove(item) {
+ if (typeof item != "object") {
+ item = this.items.get(item);
+ }
+ if (!item) {
+ return;
+ }
+ const removed = this.items.delete(item[this.uniqueId]);
+
+ if (!removed) {
+ return;
+ }
+ for (const column of this.columns.values()) {
+ column.remove(item);
+ column.updateZebra();
+ }
+ if (this.items.size === 0) {
+ this.selectedRow = null;
+ this.tbody.setAttribute("empty", "empty");
+ }
+
+ this.emit(EVENTS.ROW_REMOVED, item);
+ },
+
+ /**
+ * Updates the items in the row corresponding to the `item` object previously
+ * used to insert the row using `push` method. The linking is done via the
+ * `uniqueId` key's value.
+ */
+ update(item) {
+ const oldItem = this.items.get(item[this.uniqueId]);
+ if (!oldItem) {
+ return;
+ }
+ this.items.set(item[this.uniqueId], item);
+
+ let changed = false;
+ for (const column of this.columns.values()) {
+ if (item[column.id] != oldItem[column.id]) {
+ column.update(item);
+ changed = true;
+ }
+ }
+ if (changed) {
+ this.emit(EVENTS.ROW_UPDATED, item[this.uniqueId]);
+ this.emit(EVENTS.ROW_EDIT, item[this.uniqueId]);
+ }
+ },
+
+ /**
+ * Removes all of the rows from the table.
+ */
+ clear() {
+ this.items.clear();
+ for (const column of this.columns.values()) {
+ column.clear();
+ }
+ this.tbody.setAttribute("empty", "empty");
+ this.setPlaceholder(this.emptyText);
+
+ this.selectedRow = null;
+
+ this.emit(EVENTS.TABLE_CLEARED, this);
+ },
+
+ /**
+ * Sorts the table by a given column.
+ *
+ * @param {string} column
+ * The id of the column on which the table should be sorted.
+ */
+ sortBy(column) {
+ this.emit(EVENTS.COLUMN_SORTED, column);
+ this.sortedOn = column;
+
+ if (!this.items.size) {
+ return;
+ }
+
+ // First sort the column to "sort by" explicitly.
+ const sortedItems = this.columns.get(column).sort([...this.items.values()]);
+
+ // Then, sort all the other columns (id !== column) only based on the
+ // sortedItems provided by the first sort.
+ // Each column keeps track of the fact that it is the "sort by" column or
+ // not, so this will not shuffle the items and will just make sure each
+ // column displays the correct value.
+ for (const [id, col] of this.columns) {
+ if (id !== column) {
+ col.sort(sortedItems);
+ }
+ }
+ },
+
+ /**
+ * Filters the table based on a specific value
+ *
+ * @param {String} value: The filter value
+ * @param {Array} ignoreProps: Props to ignore while filtering
+ */
+ filterItems(value, ignoreProps = []) {
+ if (this.filteredValue == value) {
+ return;
+ }
+ if (this._editableFieldsEngine) {
+ this._editableFieldsEngine.completeEdit();
+ }
+
+ this.filteredValue = value;
+ if (!value) {
+ this.emit(EVENTS.TABLE_FILTERED, []);
+ return;
+ }
+ // Shouldn't be case-sensitive
+ value = value.toLowerCase();
+
+ const itemsToHide = [...this.items.keys()];
+ // Loop through all items and hide unmatched items
+ for (const [id, val] of this.items) {
+ for (const prop in val) {
+ const column = this.columns.get(prop);
+ if (ignoreProps.includes(prop) || column.hidden) {
+ continue;
+ }
+
+ const propValue = val[prop].toString().toLowerCase();
+ if (propValue.includes(value)) {
+ itemsToHide.splice(itemsToHide.indexOf(id), 1);
+ break;
+ }
+ }
+ }
+ this.emit(EVENTS.TABLE_FILTERED, itemsToHide);
+ },
+
+ /**
+ * Calls the afterScroll function when the user has stopped scrolling
+ */
+ onScroll() {
+ clearNamedTimeout("table-scroll");
+ setNamedTimeout("table-scroll", AFTER_SCROLL_DELAY, this.afterScroll);
+ },
+
+ /**
+ * Emits the "scroll-end" event when the whole table is scrolled
+ */
+ afterScroll() {
+ const maxScrollTop = this.tbody.scrollHeight - this.tbody.clientHeight;
+ // Emit scroll-end event when 9/10 of the table is scrolled
+ if (this.tbody.scrollTop >= 0.9 * maxScrollTop) {
+ this.emit("scroll-end");
+ }
+ },
+};
+
+TableWidget.EVENTS = EVENTS;
+
+module.exports.TableWidget = TableWidget;
+
+/**
+ * A single column object in the table.
+ *
+ * @param {TableWidget} table
+ * The table object to which the column belongs.
+ * @param {string} id
+ * Id of the column.
+ * @param {String} header
+ * The displayed string on the column's header.
+ */
+function Column(table, id, header) {
+ // By default cells are visible in the UI.
+ this._private = false;
+
+ this.tbody = table.tbody;
+ this.document = table.document;
+ this.window = table.window;
+ this.id = id;
+ this.uniqueId = table.uniqueId;
+ this.wrapTextInElements = table.wrapTextInElements;
+ this.table = table;
+ this.cells = [];
+ this.items = {};
+
+ this.highlightUpdated = table.highlightUpdated;
+
+ this.column = this.document.createElementNS(HTML_NS, "div");
+ this.column.id = id;
+ this.column.className = "table-widget-column";
+ this.tbody.appendChild(this.column);
+
+ this.splitter = this.document.createXULElement("splitter");
+ this.splitter.className = "devtools-side-splitter";
+ this.tbody.appendChild(this.splitter);
+
+ this.header = this.document.createXULElement("label");
+ this.header.className = "devtools-toolbar table-widget-column-header";
+ this.header.setAttribute("value", header);
+ this.column.appendChild(this.header);
+ if (table.headersContextMenu) {
+ this.header.setAttribute("context", table.headersContextMenu);
+ }
+ this.toggleColumn = this.toggleColumn.bind(this);
+ this.table.on(EVENTS.HEADER_CONTEXT_MENU, this.toggleColumn);
+
+ this.onColumnSorted = this.onColumnSorted.bind(this);
+ this.table.on(EVENTS.COLUMN_SORTED, this.onColumnSorted);
+
+ this.onRowUpdated = this.onRowUpdated.bind(this);
+ this.table.on(EVENTS.ROW_UPDATED, this.onRowUpdated);
+
+ this.onTableFiltered = this.onTableFiltered.bind(this);
+ this.table.on(EVENTS.TABLE_FILTERED, this.onTableFiltered);
+
+ this.onClick = this.onClick.bind(this);
+ this.onMousedown = this.onMousedown.bind(this);
+ this.column.addEventListener("click", this.onClick);
+ this.column.addEventListener("mousedown", this.onMousedown);
+}
+
+Column.prototype = {
+ // items is a cell-id to cell-index map. It is basically a reverse map of the
+ // this.cells object and is used to quickly reverse lookup a cell by its id
+ // instead of looping through the cells array. This reverse map is not kept
+ // upto date in sync with the cells array as updating it is in itself a loop
+ // through all the cells of the columns. Thus update it on demand when it goes
+ // out of sync with this.cells.
+ items: null,
+
+ // _itemsDirty is a flag which becomes true when this.items goes out of sync
+ // with this.cells
+ _itemsDirty: null,
+
+ selectedRow: null,
+
+ cells: null,
+
+ /**
+ * Gets whether the table is sorted on this column or not.
+ * 0 - not sorted.
+ * 1 - ascending order
+ * 2 - descending order
+ */
+ get sorted() {
+ return this._sortState || 0;
+ },
+
+ /**
+ * Returns a boolean indicating whether the column is hidden.
+ */
+ get hidden() {
+ return this.column.hidden;
+ },
+
+ /**
+ * Get the private state of the column (visibility in the UI).
+ */
+ get private() {
+ return this._private;
+ },
+
+ /**
+ * Set the private state of the column (visibility in the UI).
+ *
+ * @param {Boolean} state
+ * Private (true or false)
+ */
+ set private(state) {
+ this._private = state;
+ },
+
+ /**
+ * Sets the sorted value
+ */
+ set sorted(value) {
+ if (!value) {
+ this.header.removeAttribute("sorted");
+ } else {
+ this.header.setAttribute(
+ "sorted",
+ value == 1 ? "ascending" : "descending"
+ );
+ }
+ this._sortState = value;
+ },
+
+ /**
+ * Gets the selected row in the column.
+ */
+ get selectedIndex() {
+ if (!this.selectedRow) {
+ return -1;
+ }
+ return this.items[this.selectedRow];
+ },
+
+ get cellNodes() {
+ return [...this.column.querySelectorAll(".table-widget-cell")];
+ },
+
+ get visibleCellNodes() {
+ const editor = this.table._editableFieldsEngine;
+ const nodes = this.cellNodes.filter(node => {
+ // If the cell is currently being edited we should class it as visible.
+ if (editor && editor.currentTarget === node) {
+ return true;
+ }
+ return node.clientWidth !== 0;
+ });
+
+ return nodes;
+ },
+
+ /**
+ * Called when the column is sorted by.
+ *
+ * @param {string} column
+ * The id of the column being sorted by.
+ */
+ onColumnSorted(column) {
+ if (column != this.id) {
+ this.sorted = 0;
+ return;
+ } else if (this.sorted == 0 || this.sorted == 2) {
+ this.sorted = 1;
+ } else {
+ this.sorted = 2;
+ }
+ this.updateZebra();
+ },
+
+ onTableFiltered(itemsToHide) {
+ this._updateItems();
+ if (!this.cells) {
+ return;
+ }
+ for (const cell of this.cells) {
+ cell.hidden = false;
+ }
+ for (const id of itemsToHide) {
+ this.cells[this.items[id]].hidden = true;
+ }
+ this.updateZebra();
+ },
+
+ /**
+ * Called when a row is updated e.g. a cell is changed. This means that
+ * for a new row this method will be called once for each column. If a single
+ * cell is changed this method will be called just once.
+ *
+ * @param {string} event
+ * The event name of the event. i.e. EVENTS.ROW_UPDATED
+ * @param {string} id
+ * The unique id of the object associated with the row.
+ */
+ onRowUpdated(id) {
+ this._updateItems();
+
+ if (this.highlightUpdated && this.items[id] != null) {
+ if (this.table.scrollIntoViewOnUpdate) {
+ const cell = this.cells[this.items[id]];
+
+ // When a new row is created this method is called once for each column
+ // as each cell is updated. We can only scroll to cells if they are
+ // visible. We check for visibility and once we find the first visible
+ // cell in a row we scroll it into view and reset the
+ // scrollIntoViewOnUpdate flag.
+ if (cell.label.clientHeight > 0) {
+ cell.scrollIntoView();
+
+ this.table.scrollIntoViewOnUpdate = null;
+ }
+ }
+
+ if (this.table.editBookmark) {
+ // A rows position in the table can change as the result of an edit. In
+ // order to ensure that the correct row is highlighted after an edit we
+ // save the uniqueId in editBookmark. Here we send the signal that the
+ // row has been edited and that the row needs to be selected again.
+ this.table.emit(EVENTS.ROW_SELECTED, this.table.editBookmark);
+ this.table.editBookmark = null;
+ }
+
+ this.cells[this.items[id]].flash();
+ }
+
+ this.updateZebra();
+ },
+
+ destroy() {
+ this.table.off(EVENTS.COLUMN_SORTED, this.onColumnSorted);
+ this.table.off(EVENTS.HEADER_CONTEXT_MENU, this.toggleColumn);
+ this.table.off(EVENTS.ROW_UPDATED, this.onRowUpdated);
+ this.table.off(EVENTS.TABLE_FILTERED, this.onTableFiltered);
+
+ this.column.removeEventListener("click", this.onClick);
+ this.column.removeEventListener("mousedown", this.onMousedown);
+
+ this.splitter.remove();
+ this.column.remove();
+ this.cells = null;
+ this.items = null;
+ this.selectedRow = null;
+ },
+
+ /**
+ * Selects the row at the `index` index
+ */
+ selectRowAt(index) {
+ if (this.selectedRow != null) {
+ this.cells[this.items[this.selectedRow]].classList.remove(
+ "theme-selected"
+ );
+ }
+
+ const cell = this.cells[index];
+ if (cell) {
+ cell.classList.add("theme-selected");
+ this.selectedRow = cell.id;
+ } else {
+ this.selectedRow = null;
+ }
+ },
+
+ /**
+ * Selects the row with the object having the `uniqueId` value as `id`
+ */
+ selectRow(id) {
+ this._updateItems();
+ this.selectRowAt(this.items[id]);
+ },
+
+ /**
+ * Selects the next row. Cycles to first if last row is selected.
+ */
+ selectNextRow() {
+ this._updateItems();
+ let index = this.items[this.selectedRow] + 1;
+ if (index == this.cells.length) {
+ index = 0;
+ }
+ this.selectRowAt(index);
+ },
+
+ /**
+ * Selects the previous row. Cycles to last if first row is selected.
+ */
+ selectPreviousRow() {
+ this._updateItems();
+ let index = this.items[this.selectedRow] - 1;
+ if (index == -1) {
+ index = this.cells.length - 1;
+ }
+ this.selectRowAt(index);
+ },
+
+ /**
+ * Pushes the `item` object into the column. If this column is sorted on,
+ * then inserts the object at the right position based on the column's id
+ * key's value.
+ *
+ * @returns {number}
+ * The index of the currently pushed item.
+ */
+ push(item) {
+ const value = item[this.id];
+
+ if (this.sorted) {
+ let index;
+ if (this.sorted == 1) {
+ index = this.cells.findIndex(element => {
+ return (
+ naturalSortCaseInsensitive(
+ value,
+ element.value,
+ standardSessionString
+ ) === -1
+ );
+ });
+ } else {
+ index = this.cells.findIndex(element => {
+ return (
+ naturalSortCaseInsensitive(
+ value,
+ element.value,
+ standardSessionString
+ ) === 1
+ );
+ });
+ }
+ index = index >= 0 ? index : this.cells.length;
+ if (index < this.cells.length) {
+ this._itemsDirty = true;
+ }
+ this.items[item[this.uniqueId]] = index;
+ this.cells.splice(index, 0, new Cell(this, item, this.cells[index]));
+ return index;
+ }
+
+ this.items[item[this.uniqueId]] = this.cells.length;
+ return this.cells.push(new Cell(this, item)) - 1;
+ },
+
+ /**
+ * Inserts the `item` object at the given `index` index in the table.
+ */
+ insertAt(item, index) {
+ if (index < this.cells.length) {
+ this._itemsDirty = true;
+ }
+ this.items[item[this.uniqueId]] = index;
+ this.cells.splice(index, 0, new Cell(this, item, this.cells[index]));
+ this.updateZebra();
+ },
+
+ /**
+ * Event handler for the command event coming from the header context menu.
+ * Toggles the column if it was requested by the user.
+ * When called explicitly without parameters, it toggles the corresponding
+ * column.
+ *
+ * @param {string} event
+ * The name of the event. i.e. EVENTS.HEADER_CONTEXT_MENU
+ * @param {string} id
+ * Id of the column to be toggled
+ * @param {string} checked
+ * true if the column is visible
+ */
+ toggleColumn(id, checked) {
+ if (!arguments.length) {
+ // Act like a toggling method when called with no params
+ id = this.id;
+ checked = this.column.hidden;
+ }
+ if (id != this.id) {
+ return;
+ }
+ if (checked) {
+ this.column.hidden = false;
+ this.tbody.insertBefore(this.splitter, this.column.nextSibling);
+ } else {
+ this.column.hidden = true;
+ this.splitter.remove();
+ }
+ },
+
+ /**
+ * Removes the corresponding item from the column and hide the last visible
+ * splitter with CSS, so we do not add splitter elements for hidden columns.
+ */
+ remove(item) {
+ this._updateItems();
+ const index = this.items[item[this.uniqueId]];
+ if (index == null) {
+ return;
+ }
+
+ if (index < this.cells.length) {
+ this._itemsDirty = true;
+ }
+ this.cells[index].destroy();
+ this.cells.splice(index, 1);
+ delete this.items[item[this.uniqueId]];
+ },
+
+ /**
+ * Updates the corresponding item from the column.
+ */
+ update(item) {
+ this._updateItems();
+
+ const index = this.items[item[this.uniqueId]];
+ if (index == null) {
+ return;
+ }
+
+ this.cells[index].value = item[this.id];
+ },
+
+ /**
+ * Updates the `this.items` cell-id vs cell-index map to be in sync with
+ * `this.cells`.
+ */
+ _updateItems() {
+ if (!this._itemsDirty) {
+ return;
+ }
+ for (let i = 0; i < this.cells.length; i++) {
+ this.items[this.cells[i].id] = i;
+ }
+ this._itemsDirty = false;
+ },
+
+ /**
+ * Clears the current column
+ */
+ clear() {
+ this.cells = [];
+ this.items = {};
+ this._itemsDirty = false;
+ while (this.header.nextSibling) {
+ this.header.nextSibling.remove();
+ }
+ },
+
+ /**
+ * Sorts the given items and returns the sorted list if the table was sorted
+ * by this column.
+ */
+ sort(items) {
+ // Only sort the array if we are sorting based on this column
+ if (this.sorted == 1) {
+ items.sort((a, b) => {
+ const val1 = Node.isInstance(a[this.id])
+ ? a[this.id].textContent
+ : a[this.id];
+ const val2 = Node.isInstance(b[this.id])
+ ? b[this.id].textContent
+ : b[this.id];
+ return naturalSortCaseInsensitive(val1, val2, standardSessionString);
+ });
+ } else if (this.sorted > 1) {
+ items.sort((a, b) => {
+ const val1 = Node.isInstance(a[this.id])
+ ? a[this.id].textContent
+ : a[this.id];
+ const val2 = Node.isInstance(b[this.id])
+ ? b[this.id].textContent
+ : b[this.id];
+ return naturalSortCaseInsensitive(val2, val1, standardSessionString);
+ });
+ }
+
+ if (this.selectedRow) {
+ this.cells[this.items[this.selectedRow]].classList.remove(
+ "theme-selected"
+ );
+ }
+ this.items = {};
+ // Otherwise, just use the sorted array passed to update the cells value.
+ for (const [i, item] of items.entries()) {
+ // See Bug 1706679 (Intermittent)
+ // Sometimes we would reach the situation in which we were trying to sort
+ // and item that was no longer available in the TableWidget.
+ // We should find exactly what is triggering it.
+ if (!this.cells[i]) {
+ continue;
+ }
+ this.items[item[this.uniqueId]] = i;
+ this.cells[i].value = item[this.id];
+ this.cells[i].id = item[this.uniqueId];
+ }
+ if (this.selectedRow) {
+ this.cells[this.items[this.selectedRow]].classList.add("theme-selected");
+ }
+ this._itemsDirty = false;
+ this.updateZebra();
+ return items;
+ },
+
+ updateZebra() {
+ this._updateItems();
+ let i = 0;
+ for (const cell of this.cells) {
+ if (!cell.hidden) {
+ i++;
+ }
+
+ const even = !(i % 2);
+ cell.classList.toggle("even", even);
+ }
+ },
+
+ /**
+ * Click event handler for the column. Used to detect click on header for
+ * for sorting.
+ */
+ onClick(event) {
+ const target = event.originalTarget;
+
+ if (target.nodeType !== target.ELEMENT_NODE || target == this.column) {
+ return;
+ }
+
+ if (event.button == 0 && target == this.header) {
+ this.table.sortBy(this.id);
+ }
+ },
+
+ /**
+ * Mousedown event handler for the column. Used to select rows.
+ */
+ onMousedown(event) {
+ const target = event.originalTarget;
+
+ if (
+ target.nodeType !== target.ELEMENT_NODE ||
+ target == this.column ||
+ target == this.header
+ ) {
+ return;
+ }
+ if (event.button == 0) {
+ const closest = target.closest("[data-id]");
+ if (!closest) {
+ return;
+ }
+
+ const dataid = closest.getAttribute("data-id");
+ this.table.emit(EVENTS.ROW_SELECTED, dataid);
+ }
+ },
+};
+
+/**
+ * A single cell in a column
+ *
+ * @param {Column} column
+ * The column object to which the cell belongs.
+ * @param {object} item
+ * The object representing the row. It contains a key value pair
+ * representing the column id and its associated value. The value
+ * can be a DOMNode that is appended or a string value.
+ * @param {Cell} nextCell
+ * The cell object which is next to this cell. null if this cell is last
+ * cell of the column
+ */
+function Cell(column, item, nextCell) {
+ const document = column.document;
+
+ this.wrapTextInElements = column.wrapTextInElements;
+ this.label = document.createXULElement("label");
+ this.label.setAttribute("crop", "end");
+ this.label.className = "plain table-widget-cell";
+
+ if (nextCell) {
+ column.column.insertBefore(this.label, nextCell.label);
+ } else {
+ column.column.appendChild(this.label);
+ }
+
+ if (column.table.cellContextMenuId) {
+ this.label.setAttribute("context", column.table.cellContextMenuId);
+ this.label.addEventListener("contextmenu", event => {
+ // Make the ID of the clicked cell available as a property on the table.
+ // It's then available for the popupshowing or command handler.
+ column.table.contextMenuRowId = this.id;
+ });
+ }
+
+ this.value = item[column.id];
+ this.id = item[column.uniqueId];
+}
+
+Cell.prototype = {
+ set id(value) {
+ this._id = value;
+ this.label.setAttribute("data-id", value);
+ },
+
+ get id() {
+ return this._id;
+ },
+
+ get hidden() {
+ return this.label.hidden;
+ },
+
+ set hidden(value) {
+ this.label.hidden = value;
+ },
+
+ set value(value) {
+ this._value = value;
+ if (value == null) {
+ this.label.setAttribute("value", "");
+ return;
+ }
+
+ if (this.wrapTextInElements && !Node.isInstance(value)) {
+ const span = this.label.ownerDocument.createElementNS(HTML_NS, "span");
+ span.textContent = value;
+ value = span;
+ }
+
+ if (Node.isInstance(value)) {
+ this.label.removeAttribute("value");
+
+ while (this.label.firstChild) {
+ this.label.firstChild.remove();
+ }
+
+ this.label.appendChild(value);
+ } else {
+ this.label.setAttribute("value", value + "");
+ }
+ },
+
+ get value() {
+ return this._value;
+ },
+
+ get classList() {
+ return this.label.classList;
+ },
+
+ /**
+ * Flashes the cell for a brief time. This when done for with cells in all
+ * columns, makes it look like the row is being highlighted/flashed.
+ */
+ flash() {
+ if (!this.label.parentNode) {
+ return;
+ }
+ this.label.classList.remove("flash-out");
+ // Cause a reflow so that the animation retriggers on adding back the class
+ let a = this.label.parentNode.offsetWidth; // eslint-disable-line
+ const onAnimEnd = () => {
+ this.label.classList.remove("flash-out");
+ this.label.removeEventListener("animationend", onAnimEnd);
+ };
+ this.label.addEventListener("animationend", onAnimEnd);
+ this.label.classList.add("flash-out");
+ },
+
+ focus() {
+ this.label.focus();
+ },
+
+ scrollIntoView() {
+ this.label.scrollIntoView(false);
+ },
+
+ destroy() {
+ this.label.remove();
+ this.label = null;
+ },
+};
+
+/**
+ * Simple widget to make nodes matching a CSS selector editable.
+ *
+ * @param {Object} options
+ * An object with the following format:
+ * {
+ * // The node that will act as a container for the editor e.g. a
+ * // div or table.
+ * root: someNode,
+ *
+ * // The onTab event to be handled by the caller.
+ * onTab: function(event) { ... }
+ *
+ * // Optional event used to trigger the editor. By default this is
+ * // dblclick.
+ * onTriggerEvent: "dblclick",
+ *
+ * // Array or comma separated string of CSS Selectors matching
+ * // elements that are to be made editable.
+ * selectors: [
+ * "#name .table-widget-cell",
+ * "#value .table-widget-cell"
+ * ]
+ * }
+ */
+function EditableFieldsEngine(options) {
+ EventEmitter.decorate(this);
+
+ if (!Array.isArray(options.selectors)) {
+ options.selectors = [options.selectors];
+ }
+
+ this.root = options.root;
+ this.selectors = options.selectors;
+ this.onTab = options.onTab;
+ this.onTriggerEvent = options.onTriggerEvent || "dblclick";
+ this.items = options.items;
+
+ this.edit = this.edit.bind(this);
+ this.cancelEdit = this.cancelEdit.bind(this);
+ this.destroy = this.destroy.bind(this);
+
+ this.onTrigger = this.onTrigger.bind(this);
+ this.root.addEventListener(this.onTriggerEvent, this.onTrigger);
+}
+
+EditableFieldsEngine.prototype = {
+ INPUT_ID: "inlineEditor",
+
+ get changePending() {
+ return this.isEditing && this.textbox.value !== this.currentValue;
+ },
+
+ get isEditing() {
+ return this.root && !this.textbox.hidden;
+ },
+
+ get textbox() {
+ if (!this._textbox) {
+ const doc = this.root.ownerDocument;
+ this._textbox = doc.createElementNS(HTML_NS, "input");
+ this._textbox.id = this.INPUT_ID;
+
+ this.onKeydown = this.onKeydown.bind(this);
+ this._textbox.addEventListener("keydown", this.onKeydown);
+
+ this.completeEdit = this.completeEdit.bind(this);
+ doc.addEventListener("blur", this.completeEdit);
+ }
+
+ return this._textbox;
+ },
+
+ /**
+ * Called when a trigger event is detected (default is dblclick).
+ *
+ * @param {EventTarget} target
+ * Calling event's target.
+ */
+ onTrigger({ target }) {
+ this.edit(target);
+ },
+
+ /**
+ * Handle keydowns when in edit mode:
+ * - <escape> revert the value and close the textbox.
+ * - <return> apply the value and close the textbox.
+ * - <tab> Handled by the consumer's `onTab` callback.
+ * - <shift><tab> Handled by the consumer's `onTab` callback.
+ *
+ * @param {Event} event
+ * The calling event.
+ */
+ onKeydown(event) {
+ if (!this.textbox) {
+ return;
+ }
+
+ switch (event.keyCode) {
+ case KeyCodes.DOM_VK_ESCAPE:
+ this.cancelEdit();
+ event.preventDefault();
+ break;
+ case KeyCodes.DOM_VK_RETURN:
+ this.completeEdit();
+ break;
+ case KeyCodes.DOM_VK_TAB:
+ if (this.onTab) {
+ this.onTab(event);
+ }
+ break;
+ }
+ },
+
+ /**
+ * Overlay the target node with an edit field.
+ *
+ * @param {Node} target
+ * Dom node to be edited.
+ */
+ edit(target) {
+ if (!target) {
+ return;
+ }
+
+ // Some item names and values are not parsable by the client or server so should not be
+ // editable.
+ const name = target.getAttribute("data-id");
+ const item = this.items.get(name);
+ if ("isValueEditable" in item && !item.isValueEditable) {
+ return;
+ }
+
+ target.scrollIntoView(false);
+ target.focus();
+
+ if (!target.matches(this.selectors.join(","))) {
+ return;
+ }
+
+ // If we are actively editing something complete the edit first.
+ if (this.isEditing) {
+ this.completeEdit();
+ }
+
+ this.copyStyles(target, this.textbox);
+
+ target.parentNode.insertBefore(this.textbox, target);
+ this.currentTarget = target;
+ this.textbox.value = this.currentValue = target.value;
+ target.hidden = true;
+ this.textbox.hidden = false;
+
+ this.textbox.focus();
+ this.textbox.select();
+ },
+
+ completeEdit() {
+ if (!this.isEditing) {
+ return;
+ }
+
+ const oldValue = this.currentValue;
+ const newValue = this.textbox.value;
+ const changed = oldValue !== newValue;
+
+ this.textbox.hidden = true;
+
+ if (!this.currentTarget) {
+ return;
+ }
+
+ this.currentTarget.hidden = false;
+ if (changed) {
+ this.currentTarget.value = newValue;
+
+ const data = {
+ change: {
+ field: this.currentTarget,
+ oldValue,
+ newValue,
+ },
+ };
+
+ this.emit("change", data);
+ }
+ },
+
+ /**
+ * Cancel an edit.
+ */
+ cancelEdit() {
+ if (!this.isEditing) {
+ return;
+ }
+ if (this.currentTarget) {
+ this.currentTarget.hidden = false;
+ }
+
+ this.textbox.hidden = true;
+ },
+
+ /**
+ * Stop edit mode and apply changes.
+ */
+ blur() {
+ if (this.isEditing) {
+ this.completeEdit();
+ }
+ },
+
+ /**
+ * Copies various styles from one node to another.
+ *
+ * @param {Node} source
+ * The node to copy styles from.
+ * @param {Node} destination [description]
+ * The node to copy styles to.
+ */
+ copyStyles(source, destination) {
+ const style = source.ownerDocument.defaultView.getComputedStyle(source);
+ const props = [
+ "borderTopWidth",
+ "borderRightWidth",
+ "borderBottomWidth",
+ "borderLeftWidth",
+ "fontFamily",
+ "fontSize",
+ "fontWeight",
+ "height",
+ "marginTop",
+ "marginRight",
+ "marginBottom",
+ "marginLeft",
+ "marginInlineStart",
+ "marginInlineEnd",
+ ];
+
+ for (const prop of props) {
+ destination.style[prop] = style[prop];
+ }
+
+ // We need to set the label width to 100% to work around a XUL flex bug.
+ destination.style.width = "100%";
+ },
+
+ /**
+ * Destroys all editors in the current document.
+ */
+ destroy() {
+ if (this.textbox) {
+ this.textbox.removeEventListener("keydown", this.onKeydown);
+ this.textbox.remove();
+ }
+
+ if (this.root) {
+ this.root.removeEventListener(this.onTriggerEvent, this.onTrigger);
+ this.root.ownerDocument.removeEventListener("blur", this.completeEdit);
+ }
+
+ this._textbox = this.root = this.selectors = this.onTab = null;
+ this.currentTarget = this.currentValue = null;
+
+ this.emit("destroyed");
+ },
+};
diff --git a/devtools/client/shared/widgets/TreeWidget.js b/devtools/client/shared/widgets/TreeWidget.js
new file mode 100644
index 0000000000..1a54061210
--- /dev/null
+++ b/devtools/client/shared/widgets/TreeWidget.js
@@ -0,0 +1,643 @@
+/* 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/. */
+"use strict";
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
+
+/**
+ * A tree widget with keyboard navigation and collapsable structure.
+ *
+ * @param {Node} node
+ * The container element for the tree widget.
+ * @param {Object} options
+ * - emptyText {string}: text to display when no entries in the table.
+ * - defaultType {string}: The default type of the tree items. For ex.
+ * 'js'
+ * - sorted {boolean}: Defaults to true. If true, tree items are kept in
+ * lexical order. If false, items will be kept in insertion order.
+ * - contextMenuId {string}: ID of context menu to be displayed on
+ * tree items.
+ */
+function TreeWidget(node, options = {}) {
+ EventEmitter.decorate(this);
+
+ this.document = node.ownerDocument;
+ this.window = this.document.defaultView;
+ this._parent = node;
+
+ this.emptyText = options.emptyText || "";
+ this.defaultType = options.defaultType;
+ this.sorted = options.sorted !== false;
+ this.contextMenuId = options.contextMenuId;
+
+ this.setupRoot();
+
+ this.placeholder = this.document.createElementNS(HTML_NS, "label");
+ this.placeholder.className = "tree-widget-empty-text";
+ this._parent.appendChild(this.placeholder);
+
+ if (this.emptyText) {
+ this.setPlaceholderText(this.emptyText);
+ }
+ // A map to hold all the passed attachment to each leaf in the tree.
+ this.attachments = new Map();
+}
+
+TreeWidget.prototype = {
+ _selectedLabel: null,
+ _selectedItem: null,
+
+ /**
+ * Select any node in the tree.
+ *
+ * @param {array} ids
+ * An array of ids leading upto the selected item
+ */
+ set selectedItem(ids) {
+ if (this._selectedLabel) {
+ this._selectedLabel.classList.remove("theme-selected");
+ }
+ const currentSelected = this._selectedLabel;
+ if (ids == -1) {
+ this._selectedLabel = this._selectedItem = null;
+ return;
+ }
+ if (!Array.isArray(ids)) {
+ return;
+ }
+ this._selectedLabel = this.root.setSelectedItem(ids);
+ if (!this._selectedLabel) {
+ this._selectedItem = null;
+ } else {
+ if (currentSelected != this._selectedLabel) {
+ this.ensureSelectedVisible();
+ }
+ this._selectedItem = ids;
+ this.emit(
+ "select",
+ this._selectedItem,
+ this.attachments.get(JSON.stringify(ids))
+ );
+ }
+ },
+
+ /**
+ * Gets the selected item in the tree.
+ *
+ * @return {array}
+ * An array of ids leading upto the selected item
+ */
+ get selectedItem() {
+ return this._selectedItem;
+ },
+
+ /**
+ * Returns if the passed array corresponds to the selected item in the tree.
+ *
+ * @return {array}
+ * An array of ids leading upto the requested item
+ */
+ isSelected(item) {
+ if (!this._selectedItem || this._selectedItem.length != item.length) {
+ return false;
+ }
+
+ for (let i = 0; i < this._selectedItem.length; i++) {
+ if (this._selectedItem[i] != item[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ destroy() {
+ this.root.remove();
+ this.root = null;
+ },
+
+ /**
+ * Sets up the root container of the TreeWidget.
+ */
+ setupRoot() {
+ this.root = new TreeItem(this.document);
+ if (this.contextMenuId) {
+ this.root.children.addEventListener("contextmenu", event => {
+ // Call stopPropagation() and preventDefault() here so that avoid to show default
+ // context menu in about:devtools-toolbox. See Bug 1515265.
+ event.stopPropagation();
+ event.preventDefault();
+ const menu = this.document.getElementById(this.contextMenuId);
+ menu.openPopupAtScreen(event.screenX, event.screenY, true);
+ });
+ }
+
+ this._parent.appendChild(this.root.children);
+
+ this.root.children.addEventListener("mousedown", e => this.onClick(e));
+ this.root.children.addEventListener("keydown", e => this.onKeydown(e));
+ },
+
+ /**
+ * Sets the text to be shown when no node is present in the tree.
+ * The placeholder will be hidden if text is empty.
+ */
+ setPlaceholderText(text) {
+ this.placeholder.textContent = text;
+ if (text) {
+ this.placeholder.removeAttribute("hidden");
+ } else {
+ this.placeholder.setAttribute("hidden", "true");
+ }
+ },
+
+ /**
+ * Select any node in the tree.
+ *
+ * @param {array} id
+ * An array of ids leading upto the selected item
+ */
+ selectItem(id) {
+ this.selectedItem = id;
+ },
+
+ /**
+ * Selects the next visible item in the tree.
+ */
+ selectNextItem() {
+ const next = this.getNextVisibleItem();
+ if (next) {
+ this.selectedItem = next;
+ }
+ },
+
+ /**
+ * Selects the previos visible item in the tree
+ */
+ selectPreviousItem() {
+ const prev = this.getPreviousVisibleItem();
+ if (prev) {
+ this.selectedItem = prev;
+ }
+ },
+
+ /**
+ * Returns the next visible item in the tree
+ */
+ getNextVisibleItem() {
+ let node = this._selectedLabel;
+ if (node.hasAttribute("expanded") && node.nextSibling.firstChild) {
+ return JSON.parse(node.nextSibling.firstChild.getAttribute("data-id"));
+ }
+ node = node.parentNode;
+ if (node.nextSibling) {
+ return JSON.parse(node.nextSibling.getAttribute("data-id"));
+ }
+ node = node.parentNode;
+ while (node.parentNode && node != this.root.children) {
+ if (node.parentNode?.nextSibling) {
+ return JSON.parse(node.parentNode.nextSibling.getAttribute("data-id"));
+ }
+ node = node.parentNode;
+ }
+ return null;
+ },
+
+ /**
+ * Returns the previous visible item in the tree
+ */
+ getPreviousVisibleItem() {
+ let node = this._selectedLabel.parentNode;
+ if (node.previousSibling) {
+ node = node.previousSibling.firstChild;
+ while (node.hasAttribute("expanded") && !node.hasAttribute("empty")) {
+ if (!node.nextSibling.lastChild) {
+ break;
+ }
+ node = node.nextSibling.lastChild.firstChild;
+ }
+ return JSON.parse(node.parentNode.getAttribute("data-id"));
+ }
+ node = node.parentNode;
+ if (node.parentNode && node != this.root.children) {
+ node = node.parentNode;
+ while (node.hasAttribute("expanded") && !node.hasAttribute("empty")) {
+ if (!node.nextSibling.firstChild) {
+ break;
+ }
+ node = node.nextSibling.firstChild.firstChild;
+ }
+ return JSON.parse(node.getAttribute("data-id"));
+ }
+ return null;
+ },
+
+ clearSelection() {
+ this.selectedItem = -1;
+ },
+
+ /**
+ * Adds an item in the tree. The item can be added as a child to any node in
+ * the tree. The method will also create any subnode not present in the
+ * process.
+ *
+ * @param {[string|object]} items
+ * An array of either string or objects where each increasing index
+ * represents an item corresponding to an equivalent depth in the tree.
+ * Each array element can be either just a string with the value as the
+ * id of of that item as well as the display value, or it can be an
+ * object with the following propeties:
+ * - id {string} The id of the item
+ * - label {string} The display value of the item
+ * - node {DOMNode} The dom node if you want to insert some custom
+ * element as the item. The label property is not used in this
+ * case
+ * - attachment {object} Any object to be associated with this item.
+ * - type {string} The type of this particular item. If this is null,
+ * then defaultType will be used.
+ * For example, if items = ["foo", "bar", { id: "id1", label: "baz" }]
+ * and the tree is empty, then the following hierarchy will be created
+ * in the tree:
+ * foo
+ * └ bar
+ * └ baz
+ * Passing the string id instead of the complete object helps when you
+ * are simply adding children to an already existing node and you know
+ * its id.
+ */
+ add(items) {
+ this.root.add(items, this.defaultType, this.sorted);
+ for (let i = 0; i < items.length; i++) {
+ if (items[i].attachment) {
+ this.attachments.set(
+ JSON.stringify(items.slice(0, i + 1).map(item => item.id || item)),
+ items[i].attachment
+ );
+ }
+ }
+ // Empty the empty-tree-text
+ this.setPlaceholderText("");
+ },
+
+ /**
+ * Check if an item exists.
+ *
+ * @param {array} item
+ * The array of ids leading up to the item.
+ */
+ exists(item) {
+ let bookmark = this.root;
+
+ for (const id of item) {
+ if (bookmark.items.has(id)) {
+ bookmark = bookmark.items.get(id);
+ } else {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ /**
+ * Removes the specified item and all of its child items from the tree.
+ *
+ * @param {array} item
+ * The array of ids leading up to the item.
+ */
+ remove(item) {
+ this.root.remove(item);
+ this.attachments.delete(JSON.stringify(item));
+ // Display the empty tree text
+ if (this.root.items.size == 0 && this.emptyText) {
+ this.setPlaceholderText(this.emptyText);
+ }
+ },
+
+ /**
+ * Removes all of the child nodes from this tree.
+ */
+ clear() {
+ this.root.remove();
+ this.setupRoot();
+ this.attachments.clear();
+ if (this.emptyText) {
+ this.setPlaceholderText(this.emptyText);
+ }
+ },
+
+ /**
+ * Expands the tree completely
+ */
+ expandAll() {
+ this.root.expandAll();
+ },
+
+ /**
+ * Collapses the tree completely
+ */
+ collapseAll() {
+ this.root.collapseAll();
+ },
+
+ /**
+ * Click handler for the tree. Used to select, open and close the tree nodes.
+ */
+ onClick(event) {
+ let target = event.originalTarget;
+ while (target && !target.classList.contains("tree-widget-item")) {
+ if (target == this.root.children) {
+ return;
+ }
+ target = target.parentNode;
+ }
+ if (!target) {
+ return;
+ }
+
+ if (target.hasAttribute("expanded")) {
+ target.removeAttribute("expanded");
+ } else {
+ target.setAttribute("expanded", "true");
+ }
+
+ if (this._selectedLabel != target) {
+ const ids = target.parentNode.getAttribute("data-id");
+ this.selectedItem = JSON.parse(ids);
+ }
+ },
+
+ /**
+ * Keydown handler for this tree. Used to select next and previous visible
+ * items, as well as collapsing and expanding any item.
+ */
+ onKeydown(event) {
+ switch (event.keyCode) {
+ case KeyCodes.DOM_VK_UP:
+ this.selectPreviousItem();
+ break;
+
+ case KeyCodes.DOM_VK_DOWN:
+ this.selectNextItem();
+ break;
+
+ case KeyCodes.DOM_VK_RIGHT:
+ if (this._selectedLabel.hasAttribute("expanded")) {
+ this.selectNextItem();
+ } else {
+ this._selectedLabel.setAttribute("expanded", "true");
+ }
+ break;
+
+ case KeyCodes.DOM_VK_LEFT:
+ if (
+ this._selectedLabel.hasAttribute("expanded") &&
+ !this._selectedLabel.hasAttribute("empty")
+ ) {
+ this._selectedLabel.removeAttribute("expanded");
+ } else {
+ this.selectPreviousItem();
+ }
+ break;
+
+ default:
+ return;
+ }
+ event.preventDefault();
+ },
+
+ /**
+ * Scrolls the viewport of the tree so that the selected item is always
+ * visible.
+ */
+ ensureSelectedVisible() {
+ const { top, bottom } = this._selectedLabel.getBoundingClientRect();
+ const height = this.root.children.parentNode.clientHeight;
+ if (top < 0) {
+ this._selectedLabel.scrollIntoView();
+ } else if (bottom > height) {
+ this._selectedLabel.scrollIntoView(false);
+ }
+ },
+};
+
+module.exports.TreeWidget = TreeWidget;
+
+/**
+ * Any item in the tree. This can be an empty leaf node also.
+ *
+ * @param {HTMLDocument} document
+ * The document element used for creating new nodes.
+ * @param {TreeItem} parent
+ * The parent item for this item.
+ * @param {string|DOMElement} label
+ * Either the dom node to be used as the item, or the string to be
+ * displayed for this node in the tree
+ * @param {string} type
+ * The type of the current node. For ex. "js"
+ */
+function TreeItem(document, parent, label, type) {
+ this.document = document;
+ this.node = this.document.createElementNS(HTML_NS, "li");
+ this.node.setAttribute("tabindex", "0");
+ this.isRoot = !parent;
+ this.parent = parent;
+ if (this.parent) {
+ this.level = this.parent.level + 1;
+ }
+ if (label) {
+ this.label = this.document.createElementNS(HTML_NS, "div");
+ this.label.setAttribute("empty", "true");
+ this.label.setAttribute("level", this.level);
+ this.label.className = "tree-widget-item";
+ if (type) {
+ this.label.setAttribute("type", type);
+ }
+ if (typeof label == "string") {
+ this.label.textContent = label;
+ } else {
+ this.label.appendChild(label);
+ }
+ this.node.appendChild(this.label);
+ }
+ this.children = this.document.createElementNS(HTML_NS, "ul");
+ if (this.isRoot) {
+ this.children.className = "tree-widget-container";
+ } else {
+ this.children.className = "tree-widget-children";
+ }
+ this.node.appendChild(this.children);
+ this.items = new Map();
+}
+
+TreeItem.prototype = {
+ items: null,
+
+ isSelected: false,
+
+ expanded: false,
+
+ isRoot: false,
+
+ parent: null,
+
+ children: null,
+
+ level: 0,
+
+ /**
+ * Adds the item to the sub tree contained by this node. The item to be
+ * inserted can be a direct child of this node, or further down the tree.
+ *
+ * @param {array} items
+ * Same as TreeWidget.add method's argument
+ * @param {string} defaultType
+ * The default type of the item to be used when items[i].type is null
+ * @param {boolean} sorted
+ * true if the tree items are inserted in a lexically sorted manner.
+ * Otherwise, false if the item are to be appended to their parent.
+ */
+ add(items, defaultType, sorted) {
+ if (items.length == this.level) {
+ // This is the exit condition of recursive TreeItem.add calls
+ return;
+ }
+ // Get the id and label corresponding to this level inside the tree.
+ const id = items[this.level].id || items[this.level];
+ if (this.items.has(id)) {
+ // An item with same id already exists, thus calling the add method of
+ // that child to add the passed node at correct position.
+ this.items.get(id).add(items, defaultType, sorted);
+ return;
+ }
+ // No item with the id `id` exists, so we create one and call the add
+ // method of that item.
+ // The display string of the item can be the label, the id, or the item
+ // itself if its a plain string.
+ let label =
+ items[this.level].label || items[this.level].id || items[this.level];
+ const node = items[this.level].node;
+ if (node) {
+ // The item is supposed to be a DOMNode, so we fetch the textContent in
+ // order to find the correct sorted location of this new item.
+ label = node.textContent;
+ }
+ const treeItem = new TreeItem(
+ this.document,
+ this,
+ node || label,
+ items[this.level].type || defaultType
+ );
+
+ treeItem.add(items, defaultType, sorted);
+ treeItem.node.setAttribute(
+ "data-id",
+ JSON.stringify(
+ items.slice(0, this.level + 1).map(item => item.id || item)
+ )
+ );
+
+ if (sorted) {
+ // Inserting this newly created item at correct position
+ const nextSibling = [...this.items.values()].find(child => {
+ return child.label.textContent >= label;
+ });
+
+ if (nextSibling) {
+ this.children.insertBefore(treeItem.node, nextSibling.node);
+ } else {
+ this.children.appendChild(treeItem.node);
+ }
+ } else {
+ this.children.appendChild(treeItem.node);
+ }
+
+ if (this.label) {
+ this.label.removeAttribute("empty");
+ }
+ this.items.set(id, treeItem);
+ },
+
+ /**
+ * If this item is to be removed, then removes this item and thus all of its
+ * subtree. Otherwise, call the remove method of appropriate child. This
+ * recursive method goes on till we have reached the end of the branch or the
+ * current item is to be removed.
+ *
+ * @param {array} items
+ * Ids of items leading up to the item to be removed.
+ */
+ remove(items = []) {
+ const id = items.shift();
+ if (id && this.items.has(id)) {
+ const deleted = this.items.get(id);
+ if (!items.length) {
+ this.items.delete(id);
+ }
+ if (this.items.size == 0) {
+ this.label.setAttribute("empty", "true");
+ }
+ deleted.remove(items);
+ } else if (!id) {
+ this.destroy();
+ }
+ },
+
+ /**
+ * If this item is to be selected, then selected and expands the item.
+ * Otherwise, if a child item is to be selected, just expands this item.
+ *
+ * @param {array} items
+ * Ids of items leading up to the item to be selected.
+ */
+ setSelectedItem(items) {
+ if (!items[this.level]) {
+ this.label.classList.add("theme-selected");
+ this.label.setAttribute("expanded", "true");
+ return this.label;
+ }
+ if (this.items.has(items[this.level])) {
+ const label = this.items.get(items[this.level]).setSelectedItem(items);
+ if (label && this.label) {
+ this.label.setAttribute("expanded", true);
+ }
+ return label;
+ }
+ return null;
+ },
+
+ /**
+ * Collapses this item and all of its sub tree items
+ */
+ collapseAll() {
+ if (this.label) {
+ this.label.removeAttribute("expanded");
+ }
+ for (const child of this.items.values()) {
+ child.collapseAll();
+ }
+ },
+
+ /**
+ * Expands this item and all of its sub tree items
+ */
+ expandAll() {
+ if (this.label) {
+ this.label.setAttribute("expanded", "true");
+ }
+ for (const child of this.items.values()) {
+ child.expandAll();
+ }
+ },
+
+ destroy() {
+ this.children.remove();
+ this.node.remove();
+ this.label = null;
+ this.items = null;
+ this.children = null;
+ },
+};
diff --git a/devtools/client/shared/widgets/cubic-bezier.css b/devtools/client/shared/widgets/cubic-bezier.css
new file mode 100644
index 0000000000..4a73cb75ef
--- /dev/null
+++ b/devtools/client/shared/widgets/cubic-bezier.css
@@ -0,0 +1,216 @@
+/* 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/. */
+
+/* Based on Lea Verou www.cubic-bezier.com
+ See https://github.com/LeaVerou/cubic-bezier */
+
+.cubic-bezier-container {
+ display: flex;
+ width: 510px;
+ height: 370px;
+ flex-direction: row-reverse;
+ overflow: hidden;
+ padding: 5px;
+ box-sizing: border-box;
+}
+
+.cubic-bezier-container .display-wrap {
+ width: 50%;
+ height: 100%;
+ text-align: center;
+ overflow: hidden;
+}
+
+/* Coordinate Plane */
+
+.cubic-bezier-container .coordinate-plane {
+ width: 150px;
+ height: 370px;
+ margin: 0 auto;
+ position: relative;
+}
+
+.cubic-bezier-container .control-point {
+ position: absolute;
+ z-index: 1;
+ height: 10px;
+ width: 10px;
+ border: 0;
+ background: #666;
+ display: block;
+ margin: -5px 0 0 -5px;
+ outline: none;
+ border-radius: 5px;
+ padding: 0;
+ cursor: pointer;
+}
+
+.cubic-bezier-container .display-wrap {
+ background:
+ repeating-linear-gradient(0deg,
+ transparent,
+ var(--bezier-grid-color) 0,
+ var(--bezier-grid-color) 1px,
+ transparent 1px,
+ transparent 15px) no-repeat,
+ repeating-linear-gradient(90deg,
+ transparent,
+ var(--bezier-grid-color) 0,
+ var(--bezier-grid-color) 1px,
+ transparent 1px,
+ transparent 15px) no-repeat;
+ background-size: 100% 100%, 100% 100%;
+ background-position: -2px 5px, -2px 5px;
+ user-select: none;
+}
+
+.cubic-bezier-container canvas.curve {
+ background:
+ linear-gradient(-45deg,
+ transparent 49.7%,
+ var(--bezier-diagonal-color) 49.7%,
+ var(--bezier-diagonal-color) 50.3%,
+ transparent 50.3%) center no-repeat;
+ background-size: 100% 100%;
+ background-position: 0 0;
+}
+
+/* Timing Function Preview Widget */
+
+.cubic-bezier-container .timing-function-preview {
+ position: absolute;
+ bottom: 20px;
+ right: 45px;
+ width: 150px;
+}
+
+.cubic-bezier-container .timing-function-preview .scale {
+ position: absolute;
+ top: 6px;
+ left: 0;
+ z-index: 1;
+
+ width: 150px;
+ height: 1px;
+
+ background: #ccc;
+}
+
+.cubic-bezier-container .timing-function-preview .dot {
+ position: absolute;
+ top: 0;
+ left: -7px;
+ z-index: 2;
+
+ width: 10px;
+ height: 10px;
+
+ border-radius: 50%;
+ border: 2px solid white;
+ background: #4C9ED9;
+}
+
+/* Preset Widget */
+
+.cubic-bezier-container .preset-pane {
+ width: 50%;
+ height: 100%;
+ border-right: 1px solid var(--theme-splitter-color);
+ padding-right: 4px; /* Visual balance for the panel-arrowcontent border on the left */
+}
+
+#preset-categories {
+ display: flex;
+ width: 95%;
+ border: 1px solid var(--theme-splitter-color);
+ border-radius: 2px;
+ background-color: var(--theme-toolbar-background);
+ margin: 3px auto 0 auto;
+}
+
+#preset-categories .category:last-child {
+ border-right: none;
+}
+
+.cubic-bezier-container .category {
+ padding: 5px 0px;
+ width: 33.33%;
+ text-align: center;
+ text-transform: capitalize;
+ border-right: 1px solid var(--theme-splitter-color);
+ cursor: default;
+ color: var(--theme-body-color);
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.cubic-bezier-container .category:hover {
+ background-color: var(--theme-tab-toolbar-background);
+}
+
+.cubic-bezier-container .active-category {
+ background-color: var(--theme-selection-background);
+ color: var(--theme-selection-color);
+}
+
+.cubic-bezier-container .active-category:hover {
+ background-color: var(--theme-selection-background);
+}
+
+#preset-container {
+ padding: 0px;
+ width: 100%;
+ height: 331px;
+ overflow-y: auto;
+}
+
+.cubic-bezier-container .preset-list {
+ display: none;
+ padding-top: 6px;
+}
+
+.cubic-bezier-container .active-preset-list {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+}
+
+.cubic-bezier-container .preset {
+ cursor: pointer;
+ width: 33.33%;
+ margin: 5px 0px;
+ text-align: center;
+}
+
+.cubic-bezier-container .preset canvas {
+ display: block;
+ border: 1px solid var(--theme-splitter-color);
+ border-radius: 3px;
+ background-color: var(--theme-body-background);
+ margin: 0 auto;
+}
+
+.cubic-bezier-container .preset p {
+ font-size: 80%;
+ margin: 2px auto 0px auto;
+ color: var(--theme-text-color-alt);
+ text-transform: capitalize;
+ text-overflow: ellipsis;
+ overflow: hidden;
+}
+
+.cubic-bezier-container .active-preset p,
+.cubic-bezier-container .active-preset:hover p {
+ color: var(--theme-body-color);
+}
+
+.cubic-bezier-container .preset:hover canvas {
+ border-color: var(--theme-selection-background);
+}
+
+.cubic-bezier-container .active-preset canvas,
+.cubic-bezier-container .active-preset:hover canvas {
+ background-color: var(--theme-selection-background-hover);
+ border-color: var(--theme-selection-background);
+}
diff --git a/devtools/client/shared/widgets/filter-widget.css b/devtools/client/shared/widgets/filter-widget.css
new file mode 100644
index 0000000000..aeee4db42e
--- /dev/null
+++ b/devtools/client/shared/widgets/filter-widget.css
@@ -0,0 +1,242 @@
+/* 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/. */
+
+/* Main container: Displays the filters and presets in 2 columns */
+
+#filter-container {
+ width: 510px;
+ height: 200px;
+ display: flex;
+ position: relative;
+ padding: 5px;
+ box-sizing: border-box;
+ /* when opened in a xul:panel, a gray color is applied to text */
+ color: var(--theme-body-color);
+}
+
+#filter-container.dragging {
+ user-select: none;
+}
+
+#filter-container .filters-list,
+#filter-container .presets-list {
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+}
+
+#filter-container .filters-list {
+ /* Allow the filters list to take the full width when the presets list is
+ hidden */
+ flex-grow: 1;
+ padding: 0 6px;
+}
+
+#filter-container .presets-list {
+ /* Make sure that when the presets list is shown, it has a fixed width */
+ width: 200px;
+ padding-left: 6px;
+ transition: width .1s;
+ flex-shrink: 0;
+ border-left: 1px solid var(--theme-splitter-color);
+}
+
+#filter-container:not(.show-presets) .presets-list {
+ width: 0;
+ border-left: none;
+ padding-left: 0;
+ /* To hide also element's children, not on only the element */
+ overflow: hidden;
+}
+
+#filter-container.show-presets .filters-list {
+ width: 300px;
+}
+
+/* The list of filters and list of presets should push their footers to the
+ bottom, so they can take as much space as there is */
+
+#filter-container #filters,
+#filter-container #presets {
+ flex-grow: 1;
+ /* Avoid pushing below the tooltip's area */
+ overflow-y: auto;
+}
+
+/* The filters and presets list both have footers displayed at the bottom.
+ These footers have some input (taking up as much space as possible) and an
+ add button next */
+
+#filter-container .footer {
+ display: flex;
+ margin: 10px 3px;
+ align-items: center;
+}
+
+#filter-container .footer :not(button) {
+ flex-grow: 1;
+ margin-right: 3px;
+}
+
+/* Styles for 1 filter function item */
+
+#filter-container .filter,
+#filter-container .filter-name,
+#filter-container .filter-value {
+ display: flex;
+ align-items: center;
+}
+
+#filter-container .filter {
+ margin: 5px 0;
+}
+
+#filter-container .filter-name {
+ width: 120px;
+ margin-right: 10px;
+}
+
+#filter-container .filter-name label {
+ user-select: none;
+ flex-grow: 1;
+}
+
+#filter-container .filter-name label.devtools-draglabel {
+ cursor: ew-resize;
+}
+
+/* drag/drop handle */
+
+#filter-container .filter-name i {
+ width: 10px;
+ height: 10px;
+ margin-right: 10px;
+ cursor: grab;
+ background: linear-gradient(to bottom,
+ currentColor 0,
+ currentcolor 1px,
+ transparent 1px,
+ transparent 2px);
+ background-repeat: repeat-y;
+ background-size: auto 4px;
+ background-position: 0 1px;
+}
+
+#filter-container .filter-value {
+ min-width: 150px;
+ margin-right: 10px;
+ flex: 1;
+}
+
+#filter-container .filter-value input {
+ flex-grow: 1;
+}
+
+/* Fix the size of inputs */
+/* Especially needed on Linux where input are bigger */
+#filter-container input {
+ width: 8em;
+}
+
+#filter-container .preset {
+ display: flex;
+ margin-bottom: 10px;
+ cursor: pointer;
+ padding: 3px 5px;
+
+ flex-direction: row;
+ flex-wrap: wrap;
+}
+
+#filter-container .preset label,
+#filter-container .preset span {
+ display: flex;
+ align-items: center;
+}
+
+#filter-container .preset label {
+ flex: 1 0;
+ cursor: pointer;
+ color: var(--theme-body-color);
+}
+
+#filter-container .preset:hover {
+ background: var(--theme-selection-background);
+}
+
+#filter-container .preset:hover label,
+#filter-container .preset:hover span {
+ color: var(--theme-selection-color);
+}
+
+#filter-container .preset .remove-button {
+ order: 2;
+}
+
+#filter-container .preset span {
+ flex: 2 100%;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: block;
+ order: 3;
+ color: var(--theme-text-color-alt);
+}
+
+#filter-container .remove-button {
+ width: 16px;
+ height: 16px;
+ background: url(chrome://devtools/skin/images/close.svg);
+ background-size: cover;
+ font-size: 0;
+ border: none;
+ cursor: pointer;
+}
+
+#filter-container .hidden {
+ display: none !important;
+}
+
+#filter-container .dragging {
+ position: relative;
+ z-index: 10;
+ cursor: grab;
+}
+
+/* message shown when there's no filter specified */
+#filter-container p {
+ text-align: center;
+ line-height: 20px;
+}
+
+#filter-container .add,
+#toggle-presets {
+ background-size: cover;
+ border: none;
+ width: 16px;
+ height: 16px;
+ font-size: 0;
+ vertical-align: middle;
+ cursor: pointer;
+ margin: 0 5px;
+}
+
+#filter-container .add {
+ background: url(chrome://devtools/skin/images/add.svg);
+}
+
+#toggle-presets {
+ background: url(chrome://devtools/skin/images/pseudo-class.svg);
+}
+
+#filter-container .add,
+#filter-container .remove-button,
+#toggle-presets {
+ -moz-context-properties: fill;
+ fill: var(--theme-icon-color);
+}
+
+.show-presets #toggle-presets {
+ fill: var(--theme-icon-checked-color);
+}
diff --git a/devtools/client/shared/widgets/linear-widget.css b/devtools/client/shared/widgets/linear-widget.css
new file mode 100644
index 0000000000..af82a6d3ba
--- /dev/null
+++ b/devtools/client/shared/widgets/linear-widget.css
@@ -0,0 +1,61 @@
+/* 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/. */
+
+.linear-easing-function-container {
+ height: 100%;
+}
+
+.linear-easing-function-container .display-wrap {
+ --chart-size: 300px;
+ height: 100%;
+ display: grid;
+ grid-template-rows: var(--chart-size) 1fr;
+ justify-items: center;
+ align-items: center;
+ gap: 5px;
+}
+
+.linear-easing-function-container svg.chart {
+ aspect-ratio: 1 / 1;
+ max-height: 100%;
+}
+
+.linear-easing-function-container .chart-grid {
+ stroke: var(--bezier-grid-color);
+ pointer-events: none;
+}
+
+.linear-easing-function-container .chart-linear {
+ -moz-context-properties: stroke;
+ stroke: #4C9ED9;
+}
+
+.linear-easing-function-container .control-point {
+ -moz-context-properties: fill, stroke;
+ fill: var(--grey-55);
+ cursor: pointer;
+}
+
+:root.theme-dark .linear-easing-function-container .control-point {
+ fill: var(--grey-20);
+}
+
+/* Timing Function Preview Widget */
+
+.linear-easing-function-container .timing-function-preview {
+ width: var(--chart-size);
+ /* Draw a background line */
+ background: linear-gradient(0deg, transparent 45%,var(--bezier-grid-color) 45%, var(--bezier-grid-color) 55%,transparent 55%);
+}
+
+.linear-easing-function-container .timing-function-preview .dot::before {
+ content: "";
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ aspect-ratio: 1 / 1;
+ border-radius: 50%;
+ border: 2px solid white;
+ background: #4C9ED9;
+}
diff --git a/devtools/client/shared/widgets/moz.build b/devtools/client/shared/widgets/moz.build
new file mode 100644
index 0000000000..22fe366068
--- /dev/null
+++ b/devtools/client/shared/widgets/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DIRS += [
+ "tooltip",
+]
+
+DevToolsModules(
+ "Chart.js",
+ "CubicBezierPresets.js",
+ "CubicBezierWidget.js",
+ "FilterWidget.js",
+ "LinearEasingFunctionWidget.js",
+ "ShapesInContextEditor.js",
+ "Spectrum.js",
+ "TableWidget.js",
+ "TreeWidget.js",
+ "view-helpers.js",
+)
diff --git a/devtools/client/shared/widgets/spectrum.css b/devtools/client/shared/widgets/spectrum.css
new file mode 100644
index 0000000000..b7bf31668f
--- /dev/null
+++ b/devtools/client/shared/widgets/spectrum.css
@@ -0,0 +1,330 @@
+/* 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/. */
+
+:root {
+ --learn-more-underline: var(--grey-30);
+}
+
+.theme-dark:root {
+ --learn-more-underline: var(--grey-50);
+}
+
+#eyedropper-button {
+ margin-inline-end: 5px;
+ display: block;
+}
+
+#eyedropper-button::before {
+ background-image: url(chrome://devtools/skin/images/command-eyedropper.svg);
+}
+
+/* Mix-in classes */
+
+.spectrum-checker {
+ background-color: #eee;
+ background-image: linear-gradient(
+ 45deg,
+ #ccc 25%,
+ transparent 25%,
+ transparent 75%,
+ #ccc 75%,
+ #ccc
+ ),
+ linear-gradient(
+ 45deg,
+ #ccc 25%,
+ transparent 25%,
+ transparent 75%,
+ #ccc 75%,
+ #ccc
+ );
+ background-size: 12px 12px;
+ background-position: 0 0, 6px 6px;
+}
+
+.spectrum-box {
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 2px;
+ background-clip: content-box;
+}
+
+/* Elements */
+
+#spectrum-tooltip {
+ padding: 5px;
+}
+
+/**
+ * Spectrum controls set the layout for the controls section of the color picker.
+ */
+.spectrum-controls {
+ display: flex;
+ justify-content: space-between;
+ margin-block-start: 10px;
+ margin-inline-end: 5px;
+}
+
+.spectrum-controls {
+ width: 200px;
+}
+
+.spectrum-container {
+ display: flex;
+ flex-direction: column;
+ margin: -1px;
+ padding-block-end: 6px;
+}
+
+/**
+ * This styles the color preview and adds a checkered background overlay inside of it. The overlay
+ * can be manipulated using the --overlay-color variable.
+ */
+.spectrum-color-preview {
+ --overlay-color: transparent;
+ border: 1px solid transparent;
+ border-radius: 50%;
+ width: 27px;
+ height: 27px;
+ background-color: #fff;
+ background-image: linear-gradient(var(--overlay-color), var(--overlay-color)),
+ linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%),
+ linear-gradient(45deg, #ccc 25%, transparent 25%, transparent 75%, #ccc 75%);
+ background-size: 12px 12px;
+ background-position: 0 0, 6px 6px;
+}
+
+.spectrum-color-preview.high-luminance {
+ border-color: #ccc;
+}
+
+.spectrum-slider-container {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-around;
+ width: 130px;
+ margin-inline-start: 10px;
+ height: 30px;
+}
+
+/* Keep aspect ratio:
+http://www.briangrinstead.com/blog/keep-aspect-ratio-with-html-and-css */
+.spectrum-color-picker {
+ position: relative;
+ width: 205px;
+ height: 120px;
+}
+
+.spectrum-color {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+}
+
+.spectrum-sat,
+.spectrum-val {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+}
+
+.spectrum-alpha {
+ margin-block-start: 3px;
+}
+
+.spectrum-alpha,
+.spectrum-hue {
+ position: relative;
+ height: 8px;
+}
+
+.spectrum-alpha-input,
+.spectrum-hue-input {
+ width: 100%;
+ margin: 0;
+ position: absolute;
+ height: 8px;
+ border-radius: 2px;
+ direction: initial;
+}
+
+.spectrum-hue-input,
+.spectrum-alpha-input {
+ outline-offset: 4px;
+}
+
+.spectrum-hue-input::-moz-range-thumb,
+.spectrum-alpha-input::-moz-range-thumb {
+ cursor: pointer;
+ height: 12px;
+ width: 12px;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
+ background: #fff;
+ border-radius: 50%;
+ opacity: 0.9;
+ border: none;
+}
+
+.spectrum-hue-input::-moz-range-track {
+ border-radius: 2px;
+ height: 8px;
+ background: linear-gradient(
+ to right,
+ #ff0000 0%,
+ #ffff00 17%,
+ #00ff00 33%,
+ #00ffff 50%,
+ #0000ff 67%,
+ #ff00ff 83%,
+ #ff0000 100%
+ );
+}
+
+.spectrum-sat {
+ background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0));
+}
+
+.spectrum-val {
+ background-image: linear-gradient(to top, #000000, rgba(204, 154, 129, 0));
+}
+
+.spectrum-dragger {
+ user-select: none;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ cursor: pointer;
+ border-radius: 50%;
+ height: 8px;
+ width: 8px;
+ border: 1px solid white;
+ box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
+}
+
+.spectrum-color-contrast {
+ padding-block-start: 8px;
+ padding-inline-start: 4px;
+ padding-inline-end: 4px;
+ line-height: 1.2em;
+}
+
+.contrast-ratio-header-and-single-ratio,
+.contrast-ratio-range {
+ display: flex;
+ align-items: stretch;
+}
+
+.contrast-ratio-range {
+ margin-block-start: 4px;
+ margin-inline-start: 1px;
+ margin-block-end: 2px;
+}
+
+.spectrum-color-contrast.visible {
+ display: block;
+}
+
+.spectrum-color-contrast.visible:not(.range) .contrast-ratio-single,
+.spectrum-color-contrast.visible.range .contrast-ratio-range {
+ display: flex;
+}
+
+.spectrum-color-contrast,
+.spectrum-color-contrast .contrast-ratio-range,
+.spectrum-color-contrast.range .contrast-ratio-single,
+.spectrum-color-contrast.error .accessibility-color-contrast-separator,
+.spectrum-color-contrast.error .contrast-ratio-max {
+ display: none;
+}
+
+.contrast-ratio-label {
+ font-size: 10px;
+ padding-inline-end: 4px;
+ color: var(--theme-toolbar-color);
+}
+
+.spectrum-color-contrast .accessibility-contrast-value {
+ font-size: 10px;
+ color: var(--theme-body-color);
+ border-bottom: 1px solid var(--learn-more-underline);
+}
+
+.spectrum-color-contrast.visible:not(.error) .contrast-ratio-single .accessibility-contrast-value {
+ margin-inline-start: 10px;
+}
+
+.spectrum-color-contrast.visible:not(.error) .contrast-ratio-min .accessibility-contrast-value,
+.spectrum-color-contrast.visible:not(.error) .contrast-ratio-max .accessibility-contrast-value{
+ margin-inline-start: 7px;
+}
+
+.spectrum-color-contrast .accessibility-contrast-value:not(:empty)::before {
+ width: auto;
+ content: none;
+ padding-inline-start: 2px;
+}
+
+.spectrum-color-contrast.visible:not(.error) .contrast-value-and-swatch:before {
+ display: inline-flex;
+ content: "";
+ height: 9px;
+ width: 9px;
+ background-color: var(--accessibility-contrast-color);
+}
+
+.spectrum-color-contrast.visible:not(.error):-moz-locale-dir(ltr) .contrast-value-and-swatch:before {
+ box-shadow: 0 0 0 1px var(--grey-40), 6px 5px var(--accessibility-contrast-bg),
+ 6px 5px 0 1px var(--grey-40);
+}
+
+.spectrum-color-contrast.visible:not(.error):-moz-locale-dir(rtl) .contrast-value-and-swatch:before {
+ box-shadow: 0 0 0 1px var(--grey-40), -6px 5px var(--accessibility-contrast-bg),
+ -6px 5px 0 1px var(--grey-40);
+}
+
+.spectrum-color-contrast .accessibility-color-contrast-separator:before {
+ margin-inline-end: 4px;
+ color: var(--theme-body-color);
+}
+
+.spectrum-color-contrast .accessibility-color-contrast-large-text {
+ margin-inline-start: 1px;
+ margin-inline-end: 1px;
+ unicode-bidi: isolate;
+}
+
+.learn-more {
+ background-repeat: no-repeat;
+ -moz-context-properties: fill;
+ background-image: url(chrome://devtools/skin/images/info-small.svg);
+ background-color: transparent;
+ fill: var(--theme-icon-dimmed-color);
+ border: none;
+ margin-inline-start: auto;
+ margin-block-start: 1px;
+ aspect-ratio: 1 / 1;
+ width: 12px;
+}
+
+.learn-more:-moz-locale-dir(ltr) {
+ margin-inline-end: -5px;
+}
+
+.learn-more:-moz-locale-dir(rtl) {
+ margin-inline-end: -2px;
+}
+
+.learn-more:hover,
+.learn-more:focus {
+ fill: var(--theme-icon-color);
+ cursor: pointer;
+ outline: none;
+}
+
+.learn-more::-moz-focus-inner {
+ border: none;
+}
diff --git a/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js b/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js
new file mode 100644
index 0000000000..01f1b0ec91
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/EventTooltipHelper.js
@@ -0,0 +1,419 @@
+/* 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/. */
+
+"use strict";
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/inspector.properties"
+);
+
+const Editor = require("resource://devtools/client/shared/sourceeditor/editor.js");
+const beautify = require("resource://devtools/shared/jsbeautify/beautify.js");
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const CONTAINER_WIDTH = 500;
+
+const L10N_BUBBLING = L10N.getStr("eventsTooltip.Bubbling");
+const L10N_CAPTURING = L10N.getStr("eventsTooltip.Capturing");
+
+class EventTooltip extends EventEmitter {
+ /**
+ * Set the content of a provided HTMLTooltip instance to display a list of event
+ * listeners, with their event type, capturing argument and a link to the code
+ * of the event handler.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The tooltip instance on which the event details content should be set
+ * @param {Array} eventListenerInfos
+ * A list of event listeners
+ * @param {Toolbox} toolbox
+ * Toolbox used to select debugger panel
+ * @param {NodeFront} nodeFront
+ * The nodeFront we're displaying event listeners for.
+ */
+ constructor(tooltip, eventListenerInfos, toolbox, nodeFront) {
+ super();
+
+ this._tooltip = tooltip;
+ this._toolbox = toolbox;
+ this._eventEditors = new WeakMap();
+ this._nodeFront = nodeFront;
+ this._eventListenersAbortController = new AbortController();
+
+ // Used in tests: add a reference to the EventTooltip instance on the HTMLTooltip.
+ this._tooltip.eventTooltip = this;
+
+ this._headerClicked = this._headerClicked.bind(this);
+ this._eventToggleCheckboxChanged =
+ this._eventToggleCheckboxChanged.bind(this);
+
+ this._subscriptions = [];
+
+ const config = {
+ mode: Editor.modes.js,
+ lineNumbers: false,
+ lineWrapping: true,
+ readOnly: true,
+ styleActiveLine: true,
+ extraKeys: {},
+ theme: "mozilla markup-view",
+ cm6: true,
+ };
+
+ const doc = this._tooltip.doc;
+ this.container = doc.createElementNS(XHTML_NS, "ul");
+ this.container.className = "devtools-tooltip-events-container";
+
+ const sourceMapURLService = this._toolbox.sourceMapURLService;
+
+ for (let i = 0; i < eventListenerInfos.length; i++) {
+ const listener = eventListenerInfos[i];
+
+ // Create this early so we can refer to it from a closure, below.
+ const content = doc.createElementNS(XHTML_NS, "div");
+ const codeMirrorContainerId = `cm-${i}`;
+ content.id = codeMirrorContainerId;
+
+ // Header
+ const header = doc.createElementNS(XHTML_NS, "div");
+ header.className = "event-header";
+ header.setAttribute("data-event-type", listener.type);
+
+ const arrow = doc.createElementNS(XHTML_NS, "button");
+ arrow.className = "theme-twisty";
+ arrow.setAttribute("aria-expanded", "false");
+ arrow.setAttribute("aria-owns", codeMirrorContainerId);
+ arrow.setAttribute(
+ "title",
+ L10N.getFormatStr("eventsTooltip.toggleButton.label", listener.type)
+ );
+
+ header.appendChild(arrow);
+
+ if (!listener.hide.type) {
+ const eventTypeLabel = doc.createElementNS(XHTML_NS, "span");
+ eventTypeLabel.className = "event-tooltip-event-type";
+ eventTypeLabel.textContent = listener.type;
+ eventTypeLabel.setAttribute("title", listener.type);
+ header.appendChild(eventTypeLabel);
+ }
+
+ const filename = doc.createElementNS(XHTML_NS, "span");
+ filename.className = "event-tooltip-filename devtools-monospace";
+
+ let location = null;
+ let text = listener.origin;
+ let title = text;
+ if (listener.hide.filename) {
+ text = L10N.getStr("eventsTooltip.unknownLocation");
+ title = L10N.getStr("eventsTooltip.unknownLocationExplanation");
+ } else {
+ location = this._parseLocation(listener.origin);
+
+ // There will be no source actor if the listener is a native function
+ // or wasn't a debuggee, in which case there's also not going to be
+ // a sourcemap, so we don't need to worry about subscribing.
+ if (location && listener.sourceActor) {
+ location.id = listener.sourceActor;
+
+ this._subscriptions.push(
+ sourceMapURLService.subscribeByID(
+ location.id,
+ location.line,
+ location.column,
+ originalLocation => {
+ const currentLoc = originalLocation || location;
+
+ const newURI = currentLoc.url + ":" + currentLoc.line;
+ filename.textContent = newURI;
+ filename.setAttribute("title", newURI);
+
+ // This is emitted for testing.
+ this._tooltip.emitForTests("event-tooltip-source-map-ready");
+ }
+ )
+ );
+ }
+ }
+
+ filename.textContent = text;
+ filename.setAttribute("title", title);
+ header.appendChild(filename);
+
+ if (!listener.hide.debugger) {
+ const debuggerIcon = doc.createElementNS(XHTML_NS, "button");
+ debuggerIcon.className = "event-tooltip-debugger-icon";
+ const openInDebugger = L10N.getFormatStr(
+ "eventsTooltip.openInDebugger2",
+ listener.type
+ );
+ debuggerIcon.setAttribute("title", openInDebugger);
+ header.appendChild(debuggerIcon);
+ }
+
+ const attributesContainer = doc.createElementNS(XHTML_NS, "div");
+ attributesContainer.className = "event-tooltip-attributes-container";
+ header.appendChild(attributesContainer);
+
+ if (listener.tags) {
+ for (const tag of listener.tags.split(",")) {
+ const attributesBox = doc.createElementNS(XHTML_NS, "div");
+ attributesBox.className = "event-tooltip-attributes-box";
+ attributesContainer.appendChild(attributesBox);
+
+ const tagBox = doc.createElementNS(XHTML_NS, "span");
+ tagBox.className = "event-tooltip-attributes";
+ tagBox.textContent = tag;
+ tagBox.setAttribute("title", tag);
+ attributesBox.appendChild(tagBox);
+ }
+ }
+
+ if (!listener.hide.capturing) {
+ const attributesBox = doc.createElementNS(XHTML_NS, "div");
+ attributesBox.className = "event-tooltip-attributes-box";
+ attributesContainer.appendChild(attributesBox);
+
+ const capturing = doc.createElementNS(XHTML_NS, "span");
+ capturing.className = "event-tooltip-attributes";
+
+ const phase = listener.capturing ? L10N_CAPTURING : L10N_BUBBLING;
+ capturing.textContent = phase;
+ capturing.setAttribute("title", phase);
+ attributesBox.appendChild(capturing);
+ }
+
+ const toggleListenerCheckbox = doc.createElementNS(XHTML_NS, "input");
+ toggleListenerCheckbox.type = "checkbox";
+ toggleListenerCheckbox.className =
+ "event-tooltip-listener-toggle-checkbox";
+ toggleListenerCheckbox.setAttribute(
+ "aria-label",
+ L10N.getFormatStr("eventsTooltip.toggleListenerLabel", listener.type)
+ );
+ if (listener.eventListenerInfoId) {
+ toggleListenerCheckbox.checked = listener.enabled;
+ toggleListenerCheckbox.setAttribute(
+ "data-event-listener-info-id",
+ listener.eventListenerInfoId
+ );
+ toggleListenerCheckbox.addEventListener(
+ "change",
+ this._eventToggleCheckboxChanged,
+ { signal: this._eventListenersAbortController.signal }
+ );
+ } else {
+ toggleListenerCheckbox.checked = true;
+ toggleListenerCheckbox.setAttribute("disabled", true);
+ }
+ header.appendChild(toggleListenerCheckbox);
+
+ // Content
+ const editor = new Editor(config);
+ this._eventEditors.set(content, {
+ editor,
+ handler: listener.handler,
+ native: listener.native,
+ appended: false,
+ location,
+ });
+
+ content.className = "event-tooltip-content-box";
+
+ const li = doc.createElementNS(XHTML_NS, "li");
+ li.append(header, content);
+ this.container.appendChild(li);
+ this._addContentListeners(header);
+ }
+
+ this._tooltip.panel.innerHTML = "";
+ this._tooltip.panel.appendChild(this.container);
+ this._tooltip.setContentSize({ width: CONTAINER_WIDTH, height: Infinity });
+ }
+
+ _addContentListeners(header) {
+ header.addEventListener("click", this._headerClicked, {
+ signal: this._eventListenersAbortController.signal,
+ });
+ }
+
+ _headerClicked(event) {
+ // Clicking on the checkbox shouldn't impact the header (checkbox state change is
+ // handled in _eventToggleCheckboxChanged).
+ if (
+ event.target.classList.contains("event-tooltip-listener-toggle-checkbox")
+ ) {
+ event.stopPropagation();
+ return;
+ }
+
+ if (event.target.classList.contains("event-tooltip-debugger-icon")) {
+ this._debugClicked(event);
+ event.stopPropagation();
+ return;
+ }
+
+ const doc = this._tooltip.doc;
+ const header = event.currentTarget;
+ const content = header.nextElementSibling;
+ const twisty = header.querySelector(".theme-twisty");
+
+ if (content.hasAttribute("open")) {
+ header.classList.remove("content-expanded");
+ twisty.setAttribute("aria-expanded", false);
+ content.removeAttribute("open");
+ } else {
+ // Close other open events first
+ const openHeaders = doc.querySelectorAll(
+ ".event-header.content-expanded"
+ );
+ const openContent = doc.querySelectorAll(
+ ".event-tooltip-content-box[open]"
+ );
+ for (const node of openHeaders) {
+ node.classList.remove("content-expanded");
+ const nodeTwisty = node.querySelector(".theme-twisty");
+ nodeTwisty.setAttribute("aria-expanded", false);
+ }
+ for (const node of openContent) {
+ node.removeAttribute("open");
+ }
+
+ header.classList.add("content-expanded");
+ content.setAttribute("open", "");
+ twisty.setAttribute("aria-expanded", true);
+
+ const eventEditor = this._eventEditors.get(content);
+
+ if (eventEditor.appended) {
+ return;
+ }
+
+ const { editor, handler } = eventEditor;
+
+ const iframe = doc.createElementNS(XHTML_NS, "iframe");
+ iframe.classList.add("event-tooltip-editor-frame");
+ iframe.setAttribute(
+ "title",
+ L10N.getFormatStr(
+ "eventsTooltip.codeIframeTitle",
+ header.getAttribute("data-event-type")
+ )
+ );
+
+ editor.appendTo(content, iframe).then(() => {
+ const tidied = beautify.js(handler, { indent_size: 2 });
+ editor.setText(tidied);
+
+ eventEditor.appended = true;
+
+ const container = header.parentElement.getBoundingClientRect();
+ if (header.getBoundingClientRect().top < container.top) {
+ header.scrollIntoView(true);
+ } else if (content.getBoundingClientRect().bottom > container.bottom) {
+ content.scrollIntoView(false);
+ }
+
+ this._tooltip.emitForTests("event-tooltip-ready");
+ });
+ }
+ }
+
+ _debugClicked(event) {
+ const header = event.currentTarget;
+ const content = header.nextElementSibling;
+
+ const { location } = this._eventEditors.get(content);
+ if (location) {
+ // Save a copy of toolbox as it will be set to null when we hide the tooltip.
+ const toolbox = this._toolbox;
+
+ this._tooltip.hide();
+
+ toolbox.viewSourceInDebugger(
+ location.url,
+ location.line,
+ location.column,
+ location.id
+ );
+ }
+ }
+
+ async _eventToggleCheckboxChanged(event) {
+ const checkbox = event.currentTarget;
+ const id = checkbox.getAttribute("data-event-listener-info-id");
+ if (checkbox.checked) {
+ await this._nodeFront.enableEventListener(id);
+ } else {
+ await this._nodeFront.disableEventListener(id);
+ }
+ this.emit("event-tooltip-listener-toggled", {
+ hasDisabledEventListeners:
+ // No need to query the other checkboxes if the handled checkbox is unchecked
+ !checkbox.checked ||
+ this._tooltip.doc.querySelector(
+ `input.event-tooltip-listener-toggle-checkbox:not(:checked)`
+ ) !== null,
+ });
+ }
+
+ /**
+ * Parse URI and return {url, line, column}; or return null if it can't be parsed.
+ */
+ _parseLocation(uri) {
+ if (uri && uri !== "?") {
+ uri = uri.replace(/"/g, "");
+
+ let matches = uri.match(/(.*):(\d+):(\d+$)/);
+
+ if (matches) {
+ return {
+ url: matches[1],
+ line: parseInt(matches[2], 10),
+ column: parseInt(matches[3], 10),
+ };
+ } else if ((matches = uri.match(/(.*):(\d+$)/))) {
+ return {
+ url: matches[1],
+ line: parseInt(matches[2], 10),
+ column: null,
+ };
+ }
+ return { url: uri, line: 1, column: null };
+ }
+ return null;
+ }
+
+ destroy() {
+ if (this._tooltip) {
+ const boxes = this.container.querySelectorAll(
+ ".event-tooltip-content-box"
+ );
+
+ for (const box of boxes) {
+ const { editor } = this._eventEditors.get(box);
+ editor.destroy();
+ }
+
+ this._eventEditors = null;
+ this._tooltip.eventTooltip = null;
+ }
+
+ this.clearEvents();
+ if (this._eventListenersAbortController) {
+ this._eventListenersAbortController.abort();
+ this._eventListenersAbortController = null;
+ }
+
+ for (const unsubscribe of this._subscriptions) {
+ unsubscribe();
+ }
+
+ this._toolbox = this._tooltip = this._nodeFront = null;
+ }
+}
+
+module.exports.EventTooltip = EventTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/HTMLTooltip.js b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
new file mode 100644
index 0000000000..d505e3b78d
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/HTMLTooltip.js
@@ -0,0 +1,1062 @@
+/* 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/. */
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+
+loader.lazyRequireGetter(
+ this,
+ "focusableSelector",
+ "resource://devtools/client/shared/focus.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "TooltipToggle",
+ "resource://devtools/client/shared/widgets/tooltip/TooltipToggle.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "listenOnce",
+ "resource://devtools/shared/async-utils.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "DevToolsUtils",
+ "resource://devtools/shared/DevToolsUtils.js"
+);
+
+const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+const POSITION = {
+ TOP: "top",
+ BOTTOM: "bottom",
+};
+
+module.exports.POSITION = POSITION;
+
+const TYPE = {
+ NORMAL: "normal",
+ ARROW: "arrow",
+ DOORHANGER: "doorhanger",
+};
+
+module.exports.TYPE = TYPE;
+
+const ARROW_WIDTH = {
+ normal: 0,
+ arrow: 32,
+ // This is the value calculated for the .tooltip-arrow element in tooltip.css
+ // which includes the arrow width (20px) plus the extra margin added so that
+ // the drop shadow is not cropped (2px each side).
+ doorhanger: 24,
+};
+
+const ARROW_OFFSET = {
+ normal: 0,
+ // Default offset between the tooltip's edge and the tooltip arrow.
+ arrow: 20,
+ // Match other Firefox menus which use 10px from edge (but subtract the 2px
+ // margin included in the ARROW_WIDTH above).
+ doorhanger: 8,
+};
+
+const EXTRA_HEIGHT = {
+ normal: 0,
+ // The arrow is 16px tall, but merges on with the panel border
+ arrow: 14,
+ // The doorhanger arrow is 10px tall, but merges on 1px with the panel border
+ doorhanger: 9,
+};
+
+/**
+ * Calculate the vertical position & offsets to use for the tooltip. Will attempt to
+ * respect the provided height and position preferences, unless the available height
+ * prevents this.
+ *
+ * @param {DOMRect} anchorRect
+ * Bounding rectangle for the anchor, relative to the tooltip document.
+ * @param {DOMRect} viewportRect
+ * Bounding rectangle for the viewport. top/left can be different from 0 if some
+ * space should not be used by tooltips (for instance OS toolbars, taskbars etc.).
+ * @param {Number} height
+ * Preferred height for the tooltip.
+ * @param {String} pos
+ * Preferred position for the tooltip. Possible values: "top" or "bottom".
+ * @param {Number} offset
+ * Offset between the top of the anchor and the tooltip.
+ * @return {Object}
+ * - {Number} top: the top offset for the tooltip.
+ * - {Number} height: the height to use for the tooltip container.
+ * - {String} computedPosition: Can differ from the preferred position depending
+ * on the available height). "top" or "bottom"
+ */
+const calculateVerticalPosition = (
+ anchorRect,
+ viewportRect,
+ height,
+ pos,
+ offset
+) => {
+ const { TOP, BOTTOM } = POSITION;
+
+ let { top: anchorTop, height: anchorHeight } = anchorRect;
+
+ // Translate to the available viewport space before calculating dimensions and position.
+ anchorTop -= viewportRect.top;
+
+ // Calculate available space for the tooltip.
+ const availableTop = anchorTop;
+ const availableBottom = viewportRect.height - (anchorTop + anchorHeight);
+
+ // Find POSITION
+ let keepPosition = false;
+ if (pos === TOP) {
+ keepPosition = availableTop >= height + offset;
+ } else if (pos === BOTTOM) {
+ keepPosition = availableBottom >= height + offset;
+ }
+ if (!keepPosition) {
+ pos = availableTop > availableBottom ? TOP : BOTTOM;
+ }
+
+ // Calculate HEIGHT.
+ const availableHeight = pos === TOP ? availableTop : availableBottom;
+ height = Math.min(height, availableHeight - offset);
+
+ // Calculate TOP.
+ let top =
+ pos === TOP
+ ? anchorTop - height - offset
+ : anchorTop + anchorHeight + offset;
+
+ // Translate back to absolute coordinates by re-including viewport top margin.
+ top += viewportRect.top;
+
+ return {
+ top: Math.round(top),
+ height: Math.round(height),
+ computedPosition: pos,
+ };
+};
+
+/**
+ * Calculate the horizontal position & offsets to use for the tooltip. Will
+ * attempt to respect the provided width and position preferences, unless the
+ * available width prevents this.
+ *
+ * @param {DOMRect} anchorRect
+ * Bounding rectangle for the anchor, relative to the tooltip document.
+ * @param {DOMRect} viewportRect
+ * Bounding rectangle for the viewport. top/left can be different from
+ * 0 if some space should not be used by tooltips (for instance OS
+ * toolbars, taskbars etc.).
+ * @param {DOMRect} windowRect
+ * Bounding rectangle for the window. Used to determine which direction
+ * doorhangers should hang.
+ * @param {Number} width
+ * Preferred width for the tooltip.
+ * @param {String} type
+ * The tooltip type (e.g. "arrow").
+ * @param {Number} offset
+ * Horizontal offset in pixels.
+ * @param {Number} borderRadius
+ * The border radius of the panel. This is added to ARROW_OFFSET to
+ * calculate the distance from the edge of the tooltip to the start
+ * of arrow. It is separate from ARROW_OFFSET since it will vary by
+ * platform.
+ * @param {Boolean} isRtl
+ * If the anchor is in RTL, the tooltip should be aligned to the right.
+ * @return {Object}
+ * - {Number} left: the left offset for the tooltip.
+ * - {Number} width: the width to use for the tooltip container.
+ * - {Number} arrowLeft: the left offset to use for the arrow element.
+ */
+const calculateHorizontalPosition = (
+ anchorRect,
+ viewportRect,
+ windowRect,
+ width,
+ type,
+ offset,
+ borderRadius,
+ isRtl,
+ isMenuTooltip
+) => {
+ // All tooltips from content should follow the writing direction.
+ //
+ // For tooltips (including doorhanger tooltips) we follow the writing
+ // direction but for menus created using doorhangers the guidelines[1] say
+ // that:
+ //
+ // "Doorhangers opening on the right side of the view show the directional
+ // arrow on the right.
+ //
+ // Doorhangers opening on the left side of the view show the directional
+ // arrow on the left.
+ //
+ // Never place the directional arrow at the center of doorhangers."
+ //
+ // [1] https://design.firefox.com/photon/components/doorhangers.html#directional-arrow
+ //
+ // So for those we need to check if the anchor is more right or left.
+ let hangDirection;
+ if (type === TYPE.DOORHANGER && isMenuTooltip) {
+ const anchorCenter = anchorRect.left + anchorRect.width / 2;
+ const viewCenter = windowRect.left + windowRect.width / 2;
+ hangDirection = anchorCenter >= viewCenter ? "left" : "right";
+ } else {
+ hangDirection = isRtl ? "left" : "right";
+ }
+
+ const anchorWidth = anchorRect.width;
+
+ // Calculate logical start of anchor relative to the viewport.
+ const anchorStart =
+ hangDirection === "right"
+ ? anchorRect.left - viewportRect.left
+ : viewportRect.right - anchorRect.right;
+
+ // Calculate tooltip width.
+ const tooltipWidth = Math.min(width, viewportRect.width);
+
+ // Calculate tooltip start.
+ let tooltipStart = anchorStart + offset;
+ tooltipStart = Math.min(tooltipStart, viewportRect.width - tooltipWidth);
+ tooltipStart = Math.max(0, tooltipStart);
+
+ // Calculate arrow start (tooltip's start might be updated)
+ const arrowWidth = ARROW_WIDTH[type];
+ let arrowStart;
+ // Arrow and doorhanger style tooltips may need to be shifted
+ if (type === TYPE.ARROW || type === TYPE.DOORHANGER) {
+ const arrowOffset = ARROW_OFFSET[type] + borderRadius;
+
+ // Where will the point of the arrow be if we apply the standard offset?
+ const arrowCenter = tooltipStart + arrowOffset + arrowWidth / 2;
+
+ // How does that compare to the center of the anchor?
+ const anchorCenter = anchorStart + anchorWidth / 2;
+
+ // If the anchor is too narrow, align the arrow and the anchor center.
+ if (arrowCenter > anchorCenter) {
+ tooltipStart = Math.max(0, tooltipStart - (arrowCenter - anchorCenter));
+ }
+ // Arrow's start offset relative to the anchor.
+ arrowStart = Math.min(arrowOffset, (anchorWidth - arrowWidth) / 2) | 0;
+ // Translate the coordinate to tooltip container
+ arrowStart += anchorStart - tooltipStart;
+ // Make sure the arrow remains in the tooltip container.
+ arrowStart = Math.min(arrowStart, tooltipWidth - arrowWidth - borderRadius);
+ arrowStart = Math.max(arrowStart, borderRadius);
+ }
+
+ // Convert from logical coordinates to physical
+ const left =
+ hangDirection === "right"
+ ? viewportRect.left + tooltipStart
+ : viewportRect.right - tooltipStart - tooltipWidth;
+ const arrowLeft =
+ hangDirection === "right"
+ ? arrowStart
+ : tooltipWidth - arrowWidth - arrowStart;
+
+ return {
+ left: Math.round(left),
+ width: Math.round(tooltipWidth),
+ arrowLeft: Math.round(arrowLeft),
+ };
+};
+
+/**
+ * Get the bounding client rectangle for a given node, relative to a custom
+ * reference element (instead of the default for getBoundingClientRect which
+ * is always the element's ownerDocument).
+ */
+const getRelativeRect = function (node, relativeTo) {
+ // getBoxQuads is a non-standard WebAPI which will not work on non-firefox
+ // browser when running launchpad on Chrome.
+ if (
+ !node.getBoxQuads ||
+ !node.getBoxQuads({
+ relativeTo,
+ createFramesForSuppressedWhitespace: false,
+ })[0]
+ ) {
+ const { top, left, width, height } = node.getBoundingClientRect();
+ const right = left + width;
+ const bottom = top + height;
+ return { top, right, bottom, left, width, height };
+ }
+
+ // Width and Height can be taken from the rect.
+ const { width, height } = node.getBoundingClientRect();
+
+ const quadBounds = node
+ .getBoxQuads({ relativeTo, createFramesForSuppressedWhitespace: false })[0]
+ .getBounds();
+ const top = quadBounds.top;
+ const left = quadBounds.left;
+
+ // Compute right and bottom coordinates using the rest of the data.
+ const right = left + width;
+ const bottom = top + height;
+
+ return { top, right, bottom, left, width, height };
+};
+
+/**
+ * The HTMLTooltip can display HTML content in a tooltip popup.
+ *
+ * @param {Document} toolboxDoc
+ * The toolbox document to attach the HTMLTooltip popup.
+ * @param {Object}
+ * - {String} className
+ * A string separated list of classes to add to the tooltip container
+ * element.
+ * - {Boolean} consumeOutsideClicks
+ * Defaults to true. The tooltip is closed when clicking outside.
+ * Should this event be stopped and consumed or not.
+ * - {String} id
+ * The ID to assign to the tooltip container element.
+ * - {Boolean} isMenuTooltip
+ * Defaults to false. If the tooltip is a menu then this should be set
+ * to true.
+ * - {String} type
+ * Display type of the tooltip. Possible values: "normal", "arrow", and
+ * "doorhanger".
+ * - {Boolean} useXulWrapper
+ * Defaults to false. If the tooltip is hosted in a XUL document, use a
+ * XUL panel in order to use all the screen viewport available.
+ * - {Boolean} noAutoHide
+ * Defaults to false. If this property is set to false or omitted, the
+ * tooltip will automatically disappear after a few seconds. If this
+ * attribute is set to true, this will not happen and the tooltip will
+ * only hide when the user moves the mouse to another element.
+ */
+function HTMLTooltip(
+ toolboxDoc,
+ {
+ className = "",
+ consumeOutsideClicks = true,
+ id = "",
+ isMenuTooltip = false,
+ type = "normal",
+ useXulWrapper = false,
+ noAutoHide = false,
+ } = {}
+) {
+ EventEmitter.decorate(this);
+
+ this.doc = toolboxDoc;
+ this.id = id;
+ this.className = className;
+ this.type = type;
+ this.noAutoHide = noAutoHide;
+ // consumeOutsideClicks cannot be used if the tooltip is not closed on click
+ this.consumeOutsideClicks = this.noAutoHide ? false : consumeOutsideClicks;
+ this.isMenuTooltip = isMenuTooltip;
+ this.useXulWrapper = this._isXULPopupAvailable() && useXulWrapper;
+ this.preferredWidth = "auto";
+ this.preferredHeight = "auto";
+
+ // The top window is used to attach click event listeners to close the tooltip if the
+ // user clicks on the content page.
+ this.topWindow = this._getTopWindow();
+
+ this._position = null;
+
+ this._onClick = this._onClick.bind(this);
+ this._onMouseup = this._onMouseup.bind(this);
+ this._onXulPanelHidden = this._onXulPanelHidden.bind(this);
+
+ this.container = this._createContainer();
+ if (this.useXulWrapper) {
+ // When using a XUL panel as the wrapper, the actual markup for the tooltip is as
+ // follows :
+ // <panel> <!-- XUL panel used to position the tooltip anywhere on screen -->
+ // <div> <! the actual tooltip-container element -->
+ this.xulPanelWrapper = this._createXulPanelWrapper();
+ this.doc.documentElement.appendChild(this.xulPanelWrapper);
+ this.xulPanelWrapper.appendChild(this.container);
+ } else if (this._hasXULRootElement()) {
+ this.doc.documentElement.appendChild(this.container);
+ } else {
+ // In non-XUL context the container is ready to use as is.
+ this.doc.body.appendChild(this.container);
+ }
+}
+
+module.exports.HTMLTooltip = HTMLTooltip;
+
+HTMLTooltip.prototype = {
+ /**
+ * The tooltip panel is the parentNode of the tooltip content.
+ */
+ get panel() {
+ return this.container.querySelector(".tooltip-panel");
+ },
+
+ /**
+ * The arrow element. Might be null depending on the tooltip type.
+ */
+ get arrow() {
+ return this.container.querySelector(".tooltip-arrow");
+ },
+
+ /**
+ * Retrieve the displayed position used for the tooltip. Null if the tooltip is hidden.
+ */
+ get position() {
+ return this.isVisible() ? this._position : null;
+ },
+
+ get toggle() {
+ if (!this._toggle) {
+ this._toggle = new TooltipToggle(this);
+ }
+
+ return this._toggle;
+ },
+
+ /**
+ * Set the preferred width/height of the panel content.
+ * The panel content is set by appending content to `this.panel`.
+ *
+ * @param {Object}
+ * - {Number} width: preferred width for the tooltip container. If not specified
+ * the tooltip container will be measured before being displayed, and the
+ * measured width will be used as the preferred width.
+ * - {Number} height: preferred height for the tooltip container. If
+ * not specified the tooltip container will be measured before being
+ * displayed, and the measured height will be used as the preferred
+ * height.
+ *
+ * For tooltips whose content height may change while being
+ * displayed, the special value Infinity may be used to produce
+ * a flexible container that accommodates resizing content. Note,
+ * however, that when used in combination with the XUL wrapper the
+ * unfilled part of this container will consume all mouse events
+ * making content behind this area inaccessible until the tooltip is
+ * dismissed.
+ */
+ setContentSize({ width = "auto", height = "auto" } = {}) {
+ this.preferredWidth = width;
+ this.preferredHeight = height;
+ },
+
+ /**
+ * Show the tooltip next to the provided anchor element, or update the tooltip position
+ * if it was already visible. A preferred position can be set.
+ * The event "shown" will be fired after the tooltip is displayed.
+ *
+ * @param {Element} anchor
+ * The reference element with which the tooltip should be aligned
+ * @param {Object} options
+ * Optional settings for positioning the tooltip.
+ * @param {String} options.position
+ * Optional, possible values: top|bottom
+ * If layout permits, the tooltip will be displayed on top/bottom
+ * of the anchor. If omitted, the tooltip will be displayed where
+ * more space is available.
+ * @param {Number} options.x
+ * Optional, horizontal offset between the anchor and the tooltip.
+ * @param {Number} options.y
+ * Optional, vertical offset between the anchor and the tooltip.
+ */
+ async show(anchor, options) {
+ const { left, top } = this._updateContainerBounds(anchor, options);
+ const isTooltipVisible = this.isVisible();
+
+ if (this.useXulWrapper) {
+ if (!isTooltipVisible) {
+ await this._showXulWrapperAt(left, top);
+ } else {
+ this._moveXulWrapperTo(left, top);
+ }
+ } else {
+ this.container.style.left = left + "px";
+ this.container.style.top = top + "px";
+ }
+
+ if (isTooltipVisible) {
+ return;
+ }
+
+ this.container.classList.add("tooltip-visible");
+
+ // Keep a pointer on the focused element to refocus it when hiding the tooltip.
+ this._focusedElement = this.doc.activeElement;
+
+ if (this.doc.defaultView) {
+ if (!this._pendingEventListenerPromise) {
+ // On Windows and Linux, if the tooltip is shown on mousedown/click (which is the
+ // case for the MenuButton component for example), attaching the events listeners
+ // on the window right away would trigger the callbacks; which means the tooltip
+ // would be instantly hidden. To prevent such thing, the event listeners are set
+ // on the next tick.
+ this._pendingEventListenerPromise = new Promise(resolve => {
+ this.doc.defaultView.setTimeout(() => {
+ // Update the top window reference each time in case the host changes.
+ this.topWindow = this._getTopWindow();
+ this.topWindow.addEventListener("click", this._onClick, true);
+ this.topWindow.addEventListener("mouseup", this._onMouseup, true);
+ resolve();
+ }, 0);
+ });
+ }
+
+ await this._pendingEventListenerPromise;
+ this._pendingEventListenerPromise = null;
+ }
+
+ // This is redundant with tooltip-visible, and tooltip-visible
+ // should only be added from here, after the click listener is set.
+ // Otherwise, code listening to tooltip-visible may be firing a click that would be lost.
+ // Unfortunately, doing this cause many non trivial test failures.
+ this.container.classList.add("tooltip-shown");
+
+ this.emit("shown");
+ },
+
+ startTogglingOnHover(baseNode, targetNodeCb, options) {
+ this.toggle.start(baseNode, targetNodeCb, options);
+ },
+
+ stopTogglingOnHover() {
+ this.toggle.stop();
+ },
+
+ _updateContainerBounds(anchor, { position, x = 0, y = 0 } = {}) {
+ // Get anchor geometry
+ let anchorRect = getRelativeRect(anchor, this.doc);
+ if (this.useXulWrapper) {
+ anchorRect = this._convertToScreenRect(anchorRect);
+ }
+
+ const { viewportRect, windowRect } = this._getBoundingRects(anchorRect);
+
+ // Calculate the horizontal position and width
+ let preferredWidth;
+ // Record the height too since it might save us from having to look it up
+ // later.
+ let measuredHeight;
+ const currentScrollTop = this.panel.scrollTop;
+ if (this.preferredWidth === "auto") {
+ // Reset any styles that constrain the dimensions we want to calculate.
+ this.container.style.width = "auto";
+ if (this.preferredHeight === "auto") {
+ this.container.style.height = "auto";
+ }
+ ({ width: preferredWidth, height: measuredHeight } =
+ this._measureContainerSize());
+ } else {
+ preferredWidth = this.preferredWidth;
+ }
+
+ const anchorWin = anchor.ownerDocument.defaultView;
+ const anchorCS = anchorWin.getComputedStyle(anchor);
+ const isRtl = anchorCS.direction === "rtl";
+
+ let borderRadius = 0;
+ if (this.type === TYPE.DOORHANGER) {
+ borderRadius = parseFloat(
+ anchorCS.getPropertyValue("--theme-arrowpanel-border-radius")
+ );
+ if (Number.isNaN(borderRadius)) {
+ borderRadius = 0;
+ }
+ }
+
+ const { left, width, arrowLeft } = calculateHorizontalPosition(
+ anchorRect,
+ viewportRect,
+ windowRect,
+ preferredWidth,
+ this.type,
+ x,
+ borderRadius,
+ isRtl,
+ this.isMenuTooltip
+ );
+
+ // If we constrained the width, then any measured height we have is no
+ // longer valid.
+ if (measuredHeight && width !== preferredWidth) {
+ measuredHeight = undefined;
+ }
+
+ // Apply width and arrow positioning
+ this.container.style.width = width + "px";
+ if (this.type === TYPE.ARROW || this.type === TYPE.DOORHANGER) {
+ this.arrow.style.left = arrowLeft + "px";
+ }
+
+ // Work out how much vertical margin we have.
+ //
+ // This relies on us having set either .tooltip-top or .tooltip-bottom
+ // and on the margins for both being symmetrical. Fortunately the call to
+ // _measureContainerSize above will set .tooltip-top for us and it also
+ // assumes these styles are symmetrical so this should be ok.
+ const panelWindow = this.panel.ownerDocument.defaultView;
+ const panelComputedStyle = panelWindow.getComputedStyle(this.panel);
+ const verticalMargin =
+ parseFloat(panelComputedStyle.marginTop) +
+ parseFloat(panelComputedStyle.marginBottom);
+
+ // Calculate the vertical position and height
+ let preferredHeight;
+ if (this.preferredHeight === "auto") {
+ if (measuredHeight) {
+ // We already have a valid height measured in a previous step.
+ preferredHeight = measuredHeight;
+ } else {
+ this.container.style.height = "auto";
+ ({ height: preferredHeight } = this._measureContainerSize());
+ }
+ preferredHeight += verticalMargin;
+ } else {
+ const themeHeight = EXTRA_HEIGHT[this.type] + verticalMargin;
+ preferredHeight = this.preferredHeight + themeHeight;
+ }
+
+ const { top, height, computedPosition } = calculateVerticalPosition(
+ anchorRect,
+ viewportRect,
+ preferredHeight,
+ position,
+ y
+ );
+
+ this._position = computedPosition;
+ const isTop = computedPosition === POSITION.TOP;
+ this.container.classList.toggle("tooltip-top", isTop);
+ this.container.classList.toggle("tooltip-bottom", !isTop);
+
+ // If the preferred height is set to Infinity, the tooltip container should grow based
+ // on its content's height and use as much height as possible.
+ this.container.classList.toggle(
+ "tooltip-flexible-height",
+ this.preferredHeight === Infinity
+ );
+
+ this.container.style.height = height + "px";
+ this.panel.scrollTop = currentScrollTop;
+
+ return { left, top };
+ },
+
+ /**
+ * Calculate the following boundary rectangles:
+ *
+ * - Viewport rect: This is the region that limits the tooltip dimensions.
+ * When using a XUL panel wrapper, the tooltip will be able to use the whole
+ * screen (excluding space reserved by the OS for toolbars etc.) and hence
+ * the result will be in screen coordinates.
+ * Otherwise, the tooltip is limited to the tooltip's document.
+ *
+ * - Window rect: This is the bounds of the view in which the tooltip is
+ * presented. It is reported in the same coordinates as the viewport
+ * rect and is used for determining in which direction a doorhanger-type
+ * tooltip should "hang".
+ * When using the XUL panel wrapper this will be the dimensions of the
+ * window in screen coordinates. Otherwise it will be the same as the
+ * viewport rect.
+ *
+ * @param {Object} anchorRect
+ * DOMRect-like object of the target anchor element.
+ * We need to pass this to detect the case when the anchor is not in
+ * the current window (because, the center of the window is in
+ * a different window to the anchor).
+ *
+ * @return {Object} An object with the following properties
+ * viewportRect {Object} DOMRect-like object with the Number
+ * properties: top, right, bottom, left, width, height
+ * representing the viewport rect.
+ * windowRect {Object} DOMRect-like object with the Number
+ * properties: top, right, bottom, left, width, height
+ * representing the window rect.
+ */
+ _getBoundingRects(anchorRect) {
+ let viewportRect;
+ let windowRect;
+
+ if (this.useXulWrapper) {
+ // availLeft/Top are the coordinates first pixel available on the screen
+ // for applications (excluding space dedicated for OS toolbars, menus
+ // etc...)
+ // availWidth/Height are the dimensions available to applications
+ // excluding all the OS reserved space
+ const { availLeft, availTop, availHeight, availWidth } =
+ this.doc.defaultView.screen;
+ viewportRect = {
+ top: availTop,
+ right: availLeft + availWidth,
+ bottom: availTop + availHeight,
+ left: availLeft,
+ width: availWidth,
+ height: availHeight,
+ };
+
+ const { screenX, screenY, outerWidth, outerHeight } =
+ this.doc.defaultView;
+ windowRect = {
+ top: screenY,
+ right: screenX + outerWidth,
+ bottom: screenY + outerHeight,
+ left: screenX,
+ width: outerWidth,
+ height: outerHeight,
+ };
+
+ // If the anchor is outside the viewport, it possibly means we have a
+ // multi-monitor environment where the anchor is displayed on a different
+ // monitor to the "current" screen (as determined by the center of the
+ // window). This can happen when, for example, the screen is spread across
+ // two monitors.
+ //
+ // In this case we simply expand viewport in the direction of the anchor
+ // so that we can still calculate the popup position correctly.
+ if (anchorRect.left > viewportRect.right) {
+ const diffWidth = windowRect.right - viewportRect.right;
+ viewportRect.right += diffWidth;
+ viewportRect.width += diffWidth;
+ }
+ if (anchorRect.right < viewportRect.left) {
+ const diffWidth = viewportRect.left - windowRect.left;
+ viewportRect.left -= diffWidth;
+ viewportRect.width += diffWidth;
+ }
+ } else {
+ viewportRect = windowRect =
+ this.doc.documentElement.getBoundingClientRect();
+ }
+
+ return { viewportRect, windowRect };
+ },
+
+ _measureContainerSize() {
+ const xulParent = this.container.parentNode;
+ if (this.useXulWrapper && !this.isVisible()) {
+ // Move the container out of the XUL Panel to measure it.
+ this.doc.documentElement.appendChild(this.container);
+ }
+
+ this.container.classList.add("tooltip-hidden");
+ // Set either of the tooltip-top or tooltip-bottom styles so that we get an
+ // accurate height. We're assuming that the two styles will be symmetrical
+ // and that we will clear this as necessary later.
+ this.container.classList.add("tooltip-top");
+ this.container.classList.remove("tooltip-bottom");
+ const { width, height } = this.container.getBoundingClientRect();
+ this.container.classList.remove("tooltip-hidden");
+
+ if (this.useXulWrapper && !this.isVisible()) {
+ xulParent.appendChild(this.container);
+ }
+
+ return { width, height };
+ },
+
+ /**
+ * Hide the current tooltip. The event "hidden" will be fired when the tooltip
+ * is hidden.
+ */
+ async hide({ fromMouseup = false } = {}) {
+ // Exit if the disable autohide setting is in effect or if hide() is called
+ // from a mouseup event and the tooltip has noAutoHide set to true.
+ if (
+ Services.prefs.getBoolPref("devtools.popup.disable_autohide", false) ||
+ (this.noAutoHide && this.isVisible() && fromMouseup)
+ ) {
+ return;
+ }
+
+ if (!this.isVisible()) {
+ this.emit("hidden");
+ return;
+ }
+
+ // If the tooltip is hidden from a mouseup event, wait for a potential click event
+ // to be consumed before removing event listeners.
+ if (fromMouseup) {
+ await new Promise(resolve => this.topWindow.setTimeout(resolve, 0));
+ }
+
+ if (this._pendingEventListenerPromise) {
+ this._pendingEventListenerPromise.then(() => this.removeEventListeners());
+ } else {
+ this.removeEventListeners();
+ }
+
+ this.container.classList.remove("tooltip-visible", "tooltip-shown");
+ if (this.useXulWrapper) {
+ await this._hideXulWrapper();
+ }
+
+ this.emit("hidden");
+
+ const tooltipHasFocus = this.container.contains(this.doc.activeElement);
+ if (tooltipHasFocus && this._focusedElement) {
+ this._focusedElement.focus();
+ this._focusedElement = null;
+ }
+ },
+
+ removeEventListeners() {
+ this.topWindow.removeEventListener("click", this._onClick, true);
+ this.topWindow.removeEventListener("mouseup", this._onMouseup, true);
+ },
+
+ /**
+ * Check if the tooltip is currently displayed.
+ * @return {Boolean} true if the tooltip is visible
+ */
+ isVisible() {
+ return this.container.classList.contains("tooltip-visible");
+ },
+
+ /**
+ * Destroy the tooltip instance. Hide the tooltip if displayed, remove the
+ * tooltip container from the document.
+ */
+ destroy() {
+ this.hide();
+ this.removeEventListeners();
+ this.container.remove();
+ if (this.xulPanelWrapper) {
+ this.xulPanelWrapper.remove();
+ }
+ if (this._toggle) {
+ this._toggle.destroy();
+ this._toggle = null;
+ }
+ },
+
+ _createContainer() {
+ const container = this.doc.createElementNS(XHTML_NS, "div");
+ container.setAttribute("type", this.type);
+
+ if (this.id) {
+ container.setAttribute("id", this.id);
+ }
+
+ container.classList.add("tooltip-container");
+ if (this.className) {
+ container.classList.add(...this.className.split(" "));
+ }
+
+ const filler = this.doc.createElementNS(XHTML_NS, "div");
+ filler.classList.add("tooltip-filler");
+ container.appendChild(filler);
+
+ const panel = this.doc.createElementNS(XHTML_NS, "div");
+ panel.classList.add("tooltip-panel");
+ container.appendChild(panel);
+
+ if (this.type === TYPE.ARROW || this.type === TYPE.DOORHANGER) {
+ const arrow = this.doc.createElementNS(XHTML_NS, "div");
+ arrow.classList.add("tooltip-arrow");
+ container.appendChild(arrow);
+ }
+ return container;
+ },
+
+ _onClick(e) {
+ if (this._isInTooltipContainer(e.target)) {
+ return;
+ }
+
+ if (this.consumeOutsideClicks && e.button === 0) {
+ // Consume only left click events (button === 0).
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ },
+
+ /**
+ * Hide the tooltip on mouseup rather than on click because the surrounding markup
+ * may change on mousedown in a way that prevents a "click" event from being fired.
+ * If the element that received the mousedown and the mouseup are different, click
+ * will not be fired.
+ */
+ _onMouseup(e) {
+ if (this._isInTooltipContainer(e.target)) {
+ return;
+ }
+
+ this.hide({ fromMouseup: true });
+ },
+
+ _isInTooltipContainer(node) {
+ // Check if the target is the tooltip arrow.
+ if (this.arrow && this.arrow === node) {
+ return true;
+ }
+
+ if (typeof node.closest == "function" && node.closest("menupopup")) {
+ // Ignore events from menupopup elements which will not be children of the
+ // tooltip container even if their owner element is in the tooltip.
+ // See Bug 1811002.
+ return true;
+ }
+
+ const tooltipWindow = this.panel.ownerDocument.defaultView;
+ let win = node.ownerDocument.defaultView;
+
+ // Check if the tooltip panel contains the node if they live in the same document.
+ if (win === tooltipWindow) {
+ return this.panel.contains(node);
+ }
+
+ // Check if the node window is in the tooltip container.
+ while (win.parent && win.parent !== win) {
+ if (win.parent === tooltipWindow) {
+ // If the parent window is the tooltip window, check if the tooltip contains
+ // the current frame element.
+ return this.panel.contains(win.frameElement);
+ }
+ win = win.parent;
+ }
+
+ return false;
+ },
+
+ _onXulPanelHidden() {
+ if (this.isVisible()) {
+ this.hide();
+ }
+ },
+
+ /**
+ * Focus on the first focusable item in the tooltip.
+ *
+ * Returns true if we found something to focus on, false otherwise.
+ */
+ focus() {
+ const focusableElement = this.panel.querySelector(focusableSelector);
+ if (focusableElement) {
+ focusableElement.focus();
+ }
+ return !!focusableElement;
+ },
+
+ /**
+ * Focus on the last focusable item in the tooltip.
+ *
+ * Returns true if we found something to focus on, false otherwise.
+ */
+ focusEnd() {
+ const focusableElements = this.panel.querySelectorAll(focusableSelector);
+ if (focusableElements.length) {
+ focusableElements[focusableElements.length - 1].focus();
+ }
+ return focusableElements.length !== 0;
+ },
+
+ _getTopWindow() {
+ return DevToolsUtils.getTopWindow(this.doc.defaultView);
+ },
+
+ /**
+ * Check if the tooltip's owner document has XUL root element.
+ */
+ _hasXULRootElement() {
+ return this.doc.documentElement.namespaceURI === XUL_NS;
+ },
+
+ _isXULPopupAvailable() {
+ return this.doc.nodePrincipal.isSystemPrincipal;
+ },
+
+ _createXulPanelWrapper() {
+ const panel = this.doc.createXULElement("panel");
+
+ // XUL panel is only a way to display DOM elements outside of the document viewport,
+ // so disable all features that impact the behavior.
+ panel.setAttribute("animate", false);
+ panel.setAttribute("consumeoutsideclicks", false);
+ panel.setAttribute("incontentshell", false);
+ panel.setAttribute("noautofocus", true);
+ panel.setAttribute("noautohide", this.noAutoHide);
+
+ panel.setAttribute("ignorekeys", true);
+ panel.setAttribute("tooltip", "aHTMLTooltip");
+
+ // Use type="arrow" to prevent side effects (see Bug 1285206)
+ panel.setAttribute("type", "arrow");
+ panel.setAttribute("tooltip-type", this.type);
+
+ panel.setAttribute("flip", "none");
+
+ panel.setAttribute("level", "top");
+ panel.setAttribute("class", "tooltip-xul-wrapper");
+
+ // Stop this appearing as an alert to accessibility.
+ panel.setAttribute("role", "presentation");
+
+ return panel;
+ },
+
+ _showXulWrapperAt(left, top) {
+ this.xulPanelWrapper.addEventListener(
+ "popuphidden",
+ this._onXulPanelHidden
+ );
+ const onPanelShown = listenOnce(this.xulPanelWrapper, "popupshown");
+ this.xulPanelWrapper.openPopupAtScreen(left, top, false);
+ return onPanelShown;
+ },
+
+ _moveXulWrapperTo(left, top) {
+ // FIXME: moveTo should probably account for margins when called from
+ // script. Our current shadow set-up only supports one margin, so it's fine
+ // to use the margin top in both directions.
+ const margin = parseFloat(
+ this.xulPanelWrapper.ownerGlobal.getComputedStyle(this.xulPanelWrapper)
+ .marginTop
+ );
+ this.xulPanelWrapper.moveTo(left + margin, top + margin);
+ },
+
+ _hideXulWrapper() {
+ this.xulPanelWrapper.removeEventListener(
+ "popuphidden",
+ this._onXulPanelHidden
+ );
+
+ if (this.xulPanelWrapper.state === "closed") {
+ // XUL panel is already closed, resolve immediately.
+ return Promise.resolve();
+ }
+
+ const onPanelHidden = listenOnce(this.xulPanelWrapper, "popuphidden");
+ this.xulPanelWrapper.hidePopup();
+ return onPanelHidden;
+ },
+
+ /**
+ * Convert from coordinates relative to the tooltip's document, to coordinates relative
+ * to the "available" screen. By "available" we mean the screen, excluding the OS bars
+ * display on screen edges.
+ */
+ _convertToScreenRect({ left, top, width, height }) {
+ // mozInnerScreenX/Y are the coordinates of the top left corner of the window's
+ // viewport, excluding chrome UI.
+ left += this.doc.defaultView.mozInnerScreenX;
+ top += this.doc.defaultView.mozInnerScreenY;
+ return {
+ top,
+ right: left + width,
+ bottom: top + height,
+ left,
+ width,
+ height,
+ };
+ },
+};
diff --git a/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js b/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js
new file mode 100644
index 0000000000..c73bd4b6b6
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js
@@ -0,0 +1,145 @@
+/* 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/. */
+
+"use strict";
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/inspector.properties"
+);
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+// Default image tooltip max dimension
+const MAX_DIMENSION = 200;
+const CONTAINER_MIN_WIDTH = 100;
+// Should remain synchronized with tooltips.css --image-tooltip-image-padding
+const IMAGE_PADDING = 4;
+// Should remain synchronized with tooltips.css --image-tooltip-label-height
+const LABEL_HEIGHT = 20;
+
+/**
+ * Image preview tooltips should be provided with the naturalHeight and
+ * naturalWidth value for the image to display. This helper loads the provided
+ * image URL in an image object in order to retrieve the image dimensions after
+ * the load.
+ *
+ * @param {Document} doc the document element to use to create the image object
+ * @param {String} imageUrl the url of the image to measure
+ * @return {Promise} returns a promise that will resolve after the iamge load:
+ * - {Number} naturalWidth natural width of the loaded image
+ * - {Number} naturalHeight natural height of the loaded image
+ */
+function getImageDimensions(doc, imageUrl) {
+ return new Promise(resolve => {
+ const imgObj = new doc.defaultView.Image();
+ imgObj.onload = () => {
+ imgObj.onload = null;
+ const { naturalWidth, naturalHeight } = imgObj;
+ resolve({ naturalWidth, naturalHeight });
+ };
+ imgObj.src = imageUrl;
+ });
+}
+
+/**
+ * Set the tooltip content of a provided HTMLTooltip instance to display an
+ * image preview matching the provided imageUrl.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The tooltip instance on which the image preview content should be set
+ * @param {Document} doc
+ * A document element to create the HTML elements needed for the tooltip
+ * @param {String} imageUrl
+ * Absolute URL of the image to display in the tooltip
+ * @param {Object} options
+ * - {Number} naturalWidth mandatory, width of the image to display
+ * - {Number} naturalHeight mandatory, height of the image to display
+ * - {Number} maxDim optional, max width/height of the preview
+ * - {Boolean} hideDimensionLabel optional, pass true to hide the label
+ * - {Boolean} hideCheckeredBackground optional, pass true to hide
+ the checkered background
+ */
+function setImageTooltip(tooltip, doc, imageUrl, options) {
+ let {
+ naturalWidth,
+ naturalHeight,
+ hideDimensionLabel,
+ hideCheckeredBackground,
+ maxDim,
+ } = options;
+ maxDim = maxDim || MAX_DIMENSION;
+
+ let imgHeight = naturalHeight;
+ let imgWidth = naturalWidth;
+ if (imgHeight > maxDim || imgWidth > maxDim) {
+ const scale = maxDim / Math.max(imgHeight, imgWidth);
+ // Only allow integer values to avoid rounding errors.
+ imgHeight = Math.floor(scale * naturalHeight);
+ imgWidth = Math.ceil(scale * naturalWidth);
+ }
+
+ // Create tooltip content
+ const container = doc.createElementNS(XHTML_NS, "div");
+ container.classList.add("devtools-tooltip-image-container");
+
+ const wrapper = doc.createElementNS(XHTML_NS, "div");
+ wrapper.classList.add("devtools-tooltip-image-wrapper");
+ container.appendChild(wrapper);
+
+ const img = doc.createElementNS(XHTML_NS, "img");
+ img.classList.add("devtools-tooltip-image");
+ img.classList.toggle("devtools-tooltip-tiles", !hideCheckeredBackground);
+ img.style.height = imgHeight;
+ img.src = encodeURI(imageUrl);
+ wrapper.appendChild(img);
+
+ if (!hideDimensionLabel) {
+ const dimensions = doc.createElementNS(XHTML_NS, "div");
+ dimensions.classList.add("devtools-tooltip-image-dimensions");
+ container.appendChild(dimensions);
+
+ const label = naturalWidth + " \u00D7 " + naturalHeight;
+ const span = doc.createElementNS(XHTML_NS, "span");
+ span.classList.add("devtools-tooltip-caption");
+ span.textContent = label;
+ dimensions.appendChild(span);
+ }
+
+ tooltip.panel.innerHTML = "";
+ tooltip.panel.appendChild(container);
+
+ // Calculate tooltip dimensions
+ const width = Math.max(CONTAINER_MIN_WIDTH, imgWidth + 2 * IMAGE_PADDING);
+ let height = imgHeight + 2 * IMAGE_PADDING;
+ if (!hideDimensionLabel) {
+ height += parseFloat(LABEL_HEIGHT);
+ }
+
+ tooltip.setContentSize({ width, height });
+}
+
+/*
+ * Set the tooltip content of a provided HTMLTooltip instance to display a
+ * fallback error message when an image preview tooltip can not be displayed.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The tooltip instance on which the image preview content should be set
+ * @param {Document} doc
+ * A document element to create the HTML elements needed for the tooltip
+ */
+function setBrokenImageTooltip(tooltip, doc) {
+ const div = doc.createElementNS(XHTML_NS, "div");
+ div.className = "devtools-tooltip-image-broken";
+ const message = L10N.getStr("previewTooltip.image.brokenImage");
+ div.textContent = message;
+
+ tooltip.panel.innerHTML = "";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: "auto", height: "auto" });
+}
+
+module.exports.getImageDimensions = getImageDimensions;
+module.exports.setImageTooltip = setImageTooltip;
+module.exports.setBrokenImageTooltip = setBrokenImageTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/RulePreviewTooltip.js b/devtools/client/shared/widgets/tooltip/RulePreviewTooltip.js
new file mode 100644
index 0000000000..87e089d604
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/RulePreviewTooltip.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+"use strict";
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/inspector.properties"
+);
+
+/**
+ * Tooltip displayed for when a CSS property is selected/highlighted.
+ * TODO: For now, the tooltip content only shows "No Associated Rule". In Bug 1528288,
+ * we will be implementing content for showing the source CSS rule.
+ */
+class RulePreviewTooltip {
+ constructor(doc) {
+ this.show = this.show.bind(this);
+ this.destroy = this.destroy.bind(this);
+
+ // Initialize tooltip structure.
+ this._tooltip = new HTMLTooltip(doc, {
+ type: "arrow",
+ consumeOutsideClicks: true,
+ useXulWrapper: true,
+ });
+
+ this.container = doc.createElementNS(XHTML_NS, "div");
+ this.container.className = "rule-preview-tooltip-container";
+
+ this.message = doc.createElementNS(XHTML_NS, "span");
+ this.message.className = "rule-preview-tooltip-message";
+ this.message.textContent = L10N.getStr(
+ "rulePreviewTooltip.noAssociatedRule"
+ );
+ this.container.appendChild(this.message);
+
+ // TODO: Implement structure for showing the source CSS rule.
+
+ this._tooltip.panel.innerHTML = "";
+ this._tooltip.panel.appendChild(this.container);
+ this._tooltip.setContentSize({ width: "auto", height: "auto" });
+ }
+
+ /**
+ * Shows the tooltip on a given element.
+ *
+ * @param {Element} element
+ * The target element to show the tooltip with.
+ */
+ show(element) {
+ element.addEventListener("mouseout", () => this._tooltip.hide());
+ this._tooltip.show(element);
+ }
+
+ destroy() {
+ this._tooltip.destroy();
+ this.container = null;
+ this.message = null;
+ }
+}
+
+module.exports = RulePreviewTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
new file mode 100644
index 0000000000..acc71125e8
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js
@@ -0,0 +1,270 @@
+/* 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/. */
+
+"use strict";
+
+const EventEmitter = require("resource://devtools/shared/event-emitter.js");
+const KeyShortcuts = require("resource://devtools/client/shared/key-shortcuts.js");
+const {
+ HTMLTooltip,
+} = require("resource://devtools/client/shared/widgets/tooltip/HTMLTooltip.js");
+
+loader.lazyRequireGetter(
+ this,
+ "KeyCodes",
+ "resource://devtools/client/shared/keycodes.js",
+ true
+);
+
+/**
+ * Base class for all (color, gradient, ...)-swatch based value editors inside
+ * tooltips
+ *
+ * @param {Document} document
+ * The document to attach the SwatchBasedEditorTooltip. This should be the
+ * toolbox document
+ */
+
+class SwatchBasedEditorTooltip {
+ constructor(document) {
+ EventEmitter.decorate(this);
+
+ // This one will consume outside clicks as it makes more sense to let the user
+ // close the tooltip by clicking out
+ // It will also close on <escape> and <enter>
+ this.tooltip = new HTMLTooltip(document, {
+ type: "arrow",
+ consumeOutsideClicks: true,
+ useXulWrapper: true,
+ });
+
+ // By default, swatch-based editor tooltips revert value change on <esc> and
+ // commit value change on <enter>
+ this.shortcuts = new KeyShortcuts({
+ window: this.tooltip.doc.defaultView,
+ });
+ this.shortcuts.on("Escape", event => {
+ if (!this.tooltip.isVisible()) {
+ return;
+ }
+ this.revert();
+ this.hide();
+ event.stopPropagation();
+ event.preventDefault();
+ });
+ this.shortcuts.on("Return", event => {
+ if (!this.tooltip.isVisible()) {
+ return;
+ }
+ this.commit();
+ this.hide();
+ event.stopPropagation();
+ event.preventDefault();
+ });
+
+ // All target swatches are kept in a map, indexed by swatch DOM elements
+ this.swatches = new Map();
+
+ // When a swatch is clicked, and for as long as the tooltip is shown, the
+ // activeSwatch property will hold the reference to the swatch DOM element
+ // that was clicked
+ this.activeSwatch = null;
+
+ this._onSwatchClick = this._onSwatchClick.bind(this);
+ this._onSwatchKeyDown = this._onSwatchKeyDown.bind(this);
+ }
+
+ /**
+ * Reports if the tooltip is currently shown
+ *
+ * @return {Boolean} True if the tooltip is displayed.
+ */
+ isVisible() {
+ return this.tooltip.isVisible();
+ }
+
+ /**
+ * Reports if the tooltip is currently editing the targeted value
+ *
+ * @return {Boolean} True if the tooltip is editing.
+ */
+ isEditing() {
+ return this.isVisible();
+ }
+
+ /**
+ * Show the editor tooltip for the currently active swatch.
+ *
+ * @return {Promise} a promise that resolves once the editor tooltip is displayed, or
+ * immediately if there is no currently active swatch.
+ */
+ show() {
+ if (this.tooltipAnchor) {
+ const onShown = this.tooltip.once("shown");
+
+ this.tooltip.show(this.tooltipAnchor);
+ this.tooltip.once("hidden", () => this.onTooltipHidden());
+
+ return onShown;
+ }
+
+ return Promise.resolve();
+ }
+
+ /**
+ * Can be overridden by subclasses if implementation specific behavior is needed on
+ * tooltip hidden.
+ */
+ onTooltipHidden() {
+ // When the tooltip is closed by clicking outside the panel we want to commit any
+ // changes.
+ if (!this._reverted) {
+ this.commit();
+ }
+ this._reverted = false;
+
+ // Once the tooltip is hidden we need to clean up any remaining objects.
+ this.activeSwatch = null;
+ }
+
+ hide() {
+ if (this.swatchActivatedWithKeyboard) {
+ this.activeSwatch.focus();
+ this.swatchActivatedWithKeyboard = null;
+ }
+
+ this.tooltip.hide();
+ }
+
+ /**
+ * Add a new swatch DOM element to the list of swatch elements this editor
+ * tooltip knows about. That means from now on, clicking on that swatch will
+ * toggle the editor.
+ *
+ * @param {node} swatchEl
+ * The element to add
+ * @param {object} callbacks
+ * Callbacks that will be executed when the editor wants to preview a
+ * value change, or revert a change, or commit a change.
+ * - onShow: will be called when one of the swatch tooltip is shown
+ * - onPreview: will be called when one of the sub-classes calls
+ * preview
+ * - onRevert: will be called when the user ESCapes out of the tooltip
+ * - onCommit: will be called when the user presses ENTER or clicks
+ * outside the tooltip.
+ */
+ addSwatch(swatchEl, callbacks = {}) {
+ if (!callbacks.onShow) {
+ callbacks.onShow = function () {};
+ }
+ if (!callbacks.onPreview) {
+ callbacks.onPreview = function () {};
+ }
+ if (!callbacks.onRevert) {
+ callbacks.onRevert = function () {};
+ }
+ if (!callbacks.onCommit) {
+ callbacks.onCommit = function () {};
+ }
+
+ this.swatches.set(swatchEl, {
+ callbacks,
+ });
+ swatchEl.addEventListener("click", this._onSwatchClick);
+ swatchEl.addEventListener("keydown", this._onSwatchKeyDown);
+ }
+
+ removeSwatch(swatchEl) {
+ if (this.swatches.has(swatchEl)) {
+ if (this.activeSwatch === swatchEl) {
+ this.hide();
+ this.activeSwatch = null;
+ }
+ swatchEl.removeEventListener("click", this._onSwatchClick);
+ swatchEl.removeEventListener("keydown", this._onSwatchKeyDown);
+ this.swatches.delete(swatchEl);
+ }
+ }
+
+ _onSwatchKeyDown(event) {
+ if (
+ event.keyCode === KeyCodes.DOM_VK_RETURN ||
+ event.keyCode === KeyCodes.DOM_VK_SPACE
+ ) {
+ event.preventDefault();
+ event.stopPropagation();
+ this._onSwatchClick(event);
+ }
+ }
+
+ _onSwatchClick(event) {
+ const { shiftKey, clientX, clientY, target } = event;
+
+ // If mouse coordinates are 0, the event listener could have been triggered
+ // by a keybaord
+ this.swatchActivatedWithKeyboard =
+ event.key && clientX === 0 && clientY === 0;
+
+ if (shiftKey) {
+ event.stopPropagation();
+ return;
+ }
+
+ const swatch = this.swatches.get(target);
+
+ if (swatch) {
+ this.activeSwatch = target;
+ this.show();
+ swatch.callbacks.onShow();
+ event.stopPropagation();
+ }
+ }
+
+ /**
+ * Not called by this parent class, needs to be taken care of by sub-classes
+ */
+ preview(value) {
+ if (this.activeSwatch) {
+ const swatch = this.swatches.get(this.activeSwatch);
+ swatch.callbacks.onPreview(value);
+ }
+ }
+
+ /**
+ * This parent class only calls this on <esc> keydown
+ */
+ revert() {
+ if (this.activeSwatch) {
+ this._reverted = true;
+ const swatch = this.swatches.get(this.activeSwatch);
+ this.tooltip.once("hidden", () => {
+ swatch.callbacks.onRevert();
+ });
+ }
+ }
+
+ /**
+ * This parent class only calls this on <enter> keydown
+ */
+ commit() {
+ if (this.activeSwatch) {
+ const swatch = this.swatches.get(this.activeSwatch);
+ swatch.callbacks.onCommit();
+ }
+ }
+
+ get tooltipAnchor() {
+ return this.activeSwatch;
+ }
+
+ destroy() {
+ this.swatches.clear();
+ this.activeSwatch = null;
+ this.tooltip.off("keydown", this._onTooltipKeydown);
+ this.tooltip.destroy();
+ this.shortcuts.destroy();
+ }
+}
+
+module.exports = SwatchBasedEditorTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
new file mode 100644
index 0000000000..6cfceccc0b
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -0,0 +1,363 @@
+/* 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/. */
+
+"use strict";
+
+const { colorUtils } = require("resource://devtools/shared/css/color.js");
+const Spectrum = require("resource://devtools/client/shared/widgets/Spectrum.js");
+const SwatchBasedEditorTooltip = require("resource://devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js");
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/inspector.properties"
+);
+const { openDocLink } = require("resource://devtools/client/shared/link.js");
+const {
+ A11Y_CONTRAST_LEARN_MORE_LINK,
+} = require("resource://devtools/client/accessibility/constants.js");
+loader.lazyRequireGetter(
+ this,
+ "throttle",
+ "resource://devtools/shared/throttle.js",
+ true
+);
+
+loader.lazyRequireGetter(
+ this,
+ ["getFocusableElements", "wrapMoveFocus"],
+ "resource://devtools/client/shared/focus.js",
+ true
+);
+loader.lazyRequireGetter(
+ this,
+ "PICKER_TYPES",
+ "resource://devtools/shared/picker-constants.js"
+);
+
+const TELEMETRY_PICKER_EYEDROPPER_OPEN_COUNT =
+ "DEVTOOLS_PICKER_EYEDROPPER_OPENED_COUNT";
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+/**
+ * The swatch color picker tooltip class is a specific class meant to be used
+ * along with output-parser's generated color swatches.
+ * It extends the parent SwatchBasedEditorTooltip class.
+ * It just wraps a standard Tooltip and sets its content with an instance of a
+ * color picker.
+ *
+ * The activeSwatch element expected by the tooltip must follow some guidelines
+ * to be compatible with this feature:
+ * - the background-color of the activeSwatch should be set to the current
+ * color, it will be updated when the color is changed via the color-picker.
+ * - the `data-color` attribute should be set either on the activeSwatch or on
+ * a parent node, and should also contain the current color.
+ * - finally if the color value should be displayed next to the swatch as text,
+ * the activeSwatch should have a nextSibling. Note that this sibling may
+ * contain more than just text initially, but it will be updated after a color
+ * change and will only contain the text.
+ *
+ * An example of valid markup (with data-color on a parent and a nextSibling):
+ *
+ * <span data-color="#FFF"> <!-- activeSwatch.closest("[data-color]") -->
+ * <span
+ * style="background-color: rgb(255, 255, 255);"
+ * ></span> <!-- activeSwatch -->
+ * <span>#FFF</span> <!-- activeSwatch.nextSibling -->
+ * </span>
+ *
+ * Another example with everything on the activeSwatch itself:
+ *
+ * <span> <!-- container, to illustrate that the swatch has no sibling here. -->
+ * <span
+ * data-color="#FFF"
+ * style="background-color: rgb(255, 255, 255);"
+ * ></span> <!-- activeSwatch & activeSwatch.closest("[data-color]") -->
+ * </span>
+ *
+ * @param {Document} document
+ * The document to attach the SwatchColorPickerTooltip. This is either the toolbox
+ * document if the tooltip is a popup tooltip or the panel's document if it is an
+ * inline editor.
+ * @param {InspectorPanel} inspector
+ * The inspector panel, needed for the eyedropper.
+ */
+
+class SwatchColorPickerTooltip extends SwatchBasedEditorTooltip {
+ constructor(document, inspector) {
+ super(document);
+ this.inspector = inspector;
+
+ // Creating a spectrum instance. this.spectrum will always be a promise that
+ // resolves to the spectrum instance
+ this.spectrum = this.setColorPickerContent([0, 0, 0, 1]);
+ this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this);
+ this._openEyeDropper = this._openEyeDropper.bind(this);
+ this._openDocLink = this._openDocLink.bind(this);
+ this._onTooltipKeydown = this._onTooltipKeydown.bind(this);
+
+ // Selecting color by hovering on the spectrum widget could create a lot
+ // of requests. Throttle by 50ms to avoid this. See Bug 1665547.
+ this._selectColor = throttle(this._selectColor.bind(this), 50);
+
+ this.tooltip.container.addEventListener("keydown", this._onTooltipKeydown);
+ }
+
+ /**
+ * Fill the tooltip with a new instance of the spectrum color picker widget
+ * initialized with the given color, and return the instance of spectrum
+ */
+
+ setColorPickerContent(color) {
+ const { doc } = this.tooltip;
+ this.tooltip.panel.innerHTML = "";
+
+ const container = doc.createElementNS(XHTML_NS, "div");
+ container.id = "spectrum-tooltip";
+
+ const node = doc.createElementNS(XHTML_NS, "div");
+ node.id = "spectrum";
+ container.appendChild(node);
+
+ const widget = new Spectrum(node, color);
+ this.tooltip.panel.appendChild(container);
+ this.tooltip.setContentSize({ width: 215 });
+
+ widget.inspector = this.inspector;
+
+ // Wait for the tooltip to be shown before calling widget.show
+ // as it expect to be visible in order to compute DOM element sizes.
+ this.tooltip.once("shown", () => {
+ widget.show();
+ });
+
+ return widget;
+ }
+
+ /**
+ * Overriding the SwatchBasedEditorTooltip.show function to set spectrum's
+ * color.
+ */
+ async show() {
+ // set contrast enabled for the spectrum
+ const name = this.activeSwatch.dataset.propertyName;
+ const colorFunction = this.activeSwatch.dataset.colorFunction;
+
+ // Only enable contrast if the type of property is color
+ // and its value isn't inside a color-modifying function (e.g. color-mix()).
+ this.spectrum.contrastEnabled =
+ name === "color" && colorFunction !== "color-mix";
+ if (this.spectrum.contrastEnabled) {
+ const { nodeFront } = this.inspector.selection;
+ const { pageStyle } = nodeFront.inspectorFront;
+ this.spectrum.textProps = await pageStyle.getComputed(nodeFront, {
+ filterProperties: ["font-size", "font-weight", "opacity"],
+ });
+ this.spectrum.backgroundColorData = await nodeFront.getBackgroundColor();
+ }
+
+ // Then set spectrum's color and listen to color changes to preview them
+ if (this.activeSwatch) {
+ this._originalColor = this._getSwatchColorContainer().dataset.color;
+ const color = this.activeSwatch.style.backgroundColor;
+
+ this.spectrum.off("changed", this._onSpectrumColorChange);
+ this.spectrum.rgb = this._colorToRgba(color);
+ this.spectrum.on("changed", this._onSpectrumColorChange);
+ this.spectrum.updateUI();
+ }
+
+ // Call then parent class' show function
+ await super.show();
+
+ const eyeButton =
+ this.tooltip.container.querySelector("#eyedropper-button");
+ const canShowEyeDropper = await this.inspector.supportsEyeDropper();
+ if (canShowEyeDropper) {
+ eyeButton.disabled = false;
+ eyeButton.removeAttribute("title");
+ eyeButton.addEventListener("click", this._openEyeDropper);
+ } else {
+ eyeButton.disabled = true;
+ eyeButton.title = L10N.getStr("eyedropper.disabled.title");
+ }
+
+ const learnMoreButton =
+ this.tooltip.container.querySelector("#learn-more-button");
+ if (learnMoreButton) {
+ learnMoreButton.addEventListener("click", this._openDocLink);
+ learnMoreButton.addEventListener("keydown", e => e.stopPropagation());
+ }
+
+ // Add focus to the first focusable element in the tooltip and attach keydown
+ // event listener to tooltip
+ this.focusableElements[0].focus();
+ this.tooltip.container.addEventListener(
+ "keydown",
+ this._onTooltipKeydown,
+ true
+ );
+
+ this.emit("ready");
+ }
+
+ _onTooltipKeydown(event) {
+ const { target, key, shiftKey } = event;
+
+ if (key !== "Tab") {
+ return;
+ }
+
+ const focusMoved = !!wrapMoveFocus(
+ this.focusableElements,
+ target,
+ shiftKey
+ );
+ if (focusMoved) {
+ // Focus was moved to the begining/end of the tooltip, so we need to prevent the
+ // default focus change that would happen here.
+ event.preventDefault();
+ }
+
+ event.stopPropagation();
+ }
+
+ _getSwatchColorContainer() {
+ // Depending on the UI, the data-color attribute might be set on the
+ // swatch itself, or a parent node.
+ // This data attribute is also used for the "Copy color" feature, so it
+ // can be useful to set it on a container rather than on the swatch.
+ return this.activeSwatch.closest("[data-color]");
+ }
+
+ _onSpectrumColorChange(rgba, cssColor) {
+ this._selectColor(cssColor);
+ }
+
+ _selectColor(color) {
+ if (this.activeSwatch) {
+ this.activeSwatch.style.backgroundColor = color;
+
+ color = this._toDefaultType(color);
+
+ this._getSwatchColorContainer().dataset.color = color;
+ if (this.activeSwatch.nextSibling) {
+ this.activeSwatch.nextSibling.textContent = color;
+ }
+ this.preview(color);
+
+ if (this.eyedropperOpen) {
+ this.commit();
+ }
+ }
+ }
+
+ /**
+ * Override the implementation from SwatchBasedEditorTooltip.
+ */
+ onTooltipHidden() {
+ // If the tooltip is hidden while the eyedropper is being used, we should not commit
+ // the changes.
+ if (this.eyedropperOpen) {
+ return;
+ }
+
+ super.onTooltipHidden();
+ this.tooltip.container.removeEventListener(
+ "keydown",
+ this._onTooltipKeydown
+ );
+ }
+
+ _openEyeDropper() {
+ const { inspectorFront, toolbox, telemetry } = this.inspector;
+
+ telemetry
+ .getHistogramById(TELEMETRY_PICKER_EYEDROPPER_OPEN_COUNT)
+ .add(true);
+
+ // cancelling picker(if it is already selected) on opening eye-dropper
+ toolbox.nodePicker.stop({ canceled: true });
+
+ // disable simulating touch events if RDM is active
+ toolbox.tellRDMAboutPickerState(true, PICKER_TYPES.EYEDROPPER);
+
+ // pickColorFromPage will focus the content document. If the devtools are in a
+ // separate window, the colorpicker tooltip will be closed before pickColorFromPage
+ // resolves. Flip the flag early to avoid issues with onTooltipHidden().
+ this.eyedropperOpen = true;
+
+ inspectorFront.pickColorFromPage({ copyOnSelect: false }).then(() => {
+ // close the colorpicker tooltip so that only the eyedropper is open.
+ this.hide();
+
+ this.tooltip.emit("eyedropper-opened");
+ }, console.error);
+
+ inspectorFront.once("color-picked", color => {
+ toolbox.win.focus();
+ this._selectColor(color);
+ this._onEyeDropperDone();
+ });
+
+ inspectorFront.once("color-pick-canceled", () => {
+ this._onEyeDropperDone();
+ });
+ }
+
+ _openDocLink() {
+ openDocLink(A11Y_CONTRAST_LEARN_MORE_LINK);
+ this.hide();
+ }
+
+ _onEyeDropperDone() {
+ // enable simulating touch events if RDM is active
+ this.inspector.toolbox.tellRDMAboutPickerState(
+ false,
+ PICKER_TYPES.EYEDROPPER
+ );
+
+ this.eyedropperOpen = false;
+ this.activeSwatch = null;
+ }
+
+ _colorToRgba(color) {
+ color = new colorUtils.CssColor(color);
+ const rgba = color.getRGBATuple();
+ return [rgba.r, rgba.g, rgba.b, rgba.a];
+ }
+
+ _toDefaultType(color) {
+ let unit = this.inspector.defaultColorUnit;
+ let forceUppercase = false;
+ if (unit === colorUtils.CssColor.COLORUNIT.authored) {
+ unit = colorUtils.classifyColor(this._originalColor);
+ forceUppercase = colorUtils.colorIsUppercase(this._originalColor);
+ }
+
+ const colorObj = new colorUtils.CssColor(color);
+ return colorObj.toString(unit, forceUppercase);
+ }
+
+ /**
+ * Overriding the SwatchBasedEditorTooltip.isEditing function to consider the
+ * eyedropper.
+ */
+ isEditing() {
+ return this.tooltip.isVisible() || this.eyedropperOpen;
+ }
+
+ get focusableElements() {
+ return getFocusableElements(this.tooltip.container).filter(
+ el => !!el.offsetParent
+ );
+ }
+
+ destroy() {
+ super.destroy();
+ this.inspector = null;
+ this.spectrum.off("changed", this._onSpectrumColorChange);
+ this.spectrum.destroy();
+ }
+}
+
+module.exports = SwatchColorPickerTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip.js
new file mode 100644
index 0000000000..bfec4bccea
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip.js
@@ -0,0 +1,95 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ CubicBezierWidget,
+} = require("resource://devtools/client/shared/widgets/CubicBezierWidget.js");
+const SwatchBasedEditorTooltip = require("resource://devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js");
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+/**
+ * The swatch cubic-bezier tooltip class is a specific class meant to be used
+ * along with rule-view's generated cubic-bezier swatches.
+ * It extends the parent SwatchBasedEditorTooltip class.
+ * It just wraps a standard Tooltip and sets its content with an instance of a
+ * CubicBezierWidget.
+ *
+ * @param {Document} document
+ * The document to attach the SwatchCubicBezierTooltip. This is either the toolbox
+ * document if the tooltip is a popup tooltip or the panel's document if it is an
+ * inline editor.
+ */
+
+class SwatchCubicBezierTooltip extends SwatchBasedEditorTooltip {
+ constructor(document) {
+ super(document);
+
+ // Creating a cubic-bezier instance.
+ // this.widget will always be a promise that resolves to the widget instance
+ this.widget = this.setCubicBezierContent([0, 0, 1, 1]);
+ this._onUpdate = this._onUpdate.bind(this);
+ }
+
+ /**
+ * Fill the tooltip with a new instance of the cubic-bezier widget
+ * initialized with the given value, and return a promise that resolves to
+ * the instance of the widget
+ */
+
+ async setCubicBezierContent(bezier) {
+ const { doc } = this.tooltip;
+ this.tooltip.panel.innerHTML = "";
+
+ const container = doc.createElementNS(XHTML_NS, "div");
+ container.className = "cubic-bezier-container";
+
+ this.tooltip.panel.appendChild(container);
+ this.tooltip.setContentSize({ width: 510, height: 370 });
+
+ await this.tooltip.once("shown");
+ return new CubicBezierWidget(container, bezier);
+ }
+
+ /**
+ * Overriding the SwatchBasedEditorTooltip.show function to set the cubic
+ * bezier curve in the widget
+ */
+ async show() {
+ // Call the parent class' show function
+ await super.show();
+ // Then set the curve and listen to changes to preview them
+ if (this.activeSwatch) {
+ this.currentBezierValue = this.activeSwatch.nextSibling;
+ this.widget.then(widget => {
+ widget.off("updated", this._onUpdate);
+ widget.cssCubicBezierValue = this.currentBezierValue.textContent;
+ widget.on("updated", this._onUpdate);
+ this.emit("ready");
+ });
+ }
+ }
+
+ _onUpdate(bezier) {
+ if (!this.activeSwatch) {
+ return;
+ }
+
+ this.currentBezierValue.textContent = bezier + "";
+ this.preview(bezier + "");
+ }
+
+ destroy() {
+ super.destroy();
+ this.currentBezierValue = null;
+ this.widget.then(widget => {
+ widget.off("updated", this._onUpdate);
+ widget.destroy();
+ });
+ }
+}
+
+module.exports = SwatchCubicBezierTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/SwatchFilterTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchFilterTooltip.js
new file mode 100644
index 0000000000..cc28176a13
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/SwatchFilterTooltip.js
@@ -0,0 +1,117 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ CSSFilterEditorWidget,
+} = require("resource://devtools/client/shared/widgets/FilterWidget.js");
+const SwatchBasedEditorTooltip = require("resource://devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip.js");
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+/**
+ * The swatch-based css filter tooltip class is a specific class meant to be
+ * used along with rule-view's generated css filter swatches.
+ * It extends the parent SwatchBasedEditorTooltip class.
+ * It just wraps a standard Tooltip and sets its content with an instance of a
+ * CSSFilterEditorWidget.
+ *
+ * @param {Document} document
+ * The document to attach the SwatchFilterTooltip. This is either the toolbox
+ * document if the tooltip is a popup tooltip or the panel's document if it is an
+ * inline editor.
+ */
+
+class SwatchFilterTooltip extends SwatchBasedEditorTooltip {
+ constructor(document) {
+ super(document);
+
+ // Creating a filter editor instance.
+ this.widget = this.setFilterContent("none");
+ this._onUpdate = this._onUpdate.bind(this);
+ }
+
+ /**
+ * Fill the tooltip with a new instance of the CSSFilterEditorWidget
+ * widget initialized with the given filter value, and return a promise
+ * that resolves to the instance of the widget when ready.
+ */
+
+ setFilterContent(filter) {
+ const { doc } = this.tooltip;
+ this.tooltip.panel.innerHTML = "";
+
+ const container = doc.createElementNS(XHTML_NS, "div");
+ container.id = "filter-container";
+
+ this.tooltip.panel.appendChild(container);
+ this.tooltip.setContentSize({ width: 510, height: 200 });
+
+ return new CSSFilterEditorWidget(container, filter);
+ }
+
+ async show() {
+ // Call the parent class' show function
+ await super.show();
+ // Then set the filter value and listen to changes to preview them
+ if (this.activeSwatch) {
+ this.currentFilterValue = this.activeSwatch.nextSibling;
+ this.widget.off("updated", this._onUpdate);
+ this.widget.on("updated", this._onUpdate);
+ this.widget.setCssValue(this.currentFilterValue.textContent);
+ this.widget.render();
+ this.emit("ready");
+ }
+ }
+
+ _onUpdate(filters) {
+ if (!this.activeSwatch) {
+ return;
+ }
+
+ // Remove the old children and reparse the property value to
+ // recompute them.
+ while (this.currentFilterValue.firstChild) {
+ this.currentFilterValue.firstChild.remove();
+ }
+ const node = this._parser.parseCssProperty(
+ "filter",
+ filters,
+ this._options
+ );
+ this.currentFilterValue.appendChild(node);
+
+ this.preview();
+ }
+
+ destroy() {
+ super.destroy();
+ this.currentFilterValue = null;
+ this.widget.off("updated", this._onUpdate);
+ this.widget.destroy();
+ }
+
+ /**
+ * Like SwatchBasedEditorTooltip.addSwatch, but accepts a parser object
+ * to use when previewing the updated property value.
+ *
+ * @param {node} swatchEl
+ * @see SwatchBasedEditorTooltip.addSwatch
+ * @param {object} callbacks
+ * @see SwatchBasedEditorTooltip.addSwatch
+ * @param {object} parser
+ * A parser object; @see OutputParser object
+ * @param {object} options
+ * options to pass to the output parser, with
+ * the option |filterSwatch| set.
+ */
+ addSwatch(swatchEl, callbacks, parser, options) {
+ super.addSwatch(swatchEl, callbacks);
+ this._parser = parser;
+ this._options = options;
+ }
+}
+
+module.exports = SwatchFilterTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/SwatchLinearEasingFunctionTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchLinearEasingFunctionTooltip.js
new file mode 100644
index 0000000000..371bbd79fc
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/SwatchLinearEasingFunctionTooltip.js
@@ -0,0 +1,97 @@
+/* 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/. */
+
+"use strict";
+
+const {
+ LinearEasingFunctionWidget,
+} = require("devtools/client/shared/widgets/LinearEasingFunctionWidget");
+const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+/**
+ * The swatch linear-easing-function tooltip class is a specific class meant to be used
+ * along with rule-view's generated linear-easing-function swatches.
+ * It extends the parent SwatchBasedEditorTooltip class.
+ * It just wraps a standard Tooltip and sets its content with an instance of a
+ * LinearEasingFunctionWidget.
+ *
+ * @param {Document} document
+ * The document to attach the SwatchLinearEasingFunctionTooltip. This is either the toolbox
+ * document if the tooltip is a popup tooltip or the panel's document if it is an
+ * inline editor.
+ */
+
+class SwatchLinearEasingFunctionTooltip extends SwatchBasedEditorTooltip {
+ constructor(document) {
+ super(document);
+
+ this.onWidgetUpdated = this.onWidgetUpdated.bind(this);
+
+ // Creating a linear-easing-function instance.
+ // this.widget will always be a promise that resolves to the widget instance
+ this.widget = this.createWidget();
+ }
+
+ /**
+ * Fill the tooltip with a new instance of the linear-easing-function widget
+ * initialized with the given value, and return a promise that resolves to
+ * the instance of the widget
+ */
+
+ async createWidget() {
+ const { doc } = this.tooltip;
+ this.tooltip.panel.innerHTML = "";
+
+ const container = doc.createElementNS(XHTML_NS, "div");
+ container.className = "linear-easing-function-container";
+
+ this.tooltip.panel.appendChild(container);
+ this.tooltip.setContentSize({ width: 400, height: 400 });
+
+ await this.tooltip.once("shown");
+ return new LinearEasingFunctionWidget(container);
+ }
+
+ /**
+ * Overriding the SwatchBasedEditorTooltip.show function to set the linear function line
+ * in the widget
+ */
+ async show() {
+ // Call the parent class' show function
+ await super.show();
+ // Then set the line and listen to changes to preview them
+ if (this.activeSwatch) {
+ this.ruleViewCurrentLinearValueElement = this.activeSwatch.nextSibling;
+ this.widget.then(widget => {
+ widget.off("updated", this.onWidgetUpdated);
+ widget.setCssLinearValue(this.activeSwatch.getAttribute("data-linear"));
+ widget.on("updated", this.onWidgetUpdated);
+ this.emit("ready");
+ });
+ }
+ }
+
+ onWidgetUpdated(newValue) {
+ if (!this.activeSwatch) {
+ return;
+ }
+
+ this.ruleViewCurrentLinearValueElement.textContent = newValue;
+ this.activeSwatch.setAttribute("data-linear", newValue);
+ this.preview(newValue);
+ }
+
+ destroy() {
+ super.destroy();
+ this.currentFunctionText = null;
+ this.widget.then(widget => {
+ widget.off("updated", this.onWidgetUpdated);
+ widget.destroy();
+ });
+ }
+}
+
+module.exports = SwatchLinearEasingFunctionTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/TooltipToggle.js b/devtools/client/shared/widgets/tooltip/TooltipToggle.js
new file mode 100644
index 0000000000..9458c9382d
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/TooltipToggle.js
@@ -0,0 +1,197 @@
+/* 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/. */
+
+"use strict";
+
+const DEFAULT_TOGGLE_DELAY = 50;
+
+/**
+ * Tooltip helper designed to show/hide the tooltip when the mouse hovers over
+ * particular nodes.
+ *
+ * This works by tracking mouse movements on a base container node (baseNode)
+ * and showing the tooltip when the mouse stops moving. A callback can be
+ * provided to the start() method to know whether or not the node being
+ * hovered over should indeed receive the tooltip.
+ */
+function TooltipToggle(tooltip) {
+ this.tooltip = tooltip;
+ this.win = tooltip.doc.defaultView;
+
+ this._onMouseMove = this._onMouseMove.bind(this);
+ this._onMouseOut = this._onMouseOut.bind(this);
+
+ this._onTooltipMouseOver = this._onTooltipMouseOver.bind(this);
+ this._onTooltipMouseOut = this._onTooltipMouseOut.bind(this);
+}
+
+module.exports.TooltipToggle = TooltipToggle;
+
+TooltipToggle.prototype = {
+ /**
+ * Start tracking mouse movements on the provided baseNode to show the
+ * tooltip.
+ *
+ * 2 Ways to make this work:
+ * - Provide a single node to attach the tooltip to, as the baseNode, and
+ * omit the second targetNodeCb argument
+ * - Provide a baseNode that is the container of possibly numerous children
+ * elements that may receive a tooltip. In this case, provide the second
+ * targetNodeCb argument to decide wether or not a child should receive
+ * a tooltip.
+ *
+ * Note that if you call this function a second time, it will itself call
+ * stop() before adding mouse tracking listeners again.
+ *
+ * @param {node} baseNode
+ * The container for all target nodes
+ * @param {Function} targetNodeCb
+ * A function that accepts a node argument and that checks if a tooltip
+ * should be displayed. Possible return values are:
+ * - false (or a falsy value) if the tooltip should not be displayed
+ * - true if the tooltip should be displayed
+ * - a DOM node to display the tooltip on the returned anchor
+ * The function can also return a promise that will resolve to one of
+ * the values listed above.
+ * If omitted, the tooltip will be shown everytime.
+ * @param {Object} options
+ Set of optional arguments:
+ * - {Number} toggleDelay
+ * An optional delay (in ms) that will be observed before showing
+ * and before hiding the tooltip. Defaults to DEFAULT_TOGGLE_DELAY.
+ * - {Boolean} interactive
+ * If enabled, the tooltip is not hidden when mouse leaves the
+ * target element and enters the tooltip. Allows the tooltip
+ * content to be interactive.
+ */
+ start(
+ baseNode,
+ targetNodeCb,
+ { toggleDelay = DEFAULT_TOGGLE_DELAY, interactive = false } = {}
+ ) {
+ this.stop();
+
+ if (!baseNode) {
+ // Calling tool is in the process of being destroyed.
+ return;
+ }
+
+ this._baseNode = baseNode;
+ this._targetNodeCb = targetNodeCb || (() => true);
+ this._toggleDelay = toggleDelay;
+ this._interactive = interactive;
+
+ baseNode.addEventListener("mousemove", this._onMouseMove);
+ baseNode.addEventListener("mouseout", this._onMouseOut);
+
+ const target = this.tooltip.xulPanelWrapper || this.tooltip.container;
+ if (this._interactive) {
+ target.addEventListener("mouseover", this._onTooltipMouseOver);
+ target.addEventListener("mouseout", this._onTooltipMouseOut);
+ } else {
+ target.classList.add("non-interactive-toggle");
+ }
+ },
+
+ /**
+ * If the start() function has been used previously, and you want to get rid
+ * of this behavior, then call this function to remove the mouse movement
+ * tracking
+ */
+ stop() {
+ this.win.clearTimeout(this.toggleTimer);
+
+ if (!this._baseNode) {
+ return;
+ }
+
+ this._baseNode.removeEventListener("mousemove", this._onMouseMove);
+ this._baseNode.removeEventListener("mouseout", this._onMouseOut);
+
+ const target = this.tooltip.xulPanelWrapper || this.tooltip.container;
+ if (this._interactive) {
+ target.removeEventListener("mouseover", this._onTooltipMouseOver);
+ target.removeEventListener("mouseout", this._onTooltipMouseOut);
+ } else {
+ target.classList.remove("non-interactive-toggle");
+ }
+
+ this._baseNode = null;
+ this._targetNodeCb = null;
+ this._lastHovered = null;
+ },
+
+ _onMouseMove(event) {
+ if (event.target !== this._lastHovered) {
+ this._lastHovered = event.target;
+
+ this.win.clearTimeout(this.toggleTimer);
+ this.toggleTimer = this.win.setTimeout(() => {
+ this.tooltip.hide();
+ this.isValidHoverTarget(event.target).then(
+ target => {
+ if (target === null || !this._baseNode) {
+ // bail out if no target or if the toggle has been destroyed.
+ return;
+ }
+ this.tooltip.show(target);
+ },
+ reason => {
+ console.error(
+ "isValidHoverTarget rejected with unexpected reason:"
+ );
+ console.error(reason);
+ }
+ );
+ }, this._toggleDelay);
+ }
+ },
+
+ /**
+ * Is the given target DOMNode a valid node for toggling the tooltip on hover.
+ * This delegates to the user-defined _targetNodeCb callback.
+ * @return {Promise} a promise that will resolve the anchor to use for the
+ * tooltip or null if no valid target was found.
+ */
+ async isValidHoverTarget(target) {
+ const res = await this._targetNodeCb(target, this.tooltip);
+ if (res) {
+ return res.nodeName ? res : target;
+ }
+
+ return null;
+ },
+
+ _onMouseOut(event) {
+ // Only hide the tooltip if the mouse leaves baseNode.
+ if (
+ event &&
+ this._baseNode &&
+ this._baseNode.contains(event.relatedTarget)
+ ) {
+ return;
+ }
+
+ this._lastHovered = null;
+ this.win.clearTimeout(this.toggleTimer);
+ this.toggleTimer = this.win.setTimeout(() => {
+ this.tooltip.hide();
+ }, this._toggleDelay);
+ },
+
+ _onTooltipMouseOver() {
+ this.win.clearTimeout(this.toggleTimer);
+ },
+
+ _onTooltipMouseOut() {
+ this.win.clearTimeout(this.toggleTimer);
+ this.toggleTimer = this.win.setTimeout(() => {
+ this.tooltip.hide();
+ }, this._toggleDelay);
+ },
+
+ destroy() {
+ this.stop();
+ },
+};
diff --git a/devtools/client/shared/widgets/tooltip/VariableTooltipHelper.js b/devtools/client/shared/widgets/tooltip/VariableTooltipHelper.js
new file mode 100644
index 0000000000..bd458fbbf1
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/VariableTooltipHelper.js
@@ -0,0 +1,31 @@
+/* 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/. */
+
+"use strict";
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+/**
+ * Set the tooltip content of a provided HTMLTooltip instance to display a
+ * variable preview matching the provided text.
+ *
+ * @param {HTMLTooltip} tooltip
+ * The tooltip instance on which the text preview content should be set.
+ * @param {Document} doc
+ * A document element to create the HTML elements needed for the tooltip.
+ * @param {String} text
+ * Text to display in tooltip.
+ */
+function setVariableTooltip(tooltip, doc, text) {
+ // Create tooltip content
+ const div = doc.createElementNS(XHTML_NS, "div");
+ div.classList.add("devtools-monospace", "devtools-tooltip-css-variable");
+ div.textContent = text;
+
+ tooltip.panel.innerHTML = "";
+ tooltip.panel.appendChild(div);
+ tooltip.setContentSize({ width: "auto", height: "auto" });
+}
+
+module.exports.setVariableTooltip = setVariableTooltip;
diff --git a/devtools/client/shared/widgets/tooltip/css-compatibility-tooltip-helper.js b/devtools/client/shared/widgets/tooltip/css-compatibility-tooltip-helper.js
new file mode 100644
index 0000000000..40755a212b
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/css-compatibility-tooltip-helper.js
@@ -0,0 +1,292 @@
+/* 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/. */
+
+"use strict";
+
+const { BrowserLoader } = ChromeUtils.import(
+ "resource://devtools/shared/loader/browser-loader.js"
+);
+
+loader.lazyRequireGetter(
+ this,
+ "openDocLink",
+ "resource://devtools/client/shared/link.js",
+ true
+);
+
+class CssCompatibilityTooltipHelper {
+ constructor() {
+ this.addTab = this.addTab.bind(this);
+ }
+
+ #currentTooltip = null;
+ #currentUrl = null;
+
+ #createElement(doc, tag, classList = [], attributeList = {}) {
+ const XHTML_NS = "http://www.w3.org/1999/xhtml";
+ const newElement = doc.createElementNS(XHTML_NS, tag);
+ for (const elementClass of classList) {
+ newElement.classList.add(elementClass);
+ }
+
+ for (const key in attributeList) {
+ newElement.setAttribute(key, attributeList[key]);
+ }
+
+ return newElement;
+ }
+
+ /*
+ * Attach the UnsupportedBrowserList component to the
+ * ".compatibility-browser-list-wrapper" div to render the
+ * unsupported browser list
+ */
+ #renderUnsupportedBrowserList(container, unsupportedBrowsers) {
+ // Mount the ReactDOM only if the unsupported browser
+ // list is not empty. Else "compatibility-browser-list-wrapper"
+ // is not defined. For example, for property clip,
+ // unsupportedBrowsers is an empty array
+ if (!unsupportedBrowsers.length) {
+ return;
+ }
+
+ const { require } = BrowserLoader({
+ baseURI: "resource://devtools/client/shared/widgets/tooltip/",
+ window: this.#currentTooltip.doc.defaultView,
+ });
+ const {
+ createFactory,
+ createElement,
+ } = require("resource://devtools/client/shared/vendor/react.js");
+ const ReactDOM = require("resource://devtools/client/shared/vendor/react-dom.js");
+ const UnsupportedBrowserList = createFactory(
+ require("resource://devtools/client/inspector/compatibility/components/UnsupportedBrowserList.js")
+ );
+
+ const unsupportedBrowserList = createElement(UnsupportedBrowserList, {
+ browsers: unsupportedBrowsers,
+ });
+ ReactDOM.render(
+ unsupportedBrowserList,
+ container.querySelector(".compatibility-browser-list-wrapper")
+ );
+ }
+
+ /*
+ * Get the first paragraph for the compatibility tooltip
+ * Return a subtree similar to:
+ * <p data-l10n-id="css-compatibility-default-message"
+ * data-l10n-args="{&quot;property&quot;:&quot;user-select&quot;}">
+ * </p>
+ */
+ #getCompatibilityMessage(doc, data) {
+ const { msgId, property } = data;
+ return this.#createElement(doc, "p", [], {
+ "data-l10n-id": msgId,
+ "data-l10n-args": JSON.stringify({ property }),
+ });
+ }
+
+ /**
+ * Gets the paragraph elements related to the browserList.
+ * This returns an array with following subtree:
+ * [
+ * <p data-l10n-id="css-compatibility-browser-list-message"></p>,
+ * <p>
+ * <ul class="compatibility-unsupported-browser-list">
+ * <list-element />
+ * </ul>
+ * </p>
+ * ]
+ * The first element is the message and the second element is the
+ * unsupported browserList itself.
+ * If the unsupportedBrowser is an empty array, we return an empty
+ * array back.
+ */
+ #getBrowserListContainer(doc, unsupportedBrowsers) {
+ if (!unsupportedBrowsers.length) {
+ return null;
+ }
+
+ const browserList = this.#createElement(doc, "p");
+ const browserListWrapper = this.#createElement(doc, "div", [
+ "compatibility-browser-list-wrapper",
+ ]);
+ browserList.appendChild(browserListWrapper);
+
+ return browserList;
+ }
+
+ /*
+ * This is the learn more message element linking to the MDN documentation
+ * for the particular incompatible CSS declaration.
+ * The element returned is:
+ * <p data-l10n-id="css-compatibility-learn-more-message"
+ * data-l10n-args="{&quot;property&quot;:&quot;user-select&quot;}">
+ * <span data-l10n-name="link" class="link"></span>
+ * </p>
+ */
+ #getLearnMoreMessage(doc, { rootProperty }) {
+ const learnMoreMessage = this.#createElement(doc, "p", [], {
+ "data-l10n-id": "css-compatibility-learn-more-message",
+ "data-l10n-args": JSON.stringify({ rootProperty }),
+ });
+ learnMoreMessage.appendChild(
+ this.#createElement(doc, "span", ["link"], {
+ "data-l10n-name": "link",
+ })
+ );
+
+ return learnMoreMessage;
+ }
+
+ /**
+ * Fill the tooltip with inactive CSS information.
+ *
+ * @param {Object} data
+ * An object in the following format: {
+ * // Type of compatibility issue
+ * type: <string>,
+ * // The CSS declaration that has compatibility issues
+ * // The raw CSS declaration name that has compatibility issues
+ * declaration: <string>,
+ * property: <string>,
+ * // Alias to the given CSS property
+ * alias: <Array>,
+ * // Link to MDN documentation for the particular CSS rule
+ * url: <string>,
+ * deprecated: <boolean>,
+ * experimental: <boolean>,
+ * // An array of all the browsers that don't support the given CSS rule
+ * unsupportedBrowsers: <Array>,
+ * }
+ * @param {HTMLTooltip} tooltip
+ * The tooltip we are targetting.
+ */
+ async setContent(data, tooltip) {
+ const fragment = this.getTemplate(data, tooltip);
+ const { doc } = tooltip;
+
+ tooltip.panel.innerHTML = "";
+
+ tooltip.panel.addEventListener("click", this.addTab);
+ tooltip.once("hidden", () => {
+ tooltip.panel.removeEventListener("click", this.addTab);
+ });
+
+ // Because Fluent is async we need to manually translate the fragment and
+ // then insert it into the tooltip. This is needed in order for the tooltip
+ // to size to the contents properly and for tests.
+ await doc.l10n.translateFragment(fragment);
+ doc.l10n.pauseObserving();
+ tooltip.panel.appendChild(fragment);
+ doc.l10n.resumeObserving();
+
+ // Size the content.
+ tooltip.setContentSize({ width: 267, height: Infinity });
+ }
+
+ /**
+ * Get the template that the Fluent string will be merged with. This template
+ * looks like this:
+ *
+ * <div class="devtools-tooltip-css-compatibility">
+ * <p data-l10n-id="css-compatibility-default-message"
+ * data-l10n-args="{&quot;property&quot;:&quot;user-select&quot;}">
+ * <strong></strong>
+ * </p>
+ * <browser-list />
+ * <p data-l10n-id="css-compatibility-learn-more-message"
+ * data-l10n-args="{&quot;property&quot;:&quot;user-select&quot;}">
+ * <span data-l10n-name="link" class="link"></span>
+ * <strong></strong>
+ * </p>
+ * </div>
+ *
+ * @param {Object} data
+ * An object in the following format: {
+ * // Type of compatibility issue
+ * type: <string>,
+ * // The CSS declaration that has compatibility issues
+ * // The raw CSS declaration name that has compatibility issues
+ * declaration: <string>,
+ * property: <string>,
+ * // Alias to the given CSS property
+ * alias: <Array>,
+ * // Link to MDN documentation for the particular CSS rule
+ * url: <string>,
+ * // Link to the spec for the particular CSS rule
+ * specUrl: <string>,
+ * deprecated: <boolean>,
+ * experimental: <boolean>,
+ * // An array of all the browsers that don't support the given CSS rule
+ * unsupportedBrowsers: <Array>,
+ * }
+ * @param {HTMLTooltip} tooltip
+ * The tooltip we are targetting.
+ */
+ getTemplate(data, tooltip) {
+ const { doc } = tooltip;
+ const { specUrl, url, unsupportedBrowsers } = data;
+
+ this.#currentTooltip = tooltip;
+ this.#currentUrl = url
+ ? `${url}?utm_source=devtools&utm_medium=inspector-css-compatibility&utm_campaign=default`
+ : specUrl;
+ const templateNode = this.#createElement(doc, "template");
+
+ const tooltipContainer = this.#createElement(doc, "div", [
+ "devtools-tooltip-css-compatibility",
+ ]);
+
+ tooltipContainer.appendChild(this.#getCompatibilityMessage(doc, data));
+ const browserListContainer = this.#getBrowserListContainer(
+ doc,
+ unsupportedBrowsers
+ );
+ if (browserListContainer) {
+ tooltipContainer.appendChild(browserListContainer);
+ this.#renderUnsupportedBrowserList(tooltipContainer, unsupportedBrowsers);
+ }
+
+ if (this.#currentUrl) {
+ tooltipContainer.appendChild(this.#getLearnMoreMessage(doc, data));
+ }
+
+ templateNode.content.appendChild(tooltipContainer);
+ return doc.importNode(templateNode.content, true);
+ }
+
+ /**
+ * Hide the tooltip, open `this.#currentUrl` in a new tab and focus it.
+ *
+ * @param {DOMEvent} event
+ * The click event originating from the tooltip.
+ *
+ */
+ addTab(event) {
+ // The XUL panel swallows click events so handlers can't be added directly
+ // to the link span. As a workaround we listen to all click events in the
+ // panel and if a link span is clicked we proceed.
+ if (event.target.className !== "link") {
+ return;
+ }
+
+ const tooltip = this.#currentTooltip;
+ tooltip.hide();
+
+ const isMacOS = Services.appinfo.OS === "Darwin";
+ openDocLink(this.#currentUrl, {
+ relatedToCurrent: true,
+ inBackground: isMacOS ? event.metaKey : event.ctrlKey,
+ });
+ }
+
+ destroy() {
+ this.#currentTooltip = null;
+ this.#currentUrl = null;
+ }
+}
+
+module.exports = CssCompatibilityTooltipHelper;
diff --git a/devtools/client/shared/widgets/tooltip/css-query-container-tooltip-helper.js b/devtools/client/shared/widgets/tooltip/css-query-container-tooltip-helper.js
new file mode 100644
index 0000000000..633e57b9cf
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/css-query-container-tooltip-helper.js
@@ -0,0 +1,145 @@
+/* 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/. */
+
+"use strict";
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+class CssQueryContainerTooltipHelper {
+ /**
+ * Fill the tooltip with container information.
+ */
+ async setContent(data, tooltip) {
+ const res = await data.rule.domRule.getQueryContainerForNode(
+ data.ancestorIndex,
+ data.rule.inherited ||
+ data.rule.elementStyle.ruleView.inspector.selection.nodeFront
+ );
+
+ const fragment = this.#getTemplate(res, tooltip);
+ tooltip.panel.innerHTML = "";
+ tooltip.panel.appendChild(fragment);
+
+ // Size the content.
+ tooltip.setContentSize({ width: 267, height: Infinity });
+ }
+
+ /**
+ * Get the template of the tooltip.
+ *
+ * @param {Object} data
+ * @param {NodeFront} data.node
+ * @param {string} data.containerType
+ * @param {string} data.inlineSize
+ * @param {string} data.blockSize
+ * @param {HTMLTooltip} tooltip
+ * The tooltip we are targetting.
+ */
+ #getTemplate(data, tooltip) {
+ const { doc } = tooltip;
+
+ const templateNode = doc.createElementNS(XHTML_NS, "template");
+
+ const tooltipContainer = doc.createElementNS(XHTML_NS, "div");
+ tooltipContainer.classList.add("devtools-tooltip-query-container");
+ templateNode.content.appendChild(tooltipContainer);
+
+ const nodeContainer = doc.createElementNS(XHTML_NS, "header");
+ tooltipContainer.append(nodeContainer);
+
+ const containerQueryLabel = doc.createElementNS(XHTML_NS, "span");
+ containerQueryLabel.classList.add("property-name");
+ containerQueryLabel.appendChild(doc.createTextNode(`query container`));
+
+ const nodeEl = doc.createElementNS(XHTML_NS, "span");
+ nodeEl.classList.add("objectBox-node");
+ nodeContainer.append(doc.createTextNode("<"), nodeEl);
+
+ const nodeNameEl = doc.createElementNS(XHTML_NS, "span");
+ nodeNameEl.classList.add("tag-name");
+ nodeNameEl.appendChild(
+ doc.createTextNode(data.node.nodeName.toLowerCase())
+ );
+
+ nodeEl.appendChild(nodeNameEl);
+
+ if (data.node.id) {
+ const idEl = doc.createElementNS(XHTML_NS, "span");
+ idEl.classList.add("attribute-name");
+ idEl.appendChild(doc.createTextNode(`#${data.node.id}`));
+ nodeEl.appendChild(idEl);
+ }
+
+ for (const attr of data.node.attributes) {
+ if (attr.name !== "class") {
+ continue;
+ }
+ for (const cls of attr.value.split(/\s/)) {
+ const el = doc.createElementNS(XHTML_NS, "span");
+ el.classList.add("attribute-name");
+ el.appendChild(doc.createTextNode(`.${cls}`));
+ nodeEl.appendChild(el);
+ }
+ }
+ nodeContainer.append(doc.createTextNode(">"));
+
+ const ul = doc.createElementNS(XHTML_NS, "ul");
+ tooltipContainer.appendChild(ul);
+
+ const containerTypeEl = doc.createElementNS(XHTML_NS, "li");
+ const containerTypeLabel = doc.createElementNS(XHTML_NS, "span");
+ containerTypeLabel.classList.add("property-name");
+ containerTypeLabel.appendChild(doc.createTextNode(`container-type`));
+
+ const containerTypeValue = doc.createElementNS(XHTML_NS, "span");
+ containerTypeValue.classList.add("property-value");
+ containerTypeValue.appendChild(doc.createTextNode(data.containerType));
+
+ containerTypeEl.append(
+ containerTypeLabel,
+ doc.createTextNode(": "),
+ containerTypeValue
+ );
+ ul.appendChild(containerTypeEl);
+
+ const inlineSizeEl = doc.createElementNS(XHTML_NS, "li");
+
+ const inlineSizeLabel = doc.createElementNS(XHTML_NS, "span");
+ inlineSizeLabel.classList.add("property-name");
+ inlineSizeLabel.appendChild(doc.createTextNode(`inline-size`));
+
+ const inlineSizeValue = doc.createElementNS(XHTML_NS, "span");
+ inlineSizeValue.classList.add("property-value");
+ inlineSizeValue.appendChild(doc.createTextNode(data.inlineSize));
+
+ inlineSizeEl.append(
+ inlineSizeLabel,
+ doc.createTextNode(": "),
+ inlineSizeValue
+ );
+ ul.appendChild(inlineSizeEl);
+
+ if (data.containerType != "inline-size") {
+ const blockSizeEl = doc.createElementNS(XHTML_NS, "li");
+ const blockSizeLabel = doc.createElementNS(XHTML_NS, "span");
+ blockSizeLabel.classList.add("property-name");
+ blockSizeLabel.appendChild(doc.createTextNode(`block-size`));
+
+ const blockSizeValue = doc.createElementNS(XHTML_NS, "span");
+ blockSizeValue.classList.add("property-value");
+ blockSizeValue.appendChild(doc.createTextNode(data.blockSize));
+
+ blockSizeEl.append(
+ blockSizeLabel,
+ doc.createTextNode(": "),
+ blockSizeValue
+ );
+ ul.appendChild(blockSizeEl);
+ }
+
+ return doc.importNode(templateNode.content, true);
+ }
+}
+
+module.exports = CssQueryContainerTooltipHelper;
diff --git a/devtools/client/shared/widgets/tooltip/css-selector-warnings-tooltip-helper.js b/devtools/client/shared/widgets/tooltip/css-selector-warnings-tooltip-helper.js
new file mode 100644
index 0000000000..9150a2e6a9
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/css-selector-warnings-tooltip-helper.js
@@ -0,0 +1,64 @@
+/* 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/. */
+
+"use strict";
+
+const XHTML_NS = "http://www.w3.org/1999/xhtml";
+
+const SELECTOR_WARNINGS = {
+ UnconstrainedHas: {
+ l10nId: "css-selector-warning-unconstrained-has",
+ // There could be a specific section on the MDN :has page for this: https://github.com/mdn/mdn/issues/469
+ learnMoreUrl: null,
+ },
+};
+
+class CssSelectorWarningsTooltipHelper {
+ /**
+ * Fill the tooltip with selector warnings.
+ */
+ async setContent(data, tooltip) {
+ const fragment = this.#getTemplate(data, tooltip);
+ tooltip.panel.innerHTML = "";
+ tooltip.panel.append(fragment);
+
+ // Size the content.
+ tooltip.setContentSize({ width: 267, height: Infinity });
+ }
+
+ /**
+ * Get the template of the tooltip.
+ *
+ * @param {Array<String>} data: Array of selector warning kind returned by
+ * CSSRule#getSelectorWarnings
+ * @param {HTMLTooltip} tooltip
+ * The tooltip we are targetting.
+ */
+ #getTemplate(data, tooltip) {
+ const { doc } = tooltip;
+
+ const templateNode = doc.createElementNS(XHTML_NS, "template");
+
+ const tooltipContainer = doc.createElementNS(XHTML_NS, "ul");
+ tooltipContainer.classList.add("devtools-tooltip-selector-warnings");
+ templateNode.content.appendChild(tooltipContainer);
+
+ for (const selectorWarningKind of data) {
+ if (!SELECTOR_WARNINGS[selectorWarningKind]) {
+ console.error("Unknown selector warning kind", data);
+ continue;
+ }
+
+ const { l10nId } = SELECTOR_WARNINGS[selectorWarningKind];
+
+ const li = doc.createElementNS(XHTML_NS, "li");
+ li.setAttribute("data-l10n-id", l10nId);
+ tooltipContainer.append(li);
+ }
+
+ return doc.importNode(templateNode.content, true);
+ }
+}
+
+module.exports = CssSelectorWarningsTooltipHelper;
diff --git a/devtools/client/shared/widgets/tooltip/inactive-css-tooltip-helper.js b/devtools/client/shared/widgets/tooltip/inactive-css-tooltip-helper.js
new file mode 100644
index 0000000000..0c19df3e2e
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/inactive-css-tooltip-helper.js
@@ -0,0 +1,131 @@
+/* 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/. */
+
+"use strict";
+
+loader.lazyRequireGetter(
+ this,
+ "openDocLink",
+ "resource://devtools/client/shared/link.js",
+ true
+);
+
+class InactiveCssTooltipHelper {
+ constructor() {
+ this.addTab = this.addTab.bind(this);
+ }
+
+ /**
+ * Fill the tooltip with inactive CSS information.
+ *
+ * @param {String} propertyName
+ * The property name to be displayed in bold.
+ * @param {String} text
+ * The main text, which follows property name.
+ */
+ async setContent(data, tooltip) {
+ const fragment = this.getTemplate(data, tooltip);
+ const { doc } = tooltip;
+
+ tooltip.panel.innerHTML = "";
+
+ tooltip.panel.addEventListener("click", this.addTab);
+ tooltip.once("hidden", () => {
+ tooltip.panel.removeEventListener("click", this.addTab);
+ });
+
+ // Because Fluent is async we need to manually translate the fragment and
+ // then insert it into the tooltip. This is needed in order for the tooltip
+ // to size to the contents properly and for tests.
+ await doc.l10n.translateFragment(fragment);
+ doc.l10n.pauseObserving();
+ tooltip.panel.appendChild(fragment);
+ doc.l10n.resumeObserving();
+
+ // Size the content.
+ tooltip.setContentSize({ width: 267, height: Infinity });
+ }
+
+ /**
+ * Get the template that the Fluent string will be merged with. This template
+ * looks something like this but there is a variable amount of properties in the
+ * fix section:
+ *
+ * <div class="devtools-tooltip-inactive-css">
+ * <p data-l10n-id="inactive-css-not-grid-or-flex-container"
+ * data-l10n-args="{&quot;property&quot;:&quot;align-content&quot;}">
+ * </p>
+ * <p data-l10n-id="inactive-css-not-grid-or-flex-container-fix">
+ * <span data-l10n-name="link" class="link"></span>
+ * </p>
+ * </div>
+ *
+ * @param {Object} data
+ * An object in the following format: {
+ * fixId: "inactive-css-not-grid-item-fix-2", // Fluent id containing the
+ * // Inactive CSS fix.
+ * msgId: "inactive-css-not-grid-item", // Fluent id containing the
+ * // Inactive CSS message.
+ * property: "color", // Property name
+ * }
+ * @param {HTMLTooltip} tooltip
+ * The tooltip we are targetting.
+ */
+ getTemplate(data, tooltip) {
+ const XHTML_NS = "http://www.w3.org/1999/xhtml";
+ const { fixId, msgId, property, display, lineCount, learnMoreURL } = data;
+ const { doc } = tooltip;
+
+ const documentUrl = new URL(
+ learnMoreURL || `https://developer.mozilla.org/docs/Web/CSS/${property}`
+ );
+ this._currentTooltip = tooltip;
+ const { searchParams } = documentUrl;
+ searchParams.append("utm_source", "devtools");
+ searchParams.append("utm_medium", "inspector-inactive-css");
+ this._currentUrl = documentUrl.toString();
+
+ const templateNode = doc.createElementNS(XHTML_NS, "template");
+
+ // eslint-disable-next-line
+ templateNode.innerHTML = `
+ <div class="devtools-tooltip-inactive-css">
+ <p data-l10n-id="${msgId}"
+ data-l10n-args='${JSON.stringify({ property, display, lineCount })}'>
+ </p>
+ <p data-l10n-id="${fixId}">
+ <span data-l10n-name="link" class="link"></span>
+ </p>
+ </div>`;
+
+ return doc.importNode(templateNode.content, true);
+ }
+
+ /**
+ * Hide the tooltip, open `this._currentUrl` in a new tab and focus it.
+ *
+ * @param {DOMEvent} event
+ * The click event originating from the tooltip.
+ *
+ */
+ addTab(event) {
+ // The XUL panel swallows click events so handlers can't be added directly
+ // to the link span. As a workaround we listen to all click events in the
+ // panel and if a link span is clicked we proceed.
+ if (event.target.className !== "link") {
+ return;
+ }
+
+ const tooltip = this._currentTooltip;
+ tooltip.hide();
+ openDocLink(this._currentUrl);
+ }
+
+ destroy() {
+ this._currentTooltip = null;
+ this._currentUrl = null;
+ }
+}
+
+module.exports = InactiveCssTooltipHelper;
diff --git a/devtools/client/shared/widgets/tooltip/moz.build b/devtools/client/shared/widgets/tooltip/moz.build
new file mode 100644
index 0000000000..57d873c8b5
--- /dev/null
+++ b/devtools/client/shared/widgets/tooltip/moz.build
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+DevToolsModules(
+ "css-compatibility-tooltip-helper.js",
+ "css-query-container-tooltip-helper.js",
+ "css-selector-warnings-tooltip-helper.js",
+ "EventTooltipHelper.js",
+ "HTMLTooltip.js",
+ "ImageTooltipHelper.js",
+ "inactive-css-tooltip-helper.js",
+ "RulePreviewTooltip.js",
+ "SwatchBasedEditorTooltip.js",
+ "SwatchColorPickerTooltip.js",
+ "SwatchCubicBezierTooltip.js",
+ "SwatchFilterTooltip.js",
+ "SwatchLinearEasingFunctionTooltip.js",
+ "TooltipToggle.js",
+ "VariableTooltipHelper.js",
+)
diff --git a/devtools/client/shared/widgets/view-helpers.js b/devtools/client/shared/widgets/view-helpers.js
new file mode 100644
index 0000000000..589d9c6299
--- /dev/null
+++ b/devtools/client/shared/widgets/view-helpers.js
@@ -0,0 +1,430 @@
+/* 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/. */
+"use strict";
+
+const { KeyCodes } = require("resource://devtools/client/shared/keycodes.js");
+
+const PANE_APPEARANCE_DELAY = 50;
+
+var namedTimeoutsStore = new Map();
+
+/**
+ * Helper for draining a rapid succession of events and invoking a callback
+ * once everything settles down.
+ *
+ * @param string id
+ * A string identifier for the named timeout.
+ * @param number wait
+ * The amount of milliseconds to wait after no more events are fired.
+ * @param function callback
+ * Invoked when no more events are fired after the specified time.
+ */
+const setNamedTimeout = function setNamedTimeout(id, wait, callback) {
+ clearNamedTimeout(id);
+
+ namedTimeoutsStore.set(
+ id,
+ setTimeout(() => namedTimeoutsStore.delete(id) && callback(), wait)
+ );
+};
+exports.setNamedTimeout = setNamedTimeout;
+
+/**
+ * Clears a named timeout.
+ * @see setNamedTimeout
+ *
+ * @param string id
+ * A string identifier for the named timeout.
+ */
+const clearNamedTimeout = function clearNamedTimeout(id) {
+ if (!namedTimeoutsStore) {
+ return;
+ }
+ clearTimeout(namedTimeoutsStore.get(id));
+ namedTimeoutsStore.delete(id);
+};
+exports.clearNamedTimeout = clearNamedTimeout;
+
+/**
+ * Helpers for creating and messaging between UI components.
+ */
+exports.ViewHelpers = {
+ /**
+ * Convenience method, dispatching a custom event.
+ *
+ * @param Node target
+ * A custom target element to dispatch the event from.
+ * @param string type
+ * The name of the event.
+ * @param any detail
+ * The data passed when initializing the event.
+ * @return boolean
+ * True if the event was cancelled or a registered handler
+ * called preventDefault.
+ */
+ dispatchEvent(target, type, detail) {
+ if (!(target instanceof Node)) {
+ // Event cancelled.
+ return true;
+ }
+ const document = target.ownerDocument || target;
+ const dispatcher = target.ownerDocument ? target : document.documentElement;
+
+ const event = document.createEvent("CustomEvent");
+ event.initCustomEvent(type, true, true, detail);
+ return dispatcher.dispatchEvent(event);
+ },
+
+ /**
+ * Helper delegating some of the DOM attribute methods of a node to a widget.
+ *
+ * @param object widget
+ * The widget to assign the methods to.
+ * @param Node node
+ * A node to delegate the methods to.
+ */
+ delegateWidgetAttributeMethods(widget, node) {
+ widget.getAttribute = widget.getAttribute || node.getAttribute.bind(node);
+ widget.setAttribute = widget.setAttribute || node.setAttribute.bind(node);
+ widget.removeAttribute =
+ widget.removeAttribute || node.removeAttribute.bind(node);
+ },
+
+ /**
+ * Helper delegating some of the DOM event methods of a node to a widget.
+ *
+ * @param object widget
+ * The widget to assign the methods to.
+ * @param Node node
+ * A node to delegate the methods to.
+ */
+ delegateWidgetEventMethods(widget, node) {
+ widget.addEventListener =
+ widget.addEventListener || node.addEventListener.bind(node);
+ widget.removeEventListener =
+ widget.removeEventListener || node.removeEventListener.bind(node);
+ },
+
+ /**
+ * Checks if the specified object looks like it's been decorated by an
+ * event emitter.
+ *
+ * @return boolean
+ * True if it looks, walks and quacks like an event emitter.
+ */
+ isEventEmitter(object) {
+ return object?.on && object?.off && object?.once && object?.emit;
+ },
+
+ /**
+ * Checks if the specified object is an instance of a DOM node.
+ *
+ * @return boolean
+ * True if it's a node, false otherwise.
+ */
+ isNode(object) {
+ return (
+ object instanceof Node ||
+ object instanceof Element ||
+ Cu.getClassName(object) == "DocumentFragment"
+ );
+ },
+
+ /**
+ * Prevents event propagation when navigation keys are pressed.
+ *
+ * @param Event e
+ * The event to be prevented.
+ */
+ preventScrolling(e) {
+ switch (e.keyCode) {
+ case KeyCodes.DOM_VK_UP:
+ case KeyCodes.DOM_VK_DOWN:
+ case KeyCodes.DOM_VK_LEFT:
+ case KeyCodes.DOM_VK_RIGHT:
+ case KeyCodes.DOM_VK_PAGE_UP:
+ case KeyCodes.DOM_VK_PAGE_DOWN:
+ case KeyCodes.DOM_VK_HOME:
+ case KeyCodes.DOM_VK_END:
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ },
+
+ /**
+ * Check if the enter key or space was pressed
+ *
+ * @param event event
+ * The event triggered by a keydown or keypress on an element
+ */
+ isSpaceOrReturn(event) {
+ return (
+ event.keyCode === KeyCodes.DOM_VK_SPACE ||
+ event.keyCode === KeyCodes.DOM_VK_RETURN
+ );
+ },
+
+ /**
+ * Sets a toggled pane hidden or visible. The pane can either be displayed on
+ * the side (right or left depending on the locale) or at the bottom.
+ *
+ * @param object flags
+ * An object containing some of the following properties:
+ * - visible: true if the pane should be shown, false to hide
+ * - animated: true to display an animation on toggle
+ * - delayed: true to wait a few cycles before toggle
+ * - callback: a function to invoke when the toggle finishes
+ * @param Node pane
+ * The element representing the pane to toggle.
+ */
+ togglePane(flags, pane) {
+ // Make sure a pane is actually available first.
+ if (!pane) {
+ return;
+ }
+
+ // Hiding is always handled via margins, not the hidden attribute.
+ pane.removeAttribute("hidden");
+
+ // Add a class to the pane to handle min-widths, margins and animations.
+ pane.classList.add("generic-toggled-pane");
+
+ // Avoid toggles in the middle of animation.
+ if (pane.hasAttribute("animated")) {
+ return;
+ }
+
+ // Avoid useless toggles.
+ if (flags.visible == !pane.classList.contains("pane-collapsed")) {
+ if (flags.callback) {
+ flags.callback();
+ }
+ return;
+ }
+
+ // The "animated" attributes enables animated toggles (slide in-out).
+ if (flags.animated) {
+ pane.setAttribute("animated", "");
+ } else {
+ pane.removeAttribute("animated");
+ }
+
+ // Computes and sets the pane margins in order to hide or show it.
+ const doToggle = () => {
+ // Negative margins are applied to "right" and "left" to support RTL and
+ // LTR directions, as well as to "bottom" to support vertical layouts.
+ // Unnecessary negative margins are forced to 0 via CSS in widgets.css.
+ if (flags.visible) {
+ pane.style.marginLeft = "0";
+ pane.style.marginRight = "0";
+ pane.style.marginBottom = "0";
+ pane.classList.remove("pane-collapsed");
+ } else {
+ const width = Math.floor(pane.getAttribute("width")) + 1;
+ const height = Math.floor(pane.getAttribute("height")) + 1;
+ pane.style.marginLeft = -width + "px";
+ pane.style.marginRight = -width + "px";
+ pane.style.marginBottom = -height + "px";
+ }
+
+ // Wait for the animation to end before calling afterToggle()
+ if (flags.animated) {
+ const options = {
+ useCapture: false,
+ once: true,
+ };
+
+ pane.addEventListener(
+ "transitionend",
+ () => {
+ // Prevent unwanted transitions: if the panel is hidden and the layout
+ // changes margins will be updated and the panel will pop out.
+ pane.removeAttribute("animated");
+
+ if (!flags.visible) {
+ pane.classList.add("pane-collapsed");
+ }
+ if (flags.callback) {
+ flags.callback();
+ }
+ },
+ options
+ );
+ } else {
+ if (!flags.visible) {
+ pane.classList.add("pane-collapsed");
+ }
+
+ // Invoke the callback immediately since there's no transition.
+ if (flags.callback) {
+ flags.callback();
+ }
+ }
+ };
+
+ // Sometimes it's useful delaying the toggle a few ticks to ensure
+ // a smoother slide in-out animation.
+ if (flags.delayed) {
+ pane.ownerDocument.defaultView.setTimeout(
+ doToggle,
+ PANE_APPEARANCE_DELAY
+ );
+ } else {
+ doToggle();
+ }
+ },
+};
+
+/**
+ * A generic Item is used to describe children present in a Widget.
+ *
+ * This is basically a very thin wrapper around a Node, with a few
+ * characteristics, like a `value` and an `attachment`.
+ *
+ * The characteristics are optional, and their meaning is entirely up to you.
+ * - The `value` should be a string, passed as an argument.
+ * - The `attachment` is any kind of primitive or object, passed as an argument.
+ *
+ * Iterable via "for (let childItem of parentItem) { }".
+ *
+ * @param object ownerView
+ * The owner view creating this item.
+ * @param Node element
+ * A prebuilt node to be wrapped.
+ * @param string value
+ * A string identifying the node.
+ * @param any attachment
+ * Some attached primitive/object.
+ */
+function Item(ownerView, element, value, attachment) {
+ this.ownerView = ownerView;
+ this.attachment = attachment;
+ this._value = value + "";
+ this._prebuiltNode = element;
+ this._itemsByElement = new Map();
+}
+
+Item.prototype = {
+ get value() {
+ return this._value;
+ },
+ get target() {
+ return this._target;
+ },
+ get prebuiltNode() {
+ return this._prebuiltNode;
+ },
+
+ /**
+ * Immediately appends a child item to this item.
+ *
+ * @param Node element
+ * A Node representing the child element to append.
+ * @param object options [optional]
+ * Additional options or flags supported by this operation:
+ * - attachment: some attached primitive/object for the item
+ * - attributes: a batch of attributes set to the displayed element
+ * - finalize: function invoked when the child item is removed
+ * @return Item
+ * The item associated with the displayed element.
+ */
+ append(element, options = {}) {
+ const item = new Item(this, element, "", options.attachment);
+
+ // Entangle the item with the newly inserted child node.
+ // Make sure this is done with the value returned by appendChild(),
+ // to avoid storing a potential DocumentFragment.
+ this._entangleItem(item, this._target.appendChild(element));
+
+ // Handle any additional options after entangling the item.
+ if (options.attributes) {
+ options.attributes.forEach(e => item._target.setAttribute(e[0], e[1]));
+ }
+ if (options.finalize) {
+ item.finalize = options.finalize;
+ }
+
+ // Return the item associated with the displayed element.
+ return item;
+ },
+
+ /**
+ * Immediately removes the specified child item from this item.
+ *
+ * @param Item item
+ * The item associated with the element to remove.
+ */
+ remove(item) {
+ if (!item) {
+ return;
+ }
+ this._target.removeChild(item._target);
+ this._untangleItem(item);
+ },
+
+ /**
+ * Entangles an item (model) with a displayed node element (view).
+ *
+ * @param Item item
+ * The item describing a target element.
+ * @param Node element
+ * The element displaying the item.
+ */
+ _entangleItem(item, element) {
+ this._itemsByElement.set(element, item);
+ item._target = element;
+ },
+
+ /**
+ * Untangles an item (model) from a displayed node element (view).
+ *
+ * @param Item item
+ * The item describing a target element.
+ */
+ _untangleItem(item) {
+ if (item.finalize) {
+ item.finalize(item);
+ }
+ for (const childItem of item) {
+ item.remove(childItem);
+ }
+
+ this._unlinkItem(item);
+ item._target = null;
+ },
+
+ /**
+ * Deletes an item from the its parent's storage maps.
+ *
+ * @param Item item
+ * The item describing a target element.
+ */
+ _unlinkItem(item) {
+ this._itemsByElement.delete(item._target);
+ },
+
+ /**
+ * Returns a string representing the object.
+ * Avoid using `toString` to avoid accidental JSONification.
+ * @return string
+ */
+ stringify() {
+ return JSON.stringify(
+ {
+ value: this._value,
+ target: this._target + "",
+ prebuiltNode: this._prebuiltNode + "",
+ attachment: this.attachment,
+ },
+ null,
+ 2
+ );
+ },
+
+ _value: "",
+ _target: null,
+ _prebuiltNode: null,
+ finalize: null,
+ attachment: null,
+};
diff --git a/devtools/client/shared/widgets/widgets.css b/devtools/client/shared/widgets/widgets.css
new file mode 100644
index 0000000000..dbf558b2f3
--- /dev/null
+++ b/devtools/client/shared/widgets/widgets.css
@@ -0,0 +1,79 @@
+/* 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/. */
+
+/* BreacrumbsWidget */
+
+.breadcrumbs-widget-item {
+ direction: ltr;
+}
+
+.breadcrumbs-widget-item {
+ -moz-user-focus: normal;
+}
+
+/* VariablesView */
+
+.variables-view-container {
+ overflow-x: hidden;
+ overflow-y: auto;
+ direction: ltr;
+}
+
+.variables-view-element-details:not([open]) {
+ display: none;
+}
+
+.variable-or-property {
+ -moz-user-focus: normal;
+}
+
+.variables-view-scope > .title,
+.variable-or-property > .title {
+ overflow: hidden;
+}
+
+.variables-view-scope[untitled] > .title,
+.variable-or-property[untitled] > .title,
+.variable-or-property[unmatched] > .title {
+ display: none;
+}
+
+.variable-or-property:not([safe-getter]) > tooltip > label.WebIDL,
+.variable-or-property:not([overridden]) > tooltip > label.overridden,
+.variable-or-property:not([non-extensible]) > tooltip > label.extensible,
+.variable-or-property:not([frozen]) > tooltip > label.frozen,
+.variable-or-property:not([sealed]) > tooltip > label.sealed {
+ display: none;
+}
+
+.variable-or-property[pseudo-item] > tooltip,
+.variable-or-property[pseudo-item] > .title > .variables-view-edit,
+.variable-or-property[pseudo-item] > .title > .variables-view-delete,
+.variable-or-property[pseudo-item] > .title > .variables-view-add-property,
+.variable-or-property[pseudo-item] > .title > .variables-view-open-inspector,
+.variable-or-property[pseudo-item] > .title > .variable-or-property-frozen-label,
+.variable-or-property[pseudo-item] > .title > .variable-or-property-sealed-label,
+.variable-or-property[pseudo-item] > .title > .variable-or-property-non-extensible-label,
+.variable-or-property[pseudo-item] > .title > .variable-or-property-non-writable-icon {
+ display: none;
+}
+
+.variable-or-property > .title .toolbarbutton-text {
+ display: none;
+}
+
+*:not(:hover) .variables-view-delete,
+*:not(:hover) .variables-view-add-property,
+*:not(:hover) .variables-view-open-inspector {
+ visibility: hidden;
+}
+
+.variables-view-container[aligned-values] [optional-visibility] {
+ display: none;
+}
+
+/* Table Widget */
+.table-widget-body > .devtools-side-splitter:last-child {
+ display: none;
+}
diff --git a/devtools/client/shared/worker-utils.js b/devtools/client/shared/worker-utils.js
new file mode 100644
index 0000000000..bb5c54dac3
--- /dev/null
+++ b/devtools/client/shared/worker-utils.js
@@ -0,0 +1,157 @@
+/* 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/>. */
+
+"use strict";
+
+class WorkerDispatcher {
+ #msgId = 1;
+ #worker = null;
+ // Map of message ids -> promise resolution functions, for dispatching worker responses
+ #pendingCalls = new Map();
+ #url = "";
+
+ constructor(url) {
+ this.#url = url;
+ }
+
+ start() {
+ // When running in debugger jest test, we don't have access to ChromeWorker
+ if (typeof ChromeWorker == "function") {
+ this.#worker = new ChromeWorker(this.#url);
+ } else {
+ this.#worker = new Worker(this.#url);
+ }
+ this.#worker.onerror = err => {
+ console.error(`Error in worker ${this.#url}`, err.message);
+ };
+ this.#worker.addEventListener("message", this.#onMessage);
+ }
+
+ stop() {
+ if (!this.#worker) {
+ return;
+ }
+
+ this.#worker.removeEventListener("message", this.#onMessage);
+ this.#worker.terminate();
+ this.#worker = null;
+ this.#pendingCalls.clear();
+ }
+
+ task(method, { queue = false } = {}) {
+ const calls = [];
+ const push = args => {
+ return new Promise((resolve, reject) => {
+ if (queue && calls.length === 0) {
+ Promise.resolve().then(flush);
+ }
+
+ calls.push({ args, resolve, reject });
+
+ if (!queue) {
+ flush();
+ }
+ });
+ };
+
+ const flush = () => {
+ const items = calls.slice();
+ calls.length = 0;
+
+ if (!this.#worker) {
+ this.start();
+ }
+
+ const id = this.#msgId++;
+ this.#worker.postMessage({
+ id,
+ method,
+ calls: items.map(item => item.args),
+ });
+
+ this.#pendingCalls.set(id, items);
+ };
+
+ return (...args) => push(args);
+ }
+
+ invoke(method, ...args) {
+ return this.task(method)(...args);
+ }
+
+ #onMessage = ({ data: result }) => {
+ const items = this.#pendingCalls.get(result.id);
+ this.#pendingCalls.delete(result.id);
+ if (!items) {
+ return;
+ }
+
+ if (!this.#worker) {
+ return;
+ }
+
+ result.results.forEach((resultData, i) => {
+ const { resolve, reject } = items[i];
+
+ if (resultData.error) {
+ const err = new Error(resultData.message);
+ err.metadata = resultData.metadata;
+ reject(err);
+ } else {
+ resolve(resultData.response);
+ }
+ });
+ };
+}
+
+function workerHandler(publicInterface) {
+ return function (msg) {
+ const { id, method, calls } = msg.data;
+
+ Promise.all(
+ calls.map(args => {
+ try {
+ const response = publicInterface[method].apply(undefined, args);
+ if (response instanceof Promise) {
+ return response.then(
+ val => ({ response: val }),
+ err => asErrorMessage(err)
+ );
+ }
+ return { response };
+ } catch (error) {
+ return asErrorMessage(error);
+ }
+ })
+ ).then(results => {
+ globalThis.postMessage({ id, results });
+ });
+ };
+}
+
+function asErrorMessage(error) {
+ if (typeof error === "object" && error && "message" in error) {
+ // Error can't be sent via postMessage, so be sure to convert to
+ // string.
+ return {
+ error: true,
+ message: error.message,
+ metadata: error.metadata,
+ };
+ }
+
+ return {
+ error: true,
+ message: error == null ? error : error.toString(),
+ metadata: undefined,
+ };
+}
+
+// Might be loaded within a worker thread where `module` isn't available.
+if (typeof module !== "undefined") {
+ module.exports = {
+ WorkerDispatcher,
+ workerHandler,
+ };
+}
diff --git a/devtools/client/shared/workers-listener.js b/devtools/client/shared/workers-listener.js
new file mode 100644
index 0000000000..7d544481d8
--- /dev/null
+++ b/devtools/client/shared/workers-listener.js
@@ -0,0 +1,145 @@
+/* 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/. */
+
+"use strict";
+
+/**
+ * Listening to worker updates requires watching various sources. This class provides
+ * a single addListener/removeListener API that will aggregate all possible worker update
+ * events.
+ *
+ * Only supports one listener at a time.
+ */
+class WorkersListener {
+ constructor(rootFront, { registrationsOnly = false } = {}) {
+ this.rootFront = rootFront;
+ this.registrationsOnly = registrationsOnly;
+
+ // bind handlers
+ this._onContentProcessTargetAvailable =
+ this._onContentProcessTargetAvailable.bind(this);
+ this._onServiceWorkerRegistrationAvailable =
+ this._onServiceWorkerRegistrationAvailable.bind(this);
+ this._onProcessDescriptorAvailable =
+ this._onProcessDescriptorAvailable.bind(this);
+ this._onContentProcessTargetDestroyed =
+ this._onContentProcessTargetDestroyed.bind(this);
+ this._onServiceWorkerRegistrationDestroyed =
+ this._onServiceWorkerRegistrationDestroyed.bind(this);
+ this._onProcessDescriptorDestroyed =
+ this._onProcessDescriptorDestroyed.bind(this);
+
+ // Array of contentProcessTarget fronts on which we will listen for worker events.
+ this._contentProcessFronts = [];
+ this._serviceWorkerRegistrationFronts = [];
+ this._processDescriptors = [];
+ this._listener = null;
+ }
+
+ addListener(listener) {
+ if (this._listener) {
+ throw new Error("WorkersListener addListener called twice.");
+ }
+
+ this._listener = listener;
+ if (!this.registrationsOnly) {
+ this.rootFront.on("workerListChanged", this._listener);
+ this.rootFront.watchFronts(
+ "processDescriptor",
+ this._onProcessDescriptorAvailable,
+ this._onProcessDescriptorDestroyed
+ );
+ this.rootFront.on("processListChanged", this._listener);
+ }
+
+ this.rootFront.watchFronts(
+ "serviceWorkerRegistration",
+ this._onServiceWorkerRegistrationAvailable,
+ this._onServiceWorkerRegistrationDestroyed
+ );
+
+ this.rootFront.on("serviceWorkerRegistrationListChanged", this._listener);
+ }
+
+ removeListener(listener) {
+ if (!this._listener) {
+ return;
+ }
+
+ this.rootFront.off("workerListChanged", this._listener);
+ this.rootFront.off("serviceWorkerRegistrationListChanged", this._listener);
+ this.rootFront.off("processListChanged", this._listener);
+
+ for (const front of this._contentProcessFronts) {
+ front.off("workerListChanged", this._listener);
+ }
+
+ for (const front of this._serviceWorkerRegistrationFronts) {
+ front.off("push-subscription-modified", this._listener);
+ front.off("registration-changed", this._listener);
+ }
+
+ for (const processFront of this._processDescriptors) {
+ processFront.unwatchFronts(
+ "contentProcessTarget",
+ this._onContentProcessTargetAvailable
+ );
+ }
+
+ this.rootFront.unwatchFronts(
+ "processDescriptor",
+ this._onProcessDescriptorAvailable,
+ this._onProcessDescriptorDestroyed
+ );
+ this.rootFront.unwatchFronts(
+ "serviceWorkerRegistration",
+ this._onServiceWorkerRegistrationAvailable,
+ this._onServiceWorkerRegistrationDestroyed
+ );
+
+ this._contentProcessFronts = [];
+ this._serviceWorkerRegistrationFronts = [];
+ this._processDescriptors = [];
+ this._listener = null;
+ }
+
+ _onContentProcessTargetAvailable(front) {
+ this._contentProcessFronts.push(front);
+ front.on("workerListChanged", this._listener);
+ }
+
+ _onContentProcessTargetDestroyed(front) {
+ this._contentProcessFronts = this._contentProcessFronts.filter(
+ f => f !== front
+ );
+ }
+
+ _onServiceWorkerRegistrationAvailable(front) {
+ this._serviceWorkerRegistrationFronts.push(front);
+ front.on("push-subscription-modified", this._listener);
+ front.on("registration-changed", this._listener);
+ }
+
+ _onServiceWorkerRegistrationDestroyed(front) {
+ this._serviceWorkerRegistrationFronts =
+ this._serviceWorkerRegistrationFronts.filter(f => f !== front);
+ }
+
+ _onProcessDescriptorAvailable(processFront) {
+ this._processDescriptors.push(processFront);
+ processFront.watchFronts(
+ "contentProcessTarget",
+ this._onContentProcessTargetAvailable,
+ this._onContentProcessTargetDestroyed
+ );
+ }
+
+ _onProcessDescriptorDestroyed(processFront) {
+ this._processDescriptors = this._processDescriptors.filter(
+ f => f !== processFront
+ );
+ }
+}
+
+exports.WorkersListener = WorkersListener;
diff --git a/devtools/client/shared/zoom-keys.js b/devtools/client/shared/zoom-keys.js
new file mode 100644
index 0000000000..b98e1a3fbd
--- /dev/null
+++ b/devtools/client/shared/zoom-keys.js
@@ -0,0 +1,75 @@
+/* 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/. */
+
+"use strict";
+
+const ZOOM_PREF = "devtools.toolbox.zoomValue";
+const MIN_ZOOM = 0.5;
+const MAX_ZOOM = 2;
+
+const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
+const L10N = new LocalizationHelper(
+ "devtools/client/locales/toolbox.properties"
+);
+
+/**
+ * Register generic keys to control zoom level of the given document.
+ * Used by both the toolboxes and the browser console.
+ *
+ * @param {DOMWindow}
+ * The window on which we should listent to key strokes and modify the zoom factor.
+ * @param {KeyShortcuts}
+ * KeyShortcuts instance where the zoom keys should be added.
+ */
+exports.register = function (window, shortcuts) {
+ const bc = BrowsingContext.getFromWindow(window);
+ let zoomValue = parseFloat(Services.prefs.getCharPref(ZOOM_PREF));
+ const zoomIn = function (event) {
+ setZoom(zoomValue + 0.1);
+ event.preventDefault();
+ };
+
+ const zoomOut = function (event) {
+ setZoom(zoomValue - 0.1);
+ event.preventDefault();
+ };
+
+ const zoomReset = function (event) {
+ setZoom(1);
+ event.preventDefault();
+ };
+
+ const setZoom = function (newValue) {
+ // cap zoom value
+ zoomValue = Math.max(newValue, MIN_ZOOM);
+ zoomValue = Math.min(zoomValue, MAX_ZOOM);
+ // Prevent the floating-point error. (e.g. 1.1 + 0.1 = 1.2000000000000002)
+ zoomValue = Math.round(zoomValue * 10) / 10;
+
+ bc.fullZoom = zoomValue;
+
+ Services.prefs.setCharPref(ZOOM_PREF, zoomValue);
+ };
+
+ // Set zoom to whatever the last setting was.
+ setZoom(zoomValue);
+
+ shortcuts.on(L10N.getStr("toolbox.zoomIn.key"), zoomIn);
+ const zoomIn2 = L10N.getStr("toolbox.zoomIn2.key");
+ if (zoomIn2) {
+ shortcuts.on(zoomIn2, zoomIn);
+ }
+
+ shortcuts.on(L10N.getStr("toolbox.zoomOut.key"), zoomOut);
+ const zoomOut2 = L10N.getStr("toolbox.zoomOut2.key");
+ if (zoomOut2) {
+ shortcuts.on(zoomOut2, zoomOut);
+ }
+
+ shortcuts.on(L10N.getStr("toolbox.zoomReset.key"), zoomReset);
+ const zoomReset2 = L10N.getStr("toolbox.zoomReset2.key");
+ if (zoomReset2) {
+ shortcuts.on(zoomReset2, zoomReset);
+ }
+};